diff options
author | nagai <nagai@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2003-12-19 08:38:01 +0000 |
---|---|---|
committer | nagai <nagai@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2003-12-19 08:38:01 +0000 |
commit | 687cb911278d3c0dd0c408d9c6298914d5343cdc (patch) | |
tree | e645ace857db55dd3e1663329c9eb44b13cbd6d9 /ext/tk/sample/tkmultilistframe.rb | |
parent | 2b10a7d7fd8c506dfbb387ffa5374bb5297b39dc (diff) | |
download | ruby-687cb911278d3c0dd0c408d9c6298914d5343cdc.tar.gz |
* ext/tk/sample/tkmultilistbox.rb: bug fix
* ext/tk/sample/tkmultilistframe.rb: new sample script
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@5218 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'ext/tk/sample/tkmultilistframe.rb')
-rw-r--r-- | ext/tk/sample/tkmultilistframe.rb | 912 |
1 files changed, 912 insertions, 0 deletions
diff --git a/ext/tk/sample/tkmultilistframe.rb b/ext/tk/sample/tkmultilistframe.rb new file mode 100644 index 0000000000..b67f8f8dac --- /dev/null +++ b/ext/tk/sample/tkmultilistframe.rb @@ -0,0 +1,912 @@ +# +# tkmultilistframe.rb : multiple listbox widget on scrollable frame +# by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp) +# +require 'tk' + +class TkMultiListFrame < TkListbox + include TkComposite + + # lbox_height : height of listboxes (pixel) + # title_info : array [ [<title_string>,<init_width>], ... ] + # keys : hash {<option>=><value>, ... } + def initialize_composite(lbox_height, title_info, keys={}) + # argument check + if (! title_info.kind_of? Array) or (title_info.size < 2) + raise + end + + # mode + @keep_minsize = true + @show_each_hscr = true + @show_win_hscr = true + + # init arrays + @base_list = [] + @rel_list = [] + @title_list = [] + @title_cmd = [] + @lbox_list = [] + @hscr_list = [] + + # decide total width + @lbox_total = title_info.size + @width_total = 0 + title_info.each{|title, width, cmd| + @width_total += width.to_f + @title_cmd << cmd + } + + # rel-table of label=>index + @name_index = {} + + # size definition + @window_width = @width_total + @sash = 5 + @scrbar_width = 15 + @scrbar_border = 3 + @lbox_border = 1 + @title_border = 3 + @h_l_thick = 0 + + # virtical scrollbar + @v_scroll = TkScrollbar.new(@frame, 'highlightthickness'=>@h_l_thick, + 'borderwidth'=>@scrbar_border, + 'orient'=>'vertical', 'width'=>@scrbar_width) + + # horizontal scrollbar + @h_scroll = TkScrollbar.new(@frame, 'highlightthickness'=>@h_l_thick, + 'borderwidth'=>@scrbar_border, + 'orient'=>'horizontal', 'width'=>@scrbar_width) + + # create base flames + @c_title = TkCanvas.new(@frame, 'highlightthickness'=>@h_l_thick, + 'width'=>@window_width) + @f_title = TkFrame.new(@c_title, 'width'=>@width_total) + @w_title = TkcWindow.new(@c_title, 0, 0, + 'window'=>@f_title, 'anchor'=>'nw') + + @c_lbox = TkCanvas.new(@frame, 'highlightthickness'=>@h_l_thick, + 'width'=>@window_width) + @f_lbox = TkFrame.new(@c_lbox, 'width'=>@width_total) + @w_lbox = TkcWindow.new(@c_lbox, 0, 0, 'window'=>@f_lbox, 'anchor'=>'nw') + + @c_hscr = TkCanvas.new(@frame, 'highlightthickness'=>@h_l_thick, + 'width'=>@window_width) + @f_hscr = TkFrame.new(@c_hscr, 'width'=>@width_total) + @w_hscr = TkcWindow.new(@c_hscr, 0, 0, 'window'=>@f_hscr, 'anchor'=>'nw') + + # create each listbox + sum = 0.0 + @rel_list << sum/@width_total + title_info.each_with_index{|(label, width), idx| + # set relation between label and index + if @name_index.include?(label) + @name_index[label] << idx + else + @name_index[label] = [idx] + end + + # calculate relative positioning + sum += width + @rel_list << sum/@width_total + + # title field + f = TkFrame.new(@f_title, 'width'=>width) + base = [f] + + title = TkLabel.new(f, 'text'=>label, 'borderwidth'=>@title_border, + 'relief'=>'raised', 'highlightthickness'=>@h_l_thick) + title_binding(title, idx) + title.pack('fill'=>'x') + + @title_list << title + + f.place('relx'=>@rel_list[idx], 'y'=>0, 'anchor'=>'nw', 'width'=>1, + 'relheight'=>1.0, + 'relwidth'=>@rel_list[idx+1] - @rel_list[idx]) + + # listbox field + f = TkFrame.new(@f_lbox, 'width'=>width) + base << f + @lbox_list << TkListbox.new(f, 'highlightthickness'=>@h_l_thick, + 'borderwidth'=>@lbox_border + ).pack('fill'=>'both', 'expand'=>true) + f.place('relx'=>@rel_list[idx], 'y'=>0, 'anchor'=>'nw', 'width'=>1, + 'relwidth'=>@rel_list[idx+1] - @rel_list[idx], 'relheight'=>1.0) + + # scrollbar field + f = TkFrame.new(@f_hscr, 'width'=>width) + base << f + @hscr_list << TkScrollbar.new(f, 'orient'=>'horizontal', + 'width'=>@scrbar_width, + 'borderwidth'=>@scrbar_border, + 'highlightthickness'=>@h_l_thick + ).pack('fill'=>'x', 'anchor'=>'w') + f.place('relx'=>@rel_list[idx], 'y'=>0, 'anchor'=>'nw', 'width'=>1, + 'relwidth'=>@rel_list[idx+1] - @rel_list[idx]) + + @lbox_list[idx].xscrollcommand proc{|first, last| + @hscr_list[idx].set first, last + } + @hscr_list[idx].command proc{|*args| @lbox_list[idx].xview *args} + + # add new base + @base_list << base + } + + # pad + # @f_title_pad = TkFrame.new(@frame) + @f_title_pad = TkFrame.new(@frame, 'relief'=>'raised', + 'borderwidth'=>@title_border, + 'highlightthickness'=>@h_l_thick) + + @f_scr_pad = TkFrame.new(@frame, 'relief'=>'sunken', + 'borderwidth'=>1, + 'highlightthickness'=>@h_l_thick) + + # height check + title_height = 0 + @title_list.each{|w| + h = w.winfo_reqheight + title_height = h if title_height < h + } + + hscr_height = 0 + @hscr_list.each{|w| + h = w.winfo_reqheight + hscr_height = h if hscr_height < h + } + + @f_title.height title_height + @f_lbox.height lbox_height + @f_hscr.height hscr_height + + # set control procedure for virtical scroll + @lbox_list.each{|lbox| + lbox.yscrollcommand proc{|first, last| + @v_scroll.set first, last + } + } + @v_scroll.command proc{|*args| @lbox_list.each{|lbox| lbox.yview *args} } + + # set control procedure for horizoncal scroll + @c_title.xscrollcommand proc{|first, last| + @h_scroll.set first, last + } + @c_lbox.xscrollcommand proc{|first, last| + @h_scroll.set first, last + } + @c_hscr.xscrollcommand proc{|first, last| + @h_scroll.set first, last + } + @h_scroll.command proc{|*args| + @c_title.xview *args + @c_lbox.xview *args + @c_hscr.xview *args if @show_each_hscr + } + + # binding for listboxes + @mode = {} + @mode['browse'] = browse_mode_bindtag + @mode['single'] = single_mode_bindtag + @mode['extended'] = extended_mode_bindtag + @mode['multiple'] = multiple_mode_bindtag + @current_mode = 'browse' + @lbox_list.each_with_index{|l, idx| + l.bind('Shift-Key-Left', + proc{|w| focus_shift(w, -1); Tk.callback_break}, '%W') + l.bind('Shift-Key-Right', + proc{|w| focus_shift(w, 1); Tk.callback_break}, '%W') + + l.bind('Button-2', proc{|x, y| + @lbox_mark_x = x + @lbox_list.each{|lbox| lbox.scan_mark(x, y)} + }, '%x %y') + l.bind('B2-Motion', proc{|x, y| + @lbox_list.each{|lbox| lbox.scan_dragto(@lbox_mark_x, y)} + l.scan_dragto(x, y) + }, '%x %y') + + l.bindtags(l.bindtags.unshift(@mode[@current_mode])) + } + + bbox = @w_title.bbox + @c_title.height(bbox[3]) + @c_title.scrollregion(bbox) + + bbox = @w_lbox.bbox + @c_lbox.height(bbox[3]) + @c_lbox.scrollregion(bbox) + + if @show_each_hscr + bbox = @w_hscr.bbox + @c_hscr.height(bbox[3]) + @c_hscr.scrollregion(bbox) + end + + # alignment + TkGrid.rowconfigure(@frame, 0, 'weight'=>0) + TkGrid.rowconfigure(@frame, 1, 'weight'=>1) + TkGrid.rowconfigure(@frame, 2, 'weight'=>0) + TkGrid.rowconfigure(@frame, 3, 'weight'=>0) + TkGrid.columnconfigure(@frame, 0, 'weight'=>1) + TkGrid.columnconfigure(@frame, 1, 'weight'=>0) + TkGrid.columnconfigure(@frame, 2, 'weight'=>0) + @v_scroll.grid('row'=>1, 'column'=>2, 'sticky'=>'ns') + @c_title.grid('row'=>0, 'column'=>0, 'sticky'=>'news') + @f_title_pad.grid('row'=>0, 'column'=>2, 'sticky'=>'news') + @c_lbox.grid('row'=>1, 'column'=>0, 'sticky'=>'news') + @c_hscr.grid('row'=>2, 'column'=>0, 'sticky'=>'ew') if @show_each_hscr + @h_scroll.grid('row'=>3, 'column'=>0, 'sticky'=>'ew') if @show_win_hscr + @f_scr_pad.grid('row'=>2, 'rowspan'=>2, 'column'=>2, 'sticky'=>'news') + + # binding for 'Configure' event + @c_lbox.bind('Configure', + proc{|height, width| reconstruct(height, width)}, + '%h %w') + + # set default receiver of method calls + @path = @lbox_list[0].path + + # configure options + keys = {} unless keys + keys = _symbolkey2str(keys) + + # 'mode' option of listboxes + sel_mode = keys.delete('mode') + mode(sel_mode) if sel_mode + + # 'scrollbarwidth' option == 'width' option of scrollbars + width = keys.delete('scrollbarwidth') + scrollbarwidth(width) if width + + # options for listbox titles + title_font = keys.delete('titlefont') + titlefont(title_font) if title_font + + title_fg = keys.delete('titleforeground') + titleforeground(title_fg) if title_fg + + title_bg = keys.delete('titlebackground') + titlebackground(title_bg) if title_bg + + # set receivers for configure methods + delegate('DEFAULT', *@lbox_list) + delegate('activebackground', @v_scroll, @h_scroll, *@hscr_list) + delegate('troughcolor', @v_scroll, @h_scroll, *@hscr_list) + delegate('repeatdelay', @v_scroll, @h_scroll, *@hscr_list) + delegate('repeatinterval', @v_scroll, @h_scroll, *@hscr_list) + delegate('borderwidth', @frame) + delegate('width', @c_lbox, @c_title, @c_hscr) + delegate('relief', @frame) + + # configure + configure(keys) if keys.size > 0 + end + private :initialize_composite + + # set 'mode' option of listboxes + def mode(sel_mode) + @lbox_list.each{|l| + tags = l.bindtags + tags = tags - [ @mode[@current_mode] ] + l.bindtags(tags.unshift(@mode[sel_mode])) + @current_mode = sel_mode + } + end + + # keep_minsize? + def keep_minsize? + @keep_minsize + end + def keep_minsize(bool) + @keep_minsize = bool + end + + # each hscr + def show_each_hscr + @show_each_hscr = true + @c_hscr.grid('row'=>2, 'column'=>0, 'sticky'=>'ew') + end + def hide_each_hscr + @show_each_hscr = false + @c_hscr.ungrid + end + + # window hscroll + def show_win_hscr + @show_win_hscr = true + @h_scroll.grid('row'=>3, 'column'=>0, 'sticky'=>'ew') + end + def hide_win_hscr + @show_each_hscr = false + @h_scroll.ungrid + end + + # set scrollbar width + def scrollbarwidth(width) + @scrbar_width = width + @v_scroll['width'] = @scrbar_width + @h_scroll['width'] = @scrbar_width + @hscr_list.each{|hscr| hscr['width'] = @scrbar_width} + self + end + + # set scrollbar border + def scrollbarborder(width) + @scrbar_border = width + @v_scroll['border'] = @scrbar_border + @h_scroll['border'] = @scrbar_border + @hscr_list.each{|hscr| hscr['border'] = @scrbar_border} + self + end + + # set listbox borders + def listboxborder(width) + @lbox_border = width + @lbox_list.each{|w| w['border'] = @lbox_border} + self + end + + # set listbox relief + def listboxrelief(relief) + @lbox_list.each{|w| w['relief'] = relief} + self + end + + # set title borders + def titleborder(width) + @title_border = width + @f_title_pad['border'] = @title_border + @title_list.each{|label| label['border'] = @title_border} + self + end + + # set title font + def titlefont(font) + @title_list.each{|label| label['font'] = font} + title_height = 0 + @title_list.each{|w| + h = w.winfo_reqheight + title_height = h if title_height < h + } + @f_title.height title_height + bbox = @w_title.bbox + @c_title.height(bbox[3]) + @c_title.scrollregion(bbox) + self + end + + # set title foreground color + def titleforeground(fg) + @title_list.each{|label| label['foreground'] = fg} + self + end + + # set title background color + def titlebackground(bg) + @f_title_pad['background'] = bg + @title_list.each{|label| label['background'] = bg} + self + end + + # set title cmds + def titlecommand(idx, cmd=Proc.new) + @title_cmd[idx] = cmd + end + + # call title cmds + def titleinvoke(idx) + @title_cmd[idx].call if @title_cmd[idx] + end + + # get label widgets of listbox titles + def titlelabels(*indices) + @title_list[*indices] + end + + # get listbox widgets + def columns(*indices) + @lbox_list[*indices] + end + + def activate(idx) + @lbox_list.each{|lbox| lbox.activate(idx)} + end + + def bbox(idx) + @lbox_list.collect{|lbox| lbox.bbox(idx)} + end + + def delete(*idx) + @lbox_list.collect{|lbox| lbox.delete(*idx)} + end + + def get(*idx) + if idx.size == 1 + @lbox_list.collect{|lbox| lbox.get(*idx)} + else + list = @lbox_list.collect{|lbox| lbox.get(*idx)} + result = [] + list[0].each_with_index{|line, index| + result << list.collect{|lines| lines[index]} + } + result + end + end + + def _line_array_to_hash(line) + result = {} + @name_index.each_pair{|label, indices| + if indices.size == 1 + result[label] = line[indices[0]] + else + result[label] = indices.collect{|index| line[index]} + end + } + result + end + private :_line_array_to_hash + + def get_by_hash(*idx) + get_result = get(*idx) + if idx.size == 1 + _line_array_to_hash(get_result) + else + get_result.collect{|line| _line_array_to_hash(line)} + end + end + + def insert(idx, *lines) + lbox_ins = [] + (0..@lbox_list.size).each{lbox_ins << []} + + lines.each{|line| + if line.kind_of? Hash + array = [] + @name_index.each_pair{|label, indices| + if indices.size == 1 + array[indices[0]] = line[label] + else + if line[label].kind_of? Array + indices.each_with_index{|index, num| + array[index] = line[label][num] + } + else + array[indices[0]] = line[label] + end + end + } + line = array + end + + @name_index.each_pair{|label, indices| + if indices.size == 1 + lbox_ins[indices[0]] << line[indices[0]] + else + indices.each{|index| lbox_ins[index] << line[index]} + end + } + } + + @lbox_list.each_with_index{|lbox, index| + lbox.insert(idx, *lbox_ins[index]) if lbox_ins[index] + } + end + + def selection_anchor(index) + @lbox_list.each{|lbox| lbox.selection_anchor(index)} + end + + def selection_clear(first, last=None) + @lbox_list.each{|lbox| lbox.selection_clear(first, last=None)} + end + + def selection_set(first, last=None) + @lbox_list.each{|lbox| lbox.selection_set(first, last=None)} + end + + ########################################### + private + + def reconstruct(height, width) + if @keep_minsize && width <= @width_total + @f_title.width(@width_total) + @f_lbox.width(@width_total) + @f_hscr.width(@width_total) if @show_each_hscr + @window_width = @width_total + else + @f_title.width(width) + @f_lbox.width(width) + @f_hscr.width(width) if @show_each_hscr + @window_width = width + end + + @f_lbox.height(height) + + @c_title.scrollregion(@w_title.bbox) + @c_lbox.scrollregion(@w_lbox.bbox) + @c_hscr.scrollregion(@w_hscr.bbox) if @show_each_hscr + + (0..(@rel_list.size - 2)).each{|idx| + title, lbox, hscr = @base_list[idx] + title.place('relwidth'=>@rel_list[idx+1] - @rel_list[idx]) + lbox.place('relwidth'=>@rel_list[idx+1] - @rel_list[idx], + 'relheight'=>1.0) + hscr.place('relwidth'=>@rel_list[idx+1] - @rel_list[idx]) + } + end + + def resize(x) + idx = @sel_sash + return if idx == 0 + + # adjustment of relative positioning + delta = (x - @x) / @frame_width + if delta < @rel_list[idx-1] - @rel_list[idx] + (2*@sash/@frame_width) + delta = @rel_list[idx-1] - @rel_list[idx] + (2*@sash/@frame_width) + elsif delta > @rel_list[idx+1] - @rel_list[idx] - (2*@sash/@frame_width) + delta = @rel_list[idx+1] - @rel_list[idx] - (2*@sash/@frame_width) + end + @rel_list[idx] += delta + + # adjustment of leftside widget of the sash + title, lbox, hscr = @base_list[idx - 1] + title.place('relwidth'=>@rel_list[idx] - @rel_list[idx-1]) + lbox.place('relwidth'=>@rel_list[idx] - @rel_list[idx-1], 'relheight'=>1.0) + hscr.place('relwidth'=>@rel_list[idx] - @rel_list[idx-1]) + + # adjustment of rightside widget of the sash + title, lbox, hscr = @base_list[idx] + title.place('relwidth'=>@rel_list[idx+1] - @rel_list[idx], + 'relx'=>@rel_list[idx]) + lbox.place('relwidth'=>@rel_list[idx+1] - @rel_list[idx], + 'relx'=>@rel_list[idx], 'relheight'=>1.0) + hscr.place('relwidth'=>@rel_list[idx+1] - @rel_list[idx], + 'relx'=>@rel_list[idx]) + + # update reference position + @x = x + end + + def motion_cb(w, x, idx) + if x <= @sash && idx > 0 + w.cursor 'sb_h_double_arrow' + @mode = :sash + @sel_sash = idx + elsif x >= w.winfo_width - @sash && idx < @lbox_total - 1 + w.cursor 'sb_h_double_arrow' + @mode = :sash + @sel_sash = idx + 1 + else + w.cursor "" + @mode = :title + @sel_sash = 0 + end + end + + def title_binding(title, index) + title.bind('Motion', proc{|w, x, idx| motion_cb(w, x, idx.to_i)}, + "%W %x #{index}") + + title.bind('Enter', proc{|w, x, idx| motion_cb(w, x, idx.to_i)}, + "%W %x #{index}") + + title.bind('Leave', proc{|w| w.cursor ""}, "%W") + + title.bind('Button-1', + proc{|w, x| + if @mode == :sash + @x = x + @frame_width = TkWinfo.width(@f_title).to_f + else + title.relief 'sunken' + end + }, + '%W %X') + + title.bind('ButtonRelease-1', + proc{|w, x, idx| + i = idx.to_i + if @mode == :title && @title_cmd[i].kind_of?(Proc) + @title_cmd[i].call + end + title.relief 'raised' + motion_cb(w,x,i) + }, + "%W %x #{index}") + + title.bind('B1-Motion', proc{|x| resize(x) if @mode == :sash}, "%X") + end + + ################################# + def browse_mode_bindtag + t = TkBindTag.new + t.bind('Button-1', + proc{|w, y| w.focus; select_line(w, w.nearest(y))}, '%W %y') + t.bind('B1-Motion', proc{|w, y| select_line(w, w.nearest(y))}, '%W %y') + + t.bind('Shift-Button-1', + proc{|w, y| active_line(w, w.nearest(y))}, '%W %y') + + t.bind('Key-Up', proc{|w| select_shift(w, -1)}, '%W') + t.bind('Key-Down', proc{|w| select_shift(w, 1)}, '%W') + + t.bind('Control-Home', proc{|w| select_line(w, 0)}, '%W') + t.bind('Control-End', proc{|w| select_line(w, 'end')}, '%W') + + t.bind('space', proc{|w| select_line(w, w.index('active').to_i)}, '%W') + t.bind('Select', proc{|w| select_line(w, w.index('active').to_i)}, '%W') + t.bind('Control-slash', + proc{|w| select_line(w, w.index('active').to_i)}, '%W') + + t + end + + ######################## + def single_mode_bindtag + t = TkBindTag.new + t.bind('Button-1', + proc{|w, y| w.focus; select_only(w, w.nearest(y))}, '%W %y') + t.bind('ButtonRelease-1', + proc{|w, y| active_line(w, w.nearest(y))}, '%W %y') + + t.bind('Shift-Button-1', + proc{|w, y| active_line(w, w.nearest(y))}, '%W %y') + + t.bind('Key-Up', proc{|w| select_shift(w, -1)}, '%W') + t.bind('Key-Down', proc{|w| select_shift(w, 1)}, '%W') + + t.bind('Control-Home', proc{|w| select_line(w, 0)}, '%W') + t.bind('Control-End', proc{|w| select_line(w, 'end')}, '%W') + + t.bind('space', proc{|w| select_line(w, w.index('active').to_i)}, '%W') + t.bind('Select', proc{|w| select_line(w, w.index('active').to_i)}, '%W') + t.bind('Control-slash', + proc{|w| select_line(w, w.index('active').to_i)}, '%W') + t.bind('Control-backslash', + proc{@lbox_list.each{|l| l.selection_clear(0, 'end')}}) + + t + end + + ######################## + def extended_mode_bindtag + t = TkBindTag.new + t.bind('Button-1', + proc{|w, y| w.focus; select_only(w, w.nearest(y))}, '%W %y') + t.bind('B1-Motion', proc{|w, y| select_range(w, w.nearest(y))}, '%W %y') + + t.bind('ButtonRelease-1', + proc{|w, y| active_line(w, w.nearest(y))}, '%W %y') + + t.bind('Shift-Button-1', + proc{|w, y| select_range(w, w.nearest(y))}, '%W %y') + t.bind('Shift-B1-Motion', + proc{|w, y| select_range(w, w.nearest(y))}, '%W %y') + + t.bind('Control-Button-1', + proc{|w, y| select_toggle(w, w.nearest(y))}, '%W %y') + + t.bind('Control-B1-Motion', + proc{|w, y| select_drag(w, w.nearest(y))}, '%W %y') + + t.bind('Key-Up', proc{|w| active_shift(w, -1)}, '%W') + t.bind('Key-Down', proc{|w| active_shift(w, 1)}, '%W') + + t.bind('Shift-Up', proc{|w| select_expand(w, -1)}, '%W') + t.bind('Shift-Down', proc{|w| select_expand(w, 1)}, '%W') + + t.bind('Control-Home', proc{|w| select_line2(w, 0)}, '%W') + t.bind('Control-End', proc{|w| select_line2(w, 'end')}, '%W') + + t.bind('Control-Shift-Home', proc{|w| select_range(w, 0)}, '%W') + t.bind('Control-Shift-End', proc{|w| select_range(w, 'end')}, '%W') + + t.bind('space', proc{|w| select_active(w)}, '%W') + t.bind('Select', proc{|w| select_active(w)}, '%W') + t.bind('Control-slash', proc{|w| select_all}, '%W') + t.bind('Control-backslash', proc{|w| clear_all}, '%W') + + t + end + + ######################## + def multiple_mode_bindtag + t = TkBindTag.new + t.bind('Button-1', + proc{|w, y| w.focus; select_line3(w, w.nearest(y))}, '%W %y') + t.bind('ButtonRelease-1', + proc{|w, y| active_line(w, w.nearest(y))}, '%W %y') + + t.bind('Key-Up', proc{|w| active_shift(w, -1)}, '%W') + t.bind('Key-Down', proc{|w| active_shift(w, 1)}, '%W') + + t.bind('Control-Home', proc{|w| select_line2(w, 0)}, '%W') + t.bind('Control-End', proc{|w| select_line2(w, 'end')}, '%W') + + t.bind('Control-Shift-Home', proc{|w| active_line(w, 0)}, '%W') + t.bind('Control-Shift-End', proc{|w| active_line(w, 'end')}, '%W') + + t.bind('space', proc{|w| select_active(w)}, '%W') + t.bind('Select', proc{|w| select_active(w)}, '%W') + t.bind('Control-slash', proc{|w| select_all}, '%W') + t.bind('Control-backslash', proc{|w| clear_all}, '%W') + + t + end + + ######################## + def active_line(w, idx) + @lbox_list.each{|l| l.activate(idx)} + end + + def select_only(w, idx) + @lbox_list.each{|l| + l.selection_clear(0, 'end') + l.selection_anchor(idx) + l.selection_set('anchor') + } + end + + def select_range(w, idx) + @lbox_list.each{|l| + l.selection_clear(0, 'end') + l.selection_set('anchor', idx) + } + end + + def select_toggle(w, idx) + st = w.selection_includes(idx) + @lbox_list.each{|l| + l.selection_anchor(idx) + if st == 1 + l.selection_clear(idx) + else + l.selection_set(idx) + end + } + end + + def select_drag(w, idx) + st = w.selection_includes('anchor') + @lbox_list.each{|l| + if st == 1 + l.selection_set('anchor', idx) + else + l.selection_clear('anchor', idx) + end + } + end + + def select_line(w, idx) + @lbox_list.each{|l| + l.selection_clear(0, 'end') + l.activate(idx) + l.selection_anchor(idx) + l.selection_set('anchor') + } + w.selection_set('anchor') + end + + def select_line2(w, idx) + @lbox_list.each{|l| + l.activate(idx) + l.selection_anchor(idx) + l.selection_set('anchor') + } + end + + def select_line3(w, idx) + @lbox_list.each{|l| + l.selection_set(idx) + } + end + + def select_active(w) + idx = l.activate(idx) + @lbox_list.each{|l| + l.selection_set(idx) + } + end + + def select_expand(w, dir) + idx = w.index('active').to_i + dir + if idx < 0 + idx = 0 + elsif idx >= w.size + idx = w.size - 1 + end + @lbox_list.each{|l| + l.activate(idx) + l.selection_set(idx) + } + end + + def active_shift(w, dir) + idx = w.index('active').to_i + dir + if idx < 0 + idx = 0 + elsif idx >= w.size + idx = w.size - 1 + end + @lbox_list.each{|l| + l.activate(idx) + l.selection_anchor(idx) + } + end + + def select_shift(w, dir) + idx = w.index('anchor').to_i + dir + if idx < 0 + idx = 0 + elsif idx >= w.size + idx = w.size - 1 + end + @lbox_list.each{|l| + l.selection_clear(0, 'end') + l.activate(idx) + l.selection_anchor(idx) + l.selection_set('anchor') + } + end + + def select_all + @lbox_list.each{|l| + l.selection_set(0, 'end') + } + end + + def clear_all + @lbox_list.each{|l| + l.selection_clear(0, 'end') + } + end + + def focus_shift(w, dir) + idx = @lbox_list.index(w) + dir + return if idx < 0 + return if idx >= @lbox_list.size + @lbox_list[idx].focus + end + ######################## +end + +################################################ +# test +################################################ +if __FILE__ == $0 + l = TkMultiListFrame.new(nil, 200, + [ ['L1', 200, proc{p 'click L1'}], + ['L2', 100], + ['L3', 200] ], + 'width'=>350, + #'titleforeground'=>'yellow', + 'titleforeground'=>'white', + #'titlebackground'=>'navy', + 'titlebackground'=>'blue', + 'titlefont'=>'courier' + ).pack('fill'=>'both', 'expand'=>true) + l.insert('end', [1,2,3]) + l.insert('end', [4,5,6]) + l.insert('end', [4,5,6], [4,5,6]) + l.insert('end', ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', + 'cccccccccccccccccccccccccccccccccccccccccccccccccccc']) + l.insert('end', [1,2,3]) + l.insert('end', [4,5,6], [4,5,6]) + l.insert('end', ['aaaaaaaaaaaaaaa','bbbbbbbbbbbbbb','ccccccccccccccccc']) + l.insert('end', [1,2,3]) + l.insert('end', [4,5,6], [4,5,6]) + l.insert('end', ['aaaaaaaaaaaaaaa','bbbbbbbbbbbbbb','ccccccccccccccccc']) + l.insert('end', [1,2,3]) + l.insert('end', [4,5,6], [4,5,6]) + l.insert('end', ['aaaaaaaaaaaaaaa','bbbbbbbbbbbbbb','ccccccccccccccccc']) + l.insert('end', [1,2,3]) + l.insert('end', [4,5,6], [4,5,6]) + p l.columns(1) + p l.columns(1..3) + p l.columns(1,2) + Tk.mainloop +end |