atkw_class_w_test.stl
"Syntactic and semantic conventions for the SETL widget class", with test.
package Tk_interp_holder; -- small auxiliary package for holding TK interpreter object var interp; -- the master tk interpreter end Tk_interp_holder; package body Tk_interp_holder; -- small auxiliary package for holding TK interpreter object end Tk_interp_holder; package doubleclick_pak; -- doubleclick timing package for Tk procedure doubleclikk(the_Tk,proc); -- wrapper procedure for doubleclick, non-canvas items procedure canvas_doubleclikk(the_Tk,proc); -- wrapper procedure for doubleclick, canvas items end doubleclick_pak; package Tk_defaults; -- defaults for Tk attribute values var Tk_children,Tk_packed,Tk_gridded,Tk_placed,Tk_config_data,Tk_binding_tags; var Tk_bindings,Tk_binding_responses,Tk_canvas_objs,Tk_text_objs,Tk_canvas_objs_data; var Tk_canvas_tags_bindings,Tk_canvas_binding_responses; var Tk_text_tags,Tk_text_tags_bindings,Tk_text_binding_responses,Tk_text_tags_data; var new_item_from_orig_name; -- global used in rebuild process -- major global information mappings and sets for persistency system const Tk_pack_defaults := {["padx", "0"], ["anchor", "center"], ["ipadx", "0"], ["expand", "0"], ["fill", "none"], ["pady", "0"], ["ipady", "0"]}; const Tk_grid_defaults := {["padx", "0"], ["ipadx", "0"], ["pady", "0"], ["ipady", "0"], ["rowspan", "1"], ["columnspan", "1"]}; const Tk_place_defaults := {["relx", "0"], ["rely", "0"], ["anchor", "nw"]}; const Tk_button_defaults := {["anchor", "center"], ["activebackground", "systemButtonText"], ["bitmap", ""], ["default", "disabled"], ["foreground", "systemButtonText"], ["height",0], ["underline", "-1"], ["width", "0"], ["wraplength", "0"], ["bd", "borderWidth"], ["background", "systemButtonFace"], ["activeforeground", "systemButtonFace"], ["image", ""], ["command", ""], ["textvariable", ""], ["manager", "pack"], ["state", "normal"], ["font", "system"], ["highlightbackground", "systemWindowBody"], ["cursor", ""], ["relief", "flat"], ["pady", "3"], ["borderwidth", "2"], ["children", []], ["takefocus", ""], ["height", "0"], ["ismapped", 0], ["disabledforeground", "#a3a3a3"], ["highlightthickness", "4"], ["showing", "0"], ["manager", "pack"], ["highlightcolor", "systemButtonFrame"], ["justify", "center"], ["padx", "7"], ["Tk_tags", ["Button", ".", "all"]]}; const Tk_canvas_defaults := {["highlightthickness", "3"], ["selectbackground", "systemHighlight"], ["xscrollcommand", ""], ["yscrollcommand", ""], ["yscrollincrement", "0"], ["xscrollincrement", "0"], ["ismapped", 0], ["confine", "1"], ["insertborderwidth", "0"], ["closeenough", "1.0"], ["borderwidth", "0"], ["bd", "borderWidth"], ["insertofftime", "300"], ["insertontime", "600"], ["highlightcolor", "Black"], ["manager", "pack"], ["selectborderwidth", "1"], ["manager", "pack"], ["background", "systemWindowBody"], ["highlightbackground", "systemWindowBody"], ["cursor", ""], ["relief", "flat"], ["insertwidth", "2"], ["scrollregion", ""], ["selectbackground", "systemHighlight"], ["children", []], ["takefocus", ""], ["showing", "0"], ["selectforeground", "Black"], ["insertbackground", "Black"], ["Tk_tags", ["Canvas", ".", "all"]]}; const Tk_frame_defaults := {["colormap", ""], ["ismapped", 0], ["borderwidth", "0"], ["visual", ""], ["bd", "borderWidth"], ["highlightcolor", "Black"], ["manager", "pack"], ["background", "systemWindowBody"], ["manager", "pack"], ["highlightbackground", "systemWindowBody"], ["container", "0"], ["cursor", ""], ["relief", "flat"], ["class", "Frame"], ["takefocus", "0"], ["highlightthickness", "0"], ["showing", "0"],["Tk_tags", ["Frame", ".", "all"]]}; const Tk_toplevel_defaults := {["colormap", ""], ["height", "0"], ["class", "Tk"], ["width", "0"], ["borderwidth", "0"], ["visual", ""], ["bd", "borderWidth"], ["use", ""], ["menu", ""], ["ismapped", 1], ["highlightcolor", "Black"], ["background", "systemWindowBody"], ["highlightbackground", "systemWindowBody"], ["container", "0"], ["cursor", ""], ["relief", "flat"], ["showing", "1"], ["screen", ""], ["takefocus", "0"], ["highlightthickness", "0"], ["manager", "wm"],["Tk_tags", [".", "Tk", "all"]]}; const Tk_message_defaults := {["anchor", "center"], ["aspect", "150"], ["ismapped", 0], ["width", "0"], ["bd", "borderWidth"], ["textvariable", ""], ["highlightcolor", "Black"], ["manager", "pack"], ["font", "system"], ["background", "systemWindowBody"], ["justify", "left"], ["highlightbackground", "systemWindowBody"], ["cursor", ""], ["relief", "flat"], ["pady", "3"], ["borderwidth", "2"], ["padx", "6"], ["children", []], ["takefocus", "0"], ["highlightthickness", "0"], ["showing", "0"], ["foreground", "Black"], ["Tk_tags", ["Message", ".", "all"]]}; const Tk_label_defaults := {["anchor", "center"], ["bitmap", ""], ["foreground", "systemButtonText"], ["height", "0"], ["ismapped", 0], ["underline", "-1"], ["pady", "1"], ["width", "0"], ["manager", "pack"], ["wraplength", "0"], ["bd", "borderWidth"], ["background", "systemButtonFace"], ["image", ""], ["textvariable", ""], ["padx", "1"], ["font", "system"], ["highlightbackground", "systemWindowBody"], ["cursor", ""], ["relief", "flat"], ["manager", "grid"], ["borderwidth", "2"], ["children", []], ["takefocus", "0"], ["highlightthickness", "0"], ["showing", "0"], ["highlightcolor", "systemButtonFrame"], ["justify", "center"], ["Tk_tags", ["Label", ".", "all"]]}; const Tk_menubutton_defaults := {["anchor", "center"], ["bitmap", ""], ["manager", "pack"], ["height", "0"], ["ismapped", 0], ["underline", "-1"], ["width", "0"], ["wraplength", "0"], ["padx", "4"], ["bd", "borderWidth"], ["image", ""], ["textvariable", ""], ["highlightcolor", "Black"], ["state", "normal"], ["font", "system"], ["direction", "below"], ["background", "systemWindowBody"], ["justify", "left"], ["highlightbackground", "systemWindowBody"], ["cursor", ""], ["relief", "flat"], ["manager", "grid"], ["pady", "3"], ["borderwidth", "2"], ["indicatoron", "0"], ["children", []], ["takefocus", "0"], ["highlightthickness", "0"], ["disabledforeground", "#a3a3a3"], ["activebackground", "#ececec"], ["showing", "0"], ["activeforeground", "Black"], ["foreground", "Black"],["Tk_tags", ["Menubutton", ".", "all"]]}; const Tk_menuitem_defaults := {["activebackground","{} {} {} {}"], ["activeforeground","{} {} {} {}"], ["accelerator","{} {} {} {}"], ["background","{} {} {} {}"], ["bitmap","{} {} {} {}"], ["columnbreak","{} {} 0 0"], ["command","{} {} {} {}"], ["font","{} {} {} {}"], ["foreground","{} {} {} {}"], ["hidemargin","{} {} 0 0"], ["image","{} {} {} {}"],["underline", "{} {} -1 -1"], ["state", "{} {} normal normal"]}; const Tk_menu_defaults := {["activeforeground", "SystemMenuActiveText"], ["foreground", "SystemMenuText"], ["disabledforeground", "SystemMenuDisabled"], ["tearoffcommand", ""], ["postcommand", ""], ["activeborderwidth", "0"], ["borderwidth", "0"], ["bd", "borderWidth"], ["activebackground", "SystemMenuActive"], ["background", "SystemMenu"], ["title", ""], ["cursor", "arrow"], ["type", "normal"], ["font", "system"], ["relief", "flat"], ["tearoff", "0"], ["children", []], ["takefocus", "0"], ["wincoords", [0, 0]], ["selectcolor", "SystemMenuActive"], ["showing", "0"],["rect", [0, 0, 1, 1]], ["ismapped", 0], ["manager", "wm"], ["Tk_tags", ["Menu", "all"]]}; const Tk_text_defaults := {["highlightthickness", "3"], ["selectforeground", "systemHighlightText"], ["selectbackground", "systemHighlight"], ["xscrollcommand", ""], ["yscrollcommand", ""], ["setgrid", "0"], ["ismapped", 0], ["pady", "1"], ["insertborderwidth", "0"], ["borderwidth", "0"], ["bd", "borderWidth"], ["spacing1", "0"], ["insertofftime", "300"], ["insertontime", "600"], ["padx", "1"], ["highlightcolor", "Black"], ["selectborderwidth", "1"], ["state", "normal"], ["insertwidth", "1"], ["background", "systemWindowBody"], ["highlightbackground", "systemWindowBody"], ["wrap", "char"], ["spacing2", "0"], ["relief", "flat"], ["font", "Courier 12"], ["children", []], ["tabs", ""], ["takefocus", ""], ["spacing3", "0"], ["showing", "0"], ["exportselection", "1"], ["cursor", "xterm"], ["foreground", "Black"], ["insertbackground", "Black"], ["manager", "pack"],["Tk_tags", ["Text", ".", "all"]]}; const Tk_text_tag_data_defaults := {["wrap", ""], ["background", ""], ["offset", ""], ["borderwidth", ""], ["lmargin1", ""], ["spacing1", ""], ["overstrike", ""], ["fgstipple", ""], ["bgstipple", ""], ["underline", ""], ["justify", ""], ["lmargin2", ""], ["spacing2", ""], ["relief", ""], ["rmargin", ""], ["tabs", ""], ["spacing3", ""], ["foreground", ""], ["font", ""]}; const Tk_entry_defaults := {["selectforeground", "systemHighlightText"], ["selectbackground", "systemHighlight"], ["xscrollcommand", ""], ["ismapped", 0], ["insertborderwidth", "0"], ["bd", "borderWidth"], ["insertofftime", "300"], ["insertontime", "600"], ["textvariable", ""], ["highlightcolor", "Black"], ["manager", "pack"], ["selectborderwidth", "1"], ["state", "normal"], ["insertwidth", "1"], ["borderwidth", "1"], ["background", "systemWindowBody"], ["justify", "left"], ["highlightbackground", "systemWindowBody"], ["relief", "solid"], ["font", "Helvetica 12"], ["children", []], ["takefocus", ""], ["highlightthickness", "0"], ["show", ""], ["showing", "0"], ["exportselection", "1"], ["cursor", "xterm"], ["foreground", "Black"], ["insertbackground", "Black"], ["Tk_tags", ["Entry", ".", "all"]]}; const Tk_listbox_defaults := {["width", "8"], ["yscrollcommand", ".w1.w9 set"], ["selectforeground", "systemHighlightText"], ["selectbackground", "systemHighlight"], ["selectmode", "browse"], ["xscrollcommand", ""], ["manager", "pack"], ["setgrid", "0"], ["ismapped", 0], ["selectborderwidth", "0"], ["bd", "borderWidth"], ["highlightcolor", "Black"], ["manager", "pack"], ["borderwidth", "1"], ["background", "systemWindowBody"], ["highlightbackground", "systemWindowBody"], ["cursor", ""], ["relief", "solid"], ["font", "application"], ["children", []], ["takefocus", ""], ["highlightthickness", "0"], ["showing", "0"], ["exportselection", "1"], ["foreground", "Black"], ["Tk_tags", ["Listbox", ".", "all"]]}; const Tk_scrollbar_defaults := {["jump", "0"], ["ismapped", 0], ["manager", "pack"], ["orient", "vertical"], ["borderwidth", "0"], ["repeatinterval", "100"], ["bd", "borderWidth"], ["troughcolor", "#c3c3c3"], ["highlightcolor", "Black"], ["manager", "pack"], ["repeatdelay", "300"], ["elementborderwidth", "-1"], ["background", "systemWindowBody"], ["highlightbackground", "systemWindowBody"], ["cursor", ""], ["relief", "flat"], ["activerelief", "raised"], ["children", []], ["takefocus", ""], ["highlightthickness", "0"], ["activebackground", "#ececec"], ["showing", "0"],["Tk_tags", ["Scrollbar", ".", "all"]]}; const Tk_scale_defaults := {["showvalue", "1"], ["ismapped", 0], ["command", ""], ["manager", "pack"], ["bigincrement", "0.0"], ["width", "10"], ["orient", "horizontal"], ["label", ""], ["tickinterval", "0.0"], ["repeatinterval", "100"], ["bd", "borderWidth"], ["troughcolor", "#c3c3c3"], ["variable", ""], ["highlightcolor", "Black"], ["state", "normal"], ["font", "system"], ["repeatdelay", "300"], ["background", "systemWindowBody"], ["highlightbackground", "systemWindowBody"], ["cursor", ""], ["relief", "flat"], ["sliderrelief", "raised"], ["borderwidth", "2"], ["resolution", "1.0"], ["children", []], ["takefocus", ""], ["digits", "0"], ["highlightthickness", "0"], ["activebackground", "#ececec"], ["showing", "0"], ["foreground", "Black"], ["Tk_tags", ["Scale", ".", "all"]]}; const Tk_checkbutton_defaults := {["anchor", "center"], ["activebackground", "systemButtonText"], ["bitmap", ""], ["foreground", "systemButtonText"], ["height", "0"], ["ismapped", 0], ["manager", "pack"], ["underline", "-1"], ["offvalue", "r1"], ["pady", "1"], ["width", "0"], ["wraplength", "0"], ["bd", "borderWidth"], ["background", "systemButtonFace"], ["activeforeground", "systemButtonFace"], ["image", ""], ["selectimage", ""], ["textvariable", ""], ["padx", "1"], ["manager", "pack"], ["state", "normal"], ["font", "system"], ["highlightbackground", "systemWindowBody"], ["selectcolor", "#b03060"], ["cursor", ""], ["relief", "flat"], ["borderwidth", "2"], ["children", []], ["takefocus", ""], ["disabledforeground", "#a3a3a3"], ["highlightthickness", "4"], ["showing", "0"], ["highlightcolor", "systemButtonFrame"], ["justify", "center"], ["indicatoron", "1"], ["Tk_tags", ["Checkbutton", ".", "all"]]}; const Tk_radiobutton_defaults := {["anchor", "center"], ["activebackground", "systemButtonText"], ["bitmap", ""], ["foreground", "systemButtonText"],["value", "Radio1"], ["height", "0"], ["manager", "pack"], ["ismapped", 0], ["command", ""], ["underline", "-1"], ["pady", "1"], ["width", "0"], ["wraplength", "0"], ["bd", "borderWidth"], ["background", "systemButtonFace"], ["activeforeground", "systemButtonFace"], ["image", ""], ["selectimage", ""], ["textvariable", ""], ["padx", "1"], ["manager", "pack"], ["state", "normal"], ["font", "system"], ["highlightbackground", "systemWindowBody"], ["selectcolor", "#b03060"], ["cursor", ""], ["relief", "flat"], ["borderwidth", "2"], ["children", []], ["takefocus", ""], ["disabledforeground", "#a3a3a3"], ["highlightthickness", "4"], ["showing", "0"], ["highlightcolor", "systemButtonFrame"], ["justify", "center"], ["indicatoron", "1"], ["Tk_tags", ["Radiobutton", ".", "all"]]}; const Tk_oval_defaults := {["width", "1"], ["stipple", ""], ["outline", "black"], ["tags", []]}; const Tk_rectangle_defaults := {}; const Tk_arc_defaults := {["style", "pieslice"], ["width", "1"], ["outlinestipple", ""], ["stipple", ""], ["outline", "black"]}; const Tk_line_defaults := {["smooth", "0"], ["tags", []], ["joinstyle", "round"], ["width", "1"], ["splinesteps", "12"], ["capstyle", "butt"], ["stipple", ""], ["arrow", "none"], ["arrowshape", "8 10 3"], ["fill", "black"]}; const Tk_polygon_defaults := {["smooth", "0"], ["width", "1"], ["splinesteps", "12"], ["outline", ""], ["stipple", ""], ["fill", ""], ["tags", []]}; const Tk_canvaswidget_defaults := {["width", "0"], ["height", "0"], ["anchor", "nw"], ["tags", []]}; const Tk_canvasimage_defaults := {["anchor", "nw"], ["tags", []]}; const Tk_canvastext_defaults := {["width", "0"], ["justify", "left"], ["stipple", ""], ["tags", ["all"]], ["fill", "black"], ["anchor", "nw"], ["tags", []]}; const Tk_data_defaults := {["button",Tk_button_defaults],["canvas",Tk_canvas_defaults],["message",Tk_message_defaults], ["label",Tk_label_defaults],["menubutton",Tk_menubutton_defaults],["frame",Tk_frame_defaults], ["toplevel",Tk_toplevel_defaults],["menu",Tk_menu_defaults], ["scale",Tk_scale_defaults],["checkbutton",Tk_checkbutton_defaults],["radiobutton",Tk_radiobutton_defaults], ["listbox",Tk_listbox_defaults],["scrollbar",Tk_scrollbar_defaults],["oval",Tk_oval_defaults], ["rectangle",Tk_rectangle_defaults],["arc",Tk_arc_defaults],["line",Tk_line_defaults], ["polygon",Tk_polygon_defaults],["text",Tk_text_defaults],["entry",Tk_entry_defaults], ["text_tag_data",Tk_text_tag_data_defaults],["menuitem_data",Tk_menuitem_defaults], ["widget",Tk_canvaswidget_defaults],["image",Tk_canvasimage_defaults],["canvas_text",Tk_canvastext_defaults]}; end Tk_defaults; package body Tk_defaults; -- defaults for Tk attribute values (dummy body, empty) end Tk_defaults; class tkw; -- tk widget class; alternative draft class var show_commands := false; -- DEBUGGING SWITCH class var debug_trace; -- global variable for tracing class var for_tk := OM; -- for accumulating calls to Tk class var dialog_response; -- for transmitting responses from standard dialogs class var prior_id := OM,numcanceled := 0; -- global variables for doubleclick tracking class var namegen_ctr := 0; -- counter for generating auxiliary Tk variables for storing server_socket associated callback procedures var socket_error; -- instance variable,storing error resulting from read and write if object is a socket const cursors_stg := "arrow,double_arrow,based_arrow_down,based_arrow_up,draft_large,draft_small," + "top_left_arrow,right_ptr,center_ptr,right_side,left_side,bottom_side,top_side," + "center_ptr,sh_h_double_arrow,sh_v_double_arrow,sh_left_arrow,sh_right_arrow," + "sh_up_arrow,sh_down_arrow,xterm," + "x_cursor,plus,tcross,crosshair,spider,fleur,iron_cross,diamond_cross," + "cross_reverse,cross,dot," + "right_tee,left_tee,bottom_tee,top_tee," + "ll_angle,lr_angle,ul_angle,ur_angle," + "dotbox,draped_box,sizing,middlebutton,rightbutton,leftbutton,target," + "box_spiral,icon,rtl_logo," + "bottom_left_corner,bottom_right_corner,top_left_corner,top_right_corner," + "exchange,mouse,spraycan,pencil,star,boat,bogosity,pirate,man,question_arrow," + "gobbler,gumby,hand1,hand2,heart,trek,clock,circle,coffee_mug,sailboat,umbrella," + "watch,shuttle"; class var doubleclick,canvas_doubleclick; -- wrapper procedure for doubleclick; transmitted from preceding package -- 'proc' should be a 1-parameter procedure, which expects to be passed the number of prior cancelled events procedure create(); -- creation of fundamental interpreter and empty objects procedure do_all_calls(); -- transmit any accumulated calls to Tk, as single string; then stop accumulating procedure hold_calls(); -- start accumulating calls to Tk procedure Tk_id(); -- returns an object's (short) Tk name procedure Tk_kind(); -- returns an object's Tk type procedure Tk_break(); -- terminate event handling in the Tk sequence procedure Tk_continue(); -- jump in event handling in the Tk sequence procedure dooneevent(); -- wait for some (any) Tk event procedure do_later(proc); -- execute a procedure after a short delay procedure obj_from_tkname(tkname); -- reconstruct a widget from its Tk name procedure tk_parent(); -- the parent object of an object procedure win_of_pt(x,y); -- find the widget containing x,y procedure full_name(); -- finds full tk name string of widget PUBLIC FOR DEBUGGING ONLY procedure selfstr(); -- string conversion procedure beeper(); -- beep procedure; utility for SETL procedure stopper(); -- destruction of top level window to force return from Tk main loop procedure place(); -- returns object x and y coordinates if placed in parent procedure gridbox(i,j); -- returns coordinates of specified gridbox procedure raise(after_obj); -- raises object to position just after after_obj, or to top procedure lower(before_obj); -- lowers object to position just before before_obj, or to bottom procedure createtimer(interval,SETL_fun); -- create a timer callback (rings once) procedure cancel_event(id); -- cancel a timer or idle callback procedure break_event(); -- suppress further processing of an event -- ****** Operations available for all widgets ****** procedure bindtags(tag); -- gets event bindings for specified tag, or for whole widget if tag = OM -- procedure virt_event_info(virt_event); -- gets physical definition of specified virtual events, or virtual event list if param is OM [ Tk{"event",virt_event} ] procedure virt_event_delete(virt_event); -- deletes specified virtual event -- ****** Canvas Operations ****** procedure addtag_after(tag); -- **OK** --the following group of routines -- add a specified tag to the item just before (or after) that a -- given object in the display list, or to all items, or to all enclosed in a given -- rectangle, or to the nearest item to a given point, -- or to items which already have a given tag or numerical identifier procedure addtag_before(tag); -- **OK** procedure addtag_in(tag,rect); -- **OK** -- add tag to all items in a rectangle, or to all items if rect is OM procedure addtag_nearest(tag,xy,halo,start); -- nearest to x,y, or last within radius halo of x,y, or -- first such after item start in the canvas display list **OK** procedure addtag_if(newtag,hastag); -- add new tag if already has a tag. **OK** procedure addtag(newtag); -- add new tag to a canvas item **OK** procedure bbox_tags(tags); -- get bounding box of items with given tags **OK** procedure canvasx(x,roundto); -- map from screen to canvas coordinates, possibly rounded to grid units **OK** procedure canvasy(y,roundto); -- map from screen to canvas coordinates, possibly rounded to grid units **OK** procedure delete_items(tags_or_ids); -- remove the item(s) identified by a tag **OK** procedure delete(); -- delete a canvas item procedure delete_till(end_ci); -- delete a range of canvas items procedure draw_ovals(descriptor_tup); -- draw a group of ovals; called as ca.draw_ovals(descriptor_tup), ca must be canvas -- returns pair consisting of first and last ovals drawn procedure deltag(tags_or_ids); -- remove the specified tags from a canvas item **OK** procedure deltag_if(iftag,tags_or_ids); -- remove the specified tags from the item identified by an id or tag **OK** procedure get_tagindex(tag,index); -- gets the value of an index in a tagged canvas text item procedure get_select(tag); -- gets the value of sel.first and sel.last in a tagged canvas text item procedure set_select(tag,i,j); -- sets the value of sel.first and sel.last in a tagged canvas text item procedure refocus(tag); -- sets the focus to a tagged canvas text item, or gets it if tag = OM procedure find_after(); -- find all the items just above (or below) that with a given tag, **OK** -- or to all items, or to all enclosed in a given rectangle, or to the -- nearest item to a given point, or to items which already have a given tag or id procedure find_before(); -- each of these routines returns a canvas object, identified by the serial number of **OK** procedure find_in(rect); -- the canvas item which it finds **OK** procedure find_touching(rect); -- find all the items touching in a given rectangle **OK** procedure find_nearest(xy,halo,start); -- **OK** procedure find(tag); -- find all the items with a given tag **OK** procedure focus(); -- return widget in win which has the focus procedure focus_in_top(); -- return widget in same toplevel as win which has the focus procedure get_focus(); -- set focus to this window procedure read_grab(); -- determine the modal grab state of this window: none, local, or global procedure grabber(); -- return window which has exerted a grab procedure destroy(); -- destroy a widget **OK** procedure wait(); -- wait for this window to open procedure wait_close(); -- wait for this window to be destroyed -- ****** Canvas Widget Operations ****** -- procedure dchars(m,n); -- delete characters m thru n of specified canvas text item **OK** [textitem(m..n) := ""] -- procedure insert_ct_item(m,stg); -- insert specified string into canvas text item at position m **OK** [textitem(m..n) := stg] procedure index_item(ix_key); -- get numerical value of index_key, which can be active, end, last, etc. **OK** procedure lower_tid(tag_or_id,be); -- lower the item identified by an id or tag either to specified level, -- or to the start of the display list procedure raise_tid(tag_or_id,ab); -- raise the item identified by an id or tag either to speicified level, -- or to the end of the display list procedure move(tag_or_id,dx,dy); -- move the item(s) identified by an id or tag, a specified amount **OK** procedure postscript(options); -- generate postscript for the contents of a canvas. See below for options available procedure scale_item(cent_x,cent_y,amt_x,amt_y); -- **OK** -- scale a canvas item by a specified amount about a specified center procedure scan_mark(x,y); -- place mark indicating scroll position procedure scan_to(x,y); -- scroll to indicated position procedure scan_mark_1(x); -- place mark indicating scroll position procedure scan_to_1(x); -- scroll to indicated position procedure canvas_select(); -- ??? procedure xview_percent(p); -- move to place fraction p of string offscreen to the left; p is real **OK** procedure xview_scroll(n,what); -- scroll horizontally, n 'units' or 'pages' procedure yview_scroll(n,what); -- scroll vertically, n 'units' or 'pages' procedure image_of(rect); -- capture the contents of a rectangle within a canvas, as a Tk absolute image -- ****** Text Widget Operations ****** -- procedure bbox(n); -- return bounding box of specified character procedure compare(op,ix1,ix2); -- compare character indices in line.char and other allowed formats procedure debug(on_off); -- enable consistency checking for B-tree code??? -- procedure delete(m,n); -- delete one character, or a range of characters [text(m..n) := ""] **OK** -- procedure get(m,n); -- return range of characters [text(m..n)] **OK** procedure insert_tt(n,chars_and_tags); -- insert a substring; this can carry specified tags in designated subsections **OK** procedure linebox(n); -- return bounding box and baseline of line n procedure insert_image(n,img); -- insert an image at a specified text position **OK** -- procedure images(); -- return the ordered list of all images in the text widget [txt("images")] **OK** procedure index(ix_stg); -- return line.character position of specified text index **OK** -- text indices can be "current" (char under mouse), "end", "insert" (insert position), -- line.char, image (name), widget (name), mark (stg), tag_name.first, tag_name.last, procedure mark_set(name,n); -- place a named mark at the specified index **OK** procedure mark_unset(name); -- remove a named mark (can also be comma-separated list) procedure mark_gravity(name,n); -- set the 'gravity' (left,right) of a mark, which determines the placement of strings inserted at the mark procedure mark_next(n); -- return the first mark after text position n **OK** procedure mark_prev(n); -- return the last mark before text position n **OK** -- procedure scan_mark(x,y); -- place mark indicating scroll position??? -- procedure scan_to(x,y); -- scroll to indicated position procedure search(options,pattern,n,m); -- string search; returns empty string if unsuccessful **OK** -- search section of text from m to n for a pattern. 'options' parameter can be -- "forward", "backward", "nocase", (count - return count of matched characters in specified var) -- "exact", "regexp" (use regular expression matching) -- unless regular expression ,matching is specified, the 'pattern' is -- only allowed to have "*" (wildcard), ? (one char), or [abc] specified chars procedure see(n); -- scroll to make a given line.character position n visible -- text widget tag information is fetched/set by operations of the syntactic form -- tw("tag","attribute,attribute,...") and tw("tag","attribute,attribute,...") := "val,val,.."; -- to bind callback procedures to textfield tag events we use the syntax -- textfield{"tag_name","event_descriptor,event_fields_signature"} := SETL_procedure; -- this is like all other binding syntax, but carries a tag name as an extra parameter procedure tag_add(tag,index_range_stg); -- add tag to a list of character ranges **OK** (possib. off by 1) procedure tag_remove(tag,index_range_stg); -- remove tag from list of text ranges **OK** (possib. off by 1) -- procedure tag_delete(tag_list); -- delete information for list of tags [txt("tags") := list;] **OK** procedure tag_names(n); -- return ordered list of tags at specified char position. OM gives all **OK** -- procedure tag_lower(tag,below); -- lower tag to specified position in priority list of tags, or to start [txt("tags") := list;] **OK** -- procedure tag_raise(tag,above); -- raise tag to specified position in priority list of tags, or to end [txt("tags") := list;] **OK** procedure tag_nextrange(tag,n,m); -- search for first subrange of specified range that carries specified tag **OK** procedure tag_prevrange(tag,n,m); -- search for last subrange of specified range that carries specified tag **OK** procedure tag_ranges(tag); -- get list of all ranges for specified tag procedure insert_widget(n,wind); -- insert an widget window at a specified text position **OK** -- procedure widgets_in(); -- return the ordered list of all widgets in the text widget [txt("widgets")] **OK** -- procedure xview_percent(p); -- move to place fraction p of string offscreen to the left; p is real **OK** -- procedure xview_scroll(n,what); -- scroll horizontally, n 'units' or 'pages' procedure yview_percent(p); -- move to place fraction p of string offscreen to the top; p is real -- procedure yview_scroll(n,what); -- scroll vertically, n 'units' or 'pages' -- ****** Button Operations ****** procedure flash(); -- cause the button to flash **OK** procedure invoke_button(); -- trigger the button's action **OK** -- procedure deselect(); -- deselect radio button or checkbutton [button("selected") := 0;] **OK** -- procedure select_button(); -- select radio button or checkbutton [button("selected") := 1;] **OK** -- ****** Menu Operations ****** -- procedure activate(n); -- highlight specified entry (zero based) [menu("active") := n;] -- procedure add(the_type,options_values); -- add entry of specified type with specified options [menu(i..i - 1) := labels] **OK** procedure clone(); -- make linked copy of the menu (for tearoffs, etc.) -- procedure delete(m,n); -- delete entries from m to n [menu(n..m) := ""] **OK** -- procedure index(index_key); -- get numerical value of index_key, which can be active, end, last, etc. -- procedure insert(type,n,options); -- insert entry of specified type with specified options at position n [menu(n..m) := labels] **OK** procedure invoke(n); -- trigger the entry's action **OK** procedure post(i,j); -- display menu at specified coordinates procedure popup(i,j); -- display menu at specified coordinates procedure postcascade(n); -- display menu in hierarchical position for entry n procedure entry_type(n); -- get the type of menu entry n [menu(n,"type")] procedure unpost(); -- hide the menu procedure yposition(n); -- return vertical position of top of entry n -- ****** Scale Operations ****** procedure coords(n); -- transform scale value into geometric position **OK** procedure get(ij); -- get scale value, or value corresponding to given position procedure identify(ij); -- return 'trough1' (left of slider), 'slider', or 'trough2 (right of slider)' **OK** -- procedure set_scale(n); -- move the scale to indicated value **OK** [sc(OM) := n;] -- ****** Scrollbar Operations ****** procedure activate(x); -- query/set active element, which can be arrow1, arrow2, or slider procedure delta(dxy); -- convert desired horizontal or vertical value change to slider units procedure fraction(x); -- convert point position into fraction relative to scrollbar extent -- procedure identify(i); -- identify the scrollbar element (arrow1, arrow2, or slider) under point x,y -- ****** Entry Operations ****** procedure bbox(n); -- return bounding box of specified character **OK** -- procedure delete(m,n); -- delete characters from m to n [entry(m..n)] **OK** -- procedure get(m,n); -- return characters from m to n of string in the entry [entry(m..n), #entry] -- procedure index(index_key); -- get numerical value of index_key, which can be anchor, end, insert, etc. **OK** -- procedure insert(n,string); -- insert string at indicated position [entry(m..n) := stg] **OK** -- procedure scan_mark(x); -- place mark indicating scroll position??? -- procedure scan_to(x); -- scroll to indicated position procedure select(m,n); -- select characters m to n, or clear the selection **OK** procedure select_anchor(m); -- set the anchor point for the selection -- procedure xview_percent(p); -- move to place fraction p of string offscreen to the left; p is real **OK** -- procedure xview_scroll(n,what); -- scroll horizontally, n 'units' or 'pages' -- ****** Listbox Operations ****** ---> Working On these -- procedure activate(n); -- activate specified line [listbox("active") := line;] -- procedure bbox(n); -- return bounding box of specified line ???? -- procedure curselection(); -- return list of selected lines [listbox(OM)] **OK** -- procedure delete(i,j); -- delete indicated range of lines [listbox(i..j) := ""] **OK** -- procedure get(m,n); -- return lines m thru n [listbox(m..n)] **OK** -- procedure index(index_key); -- get numerical value of index_key, which can be anchor, end, insert, etc. **OK** -- procedure insert(n,strings); -- insert list of strings before indicated item [listbox(m..m - 1) := lines] **OK** procedure nearest(y); -- return index of line vertically nearest to y -- procedure scan_mark(x); -- place mark indicating scroll position??? -- procedure scan_to(x); -- scroll to indicated position -- procedure see(n); -- adjust display to make line n visible -- procedure select_anchor(m); -- set the anchor line for the selection -- procedure select(m,n); -- select lines m to n, or clear the selection procedure is_select_line(m); -- determine if line m is selected -- procedure size(); -- number of elements in list [#listbox] **OK** -- procedure xview(n); -- move to make character line visible, or read vertical scroll position -- procedure xview_percent(p); -- move to place fraction p of string offscreen to the left; p is real **OK** -- procedure xview_scroll(n,what); -- scroll horizontally, n 'units' or 'pages' procedure yview(n); -- move to make indicated line visible, or read vertical scroll position -- procedure yview_percent(p); -- move to place fraction p of string offscreen to the top; p is real -- procedure yview_scroll(n,what); -- scroll vertically, n 'units' or 'pages' -- ****** Clipboard Operations ****** procedure clear_selection(win,the_sel); -- clear specified selection in specified window procedure get_selection(win,the_sel,the_type); -- return the specified selection procedure handle_selection(win,the_type,format,the_sel,proc); -- define proc to be handler for set/the_type selection requests when 'win' is selection owner procedure own_selection(win,the_sel,proc); -- assert that win is sel owner; and that proc should be called when it loses ownership procedure selection_owner(win,the_sel); -- find string name of current owner of selection 'sel' procedure clear_clipboard(win); -- clear clipboard for specified window procedure addto_clipboard(win,the_type,format,data); -- add 'data', of specified format and type, to clipboard for specified window -- ****** Dialogs and Message boxes ****** -- Note: all these have been put in the syntax win("ask_...","options") := "option_vals"; -- ****** Absolute Image Operations; see final comments for other image operations in SETL syntax ****** procedure dither(); -- dither the image **OK** procedure write_im(file,options); -- write image to file **OK** procedure copy_im(source,options); -- copy one image to another **OK** procedure stuff_im(data,rect); -- insert data into image rectangle ??? -- ****** Window Manager Operations ****** procedure win_close(); -- close or iconify a toplevel procedure win_open(); -- open or deiconify a toplevel procedure containing(x,y); -- window containing given point procedure pixels(n); -- number of pixels corresponding to given size in screen units procedure fpixels(n); -- floating number of pixels corresponding to given size in screen units procedure rgb(color_name); -- numerical code for named color procedure get_winfo_attr(att); -- get an attribute available through the Tk 'winfo' primitives -- ****** Rastport Operations ****** procedure put_img(gr_img,x,y); -- stuff gr_img into tkrport at position x, y procedure put_add(gr_img,x,y); -- stuff gr_img into tkrport using 'sum' procedure put_dif(gr_img,x,y); -- stuff gr_img into tkrport using 'dif' procedure put_mul(gr_img,x,y); -- stuff gr_img into tkrport using 'mul' procedure put_div(gr_img,x,y); -- stuff gr_img into tkrport using 'div' procedure put_min(gr_img,x,y); -- stuff gr_img into tkrport using 'min' procedure put_max(gr_img,x,y); -- stuff gr_img into tkrport using 'max' procedure put_blend(gr_img,x,y,c1,c2); -- blend the image gr_img with the tk widget at position x, y using coefficients c1 and c2 -- ****** rotated cases of the put operations ****** procedure put_imgr(gr_img,x,y); -- stuff gr_img into tkrport at position x, y procedure put_addr(gr_img,x,y); -- stuff gr_img into tkrport using 'sum' procedure put_difr(gr_img,x,y); -- stuff gr_img into tkrport using 'dif' procedure put_mulr(gr_img,x,y); -- stuff gr_img into tkrport using 'mul' procedure put_divr(gr_img,x,y); -- stuff gr_img into tkrport using 'div' procedure put_minr(gr_img,x,y); -- stuff gr_img into tkrport using 'min' procedure put_maxr(gr_img,x,y); -- stuff gr_img into tkrport using 'max' procedure put_blendr(gr_img,x,y,c1,c2); procedure get_img(gr_img,x,y); -- stuff gr_img into tkrport at position x, y procedure get_add(gr_img,x,y); -- stuff gr_img into tkrport using 'sum' procedure get_dif(gr_img,x,y); -- stuff gr_img into tkrport using 'dif' procedure get_mul(gr_img,x,y); -- stuff gr_img into tkrport using 'mul' procedure get_div(gr_img,x,y); -- stuff gr_img into tkrport using 'div' procedure get_min(gr_img,x,y); -- stuff gr_img into tkrport using 'min' procedure get_max(gr_img,x,y); -- stuff gr_img into tkrport using 'max' procedure get_blend(gr_img,x,y,c1,c2); -- blend the image gr_img with the tk widget at position x, y using coefficients c1 and c2 -- ****** Font Routines ****** procedure font_metrics(font); -- get the metrics of the designated font procedure measure_fonted(stg,font); -- get the size of the string in the designated font procedure font_families(); -- get the list of fonts available in Tk -- ****** File Routines ****** procedure disks(); -- get the currently mounted disks -- ****** Socket Routines ****** procedure socket_close(); -- close a socket -- ****** Main Control Operations ****** procedure mainloop(); -- call the tk main loop and wait for callback procedure handle_event(); -- GIUSEPPE procedure get_event_source_function(); -- GIUSEPPE procedure quit(); -- close the tk interpreter procedure call(txt); -- transmit a command to the tk main loop procedure setvar(name,val); -- set a tk variable to the indicated value procedure getvar(name); -- read a tk variable procedure waitvar(name); -- wait for the specified tk variable to change procedure update(); -- request screen display update -- ****** Miscellaneous Utilities ****** procedure clock(); -- clock and date utility -- returns time in format [very_fine,seconds,dau,month,am_pm,weekno_in_year,mm/dd/yy,abbrev_time,monthno,dayno_in_year,dayno_in_week] -- ****** Temporarily exposed for development/debugging ****** procedure stgs_from_Tk(stg); -- get list of strings from Tk blank-delimited form; procedure stg_to_Tk(stg); -- sanitize the quote marks, blanks, backslashes, and square brackets in a string procedure as_map(stg); -- converts a Tk configuration descriptor string to a mapping from attrbute names to values -- ********** Routines for persistency ************** procedure get_Tk_packed(); -- gets the Tk packing information as a map procedure get_Tk_gridded(); -- gets the Tk gridding information as a map procedure get_Tk_children(); -- gets the full hierarchy of Tk children as a map procedure setup_from_dump(target_texwidg_name,dump_stg); -- reconstruct a text area grom its dump string procedure sep_tags_and_marks(stg_tup); -- separate a string's dump tuple into its text, plus tags_and_marks procedure reconstruct_image_from_name(img_name); -- rebuild an existing absolute image using its name procedure reconstruct_bitmap_from_name(bm_name); -- rebuild an existing absolute bitmap using its name end tkw; package body doubleclick_pak; -- supplementarty package for doubleclick-detector utility routine use tkw; -- use the main widget class var Tk,prior_id := OM,numcanceled := 0,canvasevent_pending := false; -- global variables for doubleclick tracking procedure doubleclikk(the_Tk,proc); -- wrapper procedure for doubleclick Tk := the_Tk; return lambda; -- return this closure, with 'proc' bound in, to be called when a clock occurs if canvasevent_pending then return; end if; --a non-canvas event cannot cancel a pending canvas event if prior_id /= OM then Tk.cancel_event(prior_id); numcanceled +:= 1; end if; prior_id := Tk.createtimer(200,catch_procedure(proc)); -- set the catch_procedure (a closure) to be called after a delay procedure catch_procedure(proc); -- catch procedure for doubleclick; binds in the procedure parameter of doubleclick return lambda; prior_id := OM; numc := numcanceled; numcanceled := 0; proc(numc); end lambda; end catch_procedure; end lambda; end doubleclikk; procedure canvas_doubleclikk(the_Tk,proc); -- wrapper procedure for doubleclick Tk := the_Tk; return lambda; -- return this closure, with 'proc' bound in, to be called when a clock occurs if prior_id /= OM then Tk.cancel_event(prior_id); numcanceled +:= 1; end if; prior_id := Tk.createtimer(200,catch_procedure(proc)); -- set the catch_procedure (a closure) to be called after a delay canvasevent_pending := true; -- note that a canvas item event is now pending procedure catch_procedure(proc); -- catch procedure for doubleclick; binds in the procedure parameter of doubleclick return lambda; prior_id := OM; numc := numcanceled; numcanceled := 0; proc(numc); canvasevent_pending := false; end lambda; end catch_procedure; end lambda; end canvas_doubleclikk; end doubleclick_pak; -- this class uses the tk native package, which provides the routines -- procedure tk_create(); -- create a tk interpreter -- procedure tk_kall(tkobj,cmd); -- transmit a command to the tk interpreter -- procedure tk_createcommand(tkobj,cmd,fun); -- create a new callback command for the tk interpreter -- procedure tk_dooneevent(tkobj); -- procedure tk_mainloop(tkobj); -- call the tk interpreter and wait for a callback -- procedure tk_quit(tkobj); -- close the tk interpreter????? -- procedure tk_createtimer(interval,fun); -- procedure tk_idlecallback(fun); -- The following is an alternative version of the tk widget class and various related objects -- (e.g. canvas items). Each class instance has a tk_type, which can be either button, menu, -- menubutton, frame, toplevel, label, message, scale, scrollbar, entry, listbox, text, canvas -- (the builtin tk widgets), or arc, bitmap, image, line, oval, polygon, canvas_text, widget -- (the canvas items). The tk interpreter identifies widgets by their place in its name hierarchy, -- and canvas items by their serial number in the canvas to which they belong. This class gives -- every item a unique generated name of the form Wnnn (for widgets) or Cnnn (for canvas items), -- allowing SETL widgets and canvas item objects to be identified rapidly from their tk names. -- Basic syntactic conventions: the most basic syntactic conventions established by this package -- are those concerning attribute set/get operations (corresponding to 'cget' and 'configure' operations -- in Tk, and those having to do with callback operation binding (corresponding to the Tk 'bind' -- and the Tk 'command' parameter). attribute set/get operations for a widget w are represented in the form -- w("attr,attr,..") and w("attr,attr,..") := "val,val,..". Callback operations are bound to events -- and the widgets or tags to which these events are delivered by statements of forms like -- w{"event_descriptor:event_fields_signature"} := SETL_procedure; -- the special case of button, checkbutton, radiobutton, and menu button commands are handled -- without any event_fields_signature, using the syntax -- obj{OM} := SETL_procedure; -- to implement this syntax, we use the tk_createcommand(interp,tk_command_name,SETL_procedure) -- call of the underlying native library. This posts the (parameterless) SETL_procedure to the -- tk interpreter, as a new externally implemented command whose name is tk_command_name. This -- routine is called inside the stg_for_tk routine, which converts all non-tk right-hand sides -- of such calls to string forms acceptable to the tk interpreter. -- More generally, we use this syntax to send its 'principal command' to any widget; this is the -- command triggered by whatever we choose to regard as the widget's 'principal event' For -- listboxes and tags in text widgets, this is a button-up ; for menubuttons it is -- a button-down ; for text entries and text widgets (outside of text tags) it is loss of -- focus ; for scales it is dragging motion ; for canvas_items it is button-down -- ; for menus it is a button-up ; for frames, toplevels, and canvases -- it is dragging motion . class body tkw; -- tk widget class; alternative draft use tk; -- use the tk native package use string_utility_pak; -- use various SETL utility packages use image; -- object wrappings for grlib images use Tk_interp_holder; -- small auxiliary package for holding TK interpreter object use doubleclick_pak; -- doubleclick timing package for Tk use Tk_defaults; -- defaults for Tk attribute values const button := "button", menu := "menu", menubutton := "menubutton", frame := "frame", rastport := "rastport", toplevel := "toplevel", label := "label", message := "message", scale := "scale", scrollbar := "scrollbar", entry := "entry", listbox := "listbox", text := "text", canvas := "canvas", checkbutton := "checkbutton", radiobutton := "radiobutton"; -- the tk event types const event_types := {"activate", "buttonpress", "buttonrelease", "circulate", "colormap", " configure", "deactivate", "destroy", "enter", "expose", "focusin", "focusout", "gravity", "keypress", "keyrelease", "motion", "leave", "map", "property", "reparent", "unmap", "visibility", "double", "triple"}; const event_opts_from_chars := -- mapping of event characters to option keywords {["#","serial"],["a","above"],["b","button"],["c","count"],["d","detail"], ["f","focus"],["h","height"],["k","keycode"],["m","mode"],["o","override"], ["p","place"],["s","state"],["t","time"],["w","width"],["x","x"],["y","y"], ["B","borderwidth"],["E","sendevent"],["K","keysym"],["R","root"], ["S","subwindow"],["X","rootx"],["Y","rooty"]}; const widgets := {button, checkbutton, radiobutton, menu, menubutton, frame, rastport, toplevel, label, message, scale, scrollbar, entry, listbox, text, canvas}; -- note that we do not count 'image' as a widget, but handle it a bit specially, -- even though tkw objects of type 'image'will be formed const main_command := { ["menubutton",""], --["button",""], ["checkbutton",""], ["radiobutton",""], --["scale",""], __ these items hve built-in '-command' options ["menu",""], ["frame",""], ["rastport",""], ["toplevel",""], ["entry",""], ["listbox",""], ["text",""], ["optionbutton",""], ["canvas",""], ["label",""], ["message",""], ["arc",""],["bitmap",""], ["image",""], ["line",""], ["oval",""], ["canvas_text",""], ["polygon",""], ["rectangle",""], ["widget",""]}; const main_options := {["button","text"],["menu","type"],["menubutton","text"],["frame","hw"],["rastport","hw"], ["toplevel","hw"],["label","text"],["message","text"],["scale","ft"], ["scrollbar","orient_w"],["entry","width"],["listbox","height"], ["text","hw"],["canvas","hw"],["checkbutton","text"],["radiobutton","text"]}; const horiz_scrollable := {"entry","listbox","text","canvas"}; -- horizontally scrollable widgets const fully_scrollable := {"listbox","text","canvas"}; -- fully scrollable widgets const arc := "arc", bitmap := "bitmap", imaje := "image", line := "line", oval := "oval", polygon := "polygon", rectangle := "rectangle", widget := "widget", canvas_text := "canvas_text"; const window := "window", all := "all"; const canvas_items := {arc, bitmap, imaje, line, oval, polygon, rectangle, canvas_text, widget}; -- image and widget are also text items -- principal keywords for geometry managers const geom_manager_main_atts := {"side","pack","grid","place","coords"}; const geometry_keywords := {"pack","side","grid","row","column","place"}; -- keywords indicating geometry manager calls const gen_attributes := -- general 'winfo' attributes of widgets {"children","showing","manager","parent","rect","wincoords","toplevel","ismapped","height","width", -- wincoords is absolute window location of the toplevel window ancestor of an object "mouse","screendepth","screensize","screenmm"} + -- environent attributes {"cells", "children", "class", "colormapfull", "depth", "height", "id", -- , "geometry" (moved to wm_attributes) "ismapped", "manager", "name", "parent", "pointerx", "pointery","reqheight", "reqwidth", "rootx", "rooty", "screen", "screencells", "screendepth", "screenheight", "screenwidth", "screenmmheight", "screenmmwidth", "screenvisual", "server", "toplevel", "viewable", "visual", "visualid", "vrootheight", "vrootwidth", "vrootx", "vrooty", "width", "x", "y", "atom", "atomname", "containing", "interps", "pathname", "exists", "fpixels", "pixels", "rgb", "visualsavailable"}; const wm_attributes := -- general 'wm' attributes of toplevels {"wingrid","iconified","iconposition","maxsize","minsize","resizable","sizefrom","winstate","geometry","aspect","title"}; const wm_attributes_list := ["wingrid","iconposition","maxsize","minsize","resizable","sizefrom","winstate","geometry","aspect","title", "height","width","borderwidth","bd", "borderWidth","menu","highlightcolor", "background","highlightbackground","cursor","relief","takefocus","highlightthickness"]; const for_toplevel_config := {"height","width","borderwidth","highlightcolor","background", "highlightbackground","cursor","menu","relief","takefocus","highlightthickness"}; const attributes_of := { -- maps widget and canvas_item kinds to their valid attributes -- all widgets have the following attributes, available through the Tk 'winfo' command [all,{"cells", "children", "class", "colormapfull", "depth", "geometry", "height", "id", "ismapped", "manager", "name", "parent", "pointerx", "pointery", "reqheight", "reqwidth", "rootx", "rooty", "screen", "screencells", "screendepth", "screenheight", "screenwidth", "screenmmheight", "screenmmwidth", "screenvisual", "server", "toplevel", "viewable", "visual", "visualid", "vrootheight", "vrootwidth", "vrootx", "vrooty", "width", "x", "y", "atom", "atomname", "containing", "interps", "pathname", "exists", "fpixels", "pixels", "rgb", "visualsavailable"}], -- specific widgets have the following additional attributes [button,{"activebackground", "activeforeground", "anchor", "background", "bitmap", "borderwidth", "cursor", "default", "disabledforeground", "font", "foreground", "height", "highlightbackground", "highlightcolor", "highlightthickness", "image", "justify", "padx", "pady", "relief", "state", "takefocus", "text", "textvariable", "underline", "width", "wraplength"}], [menu,{"activebackground", "activeborderwidth", "activeforeground", "background", "borderwidth", "cursor", "disabledforeground", "font", "foreground", "postcommand", "relief", "selectcolor", "takefocus", "tearoff", "tearoffcommand", "title", "type"}], [menubutton,{"activebackground", "activeforeground", "anchor", "background", "bitmap", "borderwidth", "cursor", "direction", "disabledforeground", "font", "foreground", "height", "highlightbackground", "highlightcolor", "highlightthickness", "image", "indicatoron", "justify", "menu", "padx", "pady", "relief", "state", "takefocus", "text", "textvariable", "underline", "width", "wraplength"}], [frame,{"background", "borderwidth", "class", "colormap", "container", "cursor", "height", "highlightbackground", "highlightcolor", "highlightthickness", "relief", "takefocus", "visual", "width"}], [toplevel,{"background", "borderwidth", "class", "colormap", "container", "cursor", "height", "highlightbackground", "highlightcolor", "highlightthickness", "menu", "relief", "screen", "takefocus", "use", "visual", "width"}], [label,{"anchor", "background", "bitmap", "borderwidth", "cursor", "font", "foreground", "height", "highlightbackground", "highlightcolor", "highlightthickness", "image", "justify", "padx", "pady", "relief", "takefocus", "text", "textvariable", "underline", "width", "wraplength"}], [message,{"anchor", "aspect", "background", "borderwidth", "cursor", "font", "foreground", "highlightbackground", "highlightcolor", "highlightthickness", "justify", "padx", "pady", "relief", "takefocus", "text", "textvariable", "width"}], [scale,{"activebackground", "background", "bigincrement", "borderwidth", "cursor", "digits", "font", "foreground", "from", "highlightbackground", "highlightcolor", "highlightthickness", "label", "length", "orient", "relief", "repeatdelay", "repeatinterval", "resolution", "showvalue", "sliderlength", "sliderrelief", "state", "takefocus", "tickinterval", "to", "troughcolor", "variable", "width"}], [scrollbar,{"activebackground", "activerelief", "background", "borderwidth", "cursor", "elementborderwidth", "highlightbackground", "highlightcolor", "highlightthickness", "jump", "orient", "relief", "repeatdelay", "repeatinterval", "takefocus", "troughcolor", "width"}], [entry,{"background", "borderwidth", "cursor", "exportselection", "font", "foreground", "highlightbackground", "highlightcolor", "highlightthickness", "insertbackground", "insertborderwidth", "insertofftime", "insertontime", "insertwidth", "justify", "relief", "selectbackground", "selectborderwidth", "selectforeground", "show", "state", "takefocus", "textvariable", "width", "xscrollcommand"}], [listbox,{"background", "borderwidth", "cursor", "exportselection", "font", "foreground", "height", "highlightbackground", "highlightcolor", "highlightthickness", "relief", "selectbackground", "selectborderwidth", "selectforeground", "selectmode", "setgrid", "takefocus", "width", "xscrollcommand", "yscrollcommand"}], [text,{"background", "borderwidth", "cursor", "exportselection", "font", "foreground", "height", "highlightbackground", "highlightcolor", "highlightthickness", "insertbackground", "insertborderwidth", "insertofftime", "insertontime", "insertwidth", "padx", "pady", "relief", "selectbackground", "selectborderwidth", "selectforeground", "setgrid", "spacing1", "spacing2", "spacing3", "state", "tabs", "takefocus", "width", "wrap", "xscrollcommand", "yscrollcommand"}], [canvas,{"coords", "background", "borderwidth", "class", "colormap", "container", "cursor", "height", "highlightbackground", "highlightcolor", "highlightthickness", "menu", "relief", "screen", "takefocus", "use", "visual", "width"}], [arc,{"extent", "fill", "outline", "outlinestipple", "start", "stipple", "style", "tags", "width"}], -- [bitmap,{,,,,,,,,}], -- [imaje,{,,,,,,,,}], [line,{"arrow", "arrowshape", "capstyle", "fill", "joinstyle", "smooth", "splinesteps", "stipple", "tags", "width"}], [oval,{"fill", "outline", "stipple", "tags", "width"}], [polygon,{"fill", "smooth", "splinesteps", "stipple", "tags", "width"}], [rectangle, {"fill", "outline", "stipple", "tags", "width"}], ["canvas_text",{"anchor", "fill", "font", "justify", "stipple", "tags", "text", "width"}], ["text_tag",{"background", "bgstipple", "borderwidth", "fgstipple", "font", "foreground", "justify", "lmargin1", "lmargin2", "offset", "overstrike", "relief", "rmargin", "spacing1", "spacing2", "spacing3", "tabs", "underline", "wrap"}], ["canvas_tag",{"arrow", "arrowshape", "capstyle", "fill", "joinstyle", "smooth", "splinesteps", "stipple", "tags", "width"}], ["image",{"anchor","image","tags"}], -- attributes of canvas images, not real images ["widget",{"anchor", "tags", "height", "width", "window"}] -- attributes of canvas widgets, not real widgets }; -- indices which can be used to identify menu items const menu_index_indices := {"active", "end", "last", "none"}; -- can also be n, @j, or label match pattern -- indices which can be used to identify listbox items const listbox_index_indices := {"anchor", "end", "active"}; -- can also be n, @i, or @x,y const entry_indices := {"anchor", "end", "insert", "sel.first", "sel.last"}; -- can also be n or @j const text_indices := {"current", "end", "insert", "sel.first", "sel.last"}; -- can also be line.char, @x,y, image_name, mark_name, widget_name, tag.first, tag.last const pack_options := -- options for the 'pack' geometry manager {"after","anchor","before","expand","fill","in","ipadx","ipady","ipadx","ipady","side"}; const grid_options := -- options for the 'grid' geometry manager {"column","columnspan","row","rowspan","in","ipadx","ipady","padx","pady","ipadx","ipady","sticky"}; const place_options := -- options for the 'place' geometry manager {"anchor","x","y","relx","rely","in","width","height","relheight","bordermode"}; const key_attributes := ["extent","window","image","bitmap","smooth","font"]; -- for determination of canvas item type from item number const special_atts := {"sel.anchor","end","insert","sel.first","sel.last","coords"}; -- special attributes of entry widgets, text widgets, and canvas text items (the two latter have no 'anchor') -- for text widgets tag.first and tag.last are available for all defined tags const special_atts_less_anchor := {"end","insert","sel.first","sel.last","coords"}; const pseudo_atts := {"clipboard","fonts","definedFonts","placed","packed", -- various pseudo_atts "gridded","image","type","limits","position", "active", "propagate"}; const special_lefts := {"xscroller", "yscroller", "clipboard", "grab", "sel.anchor", "sel", "active", "xview", "yview" ,"xpercent", "ypercent","limits","position"}; -- various pseudo_atts usable on left const rel_atts_of_images := {"anchor","tags","image","align","name","padx","pady"}; -- relative attributes of images, as distinct from their internal attributes const posns_map := {["mac_creator",2],["mac_hiddden",4],["mac_readonly",6],["mac_type",8],["mtime",9],["atime",10],["gid",11],["nlink",12], ["mode",13],["type",14],["ctime",15],["uid",16],["ino",17],["size",18],["dev",19],["pointer",20]}; --positions map for file attributes class var window_open_flag := OM, -- flag for base window already open name_ctr := 0, -- counter for generation of widget names proc_ctr := 0, -- counter for generation of procedure ids proc_tk_name := { }, -- maps SETL procedures into their tk string names -- obj_of := { }, -- maps widget name to widget object source_of := { }; -- maps canvas image and widget items to their source objects var name := ".", -- section of name, between '.'s tk_type := "", -- tk type of object parent := OM; -- parent object of widget procedure create(); -- creation of fundamental interpreter and empty objects -- if not adjusted, all the empty objects created here are seen as the tk root object -- initialize the tk interpreter if necessary if interp = OM then interp := tk_create(); tk_kall("update"); doubleclick := doubleclikk; canvas_doubleclick := canvas_doubleclikk; end if; if window_open_flag = OM then window_open_flag := 0; -- open master window just once tk_kall("frame . -height 300 -width 300 "); tk_type := "toplevel"; -- abend_trap := the_end; end if; end create; procedure the_end; print("debug_trace: ",debug_trace); tk_kall("beep"); stop; end the_end; procedure tk_kall(cmd); -- conditionally traces the tk_calls if show_commands then print(cmd); end if; -- trace the tk_call if for_tk = OM then res := tk_call(interp,cmd); if show_commands then print(res); end if; return res; end if; -- then execute it and return the result for_tk with:= cmd; return OM; -- otherwise accumulate command end tk_kall; procedure do_all_calls(); -- transmit all calls to Tk, as single string if for_tk = OM then return; end if; -- nothing to do if not accumulating tk_call(interp,join(for_tk,"\n")); for_tk := OM; end do_all_calls; procedure hold_calls(); -- start accumulating calls to Tk do_all_calls(); for_tk := []; end hold_calls; procedure Tk_id(); return name; end Tk_id; -- returns an object's (short) Tk name procedure Tk_kind(); return tk_type; end Tk_kind; -- returns an object's Tk type procedure Tk_break(); tk_call(interp,"break"); end Tk_break; -- terminate event handling in the Tk sequence procedure Tk_continue(); tk_call(interp,"continue"); end Tk_continue; -- jump in event handling in the Tk sequence procedure dooneevent(); tk_dooneevent(interp); end dooneevent; -- wait for some (any) Tk event procedure do_later(proc); tk_idlecallback(proc); end do_later; -- execute a procedure after a short delay procedure obj_from_tkname(tkname); -- reconstruct a widget from its Tk name -- print("obj_from_tkname: ",tkname); tkn := rbreak(tkname,":"); tkname := tkn; -- drop the prefix if full 'str' form is given obj := tkw(); -- create an empty object if #tkname < 2 then -- return a copy of the root object obj.name := "."; obj.tk_type := "toplevel"; obj.parent := OM; return obj; end if; klass := tk_kall("winfo class " + tkname); klass := case_change1(klass); obj.tk_type := klass; -- set the object type nayme := rbreak(tkname,"."); rmatch(tkname,"."); -- break off the last name fragment obj.name := nayme; -- use the first name fragment obj.parent := obj_from_tkname(tkname); -- proceed recursively return obj; end obj_from_tkname; procedure tk_parent(); return parent; end tk_parent; -- the parent object of an object procedure win_of_pt(x,y); -- find the widget containing x,y return if (wc := tk_kall("winfo containing " + str(x) + " " + str(y))) = "" then OM else obj_from_tkname(wc) end if; end win_of_pt; procedure case_change1(stg); -- workaround for case_change bug caps := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; lc := "abcdefghijklmnopqrstuvwxyz"; s1 := stg(1); if exists c = caps(j) | c = s1 then stg(1) := lc(j); end if; return stg; end case_change1; procedure as_pair(stg); -- reduce menu item configuration data to a map match(stg,"-"); att_name := break(stg," "); match(stg," "); return [att_name,stg]; end as_pair; procedure self(x); -- configuration query, item configuration query, or creation call. -- configuration queries all have the form obj("attr;attr;..."). -- creation calls have the form parent_obj("kind", "main_param"); -- 'kind' can be one of the allowed widget kinds, or can be an allowed -- canvas item arc, bitmap, image, line, oval, polygon, text, image, -- or widget. We give every item a unique generated name -- of the form Wnnn (for widgets) or Cnnn (for canvas items). fn := full_name(); -- get full name of item if x = OM then -- miscellaneous fetches, depending on widget type case tk_type when "listbox" => -- for listboxes, get the currently selected items num_elts := unstr(tk_kall(fn + " index end")); selected := []; for m in [0..num_elts] loop txt := fn + " selection includes " + str(m); if tk_kall(txt) = "1" then selected with:= (m + 1); end if; end loop; return selected; when "menu" => -- for menus, get the configuration data for al the menu items num_elts := unstr(tk_kall(fn + " index end")); item_info := []; -- will collect info for each meu item for m in [0..num_elts] loop txt := fn + " entryconfigure " + str(m); item_info with:= {as_pair(x): x in stgs_from_Tk(tk_kall(txt))}; end loop; return item_info; -- returntupleof info items, one for each menu entry when "entry" => return tk_kall(fn + " get"); -- for entries, labels, and messages, get the text when "label","message" => return tk_kall(fn + " cget -text"); when "text" => stg := tk_kall(fn + " get 0.0 end"); return stg(1..#stg - 1); when "menubutton" => -- mb(OM) gets the menu of a menubutton menu_name := tk_kall(fn + " cget -menu"); obj := tkw(); -- create an empty object nayme := rbreak(menu_name,"."); rmatch(menu_name,"."); obj.tk_type := "menu"; obj.name := nayme; -- use the first name fragment obj.parent := obj_from_tkname(menu_name); -- find the menubutton name return obj; when "canvas" => -- for canvases, get the vector of canvas items items_no_list := breakup(tk_kall(fn + " find all")," "); items_list := []; -- will build for item_no in items_no_list loop sino := str(item_no); new_name := "c" + sino; -- canvas items are named cnn, nn = serial number new_item := tkw(); -- form a blank new canvas item new_item.parent := self; -- this canvas is the parent kind := tk_kall(fn + " type " + sino); if kind = "text" then kind := "canvas_text"; end if; if kind notin canvas_items then kind := "widget"; end if; new_item.tk_type := kind; -- note its type new_item.name := new_name; -- note its name items_list with:= new_item; end loop; return items_list; when "scale" => -- for sliders, get the slider value return unstr(tk_kall(fn + " get")); when "toplevel" => -- for toplevels, get the title return tk_kall("wm title " + fn); when "socket" => -- for sockets, read the socket may_error := tk_kall("set er [catch {gets " + name + " result}]"); -- pass gets command to tk if may_error /= "0" then -- there was an error socket_error := "error: " + may_error; tk_kall("close " + name); -- pass 'close peer' command to tk return OM; -- return OM as the item read elseif (eofres := tk_kall("set er [eof " + name + "]")) /= "0" then -- there was an end of file socket_error := ""; -- note the end-of_file else socket_error := 0; -- note not end-of_file end if; res := tk_kall("set er $result"); --print("now return result: ",socket_error,"**",res); -- return the result string just read return res; end case; if tk_type in canvas_items then -- get all the information for a canvas item, as a map --print("tk_type in canvas_items: ",tk_type," ",attributes_of(tk_type)); att_list := [x: x in attributes_of(tk_type)] with "coords"; the_attrs := read_attrs_canvas_item(att_list); return {[att,the_attrs(j)]: att = att_list(j)}; end if; end if; -- end of the 'fetch main data' cases if is_string(x) then -- configuration query -- here we require the string parameter x to be a semicolon-separated list of attribute -- names, appropriate to the object type being queried. The corresponding tuple of -- attribute values is returned. Canvas items must be treated specially, -- as must a few strings such as , which we treat as attributes but tk treats differently. attr_list := chop(x); -- break into list of attributes if tk_type = "entry" or tk_type = "canvas_text" or tk_type = "text" then -- the simple attributes "rect","width","height" are handled specially attr_val_tup := if x in {"rect","width","height"} then read_attrs_widget(attr_list) elseif x in gen_attributes then get_winfo_attr(x) elseif x in {"place","pack","grid"} then get_winfo_attr(x) else read_attrs_entry_or_canv_text(attr_list) end if; elseif tk_type = "toplevel" and attr_list(1) in wm_attributes then attr_val_tup := get_wm_attr(attr_list); elseif tk_type in widgets then -- this item is some other kind of widget attr_val_tup := read_attrs_widget(attr_list); elseif tk_type in {"image","bitmap"} and parent = OM then -- this is an absolute image attr_val_tup := [ ]; -- will collect and return the attribute values in a tuple for att in attr_list loop -- 'name' is the name of the image itself attr_val_tup with:= tk_kall(name + " cget " + " -" + att); end loop; elseif tk_type in {"image","bitmap"} and parent /= OM then -- this is a canvas or text image attr_val_tup := read_attrs_can_or_text_im(attr_list); elseif tk_type in canvas_items then -- we have a canvas item attr_val_tup := read_attrs_canvas_item(attr_list); elseif tk_type = "file" then -- get file attributes attr_val_tup := read_file_atts(name,attr_list); elseif tk_type = "socket" or tk_type = "server_socket" then -- get socket attributes; but this is never called attr_val_tup := read_socket_atts(name,attr_list); else -- if this object is a text item, we must use its parent and the itemcget -- procedure to query its configuration attr_val_tup := read_attrs_text_item(attr_list); end if; return if is_tuple(attr_val_tup) and #attr_val_tup <= 1 then attr_val_tup(1) else attr_val_tup end if; elseif tk_type = "text" then -- here x is a tuple and we have a tag attributes or ranges query [tag,att_names] := x; if tag = "tag" then -- we deal with a tag ranges query txt := full_name() + " tag ranges \"" + str(att_names) + "\""; -- in this case 'att_names' is really the tag limits := breakup(tk_kall(txt)," "); -- convert to a list of pairs --print("limits: ",limits,txt); return [[one_indexing(limits(j)),limits(j + 1)]: j in [1,3..#limits]]; end if; att_names := chop(att_names); if att_names = [] then -- return all the attributes of the tag att_names := attributes_of("text_tag"); return {[att,tk_kall(full_name() + " tag cget " + tag + " -" + att)]: att in att_names}; end if; att_vals := [tk_kall(full_name() + " tag cget " + tag + " -" + att): att in att_names]; --print("att_vals: ",att_vals); return if #att_names = 1 then att_vals(1) else att_vals end if; else -- here x is a tuple, so we have an item configuration query, creation call, or pixel fetch [kind,the_text] := x; -- here either the kind must be an integer designating an item, or x is a pair, -- consisting of a valid widget or canvas-item type and its principal parameter. -- we check the validity of the parent, create a new blank -- widget or canvas item, and fill in its fields name, tk_type, -- and parent if is_integer(kind) then -- check that this is a menu or a pixel fetch if tk_type = "image" and parent = OM then -- image pixel fetch txt := name + " get " + str(kind) + " " + str(the_text); return tk_kall(txt); -- return the pixel value elseif tk_type /= "menu" then abort("Numbered item references are only allowed for menus."); end if; options := chop(the_text); return tk_kall(full_name() + " entrycget " + str(kind) +/ [" -" + item: item in options]); end if; -- otherwise we deal with a ****** creation call ****** the_text := str(orig := the_text); -- force the parameter to string form if kind = "image" and tk_type /= "text" and tk_type /= "canvas" then return make_absolute_image(orig); -- build an absolute image either from a file name or from an image class absolute image elseif kind = "bitmap" and tk_type /= "text" and tk_type /= "canvas" then return make_absolute_bitmap(orig); -- build an absolute image either from a data string or a pair elseif kind in canvas_items or (kind = "text" and tk_type = "canvas") then -- creation of a canvas or text item; parent must be canvas or text return make_canvas_or_text(kind,the_text,orig); -- build a canvas or text item elseif kind in widgets or kind = "optionbutton" then -- any parent is OK; form the name return make_new_widget(kind,the_text); -- build a widget elseif kind = "file" then -- any parent is OK; make a file widget return make_file_widget(the_text); -- build a file widget elseif kind = "socket" or kind = "server_socket" then -- any parent is OK; make a socket widget return make_socket_widget(orig); -- build a socket widget; here we want the original form of -- the parameter pair [host_and_or_port,text_blocksize_or_accept_proc] elseif kind = "named" then -- make a widget from its Tk name Tk_name := rbreak(the_text,":"); -- break Tk name out of print form return obj_from_tkname(Tk_name); -- reconstruct the tkw-class widget from its full Tk string name else abort("The widget type " + kind + " is illegal."); end if; end if; end; procedure full_name(); -- finds full tk name string of widget return if parent = OM then name elseif (pfn := parent.full_name()) = "." then "." + name else pfn + "." + name end if; end full_name; procedure selfstr(); -- string conversion return tk_type + ":" + full_name(); -- type and name end selfstr; procedure one_indexing(ix_stg); -- convert to 1-indexing back := rbreak(ix_stg,"."); return ix_stg + str(unstr(back) + 1); end one_indexing; procedure read_attrs_entry_or_canv_text(attr_list); -- read attributes of an entry widget or canvas text item, with a few -- special attributes: anchor, end, insert, sel.first, sel.last if attr_list = [] then -- return all the attributes --print("read_attrs_entry_or_canv_text, null attr_list: ",tk_type); return ((as_map(tk_kall(full_name() + " configure")) + {[att,get_winfo_attr(att)]: att in ["children","showing","manager","rect","ismapped","wincoords","toplevel"]}) - (Tk_data_defaults(tk_type)?{})) with ["text",tk_kall(full_name() + if tk_type = "text" then " dump -all 1.0 end" else " get" end if)]; -- get the text as an attribute; full form if we have a tex widget end if; attr_val_tup := [ ]; -- will collect and return the attribute values in a tuple --print("read_attrs_entry_or_canv_text: ",attr_list,tk_type,special_atts); if tk_type = "canvas_text" then -- attributes of canvas text item fn := parent.full_name(); for att in attr_list loop -- loop, using the tk cget command to get the value if att = "type" then -- get the type attr_val_tup with:= tk_type; continue; elseif att notin special_atts_less_anchor then -- need not treat specially -- not in {"end", "insert", "sel.last", "sel.first", "coords"}: att_val := tk_kall(fn + " itemcget " + name(2..) + " -" + att); attr_val_tup with:= if att = "font" then "{" + att_val + "}" elseif att = "text" then "\"" + att_val + "\"" else att_val end if; elseif att /= "coords" then -- treat specially, using 'index' res := tk_kall(fn + " index " + name(2..) + " " + att); if res = "selection isn't in item" then res := 0; end if; attr_val_tup with:= res; else -- special treatment for "coords" fn := parent.full_name(); -- get the full tk name of the parent canvas attr_val_tup with:= breakup(tk_kall(fn + " coords " + name(2..))," "); end if; end loop; return [if x = 0 then OM else x end if: x in attr_val_tup]; -- done with this case end if; fn := full_name(); -- otherwise we deal with an entry or text widget -- get the full tk name of this widget or of canvas item parent if tk_type = "text" and attr_list = [] then return tk_kall(fn + " dump -all 1.0 end"); -- get all the information for a text widget end if; for att in attr_list loop -- loop, using the tk cget command to get the value if att = "type" then -- get the type attr_val_tup with:= tk_type; elseif att = "tags" and tk_type = "text" then -- we have the "tags" attribute of a text widget tag_list := breakup(tk_kall(fn + " tag names")," "); attr_val_tup with:= tag_list; elseif att = "marks" and tk_type = "text" then -- we have the "marks" attribute of a text widget mark_list := breakup(tk_kall(fn + " mark names")," "); attr_val_tup with:= mark_list; elseif att = "widgets" and tk_type = "text" then -- we have the widgets in a text widget lis := breakup(tk_kall(full_name() + " window names ")," "); return [obj_from_tkname(item): item in lis]; elseif att = "images" and tk_type = "text" then -- we have the images in a text widget return images(); elseif att notin special_atts then -- use ordinary cget call attr_val_tup with:= tk_kall(fn + " cget -" + att); else -- use 'index' call, in form appropriate to entry and text widgets attr_val_tup with:= tk_kall(fn + " index " + att); end if; end loop; return attr_val_tup; end read_attrs_entry_or_canv_text; procedure position_in_window(fn); -- gets the anchor position of the object from its Tk name --print("position_in_window: ",fn); -- relative to the top level window of the object; works for placed objects only windn := tk_kall("winfo toplevel " + fn); offx := offy := 0; while fn /= windn and fn /= "" loop if (geometry_val := tk_call(interp,"place info " + fn)) = "" then exit; end if; -- quit if there is an ancestor level which is not 'placed' --print("geometry_val: ",geometry_val," ",fn); -- relative to the top level window of the object; works for placed objects only geometry_tup := breakup(geometry_val," "); geo_map := {geometry_tup(j..j+1): j in [1,3..#geometry_tup]}; -- break into pairs offx +:= unstr(geo_map("-x")); offy +:= unstr(geo_map("-y")); rbreak(fn,"."); rmatch(fn,"."); end loop; return [offx,offy]; end position_in_window; procedure get_winfo_attr(att); -- get an attribute available through the Tk 'winfo' primitives fn := full_name(); --print("fn: ",att," ",fn); case att when "children" => chlist := breakup(tk_kall("winfo children " + fn)," "); return [obj_from_tkname(x): x in chlist]; when "showing" => return tk_kall("winfo viewable " + fn); when "manager" => return tk_kall("winfo manager " + fn); -- pc, grid, or place when "parent" => return obj_from_tkname(tk_kall("winfo paren " + fn)); when "rect" => -- this returns the element's enclosing rectangle -- the following code works for widgets, but needs to be checked for canvas items -- [x,y] := position_in_window(fn); height := unstr(tk_kall("winfo height " + fn)); width := unstr(tk_kall("winfo width " + fn)); return [x := unstr(tk_kall("winfo rootx " + fn)), y := unstr(tk_kall("winfo rooty " + fn)),x + width,y + height]; when "ismapped" => return unstr(tk_kall("winfo ismapped " + fn)); when "wincoords" => return [unstr(tk_kall("winfo rootx " + fn)), unstr(tk_kall("winfo rooty " + fn))]; when "toplevel" => return obj_from_tkname(tk_kall("winfo toplevel " + fn)); when "mouse" => -- the following are environent attributes return [unstr(tk_kall("winfo pointerx " + fn)), unstr(tk_kall("winfo pointery " + fn))]; when "screendepth" => return unstr(tk_kall("winfo depth " + fn)); when "screensize" => return [unstr(tk_kall("winfo screenwidth " + fn)), unstr(tk_kall("winfo screenheight " + fn))]; when "screenmm" => return [unstr(tk_kall("winfo screenmmwidth " + fn)), unstr(tk_kall("winfo screenmmheight " + fn))]; when "place","pack","grid" => geometry_val := tk_kall(att + " info " + self.full_name()); val_list := breakup(geometry_val," "); geo_map := {[val_list(j)(2..),vljp1]: j in [1,3..#val_list - 1] | (vljp1 := val_list(j + 1)) /= "{}"}; return geo_map; otherwise => return unstr(tk_kall("winfo " + att + " " + fn)); end case; end get_winfo_attr; --->working procedure get_wm_attr(attr_list); -- get window-manager attributes of toplevel attvals := []; -- will collect fn := full_name(); -- widget name orig_attr_list := attr_list; -- save for testing below if attr_list = [] then -- want all attributes attr_list := ["wingrid","iconposition","maxsize","minsize","resizable","sizefrom","winstate","geometry","aspect","title"]; end if; for att in attr_list loop att := if att = "wingrid" then "grid" elseif att = "winstate" then "state" elseif att = "rect" then "geometry" else att end if; res := tk_kall("wm " + att + " " + if att = "interps" then "" else fn end if); -- "wingrid","iconposition","maxsize","minsize","resizable","sizefrom","winstate","geometry" = "rect","aspect","title" case att -- return normal tuples where blank-delimited tuples would have been returned when "grid","maxsize","minsize","resizable" => res := [unstr(x): x in breakup(res," ")]; when "geometry" => [w,h,ul_x,ul_y] := [unstr(x): x in breakup(res,"x+")]; res := [ul_x,ul_y,ul_x + w,ul_y + h]; -- return as standard rectangle end case; attvals with:= res; end loop; if orig_attr_list = [] then return {[alj,attvals(j)]: alj = attr_list(j)}; end if; return if #attvals = 1 then attvals(1) else attvals end if; end get_wm_attr; procedure containing(x,y); -- window containing given point return tk_kall("winfo containing " + x + " " + y); end containing; procedure pixels(n); -- number of pixels corresponding to given size in screen units return tk_kall("winfo pixels " + full_name() + " " + n); end pixels; procedure fpixels(n); -- floating number of pixels corresponding to given size in screen units return tk_kall("winfo fpixels " + full_name() + " " + n); end fpixels; procedure rgb(color_name); -- numerical code for named color return tk_kall("winfo rgb " + full_name() + " " + color_name); end rgb; procedure set_wm_atts(attr_list,y); -- set window-manager attributes of toplevel fn := full_name(); -- widget name --print("set_wm_atts: ",fn,attr_list,y); if #attr_list = 1 and ((al1 := attr_list(1)) = "geometry" or al1 = "resizable" or al1 = "maxsize" or al1 = "minsize" or al1 = "aspect") and is_tuple(y) and #y > 1 then y := [y]; end if; -- force to singleton if singleton wanted hold_over := OM; -- might have holdovers for processing by reconfigure_from_map if attr_list = [] then -- want to set all attributes from a map --print("from a map: ",y); y("wincoords") := OM; y("rect") := OM; -- treat these as 'write-only' y("toplevel") := OM; y("children") := OM; -- treat these as 'write-only' y("bd") := OM; y("ismapped") := OM; y("manager") := OM; y("showing") := OM; -- treat these as 'write-only' y("Tk_tags") := OM; -- the tags attributes are handled elsewhere -- eliminate read-only attributes of toplevels (can be set only when toplevel being created) y("colormap") := OM; -- can't modify -colormap option after widget is created y("visual") := OM; -- can't modify -visual option after widget is created y("use") := OM; -- can't modify -use option after widget is created y("container") := OM; -- can't modify -container option after widget is created y("screen") := OM; -- can't modify -screen option after widget is created y("class") := OM; -- can't modify -class option after widget is created hold_over := {[att,y(att)]: att in for_toplevel_config}; -- holdovers for reconfigure_from_map attr_list := [att in wm_attributes_list | y(att) /= OM and att notin for_toplevel_config and att /= "iconposition" and att /= "aspect" and att /= "wingrid"]; -- disable two atributes which may be UNIX only y := [ya: att in wm_attributes_list | (ya := y(att)) /= OM and att notin for_toplevel_config and att /= "iconposition" and att /= "aspect" and att /= "wingrid"]; end if; for att = attr_list(j) loop att := if att = "wingrid" then "grid" elseif att = "winstate" then "state" elseif att = "rect" then "geometry" else att end if; -- "wingrid","iconposition","maxsize","minsize","resizable","sizefrom","winstate","geometry" = "rect","aspect","title" if att = "state" then -- may iconify, withdraw, or set to normal yj := y(j); if yj = "iconic" or yj = "normal" or yj = "withdrawn" then yj := if yj = "iconic" then "iconify" elseif yj = "normal" then "deiconify" else "withdraw" end if; kall_res := tk_kall(txt := "wm " + yj + " " + fn); --print("kall_res: ",kall_res," ",txt); end if; continue; -- done with this case end if; if att = "geometry" then -- parameter must be put in special w_x_h+x+y form bu_y := if is_string(y(j)) then breakup(y(j),";,") else y(j) end if; -- decompose the argument if it was transmitted as a delimited string [ul_x,ul_y,lr_x,lr_y] := if is_string(bu_y(1)) then [unstr(eltx): eltx in bu_y] else bu_y end if; -- force to numeric param_val := str(lr_x - ul_x) + "x" + str(lr_y - ul_y) + "+" + ul_x + "+" + ul_y; -- put in special form else -- put parameter in normal blank-delimited form --print("att,y(j): ",att," ",y(j)," ",j," y: ",y); yy := if is_string(y(j)) then if (yj := y(j)) = "" then ["{}"] else breakup(y(j),";,") end if else y(j) end if; -- put nullstring values into Tk form param_val := join(if is_string(yy(1)) then yy else [str(z): z in yy] end if," "); -- space out parameter components with blanks after forcing to string end if; res := tk_kall("wm " + att + " " + fn + " " + param_val); --print("set_wm_atts: ","wm " + att + " " + fn + " " + param_val," ",res); end loop; if hold_over /= OM then reconfigure_from_map(hold_over); end if; -- handle remaining ttributes in reconfigure_from_map end set_wm_atts; procedure toplev_bracket_posns(stg); -- get top-level bracket positions in original string tup := single_out(stg,"{}\\ "); -- single out significant characters charloc := 0; toplev := []; parenlev := 0; was_escape := false; -- will group into top-level bracketed items for x = tup(j) loop charloc +:= 1; -- location of current character if x = "{" then if was_escape then -- open paren is escaped; don't collect was_escape := false; continue; end if; if parenlev = 0 then toplev with:= charloc; end if; -- collect parenthesis position at top level parenlev +:= 1; -- advance since now within elseif x = "}" then if was_escape then -- open paren is escaped; don't collect was_escape := false; continue; end if; parenlev -:= 1; -- decrement since now out if parenlev = 0 then toplev with:= charloc; end if; -- collect parenthesis position at top level elseif x = "\\" then -- reverse escape state was_escape := not was_escape; continue; else charloc +:= (#x - 1); -- one character was handled above end if; was_escape := false; -- applied if value not explicitly set aove end loop; return toplev; -- return the list of toplevel unescaped bracket positions end toplev_bracket_posns; -- procedure my_single_out(stg,pat); -- single out characters in pat -- if (pat_pl := [j: c = stg(j) | c in pat]) = [] then return stg; end if; -- pieces := if (pp1 := pat_pl(1)) = 1 then [] else [stg(1..pp1 - 1)] end if; -- will collect -- for pp = pat_pl(j) loop -- iterate over all the significant charater locations -- pieces with:= stg(pp); -- take the significant character -- if (next := (pat_pl(j + 1)?(#stg + 1)) - 1) > pp then -- take the next or final piece if not empty -- pieces with:= stg(pp + 1..next); -- end if; -- end loop; -- return pieces; -- end my_single_out; procedure stgs_from_Tk(stg); -- get list of strings from Tk blank-delimited form; -- break string at top-level blank positions and de-escape toplev := toplev_bracket_posns(stg); -- get top_level bracket positions if toplev = [] then return break_at_blanks(stg); end if; -- no toplevel bracket positions pieces := break_at_blanks(stg(1..toplev(1) - 1)); -- break first section for k in [1..(ntl := #toplev) - 1] loop stg_section := stg(toplev(k) + 1..toplev(k + 1) - 1); -- dont break bracketed sections pieces +:= if stg(toplev(k)) = "{" then [stg_section] else break_at_blanks(stg_section) end if; end loop; pieces +:= break_at_blanks(stg(toplev(ntl) + 1..)); -- break final section return pieces; end stgs_from_Tk; procedure break_at_blanks(stg); -- break string at unescaped blanks, and de-escape if stg = "" then return []; end if; -- empty string become null just_escaped := false; -- initialize cleaned_stg := ""; -- will build tup := []; -- will collect for c = stg(j) loop -- characters of the string if just_escaped then just_escaped := false; cleaned_stg +:= if c = "n" then "\n" elseif c = "r" then "\r" elseif c = "t" then "\t" elseif c = "t" then "\\x" else c end if; -- take the character, which may be a backslash; normally escaped characters are speical cased elseif c = "\\" then -- this is an 'escape' just_escaped := true; elseif c = " " then -- a section ends if cleaned_stg /= "" then tup with:= cleaned_stg; end if; cleaned_stg := ""; -- collect and restart else -- take this normal chaacter cleaned_stg +:= c; end if; end loop; if cleaned_stg /= "" then tup with:= cleaned_stg; end if; -- collect final section return tup; -- return the list of pieces end break_at_blanks; procedure remove_escapes(stg); -- remove Tk escapes from string if stg = "" then return ""; end if; just_escaped := false; -- initialize cleaned_stg := ""; -- will build tup := []; -- will collect for c = stg(j) loop -- characters of the string if just_escaped then just_escaped := false; cleaned_stg +:= if c = "n" then "\n" elseif c = "r" then "\r" elseif c = "t" then "\t" elseif c = "t" then "\\x" else c end if; -- take the character, which may be a backslash; tabs, eols, and hex signs get special treatment elseif c = "\\" then -- this is an 'escape' just_escaped := true; else -- take this normal chaacter cleaned_stg +:= c; end if; end loop; return cleaned_stg; -- return the cleaned_stg end remove_escapes; procedure as_map(stg); -- converts a Tk configuration descriptor string to a mapping from attrbute names to values --print("as_map: ",stg); toplev := toplev_bracket_posns(stg); -- get top_level bracket positions the_map := {}; parenlev := 0; -- initialize for k in [1,3..#toplev - 1] loop slash_parity := 0; -- restart substg := stg(toplev(k) + 1..toplev(k + 1) - 1); -- get one configuation descriptor segment -- we are looking for the pieces delimited by the first and last top level blanks attrib := break(substg," "); match(substg," "); if (attrib := attrib(2..)) = "bg" or attrib = "fg" then continue; end if; -- these ae just synonyms last_sig_blank := 0; -- intiatialize for x = substg(kk) loop -- look for last significant blank if x = "{" then if kk > 1 and substg(kk - 1) = "\\" and slash_parity = 1 then -- open bracket is escaped continue; end if; parenlev +:= 1; elseif x = "}" then if kk > 1 and substg(kk - 1) = "\\" and slash_parity = 1 then -- close bracket is escaped continue; end if; parenlev -:= 1; elseif x = " " and parenlev = 0 then -- we have a significant blank last_sig_blank := kk; elseif x = "\\" then slash_parity := 1 - slash_parity; end if; end loop; mv := substg(last_sig_blank + 1..); if (nmv := #mv) > 0 and mv(1) = "{" and mv(nmv) = "}" then mv := mv(2..nmv - 1); end if; the_map(attrib) := mv; end loop; return the_map; end as_map; procedure read_attrs_widget(attr_list); -- read widget attributes attr_val_tup := [ ]; -- will collect and return the attribute values in a tuple fn := full_name(); -- get the full tk name of this widget if attr_list = [] then res := as_map(tk_kall(fn + " configure")) + {[att,get_winfo_attr(att)]: att in ["children","showing","manager","rect","ismapped","wincoords","toplevel"]}; --print("read_attrs_widget, testing listbox: ",); return if tk_type = "listbox" then res with ["text",self(1..#self)] elseif tk_type = "toplevel" then res + get_wm_attr([]) else res end if; end if; -- empty list returns collection of all attributes for att in attr_list loop -- loop, using the tk cget command to get the value if att in gen_attributes then --print("gen_attributes read_attrs_widget: ",attr_list); if att in {"height","width"} and tk_type /= "toplevel" then attr_val_tup with:= unstr(tk_kall(fn + " cget -" + att)); continue; elseif att = "toplevel" then tlevel := tk_kall(" winfo toplevel " + fn); --print("tlevel:",tlevel); attr_val_tup with:= obj_from_tkname(tlevel); continue; end if; attr_val_tup with:= get_winfo_attr(att); continue; -- but width and heightof topleves is read using 'winfo' end if; if att = "selected" and (tk_type = "radiobutton" or tk_type = "checkbutton") then -- pseudo-attribute: read the associated variable varname := tk_kall(fn + " cget -variable "); -- get name of associated variable varval := tk_kall("set " + varname); -- get value of associated variable if tk_type = "radiobutton" then -- get the value that the variable would have if this button was on corresp_val := tk_kall(fn + " cget -value "); attr_val_tup with:= if varval = corresp_val then "1" else "0" end if; elseif tk_type = "checkbutton" then attr_val_tup with:= varval; end if; continue; -- done with this case elseif att in {"place","pack","grid"} then -- get the geometry manager info -- note that this returns the info relative to the object's parent geometry_val := tk_kall(att + " info " + self.full_name()); val_list := breakup(geometry_val," "); --print("geometry_val: ",val_list); geo_map := {[val_list(j)(2..),vljp1]: j in [1,3..#val_list - 1] | (vljp1 := val_list(j + 1)) /= "{}"}; attr_val_tup with:= geo_map; -- return the geometry information as a map continue; -- done with this case elseif att = "font" then -- attach "{" and "}" attr_val_tup with:= "{" + tk_kall(fn + " cget -" + att) + "}"; continue; -- done with this case elseif att in pseudo_atts then -- various pseudo-attribute retrieval operations case att when "active" => -- get a scrollbar's active element attr_val_tup with:= tk_kall(full_name() + " activate"); continue; when "position" => -- get a scrollbars currentpositon int_lis := breakup(tk_kall(full_name() + " get")," "); attr_val_tup with:= [unstr(x): x in int_lis]; continue; when "type" => -- get the object's type attr_val_tup with:= tk_type; continue; when "image" => -- get the contents of a canvas as an image_analysis library image --print("widget configuration query: "); return "testing"; if tk_type = "canvas" then image_no := unstr(tk_kall("save " + full_name())); -- this is the opaque integer pointer to the native grlib image object -- we create an image-analysis class object from it attr_val_tup with:= image(image_no); -- add to attribute tuple, as ifthis were an attribute else -- use ordinary configuration query attr_val_tup with:= tk_kall(fn + " cget -" + att); end if; continue; when "clipboard" => -- get the contents of the clipboard attr_val_tup with:= tk_kall("selection get -selection CLIPBOARD"); continue; when "definedFonts" => -- get the list of available fonts items := tk_kall("font names"); return breakup(items," "); continue; when "fonts" => -- get the list of available fonts items := tk_kall("font families"); items_list := []; -- will collect while items /= "" loop items_piece := break(items,"{"); rspan(items_piece," "); items_list +:= breakup(items_piece," "); item_to_close := break(items,"}"); item_to_close +:= match(items,"}"); items_list with:= item_to_close; span(items," "); end loop; items_list := [item: item = items_list(j) | item /= ""]; attr_val_tup with:= join(items_list,","); -- return the comma-separated font list continue; when "placed" => -- get the items placed into this widget items := tk_kall(" place slaves " + full_name()); when "packed" => -- get the items packed into this widget items := tk_kall(" pack slaves " + full_name()); when "gridded" => -- get the the items gridded into this widget items := tk_kall(" grid slaves " + full_name()); when "propagate" => -- get a window or frame's propagation status attr_val_tup with:= tk_kall("pack propagate " + full_name()); continue; end case; the_list := [obj_from_tkname(x): x in breakup(items," ")]; attr_val_tup with:= the_list; -- return the geometry information as a comma-separated list continue; end if; attr_val_tup with:= tk_kall(fn + " cget -" + att); end loop; return attr_val_tup; end read_attrs_widget; procedure read_attrs_can_or_text_im(attr_list); -- read the attributes of a canvas or text image attr_val_tup := [ ]; -- will collect and return the attribute values in a tuple fn := parent.full_name(); -- get the full tk name of the parent canvas or text item for att in attr_list loop if att in {"image","bitmap"} then -- convert name to an image or bitmap im_name := tk_kall(fn + " itemcget " + name(2..) + " -" + att); the_im := tkw(); -- create a new blank object the_im.name := im_name; the_im.tk_type := att; attr_val_tup with:= the_im; elseif att = "coords" then -- use the "coords" operation attr_val_tup with:= breakup(tk_kall(fn + " coords " + name(2..))," "); else -- use att-val itself attr_val_tup with:= tk_kall(fn + " itemcget " + name(2..) + " -" + att); end if; end loop; return attr_val_tup; end read_attrs_can_or_text_im; procedure read_attrs_canvas_item(attr_list); -- read the attributes of a canvas item -- The attributes of canvas items are: tags, width, and coords in all cases, plus -- for canvas geometric objects: fill, outline, and stipple -- for images: anchor and image -- for canvas text objects: anchor, fill, font, justify, stipple, and text -- for canvas widgets: anchor, height, and window -- Tk uses itemcget/itemconfigure calls to get/set all but the coords attributes, -- but a coords calls to get/set the coords attribute. --print("read_attrs_canvas_item: ",tk_type); attr_val_tup := [ ]; -- will collect and return the attribute values in a tuple fn := parent.full_name(); -- get the full tk name of the parent canvas for att in attr_list loop -- loop, using the tk cget command to get the value -- unless the attribute is 'coords' or "tags", in which case we use -- the parent 'coords' or 'gettags' command to get it case att when "coords" => attr_val_tup with:= breakup(tk_kall(fn + " coords " + name(2..))," "); when "tags" => attr_val_tup with:= breakup(tk_kall(fn + " gettags " + name(2..))," "); when "type" => attr_val_tup with:= tk_type; when "window" => res := tk_kall(fn + " itemcget " + name(2..) + " -window"); attr_val_tup with:= obj_from_tkname(res); otherwise => attr_val_tup with:= tk_kall(fn + " itemcget " + name(2..) + " -" + att); end case; end loop; return attr_val_tup; end read_attrs_canvas_item; procedure read_attrs_text_item(attr_list); -- read the attributes of a text item attr_val_tup := [ ]; -- will collect and return the attribute values in a tuple fn := parent.full_name(); -- get the full tk name of the parent widget for att in attr_list loop -- loop, using the tk cget command to get the value -- unless the attribute is 'bbox', in which case we use -- the parent bbox command to get it, or 'coords', if att = "bbox" then -- pseudo-attribute: use the "bbox" command attr_val_tup with:= breakup(tk_kall(fn + " bbox " + name(2..))," "); else attr_val_tup with:= tk_kall(fn + " itemcget " + name(2..) + " -" + att); end if; end loop; return attr_val_tup; end read_attrs_text_item; procedure read_file_atts(fname,att_list); -- get specified attributes of file atts := breakup(raw_atts := tk_kall("file attributes " + fname) + " " + tk_kall("file stat " + fname + " fs\nset x \"$fs(mtime) $fs(atime) $fs(gid) $fs(nlink) " + "$fs(mode) $fs(type) $fs(ctime) $fs(uid) $fs(ino) $fs(size) $fs(dev)\"" ) + tk_kall("file writable " + fname)," "); --print("raw_atts: ",raw_atts); link := tk_kall("file readlink " + fname); m := match(link,"couldn't readlink \""); if m/= "" then link := ""; end if; atts with:= link; -- atts are now in the order * creator,2 * hiddden,4 * readonly,6 * mac_type,8 -- mtime,9 atime,10 gid,11 nlink,12 mode,13 type,14 ctime,15 uid,16 ino,17 size,18 dev,19 pointer,20 return [atts(posns_map(att)?("UNDEFINED:" + att)): att in att_list]; -- return list of attributes end read_file_atts; procedure read_socket_atts(fname,att_list); -- get specified attributes of socket end read_socket_atts; procedure make_absolute_image(the_text); -- build an absolute image -- build an absolute image either from a file name or from an image class absolute image -- the parameter is taken to be either the file from which the image should be read, -- or a string of the special form designating the 'Grlib' core of -- an image-class image, which must be a 3-plane, discrete image if not is_string(the_text) then -- assume that it is a image-class image -- #the_text > 8 and the_text(1..8) = " string txt := "set im [image create mimage -opaque \"" + str(the_text) + "\"]"; -- we will call Tk to make a Tk absolute image --print("make_absolute_image: ",txt); else -- assume that we have an image file txt := "image create photo " + the_text + "`" + str(name_ctr +:= 1) + " -file " + the_text; end if; img_name := tk_kall(txt); -- call Tk to create the Tk asolute image image from the given grilb image or file new_image := tkw(); -- form a blank new object new_image.parent := OM; -- images have no parent new_image.tk_type := "image"; -- note its type new_image.name := img_name; -- note its name, which we make unique --print("new_image.name: ",new_image.name," ",new_image.full_name()); return new_image; end make_absolute_image; procedure reconstruct_image_from_name(img_name); -- rebuild an existing absolute image using its name new_image := tkw(); -- form a blank new object new_image.parent := OM; -- images have no parent new_image.tk_type := "image"; -- note its type new_image.name := img_name; -- note its name return new_image; end reconstruct_image_from_name; procedure reconstruct_bitmap_from_name(bm_name); -- rebuild an existing absolute bitmap using its name new_image := tkw(); -- form a blank new object new_image.parent := OM; -- images have no parent new_image.tk_type := "bitmap"; -- note its type new_image.name := bm_name; -- note its name return new_image; end reconstruct_bitmap_from_name; procedure make_absolute_bitmap(data); -- build an absolute image either from a data string or a pair img_name := "XBM`" + str(name_ctr +:= 1); -- generate new name if is_tuple(data) then -- icon and mask data are given [icon_data,mask_data] := data; -- unpack txt := "image create bitmap " + img_name + " -data \"" + icon_data + "\" -maskdata \"" + mask_data + "\""; --print("make_absolute_image: ",txt); else -- no mask data is given txt := "image create bitmap " + img_name +" -data \"" + data + "\""; end if; img_name := tk_kall(txt); -- call Tk to create the image new_image := tkw(); -- form a blank new object new_image.parent := OM; -- images have no parent new_image.tk_type := "bitmap"; -- note its type new_image.name := img_name; -- note its name, which we make unique --print("new_image.name: ",new_image.name," ",new_image.full_name()); return new_image; end make_absolute_bitmap; procedure make_canvas_or_text(kind,the_text,orig); -- build a canvas or text item if tk_type = "text" then -- we are creating a text item if kind /= "image" and kind /= "bitmap" and kind /= "widget" then abort("An item of type " + kind + " cannot have a text widget as parent."); end if; -- otherwise we must create a text image or text widget, both from an object -- of appropriate kind null; -- ********* FILL IN ********* end if; if tk_type /= "canvas" and tk_type /= "widget" then abort("An item of type " + kind + " must have a canvas or subcanvas, not a " + tk_type + " as parent."); end if; -- create the canvas item; if the kind is 'text', the object becomes -- canvas_text internally. If it is 'image' or 'widget', the second -- parameter is an actual object of type tkw, whose name must be extracted -- for the Tk creation call pref := ""; -- no prefix is used except for 'text' if kind = "text" then -- this becomes "canvas_text" internally, and text is not split pref := "canvas_"; elseif kind in {"image","bitmap"} then -- second parameter must be an image or bitmap object; get its name txt := full_name() + " create image 0 0 -image " + orig.name; -- Note that the image name has no prefixed letter ci_num := tk_kall(txt); -- this returns the serial number of the image item --print("bitmap: ",txt); new_item := tkw(); -- form the blank new canvas item new_item.parent := self; -- this canvas is the parent new_item.tk_type := kind; -- note its type new_item.name := "c" + ci_num; -- note its serial number (prefixing a 'c') new_name := new_item.full_name(); -- get the full name of the new item source_of(new_name) := orig; -- map this into the source item --print("source_of: ",source_of); return new_item; elseif kind = "widget" then -- second parameter must be a widget object; get its name txt := full_name() + " create window 0 0 -window " + orig.full_name(); ci_num := tk_kall(txt); new_item := tkw(); -- form the blank new canvas item new_item.parent := self; -- this canvas is the parent new_item.tk_type := "widget"; -- note its type new_item.name := "c" + ci_num; -- note its serial number (prefixing a 'c') new_name := new_item.full_name(); -- get the full name of the new item source_of(new_name) := orig; -- map this into the source item return new_item; end if; -- here we are creating a canvas item if kind /= "text" then -- break up the parameter list orig_breakup := the_breakup := chop(the_text); -- we allow non-numeric 'coordinates' as an identifying tags of the newly created canvas item tag_locs := {j: x = the_breakup(j) | (exists c in x | (c notin "-0123456789."))}; -- can be floating tags := [x: x = the_breakup(j) | j in tag_locs]; the_breakup := [x: x = the_breakup(j) | j notin tag_locs]; txt := (fn := full_name()) + " create " + kind + " " +/ [num + " ": num in the_breakup] + " -tag ...."; -- this special tag identifies the currently created object ci_num := tk_kall(txt); -- create the item, and get the item number which Tk assigns if kind = "oval" then tk_kall(fn + " addtag OVAL withtag ...."); end if; -- add the OVAL tag if needed -- ovals are assigned the tag "OVAL", so they can be recognized later tags := [tags(j): j in [ntgs := #tags,ntgs - 1..1]]; -- reverse order so that tags come out in order of creation parameter for tag in tags | tag /= "OVAL" loop -- attach other tags if any res := tk_kall(txt := fn + " addtag " + tag +" withtag ...."); --print("attach other tags: ",kind," ",res," ",txt); end loop; tk_kall(fn + " dtag ...."); -- remove the special 'current' mark --print("verify tags after remove: ",tk_kall(fn + " gettags " + ci_num)); else -- dont break up the parameter list txt := (fn := full_name()) + " create text 0 0 -text " + "\"" + stg_to_Tk(the_text) + "\"" + " -tag ...."; ci_num := tk_kall(txt); -- create the item, and get the item number which Tk assigns tags := []; -- FIX FIX ********** tags := [tags(j): j in [ntgs := #tags,ntgs - 1..1]] with "all"; -- here we add the 'all' tag since by default canvas text itmes have no tags -- reverse order so that tags come out in order of creation parameter for tag in tags loop -- attach other tags if any res := tk_kall(txt := fn + " addtag " + tag + " withtag ...."); end loop; tk_kall(fn + " dtag ...."); -- remove the special 'current' mark end if; --print("\ntxttxt: ",txt," ",tags," ",the_breakup," ",tag_locs," ",orig_breakup); new_name := "c" + ci_num; -- canvas items are named cnnn, where nnn is their serial number new_item := tkw(); -- form the blank new canvas item new_item.parent := self; -- this canvas is the parent new_item.tk_type := pref + kind; -- note its type new_item.name := new_name; -- note its name return new_item; end make_canvas_or_text; procedure make_new_widget(kind,the_text); -- build a widget new_name := "w" + str(name_ctr +:= 1); new_widget := tkw(); -- form the blank new widget new_widget.parent := self; -- this widget is the parent new_widget.tk_type := kind; -- note its type new_widget.name := new_name; -- note its name -- now create it, as a tk object tk_name := new_widget.full_name(); if kind = "menu" then return make_menu(new_widget,tk_name,the_text); end if; if kind = "optionbutton" then return make_optionbutton(new_widget,tk_name,the_text); end if; if (main_option := main_options(kind)) = "hw" then -- parameter must be width,height [width,height] := chop(the_text); suffix := " -width " + width + " -height " + height; elseif main_option = "ft" then -- parameter must be from,to [fromm,too] := chop(the_text); suffix := " -from " + fromm + " -to " + too; elseif main_option = "orient_w" then -- parameter must be 'h' or 'v','width' [hv,width] := chop(the_text); suffix := " -orient " + if hv = "v" then "vertical" else "horizontal" end if + " -width " + width; else if main_option = "text" and (the_text?"") /= "" then the_text := stg_to_Tk(the_text); end if; -- put the string used in creation into Tk form tt := if the_text = "OM" then "" else " \"" + the_text end if + "\""; suffix := if the_text = "OM" then "" else " -" + main_option + tt end if; end if; --print("creation call: ",kind + " " + tk_name + suffix); tk_kall(kind + " " + tk_name + suffix); --print("created new_widget: ",tk_name); return new_widget; -- return it end make_new_widget; procedure make_file_widget(the_text); -- build a file widget new_widget := tkw(); -- form the blank new widget new_widget.parent := self; -- this widget is the parent new_widget.tk_type := "file"; -- note its type new_widget.name := the_text; -- note the file name return new_widget; -- return it end make_file_widget; procedure make_socket_widget(param_pair); -- build a socket widget new_widget := tkw(); -- form the blank new widget new_widget.parent := self; -- this widget is the parent [host_and_or_port,text_blocksize_or_accept_proc] := param_pair; -- unpack the parameter pair if is_integer(host_and_or_port) then -- host_and_or_port is integer port number, so socket being created is server kind := "server_socket"; if not is_procedure(text_blocksize_or_accept_proc) then print("****** bad server socket accept handler: ",text_blocksize_or_accept_proc); stop; end if; tk_name_for_proc := "s" + str(namegen_ctr := (namegen_ctr?0) + 1); -- generate a new tk variable name tk_createcommand(interp,tk_name_for_proc,text_blocksize_or_accept_proc); -- register the setl accept-handler procedure under this name -- see comment below on semantics and typical form of accept handlers txt := "set " + "socket_var " + " [socket -server " + tk_name_for_proc + " " + str(host_and_or_port) + "]"; -- when called upon receipt of an external connection request by an external process, an accept handler will get a tuple -- [tk_socket_id,external_net_address,external_initiating_port] as its 1 parameter. it should immediately convert the tk_socket_id -- to a client socket object, and set i/o event routines for this new socket; these i/o event routines can then read the socket -- when input arrives, or write it. -- compose the tk socket-creation command tk_socket_name := tk_kall(txt); -- pass this command to tk, which responds with a generated socket name --print("created server: ",tk_socket_name); else -- host_and_or_port is a string 'host_addr:port', so a client socket is to becreated kind := "socket"; host := break(host_and_or_port,":"); m := match(host_and_or_port,":"); --break port from 'host_addr:port' if host_and_or_port = "" and m = ":" then -- null port, but ":" is present, so this is a client socket generated by an accepted request tk_socket_name := host; -- set tk peer name variable to known name of already generated socket elseif host_and_or_port = "" then -- missing host name,so error case print("****** bad client socket port: ",host,m,host_and_or_port); stop; else -- we have an acceptable host name, so we create a client socket txt := "set " + "socket_var" + " [socket " + host + " " + host_and_or_port + "]"; -- compose the tk socket-creation command tk_socket_name := tk_kall(txt); -- pass this command to tk, which respondswith a generated socket name end if; if text_blocksize_or_accept_proc = "text" then -- configure as a line-oriented client socket, to the indicated host_and_port txt := "fconfigure $" + "socket_var" + " -buffering line"; -- tk buffer_by_lines command tk_kall(txt); -- pass this command to tk elseif not is_integer(text_blocksize_or_accept_proc) or text_blocksize_or_accept_proc <= 0 then -- should have integer buffer size; should be configured as 'block' print("****** bad client socket block size: ",text_blocksize_or_accept_proc); stop; else -- configure as 'block' buffer_size := text_blocksize_or_accept_proc; -- this flags block-buffering client sockets txt := "fconfigure $" + "socket_var" + " - buffering full -buffersize " + buffer_size; -- tk buffer_by_blocks command tk_kall(txt); -- pass this command to tk end if; end if; new_widget.tk_type := kind; -- note the socket type new_widget.name := tk_socket_name; -- note the socket name return new_widget; -- return it end make_socket_widget; procedure make_menu(menu_obj,menu_name,descriptor); -- create the items of a new menu from its descriptor tk_kall("menu " + menu_name + " -tearoff 0"); -- create the menu define_menu_items(menu_name,descriptor); -- define the items of the menu return menu_obj; end make_menu; procedure make_optionbutton(button_obj,menu_name,descriptor); varname := break(descriptor,";"); span(descriptor,";"); alternatives := "" +/ [" " + x: x in breakup(descriptor,",")]; button_obj.tk_type := "menubutton"; --print("make_optionbutton: ",menu_name); tk_kall("tk_optionMenu " + menu_name + " " + varname + alternatives); -- create the optionbutton and its menu return button_obj; end make_optionbutton; procedure define_menu_items(menu_name,descriptor); -- define the items of a menu from a descriptor items := breakup(chop(descriptor),":"); for [kind,lab] in items loop if lab = OM then -- should be separator or tearoff if kind = "t" then -- tearoff tk_kall(menu_name + " add tearoff"); else -- take as separator tk_kall(menu_name + " add separator"); end if; else case kind when "c" => -- checkbutton item tk_kall(menu_name + " add checkbutton -label " + lab + " -indicatoron 1"); when "r" => -- radiobutton item tk_kall(menu_name + " add radiobutton -label " + lab + " -variable " + lab); when "s" => -- submenu item tk_kall(menu_name + " add cascade -label " + lab); otherwise => -- take as button item tk_kall(menu_name + " add command -label " + lab); end case; end if; end loop; end define_menu_items; procedure self(x) := y; -- assignment of attributes (configuration) -- 'self' can reference either a widget, canvas item, image, or bitmap. -- We require x (resp. y) to be comma or semicolon-separated lists of attribute names (resp. -- values), appropriate to the object type being queried. The values are assigned -- to the corresponding attributes -- y is allowed to be a ;-separated string, a tuple, a procedure, or a widget object. If y is a -- string, we cut it into a tuple and proceed as in the tuple case. empty option names -- can be used with option value strings which identify their options uniquely, -- e.g. for options like -- The 'geometry manager' commands pack, grid, and place can also appear in x, -- as initial 'pseudo-attributes'. If they appear, all the other attributes -- which follow them must be legal geometry manager options, and the componts of y -- must be legal values for these options. -- the canvas-item types are arc, bitmap, image, bitmap, line, oval, polygon, rectangle, text, -- canvas_widget if is_integer(y) or is_real(y) then y := str(y); end if; -- convert right-hand sides which are not procedures, objects, or tuples into strings if x = OM then -- miscellaneous sets of 'whole content', depending on widget type return set_whole_contents(y); -- set content of an entry, text, message, label, scale, or toplevel widget; -- set the menu of a menubutton; write to a client socket end if; if x = "" then -- completely reconfigure widget; y must be a mapping if tk_type = "toplevel" then return set_wm_atts([],y); end if; -- handle in set_wm_atts return reconfigure_from_map(y); -- handle in reconfigure_from_map end if; if y = OM then -- miscellaneous nulling ops of attribute return null_whole_attribute(x); -- null the value of this attribute end if; if is_tuple(x) then -- configuration of a menu item, dialog in toplevel, canvas or text widget tag, -- tag in text, or geometry manager configuration to widget other than parent [itm,the_text] := x; -- this must have the form object; int, item_attributes, -- or "tag",tag_name -- if 'the_text' is not a string, it should be a widget object, passed as part of an -- extended geometry manager call if itm = "font" then -- this is a font definition nna := #(name_and_atts := chop(the_text)); font_name := name_and_atts(1); if font_name in breakup(tk_kall("font names")," ") then tk_kall("font delete " + font_name); -- delete the font in case it exists end if; y := chop(y); att_and_vals := "" +/ [" -" + name_and_atts(j + 1) + " " + yj: yj = y(j) | j < nna]; txt := "font create " + font_name + att_and_vals; return tk_kall(txt); end if; if not is_string(the_text) then -- we have an extended geometry manager call; 'the_text' -- is the widget in which another widget is being packed, -- placed, or gridded. -- The underlying call is like self("side",frame) := "left" xx := chop(itm); if xx(1) notin geometry_keywords then abort("illegal second parameter b in widget(a,b) := c: " + str(the_text)); end if; return configure_geometry_in(xx,chop(y),the_text); -- handle extended geometry-manager calls end if; options := if (the_text?"") = "" then [] else chop(the_text) end if; case tk_type -- proceed in a manner dependent on the type of this widget when "toplevel","" => -- pseudo-configuration of dialog in toplevel or absolute master window; or wm call return configure_toplevel(itm,options,y); -- handle all the standard built-in dialogs when "text","canvas" => -- configuration of canvas or text tag, binding of procedure to click on tagged range, -- or tag configuration in text if itm = "tag" and tk_type = "text" then --tag configuration in text -- y must be a tuple of pairs tk_kall(full_name() + " tag remove " + the_text + " 1.0 end"); -- remove present ranges the_text := the_text + " " +/ [fi + " " + la + " ": [fi,la] in y]; return tk_kall(full_name() + " tag add " + the_text); -- restore new ranges end if; -- here we are configuring a canvas tag or text tag -- break y into its parts if it is a string y := chop(y); if not is_tuple(y) then y := [y]; end if; txt := full_name() + " tag configure " + str(itm) -- in this case 'itm' is the tag name +/ [" -" + item + " " + may_quote(tk_string_of(y(j)?"nothing")): item = options(j)]; --print("configuring a tag: ",txt); return tk_kall(txt); when "menu" => -- configuration of menu item if not is_integer(itm) then -- if itm = "system" and the_text = "menu" then -- definition of "Apple" menu in menu bar -- return tk_kall(full_name() + ".apple configure " + " -menu " + y.full_name()); -- elseif itm = "help" and the_text = "menu" then -- definition of "Help" menu in menu bar -- return tk_kall(full_name() + ".help configure " + " -menu " + y.full_name()); -- end if; abort("Only numbered item references are allowed for menus."); end if; if the_text = "menu" then -- should be assignment of submenu to cascade item return tk_kall(full_name() + " entryconfigure " + str(itm) + " -menu " + y.full_name()); end if; -- break y into its parts if it is a string y := chop(y); if not is_tuple(y) then y := [y]; end if; txt := full_name() + " entryconfigure " + str(itm) +/ [" -" + item + " \"" + tk_string_of(y(j)?"nothing") + "\"": item = options(j)]; return tk_kall(txt); end case; end if; -- *********** end if is_tuple(x) *********** y := chop(y); if x = "tags" and tk_type = "text" then -- configuration of the tag order of a text widget -- is now the specified tag list. We first drop all elements in it which are not actually tags tag_set := {z: z in breakup(tk_kall(full_name() + " tag names")," ")} less "sel"; y := [z: z in y | z in tag_set]; tag_set -:= {z: z in y}; -- tag_set is now the collection of tags to be dropped -- All other elements are dropped from the tag set if #tag_set > 0 then tk_kall(txt := full_name() + " tag delete" +/ [" " + tag: tag in tag_set]); end if; if #y = 0 then return; end if; -- now we raise all these elements to the end, giving them top priority. tk_kall(txt := full_name() + " tag raise " + y(1)); -- first raise the initial element for j in [2..#y] loop -- then raise each to the position after the prior tk_kall(full_name() + " tag raise " + y(j) + " " + y(j - 1)); end loop; return; elseif x in special_lefts then -- xscroller, yscroller, clipboard, grab, sel, sel.anchor, xview, yview, xpercent, ypercent, active --print("special_lefts ",x); case x when "xview" => -- set the xview in a scrollable widget txt := full_name() + " xview "+ str(unstr(y(1)?"0") - 1); --print("xview: ",txt); return tk_kall(txt); when "yview" => -- set the yview in a scrollable widget txt := full_name() + " yview "+ str(unstr(y(1)?"0") - 1); return tk_kall(txt); when "xpercent" => -- set the xview percentage in a scrollable widget txt := full_name() + " xview moveto " + (y(1)?"0"); return tk_kall(txt); when "ypercent" => -- set the yview percentage in a scrollable widget txt := full_name() + " yview moveto " + (y(1)?"0"); return tk_kall(txt); when "position" => -- set the limits of a scrollbar txt := full_name() + " set " + str(y(1)?"0") + " " + str(y(2)?"0"); --print("limits ",txt); return tk_kall(txt); when "sel" => -- set the selection in a textline or listbox --print("set the selection: ",y); [m,n] := y; if m = OM and n = OM then txt := full_name() + if tk_type = "entry" then " select clear" else " selection clear" end if; return tk_kall(txt); end if; n := if n = OM then str(unstr(m) - 1) else n end if; txt := full_name() + if tk_type = "entry" then " selection range " else " selection set " end if + str(unstr(m) - 1) + " " + str(unstr(n)); --print("set the selection cmd: ",txt); res := tk_kall(txt); return res; when "sel.anchor" => -- set the selection anchor in a textline or listbox txt := full_name() + if tk_type = "textline" then " selection from " else " selection anchor " end if + str(unstr(y(1)) - 1); return tk_kall(txt); when "active" => -- set activated element in a listbox, menu, or scrollbar txt := full_name() + " activate " + if tk_type = "scrollbar" then y(1) else str(unstr(y(1)) - 1) end if; --print("active ",txt); return tk_kall(txt); when "xscroller" => -- attach a scroller to a horizontally scrollable widget if not tk_type in horiz_scrollable then -- error: widget is not scrollable abort(tk_type + "widgets are not scrollable"); end if; if not type(y) = "TKW" or not y.tk_type = "scrollbar" then -- error: not a scrollbar abort("A widget's 'xscroller' attribute must be a scrollbar"); end if; -- now link the widget's attribute to the scrollbar, and the scrollbar's command attribute to the widget yname := y.full_name(); widname := full_name(); tk_kall(yname + " configure -command {" + widname + " xview}"); return tk_kall(widname + " configure -xscrollcommand {" + yname + " set}"); when "yscroller" => -- attach a scroller to a fully scrollable widget if not tk_type in fully_scrollable then -- error: widget is not scrollable abort(tk_type + "widgets are not vertically scrollable"); end if; if not type(y) = "TKW" or not y.tk_type = "scrollbar" then -- error: not a scrollbar abort("A widget's 'yscroller' attribute must be a scrollbar"); end if; -- now link the widget's attribute to the scrollbar, and the scrollbar's command attribute to the widget yname := y.full_name(); widname := full_name(); tk_kall(yname + " configure -command {" + widname + " yview}"); return tk_kall(widname + " configure -yscrollcommand {" + yname + " set}"); when "clipboard" => -- assignment of y to clipboard tk_kall("clipboard clear"); return tk_kall("clipboard append \"" + str(y(1)) + "\""); -- here y is transmitted as tuple when "grab" => -- set of a toplevel window grab status if tk_type /= "toplevel" then abort("'Grab' can only be set for toplevel windows"); end if; grab_stg := if y = ["global"] then "grab -global " else "grab " end if + full_name(); return tk_kall(grab_stg); end case; end if; -- otherwise we have a configuration call, either for a canvas or text item or an ordinary widget -- break x, the string of options to be configured, into a tuple orig_x := x; x := if x = OM then [] else chop(x) end if; -- allow comma if there is no semi. If x = OM we convert it into a 'list of OM's' --print("configuration call: ",x," ",y," orig_x: ",orig_x," #orig_x: ",#orig_x); if tk_type = "image" and parent = OM then -- must be an absolute image return configure_image(x,y); -- configure the image elseif tk_type = "menu" then -- configure a menu return configure_menu(x,y); -- configure a menu elseif tk_type = "bitmap" and parent = OM then -- must be an absolute bitmap return configure_bitmap(x,y); -- configure the bitmap elseif tk_type in canvas_items then -- we must configure a canvas item return configure_canvas_item(x,y); -- configure a canvas item elseif tk_type = "toplevel" and ((x(1) in wm_attributes) or orig_x = "") then -- we allow a null string, which sets all attributes from a map if orig_x = "" then x := []; end if; return set_wm_atts(x,y); -- set window-manager attributes of a toplevel elseif tk_type = "file" then -- we must configure a file item return configure_file_item(name,x,y); -- configure a file item elseif tk_type = "socket" or tk_type = "server_socket" then -- we must configure a socket item return configure_socket_item(x,y); -- configure a socket item; but this is never called end if; --print("ordinary widget configuration call: ",x," ",y); case type(y) -- this is a configuration call for an ordinary widget, or a geometry-manager operation when "TUPLE" => -- we use y in a geometry or configuration call case x(1) -- here we handle the geometry-manager related pseudo-configuration operations, -- and some others when "pack","side","grid","row","column","place" => return configure_geometry(x,y); -- handle geometry-manager calls otherwise => -- we have a configuration call for a widget, or canvas or text item --print("widget/canvas/text configuration call: ",x,"",y); if tk_type = "toplevel" and #x = 1 and x(1) = "rect" then -- configuring the rectangle of a toplevel window geomstg := str(abs(y(3) - (y1 := y(1)))) + "x"+ str(abs(y(4) - (y2 := y(2)))) + "+" + str(y1) + "+" + str(y2); -- geometry string in tk_kall(txt := "wm geometry " + full_name() + " " + geomstg); -- set the specified geometry --print("winconfig: ",txt); return; -- done with this case elseif (#name > 0 and name(1) = "w") or tk_type = "toplevel" then -- we are configuring a Tk widget or toplevel the_text := full_name() + " configure "; else -- we are configuring a canvas or text item if x = "coords" then -- if the original string x is simply 'coords', y will be -- the string of coordinates being assigned; otherwise -- this will be y(j) in the loop below coord_text := parent.full_name() + " coords " + name(2..) + " " +/ [str(xx) + " ": xx in y]; --print("coord_text: ",coord_text); return tk_kall(coord_text); -- execute the 'coords' command; then finished end if; -- otherwise this is not a coords call; handle normally the_text := parent.full_name() + " itemconfigure " + name(2..) + " "; end if; attribs_to_handle := 0; -- count of attributes not handled in subroutine for xj = x(j) loop -- assemble the options and option values if handle_pseudo_attrib(name,xj,y(j)) then continue; end if; -- we are done with this case if 'true' is returned; otherwise not attribs_to_handle +:= 1; -- count of attributes not handled in subroutine if is_procedure(yj := y(j)?"") then -- call the tk library 'createcommand' function, to associate a -- new command id of the form Pnnn with the SETL callback procedure supplied; -- then include the command id in the command string being built tk_createcommand(interp, item := "P" + str(proc_ctr +:= 1) ,y(j)); -- we will include the callback id in the command being built elseif type(yj) = "TKW" and yj.parent = OM and yj.tk_type in {"image","bitmap"} then item := yj.name; -- we are assigning an absolute image or bitmap; use its name -- Note that absolute images and bitmaps are referenced by their name elseif xj = "scrollregion" then the_text +:= (" -" + xj + " {" + join(y," ") + "} "); -- print("scrollregion: ",the_text); continue; else -- item should be a tk string value if yj = "" then item := " {}"; -- transmit nullstrings in Tk form elseif yj(1) = "{" then -- special case for fonts; omit quotes item := " " + str(yj); else item := " \"" + str(yj) + "\""; -- include the string value in the command being built end if; end if; --print("fonts, bitmaps: ",xj," ",item); the_text +:= if xj = "font" or xj = "bitmap" or xj = "image" then (" -" + xj + " " + item) -- fonts, bitmaps, and images not quoted. Note: for images, only forms like label("image") := "{}" are used else (" -" + xj + item) end if; -- add to command if not pseudo-att end loop; end case; --print("the_text: ",the_text); if attribs_to_handle = 0 then return ""; end if; -- if all attributes are handled in subroutine, there is nothing to do return tk_kall(the_text); -- we finish the operation here -- a SETL callback procedure (for commands), or a tk string value when "PROCEDURE","TKW" => -- the right-hand side is a procedure or tk widget. convert it to tk string form -- ignore all but the first component of the left-hand parameter tuple --if type(y) = "TKW" then print("TKW case: ",x," ",y); end if; the_text := full_name() + " configure -" + x(1) + " " + tk_string_of(y); return tk_kall(the_text); otherwise => -- the right-hand side is neither a string, tuple, or procedure; might be absolute image if x(1) = "propagate" then -- set propagation attribute of frame or window tk_kall("grid propagate " + full_name() + " " + y); -- set both the grid and the pack attributes return tk_kall("pack propagate " + full_name() + " " + y); end if; the_text := full_name() + " configure -" + x(1) + " " + str(y); --print("config call, final case: ",the_text); return tk_kall(the_text); end case; end; -- of procedure for self(x) := y; operations procedure analyze_text(raw_text); -- raw text analysis routine -- given a raw_text, this returns the same text with all tag and mark designators removed, along with -- two lists: a tag list of the form [i,j,tag] giving the opening and closing 'line.char' index of -- each tagged section, and a marks list of the form [i,mark] giving the 'line.char' index of the -- mark position. num_lines := 1; -- number of the current line raw_text_sections := single_out(raw_text,"`\r\n>"); -- break into sections --print("raw_text: ",raw_text,"\nraw_text_sections: ",raw_text_sections); len_raw := #raw_text; -- get the length of the raw text current_aux := ""; current_aux_len := 0; -- current auxiliary string tags_list := []; -- list of completed tags posns_of_open := {}; -- maps open tags and marks into their list of positions inside_of_opener := false; inside_of_closer := false; start_of_tag := 0; end_of_last_opener := OM; last_tag_opened := OM; -- ending position of last tag opener -- the starting position of a possible tag string, if we are inside_of_opener or inside_of_closer raw_text_loc := 1; -- next character in raw text to be examined digested_text := ""; digested_length := 0; -- the tag-free text, and its length within this line rtsix := 0; while (section := raw_text_sections(rtsix +:= 1)) /= OM loop -- iterate over the sections --print("section: ",section,20 * " ",digested_text," ",inside_of_opener," ",inside_of_closer); if (ns := #section) = 0 then continue; end if; if (ns > 1 or section notin "`\r\n") and not (inside_of_opener or inside_of_closer) then -- we have an inactive section digested_text +:= section; digested_length +:= (ns := #section); raw_text_loc +:= ns; -- and advance in the raw text continue; -- done with this piece end if; -- otherwise we have an active character raw_text_loc +:= #section; -- advance this to the next character past that being examined if section = "`" then if inside_of_opener or inside_of_closer then -- check to see if we are at the end if raw_text_loc <= len_raw and raw_text(raw_text_loc) = ">" and raw_text_loc - 1 > current_aux_len and raw_text(raw_text_loc - current_aux_len - 1..raw_text_loc - 2) = current_aux then -- we are at the end of an opener or closer tag_completed := raw_text(start_of_tag..raw_text_loc - current_aux_len - 2); if inside_of_opener then -- we have completed an opener inside_of_opener := false; last_tag_opened := tag_completed; end_of_last_opener := raw_text_loc; posns_of_open(tag_completed) := (posns_of_open(tag_completed)?[]) with (str(num_lines) + "." + str(digested_length)); --print("completed an opener: ",tag_completed," ",posns_of_open); raw_text_loc +:= 1; raw_text_sections(rtsix + 1) := raw_text_sections(rtsix + 1)(2..); -- drop the '>' else -- we have completed a closer; see if it is null, and if it closes anything --print("completed a closer: ",tag_completed," ",end_of_last_opener," ",start_of_tag - 4 - current_aux_len); inside_of_closer := false; -- when a tag ends, we must see if it is a null tag. If so, we take it as a -- current_aux change. if not, we generate an entry in the tags_list if end_of_last_opener = start_of_tag - 4 - current_aux_len then -- we have ...`><`..., so a null tag current_aux_len := #(current_aux := tag_completed); posns_of_open(tag_completed) := OM; -- this null tag is no longer open --print("current_aux changed to: ",current_aux); elseif #(opened := posns_of_open(tag_completed)) > 0 then -- tag does not apply to null section and should not be ignored tags_list with:= [opened(1), (str(num_lines) + "." + str(digested_length)),tag_completed]; -- add run to list of completed tags posns_of_open(tag_completed) := OM; -- this tag is no longer open end if; raw_text_loc +:= 1; raw_text_sections(rtsix + 1) := raw_text_sections(rtsix + 1)(2..); -- drop the '>' end if; end if; -- else not at the end; just bypass the ` character, which becomes part of the tag else -- we are not in an opener or closer; check to see if a new opener or closer is starting --print("see if strting: ",raw_text_loc," ",raw_text); if raw_text_loc > 2 and raw_text(raw_text_loc - 2) = "<" then -- may have either an opener or a closer if raw_text_loc <= len_raw and raw_text(raw_text_loc) = "`" -- possibly start of closer and raw_text_loc + current_aux_len <= len_raw and raw_text(raw_text_loc + 1..raw_text_loc + current_aux_len) = current_aux then -- definitely is start of closer start_of_tag := raw_text_loc + current_aux_len + 1; -- note start of tag inside_of_closer := true; rtsix +:= 1; raw_text_loc +:= 1; -- the following "`" character has been handled digested_text := digested_text(1..#digested_text - 1); -- drop the opening '<' digested_length -:= 1; continue; -- this "`" character has been handled elseif raw_text_loc + current_aux_len <= len_raw and raw_text(raw_text_loc..raw_text_loc + current_aux_len - 1) = current_aux then -- start of opener start_of_tag := raw_text_loc + current_aux_len; -- note start of tag inside_of_opener := true; digested_text := digested_text(1..#digested_text - 1); -- drop the opening '<' digested_length -:= 1; continue; -- this "`" character has been handled end if; -- otherwise just collect the character end if; -- end case in which we are not inside_of_opener or inside_of_closer digested_text +:= section; digested_length +:= 1; end if; -- end treatment of "`" character elseif section in "\r\n" then -- current character is a carriage return or linefeed num_lines +:= 1; digested_length := 0; -- start a new section if inside_of_opener then -- the opener ends; all characters back to the initial <`ccc -- must be digested digested_text +:= (addst := raw_text(start_of_tag - 2 - current_aux_len..raw_text_loc - 1)); inside_of_opener := false; -- no longer inside_of_opener elseif inside_of_closer then -- the closer ends digested_text +:= (addst := raw_text(start_of_tag - 3 - current_aux_len..raw_text_loc - 1) + section); inside_of_opener := false; -- no longer inside_of_opener else -- just collect the character digested_text +:= section; end if; -- else -- something else inside an opener or closer; just bypass end if; end loop; --print("digested_text: ",digested_text); print("tags_list: ",tags_list); print("posns_of_open: ",posns_of_open); return [digested_text,tags_list,posns_of_open]; -- the remaining open tags becomes the marks_list returned end analyze_text; procedure setup_text(tags_list,marks_list); -- set up pre-analyzed text in Tk text area (self) for [range_start,range_end,tag] in tags_list loop tag_add_no_offs(tag,range_start,range_end); end loop; for [mark,mark_locs] in marks_list, n in mark_locs loop -- [n1,n2] := breakup(n,"."); mark_set(mark,n); end loop; end setup_text; procedure set_whole_contents(y); -- set content of an entry, text, message, or label widget to y -- set the slider position of a scale, or the title of a toplevel widget; -- set the menu of a menubutton; write to a client socket if tk_type /= "menubutton" and tk_type /= "toplevel" then y := stg_to_Tk(str(y)); end if; -- force y to sanitized string form --print("converted y: ",y); case tk_type when "entry" => -- entry(OM) := y sets the whole entry widget text tk_kall(full_name() + " delete 0 end"); txt := full_name() + " insert 0 \"" + y + "\""; -- y has been converted to Tk form above --print("setting entry: ",txt); return tk_kall(txt); when "text" => -- text(OM) := y sets the whole text widget contents, after text analysis [vis_text,tag_list,mark_list] := analyze_text(str(y)); --print("vis_text: ",vis_text,"*"); tk_kall(full_name() + " delete 1.0 end"); to_ret := tk_kall(full_name() + " insert 1.0 \"" + vis_text + "\""); setup_text(tag_list,mark_list); return to_ret; when "message","label" => -- message(OM) := y, etc. sets the whole message or label widget text return tk_kall(full_name() + " configure -text \"" + y + "\""); -- y has been converted to Tk form above when "scale" => -- slider(OM) := y sets the slider position variable := tk_kall(full_name() + " set " + y); when "toplevel" => -- toplevel(OM) := y sets the title return tk_kall("wm title " + full_name() + " {" + y + "}"); -- y has NOT been converted to Tk form above when "menubutton" => -- toplevel(OM) := y sets the menu of a menubutton return tk_kall(full_name() + " configure -menu " + y.full_name()); when "socket" => -- socket(OM) := y writes to a socket tk_kall("puts " + name + " \"" + y + "\""); -- pass 'write string' command to tk -- y has been converted to Tk form above otherwise => abort("Illegal object type " + tk_type + " in set_whole_contents operation"); end case; end set_whole_contents; procedure reconfigure_from_map(att_map); -- reconfigure all attributes using attribute map defaults := Tk_data_defaults(tk_type); -- get the default attribute values att_map +:= {[x,y]: [x,y] in defaults | att_map(x) = OM}; att_map("wincoords") := OM; att_map("rect") := OM; -- treat these as 'write-only' att_map("toplevel") := OM; att_map("children") := OM; -- treat these as 'write-only' att_map("bd") := OM; att_map("ismapped") := OM; att_map("manager") := OM; att_map("showing") := OM; -- treat these as 'write-only' att_map("Tk_tags") := OM; -- the tags attributes are handled elsewhere if tk_type = "frame" or tk_type = "toplevel" then -- eliminate read-only attributes of toplevels (can be set only when toplevel being created) att_map("colormap") := OM; -- can't modify -colormap option after widget is created att_map("visual") := OM; -- can't modify -visual option after widget is created att_map("use") := OM; -- can't modify -use option after widget is created att_map("container") := OM; -- can't modify -container option after widget is created att_map("screen") := OM; -- can't modify -screen option after widget is created att_map("class") := OM; -- can't modify -class option after widget is created end if; if tk_type = "listbox" then items := att_map("text"); att_map("text") := OM; -- get the listbox items; this is not a standard attribute end if; if tk_type = "entry" then contents := att_map("text"); att_map("text") := OM; -- get the listbox items; this is not a standard attribute end if; att_string := "" +/ ["-" + x + " " + if (sty := str(y)) = "" then "{}" else " {" + sty + "} " end if + " ": [x,y] in att_map]; -- stg_to_Tk(str(y)) txt := (fn := full_name()) + " configure " + att_string; -- set up the reconfiguration command --if tk_type = "frame" then print("reconfiguration command: ",txt); end if; res := tk_kall(txt); --if tk_type = "toplevel" then print("reconfiguration result: ",res); end if; if tk_type = "listbox" then -- must set the listbox list elements tk_kall(fn + " delete 0 end"); -- drop all the present items res := tk_kall(fn + " insert end " + join(["{" + item + "}": item in items]," ")); -- insert the new items, quoting them (need to sanitize beter) end if; if tk_type = "entry" then contents := set_whole_contents(contents); -- set the text contents end if; return res; end reconfigure_from_map; procedure null_whole_attribute(att); -- null the value of this attribute if att in geom_manager_main_atts then -- if the side, pack, grid, or place attribute is nulled, then erase the item if name(1) = "c" then -- we have a canvas item tk_kall(parent.full_name() + " delete " + name(2..)); -- delete the canvas item -- if it is an image, then delete the source image (THIS IS A TEMPORARY FIX, SHOULD BE:) -- if it is an image or a widget, then remove the reference from the source_of map, -- and destroy the object when no references to it remain if (so := source_of(fna := full_name())) /= OM then -- the source must be an image; delete it res := tk_kall("image delete " + so.name); source_of(fna) := OM; -- remove from the source_of map --print("so: ",so," res= ",res); end if; else -- we have a widget tk_kall(if att = "side" then "pack" else att end if + " forget " + full_name()); end if; else case att when "grab" => tk_kall("grab release " + full_name()); when "placed" => -- drop all the placed items items := tk_kall(" place slaves " + full_name()); tk_kall(" place forget " + items); when "packed" => -- drop all the packed items items := tk_kall(" pack slaves " + full_name()); tk_kall(" pack forget " + items); when att = "gridded" => -- drop all the gridded items items := tk_kall(" grid slaves " + full_name()); tk_kall(" grid forget " + items); end case; end if; end null_whole_attribute; procedure configure_toplevel(itm,options,y); -- handle all the standard built-in dialogs y := chop(y); case itm when "ask" => -- open a general choice dialog -- parameters of tk_dialog call are: win,title,message,(bitmap = {} if none),labels,default_num opt_vals := {[opt,str(y(j))]: opt = options(j)}; -- maps options to their values if given -- prepare the label list in proper form labels := "" +/ ["\"" + stg_to_Tk(label) + "\" ": label in chop(opt_vals("labels")?"")]; txt := "tk_dialog " + full_name() + " \"" + opt_vals("title")?"{}" + "\" \"" + opt_vals("message")?"{}" + "\" " + if (ovbm := opt_vals("bitmap")) /= OM then "\"" + ovbm + "\"" else "{}" end if + " " + str(unstr(opt_vals("default")) - 1)?"{}" + " " + labels; when "ask_ok" => -- open an ask_ok dialog of some type txt := "tk_messageBox" +/ [" -" + opt + " \"" + y(j) + "\" ": opt = options(j)]; when "ask_color" => -- open a color-picker dialog txt := "tk_chooseColor" +/ [" -" + opt + " \"" + y(j) + "\" ": opt = options(j)]; when "ask_file" => -- open an open-file dialog if (opt := options(1)) = "filetypes" then -- use only the first option txt := "tk_getOpenFile -" + opt + " {" +/ [yval + " ": yval = y(j)] + "}"; else txt := "tk_getOpenFile" +/ [" -" + opt + " " + y(j): opt = options(j)]; end if; when "ask_save_file" => -- open a save-file dialog txt := "tk_getSaveFile" +/ [" -" + opt + " " + y(j): opt = options(j)]; otherwise => abort("Illegal configuration call " + itm + " for toplevel widget."); end case; return (dialog_response := tk_kall(txt)); end configure_toplevel; procedure configure_image(x,y); -- configure an absolute image y := if y = OM then [ ] else chop(y) end if; for att = x(j) loop -- set designated intenral attributes of the image if att = "file" then -- we read or clear the image txt := "image delete " + name; -- clear the image in any case; Note that absolute images are referenced by their name if y(j) /= OM then txt +:= ("\n image create photo \"" + name + "\" -file " + y(j)); end if; else -- simply configure the attribute txt := name + " configure -" + att + " \"" + (y(j)?"") + "\""; end if; tk_kall(txt); -- perform the Tk operation end loop; end configure_image; procedure configure_bitmap(x,y); -- configure an absolute bitmap y := if y = OM then [ ] else chop(y) end if; for att = x(j) loop -- set designated intenral attributes of the image if att = "data" then -- we read or clear the image txt := "image delete " + name; -- clear the image in any case; Note that absolute images are referenced by their name if y(j) /= OM then txt +:= ("\n image create bitmap \"" + name + "\" -file " + y(j)); end if; else -- simply configure the attribute txt := name + " configure -" + att + " \"" + (y(j)?"") + "\""; end if; tk_kall(txt); -- perform the Tk operation end loop; end configure_bitmap; procedure configure_menu(x,y); -- configure a menu x_to_y := {[xc,tk_string_of(y(j)?"")]: xc = x(j)}; -- convert procs and nullstrings in y to their Tk forms and represent as map type_and_attvals := read_whole_menu(); men_fn := full_name(); -- full name of the menu being reconfigured tk_kall("destroy " + men_fn); -- destroy the present menu, in preparation for re-creation -- assign the new menu attributes menu_attvals := type_and_attvals(1); for x in domain(menu_attvals) | (new := x_to_y(x)) /= OM loop menu_attvals(x) := new; end loop; --rbreak(men_fn,"."); men_fn +:= "w1000"; new_menu_string := "menu " + men_fn +/ [" -" + option_name + " " + tk_string_of(option_val): [option_name,option_val] in menu_attvals]; --print(new_menu_string); tk_kall(new_menu_string); -- recreate the menu with its new attributes -- now add back all the former items for j in [2..#type_and_attvals] loop item_attvals := type_and_attvals(j); -- attribute map for the items new_item_string := men_fn + " add " + item_attvals("type") + " " +/ [" -" + option_name + " " + tk_string_of(option_val): [option_name,option_val] in item_attvals | option_name /= "type"]; tk_kall(new_item_string); --print(new_item_string); end loop; end configure_menu; procedure read_whole_menu(); -- get the type and entry values of a menu -- this returns the menu data as a tuple of maps, the first component representing the menu attributes and the remaining -- representing the sucessibe menu items. Each map sends attribute namesinto attribute values men_len := unstr(tk_kall((men_name := full_name()) + " index end")); menu_optnames := breakup("activebackground,activeforeground,background,foreground,borderwidth,activeborderwidth,cursor," + "disabledforeground,font,relief,takefocus,postcommand,selectcolor,tearoff,tearoffcommand,title,type",","); data_tup := [{[optname,tk_kall(men_name + " cget -" + optname)]: optname in menu_optnames}]; -- will collect more menu_item_optnames := breakup("activebackground,activeforeground,accelerator,background,foreground,bitmap,columnbreak,command,font," + "hidemargin,image,label,state,underline,indicatoron,offvalue,onvalue,variable,selectcolor,selectimage,value,menu",","); for j in [0..men_len] loop item_type := tk_kall(men_name + " type " + j); item_data := {["type",item_type]} + {[optname,x]: optname in menu_item_optnames | (x := reduce_unknowns(tk_kall(men_name + " entrycget " + j + " -" + optname))) /= OM}; data_tup with:= item_data; end loop; return data_tup; end read_whole_menu; procedure reduce_unknowns(att_stg); -- replace 'unknown option' error messages by 'U' return if att_stg = "" or att_stg(#att_stg) /= "\"" then att_stg else OM end if; end reduce_unknowns; procedure configure_canvas_item(x,y); -- configure a canvas item -- Tk uses itemcget/itemconfigure calls to get/set all but the coords attributes, -- but a coords calls to get/set the coords attribute. Note that x has already been broken into a tuple, -- but y has not -- The attributes of canvas items are: tags, width, and coords in all cases, plus -- for canvas geometric objects: fill, outline, and stipple -- for images: anchor and image -- for canvas text objects: anchor, fill, font, justify, stipple, and text -- for canvas widgets: anchor, height, and window -- x is a tuple of attributes; y can be a string, possibly semicolon-delimited -- with a comma-delimited list of coordinates, or can be a tuple, possibly -- including a comma or semicolon-delimited delimited string of coordinates, or -- a tuple of coordinates. fn := parent.full_name(); -- get the full tk name of the parent canvas attr_list := x; -- print("configure_canvas_item: ",full_name()," ",x," ",y," ",fn); -- y := if #x = 1 then [y] else chop(y) end if; -- -- turn argument of any type into unit tuple, break string into list for att = attr_list(j) loop -- loop, using the tk itemconfigure command to set the value -- unless the attribute is 'coords', in which case we use -- the parent coords command to set it if att = "coords" and #x = 1 and ((not is_string(y1 := y(1))) or not "," in y1) then val := y; -- use whole y as matching attrib value elseif is_procedure(y(j)) then -- call the tk library 'createcommand' function, to associate a -- new command id of the form Pnnn with the SETL callback procedure supplied; -- then include the command id in the command string being built tk_createcommand(interp, val := "P" + str(proc_ctr +:= 1) ,y(j)); -- we will include the callback id in the command being built else -- item should be a tk string value val := y(j); -- include the string value in the command being built end if; if att = OM then -- we bind a procedure to the canvas item op_name := main_command(tk_type); txt := parent.full_name() + " bind " + name(2..) + " " + op_name + " " + val; --print("configure_canvas_item: ",txt," ",x," ",y," ",val," ",name," ",op_name); elseif att /= "coords" then txt := fn + " itemconfigure " + name(2..) + " -" + att + " " + str(val); --print("configure canvas_item: ",txt); stop; else -- a tuple of numbers or comma-delimited string is expected if is_string(val) and (nv := #val) > 0 and "{" = val(1) then val := val(2..nv - 1); end if; if is_string(val) and "," in val then val := chop(val); end if; txt := fn + " coords " + name(2..) + " " + "" +/ [str(d) + " ": d in val]; --print("configure coords: ",txt," y: ",y," res: ",tk_kall(txt)); print("self(OM) is: ",self(OM)); end if; res := tk_kall(txt); end loop; end configure_canvas_item; procedure configure_file_item(fname,att_list,val_list); -- configure a file item --print("configure_file_item: ",att_list,val_list); if not is_tuple(att_list) then att_list := [att_list]; end if; -- force to tuple if not is_tuple(val_list) then val_list := [val_list]; end if; -- force to tuple for att = att_list(j) loop val := val_list(j); case att when "mac_creator" => if is_string(val) and # val = 4 then tk_kall("file attributes " + fname + " -creator " + val); end if; when "mac_type" => if is_string(val) and # val = 4 then tk_kall("file attributes " + fname + " -type " + val); end if; when "mac_hidden" => tk_kall("file attributes " + fname + " -readonly " + if val = 0 or val = "0" then "0" else "1" end if); when "mac_readonly" => tk_kall("file attributes " + fname + " -readonly " + if val = 0 or val = "0" then "0" else "1" end if); when "name" => if val = "" then -- delete the file tk_kall("file delete " + fname); elseif is_string(val) then -- rename the file, or create a directory of the specifed name the_type := tk_kall("file type " + fname); -- first determine if the file exists not_exists_msg := rmatch(the_type,"no such file or directory"); not_exists := not_exists_msg /= ""; if not_exists then -- create a directory of the given name tk_kall("file mkdir " + val); else -- rename the file tk_kall("file rename " + fname + " " + val); end if; end if; end case; end loop; end configure_file_item; procedure configure_socket_item(x,y); -- configure a socket item end configure_socket_item; procedure configure_geometry(x,y); -- handle geometry-manager calls --print("configure_geometry: ",x,y); case x(1) when "pack" => -- ignore, but must be followed by pack options (pack geometry manager) if exists j in [2..#x] | x(j) notin pack_options then abort(str(x(j)) + " is not a legal option for a pack operation"); end if; if #y /= #x - 1 then abort("Different number of pack operation options and option values"); end if; the_text := "pack " + full_name(); for j in [2..#x] loop -- now assemble the options and option values the_text +:= (" -" + x(j) + " \"" + str(y(j - 1)) + "\""); end loop; when "side" => -- must be followed by pack options (pack geometry manager) if exists j in [1..#x] | x(j) notin pack_options then abort(str(x(j)) + " is not a legal option for a pack operation"); end if; if #y /= #x then abort("Different number of pack operation options and option values " + str(x) + "\n" + str(y)); end if; the_text := "pack " + full_name(); for j in [1..#x] loop -- now assemble the options and option values the_text +:= (" -" + x(j) + " \"" + str(y(j)) + "\""); end loop; when "grid" => -- ignore, but must be followed by grid options (grid geometry manager) if exists j in [2..#x] | x(j) notin grid_options then abort(str(x(j)) + " is not a legal option for a grid operation"); end if; if #y /= #x - 1 then abort("Different number of grid operation options and option values"); end if; the_text := "grid " + full_name(); for j in [2..#x] loop -- now assemble the options and option values the_text +:= (" -" + x(j) + " \"" + str(y(j - 1)) + "\""); end loop; when "row","column" => -- must be followed by grid options (grid geometry manager) if exists j in [1..#x] | x(j) notin grid_options then abort(str(x(j)) + " is not a legal option for a grid operation"); end if; if #y /= #x then abort("Different number of grid operation options and option values" + str(x) + "\n" + str(y)); end if; the_text := "grid " + full_name(); for j in [1..#x] loop -- now assemble the options and option values the_text +:= (" -" + x(j) + " \"" + str(y(j)) + "\""); end loop; when "place" => -- but must be followed by place options (place geometry manager) -- example is obj("place,x,y,anchor") := "xv,yv,nw"; if exists j in [2..#x] | x(j) notin place_options then abort(str(x(j)) + " is not a legal option for a place operation"); end if; if #y /= #x - 1 then abort("Different number of place operation options and option values" + str(x) + "\n" + str(y)); end if; the_text := "place " + full_name(); for j in [2..#x] loop -- now assemble the options and option values the_text +:= (" -" + x(j) + " \"" + str(y(j - 1)) + "\""); end loop; end case; res := tk_kall(the_text); --print("configure_geometry: ",res," ",the_text); return res; -- we finish the operation here end configure_geometry; procedure configure_geometry_in(x,y,in_widget); -- handle extended geometry-manager calls in_widget_name := in_widget.full_name(); -- get the full name of the widget which will become the container case x(1) when "pack" => -- ignore, but must be followed by pack options (pack geometry manager) if exists j in [2..#x] | x(j) notin pack_options then abort(str(x(j)) + " is not a legal option for a pack operation"); end if; if #y /= #x - 1 then abort("Different number of pack operation options and option values"); end if; the_text := "pack " + full_name(); for j in [2..#x] loop -- now assemble the options and option values the_text +:= (" -" + x(j) + " \"" + y(j - 1) + "\""); end loop; when "side" => -- must be followed by pack options (pack geometry manager) if exists j in [1..#x] | x(j) notin pack_options then abort(str(x(j)) + " is not a legal option for a pack operation"); end if; if #y /= #x then abort("Different number of pack operation options and option values " + str(x) + "\n" + str(y)); end if; the_text := "pack " + full_name(); for j in [1..#x] loop -- now assemble the options and option values the_text +:= (" -" + x(j) + " \"" + y(j) + "\""); end loop; when "grid" => -- ignore, but must be followed by grid options (grid geometry manager) if exists j in [2..#x] | x(j) notin grid_options then abort(str(x(j)) + " is not a legal option for a grid operation"); end if; if #y /= #x - 1 then abort("Different number of grid operation options and option values"); end if; the_text := "grid " + full_name(); for j in [2..#x] loop -- now assemble the options and option values the_text +:= (" -" + x(j) + " \"" + y(j - 1) + "\""); end loop; when "row","column" => -- must be followed by grid options (grid geometry manager) if exists j in [1..#x] | x(j) notin grid_options then abort(str(x(j)) + " is not a legal option for a grid operation"); end if; if #y /= #x then abort("Different number of grid operation options and option values" + str(x) + "\n" + str(y)); end if; the_text := "grid " + full_name(); for j in [1..#x] loop -- now assemble the options and option values the_text +:= (" -" + x(j) + " \"" + y(j) + "\""); end loop; when "place" => -- but must be followed by place options (place geometry manager) -- example is obj("place,x,y,anchor") := "xv,yv,nw"; if exists j in [2..#x] | x(j) notin place_options then abort(str(x(j)) + " is not a legal option for a place operation"); end if; if #y /= #x - 1 then abort("Different number of place operation options and option values" + str(x) + "\n" + str(y)); end if; the_text := "place " + full_name(); for j in [2..#x] loop -- now assemble the options and option values the_text +:= (" -" + x(j) + " \"" + y(j - 1) + "\""); end loop; end case; res := tk_kall(txt := the_text + " -in " + in_widget_name); -- we finish the operation here --print("configure_geometry_in: ",res," ",txt); return res; end configure_geometry_in; procedure handle_pseudo_attrib(name,xj,yj); -- handle special widget configuration ops case tk_type when "entry" => -- special action for the 'insert' pseudo-attribute if xj /= "insert" then return false; end if; tk_kall(full_name() + " icursor " + str(yj)); -- special action for the 'insert' pseudo-attribute; note that this is not -- possible for text widgets return true; -- done with this case when "canvas_text" => -- special action for the 'insert' pseudo-attribute if xj /= "insert" then return false; end if; tk_kall(parent.full_name() + " icursor " + name(2..) + " " + str(yj)); return true; -- done with this case when "radiobutton","checkbutton" => -- special action for the 'selected' pseudo-attribute if xj /= "selected" then return false; end if; -- else is pseudo-attribute: set the associated variable if str(yj) = "1" then tk_kall(full_name() + " select "); else tk_kall(full_name() + " deselect "); end if; return true; -- done with this case -- when "checkbutton" => -- special action for the 'selected' pseudo-attribute -- -- if xj /= "selected" then return false; end if; -- -- else is pseudo-attribute: set the associated variable -- varname := tk_kall(full_name() + " cget -variable "); -- get name of associated variable -- tk_kall("set " + varname + " " + str(yj)); -- return true; -- done with this case when "listbox" => -- special action for the 'hilight' pseudo-attribute -- set the associated variable if xj /= "hilight" then return false; end if; txt := full_name() + " activate " + str(yj); tk_kall(txt); return true; -- done with this case otherwise => if xj /= "coords" then return false; end if; -- the "coords" case must be handled specially yy := breakup(yj,","); coord_text := parent.full_name() + " coords " + name(2..) + " " +/ [str(xx) + " ": xx in yy]; tk_kall(coord_text); -- execute the 'coords' command return true; -- done with this case end case; end handle_pseudo_attrib; procedure may_quote(stg); -- quote a string if not enclosed in curly brackets return if #stg > 0 and stg(1) /= "{" then "\"" + stg + "\"" else stg end if; end may_quote; procedure tk_string_of(obj_or_proc); -- find appropriate name for tk widget, image, or procedure -- convert a SETL procedure to a tk command name if none yet issued; use full names for objects other -- than absolute images; and use the simple name for images if obj_or_proc = "" then return "{}"; end if; -- Tk form of nullstring if type(obj_or_proc) = "TKW" then -- check for the absolute image case if obj_or_proc.tk_type in {"image","bitmap"} and obj_or_proc.parent = OM then return obj_or_proc.name; end if; return obj_or_proc.full_name(); -- use full name if this is a Tk widget end if; if type(obj_or_proc) /= "PROCEDURE" then return str(obj_or_proc); end if; -- simply convert to string if not procdure if (pid := proc_tk_name(obj_or_proc)) /= OM then return pid; end if; -- procedure has already been given a tk_name; return this proc_tk_name(obj_or_proc) := (pid := "P" + str(proc_ctr +:= 1)); -- otherwise give it a new name tk_createcommand(interp,pid,obj_or_proc); -- make this name into a tk command return pid; -- return the new name end tk_string_of; procedure beeper(); -- beep procedure tk_call(interp,"beep"); end beeper; procedure stopper(); -- destruction of top level window to force return from Tk main loop tk_call(interp,"destroy ."); end stopper; procedure place(); -- returns object x and y coordinates if 'placed' in parent return [unstr((self("place")("x"))?"-9999"),unstr((self("place")("y"))?"-9999")]; end place; procedure gridbox(i,j); -- returns coordinates of specified gridbox txt := "grid bbox " + full_name() + " " + i + " " + j; return tk_call(interp,txt); end gridbox; procedure raise(after_obj); -- raises object to position just after after_obj, or to top if after_obj = OM if name = "" then return; end if; -- this should not happen if name(1) = "w" then -- raising a widget txt := "raise " + self.full_name(); if after_obj /= OM then txt +:= (" " + after_obj.full_name()); end if; else txt := if parent = OM then "raise ." else parent.full_name() + " " + "raise " + name(2..) end if; if after_obj /= OM then txt +:= (" " + (after_obj.name)(2..)); end if; end if; tk_call(interp,txt); end raise; procedure lower(before_obj); -- lowers object to position just before before_obj, or to bottom if before_obj = OM if name = "" then return; end if; -- this should not happen if name(1) = "w" then -- raising a widget txt := "lower " + self.full_name(); if before_obj /= OM then txt +:= (" " + before_obj.full_name()); end if; else txt := if parent = OM then "lower ." else parent.full_name() + " " + "raise " + name(2..) end if; if after_obj /= OM then txt +:= (" " + (after_obj.name)(2..)); end if; end if; tk_kall(txt); end lower; procedure createtimer(interval,SETL_fun); -- create a timer callback (rings once) pid := tk_string_of(SETL_fun); txt := "after " + if interval = OM then "idle" else str(interval) end if + " " + pid; return tk_kall(txt); -- return if interval = OM then tk_idlecallback(SETL_fun) else tk_createtimer(interval,SETL_fun) end if; end createtimer; procedure cancel_event(id); -- cancel a timer or idle callback return tk_kall("after cancel " + id); -- return tk_destroy(id); end cancel_event; procedure break_event(); -- suppress further processingof an event return tk_kall("break"); end break_event; -- ****** Operations available for all widgets ****** procedure bindtags(tag); -- gets event bindings for specified tag, or for whole widget if tag = OM if tag /= OM then return tk_kall("bind " + tag); end if; return tk_kall("bindtags " + full_name()); end bindtags; -- procedure virt_event_info(virt_event); -- gets physical definition of specified virtual events, or virtual event list if param is OM -- return tk_kall("event info " + virt_event?""); -- end virt_event_info; procedure virt_event_delete(virt_event); -- deletes specified virtual event return tk_call(interp,"event delete " + virt_event); end virt_event_delete; -- ****** Basic Operationss ****** procedure quit(); -- close the tk interpreter tk_call(interp,"destroy ."); -- by destroying the top window tk_quit(interp); -- and then calling the native package end quit; procedure call(txt); -- transmit a command to the tk main loop return tk_kall(txt); end call; procedure mainloop(); -- call the tk main loop and wait for callback return tk_mainloop(interp); end mainloop; -- GIUSEPPE START procedure handle_event(); -- call the tk event and handle the callbacks return tk_handle_event(interp); end handle_event; procedure get_event_source_function(); -- call the tk main loop and wait for callback return tk_get_event_source_function(); -- interp); -- ????? end get_event_source_function; -- GIUSEPPE END procedure setvar(name,val); -- set a tk variable to the indicated value txt := "set " + name + " \"" + stg_to_Tk(str(val)) + "\""; return tk_kall(txt); end setvar; procedure getvar(name); -- read a tk variable txt := "set " + name; return tk_kall(txt); end getvar; procedure update(); -- request screen display update txt := "update"; tk_kall(txt); end update; -- ****** Miscellaneous Utilities ****** procedure clock(); -- clock and date utility -- returns time in format [very_fine,seconds,dau,month,am_pm,weekno_in_year,mm/dd/yy,abbrev_time,monthno,dayno_in_year,dayno_in_week] ticks := tk_kall("clock clicks"); secs := tk_kall("set x [clock seconds]"); timetup := [unstr(ticks),unstr(secs)] + breakup(tk_kall("clock format $x -format %A%%%B%%%p%%%U%%%x%%%c%%%j%%%w"),"%"); return timetup; end clock; -- ****** Canvas Operations ****** procedure addtag_after(tag); -- add a specified tag to the item just above (or below) that with a -- given tag in the display list, or to all items, or to all enclosed -- in a given rectangle, or to the nearest item to a given point, -- or to items which already have a given tag. txt := parent.full_name() + " addtag \"" + tag + "\" above " + name(2..); --print("addtag_before: ",txt); return tk_kall(txt); end addtag_after; procedure addtag_before(tag); -- 'add tag below' case; see preceding comment txt := parent.full_name() + " addtag \"" + tag + "\" below " + name(2..); return tk_kall(txt); end addtag_before; procedure addtag_in(tag,rect); -- add tag to all items in a rectangle, or to all items if rect is OM if rect = OM then txt := full_name() + " addtag \"" + tag + "\" all"; return tk_kall(txt); end if; rect := "" +/ [str(x) + " ": x in chop(rect)]; txt := full_name() + " addtag \"" + tag + "\" enclosed " + rect; return tk_kall(txt); end addtag_in; procedure addtag_nearest(tag,xy,halo,start); -- nearest to x,y, or last within radius halo of x,y, or -- first such after item start in the canvas display list [x,y] := breakup(xy,","); txt := full_name() + " addtag \"" + tag + "\" closest " + x + " " + y; if halo /= OM then txt +:= (" " + str(halo)); end if; if start /= OM then txt +:= (" " + str(start)); end if; return tk_kall(txt); end addtag_nearest; procedure addtag_if(newtag,hastag); txt := full_name() + " addtag \"" + newtag + "\" withtag \"" + hastag + "\""; return tk_kall(txt); end addtag_if; procedure addtag(newtag); -- add new tag to a canvas item txt := parent.full_name() + " addtag \"" + newtag + "\" withtag " + name(2..); return tk_kall(txt); end addtag; procedure bbox_tags(tags); -- get bounding box of items with given tags or ids txt := full_name() + " bbox " +/ ["\"" + tag + "\" ": tag in chop(tags)]; return tk_kall(txt); end bbox_tags; procedure canvasx(x,roundto); -- map from screen to canvas coordinates, possibly rounded to grid units txt := full_name() + " canvasx " + str(x) + " " + if roundto /= OM then str(roundto) else "" end if; return unstr(tk_kall(txt)); end canvasx; procedure canvasy(y,roundto); -- map from screen to canvas coordinates, possibly rounded to grid units txt := full_name() + " canvasy " + str(y) + " " + if roundto /= OM then str(roundto) else "" end if; return unstr(tk_kall(txt)); end canvasy; procedure delete(); -- delete a canvas item if tk_type notin canvas_items then return; end if; txt := parent.full_name() + " delete " + name(2..); return tk_kall(txt); end delete; procedure delete_till(end_ci); -- delete a range of canvas items if tk_type notin canvas_items then return; end if; first_no := unstr(name(2..)); last_no := unstr(end_ci.name(2..)); pfn_d := parent.full_name() + " delete "; for itm_no in [first_no,first_no + 10..last_no - 10] loop txt := "" +/ [pfn_d + str(itm_no + j) + "\n": j in [0..9]]; tk_kall(txt); -- delete a block of 10 items last_del := itm_no + 10; -- keep track end loop; txt := "" +/ [pfn_d + str(j) + "\n": j in [last_del..last_no]]; -- delete the final group return tk_kall(txt); end delete_till; procedure draw_ovals(descriptor_tup); -- draw a group of ovals; callsed as ca.draw_ovals(descriptor_tup), ca must be canvas -- each descriptor_tup section is a pair of the form ['ulx,uly,lrx,lry',fill]. -- This should return the first and the last oval drawn fnco := (fn := full_name()) + " create oval "; -- prefix for first part of call fnic := fn + " itemconfigure "; -- prefix for second part of call txt := fnco + join(breakup(descriptor_tup(1)(1),",")," "); -- set up to create the first oval, whose Tk serial number will be needed res := tk_kall(txt); item_num := first_num := unstr(res) - 1; -- get the number of the first item created txt := "" +/ [if j = 1 then "" else fnco + join(breakup(koords + "\n",",")," ") end if + fnic + str(item_num +:= 1) + " -fill " + color + "\n": [koords,color] = descriptor_tup(j)]; --print("draw_ovals: ",txt); res := tk_kall(txt); return [item_from_itemno(self,first_num),item_from_itemno(self,item_num)]; -- the first and the last oval drawn end draw_ovals; procedure delete_items(tags_or_ids); -- remove the item(s) identified by an id or tag txt := full_name() + " delete " +/ ["\"" + tid + "\" ": tid in chop(tags_or_ids)]; return tk_kall(txt); end delete_items; procedure deltag_if(iftag,tags_or_ids); -- remove the specified tag from the item(s) identified by an id or tag txt := full_name() + " dtag " + "\"" + iftag + "\" " +/ ["\"" + tid + "\" ": tid in chop(tags_or_ids)]; return tk_kall(txt); end deltag_if; procedure get_tagindex(tag,index); -- gets the value of an index in a tagged canvas text item if tk_type /= "canvas" then return "Not a canvas!"; end if; -- must be a canvas text item txt := full_name() + " index " + tag + " " + index; return unstr(tk_kall(txt)) + 1; end get_tagindex; procedure get_select(tag); -- gets the value of sel.first and sel.last in a tagged canvas text item if tk_type /= "canvas" then return "Not a canvas!"; end if; -- must be a canvas text item txt := full_name() + " index " + tag + " sel.first"; if (sf := tk_kall(txt))(1) = "s" then -- use the insert position if the selection is empty txt := full_name() + " index " + tag + " insert"; return [(res := unstr(tk_kall(txt))) + 1,res]; -- done with this case end if; txt := full_name() + " index " + tag + " sel.last"; sl := tk_kall(txt); return [unstr(sf) + 1,unstr(sl) + 1]; end get_select; procedure set_select(tag,i,j); -- sets the value of sel.first and sel.last in a tagged canvas text item if tk_type /= "canvas" then return "Not a canvas!"; end if; -- must be a canvas text item if j = i - 1 then -- clear the selection and set the insertion point txt := full_name() + " select clear"; tk_kall(txt); txt := full_name() + " icursor " + tag + " " + str(j); return tk_kall(txt); -- done with this case end if; txt := (fn := full_name()) + " select from " + tag + " " + str(i - 1); tk_kall(txt); txt := fn + " select to " + tag + " " + str(j - 1); tk_kall(txt); return tk_kall(txt); end set_select; procedure refocus(tag); -- sets the focus to a tagged canvas text item, or gets it if tag = OM if tk_type /= "canvas" then return "Not a canvas!"; end if; -- must be a canvas text item fn := full_name(); if tag = OM then return tk_kall(fn + " focus"); end if; -- in this case, read the focus -- res := tk_kall(txt1 := "focus " + fn); -- the canvas must get the focus,to assign it to a tag txt := fn + " focus " + tag; res := tk_kall(txt); return res; end refocus; procedure deltag(tags_or_ids); -- remove the specified tags from a canvas item for tid in chop(tags_or_ids) loop txt := parent.full_name() + " dtag " + name(2..) + " \"" + tid + "\""; tk_kall(txt); end loop; end deltag; procedure find_after(); -- find the item just above that with a given tag in the display list, -- or all items, or all enclosed in a given rectangle, or the -- nearest item to a given point, or to items which already have a given tag. txt := parent.full_name() + " find above " + name(2..); if (item_no := tk_kall(txt)) = "" then return OM; end if; return item_from_itemno(parent,item_no); end find_after; procedure find_before(); -- find the item just below that with a given tag in the display list, txt := parent.full_name() + " find below " + name(2..); if (item_no := tk_kall(txt)) = "" then return OM; end if; --print(txt," ",item_no); return OM; return item_from_itemno(parent,item_no); end find_before; procedure find_in(rect); -- find all the items enclosed in a given rectangle if rect = OM then txt := full_name() + " find all"; item_list := breakup(tk_kall(txt)," "); -- get the list of items return [item_from_itemno(self,item_no): item_no in item_list]; end if; rect := "" +/ [str(x) + " ": x in chop(rect)]; txt := full_name() + " find enclosed " + rect; item_list := breakup(tk_kall(txt)," "); -- get the list of items return [item_from_itemno(self,item_no): item_no in item_list]; end find_in; procedure find_touching(rect); -- find all the items touching in a given rectangle if rect = OM then txt := full_name() + " find all"; item_list := breakup(tk_kall(txt)," "); -- get the list of items return [item_from_itemno(self,item_no): item_no in item_list]; end if; rect := "" +/ [str(x) + " ": x in chop(rect)]; txt := full_name() + " find overlapping " + rect; item_list := breakup(tk_kall(txt)," "); -- get the list of items return [item_from_itemno(self,item_no): item_no in item_list]; end find_touching; procedure find_nearest(xy,halo,start); -- find the nearest item to a given point txt := full_name() + " find " + " closest " + str(i) + " " + str(j); if halo /= OM then txt +:= (" " + str(halo)); end if; if start /= OM then txt +:= (" " + str(start)); end if; item_no := tk_kall(txt); -- get the number of the item return item_from_itemno(self,item_no); -- convert the item number to an item end find_nearest; procedure find(tag); -- find all the items having a given tag txt := full_name() + " find withtag \"" + tag + "\""; item_list := breakup(tk_kall(txt)," "); -- get the list of items return [item_from_itemno(self,item_no): item_no in item_list]; end find; procedure item_from_itemno(parnt,item_no); -- convert an item number to an item new_item := tkw(); -- form a blank new (canvas or text) item new_item.parent := parnt; -- set the parent new_item.tk_type := kind_from_config(parnt,item_no := str(item_no)); -- find its type new_item.name := "c" + item_no; -- note its serial number (prefixing a 'c') return new_item; end item_from_itemno; procedure kind_from_config(parnt,item_no); -- find the type of a canvas object from its configuration -- this routine examines the attributes of an object to determine its tk_type, using the following heuristic: -- an arc has 'extent'; a window has 'window'; an image has 'image'; a bitmap has 'bitmap'; a line has -- 'arrow' and 'smooth'; a polygon has 'smooth' but not 'arrow'; text has 'font'. the other two cases are -- oval and rectangle, which we distinguish using tags assigned when created. txt := parnt.full_name() + " itemconfigure " + item_no; info := tk_kall(txt); -- get the configuration info key_att := find_wds(info,key_attributes)(2); --print("kind_from_config: ",info," ",item_no); case key_att when "extent" => return "arc"; when "window" => return "widget"; when "image" => return "image"; when "bitmap" => return "bitmap"; when "font" => return "canvas_text"; when "smooth" => return if #find_wds(info,["arrow"]) = 0 then "polygon" else "line" end if; otherwise => info := tk_kall(txt + " -tags"); return if #find_wds(info,["OVAL"]) > 0 then "oval" else "rectangle" end if; end case; end kind_from_config; procedure find_wds(in_stg,wd_list); -- find the first word in wd_list which occurs in wd_list, if any -- return the pair [location,wd]; or [] if none fronts := "" +/domain(wff := {[wd(1),wd(2..)]: wd in wd_list | #wd > 0}); loc := 1; -- we keep track of our location in the following scan while in_stg /= "" loop skipped := break(in_stg,fronts); -- advance to the next significant character fc := any(in_stg,fronts); -- find what it is loc +:= # skipped; -- note number of characters bypassed if fc = "" then exit; end if; -- exit if at end for wd_tail in wff{fc} loop -- check to see if we have one of the words sought if match(in_stg,wd_tail) /= "" then -- found what we want return [loc,fc + wd_tail]; end if; loc +:= 1; -- note one more character bypassed end loop; end loop; return []; -- otherwise not found end find_wds; procedure focus(); -- return widget in win which has the focus txt := "focus -displayof " + full_name(); return obj_from_tkname(tk_kall(txt)); end focus; procedure focus_in_top(); -- return widget in master window which has the focus txt := "focus -lastfor " + full_name(); return obj_from_tkname(tk_kall(txt)); end focus_in_top; procedure get_focus(); -- set focus to this window txt := "focus -force " + full_name(); return tk_kall(txt); end get_focus; procedure grab_focus(x); -- grab the (modal) focus; x can be OM or "global" txt := "grab " + if x = OM then "" else "-global " end if + full_name(); return tk_kall(txt); end grab_focus; procedure release_focus(); -- release the (modal) focus txt := "grab release " + full_name(); return tk_kall(txt); end release_focus; procedure read_grab(); -- determine the modal grab state of this window: none, local, or global txt := "grab status " + full_name(); return tk_kall(txt); end read_grab; procedure grabber(); -- return window which has exerted a grab txt := "grab current " + full_name(); return tk_kall(txt); end grabber; procedure destroy(); -- destroy a widget txt := "destroy " + full_name(); --print("destroy: ",txt); return tk_kall(txt); end destroy; procedure wait(); -- wait for this window to open txt := "tkwait visibility"; return tk_kall(txt); end wait; procedure wait_close(); -- wait for this window to be destroyed txt := "tkwait window"; return tk_kall(txt); end wait_close; procedure waitvar(name); -- wait for the specified tk variable to change txt := ""; return tk_kall(txt); end waitvar; procedure index_item(ix_key); -- get numerical value of index_key, which can be active, end, last, etc. txt := parent.full_name() + " index \"" + name(2..) + "\" " + ix_key; return tk_kall(txt); end index_item; procedure lower_tid(tag_or_id,be); -- lower the item identified by an id or tag either to specified level, -- or to the start of the display list txt := full_name() + " lower \"" + tag_or_id + "\" " + if be = OM then "" else "\"" + be + "\"" end if; return tk_kall(txt); end lower_tid; procedure move(tag_or_id,dx,dy); -- move the item(s) identified by an id or tag, a specified amount txt := full_name() + " move \"" + tag_or_id + "\" " + str(dx) + " " + str(dy); return tk_kall(txt); end move; procedure postscript(options); -- generate postscript for the contents of a canvas. See below for options available cfn := full_name(); txt := cfn + " postscript " + handle_ps_options(options,cfn); return tk_kall(txt); end postscript; procedure handle_ps_options(options,cfn); -- convert comma-delimited postscript options into tk form -- the postscript options available are" colormap (map from color indices into colors), -- colormode (color, gray, or mono), file (file_name), height (of area to print), width (of area to print), -- x (left of area to print), y (top of area to print), rotate (true if paper should be turned 90 degrees), -- fontmap (map from X font names into Postscript fonts and sizes), pageheight (of output area), -- pagewidth (of output area), pagex (left of output area), pagey (top of output area), -- pageanchor (c, n,e,s, w, ne, se, nw, or sw); point from which output area offset is measured options := breakup(options?"",",;"); -- break into list option_names := {option: option = options(j) | odd(j)}; options_string := ""; if "width" notin option_names then options_string +:= " -width " + tk_kall(cfn + " cget -width"); end if; if "height" notin option_names then options_string +:= " -height " + tk_kall(cfn + " cget -height"); end if; return options_string +/[if odd(j) then " -" else " " end if + option: option = options(j)]; end handle_ps_options; procedure raise_tid(tag_or_id,ab); -- raise the item identified by an id or tag either to speicified level, -- or to the end of the display list txt := full_name() + " raise \"" + tag_or_id + "\" " + if be = OM then "" else "\"" + ab + "\"" end if; return tk_kall(txt); end raise_tid; procedure scale_item(cent_x,cent_y,amt_x,amt_y); -- scale the item(s) identified by an id or tag, by a specified amount -- about a specified center txt := parent.full_name() + " scale \"" + name(2..) + "\" " + str(cent_x) + " " + str(cent_y) + " " + str(amt_x) + " " + str(amt_y); return tk_kall(txt); end scale_item; procedure scan_mark(x,y); -- place mark indicating scroll position??? txt := full_name() + " scan mark " + str(x) + " " + str(y); return tk_kall(txt); end scan_mark; procedure scan_to(x,y); -- scroll to indicated position txt := full_name() + " scan dragto " + str(x) + " " + str(y); return tk_kall(txt); end scan_to; procedure scan_mark_1(x); -- place mark indicating scroll position txt := full_name() + " scan mark " + str(x); return tk_kall(txt); end scan_mark_1; procedure scan_to_1(x); -- scroll to indicated position txt := full_name() + " scan dragto " + str(x); return tk_kall(txt); end scan_to_1; procedure canvas_select(); -- ??? txt := ""; return tk_kall(txt); end canvas_select; procedure xview_percent(p); -- move to place fraction p of string offscreen to the left; p is real txt := full_name() + " xview moveto " + str(p); return tk_kall(txt); end xview_percent; procedure yview_percent(p); -- move to place fraction p of string offscreen to the top; p is real txt := full_name() + " yview moveto " + str(p); return tk_kall(txt); end yview_percent; procedure xview_scroll(n,what); -- scroll horizontally, n 'units' or 'pages' txt := full_name() + " xview scroll " + str(n) + " " + (what?"units"); -- 'what' can be 'units' or 'pages' return tk_kall(txt); end xview_scroll; procedure yview_scroll(n,what); -- scroll vertically, n 'units' or 'pages' txt := full_name() + " yview scroll " + str(n) + " " + (what?"units"); -- 'what' can be 'units' or 'pages' return tk_kall(txt); end yview_scroll; procedure image_of(rect); -- capture the contents of a rectangle within a canvas, as a Tk absolute image if rect = OM then -- no rectangle img_name := tk_kall("image create mimage -canvas " + full_name()); -- call Tk to create the image else [l,t,r,b] := rect; -- unpack txt := "image create mimage -canvas " + full_name() + " "; txt +:= "-canvas_x " + str(l) + " -canvas_y " + str(t) + " -canvas_width " + str(r - l) + " -canvas_height " + str(b - t); img_name := tk_kall(txt); end if; --print("orig image_name and info: ",img_name," ",tk_kall(img_name + " cget -format")); photo_img_name := tk_kall("image create photo -height 100 -width 100 "); print("photo_img_name: ",photo_img_name); res := tk_kall(photo_img_name + " copy " + img_name); print("photo_img_name res: ",res); new_image := tkw(); -- form a blank new object new_image.parent := OM; -- images have no parent new_image.tk_type := "image"; -- note its type new_image.name := photo_img_name; -- note its name, which we make unique return new_image; end image_of; -- ****** Text Widget Operations ****** procedure compare(op,ix1,ix2); -- compare character indices in line.char and other allowed formats -- op may be "==", ">", "!=", etc. op := if op = "=" then "==" elseif op = "/=" then "!=" else op end if; txt := full_name() + " compare {" + ix1 + "} " + op + " {" + ix2 + "}"; return tk_kall(txt) = "1"; end compare; procedure debug(on_off); -- enable consistency checking for B-tree code??? txt := full_name() + " debug " + if on_off = OM then "false" else on_off end if; return tk_kall(txt); end debug; procedure insert_tt(n,chars_and_tags); -- insert a substring; this can carry specified tags in designated subsections chars_and_tags := breakup(chars_and_tags,"`"); txt := full_name() + " insert " + str(n); for ct = chars_and_tags(j) loop -- build the sanitized string of chars and tags txt +:= if odd(j) then " \"" + stg_to_Tk(ct) + "\"" else " {" + join(breakup(ct,",")," ") + "}" end if; end loop; --print("insert_tt: ",txt); return tk_kall(txt); end insert_tt; procedure linebox(n); -- return bounding box and baseline of line n txt := full_name() + " dlineinfo " + str(n) + ".0"; data := [unstr(x): x in breakup(tk_kall(txt)," ")]; [l,t,w,h] := data; return [[l,t,l + w,t + h],data(5)]; end linebox; procedure insert_image(n,img); -- insert an image at a specified text position txt := full_name() + " image create " + n + " -image " + img.name; --print("insert_image: ",txt,img); return tk_kall(txt); end insert_image; procedure handle_image_options(options_values); -- convert comma-delimited image options into tk form -- options are align (), image (), name (), padx (), pady () return "" +/ [if odd(j) then "-" else "\"" end if + if is_string(ov) then ov else ov.name end if -- if the value is an image, use its name + if odd(j) then " " else "\" " end if: ov = options_values(j)]; end handle_image_options; procedure images(); -- return the ordered list of all images in a text widget txt := full_name() + " image names"; img_list := breakup(tk_kall(txt)," "); img_set := { }; for img_name in img_list loop if "#" in img_name then rbreak(img_name,"#"); rmatch(img_name,"#"); end if; img_set with:= img_name; end loop; img_list := [ ]; for img_name in img_set loop new_image := tkw(); -- form a blank new object new_image.parent := OM; -- images have no parent new_image.tk_type := if #img_name > 3 and img_name(1..4) = "XBM`" then "bitmap" else "image" end if; -- note its type new_image.name := img_name; img_list with:= new_image; end loop; return img_list; end images; procedure index(ix_stg); -- return character position of specified text index -- this operation also applies to menus, entries, and listboxes -- text indices can be "current" (char under mouse), "end", "insert" (insert position), -- line.char, image (name), widget (name), mark (stg), tag_name.first, tag_name.last, case tk_type when "listbox","menu" => if ix_stg = "sel.anchor" then -- add 1 except for "sel.last" and "end" txt := full_name() + " index anchor"; return unstr(tk_kall(txt)) + 1; else txt := full_name() + " index " + ix_stg; return unstr(tk_kall(txt)) + 1; end if; when "entry" => if is_integer(ix_stg) then -- here we get a bit position txt := full_name() + " index @" + str(abs(ix_stg)); ix := tk_kall(txt); return unstr(ix) + 1; elseif ix_stg = "sel.last" then -- add 1 except for "sel.last" and "end" if tk_kall(full_name() + " select present") = "0" then return OM; end if; txt := full_name() + " index " + ix_stg; return unstr(tk_kall(txt)); elseif ix_stg = "sel.first" then -- add 1 except for "sel.last" and "end" if tk_kall(full_name() + " select present") = "0" then return OM; end if; txt := full_name() + " index " + ix_stg; return unstr(tk_kall(txt)) + 1; elseif ix_stg = "sel.anchor" then -- add 1 except for "sel.last" and "end" if tk_kall(full_name() + " select present") = "0" then return OM; end if; txt := full_name() + " index anchor"; return unstr(tk_kall(txt)) + 1; elseif ix_stg = "end" then -- add 1 except for "sel.last" and "end" txt := full_name() + " index " + ix_stg; return unstr(tk_kall(txt)); else txt := full_name() + " index " + ix_stg; return unstr(tk_kall(txt)) + 1; end if; when "text" => -- index position in a text widget if is_integer(ix_stg) then -- here we get a bit position txt := full_name() + " index @" + str(abs(ix_stg)); ix := tk_kall(txt); return adjust_text_ix(ix); elseif ix_stg = "sel.last" then -- add 1 except for "sel.last" and "end" if tk_kall(full_name() + " select present") = "0" then return OM; end if; txt := full_name() + " index " + ix_stg; return tk_kall(txt); elseif ix_stg = "sel.first" then -- add 1 except for "sel.last" and "end" if tk_kall(full_name() + " select present") = "0" then return OM; end if; txt := full_name() + " index " + ix_stg; return tk_kall(txt) + 1; elseif ix_stg = "end" then -- add 1 except for "sel.last" and "end" txt := full_name() + " index " + ix_stg; return tk_kall(txt); else txt := full_name() + " index " + ix_stg; return adjust_text_ix(tk_kall(txt)); end if; otherwise => txt := full_name() + " index \"" + ix_stg + "\""; return tk_kall(txt); end case; end index; procedure mark_set(name,n); -- place a named mark at the specified index txt := full_name() + " mark set \"" + name + "\" \"" + str(n) + "\""; return tk_kall(txt); end mark_set; procedure mark_unset(name); -- remove a named mark (can also be comma-separated list) txt := full_name() + " mark unset \"" + name + "\""; return tk_kall(txt); end mark_unset; procedure mark_gravity(name,n); -- set the 'gravity' (left,right) of a mark, which determines the placement of strings inserted at the mark txt := full_name() + " mark gravity \"" + name + "\" " + str(n); return tk_kall(txt); end mark_gravity; procedure mark_next(n); -- return the first mark after text position n txt := full_name() + " mark next \"" + str(n) + "\""; return tk_kall(txt); end mark_next; procedure mark_prev(n); -- return the last mark before text position n txt := full_name() + " mark previous \"" + str(n) + "\""; return tk_kall(txt); end mark_prev; procedure search(options,pattern,n,m); -- string search; returns empty string if unsuccessful -- search section of text frm m to n for a pattern. 'options' parameter can be -- "forward", "backward", "nocase", "count" (return count of matched characters) -- "exact", "regexp" (use regular expression matching) -- unless regular expression ,matching is specified, the 'pattern' is -- only allowed to have "*" (wildcard), ? (one char), or [abc] specified chars txt := full_name() + " search " + handle_search_options(options) + " -- \"" + stg_to_Tk(pattern) + "\" " + if n = OM then "1.0" else str(n) end if + " " + if m = OM then "" else " " + str(m) end if; return (the_start := adjust_text_ix(tk_kall(txt))) + if "count" notin chop(options) then "" else "," + the_start + "+" + str(unstr(getvar("```")) - 1) + "char" end if; end search; procedure handle_search_options(options); -- handle possible options for search if options = OM then return ""; end if; return "" +/ [if "count" = option then "-count ``` " else "-" + option + " " end if: option in chop(options)]; end handle_search_options; procedure see(n); -- scroll to make a given line.character position n visible txt := full_name() + " see \"" + str(n) + "\""; return tk_kall(txt); end see; procedure tag_add(tag,index_range_stg); -- add tag to a list of character ranges txt := full_name() + " tag add " + tag + " " + handle_index_range(index_range_stg); return tk_kall(txt); end tag_add; procedure tag_add_no_offs(tag,range_start,range_end); txt := full_name() + " tag add " + tag + " " + range_start + " " + range_end; return tk_kall(txt); end tag_add_no_offs; procedure tag_remove(tag,index_range_stg); -- remove tag from list of text ranges txt := full_name() + " tag remove " + tag + " " + handle_index_range(index_range_stg); return tk_kall(txt); end tag_remove; procedure handle_index_range(index_range_stg); -- handle list of index ranges for tag addition ch_ixrs := chop(index_range_stg); return "" +/ [if odd(j) then normalize_text_index(rg) else "{" + rg?"end" + "}" end if + " ": rg = ch_ixrs(j)]; end handle_index_range; procedure chop(stg); -- chop at semis or commas if is string return if not is_string(stg) then stg else breakup(stg,if ";" in stg then ";" else "," end if) end if; end chop; -- procedure tag_delete(tag_list); -- delete information for list of tags [txt("tags") := list;] -- txt := full_name() + " tag delete " + handle_tag_list(tag_list); -- return tk_kall(txt); -- end tag_delete; procedure handle_tag_list(tag_list); -- handle list of index tags for tag addition tags := chop(tag_list); return "" +/ ["{" + tag + "} ": tag in tags]; end handle_tag_list; procedure tag_names(n); -- return ordered list of tags at specified char position. OM gives all txt := full_name() + " tag names " + normalize_text_index(n); return breakup(tk_kall(txt)," "); end tag_names; procedure normalize_text_index(n); -- adjust a text index to 1-basing if n = OM then return ""; end if; if is_integer(n) then return "1." + str(n - 1); end if; first_digit := span(n,"0123456789"); dot := match(n,"."); second_digit := span(n,"0123456789"); if not (#first_digit > 0 and #dot > 0 and #second_digit > 0) then return "{" + n + "}"; end if; return "{" + first_digit + dot + str(unstr(second_digit) - 1) + n + "}"; end normalize_text_index; procedure adjust_text_ix(ix); -- adjust the character number to 1-basing if ix = OM or ix ="" then return ix; end if; [first_digits,second_digits] := breakup(ix,"."); return first_digits + "." + str(unstr(second_digits) + 1); end adjust_text_ix; -- procedure tag_lower(tag,below); -- lower tag to specified position in priority list of tags, or to start -- txt := full_name() + " tag lower \"" + str(tag) + "\"" + -- if below = OM then "" else " \"" + below + "\"" end if; -- return tk_kall(txt); -- end tag_lower; -- procedure tag_raise(tag,above); -- raise tag to specified position in priority list of tags, or to end -- txt := full_name() + " tag raise \"" + str(tag) + "\"" + -- if below = OM then "" else " \"" + above + "\"" end if; -- return tk_kall(txt); -- end tag_raise; procedure tag_nextrange(tag,n,m); -- search for first subrange of specified range that carries specified tag txt := full_name() + " tag nextrange \"" + str(tag) + "\" "+ str(n) + " " + if m = OM then "" else " " + str(m) end if; [ix1,ix2] := breakup(tk_kall(txt)," "); return [adjust_text_ix(ix1),adjust_text_ix(ix2)]; end tag_nextrange; procedure tag_prevrange(tag,n,m); -- search for last subrange of specified range that carries specified tag txt := full_name() + " tag prevrange \"" + str(tag) + "\" "+ str(m) + " " + if n = OM then "" else " " + str(n) end if; [ix1,ix2] := breakup(tk_kall(txt)," "); return [adjust_text_ix(ix1),adjust_text_ix(ix2)]; end tag_prevrange; procedure tag_ranges(tag); -- get list of all ranges for specified tag if tag_prevrange(tag,"1.0","end") = [] then return []; end if; -- tag does not occur ranges := []; first_now := "1.0"; -- will collect while (nrange := tag_nextrange(tag,first_now,"end")) /= [] loop [nr1,nr2] := nrange; ranges with:= nr1; cpos := rbreak(nr2,"."); ranges with:= (nr2 + str(unstr(cpos) - 1)); first_now := nrange(2); end loop; return ranges; end tag_ranges; procedure insert_widget(n,wind); -- insert an widget window at a specified text position txt := full_name() + " window create " + n + " -window " + wind.full_name(); --print("insert_widget: ",txt); return tk_kall(txt); end insert_widget; procedure handle_options_and_values(options_values); -- convert comma-delimited widget options into tk form -- options are align (), window (), stretch (), padx (), pady (), command () return "" +/ [if odd(j) then "-" else "{" end if + if is_string(ov) then ov else ov.full_name() end if -- if the value is a widget, use its name + if odd(j) then " " else "} " end if: ov = options_values(j)]; end handle_options_and_values; -- ****** Button Operations ****** procedure flash(); -- cause the button to flash txt := full_name() + " flash"; return tk_kall(txt); end flash; procedure invoke_button(); -- trigger the button's action txt := full_name() + " invoke"; return tk_kall(txt); end invoke_button; procedure select_button(); -- select radio button or checkbutton txt := full_name() + " select"; return tk_kall(txt); end select_button; -- ****** Menu Operations ****** procedure clone(); -- make linked copy of the menu (for tearoffs, etc.) txt := full_name() + " clone"; return tk_kall(txt); end clone; procedure invoke(n); -- trigger the entry's action; note that invoke(0) does a tearoff txt := full_name() + " invoke " + str(n); return tk_kall(txt); end invoke; procedure post(i,j); -- display menu at specified coordinates txt := full_name() + " post " + str(i) + " " + str(j); return tk_kall(txt); end post; procedure postcascade(n); -- display menu in hierarchical position for entry n txt := full_name() + " postcascade \"" + str(n) + "\""; return tk_kall(txt); end postcascade; procedure popup(i,j); -- display menu at specified coordinates txt := "tk_popup " + full_name() + " " + str(i) + " " + str(j); return tk_kall(txt); end popup; procedure entry_type(n); -- get the type of menu entry n txt := full_name() + " type \"" + str(n) + "\""; return tk_kall(txt); end entry_type; procedure unpost(); -- hide the menu txt := full_name() + " unpost"; return tk_kall(txt); end unpost; procedure yposition(n); -- return vertical position of top of entry n txt := full_name() + " yposition \"" + str(n) + "\""; return tk_kall(txt); end yposition; -- ****** Scale Operations ****** procedure coords(n); -- transform scale value into geometric position txt := full_name() + " coords \"" + str(n) + "\""; return [unstr(x): x in breakup(tk_kall(txt)," ")]; -- return a tuple end coords; procedure get(ij); -- get scale value, or value corresponding to given position is_horizontal := tk_kall(full_name() + " cget -orient") = "horizontal"; txt := full_name() + " get " + join(breakup(ij,",;")," "); return unstr(tk_kall(txt)); end get; procedure identify(ij); -- return 'trough', 'slider', or 'trough2': feature under indicated point txt := full_name() + " identify " +join(breakup(ij,",;")," "); return tk_kall(txt); end identify; -- ****** Scrollbar Operations ****** procedure activate(x); -- query/set active element, which can be arrow1, arrow2, or slider txt := full_name() + " activate " + x; return tk_kall(txt); end activate; procedure delta(dxy); -- convert desired horizontal or vertical value change to slider units is_vertical := tk_kall(full_name() + " cget -orient") = "vertical"; piece := if is_vertical then "0 " + str(dxy) else str(dxy) + " 0" end if; txt := full_name() + " delta " + piece; return tk_kall(txt); end delta; procedure fraction(x); -- convert point position into fraction relative to scrollbar extent is_vertical := tk_kall(full_name() + " cget -orient") = "vertical"; --print("is_vertical",is_vertical); piece := if is_vertical then "0 " + str(x) else str(x) + " 0" end if; txt := full_name() + " fraction " + piece; return tk_kall(txt); end fraction; -- ****** Entry Operations ****** procedure bbox(n); -- return bounding box of specified character in text or entry, or line in listbox txt := full_name() + " bbox " + if tk_type = "entry" then str(n + 1) else str(n) end if; --print("bbox(n): ",txt," ",tk_kall(txt)); -- some problem for listboxes [l,t,w,h] := [unstr(x): x in breakup(tk_kall(txt)," ")]; return [l,t,w + l,h + t]; end bbox; procedure select(m,n); -- select characters m to n, or clear the selection (this also works for listboxes) if m = OM and n = OM then txt := full_name() + " select clear"; return tk_kall(txt); end if; if tk_type = "listbox" then --listbox range selection n := if is_integer(n) then (n - 1) else "\"" + n + "\"" end if; txt := full_name() + " select set \"" + (m - 1) + "\" " + n; else -- character range selection n := if n = OM then "" else "\"" + n + "\"" end if; txt := full_name() + " select range \"" + m + "\" " + n; end if; return tk_kall(txt); end select; procedure select_anchor(m); -- set the anchor point for the selection txt := full_name() + " select from \""+ str(m) + "\""; return tk_kall(txt); end select_anchor; -- ****** Rastport Operations ****** procedure put_img(gr_img,x,y); -- stuff gr_img into tkrport at position x, y tk_gr_put(interp,full_name(),gr_img.native_im(),x,y); end put_img; procedure put_add(gr_img,x,y); -- stuff gr_img into tkrport using 'sum' tk_gr_put_add(interp,full_name(),gr_img.native_im(),x,y); end put_add; procedure put_dif(gr_img,x,y); -- stuff gr_img into tkrport using 'dif' tk_gr_put_dif(interp,full_name(),gr_img.native_im(),x,y); end put_dif; procedure put_mul(gr_img,x,y); -- stuff gr_img into tkrport using 'mul' tk_gr_put_mul(interp,full_name(),gr_img.native_im(),x,y); end put_mul; procedure put_div(gr_img,x,y); -- stuff gr_img into tkrport using 'div' tk_gr_put_div(interp,full_name(),gr_img.native_im(),x,y); end put_div; procedure put_min(gr_img,x,y); -- stuff gr_img into tkrport using 'min' tk_gr_put_min(interp,full_name(),gr_img.native_im(),x,y); end put_min; procedure put_max(gr_img,x,y); -- stuff gr_img into tkrport using 'max' tk_gr_put_max(interp,full_name(),gr_img.native_im(),x,y); end put_max; procedure put_pow(gr_img,x,y); -- stuff gr_img into tkrport using 'pow' tk_gr_put_pow(interp,full_name(),gr_img.native_im(),x,y); end put_pow; procedure put_blend(gr_img,x,y,c1,c2); -- blend the image gr_img with the tk widget at position x, y using coefficients c1 and c2 tk_gr_put_blend(interp,full_name(),gr_img.native_im(),x,y,c1,c2); end put_blend; procedure put_imgr(gr_img,x,y); -- stuff gr_img into tkrport at position x, y tk_gr_put_and_rotate(interp,full_name(),gr_img.native_im(),x,y); end put_imgr; procedure put_addr(gr_img,x,y); -- stuff gr_img into tkrport using 'sum' tk_gr_put_add_and_rotate(interp,full_name(),gr_img.native_im(),x,y); end put_addr; procedure put_difr(gr_img,x,y); -- stuff gr_img into tkrport using 'dif' tk_gr_put_dif_and_rotate(interp,full_name(),gr_img.native_im(),x,y); end put_difr; procedure put_mulr(gr_img,x,y); -- stuff gr_img into tkrport using 'mul' tk_gr_put_mul_and_rotate(interp,full_name(),gr_img.native_im(),x,y); end put_mulr; procedure put_divr(gr_img,x,y); -- stuff gr_img into tkrport using 'div' tk_gr_put_div_and_rotate(interp,full_name(),gr_img.native_im(),x,y); end put_divr; procedure put_minr(gr_img,x,y); -- stuff gr_img into tkrport using 'min' tk_gr_put_min_and_rotate(interp,full_name(),gr_img.native_im(),x,y); end put_minr; procedure put_maxr(gr_img,x,y); -- stuff gr_img into tkrport using 'max' tk_gr_put_max_and_rotate(interp,full_name(),gr_img.native_im(),x,y); end put_maxr; procedure put_powr(gr_img,x,y); -- stuff gr_img into tkrport using 'pow' tk_gr_put_pow_and_rotate(interp,full_name(),gr_img.native_im(),x,y); end put_powr; procedure put_blendr(gr_img,x,y,c1,c2); -- blend the image gr_img with the tk widget at position x, y using coefficients c1 and c2 tk_gr_put_blend_and_rotate(interp,full_name(),gr_img.native_im(),x,y,c1,c2); end put_blendr; procedure get_img(gr_img,x,y); -- stuff gr_img into tkrport at position x, y x := tk_gr_get(interp,full_name(),gr_img.native_im(),x,y); end get_img; procedure get_add(gr_img,x,y); -- stuff gr_img into tkrport using 'sum' tk_gr_get_add(interp,full_name(),gr_img.native_im(),x,y); end get_add; procedure get_dif(gr_img,x,y); -- stuff gr_img into tkrport using 'dif' tk_gr_get_dif(interp,full_name(),gr_img.native_im(),x,y); end get_dif; procedure get_mul(gr_img,x,y); -- stuff gr_img into tkrport using 'mul' tk_gr_get_mul(interp,full_name(),gr_img.native_im(),x,y); end get_mul; procedure get_div(gr_img,x,y); -- stuff gr_img into tkrport using 'div' tk_gr_get_div(interp,full_name(),gr_img.native_im(),x,y); end get_div; procedure get_min(gr_img,x,y); -- stuff gr_img into tkrport using 'min' tk_gr_get_min(interp,full_name(),gr_img.native_im(),x,y); end get_min; procedure get_max(gr_img,x,y); -- stuff gr_img into tkrport using 'max' tk_gr_get_max(interp,full_name(),gr_img.native_im(),x,y); end get_max; procedure get_blend(gr_img,x,y,c1,c2); -- blend the image gr_img with the tk widget at position x, y using coefficients c1 and c2 tk_gr_get_blend(interp,full_name(),gr_img.native_im(),x,y,c1,c2); end get_blend; -- ****** Other Operations using standard SETL syntax****** procedure #self; -- various size-related operations case tk_type -- this operation is implemented differently for different types of widgets when "listbox","entry","menu" => txt := full_name() + " index end"; return unstr(tk_kall(txt)) + 1; when "text" => las := tk_kall(full_name() + " index end"); rbreak(las,"."); rmatch(las,"."); return if las = "2" and #tk_kall(full_name() + " get 1.0 {1.0 lineend}") = 0 then 0 else unstr(las) - 1 end if; when "canvas_text" => las := tk_kall(txt := parent.full_name() + " index " + name(2..) + " end"); --print("canvas_text: ",txt); return unstr(las) + 1; end case; end; procedure self(i..j); -- various extraction operations -- this operation is used to extract contiguous ranges in various situations. -- text widgets: retrieves a range of characters; absolute images: retrieves a rectangle -- listboxes: retrieves a range of lines case tk_type -- this operation is implemented differently for different types of widgets when "listbox" => -- return the range of list entries, as a blank-delimited string -- offset indices to be 1-based, as in SETL txt := full_name() + " get " + str(i - 1) + " " + str(j - 1); return stgs_from_Tk(tk_kall(txt)); -- break into list and return when "text" => -- return span of characters from text txt := full_name() + " get " + normalize_text_index(i) + " " + str(j); when "canvas_text" => -- return span of characters from canvas_text item txt := parent.full_name() + " itemcget " + name(2..) + " -text"; stg := tk_kall(txt); return stg(i..j); when "entry" => -- return span of characters from entry if is_string(i) then i := unstr(i); end if; if is_string(j) then j := unstr(j); end if; cont := tk_kall(full_name() + " get"); return if j = OM then cont(i..) else cont(i..j) end if; when "menu" => -- return the range of menu labels, as a tuple return [tk_kall(full_name() + " entrycget " + str(k) + " -label"): k in [i..j]]; when "image" => -- return an image subrectangle if parent /= OM then -- not an absolute image abort("Subimage extraction is only available for absolute images."); end if; i := chop(i); j := chop(j); if not is_tuple(i) or #i /= 2 then abort("Illegal first argument " + str(i)); end if; if not is_tuple(j) or #j /= 2 then abort("Illegal second argument " + str(j)); end if; [i1,i2] := i; [j1,j2] := j; i1 := str(i1); i2 := str(i2); j1 := str(j1); j2 := str(j2); new_im := tkw(); -- create a new image new_im.name := imnm := "`" + str(name_ctr +:= 1); -- assign name (with no originating file) new_im.tk_type := "image"; -- tk_type is "image" -- now copy the designated subrectangle into the image txt := "image create photo " + imnm; tk_kall(txt); -- create the Tk image txt := imnm + " copy " + name + " -from " + i1 + " " + i2 + " " + j1 + " " + j2; -- copy the designated subrectangle tk_kall(txt); return new_im; end case; return tk_kall(txt); end; procedure self(i..j) := y; -- various insertion and deletion operations -- this operation is used to insert contiguous ranges of items in various situations: -- text widgets: inserts a range of characters -- listboxes: insets a range of lines if is_string(y) and tk_type /= "text" and tk_type /= "canvas_text" and tk_type /= "entry" then -- convert to tuple y := chop(y); end if; case tk_type -- this operation is implemented differently for different types of widgets when "listbox" => -- offset indices to be 1-based, as in SETL listbox_len := tk_kall(full_name() + " index end"); -- get the length of the listbox j min:= unstr(listbox_len); -- constrain the end of the deletion range if j >= i then -- first delete the items in the range txt := full_name() + " delete " + str(i - 1) + " " + str(j - 1); tk_kall(txt); -- execute the deletion operation end if; -- now insert the string of labels after position i txt := full_name() + " insert " + str(i - 1) + " " +/ ["\"" + str(item) + "\" ": item in y]; when "text" => -- write span of characters to text j ?:= "end"; -- first delete the specified characters txt := full_name() + " delete " + (ip := normalize_text_index(i)) + " " + str(j); tk_kall(txt); -- execute the deletion operation -- now insert the string y into their place. txt := full_name() + " insert " + ip + " \"" + stg_to_Tk(str(y)) + "\""; when "canvas_text" => -- write span of characters to canvas_text item istg := str(i - 1); jstg := str(j - 1); if j >= i then -- first delete the characters to be over_written txt := parent.full_name() + " dchars " + name(2..) + " " + istg + " " + jstg; tk_kall(txt); -- execute the deletion operation end if; -- now insert the string y into their place. txt := parent.full_name() + " insert " + name(2..) + " " + istg + " \"" + stg_to_Tk(str(y)) + "\""; when "entry" => -- return span of characters from entry j ?:= "end"; -- first delete the specified characters txt := full_name() + " delete " + (ip := str(if is_string(i) then unstr(i) else i end if - 1)) + " " + str(j); tk_kall(txt); -- execute the deletion operation -- now insert the string y into their place. txt := full_name() + " insert " + ip + " \"" + stg_to_Tk(str(y)) + "\""; when "menu" => -- insert a range of menu labels menu_len := tk_kall(full_name() + " index end"); -- get the length of the menu j min:= unstr(menu_len); -- constrain the end of the deletion range menu_name := full_name(); if j >= i then -- first delete the items in the range txt := menu_name + " delete " + str(i) + " " + str(j); tk_kall(txt); -- execute the deletion operation end if; insert_menu_items(i,menu_name,y); return OM; -- done with this case when "image" => -- insert an image subrectangle if parent /= OM then -- not an absolute image abort("Subimage insertion is only available for absolute images."); end if; i := chop(i); j := chop(j); if not is_tuple(i) or #i /= 2 then abort("Illegal first argument " + str(i)); end if; if not is_tuple(j) or #j /= 2 then abort("Illegal second argument " + str(j)); end if; [i1,i2] := i; [j1,j2] := j; i1 := str(i1); i2 := str(i2); j1 := str(j1); j2 := str(j2); --print("insert an image subrectangle y: ",y); if type(y) /= "TKW" or y.tk_type /= "image" or y.parent /= OM then abort("Right-hand argument must be an absolute image"); -- the y argument end if; -- now copy the image into the designated subrectangle txt := name + " copy " + y.name + " -to " + i1 + " " + i2 + " " + j1 + " " + j2; end case; tk_kall(txt); end; procedure insert_menu_items(pt,menu_name,desc_lis); -- construct and insert the items of a menu from a descriptor pt -:= 1; spt := str(pt); items := breakup(desc_lis,":"); for j in [#items,#items - 1..1] loop [kind,lab] := items(j); if lab = OM then -- should be separator or tearoff if kind = "t" then -- tearoff tk_kall(menu_name + " insert " + spt + " tearoff"); else -- take as separator tk_kall(menu_name + " insert " + spt + " separator"); end if; else case kind when "c" => -- checkbutton item tk_kall(menu_name + " insert " + spt + " check -label " + lab); when "r" => -- radiobutton item tk_kall(menu_name + " insert " + spt + " radio -label " + lab + " -variable " + lab); when "s" => -- submenu item tk_kall(menu_name + " insert " + spt + " cascade -label " + lab); otherwise => -- take as button item tk_kall(menu_name + " insert " + spt + " command -label " + lab); end case; end if; end loop; end insert_menu_items; procedure self{event_des}; -- query binding to an event descriptor, for a widget, tag, or item item_name := OM; if is_tuple(event_des) then -- we are retrieving a binding for a tag or menu item --print("is_tuple event_des: ",event_des," ",tk_type); [item_name,event_des] := event_des; -- extract the item and the real event_des if item_name = "event" then return breakup(suppress_chars(tk_kall("event info " + if event_des = OM then "" else "<<" + event_des + ">>" end if),"")," "); -- return info on specific, or on all, virtual events elseif item_name = "bindings" then -- if this is not a canvas or text object, then return event binding list for binding tag -- otherwise return the event binding list for a canvas or text tag if tk_type = "text" then -- return the event binding list for a text tag res := breakup(tk_kall(full_name() + " tag bind " + event_des)," "); -- here event_des is actually the tag new_bindings := []; for x in res loop match(x,"<"); rmatch(x,">"); new_bindings with:= x; end loop; -- drop one set of angle brackets return new_bindings; elseif tk_type = "canvas" then -- return the event binding list for a tag on some canvas item or for a text tag res := breakup(tk_kall(full_name() + " bind " + event_des)," "); -- here event_des is actually the tag new_bindings := []; for x in res loop match(x,"<"); rmatch(x,">"); new_bindings with:= x; end loop; -- drop one set of angle brackets return new_bindings; else res := breakup(tk_kall("bind " + event_des)," "); -- here event_des is actually the tag --print("event binding list: ","bind " + event_des," ",res); new_bindings := []; for x in res loop match(x,"<"); rmatch(x,">"); new_bindings with:= x; end loop; -- drop one set of angle brackets return new_bindings; end if; elseif tk_type = "canvas" then return tk_kall(full_name() + " bind " + item_name + " <" + event_des + ">"); elseif tk_type = "text" then --print("text binding tag and event: ",full_name() + " bind " + item_name + " <" + event_des + ">"); return tk_kall(full_name() + " tag bind " + item_name + " <" + event_des + ">"); else -- return the Tk binding information for a specific binding tag and event. -- This covers cases like Tk("Text","") return tk_kall("bind " + item_name + " <" + event_des + ">"); end if; --->event elseif event_des = OM then -- retrieve the handler for the 'principal' event associated with the widget or item if (event_des := main_command(tk_type)) = OM then -- use the 'cget' option return tk_kall(full_name() + " cget -command"); end if; elseif event_des = "bindtags" then -- retrieve the list of binding tags for the widget return breakup(tk_kall("bindtags " + full_name())," "); -- convert to SETL list end if; -- else we are retrieving a binding for a widget, canvas item, or text item (using its id) if ":" in event_des then rbreak(event_des,":"); rmatch(event_des,":"); -- remove the parameter descriptor end if; event_des := chop(event_des); event_stg := "" +/["<" + item + ">": item in event_des]; -- convert event_stg to Tk format if item_name = OM then -- we are querying a binding for a widget, -- or for a canvas item or text item (using its id) if name = "" then return OM; end if; -- 'name' is the object name property if name(1) = "w" then -- querying binding for a widget txt := "bind " + full_name() + " " + event_stg; -- construct a Tk 'bind' instruction, which lists event fields to be passed back else -- querying binding for a canvas item, or text item (using its id) txt := parent.full_name() + " " + "bind " + name(2..) + " " + event_stg; end if; else -- we are querying a binding for a text tag, canvas tag, tag or menu item txt := "???"; end if; return tk_kall(txt); -- return the binding info end; procedure self{event_des} := y; -- set binding to an event descriptor, for a widget, tag, or menu item if tk_type = "socket" then return bind_socket_io_handler(event_des,y); end if; -- here event_des is ">" or "<" y_orig := y; -- save the original y y := chop(y); -- break into tuple if punctuated string if y = [] then y := ""; end if; -- restore nullstring case to original form --print("y is: ",y,"..",y_orig,".."); if is_tuple(event_des) and event_des(1) = "event" then -- we are setting up the list of physical events that correspond to a virtual event -- this covers cases like Tk("event","virt_event_name") := "phys_event_1,phys_event_2,..." virt_event_name := "<<" + event_des(2) + ">>"; -- get the virtual event name delres := tk_kall("event delete " + virt_event_name); -- clear the present meaning if (y?"") = "" then return delres; end if; -- null assignment is simple deletion return tk_kall("event add " + virt_event_name + " " +/["<" + phys + "> ": phys in y]); -- set up the new meaning end if; if not (is_tuple(y) or is_procedure(y) or y = "") then abort("Only procedures or the null-string can be bound to events, not " + str(y)); end if; if (isp := is_procedure(y)) or y = "" then y := tk_string_of(y); end if; -- convert the procedure to its string name; likewise nullstring -- (this is a tuple if we are sending an event) if event_des = "bindtags" then -- we are setting a widget's 'bindtags' list return tk_kall("bindtags " + full_name() + " [list " + join(y," ") + "]"); elseif not isp then -- we are sending an event under program control, or setting the binding of a binding tag -- this covers cases like Tk("event_des:xy") := "event_par_val_1,event_par_val_2,..." -- it also covers cases like Tk("Text","") := Tk_code_string -- if there is just one parameter value, it must be like Tk("event_des:x") := [event_par_val_1] if is_tuple(event_des) then -- here we handle cases like Tk("Text","") := Tk_code_string [binding_tag,event_des] := event_des; -- break out the binding tag txt := "bind " + str(binding_tag) + " <" + event_des + "> {" + y_orig + "}"; -- convert to form like 'bind Text <> {Tk_code_string}' return tk_kall(txt); -- done with this case end if; event := break(event_des,":"); match(event_des,":"); -- break out the event descriptor ev_att_stg := "" +/ ["-" + eo + " " + str(eov) + " ": c = event_des(j) | (eo := event_opts_from_chars(c)) /= OM and (eov := y(j)) /= OM]; txt := "event generate " + full_name() + " <" + event + "> " + ev_att_stg; return tk_kall(txt); end if; -- in the remaining cases, y was originally a procedure; but it has now been converted to its Tk procedure name if event_des = OM then -- bind the 'principal' event -- the various kinds of buttons (other than menu buttons) have built-in principal -- commands; for the others we use the principal commands designated above (main_command) -- note that for scales and scrollbars, the built-in principal command is invoked -- when the scale-value changes -- we assign a value to the widget or item by binding its specified -- main_command event to it. The tk binding syntax varies a bit, depending -- on whether this object is a widget or a canvas object. if (mc := main_command(tk_type)) = OM then -- use 'config' the_text := full_name() + " configure -command " + "{" + y + "}"; elseif tk_type in widgets then -- we deal with a widget if mc /= "" then the_text := " bind " + full_name() + " " + mc + " {" + y + "}"; else the_text := " bind " + full_name() + " " + mc + " {" + y + " %x %y}"; end if; else -- we deal with a canvas object the_text := parent.full_name() + " bind " + name(2..) + " " + mc + " {" + y + "}"; end if; return tk_kall(the_text); end if; item_name := OM; if is_tuple(event_des) then -- we are setting up a binding for a tag or menu item, -- or defining and sending an event under program control [item_name,event_des] := event_des; -- extract the item and the real event_des if event_des = OM then -- this is of the form tag_name, OM, so we are setting the -- main command of a canvas or text tag if type(y_orig) /= "PROCEDURE" then abort("A tag's principal command can only be bound to a procedure but is: " + str(y_orig)); end if; if tk_type = "menu" then -- must use configuration option item_name := if is_integer(item_name) then item_name - 1 else unstr(item_name) end if; txt := full_name() + " entryconfigure " + str(item_name) + " -command " + y; --print("bound: ",txt); else -- not a menu btb := if tk_type = "canvas" then " bind " else " tag bind " end if; -- Bah! txt := full_name() + btb + str(item_name) + " " + y; end if; return tk_kall(txt); end if; end if; -- else we are setting up a binding for a widget, canvas item, or text item (using its id), if ":" in event_des then param_stg := rbreak(event_des,":"); rmatch(event_des,":"); -- break out the parameter descriptor else param_stg := ""; -- otherwise no parameters end if; param_stg := "" +/["%" + c + " ": c in param_stg]; -- convert param_stg to Tk format event_des := chop(event_des); event_stg := "" +/["<" + item + ">": item in event_des]; -- convert event_stg to Tk format if item_name = OM then -- we are setting up a binding for a widget, canvas item, or text item (using its id) if name = "" then return; end if; -- this should not happen if name(1) = "w" or name = "." then -- binding for a widget txt := "bind " + self.full_name() + " " + event_stg + " {" + y + " " + param_stg + "}"; -- construct a Tk 'bind' instruction, which lists event fields to be passed back else -- binding for a widget, canvas item, or text item (using its id) txt := parent.full_name() + " " + "bind " + name(2..) + " " + event_stg + " {" + y + " " + param_stg + "}"; end if; --print("bindinginst: ",event_des," ",y," ",txt); elseif tk_type = "text" or tk_type = "canvas" then -- we are setting up a binding for a text or canvas tag txt := full_name() + if tk_type = "text" then " tag bind " else " bind " end if + item_name + " " + event_stg + " {" + y + if param_stg = "" then "" else " " end if + param_stg + "}"; --print("Binding text or canvas tag: ",item_name," ",event_des," ",txt); else txt := "???"; -- we are setting up a binding for a tag or menu item end if; res := tk_kall(txt); -- do the binding --print("Bound: ",txt," ",res); end; procedure bind_socket_io_handler(x,io_event_proc); -- binds I/O-ready callback handler to socket -- x is ">" for 'socket_readability_handler' and "<" for 'socket_writability_handler' --print("bind_socket_io_handler: ",x," ",y); if not is_string(x) or #x > 1 or x notin "" then print("****** bad socket direction indicator: ",x); stop; end if; -- validate x if not is_procedure(io_event_proc) then -- bad I/O handler print("****** bad io_event_proc for socket ",if x = ">" then "reading:" else "writing: " end if,text_blocksize_or_accept_proc); stop; end if; proc_name := "s" + str(namegen_ctr := (namegen_ctr?0) + 1); -- generate a new tk variable name tk_createcommand(interp,proc_name,io_event_proc); -- register the setl procedure under this name if x = ">" then -- set up the procedure as a 'ready to read' handler tk_kall("fileevent " + name + " readable [list " + proc_name + " " + name + "]"); -- pass 'set handler' command to tk else -- set up the procedure as a 'ready to write' handler tk_kall("fileevent " + name + " writeable [list " + proc_name + " " + name + "]"); -- pass 'set handler' command to tk end if; end bind_socket_io_handler; procedure stg_to_Tk(stg); -- sanitize the quote marks, blanks, backslashes, and square brackets in a string --print("stg_to_Tk: ",stg); newstr := break(stg,"\"[]\\ \n\r\t"); -- get the first non-special piece while stg /= "" loop piece := any(stg,"\"[]\\ \n\r\t"); newstr +:= if piece = "\n" then "\\n" elseif piece = "\r" then "\\r" elseif piece = "\t" then "\\t" else "\\" + piece end if; -- sanitize or recode the special character piece := break(stg,"\"[]\\ \n\r\t"); newstr +:= piece; -- get the next non-special piece end loop; --print("stg_to_Tk return: ",newstr); return newstr; end stg_to_Tk; -- ****** Listbox Operations ****** procedure nearest(y); -- return index of line vertically nearest to y txt := full_name() + " nearest " + str(y); return tk_kall(txt); end nearest; procedure is_select_line(m); -- determine if line m is selected txt := full_name() + " selection includes \"" + (m - 1) + "\""; return tk_kall(txt); end is_select_line; procedure yview(n); -- move to make indicated line visible, or read vertical scroll position txt := full_name() + " yview " + if n = OM or n = "" then "" else (n - 1) end if; return tk_kall(txt); end yview; -- ****** Clipboard Operations ****** procedure clear_selection(win,the_sel); -- clear specified selection in specified window txt := "selection clear "; if win /= OM then txt +:= (" -displayof " + win.full_name()); end if; if the_sel /= OM then txt +:= (" -selection " + the_sel); end if; return tk_kall(txt); end clear_selection; procedure get_selection(win,the_sel,the_type); -- return the specified selection txt := "selection get "; if win /= OM then txt +:= (" -displayof " + win.full_name()); end if; if the_sel /= OM then txt +:= (" -selection " + the_sel); end if; if the_type /= OM then txt +:= (" -type " + the_type); end if; return tk_kall(txt); end get_selection; procedure handle_selection(win,the_type,format,the_sel,proc); -- define proc to be handler for set/the_type selection requests when 'win' is selection owner txt := "selection handle "; if the_sel /= OM then txt +:= (" -selection " + the_sel); end if; if the_type /= OM then txt +:= (" -type " + the_type); end if; if format /= OM then txt +:= (" -format \"" + format + "\""); end if; txt +:= " win"; -- ********* FIX ********* handle procedure return tk_kall(txt); end handle_selection; procedure own_selection(win,the_sel,proc); -- assert that win is sel owner; and that proc should be called when it loses ownership txt := "selection own "; tk_kall(txt); if win /= OM then txt +:= (" -displayof " + win.full_name()); end if; if the_sel /= OM then txt +:= (" -selection " + the_sel); end if; if proc /= OM then txt +:= " -command "; end if; -- ********* FIX ********* handle procedure end own_selection; procedure selection_owner(win,the_sel); -- find string name of current owner of selection 'sel' txt := "selection own "; tk_kall(txt); if win /= OM then txt +:= (" -displayof " + win.full_name()); end if; if the_sel /= OM then txt +:= (" -selection " + the_sel); end if; end selection_owner; procedure clear_clipboard(win); -- clear clipboard for specified window txt := "clipboard clear " + if win = OM then "" else "-displayof " + win.full_name() end if; return tk_kall(txt); end clear_clipboard; procedure addto_clipboard(win,the_type,format,data); -- add 'data', of specified format and type, to clipboard for specified window txt := "clipboard append "; if win /= OM then txt +:= (" -displayof " + win.full_name()); end if; if format /= OM then txt +:= (" -format \"" + format + "\""); end if; if the_type /= OM then txt +:= (" -type " + the_type); end if; txt +:= (" \"" + stg_to_Tk(data) + "\""); --print("addto_clipboard: ",txt); return tk_kall(txt); end addto_clipboard; -- ****** Dialogs and Message boxes ****** -- Note: all these have been put in the syntax win("ask_...","options") := "option_vals"; -- ****** Image Operations ****** procedure dither(); -- dither the image txt := name + " redither"; --print("redither:",txt); return tk_kall(txt); end dither; procedure handle_im_options(options); -- handle image read, write, and copy operations -- image operation options are from (rectangle), to (rectangle), subsample (x_fact,y_fact), -- shrink (source to match bottom right corner of target), zoom (x_fact,y_fact) if options = "" or options = OM then return ""; end if; options := chop(options); -- divide the options into a tuple, at commas or semicolons tk_option_stg := ""; -- will build k := 1; for item = options(j) loop -- iterate over list of options if odd(k) then tk_option_stg +:= " -" + (prior := item) + " "; k +:= if item = "shrink" then 2 else 1 end if; -- no parameters in shrink case, so bypass continue; -- done with this case end if; -- otherwise we have an option if prior = "format" then tk_option_stg +:= ("\"" + item + "\" "); k +:= 1; continue; end if; pieces := breakup(item,","); -- otherwise break up the comma-delimited rectangles, etc. tk_option_stg +:= ("" +/ [piece + " ": piece in pieces]); k +:= 1; end loop; return "" +/ [c +" ": c in breakup(tk_option_stg," ") | c /= ""]; -- return the completed tk option string, eliminating double blanks end handle_im_options; procedure write_im(file,options); -- write image to file txt := name + " write " + file + " " + handle_im_options(options); --print("write_im: ",txt); res := tk_kall(txt); print(res); return res; end write_im; procedure tup_to_tx_imdat(tup); -- convert tuple of image data to tx list return "{" + join(tup," ") + "}"; end tup_to_tx_imdat; procedure stuff_im(data,rect); -- insert data into image rectangle txt := name + " put "; if is_string(data) then data := breakup(breakup(data,";"),","); end if; -- the 'data' argument is assumed to be a tuple of tuples of color values data := "{" + join([tup_to_tx_imdat(item): item in data]," ") + "}"; txt +:= (data + " "); -- add the data to the developing string if rect /= OM then -- add the optional 'to' clause txt +:= (" -to " +/ [str(item) + " ": item in chop(rect)]); end if; return tk_kall(txt); end stuff_im; procedure copy_im(source,options); -- copy one image to another txt := name + " copy " + source.name + " " + handle_im_options(options); return tk_kall(txt); end copy_im; -- ****** Window Manager Operations ****** procedure win_close(); -- close or iconify a toplevel return tk_kall("wm iconify " + full_name()); end win_close; procedure win_open(); -- open or deiconify a toplevel test_exists := tk_kall("winfo toplevel " + full_name()); isb := match(test_exists,"bad"); if isb /= "" then return OM; end if; return tk_kall("wm deiconify " + full_name()); end win_open; -- ****** Font Routines ****** procedure font_metrics(font); -- get the metrics of the designated font -- as a map from "fixed","linespace",,"ascent","descent" to ints tup := breakup(suppress_chars(tk_kall("font metrics " + font),"-")," "); return {[tup(j),unstr(tup(j + 1))]: j in [1,3..7]}; end font_metrics; procedure measure_fonted(stg,font); -- get the size of the string in the designated font return tk_kall("font measure " + font + " \"" + stg + "\""); end measure_fonted; procedure font_families(); -- get the list of fonts available in Tk return tk_kall("font families"); end font_families; -- ****** disk Routines ****** procedure disks(); -- get the currently mounted disks return tk_kall("file volume"); end disks; -- ****** Socket Routines ****** procedure socket_close(); -- close a socket return tk_kall("close " + name); -- pass this command to tk end socket_close; -- ********** Routines for persistency ************** procedure get_Tk_packed(); -- gets the Tk packing information as a map Tk_packed := {}; -- initialize all_containers := {x: x in domain(Tk_children) | (xk := tag_from_tagged_name(x)) = "toplevel" or xk = "frame"} + {x: range_tup in range(Tk_children), x in range_tup | (xk := tag_from_tagged_name(x)) = "toplevel" or xk = "frame"}; for obj_name in all_containers loop tkog_names_list := breakup(Tk_kall("pack slaves " + fn_from_tagged_name(obj_name))," "); packed_list := [(att_map_from_att_stg(Tk_kall("pack info " + obj)) - Tk_grid_defaults) less ["in",parent_name(obj)] with ["Tk_fn",tag_from_untagged_name(obj) + ":" + obj]: obj in tkog_names_list]; Tk_packed(obj_name) := packed_list; --print("\nTk_packed: "); for [x1,x2] in Tk_packed | #x2 > 0 loop print("\n",x1); for y in x2 loop print(y); end loop; end loop; end loop; --print("\nTk_packed: "); for [x1,x2] in Tk_packed | #x2 > 0 loop print("\n",x1); for y in x2 loop print(y); end loop; end loop; end get_Tk_packed; procedure get_Tk_gridded(); -- gets the Tk gridding information as a map Tk_gridded := {}; -- initialize all_containers := {x: x in domain(Tk_children) | (xk := tag_from_tagged_name(x)) = "toplevel" or xk = "frame"} + {x: range_tup in range(Tk_children), x in range_tup | (xk := tag_from_tagged_name(x)) = "toplevel" or xk = "frame"}; for obj_name in all_containers loop tkog_names_list := breakup(Tk_kall("grid slaves " + fn_from_tagged_name(obj_name))," "); gridded_list := [(att_map_from_att_stg(Tk_kall("grid info " + obj)) - Tk_grid_defaults) less ["in",parent_name(obj)] with ["Tk_fn",tag_from_untagged_name(obj) + ":" + obj]: obj in tkog_names_list]; if gridded_list /= [] then Tk_gridded(fn_from_tagged_name(obj_name)) := gridded_list; end if; --print("gridded_list: ",obj_name," ",gridded_list," ",tkog_names_list); end loop; --print("\nTk_gridded: "); for [x1,x2] in Tk_gridded | #x2 > 0 loop print("\n",x1); for y in x2 loop print(y); end loop; end loop; end get_Tk_gridded; procedure get_Tk_children(); -- gets the full hierarchy of Tk children as a map Tk_children := {}; -- initialize get_Tk_children_in("toplevel:."); -- call recursive workhorse --print("\nTk_children",Tk_children); end get_Tk_children; procedure get_Tk_children_in(obj); -- gets hierarchy of Tk children; workhorse if (otkk := tag_from_tagged_name(obj)) notin {"toplevel","frame","menubutton","menu"} then return; end if; Tk_children(obj) := children := [tag_from_untagged_name(child) + ":" + child: child in breakup(Tk_kall("winfo children " + fn_from_tagged_name(obj))," ")]; for child in children loop get_Tk_children_in(child); end loop; end get_Tk_children_in; -- ******* Auxiliary reconfiguration routines for text; handle text dump string analysis ******* procedure setup_from_dump(target_texwidg_name,dump_stg); -- reconstruct a text area grom its dump string [text_tuple,tags_tuple,marks_tuple,widgets_tuple,images_tuple] := sep_tags_and_marks(stgs_from_Tk(dump_stg)); --print("text_tuple: ",text_tuple); the_text := "" +/text_tuple; -- calculate the new text; delete the old and insert the new Tk_kall(target_texwidg_name + " delete 1.0 end"); Tk_kall(target_texwidg_name + " insert 1.0 " + stg_to_Tk(the_text)); for [win_name,cloc] in widgets_tuple loop res := Tk_kall(txt := target_texwidg_name + " window create " + cloc + " -window " + fn_from_tagged_name(str(new_item_from_orig_name(win_name)))); --print("widgets_tuple: ",txt," ",win_name," ",new_item_from_orig_name(win_name)); end loop; for [img_name,cloc] in images_tuple loop Tk_kall(target_texwidg_name + " image create " + cloc + " -image " + img_name); end loop; tags_to_ranges := {}; for [mark,ix] in marks_tuple | mark /= "insert" and mark /= "current" loop Tk_kall(target_texwidg_name + " mark set {" + mark + "} " + ix); end loop; for [tag,loc] in tags_tuple loop tags_to_ranges(tag) := (tags_to_ranges(tag)?[]) with loc; end loop; for [tag,loc_list] in tags_to_ranges | tag /= "sel" loop Tk_kall(target_texwidg_name + " tag add {" + tag + "} " + join(loc_list," ")); end loop; end setup_from_dump; procedure sep_tags_and_marks(stg_tup); -- separate a string's dump tuple into its text, plus tags_and_marks text_tuple := []; tags_tuple := []; marks_tuple := []; widgets_tuple := []; images_tuple := []; j := 0; while j < #stg_tup loop piece := stg_tup(j +:= 1); case piece when "text" => text_tuple with:= stg_tup(j +:= 1); j +:= 1; when "tagon" => tags_tuple with:= [stg_tup(j +:= 1),stg_tup(j +:= 1)]; when "tagoff" => tags_tuple with:= [stg_tup(j +:= 1),stg_tup(j +:= 1)]; when "mark" => marks_tuple with:= [stg_tup(j +:= 1),stg_tup(j +:= 1)]; when "window" => widgets_tuple with:= [stg_tup(j +:= 1),stg_tup(j +:= 1)]; when "image" => images_tuple with:= [stg_tup(j +:= 1),stg_tup(j +:= 1)]; end case; end loop; return [text_tuple,tags_tuple,marks_tuple,widgets_tuple,images_tuple]; end sep_tags_and_marks; -- ************************ Minor persisency utiliies ************************ procedure tag_from_tagged_name(tagged_name); tag := break(tagged_name,":"); return tag; end tag_from_tagged_name; procedure tag_from_untagged_name(untagged_name); tag := case_change(Tk_kall("winfo class " + untagged_name),"ul"); return tag; end tag_from_untagged_name; procedure fn_from_tagged_name(tagged_name); name := rbreak(tagged_name,":"); return name; end fn_from_tagged_name; procedure att_map_from_att_stg(att_stg); -- convert raw Tk attribute information to SETL map form val_list := breakup(att_stg," "); return {[val_list(j)(2..),vljp1]: j in [1,3..#val_list - 1] | (vljp1 := val_list(j + 1)) /= "{}"}; end att_map_from_att_stg; procedure parent_name(name); -- gets name of parent from name of child rbreak(name,"."); if #name > 1 then rmatch(name,"."); end if; return name; end parent_name; end tkw; -- ************************ END OF PACKAGE ************************ package drag_pak; -- Drag setup package; also sets up for response to drop-on event. -- This package provides an easy-use drag or drag-and-drop capability for widgets. -- Calling the routine make_draggable(the_obj,dg_start,dg,dg_end) makes the widget 'the_obj' -- draggable. The 3 additional parameters dg_start, dg, and dg_end can be OM, but -- if not they should all be procedures of one parameter, prepared to receive an integer -- point [x,y], the location of a mouse-related event. Then dg_start will be called -- at the beginning of the drag, immediately after mousedown (whose location will be -- transmitted to it.) Similarly dg will be called for each mouse_move event, and -- dg_end will be called on drag-end. -- If make_drop_sensitive(the_obj,drop_response) is called, its drop_response parameter -- should be a procedure drop_response(on_obj,dropped_obj) of two parameters, -- which will be widgets. 'drop_response' will be called whenever the drag of an object -- 'dropped_obj', made draggable by 'make_draggable', ends with the mouse positioned over -- an object 'on_obj' made drop sensitive. 'drop_response' should then take whatever action -- is appropriate for a drop of dropped_obj onto on_obj. -- The test prgram given below shows how these procedures can be used to create a -- drag-and-drop oriented variant of the usual pocket calculator. var was_dragging,dropped_at; -- the last object being dragged, and its drop point var start_canv_x,start_canv_y; -- drag starting point, canvas relative, floating var start_coords_obj; -- vector of starting coordinates var ops_in_drag_mode := {}; -- maps objects to their associated actions in specified mode var current_drag_mode := "edit"; -- current mode procedure make_draggable(the_obj,dg_start,dg,dg_end); -- make a widget draggable procedure make_horiz_draggable(the_obj,dg_start,dg,dg_end); -- make a widget or widgets horizontallly draggable procedure make_vert_draggable(the_obj,dg_start,dg,dg_end); -- make a widget or widgets verticallly draggable procedure make_drop_sensitive(the_obj,drop_response); -- build response for mouse entry event (by drop) procedure switch_drag_mode(new_mode); -- switch the draggability mode end drag_pak; package body drag_pak; -- drag setup package use tkw; -- use the basic widget package var drag_offs_x,drag_offs_y; -- offset for object being dragged procedure switch_drag_mode(new_mode); -- switch the draggability mode if new_mode = current_drag_mode then return; end if; prior_domain := domain(prior_ops := ops_in_drag_mode(current_drag_mode)?{}); -- get the object-associated operations in the new_domain := domain(new_ops := ops_in_drag_mode(new_mode)?{}); -- current and target modes current_drag_mode := new_mode; -- switch to the new mode --print("switch_drag_mode: ",new_mode,new_domain,new_ops); for obj in new_domain loop -- change to the 'new mode' operations for all objects that have one [dg_start,dg,dg_end] := new_ops(obj); -- get the start, drag, and end codes obj{"ButtonPress-1:xy"} := if dg_start = OM then attach_start_noproc([obj]) else attach_start([obj],dg_start) end if; -- only one object is put in the drag list obj{"B1-Motion:xy"} := if dg = OM then attach_drag_noproc([obj],0) else attach_drag([obj],dg,0) end if; -- we put this in 2D drag mode obj{"ButtonRelease-1:xy"} := if dg_end = OM then attach_end_noproc([obj]) else attach_end([obj],dg_end) end if; end loop; -- now turn off any remaining operations that were on in the previous mode for obj in prior_domain - new_domain loop -- change to the 'new mode' operations for all objects that have one obj{"ButtonPress-1"} := ""; obj{"B1-Motion"} := ""; obj{"ButtonRelease-1"} := ""; end loop; end switch_drag_mode; procedure make_draggable(the_obj,dg_start,dg,dg_end); -- make a widget or widgets draggable return gen_draggable(the_obj,dg_start,dg,dg_end,0); end make_draggable; procedure make_horiz_draggable(the_obj,dg_start,dg,dg_end); -- make a widget or widgets horizontallly draggable return gen_draggable(the_obj,dg_start,dg,dg_end,1); end make_horiz_draggable; procedure make_vert_draggable(the_obj,dg_start,dg,dg_end); -- make a widget or widgets verticallly draggable return gen_draggable(the_obj,dg_start,dg,dg_end,2); end make_vert_draggable; procedure gen_draggable(the_obj,dg_start,dg,dg_end,horiz_vert); -- make a widget or widgets draggable if not is_tuple(the_obj) then the_obj := [the_obj]; end if; -- force to tuple; note that a list of widgets sharing common drag routines might have been passed if (nam := (the_obj(1)).Tk_id()) = "" then return; -- this should not happen end if; if nam(1) = "w" then -- dealing with a widget w_make_draggable(the_obj,dg_start,dg,dg_end,horiz_vert); else -- dealing with a canvas item c_make_draggable(the_obj,dg_start,dg,dg_end,horiz_vert); end if; end gen_draggable; procedure w_make_draggable(the_objs,dg_start,dg,dg_end,horiz_vert); -- make a lst of widgets sharing common drag routines draggable for the_obj in the_objs loop if is_procedure(dg_start) then -- attach the start routine with extra action the_obj{"ButtonPress-1:XY"} := press_op := attach_start(the_objs,dg_start); else -- attach the start routine with no extra action the_obj{"ButtonPress-1:XY"} := press_op := attach_start_noproc(the_objs); end if; if is_procedure(dg) then -- attach the drag routine with extra action the_obj{"B1-Motion:XY"} := drag_op := attach_drag(the_objs,dg,horiz_vert); -- start the drag else -- attach the start routine with no extra action the_obj{"B1-Motion:XY"} := drag_op := attach_drag_noproc(the_objs,horiz_vert); end if; if is_procedure(dg_end) then -- attach termination routine the_obj{"ButtonRelease-1:XY"} := release_op := attach_end(the_objs,dg_end); else -- attach the termination routine with no extra action the_obj{"ButtonRelease-1:XY"} := release_op := attach_end_noproc(the_objs); end if; ops_in_drag_mode(current_drag_mode) ?:= {}; ops_in_drag_mode(current_drag_mode)(the_obj) := [press_op,drag_op,release_op]; -- save the operations associated with the object in the current mode end loop; end w_make_draggable; procedure attach_end(the_objs,dg_end); -- bind the object into the termination routine return lambda(xy); was_dragging := the_objs; -- note the objects that were being dragged [now_abs_x,now_abs_y] := xy; now_abs_x := unstr(now_abs_x); now_abs_y := unstr(now_abs_y); dg_end(the_objs,dropped_at := [now_abs_x,now_abs_y]); end lambda; -- note the point at which the drag ended end attach_end; procedure attach_end_noproc(the_objs); -- bind the object into the termination routine return lambda(xy); was_dragging := the_objs; -- note the objects that were being dragged [now_abs_x,now_abs_y] := xy; now_abs_x := unstr(now_abs_x); now_abs_y := unstr(now_abs_y); dropped_at := [now_abs_x,now_abs_y]; -- note the point at which the drag ended end lambda; end attach_end_noproc; procedure attach_start(the_objs,dg_start); -- drag start routine generator return lambda(xy); was_dragging := the_objs; -- note the objects being dragged [start_abs_x,start_abs_y] := xy; start_abs_x := unstr(start_abs_x); start_abs_y := unstr(start_abs_y); drag_offs_x := drag_offs_y := []; -- keep vector of displacements for obj in the_objs loop [place_x,place_y] := obj.place(); drag_offs_x with:= (place_x - start_abs_x); drag_offs_y with:= (place_y - start_abs_y); obj.raise(OM); -- raise dragged object to top level in rendering order end loop; dg_start(the_objs,[start_abs_x,start_abs_y]); -- call the supplementary routine end lambda; end attach_start; procedure attach_start_noproc(the_objs); -- drag start routine generator; no action version return lambda(xy); was_dragging := the_objs; -- note the objects being dragged [start_abs_x,start_abs_y] := xy; start_abs_x := unstr(start_abs_x); start_abs_y := unstr(start_abs_y); drag_offs_x := drag_offs_y := []; -- keep vector of displacements for obj in the_objs loop [place_x,place_y] := obj.place(); drag_offs_x with:= (place_x - start_abs_x); drag_offs_y with:= (place_y - start_abs_y); obj.raise(OM); -- raise dragged object to top level in rendering order end loop; end lambda; end attach_start_noproc; procedure attach_drag(the_objs,dg,horiz_vert); -- drag routine generator var parx,pary; parent := the_objs(1)("parent"); parx := parent("width"); pary := parent("height"); parx -:= 2; pary -:= 2; return lambda(xy); -- object dragging demo: drag routine [now_abs_x,now_abs_y] := xy; now_abs_x := unstr(now_abs_x); now_abs_y := unstr(now_abs_y); for obj = the_objs(j) loop nax := ((now_abs_x + drag_offs_x(j)) max 0) min parx; nay := ((now_abs_y + drag_offs_y(j)) max 0) min pary; if horiz_vert = 0 then obj("place,x,y") := str(nax) + "," + str(nay); elseif horiz_vert = 1 then obj("place,x") := str(nax); else obj("place,y") := str(nay); end if; end loop; dg(the_objs,[nax,nay]); -- call the supplementary routine, passing the list of all objects in the set as a parameter end lambda; end attach_drag; procedure attach_drag_noproc(the_objs,horiz_vert); -- drag routine generator; no action var parx,pary; parent := the_objs(1)("parent"); parx := parent("width"); pary := parent("height"); return lambda(xy); -- object dragging demo: drag routine [now_abs_x,now_abs_y] := xy; now_abs_x := unstr(now_abs_x); now_abs_y := unstr(now_abs_y); for obj = the_objs(j) loop nax := ((now_abs_x + drag_offs_x(j)) max 0) min parx; nay := ((now_abs_y + drag_offs_y(j)) max 0) min pary; if horiz_vert = 0 then obj("place,x,y") := str(nax) + "," + str(nay); elseif horiz_vert = 1 then obj("place,x") := str(nax); else obj("place,y") := str(nay); end if; end loop; end lambda; end attach_drag_noproc; procedure make_drop_sensitive(the_obj,drop_response); -- build response for mouse entry event (by drop) the_obj{"Enter:xy"} := lambda(xy); -- this is the mouse entry code wd := was_dragging; was_dragging := OM; -- was_dragging is the list of objects being dragged if wd /= OM and the_obj /= wd(1) then -- dropped on object other than itself was_dragging := OM; drop_response(the_obj,wd(1)); -- note the drop-on event end if; end lambda; end make_drop_sensitive; procedure near(xy,ab); return abs(unstr(xy(1)) - ab(1)) + abs(unstr(xy(2)) - ab(2)) < 5; end near; procedure c_make_draggable(the_objs,dg_start,dg,dg_end,horiz_vert); -- make a canvas item draggable for the_obj in the_objs loop if is_procedure(dg_start) then -- attach the start routine with extra action the_obj{"ButtonPress-1:xy"} := press_op := c_attach_start(the_objs,dg_start); else -- attach the start routine with no extra action the_obj{"ButtonPress-1:xy"} := press_op := c_attach_start_noproc(the_objs); end if; if is_procedure(dg) then -- attach the drag routine with extra action the_obj{"B1-Motion:xy"} := drag_op := c_attach_drag(the_objs,dg,horiz_vert); -- start the drag else -- attach the start routine with no extra action the_obj{"B1-Motion:xy"} := drag_op := c_attach_drag_noproc(the_objs,horiz_vert); end if; if is_procedure(dg_end) then -- attach termination routine the_obj{"ButtonRelease-1:xy"} := release_op := c_attach_end(the_objs,dg_end); else -- attach the termination routine with no extra action the_obj{"ButtonRelease-1:xy"} := release_op := c_attach_end_noproc(the_objs); end if; end loop; ops_in_drag_mode(current_drag_mode) ?:= {}; ops_in_drag_mode(current_drag_mode)(the_obj) := [press_op,drag_op,release_op]; -- save the operations associated with the object in the current mode end c_make_draggable; procedure c_attach_start_noproc(the_objs); -- drag start routine generator; no action version return lambda(xy); [start_canv_x,start_canv_y] := xy; start_canv_x := float(unstr(start_canv_x)); start_canv_y := float(unstr(start_canv_y)); start_coords_obj := [[unstr(x): x in the_obj("coords")]: the_obj in the_objs]; for the_obj in the_objs loop the_obj.raise(OM); end loop; -- raise dragged objects to top level in rendering order end lambda; end c_attach_start_noproc; procedure c_attach_start(the_objs,dg_start); -- drag start routine generator return lambda(xy); [start_canv_x,start_canv_y] := xy; start_canv_x := float(unstr(start_canv_x)); start_canv_y := float(unstr(start_canv_y)); start_coords_obj := [[unstr(x): x in the_obj("coords")]: the_obj in the_objs]; for the_obj in the_objs loop the_obj.raise(OM); end loop; -- raise dragged objects to top level in rendering order dg_start(the_objs,[start_canv_x,start_canv_y]); -- call the supplementary routine end lambda; end c_attach_start; procedure c_attach_drag_noproc(the_objs,horiz_vert); -- drag routine generator; no action return lambda(xy); -- object dragging demo: drag routine [now_canv_x,now_canv_y] := xy; now_canv_x := float(unstr(now_canv_x)); now_canv_y := float(unstr(now_canv_y)); delta_x := now_canv_x - start_canv_x; delta_y := now_canv_y - start_canv_y; for the_obj = the_objs(k) loop if horiz_vert = 0 then now_coords_stg := "" +/ [str(x + if odd(j) then delta_x else delta_y end if) + " ": x = start_coords_obj(k)(j)]; elseif horiz_vert = 1 then now_coords_stg := "" +/ [str(x + if odd(j) then delta_x else 0.0 end if) + " ": x = start_coords_obj(k)(j)]; else now_coords_stg := "" +/ [str(x + if even(j) then delta_y else 0.0 end if) + " ": x = start_coords_obj(k)(j)]; end if; the_obj("coords") := now_coords_stg; end loop; end lambda; end c_attach_drag_noproc; procedure c_attach_drag(the_objs,dg,horiz_vert); -- drag routine generator return lambda(xy); -- object dragging demo: drag routine [now_canv_x,now_canv_y] := xy; now_canv_x := float(unstr(now_canv_x)); now_canv_y := float(unstr(now_canv_y)); delta_x := now_canv_x - start_canv_x; delta_y := now_canv_y - start_canv_y; for the_obj = the_objs(k) loop if horiz_vert = 0 then now_coords_stg := "" +/ [str(x + if odd(j) then delta_x else delta_y end if) + " ": x = start_coords_obj(k)(j)]; elseif horiz_vert = 1 then now_coords_stg := "" +/ [str(x + if odd(j) then delta_x else 0.0 end if) + " ": x = start_coords_obj(k)(j)]; else now_coords_stg := "" +/ [str(x + if even(j) then delta_y else 0.0 end if) + " ": x = start_coords_obj(k)(j)]; end if; the_obj("coords") := now_coords_stg; dg(the_obj,[now_canv_x,now_canv_y]); -- call the supplementary routine end loop; end lambda; end c_attach_drag; procedure c_attach_end_noproc(the_objs); -- end drag routine generator; no action version return lambda(xy); was_dragging := the_objs; -- note the object that was being dragged [now_canv_x,now_canv_y] := xy; now_canv_x := unstr(now_canv_x); now_canv_y := unstr(now_canv_y); dropped_at := [now_canv_x,now_canv_y]; -- note the point at which the drag ended end lambda; end c_attach_end_noproc; procedure c_attach_end(the_objs,dg_end); -- end drag routine generator return lambda(xy); was_dragging := the_objs; -- note the object that was being dragged [now_canv_x,now_canv_y] := xy; now_canv_x := unstr(now_canv_x); now_canv_y := unstr(now_canv_y); dg_end(the_objs,dropped_at := [now_canv_x,now_canv_y]); -- note the point at which the drag ended end lambda; end c_attach_end; end drag_pak; class ifprinter; -- class for conditional printing class var pif := false; -- switch to turn printing on procedure create(); -- creation routine end ifprinter; class body ifprinter; -- class for conditional printing procedure self(x); -- prints its argument if not pif then return; end if; print(if is_tuple(x) then join(x,",") else x end if); end; procedure create(); -- creation routine end create; end ifprinter; program test; -- test of the main widget class use tkw,ifprinter; -- use the main widget class var Tk; -- the Tk interpreter var printer; -- various global variables for testing var lb,lb2,e,msg,the_menu,the_menu2,te,sc,ca,imaje,imaje2,cw,ov,arc,img; -- for basic_tests var nt,ntca,ntmsg,obj_of_tag := { },td,blot,tdb,tdb2,tdb3; -- for canvas_tests var check1,check2,rad1,rad2; -- for canvas_tests var img_canv; -- for image_tests Tk := tkw(); -- create the Tk interpreter printer := ifprinter(); -- create a conditional printer pif := true; -- switches printing on img := Tk("image","orchid.GIF"); -- read an image file to create an image -- basic_tests; -- do first group of basic tests -- canvas_tests; -- do second group of tests: canvas tests -- atext_widget_tests; -- do third group of tests: text widget tests OK -- dialog_tests; -- do fourth group of tests: standard dialog tests OK -- listbox_tests; -- do fifth group of tests: listbox tests primes_demo; -- do prime factorization demo -- misc_tests; -- small group of miscellaneous tests --debug_trace := "Step1"; --show_commands := true; -- start to trace the commands to tk -- lab := Tk("label","n will appear here"); lab("side") := "top"; -- sb := Tk("scrollbar","h,10"); sb("side") := "top"; --sb("fill") := "x"; -- junk:=printer("frame height,width are: ",nw("height,width")); -- junk:=printer("attributes of this canvas are ",c("background;borderwidth;relief;height")); -- Tk.quit(); -- just quit Tk main loop Tk.mainloop(); -- enter the Tk main loop procedure misc_tests; -- small group of miscellaneous tests end misc_tests; procedure drag; -- object dragging procedure end drag; procedure primes_demo; -- prime factorization demo lb := Tk("label","n will appear here"); lb("side,anchor") := "top,w"; lb2 := Tk("label","factorization will appear here"); lb2("side,anchor") := "top,w"; ca := Tk("frame","300,20"); ca("side") := "top"; button := ca("button","xxx"); button("side") := "left"; button("text") := "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"; button2 := ca("button","xxx"); button2("side") := "left"; button2("text") := "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"; -- button{"B1-Motion"} := beep; button{"B1-Motion:xy"} := showxy; button2{"B1-Motion:xy"} := showxy2; end primes_demo; procedure showxy(xy); -- 1-parameter callback routine [x,y] := xy; x_and_y := 2 * abs(unstr(x) * 1000 + unstr(y)) + 3; npf := #(pf := primefacts(x_and_y)); lb(OM) := "n: " + str(x_and_y); lb2(OM) := "factors: " +/ [str(p) + if j < npf then " * " else "" end if: p = pf(j)]; -- write number and factor to label end showxy; procedure showxy2(xy); -- 1-parameter callback routine [x,y] := xy; x_and_y := abs(unstr(x) * 1000 + unstr(y)); npf := #(pf := primefacts(x_and_y)); lb(OM) := "n: " + str(x_and_y); lb2(OM) := "factors: " +/ [str(p) + if j < npf then " * " else "" end if: p = pf(j)]; -- write number and factor to label end showxy2; procedure primefacts(n); -- prime factorization routine for demo facts := []; while even(n) loop facts with:= 2; n/:= 2; end loop; while n mod 3 = 0 loop facts with:= 3; n/:= 3; end loop; while n > 1 and (exists m in [3..fix(sqrt(float(n))) + 1] | n mod m = 0) loop facts with:= m; n/:= m; end loop; return if n > 1 then facts with n else facts end if; end primefacts; procedure listbox_tests; -- fifth group of tests: listbox tests listbox_top := Tk("toplevel","300,300"); -- create a toplevel b := listbox_top("button","Show listbox selection"); b("side") := "top"; b{OM} := showlis; lb := listbox_top("listbox",str(lb_len := 10)); -- create a listbox lb("side") := "bottom"; more := ["more_" + str(j): j in [1..lb_len]]; lb(1..0) := [if j = 1 then "\"Insert Stuff\"" elseif j = 2 then "\"Delete Stuff\"" else "choice_" + str(j) end if: j in [1..lb_len - 1]] + more; lb{OM} := pickit; -- set the command to be executed when the listbox is clicked ---> Working Here print("length of listbox: ",#lb); print("bounding box of line 2: ",lb.bbox(2)," ",lb.bbox(3)," ",lb.bbox(1)); -- some problem print("listbox end: ",lb.index("end")); e := listbox_top("entry","70"); e("side") := "top"; msg := listbox_top("message","Twas Brillig"); msg("side") := "top"; -- create a message in the frame, and pack it into the frame msg("width") := 300; end listbox_tests; procedure showlis; -- show the listbox selection print("listbox selection is: ",lb(OM)); lb("hilight") := 6; end showlis; procedure image_tests; -- sixth group of tests: image tests img_top := Tk("toplevel","300,300"); -- create a toplevel img_canv := img_top("canvas","300,300"); img_canv("side") := "top"; test_image := img_canv("image",img); -- create and place image test_image("coords,anchor") := "5,5;nw"; test_image{OM} := clearead_img; print(img("gamma,file")); print(test_image("anchor,tags,image")); -- test the fetch of some internal and relative image attributes print("pixel before: ",img(11,11)," ",img("data")); -- test the stuff_im operation img.stuff_im("#ffffff,#ffffff,#ffffff;#ffffff,#ffffff,#ffffff;#ffffff,#ffffff,#ffffff","10,10,12,12"); print("pixel after: ",img(11,11)," ",img("data")); end image_tests; procedure clearead_img(); -- clear or read the image 'img' img("file") := "orchidbig.GIF"; test_image2 := img_canv("image",img); -- create and place a second image test_image2("coords,anchor") := "80,5;nw"; print("img(5,10): ",img(5,10)); -- fetch and print the pixel value print("subrectangle extraction: ",imsub := img("5,5".."100,100")); -- test subrectangle extraction and insertion img("100,100".."195,195") := imsub; -- re-insert the image img.write_im("abs_img_test.gif","from;1,1,250,250"); img.dither(); read_copy2 := Tk("image","orchid.GIF"); img.copy_im(read_copy2,"zoom;4,4"); end clearead_img; procedure dialog_tests; -- fourth group of tests: standard dialog tests dialog_win := Tk("toplevel","10,10"); dialog_win("ask","title,message,labels,default") := "Pick a Case;Pick one of the following cases;Case A,Case B,Case C;2"; print(dialog_response); Tk("ask_ok","type,default,message") := "ok,ok,Proceed"; print(dialog_response); Tk("ask_ok","type,default,message") := "yesno,no,Proceed"; print(dialog_response); Tk("ask_file","") := ""; print("open_file: ",dialog_response); Tk("ask_save_file","") := ""; print("save_file: ",dialog_response); Tk("ask_color","initialcolor,title") := "red,Pick a Color"; print("get_color: ",dialog_response); end dialog_tests; procedure atext_widget_tests; -- third group of tests: text widget tests tnt := Tk("toplevel","300,300"); -- create a toplevel tntex := tnt("text","100,30"); -- 100 chars, 30 lines tntex(OM) := "This is a full-fledged text widget, \ncontaining multiple lines of rich text"; tntex("side,anchor") := "top;nw"; print("tntex('1.2'..'1.4'): ", tntex("1.2".."1.4")); tntex("1.2".."1.4") := ""; tntex("2.2".."2.1") := "xxxx"; print("tntex.bbox: ",tntex.bbox("1.2")); print("tntex.linebox: ",tntex.linebox("1.0")); -- ???? tntex.insert_image("1.2",["image",img]); tntex.insert_image("1.8",["image",img]); tntex.insert_image("2.5",["image",img]); print("tntex('images'): ",tntex("images")); but1 := tnt("button","Embedded button"); tntex.insert_widget("1.4",["window",but1]); print("tntex('widgets'): ",tntex("widgets")); tntex.tag_add("Big","1.1,1.10,2.1,2.10"); tntex.tag_remove("Big","1.5,1.8,2.5,2.8"); tntex("Big","font") := "{times 48 bold}"; print("tntex.tag_nextrange('Big','1.11','end'): ",tntex.tag_nextrange("Big","1.11","end")); print("tntex.tag_prevrange('Big','1.11','end'): ",tntex.tag_prevrange("Big","1.0","end")); end atext_widget_tests; procedure canvas_tests; -- second group of tests: canvas tests nt := Tk("toplevel","300,300"); -- create a toplevel ntca := nt("canvas","300,300"); ntca("side") := "top"; msgtop := Tk("toplevel","300,100"); -- create a toplevel to write messages to ntmsg := msgtop("message","Messages will appear here"); ntmsg("side") := "top"; ntmsg("width") := "500"; boxno := 0; -- count of boxes in following loop for i in [1..4], j in [1..4] loop -- set up an array of 16 small images boxno +:= 1; -- increment count of boxes imajeij := ntca("image",img); -- create and place image imajeij("coords,anchor") := (ijplace := str(-70 + 75 * i) + "," + str(-70 + 75 * j)) + ";nw"; imajeij{OM} := make_response(ijplace); -- pass the tag to the response function ntca.addtag_nearest(ijplace,ijplace,OM,OM); obj_of_tag(ijplace) := imajeij; -- map the identifying tag into the image object end loop; blot := ntca("oval","0,0,100,100"); blot("fill") := "blue"; blot{OM} := scale_me; print("nearest to 5,5: ",ntca.find_nearest(5,5)); -- print("\nabove 1: ",ntca.find_after(1)); -- print("\nbelow 3: ",ntca.find_before(3)); -- for obj in ntca.find_all() loop print("\nall: ",obj); end loop; -- print("\nIn"); for obj in ntca.find_in("0,0,100,100") loop print("\nIn: ",obj); end loop; -- print("\nTouching"); for obj in ntca.find_touching("0,0,100,100") loop print("\nTouching: ",obj); end loop; -- print("\nWith tag or id"); for obj in ntca.find("5,5") loop print("\nWith: ",obj); end loop; print("ntca('tags'): ",ntca("tags")); td := Tk("toplevel","100,100"); -- create a toplevel tdf := td("frame","100,20"); tdf("side") := "top"; tdb := tdf("button","Close Me"); tdb("side") := "left"; tdb{OM} := close_td; tdb2 := tdf("button","Flash Him"); tdb2("side") := "left"; tdb2{OM} := flash3; tdb3 := tdf("button","Execute Close Op"); tdb3("side") := "left"; tdb3{OM} := do_tdb; tdb4 := tdf("button","Set Check Drop Rad"); tdb4("side") := "left"; tdb4{OM} := set_check1; tdb5 := tdf("button","Set Rad Drop Check"); tdb5("side") := "left"; tdb5{OM} := set_radio1; check1 := tdf("checkbutton","Check1"); check1("side") := "left"; check1("variable") := "checkvar1"; check2 := tdf("checkbutton","Check2 "); check2("side") := "left"; check2("variable") := "checkvar2"; check1{OM} := print_checks; check2{OM} := print_checks; rad1 := tdf("radiobutton","Radio1"); rad1("side") := "left"; rad1("variable,value") := "radiovar,Radio1"; rad2 := tdf("radiobutton","Radio2"); rad2("side") := "left"; rad2("variable,value") := "radiovar,Radio2"; rad1{OM} := print_checks; rad2{OM} := print_checks; tde := td("entry","100"); tde(OM) := "Humongous"; tde("side") := "top"; print("tde('end'): ",tde("end")); tde("insert") := 3; print("tde('insert'): ",tde("insert")); ctex := ntca("text","This is a another canvas text item"); ctex("anchor,font") := "nw,{Times 24 bold}"; ctex("coords") := "10,10"; -- icursor(tag_or_id,m); ctex("insert") := 7; print("ctex('insert'): ",ctex("insert")); print("ctex.index_item('end'): ",ctex.index_item("end")); print("ctex(2..4): ",ctex(2..4));ctex(10..10) := "yyyyyy"; ctex(2..4) := ""; end canvas_tests; procedure print_checks(); -- print the values of all the radio and check button variables print("checkvar1: ",Tk.getvar("checkvar1")," checkvar2: ",Tk.getvar("checkvar2"), " radiovar: ",Tk.getvar("radiovar")); print("selected-checkvar1: ",check1("selected")," selected-checkvar2: ",check2("selected"), " selected-radiovar1: ",rad1("selected")," selected-radiovar2: ",rad2("selected")); end print_checks; procedure set_check1(); -- set the first checkbox check1("selected") := 1; rad1("selected") := 0; end set_check1; procedure set_radio1(); -- set the first radio button rad1("selected") := 1; check1("selected") := 0; end set_radio1; procedure do_tdb(); -- invoke operation of tdb button tdb.invoke_button(); end do_tdb; procedure flash3(); -- flash button 3 tdb3.flash(); end flash3; procedure scale_me(); -- scale item with given tag or id blot.scale_item(0,0,0.5,0.5); end scale_me; procedure close_td(); -- close window td td.destroy(); end close_td; procedure make_response(ijplace); -- create a response closure return lambda(); respond(ijplace); end lambda; end make_response; procedure respond(tag); -- internal handler routine to print tag of clicked image --print("respond: ",tag," ",ntca); ntca.addtag_in("NewTag","10,10,250,250"); ntca.addtag_in("Cosi fan Tutte",OM); ntca.move(tag,1,1); show("clicked: "+ obj_of_tag(tag)("tags")); end respond; procedure show(stg); ntmsg(OM) := stg; end show; procedure basic_tests; -- first group of basic tests nw := Tk("frame","50,50"); c := nw("canvas","150,250"); -- create a frame and canvas nw("side") := "top"; -- pack the frame into the top level window b1 := Tk("button","Put text w. tags"); b2 := Tk("button","Get tags"); -- create 4 buttons in the top window b3 := Tk("button","Tag ranges"); b4 := Tk("button","Tag font"); b1("side") := "left"; b2("side") := "left"; b3("side") := "left"; b4("side") := "left"; -- pack the buttons into the top level window b1{OM} := put_tt; b2{OM} := get_tgs; b3{OM} := get_tgrang; b4{OM} := set_tfont; b1 := nw("button","Bouton 1"); b2 := nw("button","Bouton 2"); -- create 4 buttons in the frame b3 := nw("button","Put 3..5 into Text widget"); b4 := nw("button","Get 3..5 from Text widget"); b1("side") := "left"; b2("side") := "left"; b3("side") := "left"; b4("side") := "left"; -- pack the buttons into the frame b1{OM} := beep; b2{OM} := move_oval; b3{OM} := put_text; b4{OM} := get_text; lab := nw("label","Now is the time for all good men!"); -- create a label in the frame lab("side") := "bottom"; -- pack it into the frame msg := nw("message","Twas Brillig, and the slithy toves\nDid gyre and gymbal in the wabe"); msg("side") := "top"; -- create a message in the frame, and pack it into the frame top := Tk("toplevel","150,50"); -- create a new top-level window top("title") := "Topper"; -- set its title e := top("entry","70"); e("side") := "top"; e("relief") := "raised"; -- create an entry field in the new toplevel, and pack the entry field into the toplevel e(OM) := 10 * "12345678901234567890" + "a" + 10 * "12345678901234567890"; te := top("text","70,10"); te("side") := "top"; te("relief") := "sunken"; -- create a text widget in the new toplevel, and pack the text widget into the toplevel te("tag1",OM) := beep; te("tag2",OM) := show_char; -- te("cursor") := "pirate"; te.insert_tt("end","te.bbox ,tag3,set_scale(0) ,tag4,set_scale(90) ,tag5,to_entry ,tag6, " + "xview(100) ,tag7,xview_percent(50) ,tag8"); te("tag3",OM) := p_bbox; te("tag4",OM) := set_scale0; te("tag5",OM) := set_scale90; te("tag6",OM) := to_entry; te("tag7",OM) := xview100; te("tag8",OM) := xview_perc; sc := top("scale","0,100"); sc("side") := "top"; sc("orient") := "horizontal"; sc("length") := "350"; -- create a slider widget in the new toplevel, and pack the slider into the toplevel --sc("side,orient,length") := "top,horizontal,150"; -- ********** ?????? ********** ca := top("canvas","300,300"); ca("side") := "top"; -- create a canvas in the new toplevel, and pack the canvas into the toplevel imaje := ca("image",img); imaje2 := ca("image",img); -- insert the image into the canvas twice --print("imaje: ",imaje); imaje("coords,anchor") := "50,50;nw"; junk:=printer("imaje('coords'): ",imaje("coords")); imaje{OM} := beep2; imaje2("coords,anchor") := "0,100;nw"; imaje2{OM} := beep; ov := ca("oval","30,30,60,60"); rect := ca("rectangle","70,30,100,60"); -- create an oval and a rectangle in the canvas ov("fill") := "red"; rect("fill") := "blue"; -- make them visible in red and blue -- now go on to create an arc, bitmap, image, line, polygon, text, and widget items in the canvas -- first create an arc, line, and polygon arc := ca("arc","110,30,140,60"); arc("start") := "45"; arc("extent") := "135"; arc("fill") := "green"; line := ca("line","190,30,220,30,225,60,185,60"); line("fill") := "brown"; line("smooth") := "true"; poly := ca("polygon","150,30,180,30,185,60,150,60"); poly("fill") := "yellow"; junk:=printer("poly attributes: ",poly("fill,outline,width,smooth,splinesteps")); junk:=printer("line attributes: ",line("fill,width,smooth,splinesteps")); -- access and print the poly and line attributes junk:=printer("canvas attributes: ",ca("height,width")); -- access and print the canvas attributes rect.addtag("BlueRect"); rect.addtag("LooksGood"); junk:=printer("rect attributes: ",rect("width,tags")); junk:=printer("rect bbox: ",rect("bbox")); junk:=printer("rect and oval bbox: ",ca.bbox_tags("1,2")); rect.deltag("LooksGood"); junk:=printer("rect attributes: ",rect("width,tags")); rect.addtag("LooksVeryGood"); junk:=printer("rect attributes: ",rect("width,tags")); ca.deltag_if("BlueRect","LooksVeryGood"); junk:=printer("rect attributes after deltag_if: ",rect("width,tags")); junk:=printer(str(ca.canvasx(100,OM)) + " " + str(ca.canvasy(100,OM))); ca("BlueRect",OM) := beep; ov{OM} := animate; arc{OM} := del4; -- next create a bitmap and image bm := ca("bitmap","60,90"); bm("bitmap") := "hourglass"; -- use a built-in bitmap -- im := ca("image","100,100"); im("bitmap") := the_image; -- now create a canvas text item and a canvas widget ctex := ca("text","This is a canvas text item"); ctex("anchor,font") := "w,{Times 24 bold}"; ctex("coords") := "100,90"; junk:=printer("ctex('coords'): ",ctex("coords")); junk:=printer("ctex: ",ctex("coords,font")); junk:=printer("rect: ",rect("coords,fill")); bb1 := top("button","Yet another button to press"); -- create a NEW button in the SAME toplevel as the canvas cw := ca("widget",bb1); cw("coords") := "40,40"; -- make this button a canvas widget junk:=printer("cw('coords'): ",cw("coords")); bb1{OM} := movecw; -- bm := ca("bitmap","100,90"); bm("bitmap") := "hourglass"; -- use a built-in bitmap --dchars(tag_or_id,m,n); menub := nw("menubutton","Menu"); menub("side") := "bottom"; -- create a menubutton the_menu := menub("menu","tearoff"); the_menu(1..0) := "Item1,Item2,Item3,Item4"; -- add 4 items to the menu menub("menu") := the_menu; menub := nw("menubutton","Menu2"); menub("side") := "bottom"; -- create a menubutton the_menu2 := menub("menu","normal"); the_menu2(1..0) := "iItem1,iItem2,iItem3,iItem4"; -- add 4 items to the menu menub("menu") := the_menu2; the_menu(1,OM) := beep2; -- set command for first item, "Menu" the_menu(2,OM) := drop4; -- set command for second item the_menu(3,OM) := invoke1; -- set command for third item the_menu2(1,OM) := beep; -- set command for first item, "Menu2" the_menu2(2,OM) := beep2; -- set command for second item the_menu2(3,OM) := beep3; -- set command for third item the_menu2(4,OM) := add5; -- set command for fourth item -- junk:=printer("menu entry info: ",the_menu2(1..1)); -- get label info for range of entries -- junk:=printer("menu entry info: ",the_menu2(2,"label")); -- get specified info for individual entry end basic_tests; procedure pickit; -- call test procedure for listbox command e(OM) := str(lbom := lb(OM)); -- display the current selection msg("text") := str(lbom); -- display the current selection if lbom = 1 then lb(8..7) := "inserted1,inserted2"; end if; if lbom = 2 then e(OM) := "Deleting"; lb(8..9) := ""; end if; if lbom = 3 then e(OM) := lb(8..9); end if; if lbom = 4 then e(OM) := str(#lb(8..9)); end if; -- number of listbox characters -- print(lb(OM)); end pickit; procedure beep; -- call the Tk beep utility TK.beeper(); end beep; procedure beep2; TK.beeper(); TK.beeper(); end beep2; procedure beep3; TK.beeper(); TK.beeper(); TK.beeper(); end beep3; procedure add5; the_menu2(5..4) := "More"; end add5; procedure drop4; the_menu(4..4) := ""; end drop4; procedure invoke1; the_menu.invoke(1); end invoke1; -- procedures for text widget tests procedure get_text; e(OM) := te("1.3".."1.5"); end get_text; procedure put_text; te("1.3".."1.5") := e(OM); end put_text; procedure put_tt(); te.insert_tt("end","This ,tag1, that ,tag2, the other"); end put_tt; procedure get_tgs(); print(te.tag_names(OM)); end get_tgs; -- get all the tags in the text widget procedure get_tgrang(); print(te("tag","tag1")); end get_tgrang; -- get the range of tag 1 procedure set_tfont(); te("tag1","font") := "{times 48 bold}"; te.mark_set("Jill","1.3"); print("search for 'His' ",te.search("forward,nocase","His","1.0","1.10")); print("search for 'His', exact case ",te.search("forward","His","1.0","1.10")); print("search for 'zm' ",te.search("forward,nocase","zm","1.0","1.10")); end set_tfont; procedure show_char(); print(te.index("current")," ",te("marks")," ",te.mark_next(1.0), " ",te.mark_next(1.10)," ",te.mark_prev(1.10)); end show_char; procedure p_bbox(); e(OM) := te.bbox("current"); end p_bbox; procedure set_scale0(); sc(OM) := 0; end set_scale0; procedure set_scale90(); sc(OM) := 90; [i,j] := sc.coords(90); e(OM) := sc.get(i,j) + " " + sc.identify(i,j) + " " + sc.identify(unstr(i) - 30,j) + " " + sc.identify(unstr(i) + 30,j); end set_scale90; procedure xview100(); e("xview") := 100; end xview100; procedure xview_perc(); e("xpercent") := 50; end xview_perc; procedure del4(); ca.delete_items("4"); end del4; procedure to_entry(); e(4..5) := "aa"; e(2..1) := "aa"; e.select(5,10); print("selection: ",e.index("sel.first")," ",e.index("sel.last")," ",e.index("sel.last") /= OM); end to_entry; procedure animate(); -- animate the photo print("starting photo: ",time()); for j in [50,49..1] loop -- 1.5 seconds for 50 cycles, so about 30 cycles/sec. imaje2("coords") := str(j) + "," + str(j); Tk.update(); end loop; print("ending photo: ",time()); end animate; procedure movecw(); -- animate the button print("starting button: ",time()); for j in [50..300] loop -- 6.5 seconds for 250 cycles, so about 35 cycles/sec. cw("coords") := str(j) + "," + str(j); Tk.update(); end loop; print("ending button: ",time()); end movecw; procedure move_oval(); -- move the oval ov("coords") := "200,200,300,300"; -- note that canvas geometric objects must have an appropriate number of coordinates end move_oval; end test; -- *********** Syntactic and semantic conventions for the SETL widget class *********** -- The SETL widget class is built on top of the Tk widget library, and the following discussion assumes -- some basic familirity with the operation of that library. The SETL widget class supports objects -- of the single SETL class 'Tk' but of multiple internally distinguished kinds, corresponding to -- the kinds of Tk widgets and canvas items, namely button, menu, menubutton, frame, toplevel, -- label, message, scale, scrollbar, entry, listbox, text, canvas, and arc, bitmap, image, line, oval, -- polygon, rectangle, canvas_text, and widget. -- To initialize for use of SETL widgets one must create an initial widget object (the 'Tk interpreter') -- Once this is done, additional widgets are created by calls of the form -- new_widget := p(tk_type,principal_parameter), for example b := Tk("button","Click Me") -- Here, p should be the intended parent widget of the new_widget, i.e. the one into which it will be -- placed by a subsequent geometry manager call. The principal_parameter required here depends in -- the following way on the type of the widget or other item being created: -- (1) buttons, menubuttons, labels, and messages: the text to be displayed by the widget -- (2) frames, toplevels, text widgets, and canvases: the height and width, in the format height,width -- (3) canvas items: the height and width, in the format height,width -- (4) single-line text entry fields: the width, in characters -- (5) listboxes: the height, in lines -- (6) menus: the type, i.e. normal, tearoff, or cascade -- (7) scrollbars: the orientation, i.e. horrizontal or vertical -- (8) scale (slider) widgets: the slider's lower and upper values, as lower,upper -- Once a widget or canvas item w has been created, it can be 'configured' (i.e its attributes can be set -- or queried) by calls of the form -- w("attr1,attr2,...") := "attr_val_1,attr_val_2,..."; -- where attr1,attr2,... is a list of widget attribute names, and attr_val_1,attr_val_2,... are -- the corresponding attribute values. A comma-separated list of values can be used, but if commas -- must occur in the attributes themselves, a semicolon-separated list of values can be used instead. -- The semicolon is used as separator if any semicolons occur in the string on the right. A third -- possiblity is to use a tuple of values on the right, e.g. -- w("attr1,attr2,...") := [attr_val_1,attr_val_2,...]; -- This form is appropriate if both commas and semicolons occur in some of the attributes, or if some -- of the attributes are procedures rather than strings, as in the -- w{OM} := procedure; -- case described below. A final option is to write integer-valued attributes as integers, as e.g. in -- my_listbox("height") := 12; -- Since Tk uses blanks as separators list-valued attributes (these are somewhat rare) can be written in that form, -- as e.g in -- my_text_widget("tabs,padx,pady") := "1i left 2i right 31 center,5,5"; -- To define a widget's geometry manager, i.e. make it visible in the proper place within its parent widget, -- calls of much the same syntactic form are used. Specifically, these are -- w("pack,attr1,attr2,...") := "attr_val_1,attr_val_2,..."; -- w("grid,attr1,attr2,...") := "attr_val_1,attr_val_2,..."; -- w("place,attr1,attr2,...") := "attr_val_1,attr_val_2,..."; -- These calls respectively invoke the Tk pack, grid, and place geometry managers. If the attribute name 'side', -- which occurs only for the 'pack' geometry manager, or one of the the attribute names 'row' or 'column', -- which occurs only for the 'grid' geometry manager, appears at the start of the attribute list, 'pack' or -- 'grid' can be omitted. For example, one can write -- my_button("side,padx,pady") := "top,5,5"; -- to pack a button into its parent frame. Note that only pack, grid, or place geometry manager options can -- appear in the left-hand attribute lists of such calls. -- Syntactic constructs like -- w("attr1,attr2,...") -- can also be used on the right-hand side of SETL assignments. This returns a tuple consisting of the values of -- the attributes listed. -- In some cases SETL extends an object's Tk list of attributes to treat quatities set by special Tk operations -- using this same 'attribute' syntax. An example is the insertion cursor position attribute of entry widgets and -- canvas text items, for which the syntax n := obj("insert") and obj("insert") := n is provided; likewise -- obj("end"), etc. -- Means for reading and setting the values of Tk variables, for example the special variables which -- Tk associates with groups of radio buttons, are also provided, and use much the same syntax. Specifically, -- if Tk is the master Tk interpreter object returned by the obligatory initial call Tk := tkw() (see above), -- then -- Tk("varname") -- returns, and -- Tk("varname") := val; -- sets, the value of the Tk variable 'varname'. -- Each kind of Tk widget and canvas object can be assigned a parameterless SETL 'command' -- callback procedure which is invoked by the 'principal' event to which the widget or object is sensitive. -- these 'principal' events are determined in the following way by the type of the widget or item: -- (1) buttons, menubuttons, checkbuttons, radiobuttons, and canvas items: -- button press events () -- (2) menus and listboxes: button release events () -- (3) text fields and 1-line entry fields loss of focus events () -- (4) canvases,frames, and toplevels dragging motions () -- These commands are set up using the syntax -- obj{OM} := SETL_procedure; -- In a few special cases, the syntactic forms obj(OM) and obj(OM) := val; are used to retrive or assign an -- widget's principal attribute. These depend in the following way on the widget kind: -- (1) listboxes: obj(OM) is the (first) currently selected item -- (2) text fields and 1-line entry fields obj(OM) is all the text -- (3) text fields and 1-line entry fields obj(OM) := stg sets all the text -- (4) labels and messages obj(OM) := stg sets all the text -- The following operations, which use the SETL string and tuple 'slice' syntax are provided for manipulating -- text within one-line and multi-line text widgets and various other widget-associated lists. -- (1) text fields obj(i..j) is the text between positions i and j -- note that these positions can have built-in forms (always string names) like line.char, 'end', etc., -- and can also be strings defined by the obj.mark_set(name,place) command. These 'character indices' -- can also carry modiers, from which they are eparated by blanks. The allowed modifiers are + n chars, -- - n chars, + n lines, - n lines, linestart, lineend, wordstart, and wordend. -- (2) 1-line entry fields obj(i..j) is the text characters i and j -- (3) menus and listboxes: the labels of all items between i and j -- (4) text fields obj(i..j) := stg sets the text between positions i and j -- (5) 1-line entry fields obj(i..j) := stg sets the text characters i and j -- The syntax '#obj" is used for listbox, menus, text fields, for which it designates the number of entries or lines, -- and for 1-line entry fields, where it designates the number of characters. -- Additional comments on images, canvas items and text items: Images, canvas items and text items are treated as -- 'pseudo widgets', in that they are represented by objects of the tkw class. Canvas items can be: -- (1) geometric objects like ovals and rectangles; (2) images; (3) canvas text objects; (4) canvas widgets, --i.e. arbitrary widgets ebedded in a canvas; this can include nested canvases. -- Text items can be images or text widgets. -- The attributes of canvas items are: tags, and coords in all cases, plus -- for canvas geometric objects: width, fill, outline, and stipple -- for canvas images: anchor and image -- for canvas text objects: anchor, width, fill, font, justify, stipple, and text -- for canvas widgets: width, anchor, height, and window -- The attributes of images and widgets in text are: -- tags in all cases (calculated by regarding the image or widget as a character); also align, padx, pady; plus -- for text images: image, name -- for text widgets: create, stretch, window -- The creation calls for items of this kind are: -- cgeom := ca("oval,etc","coords"); ct := ca("text","initial_text"); cim := ca("image","image_name"); -- cw := ca("widget",widget_object); -- tim := te("image","image_name"); tw := te("widget",widget_object); -- Note that in the case of a canvas geometric object the coords parameter is a comma-separated list of integers -- (defining either rectagle corners or polygon points). To create embedded widgets either in a canvas or in text -- we must supply not a string but a pre-existing widget object as the second creation parameter; in this case -- the creation call establishes a pointer relationship between the canvas or text and this object. -- The embedded objects are then configured using calls of the standard form obj("att,att,...") := values or -- values := obj("att,att,..."). However, since the names anchor, width, height, padx, and pady are used both -- for inherent and relative attributes, the relative variants of these attributes must be retrieved and set -- by using the modified names rel_anchor, rel_width, rel_height, rel_padx, and rel_pady. -- For canvas geometric objects, canvas images, and canvas text objects the configuring assignment -- obj{OM} := SETL_proc; is allowed. This is also allowed for text images. The implied 'principal event' -- is for the canvas objects, for images in text. Embedded widgets have their -- own principal commands, -- Note that Tk only allows a widget to be used in one place: it can be packed, used as a canvas widget -- in a single canvas but not in two such, etc. If these implicit semantic rules are violated, unpredictable -- graphical effects will result. Also, a widget created as a child of one toplevel cannot be used within -- a canvas or text of a different toplevel. SETL uses of widgets must obey these same rules. (But none -- of these restrictive rules apply to images.) Principal commands and other event responses intended -- for embedded widgets should be assigned to the underlying widget, not to the canvas widget item -- containing them. -- Tk image (photo image) objects should be created by a creation call using the top-level object Tk as parent, -- e.g. img := Tk("image","file_name"); Images can then be configured in the same way as other objects, -- that is by writing img("atts...") := "att_vals..."; The attributes of photo images are format, channel, data, -- file, gamma, palette, height, and width. Assignment to the special attribute "file", e.g. in the form -- img("file") := "file_name..." reads a new file into the image. img("file") := OM; clears the image. -- img("data") := tuple_of_strings writes an image from calculated data. img(top_left..bottom_right) -- creates a new image by extracting the designated section of img; img(top_left..bottom_right) := img2 -- inserts img2 into the designated section of img. Note that when initially read an image has no defined -- height or width, and so one can write to any part of the quadrant it occupies -- Event handlers for the various kinds of mouse, keyboard, and other events to which Tk widgets and canvas items -- are sensitive are set up by writing nominal assignments of one of the two the syntactic forms -- widget("event_descriptor,event_fields_signature") := SETL_procedure; -- textfield("tag_name","event_descriptor,event_fields_signature") := SETL_procedure; -- canvas("tag_name","event_descriptor,event_fields_signature") := SETL_procedure; -- The first form is used to assign callback procedures to widget-related events, and the second is used to -- assign callback procedures to tags in text fields and canvases. Examples are: -- top_frame(":x,y") := my_drag_procedure; -- text_widget("tag_name","") := my_hypertext_click_procedure; -- In the general case, the event_fields_signature is a concatenation of single characters drawn from the -- string "#abcdfhkmopstvwxyABEKNRSTWXY"; these have the following meanings: -- # event serial number all events -- a 'above' field for event configure event -- b button number buttonpress, buttonrelease -- c event count field expose, map -- d event detail field enter, leave, focusin, focusout -- f focus value established by event enter, leave -- h height field configure, expose -- k keycode keypress, keyrelease -- m mode enter, leave, focusin, focusout -- o override redirect map, reparent, configure -- p place placeontop, placeonbottom, circulate -- s state buttonpress, buttonrelease, enter, leave, keypress, keyrelease, motion -- t time all events -- v value mask configure event -- w width configure, expose -- x horizontal position, widget relative all mouse events -- y vertical position, widget relative all mouse events -- A printing character keypress, keyrelease -- B border width configure event -- E sendevent field all events -- K key symbol keypress, keyrelease -- N key symbol in decimal keypress, keyrelease -- R root window id all events -- S subwindow id all events -- T type field all events -- W pathname of widget receiving the event all events -- X horizontal position, absolute buttonpress, buttonrelease, keypress, keyrelease, motion -- Y vertical position, absolute buttonpress, buttonrelease, keypress, keyrelease, motion -- The sequence of event fields designated by the characters in event_fields_signature is grouped into a -- parameter list and transmitted to SETL_procedure used as callback; this must of course have -- the appropriate number of parameters. -- As explained above, the 'principal' event associated with each kind of widget and canvas item -- can also be defined using the syntax -- obj{OM} := SETL_procedure; -- The basic event types supported by Tk are , , , , , -- , , , , , , , , , -- , , ,