1#
2#  tkextlib/blt/tabset.rb
3#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
4#
5
6require 'tk'
7require 'tkextlib/blt.rb'
8
9module Tk::BLT
10  class Tabset < TkWindow
11    class Tab < TkObject
12      include TkTreatItemFont
13
14      TabID_TBL = TkCore::INTERP.create_table
15
16      (TabsetTab_ID = ['blt_tabset_tab'.freeze, TkUtil.untrust('00000')]).instance_eval{
17        @mutex = Mutex.new
18        def mutex; @mutex; end
19        freeze
20      }
21
22      TkCore::INTERP.init_ip_env{
23        TabID_TBL.mutex.synchronize{ TabID_TBL.clear }
24      }
25
26      def self.id2obj(tabset, id)
27        tpath = tabset.path
28        TabID_TBL.mutex.synchronize{
29          if TabID_TBL[tpath]
30            TabID_TBL[tpath][id]? TabID_TBL[tpath][id]: id
31          else
32            id
33          end
34        }
35      end
36
37      def self.new(parent, pos=nil, name=nil, keys={})
38        if pos.kind_of?(Hash)
39          keys = pos
40          name = nil
41          pos  = nil
42        end
43        if name.kind_of?(Hash)
44          keys = name
45          name = nil
46        end
47        obj = nil
48        TabID_TBL.mutex.synchronize{
49          if name && TabID_TBL[parent.path] && TabID_TBL[parent.path][name]
50            obj = TabID_TBL[parent.path][name]
51            if pos
52              if pos.to_s == 'end'
53                obj.move_after('end')
54              else
55                obj.move_before(pos)
56              end
57            end
58            obj.configure if keys && ! keys.empty?
59          else
60            (obj = self.allocate).instance_eval{
61              initialize(parent, pos, name, keys)
62              TabID_TBL[@tpath] = {} unless TabID_TBL[@tpath]
63              TabID_TBL[@tpath][@id] = self
64            }
65          end
66        }
67        obj
68      end
69
70      def initialize(parent, pos, name, keys)
71        @t = parent
72        @tpath = parent.path
73        if name
74          @path = @id = name
75          unless (list(tk_call(@tpath, 'tab', 'names', @id)).empty?)
76            if pos
77              idx = tk_call(@tpath, 'index', '-name', @id)
78              if pos.to_s == 'end'
79                tk_call(@tpath, 'move', idx, 'after', 'end')
80              else
81                tk_call(@tpath, 'move', idx, 'before', pos)
82              end
83            end
84            tk_call(@tpath, 'tab', 'configure', @id, keys)
85          else
86            pos = 'end' unless pos
87            tk_call(@tpath, 'insert', pos, @id, keys)
88          end
89        else
90          pos = 'end' unless pos
91          TabsetTab_ID.mutex.synchronize{
92            @path = @id = TabsetTab_ID.join(TkCore::INTERP._ip_id_)
93            TabsetTab_ID[1].succ!
94          }
95          tk_call(@tpath, 'insert', pos, @id, keys)
96        end
97      end
98
99      #def bind(context, cmd=Proc.new, *args)
100      #  @t.tab_bind(@id, context, cmd, *args)
101      #  self
102      #end
103      def bind(context, *args)
104        # if args[0].kind_of?(Proc) || args[0].kind_of?(Method)
105        if TkComm._callback_entry?(args[0]) || !block_given?
106          cmd = args.shift
107        else
108          cmd = Proc.new
109        end
110        @t.tab_bind(@id, context, cmd, *args)
111        self
112      end
113      #def bind_append(context, cmd=Proc.new, *args)
114      #  @t.tab_bind_append(@id, context, cmd, *args)
115      #  self
116      #end
117      def bind_append(context, *args)
118        # if args[0].kind_of?(Proc) || args[0].kind_of?(Method)
119        if TkComm._callback_entry?(args[0]) || !block_given?
120          cmd = args.shift
121        else
122          cmd = Proc.new
123        end
124        @t.tab_bind_append(@id, context, cmd, *args)
125        self
126      end
127      def bind_remove(context)
128        @t.tab_bind_remove(@id, context)
129        self
130      end
131      def bindinfo(context=nil)
132        @t.tab_bindinfo(@id, context)
133      end
134
135      def cget_tkstring(*args)
136        @t.tab_cget_tkstring(@id, *args)
137      end
138      def cget(*args)
139        @t.tab_cget(@id, *args)
140      end
141      def cget_strict(*args)
142        @t.tab_cget_strict(@id, *args)
143      end
144      def configure(*args)
145        @t.tab_configure(@id, *args)
146      end
147      def configinfo(*args)
148        @t.tab_configinfo(@id, *args)
149      end
150      def current_configinfo(*args)
151        @t.current_tab_configinfo(@id, *args)
152      end
153
154      def delete()
155        @t.delete(@id)
156        TabID_TBL.mutex.synchronize{
157          TabID_TBL[@tpath].delete(@id)
158        }
159        self
160      end
161
162      def get_name()
163        @id.dup
164      end
165
166      def focus()
167        @t.focus(self.index)
168      end
169
170      def index()
171        @t.index_name(@id)
172      end
173
174      def invoke()
175        @t.invoke(self.index)
176      end
177
178      def move_before(idx)
179        @t.move_before(self.index, idx)
180      end
181      def move_after(idx)
182        @t.move_after(self.index, idx)
183      end
184
185      def perforation_highlight(mode)
186        @t.perforation_highlight(self.index, mode)
187      end
188      def perforation_invoke()
189        @t.perforation_invoke(self.index)
190      end
191
192      def see()
193        @t.see(self.index)
194      end
195
196      def tearoff(name=None)
197        @t.tab_tearoff(self.index, *args)
198      end
199    end
200
201    ########################################
202
203    class NamedTab < Tab
204      def self.new(parent, name)
205        super(parent, nil, name, {})
206      end
207    end
208
209    ########################################
210
211    include X_Scrollable
212    include TkItemConfigMethod
213
214    TkCommandNames = ['::blt::tabset'.freeze].freeze
215    WidgetClassName = 'Tabset'.freeze
216    WidgetClassNames[WidgetClassName] ||= self
217
218    def __destroy_hook__
219      Tk::BLT::Tabset::Tab::TabID_TBL.mutex.synchronize{
220        Tk::BLT::Tabset::Tab::TabID_TBL.delete(@path)
221      }
222    end
223
224    ########################################
225
226    def __boolval_optkeys
227      super() << 'samewidth' << 'tearoff'
228    end
229    private :__strval_optkeys
230
231    def __strval_optkeys
232      super() << 'tabbackground' << 'tabforeground'
233    end
234    private :__strval_optkeys
235
236    def __item_cget_cmd(id)
237      [self.path, 'tab', 'cget', id]
238    end
239    private :__item_cget_cmd
240
241    def __item_config_cmd(id)
242      [self.path, 'tab', 'configure', id]
243    end
244    private :__item_config_cmd
245
246    def __item_pathname(tagOrId)
247      if tagOrId.kind_of?(Tk::BLT::Tabset::Tab)
248        self.path + ';' + tagOrId.id.to_s
249      else
250        self.path + ';' + tagOrId.to_s
251      end
252    end
253    private :__item_pathname
254
255    alias tab_cget_tkstring itemcget_tkstring
256    alias tab_cget itemcget
257    alias tab_cget_strict itemcget_strict
258    alias tab_configure itemconfigure
259    alias tab_configinfo itemconfiginfo
260    alias current_tab_configinfo current_itemconfiginfo
261
262    def __item_strval_optkeys(id)
263      super(id) << 'shadow'
264    end
265    private :__item_strval_optkeys
266
267    def tagid(tab)
268      if tab.kind_of?(Tk::BLT::Tabset::Tab)
269        tab.id
270      else
271        tab
272      end
273    end
274
275    def tagindex(tab)
276      if tab.kind_of?(Tk::BLT::Tabset::Tab)
277        tab.index
278      else
279        tab
280      end
281    end
282
283    ########################################
284
285    def activate(index)
286      tk_send('activate', tagindex(index))
287      self
288    end
289    alias highlight activate
290
291    #def tabbind(tag, context, cmd=Proc.new, *args)
292    #  _bind([path, "bind", tagid(tag)], context, cmd, *args)
293    #  self
294    #end
295    def tabbind(tag, context, *args)
296      # if args[0].kind_of?(Proc) || args[0].kind_of?(Method)
297      if TkComm._callback_entry?(args[0]) || !block_given?
298        cmd = args.shift
299      else
300        cmd = Proc.new
301      end
302      _bind([path, "bind", tagid(tag)], context, cmd, *args)
303      self
304    end
305    #def tabbind_append(tag, context, cmd=Proc.new, *args)
306    #  _bind_append([path, "bind", tagid(tag)], context, cmd, *args)
307    #  self
308    #end
309    def tabbind_append(tag, context, *args)
310      # if args[0].kind_of?(Proc) || args[0].kind_of?(Method)
311      if TkComm._callback_entry?(args[0]) || !block_given?
312        cmd = args.shift
313      else
314        cmd = Proc.new
315      end
316      _bind_append([path, "bind", tagid(tag)], context, cmd, *args)
317      self
318    end
319    def tabbind_remove(tag, context)
320      _bind_remove([path, "bind", tagid(tag)], context)
321      self
322    end
323    def tabbindinfo(tag, context=nil)
324      _bindinfo([path, "bind", tagid(tag)], context)
325    end
326
327    def delete(first, last=None)
328      tk_send('delete', tagindex(first), tagindex(last))
329      if first.kind_of?(Tk::BLT::Tabset::Tab)
330        TabID_TBL.mutex.synchronize{
331          TabID_TBL[@path].delete(first.id)
332        }
333      end
334      # middle tabs of the range are unknown
335      if last.kind_of?(Tk::BLT::Tabset::Tab)
336        TabID_TBL.mutex.synchronize{
337          TabID_TBL[@path].delete(last.id)
338        }
339      end
340      self
341    end
342
343    def focus(index)
344      tk_send('focus', tagindex(index))
345      self
346    end
347
348    def get_tab(index)
349      if (idx = tk_send_without_enc('get', tagindex(index))).empty?
350        nil
351      else
352        Tk::BLT::Tabset::Tab.id2obj(self, idx)
353      end
354    end
355    def get_tabobj(index)
356      if (idx = tk_send_without_enc('get', tagindex(index))).empty?
357        nil
358      else
359       Tk::BLT::Tabset::Tab.new(self, nil, name, {})
360      end
361    end
362
363    def index(str)
364      num_or_str(tk_send('index', str))
365    end
366    def index_name(tab)
367      num_or_str(tk_send('index', '-name', tagid(tab)))
368    end
369
370    def insert(pos, tab, keys={})
371      pos = 'end' if pos.nil?
372      Tk::BLT::Tabset::Tab.new(self, tagindex(pos), tagid(tab), keys)
373    end
374    def insert_tabs(pos, *tabs)
375      pos = 'end' if pos.nil?
376      if tabs[-1].kind_of?(Hash)
377        keys = tabs.pop
378      else
379        keys = {}
380      end
381      fail ArgumentError, 'no tabs is given' if tabs.empty?
382      tabs.map!{|tab| tagid(tab)}
383      tk_send('insert', tagindex(pos), *(tabs + [keys]))
384      tabs.collect{|tab| Tk::BLT::Tabset::Tab.new(self, nil, tagid(tab))}
385    end
386
387    def invoke(index)
388      tk_send('invoke', tagindex(index))
389    end
390
391    def move_before(index, base_idx)
392      tk_send('move', tagindex(index), 'before', tagindex(base_idx))
393      self
394    end
395    def move_after(index, base_idx)
396      tk_send('move', tagindex(index), 'after', tagindex(base_idx))
397      self
398    end
399
400    def nearest(x, y)
401      Tk::BLT::Tabset::Tab.id2obj(self, num_or_str(tk_send_without_enc('nearest', x, y)))
402    end
403
404    def perforation_activate(mode)
405      tk_send('perforation', 'activate', mode)
406      self
407    end
408    def perforation_highlight(index, *args)
409      if args.empty?
410        # index --> mode
411        tk_send('perforation', 'highlight', index)
412      elsif args.size == 1
413        # args[0] --> mode
414        tk_send('perforation', 'highlight', tagindex(index), args[0])
415      else # Error: call to get Tcl's error message
416        tk_send('perforation', 'highlight', tagindex(index), *args)
417      end
418      self
419    end
420    def perforation_invoke(index=nil)
421      if index
422        tk_send('perforation', 'invoke', tagindex(index))
423      else
424        tk_send('perforation', 'invoke')
425      end
426    end
427
428    def scan_mark(x, y)
429      tk_send_without_enc('scan', 'mark', x, y)
430      self
431    end
432    def scan_dragto(x, y)
433      tk_send_without_enc('scan', 'dragto', x, y)
434      self
435    end
436
437    def see(index)
438      tk_send('see', tagindex(index))
439      self
440    end
441
442    def size()
443      number(tk_send_without_enc('size'))
444    end
445
446    def select(index)
447      tk_send('select', tagindex(index))
448      self
449    end
450
451    def tab_dockall
452      tk_send('tab', 'dockall')
453      self
454    end
455
456    def tab_names(pat=None)
457      simplelist(tk_send('tab', 'names', pat)).collect{|name|
458        Tk::BLT::Tabset::Tab.id2obj(self, name)
459      }
460    end
461
462    def tab_objs(pat=None)
463      simplelist(tk_send('tab', 'names', pat)).collect{|name|
464        Tk::BLT::Tabset::Tab.new(self, nil, name, {})
465      }
466    end
467
468    def tab_ids(pat=None)
469      simplelist(tk_send('tab', 'names', pat))
470    end
471
472    def tab_pageheight
473      number(tk_send('tab', 'pageheight'))
474    end
475
476    def tab_pagewidth
477      number(tk_send('tab', 'pagewidth'))
478    end
479
480    def tab_tearoff(index, parent=None)
481      window(tk_send('tab', 'tearoff', tagindex(index), parent))
482    end
483
484    def xscrollcommand(cmd=Proc.new)
485      configure_cmd 'scrollcommand', cmd
486      self
487    end
488    alias scrollcommand xscrollcommand
489
490    def xview(*index)
491      if index.empty?
492        list(tk_send_without_enc('view'))
493      else
494        tk_send_without_enc('view', *index)
495        self
496      end
497    end
498    alias view xview
499    alias view_moveto xview_moveto
500    alias view_scroll xview_scroll
501
502    alias scrollbar xscrollbar
503  end
504end
505