home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1998 May / Pcwk5b98.iso / Borland / Cplus45 / BC45 / APPLAUNC.PAK / APPWIN.CPP < prev    next >
C/C++ Source or Header  |  1995-08-29  |  31KB  |  1,087 lines

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