1#
2# tk/composite.rb :
3#
4require 'tk'
5
6module TkComposite
7  include Tk
8  extend Tk
9
10=begin
11  def initialize(parent=nil, *args)
12    @delegates = {}
13    @option_methods = {}
14    @option_setting = {}
15
16    if parent.kind_of? Hash
17      keys = _symbolkey2str(parent)
18      parent = keys.delete('parent')
19      @frame = TkFrame.new(parent)
20      @path = @epath = @frame.path
21      initialize_composite(keys)
22    else
23      @frame = TkFrame.new(parent)
24      @path = @epath = @frame.path
25      initialize_composite(*args)
26    end
27  end
28=end
29
30  def _choice_classname_of_baseframe
31    base_class_name = nil
32
33    klass = WidgetClassNames[self.class::WidgetClassName]
34
35    if klass
36      # WidgetClassName is a known class
37      #if klass <= TkFrame || klass < TkComposite
38      if klass <= TkFrame || klass < Tk::Frame || klass < TkComposite
39        # klass is valid for the base frame
40        if self.class <= klass
41          # use my classname
42          base_class_name = self.class.name
43          if base_class_name == ''
44            # anonymous class -> use ancestor's name
45            base_class_name = klass.name
46          end
47        else
48          # not subclass -> use WidgetClassName
49          base_class_name = klass.name
50        end
51
52      else
53        # klass is invalid for the base frame
54        #if self.class < TkFrame || self.class.superclass < TkComposite
55        if self.class < TkFrame || self.class.superclass < Tk::Frame || self.class.superclass < TkComposite
56          # my class name is valid for the base frame -> use my classname
57          base_class_name = self.class.name
58          if base_class_name == ''
59            # anonymous class -> use TkFrame
60            base_class_name = nil
61          end
62        else
63          # no idea for the base frame -> use TkFrame
64          base_class_name = nil
65        end
66      end
67
68    elsif self.class::WidgetClassName && ! self.class::WidgetClassName.empty?
69      # unknown WidgetClassName is defined -> use it for the base frame
70      base_class_name = self.class::WidgetClassName
71
72    else
73      # no valid WidgetClassName
74      #if self.class < TkFrame || self.class.superclass < TkComposite
75      if self.class < TkFrame || self.class.superclass < Tk::Frame || self.class.superclass < TkComposite
76        # my class name is valid for the base frame -> use my classname
77        base_class_name = self.class.name
78        if base_class_name == ''
79          # anonymous class -> use TkFrame
80          base_class_name = nil
81        end
82      else
83        # no idea for the base frame -> use TkFrame
84        base_class_name = nil
85      end
86    end
87
88    base_class_name
89  end
90  private :_choice_classname_of_baseframe
91
92  # def initialize(parent=nil, *args)
93  def initialize(*args)
94    @delegates = {}
95    @option_methods = {}
96    @option_setting = {}
97
98    if args[-1].kind_of?(Hash)
99      keys = _symbolkey2str(args.pop)
100    else
101      keys = {}
102    end
103    parent = args.shift
104    parent = keys.delete('parent') if keys.has_key?('parent')
105
106    if keys.key?('classname')
107      keys['class'] = keys.delete('classname')
108    end
109    if (base_class_name = (keys.delete('class')).to_s).empty?
110      base_class_name = _choice_classname_of_baseframe
111    end
112
113    if base_class_name
114      # @frame = Tk::Frame.new(parent, :class=>base_class_name)
115      # --> use current TkFrame class
116      @frame = TkFrame.new(parent, :class=>base_class_name)
117    else
118      # @frame = Tk::Frame.new(parent)
119      # --> use current TkFrame class
120      @frame = TkFrame.new(parent)
121    end
122    @path = @epath = @frame.path
123
124    args.push(keys) unless keys.empty?
125    initialize_composite(*args)
126  end
127
128  def database_classname
129    @frame.database_classname
130  end
131
132  def database_class
133    @frame.database_class
134  end
135
136  def epath
137    @epath
138  end
139
140  def initialize_composite(*args) end
141  private :initialize_composite
142
143  def inspect
144    str = super
145    str.chop << ' @epath=' << @epath.inspect << '>'
146  end
147
148  def _get_opt_method_list(arg)
149    m_set, m_cget, m_info = arg
150    m_set  = m_set.to_s
151    m_cget = m_set if !m_cget && self.method(m_set).arity == -1
152    m_cget = m_cget.to_s if m_cget
153    m_info = m_info.to_s if m_info
154    [m_set, m_cget, m_info]
155  end
156  private :_get_opt_method_list
157
158  def option_methods(*opts)
159    if opts.size == 1 && opts[0].kind_of?(Hash)
160      # {name => [m_set, m_cget, m_info], name => method} style
161      opts[0].each{|name, arg|
162        m_set, m_cget, m_info = _get_opt_method_list(arg)
163        @option_methods[name.to_s] = {
164          :set => m_set, :cget => m_cget, :info => m_info
165        }
166      }
167    else
168      # [m_set, m_cget, m_info] or method style
169      opts.each{|arg|
170        m_set, m_cget, m_info = _get_opt_method_list(arg)
171        @option_methods[m_set] = {
172          :set => m_set, :cget => m_cget, :info => m_info
173        }
174      }
175    end
176  end
177
178  def delegate_alias(alias_opt, option, *wins)
179    if wins.length == 0
180      fail ArgumentError, "target widgets are not given"
181    end
182    if alias_opt != option && (alias_opt == 'DEFAULT' || option == 'DEFAULT')
183      fail ArgumentError, "cannot alias 'DEFAULT' option"
184    end
185    alias_opt = alias_opt.to_s
186    option = option.to_s
187    if @delegates[alias_opt].kind_of?(Array)
188      if (elem = @delegates[alias_opt].assoc(option))
189        wins.each{|w| elem[1].push(w)}
190      else
191        @delegates[alias_opt] << [option, wins]
192      end
193    else
194      @delegates[alias_opt] = [ [option, wins] ]
195    end
196  end
197
198  def delegate(option, *wins)
199    delegate_alias(option, option, *wins)
200  end
201
202  def __cget_delegates(slot)
203    slot = slot.to_s
204
205    if @option_methods.include?(slot)
206      if @option_methods[slot][:cget]
207        return self.__send__(@option_methods[slot][:cget])
208      else
209        if @option_setting[slot]
210          return @option_setting[slot]
211        else
212          return ''
213        end
214      end
215    end
216
217    tbl = @delegates[slot]
218    tbl = @delegates['DEFAULT'] unless tbl
219
220    begin
221      if tbl
222        opt, wins = tbl[-1]
223        opt = slot if opt == 'DEFAULT'
224        if wins && wins[-1]
225          # return wins[-1].cget(opt)
226          return wins[-1].cget_strict(opt)
227        end
228      end
229    rescue
230    end
231
232    return None
233  end
234  private :__cget_delegates
235
236  def cget_tkstring(slot)
237    if (ret = __cget_delegates(slot)) == None
238      super(slot)
239    else
240      _get_eval_string(ret)
241    end
242  end
243
244  def cget(slot)
245    if (ret = __cget_delegates(slot)) == None
246      super(slot)
247    else
248      ret
249    end
250  end
251
252  def cget_strict(slot)
253    if (ret = __cget_delegates(slot)) == None
254      super(slot)
255    else
256      ret
257    end
258  end
259
260=begin
261  def cget(slot)
262    slot = slot.to_s
263
264    if @option_methods.include?(slot)
265      if @option_methods[slot][:cget]
266        return self.__send__(@option_methods[slot][:cget])
267      else
268        if @option_setting[slot]
269          return @option_setting[slot]
270        else
271          return ''
272        end
273      end
274    end
275
276    tbl = @delegates[slot]
277    tbl = @delegates['DEFAULT'] unless tbl
278
279    begin
280      if tbl
281        opt, wins = tbl[-1]
282        opt = slot if opt == 'DEFAULT'
283        if wins && wins[-1]
284          return wins[-1].cget(opt)
285        end
286      end
287    rescue
288    end
289
290    super(slot)
291  end
292=end
293
294  def configure(slot, value=None)
295    if slot.kind_of? Hash
296      slot.each{|slot,value| configure slot, value}
297      return self
298    end
299
300    slot = slot.to_s
301
302    if @option_methods.include?(slot)
303      unless @option_methods[slot][:cget]
304        if value.kind_of?(Symbol)
305          @option_setting[slot] = value.to_s
306        else
307          @option_setting[slot] = value
308        end
309      end
310      return self.__send__(@option_methods[slot][:set], value)
311    end
312
313    tbl = @delegates[slot]
314    tbl = @delegates['DEFAULT'] unless tbl
315
316    begin
317      if tbl
318        last = nil
319        tbl.each{|opt, wins|
320          opt = slot if opt == 'DEFAULT'
321          wins.each{|w| last = w.configure(opt, value)}
322        }
323        return last
324      end
325    rescue
326    end
327
328    super(slot, value)
329  end
330
331  def configinfo(slot = nil)
332    if TkComm::GET_CONFIGINFO_AS_ARRAY
333      if slot
334        slot = slot.to_s
335        if @option_methods.include?(slot)
336          if @option_methods[slot][:info]
337            return self.__send__(@option_methods[slot][:info])
338          else
339            return [slot, '', '', '', self.cget(slot)]
340          end
341        end
342
343        tbl = @delegates[slot]
344        tbl = @delegates['DEFAULT'] unless tbl
345
346        begin
347          if tbl
348            if tbl.length == 1
349              opt, wins = tbl[0]
350              if slot == opt || opt == 'DEFAULT'
351                return wins[-1].configinfo(slot)
352              else
353                info = wins[-1].configinfo(opt)
354                info[0] = slot
355                return info
356              end
357            else
358              opt, wins = tbl[-1]
359              return [slot, '', '', '', wins[-1].cget(opt)]
360            end
361          end
362        rescue
363        end
364
365        super(slot)
366
367      else # slot == nil
368        info_list = super(slot)
369
370        tbl = @delegates['DEFAULT']
371        if tbl
372          wins = tbl[0][1]
373          if wins && wins[-1]
374            wins[-1].configinfo.each{|info|
375              slot = info[0]
376              info_list.delete_if{|i| i[0] == slot} << info
377            }
378          end
379        end
380
381        @delegates.each{|slot, tbl|
382          next if slot == 'DEFAULT'
383          if tbl.length == 1
384            opt, wins = tbl[0]
385            next unless wins && wins[-1]
386            if slot == opt
387              info_list.delete_if{|i| i[0] == slot} <<
388                wins[-1].configinfo(slot)
389            else
390              info = wins[-1].configinfo(opt)
391              info[0] = slot
392              info_list.delete_if{|i| i[0] == slot} << info
393            end
394          else
395            opt, wins = tbl[-1]
396            info_list.delete_if{|i| i[0] == slot} <<
397              [slot, '', '', '', wins[-1].cget(opt)]
398          end
399        }
400
401        @option_methods.each{|slot, m|
402          if m[:info]
403            info = self.__send__(m[:info])
404          else
405            info = [slot, '', '', '', self.cget(slot)]
406          end
407          info_list.delete_if{|i| i[0] == slot} << info
408        }
409
410        info_list
411      end
412
413    else # ! TkComm::GET_CONFIGINFO_AS_ARRAY
414      if slot
415        slot = slot.to_s
416        if @option_methods.include?(slot)
417          if @option_methods[slot][:info]
418            return self.__send__(@option_methods[slot][:info])
419          else
420            return {slot => ['', '', '', self.cget(slot)]}
421          end
422        end
423
424        tbl = @delegates[slot]
425        tbl = @delegates['DEFAULT'] unless tbl
426
427        begin
428          if tbl
429            if tbl.length == 1
430              opt, wins = tbl[0]
431              if slot == opt || opt == 'DEFAULT'
432                return wins[-1].configinfo(slot)
433              else
434                return {slot => wins[-1].configinfo(opt)[opt]}
435              end
436            else
437              opt, wins = tbl[-1]
438              return {slot => ['', '', '', wins[-1].cget(opt)]}
439            end
440          end
441        rescue
442        end
443
444        super(slot)
445
446      else # slot == nil
447        info_list = super(slot)
448
449        tbl = @delegates['DEFAULT']
450        if tbl
451          wins = tbl[0][1]
452          info_list.update(wins[-1].configinfo) if wins && wins[-1]
453        end
454
455        @delegates.each{|slot, tbl|
456          next if slot == 'DEFAULT'
457          if tbl.length == 1
458            opt, wins = tbl[0]
459            next unless wins && wins[-1]
460            if slot == opt
461              info_list.update(wins[-1].configinfo(slot))
462            else
463              info_list.update({slot => wins[-1].configinfo(opt)[opt]})
464            end
465          else
466            opt, wins = tbl[-1]
467            info_list.update({slot => ['', '', '', wins[-1].cget(opt)]})
468          end
469        }
470
471        @option_methods.each{|slot, m|
472          if m[:info]
473            info = self.__send__(m[:info])
474          else
475            info = {slot => ['', '', '', self.cget(slot)]}
476          end
477          info_list.update(info)
478        }
479
480        info_list
481      end
482    end
483  end
484end
485