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