X11 Work Bench Toolkit  1.0
frame_window.c
Go to the documentation of this file.
1 // __ _ _ //
3 // / _| _ __ __ _ _ __ ___ ___ __ __(_) _ __ __| | ___ __ __ ___ //
4 // | |_ | '__|/ _` || '_ ` _ \ / _ \ \ \ /\ / /| || '_ \ / _` | / _ \\ \ /\ / / / __| //
5 // | _|| | | (_| || | | | | || __/ \ V V / | || | | || (_| || (_) |\ V V /_| (__ //
6 // |_| |_| \__,_||_| |_| |_| \___|_____\_/\_/ |_||_| |_| \__,_| \___/ \_/\_/(_)\___| //
7 // |_____| //
8 // //
9 // a top-level window that can have menus, toolbars, a status bar, and a client area //
10 // //
12 
13 /*****************************************************************************
14 
15  X11workbench - X11 programmer's 'work bench' application and toolkit
16  Copyright (c) 2010-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  {
213  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Frame,
214  "%s - Destroy contents %d\n",
215  __FUNCTION__, i1 + 1);
216 
217  WBChildFrame *pC = pTemp->ppChildFrames[i1];
218 
219  if(pC)
220  {
221  pC->pOwner = NULL; // so it doesn't try to remove itself
222 
223  pTemp->ppChildFrames[i1] = NULL; // so I don't try to do this again
224  FWDestroyChildFrame(pC); // should validate 'pC' before destroying
225  }
226  }
227 
228  WBFree(pTemp->ppChildFrames);
229 
230  pTemp->ppChildFrames = NULL;
231  pTemp->nChildFrames = 0; // by convention
232  pTemp->nMaxChildFrames = 0; // this too
233  }
234 
235  // destroy the menu bar window and menu
236  wIDMenu = WBGetMenuWindow(pTemp->wbFW.wID);
237 
238  if(wIDMenu != None)
239  {
240  WBRemoveMenuWindow(pTemp->wbFW.wID, wIDMenu); // this should destroy it if no references
241 
242  pMB = MBGetMenuBarWindowStruct(wIDMenu);
243  if(pMB)
244  {
246  }
247  }
248 
249  if(pTemp->szTitle)
250  {
251  WBFree(pTemp->szTitle);
252  pTemp->szTitle = NULL;
253  }
254 
255  if(pTemp->szStatus)
256  {
257  WBFree(pTemp->szStatus);
258  pTemp->szStatus = NULL;
259  }
260 
261  if(pTemp->pStatusBarTabs)
262  {
263  WBFree(pTemp->pStatusBarTabs);
264  pTemp->pStatusBarTabs = NULL;
265  }
266 
267  if(pTemp->pDefMenuHandler)
268  {
269  WBFree(pTemp->pDefMenuHandler);
270  pTemp->pDefMenuHandler = NULL;
271  }
272 
273  if(pTemp->pDefaultMenuResource)
274  {
275  WBFree(pTemp->pDefaultMenuResource);
276  pTemp->pDefaultMenuResource = NULL;
277  }
278 
279  if(pTemp->pFont)
280  {
281  XFreeFont(pDisplay, pTemp->pFont);
282  pTemp->pFont = NULL;
283  }
284 
285  if(pTemp->pBoldFont)
286  {
287  XFreeFont(pDisplay, pTemp->pBoldFont);
288  pTemp->pBoldFont = NULL;
289  }
290 
291  if(pTemp->fontSet != None)
292  {
293  XFreeFontSet(pDisplay, pTemp->fontSet);
294  pTemp->fontSet = None;
295  }
296 
297  if(pTemp->fontSetBold != None)
298  {
299  XFreeFontSet(pDisplay, pTemp->fontSetBold);
300  pTemp->fontSetBold = None;
301  }
302 }
303 
304 void WBFrameWindowExit()
305 {
306  // destroy all of the frame windows
307 
308  while(pFrames)
309  {
310  FRAME_WINDOW *pTemp = pFrames;
311  pFrames = pFrames->pNext;
312 
313  // slightly different - free the structure first, THEN destroy the window
314  WBSetWindowData(pTemp->wbFW.wID, 0, NULL);
315  __internal_destroy_frame_window(pTemp);
316  WBFree(pTemp);
317 
318  WBDestroyWindow(pTemp->wbFW.wID); // making sure...
319  }
320 
321 }
322 
323 static void InternalCheckFWAtoms(void)
324 {
325 Display *pDisplay = WBGetDefaultDisplay();
326 
327 
328  if(aTAB_MESSAGE == None)
329  {
330  aTAB_MESSAGE = WBGetAtom(pDisplay, "FW_TAB_MESSAGE");
331 
332  if(aTAB_MESSAGE == None)
333  {
334  WB_ERROR_PRINT("ERROR: %s - unable to initialize atom(s)\n", __FUNCTION__);
335  }
336  }
337 }
338 
339 
340 #define LOAD_COLOR0(X,Y) if(CHGetResourceString(WBGetDefaultDisplay(), X, Y, sizeof(Y)) > 0) { }
341 #define LOAD_COLOR(X,Y,Z) if(CHGetResourceString(WBGetDefaultDisplay(), X, Y, sizeof(Y)) <= 0){ WB_WARN_PRINT("%s - WARNING: can't find color %s, using default value %s\n", __FUNCTION__, X, Z); strcpy(Y,Z); }
342 
343 static void InternalCheckFWColors(void)
344 {
345  Colormap colormap;
346 
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
1154  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
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  if(pFrameWindow->ppChildFrames) // only if I have an array of child frames do I do the message
1250  {
1251  WB_ERROR_PRINT("ERROR: %s - bad index %d - %d frames, array=%p\n",
1252  __FUNCTION__, iIndex, pFrameWindow->nChildFrames, pFrameWindow->ppChildFrames);
1253  }
1254  else
1255  {
1256  WB_DEBUG_PRINT(DebugLevel_Medium, "ERROR: %s - bad index %d - %d frames, array=%p\n",
1257  __FUNCTION__, iIndex, pFrameWindow->nChildFrames, pFrameWindow->ppChildFrames);
1258  }
1259 
1260  return NULL;
1261  }
1262 
1263  return pFrameWindow->ppChildFrames[iIndex];
1264 }
1265 
1267 {
1268 FRAME_WINDOW *pFrameWindow;
1269 int iRval;
1270 
1271 
1272  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1273 
1274  if(!pFrameWindow)
1275  {
1276  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
1277 
1278  return -1;
1279  }
1280 
1281  if(!pFrameWindow->ppChildFrames)
1282  {
1283  pFrameWindow->nMaxChildFrames = 256; // for now
1284 
1285  pFrameWindow->ppChildFrames = (WBChildFrame **)WBAlloc(pFrameWindow->nMaxChildFrames * sizeof(WBChildFrame *));
1286  if(!pFrameWindow->ppChildFrames)
1287  {
1288  pFrameWindow->nMaxChildFrames = 0;
1289 
1290 no_memory:
1291  WB_ERROR_PRINT("ERROR: %s - not enough memory\n", __FUNCTION__);
1292 
1293  return -1;
1294  }
1295 
1296  pFrameWindow->nChildFrames = 0; // initially
1297  }
1298  else if((pFrameWindow->nChildFrames + 1) >= pFrameWindow->nMaxChildFrames)
1299  {
1300  void *pTemp = WBReAlloc(pFrameWindow->ppChildFrames,
1301  (pFrameWindow->nMaxChildFrames + 128) * sizeof(WBChildFrame *));
1302 
1303  if(!pTemp)
1304  {
1305  goto no_memory;
1306  }
1307 
1308  pFrameWindow->ppChildFrames = (WBChildFrame **)pTemp; // re-assign new pointer
1309 
1310  pFrameWindow->nMaxChildFrames += 128;
1311  }
1312 
1313  // ADD NEW CHILD FRAME TO THE END [TODO insert just past current focus window instead?]
1314 
1315  iRval = pFrameWindow->nChildFrames; // always add to end of list.
1316  (pFrameWindow->nChildFrames)++;
1317  pFrameWindow->ppChildFrames[iRval] = pNew;
1318 
1319  pNew->pOwner = &(pFrameWindow->wbFW); // set THIS frame as the owner in the contained tab
1320 
1321 // WB_ERROR_PRINT("TEMPORARY: %s - adding tab %d window %u (%08xH)\n",
1322 // __FUNCTION__, iRval, (int)pNew->wID, (int)pNew->wID);
1323 
1324  FWRecalcLayout(pFW->wID); // recalc layout for frame window, first (in case it needs it)
1325  FWChildFrameRecalcLayout(pNew); // make sure I recalculate the child frame layout based on the owner
1326 
1327  FWSetFocusWindowIndex(pFW, iRval); // set focus to it
1328 
1329  return iRval;
1330 }
1331 
1333 {
1334 FRAME_WINDOW *pFrameWindow;
1335 int iIndex, i2;
1336 
1337  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1338 
1339  if(!pFrameWindow || !pCont)
1340  {
1341  WB_ERROR_PRINT("ERROR: %s - no frame window pointer or invalid child window pointer!\n", __FUNCTION__);
1342 
1343  return;
1344  }
1345 
1346  if(!pFrameWindow->ppChildFrames || !pFrameWindow->nChildFrames)
1347  {
1348  return; // no child windows in list
1349  }
1350 
1351  for(iIndex=0; iIndex < pFrameWindow->nChildFrames; iIndex++)
1352  {
1353  if(pFrameWindow->ppChildFrames[iIndex] == pCont)
1354  {
1355 #ifndef NO_DEBUG
1356  if(pCont->pOwner != &(pFrameWindow->wbFW))
1357  {
1358  WB_ERROR_PRINT("ERROR: %s - tab's owner %p does not match this frame window (%p)!\n",
1359  __FUNCTION__, (void *)pCont->pOwner, (void *)&(pFrameWindow->wbFW));
1360  }
1361 #endif // NO_DEBUG
1362 
1363  if(pCont->wID != None)
1364  {
1365  WBUnmapWindow(WBGetWindowDisplay(pCont->wID), pCont->wID); // unmap it (make it invisible) [MUST do this]
1366  }
1367 
1368  pCont->pOwner = NULL; // for now, always do this. it prevents trying to re-remove it, etc.
1369 
1370  for(i2=iIndex + 1; i2 < pFrameWindow->nChildFrames; i2++)
1371  {
1372  pFrameWindow->ppChildFrames[i2 - 1] = pFrameWindow->ppChildFrames[i2]; // slide 'em up
1373  }
1374 
1375  pFrameWindow->nChildFrames --;
1376  pFrameWindow->ppChildFrames[pFrameWindow->nChildFrames] = NULL; // by convention, to ensure no pointer re-use
1377 
1378  if(pFrameWindow->nFocusTab >= pFrameWindow->nChildFrames)
1379  {
1380  pFrameWindow->nFocusTab = pFrameWindow->nChildFrames - 1; // focus to last tab if I deleted the last tab
1381  }
1382  else if(pFrameWindow->nFocusTab > iIndex) // was the tab in focus to the right of the one I'm removing?
1383  {
1384  // decrement the tab index, since I deleted something to the left of it.
1385  pFrameWindow->nFocusTab--; // result won't be negative, because of '>' in 'if'
1386  }
1387 
1388  // invalidate the tab bar rectangle first, before I continue
1389  WBInvalidateRect(pFW->wID, &(pFrameWindow->rctLastTabBarRect), 0);
1390 
1391  if(pFrameWindow->nChildFrames <= 0)
1392  {
1393  // switch focus menu to 'default'. there are NO tabs left.
1394 
1395  Window wMB = WBGetMenuWindow(pFrameWindow->wbFW.wID);
1396 
1397  if(wMB != None)
1398  {
1400 
1401  if(pMBW)
1402  {
1403  MBSetMenuBarMenuResource(pMBW, pFrameWindow->pDefaultMenuResource);
1404  }
1405  }
1406 
1407  pFrameWindow->pMenuHandler = pFrameWindow->pDefMenuHandler; // default menu handler (must assign, so no free'd pointer usage)
1408 
1409  pFrameWindow->nChildFrames = 0; // make sure
1410 
1411  FWSetStatusText(pFW, NULL); // remove status text
1412 
1413  FWRecalcLayout(pFW->wID); // recalculate layout (this also updates the frame window and whatnot)
1414  WBInvalidateRect(pFW->wID, NULL, 1); // update everything NOW. it's 'empty'
1415  }
1416  else if(iIndex == pFrameWindow->nFocusTab) // is it the focus window? (not always)
1417  {
1418  FWRecalcLayout(pFW->wID); // recalculate layout (this also updates the frame window and whatnot)
1419 
1420  // focus window changes, so make sure everything is informed
1421 
1422  if(iIndex < pFrameWindow->nChildFrames)
1423  {
1424  FWSetFocusWindowIndex(pFW, iIndex); // note: I just deleted 'iIndex', but the *NEW* 'iIndex' has the focus now
1425  }
1426  else
1427  {
1428  // in this case, I'm deleting the last window in the bunch, so set focus to the last one
1429  FWSetFocusWindowIndex(pFW, pFrameWindow->nChildFrames - 1);
1430  }
1431  }
1432  else
1433  {
1434  // if I'm displaying a different window in the tab, just re-calc the layout
1435 
1436  FWRecalcLayout(pFW->wID); // recalculate layout (this also updates the frame window and whatnot)
1437  FWSetFocusWindowIndex(pFW, pFrameWindow->nFocusTab); // re-assign focus to same, fixes certain problems
1438  }
1439 
1440  return;
1441  }
1442  }
1443 }
1444 
1446 {
1447 FRAME_WINDOW *pFrameWindow;
1448 int iIndex;
1449 
1450 
1451  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1452 
1453  if(!pFrameWindow || !pCont || !pContNew)
1454  {
1455  WB_ERROR_PRINT("ERROR: %s - no frame window pointer or invalid child frame pointer!\n", __FUNCTION__);
1456 
1457  return;
1458  }
1459 
1460  if(!pFrameWindow->ppChildFrames || !pFrameWindow->nChildFrames)
1461  {
1462  // TODO: call 'Add' ?
1463 
1464  return; // no child windows in list
1465  }
1466 
1467  for(iIndex=0; iIndex < pFrameWindow->nChildFrames; iIndex++)
1468  {
1469  if(pFrameWindow->ppChildFrames[iIndex] == pCont)
1470  {
1471  if(pCont->wID != None)
1472  {
1473  WBUnmapWindow(WBGetWindowDisplay(pCont->wID), pCont->wID); // unmap it (make it invisible) [MUST do this]
1474  }
1475 
1476  pCont->pOwner = NULL; // for now, always do this. it prevents trying to re-remove it, etc.
1477 
1478  // now add the NEW window.
1479 
1480  pFrameWindow->ppChildFrames[iIndex] = pContNew;
1481 
1482  pContNew->pOwner = &(pFrameWindow->wbFW); // set THIS frame as the owner in the contained tab
1483 
1484  FWRecalcLayout(pFW->wID); // recalc layout for frame window, first (in case it needs it)
1485  FWChildFrameRecalcLayout(pContNew); // make sure I recalculate the layout based on the owner
1486 
1487 // WB_ERROR_PRINT("TEMPORARY: %s - replacing tab %d, window %u (%08xH)\n",
1488 // __FUNCTION__, iIndex, (int)pContNew->wID, (int)pContNew->wID);
1489 
1490  if(iIndex == pFrameWindow->nFocusTab) // is it the focus window? (not always)
1491  {
1492  FWSetFocusWindowIndex(pFW, iIndex); // this handles the change-out properly
1493  }
1494  }
1495  }
1496 }
1497 
1498 
1500 {
1501 FRAME_WINDOW *pFrameWindow;
1502 int iIndex;
1503 
1504  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1505 
1506  if(!pFrameWindow)
1507  {
1508  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
1509 
1510  return;
1511  }
1512 
1513  // locate the contained window from my list. then set as 'focus tab'
1514 
1515  if(!pFrameWindow->ppChildFrames || !pFrameWindow->nChildFrames)
1516  {
1517  return; // no child windows in list
1518  }
1519 
1520  for(iIndex=0; iIndex < pFrameWindow->nChildFrames; iIndex++)
1521  {
1522  if(pFrameWindow->ppChildFrames[iIndex] == pCont)
1523  {
1524  FWSetFocusWindowIndex(pFW, iIndex);
1525  return;
1526  }
1527  }
1528 }
1529 
1530 void FWSetFocusWindowIndex(WBFrameWindow *pFW, int iIndex)
1531 {
1532 FRAME_WINDOW *pFrameWindow;
1533 WBChildFrame *pC;
1534 int i1;
1535 Display *pDisplay = WBGetWindowDisplay(pFW->wID);
1536 Window wMB;
1537 WBMenuBarWindow *pMBW;
1538 
1539 
1540  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1541 
1542  if(!pFrameWindow)
1543  {
1544  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
1545 
1546  return;
1547  }
1548 
1549  // locate the contained window from my list. then set as 'focus tab'
1550 
1551  if(!pFrameWindow->ppChildFrames || !pFrameWindow->nChildFrames)
1552  {
1553  return; // no child windows in list, or index is greater than the maximum
1554  }
1555 
1556  if(iIndex >= pFrameWindow->nChildFrames)
1557  {
1558  iIndex = pFrameWindow->nChildFrames - 1; // a simple fix
1559  }
1560  else if(iIndex < 0)
1561  {
1562  iIndex = 0;
1563  }
1564 
1565  pFrameWindow->nFocusTab = iIndex; // set focus to THIS one
1566  pFrameWindow->nCloseTab = -1; // mark that I'm NOT closing a tab (make sure)
1567 
1568  // invalidate the tab bar rectangle first, before I continue
1569  WBInvalidateRect(pFW->wID, &(pFrameWindow->rctLastTabBarRect), 0);
1570 
1571  FWRecalcLayout(pFW->wID); // recalculate layout (this also updates the frame window and whatnot)
1572  // note that recalc'ing the layout does NOT re-paint the window.
1573 
1574  pC = pFrameWindow->ppChildFrames[iIndex];
1575 
1576  WBMapWindow(pDisplay, pC->wID); // make sure it's mapped (probably isn't)
1577  WBSetInputFocus(pC->wID);
1578 
1579  // notify the child frame that its status has changed. this will fix status text
1580  // for the frame window, and update it as needed
1581 
1583 
1584  // NOW, go through the list and hide all of the others NOT the focus window
1585  for(i1=0; i1 < pFrameWindow->nChildFrames; i1++)
1586  {
1587  if(i1 == iIndex)
1588  {
1589  continue;
1590  }
1591 
1592  pC = pFrameWindow->ppChildFrames[i1];
1593 
1594 // if(WBIsMapped(pDisplay, pC->wID))
1595  {
1596 // WB_ERROR_PRINT("TEMPORARY: %s - unmapping %u (%08xH)\n", __FUNCTION__, (unsigned int)pC->wID, (unsigned int)pC->wID);
1597 
1598  WBUnmapWindow(pDisplay, pC->wID); // unmap it (make it invisible)
1599  }
1600  }
1601 
1602 
1603  // -------------------
1604  // ASSIGN CORRECT MENU
1605  // -------------------
1606 
1607  wMB = WBGetMenuWindow(pFrameWindow->wbFW.wID);
1608 
1609  if(wMB != None) // we have a menu bar window
1610  {
1611  // NOW, switch to the menu associated with the current child frame
1612 
1613  pMBW = MBGetMenuBarWindowStruct(wMB);
1614 
1615  if(pMBW)
1616  {
1617  if(pC->pszMenuResource)
1618  {
1620  }
1621  else
1622  {
1623  MBSetMenuBarMenuResource(pMBW, pFrameWindow->pDefaultMenuResource);
1624  }
1625  }
1626  }
1627 
1628  if(pC->pMenuHandler)
1629  {
1630  pFrameWindow->pMenuHandler = pC->pMenuHandler; // use child frame's menu handler
1631  }
1632  else
1633  {
1634  pFrameWindow->pMenuHandler = pFrameWindow->pDefMenuHandler;
1635  }
1636 
1637  WBUpdateWindow(pFW->wID); // update these windows now
1638  WBUpdateWindow(pC->wID);
1639 }
1640 
1641 
1643 {
1644 FRAME_WINDOW *pFrameWindow;
1645 int iIndex;
1646 
1647  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1648 
1649  if(!pFrameWindow)
1650  {
1651  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
1652 
1653  return -1;
1654  }
1655 
1656  // locate the contained window from my list. then set as 'focus tab'
1657 
1658  if(!pFrameWindow->ppChildFrames || !pFrameWindow->nChildFrames)
1659  {
1660  return -1; // no child windows in list
1661  }
1662 
1663  if(!pCont)
1664  {
1665  return pFrameWindow->nFocusTab; // return focus tab index
1666  }
1667 
1668  for(iIndex=0; iIndex < pFrameWindow->nChildFrames; iIndex++)
1669  {
1670  if(pFrameWindow->ppChildFrames[iIndex] == pCont)
1671  {
1672  return iIndex;
1673  }
1674  }
1675 
1676  return -1;
1677 }
1678 
1680 {
1681 FRAME_WINDOW *pFrameWindow;
1682 int iI, i1;
1683 WBChildFrame *pC = NULL;
1684 
1685 
1686  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1687 
1688  if(!pFrameWindow)
1689  {
1690  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
1691 
1692  return;
1693  }
1694 
1695  // locate the contained window from my list. then set as 'focus tab'
1696 
1697  if(!pFrameWindow->ppChildFrames || !pFrameWindow->nChildFrames)
1698  {
1699  return; // no child windows in list
1700  }
1701 
1702  iI = FWGetChildFrameIndex(pFW, pCont);
1703 
1704  if(iI < 0)
1705  {
1706  return; // for now...
1707  }
1708 
1709  if(iIndex == -1) // previous
1710  {
1711  iIndex = iI - 1;
1712 
1713  if(iIndex < 0)
1714  {
1715  iIndex = 0;
1716  }
1717 
1718 // WB_ERROR_PRINT("TEMPORARY: %s - move prev %d\n", __FUNCTION__, iIndex);
1719  }
1720  else if(iIndex == -2)
1721  {
1722  iIndex = iI + 1;
1723 
1724 // WB_ERROR_PRINT("TEMPORARY: %s - move next %d\n", __FUNCTION__, iIndex);
1725  }
1726 
1727  if(iIndex >= pFrameWindow->nChildFrames)
1728  {
1729  iIndex = pFrameWindow->nChildFrames - 1;
1730  }
1731 
1732  // move 'iI' to 'iIndex', sliding everything else around
1733 
1734  if(iI > iIndex)
1735  {
1736  pC = pFrameWindow->ppChildFrames[iI];
1737 
1738  for(i1=iI; i1 > iIndex; i1--) // move 1 to the right from iIndex through iI - 1
1739  {
1740  pFrameWindow->ppChildFrames[i1] = pFrameWindow->ppChildFrames[i1 - 1];
1741  }
1742 
1743  pFrameWindow->ppChildFrames[iIndex] = pC;
1744 
1745  if(pFrameWindow->nFocusTab == iI)
1746  {
1747  pFrameWindow->nFocusTab = iIndex;
1748  }
1749  else if(pFrameWindow->nFocusTab >= iIndex && pFrameWindow->nFocusTab < iI)
1750  {
1751  pFrameWindow->nFocusTab ++; // increment it (since I moved things to the right)
1752 
1753  if(pFrameWindow->nFocusTab >= pFrameWindow->nChildFrames)
1754  {
1755  pFrameWindow->nFocusTab = pFrameWindow->nChildFrames - 1; // just in case
1756  }
1757  }
1758  }
1759  else if(iI < iIndex)
1760  {
1761  pC = pFrameWindow->ppChildFrames[iI];
1762 
1763  for(i1=iI; i1 < iIndex; i1++) // move 1 to the left from iI + 1 through iIndex
1764  {
1765  pFrameWindow->ppChildFrames[i1] = pFrameWindow->ppChildFrames[i1 + 1];
1766  }
1767 
1768  pFrameWindow->ppChildFrames[iIndex] = pC;
1769 
1770  if(pFrameWindow->nFocusTab == iI)
1771  {
1772  pFrameWindow->nFocusTab = iIndex;
1773  }
1774  else if(pFrameWindow->nFocusTab > iI && pFrameWindow->nFocusTab <= iIndex)
1775  {
1776  pFrameWindow->nFocusTab --; // decrement it (since I moved things to the left)
1777 
1778  if(pFrameWindow->nFocusTab < 0)
1779  {
1780  pFrameWindow->nFocusTab = 0; // just in case
1781  }
1782  }
1783  }
1784 
1785  WBInvalidateRect(pFW->wID, &(pFrameWindow->rctLastTabBarRect), 0);
1786 
1787  FWRecalcLayout(pFW->wID); // recalculate layout (this also updates the frame window and whatnot)
1788 
1789  WBUpdateWindow(pFW->wID); // update these windows now
1790 
1791  if(pFrameWindow->nFocusTab >= 0 && pFrameWindow->nFocusTab < pFrameWindow->nChildFrames)
1792  {
1793  // make sure I re-paint the focus window
1794 
1795  pC = pFrameWindow->ppChildFrames[pFrameWindow->nFocusTab];
1796 
1797  if(pC)
1798  {
1799  WBUpdateWindow(pC->wID);
1800  }
1801  }
1802 }
1803 
1804 void FWSetStatusText(WBFrameWindow *pFW, const char *szText)
1805 {
1806 WB_RECT rct;
1807 FRAME_WINDOW *pFrameWindow;
1808 
1809 
1810  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1811 
1812  if(!pFrameWindow)
1813  {
1814  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
1815 
1816  return;
1817  }
1818 
1819  if(pFrameWindow->szStatus)
1820  {
1821  WBFree(pFrameWindow->szStatus);
1822  pFrameWindow->szStatus = NULL;
1823  }
1824 
1825  if(szText)
1826  {
1827  pFrameWindow->szStatus = WBCopyString(szText);
1828 
1829  if(!pFrameWindow->szStatus)
1830  {
1831  WB_ERROR_PRINT("ERROR: %s - no memory\n", __FUNCTION__);
1832  }
1833  }
1834 
1835  InternalCalcStatusBarRect(pFrameWindow, &rct);
1836 
1837  WBInvalidateRect(pFrameWindow->wbFW.wID, &rct, 1); // and, finally, invalidate the status bar and force a re-paint
1838 }
1839 
1840 void FWSetStatusTabInfo(WBFrameWindow *pFW, int nTabs, const int *pTabs)
1841 {
1842 WB_RECT rct;
1843 FRAME_WINDOW *pFrameWindow;
1844 
1845 
1846  pFrameWindow = InternalGet_FRAME_WINDOW(pFW);
1847 
1848  if(!pFrameWindow)
1849  {
1850  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
1851 
1852  return;
1853  }
1854 
1855  if(pFrameWindow->pStatusBarTabs)
1856  {
1857  WBFree(pFrameWindow->pStatusBarTabs);
1858 
1859  pFrameWindow->pStatusBarTabs = NULL;
1860  }
1861 
1862  pFrameWindow->nStatusBarTabs = nTabs;
1863 
1864  if(pTabs)
1865  {
1866  pFrameWindow->pStatusBarTabs = (int *)WBAlloc(sizeof(int) * (nTabs + 1));
1867 
1868  if(!pFrameWindow->pStatusBarTabs)
1869  {
1870  WB_ERROR_PRINT("ERROR: %s - no memory (attempt to allocate %d integers)\n", __FUNCTION__, nTabs);
1871 
1872  return;
1873  }
1874 
1875  memcpy(pFrameWindow->pStatusBarTabs, pTabs, sizeof(int) * nTabs);
1876  pFrameWindow->pStatusBarTabs[nTabs] = 0; // always end in a zero, for now
1877  }
1878 
1879  InternalCalcTabBarRect(pFrameWindow, &rct);
1880 
1881  WBInvalidateRect(pFrameWindow->wbFW.wID, &rct, 0); // and, finally, invalidate the status bar but don't re-paint yet
1882 }
1883 
1884 
1885 
1886 
1888 // //
1889 // _____ _ _ _ _ _ _ //
1890 // | ____|__ __ ___ _ __ | |_ | | | | __ _ _ __ __| || |(_) _ __ __ _ //
1891 // | _| \ \ / // _ \| '_ \ | __| | |_| | / _` || '_ \ / _` || || || '_ \ / _` | //
1892 // | |___ \ V /| __/| | | || |_ | _ || (_| || | | || (_| || || || | | || (_| | //
1893 // |_____| \_/ \___||_| |_| \__| |_| |_| \__,_||_| |_| \__,_||_||_||_| |_| \__, | //
1894 // |___/ //
1895 // //
1897 
1898 
1899 static void Internal_CalcTabRect(FRAME_WINDOW *pFrameWindow, int iIndex, WB_RECT *prctTab)
1900 {
1901  if(!pFrameWindow->ppChildFrames || iIndex >= pFrameWindow->nChildFrames ||
1902  iIndex < pFrameWindow->nLeftTab || iIndex > pFrameWindow->nRightTab)
1903  {
1904  prctTab->left = prctTab->right = prctTab->top = prctTab->bottom = 0;
1905  return;
1906  }
1907 
1908  prctTab->top = pFrameWindow->rctLastTabBarRect.top + 2;
1909  prctTab->bottom = pFrameWindow->rctLastTabBarRect.bottom;
1910  prctTab->left = (iIndex - pFrameWindow->nLeftTab) * pFrameWindow->nTabBarTabWidth
1911  + TAB_BAR_SCROLL_WIDTH + 2;
1912  prctTab->right = prctTab->left + pFrameWindow->nTabBarTabWidth;
1913 }
1914 
1915 static void Internal_CalcTabGeom(FRAME_WINDOW *pFrameWindow, int iIndex, WB_GEOM *pgTab)
1916 {
1917  if(!pFrameWindow->ppChildFrames || iIndex >= pFrameWindow->nChildFrames ||
1918  iIndex < pFrameWindow->nLeftTab || iIndex > pFrameWindow->nRightTab)
1919  {
1920  pgTab->x = pgTab->y = pgTab->width = pgTab->height = 0;
1921  return;
1922  }
1923 
1924  pgTab->y = pFrameWindow->rctLastTabBarRect.top + 2;
1925  pgTab->height = pFrameWindow->rctLastTabBarRect.bottom - pgTab->y;
1926  pgTab->x = (iIndex - pFrameWindow->nLeftTab) * pFrameWindow->nTabBarTabWidth
1927  + TAB_BAR_SCROLL_WIDTH + 2;
1928  pgTab->width = pFrameWindow->nTabBarTabWidth;
1929 }
1930 
1931 static int Internal_Tab_Bar_Event(FRAME_WINDOW *pFrameWindow, XEvent *pEvent)
1932 {
1933 WB_RECT rctLeft, rctRight, rctNew, rctTemp;
1934 Display *pDisplay = WBGetWindowDisplay(pFrameWindow->wbFW.wID);
1935 XClientMessageEvent evt;
1936 int i1;
1937 
1938 
1939  if(pEvent->type == ButtonPress)
1940  {
1941  // button press within the tab zone means "grab the mouse"
1942 
1944  XGrabPointer(pDisplay, pFrameWindow->wbFW.wID, 1,
1945  ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | PointerMotionMask
1946  | EnterWindowMask | LeaveWindowMask,
1947  GrabModeAsync, // pointer mode
1948  GrabModeAsync, // keyboard mode
1949  None, None, CurrentTime);
1951 
1952  pFrameWindow->nTabBarButtonFlags |= tab_bar_button_GRAB;
1953  pFrameWindow->nTabBarButtonFlags &= ~tab_bar_button_BUTTONMASK;
1954 
1955  // calculate the rectangle for each tab, the scroll left, the scroll right, the 'x'
1956  // buttons for each of the tabs, *AND* the 'new tab' button. Handle each one as needed.
1957 
1958  // scroll buttons first
1959  // if I'm clicking within one of the scroll buttons, or the 'new tab' button, I can
1960  // leave now, after setting the appropriate bit. Drags won't be relevant.
1961 
1962  rctLeft.top = rctRight.top = rctNew.top = pFrameWindow->rctLastTabBarRect.top + 2;
1963  rctLeft.bottom = rctRight.bottom = rctNew.bottom = pFrameWindow->rctLastTabBarRect.bottom - 2;
1964 
1965  rctLeft.left = pFrameWindow->rctLastTabBarRect.left + 2;
1966  rctLeft.right = rctLeft.left + TAB_BAR_SCROLL_WIDTH - 2;
1967 
1968  rctRight.right = pFrameWindow->rctLastTabBarRect.right - 2;
1969  rctRight.left = rctRight.right - TAB_BAR_SCROLL_WIDTH + 2;
1970 
1971  // TODO: verify this formula works correctly
1972  rctNew.right = rctRight.right - TAB_BAR_SCROLL_WIDTH - 2;
1973  rctNew.left = rctNew.right - TAB_BAR_ADD_BUTTON_WIDTH - 2;
1974 
1975  if(WBPointInRect(pEvent->xbutton.x, pEvent->xbutton.y, rctLeft))
1976  {
1977  pFrameWindow->nTabBarButtonFlags |= tab_bar_button_PREV; // TODO: handle if button 'inactive'
1978  }
1979  else if(WBPointInRect(pEvent->xbutton.x, pEvent->xbutton.y, rctRight))
1980  {
1981  pFrameWindow->nTabBarButtonFlags |= tab_bar_button_NEXT; // TODO: handle if button 'inactive'
1982  }
1983  else if(WBPointInRect(pEvent->xbutton.x, pEvent->xbutton.y, rctNew))
1984  {
1985  pFrameWindow->nTabBarButtonFlags |= tab_bar_button_NEW;
1986  }
1987 
1988  if(pFrameWindow->nTabBarButtonFlags & tab_bar_button_BUTTONMASK) // I set a button flag?
1989  {
1990  // force re-paint of tab bar rect
1991 // WB_ERROR_PRINT("TEMPORARY: %s - invalidate rect: %d, %d, %d, %d\n", __FUNCTION__,
1992 // pFrameWindow->rctLastTabBarRect.left,
1993 // pFrameWindow->rctLastTabBarRect.top,
1994 // pFrameWindow->rctLastTabBarRect.right,
1995 // pFrameWindow->rctLastTabBarRect.bottom);
1996 
1997  WBInvalidateRect(pFrameWindow->wbFW.wID, &(pFrameWindow->rctLastTabBarRect), 1);
1998 
1999  return 1; // and return, saying "I did it"
2000  }
2001 
2002 // WB_ERROR_PRINT("TODO: %s - see if I'm clicking on a tab, a tab 'close' button, etc.\n", __FUNCTION__);
2003 
2004  // check to see which tab I might be clicking in
2005 
2006  if(pFrameWindow->nChildFrames)
2007  {
2008  for(i1=pFrameWindow->nLeftTab; i1 <= pFrameWindow->nRightTab; i1++)
2009  {
2010  Internal_CalcTabRect(pFrameWindow, i1, &rctTemp);
2011 
2012  if(WBPointInRect(pEvent->xbutton.x, pEvent->xbutton.y, rctTemp))
2013  {
2014  // did I click on the 'x' button in the upper right corner?
2015  // form a ~square that represents the button using font height. See
2016  rctTemp.right -= 6; // 6 from the right edge
2017  rctTemp.left = rctTemp.right - (pFrameWindow->nFontHeight + 2); // font width + 2 from right
2018  rctTemp.top += 2; // 2 pixels from top
2019  rctTemp.bottom = rctTemp.top + pFrameWindow->nFontHeight; // x height is font height
2020 
2021  // TODO: do I really want to post/send message or should i wait until mouse release? In the
2022  // case of tab delete, I might query for "are you sure" like if the file has not been saved
2023 
2024  if(WBPointInRect(pEvent->xbutton.x, pEvent->xbutton.y, rctTemp))
2025  {
2026  // clicking on the 'x' button to close the tab
2027 
2028  pFrameWindow->nCloseTab = i1; // the tab I'm closing (notify on mouse up)
2029 
2030  WBInvalidateRect(pFrameWindow->wbFW.wID, &(pFrameWindow->rctLastTabBarRect), 1);
2031  }
2032  else
2033  {
2034  pFrameWindow->nCloseTab = -1; // NOT closing a tab (make sure)
2035 
2036  // set focus to tab (do it on button press)
2037 
2038  if(i1 == pFrameWindow->nFocusTab)
2039  {
2040  return 1; // do nothing (already there)
2041  }
2042 
2043  bzero(&evt, sizeof(evt));
2044  evt.type = ClientMessage;
2045 
2046  evt.display = pDisplay;
2047  evt.window = pFrameWindow->wbFW.wID;
2048  evt.message_type = aTAB_MESSAGE;
2049 
2050  evt.data.l[0] = SET_TAB_MESSAGE;
2051  evt.data.l[1] = i1; // the tab index
2052  evt.format = 32; // always
2053 
2054  WBPostEvent(pFrameWindow->wbFW.wID, (XEvent *)&evt); // this will re-paint
2055  }
2056 
2057  return 1;
2058  }
2059  }
2060  }
2061 
2062  // if I get here, I'll do this to make sure the UI is consistent
2063  pFrameWindow->nCloseTab = -1; // NOT closing a tab (make sure)
2064 
2065  return 1; // for now assume "I handled it" since the mousie was grabbed
2066  }
2067  else if(pEvent->type == ButtonRelease)
2068  {
2069  if(!(pFrameWindow->nTabBarButtonFlags & tab_bar_button_GRAB)) // mouse grabbed?
2070  {
2071  pFrameWindow->nCloseTab = -1; // NOT closing a tab
2072  pFrameWindow->nTabBarButtonFlags &= ~tab_bar_button_BUTTONMASK; // turn off button mask if not grabbed
2073 
2074  return 0; // "not handled"
2075  }
2076 
2078  XUngrabPointer(pDisplay, CurrentTime);
2080 
2081  pFrameWindow->nTabBarButtonFlags &= ~tab_bar_button_GRAB;
2082 
2083  if(pFrameWindow->nCloseTab < 0 && // NOT closing a tab (make sure)
2084  !(pFrameWindow->nTabBarButtonFlags & tab_bar_button_BUTTONMASK)) // clicked on a tab, did we?
2085  {
2086  // if I'm releasing on a tab, and I dragged it, change the tab order according to the tab
2087  // that I'm sitting on at the moment...
2088 
2089 // TODO: enable this while dragging stuff, to re-paint the tabs during the drag. but until then, don't
2090 // WBInvalidateRect(pFrameWindow->wbFW.wID, &(pFrameWindow->rctLastTabBarRect), 1);
2091 
2092  WB_ERROR_PRINT("TODO: %s - handle tab-drags, except when I clicked the 'delete tab' button\n", __FUNCTION__);
2093  }
2094  else
2095  {
2096  // notifications
2097 
2098  bzero(&evt, sizeof(evt));
2099  evt.type = ClientMessage;
2100 
2101  evt.display = pDisplay;
2102  evt.window = pFrameWindow->wbFW.wID;
2103  evt.message_type = aTAB_MESSAGE;
2104 
2105  if(pFrameWindow->nCloseTab >= 0) // I'm closing a tab?
2106  {
2107  evt.data.l[0] = CLOSE_TAB_MESSAGE;
2108  evt.data.l[1] = pFrameWindow->nCloseTab;
2109 
2110 // WB_ERROR_PRINT("TEMPORARY: %s - close tab %d\n", __FUNCTION__, pFrameWindow->nCloseTab);
2111 
2112  pFrameWindow->nCloseTab = -1; // NOT closing a tab now (make sure) (also fixes the UI)
2113  }
2114  else if(pFrameWindow->nTabBarButtonFlags & tab_bar_button_NEXT)
2115  {
2116  if(pFrameWindow->nRightTab < (pFrameWindow->nChildFrames - 1))
2117  {
2118  evt.data.l[0] = NEXT_TAB_MESSAGE;
2119  }
2120  else
2121  {
2122  goto no_message;
2123  }
2124  }
2125  else if(pFrameWindow->nTabBarButtonFlags & tab_bar_button_PREV)
2126  {
2127  if(pFrameWindow->nLeftTab > 0)
2128  {
2129  evt.data.l[0] = PREV_TAB_MESSAGE;
2130  }
2131  else
2132  {
2133  goto no_message;
2134  }
2135  }
2136  else if(pFrameWindow->nTabBarButtonFlags & tab_bar_button_NEW)
2137  {
2138  evt.data.l[0] = NEW_TAB_MESSAGE;
2139  }
2140 
2141  evt.format = 32; // always
2142 
2143  WBPostEvent(pFrameWindow->wbFW.wID, (XEvent *)&evt);
2144 
2145 no_message:
2146 
2147  pFrameWindow->nTabBarButtonFlags &= ~tab_bar_button_BUTTONMASK;
2148 
2149  // all-important, mark tab bar rectangle 'to be re-painted' and update it
2150  WBInvalidateRect(pFrameWindow->wbFW.wID, &(pFrameWindow->rctLastTabBarRect), 1);
2151  }
2152 
2153  return 1;
2154  }
2155  else if(pEvent->type == MotionNotify)
2156  {
2157  if(pFrameWindow->nTabBarButtonFlags & tab_bar_button_GRAB) // mouse grabbed?
2158  {
2159  if(!(pFrameWindow->nTabBarButtonFlags & tab_bar_button_BUTTONMASK)) // drag not relevant for these
2160  {
2161  WB_ERROR_PRINT("TODO: %s - handle tab-drags via visual feedback\n", __FUNCTION__);
2162 
2163  return 1; // "handled" (by ignoring it)
2164  }
2165  }
2166  }
2167 
2168  return 0; // not handled
2169 }
2170 
2171 
2172 static void InternalPaintTabBar(FRAME_WINDOW *pFrameWindow, XExposeEvent *pEvent)
2173 {
2174 WB_RECT rct0, rctExpose;
2175 WB_GEOM geom0;
2176 GC gc0;
2177 
2178 
2179  if(WBFrameWindow_NO_TABS & pFrameWindow->wbFW.iFlags) // window has no tab bar?
2180  {
2181  return; // no tab bar, just bail
2182  }
2183 
2184  // obtain the most recent cached location of the tab bar. this is important.
2185 
2186  memcpy(&rct0, &(pFrameWindow->rctLastTabBarRect), sizeof(rct0));
2187 
2188  // cache the tab bar rectangle for mouse hit tests. not the same rect as rctLastTabBarRect
2189 
2190  pFrameWindow->rctTabBar.left = rct0.left + 1; // add 1 pixel for border
2191  pFrameWindow->rctTabBar.top = rct0.top + 1;
2192  pFrameWindow->rctTabBar.right = rct0.right - 1; // subtract 1 pixel for border
2193  pFrameWindow->rctTabBar.bottom = rct0.bottom - 1;
2194 
2195 
2196  // does the Expose event intersect my tab bar?
2197 
2198  rctExpose.left = pEvent->x;
2199  rctExpose.top = pEvent->y;
2200  rctExpose.right = rctExpose.left + pEvent->width;
2201  rctExpose.bottom = rctExpose.top + pEvent->height;
2202 
2203  if(!WBRectOverlapped(rct0, rctExpose))
2204  {
2205 // WB_ERROR_PRINT("TEMPORARY: %s - expose event excludes tab bar\n", __FUNCTION__);
2206 
2207  return; // do nothing (no overlap)
2208  }
2209 
2210  // intersect the two rectangles so I only re-paint what I have to
2211 
2212  if(rctExpose.top < rct0.top)
2213  {
2214  rctExpose.top = rct0.top;
2215  }
2216 
2217  if(rctExpose.bottom > rct0.bottom)
2218  {
2219  rctExpose.bottom = rct0.bottom;
2220  }
2221 
2222  if(rctExpose.left < rct0.left)
2223  {
2224  rctExpose.left = rct0.left;
2225  }
2226 
2227  if(rctExpose.right > rct0.right)
2228  {
2229  rctExpose.right = rct0.right;
2230  }
2231 
2232  // time to start painting
2233 
2234  geom0.x = rctExpose.left;
2235  geom0.y = rctExpose.top;
2236  geom0.width = rctExpose.right - rctExpose.left;
2237  geom0.height = rctExpose.bottom - rctExpose.top;
2238 
2239 // WB_ERROR_PRINT("TEMPORARY: %s - tab bar EXPOSE rect %d, %d, %d, %d\n",
2240 // __FUNCTION__, rctExpose.left, rctExpose.top, rctExpose.right, rctExpose.bottom);
2241 
2242  gc0 = WBBeginPaintGeom(pFrameWindow->wbFW.wID, &geom0);
2243 
2244  if(gc0 == None)
2245  {
2246  WB_ERROR_PRINT("ERROR: %s - no GC from WBBeginPaintGeom\n", __FUNCTION__);
2247  }
2248  else
2249  {
2250  Display *pDisplay = WBGetWindowDisplay(pFrameWindow->wbFW.wID);
2251  Drawable dw;
2252  WB_GEOM geom, g2;
2253  GC gc;
2254  WB_RECT rct, rctTemp;
2255  XColor clr;
2256  int iDepth;
2257 
2258 
2259  gc = WBGetWindowCopyGC2(pFrameWindow->wbFW.wID, gc0);
2260 
2261  if(gc != None)
2262  {
2264  XCopyGC(pDisplay, gc0,
2265  GCFont | GCFillStyle | GCForeground | GCBackground | GCCapStyle | GCFunction | GCLineWidth,
2266  gc);
2268  }
2269 
2270  iDepth = DefaultDepth(pDisplay, DefaultScreen(pDisplay)); // NOTE: depth may be too small...
2271  WB_DEBUG_PRINT((DebugLevel_Heavy | DebugSubSystem_Expose), "InternalPaintTabBar() - depth is %d\n", iDepth);
2272 
2274  dw = (Drawable)XCreatePixmap(pDisplay, pFrameWindow->wbFW.wID,
2275  rct0.right - rct0.left, rct0.bottom - rct0.top,
2276  iDepth);
2278 
2279  if(gc == None || dw == None)
2280  {
2282  if(gc != None)
2283  {
2284  XFreeGC(pDisplay, gc);
2285  }
2286 
2287  if(dw != None)
2288  {
2289  XFreePixmap(pDisplay, (Pixmap)dw);
2290  }
2292 
2293  dw = pFrameWindow->wbFW.wID;
2294  gc = gc0; // as a fallback
2295 
2296  memcpy(&rct, &rct0, sizeof(rct));
2297  memcpy(&geom, &geom0, sizeof(geom));
2298 
2299  WB_ERROR_PRINT("ERROR: %s - unable to create pixmap or 2nd gc; fallback invoked\n", __FUNCTION__);
2300  }
2301  else
2302  {
2303  Region rgn;
2304 
2305  rct.left = rct.top = 0;
2306 
2307  rct.right = rct0.right - rct0.left;
2308  rct.bottom = rct0.bottom - rct0.top;
2309 
2310  geom.x = geom.y = 0;
2311  geom.width = geom0.width;
2312  geom.height = geom0.height;
2313  geom.border = geom0.border;
2314 
2315  rgn = WBRectToRegion(&rct);
2316 
2318  XSetRegion(pDisplay, gc, rgn); // new GC has different clip region
2320 
2321  if(rgn != None)
2322  {
2323  XDestroyRegion(rgn);
2324  }
2325  }
2326 
2327 // WB_ERROR_PRINT("TEMPORARY: %s - filling display geom %d, %d, %d, %d\n",
2328 // __FUNCTION__, geom.x, geom.y, geom.width, geom.height);
2329 
2330  // fill the rectangle with the background color
2331 
2333  XSetForeground(pDisplay, gc, clrBG.pixel);
2334 
2335  XFillRectangle(pDisplay, dw, gc, rct.left, rct.top, rct.right - rct.left, rct.bottom - rct.top);
2336 
2337  XSetForeground(pDisplay, gc, clrFG.pixel);
2339 
2340  // draw some lines in some colors for the border
2341 
2342  g2.x = rct.left;
2343  g2.y = rct.top;
2344  g2.width = rct.right - rct.left;
2345  g2.height = rct.bottom - rct.top;
2346 
2347  WBDraw3DBorderRect(pDisplay, dw, gc, &g2, clrBD3.pixel, clrBD2.pixel); // sunken
2348 
2349 
2350  // draw the 'left' (aka 'PREV') button
2351 
2352  g2.x = rct.left + 1;
2353  g2.y = rct.top + 1;
2354  g2.width = TAB_BAR_SCROLL_WIDTH;
2355  g2.height = rct.bottom - rct.top - 2;
2356 
2357  if((pFrameWindow->nTabBarButtonFlags & tab_bar_button_PREV) && pFrameWindow->nLeftTab > 0)
2358  {
2359  WBDraw3DBorderRect(pDisplay, dw, gc, &g2, clrBD3.pixel, clrBD2.pixel);
2360  }
2361  else
2362  {
2363  WBDraw3DBorderRect(pDisplay, dw, gc, &g2, clrBD2.pixel, clrBD3.pixel);
2364  }
2365 
2366  g2.y += g2.height >> 2;
2367  g2.height = (g2.height >> 2) + (g2.height >> 2);
2368 
2369  WBDrawLeftArrow(pDisplay, dw, gc, &g2,
2370  (pFrameWindow->nLeftTab > 0) ? clrFG.pixel : clrBD3.pixel);
2371 
2372 
2373  // draw the 'right' (aka 'NEXT') button
2374 
2375  g2.x = rct.right - TAB_BAR_SCROLL_WIDTH - 1;
2376  g2.y = rct.top + 1;
2377  g2.width = TAB_BAR_SCROLL_WIDTH;
2378  g2.height = rct.bottom - rct.top - 2;
2379 
2380  if((pFrameWindow->nTabBarButtonFlags & tab_bar_button_NEXT) && pFrameWindow->nRightTab < (pFrameWindow->nChildFrames - 1))
2381  {
2382  WBDraw3DBorderRect(pDisplay, dw, gc, &g2, clrBD3.pixel, clrBD2.pixel);
2383  }
2384  else
2385  {
2386  WBDraw3DBorderRect(pDisplay, dw, gc, &g2, clrBD2.pixel, clrBD3.pixel);
2387  }
2388 
2389  g2.y += g2.height >> 2;
2390  g2.height = (g2.height >> 2) + (g2.height >> 2);
2391 
2392  WBDrawRightArrow(pDisplay, dw, gc, &g2,
2393  (pFrameWindow->nRightTab < (pFrameWindow->nChildFrames - 1)) ? clrFG.pixel : clrBD3.pixel);
2394 
2395 
2396  // draw the '+' button for new document
2397 
2398  g2.x = (rct.right - TAB_BAR_SCROLL_WIDTH - 1) - (TAB_BAR_ADD_BUTTON_WIDTH + 1);
2399  g2.y = rct.top + 1;
2400  g2.width = TAB_BAR_ADD_BUTTON_WIDTH;
2401  g2.height = rct.bottom - rct.top - 2;
2402 
2403  if(pFrameWindow->nTabBarButtonFlags & tab_bar_button_NEW)
2404  {
2405  WBDraw3DBorderRect(pDisplay, dw, gc, &g2, clrBD3.pixel, clrBD2.pixel);
2406  }
2407  else
2408  {
2409  WBDraw3DBorderRect(pDisplay, dw, gc, &g2, clrBD2.pixel, clrBD3.pixel);
2410  }
2411 
2412  g2.y += 2;
2413  g2.height -= 4;
2414 
2415  // invent my own color for the '+' button, in this case #0040A0
2416  RGB255_TO_XCOLOR(0, 0x40, 0xa0, clr);
2417 
2418  PXM_RGBToPixel(NULL, &clr);
2419 // WB_ERROR_PRINT("TEMPORARY: red=%d, green=%d, blue=%d, pixel=%d\n",
2420 // clr.red, clr.green, clr.blue, clr.pixel);
2421 
2423  XSetForeground(pDisplay, gc, clr.pixel/*clrFG.pixel*/); // for now use foreground color; later, ??
2425 
2426  rctTemp.left = g2.x;
2427  rctTemp.top = g2.y;
2428  rctTemp.right = g2.x + g2.width;
2429  rctTemp.bottom = g2.y + g2.height;
2430 
2431  // put a 'splat' in the middle of this button using the 'bold' font
2432  DTDrawSingleLineText(pFrameWindow->fontSetBold,
2433  "+", pDisplay, gc, dw, 0, 0, &rctTemp,
2435 
2437  XSetForeground(pDisplay, gc, clrFG.pixel); // by convention, (re)set FG color again [for text I need this]
2439 
2440  // TODO: draw the actual tabs and the text within them
2441 
2442 // WB_RECT rctLastTabBarRect; // cached value of tab bar rectangle
2443 // int nTabBarTabWidth; // cached calculated width of a tab on the tab bar
2444 // int nTabBarButtonFlags; // button flags for tab bar ('pressed')
2445 
2446  if(pFrameWindow->ppChildFrames && pFrameWindow->nChildFrames)
2447  {
2448  WBChildFrame *pC;
2449  int i1;
2450 
2451 // if(pFrameWindow->nCloseTab >= 0)
2452 // {
2453 // WB_ERROR_PRINT("TEMPORARY: %s - nCloseTab is %d\n", __FUNCTION__, pFrameWindow->nCloseTab);
2454 // }
2455 
2456  for(i1=pFrameWindow->nRightTab; i1 >= pFrameWindow->nLeftTab; i1--)
2457  {
2458  if(i1 != pFrameWindow->nFocusTab)
2459  {
2460  if(i1 >= pFrameWindow->nChildFrames)
2461  {
2462  continue;
2463  }
2464 
2465  pC = pFrameWindow->ppChildFrames[i1];
2466 
2467  if(!pC)
2468  {
2469  continue;
2470  }
2471 
2472  Internal_CalcTabGeom(pFrameWindow, i1, &g2); // the geom in window coordinates
2473  g2.x -= rct0.left; // translate to 'drawable' coordinates
2474  g2.y -= rct0.top;
2475 
2476  WBDraw3DBorderTab(pDisplay, dw, gc, &g2,
2477  pFrameWindow->nCloseTab == i1 ? -2 : 0, // -2 if I'm deleting the tab (no focus)
2478  clrFG.pixel, clrBG.pixel,
2479  clrBD2.pixel, clrBD3.pixel, clrABG.pixel,
2480  pFrameWindow->fontSet, pFrameWindow->fontSetBold,
2481  pC->aImageAtom, pC->szDisplayName);
2482  }
2483  }
2484 
2485  i1 = pFrameWindow->nFocusTab; // this one is done last
2486 
2487  if(i1 < pFrameWindow->nChildFrames)
2488  {
2489  pC = pFrameWindow->ppChildFrames[i1];
2490 
2491  if(pC)
2492  {
2493  Internal_CalcTabGeom(pFrameWindow, i1, &g2); // the geom in window coordinates
2494  g2.x -= rct0.left; // translate to 'drawable' coordinates
2495  g2.y -= rct0.top;
2496 
2497  WBDraw3DBorderTab(pDisplay, dw, gc, &g2,
2498  pFrameWindow->nCloseTab == i1 ? -1 : 1, // -1 if I'm deleting the tab, positive otherwise
2499  clrFG.pixel, clrBG.pixel,
2500  clrBD2.pixel, clrBD3.pixel, clrABG.pixel,
2501  pFrameWindow->fontSet, pFrameWindow->fontSetBold,
2502  pC->aImageAtom, pC->szDisplayName);
2503  }
2504  }
2505  }
2506 
2507  // if I'm using a pixmap, now I copy it to the window
2508 
2509  if(dw != pFrameWindow->wbFW.wID)
2510  {
2512  // copy my drawn pixmap onto the window
2513  XCopyArea(pDisplay, dw, pFrameWindow->wbFW.wID, gc0,
2514  rct.left, rct.top, rct.right - rct.left, rct.bottom - rct.top,
2515  rct0.left, rct0.top);
2517  }
2518 
2519  // RESOURCE CLEANUP
2520 
2522  if(gc != gc0)
2523  {
2524  XFreeGC(pDisplay, gc);
2525  gc = None;
2526  }
2527 
2528  if(dw != pFrameWindow->wbFW.wID)
2529  {
2530  XFreePixmap(pDisplay, (Pixmap)dw);
2531  dw = None;
2532  }
2534 
2535  WBEndPaint(pFrameWindow->wbFW.wID, gc0); // and that's it!
2536  }
2537 
2538  // finally, alter the expose event slightly so that it reflects the 'painted' area
2539 
2540  if(pEvent->y < rct0.bottom)
2541  {
2542 // WB_ERROR_PRINT("TEMPORARY: %s - altering x and height in Expose event\n", __FUNCTION__);
2543 
2544  if(pEvent->y >= rct0.top && pEvent->y < rct0.bottom) // so it only includes the tab area but overlaps it
2545  {
2546  pEvent->height -= rct0.bottom - pEvent->y;
2547  pEvent->y = rct0.bottom;
2548 
2549  if(pEvent->height < 0) // just in case
2550  {
2551  pEvent->height = 0;
2552  }
2553  }
2554  }
2555 }
2556 
2557 
2558 static void InternalPaintStatusBar(FRAME_WINDOW *pFrameWindow, XExposeEvent *pEvent)
2559 {
2560 WB_RECT rct, rctExpose, rctTemp, rctPrev;
2561 WB_GEOM geom;
2562 GC gc;
2563 int i1;
2564 const char *pszStatus;
2565 
2566 
2567  if(!(WBFrameWindow_STATUS_BAR & pFrameWindow->wbFW.iFlags)) // window has a status bar?
2568  {
2569  return; // no status bar, just bail
2570  }
2571 
2572  // calculate the location of the status bar
2573 
2574  InternalCalcStatusBarRect(pFrameWindow, &rct);
2575 
2576  // does the Expose event intersect my status bar?
2577 
2578  rctExpose.left = pEvent->x;
2579  rctExpose.top = pEvent->y;
2580  rctExpose.right = rctExpose.left + pEvent->width;
2581  rctExpose.bottom = rctExpose.top + pEvent->height;
2582 
2583  if(!WBRectOverlapped(rct, rctExpose))
2584  {
2585 // WB_ERROR_PRINT("TEMPORARY: %s - expose event excludes status bar\n", __FUNCTION__);
2586 
2587  return; // do nothing (no overlap)
2588  }
2589 
2590  // intersect the two rectangles so I only re-paint what I have to
2591 
2592  if(rctExpose.top < rct.top)
2593  {
2594  rctExpose.top = rct.top;
2595  }
2596 
2597  if(rctExpose.bottom > rct.bottom)
2598  {
2599  rctExpose.bottom = rct.bottom;
2600  }
2601 
2602  if(rctExpose.left < rct.left)
2603  {
2604  rctExpose.left = rct.left;
2605  }
2606 
2607  if(rctExpose.right > rct.right)
2608  {
2609  rctExpose.right = rct.right;
2610  }
2611 
2612  // time to start painting
2613 
2614  geom.x = rctExpose.left;
2615  geom.y = rctExpose.top;
2616  geom.width = rctExpose.right - rctExpose.left;
2617  geom.height = rctExpose.bottom - rctExpose.top;
2618 
2619  gc = WBBeginPaintGeom(pFrameWindow->wbFW.wID, &geom);
2620 
2621  if(gc == None)
2622  {
2623  WB_ERROR_PRINT("ERROR: %s - no GC from WBBeginPaintGeom\n", __FUNCTION__);
2624  }
2625  else
2626  {
2627  XFontStruct *pFont = pFrameWindow->pFont;
2628  Display *pDisplay = WBGetWindowDisplay(pFrameWindow->wbFW.wID);
2629  XPoint xpt[3];
2630 
2631  // fill the rectangle with the background color
2632 
2633  WBClearWindow(pFrameWindow->wbFW.wID, gc); // should only paint my little rectangle
2634 
2635  // draw some lines in some colors for the border
2636 
2637  // paint the 3D-looking border
2638  XSetForeground(pDisplay, gc, clrBD2.pixel);
2639  xpt[0].x=rct.left;
2640  xpt[0].y=rct.bottom - 2; // exclude first point
2641  xpt[1].x=rct.left;
2642  xpt[1].y=rct.top;
2643  xpt[2].x=rct.right - 2; // exclude last point
2644  xpt[2].y=rct.top;
2645 
2646  XDrawLines(pDisplay, pFrameWindow->wbFW.wID, gc, xpt, 3, CoordModeOrigin);
2647 
2648  XSetForeground(pDisplay, gc, clrBD3.pixel);
2649  xpt[0].x=rct.right - 1;
2650  xpt[0].y=rct.top + 1; // exclude first point
2651  xpt[1].x=rct.right - 1;
2652  xpt[1].y=rct.bottom - 1;
2653  xpt[2].x=rct.left + 1; // exclude final point
2654  xpt[2].y=rct.bottom - 1;
2655 
2656  XDrawLines(pDisplay, pFrameWindow->wbFW.wID, gc, xpt, 3, CoordModeOrigin);
2657 
2658  XSetForeground(pDisplay, gc, clrFG.pixel); // by convention, set it to FG [for text I need this]
2659 
2660  if(pFont)
2661  {
2662  rctTemp.left = rct.left + 8; // this is the outside bounding rectangle
2663  rctTemp.top = rct.top + 2; // for the entire status bar.
2664  rctTemp.right = rct.right - 8;
2665  rctTemp.bottom = rct.bottom - 2;
2666 
2667  if(!pFrameWindow->pStatusBarTabs || !pFrameWindow->nStatusBarTabs) // fixed tab width (or no tabs)
2668  {
2669  int iFixedTab = pFrameWindow->nStatusBarTabs;
2670 
2671  if(!iFixedTab)
2672  {
2673  iFixedTab = DEFAULT_STATUS_BAR_TAB;
2674  }
2675 
2676  iFixedTab *= pFrameWindow->nAvgCharWidth;
2677 
2678  pszStatus = pFrameWindow->szStatus;
2679 
2680  if(!pszStatus)
2681  {
2682  pszStatus = DEFAULT_STATUS_STRING; // default status text when none defined
2683  }
2684 
2685  DTDrawSingleLineText(pFrameWindow->fontSet,
2686  pszStatus, pDisplay,
2687  gc, pFrameWindow->wbFW.wID,
2688  iFixedTab, 0, // fixed tab widths
2690  }
2691  else
2692  {
2693  char **ppCols = NULL;
2694  char *pData = NULL;
2695  struct __status_tab_cols__ *pTabs;
2696  int nCol;
2697 
2698  // create the tab and column list using a utility (to simplify THIS code)
2699 
2700  if(!__internal_do_status_tab_cols(pFrameWindow, &rctTemp, &ppCols, &pData, &pTabs, &nCol))
2701  {
2702  memcpy(&rctPrev, &rctTemp, sizeof(rctTemp));
2703 
2704  for(i1=0; i1 < nCol && ppCols[i1]; i1++)
2705  {
2706  rctTemp.left = pTabs[i1].left;
2707  rctTemp.right = pTabs[i1].right;
2708 
2709  DTDrawSingleLineText(pFrameWindow->fontSet, ppCols[i1],
2710  pDisplay, gc, pFrameWindow->wbFW.wID,
2711  0, 0, // no tabs (won't be any)
2712  &rctTemp, DTAlignment_VCENTER | pTabs[i1].align);
2713 
2714  rctTemp.top = rctPrev.top;
2715  rctTemp.bottom = rctPrev.bottom; // restore these two 'just in case'
2716  }
2717 
2718  // WBFree the 3 'WBAlloc'd arrays
2719  WBFree(ppCols);
2720  WBFree(pData);
2721  WBFree(pTabs);
2722  }
2723  }
2724  }
2725 
2726  WBEndPaint(pFrameWindow->wbFW.wID, gc); // and that's it!
2727  }
2728 
2729  // finally, alter the expose event slightly so that it reflects the 'painted' area
2730 
2731  if(pEvent->height + pEvent->x > rct.top)
2732  {
2733 // WB_ERROR_PRINT("TEMPOPRARY: %s - altering height in Expose event\n", __FUNCTION__);
2734 
2735  pEvent->height = rct.top - pEvent->x;
2736  if(pEvent->height < 0)
2737  {
2738  pEvent->height = 0;
2739  }
2740  }
2741 
2742  // TODO: manage top, left, right and x, y, width? probably don't need to
2743 
2744 }
2745 
2746 
2747 int __internal_do_status_tab_cols(FRAME_WINDOW *pFrameWindow, const WB_RECT *prct, char ***pppCols, char **ppData,
2748  struct __status_tab_cols__ **ppTabs, int *pnCol)
2749 {
2750 int i1, i2, i3, i4, iTab, nCol;
2751 char *p1;
2752 
2753 
2754 // if(!pFrameWindow->pStatusBarTabs || !pFrameWindow->nStatusBarTabs) // fixed tab width (or no tabs)
2755 
2756  // this is where things get a bit more fun. Look through 'pStatusBarTabs' for
2757  // left-aligned and right-aligned tabs. Sort them accordingly to determine the width
2758  // of each column. THEN, create the 'pTabs' and associated 'pCols' entries from that
2759  // so that strings and columns align to one another.
2760 
2761  // step 1: how many columns?
2762 
2763  *ppTabs = NULL;
2764  *pppCols = NULL;
2765 
2766  if(pFrameWindow->szStatus)
2767  {
2768  *ppData = WBCopyString(pFrameWindow->szStatus);
2769  }
2770  else
2771  {
2772  *ppData = WBCopyString(DEFAULT_STATUS_STRING); // default status text when none defined
2773  }
2774 
2775  if(!*ppData)
2776  {
2777 error_return:
2778 
2779  *pnCol = 0;
2780 
2781  WB_ERROR_PRINT("ERROR: %s - no memory\n", __FUNCTION__);
2782 
2783  return 1; // error return
2784  }
2785 
2786 
2787  p1 = *ppData;
2788  nCol = 0;
2789 
2790  if(*p1)
2791  {
2792  for(nCol = 1; *p1; )
2793  {
2794  while(*p1 && *p1 != '\t')
2795  {
2796  p1++;
2797  }
2798 
2799  if(*p1 == '\t')
2800  {
2801  *p1 = 0;
2802 
2803  nCol++;
2804  p1++;
2805  }
2806  }
2807  }
2808 
2809  if(nCol > pFrameWindow->nStatusBarTabs)
2810  {
2811  nCol = pFrameWindow->nStatusBarTabs; // this may get even smaller
2812  }
2813 
2814  *pnCol = nCol;
2815 
2816  // 'nCol' is now the total # of columns I'll be printing to. Allocate pppCols now
2817 
2818  *pppCols = (char **)WBAlloc(sizeof(char *)*(nCol + 2));
2819 
2820  if(!*pppCols)
2821  {
2822 error_return2:
2823 
2824  WBFree(*ppData);
2825  *ppData = NULL;
2826 
2827  goto error_return;
2828  }
2829 
2830  *ppTabs = (struct __status_tab_cols__ *)WBAlloc(sizeof(**ppTabs) * (pFrameWindow->nStatusBarTabs + 1));
2831  if(!*ppTabs)
2832  {
2833  WBFree(*pppCols);
2834  *pppCols = NULL;
2835 
2836  goto error_return2;
2837  }
2838 
2839  // time to build up 'pppCols'
2840  p1 = *ppData;
2841 
2842  for(i1=0; i1 < nCol; i1++)
2843  {
2844  (*pppCols)[i1] = p1;
2845 
2846  p1 += strlen(p1) + 1;
2847  }
2848 
2849  // now, set up the tabs so the represent 'columns'. 'left-oriented' tabs will assign
2850  // the 'left' member and 'right' will be -1. 'right-oriented' tabs will assign the
2851  // 'right' member and 'left' will be -1. Then I'll generate columns from the other tab info.
2852 
2853  for(i1=0, iTab=0; i1 < pFrameWindow->nStatusBarTabs; i1++)
2854  {
2855  i2 = pFrameWindow->pStatusBarTabs[i1];
2856  if(i2 & WBStatusTabInfo_BREAK) // a 'tab break', not an actual column
2857  {
2858  continue;
2859  }
2860 
2862  {
2863  (*ppTabs)[iTab].left = -1;
2864  (*ppTabs)[iTab].right = prct->right
2865  - (i2 & WBStatusTabInfo_MASK) * pFrameWindow->nAvgCharWidth;
2866  (*ppTabs)[iTab].align = i2 & WBStatusTabInfo_JUSTIFY_MASK;
2867  }
2868  else
2869  {
2870  (*ppTabs)[iTab].left = prct->left
2871  + (i2 & WBStatusTabInfo_MASK) * pFrameWindow->nAvgCharWidth;
2872  (*ppTabs)[iTab].right = -1;
2873  (*ppTabs)[iTab].align = i2 & WBStatusTabInfo_JUSTIFY_MASK;
2874  }
2875 
2876  iTab++; // increment total count of actual tabs
2877  }
2878 
2879  *pnCol = iTab; // the real # of columns when I have tabs specified
2880 
2881 
2882  // NOW, go through this list and determine how wide each column should be
2883  //
2884 
2885  for(i1=0; i1 < iTab; i1++)
2886  {
2887  if((*ppTabs)[i1].left < 0) // RTL column
2888  {
2889  i3 = prct->left; // use the left edge for my new 'left'. initially
2890  }
2891  else
2892  {
2893  i3 = prct->right; // use the right edge for my new 'right'. initially
2894  }
2895 
2896  for(i2=0; i2 < pFrameWindow->nStatusBarTabs; i2++)
2897  {
2898  // find the minimum 'right' or maximum 'left' position for this column
2899 
2900  i4 = pFrameWindow->pStatusBarTabs[i2];
2901 
2903  {
2904  i4 = prct->right - (i4 & WBStatusTabInfo_MASK) * pFrameWindow->nAvgCharWidth;
2905  }
2906  else
2907  {
2908  i4 = prct->left + (i4 & WBStatusTabInfo_MASK) * pFrameWindow->nAvgCharWidth;
2909  }
2910 
2911  if((*ppTabs)[iTab].left < 0) // right-justified tab
2912  {
2913  if(i4 >= (*ppTabs)[iTab].right) // don't do this, it's further right than me
2914  {
2915  continue;
2916  }
2917 
2918  if(i4 >= i3) // further right than the most current 'left'?
2919  {
2920  i3 = i4 + 1; // note I add 1 to this for column spacing
2921  }
2922  }
2923  else
2924  {
2925  if(i4 <= (*ppTabs)[iTab].left) // don't do this, it's further left than me
2926  {
2927  continue;
2928  }
2929 
2930  if(i4 <= i3) // further left than the most current 'right'?
2931  {
2932  i3 = i4 - 1; // note I add 1 to this for column spacing
2933  }
2934  }
2935  }
2936 
2937  if((*ppTabs)[i1].left < 0) // RTL column
2938  {
2939  (*ppTabs)[i1].left = i3;
2940  }
2941  else
2942  {
2943  (*ppTabs)[i1].right = i3;
2944  }
2945  }
2946 
2947  return 0; // ok!
2948 }
2949 
2950 
2951 int FWDefaultCallback(Window wID, XEvent *pEvent)
2952 {
2953  FRAME_WINDOW *pFrameWindow;
2954  Window wIDMenu;
2955  int iRval = 0;
2956 #ifndef NO_DEBUG
2957  char tbuf[32]; // for keyboard input
2958  int nChar = sizeof(tbuf);
2959 #endif // NO_DEBUG
2960 
2961 
2962  if(pEvent->type == DestroyNotify)
2963  {
2964  if(pEvent->xdestroywindow.window != wID)
2965  {
2966  XEvent evt;
2967 
2968 // WB_ERROR_PRINT("TEMPORARY: %s - DestroyNotify on window %d (%08xH), window %d (%08xH)\n",
2969 // __FUNCTION__,
2970 // (int)pEvent->xdestroywindow.window, (int)pEvent->xdestroywindow.window,
2971 // (int)wID, (int)wID);
2972 
2973  memcpy(&evt, pEvent, sizeof(evt));
2974 
2975  evt.xany.window = pEvent->xdestroywindow.window;
2976 
2977  WBDispatch(&evt); // sends a copy directly to the child window, saying "you are destroyed"
2978  // it should be harmless to do this. It appears that when a window accepts 'sub-structure notify'
2979  // types of events, the owned windows don't get the Destroy Notify events.
2980  }
2981  }
2982 
2983  pFrameWindow = (FRAME_WINDOW *)FWGetFrameWindowStruct(wID);
2984 
2985  if(!pFrameWindow)
2986  {
2987  WB_ERROR_PRINT("ERROR: %s - no frame window pointer!\n", __FUNCTION__);
2988 
2989  return 0;
2990  }
2991 
2992  wIDMenu = WBGetMenuWindow(wID);
2993 
2994  // TODO: message re-direction to children BEFORE 'pFWCallback'
2995 
2996  if(wIDMenu) // only if there's a menu window
2997  {
2998  switch(pEvent->type)
2999  {
3000  case ButtonPress:
3001  case ButtonRelease:
3002  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3003  "%s - BUTTON PRESS/RELEASE\n", __FUNCTION__);
3004 
3005  // check tab bar first since I might be grabbing the mouse
3006  if(WBPointInRect(pEvent->xbutton.x, pEvent->xbutton.y, pFrameWindow->rctTabBar) ||
3007  (pFrameWindow->nTabBarButtonFlags & tab_bar_button_GRAB))
3008  {
3009  return Internal_Tab_Bar_Event(pFrameWindow, pEvent);
3010  }
3011 
3012  // check for mouse position within menu window 'wIDMenu'
3013  if(WBPointInWindow(pEvent->xbutton.window, pEvent->xbutton.x, pEvent->xbutton.y, wIDMenu))
3014  {
3015  return WBWindowDispatch(wIDMenu, pEvent); // menu window should handle THESE mousie events
3016  }
3017 
3018  break;
3019 
3020  case MotionNotify:
3021  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3022  "%s - MOTION NOTIFY\n", __FUNCTION__);
3023 
3024  // check tab bar first since I might be grabbing the mouse
3025  if(WBPointInRect(pEvent->xbutton.x, pEvent->xbutton.y, pFrameWindow->rctTabBar) ||
3026  (pFrameWindow->nTabBarButtonFlags & tab_bar_button_GRAB))
3027  {
3028  return Internal_Tab_Bar_Event(pFrameWindow, pEvent);
3029  }
3030 
3031  if(WBPointInWindow(pEvent->xmotion.window, pEvent->xmotion.x, pEvent->xmotion.y, wIDMenu))
3032  {
3033  return(WBWindowDispatch(wIDMenu, pEvent)); // menu window should handle mousie events
3034  }
3035 
3036  break;
3037 
3038  case ConfigureNotify: // window size/position change
3039  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3040  "%s - CONFIGURE NOTIFY\n", __FUNCTION__);
3041 
3042  // allow message to process first, and post a message to myself
3043  // to re-evaluate the layout. This is to avoid having a window that's
3044  // not "changed" yet trying to update its size info when size info is
3045  // not yet accurate.
3046  //
3047  // ALSO - I can get this on a window MOVE without a resize...
3048 
3049  {
3050  Display *pDisplay;
3051  XClientMessageEvent evt;
3052 
3053  pDisplay = WBGetWindowDisplay(wID);
3054 
3055  bzero(&evt, sizeof(evt));
3056  evt.type = ClientMessage;
3057 
3058  evt.display = pDisplay;
3059  evt.window = wID;
3060  evt.message_type = aRESIZE_NOTIFY;
3061  evt.format = 32; // always
3062  evt.data.l[0] = pEvent->xconfigure.x;
3063  evt.data.l[1] = pEvent->xconfigure.y;
3064  evt.data.l[2] = pEvent->xconfigure.x + pEvent->xconfigure.width; // right
3065  evt.data.l[3] = pEvent->xconfigure.y + pEvent->xconfigure.height; // bottom
3066  evt.data.l[4] = pEvent->xconfigure.border_width; // RESERVED (for now, just do it)
3067 
3068  WBPostEvent(wID, (XEvent *)&evt); // NOTE: if too slow, post 'priority' instead
3069  }
3070 
3071  break;
3072 
3073 
3074  case ClientMessage: // menus, etc. (they generate the 'ClientMessage')
3075 
3076 #ifndef NO_DEBUG
3077  {
3078  char *p1 = WBGetAtomName(WBGetWindowDisplay(wID), pEvent->xclient.message_type);
3079 
3080  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3081  "%s - CLIENT MESSAGE: %s\n", __FUNCTION__, p1);
3082 
3083  if(p1)
3084  {
3085  WBFree(p1);
3086  }
3087  }
3088 #endif // NO_DEBUG
3089 
3090  if(pEvent->xclient.message_type == aSET_FOCUS)
3091  {
3092  // set focus to window in data.l[0], or "default" if it's 'None'
3093 
3094  if(pEvent->xclient.data.l[0] == None)
3095  {
3096  // do I have a 'focus' child frame?
3097  WBChildFrame *pFocus = FWGetFocusWindow(&(pFrameWindow->wbFW));
3098 
3099  if(pFocus)
3100  {
3101  WBSetInputFocus(pFocus->wID);
3102  }
3103  else
3104  {
3105  WBSetInputFocus(wID); // set input focus to myself
3106  }
3107  }
3108  else
3109  {
3110  WBSetInputFocus((Window)pEvent->xclient.data.l[0]);
3111  }
3112  }
3113  else if(pEvent->xclient.message_type == aRESIZE_NOTIFY)
3114  {
3115  FWRecalcLayout(wID);
3116 
3117  return 1; // handled (TODO: send to user-defined callback as well?)
3118  }
3119  else if(pEvent->xclient.message_type == aMENU_COMMAND)
3120  {
3122  // _ _ _ //
3123  // _ __ ___ ___ _ __ _ _ | |__ __ _ _ __ __| | | ___ _ __ //
3124  // | '_ ` _ \ / _ \ '_ \| | | | | '_ \ / _` | '_ \ / _` | |/ _ \ '__| //
3125  // | | | | | | __/ | | | |_| | | | | | (_| | | | | (_| | | __/ | //
3126  // |_| |_| |_|\___|_| |_|\__,_| |_| |_|\__,_|_| |_|\__,_|_|\___|_| //
3127  // //
3129 
3130 
3131  // check the container window that has the current focus, and THEN
3132  // check the frame window for an appropriate handler
3133 
3134  WBChildFrame *pFocus = FWGetFocusWindow(&(pFrameWindow->wbFW));
3135 
3136  if(pFocus) // if a tab has a focus, use that window's event handler first
3137  {
3138  int iRet = WBWindowDispatch(pFocus->wID, pEvent);
3139 
3140  if(iRet) // non-zero return
3141  {
3142  WB_ERROR_PRINT("TEMPORARY: %s - child frame handles the menu\n", __FUNCTION__);
3143 
3144  return iRet; // the handler MUST return non-zero if the message should NOT be processed by the frame!
3145  }
3146  }
3147 
3148  if(pFrameWindow->pMenuHandler)
3149  {
3150  // search for the matching menu or ID - anything above 0x10000 is assumed to be a pointer
3151  const WBFWMenuHandler *pHandler = pFrameWindow->pMenuHandler;
3152 
3153  while(pHandler->lMenuID) // zero marks the end
3154  {
3155  long lID = pHandler->lMenuID;
3156 
3157  if(pHandler->lMenuID >= 0x10000L) // TODO: this isn't compatible with the WBGetAtom paradigm...
3158  {
3159 #warning this is potentially dangerous code. find a better way of managing this (lMenuID as const char *)
3160  lID = WBGetAtom(WBGetDefaultDisplay(), (const char *)lID);
3161 
3162  if(!lID)
3163  {
3164  continue;
3165  }
3166  }
3167 
3168  if(pEvent->xclient.data.l[0] == lID)
3169  {
3170  if(pHandler->callback)
3171  {
3172  if(pHandler->callback(&(pEvent->xclient)))
3173  {
3174  return 1;
3175  }
3176  }
3177 
3178  return 0; // NOT handled or handler returned zero
3179  }
3180 
3181  pHandler++;
3182  }
3183  }
3184 
3185  // flow through, likely returning 0
3186  }
3187  else if(pEvent->xclient.message_type == aMENU_UI_COMMAND)
3188  {
3190  // _ _ ___ _ _ _ //
3191  // _ __ ___ ___ _ __ _ _ | | | |_ _| | |__ __ _ _ __ __| | | ___ _ __ //
3192  // | '_ ` _ \ / _ \ '_ \| | | | | | | || | | '_ \ / _` | '_ \ / _` | |/ _ \ '__| //
3193  // | | | | | | __/ | | | |_| | | |_| || | | | | | (_| | | | | (_| | | __/ | //
3194  // |_| |_| |_|\___|_| |_|\__,_| \___/|___| |_| |_|\__,_|_| |_|\__,_|_|\___|_| //
3195  // //
3197 
3198  // check 'contained' window for an appropriate UI handler before passing
3199  // it off to the frame window's handler
3200 
3201  WBChildFrame *pFocus = FWGetFocusWindow(&(pFrameWindow->wbFW));
3202 
3203  if(pFocus)
3204  {
3205  int iRet = WBWindowDispatch(pFocus->wID, pEvent);
3206 
3207  if(iRet != 0)
3208  {
3209  WB_ERROR_PRINT("TEMPORARY: %s - child frame handles the menu UI thingy\n", __FUNCTION__);
3210  return iRet;
3211  }
3212 
3213  // TODO: determine if a '-1' value really SHOULD grey out the menu choice anyway or
3214  // if I should return some OTHER value to differentiate 'grey' from 'no handler'
3215  }
3216 
3217  if(pFrameWindow->pMenuHandler)
3218  {
3219  const WBFWMenuHandler *pHandler = pFrameWindow->pMenuHandler;
3220 
3221  while(pHandler->lMenuID) // zero marks the end
3222  {
3223  long lID = pHandler->lMenuID;
3224 
3225  if(pHandler->lMenuID >= 0x10000L)
3226  {
3227 #warning this is potentially dangerous code - find a better way of managing this (lMenuID as const char *)
3228  lID = WBGetAtom(WBGetDefaultDisplay(), (const char *)lID);
3229 
3230  if(!lID)
3231  {
3232  continue;
3233  }
3234  }
3235 
3236  if(pEvent->xclient.data.l[0] == lID) // a message handler exists
3237  {
3238  if(pHandler->UIcallback)
3239  {
3240  WBMenu *pMenu;
3241  WBMenuItem *pItem;
3242 
3243  // important detail - the 'data.l' array is assumed to have 32-bit values in it,
3244  // regardless of how its definition and 64/32-bitness affects the actual data storage.
3245  // In effect, only the lower 32-bits is valid. Hence, I must combine two 32-bit values
3246  // together in order to make a 64-bit pointer. For consistency I always use 2 values
3247  // per pointer to pass the information via the message structure. otherwise it gets
3248  // complicated and I really don't like complicated. it causes mistakes, errors, crashes...
3249 
3250 //#if !defined(__SIZEOF_POINTER__) // TODO find a better way to deal with pointer size
3251 //#define __SIZEOF_POINTER__ 0
3252 //#endif
3253 //#if __SIZEOF_POINTER__ == 4 /* to avoid warnings in 32-bit linux */
3254 // pMenu = (WBMenu *)pEvent->xclient.data.l[1];
3255 // pItem = (WBMenuItem *)pEvent->xclient.data.l[3];
3256 //#else // assume 64-bit pointers here, and if they truncate, should NOT get any warnings... well that's the idea
3257 // pMenu = (WBMenu *)((unsigned long long)pEvent->xclient.data.l[1] | ((unsigned long long)pEvent->xclient.data.l[2] << 32));
3258 // pItem = (WBMenuItem *)((unsigned long long)pEvent->xclient.data.l[3] | ((unsigned long long)pEvent->xclient.data.l[4] << 32));
3259 //#endif
3260 // // TODO: validate pointers, otherwise a posted message might crash me (like a vulnerability)
3261 //
3262 //#ifndef NO_DEBUG /* warning off for release build */
3263 //#warning this code potentially has a vulnerability in it - 'pMenu' and 'pItem' could be bad pointers
3264 //#endif // !NO_DEBUG
3265 
3266  pMenu = (WBMenu *)(pEvent->xclient.data.l[1] ? WBGetPointerFromHash(pEvent->xclient.data.l[1]) : NULL);
3267  pItem = (WBMenuItem *)(pEvent->xclient.data.l[2] ? WBGetPointerFromHash(pEvent->xclient.data.l[2]) : NULL);
3268 
3269  if(!pMenu || !pItem)
3270  {
3271  WB_ERROR_PRINT("** ERROR: %s - window=%08xH pMenu=%p, pItem=%p\n", __FUNCTION__, (int)wID, pMenu, pItem);
3272 // WBDebugDumpEvent(pEvent);
3273  }
3274 // if(!WBIsValidMenu(pMenu) || !WBIsValidMenuItem(pItem))
3275 // {
3276 // WB_ERROR_PRINT("ERROR: %s - invalid pMenu=%p or pItem=%p\n", __FUNCTION__, pMenu, pItem);
3277 // }
3278  else
3279  {
3280  return pHandler->UIcallback(pMenu, pItem);
3281  }
3282  }
3283 
3284  return 1; // NO UI handler so return '1' [aka 'enable']
3285  }
3286 
3287  pHandler++;
3288  }
3289  }
3290 
3291  return 0; // if there's no handler and no UI handler, always return 0 (not handled)
3292  }
3293  else if(pEvent->xclient.message_type == aWM_CHAR) // hotkeys for menu-related things
3294  {
3296  // //
3297  // _____ _ ___ __ __ _____ _ _ _ _ _ ____ _____ ___ __ __ _ _____ ___ ___ _ _ //
3298  // | ___|/ | / _ \ | \/ || ____|| \ | || | | | / \ / ___||_ _||_ _|\ \ / // \ |_ _||_ _|/ _ \ | \ | | //
3299  // | |_ | || | | | | |\/| || _| | \| || | | | / _ \ | | | | | | \ \ / // _ \ | | | || | | || \| | //
3300  // | _| | || |_| | | | | || |___ | |\ || |_| | / ___ \| |___ | | | | \ V // ___ \ | | | || |_| || |\ | //
3301  // |_| |_| \___/ |_| |_||_____||_| \_| \___/ /_/ \_\\____| |_| |___| \_//_/ \_\|_| |___|\___/ |_| \_| //
3302  // //
3303  // //
3305 
3306  // looking for F10 [pops up the menu at the first position] and SHIFT+F10 [context menu for child window]
3307 
3308  if(pEvent->xclient.data.l[0] == XK_F10 && // keystroke is F10
3309  (pEvent->xclient.data.l[1] & WB_KEYEVENT_KEYSYM)) // keystroke is a symbol
3310  {
3311  int iACS = (int)pEvent->xclient.data.l[1] & WB_KEYEVENT_ACSMASK;
3312 
3313  if(iACS == WB_KEYEVENT_SHIFT)
3314  {
3315  // send a command to pop up the context menu
3316 
3317  return 1; // handled
3318  }
3319  else if(!iACS) // no ctrl+alt+shift
3320  {
3321  // send command to pop up the frame's menu
3322 
3323  XClientMessageEvent evt;
3324 
3325  // post a high-priority message to myself to display the menu
3326 
3327  bzero(&evt, sizeof(evt));
3328  evt.type = ClientMessage;
3329  evt.display = WBGetWindowDisplay(wIDMenu);
3330  evt.window = wIDMenu;
3331  evt.message_type = aMENU_ACTIVATE;
3332  evt.format = 32;
3333  evt.data.l[0] = 0;
3334  evt.data.l[1] = 0;
3335 
3336  WBPostPriorityEvent(wIDMenu, (XEvent *)&evt);
3337 
3338  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Menu | DebugSubSystem_Frame,
3339  "%s - posting client event message to display menu\n", __FUNCTION__);
3340 
3341  return 1; // handled
3342  }
3343  }
3344  }
3345 #ifndef NO_DEBUG
3346  else
3347  {
3348  char *p1 = WBGetAtomName(WBGetWindowDisplay(wID), pEvent->xclient.message_type);
3349 
3350  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3351  "%s - Client message %s not handled by frame window\n",
3352  __FUNCTION__, p1);
3353 
3354  if(p1)
3355  {
3356  WBFree(p1);
3357  }
3358  }
3359 #endif // NO_DEBUG
3360 
3361  break;
3362 
3363  default:
3364  break;
3365  }
3366  }
3367  else // no main menu, like for a dialog-based or form-based application
3368  {
3369  if(pEvent->xclient.message_type == aWM_CHAR) // hotkeys for menu-related things
3370  {
3372  // //
3373  // _____ _ ___ __ __ _____ _ _ _ _ _ ____ _____ ___ __ __ _ _____ ___ ___ _ _ //
3374  // | ___|/ | / _ \ | \/ || ____|| \ | || | | | / \ / ___||_ _||_ _|\ \ / // \ |_ _||_ _|/ _ \ | \ | | //
3375  // | |_ | || | | | | |\/| || _| | \| || | | | / _ \ | | | | | | \ \ / // _ \ | | | || | | || \| | //
3376  // | _| | || |_| | | | | || |___ | |\ || |_| | / ___ \| |___ | | | | \ V // ___ \ | | | || |_| || |\ | //
3377  // |_| |_| \___/ |_| |_||_____||_| \_| \___/ /_/ \_\\____| |_| |___| \_//_/ \_\|_| |___|\___/ |_| \_| //
3378  // //
3379  // //
3381 
3382  // looking for F10 [pops up the menu at the first position] and SHIFT+F10 [context menu for child window]
3383 
3384  if(pEvent->xclient.data.l[0] == XK_F10 && // keystroke is F10
3385  (pEvent->xclient.data.l[1] & WB_KEYEVENT_KEYSYM)) // keystroke is a symbol
3386  {
3387  int iACS = (int)pEvent->xclient.data.l[1] & WB_KEYEVENT_ACSMASK;
3388 
3389  if(iACS == WB_KEYEVENT_SHIFT)
3390  {
3391  // send a command to the focus window in order to pop up the context menu
3392 
3393  return 1; // handled
3394  }
3395  }
3396  }
3397  }
3398 
3399  // TAB BAR (but with no menu)
3400 
3401  if(!(WBFrameWindow_NO_TABS & pFrameWindow->wbFW.iFlags)) // I have a tab bar
3402  {
3403  if(pEvent->type == ButtonPress ||
3404  pEvent->type == ButtonRelease ||
3405  pEvent->type == MotionNotify)
3406  {
3407  if(WBPointInRect(pEvent->xbutton.x, pEvent->xbutton.y, pFrameWindow->rctTabBar) ||
3408  (pFrameWindow->nTabBarButtonFlags & tab_bar_button_GRAB)) // grabbing mouse means I get the event
3409  {
3410  return Internal_Tab_Bar_Event(pFrameWindow, pEvent);
3411  }
3412  }
3413  else if(pEvent->type == Expose)
3414  {
3415  InternalPaintTabBar(pFrameWindow, &(pEvent->xexpose)); // this will 'validate' the tab bar area, preventing re-paint
3416  }
3417  else if(pEvent->type == ClientMessage && // tab-related client messages
3418  pEvent->xclient.message_type == aTAB_MESSAGE)
3419  {
3420 // WB_ERROR_PRINT("TODO: %s - handle ClientMessage for the tab bar\n", __FUNCTION__);
3421 // WBDebugDumpEvent(pEvent);
3422  if(pEvent->xclient.data.l[0] == NEW_TAB_MESSAGE)
3423  {
3424  Display *pDisplay;
3425  XClientMessageEvent evt;
3426 
3427  pDisplay = WBGetWindowDisplay(wID);
3428 
3429  // post a WM_COMMAND ClientMessage event, for 'IDM_FILE_NEW'
3430  // TODO: do I want to pre-define things like this in the header?
3431 
3432  bzero(&evt, sizeof(evt));
3433  evt.type = ClientMessage;
3434 
3435  evt.display = pDisplay;
3436  evt.window = wID;
3437  evt.message_type = aMENU_COMMAND;
3438  evt.format = 32; // always
3439 
3440  evt.data.l[0] = WBGetAtom(pDisplay, FW_FILE_NEW_MENU); // post a 'FILE NEW' menu event so handlers can manage it
3441  evt.data.l[1] = 0; // no hashed pointer this time
3442 
3443  WBPostEvent(wID, (XEvent *)&evt);
3444  }
3445  else if(pEvent->xclient.data.l[0] == PREV_TAB_MESSAGE)
3446  {
3447  if(pFrameWindow->nFocusTab > 0)
3448  {
3449  FWSetFocusWindowIndex(&(pFrameWindow->wbFW), pFrameWindow->nFocusTab - 1);
3450  }
3451  }
3452  else if(pEvent->xclient.data.l[0] == NEXT_TAB_MESSAGE)
3453  {
3454  if((pFrameWindow->nFocusTab + 1) < pFrameWindow->nChildFrames)
3455  {
3456  FWSetFocusWindowIndex(&(pFrameWindow->wbFW), pFrameWindow->nFocusTab + 1);
3457  }
3458  }
3459  else if(pEvent->xclient.data.l[0] == SET_TAB_MESSAGE)
3460  {
3461  if((int)pEvent->xclient.data.l[1] >= 0 &&
3462  (int)pEvent->xclient.data.l[1] < pFrameWindow->nChildFrames)
3463  {
3464  FWSetFocusWindowIndex(&(pFrameWindow->wbFW), (int)pEvent->xclient.data.l[1]);
3465  }
3466  }
3467  else if(pEvent->xclient.data.l[0] == CLOSE_TAB_MESSAGE)
3468  {
3469  if((int)pEvent->xclient.data.l[1] >= 0 &&
3470  (int)pEvent->xclient.data.l[1] < pFrameWindow->nChildFrames)
3471  {
3472  WBChildFrame *pC = pFrameWindow->ppChildFrames[pEvent->xclient.data.l[1]];
3473 
3474  FWRemoveContainedWindow(&(pFrameWindow->wbFW), pC); // remove from frame
3475  FWDestroyChildFrame(pC); // destroys 'child frame' (and superclass, if it assigned a destructor)
3476  }
3477  }
3478 
3479  return 1; // handled
3480  }
3481  }
3482 
3483 
3484  // expose event for status bar - painting the status bar and tab bar.
3485 
3486  if(pEvent->type == Expose &&
3487  (WBFrameWindow_STATUS_BAR & pFrameWindow->wbFW.iFlags))
3488  {
3489  InternalPaintStatusBar(pFrameWindow, &(pEvent->xexpose)); // this will 'validate' the status bar area, preventing re-paint
3490  }
3491 
3492 
3493  // user callback function
3494 
3495  if(pFrameWindow->pFWCallback)
3496  {
3497  // for most messages, if I handle it here, I don't do further processing
3498 
3499  iRval = (pFrameWindow->pFWCallback)(wID, pEvent);
3500 
3501  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Event | DebugSubSystem_Frame,
3502  "%s - %s event and user callback returns %d\n", __FUNCTION__, WBEventName(pEvent->type), iRval);
3503 
3504  if(iRval)
3505  {
3506  // check message types that I do *NOT* want to 'bail out' for, as well as those
3507  // that I _MUST_ bail out for.
3508 
3509  switch(pEvent->type)
3510  {
3511  case DestroyNotify: // must process after user callback
3512  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3513  "%s DestroyNotify and user callback returned a non-zero value\n", __FUNCTION__);
3514 
3515  // CONTINUE PROCESSING - after the user's callback handles DestroyNotify I must handle
3516  // it here also (at the very end)
3517 
3518  break;
3519 
3520  case Expose: // no further processing needed, special debug notification
3521  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3522  "%s Expose event and user callback returns %d\n", __FUNCTION__, iRval);
3523  return iRval; // 'expose' event already handled
3524 
3525 
3526  default: // all other messages, no further processing needed
3527  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3528  "%s - %s event and user callback returns %d\n", __FUNCTION__, WBEventName(pEvent->type), iRval);
3529  return iRval;
3530  }
3531  }
3532  else
3533  {
3534  // TODO: deal with specific events NOT handled by the user callback
3535 
3536  if(pEvent->type == ClientMessage &&
3537  pEvent->xclient.message_type == aQUERY_CLOSE)
3538  {
3539  int i1;
3540  Display *pDisplay = WBGetWindowDisplay(wID);
3541 
3542  // note that 'Query Close' would have been processed already with an "ok to close" result
3543  // SO, check contained windows, and then destroy everything except the window itself
3544  if(pFrameWindow->ppChildFrames && pFrameWindow->nChildFrames > 0)
3545  {
3546  for(i1=0; i1 < pFrameWindow->nChildFrames; i1++)
3547  {
3548  XClientMessageEvent evt;
3549 
3550  bzero(&evt, sizeof(evt));
3551  evt.type = ClientMessage;
3552  evt.display = pDisplay;
3553  evt.window = pFrameWindow->ppChildFrames[i1]->wID;
3554  evt.message_type = aQUERY_CLOSE; // QUERY_CLOSE request
3555  evt.format = 32;
3556  evt.data.l[0] = 0; // "do not close yourself yet"
3557 
3558  if((int)WBWindowDispatch(evt.window, (XEvent *)&evt) > 0) // TODO: handle errors
3559  {
3560  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Event | DebugSubSystem_Frame,
3561  "%s - QUERY CLOSE returning 1 (child refuses to close)\n", __FUNCTION__);
3562 
3563  return 1; // "not ok" to close
3564  }
3565  }
3566  }
3567 
3568 // DLGMessageBox(MessageBox_OK | MessageBox_MiddleFinger, wID, "OK to close", "Checknig if ok to close");
3569 
3570  if(pEvent->xclient.data.l[0]) // meaning "delete your private data if it's ok to close, destroy imminent"
3571  {
3572  // children say "go for it" - so now I do it to myself
3573 
3574  if(pFrameWindow->ppChildFrames && pFrameWindow->nChildFrames > 0)
3575  {
3576  for(i1=pFrameWindow->nChildFrames - 1; i1 >= 0; i1--)
3577  {
3578  WB_ERROR_PRINT("TEMPORARY: %s - destroying child frame %d\n", __FUNCTION__, i1);
3579 
3580  WBChildFrame *pC = pFrameWindow->ppChildFrames[i1];
3581  pC->pOwner = NULL; // so it doesn't try to remove itself
3582 
3583  pFrameWindow->ppChildFrames[i1] = NULL; // so I don't try to do this again
3584  FWDestroyChildFrame(pC);
3585  }
3586 
3587  pFrameWindow->nChildFrames = 0;
3588  }
3589 
3590  // to help deal with the destruction, tell the registered window proc that
3591  // I am destroying the window by sending a 'DestroyNotify' to it.
3592 
3593  if(pFrameWindow->pFWCallback)
3594  {
3595  XDestroyWindowEvent evt;
3596 
3597  bzero(&evt, sizeof(evt));
3598  evt.type = DestroyNotify;
3599  evt.display = pDisplay;
3600  evt.event = evt.window = pFrameWindow->wbFW.wID;
3601 
3602  pFrameWindow->pFWCallback(evt.event, (XEvent *)&evt);
3603  }
3604 
3605  WB_ERROR_PRINT("TEMPORARY: %s - destroying internal data, etc.\n", __FUNCTION__);
3606 
3607  WBSetWindowData(wID, 0, NULL);
3608 
3609  __internal_destroy_frame_window(pFrameWindow); // this destroys the children
3610  WBFree(pFrameWindow);
3611 
3612  // NOTE: caller must unregister the callback, etc.
3613  }
3614 
3615  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3616  "%s - QUERY CLOSE returning zero\n", __FUNCTION__);
3617 
3618  return 0; // OK to close
3619  }
3620  }
3621  }
3622 
3623 
3624  // At this point iRval _COULD_ be non-zero (example, DestroyNotify)
3625  // so don't do any handling for those messages. For everything else,
3626  // a return value of ZERO means "not handled", so handle them.
3627 
3628  if(!iRval)
3629  {
3630  switch(pEvent->type)
3631  {
3632  case KeyPress:
3633  {
3634 #ifndef NO_DEBUG
3635  int iACS = 0;
3636  int iKey = WBKeyEventProcessKey((XKeyEvent *)pEvent, tbuf, &nChar, &iACS);
3637 
3638  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame | DebugSubSystem_Keyboard,
3639  "%s KEY PRESS for KEY %d KEYCODE %d MASK=%d (%xH)\n",
3640  __FUNCTION__, iKey, ((XKeyEvent *)pEvent)->keycode,
3641  ((XKeyEvent *)pEvent)->state, ((XKeyEvent *)pEvent)->state);
3642 #endif // NO_DEBUG
3643 
3644  // check for menu and hotkey activation.
3645 // if(nChar > 0) // only for "real" characters (i.e. not just the ALT key)
3646  {
3648 
3649  if(pMenuBar) // menu bar exists?
3650  {
3651  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Menu | DebugSubSystem_Frame | DebugSubSystem_Keyboard,
3652  "%s call to MBMenuProcessHotKey for menu window %d (%08xH)\n",
3653  __FUNCTION__, (int)pMenuBar->wSelf, (int)pMenuBar->wSelf);
3654 
3655  iRval = MBMenuProcessHotKey(pMenuBar->pMenu, (XKeyEvent *)pEvent);
3656  }
3657  }
3658  }
3659  break;
3660 
3661  case KeyRelease:
3662  {
3663  // KeyRelease
3664 #ifndef NO_DEBUG
3665  int iACS = 0, iKey = WBKeyEventProcessKey((XKeyEvent *)pEvent, tbuf, &nChar, &iACS);
3666 
3667  if(nChar > 0)
3668  {
3669  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame | DebugSubSystem_Keyboard,
3670  "%s KEY RELEASE for KEY %d KEYCODE %d MASK=%d (%xH)\n",
3671  __FUNCTION__, iKey, ((XKeyEvent *)pEvent)->keycode,
3672  ((XKeyEvent *)pEvent)->state, ((XKeyEvent *)pEvent)->state);
3673 
3674  }
3675 #endif // NO_DEBUG
3676 
3677  }
3678 
3679  break;
3680 
3681 
3682 
3683  case SelectionRequest:
3684  case SelectionClear:
3685  case SelectionNotify:
3686 
3687  return 0; // NOT handled (default handler might want to handle them, but not me)
3688 
3689 // return FWDoSelectionEvents(&(pFrameWindow->wbFW), wID, wIDMenu, pEvent); <-- no longer needed
3690  }
3691  }
3692 
3693 
3694  // TODO: message re-direction to children AFTER 'pFWCallback'
3695 
3696 
3697 
3698  // ----------------------------------------
3699  // DESTROY DESTROY DESTROY DESTROY DESTROY
3700  //
3701  // special handling for 'DestroyNotify'
3702  //
3703  // DESTROY DESTROY DESTROY DESTROY DESTROY
3704  // ----------------------------------------
3705 
3706 
3707  if(pEvent->type == DestroyNotify &&
3708  pEvent->xdestroywindow.window == wID)
3709  {
3710 // int boolQuitFlag = (pFrameWindow->wbFW.iFlags & WBFrameWindow_APP_WINDOW) != 0;
3711 
3712  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Frame,
3713  "%s - DestroyNotify\n", __FUNCTION__);
3714 
3715  WBSetWindowData(wID, 0, NULL);
3716 
3717  __internal_destroy_frame_window(pFrameWindow);
3718 
3719  WBFree(pFrameWindow);
3720 
3721 // if(boolQuitFlag)
3722 // bQuitFlag = TRUE; // set the global 'quit' flag if I'm an application top-level window
3723 
3724  WB_DEBUG_PRINT(DebugLevel_ERROR/*DebugLevel_Verbose | DebugSubSystem_Event | DebugSubSystem_Frame*/,
3725  "%s - frame window destroyed\n", __FUNCTION__);
3726  return 1;
3727  }
3728 
3729  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Event | DebugSubSystem_Frame,
3730  "%s - frame window callback returns %d\n", __FUNCTION__, iRval);
3731 
3732  return iRval; // return back the 'handled' status
3733 }
3734 
3735 
3736 
void FWReplaceContainedWindow(WBFrameWindow *pFW, WBChildFrame *pCont, WBChildFrame *pContNew)
Replace a &#39;contained&#39; window from a frame window.
structure for managing menu items
Definition: menu.h:185
void WBSetWMProtocols(Window wID, Atom aProperty,...)
re-assign standard WM (Window Manager) &#39;window type&#39; properties and notify the root window (reserved)...
set this to disable tabs (single child frame only)
Definition: frame_window.h:290
void * WBGetPointerFromHash(WB_UINT32 uiHash)
Obtain a pointer from a 32-bit &#39;secure&#39; pointer hash value.
&#39;window helper&#39; main header file for the X11workbench Toolkit API
static __inline__ Display * WBGetDefaultDisplay(void)
Returns the default Display.
void FWRemoveContainedWindow(WBFrameWindow *pFW, WBChildFrame *pCont)
Removes a &#39;contained&#39; window from a frame window. Does not destroy the &#39;contained&#39; window...
#define WB_DEBUG_PRINT(L,...)
Preferred method of implementing conditional debug output.
Definition: debug_helper.h:196
set this flag for application top-level window and whenever it is destroyed the application will exit...
Definition: frame_window.h:288
Utilities for copying and drawing text, determining text extents, and so on.
void FWDestroyFrameWindow2(WBFrameWindow *pFrameWindow)
Function to destroy a frame window based on the WBFrameWindow structure.
Definition: frame_window.c:689
#define FWGetFocusWindow(pFW)
A macro to aid code readability, returning the Window ID of the contained window that has the focus...
Definition: frame_window.h:926
bold font weight (mutually exclusive0
Definition: font_helper.h:417
int WBKeyEventProcessKey(const XKeyEvent *pEvent, char *pBuf, int *pcbLen, int *piAltCtrlShift)
Generic keyboard event translation utility.
unsigned int ulTag
tag indicating I&#39;m a frame window
Definition: frame_window.h:266
int WBUnmapWindow(Display *pDisplay, Window wID)
wrapper for XUnmapWindow, makes window invisible without destroying it
Window wID
Window id for the frame window.
Definition: frame_window.h:267
#define LOAD_COLOR0(X, Y)
macro to load a color, mostly for readability
#define FW_FILE_NEW_MENU
Definition: child_frame.h:105
void FWDestroyFrameWindow(Window wID)
Function to destroy a frame window based on the Window id.
Definition: frame_window.c:684
void DTDrawSingleLineText(XFontSet fontSet, const char *szText, Display *pDisplay, GC gc, Drawable dw, int iTabWidth, int iTabOrigin, const WB_RECT *prcBounds, int iAlignment)
draw single-line text
Definition: draw_text.c:1282
char * szDisplayName
display name shown in tab and title bar. You should not alter this member directly.
Definition: frame_window.h:432
WBFrameWindow * pOwner
a pointer to the WBFrameWindow owner
Definition: frame_window.h:413
int FWGetChildFrameIndex(WBFrameWindow *pFW, WBChildFrame *pCont)
Sets the focus to a specific contained window using its tab order index.
void MBDestroyMenuBarWindow(WBMenuBarWindow *pMenuBar)
Destroy a &#39;Menu Bar&#39; window.
Definition: menu_bar.c:472
#define FRAME_WINDOW_TAG
TAG for the WBFrameWindow structure.
Definition: frame_window.h:180
void WBDraw3DBorderRect(Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomBorder, unsigned long lBorderColor1, unsigned long lBorderColor2)
Draw a 3D &#39;border&#39; rectangle.
Atom aQUERY_CLOSE
query if it&#39;s ok to close (and optionally destroy yourself if ok) a window
void WBGetWindowRect(Window wID, WB_RECT *pRect)
Returns the WB_RECT (rectangle) defined by the window&#39;s geometry, including the border area...
XFontStruct * WBLoadModifyFont(Display *pDisplay, const XFontStruct *pOriginal, int iFontSize, int iFlags)
load and modify a font according to the specified size and flags
Definition: font_helper.c:1350
Atom aMENU_UI_COMMAND
UI notifications sent by menus to owning Frame windows via ClientMessage using &#39;WBWindowDispatch&#39;.
mask for the &#39;justification&#39; value
Definition: frame_window.h:305
#define WB_MOUSE_INPUT_MASK
&#39;Mouse&#39; input mask, bit flag for window creation
static __inline__ WBFrameWindow * FWGetFrameWindowStruct(Window wID)
Obtain the associated WBFrameWindow structure pointer for a frame window&#39;s Window ID...
Definition: frame_window.h:819
int WBWindowDispatch(Window wID, XEvent *pEvent)
Dispatches a window XEvent. May be called directly.
#define WB_KEYBOARD_INPUT_MASK
&#39;Keyboard&#39; input mask, bit flag for window creation
int WBPointInWindow(Window wIDRef, int iX, int iY, Window wIDQuery)
Returna a non-zero value if X,Y coordinates relative to the reference window are within the query win...
void MBSetMenuBarMenuResource(WBMenuBarWindow *pMenuBar, const char *pszResource)
Assign a new WBMenu for a Menu Bar window.
Definition: menu_bar.c:491
GC WBBeginPaintGeom(Window wID, WB_GEOM *pgBounds)
&#39;Paint&#39; helper, creates a GC for use in updating the window for a specified rectangular area ...
Structure that defines a Child Frame within a Frame Window.
Definition: frame_window.h:409
structure for managing menu items
Definition: menu.h:127
const char * WBEventName(int iEventID)
debug function to return the name of an X11 event
XFontSet WBFontSetFromFont(Display *pDisplay, const XFontStruct *pFont)
Creates an &#39;XFontSet&#39; from an XFontStruct for a given display.
Definition: font_helper.c:1749
unsigned int border
Atom aWM_DELETE_WINDOW
Delete Window notification event.
Atom aWM_CHAR
keystroke/character notifications generated by API
horizontally centered text. tabs are treated as white space
Definition: draw_text.h:78
void FWSetMenuHandlers(WBFrameWindow *pFW, const WBFWMenuHandler *pHandlerArray)
Function to assign the default menu handler to a frame window.
Definition: frame_window.c:761
int WBPostPriorityEvent(Window wID, XEvent *pEvent)
Places a copy of the specified event at the end of the priority (internal) event queue.
&#39;configuration helper&#39; main header file for the X11 Work Bench Toolkit API
void PXM_RGBToPixel(XStandardColormap *pMap, XColor *pColor)
Icon Registration for application &#39;large&#39; and &#39;small&#39; icons.
#define RGB255_TO_XCOLOR(R, G, B, X)
Simple RGB assignment to pixel, 0-255 RGB.
Definition: pixmap_helper.h:95
set this to make window immediately visible
Definition: frame_window.h:289
void FWChildFrameStatusChanged(WBChildFrame *pChildFrame)
Notify Child Frame to update status text in Frame Window.
Definition: child_frame.c:879
void FWRecalcLayout(Window wID)
Force a frame window to recalculate its layout, which may involve resizing multiple contained windows...
void * WBReAlloc(void *pBuf, int nNewSize)
High performance memory sub-allocator &#39;re-allocate&#39;.
XColor FWGetDefaultFG(void)
Get the default foreground color.
Definition: frame_window.c:394
#define WB_STANDARD_INPUT_MASK
&#39;Standard&#39; input mask, bit flag for window creation
void FWSetFocusWindow(WBFrameWindow *pFW, WBChildFrame *pCont)
Sets the focus to a specific contained window using the Window ID.
Atom aSET_FOCUS
dialog focus messages
void WBInvalidateGeom(Window wID, const WB_GEOM *pGeom, int bPaintNow)
&#39;Paint&#39; helper, invalidates a geometry for asynchronous Expose event generation
void FWChildFrameRecalcLayout(WBChildFrame *pChildFrame)
Child frame notification callback (called by frame window)
Definition: child_frame.c:799
XColor FWGetDefaultBD(void)
Get the default border color.
Definition: frame_window.c:408
Atom aTAB_MESSAGE
command sent by Client Message related to &#39;tab&#39; operations
Definition: frame_window.c:192
void WBClearWindow(Window wID, GC gc)
&#39;Paint&#39; helper, erases background by painting the background color within the clipping region ...
Child Frame API functions.
void WBCreateWindowDefaultGC(Window wID, unsigned long clrFG, unsigned long clrBG)
creates a default GC for a window
Region WBRectToRegion(const WB_RECT *pRect)
&#39;Paint&#39; helper, converts a WB_RECT structure to a Region.
void WBSetApplicationWindow(Window wID)
Destroy a window.
center using entire text height (ascent + descent for single line)
Definition: draw_text.h:86
WBMenuBarWindow * MBCreateMenuBarWindow(Window wIDParent, const char *pszResource, int iFlags)
Create a Menu Bar windows and its associated WBMenuBarWindow structure.
Definition: menu_bar.c:305
void * WBAlloc(int nSize)
High performance memory sub-allocator &#39;allocate&#39;.
int FWAddContainedWindow(WBFrameWindow *pFW, WBChildFrame *pNew)
Adds a &#39;contained&#39; window and returns the tab order index.
unsigned int width
Window WBGetParentWindow(Window wID)
Returns the window&#39;s parent (or None if there is no parent)
#define END_XCALL_DEBUG_WRAPPER
wrapper macro for calls into the X11 library. This macro follows the call(s)
structure for defining a menu bar window
Definition: menu_bar.h:113
static __inline__ WBMenuBarWindow * MBGetMenuBarWindowStruct(Window wID)
Obtain a pointer to the WBMenuBarWindow structure from a Window ID of a Menu Bar window.
Definition: menu_bar.h:191
void FWSetFocusWindowIndex(WBFrameWindow *pFW, int iIndex)
Sets the focus to a specific contained window using its tab order index.
int FWGetNumContWindows(const WBFrameWindow *pFW)
Returns the total number of &#39;contained&#39; windows.
void FWSetUserCallback(WBFrameWindow *pFW, WBWinEvent pCallBack)
assign a new WBWinEvent callback function for a frame window
Definition: frame_window.c:739
unsigned int height
Window WBGetMenuWindow(Window wID)
Returns the Window ID of the (first) menu window assigned to a (frame) window.
int WBFontSetAvgCharWidth(Display *pDisplay, XFontSet fontSet)
Get the average character width for a font set.
Definition: font_helper.c:1227
#define WB_ERROR_PRINT(...)
Preferred method of implementing an &#39;error level&#39; debug message for all subsystems.
Definition: debug_helper.h:268
void WBDraw3DBorderTab(Display *pDisplay, Drawable dw, GC gc, WB_GEOM *pgeomOutline, int fFocus, unsigned long lFGColor, unsigned long lBGColor, unsigned long lBorderColor1, unsigned long lBorderColor2, unsigned long lHighlightColor, XFontSet fontSet, XFontSet fontSetBold, Atom aGraphic, const char *szText)
Draw a &#39;tab&#39; within a specified &#39;outline&#39; rectangle.
void WBFree(void *pBuf)
High performance memory sub-allocator &#39;free&#39;.
static __inline__ void WBInvalidateRect(Window wID, const WB_RECT *pRCT, int bPaintFlag)
&#39;Paint&#39; helper, invalidates a WB_RECT for asynchronous Expose event generation
int WBPostEvent(Window wID, XEvent *pEvent)
Places a copy of the specified event at the end of the regular (internal) event queue.
#define WBGetWindowCopyGC2(wID, gcSrc)
makes a copy of the specified GC for the desired window
void WBDestroyWindow(Window wID)
Destroy a window.
represents a &#39;break&#39; (marks end of a column)
Definition: frame_window.h:304
structure for managing menu callbacks
Definition: frame_window.h:215
#define WB_KEYEVENT_SHIFT
&#39;AltCtrlShift&#39; bit flag for Shift modifier for WBKeyEventProcessKey()
mask for the actual tab value
Definition: frame_window.h:303
void FWSetStatusTabInfo(WBFrameWindow *pFW, int nTabs, const int *pTabs)
Sets the &#39;status&#39; tab info for a Frame Window with a status bar.
void WBUpdateWindow(Window wID)
&#39;Paint&#39; helper, generates an asynchronous Expose event for non-empty &#39;invalid&#39; region ...
void WBSetWindowIcon(Window wID, int idIcon)
assigns an icon resource (by ID) to a window
#define WBRectOverlapped(R1, R2)
Returns logical TRUE if the rectangle R1 overlaps/intersects R2.
Atom aRESIZE_NOTIFY
notification of window re-size via ClientMessage
void FWDestroyChildFrame(WBChildFrame *pChildFrame)
Destroy an Child Frame.
Definition: child_frame.c:312
Atom aMENU_ACTIVATE
Internal Client Message Atom for &#39;ACTIVATE&#39; notification.
Definition: menu_bar.c:150
internal wrapper struct for &#39;rectangle&#39; definition
void WBDispatch(XEvent *pEvent)
Generic Event Dispatcher, using message type to dispatch.
#define LOAD_COLOR(X, Y, Z)
macro to load a color with a fallback, mostly for readability
#define WB_KEYEVENT_ACSMASK
&#39;AltCtrlShift&#39; bit mask for Alt+Ctrl+Shift bits for WBKeyEventProcessKey()
int WBFontSetDescent(Display *pDisplay, XFontSet fontSet)
Get the maximum character descent from a font set.
Definition: font_helper.c:1094
int(* WBWinEvent)(Window wID, XEvent *pEvent)
event callback function type for window events
void WBDrawLeftArrow(Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomRect, unsigned long lColor)
Draw a left arrow in a window within a specified geometry.
Atom aWM_TAKE_FOCUS
&#39;Take Focus&#39; - TODO document this properly
Atom aImageAtom
&#39;image&#39; atom for display in tabs. default is &#39;None&#39;. You should not alter this member directly...
Definition: frame_window.h:433
XColor FWGetDefaultBG(void)
Get the default background color.
Definition: frame_window.c:401
int WBFontSetAscent(Display *pDisplay, XFontSet fontSet)
Get the maximum character ascent from a font set.
Definition: font_helper.c:1136
bit flag to &#39;right justify&#39; the column location
Definition: frame_window.h:310
Atom aMENU_COMMAND
commands sent by menus via ClientMessage
XFontStruct * WBGetDefaultFont(void)
Returns a pointer to the default font&#39;s XFontStruct.
int MBMenuProcessHotKey(WBMenu *pMenu, XKeyEvent *pEvent)
Event handler for menu hotkeys.
Definition: menu.c:1229
Window wID
window identifier for the &#39;Child Frame&#39; window. may contain &#39;None&#39; while being destroyed ...
Definition: frame_window.h:412
WBChildFrame * FWGetContainedWindowByIndex(const WBFrameWindow *pFW, int iIndex)
Returns the Window ID for the specified &#39;contained&#39; window. The index follows the tab order...
Display * WBGetWindowDisplay(Window wID)
returns the Display associated with a window
void WBSetWindowData(Window wID, int iIndex, void *pData)
assign &#39;data pointer&#39; for a window and specified index value
char * pszMenuResource
resource string for this child frame&#39;s menu (NULL = &#39;use default&#39;)
Definition: frame_window.h:437
char * WBGetAtomName(Display *pDisplay, Atom aAtom)
Lookup and/or allocate an internal Atom for a named string.
#define BEGIN_XCALL_DEBUG_WRAPPER
wrapper macro for calls into the X11 library. This macro precedes the call(s)
WBMenu * pMenu
a pointer to the associated WBMenu structure
Definition: menu_bar.h:119
void WBSetInputFocus(Window wID)
set input focus to a specific window
#define WBPointInRect(X, Y, R)
Returns logical TRUE if the point (X,Y) is within the borders of the rectangle &#39;R&#39;.
main controlling structure for frame windows
Definition: frame_window.h:264
Window WBCreateWindow(Display *pDisplay, Window wIDParent, WBWinEvent pProc, const char *szClass, int iX, int iY, int iWidth, int iHeight, int iBorder, int iIO, int iFlags, XSetWindowAttributes *pXSWA)
Create a window.
left-justified text
Definition: draw_text.h:77
void WBGetClientRect(Window wID, WB_RECT *pRect)
Returns the WB_RECT (rectangle) defined by the window&#39;s geometry, excluding the border area...
char * WBCopyString(const char *pSrc)
A simple utility that returns a WBAlloc() copy of a 0-byte terminated string.
void WBSetWMProperties(Window wID, const char *szTitle, XSizeHints *pNormalHints, XWMHints *pWMHints, XClassHint *pClassHints)
assign standard WM (Window Manager) properties via XSetWMProperties
WBFWMenuHandler * pMenuHandler
menu handler for this child frame&#39;s menu (NULL = &#39;use default&#39;)
Definition: frame_window.h:438
WBFrameWindow * FWCreateFrameWindow(const char *szTitle, int idIcon, const char *szMenuResource, int iX, int iY, int iWidth, int iHeight, WBWinEvent pUserCallback, int iFlags)
Create a frame window.
Definition: frame_window.c:444
set this to enable a &#39;status bar&#39; at the bottom
Definition: frame_window.h:291
int(* callback)(XClientMessageEvent *)
menu callback (gets pointer to the &#39;XClientMessageEvent&#39;).
Definition: frame_window.h:219
void FWSetStatusText(WBFrameWindow *pFW, const char *szText)
Sets the &#39;status&#39; text for a Frame Window with a status bar, forcing a re-paint.
Window wSelf
The window ID of the menu bar window.
Definition: menu_bar.h:116
void WBDrawRightArrow(Display *pDisplay, Drawable wID, GC gc, WB_GEOM *pgeomRect, unsigned long lColor)
Draw a right arrow in a window within a specified geometry.
XFontStruct * WBCopyFont(XFontStruct *pOldFont)
make a copy of an existing font (best when assigning to a window)
Definition: font_helper.c:97
int WBMapWindow(Display *pDisplay, Window wID)
Wrapper for XMapWindow, makes window visible.
Frame Window API functions and definitions.
unsigned long lMenuID
menu ID (< 0x10000L) or const pointer to string
Definition: frame_window.h:217
internal wrapper struct for X11 &#39;geometry&#39; definition
Atom WBGetAtom(Display *pDisplay, const char *szAtomName)
Lookup and/or allocate an internal Atom for a named string (lookups include X11 atoms) ...
void FWMoveChildFrameTabIndex(WBFrameWindow *pFW, WBChildFrame *pCont, int iIndex)
Sets the specific contained window to a particular index in the tab order.
void WBEndPaint(Window wID, GC gc)
&#39;Paint&#39; helper, frees resources and marks the update region &#39;valid&#39;
int(* UIcallback)(WBMenu *, WBMenuItem *)
menu &#39;UI&#39; callback to handle displaying menu states.
Definition: frame_window.h:226
void WBRemoveMenuWindow(Window wID, Window wIDMenu)
Remove (detach) the specified menu window from a (frame) window.
#define WB_KEYEVENT_KEYSYM
&#39;AltCtrlShift&#39; bit flag for &#39;VK_&#39; keys for WBKeyEventProcessKey()