home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / C / BC_502 / APPLAUNC.PAK / APPWIN.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  30.2 KB  |  1,090 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows
  3. // Copyright (c) 1993, 1995 by Borland International, All Rights Reserved
  4. //----------------------------------------------------------------------------
  5. #include <owl/pch.h>
  6. #include <owl/applicat.h>
  7. #include <owl/inputdia.h>
  8. #include "appwin.h"
  9. #include "dialogs.h"
  10. #include "applaunc.rh"
  11.  
  12. DEFINE_RESPONSE_TABLE1(TAppWindow, TFloatingFrame)
  13.   EV_WM_NCRBUTTONDOWN,
  14.   EV_WM_DROPFILES,
  15.   EV_WM_ENDSESSION,
  16.   EV_WM_TIMER,
  17.   EV_COMMAND(CM_ADD_APP, CmAddApp),
  18.   EV_COMMAND(CM_REMOVE_APPS, CmRemoveApps),
  19.   EV_COMMAND(CM_CONFIG_OPTIONS, CmConfigOptions),
  20.   EV_COMMAND(CM_READ_CONFIG, CmReadConfig),
  21.   EV_COMMAND(CM_HELP, CmHelp),
  22.   EV_COMMAND(CM_DUMMY, CmDummy),
  23.   EV_REGISTERED("CM_PROPERTIES", CmProperties),
  24.   EV_REGISTERED("CM_BUTTON_PRESSED", CmButtonPressed),
  25.   EV_REGISTERED("CM_BUTTON_DRAG", CmButtonDrag),
  26.   EV_REGISTERED("CM_REQUEST_ID", CmRequestId),
  27.   EV_REGISTERED("CM_SENDING_ID", CmSendingId),
  28. END_RESPONSE_TABLE;
  29.  
  30. const   int   TAppWindow::NumBufSize = 10;
  31.  
  32. static  char  NumBuf[TAppWindow::NumBufSize+1];
  33.  
  34. //
  35. // Constructor.
  36. //
  37. TAppWindow::TAppWindow(char* title, TWindow* client, string& startupPath)
  38. :
  39.   TFloatingFrame(0, title, client, true,
  40.                  TFloatingFrame::DefaultCaptionHeight, true),
  41.   StartupPath(startupPath), Orientation(0), CurINISecNum(UINT_MAX),
  42.   SaveOnExit(0), CleanedUp(0), ConfirmOnRemove(1)
  43. {
  44.   Attr.Style |= WS_POPUP | WS_BORDER | WS_MINIMIZEBOX | WS_SYSMENU;
  45.   SetMargins(TSize(4, 4));
  46.   EnableTinyCaption(52);
  47.  
  48.   // cache client, we know it is a TAppButtonBar.
  49.   //
  50.   AppButtons = TYPESAFE_DOWNCAST(client, TAppButtonBar);
  51.  
  52.   // Setup menu items.
  53.   //
  54.   PopupMenu.AppendMenu(MF_STRING, CM_ADD_APP, "Add Application(s)...");
  55.   PopupMenu.AppendMenu(MF_STRING, CM_REMOVE_APPS, "Remove Application(s)...");
  56.   PopupMenu.AppendMenu(MF_STRING, CM_CONFIG_OPTIONS, "Config Options...");
  57.   PopupMenu.AppendMenu(MF_STRING, CM_READ_CONFIG, "Read Configuration...");
  58.   PopupMenu.AppendMenu(MF_STRING, CM_HELP, "Help");
  59.   PopupMenu.AppendMenu(MF_STRING, CM_EXIT, "Exit Program");
  60.  
  61.   // Initialize file open data.
  62.   //
  63.   FileData.Flags = OFN_FILEMUSTEXIST|OFN_HIDEREADONLY|OFN_CREATEPROMPT|
  64.                    OFN_OVERWRITEPROMPT;
  65.   FileData.SetFilter("AllFiles (*.*)|*.*|");
  66.  
  67.   // Calculate the maximum # of apps that will fix in the button bar given
  68.   // the current screen resolution.  Take the minimum between horizontal
  69.   // and vertical orientation.
  70.   //
  71.   XExt = GetSystemMetrics(SM_CXSCREEN);
  72.   YExt = GetSystemMetrics(SM_CYSCREEN);
  73.   int minPixels = min(XExt, YExt);
  74.   MaxApps = minPixels / TAppButton::ButtonPixelSize - 3;
  75.  
  76.   // Insert place holder button.
  77.   // Place holder button is used only when there are no apps.
  78.   //
  79.   AppRemoverGadget = new TBitmapGadget(IDB_APPREMOVER, IDB_APPREMOVER,
  80.                                        TGadget::Embossed, 2, 0);
  81.   AppButtons->Insert(*AppRemoverGadget);
  82.  
  83.   // Register INI section management messages.
  84.   //
  85.   CM_REQUEST_ID = ::RegisterWindowMessage("CM_REQUEST_ID");
  86.   CM_SENDING_ID = ::RegisterWindowMessage("CM_SENDING_ID");
  87.  
  88.   // Create INI entries array.
  89.   //
  90.   InUseEntries = new char[NEntries];
  91. }
  92.  
  93. //
  94. // Destructor.  Remove all application records from app manager.
  95. //
  96. TAppWindow::~TAppWindow()
  97. {
  98.   AppMgr.Flush(1);
  99.   delete[] InUseEntries;
  100. }
  101.  
  102. //
  103. // SetupWindow(). Restore from ini file (applaunc.ini in startup dir) and
  104. // tells Windows that we want to accept drag and drop.  Append 'Help' item
  105. // to system menu.
  106. //
  107. void
  108. TAppWindow::SetupWindow()
  109. {
  110.   TFloatingFrame::SetupWindow();
  111.  
  112.   // Initialize INI entries.
  113.   //
  114.   InitEntries();
  115.  
  116.   RestoreFromINIFile();
  117.   UpdateAppButtons();
  118.   DragAcceptFiles(true);
  119.  
  120.   // Append about menu item to system menu.
  121.   //
  122.   TSystemMenu sysMenu(HWindow);
  123.   sysMenu.AppendMenu(MF_SEPARATOR, 0, (LPSTR)0);
  124.   sysMenu.AppendMenu(MF_STRING, CM_HELP, "Help");
  125. }
  126.  
  127. //
  128. // CleanupWindow(). Tell windows that we are not accepting drag and drop
  129. // any more. Other cleanup.
  130. //
  131. void
  132. TAppWindow::CleanupWindow()
  133. {
  134.   AppLauncherCleanup();
  135.   DragAcceptFiles(false);
  136.   TWindow::CleanupWindow();
  137. }
  138.  
  139. //
  140. // CmProperties(). If the right mouse button was clicked over a button
  141. // this routine is called to handle it. 'wParam' is id of the button that
  142. // was pressed.  Bring up properties dialog.  If 'OK' terminated the dialog
  143. // then update current app record and button.
  144. //
  145. LRESULT
  146. TAppWindow::CmProperties(WPARAM wParam, LPARAM)
  147. {
  148.   unsigned              loc = AppButtons->LocFromId(wParam);
  149.   TAppRec*              appRec = AppMgr[loc];
  150.   TAppPropertiesData    properties(appRec);
  151.   TAppPropertiesDialog  propDialog(this, ID_APP_PROPERTIES_DIALOG, properties);
  152.  
  153.   propDialog.Execute();
  154.  
  155.   if (properties.ChangeBitmap) {
  156.     AppButtons->MoveButton(loc, loc, appRec->GetIconPath());
  157.     ReIdButtons();
  158.     UpdateAppButtons();
  159.   }
  160.   return 1;
  161. }
  162.  
  163. //
  164. // CmButtonPressed(). When a button is pressed this routine is called
  165. // to handle the starting up of the app.  If requested prompts the user
  166. // for additional input, which will be appended to arguments already
  167. // entered (see properties dialog). Uses WinExec() to run the program.
  168. //
  169. LRESULT
  170. TAppWindow::CmButtonPressed(WPARAM wParam, LPARAM)
  171. {
  172.   TAppRec*  appRec = AppMgr[AppButtons->LocFromId(wParam)];
  173.   int       show;
  174.   string    cmdLine = appRec->ProgramPath;
  175.   char*     appArgs = new char[ProgramArgsLen + 1];
  176.   string    msgBoxTitle;
  177.  
  178.   strcpy(appArgs, appRec->ProgramArgs.c_str());
  179.   if (appRec->PromptForInput)
  180.     TInputDialog(this, "Application arguments", "Enter application arguments:",
  181.                  appArgs, ProgramArgsLen).Execute();
  182.  
  183.   cmdLine += " ";
  184.   cmdLine += appArgs;
  185.   switch (appRec->StartupStyle) {
  186.     case 1 :
  187.       show = SW_SHOWNORMAL;
  188.       break;
  189.     case 2 :
  190.       show = SW_MINIMIZE;
  191.       break;
  192.     case 3 :
  193.       show = SW_MAXIMIZE;
  194.       break;
  195.   };
  196.  
  197.   int result = WinExec(cmdLine.c_str(), show);
  198.   switch (result) {
  199.     case 0:  msgBoxTitle = "Out of memory"; break;
  200.     case 2:  msgBoxTitle = "File not found"; break;
  201.     case 3:  msgBoxTitle = "Path not found"; break;
  202.     default: msgBoxTitle = "Error in WinExec"; break;
  203.   }
  204.   if (result <= 32 ) {
  205.     string appMsg = "App:\n";
  206.  
  207.     appMsg += appRec->ProgramPath;
  208.     MessageBox(appMsg.c_str(), msgBoxTitle.c_str(), MB_OK);
  209.   }
  210.  
  211.   delete appArgs;
  212.   return 1;
  213. }
  214.  
  215. //
  216. // CmButtonDrag().  Called when button is dragged to another part of
  217. // AppLauncher.  The effect is to move the app or remove it.  If the
  218. // drop point is the AppRemoverGadget then remove the app, else move the
  219. // the app to new location.
  220. //
  221. LRESULT
  222. TAppWindow::CmButtonDrag(WPARAM wParam, LPARAM lParam)
  223. {
  224.   unsigned  loc = AppButtons->LocFromId(wParam);
  225.   int       newLoc = LocOfNearestButtonFromPoint(TPoint(LOWORD(lParam),
  226.                                                         HIWORD(lParam)));
  227.   TAppRec*  appRec = AppMgr[loc];
  228.  
  229.   if (newLoc == -2) {                   // if on app remover.
  230.     if (ConfirmRemove(appRec->ProgramPath)) {
  231.       AnimateAppRemoverGadget();
  232.       RemoveApp(loc);
  233.     }
  234.   }
  235.   else {                                // else move app.
  236.     unsigned  nButtons = AppMgr.Count();
  237.  
  238.     if (!(newLoc == -1 && loc == nButtons - 1)) {  // don't move self.
  239.       newLoc = newLoc == -1 ? nButtons : newLoc;
  240.       AppButtons->MoveButton(loc, newLoc, appRec->GetIconPath());
  241.  
  242.       // Move internal app record. Adjust newLoc depending on were
  243.       // button is being moved to.
  244.       //
  245.       AppMgr.Detach(loc, 0);
  246.       AppMgr.AddAt(appRec, newLoc > loc ? newLoc - 1 : newLoc);
  247.     }
  248.   }
  249.   ReIdButtons();
  250.   UpdateAppButtons();
  251.   return 1;
  252. }
  253.  
  254. //
  255. // EvNCRButtonDown(). Bring up the main menu if the right mouse button was
  256. // pressed while over the title bar.
  257. //
  258. void
  259. TAppWindow::EvNCRButtonDown(UINT /*modKeys*/, TPoint& point)
  260. {
  261.   if(!IsIconic())
  262.     PopupMenu.TrackPopupMenu(TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
  263.                              point.x, point.y, 0, HWindow, 0);
  264. }
  265.  
  266. //
  267. // EvDropFiles().  Handle the dropping of files to Applauncher.
  268. //
  269. void
  270. TAppWindow::EvDropFiles(TDropInfo drop)
  271. {
  272.   int totalNumberOfFiles = drop.DragQueryFileCount();
  273.   TPoint  point;
  274.  
  275.   drop.DragQueryPoint(point);
  276.  
  277.   int     loc = LocOfNearestButtonFromPoint(point);
  278.  
  279.   loc = loc == -2 ? -1 : loc;
  280.   for (int i = 0; i < totalNumberOfFiles; i++ ) {
  281.     int     fileLength = drop.DragQueryFileNameLen(i) + 1;
  282.     char*   fileName = new char [fileLength + 1];
  283.  
  284.     drop.DragQueryFile(i, fileName, fileLength);
  285.     if(!DoAddApp(fileName, loc, string()))
  286.       break;
  287.     delete fileName;
  288.   }
  289.   ReIdButtons();
  290.   UpdateAppButtons();
  291.   drop.DragFinish();
  292. }
  293.  
  294. //
  295. // EvEndSession().  In case Windows closes us down.
  296. //
  297. void
  298. TAppWindow::EvEndSession(bool, bool)
  299. {
  300.   AppLauncherCleanup();
  301. }
  302.  
  303. //
  304. // CmAddApp().  Add applications to AppLauncher. Continue until cancel has
  305. // been pressed.
  306. //
  307. void
  308. TAppWindow::CmAddApp()
  309. {
  310.   string              progPath;
  311.   int                 loc;
  312.   StringList          paths(20, 1);
  313.   unsigned            stop = AppMgr.Count();
  314.  
  315.   // Initialize string vector with apps already in AppLauncher.
  316.   //
  317.   for (unsigned i = 0; i < stop ;i++ )
  318.     paths.Add(AppMgr[i]->ProgramPath);
  319.  
  320.   while (1) {
  321.     if (TAddAppDialog(this, ID_ADD_APP_DIALOG, progPath, loc, paths).Execute()
  322.         == IDOK) {
  323.       if (DoAddApp(progPath, loc, string())) {
  324.         ReIdButtons();
  325.         UpdateAppButtons();
  326.       }
  327.       else
  328.         break;
  329.     }
  330.     else
  331.       break;
  332.   }
  333. }
  334.  
  335. //
  336. // CmRemoveApps().  Ask user which apps to remove, then removes them.
  337. //
  338. void
  339. TAppWindow::CmRemoveApps()
  340. {
  341.   StringList           strings(MaxApps,1);
  342.   NumberList           selections(MaxApps,1);
  343.   unsigned             stop = AppMgr.Count();
  344.  
  345.   // initialize vector with apps to pick from.
  346.   //
  347.   for (unsigned i = 0; i < stop ;i++ )
  348.     strings.Add(AppMgr[i]->ProgramPath);
  349.  
  350.   TMyPickListDialog   pickListDialog(this, ID_PICK_LIST_DIALOG, strings,
  351.                                    selections);
  352.   if (pickListDialog.Execute() == IDOK && ConfirmRemove(string())) {
  353.     for (int j = selections.Count() - 1; j >= 0; j-- )
  354.       RemoveApp(selections[j]);
  355.     ReIdButtons();
  356.     UpdateAppButtons();
  357.   }
  358. }
  359.  
  360. //
  361. // CmConfigOptions(). Bring up AppLauncher configuration dialog.
  362. //
  363. void
  364. TAppWindow::CmConfigOptions()
  365. {
  366.   TConfigRec    rec(Orientation, SaveOnExit, ConfirmOnRemove);
  367.   TConfigDialog configDialog(this, ID_CONFIG_DIALOG, rec);
  368.  
  369.   if (configDialog.Execute() == IDOK) {
  370.     int oldOrientation = Orientation;
  371.  
  372.     Orientation = rec.Orientation;
  373.     SaveOnExit = rec.SaveOnExit;
  374.     ConfirmOnRemove = rec.ConfirmOnRemove;
  375.     if (oldOrientation != Orientation) {
  376.       AppButtons->ChangeOrientation(!Orientation ?
  377.                                      TGadgetWindow::Vertical :
  378.                                      TGadgetWindow::Horizontal);
  379.       UpdateAppButtons();
  380.     }
  381.     if (rec.SaveNow)
  382.       if (!SaveToINIFile(CurINISecNum))
  383.         MessageBox("Maximum number of INI sections have already been created",
  384.                    "Saving to INI file", MB_OK);
  385.   }
  386. }
  387.  
  388. //
  389. // CmReadConfig(). Read a configuration from the INI file.  The user is asked
  390. // to choose the configuration (section from ini file) desired.  If the
  391. // list presented is empty then there are no configurations available.
  392. // NOTE: If multiple instances of Applauncher are running and more than
  393. // 2 instances attempt to perform this operation there is a change (depending
  394. // on the sections chosen) that those instances may use the same INI
  395. // section, which can lead to unpredictable results.  It is best if you
  396. // complete this operation on one instance at a time to avoid conflict.
  397. //
  398. void
  399. TAppWindow::CmReadConfig()
  400. {
  401.   StringList           sections(20,1);
  402.   NumberList           selections(20,1);
  403.   unsigned             secNum = 1;
  404.  
  405.   FillEntries();
  406.   for (; secNum < NEntries; secNum++) {
  407.     if (secNum != CurINISecNum && InUseEntries[secNum-1] == NOT_INUSE &&
  408.         INISectionExists(secNum)) {
  409.       string section = string("AL") + itoa(secNum, NumBuf, 10);
  410.       sections.Add(section);
  411.     }
  412.   }
  413.  
  414.   if (!sections.IsEmpty()) {
  415.     TMyPickListDialog   pickListDialog(this, ID_PICK_LIST_DIALOG, sections,
  416.                                      selections);
  417.     if (pickListDialog.Execute() == IDOK && selections.Count() != 0) {
  418.       unsigned oldSecNum = CurINISecNum;
  419.  
  420.       AppButtons->Remove(*AppRemoverGadget);   // don't want to delete it.
  421.       AppMgr.Flush(1);
  422.       AppButtons->Flush(1);
  423.       AppButtons->Insert(*AppRemoverGadget);   // put it back in.
  424.       UpdateAppButtons();
  425.       if (RestoreFromINIFile(atoi(sections[selections[0]].substr(2).c_str()))) {
  426.         MarkInUse(oldSecNum, false);
  427.         UpdateAppButtons();
  428.       }
  429.       else
  430.         MessageBox("Reading configuration from INI failed", "Error",
  431.                    MB_OK);
  432.     }
  433.   } else
  434.     MessageBox("There are no available INI sections", "Read Configuration",
  435.                MB_OK);
  436.  
  437. }
  438.  
  439. //
  440. // CmHelp().  Display help.
  441. //
  442. void
  443. TAppWindow::CmHelp()
  444. {
  445.   DisplayHelp();
  446. }
  447.  
  448. //
  449. // DoAddApp(). Do the work of adding a new app to AppLauncher.  Remove
  450. // placeholder button if necessary.  If the maximum number of apps have
  451. // been added then display an error message and return 0. A return of
  452. // 1 indicates success.
  453. //
  454. int
  455. TAppWindow::DoAddApp(const string& path, int loc, const string& rec)
  456. {
  457.   unsigned  cnt = AppMgr.Count();
  458.  
  459.   if (cnt == MaxApps) {
  460.     MessageBox("The maximum number of apps have already been added", "Error", MB_OK);
  461.     return 0;
  462.   }
  463.  
  464.   TAppRec*  appRec;;
  465.   if (rec.is_null() || rec[0] == ' ') {
  466.     appRec = new TAppRec;
  467.     appRec->ProgramPath = path;
  468.   } else
  469.     appRec = new TAppRec(rec);
  470.  
  471.   if (loc == -1 || loc < -1 || loc > cnt )
  472.     loc = cnt;
  473.  
  474.   TGadget*  newButton = CreateButton(appRec->GetIconPath(), loc);
  475.   AppMgr.AddAt(appRec, loc);
  476.   if (loc == cnt)
  477.     AppButtons->Insert(*newButton, TGadgetWindow::Before, AppRemoverGadget);
  478.   else
  479.     AppButtons->Insert(*newButton, TGadgetWindow::Before,
  480.                        AppButtons->ButtonWithId(AppButtons->IdFromLoc(loc)));
  481.   return 1;
  482. }
  483.  
  484. //
  485. // RemoveApp(). Remove an application from AppLauncher.
  486. //
  487. void
  488. TAppWindow::RemoveApp(unsigned loc)
  489. {
  490.   AppMgr.Detach(loc, 1);
  491.   AppButtons->DestroyButton(loc);
  492. }
  493.  
  494. //
  495. // CreateButton(). Create a new button from program path and id.
  496. //
  497. TGadget*
  498. TAppWindow::CreateButton(string& path, int loc)
  499. {
  500.   if (loc == -1)
  501.     loc = AppMgr.Count();
  502.  
  503.   return new TAppButton(GetApplication()->GetInstance(), path,
  504.                         AppButtons->IdFromLoc(loc));
  505. }
  506.  
  507. //
  508. //  INI file format...
  509. //INI file format:
  510. //[AL##]                    // section
  511. //XYLoc = #;#
  512. //Orientation = <0|1>       // 0 = vertical, 1 = horizontal.
  513. //SaveOnExit = <0|1>
  514. //ConfirmOnRemove = <0|1>
  515. //App## = <appstring>
  516. //...
  517.  
  518. //<appstring> :=
  519. //  <program path>;<program args>;<icon path>;<prompt for input>;<statrup style>
  520. //
  521.  
  522. //
  523. // RestoreFromINIFile(). Restore AppLauncher from INI file. If restore
  524. // was successfull then return 1, else return 0.
  525. //
  526. int
  527. TAppWindow::RestoreFromINIFile(unsigned secNumber)
  528. {
  529.  
  530.   if (secNumber == UINT_MAX) {
  531.     secNumber = NextAvailableSection();
  532.     if (secNumber == UINT_MAX)        // in this case no INI file exists.
  533.       return 1;
  534.   }
  535.   else if (SectionInUse(secNumber))
  536.     return 0;
  537.  
  538.   char      numBuf[NumBufSize+1];
  539.   string    section = string("AL") + itoa(secNumber, numBuf, 10);
  540.  
  541.   if (RestoreXYLoc(section)       &&
  542.       RestoreOrientation(section) &&
  543.       RestoreSaveOnExit(section)  &&
  544.       RestoreConfirmOnRemove(section) &&
  545.       RestoreApps(section)) {
  546.     CurINISecNum = secNumber;
  547.     MarkInUse(CurINISecNum, true);
  548.     return 1;
  549.   }
  550.   return 0;
  551. }
  552.  
  553. //
  554. // RestoreXYLoc().  Restore the last x and y location of AppLauncher.
  555. // If the x and y locations would put AppLauncher off the screen the
  556. // locations are adjusted.
  557. //
  558. int
  559. TAppWindow::RestoreXYLoc(const string& section)
  560. {
  561.   char* firstNum;
  562.   char* secondNum;
  563.  
  564.   GetINIEntry(string("XYLoc"), section, NumBuf, NumBufSize);
  565.   if ((firstNum = strtok(NumBuf, ";")) != 0 &&
  566.       (secondNum = strtok(0, ";")) != 0) {
  567.     Attr.X = atoi(firstNum);
  568.     Attr.Y = atoi(secondNum);
  569.     Attr.X = Attr.X < 0 ? 0 : Attr.X;
  570.     Attr.Y = Attr.Y < 0 ? 0 : Attr.Y;
  571.     if (Attr.X > XExt )
  572.       Attr.X = XExt - Attr.W;
  573.     if (Attr.Y + Attr.H > YExt )
  574.       Attr.Y = 0;
  575.     MoveWindow(Attr.X, Attr.Y, Attr.W, Attr.H, true);
  576.   } else {
  577.     MessageBox("XYLoc entry not found or invalid", "Error", MB_OK);
  578.     return 0;
  579.   }
  580.   return 1;
  581. }
  582.  
  583. //
  584. // RestoreOrientation().  Set the orientation (vertical or horizontal) of
  585. // AppLauncher.
  586. //
  587. int
  588. TAppWindow::RestoreOrientation(const string& section)
  589. {
  590.   if (!GetINIEntry(string("Orientation"), section, NumBuf, NumBufSize)) {
  591.     MessageBox( "Orientation entry not found or invalid", "Error", MB_OK);
  592.     return 0;
  593.   }
  594.   Orientation = strcmp(NumBuf, "0") != 0;
  595.   AppButtons->ChangeOrientation(!Orientation ? TGadgetWindow::Vertical :
  596.                                                TGadgetWindow::Horizontal);
  597.   return 1;
  598. }
  599.  
  600. //
  601. // RestoreSaveOnExit(). Set save on exit value (0 or 1).
  602. //
  603. int
  604. TAppWindow::RestoreSaveOnExit(const string& section)
  605. {
  606.   if (!GetINIEntry(string("SaveOnExit"), section, NumBuf, NumBufSize)) {
  607.     MessageBox( "SaveOnExit entry not found or invalid", "Error", MB_OK);
  608.     return 0;
  609.   }
  610.   SaveOnExit = strcmp(NumBuf, "0") != 0;
  611.   return 1;
  612. }
  613.  
  614. //
  615. // RestoreConfirmOnRemove(). Set confirm on remove value (0 or 1).
  616. //
  617. int
  618. TAppWindow::RestoreConfirmOnRemove(const string& section)
  619. {
  620.   if (!GetINIEntry(string("ConfirmOnRemove"), section, NumBuf, NumBufSize)) {
  621.     MessageBox( "ConfirmOnRemove entry not found or invalid", "Error", MB_OK);
  622.     return 0;
  623.   }
  624.   ConfirmOnRemove = strcmp(NumBuf, "0") != 0;
  625.   return 1;
  626. }
  627.  
  628. //
  629. // RestoreApps().  Add apps from INI file.
  630. //
  631. int
  632. TAppWindow::RestoreApps(const string& section)
  633. {
  634.   char* destBuf = new char[512];
  635.   int   retval = 1;
  636.  
  637.   for (unsigned x = 1; ; x++ ) {
  638.     itoa(x, NumBuf, 10);
  639.     string app = "App";
  640.     app += NumBuf;
  641.     if (!GetINIEntry(app, section, destBuf, 512))
  642.       break;
  643.     else {
  644.       if (!DoAddApp(string(), x - 1, string(destBuf))) {
  645.         MessageBox("Application record string was invalid or too many apps",
  646.                    "Error", MB_OK);
  647.         retval = 0;
  648.         break;
  649.       }
  650.     }
  651.   }
  652.   delete destBuf;
  653.   return retval;
  654. }
  655.  
  656. //
  657. // GetINIEntry().  Read an entry from the INI file.  Specifing the entry (key)
  658. // and section.  On success 1 is returned, else 0.
  659. //
  660. int
  661. TAppWindow::GetINIEntry(const string& entry, const string& section,
  662.                         char* dest, unsigned destBufLen)
  663. {
  664.   string defStr("x");
  665.   string iniPath = StartupPath + "applaunc.ini";
  666.  
  667.   GetPrivateProfileString(section.c_str(), entry.c_str(), defStr.c_str(), dest,
  668.                           destBufLen, iniPath.c_str());
  669.   if (strcmp(dest, defStr.c_str()) == 0)
  670.     return 0;
  671.   return 1;
  672. }
  673.  
  674. //
  675. // SaveToINIFile(). Save all settings and apps to INI file.
  676. // Returns 1 on success, 0 otherwise.
  677. //
  678. int
  679. TAppWindow::SaveToINIFile(unsigned secNumber)
  680. {
  681.   if (secNumber == UINT_MAX)
  682.     secNumber = NextSection();
  683.  
  684.   if (secNumber == UINT_MAX)
  685.     return 0;
  686.  
  687.   string section = string("AL") + itoa(secNumber, NumBuf, NumBufSize);
  688.   int retval =  (SaveXYLoc(section)       &&
  689.                  SaveOrientation(section) &&
  690.                  SaveSaveOnExit(section)  &&
  691.                  SaveConfirmOnRemove(section)  &&
  692.                  SaveApps(section));
  693.   if (retval) {
  694.     CurINISecNum = secNumber;
  695.     MarkInUse(secNumber, true);
  696.   }
  697.   return retval;
  698. }
  699.  
  700. //
  701. // SaveXYLoc().  Save current x and y location of AppLauncher.
  702. // Returns 1 on success, 0 otherwise.
  703. //
  704. int
  705. TAppWindow::SaveXYLoc(const string& section)
  706. {
  707.   string xyLocStr;
  708.  
  709.   Attr.X = Attr.X < 0 ? 0 : Attr.X;
  710.   Attr.Y = Attr.Y < 0 ? 0 : Attr.Y;
  711.   itoa(Attr.X, NumBuf, 10);
  712.   xyLocStr += NumBuf;
  713.   xyLocStr += ";";
  714.   itoa(Attr.Y, NumBuf, 10);
  715.   xyLocStr += NumBuf;
  716.   return WriteINIEntry(section, "XYLoc", xyLocStr);
  717. }
  718.  
  719. //
  720. // SaveOrientation().  Save current orientation of AppLauncher.
  721. // Returns 1 on success, 0 otherwise.
  722. //
  723. int
  724. TAppWindow::SaveOrientation(const string& section)
  725. {
  726.   string orientationStr;
  727.  
  728.   itoa(Orientation, NumBuf, NumBufSize);
  729.   orientationStr = NumBuf;
  730.   return WriteINIEntry(section, "Orientation", orientationStr);
  731. }
  732.  
  733. //
  734. // SaveSaveOnExit(). Save the save on exit indicator.
  735. // Returns 1 on success, 0 otherwise.
  736. //
  737. int
  738. TAppWindow::SaveSaveOnExit(const string& section)
  739. {
  740.   string saveOnExitStr;
  741.  
  742.   itoa(SaveOnExit, NumBuf, NumBufSize);
  743.   saveOnExitStr = NumBuf;
  744.   return WriteINIEntry(section, "SaveOnExit", saveOnExitStr);
  745. }
  746.  
  747. //
  748. // SaveConfirmOnRemove(). Save the confirm on remove indicator.
  749. // Returns 1 on success, 0 otherwise.
  750. //
  751. int
  752. TAppWindow::SaveConfirmOnRemove(const string& section)
  753. {
  754.   string confirmOnRemoveStr;
  755.  
  756.   itoa(ConfirmOnRemove, NumBuf, NumBufSize);
  757.   confirmOnRemoveStr = NumBuf;
  758.   return WriteINIEntry(section, "ConfirmOnRemove", confirmOnRemoveStr);
  759. }
  760.  
  761. //
  762. // SaveApps().  Save all apps to INI file. Each app is on a separate line.
  763. // Returns 1 on success, 0 otherwise.
  764. //
  765. int
  766. TAppWindow::SaveApps(const string& section)
  767. {
  768.   char*   appBuf = new char[1024];
  769.   int     retval = 1;
  770.   unsigned i;
  771.  
  772.   for (i = 1; i <= AppMgr.Count(); i++) {
  773.     itoa(i, NumBuf, 10);
  774.     if(!WriteINIEntry(section, string("App") + string(NumBuf),
  775.                       AppMgr[i-1]->AsString())) {
  776.       MessageBox("Saving application failed", "Save configuration failure",
  777.                  MB_OK);
  778.       retval = 0;
  779.     }
  780.   }
  781.   // Remove any entries from INI file for apps that have been removed from
  782.   // AppLauncher.
  783.   //
  784.   while (1) {
  785.     itoa(i, NumBuf, 10);
  786.     if (GetINIEntry(string("App") + string(NumBuf), section, appBuf, 1024)) {
  787.       if(!WriteINIEntry(section, string("App") + string(NumBuf), string())) {
  788.         MessageBox("Saving application failed", "Save configuration failure",
  789.                    MB_OK);
  790.         retval = 0;
  791.       }
  792.     } else
  793.       break;
  794.     i++;
  795.   }
  796.   delete appBuf;
  797.   return retval;
  798. }
  799.  
  800. //
  801. // WriteINIEntry().  Write given key and value to given section in INI file.
  802. // Returns 1 on success, 0 otherwise.
  803. //
  804. int
  805. TAppWindow::WriteINIEntry(const string& section, const string& entryKey,
  806.                           const string& entryValue)
  807. {
  808.   string iniPath = StartupPath + "applaunc.ini";
  809.   return WritePrivateProfileString(section.c_str(), entryKey.c_str(),
  810.                                    entryValue.is_null() ? (LPCSTR)0 :
  811.                                                           entryValue.c_str(),
  812.                                    iniPath.c_str());
  813. }
  814.  
  815. //
  816. // UpdateAppButtons().  Ask button bar to redraw itself (begin a LayoutSession).
  817. //
  818. void
  819. TAppWindow::UpdateAppButtons()
  820. {
  821.   AppButtons->ReDraw();
  822. }
  823.  
  824. //
  825. // ReIdButtons().  In the event that buttons are moved around or removed the
  826. // remaining buttons need to be re-numbered to stay in sync with the
  827. // internal array of app records.
  828. //
  829. void
  830. TAppWindow::ReIdButtons()
  831. {
  832.   TAppButton* button = TYPESAFE_DOWNCAST(AppButtons->FirstGadget(), TAppButton);
  833.   unsigned  nButtons = AppMgr.Count();
  834.   for (unsigned i = 0; i < nButtons; i++ ) {
  835.     button->RealId = AppButtons->IdFromLoc(i);
  836.     button = TYPESAFE_DOWNCAST(AppButtons->NextGadget(*button), TAppButton);
  837.   }
  838. }
  839.  
  840. //
  841. // DisplayHelp().
  842. //
  843. void
  844. TAppWindow::DisplayHelp()
  845. {
  846.   string helpPath = StartupPath + "applaunc.hlp";
  847.   WinHelp(helpPath.c_str(), HELP_FORCEFILE, 0L);
  848. }
  849.  
  850. //
  851. // LocOfNearestButtonFromPoint().  Return location of nearest button to
  852. // given point.  If the drop point is on the margin or title bar then
  853. // -1 (append) is returned.  If the drop point is the app remover then
  854. // -2 is returned.  Otherwise the location of the button
  855. // on the drop point is returned.
  856. //
  857. int
  858. TAppWindow::LocOfNearestButtonFromPoint(const TPoint& point)
  859. {
  860.   TPoint  p(point);
  861.   int     loc = -1;
  862.  
  863.   TGadget*g = AppButtons->GadgetFromPoint(p);
  864.   if (g)
  865.     if (g == AppRemoverGadget)
  866.       loc = -2;
  867.     else
  868.       loc = AppButtons->LocFromId(TYPESAFE_DOWNCAST(g, TAppButton)->RealId);
  869.   return loc;
  870. }
  871.  
  872. //
  873. // AppLauncherCleanup(). Save settings to ini file if requested. Mark ini
  874. // file section as not being in use so other instances may use the section.
  875. // Shutdown any help files active.
  876. //
  877. void
  878. TAppWindow::AppLauncherCleanup()
  879. {
  880.   if (!CleanedUp) {
  881.     CleanedUp = 1;
  882.     if (SaveOnExit)
  883.       if (!SaveToINIFile(CurINISecNum))
  884.         ::MessageBox(0, "Maximum number of INI sections have already been created",
  885.                         "Saving to INI file", MB_OK);
  886.     MarkInUse(CurINISecNum, false);
  887.     delete AppRemoverGadget;
  888.  
  889.     // Close help window if it is up.
  890.     //
  891.     string helpPath = StartupPath + "applaunc.hlp";
  892.     WinHelp(helpPath.c_str(), HELP_QUIT, 0L);
  893.   }
  894. }
  895.  
  896. //
  897. // ConfirmRemove(). If 'ConfirmOnRemove' != 0 then return whether or not
  898. // the user confirms. Else return true.
  899. //
  900. bool
  901. TAppWindow::ConfirmRemove(const string& p)
  902. {
  903.   if (ConfirmOnRemove) {
  904.     string msg("Are you sure you want to remove ");
  905.     if (p.is_null())
  906.       msg += "multiple items?";
  907.     else
  908.       msg += string("item '") + p + "'?";
  909.     if (MessageBox(msg.c_str(), p.is_null() ? "Remove Items" : "Remove Item",
  910.                    MB_YESNO) != IDYES)
  911.       return false;
  912.   }
  913.   return true;
  914. }
  915.  
  916. //
  917. // AnimateAppRemoverGadget(). Change bitmap when apps are dropped on it.
  918. // waits about 1/2 a second between bitmaps.
  919. //
  920. void
  921. TAppWindow::AnimateAppRemoverGadget()
  922. {
  923.   AppRemoverGadget->SelectImage(1, true);
  924.   for (unsigned long start = GetTickCount(), end = start + 500; start < end; )
  925.     start = GetTickCount();
  926.   AppRemoverGadget->SelectImage(0, true);
  927. }
  928.  
  929.  
  930. //
  931. // INI section managment...
  932. //
  933. // The process of managing the INI sections involves communication with
  934. // the other instances of AppLauncher.  This is accomplished through
  935. // broadcast messages.  Whenever INI sections are referenced a
  936. // broadcast message (CM_REQUEST_ID) is sent to all other instances of
  937. // AppLauncher.  They respond by broadcasting a CM_SENDING_ID with their
  938. // Current INI section number.  The result is that all the the instances
  939. // of AppLauncher get 'updated' on the INI sections that are in use.  This
  940. // guarantees that no INI section is being used more than one instance
  941. // of AppLauncher.
  942. //
  943.  
  944. //
  945. // Initialize entries array with sections in INI file, if they exist.
  946. //
  947. void
  948. TAppWindow::InitEntries()
  949. {
  950.   for (unsigned i = 0; i < NEntries; i++ ) {
  951.     if (INISectionExists(i+1))
  952.       InUseEntries[i] = NOT_INUSE;
  953.     else
  954.       InUseEntries[i] = NO_ENTRY;
  955.   }
  956.  
  957.   FillEntries(1);
  958. }
  959.  
  960. //
  961. // Returns: 0 if not in use, 1 if it is, 2 otherwise (section does not exist).
  962. //
  963. int
  964. TAppWindow::SectionInUse(unsigned sec)
  965. {
  966.   FillEntries();
  967.   if (sec < NEntries && sec != UINT_MAX)
  968.     if (InUseEntries[sec-1] != NO_ENTRY )
  969.       return InUseEntries[sec-1] == INUSE ? 1 : 0;
  970.   return 2;
  971. }
  972.  
  973. //
  974. // Mark a section as being in use or not.  If 'sec' is out of range false
  975. // is returned, else true.
  976. //
  977. bool
  978. TAppWindow::MarkInUse(unsigned sec, bool mark)
  979. {
  980.   if (sec < NEntries && sec != UINT_MAX) {
  981.     InUseEntries[sec-1] = mark ? INUSE : NOT_INUSE;
  982.     return true;
  983.   }
  984.   return  false;
  985. }
  986.  
  987. //
  988. // Return the next section in INI file that is availible (not in use).
  989. //
  990. unsigned
  991. TAppWindow::NextAvailableSection()
  992. {
  993.   FillEntries();
  994.   for (unsigned i = 0; i < NEntries; i++ )
  995.     if (InUseEntries[i] == NOT_INUSE)
  996.       return i+1;
  997.   return UINT_MAX;
  998. }
  999.  
  1000. //
  1001. // NextSection(). Return the next section that is not in use or flaged as
  1002. // non-existant.  This function is used primarily when creating new
  1003. // INI sections because if no sections are available the function
  1004. // returns id of new section.
  1005. //
  1006. unsigned
  1007. TAppWindow::NextSection()
  1008. {
  1009.   FillEntries();
  1010.   for (unsigned i = 0; i < NEntries; i++ )
  1011.     if (InUseEntries[i] == NO_ENTRY || InUseEntries[i] == NOT_INUSE)
  1012.       return i+1;
  1013.   return UINT_MAX;
  1014. }
  1015.  
  1016. //
  1017. // INISectionExists().  Return 1 if given INI section exists in the INI
  1018. // file, 0 otherwise.
  1019. //
  1020. int
  1021. TAppWindow::INISectionExists(unsigned sec)
  1022. {
  1023.   char buf[10];
  1024.   return GetINIEntry(string("XYLoc"), string("AL") + itoa(sec, buf, 10),
  1025.                      NumBuf, NumBufSize);
  1026. }
  1027.  
  1028. //
  1029. // CmRequestId(). Recieved as a result of another instance inquiring
  1030. // as to which INI sections are in use.
  1031. //
  1032. LRESULT
  1033. TAppWindow::CmRequestId(WPARAM /*wParam*/, LPARAM lParam)
  1034. {
  1035.   if (lParam != CurINISecNum)
  1036.     ::PostMessage(HWND_BROADCAST, CM_SENDING_ID, 0, CurINISecNum);
  1037.   return 1;
  1038. }
  1039.  
  1040. //
  1041. // CmSendingId(). Broadcast CM_SENDING_ID with current INI section number
  1042. // in LPARAM.
  1043. //
  1044. LRESULT
  1045. TAppWindow::CmSendingId(WPARAM /*wParam*/, LPARAM lParam)
  1046. {
  1047.   if (lParam != UINT_MAX)
  1048.     InUseEntries[(unsigned)(lParam-1)] = INUSE;
  1049.   return 1;
  1050. }
  1051.  
  1052. //
  1053. // FillEntries(). Mark INI section entries array according to which
  1054. // INI sections are in use by another instance.  First unmark any entries
  1055. // that are marked as being in use by another instance.  We need to do
  1056. // this incase one or more instances have been closed since the last time
  1057. // this function was called.  Setup timer for a 1/2 second delay so
  1058. // other instances have a chance to respond to the CM_REQUEST_ID message.
  1059. // Broadcast CM_REQUEST_ID message.  Give control back to OWL (Windows)
  1060. // so we (and other apps) can process messages. Finally, destroy timer.
  1061. //
  1062. void
  1063. TAppWindow::FillEntries(int skipClear)
  1064. {
  1065.   if (!skipClear)
  1066.     for (unsigned i = 0; i < NEntries; i++ )
  1067.       InUseEntries[i] = InUseEntries[i] == INUSE ? NOT_INUSE : InUseEntries[i];
  1068.  
  1069.   unsigned timerId = SetTimer(1, 500U, 0);   // 1/2 second delay.
  1070.   WaitingForMsg = 1;
  1071.   ::PostMessage(HWND_BROADCAST, CM_REQUEST_ID, 0, CurINISecNum);
  1072.   GetApplication()->MessageLoop();
  1073.   KillTimer(timerId);
  1074. }
  1075.  
  1076. //
  1077. // EvTimer().  When this gets called ~1/2 second has elapsed since the timer
  1078. // was created.  Clear indicator flag and give call EndModal() so
  1079. // AppLauncher can resume execution.
  1080. //
  1081. void
  1082. TAppWindow::EvTimer(UINT)
  1083. {
  1084.   if(WaitingForMsg ) {
  1085.     WaitingForMsg = 0;
  1086.     GetApplication()->EndModal(1);
  1087.   }
  1088. }
  1089.  
  1090.