1#
2# tk/optionobj.rb : control options for a group of widgets
3#
4#  NOTE: If you want to use key-only option (no value),
5#        use Tk::None for the value of the key-only option.
6#
7#        e.g. hash_kv({'aaa'=>1, 'bbb'=>Tk::None, 'ccc'=>3})
8#                 => ["-aaa", 1, "-bbb", "-ccc", 3]
9#
10require 'tk'
11
12module Tk
13  class OptionObj < Hash
14    include TkUtil
15
16    def initialize(hash = nil)
17      super()
18      @observ = []
19      update_without_notify(_symbolkey2str(hash)) if hash
20    end
21
22    def observ_info
23      @observ.dup
24    end
25
26    def observs
27      @observ.collect{|win|
28        if win.kind_of?(Array)
29          win[0]
30        else
31          win
32        end
33      }
34    end
35
36    def _remove_win(win)
37      if win.kind_of?(Array)
38        widget, method = win
39        @observ.delete_if{|x|
40          if x.kind_of?(Array)
41            x[0] == widget
42          else
43            x == widget
44          end
45        }
46      else
47        @observ.delete_if{|x|
48          if x.kind_of?(Array)
49            x[0] == win
50          else
51            x == win
52          end
53        }
54      end
55    end
56    private :_remove_win
57
58    def assign(*wins)
59      # win :=
60      #   widget             #==> call widget.configure(hash)
61      #   [widget]           #==> call widget.configure(hash)
62      #   [widget, nil, {src=>target, ... }]
63      #                      #==> call widget.configure(hash)
64      #                               with converting hash-key
65      #   [widget, method]   #==> call widget.method(hash)
66      #   [widget, method, {src=>target, ... }]
67      #                      #==> call widget.method(hash)
68      #                               with converting hash-key
69      #   [widget [receiver, method, arg, ... ]]
70      #                      #==> call receiver.method(arg, ... , hash)
71      #   [widget [receiver, method, arg, ... ], {src=>target, ... }]
72      #                      #==> call receiver.method(arg, ... , hash)
73      #                               with onverting hash-key
74      #
75      # src := option_name_on_optobj
76      #
77      # target :=
78      #   nil                #==> not use the src
79      #   option_name_on_target_widget
80      #   [ option_name_on_target_widget, ... ]
81      #                      #==> set all of them
82      #
83      wins.each{|win|
84        _remove_win(win)
85        @observ << win
86        notify(win)
87      }
88      self
89    end
90
91    def unassign(*wins)
92      wins.each{|win|
93        _remove_win(win)
94      }
95      self
96    end
97
98    def notify(target = nil)
99      if target
100        targets = [target]
101      elsif @observ.empty?
102        return self
103      else
104        targets = @observ.dup
105      end
106
107      return self if empty?
108
109      org_hash = _symbolkey2str(self)
110
111      targets.each{|win|
112        widget = receiver = win
113        hash = org_hash
114        begin
115          if win.kind_of?(Array)
116            widget, method, conv_tbl = win
117            receiver = widget
118
119            if conv_tbl
120              hash = {}
121              org_hash.each{|key, val|
122                key = conv_tbl[key] if conv_tbl.key?(key)
123                next unless key
124                if key.kind_of?(Array)
125                  key.each{|k| hash[k] = val}
126                else
127                  hash[key] = val
128                end
129              }
130            end
131
132            if method.kind_of?(Array)
133              receiver, method, *args = method
134              receiver.__send__(method, *(args << hash))
135            elsif method
136              widget.__send__(method, hash)
137            else
138              widget.configure(hash)
139            end
140
141          else
142            widget.configure(self)
143          end
144        rescue => e
145          if ( ( widget.kind_of?(TkObject) \
146                && widget.respond_to?('exist?') \
147                && ! receiver.exist? ) \
148            || ( receiver.kind_of?(TkObject) \
149                && receiver.respond_to?('exist?') \
150                && ! receiver.exist? ) )
151            @observ.delete(win)
152          else
153            fail e
154          end
155        end
156      }
157
158      self
159    end
160    alias apply notify
161
162    def +(hash)
163      unless hash.kind_of?(Hash)
164        fail ArgumentError, "expect a Hash"
165      end
166      new_obj = self.dup
167      new_obj.update_without_notify(_symbolkey2str(hash))
168      new_obj
169    end
170
171    alias update_without_notify update
172
173    def update(hash)
174      update_without_notify(_symbolkey2str(hash))
175      notify
176    end
177
178    def configure(key, value=nil)
179      if key.kind_of?(Hash)
180        update(key)
181      else
182        store(key,value)
183      end
184    end
185
186    def [](key)
187      super(key.to_s)
188    end
189    alias cget []
190
191    def store(key, val)
192      key = key.to_s
193      super(key, val)
194      notify
195    end
196    def []=(key, val)
197      store(key,val)
198    end
199
200    def replace(hash)
201      super(_symbolkey2str(hash))
202      notify
203    end
204
205    def default(opt)
206      fail RuntimeError, "unknown option `#{opt}'"
207    end
208    private :default
209
210    undef :default=
211  end
212end
213