ref: d09cf07a82cf4ffb846a31d0384e774b4c4661e1
dir: /appl/demo/camera/tkinterface.b/
# Sort out timing with taking photo & getting jpg/thumbnail - make sure it gets the right one when 2photos have been taken & sort out 'cannot communicate with camera' error implement tkinterface; include "tk.m"; tk: Tk; include "tkclient.m"; tkclient: Tkclient; include "sys.m"; sys : Sys; include "daytime.m"; daytime: Daytime; include "readdir.m"; readdir: Readdir; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "selectfile.m"; selectfile: Selectfile; include "string.m"; str : String; include "draw.m"; draw: Draw; Context, Display, Point, Rect, Image, Screen, Font: import draw; include "grid/readjpg.m"; readjpg: Readjpg; display : ref draw->Display; context : ref draw->Context; camerapath := ""; savepath := ""; tmppath := "/tmp/"; usecache := 1; working := 0; processing := 0; coords: draw->Rect; DONE : con 1; KILLED : con 2; font: ref Draw->Font; tkfont := ""; tkfontb := ""; tkfontf := ""; ssize := 3; maxsize : Point; nilrect := Draw->Rect((0,0),(0,0)); runwithoutcam := 0; toplevels : list of (ref Tk->Toplevel, string, list of int, int) = nil; procimg : ref Draw->Image; loadimg: ref Draw->Image; tkinterface : module { init : fn (ctxt : ref Draw->Context, argv : list of string); }; init(ctxt : ref Draw->Context, argv : list of string) { display = ctxt.display; context = ctxt; sys = load Sys Sys->PATH; # sys->pctl(Sys->NEWPGRP, nil); # sys->pctl(Sys->FORKNS, nil); str = load String String->PATH; readdir = load Readdir Readdir->PATH; daytime = load Daytime Daytime->PATH; bufio = load Bufio Bufio->PATH; str = load String String->PATH; draw = load Draw Draw->PATH; tk = load Tk Tk->PATH; tkclient = load Tkclient Tkclient->PATH; tkclient->init(); selectfile = load Selectfile Selectfile->PATH; selectfile->init(); readjpg = load Readjpg Readjpg->PATH; readjpg->init(display); font = draw->Font.open(display, "/fonts/charon/plain.small.font"); runfrom := hd argv; p := isat2(runfrom,"/"); savepath = runfrom[:p+1]; argv = tl argv; while (argv != nil) { if (camerapath == "" && (hd argv)[0] == '/') camerapath = hd argv; if (hd argv == "nocache") usecache = 0; argv = tl argv; } if (camerapath == "") camerapath = "./"; if (camerapath != "" && camerapath[len camerapath - 1] != '/') camerapath[len camerapath] = '/'; r := display.image.r; # if (r.dx() < 800 || r.dy() < 600) ssize = 2; if (r.dx() < 400 || r.dy() < 300) ssize = 1; maxsize = (r.dx(), r.dy()); if (ssize == 1) { tkfont = "/fonts/charon/plain.tiny.font"; tkfontb = "/fonts/charon/bold.tiny.font"; tkfontf = "/fonts/pelm/unicode.8.font"; } else if (ssize == 2) { tkfont = "/fonts/charon/plain.small.font"; tkfontb = "/fonts/charon/bold.small.font"; tkfontf = "/fonts/pelm/unicode.8.font"; } else { tkfont = "/fonts/charon/plain.normal.font"; tkfontb = "/fonts/charon/bold.normal.font"; tkfontf = "/fonts/pelm/unicode.8.font"; } if ((sys->stat(tkfont)).t0 == -1) tkfont = ""; else tkfont = " -font " + tkfont; if ((sys->stat(tkfontb)).t0 == -1) tkfontb = ""; else tkfontb = " -font " + tkfontb; if ((sys->stat(tkfontf)).t0 == -1) tkfontf = ""; else tkfontf = " -font " + tkfontf; procimg = display.open("camproc.bit"); loadimg = display.open("camload.bit"); spawn tkstuff(); } # Tk stuff thumbscr := array[] of { "frame .f", "frame .fthumb -bg white", "frame .f.finfo", "frame .f.fsnap", "menubutton .f.fsnap.fsettings.mb2 -text {Selected\n(0 files)} -menu .m2 @", "menu .m2 @", ".m2 add command -text {Select All} -command {send butchan selectall 1}", ".m2 add command -text {Select None} -command {send butchan selectall 0}", ".m2 add command -text {Invert Selection} -command {send butchan invert}", ".m2 add command -text {Refresh Files} -command {send butchan refresh}", "menu .m @", "frame .f.fsnap.fsettings -borderwidth 1 -relief raised", "menubutton .f.fsnap.fsettings.mb -text {Settings} -menu .m &", "button .f.fsnap.fsettings.b -text {Information} -command {send butchan info} &", "grid .f.fsnap.fsettings.b -row 0 -column 0 -sticky ew", "grid .f.fsnap.fsettings.mb -row 1 -column 0 -sticky ew", "grid .f.fsnap.fsettings.mb2 -row 2 -column 0 -sticky ew", "frame .f.fsnap.fstore -borderwidth 1 -relief raised", "label .f.fsnap.fstore.l1 -text { Photos taken: } @", "label .f.fsnap.fstore.l2 -text { Remaining: } @", "label .f.fsnap.fstore.l3 -text { } @", "label .f.fsnap.fstore.l4 -text { } @", "grid .f.fsnap.fstore.l1 -row 0 -column 0 -sticky w", "grid .f.fsnap.fstore.l2 -row 1 -column 0 -sticky w", "grid .f.fsnap.fstore.l3 -row 0 -column 1 -sticky w", "grid .f.fsnap.fstore.l4 -row 1 -column 1 -sticky w", "frame .f.fsnap.ftime -borderwidth 1 -relief raised", "label .f.fsnap.ftime.l1 -text {Local: } @", "label .f.fsnap.ftime.l2 -text {Camera: } @", "label .f.fsnap.ftime.l3", "label .f.fsnap.ftime.l4", "checkbutton .f.fsnap.ftime.cb -text {Set camera to local time} -variable time &", "button .f.fsnap.ftime.b -text {refresh} -command {send butchan gettime} &", "grid .f.fsnap.ftime.l1 -row 0 -column 0 -sticky w", "grid .f.fsnap.ftime.l2 -row 1 -column 0 -sticky w", "grid .f.fsnap.ftime.l3 -row 0 -column 1 -sticky w", "grid .f.fsnap.ftime.l4 -row 1 -column 1 -sticky w", "grid .f.fsnap.ftime.cb -row 2 -column 0 -columnspan 2", "grid .f.fsnap.ftime.b -row 3 -column 0 -columnspan 2", "button .f.fsnap.b -text {Take Photo} -command {send butchan snap} &", "grid columnconfigure .f.fsnap 2 -minsize 150", "frame .f.fcom", "frame .f.f1 -background #0d0d0d1a", "canvas .f.f1.c1 -yscrollcommand {.f.f1.sb1 set} -height 255 -width 542 -bg white", ".f.f1.c1 create window 0 0 -window .fthumb -anchor nw", "scrollbar .f.f1.sb1 -command {.f.f1.c1 yview}", # "frame .f.f2", # "canvas .f.f2.c1 -width 556 -height 304", # ".f.f2.c1 create window 0 0 -window .f.fsnap -anchor nw", "grid .f.fsnap -column 0 -row 0", "grid .f.f1 -column 0 -row 1", "grid .f.f1.c1 -column 0 -row 0", "grid .f.f1.sb1 -column 1 -row 0 -sticky ns", # "grid .f.f2 -column 0 -row 0", # "grid .f.f2.c1 -column 0 -row 0 -sticky ew", "bind .Wm_t <ButtonPress-1> +{focus .}", "bind .Wm_t.title <ButtonPress-1> +{focus .}", }; lastpath := ""; Aitem: adt { pname,desc: string; dtype,factory: int; read, location: string; data: list of (string, int); }; LIST: con 0; MINMAX: con 1; OTHER: con 2; noabilities := 0; abilities : array of Aitem; getdesc(l : list of string): list of string { s := ""; while(hd l != "min" && hd l != "items" && tl l != nil) { s += hd l + " "; l = tl l; } while (s[len s - 1] == ' ' || s[len s - 1] == '\n') s = s[:len s -1]; l = s :: l; return l; } inflist : list of (string, string); ablmenu : array of string; getabilities() { inflist = nil; abilities = array[200] of Aitem; fd := bufio->open(camerapath+"abilities", bufio->OREAD); if (runwithoutcam) fd = bufio->open("/usr/danny/camera/abls", bufio->OREAD); i := 0; for (;;) { take := 0; s := fd.gets('\n'); if (s == "") break; (n, lst) := sys->tokenize(s," ,:\t\n"); abilities[i].data = nil; abilities[i].read = ""; if (lst != nil && len hd lst == 4) { abilities[i].pname = hd lst; lst = getdesc(tl lst); abilities[i].desc = hd lst; if (hd tl lst == "items") { abilities[i].dtype = LIST; abilities[i].factory = int hd tl tl tl tl lst; noitems := int hd tl tl lst; for (k := 0; k < noitems; k++) { s = fd.gets('\n'); (n2, lst2) := sys->tokenize(s,",:\t\n"); name := hd lst2; val := int hd tl lst2; if (k == 0) { if (abilities[i].pname == "ssiz") abilities[i].factory = val; else if (abilities[i].pname == "scpn") abilities[i].factory = val; } if (val == abilities[i].factory && noitems > 1) name += " *"; abilities[i].data = (name, val) :: abilities[i].data; } if (noitems < 2) { inflist = (abilities[i].desc, (hd abilities[i].data).t0) :: inflist; take = 1; } } else if (hd tl lst == "min") { abilities[i].dtype = MINMAX; abilities[i].factory = int hd tl tl tl tl tl tl lst; min := int hd tl tl lst; max := int hd tl tl tl tl lst; mul := 1; while (max > 200000) { min /= 10; max /= 10; mul *= 10; } abilities[i].data = ("min", min) :: abilities[i].data; abilities[i].data = ("max", max) :: abilities[i].data; abilities[i].data = ("mul", mul) :: abilities[i].data; } else { inflist = (abilities[i].desc,list2string(tl lst)) :: inflist; take = 1; } if (take || abilities[i].desc == "Time Format" || abilities[i].desc == "Date Format" || abilities[i].desc == "File Type" || contains(abilities[i].desc,"Video") || contains(abilities[i].desc,"Media") || contains(abilities[i].desc,"Sound") || contains(abilities[i].desc,"Volume") || contains(abilities[i].desc,"Slide") || contains(abilities[i].desc,"Timelapse") || contains(abilities[i].desc,"Burst") || contains(abilities[i].desc,"Power") || contains(abilities[i].desc,"Sleep")) i--; i++; } } noabilities = i; } isat(s: string, test: string): int { num := -1; if (len test > len s) return -1; for (i := 0; i < (1 + (len s) - (len test)); i++) { if (num == -1 && test == s[i:i+len test]) num = i; } return num; } isat2(s: string, test: string): int { num := -1; if (len test > len s) return -1; for (i := len s - len test; i >= 0; i--) { if (num == -1 && test == s[i:i+len test]) num = i; } return num; } nomatches(s: string): int { n := 0; for (i := 0; i < noabilities; i++) { test := abilities[i].desc; if (len s <= len test && test[:len s] == s) n++; } return n; } matches(s1,s2: string): int { if (len s1 < len s2) return 0; if (s1[:len s2] == s2) return 1; return 0; } biggestmatch(nm: int, s: string, l: int): string { bigmatch := s; match := s[:l]; for (;;) { if (bigmatch == match) break; if (nomatches(bigmatch) == nm) return bigmatch; p := isat2(bigmatch," "); if (p < len match) break; bigmatch = bigmatch[:p]; } return match; } getabllist(): array of string { los : list of string; los = nil; for (i := 0; i < noabilities; i++) { p := 0; p2 := 0; nm : int; for (;;) { nm = -1; tmpl := los; while (tmpl != nil) { if (matches(abilities[i].desc, hd tmpl)) nm = 0; tmpl = tl tmpl; } if (nm == 0) break; p += p2; tmp := abilities[i].desc[p:]; p2 = isat(tmp, " "); if (p2 == -1) p2 = len tmp; else p2++; nm = nomatches(abilities[i].desc[:p+p2]); if (nm <= 5) break; } if (nm > 0) { listitem := biggestmatch(nm, abilities[i].desc,p+p2); los = listitem :: los; } } ar := array[len los] of string; for (i = len ar - 1; i >= 0; i--) { ar[i] = hd los; los = tl los; } return ar; } buildabilitiesframes(top: ref Tk->Toplevel) { ablmenu = getabllist(); tkcmd(top, ".m add command -text {Refresh Main Screen} -command {send butchan refreshstate}"); tkcmd(top, ".m add command -text {Reset Camera} -command {send butchan reset}"); for (k := 0; k < len ablmenu; k++) { if (len ablmenu[k] > 4 && (ablmenu[k][:4] == "Zoom" || ablmenu[k][:5] == "Still")) buildabilitiesframe(top,k,"butchan"); else tkcmd(top, ".m add command -text {"+ablmenu[k]+ "} -command {send butchan abls "+string k+"}"); } tkcmd(top, "menu .mthumb "+tkfont); tkcmd(top, ".mthumb add command -label {Selection (88 files)}"); tkcmd(top, ".mthumb add separator"); for (k = nothumbs; k < len menu; k++) tkcmd(top, ".mthumb add command -text {"+menu[k].text+"} " + "-command {send butchan}"); } buildabilitiesframe(top: ref Tk->Toplevel,k: int, chanout: string) { nm := string nomatches(ablmenu[k]); count2 := 0; for (i := 0; i < noabilities; i++) { if (matches(abilities[i].desc,ablmenu[k])) { frame : string; case abilities[i].pname { "scpn" or "ssiz" or "zpos" => frame = ".f.fsnap.f"+abilities[i].pname; tkcmd(top, "frame "+frame+" -borderwidth 1 -relief raised"); * => frame = ".f"; if (count2 == 0) { tkcmd(top, "frame "+frame); tkcmd(top, "label "+frame+".l -text {"+ablmenu[k]+"}"+tkfontb); tkcmd(top, "grid "+frame+".l -row 0 -column 0 -columnspan "+nm); } frame = frame + ".f"+string count2; tkcmd(top, "frame "+frame+" -borderwidth 1 -relief raised"); tkcmd(top, "grid "+frame+" -row 1 -column "+string count2+ " -sticky nsew"); mul := getval(abilities[i].data,"mul"); s := abilities[i].desc[len ablmenu[k]:]; if (mul != 1 && abilities[i].dtype == MINMAX) s += " (x"+string mul+")"; tkcmd(top, "label "+frame+".l -text {"+s+"}"+tkfont); tkcmd(top, "grid "+frame+".l -row 0 -column 0 -sticky nw"); } if (abilities[i].dtype == MINMAX) { abilities[i].location = frame+".sc"; min := getval(abilities[i].data,"min"); max := getval(abilities[i].data,"max"); tkcmd(top, sys->sprint("scale %s.sc -to %d -from %d %s", frame,min,max,tkfont)); tkcmd(top, "bind "+frame+".sc <ButtonPress-3> {send " + chanout + " scaleval " + string i + " %X %Y}"); tkcmd(top, "grid "+frame+".sc -row 1 -column 0"); } else if (abilities[i].dtype == LIST) { tkcmd(top, "frame "+frame+".frb"); tkcmd(top, "grid "+frame+".frb -row 1 -column 0"); tmp := abilities[i].data; row := 0; while (tmp != nil) { (name, val) := hd tmp; s := sys->sprint("radiobutton %s.frb.rb%d -text {%s} -value %d -variable %s -height %d %s",frame,row,name,val,abilities[i].pname,24 - (3*(3-ssize)), tkfont); tkcmd(top,s); tkcmd(top, sys->sprint("grid %s.frb.rb%d -row %d -column 0 -sticky w", frame,row,row)); tmp = tl tmp; row++; } } tkcmd(top, "button "+frame+".bs -text {Set} -command "+ "{send "+chanout+" set "+string i+"}"+butheight+tkfont); tkcmd(top, "grid "+frame+".bs -row 2 -column 0 -sticky ew"); if (abilities[i].dtype == MINMAX) { tkcmd(top, "button "+frame+".bf -text {Default} -command "+ "{send "+chanout+" setdef "+string i+"}"+butheight+tkfont); tkcmd(top, "grid "+frame+".bf -row 3 -column 0 -sticky ew"); } count2++; } } } getvaluescr := array[] of { "frame .f -height 84 -width 114 -borderwidth 2 -relief raised", "label .f.l1 -text {Enter Value:} @", "entry .f.e1 -width 100 -bg white @", "button .f.b1 -text { ok } -command {send chanin ok} &", "button .f.b2 -text cancel -command {send chanin cancel} &", "grid .f.l1 -column 1 -row 0 -columnspan 2 -padx 0 -sticky w", "grid .f.e1 -column 1 -row 1 -columnspan 2 -padx 0 -pady 5", "grid .f.b1 -column 1 -row 2 -padx 0", "grid .f.b2 -column 2 -row 2 -padx 0", "grid columnconfigure .f 1 -minsize 20", "grid columnconfigure .f 2 -minsize 20", "grid columnconfigure .f 3 -minsize 5", "grid rowconfigure .f 0 -minsize 20", "grid rowconfigure .f 1 -minsize 20", "grid rowconfigure .f 2 -minsize 20", "grid columnconfigure .f 0 -minsize 5", "bind .f.e1 <Key> {send chanin key %s}", "focus .f.e1", "pack .f", "update", }; getvaluescreen(x,y: string): int { x = string ((int x) - 55); y = string ((int y) - 30); (top, nil) := tkclient->toplevel(context, "-x "+x+" -y "+y, nil, tkclient->Plain); chanin := chan of string; tk->namechan(top, chanin, "chanin"); for (tk1 := 0; tk1 < len getvaluescr; tk1++) tkcmd(top, getvaluescr[tk1]); tkclient->onscreen(top, "exact"); tkclient->startinput(top, "kbd"::"ptr"::nil); for(;;) { alt { s := <-top.ctxt.kbd => tk->keyboard(top, s); s := <-top.ctxt.ptr => tk->pointer(top, *s); inp := <- chanin => if (inp == "ok") return int tkcmd(top, ".f.e1 get"); else if (inp == "cancel") return -1; else if (inp[:3] == "key") { s := " "; s[0] = int inp[4:]; if (s[0] == '\n') return int tkcmd(top, ".f.e1 get"); if (s[0] >= '0' && s[0] <= '9') { tkcmd(top, ".f.e1 delete sel.first sel.last"); tkcmd(top, ".f.e1 insert insert {"+s+"}; update"); } } } } } infoscreen() { (top, titlebar) := tkclient->toplevel(context, "", "Information", Tkclient->Hide); tmp := inflist; tkcmd(top, "frame .f"); tkcmd(top, "label .f.l -text {Information}"); tkcmd(top, "grid .f.l -row 0 -column 0 -columnspan 2"); tkcmd(top, "frame .f.finfo -borderwidth 1 -relief raised"); tkcmd(top, "grid .f.finfo"); infrow := 0; while (tmp != nil) { infrow++; s := string infrow; (d1,d2) := hd tmp; tkcmd(top, "label .f.finfo.l"+s+"1 -text {"+d1+"}"); tkcmd(top, "label .f.finfo.l"+s+"2 -text {"+d2+"}"); tkcmd(top, "grid .f.finfo.l"+s+"1 -row "+s+" -column 0 -sticky w"); tkcmd(top, "grid .f.finfo.l"+s+"2 -row "+s+" -column 1 -sticky e"); tmp = tl tmp; } tkcmd(top, "pack .f; update"); tkclient->onscreen(top, nil); tkclient->startinput(top, "kbd"::"ptr"::nil); main: for(;;) { alt { s := <-top.ctxt.kbd => tk->keyboard(top, s); s := <-top.ctxt.ptr => tk->pointer(top, *s); title := <-top.ctxt.ctl or title = <-top.wreq or title = <- titlebar => if (title == "exit") break main; tkclient->wmctl(top, title); } } } settingsscreen(k: int, ctlchan: chan of int) { low := toplevels; for (;low != nil; low = tl low) { (tplvl, name, nil,nil) := hd low; if (name == ablmenu[k]) { tkcmd(tplvl, "raise .; focus .; update"); ctlchan <-= DONE; return; } } pid := sys->pctl(0, nil); (top, titlebar) := tkclient->toplevel(context, "", "Config", Tkclient->Appl); chanin := chan of string; tk->namechan(top,chanin, "chanin"); buildabilitiesframe(top,k, "chanin"); tkcmd(top,"bind .Wm_t <ButtonPress-1> +{focus .}"); tkcmd(top,"bind .Wm_t.title <ButtonPress-1> +{focus .}"); tkcmd(top, "pack .f; update"); err := 0; allread := 1; l : list of int = nil; for (i := 0; i < noabilities; i++) { if (matches(abilities[i].desc, ablmenu[k])) { l = i :: l; if (abilities[i].read != "") setmystate(top,i,abilities[i].read); else allread = 0; } } tkclient->onscreen(top, nil); tkclient->startinput(top, "kbd"::"ptr"::nil); if (!allread) { spawn workingscreen2(getcoords(top),pid, ctlchan,0); ltmp := l; for (;ltmp != nil; ltmp = tl ltmp) { if (abilities[hd ltmp].read == "" && getstate(top, hd ltmp) == -1) { err = 1; break; } } } if (!err) spawn settingsloop(top,chanin,titlebar,k,l); ctlchan <-= DONE; } settingsloop(top: ref Tk->Toplevel, chanin,titlebar: chan of string, k: int, abls: list of int) { tkcmd(top, "focus .Wm_t"); pid := sys->pctl(0,nil); addtoplevel(top,ablmenu[k], abls, pid); ctlchan := chan of int; main: for(;;) { alt { s := <-top.ctxt.kbd => tk->keyboard(top, s); s := <-top.ctxt.ptr => tk->pointer(top, *s); inp := <- chanin => tkcmd(top, "focus ."); (n, lst) := sys->tokenize(inp, " \t\n"); case hd lst { "scaleval" => i := int hd tl lst; val := getvaluescreen(hd tl tl lst, hd tl tl tl lst); if (val != -1) tkcmd(top, abilities[i].location+" set "+string val+";update"); "set" or "setdef" => if (working) dialog(" Camera is busy! ", 2,-1,getcoords(top)); else { spawn set(top, int hd tl lst, hd lst, ctlchan); <-ctlchan; working = 0; } } clearbuffer(chanin); title := <-top.ctxt.ctl or title = <-top.wreq or title = <- titlebar => if (title == "exit") break main; tkclient->wmctl(top, title); } } deltoplevel(top); } clearbuffer(c: chan of string) { tc := chan of int; spawn timer(tc); main: for (;;) alt { del := <-c => ; tick := <-tc => break main; } } timer(tick: chan of int) { sys->sleep(100); tick <- = 1; } getval(l: list of (string,int), s: string): int { while (l != nil) { (name,val) := hd l; if (name == s) return val; l = tl l; } return -2; } list2string(l : list of string): string { s := ""; while (l != nil) { s += " " + hd l; l = tl l; } if (s != "") return s[1:]; return s; } JPG: con 0; THUMB: con 1; Imgloaded: adt { name: string; imgtype: int; }; nofiles := 0; filelist := array[200] of string; thumbimg := array[200] of ref draw->Image; selected := array[200] of { * => 0 }; noselected := 0; fnew : list of int; imgloaded : list of Imgloaded; maxwidth, maxheight: int; nothumbs := 0; nocamera(): int { (n,dir) := sys->stat(camerapath+"ctl"); if (n != -1) return 0; return 1; } startuptkstuff(top: ref Tk->Toplevel, ctlchan: chan of int) { pid := sys->pctl(0, nil); spawn workingscreen2(coords,pid, ctlchan,1); getabilities(); (dirs,n) := readdir->init(camerapath+"thumb", readdir->NAME); if (n == -1) nothumbs = 1; buildabilitiesframes(top); refreshfilelist(top,0); ctlchan <-= DONE; } tibuild := 0; butheight := ""; tkstuff() { if (!runwithoutcam && nocamera()) { dialog("Cannot find camera!",0,-1,nilrect); exit; } (win, titlebar) := tkclient->toplevel(context, "", "Camera", Tkclient->Appl); tkcmd(win, "frame .test"); if (tkcmd(win, ".test cget -bg") == "#ffffffff") tibuild = 1; tkcmd(win, "destroy .test"); butheight = " -height "+string (16 + (5*tibuild) - (3*(3-ssize))); butchan := chan of string; tk->namechan(win, butchan, "butchan"); for (tk1 := 0; tk1 < len thumbscr; tk1++) tkcmd(win, thumbscr[tk1]); coords = display.image.r; ctlchan := chan of int; imgloaded = nil; spawn startuptkstuff(win, ctlchan); e := <- ctlchan; if (e == KILLED) { dialog("Cancel during load!",0,-1,coords); exit; } working = 0; spawn mainscreen(win, 1, ctlchan); <- ctlchan; working = 0; processing = 0; tkcmd(win, "pack propagate . 0"); resizemain(win,1); tkcmd(win, "pack .f; update; focus ."); coords = getcoords(win); loadimg = nil; tkclient->onscreen(win, nil); tkclient->startinput(win, "kbd"::"ptr"::nil); main: for (;;) { alt { s := <-win.ctxt.kbd => tk->keyboard(win, s); s := <-win.ctxt.ptr => tk->pointer(win, *s); inp := <-butchan => tkcmd(win, "focus ."); (n, lst) := sys->tokenize(inp, "\t\n "); case hd lst { # Communicates internally "scaleval" => i := int hd tl lst; val := getvaluescreen(hd tl tl lst, hd tl tl tl lst); if (val != -1) tkcmd(win, abilities[i].location+" set "+string val); "info" => spawn infoscreen(); "unload" => i := int hd tl lst; for (k := 0; k < nofiles; k++) { if (i == k || (i == -1 && selected[k])) { delloaded(filelist[k],JPG); delloaded(filelist[k],THUMB); } } "invert" => nf := 0; for (i := 0; i < nofiles; i++) selected[i] = 1 - selected[i]; doselect(win); "selectall" => val := int hd tl lst; for (i := 0; i < nofiles; i++) selected[i] = val; doselect(win); "select" => i := int hd tl lst; selected[i] = 1 - selected[i]; doselect(win); "selectonly" => i := int hd tl lst; val := selected[i]; for (k := 0; k < nofiles; k++) selected[k] = 0; if (noselected - val == 0) selected[i] = 1 - val; else selected[i] = 1; doselect(win); "menu" => i := int hd tl lst; if (selected[i] && noselected > 1) i = -1; title := "Selection ("+string noselected+" files)"; if (i != -1) title = filelist[i]+".jpg"; si := string i; tkcmd(win, ".mthumb entryconfigure 0 -text {"+title+"}"); for (k := nothumbs; k < len menu; k++) tkcmd(win, ".mthumb entryconfigure "+string (2+k-nothumbs)+ " -command {send butchan "+ menu[k].com+" "+si+"}"); tkcmd(win, ".mthumb post "+hd tl tl lst+" "+hd tl tl tl lst); * => if (!processing) spawn dealwithcamera(win, lst); } tkcmd(win, "update"); clearbuffer(butchan); title := <-win.ctxt.ctl or title = <-win.wreq or title = <-titlebar => if (title == "exit") break main; err := tkclient->wmctl(win, title); if (err == nil && title == "!size") { (n, lst) := sys->tokenize(title, " "); if (hd tl lst == ".") resizemain(win,0); } coords = getcoords(win); } } for (; toplevels != nil; toplevels = tl toplevels) { (nil, nil, nil, pid) := hd toplevels; if (pid != -1) kill(pid); } while (imgloaded != nil) { (fname, ftype) := hd imgloaded; sys->remove(tmppath+fname+"."+string ftype+"~"); imgloaded = tl imgloaded; } tkcmd(win, "destroy ."); exit; } dealwithcamera(win: ref Tk->Toplevel, lst: list of string) { ctlchan := chan of int; processing = 1; case hd lst { "gettime" => spawn refreshtime(win, ctlchan); <- ctlchan; "show" => spawn loadthumb(win,int hd tl lst,ctlchan); <- ctlchan; "snap" => selected[nofiles+1] = 0; spawn takephoto(win, ctlchan); <- ctlchan; working = 0; if (fnew == nil) break; spawn waittilready(camerapath+"jpg/"+filelist[hd fnew]+".jpg", ctlchan); e := <- ctlchan; working = 0; if (e == DONE) { spawn loadnewthumb(win, ctlchan); <- ctlchan; working = 0; } "abls" => spawn settingsscreen(int hd tl lst, ctlchan); <- ctlchan; "set" or "setdef" => spawn set(win, int hd tl lst, hd lst, ctlchan); <- ctlchan; "del" => spawn delete(win, int hd tl lst, ctlchan); <- ctlchan; "view" => i := int hd tl lst; unnew(win, i); if (i == -1) multiview(); else vw(i); "refresh" => spawn refresh(win, ctlchan); <- ctlchan; "refreshstate" => spawn mainscreen(win, 0, ctlchan); <- ctlchan; "dnld" => i := int hd tl lst; unnew(win, i); if (i == -1) multidownload(); else dnld(i, ""); "reset" => if (dialog("reset camera to default settings?",1,-1,coords)) { spawn resetcam(win,1, ctlchan); <- ctlchan; } } processing = 0; working = 0; } refresh(top: ref Tk->Toplevel, ctlchan: chan of int) { pid := sys->pctl(0, nil); spawn workingscreen2(coords,pid, ctlchan,0); refreshfilelist(top,1); ctlchan <-= DONE; } delete(top: ref Tk->Toplevel, i: int, ctlchan: chan of int) { pid := sys->pctl(0, nil); ok : int; s := ""; loi : list of int; loi = nil; if (i == -1) { for (k := 0; k < nofiles; k++) if (selected[k]) s+= filelist[k]+".jpg\n"; if (!dialog("Delete Selected files?\n\n"+s,1,-1,coords)) { ctlchan <-= DONE; return; } } else if (!dialog("Delete "+filelist[i]+".jpg?",1,i,coords)) { ctlchan <-= DONE; return; } spawn workingscreen2(coords,pid, ctlchan,0); s = ""; for (k := 0; k < nofiles; k++) { if ((i == -1 && selected[k]) || k == i) { s += filelist[k]+".jpg "; ok = sys->remove(camerapath+ "jpg/"+filelist[k]+".jpg"); if (ok == -1) s+="failed\n"; else { s+="ok\n"; loi = k :: loi; } } } if (loi == nil && i != -1) { dialog("cannot remove "+filelist[i]+".jpg?",0,i,coords); ctlchan <-= DONE; return; } while (loi != nil) { delloaded(filelist[hd loi],JPG); delloaded(filelist[hd loi],THUMB); delselect(hd loi); loi = tl loi; } refreshfilelist(top,0); getstore(top); if (i == -1) dialog("Files deleted:\n\n"+s,0,-1,coords); ctlchan <-= DONE; } delselect(n: int) { for (i := n; i < nofiles - 1; i++) selected[i] = selected[i+1]; selected[nofiles - 1] = 0; } doselect(top: ref Tk->Toplevel) { n := 0; for (i := 0; i < nofiles; i++) { col := "white"; if (selected[i]) { col = "blue"; n++; } tkcmd(top,".fthumb.p"+string i+" configure -bg "+col); } noselected = n; s := " files"; if (n == 1) s = " file"; tkcmd(top, ".f.fsnap.fsettings.mb2 configure -text {Selected\n("+string n+s+")}"); } takephoto(top: ref Tk->Toplevel, ctlchan: chan of int) { pid := sys->pctl(0, nil); spawn workingscreen2(coords,pid, ctlchan,0); fd := sys->open(camerapath+"ctl",sys->OWRITE); if (fd != nil) { e := sys->fprint(fd, "snap"); if (e < 0) { dialog("Could not take photo",0,-1,coords); getstore(top); } else { getstore(top); n := nofiles; for (i := 0; i < 5; i++) { refreshfilelist(top,1); sys->sleep(1000); if (nofiles > n) break; } } } ctlchan <-= DONE; } unnew(top: ref Tk->Toplevel, i: int) { if (fnew == nil) return; tmp : list of int = nil; for (;fnew != nil; fnew = tl fnew) { if (i == -1 && selected[hd fnew]) i = hd fnew; if (hd fnew == i) tkcmd(top, ".fthumb.mb"+string hd fnew+" configure -fg black; update"); else tmp = hd fnew :: tmp; } fnew = tmp; } refreshtime(top: ref Tk->Toplevel, ctlchan: chan of int) { pid := sys->pctl(0, nil); spawn workingscreen2(coords,pid, ctlchan,0); if (!samedate(top) && tkcmd(top, "variable time") == "1") settime(); gettime(top); ctlchan <-= DONE; } addtoplevel(top: ref Tk->Toplevel, name: string, abls: list of int, pid: int) { ltmp := toplevels; isin := 0; for (;ltmp != nil; ltmp = tl ltmp) { (tplvl, nil, nil, nil) := hd ltmp; if (tplvl == top) isin = 1; } if (!isin) toplevels = (top, name, abls, pid) :: toplevels; } deltoplevel(top: ref Tk->Toplevel) { ltmp : list of (ref Tk->Toplevel, string, list of int, int) = nil;; for (;toplevels != nil; toplevels = tl toplevels) { (tplvl, nm, loi, p) := hd toplevels; if (tplvl != top) ltmp = (tplvl, nm, loi, p) :: ltmp; } toplevels = ltmp; } resetcam(top: ref Tk->Toplevel, show: int, ctlchan: chan of int) { pid := sys->pctl(0, nil); spawn workingscreen2(coords,pid, ctlchan,0); for (i := 0; i < noabilities; i++) setstate(i, string abilities[i].factory); if (show) { ltmp := toplevels; for (;ltmp != nil; ltmp = tl ltmp) { (tplvl, nm, loi, p) := hd ltmp; for (; loi != nil; loi = tl loi) setmystate(tplvl, hd loi, string abilities[hd loi].factory); } if (top != nil) getstore(top); } ctlchan <-= DONE; } set(top: ref Tk->Toplevel, i: int, s: string, ctlchan: chan of int) { pid := sys->pctl(0, nil); spawn workingscreen2(getcoords(top),pid, ctlchan,0); val : string; if (s == "setdef") { val = string abilities[i].factory; setmystate(top,i,val); } else { if (abilities[i].dtype == MINMAX) { val = tkcmd(top, abilities[i].location+" get"); mul := getval(abilities[i].data, "mul"); val = string (int val * mul); } else { val = tkcmd(top, "variable "+abilities[i].pname); } } e := setstate(i,val); if (e == 2) getstore(top); else if (e == 0) dialog("cannot communicate with camera",0,-1,coords); ctlchan <-= DONE; } setstate(i: int, val: string): int { fd := sys->open(camerapath+"ctl",sys->OWRITE); if (fd != nil) { sys->fprint(fd, "%s %s",abilities[i].pname,val); abilities[i].read = val; if (abilities[i].pname == "ssiz" || abilities[i].pname == "scpn") return 2; return 1; } else return 0; } getfirst(s: string): string { (n, lst) := sys->tokenize(s," \n\t"); if (lst == nil) return ""; return hd lst; } getabl(pname: string): int { for (i := 0; i < noabilities; i++) if (abilities[i].pname == pname) return i; return -1; } getstate(top: ref Tk->Toplevel, i: int): int { fd := sys->open(camerapath+"state", sys->OWRITE); if (fd != nil) { sys->fprint(fd ,"%s", abilities[i].pname); sys->sleep(500); fdi := bufio->open(camerapath+"state",sys->OREAD); if (fdi != nil) { s := fdi.gets('\n'); if (s != nil) { (n,lst) := sys->tokenize(s,":\n"); val := hd tl lst; setmystate(top,i,val); } return 0; } } dialog("cannot communicate with camera",0,-1,coords); return -1; } setmystate(top: ref Tk->Toplevel, i: int, val: string) { abilities[i].read = val; if (abilities[i].dtype == LIST) tkcmd(top, "variable "+abilities[i].pname+" "+val); else if (abilities[i].dtype == MINMAX) { mul := getval(abilities[i].data, "mul"); tkcmd(top, abilities[i].location+" set "+string((int val)/mul)); } tkcmd(top, "update"); } max(a,b: int): int { if (a > b) return a; return b; } refreshfilelist(win: ref Tk->Toplevel, refresh: int): int { if (refresh) { fd := sys->open(camerapath+"ctl",sys->OWRITE); if (fd == nil) { dialog("cannot communicate with camera",0,-1,coords); return -1; } else sys->fprint(fd, "refresh"); } oldlist := filelist[:nofiles]; for (i := 0; i < nofiles; i++) { si := string i; tk->cmd(win, "grid forget .fthumb.mb"+si+" .fthumb.p"+si); tk->cmd(win, "destroy .fthumb.mb"+si+" .fthumb.p"+si+" .mthumb"+si); } (dirs,n) := readdir->init(camerapath+"jpg", readdir->NAME); if (n == -1) return -1; nofiles = n; row := 0; col := 0; nocols := -1; w1 := int tkcmd(win, ".f.f1.c1 cget -width"); w := 0; fnew = nil; for (i = 0; i < nofiles; i++) { filelist[i] = dirs[i].name; if (len filelist[i] > 3 && filelist[i][len filelist[i] - 4] == '.') filelist[i] = filelist[i][:len filelist[i]-4]; isnew := 1; for (k := 0; k < len oldlist; k++) { if (filelist[i] == oldlist[k]) { isnew = 0; break; } } si := string i; tkcmd(win, "menubutton .fthumb.mb"+si+" -bg white " + "-text {"+filelist[i]+".jpg} -menu .mthumb"+si+tkfontf); if (isnew && refresh) { fnew = i :: fnew; tkcmd(win, ".fthumb.mb"+si+" configure -fg red"); } thumbimg[i] = display.newimage(Rect((0,0),(90,90)),draw->RGB24,0,int 16rffcc00ff); e := tkcmd(win,"panel .fthumb.p"+si+" -borderwidth 2 -bg white"+ " -height 90 -width 90 -relief raised"); tk->putimage(win,".fthumb.p"+si, thumbimg[i],nil); tkcmd(win, "bind .fthumb.p"+si+" <Double-Button-1> {send butchan view "+si+"}"); tkcmd(win, "bind .fthumb.p"+si+" <ButtonPress-1> {send butchan selectonly "+si+"}"); tkcmd(win, "bind .fthumb.p"+si+" <ButtonPress-2> {send butchan select "+si+"}"); tkcmd(win, "bind .fthumb.p"+si+" <ButtonPress-3> {send butchan menu "+si+" %X %Y}"); thisw := int tkcmd(win, ".fthumb.mb"+si+" cget -width"); w += max(94, thisw); if ((nocols == -1 && w >= w1-(col*2)) || col == nocols) { nocols = col; col = 0; row+=2; w = thisw; } if (col == 0) tkcmd(win, "grid rowconfigure .fthumb "+string (row+1)+ " -minsize "+string (105 - 2*(3-ssize))); tkcmd(win, "grid .fthumb.mb"+si+" -row "+string row+" -column "+string col); tkcmd(win, "grid .fthumb.p"+si+" -row "+string (row+1)+" -column "+string col+" -sticky n"); tkcmd(win, "menu .mthumb"+si+tkfont); for (k = nothumbs; k < len menu; k++) tkcmd(win, ".mthumb"+si+" add command -text {"+menu[k].text+"} " + "-command {send butchan "+menu[k].com+" "+si+"}"); if (isloaded(filelist[i],THUMB) && usecache) loadthumbnail(win,i); col++; } if (row == 0) nocols = col; doselect(win); size := tkcmd(win, "grid size .fthumb"); csize := int size[:isat(size, " ")]; rsize := int size[isat(size, " ")+1:]; if (csize > nocols) tkcmd(win, "grid columndelete .fthumb "+string nocols+" "+string csize); if (rsize > row+1) tkcmd(win, "grid rowdelete .fthumb "+string (row+2)+" "+string rsize); height := string (2 + int tkcmd(win, ".fthumb cget -height")); width := tkcmd(win, ".f.f1.c1 cget -width"); colsize : int; if (nocols > 0) colsize = int width / nocols; else colsize = int width; for (i = 0; i < nocols; i++) tkcmd(win, "grid columnconfigure .fthumb "+string i+" -minsize "+string colsize); tkcmd(win, ".f.f1.c1 configure -scrollregion { 0 0 "+width+" "+height+"}"); tkcmd(win, "update"); return 0; } Mtype: adt { text, com: string; }; menu := array[] of { Mtype ("Show Thumbnail", "show"), Mtype ("Download", "dnld"), Mtype ("View", "view"), Mtype ("Delete", "del"), Mtype ("Clear Cache", "unload"), Mtype ("Refresh Files", "refresh"), }; tkcmd(top: ref Tk->Toplevel, cmd: string): string { if (cmd[len cmd - 1] == '$') cmd = cmd[:len cmd - 1] + tkfontb; else if (cmd[len cmd - 1] == '@') cmd = cmd[:len cmd - 1] + tkfont; if (cmd[len cmd - 1] == '&') cmd = cmd[:len cmd - 1] + butheight+tkfont; e := tk->cmd(top, cmd); if (e != "" && e[0] == '!') sys->print("tk error: '%s': %s\n",cmd,e); return e; } loadnewthumb(top: ref Tk->Toplevel, ctlchan: chan of int) { pid := sys->pctl(0,nil); spawn workingscreen2(coords,pid, ctlchan,0); getstore(top); for (tmp := fnew; tmp != nil; tmp = tl tmp) loadthumbnail(top,hd tmp); ctlchan <-= DONE; } loadthumb(top: ref Tk->Toplevel, i: int, ctlchan: chan of int) { pid := sys->pctl(0, nil); spawn workingscreen2(coords,pid, ctlchan,0); if (i == -1) { for (k := 0; k < nofiles; k++) if (selected[k]) if (loadthumbnail(top, k) != 0) break; } else loadthumbnail(top, i); ctlchan <-= DONE; } loadthumbnail(top: ref Tk->Toplevel, i: int): int { fd : ref sys->FD; if (usecache && isloaded(filelist[i],THUMB)) fd = sys->open(tmppath+filelist[i]+"."+string THUMB+"~",sys->OREAD); else fd = sys->open(camerapath+"thumb/"+filelist[i]+".bit",sys->OREAD); if (fd == nil) { if (usecache && isloaded(filelist[i],THUMB)) { delloaded(filelist[i],THUMB); return loadthumbnail(top,i); } else dialog("cannot open "+filelist[i]+".bit",0,-1,coords); return -2; } image := display.readimage(fd); if (image == nil) { if (usecache && isloaded(filelist[i],THUMB)) { delloaded(filelist[i],THUMB); return loadthumbnail(top,i); } else dialog("Could not load thumbnail: "+filelist[i]+".jpg",0,-1,coords); return -1; } else { p := Point((90-image.r.max.x)/2,(90-image.r.max.y)/2); thumbimg[i].draw(image.r.addpt(p), image,nil,(0,0)); si := string i; tkcmd(top,".fthumb.p"+si+" dirty"); fd = nil; n := -1; if (usecache) { fd = sys->create(tmppath+filelist[i]+"."+string THUMB+"~",sys->OWRITE,8r666); n = display.writeimage(fd, image); } x := int tkcmd(top, ".fthumb.mb"+string i+" cget -actx"); y := int tkcmd(top, ".fthumb.mb"+string i+" cget -acty"); h := int tkcmd(top, ".fthumb.mb"+string i+" cget -height"); x1 := int tkcmd(top, ".fthumb cget -actx"); y1 := int tkcmd(top, ".fthumb cget -acty"); tkcmd(top, ".f.f1.c1 see "+string (x-x1)+" " +string (y-y1)+ " "+string (x-x1+90)+" " +string (y-y1+h+102)+"; update"); if (!usecache || n == 0) imgloaded = (filelist[i],THUMB) :: imgloaded; } return 0; } isloaded(name: string, ftype: int): int { tmp := imgloaded; while (tmp != nil) { ic := hd tmp; if (ic.name == name && ic.imgtype == ftype) return 1; tmp = tl tmp; } return 0; } delloaded(name: string, ftype: int) { tmp : list of Imgloaded; tmp = nil; while (imgloaded != nil) { ic := hd imgloaded; if (ic.name != name || ic.imgtype != ftype) tmp = ic :: tmp; else sys->remove(tmppath+ic.name+"."+string ic.imgtype+"~"); imgloaded = tl imgloaded; } imgloaded = tmp; } dialog(msg: string, diagtype, img: int, r: Rect): int { if (diagtype == 2) diagtype = 0; else working = 0; tmpimg : ref draw->Image; out := 0; title := "Dialog"; if (diagtype == 0) title = "Alert!"; (win, titlebar) := tkclient->toplevel(context, "" , title, Tkclient->Appl); diagchan := chan of string; tk->namechan(win, diagchan, "diagchan"); tkcmd(win, "frame .f"); tkcmd(win, "label .f.l -text {"+msg+"}"+tkfont); tkcmd(win, "button .f.bo -text { ok } -command {send diagchan ok} "+butheight+tkfont); tkcmd(win, "button .f.bc -text {cancel} -command {send diagchan cancel}"+butheight+tkfont); if (img >= 0 && isloaded(filelist[img], THUMB) && usecache) { fd := sys->open(tmppath+filelist[img]+"."+string THUMB+"~", sys->OREAD); if (fd != nil) { tmpimg = display.readimage(fd); tkcmd(win,"panel .f.p -height "+string tmpimg.r.max.y+ " -width "+string tmpimg.r.max.x+" -borderwidth 2 -relief raised"); tk->putimage(win,".f.p", tmpimg, nil); tkcmd(win, "grid .f.p -row 1 -column 0 -columnspan 2 -padx 5 -pady 5"); } } tkcmd(win, "grid .f.l -row 0 -column 0 -columnspan 2 -padx 10 -pady 5"); if (diagtype == 1) { tkcmd(win, "grid .f.bo -row 2 -column 0 -padx 5 -pady 5"); tkcmd(win, "grid .f.bc -row 2 -column 1 -padx 5 -pady 5"); } else tkcmd(win, "grid .f.bo -row 2 -column 0 -columnspan 2 -padx 5 -pady 5"); if (!r.eq(nilrect)) centrewin(win, r, 1); else tkcmd(win, "pack .f; focus .; update"); tkclient->onscreen(win, "exact"); tkclient->startinput(win, "kbd"::"ptr"::nil); main: for (;;) { alt { s := <-win.ctxt.kbd => tk->keyboard(win, s); s := <-win.ctxt.ptr => tk->pointer(win, *s); inp := <-diagchan => if (inp == "ok") { out = 1; break main; } if (inp == "cancel") break main; title = <-win.ctxt.ctl or title = <-win.wreq or title = <-titlebar => if (title == "exit") break main; else tkclient->wmctl(win, title); } } return out; } snapscr := array[] of { "label .f.fsnap.ltime -text {Date and Time} $", "label .f.fsnap.lstore -text {Memory Status} $", "label .f.fsnap.lzpos -text {Zoom} $", "label .f.fsnap.lssiz -text {Resolution} $", "label .f.fsnap.lscpn -text {Compression} $", "grid .f.fsnap.ltime -row 0 -column 0 -sticky sw", "grid .f.fsnap.lstore -row 0 -column 1 -sticky sw", "grid .f.fsnap.lscpn -row 2 -column 0 -sticky sw", "grid .f.fsnap.lssiz -row 2 -column 1 -sticky sw", "grid .f.fsnap.lzpos -row 2 -column 2 -sticky sw", "grid .f.fsnap.ftime -row 1 -column 0 -sticky nsew", "grid .f.fsnap.fstore -row 1 -column 1 -sticky nsew", "grid .f.fsnap.fsettings -row 1 -column 2 -sticky nsew", "grid .f.fsnap.fscpn -row 3 -column 0 -sticky nsew", "grid .f.fsnap.fssiz -row 3 -column 1 -sticky nsew", "grid .f.fsnap.fzpos -row 3 -column 2 -sticky nsew", "grid .f.fsnap.b -row 4 -column 0 -columnspan 3", "grid rowconfigure .f.fsnap 0 -minsize 30", "grid rowconfigure .f.fsnap 2 -minsize 30", "grid rowconfigure .f.fsnap 4 -minsize 30", "update", }; mainscreen(win: ref Tk->Toplevel, opt: int, ctlchan: chan of int) { pid := sys->pctl(0, nil); spawn workingscreen2(coords, pid, ctlchan, opt); if (opt == 1) { for (tk1 := 0; tk1 < len snapscr; tk1++) tkcmd(win, snapscr[tk1]); gettime(win); if (samedate(win)) tkcmd(win, "variable time 1; update"); } getstore(win); lst := getabl("scpn") :: getabl("ssiz") :: getabl("zpos") :: nil; if (getstate(win, hd tl tl lst) == 0); if (getstate(win, hd tl lst) == 0); getstate(win, hd lst); if (opt == 1) { addtoplevel(win, "", lst, -1); height := tkcmd(win, ".f.fsnap cget -height"); width := tkcmd(win, ".f.fsnap cget -width"); # tkcmd(win, ".f.f2.c1 configure -scrollregion { 0 0 "+width+" "+height+"}"); # tkcmd(win, ".f.f2.c1 configure -height "+height+"}"); } ctlchan <-= DONE; } kill(pid: int) { pctl := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE); if (pctl != nil) sys->write(pctl, array of byte "kill", len "kill"); } gettime(win: ref Tk->Toplevel) { tkcmd(win,".f.fsnap.ftime.l3 configure -text {}"+tkfont); tkcmd(win,".f.fsnap.ftime.l4 configure -text {}"+tkfont); fdi := bufio->open(camerapath+"date",sys->OREAD); if (fdi != nil) { s := fdi.gets('\n'); if (s != nil) { if (s[len s - 1] == '\n') s = s[:len s - 1]; tm := daytime->local(daytime->now()); time := sys->sprint("%d/%d/%d %d:%d:%d", tm.mon+1, tm.mday, tm.year-100, tm.hour,tm.min,tm.sec); ltime = addzeros(time); ctime = addzeros(s[len "date is ":]); tk->cmd(win,".f.fsnap.ftime.l3 configure -text {"+ltime+"}"); tk->cmd(win,".f.fsnap.ftime.l4 configure -text {"+ctime+"}"); } } if (len ltime < 16) ltime = "??/??/?? ??:??:??"; if (len ctime < 16) ctime = "??/??/?? ??:??:??"; tkcmd(win, "update"); } addzeros(s: string): string { s[len s] = ' '; rs := ""; start := 0; isnum := 0; for (i := 0; i < len s; i++) { if (s[i] < '0' || s[i] > '9') { if (isnum && i - start < 2) rs[len rs] = '0'; rs += s[start:i+1]; start = i+1; isnum = 0; } else isnum = 1; } i = len rs - 1; while (i >= 0 && rs[i] == ' ') i--; return rs[:i+1]; } samedate(win: ref Tk->Toplevel): int { s1 := tkcmd(win, ".f.fsnap.ftime.l3 cget -text"); s2 := tkcmd(win, ".f.fsnap.ftime.l4 cget -text"); if (s1 == "" || s1 == "") return 0; if (s1[:len s1 - 3] == s2[:len s2 - 3]) return 1; return 0; } settime() { tm := daytime->local(daytime->now()); fd := sys->open(camerapath+"date", sys->OWRITE); if (fd != nil) { sys->fprint(fd, "%s", addzeros(sys->sprint("%d/%d/%d %d:%d:%d" ,tm.mon+1, tm.mday, tm.year-100, tm.hour,tm.min,tm.sec))); } } getstore(win: ref Tk->Toplevel) { fdi := bufio->open(camerapath+"storage",sys->OREAD); if (fdi != nil) { for(i := 0; i < 3; i++) { s := fdi.gets('\n'); if (s == nil) break; if (i > 0) { (n,lst) := sys->tokenize(s,"\t\n:"); val := string int hd tl lst; if (i == 2 && val == "0") tkcmd(win, ".f.fsnap.b configure -state disabled"); else tkcmd(win, ".f.fsnap.b configure -state normal"); tkcmd(win,".f.fsnap.fstore.l"+string (2+i)+" configure -text {"+val+" }"); } } tkcmd(win, "update"); } } contains(s: string, test: string): int { num :=0; if (len test > len s) return 0; for (i := 0; i < (1 + (len s) - (len test)); i++) { if (test == s[i:i+len test]) num++; } return num; } multidownload() { getpath := selectfile->filename(context, display.image, "Multiple download to directory...", nil, lastpath); if (getpath == "" || getpath[0] != '/' || getpath[len getpath - 1] != '/') return; s := ""; for (k := 0; k < nofiles; k++) { if (selected[k]) { e := dnld(k,getpath); if (e != 1) s += filelist[k]+".jpg "; if (e == 3) { s += "cancelled\n"; break; } else if (e == 0) s += "failed\n"; working = 0; } } if (s != "") s = ":\n\n"+s; dialog("Multiple download complete"+s,0,-1,coords); } downloading := ""; dnld(i: int, path: string): int { ctlchan := chan of int; ctlchans := chan of string; chanout := chan of string; spawn downloadscreen(coords, i, ctlchans, chanout); spawn download(i,path,ctlchan, ctlchans, chanout); pid := <-ctlchan; alt { s := <-ctlchans => chanout <-= "!done!"; if (s == "kill") { if (downloading != "") { (n,lst) := sys->tokenize(downloading, " \t\n"); for(;lst != nil; lst = tl lst) sys->remove(hd lst); } kill(pid); return 3; } else return dnld(i, "!"+s); e := <-ctlchan => chanout <-= "!show!"; chanout <-= "!done!"; return e; } return 0; } filelenrefresh(filename: string): int { fd := sys->open(camerapath+"ctl",sys->OWRITE); if (fd != nil) { sys->fprint(fd, "refresh"); (n, dir) := sys->stat(filename); if (n == -1) return -1; return int dir.length; } return -1; } testfilesize(filename: string): int { e := filelenrefresh(filename); if (e == 0) { e2 := dialog("Camera is still processing image\nwait until ready?",1,-1,coords); if (e2 == 0) return 0; ctlchan := chan of int; spawn waittilready(filename, ctlchan); e3 := <- ctlchan; working = 0; if (e3 == KILLED) return 0; return testfilesize(filename); } else return e; } waittilready(filename: string, ctlchan: chan of int) { pid := sys->pctl(0, nil); spawn workingscreen2(coords,pid,ctlchan,0); for (;;) { if (filelenrefresh(filename) != 0) break; sys->sleep(2000); } ctlchan <-= DONE; } download(i: int, path: string, ctlchan: chan of int, ctlchans, chanout: chan of string) { ctlchan <-= sys->pctl(0, nil); downloading = ""; savename : string; if (path == "") { savename = selectfile->filename(context, display.image, "Save "+filelist[i]+".jpg to directory...", "*.jpg" :: "*.jpeg" :: nil, lastpath); if (savename == "" || savename[0] != '/') { ctlchan <-= 0; return; } } else savename = path; # Used when retrying due to cache copy failing if (savename[0] == '!') { delloaded(filelist[i],JPG); savename = savename[1:]; path = ""; } confirm := 1; # Don't confirm overwrite if (savename[0] == '$') { confirm = 0; savename = savename[1:]; } if (savename[len savename - 1] == '/') savename += filelist[i]+".jpg"; if (!hasext(savename, ".jpg")) savename += ".jpg"; p := isat2(savename,"/"); lastpath = savename[:p+1]; filename := camerapath+"jpg/"+filelist[i]+".jpg"; filesize := testfilesize(filename); cached := 0; if (filesize > 0 && isloaded(filelist[i],JPG) && usecache) { cachefilename := tmppath+filelist[i]+"."+string JPG+"~"; if (testfilesize(cachefilename) == filesize) { cached = 1; filename = cachefilename; } else delloaded(filelist[i],JPG); } fd := sys->open(filename, sys->OREAD); if (filesize < 1 || fd == nil) { ctlchan <-= -1; return; } read := 0; cancel : int; buf : array of byte; fd2, fd3 : ref sys->FD = nil; cachename := tmppath+filelist[i]+"."+string JPG+"~"; if (confirm) (fd2, cancel) = create(savename, coords); else fd2 = sys->create(savename,sys->OWRITE, 8r666); if (fd2 == nil) { ctlchan <-= cancel; return; } if (usecache && !cached) fd3 = sys->create(cachename,sys->OWRITE,8r666); chanout <-= "!show!"; chanout <-= "l2 Downloading..."; chanout <-= "pc 0"; n : int; downloading = savename; if (fd3 != nil) downloading += " "+cachename; loop: for(;;) { rlen := 8192; if (read + rlen >= filesize) rlen = filesize - read; buf = array[rlen] of byte; n = sys->read(fd,buf,len buf); read += n; sout := "pc "+string ( (100*read)/filesize); chanout <-= sout; if (n < 1) break loop; written := 0; while (written < n) { n2 := sys->write(fd2,buf,n); if (n2 < 1) break loop; if (fd3 != nil) sys->write(fd3,buf,n); written += n2; } } chanout <-= "pc 100"; downloading = ""; fd = nil; fd2 = nil; if (read < filesize || n == -1) { if (cached) { ctlchans <-= savename; return; } sys->remove(savename); sys->remove(cachename); if (path == "") dialog(sys->sprint("Download Failed: %s.jpg\nread %d of %d bytes\n", filelist[i],read,filesize), 0, i,coords); ctlchan <-= 0; return; } # save it in cache if (usecache) imgloaded = (filelist[i],JPG) :: imgloaded; if (path == "") dialog(filelist[i]+".jpg downloaded",0,i,coords); ctlchan <-= 1; } downloadscr := array[] of { "frame .f -borderwidth 2 -relief raised", "label .f.l1 -text { } @", "label .f.l2 -text {Waiting...} @", "button .f.b -text {Cancel} -command {send ctlchans kill} &", "grid .f.l1 -row 0 -column 0 -columnspan 2 -pady 5", "grid .f.l2 -row 2 -column 1 -sticky w -padx 10", "grid .f.p -row 3 -column 1 -columnspan 1 -padx 10", "grid .f.b -row 4 -column 0 -pady 5 -columnspan 2", }; downloadscreen(r: Rect, i: int, ctlchans, chanin: chan of string) { working = 1; <- chanin; (top, nil) := tkclient->toplevel(context,"", nil, tkclient->Plain); progr := Rect((0,0),(100,15)); imgbg := display.newimage(progr,draw->CMAP8,1,draw->Black); black := display.newimage(progr,draw->CMAP8,1,draw->Black); white := display.newimage(progr,draw->CMAP8,1,draw->White); imgfg := display.newimage(progr,draw->CMAP8,1,draw->Blue); tkcmd(top, "panel .f.p -width 100 -height 15 -bg white -borderwidth 2 - relief raised"); tk->putimage(top, ".f.p",imgbg,nil); tk->namechan(top, ctlchans, "ctlchans"); for (tk1 := 0; tk1 < len downloadscr; tk1++) tkcmd(top, downloadscr[tk1]); tmpimg : ref Image = nil; if (i >= 0 && isloaded(filelist[i], THUMB) && usecache) tmpimg = display.open(tmppath+filelist[i]+"."+string THUMB+"~"); if (tmpimg == nil) tmpimg = procimg; if (tmpimg != nil) { w := tmpimg.r.dx(); h := tmpimg.r.dy(); tkcmd(top, "panel .f.p2 -width "+string w+" -height "+string h+ " -borderwidth 2 -relief raised"); tk->putimage(top, ".f.p2", tmpimg, nil); tkcmd(top, "grid .f.p2 -row 2 -column 0 -rowspan 2 -sticky e"); tkcmd(top, "grid columnconfigure .f 0 -minsize "+string (w + 14)); } tkcmd(top, ".f.l1 configure -text {"+filelist[i]+".jpg}"); centrewin(top,r,1); oldcoords := coords; tkclient->onscreen(top, "exact"); tkclient->startinput(top, "kbd"::"ptr"::nil); main: for (;;) { alt { s := <-top.ctxt.kbd => tk->keyboard(top, s); s := <-top.ctxt.ptr => tk->pointer(top, *s); text := <-chanin => if (!oldcoords.eq(coords)) { centrewin(top,coords,0); oldcoords = coords; } if (text == "!done!") break main; if (text[:2] == "pc") { val := int text[3:]; imgbg.draw(((0,0),(val,15)), imgfg,nil,(0,0)); if (val != 100) imgbg.draw(((val+1,0),(100,15)), black,nil,(0,0)); imgbg.text((42,1),white,(0,0),font, text[3:]+"%"); tkcmd(top,".f.p dirty; update"); } else if (text[:2] == "l2") tkcmd(top, ".f.l2 configure -text {"+text[3:]+"}; update"); } } working = 0; } centrewin(top: ref Tk->Toplevel, r: Rect, first: int) { s := ""; if (first) s = "pack .f;"; w := int tkcmd(top, ".f cget -width"); h := int tkcmd(top, ".f cget -height"); tmp := tk->cmd(top, ".Wm_t cget -height"); if (tmp != "" && tmp[0] != '!') { h += int tmp; s += "focus .;"; } px := r.min.x + ((r.max.x - r.min.x - w) / 2); py := r.min.y + ((r.max.y - r.min.y - h) / 2); tkcmd(top, ". configure -x "+string px+" -y "+string py); tkcmd(top, s+"raise .; update"); } workingscr2 := array[] of { "frame .f -borderwidth 2 -relief raised", "label .f.l3 -text { } -width 220 -height 2", "label .f.l -text {Please Wait} @", "label .f.l2 -text {|} -width 20 @", "button .f.b -text {Cancel} -command {send chanin kill} &", "grid .f.l -row 1 -column 0 -sticky e", "grid .f.l2 -row 1 -column 1 -sticky w", "grid .f.b -pady 5 -row 3 -column 0 -columnspan 2", "grid .f.l3 -row 4 -column 0 -columnspan 2", "grid rowconfigure .f 1 -minsize 80", }; workingscreen2(r : Rect, pid: int, ctlchan: chan of int, loading: int) { (top, nil) := tkclient->toplevel(context,"",nil, tkclient->Plain); chanin := chan of string; tk->namechan(top, chanin, "chanin"); for (tk1 := 0; tk1 < len workingscr2; tk1++) tkcmd(top, workingscr2[tk1]); if (loading) { # loadimg := display.open("camload.bit"); if (loadimg != nil) { w := loadimg.r.dx(); h := loadimg.r.dy(); tkcmd(top, "panel .f.p -width "+string w+" -height "+string h+ " -borderwidth 2 -relief raised"); tk->putimage(top, ".f.p", loadimg, nil); tkcmd(top, "grid .f.p -row 2 -column 0 -columnspan 2 -pady 5 -padx 20"); tkcmd(top, "grid forget .f.l .f.l2; grid rowconfigure .f 1 -minsize 20"); } } else { if (procimg != nil) { w := procimg.r.dx(); h := procimg.r.dy(); tkcmd(top, "panel .f.p -width "+string w+" -height "+string h+ " -borderwidth 2 -relief raised"); tk->putimage(top, ".f.p", procimg, nil); tkcmd(top, "grid .f.p -row 2 -column 0 -columnspan 2"); tkcmd(top, "grid rowconfigure .f 1 -minsize 30"); tkcmd(top, "grid rowconfigure .f 2 -minsize 50"); } } centrewin(top,r,1); spawn workingupdate(top,chanin); tkclient->onscreen(top, "exact"); tkclient->startinput(top, "kbd"::"ptr"::nil); main: for (;;) { alt { s := <-top.ctxt.kbd => tk->keyboard(top, s); s := <-top.ctxt.ptr => tk->pointer(top, *s); inp := <-chanin => if (inp == "done") break main; if (inp == "kill") { working = 0; if (pid != -1) kill(pid); ctlchan <-= KILLED; <-chanin; break main; } } } } workingupdate(top: ref Tk->Toplevel, chanout: chan of string) { show := array[] of { "/", "-", "\\\\", "|", }; if (working) { chanout <-= "done"; return; } working = 1; oldcoords := coords; hidden := 0; loop: for(;;) { for (i := 0; i < 4; i++) { sys->sleep(100); tkcmd(top, ".f.l2 configure -text {"+show[i]+"}; update"); if (!working) break loop; if (!oldcoords.eq(coords)) { centrewin(top, coords,0); oldcoords = coords; } } } chanout <-= "done"; } scrollx := 0; scrolly := 0; resizemain(top: ref Tk->Toplevel, init: int) { h, w: int; if (init) { growheight(top, 4000); h = int tkcmd(top, ".f.fsnap cget -height") + int tkcmd(top, ".Wm_t cget -height") + 2 * (124 - (5*(3-ssize))); if (h > display.image.r.dy()) h = display.image.r.dy(); w = display.image.r.dx(); } else { r := tk->rect(top, ".", 0); h = r.dy(); w = r.dx(); } ht := int tkcmd(top, ".Wm_t cget -height"); hf := int tkcmd(top, ".f cget -height"); wf := int tkcmd(top, ".f cget -width"); wsb := int tkcmd(top, ".f.f1.sb1 cget -width"); growwidth(top, w - 4); ws := int tkcmd(top, ".f.fsnap cget -width"); if (w > ws + 4) w = ws + 4; shrinkwidth(top,w - 4); ws = int tkcmd(top, ".f.fsnap cget -width"); if (w < ws || init) w = ws + 4; hmax := ((3*(h - ht))/5) - 4; growheight(top, hmax); shrinkheight(top, hmax); hs := int tkcmd(top, ".f.fsnap cget -height"); hmb := int tkcmd(top, ".f.fsnap.fsettings.mb cget -height"); if (h < ht+hs + 107 + hmb) h = ht+hs+107 + hmb; # hc2 = int tkcmd(top, ".f.fsnap cget -height"); wc2 := int tkcmd(top, ".f.fsnap cget -width"); hc1 := h - ht - hs - 4; wc1 := w-wsb-4; # wc1 = wc2 - wsb; tkcmd(top, ".f.f1.c1 configure -height "+string hc1+" -width "+string wc1); # tkcmd(top, ".f.f2.c1 configure -height "+string hc2+" -width "+string wc2); if (w < wc2 + 4) w = wc2 + 4; ws = int tkcmd(top, ".f.fsnap cget -width"); hs = int tkcmd(top, ".f.fsnap cget -height"); tkcmd(top, ". configure -height "+string h+" -width "+string w+"; update"); refreshfilelist(top, 0); } growwidth(top: ref Tk->Toplevel, wc2: int) { ws := int tkcmd(top, ".f.fsnap cget -width"); if (wc2 > ws && reducew[2]) { tkcmd(top, ".f.fsnap.ftime.l1 configure -text {Local:}"); tkcmd(top, ".f.fsnap.ftime.l2 configure -text {Camera:}"); tkcmd(top, ".f.fsnap.ftime.cb configure -text {Set to local time}"); reducew[2] = 0; } ws = int tkcmd(top, ".f.fsnap cget -width"); if (wc2 > ws && reducew[1]) { tkcmd(top, ".f.fsnap.ftime.l3 configure -text {"+ltime+"}"); tkcmd(top, ".f.fsnap.ftime.l4 configure -text {"+ctime+"}"); tkcmd(top, ".f.fsnap.ftime.cb configure -text {Set camera to local time}"); reducew[1] = 0; } ws = int tkcmd(top, ".f.fsnap cget -width"); if (wc2 > ws && reducew[0]) { tkcmd(top, ".f.fsnap.fstore.l1 configure -text { Photos taken:}"); reducew[0] = 0; } ws = int tkcmd(top, ".f.fsnap cget -width"); if (wc2 > ws) { wfs += wc2 - ws; if (wfs > 125-(20*(3-ssize))) wfs = 125-(20*(3-ssize)); tkcmd(top, "grid columnconfigure .f.fsnap 2 -minsize "+string wfs); } } growheight(top: ref Tk->Toplevel, hc2: int) { hs := int tkcmd(top, ".f.fsnap cget -height"); if (hc2 > hs) { tk->cmd(top, "grid .f.fsnap.fsettings.mb2 -row 2 -column 0 -sticky ew"); tk->cmd(top, "grid .f.fsnap.ftime.cb -row 2 -column 0 -columnspan 2"); tk->cmd(top, "grid .f.fsnap.ftime.b -row 3 -column 0 -columnspan 2"); } hs = int tkcmd(top, ".f.fsnap cget -height"); if (hc2 > hs) { hsc := int tkcmd(top, ".f.fsnap.fzpos.sc cget -height"); hsc += hc2 - hs; if (hsc > 88-(10*(3-ssize))) hsc = 88-(10*(3-ssize)); tkcmd(top, ".f.fsnap.fzpos.sc configure -height "+string hsc); } hs = int tkcmd(top, ".f.fsnap cget -height"); if (hc2 > hs) { hfs += hc2 - hs; if (hfs > 30 - (5*(3-ssize))) hfs = 30- (5*(3-ssize)); tkcmd(top, "grid rowconfigure .f.fsnap 0 -minsize "+string hfs); tkcmd(top, "grid rowconfigure .f.fsnap 2 -minsize "+string hfs); tkcmd(top, "grid rowconfigure .f.fsnap 4 -minsize "+string hfs); } } shrinkheight(top: ref Tk->Toplevel, hc2: int) { hs := int tkcmd(top, ".f.fsnap cget -height"); if (hc2 < hs) { hfs -= hs - hc2; if (hfs < 15) hfs = 15; tkcmd(top, "grid rowconfigure .f.fsnap 0 -minsize "+string hfs); tkcmd(top, "grid rowconfigure .f.fsnap 2 -minsize "+string hfs); tkcmd(top, "grid rowconfigure .f.fsnap 4 -minsize "+string hfs); } hs = int tkcmd(top, ".f.fsnap cget -height"); if (hc2 < hs) { hsc := int tkcmd(top, ".f.fsnap.fzpos.sc cget -height"); hsc -= hs - hc2; if (hsc < 55-(5*(3-ssize))) hsc = 55-(5*(3-ssize)); tkcmd(top, ".f.fsnap.fzpos.sc configure -height "+string hsc); } hs = int tkcmd(top, ".f.fsnap cget -height"); if (hc2 < hs) { tk->cmd(top, "grid forget .f.fsnap.fsettings.mb2"); tk->cmd(top, "grid forget .f.fsnap.ftime.cb"); tk->cmd(top, "grid forget .f.fsnap.ftime.b"); } } shrinkwidth(top: ref Tk->Toplevel, wc2: int) { ws := int tkcmd(top, ".f.fsnap cget -width"); wib := int tkcmd(top, ".f.fsnap.fsettings.b cget -width"); if (wc2 < ws) { diff := ws - wc2; wfs -= diff; if (wfs < wib) wfs = wib; tkcmd(top, "grid columnconfigure .f.fsnap 2 -minsize "+string wfs); } ws = int tkcmd(top, ".f.fsnap cget -width"); if (wc2 < ws) { tkcmd(top, ".f.fsnap.fstore.l1 configure -text { Taken:}"); reducew[0] = 1; } ws = int tkcmd(top, ".f.fsnap cget -width"); if (wc2 < ws) { tkcmd(top, ".f.fsnap.ftime.l3 configure -text {"+ltime[len ltime - 8:]+"}"); tkcmd(top, ".f.fsnap.ftime.l4 configure -text {"+ctime[len ctime - 8:]+"}"); tkcmd(top, ".f.fsnap.ftime.cb configure -text {Set to local time}"); reducew[1] = 1; } ws = int tkcmd(top, ".f.fsnap cget -width"); if (wc2 < ws) { tkcmd(top, ".f.fsnap.ftime.l1 configure -text {C:}"); tkcmd(top, ".f.fsnap.ftime.l2 configure -text {}"); tkcmd(top, ".f.fsnap.ftime.l3 configure -text {"+ctime[len ctime - 17:len ctime - 8]+"}"); tkcmd(top, ".f.fsnap.ftime.cb configure -text {Set local}"); reducew[2] = 1; } ws = int tkcmd(top, ".f.fsnap cget -width"); if (wc2 > ws) { wfs = 125-(20*(3-ssize)); tkcmd(top, "grid columnconfigure .f.fsnap 2 -minsize "+string wfs); } } ltime, ctime: string; wfs := 150; hfs := 30; reducew := array[10] of { * => 0 }; getcoords(top: ref Tk->Toplevel): Rect { h := int tkcmd(top, ". cget -height"); w := int tkcmd(top, ". cget -width"); x := int tkcmd(top, ". cget -actx"); y := int tkcmd(top, ". cget -acty"); r := Rect((x,y),(x+w,y+h)); return r; } viewscr := array[] of { "frame .f -bg", "canvas .f.c -yscrollcommand {.f.sy set} -xscrollcommand {.f.sx set} -height 300 -width 500", "scrollbar .f.sx -command {.f.c xview} -orient horizontal", "scrollbar .f.sy -command {.f.c yview}", "grid .f.c -row 0 -column 0", "grid .f.sy -row 0 -column 1 -sticky ns", "grid .f.sx -row 1 -column 0 -sticky ew", "bind .Wm_t <ButtonPress-1> +{focus .}", "bind .Wm_t.title <ButtonPress-1> +{focus .}", "pack propagate . 0", "menu .m @", ".m add command -text {Save As...}", ".m add separator", ".m add command -text {bit} -command {send butchan save bit}", ".m add command -text {jpeg} -command {send butchan save jpg}", }; resizeview(top: ref Tk->Toplevel, wp,hp: int) { w := int tkcmd(top, ". cget -width"); h := int tkcmd(top, ". cget -height"); hs := int tkcmd(top, ".f.sx cget -height"); ws := int tkcmd(top, ".f.sy cget -width"); ht := int tkcmd(top, ".Wm_t cget -height"); wc := w - ws - 4; hc := h - hs - ht - 6; wpc := wc - wp; hpc := hc - hp; if (wpc > 0) { wc -= wpc; w -= wpc; } if (hpc > 0) { hc -= hpc; h -= hpc; } tkcmd(top, ". configure -height "+string h+" -width "+string w); tkcmd(top, ".f.c configure -height "+string hc+" -width "+string wc); tkcmd(top, "update"); } multiview() { s := ""; for (k := 0; k < nofiles; k++) { if (selected[k]) { e := vw(k); if (e != 0) s += filelist[k]+".jpg "; if (e == 3) { s += "cancelled\n"; break; } else if (e == -1) s += "failed\n"; } } if (s != "") dialog("Multiple view complete:\n\n"+s,0,-1,coords); } vw(i: int): int { # raise window if it is already open low := toplevels; for(; low != nil; low = tl low) { (tplvl, name, nil, nil) := hd low; if (filelist[i]+".jpg" == name) { tkcmd(tplvl, "raise .; focus .; update"); return 0; } } ctlchan := chan of int; ctlchans := chan of string; chanout := chan of string; chanin := chan of string; spawn downloadscreen(coords, i, ctlchans, chanout); chanout <-= "!show!"; spawn view(i,ctlchan, chanin, chanout); pid := <-ctlchan; killed := 0; for (;;) alt { s := <-ctlchans => if (s == "kill") { chanin <-= "kill"; killed = 1; } e := <-ctlchan => chanout <-= "!done!"; if (killed) return 3; if (e == -1) dialog(sys->sprint("Cannot read file: %s.jpg\n%r",filelist[i]),0,i,coords); if (e == -2) return vw(i); else return e; } return 0; } view(i: int, ctlchan: chan of int, chanin, chanout: chan of string) { ctlchan <-= sys->pctl(0, nil); titlename := filelist[i]+".jpg"; filename := camerapath+"jpg/"+filelist[i]+".jpg"; filesize := testfilesize(filename); cached := 0; if (filesize > 0 && isloaded(filelist[i],JPG) && usecache) { cachefilename := tmppath+filelist[i]+"."+string JPG+"~"; if (testfilesize(cachefilename) == filesize) { cached = 1; filename = cachefilename; } else delloaded(filelist[i],JPG); } if (filesize < 1) { ctlchan <-= -1; return; } img: ref Image; cachepath := ""; if (!cached && usecache) cachepath = tmppath+filelist[i]+"."+string JPG+"~"; img = readjpg->jpg2img(filename, cachepath, chanin, chanout); if(img == nil) { if (cachepath != nil) sys->remove(cachepath); if (!cached) ctlchan <-= -1; else { delloaded(filelist[i], JPG); ctlchan <-= -2; } return; } else { chanout <-= "l2 Displaying"; if (cachepath != "") imgloaded = (filelist[i], JPG) :: imgloaded; (t, titlechan) := tkclient->toplevel(context, "", titlename, Tkclient->Appl); butchan := chan of string; tk->namechan(t, butchan, "butchan"); tkcmd(t, "focus .Wm_t; update"); for (tk1 := 0; tk1 < len viewscr; tk1++) tkcmd(t, viewscr[tk1]); w := img.r.dx(); h := img.r.dy(); tkcmd(t, "panel .p -width "+string w+" -height "+string h); tk->putimage(t, ".p",img,nil); tkcmd(t, "bind .p <ButtonPress-2> {send butchan move %X %Y}"); tkcmd(t, "bind .p <ButtonRelease-2> {send butchan release}"); tkcmd(t, "bind .p <ButtonPress-3> {send butchan menu %X %Y}"); tkcmd(t, ".f.c create window 0 0 -window .p -anchor nw"); tkcmd(t, ".f.c configure -scrollregion {0 0 "+string w+" "+string h+"}"); ctlchan <-= 0; addtoplevel(t,titlename,nil, sys->pctl(0,nil)); h1 := 300; w1 := 500; ht := int tkcmd(t, ".Wm_t cget -height"); if (h1 > display.image.r.dy() - ht) h1 = display.image.r.dy() - ht; if (w1 > display.image.r.dx()) w1 = display.image.r.dx(); tkcmd(t, ". configure -width "+string w1+" -height "+string h1); resizeview(t,w,h); tkcmd(t, "pack .f; update"); scrolling := 0; origin := Point (0,0); tkclient->onscreen(t, nil); tkclient->startinput(t, "kbd"::"ptr"::nil); loop: for(;;) alt{ s := <-t.ctxt.kbd => tk->keyboard(t, s); s := <-t.ctxt.ptr => tk->pointer(t, *s); inp := <- butchan => (n, lst) := sys->tokenize(inp, " \t\n"); case hd lst { "save" => ftype := "."+hd tl lst; savename := selectfile->filename(context, display.image, "Save "+filelist[i]+ftype+" to directory...", "*"+ftype :: nil, lastpath); if (savename != "" && savename[0] == '/') { lastpath = savename[:isat2(savename,"/")+1]; if (savename[len savename - 1] == '/') savename += filelist[i]+ftype; if (!hasext(savename, ftype)) savename += ftype; (fd, cancel) := create(savename, getcoords(t)); if (fd != nil) { n2 := -1; if (ftype == ".bit") n2 = display.writeimage(fd,img); if (ftype == ".jpg") n2 = 1 - dnld(i, "$"+savename); if (n2 == 0) { dialog(filelist[i]+ftype+" saved",0,i,getcoords(t)); break; } dialog("Could not save: "+filelist[i]+ftype,0,i,getcoords(t)); } if (!cancel) dialog("Could not save: "+filelist[i]+ftype,0,i,getcoords(t)); break; } "menu" => tkcmd(t, ".m post "+hd tl lst+" "+hd tl tl lst); "release" => scrolling = 0; "move" => newpoint := Point (int hd tl lst, int hd tl tl lst); if (scrolling) { diff := (origin.sub(newpoint)).mul(2); tkcmd(t, ".f.c xview scroll "+string diff.x+" units"); tkcmd(t, ".f.c yview scroll "+string diff.y+" units"); origin = newpoint; # clearbuffer(butchan); } else { origin = newpoint; scrolling = 1; } } s := <-t.ctxt.ctl or s = <-t.wreq or s = <-titlechan => if (s == "exit") break loop; e := tkclient->wmctl(t, s); if (e == nil && s[0] == '!') resizeview(t,w,h); } deltoplevel(t); } } create(filename: string, co: Rect): (ref sys->FD, int) { (n,dir) := sys->stat(filename); if (n != -1 && !dialog("overwrite "+filename+"?",1,-1,co)) return (nil,1); return (sys->create(filename,sys->OWRITE,8r666), 0); } hasext(name,ext: string): int { if (len name >= len ext && name[len name - len ext:] == ext) return 1; return 0; }