X11workbench Toolkit  1.0
dialog_window.c
Go to the documentation of this file.
1 // _ _ _ _ _ //
3 // __| |(_) __ _ | | ___ __ _ __ __(_) _ __ __| | ___ __ __ ___ //
4 // / _` || | / _` || | / _ \ / _` | \ \ /\ / /| || '_ \ / _` | / _ \\ \ /\ / / / __| //
5 // | (_| || || (_| || || (_) || (_| | \ V V / | || | | || (_| || (_) |\ V V /_| (__ //
6 // \__,_||_| \__,_||_| \___/ \__, |_____\_/\_/ |_||_| |_| \__,_| \___/ \_/\_/(_)\___| //
7 // |___/|_____| //
8 // //
9 // modal or modeless window based on a dialog template //
10 // //
12 
13 /*****************************************************************************
14 
15  X11workbench - X11 programmer's 'work bench' application and toolkit
16  Copyright (c) 2010-2018 by Bob Frazier (aka 'Big Bad Bombastic Bob')
17  all rights reserved
18 
19  DISCLAIMER: The X11workbench application and toolkit software are supplied
20  'as-is', with no warranties, either implied or explicit.
21  Any claims to alleged functionality or features should be
22  considered 'preliminary', and might not function as advertised.
23 
24  BSD-like license:
25 
26  There is no restriction as to what you can do with this software, so long
27  as you include the above copyright notice and DISCLAIMER for any distributed
28  work that is equal to or derived from this one, along with this paragraph
29  that explains the terms of the license if the source is also being made
30  available. A "derived work" describes a work that uses a significant portion
31  of the source files or algorithms that are included with this one.
32  Specifically excluded from this are files that were generated by the software,
33  or anything that is included with the software that is part of another package
34  (such as files that were created or added during the 'configure' process).
35  Specifically included is the use of part or all of any of the X11 workbench
36  toolkit source or header files in your distributed application. If you do not
37  ship the source, the above copyright statement is still required to be placed
38  in a reasonably prominent place, such as documentation, splash screens, and/or
39  'about the application' dialog boxes.
40 
41  Use and distribution are in accordance with GPL, LGPL, and/or the above
42  BSD-like license. See COPYING and README files for more information.
43 
44 
45  Additional information at http://sourceforge.net/projects/X11workbench
46 
47 ******************************************************************************/
48 
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <unistd.h>
56 #include <memory.h>
57 #include <string.h>
58 #include <strings.h>
59 #include <signal.h>
60 #include <time.h>
61 #include <ctype.h>
62 
63 
64 #ifndef XK_Delete /* moslty for interix */
65 #define XK_MISCELLANY /* mostly for interix */
66 #include <X11/keysymdef.h> // some platforms don't automatically include this with X headers
67 #endif // XK_Delete
68 
69 #include "window_helper.h"
70 #include "pixmap_helper.h" // pixmap helpers, including pre-defined icons
71 #include "dialog_window.h"
72 #include "dialog_controls.h"
73 #include "conf_help.h"
74 #include "draw_text.h"
75 
76 
77 #define THIS_SUBSYSTEM DebugSubSystem_Dialog
78 
79 
115 typedef struct __DIALOG_WINDOW__
116 {
118 
120 
121  int nContents;
123  char *szTitle;
124 
126 
128 
129 } DIALOG_WINDOW;
130 
131 
132 static int InternalCreateChildWindows(DIALOG_WINDOW *pDlg, const char *szDialogResource);
133 
134 int DLGDefaultCallback(Window wID, XEvent *pEvent); // default callback for dialog windows (call for default processing)
135 
136 static DIALOG_WINDOW *pDialogs = NULL; // pointer to linked list of dialog windows (WBAlloc'd)
137 
138 static XColor clrFG, clrBG, clrBD;
139 static int iInitColorFlag = 0;
140 
141 static void __internal_destroy_dialog_window(DIALOG_WINDOW *pTemp)
142 {
143  int i1;
144  if(pTemp->pwContents)
145  {
147  int nContents = pTemp->nContents;
148  int nMaxContents = pTemp->nMaxContents;
149 
150  pTemp->pwContents = NULL;
151  pTemp->nContents = 0;
152  pTemp->nMaxContents = 0;
153 
154  for(i1=0; i1 < nContents && i1 < nMaxContents; i1++)
155  {
156  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Frame,
157  "%s - Destroy contents %d\n",
158  __FUNCTION__, i1 + 1);
159 
160  if((int)(pwContents[i1].wID) > 0)
161  WBDestroyWindow(pwContents[i1].wID);
162 
163  // TODO: do I need to release/free 'aClass' or shall I organize this elsewhere?
164  }
165 
166  WBFree(pwContents);
167  }
168 
169  if(pTemp->szTitle)
170  {
171  WBFree(pTemp->szTitle);
172  }
173 
174  pTemp->szTitle = NULL;
175 }
176 
177 
178 int DLGPixelHeight(WBDialogWindow *pDlg, int iHeight)
179 {
180  return iHeight * 2; // for now
181 }
182 
183 int DLGPixelWidth(WBDialogWindow *pDlg, int iWidth)
184 {
185  return iWidth * 2; // for now
186 }
187 
188 void WBDialogWindowExit()
189 {
190  // destroy all of the dialog windows
191 
192  while(pDialogs)
193  {
194  DIALOG_WINDOW *pTemp = pDialogs;
195  Window wID = pTemp->wbDLG.wID;
196 
197  pDialogs = pDialogs->pNext;
198 
199  WB_ERROR_PRINT("WARNING: %s - dialog window %d not previously destroyed\n", __FUNCTION__, (int)(pTemp->wbDLG.wID));
200 
201  // slightly different - free the structure first, THEN destroy the window
202  WBSetWindowData(pTemp->wbDLG.wID, 0, NULL);
203  __internal_destroy_dialog_window(pTemp);
204  WBFree(pTemp);
205 
206  if(wID > 0)
207  {
208  WBDestroyWindow(wID); // making sure...
209  }
210  }
211 
212 }
213 
214 #define LOAD_COLOR0(X,Y) if(CHGetResourceString(WBGetDefaultDisplay(), X, Y, sizeof(Y)) > 0) { }
215 #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); }
216 
217 static void InternalCheckColors(void)
218 {
219  Colormap colormap;
220 
221  // *Dialog.background, *Dialog.foreground, *WmDialog.background, *WmDialog.foreground,
222  // *Form.background, *Form.foreground, *background, *foreground
223 
224  if(!iInitColorFlag)
225  {
226  char szFG[16], szBG[16], szBD[16];
227  colormap = DefaultColormap(WBGetDefaultDisplay(), DefaultScreen(WBGetDefaultDisplay()));
228 
229  LOAD_COLOR0("*Dialog.foreground",szFG) else LOAD_COLOR0("*Form.foreground", szFG)
230  else LOAD_COLOR0("*WmDialogShell.foreground",szFG) else LOAD_COLOR0("*WmForm.foreground", szFG)
231  else LOAD_COLOR("*foreground", szFG, "#000000");
232  LOAD_COLOR0("*Dialog.background",szBG) else LOAD_COLOR0("*Form.background", szBG)
233  else LOAD_COLOR0("*WmDialogShell.background",szBG) else LOAD_COLOR0("*WmForm.background", szBG)
234  else LOAD_COLOR("*background", szBG, "#dcdad5"); // default for gnome
235  LOAD_COLOR0("*Dialog.border",szBD) else LOAD_COLOR0("*Form.border", szBD)
236  else LOAD_COLOR0("*WmDialogShell.border",szBD) else LOAD_COLOR0("*WmForm.border", szBD)
237  else LOAD_COLOR0("*borderColor", szBD)
238  else LOAD_COLOR("*border", szBD, "black"); // default for gnome
239 
240  XParseColor(WBGetDefaultDisplay(), colormap, szFG, &clrFG);
241  XAllocColor(WBGetDefaultDisplay(), colormap, &clrFG);
242  XParseColor(WBGetDefaultDisplay(), colormap, szBG, &clrBG);
243  XAllocColor(WBGetDefaultDisplay(), colormap, &clrBG);
244  XParseColor(WBGetDefaultDisplay(), colormap, szBD, &clrBD);
245  XAllocColor(WBGetDefaultDisplay(), colormap, &clrBD);
246 
247  iInitColorFlag = 1;
248  }
249 }
250 
251 WBDialogWindow *DLGCreateDialogWindow(const char *szTitle, const char *szDialogResource,
252  int iX, int iY, int iWidth, int iHeight,
253  WBWinEvent pUserCallback, int iFlags, void *pUserData)
254 {
255  DIALOG_WINDOW *pNew = WBAlloc(sizeof(DIALOG_WINDOW));
256  Display *pDisplay = WBGetDefaultDisplay();
257  XSetWindowAttributes xswa; /* Temporary Set Window Attribute struct */
258  XSizeHints xsh; /* Size hints for window manager */
259  XWMHints xwmh;
260 // Atom aProto[3];
261  XClientMessageEvent evt;
262  int i1;
263 //#ifndef NO_DEBUG
264 // WB_UINT64 ullTime = WBGetTimeIndex();
265 //#endif // NO_DEBUG
266 
267 
268 // WB_ERROR_PRINT("TEMPORARY: %s line %d delta tick %lld\n", __FUNCTION__, __LINE__, (WBGetTimeIndex() - ullTime));
269 
270  if(!pNew)
271  {
272  WB_ERROR_PRINT("%s - not enough memory to create dialog window (1)\n", __FUNCTION__);
273  return NULL;
274  }
275 
276  WBDialogControlsInit(); // make sure I initialize all of the dialog controls stuff
277 
278  InternalCheckColors(); // must do this first
279 
280  bzero(pNew, sizeof(*pNew));
281  pNew->wbDLG.ulTag = DIALOG_WINDOW_TAG;
282  pNew->wbDLG.wID = -1; // initial (bad) value
283  pNew->wbDLG.wIDOwner = None; // initially, no owner
284  pNew->wbDLG.iFlags = iFlags; // save this while I'm at it
285  pNew->wbDLG.pUserData = pUserData; // save this too
286 
287  if(szTitle)
288  {
289  pNew->szTitle = WBAlloc(strlen(szTitle) + 1);
290  if(!pNew->szTitle)
291  {
292  WBFree(pNew);
293 
294  WB_ERROR_PRINT("%s - not enough memory to create dialog window (2)\n", __FUNCTION__);
295  return NULL;
296  }
297  strcpy(pNew->szTitle, szTitle);
298  }
299 
300  // add struct to beginning of linked list 'cause it's faster that way
301 
302  pNew->pNext = pDialogs;
303  pDialogs = pNew;
304 
305  // NOW I get to create the actual window with its GC and callback proc
306 
307  // copy standard colors into structure at this time [I will reference 'pNew->wbDLG' values from this point]
308  memcpy(&(pNew->wbDLG.clrFG), &clrFG, sizeof(clrFG));
309  memcpy(&(pNew->wbDLG.clrBG), &clrBG, sizeof(clrBG));
310  memcpy(&(pNew->wbDLG.clrBD), &clrBD, sizeof(clrBD));
311 
312  // setting up the standard attributes
313  bzero(&xswa, sizeof(xswa));
314 
315  xswa.border_pixel = pNew->wbDLG.clrBD.pixel;
316  xswa.background_pixel = pNew->wbDLG.clrBG.pixel;
317  xswa.colormap = DefaultColormap(pDisplay, DefaultScreen(pDisplay));
318  xswa.bit_gravity = CenterGravity;
319 
320  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Dialog,
321  "%s - creating dialog window at %d %d %d %d\n",
322  __FUNCTION__, iX, iY, iWidth, iHeight);
323 
324  pNew->wbDLG.wID = WBCreateWindow(pDisplay, None, DLGDefaultCallback, "DialogWindow",
325  iX, iY, iWidth, iHeight, 1, InputOutput,
326  CWBorderPixel | CWBackPixel | CWColormap | CWBitGravity,
327  &xswa);
328  if(pNew->wbDLG.wID <= 0)
329  {
331 
332  WB_ERROR_PRINT("DLGCreateDialogWindow - XCreateWindow fail\n");
333  return NULL;
334  }
335 
336  // immediately identify this window using window data
337  WBSetWindowData(pNew->wbDLG.wID, 0, (void *)pNew);
338 
339  // register user callback function
340  pNew->pDLGCallback = pUserCallback;
341 
342  // before I do anything else, create the child windows (this will set up the correct dialog size)
343 
344  if(!InternalCreateChildWindows(pNew, szDialogResource))
345  {
346  // since the window wasn't exposed yet, I have to post the destroy
347  // rather than destroy it directly...
348 
349  WB_WARN_PRINT("%s - InternalCreateChildWindows failed, destroying dialog box\n", __FUNCTION__);
350 
351  DLGDestroyDialogWindow2((WBDialogWindow *)pNew); // don't display the dialog box
352  return NULL;
353  }
354 
355  // NOW, set up the window properties, including sizeability and the correct min/max/current dimensions
356 
357  bzero(&xsh, sizeof(xsh));
358 
359  xsh.flags = (USPosition | USSize | PWinGravity);
360  xsh.x = iX;
361  xsh.y = iY;
362  xsh.win_gravity = NorthWestGravity; // StaticGravity
363 
364  if(pNew->wbDLG.iClientWidth > 0 && pNew->wbDLG.iClientHeight > 0) // for now only allow this
365  {
366  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Dialog,
367  "%s setting width/height, iClientWidth=%d iClientHeight=%d\n",
368  __FUNCTION__, pNew->wbDLG.iClientWidth, pNew->wbDLG.iClientHeight);
369 
370  xsh.width = DLGPixelWidth((WBDialogWindow *)pNew, pNew->wbDLG.iClientWidth) + 2; /* 2 is twice border width */
371  xsh.height = DLGPixelHeight((WBDialogWindow *)pNew, pNew->wbDLG.iClientHeight) + 2;
372  }
373  else // dialog is NOT resizeable
374  {
375  xsh.width = xsh.base_width = iWidth;
376  xsh.height = xsh.base_height = iHeight;
377  }
378 
379  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Dialog,
380  "%s calling XMoveResizeWindow, iX=%d iY=%d iWidth=%d iHeight=%d xsh.width=%d xsh.height=%d\n",
381  __FUNCTION__, iX, iY, iWidth, iHeight, xsh.width, xsh.height);
382 
383  XMoveResizeWindow(pDisplay, pNew->wbDLG.wID, iX, iY, xsh.width, xsh.height); // do this FIRST before doing 'hints'
384 
385  if(1) // dialog cannot be re-sized
386  {
387  xsh.flags |= (PMinSize | PMaxSize);
388 
389  xsh.min_width = xsh.max_width = xsh.width;
390  xsh.min_height = xsh.max_height = xsh.height;
391 
392  xsh.flags |= PBaseSize;
393  xsh.base_width = xsh.min_width; // 'base width' is like "where you start from" I think... ( == min_width if not specified)
394  xsh.base_height = xsh.min_height; // 'base height' is like "where you start from" I think... ( == min_height if not specified)
395  }
396  else
397  {
398  // TODO: resizable dialog box
399  }
400 
401  bzero(&xwmh, sizeof(xwmh));
402  xwmh.flags = InputHint;
403  xwmh.input = !0; // this represents 'Locally Active'
404 
405 // from http://mail.gnome.org/archives/wm-spec-list/2007-March/msg00000.html
406 
407 // The ICCCM provides for focus models for a window:
408 //
409 // Input Model Input Field WM_TAKE_FOCUS
410 // No Input False Absent
411 // Passive True Absent
412 // Locally Active True Present
413 // Globally Active False Present
414 //
415 // No Input - The application never expects to receive focus.
416 //
417 // Passive - The application will get focus but it will not grab it on
418 // its own. Instead it will only get focus if the window manager gives it
419 // to it. The application cannot help determine if it wants the focus or
420 // not at the given time/situation.
421 //
422 // Locally Active - The same as passive, but the application will also
423 // give focus to other windows that it owns of its own free will, without
424 // any interaction with the window manager, using XSetInputFocus.
425 //
426 // Globally Active - The application will not receive focus from the
427 // window manager. Instead it will determine when it wants focus (from
428 // WM_TAKE_FOCUS suggestions given by the window manager, or from events
429 // such as ButtonPress or KeyPress). The application will then acquire
430 // focus on its own using XSetInputFocus.
431 
432  // set title, size hints, and 'WM_HINTS' hints (so WM knows where to put the window and how to set focus)
433  WBSetWMProperties(pNew->wbDLG.wID, szTitle, &xsh, &xwmh, NULL);
434 
435 #if 0
436  aProto[0] = aWM_DELETE_WINDOW; // for now, that's the only one
437  aProto[1] = aWM_TAKE_FOCUS; // GDK does this, see set_wm_protocols() in gdkwindow-x11.c
438 // aProto[2] = a_NET_WM_PING; // GDK does this, see set_wm_protocols() in gdkwindow-x11.c
439 // aProto[3] = a_NET_WM_SYNC_REQUEST; // GDK does this when HAVE_XSYNC is enabled
440 
441  aProto[sizeof(aProto)/sizeof(aProto[0]) - 1] = None; // have an extra one at the end
442 
443  XSetWMProtocols(pDisplay, pNew->wbDLG.wID, aProto, sizeof(aProto)/sizeof(aProto[0]) - 1);
444 #endif // 0
445 
446  WBSetWMProtocols(pNew->wbDLG.wID, aWM_DELETE_WINDOW, aWM_TAKE_FOCUS, None);
447 
448  WBCreateWindowDefaultGC(pNew->wbDLG.wID, pNew->wbDLG.clrFG.pixel, pNew->wbDLG.clrBG.pixel);
449 
450  // now allow certain kinds of input messages (I should be able to handle messages at this point)
451  XSelectInput(pDisplay, pNew->wbDLG.wID, WB_STANDARD_INPUT_MASK | WB_KEYBOARD_INPUT_MASK);
452 
453  // now that the controls have been created, assign the title, and send
454  // an event to the user callback to initialize the dialog box contents
455 
456 // WBSetWindowIcon(pNew->wbDLG.wID, idIcon); TODO: get icon info from resource??
457 
458  if(pUserCallback)
459  {
460  bzero(&evt, sizeof(evt));
461  evt.type = ClientMessage;
462  evt.display = pDisplay;
463  evt.window = pNew->wbDLG.wID;
464  evt.message_type = aDIALOG_INIT;
465  evt.format = 32;
466 
467  if(!pUserCallback(pNew->wbDLG.wID, (XEvent *)&evt)) // callback returned zero?
468  {
469  DLGDestroyDialogWindow2((WBDialogWindow *)pNew); // don't display the dialog box
470 
471  WB_ERROR_PRINT("DLGCreateDialogWindow - callback returns zero on init\n");
472  return NULL;
473  }
474  }
475 
476  // Last but not least, set the focus for the control that's
477  // first in my list that has the 'CAN_HAVE_FOCUS' bit set,
478  // is visible, and is enabled.
479 
480  for(i1=0; i1 < pNew->nContents; i1++)
481  {
482  if((pNew->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS) &&
483  !(pNew->pwContents[i1].iFlags & WBDialogEntry_DISABLED) && // NOT disabled
484  (pNew->pwContents[i1].iFlags & WBDialogEntry_VISIBLE)) // must ALSO be visible
485  {
486  pNew->pwContents[i1].iFlags |= WBDialogEntry_HAS_FOCUS;
487 
488  break;
489  }
490  }
491 
492  DLGRecalcLayout(pNew->wbDLG.wID); // prior to making me visible do this
493 
494  if(iFlags & WBDialogWindow_APP_WINDOW)
495  {
496  WBSetApplicationWindow(pNew->wbDLG.wID);
497  }
498 
499  if((iFlags & WBDialogWindow_VISIBLE) ||
500  (iFlags & WBDialogWindow_DOMODAL)) // 'DOMODAL' implies visible
501  {
502  WBMapWindow(pDisplay, pNew->wbDLG.wID); // make window visible
503 
504  // 'warp' the mouse pointer to the center of the dialog box
505 
506  if(iFlags & (WBDialogWindow_DOMODAL | WBDialogWindow_CENTERMOUSE))
507  {
509  XWarpPointer(pDisplay, None, pNew->wbDLG.wID, 0, 0, 0, 0, xsh.width / 2, xsh.height / 2);
511  }
512  }
513 
514  if(iFlags & WBDialogWindow_DOMODAL)
515  {
516  WBShowModal(pNew->wbDLG.wID, 0); // ignore return value
517  WBGetParentWindow(pNew->wbDLG.wID); // this syncs everything up
518  }
519 
520  return (WBDialogWindow *)pNew;
521 }
522 
523 void DLGAssignOwner(WBDialogWindow *pDlg, Window wIDOwner)
524 {
525  if(!pDlg || pDlg->ulTag != DIALOG_WINDOW_TAG)
526  {
527  return;
528  }
529 
530  pDlg->wIDOwner = wIDOwner;
531 }
532 
533 void DLGDestroyDialogWindow(Window wID)
534 {
536 }
537 
539 {
540  // step 1: unhook pDialogWindow
541  DIALOG_WINDOW *pTemp = pDialogs;
542  DIALOG_WINDOW *pPrev = NULL;
543  Window wID; //, wIDOwner;
544 // int i1;
545 
546  if(!pDialogWindow || pDialogWindow->ulTag != DIALOG_WINDOW_TAG)
547  {
548  return;
549  }
550 
551  wID = pDialogWindow->wID;
552 // wIDOwner = pDialogWindow->wIDOwner;
553 
554  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Dialog,
555  "%s - Destroying dialog window %d (%08xH)\n",
556  __FUNCTION__, (int)wID, (int)wID);
557 
558  while(pTemp && pTemp != (DIALOG_WINDOW *)pDialogWindow)
559  {
560  pPrev = pTemp;
561  pTemp = pTemp->pNext;
562  }
563 
564  if(pTemp)
565  {
566  if(pPrev)
567  pPrev->pNext = pTemp->pNext; // unhook
568  else if(pDialogs == pTemp)
569  pDialogs = pTemp->pNext;
570  }
571 
572 
573  if(wID > 0)
574  {
575  WBDestroyWindow(wID); // this will free the structure
576  }
577  else
578  {
579  // I must destroy WBAlloc'd entries and contained windows in lieu of 'DLGDefaultCallback'
580  __internal_destroy_dialog_window((DIALOG_WINDOW *)pDialogWindow);
581  WBFree(pDialogWindow);
582  }
583 
584 }
585 
586 void DLGSetUserCallback(WBDialogWindow *pDialogWindow, WBWinEvent pCallBack)
587 {
588  if(pDialogWindow->ulTag != DIALOG_WINDOW_TAG)
589  return;
590 
591  ((DIALOG_WINDOW *)pDialogWindow)->pDLGCallback = pCallBack;
592 }
593 
594 int DLGDefaultCallback(Window wID, XEvent *pEvent)
595 {
596  Display *pDisplay = WBGetWindowDisplay(wID);
597  DIALOG_WINDOW *pDialogWindow = (DIALOG_WINDOW *)DLGGetDialogWindowStruct(wID);
598  int i1, i2, iRval;
599 // Window wIDMenu;
600 
601  if(!pDialogWindow || pDialogWindow->wbDLG.ulTag != DIALOG_WINDOW_TAG)
602  {
603  WB_WARN_PRINT("%s - invalid 'pDialogWindow' for window %d (%08xH)\n",
604  __FUNCTION__, (unsigned int)wID, (unsigned int)wID);
605  return 0;
606  }
607 
608  // TODO: message re-direction to children BEFORE 'pDLGCallback'
609 
610 // if(pEvent->type == DestroyNotify)
611 // {
612 // WB_ERROR_PRINT("!!!TEMPORARY: %s wID=%d got DestroyNotify for %d\n",
613 // __FUNCTION__, (int)wID, (int)pEvent->xdestroywindow.window);
614 // }
615 
616  switch(pEvent->type)
617  {
618  case ButtonPress:
619  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Dialog,
620  "%s - BUTTON PRESS - dialog window\n", __FUNCTION__);
621 
622  break;
623 
624  case ButtonRelease:
625  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Dialog,
626  "%s - BUTTON RELEASE - dialog window\n", __FUNCTION__);
627 
628  break;
629 
630  case MotionNotify:
631  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Dialog,
632  "%s - MOTION NOTIFY - dialog window\n", __FUNCTION__);
633 
634  break;
635 
636  case ClientMessage: // menus, etc.
637  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Dialog,
638  "%s - CLIENT MESSAGE - dialog window\n", __FUNCTION__);
639 
640  if(pEvent->xclient.message_type == aWM_PROTOCOLS && pEvent->xclient.window == wID)
641  {
642  if(pEvent->xclient.data.l[0] == aWM_DELETE_WINDOW)
643  {
644  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Dialog,
645  "%s - WM_DELETE_WINDOW (calling WBDestroyWindow(%d))\n", __FUNCTION__, (int)wID);
646  WBDestroyWindow(wID); // should be safe to call this under any circumstances
647 
648 #if 0
649  Window wIDOwner = pDialogWindow->wbDLG.wIDOwner;
650 
651  WBUnmapWindow(pDisplay, wID);
652  WBSetWindowData(wID, 0, NULL); // divorce 'pDialogWindow' from the window
653 
654  __internal_destroy_dialog_window(pDialogWindow);
655 
656  WBFree(pDialogWindow); // 'pDialogWindow' will never be referenced again
657 
658  // note - this MIGHT recurse
659  WBDestroyWindow(wID); // should be safe to call this under any circumstances
660 // // NOTE: if I'm already "being destroyed" I don't want to call this
661 // BEGIN_XCALL_DEBUG_WRAPPER
662 // XDestroyWindow(pDisplay, wID);
663 // END_XCALL_DEBUG_WRAPPER
664 
665  // if the window had an owner, raise it and assign focus to it
666 
667  if(wIDOwner != None)
668  {
669  WBPostDelayedSetFocusAppEvent(WBGetWindowDisplay(wIDOwner), wIDOwner, wID, 100); // 100 msec delay
670  }
671 #endif // 0
672  return 1;
673  }
674  }
675  else if(pEvent->xclient.message_type == aCONTROL_NOTIFY) // 'control notification' messages
676  {
677  // for control notifications, FIRST give the user callback a chance to handle it
678  // and if nothing is done (zero return), I do my own processing here
679 
680  if(pDialogWindow->pDLGCallback)
681  {
682  // if I handle it here, I don't do further processing
683 
684  iRval = pDialogWindow->pDLGCallback(wID, pEvent);
685 
686  if(iRval)
687  {
688  return iRval;
689  }
690  }
691 
692  // check for OK/Cancel/Yes/No/Retry/Ignore/Abort button press and exit modal for any of them
693 
694  if(pEvent->xclient.data.l[0] == (long)aBUTTON_PRESS &&
695  (pEvent->xclient.data.l[1] == IDOK || pEvent->xclient.data.l[1] == IDCANCEL ||
696  pEvent->xclient.data.l[1] == IDYES || pEvent->xclient.data.l[1] == IDNO ||
697  pEvent->xclient.data.l[1] == IDRETRY || pEvent->xclient.data.l[1] == IDABORT ||
698  pEvent->xclient.data.l[1] == IDIGNORE))
699  {
700  WBEndModal(wID, pEvent->xclient.data.l[1]);
701 
702  return 1; // handled
703  }
704  else if(pEvent->xclient.data.l[0] == (long)aLIST_NOTIFY)
705  {
706  if(pEvent->xclient.data.l[2] == WB_LIST_DBLCLICK)
707  {
708  // default behavior on list double click is to close the dialog with 'IDOK'
709  WBEndModal(wID, IDOK);
710 
711  return 1; // handled
712  }
713 
714 #ifndef NO_DEBUG
715  {
716  char *p1 = WBGetAtomName(pDisplay, (Atom)pEvent->xclient.data.l[0]);
717  WB_WARN_PRINT("%s - TODO: LIST_NOTIFY control notification message %ld (%s) %ld (%08lxH), %ld (%08lxH)\n",
718  __FUNCTION__, pEvent->xclient.data.l[0], p1,
719  pEvent->xclient.data.l[1], pEvent->xclient.data.l[1],
720  pEvent->xclient.data.l[2], pEvent->xclient.data.l[2]);
721  if(p1)
722  {
723  WBFree(p1);
724  }
725  }
726 #endif // NO_DEBUG
727 
728  return 0; // NOT handled (default response for unhandled stuff
729  }
730 
731 #ifndef NO_DEBUG
732  {
733  char *p1 = WBGetAtomName(pDisplay, (Atom)pEvent->xclient.data.l[0]);
734  WB_WARN_PRINT("%s - TODO: control notification messages %ld (%s) %ld (%08lxH), %ld (%08lxH)\n",
735  __FUNCTION__, pEvent->xclient.data.l[0], p1,
736  pEvent->xclient.data.l[1], pEvent->xclient.data.l[1],
737  pEvent->xclient.data.l[2], pEvent->xclient.data.l[2]);
738  if(p1)
739  {
740  WBFree(p1);
741  }
742  }
743 #endif // NO_DEBUG
744  }
745  else if(pEvent->xclient.message_type == aGOTFOCUS) // 'focus change' messages
746  {
747  WBDialogEntry *pEntry = (WBDialogEntry *)WBGetPointerFromHash(((XClientMessageEvent *)pEvent)->data.l[1]);
748 
749  WBDestroyPointerHash(((XClientMessageEvent *)pEvent)->data.l[1]); // by convention, do this after receiving it
750 
751  if(pEntry < pDialogWindow->pwContents ||
752  pEntry >= pDialogWindow->pwContents + pDialogWindow->nContents ||
753  (((unsigned long)pEntry - (unsigned long)pDialogWindow->pwContents) % sizeof(*pEntry)) != 0)
754  {
755  // validity check - if it fails, it's not a valid pointer and I reject it
756 
757  WB_WARN_PRINT("%s - WARNING: invalid dialog entry pointer in GOTFOCUS message (ignored)\n",
758  __FUNCTION__);
759  }
760  else if(pEntry->iFlags & WBDialogEntry_HAS_FOCUS)
761  {
762  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Dialog,
763  "%s - INFO: control already has focus in GOTFOCUS message\n",
764  __FUNCTION__);
765  }
766  else if((pEntry->iFlags & WBDialogEntry_CAN_HAVE_FOCUS) &&
767  (pEntry->iFlags & WBDialogEntry_VISIBLE) &&
768  !(pEntry->iFlags & WBDialogEntry_DISABLED))
769  {
770  for(i1=0; i1 < pDialogWindow->nContents; i1++)
771  {
772  if(pEntry == pDialogWindow->pwContents + i1)
773  {
774  continue;
775  }
776  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_HAS_FOCUS)
777  {
778  pDialogWindow->pwContents[i1].iFlags &= ~WBDialogEntry_HAS_FOCUS;
779  }
780  }
781 
782  pEntry->iFlags |= WBDialogEntry_HAS_FOCUS;
783  }
784  else // items that can't actually get the focus - need to handle it properly
785  {
786  int bFound = 0;
787  // set focus to NEXT control that can have focus (if it does not already have focus)
788 
789  i2 = (pEntry - pDialogWindow->pwContents);
790 
791  for(i1 = i2 + 1; i1 < pDialogWindow->nContents; i1++)
792  {
793  if((pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS) &&
794  (pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_VISIBLE) &&
795  !(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_DISABLED))
796  {
797  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_HAS_FOCUS)
798  {
799  // already has focus - nothing left to do
800  bFound = -1; // to indicate 'do nothing'
801  break;
802  }
803 
804  // this is my new focus item - unset focus for everything else
805 
806  for(i2=0; i2 < pDialogWindow->nContents; i2++)
807  {
808  if(i2 == i1)
809  {
810  continue;
811  }
812 
813  if(pDialogWindow->pwContents[i2].iFlags & WBDialogEntry_HAS_FOCUS)
814  {
815  pDialogWindow->pwContents[i2].iFlags &= ~WBDialogEntry_HAS_FOCUS;
816 
817  WBInvalidateGeom(pDialogWindow->pwContents[i2].wID, NULL, 1); // force re-paint
818  }
819  }
820 
821  bFound = 1;
822 
823  break;
824  }
825  }
826 
827  // wrap around
828  if(!bFound)
829  {
830  for(i1 = 0; i1 < i2; i1++)
831  {
832  if((pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS) &&
833  (pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_VISIBLE) &&
834  !(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_DISABLED))
835  {
836  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_HAS_FOCUS)
837  {
838  // already has focus - nothing left to do
839  bFound = -1; // to indicate 'do nothing'
840  break;
841  }
842 
843  // this is my new focus item - unset focus for everything else
844 
845  for(i2=0; i2 < pDialogWindow->nContents; i2++)
846  {
847  if(i2 == i1)
848  {
849  continue;
850  }
851 
852  if(pDialogWindow->pwContents[i2].iFlags & WBDialogEntry_HAS_FOCUS)
853  {
854  pDialogWindow->pwContents[i2].iFlags &= ~WBDialogEntry_HAS_FOCUS;
855 
856  WBInvalidateGeom(pDialogWindow->pwContents[i2].wID, NULL, 1); // force re-paint
857  }
858  }
859 
860  bFound = 1;
861 
862  break;
863  }
864  }
865  }
866 
867  // now post the "set focus to this item" event (high priority)
868  if(bFound > 0) // need to send message for focus change
869  {
870  XClientMessageEvent evt = {
871  .type=ClientMessage,
872  .serial=0,
873  .send_event=0,
874  .display=pDisplay,
875  .window=wID,
876  .message_type=aDLG_FOCUS,
877  .format=32
878  };
879  evt.data.l[0] = 0;
880  evt.data.l[1] = pDialogWindow->pwContents[i1].iID;
881  evt.data.l[2] = 0; // do not assign 'checked' for button controls
882 
883  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Dialog,
884  "%s - sending 'DLG_FOCUS notification - control id %d\n",
885  __FUNCTION__, pDialogWindow->pwContents[i1].iID);
886 
887  WBPostPriorityEvent(wID, (XEvent *)&evt); // priority event makes it happen faster
888  }
889  else
890  {
891  WB_WARN_PRINT("%s - received 'GOTFOCUS' for non-input control id %d\n",
892  __FUNCTION__, pDialogWindow->pwContents[i1].iID);
893  }
894  }
895  }
896  else if(pEvent->xclient.message_type == aLOSTFOCUS) // 'focus change' messages
897  {
898  WBDialogEntry *pEntry = (WBDialogEntry *)WBGetPointerFromHash(((XClientMessageEvent *)pEvent)->data.l[1]);
899 
900  WBDestroyPointerHash(((XClientMessageEvent *)pEvent)->data.l[1]); // by convention, do this after receiving it
901 
902  if(pEntry < pDialogWindow->pwContents ||
903  pEntry >= pDialogWindow->pwContents + pDialogWindow->nContents ||
904  (((unsigned long)pEntry - (unsigned long)pDialogWindow->pwContents) % sizeof(*pEntry)) != 0)
905  {
906  // validity check - if it fails, it's not a valid pointer and I reject it
907 
908  WB_WARN_PRINT("%s - WARNING: invalid dialog entry pointer in GOTFOCUS message (ignored)\n",
909  __FUNCTION__);
910  }
911  else
912  {
913  WB_WARN_PRINT("%s - TODO: handle 'LOST FOCUS' notifications - control id %d\n",
914  __FUNCTION__, pEntry->iID);
915  }
916  }
917  else if(pEvent->xclient.message_type == aDLG_FOCUS) // dialog focus change message
918  {
919  // data.l[0] == 0 for "set to specific dialog control"
920  // data.l[0] < 0 for 'previous', > 0 for 'next'
921  // this must be sent via the message queue or recursion might occur
922 
923  WBDialogEntry *pEntry = NULL;
924  Window wFocus;
925  int iCurFocus;
926 
927  // FIRST, find out which dialog control entry belongs to the item that will
928  // end up with the focus. If the focus bit is already set, just exit.
929 
930  if(!pEvent->xclient.data.l[0])
931  {
932  for(i1=0; i1 < pDialogWindow->nContents; i1++)
933  {
934  if(pDialogWindow->pwContents[i1].iID == pEvent->xclient.data.l[1])
935  {
936  pEntry = pDialogWindow->pwContents + i1;
937  break;
938  }
939  }
940  }
941  else
942  {
943  // who has focus RIGHT NOW ?
944  for(iCurFocus = -1, i1=0; i1 < pDialogWindow->nContents; i1++)
945  {
946  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_HAS_FOCUS)
947  {
948  iCurFocus = i1;
949  break;
950  }
951  }
952 
953  if(iCurFocus < 0)
954  {
955  // no focus, so find the first one (or last one) possible
956  if(pEvent->xclient.data.l[0] < 0)
957  {
958  for(i1=pDialogWindow->nContents - 1; i1 >= 0; i1--)
959  {
960  if((pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS) &&
961  (pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_VISIBLE) &&
962  !(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_DISABLED))
963  {
964  pEntry = pDialogWindow->pwContents + i1;
965  break;
966  }
967  }
968  }
969  else
970  {
971  for(i1=0; i1 < pDialogWindow->nContents; i1++)
972  {
973  if((pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS) &&
974  (pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_VISIBLE) &&
975  !(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_DISABLED))
976  {
977  pEntry = pDialogWindow->pwContents + i1;
978  break;
979  }
980  }
981  }
982  }
983  else
984  {
985  if(pEvent->xclient.data.l[0] < 0)
986  {
987  for(i1=iCurFocus - 1; i1 >= 0; i1--)
988  {
989  if((pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS) &&
990  (pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_VISIBLE) &&
991  !(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_DISABLED))
992  {
993  pEntry = pDialogWindow->pwContents + i1;
994  break;
995  }
996  }
997 
998  if(!pEntry)
999  {
1000  for(i1=pDialogWindow->nContents - 1; i1 > iCurFocus; i1--)
1001  {
1002  if((pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS) &&
1003  (pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_VISIBLE) &&
1004  !(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_DISABLED))
1005  {
1006  pEntry = pDialogWindow->pwContents + i1;
1007  break;
1008  }
1009  }
1010  }
1011  }
1012  else
1013  {
1014  for(i1=iCurFocus + 1; i1 < pDialogWindow->nContents; i1++)
1015  {
1016  if((pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS) &&
1017  (pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_VISIBLE) &&
1018  !(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_DISABLED))
1019  {
1020  pEntry = pDialogWindow->pwContents + i1;
1021  break;
1022  }
1023  }
1024 
1025  if(!pEntry)
1026  {
1027  for(i1=0; i1 < iCurFocus; i1++)
1028  {
1029  if((pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS) &&
1030  (pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_VISIBLE) &&
1031  !(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_DISABLED))
1032  {
1033  pEntry = pDialogWindow->pwContents + i1;
1034  break;
1035  }
1036  }
1037  }
1038  }
1039  }
1040  }
1041 
1042  if(!pEntry)
1043  {
1044  WB_WARN_PRINT("%s - unable to set focus for DLG_FOCUS event, l[0]=%ld, l[1]=%ld\n",
1045  __FUNCTION__, pEvent->xclient.data.l[0], pEvent->xclient.data.l[1]);
1046  // no focus - can't do it
1047  break; // for now allow it without error
1048  }
1049  else
1050  {
1051  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Dialog,
1052  "%s - setting focus for DLG_FOCUS event, l[0]=%ld, l[1]=%ld\n",
1053  __FUNCTION__, pEvent->xclient.data.l[0], pEvent->xclient.data.l[1]);
1054  }
1055 
1056  // set all of the focus bits correctly
1057 
1058  for(i1=0; i1 < pDialogWindow->nContents; i1++)
1059  {
1060  if(pEntry == pDialogWindow->pwContents + i1)
1061  {
1062  pEntry->iFlags |= WBDialogEntry_HAS_FOCUS;
1063  }
1064  else if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_HAS_FOCUS)
1065  {
1066  pDialogWindow->pwContents[i1].iFlags &= ~WBDialogEntry_HAS_FOCUS;
1067  WBInvalidateGeom(pDialogWindow->pwContents[i1].wID, NULL, 1); // force re-paint
1068  }
1069  }
1070 
1071  // NEXT, see if I (or any of my children) have the focus. If not, I've already
1072  // set the 'HAS FOCUS' bit so exit without actually changing the focus.
1073  // Otherwise I'll need to actually set the focus via WBSetInputFocus
1074 
1075  wFocus = None;
1076  XGetInputFocus(pDisplay, &wFocus, &i1);
1077 
1078  if(wFocus != None && (wFocus == wID || WBIsChildWindow(wID, wFocus)))
1079  {
1080 #ifndef NO_DEBUG
1081  char *p1 = WBGetAtomName(pDisplay, (Atom)pEntry->iID);
1082 
1083  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Dialog,
1084  "%s - me or child has focus, setting to %d (%s), %d (%08xH)\n",
1085  __FUNCTION__, pEntry->iID, p1,
1086  (int)pEntry->wID, (int)pEntry->wID);
1087  if(p1)
1088  {
1089  WBFree(p1);
1090  }
1091 #endif // NO_DEBUG
1092 
1093  //XSetInputFocus(pDisplay, pEntry->wID, RevertToParent, CurrentTime);
1094  WBSetInputFocus(pEntry->wID);
1095  WBInvalidateGeom(pEntry->wID, NULL, 1); // force re-paint
1096  }
1097 
1098  // Finally, for a control that supports it, set the 'checked' property if
1099  // 'data.l[2]' is NOT zero
1100 
1101  if(pEvent->xclient.data.l[2])
1102  {
1103  WBDialogControl *pCtrl = DLGGetDialogControlStruct(pEntry->wID);
1104  if(pCtrl)
1105  {
1106  // if it's a checkable button, set/toggle the check
1107  // for now, I use the type indicated by 'ulFlags' in case I have a control
1108  // of a different class that behaves as one of these kinds of buttons
1109 
1110  int iType = pCtrl->ulFlags & BUTTON_TYPEMASK; // TODO: use the class or the type?
1111 
1112  if(iType == BUTTON_CheckButton || iType == BUTTON_TriStateButton ||
1113  iType == BUTTON_RadioButton || iType == BUTTON_FirstRadioButton)
1114  {
1115  WBDialogControlSetCheck(pCtrl, -1); // flip check on checkbox; set check on radio button
1116  }
1117  }
1118  }
1119  }
1120 
1121  break;
1122 
1123  case KeyPress:
1124  case KeyRelease:
1125 
1126  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Keyboard | DebugSubSystem_Dialog,
1127  "%s KEY PRESS/RELEASE - dialog window - check for hotkeys\n", __FUNCTION__);
1128 
1129  iRval = DLGProcessHotKey((WBDialogWindow *)pDialogWindow, pEvent);
1130 
1131  if(iRval)
1132  {
1133  return iRval;
1134  }
1135 
1136  break;
1137 
1138  case FocusIn:
1139 
1140  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Dialog,
1141  "%s - FocusIn handler\n", __FUNCTION__);
1142 
1143  // first, check for a window that has the focus
1144 
1145  for(i1=0; i1 < pDialogWindow->nContents; i1++)
1146  {
1147  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_HAS_FOCUS)
1148  {
1149  Window wIDCtrl = pDialogWindow->pwContents[i1].wID;
1150  Display *pDisplay = WBGetWindowDisplay(wIDCtrl);
1151  int iCtrlID = pDialogWindow->pwContents[i1].iID;
1152 
1153  if(WBIsValid(pDisplay, wIDCtrl))
1154  {
1155 #ifndef NO_DEBUG
1156  WB_IF_DEBUG_LEVEL(DebugLevel_Heavy | DebugSubSystem_Dialog)
1157  {
1158  char *p1 = NULL;
1159  if(iCtrlID < WB_MIN_STD_CTRL_ID)
1160  {
1161  p1 = WBGetAtomName(pDisplay, (Atom)iCtrlID);
1162  }
1163 
1164  WBDebugPrint("%s - me or child has focus, setting to %d (%s), %d (%08xH)\n",
1165  __FUNCTION__, iCtrlID,
1166  (const char *)(p1 ? p1 : "NULL"),
1167  (int)pDialogWindow->pwContents[i1].wID, (int)pDialogWindow->pwContents[i1].wID);
1168  if(p1)
1169  {
1170  WBFree(p1);
1171  }
1172  }
1173 #endif // NO_DEBUG
1174 
1175  //XSetInputFocus(pDisplay, wID, RevertToParent, CurrentTime);
1176 // WBSetInputFocus(wID);
1177  {
1178  XClientMessageEvent evt = {
1179  .type=ClientMessage,
1180  .serial=0,
1181  .send_event=0,
1182  .display=pDisplay,
1183  .window=wID,
1184  .message_type=aDLG_FOCUS,
1185  .format=32
1186  };
1187  evt.data.l[0] = 0;
1188  evt.data.l[1] = iCtrlID; // set focus to this one
1189  evt.data.l[2] = 0; // do not assign 'checked' to button controls
1190 
1191  WBPostEvent(wID, (XEvent *)&evt); // send a regular event so things happen in the right order
1192  }
1193 
1194  break;
1195  }
1196  else
1197  {
1198  WB_WARN_PRINT("Warning: %s %s:%d window %d (%08xH) isn't valid, can't set focus\n",
1199  __FUNCTION__, __FILE__, __LINE__, (int)wID, (int)wID);
1200  }
1201  }
1202  }
1203 
1204  if(i1 >= pDialogWindow->nContents) // none had the focus? Find one and set it!
1205  {
1206  for(i1=0; i1 < pDialogWindow->nContents; i1++)
1207  {
1208  if((pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS) &&
1209  (pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_VISIBLE) &&
1210  !(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_DISABLED))
1211  {
1212  Window wIDCtrl = pDialogWindow->pwContents[i1].wID;
1213  Display *pDisplay = WBGetWindowDisplay(wIDCtrl);
1214  int iCtrlID = pDialogWindow->pwContents[i1].iID;
1215 
1216  if(WBIsValid(pDisplay, wIDCtrl))
1217  {
1218 #ifndef NO_DEBUG
1219  WB_IF_DEBUG_LEVEL(DebugLevel_Heavy | DebugSubSystem_Dialog)
1220  {
1221  char *p1 = NULL;
1222  if(iCtrlID < WB_MIN_STD_CTRL_ID)
1223  {
1224  p1 = WBGetAtomName(pDisplay, (Atom)iCtrlID);
1225  }
1226 
1227  WBDebugPrint("%s - me or child has focus, setting to %d (%s), %d (%08xH)\n",
1228  __FUNCTION__, iCtrlID,
1229  (const char *)(p1 ? p1 : "NULL"),
1230  (int)pDialogWindow->pwContents[i1].wID, (int)pDialogWindow->pwContents[i1].wID);
1231  if(p1)
1232  {
1233  WBFree(p1);
1234  }
1235  }
1236 #endif // NO_DEBUG
1237 
1238 // pDialogWindow->pwContents[i1].iFlags |= WBDialogEntry_HAS_FOCUS;
1239 //
1240 // //XSetInputFocus(pDisplay, wID, RevertToParent, CurrentTime);
1241 // WBSetInputFocus(wID);
1242  {
1243  XClientMessageEvent evt = {
1244  .type=ClientMessage,
1245  .serial=0,
1246  .send_event=0,
1247  .display=pDisplay,
1248  .window=wID,
1249  .message_type=aDLG_FOCUS,
1250  .format=32
1251  };
1252  evt.data.l[0] = 0;
1253  evt.data.l[1] = iCtrlID; // set focus to this one
1254 
1255  WBPostEvent(wID, (XEvent *)&evt); // send a regular event so things happen in the right order
1256  }
1257  break;
1258  }
1259  else
1260  {
1261  WB_WARN_PRINT("Warning: %s %s:%d window %d (%08xH) isn't valid, can't set focus\n",
1262  __FUNCTION__, __FILE__, __LINE__, (int)wID, (int)wID);
1263  }
1264  }
1265  }
1266  }
1267 
1268  break;
1269  }
1270 
1271 
1272  if(pDialogWindow->pDLGCallback)
1273  {
1274  // for most messages, if I handle it here, I don't do further processing
1275 
1276  iRval = pDialogWindow->pDLGCallback(wID, pEvent);
1277 
1278  if(iRval)
1279  {
1280  // check message types that I do *NOT* want to 'bail out' for
1281 
1282  switch(pEvent->type)
1283  {
1284  case DestroyNotify:
1285  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Dialog,
1286  "%s - DestroyNotify and user callback returned a non-zero value\n", __FUNCTION__);
1287  break;
1288 
1289  case Expose:
1290  return iRval; // 'expose' event already handled
1291 
1292  default:
1293  return iRval;
1294  }
1295  }
1296  }
1297 
1298  // TODO: message re-direction to children AFTER 'pDLGCallback'
1299  // the system is SUPPOSED to destroy children first...
1300 
1301  // special handling for 'destroy'
1302  if(pEvent->type == DestroyNotify &&
1303  pEvent->xdestroywindow.window == wID)
1304  {
1305  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Dialog,
1306  "%s - DestroyNotify Window=%d\n", __FUNCTION__, (int)wID);
1307 
1308  WBSetWindowData(wID, 0, NULL);
1309 
1310  if(pDialogWindow)
1311  {
1312  Window wIDOwner = pDialogWindow->wbDLG.wIDOwner;
1313  __internal_destroy_dialog_window(pDialogWindow);
1314 
1315  WBFree(pDialogWindow);
1316 
1317  // if the window had an owner, raise it and assign focus to it
1318 
1319  if(wIDOwner != None)
1320  {
1321  WBPostDelayedSetFocusAppEvent(WBGetWindowDisplay(wIDOwner), wIDOwner, wID, 100); // 100 msec delay
1322  }
1323  }
1324 
1325  return 1; // tells caller I did internal handling for this
1326  }
1327 
1328  return 0;
1329 }
1330 
1331 void DLGRecalcLayout(Window wID)
1332 {
1333  // this callback happens any time I change the window size. Use this
1334  // also to re-size scrollbars and status bars and menus.
1335 
1336 }
1337 
1338 void * DLGGetDialogWindowUserData(Window wID)
1339 {
1341 
1342  if(!pDlg)
1343  {
1344  return NULL;
1345  }
1346 
1347  return pDlg->wbDLG.pUserData;
1348 }
1349 
1350 Window DLGGetDialogControl(WBDialogWindow *pDialog, int iControlID)
1351 {
1352  DIALOG_WINDOW *pSelf = (DIALOG_WINDOW *)pDialog;
1353  int i1;
1354 
1355  if(!pSelf || !pSelf->pwContents)
1356  return None; /* 0 */
1357 
1358  for(i1=0; i1 < pSelf->nContents && i1 < pSelf->nMaxContents; i1++)
1359  {
1360  if(pSelf->pwContents[i1].iID == iControlID)
1361  {
1362  return pSelf->pwContents[i1].wID;
1363  }
1364  }
1365 
1366  return None;
1367 }
1368 
1370 {
1371  DIALOG_WINDOW *pSelf = (DIALOG_WINDOW *)pDialog;
1372  int i1;
1373 
1374  if(!pSelf || !pSelf->pwContents)
1375  return NULL;
1376 
1377  for(i1=0; i1 < pSelf->nContents && i1 < pSelf->nMaxContents; i1++)
1378  {
1379  if(pSelf->pwContents[i1].wID == idControl)
1380  {
1381  return pSelf->pwContents + i1;
1382  }
1383  }
1384 
1385  return NULL;
1386 }
1387 
1389 {
1390  DIALOG_WINDOW *pSelf = (DIALOG_WINDOW *)pDialog;
1391 
1392  if(!pSelf || !pSelf->pwContents)
1393  return None; /* 0 */
1394 
1395  return pSelf->pwContents[0].wID;
1396 }
1397 
1398 Window DLGGetNextDialogControl(WBDialogWindow *pDialog, Window idControl)
1399 {
1400  DIALOG_WINDOW *pSelf = (DIALOG_WINDOW *)pDialog;
1401  int i1;
1402 
1403  if(!pSelf || !pSelf->pwContents)
1404  return None;
1405 
1406  for(i1=0; i1 < pSelf->nContents && i1 < pSelf->nMaxContents; i1++)
1407  {
1408  if(pSelf->pwContents[i1].wID == idControl)
1409  {
1410  if((i1 + 1) >= pSelf->nContents || (i1 + 1) >= pSelf->nMaxContents)
1411  i1 = 0;
1412  else
1413  i1++;
1414 
1415  return pSelf->pwContents[i1].wID;
1416  }
1417  }
1418 
1419  return None;
1420 }
1421 
1422 Window DLGGetPrevDialogControl(WBDialogWindow *pDialog, Window idControl)
1423 {
1424  DIALOG_WINDOW *pSelf = (DIALOG_WINDOW *)pDialog;
1425  int i1;
1426 
1427  if(!pSelf || !pSelf->pwContents)
1428  return None;
1429 
1430  for(i1=0; i1 < pSelf->nContents && i1 < pSelf->nMaxContents; i1++)
1431  {
1432  if(pSelf->pwContents[i1].wID == idControl)
1433  {
1434  if(i1 == 0)
1435  {
1436  i1 = pSelf->nContents - 1;
1437 
1438  if(i1 >= pSelf->nMaxContents)
1439  i1 = pSelf->nMaxContents - 1;
1440  }
1441  else
1442  i1--;
1443 
1444  return pSelf->pwContents[i1].wID;
1445  }
1446  }
1447 
1448  return None;
1449 }
1450 
1451 
1452 int DLGProcessHotKey(WBDialogWindow *pDLG, XEvent *pEvent)
1453 {
1454 //char tbuf[16];
1455 //int cbLen;
1456 KeySym ks;
1457 int iACSMask = ShiftMask | ControlMask | Mod1Mask;
1458 int i1, iCurFocus, iCanHaveFocus = 0, iNoEnter = 0, iNoEsc = 0, iNoTab = 0;
1459 WBDialogEntry *pFocusEntry = NULL, *pDefaultEntry = NULL, *pCancelEntry = NULL;
1460 Display *pDisplay = WBGetWindowDisplay(pDLG->wID);
1461 
1462 
1463  iACSMask &= ((XKeyEvent *)pEvent)->state;
1464 
1465 // TODO: do I need the CTRL/ALT/SHIFT info? uncomment these later if I need it
1466 // for now they are commented out because linux GCC barphs on unused assigned vars
1467 // iCtrl = (iACSMask & ControlMask) ? 1 : 0;
1468 // iShift = (iACSMask & ShiftMask) ? 1 : 0;
1469 // iAlt = (iACSMask & Mod1Mask) ? 1 : 0;
1470 
1471  // find default & current focus control entries
1472 
1473  iCurFocus = -1; // for later; helps make sure things work
1474 
1475  for(i1=0; ((DIALOG_WINDOW *)pDLG)->pwContents && i1 < ((DIALOG_WINDOW *)pDLG)->nContents; i1++)
1476  {
1477  if((((DIALOG_WINDOW *)pDLG)->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS) &&
1478  (((DIALOG_WINDOW *)pDLG)->pwContents[i1].iFlags & WBDialogEntry_VISIBLE) &&
1479  !(((DIALOG_WINDOW *)pDLG)->pwContents[i1].iFlags & WBDialogEntry_DISABLED))
1480  {
1481  iCanHaveFocus = 1;
1482  }
1483 
1484  if(((DIALOG_WINDOW *)pDLG)->pwContents[i1].iFlags & WBDialogEntry_HAS_FOCUS)
1485  {
1486  pFocusEntry = ((DIALOG_WINDOW *)pDLG)->pwContents + i1;
1487  iCurFocus = i1; // store here also
1488  }
1489 
1490  if(((DIALOG_WINDOW *)pDLG)->pwContents[i1].iFlags & WBDialogEntry_DEFAULT)
1491  {
1492  pDefaultEntry = ((DIALOG_WINDOW *)pDLG)->pwContents + i1;
1493  }
1494 
1495  if(((DIALOG_WINDOW *)pDLG)->pwContents[i1].aClass == aCANCELBUTTON_CONTROL)
1496  {
1497  pCancelEntry = ((DIALOG_WINDOW *)pDLG)->pwContents + i1;
1498  }
1499 
1500  if(pDefaultEntry && pFocusEntry && pCancelEntry)
1501  {
1502  break;
1503  }
1504  }
1505 
1506  if(pFocusEntry
1507  && (pFocusEntry->aClass == aEDIT_CONTROL || (pFocusEntry->iFlags & WBDialogEntry_EDIT)))
1508  {
1509  if(pFocusEntry->iFlags & WBDialogEntry_MULTILINE)
1510  {
1511  iNoEnter = 1;
1512  }
1513  if(pFocusEntry->iFlags & WBDialogEntry_ALLCHARS)
1514  {
1515  iNoEnter = 1;
1516  iNoEsc = 1;
1517  iNoTab = 1;
1518  }
1519  }
1520 
1521  if(pEvent->type == KeyPress || pEvent->type == KeyRelease)
1522  {
1523  ks = XLookupKeysym((XKeyEvent *)pEvent, 0); // always use '0' index for these
1524 
1525  if(iACSMask == Mod1Mask) // alt key held down, no others
1526  {
1527  const char *pKey = XKeysymToString(ks);
1528 
1529  if(!pKey
1530 #ifdef XK_Alt_L // key symbol for left Alt button
1531  || ks == XK_Alt_L
1532 #endif // XK_Alt_L
1533 #ifdef XK_Alt_R // key symbol for right Alt button
1534  || ks == XK_Alt_R
1535 #endif // XK_Alt_R
1536  || ks == XK_Return // don't translate this one, either - the string is 'Return' - oops
1537  || strlen(pKey) > 1) // final test, multi-byte chars don't compare with this algorith - TODO: fix it!
1538  {
1539  // alt key is non-printing - this code will skip it
1540  return 0;
1541  }
1542 
1543  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Event | DebugSubSystem_Dialog | DebugSubSystem_Keyboard,
1544  "%s - user pressed Alt+%s %d (%xH)\n",
1545  __FUNCTION__, pKey,
1546  (unsigned int)ks, (unsigned int)ks); // KeySym is a 'CARD32', which is an unsigned 32-bit value
1547 
1548  if(pEvent->type == KeyRelease && iCanHaveFocus)
1549  {
1550  // who gets this alt key? Keystroke is ALT + *pKey
1551  // search everyone's caption until I find one. Start with the control that
1552  // currently has the focus, then wrap around. That way duplicate underscores
1553  // will bounce from one to the other in dialog order.
1554 
1555  if(iCurFocus < 0 || iCurFocus >= ((DIALOG_WINDOW *)pDLG)->nContents) // safety valve anyway
1556  {
1557  iCurFocus = -1; // force it to this value if corrupt or not assigned
1558  }
1559 
1560  // this 'clever' loop will stop when it reaches 'iCurFocus' or the end of the list
1561  // and will wrap around if 'iCurFocus' is NOT negative. Above code forces it to be in range
1562  for(i1=iCurFocus + 1; i1 != iCurFocus && i1 < ((DIALOG_WINDOW *)pDLG)->nContents;
1563  i1 = iCurFocus < 0 || (i1 + 1) < ((DIALOG_WINDOW *)pDLG)->nContents ? i1 + 1 : 0)
1564  {
1565  // look at the caption, see if it contains an underscore - first one is hotkey
1566  WBDialogEntry *pEntry = ((DIALOG_WINDOW *)pDLG)->pwContents + i1;
1567 
1568  if((pEntry->iFlags & WBDialogEntry_CAN_HAVE_FOCUS) &&
1569  (pEntry->iFlags & WBDialogEntry_VISIBLE) &&
1570  !(pEntry->iFlags & WBDialogEntry_DISABLED))
1571  {
1573 
1574  if(pCtrl)
1575  {
1576  const char *pCaption = WBDialogControlGetCaption(pCtrl);
1577  Window wIDOwner = pCtrl->pOwner ? pCtrl->pOwner->wID : WBGetParentWindow(pEntry->wID);
1578 
1579  if(pCaption)
1580  {
1581  const char *p1 = strchr(pCaption, '_'); // find underscore
1582  if(p1 && p1[1])
1583  {
1584  if(toupper(p1[1]) == toupper(*pKey)) // compare as upper case
1585  {
1586  WB_ERROR_PRINT("TEMPORARY: %s - key release, key=\"%s\", ks=%d\n", __FUNCTION__, pKey, (int)ks);
1587 
1588  // switch focus to THIS control
1589 
1590  XClientMessageEvent evt = {
1591  .type=ClientMessage,
1592  .serial=0,
1593  .send_event=0,
1594  .display=pDisplay,
1595  .window=wIDOwner,
1596  .message_type=aDLG_FOCUS,
1597  .format=32
1598  };
1599  evt.data.l[0] = 0;
1600  evt.data.l[1] = pEntry->iID;
1601  evt.data.l[2] = 1; // to indicate that it should be 'checked' if it's a button
1602 
1603  WBPostPriorityEvent(wIDOwner, (XEvent *)&evt); // priority event makes it happen faster
1604  return 1; // handled!
1605  }
1606  }
1607  }
1608  }
1609  }
1610  }
1611 
1612  return 0; // did NOT handle it
1613  }
1614  }
1615  else if(!iNoEnter
1616  && pEvent->type == KeyRelease
1617  && ((int)ks == XK_Return || (int)ks == XK_KP_Enter) // someone pressed <ENTER>
1618  && iACSMask == 0) // no modifiers
1619  {
1620  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Event | DebugSubSystem_Dialog | DebugSubSystem_Keyboard,
1621  "%s - DIALOG WINDOW - <enter> detected\n", __FUNCTION__);
1622 
1623  if((!(pFocusEntry->iFlags & WBDialogEntry_PUSHBUTTON) && pDefaultEntry)
1624  || pFocusEntry == pDefaultEntry)
1625  {
1626  XClientMessageEvent evt = {
1627  .type=ClientMessage,
1628  .serial=0,
1629  .send_event=0,
1630  .display=pDisplay,
1631  .window=pDLG->wID,
1632  .message_type=aCONTROL_NOTIFY,
1633  .format=32
1634  };
1635  evt.data.l[0] = aBUTTON_PRESS;
1636  evt.data.l[1] = pDefaultEntry->iID;
1637  evt.data.l[2] = WBCreatePointerHash(pDefaultEntry);
1638 
1639  // simulate a button press of the default button
1640  WBPostPriorityEvent(pDLG->wID, (XEvent *)&evt); // priority event makes it happen faster
1641  return 1;
1642  }
1643  }
1644  else if(!iNoEsc
1645  && pEvent->type == KeyRelease
1646  && ((int)ks == XK_Escape) // someone pressed <ESC>
1647  && iACSMask == 0) // no modifiers
1648  {
1649  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Event | DebugSubSystem_Dialog | DebugSubSystem_Keyboard,
1650  "%s - DIALOG WINDOW - <ESC> detected\n", __FUNCTION__);
1651 
1652  if((!(pFocusEntry->iFlags & WBDialogEntry_PUSHBUTTON) && pCancelEntry)
1653  || pFocusEntry->aClass == aCANCELBUTTON_CONTROL)
1654  {
1655  XClientMessageEvent evt = {
1656  .type=ClientMessage,
1657  .serial=0,
1658  .send_event=0,
1659  .display=pDisplay,
1660  .window=pDLG->wID,
1661  .message_type=aCONTROL_NOTIFY,
1662  .format=32
1663  };
1664  evt.data.l[0] = aBUTTON_PRESS;
1665  evt.data.l[1] = pCancelEntry->iID;
1666  evt.data.l[2] = WBCreatePointerHash(pCancelEntry);
1667 
1668  // simulate a button press of the default button
1669  WBPostPriorityEvent(pDLG->wID, (XEvent *)&evt); // priority event makes it happen faster
1670  return 1;
1671  }
1672  }
1673  else if(!iNoTab
1674  && pEvent->type == KeyRelease
1675  && ((int)ks == XK_Tab || (int)ks == XK_KP_Tab) // someone pressed <TAB>
1676  && (iACSMask == 0 || iACSMask == ShiftMask)) // either a tab or back-tab
1677  {
1678  XClientMessageEvent evt = {
1679  .type=ClientMessage,
1680  .serial=0,
1681  .send_event=0,
1682  .display=pDisplay,
1683  .window=pDLG->wID,
1684  .message_type=aDLG_FOCUS,
1685  .format=32
1686  };
1687  evt.data.l[0] = iACSMask == 0 ? 1 : -1;
1688  evt.data.l[1] = 0;
1689  evt.data.l[2] = 0;
1690 
1691  WBPostPriorityEvent(pDLG->wID, (XEvent *)&evt); // priority event makes it happen faster
1692  return 1;
1693  }
1694  else if(pEvent->type == KeyRelease)
1695  {
1696 // fprintf(stderr, "TEMPORARY: user pressed %s (%d,%d) and the state is %d (%08xH)\n",
1697 // tbuf, tbuf[0], ((XKeyEvent *)pEvent)->keycode,
1698 // ((XKeyEvent *)pEvent)->state,((XKeyEvent *)pEvent)->state );
1699  }
1700  }
1701  else
1702  {
1703  WB_WARN_PRINT("%s - calling DLGProcessHotKey and it's not a key event\n", __FUNCTION__);
1704  }
1705 
1706  return 0;
1707 }
1708 
1709 
1710 
1711 
1712 
1713 
1714 
1715 // creating child windows
1716 
1717 enum DlgResourceStringKeywordIndices
1718 {
1719  DlgResourceKW__BEGIN_DIALOG=0,
1720  DlgResourceKW__END_DIALOG,
1721  DlgResourceKW__FONT,
1722  DlgResourceKW__CONTROL,
1723  DlgResourceKW__ID,
1724  DlgResourceKW__X,
1725  DlgResourceKW__Y,
1726  DlgResourceKW__WIDTH,
1727  DlgResourceKW__HEIGHT,
1728  DlgResourceKW__VISIBLE,
1729  DlgResourceKW__INVISIBLE,
1730  DlgResourceKW__TITLE,
1731  DlgResourceKW__VA_DLGTOP, // control alignment flags with respect to dialog
1732  DlgResourceKW__VA_DLGBOTTOM,
1733  DlgResourceKW__VA_DLGCENTER,
1734  DlgResourceKW__HA_DLGLEFT,
1735  DlgResourceKW__HA_DLGRIGHT,
1736  DlgResourceKW__HA_DLGCENTER,
1737  DlgResourceKW__VRESIZE,
1738  DlgResourceKW__HRESIZE,
1739  DlgResourceKW__BORDER,
1740  DlgResourceKW__NOBORDER,
1741  DlgResourceKW__CLICKABLE,
1742  DlgResourceKW__CHECKED,
1743  DlgResourceKW__ENABLED,
1744  DlgResourceKW__DISABLED,
1745  DlgResourceKW__VA_TEXT_TOP,
1746  DlgResourceKW__VA_TEXT_BOTTOM,
1747  DlgResourceKW__VA_TEXT_CENTER,
1748  DlgResourceKW__HA_TEXT_LEFT,
1749  DlgResourceKW__HA_TEXT_RIGHT,
1750  DlgResourceKW__HA_TEXT_CENTER,
1751 };
1752 
1753 static const char * const aszKeywords[]={ "BEGIN_DIALOG","END_DIALOG","FONT","CONTROL","ID",
1754  "X","Y","WIDTH","HEIGHT","VISIBLE","INVISIBLE","TITLE",
1755  "VALIGN_DIALOG_BOTTOM","VALIGN_DIALOG_TOP","VALIGN_DIALOG_CENTER",
1756  "HALIGN_DIALOG_LEFT","HALIGN_DIALOG_RIGHT","HALIGN_DIALOG_CENTER",
1757  "VRESIZE","HRESIZE","BORDER","NOBORDER","CLICKABLE",
1758  "CHECKED","ENABLED","DISABLED",
1759  "VALIGN_TEXT_TOP","VALIGN_TEXT_BOTTOM","VALIGN_TEXT_CENTER",
1760  "HALIGN_TEXT_LEFT","HALIGN_TEXT_RIGHT","HALIGN_TEXT_CENTER",
1761  NULL };
1762 
1763 // some built-in symbols for dialog resources and their corresponding symbol IDs
1764 static const char * const aszSymbols[]={ "IDOK", "IDCANCEL", "IDYES", "IDNO", "IDRETRY", "IDABORT", "IDIGNORE", "IDSTATIC", "IDNONE", NULL };
1765 static const int iIDSymbols[]={ IDOK, IDCANCEL, IDYES, IDNO, IDRETRY, IDABORT, IDIGNORE, IDSTATIC, IDNONE, -1 };
1766 
1767 static int InternalCreateChildWindowsSub(DIALOG_WINDOW *pDlg, const char **pszDialogResource);
1768 
1769 static int InternalCreateChildWindows(DIALOG_WINDOW *pDlg, const char *szDialogResource)
1770 {
1771 const char *p1, *p2;
1772 int iRval;
1773 
1774  if(!szDialogResource || !*szDialogResource)
1775  {
1776  return 0; // don't allow it
1777  }
1778 
1779  p1 = szDialogResource;
1780  while(*p1 && *p1 <= ' ')
1781  p1++;
1782 
1783  p2 = p1;
1784  while(*p1 && *p1 > ' ')
1785  p1++;
1786 
1787  // must be 'BEGIN_DIALOG'
1788  if((p1 - p2) != strlen(aszKeywords[DlgResourceKW__BEGIN_DIALOG]) ||
1789  memcmp(p2, aszKeywords[DlgResourceKW__BEGIN_DIALOG], (p1 - p2)) != 0)
1790  {
1791  WB_WARN_PRINT("%s - dialog box resource missing 'BEGIN_DIALOG'\n", __FUNCTION__);
1792  return 0;
1793  }
1794 
1795  while(*p1 && *p1 <= ' ')
1796  p1++;
1797 
1798  iRval = InternalCreateChildWindowsSub(pDlg, &p1);
1799  if(!iRval)
1800  {
1801  WB_ERROR_PRINT("%s - InternalCreateChildWindowsSub failure at \"%-.30s\"\n", __FUNCTION__, p1);
1802  }
1803 
1804  return iRval;
1805 }
1806 
1807 static int get_id_val(const char *szVal, int iLen)
1808 {
1809 // NOTE: if I need a different list for dialog control ID symbols, put it "up there" with aszSymbols (etc.)
1810 int i1;
1811 char tbuf[256];
1812 
1813 
1814  if(iLen > 0)
1815  {
1816  if(iLen >= sizeof(tbuf))
1817  {
1818  iLen = sizeof(tbuf) - 1;
1819  }
1820 
1821  memcpy(tbuf, szVal, iLen);
1822  }
1823 
1824  if(iLen < 0)
1825  {
1826  iLen = 0;
1827  }
1828 
1829  tbuf[iLen] = 0;
1830 
1831  if(!tbuf[0])
1832  {
1833  return 0;
1834  }
1835 
1836  WBDeQuoteString(tbuf);
1837 
1838  if(tbuf[0] >= '0' && tbuf[0] <= '9')
1839  {
1840  return atoi(tbuf);
1841  }
1842 
1843  // check for well-known alpha-numeric values
1844 
1845  for(i1=0; aszSymbols[i1]; i1++)
1846  {
1847  if(!strcmp(tbuf, aszSymbols[i1]))
1848  {
1849  return iIDSymbols[i1];
1850  }
1851  }
1852 
1853  // TODO: registered list of symbol names
1854 
1855  // now assume it's an atom, and return that
1856 
1857  return WBGetAtom(WBGetDefaultDisplay(), tbuf);
1858 }
1859 
1860 
1861 
1862 static int InternalCreateChildWindowsSub(DIALOG_WINDOW *pDlg, const char **pszDialogResource)
1863 {
1864 const char *p1, *p2, *p3;
1865 int i1, iKW, nKids = 0, iRval = 0;
1866 unsigned int iCurSize = 0, iCurContentSize = 0;
1867 struct _KIDS_
1868 {
1869  Atom aClass; // atom 'class' for control entry
1870  int iID; // ID for control entry
1871  int iX, iY, iWidth, iHeight; // that too
1872  int iFlags; // flags to be assigned when control is created
1873  char *szTitle; // pointer to WBAlloc'd string with title
1874  char *szProp; // pointer to WBAlloc'd string with additional properties
1875 } *pKids = NULL;
1876 char tbuf[256];
1877 
1878 
1879  p1 = p2 = *pszDialogResource;
1880 
1881  while(*p1)
1882  {
1883  p2 = p1;
1884  while(*p1 && *p1 > ' ')
1885  {
1886  if(*p1 == '"' || *p1 == '\'')
1887  {
1888  // we allow embedded quoted strings preceded by non-quotes
1889  char c = *(p1++);
1890 
1891  while(*p1 &&
1892  (*p1 != c || p1[1] == c))
1893  {
1894  if(*p1 == c)
1895  {
1896  p1++;
1897  }
1898 
1899  if(*p1 == '\n' || *p1 == '\r') // not allowed
1900  {
1901  WB_WARN_PRINT("%s - Illegal newline/return character in embedded string within dialog resource\n",
1902  __FUNCTION__);
1903  *pszDialogResource = p2;
1904  return 0;
1905  }
1906 
1907  p1++;
1908  }
1909 
1910  if(*p1 == c)
1911  {
1912  p1++;
1913  }
1914  }
1915  else
1916  {
1917  p1++;
1918  }
1919  }
1920 
1921  for(iKW=0; aszKeywords[iKW]; iKW++)
1922  {
1923  i1 = strlen(aszKeywords[iKW]);
1924  if(i1 <= (p1 - p2) && // keyword length shorter than 'term' or equal in size
1925  (p2[i1] <= ' ' || p2[i1] == ':') &&
1926  !memcmp(p2, aszKeywords[iKW], i1))
1927  {
1928  p3 = p2 + i1;
1929 
1930  if(*p3 == ':')
1931  {
1932  p3++;
1933  }
1934 
1935  break;
1936  }
1937  }
1938 
1939  if(!aszKeywords[iKW])
1940  {
1941  WB_WARN_PRINT("%s - NULL in aszKeywords in InternalCreateChildWindowsSub within dialog resource\n", __FUNCTION__);
1942  // TODO: do I handle this as a custom property for a control?
1943  *pszDialogResource = p2;
1944  return 0;
1945  }
1946 
1947  if(iKW == DlgResourceKW__END_DIALOG)
1948  {
1949  if(!pKids)
1950  {
1951  pKids = WBAlloc(sizeof(*pKids)); // because it can't be NULL
1952  nKids = 0;
1953  }
1954 
1955  break;
1956  }
1957 
1958  if(iKW == DlgResourceKW__BEGIN_DIALOG)
1959  {
1960  WB_WARN_PRINT("%s - nested dialogs not supported (yet)\n", __FUNCTION__);
1961 
1962  *pszDialogResource = p2;
1963  return 0;
1964  }
1965 
1966  switch(iKW)
1967  {
1968  case DlgResourceKW__CONTROL:
1969 
1970  if(!pKids)
1971  {
1972  pKids = WBAlloc(iCurSize = (256 * sizeof(*pKids)));
1973  if(!pKids)
1974  {
1975  WB_ERROR_PRINT("%s - not enough memory for controls\n", __FUNCTION__);
1976  iKW = -1;
1977  break;
1978  }
1979 
1980 //#ifdef HAVE_MALLOC_USABLE_SIZE
1981  iCurSize = WBAllocUsableSize(pKids); // re-evaluate actual size
1982 //#endif // HAVE_MALLOC_USABLE_SIZE
1983 
1984  nKids = 1;
1985  }
1986  else if((nKids + 1) * sizeof(*pKids) > iCurSize)
1987  {
1988  int iNewSize;
1989  void *p0;
1990 
1991  iNewSize = (nKids + 128) * sizeof(*pKids);
1992  p0 = WBReAlloc(pKids, iNewSize);
1993 
1994  if(!p0)
1995  {
1996  WB_ERROR_PRINT("%s - not enough memory for all controls\n", __FUNCTION__);
1997  iKW = -1;
1998  break;
1999  }
2000 
2001 //#ifdef HAVE_MALLOC_USABLE_SIZE
2002  iCurSize = WBAllocUsableSize(p0); // re-evaluate actual size
2003 //#else // HAVE_MALLOC_USABLE_SIZE
2004 // iCurSize = iNewSize;
2005 //#endif // HAVE_MALLOC_USABLE_SIZE
2006 
2007  pKids = (struct _KIDS_ *)p0;
2008  nKids++;
2009  }
2010  else
2011  {
2012  nKids++;
2013  }
2014 
2015  memset(pKids + nKids - 1, 0, sizeof(*pKids));
2016 
2017  // get the correct atom for the control name
2018 
2019  if(p1 <= p3)
2020  {
2021  WB_WARN_PRINT("%s - Invalid control name %-.20s\n", __FUNCTION__, p3);
2022  iKW = -1;
2023 
2024  break;
2025  }
2026 
2027  memcpy(tbuf, p3, p1 - p3);
2028  tbuf[p1 - p3] = 0;
2029 
2030  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Dialog,
2031  "%s - control %s\n", __FUNCTION__, tbuf);
2032 
2033  pKids[nKids - 1].aClass = WBGetAtom(WBGetDefaultDisplay(), tbuf);
2034 
2035  break;
2036 
2037  case DlgResourceKW__ID:
2038  if(!nKids) // ID applied to dialog box is ignored
2039  {
2040  WB_WARN_PRINT("%s - Ignoring ID for dialog box, %.20s\n", __FUNCTION__, p3);
2041  }
2042  else
2043  {
2044  pKids[nKids - 1].iID = get_id_val(p3, p1 - p3);
2045  }
2046 
2047  break;
2048 
2049  case DlgResourceKW__X:
2050  if(!nKids) // X applied to dialog box is ignored
2051  {
2052  WB_WARN_PRINT("%s - Ignoring X for dialog box, %.20s\n", __FUNCTION__, p3);
2053  }
2054  else
2055  {
2056  pKids[nKids - 1].iX = get_id_val(p3, p1 - p3);
2057  }
2058 
2059  break;
2060 
2061  case DlgResourceKW__Y:
2062  if(!nKids) // Y applied to dialog box is ignored
2063  {
2064  WB_WARN_PRINT("%s - Ignoring Y for dialog box, %.20s\n", __FUNCTION__, p3);
2065  }
2066  else
2067  {
2068  pKids[nKids - 1].iY = get_id_val(p3, p1 - p3);
2069  }
2070 
2071  break;
2072 
2073  case DlgResourceKW__WIDTH:
2074  if(!nKids)
2075  {
2076  pDlg->wbDLG.iClientWidth = get_id_val(p3, p1 - p3);
2077  }
2078  else
2079  {
2080  pKids[nKids - 1].iWidth = get_id_val(p3, p1 - p3);
2081  }
2082 
2083  break;
2084 
2085  case DlgResourceKW__HEIGHT:
2086  if(!nKids)
2087  {
2088  pDlg->wbDLG.iClientHeight = get_id_val(p3, p1 - p3);
2089  }
2090  else
2091  {
2092  pKids[nKids - 1].iHeight = get_id_val(p3, p1 - p3);
2093  }
2094 
2095  break;
2096 
2097  case DlgResourceKW__VA_DLGTOP:
2098  if(!nKids) // visible applied to dialog box is ignored
2099  {
2100  WB_WARN_PRINT("%s - RESERVED: 'top aligned' property on dialog box\n", __FUNCTION__);
2101  }
2102  else
2103  {
2104  pKids[nKids - 1].iFlags &= ~WBDialogEntry_VAlignMask;
2105  pKids[nKids - 1].iFlags |= WBDialogEntry_VAlignTop;
2106  }
2107 
2108  break;
2109 
2110  case DlgResourceKW__VA_DLGBOTTOM:
2111  if(!nKids) // visible applied to dialog box is ignored
2112  {
2113  WB_WARN_PRINT("%s - RESERVED: 'bottom aligned' property on dialog box\n", __FUNCTION__);
2114  }
2115  else
2116  {
2117  pKids[nKids - 1].iFlags &= ~WBDialogEntry_VAlignMask;
2118  pKids[nKids - 1].iFlags |= WBDialogEntry_VAlignBottom;
2119  }
2120 
2121  break;
2122 
2123  case DlgResourceKW__VA_DLGCENTER:
2124  if(!nKids) // visible applied to dialog box is ignored
2125  {
2126  WB_WARN_PRINT("%s - RESERVED: 'center aligned' property on dialog box\n", __FUNCTION__);
2127  }
2128  else
2129  {
2130  pKids[nKids - 1].iFlags &= ~WBDialogEntry_VAlignMask;
2131  pKids[nKids - 1].iFlags |= WBDialogEntry_VAlignCenter;
2132  }
2133 
2134  break;
2135 
2136  case DlgResourceKW__HA_DLGLEFT:
2137  if(!nKids) // visible applied to dialog box is ignored
2138  {
2139  WB_WARN_PRINT("%s - RESERVED: 'left aligned' property on dialog box\n", __FUNCTION__);
2140  }
2141  else
2142  {
2143  pKids[nKids - 1].iFlags &= ~WBDialogEntry_HAlignMask;
2144  pKids[nKids - 1].iFlags |= WBDialogEntry_HAlignLeft;
2145  }
2146 
2147  break;
2148 
2149  case DlgResourceKW__HA_DLGRIGHT:
2150  if(!nKids) // visible applied to dialog box is ignored
2151  {
2152  WB_WARN_PRINT("%s - RESERVED: 'right aligned' property on dialog box\n", __FUNCTION__);
2153  }
2154  else
2155  {
2156  pKids[nKids - 1].iFlags &= ~WBDialogEntry_HAlignMask;
2157  pKids[nKids - 1].iFlags |= WBDialogEntry_HAlignRight;
2158  }
2159 
2160  break;
2161 
2162  case DlgResourceKW__HA_DLGCENTER:
2163  if(!nKids) // visible applied to dialog box is ignored
2164  {
2165  WB_WARN_PRINT("%s - RESERVED: 'center aligned' property on dialog box\n", __FUNCTION__);
2166  }
2167  else
2168  {
2169  pKids[nKids - 1].iFlags &= ~WBDialogEntry_HAlignMask;
2170  pKids[nKids - 1].iFlags |= WBDialogEntry_HAlignCenter;
2171  }
2172 
2173  break;
2174 
2175  case DlgResourceKW__VA_TEXT_TOP:
2176  if(!nKids) // visible applied to dialog box is ignored
2177  {
2178  WB_WARN_PRINT("%s - RESERVED: 'top text aligned' property on dialog box\n", __FUNCTION__);
2179  }
2180  else
2181  {
2182 // WB_ERROR_PRINT("TEMPORARY: %s - setting VA_TEXT_TOP\n", __FUNCTION__);
2183  pKids[nKids - 1].iFlags &= ~WBDialogEntry_VA_TEXT_MASK;
2184  pKids[nKids - 1].iFlags |= WBDialogEntry_VA_TEXT_TOP;
2185  }
2186 
2187  break;
2188 
2189  case DlgResourceKW__VA_TEXT_BOTTOM:
2190  if(!nKids) // visible applied to dialog box is ignored
2191  {
2192  WB_WARN_PRINT("%s - RESERVED: 'bottom text aligned' property on dialog box\n", __FUNCTION__);
2193  }
2194  else
2195  {
2196 // WB_ERROR_PRINT("TEMPORARY: %s - setting VA_TEXT_BOTTOM\n", __FUNCTION__);
2197  pKids[nKids - 1].iFlags &= ~WBDialogEntry_VA_TEXT_MASK;
2198  pKids[nKids - 1].iFlags |= WBDialogEntry_VA_TEXT_BOTTOM;
2199  }
2200 
2201  break;
2202 
2203  case DlgResourceKW__VA_TEXT_CENTER:
2204  if(!nKids) // visible applied to dialog box is ignored
2205  {
2206  WB_WARN_PRINT("%s - RESERVED: 'vertical center text aligned' property on dialog box\n", __FUNCTION__);
2207  }
2208  else
2209  {
2210 // WB_ERROR_PRINT("TEMPORARY: %s - setting VA_TEXT_CENTER\n", __FUNCTION__);
2211  pKids[nKids - 1].iFlags &= ~WBDialogEntry_VA_TEXT_MASK;
2212  pKids[nKids - 1].iFlags |= WBDialogEntry_VA_TEXT_CENTER;
2213  }
2214 
2215  break;
2216 
2217  case DlgResourceKW__HA_TEXT_LEFT:
2218  if(!nKids) // visible applied to dialog box is ignored
2219  {
2220  WB_WARN_PRINT("%s - RESERVED: 'left text aligned' property on dialog box\n", __FUNCTION__);
2221  }
2222  else
2223  {
2224 // WB_ERROR_PRINT("TEMPORARY: %s - setting HA_TEXT_LEFT\n", __FUNCTION__);
2225  pKids[nKids - 1].iFlags &= ~WBDialogEntry_HA_TEXT_MASK;
2226  pKids[nKids - 1].iFlags |= WBDialogEntry_HA_TEXT_LEFT;
2227  }
2228 
2229  break;
2230 
2231  case DlgResourceKW__HA_TEXT_RIGHT:
2232  if(!nKids) // visible applied to dialog box is ignored
2233  {
2234  WB_WARN_PRINT("%s - RESERVED: 'top text aligned' property on dialog box\n", __FUNCTION__);
2235  }
2236  else
2237  {
2238 // WB_ERROR_PRINT("TEMPORARY: %s - setting HA_TEXT_RIGHT\n", __FUNCTION__);
2239  pKids[nKids - 1].iFlags &= ~WBDialogEntry_HA_TEXT_MASK;
2240  pKids[nKids - 1].iFlags |= WBDialogEntry_HA_TEXT_RIGHT;
2241  }
2242 
2243  break;
2244 
2245  case DlgResourceKW__HA_TEXT_CENTER:
2246  if(!nKids) // visible applied to dialog box is ignored
2247  {
2248  WB_WARN_PRINT("%s - RESERVED: 'center text aligned' property on dialog box\n", __FUNCTION__);
2249  }
2250  else
2251  {
2252 // WB_ERROR_PRINT("TEMPORARY: %s - setting HA_TEXT_CENTER\n", __FUNCTION__);
2253  pKids[nKids - 1].iFlags &= ~WBDialogEntry_HA_TEXT_MASK;
2254  pKids[nKids - 1].iFlags |= WBDialogEntry_HA_TEXT_CENTER;
2255  }
2256 
2257  break;
2258 
2259  case DlgResourceKW__HRESIZE:
2260  if(!nKids)
2261  {
2263  }
2264  else
2265  {
2266  WB_WARN_PRINT("%s - WARNING: 'HRESIZE' property on dialog control (ignored)\n", __FUNCTION__);
2267  }
2268 
2269  break;
2270 
2271  case DlgResourceKW__VRESIZE:
2272  if(!nKids)
2273  {
2275  }
2276  else
2277  {
2278  WB_WARN_PRINT("%s - WARNING: 'VRESIZE' property on dialog control (ignored)\n", __FUNCTION__);
2279  }
2280 
2281  break;
2282 
2283  case DlgResourceKW__TITLE:
2284  if(!nKids)
2285  {
2286  if(pDlg->szTitle)
2287  WBFree(pDlg->szTitle);
2288 
2289  pDlg->szTitle = WBAlloc(p1 - p3 + 1);
2290  if(!pDlg->szTitle)
2291  {
2292  WB_WARN_PRINT("%s - Not enough memory for dialog box title %-.20s\n", __FUNCTION__, p3);
2293  iKW = -1;
2294  break;
2295  }
2296 
2297  memcpy(pDlg->szTitle, p3, p1 - p3);
2298  pDlg->szTitle[p1 - p3] = 0;
2299 
2300  WBDeQuoteString(pDlg->szTitle);
2301  }
2302  else
2303  {
2304  struct _KIDS_ *pKid = pKids + nKids - 1;
2305 
2306  if(pKid->szTitle)
2307  WBFree(pKid->szTitle);
2308 
2309  pKid->szTitle = WBAlloc(p1 - p3 + 1);
2310  if(!pKid->szTitle)
2311  {
2312  WB_WARN_PRINT("%s - Not enough memory for control title %-.20s\n", __FUNCTION__, p3);
2313  iKW = -1;
2314  break;
2315  }
2316 
2317  memcpy(pKid->szTitle, p3, p1 - p3);
2318  pKid->szTitle[p1 - p3] = 0;
2319 
2320  WBDeQuoteString(pKid->szTitle);
2321  }
2322 
2323  break;
2324 
2325  case DlgResourceKW__FONT:
2326  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Dialog,
2327  "%s - font definition %-.20s\n", __FUNCTION__, p3);
2328  // TODO: only valid for dialog box at this time?
2329  break;
2330 
2331  case DlgResourceKW__VISIBLE:
2332  if(!nKids) // visible applied to dialog box is ignored
2333  {
2334  WB_WARN_PRINT("%s - RESERVED: 'visible' property on dialog box\n", __FUNCTION__);
2335  }
2336  else
2337  {
2338  pKids[nKids - 1].iFlags |= WBDialogEntry_VISIBLE;
2339  }
2340 
2341  break;
2342 
2343  case DlgResourceKW__INVISIBLE:
2344  if(!nKids) // visible applied to dialog box is ignored
2345  {
2346  WB_WARN_PRINT("%s - RESERVED: 'invisible' property on dialog box\n", __FUNCTION__);
2347  }
2348  else
2349  {
2350  pKids[nKids - 1].iFlags &= ~WBDialogEntry_VISIBLE;
2351  }
2352 
2353  break;
2354 
2355  case DlgResourceKW__BORDER:
2356  if(!nKids)
2357  {
2358  WB_WARN_PRINT("%s - RESERVED: 'noborder' property on dialog box\n", __FUNCTION__);
2359  }
2360  else
2361  {
2362  pKids[nKids - 1].iFlags &= ~WBDialogEntry_NO_BORDER; // for those that have a border, shut it off
2363  }
2364 
2365  break;
2366 
2367  case DlgResourceKW__NOBORDER:
2368  if(!nKids)
2369  {
2370  WB_WARN_PRINT("%s - RESERVED: 'noborder' property on dialog box\n", __FUNCTION__);
2371  }
2372  else
2373  {
2374  pKids[nKids - 1].iFlags |= WBDialogEntry_NO_BORDER; // for those that have a border, shut it off
2375  }
2376 
2377  break;
2378 
2379  case DlgResourceKW__CLICKABLE:
2380  if(!nKids)
2381  {
2382  WB_WARN_PRINT("%s - RESERVED: 'clickable' property on dialog box\n", __FUNCTION__);
2383  }
2384  else
2385  {
2386  pKids[nKids - 1].iFlags |= WBDialogEntry_CAN_HAVE_FOCUS; // for those that don't normally get focus, allow it
2387  }
2388 
2389  break;
2390 
2391  case DlgResourceKW__CHECKED:
2392  if(!nKids)
2393  {
2394  WB_WARN_PRINT("%s - RESERVED: 'checked' property on dialog box\n", __FUNCTION__);
2395  }
2396  else if(pKids[nKids - 1].aClass == aRADIOBUTTON_CONTROL ||
2397  pKids[nKids - 1].aClass == aFIRSTRADIOBUTTON_CONTROL ||
2398  pKids[nKids - 1].aClass == aTRISTATEBUTTON_CONTROL ||
2399  pKids[nKids - 1].aClass == aCHECKBUTTON_CONTROL) // TODO: allow others?
2400  {
2401  pKids[nKids - 1].iFlags |= WBDialogEntry_CHECKED; // for those can be 'checked'
2402  }
2403 
2404  break;
2405 
2406  case DlgResourceKW__ENABLED:
2407  if(!nKids)
2408  {
2409  WB_WARN_PRINT("%s - RESERVED: 'disabled' property on dialog box\n", __FUNCTION__);
2410  }
2411  else
2412  {
2413  pKids[nKids - 1].iFlags &= ~WBDialogEntry_DISABLED; // will be 'disabled' initially
2414  }
2415 
2416  break;
2417 
2418  case DlgResourceKW__DISABLED:
2419  if(!nKids)
2420  {
2421  WB_WARN_PRINT("%s - RESERVED: 'disabled' property on dialog box\n", __FUNCTION__);
2422  }
2423  else
2424  {
2425  pKids[nKids - 1].iFlags |= WBDialogEntry_DISABLED; // will be 'disabled' initially
2426  }
2427 
2428  break;
2429  }
2430 
2431  if(iKW < 0) // error in control list
2432  {
2433  if(pKids)
2434  {
2435  for(i1=0; i1 < nKids; i1++)
2436  {
2437  if(pKids[i1].szTitle)
2438  {
2439  WBFree(pKids[i1].szTitle);
2440  }
2441  if(pKids[i1].szProp)
2442  {
2443  WBFree(pKids[i1].szProp);
2444  }
2445  }
2446 
2447  WBFree(pKids);
2448  pKids = NULL; // my error flag below
2449  }
2450 
2451  break; // error flag from above
2452  }
2453 
2454  while(*p1 && *p1 <= ' ')
2455  p1++;
2456  }
2457 
2458  if(!pKids)
2459  {
2460  WB_ERROR_PRINT("%s - NULL 'pKids' in InternalCreateChildWindowsSub\n", __FUNCTION__);
2461  *pszDialogResource = p2; // point of error
2462 
2463  return 0;
2464  }
2465 
2466  if(!pDlg->pwContents) // normally THIS will be the case
2467  {
2468  iCurContentSize = ((nKids + 384) & 0xffffff00L)
2469  * sizeof(*(pDlg->pwContents));
2470  pDlg->pwContents = (WBDialogEntry *)WBAlloc(iCurContentSize);
2471 
2472  if(!pDlg->pwContents)
2473  {
2474  iCurContentSize = 0;
2475 
2476  WB_ERROR_PRINT("%s - NULL 'pwContents' in InternalCreateChildWindowsSub\n", __FUNCTION__);
2477 
2478  for(i1=0; i1 < nKids; i1++)
2479  {
2480  if(pKids[i1].szTitle)
2481  {
2482  WBFree(pKids[i1].szTitle);
2483  }
2484  if(pKids[i1].szProp)
2485  {
2486  WBFree(pKids[i1].szProp);
2487  }
2488  }
2489 
2490  WBFree(pKids);
2491 
2492  *pszDialogResource = p1; // point of error
2493 
2494  return 0;
2495  }
2496 
2497 //#ifdef HAVE_MALLOC_USABLE_SIZE
2498  iCurContentSize = WBAllocUsableSize(pDlg->pwContents); // re-evaluate actual size
2499 //#endif // HAVE_MALLOC_USABLE_SIZE
2500 
2501  pDlg->nContents = 0; // force it
2502 
2503  // calculate 'nMaxContents' from actual memory block size
2504  pDlg->nMaxContents = iCurContentSize
2505  / sizeof(*(pDlg->pwContents));
2506  }
2507  else if((pDlg->nContents + nKids) > pDlg->nMaxContents) // unlikely but maybe later?
2508  {
2509  int iNewSize = (pDlg->nContents + nKids + 128) * sizeof(*(pDlg->pwContents));
2510  void *p0 = WBReAlloc(pDlg->pwContents, iNewSize);
2511 
2512  if(!p0)
2513  {
2514  WB_ERROR_PRINT("%s - NULL 'pwContents' (re-alloc) in InternalCreateChildWindowsSub\n", __FUNCTION__);
2515  for(i1=0; i1 < nKids; i1++)
2516  {
2517  if(pKids[i1].szTitle)
2518  {
2519  WBFree(pKids[i1].szTitle);
2520  }
2521  if(pKids[i1].szProp)
2522  {
2523  WBFree(pKids[i1].szProp);
2524  }
2525  }
2526 
2527  WBFree(pKids);
2528 
2529  *pszDialogResource = p1; // point of error
2530 
2531  return 0;
2532  }
2533 
2534 //#ifdef HAVE_MALLOC_USABLE_SIZE
2535  iCurContentSize = WBAllocUsableSize(p0); // re-evaluate actual size
2536 //#else // HAVE_MALLOC_USABLE_SIZE
2537 // iCurContentSize = iNewSize;
2538 //#endif // HAVE_MALLOC_USABLE_SIZE
2539 
2540  pDlg->pwContents = (WBDialogEntry *)p0;
2541  // calculate 'nMaxContents' from actual memory block size
2542  pDlg->nMaxContents = iCurContentSize
2543  / sizeof(*(pDlg->pwContents));
2544  }
2545 
2546 // fprintf(stderr, "TEMPORARY: dialog has %d controls\n", nKids);
2547 
2548  // load up the structure but don't create the windows (yet)
2549 
2550  for(i1=0, iRval = 1; i1 < nKids; i1++)
2551  {
2552  WBDialogControl *pCtrl = NULL;
2553  WBDialogEntry *pEntry = pDlg->pwContents + pDlg->nContents + i1;
2554 
2555  pEntry->ulTag = DIALOG_ENTRY_TAG;
2556  pEntry->aClass = pKids[i1].aClass;
2557  pEntry->iID = pKids[i1].iID;
2558  pEntry->iX = pKids[i1].iX;
2559  pEntry->iY = pKids[i1].iY;
2560  pEntry->iWidth = pKids[i1].iWidth;
2561  pEntry->iHeight = pKids[i1].iHeight;
2562 
2563  pEntry->iFlags = pKids[i1].iFlags;
2564  pEntry->wID = -1; // not assigned yet
2565 
2566  // structure entry complete - create the control
2567  pCtrl = WBDialogControlCreate(pEntry->aClass, (WBDialogWindow *)pDlg, pEntry,
2568  pEntry->iX, pEntry->iY, pEntry->iWidth, pEntry->iHeight,
2569  pKids[i1].szTitle, pKids[i1].szProp);
2570 
2571  if(!pCtrl)
2572  {
2573 #ifndef NO_DEBUG
2574  char *p1 = WBGetAtomName(WBGetDefaultDisplay(), pEntry->aClass);
2575 
2576  WB_ERROR_PRINT("%s - Unable to create control %d via WBDialogControlCreate\n", __FUNCTION__, i1);
2577  WB_WARN_PRINT("%s - class: %s title: %s id: %d\n", __FUNCTION__, p1, pKids[i1].szTitle, pKids[i1].iID);
2578 
2579  if(p1)
2580  {
2581  WBFree(p1);
2582  }
2583 #endif // NO_DEBUG
2584 
2585  iRval = 0;
2586  break;
2587  }
2588 
2589  pEntry->wID = pCtrl->wID; // assign it now
2590  }
2591 
2592  pDlg->nContents += nKids;
2593 
2594  // final cleanup
2595 
2596  for(i1=0; i1 < nKids; i1++)
2597  {
2598  if(pKids[i1].szTitle)
2599  {
2600  WBFree(pKids[i1].szTitle);
2601  }
2602  if(pKids[i1].szProp)
2603  {
2604  WBFree(pKids[i1].szProp);
2605  }
2606  }
2607 
2608  WBFree(pKids); // not needed now
2609 
2610  *pszDialogResource = p1;
2611 
2612  return iRval;
2613 }
2614 
void * WBGetPointerFromHash(WB_UINT32 uiHash)
Obtain a pointer from a 32-bit &#39;secure&#39; pointer hash value.
#define IDOK
Atom aLOSTFOCUS
CONTROL_NOTIFY ClientMessage for LOSTFOCUS event.
void WBSetWMProtocols(Window wID, Atom aProperty,...)
re-assign standard WM (Window Manager) &#39;window type&#39; properties and notify the root window (reserved)...
void * DLGGetDialogWindowUserData(Window wID)
Returns a pointer to the dialog window&#39;s &#39;user data&#39; assigned at create time.
&#39;window helper&#39; main header file for the X11workbench Toolkit API
static __inline__ Display * WBGetDefaultDisplay(void)
Returns the default Display.
Atom aGOTFOCUS
CONTROL_NOTIFY ClientMessage for GOTFOCUS event.
Horizontal text alignment bits.
set for "default" controls
int iClientWidth
width of the dialog window&#39;s client area
#define WB_DEBUG_PRINT(L,...)
Preferred method of implementing conditional debug output.
Definition: debug_helper.h:299
Utilities for copying and drawing text, determining text extents, and so on.
WBDialogWindow * DLGCreateDialogWindow(const char *szTitle, const char *szDialogResource, int iX, int iY, int iWidth, int iHeight, WBWinEvent pUserCallback, int iFlags, void *pUserData)
create a dialog window using a text resource
unsigned int ulTag
tag word, always assigned to DIALOG_WINDOW_TAG
BUTTON - Tri-state button.
int WBUnmapWindow(Display *pDisplay, Window wID)
wrapper for XUnmapWindow, makes window invisible without destroying it
int iX
horizontal position of upper left corner of control (client-relative)
BUTTON - Checkbox button.
#define LOAD_COLOR0(X, Y)
macro to load a color, mostly for readability
WBDialogControl * WBDialogControlCreate(Atom aClass, WBDialogWindow *pOwner, WBDialogEntry *pDialogEntry, int iX, int iY, int iWidth, int iHeight, const char *szTitle, const char *szPropertyList)
Create a dialog control window.
XColor clrBD
border pixel color
Atom aLIST_NOTIFY
CONTROL_NOTIFY ClientMessage for LIST_NOTIFY event.
int WBShowModal(Window wID, int bMenuSplashFlag)
Shows a &#39;modal&#39; window by processing events until the window closes.
WBDialogEntry * pwContents
WBAlloc&#39;d array of child window/control information as WBDialogEntry following order of windows creat...
Window DLGGetNextDialogControl(WBDialogWindow *pDialog, Window idControl)
Return the Window ID of the NEXT dialog control within the &#39;tab order&#39;, based on the specified contro...
int nContents
size of &#39;pwContents&#39; when pwContents is not NULL
#define WB_KEYBOARD_INPUT_MASK
&#39;Keyboard&#39; input mask, bit flag for window creation
Auto-center mouse pointer.
user-editable text
Atom aWM_DELETE_WINDOW
Delete Window notification event.
int DLGPixelHeight(WBDialogWindow *pDlg, int iHeight)
Height of a single dialog unit (in pixels)
unsigned long ulFlags
generic flag bits
Atom aWM_PROTOCOLS
WM supported protocols.
int WBPostPriorityEvent(Window wID, XEvent *pEvent)
Places a copy of the specified event at the end of the priority (internal) event queue.
WB_UINT32 WBCreatePointerHash(void *pPointer)
Create/obtain a 32-bit &#39;secure&#39; hash for a pointer.
&#39;configuration helper&#39; main header file for the X11 Work Bench Toolkit API
for edit controls, prevents trapping <ENTER>
Window DLGGetFirstDialogControl(WBDialogWindow *pDialog)
Return the Window ID of the FIRST dialog control within the &#39;tab order&#39;.
Atom aDLG_FOCUS
dialog focus messages
Horizontally align text center.
Horizontally align text right.
void * WBReAlloc(void *pBuf, int nNewSize)
High performance memory sub-allocator &#39;re-allocate&#39;.
Atom aDIALOG_INIT
DIALOG_INIT ClientMessage, sent to dialog window callback on frame create.
WBDialogWindow wbDLG
WBDialogWIndow structure, required to be at the beginning.
prevents trapping <ESC> <ENTER> and anything else
Window wID
window ID of control
BUTTON - Radio button (generic)
#define WB_STANDARD_INPUT_MASK
&#39;Standard&#39; input mask, bit flag for window creation
#define IDNO
Atom aRADIOBUTTON_CONTROL
Standard Dialog Control - radio button - see RADIOBUTTON_CONTROL_STR.
#define IDYES
char * szTitle
title bar string (WBAlloc&#39;d pointer, must WBFree on destroy)
void WBInvalidateGeom(Window wID, const WB_GEOM *pGeom, int bPaintNow)
&#39;Paint&#39; helper, invalidates a geometry for asynchronous Expose event generation
#define DIALOG_WINDOW_TAG
int nMaxContents
extent of &#39;pwContents&#39; when pwContents is not NULL
Application Window flag.
Window wID
Window ID of the dialog control window.
&#39;checked&#39; state
void WBCreateWindowDefaultGC(Window wID, unsigned long clrFG, unsigned long clrBG)
creates a default GC for a window
void WBDeQuoteString(char *pString)
De-Quote a string &#39;in place&#39;, that is modifying the original string by removing quotes.
int WBIsValid(Display *pDisplay, Window wID)
returns non-zero if &#39;valid&#39; (i.e. &#39;not destroyed&#39;)
#define IDABORT
dialog box flag, can be resized vertically
void WBSetApplicationWindow(Window wID)
Destroy a window.
#define DIALOG_ENTRY_TAG
void * WBAlloc(int nSize)
High performance memory sub-allocator &#39;allocate&#39;.
re-alignment with respect to center (as a percentage, stretching allowed)
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)
Window DLGGetDialogControl(WBDialogWindow *pDialog, int iControlID)
returns the Window ID of a member control of the dialog window using the &#39;control ID&#39; ...
struct __DIALOG_WINDOW__ * pNext
internal use only, pointer to next entry in chain (linked list)
int iWidth
width of control
BUTTON - &#39;First&#39; radio button in a group.
int DLGProcessHotKey(WBDialogWindow *pDLG, XEvent *pEvent)
return non-zero if a hot-key was processed and no further processing should be done. Otherwise returns zero.
#define WB_ERROR_PRINT(...)
Preferred method of implementing an &#39;error level&#39; debug message for all subsystems.
Definition: debug_helper.h:356
resize maintains position/size with respect to bottom
void DLGSetUserCallback(WBDialogWindow *pDialogWindow, WBWinEvent pCallBack)
Assign the callback function for a dialog window.
#define IDNONE
Window wID
window ID of the dialog (frame) window
void WBFree(void *pBuf)
High performance memory sub-allocator &#39;free&#39;.
int WBPostEvent(Window wID, XEvent *pEvent)
Places a copy of the specified event at the end of the regular (internal) event queue.
#define IDCANCEL
void WBDestroyWindow(Window wID)
Destroy a window.
XColor clrBG
background pixel color
causes the control to be drawn &#39;disabled&#39; and NOT be selectable for focus
Window wIDOwner
window ID of the dialog&#39;s OWNER (can be &#39;None&#39;, should be assigned for Modal dialogs) ...
int iFlags
bitmask of attribute flags (see &#39;WBDialogWindowFlags&#39; enumeration)
Vertically align text center (default)
void * pUserData
user data that&#39;s assignable via API
int WBAllocUsableSize(void *pBuf)
High performance memory sub-allocator, similar to &#39;malloc_usable_size&#39;.
Atom aEDIT_CONTROL
Standard Dialog Control - editable text (single or multi-line, scrollable, clipboard) - see EDIT_CONT...
int DLGPixelWidth(WBDialogWindow *pDlg, int iWidth)
Width of a single dialog unit (in pixels)
set for &#39;pushbuttons&#39; (which override &#39;default&#39; for <ENTER>)
int WBIsChildWindow(Window wIDParent, Window wIDChild)
Returns non-zero if wIDParent is in a parent relationsihp with wIDChild.
Atom WBGetAtom(Display *pDisplay, const char *szAtomName)
Lookup and/or allocate an internal Atom for a named string (lookups include X11 atoms) ...
XColor clrFG
foreground pixel color
void DLGDestroyDialogWindow(Window wID)
Destroy a modeless dialog window via its window ID.
#define LOAD_COLOR(X, Y, Z)
macro to load a color with a fallback, mostly for readability
Atom aCANCELBUTTON_CONTROL
Standard Dialog Control - cancel pushbutton control (accepts <ESC> as hotkey) - see CANCELBUTTON_CONT...
int(* WBWinEvent)(Window wID, XEvent *pEvent)
event callback function type for window events
Atom aWM_TAKE_FOCUS
&#39;Take Focus&#39; - TODO document this properly
Structure identifying the properties of a dialog box control.
Atom aCHECKBUTTON_CONTROL
Standard Dialog Control - check[box] button - push-on/push-off with &#39;check mark&#39; (or whatever) - see ...
void DLGAssignOwner(WBDialogWindow *pDlg, Window wIDOwner)
Assign owning window to dialog.
Structure identifying one of the controls that appears on a dialog window.
WBDialogWindow * pOwner
pointer to owner dialog box
const char * WBDialogControlGetCaption(WBDialogControl *pCtrl)
Obtain a pointer to the assigned text for the &#39;CAPTION&#39; property of a dialog control.
void DLGDestroyDialogWindow2(WBDialogWindow *pDialogWindow)
Destroy a modeless dialog window via a pointer to its WBDialogWindow structure.
resize maintains position/size with respect to top
int iY
vertical position of upper left corner of control (client-relative)
#define WB_LIST_DBLCLICK
Display * WBGetWindowDisplay(Window wID)
returns the Display associated with a window
Atom aCONTROL_NOTIFY
dialog control and child window notification messages
static __inline__ WBDialogWindow * DLGGetDialogWindowStruct(Window wID)
Returns a pointer to the dialog window&#39;s WBDialogWindow structure.
set when item gets focus. cleared when no focus
WBWinEvent pDLGCallback
pointer to dialog window callback function
void WBSetWindowData(Window wID, int iIndex, void *pData)
assign &#39;data pointer&#39; for a window and specified index value
resize maintains position/size with respect to right
int iID
control unique identifier (number or atom, as appropriate, unique to dialog window) ...
Atom aFIRSTRADIOBUTTON_CONTROL
Standard Dialog Control - &#39;first&#39; radio button - defines start of radio button &#39;group&#39; - see FIRSTRAD...
&#39;no border&#39; flag (don&#39;t display a border - statics, typically)
#define WB_MIN_STD_CTRL_ID
#define BEGIN_XCALL_DEBUG_WRAPPER
wrapper macro for calls into the X11 library. This macro precedes the call(s)
re-alignment with respect to center (as a percentage, stretching allowed)
vertical alignment mask
void WBDestroyPointerHash(WB_UINT32 uiHash)
Destroy a 32-bit &#39;secure&#39; hash for a pointer.
Horizontally align text left (default)
void WBSetInputFocus(Window wID)
set input focus to a specific window
Window DLGGetPrevDialogControl(WBDialogWindow *pDialog, Window idControl)
Return the Window ID of the PREVIOUS dialog control within the &#39;tab order&#39;, based on the specified co...
Structure identifying a dialog (frame) window.
Window WBCreateWindow(Display *pDisplay, Window wIDParent, WBWinEvent pProc, const char *szClass, int iX, int iY, int iWidth, int iHeight, int iBorder, int iIO, int iFlags, XSetWindowAttributes *pXSWA)
Create a window.
char * WBGetAtomName(Display *pDisplay, Atom aAtom)
Lookup and/or allocate an internal Atom for a named string.
Atom aBUTTON_PRESS
CONTROL_NOTIFY ClientMessage for BUTTON_PRESS event.
WBDialogEntry * DLGGetDialogControlEntry(WBDialogWindow *pDialog, Window idControl)
returns a pointer to the WBDialogEntry for a member control using the control&#39;s Window ID ...
Vertically align text top.
void WBDebugPrint(const char *pFmt,...) __attribute__((format(printf
conditional debug message output
resize maintains position/size with respect to left
Visibility flag.
DIALOG_WINDOW structure (internal), an extension of WBDialogWindow.
Atom aTRISTATEBUTTON_CONTROL
Standard Dialog Control - tristate - like check, but with a third state - see TRISTATEBUTTON_CONTROL_...
void WBEndModal(Window wID, int iRval)
End a modal window with a specific return value.
void WBSetWMProperties(Window wID, const char *szTitle, XSizeHints *pNormalHints, XWMHints *pWMHints, XClassHint *pClassHints)
assign standard WM (Window Manager) properties via XSetWMProperties
int iClientHeight
height of the dialog window&#39;s client area
BUTTON - &#39;type mask&#39; for buttons.
set when item CAN have focus.
void DLGRecalcLayout(Window wID)
Force re-calculation of dialog window (and control) layout.
#define IDRETRY
horizontal alignment mask
#define IDIGNORE
Vertical text alignment bits.
#define WB_IF_DEBUG_LEVEL(L)
Preferred method of implementing conditional debug &#39;if block&#39; code.
Definition: debug_helper.h:339
struct __DIALOG_WINDOW__ DIALOG_WINDOW
DIALOG_WINDOW structure (internal), an extension of WBDialogWindow.
void WBDialogControlSetCheck(WBDialogControl *pCtrl, int iCheck)
Assign the TEXT property for a control, which is different from the CAPTION or Title.
dialog box flag, can be resized horizontally
Vertically align text bottom.
int WBMapWindow(Display *pDisplay, Window wID)
Wrapper for XMapWindow, makes window visible.
void WBDialogControlsInit(void)
Initialization function for dialog controls.
int iHeight
height of control
unsigned int ulTag
tag word, always assigned to DIALOG_ENTRY_TAG
#define WB_WARN_PRINT(...)
Preferred method of implementing a &#39;warning level&#39; debug message for all subsystems.
Definition: debug_helper.h:349
static __inline__ WBDialogControl * DLGGetDialogControlStruct(Window wID)
Returns a pointer to the WBDialogControl structure for a dialog control with validation. May return NULL.
Atom aClass
control class identifier
void WBPostDelayedSetFocusAppEvent(Display *pDisplay, Window wID, Window wIDFrom, unsigned int iDelay)
Creates a &#39;set focus&#39; ClientMessage event for the application event handler.
int iFlags
visibility, focus, etc.
#define IDSTATIC