From fcbf63e62c627deae76c1b8cb8c0876c536ed811 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 16 Mar 2020 18:49:26 +0900 Subject: Fresh start --- jni/ruby/ext/tk/sample/figmemo_sample.rb | 456 +++++++++++++++++++++++++++++++ 1 file changed, 456 insertions(+) create mode 100644 jni/ruby/ext/tk/sample/figmemo_sample.rb (limited to 'jni/ruby/ext/tk/sample/figmemo_sample.rb') diff --git a/jni/ruby/ext/tk/sample/figmemo_sample.rb b/jni/ruby/ext/tk/sample/figmemo_sample.rb new file mode 100644 index 0000000..da6c417 --- /dev/null +++ b/jni/ruby/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 extension --> 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