home *** CD-ROM | disk | FTP | other *** search
/ Computerworld 1996 March / Computerworld_1996-03_cd.bin / idg_cd3 / utility / applau13 / main.pas < prev    next >
Pascal/Delphi Source File  |  1996-02-14  |  26KB  |  927 lines

  1. unit Main;
  2.  
  3. interface
  4.  
  5. uses
  6.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  7.   Forms, Dialogs, ShellAPI, ExtCtrls, StdCtrls, Buttons, IniFiles, Menus;
  8.  
  9. const
  10.   ProgramStr      = 'AppLaunch';
  11.   VersionStr      = '1.3';
  12.   CopyrightStr    = '⌐ Copyright 1994-1995 by Anders Ohlsson';
  13.   EmailAdrStr     = 'ao@e.kth.se, ao@it.kth.se or ao@sto.foa.se';
  14.  
  15.   CopyrightNotice : String = ProgramStr+' '+VersionStr+' - '+
  16.                              CopyrightStr+' - '+EmailAdrStr;
  17.  
  18.   IniFile         : String = 'LAUNCH.INI';
  19.   WriFile         : String = 'LAUNCH.WRI';
  20.  
  21.   AbsMaxNrOfApps = 32;
  22.   AppWidth       = 36;
  23.  
  24. type
  25.   TSlotRec = record
  26.     Active    : Boolean;
  27.     Command,
  28.     Directory,
  29.     Hint,
  30.     IconFile  : String;
  31.     IconNr    : Word;
  32.     Panel     : TPanel;
  33.     Image     : TImage;
  34.     Icon      : TIcon;
  35.   end;
  36.  
  37.   TMainForm = class(TForm)
  38.     PopupMenu1: TPopupMenu;
  39.     Help1: TMenuItem;
  40.     ViewDocs: TMenuItem;
  41.     About1: TMenuItem;
  42.     Save1: TMenuItem;
  43.     Load1: TMenuItem;
  44.     N1: TMenuItem;
  45.     Exit1: TMenuItem;
  46.     Properties1: TMenuItem;
  47.     N2: TMenuItem;
  48.     StatusBar: TPanel;
  49.     Toggle1: TMenuItem;
  50.     procedure FormCreate(Sender: TObject);
  51.     procedure Load1Click(Sender: TObject);
  52.     procedure Save1Click(Sender: TObject);
  53.     procedure ViewDocsClick(Sender: TObject);
  54.     procedure About1Click(Sender: TObject);
  55.     procedure Exit1Click(Sender: TObject);
  56.     procedure StatusBarDragOver(Sender, Source: TObject; X, Y: Integer;
  57.       State: TDragState; var Accept: Boolean);
  58.     procedure StatusBarDragDrop(Sender, Source: TObject; X, Y: Integer);
  59.     procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  60.     procedure PopupMenu1Popup(Sender: TObject);
  61.     procedure Properties1Click(Sender: TObject);
  62.     procedure Toggle1Click(Sender: TObject);
  63.     procedure FormActivate(Sender: TObject);
  64.   private
  65.     { Private declarations }
  66.     Slot         : array [0..AbsMaxNrOfApps-1] of TSlotRec;
  67.     ExecFailed,
  68.     Modified,
  69.     DragOn       : Boolean;
  70.     MousePos     : TPoint;
  71.     procedure DropFiles(var Msg : TWMDropFiles); message WM_DROPFILES;
  72.     procedure ApplicationHint(Sender : TObject);
  73.     procedure ImageDragOver(Sender, Source : TObject; X, Y : Integer;
  74.                             State : TDragState; var Accept : Boolean);
  75.     procedure ImageDragDrop(Sender, Source : TObject; X, Y : Integer);
  76.     procedure ImageDblClick(Sender : TObject);
  77.     procedure ImageMouseDown(Sender : TObject; Button : TMouseButton;
  78.                              Shift : TShiftState; X, Y : Integer);
  79.     procedure ImageMouseUp(Sender : TObject; Button : TMouseButton;
  80.                            Shift : TShiftState; X, Y : Integer);
  81.     procedure ImageMouseMove(Sender : TObject; Shift : TShiftState;
  82.                              X, Y : Integer);
  83.     procedure LoadLayout;
  84.     procedure SaveLayout;
  85.   public
  86.     { Public declarations }
  87.   end;
  88.  
  89. var
  90.   MainForm     : TMainForm;
  91.   ScreenWidth,
  92.   MaxNrOfApps,
  93.   NrOfApps     : Integer;
  94.  
  95. implementation
  96.  
  97. uses Info, About, Resource;
  98.  
  99. {$R *.DFM}
  100.  
  101. {
  102.   DropFiles is called when a file is dragged from the file manager and
  103.   dropped onto the application
  104. }
  105. procedure TMainForm.DropFiles(var Msg : TWMDropFiles);
  106. var
  107.   FileName,
  108.   IconFile   : array [0..255] of Char;
  109.   NrOfFiles,
  110.   BufSize,
  111.   IconNr     : Word;
  112.   TheIcon    : HIcon;
  113.   Point      : TPoint;
  114.   j          : Integer;
  115. begin
  116.   { If the bar is full, inform the user and exit }
  117.   if NrOfApps = MaxNrOfApps then begin
  118.     MessageDlg('Sorry! The bar is full...',mtError,[mbOK],0);
  119.     DragFinish(Msg.Drop);
  120.     Exit;
  121.   end;
  122.  
  123.   { Get the coordinates where the file was dropped }
  124.   DragQueryPoint(Msg.Drop,Point);
  125.  
  126.   { Get the app number from the drop point }
  127.   j := Point.x div AppWidth;
  128.  
  129.   { If the slot is taken, inform the user and exit }
  130.   if Slot[j].Active then begin
  131.     MessageDlg('Sorry! That slot is already taken...',mtError,[mbOK],0);
  132.     DragFinish(Msg.Drop);
  133.     Exit;
  134.   end;
  135.  
  136.   { Get the number of files that were dropped on us }
  137.   NrOfFiles := DragQueryFile(Msg.Drop,Word(-1),FileName,BufSize);
  138.  
  139.   { If more than one file was dropped, inform the user and exit }
  140.   if NrOfFiles > 1 then begin
  141.     MessageDlg('Hey! Only one file at a time...',mtError,[mbOK],0);
  142.     DragFinish(Msg.Drop);
  143.     Exit;
  144.   end;
  145.  
  146.   { Get the name of the file that was dropped on us }
  147.   DragQueryFile(Msg.Drop,0,FileName,BufSize);
  148.  
  149.   { Initialize the info form }
  150.   InfoForm.CmdStr.Text := StrPas(FileName);
  151.   InfoForm.DirStr.Text := ExtractFilePath(StrPas(FileName));
  152.   InfoForm.InfoStr.Text := '';
  153.   InfoForm.IconFileStr.Text := '';
  154.   InfoForm.IconNrStr.Text := '';
  155.  
  156.   { Show the info form }
  157.   InfoForm.ShowModal;
  158.  
  159.   { If the OK button was pressed we go ahead }
  160.   if InfoForm.ModalResult = idOK then begin
  161.     { Set some attributes for this slot }
  162.     Slot[j].Active := True;
  163.     Slot[j].Command := InfoForm.CmdStr.Text;
  164.     Slot[j].Directory := InfoForm.DirStr.Text;
  165.     Slot[j].Hint := InfoForm.InfoStr.Text;
  166.     Slot[j].Panel.Hint := '| '+Slot[j].Hint;
  167.     if InfoForm.IconFileStr.Text <> '' then begin
  168.       Slot[j].IconFile := InfoForm.IconFileStr.Text;
  169.       StrPCopy(IconFile,Slot[j].IconFile);
  170.     end
  171.     else begin
  172.       Slot[j].IconFile := '';
  173.       StrPCopy(IconFile,InfoForm.CmdStr.Text);
  174.     end;
  175.     try
  176.       IconNr := StrToInt(InfoForm.IconNrStr.Text);
  177.     except
  178.       on EConvertError do
  179.         IconNr := 0;
  180.     end;
  181.     Slot[j].IconNr := IconNr;
  182.  
  183.     { Extract the icon }
  184.     TheIcon := ExtractIcon(HInstance,IconFile,IconNr);
  185.     if TheIcon < 2 then
  186.       TheIcon := LoadIcon(0,idi_Question);
  187.  
  188.     { Set some attributes for this slot }
  189.     Slot[j].Icon.Handle := TheIcon;
  190.  
  191.     { Initialize the image and draw the icon }
  192.     with Slot[j].Image.Canvas do begin
  193.       Pen.Color := clSilver;
  194.       Pen.Style := psSolid;
  195.       Brush.Color := clSilver;
  196.       Brush.Style := bsSolid;
  197.       Rectangle(0,0,AppWidth-3,AppWidth-3);
  198.       Draw(0,0,Slot[j].Icon);
  199.     end;
  200.  
  201.     { Increment the number of apps }
  202.     Inc(NrOfApps);
  203.  
  204.     { Raise modification flag }
  205.     Modified := True;
  206.   end;
  207.  
  208.   { Tell Windows that we're done for now }
  209.   DragFinish(Msg.Drop);
  210. end;
  211.  
  212. {
  213.   LoadLayout loads the layout from the INI file
  214. }
  215. procedure TMainForm.LoadLayout;
  216. var
  217.   MyIniFile      : TIniFile;
  218.   MyStringList1,
  219.   MyStringList2  : TStringList;
  220.   IconFile       : array [0..255] of Char;
  221.   IconNr         : Word;
  222.   TheIcon        : HIcon;
  223.   i, j           : Integer;
  224.   WasChecked     : Boolean;
  225. begin
  226.   { Reset all slots }
  227.   for i:=0 to MaxNrOfApps-1 do
  228.     with Slot[i] do begin
  229.       { Invalidate the slot }
  230.       Active := False;
  231.       Command := '';
  232.       Directory := '';
  233.       Hint := '';
  234.       IconFile := '';
  235.       IconNr := 0;
  236.       Panel.Hint := '';
  237.  
  238.       { Initialize the image }
  239.       with Image.Canvas do begin
  240.         Pen.Color := clSilver;
  241.         Pen.Style := psSolid;
  242.         Brush.Color := clSilver;
  243.         Brush.Style := bsSolid;
  244.         Rectangle(0,0,AppWidth-3,AppWidth-3);
  245.       end;
  246.     end;
  247.  
  248.   { Create the string lists }
  249.   MyStringList1 := TStringList.Create;
  250.   MyStringList1.Sorted := True; { This will automagically ignore duplicates }
  251.   MyStringList2 := TStringList.Create;
  252.  
  253.   { Create the ini file object }
  254.   MyIniFile := TIniFile.Create(IniFile);
  255.  
  256.   { Remember if the resources was visible before }
  257.   WasChecked := Toggle1.Checked;
  258.  
  259.   { Determine if the resources should be visible or not }
  260.   Toggle1.Checked := MyIniFile.ReadBool(ProgramStr,'Resources',True);
  261.  
  262.   { Read the section that tells us which apps are to be loaded }
  263.   MyIniFile.ReadSection(ProgramStr,MyStringList1);
  264.  
  265.   { Calculate the number of apps we've got }
  266.   NrOfApps := MyStringList1.Count;
  267.   if NrOfApps < 0 then
  268.     NrOfApps := 0;
  269.  
  270.   { Process the apps one by one }
  271.   for i:=0 to NrOfApps-1 do begin
  272.     { Skip if invalid section prefix }
  273.     if Copy(MyStringList1[i],1,3) <> 'App' then
  274.       continue;
  275.  
  276.     { Determine section number, skip if invalid }
  277.     try
  278.       j := StrToInt(Copy(MyStringList1[i],4,2));
  279.     except
  280.       { Skip this app if conversion fails }
  281.       on EConvertError do
  282.         continue;
  283.     end;
  284.  
  285.     { Skip this app if the number is too high }
  286.     if j > MaxNrOfApps-1 then
  287.       continue;
  288.  
  289.     { Read this app's section }
  290.     MyStringList2.Clear;
  291.     MyIniFile.ReadSectionValues(MyStringList1[i],MyStringList2);
  292.  
  293.     { Set some attributes for this slot }
  294.     Slot[j].Active := True;
  295.     Slot[j].Command := MyStringList2.Values['ProgFile'];
  296.     Slot[j].Directory := MyStringList2.Values['WorkDir'];
  297.     Slot[j].Hint := MyStringList2.Values['ProgInfo'];
  298.     Slot[j].Panel.Hint := '| '+Slot[j].Hint;
  299.  
  300.     { Determine where the icon is found }
  301.     Slot[j].IconFile := MyStringList2.Values['IconFile'];
  302.     StrPCopy(IconFile,Slot[j].IconFile);
  303.     if IconFile[0] = #0 then
  304.       StrPCopy(IconFile,Slot[j].Command);
  305.  
  306.     { Determine which icon to get }
  307.     try
  308.       IconNr := StrToInt(MyStringList2.Values['IconNr']);
  309.     except
  310.       on EConvertError do
  311.         IconNr := 0;
  312.     end;
  313.     Slot[j].IconNr := IconNr;
  314.  
  315.     { Extract the icon }
  316.     TheIcon := ExtractIcon(HInstance,IconFile,IconNr);
  317.     if TheIcon < 2 then
  318.       TheIcon := LoadIcon(0,idi_Question);
  319.  
  320.     { Set some attributes for this slot }
  321.     Slot[j].Icon.Handle := TheIcon;
  322.  
  323.     { Draw the icon on the image }
  324.     Slot[j].Image.Canvas.Draw(0,0,Slot[j].Icon);
  325.   end;
  326.  
  327.   { Free the ini file object }
  328.   MyIniFile.Free;
  329.  
  330.   { Free the string lists }
  331.   MyStringList1.Free;
  332.   MyStringList2.Free;
  333.  
  334.   { If we're reloading we need to adjust the resource and main forms }
  335.   if ResourceForm <> nil then begin
  336.     { Enable/disable the resource form accordingly }
  337.     ResourceForm.Visible := Toggle1.Checked;
  338.  
  339.     { Modify the size of the main form accordingly }
  340.     if not WasChecked and Toggle1.Checked then
  341.       Width := Width-3*AppWidth;
  342.     if WasChecked and not Toggle1.Checked then
  343.       Width := Width+3*AppWidth;
  344.   end;
  345.  
  346.   { Clear modification flag }
  347.   Modified := False;
  348. end;
  349.  
  350. {
  351.   SaveLayout saves the layout to the INI file
  352. }
  353. procedure TMainForm.SaveLayout;
  354. var
  355.   MyIniFile : TIniFile;
  356.   Section   : String;
  357.   i         : Integer;
  358.   fp        : TextFile;
  359. begin
  360.   { Destroy the INI file so that we don't collect a lot of garbage }
  361.   AssignFile(fp,IniFile);
  362.   Erase(fp);
  363.  
  364.   { Create the ini file object }
  365.   MyIniFile := TIniFile.Create(IniFile);
  366.  
  367.   { Remember if the resource form was visible or not }
  368.   MyIniFile.WriteBool(ProgramStr,'Resources',Toggle1.Checked);
  369.  
  370.   for i:=0 to MaxNrOfApps-1 do
  371.     with MyIniFile do
  372.       with Slot[i] do
  373.         if Active then begin
  374.           { Determine the section name }
  375.           Section := 'App'+IntToStr(i div 10)+IntToStr(i mod 10);
  376.  
  377.           { Write the AppXX=1 entry in the first section }
  378.           WriteInteger(ProgramStr,Section,1);
  379.  
  380.           { Write the AppXX section }
  381.           WriteString(Section,'ProgFile',Command);
  382.           if Directory <> '' then
  383.             WriteString(Section,'WorkDir',Directory);
  384.           WriteString(Section,'ProgInfo',Hint);
  385.           if IconFile <> '' then
  386.             WriteString(Section,'IconFile',IconFile);
  387.           if IconNr <> 0 then
  388.             WriteInteger(Section,'IconNr',IconNr);
  389.         end;
  390.  
  391.   { Free the ini file object }
  392.   MyIniFile.Free;
  393.  
  394.   { Clear modification flag }
  395.   Modified := False;
  396. end;
  397.  
  398. {
  399.   FormCreate is called when the form is created
  400. }
  401. procedure TMainForm.FormCreate(Sender: TObject);
  402. var
  403.   i : Integer;
  404. begin
  405.   { Tell Windows that we accept dragged files }
  406.   DragAcceptFiles(Handle,True);
  407.  
  408.   { We're not dragging anything yet }
  409.   DragOn := False;
  410.  
  411.   { Set the application's title }
  412.   Application.Title := ProgramStr;
  413.  
  414.   { Set the application's OnHint event handler }
  415.   Application.OnHint := ApplicationHint;
  416.  
  417.   { Set the initial caption for our statusbar }
  418.   StatusBar.Font.Size := 7;
  419.   StatusBar.Caption := CopyrightNotice;
  420.  
  421.   { Calculate the maximum number of apps we can have }
  422.   ScreenWidth := GetSystemMetrics(sm_cxScreen);
  423.   MaxNrOfApps := ScreenWidth div AppWidth;
  424.   if MaxNrOfApps > AbsMaxNrOfApps then
  425.     MaxNrOfApps := AbsMaxNrOfApps;
  426.  
  427.   { Modify our coordinates and size }
  428.   Top := 0;
  429.   Left := 0;
  430.   Width := AppWidth*MaxNrOfApps+1;
  431.   Height := AppWidth+StatusBar.Height;
  432.  
  433.   { Create a panel and an image for each slot }
  434.   for i:=0 to MaxNrOfApps-1 do
  435.     with Slot[i] do begin
  436.       { Create the panel }
  437.       Panel := TPanel.Create(MainForm);
  438.       Panel.Left := AppWidth*i;
  439.       Panel.Top := 0;
  440.       Panel.Width := AppWidth+1;
  441.       Panel.Height := AppWidth+1;
  442.       Panel.BevelOuter := bvRaised;
  443.       Panel.BorderStyle := bsSingle;
  444.       Panel.ShowHint := True;
  445.       Panel.Parent := MainForm;
  446.  
  447.       { Create the image }
  448.       Image := TImage.Create(Panel);
  449.       Image.Align := alClient;
  450.       Image.Parent := Panel;
  451.  
  452.       { Set event handlers for the image }
  453.       Image.OnDragDrop := ImageDragDrop;
  454.       Image.OnDragOver := ImageDragOver;
  455.       Image.OnDblClick := ImageDblClick;
  456.       Image.OnMouseDown := ImageMouseDown;
  457.       Image.OnMouseUp := ImageMouseUp;
  458.       Image.OnMouseMove := ImageMouseMove;
  459.  
  460.       { Create the icon }
  461.       Icon := TIcon.Create;
  462.     end;
  463.  
  464.   { Add path information to our filenames }
  465.   IniFile := ExtractFilePath(Application.ExeName)+IniFile;
  466.   WriFile := ExtractFilePath(Application.ExeName)+WriFile;
  467.  
  468.   { Read the INI file }
  469.   LoadLayout;
  470. end;
  471.  
  472. {
  473.   ApplicationHint is called when the application gets an OnHint message
  474. }
  475. procedure TMainForm.ApplicationHint(Sender : TObject);
  476. begin
  477.   with StatusBar do
  478.     if Application.Hint <> '' then begin
  479.       { If we got a hint, show it }
  480.       Font.Size := 9;
  481.       Alignment := taLeftJustify;
  482.       Caption := Application.Hint;
  483.     end
  484.     else begin
  485.       { If we didn't got a hint, show default }
  486.       Font.Size := 7;
  487.       Alignment := taCenter;
  488.       Caption := CopyrightNotice;
  489.     end;
  490. end;
  491.  
  492. {
  493.   ImageDragOver is called when the user drags a slot over a slot
  494. }
  495. procedure TMainForm.ImageDragOver(Sender, Source : TObject; X, Y : Integer;
  496.                                   State : TDragState; var Accept : Boolean);
  497. var
  498.   i : Integer;
  499. begin
  500.   Accept := False;
  501.  
  502.   if Sender is TImage then begin
  503.     { Find the slot corresponding to the Sender }
  504.     i := 0;
  505.     while i < MaxNrOfApps do
  506.       if Slot[i].Image = Sender then
  507.         break
  508.       else
  509.         Inc(i);
  510.  
  511.     { if OK and not active, accept the item }
  512.     if i < MaxNrOfApps then
  513.       Accept := (Sender <> Source) and not Slot[i].Active;
  514.   end;
  515. end;
  516.  
  517. {
  518.   ImageDragDrop is called when the user drops a slot in a slot
  519. }
  520. procedure TMainForm.ImageDragDrop(Sender, Source : TObject; X, Y : Integer);
  521. var
  522.   dst, src : Integer;
  523. begin
  524.   { Find the slot corresponding to the Sender }
  525.   dst := 0;
  526.   while dst < MaxNrOfApps do
  527.     if Slot[dst].Image = Sender then
  528.       break
  529.     else
  530.       Inc(dst);
  531.  
  532.   { Find the slot corresponding to the Source }
  533.   src := 0;
  534.   while src < MaxNrOfApps do
  535.     if Slot[src].Image = Source then
  536.       break
  537.     else
  538.       Inc(src);
  539.  
  540.   { If source and destination are both OK, go ahead and move }
  541.   if (dst < MaxNrOfApps) and (src < MaxNrOfApps) then begin
  542.     { Copy some attributes }
  543.     Slot[dst].Active := True;
  544.     Slot[dst].Command := Slot[src].Command;
  545.     Slot[dst].Directory := Slot[src].Directory;
  546.     Slot[dst].Hint := Slot[src].Hint;
  547.     Slot[dst].Panel.Hint := Slot[src].Panel.Hint;
  548.     Slot[dst].IconFile := Slot[src].IconFile;
  549.     Slot[dst].IconNr := Slot[src].IconNr;
  550.     Slot[dst].Icon.Assign(Slot[src].Icon);
  551.  
  552.     { Invalidate the source }
  553.     with Slot[src] do begin
  554.       Active := False;
  555.       Command := '';
  556.       Directory := '';
  557.       Hint := '';
  558.       IconFile := '';
  559.       IconNr := 0;
  560.       Panel.Hint := '';
  561.     end;
  562.  
  563.     { Delete the source icon from the image }
  564.     with Slot[src].Image.Canvas do begin
  565.       Pen.Color := clSilver;
  566.       Pen.Style := psSolid;
  567.       Brush.Color := clSilver;
  568.       Brush.Style := bsSolid;
  569.       Rectangle(0,0,AppWidth-3,AppWidth-3);
  570.     end;
  571.  
  572.     { Initialize the destination image and draw the icon }
  573.     with Slot[dst].Image.Canvas do begin
  574.       Pen.Color := clSilver;
  575.       Pen.Style := psSolid;
  576.       Brush.Color := clSilver;
  577.       Brush.Style := bsSolid;
  578.       Rectangle(0,0,AppWidth-3,AppWidth-3);
  579.       Draw(0,0,Slot[dst].Icon);
  580.     end;
  581.  
  582.     { Raise modification flag }
  583.     Modified := True;
  584.   end;
  585. end;
  586.  
  587. {
  588.   ImageDblClick is called when the user double clicks on the application
  589. }
  590. procedure TMainForm.ImageDblClick(Sender : TObject);
  591. var
  592.   CmdStr : array [0..255] of Char;
  593.   i      : Integer;
  594. begin
  595.   ExecFailed := False;
  596.  
  597.   { Find the app corresponding to the Sender }
  598.   i := 0;
  599.   while i < MaxNrOfApps do
  600.     if Slot[i].Image = Sender then
  601.       break
  602.     else
  603.       Inc(i);
  604.  
  605.   { If valid and active, launch the app }
  606.   if i < MaxNrOfApps then
  607.     if Slot[i].Active then begin
  608.       try
  609.         with Slot[i] do begin
  610.           StrPCopy(CmdStr,Command);
  611.           if Directory[Length(Directory)] = '\' then
  612.            { Kill the trailing backslash... The on-line help for ExtractFilePath
  613.              says it should work anyway, but it doesn't... BUG! }
  614.             ChDir(Copy(Directory,1,Length(Directory)-1))
  615.           else
  616.             ChDir(Directory);
  617.         end;
  618.         if WinExec(CmdStr,SW_SHOWNORMAL) < 32 then begin
  619.           MessageDlg('Could not execute '+StrPas(CmdStr)+'.',mtError,[mbOk],0);
  620.           ExecFailed := True; { Otherwise we'll start dragging }
  621.         end;
  622.       except
  623.         on EInOutError do begin
  624.           MessageDlg('Invalid working directory ('+Slot[i].Directory+').',mtError,[mbOk],0);
  625.           ExecFailed := True; { Otherwise we'll start dragging }
  626.         end;
  627.       end;
  628.     end;
  629. end;
  630.  
  631. {
  632.   ImageMouseDown is called when the user presses a mouse button on an image
  633. }
  634. procedure TMainForm.ImageMouseDown(Sender : TObject; Button : TMouseButton;
  635.                                    Shift : TShiftState; X, Y : Integer);
  636. begin
  637.   { If the previous WinExec failed we must invalidate the first MouseDown
  638.     message, otherwise we'll start dragging }
  639.   if ExecFailed then begin
  640.     ExecFailed := False;
  641.     Exit;
  642.   end;
  643.  
  644.   if Button = mbLeft then
  645.     DragOn := True;
  646. end;
  647.  
  648. {
  649.   ImageMouseUp is called when the user releases a mouse button on an image
  650. }
  651. procedure TMainForm.ImageMouseUp(Sender : TObject; Button : TMouseButton;
  652.                                  Shift : TShiftState; X, Y : Integer);
  653. begin
  654.   if Button = mbLeft then
  655.     DragOn := False;
  656. end;
  657.  
  658. {
  659.   ImageMouseMove is called when the user moves the mouse on an image
  660. }
  661. procedure TMainForm.ImageMouseMove(Sender : TObject; Shift : TShiftState;
  662.                                    X, Y : Integer);
  663. var
  664.   i : Integer;
  665. begin
  666.   if DragOn then begin
  667.     { Find the app corresponding to the Sender }
  668.     i := 0;
  669.     while i < MaxNrOfApps do
  670.       if Slot[i].Image = Sender then
  671.         break
  672.       else
  673.         Inc(i);
  674.  
  675.     { If valid and active, start dragging }
  676.     if (i < MaxNrOfApps) and Slot[i].Active then
  677.       Slot[i].Image.BeginDrag(False);
  678.   end;
  679. end;
  680.  
  681. {
  682.   Load1Click is called when the user selects the 'Load layout' option from the menu
  683. }
  684. procedure TMainForm.Load1Click(Sender: TObject);
  685. begin
  686.   if Modified and (MessageDlg('The layout has changed. Reload?',mtWarning,[mbYes,mbNo],0) <> idYes) then
  687.     Exit;
  688.  
  689.   LoadLayout;
  690. end;
  691.  
  692. {
  693.   Save1Click is called when the user selects the 'Save layout' option from the menu
  694. }
  695. procedure TMainForm.Save1Click(Sender: TObject);
  696. begin
  697.   if Modified and (MessageDlg('Are you sure you want to save?',mtWarning,[mbYes,mbNo],0) <> idYes) then
  698.     Exit;
  699.  
  700.   SaveLayout;
  701. end;
  702.  
  703. {
  704.   ViewDocsClick is called when the user selects the 'View documentation' option from the menu
  705. }
  706. procedure TMainForm.ViewDocsClick(Sender: TObject);
  707. var
  708.   CmdStr : array [0..255] of Char;
  709. begin
  710.   { Start WRITE.EXE with our docfile }
  711.   StrPCopy(CmdStr,'write.exe '+WriFile);
  712.   if WinExec(CmdStr,SW_SHOWNORMAL) < 32 then
  713.     MessageDlg('Could not execute "'+StrPas(CmdStr)+'".',mtError,[mbOk],0);
  714. end;
  715.  
  716. {
  717.   About1Click is called when the user selects the 'About AppLaunch' option from the menu
  718. }
  719. procedure TMainForm.About1Click(Sender: TObject);
  720. begin
  721.   AboutForm.ShowModal;
  722. end;
  723.  
  724. {
  725.   Exit1Click is called when the user selects the 'Exit' option from the menu
  726. }
  727. procedure TMainForm.Exit1Click(Sender: TObject);
  728. begin
  729.   Close;
  730. end;
  731.  
  732. {
  733.   StatusBarDragOver is called when the user drags a slot over the status bar
  734. }
  735. procedure TMainForm.StatusBarDragOver(Sender, Source: TObject; X,
  736.   Y: Integer; State: TDragState; var Accept: Boolean);
  737. begin
  738.   Accept := False;
  739.  
  740.   { Accept all slots }
  741.   if Source is TImage then
  742.     Accept := True;
  743. end;
  744.  
  745. {
  746.   StatusBarDragDrop is called when the user drops a slot on the status bar
  747. }
  748. procedure TMainForm.StatusBarDragDrop(Sender, Source: TObject; X,
  749.   Y: Integer);
  750. var
  751.   i : Integer;
  752. begin
  753.   { Find the slot corresponding to the Source }
  754.   i := 0;
  755.   while i < MaxNrOfApps do
  756.     if Slot[i].Image = Source then
  757.       break
  758.     else
  759.       Inc(i);
  760.  
  761.   { If valid and active, delete the app }
  762.   if (i < MaxNrOfApps) and Slot[i].Active then begin
  763.     { Invalidate the slot}
  764.     with Slot[i] do begin
  765.       Active := False;
  766.       Command := '';
  767.       Directory := '';
  768.       Hint := '';
  769.       IconFile := '';
  770.       IconNr := 0;
  771.       Panel.Hint := '';
  772.     end;
  773.  
  774.     { Delete the icon from the image }
  775.     with Slot[i].Image.Canvas do begin
  776.       Pen.Color := clSilver;
  777.       Pen.Style := psSolid;
  778.       Brush.Color := clSilver;
  779.       Brush.Style := bsSolid;
  780.       Rectangle(0,0,AppWidth-3,AppWidth-3);
  781.     end;
  782.  
  783.     { Decrease the number of apps }
  784.     Dec(NrOfApps);
  785.  
  786.     { Raise modification flag }
  787.     Modified := True;
  788.   end;
  789. end;
  790.  
  791. {
  792.   FormCloseQuery is called before the application terminates
  793. }
  794. procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  795. begin
  796.   CanClose := True;
  797.  
  798.   if Modified and (MessageDlg('The layout has changed. Save?',mtWarning,[mbYes,mbNo],0) = idYes) then
  799.     SaveLayout;
  800. end;
  801.  
  802. {
  803.   PopupMenu1Popup is called when the popup menu is activated
  804. }
  805. procedure TMainForm.PopupMenu1Popup(Sender: TObject);
  806. begin
  807.   { Enable the load/save layout options if the layout has been modified,
  808.     otherwise disable them }
  809.   Load1.Enabled := Modified;
  810.   Save1.Enabled := Modified;
  811.  
  812.   { Save current mouse coordinates }
  813.   GetCursorPos(MousePos);
  814.  
  815.   { Adjust mouse coordinates to the position of the form }
  816.   Dec(MousePos.X,MainForm.Left);
  817.   Dec(MousePos.Y,MainForm.Top);
  818. end;
  819.  
  820. {
  821.   Properties1Click is called when the user selects the 'Properties' option from the menu
  822. }
  823. procedure TMainForm.Properties1Click(Sender: TObject);
  824. var
  825.   WasActive : Boolean;
  826.   IconFile  : array [0..255] of Char;
  827.   IconNr    : Word;
  828.   TheIcon   : HIcon;
  829.   j         : Integer;
  830. begin
  831.   j := MousePos.X div AppWidth;
  832.  
  833.   if j < MaxNrOfApps then begin
  834.     { Initialize the info form }
  835.     InfoForm.CmdStr.Text := Slot[j].Command;
  836.     InfoForm.DirStr.Text := Slot[j].Directory;
  837.     InfoForm.InfoStr.Text := Slot[j].Hint;
  838.     InfoForm.IconFileStr.Text := Slot[j].IconFile;
  839.     if Slot[j].IconNr <> 0 then
  840.       InfoForm.IconNrStr.Text := IntToStr(Slot[j].IconNr)
  841.     else
  842.       InfoForm.IconNrStr.Text := '';
  843.  
  844.     { Show the info form }
  845.     InfoForm.ShowModal;
  846.  
  847.     { Remember if this slot was active before }
  848.     WasActive := Slot[j].Active;
  849.  
  850.     { If the OK button was pressed we go ahead }
  851.     if InfoForm.ModalResult = idOK then begin
  852.       { Set some attributes for this slot }
  853.       Slot[j].Active := True;
  854.       Slot[j].Command := InfoForm.CmdStr.Text;
  855.       Slot[j].Directory := InfoForm.DirStr.Text;
  856.       Slot[j].Hint := InfoForm.InfoStr.Text;
  857.       Slot[j].Panel.Hint := '| '+Slot[j].Hint;
  858.       if InfoForm.IconFileStr.Text <> '' then begin
  859.         Slot[j].IconFile := InfoForm.IconFileStr.Text;
  860.         StrPCopy(IconFile,Slot[j].IconFile);
  861.       end
  862.       else begin
  863.         Slot[j].IconFile := '';
  864.         StrPCopy(IconFile,InfoForm.CmdStr.Text);
  865.       end;
  866.       try
  867.         IconNr := StrToInt(InfoForm.IconNrStr.Text);
  868.       except
  869.         on EConvertError do
  870.           IconNr := 0;
  871.       end;
  872.       Slot[j].IconNr := IconNr;
  873.  
  874.       { Extract the icon }
  875.       TheIcon := ExtractIcon(HInstance,IconFile,IconNr);
  876.       if TheIcon < 2 then
  877.         TheIcon := LoadIcon(0,idi_Question);
  878.  
  879.       { Set some attributes for this slot }
  880.       Slot[j].Icon.Handle := TheIcon;
  881.  
  882.       { Initialize the image and draw the icon }
  883.       with Slot[j].Image.Canvas do begin
  884.         Pen.Color := clSilver;
  885.         Pen.Style := psSolid;
  886.         Brush.Color := clSilver;
  887.         Brush.Style := bsSolid;
  888.         Rectangle(0,0,AppWidth-3,AppWidth-3);
  889.         Draw(0,0,Slot[j].Icon);
  890.       end;
  891.  
  892.       { Increment the number of apps }
  893.       if not WasActive then
  894.         Inc(NrOfApps);
  895.  
  896.       { Raise modification flag }
  897.       Modified := True;
  898.     end;
  899.   end;
  900. end;
  901.  
  902. procedure TMainForm.Toggle1Click(Sender: TObject);
  903. begin
  904.   { Toggle the check mark on the popup menu }
  905.   Toggle1.Checked := not Toggle1.Checked;
  906.  
  907.   { Modify the size of the main form accordingly }
  908.   if Toggle1.Checked then
  909.     Width := Width-3*AppWidth
  910.   else
  911.     Width := Width+3*AppWidth;
  912.  
  913.   { Enable/disable the resource form accordingly }
  914.   ResourceForm.Visible := Toggle1.Checked;
  915.  
  916.   { Raise modification flag }
  917.   Modified := True;
  918. end;
  919.  
  920. procedure TMainForm.FormActivate(Sender: TObject);
  921. begin
  922.   PixelsPerInch := 96;
  923. end;
  924.  
  925. end.
  926.  
  927.