<> <> <> <> <> DIRECTORY Atom USING [GetPName, MakeAtom], Buttons USING [Button, ButtonProc, Create, SetDisplayStyle], ChoiceButtons USING [ChoiceDoesntExist, EnumTypeRef, BuildEnumTypeSelection, GetSelectedButton, UpdateChoiceButtons], Containers USING [ ChildXBound, ChildYBound, Container, Create ], Icons USING [ IconFlavor, NewIconFromFile ], IO USING [atom, STREAM, PutRope, PutFR, real, RIS, RopeFromROS, rope, ROS], Labels USING [Create, Label, Set], LoganBerry USING [AttributeType, AttributeValue, BuildIndices, Close, CompactLogs, DeleteEntry, Describe, Entry, Error, ErrorCode, IndexInfo, nullDB, Open, OpenDB, SchemaInfo, WriteEntry], LoganQuery USING [AbortQuery, AttributePattern, AttributePatternRec, AttributePatterns, BooleanFilterEntries, Cursor, EndGenerate, NextEntry, ParseBooleanQuery, ParseTree, PatternsToEntry, QueryEntries, QueryPlan, ReadAttributePatterns, WriteAttributePatterns, SyntaxError], LoganQueryClass USING [MergeEntries], Menus USING [AppendMenuEntry, ChangeNumberOfLines, ClickProc, CreateEntry, CreateMenu, GetNumberOfLines, Menu, MenuLine], MessageWindow USING [Append, Blink], PatternMatch USING [EachPairAction, Pairs], RefTab USING [Ref, EachPairAction, Fetch, Pairs, Create, Store], Rope USING [Concat, Equal, IsEmpty, Length, SkipTo, Substr, ROPE], Rules USING [ Create, Rule ], TiogaButtons USING [CreateButton, CreateViewer, TiogaButtonProc], TypeScript USING [ Create, ChangeLooks, PutChar, PutRope ], ViewerBLT USING [ChangeNumberOfLines], ViewerClasses USING [ Viewer, <> ViewerFlavor, ViewerRec ], ViewerEvents USING [ EventProc, RegisterEventProc ], ViewerOps USING [AddProp, CreateViewer, MoveViewer, PaintViewer], ViewerTools USING [GetContents, MakeNewTextViewer, SetContents, SetSelection], LoganBerryBrowser; LoganBerryBrowserImpl: CEDAR PROGRAM IMPORTS Atom, Buttons, ChoiceButtons, Containers, Icons, IO, Labels, LoganBerry, LoganQuery, LoganQueryClass, Menus, MessageWindow, PatternMatch, RefTab, Rope, Rules, TiogaButtons, TypeScript, ViewerBLT, ViewerEvents, ViewerOps, ViewerTools EXPORTS LoganBerryBrowser = BEGIN <<>> <> <<>> ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; Viewer: TYPE = ViewerClasses.Viewer; BrowserTool: TYPE = LoganBerryBrowser.Tool; DBList: TYPE = LIST OF LoganBerryBrowser.DBInfo; DisplayProc: TYPE = LoganBerryBrowser.DisplayProc; FormField: TYPE = REF FormFieldRec; FormFieldRec: TYPE = RECORD [ textViewer: Viewer, patternButton: ChoiceButtons.EnumTypeRef, feedbackArea: Labels.Label ]; HistoryData: TYPE = REF HistoryDataRec; HistoryDataRec: TYPE = RECORD [ tool: BrowserTool, cmd: ROPE, form: LoganQuery.AttributePatterns, other: ROPE ]; <<>> <> browserIcon: Icons.IconFlavor ¬ tool; itemHeight: CARDINAL = 14; interItemHeight: CARDINAL = 3; ruleHeight: CARDINAL = 1; patternButtonWidth: CARDINAL = 100; patternHSpace: CARDINAL = 5; feedbackHSpace: CARDINAL = 350; historyHeight: CARDINAL = 100; defaultPattern: ROPE ¬ "DWIM"; <> <<>> MakeEntryForm: PROC [tool: BrowserTool] RETURNS [] ~ { AddPattern: PatternMatch.EachPairAction ~ { patternChoices ¬ CONS[name, patternChoices]; }; patternChoices: LIST OF ROPE ¬ LIST["DWIM"]; prompt: Viewer; [] ¬ PatternMatch.Pairs[AddPattern]; tool.entryform ¬ RefTab.Create[]; FOR k: LIST OF LoganBerry.AttributeType ¬ tool.fields, k.rest WHILE k # NIL DO field: FormField ¬ NEW[FormFieldRec]; field.patternButton ¬ ChoiceButtons.BuildEnumTypeSelection[viewer: tool.details, x: patternHSpace, y: tool.height, buttonNames: patternChoices, default: defaultPattern, style: flipThru, maxWidth: patternButtonWidth]; prompt ¬ Buttons.Create[info: [name: Rope.Concat[Atom.GetPName[k.first], ":"], wx: field.patternButton.nextx, wy: tool.height, parent: tool.outer, scrollable: FALSE, border: FALSE], proc: SelectTextField, clientData: field]; field.textViewer ¬ ViewerTools.MakeNewTextViewer[info: [wx: prompt.wx + prompt.ww + patternHSpace, wy: tool.height, wh: itemHeight, ww: 9999, parent: tool.outer, scrollable: FALSE, border: FALSE]]; Containers.ChildXBound[container: tool.outer, child: field.textViewer]; field.feedbackArea ¬ Labels.Create[info: [parent: tool.details, wx: feedbackHSpace, wy: tool.height, ww: 9999, wh: 0, border: FALSE, scrollable: FALSE]]; tool.height ¬ tool.height + itemHeight + interItemHeight; [] ¬ RefTab.Store[tool.entryform, k.first, field]; ENDLOOP; IF tool.wantInputArea THEN { prompt ¬ Buttons.Create[info: [name: "other attributes:", wx: patternHSpace, wy: tool.height, parent: tool.outer, scrollable: TRUE, border: FALSE], proc: SelectInputArea, clientData: tool]; tool.inputArea ¬ ViewerTools.MakeNewTextViewer[info: [wx: prompt.wx + prompt.ww + patternHSpace, wy: tool.height, wh: itemHeight, ww: 9999, parent: tool.outer, border: FALSE]]; Containers.ChildXBound[container: tool.outer, child: tool.inputArea]; tool.height ¬ tool.height + itemHeight + interItemHeight; }; }; SelectTextField: Buttons.ButtonProc = { <<[parent: ViewerClasses.Viewer, clientData: REF ANY _ NIL, mouseButton: ViewerClasses.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> field: FormField = NARROW[clientData]; ViewerTools.SetSelection[field.textViewer, NIL]; }; SelectInputArea: Buttons.ButtonProc = { <<[parent: ViewerClasses.Viewer, clientData: REF ANY _ NIL, mouseButton: ViewerClasses.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> tool: BrowserTool = NARROW[clientData]; ViewerTools.SetSelection[tool.inputArea, NIL]; }; MakeDatabaseButton: PROC [tool: BrowserTool] RETURNS [] ~ { button: Buttons.Button; xpos: INT ¬ 0; IF tool.databases.rest = NIL THEN RETURN; -- only a single database to choose from so don't need selection buttons button ¬ Labels.Create[info: [name: "Databases: ", wx: xpos, wy: tool.height, parent: tool.details, scrollable: FALSE, border: FALSE]]; FOR d: DBList ¬ tool.databases, d.rest WHILE d#NIL DO xpos ¬ xpos + button.ww; button ¬ Buttons.Create[ info: [name: d.first.shortname, wx: xpos, wy: tool.height, parent: tool.details, border: TRUE], proc: ToggleDatabaseButton, fork: FALSE, paint: FALSE, clientData: d.first ]; Buttons.SetDisplayStyle[button, IF d.first.selected THEN $WhiteOnBlack ELSE $BlackOnWhite]; ENDLOOP; tool.height ¬ tool.height + itemHeight + interItemHeight; }; ToggleDatabaseButton: Buttons.ButtonProc ~ { <<[parent: ViewerClasses.Viewer, clientData: REF ANY _ NIL, mouseButton: ViewerClasses.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> db: LoganBerryBrowser.DBInfo = NARROW [clientData]; db.selected ¬ NOT db.selected; Buttons.SetDisplayStyle[parent, IF db.selected THEN $WhiteOnBlack ELSE $BlackOnWhite]; }; MakeSortButton: PROC [tool: BrowserTool] RETURNS [] ~ { choices: LIST OF ROPE ¬ NIL; IF NOT tool.wantSortButton THEN RETURN; FOR i: LIST OF LoganBerry.IndexInfo ¬ tool.databases.first.schema.indices, i.rest WHILE i#NIL DO choices ¬ CONS[Atom.GetPName[i.first.key], choices]; ENDLOOP; choices ¬ CONS["ANY", choices]; tool.height ¬ tool.height + interItemHeight; tool.sortButton ¬ ChoiceButtons.BuildEnumTypeSelection[viewer: tool.details, x: patternHSpace, y: tool.height, title: "Order by:", buttonNames: choices, default: "ANY", style: menuSelection --vs. flipThru--]; tool.queryFeedback ¬ Labels.Create[info: [parent: tool.details, wx: MAX[tool.sortButton.nextx, feedbackHSpace], wy: tool.height, ww: 9999, wh: 0, border: FALSE, scrollable: FALSE]]; tool.height ¬ tool.height + itemHeight + interItemHeight; }; ReadEntryForm: PUBLIC PROC [tool: BrowserTool] RETURNS [form: LoganQuery.AttributePatterns, other: Rope.ROPE, orderBy: LoganBerry.AttributeType] ~ { NextField: RefTab.EachPairAction = { <<[key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOLEAN]>> text: ROPE; label: ATOM = NARROW[key]; field: FormField = NARROW[val]; text ¬ ViewerTools.GetContents[field.textViewer]; IF NOT Rope.Equal[text, ""] THEN { fv: LoganQuery.AttributePattern ¬ NEW[LoganQuery.AttributePatternRec]; fv.attr.type ¬ label; fv.attr.value ¬ text; fv.ptype ¬ ChoiceButtons.GetSelectedButton[field.patternButton]; IF form=NIL THEN form ¬ end ¬ LIST[fv] ELSE {end.rest ¬ LIST[fv]; end ¬ end.rest;}; }; Labels.Set[field.feedbackArea, NIL]; RETURN[quit: FALSE]; }; end, otherPatterns: LoganQuery.AttributePatterns; form ¬ NIL; [] ¬ RefTab.Pairs[tool.entryform, NextField]; other ¬ ViewerTools.GetContents[tool.inputArea]; AddToHistory[tool, form, other]; <> <> <
> orderBy ¬ Atom.MakeAtom[ChoiceButtons.GetSelectedButton[tool.sortButton]]; IF orderBy = $ANY THEN orderBy ¬ NIL; Labels.Set[tool.queryFeedback, NIL]; }; RestoreEntryForm: PUBLIC PROC [tool: BrowserTool, form: LoganQuery.AttributePatterns, other: ROPE] RETURNS [] ~ { RestoreField: RefTab.EachPairAction = { <<[key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOLEAN]>> label: ATOM = NARROW[key]; field: FormField = NARROW[val]; f: LoganQuery.AttributePatterns; FOR f ¬ form, f.rest WHILE f # NIL DO IF f.first.attr.type = label THEN EXIT; ENDLOOP; IF f # NIL THEN { ChoiceButtons.UpdateChoiceButtons[tool.details, field.patternButton, f.first.ptype ! ChoiceButtons.ChoiceDoesntExist => CONTINUE]; ViewerTools.SetContents[field.textViewer, f.first.attr.value, FALSE]; } ELSE { ViewerTools.SetContents[field.textViewer, "", FALSE]; }; Labels.Set[field.feedbackArea, NIL, FALSE]; RETURN[quit: FALSE]; }; ChoiceButtons.UpdateChoiceButtons[tool.details, tool.sortButton, Atom.GetPName[form.first.attr.type] ! ChoiceButtons.ChoiceDoesntExist => CONTINUE]; Labels.Set[tool.queryFeedback, NIL, FALSE]; [] ¬ RefTab.Pairs[tool.entryform, RestoreField]; ViewerTools.SetContents[tool.inputArea, other, FALSE]; ViewerOps.PaintViewer[viewer: tool.outer, hint: all]; }; ReportFeedback: PUBLIC PROC [plan: LoganQuery.QueryPlan, tool: BrowserTool] RETURNS [] ~ { Labels.Set[tool.queryFeedback, IO.PutFR["(%g - %3.2f)", IO.atom[plan.first.attr.type], IO.real[plan.first.cost]]]; FOR p: LoganQuery.QueryPlan ¬ plan, p.rest WHILE p # NIL DO ff: FormField ¬ NARROW[RefTab.Fetch[tool.entryform, p.first.attr.type].val]; IF ff#NIL THEN Labels.Set[ff.feedbackArea, IO.PutFR["%g(%g)", IO.rope[p.first.errMsg], IO.rope[p.first.infoMsg]]]; ENDLOOP; }; AppendPatterns: PROC [p1, p2: LoganQuery.AttributePatterns] RETURNS [LoganQuery.AttributePatterns] ~ { temp: LoganQuery.AttributePatterns ¬ p1; IF p1 = NIL THEN RETURN[p2]; UNTIL temp.rest = NIL DO temp ¬ temp.rest ENDLOOP; temp.rest ¬ p2; RETURN[p1]; }; <<>> <> <<>> <> <> <<>> AddToHistory: PUBLIC PROC [tool: BrowserTool, form: LoganQuery.AttributePatterns, other: ROPE] RETURNS [] ~ { data: HistoryData = NEW[HistoryDataRec ¬ [tool, NIL, form, other]]; line: STREAM ¬ IO.ROS[]; LoganQuery.WriteAttributePatterns[line, form]; IO.PutRope[line, " "]; IO.PutRope[line, other]; [] ¬ TiogaButtons.CreateButton[viewer: tool.history, rope: IO.RopeFromROS[line], proc: HistoryButtonProc, clientData: data]; }; HistoryButtonProc: TiogaButtons.TiogaButtonProc = { <<[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> < restore the entry form; Middle => restore form and re-execute (not currently implemented).>> data: HistoryData ¬ NARROW[clientData]; RestoreEntryForm[data.tool, data.form, data.other]; }; <<>> <> <<>> DividingLine: PROC [tool: BrowserTool] RETURNS [] ~ { rule: Rules.Rule; rule ¬ Rules.Create[info: [parent: tool.outer, wx: 0, wy: tool.height, ww: 9999, wh: ruleHeight]]; Containers.ChildXBound[tool.outer, rule]; -- constrain rule to be width of parent tool.height ¬ tool.height + ruleHeight; }; HideViewer: PROC [v: Viewer] RETURNS [] ~ { ViewerOps.MoveViewer[viewer: v, x: 2000, y: v.wy, w: v.ww, h: v.wh]; }; UnHideViewer: PROC [v: Viewer] RETURNS [] ~ { ViewerOps.MoveViewer[viewer: v, x: 0, y: v.wy, w: v.ww, h: v.wh]; }; ResetViewer: PROC [v: Viewer] RETURNS [] ~ { v.class.init[v]; }; <<>> <> CreateTool: PUBLIC PROC [db: LoganBerry.OpenDB ¬ LoganBerry.nullDB, tool: BrowserTool] = { innerInfo: ViewerClasses.ViewerRec; <> IF db#LoganBerry.nullDB THEN tool.databases ¬ LIST[NEW[LoganBerryBrowser.DBInfoRec ¬ [db: db]]]; IF tool.databases = NIL THEN { MessageWindow.Append[message: "No LoganBerry database specified to browse!", clearFirst: TRUE]; RETURN; }; FOR d: DBList ¬ tool.databases, d.rest WHILE d#NIL DO prev, loc: INT ¬ 1; d.first.schema ¬ LoganBerry.Describe[db: d.first.db]; WHILE loc < Rope.Length[d.first.schema.dbName] DO prev ¬ loc; loc ¬ Rope.SkipTo[s: d.first.schema.dbName, pos: prev+1, skip: "/>]"]; ENDLOOP; d.first.shortname ¬ Rope.Substr[base: d.first.schema.dbName, start: prev+1]; ENDLOOP; <> <> <> <> <> <> <> <> <> <> SetDefaults[tool]; <> tool.outer ¬ Containers.Create[info: [ name: tool.name, icon: tool.icon, label: tool.databases.first.shortname, iconic: FALSE, column: left, menu: tool.menu, scrollable: FALSE]]; Menus.ChangeNumberOfLines[tool.menu, 1]; ViewerBLT.ChangeNumberOfLines[tool.outer, 1]; ViewerOps.AddProp[viewer: tool.outer, prop: $BrowserTool, val: tool]; [] ¬ ViewerEvents.RegisterEventProc[proc: DestroyProc, event: destroy, filter: tool.outer]; <> tool.details ¬ Containers.Create[info: [ parent: tool.outer, ww: 9999, wh: 9999, scrollable: FALSE, border: FALSE]]; Containers.ChildXBound[container: tool.outer, child: tool.details]; Containers.ChildYBound[container: tool.outer, child: tool.details]; tool.height ¬ interItemHeight; MakeDatabaseButton[tool]; MakeEntryForm[tool]; MakeSortButton[tool]; DividingLine[tool]; <> tool.history ¬ TiogaButtons.CreateViewer[info: [ parent: tool.outer, wy: tool.height, ww: 9999, wh: historyHeight, border: TRUE]]; Containers.ChildXBound[tool.outer, tool.history]; -- constrain rule to be width of parent HideViewer[tool.history]; <> innerInfo ¬ [ name: "LoganBerry Browser output", parent: tool.outer, wx: 0, wy: tool.height, ww: 9999, wh: 9999, border: FALSE]; tool.inner ¬ SELECT tool.innerFlavor FROM $Typescript => TypeScript.Create[info: innerInfo], ENDCASE => ViewerOps.CreateViewer[flavor: tool.innerFlavor, info: innerInfo]; Containers.ChildXBound[tool.outer, tool.inner]; -- constrain rule to be width of parent Containers.ChildYBound[tool.outer, tool.inner]; -- constrain rule to be height of parent }; PrintEntry: PUBLIC PROC [entry: LoganBerry.Entry, tool: BrowserTool] = { <> IF tool.innerFlavor#$TypeScript THEN { MessageWindow.Append[message: "Wrong flavor of browser's output viewer", clearFirst: TRUE]; MessageWindow.Blink[]; RETURN; }; FOR e: LoganBerry.Entry ¬ entry, e.rest UNTIL e = NIL DO IF e.first.type#NIL THEN { TypeScript.ChangeLooks[tool.inner, 'b]; -- print atribute type in bold TypeScript.PutRope[tool.inner, Atom.GetPName[e.first.type]]; TypeScript.ChangeLooks[tool.inner, ' ]; -- restore normal looks TypeScript.PutRope[tool.inner, ": "]; }; TypeScript.PutRope[tool.inner, e.first.value]; TypeScript.PutChar[tool.inner, '\n]; ENDLOOP; TypeScript.PutChar[tool.inner, '\n]; }; PrintEntryAsButton: PUBLIC PROC [entry: LoganBerry.Entry, tool: BrowserTool, proc: TiogaButtons.TiogaButtonProc, clientData: REF ANY ¬ NIL] = { <> EntryToRope: PROC [entry: LoganBerry.Entry] RETURNS [rope: ROPE] ~ { rope ¬ NIL; FOR l: LoganBerry.Entry ¬ entry, l.rest WHILE l # NIL DO IF l.first.type#NIL THEN rope ¬ IO.PutFR["%g%g: ", IO.rope[rope], IO.atom[l.first.type]]; rope ¬ IO.PutFR["%g%g\n", IO.rope[rope], IO.rope[l.first.value]]; ENDLOOP; }; IF tool.innerFlavor#$TiogaButtons THEN { MessageWindow.Append[message: "Wrong flavor of browser's output viewer", clearFirst: TRUE]; MessageWindow.Blink[]; RETURN; }; [] ¬ TiogaButtons.CreateButton[viewer: tool.inner, rope: EntryToRope[entry], proc: proc, clientData: clientData]; }; <> <<>> StopProc: PUBLIC Menus.ClickProc = TRUSTED { <<[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> tool: BrowserTool ¬ NARROW[clientData]; put: DisplayProc ¬ GetDisplayProc[tool]; LoganQuery.AbortQuery[tool.cursor]; put[LIST[[NIL, "-- Aborted"]], tool]; }; warningThreshold: REAL = 0.75; -- give warning if 3/4 of the database must be searched BrowseProc: PUBLIC Menus.ClickProc = { <<[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> ENABLE { LoganBerry.Error => {ReportLBError[ec, explanation, NARROW[clientData]]; CONTINUE}; LoganQuery.SyntaxError => {ReportLBError[$BadQuery, explanation, NARROW[clientData]]; CONTINUE}; }; tool: BrowserTool ¬ NARROW[clientData]; put: DisplayProc ¬ GetDisplayProc[tool]; form: LoganQuery.AttributePatterns; base: LoganBerry.AttributeType; other: ROPE; queryTree: LoganQuery.ParseTree ¬ NIL; newcursor: LoganQuery.Cursor; plan: LoganQuery.QueryPlan; entry: LoganBerry.Entry; IF mouseButton # blue THEN ResetViewer[tool.inner]; [form, other, base] ¬ ReadEntryForm[tool]; IF NOT Rope.IsEmpty[other] THEN { queryTree ¬ LoganQuery.ParseBooleanQuery[IO.RIS[other]]; }; tool.cursor.class ¬ NIL; FOR d: DBList ¬ tool.databases, d.rest WHILE d#NIL DO IF d.first.selected THEN { [newcursor, plan] ¬ LoganQuery.QueryEntries[db: d.first.db, patterns: form, baseIndex: base]; IF queryTree # NIL THEN newcursor ¬ LoganQuery.BooleanFilterEntries[input: newcursor, query: queryTree, inputOrder: plan.first.attr.type, primaryKey: d.first.schema.indices.first.key]; IF tool.cursor.class=NIL THEN tool.cursor ¬ newcursor ELSE tool.cursor ¬ LoganQueryClass.MergeEntries[input1: newcursor, input2: tool.cursor, atype: plan.first.attr.type]; }; ENDLOOP; IF tool.cursor.class=NIL THEN { ReportLBError[$NoOp, "No database selected", tool]; RETURN; }; ReportFeedback[plan, tool]; IF plan.first.cost > warningThreshold THEN MessageWindow.Append[message: "Browsing complete database...", clearFirst: TRUE]; entry ¬ LoganQuery.NextEntry[tool.cursor]; WHILE entry # NIL DO put[entry, tool]; entry ¬ LoganQuery.NextEntry[tool.cursor]; ENDLOOP; put[LIST[[NIL, "-- Done."]], tool]; LoganQuery.EndGenerate[tool.cursor]; }; <<>> UpdateProc: PUBLIC Menus.ClickProc = { <<[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> DoUpdateOrReplace[tool: NARROW[clientData], replace: FALSE, reset: mouseButton # blue]; }; ReplaceProc: PUBLIC Menus.ClickProc = { <<[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> DoUpdateOrReplace[tool: NARROW[clientData], replace: TRUE, reset: mouseButton # blue]; }; DoUpdateOrReplace: PROC [tool: BrowserTool, replace: BOOLEAN ¬ FALSE, reset: BOOLEAN ¬ FALSE] ~ { ENABLE { LoganBerry.Error => {ReportLBError[ec, explanation, tool]; CONTINUE}; LoganQuery.SyntaxError => {ReportLBError[$MalformedAttribute, explanation, tool]; CONTINUE}; }; put: DisplayProc ¬ GetDisplayProc[tool]; form: LoganQuery.AttributePatterns; other: ROPE; entry: LoganBerry.Entry; dbinfo: LoganBerryBrowser.DBInfo ¬ NIL; IF reset THEN ResetViewer[tool.inner]; [form, other,] ¬ ReadEntryForm[tool]; IF NOT Rope.IsEmpty[other] THEN { otherPatterns: LoganQuery.AttributePatterns ¬ LoganQuery.ReadAttributePatterns[IO.RIS[other]]; form ¬ AppendPatterns[form, otherPatterns]; }; entry ¬ LoganQuery.PatternsToEntry[form]; FOR d: DBList ¬ tool.databases, d.rest WHILE d#NIL DO IF d.first.selected THEN IF dbinfo=NIL THEN dbinfo ¬ d.first ELSE {ReportLBError[$Update, "Please select a single database", tool]; RETURN;}; ENDLOOP; IF dbinfo=NIL THEN { ReportLBError[$NoOp, "No database selected", tool]; RETURN; }; LoganBerry.WriteEntry[db: dbinfo.db, entry: entry, replace: replace]; put[entry, tool]; put[LIST[[NIL, "-- Done."]], tool]; }; DeleteProc: PUBLIC Menus.ClickProc = { <<[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> ENABLE { LoganBerry.Error => {ReportLBError[ec, explanation, NARROW[clientData]]; CONTINUE}; LoganQuery.SyntaxError => {ReportLBError[$BadQuery, explanation, NARROW[clientData]]; CONTINUE}; }; tool: BrowserTool ¬ NARROW[clientData]; put: DisplayProc ¬ GetDisplayProc[tool]; form: LoganQuery.AttributePatterns; base: LoganBerry.AttributeType; other: ROPE; queryTree: LoganQuery.ParseTree ¬ NIL; entry: LoganBerry.Entry; primaryKey: LoganBerry.AttributeType = tool.databases.first.schema.indices.first.key; plan: LoganQuery.QueryPlan; dbinfo: LoganBerryBrowser.DBInfo ¬ NIL; IF mouseButton # blue THEN ResetViewer[tool.inner]; [form, other, base] ¬ ReadEntryForm[tool]; FOR d: DBList ¬ tool.databases, d.rest WHILE d#NIL DO IF d.first.selected THEN IF dbinfo=NIL THEN dbinfo ¬ d.first ELSE {ReportLBError[$Delete, "Please select a single database", tool]; RETURN;}; ENDLOOP; IF dbinfo=NIL THEN { ReportLBError[$NoOp, "No database selected", tool]; RETURN; }; [tool.cursor, plan] ¬ LoganQuery.QueryEntries[db: dbinfo.db, patterns: form, baseIndex: base]; IF NOT Rope.IsEmpty[other] THEN { queryTree ¬ LoganQuery.ParseBooleanQuery[IO.RIS[other]]; tool.cursor ¬ LoganQuery.BooleanFilterEntries[input: tool.cursor, query: queryTree, inputOrder: plan.first.attr.type, primaryKey: dbinfo.schema.indices.first.key]; }; ReportFeedback[plan, tool]; put[LIST[[NIL, "-- Deleting..."]], tool]; tool.lastDeletes ¬ NIL; entry ¬ LoganQuery.NextEntry[tool.cursor]; WHILE entry # NIL DO LoganBerry.DeleteEntry[db: dbinfo.db, key: primaryKey, value: GetAttributeValue[entry, primaryKey]]; tool.lastDeletes ¬ CONS[entry, tool.lastDeletes]; put[entry, tool]; entry ¬ LoganQuery.NextEntry[tool.cursor]; ENDLOOP; put[LIST[[NIL, "-- Done."]], tool]; LoganQuery.EndGenerate[tool.cursor]; }; UnDeleteProc: PUBLIC Menus.ClickProc = { <<[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> ENABLE LoganBerry.Error => {ReportLBError[ec, explanation, NARROW[clientData]]; CONTINUE}; tool: BrowserTool ¬ NARROW[clientData]; put: DisplayProc ¬ GetDisplayProc[tool]; dbinfo: LoganBerryBrowser.DBInfo ¬ NIL; IF mouseButton # blue THEN ResetViewer[tool.inner]; FOR d: DBList ¬ tool.databases, d.rest WHILE d#NIL DO IF d.first.selected THEN IF dbinfo=NIL THEN dbinfo ¬ d.first ELSE {ReportLBError[$UnDelete, "Please select a single database", tool]; RETURN;}; ENDLOOP; IF dbinfo=NIL THEN { ReportLBError[$NoOp, "No database selected", tool]; RETURN; }; put[LIST[[NIL, "-- UnDeleting..."]], tool]; FOR l: LIST OF LoganBerry.Entry ¬ tool.lastDeletes, l.rest WHILE l#NIL DO LoganBerry.WriteEntry[db: dbinfo.db, entry: l.first ! LoganBerry.Error => {ReportLBError[ec, explanation, tool]; CONTINUE};]; put[l.first, tool]; ENDLOOP; tool.lastDeletes ¬ NIL; -- can't undo an undelete put[LIST[[NIL, "-- Done."]], tool]; }; DetailsProc: PUBLIC Menus.ClickProc = { <<[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> tool: BrowserTool ¬ NARROW[clientData]; IF tool.details.wx = 0 THEN HideViewer[tool.details] ELSE UnHideViewer[tool.details]; }; <<>> HistoryProc: PUBLIC Menus.ClickProc = { <<[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> tool: BrowserTool ¬ NARROW[clientData]; IF tool.history.wx = 0 THEN { HideViewer[tool.history]; ViewerOps.MoveViewer[viewer: tool.inner, x: tool.inner.wx, y: tool.inner.wy - historyHeight, w: tool.inner.ww, h: tool.inner.wh]; tool.height ¬ tool.height - historyHeight; } ELSE { ViewerOps.MoveViewer[viewer: tool.inner, x: tool.inner.wx, y: tool.inner.wy + historyHeight, w: tool.inner.ww, h: tool.inner.wh]; UnHideViewer[tool.history]; tool.height ¬ tool.height + historyHeight; }; }; DestroyProc: ViewerEvents.EventProc = { <<[viewer: ViewerClasses.Viewer, event: ViewerEvents.ViewerEvent, before: BOOL] RETURNS [abort: BOOL _ FALSE]>> <<-- Does nothing at the moment>> <> < {ReportLBError[ec, explanation, tool]; CONTINUE}];>> }; <<>> <> SetDefaults: PROC [tool: BrowserTool] RETURNS [] ~ { IF tool.name = NIL THEN tool.name ¬ Rope.Concat["LoganBerry Browser: ", tool.databases.first.schema.dbName]; IF tool.menu = NIL THEN { tool.menu ¬ StandardMainMenu[tool]; }; IF tool.innerFlavor = NIL THEN tool.innerFlavor ¬ $TypeScript; IF tool.icon = unInit THEN tool.icon ¬ browserIcon; IF tool.fields = NIL THEN tool.fields ¬ IndexKeys[tool]; }; IndexKeys: PROC [tool: BrowserTool] RETURNS [fields: LIST OF LoganBerry.AttributeType] ~ { tail: LIST OF LoganBerry.AttributeType ¬ NIL; FOR i: LIST OF LoganBerry.IndexInfo ¬ tool.databases.first.schema.indices, i.rest WHILE i#NIL DO IF tail#NIL THEN {tail.rest ¬ LIST[i.first.key]; tail ¬ tail.rest;} ELSE {fields ¬ tail ¬ LIST[i.first.key];} ENDLOOP; }; StandardMainMenu: PROC [tool: BrowserTool] RETURNS [menu: Menus.Menu] ~ { AppendMenu: PROC[menu: Menus.Menu, name: ROPE, proc: Menus.ClickProc, line: Menus.MenuLine ¬ 0, guarded: BOOL ¬ FALSE] = { Menus.AppendMenuEntry[ menu: menu, entry: Menus.CreateEntry[name: name, proc: proc, clientData: tool, guarded: guarded], line: line ]; }; menu ¬ Menus.CreateMenu[lines: 2]; <<--first line-->> AppendMenu[menu, "STOP!", StopProc]; AppendMenu[menu, "Browse", BrowseProc]; AppendMenu[menu, "Update", UpdateProc]; AppendMenu[menu, "Replace", ReplaceProc]; AppendMenu[menu, "Delete", DeleteProc, 0, TRUE]; AppendMenu[menu, "UnDelete", UnDeleteProc, 0, TRUE]; AppendMenu[menu, "History", HistoryProc]; AppendMenu[menu, "Details", DetailsProc]; AppendMenu[menu, "AdminOps", AdminOpsProc]; <<--second line-->> AppendMenu[menu, "Open", OpenProc, 1]; AppendMenu[menu, "Close", CloseProc, 1, TRUE]; AppendMenu[menu, "BuildIndices", BuildIndicesProc, 1, TRUE]; AppendMenu[menu, "CompactLogs", CompactLogsProc, 1, TRUE]; }; AdminOpsProc: PUBLIC Menus.ClickProc = { <<[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> tool: BrowserTool ¬ NARROW[clientData]; newCount: CARDINAL ¬ IF Menus.GetNumberOfLines[tool.menu] = 1 THEN 2 ELSE 1; Menus.ChangeNumberOfLines[tool.menu, newCount]; ViewerBLT.ChangeNumberOfLines[tool.outer, newCount]; }; <<>> OpenProc: PUBLIC Menus.ClickProc = { <<[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> tool: BrowserTool ¬ NARROW[clientData]; FOR d: DBList ¬ tool.databases, d.rest WHILE d#NIL DO IF d.first.selected THEN d.first.db ¬ LoganBerry.Open[dbName: d.first.schema.dbName ! LoganBerry.Error => {ReportLBError[ec, explanation, tool]; CONTINUE}]; ENDLOOP; }; <<>> CloseProc: PUBLIC Menus.ClickProc = { <<[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> tool: BrowserTool ¬ NARROW[clientData]; FOR d: DBList ¬ tool.databases, d.rest WHILE d#NIL DO IF d.first.selected THEN LoganBerry.Close[db: d.first.db ! LoganBerry.Error => {ReportLBError[ec, explanation, tool]; CONTINUE}]; ENDLOOP; }; <<>> BuildIndicesProc: PUBLIC Menus.ClickProc = { <<[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> tool: BrowserTool ¬ NARROW[clientData]; FOR d: DBList ¬ tool.databases, d.rest WHILE d#NIL DO IF d.first.selected THEN LoganBerry.BuildIndices[db: d.first.db ! LoganBerry.Error => {ReportLBError[ec, explanation, tool]; CONTINUE}]; ENDLOOP; }; <<>> CompactLogsProc: PUBLIC Menus.ClickProc = { <<[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> tool: BrowserTool ¬ NARROW[clientData]; FOR d: DBList ¬ tool.databases, d.rest WHILE d#NIL DO IF d.first.selected THEN LoganBerry.CompactLogs[db: d.first.db ! LoganBerry.Error => {ReportLBError[ec, explanation, tool]; CONTINUE}]; ENDLOOP; }; GetAttributeValue: PROC [entry: LoganBerry.Entry, type: LoganBerry.AttributeType] RETURNS [LoganBerry.AttributeValue] ~ { FOR e: LoganBerry.Entry ¬ entry, e.rest WHILE e # NIL DO IF e.first.type = type THEN RETURN[e.first.value]; ENDLOOP; RETURN[NIL]; }; ReportLBError: PROC [ec: LoganBerry.ErrorCode, explanation: ROPE, tool: BrowserTool] RETURNS [] ~ { put: DisplayProc ¬ GetDisplayProc[tool]; put[LIST[[$ERROR, IO.PutFR["%g - %g", IO.atom[ec], IO.rope[explanation]]]], tool]; }; <<>> GetDisplayProc: PROC [tool: BrowserTool] RETURNS [proc: DisplayProc] ~ { <> RETURN[IF tool.display=NIL THEN PrintEntry ELSE tool.display]; }; <<>> <> browserIcon ¬ Icons.NewIconFromFile[file: "LoganBerry.icons", n: 0 ! ANY => CONTINUE]; END. <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<>> <<>> <<>> <<>> <<>> <<>>