1#
2#  tkextlib/itk/incr_tk.rb
3#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
4#
5
6require 'tk'
7require 'tk/menuspec'
8require 'tkextlib/itcl.rb'
9
10# call setup script
11require 'tkextlib/itk.rb'
12
13#TkPackage.require('Itk', '3.2')
14TkPackage.require('Itk')
15
16module Tk
17  module Itk
18    include Tk
19    extend Tk
20
21    LIBRARY = TkVarAccess.new('::itk::library')
22
23    PACKAGE_NAME = 'Itk'.freeze
24    def self.package_name
25      PACKAGE_NAME
26    end
27
28    def self.package_version
29      begin
30        TkPackage.require('Itk')
31      rescue
32        ''
33      end
34    end
35
36    def self.usual(arg, *args)
37      tk_call('::itk::usual', arg, *args)
38    end
39
40    def self.usual_names
41      list(tk_call('::itk::usual'))
42    end
43
44    ############################
45
46    class Archetype < TkWindow
47      TkCommandNames = [].freeze
48      # WidgetClassName = 'Archetype'.freeze
49      # WidgetClassNames[WidgetClassName] = self
50
51      def self.to_eval
52        '::itk::' << self::WidgetClassName
53      end
54
55      def __destroy_hook__
56        Tk::Itk::Component::ComponentID_TBL.delete(self.path)
57      end
58
59      #### [incr Tk] public methods
60      def component
61        simplelist(tk_send('component'))
62      end
63
64      def component_path(name)
65        window(tk_send('component', name))
66      end
67      alias component_widget component_path
68
69      def component_invoke(name, cmd, *args)
70        window(tk_send('component', name, cmd, *args))
71      end
72
73      def component_obj(*names)
74        names = component if names.empty?
75        names.collect{|name| Tk::Itk::Component.new(self.path, name) }
76      end
77
78      #### [incr Tk] protected methods
79=begin
80      def itk_component_add(visibility, name, create_cmds, option_cmds=None)
81        args = []
82        visibility.each{|v| v = v.to_s; args << ( (v[0] == ?-)? v: "-#{v}" )}
83        args << '--' << name << create_cmd << option_cmds
84        tk_call('itk_component', 'add', *args)
85      end
86
87      def itk_component_delete(*names)
88        tk_call('itk_component', 'delete', *names)
89      end
90
91      def itk_initialize(keys={})
92        tk_call('itk_initialize', keys)
93      end
94
95      def itk_option_add(*args)
96        tk_call('itk_option', 'add', *args)
97      end
98
99      def itk_option_define(name, resource, klass, init, config=None)
100        tk_call('itk_option', 'define', name, resource, klass, init, config)
101      end
102
103      def itk_option_remove(*args)
104        tk_call('itk_option', 'remove', *args)
105      end
106=end
107    end
108
109    ############################
110
111    class Toplevel < Archetype
112      TkCommandNames = ['::itk::Toplevel'].freeze
113      WidgetClassName = 'Toplevel'.freeze
114      WidgetClassNames[WidgetClassName] ||= self
115
116      include Wm
117      include TkMenuSpec
118
119      def __strval_optkeys
120        super() << 'title'
121      end
122      private :__strval_optkeys
123    end
124
125    ############################
126
127    class Widget < Archetype
128      TkCommandNames = ['::itk::Widget'].freeze
129      WidgetClassName = 'Widget'.freeze
130      WidgetClassNames[WidgetClassName] ||= self
131    end
132
133
134    ############################
135
136    class Component < TkObject
137      def __cget_cmd
138        [self.master, 'component', self.name, 'cget']
139      end
140      private :__cget_cmd
141
142      def __config_cmd
143        [self.master, 'component', self.name, 'configure']
144      end
145      private :__config_cmd
146
147      ComponentID_TBL = TkCore::INTERP.create_table
148
149      (Itk_Component_ID = ['itk:component'.freeze, TkUtil.untrust('00000')]).instance_eval{
150        @mutex = Mutex.new
151        def mutex; @mutex; end
152        freeze
153      }
154
155      TkCore::INTERP.init_ip_env{
156        ComponentID_TBL.mutex.synchronize{ ComponentID_TBL.clear }
157      }
158
159      def self.id2obj(master, id)
160        if master.kind_of?(TkObject)
161          master = master.path
162        else
163          master = master.to_s
164        end
165        ComponentID_TBL.mutex.synchronize{
166          if ComponentID_TBL.key?(master)
167            (ComponentID_TBL[master].key?(id))? ComponentID_TBL[master][id]: id
168          else
169            id
170          end
171        }
172      end
173
174      def self.new(master, component=nil)
175        if master.kind_of?(TkObject)
176          master = master.path
177        else
178          master = master.to_s
179        end
180
181        if component.kind_of?(Tk::Itk::Component)
182          component = component.name
183        elsif component
184          component = component.to_s
185        else
186          Itk_Component_ID.mutex.synchronize{
187            component = Itk_Component_ID.join(TkCore::INTERP._ip_id_)
188            Itk_Component_ID[1].succ!
189          }
190        end
191
192        ComponentID_TBL.mutex.synchronize{
193          if ComponentID_TBL.key?(master)
194            if ComponentID_TBL[master].key?(component)
195              return ComponentID_TBL[master][component]
196            end
197          else
198            ComponentID_TBL[master] = {}
199          end
200        }
201
202        super(master, component)
203      end
204
205      def initialize(master, component)
206        @master = master
207        @component = component
208
209        ComponentID_TBL.mutex.synchronize{
210          ComponentID_TBL[@master][@component] = self
211        }
212
213        begin
214          @widget = window(tk_call(@master, 'component', @component))
215          @path = @widget.path
216        rescue
217          @widget = nil
218          @path = nil
219        end
220      end
221
222      def path
223        unless @path
224          begin
225            @widget = window(tk_call(@master, 'component', @component))
226            @path = @widget.path
227          rescue
228            fail RuntimeError, 'component is not assigned to a widget'
229          end
230        end
231        @path
232      end
233
234      def epath
235        path()
236      end
237
238      def to_eval
239        path()
240      end
241
242      def master
243        @master
244      end
245
246      def name
247        @component
248      end
249
250      def widget
251        unless @widget
252          begin
253            @widget = window(tk_call(@master, 'component', @component))
254            @path = @widget.path
255          rescue
256            fail RuntimeError, 'component is not assigned to a widget'
257          end
258        end
259        @widget
260      end
261
262      def widget_class
263        unless @widget
264          begin
265            @widget = window(tk_call(@master, 'component', @component))
266            @path = @widget.path
267            @widget.classname
268          rescue
269            nil
270          end
271        end
272      end
273
274      def method_missing(id, *args)
275        name = id.id2name
276
277        # try 1 : component command
278        begin
279          return tk_call(@master, 'component', @component, name, *args)
280        rescue
281        end
282
283        # try 2 : component configure
284        len = args.length
285        begin
286          case len
287          when 1
288            if name[-1] == ?=
289              return configure(name[0..-2], args[0])
290            else
291              return configure(name, args[0])
292            end
293          when 0
294            return cget(name)
295          end
296        rescue
297        end
298
299        # try 3 : widget method or widget configure
300        begin
301          unless @widget
302            @widget = window(tk_call(@master, 'component', @component))
303            @path = @widget.path
304          end
305          @widget.__send__(id, *args)
306        rescue
307        end
308
309        # unknown method
310        super(id, *args)
311        # fail RuntimeError, "unknown method '#{name}' for #{self.inspect}"
312      end
313
314      def tk_send(cmd, *rest)
315        begin
316          tk_call(@master, 'component', @component, cmd, *rest)
317        rescue
318          unless @path
319            begin
320              @widget = window(tk_call(@master, 'component', @component))
321              @path = @widget.path
322            rescue
323              fail RuntimeError, 'component is not assigned to a widget'
324            end
325          end
326          tk_call(@path, cmd, *rest)
327        end
328      end
329
330      def tk_send_without_enc(cmd, *rest)
331        begin
332          tk_call_without_enc(@master, 'component', @component, cmd, *rest)
333        rescue
334          unless @path
335            begin
336              @widget = window(tk_call(@master, 'component', @component))
337              @path = @widget.path
338            rescue
339              fail RuntimeError, 'component is not assigned to a widget'
340            end
341          end
342          tk_call_without_enc(@path, cmd, *rest)
343        end
344      end
345
346      def tk_send_with_enc(cmd, *rest)
347        begin
348          tk_call_with_enc(@master, 'component', @component, cmd, *rest)
349        rescue
350          unless @path
351            begin
352              @widget = window(tk_call(@master, 'component', @component))
353              @path = @widget.path
354            rescue
355              fail RuntimeError, 'component is not assigned to a widget'
356            end
357          end
358          tk_call_with_enc(@path, cmd, *rest)
359        end
360      end
361
362      #def bind(*args)
363      #  unless @widget
364      #    begin
365      #      @widget = window(tk_call(@master, 'component', @component))
366      #      @path = @widget.path
367      #    rescue
368      #      fail RuntimeError, 'component is not assigned to a widget'
369      #    end
370      #  end
371      #  @widget.bind(*args)
372      #end
373      def bind(context, *args)
374        unless @widget
375          begin
376            @widget = window(tk_call(@master, 'component', @component))
377            @path = @widget.path
378          rescue
379            fail RuntimeError, 'component is not assigned to a widget'
380          end
381        end
382        # if args[0].kind_of?(Proc) || args[0].kind_of?(Method)
383        if TkComm._callback_entry?(args[0]) || !block_given?
384          cmd = args.shift
385        else
386          cmd = Proc.new
387        end
388        @widget.bind(context, cmd, *args)
389      end
390
391      #def bind_append(*args)
392      #  unless @widget
393      #    begin
394      #      @widget = window(tk_call(@master, 'component', @component))
395      #      @path = @widget.path
396      #    rescue
397      #      fail RuntimeError, 'component is not assigned to a widget'
398      #    end
399      #  end
400      #  @widget.bind_append(*args)
401      #end
402      def bind_append(context, *args)
403        unless @widget
404          begin
405            @widget = window(tk_call(@master, 'component', @component))
406            @path = @widget.path
407          rescue
408            fail RuntimeError, 'component is not assigned to a widget'
409          end
410        end
411        # if args[0].kind_of?(Proc) || args[0].kind_of?(Method)
412        if TkComm._callback_entry?(args[0]) || !block_given?
413          cmd = args.shift
414        else
415          cmd = Proc.new
416        end
417        @widget.bind_append(context, cmd, *args)
418      end
419
420      def bind_remove(*args)
421        unless @widget
422          begin
423            @widget = window(tk_call(@master, 'component', @component))
424            @path = @widget.path
425          rescue
426            fail RuntimeError, 'component is not assigned to a widget'
427          end
428        end
429        @widget.bind_remove(*args)
430      end
431
432      def bindinfo(*args)
433        unless @widget
434          begin
435            @widget = window(tk_call(@master, 'component', @component))
436            @path = @widget.path
437          rescue
438            fail RuntimeError, 'component is not assigned to a widget'
439          end
440        end
441        @widget.bindinfo(*args)
442      end
443
444    end
445  end
446end
447