X11workbench Toolkit  1.0
frame_window.c
Go to the documentation of this file.
1 // __ _ _ //
3 // / _| _ __ __ _ _ __ ___ ___ __ __(_) _ __ __| | ___ __ __ ___ //
4 // | |_ | '__|/ _` || '_ ` _ \ / _ \ \ \ /\ / /| || '_ \ / _` | / _ \\ \ /\ / / / __| //
5 // | _|| | | (_| || | | | | || __/ \ V V / | || | | || (_| || (_) |\ V V /_| (__ //
6 // |_| |_| \__,_||_| |_| |_| \___|_____\_/\_/ |_||_| |_| \__,_| \___/ \_/\_/(_)\___| //
7 // |_____| //
8 // //
9 // a top-level window that can have menus, toolbars, a status bar, and a client area //
10 // //
12 
13 /*****************************************************************************
14 
15  X11workbench - X11 programmer's 'work bench' application and toolkit
16  Copyright (c) 2010-2019 by Bob Frazier (aka 'Big Bad Bombastic Bob')
17 
18 
19  DISCLAIMER: The X11workbench application and toolkit software are supplied
20  'as-is', with no warranties, either implied or explicit.
21  Any claims to alleged functionality or features should be
22  considered 'preliminary', and might not function as advertised.
23 
24  MIT-like license:
25 
26  There is no restriction as to what you can do with this software, so long
27  as you include the above copyright notice and DISCLAIMER for any distributed
28  work that is equal to or derived from this one, along with this paragraph
29  that explains the terms of the license if the source is also being made
30  available. A "derived work" describes a work that uses a significant portion
31  of the source files or algorithms that are included with this one.
32  Specifically excluded from this are files that were generated by the software,
33  or anything that is included with the software that is part of another package
34  (such as files that were created or added during the 'configure' process).
35  Specifically included is the use of part or all of any of the X11 workbench
36  toolkit source or header files in your distributed application. If you do not
37  ship the source, the above copyright statement is still required to be placed
38  in a reasonably prominent place, such as documentation, splash screens, and/or
39  'about the application' dialog boxes.
40 
41  Use and distribution are in accordance with GPL, LGPL, and/or the above
42  MIT-like license. See COPYING and README files for more information.
43 
44 
45  Additional information at http://sourceforge.net/projects/X11workbench
46 
47 ******************************************************************************/
48 
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <unistd.h>
60 #include <memory.h>
61 #include <string.h>
62 #include <strings.h>
63 #include <signal.h>
64 #include <time.h>
65 
66 #define _FRAME_WINDOW_C_ /* because of the atoms */
67 
68 #include "window_helper.h"
69 #include "pixmap_helper.h" // pixmap helpers, including pre-defined icons
70 #include "frame_window.h"
71 #include "child_frame.h"
72 #include "conf_help.h"
73 #include "draw_text.h"
74 #include "dialog_window.h"
75 
76 
77 #define TAB_BAR_HEIGHT 24 /* for now */
78 #define TAB_BAR_TAB_WIDTH 20 /* 20 average characters wide */
79 #define TAB_BAR_SCROLL_WIDTH 12 /* width of 'tab scroll' buttons */
80 #define TAB_BAR_ADD_BUTTON_WIDTH 12 /* width of 'tab add' button */
81 #define STATUS_BAR_HEIGHT 20
82 #define DEFAULT_STATUS_BAR_TAB 16 /* default 16 characters per column */
83 
84 #define DEFAULT_STATUS_STRING "Status: none"
85 
86 // NOTE: estimating twice the average font char width as 12. Should see at least 2 tabs, always, with min width (YMMV)
87 #define FRAME_WINDOW_MIN_WIDTH (100 + 12 * TAB_BAR_TAB_WIDTH) /* re-sizable window absolute minimum width */
88 #define FRAME_WINDOW_MIN_HEIGHT (100 + TAB_BAR_HEIGHT + STATUS_BAR_HEIGHT) /* re-sizable window absolute minimum height */
89 
90 #define NEW_TAB_MESSAGE 1
91 #define NEXT_TAB_MESSAGE 2
92 #define PREV_TAB_MESSAGE 3
93 #define SET_TAB_MESSAGE 4
94 #define CLOSE_TAB_MESSAGE 5
95 #define REORDER_TAB_MESSAGE 6
96 
97 
98 typedef struct __FRAME_WINDOW__
99 {
100  WBFrameWindow wbFW;
101 
102  WBFWMenuHandler *pMenuHandler; // pointer to the current menu handler structure array. don't 'free' this (it's a cache)
103  WBFWMenuHandler *pDefMenuHandler; // pointer to (internally allocated) default menu handler (assigned by 'FWSetMenuHandlers()')
104 
105  int nChildFrames; // total number of child frames
106  int nMaxChildFrames; // max number of child frames within the current array
107  WBChildFrame **ppChildFrames; // Pointer to type-abstracted array of child frames
108  // ctrl+alt+pageup/pagedown to navigate from one tab to another
109  // NOTE: 'ppChildFrames' is a contiguous array, without any NULLs in between
110 
111  WB_RECT rctTabBar; // tab bar rectangle for mousie hits (from last Expose)
112  int nFocusTab; // which tab has the focus? 0 if no tabs
113  int nLastTab; // last tab to have focus; -1 when no tabs visible
114  int nLeftTab, nRightTab; // left and right tab indices, same order as in ppChildFrames (0 if no tabs)
115  int nCloseTab; // a flag that indicates I'm closing a tab (UI mostly, triggers event on mouse-up)
116 
117  char *szTitle; // title bar string (WBAlloc'd)
118  char *szStatus; // status bar string (WBAlloc'd)
119 
120  int nStatusBarTabs; // # of tab entries. can be negative if pStatusBarTabs is NULL
121  int *pStatusBarTabs; // status bar tab values (WBAlloc'd). may be NULL.
122 
123  char *pDefaultMenuResource; // menu resource for 'default' menu
124 
125  int (* pFWCallback)(Window wID, XEvent *pEvent); // registered callback function (for various things)
126 
127  int bTabBarRectAntiRecurse; // anti-recurse flag for calculating tab bar rect
128  WB_RECT rctLastTabBarRect; // cached value of tab bar rectangle
129  int nTabBarTabWidth; // cached calculated width of a tab on the tab bar
130  int nTabBarButtonFlags; // button flags for tab bar ('pressed')
131 
132  // fonts and font sets and metrics
133  WB_FONT pFont; // cached font for regular font
134  WB_FONT pBoldFont; // cached font for bold font
135  int nAvgCharWidth; // cached average character width for the specified font
136  int nFontHeight; // cached font height for fonts
137  int nFontAscent; // cached font ascent
138  int nFontDescent; // cached font descent
139 
140  struct __FRAME_WINDOW__ *pNext; // next in chain (internal use)
141 
142 } FRAME_WINDOW;
143 
144 struct __status_tab_cols__
145 {
146  int left;
147  int right;
148  int align;
149 };
150 
151 enum __tab_bar_button_flags__
152 {
153  tab_bar_button_PREV = 1, // left-scroll button "clicked" - ctrl+alt+pageup
154  tab_bar_button_NEXT = 2, // right-scroll button "clicked" - ctrl+alt_pagedown
155  tab_bar_button_NEW = 4, // new tab - ctrl+N
156  tab_bar_button_BUTTONMASK = 7, // bitmask for the buttons (disables 'drag' handling)
157  tab_bar_button_GRAB = 8 // mouse being grabbed
158 };
159 
160 
161 
162 
163 int FWDefaultCallback(Window wID, XEvent *pEvent); // default callback for frame windows (call for default processing)
164 
165 static FRAME_WINDOW *InternalGet_FRAME_WINDOW(WBFrameWindow *pFW);
166 
167 static void InternalPaintTabBar(FRAME_WINDOW *pFrameWindow, XExposeEvent *pEvent);
168 static void InternalPaintStatusBar(FRAME_WINDOW *pFrameWindow, XExposeEvent *pEvent);
169 
170 static int Internal_Tab_Bar_Event(FRAME_WINDOW *pFrameWindow, XEvent *pEvent);
171 
172 static void InternalCalcStatusBarRect(FRAME_WINDOW *pFrameWindow, WB_RECT *pRect);
173 static void InternalCalcTabBarRect(FRAME_WINDOW *pFrameWindow, WB_RECT *pRect);
174 
175 int __internal_do_status_tab_cols(FRAME_WINDOW *pFrameWindow, const WB_RECT *prct, char ***pppCols, char **ppData,
176  struct __status_tab_cols__ **ppTabs, int *pnCol);
177 
178 
179 static FRAME_WINDOW *pFrames = NULL; // pointer to linked list of frame windows (WBAlloc'd)
180 
181 static XColor clrFG, clrBG, clrBD, clrBD2, clrBD3, clrHBG;
182 static int iInitColorFlag = 0;
183 
184 
185 // ATOMS used by frame windows
186 
191 Atom aTAB_MESSAGE = None;
192 
198 static void __internal_destroy_frame_window(FRAME_WINDOW *pTemp)
199 {
200 int i1;
201 Window wIDMenu;
202 WBMenuBarWindow *pMB;
203 Display *pDisplay;
204 
205 
206  pDisplay = WBGetWindowDisplay(pTemp->wbFW.wID);
207 
208  if(pTemp->ppChildFrames)
209  {
210  for(i1=pTemp->nChildFrames - 1; i1 >= 0; i1--)
211  {
212  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Frame,
213  "%s - Destroy contents %d\n",
214  __FUNCTION__, i1 + 1);
215 
216  WBChildFrame *pC = pTemp->ppChildFrames[i1];
217 
218  if(pC)
219  {
220  pC->pOwner = NULL; // so it doesn't try to remove itself
221 
222  pTemp->ppChildFrames[i1] = NULL; // so I don't try to do this again
223  FWDestroyChildFrame(pC); // should validate 'pC' before destroying
224  }
225  }
226 
227  WBFree(pTemp->ppChildFrames);
228 
229  pTemp->ppChildFrames = NULL;
230  pTemp->nChildFrames = 0; // by convention
231  pTemp->nMaxChildFrames = 0; // this too
232  }
233 
234  // destroy the menu bar window and menu
235  wIDMenu = WBGetMenuWindow(pTemp->wbFW.wID);
236 
237  if(wIDMenu != None)
238  {
239  WBRemoveMenuWindow(pTemp->wbFW.wID, wIDMenu); // this should destroy it if no references
240 
241  pMB = MBGetMenuBarWindowStruct(wIDMenu);
242  if(pMB)
243  {
245  }
246  }
247 
248  if(pTemp->szTitle)
249  {
250  WBFree(pTemp->szTitle);
251  pTemp->szTitle = NULL;
252  }
253 
254  if(pTemp->szStatus)
255  {
256  WBFree(pTemp->szStatus);
257  pTemp->szStatus = NULL;
258  }
259 
260  if(pTemp->pStatusBarTabs)
261  {
262  WBFree(pTemp->pStatusBarTabs);
263  pTemp->pStatusBarTabs = NULL;
264  }
265 
266  if(pTemp->pDefMenuHandler)
267  {
268  WBFree(pTemp->pDefMenuHandler);
269  pTemp->pDefMenuHandler = NULL;
270  }
271 
272  if(pTemp->pDefaultMenuResource)
273  {
274  WBFree(pTemp->pDefaultMenuResource);
275  pTemp->pDefaultMenuResource = NULL;
276  }
277 
278  if(pTemp->pFont)
279  {
280  WBFreeFont(pDisplay, pTemp->pFont);
281  pTemp->pFont = NULL;
282  }
283 
284  if(pTemp->pBoldFont)
285  {
286  WBFreeFont(pDisplay, pTemp->pBoldFont);
287  pTemp->pBoldFont = NULL;
288  }
289 
290 }
291 
292 void WBFrameWindowExit()
293 {
294  // destroy all of the frame windows
295 
296  while(pFrames)
297  {
298  FRAME_WINDOW *pTemp = pFrames;
299  pFrames = pFrames->pNext;
300 
301  // slightly different - free the structure first, THEN destroy the window
302  WBSetWindowData(pTemp->wbFW.wID, 0, NULL);
303  __internal_destroy_frame_window(pTemp);
304  WBFree(pTemp);
305 
306  WBDestroyWindow(pTemp->wbFW.wID); // making sure...
307  }
308 
309 }
310 
311 static void InternalCheckFWAtoms(void)
312 {
313 Display *pDisplay = WBGetDefaultDisplay();
314 
315 
316  if(aTAB_MESSAGE == None)
317  {
318  aTAB_MESSAGE = WBGetAtom(pDisplay, "FW_TAB_MESSAGE");
319 
320  if(aTAB_MESSAGE == None)
321  {
322  WB_ERROR_PRINT("ERROR: %s - unable to initialize atom(s)\n", __FUNCTION__);
323  }
324  }
325 }
326 
327 
328 //#define LOAD_COLOR0(X,Y) if(CHGetResourceString(WBGetDefaultDisplay(), X, Y, sizeof(Y)) > 0) { }
329 //#define LOAD_COLOR(X,Y,Z) if(CHGetResourceString(WBGetDefaultDisplay(), X, Y, sizeof(Y)) <= 0){ WB_WARN_PRINT("%s - WARNING: can't find color %s, using default value %s\n", __FUNCTION__, X, Z); strcpy(Y,Z); }
330 #define COPY_COLOR_NAME(X,Y,Z) {const char *pX = X(WBGetDefaultDisplay()); if(pX) strncpy(Y,pX,sizeof(Y)); else strncpy(Y,Z,sizeof(Y));}
331 
332 static void InternalCheckFWColors(void)
333 {
334 Colormap colormap;
335 int iY, iU, iV, iR, iG, iB;
336 
337 
338  // *Frame.background, *Frame.foreground, *WmFrame.background, *WmFrame.foreground,
339  // *Form.background, *Form.foreground, *background, *foreground
340 
341  if(!iInitColorFlag)
342  {
343 // static const char szBD2[]="#FFFFFF"; // border colors (TODO: derive them?)
344 // static const char szBD3[]="#9C9A94";
345  char szFG[16], szBG[16], szBD[16], szHBG[16]; // note colors can typically be up to 13 characters + 0 byte
346 
347  colormap = DefaultColormap(WBGetDefaultDisplay(), DefaultScreen(WBGetDefaultDisplay()));
348 
349  COPY_COLOR_NAME(CHGetTextColor,szFG,"#000000");
351 
353 
354  COPY_COLOR_NAME(CHGetBorderColor,szBD,"#000000");
355 
356  // TODO: add some sanity to this, maybe an API for loading colors? *MOST* of this is now obsolete
357  // and XSETTINGS uses completely different naming.
358 
359 #if 0
360  // (these color names and standards have changed *WAY* too many times...)
361  LOAD_COLOR0("*Frame.foreground",szFG) else LOAD_COLOR0("*Form.foreground", szFG)
362  else LOAD_COLOR0("*WmFrame.foreground",szFG) else LOAD_COLOR0("*WmForm.foreground", szFG)
363  else LOAD_COLOR("*foreground", szFG, "#000000");
364  LOAD_COLOR0("*Frame.background",szBG) else LOAD_COLOR0("*Form.background", szBG)
365  else LOAD_COLOR0("*WmFrame.background",szBG) else LOAD_COLOR0("*WmForm.background", szBG)
366  else LOAD_COLOR("*background", szBG, "#dcdad5"); // default for gnome
367  LOAD_COLOR0("*Frame.border",szBD) else LOAD_COLOR0("*Form.border", szBD)
368  else LOAD_COLOR0("*WmFrame.border",szBD) else LOAD_COLOR0("*WmForm.border", szBD)
369  else LOAD_COLOR0("*borderColor", szBD)
370  else LOAD_COLOR("*border", szBD, "black"); // default for gnome
371 
372  LOAD_COLOR("selected_bg_color", szABG, "#0040FF"); // a slightly greenish blue for the 'selected' color
373 #endif // 0
374 
375  XParseColor(WBGetDefaultDisplay(), colormap, szFG, &clrFG);
376  XAllocColor(WBGetDefaultDisplay(), colormap, &clrFG);
377  XParseColor(WBGetDefaultDisplay(), colormap, szBG, &clrBG);
378  XAllocColor(WBGetDefaultDisplay(), colormap, &clrBG);
379  XParseColor(WBGetDefaultDisplay(), colormap, szBD, &clrBD);
380  XAllocColor(WBGetDefaultDisplay(), colormap, &clrBD);
381  XParseColor(WBGetDefaultDisplay(), colormap, szHBG, &clrHBG);
382  XAllocColor(WBGetDefaultDisplay(), colormap, &clrHBG);
383 
384 
385  // ---------------------------------------------------------------------------------------
386  // 3D border colors - determine a decent set of border colors for clrBD2 and clrBD3 using
387  // the background color. highlight luminocity will average between black and background
388  // for the shaded color (clrBD3) and between white and background for highlight (clrBD2).
389  // ---------------------------------------------------------------------------------------
390 
391  if((clrBG.flags & (DoRed | DoGreen | DoBlue)) != (DoRed | DoGreen | DoBlue))
392  {
394  &clrBG); // make sure RGB is correctly assigned
395  }
396 
397  RGB255_FROM_XCOLOR(clrBG, iR, iG, iB);
398 
399  PXM_RGBToYUV(iR, iG, iB, &iY, &iU, &iV);
400 
401  // the highlight color should be 1/4 of the way between the background color and white, using the same U and V
402 
403  PXM_YUVToRGB((3 * iY + 256) / 4, iU, iV, &iR, &iG, &iB);
404 
405  RGB255_TO_XCOLOR(iR, iG, iB, clrBD2); // assign new RGB values to the XColor struct
407  &clrBD2); // re-assign pixel element from RGB values
408 
409  // the shaded color should be 3/4 of the way between black and the background color, using the same U and V
410 
411  PXM_YUVToRGB((3 * iY) / 4, iU, iV, &iR, &iG, &iB);
412 
413  RGB255_TO_XCOLOR(iR, iG, iB, clrBD3); // assign new RGB values to the XColor struct
415  &clrBD3); // re-assign pixel element from RGB values
416 
417  XAllocColor(WBGetDefaultDisplay(), colormap, &clrBD2);
418  XAllocColor(WBGetDefaultDisplay(), colormap, &clrBD3);
419 
420  iInitColorFlag = 1;
421  }
422 }
423 
424 XColor FWGetDefaultFG(void)
425 {
426  InternalCheckFWColors();
427 
428  return clrFG;
429 }
430 
431 XColor FWGetDefaultBG(void)
432 {
433  InternalCheckFWColors();
434 
435  return clrBG;
436 }
437 
438 XColor FWGetDefaultBD(void)
439 {
440  InternalCheckFWColors();
441 
442  return clrBD;
443 }
444 
446 {
447 FRAME_WINDOW *pFrameWindow;
448 
449 
450  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
451 
452  if(!pFrameWindow)
453  {
454  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
455 
456  return NULL;
457  }
458 
459  return pFrameWindow->pFont;
460 }
461 
463 {
464 FRAME_WINDOW *pFrameWindow;
465 
466 
467  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
468 
469  if(!pFrameWindow)
470  {
471  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
472 
473  return NULL;
474  }
475 
476  return pFrameWindow->pBoldFont;
477 }
478 
479 
480 static FRAME_WINDOW *InternalGet_FRAME_WINDOW(WBFrameWindow *pFW)
481 {
482 FRAME_WINDOW *pTemp;
483 
484 
485  if(!pFW || pFW->ulTag != FRAME_WINDOW_TAG)
486  {
487  return NULL; // not valid
488  }
489 
490  // normally will not take long - there aren't many frame windows
491 
492  pTemp = pFrames; // start at the head of the list
493 
494  while(pTemp && pTemp != (FRAME_WINDOW *)pFW)
495  {
496  pTemp = pTemp->pNext;
497  }
498 
499  if(pTemp)
500  {
501  return(pTemp);
502  }
503 
504  return NULL; // not valid (this also helps with possible 'use after free' bugs)
505 }
506 
507 
508 WBFrameWindow *FWCreateFrameWindow(const char *szTitle, int idIcon, const char *szMenuResource,
509  int iX, int iY, int iWidth, int iHeight,
510  WBWinEvent pUserCallback, int iFlags)
511 {
512  FRAME_WINDOW *pNew;
513  Display *pDisplay;
514  XSetWindowAttributes xswa; /* Temporary Set Window Attribute struct */
515  XSizeHints xsh; /* Size hints for window manager */
516  XWMHints xwmh;
517 // Atom aProto[3];
518 
519  pNew = (FRAME_WINDOW *)WBAlloc(sizeof(*pNew));
520 
521  if(!pNew)
522  {
523  return NULL;
524  }
525 
526  pDisplay = WBGetDefaultDisplay();
527 
528  InternalCheckFWColors(); // this assigns clrBD and clrBG properly
529  InternalCheckFWAtoms(); // if not already done, assign atoms at this time
530 
531 
532  bzero(pNew, sizeof(*pNew)); // NULL out the entire structure
533  // assign the 'non-NULL' values for structure members
534  pNew->wbFW.ulTag = FRAME_WINDOW_TAG;
535  pNew->wbFW.wID = -1; // initial (bad) value
536  pNew->wbFW.iFlags = iFlags; // save this while I'm at it
537 
538  if(szTitle)
539  {
540  pNew->szTitle = WBAlloc(strlen(szTitle) + 1);
541  if(!pNew->szTitle)
542  {
543  WBFree(pNew);
544  return NULL;
545  }
546  strcpy(pNew->szTitle, szTitle);
547  }
548 
549  pNew->nStatusBarTabs = 0; // DEFAULT_STATUS_BAR_TAB;
550  pNew->pStatusBarTabs = NULL;
551 
552  pNew->bTabBarRectAntiRecurse = 0; // must do this too (anti-recurse flag for tab bar)
553  pNew->nTabBarButtonFlags = 0; // must do this as well (tab bar button bit flags)
554  pNew->nCloseTab = -1; // mark that I'm NOT closing a tab (make sure
555 
556  // do the default status now
557 
558  pNew->szStatus = NULL;
559 
560  // fonts, font sets, and related info
561 
562  pNew->pFont = WBCopyFont(WBGetDefaultDisplay(), WBGetDefaultFont()); // make copy of default font
563 
564  if(pNew->pFont)
565  {
566  pNew->pBoldFont = WBCopyModifyFont(pDisplay, WBGetDefaultFont(), 0, WBFontFlag_WT_BOLD);
567  }
568 
569  if(!pNew->pFont || !pNew->pBoldFont)
570  {
571  WB_ERROR_PRINT("ERROR: %s - unable to create frame window (font functions failed)\r\n", __FUNCTION__);
572 
574 
575  return NULL;
576  }
577 
578  // cache font metrics. use 'fontSet'
579 
580  pNew->nAvgCharWidth = WBFontAvgCharWidth(pNew->pFont);
581  pNew->nFontAscent = WBFontAscent(pNew->pFont);
582  pNew->nFontDescent = WBFontDescent(pNew->pFont);
583  pNew->nFontHeight = pNew->nFontAscent + pNew->nFontDescent; // TODO: use 'max bounds' info??
584 
585 
586  // -------------------------------
587  // INTERNAL LIST OF FRAME WINDOWS
588  // -------------------------------
589 
590  // add struct to beginning of linked list 'cause it's faster that way
591 
592  pNew->pNext = pFrames;
593  pFrames = pNew;
594 
595 
596  // NOW I get to create the actual window with its WBGC and callback proc
597 
598 // bzero(&xswa, sizeof(xswa));
599 //
600 // xswa.border_pixel = clrBD.pixel;
601 // xswa.background_pixel = clrBG.pixel;
602 // xswa.colormap = DefaultColormap(pDisplay, DefaultScreen(pDisplay));
603 // xswa.bit_gravity = CenterGravity;
604 
605  WBInitWindowAttributes(&xswa, clrBD.pixel, clrBG.pixel,
606  DefaultColormap(pDisplay, DefaultScreen(pDisplay)),
607  CenterGravity);
608 
609  pNew->wbFW.wID = WBCreateWindow(pDisplay, None, FWDefaultCallback, "FrameWindow",
610  iX, iY, iWidth, iHeight, 1, InputOutput,
611  CWBorderPixel | CWBackPixel | CWColormap | CWBitGravity,
612  &xswa);
613 
614  if(pNew->wbFW.wID == None)//<= 0)
615  {
617 
618  WB_ERROR_PRINT("ERROR: %s - unable to create frame window (WBCreateWindow failed)\r\n", __FUNCTION__);
619 
620  return NULL;
621  }
622 
623  // immediately identify this window using window data
624  WBSetWindowData(pNew->wbFW.wID, 0, (void *)pNew);
625  pNew->pFWCallback = pUserCallback;
626 
627  bzero(&xsh, sizeof(xsh));
628 
629  xsh.flags = (USPosition | USSize | PBaseSize | PWinGravity);
630  xsh.x = iX;
631  xsh.y = iY;
632  xsh.width = iWidth;
633  xsh.height = iHeight;
634  xsh.base_width = iWidth / 8 > FRAME_WINDOW_MIN_WIDTH ? iWidth / 8 : FRAME_WINDOW_MIN_WIDTH; // set min size
635  xsh.base_height = iHeight / 8 > FRAME_WINDOW_MIN_HEIGHT ? iHeight / 8 : FRAME_WINDOW_MIN_HEIGHT;
636 
637  // if I have a status bar, assign space for it in the 'base height'
638  if(WBFrameWindow_STATUS_BAR & pNew->wbFW.iFlags) // window has tabs
639  {
640  xsh.base_height += STATUS_BAR_HEIGHT; // TODO: base this on font height. use the same font as the menu.
641  }
642 
643  xsh.win_gravity = NorthWestGravity; // StaticGravity
644 
645  bzero(&xwmh, sizeof(xwmh));
646  xwmh.flags = InputHint;
647  xwmh.input = !0; // this represents 'Locally Active'
648 
649 // from http://mail.gnome.org/archives/wm-spec-list/2007-March/msg00000.html
650 
651 // The ICCCM provides for focus models for a window:
652 //
653 // Input Model Input Field WM_TAKE_FOCUS
654 // No Input False Absent
655 // Passive True Absent
656 // Locally Active True Present
657 // Globally Active False Present
658 //
659 // No Input - The application never expects to receive focus.
660 //
661 // Passive - The application will get focus but it will not grab it on
662 // its own. Instead it will only get focus if the window manager gives it
663 // to it. The application cannot help determine if it wants the focus or
664 // not at the given time/situation.
665 //
666 // Locally Active - The same as passive, but the application will also
667 // give focus to other windows that it owns of its own free will, without
668 // any interaction with the window manager, using XSetInputFocus.
669 //
670 // Globally Active - The application will not receive focus from the
671 // window manager. Instead it will determine when it wants focus (from
672 // WM_TAKE_FOCUS suggestions given by the window manager, or from events
673 // such as ButtonPress or KeyPress). The application will then acquire
674 // focus on its own using XSetInputFocus.
675 
676  // set title, size hints, and 'WM_HINTS' hints (so WM knows where to put the window and how to set focus)
677  WBSetWMProperties(pNew->wbFW.wID, szTitle, &xsh, &xwmh, NULL);
678 
679 #if 0
680  aProto[0] = aWM_DELETE_WINDOW; // for now, that's the only one
681  aProto[1] = aWM_TAKE_FOCUS; // GDK does this, see set_wm_protocols() in gdkwindow-x11.c
682 // aProto[2] = a_NET_WM_PING; // GDK does this, see set_wm_protocols() in gdkwindow-x11.c
683 // aProto[3] = a_NET_WM_SYNC_REQUEST; // GDK does this when HAVE_XSYNC is enabled
684 
685  aProto[sizeof(aProto)/sizeof(aProto[0]) - 1] = None; // have an extra one at the end
686 
687  XSetWMProtocols(pDisplay, pNew->wbFW.wID, aProto, sizeof(aProto)/sizeof(aProto[0]) - 1);
688  // sets WM_PROTOCOLS property for the main window...
689 #endif // 0
690 
691  WBSetWMProtocols(pNew->wbFW.wID, aWM_DELETE_WINDOW, aWM_TAKE_FOCUS, None);
692 
693  WBCreateWindowDefaultGC(pNew->wbFW.wID, clrFG.pixel, clrBG.pixel);
694 
695  // now allow specific kinds of input messages
696  XSelectInput(pDisplay, pNew->wbFW.wID,
698 
699 
700  // NEXT, create the menu (if the resource string isn't NULL)
701 
702  if(szMenuResource && *szMenuResource)
703  {
704  WBMenuBarWindow *pMenu;
705 
706  pNew->pDefaultMenuResource = WBCopyString(szMenuResource);
707 
708  if(!pNew->pDefaultMenuResource)
709  {
711  return NULL;
712  }
713 
714  pMenu = MBCreateMenuBarWindow(pNew->wbFW.wID, szMenuResource, 0);
715 
716  if(!pMenu)
717  {
719  return NULL;
720  }
721  }
722 
723  FWRecalcLayout(pNew->wbFW.wID); // prior to making me visible do this
724 
725  WBSetWindowIcon(pNew->wbFW.wID, idIcon);
726 
727  if(iFlags & WBFrameWindow_APP_WINDOW)
728  {
729 // WB_ERROR_PRINT("TEMPORARY: %s - setting %u (%08xH) as application window\n",
730 // __FUNCTION__, (int)pNew->wbFW.wID, (int)pNew->wbFW.wID);
731 
732  WBSetApplicationWindow(pNew->wbFW.wID);
733  }
734 
735  if(iFlags & WBFrameWindow_VISIBLE)
736  {
737  WBMapWindow(pDisplay, pNew->wbFW.wID); // make window visible
738  WBGetParentWindow(pNew->wbFW.wID); // this syncs everything up
739  }
740 
741  return (WBFrameWindow *)pNew;
742 }
743 
744 void FWDestroyFrameWindow(Window wID)
745 {
747 }
748 
750 {
751  // step 1: unhook pFrameWindow
752  FRAME_WINDOW *pTemp = pFrames;
753  FRAME_WINDOW *pPrev = NULL;
754  Window wID;
755 // int i1;
756 
757  if(!pFrameWindow || pFrameWindow->ulTag != FRAME_WINDOW_TAG)
758  {
759  WB_ERROR_PRINT("ERROR: %s - invalid frame window pointer!\n", __FUNCTION__);
760 
761  return;
762  }
763 
764  wID = pFrameWindow->wID;
765 
766  // this next part validates that it's in the linked list
767 
768  while(pTemp && pTemp != (FRAME_WINDOW *)pFrameWindow)
769  {
770  pPrev = pTemp;
771  pTemp = pTemp->pNext;
772  }
773 
774  if(pTemp)
775  {
776  if(pPrev)
777  {
778  pPrev->pNext = pTemp->pNext; // unhook
779  }
780  else if(pFrames == pTemp)
781  {
782  pFrames = pTemp->pNext;
783  }
784  }
785 
786 
787  if(wID > 0)
788  {
789  WBDestroyWindow(wID); // this will free the structure
790  }
791  else
792  {
793  // I must destroy WBAlloc'd entries and contained windows in lieu of 'FWDefaultCallback'
794  __internal_destroy_frame_window((FRAME_WINDOW *)pFrameWindow);
795  WBFree(pFrameWindow);
796  }
797 }
798 
800 {
801 FRAME_WINDOW *pFrameWindow;
802 
803 
804  if(!pFW || pFW->ulTag != FRAME_WINDOW_TAG)
805  {
806  return;
807  }
808 
809  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
810 
811  if(!pFrameWindow)
812  {
813  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
814 
815  return;
816  }
817 
818  pFrameWindow->pFWCallback = pCallBack;
819 }
820 
821 void FWSetMenuHandlers(WBFrameWindow *pFW, const WBFWMenuHandler *pHandlerArray)
822 {
823 int i1;
824 const WBFWMenuHandler *pH;
825 FRAME_WINDOW *pFrameWindow;
826 
827 
828  if(!pFW || pFW->ulTag != FRAME_WINDOW_TAG)
829  {
830  return;
831  }
832 
833  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
834 
835  if(!pFrameWindow)
836  {
837  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
838 
839  return;
840  }
841 
842  if(pFrameWindow->pDefMenuHandler)
843  {
844  if(pFrameWindow->pMenuHandler == pFrameWindow->pDefMenuHandler)
845  {
846  pFrameWindow->pMenuHandler = NULL; // don't re-use pointer
847  }
848 
849  WBFree(pFrameWindow->pDefMenuHandler);
850 
851  pFrameWindow->pDefMenuHandler = NULL;
852  }
853 
854  if(!pHandlerArray)
855  {
856  return;
857  }
858 
859  // count the number of entries
860  for(i1=0, pH = pHandlerArray; pH->lMenuID || pH->callback || pH->UIcallback; i1++, pH++)
861  {
862  // NOTHING inside the loop. just count.
863  }
864 
865  // allocate space and make a copy
866 
867  pFrameWindow->pDefMenuHandler = (WBFWMenuHandler *)WBAlloc(sizeof(WBFWMenuHandler) * (i1 + 2));
868 
869  if(pFrameWindow->pDefMenuHandler)
870  {
871  memcpy(pFrameWindow->pDefMenuHandler, pHandlerArray,
872  sizeof(WBFWMenuHandler) * (i1 + 1));
873  }
874 
875  if(!pFrameWindow->pMenuHandler)
876  {
877  pFrameWindow->pMenuHandler = pFrameWindow->pDefMenuHandler; // re-assigned
878  }
879 }
880 
881 
882 static void InternalCalcStatusBarRect(FRAME_WINDOW *pFrameWindow, WB_RECT *pRect)
883 {
884 int iBarHeight;
885 //WB_FONT pFont;
886 
887 
888  WBGetClientRect(pFrameWindow->wbFW.wID, pRect); // get the rectangle for the client area
889 
890 // pFont = pFrameWindow->pFont; // TODO: do I still need this??
891 
892  iBarHeight = pFrameWindow->nFontHeight + 8; // 8 pixels more than overall font height
893 
894  if(iBarHeight < STATUS_BAR_HEIGHT) // the minimum value
895  {
896  iBarHeight = STATUS_BAR_HEIGHT;
897  }
898 
899  pRect->top = pRect->bottom - iBarHeight; // TODO: base this on font height
900 }
901 
902 static void InternalCalcTabBarRect(FRAME_WINDOW *pFrameWindow, WB_RECT *pRect)
903 {
904 Window wIDMenu;
905 WB_RECT rct, rct2;
906 WBMenuBarWindow *pMB;
907 int iBarHeight, nVisibleTabs;
908 //WB_FONT pFont;
909 
910 
911  if(pFrameWindow->bTabBarRectAntiRecurse)
912  {
913  memcpy(pRect, &(pFrameWindow->rctLastTabBarRect), sizeof(*pRect));
914  return; // don't do anything if I'm recursing
915  }
916 
917  WBGetClientRect(pFrameWindow->wbFW.wID, pRect); // get the rectangle for the client area (always)
918 
919  if(WBFrameWindow_NO_TABS & pFrameWindow->wbFW.iFlags) // window has NO tabs
920  {
921  pRect->bottom = pRect->top; // no tab bar (empty rectangle where it WOULD be)
922 
923  pFrameWindow->nFocusTab = 0;
924  pFrameWindow->nLastTab = -1;
925  pFrameWindow->nLeftTab = 0;
926  pFrameWindow->nRightTab = 0;
927 
928 // WB_ERROR_PRINT("TEMPORARY: %s - EMPTY tab rectangle\n", __FUNCTION__);
929 
930  memcpy(&(pFrameWindow->rctLastTabBarRect), pRect, sizeof(*pRect));
931 
932  return;
933  }
934 
935 // pFont = pFrameWindow->pFont;
936 
937  iBarHeight = pFrameWindow->nFontHeight + 14; // 14 pixels more than overall font height
938 
939  if(iBarHeight < TAB_BAR_HEIGHT) // the minimum value
940  {
941  iBarHeight = TAB_BAR_HEIGHT;
942  }
943 
944  WBGetClientRect(pFrameWindow->wbFW.wID, &rct); // get the rectangle for the client area
945 
946  pRect->top = rct.top;
947  pRect->left = rct.left;
948  pRect->right = rct.right;
949  pRect->bottom = rct.bottom;
950 
951  wIDMenu = WBGetMenuWindow(pFrameWindow->wbFW.wID);
952 
953  if(wIDMenu != None)
954  {
955  pMB = MBGetMenuBarWindowStruct(wIDMenu);
956 
957  if(pMB)
958  {
959  WBGetWindowRect(wIDMenu, &rct2);
960 
961  // what I want is the height.
962  pRect->top += rct2.bottom - rct2.top + 1; // regardless of rct2 values, add the height, plus 1 pixel as calculated here
963  }
964  }
965 
966  pRect->bottom = pRect->top + iBarHeight;
967 
968  if(memcmp(&(pFrameWindow->rctLastTabBarRect), pRect, sizeof(*pRect)))
969  {
970 // WB_ERROR_PRINT("TEMPORARY: %s - new 'last tab bar rect': %d, %d, %d, %d\n", __FUNCTION__,
971 // pRect->left, pRect->top, pRect->right, pRect->bottom);
972 
973  // since the rectangle has changed, I need to re-paint things
974 
975  WBInvalidateRect(pFrameWindow->wbFW.wID, pRect, 0); // make sure I re-paint the tab bar, but not *RIGHT* *NOW*
976  }
977 
978  memcpy(&(pFrameWindow->rctLastTabBarRect), pRect, sizeof(*pRect)); // cache for later
979 
980  // NOW, use the current 'focus tab' to determine which tabs should be visible. AND, determine
981  // which tabs should be visible based on the WIDTH.
982 
983  if(!pFrameWindow->nChildFrames) // there aren't any
984  {
985  pFrameWindow->nFocusTab = 0;
986  pFrameWindow->nLastTab = -1;
987  pFrameWindow->nLeftTab = 0;
988  pFrameWindow->nRightTab = 0;
989 
990  return;
991  }
992 
993  // verify valid 'focus tab' to maintain consistency
994 
995  if(pFrameWindow->nFocusTab < 0)
996  {
997  pFrameWindow->bTabBarRectAntiRecurse = 1;
998 
999  FWSetFocusWindowIndex(&(pFrameWindow->wbFW), 0);
1000 
1001  pFrameWindow->bTabBarRectAntiRecurse = 0;
1002  }
1003  else if(pFrameWindow->nFocusTab >= pFrameWindow->nChildFrames)
1004  {
1005  pFrameWindow->bTabBarRectAntiRecurse = 1;
1006 
1007  WB_ERROR_PRINT("WARN: %s - focus tab exceeds # child frames - %d >= %d (fixing it)\n",
1008  __FUNCTION__, pFrameWindow->nFocusTab, pFrameWindow->nChildFrames);
1009 
1010  FWSetFocusWindowIndex(&(pFrameWindow->wbFW), pFrameWindow->nChildFrames - 1);
1011 
1012  pFrameWindow->bTabBarRectAntiRecurse = 0;
1013  }
1014 
1015  // figure out what the width of a single 'tab' will be
1016 
1017  pFrameWindow->nTabBarTabWidth = (TAB_BAR_TAB_WIDTH * pFrameWindow->nAvgCharWidth)
1018  + 6 + 14; // 6 is border and spacing plus 12 for doc icon and spacing
1019  // TODO: use some kind of '#define' for these 'magic number' values
1020 
1021 
1022  // verify the right/left tabs are 'in range' by calculating tab width and placement
1023 
1024  nVisibleTabs = pRect->right - pRect->left // CALCULATE TOTAL WIDTH OF TAB AREA, THEN DIVIDE BY TAB BAR WIDTH
1025  - TAB_BAR_SCROLL_WIDTH * 2
1026  - TAB_BAR_ADD_BUTTON_WIDTH
1027  - 8; // 8 pixels for additional spacing - 1 per scroll button, 2 more for 'add' buttun, + 2 more
1028 
1029  nVisibleTabs /= pFrameWindow->nTabBarTabWidth; // figure out how many "full" tabs I can view in the current tab bar area
1030 
1031  if(nVisibleTabs <= 0)
1032  {
1033  nVisibleTabs = 1; // always, just in case
1034  }
1035 
1036 
1037  // now, RE-adjust the tab bar width UP to match "that" number of tabs visible
1038  // this value will be cached in the FRAME_WINDOW structure for when I paint it
1039 
1040  pFrameWindow->nTabBarTabWidth = (pRect->right - pRect->left
1041  - TAB_BAR_SCROLL_WIDTH * 2
1042  - TAB_BAR_ADD_BUTTON_WIDTH
1043  - 8) // 8 pixels for additional spacing - 1 per scroll button, 2 more for 'add' buttun, + 2 more
1044  / nVisibleTabs; // re-calculates based on # of visible tabs - so they're a tad bigger now...
1045 
1046  if(nVisibleTabs >= pFrameWindow->nChildFrames) // enough room for everyone? If so, show maximum # of tabs
1047  {
1048  pFrameWindow->nLeftTab = 0;
1049  pFrameWindow->nRightTab = pFrameWindow->nChildFrames - 1;
1050 
1051 // WB_ERROR_PRINT("TEMPORARY: %s - left tab=%d, right tab=%d\n", __FUNCTION__,
1052 // pFrameWindow->nLeftTab, pFrameWindow->nRightTab);
1053  }
1054  else // mangling the right/left tabs to include focus tab, and fit in the width
1055  {
1056  // in case right/left are out of range, fix them first
1057  if(pFrameWindow->nRightTab >= pFrameWindow->nChildFrames)
1058  {
1059  pFrameWindow->nRightTab = pFrameWindow->nChildFrames - 1;
1060  pFrameWindow->nLeftTab = pFrameWindow->nChildFrames - nVisibleTabs;
1061  }
1062 
1063  if(pFrameWindow->nLeftTab < 0)
1064  {
1065  pFrameWindow->nLeftTab = 0;
1066  pFrameWindow->nRightTab = pFrameWindow->nLeftTab + nVisibleTabs - 1;
1067  }
1068 
1069  // shrank window and not as many tabs are visible now??
1070  if(pFrameWindow->nRightTab >= pFrameWindow->nLeftTab + nVisibleTabs)
1071  {
1072  pFrameWindow->nRightTab = pFrameWindow->nLeftTab + nVisibleTabs - 1;
1073  }
1074 
1075  // grew window and more than the previous visible tabs are visible now?
1076  if((unsigned int)pFrameWindow->nLeftTab + (unsigned int)nVisibleTabs >= (unsigned int)pFrameWindow->nRightTab)
1077  { // NOTE: promoted to 'unsigned int' to avoid gcc's overflow warnings - none of these values are negative at this point
1078 
1079  // extend left to include more tabs
1080  pFrameWindow->nLeftTab = pFrameWindow->nRightTab - nVisibleTabs + 1;
1081 
1082  if(pFrameWindow->nLeftTab < 0)
1083  {
1084  pFrameWindow->nRightTab -= pFrameWindow->nLeftTab;
1085  pFrameWindow->nLeftTab = 0;
1086  }
1087  }
1088 
1089  if(pFrameWindow->nFocusTab < pFrameWindow->nLeftTab) // is my focus tab scrolled too far left?
1090  {
1091  pFrameWindow->nLeftTab = pFrameWindow->nFocusTab;
1092  pFrameWindow->nRightTab = pFrameWindow->nLeftTab + nVisibleTabs - 1;
1093  }
1094  else if(pFrameWindow->nFocusTab > pFrameWindow->nRightTab) // is my focus tab scrolled too far right?
1095  {
1096  pFrameWindow->nRightTab = pFrameWindow->nFocusTab;
1097  pFrameWindow->nLeftTab = pFrameWindow->nRightTab - nVisibleTabs +1;
1098  }
1099 
1100 // WB_ERROR_PRINT("TEMPORARY: %s - left tab=%d, right tab=%d, focus tab = %d\n", __FUNCTION__,
1101 // pFrameWindow->nLeftTab, pFrameWindow->nRightTab, pFrameWindow->nFocusTab);
1102  }
1103 
1104  // th-dya th-dya th-dya th-that's all, folks!
1105 }
1106 
1107 void FWRecalcLayout(Window wID)
1108 {
1109 WB_RECT rct, rct2;
1110 FRAME_WINDOW *pFrameWindow;
1111 Window wIDMenu;
1112 WBMenuBarWindow *pMB;
1113 WBChildFrame *pCW;
1114 int i1, iMax;
1115 int fPaintNow = 0;
1116 
1117 
1118  pFrameWindow = (FRAME_WINDOW *)FWGetFrameWindowStruct(wID);
1119 
1120  if(!pFrameWindow)
1121  {
1122  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
1123 
1124  return;
1125  }
1126 
1127  // this callback happens any time I change the window size. Use this
1128  // also to re-size scrollbars and status bars and menus.
1129 
1130  // calculate the actual size of the client area and inform all of the 'Child Frame'
1131  // windows so that they can assign the size and fix scrollbars, etc. as appropriate
1132 
1133  WBGetClientRect(wID, &rct); // get the rectangle for the entire client area
1134 
1135  // obtain the height and position of the menu (if any), toolbar (if any), and tab area (if any)
1136  // and subtract that from rct.top - then record this as my "client area" in the FRAME_WINDOW
1137  // structure (iClientX, iClientY, iClientWidth, iClientHeight).
1138 
1139 
1140  if(!(WBFrameWindow_NO_TABS & pFrameWindow->wbFW.iFlags)) // window has tabs
1141  {
1142  InternalCalcTabBarRect(pFrameWindow, &rct2);
1143 
1144  rct.top = rct2.bottom + 1;
1145  }
1146  else if(None != (wIDMenu = WBGetMenuWindow(wID)))
1147  {
1148  pMB = MBGetMenuBarWindowStruct(wIDMenu);
1149 
1150  if(pMB)
1151  {
1152  WBGetWindowRect(wIDMenu, &rct2);
1153 
1154  // what I want is the height.
1155  rct.top += rct2.bottom - rct2.top + 1; // regardless of rct2 values, add the height, plus 1 pixel as calculated here
1156  }
1157  }
1158 
1159 
1160  // if this window has a status bar on the bottom, subtract an appropriate height
1161  // from the bottom of the client area.
1162 
1163  if(WBFrameWindow_STATUS_BAR & pFrameWindow->wbFW.iFlags) // window has a status bar
1164  {
1165  InternalCalcStatusBarRect(pFrameWindow, &rct2);
1166 
1167  rct.bottom = rct2.top - 1;
1168  }
1169 
1170  iMax = FWGetNumContWindows(&(pFrameWindow->wbFW)); // grab this value now. will use it later
1171 
1172  // Now that I've calculated the new client rectangle area, assign appropriate thingies
1173  // (but if it matches, skip next part, since nothing changed)
1174 
1175  rct.right -= rct.left; // now it's a width
1176  rct.bottom -= rct.top; // now it's a height
1177 
1178  if(pFrameWindow->wbFW.iClientX == rct.left &&
1179  pFrameWindow->wbFW.iClientY == rct.top &&
1180  pFrameWindow->wbFW.iClientWidth == rct.right &&
1181  pFrameWindow->wbFW.iClientHeight == rct.bottom)
1182  {
1183  goto check_curtab; // nothing changed
1184  }
1185 
1186  // assign the new values. This will be the "true" client area
1187  // of the frame window, excluding "things"
1188  pFrameWindow->wbFW.iClientX = rct.left;
1189  pFrameWindow->wbFW.iClientY = rct.top;
1190  pFrameWindow->wbFW.iClientWidth = rct.right; // which is now a width
1191  pFrameWindow->wbFW.iClientHeight = rct.bottom; // which is now a height
1192 
1193 
1194  // next, inform all of the child frames that I'm different now
1195  // NOTE: I won't be re-calculating child frames unless the frame window changed
1196  // but if you must FORCE it, call THIS function first, then FWChildFrameRecalcLayout()
1197  // on whatever child frame window(s) you need to force re-calc on. There is
1198  // no harm in re-calling the layout recalc for the child frame after this function
1199  // returns to the caller.
1200 
1201  for(i1=0; i1 < iMax; i1++)
1202  {
1203  pCW = FWGetContainedWindowByIndex(&(pFrameWindow->wbFW), i1);
1204 
1205  if(pCW)
1206  {
1207  FWChildFrameRecalcLayout(pCW); // inform the child frame of the layout change. should only invalidate, not re-paint
1208  }
1209  }
1210 
1211  fPaintNow = 1; // before I exit, update the child frame window
1212 
1213 
1214  // temporarily use 'error level' so I can always see this
1215  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
1216  "%s - %d, %d, %d, %d\n", __FUNCTION__,
1217  pFrameWindow->wbFW.iClientX, pFrameWindow->wbFW.iClientY,
1218  pFrameWindow->wbFW.iClientWidth, pFrameWindow->wbFW.iClientHeight);
1219 
1220  WBInvalidateGeom(wID, NULL, 1); // and, finally, force a re-paint on myself (mostly hidden by child frame, except tabs, borders, etc.)
1221 
1222 
1223 check_curtab:
1224 
1225  // if my tab focus has changed, notify all child windows
1226 
1227  if(!iMax) // no contained windows, bail out now
1228  {
1229  pFrameWindow->nLastTab = -1; // always assign to '-1' if there are no tabs
1230 
1231  return; // no need to update anything
1232  }
1233 
1234  // did I switch tabs? if so, notify the NEW focus window that it needs to display itself
1235 
1236  pCW = FWGetContainedWindowByIndex(&(pFrameWindow->wbFW), pFrameWindow->nFocusTab);
1237 
1238  if(pFrameWindow->nFocusTab != pFrameWindow->nLastTab)
1239  {
1240  pFrameWindow->nLastTab = pFrameWindow->nFocusTab;
1241 
1242  if(pCW)
1243  {
1244  if(!fPaintNow) // if I didn't do it already
1245  {
1246  FWChildFrameRecalcLayout(pCW); // just in case, inform the child frame of a possible layout change.
1247  }
1248 
1249  WBInvalidateRect(pCW->wID, NULL, 0); // invalidate the child frame window
1250  fPaintNow = 1;
1251  }
1252  }
1253 
1254  if(pCW && fPaintNow)
1255  {
1256  // if re-calc layout in any way caused a need to update the child window, do it NOW
1257  // this is a do-nothing if the invalid region is empty
1258 
1259  WBUpdateWindow/*Immediately*/(pCW->wID);
1260  }
1261 }
1262 
1263 
1264 // CONTAINED WINDOWS
1265 
1267 {
1268 const FRAME_WINDOW *pFrameWindow;
1269 
1270 
1271  pFrameWindow = InternalGet_FRAME_WINDOW((WBFrameWindow *)pFW);
1272 
1273  if(!pFrameWindow)
1274  {
1275  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
1276 
1277  return -1;
1278  }
1279 
1280  if(!pFrameWindow->ppChildFrames)
1281  {
1282  return 0;
1283  }
1284 
1285  return pFrameWindow->nChildFrames;
1286 }
1287 
1289 {
1290 const FRAME_WINDOW *pFrameWindow;
1291 
1292 
1293  pFrameWindow = InternalGet_FRAME_WINDOW((WBFrameWindow *)pFW);
1294 
1295  if(!pFrameWindow)
1296  {
1297  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
1298 
1299  return NULL;
1300  }
1301 
1302  if(pFrameWindow->nChildFrames <= 0)
1303  {
1304  WB_WARN_PRINT("WARNING: %s.%d called with no child frames, returning NULL\n",
1305  __FUNCTION__, __LINE__);
1306 
1307  return NULL;
1308  }
1309 
1310  if(iIndex < 0) // implies "the current focus"
1311  {
1312  iIndex = pFrameWindow->nFocusTab;
1313  }
1314 
1315  if(iIndex < 0 || // still test for this in case 'nFocusTab' is negative (but unlikely)
1316  !pFrameWindow->ppChildFrames || iIndex >= pFrameWindow->nChildFrames)
1317  {
1318  if(pFrameWindow->ppChildFrames) // only if I have an array of child frames do I do the message
1319  {
1320  WB_ERROR_PRINT("ERROR: %s - bad index %d - %d frames, array=%p\n",
1321  __FUNCTION__, iIndex, pFrameWindow->nChildFrames, pFrameWindow->ppChildFrames);
1322  }
1323  else
1324  {
1325  WB_DEBUG_PRINT(DebugLevel_Medium, "ERROR: %s - bad index %d - %d frames, array=%p\n",
1326  __FUNCTION__, iIndex, pFrameWindow->nChildFrames, pFrameWindow->ppChildFrames);
1327  }
1328 
1329  return NULL;
1330  }
1331 
1332  return pFrameWindow->ppChildFrames[iIndex];
1333 }
1334 
1336 {
1337 FRAME_WINDOW *pFrameWindow;
1338 int iRval;
1339 
1340 
1341  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1342 
1343  if(!pFrameWindow)
1344  {
1345  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
1346 
1347  return -1;
1348  }
1349 
1350  if(!pFrameWindow->ppChildFrames)
1351  {
1352  pFrameWindow->nMaxChildFrames = 256; // for now
1353 
1354  pFrameWindow->ppChildFrames = (WBChildFrame **)WBAlloc(pFrameWindow->nMaxChildFrames * sizeof(WBChildFrame *));
1355  if(!pFrameWindow->ppChildFrames)
1356  {
1357  pFrameWindow->nMaxChildFrames = 0;
1358 
1359 no_memory:
1360  WB_ERROR_PRINT("ERROR: %s - not enough memory\n", __FUNCTION__);
1361 
1362  return -1;
1363  }
1364 
1365  pFrameWindow->nChildFrames = 0; // initially
1366  }
1367  else if((pFrameWindow->nChildFrames + 1) >= pFrameWindow->nMaxChildFrames)
1368  {
1369  void *pTemp = WBReAlloc(pFrameWindow->ppChildFrames,
1370  (pFrameWindow->nMaxChildFrames + 128) * sizeof(WBChildFrame *));
1371 
1372  if(!pTemp)
1373  {
1374  goto no_memory;
1375  }
1376 
1377  pFrameWindow->ppChildFrames = (WBChildFrame **)pTemp; // re-assign new pointer
1378 
1379  pFrameWindow->nMaxChildFrames += 128;
1380  }
1381 
1382  // ADD NEW CHILD FRAME TO THE END [TODO insert just past current focus window instead?]
1383 
1384  iRval = pFrameWindow->nChildFrames; // always add to end of list.
1385  (pFrameWindow->nChildFrames)++;
1386  pFrameWindow->ppChildFrames[iRval] = pNew;
1387 
1388  pNew->pOwner = &(pFrameWindow->wbFW); // set THIS frame as the owner in the contained tab
1389 
1390 // WB_ERROR_PRINT("TEMPORARY: %s - adding tab %d window %u (%08xH)\n",
1391 // __FUNCTION__, iRval, (int)pNew->wID, (int)pNew->wID);
1392 
1393  FWRecalcLayout(pFW->wID); // recalc layout for frame window, first (in case it needs it)
1394  FWChildFrameRecalcLayout(pNew); // make sure I recalculate the child frame layout based on the owner
1395 
1396  FWSetFocusWindowIndex(pFW, iRval); // set focus to it
1397 
1398  return iRval;
1399 }
1400 
1402 {
1403 FRAME_WINDOW *pFrameWindow;
1404 int iIndex, i2;
1405 
1406  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1407 
1408  if(!pFrameWindow || !pCont)
1409  {
1410  WB_ERROR_PRINT("ERROR: %s - no frame window pointer or invalid child window pointer!\n", __FUNCTION__);
1411 
1412  return;
1413  }
1414 
1415  if(!pFrameWindow->ppChildFrames || !pFrameWindow->nChildFrames)
1416  {
1417  return; // no child windows in list
1418  }
1419 
1420  for(iIndex=0; iIndex < pFrameWindow->nChildFrames; iIndex++)
1421  {
1422  if(pFrameWindow->ppChildFrames[iIndex] == pCont)
1423  {
1424 #ifndef NO_DEBUG
1425  if(pCont->pOwner != &(pFrameWindow->wbFW))
1426  {
1427  WB_ERROR_PRINT("ERROR: %s - tab's owner %p does not match this frame window (%p)!\n",
1428  __FUNCTION__, (void *)pCont->pOwner, (void *)&(pFrameWindow->wbFW));
1429  }
1430 #endif // NO_DEBUG
1431 
1432  if(pCont->wID != None)
1433  {
1434  WBUnmapWindow(WBGetWindowDisplay(pCont->wID), pCont->wID); // unmap it (make it invisible) [MUST do this]
1435  }
1436 
1437  pCont->pOwner = NULL; // for now, always do this. it prevents trying to re-remove it, etc.
1438 
1439  for(i2=iIndex + 1; i2 < pFrameWindow->nChildFrames; i2++)
1440  {
1441  pFrameWindow->ppChildFrames[i2 - 1] = pFrameWindow->ppChildFrames[i2]; // slide 'em up
1442  }
1443 
1444  pFrameWindow->nChildFrames --;
1445  pFrameWindow->ppChildFrames[pFrameWindow->nChildFrames] = NULL; // by convention, to ensure no pointer re-use
1446 
1447  if(pFrameWindow->nFocusTab >= pFrameWindow->nChildFrames)
1448  {
1449  pFrameWindow->nFocusTab = pFrameWindow->nChildFrames - 1; // focus to last tab if I deleted the last tab
1450  }
1451  else if(pFrameWindow->nFocusTab > iIndex) // was the tab in focus to the right of the one I'm removing?
1452  {
1453  // decrement the tab index, since I deleted something to the left of it.
1454  pFrameWindow->nFocusTab--; // result won't be negative, because of '>' in 'if'
1455  }
1456 
1457  // invalidate the tab bar rectangle first, before I continue
1458  WBInvalidateRect(pFW->wID, &(pFrameWindow->rctLastTabBarRect), 0);
1459 
1460  if(pFrameWindow->nChildFrames <= 0)
1461  {
1462  // switch focus menu to 'default'. there are NO tabs left.
1463 
1464  Window wMB = WBGetMenuWindow(pFrameWindow->wbFW.wID);
1465 
1466  if(wMB != None)
1467  {
1469 
1470  if(pMBW)
1471  {
1472  MBSetMenuBarMenuResource(pMBW, pFrameWindow->pDefaultMenuResource);
1473  }
1474  }
1475 
1476  pFrameWindow->pMenuHandler = pFrameWindow->pDefMenuHandler; // default menu handler (must assign, so no free'd pointer usage)
1477 
1478  pFrameWindow->nChildFrames = 0; // make sure
1479 
1480  FWSetStatusText(pFW, NULL); // remove status text
1481 
1482  FWRecalcLayout(pFW->wID); // recalculate layout (this also updates the frame window and whatnot)
1483  WBInvalidateRect(pFW->wID, NULL, 1); // update everything NOW. it's 'empty'
1484  }
1485  else if(iIndex == pFrameWindow->nFocusTab) // is it the focus window? (not always)
1486  {
1487  FWRecalcLayout(pFW->wID); // recalculate layout (this also updates the frame window and whatnot)
1488 
1489  // focus window changes, so make sure everything is informed
1490 
1491  if(iIndex < pFrameWindow->nChildFrames)
1492  {
1493  FWSetFocusWindowIndex(pFW, iIndex); // note: I just deleted 'iIndex', but the *NEW* 'iIndex' has the focus now
1494  }
1495  else
1496  {
1497  // in this case, I'm deleting the last window in the bunch, so set focus to the last one
1498  FWSetFocusWindowIndex(pFW, pFrameWindow->nChildFrames - 1);
1499  }
1500  }
1501  else
1502  {
1503  // if I'm displaying a different window in the tab, just re-calc the layout
1504 
1505  FWRecalcLayout(pFW->wID); // recalculate layout (this also updates the frame window and whatnot)
1506  FWSetFocusWindowIndex(pFW, pFrameWindow->nFocusTab); // re-assign focus to same, fixes certain problems
1507  }
1508 
1509  return;
1510  }
1511  }
1512 }
1513 
1515 {
1516 FRAME_WINDOW *pFrameWindow;
1517 int iIndex;
1518 
1519 
1520  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1521 
1522  if(!pFrameWindow || !pCont || !pContNew)
1523  {
1524  WB_ERROR_PRINT("ERROR: %s - no frame window pointer or invalid child frame pointer!\n", __FUNCTION__);
1525 
1526  return;
1527  }
1528 
1529  if(!pFrameWindow->ppChildFrames || !pFrameWindow->nChildFrames)
1530  {
1531  // TODO: call 'Add' ?
1532 
1533  return; // no child windows in list
1534  }
1535 
1536  for(iIndex=0; iIndex < pFrameWindow->nChildFrames; iIndex++)
1537  {
1538  if(pFrameWindow->ppChildFrames[iIndex] == pCont)
1539  {
1540  if(pCont->wID != None)
1541  {
1542  WBUnmapWindow(WBGetWindowDisplay(pCont->wID), pCont->wID); // unmap it (make it invisible) [MUST do this]
1543  }
1544 
1545  pCont->pOwner = NULL; // for now, always do this. it prevents trying to re-remove it, etc.
1546 
1547  // now add the NEW window.
1548 
1549  pFrameWindow->ppChildFrames[iIndex] = pContNew;
1550 
1551  pContNew->pOwner = &(pFrameWindow->wbFW); // set THIS frame as the owner in the contained tab
1552 
1553  FWRecalcLayout(pFW->wID); // recalc layout for frame window, first (in case it needs it)
1554  FWChildFrameRecalcLayout(pContNew); // make sure I recalculate the layout based on the owner
1555 
1556 // WB_ERROR_PRINT("TEMPORARY: %s - replacing tab %d, window %u (%08xH)\n",
1557 // __FUNCTION__, iIndex, (int)pContNew->wID, (int)pContNew->wID);
1558 
1559  if(iIndex == pFrameWindow->nFocusTab) // is it the focus window? (not always)
1560  {
1561  FWSetFocusWindowIndex(pFW, iIndex); // this handles the change-out properly
1562  }
1563  }
1564  }
1565 }
1566 
1567 
1569 {
1570 FRAME_WINDOW *pFrameWindow;
1571 int iIndex;
1572 
1573  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1574 
1575  if(!pFrameWindow)
1576  {
1577  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
1578 
1579  return;
1580  }
1581 
1582  // locate the contained window from my list. then set as 'focus tab'
1583 
1584  if(!pFrameWindow->ppChildFrames || !pFrameWindow->nChildFrames)
1585  {
1586  return; // no child windows in list
1587  }
1588 
1589  for(iIndex=0; iIndex < pFrameWindow->nChildFrames; iIndex++)
1590  {
1591  if(pFrameWindow->ppChildFrames[iIndex] == pCont)
1592  {
1593  FWSetFocusWindowIndex(pFW, iIndex);
1594  return;
1595  }
1596  }
1597 }
1598 
1599 void FWSetFocusWindowIndex(WBFrameWindow *pFW, int iIndex)
1600 {
1601 FRAME_WINDOW *pFrameWindow;
1602 WBChildFrame *pC;
1603 int i1;
1604 Display *pDisplay = WBGetWindowDisplay(pFW->wID);
1605 Window wMB;
1606 WBMenuBarWindow *pMBW;
1607 
1608 
1609  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1610 
1611  if(!pFrameWindow)
1612  {
1613  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
1614 
1615  return;
1616  }
1617 
1618  // locate the contained window from my list. then set as 'focus tab'
1619 
1620  if(!pFrameWindow->ppChildFrames || !pFrameWindow->nChildFrames)
1621  {
1622  return; // no child windows in list, or index is greater than the maximum
1623  }
1624 
1625  if(iIndex >= pFrameWindow->nChildFrames)
1626  {
1627  iIndex = pFrameWindow->nChildFrames - 1; // a simple fix
1628  }
1629  else if(iIndex < 0)
1630  {
1631  iIndex = 0;
1632  }
1633 
1634  pFrameWindow->nFocusTab = iIndex; // set focus to THIS one
1635  pFrameWindow->nCloseTab = -1; // mark that I'm NOT closing a tab (make sure)
1636 
1637  // invalidate the tab bar rectangle first, before I continue
1638  WBInvalidateRect(pFW->wID, &(pFrameWindow->rctLastTabBarRect), 0);
1639 
1640  FWRecalcLayout(pFW->wID); // recalculate layout (this also updates the frame window and whatnot)
1641  // note that recalc'ing the layout does NOT re-paint the window.
1642 
1643  pC = pFrameWindow->ppChildFrames[iIndex];
1644 
1645  WBMapWindow(pDisplay, pC->wID); // make sure it's mapped (probably isn't)
1646  WBSetInputFocus(pC->wID);
1647 
1648  // notify the child frame that its status has changed. this will fix status text
1649  // for the frame window, and update it as needed
1650 
1652 
1653  // NOW, go through the list and hide all of the others NOT the focus window
1654  for(i1=0; i1 < pFrameWindow->nChildFrames; i1++)
1655  {
1656  if(i1 == iIndex)
1657  {
1658  continue;
1659  }
1660 
1661  pC = pFrameWindow->ppChildFrames[i1];
1662 
1663 // if(WBIsMapped(pDisplay, pC->wID))
1664  {
1665 // WB_ERROR_PRINT("TEMPORARY: %s - unmapping %u (%08xH)\n", __FUNCTION__, (unsigned int)pC->wID, (unsigned int)pC->wID);
1666 
1667  WBUnmapWindow(pDisplay, pC->wID); // unmap it (make it invisible)
1668  }
1669  }
1670 
1671 
1672  // -------------------
1673  // ASSIGN CORRECT MENU
1674  // -------------------
1675 
1676  wMB = WBGetMenuWindow(pFrameWindow->wbFW.wID);
1677 
1678  if(wMB != None) // we have a menu bar window
1679  {
1680  // NOW, switch to the menu associated with the current child frame
1681 
1682  pMBW = MBGetMenuBarWindowStruct(wMB);
1683 
1684  if(pMBW)
1685  {
1686  if(pC->pszMenuResource)
1687  {
1689  }
1690  else
1691  {
1692  MBSetMenuBarMenuResource(pMBW, pFrameWindow->pDefaultMenuResource);
1693  }
1694  }
1695  }
1696 
1697  if(pC->pMenuHandler)
1698  {
1699  pFrameWindow->pMenuHandler = pC->pMenuHandler; // use child frame's menu handler
1700  }
1701  else
1702  {
1703  pFrameWindow->pMenuHandler = pFrameWindow->pDefMenuHandler;
1704  }
1705 
1706  WBUpdateWindow(pFW->wID); // update these windows now
1707  WBUpdateWindow(pC->wID);
1708 }
1709 
1710 
1712 {
1713 FRAME_WINDOW *pFrameWindow;
1714 int iIndex;
1715 
1716  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1717 
1718  if(!pFrameWindow)
1719  {
1720  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
1721 
1722  return -1;
1723  }
1724 
1725  // locate the contained window from my list. then set as 'focus tab'
1726 
1727  if(!pFrameWindow->ppChildFrames || !pFrameWindow->nChildFrames)
1728  {
1729  return -1; // no child windows in list
1730  }
1731 
1732  if(!pCont)
1733  {
1734  return pFrameWindow->nFocusTab; // return focus tab index
1735  }
1736 
1737  for(iIndex=0; iIndex < pFrameWindow->nChildFrames; iIndex++)
1738  {
1739  if(pFrameWindow->ppChildFrames[iIndex] == pCont)
1740  {
1741  return iIndex;
1742  }
1743  }
1744 
1745  return -1;
1746 }
1747 
1749 {
1750 FRAME_WINDOW *pFrameWindow;
1751 int iI, i1;
1752 WBChildFrame *pC = NULL;
1753 
1754 
1755  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1756 
1757  if(!pFrameWindow)
1758  {
1759  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
1760 
1761  return;
1762  }
1763 
1764  // locate the contained window from my list. then set as 'focus tab'
1765 
1766  if(!pFrameWindow->ppChildFrames || !pFrameWindow->nChildFrames)
1767  {
1768  return; // no child windows in list
1769  }
1770 
1771  iI = FWGetChildFrameIndex(pFW, pCont);
1772 
1773  if(iI < 0)
1774  {
1775  return; // for now...
1776  }
1777 
1778  if(iIndex == -1) // previous
1779  {
1780  iIndex = iI - 1;
1781 
1782  if(iIndex < 0)
1783  {
1784  iIndex = 0;
1785  }
1786 
1787 // WB_ERROR_PRINT("TEMPORARY: %s - move prev %d\n", __FUNCTION__, iIndex);
1788  }
1789  else if(iIndex == -2)
1790  {
1791  iIndex = iI + 1;
1792 
1793 // WB_ERROR_PRINT("TEMPORARY: %s - move next %d\n", __FUNCTION__, iIndex);
1794  }
1795 
1796  if(iIndex >= pFrameWindow->nChildFrames)
1797  {
1798  iIndex = pFrameWindow->nChildFrames - 1;
1799  }
1800 
1801  // move 'iI' to 'iIndex', sliding everything else around
1802 
1803  if(iI > iIndex)
1804  {
1805  pC = pFrameWindow->ppChildFrames[iI];
1806 
1807  for(i1=iI; i1 > iIndex; i1--) // move 1 to the right from iIndex through iI - 1
1808  {
1809  pFrameWindow->ppChildFrames[i1] = pFrameWindow->ppChildFrames[i1 - 1];
1810  }
1811 
1812  pFrameWindow->ppChildFrames[iIndex] = pC;
1813 
1814  if(pFrameWindow->nFocusTab == iI)
1815  {
1816  pFrameWindow->nFocusTab = iIndex;
1817  }
1818  else if(pFrameWindow->nFocusTab >= iIndex && pFrameWindow->nFocusTab < iI)
1819  {
1820  pFrameWindow->nFocusTab ++; // increment it (since I moved things to the right)
1821 
1822  if(pFrameWindow->nFocusTab >= pFrameWindow->nChildFrames)
1823  {
1824  pFrameWindow->nFocusTab = pFrameWindow->nChildFrames - 1; // just in case
1825  }
1826  }
1827  }
1828  else if(iI < iIndex)
1829  {
1830  pC = pFrameWindow->ppChildFrames[iI];
1831 
1832  for(i1=iI; i1 < iIndex; i1++) // move 1 to the left from iI + 1 through iIndex
1833  {
1834  pFrameWindow->ppChildFrames[i1] = pFrameWindow->ppChildFrames[i1 + 1];
1835  }
1836 
1837  pFrameWindow->ppChildFrames[iIndex] = pC;
1838 
1839  if(pFrameWindow->nFocusTab == iI)
1840  {
1841  pFrameWindow->nFocusTab = iIndex;
1842  }
1843  else if(pFrameWindow->nFocusTab > iI && pFrameWindow->nFocusTab <= iIndex)
1844  {
1845  pFrameWindow->nFocusTab --; // decrement it (since I moved things to the left)
1846 
1847  if(pFrameWindow->nFocusTab < 0)
1848  {
1849  pFrameWindow->nFocusTab = 0; // just in case
1850  }
1851  }
1852  }
1853 
1854  WBInvalidateRect(pFW->wID, &(pFrameWindow->rctLastTabBarRect), 0);
1855 
1856  FWRecalcLayout(pFW->wID); // recalculate layout (this also updates the frame window and whatnot)
1857 
1858  WBUpdateWindow(pFW->wID); // update these windows now
1859 
1860  if(pFrameWindow->nFocusTab >= 0 && pFrameWindow->nFocusTab < pFrameWindow->nChildFrames)
1861  {
1862  // make sure I re-paint the focus window
1863 
1864  pC = pFrameWindow->ppChildFrames[pFrameWindow->nFocusTab];
1865 
1866  if(pC)
1867  {
1868  WBUpdateWindow(pC->wID);
1869  }
1870  }
1871 }
1872 
1873 void FWSetStatusText(WBFrameWindow *pFW, const char *szText)
1874 {
1875 WB_RECT rct;
1876 FRAME_WINDOW *pFrameWindow;
1877 
1878 
1879  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1880 
1881  if(!pFrameWindow)
1882  {
1883  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
1884 
1885  return;
1886  }
1887 
1888  if(pFrameWindow->szStatus)
1889  {
1890  WBFree(pFrameWindow->szStatus);
1891  pFrameWindow->szStatus = NULL;
1892  }
1893 
1894  if(szText)
1895  {
1896  pFrameWindow->szStatus = WBCopyString(szText);
1897 
1898  if(!pFrameWindow->szStatus)
1899  {
1900  WB_ERROR_PRINT("ERROR: %s - no memory\n", __FUNCTION__);
1901  }
1902  }
1903 
1904  InternalCalcStatusBarRect(pFrameWindow, &rct);
1905 
1906  WBInvalidateRect(pFrameWindow->wbFW.wID, &rct, 1); // and, finally, invalidate the status bar and force a re-paint
1907 }
1908 
1909 void FWSetStatusTabInfo(WBFrameWindow *pFW, int nTabs, const int *pTabs)
1910 {
1911 WB_RECT rct;
1912 FRAME_WINDOW *pFrameWindow;
1913 
1914 
1915  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1916 
1917  if(!pFrameWindow)
1918  {
1919  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
1920 
1921  return;
1922  }
1923 
1924  if(pFrameWindow->pStatusBarTabs)
1925  {
1926  WBFree(pFrameWindow->pStatusBarTabs);
1927 
1928  pFrameWindow->pStatusBarTabs = NULL;
1929  }
1930 
1931  pFrameWindow->nStatusBarTabs = nTabs;
1932 
1933  if(pTabs)
1934  {
1935  pFrameWindow->pStatusBarTabs = (int *)WBAlloc(sizeof(int) * (nTabs + 1));
1936 
1937  if(!pFrameWindow->pStatusBarTabs)
1938  {
1939  WB_ERROR_PRINT("ERROR: %s - no memory (attempt to allocate %d integers)\n", __FUNCTION__, nTabs);
1940 
1941  return;
1942  }
1943 
1944  memcpy(pFrameWindow->pStatusBarTabs, pTabs, sizeof(int) * nTabs);
1945  pFrameWindow->pStatusBarTabs[nTabs] = 0; // always end in a zero, for now
1946  }
1947 
1948  InternalCalcTabBarRect(pFrameWindow, &rct);
1949 
1950  WBInvalidateRect(pFrameWindow->wbFW.wID, &rct, 0); // and, finally, invalidate the status bar but don't re-paint yet
1951 }
1952 
1953 
1954 
1955 
1956 
1958 // //
1959 // _____ _ _ _ _ _ _ //
1960 // | ____|__ __ ___ _ __ | |_ | | | | __ _ _ __ __| || |(_) _ __ __ _ //
1961 // | _| \ \ / // _ \| '_ \ | __| | |_| | / _` || '_ \ / _` || || || '_ \ / _` | //
1962 // | |___ \ V /| __/| | | || |_ | _ || (_| || | | || (_| || || || | | || (_| | //
1963 // |_____| \_/ \___||_| |_| \__| |_| |_| \__,_||_| |_| \__,_||_||_||_| |_| \__, | //
1964 // |___/ //
1965 // //
1967 
1968 
1969 static void Internal_CalcTabRect(FRAME_WINDOW *pFrameWindow, int iIndex, WB_RECT *prctTab)
1970 {
1971  if(!pFrameWindow->ppChildFrames || iIndex >= pFrameWindow->nChildFrames ||
1972  iIndex < pFrameWindow->nLeftTab || iIndex > pFrameWindow->nRightTab)
1973  {
1974  prctTab->left = prctTab->right = prctTab->top = prctTab->bottom = 0;
1975  return;
1976  }
1977 
1978  prctTab->top = pFrameWindow->rctLastTabBarRect.top + 2;
1979  prctTab->bottom = pFrameWindow->rctLastTabBarRect.bottom;
1980  prctTab->left = (iIndex - pFrameWindow->nLeftTab) * pFrameWindow->nTabBarTabWidth
1981  + TAB_BAR_SCROLL_WIDTH + 2;
1982  prctTab->right = prctTab->left + pFrameWindow->nTabBarTabWidth;
1983 }
1984 
1985 static void Internal_CalcTabGeom(FRAME_WINDOW *pFrameWindow, int iIndex, WB_GEOM *pgTab)
1986 {
1987  if(!pFrameWindow->ppChildFrames || iIndex >= pFrameWindow->nChildFrames ||
1988  iIndex < pFrameWindow->nLeftTab || iIndex > pFrameWindow->nRightTab)
1989  {
1990  pgTab->x = pgTab->y = pgTab->width = pgTab->height = 0;
1991  return;
1992  }
1993 
1994  pgTab->y = pFrameWindow->rctLastTabBarRect.top + 2;
1995  pgTab->height = pFrameWindow->rctLastTabBarRect.bottom - pgTab->y;
1996  pgTab->x = (iIndex - pFrameWindow->nLeftTab) * pFrameWindow->nTabBarTabWidth
1997  + TAB_BAR_SCROLL_WIDTH + 2;
1998  pgTab->width = pFrameWindow->nTabBarTabWidth;
1999 }
2000 
2001 static int Internal_Tab_Bar_Event(FRAME_WINDOW *pFrameWindow, XEvent *pEvent)
2002 {
2003 WB_RECT rctLeft, rctRight, rctNew, rctTemp;
2004 Display *pDisplay = WBGetWindowDisplay(pFrameWindow->wbFW.wID);
2005 XClientMessageEvent evt;
2006 int i1;
2007 
2008 
2009  if(pEvent->type == ButtonPress)
2010  {
2011  // button press within the tab zone means "grab the mouse"
2012 
2014  XGrabPointer(pDisplay, pFrameWindow->wbFW.wID, 1,
2015  ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | PointerMotionMask
2016  | EnterWindowMask | LeaveWindowMask,
2017  GrabModeAsync, // pointer mode
2018  GrabModeAsync, // keyboard mode
2019  None, None, CurrentTime);
2021 
2022  pFrameWindow->nTabBarButtonFlags |= tab_bar_button_GRAB;
2023  pFrameWindow->nTabBarButtonFlags &= ~tab_bar_button_BUTTONMASK;
2024 
2025  // calculate the rectangle for each tab, the scroll left, the scroll right, the 'x'
2026  // buttons for each of the tabs, *AND* the 'new tab' button. Handle each one as needed.
2027 
2028  // scroll buttons first
2029  // if I'm clicking within one of the scroll buttons, or the 'new tab' button, I can
2030  // leave now, after setting the appropriate bit. Drags won't be relevant.
2031 
2032  rctLeft.top = rctRight.top = rctNew.top = pFrameWindow->rctLastTabBarRect.top + 2;
2033  rctLeft.bottom = rctRight.bottom = rctNew.bottom = pFrameWindow->rctLastTabBarRect.bottom - 2;
2034 
2035  rctLeft.left = pFrameWindow->rctLastTabBarRect.left + 2;
2036  rctLeft.right = rctLeft.left + TAB_BAR_SCROLL_WIDTH - 2;
2037 
2038  rctRight.right = pFrameWindow->rctLastTabBarRect.right - 2;
2039  rctRight.left = rctRight.right - TAB_BAR_SCROLL_WIDTH + 2;
2040 
2041  // TODO: verify this formula works correctly
2042  rctNew.right = rctRight.right - TAB_BAR_SCROLL_WIDTH - 2;
2043  rctNew.left = rctNew.right - TAB_BAR_ADD_BUTTON_WIDTH - 2;
2044 
2045  if(WBPointInRect(pEvent->xbutton.x, pEvent->xbutton.y, rctLeft))
2046  {
2047  pFrameWindow->nTabBarButtonFlags |= tab_bar_button_PREV; // TODO: handle if button 'inactive'
2048  }
2049  else if(WBPointInRect(pEvent->xbutton.x, pEvent->xbutton.y, rctRight))
2050  {
2051  pFrameWindow->nTabBarButtonFlags |= tab_bar_button_NEXT; // TODO: handle if button 'inactive'
2052  }
2053  else if(WBPointInRect(pEvent->xbutton.x, pEvent->xbutton.y, rctNew))
2054  {
2055  pFrameWindow->nTabBarButtonFlags |= tab_bar_button_NEW;
2056  }
2057 
2058  if(pFrameWindow->nTabBarButtonFlags & tab_bar_button_BUTTONMASK) // I set a button flag?
2059  {
2060  // force re-paint of tab bar rect
2061 // WB_ERROR_PRINT("TEMPORARY: %s - invalidate rect: %d, %d, %d, %d\n", __FUNCTION__,
2062 // pFrameWindow->rctLastTabBarRect.left,
2063 // pFrameWindow->rctLastTabBarRect.top,
2064 // pFrameWindow->rctLastTabBarRect.right,
2065 // pFrameWindow->rctLastTabBarRect.bottom);
2066 
2067  WBInvalidateRect(pFrameWindow->wbFW.wID, &(pFrameWindow->rctLastTabBarRect), 1);
2068 
2069  return 1; // and return, saying "I did it"
2070  }
2071 
2072 // WB_ERROR_PRINT("TODO: %s - see if I'm clicking on a tab, a tab 'close' button, etc.\n", __FUNCTION__);
2073 
2074  // check to see which tab I might be clicking in
2075 
2076  if(pFrameWindow->nChildFrames)
2077  {
2078  for(i1=pFrameWindow->nLeftTab; i1 <= pFrameWindow->nRightTab; i1++)
2079  {
2080  Internal_CalcTabRect(pFrameWindow, i1, &rctTemp);
2081 
2082  if(WBPointInRect(pEvent->xbutton.x, pEvent->xbutton.y, rctTemp))
2083  {
2084  // did I click on the 'x' button in the upper right corner?
2085  // form a ~square that represents the button using font height. See
2086  rctTemp.right -= 6; // 6 from the right edge
2087  rctTemp.left = rctTemp.right - (pFrameWindow->nFontHeight + 2); // font width + 2 from right
2088  rctTemp.top += 2; // 2 pixels from top
2089  rctTemp.bottom = rctTemp.top + pFrameWindow->nFontHeight; // x height is font height
2090 
2091  // TODO: do I really want to post/send message or should i wait until mouse release? In the
2092  // case of tab delete, I might query for "are you sure" like if the file has not been saved
2093 
2094  if(WBPointInRect(pEvent->xbutton.x, pEvent->xbutton.y, rctTemp))
2095  {
2096  // clicking on the 'x' button to close the tab
2097 
2098  pFrameWindow->nCloseTab = i1; // the tab I'm closing (notify on mouse up)
2099 
2100  WBInvalidateRect(pFrameWindow->wbFW.wID, &(pFrameWindow->rctLastTabBarRect), 1);
2101  }
2102  else
2103  {
2104  pFrameWindow->nCloseTab = -1; // NOT closing a tab (make sure)
2105 
2106  // set focus to tab (do it on button press)
2107 
2108  if(i1 == pFrameWindow->nFocusTab)
2109  {
2110  return 1; // do nothing (already there)
2111  }
2112 
2113  bzero(&evt, sizeof(evt));
2114  evt.type = ClientMessage;
2115 
2116  evt.display = pDisplay;
2117  evt.window = pFrameWindow->wbFW.wID;
2118  evt.message_type = aTAB_MESSAGE;
2119 
2120  evt.data.l[0] = SET_TAB_MESSAGE;
2121  evt.data.l[1] = i1; // the tab index
2122  evt.format = 32; // always
2123 
2124  WBPostEvent(pFrameWindow->wbFW.wID, (XEvent *)&evt); // this will re-paint
2125  }
2126 
2127  return 1;
2128  }
2129  }
2130  }
2131 
2132  // if I get here, I'll do this to make sure the UI is consistent
2133  pFrameWindow->nCloseTab = -1; // NOT closing a tab (make sure)
2134 
2135  return 1; // for now assume "I handled it" since the mousie was grabbed
2136  }
2137  else if(pEvent->type == ButtonRelease)
2138  {
2139  if(!(pFrameWindow->nTabBarButtonFlags & tab_bar_button_GRAB)) // mouse grabbed?
2140  {
2141  pFrameWindow->nCloseTab = -1; // NOT closing a tab
2142  pFrameWindow->nTabBarButtonFlags &= ~tab_bar_button_BUTTONMASK; // turn off button mask if not grabbed
2143 
2144  return 0; // "not handled"
2145  }
2146 
2148  XUngrabPointer(pDisplay, CurrentTime);
2150 
2151  pFrameWindow->nTabBarButtonFlags &= ~tab_bar_button_GRAB;
2152 
2153  if(pFrameWindow->nCloseTab < 0 && // NOT closing a tab (make sure)
2154  !(pFrameWindow->nTabBarButtonFlags & tab_bar_button_BUTTONMASK)) // clicked on a tab, did we?
2155  {
2156  // if I'm releasing on a tab, and I dragged it, change the tab order according to the tab
2157  // that I'm sitting on at the moment...
2158 
2159 // TODO: enable this while dragging stuff, to re-paint the tabs during the drag. but until then, don't
2160 // WBInvalidateRect(pFrameWindow->wbFW.wID, &(pFrameWindow->rctLastTabBarRect), 1);
2161 
2162  WB_ERROR_PRINT("TODO: %s - handle tab-drags, except when I clicked the 'delete tab' button\n", __FUNCTION__);
2163  }
2164  else
2165  {
2166  // notifications
2167 
2168  bzero(&evt, sizeof(evt));
2169  evt.type = ClientMessage;
2170 
2171  evt.display = pDisplay;
2172  evt.window = pFrameWindow->wbFW.wID;
2173  evt.message_type = aTAB_MESSAGE;
2174 
2175  if(pFrameWindow->nCloseTab >= 0) // I'm closing a tab?
2176  {
2177  evt.data.l[0] = CLOSE_TAB_MESSAGE;
2178  evt.data.l[1] = pFrameWindow->nCloseTab;
2179 
2180 // WB_ERROR_PRINT("TEMPORARY: %s - close tab %d\n", __FUNCTION__, pFrameWindow->nCloseTab);
2181 
2182  pFrameWindow->nCloseTab = -1; // NOT closing a tab now (make sure) (also fixes the UI)
2183  }
2184  else if(pFrameWindow->nTabBarButtonFlags & tab_bar_button_NEXT)
2185  {
2186  if(pFrameWindow->nRightTab < (pFrameWindow->nChildFrames - 1))
2187  {
2188  evt.data.l[0] = NEXT_TAB_MESSAGE;
2189  }
2190  else
2191  {
2192  goto no_message;
2193  }
2194  }
2195  else if(pFrameWindow->nTabBarButtonFlags & tab_bar_button_PREV)
2196  {
2197  if(pFrameWindow->nLeftTab > 0)
2198  {
2199  evt.data.l[0] = PREV_TAB_MESSAGE;
2200  }
2201  else
2202  {
2203  goto no_message;
2204  }
2205  }
2206  else if(pFrameWindow->nTabBarButtonFlags & tab_bar_button_NEW)
2207  {
2208  evt.data.l[0] = NEW_TAB_MESSAGE;
2209  }
2210 
2211  evt.format = 32; // always
2212 
2213  WBPostEvent(pFrameWindow->wbFW.wID, (XEvent *)&evt);
2214 
2215 no_message:
2216 
2217  pFrameWindow->nTabBarButtonFlags &= ~tab_bar_button_BUTTONMASK;
2218 
2219  // all-important, mark tab bar rectangle 'to be re-painted' and update it
2220  WBInvalidateRect(pFrameWindow->wbFW.wID, &(pFrameWindow->rctLastTabBarRect), 1);
2221  }
2222 
2223  return 1;
2224  }
2225  else if(pEvent->type == MotionNotify)
2226  {
2227  if(pFrameWindow->nTabBarButtonFlags & tab_bar_button_GRAB) // mouse grabbed?
2228  {
2229  if(!(pFrameWindow->nTabBarButtonFlags & tab_bar_button_BUTTONMASK)) // drag not relevant for these
2230  {
2231  WB_ERROR_PRINT("TODO: %s - handle tab-drags via visual feedback\n", __FUNCTION__);
2232 
2233  return 1; // "handled" (by ignoring it)
2234  }
2235  }
2236  }
2237 
2238  return 0; // not handled
2239 }
2240 
2241 
2242 static void InternalPaintTabBar(FRAME_WINDOW *pFrameWindow, XExposeEvent *pEvent)
2243 {
2244 WB_RECT rct0, rctExpose;
2245 WB_GEOM geom0;
2246 WBGC gc0;
2247 
2248 
2249  if(WBFrameWindow_NO_TABS & pFrameWindow->wbFW.iFlags) // window has no tab bar?
2250  {
2251  return; // no tab bar, just bail
2252  }
2253 
2254  // obtain the most recent cached location of the tab bar. this is important.
2255 
2256  memcpy(&rct0, &(pFrameWindow->rctLastTabBarRect), sizeof(rct0));
2257 
2258  // cache the tab bar rectangle for mouse hit tests. not the same rect as rctLastTabBarRect
2259 
2260  pFrameWindow->rctTabBar.left = rct0.left + 1; // add 1 pixel for border
2261  pFrameWindow->rctTabBar.top = rct0.top + 1;
2262  pFrameWindow->rctTabBar.right = rct0.right - 1; // subtract 1 pixel for border
2263  pFrameWindow->rctTabBar.bottom = rct0.bottom - 1;
2264 
2265 
2266  // does the Expose event intersect my tab bar?
2267 
2268  rctExpose.left = pEvent->x;
2269  rctExpose.top = pEvent->y;
2270  rctExpose.right = rctExpose.left + pEvent->width;
2271  rctExpose.bottom = rctExpose.top + pEvent->height;
2272 
2273  if(!WBRectOverlapped(rct0, rctExpose))
2274  {
2275 // WB_ERROR_PRINT("TEMPORARY: %s - expose event excludes tab bar\n", __FUNCTION__);
2276 
2277  return; // do nothing (no overlap)
2278  }
2279 
2280  // intersect the two rectangles so I only re-paint what I have to
2281 
2282  if(rctExpose.top < rct0.top)
2283  {
2284  rctExpose.top = rct0.top;
2285  }
2286 
2287  if(rctExpose.bottom > rct0.bottom)
2288  {
2289  rctExpose.bottom = rct0.bottom;
2290  }
2291 
2292  if(rctExpose.left < rct0.left)
2293  {
2294  rctExpose.left = rct0.left;
2295  }
2296 
2297  if(rctExpose.right > rct0.right)
2298  {
2299  rctExpose.right = rct0.right;
2300  }
2301 
2302  // time to start painting
2303 
2304  geom0.x = rctExpose.left;
2305  geom0.y = rctExpose.top;
2306  geom0.width = rctExpose.right - rctExpose.left;
2307  geom0.height = rctExpose.bottom - rctExpose.top;
2308 
2309 // WB_ERROR_PRINT("TEMPORARY: %s - tab bar EXPOSE rect %d, %d, %d, %d\n",
2310 // __FUNCTION__, rctExpose.left, rctExpose.top, rctExpose.right, rctExpose.bottom);
2311 
2312  gc0 = WBBeginPaintGeom(pFrameWindow->wbFW.wID, &geom0);
2313 
2314  if(gc0 == None)
2315  {
2316  WB_ERROR_PRINT("ERROR: %s - no GC from WBBeginPaintGeom\n", __FUNCTION__);
2317  }
2318  else
2319  {
2320  Display *pDisplay = WBGetWindowDisplay(pFrameWindow->wbFW.wID);
2321  Drawable dw;
2322  WB_GEOM geom, g2;
2323  WBGC gc;
2324  WB_RECT rct, rctTemp;
2325  XColor clr;
2326  int iDepth;
2327 
2328 
2329  gc = WBGetWindowCopyGC2(pFrameWindow->wbFW.wID, gc0);
2330 
2331 // if(gc != None)
2332 // {
2333 // WBCopyGC2(gc0,
2334 // GCFont | GCFillStyle | GCForeground | GCBackground | GCCapStyle | GCFunction | GCLineWidth,
2335 // gc);
2336 // }
2337 
2338  iDepth = DefaultDepth(pDisplay, DefaultScreen(pDisplay)); // NOTE: depth may be too small...
2339  WB_DEBUG_PRINT((DebugLevel_Heavy | DebugSubSystem_Expose), "InternalPaintTabBar() - depth is %d\n", iDepth);
2340 
2342  dw = (Drawable)XCreatePixmap(pDisplay, pFrameWindow->wbFW.wID,
2343  rct0.right - rct0.left, rct0.bottom - rct0.top,
2344  iDepth);
2346 
2347  if(gc == None || dw == None)
2348  {
2350  if(gc != None)
2351  {
2352  WBFreeGC(gc);
2353  }
2354 
2355  if(dw != None)
2356  {
2357  XFreePixmap(pDisplay, (Pixmap)dw);
2358  }
2360 
2361  dw = pFrameWindow->wbFW.wID;
2362  gc = gc0; // as a fallback
2363 
2364  memcpy(&rct, &rct0, sizeof(rct));
2365  memcpy(&geom, &geom0, sizeof(geom));
2366 
2367  WB_ERROR_PRINT("ERROR: %s - unable to create pixmap or 2nd gc; fallback invoked\n", __FUNCTION__);
2368  }
2369  else
2370  {
2371  Region rgn;
2372 
2373  rct.left = rct.top = 0;
2374 
2375  rct.right = rct0.right - rct0.left;
2376  rct.bottom = rct0.bottom - rct0.top;
2377 
2378  geom.x = geom.y = 0;
2379  geom.width = geom0.width;
2380  geom.height = geom0.height;
2381  geom.border = geom0.border;
2382 
2383  rgn = WBRectToRegion(&rct);
2384 
2386  WBSetRegion(gc, rgn); // new GC has different clip region
2388 
2389  if(rgn != None)
2390  {
2391  XDestroyRegion(rgn);
2392  }
2393  }
2394 
2395 // WB_ERROR_PRINT("TEMPORARY: %s - filling display geom %d, %d, %d, %d\n",
2396 // __FUNCTION__, geom.x, geom.y, geom.width, geom.height);
2397 
2398  // fill the rectangle with the background color
2399 
2401  WBSetForeground(gc, clrBG.pixel);
2402 
2403  WBFillRectangle(pDisplay, dw, gc, rct.left, rct.top, rct.right - rct.left, rct.bottom - rct.top);
2404 
2405  WBSetForeground(gc, clrFG.pixel);
2407 
2408  // draw some lines in some colors for the border
2409 
2410  g2.x = rct.left;
2411  g2.y = rct.top;
2412  g2.width = rct.right - rct.left;
2413  g2.height = rct.bottom - rct.top;
2414 
2415  WBDraw3DBorderRect(pDisplay, dw, gc, &g2, clrBD3.pixel, clrBD2.pixel); // sunken
2416 
2417 
2418  // draw the 'left' (aka 'PREV') button
2419 
2420  g2.x = rct.left + 1;
2421  g2.y = rct.top + 1;
2422  g2.width = TAB_BAR_SCROLL_WIDTH;
2423  g2.height = rct.bottom - rct.top - 2;
2424 
2425  if((pFrameWindow->nTabBarButtonFlags & tab_bar_button_PREV) && pFrameWindow->nLeftTab > 0)
2426  {
2427  WBDraw3DBorderRect(pDisplay, dw, gc, &g2, clrBD3.pixel, clrBD2.pixel);
2428  }
2429  else
2430  {
2431  WBDraw3DBorderRect(pDisplay, dw, gc, &g2, clrBD2.pixel, clrBD3.pixel);
2432  }
2433 
2434  g2.y += g2.height >> 2;
2435  g2.height = (g2.height >> 2) + (g2.height >> 2);
2436 
2437  WBDrawLeftArrow(pDisplay, dw, gc, &g2,
2438  (pFrameWindow->nLeftTab > 0) ? clrFG.pixel : clrBD3.pixel);
2439 
2440 
2441  // draw the 'right' (aka 'NEXT') button
2442 
2443  g2.x = rct.right - TAB_BAR_SCROLL_WIDTH - 1;
2444  g2.y = rct.top + 1;
2445  g2.width = TAB_BAR_SCROLL_WIDTH;
2446  g2.height = rct.bottom - rct.top - 2;
2447 
2448  if((pFrameWindow->nTabBarButtonFlags & tab_bar_button_NEXT) && pFrameWindow->nRightTab < (pFrameWindow->nChildFrames - 1))
2449  {
2450  WBDraw3DBorderRect(pDisplay, dw, gc, &g2, clrBD3.pixel, clrBD2.pixel);
2451  }
2452  else
2453  {
2454  WBDraw3DBorderRect(pDisplay, dw, gc, &g2, clrBD2.pixel, clrBD3.pixel);
2455  }
2456 
2457  g2.y += g2.height >> 2;
2458  g2.height = (g2.height >> 2) + (g2.height >> 2);
2459 
2460  WBDrawRightArrow(pDisplay, dw, gc, &g2,
2461  (pFrameWindow->nRightTab < (pFrameWindow->nChildFrames - 1)) ? clrFG.pixel : clrBD3.pixel);
2462 
2463 
2464  // draw the '+' button for new document
2465 
2466  g2.x = (rct.right - TAB_BAR_SCROLL_WIDTH - 1) - (TAB_BAR_ADD_BUTTON_WIDTH + 1);
2467  g2.y = rct.top + 1;
2468  g2.width = TAB_BAR_ADD_BUTTON_WIDTH;
2469  g2.height = rct.bottom - rct.top - 2;
2470 
2471  if(pFrameWindow->nTabBarButtonFlags & tab_bar_button_NEW)
2472  {
2473  WBDraw3DBorderRect(pDisplay, dw, gc, &g2, clrBD3.pixel, clrBD2.pixel);
2474  }
2475  else
2476  {
2477  WBDraw3DBorderRect(pDisplay, dw, gc, &g2, clrBD2.pixel, clrBD3.pixel);
2478  }
2479 
2480  g2.y += 2;
2481  g2.height -= 4;
2482 
2483  // invent my own color for the '+' button, in this case #0040A0
2484  RGB255_TO_XCOLOR(0, 0x40, 0xa0, clr);
2485 
2486  PXM_RGBToPixel(NULL, &clr);
2487 // WB_ERROR_PRINT("TEMPORARY: red=%d, green=%d, blue=%d, pixel=%d\n",
2488 // clr.red, clr.green, clr.blue, clr.pixel);
2489 
2491  WBSetForeground(gc, clr.pixel/*clrFG.pixel*/); // for now use foreground color; later, ??
2493 
2494  rctTemp.left = g2.x;
2495  rctTemp.top = g2.y;
2496  rctTemp.right = g2.x + g2.width;
2497  rctTemp.bottom = g2.y + g2.height;
2498 
2499  // put a 'splat' in the middle of this button using the 'bold' font
2500  DTDrawSingleLineText(pFrameWindow->pBoldFont,
2501  "+", pDisplay, gc, dw, 0, 0, &rctTemp,
2503 
2505  WBSetForeground(gc, clrFG.pixel); // by convention, (re)set FG color again [for text I need this]
2507 
2508  // TODO: draw the actual tabs and the text within them
2509 
2510 // WB_RECT rctLastTabBarRect; // cached value of tab bar rectangle
2511 // int nTabBarTabWidth; // cached calculated width of a tab on the tab bar
2512 // int nTabBarButtonFlags; // button flags for tab bar ('pressed')
2513 
2514  if(pFrameWindow->ppChildFrames && pFrameWindow->nChildFrames)
2515  {
2516  WBChildFrame *pC;
2517  int i1;
2518 
2519 // if(pFrameWindow->nCloseTab >= 0)
2520 // {
2521 // WB_ERROR_PRINT("TEMPORARY: %s - nCloseTab is %d\n", __FUNCTION__, pFrameWindow->nCloseTab);
2522 // }
2523 
2524  for(i1=pFrameWindow->nRightTab; i1 >= pFrameWindow->nLeftTab; i1--)
2525  {
2526  if(i1 != pFrameWindow->nFocusTab)
2527  {
2528  if(i1 >= pFrameWindow->nChildFrames)
2529  {
2530  continue;
2531  }
2532 
2533  pC = pFrameWindow->ppChildFrames[i1];
2534 
2535  if(!pC)
2536  {
2537  continue;
2538  }
2539 
2540  Internal_CalcTabGeom(pFrameWindow, i1, &g2); // the geom in window coordinates
2541  g2.x -= rct0.left; // translate to 'drawable' coordinates
2542  g2.y -= rct0.top;
2543 
2544  WBDraw3DBorderTab(pDisplay, dw, gc, &g2,
2545  pFrameWindow->nCloseTab == i1 ? -2 : 0, // -2 if I'm deleting the tab (no focus)
2546  clrFG.pixel, clrBG.pixel,
2547  clrBD2.pixel, clrBD3.pixel, clrHBG.pixel,
2548  pFrameWindow->pFont, pFrameWindow->pBoldFont,
2549  pC->aImageAtom, pC->szDisplayName);
2550  }
2551  }
2552 
2553  i1 = pFrameWindow->nFocusTab; // this one is done last
2554 
2555  if(i1 < pFrameWindow->nChildFrames)
2556  {
2557  pC = pFrameWindow->ppChildFrames[i1];
2558 
2559  if(pC)
2560  {
2561  Internal_CalcTabGeom(pFrameWindow, i1, &g2); // the geom in window coordinates
2562  g2.x -= rct0.left; // translate to 'drawable' coordinates
2563  g2.y -= rct0.top;
2564 
2565  WBDraw3DBorderTab(pDisplay, dw, gc, &g2,
2566  pFrameWindow->nCloseTab == i1 ? -1 : 1, // -1 if I'm deleting the tab, positive otherwise
2567  clrFG.pixel, clrBG.pixel,
2568  clrBD2.pixel, clrBD3.pixel, clrHBG.pixel,
2569  pFrameWindow->pFont, pFrameWindow->pBoldFont,
2570  pC->aImageAtom, pC->szDisplayName);
2571  }
2572  }
2573  }
2574 
2575  // if I'm using a pixmap, now I copy it to the window
2576 
2577  if(dw != pFrameWindow->wbFW.wID)
2578  {
2580  // copy my drawn pixmap onto the window
2581  // TODO: re-do this so it's done via an image instead?
2582  XCopyArea(pDisplay, dw, pFrameWindow->wbFW.wID, gc0->gc,
2583  rct.left, rct.top, rct.right - rct.left, rct.bottom - rct.top,
2584  rct0.left, rct0.top);
2586  }
2587 
2588  // RESOURCE CLEANUP
2589 
2591  if(gc != gc0)
2592  {
2593  WBFreeGC(gc);
2594  gc = None;
2595  }
2596 
2597  if(dw != pFrameWindow->wbFW.wID)
2598  {
2600  XFreePixmap(pDisplay, (Pixmap)dw);
2602 
2603  dw = None;
2604  }
2606 
2607  WBEndPaint(pFrameWindow->wbFW.wID, gc0); // and that's it!
2608  }
2609 
2610  // finally, alter the expose event slightly so that it reflects the 'painted' area
2611 
2612  if(pEvent->y < rct0.bottom)
2613  {
2614 // WB_ERROR_PRINT("TEMPORARY: %s - altering x and height in Expose event\n", __FUNCTION__);
2615 
2616  if(pEvent->y >= rct0.top && pEvent->y < rct0.bottom) // so it only includes the tab area but overlaps it
2617  {
2618  pEvent->height -= rct0.bottom - pEvent->y;
2619  pEvent->y = rct0.bottom;
2620 
2621  if(pEvent->height < 0) // just in case
2622  {
2623  pEvent->height = 0;
2624  }
2625  }
2626  }
2627 }
2628 
2629 
2630 static void InternalPaintStatusBar(FRAME_WINDOW *pFrameWindow, XExposeEvent *pEvent)
2631 {
2632 WB_RECT rct, rctExpose, rctTemp, rctPrev;
2633 WB_GEOM geom;
2634 WBGC gc;
2635 int i1;
2636 const char *pszStatus;
2637 
2638 
2639  if(!(WBFrameWindow_STATUS_BAR & pFrameWindow->wbFW.iFlags)) // window has a status bar?
2640  {
2641  return; // no status bar, just bail
2642  }
2643 
2644  // calculate the location of the status bar
2645 
2646  InternalCalcStatusBarRect(pFrameWindow, &rct);
2647 
2648  // does the Expose event intersect my status bar?
2649 
2650  rctExpose.left = pEvent->x;
2651  rctExpose.top = pEvent->y;
2652  rctExpose.right = rctExpose.left + pEvent->width;
2653  rctExpose.bottom = rctExpose.top + pEvent->height;
2654 
2655  if(!WBRectOverlapped(rct, rctExpose))
2656  {
2657 // WB_ERROR_PRINT("TEMPORARY: %s - expose event excludes status bar\n", __FUNCTION__);
2658 
2659  return; // do nothing (no overlap)
2660  }
2661 
2662  // intersect the two rectangles so I only re-paint what I have to
2663 
2664  if(rctExpose.top < rct.top)
2665  {
2666  rctExpose.top = rct.top;
2667  }
2668 
2669  if(rctExpose.bottom > rct.bottom)
2670  {
2671  rctExpose.bottom = rct.bottom;
2672  }
2673 
2674  if(rctExpose.left < rct.left)
2675  {
2676  rctExpose.left = rct.left;
2677  }
2678 
2679  if(rctExpose.right > rct.right)
2680  {
2681  rctExpose.right = rct.right;
2682  }
2683 
2684  // time to start painting
2685 
2686  geom.x = rctExpose.left;
2687  geom.y = rctExpose.top;
2688  geom.width = rctExpose.right - rctExpose.left;
2689  geom.height = rctExpose.bottom - rctExpose.top;
2690 
2691  gc = WBBeginPaintGeom(pFrameWindow->wbFW.wID, &geom);
2692 
2693  if(gc == None)
2694  {
2695  WB_ERROR_PRINT("ERROR: %s - no GC from WBBeginPaintGeom\n", __FUNCTION__);
2696  }
2697  else
2698  {
2699  WB_FONT pFont = pFrameWindow->pFont;
2700  Display *pDisplay = WBGetWindowDisplay(pFrameWindow->wbFW.wID);
2701  XPoint xpt[3];
2702 
2703  // fill the rectangle with the background color
2704 
2705  WBClearWindow(pFrameWindow->wbFW.wID, gc); // should only paint my little rectangle
2706 
2707  // draw some lines in some colors for the border
2708 
2709  // paint the 3D-looking border
2710  WBSetForeground(gc, clrBD2.pixel);
2711  xpt[0].x=rct.left;
2712  xpt[0].y=rct.bottom - 2; // exclude first point
2713  xpt[1].x=rct.left;
2714  xpt[1].y=rct.top;
2715  xpt[2].x=rct.right - 2; // exclude last point
2716  xpt[2].y=rct.top;
2717 
2718  WBDrawLines(pDisplay, pFrameWindow->wbFW.wID, gc, xpt, 3, CoordModeOrigin);
2719 
2720  WBSetForeground(gc, clrBD3.pixel);
2721  xpt[0].x=rct.right - 1;
2722  xpt[0].y=rct.top + 1; // exclude first point
2723  xpt[1].x=rct.right - 1;
2724  xpt[1].y=rct.bottom - 1;
2725  xpt[2].x=rct.left + 1; // exclude final point
2726  xpt[2].y=rct.bottom - 1;
2727 
2728  WBDrawLines(pDisplay, pFrameWindow->wbFW.wID, gc, xpt, 3, CoordModeOrigin);
2729 
2730  WBSetForeground(gc, clrFG.pixel); // by convention, set it to FG [for text I need this]
2731 
2732  if(pFont)
2733  {
2734  rctTemp.left = rct.left + 8; // this is the outside bounding rectangle
2735  rctTemp.top = rct.top + 2; // for the entire status bar.
2736  rctTemp.right = rct.right - 8;
2737  rctTemp.bottom = rct.bottom - 2;
2738 
2739  if(!pFrameWindow->pStatusBarTabs || !pFrameWindow->nStatusBarTabs) // fixed tab width (or no tabs)
2740  {
2741  int iFixedTab = pFrameWindow->nStatusBarTabs;
2742 
2743  if(!iFixedTab)
2744  {
2745  iFixedTab = DEFAULT_STATUS_BAR_TAB;
2746  }
2747 
2748  iFixedTab *= pFrameWindow->nAvgCharWidth;
2749 
2750  pszStatus = pFrameWindow->szStatus;
2751 
2752  if(!pszStatus)
2753  {
2754  pszStatus = DEFAULT_STATUS_STRING; // default status text when none defined
2755  }
2756 
2757  DTDrawSingleLineText(pFont,
2758  pszStatus, pDisplay,
2759  gc, pFrameWindow->wbFW.wID,
2760  iFixedTab, 0, // fixed tab widths
2762  }
2763  else
2764  {
2765  char **ppCols = NULL;
2766  char *pData = NULL;
2767  struct __status_tab_cols__ *pTabs;
2768  int nCol;
2769 
2770  // create the tab and column list using a utility (to simplify THIS code)
2771 
2772  if(!__internal_do_status_tab_cols(pFrameWindow, &rctTemp, &ppCols, &pData, &pTabs, &nCol))
2773  {
2774  memcpy(&rctPrev, &rctTemp, sizeof(rctTemp));
2775 
2776  for(i1=0; i1 < nCol && ppCols[i1]; i1++)
2777  {
2778  rctTemp.left = pTabs[i1].left;
2779  rctTemp.right = pTabs[i1].right;
2780 
2781  DTDrawSingleLineText(pFont, ppCols[i1],
2782  pDisplay, gc, pFrameWindow->wbFW.wID,
2783  0, 0, // no tabs (won't be any)
2784  &rctTemp, DTAlignment_VCENTER | pTabs[i1].align);
2785 
2786  rctTemp.top = rctPrev.top;
2787  rctTemp.bottom = rctPrev.bottom; // restore these two 'just in case'
2788  }
2789 
2790  // WBFree the 3 'WBAlloc'd arrays
2791  WBFree(ppCols);
2792  WBFree(pData);
2793  WBFree(pTabs);
2794  }
2795  }
2796  }
2797 
2798  WBEndPaint(pFrameWindow->wbFW.wID, gc); // and that's it!
2799  }
2800 
2801  // finally, alter the expose event slightly so that it reflects the 'painted' area
2802 
2803  if(pEvent->height + pEvent->x > rct.top)
2804  {
2805 // WB_ERROR_PRINT("TEMPOPRARY: %s - altering height in Expose event\n", __FUNCTION__);
2806 
2807  pEvent->height = rct.top - pEvent->x;
2808  if(pEvent->height < 0)
2809  {
2810  pEvent->height = 0;
2811  }
2812  }
2813 
2814  // TODO: manage top, left, right and x, y, width? probably don't need to
2815 
2816 }
2817 
2818 
2819 int __internal_do_status_tab_cols(FRAME_WINDOW *pFrameWindow, const WB_RECT *prct, char ***pppCols, char **ppData,
2820  struct __status_tab_cols__ **ppTabs, int *pnCol)
2821 {
2822 int i1, i2, i3, i4, iTab, nCol;
2823 char *p1;
2824 
2825 
2826 // if(!pFrameWindow->pStatusBarTabs || !pFrameWindow->nStatusBarTabs) // fixed tab width (or no tabs)
2827 
2828  // this is where things get a bit more fun. Look through 'pStatusBarTabs' for
2829  // left-aligned and right-aligned tabs. Sort them accordingly to determine the width
2830  // of each column. THEN, create the 'pTabs' and associated 'pCols' entries from that
2831  // so that strings and columns align to one another.
2832 
2833  // step 1: how many columns?
2834 
2835  *ppTabs = NULL;
2836  *pppCols = NULL;
2837 
2838  if(pFrameWindow->szStatus)
2839  {
2840  *ppData = WBCopyString(pFrameWindow->szStatus);
2841  }
2842  else
2843  {
2844  *ppData = WBCopyString(DEFAULT_STATUS_STRING); // default status text when none defined
2845  }
2846 
2847  if(!*ppData)
2848  {
2849 error_return:
2850 
2851  *pnCol = 0;
2852 
2853  WB_ERROR_PRINT("ERROR: %s - no memory\n", __FUNCTION__);
2854 
2855  return 1; // error return
2856  }
2857 
2858 
2859  p1 = *ppData;
2860  nCol = 0;
2861 
2862  if(*p1)
2863  {
2864  for(nCol = 1; *p1; )
2865  {
2866  while(*p1 && *p1 != '\t')
2867  {
2868  p1++;
2869  }
2870 
2871  if(*p1 == '\t')
2872  {
2873  *p1 = 0;
2874 
2875  nCol++;
2876  p1++;
2877  }
2878  }
2879  }
2880 
2881  if(nCol > pFrameWindow->nStatusBarTabs)
2882  {
2883  nCol = pFrameWindow->nStatusBarTabs; // this may get even smaller
2884  }
2885 
2886  *pnCol = nCol;
2887 
2888  // 'nCol' is now the total # of columns I'll be printing to. Allocate pppCols now
2889 
2890  *pppCols = (char **)WBAlloc(sizeof(char *)*(nCol + 2));
2891 
2892  if(!*pppCols)
2893  {
2894 error_return2:
2895 
2896  WBFree(*ppData);
2897  *ppData = NULL;
2898 
2899  goto error_return;
2900  }
2901 
2902  *ppTabs = (struct __status_tab_cols__ *)WBAlloc(sizeof(**ppTabs) * (pFrameWindow->nStatusBarTabs + 1));
2903  if(!*ppTabs)
2904  {
2905  WBFree(*pppCols);
2906  *pppCols = NULL;
2907 
2908  goto error_return2;
2909  }
2910 
2911  // time to build up 'pppCols'
2912  p1 = *ppData;
2913 
2914  for(i1=0; i1 < nCol; i1++)
2915  {
2916  (*pppCols)[i1] = p1;
2917 
2918  p1 += strlen(p1) + 1;
2919  }
2920 
2921  // now, set up the tabs so the represent 'columns'. 'left-oriented' tabs will assign
2922  // the 'left' member and 'right' will be -1. 'right-oriented' tabs will assign the
2923  // 'right' member and 'left' will be -1. Then I'll generate columns from the other tab info.
2924 
2925  for(i1=0, iTab=0; i1 < pFrameWindow->nStatusBarTabs; i1++)
2926  {
2927  i2 = pFrameWindow->pStatusBarTabs[i1];
2928  if(i2 & WBStatusTabInfo_BREAK) // a 'tab break', not an actual column
2929  {
2930  continue;
2931  }
2932 
2934  {
2935  (*ppTabs)[iTab].left = -1;
2936  (*ppTabs)[iTab].right = prct->right
2937  - (i2 & WBStatusTabInfo_MASK) * pFrameWindow->nAvgCharWidth;
2938  (*ppTabs)[iTab].align = i2 & WBStatusTabInfo_JUSTIFY_MASK;
2939  }
2940  else
2941  {
2942  (*ppTabs)[iTab].left = prct->left
2943  + (i2 & WBStatusTabInfo_MASK) * pFrameWindow->nAvgCharWidth;
2944  (*ppTabs)[iTab].right = -1;
2945  (*ppTabs)[iTab].align = i2 & WBStatusTabInfo_JUSTIFY_MASK;
2946  }
2947 
2948  iTab++; // increment total count of actual tabs
2949  }
2950 
2951  *pnCol = iTab; // the real # of columns when I have tabs specified
2952 
2953 
2954  // NOW, go through this list and determine how wide each column should be
2955  //
2956 
2957  for(i1=0; i1 < iTab; i1++)
2958  {
2959  if((*ppTabs)[i1].left < 0) // RTL column
2960  {
2961  i3 = prct->left; // use the left edge for my new 'left'. initially
2962  }
2963  else
2964  {
2965  i3 = prct->right; // use the right edge for my new 'right'. initially
2966  }
2967 
2968  for(i2=0; i2 < pFrameWindow->nStatusBarTabs; i2++)
2969  {
2970  // find the minimum 'right' or maximum 'left' position for this column
2971 
2972  i4 = pFrameWindow->pStatusBarTabs[i2];
2973 
2975  {
2976  i4 = prct->right - (i4 & WBStatusTabInfo_MASK) * pFrameWindow->nAvgCharWidth;
2977  }
2978  else
2979  {
2980  i4 = prct->left + (i4 & WBStatusTabInfo_MASK) * pFrameWindow->nAvgCharWidth;
2981  }
2982 
2983  if((*ppTabs)[iTab].left < 0) // right-justified tab
2984  {
2985  if(i4 >= (*ppTabs)[iTab].right) // don't do this, it's further right than me
2986  {
2987  continue;
2988  }
2989 
2990  if(i4 >= i3) // further right than the most current 'left'?
2991  {
2992  i3 = i4 + 1; // note I add 1 to this for column spacing
2993  }
2994  }
2995  else
2996  {
2997  if(i4 <= (*ppTabs)[iTab].left) // don't do this, it's further left than me
2998  {
2999  continue;
3000  }
3001 
3002  if(i4 <= i3) // further left than the most current 'right'?
3003  {
3004  i3 = i4 - 1; // note I add 1 to this for column spacing
3005  }
3006  }
3007  }
3008 
3009  if((*ppTabs)[i1].left < 0) // RTL column
3010  {
3011  (*ppTabs)[i1].left = i3;
3012  }
3013  else
3014  {
3015  (*ppTabs)[i1].right = i3;
3016  }
3017  }
3018 
3019  return 0; // ok!
3020 }
3021 
3022 
3023 int FWDefaultCallback(Window wID, XEvent *pEvent)
3024 {
3025  FRAME_WINDOW *pFrameWindow;
3026  Window wIDMenu;
3027  int iRval = 0;
3028 #ifndef NO_DEBUG
3029  char tbuf[32]; // for keyboard input
3030  int nChar = sizeof(tbuf);
3031 #endif // NO_DEBUG
3032 
3033 
3034  if(pEvent->type == DestroyNotify)
3035  {
3036  if(pEvent->xdestroywindow.window != wID)
3037  {
3038  XEvent evt;
3039 
3040 // WB_ERROR_PRINT("TEMPORARY: %s - DestroyNotify on window %d (%08xH), window %d (%08xH)\n",
3041 // __FUNCTION__,
3042 // (int)pEvent->xdestroywindow.window, (int)pEvent->xdestroywindow.window,
3043 // (int)wID, (int)wID);
3044 
3045  memcpy(&evt, pEvent, sizeof(evt));
3046 
3047  evt.xany.window = pEvent->xdestroywindow.window;
3048 
3049  WBDispatch(&evt); // sends a copy directly to the child window, saying "you are destroyed"
3050  // it should be harmless to do this. It appears that when a window accepts 'sub-structure notify'
3051  // types of events, the owned windows don't get the Destroy Notify events.
3052  }
3053  }
3054 
3055  pFrameWindow = (FRAME_WINDOW *)FWGetFrameWindowStruct(wID);
3056 
3057  if(!pFrameWindow)
3058  {
3059  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
3060 
3061  return 0;
3062  }
3063 
3064  wIDMenu = WBGetMenuWindow(wID);
3065 
3066  // TODO: message re-direction to children BEFORE 'pFWCallback'
3067 
3068  if(wIDMenu) // only if there's a menu window
3069  {
3070  switch(pEvent->type)
3071  {
3072  case ButtonPress:
3073  case ButtonRelease:
3074  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3075  "%s - BUTTON PRESS/RELEASE\n", __FUNCTION__);
3076 
3077  // check tab bar first since I might be grabbing the mouse
3078  if(WBPointInRect(pEvent->xbutton.x, pEvent->xbutton.y, pFrameWindow->rctTabBar) ||
3079  (pFrameWindow->nTabBarButtonFlags & tab_bar_button_GRAB))
3080  {
3081  return Internal_Tab_Bar_Event(pFrameWindow, pEvent);
3082  }
3083 
3084  // check for mouse position within menu window 'wIDMenu'
3085  if(WBPointInWindow(pEvent->xbutton.window, pEvent->xbutton.x, pEvent->xbutton.y, wIDMenu))
3086  {
3087  return WBWindowDispatch(wIDMenu, pEvent); // menu window should handle THESE mousie events
3088  }
3089 
3090  break;
3091 
3092  case MotionNotify:
3093  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3094  "%s - MOTION NOTIFY\n", __FUNCTION__);
3095 
3096  // check tab bar first since I might be grabbing the mouse
3097  if(WBPointInRect(pEvent->xbutton.x, pEvent->xbutton.y, pFrameWindow->rctTabBar) ||
3098  (pFrameWindow->nTabBarButtonFlags & tab_bar_button_GRAB))
3099  {
3100  return Internal_Tab_Bar_Event(pFrameWindow, pEvent);
3101  }
3102 
3103  if(WBPointInWindow(pEvent->xmotion.window, pEvent->xmotion.x, pEvent->xmotion.y, wIDMenu))
3104  {
3105  return(WBWindowDispatch(wIDMenu, pEvent)); // menu window should handle mousie events
3106  }
3107 
3108  break;
3109 
3110  case ConfigureNotify: // window size/position change
3111  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3112  "%s - CONFIGURE NOTIFY\n", __FUNCTION__);
3113 
3114  // allow message to process first, and post a message to myself
3115  // to re-evaluate the layout. This is to avoid having a window that's
3116  // not "changed" yet trying to update its size info when size info is
3117  // not yet accurate.
3118  //
3119  // ALSO - I can get this on a window MOVE without a resize...
3120 
3121  {
3122  Display *pDisplay;
3123  XClientMessageEvent evt;
3124 
3125  pDisplay = WBGetWindowDisplay(wID);
3126 
3127  bzero(&evt, sizeof(evt));
3128  evt.type = ClientMessage;
3129 
3130  evt.display = pDisplay;
3131  evt.window = wID;
3132  evt.message_type = aRESIZE_NOTIFY;
3133  evt.format = 32; // always
3134  evt.data.l[0] = pEvent->xconfigure.x;
3135  evt.data.l[1] = pEvent->xconfigure.y;
3136  evt.data.l[2] = pEvent->xconfigure.x + pEvent->xconfigure.width; // right
3137  evt.data.l[3] = pEvent->xconfigure.y + pEvent->xconfigure.height; // bottom
3138  evt.data.l[4] = pEvent->xconfigure.border_width; // RESERVED (for now, just do it)
3139 
3140  WBPostEvent(wID, (XEvent *)&evt); // NOTE: if too slow, post 'priority' instead
3141  }
3142 
3143  break;
3144 
3145 
3146  case ClientMessage: // menus, etc. (they generate the 'ClientMessage')
3147 
3148 #ifndef NO_DEBUG
3149  {
3150  char *p1 = WBGetAtomName(WBGetWindowDisplay(wID), pEvent->xclient.message_type);
3151 
3152  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3153  "%s - CLIENT MESSAGE: %s\n", __FUNCTION__, p1);
3154 
3155  if(p1)
3156  {
3157  WBFree(p1);
3158  }
3159  }
3160 #endif // NO_DEBUG
3161 
3162  if(pEvent->xclient.message_type == aSET_FOCUS)
3163  {
3164  // set focus to window in data.l[0], or "default" if it's 'None'
3165 
3166  if(pEvent->xclient.data.l[0] == None)
3167  {
3168  // do I have a 'focus' child frame?
3169  WBChildFrame *pFocus = FWGetFocusWindow(&(pFrameWindow->wbFW));
3170 
3171  if(pFocus)
3172  {
3173  WBSetInputFocus(pFocus->wID);
3174  }
3175  else
3176  {
3177  WBSetInputFocus(wID); // set input focus to myself
3178  }
3179  }
3180  else
3181  {
3182  WBSetInputFocus((Window)pEvent->xclient.data.l[0]);
3183  }
3184  }
3185  else if(pEvent->xclient.message_type == aRESIZE_NOTIFY)
3186  {
3187  FWRecalcLayout(wID);
3188 
3189  return 1; // handled (TODO: send to user-defined callback as well?)
3190  }
3191  else if(pEvent->xclient.message_type == aMENU_COMMAND)
3192  {
3194  // _ _ _ //
3195  // _ __ ___ ___ _ __ _ _ | |__ __ _ _ __ __| | | ___ _ __ //
3196  // | '_ ` _ \ / _ \ '_ \| | | | | '_ \ / _` | '_ \ / _` | |/ _ \ '__| //
3197  // | | | | | | __/ | | | |_| | | | | | (_| | | | | (_| | | __/ | //
3198  // |_| |_| |_|\___|_| |_|\__,_| |_| |_|\__,_|_| |_|\__,_|_|\___|_| //
3199  // //
3201 
3202 
3203  // check the container window that has the current focus, and THEN
3204  // check the frame window for an appropriate handler
3205 
3206  WBChildFrame *pFocus = FWGetFocusWindow(&(pFrameWindow->wbFW));
3207 
3208  if(pFocus) // if a tab has a focus, use that window's event handler first
3209  {
3210  int iRet = WBWindowDispatch(pFocus->wID, pEvent);
3211 
3212  if(iRet) // non-zero return
3213  {
3214  WB_ERROR_PRINT("TEMPORARY: %s - child frame handles the menu\n", __FUNCTION__);
3215 
3216  return iRet; // the handler MUST return non-zero if the message should NOT be processed by the frame!
3217  }
3218  }
3219 
3220  if(pFrameWindow->pMenuHandler)
3221  {
3222  // search for the matching menu or ID - anything above 0x10000 is assumed to be a pointer
3223  const WBFWMenuHandler *pHandler = pFrameWindow->pMenuHandler;
3224 
3225  while(pHandler->lMenuID) // zero marks the end
3226  {
3227  uintptr_t lID = pHandler->lMenuID;
3228 
3229  if(pHandler->lMenuID >= 0x10000L) // TODO: this isn't compatible with the WBGetAtom paradigm...
3230  {
3231 #warning is this still potentially dangerous code?
3232  lID = WBGetAtom(WBGetDefaultDisplay(), (const char *)lID);
3233 
3234  if(!lID)
3235  {
3236  continue;
3237  }
3238  }
3239 
3240  if(pEvent->xclient.data.l[0] == lID)
3241  {
3242  if(pHandler->callback)
3243  {
3244  if(pHandler->callback(&(pEvent->xclient)))
3245  {
3246  return 1;
3247  }
3248  }
3249 
3250  return 0; // NOT handled or handler returned zero
3251  }
3252 
3253  pHandler++;
3254  }
3255  }
3256 
3257  // flow through, likely returning 0
3258  }
3259  else if(pEvent->xclient.message_type == aMENU_UI_COMMAND)
3260  {
3262  // _ _ ___ _ _ _ //
3263  // _ __ ___ ___ _ __ _ _ | | | |_ _| | |__ __ _ _ __ __| | | ___ _ __ //
3264  // | '_ ` _ \ / _ \ '_ \| | | | | | | || | | '_ \ / _` | '_ \ / _` | |/ _ \ '__| //
3265  // | | | | | | __/ | | | |_| | | |_| || | | | | | (_| | | | | (_| | | __/ | //
3266  // |_| |_| |_|\___|_| |_|\__,_| \___/|___| |_| |_|\__,_|_| |_|\__,_|_|\___|_| //
3267  // //
3269 
3270  // check 'contained' window for an appropriate UI handler before passing
3271  // it off to the frame window's handler
3272 
3273  WBChildFrame *pFocus = FWGetFocusWindow(&(pFrameWindow->wbFW));
3274 
3275  if(pFocus)
3276  {
3277  int iRet = WBWindowDispatch(pFocus->wID, pEvent);
3278 
3279  if(iRet != 0)
3280  {
3281  WB_ERROR_PRINT("TEMPORARY: %s - child frame handles the menu UI thingy\n", __FUNCTION__);
3282  return iRet;
3283  }
3284 
3285  // TODO: determine if a '-1' value really SHOULD grey out the menu choice anyway or
3286  // if I should return some OTHER value to differentiate 'grey' from 'no handler'
3287  }
3288 
3289  if(pFrameWindow->pMenuHandler)
3290  {
3291  const WBFWMenuHandler *pHandler = pFrameWindow->pMenuHandler;
3292 
3293  while(pHandler->lMenuID) // zero marks the end
3294  {
3295  uintptr_t lID = pHandler->lMenuID;
3296 
3297  if(pHandler->lMenuID >= 0x10000L)
3298  {
3299 #warning is this still potentially dangerous code?
3300  lID = WBGetAtom(WBGetDefaultDisplay(), (const char *)lID);
3301 
3302  if(!lID)
3303  {
3304  continue;
3305  }
3306  }
3307 
3308  if(pEvent->xclient.data.l[0] == lID) // a message handler exists
3309  {
3310  if(pHandler->UIcallback)
3311  {
3312  WBMenu *pMenu;
3313  WBMenuItem *pItem;
3314 
3315  // important detail - the 'data.l' array is assumed to have 32-bit values in it,
3316  // regardless of how its definition and 64/32-bitness affects the actual data storage.
3317  // In effect, only the lower 32-bits is valid. Hence, I must combine two 32-bit values
3318  // together in order to make a 64-bit pointer. For consistency I always use 2 values
3319  // per pointer to pass the information via the message structure. otherwise it gets
3320  // complicated and I really don't like complicated. it causes mistakes, errors, crashes...
3321 
3322 //#if !defined(__SIZEOF_POINTER__) // TODO find a better way to deal with pointer size
3323 //#define __SIZEOF_POINTER__ 0
3324 //#endif
3325 //#if __SIZEOF_POINTER__ == 4 /* to avoid warnings in 32-bit linux */
3326 // pMenu = (WBMenu *)pEvent->xclient.data.l[1];
3327 // pItem = (WBMenuItem *)pEvent->xclient.data.l[3];
3328 //#else // assume 64-bit pointers here, and if they truncate, should NOT get any warnings... well that's the idea
3329 // pMenu = (WBMenu *)((unsigned long long)pEvent->xclient.data.l[1] | ((unsigned long long)pEvent->xclient.data.l[2] << 32));
3330 // pItem = (WBMenuItem *)((unsigned long long)pEvent->xclient.data.l[3] | ((unsigned long long)pEvent->xclient.data.l[4] << 32));
3331 //#endif
3332 // // TODO: validate pointers, otherwise a posted message might crash me (like a vulnerability)
3333 //
3334 //#ifndef NO_DEBUG /* warning off for release build */
3335 //#warning this code potentially has a vulnerability in it - 'pMenu' and 'pItem' could be bad pointers
3336 //#endif // !NO_DEBUG
3337 
3338  pMenu = (WBMenu *)(pEvent->xclient.data.l[1] ? WBGetPointerFromHash(pEvent->xclient.data.l[1]) : NULL);
3339  pItem = (WBMenuItem *)(pEvent->xclient.data.l[2] ? WBGetPointerFromHash(pEvent->xclient.data.l[2]) : NULL);
3340 
3341  if(!pMenu || !pItem)
3342  {
3343  WB_ERROR_PRINT("** ERROR: %s - window=%08xH pMenu=%p, pItem=%p\n", __FUNCTION__, (int)wID, pMenu, pItem);
3344 // WBDebugDumpEvent(pEvent);
3345  }
3346 // if(!WBIsValidMenu(pMenu) || !WBIsValidMenuItem(pItem))
3347 // {
3348 // WB_ERROR_PRINT("ERROR: %s - invalid pMenu=%p or pItem=%p\n", __FUNCTION__, pMenu, pItem);
3349 // }
3350  else
3351  {
3352  return pHandler->UIcallback(pMenu, pItem);
3353  }
3354  }
3355 
3356  return 1; // NO UI handler so return '1' [aka 'enable']
3357  }
3358 
3359  pHandler++;
3360  }
3361  }
3362 
3363  return 0; // if there's no handler and no UI handler, always return 0 (not handled)
3364  }
3365  else if(pEvent->xclient.message_type == aWB_CHAR) // hotkeys for menu-related things
3366  {
3368  // //
3369  // _____ _ ___ __ __ _____ _ _ _ _ _ ____ _____ ___ __ __ _ _____ ___ ___ _ _ //
3370  // | ___|/ | / _ \ | \/ || ____|| \ | || | | | / \ / ___||_ _||_ _|\ \ / // \ |_ _||_ _|/ _ \ | \ | | //
3371  // | |_ | || | | | | |\/| || _| | \| || | | | / _ \ | | | | | | \ \ / // _ \ | | | || | | || \| | //
3372  // | _| | || |_| | | | | || |___ | |\ || |_| | / ___ \| |___ | | | | \ V // ___ \ | | | || |_| || |\ | //
3373  // |_| |_| \___/ |_| |_||_____||_| \_| \___/ /_/ \_\\____| |_| |___| \_//_/ \_\|_| |___|\___/ |_| \_| //
3374  // //
3375  // //
3377 
3378  // looking for F10 [pops up the menu at the first position] and SHIFT+F10 [context menu for child window]
3379 
3380  if(pEvent->xclient.data.l[0] == XK_F10 && // keystroke is F10
3381  (pEvent->xclient.data.l[1] & WB_KEYEVENT_KEYSYM)) // keystroke is a symbol
3382  {
3383  int iACS = (int)pEvent->xclient.data.l[1] & WB_KEYEVENT_ACSMASK;
3384 
3385  if(iACS == WB_KEYEVENT_SHIFT)
3386  {
3387  // send a command to pop up the context menu
3388 
3389  return 1; // handled
3390  }
3391  else if(!iACS) // no ctrl+alt+shift
3392  {
3393  // send command to pop up the frame's menu
3394 
3395  XClientMessageEvent evt;
3396 
3397  // post a high-priority message to myself to display the menu
3398 
3399  bzero(&evt, sizeof(evt));
3400  evt.type = ClientMessage;
3401  evt.display = WBGetWindowDisplay(wIDMenu);
3402  evt.window = wIDMenu;
3403  evt.message_type = aMENU_ACTIVATE;
3404  evt.format = 32;
3405  evt.data.l[0] = 0;
3406  evt.data.l[1] = 0;
3407 
3408  WBPostPriorityEvent(wIDMenu, (XEvent *)&evt);
3409 
3410  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Menu | DebugSubSystem_Frame,
3411  "%s - posting client event message to display menu\n", __FUNCTION__);
3412 
3413  return 1; // handled
3414  }
3415  }
3416  }
3417 #ifndef NO_DEBUG
3418  else
3419  {
3420  char *p1 = WBGetAtomName(WBGetWindowDisplay(wID), pEvent->xclient.message_type);
3421 
3422  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3423  "%s - Client message %s not handled by frame window\n",
3424  __FUNCTION__, p1);
3425 
3426  if(p1)
3427  {
3428  WBFree(p1);
3429  }
3430  }
3431 #endif // NO_DEBUG
3432 
3433  break;
3434 
3435  default:
3436  break;
3437  }
3438  }
3439  else // no main menu, like for a dialog-based or form-based application
3440  {
3441  if(pEvent->xclient.message_type == aWB_CHAR) // hotkeys for menu-related things
3442  {
3444  // //
3445  // _____ _ ___ __ __ _____ _ _ _ _ _ ____ _____ ___ __ __ _ _____ ___ ___ _ _ //
3446  // | ___|/ | / _ \ | \/ || ____|| \ | || | | | / \ / ___||_ _||_ _|\ \ / // \ |_ _||_ _|/ _ \ | \ | | //
3447  // | |_ | || | | | | |\/| || _| | \| || | | | / _ \ | | | | | | \ \ / // _ \ | | | || | | || \| | //
3448  // | _| | || |_| | | | | || |___ | |\ || |_| | / ___ \| |___ | | | | \ V // ___ \ | | | || |_| || |\ | //
3449  // |_| |_| \___/ |_| |_||_____||_| \_| \___/ /_/ \_\\____| |_| |___| \_//_/ \_\|_| |___|\___/ |_| \_| //
3450  // //
3451  // //
3453 
3454  // looking for F10 [pops up the menu at the first position] and SHIFT+F10 [context menu for child window]
3455 
3456  if(pEvent->xclient.data.l[0] == XK_F10 && // keystroke is F10
3457  (pEvent->xclient.data.l[1] & WB_KEYEVENT_KEYSYM)) // keystroke is a symbol
3458  {
3459  int iACS = (int)pEvent->xclient.data.l[1] & WB_KEYEVENT_ACSMASK;
3460 
3461  if(iACS == WB_KEYEVENT_SHIFT)
3462  {
3463  // send a command to the focus window in order to pop up the context menu
3464 
3465  return 1; // handled
3466  }
3467  }
3468  }
3469  }
3470 
3471  // TAB BAR (but with no menu)
3472 
3473  if(!(WBFrameWindow_NO_TABS & pFrameWindow->wbFW.iFlags)) // I have a tab bar
3474  {
3475  if(pEvent->type == ButtonPress ||
3476  pEvent->type == ButtonRelease ||
3477  pEvent->type == MotionNotify)
3478  {
3479  if(WBPointInRect(pEvent->xbutton.x, pEvent->xbutton.y, pFrameWindow->rctTabBar) ||
3480  (pFrameWindow->nTabBarButtonFlags & tab_bar_button_GRAB)) // grabbing mouse means I get the event
3481  {
3482  return Internal_Tab_Bar_Event(pFrameWindow, pEvent);
3483  }
3484  }
3485  else if(pEvent->type == Expose)
3486  {
3487  InternalPaintTabBar(pFrameWindow, &(pEvent->xexpose)); // this will 'validate' the tab bar area, preventing re-paint
3488  }
3489  else if(pEvent->type == ClientMessage && // tab-related client messages
3490  pEvent->xclient.message_type == aTAB_MESSAGE)
3491  {
3492 // WB_ERROR_PRINT("TODO: %s - handle ClientMessage for the tab bar\n", __FUNCTION__);
3493 // WBDebugDumpEvent(pEvent);
3494  if(pEvent->xclient.data.l[0] == NEW_TAB_MESSAGE)
3495  {
3496  Display *pDisplay;
3497  XClientMessageEvent evt;
3498 
3499  pDisplay = WBGetWindowDisplay(wID);
3500 
3501  // post a WM_COMMAND ClientMessage event, for 'IDM_FILE_NEW'
3502  // TODO: do I want to pre-define things like this in the header?
3503 
3504  bzero(&evt, sizeof(evt));
3505  evt.type = ClientMessage;
3506 
3507  evt.display = pDisplay;
3508  evt.window = wID;
3509  evt.message_type = aMENU_COMMAND;
3510  evt.format = 32; // always
3511 
3512  evt.data.l[0] = WBGetAtom(pDisplay, FW_FILE_NEW_MENU); // post a 'FILE NEW' menu event so handlers can manage it
3513  evt.data.l[1] = 0; // no hashed pointer this time
3514 
3515  WBPostEvent(wID, (XEvent *)&evt);
3516  }
3517  else if(pEvent->xclient.data.l[0] == PREV_TAB_MESSAGE)
3518  {
3519  if(pFrameWindow->nFocusTab > 0)
3520  {
3521  FWSetFocusWindowIndex(&(pFrameWindow->wbFW), pFrameWindow->nFocusTab - 1);
3522  }
3523  }
3524  else if(pEvent->xclient.data.l[0] == NEXT_TAB_MESSAGE)
3525  {
3526  if((pFrameWindow->nFocusTab + 1) < pFrameWindow->nChildFrames)
3527  {
3528  FWSetFocusWindowIndex(&(pFrameWindow->wbFW), pFrameWindow->nFocusTab + 1);
3529  }
3530  }
3531  else if(pEvent->xclient.data.l[0] == SET_TAB_MESSAGE)
3532  {
3533  if((int)pEvent->xclient.data.l[1] >= 0 &&
3534  (int)pEvent->xclient.data.l[1] < pFrameWindow->nChildFrames)
3535  {
3536  FWSetFocusWindowIndex(&(pFrameWindow->wbFW), (int)pEvent->xclient.data.l[1]);
3537  }
3538  }
3539  else if(pEvent->xclient.data.l[0] == CLOSE_TAB_MESSAGE)
3540  {
3541  if((int)pEvent->xclient.data.l[1] >= 0 &&
3542  (int)pEvent->xclient.data.l[1] < pFrameWindow->nChildFrames)
3543  {
3544  WBChildFrame *pC = pFrameWindow->ppChildFrames[pEvent->xclient.data.l[1]];
3545 
3546  FWRemoveContainedWindow(&(pFrameWindow->wbFW), pC); // remove from frame
3547  FWDestroyChildFrame(pC); // destroys 'child frame' (and superclass, if it assigned a destructor)
3548  }
3549  }
3550 
3551  return 1; // handled
3552  }
3553  }
3554 
3555 
3556  // expose event for status bar - painting the status bar and tab bar.
3557 
3558  if(pEvent->type == Expose &&
3559  (WBFrameWindow_STATUS_BAR & pFrameWindow->wbFW.iFlags))
3560  {
3561  InternalPaintStatusBar(pFrameWindow, &(pEvent->xexpose)); // this will 'validate' the status bar area, preventing re-paint
3562  }
3563 
3564 
3565  // user callback function
3566 
3567  if(pFrameWindow->pFWCallback)
3568  {
3569  // for most messages, if I handle it here, I don't do further processing
3570 
3571  iRval = (pFrameWindow->pFWCallback)(wID, pEvent);
3572 
3573  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Event | DebugSubSystem_Frame,
3574  "%s - %s event and user callback returns %d\n", __FUNCTION__, WBEventName(pEvent->type), iRval);
3575 
3576  if(iRval)
3577  {
3578  // check message types that I do *NOT* want to 'bail out' for, as well as those
3579  // that I _MUST_ bail out for.
3580 
3581  switch(pEvent->type)
3582  {
3583  case DestroyNotify: // must process after user callback
3584  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3585  "%s DestroyNotify and user callback returned a non-zero value\n", __FUNCTION__);
3586 
3587  // CONTINUE PROCESSING - after the user's callback handles DestroyNotify I must handle
3588  // it here also (at the very end)
3589 
3590  break;
3591 
3592  case Expose: // no further processing needed, special debug notification
3593  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3594  "%s Expose event and user callback returns %d\n", __FUNCTION__, iRval);
3595  return iRval; // 'expose' event already handled
3596 
3597 
3598  default: // all other messages, no further processing needed
3599  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3600  "%s - %s event and user callback returns %d\n", __FUNCTION__, WBEventName(pEvent->type), iRval);
3601  return iRval;
3602  }
3603  }
3604  else
3605  {
3606  // TODO: deal with specific events NOT handled by the user callback
3607 
3608  if(pEvent->type == ClientMessage &&
3609  pEvent->xclient.message_type == aQUERY_CLOSE)
3610  {
3611  int i1;
3612  Display *pDisplay = WBGetWindowDisplay(wID);
3613 
3614  // note that 'Query Close' would have been processed already with an "ok to close" result
3615  // SO, check contained windows, and then destroy everything except the window itself
3616  if(pFrameWindow->ppChildFrames && pFrameWindow->nChildFrames > 0)
3617  {
3618  for(i1=0; i1 < pFrameWindow->nChildFrames; i1++)
3619  {
3620  XClientMessageEvent evt;
3621 
3622  bzero(&evt, sizeof(evt));
3623  evt.type = ClientMessage;
3624  evt.display = pDisplay;
3625  evt.window = pFrameWindow->ppChildFrames[i1]->wID;
3626  evt.message_type = aQUERY_CLOSE; // QUERY_CLOSE request
3627  evt.format = 32;
3628  evt.data.l[0] = 0; // "do not close yourself yet"
3629 
3630  if((int)WBWindowDispatch(evt.window, (XEvent *)&evt) > 0) // TODO: handle errors
3631  {
3632  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Event | DebugSubSystem_Frame,
3633  "%s - QUERY CLOSE returning 1 (child refuses to close)\n", __FUNCTION__);
3634 
3635  return 1; // "not ok" to close
3636  }
3637  }
3638  }
3639 
3640 // DLGMessageBox(MessageBox_OK | MessageBox_MiddleFinger, wID, "OK to close", "Checknig if ok to close");
3641 
3642  if(pEvent->xclient.data.l[0]) // meaning "delete your private data if it's ok to close, destroy imminent"
3643  {
3644  // children say "go for it" - so now I do it to myself
3645 
3646  if(pFrameWindow->ppChildFrames && pFrameWindow->nChildFrames > 0)
3647  {
3648  for(i1=pFrameWindow->nChildFrames - 1; i1 >= 0; i1--)
3649  {
3650  WB_ERROR_PRINT("TEMPORARY: %s - destroying child frame %d\n", __FUNCTION__, i1);
3651 
3652  WBChildFrame *pC = pFrameWindow->ppChildFrames[i1];
3653  pC->pOwner = NULL; // so it doesn't try to remove itself
3654 
3655  pFrameWindow->ppChildFrames[i1] = NULL; // so I don't try to do this again
3656  FWDestroyChildFrame(pC);
3657  }
3658 
3659  pFrameWindow->nChildFrames = 0;
3660  }
3661 
3662  // to help deal with the destruction, tell the registered window proc that
3663  // I am destroying the window by sending a 'DestroyNotify' to it.
3664 
3665  if(pFrameWindow->pFWCallback)
3666  {
3667  XDestroyWindowEvent evt;
3668 
3669  bzero(&evt, sizeof(evt));
3670  evt.type = DestroyNotify;
3671  evt.display = pDisplay;
3672  evt.event = evt.window = pFrameWindow->wbFW.wID;
3673 
3674  pFrameWindow->pFWCallback(evt.event, (XEvent *)&evt);
3675  }
3676 
3677  WB_ERROR_PRINT("TEMPORARY: %s - destroying internal data, etc.\n", __FUNCTION__);
3678 
3679  WBSetWindowData(wID, 0, NULL);
3680 
3681  __internal_destroy_frame_window(pFrameWindow); // this destroys the children
3682  WBFree(pFrameWindow);
3683 
3684  // NOTE: caller must unregister the callback, etc.
3685  }
3686 
3687  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3688  "%s - QUERY CLOSE returning zero\n", __FUNCTION__);
3689 
3690  return 0; // OK to close
3691  }
3692  }
3693  }
3694 
3695 
3696  // At this point iRval _COULD_ be non-zero (example, DestroyNotify)
3697  // so don't do any handling for those messages. For everything else,
3698  // a return value of ZERO means "not handled", so handle them.
3699 
3700  if(!iRval)
3701  {
3702  switch(pEvent->type)
3703  {
3704  case KeyPress:
3705  {
3706 #ifndef NO_DEBUG
3707  int iACS = 0;
3708  int iKey = WBKeyEventProcessKey((XKeyEvent *)pEvent, tbuf, &nChar, &iACS);
3709 
3710  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame | DebugSubSystem_Keyboard,
3711  "%s KEY PRESS for KEY %d KEYCODE %d MASK=%d (%xH)\n",
3712  __FUNCTION__, iKey, ((XKeyEvent *)pEvent)->keycode,
3713  ((XKeyEvent *)pEvent)->state, ((XKeyEvent *)pEvent)->state);
3714 #endif // NO_DEBUG
3715 
3716  // check for menu and hotkey activation.
3717 // if(nChar > 0) // only for "real" characters (i.e. not just the ALT key)
3718  {
3720 
3721  if(pMenuBar) // menu bar exists?
3722  {
3723  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Menu | DebugSubSystem_Frame | DebugSubSystem_Keyboard,
3724  "%s call to MBMenuProcessHotKey for menu window %d (%08xH)\n",
3725  __FUNCTION__, (int)pMenuBar->wSelf, (int)pMenuBar->wSelf);
3726 
3727  iRval = MBMenuProcessHotKey(pMenuBar->pMenu, (XKeyEvent *)pEvent);
3728  }
3729  }
3730  }
3731  break;
3732 
3733  case KeyRelease:
3734  {
3735  // KeyRelease
3736 #ifndef NO_DEBUG
3737  int iACS = 0, iKey = WBKeyEventProcessKey((XKeyEvent *)pEvent, tbuf, &nChar, &iACS);
3738 
3739  if(nChar > 0)
3740  {
3741  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame | DebugSubSystem_Keyboard,
3742  "%s KEY RELEASE for KEY %d KEYCODE %d MASK=%d (%xH)\n",
3743  __FUNCTION__, iKey, ((XKeyEvent *)pEvent)->keycode,
3744  ((XKeyEvent *)pEvent)->state, ((XKeyEvent *)pEvent)->state);
3745 
3746  }
3747 #endif // NO_DEBUG
3748 
3749  }
3750 
3751  break;
3752 
3753 
3754 
3755  case SelectionRequest:
3756  case SelectionClear:
3757  case SelectionNotify:
3758 
3759  return 0; // NOT handled (default handler might want to handle them, but not me)
3760 
3761 // return FWDoSelectionEvents(&(pFrameWindow->wbFW), wID, wIDMenu, pEvent); <-- no longer needed
3762  }
3763  }
3764 
3765 
3766  // TODO: message re-direction to children AFTER 'pFWCallback'
3767 
3768 
3769 
3770  // ----------------------------------------
3771  // DESTROY DESTROY DESTROY DESTROY DESTROY
3772  //
3773  // special handling for 'DestroyNotify'
3774  //
3775  // DESTROY DESTROY DESTROY DESTROY DESTROY
3776  // ----------------------------------------
3777 
3778 
3779  if(pEvent->type == DestroyNotify &&
3780  pEvent->xdestroywindow.window == wID)
3781  {
3782 // int boolQuitFlag = (pFrameWindow->wbFW.iFlags & WBFrameWindow_APP_WINDOW) != 0;
3783 
3784  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3785  "%s - DestroyNotify\n", __FUNCTION__);
3786 
3787  WBSetWindowData(wID, 0, NULL);
3788 
3789  __internal_destroy_frame_window(pFrameWindow);
3790 
3791  WBFree(pFrameWindow);
3792 
3793 // if(boolQuitFlag)
3794 // bQuitFlag = TRUE; // set the global 'quit' flag if I'm an application top-level window
3795 
3796  WB_DEBUG_PRINT(DebugLevel_ERROR/*DebugLevel_Verbose | DebugSubSystem_Event | DebugSubSystem_Frame*/,
3797  "%s - frame window destroyed\n", __FUNCTION__);
3798  return 1;
3799  }
3800 
3801  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Event | DebugSubSystem_Frame,
3802  "%s - frame window callback returns %d\n", __FUNCTION__, iRval);
3803 
3804  return iRval; // return back the 'handled' status
3805 }
3806 
3807 
3808 
void * WBGetPointerFromHash(WB_UINT32 uiHash)
Obtain a pointer from a 32-bit 'secure' pointer hash value.
GC gc
the associated 'GC'
void FWReplaceContainedWindow(WBFrameWindow *pFW, WBChildFrame *pCont, WBChildFrame *pContNew)
Replace a 'contained' window from a frame window.
Window wID
Window id for the frame window.
Definition: frame_window.h:277
int WBPointInWindow(Window wIDRef, int iX, int iY, Window wIDQuery)
Returna a non-zero value if X,Y coordinates relative to the reference window are within the query win...
void WBSetWMProperties(Window wID, const char *szTitle, XSizeHints *pNormalHints, XWMHints *pWMHints, XClassHint *pClassHints)
assign standard WM (Window Manager) properties via XSetWMProperties
structure for managing menu items
Definition: menu.h:127
#define WBRectOverlapped(R1, R2)
Returns logical TRUE if the rectangle R1 overlaps/intersects R2.
static __inline__ XStandardColormap * PXM_StandardColormapFromColormap(Display *pDisplay, Colormap colormap)
create temporary XStandardColormap from a Colormap
Atom aMENU_UI_COMMAND
UI notifications sent by menus to owning Frame windows via ClientMessage using 'WBWindowDispatch'.
void PXM_RGBToYUV(int iR, int iG, int iB, int *piY, int *piU, int *piV)
Convert R, G, B values to Y, U, V with 0-255 range.
set this to disable tabs (single child frame only)
Definition: frame_window.h:300
'window helper' main header file for the X11workbench Toolkit API
void WBFreeFont(Display *pDisplay, WB_FONT pFont)
free a WB_FONT that was created using one of the WBFont APIs
Definition: font_helper.c:250
void FWRemoveContainedWindow(WBFrameWindow *pFW, WBChildFrame *pCont)
Removes a 'contained' window from a frame window. Does not destroy the 'contained' window.
void WBClearWindow(Window wID, WBGC gc)
'Paint' helper, erases background by painting the background color within the clipping region
#define WB_DEBUG_PRINT(L,...)
Preferred method of implementing conditional debug output.
Definition: debug_helper.h:364
set this flag for application top-level window and whenever it is destroyed the application will exit
Definition: frame_window.h:298
int WBFontAscent(WB_FONTC pFont0)
Get the maximum character ascent from a WB_FONT.
Definition: font_helper.c:495
Utilities for copying and drawing text, determining text extents, and so on.
void FWDestroyFrameWindow2(WBFrameWindow *pFrameWindow)
Function to destroy a frame window based on the WBFrameWindow structure.
Definition: frame_window.c:749
structure for defining a menu bar window
Definition: menu_bar.h:113
#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:942
bold font weight (mutually exclusive0
Definition: font_helper.h:511
int WBKeyEventProcessKey(const XKeyEvent *pEvent, char *pBuf, int *pcbLen, int *piAltCtrlShift)
Generic keyboard event translation utility.
int WBUnmapWindow(Display *pDisplay, Window wID)
wrapper for XUnmapWindow, makes window invisible without destroying it
#define WBPointInRect(X, Y, R)
Returns logical TRUE if the point (X,Y) is within the borders of the rectangle 'R'.
int WBFontAvgCharWidth(WB_FONTC pFont0)
Get the average character width for a font.
Definition: font_helper.c:343
#define FW_FILE_NEW_MENU
Definition: child_frame.h:105
void FWDestroyFrameWindow(Window wID)
Function to destroy a frame window based on the Window id.
Definition: frame_window.c:744
void WBCreateWindowDefaultGC(Window wID, unsigned long clrFG, unsigned long clrBG)
creates a default WBGC for a window
void WBGetWindowRect(Window wID, WB_RECT *pRect)
Returns the WB_RECT (rectangle) defined by the window's geometry, including the border area.
void WBInitWindowAttributes(XSetWindowAttributes *pXSWA, unsigned long lBorderPixel, unsigned long lBackgroundPixel, Colormap clrMap, int iBitGravity)
initializes the XSetWIndowAttributes structure with minimal attributes
void PXM_YUVToRGB(int iY, int iU, int iV, int *piR, int *piG, int *piB)
Convert Y, U, V values to R, G, B with 0-255 range.
WBFWMenuHandler * pMenuHandler
menu handler for this child frame's menu (NULL = 'use default')
Definition: frame_window.h:455
structure for managing menu callbacks
Definition: frame_window.h:217
int FWGetChildFrameIndex(WBFrameWindow *pFW, WBChildFrame *pCont)
Sets the focus to a specific contained window using its tab order index.
WB_FONT WBCopyFont(Display *pDisplay, WB_FONTC pOldFont)
make a copy of an existing font (best when assigning to a window)
Definition: font_helper.c:168
void MBDestroyMenuBarWindow(WBMenuBarWindow *pMenuBar)
Destroy a 'Menu Bar' window.
Definition: menu_bar.c:511
Window wID
window identifier for the 'Child Frame' window. may contain 'None' while being destroyed
Definition: frame_window.h:429
#define FRAME_WINDOW_TAG
TAG for the WBFrameWindow structure.
Definition: frame_window.h:182
unsigned int border
unsigned int width
const char * CHGetStaticBackgroundColor(Display *pDisplay)
returns background color for static elements
Definition: conf_help.c:3288
structure for managing menu items
Definition: menu.h:185
mask for the 'justification' value
Definition: frame_window.h:315
static __inline__ WBFrameWindow * FWGetFrameWindowStruct(Window wID)
Obtain the associated WBFrameWindow structure pointer for a frame window's Window ID.
Definition: frame_window.h:835
int WBWindowDispatch(Window wID, XEvent *pEvent)
Dispatches a window XEvent. May be called directly.
void MBSetMenuBarMenuResource(WBMenuBarWindow *pMenuBar, const char *pszResource)
Assign a new WBMenu for a Menu Bar window.
Definition: menu_bar.c:530
const char * WBEventName(int iEventID)
debug function to return the name of an X11 event
WB_FONT WBCopyModifyFont(Display *pDisplay, WB_FONTC pOriginal, int iFontSize, int iFlags)
load and modify a font according to the specified size and flags
Definition: font_helper.c:660
WB_FONTC FWGetFont(WBFrameWindow *pFW)
Get the frame window WB_FONTC.
Definition: frame_window.c:445
Atom aWM_DELETE_WINDOW
Delete Window notification event.
horizontally centered text. tabs are treated as white space
Definition: draw_text.h:78
void WBDrawLeftArrow(Display *pDisplay, Drawable wID, WBGC gc, WB_GEOM *pgeomRect, unsigned long lColor)
Draw a left arrow in a window within a specified geometry.
void FWSetMenuHandlers(WBFrameWindow *pFW, const WBFWMenuHandler *pHandlerArray)
Function to assign the default menu handler to a frame window.
Definition: frame_window.c:821
int WBPostPriorityEvent(Window wID, XEvent *pEvent)
Places a copy of the specified event at the end of the priority (internal) event queue.
WB_FONTC FWGetBoldFont(WBFrameWindow *pFW)
Get the frame window bold WB_FONTC.
Definition: frame_window.c:462
Atom aWB_CHAR
keystroke/character notifications generated by API
void WBGetClientRect(Window wID, WB_RECT *pRect)
Returns the WB_RECT (rectangle) defined by the window's geometry, excluding the border area.
static __inline__ Display * WBGetDefaultDisplay(void)
Returns the default Display.
internal wrapper struct for X11 'geometry' definition
'configuration helper' main header file for the X11 Work Bench Toolkit API
void PXM_RGBToPixel(XStandardColormap *pMap, XColor *pColor)
Icon Registration for application 'large' and 'small' icons.
#define RGB255_TO_XCOLOR(R, G, B, X)
Simple RGB assignment to pixel, 0-255 RGB.
Definition: pixmap_helper.h:94
set this to make window immediately visible
Definition: frame_window.h:299
void FWChildFrameStatusChanged(WBChildFrame *pChildFrame)
Notify Child Frame to update status text in Frame Window.
Definition: child_frame.c:897
void FWRecalcLayout(Window wID)
Force a frame window to recalculate its layout, which may involve resizing multiple contained windows...
void PXM_PixelToRGB(XStandardColormap *pMap, XColor *pColor)
Convert the pixel menber of an XColor to RGB.
void DTDrawSingleLineText(WB_FONTC pFont, const char *szText, Display *pDisplay, WBGC gc, Drawable dw, int iTabWidth, int iTabOrigin, const WB_RECT *prcBounds, int iAlignment)
draw single-line text
Definition: draw_text.c:1531
uintptr_t lMenuID
menu ID (< 0x10000L) or const pointer to string
Definition: frame_window.h:219
void * WBReAlloc(void *pBuf, int nNewSize)
High performance memory sub-allocator 're-allocate'.
int WBSetForeground(WBGC hGC, unsigned long foreground)
Assign foreground color, a wrapper for XSetForeground()
XColor FWGetDefaultFG(void)
Get the default foreground color.
Definition: frame_window.c:424
#define COPY_COLOR_NAME(X, Y, Z)
macro to get a color name or use default if it does not exist in settings
#define WBGetWindowCopyGC2(wID, gcSrc)
makes a copy of the specified WBGC for the desired window
Atom aMENU_COMMAND
commands sent by menus via ClientMessage
void FWSetFocusWindow(WBFrameWindow *pFW, WBChildFrame *pCont)
Sets the focus to a specific contained window using the Window ID.
#define RGB255_FROM_XCOLOR(X, R, G, B)
Simple RGB assignment from pixel, 0-255 RGB.
void WBInvalidateGeom(Window wID, const WB_GEOM *pGeom, int bPaintNow)
'Paint' helper, invalidates a geometry for asynchronous Expose event generation
void FWChildFrameRecalcLayout(WBChildFrame *pChildFrame)
Child frame notification callback (called by frame window)
Definition: child_frame.c:845
XColor FWGetDefaultBD(void)
Get the default border color.
Definition: frame_window.c:438
int WBDrawLines(Display *display, Drawable d, WBGC gc, XPoint *points, int npoints, int mode)
Wrapper for XDrawLine()
Atom aTAB_MESSAGE
command sent by Client Message related to 'tab' operations
Definition: frame_window.c:191
Child Frame API functions.
void WBDrawRightArrow(Display *pDisplay, Drawable wID, WBGC gc, WB_GEOM *pgeomRect, unsigned long lColor)
Draw a right arrow in a window within a specified geometry.
Region WBRectToRegion(const WB_RECT *pRect)
'Paint' helper, converts a WB_RECT structure to a Region.
void WBSetApplicationWindow(Window wID)
Assign the main 'Appklication' window.
center using entire text height (ascent + descent for single line)
Definition: draw_text.h:86
const char * CHGetTextColor(Display *pDisplay)
returns text color
Definition: conf_help.c:3399
WBMenuBarWindow * MBCreateMenuBarWindow(Window wIDParent, const char *pszResource, int iFlags)
Create a Menu Bar windows and its associated WBMenuBarWindow structure.
Definition: menu_bar.c:343
void * WBAlloc(int nSize)
High performance memory sub-allocator 'allocate'.
int FWAddContainedWindow(WBFrameWindow *pFW, WBChildFrame *pNew)
Adds a 'contained' window and returns the tab order index.
Window WBGetParentWindow(Window wID)
Returns the window's parent (or None if there is no parent)
#define END_XCALL_DEBUG_WRAPPER
wrapper macro for calls into the X11 library. This macro follows the call(s)
static __inline__ WBMenuBarWindow * MBGetMenuBarWindowStruct(Window wID)
Obtain a pointer to the WBMenuBarWindow structure from a Window ID of a Menu Bar window.
Definition: menu_bar.h:191
void FWSetFocusWindowIndex(WBFrameWindow *pFW, int iIndex)
Sets the focus to a specific contained window using its tab order index.
int FWGetNumContWindows(const WBFrameWindow *pFW)
Returns the total number of 'contained' windows.
void FWSetUserCallback(WBFrameWindow *pFW, WBWinEvent pCallBack)
assign a new WBWinEvent callback function for a frame window
Definition: frame_window.c:799
int WBSetRegion(WBGC hGC, Region rgnClip)
Assign clipping region, wrapper for XSetRegion()
Window WBGetMenuWindow(Window wID)
Returns the Window ID of the (first) menu window assigned to a (frame) window.
const char * CHGetHighlightBackgroundColor(Display *pDisplay)
returns highlight background color
Definition: conf_help.c:3425
#define WB_ERROR_PRINT(...)
Preferred method of implementing an 'error level' debug message for all subsystems.
Definition: debug_helper.h:474
void WBFree(void *pBuf)
High performance memory sub-allocator 'free'.
static __inline__ void WBInvalidateRect(Window wID, const WB_RECT *pRCT, int bPaintFlag)
'Paint' helper, invalidates a WB_RECT for asynchronous Expose event generation
int WBPostEvent(Window wID, XEvent *pEvent)
Places a copy of the specified event at the end of the regular (internal) event queue.
void WBDestroyWindow(Window wID)
Destroy a window.
represents a 'break' (marks end of a column)
Definition: frame_window.h:314
#define WB_KEYEVENT_SHIFT
'AltCtrlShift' bit flag for Shift modifier for WBKeyEventProcessKey()
int(* UIcallback)(WBMenu *, WBMenuItem *)
menu 'UI' callback to handle displaying menu states.
Definition: frame_window.h:228
mask for the actual tab value
Definition: frame_window.h:313
#define WB_MOUSE_INPUT_MASK
'Mouse' input mask, bit flag for window creation
void FWSetStatusTabInfo(WBFrameWindow *pFW, int nTabs, const int *pTabs)
Sets the 'status' tab info for a Frame Window with a status bar.
void WBUpdateWindow(Window wID)
'Paint' helper, generates an asynchronous Expose event for non-empty 'invalid' region
void WBSetWindowIcon(Window wID, int idIcon)
assigns an icon resource (by ID) to a window
void FWDestroyChildFrame(WBChildFrame *pChildFrame)
Destroy an Child Frame.
Definition: child_frame.c:329
Atom aMENU_ACTIVATE
Internal Client Message Atom for 'ACTIVATE' notification.
Definition: menu_bar.c:150
Window WBCreateWindow(Display *pDisplay, Window wIDParent, WBWinEvent pProc, const char *szClass, int iX, int iY, int iWidth, int iHeight, int iBorder, int iIO, WB_UINT64 iFlags, XSetWindowAttributes *pXSWA)
Create a window.
Window wSelf
The window ID of the menu bar window.
Definition: menu_bar.h:116
Atom WBGetAtom(Display *pDisplay, const char *szAtomName)
Lookup and/or allocate an internal Atom for a named string (lookups include X11 atoms)
void WBDispatch(XEvent *pEvent)
Generic Event Dispatcher, using message type to dispatch.
#define WB_STANDARD_INPUT_MASK
'Standard' input mask, bit flag for window creation
#define WB_KEYEVENT_ACSMASK
'AltCtrlShift' bit mask for Alt+Ctrl+Shift bits for WBKeyEventProcessKey()
int(* WBWinEvent)(Window wID, XEvent *pEvent)
event callback function type for window events
Atom aWM_TAKE_FOCUS
'Take Focus' - TODO document this properly
XColor FWGetDefaultBG(void)
Get the default background color.
Definition: frame_window.c:431
main controlling structure for frame windows
Definition: frame_window.h:274
bit flag to 'right justify' the column location
Definition: frame_window.h:320
unsigned int height
internal wrapper struct for 'rectangle' definition
int WBFontDescent(WB_FONTC pFont0)
Get the maximum character descent from a WB_FONT.
Definition: font_helper.c:443
int MBMenuProcessHotKey(WBMenu *pMenu, XKeyEvent *pEvent)
Event handler for menu hotkeys.
Definition: menu.c:1228
Atom aQUERY_CLOSE
query if it's ok to close (and optionally destroy yourself if ok) a window
int(* callback)(XClientMessageEvent *)
menu callback (gets pointer to the 'XClientMessageEvent').
Definition: frame_window.h:221
WBFrameWindow * pOwner
a pointer to the WBFrameWindow owner
Definition: frame_window.h:430
WBChildFrame * FWGetContainedWindowByIndex(const WBFrameWindow *pFW, int iIndex)
Returns the Window ID for the specified 'contained' window. The index follows the tab order.
Display * WBGetWindowDisplay(Window wID)
returns the Display associated with a window
void WBFreeGC(WBGC hGC)
Free resources for a WBGC, wrapper for XFreeGC()
void WBSetWindowData(Window wID, int iIndex, void *pData)
assign 'data pointer' for a window and specified index value
void WBSetWMProtocols(Window wID, Atom aProperty,...)
re-assign standard WM (Window Manager) 'window type' properties and notify the root window (reserved)
void WBDraw3DBorderRect(Display *pDisplay, Drawable wID, WBGC gc, WB_GEOM *pgeomBorder, unsigned long lBorderColor1, unsigned long lBorderColor2)
Draw a 3D 'border' rectangle.
unsigned int ulTag
tag indicating I'm a frame window
Definition: frame_window.h:276
#define BEGIN_XCALL_DEBUG_WRAPPER
wrapper macro for calls into the X11 library. This macro precedes the call(s)
void WBSetInputFocus(Window wID)
set input focus to a specific window
int WBFillRectangle(Display *display, Drawable d, WBGC gc, int x, int y, unsigned int width, unsigned int height)
Wrapper for XFillRectangle()
char * WBGetAtomName(Display *pDisplay, Atom aAtom)
Lookup and/or allocate an internal Atom for a named string.
WBMenu * pMenu
a pointer to the associated WBMenu structure
Definition: menu_bar.h:119
An allocated structure containing XFontStruct, XFontInfo, and XftFont [as applicable] for a specified...
Definition: font_helper.h:152
left-justified text
Definition: draw_text.h:77
char * WBCopyString(const char *pSrc)
A simple utility that returns a WBAlloc() copy of a 0-byte terminated string.
#define WB_KEYBOARD_INPUT_MASK
'Keyboard' input mask, bit flag for window creation
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:508
set this to enable a 'status bar' at the bottom
Definition: frame_window.h:301
void WBEndPaint(Window wID, WBGC gc)
'Paint' helper, frees resources and marks the update region 'valid'
internal wrapper struct for GC with local cache
Structure that defines a Child Frame within a Frame Window.
Definition: frame_window.h:426
Atom aRESIZE_NOTIFY
notification of window re-size via ClientMessage
Atom aSET_FOCUS
dialog focus messages
void FWSetStatusText(WBFrameWindow *pFW, const char *szText)
Sets the 'status' text for a Frame Window with a status bar, forcing a re-paint.
const char * CHGetBorderColor(Display *pDisplay)
returns border color
Definition: conf_help.c:3279
int WBMapWindow(Display *pDisplay, Window wID)
Wrapper for XMapWindow, makes window visible.
Frame Window API functions and definitions.
void FWMoveChildFrameTabIndex(WBFrameWindow *pFW, WBChildFrame *pCont, int iIndex)
Sets the specific contained window to a particular index in the tab order.
Atom aImageAtom
'image' atom for display in tabs. default is 'None'. You should not alter this member directly.
Definition: frame_window.h:450
char * szDisplayName
display name shown in tab and title bar. You should not alter this member directly.
Definition: frame_window.h:449
#define WB_WARN_PRINT(...)
Preferred method of implementing a 'warning level' debug message for all subsystems.
Definition: debug_helper.h:467
void WBDraw3DBorderTab(Display *pDisplay, Drawable dw, WBGC gc, WB_GEOM *pgeomOutline, int fFocus, unsigned long lFGColor, unsigned long lBGColor, unsigned long lBorderColor1, unsigned long lBorderColor2, unsigned long lHighlightColor, WB_FONTC pFont, WB_FONTC pFontBold, Atom aGraphic, const char *szText)
Draw a 'tab' within a specified 'outline' rectangle.
char * pszMenuResource
resource string for this child frame's menu (NULL = 'use default')
Definition: frame_window.h:454
void WBRemoveMenuWindow(Window wID, Window wIDMenu)
Remove (detach) the specified menu window from a (frame) window.
WBGC WBBeginPaintGeom(Window wID, WB_GEOM *pgBounds)
'Paint' helper, creates a WBGC for use in updating the window for a specified rectangular area
#define WB_KEYEVENT_KEYSYM
'AltCtrlShift' bit flag for 'VK_' keys for WBKeyEventProcessKey()
WB_FONTC WBGetDefaultFont(void)
Returns a pointer to the default font WB_FONT for the default display. This is a shared resource; do ...