# 
def random_N
  @RandomN[0] || 500
end

#
# Demo: random N items
#
def demoRandom(t)
  init_pics('folder-*', 'small-*')

  height = t.font.metrics(:linespace)
  height = 18 if height < 18
  t.configure(:itemheight=>height, :selectmode=>:extended, 
              :showroot=>true, :showrootbutton=>true, :showbuttons=>true, 
              :showlines=>true, :scrollmargin=>16, 
              :xscrolldelay=>[500, 50], :yscrolldelay=>[500, 50])

  if $Version_1_1_OrLater
    t.column_create(:expand=>true, :text=>'Item', 
                    :itembackground=>['#e0e8f0', []], :tag=>'item')
    t.column_create(:text=>'Parent', :justify=>:center, 
                    :itembackground=>['gray90', []], :tag=>'parent')
    t.column_create(:text=>'Depth', :justify=>:center, 
                    :itembackground=>['linen', []], :tag=>'depth')
  else # TreeCtrl 1.0
    t.column_configure(0, :expand=>true, :text=>'Item', 
                       :itembackground=>['#e0e8f0', []], :tag=>'item')
    t.column_configure(1, :text=>'Parent', :justify=>:center, 
                       :itembackground=>['gray90', []], :tag=>'parent')
    t.column_configure(2, :text=>'Depth', :justify=>:center, 
                       :itembackground=>['linen', []], :tag=>'depth')
  end

  t.element_create('e1', :image, :image=>[
                     @images['folder-open'], ['open'], 
                     @images['folder-closed'], []
                   ])
  t.element_create('e2', :image, :image=>@images['small-file'])
  t.element_create('e3', :text, 
                   :fill=>[@SystemHighlightText, ['selected', 'focus']])
  t.element_create('e4', :text, :fill=>'blue')
  t.element_create('e6', :text)
  t.element_create('e5', :rect, :showfocus=>true, 
                   :fill=>[
                     @SystemHighlight, ['selected', 'focus'], 
                     'gray', ['selected', '!focus']
                   ])

  s = t.style_create('s1')
  t.style_elements(s, ['e5', 'e1', 'e3', 'e4'])
  t.style_layout(s, 'e1', :padx=>[0,4], :expand=>:ns)
  t.style_layout(s, 'e3', :padx=>[0,4], :expand=>:ns)
  t.style_layout(s, 'e4', :padx=>[0,6], :expand=>:ns)
  t.style_layout(s, 'e5', :union=>['e3'], :iexpand=>:ns, :ipadx=>2)

  s = t.style_create('s2')
  t.style_elements(s, ['e5', 'e2', 'e3'])
  t.style_layout(s, 'e2', :padx=>[0,4], :expand=>:ns)
  t.style_layout(s, 'e3', :padx=>[0,4], :expand=>:ns)
  t.style_layout(s, 'e5', :union=>['e3'], :iexpand=>:ns, :ipadx=>2)

  s = t.style_create('s3')
  t.style_elements(s, ['e6'])
  t.style_layout(s, 'e6', :padx=>6, :expand=>:ns)

  @Priv[:sensitive, t] = [
    [:item, 's1',  'e5', 'e1', 'e3'], 
    [:item, 's2',  'e5', 'e2', 'e3']
  ]
  @Priv[:dragimage, t] = [
    [:item, 's1',  'e1', 'e3'], 
    [:item, 's2',  'e2', 'e3']
  ]

  clicks = Tk::Clock.clicks
  items = [ t.index(:root) ]
  (1...(random_N())).each{|i|
    item_i = t.item_create
    item_j = nil
    loop {
      j = rand(i)
      item_j = items[j]
      break if t.depth(item_j) < 5
    }
    if $Version_1_1_OrLater
      t.item_collapse(item_i) if rand(2) == 0
    else # TreeCtrl 1.0
      t.collapse(item_i) if rand(2) == 0
    end
    if rand(2) == 0
      t.item_lastchild(item_j, item_i)
    else
      t.item_firstchild(item_j, item_i)
    end
    items << item_i
  }
  puts "created #{random_N() - 1} items in #{Tk::Clock.clicks - clicks} clicks"

  clicks = Tk::Clock.clicks
  (0...(random_N())).each{|i|
    item_i = items[i]
    numChildren = t.item_numchildren(item_i)
    if numChildren > 0
      if $Version_1_1_OrLater
        t.item_configure(item_i, :button=>true)
      else # TreeCtrl 1.0
        t.item_hasbutton(item_i, true)
      end
      t.item_style_set(item_i, 0, 's1', 1, 's3', 2, 's3')
      t.item_complex(item_i, 
                     [ ['e3', {:text=>"Item #{i}"}], 
                       ['e4', {:text=>"(#{numChildren})"}] ], 
                     [ ['e6', {:text=>"#{t.item_parent(item_i)}"}] ], 
                     [ ['e6', {:text=>"#{t.depth(item_i)}"}] ])
    else
      t.item_style_set(item_i, 1, 's3', 2, 's3', 0, 's2')
      t.item_complex(item_i, 
                     [ ['e3', {:text=>"Item #{i}"}] ], 
                     [ ['e6', {:text=>"#{t.item_parent(item_i)}"}] ], 
                     [ ['e6', {:text=>"#{t.depth(item_i)}"}] ])
    end
  }
  puts "configured #{random_N()} items in #{Tk::Clock.clicks - clicks} clicks"

  treeCtrlRandom = TkBindTag.new

  treeCtrlRandom.bind('Double-ButtonPress-1', 
                      proc{|w, x, y|
                        Tk::TreeCtrl::BindCallback.doubleButton1(w, x, y)
                        Tk.callback_break
                      }, '%W %x %y')

  treeCtrlRandom.bind('Control-ButtonPress-1', 
                      proc{|w, x, y|
                        @Priv['selectMode'] = :toggle
                        randomButton1(w, x, y)
                        Tk.callback_break
                      }, '%W %x %y')

  treeCtrlRandom.bind('Shift-ButtonPress-1', 
                      proc{|w, x, y|
                        @Priv['selectMode'] = :add
                        randomButton1(w, x, y)
                        Tk.callback_break
                      }, '%W %x %y')

  treeCtrlRandom.bind('ButtonPress-1', 
                      proc{|w, x, y|
                        @Priv['selectMode'] = :set
                        randomButton1(w, x, y)
                        Tk.callback_break
                      }, '%W %x %y')

  treeCtrlRandom.bind('Button1-Motion', 
                      proc{|w, x, y|
                        randomMotion1(w, x, y)
                        Tk.callback_break
                      }, '%W %x %y')

  treeCtrlRandom.bind('Button1-Leave', 
                      proc{|w, x, y|
                        randomLeave1(w, x, y)
                        Tk.callback_break
                      }, '%W %x %y')

  treeCtrlRandom.bind('ButtonRelease-1', 
                      proc{|w, x, y|
                        randomRelease1(w, x, y)
                        Tk.callback_break
                      }, '%W %x %y')

  t.bindtags = [ t, treeCtrlRandom, Tk::TreeCtrl, t.winfo_toplevel, :all ]
