home *** CD-ROM | disk | FTP | other *** search
/ PC Open 19 / pcopen19.iso / Zipped / CALMIR21.ZIP / SOURCE.ZIP / SRC / STRTPROP.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1998-02-20  |  22.4 KB  |  802 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 Strtprop;
  24.  
  25. { Start Menu Properties Dialog
  26.  
  27.   The main control is a TOutline that contains a copy of the start
  28.   menu.  Each outline node contains a pointer to a dynamic 255 char
  29.   string that stores additional data.  The string size is fixed
  30.   because TOutlineNode's Data property cannot be used easily with
  31.   AssignStr, which requires a var parameter.
  32. }
  33.  
  34. interface
  35.  
  36. uses WinTypes, WinProcs, Classes, Graphics, Forms, Controls, Buttons,
  37.   StdCtrls, Menus, Grids, Outline, TabNotBk, SysUtils, Chklist, StylSped,
  38.   Scrtree, Messages, CalForm, Settings, ExtCtrls;
  39.  
  40. type
  41.   TStartPropDlg = class(TCalForm)
  42.     OKBtn: TBitBtn;
  43.     CancelBtn: TBitBtn;
  44.     Notebook: TTabbedNotebook;
  45.     OutlineMenu: TPopupMenu;
  46.     AddItem: TMenuItem;
  47.     InsertItem: TMenuItem;
  48.     EditItem: TMenuItem;
  49.     DeleteItem: TMenuItem;
  50.     ExpandItem: TMenuItem;
  51.     CollapseItem: TMenuItem;
  52.     N1: TMenuItem;
  53.     Convert: TMenuItem;
  54.     AddBtn: TStyleSpeed;
  55.     InsertBtn: TStyleSpeed;
  56.     EditBtn: TStyleSpeed;
  57.     DeleteBtn: TStyleSpeed;
  58.     ExpandBtn: TStyleSpeed;
  59.     CollapseBtn: TStyleSpeed;
  60.     ConvertBtn: TStyleSpeed;
  61.     Outline: TScrollTree;
  62.     Modified: TCheckBox;
  63.     HelpBtn: TBitBtn;
  64.     Win31Menu: TRadioButton;
  65.     Win95Menu: TRadioButton;
  66.     Bevel1: TBevel;
  67.     Label1: TLabel;
  68.     Label2: TLabel;
  69.     NewStyles: TCheckList;
  70.     PrefList: TCheckList;
  71.     procedure OutlineDragDrop(Sender, Source: TObject; X, Y: Integer);
  72.     procedure OutlineDragOver(Sender, Source: TObject; X, Y: Integer;
  73.       State: TDragState; var Accept: Boolean);
  74.     procedure OutlineEndDrag(Sender, Target: TObject; X, Y: Integer);
  75.     procedure OutlineMouseDown(Sender: TObject; Button: TMouseButton;
  76.       Shift: TShiftState; X, Y: Integer);
  77.     procedure AddItemClick(Sender: TObject);
  78.     procedure InsertItemClick(Sender: TObject);
  79.     procedure EditItemClick(Sender: TObject);
  80.     procedure DeleteItemClick(Sender: TObject);
  81.     procedure ExpandItemClick(Sender: TObject);
  82.     procedure CollapseItemClick(Sender: TObject);
  83.     procedure ConvertClick(Sender: TObject);
  84.     procedure FormCreate(Sender: TObject);
  85.     procedure OKBtnClick(Sender: TObject);
  86.     procedure FormDestroy(Sender: TObject);
  87.     procedure FormClose(Sender: TObject; var Action: TCloseAction);
  88.     procedure OutlineDrawItem(Control: TWinControl; Index: Integer;
  89.       Rect: TRect; State: TOwnerDrawState);
  90.     procedure CancelBtnClick(Sender: TObject);
  91.     procedure PrefListClick(Sender: TObject);
  92.     procedure OutlineDblClick(Sender: TObject);
  93.     procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  94.     procedure Win31MenuClick(Sender: TObject);
  95.     procedure NewStylesClick(Sender: TObject);
  96.     procedure NotebookChange(Sender: TObject; NewTab: Integer;
  97.       var AllowChange: Boolean);
  98.     procedure FormResize(Sender: TObject);
  99.     procedure OutlineMouseUp(Sender: TObject; Button: TMouseButton;
  100.       Shift: TShiftState; X, Y: Integer);
  101.     procedure ModifiedMouseUp(Sender: TObject; Button: TMouseButton;
  102.       Shift: TShiftState; X, Y: Integer);
  103.   private
  104.     { Private declarations }
  105.     DragItem : Longint;
  106.     IconsChanged : Boolean;
  107.     ProgramsMenu : Longint;
  108.     CurrentGroup : Longint;
  109.     procedure ConvertProgItem(Sender : TObject;
  110.      const group, caption: TFilename; const data: string);
  111.     function AddOutlineNode(index : Longint;
  112.       const cap, data : string; Op: TAttachMode): Longint;
  113.     procedure DisposeNode(node : TOutlineNode);
  114.     function MenuNodeAt(X, Y : Integer): Longint;
  115.  
  116.     procedure ProcessDDECommands;
  117.     procedure DDECreateGroup(const s: string);
  118.     procedure DDEShowGroup(const s: string);
  119.     procedure DDEDeleteGroup(const s: string);
  120.     procedure DDEAddItem(const Command, Caption, IconFile: TFilename;
  121.       IconIndex, X, Y: Integer; Directory: TFilename);
  122.     procedure DDEDeleteItem(const s: string);
  123.     function FindChildNode(ParentNode: Longint; const caption: string): Longint;
  124.   public
  125.     { Public declarations }
  126.     procedure Configure;
  127.     procedure SettingsChanged(Changes : TSettingChanges); override;
  128.   end;
  129.  
  130. var
  131.   StartPropDlg: TStartPropDlg;
  132.   NewStartItems : TStringList;
  133.   ShellDDEBuf   : TStringList;
  134.  
  135. implementation
  136.  
  137. {$R *.DFM}
  138.  
  139. uses Start, ProgConv, MenuEdit, MultiGrd, IconWin, Directry, Files,
  140.   CompSys, MiscUtil, Dialogs, Strings, Desk, Resource, Locale,
  141.   Task;
  142.  
  143.  
  144. procedure TStartPropDlg.OutlineDragDrop(Sender, Source: TObject; X,
  145.   Y: Integer);
  146. const
  147.   Attach: array[Boolean] of TAttachMode = (oaInsert, oaAddChild);
  148. var
  149.   dest : Longint;
  150.   i : Integer;
  151. begin
  152.   Outline.DropFocus := -1;
  153.   dest := Outline.GetItemAt(X, Y);
  154.  
  155.   { Handle drops from icon windows or from outline itself }
  156.  
  157.   if Source is TMultiGrid then
  158.     with (TMultiGrid(Source).Owner as TIconWindow).CompileSelection(False) do
  159.       for i := 0 to Count-1 do
  160.         with TDirItem(Items[i]) do
  161.  
  162.           AddOutlineNode(dest, GetTitle, GetStartInfo, oaAddChild)
  163.  
  164.   else with Outline, Items[DragItem] do begin
  165.     { Strange things seem to happen without BeginUpdate/EndUpdate! }
  166.     BeginUpdate;
  167.     if GetAsyncKeyState(VK_CONTROL) < 0 then
  168.       AddOutlineNode(dest, Text,
  169.         PString(Data)^, Attach[GetAsyncKeyState(VK_MENU) < 0])
  170.     else begin
  171.       Collapse;
  172.       IconsChanged := True;
  173.       MoveTo(dest, Attach[GetAsyncKeyState(VK_MENU) < 0]);
  174.     end;
  175.     EndUpdate;
  176.   end;
  177.   Modified.Checked := True;
  178. end;
  179.  
  180.  
  181. procedure TStartPropDlg.OutlineDragOver(Sender, Source: TObject; X,
  182.   Y: Integer; State: TDragState; var Accept: Boolean);
  183. begin
  184.   Accept := ((Sender = Source) or
  185.              (Source is TMultiGrid) and (Source <> Computer.Grid))
  186.              and (Outline.GetItemAt(X, Y) > 0);
  187.  
  188.   with Outline do
  189.     if not Accept or (State = dsDragLeave) then DropFocus := -1
  190.     else DropFocus := GetCellAt(X, Y);
  191. end;
  192.  
  193.  
  194. procedure TStartPropDlg.OutlineEndDrag(Sender, Target: TObject; X,
  195.   Y: Integer);
  196. begin
  197.   ClipCursor(nil);
  198. end;
  199.  
  200.  
  201. function TStartPropDlg.MenuNodeAt(X, Y : Integer): Longint;
  202. var
  203.   P : Integer;
  204. begin
  205.   Result := Outline.GetItem(X, Y);
  206.   if Result > 0 then with Outline.Items[Result] do begin
  207.     P := Level * 20 + 5;
  208.     if HasItems and (X >= P) and (X <= P+12) then Exit;
  209.   end;
  210.   Result := 0;
  211. end;
  212.  
  213.  
  214.  
  215. procedure TStartPropDlg.OutlineMouseDown(Sender: TObject;
  216.   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  217. var
  218.   r : TRect;
  219.   i : Longint;
  220.   p : TPoint;
  221. begin
  222.   if ssDouble in Shift then
  223.     Exit
  224.  
  225.   else if Button = mbRight then with Outline do begin
  226.     { Select the item under the cursor and popup menu }
  227.     if GetCaptureControl <> nil then Exit;
  228.     i := GetItem(X, Y);
  229.     if i > 0 then SelectedItem := i;
  230.     GetCursorPos(p);
  231.     OutlineMenu.Popup(p.X, p.Y);
  232.   end
  233.  
  234.   else with Outline do begin
  235.     DragItem := GetItem(X, Y);
  236.     if (DragItem > 0) and (MenuNodeAt(X, Y) = 0) then begin
  237.       with ClientRect do begin
  238.         r.TopLeft := ClientToScreen(TopLeft);
  239.         r.BottomRight := ClientToScreen(Bottomright);
  240.         ClipCursor(@r);
  241.       end;
  242.       BeginDrag(False);
  243.     end;
  244.   end
  245. end;
  246.  
  247.  
  248. procedure TStartPropDlg.AddItemClick(Sender: TObject);
  249. begin
  250.   with MenuEditDlg do
  251.   if EditItem(LoadStr(SAddMenuItem), '', ';;;;') = mrOK then
  252.     AddOutlineNode(Outline.SelectedItem, CaptionEdit.Text, DataString, oaInsert);
  253. end;
  254.  
  255.  
  256. procedure TStartPropDlg.InsertItemClick(Sender: TObject);
  257. begin
  258.   with Outline, MenuEditDlg do
  259.     if SelectedItem = 0 then AddItem.Click else
  260.       if EditItem(LoadStr(SInsertMenuItem), '', ';;;;') = mrOK then
  261.         AddOutlineNode(SelectedItem, CaptionEdit.Text, DataString, oaAddChild);
  262. end;
  263.  
  264.  
  265. procedure TStartPropDlg.EditItemClick(Sender: TObject);
  266. var
  267.   node : TOutlineNode;
  268. begin
  269.   ShowHourglass;
  270.   with Outline, MenuEditDlg do
  271.     if (SelectedItem > 0) then begin
  272.       node := Items[SelectedItem];
  273.       if EditItem(LoadStr(SMenuItemProperties), node.Text,
  274.       PString(node.Data)^) = mrOK then begin
  275.         PString(node.Data)^ := DataString;
  276.         node.Text := CaptionEdit.Text;
  277.         Modified.Checked := True;
  278.         if IconChanged then IconsChanged := True;
  279.       end;
  280.     end;
  281. end;
  282.  
  283.  
  284. procedure TStartPropDlg.DisposeNode(node : TOutlineNode);
  285. var i : Longint;
  286. begin
  287.   { Recursive procedure to free dynamic strings }
  288.  
  289.   Dispose(PString(node.Data));
  290.   i := node.GetFirstChild;
  291.   while i <> -1 do begin
  292.     DisposeNode(Outline.Items[i]);
  293.     i := node.GetNextChild(i);
  294.   end;
  295. end;
  296.  
  297.  
  298. procedure TStartPropDlg.DeleteItemClick(Sender: TObject);
  299. var
  300.   node: TOutlineNode;
  301.   i : Longint;
  302. begin
  303.   with Outline do
  304.     if SelectedItem > 0 then begin
  305.       node := Items[SelectedItem];
  306.       if node.HasItems and (MsgDialogRes(SQueryDeleteMenu,
  307.         mtConfirmation, [mbYes, mbNo], 0) <> mrYes) then Exit;
  308.  
  309.       DisposeNode(node);
  310.       DeleteSelectedNode;
  311.       Modified.Checked := True;
  312.       IconsChanged := True;
  313.     end;
  314. end;
  315.  
  316.  
  317. procedure TStartPropDlg.ExpandItemClick(Sender: TObject);
  318. begin
  319.   Outline.FullExpand;
  320. end;
  321.  
  322.  
  323. procedure TStartPropDlg.CollapseItemClick(Sender: TObject);
  324. begin
  325.   Outline.FullCollapse;
  326. end;
  327.  
  328.  
  329. procedure TStartPropDlg.ConvertClick(Sender: TObject);
  330. begin
  331.   ShowHourGlass;
  332.   with TConvertDlg.Create(Application) do
  333.   try
  334.     OnConvertProg := ConvertProgItem;
  335.     ShowModal;
  336.   finally
  337.     Free;
  338.   end;
  339. end;
  340.  
  341.  
  342. procedure TStartPropDlg.ConvertProgItem(Sender : TObject;
  343.   const group, caption: TFilename; const data: string);
  344. var
  345.   i, parentnode: Longint;
  346. begin
  347.   with Outline do begin
  348.     { Find existing submenu containing the group }
  349.     parentnode := GetTextItem(group);
  350.  
  351.     if parentnode = 0 then begin
  352.       { Create a new group node and add the item to it }
  353.       parentnode := AddOutlineNode(SelectedItem, group, ';;;;', oaAdd);
  354.       AddOutlineNode(parentnode, caption, data, oaAddChild);
  355.     end
  356.  
  357.     else begin
  358.       { An existing group has been found.  Now look for a matching
  359.         menu item, and update it if found.  Otherwise, just add
  360.         another item }
  361.  
  362.       i := Items[parentnode].GetFirstChild;
  363.       while i <> -1 do
  364.         if CompareText(Items[i].Text, caption) = 0 then begin
  365.           PString(Items[i].Data)^ := data;
  366.           Exit;
  367.         end
  368.         else i := Items[parentnode].GetNextChild(i);
  369.  
  370.       AddOutlineNode(parentnode, caption, data, oaAddChild);
  371.     end;
  372.   end;
  373. end;
  374.  
  375.  
  376.  
  377. procedure TStartPropDlg.FormCreate(Sender: TObject);
  378. var i: Integer;
  379. begin
  380.   { A menu editor dialog is created here to speed up editing }
  381.   Notebook.PageIndex := 0;
  382.   MenuEditDlg := TMenuEditDlg.Create(Application);
  383.   StartMenu.AssignToOutline(Outline);
  384.  
  385.   with NewStartItems do begin
  386.     for i := 0 to Count-1 do AddOutlineNode(0,
  387.       GetStrKey(Strings[i]), GetStrValue(Strings[i]), oaAdd);
  388.     Clear;
  389.   end;
  390.  
  391.   with Outline do begin
  392.     SetUpdateState(False);
  393.     Canvas.Font.Assign(Font);
  394.     Canvas.Pen.Color := clTeal;
  395.   end;
  396.  
  397.  
  398.   if StartMenu3D then Win95Menu.Checked := True
  399.   else Win31Menu.Checked := True;
  400.  
  401.   NewStyles.SetData([LargeRootMenu, ColouredBar, BoldSelect]);
  402.   PrefList.SetData([ShellStartup, StartMouseUp, ShellDDE]);
  403.  
  404.   LoadPosition(ini, 'Start Menu Properties');
  405.   Configure;
  406.  
  407.   Modified.Checked := False;
  408.   IconsChanged := False;
  409.  
  410.   ProcessDDECommands;
  411. end;
  412.  
  413. function TStartPropDlg.AddOutlineNode(index : Longint;
  414.   const cap, data : string; Op: TAttachMode): Longint;
  415. var
  416.   p: PString;
  417. begin
  418.   { Add a new outline node with a dynamic string as the Data }
  419.   Modified.Checked := True;
  420.   IconsChanged := True;
  421.   New(p);
  422.   p^ := data;
  423.   case Op of
  424.     oaAdd      : Result := Outline.AddObject(index, cap, p);
  425.     oaAddChild : Result := Outline.AddChildObject(index, cap, p);
  426.     oaInsert   : Result := Outline.InsertObject(index, cap, p);
  427.   end;
  428. end;
  429.  
  430.  
  431. procedure TStartPropDlg.OKBtnClick(Sender: TObject);
  432. begin
  433.   StartMenu3D := Win95Menu.Checked;
  434.  
  435.   NewStyles.GetData([@LargeRootMenu, @ColouredBar, @BoldSelect]);
  436.   PrefList.GetData([@ShellStartup, @StartMouseUp, @ShellDDE]);
  437.  
  438.   SaveStartProp;
  439.   StartMenu.Configure;
  440.   Taskbar.Configure;
  441.  
  442.   if Modified.Checked then begin
  443.     YieldDuringLoad := True;
  444.     Cursor := crHourglass;
  445.     try
  446.       EnableControls(False);
  447.       if IconsChanged then DeleteFile(FileWritePath + 'bmpcache.bmp');
  448.       StartMenu.RebuildFromOutline(Outline);
  449.     finally
  450.       EnableControls(True);
  451.       YieldDuringLoad := False;
  452.       Cursor := crDefault;
  453.       PlaySound(Sounds.Values['NotifyCompletion']);
  454.     end;
  455.   end;
  456.   Close;
  457.   AnnounceSettingsChanged([scStartMenu]);
  458. end;
  459.  
  460.  
  461. procedure TStartPropDlg.FormDestroy(Sender: TObject);
  462. var
  463.   i: Longint;
  464. begin
  465.   with Outline do
  466.     for i := 1 to ItemCount do Dispose(PString(Items[i].Data));
  467.  
  468.   MenuEditDlg.Free;
  469.   MenuEditDlg := nil;
  470.   StartPropDlg := nil;
  471. end;
  472.  
  473.  
  474. procedure TStartPropDlg.FormClose(Sender: TObject;
  475.   var Action: TCloseAction);
  476. begin
  477.   Action := caFree;
  478.   SavePosition(ini, 'Start Menu Properties');
  479. end;
  480.  
  481.  
  482. procedure TStartPropDlg.OutlineDrawItem(Control: TWinControl;
  483.   Index: Integer; Rect: TRect; State: TOwnerDrawState);
  484. var
  485.   item: TOutlineNode;
  486.   x, y : Integer;
  487. begin
  488.   { Fast outline drawing with no BrushCopy.  Unlike the tree view,
  489.     (see TREE.PAS) there are no disadvantages here because all the
  490.     pictures are square or rectangular, so no transparency is
  491.     needed }
  492.  
  493.   with Outline do begin
  494.     index := GetItem(0, Rect.Top);
  495.     item := Items[index];
  496.     x := Rect.Left + item.Level * 20 + 4;
  497.     y := (Rect.Top + Rect.Bottom) div 2;
  498.  
  499.     with Canvas do begin
  500.       if odSelected in State then begin
  501.         Brush.Color := clHighlight;
  502.         Font.Color := clHighlightText;
  503.       end
  504.       else begin
  505.         Brush.Color := Color;
  506.         Font.Color := clWindowText;
  507.       end;
  508.  
  509.       FillRect(Rect);
  510.  
  511.       if item.HasItems then
  512.         if item.Expanded then Draw(x+1, Rect.Top+2, PictureOpen)
  513.         else Draw(x+1, Rect.Top+2, PictureClosed)
  514.       else
  515.         Draw(x+1, Rect.Top+4, PictureLeaf);
  516.  
  517.       TextOut(x + 19, Rect.Top+1, item.Text);
  518.  
  519.       { Draw horizontal line connecting node to branch }
  520.  
  521.       MoveTo(x - 4, y);
  522.       Dec(x, 16);
  523.       LineTo(x, y);
  524.  
  525.       { Draw vertical line, it's length depending on whether
  526.         this node has additional siblings }
  527.  
  528.       if Item.Parent.GetLastChild = Item.Index then
  529.         LineTo(x, Rect.Top-1)
  530.       else begin
  531.         MoveTo(x, Rect.Top);
  532.         LineTo(x, Rect.Bottom);
  533.       end;
  534.  
  535.       { Loop back to the root through all parent nodes, drawing a
  536.         vertical line if the parent has child nodes to be drawn
  537.         below this node }
  538.  
  539.       item := item.Parent;
  540.  
  541.       while (Item <> nil) and (Item.Parent <> nil) do begin
  542.         Dec(x, 20);
  543.         if Item.Parent.GetLastChild > Item.Index then begin
  544.           MoveTo(x, Rect.Top);
  545.           LineTo(x, Rect.Bottom);
  546.         end;
  547.         item := item.Parent;
  548.       end;
  549.     end;
  550.   end;
  551. end;
  552.  
  553.  
  554. procedure TStartPropDlg.CancelBtnClick(Sender: TObject);
  555. begin
  556.   Close;
  557. end;
  558.  
  559. procedure TStartPropDlg.SettingsChanged(Changes : TSettingChanges);
  560. begin
  561.   if [scDesktop, scDisplay, scSystem] * Changes <> [] then Configure;
  562. end;
  563.  
  564.  
  565. procedure TStartPropDlg.Configure;
  566. begin
  567.   Outline.ThumbTracking := TrackThumb;
  568.   Outline.ItemHeight := LineHeight;
  569. end;
  570.  
  571.  
  572. procedure TStartPropDlg.PrefListClick(Sender: TObject);
  573. begin
  574.   if PrefList.ItemIndex = 3 then begin
  575.     Modified.Checked := True;
  576.     IconsChanged := True;
  577.   end;
  578.  
  579. end;
  580.  
  581.  
  582. procedure TStartPropDlg.OutlineDblClick(Sender: TObject);
  583. begin
  584.   with Outline do
  585.     if (SelectedItem > 0) and
  586.       not Items[SelectedItem].HasItems then EditBtn.Click;
  587. end;
  588.  
  589. procedure TStartPropDlg.FormCloseQuery(Sender: TObject;
  590.   var CanClose: Boolean);
  591. begin
  592.   CanClose := Controls[0].Enabled;
  593. end;
  594.  
  595. procedure TStartPropDlg.Win31MenuClick(Sender: TObject);
  596. begin
  597.   NewStyles.Enabled := Win95Menu.Checked;
  598.   Modified.Checked := True;
  599.   IconsChanged := True;
  600. end;
  601.  
  602. procedure TStartPropDlg.NewStylesClick(Sender: TObject);
  603. begin
  604.   if NewStyles.ItemIndex = 0 then begin
  605.     Modified.Checked := True;
  606.     IconsChanged := True;
  607.   end;
  608. end;
  609.  
  610. procedure TStartPropDlg.NotebookChange(Sender: TObject; NewTab: Integer;
  611.   var AllowChange: Boolean);
  612. begin
  613.   FreePageHandles(Notebook);
  614. end;
  615.  
  616.  
  617.  
  618. procedure TStartPropDlg.FormResize(Sender: TObject);
  619. begin
  620.   StretchShift([HelpBtn, CancelBtn, OKBtn, Modified], [stLeft, stTop]);
  621.   StretchShift([Notebook, Outline], [stWidth, stHeight]);
  622.   StretchShift([Bevel1, PrefList], [stWidth]);
  623. end;
  624.  
  625.  
  626. procedure TStartPropDlg.OutlineMouseUp(Sender: TObject;
  627.   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  628. var
  629.   item : Longint;
  630. begin
  631.   if (Button = mbLeft) and not (ssDouble in Shift) then begin
  632.     item := MenuNodeAt(X, Y);
  633.     if item > 0 then with Outline.Items[item] do
  634.       Expanded := not Expanded;
  635.   end;
  636. end;
  637.  
  638. function TStartPropDlg.FindChildNode(ParentNode: Longint; const caption: string): Longint;
  639. var
  640.   node : TOutlineNode;
  641. begin
  642.   with Outline do begin
  643.     node := Items[ParentNode];
  644.     Result := node.GetFirstChild;
  645.     while Result <> -1 do begin
  646.       if Items[Result].Text = caption then Exit
  647.       else Result := node.GetNextChild(Result);
  648.     end;
  649.     Result := 0;
  650.   end;
  651. end;
  652.  
  653. procedure TStartPropDlg.DDECreateGroup(const s: string);
  654. var
  655.   item : Longint;
  656. begin
  657.   item := FindChildNode(ProgramsMenu, s);
  658.   if item = 0 then item := AddOutlineNode(ProgramsMenu, s, '', oaAddChild);
  659.   CurrentGroup := item;
  660. end;
  661.  
  662.  
  663. procedure TStartPropDlg.DDEShowGroup(const s: string);
  664. var
  665.   item : Longint;
  666. begin
  667.   item := FindChildNode(ProgramsMenu, s);
  668.   if item > 0 then CurrentGroup := item;
  669. end;
  670.  
  671. procedure TStartPropDlg.DDEDeleteGroup(const s: string);
  672. var
  673.   item : Longint;
  674. begin
  675.   item := FindChildNode(ProgramsMenu, s);
  676.   if item > 0 then Outline.Delete(item);
  677. end;
  678.  
  679.  
  680. procedure TStartPropDlg.DDEAddItem(const Command, Caption, IconFile: TFilename;
  681.   IconIndex, X, Y: Integer; Directory: TFilename);
  682. begin
  683.   if Directory = '' then Directory := ExtractFileDir(Command);
  684.   AddOutlineNode(CurrentGroup, Caption,
  685.     PackStartInfo(Command, Directory, IconFile, 0, IconIndex), oaAddChild);
  686. end;
  687.  
  688.  
  689. procedure TStartPropDlg.DDEDeleteItem(const s: string);
  690. var
  691.   item : Longint;
  692. begin
  693.   item := FindChildNode(CurrentGroup, s);
  694.   if item > 0 then Outline.Delete(item);
  695. end;
  696.  
  697.  
  698. function GetStrParam(var s: string): TFilename;
  699. var i: Integer;
  700. begin
  701.   if (s = '') or (s[1] = ')') or (s[1] = ']') then Result := ''
  702.   else begin
  703.     if s[1] = '"' then begin
  704.       i := 2;
  705.       while (i < Length(s)) and (s[i] <> '"') do Inc(i);
  706.       Result := Copy(s, 2, i-2);
  707.       if (i < Length(s)) and (s[i+1] = ',') then Inc(i);
  708.     end
  709.     else begin
  710.       i := 1;
  711.       while (i < Length(s)) and (s[i] <> ',') and (s[i] <> ')') do Inc(i);
  712.       Result := Copy(s, 1, i-1);
  713.     end;
  714.     Delete(s, 1, i);
  715.   end;
  716. end;
  717.  
  718.  
  719. function GetIntParam(var s: string): Integer;
  720. var
  721.   i: Integer;
  722.   field : string[15];
  723. begin
  724.   if (s = '') or (s[1] = ')') or (s[1] = ']') then Result := 0
  725.   else begin
  726.     i := 1;
  727.     while (i < Length(s)) and ((s[i] in Digits) or (s[i] = '-')) do Inc(i);
  728.     Result := StrToIntDef(Copy(s, 1, i-1), 0);
  729.     Delete(s, 1, i);
  730.   end;
  731. end;
  732.  
  733. const
  734.   CommandPrefixes : array[0..5] of string[15] =
  735.    ('[CreateGroup(', '[ShowGroup(', '[DeleteGroup(',
  736.     '[AddItem(', '[ReplaceItem(', '[DeleteItem(');
  737.  
  738. function ParseCommand(var s: string): Integer;
  739. var t: string[15];
  740. begin
  741.   for Result := 0 to High(CommandPrefixes) do begin
  742.     t := Copy(s, 1, Length(CommandPrefixes[Result]));
  743.     if CompareText(CommandPrefixes[Result], t) = 0 then begin
  744.       Delete(s, 1, Length(CommandPrefixes[Result]));
  745.       Exit;
  746.     end;
  747.   end;
  748.   Result := -1;
  749. end;
  750.  
  751. procedure TStartPropDlg.ProcessDDECommands;
  752. var
  753.   i,p : Integer;
  754.   s: string;
  755. begin
  756.   ProgramsMenu := Max(Outline.GetTextItem(
  757.     ini.ReadString('Start menu', 'ProgramsGroup', 'Programs')), 0);
  758.   CurrentGroup := ProgramsMenu;
  759.  
  760.   i := 0;
  761.   while i < ShellDDEBuf.Count do begin
  762.     s := ShellDDEBuf[i];
  763.     case ParseCommand(s) of
  764.      0: DDECreateGroup(GetStrParam(s));
  765.      1: DDEShowGroup(GetStrParam(s));
  766.      2: DDEDeleteGroup(GetStrParam(s));
  767.      3: DDEAddItem(GetStrParam(s), GetStrParam(s), GetStrParam(s),
  768.           GetIntParam(s), GetIntParam(s), GetIntParam(s), GetStrParam(s));
  769.      4: DDEDeleteItem(GetStrParam(s));
  770.      5: DDEDeleteItem(GetStrParam(s));
  771.     end;
  772.  
  773.     p := Pos('][', s);
  774.     if p > 0 then begin
  775.       Delete(s, 1, p);
  776.       if s > '' then ShellDDEBuf.Insert(i+1, s);
  777.     end;
  778.  
  779.     Inc(i);
  780.   end;
  781.   ShellDDEBuf.Clear;
  782. end;
  783.  
  784.  
  785. procedure DoneStartProp; far;
  786. begin
  787.   NewStartItems.Free;
  788.   ShellDDEBuf.Free;
  789. end;
  790.  
  791. procedure TStartPropDlg.ModifiedMouseUp(Sender: TObject;
  792.   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  793. begin
  794.   IconsChanged := Modified.Checked;
  795. end;
  796.  
  797. initialization
  798.   NewStartItems := TStringList.Create;
  799.   ShellDDEBuf := TStringList.Create;
  800.   AddExitProc(DoneStartProp);
  801. end.
  802.