c_applic.cpp 25 KB

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