c_applic.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. // c_applic.cpp
  2. // The "application" part of my program. This is concerned with
  3. // initial creation of windows and the capture of any command-line
  4. // or other options.
  5. //
  6. // Copyright (C)/©/¸ Codemist Ltd, 1995-2002
  7. /*
  8. * This code may be used and modified, and redistributed in binary
  9. * or source form, subject to the "CCL Public License", which should
  10. * accompany it. This license is a variant on the BSD license, and thus
  11. * permits use of code derived from this in either open and commercial
  12. * projects: but it does require that updates to this code be made
  13. * available back to the originators of the package.
  14. * Before merging other code in with this or linking this code
  15. * with other packages or libraries please check that the license terms
  16. * of the other material are compatible with those of this.
  17. */
  18. /* Signature: 59c7129b 10-Oct-2002 */
  19. #include "cwin.hpp"
  20. extern "C" {
  21. extern int Lstop(int a, int b);
  22. }
  23. //
  24. // Windows enters my code at WinMain, but with MFC I arrive via all sorts
  25. // of jolly initialisation code. To support old code I read the command
  26. // line that invoked me, and parse it into words which I store in argv[],
  27. // much as per the regular C startup process.
  28. //
  29. char programName[64];
  30. char *cwin_full_program_name;
  31. static int argc;
  32. static char **argv;
  33. static int set_up_argv()
  34. // This sets up argc and argv[] as expected for a regular C application.
  35. // It arranges that argv[0] is an unqualified name, forced into upper case.
  36. // Ie argv[0] does not have a path-name on the front of it or any ".EXE"
  37. // suffix after it.
  38. // I return a flag that indicates whether "--" was found among the arguments.
  39. // (and now I will return the same flag if "-f" was among the arguments)
  40. {
  41. int doubledashFound = 0;
  42. int i = 0, c, len = 0;
  43. char *w = GetCommandLine();
  44. // The string I obtained there may be in UniCode, but I will suppose that it
  45. // does not contain any funny characters. In particular I will demand
  46. // that this module is compiled with Unicode mapped onto ordinary 8-bit
  47. // chars.
  48. char *w1 = w, *argbuf;
  49. // I scan the command line once to assess its length. I treat any item
  50. // that STARTS with a double-quote mark as running on until the following
  51. // double quote (even if there are blanks in the way), and in that case I
  52. // remove the quotes before passing on to the user's program. Something
  53. // like this appears to be essential for dealing with Windows-95 with
  54. // its long filenames: it puts the first item on the command line (the name
  55. // of the program that is being executed) in double quotes in case it is long
  56. // and in case it has embedded whitespace, and unless I strip those quotes
  57. // later on bits of code crash. Note now the subtle distinction between
  58. // -W "yy"
  59. // and -W"yy"
  60. // where -W is some option that user code tries to interpret. In the first
  61. // case the quotes are gobbled up and removed here, in the second they
  62. // remain for the user to see. Note that
  63. // -W"yy is here"
  64. // returns three words, two of which have embedded quote marks. It is probably
  65. // impossible to guarantee to win all cases here!
  66. do
  67. { while ((c = *w++) == ' ' || c == '\t');// Blank at start of an item
  68. if (c == 0) break;
  69. i++; // Count of words
  70. if (c == '"') // items in double quotes?
  71. { c = *w++;
  72. while (c != 0 && c != '"') c = *w++, len++;
  73. if (c == '"') c = *w++;
  74. }
  75. else while (c != 0 && c != ' ' && c != '\t') c = *w++, len++;
  76. } while (c != 0);
  77. // Now I can allocate space for the argument vector and a copy of the data.
  78. // I grab a little more space than I am going to use as a matter of caution.
  79. argv = (char **)malloc((i+1)*sizeof(char *));
  80. argbuf = (char *)malloc(i+len);
  81. argc = 0;
  82. if (argv==NULL || argbuf==NULL) return 0;
  83. // Re-scan the command line copying characters into buffers
  84. w = w1;
  85. do
  86. { while ((c = *w++) == ' ' || c == '\t');
  87. if (c == 0) break;
  88. argv[argc++] = argbuf;
  89. if (c == '"') // I strip the quotes while I tokenise
  90. { c = *w++;
  91. while (c != 0 && c != '"') *argbuf++ = (char)c, c = *w++;
  92. if (c == '"') c = *w++;
  93. }
  94. else while (c != 0 && c != ' ' && c != '\t')
  95. *argbuf++ = (char)c, c = *w++;
  96. *argbuf++ = 0;
  97. if (argv[argc-1][0] == '-' &&
  98. (argv[argc-1][1] == '-' ||
  99. argv[argc-1][1] == 'f' ||
  100. argv[argc-1][1] == 'F')) doubledashFound = 1;
  101. } while (c != 0);
  102. // Put a NULL pointer at the end of argv[], just to be safe
  103. argv[argc] = NULL;
  104. // Now I want to trim argv[0] so that even if it started with a full
  105. // path or with an extension (eg. "\bin\csl.exe") it is passed on trimmed
  106. // down to just its root (eg. "csl" in the above case). This string will
  107. // be left in programName too.
  108. w = w1 = argv[0];
  109. cwin_full_program_name = NULL;
  110. while ((c = *w++) != 0)
  111. { if (c == '\\') w1 = w;
  112. // I take the view that if argv[0] contains a ":" character then it can be
  113. // presumed to be a fully rooted file name, including a drive specification.
  114. // In such cases I will use it when I want the full name of the executable
  115. // I am running. Well I will also require in that case that it should end
  116. // in a ".exe" suffix. This final test is certainly needed under Windows
  117. // NT 4.0 when one launches CSL from a command line but specifying
  118. // a drive to find it on. What I want is the text
  119. // "D:\xxxx.exe"
  120. else if (c == ':' && *w=='\\')
  121. { i = strlen(w)-4;
  122. if (i > 0 && w[i]=='.' &&
  123. tolower(w[i+1])=='e' &&
  124. tolower(w[i+2])=='x' &&
  125. tolower(w[i+3])=='e')
  126. cwin_full_program_name = argv[0];
  127. }
  128. }
  129. if (*w1 == 0) w1 = "CWIN"; // Final char of argv[0] was '\': use default
  130. w = programName;
  131. while ((c = *w1++) != 0 && c != '.') *w++ = (char)toupper(c);
  132. *w = 0;
  133. argv[0] = programName;
  134. if (cwin_full_program_name == NULL)
  135. // Now I would like to get a full path to the program name... The
  136. // SearchPath function looks first in the directory from which the
  137. // application was fetched, and provided that the ".exe" extension
  138. // I specify here is correct the file really ought to be located!
  139. { int nameLength = SearchPath(NULL, programName, ".EXE", 0, argbuf, &w);
  140. // There is one critically important case where "SearchPath" will fail here,
  141. // and that is when the program has been started from a debugger and the
  142. // real name of the program is just not available. In that case tough
  143. // luck, you will have to make resources available by some means NOT
  144. // dependent on the program name or the directory it lives in. Maybe in some
  145. // cases with DOS extenders the program will appear to have been loaded from
  146. // a directory distinct from the one that the obvious ".EXE" file lives in.
  147. // In those cases I had better hope that argv[0] gave me a completely
  148. // rooted file name.
  149. cwin_full_program_name = (char *)malloc(nameLength+1);
  150. if (cwin_full_program_name == NULL)
  151. cwin_full_program_name = "cwin.exe";
  152. else
  153. { if (SearchPath(NULL, programName, ".EXE",
  154. nameLength+1, cwin_full_program_name, &w) == 0)
  155. cwin_full_program_name = "cwin.exe";
  156. }
  157. }
  158. return doubledashFound;
  159. }
  160. CTheApp theApp;
  161. CString mainWindowClass;
  162. UINT clipboardformat;
  163. BOOL CTheApp::InitInstance()
  164. {
  165. // I find the explanations about m_nCmdShow and GetStartupInfo jolly
  166. // confusing! However the code as given here will be tested with CSL launched
  167. // from a command line with
  168. // start csl ...
  169. // start /min csl ...
  170. // start /max csl ...
  171. // and start csl -- logfile.log
  172. //
  173. // The last of these ought to start CSL minimised even if /min or
  174. // /max is given as well.
  175. //
  176. // Initial testing is on NT. Next I will need to try Windows 95. The
  177. // issue of win32s (on Windows 3.1x) will be gently ignored now.
  178. int nShow = m_nCmdShow;
  179. #ifdef __WATCOM_CPLUSPLUS__
  180. #if __WATCOM_CPLUSPLUS__ > 1060
  181. // Oh calamity. The following 3 lines seem necessary to make the initial
  182. // window behave properly, but when I inserted them I had reports that the
  183. // code would not link on a system with just the previous version of
  184. // Watcom C. While I investigate this I will try to disable this facility
  185. // unless I have version 11.0 installed, and maybe this will allow us to
  186. // keep moving forward.
  187. STARTUPINFO su;
  188. GetStartupInfo(&su);
  189. if (su.dwFlags & 1) nShow = su.wShowWindow;
  190. #endif
  191. #endif
  192. // I will grab information out of the registry as soon as the application
  193. // is started.
  194. SetRegistryKey("Codemist"); // Use registry rather than ".ini" file
  195. int left = GetProfileInt("MainWindow", "ScreenLeft", -1000000);
  196. int width = GetProfileInt("MainWindow", "ScreenWidth", -1000000);
  197. int top = GetProfileInt("MainWindow", "ScreenTop", -1000000);
  198. int height = GetProfileInt("MainWindow", "ScreenHeight", -1000000);
  199. int fsize = GetProfileInt("MainWindow", "FontSize", 0);
  200. int fweight= GetProfileInt("MainWindow", "FontWeight", 0);
  201. int linel = GetProfileInt("MainWindow", "LineLength", -1000000);
  202. CString fname(GetProfileString("MainWindow", "FontName", "Courier New"));
  203. int doubledashFound = set_up_argv();
  204. mainWindow = NULL;
  205. mainWindowClass = ::AfxRegisterWndClass(CS_DBLCLKS,
  206. LoadStandardCursor(IDC_ARROW),
  207. (HBRUSH)(COLOR_WINDOW+1),
  208. LoadIcon("CWIN"));
  209. /*
  210. * I introduce a private clipboard format here. It will be just like
  211. * simple text (to start with) except that certain control characters
  212. * will be used to separate off the places that prompt strings occus. This
  213. * can be exploited in PASTE operations so that prompts issued by this
  214. * system do not get re-entered when previous input is copies and pasted.
  215. */
  216. clipboardformat = RegisterClipboardFormat("Codemist Text");
  217. /*
  218. * clipboardformat is left zero if anything went wrong, so in such cases
  219. * I must not attempt to use it.
  220. */
  221. if ((mainWindow = new CMainWindow()) == NULL) return FALSE;
  222. m_pMainWnd = mainWindow;
  223. CClientDC dc(mainWindow);
  224. mainWindow->windowFonts.InitFont(&dc, (LPCTSTR)fname, fweight, fsize);
  225. // If there was "--" given as an argument I start off with the window
  226. // minimized. This is because I then expect an application to use this
  227. // flag to enable output to a file rather than to the screen.
  228. WINDOWPLACEMENT wp;
  229. mainWindow->GetWindowPlacement(&wp);
  230. // Here I will see where the window is about to be placed, and adjust its
  231. // width (and maybe its left hand side) in an attempt to make the client
  232. // are just big enough for 80 columns. I am a bit unhappy about the
  233. // calculation here using system metrics, and have added in one more
  234. // CXBORDER as a fudge to bring experimental reality on MY system into
  235. // line. Hope it is OK on other systems and configurations too.
  236. // The "+5" on the end is an attempt to leave room for a caret to the right
  237. // of the last sensible character on a line...
  238. if (left!=-1000000 && width!=-1000000 &&
  239. top!=-1000000 && height!=-1000000 &&
  240. linel!=-1000000)
  241. { mainWindow->SetWindowPos(NULL, left, top, width, height,
  242. SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOZORDER);
  243. cwin_linelength = linel;
  244. }
  245. else
  246. { int screenWidth = GetSystemMetrics(SM_CXSCREEN);
  247. RECT *wr = &wp.rcNormalPosition;
  248. int left = wr->left;
  249. int cwidth = 80*mainWindow->windowFonts.HCourier.across['X'] +
  250. 3*GetSystemMetrics(SM_CXBORDER) +
  251. 2*GetSystemMetrics(SM_CXFRAME) +
  252. GetSystemMetrics(SM_CXVSCROLL) + 5;
  253. // Try to get the whole window onto the screen.
  254. if (left + cwidth > screenWidth)
  255. { left = screenWidth - cwidth;
  256. if (left < 0) left = 0;
  257. }
  258. mainWindow->SetWindowPos(NULL,
  259. left, wr->top,
  260. cwidth, (wr->bottom - wr->top),
  261. SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOZORDER);
  262. cwin_linelength = 80;
  263. WriteProfileInt("MainWindow", "ScreenLeft", left);
  264. WriteProfileInt("MainWindow", "ScreenWidth", cwidth);
  265. WriteProfileInt("MainWindow", "ScreenTop", wr->top);
  266. WriteProfileInt("MainWindow", "ScreenHeight", wr->bottom-wr->top);
  267. WriteProfileInt("MainWindow", "LineLength", cwin_linelength);
  268. }
  269. mainWindow->ShowWindow(doubledashFound ? SW_SHOWMINNOACTIVE : nShow);
  270. mainWindow->UpdateWindow();
  271. return TRUE;
  272. }
  273. class CenteredDialogBox : public CDialog
  274. {
  275. public:
  276. void CreateAndDisplay(HGLOBAL h);
  277. };
  278. void CenteredDialogBox::CreateAndDisplay(HGLOBAL h)
  279. {
  280. InitModalIndirect(h);
  281. DoModal();
  282. }
  283. // In-store dialog-box templates need some of their strings in 16-bit
  284. // Unicode form. This code stretches out a simple string. It also round
  285. // the length of the data written to a multiple of 8 bytes, which seems to
  286. // be an unpublished (?) requirement for the dialog box template structures.
  287. static LPWORD WidenString(LPWORD p, char *q)
  288. {
  289. int n = 0;
  290. while ((*p++ = *q++) != 0) n++;
  291. if (n & 1) *p++ = 0;
  292. return p;
  293. }
  294. // The following function fills in details about one control within a
  295. // dialog box template.
  296. static LPWORD PlantDlgItem(LPWORD p3, int x, int y, int cx, int cy,
  297. int id, DWORD style, int type, char *text)
  298. {
  299. LPDLGITEMTEMPLATE p2 = (LPDLGITEMTEMPLATE)p3;
  300. p2->x = (short)x; p2->y = (short)y; p2->cx = (short)cx, p2->cy = (short)cy;
  301. p2->id = (WORD)id;
  302. p2->style = style;
  303. p3 = (LPWORD)(p2 + 1);
  304. *p3++ = 0xffff;
  305. *p3++ = (WORD)type;
  306. int n = 1;
  307. while ((*p3++ = *text++) != 0) n++;
  308. if (n & 1) *p3++ = 0;
  309. *p3++ = 0;
  310. return p3;
  311. }
  312. // I make the "ABOUT" dialog box from an in-memory template, and
  313. // this makes it possible to make the text that is included depend on
  314. // strings that the user can potentially reconfigure. The strings put here
  315. // are somewhat generic. Note also that if a user hits the HELP menu
  316. // during system start-up before the regular user code at main() has been
  317. // entered than the messages shown here will appear, even though later on
  318. // the user's properly selected messages will be the ones that show up. I
  319. // think that on balance I almost count that to be a positive advantage! It
  320. // means that the "CWIN" information and credits are at least just about
  321. // available to all users!
  322. char about_box_title[32] = "About CWIN";
  323. char about_box_description[32] = "The CWIN window driver";
  324. char about_box_rights_1[32] = "Copyright Codemist Ltd.";
  325. char about_box_rights_2[32] = "A C Norman 1994-6";
  326. void CTheApp::OnAbout()
  327. {
  328. HGLOBAL h = GlobalAlloc(GMEM_ZEROINIT, 1024);
  329. if (!h) return;
  330. LPDLGTEMPLATE p1 = (LPDLGTEMPLATE)GlobalLock(h);
  331. WORD *p0 = (WORD *)p1;
  332. p1->style = WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_MODALFRAME;
  333. p1->cdit = 5;
  334. p1->cx = 167; p1->cy = 86;
  335. LONG units = ::GetDialogBaseUnits();
  336. int dlgx = units & 0xffff, dlgy = (units >> 16) & 0xffff;
  337. p1->x = (short)((4*mainWindow->clientWidth)/dlgx - p1->cx)/2;
  338. p1->y = (short)((8*mainWindow->clientHeight)/dlgy - p1->cy)/2;
  339. LPWORD p2 = (LPWORD)(p1 + 1);
  340. *p2++ = 0; // no menu
  341. *p2++ = 0; // a predefined box class
  342. p2 = WidenString(p2, about_box_title);
  343. p2 = PlantDlgItem(p2, 0, 4, 167, 8, -1,
  344. WS_CHILD | WS_VISIBLE | SS_CENTER, 0x0082, about_box_description);
  345. p2 = PlantDlgItem(p2, 0, 45, 167, 8, -1,
  346. WS_CHILD | WS_VISIBLE | SS_CENTER, 0x0082, about_box_rights_1);
  347. p2 = PlantDlgItem(p2, 0, 53, 167, 8, -1,
  348. WS_CHILD | WS_VISIBLE | SS_CENTER, 0x0082, about_box_rights_2);
  349. p2 = PlantDlgItem(p2, 74, 22, 0, 0, -1,
  350. WS_CHILD | WS_VISIBLE | SS_ICON, 0x0082, "CWIN");
  351. p2 = PlantDlgItem(p2, 66, 65, 32, 14, IDOK,
  352. WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 0x0080, "OK");
  353. GlobalUnlock(h);
  354. CenteredDialogBox dlg;
  355. dlg.CreateAndDisplay(h);
  356. GlobalFree(h);
  357. }
  358. // When I want to pop up a box that says "Press OK to exit" I make the
  359. // structure that defines the dialog box here in memory rather than putting
  360. // it into my resource file. The reason for taking this step is that it
  361. // allows to to keep the resource file as spartan and simple as possible.
  362. // It also provides a place for me to ensure that the dialog box is central
  363. // in the area that my window occupies.
  364. static void DoFinishBox()
  365. {
  366. HGLOBAL h = GlobalAlloc(GMEM_ZEROINIT, 1024);
  367. if (!h) return;
  368. LPDLGTEMPLATE p1 = (LPDLGTEMPLATE)GlobalLock(h);
  369. WORD *p0 = (WORD *)p1; //DEBUG
  370. p1->style = WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_MODALFRAME;
  371. p1->cdit = 2;
  372. p1->cx = 95; p1->cy = 52;
  373. // I want the box to appear in the centre of where my window is. This
  374. // causes extra fun because of the special coordinate system used with
  375. // dialog boxes - I have to convert units.
  376. LONG units = ::GetDialogBaseUnits();
  377. int dlgx = units & 0xffff, dlgy = (units >> 16) & 0xffff;
  378. p1->x = (short)((4*theApp.mainWindow->clientWidth)/dlgx - p1->cx)/2;
  379. p1->y = (short)((8*theApp.mainWindow->clientHeight)/dlgy - p1->cy)/2;
  380. LPWORD p2 = (LPWORD)(p1 + 1);
  381. *p2++ = 0; // no menu
  382. *p2++ = 0; // a predefined box class
  383. *p2++ = 0; // no title
  384. p2 = PlantDlgItem(p2, 1, 10, 94, 12, -1,
  385. WS_CHILD | WS_VISIBLE | SS_CENTER, 0x0082, "Press OK to exit");
  386. p2 = PlantDlgItem(p2, 28, 23, 40, 14, IDOK,
  387. WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 0x0080, "OK");
  388. GlobalUnlock(h);
  389. CenteredDialogBox dlg;
  390. dlg.CreateAndDisplay(h);
  391. GlobalFree(h);
  392. }
  393. // Here I override the Run member of my application so that I take control
  394. // of the way in which the underlying window system is polled. This is
  395. // somewhat delicate! The code here has to be a close enough shadow of
  396. // what MFC does that I do not cause conflict.
  397. int cwin_pause_at_end;
  398. int CTheApp::Run() // Main running routine until application exits
  399. {
  400. // If I had not managed to open a main window then I should give up.
  401. if (m_pMainWnd == NULL
  402. // && AfxOleGetUserCtrl() // Embedding or Automation invocation?
  403. ) AfxPostQuitMessage(0);
  404. // The message handler needs to access m_msgCur which is a private member
  405. // of the class, so I can not use just (theApp.m_msgCur) to get at it. To
  406. // work around the problem I just dump a reference to it in the variable
  407. // msgPtr.
  408. //+++ This was OK with Visual C++ version 5, but by version 7 (in
  409. //+++ Visual Studio .NET) the m_msgCur member no longer exists, and so this
  410. //+++ code needs to be rebuilt! Oh dear, especially since the way it
  411. //+++ was breaking private visibility is an indication that the original
  412. //+++ version was something of a crock! ACN September 2002
  413. //+++ msgPtr = &m_msgCur;
  414. // Now the real fun! I call cwin_main() which fires up my application code
  415. // Remember that cwin_main() should either return to me or do a cwin_exit()
  416. // and it should NOT call exit().
  417. cwin_pause_at_end = FALSE;
  418. int returnCode;
  419. // try {
  420. returnCode = cwin_main(argc, argv);
  421. // }
  422. // catch (int rc) { returnCode = rc; }
  423. if (cwin_pause_at_end)
  424. { cwin_maximize();
  425. cwin_ensure_screen();
  426. DoFinishBox();
  427. }
  428. if (m_pMainWnd != NULL) delete m_pMainWnd;
  429. m_pMainWnd = mainWindow = NULL;
  430. int returnCode1 = ExitInstance();
  431. if (returnCode1 > returnCode) returnCode = returnCode1;
  432. if (m_pMainWnd != NULL) m_pMainWnd->SendMessage(WM_CLOSE);
  433. // Although I go to some trouble to collect a return code here I find such
  434. // things somewhat unhelpful under Windows, and so at the last minute I
  435. // check away the information and return zero.
  436. return 0; // returnCode;
  437. }
  438. //void cwin_exit(int r)
  439. //{
  440. // throw r;
  441. //}
  442. BEGIN_MESSAGE_MAP(CTheApp, CWinApp)
  443. ON_COMMAND(IDM_HELPCONTENTS, OnHelpContents)
  444. ON_COMMAND(IDM_HELPSEARCH, OnHelpSearch)
  445. ON_COMMAND(IDM_HELP_ON_HELP, OnHelpUsing)
  446. ON_COMMAND(IDM_ABOUT, OnAbout)
  447. #ifdef DEMOVERSION
  448. ON_COMMAND(IDM_EUPRICES, OnEUPrices)
  449. ON_COMMAND(IDM_WORLDPRICES, OnWorldPrices)
  450. ON_COMMAND(IDM_ORDERFORM, OnOrderform)
  451. #endif
  452. ON_COMMAND_RANGE(IDM_DYNAMIC_ITEMS, IDM_LAST_DYNAMIC, OnDynamic)
  453. END_MESSAGE_MAP()
  454. // At various times I will want to go back and poll the window manager
  455. // to ensure that mouse activity is responded to, the screen is re-drawn and
  456. // other applications get a share of the CPU. To do that I will arrange that
  457. // 'cwin_poll_window_manager()' is called from time to time in the middle of
  458. // whatever else I am doing. This grabs a message from the window manager
  459. // and dispatches it to whatever handler is relevant.
  460. //!! At one stage I wanted to display a clock at the top left of the
  461. //!! title bar. I updates this by arranging to use idle processing, and
  462. //!! within the idle handler I watched to see if another 5 seconds had
  463. //!! gone by -- if so I updates the clock. Well firstly I ought to have
  464. //!! had a separate thread waking up every few seconds to do this, and
  465. //!! secondly this causes monitoring programs to get the impression that
  466. //!! I am a very heavy user of the CPU. CSL in fact replaces the clock
  467. //!! with a message about time used and GC activity, so this ends up
  468. //!! pretty pointless. I will not delete the code from my source file
  469. //!! just yet -- it is commented out with "//!!", but I will eventually
  470. //!! purge it.
  471. //!! static void timer_processing()
  472. //!! {
  473. //!! SYSTEMTIME t1;
  474. //!! GetSystemTime(&t1);
  475. //!! //- if (t1.wHour != lastFlushTime.wHour ||
  476. //!! //- (t1.wMinute - lastFlushTime.wMinute)*60 +
  477. //!! //- (t1.wSecond - lastFlushTime.wSecond) > 3)
  478. //!! //- cwin_almost_ensure_screen();
  479. //!! if (!theApp.mainWindow->leftSetByUser)
  480. //!! {
  481. //!! // Here I arrange to update the title-bar clock about once every 5 secs. It
  482. //!! // seems that every second it too frequent, especially since it often flashes
  483. //!! // the title-bar while re-drawing it. But 10 seconds is too long and lets
  484. //!! // the user feel things may be stuck.
  485. //!! // If the user explicitly sets any value in the left part of the title bar
  486. //!! // then this action is disabled. I do not set titleUpdateTime at the start
  487. //!! // of a run but that does not matter - it will fall into line within a few
  488. //!! // seconds whatever its initial value (however junky) is.
  489. //!! if (theApp.mainWindow->titleUpdateTime.wHour != t1.wHour ||
  490. //!! theApp.mainWindow->titleUpdateTime.wMinute != t1.wMinute ||
  491. //!! theApp.mainWindow->titleUpdateTime.wSecond/5 != t1.wSecond/5)
  492. //!! { theApp.mainWindow->titleUpdateTime = t1;
  493. //!! theApp.mainWindow->titleUpdateTime.wSecond =
  494. //!! 5*(theApp.mainWindow->titleUpdateTime.wSecond/5);
  495. //!! theApp.mainWindow->cwin_display_date();
  496. //!! }
  497. //!! }
  498. //!! }
  499. //!!
  500. //!! // This task will busy-wait in its idle-state watching time go by and
  501. //!! // occasionally updating the screen etc.
  502. //!!
  503. //!! BOOL CTheApp::OnIdle(LONG lCount)
  504. //!! {
  505. //!! BOOL r = CWinApp::OnIdle(lCount);
  506. //!! if (!r) timer_processing();
  507. //!! return r;
  508. //!! }
  509. //!!
  510. void cwin_poll_window_manager(int waitForEvent)
  511. {
  512. // If the application has registered an idle-time handler then that gets
  513. // invoked until it has finished or until a real message arrives whenever
  514. // I call cwin_poll_window_manager(). I also process ALL the window messages
  515. // that have stacked up and only go back to the user when otherwise idle.
  516. MSG msg;
  517. if (!waitForEvent &&
  518. !::PeekMessage(&msg,
  519. // theApp.msgPtr,
  520. NULL, 0, 0, PM_NOREMOVE))
  521. return; // These days if there are no messages I do nothing
  522. // Drop through if there is a message waiting for me, so that
  523. // PumpMessage will not block unless I have asked for that.
  524. do
  525. { if (!theApp.PumpMessage())
  526. { Lstop(0, 1); // the "1" here actually means "0"!
  527. return;
  528. }
  529. // If the user selects CLOSE from the system menu it causes PumpMessage to
  530. // return FALSE, so in that case I close things down.
  531. } while (::PeekMessage(&msg,
  532. //theApp.msgPtr,
  533. NULL, 0, 0, PM_NOREMOVE));
  534. LONG lIdle = 0;
  535. while (AfxGetApp()->OnIdle(lIdle++)); // in case higher levels use idle!
  536. }
  537. void cwin_minimize()
  538. {
  539. WINDOWPLACEMENT wp;
  540. if (!::GetWindowPlacement(theApp.mainWindow->m_hWnd, &wp)) return;
  541. wp.showCmd = SW_SHOWMINIMIZED;
  542. ::SetWindowPlacement(theApp.mainWindow->m_hWnd, &wp);
  543. }
  544. void cwin_maximize()
  545. {
  546. WINDOWPLACEMENT wp;
  547. if (!::GetWindowPlacement(theApp.mainWindow->m_hWnd, &wp)) return;
  548. wp.showCmd = SW_RESTORE;
  549. ::SetWindowPlacement(theApp.mainWindow->m_hWnd, &wp);
  550. }
  551. void CTheApp::OnHelpContents() // Start on contents page.
  552. {
  553. WinHelp(0L, HELP_CONTENTS);
  554. }
  555. void CTheApp::OnHelpSearch()
  556. {
  557. WinHelp((DWORD)"", HELP_PARTIALKEY); // Search through keywords.
  558. }
  559. void CTheApp::OnDynamic(unsigned int commandId)
  560. {
  561. // char hah[100];
  562. // sprintf(hah, "Help message %d %s\n", commandId,
  563. // dynamic_files[commandId-IDM_DYNAMIC_ITEMS]);
  564. // DisplayMsg(hah);
  565. ::WinHelp(mainWindow->m_hWnd,
  566. dynamic_files[commandId-IDM_DYNAMIC_ITEMS],
  567. HELP_CONTENTS,
  568. 0);
  569. }
  570. void CTheApp::cwin_set_help_file(const char *key, const char *path)
  571. {
  572. char key1[8];
  573. int i;
  574. if (key == NULL)
  575. { WriteProfileInt("HelpItems", "HowMany", 0);
  576. for (i=0; i<dynamicCount; i++)
  577. { sprintf(key1, "T%.3d", i);
  578. WriteProfileString("HelpItems", key1, NULL);
  579. sprintf(key1, "P%.3d", i);
  580. WriteProfileString("HelpItems", key1, NULL);
  581. }
  582. return;
  583. }
  584. for (i=0; i<dynamicCount; i++)
  585. { if (strcmp(key, dynamic[i]) == 0) break;
  586. }
  587. if (i == dynamicCount) // not found
  588. { if (path == NULL) return;
  589. else
  590. { dynamic[dynamicCount] = key;
  591. dynamic_files[dynamicCount++] = path;
  592. }
  593. }
  594. else
  595. { if (path == NULL)
  596. { dynamicCount--;
  597. for (;i<dynamicCount; i++)
  598. { dynamic[i] = dynamic[i+1];
  599. dynamic_files[i] = dynamic_files[i+1];
  600. }
  601. }
  602. else dynamic_files[i] = path;
  603. }
  604. WriteProfileInt("HelpItems", "HowMany", dynamicCount);
  605. for (i=0; i<dynamicCount; i++)
  606. { sprintf(key1, "T%.3d", i);
  607. WriteProfileString("HelpItems", key1, dynamic[i]);
  608. sprintf(key1, "P%.3d", i);
  609. WriteProfileString("HelpItems", key1, dynamic_files[i]);
  610. }
  611. if (path == NULL)
  612. { sprintf(key1, "T%.3d", i);
  613. WriteProfileString("HelpItems", key1, NULL);
  614. sprintf(key1, "P%.3d", i);
  615. WriteProfileString("HelpItems", key1, NULL);
  616. }
  617. }
  618. void cwin_set_help_file(const char *key, const char *path)
  619. {
  620. theApp.cwin_set_help_file(key, path);
  621. }
  622. // DisplayMsg is used a bit like fprintf(stderr, ...) but ONLY for debugging.
  623. // It pops up a modal dialog box each time it is called. This is easy to
  624. // code, but a bit clumsy in the way it disturbs the screen.
  625. void
  626. #ifdef _MSC_VER
  627. __cdecl
  628. #endif
  629. DisplayMsg(char *msg, ...)
  630. {
  631. char buffer[256];
  632. va_list a;
  633. va_start(a, msg);
  634. vsprintf(buffer, msg, a);
  635. va_end(a);
  636. AfxMessageBox(buffer);
  637. }
  638. // End of c_applic.cpp