home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1998 April A / Pcwk4a98.iso / PROGRAM / DELPHI16 / Calmira / Src / SRC / START.PAS < prev    next >
Pascal/Delphi Source File  |  1997-02-15  |  20KB  |  703 lines

  1. {**************************************************************************}
  2. {                                                                          }
  3. {    Calmira shell for Microsoft« Windows(TM) 3.1                          }
  4. {    Source Release 1.0                                                    }
  5. {    Copyright (C) 1997  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 Start;
  24.  
  25. { Start Menu
  26.  
  27.   TStartMenu is a popup menu that is owner-drawn, so that it has a 3D
  28.   effect and small icons.  The small icons are stored in one large bitmap
  29.   to conserve memory.  They are ordered in tree-traversal order, so when
  30.   the start menu next loads, all the small icons are in the right places.
  31.  
  32.   TStartMenuItem is the class of menu item that is used for the start
  33.   menu.  It has a Data property which holds a string containing
  34.   encoded information about the item.  This data can be expanded with
  35.   the ExtractStartInfo function.
  36. }
  37.  
  38. interface
  39.  
  40. uses Classes, SysUtils, Menus, Outline, Messages, WinTypes, Graphics, Forms;
  41.  
  42. type
  43.   TStartFlags = (sfTop, sfBottom, sfSeparator);
  44.  
  45.   TStartMenuItem = class(TMenuItem)
  46.   private
  47.     FData : PString;
  48.     FImgOffset : Word;
  49.     function GetData: string;
  50.     procedure SetData(const Value: string);
  51.   public
  52.     Flags : set of TStartFlags;
  53.     constructor Create(AOwner: TComponent); override;
  54.     destructor Destroy; override;
  55.     procedure Click; override;
  56.     procedure PutBitmap;
  57.     property Data : string read GetData write SetData;
  58.     property ImgOffset : Word read FImgOffset;
  59.   end;
  60.  
  61.   TStartMacroEvent = procedure (Sender : TObject; const macro, params : string)
  62.     of object;
  63.  
  64.   TStartMenu = class(TPopupMenu)
  65.   private
  66.     Canvas : TCanvas;
  67.     Window: HWND;
  68.     FItemHeight : Integer;
  69.     FOnStartMacro : TStartMacroEvent;
  70.     procedure WndProc(var Message: TMessage);
  71.     procedure PaintMenu(DC: HDC; const Rect : TRect; state : Word;
  72.       item : TStartMenuItem);
  73.     function GetHeight : Integer;
  74.     procedure SetOwnerDraw(menu : TMenuItem);
  75.   public
  76.     constructor Create(AOwner: TComponent); override;
  77.     destructor Destroy; override;
  78.     procedure Configure;
  79.     procedure Popup(X, Y: Integer; TrackLeft : Boolean);
  80.     procedure Clear;
  81.     function Find(const cap : string; submenu: Boolean): TMenuItem;
  82.     procedure Load;
  83.     procedure RunStartup;
  84.     procedure RebuildFromOutline(Outline : TOutline);
  85.     procedure AssignToOutline(Outline : TOutline);
  86.     procedure HandleClick(Sender : TObject);
  87.     property OnStartMacro : TStartMacroEvent read FOnStartMacro write FOnStartMacro;
  88.     property Height : Integer read GetHeight;
  89.   end;
  90.  
  91.   TStartImages = class(TBitmap)
  92.   private
  93.     FNext : Integer;
  94.   public
  95.     function Add(bmp : TBitmap): Integer;
  96.     property Next: Integer read FNext write FNext;
  97.   end;
  98.  
  99.   { TStartInfo is only slightly larger than a 255 char string, so
  100.     placing it on the stack is OK, provided that there is no recursion  }
  101.  
  102.   TStartInfo = record
  103.     Command: TFilename;
  104.     Directory : TFilename;
  105.     ShowMode : Integer;
  106.     IconFile : TFilename;
  107.     IconIndex : Integer;
  108.   end;
  109.  
  110. function ExtractStartInfo(const s: string): TStartInfo;
  111. function PackStartInfo(const command, directory, iconfile: TFilename;
  112.   showmode, iconindex: Integer): string;
  113.  
  114. var StartMenu : TStartMenu;
  115.  
  116. implementation
  117.  
  118. uses Strings, IniFiles, Desk, Files, Directry, Dialogs, FileMan, Environs,
  119.   Controls, MiscUtil, WinProcs, Sys, Referenc, Settings, Resource;
  120.  
  121. { TStartMenu and its items need to share some graphics objects, so they
  122.   are global rather than parameters }
  123.  
  124. var
  125.   Images : TStartImages;
  126.   TempIcon : TIcon;
  127.   SmallBmp : TBitmap;
  128.   UsingCache : Boolean;
  129.  
  130.  
  131. function TStartImages.Add(bmp : TBitmap): Integer;
  132. begin
  133.   if FNext + 16 > Width then Width := Width + (64 * 16);
  134.   Result := FNext;
  135.   Canvas.Draw(FNext, 0, bmp);
  136.   Inc(FNext, 16);
  137. end;
  138.  
  139.  
  140.  
  141. { TStartMenuItem }
  142.  
  143. constructor TStartMenuItem.Create(AOwner: TComponent);
  144. begin
  145.   inherited Create(AOwner);
  146.   FData := NullStr;
  147. end;
  148.  
  149. destructor TStartMenuItem.Destroy;
  150. begin
  151.   DisposeStr(FData);
  152.   inherited Destroy;
  153. end;
  154.  
  155. procedure TStartMenuItem.Click;
  156. begin
  157.   if not (sfSeparator in Flags) then inherited Click;
  158. end;
  159.  
  160. function TStartMenuItem.GetData: string;
  161. begin
  162.   Result := FData^;
  163. end;
  164.  
  165. procedure TStartMenuItem.SetData(const Value: string);
  166. begin
  167.   if FData^ <> Value then AssignStr(FData, Value);
  168. end;
  169.  
  170.  
  171. const
  172.   CommandTable : array[0..4] of string[11] =
  173.   ('$FOLDER', '$FIND', '$RUN', '$EXPLORE', '$SHUTDOWN');
  174.  
  175. procedure TStartMenuItem.PutBitmap;
  176. var
  177.   Command, IconFile: TFilename;
  178.   src, dest : THandle;
  179.   i, j, IconIndex: Integer;
  180.   c: TColor;
  181. begin
  182.   Command := '';
  183.   IconFile := '';
  184.   IconIndex := 0;
  185.   Unformat(Data, '%s;%S;%D;%s;%d', [@Command, 79, @IconFile, 79, @IconIndex]);
  186.  
  187.   i := Pos(' ', Command);
  188.   if i > 0 then Command[0] := Chr(i-1);
  189.  
  190.   if (Count > 0) and (IconFile = '') then begin
  191.     { default group bitmap }
  192.     FImgOffset := 16;
  193.     Exit;
  194.   end;
  195.  
  196.   if (Command[1] = '$') and (IconFile = '') then begin
  197.     { Find an auxiliary image }
  198.     i := 0;
  199.     while i <= High(CommandTable) do
  200.       if CompareText(Command, CommandTable[i]) = 0 then System.Break else Inc(i);
  201.     FImgOffset := 32 + i * 16;
  202.     Exit;
  203.   end;
  204.  
  205.   if UsingCache then begin
  206.     { When this function is called, Images.Next points to where the
  207.       small icon image should be placed }
  208.     FImgOffset := Images.Next;
  209.     Images.Next := Images.Next + 16;
  210.     Exit;
  211.   end;
  212.  
  213.   if CompareText(ExtractFileExt(IconFile), '.bmp') = 0 then
  214.     SmallBmp.LoadFromFile(EnvironSubst(IconFile))
  215.   else begin
  216.     { Use a reference object to do the icon searching }
  217.     FileRef.Target := Lowercase(command);
  218.     FileRef.IconFile := IconFile;
  219.     FileRef.IconIndex := IconIndex;
  220.     FileRef.AssignIcon(TempIcon);
  221.     ShrinkIcon(TempIcon.Handle, SmallBmp);
  222.   end;
  223.  
  224.   { Add the new 16 x 16 image to the list and remember where you put it }
  225.   FImgOffset := Images.Add(SmallBmp);
  226. end;
  227.  
  228.  
  229. { TStartMenu }
  230.  
  231. constructor TStartMenu.Create(AOwner: TComponent);
  232. begin
  233.   inherited Create(AOwner);
  234.   Window := AllocateHWnd(WndProc);
  235.   Canvas := TCanvas.Create;
  236.   Canvas.Brush.Color := clSilver;
  237.  
  238.   { A convenient place to create global objects! }
  239.   Images := TStartImages.Create;
  240.   TempIcon := TIcon.Create;
  241.   SmallBmp := InitBitmap(TBitmap.Create, 16, 16, clSilver);
  242.  
  243.   Configure;
  244. end;
  245.  
  246.  
  247. destructor TStartMenu.Destroy;
  248. begin
  249.   Canvas.Free;
  250.   Images.Free;
  251.   TempIcon.Free;
  252.   SmallBmp.Free;
  253.   DeallocateHWnd(Window);
  254.   inherited Destroy;
  255. end;
  256.  
  257.  
  258. procedure TStartMenu.Configure;
  259. const
  260.   FontStyles : array[Boolean] of TFontStyles = ([], [fsBold]);
  261.   DDEService : array[Boolean] of string[7] = ('CALMIRA', 'PROGMAN');
  262. begin
  263.   FillMenu := BoldSelect or (ColorToRGB(clMenu) <> clSilver);
  264.   ini.ReadFont('Start menu', Canvas.Font);
  265.  
  266.   { When Windows sends WM_MEASUREITEM messages, the start menu has
  267.     no valid canvas to measure the text widths.  So it utilises
  268.     the canvas from a bitmap by setting its font and, later,
  269.     calling its TextWidth method }
  270.  
  271.   with Images.Canvas.Font do begin
  272.     Assign(Canvas.Font);
  273.     Style := FontStyles[BoldSelect];
  274.   end;
  275.  
  276.   { When BoldSelect is on, menu items need to be wider to accomodate
  277.     the font.  So when BoldSelect changes, the start menu must be
  278.     "invalidated" so that Windows sends more WM_MEASUREITEM messages
  279.     to find the new widths }
  280.  
  281.   if StartMenu3D then SetOwnerDraw(Items);
  282. end;
  283.  
  284.  
  285. procedure TStartMenu.Clear;
  286. begin
  287.   with Items do while Count > 0 do Items[0].Free;
  288. end;
  289.  
  290.  
  291. procedure TStartMenu.SetOwnerDraw(menu : TMenuItem);
  292. var
  293.   i : Integer;
  294.   item : TMenuItem;
  295. begin
  296.   { Recurses through the menu tree, setting each item to owner-draw.
  297.     With the 4th parameter of ModifyMenu, don't confuse the Handle
  298.     with the Command }
  299.  
  300.   for i := 0 to menu.Count-1 do begin
  301.     item := menu.Items[i];
  302.     if item.Count > 0 then begin
  303.       ModifyMenu(menu.Handle, i, MF_BYPOSITION or MF_OWNERDRAW or MF_POPUP,
  304.         item.Handle, Pointer(item));
  305.       SetOwnerDraw(item);
  306.     end
  307.     else
  308.       ModifyMenu(menu.Handle, i, MF_BYPOSITION or MF_OWNERDRAW,
  309.         item.Command, Pointer(item));
  310.   end;
  311. end;
  312.  
  313.  
  314.  
  315. procedure TStartMenu.Load;
  316. var
  317.   startini: TIniFile;
  318.  
  319. procedure AddToMenu(menu: TMenuItem; const section: string);
  320. var
  321.   names: TStringList;
  322.   s    : string;
  323.   item : TStartMenuItem;
  324.   i    : Integer;
  325. begin
  326.   { Reads an entire INI file section, turns each entry into
  327.     a menu item, and adds the items to the menu parameter }
  328.  
  329.   names := TStringList.Create;
  330.   menu.Caption := ExtractFilename(section);
  331.  
  332.   try
  333.     startini.ReadSection(section, names);
  334.  
  335.     for i := 0 to names.Count-1 do begin
  336.       item := TStartMenuItem.Create(self);
  337.       s := names[i];
  338.       item.Data := startini.ReadString(section, s, '');
  339.  
  340.       if s[Length(s)] = '*' then begin
  341.         Dec(s[0]);
  342.         AddToMenu(item, Format('%s\%s', [section, s]));
  343.       end
  344.       else
  345.         item.OnClick := HandleClick;
  346.  
  347.       item.Caption := s;
  348.       menu.Add(item);
  349.     end;
  350.   finally
  351.     names.Free;
  352.   end;
  353. end;
  354.  
  355.  
  356. procedure AssignBitmaps(menu : TMenuItem);
  357. var
  358.   item: TStartMenuItem;
  359.   i : Integer;
  360. begin
  361.   { AssignBitmaps recursively travels the tree, calling PutBitmap
  362.     for each menu item.  It also calculates the menu item's flags
  363.     used when painting.  The Tag stores the item's height. }
  364.  
  365.   for i := 0 to menu.Count-1 do begin
  366.     item := TStartMenuItem(menu.Items[i]);
  367.     with item do begin
  368.       if i = 0 then Include(Flags, sfTop);
  369.       if i = menu.Count-1 then Include(Flags, sfBottom);
  370.       if (Caption > '') and (Caption[1] = '-') then begin
  371.         Include(Flags, sfSeparator);
  372.         Tag := FItemHeight div 2;
  373.       end
  374.       else Tag := FItemHeight;
  375.       PutBitmap;
  376.     end;
  377.     if item.Count > 0 then AssignBitmaps(item);
  378.   end;
  379. end;
  380.  
  381.  
  382.  
  383. var
  384.   Defaults : TBitmap;
  385.   CacheFile : TFilename;
  386. begin { TStartMenu.Load }
  387.   Clear;
  388.   startini := TIniFile.Create(StartFile);
  389.   Desktop.SetCursor(crHourGlass);
  390.   AddToMenu(Items, 'Start');
  391.   CacheFile := ApplicationPath + 'bmpcache.bmp';
  392.   try
  393.     if StartMenu3D then begin
  394.       FItemHeight := Abs(Canvas.Font.Height) + 10;
  395.       if FileExists(CacheFile) then begin
  396.         UsingCache := True;
  397.         Images.LoadFromFile(CacheFile);
  398.       end
  399.       else begin
  400.         { copy preset pictures into cache bitmap }
  401.         UsingCache := False;
  402.         InitBitmap(Images, 128 * 16, 16, clSilver);
  403.         Defaults := TBitmap.Create;
  404.         Defaults.Handle := LoadBitmap(HInstance, 'STARTBMPS');
  405.         Images.Canvas.Draw(0, 0, Defaults);
  406.         Defaults.Free;
  407.       end;
  408.       Images.Next := 128; { skip over preset pictures }
  409.       AssignBitmaps(Items);
  410.       SetOwnerDraw(Items);
  411.       if not UsingCache then begin
  412.         { chop off any empty space at the end before saving file }
  413.         Images.Width := Images.Next;
  414.         Images.SaveToFile(CacheFile);
  415.       end;
  416.     end
  417.     else begin
  418.       DeleteFile(CacheFile);
  419.       FItemHeight := GetSystemMetrics(SM_CYMENU);
  420.     end;
  421.   finally
  422.     startini.Free;
  423.     Desktop.ReleaseCursor;
  424.   end;
  425. end;
  426.  
  427.  
  428. function TStartMenu.GetHeight : Integer;
  429. var
  430.   i: Integer;
  431. begin
  432.   Result := 2;
  433.   if StartMenu3D then
  434.     for i := 0 to Items.Count-1 do Inc(Result, TStartMenuItem(Items[i]).Tag)
  435.   else
  436.     Inc(Result, Items.Count * FItemHeight);
  437. end;
  438.  
  439.  
  440. procedure TStartMenu.RebuildFromOutline(Outline : TOutline);
  441. var
  442.   startini : TIniFile;
  443.   i : Integer;
  444.   section : string[127];
  445. begin
  446.   DeleteFile(StartFile);
  447.   DeleteFile(ApplicationPath + 'bmpcache.bmp');
  448.  
  449.   { This routine works on the outline from the Start Properties dialog.
  450.     It assumes that each outline node has a dynamic string pointed to by
  451.     the Data property }
  452.  
  453.   startini := TIniFile.Create(StartFile);
  454.   try
  455.     with Outline do
  456.     for i := 1 to ItemCount do with Items[i] do begin
  457.       if Level = 1 then section := 'Start'
  458.       else section := 'Start\' + Parent.FullPath;
  459.  
  460.       if HasItems then
  461.         startini.WriteString(section, Text + '*', PString(Data)^)
  462.       else
  463.         startini.WriteString(section, Text, PString(Data)^);
  464.     end;
  465.   finally
  466.     startini.Free;
  467.     Load;
  468.   end;
  469. end;
  470.  
  471.  
  472. procedure TStartMenu.AssignToOutline(Outline : TOutline);
  473.  
  474. procedure Translate(menu: TMenuItem; dest : Longint);
  475. var
  476.   node : Longint;
  477.   p: PString;
  478.   i : Integer;
  479. begin
  480.   with menu do
  481.     for i := 0 to Count-1 do begin
  482.       New(p);
  483.       p^ := (Items[i] as TStartMenuItem).Data;
  484.       node := Outline.AddChildObject(dest, Items[i].Caption, TObject(p));
  485.       if Items[i].Count > 0 then Translate(Items[i], node);
  486.     end;
  487. end;
  488.  
  489. begin
  490.   Translate(Items, 0);
  491. end;
  492.  
  493.  
  494.  
  495. procedure TStartMenu.HandleClick(Sender : TObject);
  496. const
  497.   ShowCmdsEx : array[TWindowState] of Word =
  498.     (SW_SHOWNORMAL, SW_SHOWMINNOACTIVE, SW_SHOWMAXIMIZED);
  499. var
  500.   filename, params: TFilename;
  501. begin
  502.   with ExtractStartInfo((Sender as TStartMenuItem).Data) do begin
  503.  
  504.     filename := command;
  505.     params := '';
  506.     Unformat(command, '%s %s', [@filename, 79, @params, 79]);
  507.  
  508.     if (filename[1] = '$') and Assigned(FOnStartMacro) then
  509.       FOnStartMacro(self, EnvironSubst(filename), EnvironSubst(params))
  510.     else begin
  511.       LastIconFile := iconfile;
  512.       LastIconIndex := iconindex;
  513.       LastInstance := DefaultExec(filename, params, directory,
  514.         ShowCmdsEx[TWindowState(Abs(showmode) mod 3)]);
  515.     end;
  516.   end;
  517. end;
  518.  
  519.  
  520. procedure TStartMenu.RunStartup;
  521. var
  522.   i: Integer;
  523.   item : TMenuItem;
  524. begin
  525.   item := Find('Startup', True);
  526.   if item <> nil then with item do
  527.     for i := 0 to Count-1 do Items[i].Click;
  528. end;
  529.  
  530.  
  531. procedure TStartMenu.Popup(X, Y: Integer; TrackLeft : Boolean);
  532. const
  533.   Flags: array[TPopupAlignment] of Word = (TPM_LEFTALIGN, TPM_RIGHTALIGN,
  534.     TPM_CENTERALIGN);
  535.  
  536.   Tracks : array[Boolean] of Word =
  537.     (TPM_RIGHTBUTTON, TPM_LEFTBUTTON);
  538. begin
  539.   TrackPopupMenu(Items.Handle, Flags[Alignment] or Tracks[TrackLeft], X, Y,
  540.     0, Window, nil);
  541. end;
  542.  
  543.  
  544. procedure TStartMenu.PaintMenu(DC: HDC; const Rect : TRect;
  545.   state : Word; item : TStartMenuItem);
  546. const
  547.   PenColors : array[Boolean] of TColor = (clSilver, clGray);
  548. var
  549.   y: Integer;
  550. begin
  551.   with Canvas, Rect do begin
  552.     { grab the DC that Windows provides }
  553.     Handle := DC;
  554.  
  555.     if FillMenu then FillRect(Rect);
  556.     if sfSeparator in item.Flags then state := state and not ODS_SELECTED;
  557.  
  558.     if state and ODS_SELECTED > 0 then
  559.       { upper shadow for selected item }
  560.       Pen.Color := clGray
  561.     else if sfTop in item.Flags then begin
  562.       { top item -- draw over menu border with gray }
  563.       Pen.Color := clGray;
  564.       MoveTo(Left-1, Top-1);
  565.       LineTo(Right+1, Top-1);
  566.       Pen.Color := clWhite;
  567.     end
  568.     else
  569.       Pen.Color := clSilver;
  570.  
  571.     { Draw top of menu item }
  572.     MoveTo(Left, Top);
  573.     LineTo(Right, Top);
  574.  
  575.     { Prepare font for output, and prepare pen for drawing the
  576.       bottom of the menu item }
  577.  
  578.     if state and ODS_SELECTED > 0 then begin
  579.       if BoldSelect then begin
  580.         Font.Style := [fsBold];
  581.         Font.Color := clBlack;
  582.       end
  583.       else Font.Color := clWhite;
  584.       Pen.Color := clWhite;
  585.     end
  586.     else begin
  587.       if BoldSelect then Font.Style := [];
  588.       Font.Color := clBlack;
  589.       Pen.Color := PenColors[sfBottom in item.Flags];
  590.     end;
  591.  
  592.     { draw bottom of item }
  593.     MoveTo(Left, Bottom-1);
  594.     LineTo(Right, Bottom-1);
  595.  
  596.     if sfSeparator in item.Flags then begin
  597.       Pen.Color := clGray;
  598.       y := (Top + Bottom) div 2;
  599.       MoveTo(Left, y);
  600.       LineTo(Right, y);
  601.       Pen.Color := clWhite;
  602.       MoveTo(Left, y+1);
  603.       LineTo(Right, y+1);
  604.     end
  605.     else
  606.       TextOut(Left + 40, Top + 4, item.Caption);
  607.  
  608.     { draw the left and right sides }
  609.     Pen.Color := clWhite;
  610.     MoveTo(Left, Top);
  611.     LineTo(Left, Bottom);
  612.  
  613.     Pen.Color := clBlack;
  614.     MoveTo(Right+1, Top);
  615.     LineTo(Right+1, Bottom);
  616.  
  617.     Pen.Color := clGray;
  618.     MoveTo(Right, Top);
  619.     LineTo(Right, Bottom);
  620.     MoveTo(Left-1, Top);
  621.     LineTo(Left-1, Bottom);
  622.  
  623.     { now for the icon... }
  624.     if not (sfSeparator in item.Flags) then
  625.       BitBlt(Handle, Left + 16, Top + 2, 16, 16,
  626.         Images.Canvas.Handle, item.ImgOffset, 0, SRCCOPY);
  627.  
  628.     { reset the canvas object }
  629.     Handle := 0;
  630.   end;
  631. end;
  632.  
  633.  
  634. procedure TStartMenu.WndProc(var Message: TMessage);
  635. begin
  636.   { This is a simplified version of the WndProc from the Menus VCL. }
  637.   try
  638.     case Message.Msg of
  639.       WM_DRAWITEM:
  640.         with TDRAWITEMSTRUCT(Pointer(Message.lParam)^) do
  641.           PaintMenu(hDC, rcItem, itemState, TStartMenuItem(itemData));
  642.  
  643.       WM_MEASUREITEM:
  644.         with TMEASUREITEMSTRUCT(Pointer(Message.lParam)^) do begin
  645.           itemHeight := TMenuItem(itemData).Tag;
  646.           itemWidth := Images.Canvas.TextWidth(TMenuItem(itemData).Caption) + 40;
  647.         end;
  648.  
  649.       WM_COMMAND:
  650.           DispatchCommand(Message.wParam);
  651.     end;
  652.     with Message do Result := DefWindowProc(Window, Msg, wParam, lParam);
  653.   except
  654.     Application.HandleException(Self);
  655.   end;
  656. end;
  657.  
  658.  
  659. function ExtractStartInfo(const s: string): TStartInfo;
  660. begin
  661.   FillChar(Result, SizeOf(Result), 0);
  662.  
  663.   if Unformat(s, '%s;%s;%d;%s;%d',
  664.     [@Result.command, 79, @Result.directory, 79, @Result.showmode,
  665.      @Result.iconfile, 79, @Result.iconindex]) = 1
  666.     then Result.command := '';
  667. end;
  668.  
  669.  
  670. function PackStartInfo(const command, directory, iconfile: TFilename;
  671.  showmode, iconindex: Integer): string;
  672. begin
  673.   Result := Format('%.79s;%.79s;%d;%.79s;%d',
  674.     [command, directory, showmode, iconfile, iconindex]);
  675. end;
  676.  
  677.  
  678. function TStartMenu.Find(const cap : string; Submenu: Boolean): TMenuItem;
  679.  
  680. function FindCap(node : TMenuItem): TMenuItem;
  681. var
  682.   i: Integer;
  683.   item : TMenuItem;
  684. begin
  685.   Result := nil;
  686.   for i := 0 to node.Count-1 do begin
  687.     item := node.Items[i];
  688.     if ((item.Count > 0) = Submenu) and (CompareText(item.Caption, cap) = 0) then
  689.       Result := item
  690.     else if item.Count > 0 then
  691.       Result := FindCap(item);
  692.  
  693.     if Result <> nil then Exit;
  694.   end;
  695. end;
  696.  
  697. begin
  698.   Result := FindCap(items);
  699. end;
  700.  
  701. end.
  702.  
  703.