end

def randomButton1(t, x, y)
  t.set_focus
  id = t.identify(x, y)
  puts id.inspect
  @Priv['buttonMode'] = ''

  # Click outside any item
  if id.empty?
    t.selection_clear

  # Click in header
  elsif id[0] == 'header'
    Tk::TreeCtrl::BindCallback.buttonPress1(t, x, y)

  # Click in item
  else
    where, item, arg1, arg2, arg3, arg4 = id
    case arg1
    when 'button'
      if $Version_1_1_OrLater
        t.item_toggle(item)
      else # TreeCtrl 1.0
        t.toggle(item)
      end

    when 'line'
      if $Version_1_1_OrLater
        t.item_toggle(arg2)
      else # TreeCtrl 1.0
        t.toggle(arg2)
      end

    when 'column'
      ok = false
      # Clicked an element
      if id.length == 6
        column = id[3]
        e = id[5]
        @Priv.list_element(:sensitive, t).each{|lst|
          c, s, *eList = TkComm.simplelist(lst)
          next if column != t.column_index(c)
          next if t.item_style_set(item, c) != s
          next if eList.find{|le| le == e} == nil
          ok = true
          break
        }
      end
      unless ok
        t.selection_clear
        return
      end

      @Priv[:drag, :motion] = 0
      @Priv[:drag, :x] = t.canvasx(x)
      @Priv[:drag, :y] = t.canvasy(y)
      @Priv[:drop] = ''

      if @Priv['selectMode'] == 'add'
          Tk::TreeCtrl::BindCallback.beginExtend(t, item)
      elsif @Priv['selectMode'] == 'toggle'
          Tk::TreeCtrl::BindCallback.beginToggle(t, item)
      elsif ! t.selection_includes(item)
          Tk::TreeCtrl::BindCallback.beginSelect(t, item)
      end
      t.activate(item)

      if t.selection_includes(item)
        @Priv['buttonMode'] = 'drag'
      end
    end
  end
