1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186 |
- MODULE WMArchives; (** AUTHOR "FN,PL"; PURPOSE "GUI for Archives"; *)
- IMPORT
- Commands, Streams, Modules, Files, FileHandlers, Archives, Strings, KernelLog, Texts, TextUtilities, Raster,
- WMDropTarget, WMComponents, WMStandardComponents, WMTrees, WMPopups,
- WMGraphics, WMDialogs, WMRectangles,
- WMEditors, WMRestorable, WMMessages, WMGrids, WMStringGrids, WMProperties, XML,
- WM := WMWindowManager;
- CONST
- WindowWidth = 600; WindowHeight = 400;
- NameSize = 128;
- BufSize = 16*1024;
- TreePreviewSize = 16;
- TYPE
- KillerMsg = OBJECT
- END KillerMsg;
- EntryInfo = Archives.EntryInfo;
- ArchiveDropInterface* = OBJECT(WMDropTarget.DropFiles)
- VAR
- out : Streams.Writer;
- at : ArchiveTree;
- parent : WMTrees.TreeNode;
- entryName, caption : Strings.String;
- PROCEDURE &New*(t : ArchiveTree; n : WMTrees.TreeNode);
- BEGIN
- at := t; parent := n
- END New;
- PROCEDURE OpenPut*(CONST remoteName : ARRAY OF CHAR; VAR outw : Streams.Writer; VAR res : WORD);
- BEGIN
- res := -1;
- caption := Strings.NewString(remoteName);
- entryName := at.GetPath(parent);
- Strings.Append(entryName^, remoteName);
- (* check if exists *)
- at.archive.Acquire;
- IF (at.archive.GetEntryInfo(entryName^) = NIL) OR
- (WMDialogs.Confirmation("Confirm overwriting", remoteName) = WMDialogs.ResYes) THEN
- Streams.OpenWriter(out, at.archive.OpenSender(entryName^));
- res := 0
- END;
- at.archive.Release;
- outw := out;
- END OpenPut;
- PROCEDURE ClosePut*(VAR res : WORD);
- VAR ei : EntryInfo;
- BEGIN
- IF out # NIL THEN out.Update END;
- at.archive.Acquire; ei := at.archive.GetEntryInfo(entryName^); at.archive.Release;
- at.AddChildNode(parent, caption, ei, TRUE);
- END ClosePut;
- END ArchiveDropInterface;
- ArchiveDropTarget* = OBJECT(WMDropTarget.DropTarget)
- VAR
- tree : ArchiveTree;
- node : WMTrees.TreeNode;
- PROCEDURE &New*(t : ArchiveTree; n : WMTrees.TreeNode);
- BEGIN
- tree := t; node := n
- END New;
- PROCEDURE GetInterface*(type : LONGINT) : WMDropTarget.DropInterface;
- VAR di : ArchiveDropInterface;
- BEGIN
- IF type = WMDropTarget.TypeFiles THEN
- NEW(di, tree, node);
- RETURN di
- ELSE RETURN NIL
- END
- END GetInterface;
- END ArchiveDropTarget;
- ArchiveTree* = OBJECT(WMStandardComponents.Panel)
- VAR
- tree -: WMTrees.Tree;
- treeView -: WMTrees.TreeView;
- archive : Archives.Archive;
- archiveName : ARRAY NameSize OF CHAR;
- popup : WMPopups.Popup;
- label : WMStandardComponents.Label;
- toolbar : WMStandardComponents.Panel;
- refreshBtn : WMStandardComponents.Button;
- px, py : LONGINT;
- draggedString : Strings.String;
- showFiles : WMProperties.BooleanProperty;
- showImagePreview : WMProperties.BooleanProperty;
- NodeChanged* : PROCEDURE {DELEGATE} (sender, data : ANY);
- PROCEDURE & Init*;
- BEGIN
- Init^;
- (* title *)
- NEW(label); label.alignment.Set(WMComponents.AlignTop);
- label.fillColor.Set(0CCCCCCFFH);
- label.SetCaption("Resource Index"); label.bounds.SetHeight(20);
- SELF.AddContent(label);
- (* toolbar *)
- NEW(toolbar); toolbar.alignment.Set(WMComponents.AlignTop);
- toolbar.bounds.SetHeight(20);
- SELF.AddContent(toolbar);
- (* refresh button *)
- NEW(refreshBtn); refreshBtn.alignment.Set(WMComponents.AlignLeft);
- refreshBtn.caption.SetAOC("Refresh");
- refreshBtn.onClick.Add(RefreshHandler);
- toolbar.AddContent(refreshBtn);
- (* treeView *)
- NEW(treeView); treeView.alignment.Set(WMComponents.AlignClient);
- SELF.AddContent(treeView);
- treeView.SetExtContextMenuHandler(ContextMenu);
- treeView.SetDrawNodeProc(DrawTreeNode);
- treeView.SetMeasureNodeProc(MeasureTreeNode);
- treeView.SetExtDragDroppedHandler(MyDragDropped);
- treeView.onStartDrag.Add(MyStartDrag);
- tree := treeView.GetTree();
- NEW(showFiles, ProtShowFiles, NIL, NIL); properties.Add(showFiles);
- NEW(showImagePreview, ProtShowImgPrev, NIL, NIL); properties.Add(showImagePreview);
- SetNameAsString(StrArchiveTree)
- END Init;
- (** set a new model. the view is re-initialized *)
- PROCEDURE SetArchive*(archive : Archives.Archive);
- VAR i : LONGINT;
- node : WMTrees.TreeNode;
- name : Strings.String;
- archiveEntries : Archives.Index;
- BEGIN
- ASSERT(archive # NIL);
- SELF.archive := archive;
- NEW(node);
- tree.Acquire;
- tree.SetRoot(node);
- tree.InclNodeState(node, WMTrees.NodeAlwaysExpanded);
- RemovePartitionLabel(archive.name, archiveName);
- tree.SetNodeCaption(node, Strings.NewString(archiveName));
- tree.Release;
- treeView.SetFirstLine(0, TRUE);
- archive.Acquire; archiveEntries := archive.GetIndex(); archive.Release;
- FOR i := 0 TO LEN(archiveEntries^)-1 DO
- name := archiveEntries[i].GetName();
- InsertTreeNode(node, name, archiveEntries[i])
- END;
- END SetArchive;
- (* ----- handlers ------------------------------------------------- *)
- (* called when a drag-operation has been started *)
- PROCEDURE MyStartDrag(sender, data : ANY);
- VAR img : WMGraphics.Image;
- c : WMGraphics.BufferCanvas;
- w : LONGINT;
- a : ANY;
- s : Strings.String;
- BEGIN
- tree.Acquire; a := tree.GetNodeData(treeView.draggedNode); tree.Release;
- IF a # NIL THEN
- (* render to bitmap *)
- s := a(EntryInfo).GetName();
- NEW(draggedString, LEN(s^)+64);
- AppendToArchiveName(s^, draggedString^);
- w := Strings.Length(draggedString^) * 7;
- IF w > 400 THEN w := 400 END;
- NEW(img); Raster.Create(img, w, 25, Raster.BGRA8888);
- NEW(c, img); c.SetColor(LONGINT(0FFFF00FFH));
- c.Fill(WMRectangles.MakeRect(0, 0, w, 25), 0FF80H, WMGraphics.ModeCopy);
- c.DrawString(3, 20, draggedString^);
- IF treeView.StartDrag(a, img, 0,0, DragArrived, NIL) THEN KernelLog.String("DraggingStarted"); KernelLog.Ln
- ELSE KernelLog.String("Drag could not be started"); KernelLog.Ln
- END
- END;
- END MyStartDrag;
- (* called when an object dragged from this treeView has been dropped anywhere *)
- PROCEDURE DragArrived(sender, data : ANY);
- VAR di : WM.DragInfo;
- dt : WMDropTarget.DropTarget;
- itf : WMDropTarget.DropInterface;
- res : WORD;
- text : Texts.Text;
- textPos : Texts.TextPosition;
- caption, entryName : Strings.String;
- a : ANY;
- rec : Streams.Receiver;
- BEGIN
- IF (data # NIL) & (data IS WM.DragInfo) THEN
- di := data(WM.DragInfo);
- IF (di.data # NIL) & (di.data IS WMDropTarget.DropTarget) THEN
- dt := di.data(WMDropTarget.DropTarget)
- ELSE RETURN
- END
- ELSE RETURN
- END;
- (* drop text *)
- itf := dt.GetInterface(WMDropTarget.TypeText);
- IF itf # NIL THEN
- text := itf(WMDropTarget.DropText).text;
- textPos := itf(WMDropTarget.DropText).pos;
- IF (text # NIL) & (textPos # NIL) THEN
- text.AcquireWrite;
- TextUtilities.StrToText(text, textPos.GetPosition(), draggedString^);
- text.ReleaseWrite;
- draggedString := NIL
- END;
- RETURN
- END;
- (* drop file *)
- itf := dt.GetInterface(WMDropTarget.TypeFiles);
- IF itf # NIL THEN
- tree.Acquire;
- caption := tree.GetNodeCaption(treeView.draggedNode);
- a := tree.GetNodeData(treeView.draggedNode);
- IF a = NIL THEN RETURN END;
- entryName := a(EntryInfo).GetName();
- tree.Release;
- archive.Acquire;
- rec := archive.OpenReceiver(entryName^);
- CopyFile(rec, itf(WMDropTarget.DropFiles), caption^, res);
- archive.Release;
- RETURN
- END;
- END DragArrived;
- (* refresh archive index *)
- PROCEDURE RefreshHandler(sender, data: ANY);
- BEGIN
- SetArchive(archive)
- END RefreshHandler;
- (* called when an object has been dropped on this treeView *)
- PROCEDURE MyDragDropped(x, y : LONGINT; dragInfo : WM.DragInfo; VAR handled : BOOLEAN);
- BEGIN
- handled := TRUE;
- DragDropped(x, y, dragInfo);
- IF NodeChanged # NIL THEN NodeChanged(SELF, dragInfo) END
- END MyDragDropped;
- (* called by MyDraggedDropped *)
- PROCEDURE DragDropped*(x, y : LONGINT; dragInfo : WM.DragInfo);
- VAR dropTarget : ArchiveDropTarget;
- parent : WMTrees.TreeNode;
- accept : BOOLEAN;
- data : ANY;
- BEGIN
- tree.Acquire;
- parent := treeView.GetNodeAtPos(x, y);
- data := tree.GetNodeData(parent);
- tree.Release;
- IF (parent = NIL) OR (data # NIL) THEN
- accept := FALSE
- ELSE
- accept := TRUE;
- NEW(dropTarget, SELF, parent);
- dragInfo.data := dropTarget;
- END;
- ConfirmDrag(accept, dragInfo)
- END DragDropped;
- (* pop up context-menu *)
- PROCEDURE ContextMenu(sender : ANY; x, y: LONGINT);
- VAR wmx, wmy : LONGINT;
- node : WMTrees.TreeNode;
- data : ANY;
- BEGIN
- node := treeView.GetNodeAtPos(x, y);
- IF node # NIL THEN
- px := x; py := y;
- NEW(popup);
- tree.Acquire; data := tree.GetNodeData(node); tree.Release;
- IF data = NIL THEN (* directory *)
- popup.AddParButton("Create Folder", CreateFolder, node);
- ELSE (* leaf-node *)
- popup.AddParButton("Delete", DeleteEntry, node);
- popup.AddParButton("Rename", RenameEntry, node);
- END;
- ToWMCoordinates(x, y, wmx, wmy);
- popup.Popup(wmx, wmy+40)
- END
- END ContextMenu;
- PROCEDURE CreateFolder(sender, data : ANY);
- VAR node : WMTrees.TreeNode;
- wmx, wmy : LONGINT;
- name : ARRAY 128 OF CHAR;
- input : WMDialogs.MiniStringInput;
- BEGIN
- tree.Acquire;
- ToWMCoordinates(px, py, wmx, wmy);
- NEW(input);
- IF input.Show(wmx, wmy, name) = WMDialogs.ResOk THEN
- IF name # "" THEN
- NEW(node);
- tree.InclNodeState(data(WMTrees.TreeNode), WMTrees.NodeExpanded);
- tree.AddChildNode(data(WMTrees.TreeNode), node);
- tree.SetNodeCaption(node, Strings.NewString(name));
- END
- END;
- tree.Release;
- END CreateFolder;
- PROCEDURE DeleteEntry(sender, data : ANY);
- VAR node : WMTrees.TreeNode;
- nodeData : ANY;
- name : Strings.String;
- BEGIN
- tree.Acquire;
- node := data(WMTrees.TreeNode);
- nodeData := tree.GetNodeData(data(WMTrees.TreeNode));
- name := nodeData(EntryInfo).GetName();
- KernelLog.String("Delete entry "); KernelLog.String(name^); KernelLog.Ln;
- archive.Acquire; archive.RemoveEntry(name^); archive.Release;
- tree.RemoveNode(node);
- tree.Release;
- END DeleteEntry;
- PROCEDURE RenameEntry(sender, data : ANY);
- VAR rename : WMDialogs.MiniStringInput;
- wmx, wmy : LONGINT;
- name, caption : ARRAY 128 OF CHAR;
- entryInfo : ANY;
- s : Strings.String;
- BEGIN
- IF popup # NIL THEN popup.Close; popup := NIL END;
- NEW(rename);
- tree.Acquire;
- entryInfo := tree.GetNodeData(data(WMTrees.TreeNode));
- s := entryInfo(EntryInfo).GetName();
- COPY(s^, name);
- ToWMCoordinates(px, py, wmx, wmy);
- IF rename.Show(wmx, wmy+40, name) = WMDialogs.ResOk THEN
- IF s^ # name THEN
- archive.Acquire; entryInfo := archive.RenameEntry(s^, name); archive.Release;
- IF entryInfo # NIL THEN
- tree.SetNodeData(data(WMTrees.TreeNode), entryInfo);
- RemovePath(name, caption);
- tree.SetNodeCaption(data(WMTrees.TreeNode), Strings.NewString(caption))
- END
- END
- END;
- tree.Release
- END RenameEntry;
- (* ----- internal functions ------------------------------------------*)
- (* concatenate the captions of all nodes from NODE to the tree's root separated by '/' *)
- PROCEDURE GetPath(node : WMTrees.TreeNode) : Strings.String;
- VAR result : Strings.String;
- (* recursive method *)
- PROCEDURE GetPathRecursive(node : WMTrees.TreeNode) : Strings.String;
- VAR parent : WMTrees.TreeNode;
- name, path : Strings.String;
- BEGIN
- IF node = tree.GetRoot() THEN NEW(path, 128); path[0] := 0X; RETURN path END;
- parent := tree.GetParent(node);
- path := GetPath(parent);
- name := tree.GetNodeCaption(node);
- Strings.Append(path^, name^);
- Strings.Append(path^, "/");
- RETURN path
- END GetPathRecursive;
- BEGIN
- tree.Acquire; result := GetPathRecursive(node); tree.Release;
- RETURN result
- END GetPath;
- (* return parent's child with name. return NIL if there is no such child *)
- PROCEDURE FindChildNode(parent : WMTrees.TreeNode; name : Strings.String) : WMTrees.TreeNode;
- VAR child : WMTrees.TreeNode;
- temp : Strings.String;
- BEGIN
- tree.Acquire;
- child := tree.GetChildren(parent);
- WHILE child # NIL DO
- temp := tree.GetNodeCaption(child);
- IF temp^ = name^ THEN
- tree.Release;
- RETURN child
- END;
- child := tree.GetNextSibling(child);
- END;
- tree.Release;
- RETURN NIL
- END FindChildNode;
- (* add a child with caption and data to parent. if REPLACE is TRUE, existing nodes with the same name will be replaced *)
- PROCEDURE AddChildNode(parent : WMTrees.TreeNode; caption : Strings.String; data : EntryInfo; replace : BOOLEAN);
- VAR child : WMTrees.TreeNode;
- imgName : Strings.String;
- imgPath : ARRAY 512 OF CHAR;
- img : WMGraphics.Image;
- state : SET;
- BEGIN
- IF (caption^ # "") THEN
- IF replace THEN child := FindChildNode(parent, caption) END;
- tree.Acquire;
- IF child = NIL THEN
- NEW(child);
- tree.AddChildNode(parent, child);
- tree.SetNodeCaption(child, caption);
- IF ~showFiles.Get() THEN
- state := tree.GetNodeState(child); INCL(state, WMTrees.NodeHidden);
- tree.SetNodeState(child, state)
- END
- END;
- tree.SetNodeData(child, data);
- IF showImagePreview.Get() THEN
- COPY(archive.name, imgPath);
- Strings.Append(imgPath, "://");
- imgName := data.GetName();
- Strings.Append(imgPath, imgName^); Strings.Append(imgPath, "");
- img := WMGraphics.LoadImage(imgPath, FALSE);
- tree.SetNodeImage(child, img);
- END;
- tree.Release;
- END
- END AddChildNode;
- (* recursive method insert a new node at the right place *)
- PROCEDURE InsertTreeNode(parent : WMTrees.TreeNode; name : Strings.String; data : EntryInfo);
- VAR posOfSlash : SIZE;
- subnode : WMTrees.TreeNode;
- dirName, tail : Strings.String;
- BEGIN
- posOfSlash := Strings.Pos("/", name^);
- IF posOfSlash > -1 THEN (* go deeper in tree *)
- SplitString(name, dirName, tail, posOfSlash);
- subnode := FindChildNode(parent, dirName);
- IF subnode = NIL THEN (* add new directory *)
- NEW(subnode);
- tree.Acquire;
- tree.AddChildNode(parent, subnode);
- tree.SetNodeCaption(subnode, dirName);
- tree.Release
- END;
- InsertTreeNode(subnode, tail, data)
- ELSE (* add a leaf *)
- AddChildNode(parent, name, data, FALSE)
- END
- END InsertTreeNode;
- (* plug-in method for treeView *)
- PROCEDURE DrawTreeNode(canvas : WMGraphics.Canvas; w, h : LONGINT; node : WMTrees.TreeNode; state : SET);
- VAR dx, tdx, tdy, width, height : LONGINT; f : WMGraphics.Font;
- img : WMGraphics.Image;
- caption : Strings.String;
- BEGIN
- tree.Acquire;
- img := tree.GetNodeImage(node);
- caption := tree.GetNodeCaption(node);
- tree.Release;
- dx := 0;
- f := GetFont();
- IF img # NIL THEN
- IF img.width > 16 THEN width := 16 ELSE width := img.width END;
- IF img.height > 16 THEN height := 16 ELSE height := img.height END;
- canvas.ScaleImage(img, WMRectangles.MakeRect(0, 0, img.width, img.height), WMRectangles.MakeRect(0, 0, width, height), WMGraphics.ModeSrcOverDst, WMGraphics.ScaleBox);
- dx := 21;
- END;
- canvas.SetFont(f);
- IF WMTrees.StateSelected IN state THEN canvas.SetColor(treeView.clTextSelected.Get())
- ELSIF WMTrees.StateHover IN state THEN canvas.SetColor(treeView.clTextHover.Get())
- ELSE canvas.SetColor(treeView.clTextDefault.Get())
- END;
- f.GetStringSize(caption^, tdx, tdy);
- IF WMTrees.StateSelected IN state THEN canvas.Fill(WMRectangles.MakeRect(0, 0, dx + tdx, h), treeView.clSelected.Get(), WMGraphics.ModeSrcOverDst)
- ELSIF WMTrees.StateHover IN state THEN canvas.Fill(WMRectangles.MakeRect(0, 0, dx + tdx, h), treeView.clHover.Get(), WMGraphics.ModeSrcOverDst)
- END;
- IF caption # NIL THEN canvas.DrawString(dx, h - f.descent -1, caption^) END;
- END DrawTreeNode;
- (* plug-in method for treeView *)
- PROCEDURE MeasureTreeNode(node : WMTrees.TreeNode; VAR w, h : LONGINT);
- BEGIN
- h := TreePreviewSize; w := 400
- END MeasureTreeNode;
- (* ----- helper -------------------------------------------------- *)
- (* dest := archive.name || :// || src *)
- PROCEDURE AppendToArchiveName(CONST src: ARRAY OF CHAR; VAR dest : ARRAY OF CHAR);
- VAR i, j : LONGINT;
- BEGIN
- i := 0;
- WHILE archiveName[i] # 0X DO dest[i] := archiveName[i]; INC(i) END;
- dest[i] := ':'; INC(i);
- dest[i] := '/'; INC(i);
- dest[i] := '/'; INC(i);
- j := 0;
- WHILE src[j] # 0X DO dest[i+j] := src[j]; INC(j) END
- END AppendToArchiveName;
- END ArchiveTree;
- NodeEntry = OBJECT
- VAR
- name, full, size : Strings.String;
- node : WMTrees.TreeNode;
- END NodeEntry;
- NodeList = POINTER TO ARRAY OF NodeEntry;
- SelectionWrapper = POINTER TO RECORD
- sel : NodeList;
- END;
- (* GUI Window to perform basic operations on Archives *)
- Window* = OBJECT (WMComponents.FormWindow)
- VAR
- topToolbar, statusbar, sidePanel : WMStandardComponents.Panel;
- load : WMStandardComponents.Button;
- statusLabel : WMStandardComponents.Label;
- filenameEdit : WMEditors.Editor;
- archiveTree : ArchiveTree;
- list : WMStringGrids.StringGrid;
- nodeContent, selection : NodeList;
- curArc : Archives.Archive;
- popup : WMPopups.Popup;
- px, py: LONGINT;
- node : WMTrees.TreeNode;
- curFiles, curFolders, curBytes : LONGINT;
- PROCEDURE &New*(c : WMRestorable.Context);
- VAR vc : WMComponents.VisualComponent; xml : XML.Element; s : Strings.String;
- BEGIN
- IncCount;
- vc := CreateForm();
- Init(WindowWidth, WindowHeight, FALSE);
- SetContent(vc);
- SetTitle(Strings.NewString("WMArchives"));
- SetIcon(WMGraphics.LoadImage("WMIcons.tar://WMArchives.png", TRUE));
- IF c # NIL THEN
- (* restore the desktop *)
- WMRestorable.AddByContext(SELF, c);
- IF c.appData # NIL THEN
- xml := c.appData(XML.Element);
- s := xml.GetAttributeValue("file");
- IF s # NIL THEN Load(s^) END;
- Resized(GetWidth(), GetHeight())
- END
- ELSE
- WM.DefaultAddWindow(SELF)
- END;
- CSChanged;
- END New;
- PROCEDURE CreateForm() : WMComponents.VisualComponent;
- VAR panel : WMStandardComponents.Panel; resizerH : WMStandardComponents.Resizer;
- BEGIN
- NEW(panel); panel.alignment.Set(WMComponents.AlignClient); panel.fillColor.Set(0FFFFFFFFH);
- (* -- topToolbar -- *)
- NEW(topToolbar); topToolbar.bounds.SetHeight(20); topToolbar.alignment.Set(WMComponents.AlignTop);
- panel.AddContent(topToolbar);
- NEW(filenameEdit); filenameEdit.alignment.Set(WMComponents.AlignLeft); filenameEdit.multiLine.Set(FALSE);
- filenameEdit.bounds.SetWidth(200); filenameEdit.fillColor.Set(0FFFFFFFFH); filenameEdit.tv.showBorder.Set(TRUE);
- filenameEdit.tv.borders.Set(WMRectangles.MakeRect(3,3,1,1)); filenameEdit.onEnter.Add(LoadHandler);
- filenameEdit.tv.textAlignV.Set(WMGraphics.AlignCenter);
- topToolbar.AddContent(filenameEdit);
- NEW(load); load.caption.SetAOC("Load"); load.alignment.Set(WMComponents.AlignLeft); load.onClick.Add(LoadHandler);
- topToolbar.AddContent(load);
- (* -- statusbar -- *)
- NEW(statusbar); statusbar.bounds.SetHeight(20); statusbar.alignment.Set(WMComponents.AlignBottom);
- panel.AddContent(statusbar); statusbar.fillColor.Set(0CCCCCCFFH);
- NEW(statusLabel); statusLabel.bounds.SetWidth(WindowWidth); statusLabel.textColor.Set(0000000FFH); statusLabel.alignment.Set(WMComponents.AlignLeft);
- statusLabel.caption.SetAOC(" Total -- Folder(s) and -- Byte(s) in -- File(s)");
- statusbar.AddContent(statusLabel);
- (* -- main Archive Panel -- *)
- NEW(sidePanel); sidePanel.bounds.SetWidth(200); sidePanel.alignment.Set(WMComponents.AlignLeft);
- panel.AddContent(sidePanel);
- NEW(resizerH); resizerH.alignment.Set(WMComponents.AlignRight); sidePanel.AddContent(resizerH);
- NEW(archiveTree); archiveTree.alignment.Set(WMComponents.AlignClient);
- archiveTree.treeView.onClickNode.Add(NodeClicked); archiveTree.showFiles.Set(FALSE); archiveTree.showImagePreview.Set(FALSE);
- sidePanel.AddContent(archiveTree);
- archiveTree.NodeChanged := RefreshList;
- (* File-List *)
- NEW(list); list.alignment.Set(WMComponents.AlignClient);
- list.bounds.SetWidth(WindowWidth - 200);
- list.SetExtContextMenuHandler(ContextMenu);
- list.SetExtDragDroppedHandler(MyDragDropped);
- list.onClickSelected.Add(OpenFile);
- list.onStartDrag.Add(MyStartDrag);
- panel.AddContent(list);
- InitList;
- RETURN panel
- END CreateForm;
- PROCEDURE InitList;
- BEGIN
- list.model.Acquire;
- list.model.SetNofCols(2);
- list.model.SetNofRows(1);
- list.fixedRows.Set(1);
- list.adjustFocusPosition.Set(FALSE);
- list.model.SetCellText(0, 0, Strings.NewString("Filename"));
- list.model.SetCellText(1, 0, Strings.NewString("Size"));
- list.SetSelectionMode(WMGrids.GridSelectRows);
- list.SetSelection(-1, -1, -1, -1);
- AdjustTabSize;
- list.model.Release
- END InitList;
- PROCEDURE Resized*(width, height : LONGINT);
- BEGIN
- Resized^(width, height);
- AdjustTabSize;
- END Resized;
- (* --- helpers --------------------------------------------------- *)
- (* adjusts the Tab sizes to the current windwo width *)
- PROCEDURE AdjustTabSize;
- VAR colWidths : WMGrids.Spacings; col0Width : LONGINT;
- BEGIN
- NEW(colWidths, 2);
- col0Width := (list.bounds.GetWidth() DIV 6)*5;
- colWidths[0] := col0Width;
- colWidths[1] := list.bounds.GetWidth() - col0Width;
- list.SetColSpacings(colWidths);
- END AdjustTabSize;
- (* removes the multiline input *)
- PROCEDURE FixFilename(VAR filename : ARRAY OF CHAR);
- VAR i : LONGINT; found : BOOLEAN;
- BEGIN
- i := 0;
- WHILE (i < LEN(filename)) & ~found DO
- IF ORD(filename[i]) = 10 THEN filename[i] := 0X; found := TRUE;
- ELSIF filename[i] = 0X THEN found := TRUE END;
- INC(i);
- END;
- END FixFilename;
- PROCEDURE GetFormatFromFilename(CONST filename : ARRAY OF CHAR; VAR format : ARRAY OF CHAR);
- VAR file : ARRAY 128 OF CHAR;
- BEGIN
- IF filename = "" THEN COPY("tar", format);
- ELSE
- Strings.GetExtension(filename, file, format);
- Strings.LowerCase(format);
- END
- END GetFormatFromFilename;
- (* --- handlers --------------------------------------------------- *)
- PROCEDURE NodeClicked(sender, data : ANY);
- VAR curNode : WMTrees.TreeNode;
- tree : WMTrees.Tree; entry : NodeEntry;
- any : ANY;
- string : Strings.String; temp : ARRAY 128 OF CHAR;
- counter, tempInt : LONGINT;
- img : WMGraphics.Image;
- BEGIN
- IF (sender IS WMTrees.TreeView) & (data IS WMTrees.TreeNode) THEN
- node := data(WMTrees.TreeNode);
- tree := sender(WMTrees.TreeView).GetTree();
- tree.Acquire;
- (* count elements in node *)
- curNode := tree.GetChildren(node);
- counter := 0;
- WHILE curNode # NIL DO
- IF tree.GetNodeData(curNode) # NIL THEN INC(counter) END;
- curNode := tree.GetNextSibling(curNode);
- END;
- (* build array with elements *)
- NEW(nodeContent, counter);
- curNode := tree.GetChildren(node); counter := 0; curFiles := 0; curFolders := 0; curBytes := 0;
- WHILE curNode # NIL DO
- NEW(entry);
- entry.node := curNode;
- any := tree.GetNodeData(curNode);
- IF any # NIL THEN
- string := any(Archives.EntryInfo).GetName();
- entry.full := Strings.NewString(string^);
- tempInt := any(Archives.EntryInfo).GetSize();
- Strings.IntToStr(tempInt, string^);
- entry.size := string;
- entry.name := tree.GetNodeCaption(curNode);
- nodeContent[counter] := entry;
- INC(counter); INC(curFiles); INC(curBytes, tempInt)
- ELSE
- INC(curFolders)
- END;
- curNode := tree.GetNextSibling(curNode)
- END;
- tree.Release;
- COPY("icons.tar://", temp);
- Strings.Append(temp, "File.png");
- img := WMGraphics.LoadImage(temp, TRUE);
- (* fill list with array *)
- list.model.Acquire;
- list.model.SetNofRows(counter+1);
- WHILE counter > 0 DO
- list.model.SetCellText(0, counter, nodeContent[counter-1].name);
- list.model.SetCellText(1, counter, nodeContent[counter-1].size);
- list.model.SetCellData(0, counter, nodeContent[counter-1]);
- list.model.SetCellImage(0, counter, img);
- DEC(counter);
- END;
- list.model.Release;
- UpdateStatusbar;
- END
- END NodeClicked;
- PROCEDURE LoadHandler(sender, data : ANY);
- VAR filename : ARRAY 256 OF CHAR;
- BEGIN
- filenameEdit.GetAsString(filename);
- FixFilename(filename);
- filenameEdit.SetAsString(filename);
- Load(filename)
- END LoadHandler;
- PROCEDURE Load(CONST filename : ARRAY OF CHAR);
- VAR format : ARRAY 16 OF CHAR;
- BEGIN
- GetFormatFromFilename(filename, format);
- curArc := Archives.Old(filename, format);
- IF curArc = NIL THEN
- curArc := Archives.New(filename, format)
- END;
- IF curArc # NIL THEN filenameEdit.SetAsString(curArc.name); archiveTree.SetArchive(curArc) END
- END Load;
- PROCEDURE Handle*(VAR x: WMMessages.Message);
- VAR data : XML.Element; a : XML.Attribute; n : ARRAY 16 OF CHAR;
- filename : ARRAY 256 OF CHAR;
- BEGIN
- IF (x.msgType = WMMessages.MsgExt) & (x.ext # NIL) THEN
- IF (x.ext IS KillerMsg) THEN Close
- ELSIF (x.ext IS WMRestorable.Storage) THEN
- NEW(data); n := "WMArchivesData"; data.SetName(n);
- filenameEdit.GetAsString(filename);
- NEW(a); n := "file"; a.SetName(n); a.SetValue(filename); data.AddAttribute(a);
- x.ext(WMRestorable.Storage).Add("WMArchives", "WMArchives.Restore", SELF, data)
- ELSE Handle^(x)
- END
- ELSE Handle^(x)
- END
- END Handle;
- PROCEDURE ContextMenu(sender : ANY; x, y: LONGINT);
- VAR curSel : NodeList;
- w : SelectionWrapper;
- BEGIN
- px := x; py := y;
- NEW(popup);
- curSel := GetSelection();
- NEW(w); w.sel := curSel;
- IF LEN(curSel) = 0 THEN RETURN END;
- popup.AddParButton("Open", Open, w);
- popup.AddParButton("Delete", DeleteEntries, w);
- IF LEN(curSel) = 1 THEN
- popup.AddParButton("Rename", RenameEntry, w)
- END;
- list.ToWMCoordinates(x, y, px, py);
- popup.Popup(px, py)
- END ContextMenu;
- PROCEDURE GetSelection() : NodeList;
- VAR selection : NodeList;
- l, t, r, b, i, j : LONGINT;
- p : ANY;
- BEGIN
- list.model.Acquire;
- list.GetSelection(l, t, r, b);
- NEW(selection, b- t + 1);
- j := 0;
- FOR i := t TO b DO
- p := list.model.GetCellData(0, i);
- IF (p # NIL) & (p IS NodeEntry) THEN
- selection[j] := p(NodeEntry);
- INC(j)
- END
- END;
- list.model.Release;
- RETURN selection
- END GetSelection;
- PROCEDURE Open(sender, data : ANY);
- VAR d: NodeList; i : LONGINT;
- BEGIN
- IF (popup # NIL) THEN popup.Close; popup := NIL END;
- IF (data # NIL) & (data IS SelectionWrapper) THEN
- d := data(SelectionWrapper).sel;
- IF (d # NIL) THEN
- FOR i := 0 TO LEN(d)-1 DO
- OpenFile(SELF, d[i]);
- END;
- END;
- END;
- END Open;
- PROCEDURE OpenFile(sender, data : ANY);
- VAR filename : Files.FileName;
- BEGIN
- IF (data # NIL) & (data IS NodeEntry) THEN
- COPY(curArc.name, filename);
- Strings.Append(filename, "://");
- Strings.Append(filename, data(NodeEntry).full^);
- FileHandlers.OpenFile(filename, NIL, SELF);
- END;
- END OpenFile;
- PROCEDURE RenameEntry(sender, data : ANY);
- VAR rename : WMDialogs.MiniStringInput;
- name, caption : ARRAY 128 OF CHAR;
- entryInfo : ANY; entry : NodeEntry;
- s : Strings.String;
- BEGIN
- IF (data # NIL) & (data IS SelectionWrapper) THEN
- entry := data(SelectionWrapper).sel[0];
- IF entry # NIL THEN
- NEW(rename);
- archiveTree.tree.Acquire;
- entryInfo := archiveTree.tree.GetNodeData(entry.node);
- s := entryInfo(EntryInfo).GetName();
- COPY(s^, name);
- IF rename.Show(px, py, name) = WMDialogs.ResOk THEN
- IF s^ # name THEN
- archiveTree.archive.Acquire; entryInfo := archiveTree.archive.RenameEntry(s^, name); archiveTree.archive.Release;
- IF entryInfo # NIL THEN
- archiveTree.tree.SetNodeData(entry.node, entryInfo);
- RemovePath(name, caption);
- archiveTree.tree.SetNodeCaption(entry.node, Strings.NewString(caption));
- NodeClicked(archiveTree.treeView, archiveTree.tree.GetParent(entry.node))
- END
- END
- END;
- archiveTree.tree.Release;
- END
- END
- END RenameEntry;
- PROCEDURE DeleteEntries(sender, data : ANY);
- VAR parent : WMTrees.TreeNode;
- entry : NodeEntry; entryInfo : ANY; s : Strings.String;
- dr, i : LONGINT; name : ARRAY 128 OF CHAR;
- delete, always, never : BOOLEAN;
- BEGIN
- IF (data # NIL) & (data IS SelectionWrapper) THEN
- always := FALSE; never := FALSE;
- archiveTree.tree.Acquire;
- FOR i := 0 TO LEN(data(SelectionWrapper).sel) - 1 DO
- entry := data(SelectionWrapper).sel[i];
- delete := FALSE;
- IF entry # NIL THEN
- entryInfo := archiveTree.tree.GetNodeData(entry.node);
- parent := archiveTree.tree.GetParent(entry.node);
- s := entryInfo(EntryInfo).GetName();
- COPY(s^, name);
- IF ~always & ~never THEN
- dr := WMDialogs.Message(WMDialogs.TConfirmation, "Confirm deleting file", name,
- {WMDialogs.ResNo, WMDialogs.ResAbort, WMDialogs.ResYes, WMDialogs.ResAll});
- IF dr IN {WMDialogs.ResYes, WMDialogs.ResAll} THEN delete := TRUE END;
- IF dr = WMDialogs.ResAll THEN always := TRUE END;
- IF dr = WMDialogs.ResAbort THEN never := TRUE END;
- END;
- IF ~never & (delete OR always) THEN
- archiveTree.archive.Acquire; archiveTree.archive.RemoveEntry(name); archiveTree.archive.Release;
- archiveTree.tree.RemoveNode(entry.node);
- NodeClicked(archiveTree.treeView, parent)
- END
- END
- END;
- archiveTree.tree.Release;
- END
- END DeleteEntries;
- PROCEDURE RefreshList(sender, data : ANY);
- VAR selected : WMTrees.TreeNode;
- BEGIN
- IF node # NIL THEN selected := node;
- ELSE selected := archiveTree.tree.GetRoot() END;
- NodeClicked(archiveTree.treeView, selected)
- END RefreshList;
- PROCEDURE UpdateStatusbar;
- VAR statusStr, tempStr : ARRAY 256 OF CHAR;
- BEGIN
- COPY(" Total ", statusStr);
- Strings.IntToStr(curFolders, tempStr); Strings.Append(statusStr, tempStr);
- Strings.Append(statusStr, " Folder(s) and ");
- Strings.IntToStr(curBytes, tempStr); Strings.Append(statusStr, tempStr);
- Strings.Append(statusStr, " Byte(s) in ");
- Strings.IntToStr(curFiles, tempStr); Strings.Append(statusStr, tempStr);
- Strings.Append(statusStr, " File(s)");
- statusLabel.caption.SetAOC(statusStr);
- END UpdateStatusbar;
- (* ----- drag operations ----------------------------------------- *)
- (* called when an object has been dropped on the file-list *)
- PROCEDURE MyDragDropped(x, y : LONGINT; dragInfo : WM.DragInfo; VAR handled : BOOLEAN);
- BEGIN
- handled := TRUE;
- ListDragDropped(x, y, dragInfo);
- RefreshList(SELF, node)
- END MyDragDropped;
- (* called by MyDraggedDropped *)
- PROCEDURE ListDragDropped(x, y : LONGINT; dragInfo : WM.DragInfo);
- VAR dropTarget : ArchiveDropTarget;
- parent : WMTrees.TreeNode;
- accept : BOOLEAN;
- data : ANY;
- BEGIN
- archiveTree.tree.Acquire;
- IF node # NIL THEN parent := node
- ELSE parent := archiveTree.tree.GetRoot() END;
- data := archiveTree.tree.GetNodeData(parent);
- archiveTree.tree.Release;
- IF (parent = NIL) OR (data # NIL) THEN
- accept := FALSE
- ELSE
- accept := TRUE;
- NEW(dropTarget, archiveTree, parent);
- dragInfo.data := dropTarget;
- END;
- ConfirmDrag(accept, dragInfo)
- END ListDragDropped;
- (* called when a drag-operation has been started *)
- PROCEDURE MyStartDrag(sender, data : ANY);
- VAR img : WMGraphics.Image;
- c : WMGraphics.BufferCanvas;
- top, i : LONGINT;
- BEGIN
- selection := GetSelection();
- (* render to bitmap *)
- NEW(img); Raster.Create(img, 100, 200, Raster.BGRA8888);
- NEW(c, img);
- c.SetColor(LONGINT(0FFFF00FFH));
- top := 0;
- FOR i := 0 TO LEN(selection) - 1 DO
- IF selection[i] # NIL THEN
- c.Fill(WMRectangles.MakeRect(0, top, 100, top + 25), 0FF80H, WMGraphics.ModeCopy);
- c.DrawString(3, top + 20, selection[i].name^);
- INC(top, 25)
- END
- END;
- IF list.StartDrag(NIL, img, 0,0,ListDragArrived, NIL) THEN KernelLog.String("Dragging started")
- ELSE KernelLog.String("Drag could not be started")
- END;
- END MyStartDrag;
- (* called when an object dragged from the list has been dropped anywhere *)
- PROCEDURE ListDragArrived(sender, data : ANY);
- VAR di : WM.DragInfo;
- dt : WMDropTarget.DropTarget;
- itf : WMDropTarget.DropInterface;
- i : LONGINT; res : WORD;
- sel : NodeList;
- url, caption : ARRAY 1024 OF CHAR;
- text : Texts.Text;
- textPos : Texts.TextPosition;
- rec : Streams.Receiver;
- nl: ARRAY 2 OF CHAR;
- BEGIN
- sel := selection;
- IF sel = NIL THEN RETURN END;
- IF (data # NIL) & (data IS WM.DragInfo) THEN
- di := data(WM.DragInfo);
- IF (di.data # NIL) & (di.data IS WMDropTarget.DropTarget) THEN
- dt := di.data(WMDropTarget.DropTarget)
- ELSE RETURN
- END
- ELSE RETURN
- END;
- (* File *)
- itf := dt.GetInterface(WMDropTarget.TypeFiles);
- IF itf # NIL THEN
- FOR i := 0 TO LEN(selection) - 1 DO
- IF selection[i] # NIL THEN
- COPY(selection[i].full^, url);
- COPY(selection[i].name^, caption);
- archiveTree.archive.Acquire;
- rec := archiveTree.archive.OpenReceiver(url);
- CopyFile(rec, itf(WMDropTarget.DropFiles), caption, res);
- archiveTree.archive.Release;
- END
- END;
- RETURN
- END;
- (* Text *)
- itf := dt.GetInterface(WMDropTarget.TypeText);
- IF itf # NIL THEN
- text := itf(WMDropTarget.DropText).text;
- textPos := itf(WMDropTarget.DropText).pos;
- IF (text # NIL) & (textPos # NIL) THEN
- text.AcquireWrite;
- FOR i := 0 TO LEN(selection) - 1 DO
- IF selection[i] # NIL THEN
- COPY(selection[i].name^, url);
- nl[0] := CHR(Texts.NewLineChar);
- nl[1] := 0X;
- Strings.Append(url, nl);
- TextUtilities.StrToText(text, textPos.GetPosition(), url)
- END
- END;
- text.ReleaseWrite
- END;
- RETURN
- END;
- END ListDragArrived;
- PROCEDURE Close*;
- BEGIN
- Close^;
- DecCount;
- END Close;
- END Window;
- VAR
- nofWindows : LONGINT;
- ProtShowFiles, ProtShowImgPrev : WMProperties.BooleanProperty;
- StrArchiveTree : Strings.String;
- (* ----- helpers ------------------------------------------------------- *)
- (* copy the head of string to head and the tail of string to tail *)
- PROCEDURE SplitString(string : Strings.String; VAR head, tail : Strings.String; index : SIZE);
- VAR i : LONGINT;
- BEGIN
- NEW(head, index+1);
- NEW(tail, LEN(string^)-index);
- (* head *)
- i := 0;
- WHILE i < index DO
- head[i] := string[i];
- INC(i)
- END;
- head[i] := 0X;
- (* tail *)
- i := 0;
- WHILE string[index+1+i] # 0X DO
- tail[i] := string[index+1+i];
- INC(i)
- END;
- tail[i] := 0X
- END SplitString;
- (* "path1/path2/.../name" => "name" *)
- PROCEDURE RemovePath(CONST src : ARRAY OF CHAR; VAR dest : ARRAY OF CHAR);
- VAR i, j : LONGINT;
- BEGIN
- i := LEN(src) - 1;
- WHILE (i > 0) & (src[i] # '/') DO DEC(i) END;
- IF i > 0 THEN INC(i) END;
- FOR j := 0 TO LEN(src) - 1 - i DO
- dest[j] := src[i];
- INC(i)
- END
- END RemovePath;
- (* "PART:file.ext" => "file.ext" *)
- PROCEDURE RemovePartitionLabel(CONST src : ARRAY OF CHAR; VAR dest : ARRAY OF CHAR);
- VAR i, j : LONGINT;
- BEGIN
- i := 0;
- WHILE (i < LEN(src)) & (src[i] # ':') DO INC(i) END;
- IF i = LEN(src) THEN
- COPY(src, dest)
- ELSE
- j := 0;
- WHILE j + i + 1 < LEN(src) DO
- dest[j] := src[i+j+1];
- INC(j)
- END
- END
- END RemovePartitionLabel;
- (* transfer data from rec to target *)
- PROCEDURE CopyFile(rec : Streams.Receiver; target : WMDropTarget.DropFiles; CONST remote : ARRAY OF CHAR; VAR res : WORD);
- VAR w : Streams.Writer; r : Streams.Reader; buf: ARRAY BufSize OF CHAR; len: LONGINT;
- BEGIN
- res := -1;
- Streams.OpenReader(r, rec);
- target.OpenPut(remote, w, res);
- IF res = 0 THEN
- REPEAT
- r.Bytes(buf, 0, BufSize, len); w.Bytes(buf, 0, len);
- UNTIL r.res # 0;
- target.ClosePut(res)
- END
- END CopyFile;
- (* ---- window stuff -------------------------------------------------------------------- *)
- PROCEDURE InitPrototypes;
- VAR plArchiveTree : WMProperties.PropertyList;
- BEGIN
- (* archive tree *)
- NEW(plArchiveTree); WMComponents.propertyListList.Add("Archive Tree", plArchiveTree);
- NEW(ProtShowFiles, NIL, Strings.NewString("Show Files"), Strings.NewString("Enables Tree to show the Files")); plArchiveTree.Add(ProtShowFiles);
- ProtShowFiles.Set(TRUE);
- NEW(ProtShowImgPrev, NIL, Strings.NewString("Show Image Preview"), Strings.NewString("Enables Tree to show Image Previews")); plArchiveTree.Add(ProtShowImgPrev);
- ProtShowImgPrev.Set(TRUE);
- StrArchiveTree := Strings.NewString("Archive Tree")
- END InitPrototypes;
- PROCEDURE Open*(context : Commands.Context);
- VAR window : Window; filename : Files.FileName;
- BEGIN
- NEW(window, NIL);
- IF context.arg.GetString(filename) THEN
- window.Load(filename);
- END;
- END Open;
- PROCEDURE Restore*(context : WMRestorable.Context);
- VAR win : Window;
- BEGIN
- ASSERT(context # NIL);
- NEW(win, context)
- END Restore;
- PROCEDURE IncCount;
- BEGIN {EXCLUSIVE}
- INC(nofWindows);
- END IncCount;
- PROCEDURE DecCount;
- BEGIN {EXCLUSIVE}
- DEC(nofWindows);
- END DecCount;
- PROCEDURE Cleanup;
- VAR die : KillerMsg;
- msg : WMMessages.Message;
- m : WM.WindowManager;
- BEGIN {EXCLUSIVE}
- NEW(die);
- msg.ext := die;
- msg.msgType := WMMessages.MsgExt;
- m := WM.GetDefaultManager();
- m.Broadcast(msg);
- AWAIT(nofWindows = 0);
- END Cleanup;
- BEGIN
- InitPrototypes;
- Modules.InstallTermHandler(Cleanup);
- END WMArchives.
- WMArchives.Open ~
- WMArchives.Open traditional.skin ~
- System.Free WMArchives ~
|