diff options
Diffstat (limited to 'ipl/gpacks/vib/vibfile.icn')
-rw-r--r-- | ipl/gpacks/vib/vibfile.icn | 603 |
1 files changed, 603 insertions, 0 deletions
diff --git a/ipl/gpacks/vib/vibfile.icn b/ipl/gpacks/vib/vibfile.icn new file mode 100644 index 0000000..da1dd43 --- /dev/null +++ b/ipl/gpacks/vib/vibfile.icn @@ -0,0 +1,603 @@ +############################################################################ +# +# vibfile.icn -- procedures for reading and writing specs to files +# +############################################################################ +# +# This file is in the public domain. +# +############################################################################ + +$include "vibdefn.icn" +$include "vdefns.icn" + +############################################################################ +# constants and globals that are used only in this file: +############################################################################ +$define PTITLE "#===<<vib prototype file>>===" +$define HEADER "#===<<vib:begin>>===\tmodify using vib; do not remove this marker line" +$define TRAILER "#===<<vib:end>>===\tend of section maintained by vib" +$define XHEADER "#===<<xie:begin>>===" # for detecting old files +$define XTRAILER "#===<<xie:end>>===" +$define HMATCH 20 # number of chars that must match in header +$define TMATCH 18 # number of chars that must match in trailer +global USER_PREFIX, # user code preceding spec + USER_SUFFIX # user code following spec + +############################################################################ +# new_session() creates a new, empty VIB session +############################################################################ +procedure new_session() + local fname + + if not flush_session() then fail + SIZER := create_sizer() + clear_screen() + SESSION := newname() + label_session() + USER_PREFIX := USER_SUFFIX := &null + return +end + +############################################################################ +# load_session() reads in a saved VIB session file so that it can be +# re-edited. +############################################################################ +procedure load_session(s) + local line, ifile, l, o + + ifile := open(s, "r") | fail + clear_screen() + + USER_PREFIX := USER_SUFFIX := "" + while line := read(ifile) do { + if line ? match((HEADER | XHEADER)[1 +: HMATCH]) then + break + if line ? match("# Session Code:") then { + Notice("Old file format; use uix to convert") + USER_PREFIX := USER_SUFFIX := &null + fail + } + USER_PREFIX ||:= line + USER_PREFIX ||:= "\n" + line := &null + } + + DIRTY := &null + if not (\line ? match((HEADER | XHEADER)[1 +: HMATCH])) then { + Notice("No interface section found; creating one") + USER_PREFIX ||:= "\n\n\n" + DIRTY := 1 + } + + while o := load_object(ifile) do case o.typ of { + "Button" : init_object(load_button, button_obj(), o) + "Text" : init_object(load_text_input, text_input_obj(), o) + "Scrollbar" : init_object(load_slider, slider_obj(), o) + "Slider" : init_object(load_slider, slider_obj(), o) + "Line" : init_object(load_line, line_obj(), o) + "Rect" : init_object(load_rect, rect_obj(), o) + "Label" : init_object(load_label, label_obj(), o) + "Message" : init_object(load_label, label_obj(), o) + "Choice" : init_object(load_radio_button, radio_button_obj(), o) + "Menu" : init_object(load_menu, menu_obj(), o) + "Sizer" : init_object(load_sizer, sizer_obj(), o) + "Check" : init_object(load_xbox, button_obj(), o) + "List" : init_object(load_list, list_obj(), o) + } + + while USER_SUFFIX ||:= read(ifile) do + USER_SUFFIX ||:= "\n" + close(ifile) + return +end + +############################################################################# +# init_object() initializes an object record and calls a proc to register it. +############################################################################# +procedure init_object(proc, r, o) + r.id := o.id + r.proc := o.proc + r.x := integer(o.x) + r.y := o.y + CANVASY + r.w := integer(o.w) + r.h := integer(o.h) + r.focus := 0 + push(O_LIST, r) # must precede proc call + proc(r, o) # call object-specific procedure + update_bb(r) + draw_object(r) +end + +############################################################################ +# load_object() reads the next object from a saved session file. +############################################################################ +procedure load_object(f) + local c, s, l, r + + # find a line where the first nonblank character is a "[" + repeat { + while (c := reads(f, 1)) & upto(' \t\f', c) + if \c == "[" then + break + s := (c || read(f)) | fail + if s ? match((TRAILER | XTRAILER)[1 +: TMATCH]) then + fail + } + + # load the list of values + l := load_strings(f) | fail + + # break them down into an ext_rec record + r := ext_rec() + s := get(l) | fail + s ? { + r.id := tab(upto(':')) | fail; move(1) + r.typ := tab(upto(':')) | fail; move(1) + r.sty := tab(upto(':')) | fail; move(1) + r.num := tab(upto(':')) | fail; move(1) + r.x := tab(upto(',')) | fail; move(1) + r.y := tab(upto(',')) | fail; move(1) + r.w := tab(upto(',')) | fail; move(1) + r.h := tab(upto(':')) | fail; move(1) + r.lbl := tab(0) + } + r.proc := get(l) | "" + r.etc := get(l) | [] + return r +end + +############################################################################ +# load_strings() reads a list of strings after "[" has already been consumed. +############################################################################ +procedure load_strings(f) + local l, c, s, n + + l := [] + n := 0 + while c := reads(f, 1) do case c of { + + "]": return l # end of list + ",": (n <:= *l) | put(l, &null) + " ": next # whitespace: do nothing + "\t": next + "\r": next + "\n": next + "[": put(l, load_strings(f)) # nested list + + "\"": { # string constant + s := "" + while (c := reads(f, 1)) & not upto('"\n"', c) do + if s == "\\" then + s ||:= reads(f, 1) + else + s ||:= c + put(l, s) + } + + default: { # anything else: consume to separator + s := c + while (c := reads(f, 1)) & not upto(',] \t\r\n', c) do + s ||:= c + put(l, s) + if c == "]" then + return l + } + } + + fail # EOF hit +end + +############################################################################ +# save_session() saves the current session to a file. If "pflag" is set, +# the standard prefix is used (for prototype mode). +############################################################################ +procedure save_session(s, pflag) + local ofile + + sanity_check() | fail + ofile := open(s, "w") + if /ofile then { + Notice("Could not open " || s, "(FILE WAS NOT SAVED)") + fail + } + if /SIZER.dlog then + save_app(ofile, pflag, s) + else + save_dlog(ofile, pflag, s) + close(ofile) + if /pflag then + DIRTY := &null + return +end + +############################################################################ +# sanity_check() issues warnings if certain things don't look right. +############################################################################ +procedure sanity_check() + local messages, npush, ndflt, nrect, nlist, o + + messages := [] + npush := ndflt := nrect := nlist := 0 + every o := !O_LIST do { + case type(o) of { + "button_obj": { + if /o.toggle then npush +:= 1 + if \o.dflt then ndflt +:= 1 + } + "rect_obj": { + nrect +:= 1 + } + "list_obj": { + nlist +:= 1 + } + } + } + + if \SIZER.dlog then { + if ndflt > 1 then + put(messages, "", + "More than one button is marked as the default.", + "Only one will be used.") + if npush = 0 then + put(messages, "", + "There is no non-toggle button, so it will not", + "be possible to dismiss the dialog box.") + if nrect > 0 | nlist > 0 then + put(messages, "", + "There are one or more regions or text lists,", + "but these do not function in dialog boxes.") + } + else { + if ndflt > 0 then + put(messages, "", + "A button is marked as a dialog default,", + "but this is not a dialog specification.") + } + + if *messages = 0 then + return + + push(messages, "Warning:") + case TextDialog(messages, , , , ["Continue", "Cancel"], 2) of { + "Continue": return + "Cancel": fail + } +end + +############################################################################ +# save_app() saves the session as an application. If "pflag" is set, +# the standard prefix is used (for prototype mode). +############################################################################ +procedure save_app(ofile, pflag, filename) + local id + + id := ("" ~== \SIZER.id) | "ui" + + if \pflag then + write(ofile, PTITLE, "\n\n") + if \pflag | /USER_PREFIX then { + if /pflag then + ipl_header(ofile, filename, "Program to", "vsetup") + app_prologue(ofile, id, pflag) + if /pflag then + save_procs(ofile) + } + else + writes(ofile, USER_PREFIX) + + write(ofile, HEADER) + write(ofile, "procedure ", id, "_atts()") + writes(ofile, " return [\"size=", SIZER.x + SIZER.w, ",", + SIZER.y - CANVASY + SIZER.h, "\", \"bg=", VBackground, "\"") + writes(ofile, ", \"label=", "" ~== SIZER.label, "\"") + write(ofile, "]") + write(ofile,"end") + write(ofile) + write(ofile, "procedure ", id, "(win, cbk)") + write(ofile, "return vsetup(win, cbk,") + output_spec(ofile, SIZER) + output_all(ofile, O_LIST) + write(ofile, " )") + write(ofile, "end") + write(ofile, TRAILER) + + if /pflag & \USER_SUFFIX then + writes(ofile, USER_SUFFIX) + return +end + +############################################################################ +# save_procs() generates empty callback procedures in lexical order. +############################################################################ +procedure save_procs(ofile) + local o, t, l + + t := table() + every o := !O_LIST do + t["" ~== \o.proc] := o + l := sort(t, 3) + while get(l) do { + o := get(l) + writes(ofile, "procedure ", o.proc, "(vidget, ") + if type(o) == "rect_obj" then + write(ofile, "e, x, y)") + else + write(ofile, "value)") + write(ofile, " return") + write(ofile, "end") + write(ofile) + } + return +end + +############################################################################ +# save_dlog() saves the session as a dialog. If "pflag" is set, +# the standard prefix is used (for prototype mode). +############################################################################ +procedure save_dlog(ofile, pflag, filename) + local id + + id := ("" ~== \SIZER.id) | "dl" + + if \pflag then + dlog_prototype(ofile, id) + else if /USER_PREFIX then { + ipl_header(ofile, filename, "Procedure to", "dsetup") + dlog_prologue(ofile) + } + else + writes(ofile, USER_PREFIX) + + write(ofile, HEADER) + write(ofile, "procedure ", id, "(win, deftbl)") + write(ofile, "static dstate") + write(ofile, "initial dstate := dsetup(win,") + output_spec(ofile, SIZER) + output_all(ofile, O_LIST) + write(ofile, " )") + write(ofile, "return dpopup(win, deftbl, dstate)") + write(ofile, "end") + write(ofile, TRAILER) + + if /pflag & \USER_SUFFIX then + writes(ofile, USER_SUFFIX) + return +end + +############################################################################ +# output_all() outputs the members of an object list, sorted by ID, +# but with rectangles last so that they can enclose other objects. +############################################################################ +record output_rec(obj, key) + +procedure output_all(f, l) + local t, e, k + + t := [] + every e := !l do { + if type(e) == "rect_obj" then + k := "~" || right(e.w * e.h, 20) || e.id # rects last, by area + else + k := e.id + put(t, output_rec(e, k)) + } + t := sortf(t, 2) + every e := !t do + output_spec(f, e.obj) + return +end + +############################################################################ +# output_spec() outputs the spec for an object. +############################################################################ +procedure output_spec(f, o) + local r + + # set standard fields + r := ext_rec(o) + r.id := o.id + r.proc := o.proc + r.x := o.x + r.y := o.y - CANVASY + r.w := o.w + r.h := o.h + # set type-dependent fields + case type(o) of { + "sizer_obj" : save_sizer(r, o) + "button_obj" : save_button(r, o) + "text_input_obj" : save_text_input(r, o) + "line_obj" : save_line(r, o) + "rect_obj" : save_rect(r, o) + "slider_obj" : save_slider(r, o) + "radio_button_obj" : save_radio_button(r, o) + "label_obj" : save_label(r, o) + "menu_obj" : save_menu(r, o) + "list_obj" : save_list_obj(r, o) + } + writes(f, " [\"") + writes(f, r.id, ":", r.typ, ":", r.sty, ":", r.num, ":") + writes(f, r.x, ",", r.y, ",", r.w, ",", r.h, ":") + writes(f, r.lbl, "\",") + if /SIZER.dlog then + writes(f, r.proc) + if \r.etc then + output_list(f, r.etc) + write(f, "],") + return +end + +############################################################################ +# output_list() outputs a list in Icon form preceded by ",\n". +############################################################################ +procedure output_list(f, a) + local prefix, elem, n + static indent + initial indent := " " + + n := 0 + indent ||:= " " + writes(f, ",\n", indent, "[") + prefix := "" + while elem := get(a) do + if type(elem) == "list" then { + output_list(f, elem) + prefix := ",\n" || indent + n := 0 + } + else { + writes(f, prefix, image(elem)) + if (n +:= 1) % 5 = 0 then + prefix := ",\n" || indent + else + prefix := "," + } + writes(f, "]") + indent := indent[1:-3] +end + +############################################################################ +# prototype() saves, compiles, and executes the current session. +############################################################################ +procedure prototype() + local f, line + + if f := open(PROTOFILE) then { + line := read(f) + close(f) + if \line & not (line ? =PTITLE) then { + Notice("Cannot create prototype file " || PROTOFILE || ":", + "it already contains something that is not a VIB prototype") + fail + } + } + + # write source file + if save_session(PROTOFILE, 1) then { + # translate and execute + WAttrib("pointer=" || ("wait" | "watch")) + system(BUILDPROTO) + remove(PROTOFILE) + WAttrib("pointer=" || ("left ptr" | "arrow")) + system(EXECPROTO) + } +end + +############################################################################ +# newname() invents a name when creating a new file. +############################################################################ +procedure newname() + local s, i, f + + every i := seq() do { + s := "app" || i || ".icn" # invent "app<n>.icn" file name + if f := open(s) then + close(f) # can't use this name; already exists + else + return s # found a safe new name + } +end + +############################################################################ +# ipl_header() writes a standard IPL application header. +############################################################################ +procedure ipl_header(ofile, filename, subject, links) + local hline, date + + hline := repl("#", 76) + &dateline ? { + tab(upto(',') + 2) + date := tab(upto(',') + 6) + } + + write(ofile, hline) + write(ofile, "#\n#\tFile: ", filename) + write(ofile, "#\n#\tSubject: ", subject, " ...") + write(ofile, "#\n#\tAuthor: ") + write(ofile, "#\n#\tDate: ", date) + write(ofile, "#\n", hline) + write(ofile, "#\n#\n#\n", hline) + write(ofile, "#\n# Requires:\n#\n", hline) + write(ofile, "#\n# Links: ", links) + write(ofile, "#\n", hline) + write(ofile) + return +end + +############################################################################ +# app_prologue() writes a main program and other code for a new application. +############################################################################ +procedure app_prologue(f, id, pflag) + local vecho, e + + if \pflag then + vecho := ", VEcho" + else + vecho := "" + + every write(f, ![ + "# This vib interface specification is a working program that responds", + "# to vidget events by printing messages. Use a text editor to replace", + "# this skeletal program with your own code. Retain the vib section at", + "# the end and use vib to make any changes to the interface.", + "", + "link vsetup", + "", + "procedure main(args)", + " local vidgets, root, paused", + "", + " (WOpen ! " || id || "_atts()) | stop(\"can't open window\")", + " vidgets := " || id || "(" || vecho || ")\t\t\t\t# set up vidgets", + " root := vidgets[\"root\"]" + ]) + + # generate a sample VSetItems call for every list object (prototyping only) + if \pflag then + every e := !O_LIST do + if type(e) == "list_obj" then + write(f, " VSetItems(vidgets[\"", e.id, + "\"], [\"a\", \"b\", \"c\", \"d\"])"); + + every write(f, ![ + "", + " paused := 1\t\t\t\t\t# flag no work to do", + " repeat {", + " # handle any events that are available, or", + " # wait for events if there is no other work to do", + " while (*Pending() > 0) | \\paused do {", + " ProcessEvent(root, QuitCheck)", + " }", + " # if <paused> is set null, code can be added here", + " # to perform useful work between checks for input", + " }", + "end", + ""]) +end + +############################################################################ +# dlog_prologue() writes a header for a dialog file. +############################################################################ +procedure dlog_prologue(f) +every write(f, ![ + "# Link this dialog specification with the rest of your program code.", + "# Use vib to make any changes.", + "", + "link dsetup", + ""]) +end + +############################################################################ +# dlog_prototype() writes a header for a dialog prototyping run. +############################################################################ +procedure dlog_prototype(f, id) + write(f, PTITLE) + write(f) + write(f, "link dsetup, graphics") + write(f) + write(f, "procedure main(args)") + write(f, " remove(", image(PROTOEXE), ")") + write(f, " dproto(", id, ", , ", + SIZER.x + SIZER.w, ", ", SIZER.y - CANVASY + SIZER.h, ", args)") + write(f, "end") + write(f) +end |