X11 Work Bench Toolkit  1.0
dialog_window.c
Go to the documentation of this file.
1 
2 // _ _ _ _ _ //
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-2016 by Bob Frazier (aka 'Big Bad Bombastic Bob')
17  all rights reserved
18 
19  DISCLAIMER: The X11workbench application and toolkit software are supplied
20  'as-is', with no warranties, either implied or explicit.
21  Any claims to alleged functionality or features should be
22  considered 'preliminary', and might not function as advertised.
23 
24  BSD-like license:
25 
26  There is no restriction as to what you can do with this software, so long
27  as you include the above copyright notice and DISCLAIMER for any distributed
28  work that is equal to or derived from this one, along with this paragraph
29  that explains the terms of the license if the source is also being made
30  available. A "derived work" describes a work that uses a significant portion
31  of the source files or algorithms that are included with this one.
32  Specifically excluded from this are files that were generated by the software,
33  or anything that is included with the software that is part of another package
34  (such as files that were created or added during the 'configure' process).
35  Specifically included is the use of part or all of any of the X11 workbench
36  toolkit source or header files in your distributed application. If you do not
37  ship the source, the above copyright statement is still required to be placed
38  in a reasonably prominent place, such as documentation, splash screens, and/or
39  'about the application' dialog boxes.
40 
41  Use and distribution are in accordance with GPL, LGPL, and/or the above
42  BSD-like license. See COPYING and README files for more information.
43 
44 
45  Additional information at http://sourceforge.net/projects/X11workbench
46 
47 ******************************************************************************/
48 
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <unistd.h>
56 #include <memory.h>
57 #include <string.h>
58 #include <strings.h>
59 #include <signal.h>
60 #include <time.h>
61 
62 #ifndef XK_Delete /* moslty for interix */
63 #define XK_MISCELLANY /* mostly for interix */
64 #include <X11/keysymdef.h> // some platforms don't automatically include this with X headers
65 #endif // XK_Delete
66 
67 #include "window_helper.h"
68 #include "pixmap_helper.h" // pixmap helpers, including pre-defined icons
69 #include "dialog_window.h"
70 #include "dialog_controls.h"
71 #include "conf_help.h"
72 #include "draw_text.h"
73 
74 
75 #define THIS_SUBSYSTEM DebugSubSystem_Dialog
76 
77 
113 typedef struct __DIALOG_WINDOW__
114 {
116 
118 
119  int nContents;
121  char *szTitle;
122 
124 
126 
127 } DIALOG_WINDOW;
128 
129 
130 static int InternalCreateChildWindows(DIALOG_WINDOW *pDlg, const char *szDialogResource);
131 
132 int DLGDefaultCallback(Window wID, XEvent *pEvent); // default callback for dialog windows (call for default processing)
133 
134 static DIALOG_WINDOW *pDialogs = NULL; // pointer to linked list of dialog windows (WBAlloc'd)
135 
136 static XColor clrFG, clrBG, clrBD;
137 static int iInitColorFlag = 0;
138 
139 static void __internal_destroy_dialog_window(DIALOG_WINDOW *pTemp)
140 {
141  int i1;
142  if(pTemp->pwContents)
143  {
145  int nContents = pTemp->nContents;
146  int nMaxContents = pTemp->nMaxContents;
147 
148  pTemp->pwContents = NULL;
149  pTemp->nContents = 0;
150  pTemp->nMaxContents = 0;
151 
152  for(i1=0; i1 < nContents && i1 < nMaxContents; i1++)
153  {
155  "%s - Destroy contents %d\n",
156  __FUNCTION__, i1 + 1);
157 
158  if((int)(pwContents[i1].wID) > 0)
159  WBDestroyWindow(pwContents[i1].wID);
160 
161  // TODO: do I need to release/free 'aClass' or shall I organize this elsewhere?
162  }
163 
164  WBFree(pwContents);
165  }
166 
167  if(pTemp->szTitle)
168  {
169  WBFree(pTemp->szTitle);
170  }
171 
172  pTemp->szTitle = NULL;
173 }
174 
175 
176 int DLGPixelHeight(WBDialogWindow *pDlg, int iHeight)
177 {
178  return iHeight * 2; // for now
179 }
180 
181 int DLGPixelWidth(WBDialogWindow *pDlg, int iWidth)
182 {
183  return iWidth * 2; // for now
184 }
185 
186 void WBDialogWindowExit()
187 {
188  // destroy all of the dialog windows
189 
190  while(pDialogs)
191  {
192  DIALOG_WINDOW *pTemp = pDialogs;
193  Window wID = pTemp->wbDLG.wID;
194 
195  pDialogs = pDialogs->pNext;
196 
197  WB_ERROR_PRINT("WARNING: %s - dialog window %d not previously destroyed\n", __FUNCTION__, (int)(pTemp->wbDLG.wID));
198 
199  // slightly different - free the structure first, THEN destroy the window
200  WBSetWindowData(pTemp->wbDLG.wID, 0, NULL);
201  __internal_destroy_dialog_window(pTemp);
202  WBFree(pTemp);
203 
204  if(wID > 0)
205  {
206  WBDestroyWindow(wID); // making sure...
207  }
208  }
209 
210 }
211 
212 #define LOAD_COLOR0(X,Y) if(CHGetResourceString(WBGetDefaultDisplay(), X, Y, sizeof(Y)) > 0) { }
213 #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); }
214 
215 static void InternalCheckColors(void)
216 {
217  Colormap colormap;
218 
219  // *Dialog.background, *Dialog.foreground, *WmDialog.background, *WmDialog.foreground,
220  // *Form.background, *Form.foreground, *background, *foreground
221 
222  if(!iInitColorFlag)
223  {
224  char szFG[16], szBG[16], szBD[16];
225  colormap = DefaultColormap(WBGetDefaultDisplay(), DefaultScreen(WBGetDefaultDisplay()));
226 
227  LOAD_COLOR0("*Dialog.foreground",szFG) else LOAD_COLOR0("*Form.foreground", szFG)
228  else LOAD_COLOR0("*WmDialogShell.foreground",szFG) else LOAD_COLOR0("*WmForm.foreground", szFG)
229  else LOAD_COLOR("*foreground", szFG, "#000000");
230  LOAD_COLOR0("*Dialog.background",szBG) else LOAD_COLOR0("*Form.background", szBG)
231  else LOAD_COLOR0("*WmDialogShell.background",szBG) else LOAD_COLOR0("*WmForm.background", szBG)
232  else LOAD_COLOR("*background", szBG, "#dcdad5"); // default for gnome
233  LOAD_COLOR0("*Dialog.border",szBD) else LOAD_COLOR0("*Form.border", szBD)
234  else LOAD_COLOR0("*WmDialogShell.border",szBD) else LOAD_COLOR0("*WmForm.border", szBD)
235  else LOAD_COLOR0("*borderColor", szBD)
236  else LOAD_COLOR("*border", szBD, "black"); // default for gnome
237 
238  XParseColor(WBGetDefaultDisplay(), colormap, szFG, &clrFG);
239  XAllocColor(WBGetDefaultDisplay(), colormap, &clrFG);
240  XParseColor(WBGetDefaultDisplay(), colormap, szBG, &clrBG);
241  XAllocColor(WBGetDefaultDisplay(), colormap, &clrBG);
242  XParseColor(WBGetDefaultDisplay(), colormap, szBD, &clrBD);
243  XAllocColor(WBGetDefaultDisplay(), colormap, &clrBD);
244 
245  iInitColorFlag = 1;
246  }
247 }
248 
249 WBDialogWindow *DLGCreateDialogWindow(const char *szTitle, const char *szDialogResource,
250  int iX, int iY, int iWidth, int iHeight,
251  WBWinEvent pUserCallback, int iFlags, void *pUserData)
252 {
253  DIALOG_WINDOW *pNew = WBAlloc(sizeof(DIALOG_WINDOW));
254  Display *pDisplay = WBGetDefaultDisplay();
255  XSetWindowAttributes xswa; /* Temporary Set Window Attribute struct */
256  XSizeHints xsh; /* Size hints for window manager */
257  XWMHints xwmh;
258 // Atom aProto[3];
259  XClientMessageEvent evt;
260  int i1;
261 //#ifndef NO_DEBUG
262 // WB_UINT64 ullTime = WBGetTimeIndex();
263 //#endif // NO_DEBUG
264 
265 
266 // WB_ERROR_PRINT("TEMPORARY: %s line %d delta tick %lld\n", __FUNCTION__, __LINE__, (WBGetTimeIndex() - ullTime));
267 
268  if(!pNew)
269  {
270  WB_ERROR_PRINT("%s - not enough memory to create dialog window (1)\n", __FUNCTION__);
271  return NULL;
272  }
273 
274  WBDialogControlsInit(); // make sure I initialize all of the dialog controls stuff
275 
276  InternalCheckColors(); // must do this first
277 
278  bzero(pNew, sizeof(*pNew));
279  pNew->wbDLG.ulTag = DIALOG_WINDOW_TAG;
280  pNew->wbDLG.wID = -1; // initial (bad) value
281  pNew->wbDLG.wIDOwner = None; // initially, no owner
282  pNew->wbDLG.iFlags = iFlags; // save this while I'm at it
283  pNew->wbDLG.pUserData = pUserData; // save this too
284 
285  if(szTitle)
286  {
287  pNew->szTitle = WBAlloc(strlen(szTitle) + 1);
288  if(!pNew->szTitle)
289  {
290  WBFree(pNew);
291 
292  WB_ERROR_PRINT("%s - not enough memory to create dialog window (2)\n", __FUNCTION__);
293  return NULL;
294  }
295  strcpy(pNew->szTitle, szTitle);
296  }
297 
298  // add struct to beginning of linked list 'cause it's faster that way
299 
300  pNew->pNext = pDialogs;
301  pDialogs = pNew;
302 
303  // NOW I get to create the actual window with its GC and callback proc
304 
305  // copy standard colors into structure at this time [I will reference 'pNew->wbDLG' values from this point]
306  memcpy(&(pNew->wbDLG.clrFG), &clrFG, sizeof(clrFG));
307  memcpy(&(pNew->wbDLG.clrBG), &clrBG, sizeof(clrBG));
308  memcpy(&(pNew->wbDLG.clrBD), &clrBD, sizeof(clrBD));
309 
310  // setting up the standard attributes
311  bzero(&xswa, sizeof(xswa));
312 
313  xswa.border_pixel = pNew->wbDLG.clrBD.pixel;
314  xswa.background_pixel = pNew->wbDLG.clrBG.pixel;
315  xswa.colormap = DefaultColormap(pDisplay, DefaultScreen(pDisplay));
316  xswa.bit_gravity = CenterGravity;
317 
319  "%s - creating dialog window at %d %d %d %d\n",
320  __FUNCTION__, iX, iY, iWidth, iHeight);
321 
322  pNew->wbDLG.wID = WBCreateWindow(pDisplay, None, DLGDefaultCallback, "DialogWindow",
323  iX, iY, iWidth, iHeight, 1, InputOutput,
324  CWBorderPixel | CWBackPixel | CWColormap | CWBitGravity,
325  &xswa);
326  if(pNew->wbDLG.wID <= 0)
327  {
329 
330  WB_ERROR_PRINT("DLGCreateDialogWindow - XCreateWindow fail\n");
331  return NULL;
332  }
333 
334  // immediately identify this window using window data
335  WBSetWindowData(pNew->wbDLG.wID, 0, (void *)pNew);
336 
337  // register user callback function
338  pNew->pDLGCallback = pUserCallback;
339 
340  // before I do anything else, create the child windows (this will set up the correct dialog size)
341 
342  if(!InternalCreateChildWindows(pNew, szDialogResource))
343  {
344  // since the window wasn't exposed yet, I have to post the destroy
345  // rather than destroy it directly...
346 
347  WB_WARN_PRINT("%s - InternalCreateChildWindows failed, destroying dialog box\n", __FUNCTION__);
348 
349  DLGDestroyDialogWindow2((WBDialogWindow *)pNew); // don't display the dialog box
350  return NULL;
351  }
352 
353  // NOW, set up the window properties, including sizeability and the correct min/max/current dimensions
354 
355  bzero(&xsh, sizeof(xsh));
356 
357  xsh.flags = (USPosition | USSize | PWinGravity);
358  xsh.x = iX;
359  xsh.y = iY;
360  xsh.win_gravity = NorthWestGravity; // StaticGravity
361 
362  if(pNew->wbDLG.iClientWidth > 0 && pNew->wbDLG.iClientHeight > 0) // for now only allow this
363  {
365  "%s setting width/height, iClientWidth=%d iClientHeight=%d\n",
366  __FUNCTION__, pNew->wbDLG.iClientWidth, pNew->wbDLG.iClientHeight);
367 
368  xsh.width = DLGPixelWidth((WBDialogWindow *)pNew, pNew->wbDLG.iClientWidth) + 2; /* 2 is twice border width */
369  xsh.height = DLGPixelHeight((WBDialogWindow *)pNew, pNew->wbDLG.iClientHeight) + 2;
370  }
371  else // dialog is NOT resizeable
372  {
373  xsh.width = xsh.base_width = iWidth;
374  xsh.height = xsh.base_height = iHeight;
375  }
376 
378  "%s calling XMoveResizeWindow, iX=%d iY=%d iWidth=%d iHeight=%d xsh.width=%d xsh.height=%d\n",
379  __FUNCTION__, iX, iY, iWidth, iHeight, xsh.width, xsh.height);
380 
381  XMoveResizeWindow(pDisplay, pNew->wbDLG.wID, iX, iY, xsh.width, xsh.height); // do this FIRST before doing 'hints'
382 
383  if(1) // dialog cannot be re-sized
384  {
385  xsh.flags |= (PMinSize | PMaxSize);
386 
387  xsh.min_width = xsh.max_width = xsh.width;
388  xsh.min_height = xsh.max_height = xsh.height;
389 
390  xsh.flags |= PBaseSize;
391  xsh.base_width = xsh.min_width; // 'base width' is like "where you start from" I think... ( == min_width if not specified)
392  xsh.base_height = xsh.min_height; // 'base height' is like "where you start from" I think... ( == min_height if not specified)
393  }
394  else
395  {
396  // TODO: resizable dialog box
397  }
398 
399  bzero(&xwmh, sizeof(xwmh));
400  xwmh.flags = InputHint;
401  xwmh.input = !0; // this represents 'Locally Active'
402 
403 // from http://mail.gnome.org/archives/wm-spec-list/2007-March/msg00000.html
404 
405 // The ICCCM provides for focus models for a window:
406 //
407 // Input Model Input Field WM_TAKE_FOCUS
408 // No Input False Absent
409 // Passive True Absent
410 // Locally Active True Present
411 // Globally Active False Present
412 //
413 // No Input - The application never expects to receive focus.
414 //
415 // Passive - The application will get focus but it will not grab it on
416 // its own. Instead it will only get focus if the window manager gives it
417 // to it. The application cannot help determine if it wants the focus or
418 // not at the given time/situation.
419 //
420 // Locally Active - The same as passive, but the application will also
421 // give focus to other windows that it owns of its own free will, without
422 // any interaction with the window manager, using XSetInputFocus.
423 //
424 // Globally Active - The application will not receive focus from the
425 // window manager. Instead it will determine when it wants focus (from
426 // WM_TAKE_FOCUS suggestions given by the window manager, or from events
427 // such as ButtonPress or KeyPress). The application will then acquire
428 // focus on its own using XSetInputFocus.
429 
430  // set title, size hints, and 'WM_HINTS' hints (so WM knows where to put the window and how to set focus)
431  WBSetWMProperties(pNew->wbDLG.wID, szTitle, &xsh, &xwmh, NULL);
432 
433 #if 0
434  aProto[0] = aWM_DELETE_WINDOW; // for now, that's the only one
435  aProto[1] = aWM_TAKE_FOCUS; // GDK does this, see set_wm_protocols() in gdkwindow-x11.c
436 // aProto[2] = a_NET_WM_PING; // GDK does this, see set_wm_protocols() in gdkwindow-x11.c
437 // aProto[3] = a_NET_WM_SYNC_REQUEST; // GDK does this when HAVE_XSYNC is enabled
438 
439  aProto[sizeof(aProto)/sizeof(aProto[0]) - 1] = None; // have an extra one at the end
440 
441  XSetWMProtocols(pDisplay, pNew->wbDLG.wID, aProto, sizeof(aProto)/sizeof(aProto[0]) - 1);
442 #endif // 0
443 
445 
446  WBCreateWindowDefaultGC(pNew->wbDLG.wID, pNew->wbDLG.clrFG.pixel, pNew->wbDLG.clrBG.pixel);
447 
448  // now allow certain kinds of input messages (I should be able to handle messages at this point)
449  XSelectInput(pDisplay, pNew->wbDLG.wID, WB_STANDARD_INPUT_MASK | WB_KEYBOARD_INPUT_MASK);
450 
451  // now that the controls have been created, assign the title, and send
452  // an event to the user callback to initialize the dialog box contents
453 
454 // WBSetWindowIcon(pNew->wbDLG.wID, idIcon); TODO: get icon info from resource??
455 
456  if(pUserCallback)
457  {
458  bzero(&evt, sizeof(evt));
459  evt.type = ClientMessage;
460  evt.display = pDisplay;
461  evt.window = pNew->wbDLG.wID;
462  evt.message_type = aDIALOG_INIT;
463  evt.format = 32;
464 
465  if(!pUserCallback(pNew->wbDLG.wID, (XEvent *)&evt)) // callback returned zero?
466  {
467  DLGDestroyDialogWindow2((WBDialogWindow *)pNew); // don't display the dialog box
468 
469  WB_ERROR_PRINT("DLGCreateDialogWindow - callback returns zero on init\n");
470  return NULL;
471  }
472  }
473 
474  // Last but not least, set the focus for the control that's
475  // first in my list that has the 'CAN_HAVE_FOCUS' bit set.
476 
477  for(i1=0; i1 < pNew->nContents; i1++)
478  {
479  if(pNew->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS) // TODO: check visibility?
480  {
482 
483  break;
484  }
485  }
486 
487  DLGRecalcLayout(pNew->wbDLG.wID); // prior to making me visible do this
488 
489  if(iFlags & WBDialogWindow_APP_WINDOW)
490  {
492  }
493 
494  if((iFlags & WBDialogWindow_VISIBLE) ||
495  (iFlags & WBDialogWindow_DOMODAL)) // 'DOMODAL' implies visible
496  {
497  WBMapWindow(pDisplay, pNew->wbDLG.wID); // make window visible
498 
499  // 'warp' the mouse pointer to the center of the dialog box
500 
501  if(iFlags & (WBDialogWindow_DOMODAL | WBDialogWindow_CENTERMOUSE))
502  {
504  XWarpPointer(pDisplay, None, pNew->wbDLG.wID, 0, 0, 0, 0, xsh.width / 2, xsh.height / 2);
506  }
507  }
508 
509  if(iFlags & WBDialogWindow_DOMODAL)
510  {
511  WBShowModal(pNew->wbDLG.wID, 0); // ignore return value
512  WBGetParentWindow(pNew->wbDLG.wID); // this syncs everything up
513  }
514 
515  return (WBDialogWindow *)pNew;
516 }
517 
518 void DLGAssignOwner(WBDialogWindow *pDlg, Window wIDOwner)
519 {
520  if(!pDlg || pDlg->ulTag != DIALOG_WINDOW_TAG)
521  {
522  return;
523  }
524 
525  pDlg->wIDOwner = wIDOwner;
526 }
527 
528 void DLGDestroyDialogWindow(Window wID)
529 {
531 }
532 
534 {
535  // step 1: unhook pDialogWindow
536  DIALOG_WINDOW *pTemp = pDialogs;
537  DIALOG_WINDOW *pPrev = NULL;
538  Window wID; //, wIDOwner;
539 // int i1;
540 
541  if(!pDialogWindow || pDialogWindow->ulTag != DIALOG_WINDOW_TAG)
542  {
543  return;
544  }
545 
546  wID = pDialogWindow->wID;
547 // wIDOwner = pDialogWindow->wIDOwner;
548 
550  "%s - Destroying dialog window %d (%08xH)\n",
551  __FUNCTION__, (int)wID, (int)wID);
552 
553  while(pTemp && pTemp != (DIALOG_WINDOW *)pDialogWindow)
554  {
555  pPrev = pTemp;
556  pTemp = pTemp->pNext;
557  }
558 
559  if(pTemp)
560  {
561  if(pPrev)
562  pPrev->pNext = pTemp->pNext; // unhook
563  else if(pDialogs == pTemp)
564  pDialogs = pTemp->pNext;
565  }
566 
567 
568  if(wID > 0)
569  {
570  WBDestroyWindow(wID); // this will free the structure
571  }
572  else
573  {
574  // I must destroy WBAlloc'd entries and contained windows in lieu of 'DLGDefaultCallback'
575  __internal_destroy_dialog_window((DIALOG_WINDOW *)pDialogWindow);
576  WBFree(pDialogWindow);
577  }
578 
579 }
580 
581 void DLGSetUserCallback(WBDialogWindow *pDialogWindow, WBWinEvent pCallBack)
582 {
583  if(pDialogWindow->ulTag != DIALOG_WINDOW_TAG)
584  return;
585 
586  ((DIALOG_WINDOW *)pDialogWindow)->pDLGCallback = pCallBack;
587 }
588 
589 int DLGDefaultCallback(Window wID, XEvent *pEvent)
590 {
591  Display *pDisplay = WBGetWindowDisplay(wID);
592  DIALOG_WINDOW *pDialogWindow = (DIALOG_WINDOW *)DLGGetDialogWindowStruct(wID);
593  int i1, i2, iRval;
594 // Window wIDMenu;
595 
596  if(!pDialogWindow || pDialogWindow->wbDLG.ulTag != DIALOG_WINDOW_TAG)
597  {
598  WB_WARN_PRINT("%s - invalid 'pDialogWindow' for window %d (%08xH)\n",
599  __FUNCTION__, (unsigned int)wID, (unsigned int)wID);
600  return 0;
601  }
602 
603  // TODO: message re-direction to children BEFORE 'pDLGCallback'
604 
605 // if(pEvent->type == DestroyNotify)
606 // {
607 // WB_ERROR_PRINT("!!!TEMPORARY: %s wID=%d got DestroyNotify for %d\n",
608 // __FUNCTION__, (int)wID, (int)pEvent->xdestroywindow.window);
609 // }
610 
611  switch(pEvent->type)
612  {
613  case ButtonPress:
615  "%s - BUTTON PRESS - dialog window\n", __FUNCTION__);
616 
617  break;
618 
619  case ButtonRelease:
621  "%s - BUTTON RELEASE - dialog window\n", __FUNCTION__);
622 
623  break;
624 
625  case MotionNotify:
627  "%s - MOTION NOTIFY - dialog window\n", __FUNCTION__);
628 
629  break;
630 
631  case ClientMessage: // menus, etc.
633  "%s - CLIENT MESSAGE - dialog window\n", __FUNCTION__);
634 
635  if(pEvent->xclient.message_type == aWM_PROTOCOLS && pEvent->xclient.window == wID)
636  {
637  if(pEvent->xclient.data.l[0] == aWM_DELETE_WINDOW)
638  {
640  "%s - WM_DELETE_WINDOW (calling WBDestroyWindow(%d))\n", __FUNCTION__, (int)wID);
641  WBDestroyWindow(wID); // should be safe to call this under any circumstances
642 
643 #if 0
644  Window wIDOwner = pDialogWindow->wbDLG.wIDOwner;
645 
646  WBUnmapWindow(pDisplay, wID);
647  WBSetWindowData(wID, 0, NULL); // divorce 'pDialogWindow' from the window
648 
649  __internal_destroy_dialog_window(pDialogWindow);
650 
651  WBFree(pDialogWindow); // 'pDialogWindow' will never be referenced again
652 
653  // note - this MIGHT recurse
654  WBDestroyWindow(wID); // should be safe to call this under any circumstances
655 // // NOTE: if I'm already "being destroyed" I don't want to call this
656 // BEGIN_XCALL_DEBUG_WRAPPER
657 // XDestroyWindow(pDisplay, wID);
658 // END_XCALL_DEBUG_WRAPPER
659 
660  // if the window had an owner, raise it and assign focus to it
661 
662  if(wIDOwner != None)
663  {
664  WBPostDelayedSetFocusAppEvent(WBGetWindowDisplay(wIDOwner), wIDOwner, wID, 100); // 100 msec delay
665  }
666 #endif // 0
667  return 1;
668  }
669  }
670  else if(pEvent->xclient.message_type == aCONTROL_NOTIFY) // 'control notification' messages
671  {
672  // for control notifications, FIRST give the user callback a chance to handle it
673  // and if nothing is done (zero return), I do my own processing here
674 
675  if(pDialogWindow->pDLGCallback)
676  {
677  // if I handle it here, I don't do further processing
678 
679  iRval = pDialogWindow->pDLGCallback(wID, pEvent);
680 
681  if(iRval)
682  {
683  return iRval;
684  }
685  }
686 
687  // check for OK/Cancel/Yes/No/Retry/Ignore/Abort button press and exit modal for any of them
688 
689  if(pEvent->xclient.data.l[0] == (long)aBUTTON_PRESS &&
690  (pEvent->xclient.data.l[1] == IDOK || pEvent->xclient.data.l[1] == IDCANCEL ||
691  pEvent->xclient.data.l[1] == IDYES || pEvent->xclient.data.l[1] == IDNO ||
692  pEvent->xclient.data.l[1] == IDRETRY || pEvent->xclient.data.l[1] == IDABORT ||
693  pEvent->xclient.data.l[1] == IDIGNORE))
694  {
695  WBEndModal(wID, pEvent->xclient.data.l[1]);
696 
697  return 1; // handled
698  }
699  else if(pEvent->xclient.data.l[0] == (long)aLIST_NOTIFY)
700  {
701  if(pEvent->xclient.data.l[2] == WB_LIST_DBLCLICK)
702  {
703  // default behavior on list double click is to close the dialog with 'IDOK'
704  WBEndModal(wID, IDOK);
705 
706  return 1; // handled
707  }
708 
709 #ifndef NO_DEBUG
710  {
711  char *p1 = WBGetAtomName(pDisplay, (Atom)pEvent->xclient.data.l[0]);
712  WB_WARN_PRINT("%s - TODO: LIST_NOTIFY control notification message %ld (%s) %ld (%08lxH), %ld (%08lxH)\n",
713  __FUNCTION__, pEvent->xclient.data.l[0], p1,
714  pEvent->xclient.data.l[1], pEvent->xclient.data.l[1],
715  pEvent->xclient.data.l[2], pEvent->xclient.data.l[2]);
716  if(p1)
717  {
718  WBFree(p1);
719  }
720  }
721 #endif // NO_DEBUG
722 
723  return 0; // NOT handled (default response for unhandled stuff
724  }
725 
726 #ifndef NO_DEBUG
727  {
728  char *p1 = WBGetAtomName(pDisplay, (Atom)pEvent->xclient.data.l[0]);
729  WB_WARN_PRINT("%s - TODO: control notification messages %ld (%s) %ld (%08lxH), %ld (%08lxH)\n",
730  __FUNCTION__, pEvent->xclient.data.l[0], p1,
731  pEvent->xclient.data.l[1], pEvent->xclient.data.l[1],
732  pEvent->xclient.data.l[2], pEvent->xclient.data.l[2]);
733  if(p1)
734  {
735  WBFree(p1);
736  }
737  }
738 #endif // NO_DEBUG
739  }
740  else if(pEvent->xclient.message_type == aGOTFOCUS) // 'focus change' messages
741  {
742  WBDialogEntry *pEntry = (WBDialogEntry *)(((XClientMessageEvent *)pEvent)->data.l[1]);
743 
744  if(pEntry < pDialogWindow->pwContents ||
745  pEntry >= pDialogWindow->pwContents + pDialogWindow->nContents ||
746  (((unsigned long)pEntry - (unsigned long)pDialogWindow->pwContents) % sizeof(*pEntry)) != 0)
747  {
748  // validity check - if it fails, it's not a valid pointer and I reject it
749 
750  WB_WARN_PRINT("%s - WARNING: invalid dialog entry pointer in GOTFOCUS message (ignored)\n",
751  __FUNCTION__);
752  }
753  else if(pEntry->iFlags & WBDialogEntry_HAS_FOCUS)
754  {
756  "%s - INFO: control already has focus in GOTFOCUS message\n",
757  __FUNCTION__);
758  }
759  else if(pEntry->iFlags & WBDialogEntry_CAN_HAVE_FOCUS)
760  {
761  for(i1=0; i1 < pDialogWindow->nContents; i1++)
762  {
763  if(pEntry == pDialogWindow->pwContents + i1)
764  {
765  continue;
766  }
767  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_HAS_FOCUS)
768  {
769  pDialogWindow->pwContents[i1].iFlags &= ~WBDialogEntry_HAS_FOCUS;
770  }
771  }
772 
773  pEntry->iFlags |= WBDialogEntry_HAS_FOCUS;
774  }
775  else // items that can't actually get the focus - need to handle it properly
776  {
777  int bFound = 0;
778  // set focus to NEXT control that can have focus (if it does not already have focus)
779 
780  i2 = (pEntry - pDialogWindow->pwContents);
781 
782  for(i1 = i2 + 1; i1 < pDialogWindow->nContents; i1++)
783  {
784  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS)
785  {
786  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_HAS_FOCUS)
787  {
788  // already has focus - nothing left to do
789  bFound = -1; // to indicate 'do nothing'
790  break;
791  }
792 
793  // this is my new focus item - unset focus for everything else
794 
795  for(i2=0; i2 < pDialogWindow->nContents; i2++)
796  {
797  if(i2 == i1)
798  {
799  continue;
800  }
801 
802  if(pDialogWindow->pwContents[i2].iFlags & WBDialogEntry_HAS_FOCUS)
803  {
804  pDialogWindow->pwContents[i2].iFlags &= ~WBDialogEntry_HAS_FOCUS;
805 
806  WBInvalidateGeom(pDialogWindow->pwContents[i2].wID, NULL, 1); // force re-paint
807  }
808  }
809 
810  bFound = 1;
811 
812  break;
813  }
814  }
815 
816  // wrap around
817  if(!bFound)
818  {
819  for(i1 = 0; i1 < i2; i1++)
820  {
821  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS)
822  {
823  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_HAS_FOCUS)
824  {
825  // already has focus - nothing left to do
826  bFound = -1; // to indicate 'do nothing'
827  break;
828  }
829 
830  // this is my new focus item - unset focus for everything else
831 
832  for(i2=0; i2 < pDialogWindow->nContents; i2++)
833  {
834  if(i2 == i1)
835  {
836  continue;
837  }
838 
839  if(pDialogWindow->pwContents[i2].iFlags & WBDialogEntry_HAS_FOCUS)
840  {
841  pDialogWindow->pwContents[i2].iFlags &= ~WBDialogEntry_HAS_FOCUS;
842 
843  WBInvalidateGeom(pDialogWindow->pwContents[i2].wID, NULL, 1); // force re-paint
844  }
845  }
846 
847  bFound = 1;
848 
849  break;
850  }
851  }
852  }
853 
854  // now post the "set focus to this item" event (high priority)
855  if(bFound > 0) // need to send message for focus change
856  {
857  XClientMessageEvent evt = {
858  .type=ClientMessage,
859  .serial=0,
860  .send_event=0,
861  .display=pDisplay,
862  .window=wID,
863  .message_type=aDLG_FOCUS,
864  .format=32
865  };
866  evt.data.l[0] = 0;
867  evt.data.l[1] = pDialogWindow->pwContents[i1].iID;
868 
869  WBPostPriorityEvent(wID, (XEvent *)&evt); // priority event makes it happen faster
870  }
871  }
872  }
873  else if(pEvent->xclient.message_type == aLOSTFOCUS) // 'focus change' messages
874  {
875  WB_WARN_PRINT("%s - TODO: handle 'LOST FOCUS' notifications - control id %d\n",
876  __FUNCTION__, ((WBDialogEntry *)(((XClientMessageEvent *)pEvent)->data.l[1]))->iID);
877  }
878  else if(pEvent->xclient.message_type == aDLG_FOCUS) // dialog focus change message
879  {
880  // data.l[0] == 0 for "set to specific dialog control"
881  // data.l[0] < 0 for 'previous', > 0 for 'next'
882  // this must be sent via the message queue or recursion might occur
883 
884  WBDialogEntry *pEntry = NULL;
885  Window wFocus;
886  int iCurFocus;
887 
888  // FIRST, find out which dialog control entry belongs to the item that will
889  // end up with the focus. If the focus bit is already set, just exit.
890 
891  if(!pEvent->xclient.data.l[0])
892  {
893  for(i1=0; i1 < pDialogWindow->nContents; i1++)
894  {
895  if(pDialogWindow->pwContents[i1].iID == pEvent->xclient.data.l[1])
896  {
897  pEntry = pDialogWindow->pwContents + i1;
898  break;
899  }
900  }
901  }
902  else
903  {
904  // who has focus RIGHT NOW ?
905  for(iCurFocus = -1, i1=0; i1 < pDialogWindow->nContents; i1++)
906  {
907  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_HAS_FOCUS)
908  {
909  iCurFocus = i1;
910  break;
911  }
912  }
913 
914  if(iCurFocus < 0)
915  {
916  // no focus, so find the first one (or last one) possible
917  if(pEvent->xclient.data.l[0] < 0)
918  {
919  for(i1=pDialogWindow->nContents - 1; i1 >= 0; i1--)
920  {
921  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS)
922  {
923  pEntry = pDialogWindow->pwContents + i1;
924  break;
925  }
926  }
927  }
928  else
929  {
930  for(i1=0; i1 < pDialogWindow->nContents; i1++)
931  {
932  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS)
933  {
934  pEntry = pDialogWindow->pwContents + i1;
935  break;
936  }
937  }
938  }
939  }
940  else
941  {
942  if(pEvent->xclient.data.l[0] < 0)
943  {
944  for(i1=iCurFocus - 1; i1 >= 0; i1++)
945  {
946  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS)
947  {
948  pEntry = pDialogWindow->pwContents + i1;
949  break;
950  }
951  }
952 
953  if(!pEntry)
954  {
955  for(i1=pDialogWindow->nContents - 1; i1 > iCurFocus; i1--)
956  {
957  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS)
958  {
959  pEntry = pDialogWindow->pwContents + i1;
960  break;
961  }
962  }
963  }
964  }
965  else
966  {
967  for(i1=iCurFocus + 1; i1 < pDialogWindow->nContents; i1++)
968  {
969  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS)
970  {
971  pEntry = pDialogWindow->pwContents + i1;
972  break;
973  }
974  }
975 
976  if(!pEntry)
977  {
978  for(i1=0; i1 < iCurFocus; i1++)
979  {
980  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS)
981  {
982  pEntry = pDialogWindow->pwContents + i1;
983  break;
984  }
985  }
986  }
987  }
988  }
989  }
990 
991  if(!pEntry)
992  {
993  WB_WARN_PRINT("%s - unable to set focus for DLG_FOCUS event, l[0]=%ld, l[1]=%ld\n",
994  __FUNCTION__, pEvent->xclient.data.l[0], pEvent->xclient.data.l[1]);
995  // no focus - can't do it
996  break; // for now allow it without error
997  }
998 
999  // set all of the focus bits correctly
1000 
1001  for(i1=0; i1 < pDialogWindow->nContents; i1++)
1002  {
1003  if(pEntry == pDialogWindow->pwContents + i1)
1004  {
1005  pEntry->iFlags |= WBDialogEntry_HAS_FOCUS;
1006  }
1007  else if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_HAS_FOCUS)
1008  {
1009  pDialogWindow->pwContents[i1].iFlags &= ~WBDialogEntry_HAS_FOCUS;
1010  WBInvalidateGeom(pDialogWindow->pwContents[i1].wID, NULL, 1); // force re-paint
1011  }
1012  }
1013 
1014  // NEXT, see if I (or any of my children) have the focus. If not, I've already
1015  // set the 'HAS FOCUS' bit so exit without actually changing the focus.
1016  // Otherwise I'll need to actually set the focus via WBSetInputFocus
1017 
1018  wFocus = None;
1019  XGetInputFocus(pDisplay, &wFocus, &i1);
1020 
1021  if(wFocus != None && (wFocus == wID || WBIsChildWindow(wID, wFocus)))
1022  {
1023 #ifndef NO_DEBUG
1024  char *p1 = WBGetAtomName(pDisplay, (Atom)pEntry->iID);
1025 
1027  "%s - me or child has focus, setting to %d (%s), %d (%08xH)\n",
1028  __FUNCTION__, pEntry->iID, p1,
1029  (int)pEntry->wID, (int)pEntry->wID);
1030  if(p1)
1031  {
1032  WBFree(p1);
1033  }
1034 #endif // NO_DEBUG
1035 
1036  //XSetInputFocus(pDisplay, pEntry->wID, RevertToParent, CurrentTime);
1037  WBSetInputFocus(pEntry->wID);
1038  WBInvalidateGeom(pEntry->wID, NULL, 1); // force re-paint
1039  }
1040  }
1041 
1042  break;
1043 
1044  case KeyPress:
1045  case KeyRelease:
1046 
1048  "%s KEY PRESS/RELEASE - dialog window - check for hotkeys\n", __FUNCTION__);
1049 
1050  iRval = DLGProcessHotKey((WBDialogWindow *)pDialogWindow, pEvent);
1051 
1052  if(iRval)
1053  {
1054  return iRval;
1055  }
1056 
1057  break;
1058 
1059  case FocusIn:
1060 
1062  "%s - FocusIn handler\n", __FUNCTION__);
1063 
1064  for(i1=0; i1 < pDialogWindow->nContents; i1++)
1065  {
1066  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_HAS_FOCUS)
1067  {
1068  Display *pDisplay = WBGetWindowDisplay(pDialogWindow->pwContents[i1].wID);
1069  Window wID = pDialogWindow->pwContents[i1].wID;
1070 
1071  if(WBIsValid(pDisplay, wID))
1072  {
1073 #ifndef NO_DEBUG
1075  {
1076  char *p1 = NULL;
1077  if(pDialogWindow->pwContents[i1].iID < WB_MIN_STD_CTRL_ID)
1078  {
1079  p1 = WBGetAtomName(pDisplay, (Atom)pDialogWindow->pwContents[i1].iID);
1080  }
1081 
1082  WBDebugPrint("%s - me or child has focus, setting to %d (%s), %d (%08xH)\n",
1083  __FUNCTION__, pDialogWindow->pwContents[i1].iID,
1084  (const char *)(p1 ? p1 : "NULL"),
1085  (int)pDialogWindow->pwContents[i1].wID, (int)pDialogWindow->pwContents[i1].wID);
1086  if(p1)
1087  {
1088  WBFree(p1);
1089  }
1090  }
1091 #endif // NO_DEBUG
1092 
1093  //XSetInputFocus(pDisplay, wID, RevertToParent, CurrentTime);
1094  WBSetInputFocus(wID);
1095 
1096  break;
1097  }
1098  else
1099  {
1100  WB_WARN_PRINT("Warning: %s %s:%d window %d (%08xH) isn't valid, can't set focus\n",
1101  __FUNCTION__, __FILE__, __LINE__, (int)wID, (int)wID);
1102  }
1103  }
1104  }
1105 
1106  if(i1 >= pDialogWindow->nContents) // none had the focus? Find one and set it!
1107  {
1108  for(i1=0; i1 < pDialogWindow->nContents; i1++)
1109  {
1110  if(pDialogWindow->pwContents[i1].iFlags & WBDialogEntry_CAN_HAVE_FOCUS)
1111  {
1112  Display *pDisplay = WBGetWindowDisplay(pDialogWindow->pwContents[i1].wID);
1113  Window wID = pDialogWindow->pwContents[i1].wID;
1114 
1115  // TODO: check visibility?
1116  if(WBIsValid(pDisplay,wID))
1117  {
1118 #ifndef NO_DEBUG
1120  {
1121  char *p1 = NULL;
1122  if(pDialogWindow->pwContents[i1].iID < WB_MIN_STD_CTRL_ID)
1123  {
1124  p1 = WBGetAtomName(pDisplay, (Atom)pDialogWindow->pwContents[i1].iID);
1125  }
1126 
1127  WBDebugPrint("%s - me or child has focus, setting to %d (%s), %d (%08xH)\n",
1128  __FUNCTION__, pDialogWindow->pwContents[i1].iID,
1129  (const char *)(p1 ? p1 : "NULL"),
1130  (int)pDialogWindow->pwContents[i1].wID, (int)pDialogWindow->pwContents[i1].wID);
1131  if(p1)
1132  {
1133  WBFree(p1);
1134  }
1135  }
1136 #endif // NO_DEBUG
1137 
1138  pDialogWindow->pwContents[i1].iFlags |= WBDialogEntry_HAS_FOCUS;
1139 
1140  //XSetInputFocus(pDisplay, wID, RevertToParent, CurrentTime);
1141  WBSetInputFocus(wID);
1142  break;
1143  }
1144  else
1145  {
1146  WB_WARN_PRINT("Warning: %s %s:%d window %d (%08xH) isn't valid, can't set focus\n",
1147  __FUNCTION__, __FILE__, __LINE__, (int)wID, (int)wID);
1148  }
1149  }
1150  }
1151  }
1152 
1153  break;
1154  }
1155 
1156 
1157  if(pDialogWindow->pDLGCallback)
1158  {
1159  // for most messages, if I handle it here, I don't do further processing
1160 
1161  iRval = pDialogWindow->pDLGCallback(wID, pEvent);
1162 
1163  if(iRval)
1164  {
1165  // check message types that I do *NOT* want to 'bail out' for
1166 
1167  switch(pEvent->type)
1168  {
1169  case DestroyNotify:
1171  "%s - DestroyNotify and user callback returned a non-zero value\n", __FUNCTION__);
1172  break;
1173 
1174  case Expose:
1175  return iRval; // 'expose' event already handled
1176 
1177  default:
1178  return iRval;
1179  }
1180  }
1181  }
1182 
1183  // TODO: message re-direction to children AFTER 'pDLGCallback'
1184  // the system is SUPPOSED to destroy children first...
1185 
1186  // special handling for 'destroy'
1187  if(pEvent->type == DestroyNotify &&
1188  pEvent->xdestroywindow.window == wID)
1189  {
1191  "%s - DestroyNotify Window=%d\n", __FUNCTION__, (int)wID);
1192 
1193  WBSetWindowData(wID, 0, NULL);
1194 
1195  if(pDialogWindow)
1196  {
1197  Window wIDOwner = pDialogWindow->wbDLG.wIDOwner;
1198  __internal_destroy_dialog_window(pDialogWindow);
1199 
1200  WBFree(pDialogWindow);
1201 
1202  // if the window had an owner, raise it and assign focus to it
1203 
1204  if(wIDOwner != None)
1205  {
1206  WBPostDelayedSetFocusAppEvent(WBGetWindowDisplay(wIDOwner), wIDOwner, wID, 100); // 100 msec delay
1207  }
1208  }
1209 
1210  return 1; // tells caller I did internal handling for this
1211  }
1212 
1213  return 0;
1214 }
1215 
1216 void DLGRecalcLayout(Window wID)
1217 {
1218  // this callback happens any time I change the window size. Use this
1219  // also to re-size scrollbars and status bars and menus.
1220 
1221 }
1222 
1223 void * DLGGetDialogWindowUserData(Window wID)
1224 {
1226 
1227  if(!pDlg)
1228  {
1229  return NULL;
1230  }
1231 
1232  return pDlg->wbDLG.pUserData;
1233 }
1234 
1235 Window DLGGetDialogControl(WBDialogWindow *pDialog, int iControlID)
1236 {
1237  DIALOG_WINDOW *pSelf = (DIALOG_WINDOW *)pDialog;
1238  int i1;
1239 
1240  if(!pSelf || !pSelf->pwContents)
1241  return None; /* 0 */
1242 
1243  for(i1=0; i1 < pSelf->nContents && i1 < pSelf->nMaxContents; i1++)
1244  {
1245  if(pSelf->pwContents[i1].iID == iControlID)
1246  {
1247  return pSelf->pwContents[i1].wID;
1248  }
1249  }
1250 
1251  return None;
1252 }
1253 
1255 {
1256  DIALOG_WINDOW *pSelf = (DIALOG_WINDOW *)pDialog;
1257  int i1;
1258 
1259  if(!pSelf || !pSelf->pwContents)
1260  return NULL;
1261 
1262  for(i1=0; i1 < pSelf->nContents && i1 < pSelf->nMaxContents; i1++)
1263  {
1264  if(pSelf->pwContents[i1].wID == idControl)
1265  {
1266  return pSelf->pwContents + i1;
1267  }
1268  }
1269 
1270  return NULL;
1271 }
1272 
1274 {
1275  DIALOG_WINDOW *pSelf = (DIALOG_WINDOW *)pDialog;
1276 
1277  if(!pSelf || !pSelf->pwContents)
1278  return None; /* 0 */
1279 
1280  return pSelf->pwContents[0].wID;
1281 }
1282 
1283 Window DLGGetNextDialogControl(WBDialogWindow *pDialog, Window idControl)
1284 {
1285  DIALOG_WINDOW *pSelf = (DIALOG_WINDOW *)pDialog;
1286  int i1;
1287 
1288  if(!pSelf || !pSelf->pwContents)
1289  return None;
1290 
1291  for(i1=0; i1 < pSelf->nContents && i1 < pSelf->nMaxContents; i1++)
1292  {
1293  if(pSelf->pwContents[i1].wID == idControl)
1294  {
1295  if((i1 + 1) >= pSelf->nContents || (i1 + 1) >= pSelf->nMaxContents)
1296  i1 = 0;
1297  else
1298  i1++;
1299 
1300  return pSelf->pwContents[i1].wID;
1301  }
1302  }
1303 
1304  return None;
1305 }
1306 
1307 Window DLGGetPrevDialogControl(WBDialogWindow *pDialog, Window idControl)
1308 {
1309  DIALOG_WINDOW *pSelf = (DIALOG_WINDOW *)pDialog;
1310  int i1;
1311 
1312  if(!pSelf || !pSelf->pwContents)
1313  return None;
1314 
1315  for(i1=0; i1 < pSelf->nContents && i1 < pSelf->nMaxContents; i1++)
1316  {
1317  if(pSelf->pwContents[i1].wID == idControl)
1318  {
1319  if(i1 == 0)
1320  {
1321  i1 = pSelf->nContents - 1;
1322 
1323  if(i1 >= pSelf->nMaxContents)
1324  i1 = pSelf->nMaxContents - 1;
1325  }
1326  else
1327  i1--;
1328 
1329  return pSelf->pwContents[i1].wID;
1330  }
1331  }
1332 
1333  return None;
1334 }
1335 
1336 
1337 int DLGProcessHotKey(WBDialogWindow *pDLG, XEvent *pEvent)
1338 {
1339 //char tbuf[16];
1340 //int cbLen;
1341 KeySym ks;
1342 int iACSMask = ShiftMask | ControlMask | Mod1Mask /* | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask */;
1343 int i1, /* iCtrl, iAlt, iShift, */ iNoEnter = 0, iNoEsc = 0, iNoTab = 0;
1344 WBDialogEntry *pFocusEntry = NULL, *pDefaultEntry = NULL, *pCancelEntry = NULL;
1345 Display *pDisplay = WBGetWindowDisplay(pDLG->wID);
1346 
1347 
1348  iACSMask &= ((XKeyEvent *)pEvent)->state;
1349 
1350 // TODO: do I need the CTRL/ALT/SHIFT info? uncomment these later if I need it
1351 // for now they are commented out because linux GCC barphs on unused assigned vars
1352 // iCtrl = (iACSMask & ControlMask) ? 1 : 0;
1353 // iShift = (iACSMask & ShiftMask) ? 1 : 0;
1354 // iAlt = (iACSMask & Mod1Mask) ? 1 : 0;
1355 
1356  // find default & current focus control entries
1357  for(i1=0; ((DIALOG_WINDOW *)pDLG)->pwContents && i1 < ((DIALOG_WINDOW *)pDLG)->nContents; i1++)
1358  {
1359  if(((DIALOG_WINDOW *)pDLG)->pwContents[i1].iFlags & WBDialogEntry_HAS_FOCUS)
1360  {
1361  pFocusEntry = ((DIALOG_WINDOW *)pDLG)->pwContents + i1;
1362  }
1363 
1364  if(((DIALOG_WINDOW *)pDLG)->pwContents[i1].iFlags & WBDialogEntry_DEFAULT)
1365  {
1366  pDefaultEntry = ((DIALOG_WINDOW *)pDLG)->pwContents + i1;
1367  }
1368 
1369  if(((DIALOG_WINDOW *)pDLG)->pwContents[i1].aClass == aCANCELBUTTON_CONTROL)
1370  {
1371  pCancelEntry = ((DIALOG_WINDOW *)pDLG)->pwContents + i1;
1372  }
1373 
1374  if(pDefaultEntry && pFocusEntry && pCancelEntry)
1375  {
1376  break;
1377  }
1378  }
1379 
1380  if(pFocusEntry
1381  && (pFocusEntry->aClass == aEDIT_CONTROL || (pFocusEntry->iFlags & WBDialogEntry_EDIT)))
1382  {
1383  if(pFocusEntry->iFlags & WBDialogEntry_MULTILINE)
1384  {
1385  iNoEnter = 1;
1386  }
1387  if(pFocusEntry->iFlags & WBDialogEntry_ALLCHARS)
1388  {
1389  iNoEnter = 1;
1390  iNoEsc = 1;
1391  iNoTab = 1;
1392  }
1393  }
1394 
1395  if(pEvent->type == KeyPress || pEvent->type == KeyRelease)
1396  {
1397  ks = XLookupKeysym((XKeyEvent *)pEvent, 0); // always use '0' index for these
1398 
1399  if(iACSMask == Mod1Mask) // alt key held down, no others
1400  {
1401  const char *pKey = XKeysymToString(ks);
1402 
1403  if(!pKey)
1404  {
1405  // alt key is non-printing - this code will skip it
1406  return 0;
1407  }
1408 
1410  "%s - user pressed Alt+%s %d (%xH)\n",
1411  __FUNCTION__, pKey, (int)ks, (int)ks);
1412 
1413  if(pEvent->type == KeyRelease)
1414  {
1415  // who gets this alt key? Keystroke is ALT + *pKey
1416  // search everyone's caption until I find one. Start with the control that
1417  // currently has the focus, then wrap around. That way duplicate underscores
1418  // will bounce from one to the other in dialog order.
1419 
1420 
1421  }
1422  }
1423  else if(!iNoEnter
1424  && pEvent->type == KeyRelease
1425  && ((int)ks == XK_Return || (int)ks == XK_KP_Enter) // someone pressed <ENTER>
1426  && iACSMask == 0) // no modifiers
1427  {
1429  "%s - DIALOG WINDOW - <enter> detected\n", __FUNCTION__);
1430 
1431  if((!(pFocusEntry->iFlags & WBDialogEntry_PUSHBUTTON) && pDefaultEntry)
1432  || pFocusEntry == pDefaultEntry)
1433  {
1434  XClientMessageEvent evt = {
1435  .type=ClientMessage,
1436  .serial=0,
1437  .send_event=0,
1438  .display=pDisplay,
1439  .window=pDLG->wID,
1440  .message_type=aCONTROL_NOTIFY,
1441  .format=32
1442  };
1443  evt.data.l[0] = aBUTTON_PRESS;
1444  evt.data.l[1] = pDefaultEntry->iID;
1445  evt.data.l[2] = 0;
1446 
1447  // simulate a button press of the default button
1448  WBPostPriorityEvent(pDLG->wID, (XEvent *)&evt); // priority event makes it happen faster
1449  return 1;
1450  }
1451  }
1452  else if(!iNoEsc
1453  && pEvent->type == KeyRelease
1454  && ((int)ks == XK_Escape) // someone pressed <ESC>
1455  && iACSMask == 0) // no modifiers
1456  {
1458  "%s - DIALOG WINDOW - <ESC> detected\n", __FUNCTION__);
1459 
1460  if((!(pFocusEntry->iFlags & WBDialogEntry_PUSHBUTTON) && pCancelEntry)
1461  || pFocusEntry->aClass == aCANCELBUTTON_CONTROL)
1462  {
1463  XClientMessageEvent evt = {
1464  .type=ClientMessage,
1465  .serial=0,
1466  .send_event=0,
1467  .display=pDisplay,
1468  .window=pDLG->wID,
1469  .message_type=aCONTROL_NOTIFY,
1470  .format=32
1471  };
1472  evt.data.l[0] = aBUTTON_PRESS;
1473  evt.data.l[1] = pCancelEntry->iID;
1474  evt.data.l[2] = 0;
1475 
1476  // simulate a button press of the default button
1477  WBPostPriorityEvent(pDLG->wID, (XEvent *)&evt); // priority event makes it happen faster
1478  return 1;
1479  }
1480  }
1481  else if(!iNoTab
1482  && pEvent->type == KeyRelease
1483  && ((int)ks == XK_Tab || (int)ks == XK_KP_Tab) // someone pressed <TAB>
1484  && (iACSMask == 0 || iACSMask == ShiftMask)) // either a tab or back-tab
1485  {
1486  XClientMessageEvent evt = {
1487  .type=ClientMessage,
1488  .serial=0,
1489  .send_event=0,
1490  .display=pDisplay,
1491  .window=pDLG->wID,
1492  .message_type=aDLG_FOCUS,
1493  .format=32
1494  };
1495  evt.data.l[0] = iACSMask == 0 ? 1 : -1;
1496  evt.data.l[1] = 0;
1497 
1498  WBPostPriorityEvent(pDLG->wID, (XEvent *)&evt); // priority event makes it happen faster
1499  return 1;
1500  }
1501  else if(pEvent->type == KeyRelease)
1502  {
1503 // fprintf(stderr, "TEMPORARY: user pressed %s (%d,%d) and the state is %d (%08xH)\n",
1504 // tbuf, tbuf[0], ((XKeyEvent *)pEvent)->keycode,
1505 // ((XKeyEvent *)pEvent)->state,((XKeyEvent *)pEvent)->state );
1506  }
1507  }
1508  else
1509  {
1510  WB_WARN_PRINT("%s - calling DLGProcessHotKey and it's not a key event\n", __FUNCTION__);
1511  }
1512 
1513  return 0;
1514 }
1515 
1516 
1517 
1518 
1519 
1520 
1521 
1522 // creating child windows
1523 
1524 enum DlgResourceStringKeywordIndices
1525 {
1526  DlgResourceKW__BEGIN_DIALOG=0,
1527  DlgResourceKW__END_DIALOG,
1528  DlgResourceKW__FONT,
1529  DlgResourceKW__CONTROL,
1530  DlgResourceKW__ID,
1531  DlgResourceKW__X,
1532  DlgResourceKW__Y,
1533  DlgResourceKW__WIDTH,
1534  DlgResourceKW__HEIGHT,
1535  DlgResourceKW__VISIBLE,
1536  DlgResourceKW__TITLE,
1537  DlgResourceKW__VATOP,
1538  DlgResourceKW__VABOTTOM,
1539  DlgResourceKW__VACENTER,
1540  DlgResourceKW__HALEFT,
1541  DlgResourceKW__HARIGHT,
1542  DlgResourceKW__HACENTER,
1543  DlgResourceKW__VRESIZE,
1544  DlgResourceKW__HRESIZE
1545 };
1546 
1547 static const char * const aszKeywords[]={ "BEGIN_DIALOG","END_DIALOG","FONT","CONTROL","ID","X","Y","WIDTH","HEIGHT","VISIBLE","TITLE",
1548  "VALIGN_BOTTOM","VALIGN_TOP","VALIGN_CENTER","HALIGN_LEFT","HALIGN_RIGHT","HALIGN_CENTER",
1549  "VRESIZE","HRESIZE",
1550  NULL };
1551 
1552 // some built-in symbols for dialog resources and their corresponding symbol IDs
1553 static const char * const aszSymbols[]={ "IDOK", "IDCANCEL", "IDYES", "IDNO", "IDRETRY", "IDABORT", "IDIGNORE", "IDSTATIC", "IDNONE", NULL };
1554 static const int iIDSymbols[]={ IDOK, IDCANCEL, IDYES, IDNO, IDRETRY, IDABORT, IDIGNORE, IDSTATIC, IDNONE, -1 };
1555 
1556 static int InternalCreateChildWindowsSub(DIALOG_WINDOW *pDlg, const char **pszDialogResource);
1557 
1558 static int InternalCreateChildWindows(DIALOG_WINDOW *pDlg, const char *szDialogResource)
1559 {
1560 const char *p1, *p2;
1561 int iRval;
1562 
1563  if(!szDialogResource || !*szDialogResource)
1564  {
1565  return 0; // don't allow it
1566  }
1567 
1568  p1 = szDialogResource;
1569  while(*p1 && *p1 <= ' ')
1570  p1++;
1571 
1572  p2 = p1;
1573  while(*p1 && *p1 > ' ')
1574  p1++;
1575 
1576  // must be 'BEGIN_DIALOG'
1577  if((p1 - p2) != strlen(aszKeywords[DlgResourceKW__BEGIN_DIALOG]) ||
1578  memcmp(p2, aszKeywords[DlgResourceKW__BEGIN_DIALOG], (p1 - p2)) != 0)
1579  {
1580  WB_WARN_PRINT("%s - dialog box resource missing 'BEGIN_DIALOG'\n", __FUNCTION__);
1581  return 0;
1582  }
1583 
1584  while(*p1 && *p1 <= ' ')
1585  p1++;
1586 
1587  iRval = InternalCreateChildWindowsSub(pDlg, &p1);
1588  if(!iRval)
1589  {
1590  WB_ERROR_PRINT("%s - InternalCreateChildWindowsSub failure at \"%-.30s\"\n", __FUNCTION__, p1);
1591  }
1592 
1593  return iRval;
1594 }
1595 
1596 static int get_id_val(const char *szVal, int iLen)
1597 {
1598 // NOTE: if I need a different list for dialog control ID symbols, put it "up there" with aszSymbols (etc.)
1599 int i1;
1600 char tbuf[256];
1601 
1602 
1603  if(iLen > 0)
1604  {
1605  if(iLen >= sizeof(tbuf))
1606  {
1607  iLen = sizeof(tbuf) - 1;
1608  }
1609 
1610  memcpy(tbuf, szVal, iLen);
1611  }
1612 
1613  if(iLen < 0)
1614  {
1615  iLen = 0;
1616  }
1617 
1618  tbuf[iLen] = 0;
1619 
1620  if(!tbuf[0])
1621  {
1622  return 0;
1623  }
1624 
1625  WBDeQuoteString(tbuf);
1626 
1627  if(tbuf[0] >= '0' && tbuf[0] <= '9')
1628  {
1629  return atoi(tbuf);
1630  }
1631 
1632  // check for well-known alpha-numeric values
1633 
1634  for(i1=0; aszSymbols[i1]; i1++)
1635  {
1636  if(!strcmp(tbuf, aszSymbols[i1]))
1637  {
1638  return iIDSymbols[i1];
1639  }
1640  }
1641 
1642  // TODO: registered list of symbol names
1643 
1644  // now assume it's an atom, and return that
1645 
1646  return WBGetAtom(WBGetDefaultDisplay(), tbuf);
1647 }
1648 
1649 
1650 
1651 static int InternalCreateChildWindowsSub(DIALOG_WINDOW *pDlg, const char **pszDialogResource)
1652 {
1653 const char *p1, *p2, *p3;
1654 int i1, iKW, nKids = 0, iRval = 0;
1655 unsigned int iCurSize = 0, iCurContentSize = 0;
1656 struct _KIDS_
1657 {
1658  Atom aClass; // atom 'class' for control entry
1659  int iID; // ID for control entry
1660  int iX, iY, iWidth, iHeight; // that too
1661  int iFlags;
1662  char *szTitle; // pointer to WBAlloc'd string with title
1663  char *szProp; // pointer to WBAlloc'd string with additional properties
1664 } *pKids = NULL;
1665 char tbuf[256];
1666 
1667 
1668  p1 = p2 = *pszDialogResource;
1669 
1670  while(*p1)
1671  {
1672  p2 = p1;
1673  while(*p1 && *p1 > ' ')
1674  {
1675  if(*p1 == '"' || *p1 == '\'')
1676  {
1677  // we allow embedded quoted strings preceded by non-quotes
1678  char c = *(p1++);
1679 
1680  while(*p1 &&
1681  (*p1 != c || p1[1] == c))
1682  {
1683  if(*p1 == c)
1684  {
1685  p1++;
1686  }
1687 
1688  if(*p1 == '\n' || *p1 == '\r') // not allowed
1689  {
1690  WB_WARN_PRINT("%s - Illegal newline/return character in embedded string within dialog resource\n",
1691  __FUNCTION__);
1692  *pszDialogResource = p2;
1693  return 0;
1694  }
1695 
1696  p1++;
1697  }
1698 
1699  if(*p1 == c)
1700  {
1701  p1++;
1702  }
1703  }
1704  else
1705  {
1706  p1++;
1707  }
1708  }
1709 
1710  for(iKW=0; aszKeywords[iKW]; iKW++)
1711  {
1712  i1 = strlen(aszKeywords[iKW]);
1713  if(i1 <= (p1 - p2) && // keyword length shorter than 'term' or equal in size
1714  (p2[i1] <= ' ' || p2[i1] == ':') &&
1715  !memcmp(p2, aszKeywords[iKW], i1))
1716  {
1717  p3 = p2 + i1;
1718 
1719  if(*p3 == ':')
1720  {
1721  p3++;
1722  }
1723 
1724  break;
1725  }
1726  }
1727 
1728  if(!aszKeywords[iKW])
1729  {
1730  WB_WARN_PRINT("%s - NULL in aszKeywords in InternalCreateChildWindowsSub within dialog resource\n", __FUNCTION__);
1731  // TODO: do I handle this as a custom property for a control?
1732  *pszDialogResource = p2;
1733  return 0;
1734  }
1735 
1736  if(iKW == DlgResourceKW__END_DIALOG)
1737  {
1738  if(!pKids)
1739  {
1740  pKids = WBAlloc(sizeof(*pKids)); // because it can't be NULL
1741  nKids = 0;
1742  }
1743 
1744  break;
1745  }
1746 
1747  if(iKW == DlgResourceKW__BEGIN_DIALOG)
1748  {
1749  WB_WARN_PRINT("%s - nested dialogs not supported (yet)\n", __FUNCTION__);
1750 
1751  *pszDialogResource = p2;
1752  return 0;
1753  }
1754 
1755  switch(iKW)
1756  {
1757  case DlgResourceKW__CONTROL:
1758 
1759  if(!pKids)
1760  {
1761  pKids = WBAlloc(iCurSize = (256 * sizeof(*pKids)));
1762  if(!pKids)
1763  {
1764  WB_ERROR_PRINT("%s - not enough memory for controls\n", __FUNCTION__);
1765  iKW = -1;
1766  break;
1767  }
1768 
1769 //#ifdef HAVE_MALLOC_USABLE_SIZE
1770  iCurSize = WBAllocUsableSize(pKids); // re-evaluate actual size
1771 //#endif // HAVE_MALLOC_USABLE_SIZE
1772 
1773  nKids = 1;
1774  }
1775  else if((nKids + 1) * sizeof(*pKids) > iCurSize)
1776  {
1777  int iNewSize;
1778  void *p0;
1779 
1780  iNewSize = (nKids + 128) * sizeof(*pKids);
1781  p0 = WBReAlloc(pKids, iNewSize);
1782 
1783  if(!p0)
1784  {
1785  WB_ERROR_PRINT("%s - not enough memory for all controls\n", __FUNCTION__);
1786  iKW = -1;
1787  break;
1788  }
1789 
1790 //#ifdef HAVE_MALLOC_USABLE_SIZE
1791  iCurSize = WBAllocUsableSize(p0); // re-evaluate actual size
1792 //#else // HAVE_MALLOC_USABLE_SIZE
1793 // iCurSize = iNewSize;
1794 //#endif // HAVE_MALLOC_USABLE_SIZE
1795 
1796  pKids = (struct _KIDS_ *)p0;
1797  nKids++;
1798  }
1799  else
1800  {
1801  nKids++;
1802  }
1803 
1804  memset(pKids + nKids - 1, 0, sizeof(*pKids));
1805 
1806  // get the correct atom for the control name
1807 
1808  if(p1 <= p3)
1809  {
1810  WB_WARN_PRINT("%s - Invalid control name %-.20s\n", __FUNCTION__, p3);
1811  iKW = -1;
1812 
1813  break;
1814  }
1815 
1816  memcpy(tbuf, p3, p1 - p3);
1817  tbuf[p1 - p3] = 0;
1818 
1820  "%s - control %s\n", __FUNCTION__, tbuf);
1821 
1822  pKids[nKids - 1].aClass = WBGetAtom(WBGetDefaultDisplay(), tbuf);
1823 
1824  break;
1825 
1826  case DlgResourceKW__ID:
1827  if(!nKids) // ID applied to dialog box is ignored
1828  {
1829  WB_WARN_PRINT("%s - Ignoring ID for dialog box, %.20s\n", __FUNCTION__, p3);
1830  }
1831  else
1832  {
1833  pKids[nKids - 1].iID = get_id_val(p3, p1 - p3);
1834  }
1835 
1836  break;
1837 
1838  case DlgResourceKW__X:
1839  if(!nKids) // X applied to dialog box is ignored
1840  {
1841  WB_WARN_PRINT("%s - Ignoring X for dialog box, %.20s\n", __FUNCTION__, p3);
1842  }
1843  else
1844  {
1845  pKids[nKids - 1].iX = get_id_val(p3, p1 - p3);
1846  }
1847 
1848  break;
1849 
1850  case DlgResourceKW__Y:
1851  if(!nKids) // Y applied to dialog box is ignored
1852  {
1853  WB_WARN_PRINT("%s - Ignoring Y for dialog box, %.20s\n", __FUNCTION__, p3);
1854  }
1855  else
1856  {
1857  pKids[nKids - 1].iY = get_id_val(p3, p1 - p3);
1858  }
1859 
1860  break;
1861 
1862  case DlgResourceKW__WIDTH:
1863  if(!nKids)
1864  {
1865  pDlg->wbDLG.iClientWidth = get_id_val(p3, p1 - p3);
1866  }
1867  else
1868  {
1869  pKids[nKids - 1].iWidth = get_id_val(p3, p1 - p3);
1870  }
1871 
1872  break;
1873 
1874  case DlgResourceKW__HEIGHT:
1875  if(!nKids)
1876  {
1877  pDlg->wbDLG.iClientHeight = get_id_val(p3, p1 - p3);
1878  }
1879  else
1880  {
1881  pKids[nKids - 1].iHeight = get_id_val(p3, p1 - p3);
1882  }
1883 
1884  break;
1885 
1886  case DlgResourceKW__VATOP:
1887  if(!nKids) // visible applied to dialog box is ignored
1888  {
1889  WB_WARN_PRINT("%s - RESERVED: 'top aligned' property on dialog box\n", __FUNCTION__);
1890  }
1891  else
1892  {
1893  pKids[nKids - 1].iFlags &= ~WBDialogEntry_VAlignMask;
1894  pKids[nKids - 1].iFlags |= WBDialogEntry_VAlignTop;
1895  }
1896 
1897  break;
1898 
1899  case DlgResourceKW__VABOTTOM:
1900  if(!nKids) // visible applied to dialog box is ignored
1901  {
1902  WB_WARN_PRINT("%s - RESERVED: 'top aligned' property on dialog box\n", __FUNCTION__);
1903  }
1904  else
1905  {
1906  pKids[nKids - 1].iFlags &= ~WBDialogEntry_VAlignMask;
1907  pKids[nKids - 1].iFlags |= WBDialogEntry_VAlignBottom;
1908  }
1909 
1910  break;
1911 
1912  case DlgResourceKW__VACENTER:
1913  if(!nKids) // visible applied to dialog box is ignored
1914  {
1915  WB_WARN_PRINT("%s - RESERVED: 'top aligned' property on dialog box\n", __FUNCTION__);
1916  }
1917  else
1918  {
1919  pKids[nKids - 1].iFlags &= ~WBDialogEntry_VAlignMask;
1920  pKids[nKids - 1].iFlags |= WBDialogEntry_VAlignCenter;
1921  }
1922 
1923  break;
1924 
1925  case DlgResourceKW__HALEFT:
1926  if(!nKids) // visible applied to dialog box is ignored
1927  {
1928  WB_WARN_PRINT("%s - RESERVED: 'top aligned' property on dialog box\n", __FUNCTION__);
1929  }
1930  else
1931  {
1932  pKids[nKids - 1].iFlags &= ~WBDialogEntry_HAlignMask;
1933  pKids[nKids - 1].iFlags |= WBDialogEntry_HAlignLeft;
1934  }
1935 
1936  break;
1937 
1938  case DlgResourceKW__HARIGHT:
1939  if(!nKids) // visible applied to dialog box is ignored
1940  {
1941  WB_WARN_PRINT("%s - RESERVED: 'top aligned' property on dialog box\n", __FUNCTION__);
1942  }
1943  else
1944  {
1945  pKids[nKids - 1].iFlags &= ~WBDialogEntry_HAlignMask;
1946  pKids[nKids - 1].iFlags |= WBDialogEntry_HAlignRight;
1947  }
1948 
1949  break;
1950 
1951  case DlgResourceKW__HACENTER:
1952  if(!nKids) // visible applied to dialog box is ignored
1953  {
1954  WB_WARN_PRINT("%s - RESERVED: 'top aligned' property on dialog box\n", __FUNCTION__);
1955  }
1956  else
1957  {
1958  pKids[nKids - 1].iFlags &= ~WBDialogEntry_HAlignMask;
1959  pKids[nKids - 1].iFlags |= WBDialogEntry_HAlignCenter;
1960  }
1961 
1962  break;
1963 
1964  case DlgResourceKW__HRESIZE:
1965  if(!nKids)
1966  {
1968  }
1969  else
1970  {
1971  WB_WARN_PRINT("%s - WARNING: 'HRESIZE' property on dialog control (ignored)\n", __FUNCTION__);
1972  }
1973 
1974  break;
1975 
1976  case DlgResourceKW__VRESIZE:
1977  if(!nKids)
1978  {
1980  }
1981  else
1982  {
1983  WB_WARN_PRINT("%s - WARNING: 'VRESIZE' property on dialog control (ignored)\n", __FUNCTION__);
1984  }
1985 
1986  break;
1987 
1988  case DlgResourceKW__TITLE:
1989  if(!nKids)
1990  {
1991  if(pDlg->szTitle)
1992  WBFree(pDlg->szTitle);
1993 
1994  pDlg->szTitle = WBAlloc(p1 - p3 + 1);
1995  if(!pDlg->szTitle)
1996  {
1997  WB_WARN_PRINT("%s - Not enough memory for dialog box title %-.20s\n", __FUNCTION__, p3);
1998  iKW = -1;
1999  break;
2000  }
2001 
2002  memcpy(pDlg->szTitle, p3, p1 - p3);
2003  pDlg->szTitle[p1 - p3] = 0;
2004 
2005  WBDeQuoteString(pDlg->szTitle);
2006  }
2007  else
2008  {
2009  struct _KIDS_ *pKid = pKids + nKids - 1;
2010 
2011  if(pKid->szTitle)
2012  WBFree(pKid->szTitle);
2013 
2014  pKid->szTitle = WBAlloc(p1 - p3 + 1);
2015  if(!pKid->szTitle)
2016  {
2017  WB_WARN_PRINT("%s - Not enough memory for control title %-.20s\n", __FUNCTION__, p3);
2018  iKW = -1;
2019  break;
2020  }
2021 
2022  memcpy(pKid->szTitle, p3, p1 - p3);
2023  pKid->szTitle[p1 - p3] = 0;
2024 
2025  WBDeQuoteString(pKid->szTitle);
2026  }
2027 
2028  break;
2029 
2030  case DlgResourceKW__FONT:
2032  "%s - font definition %-.20s\n", __FUNCTION__, p3);
2033  // TODO: only valid for dialog box at this time?
2034  break;
2035 
2036  case DlgResourceKW__VISIBLE:
2037  if(!nKids) // visible applied to dialog box is ignored
2038  {
2039  WB_WARN_PRINT("%s - RESERVED: 'visible' property on dialog box\n", __FUNCTION__);
2040  }
2041  else
2042  {
2043  pKids[nKids - 1].iFlags |= WBDialogEntry_VISIBLE;
2044  }
2045 
2046  break;
2047  }
2048 
2049  if(iKW < 0) // error in control list
2050  {
2051  if(pKids)
2052  {
2053  for(i1=0; i1 < nKids; i1++)
2054  {
2055  if(pKids[i1].szTitle)
2056  {
2057  WBFree(pKids[i1].szTitle);
2058  }
2059  if(pKids[i1].szProp)
2060  {
2061  WBFree(pKids[i1].szProp);
2062  }
2063  }
2064 
2065  WBFree(pKids);
2066  pKids = NULL; // my error flag below
2067  }
2068 
2069  break; // error flag from above
2070  }
2071 
2072  while(*p1 && *p1 <= ' ')
2073  p1++;
2074  }
2075 
2076  if(!pKids)
2077  {
2078  WB_ERROR_PRINT("%s - NULL 'pKids' in InternalCreateChildWindowsSub\n", __FUNCTION__);
2079  *pszDialogResource = p2; // point of error
2080 
2081  return 0;
2082  }
2083 
2084  if(!pDlg->pwContents) // normally THIS will be the case
2085  {
2086  iCurContentSize = ((nKids + 384) & 0xffffff00L)
2087  * sizeof(*(pDlg->pwContents));
2088  pDlg->pwContents = (WBDialogEntry *)WBAlloc(iCurContentSize);
2089 
2090  if(!pDlg->pwContents)
2091  {
2092  iCurContentSize = 0;
2093 
2094  WB_ERROR_PRINT("%s - NULL 'pwContents' in InternalCreateChildWindowsSub\n", __FUNCTION__);
2095 
2096  for(i1=0; i1 < nKids; i1++)
2097  {
2098  if(pKids[i1].szTitle)
2099  {
2100  WBFree(pKids[i1].szTitle);
2101  }
2102  if(pKids[i1].szProp)
2103  {
2104  WBFree(pKids[i1].szProp);
2105  }
2106  }
2107 
2108  WBFree(pKids);
2109 
2110  *pszDialogResource = p1; // point of error
2111 
2112  return 0;
2113  }
2114 
2115 //#ifdef HAVE_MALLOC_USABLE_SIZE
2116  iCurContentSize = WBAllocUsableSize(pDlg->pwContents); // re-evaluate actual size
2117 //#endif // HAVE_MALLOC_USABLE_SIZE
2118 
2119  pDlg->nContents = 0; // force it
2120 
2121  // calculate 'nMaxContents' from actual memory block size
2122  pDlg->nMaxContents = iCurContentSize
2123  / sizeof(*(pDlg->pwContents));
2124  }
2125  else if((pDlg->nContents + nKids) > pDlg->nMaxContents) // unlikely but maybe later?
2126  {
2127  int iNewSize = (pDlg->nContents + nKids + 128) * sizeof(*(pDlg->pwContents));
2128  void *p0 = WBReAlloc(pDlg->pwContents, iNewSize);
2129 
2130  if(!p0)
2131  {
2132  WB_ERROR_PRINT("%s - NULL 'pwContents' (re-alloc) in InternalCreateChildWindowsSub\n", __FUNCTION__);
2133  for(i1=0; i1 < nKids; i1++)
2134  {
2135  if(pKids[i1].szTitle)
2136  {
2137  WBFree(pKids[i1].szTitle);
2138  }
2139  if(pKids[i1].szProp)
2140  {
2141  WBFree(pKids[i1].szProp);
2142  }
2143  }
2144 
2145  WBFree(pKids);
2146 
2147  *pszDialogResource = p1; // point of error
2148 
2149  return 0;
2150  }
2151 
2152 //#ifdef HAVE_MALLOC_USABLE_SIZE
2153  iCurContentSize = WBAllocUsableSize(p0); // re-evaluate actual size
2154 //#else // HAVE_MALLOC_USABLE_SIZE
2155 // iCurContentSize = iNewSize;
2156 //#endif // HAVE_MALLOC_USABLE_SIZE
2157 
2158  pDlg->pwContents = (WBDialogEntry *)p0;
2159  // calculate 'nMaxContents' from actual memory block size
2160  pDlg->nMaxContents = iCurContentSize
2161  / sizeof(*(pDlg->pwContents));
2162  }
2163 
2164 // fprintf(stderr, "TEMPORARY: dialog has %d controls\n", nKids);
2165 
2166  // load up the structure but don't create the windows (yet)
2167 
2168  for(i1=0, iRval = 1; i1 < nKids; i1++)
2169  {
2170  WBDialogControl *pCtrl = NULL;
2171  WBDialogEntry *pEntry = pDlg->pwContents + pDlg->nContents + i1;
2172 
2173  pEntry->ulTag = DIALOG_ENTRY_TAG;
2174  pEntry->aClass = pKids[i1].aClass;
2175  pEntry->iID = pKids[i1].iID;
2176  pEntry->iX = pKids[i1].iX;
2177  pEntry->iY = pKids[i1].iY;
2178  pEntry->iWidth = pKids[i1].iWidth;
2179  pEntry->iHeight = pKids[i1].iHeight;
2180 
2181  pEntry->iFlags = pKids[i1].iFlags;
2182  pEntry->wID = -1; // not assigned yet
2183 
2184  // structure entry complete - create the control
2185  pCtrl = WBDialogControlCreate(pEntry->aClass, (WBDialogWindow *)pDlg, pEntry,
2186  pEntry->iX, pEntry->iY, pEntry->iWidth, pEntry->iHeight,
2187  pKids[i1].szTitle, pKids[i1].szProp);
2188 
2189  if(!pCtrl)
2190  {
2191 #ifndef NO_DEBUG
2192  char *p1 = WBGetAtomName(WBGetDefaultDisplay(), pEntry->aClass);
2193 
2194  WB_ERROR_PRINT("%s - Unable to create control %d via WBDialogControlCreate\n", __FUNCTION__, i1);
2195  WB_WARN_PRINT("%s - class: %s title: %s id: %d\n", __FUNCTION__, p1, pKids[i1].szTitle, pKids[i1].iID);
2196 
2197  if(p1)
2198  {
2199  WBFree(p1);
2200  }
2201 #endif // NO_DEBUG
2202 
2203  iRval = 0;
2204  break;
2205  }
2206 
2207  pEntry->wID = pCtrl->wID; // assign it now
2208  }
2209 
2210  pDlg->nContents += nKids;
2211 
2212  // final cleanup
2213 
2214  for(i1=0; i1 < nKids; i1++)
2215  {
2216  if(pKids[i1].szTitle)
2217  {
2218  WBFree(pKids[i1].szTitle);
2219  }
2220  if(pKids[i1].szProp)
2221  {
2222  WBFree(pKids[i1].szProp);
2223  }
2224  }
2225 
2226  WBFree(pKids); // not needed now
2227 
2228  *pszDialogResource = p1;
2229 
2230  return iRval;
2231 }
2232