############################################################################ # # vibmenu.icn -- procedures for defining a menu object # ############################################################################ # # This file is in the public domain. # ############################################################################ $include "vdefns.icn" $include "vibdefn.icn" global startyMENU, MENU_TALK global MENU_VIDGET global reg_list, ins_list global SIM_TAB ########################################################################## # menu_obj: # v : vidget used for drawing menu # proc : name of user callback procedure # id : unique means of identifying instance # x,y,w,h : bounding box # label : menu button label # lx,ly : label coordinates # style : style of menu ... currently only pull down is supported # focus : should focus lines be drawn around this object? # items : a list of menu items that make up the menu # # menu_item: # label : menu choice name # items : a list of menu_items for a submenu, or an empty list # # menu_id: # v : text vidget for label field # item : corresponding menu_item record ########################################################################## record menu_obj(v, proc, id, x, y, w, h, label, lx, ly, style, focus, items) record menu_item(label, items) record menu_id(tv, item) ########################################################################## # create_menu() creates a menu instance and draws the menu button. ########################################################################## procedure create_menu(x, y, label, style) local r, id id := next_id("menu") /style := "pull" r := menu_obj(, "menu_cb" || id, "menu" || id, x, y, 0, 0, label, 0, 0, style, 0, []) r.v := Vbutton(ROOT, x, y, APPWIN, label, , id, V_RECT) VRemove(ROOT, r.v, 1) return r end ########################################################################## # copy_submenu() recursively copies a cascading menu. ########################################################################## procedure copy_submenu(old, temp) local i /temp := copy(old) if *old.items > 0 then { temp.items := [] every put(temp.items, copy_submenu(!old.items)) } return temp end ########################################################################## # copy_menu() makes a copy of a menu old and returns it in new. ########################################################################## procedure copy_menu(new, old) every put(new.items, copy_submenu(!old.items)) end ########################################################################## # add_item() adds a menu choice with name "label" to the menu at the # location indicated by "after". ########################################################################## procedure add_item(menu, label, after) local choice after >:= *menu.items choice := menu_item(label, []) menu.items := menu.items[1:after+1] ||| [choice] ||| menu.items[after+1:0] end ########################################################################## # update_menu_bb() updates various attributes of the menu that # change when the menu button label is altered. ########################################################################## procedure update_menu_bb(object) object.w := object.v.aw # disallow changes object.h := object.v.ah # .lx/.ly values must agree with locations drawn by menu vidgets # else the simulation of a menu leaves the label in the wrong place # and moving the menu then leaves debris behind on the screen object.lx := object.x + 4 object.ly := object.y + WAttrib(APPWIN, "ascent") + 4 end ########################################################################## # draw_menu() draws the given menu button object. ########################################################################## procedure draw_menu(r) VResize(r.v, r.x, r.y, r.w, r.h) VDraw(r.v) return r end ########################################################################## # load_menu() restores a menu object from session code. ########################################################################## procedure load_menu(r, o) r.style := o.sty r.label := o.lbl r.items := load_submenu(o.etc) r.v := Vbutton(ROOT, r.x, r.y, APPWIN, r.label, , r.id, V_RECT) VRemove(ROOT, r.v, 1) end ########################################################################## # load_submenu() restores a menu or submenu list. ########################################################################## procedure load_submenu(spec) local i, r, lst lst := [] while i := get(spec) do { r := menu_item() r.label := i if type(spec[1]) == "list" then { r.items := load_submenu(get(spec)) } else r.items := [] put(lst, r) } return lst end ########################################################################## # save_menu() augments the record for saving a menu object. ########################################################################## procedure save_menu(r, o) r.typ := "Menu" r.lbl := o.label r.sty := o.style r.etc := save_submenu(o.items) return end ########################################################################## # save_submenu() builds a list representing a submenu. ########################################################################## procedure save_submenu(items) local l, i l := [] every i := !items do { put(l, i.label) if *i.items > 0 then put(l, save_submenu(i.items)) } return l end ########################################################################## # simulate_sub_menu() is called by simulate_menu to recursively construct # WIT submenus and place them in a table for reference # by simulate_menu(). ########################################################################## procedure simulate_sub_menu(obj, label) local i, temp_list every i := 1 to *obj.items do { if *obj.items[i].items > 0 then simulate_sub_menu(obj.items[i], label || "_" || obj.items[i].label) } temp_list := [&window] every i := 1 to *obj.items do { put(temp_list, obj.items[i].label) if *obj.items[i].items > 0 then put(temp_list, SIM_TAB["id_" || label || "_" || obj.items[i].label]) else put(temp_list, &null) # null callback } SIM_TAB["id_" || label] := Vsub_menu ! temp_list end ########################################################################## # simulate_menu() creates a complete WIT menu object so that the # VIB user can see what the menu looks like without # prototyping. ########################################################################## procedure simulate_menu(obj) local i, temp_list, sim_menu, tmp SIM_TAB := table() every i := 1 to *obj.items do { if *obj.items[i].items > 0 then simulate_sub_menu(obj.items[i], obj.items[i].label) } temp_list := [&window] every i := 1 to *obj.items do { put(temp_list, obj.items[i].label) if *obj.items[i].items > 0 then put(temp_list, SIM_TAB["id_" || obj.items[i].label]) else put(temp_list, &null) # null callback } sim_menu := Vmenu_bar_item(&window, obj.label, , , , , Vsub_menu ! temp_list) tmp := ScratchCanvas(ROOT.win, obj.w, obj.h) CopyArea(ROOT.win, tmp, obj.x, obj.y, obj.w, obj.h) VInsert(ROOT, sim_menu, obj.x, obj.y) VResize(sim_menu) VEvent(sim_menu, &mpress) VRemove(ROOT, sim_menu, 1) CopyArea(tmp, ROOT.win, 0, 0, obj.w, obj.h, obj.x, obj.y) EraseArea(tmp) end ########################################################################## # menu_atts() defines the attribute sheet template for a menu object. ########################################################################## procedure menu_atts() local tempy MENU_TALK := Vdialog(&window, PAD, PAD) tempy := 0 VRegister(MENU_TALK, Vtext(&window, "menu label: ", , 1, TEXTCHARS, LBMASK), 0, tempy) tempy +:= PAD VRegister(MENU_TALK, Vtext(&window, "ID: ", , 2, TEXTCHARS, IDMASK), 0, tempy) tempy +:= PAD VRegister(MENU_TALK, Vtext(&window, "callback: ", , 3, TEXTCHARS, CBMASK), 0, tempy) VRegister(MENU_TALK, Vtext(&window, "x: ", , 4, 3, &digits), 80 + TEXTWIDTH + 10, 0) VRegister(MENU_TALK, Vtext(&window, "y: ", , 5, 3, &digits), 80 + TEXTWIDTH + 10, PAD) VFormat(MENU_TALK) startyMENU := tempy end ########################################################################## # display_menu_atts() displays the attribute sheet with the current # attributes for the given menu instance. ########################################################################## procedure display_menu_atts(object) local i, data, send_data, new, v, dw, l initial menu_atts() new := copy(object) new.y -:= CANVASY new.items := [] copy_menu(new, object) repeat { menu_list_atts(MENU_TALK, startyMENU, new.items) VFormat(MENU_TALK) MENU_VIDGET := &null send_data := [new.label, new.id, new.proc, new.x, new.y] every put(send_data, (!new.items).label) data := VOpenDialog(MENU_TALK, , "menu_dialog", send_data, "Okay") every VUnregister(MENU_TALK, !reg_list) every VRemove(MENU_TALK, !ins_list, 1) if data === send_data then fail # cancelled new.label := strip(get(data)) new.id := strip(get(data)) new.proc := strip(get(data)) new.x := get(data) new.y := get(data) every (!new.items).label := get(data) # if "add" or "del" was pressed, process it and loop to re-post dialog if \MENU_VIDGET then { l := [] every i := 1 to *new.items do { v := reg_list[i] if v.ay - PAD < MENU_VIDGET.ay-1 < v.ay then put(l, menu_item("", [])) if v.ay ~= MENU_VIDGET.ay-1 then put(l, new.items[i]) } if MENU_VIDGET.ay-1 > reg_list[*new.items].ay | *l = 0 then put(l, menu_item("", [])) new.items := l next } # check for legal field values if illegal(new.id, "ID", "s") | illegal(new.label, "Label", "l") | illegal(new.proc, "Callback", "p") | illegal(new.x, "X", "i") | illegal(new.y, "Y", "i") then fail # everything is valid dw := VFWidth * (*new.label - *object.label) object.label := new.label object.id := new.id object.proc := new.proc object.items := new.items object.v.s := object.label object.v.aw := object.w + dw unfocus_object(object) move_object(object, new.x, new.y + CANVASY, object.w, object.h) focus_object(object) break } end ########################################################################## # display_submenu_atts() displays the attribute sheet with the current # attributes for the given submenu instance. ########################################################################## procedure display_submenu_atts(button, val) local submenu_talk, send_data, data, old_reg, old_ins local entry, items, s, i, v old_reg := reg_list old_ins := ins_list entry := button.id.item items := copy(entry.items) if *items = 0 then every 1 to 3 do put(items, menu_item("", [])) repeat { submenu_talk := Vdialog(&window, PAD, PAD) v := Vmessage(&window, "\"" || button.id.tv.data || \"\" submenu entries") VInsert(submenu_talk, v, 0, 0) menu_list_atts(submenu_talk, 0, items) VFormat(submenu_talk) MENU_VIDGET := &null send_data := [] every put(send_data, (!items).label) data := VOpenDialog(submenu_talk, , "submenu_dialog", send_data, "Okay") every VUnregister(MENU_TALK, !reg_list) every VRemove(MENU_TALK, !ins_list, 1) if data === send_data then { reg_list := old_reg ins_list := old_ins fail # cancelled } every (!items).label := get(data) # update new labels if *(items := update_menu_list(items)) > 0 then next # loop to re-post dialog # the revised list has been accepted entry.items := items VErase(button) if *items = 0 then s := "create submenu" else s := "edit submenu (" || *items || ")" button.aw +:= VFWidth * (*s - *button.s) button.s := s VResize(button) VDraw(button) break } reg_list := old_reg ins_list := old_ins end ########################################################################## # menu_list_atts() adds the menu items (with add/del/submenu buttons) # and okay/cancel buttons to a dialog box. # ins_list and reg_list are set. ########################################################################## procedure menu_list_atts(menu, y, itemlist) local i, s, v, id # construct text fields with "add", "del", and "submenu" buttons reg_list := [] ins_list := [] every i := 0 to *itemlist do { y +:= PAD v := Vbutton(&window, "add", menu_mod_cb, V_OK, , 28, 17) VInsert(menu, v, 0, y + PAD / 2) put(ins_list, v) if i = 0 then next v := Vbutton(&window, "del", menu_mod_cb, V_OK, , 28, 17) VInsert(menu, v, 35 + TEXTWIDTH, y + 1) put(ins_list, v) v := Vtext(&window, "", , 100 + i, TEXTCHARS, LBMASK) VRegister(menu, v, 35, y) put(reg_list, v) id := menu_id(v, itemlist[i]) if *itemlist[i].items = 0 then s := "create submenu" else s := "edit submenu (" || *itemlist[i].items || ")" v := Vbutton(&window, s, display_submenu_atts, id, , , 17) VInsert(menu, v, 35 + TEXTWIDTH + 40, y + 1) put(ins_list, v) } # add "Okay" and "Cancel" y +:= 2 * PAD v := Vbutton(&window, "Okay", , V_OK, , 50, 30) VInsert(menu, v, TEXTWIDTH / 2 + 30, y) put(ins_list, v) v := Vbutton(&window, "Cancel", , V_CANCEL, , 50, 30) VInsert(menu, v, TEXTWIDTH / 2 + 100, y) put(ins_list, v) end ########################################################################## # update_menu_list() creates a new item list reflecting adds and deletes. ########################################################################## procedure update_menu_list(oldlist) local newlist, v, i if /MENU_VIDGET then fail newlist := [] every i := 1 to *oldlist do { v := reg_list[i] if v.ay - PAD < MENU_VIDGET.ay-1 < v.ay then put(newlist, menu_item("", [])) if v.ay ~= MENU_VIDGET.ay-1 then put(newlist, oldlist[i]) } if MENU_VIDGET.ay-1 > reg_list[*oldlist].ay then put(newlist, menu_item("", [])) MENU_VIDGET := &null return newlist end ########################################################################## # menu_mod_cb is called when an "add" or "del" button is pressed. ########################################################################## procedure menu_mod_cb(v) MENU_VIDGET := v end