end

def randomMotion1(t, x, y)
  case @Priv['buttonMode']
  when 'resize', 'header'
    Tk::TreeCtrl::BindCallback.motion1(t, x, y)
  when 'drag'
    randomAutoScanCheck(t, x, y)
    randomMotion(t, x, y)
  end
end

def randomMotion(t, x, y)
  case @Priv['buttonMode']
  when 'resize', 'header'
    Tk::TreeCtrl::BindCallback.motion1(t, x, y)

  when 'drag'
    # Detect initial mouse movement
    unless @Priv.bool_element(:drag, :motion)
      @Priv[:selection] = t.selection_get
      @Priv[:drop] = ''
      t.dragimage_clear
      # For each selected item, add 2nd and 3rd elements of
      # column "item" to the dragimage
      @Priv.list_element(:selection).each{|i|
        @Priv.list_element(:dragimage,t).each{|lst|
          c, s, *eList = TkComm.simplelist(lst)
          if t.item_style_set(i, c) == s
            t.dragimage_add(i, c, *eList)
          end
        }
      }
      @Priv[:drag,:motion] = true
    end

    # Find the item under the cursor
    cursor = 'X_cursor'
    drop = ''
    id = t.identify(x, y)
    ok = false
    if !id.empty? && id[0] == 'item' && id.length == 6
      item = id[1]
      column = id[3]
      e = id[5]
      @Priv.list_element(:sensitive,t).each{|lst|
        c, s, *eList = TkComm.simplelist(lst)
        next if column != t.column_index(c)
        next if t.item_style_set(item, c) != s
        next unless eList.find{|val| val.to_s == e.to_s}
        ok = true
        break
      }
      ok = true if @Priv.list_element(:sensitive,t).find{|val| TkComm.simplelist(val).index(e)}
    end

    if ok
      # If the item is not in the pre-drag selection
      # (i.e. not being dragged) see if we can drop on it
      unless @Priv.list_element(:selection).find{|val| val.to_s == item.to_s}
        drop = item
        # We can drop if dragged item isn't an ancestor
        @Priv.list_element(:selection).each{|item2|
          if t.item_isancestor(item2, item)
            drop = ''
            break
          end
        }
        if drop != ''
          x1, y1, x2, y2 = t.item_bbox(drop)
          if y < y1 + 3
            cursor = 'top_side'
            @Priv[:drop,:pos] = 'prevsibling'
          elsif y >= y2 - 3
            cursor = 'bottom_side'
            @Priv[:drop,:pos] = 'nextsibling'
          else
            cursor = ''
            @Priv[:drop,:pos] = 'lastchild'
          end
        end
      end
    end

    t[:cursor] = cursor if t[:cursor] != cursor

    # Select the item under the cursor (if any) and deselect
    # the previous drop-item (if any)
    t.selection_modify(drop, @Priv[:drop])
    @Priv[:drop] = drop

    # Show the dragimage in its new position
    x = t.canvasx(x) - @Priv.numeric_element(:drag,:x)
    y = t.canvasx(y) - @Priv.numeric_element(:drag,:y)
    t.dragimage_offset(x, y)
    t.dragimage_configure(:visible=>true)
  end
end

def randomLeave1(t, x, y)
  # This is called when I do ButtonPress-1 on Unix for some reason,
  # and buttonMode is undefined.
  return unless @Priv.exist?('buttonMode')
  case @Priv['buttonMode']
  when 'header'
    Tk::TreeCtrl::BindCallback.leave1(t, x, y)
  end
end

def randomRelease1(t, x, y)
  case @Priv['buttonMode']
  when 'resize', 'header'
    Tk::TreeCtrl::BindCallback.release1(t, x, y)
  when 'drag'
    Tk::TreeCtrl::BindCallback.autoScanCancel(t)
    t.dragimage_configure(:visible=>false)
    t.selection_modify('', @Priv[:drop])
    t[:cursor] = ''
    if @Priv[:drop] != ''
      randomDrop(t, @Priv[:drop], @Priv.list_element(:selection), 
                 @Priv[:drop, :pos])
    end
  end
  @Priv['buttonMode'] = ''
