summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog10
-rw-r--r--ext/tcltklib/tcltklib.c4
-rw-r--r--ext/tk/MANIFEST1
-rw-r--r--ext/tk/lib/tk.rb7
-rw-r--r--ext/tk/lib/tkafter.rb2
-rw-r--r--ext/tk/sample/tkcombobox.rb418
6 files changed, 438 insertions, 4 deletions
diff --git a/ChangeLog b/ChangeLog
index be259dd5b3a..ea5543542fa 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+Sat Nov 22 22:48:46 2003 Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
+
+ * ext/tk/lib/tk.rb: add Tk.grab_release and fix bug of TkComposite
+
+ * ext/tk/lib/tkafter.rb: bug fix of TkAfter#start
+
+ * ext/tk/sample/tkcombobox.rb: new sample script
+
+ * ext/tcltklib/tcltklib.c: add native thread check
+
Sat Nov 22 18:49:47 2003 NAKAMURA Usaku <usa@ruby-lang.org>
* ext/curses/curses.c (window_nodelay): nodelay() of NetBSD's
diff --git a/ext/tcltklib/tcltklib.c b/ext/tcltklib/tcltklib.c
index d20c24eb222..50c654d8144 100644
--- a/ext/tcltklib/tcltklib.c
+++ b/ext/tcltklib/tcltklib.c
@@ -503,7 +503,9 @@ lib_eventloop_core(check_root, check_var)
rb_trap_exec();
} else {
DUMP1("thread scheduling");
- rb_thread_schedule();
+ if (is_ruby_native_thread()) {
+ rb_thread_schedule();
+ }
}
}
return 1;
diff --git a/ext/tk/MANIFEST b/ext/tk/MANIFEST
index f86797b5fb0..96f7e744f7e 100644
--- a/ext/tk/MANIFEST
+++ b/ext/tk/MANIFEST
@@ -26,6 +26,7 @@ sample/safe-tk.rb
sample/tkballoonhelp.rb
sample/tkbiff.rb
sample/tkbrowse.rb
+sample/tkcombobox.rb
sample/tkdialog.rb
sample/tkfrom.rb
sample/tkhello.rb
diff --git a/ext/tk/lib/tk.rb b/ext/tk/lib/tk.rb
index cec589047f5..308e6ddfd80 100644
--- a/ext/tk/lib/tk.rb
+++ b/ext/tk/lib/tk.rb
@@ -4261,6 +4261,9 @@ class TkWindow<TkObject
def grab_current
grab('current')
end
+ def grab_release
+ grab('release')
+ end
def grab_set
grab('set')
end
@@ -5681,8 +5684,8 @@ module TkComposite
if parent.kind_of? Hash
keys = _symbolkey2str(parent)
- parent = keys['parent']
- keys['parent'] = @frame = TkFrame.new(parent)
+ parent = keys.delete('parent')
+ @frame = TkFrame.new(parent)
@delegates['DEFAULT'] = @frame
@path = @epath = @frame.path
initialize_composite(keys)
diff --git a/ext/tk/lib/tkafter.rb b/ext/tk/lib/tkafter.rb
index 663b977ed22..92ad182fb9e 100644
--- a/ext/tk/lib/tkafter.rb
+++ b/ext/tk/lib/tkafter.rb
@@ -306,7 +306,7 @@ class TkTimer
fail format("%s need to be Proc", @init_proc.inspect)
end
@current_proc = @init_proc
- set_callback(sleep, @init_args)
+ set_callback(@init_sleep, @init_args)
@set_next = false if @in_callback
else
set_next_callback(@init_args)
diff --git a/ext/tk/sample/tkcombobox.rb b/ext/tk/sample/tkcombobox.rb
new file mode 100644
index 00000000000..08a2dd73a7d
--- /dev/null
+++ b/ext/tk/sample/tkcombobox.rb
@@ -0,0 +1,418 @@
+#
+# tkcombobox.rb : TkAutoScrollbox & TkCombobox
+#
+# by Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp)
+#
+require 'tk'
+
+class TkAutoScrollbox < TkListbox
+ include TkComposite
+
+ @@up_bmp = TkBitmapImage.new(:data=><<EOD)
+#define up_arrow_width 9
+#define up_arrow_height 9
+static unsigned char up_arrow_bits[] = {
+ 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x38, 0x00, 0x38, 0x00, 0x7c, 0x00,
+ 0x7c, 0x00, 0xfe, 0x00, 0x00, 0x00};
+EOD
+
+ @@down_bmp = TkBitmapImage.new(:data=><<EOD)
+#define up_arrow_width 9
+#define up_arrow_height 9
+static unsigned char down_arrow_bits[] = {
+ 0x00, 0x00, 0xfe, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x38, 0x00,
+ 0x10, 0x00, 0x10, 0x00, 0x00, 0x00};
+EOD
+
+ def initialize_composite(keys={})
+ keys = _symbolkey2str(keys)
+
+ @initwait = keys.delete('startwait'){300}
+ @interval = keys.delete('interval'){150}
+ @initwait -= @interval
+ @initwait = 0 if @initwait < 0
+
+ @lbox = TkListbox.new(@frame, :borderwidth=>0)
+ @path = @lbox.path
+ TkPack.propagate(@lbox, false)
+
+ @scr = TkScrollbar.new(@frame, :width=>10)
+
+ @lbox.yscrollcommand(proc{|*args| @scr.set(*args); _config_proc})
+ @scr.command(proc{|*args| @lbox.yview(*args); _config_proc})
+
+ @up_arrow = TkLabel.new(@lbox, :image=>@@up_bmp,
+ :relief=>:raised, :borderwidth=>1)
+ @down_arrow = TkLabel.new(@lbox, :image=>@@down_bmp,
+ :relief=>:raised, :borderwidth=>1)
+
+ _init_binding
+
+ @lbox.pack(:side=>:left, :fill=>:both, :expand=>:true)
+
+ delegate('DEFAULT', @lbox)
+ delegate('background', @frame, @scr)
+ delegate('activebackground', @scr)
+ delegate('troughcolor', @scr)
+ delegate('repeatdelay', @scr)
+ delegate('repeatinterval', @scr)
+ delegate('relief', @frame)
+ delegate('borderwidth', @frame)
+
+ scrollbar(keys.delete('scrollbar')){false}
+
+ configure keys unless keys.empty?
+ end
+
+ ############################
+ private
+ ############################
+ def _show_up_arrow
+ unless @up_arrow.winfo_mapped?
+ @up_arrow.pack(:side=>:top, :fill=>:x)
+ end
+ end
+
+ def _show_down_arrow
+ unless @down_arrow.winfo_mapped?
+ @down_arrow.pack(:side=>:bottom, :fill=>:x)
+ end
+ end
+
+ def _set_sel(idx)
+ @lbox.activate(idx)
+ @lbox.selection_clear(0, 'end')
+ @lbox.selection_set(idx)
+ end
+
+ def _check_sel(cidx, tidx = nil, bidx = nil)
+ _set_sel(cidx)
+ unless tidx
+ tidx = @lbox.nearest(0)
+ tidx += 1 if tidx > 0
+ end
+ unless bidx
+ bidx = @lbox.nearest(10000)
+ bidx -= 1 if bidx < @lbox.index('end') - 1
+ end
+ if cidx > bidx
+ _set_sel(bidx)
+ end
+ if cidx < tidx
+ _set_sel(tidx)
+ end
+ end
+
+ def _up_proc
+ cidx = @lbox.curselection[0]
+ idx = @lbox.nearest(0)
+ if idx >= 0
+ @lbox.see(idx - 1)
+ _set_sel(idx)
+ @up_arrow.pack_forget if idx == 1
+ @up_timer.stop if idx == 0
+ _show_down_arrow if @lbox.bbox('end') == []
+ end
+ if cidx && cidx > 0 && (idx == 0 || cidx == @lbox.nearest(10000))
+ _set_sel(cidx - 1)
+ end
+ end
+
+ def _down_proc
+ cidx = @lbox.curselection[0]
+ eidx = @lbox.index('end') - 1
+ idx = @lbox.nearest(10000)
+ if idx <= eidx
+ @lbox.see(idx + 1)
+ _set_sel(cidx + 1) if cidx < eidx
+ @down_arrow.pack_forget if idx + 1 == eidx
+ @down_timer.stop if idx == eidx
+ _show_up_arrow if @lbox.bbox(0) == []
+ end
+ if cidx && cidx < eidx && (eidx == idx || cidx == @lbox.nearest(0))
+ _set_sel(cidx + 1)
+ end
+ end
+
+ def _key_UP_proc
+ cidx = @lbox.curselection[0]
+ _set_sel(cidx = @lbox.index('activate')) unless cidx
+ cidx -= 1
+ if cidx == 0
+ @up_arrow.pack_forget
+ elsif cidx == @lbox.nearest(0)
+ @lbox.see(cidx - 1)
+ end
+ end
+
+ def _key_DOWN_proc
+ cidx = @lbox.curselection[0]
+ _set_sel(cidx = @lbox.index('activate')) unless cidx
+ cidx += 1
+ if cidx == @lbox.index('end') - 1
+ @down_arrow.pack_forget
+ elsif cidx == @lbox.nearest(10000)
+ @lbox.see(cidx + 1)
+ end
+ end
+
+ def _config_proc
+ if @lbox.size == 0
+ @up_arrow.pack_forget
+ @down_arrow.pack_forget
+ return
+ end
+ tidx = @lbox.nearest(0)
+ bidx = @lbox.nearest(10000)
+ if tidx > 0
+ _show_up_arrow
+ tidx += 1
+ else
+ @up_arrow.pack_forget unless @up_timer.running?
+ end
+ if bidx < @lbox.index('end') - 1
+ _show_down_arrow
+ bidx -= 1
+ else
+ @down_arrow.pack_forget unless @down_timer.running?
+ end
+ cidx = @lbox.curselection[0]
+ _check_sel(cidx, tidx, bidx) if cidx
+ end
+
+ def _init_binding
+ @up_timer = TkAfter.new(@interval, -1, proc{_up_proc})
+ @down_timer = TkAfter.new(@interval, -1, proc{_down_proc})
+
+ @up_timer.set_start_proc(@initwait, proc{})
+ @down_timer.set_start_proc(@initwait, proc{})
+
+ @up_arrow.bind('Enter', proc{@up_timer.start})
+ @up_arrow.bind('Leave', proc{@up_timer.stop if @up_arrow.winfo_mapped?})
+ @down_arrow.bind('Enter', proc{@down_timer.start})
+ @down_arrow.bind('Leave', proc{@down_timer.stop if @down_arrow.winfo_mapped?})
+
+ @lbox.bind('Configure', proc{_config_proc})
+ @lbox.bind('Enter', proc{|y| _set_sel(@lbox.nearest(y))}, '%y')
+ @lbox.bind('Motion', proc{|y|
+ @up_timer.stop if @up_timer.running?
+ @down_timer.stop if @down_timer.running?
+ _check_sel(@lbox.nearest(y))
+ }, '%y')
+
+ @lbox.bind('Up', proc{_key_UP_proc})
+ @lbox.bind('Down', proc{_key_DOWN_proc})
+ end
+
+ ############################
+ public
+ ############################
+ def scrollbar(mode)
+ if mode
+ @scr.pack(:side=>:right, :fill=>:y)
+ else
+ @scr.pack_forget
+ end
+ end
+end
+
+################################################
+
+class TkCombobox < TkEntry
+ include TkComposite
+
+ @@down_btn_bmp = TkBitmapImage.new(:data=><<EOD)
+#define down_arrow_width 11
+#define down_arrow_height 11
+static unsigned char down_arrow_bits[] = {
+ 0x00, 0x00, 0xfe, 0x03, 0xfc, 0x01, 0xfc, 0x01, 0xf8, 0x00, 0xf8, 0x00,
+ 0x70, 0x00, 0x70, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00};
+EOD
+
+ @@up_btn_bmp = TkBitmapImage.new(:data=><<EOD)
+#define up_arrow_width 11
+#define up_arrow_height 11
+static unsigned char up_arrow_bits[] = {
+ 0x00, 0x00, 0x20, 0x00, 0x20, 0x00, 0x70, 0x00, 0x70, 0x00, 0xf8, 0x00,
+ 0xf8, 0x00, 0xfc, 0x01, 0xfc, 0x01, 0xfe, 0x03, 0x00, 0x00};
+EOD
+
+ def _button_proc(dir = true)
+ @btn.relief(:sunken)
+ x = @frame.winfo_rootx
+ y = @frame.winfo_rooty
+ if dir
+ @top.geometry("+#{x}+#{y + @frame.winfo_height}")
+ else
+ @btn.image(@@up_btn_bmp)
+ @top.geometry("+#{x}+#{y - @top.winfo_reqheight}")
+ end
+ @top.deiconify
+ @lst.focus
+
+ if (idx = values.index(@ent.value))
+ @lst.see(idx - 1)
+ @lst.activate(idx)
+ @lst.selection_set(idx)
+ elsif @lst.size > 0
+ @lst.see(0)
+ @lst.activate(0)
+ @lst.selection_set(0)
+ end
+ @top.grab
+
+ begin
+ @var.tkwait
+ if (idx = @var.to_i) >= 0
+ @ent.value = @lst.get(idx)
+ end
+ @top.withdraw
+ @btn.relief(:raised)
+ @btn.image(@@down_btn_bmp)
+ rescue
+ ensure
+ begin
+ @top.grab(:release)
+ @ent.focus
+ rescue
+ end
+ end
+ end
+ private :_button_proc
+
+ def _init_bindings
+ @btn.bind('1', proc{_button_proc(true)})
+ @btn.bind('3', proc{_button_proc(false)})
+
+ @lst.bind('1', proc{|y| @var.value = @lst.nearest(y)}, '%y')
+ @lst.bind('Return', proc{@var.value = @lst.curselection[0]})
+
+ cancel = TkVirtualEvent.new('2', '3', 'Escape')
+ @lst.bind(cancel, proc{@var.value = -1})
+ end
+ private :_init_bindings
+
+ def initialize_composite(keys={})
+ keys = _symbolkey2str(keys)
+
+ @btn = TkLabel.new(@frame, :relief=>:raised, :borderwidth=>3,
+ :image=>@@down_btn_bmp).pack(:side=>:right,
+ :ipadx=>2, :fill=>:y)
+ @ent = TkEntry.new(@frame).pack(:side=>:left)
+ @path = @ent.path
+
+ @top = TkToplevel.new(@btn, :borderwidth=>1, :relief=>:raised) {
+ withdraw
+ transient
+ overrideredirect(true)
+ }
+
+ startwait = keys.delete('startwait'){300}
+ interval = keys.delete('interval'){150}
+ @lst = TkAutoScrollbox.new(@top,
+ :startwait=>startwait,
+ :interval=>interval).pack(:fill=>:both,
+ :expand=>true)
+ @ent_list = []
+
+ @var = TkVariable.new
+
+ _init_bindings
+
+ delegate('DEFAULT', @ent)
+ delegate('height', @lst)
+ delegate('relief', @frame)
+ delegate('borderwidth', @frame)
+
+ if mode = keys.delete('scrollbar')
+ scrollbar(mode)
+ end
+
+ configure keys unless keys.empty?
+ end
+
+ def scrollbar(mode)
+ @lst.scrollbar(mode)
+ end
+
+ def _reset_width
+ len = @ent.width
+ @lst.get(0, 'end').each{|l| len = l.length if l.length > len}
+ @lst.width(len + 1)
+ end
+ private :_reset_width
+
+ def add(ent)
+ ent = ent.to_s
+ unless @ent_list.index(ent)
+ @ent_list << ent
+ @lst.insert('end', ent)
+ end
+ _reset_width
+ self
+ end
+
+ def remove(ent)
+ ent = ent.to_s
+ @ent_list.delete(ent)
+ if idx = @lst.get(0, 'end').index(ent)
+ @lst.delete(idx)
+ end
+ _reset_width
+ self
+ end
+
+ def values(ary = nil)
+ if ary
+ @lst.delete(0, 'end')
+ @ent_list.clear
+ ary.each{|ent| add(ent)}
+ _reset_width
+ self
+ else
+ @lst.get(0, 'end')
+ end
+ end
+
+ def see(idx)
+ @lst.see(@lst.index(idx) - 1)
+ end
+
+ def list_index(idx)
+ @lst.index(idx)
+ end
+end
+
+
+################################################
+# test
+################################################
+if __FILE__ == $0
+ v = TkVariable.new
+ e = TkCombobox.new(:height=>7, :scrollbar=>true, :textvariable=>v,
+ :startwait=>400, :interval=>200).pack
+ e.values(%w(aa bb cc dd ee ff gg hh ii jj kk ll mm nn oo pp qq rr ss tt uu))
+ #e.see(e.list_index('end') - 2)
+ e.value = 'cc'
+ TkFrame.new{|f|
+ fnt = TkFont.new('Helvetica 10')
+ TkLabel.new(f, :font=>fnt, :text=>'TkCombobox value :').pack(:side=>:left)
+ TkLabel.new(f, :font=>fnt, :textvariable=>v).pack(:side=>:left)
+ }.pack
+
+ TkFrame.new(:relief=>:raised, :borderwidth=>2,
+ :height=>3).pack(:fill=>:x, :expand=>true, :padx=>5, :pady=>3)
+
+ l = TkAutoScrollbox.new(nil, :relief=>:groove, :borderwidth=>4,
+ :width=>20).pack(:fill=>:both, :expand=>true)
+ (0..20).each{|i| l.insert('end', "line #{i}")}
+
+ TkFrame.new(:relief=>:ridge, :borderwidth=>3){
+ TkButton.new(self, :text=>'ON',
+ :command=>proc{l.scrollbar(true)}).pack(:side=>:left)
+ TkButton.new(self, :text=>'OFF',
+ :command=>proc{l.scrollbar(false)}).pack(:side=>:right)
+ pack(:fill=>:x)
+ }
+ Tk.mainloop
+end