1#
2#  tkextlib/bwidget/tree.rb
3#                               by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
4#
5
6require 'tk'
7require 'tk/canvas'
8require 'tkextlib/bwidget.rb'
9
10module Tk
11  module BWidget
12    class Tree < TkWindow
13      class Node < TkObject
14      end
15    end
16  end
17end
18
19class Tk::BWidget::Tree
20  include TkItemConfigMethod
21  include Scrollable
22
23  TkCommandNames = ['Tree'.freeze].freeze
24  WidgetClassName = 'Tree'.freeze
25  WidgetClassNames[WidgetClassName] ||= self
26
27  class Event_for_Items < TkEvent::Event
28    def self._get_extra_args_tbl
29      [
30        TkComm.method(:string)   # item idenfier
31      ]
32    end
33  end
34
35  def __strval_optkeys
36    super() << 'crossfill' << 'linesfill'
37  end
38  private :__strval_optkeys
39
40  def __boolval_optkeys
41    super() << 'dragenabled' << 'dropenabled' <<
42      'redraw' << 'selectfill' << 'showlines'
43  end
44  private :__boolval_optkeys
45
46  def __tkvariable_optkeys
47    super() << 'helpvar'
48  end
49  private :__tkvariable_optkeys
50
51  def tagid(tag)
52    if tag.kind_of?(Tk::BWidget::Tree::Node)
53      tag.id
54    else
55      # tag
56      _get_eval_string(tag)
57    end
58  end
59
60  def areabind(context, *args)
61    if TkComm._callback_entry?(args[0]) || !block_given?
62      cmd = args.shift
63    else
64      cmd = Proc.new
65    end
66    _bind_for_event_class(Event_for_Items, [path, 'bindArea'],
67                          context, cmd, *args)
68    self
69  end
70
71  def areabind_append(context, *args)
72    if TkComm._callback_entry?(args[0]) || !block_given?
73      cmd = args.shift
74    else
75      cmd = Proc.new
76    end
77    _bind_append_for_event_class(Event_for_Items, [path, 'bindArea'],
78                                 context, cmd, *args)
79    self
80  end
81
82  def areabind_remove(*args)
83    _bind_remove_for_event_class(Event_for_Items, [path, 'bindArea'], *args)
84    self
85  end
86
87  def areabindinfo(*args)
88    _bindinfo_for_event_class(Event_for_Items, [path, 'bindArea'], *args)
89  end
90
91  #def imagebind(*args)
92  #  _bind_for_event_class(Event_for_Items, [path, 'bindImage'], *args)
93  #  self
94  #end
95  def imagebind(context, *args)
96    #if args[0].kind_of?(Proc) || args[0].kind_of?(Method)
97    if TkComm._callback_entry?(args[0]) || !block_given?
98      cmd = args.shift
99    else
100      cmd = Proc.new
101    end
102    _bind_for_event_class(Event_for_Items, [path, 'bindImage'],
103                          context, cmd, *args)
104    self
105  end
106
107  #def imagebind_append(*args)
108  #  _bind_append_for_event_class(Event_for_Items, [path, 'bindImage'], *args)
109  #  self
110  #end
111  def imagebind_append(context, *args)
112    #if args[0].kind_of?(Proc) || args[0].kind_of?(Method)
113    if TkComm._callback_entry?(args[0]) || !block_given?
114      cmd = args.shift
115    else
116      cmd = Proc.new
117    end
118    _bind_append_for_event_class(Event_for_Items, [path, 'bindImage'],
119                                 context, cmd, *args)
120    self
121  end
122
123  def imagebind_remove(*args)
124    _bind_remove_for_event_class(Event_for_Items, [path, 'bindImage'], *args)
125    self
126  end
127
128  def imagebindinfo(*args)
129    _bindinfo_for_event_class(Event_for_Items, [path, 'bindImage'], *args)
130  end
131
132  #def textbind(*args)
133  #  _bind_for_event_class(Event_for_Items, [path, 'bindText'], *args)
134  #  self
135  #end
136  def textbind(context, *args)
137    #if args[0].kind_of?(Proc) || args[0].kind_of?(Method)
138    if TkComm._callback_entry?(args[0]) || !block_given?
139      cmd = args.shift
140    else
141      cmd = Proc.new
142    end
143    _bind_for_event_class(Event_for_Items, [path, 'bindText'],
144                          context, cmd, *args)
145    self
146  end
147
148  #def textbind_append(*args)
149  #  _bind_append_for_event_class(Event_for_Items, [path, 'bindText'], *args)
150  #  self
151  #end
152  def textbind_append(context, *args)
153    #if args[0].kind_of?(Proc) || args[0].kind_of?(Method)
154    if TkComm._callback_entry?(args[0]) || !block_given?
155      cmd = args.shift
156    else
157      cmd = Proc.new
158    end
159    _bind_append_for_event_class(Event_for_Items, [path, 'bindText'],
160                                 context, cmd, *args)
161    self
162  end
163
164  def textbind_remove(*args)
165    _bind_remove_for_event_class(Event_for_Items, [path, 'bindText'], *args)
166    self
167  end
168
169  def textbindinfo(*args)
170    _bindinfo_for_event_class(Event_for_Items, [path, 'bindText'], *args)
171  end
172
173  def close_tree(node, recurse=None)
174    tk_send('closetree', tagid(node), recurse)
175    self
176  end
177
178  def delete(*args)
179    tk_send('delete', *(args.collect{|node| tagid(node)}))
180    self
181  end
182
183  def edit(node, text, *args)
184    tk_send('edit', tagid(node), text, *args)
185    self
186  end
187
188  def exist?(node)
189    bool(tk_send('exists', tagid(node)))
190  end
191
192  def find(findinfo, confine=None)
193    Tk::BWidget::Tree::Node.id2obj(self, tk_send(findinfo, confine))
194  end
195  def find_position(x, y, confine=None)
196    self.find(_at(x,y), confine)
197  end
198  def find_line(linenum)
199    self.find(linenum)
200  end
201
202  def index(node)
203    num_or_str(tk_send('index', tagid(node)))
204  end
205
206  def insert(idx, parent, node, keys={})
207    tk_send('insert', idx, tagid(parent), tagid(node), *hash_kv(keys))
208    self
209  end
210
211  def line(node)
212    number(tk_send('line', tagid(node)))
213  end
214
215  def move(parent, node, idx)
216    tk_send('move', tagid(parent), tagid(node), idx)
217    self
218  end
219
220  def get_node(node, idx)
221    Tk::BWidget::Tree::Node.id2obj(self, tk_send('nodes', tagid(node), idx))
222  end
223
224  def nodes(node, first=None, last=None)
225    simplelist(tk_send('nodes', tagid(node), first, last)).collect{|node|
226      Tk::BWidget::Tree::Node.id2obj(self, node)
227    }
228  end
229
230  def open?(node)
231    bool(self.itemcget(tagid(node), 'open'))
232  end
233
234  def open_tree(node, recurse=None)
235    tk_send('opentree', tagid(node), recurse)
236    self
237  end
238
239  def parent(node)
240    Tk::BWidget::Tree::Node.id2obj(self, tk_send('parent', tagid(node)))
241  end
242
243  def reorder(node, neworder)
244    tk_send('reorder', tagid(node), neworder)
245    self
246  end
247
248  def see(node)
249    tk_send('see', tagid(node))
250    self
251  end
252
253  def selection_add(*args)
254    tk_send_without_enc('selection', 'add',
255                        *(args.collect{|node| tagid(node)}))
256    self
257  end
258
259  def selection_clear
260    tk_send_without_enc('selection', 'clear')
261    self
262  end
263
264  def selection_get
265    list(tk_send_without_enc('selection', 'get'))
266  end
267
268  def selection_include?(*args)
269    bool(tk_send_without_enc('selection', 'get',
270                             *(args.collect{|node| tagid(node)})))
271  end
272
273  def selection_range(*args)
274    tk_send_without_enc('selection', 'range',
275                        *(args.collect{|node| tagid(node)}))
276    self
277  end
278
279  def selection_remove(*args)
280    tk_send_without_enc('selection', 'remove',
281                        *(args.collect{|node| tagid(node)}))
282    self
283  end
284
285  def selection_set(*args)
286    tk_send_without_enc('selection', 'set',
287                        *(args.collect{|node| tagid(node)}))
288    self
289  end
290
291  def selection_toggle(*args)
292    tk_send_without_enc('selection', 'toggle',
293                        *(args.collect{|node| tagid(node)}))
294    self
295  end
296
297  def toggle(node)
298    tk_send_without_enc('toggle', tagid(node))
299    self
300  end
301
302  def visible(node)
303    bool(tk_send_without_enc('visible', tagid(node)))
304  end
305end
306
307class Tk::BWidget::Tree::Node
308  include TkTreatTagFont
309
310  TreeNode_TBL = TkCore::INTERP.create_table
311
312  (TreeNode_ID = ['bw:node'.freeze, TkUtil.untrust('00000')]).instance_eval{
313    @mutex = Mutex.new
314    def mutex; @mutex; end
315    freeze
316  }
317
318  TkCore::INTERP.init_ip_env{
319    TreeNode_TBL.mutex.synchronize{ TreeNode_TBL.clear }
320  }
321
322  def self.id2obj(tree, id)
323    tpath = tree.path
324    TreeNode_TBL.mutex.synchronize{
325      if TreeNode_TBL[tpath]
326        TreeNode_TBL[tpath][id]? TreeNode_TBL[tpath][id]: id
327      else
328        id
329      end
330    }
331  end
332
333  def initialize(tree, *args)
334    if tree.kind_of?(Tk::BWidget::Tree)
335      @tree = tree
336      parent = args.shift
337      if parent.kind_of?(Tk::BWidget::Tree::Node)
338        if parent.tree.path != @tree.path
339          fail RuntimeError, 'tree of parent node is not match'
340        end
341      end
342    elsif tree.kind_of?(Tk::BWidget::Tree::Node)
343      @tree = tree.tree
344      parent = tree.parent
345    else
346      fail RuntimeError,
347        "expect Tk::BWidget::Tree or Tk::BWidget::Tree::Node for 1st argument"
348    end
349
350    if args[-1].kind_of?(Hash)
351      keys = _symbolkey2str(args.pop)
352    else
353      keys = {}
354    end
355
356    index = keys.delete('index')
357    unless args.empty?
358      index = args.shift
359    end
360    index = 'end' unless index
361
362    unless args.empty?
363      fail RuntimeError, 'too much arguments'
364    end
365
366    @tpath = @tree.path
367
368    if keys.key?('nodename')
369      @path = @id = keys.delete('nodename')
370    else
371      TreeNode_ID.mutex.synchronize{
372        @path = @id = TreeNode_ID.join(TkCore::INTERP._ip_id_)
373        TreeNode_ID[1].succ!
374      }
375    end
376
377    TreeNode_TBL.mutex.synchronize{
378      TreeNode_TBL[@id] = self
379      TreeNode_TBL[@tpath] = {} unless TreeNode_TBL[@tpath]
380      TreeNode_TBL[@tpath][@id] = self
381    }
382
383    @tree.insert(index, parent, @id, keys)
384  end
385
386  def tree
387    @tree
388  end
389
390  def id
391    @id
392  end
393
394  def [](key)
395    cget(key)
396  end
397
398  def []=(key, val)
399    configure(key, val)
400    val
401  end
402
403  def cget_tkstring(key)
404    @tree.itemcget_tkstring(@id, key)
405  end
406  def cget(key)
407    @tree.itemcget(@id, key)
408  end
409  def cget_strict(key)
410    @tree.itemcget_strict(@id, key)
411  end
412
413  def configure(key, val=None)
414    @tree.itemconfigure(@id, key, val)
415  end
416
417  def configinfo(key=nil)
418    @tree.itemconfiginfo(@id, key)
419  end
420
421  def current_configinfo(key=nil)
422    @tree.current_itemconfiginfo(@id, key)
423  end
424
425  def close_tree(recurse=None)
426    @tree.close_tree(@id, recurse)
427    self
428  end
429
430  def delete
431    @tree.delete(@id)
432    self
433  end
434
435  def edit(*args)
436    @tree.edit(@id, *args)
437    self
438  end
439
440  def exist?
441    @tree.exist?(@id)
442  end
443
444  def index
445    @tree.index(@id)
446  end
447
448  def move(index, parent=nil)
449    if parent
450      @tree.move(parent, @id, index)
451    else
452      @tree.move(self.parent, @id, index)
453    end
454  end
455
456  def open_tree(recurse=None)
457    @tree.open_tree(@id, recurse)
458    self
459  end
460
461  def open?
462    bool(@tree.itemcget(@id, 'open'))
463  end
464
465  def parent
466    @tree.parent(@id)
467  end
468
469  def reorder(neworder)
470    @tree.reorder(@id, neworder)
471  end
472
473  def see
474    @tree.see(@id)
475  end
476
477  def selection_add
478    @tree.selection_add(@id)
479  end
480
481  def selection_remove
482    @tree.selection_remove(@id)
483  end
484
485  def selection_set
486    @tree.selection_set(@id)
487  end
488
489  def selection_toggle
490    @tree.selection_toggle(@id)
491  end
492
493  def toggle
494    @tree.toggle(@id)
495  end
496
497  def visible
498    @tree.visible(@id)
499  end
500end
501