1#
2# Tk::RbWidget::Editable_Listbox class
3#
4#   When "DoubleClick-1" on a listbox item, the entry box is opend on the
5#   item. And when hit "Return" key on the entry box after modifying the
6#   text, the entry box is closed and the item is changed. Or when hit
7#   "Escape" key, the entry box is closed without modification.
8#
9#                              by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
10#
11require 'tk'
12
13module Tk
14  module RbWidget
15    class Editable_Listbox < TkListbox
16    end
17  end
18end
19
20
21class Tk::RbWidget::Editable_Listbox < TkListbox
22  #------------------------------------
23  BindTag = TkBindTag.new_by_name(self.to_s.gsub(/::/, '#'))
24
25  BindTag.bind('FocusIn', :widget){|w|
26    w.instance_eval{
27      if idx = @ebox.pos
28        see(idx) if bbox(idx).empty?
29        @ebox.focus(true)
30      end
31    }
32  }
33
34  BindTag.bind('Double-1', :widget, :y){|w, y|
35    w.instance_eval{ _ebox_placer(nearest(y)) }
36  }
37
38  BindTag.bind('Return', :widget){|w|
39    w.instance_eval{
40      if idx = index(:active)
41        _ebox_placer(idx)
42      end
43    }
44  }
45  #------------------------------------
46
47  def configure(*args)
48    ret = super
49
50    case cget(:state)
51    when 'normal'
52      # do nothing
53    when 'disabled'
54      _ebox_erase
55    else # unknown
56      # do nothing
57
58    end
59
60    ret
61  end
62
63  def _ebox_move(idx)
64    return nil if cget(:state) == 'disabled'
65    x, y, w, h = bbox(idx)
66    return nil unless y && h
67    @ebox.place(:x => 0, :relwidth => 1.0,
68                :y => y - selectborderwidth,
69                :height => h + 2 * selectborderwidth)
70    @ebox.pos = idx
71    @ebox.focus
72  end
73
74  def _ebox_placer(idx)
75    return nil unless _ebox_move(idx)
76    @ebox.value = listvariable.list[idx]
77    @ebox.xview_moveto(self.xview[0])
78  end
79
80  def _ebox_erase
81    @ebox.place_forget
82    @ebox.pos = nil
83  end
84  private :_ebox_move, :_ebox_placer, :_ebox_erase
85
86  def _setup_ebox_bindings
87    # bindings for entry
88    @ebox.bind('Return'){
89      list = listvariable.list
90      list[@ebox.pos] = @ebox.value if @ebox.pos
91      listvariable.value = list
92      _ebox_erase
93      focus
94    }
95
96    @ebox.bind('Escape'){ _ebox_erase }
97  end
98  def _setup_listbox_bindings
99    # bindings for listbox
100    tags = bindtags
101    bindtags(tags.insert(tags.index(self) + 1, self.class::BindTag))
102  end
103  private :_setup_ebox_bindings,  :_setup_listbox_bindings
104
105  def yview(*args)
106    if !@ebox.pos || bbox(@ebox.pos).empty?
107      @ebox.place_forget
108    else
109      _ebox_move(@ebox.pos)
110    end
111    super
112  end
113
114  def create_self(keys)
115    super(keys)
116
117    unless self.listvariable
118      self.listvariable = TkVariable.new(self.get(0, :end))
119    end
120
121    @ebox = TkEntry.new(self){
122      @pos = nil
123      def self.pos; @pos; end
124      def self.pos=(idx); @pos = idx; end
125    }
126
127    _setup_ebox_bindings
128    _setup_listbox_bindings
129  end
130end
131
132if $0 == __FILE__
133  #lbox0 = TkListbox.new.pack(:side=>:left)
134  #lbox0.insert(:end,     0,1,2,3,4,5,6,7,8,9,0,1,2,3)
135
136  scr = TkScrollbar.new.pack(:side=>:right, :fill=>:y)
137
138  lbox1 = Tk::RbWidget::Editable_Listbox.new.pack(:side=>:left)
139  lbox2 = Tk::RbWidget::Editable_Listbox.new.pack(:side=>:left)
140
141  scr.assign(lbox1, lbox2)
142
143  lbox1.insert(:end, *%w(a b c d e f g h i j k l m n))
144  lbox2.insert(:end,     0,1,2,3,4,5,6,7,8,9,0,1,2,3)
145
146
147  Tk.mainloop
148end
149