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