home *** CD-ROM | disk | FTP | other *** search
/ PC Open 19 / pcopen19.iso / Zipped / CALMIR21.ZIP / SOURCE.ZIP / SRC / TREE.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1998-02-20  |  26.3 KB  |  934 lines

  1. {**************************************************************************}
  2. {                                                                          }
  3. {    Calmira shell for Microsoft« Windows(TM) 3.1                          }
  4. {    Source Release 2.1                                                    }
  5. {    Copyright (C) 1997-1998 Li-Hsin Huang                                 }
  6. {                                                                          }
  7. {    This program is free software; you can redistribute it and/or modify  }
  8. {    it under the terms of the GNU General Public License as published by  }
  9. {    the Free Software Foundation; either version 2 of the License, or     }
  10. {    (at your option) any later version.                                   }
  11. {                                                                          }
  12. {    This program is distributed in the hope that it will be useful,       }
  13. {    but WITHOUT ANY WARRANTY; without even the implied warranty of        }
  14. {    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         }
  15. {    GNU General Public License for more details.                          }
  16. {                                                                          }
  17. {    You should have received a copy of the GNU General Public License     }
  18. {    along with this program; if not, write to the Free Software           }
  19. {    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             }
  20. {                                                                          }
  21. {**************************************************************************}
  22.  
  23. unit Tree;
  24.  
  25. { This form serves two purposes: the global variable Explorer points
  26.   to the "Explorer" window that is used to navigate disks.  An
  27.   extra function called SelectFolder() creates a modal tree dialog
  28.   for the user to pick a directory.
  29.  
  30.   Since Delphi's form inheritance is rather limited, both versions
  31.   of the tree are handled by one class, and the IsDialog field
  32.   determines how the object should behave.
  33.  
  34.   Directory outlines
  35.  
  36.   Delphi's sample TDirectoryOutline is pretty hopeless, as most Delphi
  37.   programmers have discovered.  The tree view needs to indicate folders
  38.   which contain sub-folders, but TOutline can't cope with drawing
  39.   plus/minus symbols together with node pictures, and TDirectoryOutline
  40.   doesn't bother to tackle this.
  41.  
  42.   So some custom code is required, which builds each level of the
  43.   tree as the user reaches it, but also checks for sub-folders.
  44.  
  45.  
  46.   Outline drawing
  47.  
  48.   The main feature of the tree view is the that way it owner-draws the
  49.   TOutline control.  The default TOutline painting method uses BrushCopy(),
  50.   which provides bitmap transparency but is extremely slow.  The tree
  51.   view just uses Draw(), which makes it very fast, but this means that
  52.   selected items can only be focused and not highlighted.
  53.  
  54.   Another problem is that level 1 nodes (i.e. disk drives) need to have
  55.   descriptive captions, but this makes it harder to obtain the
  56.   selected folder using the FullPath property.  The solution is to store
  57.   the descriptive captions in a separate TStringList which is accessed
  58.   during drawing.
  59. }
  60.  
  61.  
  62. interface
  63.  
  64. uses
  65.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  66.   Forms, Dialogs, Grids, Outline, StdCtrls, IconWin, FileCtrl, Menus,
  67.   ExtCtrls, CalForm, Settings, Scrtree, CalMsgs, Sysmenu, Buttons;
  68.  
  69. type
  70.   TExplorer = class(TCalForm)
  71.     PopupMenu: TPopupMenu;
  72.     OpenFolder: TMenuItem;
  73.     OpenNew: TMenuItem;
  74.     RefreshTree: TMenuItem;
  75.     N2: TMenuItem;
  76.     ExpandLevel: TMenuItem;
  77.     ExpandBranch: TMenuItem;
  78.     ExpandAll: TMenuItem;
  79.     CollapseBranch: TMenuItem;
  80.     N1: TMenuItem;
  81.     FileWindow: TMenuItem;
  82.     Outline: TScrollTree;
  83.     SystemMenu: TSystemMenu;
  84.     OKBtn: TBitBtn;
  85.     CancelBtn: TBitBtn;
  86.     procedure FormCreate(Sender: TObject);
  87.     procedure OutlineDrawItem(Control: TWinControl; Index: Integer;
  88.       Rect: TRect; State: TOwnerDrawState);
  89.     procedure FormResize(Sender: TObject);
  90.     procedure OpenFolderClick(Sender: TObject);
  91.     procedure OpenNewClick(Sender: TObject);
  92.     procedure ExpandLevelClick(Sender: TObject);
  93.     procedure ExpandBranchClick(Sender: TObject);
  94.     procedure ExpandAllClick(Sender: TObject);
  95.     procedure CollapseBranchClick(Sender: TObject);
  96.     procedure FormClose(Sender: TObject; var Action: TCloseAction);
  97.     procedure OutlineMouseDown(Sender: TObject; Button: TMouseButton;
  98.       Shift: TShiftState; X, Y: Integer);
  99.     procedure OutlineClick(Sender: TObject);
  100.     procedure FormDestroy(Sender: TObject);
  101.     procedure RefreshTreeClick(Sender: TObject);
  102.     procedure OutlineExpand(Sender: TObject; Index: Longint);
  103.     procedure FileWindowClick(Sender: TObject);
  104.     procedure FormHide(Sender: TObject);
  105.     procedure FormShow(Sender: TObject);
  106.     procedure FormPaint(Sender: TObject);
  107.     procedure OutlineKeyDown(Sender: TObject; var Key: Word;
  108.       Shift: TShiftState);
  109.     procedure OutlineDragOver(Sender, Source: TObject; X, Y: Integer;
  110.       State: TDragState; var Accept: Boolean);
  111.     procedure OutlineDragDrop(Sender, Source: TObject; X, Y: Integer);
  112.     procedure OutlineMouseUp(Sender: TObject; Button: TMouseButton;
  113.       Shift: TShiftState; X, Y: Integer);
  114.     procedure PopupMenuPopup(Sender: TObject);
  115.     procedure DelClick(Sender: TObject);
  116.   private
  117.     { Private declarations }
  118.     FilePane : TIconWindow;
  119.     PreventClick : Boolean;
  120.     Walking: Boolean;
  121.     DriveCaptions : TStringList;
  122.     BmpList : TBitmap;
  123.     IsDialog : Boolean;
  124.     procedure AlignFilePane;
  125.     procedure WMWindowPosChanged(var Msg : TWMWindowPosChanged); message WM_WINDOWPOSCHANGED;
  126.   protected
  127.     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  128.     procedure ExpandFolder(Index: Longint);
  129.     procedure Walktree(Index: Longint);
  130.     function FindDirectory(const Dir: string; ExpandPath: Boolean): Longint;
  131.   public
  132.     { Public declarations }
  133.     function SelectedFolder : TFilename;
  134.     procedure BuildTree;
  135.     procedure Configure;
  136.     procedure Travel(const folder: TFilename);
  137.     procedure SettingsChanged(Changes : TSettingChanges); override;
  138.     constructor CreateDialog(AOwner : TComponent);
  139.   end;
  140.  
  141. procedure OpenExplorer(const default : TFilename);
  142. function SelectFolder(const default : TFilename): TFilename;
  143.  
  144.  
  145. var
  146.   Explorer: TExplorer;
  147.  
  148. implementation
  149.  
  150. {$R *.DFM}
  151.  
  152. uses Strings, Desk, MiscUtil, Files, Resource, CompSys, Iconic,
  153.   Drives, MultiGrd, Referenc, Locale, FileMan, Task;
  154.  
  155. const
  156.   { TOutlineNode's Data property is used to store flags, which speeds
  157.     up drawing by avoiding the call to GetLastChild by marking the last
  158.     child node.  The HasChildren flag determines if subdirectories
  159.     exist. }
  160.  
  161.   IsLastChild = 1;
  162.   HasChildren = 2;
  163.  
  164.  
  165. function ExtractNodeDir(const s: TFilename): TFilename;
  166. var p: Integer;
  167. begin
  168.   { Returns the name of a folder, given an outline node's FullPath,
  169.     which looks something like
  170.  
  171.     Computer\c:\\delphi\projects
  172.  
  173.     The first Delete() call chops off 'Computer\' and the second
  174.     removes the extra '\'.  This should leave a valid folder.
  175.   }
  176.  
  177.   Result := s;
  178.   p := Pos('\', Result);
  179.   if p > 0 then System.Delete(Result, 1, p);
  180.   p := Pos('\\', Result);
  181.   if p > 0 then System.Delete(Result, p, 1);
  182. end;
  183.  
  184.  
  185. procedure TExplorer.BuildTree;
  186. var
  187.   root : string[3];
  188.   i: Integer;
  189.   Last : Longint;
  190.   Letter : Char;
  191.   DriveType : TDriveType;
  192.   title : string[63];
  193.   node : TOutlineNode;
  194. begin
  195.   { Constructs the 1st two levels of the outline.
  196.  
  197.     Fixed drives are searched for a volume label and removeable drives
  198.     are just indicated as such.  Each title is added to the DriveCaptions
  199.     list. }
  200.  
  201.   DriveCaptions.Clear;
  202.   Outline.Clear;
  203.   Outline.AddChild(0, Computer.Caption);
  204.   Last := 0;
  205.  
  206.   for Letter := 'A' to 'Z' do begin
  207.     DriveType := GuessDriveType(Letter);
  208.     if DriveType <> dtNoDrive then begin
  209.       Last := Outline.AddChild(1, LowCase(Letter) + ':\');
  210.       node := Outline.Items[Last];
  211.       case DriveType of
  212.         dtFloppy,
  213.         dtCDROM  : title := '';
  214.         dtFixed,
  215.         dtNetwork: title := GetNetworkVolume(Letter);
  216.         dtRAM    : title := GetVolumeID(Letter);
  217.       end;
  218.       if title = '' then title := MakeDriveName(DriveType, Letter)
  219.       else title := Format('%s (%s:)', [title, Letter]);
  220.       DriveCaptions.AddObject(title, node);
  221.     end;
  222.   end;
  223.  
  224.   if Last > 0 then Outline.Items[Last].Data := Pointer(IsLastChild);
  225.  
  226.   Outline.Items[1].Expand;
  227. end;
  228.  
  229.  
  230. procedure TExplorer.FormCreate(Sender: TObject);
  231. begin
  232.   with SystemMenu do begin
  233.     DeleteCommand(SC_SIZE);
  234.     DeleteCommand(SC_MAXIMIZE);
  235.   end;
  236.  
  237.   MinimumWidth := 128;
  238.   MinimumHeight := 64;
  239.  
  240.   BmpList := TResBitmap.AlternateLoad('TREEBMPS', 'explrico.bmp');
  241.  
  242.   DriveCaptions := TStringList.Create;
  243.   Icon.Assign(Icons.Get('Explorer'));
  244.   Configure;
  245.  
  246.   if not IsDialog then begin
  247.     OKBtn.Free;
  248.     CancelBtn.Free;
  249.     LoadPosition(ini, 'Explorer');
  250.     FileWindow.Checked := ini.ReadBool('Explorer', 'FileWindow', False);
  251.   end;
  252.  
  253.   BuildTree;
  254. end;
  255.  
  256.  
  257. procedure TExplorer.Configure;
  258. begin
  259.   Color := Colors[ccWinFrame];
  260.   with Outline do begin
  261.     Font.Assign(GlobalFont);
  262.     Canvas.Font.Assign(Font);
  263.     Canvas.Pen.Color := clTeal;
  264.     ItemHeight := LineHeight;
  265.     ThumbTracking := TrackThumb;
  266.   end;
  267. end;
  268.  
  269.  
  270. procedure TExplorer.OutlineDrawItem(Control: TWinControl; Index: Integer;
  271.   Rect: TRect; State: TOwnerDrawState);
  272. const
  273.   PictureOpenRect : TRect =
  274.     (Left: 128; Top: 0; Right: 144; Bottom: 12);
  275.   PictureClosedRect : TRect =
  276.     (Left: 144; Top: 0; Right: 160; Bottom: 12);
  277.   PicturePlusRect : TRect =
  278.     (Left: 160; Top: 0; Right: 169; Bottom: 9);
  279.   PictureMinusRect : TRect =
  280.     (Left: 176; Top: 0; Right: 185; Bottom: 9);
  281. var
  282.   item: TOutlineNode;
  283.   x, y, L : Integer;
  284.   folder : string[12];
  285. begin
  286.   with Outline do begin
  287.     { TOutline [mistakenly?] passes the graphical row as the Index
  288.       rather than the index of the outline item, so we must convert
  289.       it back. }
  290.  
  291.     Index := GetItem(0, Rect.Top);
  292.     item := Items[index];
  293.     L := item.Level;
  294.     x := Rect.Left + (L-1) * 20 + 4;
  295.     y := (Rect.Top + Rect.Bottom) div 2;
  296.  
  297.     with Canvas do begin
  298.       FillRect(Rect);
  299.  
  300.       { index = 1   the Computer 'icon' is drawn
  301.         level = 2   the drive type is used to offset into the bitmap list
  302.         else        an open or closed folder is drawn }
  303.  
  304.       if index = 1 then
  305.         CopyRect(Bounds(x, Rect.Top, 16, 16), BmpList.Canvas,
  306.           Bounds(0, 0, 16, 16))
  307.  
  308.       else if L = 2 then
  309.         CopyRect(Bounds(x, Rect.Top, 16, 16), BmpList.Canvas,
  310.           Bounds(Succ(Ord(GuessDriveType(item.Text[1]))) * 16, 0, 16, 16))
  311.  
  312.       else if item.HasItems and item.Expanded then
  313.         CopyRect(Bounds(x, Rect.Top+2, 16, 12), BmpList.Canvas, PictureOpenRect)
  314.       else
  315.         CopyRect(Bounds(x, Rect.Top+2, 16, 12), BmpList.Canvas, PictureClosedRect);
  316.  
  317.       { items on level 2 are disk drives, which have their captions
  318.         stored in the string list }
  319.  
  320.       if L = 2 then
  321.         TextOut(x + 19, Rect.Top+1, DriveCaptions[DriveCaptions.IndexOfObject(item)])
  322.       else begin
  323.         folder := item.Text;
  324.         if UpcaseFirstChar then folder[1] := UpCase(folder[1]);
  325.         TextOut(x + 19, Rect.Top+1, folder);
  326.       end;
  327.  
  328.       if index = 1 then exit;
  329.  
  330.       { Draw the horizontal line connecting the node }
  331.       MoveTo(x - 4, y);
  332.       Dec(x, 16);
  333.       LineTo(x, y);
  334.  
  335.       { If the node is the last child, don't extend the vertical
  336.         line any further than the middle }
  337.  
  338.       if Longint(item.Data) and IsLastChild > 0 then
  339.         LineTo(x, Rect.Top-1)
  340.       else begin
  341.         MoveTo(x, Rect.Top);
  342.         LineTo(x, Rect.Bottom);
  343.       end;
  344.  
  345.       { Draw a suitable plus/minus picture depending on if
  346.         there are subfolders }
  347.  
  348.       if Longint(item.Data) and HasChildren > 0 then
  349.         if item.Expanded then
  350.           CopyRect(Bounds(x-4, y-4, 9, 9), BmpList.Canvas, PictureMinusRect)
  351.           {Draw(x - 4, y - 4, PictureMinus)}
  352.         else
  353.           CopyRect(bounds(x-4, y-4, 9, 9), BmpList.Canvas, PicturePlusRect);
  354.           {Draw(x - 4, y - 4, PicturePlus);}
  355.  
  356.       { Draw the vertical lines to the left of the node's bitmap,
  357.         by moving up through the parent nodes.  If a parent node
  358.         is a "last child", then don't draw a line (because there
  359.         are no siblings underneath it) }
  360.  
  361.       Dec(x, 20);
  362.       while x > 0 do begin
  363.         item := item.Parent;
  364.         if not Longint(item.Data) and IsLastChild > 0 then begin
  365.           MoveTo(x, Rect.Top);
  366.           LineTo(x, Rect.Bottom);
  367.         end;
  368.         Dec(x, 20);
  369.       end;
  370.  
  371.       if ([odSelected, odFocused] * State) <> [] then DrawFocusRect(Rect);
  372.     end;
  373.   end;
  374. end;
  375.  
  376.  
  377. function TExplorer.SelectedFolder : TFilename;
  378. var p: Integer;
  379. begin
  380.   with Outline do
  381.     if SelectedItem = 1 then Result := ''
  382.     else Result := ExtractNodeDir(Items[SelectedItem].FullPath);
  383. end;
  384.  
  385.  
  386. procedure TExplorer.Notification(AComponent: TComponent; Operation: TOperation);
  387. begin
  388.   { The tree view must be kept informed if it's slave icon window
  389.     has been destroyed }
  390.   inherited Notification(AComponent, Operation);
  391.   if (AComponent = FilePane) and (Operation = opRemove) then FilePane := nil;
  392. end;
  393.  
  394.  
  395. procedure TExplorer.FormResize(Sender: TObject);
  396. begin
  397.   if IsDialog then begin
  398.     StretchShift([Outline], [stWidth, stHeight]);
  399.     StretchShift([OKBtn, CancelBtn], [stLeft, stTop]);
  400.   end
  401.   else begin
  402.     Outline.Width := ClientWidth - 8;
  403.     Outline.Height := ClientHeight - Outline.Top - 4;
  404.   end;
  405.   Invalidate;
  406. end;
  407.  
  408.  
  409. procedure TExplorer.AlignFilePane;
  410. var
  411.   w: Integer;
  412. begin
  413.   if (WindowState = wsMinimized) or (FilePane = nil) or
  414.     (FilePane.WindowState = wsMinimized) then Exit;
  415.  
  416.   FilePane.WindowState := wsNormal;
  417.  
  418.   { SetWindowPos conveniently repositions windows without activating them }
  419.  
  420.   if FileWindow.Checked then begin
  421.     with FilePane do
  422.       if Visible then w := Width
  423.       else w := CalcSize(FilePaneCols, 4).x;
  424.  
  425.     SetWindowPos(FilePane.Handle, Handle, Left + Width - 1, Top,
  426.       w, Height, SWP_NOACTIVATE)
  427.   end
  428.   else
  429.     SetWindowPos(FilePane.Handle, Handle, 0, 0, 0, 0,
  430.       SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE);
  431. end;
  432.  
  433.  
  434. procedure TExplorer.OpenFolderClick(Sender: TObject);
  435. var
  436.   s: TFilename;
  437.   w: TIconWindow;
  438. begin
  439.   { A modal tree dialog returns immediately after a folder is
  440.     "opened" or Enter is pressed }
  441.  
  442.   if IsDialog then begin
  443.     if Outline.SelectedItem > 1 then ModalResult := mrOK;
  444.     Exit;
  445.   end;
  446.  
  447.   if Outline.SelectedItem = 1 then Computer.ShowNormal
  448.   else begin
  449.     s := SelectedFolder;
  450.     w := Desktop.WindowOf(s);
  451.  
  452.     if w <> nil then begin
  453.       { there is an existing window of the selected directory }
  454.       if w = FilePane then begin
  455.         { the window already belongs to explorer }
  456.         if w.Dir.Fullname <> s then w.RefreshWin;
  457.         Exit;
  458.       end;
  459.  
  460.       { don't use w.Free because this method may be called during
  461.         a KeyPress event of w }
  462.  
  463.       w.Release;
  464.     end;
  465.  
  466.     if FilePane <> nil then begin
  467.       FilePane.ChangeDir(s);
  468.       AlignFilePane;
  469.     end
  470.     else begin
  471.       FilePane := TIconWindow.Init(Application, s, DefaultFilter);
  472.       FilePane.Locked := True;
  473.       AlignFilePane;
  474.       FilePane.Visible := True;
  475.     end;
  476.   end;
  477. end;
  478.  
  479.  
  480. procedure TExplorer.OpenNewClick(Sender: TObject);
  481. var s: TFilename;
  482. begin
  483.   if IsDialog then Exit;
  484.  
  485.   if Outline.SelectedItem = 1 then Computer.ShowNormal
  486.   else begin
  487.     s := SelectedFolder;
  488.     if (FilePane <> nil) and (FilePane.Dir.Fullname = s) then begin
  489.       { release the file pane }
  490.       FilePane.Locked := False;
  491.       FilePane := nil;
  492.       SetFocus;
  493.     end
  494.     else
  495.       Desktop.OpenFolder(s);
  496.   end;
  497. end;
  498.  
  499.  
  500. procedure TExplorer.ExpandLevelClick(Sender: TObject);
  501. var item : TOutlineNode;
  502. begin
  503.   with Outline do begin
  504.     item := Items[SelectedItem];
  505.     if not item.HasItems and (Longint(item.Data) and HasChildren > 0) then
  506.       ExpandFolder(SelectedItem);
  507.     item.Expand;
  508.   end;
  509. end;
  510.  
  511.  
  512. procedure TExplorer.ExpandBranchClick(Sender: TObject);
  513. begin
  514.   Desktop.SetCursor(crHourGlass);
  515.   Update;
  516.   Walking := True;
  517.   try
  518.     with Outline do begin
  519.       WalkTree(SelectedItem);
  520.       Items[SelectedItem].FullExpand;
  521.     end;
  522.   finally
  523.     Desktop.ReleaseCursor;
  524.     Walking := False;
  525.   end;
  526. end;
  527.  
  528.  
  529. procedure TExplorer.ExpandAllClick(Sender: TObject);
  530. begin
  531.   UpdateScreen;
  532.   Desktop.SetCursor(crHourGlass);
  533.   LockWindowUpdate(Outline.Handle);
  534.   Walking := True;
  535.   try
  536.     WalkTree(1);
  537.     Outline.FullExpand;
  538.   finally
  539.     LockWindowUpdate(0);
  540.     Desktop.ReleaseCursor;
  541.     Walking := False;
  542.   end;
  543. end;
  544.  
  545.  
  546. procedure TExplorer.CollapseBranchClick(Sender: TObject);
  547. begin
  548.   with Outline do Items[SelectedItem].Collapse;
  549. end;
  550.  
  551.  
  552. procedure TExplorer.FormClose(Sender: TObject; var Action: TCloseAction);
  553. begin
  554.   if IsDialog then Action := caHide
  555.   else begin
  556.     Action := caFree;
  557.     if FilePane <> nil then begin
  558.       FilePane.Locked := False;
  559.       if TreeCloseFilePane then FilePane.Close;
  560.       FilePane := nil;
  561.     end;
  562.   end;
  563. end;
  564.  
  565.  
  566. procedure TExplorer.OutlineMouseDown(Sender: TObject; Button: TMouseButton;
  567.   Shift: TShiftState; X, Y: Integer);
  568. var
  569.   i: Integer;
  570.   p: TPoint;
  571. begin
  572.   if Button = mbRight then with Outline do begin
  573.     PreventClick := True;
  574.     i := GetItem(X, Y);
  575.     if i > 0 then begin
  576.       SelectedItem := i;
  577.       GetCursorPos(p);
  578.       PopupMenu.Popup(p.X, p.Y);
  579.     end;
  580.   end;
  581. end;
  582.  
  583. procedure TExplorer.OutlineClick(Sender: TObject);
  584. begin
  585.   if FileWindow.Checked and
  586.     not (PreventClick or Walking or IsDialog) then OpenFolder.Click;
  587.   PreventClick := False;
  588. end;
  589.  
  590.  
  591. procedure TExplorer.FormDestroy(Sender: TObject);
  592. begin
  593.   if not IsDialog then begin
  594.     SavePosition(ini, 'Explorer');
  595.     ini.WriteBool('Explorer', 'FileWindow', FileWindow.Checked);
  596.     ini.WriteString('Explorer', 'LastFolder', SelectedFolder);
  597.     Explorer := nil;
  598.   end;
  599.  
  600.   DriveCaptions.Free;
  601.   BmpList.Free;
  602. end;
  603.  
  604.  
  605. procedure TExplorer.RefreshTreeClick(Sender: TObject);
  606. var
  607.   last : TFilename;
  608.   i: Longint;
  609. begin
  610.   last := SelectedFolder;
  611.   BuildTree;
  612.   if last > '' then Travel(last);
  613. end;
  614.  
  615.  
  616. procedure TExplorer.Travel(const folder: TFilename);
  617. var i: Longint;
  618. begin
  619.   Walking := True;
  620.   try
  621.     i := 0;
  622.     if (Length(folder) = 3) or HDirectoryExists(folder) then
  623.       i := FindDirectory(folder, True);
  624.   finally
  625.     Walking := False;
  626.   end;
  627.  
  628.   if i > 0 then begin
  629.     PreventClick := True;
  630.     Outline.SelectedItem := i;
  631.   end;
  632. end;
  633.  
  634.  
  635. procedure TExplorer.OutlineExpand(Sender: TObject; Index: Longint);
  636. var
  637.   node : TOutlineNode;
  638. begin
  639.   node := Outline.Items[Index];
  640.  
  641.     if not node.HasItems and
  642.        ((Longint(node.Data) and HasChildren > 0) or (node.Level = 2)) then begin
  643.        ExpandFolder(Index);
  644.        if not node.HasItems then node.Expanded := False;
  645.     end;
  646. end;
  647.  
  648.  
  649.  
  650. function TExplorer.FindDirectory(const Dir: string; ExpandPath : Boolean): Longint;
  651. var
  652.   start: Longint;
  653.   node : TOutlineNode;
  654.   this : string[12];
  655. begin
  656.   { FindDirectory locates an outline node by recursing until the top level
  657.     folder is extracted.  Then it unrolls, searching for directory names
  658.     as it returns, while expanding the nodes it passes through }
  659.  
  660.   if Length(Dir) = 3 then begin
  661.     Result := Outline.GetTextItem(Dir);
  662.     if (Result > 0) and ExpandPath then Outline.Items[Result].Expand;
  663.   end
  664.   else begin
  665.     Result := 0;
  666.     this := ExtractFilename(Dir);
  667.     if (this = '') or (Length(this) = Length(Dir)) then Exit;
  668.     start := FindDirectory(ExtractFileDir(Dir), ExpandPath);
  669.     if start > 0 then begin
  670.       node := Outline.Items[start];
  671.       Result := node.GetFirstChild;
  672.       while Result <> -1 do
  673.         if Outline.Items[Result].Text = this then begin
  674.           if ExpandPath then Outline.Items[Result].Expand;
  675.           Exit;
  676.         end
  677.         else Result := node.GetNextChild(Result);
  678.     end;
  679.   end;
  680. end;
  681.  
  682.  
  683.  
  684. procedure TExplorer.Walktree(Index: Longint);
  685. var
  686.   i: Longint;
  687.   p: TOutlineNode;
  688. begin
  689.   { Expands a branch of the tree beginning at Index.  This is not the
  690.     same as FullExpand because this expansion causes new nodes to be
  691.     added when directories are found }
  692.  
  693.   p := Outline.Items[Index];
  694.   p.Expand;
  695.   i := p.GetFirstChild;
  696.   while i <> -1 do begin
  697.     if Longint(Outline.Items[i].Data) and HasChildren > 0 then WalkTree(i);
  698.     i := p.GetNextChild(i);
  699.     if GetAsyncKeyState(VK_ESCAPE) < 0 then Abort;
  700.   end;
  701. end;
  702.  
  703.  
  704. const
  705.   MaskFlags: array[Boolean] of Word = (0, faHidden);
  706.  
  707. function HasSubDirectories(const Dirname: string): Boolean;
  708. var
  709.   rec : TSearchRec;
  710.   code : Integer;
  711. begin
  712.   code := FindFirst(Dirname + '\*.*', faDirectory or MaskFlags[ShowHidSys], rec);
  713.   while code = 0 do
  714.     if (rec.attr and faDirectory <> 0) and (rec.name[1] <> '.') then Break
  715.     else code := FindNext(rec);
  716.  
  717.   Result := code = 0;
  718. end;
  719.  
  720.  
  721. procedure TExplorer.ExpandFolder(Index: Longint);
  722. var
  723.   rec : TSearchRec;
  724.   path : TFilename;
  725.   last : Longint;
  726.   par, item : TOutlineNode;
  727.   code, i : Integer;
  728.   sortlist : TStringList;
  729. begin
  730.   last := -1;
  731.   par := Outline.Items[Index];
  732.   path := MakePath(ExtractNodeDir(par.FullPath));
  733.   sortlist := TUniqueStrings.Create;
  734.  
  735.   try
  736.  
  737.   code := FindFirst(path + '*.*', faDirectory or MaskFlags[ShowHidSys], rec);
  738.  
  739.   if code = -3 then
  740.     MsgDialogResFmt(SCannotOPenFolder, [MakeDirname(path)],
  741.       mtError, [mbOK], 0);
  742.  
  743.   while code = 0 do begin
  744.     if (rec.attr and faDirectory <> 0) and (rec.name[1] <> '.') then begin
  745.       rec.name := Lowercase(rec.name);
  746.       if HasSubDirectories(path + rec.name) then
  747.         sortlist.AddObject(rec.name, Pointer(HasChildren))
  748.       else
  749.         sortlist.Add(rec.name);
  750.     end;
  751.     if GetAsyncKeyState(VK_ESCAPE) < 0 then Break;
  752.     code := FindNext(rec);
  753.   end;
  754.  
  755.   with sortlist do
  756.     if Count > 0 then begin
  757.       for i := 0 to Count-1 do
  758.         last := Outline.AddChildObject(Index, Strings[i], Objects[i]);
  759.  
  760.       item := Outline.Items[last];
  761.       item.Data := Pointer(IsLastChild or Longint(item.Data));
  762.       par.Data := Pointer(HasChildren or Longint(par.Data));
  763.     end;
  764.  
  765.   finally
  766.     sortlist.Free;
  767.   end;
  768. end;
  769.  
  770.  
  771. type
  772.   TProtectedWin = class(TWinControl);
  773.  
  774. procedure OpenExplorer(const default : TFilename);
  775. var dest : TFilename;
  776. begin
  777.   ShowHourGlass;
  778.   if Explorer = nil then Explorer := TExplorer.Create(Application);
  779.   with Explorer do begin
  780.     dest := default;
  781.     if (dest = '') and ExploreLastFolder then begin
  782.       dest := ini.ReadString('Explorer', 'LastFolder', '');
  783.       if (dest > '') and (dfRemoveable in GetDriveFlags(dest[1])) then dest := '';
  784.     end;
  785.     Travel(dest);
  786.     ShowNormal;
  787.     if SelectedFolder > '' then TProtectedWin(Outline).Click;
  788.   end;
  789. end;
  790.  
  791.  
  792. procedure TExplorer.FileWindowClick(Sender: TObject);
  793. begin
  794.   FileWindow.Checked := not FileWindow.Checked;
  795. end;
  796.  
  797.  
  798. procedure TExplorer.FormHide(Sender: TObject);
  799. begin
  800.   if not IsDialog and ExplorerTask then Taskbar.DeleteButton(Handle);
  801. end;
  802.  
  803.  
  804. procedure TExplorer.FormShow(Sender: TObject);
  805. begin
  806.   if not IsDialog and ExplorerTask then Taskbar.AddButton(Handle);
  807. end;
  808.  
  809.  
  810. procedure TExplorer.FormPaint(Sender: TObject);
  811. begin
  812.   if not IsDialog then Border3D(Canvas, ClientWidth-1, ClientHeight-1);
  813. end;
  814.  
  815.  
  816. constructor TExplorer.CreateDialog(AOwner : TComponent);
  817. var
  818.   OKBtn, CancelBtn : TBitBtn;
  819. begin
  820.   IsDialog := True;
  821.   inherited Create(AOwner);
  822.   BorderStyle := bsDialog;
  823.   Position := poScreenCenter;
  824.   OpenNew.Enabled := False;
  825.   FileWindow.Enabled := False;
  826. end;
  827.  
  828.  
  829. function SelectFolder(const default: TFilename) : TFilename;
  830. begin
  831.   with TExplorer.CreateDialog(Application) do begin
  832.     Caption := LoadStr(SSelectFolder);
  833.     Travel(default);
  834.     try
  835.       Result := '';
  836.       if ShowModal = mrOK then Result := SelectedFolder
  837.       else Result := default;
  838.     finally
  839.       Free;
  840.     end;
  841.   end;
  842. end;
  843.  
  844.  
  845. procedure TExplorer.OutlineKeyDown(Sender: TObject; var Key: Word;
  846.   Shift: TShiftState);
  847. begin
  848.   if IsDialog and (Key = VK_ESCAPE) then ModalResult := mrCancel;
  849. end;
  850.  
  851.  
  852.  
  853. procedure TExplorer.OutlineDragOver(Sender, Source: TObject; X, Y: Integer;
  854.   State: TDragState; var Accept: Boolean);
  855. begin
  856.   Accept := (Source is TMultiGrid) and (Source <> Computer.Grid)
  857.     and (Outline.GetItemAt(X, Y) > 1);
  858.  
  859.   with Outline do
  860.     if not Accept or (State = dsDragLeave) then DropFocus := -1
  861.     else DropFocus := GetCellAt(X, Y);
  862. end;
  863.  
  864.  
  865. procedure TExplorer.OutlineDragDrop(Sender, Source: TObject; X,
  866.   Y: Integer);
  867. begin
  868.   with Outline do begin
  869.     DropFocus := -1;
  870.     FolderRef.Target := ExtractNodeDir(Items[GetItemAt(X, Y)].FullPath);
  871.   end;
  872.   FolderRef.DragDrop(Source);
  873. end;
  874.  
  875.  
  876. procedure TExplorer.SettingsChanged(Changes : TSettingChanges);
  877. begin
  878.   if [scSystem, scDisplay, scDesktop] * Changes <> [] then
  879.     Configure;
  880.   if scDevices in Changes then RefreshTree.Click;
  881. end;
  882.  
  883.  
  884. procedure TExplorer.OutlineMouseUp(Sender: TObject; Button: TMouseButton;
  885.   Shift: TShiftState; X, Y: Integer);
  886. var
  887.   junction : Integer;
  888.   item : Longint;
  889.   node : TOutlineNode;
  890. begin
  891.   if (Button = mbLeft) and not (ssDouble in Shift) then with Outline do begin
  892.     { Test if the user clicked on + or - box }
  893.     item := GetItemAt(X, Y);
  894.     if item > 0 then begin
  895.       node := Items[item];
  896.       if Longint(node.Data) and HasChildren > 0 then begin
  897.         junction := (node.Level-1) * 20 - 12;
  898.         if (X > junction - 6) and (X < junction + 6) then 
  899.           with node do Expanded := not Expanded;
  900.       end;
  901.     end;
  902.   end
  903. end;
  904.  
  905. procedure TExplorer.WMWindowPosChanged(var Msg : TWMWindowPosChanged);
  906. begin
  907.   inherited;
  908.   AlignFilePane;
  909. end;
  910.  
  911.  
  912. procedure TExplorer.PopupMenuPopup(Sender: TObject);
  913. begin
  914.   {Del.Enabled := not IsDialog and (Length(SelectedFolder) > 3);}
  915. end;
  916.  
  917. procedure TExplorer.DelClick(Sender: TObject);
  918. var s: TFilename;
  919. begin
  920. (*
  921.   Disabled due to instability
  922.  
  923.   s := SelectedFolder;
  924.   if (Length(s) > 3) and not (ConfirmDelStart and
  925.     (MsgDialogResFmt(SQueryDeleteItems, [1, '', ExtractFileDir(s)],
  926.       mtConfirmation, [mbYes, mbNo], 0) <> mrYes)) then begin
  927.     Desktop.CloseSubWindows(s);
  928.     if DeleteDirectory(s) then Outline.DeleteSelectedNode;
  929.   end;
  930. *)
  931. end;
  932.  
  933. end.
  934.