1#
2def random_N
3  @RandomN[0] || 500
4end
5
6#
7# Demo: random N items
8#
9def demoRandom(t)
10  init_pics('folder-*', 'small-*')
11
12  height = t.font.metrics(:linespace)
13  height = 18 if height < 18
14  t.configure(:itemheight=>height, :selectmode=>:extended,
15              :showroot=>true, :showrootbutton=>true, :showbuttons=>true,
16              :showlines=>true, :scrollmargin=>16,
17              :xscrolldelay=>[500, 50], :yscrolldelay=>[500, 50])
18
19  if $Version_1_1_OrLater
20    t.column_create(:expand=>true, :text=>'Item',
21                    :itembackground=>['#e0e8f0', []], :tag=>'item')
22    t.column_create(:text=>'Parent', :justify=>:center,
23                    :itembackground=>['gray90', []], :tag=>'parent')
24    t.column_create(:text=>'Depth', :justify=>:center,
25                    :itembackground=>['linen', []], :tag=>'depth')
26  else # TreeCtrl 1.0
27    t.column_configure(0, :expand=>true, :text=>'Item',
28                       :itembackground=>['#e0e8f0', []], :tag=>'item')
29    t.column_configure(1, :text=>'Parent', :justify=>:center,
30                       :itembackground=>['gray90', []], :tag=>'parent')
31    t.column_configure(2, :text=>'Depth', :justify=>:center,
32                       :itembackground=>['linen', []], :tag=>'depth')
33  end
34
35  t.element_create('e1', :image, :image=>[
36                     @images['folder-open'], ['open'],
37                     @images['folder-closed'], []
38                   ])
39  t.element_create('e2', :image, :image=>@images['small-file'])
40  t.element_create('e3', :text,
41                   :fill=>[@SystemHighlightText, ['selected', 'focus']])
42  t.element_create('e4', :text, :fill=>'blue')
43  t.element_create('e6', :text)
44  t.element_create('e5', :rect, :showfocus=>true,
45                   :fill=>[
46                     @SystemHighlight, ['selected', 'focus'],
47                     'gray', ['selected', '!focus']
48                   ])
49
50  s = t.style_create('s1')
51  t.style_elements(s, ['e5', 'e1', 'e3', 'e4'])
52  t.style_layout(s, 'e1', :padx=>[0,4], :expand=>:ns)
53  t.style_layout(s, 'e3', :padx=>[0,4], :expand=>:ns)
54  t.style_layout(s, 'e4', :padx=>[0,6], :expand=>:ns)
55  t.style_layout(s, 'e5', :union=>['e3'], :iexpand=>:ns, :ipadx=>2)
56
57  s = t.style_create('s2')
58  t.style_elements(s, ['e5', 'e2', 'e3'])
59  t.style_layout(s, 'e2', :padx=>[0,4], :expand=>:ns)
60  t.style_layout(s, 'e3', :padx=>[0,4], :expand=>:ns)
61  t.style_layout(s, 'e5', :union=>['e3'], :iexpand=>:ns, :ipadx=>2)
62
63  s = t.style_create('s3')
64  t.style_elements(s, ['e6'])
65  t.style_layout(s, 'e6', :padx=>6, :expand=>:ns)
66
67  @Priv[:sensitive, t] = [
68    [:item, 's1',  'e5', 'e1', 'e3'],
69    [:item, 's2',  'e5', 'e2', 'e3']
70  ]
71  @Priv[:dragimage, t] = [
72    [:item, 's1',  'e1', 'e3'],
73    [:item, 's2',  'e2', 'e3']
74  ]
75
76  clicks = Tk::Clock.clicks
77  items = [ t.index(:root) ]
78  (1...(random_N())).each{|i|
79    item_i = t.item_create
80    item_j = nil
81    loop {
82      j = rand(i)
83      item_j = items[j]
84      break if t.depth(item_j) < 5
85    }
86    if $Version_1_1_OrLater
87      t.item_collapse(item_i) if rand(2) == 0
88    else # TreeCtrl 1.0
89      t.collapse(item_i) if rand(2) == 0
90    end
91    if rand(2) == 0
92      t.item_lastchild(item_j, item_i)
93    else
94      t.item_firstchild(item_j, item_i)
95    end
96    items << item_i
97  }
98  puts "created #{random_N() - 1} items in #{Tk::Clock.clicks - clicks} clicks"
99
100  clicks = Tk::Clock.clicks
101  (0...(random_N())).each{|i|
102    item_i = items[i]
103    numChildren = t.item_numchildren(item_i)
104    if numChildren > 0
105      if $Version_1_1_OrLater
106        t.item_configure(item_i, :button=>true)
107      else # TreeCtrl 1.0
108        t.item_hasbutton(item_i, true)
109      end
110      t.item_style_set(item_i, 0, 's1', 1, 's3', 2, 's3')
111      t.item_complex(item_i,
112                     [ ['e3', {:text=>"Item #{i}"}],
113                       ['e4', {:text=>"(#{numChildren})"}] ],
114                     [ ['e6', {:text=>"#{t.item_parent(item_i)}"}] ],
115                     [ ['e6', {:text=>"#{t.depth(item_i)}"}] ])
116    else
117      t.item_style_set(item_i, 1, 's3', 2, 's3', 0, 's2')
118      t.item_complex(item_i,
119                     [ ['e3', {:text=>"Item #{i}"}] ],
120                     [ ['e6', {:text=>"#{t.item_parent(item_i)}"}] ],
121                     [ ['e6', {:text=>"#{t.depth(item_i)}"}] ])
122    end
123  }
124  puts "configured #{random_N()} items in #{Tk::Clock.clicks - clicks} clicks"
125
126  treeCtrlRandom = TkBindTag.new
127
128  treeCtrlRandom.bind('Double-ButtonPress-1',
129                      proc{|w, x, y|
130                        Tk::TreeCtrl::BindCallback.doubleButton1(w, x, y)
131                        Tk.callback_break
132                      }, '%W %x %y')
133
134  treeCtrlRandom.bind('Control-ButtonPress-1',
135                      proc{|w, x, y|
136                        @Priv['selectMode'] = :toggle
137                        randomButton1(w, x, y)
138                        Tk.callback_break
139                      }, '%W %x %y')
140
141  treeCtrlRandom.bind('Shift-ButtonPress-1',
142                      proc{|w, x, y|
143                        @Priv['selectMode'] = :add
144                        randomButton1(w, x, y)
145                        Tk.callback_break
146                      }, '%W %x %y')
147
148  treeCtrlRandom.bind('ButtonPress-1',
149                      proc{|w, x, y|
150                        @Priv['selectMode'] = :set
151                        randomButton1(w, x, y)
152                        Tk.callback_break
153                      }, '%W %x %y')
154
155  treeCtrlRandom.bind('Button1-Motion',
156                      proc{|w, x, y|
157                        randomMotion1(w, x, y)
158                        Tk.callback_break
159                      }, '%W %x %y')
160
161  treeCtrlRandom.bind('Button1-Leave',
162                      proc{|w, x, y|
163                        randomLeave1(w, x, y)
164                        Tk.callback_break
165                      }, '%W %x %y')
166
167  treeCtrlRandom.bind('ButtonRelease-1',
168                      proc{|w, x, y|
169                        randomRelease1(w, x, y)
170                        Tk.callback_break
171                      }, '%W %x %y')
172
173  t.bindtags = [ t, treeCtrlRandom, Tk::TreeCtrl, t.winfo_toplevel, :all ]
174end
175
176def randomButton1(t, x, y)
177  t.set_focus
178  id = t.identify(x, y)
179  puts id.inspect
180  @Priv['buttonMode'] = ''
181
182  # Click outside any item
183  if id.empty?
184    t.selection_clear
185
186  # Click in header
187  elsif id[0] == 'header'
188    Tk::TreeCtrl::BindCallback.buttonPress1(t, x, y)
189
190  # Click in item
191  else
192    where, item, arg1, arg2, arg3, arg4 = id
193    case arg1
194    when 'button'
195      if $Version_1_1_OrLater
196        t.item_toggle(item)
197      else # TreeCtrl 1.0
198        t.toggle(item)
199      end
200
201    when 'line'
202      if $Version_1_1_OrLater
203        t.item_toggle(arg2)
204      else # TreeCtrl 1.0
205        t.toggle(arg2)
206      end
207
208    when 'column'
209      ok = false
210      # Clicked an element
211      if id.length == 6
212        column = id[3]
213        e = id[5]
214        @Priv.list_element(:sensitive, t).each{|lst|
215          c, s, *eList = TkComm.simplelist(lst)
216          next if column != t.column_index(c)
217          next if t.item_style_set(item, c) != s
218          next if eList.find{|le| le == e} == nil
219          ok = true
220          break
221        }
222      end
223      unless ok
224        t.selection_clear
225        return
226      end
227
228      @Priv[:drag, :motion] = 0
229      @Priv[:drag, :x] = t.canvasx(x)
230      @Priv[:drag, :y] = t.canvasy(y)
231      @Priv[:drop] = ''
232
233      if @Priv['selectMode'] == 'add'
234          Tk::TreeCtrl::BindCallback.beginExtend(t, item)
235      elsif @Priv['selectMode'] == 'toggle'
236          Tk::TreeCtrl::BindCallback.beginToggle(t, item)
237      elsif ! t.selection_includes(item)
238          Tk::TreeCtrl::BindCallback.beginSelect(t, item)
239      end
240      t.activate(item)
241
242      if t.selection_includes(item)
243        @Priv['buttonMode'] = 'drag'
244      end
245    end
246  end
247end
248
249def randomMotion1(t, x, y)
250  case @Priv['buttonMode']
251  when 'resize', 'header'
252    Tk::TreeCtrl::BindCallback.motion1(t, x, y)
253  when 'drag'
254    randomAutoScanCheck(t, x, y)
255    randomMotion(t, x, y)
256  end
257end
258
259def randomMotion(t, x, y)
260  case @Priv['buttonMode']
261  when 'resize', 'header'
262    Tk::TreeCtrl::BindCallback.motion1(t, x, y)
263
264  when 'drag'
265    # Detect initial mouse movement
266    unless @Priv.bool_element(:drag, :motion)
267      @Priv[:selection] = t.selection_get
268      @Priv[:drop] = ''
269      t.dragimage_clear
270      # For each selected item, add 2nd and 3rd elements of
271      # column "item" to the dragimage
272      @Priv.list_element(:selection).each{|i|
273        @Priv.list_element(:dragimage,t).each{|lst|
274          c, s, *eList = TkComm.simplelist(lst)
275          if t.item_style_set(i, c) == s
276            t.dragimage_add(i, c, *eList)
277          end
278        }
279      }
280      @Priv[:drag,:motion] = true
281    end
282
283    # Find the item under the cursor
284    cursor = 'X_cursor'
285    drop = ''
286    id = t.identify(x, y)
287    ok = false
288    if !id.empty? && id[0] == 'item' && id.length == 6
289      item = id[1]
290      column = id[3]
291      e = id[5]
292      @Priv.list_element(:sensitive,t).each{|lst|
293        c, s, *eList = TkComm.simplelist(lst)
294        next if column != t.column_index(c)
295        next if t.item_style_set(item, c) != s
296        next unless eList.find{|val| val.to_s == e.to_s}
297        ok = true
298        break
299      }
300      ok = true if @Priv.list_element(:sensitive,t).find{|val| TkComm.simplelist(val).index(e)}
301    end
302
303    if ok
304      # If the item is not in the pre-drag selection
305      # (i.e. not being dragged) see if we can drop on it
306      unless @Priv.list_element(:selection).find{|val| val.to_s == item.to_s}
307        drop = item
308        # We can drop if dragged item isn't an ancestor
309        @Priv.list_element(:selection).each{|item2|
310          if t.item_isancestor(item2, item)
311            drop = ''
312            break
313          end
314        }
315        if drop != ''
316          x1, y1, x2, y2 = t.item_bbox(drop)
317          if y < y1 + 3
318            cursor = 'top_side'
319            @Priv[:drop,:pos] = 'prevsibling'
320          elsif y >= y2 - 3
321            cursor = 'bottom_side'
322            @Priv[:drop,:pos] = 'nextsibling'
323          else
324            cursor = ''
325            @Priv[:drop,:pos] = 'lastchild'
326          end
327        end
328      end
329    end
330
331    t[:cursor] = cursor if t[:cursor] != cursor
332
333    # Select the item under the cursor (if any) and deselect
334    # the previous drop-item (if any)
335    t.selection_modify(drop, @Priv[:drop])
336    @Priv[:drop] = drop
337
338    # Show the dragimage in its new position
339    x = t.canvasx(x) - @Priv.numeric_element(:drag,:x)
340    y = t.canvasx(y) - @Priv.numeric_element(:drag,:y)
341    t.dragimage_offset(x, y)
342    t.dragimage_configure(:visible=>true)
343  end
344end
345
346def randomLeave1(t, x, y)
347  # This is called when I do ButtonPress-1 on Unix for some reason,
348  # and buttonMode is undefined.
349  return unless @Priv.exist?('buttonMode')
350  case @Priv['buttonMode']
351  when 'header'
352    Tk::TreeCtrl::BindCallback.leave1(t, x, y)
353  end
354end
355
356def randomRelease1(t, x, y)
357  case @Priv['buttonMode']
358  when 'resize', 'header'
359    Tk::TreeCtrl::BindCallback.release1(t, x, y)
360  when 'drag'
361    Tk::TreeCtrl::BindCallback.autoScanCancel(t)
362    t.dragimage_configure(:visible=>false)
363    t.selection_modify('', @Priv[:drop])
364    t[:cursor] = ''
365    if @Priv[:drop] != ''
366      randomDrop(t, @Priv[:drop], @Priv.list_element(:selection),
367                 @Priv[:drop, :pos])
368    end
369  end
370  @Priv['buttonMode'] = ''
371end
372
373def randomDrop(t, target, src, pos)
374  parentList = []
375  case pos
376  when 'lastchild'
377    parent = target
378  when 'prevsibling'
379    parent = t.item_parent(target)
380  when 'nextsibling'
381    parent = t.item_parent(target)
382  end
383  src.each{|item|
384    # Ignore any item whose ancestor is also selected
385    ignore = false
386    t.item_ancestors(item).each{|ancestor|
387      if src.find{|val| val.to_s == ancestor.to_s}
388        ignore = true
389        break
390      end
391    }
392    next if ignore
393
394    # Update the old parent of this moved item later
395    unless parentList.find{|val| val.to_s == item.to_s}
396      parentList << t.item_parent(item)
397    end
398
399    # Add to target
400    t.__send__("item_#{pos}", target, item)
401
402    # Update text: parent
403    t.item_element_configure(item, 'parent', 'e6', :text=>parent)
404
405    # Update text: depth
406    t.item_element_configure(item, 'depth', 'e6', :text=>t.depth(item))
407
408    # Recursively update text: depth
409    itemList = []
410    item = t.item_firstchild(item)
411    itemList << item if item != ''
412
413    while item = itemList.pop
414      t.item_element_configure(item, 'depth', 'e6', :text=>t.depth(item))
415
416      item2 = t.item_nextsibling(item)
417      itemList << item2 if item2 != ''
418
419      item2 = t.item_firstchild(item)
420      itemList << item2 if item2 != ''
421    end
422  }
423
424  # Update items that lost some children
425  parentList.each{|item|
426    numChildren = t.item_numchildren(item)
427    if numChildren == 0
428      if $Version_1_1_OrLater
429        t.item_configure(item, :button=>false)
430      else # TreeCtrl 1.0
431        t.item_hasbutton(item, false)
432      end
433      t.item_style_map(item, 'item', 's2', ['e3', 'e3'])
434    else
435      t.item_element_configure(item, 'item', 'e4', :text=>"(#{numChildren})")
436    end
437  }
438
439  # Update the target that gained some children
440  if t.item_style_set(parent, 0) != 's1'
441    if $Version_1_1_OrLater
442      t.item_configure(parent, :button=>true)
443    else # TreeCtrl 1.0
444      t.item_hasbutton(parent, true)
445    end
446    t.item_style_map(parent, 'item', 's1', ['e3', 'e3'])
447  end
448  numChildren = t.item_numchildren(parent)
449  t.item_element_configure(parent, 'item', 'e4', :text=>"(#{numChildren})")
450end
451
452# Same as TreeCtrl::AutoScanCheck, but calls RandomMotion and
453# RandomAutoScanCheckAux
454def randomAutoScanCheck(t, x, y)
455  x1, y1, x2, y2 = t.contentbox
456  margin = t.winfo_pixels(t.scrollmargin)
457  if x < x1 + margin || x >= x2 - margin || y < y1 + margin || y >= y2 - margin
458    if ! @Priv.exist?(:autoscan, :afterId, t)
459      if y >= y2 - margin
460        t.yview(:scroll, 1, :units)
461        delay = t.yscrolldelay
462      elsif y < y1 + margin
463        t.yview(:scroll, -1, :units)
464        delay = t.yscrolldelay
465      elsif x >= x2 - margin
466        t.xview(:scroll, 1, :units)
467        delay = t.xscrolldelay
468      elsif x < x1 + margin
469        t.xview(:scroll, -1, :units)
470        delay = t.xscrolldelay
471      end
472      if @Priv.exist?(:autoscan, :scanning, t)
473        delay = delay[1] if delay.kind_of?(Array)
474      else
475        delay = delay[0] if delay.kind_of?(Array)
476        @Priv[:autoscan, :scanning, t] = true
477      end
478      case @Priv['buttonMode']
479      when 'drag', 'marquee'
480        randomMotion(t, x, y)
481      end
482      @Priv[:autoscan, :afterId, t] =
483        Tk.after(delay, proc{ randomAutoScanCheckAux(t) })
484    end
485    return
486  end
487  Tk::TreeCtrl::BindCallback.autoScanCancel(t)
488end
489
490def randomAutoScanCheckAux(t)
491  @Priv.unset(:autoscan, :afterId, t)
492  x = t.winfo_pointerx - t.winfo_rootx
493  y = t.winfo_pointery - t.winfo_rooty
494  randomAutoScanCheck(t, x, y)
495end
496
497#
498# Demo: random N items, button images
499#
500def demoRandom2(t)
501  demoRandom(t)
502
503  init_pics('mac-*')
504
505  t.configure(:openbuttonimage=>@images['mac-collapse'],
506              :closedbuttonimage=>@images['mac-expand'],
507              :showlines=>false)
508end
509