From e6697a6405f1330ef071220396b8afef1cd1079a Mon Sep 17 00:00:00 2001 From: nagai Date: Tue, 10 Jun 2008 20:59:10 +0000 Subject: * ext/tk/tcltklib.c: SEGV when tcltk-stubs is enabled. * ext/tk/tcltklib.c: avoid error on a shared object. * ext/tk/extconf.rb: support --with-tcltkversion * ext/tk/README.tcltklib: add document about --with-tcltkversion * ext/tk/lib/tk.rb, ext/tk/lib/multi-tk.rb, ext/tk/lib/remote-tk.rb: not work on $SAFE==4 * ext/tk/lib/multi-tk.rb: Object#methods returns Symbols on Ruby1.9. * ext/tk/lib/tk/timer.rb: add TkTimer#at_end(proc) to register the procedure which called at end of the timer. * ext/tk/lib/tk.rb, ext/tk/lib/tk/itemfont.rb, ext/tk/lib/font.rb: support __IGNORE_UNKNOWN_CONFIGURE_OPTION__ about font options. * ext/tk/lib/*: treat __IGNORE_UNKNOWN_CONFIGURE_OPTION__ * ext/tk/lib/tkextlib/iwidgets/scrolledcanvas.rb, ext/tk/lib/tkextlib/iwidgets/scrolledlistbox.rb, ext/tk/lib/tkextlib/iwidgets/scrolledtext.rb: bug fix. * ext/tk/lib/tk/text.rb: typo. call a wrong method. * ext/tk/lib/tk/itemconfig.rb: ditto. * ext/tk/lib/tk.rb, ext/tk/lib/tk/itemconfig.rb, ext/tk/lib/tk/canvas.rb: support alias names of option keys. * ext/tk/lib/tk/grid.rb: lack of module-method definitions. * ext/tk/lib/tk/pack.rb, ext/tk/lib/tk/grid.rb: increase supported parameter patterns of configure method. * ext/tk/lib/tk.rb: add TkWindow#grid_anchor, grid_column, grid_row. * ext/tk/lib/tk/wm.rb: methods of Tk::Wm_for_General module cannot pass the given block to methods of Tk::Wm module. * ext/tk/lib/tk/wm.rb: Wm#overrideredirect overwrites arguemnt to an invalid value. * ext/tk/lib/tk.rb: fix memory (object) leak bug. * ext/tk/tcltklib.c, ext/tk/tkutil/tkutil.c: fix memory leak. * ext/tk/sample/demos-jp/aniwave.rb, ext/tk/sample/demos-en/aniwave.rb: bug fix. * ext/tk/lib/tkextlib/blt/component.rb, ext/tk/lib/tkextlib/tile/tentry.rb, ext/tk/lib/tkextlib/tile/treeview.rb: ditto. * ext/tk/lib/tkextlib/tile/tpaned.rb: improve TPaned#add. * ext/tk/sample/demos-jp/widget, ext/tk/sample/demos-en/widget, ext/tk/sample/demos-jp/style.rb, ext/tk/sample/demos-en/style.rb, ext/tk/sample/demos-jp/bind.rb, ext/tk/sample/demos-en/bind.rb: bug fix. * ext/tk/sample/ttk_wrapper.rb: ditto. * ext/tk/sample/ttk_wrapper.rb: support "if __FILE__ == $0" idiom. * ext/tk/sample/tktextio.rb: add binding for 'Ctrl-u' at console mode. * ext/tk/lib/tkextlib/tile.rb, ext/tk/lib/tkextlib/tile/style.rb, ext/tk/sample/ttk_wrapper.rb: improve treating and control themes. add Tk::Tile.themes and Tk::Tile.set_theme(theme). * ext/tk/lib/tkextlib/tile.rb: lack of autoload definitions. * ext/tk/lib/tkextlib/tile/tnotebook.rb: cannot use kanji (not UTF-8) characters for headings. * ext/tk/lib/tkextlib/tkDND/shape.rb: wrong package name. * ext/tk/tkutil/tkutil.c: improve handling callback-subst-keys. Now, support longnam-keys (e.g. '%CTT' on tkdnd-2.0; however, still not support tkdnd-2.0 on tkextlib), and symbols of parameters (e.g. :widget=>'%W', :keycode=>'%k', '%x'=>:x, '%X'=>:root_x, and so on; those are attributes of event object). It means that Ruby/Tk accepts not only "widget.bind(ev, '%W', '%k', ...){|w, k, ...| ... }", but also "widget.bind(ev, :widget, :keycode, ...){|w, k, ...| ... }". It is potentially incompatible, when user passes symbols to the arguments of the callback block (the block receives the symbols as strings). I think that is very rare case (probably, used by Ruby/Tk experts only). When causes such trouble, please give strings instead of such symbol parameters (e.g. call Symbol#to_s method). * ext/tk/lib/tk/event.rb, ext/tk/lib/tk/validation.rb, ext/tk/lib/tkextlib/blt/treeview.rb, ext/tk/lib/tkextlib/winico/winico.rb: ditto. * ext/tk/tkutil/tkutil.c: strings are available on subst_tables on TkUtil::CallbackSubst class (it is useful on Ruby 1.9). * ext/tk/lib/tk/spinbox.rb, ext/tk/lib/tkextlib/iwidgets/hierarchy.rb, ext/tk/lib/tkextlib/iwidgets/spinner.rb, ext/tk/lib/tkextlib/iwidgets/entryfield.rb, ext/tk/lib/tkextlib/iwidgets/calendar.rb, ext/tk/lib/tkextlib/blt/dragdrop.rb, ext/tk/lib/tkextlib/tkDND/tkdnd.rb, ext/tk/lib/tkextlib/treectrl/tktreectrl.rb, ext/tk/lib/tkextlib/tktable/tktable.rb: disable code piece became unnecessary by reason of the changes of ext/tk/tkutil/tkutil.c. * ext/tk/lib/tk.rb, ext/tk/lib/multi-tk.rb: change strategy to define the constant WITH_ENCODING. * ext/tk/lib/tk.rb: fix bug on Tk::Encoding.tk_encoding_names. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@17083 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/tk/sample/figmemo_sample.rb | 456 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 456 insertions(+) create mode 100644 ext/tk/sample/figmemo_sample.rb (limited to 'ext/tk/sample/figmemo_sample.rb') diff --git a/ext/tk/sample/figmemo_sample.rb b/ext/tk/sample/figmemo_sample.rb new file mode 100644 index 0000000000..1b6979d2dd --- /dev/null +++ b/ext/tk/sample/figmemo_sample.rb @@ -0,0 +1,456 @@ +#!/usr/bin/env ruby +require 'tk' + +begin + # try to use Img extension + require 'tkextlib/tkimg' +rescue Exception + # cannot use Img extention --> ignore +end + + +############################ +# scrolled_canvas +class TkScrolledCanvas < TkCanvas + include TkComposite + + def initialize_composite(keys={}) + @h_scr = TkScrollbar.new(@frame) + @v_scr = TkScrollbar.new(@frame) + + @canvas = TkCanvas.new(@frame) + @path = @canvas.path + + @canvas.xscrollbar(@h_scr) + @canvas.yscrollbar(@v_scr) + + TkGrid.rowconfigure(@frame, 0, :weight=>1, :minsize=>0) + TkGrid.columnconfigure(@frame, 0, :weight=>1, :minsize=>0) + + @canvas.grid(:row=>0, :column=>0, :sticky=>'news') + @h_scr.grid(:row=>1, :column=>0, :sticky=>'ew') + @v_scr.grid(:row=>0, :column=>1, :sticky=>'ns') + + delegate('DEFAULT', @canvas) + delegate('background', @canvas, @h_scr, @v_scr) + delegate('activebackground', @h_scr, @v_scr) + delegate('troughcolor', @h_scr, @v_scr) + delegate('repeatdelay', @h_scr, @v_scr) + delegate('repeatinterval', @h_scr, @v_scr) + delegate('borderwidth', @frame) + delegate('relief', @frame) + + delegate_alias('canvasborderwidth', 'borderwidth', @canvas) + delegate_alias('canvasrelief', 'relief', @canvas) + + delegate_alias('scrollbarborderwidth', 'borderwidth', @h_scr, @v_scr) + delegate_alias('scrollbarrelief', 'relief', @h_scr, @v_scr) + + configure(keys) unless keys.empty? + end +end + +############################ +class PhotoCanvas < TkScrolledCanvas + +USAGE = <@photo) + + width = self.width + height = self.height + @scr_region = [-width, -height, width, height] + self.scrollregion(@scr_region) + self.xview_moveto(0.25) + self.yview_moveto(0.25) + + @col = 'red' + @font = 'Helvetica -12' + + @memo_id_num = -1 + @memo_id_head = 'memo_' + @memo_id_tag = nil + @overlap_d = 2 + + @state = TkVariable.new + @border = 2 + @selectborder = 1 + @delta = @border + @selectborder + @entry = TkEntry.new(self, :relief=>:ridge, :borderwidth=>@border, + :selectborderwidth=>@selectborder, + :highlightthickness=>0) + @entry.bind('Return'){@state.value = 0} + + @mode = old_mode = 0 + + _state0() + + bind('2', :x, :y){|x,y| scan_mark(x,y)} + bind('B2-Motion', :x, :y){|x,y| scan_dragto(x,y)} + + bind('3'){ + next if (old_mode = @mode) == 0 + @items.each{|item| item.delete } + _state0() + } + + bind('Double-3', :widget, :x, :y){|w, x, y| + next if old_mode != 0 + x = w.canvasx(x) + y = w.canvasy(y) + tag = nil + w.find_overlapping(x - @overlap_d, y - @overlap_d, + x + @overlap_d, y + @overlap_d).find{|item| + ! (item.tags.find{|name| + if name =~ /^(#{@memo_id_head}\d+)$/ + tag = $1 + end + }.empty?) + } + w.delete(tag) if tag + } + end + + #----------------------------------- + private + def _state0() # init + @mode = 0 + + @memo_id_num += 1 + @memo_id_tag = @memo_id_head + @memo_id_num.to_s + + @target = nil + @items = [] + @mark = [0, 0] + bind_remove('Motion') + bind('ButtonRelease-1', proc{|x,y| _state1(x,y)}, '%x', '%y') + end + + def _state1(x,y) # set center + @mode = 1 + + @target = TkcOval.new(self, + [canvasx(x), canvasy(y)], [canvasx(x), canvasy(y)], + :outline=>@col, :width=>3, :tags=>[@memo_id_tag]) + @items << @target + @mark = [x,y] + + bind('Motion', proc{|x,y| _state2(x,y)}, '%x', '%y') + bind('ButtonRelease-1', proc{|x,y| _state3(x,y)}, '%x', '%y') + end + + def _state2(x,y) # create circle + @mode = 2 + + r = Integer(Math.sqrt((x-@mark[0])**2 + (y-@mark[1])**2)) + @target.coords([canvasx(@mark[0] - r), canvasy(@mark[1] - r)], + [canvasx(@mark[0] + r), canvasy(@mark[1] + r)]) + end + + def _state3(x,y) # set line start + @mode = 3 + + @target = TkcLine.new(self, + [canvasx(x), canvasy(y)], [canvasx(x), canvasy(y)], + :arrow=>:first, :arrowshape=>[10, 14, 5], + :fill=>@col, :tags=>[@memo_id_tag]) + @items << @target + @mark = [x, y] + + bind('Motion', proc{|x,y| _state4(x,y)}, '%x', '%y') + bind('ButtonRelease-1', proc{|x,y| _state5(x,y)}, '%x', '%y') + end + + def _state4(x,y) # create line + @mode = 4 + + @target.coords([canvasx(@mark[0]), canvasy(@mark[1])], + [canvasx(x), canvasy(y)]) + end + + def _state5(x,y) # set text + @mode = 5 + + if x - @mark[0] >= 0 + justify = 'left' + dx = - @delta + + if y - @mark[1] >= 0 + anchor = 'nw' + dy = - @delta + else + anchor = 'sw' + dy = @delta + end + else + justify = 'right' + dx = @delta + + if y - @mark[1] >= 0 + anchor = 'ne' + dy = - @delta + else + anchor = 'se' + dy = @delta + end + end + + bind_remove('Motion') + + @entry.value = '' + @entry.configure(:justify=>justify, :font=>@font, :foreground=>@col) + + ewin = TkcWindow.new(self, [canvasx(x)+dx, canvasy(y)+dy], + :window=>@entry, :state=>:normal, :anchor=>anchor, + :tags=>[@memo_id_tag]) + + @entry.focus + @entry.grab + @state.wait + @entry.grab_release + + ewin.delete + + @target = TkcText.new(self, [canvasx(x), canvasy(y)], + :anchor=>anchor, :justify=>justify, + :fill=>@col, :font=>@font, :text=>@entry.value, + :tags=>[@memo_id_tag]) + + _state0() + end + + #----------------------------------- + public + def load_photo(filename) + @photo.configure(:file=>filename) + end + + def modified? + ! ((find_withtag('all') - [@img]).empty?) + end + + def fig_erase + (find_withtag('all') - [@img]).each{|item| item.delete} + end + + def reset_region + width = @photo.width + height = @photo.height + + if width > @scr_region[2] + @scr_region[0] = -width + @scr_region[2] = width + end + + if height > @scr_region[3] + @scr_region[1] = -height + @scr_region[3] = height + end + + self.scrollregion(@scr_region) + self.xview_moveto(0.25) + self.yview_moveto(0.25) + end + + def get_texts + ret = [] + find_withtag('all').each{|item| + if item.kind_of?(TkcText) + ret << item[:text] + end + } + ret + end +end +############################ + +# define methods for menu +def open_file(canvas, fname) + if canvas.modified? + ret = Tk.messageBox(:icon=>'warning',:type=>'okcancel',:default=>'cancel', + :message=>'Canvas may be modified. Realy erase? ') + return if ret == 'cancel' + end + + filetypes = [ + ['GIF Files', '.gif'], + ['GIF Files', [], 'GIFF'], + ['PPM Files', '.ppm'], + ['PGM Files', '.pgm'] + ] + + begin + if Tk::Img::package_version != '' + filetypes << ['JPEG Files', ['.jpg', '.jpeg']] + filetypes << ['PNG Files', '.png'] + filetypes << ['PostScript Files', '.ps'] + filetypes << ['PDF Files', '.pdf'] + filetypes << ['Windows Bitmap Files', '.bmp'] + filetypes << ['Windows Icon Files', '.ico'] + filetypes << ['PCX Files', '.pcx'] + filetypes << ['Pixmap Files', '.pixmap'] + filetypes << ['SGI Files', '.sgi'] + filetypes << ['Sun Raster Files', '.sun'] + filetypes << ['TGA Files', '.tga'] + filetypes << ['TIFF Files', '.tiff'] + filetypes << ['XBM Files', '.xbm'] + filetypes << ['XPM Files', '.xpm'] + end + rescue + end + + filetypes << ['ALL Files', '*'] + + fpath = Tk.getOpenFile(:filetypes=>filetypes) + return if fpath.empty? + + begin + canvas.load_photo(fpath) + rescue => e + Tk.messageBox(:icon=>'error', :type=>'ok', + :message=>"Fail to read '#{fpath}'.\n#{e.message}") + end + + canvas.fig_erase + canvas.reset_region + + fname.value = fpath +end + +# -------------------------------- +def save_memo(canvas, fname) + initname = fname.value + if initname != '-' + initname = File.basename(initname, File.extname(initname)) + fpath = Tk.getSaveFile(:filetypes=>[ ['Text Files', '.txt'], + ['ALL Files', '*'] ], + :initialfile=>initname) + else + fpath = Tk.getSaveFile(:filetypes=>[ ['Text Files', '.txt'], + ['ALL Files', '*'] ]) + end + return if fpath.empty? + + begin + fid = open(fpath, 'w') + rescue => e + Tk.messageBox(:icon=>'error', :type=>'ok', + :message=>"Fail to open '#{fname.value}'.\n#{e.message}") + end + + begin + canvas.get_texts.each{|txt| + fid.print(txt, "\n") + } + ensure + fid.close + end +end + +# -------------------------------- +def ps_print(canvas, fname) + initname = fname.value + if initname != '-' + initname = File.basename(initname, File.extname(initname)) + fpath = Tk.getSaveFile(:filetypes=>[ ['Postscript Files', '.ps'], + ['ALL Files', '*'] ], + :initialfile=>initname) + else + fpath = Tk.getSaveFile(:filetypes=>[ ['Postscript Files', '.ps'], + ['ALL Files', '*'] ]) + end + return if fpath.empty? + + bbox = canvas.bbox('all') + canvas.postscript(:file=>fpath, :x=>bbox[0], :y=>bbox[1], + :width=>bbox[2] - bbox[0], :height=>bbox[3] - bbox[1]) +end + +# -------------------------------- +def quit(canvas) + ret = Tk.messageBox(:icon=>'warning', :type=>'okcancel', + :default=>'cancel', + :message=>'Realy quit? ') + exit if ret == 'ok' +end + +# -------------------------------- +# setup root +root = TkRoot.new(:title=>'Fig Memo') + +# create canvas frame +canvas = PhotoCanvas.new(root).pack(:fill=>:both, :expand=>true) +usage_frame = TkFrame.new(root, :relief=>:ridge, :borderwidth=>2) +hide_btn = TkButton.new(usage_frame, :text=>'hide usage', + :font=>{:size=>8}, :pady=>1, + :command=>proc{usage_frame.unpack}) +hide_btn.pack(:anchor=>'e', :padx=>5) +usage = TkLabel.new(usage_frame, :text=>PhotoCanvas::USAGE, + :font=>'Helvetica 8', :justify=>:left).pack + +show_usage = proc{ + usage_frame.pack(:before=>canvas, :fill=>:x, :expand=>true) +} + +fname = TkVariable.new('-') +f = TkFrame.new(root, :relief=>:sunken, :borderwidth=>1).pack(:fill=>:x) +label = TkLabel.new(f, :textvariable=>fname, + :font=>{:size=>-12, :weight=>:bold}, + :anchor=>'w').pack(:side=>:left, :fill=>:x, :padx=>10) + +# create menu +mspec = [ + [ ['File', 0], + ['Show Usage', proc{show_usage.call}, 5], + '---', + ['Open Image File', proc{open_file(canvas, fname)}, 0], + ['Save Memo Texts', proc{save_memo(canvas, fname)}, 0], + '---', + ['Save Postscript', proc{ps_print(canvas, fname)}, 5], + '---', + ['Quit', proc{quit(canvas)}, 0] + ] +] +root.add_menubar(mspec) + +# manage wm_protocol +root.protocol(:WM_DELETE_WINDOW){quit(canvas)} + +# show usage +show_usage.call + +# -------------------------------- +# start eventloop +Tk.mainloop -- cgit v1.2.3