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