home *** CD-ROM | disk | FTP | other *** search
/ PC Format Collection 48 / SENT14D.ISO / tech / delphi / disk15 / experts.pak / APP.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1995-08-24  |  32.8 KB  |  1,196 lines

  1. unit App;
  2.  
  3. interface
  4.  
  5. uses
  6.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  7.   Forms, Dialogs, ToolIntf, StdCtrls, Buttons, ExtCtrls;
  8.  
  9. type
  10.   TMoveDirection = (mdPrevious, mdNext, mdNoMove);
  11.  
  12.   TAppExpert = class(TForm)
  13.     Notebook: TNotebook;
  14.     Label1: TLabel;
  15.     BitBtn1: TBitBtn;
  16.     PrevButton: TBitBtn;
  17.     NextButton: TBitBtn;
  18.     cbFileMenu: TCheckBox;
  19.     cbEditMenu: TCheckBox;
  20.     cbWindowMenu: TCheckBox;
  21.     cbHelpMenu: TCheckBox;
  22.     Label2: TLabel;
  23.     Label3: TLabel;
  24.     Label4: TLabel;
  25.     Label5: TLabel;
  26.     Label6: TLabel;
  27.     Panel1: TPanel;
  28.     ExtHeader: THeader;
  29.     ExtListBox: TListBox;
  30.     AddButton: TButton;
  31.     EditButton: TButton;
  32.     DeleteButton: TButton;
  33.     UpButton: TButton;
  34.     DownButton: TButton;
  35.     appinfo: TLabel;
  36.     Speedbar: TPaintBox;
  37.     MenuList: TListBox;
  38.     MenuItemList: TListBox;
  39.     Button1: TButton;
  40.     Button2: TButton;
  41.     Button3: TButton;
  42.     Label7: TLabel;
  43.     Label8: TLabel;
  44.     Label9: TLabel;
  45.     AppName: TEdit;
  46.     Label13: TLabel;
  47.     AppPath: TEdit;
  48.     PathBrowse: TButton;
  49.     Label15: TLabel;
  50.     Sample: TPaintBox;
  51.     GroupBox1: TGroupBox;
  52.     cbMDIApp: TCheckBox;
  53.     cbStatusLine: TCheckBox;
  54.     cbHints: TCheckBox;
  55.     procedure FormCreate(Sender: TObject);
  56.     procedure NextPrevClick(Sender: TObject);
  57.     procedure DrawExtension(Control: TWinControl; Index: Integer;
  58.       Rect: TRect; State: TOwnerDrawState);
  59.     procedure AddClick(Sender: TObject);
  60.     procedure HeaderSized(Sender: TObject; ASection, AWidth: Integer);
  61.     procedure EditClick(Sender: TObject);
  62.     procedure DeleteClick(Sender: TObject);
  63.     procedure MoveClick(Sender: TObject);
  64.     procedure SpeedbarPaint(Sender: TObject);
  65.     procedure FormDestroy(Sender: TObject);
  66.     procedure MenuListClick(Sender: TObject);
  67.     procedure DrawMenuItem(Control: TWinControl; Index: Integer;
  68.       Rect: TRect; State: TOwnerDrawState);
  69.     procedure InsertClick(Sender: TObject);
  70.     procedure SpaceClick(Sender: TObject);
  71.     procedure SpeedMouseDown(Sender: TObject; Button: TMouseButton;
  72.       Shift: TShiftState; X, Y: Integer);
  73.     procedure RemoveClick(Sender: TObject);
  74.     procedure BrowseClick(Sender: TObject);
  75.     procedure SamplePaint(Sender: TObject);
  76.     procedure MenuClicked(Sender: TObject);
  77.   private
  78.     { Private declarations }
  79.     SpeedList: TList;
  80.     ButtonList: TList;
  81.     FSpeedIndex: Integer;
  82.     SpeedPointer: TBitmap;
  83.     Offscreen: TBitmap;
  84.     SampleBmp: TBitmap;
  85.     procedure RefreshButtons;
  86.     function NextPage(Direction: TMoveDirection): Integer;
  87.     function SpeedButtonRect(Index: Integer): TRect;
  88.     function SpeedButtonAtPos(Pos: TPoint): Integer;
  89.     function GetSpeedButtonCount: Integer;
  90.     function GetSpeedButtonID(Value: Integer): Integer;
  91.     function ValidateInfo: Boolean;
  92.   public
  93.     { Public declarations }
  94.     function HasMenus: Boolean;
  95.     property SpeedButtonCount: Integer read GetSpeedButtonCount;
  96.     property SpeedButtonID[Value: Integer]: Integer read GetSpeedButtonID;
  97.   end;
  98.  
  99. procedure ApplicationExpert(ToolServices: TIToolServices);
  100.  
  101. implementation
  102.  
  103. uses ExConst, Filters, FileCtrl;
  104.  
  105. {$R *.DFM}
  106.  
  107. const
  108.   { page numbers }
  109.   pgMenus   = 0;
  110.   pgExtensions = 1;
  111.   pgSpeedbar = 2;
  112.   pgAppInfo = 3;
  113.  
  114.   FirstPage = pgMenus;
  115.   LastPage = pgAppInfo;
  116.  
  117.   DefaultButtonSize: TPoint = (X: 24; Y: 24);
  118.   DefaultButtonSpace: Integer = 6;
  119.  
  120.   MenuItemCount = 18;
  121.  
  122. type
  123.   TMainItems = (mmFile, mmEdit, mmWindow, mmHelp);
  124.  
  125. const
  126.   MenuItemCounts: array[TMainItems] of Integer = (7, 4, 3, 4);
  127.   MenuItemOffsets: array[TMainItems] of Integer = (0, 7, 11, 14);
  128.   SampleBitmaps: array[FirstPage..LastPage] of PChar = (
  129.     'MENUDSGN', 'EXTDSGN', 'SPEEDDSGN', 'INFODSGN');
  130.  
  131. { TButtonImage - draws the image of a TSpeedButton }
  132. type
  133.   TButtonImage = class(TObject)
  134.   private
  135.     FBitmapID: Word;
  136.     FBitmap: TBitmap;
  137.     FNumGlyphs: Integer;
  138.     procedure SetBitmapID(Value: Word);
  139.   public
  140.     constructor Create;
  141.     destructor Destroy; override;
  142.     procedure Draw(Canvas: TCanvas; X, Y: Integer);
  143.     property BitmapID: Word read FBitmapID write SetBitmapID;
  144.     property NumGlyphs: Integer read FNumGlyphs write FNumGlyphs;
  145.   end;
  146.  
  147. { Code generation support }
  148. type
  149.   TCodeSnipet = (csProgram, csMainIntf, csMainImpl, csFormCreateProc,
  150.     csShowHelpProc, csFileNewProc, csFileOpenProc, csFileSaveProc,
  151.     csFileSaveAsProc, csFilePrintProc, csFilePrintSetupProc, csFileExitProc,
  152.     csEditUndoProc, csEditCutProc, csEditCopyProc, csEditPasteProc,
  153.     csWindowTileProc, csWindowCascadeProc, csWindowArrangeProc,
  154.     csHelpContentsProc, csHelpSearchProc, csHelpHowToUseProc,
  155.     csHelpAboutProc, csForm, csFormMenu, csCreateMethod, csFormMDI, csHints,
  156.     csMenuObject, csFileMenuObject, csEditMenuObject, csWindowMenuObject,
  157.     csHelpMenuObject, csOpenDialogObject, csSaveDialogObject,
  158.     csPrintDialogObject, csPrintSetupDialogObject, csStatusLineObject,
  159.     csSpeedbarObject, csSpeedButtonObject);
  160.  
  161. const
  162.   SourceBufferSize = 1024;
  163.  
  164. var
  165.   CodeSnipets: array[TCodeSnipet] of PChar;
  166.   CodeResource: THandle;
  167.   SourceBuffer: PChar;
  168.  
  169. procedure InitCodeGeneration;
  170. var
  171.   P: PChar;
  172.   SnipetIndex: TCodeSnipet;
  173. begin
  174.   SourceBuffer := StrAlloc(SourceBufferSize);
  175.  
  176.   CodeResource := LoadResource(HInstance,
  177.     FindResource(HInstance, 'SNIPETS', RT_RCDATA));
  178.   P := LockResource(CodeResource);
  179.   for SnipetIndex := Low(TCodeSnipet) to High(TCodeSnipet) do
  180.   begin
  181.     CodeSnipets[SnipetIndex] := P;
  182.     while P^ <> '|' do Inc(P);
  183.     P^ := #0;
  184.     Inc(P);
  185.   end;
  186. end;
  187.  
  188. procedure DoneCodeGeneration;
  189. begin
  190.   StrDispose(SourceBuffer);
  191.   UnlockResource(CodeResource);
  192.   FreeResource(CodeResource);
  193. end;
  194.  
  195. procedure BinToHex(Binary, Text: PChar; Count: Integer);
  196. const
  197.   HexChars: array[0..15] of Char = '0123456789ABCDEF';
  198. var
  199.   I: Integer;
  200. begin
  201.   for I := 0 to Count - 1 do
  202.   begin
  203.     Text^ := HexChars[(Byte(Binary[I]) and $F0) SHR 4];
  204.     Inc(Text);
  205.     Text^ := HexChars[(Byte(Binary[I]) and $0F)];
  206.     Inc(Text);
  207.   end;
  208. end;
  209.  
  210. procedure WriteBinaryAsText(Input: TStream; Output: TStream);
  211. const
  212.   BytesPerLine = 32;
  213.   NewLine: PChar = #13#10;
  214. var
  215.   MultiLine: Boolean;
  216.   I: Integer;
  217.   Count: Longint;
  218.   Buffer: array[0..BytesPerLine - 1] of Char;
  219.   Text: array[0..BytesPerLine * 2 - 1] of Char;
  220. begin
  221.   Count := Input.Size;
  222.   MultiLine := Count > BytesPerLine;
  223.   BinToHex(@Count, Text, 4);
  224.   Output.Write(Text, 4 * 2);
  225.  
  226.   while Count > 0 do
  227.   begin
  228.     if MultiLine then Output.Write(NewLine[0], 2);
  229.     if Count >= BytesPerLine then I := BytesPerLine else I := Count;
  230.     Input.Read(Buffer, I);
  231.     BinToHex(Buffer, Text, I);
  232.     Output.Write(Text, I * 2);
  233.     Dec(Count, I);
  234.   end;
  235. end;
  236.  
  237. procedure FmtWrite(Stream: TStream; Fmt: PChar; const Args: array of const);
  238. begin
  239.   StrLFmt(SourceBuffer, SourceBufferSize, Fmt, Args);
  240.   Stream.Write(SourceBuffer[0], StrLen(SourceBuffer));
  241. end;
  242.  
  243. procedure WriteSnipet(Stream: TStream; Snipet: TCodeSnipet);
  244. begin
  245.   Stream.Write(CodeSnipets[Snipet][0], StrLen(CodeSnipets[Snipet]));
  246. end;
  247.  
  248. procedure WriteIdent(Stream: TStream; ResID: Word; const VarType: string);
  249. begin
  250.   StrPCopy(SourceBuffer, Format('    %s: %s;'#13#10, [LoadStr(ResID), VarType]));
  251.   Stream.Write(SourceBuffer[0], StrLen(SourceBuffer));
  252. end;
  253.  
  254. procedure WriteMenuItems(Stream: TStream; MenuIndex: TMainItems);
  255. var
  256.   I: Integer;
  257. begin
  258.   for I := 0 to MenuItemCounts[MenuIndex] - 1 do
  259.     WriteIdent(Stream, sMenuItemNameBase + MenuItemOffsets[MenuIndex] + I,
  260.       'TMenuItem');
  261. end;
  262.  
  263. procedure WriteMethodDecl(Stream: TStream; ResID: Word);
  264. begin
  265.   StrPCopy(SourceBuffer, Format('    procedure %s(Sender: TObject);'#13#10,
  266.     [LoadStr(ResID)]));
  267.   Stream.Write(SourceBuffer[0], StrLen(SourceBuffer));
  268. end;
  269.  
  270. procedure WriteMethodHeader(Stream: TStream; ResID: Word);
  271. begin
  272.   StrPCopy(SourceBuffer, Format('procedure T%s.%s(Sender: TObject);'#13#10,
  273.     [LoadStr(sMainForm), LoadStr(ResID)]));
  274.   Stream.Write(SourceBuffer[0], StrLen(SourceBuffer));
  275. end;
  276.  
  277. procedure WriteMenuMethodDecls(Stream: TStream; MenuIndex: TMainItems);
  278. var
  279.   I: Integer;
  280. begin
  281.   for I := 0 to MenuItemCounts[MenuIndex] - 1 do
  282.     WriteMethodDecl(Stream, sMenuProcNames + MenuItemOffsets[MenuIndex] + I);
  283. end;
  284.  
  285. procedure WriteMenuMethods(Stream: TStream; MenuIndex: TMainItems;
  286.   BaseSnipet: TCodeSnipet);
  287. var
  288.   ID, I: Integer;
  289.   Snipet: TCodeSnipet;
  290. begin
  291.   ID := sMenuProcNames + MenuItemOffsets[MenuIndex];
  292.   for I := 0 to MenuItemCounts[MenuIndex] - 1 do
  293.   begin
  294.     WriteMethodHeader(Stream, ID + I);
  295.     Snipet := TCodeSnipet( I + Ord(BaseSnipet) );
  296.     WriteSnipet(Stream, Snipet);
  297.   end;
  298. end;
  299.  
  300. procedure WriteGlyphData(Stream: TStream; BitmapID: Word);
  301. var
  302.   Bitmap: TBitmap;
  303.   Memory: TMemoryStream;
  304. begin
  305.   Bitmap := TBitmap.Create;
  306.   try
  307.     Bitmap.Handle := LoadBitmap(HInstance, PChar(BitmapID));
  308.  
  309.     { stream the bitmap to a memory stream, and the write that stream as text }
  310.     Memory := TMemoryStream.Create;
  311.     try
  312.       Bitmap.SaveToStream(Memory);
  313.       Memory.Position := 0;
  314.       WriteBinaryAsText(Memory, Stream);
  315.     finally
  316.       Memory.Free;
  317.     end;
  318.  
  319.   finally
  320.     Bitmap.Free;
  321.   end;
  322.   FmtWrite(Stream, '}'#13#10'end'#13#10, [nil]);
  323. end;
  324.  
  325. function GenerateProjectSource(AppExpert: TAppExpert): TFileName;
  326. var
  327.   ProjectFile: TFileStream;
  328. begin
  329.   Result := AppExpert.AppPath.Text;
  330.   if (Result > '') and not (Result[Length(Result)] in [':', '\']) then
  331.     Result := Result + '\';
  332.   Result := Result + AppExpert.AppName.Text + '.DPR';
  333.  
  334.   ProjectFile := TFileStream.Create(Result, fmCreate);
  335.   try
  336.     StrFmt(SourceBuffer, CodeSnipets[csProgram], [AppExpert.AppName.Text]);
  337.     ProjectFile.Write(SourceBuffer[0], StrLen(SourceBuffer));
  338.   finally
  339.     ProjectFile.Free;
  340.   end;
  341. end;
  342.  
  343. procedure GenerateMainSourceFile(AppExpert: TAppExpert);
  344. var
  345.   Stream: TFileStream;
  346.   FileName: TFileName;
  347.   ClassDecl: PChar;
  348.   SourcePos: PChar;
  349.   ButtonName: string[80];
  350.   ButtonText: string[30];
  351.   ButtonID: Integer;
  352.   ID, I: Integer;
  353.   Snipet: TCodeSnipet;
  354. begin
  355.   FileName := AppExpert.AppPath.Text;
  356.   if (FileName > '') and (not (FileName[Length(FileName)] in [':', '\'])) then
  357.     FileName := FileName + '\';
  358.   FileName := FileName + LoadStr(sMainSourceFile);
  359.  
  360.   Stream := TFileStream.Create(FileName, fmCreate);
  361.   try
  362.     WriteSnipet(Stream, csMainIntf);
  363.  
  364.     SourcePos := SourceBuffer;
  365.     SourceBuffer[0] := #0;
  366.  
  367.     { create the menu declarations }
  368.     if AppExpert.HasMenus then
  369.     begin
  370.       WriteIdent(Stream, sMainMenu, 'TMainMenu');
  371.       if AppExpert.cbFileMenu.Checked then WriteMenuItems(Stream, mmFile);
  372.       if AppExpert.cbEditMenu.Checked then WriteMenuItems(Stream, mmEdit);
  373.       if AppExpert.cbWindowMenu.Checked then WriteMenuItems(Stream, mmWindow);
  374.       if AppExpert.cbHelpMenu.Checked then WriteMenuItems(Stream, mmHelp);
  375.      end;
  376.  
  377.     { create any variable declarations }
  378.     if AppExpert.cbStatusLine.Checked then
  379.       WriteIdent(Stream, sStatusLine, 'TPanel');
  380.  
  381.     if AppExpert.cbFileMenu.Checked then
  382.     begin
  383.       WriteIdent(Stream, sOpenDialog, 'TOpenDialog');
  384.       WriteIdent(Stream, sSaveDialog, 'TSaveDialog');
  385.       WriteIdent(Stream, sPrintDialog, 'TPrintDialog');
  386.       WriteIdent(Stream, sPrintSetupDialog, 'TPrinterSetupDialog');
  387.     end;
  388.  
  389.     { create speedbuttons }
  390.     if AppExpert.SpeedButtonCount > 0 then
  391.     begin
  392.       WriteIdent(Stream, sSpeedBar, 'TPanel');
  393.  
  394.       ButtonName := '    ' + LoadStr(sSpeedButton) +
  395.         ': TSpeedButton;  { %s }'#13#10;
  396.  
  397.       ButtonID := 1;
  398.       for I := 0 to AppExpert.SpeedButtonCount - 1 do
  399.       begin
  400.         if AppExpert.SpeedButtonID[I] > -1 then
  401.         begin
  402.           ButtonText := LoadStr(AppExpert.SpeedButtonID[I]);
  403.           StrPCopy(SourceBuffer, Format(ButtonName, [ButtonID, ButtonText]));
  404.           Stream.Write(SourceBuffer[0], StrLen(SourceBuffer));
  405.           Inc(ButtonID);
  406.         end;
  407.       end;
  408.     end;
  409.  
  410.     { generate method declarations }
  411.     if AppExpert.cbStatusLine.Checked and AppExpert.cbHints.Checked then
  412.     begin
  413.       WriteMethodDecl(Stream, sFormCreateProc);
  414.       WriteMethodDecl(Stream, sShowHelpProc);
  415.     end;
  416.  
  417.     if AppExpert.cbFileMenu.Checked then WriteMenuMethodDecls(Stream, mmFile);
  418.     if AppExpert.cbEditMenu.Checked then WriteMenuMethodDecls(Stream, mmEdit);
  419.     if AppExpert.cbWindowMenu.Checked then WriteMenuMethodDecls(Stream, mmWindow);
  420.     if AppExpert.cbHelpMenu.Checked then WriteMenuMethodDecls(Stream, mmHelp);
  421.  
  422.     WriteSnipet(Stream, csMainImpl);
  423.  
  424.     { write code implementations }
  425.     if AppExpert.cbStatusLine.Checked and AppExpert.cbHints.Checked then
  426.     begin
  427.       WriteMethodHeader(Stream, sFormCreateProc);
  428.       WriteSnipet(Stream, csFormCreateProc);
  429.       WriteMethodHeader(Stream, sShowHelpProc);
  430.       WriteSnipet(Stream, csShowHelpProc);
  431.     end;
  432.  
  433.     if AppExpert.cbFileMenu.Checked then
  434.       WriteMenuMethods(Stream, mmFile, csFileNewProc);
  435.  
  436.     if AppExpert.cbEditMenu.Checked then
  437.       WriteMenuMethods(Stream, mmEdit, csEditUndoProc);
  438.  
  439.     if AppExpert.cbWindowMenu.Checked then
  440.       WriteMenuMethods(Stream, mmWindow, csWindowTileProc);
  441.  
  442.     if AppExpert.cbHelpMenu.Checked then
  443.       WriteMenuMethods(Stream, mmHelp, csHelpContentsProc);
  444.  
  445.     FmtWrite(Stream, 'end.'#13#10, [nil]);
  446.  
  447.   finally
  448.     Stream.Free;
  449.   end;
  450. end;
  451.  
  452. procedure GenerateMainFormFile(AppExpert: TAppExpert);
  453. const
  454.   ButtonWidth = 25;
  455.   SpaceWidth = 4;
  456. var
  457.   TextStream: TFileStream;
  458.   FormStream: TFileStream;
  459.   TextName: TFileName;
  460.   FormName: TFileName;
  461.   Filter: string;
  462.   ButtonNumber: Integer;
  463.   ButtonID: Integer;
  464.   ButtonMethod: string;
  465.   ButtonHint: string;
  466.   ButtonX: Integer;
  467.   I: Integer;
  468. begin
  469.   TextName := AppExpert.AppPath.Text;
  470.   if (TextName > '') and (not (TextName[Length(TextName)] in [':', '\'])) then
  471.     TextName := TextName + '\';
  472.   FormName := TextName + LoadStr(sMainFormFile);
  473.   TextName := TextName + LoadStr(sMainFormText);
  474.  
  475.   TextStream := TFileStream.Create(TextName, fmCreate);
  476.   try
  477.     WriteSnipet(TextStream, csForm);
  478.     if AppExpert.cbMDIApp.Checked then WriteSnipet(TextStream, csFormMDI);
  479.     if AppExpert.HasMenus then WriteSnipet(TextStream, csFormMenu);
  480.     if AppExpert.cbHints.Checked then
  481.     begin
  482.       WriteSnipet(TextStream, csHints);
  483.       if AppExpert.cbStatusLine.Checked then
  484.         WriteSnipet(TextStream, csCreateMethod);
  485.     end;
  486.  
  487.     { write menus }
  488.     if AppExpert.HasMenus then
  489.     begin
  490.       WriteSnipet(TextStream, csMenuObject);
  491.  
  492.       if AppExpert.cbFileMenu.Checked then
  493.         WriteSnipet(TextStream, csFileMenuObject);
  494.       if AppExpert.cbEditMenu.Checked then
  495.         WriteSnipet(TextStream, csEditMenuObject);
  496.       if AppExpert.cbWindowMenu.Checked then
  497.         WriteSnipet(TextStream, csWindowMenuObject);
  498.       if AppExpert.cbHelpMenu.Checked then
  499.         WriteSnipet(TextStream, csHelpMenuObject);
  500.  
  501.       FmtWrite(TextStream, '  end'#13#10, [nil]);
  502.  
  503.       if AppExpert.cbFileMenu.Checked then
  504.       begin
  505.         { create the dialog objects }
  506.         Filter := '';
  507.         for I := 0 to AppExpert.ExtListBox.Items.Count - 1 do
  508.           Filter := Filter + AppExpert.ExtListBox.Items[I] + '|';
  509.         if Filter[Length(Filter)] = '|' then Dec(Filter[0]);
  510.  
  511.         FmtWrite(TextStream, CodeSnipets[csOpenDialogObject], [Filter]);
  512.         FmtWrite(TextStream, CodeSnipets[csSaveDialogObject], [Filter]);
  513.         WriteSnipet(TextStream, csPrintDialogObject);
  514.         WriteSnipet(TextStream, csPrintSetupDialogObject);
  515.       end;
  516.  
  517.     end;
  518.  
  519.     if AppExpert.cbStatusLine.Checked then
  520.       WriteSnipet(TextStream, csStatusLineObject);
  521.  
  522.     { create speedbuttons }
  523.     if AppExpert.SpeedButtonCount > 0 then
  524.     begin
  525.       WriteSnipet(TextStream, csSpeedbarObject);
  526.  
  527.       ButtonNumber := 0;
  528.       ButtonX := 8;
  529.  
  530.       for I := 0 to AppExpert.SpeedButtonCount - 1 do
  531.       begin
  532.         if AppExpert.SpeedButtonID[I] > -1 then
  533.         begin
  534.           Inc(ButtonNumber);
  535.           ButtonID := AppExpert.SpeedButtonID[I] - sMenuItemTextBase;
  536.           ButtonMethod := LoadStr(ButtonID + sMenuProcNames);
  537.           ButtonHint := LoadStr(ButtonID + sHintBase);
  538.           FmtWrite(TextStream, CodeSnipets[csSpeedButtonObject],
  539.             [ButtonNumber, ButtonX, ButtonMethod, ButtonHint]);
  540.           WriteGlyphData(TextStream, ButtonID + 100);
  541.           Inc(ButtonX, ButtonWidth - 1);
  542.         end
  543.         else Inc(ButtonX, SpaceWidth);
  544.       end;
  545.  
  546.       FmtWrite(TextStream, '  end'#13#10, [nil]);
  547.     end;
  548.  
  549.     FmtWrite(TextStream, 'end'#13#10, [nil]);
  550.  
  551.     { reset the text stream for conversion }
  552.     TextStream.Position := 0;
  553.  
  554.     FormStream := TFileStream.Create(FormName, fmCreate);
  555.     try
  556.       ObjectTextToResource(TextStream, FormStream);
  557.     finally
  558.       FormStream.Free;
  559.     end;
  560.  
  561.   finally
  562.     TextStream.Free;
  563.   end;
  564. end;
  565.  
  566. { interface procedure }
  567. procedure ApplicationExpert(ToolServices: TIToolServices);
  568. var
  569.   D: TAppExpert;
  570.   UsesClause: string;
  571.   ProjectName: TFileName;
  572. begin
  573.   D := TAppExpert.Create(Application);
  574.   try
  575.     if D.ShowModal = mrOK then
  576.     begin
  577.  
  578.       InitCodeGeneration;
  579.       try
  580.         ProjectName := ExpandFileName(GenerateProjectSource(D));
  581.         GenerateMainSourceFile(D);
  582.         GenerateMainFormFile(D);
  583.       finally
  584.         DoneCodeGeneration;
  585.       end;
  586.  
  587.       { open the new project }
  588.       if (ToolServices <> nil) and ToolServices.CloseProject then
  589.         ToolServices.OpenProject(ProjectName);
  590.     end;
  591.   finally
  592.     D.Free;
  593.   end;
  594. end;
  595.  
  596. function EditFilterInfo(var Filter: string): Boolean;
  597. var
  598.   D: TFilterDlg;
  599. begin
  600.   D := TFilterDlg.Create(Application);
  601.   try
  602.     D.Filter := Filter;
  603.     Result := D.ShowModal = mrOK;
  604.     if Result then Filter := D.Filter;
  605.   finally
  606.     D.Free;
  607.   end;
  608. end;
  609.  
  610. procedure ClearButtonImages(List: TList);
  611. var
  612.   I: Integer;
  613. begin
  614.   for I := 0 to List.Count - 1 do
  615.     TButtonImage(List[I]).Free;
  616.   List.Clear;
  617. end;
  618.  
  619. { TButtonImage }
  620. constructor TButtonImage.Create;
  621. begin
  622.   FBitmap := TBitmap.Create;
  623.   FNumGlyphs := 1;
  624. end;
  625.  
  626. destructor TButtonImage.Destroy;
  627. begin
  628.   FBitmap.Free;
  629.   inherited Destroy;
  630. end;
  631.  
  632. procedure TButtonImage.SetBitmapID(Value: Word);
  633. begin
  634.   if FBitmapID <> Value then
  635.   begin
  636.     FBitmapID := Value;
  637.     FBitmap.Handle := LoadBitmap(HInstance, PChar(FBitmapID));
  638.   end;
  639. end;
  640.  
  641. procedure TButtonImage.Draw(Canvas: TCanvas; X, Y: Integer);
  642. var
  643.   BX, BY: Integer;
  644.   Target: TRect;
  645.   Source: TRect;
  646.   SavePen, SaveBrush: TColor;
  647. begin
  648.   with Canvas do
  649.   begin
  650.     SavePen := Canvas.Pen.Color;
  651.     SaveBrush := Canvas.Brush.Color;
  652.  
  653.     Target := DrawButtonFace(Canvas, Bounds(X, Y, DefaultButtonSize.X,
  654.       DefaultButtonSize.Y), 1, bsWin31, False, False, False);
  655.  
  656.     { draw bitmap }
  657.     BX := FBitmap.Width div FNumGlyphs;
  658.     if BX > 0 then
  659.     begin
  660.       Target := Bounds(X, Y, BX, FBitmap.Height);
  661.       OffsetRect(Target, (DefaultButtonSize.X div 2) - (BX div 2),
  662.         (DefaultButtonSize.Y div 2) - (FBitmap.Height div 2));
  663.       Source := Bounds(0, 0, BX, FBitmap.Height);
  664.       BrushCopy(Target, FBitmap, Source,
  665.         FBitmap.Canvas.Pixels[0, FBitmap.Height - 1]);
  666.     end;
  667.  
  668.     Canvas.Pen.Color := SavePen;
  669.     Canvas.Brush.Color := SaveBrush;
  670.   end;
  671. end;
  672.  
  673.  
  674. { TAppExpert }
  675. procedure TAppExpert.FormCreate(Sender: TObject);
  676. var
  677.   ID: Word;
  678.   ButtonImage: TButtonImage;
  679. begin
  680.   SpeedList := TList.Create;
  681.   ButtonList := TList.Create;
  682.   SpeedPointer := TBitmap.Create;
  683.   SpeedPointer.Handle := LoadBitmap(HInstance, 'SPEEDPOINTER');
  684.   Offscreen := TBitmap.Create;
  685.   Offscreen.Width := SpeedBar.Width;
  686.   Offscreen.Height := SpeedBar.Height;
  687.  
  688.   SampleBmp := TBitmap.Create;
  689.  
  690.   { fill the MenuItemList with the speedbuttons }
  691.   for ID := sMenuItemTextBase to sMenuItemTextBase + MenuItemCount - 1 do
  692.   begin
  693.     ButtonImage := TButtonImage.Create;
  694.     ButtonImage.NumGlyphs := 2;
  695.     ButtonImage.BitmapID := ID;
  696.     ButtonList.Add(ButtonImage);
  697.   end;
  698.  
  699.   { This is required to prevent the speedbar from erasing its background
  700.     each time it paints.  This dramatically reduces (eliminates) any
  701.     flicker when painting. (Try commenting out this line to see the
  702.     difference) }
  703.   SpeedBar.ControlStyle := [csOpaque];
  704.  
  705.   NoteBook.PageIndex := FirstPage;
  706.   SampleBmp.Handle := LoadBitmap(HInstance, SampleBitmaps[FirstPage]);
  707.  
  708.   RefreshButtons;
  709. end;
  710.  
  711. procedure TAppExpert.FormDestroy(Sender: TObject);
  712. begin
  713.   ClearButtonImages(ButtonList);
  714.   ButtonList.Free;
  715.   SpeedList.Free;
  716.   SpeedPointer.Free;
  717.   Offscreen.Free;
  718.   SampleBmp.Free;
  719. end;
  720.  
  721. function TAppExpert.HasMenus: Boolean;
  722. begin
  723.   Result := (cbFileMenu.Checked) or (cbEditMenu.Checked) or
  724.     (cbWindowMenu.Checked) or (cbHelpMenu.Checked);
  725. end;
  726.  
  727. { calculate which page is next based on current page and settings.
  728.   -1 = last page
  729.   -2 = cannot move in requested direction }
  730. function TAppExpert.NextPage(Direction: TMoveDirection): Integer;
  731. var
  732.   CurPage: Integer;
  733. begin
  734.   Result := -2;
  735.   CurPage := Notebook.PageIndex;
  736.  
  737.   case Direction of
  738.  
  739.     mdNoMove: if CurPage = LastPage then Result := -1
  740.       else Result := 0;
  741.  
  742.     mdPrevious:
  743.       begin
  744.         case CurPage of
  745.           pgMenus: begin { do nothing } end;
  746.           pgExtensions: Result := pgMenus;
  747.           pgSpeedbar: if cbFileMenu.Checked then Result := pgExtensions
  748.             else Result := pgMenus;
  749.           pgAppInfo: if HasMenus then Result := pgSpeedbar
  750.             else Result := pgMenus;
  751.         end;
  752.       end;
  753.  
  754.     mdNext:
  755.       begin
  756.         case CurPage of
  757.           pgMenus:
  758.             if cbFileMenu.Checked then Result := pgExtensions
  759.             else if HasMenus then Result := pgSpeedbar
  760.             else Result := pgAppInfo;
  761.           pgExtensions: Result := pgSpeedbar;
  762.           pgSpeedbar: Result := pgAppInfo;
  763.           pgAppInfo: Result := -1;
  764.         end;
  765.       end;
  766.   end;
  767. end;
  768.  
  769. procedure TAppExpert.RefreshButtons;
  770. var
  771.   NewPage: Integer;
  772. begin
  773.   case NextPage(mdNoMove) of
  774.    -1:
  775.      begin
  776.        NextButton.Caption := LoadStr(sCreate);
  777.        NextButton.Glyph.Handle := LoadBitmap(HInstance, 'CREATE');
  778.        NextButton.NumGlyphs := 1;
  779.      end;
  780.     0:
  781.       begin
  782.         NextButton.Caption := LoadStr(sNext);
  783.         NextButton.Glyph.Handle := LoadBitmap(HInstance, 'NEXT');
  784.         NextButton.NumGlyphs := 2;
  785.       end;
  786.   end;
  787.   case NextPage(mdPrevious) of
  788.     -2: PrevButton.Enabled := False;
  789.     else PrevButton.Enabled := True;
  790.   end;
  791. end;
  792.  
  793. procedure RemoveItems(List: TList; MenuIndex: TMainItems);
  794. var
  795.   StartID: Integer;
  796.   EndID: Integer;
  797.   I: Integer;
  798.   ButtonImage: TButtonImage;
  799. begin
  800.   StartID := sMenuItemTextBase + MenuItemOffsets[MenuIndex];
  801.   EndID := StartID + MenuItemCounts[MenuIndex];
  802.  
  803.   I := 0;
  804.  
  805.   while I < List.Count do
  806.   begin
  807.     ButtonImage := TButtonImage(List[I]);
  808.     if (ButtonImage <> nil) and (ButtonImage.BitmapID < EndID) and
  809.       (ButtonImage.BitmapID >= StartID) then
  810.       List.Delete(I)
  811.     else Inc(I);
  812.   end;
  813. end;
  814.  
  815. procedure TAppExpert.MenuClicked(Sender: TObject);
  816. var
  817.   MenuIndex: TMainItems;
  818.   MenuOn: Boolean;
  819. begin
  820.   { a menu category has been turned on/off }
  821.   for MenuIndex := Low(TMainItems) to High(TMainItems) do
  822.   begin
  823.     case MenuIndex of
  824.       mmFile: MenuOn := cbFileMenu.Checked;
  825.       mmEdit: MenuOn := cbEditMenu.Checked;
  826.       mmWindow: MenuOn := cbWindowMenu.Checked;
  827.       mmHelp: MenuOn := cbHelpMenu.Checked;
  828.     end;
  829.     if not MenuOn then
  830.     begin
  831.       RemoveItems(SpeedList, MenuIndex);
  832.       FSpeedIndex := 0;
  833.     end;
  834.     if MenuList.ItemIndex = Ord(MenuIndex) then
  835.       MenuListClick(Self);
  836.   end;
  837. end;
  838.  
  839. function TAppExpert.ValidateInfo: Boolean;
  840. begin
  841.   Result := False;
  842.   if AppName.Text = '' then
  843.   begin
  844.     MessageDlg(LoadStr(sAppNameRequired), mtError, [mbOK], 0);
  845.     Exit;
  846.   end;
  847.   if not IsValidIdent(AppName.Text) then
  848.   begin
  849.     MessageDlg(LoadStr(sInvalidAppName), mtError, [mbOK], 0);
  850.     Exit;
  851.   end;
  852.   if not DirectoryExists(AppPath.Text) then
  853.   begin
  854.     MessageDlg(LoadStr(sInvalidPath), mtError, [mbOK], 0);
  855.     Exit;
  856.   end;
  857.   Result := True;
  858. end;
  859.  
  860. procedure TAppExpert.NextPrevClick(Sender: TObject);
  861. var
  862.   NewPage: Integer;
  863. begin
  864.   if Sender = PrevButton then NewPage := NextPage(mdPrevious)
  865.   else NewPage := NextPage(mdNext);
  866.  
  867.   case NewPage of
  868.    -1: if ValidateInfo then ModalResult := mrOK;
  869.    -2: begin { do nothing } end;
  870.     else
  871.     begin
  872.       if SampleBitmaps[NewPage] <> nil then
  873.       begin
  874.         SampleBmp.Handle := LoadBitmap(HInstance, SampleBitmaps[NewPage]);
  875.         Sample.Invalidate;
  876.       end;
  877.       NoteBook.PageIndex := NewPage;
  878.     end;
  879.   end;
  880.   RefreshButtons;
  881. end;
  882.  
  883. { draw the file extension list box }
  884. procedure TAppExpert.DrawExtension(Control: TWinControl; Index: Integer;
  885.   Rect: TRect; State: TOwnerDrawState);
  886. var
  887.   P: Integer;
  888.   R: TRect;
  889.   C: array[0..255] of Char;
  890.   S: string;
  891. begin
  892.   { find the separator in the string }
  893.   P := Pos('|', ExtListBox.Items[Index]);
  894.  
  895.   { adjust the rectangle so we draw only the left "column" }
  896.   R := Rect;
  897.  
  898.   { draw the filter description }
  899.   S := Copy(ExtListBox.Items[Index], 1, P - 1);
  900.   R.Right := R.Left + ExtHeader.SectionWidth[0];
  901.   ExtTextOut(ExtListBox.Canvas.Handle, R.Left, R.Top, ETO_CLIPPED or
  902.     ETO_OPAQUE, @R, StrPCopy(C, S), Length(S), nil);
  903.  
  904.   { move the rectangle to the next column }
  905.   R.Left := R.Right;
  906.   R.Right := Rect.Right;
  907.   S := Copy(ExtListBox.Items[Index], P + 1, 255);
  908.   ExtTextOut(ExtListBox.Canvas.Handle, R.Left, R.Top, ETO_CLIPPED or
  909.     ETO_OPAQUE, @R, StrPCopy(C, S), Length(S), nil);
  910. end;
  911.  
  912. procedure TAppExpert.HeaderSized(Sender: TObject; ASection,
  913.   AWidth: Integer);
  914. begin
  915.   ExtListBox.Invalidate;
  916. end;
  917.  
  918. procedure TAppExpert.AddClick(Sender: TObject);
  919. var
  920.   Filter: string;
  921. begin
  922.   Filter := '';
  923.   if EditFilterInfo(Filter) then
  924.     ExtListBox.Items.Add(Filter);
  925. end;
  926.  
  927. procedure TAppExpert.EditClick(Sender: TObject);
  928. var
  929.   Filter: string;
  930. begin
  931.   if ExtListBox.ItemIndex > -1 then
  932.   begin
  933.     Filter := ExtListBox.Items[ExtListBox.ItemIndex];
  934.     if EditFilterInfo(Filter) then
  935.       ExtListBox.Items[ExtListBox.ItemIndex] := Filter;
  936.   end;
  937. end;
  938.  
  939. procedure TAppExpert.DeleteClick(Sender: TObject);
  940. begin
  941.   if ExtListBox.ItemIndex > -1 then
  942.     ExtListBox.Items.Delete(ExtListBox.ItemIndex);
  943. end;
  944.  
  945. procedure TAppExpert.MoveClick(Sender: TObject);
  946. var
  947.   Delta: Integer;
  948.   NewPos: Integer;
  949. begin
  950.   if Sender = UpButton then Delta := -1
  951.   else if Sender = DownButton then Delta := 1
  952.   else Delta := 0;
  953.  
  954.   if Delta <> 0 then
  955.   begin
  956.     NewPos := ExtListBox.ItemIndex + Delta;
  957.     if (NewPos >= 0) and (NewPos < ExtListBox.Items.Count) then
  958.     begin
  959.       ExtListBox.Items.Move(ExtListBox.ItemIndex, NewPos);
  960.       ExtListBox.ItemIndex := NewPos;
  961.     end;
  962.   end;
  963. end;
  964.  
  965. { return the rectangle of the specified speedbutton or space }
  966. function TAppExpert.SpeedButtonRect(Index: Integer): TRect;
  967. var
  968.   I: Integer;
  969.   X: Integer;
  970. begin
  971.   X := 10;  { first usable position }
  972.  
  973.   for I := 0 to Index - 1 do
  974.     if SpeedList[I] = nil then Inc(X, DefaultButtonSpace)
  975.     else Inc(X, DefaultButtonSize.X - 1);
  976.  
  977.   Result := Bounds(X, 5, DefaultButtonSize.X, DefaultButtonSize.Y);
  978.   if (Index < SpeedList.Count) and (SpeedList[Index] = nil) then
  979.     Result.Right := Result.Left + DefaultButtonSpace;
  980. end;
  981.  
  982. { return an index into SpeedList from the TPoint }
  983. function TAppExpert.SpeedButtonAtPos(Pos: TPoint): Integer;
  984. var
  985.   R: TRect;
  986.   I: Integer;
  987. begin
  988.   for I := 0 to SpeedList.Count - 1 do
  989.   begin
  990.     R := SpeedButtonRect(I);
  991.     if PtInRect(R, Pos) then
  992.     begin
  993.       Result := I;
  994.       Exit;
  995.     end;
  996.   end;
  997.   Result := -1;
  998. end;
  999.  
  1000. function TAppExpert.GetSpeedButtonCount: Integer;
  1001. begin
  1002.   Result := SpeedList.Count;
  1003. end;
  1004.  
  1005. function TAppExpert.GetSpeedButtonID(Value: Integer): Integer;
  1006. var
  1007.   ButtonImage: TButtonImage;
  1008. begin
  1009.   ButtonImage := TButtonImage(SpeedList[Value]);
  1010.   if ButtonImage <> nil then Result := ButtonImage.BitmapID
  1011.   else Result := -1;
  1012. end;
  1013.  
  1014. procedure TAppExpert.SpeedbarPaint(Sender: TObject);
  1015. var
  1016.   I: Integer;
  1017.   ButtonImage: TButtonImage;
  1018.   X: Integer;
  1019.   R: TRect;
  1020. begin
  1021.   with Offscreen.Canvas do
  1022.   begin
  1023.     Pen.Color := clWindowFrame;
  1024.     Brush.Style := bsClear;
  1025.     Brush.Color := SpeedBar.Color;
  1026.  
  1027.     Rectangle(1, 1, SpeedBar.Width - 1, SpeedBar.Height - 1);
  1028.     Pen.Color := clBtnShadow;
  1029.     PolyLine([Point(0, Speedbar.Height - 1), Point(0, 0),
  1030.       Point(SpeedBar.Width - 1, 0)]);
  1031.     Pen.Color := clBtnHighlight;
  1032.     PolyLine([ Point(SpeedBar.Width - 1, 0),
  1033.       Point(SpeedBar.Width - 1, SpeedBar.Height)]);
  1034.   end;
  1035.  
  1036.   { Draw the buttons in the list }
  1037.   X := 10;
  1038.   for I := 0 to SpeedList.Count - 1 do
  1039.   begin
  1040.     ButtonImage := TButtonImage(SpeedList[I]);
  1041.     if ButtonImage = nil then
  1042.     begin
  1043.       Offscreen.Canvas.Brush.Style := bsSolid;
  1044.       Offscreen.Canvas.Brush.Color := clBtnShadow;
  1045.       R := Bounds(X + 2, 5, DefaultButtonSpace - 3, DefaultButtonSize.Y - 2);
  1046.       Offscreen.Canvas.FillRect(R);
  1047.       Inc(X, DefaultButtonSpace);
  1048.     end
  1049.     else
  1050.     begin
  1051.       Offscreen.Canvas.Brush.Style := bsSolid;
  1052.       ButtonImage.Draw(Offscreen.Canvas, X, 4);
  1053.       Inc(X, DefaultButtonSize.X - 1);
  1054.     end;
  1055.  
  1056.     if X + (DefaultButtonSize.X * 2) > SpeedBar.Width then Break;
  1057.  
  1058.     { draw the insertion point }
  1059.     R := SpeedButtonRect(FSpeedIndex);
  1060.     OffsetRect(R, -5, 0);
  1061.     R.Top := R.Bottom + 2;
  1062.     R.Bottom := R.Top + SpeedPointer.Height;
  1063.     R.Right := R.Left + SpeedPointer.Width;
  1064.     Offscreen.Canvas.Brush.Color := SpeedBar.Color;
  1065.     Offscreen.Canvas.BrushCopy(R, SpeedPointer, Rect(0, 0, SpeedPointer.Width,
  1066.       SpeedPointer.Height), clWhite);
  1067.   end;
  1068.   SpeedBar.Canvas.Draw(0, 0, Offscreen);
  1069. end;
  1070.  
  1071. { The list of menus was clicked }
  1072. procedure TAppExpert.MenuListClick(Sender: TObject);
  1073. var
  1074.   ID: Word;
  1075.   I: Integer;
  1076.   ButtonIndex: Integer;
  1077.   MenuOn: Boolean;
  1078. begin
  1079.   if MenuList.ItemIndex > -1 then
  1080.   begin
  1081.     ID := sMenuItemTextBase + MenuItemOffsets[ TMainItems(MenuList.ItemIndex) ];
  1082.  
  1083.     MenuItemList.Items.BeginUpdate;
  1084.  
  1085.     try
  1086.       MenuItemList.Clear;
  1087.  
  1088.       case MenuList.ItemIndex of
  1089.         0: MenuOn := cbFileMenu.Checked;
  1090.         1: MenuOn := cbEditMenu.Checked;
  1091.         2: MenuOn := cbWindowMenu.Checked;
  1092.         3: MenuOn := cbHelpMenu.Checked;
  1093.       end;
  1094.  
  1095.       if MenuOn then
  1096.       begin
  1097.         { load the list box with the buttons and text }
  1098.         for I := 0 to MenuItemCounts[ TMainItems(MenuList.ItemIndex) ] - 1 do
  1099.         begin
  1100.           ButtonIndex := I + MenuItemOffsets[ TMainItems(MenuList.ItemIndex) ];
  1101.           MenuItemList.Items.AddObject(LoadStr(ID + I), ButtonList[ButtonIndex]);
  1102.         end;
  1103.       end;
  1104.  
  1105.     finally
  1106.       MenuItemList.Items.EndUpdate;
  1107.     end;
  1108.   end;
  1109. end;
  1110.  
  1111. procedure TAppExpert.DrawMenuItem(Control: TWinControl; Index: Integer;
  1112.   Rect: TRect; State: TOwnerDrawState);
  1113. var
  1114.   ButtonImage: TButtonImage;
  1115.   R: TRect;
  1116.   C: array[0..255] of Char;
  1117. begin
  1118.   ExtTextOut(MenuItemList.Canvas.Handle, R.Left, R.Top, ETO_OPAQUE,
  1119.     @Rect, nil, 0, nil);
  1120.   ButtonImage := TButtonImage(MenuItemList.Items.Objects[Index]);
  1121.   ButtonImage.Draw(MenuItemList.Canvas, Rect.Left + 2, Rect.Top + 1);
  1122.  
  1123.   R := Rect;
  1124.   Inc(R.Left, DefaultButtonSize.X + 2 + 4);
  1125.   DrawText(MenuItemList.Canvas.Handle,
  1126.     StrPCopy(C, MenuItemList.Items[Index]), -1, R, DT_VCENTER or DT_SINGLELINE);
  1127. end;
  1128.  
  1129. { Insert the current button into the speedbar }
  1130. procedure TAppExpert.InsertClick(Sender: TObject);
  1131. var
  1132.   ButtonImage: TButtonImage;
  1133. begin
  1134.   if MenuItemList.ItemIndex > -1 then
  1135.   begin
  1136.     with MenuItemList do
  1137.       ButtonImage := TButtonImage(Items.Objects[ItemIndex]);
  1138.     if FSpeedIndex < SpeedList.Count then
  1139.       SpeedList.Insert(FSpeedIndex, ButtonImage)
  1140.     else
  1141.       SpeedList.Add(ButtonImage);
  1142.     Inc(FSpeedIndex);
  1143.     SpeedBar.Invalidate;
  1144.   end;
  1145. end;
  1146.  
  1147. procedure TAppExpert.SpaceClick(Sender: TObject);
  1148. begin
  1149.   if FSpeedIndex < SpeedList.Count then
  1150.     SpeedList.Insert(FSpeedIndex, nil)
  1151.   else
  1152.     SpeedList.Add(nil);
  1153.   Inc(FSpeedIndex);
  1154.   SpeedBar.Invalidate;
  1155. end;
  1156.  
  1157. procedure TAppExpert.RemoveClick(Sender: TObject);
  1158. begin
  1159.   if FSpeedIndex < SpeedList.Count then
  1160.   begin
  1161.     SpeedList.Delete(FSpeedIndex);
  1162.     if FSpeedIndex > SpeedList.Count then
  1163.       FSpeedIndex := SpeedList.Count;
  1164.     SpeedBar.Invalidate;
  1165.   end;
  1166. end;
  1167.  
  1168. { The mouse was clicked in the speedbar area }
  1169. procedure TAppExpert.SpeedMouseDown(Sender: TObject; Button: TMouseButton;
  1170.   Shift: TShiftState; X, Y: Integer);
  1171. var
  1172.   Index: Integer;
  1173. begin
  1174.   Index := SpeedButtonAtPos(Point(X, Y));
  1175.   if Index <> -1 then FSpeedIndex := Index
  1176.   else FSpeedIndex := SpeedList.Count;
  1177.   Speedbar.Invalidate;
  1178. end;
  1179.  
  1180. procedure TAppExpert.BrowseClick(Sender: TObject);
  1181. var
  1182.   D: string;
  1183. begin
  1184.   D := AppPath.Text;
  1185.   if SelectDirectory(D, [sdAllowCreate, sdPrompt, sdPerformCreate], 0) then
  1186.     AppPath.Text := D;
  1187. end;
  1188.  
  1189. procedure TAppExpert.SamplePaint(Sender: TObject);
  1190. begin
  1191.   if SampleBmp <> nil then
  1192.     Sample.Canvas.Draw(0, 0, SampleBmp);
  1193. end;
  1194.  
  1195. end.
  1196.