end

def randomDrop(t, target, src, pos)
  parentList = []
  case pos
  when 'lastchild'
    parent = target
  when 'prevsibling'
    parent = t.item_parent(target)
  when 'nextsibling'
    parent = t.item_parent(target)
  end
  src.each{|item|
    # Ignore any item whose ancestor is also selected
    ignore = false
    t.item_ancestors(item).each{|ancestor|
      if src.find{|val| val.to_s == ancestor.to_s}
        ignore = true
        break
      end
    }
    next if ignore

    # Update the old parent of this moved item later
    unless parentList.find{|val| val.to_s == item.to_s}
      parentList << t.item_parent(item)
    end

    # Add to target
    t.__send__("item_#{pos}", target, item)

    # Update text: parent
    t.item_element_configure(item, 'parent', 'e6', :text=>parent)

    # Update text: depth
    t.item_element_configure(item, 'depth', 'e6', :text=>t.depth(item))

    # Recursively update text: depth
    itemList = []
    item = t.item_firstchild(item)
    itemList << item if item != ''

    while item = itemList.pop
      t.item_element_configure(item, 'depth', 'e6', :text=>t.depth(item))

      item2 = t.item_nextsibling(item)
      itemList << item2 if item2 != ''

      item2 = t.item_firstchild(item)
      itemList << item2 if item2 != ''
    end
  }

  # Update items that lost some children
  parentList.each{|item|
    numChildren = t.item_numchildren(item)
    if numChildren == 0
      if $Version_1_1_OrLater
        t.item_configure(item, :button=>false)
      else # TreeCtrl 1.0
        t.item_hasbutton(item, false)
      end
      t.item_style_map(item, 'item', 's2', ['e3', 'e3'])
    else
      t.item_element_configure(item, 'item', 'e4', :text=>"(#{numChildren})")
    end
  }

  # Update the target that gained some children
  if t.item_style_set(parent, 0) != 's1'
    if $Version_1_1_OrLater
      t.item_configure(parent, :button=>true)
    else # TreeCtrl 1.0
      t.item_hasbutton(parent, true)
    end
    t.item_style_map(parent, 'item', 's1', ['e3', 'e3'])
  end
  numChildren = t.item_numchildren(parent)
  t.item_element_configure(parent, 'item', 'e4', :text=>"(#{numChildren})")
end

# Same as TreeCtrl::AutoScanCheck, but calls RandomMotion and
# RandomAutoScanCheckAux
def randomAutoScanCheck(t, x, y)
  x1, y1, x2, y2 = t.contentbox
  margin = t.winfo_pixels(t.scrollmargin)
  if x < x1 + margin || x >= x2 - margin || y < y1 + margin || y >= y2 - margin
    if ! @Priv.exist?(:autoscan, :afterId, t)
      if y >= y2 - margin
        t.yview(:scroll, 1, :units)
        delay = t.yscrolldelay
      elsif y < y1 + margin
        t.yview(:scroll, -1, :units)
        delay = t.yscrolldelay
      elsif x >= x2 - margin
        t.xview(:scroll, 1, :units)
        delay = t.xscrolldelay
      elsif x < x1 + margin
        t.xview(:scroll, -1, :units)
        delay = t.xscrolldelay
      end
      if @Priv.exist?(:autoscan, :scanning, t)
        delay = delay[1] if delay.kind_of?(Array)
      else
        delay = delay[0] if delay.kind_of?(Array)
        @Priv[:autoscan, :scanning, t] = true
      end
      case @Priv['buttonMode']
      when 'drag', 'marquee'
        randomMotion(t, x, y)
      end
      @Priv[:autoscan, :afterId, t] = 
        Tk.after(delay, proc{ randomAutoScanCheckAux(t) })
    end
    return
  end
  Tk::TreeCtrl::BindCallback.autoScanCancel(t)
end

def randomAutoScanCheckAux(t)
  @Priv.unset(:autoscan, :afterId, t)
  x = t.winfo_pointerx - t.winfo_rootx
  y = t.winfo_pointery - t.winfo_rooty
  randomAutoScanCheck(t, x, y)
end

#
# Demo: random N items, button images
#
def demoRandom2(t)
  demoRandom(t)

  init_pics('mac-*')

  t.configure(:openbuttonimage=>@images['mac-collapse'], 
              :closedbuttonimage=>@images['mac-expand'], 
              :showlines=>false)
end
