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