home *** CD-ROM | disk | FTP | other *** search
/ PC Open 19 / pcopen19.iso / Win31 / Calmira / SOURCE.ZIP / SRC / DESK.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1998-02-20  |  20.1 KB  |  671 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 Desk;
  24.  
  25. { TDesktop
  26.  
  27.   TDesktop manages forms on the screen, and is an extension to Delphi's
  28.   TScreen component.
  29.  
  30.   Fields
  31.  
  32.   FormList - temporary list to hold forms during processing
  33.   WindowList - always contains a list of open icon windows
  34.   WindowMenu - a popup menu that mirrors WindowList
  35.   RefreshList - a list of folders that have had their contents
  36.     changed and need refreshing once an operation has finished
  37.   CursorStack - holds the previous TCursor values
  38.  
  39.   Methods
  40.  
  41.   Load - Loads the desktop from the INI file
  42.   Save - Saves the desktop to the INI file
  43.   Refresh - refreshes the given folder if it is on screen
  44.   RefreshNow - refreshes all windows in refresh list, then clears the list
  45.   WindowOf - returns the icon window displaying the given folder,
  46.     or nil if no such window exists
  47.   OpenFolder - opens an icon window of the given folder, or brings
  48.     an existing window to the front
  49.   CloseSubWindows - closes all windows which show the given directory
  50.     and all its subdirectories
  51.   CloseLowerWindows - closes all windows which show subdirectories
  52.     of the given directory
  53.   ClosePathWindows - closes all windows showing parent directories
  54.     of the given directory
  55.   CloseOtherWindows - closes all windows except the one passed as parameter
  56.   Cascade - cascades icon windows from the top left
  57.   CloseWindows - closes all icon windows
  58.   ArrangeIcons - mimics "Arrange Icons" from the Windows Task Manager
  59.     except that shortcuts etc. are not moved.
  60.   SnapToGrid - repositions icons so that they line up with an
  61.     invisible grid
  62.   RenameWindows - calls the FolderRenamed method for each icon window
  63.   AddWindow - adds an entry to the window list and a new menu item
  64.   RemoveWindow - reverses effects of AddWindow
  65.   WindowSelect - the event handler for menu items, which brings a
  66.     window to the front.
  67.   EnableForms - changes the Enabled property of all forms on screen
  68.     except those needed to interact with the user during a file
  69.     operation.  Simulates modal file operations.
  70.   Revert - reloads the minimized positions of extended forms (TExtForm).
  71.   NextForm - brings the bottom form to the front, typically when the
  72.     user presses Ctrl-Tab
  73.   SetCursor - saves the current cursor to the cursor stack and
  74.     changes the screen's cursor.
  75.   ReleaseCursor - Restores the previously displayed screen cursor
  76. }
  77.  
  78.  
  79. interface
  80.  
  81. uses Classes, IconWin, SysUtils, Graphics, FileCtrl, Forms, WinTypes,
  82.   Menus, Controls;
  83.  
  84. type
  85.   TDesktop = class(TComponent)
  86.   private
  87.     FormList: TList;
  88.     CursorStack : TList;
  89.     WindowList : TStringList;
  90.     CascadeDelta : Integer;
  91.     function Each(FormClass: TFormClass): TList;
  92.   public
  93.     WindowMenu : TPopupMenu;
  94.     RefreshList : TStringList;
  95.     constructor Create(AOwner: TComponent); override;
  96.     destructor Destroy; override;
  97.     procedure Load;
  98.     procedure Save;
  99.     procedure Refresh(const foldername: TFilename);
  100.     procedure RefreshNow;
  101.     procedure UpdateFileWindow(const filename: TFilename);
  102.     function WindowOf(const foldername : TFilename): TIconWindow;
  103.     procedure OpenFolder(foldername: TFilename);
  104.     procedure OpenFolderRefresh(const foldername: TFilename);
  105.     procedure CloseSubWindows(const foldername: TFilename);
  106.     procedure CloseLowerWindows(const foldername: TFilename);
  107.     procedure ClosePathWindows(const foldername: TFilename);
  108.     procedure CloseOtherWindows(Sender : TIconWindow);
  109.     procedure Cascade;
  110.     procedure CloseWindows;
  111.     procedure ArrangeIcons;
  112.     procedure SnapToGrid;
  113.     procedure RenameWindows(const previous, current: TFilename);
  114.     procedure AddWindow(Win : TIconWindow);
  115.     procedure RemoveWindow(Win : TIconWindow);
  116.     procedure WindowSelect(Sender: TObject);
  117.     procedure EnableForms(Enable : Boolean);
  118.     procedure Revert;
  119.     procedure NextForm;
  120.     procedure SetCursor(Cursor : TCursor);
  121.     procedure ReleaseCursor;
  122.   end;
  123.  
  124.  
  125. var Desktop : TDesktop;
  126.  
  127. implementation
  128.  
  129. uses Directry, WinProcs, Shorts, WasteBin, CompSys, Settings, Resource,
  130.   Strings, FileFind, Files, MiscUtil, Drives, Tree, Progress,
  131.   Replace, CalForm, Start, ExtForm, FileMan, Dialogs;
  132.  
  133.  
  134. constructor TDesktop.Create(AOwner: TComponent);
  135. begin
  136.   inherited Create(AOwner);
  137.   FormList := TList.Create;
  138.   CursorStack := TList.Create;
  139.   RefreshList := TUniqueStrings.Create;
  140.   WindowList := TUniqueStrings.Create;
  141.   WindowMenu := TPopupMenu.Create(self);
  142.   CascadeDelta := GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXSIZE);
  143. end;
  144.  
  145.  
  146. destructor TDesktop.Destroy;
  147. var i: Integer;
  148. begin
  149.   FormList.Free;
  150.   CursorStack.Free;
  151.   RefreshList.Free;
  152.   with WindowList do
  153.     for i := 0 to Count-1 do Objects[i].Free;
  154.   WindowList.Free;
  155.   ArrangeIconicWindows(GetDesktopWindow);
  156.   inherited Destroy;
  157. end;
  158.  
  159.  
  160. { The Each method is useful for finding all the forms of a particular
  161.   type.  It copies them to FormList and returns this list -- this is
  162.   important because we can't reliably close forms while iterating
  163.   through the Screen.Forms property, even when going backwards }
  164.  
  165. function TDesktop.Each(FormClass: TFormClass): TList;
  166. var i: Integer;
  167. begin
  168.   FormList.Clear;
  169.   with Screen do
  170.     for i := 0 to FormCount-1 do
  171.       if Forms[i] is FormClass then FormList.Add(Forms[i]);
  172.   Result := FormList;
  173. end;
  174.  
  175.  
  176. { OpenFolder encapsulates the process of displaying a new icon window.
  177.   If the window already exists, it is brought forward.  When the user
  178.   chooses to use a single window for all browsing, then any existing
  179.   window is forced to change its directory path.
  180.  
  181.   While it might seem useful to return the window object, TIconWindow
  182.   could raise an exception and destroy itself if an invalid path is
  183.   specified AND discard the exception, leaving the caller with an
  184.   invalid pointer.  So it is safer to not return anything. }
  185.  
  186. procedure TDesktop.OpenFolder(foldername: TFilename);
  187. var
  188.   IconWindow : TIconWindow;
  189.   i : Integer;
  190. begin
  191.   foldername := Lowercase(Trim(foldername));
  192.   IconWindow := WindowOf(foldername);
  193.  
  194.   if IconWindow = nil then begin
  195.     if BrowseSame xor (GetAsyncKeyState(VK_MENU) < 0) then
  196.       with Screen do
  197.       for i := 0 to FormCount-1 do
  198.         if Forms[i] is TIconWindow then with TIconWindow(Forms[i]) do begin
  199.           ChangeDir(foldername);
  200.           SetFocus;
  201.           Exit;
  202.         end;
  203.  
  204.     TIconWindow.Init(Application, foldername, DefaultFilter).Show;
  205.   end
  206.   else IconWindow.ShowNormal;
  207. end;
  208.  
  209.  
  210. procedure TDesktop.OpenFolderRefresh(const foldername: TFilename);
  211. var
  212.   w: TIconWindow;
  213. begin
  214.   w := Desktop.WindowOf(foldername);
  215.   if w <> nil then with w do begin
  216.     RefreshWin;
  217.     ShowNormal;
  218.     Exit;
  219.   end;
  220.   Desktop.OpenFolder(foldername)
  221. end;
  222.  
  223. function TDesktop.WindowOf(const foldername : TFilename): TIconWindow;
  224. var i : Integer;
  225. begin
  226.   i := WindowList.IndexOf(foldername);
  227.   if i <> -1 then Result := TIconWindow(WindowList.Objects[i])
  228.   else Result := nil;
  229. end;
  230.  
  231.  
  232. { A "subwindow" is thought of like a "subset", i.e., the window itself
  233.   or any windows showing subdirectories }
  234.  
  235. procedure TDesktop.CloseSubWindows(const foldername: TFilename);
  236. var f: TIconWindow;
  237. begin
  238.   f := WindowOf(foldername);
  239.   if f <> nil then f.Close;
  240.   CloseLowerWindows(foldername);
  241. end;
  242.  
  243.  
  244. procedure TDesktop.CloseLowerWindows(const foldername: TFilename);
  245. var i: Integer;
  246. begin
  247.   with Each(TIconWindow) do
  248.     for i := 0 to Count-1 do
  249.       if IsAncestorDir(foldername, TIconWindow(Items[i]).Dir.Fullname) then
  250.         TIconWindow(Items[i]).Close;
  251. end;
  252.  
  253.  
  254. procedure TDesktop.ClosePathWindows(const foldername: TFilename);
  255. var i: Integer;
  256. begin
  257.   with Each(TIconWindow) do
  258.     for i := 0 to Count-1 do
  259.       if IsAncestorDir(TIconWindow(Items[i]).Dir.Fullname, foldername) then
  260.         TIconWindow(Items[i]).Close;
  261. end;
  262.  
  263.  
  264. procedure TDesktop.Refresh(const foldername: TFilename);
  265. var f: TIconWindow;
  266. begin
  267.   f := WindowOf(Foldername);
  268.   if f <> nil then f.RefreshWin;
  269. end;
  270.  
  271. { TScreen is organised such that the topmost form has the lowest index.
  272.   To cascade using the current Z-order, the loop must go backwards to
  273.   prevent exposing windows underneath (they take a long time to redraw).
  274.   Try a forward loop and see! }
  275.  
  276. procedure TDesktop.Cascade;
  277. var
  278.   i, tl: Integer;
  279.   size : TPoint;
  280. begin
  281.   tl := 0;
  282.   size := TIconWindow.CalcSize(5, 4);
  283.   with Each(TIconWindow) do
  284.     for i := Count-1 downto 0 do
  285.       with TIconWindow(Items[i]) do
  286.         if WindowState <> wsMinimized then begin
  287.           SetBounds(tl, tl, size.x, size.y);
  288.           Inc(tl, CascadeDelta);
  289.           if tl + size.x > Screen.Width then tl := 0;
  290.         end;
  291. end;
  292.  
  293.  
  294. {
  295. procedure TDesktop.CascadeTasks;
  296. var
  297.   i, tl, delta: Integer;
  298.   R : TRect;
  299.   Wnd : HWnd;
  300. begin
  301.   tl := 0;
  302.   with Taskbar.ButtonList do
  303.     for i := 0 to Count-1 do with Buttons[i] do
  304.       if WindowType = wtGeneral then begin
  305.         Wnd := Window;
  306.         if IsIconic(Wnd) or IsZoomed(Wnd) then Continue;
  307.         GetWindowRect(@R);
  308.         if (R.Left = R.Right) or (R.Top = R.Bottom) then Continue;
  309.         MoveWindow(Wnd, tl, tl, R.Right - R.Left, R.TOp - R.Bottom)
  310.         Inc(tl, delta);
  311.         if tl + size.x > Screen.Width then tl := 0;
  312.       end;
  313. end;
  314. }
  315.  
  316.  
  317. procedure TDesktop.CloseWindows;
  318. var i: Integer;
  319. begin
  320.   with Each(TIconWindow) do
  321.     for i := 0 to Count-1 do TIconWindow(Items[i]).Close;
  322. end;
  323.  
  324.  
  325. procedure TDesktop.CloseOtherWindows(Sender : TIconWindow);
  326. var i: Integer;
  327. begin
  328.   with Each(TIconWindow) do
  329.     for i := 0 to Count-1 do
  330.       if Items[i] <> Sender then TIconWindow(Items[i]).Close;
  331. end;
  332.  
  333.  
  334. function EnumMinWindows(Wnd: HWnd; List: TList): Bool; export;
  335. begin
  336.   if IsWindowVisible(Wnd) and
  337.     (GetWindowLong(Wnd, GWL_STYLE) and WS_MINIMIZEBOX > 0) then
  338.     List.Add(Pointer(Wnd));
  339.   Result := True;
  340. end;
  341.  
  342.  
  343.  
  344. { Firstly, this procedure calculates the dimensions of the icon grid, and
  345.   where to put the bottom row (depending on whether the taskbar is showing).
  346.   For each window, it checks that it doesn't belong to a fixed object.
  347.   Then it slots the icon into the right place, and calculates the position
  348.   of the next icon.
  349.  
  350.   Icons with a Y coordinate of Screen.Height are usually the ones hidden by
  351.   the taskbar, so they are left alone.  (-1, -1) tells Windows to find
  352.   a position when the form is next minimized }
  353.  
  354. procedure TDesktop.ArrangeIcons;
  355. var
  356.   list : TList;
  357.   NextPos : TPoint;
  358.   Spacing, FarLeft, i: Integer;
  359.   Wnd : HWND;
  360.   control : TWinControl;
  361. begin
  362.   Spacing := GetSystemMetrics(SM_CXICONSPACING);
  363.   FarLeft := (Spacing - 32) div 2;
  364.   NextPos.X := FarLeft;
  365.   NextPos.Y := Screen.Height;
  366.   Dec(NextPos.Y, 30 + MinAppHeight);
  367.  
  368.   list := TList.Create;
  369.   try
  370.     EnumWindows(@EnumMinWindows, Longint(list));
  371.     for i := 0 to list.Count-1 do begin
  372.       Wnd := Longint(list[i]);
  373.       control := FindControl(wnd);
  374.  
  375.       if (control is TShort) or (control = Computer) or (control = Bin) then
  376.         Continue;
  377.  
  378.       if GetMinPosition(wnd).y < Screen.Height then
  379.         if not IsIconic(Wnd) then
  380.           MoveDesktopIcon(wnd, Point(-1, -1))
  381.         else begin
  382.           MoveDesktopIcon(wnd, NextPos);
  383.           Inc(NextPos.X, spacing);
  384.           if NextPos.X > Screen.Width then begin
  385.             Dec(NextPos.Y, Spacing);
  386.             NextPos.X := FarLeft;
  387.           end
  388.         end;
  389.     end;
  390.   finally
  391.     list.Free;
  392.   end;
  393. end;
  394.  
  395.  
  396. { SnapToGrid uses a bit of modulo maths to determine the closest square.
  397.   The Snap function is given a coordinate and a grid size, and returns
  398.   where the coordinate should snap to. }
  399.  
  400. procedure TDesktop.SnapToGrid;
  401. var
  402.   list : TList;
  403.   i: Integer;
  404.   Wnd : HWND;
  405.   MinPos : TPoint;
  406.  
  407. function Nearest(value, lower, upper: Integer): Integer;
  408. begin
  409.   if value - lower < upper - value then Result := lower
  410.   else Result := upper;
  411. end;
  412.  
  413. function Snap(p, grid: Integer): Integer;
  414. begin
  415.   Result := p;
  416.   if p mod grid <> 0 then
  417.     Result := Nearest(p, p - (p mod grid), p + grid - (p mod grid));
  418. end;
  419.  
  420. begin
  421.   list := TList.Create;
  422.   try
  423.     EnumWindows(@EnumMinWindows, Longint(list));
  424.     for i := 0 to list.Count-1 do begin
  425.       Wnd := Longint(list[i]);
  426.       MinPos := GetMinPosition(wnd);
  427.       with MinPos do
  428.       if (x > -1) and (y > -1) and (y < Screen.Height) then begin
  429.         x := Snap(x, DeskGrid.x);
  430.         y := Snap(y, DeskGrid.y);
  431.         MoveDesktopIcon(wnd, MinPos);
  432.       end;
  433.     end;
  434.   finally
  435.     list.Free;
  436.   end;
  437. end;
  438.  
  439.  
  440.  
  441. procedure TDesktop.RefreshNow;
  442. var i: Integer;
  443. begin
  444.   if RefreshList.Count = 0 then Exit;
  445.  
  446.   with Each(TIconWindow) do begin
  447.     for i := 0 to Count-1 do
  448.       if RefreshList.IndexOf(TIconWindow(Items[i]).Dir.Fullname) <> -1 then
  449.         TIconWindow(Items[i]).RefreshWin;
  450.   end;
  451.   RefreshList.Clear;
  452. end;
  453.  
  454.  
  455. procedure TDesktop.RenameWindows(const previous, current: TFilename);
  456. var i: Integer;
  457. begin
  458.   with Each(TIconWindow) do
  459.     for i := 0 to Count-1 do
  460.       TIconWindow(Items[i]).FolderRenamed(previous, current);
  461. end;
  462.  
  463.  
  464. { Just as TForm informs TScreen when it is created or destroyed, so
  465.   TIconWindow informs TDesktop.  All icon windows are stored in a
  466.   sorted string list, and the sort ordering is useful when maintaining
  467.   a popup menu. }
  468.  
  469. procedure TDesktop.AddWindow(Win : TIconWindow);
  470. var m: TMenuItem;
  471. begin
  472.   m := TMenuItem.Create(self);
  473.   m.Caption := Win.Dir.Fullname;
  474.   m.OnClick := WindowSelect;
  475.   WindowMenu.Items.Insert(WindowList.AddObject(m.Caption, Win), m);
  476. end;
  477.  
  478. procedure TDesktop.RemoveWindow(Win : TIconWindow);
  479. var i: Integer;
  480. begin
  481.   with WindowList do begin
  482.     i := IndexOfObject(Win);
  483.     if i <> -1 then begin
  484.       WindowMenu.Items[i].Free;
  485.       Delete(i);
  486.     end;
  487.   end;
  488. end;
  489.  
  490.  
  491. { This is the OnClick handler for menu items showing current open windows }
  492.  
  493. procedure TDesktop.WindowSelect(Sender: TObject);
  494. begin
  495.   OpenFolder((Sender as TMenuItem).Caption);
  496. end;
  497.  
  498.  
  499. { EnableForms takes the place of EnableTaskWindows.  All forms are disabled
  500.   or enabled except the ones which can be active during a file operation.
  501.   This gives the appearance of a modal state. }
  502.  
  503. procedure TDesktop.EnableForms(Enable : Boolean);
  504. var
  505.   i: Integer;
  506.   f: TForm;
  507. begin
  508.   with Screen do
  509.     for i := 0 to FormCount-1 do begin
  510.       f := Forms[i];
  511.       if (f <> ProgressBox) and (f <> ReplaceBox) then
  512.         f.Enabled := Enable;
  513.     end;
  514. end;
  515.  
  516.  
  517. { The desktop is responsible for loading shortcuts and previously
  518.   opened icon windows.  To prevent errors from slowing down the loading,
  519.   only folders which exist on fixed drives are processed }
  520.  
  521. procedure TDesktop.Load;
  522. var
  523.   i : Integer;
  524.   s: TShort;
  525.   strings : TStringList;
  526.   IconWindow : TIconWindow;
  527.   fname : TFilename;
  528. begin
  529.   for i := 0 to ini.ReadInteger('Desktop', 'NumShorts', 0)-1 do begin
  530.     s := TShort.Create(Application);
  531.     s.LoadFromIni(ini, 'Shortcut' + IntToStr(i));
  532.   end;
  533.  
  534.   strings := TStringList.Create;
  535.   try
  536.     ini.ReadStrings('Folders', strings);
  537.     for i := 0 to strings.Count-1 do begin
  538.       fname := strings[i];
  539.       if Desktop.WindowOf(fname) <> nil then Continue;
  540.       if not (dfRemoveable in GetDriveFlags(fname[1])) and
  541.         ((Length(fname) = 3) or HDirectoryExists(fname)) then begin
  542.         IconWindow := TIconWindow.Init(Application, fname, DefaultFilter);
  543.         with IconWindow do begin
  544.           LoadDimensions;
  545.           Show;
  546.           Update;
  547.         end;
  548.       end;
  549.     end;
  550.   finally
  551.     strings.Free;
  552.   end;
  553. end;
  554.  
  555.  
  556.  
  557. procedure TDesktop.Save;
  558. var
  559.   i: Integer;
  560. begin
  561.   SetCursor(crHourGlass);
  562.   with ini do begin
  563.     for i := 1 to ReadInteger('Desktop', 'NumShorts', 0) do
  564.       EraseSection('Shortcut' + IntToStr(i));
  565.  
  566.     with Each(TShort) do begin
  567.       for i := 0 to Count-1 do
  568.         TShort(Items[i]).SaveToIni(ini, 'Shortcut' + IntToStr(i));
  569.       WriteInteger('Desktop', 'NumShorts', Count);
  570.     end;
  571.  
  572.     EraseSection('Folders');
  573.     if SaveWindows then
  574.       with Each(TIconWindow) do begin
  575.         WriteInteger('Folders', 'Count', Count);
  576.         for i := 0 to Count-1 do begin
  577.           TIconWindow(Items[i]).SaveDimensions;
  578.           WriteString('Folders', 'S' + IntToStr(i),
  579.             TIconWindow(Items[i]).Dir.Fullname);
  580.         end;
  581.       end;
  582.  
  583.   end;
  584.  
  585.   Computer.SavePosition(ini, 'Computer');
  586.   Bin.SavePosition(ini, 'Bin');
  587.   Bin.SaveTrash;
  588.   ini.WriteSectionValues('Window positions', WindowPos);
  589.   ReleaseCursor;
  590. end;
  591.  
  592.  
  593. { This is useful if the user accidentally presses Arrange Icons from the
  594.   Windows task manager (which also arranges shortcuts!).  Since TExtForm
  595.   saves its last icon position, it can be told to move itself back.  }
  596.  
  597. procedure TDesktop.Revert;
  598. var
  599.   i: Integer;
  600. begin
  601.   with Each(TExtForm) do
  602.     for i := 0 to Count-1 do
  603.       with TExtForm(Items[i]) do MinPosition := LastMinPosition;
  604. end;
  605.  
  606.  
  607. { NextForm is called when the user presses Ctrl-Tab.  When a form is
  608.   brought to the front, it sticks itself at the top of Screen.Forms.
  609.   This makes it difficult to select the next form in Z-order (you end
  610.   up flipping between two forms!), so we must bring forward the form
  611.   at the very bottom of the pack }
  612.  
  613. procedure TDesktop.NextForm;
  614. var
  615.   f: TForm;
  616.   i: Integer;
  617. begin
  618.   with Screen do
  619.     for i := FormCount-1 downto 0 do begin
  620.       f := Forms[i];
  621.       if f.Visible and IsWindowEnabled(f.Handle) and (f <> Screen.ActiveForm) and
  622.        (f <> Application.MainForm) and not (f is TShort) then begin
  623.         f.BringToFront;
  624.         f.WindowState := wsNormal;
  625.         Exit;
  626.       end;
  627.     end;
  628. end;
  629.  
  630. { SetCursor and ReleaseCursor are extremely useful to prevent the wrong
  631.   cursor from being displayed after a try...finally block.  If a "busy"
  632.   operation sets the hourglass cursor and calls another busy function,
  633.   the second function would reset the cursor to crDefault after it
  634.   finished.  Of course, the first operation might still be busy, so a
  635.   stack based approach is needed to maintain the right cursor. }
  636.  
  637. procedure TDesktop.SetCursor(Cursor : TCursor);
  638. begin
  639.   CursorStack.Add(Pointer(Screen.Cursor));
  640.   Screen.Cursor := Cursor;
  641. end;
  642.  
  643. procedure TDesktop.ReleaseCursor;
  644. begin
  645.   with CursorStack do
  646.     if Count > 0 then begin
  647.       Screen.Cursor := TCursor(Items[Count-1]);
  648.       Delete(Count-1);
  649.     end;
  650. end;
  651.  
  652. procedure TDesktop.UpdateFileWindow(const filename: TFilename);
  653. var
  654.   w : TIconWindow;
  655.   rec : TSearchRec;
  656.   i : Integer;
  657. begin
  658.   w := Desktop.WindowOf(ExtractFileDir(filename));
  659.   if w <> nil then with w do begin
  660.     if FindFirst(filename, faAnyFile, rec) = 0 then
  661.     with Dir do begin
  662.       if Find(ExtractFilename(filename), i) then FreeObject(i);
  663.       AddItem(rec);
  664.       Update;
  665.     end;
  666.   end;
  667. end;
  668.  
  669.  
  670. end.
  671.