X11 Work Bench Toolkit  1.0
X11workbench.c
1 // __ __ _ _ _ _ _ //
3 // \ \/ // |/ |__ __ ___ _ _ | | __| |__ ___ _ __ ___ | |__ ___ //
4 // \ / | || |\ \ /\ / // _ \ | '__|| |/ /| '_ \ / _ \| '_ \ / __|| '_ \ / __| //
5 // / \ | || | \ V V /| (_) || | | < | |_) || __/| | | || (__ | | | | _| (__ //
6 // /_/\_\|_||_| \_/\_/ \___/ |_| |_|\_\|_.__/ \___||_| |_| \___||_| |_|(_)\___| //
7 // //
8 // 'main' source for X11workbench //
9 // //
11 
12 /*****************************************************************************
13 
14  X11workbench - X11 programmer's 'work bench' application and toolkit
15  Copyright (c) 2010-2016 by Bob Frazier (aka 'Big Bad Bombastic Bob')
16  all rights reserved
17 
18  DISCLAIMER: The X11workbench application and toolkit software are supplied
19  'as-is', with no warranties, either implied or explicit.
20  Any claims to alleged functionality or features should be
21  considered 'preliminary', and might not function as advertised.
22 
23  BSD-like license:
24 
25  There is no restriction as to what you can do with this software, so long
26  as you include the above copyright notice and DISCLAIMER for any distributed
27  work that is equal to or derived from this one, along with this paragraph
28  that explains the terms of the license if the source is also being made
29  available. A "derived work" describes a work that uses a significant portion
30  of the source files or algorithms that are included with this one.
31  Specifically excluded from this are files that were generated by the software,
32  or anything that is included with the software that is part of another package
33  (such as files that were created or added during the 'configure' process).
34  Specifically included is the use of part or all of any of the X11 workbench
35  toolkit source or header files in your distributed application. If you do not
36  ship the source, the above copyright statement is still required to be placed
37  in a reasonably prominent place, such as documentation, splash screens, and/or
38  'about the application' dialog boxes.
39 
40  Use and distribution are in accordance with GPL, LGPL, and/or the above
41  BSD-like license. See COPYING and README files for more information.
42 
43 
44  Additional information at http://sourceforge.net/projects/X11workbench
45 
46 ******************************************************************************/
47 
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <stdarg.h>
51 #include <unistd.h>
52 #include <memory.h>
53 #include <string.h>
54 #include <strings.h>
55 #include <signal.h>
56 #include <time.h>
57 #include <errno.h>
58 #include <fcntl.h>
59 #include <sys/stat.h>
60 
61 // project includes
62 #include "X11workbench.h"
63 #include "gizzard.h"
64 #include "gdb_helper.h"
65 #include "refactor.h"
66 
67 // X11workbench toolkit library includes
68 #include "window_helper.h"
69 #include "pixmap_helper.h" // pixmap helpers, including pre-defined icons
70 #include "frame_window.h"
71 #include "edit_window.h" // edit (client) window
72 #include "dialog_window.h"
73 #include "file_help.h"
74 #include "conf_help.h"
75 #include "draw_text.h"
76 
77 // pixmap data
78 #include "application_icon.xpm" /* 19x19 icon presented to the OS for alt-tab etc. */
79 #include "icon_app.xpm" /* application icon that's the same size as the others, 36x36 */
80 #include "textfiledoc.xpm"
81 #include "newdoc.xpm"
82 #include "clangdoc.xpm"
83 #include "makefiledoc.xpm"
84 
85 
86 //#define NO_SPLASH /* temporary, later put it as a configure option - need to get 'gleam' to work better */
87 
88 #define STRING "Hello, world"
89 #define BORDER 32 /* was 1 */
90 #define FONT "fixed"
91 
92 #define APP_NAME "X11workbench"
93 
94 
95 #ifndef NO_SPLASH
96 #include "splash.xpm" // splash pixmap
97 #endif // NO_SPLASH
98 
99 
100 // function prototypes
101 
102 static int do_main(int argc, char *argv[], char *envp[]);
103 static void usage(void);
104 static void SetSignalHandlers(void);
105 static int MyWindowCallback(Window wID, XEvent *pEvent);
106 static int FileExitHandler(XClientMessageEvent *);
107 static int FileNewHandler(XClientMessageEvent *);
108 static int FileOpenHandler(XClientMessageEvent *);
109 static int FileSaveHandler(XClientMessageEvent *);
110 static int FileSaveUIHandler(WBMenu *, WBMenuItem *);
111 static int FileSaveAsHandler(XClientMessageEvent *);
112 static int FileSaveAsUIHandler(WBMenu *, WBMenuItem *);
113 static int FileSaveAllHandler(XClientMessageEvent *);
114 static int FileSaveAllUIHandler(WBMenu *, WBMenuItem *);
115 static int FileCloseHandler(XClientMessageEvent *);
116 
117 static int HelpAboutHandler(XClientMessageEvent *);
118 static int HelpContentsHandler(XClientMessageEvent *);
119 static int HelpContextHandler(XClientMessageEvent *);
120 
121 static int TabLeftHandler(XClientMessageEvent *);
122 static int TabRightHandler(XClientMessageEvent *);
123 static int TabMoveLeftHandler(XClientMessageEvent *);
124 static int TabMoveRightHandler(XClientMessageEvent *);
125 static int TabUIHandler(WBMenu *, WBMenuItem *);
126 
127 static int ToolBoxHandler(XClientMessageEvent *pEvent);
128 static int ToolBoxUIHandler(WBMenu *pMenu, WBMenuItem *pMenuItem);
129 
130 
131 int main(int argc, char *argv0[], char *envp0[])
132 {
133 int iRval = 1;
134 char **argv = argv0; // re-define as char ** so I can re-allocate it as needed
135 char **envp = envp0;
136 
137  if(!WBParseStandardArguments(&argc, &argv, &envp))
138  {
139  iRval = do_main(argc, argv, envp);
140 
141  if(envp && envp != envp0)
142  {
143  WBFree(envp);
144  }
145 
146  if(argv && argv != argv0)
147  {
148  WBFree(argv);
149  }
150  }
151  else
152  {
153  usage();
154  }
155 
156  return iRval;
157 }
158 
159 
160 // global variables
161 
162 static Display *pX11Display = NULL; /* X server connection */
163 static WBFrameWindow *pMainFrame = NULL;
164 static XColor clrGreen;
165 
167 // _____ __ ___ _ ____ _ _ //
168 // | ___| __ __ _ _ __ ___ ___ \ \ / (_)_ __ __| | _____ __ / ___|| |_ _ __ _ _ ___| |_ ___ //
169 // | |_ | '__/ _` | '_ ` _ \ / _ \ \ \ /\ / /| | '_ \ / _` |/ _ \ \ /\ / / \___ \| __| '__| | | |/ __| __/ __| //
170 // | _|| | | (_| | | | | | | __/ \ V V / | | | | | (_| | (_) \ V V / ___) | |_| | | |_| | (__| |_\__ \ //
171 // |_| |_| \__,_|_| |_| |_|\___| \_/\_/ |_|_| |_|\__,_|\___/ \_/\_/ |____/ \__|_| \__,_|\___|\__|___/ //
172 // //
174 
175 // application menu and the application menu handler structure 'main_menu_handlers'
176 
178 // default application menu - what I see when there's no open document
180 static char szAppMenu[]="1\n"
181  "_File\tpopup\t2\n"
182  "_Tools\tpopup\t5\n"
183  "\tseparator\n"
184  "_Help\tpopup\t3\n"
185  "\n"
186  "2\n"
187  "_New File\tIDM_FILE_NEW\tNew File\tCtrl+N\n"
188  "_Open File\tIDM_FILE_OPEN\tOpen File\tCtrl+O\n"
189  "\tseparator\n"
190  "E_xit\tIDM_FILE_EXIT\tClose Application\tAlt+F4\n"
191  "\n"
192  "3\n"
193  "_Contents\tIDM_HELP_CONTENTS\tHelp Contents\tAlt+F1\n"
194  "Conte_xt\tIDM_HELP_CONTEXT\tContext Help\tF1\n"
195  "\tseparator\n"
196  "_About X11workbench\tIDM_HELP_ABOUT\tAbout X11workbench\tAlt+F1\n"
197  "\n"
198  "5\n"
199  "_Toolbox\tIDM_TOOLBOX\tDisplay (or hide) the Toolbox\n"
200  "_Options\tIDM_TOOLS_OPTIONS\tDisplay Options Editor\n"
201  "\n";
202 
204 // edit menu - what I see when there's a child frame active
206 static char szEditMenu[]="1\n"
207  "_File\tpopup\t2\n"
208  "_Edit\tpopup\t4\n"
209  "_Tools\tpopup\t5\n"
210  "\tseparator\n"
211  "_Window\tpopup\t6\n"
212  "\tseparator\n"
213  "_Help\tpopup\t3\n"
214  "\n"
215  "2\n"
216  "_New File\t" FW_FILE_NEW_MENU "\tNew File\t" FW_FILE_NEW_ACCEL "\n"
217  "_Open File\t" FW_FILE_OPEN_MENU "\tOpen File\t" FW_FILE_OPEN_ACCEL "\n"
218  "_Save File\t" FW_FILE_SAVE_MENU "\tSave File\t" FW_FILE_SAVE_ACCEL "\n"
219  "Save _As\t" FW_FILE_SAVE_AS_MENU "\tSave As\t" FW_FILE_SAVE_AS_ACCEL "\n"
220  "Save A_ll\t" FW_FILE_SAVE_ALL_MENU "\tSave All\t" FW_FILE_SAVE_ALL_ACCEL "\n"
221  "\tseparator\n"
222  "_Close File\t" FW_FILE_CLOSE_MENU "\tClose File\tCtrl+F4\n"
223  "\tseparator\n"
224  "E_xit\tIDM_FILE_EXIT\tClose Application\tAlt+F4\n"
225  "\n"
226  "3\n"
227  "_Contents\tIDM_HELP_CONTENTS\tHelp Contents\tAlt+F1\n"
228  "Conte_xt\tIDM_HELP_CONTEXT\tContext Help\tF1\n"
229  "\tseparator\n"
230  "_About X11workbench\tIDM_HELP_ABOUT\tAbout X11workbench\tAlt+F1\n"
231  "\n"
232  "4\n"
233  "_Undo\tIDM_EDIT_UNDO\tUn-do last action\tCtrl+Z\n"
234  "_Redo\tIDM_EDIT_REDO\tRe-do last action\tShift+Ctrl+Z\n"
235  "\tseparator\n"
236  "Cu_t\t" FW_EDIT_CUT_MENU "\tCut to Clipboard\t" FW_EDIT_CUT_ACCEL "\n"
237  "_Copy\t" FW_EDIT_COPY_MENU "\tCopy to Clipboard\t" FW_EDIT_COPY_ACCEL "\n"
238  "_Paste\t" FW_EDIT_PASTE_MENU "\tPaste from Clipboard\t" FW_EDIT_PASTE_ACCEL "\n"
239  "_Delete\t" FW_EDIT_DELETE_MENU "\tDelete selection\n"
240  "\tseparator\n"
241  "Select _All\t" FW_EDIT_SELECT_ALL_MENU "\tSelect All in context\t" FW_EDIT_SELECT_ALL_ACCEL "\n"
242  "Select _None\t" FW_EDIT_SELECT_NONE_MENU "\tSelect None in context\t" FW_EDIT_SELECT_NONE_ACCEL "\n"
243  "\tseparator\n"
244  "_Find\tIDM_EDIT_FIND\tFind within Document\tCtrl+F\n"
245  "Find Ne_xt\tIDM_EDIT_FIND_NEXT\tFind next occurence within Document\tCtrl+G\n"
246  "_Project Find\tIDM_EDIT_PROJ_FIND\tFind within entire project\tCtrl+Shift+F\n"
247  "Pro_ject Find Next\tIDM_EDIT_PROJ_FIND_NEXT\tFind next occurrence within entire project\tCtrl+Shift+G\n"
248  "\n"
249  "5\n"
250  "_Toolbox\tIDM_TOOLBOX\tDisplay (or hide) the Toolbox\n"
251  "_Options\tIDM_TOOLS_OPTIONS\tDisplay Options Editor\n"
252  "\n"
253  "6\n"
254  "Tab _Left\tIDM_TAB_LEFT\tScroll Tab Left\tCtrl+Alt+PgUp\n"
255  "Tab _Right\tIDM_TAB_RIGHT\tScroll Tab Right\tCtrl+Alt+PgDown\n"
256  "\tseparator\n"
257  "Re-order Le_ft\tIDM_TAB_MOVE_LEFT\tScroll Tab Left\tCtrl+Alt+Shift+PgUp\n"
258  "Re-order R_ight\tIDM_TAB_MOVE_RIGHT\tScroll Tab Right\tCtrl+Alt+Shift+PgDown\n"
259  "\tseparator\n" // NOTE: I can add a list of windows here, with hotkeys
260  "\n";
261 
262 
263 // NOTES ON 'F' KEYS - these were compiled by someone else with respect to winders
264 // CTRL+ALT (and +SHIFT) any F key in X11 switches to a virtual desktop in console mode
265 //
266 // F1 - brings up a help window
267 // Alt - system menu
268 // F2 - in winders, rename selected object.
269 // Alt+Ctrl - in MS Orifice, opens documents library (don't do this in X11)
270 // F3 - in winders, open search box
271 // F4 - in winders XP, display address bar list (or similar)
272 // Alt - close application/window
273 // F5 - window refresh/update (such as in Firefox)
274 // F6 - cycle through screen elements of a window
275 // F7 - turn on/off "caret mode" in Firefox; in MS Weird, spell/grammar checking
276 // F8 - extend selection (MS orifice)
277 // F9 - update fields (MS orifice, particularly ExHell)
278 // F10 - activates the menu
279 // SHIFT - pops up a context menu (in X11 at least) like a right-click would
280 // ALT - in X11, maximize/normal (retaining window decorations)
281 // F11 - toggle between "true full-screen" (no window decorations) and normal [works in gnome/mate as well]
282 // F12 - opens 'save as' dialog (in MS Orifice)
283 
284 
285 
286 
287 // menu handler, similar to what MFC does
288 // in theory I can swap in a new menu handler when the window focus changes
289 // this is the DEFAULT handler, when no 'child frame' has the focus. It also handles
290 // all of the OTHER menu stuff, out of convenience
291 
292 FW_MENU_HANDLER_BEGIN(main_menu_handlers)
293  FW_MENU_HANDLER_ENTRY("IDM_FILE_EXIT",FileExitHandler,NULL)
294  FW_MENU_HANDLER_ENTRY("IDM_FILE_NEW",FileNewHandler,NULL)
295  FW_MENU_HANDLER_ENTRY("IDM_FILE_OPEN",FileOpenHandler,NULL)
296  FW_MENU_HANDLER_ENTRY("IDM_FILE_SAVE",FileSaveHandler,FileSaveUIHandler)
297  FW_MENU_HANDLER_ENTRY("IDM_FILE_SAVE_AS",FileSaveAsHandler,FileSaveAsUIHandler)
298  FW_MENU_HANDLER_ENTRY("IDM_FILE_SAVE_ALL",FileSaveAllHandler,FileSaveAllUIHandler)
299 
300  FW_MENU_HANDLER_ENTRY("IDM_FILE_CLOSE",FileCloseHandler,NULL) // TODO: do a UI handler?
301 
302  FW_MENU_HANDLER_ENTRY("IDM_TOOLBOX",ToolBoxHandler,ToolBoxUIHandler)
303 
304  FW_MENU_HANDLER_ENTRY("IDM_TAB_LEFT",TabLeftHandler, TabUIHandler)
305  FW_MENU_HANDLER_ENTRY("IDM_TAB_RIGHT",TabRightHandler, TabUIHandler)
306  FW_MENU_HANDLER_ENTRY("IDM_TAB_MOVE_LEFT",TabMoveLeftHandler, TabUIHandler)
307  FW_MENU_HANDLER_ENTRY("IDM_TAB_MOVE_RIGHT",TabMoveRightHandler, TabUIHandler)
308 
309  FW_MENU_HANDLER_ENTRY("IDM_HELP_ABOUT",HelpAboutHandler,NULL)
310  FW_MENU_HANDLER_ENTRY("IDM_HELP_CONTEXT",HelpContextHandler,NULL)
311  FW_MENU_HANDLER_ENTRY("IDM_HELP_CONTENTS",HelpContentsHandler,NULL)
312 
313 #if 0
314  // additional EDIT MENU HANDLERS
315 
316  FW_MENU_HANDLER_ENTRY("IDM_EDIT_FIND",EditFindHandler,EditFindUIHandler)
317  FW_MENU_HANDLER_ENTRY("IDM_EDIT_FIND_NEXT",EditFindNextHandler,EditFindNextUIHandler)
318  FW_MENU_HANDLER_ENTRY("IDM_EDIT_PROJ_FIND",EditProjFindHandler,EditProjFindUIHandler)
319  FW_MENU_HANDLER_ENTRY("IDM_EDIT_PROJ_FIND_NEXT",EditProjFindNextHandler,EditProjFindNextUIHandler)
320 #endif // 0
321 
323 
324 
325 
326 // end of global variables
327 
328 
329 
330 static void usage(void)
331 {
332  fputs("X11workbench - Copyright (c) 2010-2016 by S.F.T. Inc. - all rights reserved\n\n"
333  "Usage: X11workbench [options] filename [filename [...]]\n"
334  " where 'filename' represents one or more files or workspaces to be opened on startup\n"
335  "\n"
336  "Standard X11workbench Options\n"
337  "-h display this message\n"
338 #ifndef NO_DEBUG
339  "-d dump settings on startup\n"
340 #endif // NO_DEBUG
341 #ifndef NO_SPLASH
342  "--nosplash Skip the 'splash' screen on startup\n"
343 #endif // !NO_SPLASH
344  "\n", stderr);
345 
346  WBToolkitUsage();
347 }
348 
349 static void get_min_window_height_width(int *piMinHeight, int *piMinWidth)
350 {
351  // DEMO CODE - using the 'STRING' size, calculate the minimum width/height and pass that
352  // as parameters to WBInitSizeHints
353 
354  // calculating the actual font height for the default font
355 
356  unsigned long fth = WBGetDefaultFont()->max_bounds.ascent // default font height
357  + WBGetDefaultFont()->max_bounds.descent;
358 
359  // the pad and border are part of the 'hello world' text and green border displayed for the demo
360  unsigned long pad = BORDER; // font padding (only used here)
361  unsigned long bw = 1; // border width (only used here)
362 
363  *piMinHeight = fth + pad * 2 + bw * 2;
364  *piMinWidth = XTextWidth(WBGetDefaultFont(), // minimum width (based on text width)
365  STRING,
366  strlen(STRING)) + pad * 2 + bw * 2;
367 }
368 
369 
371 // do_main - initialization, loop, termination
372 // (the classic top-down model)
374 
375 int do_main(int argc, char *argv[], char *envp[])
376 {
377 //unsigned long fg, bg, bd; /* Pixel values */
378 XEvent event; /* Event received */
379 XSizeHints xsh; /* Size hints for window manager */
380 //XSetWindowAttributes xswa; /* Temporary Set Window Attribute struct */
381 char szGreen[]="#00FF00"; // text color for green
382 Colormap colormap;
383 int i1, iMinHeight, iMinWidth;
384 #ifndef NO_DEBUG
385 int iDebugDumpConfig = 0;
386 #endif // NO_DEBUG
387 #ifndef NO_SPLASH
388 int bNoSplash = 0;
389 #endif // !NO_SPLASH
390 
391 
392  // as opposed to 'getarg' this method is system independent
393  // some POSIX systems don't support 'getarg' correctly
394  // TODO: put it in platform_helper.[ch] and use HAVE_GETARG
395 
396  while(argc > 1)
397  {
398  if(argv[1][0] != '-' || !argv[1][1])
399  {
400  if(argv[1][0] == '-')
401  {
402  argv++;
403  argc--;
404  }
405 
406  break;
407  }
408 
409  if(argv[1][1] == '-') // a double-dash
410  {
411  // double-dash items go here. only 'one per'
412 
413  if(!strcmp(&(argv[1][2]), "nosplash"))
414  {
415  bNoSplash = 1;
416  goto next_argument;
417  }
418 
419  fprintf(stderr, "Unrecognized option \"%s\"\n", argv[1]);
420  usage();
421 
422  return 1; // illegal argument
423  }
424 
425  for(i1=1; argv[1][i1]; i1++)
426  {
427  if(argv[1][i1] == 'h' ||
428  argv[1][i1] == 'H')
429  {
430  usage();
431  return 0;
432  }
433 #ifndef NO_DEBUG
434  else if(argv[1][i1] == 'd')
435  {
436  iDebugDumpConfig = 1;
437  }
438 #endif // NO_DEBUG
439  else
440  {
441  fprintf(stderr, "Unrecognized option \"-%c\"\n", argv[1][i1]);
442 
443  usage();
444  return 1;
445  }
446  }
447 
448 next_argument:
449  argv++;
450  argc--;
451  }
452 
453  // initialization - set signal handlers, open display, etc.
454 
455  SetSignalHandlers();
456 
457  pX11Display = WBInit(NULL); // initialize X11 Work Bench Toolkit and obtain Display pointer
458  if(!pX11Display)
459  {
460  return 1; // can't open display, so byby!
461  }
462 
463  PXM_RegisterAppIcons(icon_app_xpm, application_icon_xpm);
464 
465 
466 #ifndef NO_DEBUG
467  // This DEBUG section dumps the config data when theh '-d' option was specified on the command line.
468  // it requires a Display object so I must do it HERE. Then I can call WBExit and bail out.
469 
470  if(iDebugDumpConfig)
471  {
472  CHDumpConfig();
473 
474  WBExit();
475  return 0;
476  }
477 #endif // NO_DEBUG
478 
479  // SUPPLEMENTAL INITIALIZATION STUFF
480 
481  // color selection
482 
483 // bd = WhitePixel(pX11Display, DefaultScreen(pX11Display)); // border
484 // bg = BlackPixel(pX11Display, DefaultScreen(pX11Display)); // background
485 // fg = WhitePixel(pX11Display, DefaultScreen(pX11Display)); // foreground
486 
487  // dont' forget the colormap
488 
489  colormap = WBDefaultColormap(pX11Display);
490 
491  // additional allocated colors - in this case, GREEN
492  XParseColor(pX11Display, colormap, szGreen, &clrGreen);
493  XAllocColor(pX11Display, colormap, &clrGreen); // NOTE: do I need 'XFreeColors' for 'clrGreen' ?
494 
495 // 11 bits U+07FF 110xxxxx 10xxxxxx
496 // A9 --> 10101001 --> 11000010 10101001 --> C2 A9
497 #define UTF8_COPYRIGHT "\xc2""\xa9"
498 
499 #ifndef NO_SPLASH
500  if(!bNoSplash)
501  {
502  DLGSplashScreen(splash_xpm,
503 // "Copyright " UTF8_COPYRIGHT " 2010-2016 by Big Bad Bombastic Bob\nAll Rights Reserved", // text string with unicode char in it U+00A9
504  "Copyright (c) 2010-2016 by Big Bad Bombastic Bob\nAll Rights Reserved", // 1 or 2 lines only
505  WhitePixel(pX11Display, DefaultScreen(pX11Display))); // white text
506  }
507 #endif // NO_SPLASH
508 
509  get_min_window_height_width(&iMinHeight, &iMinWidth);
510 
511  // I will need to center the new window, so figure out how to do that
512 
513  WBInitSizeHints(&xsh, // pointer to XSizeHints
514  pX11Display, // Display pointer
515  iMinHeight, // minimum height (based on font height)
516  iMinWidth); // minimum width
517 
518 // // init window attributes
519 //
520 // WBInitWindowAttributes(&xswa, // attribute structure
521 // bd, // border pixel color
522 // bg, // background window color
523 // colormap, // colormap
524 // CenterGravity); // gravity
525 
526  // create frame window object (always uses default display)
527 
528  pMainFrame = FWCreateFrameWindow("X11workbench", // title
529  ID_APPLICATION, // icon
530  szAppMenu, // application menu
531  xsh.x, xsh.y, // position
532  xsh.width, xsh.height, // size
533  MyWindowCallback, // callback
534  WBFrameWindow_APP_WINDOW // flags and attributes
537 
538  if(!pMainFrame)
539  {
540  WB_ERROR_PRINT("%s - Unable to create main frame window\n", __FUNCTION__);
541 
542  WBExit();
543  return 2;
544  }
545 
546 // {
547 // const char *pNothing = szEditMenu; // to avoid certain warnings - remove later
548 //
549 // pNothing = pNothing;
550 // }
551 
552  // assign menu handlers to the frame window (this does the callback functions for me)
553  // this is part of the frame window functionality for the DEFAULT menu
554 
555  FWSetMenuHandlers(pMainFrame, main_menu_handlers);
556 
557  //=========================
558  // MAIN MESSAGE LOOP
559  //=========================
560 
561  while (!bQuitFlag /*1*/)
562  {
563  if(!WBCheckGetEvent(pX11Display, &event))
564  {
565  // SLEEP if no event while in message loop (function returns without blocking)
566  // otherwise I can do background tasks during this loop iteration.
567 
568  // if I have NO BACKGROUND PROCESSES to do, I can use 'WBWaitForEvent'
569 
570  if(1)
571  {
572  WBWaitForEvent(pX11Display);
573  }
574  else
575  {
576 #ifdef HAVE_NANOSLEEP
577  struct timespec tsp;
578  tsp.tv_sec = 0;
579  tsp.tv_nsec = 500000; // wait for .5 msec
580 
581  nanosleep(&tsp, NULL);
582 #else // HAVE_NANOSLEEP
583 
584  usleep(500); // 100 microsecs - a POSIX alternative to 'nanosleep'
585 
586 #endif // HAVE_NANOSLEEP
587  }
588 
589  continue; // skip the 'WBDispatch' since there was no event
590  }
591 
592  WBDispatch(&event);
593  }
594 
595  if(pMainFrame)
596  {
597  WBDestroyWindow(pMainFrame->wID);
598  }
599 
600  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Application,
601  "%s - Application about to exit\n", __FUNCTION__);
602 
603  WBExit();
604 
605  return 0;
606 }
607 
608 
609 
611 // CALLBACK FUNCTIONS
613 
614 extern void TestFunc(Display *pDisplay, GC gc, Window wID, int iX, int iY);
615 
616 static int MyWindowCallback(Window wID, XEvent *pEvent)
617 {
618 XWindowAttributes xwa; /* Temp Get Window Attribute struct */
619 int iRval = 0;
620 
621  /*
622  * On the last of each group of Expose events, repaint the entire
623  * window. See Section 8.4.5.1.
624  */
625 
626  if(pEvent->type == DestroyNotify &&
627  pEvent->xdestroywindow.window == wID)
628  {
629  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Application,
630  "%s - DestroyNotify\n", __FUNCTION__);
631 
632  if(pMainFrame && pMainFrame->wID == wID)
633  {
634  pMainFrame = NULL; // because I'm destroying it
635  }
636 
637  return 0; // let remaining default processing happen
638  }
639 
640  if(pEvent->type == ClientMessage &&
641  pEvent->xclient.message_type == aQUERY_CLOSE)
642  {
643  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Application,
644  "%s - QUERY_CLOSE\n", __FUNCTION__);
645 
646  if(pEvent->xclient.data.l[0]) // close is imminent if I return 0
647  {
648  if(pMainFrame && pMainFrame->wID == wID)
649  {
650  pMainFrame = NULL; // because I'm destroying it
651  }
652  }
653 
654  return 0; // "ok to close" (let frame window handle anything else)
655  }
656 
657  if (pEvent->type == Expose && pEvent->xexpose.count == 0)
658  {
659  GC gc;
660  WB_GEOM geom;
661 
662 // NOTE: this is managed by the toolkit
663 // /*
664 // * Remove any other pending Expose events from the queue to
665 // * avoid multiple repaints. See Section 8.7.
666 // */
667 // while(!bQuitFlag && XCheckTypedWindowEvent(pX11Display, wID, Expose, pEvent))
668 // ;
669 //
670 // if(bQuitFlag)
671 // {
672 // iRval = 1;
673 // WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Application,
674 // "%s - Quit flag set - returning %d\n", __FUNCTION__, iRval);
675 //
676 // return iRval; // let default processing happen
677 // }
678 
679  /*
680  * Find out how big the window is now, so that we can center
681  * the text in it.
682  */
683  if(XGetWindowAttributes(pX11Display, wID, &xwa) == 0)
684  {
685  WB_ERROR_PRINT("%s - Cannot get correct attributes for window! Returning %d\n",
686  __FUNCTION__, iRval);
687  return iRval;
688  }
689 
690  gc = WBBeginPaint(wID, &(pEvent->xexpose), &geom); // begin paint, get a gc for it
691 
692  if(gc == None)
693  {
694  WB_ERROR_PRINT("%s - Cannot get graphics context for paint\n", __FUNCTION__);
695  return 0; // not handled
696  }
697 
698  WBClearWindow(wID, gc); // does the erase background intelligently
699 
700 
701 #ifdef DISPLAY_HELLO_WORLD
702  {
703  XFontStruct *pFont;
704  int x, y, x0, y0, x1, y1;
705 
706  pFont = WBGetWindowFontStruct(wID);
707  if(!pFont)
708  {
709  WB_ERROR_PRINT("%s - No font - returning %d\n", __FUNCTION__, iRval);
710  return iRval; // let default processing happen
711  }
712 
713  x0 = XTextWidth(pFont, STRING, strlen(STRING));
714  y0 = pFont->max_bounds.ascent
715  - pFont->max_bounds.descent;
716 
717  x = (xwa.width - x0) / 2;
718  y = (xwa.height + pFont->max_bounds.ascent
719  - pFont->max_bounds.descent) / 2;
720 
721  // adjust new values for X0 and y0 to surround x and y
722 
723  x1 = x + x0 + 20;
724  x0 = x - 20;
725 
726  y1 = y + pFont->max_bounds.descent + 20;
727  y0 = y - y0 - 20;
728 
729  /*
730  * Fill the window with the background color, and then paint
731  * the centered string.
732  */
733 
734  XSetForeground(pX11Display, gc, WBGetWindowFGColor(wID));
735  XDrawString(pX11Display, wID, gc, x, y, STRING, strlen(STRING));
736 
737  XSetForeground(pX11Display, gc, clrGreen.pixel);
738 
739  // draw my green rectangle
740 
741  XDrawRectangle(pX11Display, wID, gc, x0, y0, x1 - x0, y1 - y0);
742  XSetForeground(pX11Display, gc, WBGetWindowFGColor(wID)); // restore it at the end
743  }
744 #endif // DISPLAY_HELLO_WORLD
745 
746 // TestFunc(pX11Display, gc, wID, x0 - 32, y0 - 32); // TEMPORARY
747 
748  WBEndPaint(wID, gc); // done now [free resources]
749 
750  iRval = 1; // processed
751 
752  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Window,
753  "%s - Expose returning %d\n", __FUNCTION__, iRval);
754 
755  return iRval; // let default processing happen
756  }
757 
758  // menu events
759  if(pEvent->type == ClientMessage && pEvent->xclient.message_type == aMENU_COMMAND)
760  {
761  iRval = 1;
762 
763  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Window | DebugSubSystem_Menu | DebugSubSystem_Event,
764  "%s - detecting main window menu event %ld\n", __FUNCTION__,
765  pEvent->xclient.data.l[0]);
766  }
767 
768  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Window,
769  "%s - Returning %d\n", __FUNCTION__, iRval);
770 
771  return iRval; // let default processing happen if zero, else 'processed'
772 }
773 
774 
776 // __ __ ____ _ _ _ _ //
777 // | \/ | ___ _ __ _ _ / ___|__ _| | | |__ __ _ ___| | _____ //
778 // | |\/| |/ _ \ '_ \| | | | | | / _` | | | '_ \ / _` |/ __| |/ / __| //
779 // | | | | __/ | | | |_| | | |__| (_| | | | |_) | (_| | (__| <\__ \ //
780 // |_| |_|\___|_| |_|\__,_| \____\__,_|_|_|_.__/ \__,_|\___|_|\_\___/ //
781 // //
783 
784 static int FileExitHandler(XClientMessageEvent *pEvent)
785 {
786  // TODO: 'OnExit' processing for open files (files to be saved, etc.)
787 
788  bQuitFlag = 1; // time to die
789 
790  return 1; // handled
791 }
792 
793 static int FileNewHandler(XClientMessageEvent *pEvent)
794 {
795 WBEditWindow *pEW;
796 
797  if(!pMainFrame)
798  {
800  "File New", "'File _New' and no container window");
801  return 1;
802  }
803 
804  // create a new child frame within the main frame
805  // this should be pretty straightforward as I implement it properly
806 
807  // 1st, create a new 'WBEditWindow', attaching it to the frame
808 
809  pEW = WBCreateEditWindow(pMainFrame, NULL, szEditMenu, main_menu_handlers, 0);
810 
811  if(!pEW)
812  {
814  "File New", "'File _New' unable to create edit window");
815  }
816 
817  return 1; // handled
818 }
819 
820 static int FileOpenHandler(XClientMessageEvent *pEvent)
821 {
822  Window wIDOwner = pMainFrame ? pMainFrame->wID : -1;
823  WBEditWindow *pEW;
824  char *pFile;
825 
826  pFile = DLGFileDialog(FileDialog_Open, wIDOwner, ".", "",
827  ".txt\tText Files\n"
828  ".c\tC language file\n"
829  ".cpp\tC language file\n"
830  ".h\tC language header\n"
831  "Makefile\tMake Files\n"
832  ".mk\tMake 'include' Files\n"
833  ".am\tAutotools File\n"
834  ".*\tAll Files\n");
835 
836  if(pFile)
837  {
838 // DLGMessageBox(MessageBox_OK, wIDOwner,
839 // "File Open", pFile);
840 // // TODO: do something with file name
841 
842  // create a new tab in the frame window
843  // 1st, create a new 'WBEditWindow', attaching it to the frame
844 
845  pEW = WBCreateEditWindow(pMainFrame, NULL, szEditMenu, main_menu_handlers, 0);
846 
847  if(!pEW)
848  {
850  "File Open", "'File _Open' unable to create edit window");
851  }
852 
853  // next, load the contents of the file into it
854 
855  if(WBEditWindowLoadFile(pEW, pFile))
856  {
858  "File Open", "'File _Open' unable to read file into edit window");
859  }
860 
861  WBFree(pFile); // required resource cleanup
862  }
863 
864  return 1; // handled
865 }
866 
867 static int FileSaveHandler(XClientMessageEvent *pEvent)
868 {
869  Window wIDOwner = pMainFrame ? pMainFrame->wID : -1;
870 
872  "File Save",
873  "'File _Save' not currently implemented");
874 
875  return 1; // handled
876 }
877 
878 static int FileSaveUIHandler(WBMenu *pMenu, WBMenuItem *pItem)
879 {
880  return -1; // disabled
881 }
882 
883 static int FileSaveAsHandler(XClientMessageEvent *pEvent)
884 {
885  Window wIDOwner = pMainFrame ? pMainFrame->wID : -1;
886 
888  "File Save As",
889  "'File Save _As' not currently implemented");
890 
891  return 1; // handled
892 }
893 
894 static int FileSaveAsUIHandler(WBMenu *pMenu, WBMenuItem *pItem)
895 {
896  return -1; // disabled
897 }
898 
899 static int FileSaveAllHandler(XClientMessageEvent *pEvent)
900 {
901  Window wIDOwner = pMainFrame ? pMainFrame->wID : -1;
902 
904  "File Save All",
905  "'File Save A_ll' not currently implemented");
906 
907  return 1; // handled
908 }
909 
910 static int FileSaveAllUIHandler(WBMenu *pMenu, WBMenuItem *pItem)
911 {
912  return -1; // disabled
913 }
914 
915 static int HelpAboutHandler(XClientMessageEvent *pEvent)
916 {
917  Window wIDOwner = pMainFrame ? pMainFrame->wID : -1;
918 
919 
920  DLGMessageBox(MessageBox_OK | /*MessageBox_Info*/ MessageBox_App, wIDOwner,
921  "About X11workbench",
922  "X11workbench - BBB's answer to a proper development environment for X11");
923 
924  return 1; // handled
925 }
926 
927 static int FileCloseHandler(XClientMessageEvent *pEvent)
928 {
929  WBChildFrame *pC;
930 
931  if(!pMainFrame)
932  {
933  return 0;
934  }
935 
936  pC = FWGetFocusWindow(pMainFrame);
937 
938  if(pC)
939  {
940  // TODO: check for 'safe to close it' ?
941 
942  FWRemoveContainedWindow(pMainFrame, pC);
943 
944  FWDestroyChildFrame(pC); // this will destroy the super-class as well
945  }
946 
947  return 1;
948 }
949 
950 static int TabLeftHandler(XClientMessageEvent *pEvent)
951 {
952 int iIndex;
953 
954  if(!pMainFrame)
955  {
956  return 0;
957  }
958 
959  iIndex = FWGetChildFrameIndex(pMainFrame, NULL);
960 
961  WB_ERROR_PRINT("TEMPORARY: %s - move left %d\n", __FUNCTION__, iIndex);
962 
963  if(iIndex > 0)
964  {
965  FWSetFocusWindowIndex(pMainFrame, iIndex - 1);
966  }
967 
968  return 1;
969 }
970 
971 static int TabRightHandler(XClientMessageEvent *pEvent)
972 {
973 int iIndex;
974 
975  if(!pMainFrame)
976  {
977  return 0;
978  }
979 
980  iIndex = FWGetChildFrameIndex(pMainFrame, NULL);
981 
982  WB_ERROR_PRINT("TEMPORARY: %s - move right %d\n", __FUNCTION__, iIndex);
983 
984  if(iIndex >= 0)
985  {
986  FWSetFocusWindowIndex(pMainFrame, iIndex + 1);
987  }
988 
989  return 1;
990 }
991 
992 static int TabMoveLeftHandler(XClientMessageEvent *pEvent)
993 {
994  WBChildFrame *pC;
995 
996  if(!pMainFrame)
997  {
998  return 0;
999  }
1000 
1001  pC = FWGetFocusWindow(pMainFrame);
1002 
1003  if(pC)
1004  {
1005  FWMoveChildFrameTabIndex(pMainFrame, pC, -1);
1006  }
1007 
1008  return 1;
1009 }
1010 
1011 static int TabMoveRightHandler(XClientMessageEvent *pEvent)
1012 {
1013  WBChildFrame *pC;
1014 
1015  if(!pMainFrame)
1016  {
1017  return 0;
1018  }
1019 
1020  pC = FWGetFocusWindow(pMainFrame);
1021 
1022  if(pC)
1023  {
1024  FWMoveChildFrameTabIndex(pMainFrame, pC, -2);
1025  }
1026 
1027  return 1;
1028 }
1029 
1030 static int TabUIHandler(WBMenu *pMenu, WBMenuItem *pMenuItem)
1031 {
1032  if(FWGetContainedWindowByIndex(pMainFrame, 1) != NULL)
1033  {
1034  return 1; // enabled (more than one tab)
1035  }
1036 
1037  return -1; // disabled (zero or one child frames)
1038 }
1039 
1040 static int ToolBoxHandler(XClientMessageEvent *pEvent)
1041 {
1042  DLGMessageBox(MessageBox_WTF | MessageBox_No, None, "X11workbench - Unimplemented",
1043  "ToolBox Handler not (yet) implemented");
1044 
1045  return 1; // "handled"
1046 }
1047 
1048 static int ToolBoxUIHandler(WBMenu *pMenu, WBMenuItem *pMenuItem)
1049 {
1050  return 1; // enabled (TODO: check # of tabs, etc.)
1051 }
1052 
1053 
1054 
1055 static int HelpContentsHandler(XClientMessageEvent *pEvent)
1056 {
1057  Window wIDOwner = pMainFrame ? pMainFrame->wID : -1;
1058 
1059  char *pTemp = DLGInputBox(wIDOwner, "Help Contents", "Enter a search term for the documentation", "WBInit", -1, -1);
1060 
1061  if(pTemp)
1062  {
1063  DoContextSensitiveHelp(pTemp);
1064 
1065  WBFree(pTemp);
1066  }
1067 // else
1068 // {
1069 // DLGMessageBox(MessageBox_OK | MessageBox_Bang, wIDOwner,
1070 // "Something Bad Happened", "Unexpected 'NULL' return from DLGInputBox");
1071 // }
1072 
1073  return 1; // handled
1074 }
1075 
1076 static int HelpContextHandler(XClientMessageEvent *pEvent)
1077 {
1078  Window wIDOwner = pMainFrame ? pMainFrame->wID : -1;
1079 
1081  "Context Help", "TODO: implement the context-sensitive help");
1082 
1083 // DoContextSensitiveHelp(szWhateverWord);
1084 
1085  return 1; // handled
1086 }
1087 
1088 
1089 
1090 
1092 // SIGNAL HANDLER //
1094 
1095 
1096 typedef struct _xsiginfo_
1097 {
1098  const char *szSignal;
1099  int nSignal;
1100 } XSIGINFO;
1101 
1102 #define SIGINFO_ENTRY(X) { #X, X }
1103 static const XSIGINFO aSigInfo[] =
1104 {
1105 SIGINFO_ENTRY(SIGHUP), // terminate process terminal line hangup
1106 SIGINFO_ENTRY(SIGINT), // terminate process interrupt program
1107 SIGINFO_ENTRY(SIGQUIT), // create core image quit program
1108 SIGINFO_ENTRY(SIGILL), // create core image illegal instruction
1109 SIGINFO_ENTRY(SIGTRAP), // create core image trace trap
1110 SIGINFO_ENTRY(SIGABRT), // create core image abort(3) call (formerly SIGIOT)
1111 #ifdef SIGEMT
1112 SIGINFO_ENTRY(SIGEMT), // create core image emulate instruction executed
1113 #endif // SIGEMT
1114 SIGINFO_ENTRY(SIGFPE), // create core image floating-point exception
1116 SIGINFO_ENTRY(SIGBUS), // create core image bus error
1117 SIGINFO_ENTRY(SIGSEGV), // create core image segmentation violation
1118 SIGINFO_ENTRY(SIGSYS), // create core image non-existent system call invoked
1119 SIGINFO_ENTRY(SIGPIPE), // terminate process write on a pipe with no reader
1120 SIGINFO_ENTRY(SIGALRM), // terminate process real-time timer expired
1121 SIGINFO_ENTRY(SIGTERM), // terminate process software termination signal
1122 SIGINFO_ENTRY(SIGURG), // discard signal urgent condition present on socket
1124 //SIGINFO_ENTRY(SIGTSTP), // stop process stop signal generated from keyboard
1125 //SIGINFO_ENTRY(SIGCONT), // discard signal continue after stop
1126 SIGINFO_ENTRY(SIGCHLD), // discard signal child status has changed
1127 SIGINFO_ENTRY(SIGTTIN), // stop process background read attempted from control terminal
1128 SIGINFO_ENTRY(SIGTTOU), // stop process background write attempted to control terminal
1129 SIGINFO_ENTRY(SIGIO), // discard signal I/O is possible on a descriptor (see fcntl(2))
1130 SIGINFO_ENTRY(SIGXCPU), // terminate process cpu time limit exceeded (see setrlimit(2))
1131 SIGINFO_ENTRY(SIGXFSZ), // terminate process file size limit exceeded (see setrlimit(2))
1132 SIGINFO_ENTRY(SIGVTALRM),// terminate process virtual time alarm (see setitimer(2))
1133 SIGINFO_ENTRY(SIGPROF), // terminate process profiling timer alarm (see setitimer(2))
1134 SIGINFO_ENTRY(SIGWINCH), // discard signal Window size change
1135 #ifdef SIGINFO
1136 SIGINFO_ENTRY(SIGINFO), // discard signal status request from keyboard
1137 #endif // SIGINFO
1138 SIGINFO_ENTRY(SIGUSR1), // terminate process User defined signal 1
1139 SIGINFO_ENTRY(SIGUSR2), // terminate process User defined signal 2
1140 {NULL, 0} // marks end of list
1141 };
1142 
1143 
1144 static void signalproc(int iSig)
1145 {
1146 static int bCtrlCFlag = 0; // when I hit ctrl+c multiple times, kill the app anyway in case of infinite loop or something
1147 
1148  int i1;
1149 
1150 #if 0 /* this is broken now that I have a worker thread - no longer compatible */
1151  if(iSig == SIGTSTP) // suspend - special 'way cool' handling here [auto-fork!]
1152  {
1153 
1154  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Application,
1155  "\n**********************************************"
1156  "\nSignal received: SIGTSTP (detaching via fork)"
1157  "\n The process has now gone into the background"
1158  "\n and is still running (i.e. not suspended)."
1159  "\n**********************************************\n");
1160  fflush(stderr);
1161 
1162  // POSSIBLE (ALTERNATE) NON-FORKING METHOD
1163  // see man termios(4) - change process group of terminal, force me into the background
1164  // I change the controlling terminal process group to that of the shell that created me
1165 // {
1166 // pid_t idgParent;
1167 // int idParent;
1168 // idParent = getppid(); // parent PID
1169 // idgParent = getpgid(idParent); // get process group ID using parent's process id
1170 //
1171 // if(idgParent != -1) // wasn't an error
1172 // tcsetpgrp(0, idgParent);
1173 //
1174 // if(idParent != -1)
1175 // kill(idParent, SIGCHLD); // child process status changed
1176 // }
1177 
1178  // to simplify matters, I simply fork the process and exit from the original.
1179  // This satisfies the shell's need to wait for the original process ID when it's
1180  // not in the background, and effectively keeps the process running on the GUI.
1181  //
1182  // De nada. Bitte. You're welcome.
1183 
1184  if(fork()) // non-zero means fork failed or NOT the forked process
1185  {
1186  exit(0);
1187  }
1188  return;
1189  }
1190  else if(iSig == SIGCONT) // continue
1191  {
1192  return; // for now, just do nothing
1193  }
1194 #endif // 0
1195 
1196  for(i1=0; i1 < sizeof(aSigInfo)/sizeof(aSigInfo[0]); i1++)
1197  {
1198  if(!aSigInfo[i1].szSignal)
1199  {
1200  break;
1201  }
1202 
1203  if(aSigInfo[i1].nSignal == iSig)
1204  {
1205  WB_DEBUG_PRINT(DebugLevel_ERROR | DebugSubSystem_Application,
1206  "\nX11workbench - Signal received: %s\n", aSigInfo[i1].szSignal);
1207 
1208  // only some signals will actually terminate the application. I shall look for those now.
1209 
1210  if(iSig == SIGHUP || iSig == SIGINT || iSig == SIGQUIT ||
1211  iSig == SIGILL || iSig == SIGTRAP || iSig == SIGABRT ||
1212 #ifdef SIGEMT
1213  iSig == SIGEMT ||
1214 #endif // SIGEMT
1215  iSig == SIGSYS || iSig == SIGTERM || iSig == SIGSTOP ||
1216  iSig == SIGXCPU || iSig == SIGXFSZ || iSig == SIGFPE)
1217  {
1218  if(bQuitFlag && (iSig == SIGSTOP || iSig == SIGTERM || iSig == SIGINT))
1219  {
1220  // this section allows you to use TERM STOP or INT 3 times in a row to guarantee
1221  // that it will kill the process
1222  if(!bCtrlCFlag)
1223  {
1224  WB_ERROR_PRINT("\n----------------------------------------------------------------------------"
1225  "\n X11workbench CTRL+C signal handler"
1226  "\nINT/STOP/TERM, waiting for soft termination - signal again to terminate NOW!"
1227  "\n----------------------------------------------------------------------------\n");
1228  fflush(stderr); // make sure it flushes output IMMEDIATELY
1229  bCtrlCFlag = 1; // if I get another, I kill it
1230  }
1231  else
1232  {
1233  WB_ERROR_PRINT("\n----------------------------------------------------------------------------"
1234  "\n X11workbench CTRL+C signal handler"
1235  "\nINT/STOP/TERM, aborting waiting for soft termination - terminate RIGHT NOW!!"
1236  "\n----------------------------------------------------------------------------\n");
1237  exit(1); // exit with abnormal termination
1238  }
1239  }
1240  else
1241  {
1242  bCtrlCFlag = 0; // reset it (quit was aborted??)
1243  }
1244 
1245  bQuitFlag = TRUE; // soft termination
1246  }
1247 
1248  if(iSig == SIGKILL || iSig == SIGSEGV) // special handling, must terminate process
1249  {
1250  WB_ERROR_PRINT("caught signal %d, terminating now\n", iSig);
1251 
1252  exit(0);
1253  }
1254 
1255  if(iSig == SIGBUS) // might happen
1256  {
1257  WB_DEBUG_PRINT(DebugLevel_ERROR | DebugSubSystem_Application,
1258  "SIGBUS received (this is usually caused by use of a data pointer instead of a function pointer)\n");
1259  exit(1);
1260  }
1261 
1262  return;
1263  }
1264  }
1265 
1266  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Application,
1267  "signal proc (%d) (%08xH)\n", iSig, iSig);
1268 
1269 // bQuitFlag = TRUE;
1270 }
1271 
1272 
1273 static void SetSignalHandlers()
1274 {
1275  int i1;
1276  for(i1=0; i1 < sizeof(aSigInfo)/sizeof(aSigInfo[0]); i1++)
1277  {
1278  if(!aSigInfo[i1].szSignal)
1279  break;
1280 
1281  if(signal(aSigInfo[i1].nSignal, signalproc))
1282  {
1283  WB_WARN_PRINT("%s - WARNING: Unable to assign signal proc for %s\n", __FUNCTION__, aSigInfo[i1].szSignal);
1284  }
1285  }
1286 }
1287 
1288 
1289 
1290 
1292 // ____ _ _ _ _ _ //
1293 // / ___|___ _ __ | |_ _____ _| |_ | | | | ___| |_ __ //
1294 // | | / _ \| '_ \| __/ _ \ \/ / __| | |_| |/ _ \ | '_ \ //
1295 // | |__| (_) | | | | || __/> <| |_ | _ | __/ | |_) | //
1296 // \____\___/|_| |_|\__\___/_/\_\\__| |_| |_|\___|_| .__/ //
1297 // |_| //
1299 
1300 // CONTEXT SENSITIVE HELP NOTES using DOXYGEN GENERATED DOCS
1301 
1302 // doxygen generates doc/html/group__*.html files that will contain the desired documentation
1303 //
1304 // Each documented item has a tag similar to the following:
1305 //
1306 // <!-- doxytag: member="window_helper.h::WBSetInputFocus" ref="ga28d82cd699b08cf93278ae26c5ad4788" args="(Window wID)" -->
1307 //
1308 // jumping to the correct HTML link with the anchor set to the 'ref' value will open it up in doxygen, as
1309 //
1310 // file:///usr/local/share/X11workbench/doc/html/group__wcore.html#ga28d82cd699b08cf93278ae26c5ad4788
1311 //
1312 
1313 static char * InternalMan2Html(const char *szTerm, const char *szText);
1314 
1315 void DoContextSensitiveHelp(const char *szTerm)
1316 {
1317 char szDocFilePath[PATH_MAX * 2], szName[PATH_MAX];
1318 char *p1, *p2, *p3;//, *p5;
1319 const char *p4;
1320 void *pSettings, *pDirList;
1321 int i1;
1322 unsigned long dwAttr;
1323 FILE *pTemp;
1324 WB_FILE_HANDLE hProcess;
1325 static char szLineBuf[4096], szDoxyTag[PATH_MAX * 2 + 512], szHelpBrowser[PATH_MAX];
1326 
1327 
1328  // step 1: get 'docs' directory from settings, and if there isn't a setting for it,
1329  // use the current directory + "docs/html"
1330 
1331  bzero(szDocFilePath, sizeof(szDocFilePath));
1332 
1333 
1334  pSettings = CHOpenConfFile(APP_NAME, CH_FLAGS_DEFAULT);
1335 
1336  if(pSettings)
1337  {
1338  if(0 >= CHGetConfFileString(pSettings, "paths", "browser", szHelpBrowser, sizeof(szHelpBrowser)))
1339  {
1340  WB_ERROR_PRINT("ERROR did not find 'paths' 'browser', using default\n");
1341  goto find_url_opener;
1342  }
1343 // else
1344 // {
1345 // WB_ERROR_PRINT("TEMPORARY: %s - browser \"%s\"\n", __FUNCTION__, szHelpBrowser);
1346 // }
1347 
1348  i1 = CHGetConfFileString(pSettings, "paths","documentation",szDocFilePath, sizeof(szDocFilePath) - 2);
1349 
1350  CHCloseConfFile(pSettings);
1351  pSettings = NULL;
1352  }
1353  else
1354  {
1355 find_url_opener:
1356 
1357  i1 = 0;
1358 
1359  p1 = CHGetMimeDefaultApp("text/html");
1360  p2 = p3 = NULL;
1361 
1362  if(!p1 || *p1 <= ' ')
1363  {
1364  if(p1)
1365  {
1366  WBFree(p1);
1367  }
1368 
1369  p1 = CHGetMimeDefaultApp("x-scheme-handler/http"); // Mate uses this one
1370  }
1371 
1372  if(p1 && *p1 > ' ')
1373  {
1374  if(strlen(p1) > 8 /* strlen(".desktop") */ &&
1375  !memcmp(p1 + strlen(p1) - 8, ".desktop", 8)) // it's a '.desktop' file
1376  {
1377  p2 = p1;
1378  p1 = CHGetDesktopFileInfo(p2, "Exec");
1379  // NOTE: the 'Exec' string will have a '%f' or '%u' or similar in it. trim that.
1380 
1381  if(!p1)
1382  {
1383  p1 = p2; // restore original, hope it still works
1384  }
1385  else
1386  {
1387  WBFree(p2);
1388  p2 = p1 + strlen(p1);
1389 
1390  while(p2 > p1 && *(p2 - 1) <= ' ')
1391  {
1392  *(--p2) = 0; // right-trim
1393  }
1394 
1395  if(p2 >= p1 + 2 && *(p2 - 2) == '%' &&
1396  (*(p2 - 1) == 'u' || *(p2 - 1) == 'U' || *(p2 - 1) == 'f' || *(p2 - 1) == 'F'))
1397  {
1398  p2 -= 2;
1399  *p2 = 0; // trim off argument
1400 
1401  while(p2 > p1 && *(p2 - 1) <= ' ')
1402  {
1403  *(--p2) = 0; // right-trim
1404  }
1405  }
1406 
1407  // NOTE: for _NOW_ assume that there are no extra parameters [later I'll fix that...]
1408  }
1409  }
1410 
1411  strcpy(szHelpBrowser, p1);
1412  WBFree(p1);
1413 
1414  p1 = NULL;
1415  p2 = NULL;
1416  }
1417  else
1418  {
1419  if(p1)
1420  {
1421  WBFree(p1);
1422  }
1423 
1424  WB_ERROR_PRINT("%s - No default browser available!\n", __FUNCTION__);
1425  return;
1426  }
1427  }
1428 
1429  if(i1 <= 0)
1430  {
1431  p1 = WBGetCanonicalPath("doc/html/");
1432  if(p1)
1433  {
1434  strncpy(szDocFilePath, p1, sizeof(szDocFilePath) - 2);
1435  WBFree(p1);
1436  }
1437  else
1438  {
1439  strcpy(szDocFilePath, "doc/html/");
1440  }
1441  }
1442 
1443  if(szDocFilePath[strlen(szDocFilePath) - 1] != '/')
1444  {
1445  strcat(szDocFilePath, "/");
1446  }
1447 
1448 // WB_ERROR_PRINT("TEMPORARY: %s - szDocFilePath = \"%s\"\n", __FUNCTION__, szDocFilePath);
1449 // WB_ERROR_PRINT(" %s - szTerm = \"%s\"\n", __FUNCTION__, szTerm);
1450 
1451  p1 = szDocFilePath + strlen(szDocFilePath);
1452 
1453  // now that THAT mess is over with, determine which file has the appropriate term in it,
1454  // and invoke the default web browser to open that link. I'll do this using 'xdg-open'.
1455 
1456  strcpy(p1, "group__*.html");
1457 
1458  pDirList = WBAllocDirectoryList(szDocFilePath);
1459 
1460  *p1 = 0; // 'szDocFilePath' is once again, ONLY a path
1461  szDoxyTag[0] = 0; // it will be a flag
1462 
1463  if(!pDirList)
1464  {
1465  WB_ERROR_PRINT("ERROR: %s no dir list \"%s\"\n", __FUNCTION__, szDocFilePath);
1466  return;
1467  }
1468  else
1469  {
1470  while(!WBNextDirectoryEntry(pDirList, szName, sizeof(szName), &dwAttr))
1471  {
1472  if(S_ISREG(dwAttr))
1473  {
1474  strcpy(p1, szName);
1475 
1476 // WB_ERROR_PRINT("TEMPORARY: opening \"%s\"\n", szDocFilePath);
1477 
1478  pTemp = fopen(szDocFilePath, "r");
1479  if(!pTemp)
1480  {
1481  WB_ERROR_PRINT("ERROR: %s - unable to open \"%s\"\n", __FUNCTION__, szDocFilePath);
1482  }
1483  else
1484  {
1485  *p1 = 0; // file name not needed now, but path name IS needed
1486 
1487  while(!feof(pTemp))
1488  {
1489  if(!fgets(szLineBuf, sizeof(szLineBuf), pTemp))
1490  {
1491  break;
1492  }
1493 
1494  // THIS METHOD APPLIES TO Doxygen version 1.8.3.1 and later
1495  // (it has also been tested with Doxygen 1.8.13.2)
1496  // If you have an earlier version of doxygen, consider UPGRADING PLEASE!
1497  // If you cannot upgrade Doxygen, and this API does not work with your
1498  // generated documentation, consider downloading the pre-built documentation.
1499 
1500  if(NULL != (p2 = strstr(szLineBuf, "class=\"el\"")))
1501  {
1502  while(p2 > szLineBuf && *p2 != '<') // search for start of tag
1503  {
1504  p2--;
1505  }
1506 
1507  if(p2 >= szLineBuf && *p2 == '<')
1508  {
1509  // parse the tag, find 'href'
1510 
1511  p4 = CHFindEndOfXMLTag(p2, -1); // XML parse helper
1512 
1513  if(*p4 == '>')
1514  {
1515  // the search term will be the 'anchor' term.
1516 
1517  if(!memcmp(p4 + 1, szTerm, strlen(szTerm)) &&
1518  !memcmp(p4 + 1 + strlen(szTerm), "</a>", 4)) // ending tag
1519  {
1520  p3 = CHParseXMLTagContents(p2, p4 - p2);
1521  if(p3)
1522  {
1523  // now that I have the tag, grab the 'href' member
1524  for(p2=p3; *p2; p2 += strlen(p2) + 1)
1525  {
1526  if(!strncmp(p2, "href=", 5))
1527  {
1528  strcpy(szDoxyTag, "file://");
1529  strcat(szDoxyTag, szDocFilePath); // the path only at this point
1530  strcat(szDoxyTag, p2 + 5); // the 'href' text
1531  break;
1532  }
1533  }
1534 
1535  WBFree(p3);
1536  if(szDoxyTag[0])
1537  {
1538  break;
1539  }
1540  }
1541  }
1542  }
1543  }
1544  }
1545 
1546 #if 0 /* this is the OLD way - new method uses '<a class="el" href="">' tags */
1547  if(strstr(szLineBuf, "doxytag"))
1548  {
1549  p2 = strstr(szLineBuf, szTerm);
1550 
1551  if(p2 && p2 > szLineBuf + 10 && *(p2 - 1) == ':' && *(p2 - 2) == ':' &&
1552  (p2[strlen(szTerm)] == '"' || p2[strlen(szTerm)] == '\'')) // must be followed by a quote mark
1553  {
1554  // grab the entire XML tag
1555  while(p2 > szLineBuf && (*p2 != '<' || p2[1] != '!' || p2[2] != '-' || p2[3] != '-'))
1556  {
1557  p2--;
1558  }
1559 
1560  if(*p2 == '<' && p2[1] == '!' && p2[2] == '-' && p2[3] == '-') // doxytag comment block
1561  {
1562  p4 = CHFindEndOfXMLTag(p2 + 4, -1); // point past the '<!--' first, then find the end
1563 
1564  if(*p4 == '>')
1565  {
1566  p3 = CHParseXMLTagContents(p2 + 4, p4 - (p2 + 4));
1567  if(p3)
1568  {
1569  // now that I have the tag, grab the 'ref' member
1570  for(p2=p3; *p2; p2 += strlen(p2) + 1)
1571  {
1572  if(!strncmp(p2, "ref=", 4))
1573  {
1574  strcpy(szDoxyTag, "file://");
1575  strcat(szDoxyTag, szDocFilePath);
1576  strcat(szDoxyTag, "#");
1577  strcat(szDoxyTag, p2 + 4);
1578  break;
1579  }
1580  }
1581 
1582  WBFree(p3);
1583  if(szDoxyTag[0])
1584  {
1585  break;
1586  }
1587 // TODO: with this much nesting, consider writing utility functions
1588 // to do some of it and make the code more readable
1589  }
1590  }
1591  }
1592  }
1593  }
1594 #endif // 0
1595  }
1596 
1597  fclose(pTemp);
1598  pTemp = NULL; // by convention
1599  }
1600  }
1601  }
1602  }
1603 
1604  if(szDoxyTag[0]) // found the right file
1605  {
1606 // WB_ERROR_PRINT("TEMPORARY: %s spawn %s\n with \"%s\"\n\n",
1607 // __FUNCTION__, szHelpBrowser, szDoxyTag);
1608  hProcess = WBRunAsync(szHelpBrowser, szDoxyTag, NULL);
1609 
1610  // TODO: does '--new-instance' work properly?
1611 
1612  if(hProcess != WB_INVALID_FILE_HANDLE)
1613  {
1614  // TODO: display 'wait' cursor and wait for app window to appear ?
1615 
1616  return;
1617  }
1618 
1619  goto fail_to_run_help;
1620  }
1621 
1622  // TODO: other documentation
1623 
1624  // search for related language runtime library MAN pages
1625  // command line: man -S 2:3 keyword - pipe output via man2html then open temp html and delete it
1626 
1627  // TODO: write 'man to HTML' mini-web-server?
1628  // TODO: use embedded web client if webkit is available?
1629 
1630  p2 = WBRunResult("man", "-S", "2:3", szTerm, NULL);
1631  if(!p2 || !*p2)
1632  {
1633  if(p2)
1634  {
1635  WBFree(p2);
1636  p2 = NULL;
1637  }
1638 
1639 fail_to_run_man2html:
1640 // WB_ERROR_PRINT("TEMPORARY: fail to run man2html \"%s\"\n", p1);
1641 
1642  goto fail_to_run_help;
1643  }
1644 
1645 // WB_ERROR_PRINT("TEMPORARY: man output \"%s\"\n", p2);
1646 
1647  // instead of calling an external program to convert, I wrote something
1648  // that does the conversion as reliably as possible. external scripts
1649  // sometimes 'pooch up' the HTML version of the man page, inject garbage,
1650  // lose large chunks of text, or have issues with hyphenated words.
1651  // it's possible MY code may exhibit similar problems, but I think I've
1652  // nailed the issues without too much trouble. And, if I ever set up a
1653  // web server for the help system, I'll fix linkage as well. That would be
1654  // EVEN MORE interesting, actually... (and it's less troublesome than trying
1655  // to get groff to work with man consistently across multiple platforms)
1656 
1657  p3 = InternalMan2Html(szTerm, p2);
1658 
1659  WBFree(p2);
1660  p2 = NULL;
1661 
1662  if(!p3)
1663  {
1664  WB_ERROR_PRINT("Failed to run InternalMan2Html in %s\n", __FUNCTION__);
1665  goto fail_to_run_man2html;
1666  }
1667 
1668  p2 = WBTempFile(".html"); // temporary HTML file
1669 
1670  // write 'p3' to a temp html file now
1671  if(!p2) // new temp file name
1672  {
1673  WBFree(p3);
1674  p3 = NULL;
1675 
1676 // WB_ERROR_PRINT("TEMPORARY: here I am (2)\n");
1677  goto fail_to_run_man2html;
1678  }
1679 
1680  if(FBWriteFileFromBuffer(p2, p3, strlen(p3)) < 0)
1681  {
1682 // WB_ERROR_PRINT("TEMPORARY: here I am (3) p2=\"%s\"\n", p2);
1683 
1684  WBFree(p2);
1685  WBFree(p3);
1686  p2 = p3 = NULL;
1687 
1688  goto fail_to_run_man2html;
1689  }
1690 
1691  // p2 contains the ".html" temp file name
1692 
1693  hProcess = WBRunAsync(szHelpBrowser, p2, NULL);
1694 
1695  // TODO: does '--new-instance' work properly?
1696 
1697 
1698  WBFree(p2);
1699  WBFree(p3);
1700  p1 = p2 = p3 = NULL;
1701 
1702  if(hProcess != WB_INVALID_FILE_HANDLE)
1703  {
1704  // TODO: display 'wait' cursor and wait for app window to appear ?
1705 
1706  return;
1707  }
1708 
1709 
1710 
1711 fail_to_run_help:
1712 
1713  p1 = WBCopyString("No help available for \"");
1714  WBCatString(&p1, szTerm);
1715  WBCatString(&p1, "\"");
1716 
1717  if(p1)
1718  {
1719  Window wIDOwner = pMainFrame ? pMainFrame->wID : -1;
1720 
1722  "Context Help", p1);
1723 
1724  WBFree(p1);
1725  }
1726 }
1727 
1728 static char * CreateNBSPString(const char *szRef, int nLen)
1729 {
1730 static const char szNBSP[]="&nbsp";
1731 int i1, i2;
1732 char *pRval;
1733 
1734 #define NBSP_TAB_WIDTH 8
1735 
1736  if(nLen < 0)
1737  {
1738  return NULL;
1739  }
1740 
1741  pRval = WBAlloc(nLen * 8 * (sizeof(szNBSP) - 1) + 2);
1742 
1743  if(pRval)
1744  {
1745  char *p2 = pRval;
1746  const char *p1 = szRef;
1747 
1748  for(i1=0, i2=0; i1 < nLen; p1++, i1++, i2 = (i2 + 1) % NBSP_TAB_WIDTH)
1749  {
1750  if(*p1 == '\t')
1751  {
1752  for(; i2 < NBSP_TAB_WIDTH; i2++)
1753  {
1754  memcpy(p2, szNBSP, sizeof(szNBSP) - 1);
1755  p2 += sizeof(szNBSP) - 1;
1756  }
1757  i2 = NBSP_TAB_WIDTH - 1; // tab position
1758  }
1759  else
1760  {
1761  memcpy(p2, szNBSP, sizeof(szNBSP) - 1);
1762  p2 += sizeof(szNBSP) - 1;
1763  }
1764  }
1765 
1766  *p2 = 0;
1767  }
1768 
1769  return pRval;
1770 }
1771 
1772 
1773 
1775 // __ __ _ _ _ _ _ _ _____ __ __ _ //
1776 // | \/ | / \ | \ | | | |_ ___ | | | |_ _| \/ | | //
1777 // | |\/| | / _ \ | \| | | __/ _ \ | |_| | | | | |\/| | | //
1778 // | | | |/ ___ \| |\ | | || (_) | | _ | | | | | | | |___ //
1779 // |_| |_/_/ \_\_| \_| \__\___/ |_| |_| |_| |_| |_|_____| //
1780 // //
1782 
1783 static char * InternalMan2Html(const char *szTerm, const char *szText)
1784 {
1785 char *pRval, *p1, *p2, *pTemp, *pHyphenBuf = NULL;
1786 const char *p3, *p4, *p5;
1787 int i1, i2, cbLineLen, nTabTwist, bNoHyphen = 0;
1788 static const char szNBSP[]="&nbsp;";
1789 
1790 
1791  pRval = WBCopyString("<HTML><HEAD><TITLE>X11workbench Help - man ");
1792  if(pRval)
1793  {
1794  WBCatString(&pRval, szTerm);
1795 
1796  if(pRval)
1797  {
1798  WBCatString(&pRval, "</TITLE></HEAD><BODY><TT>\r\n");
1799  }
1800  }
1801 
1802  if(!pRval)
1803  {
1804  return NULL;
1805  }
1806 
1807  p3 = szText;
1808 
1809  while(*p3 && pRval)
1810  {
1811  // skip white space
1812  p4 = p3;
1813  nTabTwist = 0;
1814 
1815  while(*p3 && *p3 <= ' ')
1816  {
1817  if(*p3 == '\r' || *p3 == '\n' || *p3 == '\f')
1818  {
1819  // TODO: handle tabs?
1820  pTemp = CreateNBSPString(p4, p3 - p4);
1821  if(pTemp)
1822  {
1823  WBCatString(&pRval, pTemp);
1824  WBFree(pTemp);
1825  pTemp = NULL;
1826  }
1827 
1828  if(pHyphenBuf)
1829  {
1830  // rare possibility of hyphenated text on a blank line
1831  // but I want to indent it past the end of the white space
1832  if(pRval)
1833  {
1834  WBCatString(&pRval, pHyphenBuf);
1835  }
1836 
1837  WBFree(pHyphenBuf);
1838  pHyphenBuf = NULL;
1839  }
1840 
1841  p4 = p3; // to indicate I shouldn't copy anything
1842  break; // it will fall through to the next part
1843  }
1844 
1845  if(*p3 == '\t')
1846  {
1847  nTabTwist = 0; // reset after a tab
1848  }
1849  else
1850  {
1851  nTabTwist++;
1852  }
1853 
1854  p3++;
1855  }
1856 
1857  nTabTwist = nTabTwist % NBSP_TAB_WIDTH; // 'tab twist' for converting tabs
1858 
1859  if(pRval && p3 > p4)
1860  {
1861  pTemp = CreateNBSPString(p4, p3 - p4);
1862  if(pTemp)
1863  {
1864  WBCatString(&pRval, pTemp);
1865  WBFree(pTemp);
1866  pTemp = NULL;
1867  }
1868 
1869  p4 = p3;
1870  }
1871 
1872  if(!pRval)
1873  {
1874  break;
1875  }
1876 
1877  // at this point 'p3' and 'p4' point to the first non-white-space character
1878  // and all of the white space (any any 'hyphen buf' stuff) is copied
1879 
1880  while(*p3 && *p3 != '\r' && *p3 != '\n' && *p3 != '\f')
1881  {
1882  p3++; // go to the end of the line
1883  }
1884 
1885  cbLineLen = p3 - p4; // not including CR, LF, or FF
1886 
1887  if(*p3 == '\r' && p3[1] == '\n') // for now just check for <CRLF>
1888  {
1889  p3++;
1890  }
1891 
1892  p3++;
1893 
1894  if(!cbLineLen || pHyphenBuf) // blank line
1895  {
1896  // if I have de-hyphenated text, I need to INSERT it into the buffer. There will be no tabs
1897  // in it and for NOW I'll assume there's no bolding nor underlining [later I might have to
1898  // check for this independently and I don't want to complicate this any more]
1899 
1900  if(pHyphenBuf)
1901  {
1902  nTabTwist = (nTabTwist + strlen(pHyphenBuf)) % NBSP_TAB_WIDTH;
1903 
1904  WBCatString(&pRval, pHyphenBuf);
1905 
1906  WBFree(pHyphenBuf);
1907  pHyphenBuf = NULL;
1908  }
1909 
1910  if(!cbLineLen)
1911  {
1912  if(pRval)
1913  {
1914  WBCatString(&pRval, "<br>\r\n");
1915  }
1916 
1917  continue;
1918  }
1919  }
1920 
1921  // I need to look for backspaces and handle them as bold-text or underscores (depending)
1922 
1923 #define STRSEGCMP(X,Y) strncmp(X,Y,strlen(Y))
1924 
1925  if(!bNoHyphen &&
1926  (!STRSEGCMP(p4, "SEE ALSO") ||
1927  !STRSEGCMP(p4, "S\x08""SE\x08""EE\x08""E A\x08""AL\x08""LS\x08""SO\x08""O") ||
1928  !STRSEGCMP(p4, "S\x08""SE\x08""EE\x08""E \x08 A\x08""AL\x08""LS\x08""SO\x08""O")))
1929  {
1930  bNoHyphen = 1;
1931  }
1932 
1933  p5 = p4; // p4 is the beginning of my line, of 'cbLineLen' characters
1934 
1935  while(p5 < p3 && *p5 != '\x08' && *p5 != '<' && *p5 != '>') // backspace in the line? 'GT'? 'LT'?
1936  {
1937  p5++;
1938  }
1939 
1940  if(p5 >= p3 || !cbLineLen) // no backspaces or zero-length line
1941  {
1942  if(cbLineLen)
1943  {
1944  // if there are tabs in the line I have to deal with this differently
1945  p5 = p4;
1946  i1 = 0;
1947 
1948  while(p5 < p3) // tabs?
1949  {
1950  if(*p5 == '\t')
1951  {
1952  i1++; // count them
1953  }
1954 
1955  p5++;
1956  }
1957  if(!i1) // no tabs
1958  {
1959  WBCatStringN(&pRval, p4, cbLineLen);
1960  }
1961  else
1962  {
1963  // each tab converts to a variable number of spaces up to NBSP_TAB_WIDTH
1964  // this is a little silly but it's probably the easiest way to make it work
1965 
1966  pTemp = WBAlloc(i1 * NBSP_TAB_WIDTH * (sizeof(szNBSP) - 1) + cbLineLen + 4);
1967  if(!pTemp)
1968  {
1969  WBFree(pRval);
1970  pRval = NULL;
1971 
1972  break; // buh-bye (error)
1973  }
1974 
1975  for(p1=pTemp, p5 = p4, i1 = 0, i2=nTabTwist; i1 < cbLineLen;
1976  i1++, p5++, i2 = (i2 + 1) % NBSP_TAB_WIDTH)
1977  {
1978  if(*p5 == '\t')
1979  {
1980  for(; i2 < NBSP_TAB_WIDTH; i2++)
1981  {
1982  memcpy(p1, szNBSP, sizeof(szNBSP) - 1);
1983  p1 += sizeof(szNBSP) - 1;
1984  }
1985 
1986  i2 = NBSP_TAB_WIDTH - 1; // so when it increments it will be zero
1987  }
1988  else
1989  {
1990  *(p1++) = *p5;
1991  }
1992  }
1993 
1994  *p1 = 0;
1995 
1996  WBCatString(&pRval, pTemp);
1997 
1998  WBFree(pTemp);
1999  pTemp = NULL;
2000  }
2001 
2002  // check for hyphenated text
2003 
2004  if(pRval)
2005  {
2006  p2 = pRval + strlen(pRval);
2007 
2008  if(bNoHyphen)
2009  {
2010  if(*(p2 - 1) == '-')
2011  {
2012  p2--;
2013  *p2 = 0; // trim the dash
2014 
2015  while(p2 > pRval && *(p2 - 1) > ' ' && *(p2 - 1) != '.'
2016  && *(p2 - 1) != ',' && *(p2 - 1) != ')' && *(p2 - 1) != ']' && *(p2 - 1) != '}')
2017  {
2018  p2--; // decrement until at the beginning of a word
2019  }
2020 
2021  pHyphenBuf = WBCopyString(p2);
2022  *p2 = 0; // trim hyphenated partial word off of the string
2023 
2024 // WB_ERROR_PRINT("TEMPORARY: (d) pHyphenBuf=\"%s\"\n", pHyphenBuf);
2025  }
2026  else if(p2 > pRval + 3 && *(p2 - 3) == '\xe2' && *(p2 - 2) == '\x80' && *(p2 - 1) == '\x90')
2027  {
2028  // the sequence <E2><80><90> is found on debian's man program, and since they
2029  // define the gnu standard, it's likely to be elsewhere also
2030 
2031  p2 -= 3; // 3 character sequence (remove it)
2032  *p2 = 0; // trim the 3 character sequence
2033 
2034  while(p2 > pRval && *(p2 - 1) > ' ' && *(p2 - 1) != '.'
2035  && *(p2 - 1) != ',' && *(p2 - 1) != ')' && *(p2 - 1) != ']' && *(p2 - 1) != '}')
2036  {
2037  p2--; // decrement until at the beginning of a word
2038  }
2039 
2040  pHyphenBuf = WBCopyString(p2);
2041  *p2 = 0; // trim hyphenated partial word off of the string
2042 
2043 // WB_ERROR_PRINT("TEMPORARY: (e) pHyphenBuf=\"%s\"\n", pHyphenBuf);
2044  }
2045  }
2046  else if(p2 > pRval + 3 && *(p2 - 3) == '\xe2' && *(p2 - 2) == '\x80' && *(p2 - 1) == '\x90')
2047  {
2048  // the sequence <E2><80><90> is found on debian's man program, and since they
2049  // define the gnu standard, it's likely to be elsewhere also
2050 
2051  p2 -= 2; // 3 character sequence (remove it, replace with '-')
2052  *(p2 - 1) = '-';
2053  *p2 = 0; // trim the 3 character sequence
2054 
2055 // WB_ERROR_PRINT("TEMPORARY: (f) pHyphenBuf=\"%s\"\n", pHyphenBuf);
2056  }
2057  }
2058  }
2059 
2060  if(pRval)
2061  {
2062  WBCatString(&pRval, "<br>\r\n");
2063  }
2064  }
2065  else
2066  {
2067  p5 = p4;
2068  i1 = 0;
2069 
2070  while(p5 < p3) // tabs?
2071  {
2072  if(*p5 == '\t')
2073  {
2074  i1++; // count them
2075  }
2076 
2077  p5++;
2078  }
2079 
2080  pTemp = WBAlloc(i1 * NBSP_TAB_WIDTH * (sizeof(szNBSP) - 1) + cbLineLen * 12 + 4); // way more than enough space (12 times the length)
2081 
2082  if(!pTemp)
2083  {
2084  WBFree(pRval);
2085  pRval = NULL;
2086  break;
2087  }
2088 
2089  memcpy(pTemp, p4, cbLineLen);
2090  pTemp[cbLineLen] = 0;
2091 
2092  p2 = pTemp;
2093  i2 = nTabTwist;
2094 
2095  while(*p2)
2096  {
2097  if(*p2 == '\x8') // backspace at start of line?
2098  {
2099  strcpy(p2, p2 + 1); // just skip it
2100  continue;
2101  }
2102 
2103  if(p2[1] == '\x8') // next char is a backspace (this precludes line starting with a backspace
2104  {
2105  // there are 2 options possible - one is A<BS>A and the other is _<BS>A or A<BS>_ [either fine]
2106 
2107  if(*p2 == p2[2]) // BOLD
2108  {
2109  // Insert a <B> here and overwrite the first 2 characters
2110 
2111  memmove(p2 + 1, p2, strlen(p2) + 1); // 2 chars overwritten, move over 1
2112  memcpy(p2, "<B>", 3);
2113 
2114  p2 += 3; // point to the original character
2115 
2116  // if the first character in the sequence is a '<' or '>' I have to deal with it FIRST
2117  if(*p2 == '<' || *p2 == '>')
2118  {
2119  char cTemp = *p2;
2120 
2121  memmove(p2 + 3, p2, strlen(p2) + 1); // need room for '&gt;' or '&lt;' overwriting the original
2122 
2123  if(cTemp == '<')
2124  {
2125  memcpy(p2, "&lt;", 4);
2126  }
2127  else // if(cTemp == '>')
2128  {
2129  memcpy(p2, "&gt;", 4);
2130  }
2131 
2132  p2 += 4; // increment past the tag
2133  }
2134  else
2135  {
2136  p2++; // increment past the character
2137  }
2138 
2139  while(*p2 && p2[1] == '\x8' && *p2 == p2[2])
2140  {
2141  if(*p2 == '<' || *p2 == '>')
2142  {
2143  char cTemp = *p2;
2144 
2145  memmove(p2 + 1, p2, strlen(p2) + 1); // need room for '&gt;' or '&lt;' and minus 3 chars
2146 
2147  if(cTemp == '<')
2148  {
2149  memcpy(p2, "&lt;", 4);
2150  }
2151  else // if(cTemp == '>')
2152  {
2153  memcpy(p2, "&gt;", 4);
2154  }
2155 
2156  p2 += 4; // increment past the tag
2157  }
2158  else
2159  {
2160  memcpy(p2, p2 + 2, strlen(p2 + 2) + 1); // skip the A<BS> and just have A
2161  p2++;
2162  }
2163  }
2164 
2165  // now that I'm at the end of the sequence, insert </B>
2166 
2167  memmove(p2 + 4, p2, strlen(p2) + 1);
2168  memcpy(p2, "</B>", 4);
2169 
2170  p2 += 4;
2171  continue;
2172  }
2173  else if(*p2 == '_' || p2[2] == '_') // UNDERSCORE
2174  {
2175  // Insert a <U> here
2176 
2177  memmove(p2 + 1, p2, strlen(p2) + 1); // 2 chars overwritten, move over 1
2178  if(*p2 != '_')
2179  {
2180  p2[2] = *p2; // move the character to underline "up there"
2181  }
2182  memcpy(p2, "<U>", 3);
2183 
2184  p2 += 3; // point to the original 'underscored' character
2185 
2186  // if the first character in the sequence is a '<' or '>' I have to deal with it FIRST
2187  if(*p2 == '<' || *p2 == '>')
2188  {
2189  char cTemp = *p2;
2190 
2191  memmove(p2 + 3, p2, strlen(p2) + 1); // need room for '&gt;' or '&lt;' overwriting the original
2192 
2193  if(cTemp == '<')
2194  {
2195  memcpy(p2, "&lt;", 4);
2196  }
2197  else // if(cTemp == '>')
2198  {
2199  memcpy(p2, "&gt;", 4);
2200  }
2201 
2202  p2 += 4; // increment past the tag
2203  }
2204  else
2205  {
2206  p2++; // increment past the character
2207  }
2208 
2209 
2210  while(*p2 && p2[1] == '\x8' && (*p2 == '_' || p2[2] == '_'))
2211  {
2212  if(*p2 == '_')
2213  {
2214  *p2 = p2[2]; // move it "back down"
2215  }
2216 
2217  if(*p2 == '<' || *p2 == '>')
2218  {
2219  char cTemp = *p2;
2220 
2221  memmove(p2 + 1, p2, strlen(p2) + 1); // need room for '&gt;' or '&lt;' and minus 3 chars
2222 
2223  if(cTemp == '<')
2224  {
2225  memcpy(p2, "&lt;", 4);
2226  }
2227  else // if(cTemp == '>')
2228  {
2229  memcpy(p2, "&gt;", 4);
2230  }
2231 
2232  p2 += 4; // increment past the tag
2233  }
2234  else
2235  {
2236  memcpy(p2, p2 + 2, strlen(p2 + 2) + 1); // skip the A<BS> and just have A
2237  p2++;
2238  }
2239  }
2240 
2241  // now that I'm at the end of the sequence, insert </U>
2242 
2243  memmove(p2 + 4, p2, strlen(p2) + 1);
2244  memcpy(p2, "</U>", 4);
2245 
2246  p2 += 4;
2247  continue;
2248  }
2249  }
2250  else if(*p2 == '<' || *p2 == '>')
2251  {
2252  memmove(p2 + 3, p2, strlen(p2) + 1); // need room for '&gt;' or '&lt;'
2253 
2254  if(*p2 == '<')
2255  {
2256  memcpy(p2, "&lt;", 4);
2257  }
2258  else // if(*p2 == '>')
2259  {
2260  memcpy(p2, "&gt;", 4);
2261  }
2262 
2263  p2 += 3; // point at the ';' and loop
2264  }
2265 
2266  // once THAT has been done, check for a tab character and insert '&nbsp;'s
2267  if(*p2 == '\t')
2268  {
2269  p1 = p2; // current position - has a tab in it
2270  p2 += (sizeof(szNBSP) - 1) * (NBSP_TAB_WIDTH - i2); // next character to process, skips nbsp's
2271 
2272  if(p1[1])
2273  {
2274  memmove(p2, p1 + 1, strlen(p1 + 1) + 1); // don't copy the tab, but DO copy everything else (p1 + 1)
2275  }
2276  else
2277  {
2278  *p2 = 0; // end the string (I'm at the end)
2279  }
2280 
2281  for(; i2 < NBSP_TAB_WIDTH; i2++)
2282  {
2283  memcpy(p1, szNBSP, sizeof(szNBSP) - 1);
2284  p1 += sizeof(szNBSP) - 1;
2285  }
2286 
2287  i2 = 0; // no tab twist at the moment
2288  }
2289  else
2290  {
2291  p2++; // next character
2292  i2 = (i2 + 1) % NBSP_TAB_WIDTH; // "tab twist"
2293  }
2294  }
2295 
2296  // pTemp now contains the entire 'cooked' line. If I must trim off a trailing hyphen
2297  // put the text into 'pHyphenBuf'. Then create a line from the rest
2298 
2299  p2 = pTemp + strlen(pTemp);
2300 
2301  if(bNoHyphen)
2302  {
2303  if(*pTemp && *(p2 - 1) == '-') // hyphenated
2304  {
2305  p2--;
2306  *p2 = 0; // trim the dash
2307 
2308  while(p2 > pTemp && *(p2 - 1) > ' ' && *(p2 - 1) != '.'
2309  && *(p2 - 1) != ',' && *(p2 - 1) != ')' && *(p2 - 1) != ']' && *(p2 - 1) != '}')
2310  {
2311  p2--; // decrement until at the beginning of a word
2312  }
2313 
2314  pHyphenBuf = WBCopyString(p2);
2315  *p2 = 0; // trim hyphenated partial word off of the string
2316 
2317 // WB_ERROR_PRINT("TEMPORARY: (b) pHyphenBuf=\"%s\"\n", pHyphenBuf);
2318  }
2319  else if(p2 > pTemp + 3 && *(p2 - 3) == '\xe2' && *(p2 - 2) == '\x80' && *(p2 - 1) == '\x90')
2320  {
2321  // the sequence <E2><80><90> is found on debian's man program, and since they
2322  // define the gnu standard, it's likely to be elsewhere also
2323 
2324  p2 -= 3; // 3 character sequence (remove it)
2325  *p2 = 0; // trim the 3 character sequence
2326 
2327  while(p2 > pTemp && *(p2 - 1) > ' ' && *(p2 - 1) != '.'
2328  && *(p2 - 1) != ',' && *(p2 - 1) != ')' && *(p2 - 1) != ']' && *(p2 - 1) != '}')
2329  {
2330  p2--; // decrement until at the beginning of a word
2331  }
2332 
2333  pHyphenBuf = WBCopyString(p2);
2334  *p2 = 0; // trim hyphenated partial word off of the string
2335 
2336 // WB_ERROR_PRINT("TEMPORARY: (c) pHyphenBuf=\"%s\"\n", pHyphenBuf);
2337  }
2338  }
2339  else if(p2 > pTemp + 3 && *(p2 - 3) == '\xe2' && *(p2 - 2) == '\x80' && *(p2 - 1) == '\x90')
2340  {
2341  // the sequence <E2><80><90> is found on debian's man program, and since they
2342  // define the gnu standard, it's likely to be elsewhere also
2343 
2344  p2 -= 2; // 3 character sequence (remove it, replace with '-')
2345  *(p2 - 1) = '-';
2346  *p2 = 0; // trim the 3 character sequence
2347 
2348 // WB_ERROR_PRINT("TEMPORARY: (f) pHyphenBuf=\"%s\"\n", pHyphenBuf);
2349  }
2350 
2351  strcat(pTemp, "<br>\r\n");
2352 
2353  if(pRval)
2354  {
2355  WBCatString(&pRval, pTemp);
2356  }
2357 
2358  WBFree(pTemp);
2359  pTemp = NULL; // by convention
2360  }
2361  }
2362 
2363  if(pRval)
2364  {
2365  WBCatString(&pRval, "<br>\r\n<hr size=2>\r\n<p align=\"center\">Generated internally by X11workbench</p>\r\n"
2366  "</TT></BODY></HTML>\r\n");
2367  }
2368 
2369  return pRval;
2370 }
2371 
Icon - multiple question marks in red triangle.
char * CHGetMimeDefaultApp(const char *szMimeType)
Get the default application for a particular MIME type.
Definition: conf_help.c:3056
GC WBBeginPaint(Window wID, XExposeEvent *pEvent, WB_GEOM *pgBounds)
&#39;Paint&#39; helper, creates a GC for use in updating the window in an Expose event handler ...
structure for managing menu items
Definition: menu.h:185
#define FW_EDIT_SELECT_ALL_ACCEL
Definition: child_frame.h:129
int WBEditWindowLoadFile(WBEditWindow *pEditWindow, const char *pszFileName)
Open an existing file and read its contents into the Edit Window, storing the file name for later ref...
Definition: edit_window.c:436
&#39;window helper&#39; main header file for the X11workbench Toolkit API
#define FW_FILE_SAVE_ACCEL
Definition: child_frame.h:122
void FWRemoveContainedWindow(WBFrameWindow *pFW, WBChildFrame *pCont)
Removes a &#39;contained&#39; window from a frame window. Does not destroy the &#39;contained&#39; window...
int bQuitFlag
&#39;Quit&#39; Flag - you should check this periodically in your main (message) loop and exit whenever it is ...
#define WB_DEBUG_PRINT(L,...)
Preferred method of implementing conditional debug output.
Definition: debug_helper.h:196
static __inline__ Colormap WBDefaultColormap(Display *pDisplay)
returns the default colormap for the default screen of the specified display
set this flag for application top-level window and whenever it is destroyed the application will exit...
Definition: frame_window.h:288
Utilities for copying and drawing text, determining text extents, and so on.
#define FWGetFocusWindow(pFW)
A macro to aid code readability, returning the Window ID of the contained window that has the focus...
Definition: frame_window.h:926
void WBCatStringN(char **ppDest, const char *pSrc, unsigned int nMaxChars)
A simple utility that concatenates a string onto the end of a 0-byte terminated WBAlloc() string up t...
Window wID
Window id for the frame window.
Definition: frame_window.h:267
void WBExit()
deletes any remaining global objects, frees the Display pointer, and terminates event processing ...
char * WBRunResult(const char *szAppName,...)
Run an application synchronously, returning &#39;stdout&#39; output in a character buffer.
#define FW_FILE_NEW_MENU
Definition: child_frame.h:105
void WBWaitForEvent(Display *pDisplay)
Wait for an event, blocking indefinitely.
Display * WBInit(const char *szDisplayName)
initializes default objects for the specified Display (required)
int WBCheckGetEvent(Display *pDisplay, XEvent *pEvent)
Main message loop, high level API to check for and retrieve the next event.
void PXM_RegisterAppIcons(char *ppRegAppLarge[], char *ppRegAppSmall[])
Icon Registration for application &#39;large&#39; and &#39;small&#39; icons.
void * WBAllocDirectoryList(const char *szDirSpec)
Allocate a &#39;Directory List&#39; object for a specified directory spec.
Definition: file_help.c:1191
int FWGetChildFrameIndex(WBFrameWindow *pFW, WBChildFrame *pCont)
Sets the focus to a specific contained window using its tab order index.
#define FW_EDIT_DELETE_MENU
Definition: child_frame.h:115
Edit Window API functions.
const char * CHFindEndOfXMLTag(const char *pTagContents, int cbLength)
Parses contents of an XML tag to find the end of it.
Definition: conf_help.c:3023
Atom aQUERY_CLOSE
query if it&#39;s ok to close (and optionally destroy yourself if ok) a window
#define FW_FILE_SAVE_ALL_MENU
Definition: child_frame.h:110
#define FW_EDIT_SELECT_NONE_MENU
Definition: child_frame.h:117
Structure that defines a Child Frame within a Frame Window.
Definition: frame_window.h:409
structure for managing menu items
Definition: menu.h:127
#define FW_FILE_SAVE_MENU
Definition: child_frame.h:108
Button Bits - No button.
void FWSetMenuHandlers(WBFrameWindow *pFW, const WBFWMenuHandler *pHandlerArray)
Function to assign the default menu handler to a frame window.
Definition: frame_window.c:761
void * CHOpenConfFile(const char *szAppName, int iFlags)
open configuration file for read/write, optionally creating it, based on application name ...
Definition: conf_help.c:637
Button Bits - OK button.
int CHGetConfFileString(void *hFile, const char *szSection, const char *szIdentifier, char *szData, int cbData)
obtain a string from a configuration file
Definition: conf_help.c:1223
&#39;configuration helper&#39; main header file for the X11 Work Bench Toolkit API
set this to make window immediately visible
Definition: frame_window.h:289
#define FW_EDIT_CUT_ACCEL
Definition: child_frame.h:126
Icon - red stop sign.
char * WBTempFile(const char *szExt)
Get the name for a new, unique temporary file, creating the file in the process, and save its name fo...
Icon - white &#39;middle finger&#39; on red triangle.
#define FW_EDIT_PASTE_ACCEL
Definition: child_frame.h:128
void WBClearWindow(Window wID, GC gc)
&#39;Paint&#39; helper, erases background by painting the background color within the clipping region ...
void CHCloseConfFile(void *pFile)
close configuration file opened by CHOpenConfFile(), but does NOT free memory resources ...
Definition: conf_help.c:831
void WBToolkitUsage(void)
Displays &#39;usage&#39; for toolkit options to stderr.
unsigned long WBGetWindowFGColor(Window wID)
Returns the currently assigned foreground color.
void * WBAlloc(int nSize)
High performance memory sub-allocator &#39;allocate&#39;.
void FWSetFocusWindowIndex(WBFrameWindow *pFW, int iIndex)
Sets the focus to a specific contained window using its tab order index.
void CHDumpConfig()
dump config information using debug output functions
Definition: conf_help.c:3192
WB_PROCESS_ID WBRunAsync(const char *szAppName,...)
Run an application asynchronously.
#define FW_EDIT_CUT_MENU
Definition: child_frame.h:112
#define FW_MENU_HANDLER_BEGIN(X)
Use this macro to begin definition of a WBFWMenuHandler array.
Definition: frame_window.h:877
#define WB_ERROR_PRINT(...)
Preferred method of implementing an &#39;error level&#39; debug message for all subsystems.
Definition: debug_helper.h:268
void WBFree(void *pBuf)
High performance memory sub-allocator &#39;free&#39;.
void WBDestroyWindow(Window wID)
Destroy a window.
int DLGMessageBox(int iType, Window wIDOwner, const char *szTitle, const char *szMessage)
Display a modal &#39;message box&#39; dialog window with a specific title, message, and button combination...
Definition: dialog_impl.c:231
#define FW_MENU_HANDLER_END
Use this macro to complete the definition of a WBFWMenuHandler array.
Definition: frame_window.h:891
int WBParseStandardArguments(int *pargc, char ***pargv, char ***penvp)
parses standard C arguments as passed to &#39;main()&#39;
#define FW_FILE_SAVE_ALL_ACCEL
Definition: child_frame.h:124
void FWDestroyChildFrame(WBChildFrame *pChildFrame)
Destroy an Child Frame.
Definition: child_frame.c:312
int WBNextDirectoryEntry(void *pDirectoryList, char *szNameReturn, int cbNameReturn, unsigned long *pdwModeAttrReturn)
Obtain information about the next entry in a &#39;Directory List&#39;.
Definition: file_help.c:1394
char * DLGFileDialog(int iType, Window wIDOwner, const char *szDefPath, const char *szDefName, const char *szExtAndDescList)
Display a modal File Dialog window, returning a WBAlloc&#39;d pointer to a null-byte terminated string co...
Definition: dialog_impl.c:962
#define FW_FILE_SAVE_AS_MENU
Definition: child_frame.h:109
void WBDispatch(XEvent *pEvent)
Generic Event Dispatcher, using message type to dispatch.
void WBInitSizeHints(XSizeHints *pSH, Display *pDisplay, int iMinHeight, int iMinWidth)
initializes the XSizeHints structure with standard attributes
Atom aMENU_COMMAND
commands sent by menus via ClientMessage
XFontStruct * WBGetDefaultFont(void)
Returns a pointer to the default font&#39;s XFontStruct.
WBChildFrame * FWGetContainedWindowByIndex(const WBFrameWindow *pFW, int iIndex)
Returns the Window ID for the specified &#39;contained&#39; window. The index follows the tab order...
static __inline__ int FBWriteFileFromBuffer(const char *szFileName, const char *pBuf, unsigned int cbBuf)
Write a file from a regular buffer using a file name.
Definition: file_help.h:371
#define FW_FILE_SAVE_AS_ACCEL
Definition: child_frame.h:123
WBEditWindow * WBCreateEditWindow(WBFrameWindow *pOwner, XFontStruct *pFont, const char *szFocusMenu, const WBFWMenuHandler *pHandlerArray, int fFlags)
Create an Edit Window.
Definition: edit_window.c:250
char * WBGetCanonicalPath(const char *szFileName)
Return the canonical path for a file name (similar to POSIX &#39;realpath()&#39; funtion) ...
Definition: file_help.c:807
Type Flag - File Open Dialog.
void DLGSplashScreen(char *aXPM[], const char *szCopyright, unsigned long clrText)
Display a splash screen for 5 seconds (with animation and copyright string), and then return...
Definition: dialog_impl.c:1063
#define FW_EDIT_SELECT_ALL_MENU
Definition: child_frame.h:116
#define FW_EDIT_SELECT_NONE_ACCEL
Definition: child_frame.h:130
main controlling structure for frame windows
Definition: frame_window.h:264
#define FW_FILE_CLOSE_MENU
Definition: child_frame.h:106
Icon - application icon (from icon_app.xpm)
#define FW_FILE_OPEN_ACCEL
Definition: child_frame.h:121
Icon - exclamation point on yellow triangle.
#define FW_EDIT_COPY_ACCEL
Definition: child_frame.h:127
char * WBCopyString(const char *pSrc)
A simple utility that returns a WBAlloc() copy of a 0-byte terminated string.
#define CH_FLAGS_DEFAULT
Definition: conf_help.h:113
Structure that defines an Edit Window.
Definition: edit_window.h:156
WBFrameWindow * FWCreateFrameWindow(const char *szTitle, int idIcon, const char *szMenuResource, int iX, int iY, int iWidth, int iHeight, WBWinEvent pUserCallback, int iFlags)
Create a frame window.
Definition: frame_window.c:444
set this to enable a &#39;status bar&#39; at the bottom
Definition: frame_window.h:291
char * DLGInputBox(Window wIDOwner, const char *szTitle, const char *szPrompt, const char *szDefault, int iWidth, int iMaxChar)
Displays a special purpose dialog window that retrieves a character string as input.
Definition: dialog_impl.c:558
char * CHGetDesktopFileInfo(const char *szDesktopFile, const char *szInfo)
Get the default application for a particular MIME type.
Definition: conf_help.c:3087
XFontStruct * WBGetWindowFontStruct(Window wID)
Returns the current XFontStruct pointer assigned to the window (may be NULL)
#define FW_EDIT_PASTE_MENU
Definition: child_frame.h:114
Frame Window API functions and definitions.
internal wrapper struct for X11 &#39;geometry&#39; definition
void FWMoveChildFrameTabIndex(WBFrameWindow *pFW, WBChildFrame *pCont, int iIndex)
Sets the specific contained window to a particular index in the tab order.
#define FW_MENU_HANDLER_ENTRY(X, Y, Z)
Use this macro to define an entry for a WBFWMenuHandler array.
Definition: frame_window.h:886
void WBEndPaint(Window wID, GC gc)
&#39;Paint&#39; helper, frees resources and marks the update region &#39;valid&#39;
#define WB_WARN_PRINT(...)
Preferred method of implementing a &#39;warning level&#39; debug message for all subsystems.
Definition: debug_helper.h:261
Icon - yellow circle with white exclamation point.
#define FW_FILE_OPEN_MENU
Definition: child_frame.h:107
#define FW_FILE_NEW_ACCEL
Definition: child_frame.h:119
#define FW_EDIT_COPY_MENU
Definition: child_frame.h:113
char * CHParseXMLTagContents(const char *pTagContents, int cbLength)
Parses contents of a single XML tag, returning as WBAlloc&#39;d string list similar to environment string...
Definition: conf_help.c:2546
void WBCatString(char **ppDest, const char *pSrc)
A simple utility that concatenates a string onto the end of a 0-byte terminated WBAlloc() string...