############################################################################ # # File: dialog.icn # # Subject: Procedures for dialogs # # Authors: Ralph E. Griswold and Gregg M. Townsend # # Date: December 14, 1999 # ############################################################################ # # This file is in the public domain. # ############################################################################ # # This file contains several procedures for posting dialog boxes: # # AskDialog() -- TextDialog() with only caption and "No" instead of "Cancel" # Notice(win, captions) -- notice dialog (a simple text dialog) # TextDialog(win, captions, labels, defaults...) -- text dialog # ToggleDialog(win, captions, labels, defaults...) -- toggle dialog # SelectDialog(win, captions, labels, defaults...) -- selection dialog # SaveDialog(win, caption, filename, len) -- save file dialog # OpenDialog(win, caption, filename, len) -- open file dialog # ColorDialog(win, captions, refcolor, callback, id) -- color dialog # # In all cases, the first or only caption is used as a dialog box ID, # used to remember the dialog box location when it is closed. A later # posting using the same ID places the new box at the same location. # ############################################################################ # # ColorDialog(win, captions, color, callback, id) -- display color dialog # # captions list of dialog box captions; default is ["Select color:"] # color reference color setting; none displayed if not supplied # callback procedure to call when the setting is changed # id arbitrary value passed to callback # # ColorDialog displays a dialog window with R/G/B and H/S/V sliders for # color selection. When the "Okay" or "Cancel" button is pressed, # ColorDialog returns the button name, with the ColorValue of the final # settings stored in the global variable dialog_value. # # If a callback procedure is specified, callback(id, k) is called whenever # the settings are changed; k is the ColorValue of the settings. # ############################################################################ # # Popup(x, y, w, h, proc, args...) creates a subwindow of the specified # size, calls proc(args), and awaits its success or failure. Then, the # overlaid area is restored and the result of proc is produced. &window, # as seen by proc, is a new binding of win in which dx, dy, and clipping # have been set. The usable area begins at (0,0); its size is # (WAttrib(win, "clipw"), WAttrib(win, "cliph")). Defaults are: # x, y positioned to center the subwindow # w, h 250, 150 # proc Event # ############################################################################ # # Requires: Version 9 graphics # ############################################################################ # # Links: graphics, vbuttons, vdialog, vradio, vslider, vidgets # ############################################################################ link graphics link vbuttons link vdialog link vradio link vslider link vidgets $include "vdefns.icn" global dialog_button global dialog_value $define ButtonWidth 50 # minimum button width $define ButtonHeight 30 # button height $define FieldWidth 10 # default field width $define OpenWidth 50 # default field width for Open/SaveDialog $define XOff 0 # offset for text vidgets $define XOffButton 85 # initial x offset for buttons $define XOffIncr 15 # space between buttons procedure Dialog(win, captions, labels, defaults, widths, buttons, index) Dialog := TextDialog return Dialog(win, captions, labels, defaults, widths, buttons, index) end procedure AskDialog(win, caption) return TextDialog(win, caption, , , , , ["Okay", "No"]) end procedure TextDialog( #: text dialog win, captions, labels, defaults, widths, buttons, index ) local maxl, lead, pad, default_button, i, maxw, cwidth, id, label_width local button, maxb, dialog, x, y, button_space, default_width, box_id local temp_win static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then { win :=: captions :=: labels :=: defaults :=: widths :=: buttons :=: index win := &window /win := temp_win := WOpen("canvas=hidden", "bg=" || VBackground) } /captions := [] /labels := [] /defaults := [] /widths := [] /buttons := ["Okay", "Cancel"] /index := 1 if type(captions) ~== "list" then captions := [captions] if type(labels) ~== "list" then labels := [labels] if type(defaults) ~== "list" then defaults := [defaults] if type(widths) ~== "list" then widths := [widths] if type(buttons) ~== "list" then buttons := [buttons] default_button := buttons[index] # null if out of bounds default_width := widths[-1] | FieldWidth maxl := 0 every maxl <:= *(labels | defaults | widths) until *labels = maxl do put(labels, labels[-1] | "") until *defaults = maxl do put(defaults, defaults[-1] | "") until *widths = maxl do put(widths, widths[-1] | 10) id := 0 label_width := 0 every label_width <:= TextWidth(win, !labels) if label_width > 0 then label_width +:= 15 maxb := 0 every maxb <:= TextWidth(win, !buttons) maxb +:= 10 maxb <:= ButtonWidth lead := WAttrib(win, "leading") pad := 2 * lead cwidth := WAttrib(win, "fwidth") dialog := Vdialog(win, pad, pad) maxw := 0 every maxw <:= TextWidth(win, !captions) y := -lead every VInsert(dialog, Vmessage(win, !captions), 0, y +:= lead) every i := 1 to maxl do { y +:= pad if *labels[i] > 0 then VInsert(dialog, Vmessage(win, labels[i]), 0, y) VRegister(dialog, Vtext(win, "", , id +:= 1, widths[i]), label_width, y) maxw <:= label_width + widths[i] * cwidth } y +:= (3 * pad) / 2 button_space := maxb * *buttons + XOffIncr * (*buttons - 1) maxw <:= button_space x := ((maxw - button_space) / 2) every button := !buttons do { VInsert(dialog, Vbutton(win, button, dialog_cb, V_OK, , maxb, ButtonHeight), x, y) x +:= maxb + XOffIncr } VFormat(dialog) box_id := captions[1] | "TextDialog" dialog_value := VOpenDialog(dialog, , box_id, defaults, default_button) WClose(\temp_win) return dialog_button end procedure ToggleDialog( #: toggle dialog win, captions, labels, defaults, buttons, index ) local maxl, lead, pad, default_button, i, maxw, cwidth, id, label_width local button, maxb, dialog, x, y, button_space, default_width, box_id local temp_win static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then { win :=: captions :=: labels :=: defaults :=: buttons :=: index win := &window /win := temp_win := WOpen("canvas=hidden", "bg=" || VBackground) } /captions := [] /labels := [] /defaults := [] /buttons := ["Okay", "Cancel"] /index := 1 if type(captions) ~== "list" then captions := [captions] if type(labels) ~== "list" then labels := [labels] if type(defaults) ~== "list" then defaults := [defaults] if type(buttons) ~== "list" then buttons := [buttons] default_button := buttons[index] # null if out of bounds maxl := 0 every maxl <:= *(labels | defaults) every maxl <:= *labels until *labels = maxl do put(labels, labels[-1] | "") until *defaults = maxl do put(defaults, defaults[-1] | &null) id := 0 label_width := 0 every label_width <:= TextWidth(win, !labels) if label_width > 0 then label_width +:= 30 maxb := 0 every maxb <:= TextWidth(win, !buttons) maxb +:= 10 maxb <:= ButtonWidth lead := WAttrib(win, "leading") pad := 2 * lead cwidth := WAttrib(win, "fwidth") dialog := Vdialog(win, pad, pad) maxw := 0 every maxw <:= TextWidth(win, !captions) y := -lead every VInsert(dialog, Vmessage(win, !captions), 0, y +:= lead) every i := 1 to maxl do { y +:= pad VRegister(dialog, Vtoggle(win, labels[i], , id +:= 1, V_CHECK_NO, label_width), 0, y) maxw <:= label_width } y +:= (3 * pad) / 2 button_space := maxb * *buttons + XOffIncr * (*buttons - 1) maxw <:= button_space x := ((maxw - button_space) / 2) every button := !buttons do { VInsert(dialog, Vbutton(win, button, dialog_cb, V_OK, , maxb, ButtonHeight), x, y) x +:= maxb + XOffIncr } VFormat(dialog) box_id := captions[1] | "ToggleDialog" dialog_value := VOpenDialog(dialog, , box_id, defaults, default_button) WClose(\temp_win) return dialog_button end procedure SelectDialog( #: selection dialog win, captions, labels, deflt, buttons, index ) local maxl, lead, pad, default_button, i, maxw, cwidth, label_width local button, maxb, dialog, x, y, button_space, box_id local temp_win static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then { win :=: captions :=: labels :=: deflt :=: buttons :=: index win := &window /win := temp_win := WOpen("canvas=hidden", "bg=" || VBackground) } /captions := [] /labels := [] /buttons := ["Okay", "Cancel"] /index := 1 if type(captions) ~== "list" then captions := [captions] if type(labels) ~== "list" then labels := [labels] if type(buttons) ~== "list" then buttons := [buttons] default_button := buttons[index] # null if out of bounds maxl := 0 every maxl <:= *labels until *labels = maxl do put(labels, labels[-1] | "") label_width := 0 every label_width <:= TextWidth(win, !labels) if label_width > 0 then label_width +:= 15 maxb := 0 every maxb <:= TextWidth(win, !buttons) maxb +:= 10 maxb <:= ButtonWidth lead := WAttrib(win, "leading") pad := 2 * lead cwidth := WAttrib(win, "fwidth") dialog := Vdialog(win, pad, pad) maxw := 0 every maxw <:= TextWidth(win, !captions) y := -lead every VInsert(dialog, Vmessage(win, !captions), 0, y +:= lead) y +:= 2 * lead VRegister(dialog, Vvert_radio_buttons(win, labels, , 1, V_DIAMOND_NO), 0, y) y +:= integer(0.83 * (pad * (*labels - 1)) + 1.5 * pad) button_space := maxb * *buttons + XOffIncr * (*buttons - 1) maxw <:= button_space x := ((maxw - button_space) / 2) every button := !buttons do { VInsert(dialog, Vbutton(win, button, dialog_cb, V_OK, , maxb, ButtonHeight), x, y) x +:= maxb + XOffIncr } VFormat(dialog) box_id := captions[1] | "ToggleDialog" dialog_value := VOpenDialog(dialog, , box_id, [deflt], default_button)[1] WClose(\temp_win) return dialog_button end procedure Notice(captions[]) #: notice dialog local win, temp_win static type initial type := proc("type", 0) # protect attractive name if type(captions[1]) == "window" then win := get(captions) else { win := &window /win := temp_win := WOpen("canvas=hidden", "bg=" || VBackground) } TextDialog(win, captions, , , , "Okay") dialog_value := &null WClose(\temp_win) return dialog_button end procedure SaveDialog(win, caption, filename, len) #: save dialog local temp_win static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then { win :=: caption :=: filename :=: len win := &window /win := temp_win := WOpen("canvas=hidden", "bg=" || VBackground) } /caption := "Save:" /filename := "" /len := OpenWidth TextDialog(win, caption, , filename, len, ["Yes", "No", "Cancel"]) dialog_value := dialog_value[1] WClose(\temp_win) return dialog_button end procedure OpenDialog(win, caption, filename, len) #: open dialog local temp_win static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then { win :=: caption :=: filename :=: len win := &window /win := temp_win := WOpen("canvas=hidden", "bg=" || VBackground) } /caption := "Open:" /filename := "" /len := OpenWidth TextDialog(win, caption, , filename, len) dialog_value := dialog_value[1] WClose(\temp_win) return dialog_button end procedure dialog_cb(vidget, s) dialog_button := vidget.s return end # ColorDialog(win, captions, color, callback, id) -- display color dialog record cdl_rec(rect, orgcolor, refcolor, mutable, callback, id, r, g, b, h, s, v, rv, gv, bv, hv, sv, vv, fg, fillargs, dialog, nc) global cdl_data # data for current color dialog $define PickerWidth 300 # overall color picker width $define SliderHeight 200 # height of a slider $define SliderWidth 15 # width of one slider $define SliderPad 5 # distance between sliders $define MaxStaticCol 200 # maximum colors before recycling procedure ColorDialog( #: color dialog win, captions, refcolor, callback, id ) local x1, x2, dx, y, bw, lead, pad, dialog, box_id, temp_win static type initial type := proc("type", 0) # protect attractive name if type(win) ~== "window" then { win :=: captions :=: refcolor :=: callback :=: id win := &window /win := temp_win := WOpen("canvas=hidden", "bg=" || VBackground) } /captions := "Select color:" if type(captions) ~== "list" then captions := [captions] cdl_data := cdl_rec() cdl_data.callback := callback cdl_data.id := id cdl_data.refcolor := refcolor cdl_data.orgcolor := ColorValue(win, \refcolor | Fg(win) | "gray") cdl_data.orgcolor ? { cdl_data.r := integer(tab(many(&digits))) move(1) cdl_data.g := integer(tab(many(&digits))) move(1) cdl_data.b := integer(tab(many(&digits))) } HSV(cdl_data.orgcolor) ? { cdl_data.h := integer(tab(many(&digits))) move(1) cdl_data.s := integer(tab(many(&digits))) move(1) cdl_data.v := integer(tab(many(&digits))) } lead := WAttrib(win, "leading") pad := 2 * lead y := -lead dialog := Vdialog(win, pad, pad, cdl_init) every VInsert(dialog, Vmessage(win, !captions), 0, y +:= lead) dx := SliderWidth + SliderPad x1 := 0 - dx x2 := PickerWidth + SliderPad y +:= pad cdl_data.dialog := dialog cdl_data.rv := cdl_slider(dialog, "r", x1 +:= dx, y, 0, 65535, cdl_data.r) cdl_data.gv := cdl_slider(dialog, "g", x1 +:= dx, y, 0, 65535, cdl_data.g) cdl_data.bv := cdl_slider(dialog, "b", x1 +:= dx, y, 0, 65535, cdl_data.b) cdl_data.vv := cdl_slider(dialog, "v", x2 -:= dx, y, 0, 100, cdl_data.v) cdl_data.sv := cdl_slider(dialog, "s", x2 -:= dx, y, 0, 100, cdl_data.s) cdl_data.hv := cdl_slider(dialog, "h", x2 -:= dx, y, 0, 360, cdl_data.h) x1 +:= dx + SliderPad x2 -:= 2 * SliderPad cdl_data.rect := Vpane(win, , , "sunken", x2 - x1, SliderHeight - 3 * lead - SliderPad) VInsert(dialog, cdl_data.rect, x1, y) y +:= SliderHeight + pad bw := TextWidth(win, "Cancel") + 10 VInsert(dialog, Vbutton(win, "Okay", cdl_exit, V_OK, , bw, ButtonHeight), PickerWidth / 2 - bw - 10, y) VInsert(dialog, Vbutton(win, "Cancel", cdl_exit, V_OK, , bw, ButtonHeight), PickerWidth / 2 + 10, y) VFormat(dialog) box_id := captions[1] | "ColorDialog" VOpenDialog(dialog, , box_id, , "Okay") dialog_value := cdl_data.r || "," || cdl_data.g || "," || cdl_data.b WClose(\temp_win) return dialog_button end procedure cdl_slider(dialog, id, x, y, low, high, init) # place a slider local v v := Vvert_slider(dialog.win, cdl_setval, id, SliderHeight, SliderWidth, low, high, init) VInsert(dialog, v, x, y) return v end procedure cdl_init() # initialize non-vidget part of dialog local r r := cdl_data.rect cdl_data.fg := Fg(r.win) cdl_data.fillargs := [r.win, r.ux, r.uy, r.uw, r.uh] if cdl_data.mutable := NewColor(cdl_data.rect.win, cdl_data.orgcolor) then { Fg(r.win, cdl_data.mutable) FillRectangle ! cdl_data.fillargs } else cdl_data.nc := 0 if Fg(r.win, \cdl_data.refcolor) then { cdl_data.fillargs[-1] -:= r.uh / 8 FillRectangle(r.win, r.ux, r.uy + r.uh, r.uw, -r.uh / 8) } Fg(r.win, cdl_data.fg) cdl_sethsv() return end procedure cdl_exit(vidget, s) # save position and button name on exit dialog_button := vidget.s FreeColor(cdl_data.rect.win, \cdl_data.mutable) EraseArea(cdl_data.rect.win) return end procedure cdl_setval(v, x) # set value in response to slider motion static recurse if /recurse then { # if not a recursive call recurse := 1 # note to prevent recursion case v.id of { "r": { cdl_data.r := x; cdl_sethsv(); } "g": { cdl_data.g := x; cdl_sethsv(); } "b": { cdl_data.b := x; cdl_sethsv(); } "h": { cdl_data.h := x; cdl_setrgb(); } "s": { cdl_data.s := x; cdl_setrgb(); } "v": { cdl_data.v := x; cdl_setrgb(); } } recurse := &null } return end procedure cdl_sethsv() # set h/s/v values from r/g/b local c HSV(c := cdl_data.r || "," || cdl_data.g || "," || cdl_data.b) ? { VSetState(cdl_data.hv, cdl_data.h := integer(tab(many(&digits)))) move(1) VSetState(cdl_data.sv, cdl_data.s := integer(tab(many(&digits)))) move(1) VSetState(cdl_data.vv, cdl_data.v := integer(tab(many(&digits)))) } cdl_setcolor(c) return end procedure cdl_setrgb() # set r/g/b values from h/s/v local c (c := HSVValue(cdl_data.h || "/" || cdl_data.s || "/" || cdl_data.v)) ? { VSetState(cdl_data.rv, cdl_data.r := integer(tab(many(&digits)))) move(1) VSetState(cdl_data.gv, cdl_data.g := integer(tab(many(&digits)))) move(1) VSetState(cdl_data.bv, cdl_data.b := integer(tab(many(&digits)))) } cdl_setcolor(c) return end procedure cdl_setcolor(c) # display new color and invoke callback local r, win, x1, x2, y, dy r := cdl_data.rect win := r.win if \cdl_data.mutable then Color(win, cdl_data.mutable, c) # set the mutable color else { if ((cdl_data.nc +:= 1) > MaxStaticCol) | (not Fg(win, c)) then { EraseArea(win) # free allocated colors VDraw(cdl_data.dialog) # redraw vidget if Fg(r.win, \cdl_data.refcolor) then # redraw reference color FillRectangle(r.win, r.ux, r.uy + r.uh, r.uw, -r.uh / 8) Fg(win, c) # set new foreground cdl_data.nc := 1 } FillRectangle ! cdl_data.fillargs Fg(win, cdl_data.fg) } x1 := cdl_data.rect.ax x2 := x1 + cdl_data.rect.aw y := cdl_data.rect.ay + cdl_data.rect.ah + SliderPad dy := WAttrib(win, "leading") EraseArea(win, x1, y, x2 - x1, 3 * dy) # erase and redraw text area y +:= WAttrib(win, "ascent") x2 -:= TextWidth(win, "h: 360") DrawString(win, x1, y, "r: " || right(cdl_data.r, 5)) DrawString(win, x2, y, "h: " || right(cdl_data.h, 3)) y +:= dy DrawString(win, x1, y, "g: " || right(cdl_data.g, 5)) DrawString(win, x2, y, "s: " || right(cdl_data.s, 3)) y +:= dy DrawString(win, x1, y, "b: " || right(cdl_data.b, 5)) DrawString(win, x2, y, "v: " || right(cdl_data.v, 3)) (\cdl_data.callback)(cdl_data.id, c) # invoke user callback, if any return end # Popup(win, x, y, w, h, proc, args[]) $define BorderWidth 4 $define ShadowWidth 4 procedure Popup(args[]) #: create popup subwindow local win, x, y, w, h, xx, yy, ww, hh, dx, dy, s, proc, retv, ampwin, save # Get parameters. PushWin(args) win := get(args) x := get(args); integer(x) | runerr(101, \x) y := get(args); integer(y) | runerr(101, \y) w := \get(args) | 250; integer(w) | runerr(101, w) h := \get(args) | 150; integer(h) | runerr(101, h) proc := \get(args) | Event # Handle defaults dx := WAttrib(win, "dx") dy := WAttrib(win, "dy") w >:= WAttrib(win, "width") # limit to size of full win h >:= WAttrib(win, "height") /x := (WAttrib(win, "width") - w) / 2 - dx # center the subwindow /y := (WAttrib(win, "height") - h) / 2 - dy # Adjust subwindow configuration parameters. xx := x - BorderWidth yy := y - BorderWidth ww := w + 2 * BorderWidth + ShadowWidth hh := h + 2 * BorderWidth + ShadowWidth # Save original window contents. save := ScratchCanvas(ww, hh, "__Popup__") | stop("can't get ScratchCanvas in Popup()") CopyArea(win, save, xx, yy, ww, hh) # Save &window and create subwindow. ampwin := &window &window := Clone(win) | stop("can't Clone in Popup()") WAttrib("drawop=copy", "fillstyle=solid", "linestyle=solid", "linewidth=1", "dx=" || (dx + x), "dy=" || (dy + y)) DrawRectangle(-BorderWidth, -BorderWidth, ww-ShadowWidth-1, hh-ShadowWidth-1) BevelRectangle(-BorderWidth + 1, -BorderWidth + 1, ww - ShadowWidth - 2, hh - ShadowWidth - 2, BorderWidth) FillRectangle(-BorderWidth + ShadowWidth, h + BorderWidth, ww - ShadowWidth, ShadowWidth) FillRectangle(w + BorderWidth, -BorderWidth + ShadowWidth, ShadowWidth, hh - ShadowWidth) Clip(0, 0, w, h) EraseArea() # Flush any previously entered events on the window while *Pending(win) > 0 do Event(win) # Call proc; save result, if any, or use args as flag if none. retv := (proc ! args) | args # Restore window and return result. Use &window to ensure drawop=copy. Clip(-BorderWidth, -BorderWidth, ww, hh) CopyArea(save, &window, 0, 0, ww, hh, -BorderWidth, -BorderWidth) EraseArea(save) &window := ampwin return args ~=== retv end