X11 Work Bench Toolkit  1.0
dialog_controls.c
Go to the documentation of this file.
1 
2 // //
3 // _ _ _ _ _ //
4 // __| |(_) __ _ | | ___ __ _ ___ ___ _ __ | |_ _ __ ___ | | ___ ___ //
5 // / _` || | / _` || | / _ \ / _` | / __|/ _ \ | '_ \ | __|| '__|/ _ \ | |/ __| / __| //
6 // | (_| || || (_| || || (_) || (_| | | (__| (_) || | | || |_ | | | (_) || |\__ \ _| (__ //
7 // \__,_||_| \__,_||_| \___/ \__, |_____\___|\___/ |_| |_| \__||_| \___/ |_||___/(_)\___| //
8 // |___/|_____| //
9 // //
10 // dialog control support //
11 // construction, destruction, message handling, and expose handlers for dialog controls //
12 // //
14 
15 /*****************************************************************************
16 
17  X11workbench - X11 programmer's 'work bench' application and toolkit
18  Copyright (c) 2010-2016 by Bob Frazier (aka 'Big Bad Bombastic Bob')
19  all rights reserved
20 
21  DISCLAIMER: The X11workbench application and toolkit software are supplied
22  'as-is', with no warranties, either implied or explicit.
23  Any claims to alleged functionality or features should be
24  considered 'preliminary', and might not function as advertised.
25 
26  BSD-like license:
27 
28  There is no restriction as to what you can do with this software, so long
29  as you include the above copyright notice and DISCLAIMER for any distributed
30  work that is equal to or derived from this one, along with this paragraph
31  that explains the terms of the license if the source is also being made
32  available. A "derived work" describes a work that uses a significant portion
33  of the source files or algorithms that are included with this one.
34  Specifically excluded from this are files that were generated by the software,
35  or anything that is included with the software that is part of another package
36  (such as files that were created or added during the 'configure' process).
37  Specifically included is the use of part or all of any of the X11 workbench
38  toolkit source or header files in your distributed application. If you do not
39  ship the source, the above copyright statement is still required to be placed
40  in a reasonably prominent place, such as documentation, splash screens, and/or
41  'about the application' dialog boxes.
42 
43  Use and distribution are in accordance with GPL, LGPL, and/or the above
44  BSD-like license. See COPYING and README files for more information.
45 
46 
47  Additional information at http://sourceforge.net/projects/X11workbench
48 
49 ******************************************************************************/
50 
51 
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <unistd.h>
55 #include <memory.h>
56 #include <string.h>
57 #include <strings.h>
58 #include <signal.h>
59 #include <time.h>
60 #include <sys/stat.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 <X11/cursorfont.h> /* for special cursors */
68 
69 #include "window_helper.h"
70 #include "pixmap_helper.h" // pixmap helpers, including pre-defined icons
71 #include "dialog_window.h"
72 #include "dialog_controls.h"
73 #include "dialog_support.h" // internal stuff
74 #include "conf_help.h"
75 #include "text_object.h"
76 #include "file_help.h"
77 #include "draw_text.h"
78 #include "window_dressing.h"
79 
82 #define THIS_SUBSYSTEM DebugSubSystem_DialogCtrl
83 
84 #define KEYSYM_DEBUG_FLAG DebugLevel_Excessive/*DebugLevel_ERROR*/
85 
86 #define DEFAULT_STATIC_TAB_WIDTH 4 /* for drawing static text */
87 #define DEFAULT_BUTTON_TAB_WIDTH 4 /* for drawing button text */
88 
89 
90 static void FileListControlDisplayProc(WBDialogControl *pList, void *pData, int iSelected, GC gc, WB_GEOM *pGeom, XFontSet fontSet);
91 
92 
93 
94 #define DEFINE_CREATE_CONTROL(X) \
95  static WBDialogControl * do_create_##X(WBDialogControl *pDialogControl, \
96  int iX, int iY, int iWidth, int iHeight, \
97  const char *szClassName, const char *szTitle);
98 
99 #define IMPLEMENT_CREATE_CONTROL(X) \
100  static WBDialogControl * do_create_##X(WBDialogControl *pDialogControl, \
101  int iX, int iY, int iWidth, int iHeight, \
102  const char *szClassName, const char *szTitle)
103 
104 #define BEGIN_CREATE_CONTROL(X) Atom aThis = a##X; { aThis = aThis; }
105 
106 DEFINE_CREATE_CONTROL(FRAME_CONTROL)
107 DEFINE_CREATE_CONTROL(TEXT_CONTROL)
108 DEFINE_CREATE_CONTROL(ICON_CONTROL)
109 DEFINE_CREATE_CONTROL(IMAGE_CONTROL)
110 DEFINE_CREATE_CONTROL(EDIT_CONTROL)
111 DEFINE_CREATE_CONTROL(PUSHBUTTON_CONTROL)
112 DEFINE_CREATE_CONTROL(DEFPUSHBUTTON_CONTROL)
113 DEFINE_CREATE_CONTROL(CANCELBUTTON_CONTROL)
114 DEFINE_CREATE_CONTROL(RADIOBUTTON_CONTROL)
115 DEFINE_CREATE_CONTROL(FIRSTRADIOBUTTON_CONTROL)
116 DEFINE_CREATE_CONTROL(CHECKBUTTON_CONTROL)
117 DEFINE_CREATE_CONTROL(TRISTATEBUTTON_CONTROL)
118 DEFINE_CREATE_CONTROL(HSCROLL_CONTROL)
119 DEFINE_CREATE_CONTROL(VSCROLL_CONTROL)
120 DEFINE_CREATE_CONTROL(SLIDER_CONTROL)
121 DEFINE_CREATE_CONTROL(KNOB_CONTROL)
122 DEFINE_CREATE_CONTROL(LIST_CONTROL)
123 DEFINE_CREATE_CONTROL(COMBO_CONTROL)
124 DEFINE_CREATE_CONTROL(TREE_CONTROL)
125 DEFINE_CREATE_CONTROL(COMBOTREE_CONTROL)
126 DEFINE_CREATE_CONTROL(FILE_LIST_CONTROL)
127 DEFINE_CREATE_CONTROL(FILE_COMBO_CONTROL)
128 DEFINE_CREATE_CONTROL(PATH_TREE_CONTROL)
129 DEFINE_CREATE_CONTROL(TAB_CONTROL)
130 
131 
132 static int GetWBDialogControlStructSize(Atom aClass)
133 {
134  if(aClass == aICON_CONTROL ||
135  aClass == aIMAGE_CONTROL)
136  {
137  return sizeof(struct _WB_IMAGE_CONTROL_);
138  }
139 
140  if(aClass == aPUSHBUTTON_CONTROL ||
141  aClass == aDEFPUSHBUTTON_CONTROL ||
142  aClass == aCANCELBUTTON_CONTROL)
143  {
144  return sizeof(struct _WB_PUSHBUTTON_CONTROL_);
145  }
146 
147  if(aClass == aEDIT_CONTROL)
148  {
149  return sizeof(struct _WB_EDIT_CONTROL_);
150  }
151 
152  if(aClass == aCOMBO_CONTROL)
153  {
154  return sizeof(struct _WB_COMBO_CONTROL_);
155  }
156 
157  if(aClass == aLIST_CONTROL ||
158  aClass == aFILE_LIST_CONTROL)
159  {
160  return sizeof(struct _WB_LIST_CONTROL_);
161  }
162 
163  if(aClass == aTREE_CONTROL ||
164  aClass == aPATH_TREE_CONTROL)
165  {
166  return sizeof(struct _WB_TREE_CONTROL_);
167  }
168 
169  return sizeof(WBDialogControl);
170 }
171 
172 int DLGControlDefaultCallback(Window wID, XEvent *pEvent)
173 {
174 #ifndef NO_DEBUG
175  Display *pDisplay = WBGetWindowDisplay(wID);
176 #endif // NO_DEBUG
177  WBDialogControl *pDialogControl = DLGGetDialogControlStruct(wID);
178  int iRval;
179 
180  if(!pDialogControl)
181  {
182  WB_ERROR_PRINT("%s - no dialog control struct for window %d (%08xH)\n",
183  __FUNCTION__, (int)wID, (int)wID);
184  return 0;
185  }
186 
187  // TODO: message re-direction to children BEFORE 'pDLGCallback'
188 
189  switch(pEvent->type)
190  {
191  case ButtonPress:
193  "BUTTON PRESS - dialog control\n");
194 
195  // if this window does NOT have the focus, then I must switch focus
196  // also make sure parent window is 'moved forward' so I can view it
197 
198  // TODO: in cases where button presses need to be generically overridden
199  // I can do that here
200 
201  break;
202 
203  case ButtonRelease:
205  "BUTTON RELEASE - dialog control\n");
206 
207  // TODO: in cases where button presses need to be generically overridden
208  // I can do that here
209 
210  break;
211 
212  case MotionNotify:
214  "MOTION NOTIFY - dialog control\n");
215 
216  // TODO: in cases where mouse drags need to be generically overridden
217  // I can do that here
218 
219  break;
220 
221  case KeyPress:
222  case KeyRelease:
223 
225  "%s KEY PRESS/RELEASE - dialog control - check for hotkeys\n", __FUNCTION__);
226 
227  // TODO: in cases where key presses need to be generically overridden
228  // I can do that here
229 
230  iRval = DLGProcessHotKey(pDialogControl->pOwner, pEvent);
231 
232  if(iRval)
233  {
234  return iRval;
235  }
236 
237  break;
238 
239  case FocusIn:
240 
241 // WB_ERROR_PRINT("TEMPORARY: %s - FocusIn for dialog control ID %d window %d (%08xH)\n", __FUNCTION__,
242 // (int)(pDialogControl->pDlgControlEntry ? pDialogControl->pDlgControlEntry->iID : -1),
243 // (int)wID, (int)wID);
244 
246  "FocusIn - dialog control\n");
247 
248  DLGNotifyOwner(pDialogControl, aGOTFOCUS,
249  (long)(pDialogControl->pDlgControlEntry ? pDialogControl->pDlgControlEntry->iID : pDialogControl->wID),
250  (unsigned long)pDialogControl->pDlgControlEntry, 0, 0, 0);
251 
252  // now update myself
253  WBInvalidateGeom(wID, NULL, 1);
254 
255  break;
256 
257  case FocusOut:
258 
260  "FocusOut - dialog control\n");
261 
262  DLGNotifyOwner(pDialogControl, aLOSTFOCUS,
263  (long)(pDialogControl->pDlgControlEntry ? pDialogControl->pDlgControlEntry->iID : pDialogControl->wID),
264  (unsigned long)pDialogControl->pDlgControlEntry, 0, 0, 0);
265 
266  // now update myself
267  WBInvalidateGeom(wID, NULL, 1);
268 
269  break;
270 
271  }
272 
273 
274  if(pDialogControl->pDLGControlCallback)
275  {
276  // for most messages, if I handle it here, I don't do further processing
277 
278  int iRval = pDialogControl->pDLGControlCallback(wID, pEvent);
279 
280  if(iRval)
281  {
282  // check message types that I do *NOT* want to 'bail out' for
283 
284  switch(pEvent->type)
285  {
286  case DestroyNotify:
288  "%s - DestroyNotify and user callback returned %d\n", __FUNCTION__, iRval);
289 
290  break;;
291 
292  case Expose:
293  return iRval; // 'expose' event already handled
294 
295  default:
296  return iRval;
297  }
298  }
299  }
300 
301  // un-handled messages
302 
303  if(pEvent->type == ClientMessage &&
304  pEvent->xclient.window == wID)
305  {
306  if(pEvent->xclient.message_type == aWM_PROTOCOLS && pEvent->xclient.window == wID)
307  {
309  "CLIENT MESSAGE - dialog control - WM_PROTOCOLS\n");
310 
311  return 0; // "not handled"
312  }
313 // else if(pEvent->xclient.message_type == aCONTROL_NOTIFY) // 'control notification' messages
314 // {
315 // }
316 // else if(pEvent->xclient.message_type == aDLG_FOCUS) // dialog focus change message
317 // {
318 // }
319 
320 #ifndef NO_DEBUG
321  {
322  char *p1 = WBGetAtomName(pDisplay, pEvent->xclient.message_type);
324  "CLIENT MESSAGE - dialog control (NOT HANDLED) - %s\n", p1);
325  if(p1)
326  {
327  WBFree(p1);
328  }
329  }
330 #endif // NO_DEBUG
331  return 1;
332  }
333 
334 // // special handling for 'destroy'
335 // if(pEvent->type == DestroyNotify &&
336 // pEvent->xdestroywindow.window == wID)
337 // {
338 // WBUnregisterWindowCallback(wID); // force this to happen, regardless
339 
349 // return 1;
350 // }
351 
352  return 0;
353 }
354 
355 void DLGRegisterControlCallback(WBDialogControl *pDialogControl, const char *szClassName, WBWinEvent pCallback)
356 {
357  pDialogControl->pDLGControlCallback = pCallback;
358  WBRegisterWindowCallback(pDialogControl->wID, DLGControlDefaultCallback);
359  WBSetWindowClassName(pDialogControl->wID, szClassName);
360 }
361 
362 
364  WBDialogEntry *pDialogEntry, // pointer to the dialog entry I'll be using
365  int iX, int iY, int iWidth, int iHeight,
366  const char *szTitle, const char *szPropertyList)
367 {
368 WBDialogControl *pRval;
369 int i1;
370 
371 #define DO_CREATE_CONTROL(X) if(aClass == a##X) \
372  { return do_create_##X(pRval, iX, iY, iWidth, iHeight, #X, szTitle); }
373 
374  pRval = WBAlloc(i1 = GetWBDialogControlStructSize(aClass));
375  if(!pRval)
376  {
377  return NULL;
378  }
379 
380  memset(pRval, 0, i1);
381 
382  iX = DLGPixelWidth(pOwner, iX);
383  iY = DLGPixelHeight(pOwner, iY);
384  iWidth = DLGPixelWidth(pOwner, iWidth);
385  iHeight = DLGPixelHeight(pOwner, iHeight);
386 
387  pRval->ulTag = DIALOG_CONTROL_TAG;
388  pRval->wID = -1; // initial value
389  pRval->aClass = aClass;
390  pRval->pOwner = pOwner;
391  pRval->pDlgControlEntry = pDialogEntry;
392  pRval->ulFlags = 0;
393  pRval->pPropList = NULL; // initial value
394  pRval->cbStructSize = i1; // keep track of how large the structure _REALLY_ is
395 
396  if(WBDialogControlSetPropList(pRval, szPropertyList))
397  {
398  WB_ERROR_PRINT("%s - Error return from WBDialogSetPropList\n", __FUNCTION__);
399  WBFree(pRval);
400  return NULL;
401  }
402 
403  DO_CREATE_CONTROL(FRAME_CONTROL); // NOTE: if requested type matches FRAME_CONTROL it returns
404  DO_CREATE_CONTROL(TEXT_CONTROL);
405  DO_CREATE_CONTROL(ICON_CONTROL);
406  DO_CREATE_CONTROL(IMAGE_CONTROL);
407  DO_CREATE_CONTROL(EDIT_CONTROL);
408  DO_CREATE_CONTROL(PUSHBUTTON_CONTROL);
409  DO_CREATE_CONTROL(DEFPUSHBUTTON_CONTROL);
410  DO_CREATE_CONTROL(CANCELBUTTON_CONTROL);
411  DO_CREATE_CONTROL(RADIOBUTTON_CONTROL);
412  DO_CREATE_CONTROL(FIRSTRADIOBUTTON_CONTROL);
413  DO_CREATE_CONTROL(CHECKBUTTON_CONTROL);
414  DO_CREATE_CONTROL(TRISTATEBUTTON_CONTROL);
415  DO_CREATE_CONTROL(HSCROLL_CONTROL);
416  DO_CREATE_CONTROL(VSCROLL_CONTROL);
417  DO_CREATE_CONTROL(SLIDER_CONTROL);
418  DO_CREATE_CONTROL(KNOB_CONTROL);
419  DO_CREATE_CONTROL(LIST_CONTROL);
420  DO_CREATE_CONTROL(COMBO_CONTROL);
421  DO_CREATE_CONTROL(TREE_CONTROL);
422  DO_CREATE_CONTROL(COMBOTREE_CONTROL);
423  DO_CREATE_CONTROL(FILE_LIST_CONTROL);
424  DO_CREATE_CONTROL(FILE_COMBO_CONTROL);
425  DO_CREATE_CONTROL(PATH_TREE_CONTROL);
426  DO_CREATE_CONTROL(TAB_CONTROL);
427 
428 
429  // TODO: user-registered controls
430 
431 
432  // if I get here the control was unrecognized (would have returned if any of the above matched)
433 
434  WB_ERROR_PRINT("%s - Unrecognized control in WBDialogControlCreate\n", __FUNCTION__);
435 
436  // unrecognized control
437  if(pRval->pPropList)
438  {
440  pRval->pPropList = NULL; // as a matter of course
441  }
442 
443  WBFree(pRval);
444 
445  return NULL;
446 }
447 
448 
449 #define LOAD_COLOR0(X,Y) if(CHGetResourceString(WBGetDefaultDisplay(), X, Y, sizeof(Y)) > 0) { }
450 #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); }
451 
452 static void alloc_control_colors(WBDialogControl *pDialogControl,
453  const char *szFGName, const char *szBGName,
454  const char *szHFGName, const char *szHBGName,
455  const char *szAFGName, const char *szABGName,
456  const char *szBDName, int bUseStaticColors)
457 {
458  static const char *szBorder2="#FFFFFF", *szBorder2W="#C8C6C0", *szBorder3="#9C9A94"; // for 3D borders
459  static const char *szHFGDef="#E0E0E0", *szHBGDef="#0000A0"; // highlight FG/BG
460  static const char *szAFGDef="#000000", *szABGDef="white"; // active FG/BG
461  char szFG[18], szBG[18], szBD[18], szHFG[18], szHBG[18], szAFG[18], szABG[18];
462  Colormap colormap = DefaultColormap(WBGetDefaultDisplay(), DefaultScreen(WBGetDefaultDisplay()));
463 
464 
465  // TODO: add some sanity to this, maybe an API for loading colors? *MOST* of this is now obsolete
466  // and XSETTINGS uses completely different naming.
467 
468  LOAD_COLOR0(szFGName,szFG) else LOAD_COLOR0("*Dialog.foreground",szFG) else LOAD_COLOR0("*Form.foreground", szFG)
469  else LOAD_COLOR0("*WmDialogShell.foreground",szFG) else LOAD_COLOR0("*WmForm.foreground", szFG)
470  else LOAD_COLOR("*foreground", szFG, "#000000");
471  if(bUseStaticColors)
472  {
473  LOAD_COLOR0(szBGName,szBG) else LOAD_COLOR0("*Dialog.background",szBG) else LOAD_COLOR0("*Form.background", szBG)
474  else LOAD_COLOR0("*WmDialogShell.background",szBG)
475  else LOAD_COLOR("*WmForm.background", szBG, "#dcdad5"); // default for gnome is dcdad5
476  }
477  else
478  {
479  LOAD_COLOR0(szBGName,szBG) else LOAD_COLOR0("*Window.background",szBG)
480  else LOAD_COLOR("*background", szBG, "white");
481  }
482  LOAD_COLOR(szHFGName,szHFG,szHFGDef);
483  LOAD_COLOR(szHBGName,szHBG,szHBGDef);
484  if(bUseStaticColors)
485  {
486  LOAD_COLOR(szAFGName,szAFG,szAFGDef);
487  LOAD_COLOR(szABGName,szABG,szABGDef);
488  }
489  else
490  {
491  LOAD_COLOR(szAFGName,szAFG,szFG);
492  LOAD_COLOR(szABGName,szABG,szBG);
493  }
494  LOAD_COLOR0(szBDName,szBD) else LOAD_COLOR0("*Dialog.border",szBD) else LOAD_COLOR0("*Form.border", szBD)
495  else LOAD_COLOR0("*WmDialogShell.border",szBD) else LOAD_COLOR0("*WmForm.border", szBD)
496  else LOAD_COLOR0("*borderColor", szBD)
497  else LOAD_COLOR("*border", szBD, "black"); // default for gnome
498 
499 
500 // // TEMPORARILY DUMP COLOR MAPPING
501 // WB_ERROR_PRINT("TEMPORARY: %s - control colors:\n", __FUNCTION__);
502 // WB_ERROR_PRINT(" %s=%s default=%s\n", szFGName, szFG, "#000000");
503 // WB_ERROR_PRINT(" %s=%s default=%s\n", szBGName, szBG, "#dcdad5 or 'white'");
504 // WB_ERROR_PRINT(" %s=%s default=%s\n", szHFGName, szHFG, szHFGDef);
505 // WB_ERROR_PRINT(" %s=%s default=%s\n", szHBGName, szHBG, szHBGDef);
506 // WB_ERROR_PRINT(" %s=%s default=%s\n", szAFGName, szAFG, szAFGDef);
507 // WB_ERROR_PRINT(" %s=%s default=%s\n", szABGName, szABG, szABGDef);
508 // WB_ERROR_PRINT(" %s=%s default=%s\n", szBDName, szBD, "black");
509 
510 
511  XParseColor(WBGetDefaultDisplay(), colormap, szFG, &(pDialogControl->clrFG));
512  XAllocColor(WBGetDefaultDisplay(), colormap, &(pDialogControl->clrFG));
513  XParseColor(WBGetDefaultDisplay(), colormap, szBG, &(pDialogControl->clrBG));
514  XAllocColor(WBGetDefaultDisplay(), colormap, &(pDialogControl->clrBG));
515  XParseColor(WBGetDefaultDisplay(), colormap, szAFG, &(pDialogControl->clrAFG));
516  XAllocColor(WBGetDefaultDisplay(), colormap, &(pDialogControl->clrAFG));
517  XParseColor(WBGetDefaultDisplay(), colormap, szABG, &(pDialogControl->clrABG));
518  XAllocColor(WBGetDefaultDisplay(), colormap, &(pDialogControl->clrABG));
519  XParseColor(WBGetDefaultDisplay(), colormap, szHFG, &(pDialogControl->clrHFG));
520  XAllocColor(WBGetDefaultDisplay(), colormap, &(pDialogControl->clrHFG));
521  XParseColor(WBGetDefaultDisplay(), colormap, szHBG, &(pDialogControl->clrHBG));
522  XAllocColor(WBGetDefaultDisplay(), colormap, &(pDialogControl->clrHBG));
523  XParseColor(WBGetDefaultDisplay(), colormap, szBD, &(pDialogControl->clrBD));
524 
525  // 3D border colors for now these are hard-coded - later derive them from FG and BG colors
526  if(pDialogControl->clrBG.red >= 60000 && pDialogControl->clrBG.green >= 60000 &&
527  pDialogControl->clrBG.blue >= 60000) // note see man page on XColor, values 0 through 65535 for RGB
528  {
529  XParseColor(WBGetDefaultDisplay(), colormap, szBorder2W, &(pDialogControl->clrBD2));
530  }
531  else
532  {
533  XParseColor(WBGetDefaultDisplay(), colormap, szBorder2, &(pDialogControl->clrBD2));
534  }
535  XAllocColor(WBGetDefaultDisplay(), colormap, &(pDialogControl->clrBD2));
536  XParseColor(WBGetDefaultDisplay(), colormap, szBorder3, &(pDialogControl->clrBD3));
537  XAllocColor(WBGetDefaultDisplay(), colormap, &(pDialogControl->clrBD3));
538 }
539 
540 static Display *dialog_control_get_display(WBDialogControl *pDialogControl)
541 {
542 Display *pRval = NULL;
543 
544  if(pDialogControl && pDialogControl->pOwner)
545  {
546  pRval = WBGetWindowDisplay(pDialogControl->pOwner->wID);
547  }
548 
549  if(!pRval)
550  {
551  pRval = WBGetDefaultDisplay();
552  }
553 
554  return pRval;
555 }
556 
557 static Window standard_do_create_control(WBDialogControl *pDialogControl,
558  int iX, int iY, int iWidth, int iHeight, int iBorderWidth,
559  const char *szClassName, const char *szTitle, WBWinEvent pCallback)
560 {
561  XSetWindowAttributes xswa; /* Temporary Set Window Attribute struct */
562  Display *pDisplay = dialog_control_get_display(pDialogControl);
563 
564  bzero(&xswa, sizeof(xswa));
565 
566  xswa.border_pixel = pDialogControl->clrBD.pixel;
567  xswa.background_pixel = pDialogControl->clrBG.pixel;
568  xswa.colormap = DefaultColormap(pDisplay, DefaultScreen(pDisplay));
569  xswa.bit_gravity = CenterGravity;
570 
571  pDialogControl->wID = XCreateWindow(pDisplay, pDialogControl->pOwner ? pDialogControl->pOwner->wID : 0,
572  iX, iY, iWidth, iHeight,
573  iBorderWidth,
574  DefaultDepth(pDisplay, DefaultScreen(pDisplay)),
575  InputOutput,
576  DefaultVisual(pDisplay, DefaultScreen(pDisplay)),
577  CWBorderPixel | CWBackPixel | CWColormap | CWBitGravity,
578  &xswa);
579  if(pDialogControl->wID <= 0 /* == None */)
580  {
581  return None;
582  }
583 
584  // TODO: standardize this a little better using WBCreateWindow
585 
586  DLGRegisterControlCallback(pDialogControl, szClassName, pCallback); // adds it to the 'WB' window matrix
587  WBSetParentWindow(pDialogControl->wID, pDialogControl->pOwner ? pDialogControl->pOwner->wID : None);
588 
589  // immediately identify this window using window data
590  WBSetWindowData(pDialogControl->wID, 0, (void *)pDialogControl);
591 
593  "%s - TEMPORARY: creating %s control with caption \"%s\"\n",
594  __FUNCTION__, szClassName, szTitle);
595 
596  pDialogControl->pCaption = WBCopyString(szTitle);
597 // XSetStandardProperties(pDisplay, pDialogControl->wID, szTitle, szTitle, None,
598 // NULL, 0, NULL); // argv, argc, &xsh);
599  // this has been superseded by XSetWMProperties() and should not be used any more
600 
601  WBCreateWindowDefaultGC(pDialogControl->wID, pDialogControl->clrFG.pixel, pDialogControl->clrBG.pixel);
602 
603  return pDialogControl->wID;
604 }
605 
606 // IMPLEMENT_CREATE_CONTROL
607 // static WBDialogControl * do_create_##X(WBDialogControl *pDialogControl,
608 // int iX, int iY, int iWidth, int iHeight,
609 // const char *szClassName, const char *szTitle);
610 
611 static int static_callback(Window wID, XEvent *pEvent);
612 
613 IMPLEMENT_CREATE_CONTROL(FRAME_CONTROL)
614 {
615 BEGIN_CREATE_CONTROL(FRAME_CONTROL);
616 
617  Display *pDisplay = dialog_control_get_display(pDialogControl);
618  int iBorderWidth = 1;
619 
620  aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
621 
622  pDialogControl->ulFlags &= ~STATIC_TYPEMASK;
623  pDialogControl->ulFlags |= STATIC_Frame;
624 
625  // if 3D border
626  {
627  iBorderWidth = 0;
628  pDialogControl->ulFlags |= STATIC_3DBorder;
629  }
630 
631  // color information for border, foreground, background
632 
633  alloc_control_colors(pDialogControl, "*Dialog.foreground", "*Dialog.background",
634  "*Dialog.foreground", "*Dialog.background",
635  "*Dialog.foreground", "*Dialog.background",
636  "*Dialog.border", 1);
637 
638  if(standard_do_create_control(pDialogControl, iX, iY, iWidth, iHeight, iBorderWidth,
639  szClassName, szTitle, static_callback)
640  == None)
641  {
642  if(pDialogControl->pPropList)
643  {
644  DLGCDestroyProperties(pDialogControl->pPropList);
645  pDialogControl->pPropList = NULL; // as a matter of course
646  }
647 
648  WBFree(pDialogControl);
649  return NULL;
650  }
651 
652  // now allow certain kinds of input messages (I should be able to handle messages at this point)
653  XSelectInput(pDisplay, pDialogControl->wID, WB_STANDARD_INPUT_MASK);
654 
655 
656  if(pDialogControl->pDlgControlEntry &&
657  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE))
658  {
659  XMapWindow(pDisplay, pDialogControl->wID);
660  }
661 
662  return pDialogControl;
663 }
664 
665 
666 IMPLEMENT_CREATE_CONTROL(TEXT_CONTROL)
667 {
668 BEGIN_CREATE_CONTROL(TEXT_CONTROL);
669 
670  Display *pDisplay = dialog_control_get_display(pDialogControl);
671  int iBorderWidth = 1;
672 
673  aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
674 
675  pDialogControl->ulFlags &= ~STATIC_TYPEMASK;
676  pDialogControl->ulFlags |= STATIC_Text;
677 
678  // if 3D border
679  {
680  iBorderWidth = 0;
681  pDialogControl->ulFlags |= STATIC_3DBorder;
682  }
683 
684 // // focus
685 // pDialogControl->pDlgControlEntry->iFlags |= WBDialogEntry_CAN_HAVE_FOCUS;
686 
687  // color information for border, foreground, background
688 
689  alloc_control_colors(pDialogControl, "*Label.foreground", "*Label.background",
690  "*Label.highlightColor", "*Label.highlightBackground",
691  "*Labelframe.highlightColor", "*Labelframe.background", // slightly different
692  "*Labelframe.foreground", 1);
693 
694  if(standard_do_create_control(pDialogControl, iX, iY, iWidth, iHeight, iBorderWidth,
695  szClassName, szTitle, static_callback)
696  == None)
697  {
698  if(pDialogControl->pPropList)
699  {
700  DLGCDestroyProperties(pDialogControl->pPropList);
701  pDialogControl->pPropList = NULL; // as a matter of course
702  }
703 
704  WBFree(pDialogControl);
705  return NULL;
706  }
707 
708  // now allow certain kinds of input messages (I should be able to handle messages at this point)
709  XSelectInput(pDisplay, pDialogControl->wID, WB_STANDARD_INPUT_MASK);
710 
711 
712  if(pDialogControl->pDlgControlEntry &&
713  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE))
714  {
715  XMapWindow(pDisplay, pDialogControl->wID);
716  }
717 
718  return pDialogControl;
719 }
720 
721 
722 IMPLEMENT_CREATE_CONTROL(ICON_CONTROL)
723 {
724 BEGIN_CREATE_CONTROL(ICON_CONTROL);
725 
726  Display *pDisplay = dialog_control_get_display(pDialogControl);
727  int iBorderWidth = 1;
728 
729  aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
730 
731  pDialogControl->ulFlags &= ~STATIC_TYPEMASK;
732  pDialogControl->ulFlags |= STATIC_Icon;
733 
734  // if 3D border
735  {
736  iBorderWidth = 0;
737  pDialogControl->ulFlags |= STATIC_3DBorder;
738  }
739 
740 
741  // color information for border, foreground, background
742 
743  alloc_control_colors(pDialogControl, "*Dialog.foreground", "*Dialog.background",
744  "*Dialog.foreground", "*Dialog.background",
745  "*Dialog.foreground", "*Dialog.background",
746  "*Dialog.border", 1);
747 
748  if(standard_do_create_control(pDialogControl, iX, iY, iWidth, iHeight, iBorderWidth,
749  szClassName, szTitle, static_callback)
750  == None)
751  {
752  if(pDialogControl->pPropList)
753  {
754  DLGCDestroyProperties(pDialogControl->pPropList);
755  pDialogControl->pPropList = NULL; // as a matter of course
756  }
757 
758  WBFree(pDialogControl);
759  return NULL;
760  }
761 
762  // now allow certain kinds of input messages (I should be able to handle messages at this point)
763  XSelectInput(pDisplay, pDialogControl->wID, WB_STANDARD_INPUT_MASK);
764 
765 
766  // TODO: scan properties for 'ICON' property, load icon, assign to
767  // ((struct _WB_IMAGE_CONTROL_ *)pDialogControl)->pixmap
768 
769  if(pDialogControl->pDlgControlEntry &&
770  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE))
771  {
772  XMapWindow(pDisplay, pDialogControl->wID);
773  }
774 
775  return pDialogControl;
776 }
777 
778 
779 IMPLEMENT_CREATE_CONTROL(IMAGE_CONTROL)
780 {
781 BEGIN_CREATE_CONTROL(IMAGE_CONTROL);
782 
783  Display *pDisplay = dialog_control_get_display(pDialogControl);
784  int iBorderWidth = 1;
785 
786  aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
787 
788  pDialogControl->ulFlags &= ~STATIC_TYPEMASK;
789  pDialogControl->ulFlags |= STATIC_Image;
790 
791  // if 3D border
792  {
793  iBorderWidth = 0;
794  pDialogControl->ulFlags |= STATIC_3DBorder;
795  }
796 
797 
798  // color information for border, foreground, background
799 
800  alloc_control_colors(pDialogControl, "*Dialog.foreground", "*Dialog.background",
801  "*Dialog.foreground", "*Dialog.background",
802  "*Dialog.foreground", "*Dialog.background",
803  "*Dialog.border", 1);
804 
805  if(standard_do_create_control(pDialogControl, iX, iY, iWidth, iHeight, iBorderWidth,
806  szClassName, szTitle, static_callback)
807  == None)
808  {
809  if(pDialogControl->pPropList)
810  {
811  DLGCDestroyProperties(pDialogControl->pPropList);
812  pDialogControl->pPropList = NULL; // as a matter of course
813  }
814 
815  WBFree(pDialogControl);
816  return NULL;
817  }
818 
819  // now allow certain kinds of input messages (I should be able to handle messages at this point)
820  XSelectInput(pDisplay, pDialogControl->wID, WB_STANDARD_INPUT_MASK);
821 
822 
823  if(pDialogControl->pDlgControlEntry &&
824  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE))
825  {
826  XMapWindow(pDisplay, pDialogControl->wID);
827  }
828 
829  return pDialogControl;
830 }
831 
832 
833 static int edit_callback(Window wID, XEvent *pEvent);
834 
835 IMPLEMENT_CREATE_CONTROL(EDIT_CONTROL)
836 {
837 BEGIN_CREATE_CONTROL(EDIT_CONTROL);
838 
839  WBEditControl *pPrivate = (WBEditControl *)pDialogControl;
840  Display *pDisplay = dialog_control_get_display(pDialogControl);
841 
842 // pDialogControl->ulFlags &= ~EDIT_TYPEMASK;
843 // pDialogControl->ulFlags |= EDIT_Frame;
844 
845 
846  // focus
848 
849  // private member initialization
850 // pPrivate->pState = NULL; // nothing, yet (until text is assigned)
851  WBInitializeInPlaceTextObject(&(pPrivate->xTextObject), None);
852 
853  // NOTE: it's assumed that vtable will NOT be NULL after calling that...
854 
855 
856  // TODO: determine single/multi line behavior. for now, SINGLE only
857  pPrivate->xTextObject.vtable->set_linefeed(&(pPrivate->xTextObject), LineFeed_NONE); // single-line
858 
859 
860  // color information for border, foreground, background
861 
862  alloc_control_colors(pDialogControl, "*Text.foreground", "*Text.background",
863  "*Text.selectForeground", "*Text.selectBackground",
864  "*Text.activeForeground", "*Text.activeBackground",
865  "*Text.border", 0);
866 
867 // WB_ERROR_PRINT("TEMPORARY: EDIT colors FG=%lxH BG=%lxH HF=%lx HB=%lx AF=%lx AB=%lx\n",
868 // pDialogControl->clrFG.pixel,pDialogControl->clrBG.pixel,
869 // pDialogControl->clrHFG.pixel,pDialogControl->clrHBG.pixel,
870 // pDialogControl->clrAFG.pixel,pDialogControl->clrABG.pixel);
871 
872  // assign highlight colors to text object (other colors obtained from the GC)
873  pPrivate->xTextObject.vtable->highlight_colors(&(pPrivate->xTextObject), pDialogControl->clrHFG, pDialogControl->clrHBG);
874 
875  if(standard_do_create_control(pDialogControl, iX, iY, iWidth, iHeight, 1, // border width is 1
876  szClassName, szTitle, edit_callback)
877  == None)
878  {
879  if(pDialogControl->pPropList)
880  {
881  DLGCDestroyProperties(pDialogControl->pPropList);
882  pDialogControl->pPropList = NULL; // as a matter of course
883  }
884 
885  // destroy any allocated 'text object' stuff thus far
887 
888  WBFree(pDialogControl);
889  return NULL;
890  }
891 
892  pPrivate->xTextObject.wIDOwner = pDialogControl->wID; // TODO: make assigning this an API function?
893 
894  // this one needs a special cursor
895  WBSetWindowDefaultCursor(pDialogControl->wID, XC_xterm);//tcross);
896 
897  // assign colors to TEXT OBJECT
898 
899  // now allow certain kinds of input messages (I should be able to handle messages at this point)
900  XSelectInput(pDisplay, pDialogControl->wID,
902 
903  if(pDialogControl->pDlgControlEntry &&
904  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE))
905  {
906  XMapWindow(pDisplay, pDialogControl->wID);
907  }
908 
909  // create timer for cursor blink
910  CreateTimer(pDisplay, pDialogControl->wID, 333333, 1, 1); // TODO: use #define for timer ID
911 
912  return pDialogControl;
913 }
914 
915 
916 static int button_callback(Window wID, XEvent *pEvent);
917 
918 IMPLEMENT_CREATE_CONTROL(PUSHBUTTON_CONTROL)
919 {
920 BEGIN_CREATE_CONTROL(PUSHBUTTON_CONTROL);
921 
922 XFontSet fsBold;
923 
924 
925  Display *pDisplay = dialog_control_get_display(pDialogControl);
926 
927 // aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
928 
929  pDialogControl->ulFlags &= ~BUTTON_TYPEMASK;
930  pDialogControl->ulFlags |= BUTTON_PushButton;
931 
932 
933  // focus
935  | WBDialogEntry_PUSHBUTTON; // override 'default' when I have focus
936 
937  // color information for border, foreground, background
938 
939  alloc_control_colors(pDialogControl, "*Button.foreground", "*Button.background",
940  "*Button.highlightForeground", "*Button.highlightBackground",
941  "*Button.activeForeground", "*Button.activeBackground",
942  "*Button.border", 1);
943 
944  if(standard_do_create_control(pDialogControl, iX, iY, iWidth, iHeight, 0, // border width is zero
945  szClassName, szTitle, button_callback)
946  == None)
947  {
948  if(pDialogControl->pPropList)
949  {
950  DLGCDestroyProperties(pDialogControl->pPropList);
951  pDialogControl->pPropList = NULL; // as a matter of course
952  }
953 
954  WBFree(pDialogControl);
955  return NULL;
956  }
957 
958  // assign BOLD font to this one... (TODO: query owner dialog box? get fonts from that?)
959 
960  fsBold = WBCopyModifyFontSet(pDisplay, WBGetDefaultFontSet(pDisplay),
961  0, WBFontFlag_WT_BOLD); // BOLD version
962 
963  if(fsBold != None)
964  {
965  WBSetWindowFontSet(pDialogControl->wID, fsBold);
966  }
967 
968  // now allow certain kinds of input messages (I should be able to handle messages at this point)
969  XSelectInput(pDisplay, pDialogControl->wID,
971 
972 
973  if(pDialogControl->pDlgControlEntry &&
974  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE))
975  {
976  XMapWindow(pDisplay, pDialogControl->wID);
977  }
978 
979  return pDialogControl;
980 }
981 
982 
983 IMPLEMENT_CREATE_CONTROL(DEFPUSHBUTTON_CONTROL)
984 {
985 BEGIN_CREATE_CONTROL(DEFPUSHBUTTON_CONTROL);
986 
987 XFontSet fsBold;
988 
989 
990  Display *pDisplay = dialog_control_get_display(pDialogControl);
991 
992 // aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
993 
994  pDialogControl->ulFlags &= ~BUTTON_TYPEMASK;
995  pDialogControl->ulFlags |= BUTTON_DefPushButton;
996 
997 
998  // focus
1000  | WBDialogEntry_PUSHBUTTON // override 'default' when I have focus (trivial here)
1002 
1003  // color information for border, foreground, background
1004 
1005  alloc_control_colors(pDialogControl, "*Button.foreground", "*Button.background",
1006  "*Button.highlightForeground", "*Button.highlightBackground",
1007  "*Button.activeForeground", "*Button.activeBackground",
1008  "*Button.border", 1);
1009 
1010  if(standard_do_create_control(pDialogControl, iX, iY, iWidth, iHeight, 0, // border width is zero
1011  szClassName, szTitle, button_callback)
1012  == None)
1013  {
1014  if(pDialogControl->pPropList)
1015  {
1016  DLGCDestroyProperties(pDialogControl->pPropList);
1017  pDialogControl->pPropList = NULL; // as a matter of course
1018  }
1019 
1020  WBFree(pDialogControl);
1021  return NULL;
1022  }
1023 
1024  // assign BOLD font to this one... (TODO: query owner dialog box? get fonts from that?)
1025 
1026  fsBold = WBCopyModifyFontSet(pDisplay, WBGetDefaultFontSet(pDisplay),
1027  0, WBFontFlag_WT_BOLD); // BOLD version
1028 
1029  if(fsBold != None)
1030  {
1031  WBSetWindowFontSet(pDialogControl->wID, fsBold);
1032  }
1033 
1034  // now allow certain kinds of input messages (I should be able to handle messages at this point)
1035  XSelectInput(pDisplay, pDialogControl->wID,
1037 
1038 
1039  if(pDialogControl->pDlgControlEntry &&
1040  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE))
1041  {
1042  XMapWindow(pDisplay, pDialogControl->wID);
1043  }
1044 
1045  return pDialogControl;
1046 }
1047 
1048 
1049 IMPLEMENT_CREATE_CONTROL(CANCELBUTTON_CONTROL)
1050 {
1051 BEGIN_CREATE_CONTROL(CANCELBUTTON_CONTROL);
1052 
1053 XFontSet fsBold;
1054 
1055 
1056  Display *pDisplay = dialog_control_get_display(pDialogControl);
1057 
1058 // aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
1059 
1060  pDialogControl->ulFlags &= ~BUTTON_TYPEMASK;
1061  pDialogControl->ulFlags |= BUTTON_CancelButton;
1062 
1063 
1064  // focus
1066  | WBDialogEntry_PUSHBUTTON; // override 'default' when I have focus
1067 
1068  // color information for border, foreground, background
1069 
1070  alloc_control_colors(pDialogControl, "*Button.foreground", "*Button.background",
1071  "*Button.highlightForeground", "*Button.highlightBackground",
1072  "*Button.activeForeground", "*Button.activeBackground",
1073  "*Button.border", 1);
1074 
1075  if(standard_do_create_control(pDialogControl, iX, iY, iWidth, iHeight, 0, // border width is zero
1076  szClassName, szTitle, button_callback)
1077  == None)
1078  {
1079  if(pDialogControl->pPropList)
1080  {
1081  DLGCDestroyProperties(pDialogControl->pPropList);
1082  pDialogControl->pPropList = NULL; // as a matter of course
1083  }
1084 
1085  WBFree(pDialogControl);
1086  return NULL;
1087  }
1088 
1089  // assign BOLD font to this one... (TODO: query owner dialog box? get fonts from that?)
1090 
1091  fsBold = WBCopyModifyFontSet(pDisplay, WBGetDefaultFontSet(pDisplay),
1092  0, WBFontFlag_WT_BOLD); // BOLD version
1093 
1094  if(fsBold != None)
1095  {
1096  WBSetWindowFontSet(pDialogControl->wID, fsBold);
1097  }
1098 
1099  // now allow certain kinds of input messages (I should be able to handle messages at this point)
1100  XSelectInput(pDisplay, pDialogControl->wID,
1102 
1103 
1104  if(pDialogControl->pDlgControlEntry &&
1105  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE))
1106  {
1107  XMapWindow(pDisplay, pDialogControl->wID);
1108  }
1109 
1110  return pDialogControl;
1111 }
1112 
1113 
1114 IMPLEMENT_CREATE_CONTROL(RADIOBUTTON_CONTROL)
1115 {
1116 BEGIN_CREATE_CONTROL(RADIOBUTTON_CONTROL);
1117 
1118  Display *pDisplay = dialog_control_get_display(pDialogControl);
1119 
1120 // aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
1121 
1122  pDialogControl->ulFlags &= ~BUTTON_TYPEMASK;
1123  pDialogControl->ulFlags |= BUTTON_RadioButton;
1124 
1125 
1126  // focus
1128 
1129  // color information for border, foreground, background
1130 
1131  alloc_control_colors(pDialogControl, "*Radiobutton.foreground", "*Radiobutton.background",
1132  "*Radiobutton.highlightForeground", "*Radiobutton.highlightBackground",
1133  "*Radiobutton.activeForeground", "*RadioButton.activeBackground",
1134  "*Radiobutton.border", 1);
1135 
1136  if(standard_do_create_control(pDialogControl, iX, iY, iWidth, iHeight, 0, // border width is zero
1137  szClassName, szTitle, button_callback)
1138  == None)
1139  {
1140  if(pDialogControl->pPropList)
1141  {
1142  DLGCDestroyProperties(pDialogControl->pPropList);
1143  pDialogControl->pPropList = NULL; // as a matter of course
1144  }
1145 
1146  WBFree(pDialogControl);
1147  return NULL;
1148  }
1149 
1150  // now allow certain kinds of input messages (I should be able to handle messages at this point)
1151  XSelectInput(pDisplay, pDialogControl->wID,
1153 
1154 
1155  if(pDialogControl->pDlgControlEntry &&
1156  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE))
1157  {
1158  XMapWindow(pDisplay, pDialogControl->wID);
1159  }
1160 
1161  return pDialogControl;
1162 }
1163 
1164 
1165 IMPLEMENT_CREATE_CONTROL(FIRSTRADIOBUTTON_CONTROL)
1166 {
1167 BEGIN_CREATE_CONTROL(FIRSTRADIOBUTTON_CONTROL);
1168 
1169  Display *pDisplay = dialog_control_get_display(pDialogControl);
1170 
1171 // aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
1172 
1173  pDialogControl->ulFlags &= ~BUTTON_TYPEMASK;
1174  pDialogControl->ulFlags |= BUTTON_FirstRadioButton;
1175 
1176 
1177  // focus
1179 
1180  // color information for border, foreground, background
1181 
1182  alloc_control_colors(pDialogControl, "*Radiobutton.foreground", "*Radiobutton.background",
1183  "*Radiobutton.highlightForeground", "*Radiobutton.highlightBackground",
1184  "*Radiobutton.activeForeground", "*RadioButton.activeBackground",
1185  "*Radiobutton.border", 1);
1186 
1187  if(standard_do_create_control(pDialogControl, iX, iY, iWidth, iHeight, 0, // border width is zero
1188  szClassName, szTitle, button_callback)
1189  == None)
1190  {
1191  if(pDialogControl->pPropList)
1192  {
1193  DLGCDestroyProperties(pDialogControl->pPropList);
1194  pDialogControl->pPropList = NULL; // as a matter of course
1195  }
1196 
1197  WBFree(pDialogControl);
1198  return NULL;
1199  }
1200 
1201  // now allow certain kinds of input messages (I should be able to handle messages at this point)
1202  XSelectInput(pDisplay, pDialogControl->wID,
1204 
1205 
1206  if(pDialogControl->pDlgControlEntry &&
1207  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE))
1208  {
1209  XMapWindow(pDisplay, pDialogControl->wID);
1210  }
1211 
1212  return pDialogControl;
1213 }
1214 
1215 
1216 IMPLEMENT_CREATE_CONTROL(CHECKBUTTON_CONTROL)
1217 {
1218 BEGIN_CREATE_CONTROL(CHECKBUTTON_CONTROL);
1219 
1220 
1221  Display *pDisplay = dialog_control_get_display(pDialogControl);
1222 
1223 // aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
1224 
1225  pDialogControl->ulFlags &= ~BUTTON_TYPEMASK;
1226  pDialogControl->ulFlags |= BUTTON_CheckButton;
1227 
1228 
1229  // focus
1231 
1232  // color information for border, foreground, background
1233 
1234  alloc_control_colors(pDialogControl, "*Checkbutton.foreground", "*Checkbutton.background",
1235  "*Checkbutton.highlightForeground", "*Checkbutton.highlightBackground",
1236  "*Checkbutton.activeForeground", "*CheckButton.activeBackground",
1237  "*Checkbutton.border", 1);
1238 
1239  if(standard_do_create_control(pDialogControl, iX, iY, iWidth, iHeight, 0, // border width is zero
1240  szClassName, szTitle, button_callback)
1241  == None)
1242  {
1243  if(pDialogControl->pPropList)
1244  {
1245  DLGCDestroyProperties(pDialogControl->pPropList);
1246  pDialogControl->pPropList = NULL; // as a matter of course
1247  }
1248 
1249  WBFree(pDialogControl);
1250  return NULL;
1251  }
1252 
1253  // now allow certain kinds of input messages (I should be able to handle messages at this point)
1254  XSelectInput(pDisplay, pDialogControl->wID,
1256 
1257 
1258  if(pDialogControl->pDlgControlEntry &&
1259  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE))
1260  {
1261  XMapWindow(pDisplay, pDialogControl->wID);
1262  }
1263 
1264  return pDialogControl;
1265 }
1266 
1267 
1268 IMPLEMENT_CREATE_CONTROL(TRISTATEBUTTON_CONTROL)
1269 {
1270 BEGIN_CREATE_CONTROL(TRISTATEBUTTON_CONTROL);
1271 
1272  Display *pDisplay = dialog_control_get_display(pDialogControl);
1273 
1274 // aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
1275 
1276  pDialogControl->ulFlags &= ~BUTTON_TYPEMASK;
1277  pDialogControl->ulFlags |= BUTTON_TriStateButton;
1278 
1279 
1280  // focus
1282 
1283  // color information for border, foreground, background
1284 
1285  alloc_control_colors(pDialogControl, "*Checkbutton.foreground", "*Checkbutton.background",
1286  "*Checkbutton.highlightForeground", "*Checkbutton.highlightBackground",
1287  "*Checkbutton.activeForeground", "*CheckButton.activeBackground",
1288  "*Checkbutton.border", 1);
1289 
1290  if(standard_do_create_control(pDialogControl, iX, iY, iWidth, iHeight, 0, // border width is zero
1291  szClassName, szTitle, button_callback)
1292  == None)
1293  {
1294  if(pDialogControl->pPropList)
1295  {
1296  DLGCDestroyProperties(pDialogControl->pPropList);
1297  pDialogControl->pPropList = NULL; // as a matter of course
1298  }
1299 
1300  WBFree(pDialogControl);
1301  return NULL;
1302  }
1303 
1304  // now allow certain kinds of input messages (I should be able to handle messages at this point)
1305  XSelectInput(pDisplay, pDialogControl->wID,
1307 
1308 
1309  if(pDialogControl->pDlgControlEntry &&
1310  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE))
1311  {
1312  XMapWindow(pDisplay, pDialogControl->wID);
1313  }
1314 
1315  return pDialogControl;
1316 }
1317 
1318 
1319 IMPLEMENT_CREATE_CONTROL(HSCROLL_CONTROL)
1320 {
1321 BEGIN_CREATE_CONTROL(HSCROLL_CONTROL);
1322 
1323 // aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
1324 
1325 
1326  return NULL; // for now
1327 }
1328 
1329 
1330 IMPLEMENT_CREATE_CONTROL(VSCROLL_CONTROL)
1331 {
1332 BEGIN_CREATE_CONTROL(VSCROLL_CONTROL);
1333 
1334 // aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
1335 
1336 
1337  return NULL; // for now
1338 }
1339 
1340 
1341 IMPLEMENT_CREATE_CONTROL(SLIDER_CONTROL)
1342 {
1343 BEGIN_CREATE_CONTROL(SLIDER_CONTROL);
1344 
1345 // aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
1346 
1347 
1348  return NULL; // for now
1349 }
1350 
1351 
1352 IMPLEMENT_CREATE_CONTROL(KNOB_CONTROL)
1353 {
1354 BEGIN_CREATE_CONTROL(KNOB_CONTROL);
1355 
1356 // aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
1357 
1358 
1359  return NULL; // for now
1360 }
1361 
1362 
1363 static int list_callback(Window wID, XEvent *pEvent);
1364 
1365 IMPLEMENT_CREATE_CONTROL(LIST_CONTROL)
1366 {
1367 BEGIN_CREATE_CONTROL(LIST_CONTROL);
1368 
1369  Display *pDisplay = dialog_control_get_display(pDialogControl);
1370 
1371 // aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
1372 
1373  pDialogControl->ulFlags |= CONTROL_SupportListInfo;
1374 
1375  // focus
1376  pDialogControl->pDlgControlEntry->iFlags |= WBDialogEntry_CAN_HAVE_FOCUS; // override 'default' when I have focus
1377 
1378 
1379  // color information for border, foreground, background
1380 
1381  alloc_control_colors(pDialogControl, "*List.foreground", "*List.background",
1382  "*List.highlightForeground", "*List.highlightBackground",
1383  "*List.activeForeground", "*List.activeBackground",
1384  "*List.border", 0);
1385 
1386  if(standard_do_create_control(pDialogControl, iX, iY, iWidth, iHeight, 0, // border width is zero
1387  szClassName, szTitle, list_callback)
1388  == None)
1389  {
1390  if(pDialogControl->pPropList)
1391  {
1392  DLGCDestroyProperties(pDialogControl->pPropList);
1393  pDialogControl->pPropList = NULL; // as a matter of course
1394  }
1395 
1396  WBFree(pDialogControl);
1397  return NULL;
1398  }
1399 
1400  // initialize list element with default settings if not already initialized on window create
1401  if(DLGInitControlListInfoDefault(pDialogControl))
1402 // DLGInitControlListInfo(pDialogControl, ListInfoFlags_SORTED, DLGCDefaultListInfoAllocator, WBFree, NULL, NULL))
1403  {
1404  WB_ERROR_PRINT("%s - Unable to initialize list entry for control\n", __FUNCTION__);
1405  if(pDialogControl->pPropList)
1406  {
1407  DLGCDestroyProperties(pDialogControl->pPropList);
1408  pDialogControl->pPropList = NULL; // as a matter of course
1409  }
1410 
1411  WBSetWindowData(pDialogControl->wID, 0, NULL);
1412  WBFree(pDialogControl);
1413  return NULL;
1414  }
1415 
1416  ((WBListControl *)pDialogControl)->fsBold = None; // make sure
1417 
1418 
1419  // now allow certain kinds of input messages (I should be able to handle messages at this point)
1420  XSelectInput(pDisplay, pDialogControl->wID,
1422 
1423 
1424  if(pDialogControl->pDlgControlEntry &&
1425  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE))
1426  {
1427  XMapWindow(pDisplay, pDialogControl->wID);
1428  }
1429 
1430  return pDialogControl;
1431 }
1432 
1433 
1434 static int combo_callback(Window wID, XEvent *pEvent)
1435 {
1436 // Display *pDisplay = WBGetWindowDisplay(wID);
1437  WBDialogControl *pDialogControl = DLGGetDialogControlStruct(wID);
1438 
1439 
1440  // special handling for 'destroy'
1441  if(pEvent->type == DestroyNotify &&
1442  pEvent->xdestroywindow.window == wID)
1443  {
1445  "%s - DestroyNotify\n", __FUNCTION__);
1446 
1447  WBSetWindowData(wID, 0, NULL);
1448 
1449  if(pDialogControl)
1450  {
1451  WBComboControl *pPrivate = (WBComboControl *)pDialogControl;
1452 
1453  if(pDialogControl->pCaption)
1454  {
1455  WBFree(pDialogControl->pCaption);
1456  }
1457 
1458  if(pDialogControl->pPropList)
1459  {
1460  DLGCDestroyProperties(pDialogControl->pPropList);
1461  pDialogControl->pPropList = NULL; // as a matter of course
1462  }
1463 
1464  // free up privately allocated stuff
1466 
1467  WBFree(pDialogControl);
1468  }
1469 
1470  return 1; // handled
1471  }
1472 
1473  return 0; // temporary
1474 }
1475 
1476 IMPLEMENT_CREATE_CONTROL(COMBO_CONTROL)
1477 {
1478 BEGIN_CREATE_CONTROL(COMBO_CONTROL);
1479 
1480  WBComboControl *pPrivate = (WBComboControl *)pDialogControl;
1481  Display *pDisplay = dialog_control_get_display(pDialogControl);
1482 
1483 // aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
1484 
1485  pDialogControl->ulFlags |= CONTROL_SupportListInfo;
1486 
1487  // focus
1488  pDialogControl->pDlgControlEntry->iFlags |= WBDialogEntry_CAN_HAVE_FOCUS; // override 'default' when I have focus
1489 
1490  WBInitializeInPlaceTextObject(&(pPrivate->xTextObject), None);
1491 
1492  // TODO: determine single/multi line behavior. for now, SINGLE only
1493  pPrivate->xTextObject.vtable->set_linefeed(&(pPrivate->xTextObject), LineFeed_NONE); // single-line
1494 
1495 
1496  // color information for border, foreground, background
1497 
1498  alloc_control_colors(pDialogControl, "*Combo.foreground", "*Combo.background",
1499  "*Combo.highlightForeground", "*Combo.highlightBackground",
1500  "*Combo.activeForeground", "*Combo.activeBackground",
1501  "*Combo.border", 0);
1502 
1503  if(standard_do_create_control(pDialogControl, iX, iY, iWidth, iHeight, 0, // border width is zero
1504  szClassName, szTitle, combo_callback)
1505  == None)
1506  {
1507  if(pDialogControl->pPropList)
1508  {
1509  DLGCDestroyProperties(pDialogControl->pPropList);
1510  pDialogControl->pPropList = NULL; // as a matter of course
1511  }
1512 
1513  // destroy any allocated stuff thus far
1514  WBDestroyInPlaceTextObject(&(pPrivate->xTextObject)); // by convention
1515 
1516  WBFree(pDialogControl);
1517  return NULL;
1518  }
1519 
1520  pPrivate->xTextObject.wIDOwner = pDialogControl->wID; // TODO: make assigning this an API function?
1521 
1522 
1523 // // initialize list element with default settings if not already initialized on window create
1524 // if(DLGInitControlListInfoDefault(pDialogControl))
1526 // {
1527 // WB_ERROR_PRINT("%s - Unable to initialize list entry for control\n", __FUNCTION__);
1528 // if(pDialogControl->pPropList)
1529 // DLGCDestroyProperties(pDialogControl->pPropList);
1530 //
1531 // WBSetWindowData(wID, 0, NULL);
1532 // WBFree(pDialogControl);
1533 // return NULL;
1534 // }
1535 
1536  // now allow certain kinds of input messages (I should be able to handle messages at this point)
1537  XSelectInput(pDisplay, pDialogControl->wID,
1539 
1540 
1541  if(pDialogControl->pDlgControlEntry &&
1542  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE))
1543  {
1544  XMapWindow(pDisplay, pDialogControl->wID);
1545  }
1546 
1547  return pDialogControl;
1548 }
1549 
1550 
1551 static int tree_callback(Window wID, XEvent *pEvent);
1552 
1553 IMPLEMENT_CREATE_CONTROL(TREE_CONTROL)
1554 {
1555 BEGIN_CREATE_CONTROL(TREE_CONTROL);
1556 
1557  Display *pDisplay = dialog_control_get_display(pDialogControl);
1558 
1559 // aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
1560 
1561  pDialogControl->ulFlags |= CONTROL_SupportListInfo;
1562 
1563  // focus
1564  pDialogControl->pDlgControlEntry->iFlags |= WBDialogEntry_CAN_HAVE_FOCUS; // override 'default' when I have focus
1565 
1566 
1567  // color information for border, foreground, background
1568 
1569  alloc_control_colors(pDialogControl, "*List.foreground", "*List.background",
1570  "*List.highlightForeground", "*List.highlightBackground",
1571  "*List.activeForeground", "*List.activeBackground",
1572  "*List.border", 0);
1573 
1574  if(standard_do_create_control(pDialogControl, iX, iY, iWidth, iHeight, 0, // border width is zero
1575  szClassName, szTitle, tree_callback)
1576  == None)
1577  {
1578  if(pDialogControl->pPropList)
1579  {
1580  DLGCDestroyProperties(pDialogControl->pPropList);
1581  pDialogControl->pPropList = NULL; // as a matter of course
1582  }
1583 
1584  WBFree(pDialogControl);
1585  return NULL;
1586  }
1587 
1588  // initialize list element with default settings if not already initialized on window create
1589  if(DLGInitControlListInfoDefault(pDialogControl))
1590 // DLGInitControlListInfo(pDialogControl, ListInfoFlags_SORTED, DLGCDefaultListInfoAllocator, WBFree, NULL, NULL))
1591  {
1592  if(pDialogControl->pPropList)
1593  {
1594  DLGCDestroyProperties(pDialogControl->pPropList);
1595  }
1596 
1597  WBSetWindowData(pDialogControl->wID, 0, NULL);
1598  WBFree(pDialogControl);
1599  return NULL;
1600  }
1601 
1602  ((WBTreeControl *)pDialogControl)->fsBold = None;
1603 
1604  // now allow certain kinds of input messages (I should be able to handle messages at this point)
1605  XSelectInput(pDisplay, pDialogControl->wID,
1607 
1608 
1609  if(pDialogControl->pDlgControlEntry &&
1610  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE))
1611  {
1612  XMapWindow(pDisplay, pDialogControl->wID);
1613  }
1614 
1615  return pDialogControl;
1616 }
1617 
1618 
1619 static int combo_tree_callback(Window wID, XEvent *pEvent)
1620 {
1621  return 0; // temporary
1622 }
1623 
1624 IMPLEMENT_CREATE_CONTROL(COMBOTREE_CONTROL)
1625 {
1626 BEGIN_CREATE_CONTROL(COMBOTREE_CONTROL);
1627 
1628 // aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
1629 
1630  int (*x)(Window wID, XEvent *pEvent) = NULL; // warning avoidance
1631  x = combo_tree_callback; // warning avoidance
1632 
1633  x = x; // more warning avoidance (linux gcc is picky)
1634 
1635 
1636  return NULL; // for now
1637 }
1638 
1639 
1640 static int file_list_callback(Window wID, XEvent *pEvent);
1641 
1642 IMPLEMENT_CREATE_CONTROL(FILE_LIST_CONTROL)
1643 {
1644 BEGIN_CREATE_CONTROL(FILE_LIST_CONTROL);
1645 
1646 int iVisible = pDialogControl->pDlgControlEntry &&
1647  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE);
1648 
1649  if(iVisible)
1650  {
1651  pDialogControl->pDlgControlEntry->iFlags &= ~ WBDialogEntry_VISIBLE; // turn off visibility first
1652  }
1653 
1654  pDialogControl = do_create_LIST_CONTROL(pDialogControl, iX, iY, iWidth, iHeight, szClassName, szTitle);
1655  // these are very similar so use the LIST CONTROL create method. Initially, the 'list control'
1656  // callback is assigned. I must immediately assign the correct proc (and class name) after it returns.
1657 
1658  if(pDialogControl)
1659  {
1660  pDialogControl->aClass = aThis; // re-define the correct class
1661  DLGRegisterControlCallback(pDialogControl, szClassName, file_list_callback); // use this callback
1662 
1663  DLGModifyControlListInfo(pDialogControl, 0, 0, 0, 0, 0, 0,
1664  1, FileListControlDisplayProc, // only modify the display proc
1665  0, 0);
1666 
1667  if(iVisible)
1668  {
1669  pDialogControl->pDlgControlEntry->iFlags |= WBDialogEntry_VISIBLE; // turn visibility back on
1670  XMapWindow(WBGetWindowDisplay(pDialogControl->wID), pDialogControl->wID); // now show it
1671  }
1672  }
1673 
1674  return pDialogControl;
1675 }
1676 
1677 
1678 static int file_combo_callback(Window wID, XEvent *pEvent)
1679 {
1680 // return 0; // temporary
1681 
1682  // for all unhandled messages, call the 'combo_callback'
1683  // this takes care of things that are common to both FILE COMBO and the COMBO handler
1684 
1685  return combo_callback(wID, pEvent);
1686 }
1687 
1688 IMPLEMENT_CREATE_CONTROL(FILE_COMBO_CONTROL)
1689 {
1690 BEGIN_CREATE_CONTROL(FILE_COMBO_CONTROL);
1691 
1692 int iVisible = pDialogControl->pDlgControlEntry &&
1693  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE);
1694 
1695  if(iVisible)
1696  {
1697  pDialogControl->pDlgControlEntry->iFlags &= ~ WBDialogEntry_VISIBLE; // turn off visibility first
1698  }
1699 
1700  pDialogControl = do_create_COMBO_CONTROL(pDialogControl, iX, iY, iWidth, iHeight, szClassName, szTitle);
1701  // these are very similar so use the COMBO CONTROL create method. Initially, the 'combo control'
1702  // callback is assigned. I must immediately assign the correct proc (and class name) after it returns.
1703 
1704  if(pDialogControl)
1705  {
1706  pDialogControl->aClass = aThis; // re-define the correct class
1707  DLGRegisterControlCallback(pDialogControl, szClassName, file_combo_callback); // use this callback
1708 
1709  if(iVisible)
1710  {
1711  pDialogControl->pDlgControlEntry->iFlags |= WBDialogEntry_VISIBLE; // turn visibility back on
1712  XMapWindow(WBGetWindowDisplay(pDialogControl->wID), pDialogControl->wID); // now show it
1713  }
1714  }
1715 
1716  return pDialogControl;
1717 }
1718 
1719 
1720 static int path_tree_callback(Window wID, XEvent *pEvent);
1721 
1722 IMPLEMENT_CREATE_CONTROL(PATH_TREE_CONTROL)
1723 {
1724 BEGIN_CREATE_CONTROL(PATH_TREE_CONTROL);
1725 
1726 int iVisible = pDialogControl->pDlgControlEntry &&
1727  (pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_VISIBLE);
1728 
1729  if(iVisible)
1730  {
1731  pDialogControl->pDlgControlEntry->iFlags &= ~ WBDialogEntry_VISIBLE; // turn off visibility first
1732  }
1733 
1734  pDialogControl = do_create_TREE_CONTROL(pDialogControl, iX, iY, iWidth, iHeight, szClassName, szTitle);
1735  // these are very similar so use the TREE CONTROL create method. Initially, the 'tree control'
1736  // callback is assigned. I must immediately assign the correct proc (and class name) after it returns.
1737 
1738  if(pDialogControl)
1739  {
1740  pDialogControl->aClass = aThis; // re-define the correct class
1741  DLGRegisterControlCallback(pDialogControl, szClassName, path_tree_callback); // use this callback
1742 
1743  if(iVisible)
1744  {
1745  pDialogControl->pDlgControlEntry->iFlags |= WBDialogEntry_VISIBLE; // turn visibility back on
1746  XMapWindow(WBGetWindowDisplay(pDialogControl->wID), pDialogControl->wID); // now show it
1747  }
1748  }
1749 
1750  return pDialogControl;
1751 }
1752 
1753 
1754 IMPLEMENT_CREATE_CONTROL(TAB_CONTROL)
1755 {
1756 BEGIN_CREATE_CONTROL(TAB_CONTROL);
1757 
1758 // aThis = aThis; // so BEGIN_CREATE_CONTROL does not cause warnings for unused variables
1759 
1760 
1761  return NULL; // for now
1762 }
1763 
1764 
1765 
1766 // callbacks
1767 
1768 static int StaticDoExposeEvent(XExposeEvent *pEvent, Display *pDisplay,
1769  Window wID, WBDialogControl *pSelf);
1770 
1771 static int static_callback(Window wID, XEvent *pEvent)
1772 {
1773  Display *pDisplay = WBGetWindowDisplay(wID);
1774  WBDialogControl *pDialogControl = DLGGetDialogControlStruct(wID);
1775 
1776  if(pDialogControl && pEvent->type == Expose)
1777  {
1778  return StaticDoExposeEvent(&(pEvent->xexpose), pDisplay, wID, pDialogControl);
1779  }
1780 
1781  // special handling for 'destroy'
1782  if(pEvent->type == DestroyNotify &&
1783  pEvent->xdestroywindow.window == wID)
1784  {
1786  "%s - DestroyNotify\n", __FUNCTION__);
1787 
1788  WBSetWindowData(wID, 0, NULL);
1789 
1790  if(pDialogControl)
1791  {
1792  if(pDialogControl->aClass == aICON_CONTROL ||
1793  pDialogControl->aClass == aIMAGE_CONTROL)
1794  {
1795 
1796  Pixmap pxOld = ((struct _WB_PUSHBUTTON_CONTROL_ *)pDialogControl)->pixmap;
1797  if(pxOld != None)
1798  {
1800  if(!pDisplay)
1801  {
1802  XFreePixmap(WBGetDefaultDisplay(), pxOld);
1803  }
1804  else
1805  {
1806  XFreePixmap(pDisplay, pxOld);
1807  }
1809  }
1810  }
1811 
1812  if(pDialogControl->pCaption)
1813  {
1814  WBFree(pDialogControl->pCaption);
1815  }
1816 
1817  if(pDialogControl->pPropList)
1818  {
1819  DLGCDestroyProperties(pDialogControl->pPropList);
1820  pDialogControl->pPropList = NULL; // as a matter of course
1821  }
1822 
1823  WBFree(pDialogControl);
1824  }
1825 
1826  return 1;
1827  }
1828 
1829  return 0; // not handled
1830 }
1831 
1832 
1833 static int EditDoExposeEvent(XExposeEvent *pEvent, Display *pDisplay,
1834  Window wID, WBDialogControl *pSelf);
1835 static int EditDoCharEvent(XClientMessageEvent *pEvent, Display *pDisplay,
1836  Window wID, WBDialogControl *pSelf);
1837 static int EditDoPointerEvent(XClientMessageEvent *pEvent, Display *pDisplay,
1838  Window wID, WBDialogControl *pSelf);
1839 
1840 static int edit_callback(Window wID, XEvent *pEvent)
1841 {
1842  int iRval = 0;
1843  Atom aNotification = None;
1844  Display *pDisplay = WBGetWindowDisplay(wID);
1845  WBDialogControl *pDialogControl = DLGGetDialogControlStruct(wID);
1846  WBEditControl *pPrivate = (WBEditControl *)pDialogControl;
1847 // char tbuf[32];
1848 // int nChar = sizeof(tbuf);
1849 
1850 
1851  if(pDialogControl && pEvent->type == Expose)
1852  {
1853  return EditDoExposeEvent(&(pEvent->xexpose), pDisplay, wID, pDialogControl);
1854  }
1855 
1856  // special handling for 'destroy'
1857  if(pEvent->type == DestroyNotify &&
1858  pEvent->xdestroywindow.window == wID)
1859  {
1861  "%s - DestroyNotify\n", __FUNCTION__);
1862 
1863  WBSetWindowData(wID, 0, NULL);
1864 
1865  if(pDialogControl)
1866  {
1867  WBEditControl *pPrivate = (WBEditControl *)pDialogControl;
1868 
1869  if(pDialogControl->pCaption)
1870  {
1871  WBFree(pDialogControl->pCaption);
1872  }
1873 
1874  if(pDialogControl->pPropList)
1875  {
1876  DLGCDestroyProperties(pDialogControl->pPropList);
1877  pDialogControl->pPropList = NULL; // as a matter of course
1878  }
1879 
1880  DeleteTimer(pDisplay, wID, 1); // TODO: use #define for timer ID
1881 
1882  // free up privately allocated stuff
1883  WBDestroyInPlaceTextObject(&(pPrivate->xTextObject)); // destroy in-place text object
1884 
1885  WBFree(pDialogControl);
1886  }
1887 
1888  return 1; // handled
1889  }
1890 
1891  // MOUSE/KEYBOARD INPUT
1892  if(pEvent->type == ButtonPress ||
1893  pEvent->type == ButtonRelease ||
1894  pEvent->type == MotionNotify)
1895  {
1896  return 0; // not handled (use WM_POINTER messages generated by toolkit)
1897  }
1898  else if(pEvent->type == KeyPress || pEvent->type == KeyRelease)
1899  {
1900  return 0; // NOT handled (use WM_CHAR messages instead)
1901  }
1902 
1903  // CLIENT MESSAGE
1904  else if(pEvent->type == ClientMessage)
1905  {
1906  // ClientMessage events - I want WM_CHAR and WM_POINTER
1907 
1908  if(pEvent->xclient.message_type == aWM_CHAR)
1909  {
1910  iRval = EditDoCharEvent(&(pEvent->xclient), pDisplay, wID, pDialogControl);
1911 
1912  if(iRval > 0)
1913  {
1914  aNotification = aTEXT_CHANGED;
1915  }
1916  else if(iRval < 0) // key handled, text NOT changed
1917  {
1918  iRval = 1; // to indicate that no further key processing is needed
1919  }
1920  }
1921  else if(pEvent->xclient.message_type == aWM_POINTER)
1922  {
1923  iRval = EditDoPointerEvent(&(pEvent->xclient), pDisplay, wID, pDialogControl);
1924 
1925  if(iRval) // "handled"
1926  {
1927  aNotification = aTEXT_CHANGED; // TODO: is this necessary??
1928  }
1929  }
1930  // PROPERTY NOTIFICATIONS - special handling for 'CAPTION' assignments
1931  else if(pEvent->xclient.message_type == aDLGC_PROP_NOTIFY)
1932  {
1933  // if I change the 'caption' property I will need to update the xTextObject accordingly
1934 
1935  if(pEvent->xclient.data.l[0] == aDLGC_CAPTION)
1936  {
1937 // WB_ERROR_PRINT("TEMPORARY: %s - caption notification \"%s\"\n", __FUNCTION__, pDialogControl->pCaption);
1938 
1939  // mirror the text within the TEXT_OBJECT
1940  pPrivate->xTextObject.vtable->set_text(&(pPrivate->xTextObject), pDialogControl->pCaption, 0);
1941 
1942  // invalidate the entire window if not already done
1943  if(WBIsMapped(pDisplay, wID)) // if I'm visible...
1944  {
1945  WBInvalidateGeom(wID, NULL, 1); // paint it now
1946  }
1947  else
1948  {
1949  WBInvalidateGeom(wID, NULL, 0); // just invalidate it
1950  }
1951  }
1952  }
1953  else if(pEvent->xclient.message_type == aWM_TIMER)
1954  {
1955  int bHasFocus = pDialogControl->pDlgControlEntry
1956  ? pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_HAS_FOCUS
1957  : 0;
1958 
1959  pPrivate->xTextObject.vtable->cursor_blink(&(pPrivate->xTextObject), bHasFocus);
1960 
1961  iRval = 1; // handled
1962 // aNotification = None;
1963  }
1964  else
1965  {
1966  iRval = 0; // NOT handled
1967  }
1968  }
1969  else
1970  {
1971  iRval = 0; // not handled (default case)
1972  }
1973 
1974 
1975  // *********************
1976  // CONTROL NOTIFICATIONS
1977  // *********************
1978 
1979  if(iRval && aNotification != None &&
1980  pDialogControl->pOwner &&
1981  pDialogControl->pDlgControlEntry)
1982  {
1984  "%s:%d - Post Event: %08xH %08xH %08xH %pH\n", __FUNCTION__, __LINE__,
1985  (int)aCONTROL_NOTIFY, (int)aNotification,
1986  (int)pDialogControl->pDlgControlEntry->iID, pDialogControl);
1987 
1988  DLGNotifyOwnerAsync(pDialogControl, aCONTROL_NOTIFY, aNotification,
1989  pDialogControl->pDlgControlEntry->iID,
1990  (long)pDialogControl, 0, 0);
1991  }
1992 
1993  return iRval; // 0 if not handled, 1 if handled
1994 }
1995 
1996 
1997 static int PushButtonDoExposeEvent(XExposeEvent *pEvent, Display *pDisplay,
1998  Window wID, WBDialogControl *pSelf); // these are different
1999 static int ButtonDoExposeEvent(XExposeEvent *pEvent, Display *pDisplay,
2000  Window wID, WBDialogControl *pSelf);
2001 
2002 static int button_callback(Window wID, XEvent *pEvent)
2003 {
2004  Display *pDisplay = WBGetWindowDisplay(wID);
2005  WBDialogControl *pDialogControl = DLGGetDialogControlStruct(wID);
2006 
2007  if(pDialogControl && pEvent->type == Expose)
2008  {
2009  int iType = pDialogControl->ulFlags & BUTTON_TYPEMASK;
2010 
2011  if(iType == BUTTON_PushButton || iType == BUTTON_DefPushButton
2012  || iType == BUTTON_CancelButton)
2013  {
2014  // pushbuttons have their own appearance so they must be painted differently
2015 
2016  return PushButtonDoExposeEvent(&(pEvent->xexpose), pDisplay, wID, pDialogControl); // temporary
2017  }
2018 
2019  // other buttons are some kind of gadget plus text, so they paint with the same proc
2020  return ButtonDoExposeEvent(&(pEvent->xexpose), pDisplay, wID, pDialogControl); // temporary
2021  }
2022 
2023  // special handling for 'destroy'
2024  if(pEvent->type == DestroyNotify &&
2025  pEvent->xdestroywindow.window == wID)
2026  {
2028  "%s - DestroyNotify\n", __FUNCTION__);
2029 
2030  WBSetWindowData(wID, 0, NULL);
2031 
2032  if(pDialogControl)
2033  {
2034  if(pDialogControl->aClass == aPUSHBUTTON_CONTROL ||
2035  pDialogControl->aClass == aDEFPUSHBUTTON_CONTROL ||
2036  pDialogControl->aClass == aCANCELBUTTON_CONTROL)
2037  {
2038  Pixmap pxOld = ((struct _WB_PUSHBUTTON_CONTROL_ *)pDialogControl)->pixmap;
2039 
2040  if(pxOld != None)
2041  {
2043  if(!pDisplay)
2044  {
2045  XFreePixmap(WBGetDefaultDisplay(), pxOld);
2046  }
2047  else
2048  {
2049  XFreePixmap(pDisplay, pxOld);
2050  }
2052  }
2053  }
2054 
2055  if(pDialogControl->pCaption)
2056  {
2057  WBFree(pDialogControl->pCaption);
2058  }
2059 
2060  if(pDialogControl->pPropList)
2061  {
2062  DLGCDestroyProperties(pDialogControl->pPropList);
2063  pDialogControl->pPropList = NULL; // as a matter of course
2064  }
2065 
2066  WBFree(pDialogControl);
2067  }
2068 
2069  return 1;
2070  }
2071 
2072  // CONTROL NOTIFICATIONS
2073 
2074  if(pDialogControl->pOwner &&
2075  pDialogControl->pDlgControlEntry)
2076  {
2077  if(pEvent->type == ButtonPress)
2078  {
2079  // button press event - what kind of button am I?
2080  if((pDialogControl->aClass == aPUSHBUTTON_CONTROL ||
2081  pDialogControl->aClass == aDEFPUSHBUTTON_CONTROL ||
2082  pDialogControl->aClass == aCANCELBUTTON_CONTROL)
2083  && ((XButtonEvent *)pEvent)->button == 1) // left button only
2084  {
2085  // re-paint button "pressed"
2086  pDialogControl->pDlgControlEntry->iFlags |= WBDialogEntry_PRESSED;
2087 
2088  WB_ERROR_PRINT("TEMPORARY: %s - button press\n", __FUNCTION__);
2089 
2090  WBInvalidateRect(wID, NULL, 0);
2091  WBUpdateWindowImmediately(wID); // make sure I paint it NOW
2092 
2093  return 0; // not handled - button release will activate these buttons
2094  }
2095  }
2096  else if(pEvent->type == ButtonRelease)
2097  {
2099  "%s - BUTTON RELEASE for BUTTON %d\n",
2100  __FUNCTION__, ((XButtonEvent *)pEvent)->button);
2101 
2102  // for pushbuttons, kick an even notifier upstairs to the owning window/control
2103  if((pDialogControl->aClass == aPUSHBUTTON_CONTROL ||
2104  pDialogControl->aClass == aDEFPUSHBUTTON_CONTROL ||
2105  pDialogControl->aClass == aCANCELBUTTON_CONTROL)
2106  && ((XButtonEvent *)pEvent)->button == 1) // left button only
2107  {
2109  pDialogControl->pDlgControlEntry->iID,
2110  (long)pDialogControl, 0, 0);
2111 
2113  "%s:%d - Post Event: %08xH %08xH %08xH %pH\n", __FUNCTION__, __LINE__,
2114  (int)aCONTROL_NOTIFY, (int)aBUTTON_PRESS,
2115  (int)pDialogControl->pDlgControlEntry->iID, pDialogControl);
2116 
2117 // WBEndModal(wID, pItem->iAction);
2118 
2119  if(pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_PRESSED)
2120  {
2121  pDialogControl->pDlgControlEntry->iFlags &= ~WBDialogEntry_PRESSED; // flip the bit back off
2122 
2123  WBInvalidateRect(wID, NULL, 0);
2124  WBUpdateWindowImmediately(wID); // make sure I paint it NOW
2125  }
2126 
2127  return 1; // handled
2128  }
2129  }
2130  else if(pEvent->type == KeyPress)
2131  {
2132  // certain key press events SHOULD cause me to do something, like "press"
2133  // the button and give positive feedback.
2134 
2135  // TODO: implement spacebar and enter key doing this
2136 
2137  if(pDialogControl->aClass == aPUSHBUTTON_CONTROL ||
2138  pDialogControl->aClass == aDEFPUSHBUTTON_CONTROL ||
2139  pDialogControl->aClass == aCANCELBUTTON_CONTROL)
2140  {
2141  int iACS = 0, iKey = WBKeyEventProcessKey((XKeyEvent *)pEvent, NULL, NULL, &iACS);
2142 
2143  WB_ERROR_PRINT("TEMPORARY: %s- keydown event, %d iACS=%d\n", __FUNCTION__, iKey, iACS);
2144 
2145  if((iKey == ' ' || iKey == '\n' || iKey == '\r')
2146  && iACS == 0 && pDialogControl->pDlgControlEntry)
2147  {
2148  WB_ERROR_PRINT("TEMPORARY: %s - key press\n", __FUNCTION__);
2149 
2150  pDialogControl->pDlgControlEntry->iFlags |= WBDialogEntry_PRESSED;
2151 
2152  WBInvalidateRect(wID, NULL, 0);
2153  WBUpdateWindowImmediately(wID); // make sure I paint it NOW
2154 
2155 // pDialogControl->pDlgControlEntry->iFlags &= ~WBDialogEntry_PRESSED; // flip the bit back off
2156  }
2157 
2158  return 1; // handled
2159  }
2160  }
2161  else if(pEvent->type == KeyRelease)
2162  {
2163  // KeyRelease
2164 
2165  // KeyPress - space bar will toggle the button or press it (as required)
2166  int iACS = 0, iKey = WBKeyEventProcessKey((XKeyEvent *)pEvent, NULL, NULL, &iACS);
2167 
2169  "%s - KEY RELEASE for KEY %d\n",
2170  __FUNCTION__, ((XKeyEvent *)pEvent)->keycode);
2171 
2172  if((pDialogControl->aClass == aPUSHBUTTON_CONTROL ||
2173  pDialogControl->aClass == aDEFPUSHBUTTON_CONTROL ||
2174  pDialogControl->aClass == aCANCELBUTTON_CONTROL) &&
2175  iACS == 0 &&
2176  (iKey == ' ' || iKey == '\n' || iKey == '\r')) // space bar, enter, return
2177  {
2178  DLGNotifyOwnerAsync(pDialogControl, aCONTROL_NOTIFY, aBUTTON_PRESS,
2179  pDialogControl->pDlgControlEntry->iID,
2180  (long)pDialogControl, 0, 0);
2181 
2183  "%s:%d - Post Event: %08xH %08xH %08xH %pH\n", __FUNCTION__, __LINE__,
2184  (int)aCONTROL_NOTIFY, (int)aBUTTON_PRESS,
2185  (int)pDialogControl->pDlgControlEntry->iID, pDialogControl);
2186 
2187  if(pDialogControl->pDlgControlEntry->iFlags & WBDialogEntry_PRESSED)
2188  {
2189  pDialogControl->pDlgControlEntry->iFlags &= ~WBDialogEntry_PRESSED; // flip the bit back off
2190 
2191  WBInvalidateRect(wID, NULL, 0);
2192  WBUpdateWindowImmediately(wID); // make sure I paint it NOW
2193  }
2194 
2195 // WBEndModal(wID, pItem->iAction);
2196  return 1; // handled
2197  }
2198  }
2199  }
2200 
2201  return 0; // not handled
2202 }
2203 
2204 static int ListDoExposeEvent(XExposeEvent *pEvent, Display *pDisplay,
2205  Window wID, WBDialogControl *pSelf);
2206 
2207 static int ListDoCharEvent(XClientMessageEvent *pEvent, Display *pDisplay,
2208  Window wID, WBDialogControl *pSelf);
2209 
2210 static int list_callback(Window wID, XEvent *pEvent)
2211 {
2212  int i1, iRval = 0;
2213  Atom aNotification = None;
2214  Display *pDisplay = WBGetWindowDisplay(wID);
2215  WBDialogControl *pDialogControl = DLGGetDialogControlStruct(wID);
2216 // char tbuf[32];
2217 // int nChar = sizeof(tbuf);
2218  LISTINFO *pListInfo = NULL;
2219  WB_DIALOG_PROP *pProp;
2220 
2221  if(pDialogControl && pEvent->type == Expose)
2222  {
2223  return ListDoExposeEvent(&(pEvent->xexpose), pDisplay, wID, pDialogControl);
2224  }
2225 
2226  pProp = (WB_DIALOG_PROP *)WBDialogControlGetDialogProp(pDialogControl, aDLGC_LISTINFO);
2227 
2228  if(pProp)
2229  {
2230  pListInfo = (LISTINFO *)pProp->pVal;
2231  }
2232 
2233 
2234  // special handling for 'destroy'
2235  if(pEvent->type == DestroyNotify &&
2236  pEvent->xdestroywindow.window == wID)
2237  {
2239  "%s - DestroyNotify\n", __FUNCTION__);
2240 
2241  if(((WBListControl *)pDialogControl)->fsBold == None)
2242  {
2243  // free up the allocated font set, if there is one
2244  XFreeFontSet(pDisplay, ((WBListControl *)pDialogControl)->fsBold);
2245 
2246  ((WBListControl *)pDialogControl)->fsBold = None;
2247  }
2248 
2249  WBSetWindowData(wID, 0, NULL);
2250 
2251  if(pDialogControl)
2252  {
2253  if(pDialogControl->pCaption)
2254  {
2255  WBFree(pDialogControl->pCaption);
2256  }
2257 
2258  if(pDialogControl->pPropList)
2259  {
2260  DLGCDestroyProperties(pDialogControl->pPropList);
2261  pDialogControl->pPropList = NULL; // as a matter of course
2262  }
2263 
2264  WBFree(pDialogControl);
2265  }
2266 
2267  return 1;
2268  }
2269 
2270  // handle scroll bars (mousie-clickie and keystrokes)
2271  // this will generate 'scroll notify' events (as appropriate)
2272  i1 = DLGScrollBarHandler(wID, pDialogControl, pEvent);
2273 
2274  if(i1)
2275  {
2276  return i1;
2277  }
2278 
2279 #if 0
2280  if(pEvent->type == KeyPress)
2281  {
2282 #ifndef NO_DEBUG
2283  int iACS = 0, iKey = WBKeyEventProcessKey((XKeyEvent *)pEvent, tbuf, &nChar, &iACS);
2285  "%s KEY PRESS for KEY %d KEYCODE %d\n", __FUNCTION__, iKey, ((XKeyEvent *)pEvent)->keycode);
2286 
2287  // TODO: extended select modifier keys, toggles, 'auto-complete' select, etc.
2288 
2289 #endif // NO_DEBUG
2290  iRval = 1; // handled
2291  }
2292  else if(pEvent->type == KeyRelease)
2293  {
2294  // KeyRelease
2295 
2296  int iACS = 0, iKey = WBKeyEventProcessKey((XKeyEvent *)pEvent, tbuf, &nChar, &iACS);
2297 
2298  if(nChar > 0)
2299  {
2301  "%s KEY RELEASE for KEY %d KEYCODE %d MASK=%d (%xH)\n",
2302  __FUNCTION__, iKey, ((XKeyEvent *)pEvent)->keycode,
2303  ((XKeyEvent *)pEvent)->state, ((XKeyEvent *)pEvent)->state);
2304 
2305 // if(iKey == 8) // backspace
2306 // {
2307 // // TODO: delete char before cursor or highlighted text
2308 // // for now just delete the LAST character
2309 //
2310 // int iLen = strlen(pDialogControl->pCaption);
2311 // if(iLen > 0)
2312 // {
2313 // pDialogControl->pCaption[iLen - 1] = 0;
2314 // }
2315 //
2316 // WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Event | DebugSubSystem_DialogCtrl | DebugSubSystem_Keyboard,
2317 // "%s - BACKSPACE key pressed\n", __FUNCTION__);
2318 // }
2319 // else
2320 // {
2321 // // TODO: insert or concatenate? for now concatenate
2322 // WBCatString(&(pDialogControl->pCaption), tbuf);
2323 // }
2324 //
2325 // aNotification = aTEXT_CHANGED;
2326 // WBInvalidateGeom(wID, NULL, 1);
2327  }
2328  else
2329  {
2330  if(iACS & WB_KEYEVENT_KEYSYM)
2331  {
2332  // TODO: international, 'dead' and other KEYSYM key assignments
2333 #define KEYSYM_MATCH_CURSOR_NAME(X) (iKey == XK_##X || iKey == XK_KP_##X)
2334  if(KEYSYM_MATCH_CURSOR_NAME(Home))
2335  {
2337  "%s - Home key pressed\n", __FUNCTION__);
2338  }
2339  else if(KEYSYM_MATCH_CURSOR_NAME(End))
2340  {
2342  "%s - End key pressed\n", __FUNCTION__);
2343  }
2344  else if(KEYSYM_MATCH_CURSOR_NAME(Left)) // if HSCROLL enabled
2345  {
2347  "%s - Left key pressed\n", __FUNCTION__);
2348  }
2349  else if(KEYSYM_MATCH_CURSOR_NAME(Right)) // if HSCROLL enabled
2350  {
2352  "%s - Right key pressed\n", __FUNCTION__);
2353  }
2354  else if(KEYSYM_MATCH_CURSOR_NAME(Up))
2355  {
2357  "%s - Up key pressed\n", __FUNCTION__);
2358  }
2359  else if(KEYSYM_MATCH_CURSOR_NAME(Down))
2360  {
2362  "%s - Down key pressed\n", __FUNCTION__);
2363  }
2364  else if(KEYSYM_MATCH_CURSOR_NAME(Page_Up))
2365  {
2367  "%s - Page Up key pressed\n", __FUNCTION__);
2368  }
2369  else if(KEYSYM_MATCH_CURSOR_NAME(Page_Down))
2370  {
2372  "%s - Page Down key pressed\n", __FUNCTION__);
2373  }
2374  else if(KEYSYM_MATCH_CURSOR_NAME(Begin)) // beginning of current line
2375  {
2377  "%s - Beginning Of Line key pressed\n", __FUNCTION__);
2378  }
2379 #undef KEYSYM_MATCH_CURSOR_NAME
2380  else
2381  {
2382  // is it a cursor key? let's find out
2384  "%s - CURSOR KEY? %d (%08xH) %d (%08xH)\n",
2385  __FUNCTION__, iKey, iKey, iACS, iACS);
2386  }
2387  }
2388  }
2389 
2390  iRval = 1;
2391  }
2392 #endif // 0
2393 
2394  // processing notifications sent to me
2395 
2396  if(pEvent->type == ClientMessage &&
2397  pEvent->xclient.message_type == aCONTROL_NOTIFY)
2398  {
2399  char *p1 = WBGetAtomName(pDisplay,(Atom)pEvent->xclient.data.l[0]);
2401  "%s - CONTROL_NOTIFY for window %d (%08xH) - %s %ld\n", __FUNCTION__,
2402  (int)wID, (int)wID, p1, pEvent->xclient.data.l[1]);
2403  if(p1)
2404  {
2405  WBFree(p1);
2406  }
2407  }
2408  else if(pEvent->type == ClientMessage &&
2409  pEvent->xclient.message_type == aWM_CHAR)
2410  {
2411  iRval = ListDoCharEvent(&(pEvent->xclient), pDisplay, wID, pDialogControl);
2412 
2413  if(iRval > 0)
2414  {
2415 // POOBAH
2416  DLGNotifyOwner(pDialogControl, aCONTROL_NOTIFY, aLIST_NOTIFY,
2417  pDialogControl->pDlgControlEntry->iID,
2418  WB_LIST_SELCHANGE, pListInfo->nPos, 0); // synchronous notification
2419  return 1; // "handled"
2420  }
2421 
2422  iRval = 0; // "not handled"
2423  }
2424  else if(pEvent->type == ClientMessage &&
2425  pEvent->xclient.message_type == aWM_POINTER)
2426  {
2427  if(pEvent->xclient.data.l[0] == WB_POINTER_CLICK)
2428  {
2429  // TODO: handle shift-click, ctrl-click, alt-click
2430 
2431  if(pEvent->xclient.data.l[1] == WB_POINTER_BUTTON1 && // left button
2432  !pEvent->xclient.data.l[2])
2433  {
2434  int iX = pEvent->xclient.data.l[3];
2435  int iY = pEvent->xclient.data.l[4];
2436 
2437  WB_SCROLLINFO *pScrollInfo = (WB_SCROLLINFO *)WBDialogControlGetProperty2(pDialogControl, aDLGC_SCROLLINFO);
2438 
2439  if(WB_LIKELY(pScrollInfo != NULL) &&
2440  WB_UNLIKELY(WBPointInGeom(iX, iY, pScrollInfo->geomVBar) != 0))
2441  {
2442  // mouse click was INSIDE of the scroll bar area - this should have already been handled
2443 
2444  WB_ERROR_PRINT("UNHANDLED SCROLL BAR mouse message inside VBAR %d %d %d %d %d\n",
2445  (int)pEvent->xclient.data.l[0],
2446  (int)pEvent->xclient.data.l[1],
2447  (int)pEvent->xclient.data.l[2],
2448  (int)pEvent->xclient.data.l[3],
2449  (int)pEvent->xclient.data.l[4]);
2450 
2451  return 0;
2452  }
2453 
2454  // use information about the list to determine where I clicked (which selected item or not)
2455  // TODO: encapsulate item selection via message post
2456 
2457  if(pListInfo && pListInfo->nItemHeight > 0)
2458  {
2459  i1 = (iY - 1) / pListInfo->nItemHeight; // item number within list 'display area'
2460 
2461 // WB_ERROR_PRINT("TEMPORARY: clicked on item %d, top item is %d\n", i1, pListInfo->nTop);
2462 
2463  if(i1 >= 0 && i1 <= pListInfo->nHeight)
2464  {
2465  i1 += pListInfo->nTop; // actual index
2466  }
2467  else
2468  {
2469  i1 = -1; // as a flag
2470  }
2471  }
2472  if(i1 >= 0 && i1 < pListInfo->nItems)
2473  {
2474  if(pListInfo->nPos != i1)
2475  {
2476  // set new selection
2477  pListInfo->nPos = i1;
2478 
2479  if(i1 < pListInfo->nTop) // adjust top if needed
2480  {
2481  pListInfo->nTop = i1;
2482  }
2483  else if(i1 >= pListInfo->nHeight + pListInfo->nTop)
2484  {
2485  pListInfo->nTop = i1 - pListInfo->nHeight + 1;
2486  }
2487 
2488  WBInvalidateGeom(wID, NULL, 0); // asynchronous re-paint
2489  WBUpdateWindow(wID); // posts expose event
2490 
2491  DLGNotifyOwner(pDialogControl, aCONTROL_NOTIFY, aLIST_NOTIFY,
2492  pDialogControl->pDlgControlEntry->iID,
2493  WB_LIST_SELCHANGE, pListInfo->nPos, 0); // synchronous notification
2494  }
2495  }
2496 
2497  return 1; // handled
2498  }
2499  }
2500  else if(pEvent->xclient.data.l[0] == WB_POINTER_DBLCLICK)
2501  {
2502  if(pEvent->xclient.data.l[1] == WB_POINTER_BUTTON1 && // left button
2503  !pEvent->xclient.data.l[2])
2504  {
2505  int iX = pEvent->xclient.data.l[3];
2506  int iY = pEvent->xclient.data.l[4];
2507 
2508  // NOTE: no need to notify selection (would have already been done)
2509 
2510  WB_SCROLLINFO *pScrollInfo = (WB_SCROLLINFO *)WBDialogControlGetProperty2(pDialogControl, aDLGC_SCROLLINFO);
2511 
2512  if(WB_LIKELY(pScrollInfo != NULL) &&
2513  WB_UNLIKELY(WBPointInGeom(iX, iY, pScrollInfo->geomVBar) != 0))
2514  {
2515  WB_ERROR_PRINT("UNHANDLED SCROLL BAR mouse message %d %d %d %d %d\n",
2516  (int)pEvent->xclient.data.l[0],
2517  (int)pEvent->xclient.data.l[1],
2518  (int)pEvent->xclient.data.l[2],
2519  (int)pEvent->xclient.data.l[3],
2520  (int)pEvent->xclient.data.l[4]);
2521 
2522  return 0;
2523  }
2524 
2525  // for now assume selection hasn't changed, and post a double-click notification
2526  // back to the list control's owner so that appropriate action will be taken
2527 
2528  DLGNotifyOwner(pDialogControl, aCONTROL_NOTIFY, aLIST_NOTIFY,
2529  pDialogControl->pDlgControlEntry->iID,
2530  WB_LIST_DBLCLICK, pListInfo->nPos, 0); // synchronous notification
2531 
2532  return 1;
2533  }
2534 
2535  return 0; // so no further processing happens
2536  }
2537  else if(pEvent->xclient.data.l[0] == WB_POINTER_CANCEL)
2538  {
2539  // canceling drag (as appropriate)
2540 
2541  WB_SCROLLINFO *pScrollInfo = (WB_SCROLLINFO *)WBDialogControlGetProperty2(pDialogControl, aDLGC_SCROLLINFO);
2542 
2543  if(pScrollInfo &&
2544  pEvent->xclient.data.l[1] == WB_POINTER_BUTTON1 && // left button
2545  !pEvent->xclient.data.l[2])
2546  {
2547  WB_ERROR_PRINT("UNHANDLED SCROLL BAR mouse message %d %d %d %d %d\n",
2548  (int)pEvent->xclient.data.l[0],
2549  (int)pEvent->xclient.data.l[1],
2550  (int)pEvent->xclient.data.l[2],
2551  (int)pEvent->xclient.data.l[3],
2552  (int)pEvent->xclient.data.l[4]);
2553 
2554  pScrollInfo->iScrollState &= ~WBScrollState_LDRAG;
2555  }
2556 
2557  return 1;
2558  }
2559  else if(pEvent->xclient.data.l[0] == WB_POINTER_DRAG ||
2560  pEvent->xclient.data.l[0] == WB_POINTER_MOVE)
2561  {
2562  if(pEvent->xclient.data.l[1] == WB_POINTER_BUTTON1 && // left button
2563  !pEvent->xclient.data.l[2])
2564  {
2565  int iX = pEvent->xclient.data.l[3];
2566  int iY = pEvent->xclient.data.l[4];
2567 
2568  WB_SCROLLINFO *pScrollInfo = (WB_SCROLLINFO *)WBDialogControlGetProperty2(pDialogControl, aDLGC_SCROLLINFO);
2569 
2570  if(WB_LIKELY(pScrollInfo != NULL) &&
2571  WB_UNLIKELY(WBPointInGeom(iX, iY, pScrollInfo->geomVBar) != 0))
2572  {
2573  WB_ERROR_PRINT("UNHANDLED SCROLL BAR mouse message %d %d %d %d %d\n",
2574  (int)pEvent->xclient.data.l[0],
2575  (int)pEvent->xclient.data.l[1],
2576  (int)pEvent->xclient.data.l[2],
2577  (int)pEvent->xclient.data.l[3],
2578  (int)pEvent->xclient.data.l[4]);
2579  }
2580  }
2581 
2582  return 0; // for now (to enforce 'NOT HANDLED')
2583  }
2584  else if(pEvent->xclient.data.l[0] == WB_POINTER_DROP)
2585  {
2586  return 1; // "handled" (just a notification anyway)
2587  }
2588  }
2589  else if(pEvent->type == ClientMessage &&
2590  pEvent->xclient.message_type == aSCROLL_NOTIFY)
2591  {
2593  "%s - SCROLL_NOTIFY for window %d (%08xH) - %ld %ld %ld\n", __FUNCTION__,
2594  (int)wID, (int)wID, pEvent->xclient.data.l[0],
2595  pEvent->xclient.data.l[1], pEvent->xclient.data.l[2]);
2596 
2597  if(pEvent->xclient.data.l[0] == WB_SCROLL_VERTICAL)
2598  {
2599  int iOldTop = pListInfo->nTop;
2600  int iOldPos = pListInfo->nPos;
2601 
2602  switch(pEvent->xclient.data.l[1])
2603  {
2604  case WB_SCROLL_FORWARD:
2605  pListInfo->nPos ++;
2606  break;
2607  case WB_SCROLL_BACKWARD:
2608  pListInfo->nPos --;
2609  break;
2610  case WB_SCROLL_PAGEFWD:
2611  pListInfo->nPos += pListInfo->nHeight;
2612  break;
2613  case WB_SCROLL_PAGEBACK:
2614  pListInfo->nPos -= pListInfo->nHeight;
2615  break;
2616  case WB_SCROLL_FIRST:
2617  pListInfo->nPos = 0;
2618  break;
2619  case WB_SCROLL_LAST:
2620  pListInfo->nPos = pListInfo->nItems - 1;
2621  break;
2622  case WB_SCROLL_KNOB:
2623  pListInfo->nTop = pEvent->xclient.data.l[2];
2624  pListInfo->nPos += pListInfo->nTop - iOldTop;
2625  break;
2626  case WB_SCROLL_RELATIVE:
2627  pListInfo->nPos += pEvent->xclient.data.l[2];
2628  break;
2629  case WB_SCROLL_ABSOLUTE:
2630  pListInfo->nPos = pEvent->xclient.data.l[2];
2631  break;
2632  }
2633 
2634  if(iOldTop != pListInfo->nTop || iOldPos != pListInfo->nPos)
2635  {
2636  if(pListInfo->nPos < 0)
2637  {
2638  pListInfo->nPos = 0;
2639  }
2640  else if(pListInfo->nPos >= pListInfo->nItems)
2641  {
2642  pListInfo->nPos = pListInfo->nItems - 1;
2643  }
2644  if(pListInfo->nTop > pListInfo->nPos)
2645  {
2646  pListInfo->nTop = pListInfo->nPos;
2647  }
2648  else if(pListInfo->nTop + pListInfo->nHeight <= pListInfo->nPos)
2649  {
2650  pListInfo->nTop = pListInfo->nPos - pListInfo->nHeight + 1;
2651  }
2652 
2653  if(iOldTop != pListInfo->nTop || iOldPos != pListInfo->nPos)
2654  {
2655  WBInvalidateGeom(wID, NULL, 0);
2656 
2657  if(pEvent->xclient.data.l[1] == WB_SCROLL_KNOB)
2658  {
2660  }
2661  else
2662  {
2663  WBUpdateWindow(wID);
2664  }
2665  }
2666  }
2667 
2668  return 1;
2669  }
2670 
2671  return 1;
2672  }
2673 
2674 
2675 
2676  // CONTROL NOTIFICATIONS to OWNER
2677 
2678  if(iRval && aNotification != None &&
2679  pDialogControl->pOwner &&
2680  pDialogControl->pDlgControlEntry)
2681  {
2682  DLGNotifyOwnerAsync(pDialogControl, aCONTROL_NOTIFY, aNotification,
2683  pDialogControl->pDlgControlEntry->iID,
2684  (long)pDialogControl, 0, 0);
2685 
2687  "%s:%d - Post Event: %08xH %08xH %08xH %pH\n", __FUNCTION__, __LINE__,
2688  (int)aCONTROL_NOTIFY, (int)aNotification,
2689  (int)pDialogControl->pDlgControlEntry->iID, pDialogControl);
2690  }
2691 
2692  return iRval; // 0 if not handled, 1 if handled
2693 }
2694 
2695 
2696 static int file_list_callback(Window wID, XEvent *pEvent)
2697 {
2698 //Display *pDisplay = WBGetWindowDisplay(wID);
2699 WBDialogControl *pDialogControl = DLGGetDialogControlStruct(wID);
2700 char *p1;
2701 const char *p2;
2702 void *pV;
2703 unsigned long dw1;
2704 char tbuf[NAME_MAX + 4];
2705 
2706 
2707 // WB_ERROR_PRINT("TEMPORARY: %s\n", __FUNCTION__);
2708 
2709  if(pEvent->type == ClientMessage &&
2710  pEvent->xclient.message_type == aDLGC_PROP_NOTIFY)
2711  {
2713  "%s - DLGC_PROP_NOTIFY for window %d (%08xH)\n", __FUNCTION__,
2714  (int)wID, (int)wID);
2715 
2716  if(pEvent->xclient.data.l[0] == aDLGC_PATH) // TODO: include a 'DLGC_FILTER' property as well
2717  {
2718  if(pDialogControl->pOwner)
2719  {
2720  WBBeginWaitCursor(pDialogControl->pOwner->wID);
2721  }
2722  else
2723  {
2724  WBBeginWaitCursor(wID);
2725  }
2726 
2728  "%s - DLGC_PROP_NOTIFY DLGC_PATH for window %d (%08xH)\n", __FUNCTION__,
2729  (int)wID, (int)wID);
2730 
2731  // property change notification for aPATH must refresh contents
2732  DLGDelControlListEntry(pDialogControl, ControlListIndex_DELETE_ALL); // delete all
2733 
2734  p2 = WBDialogControlGetProperty(pDialogControl, aDLGC_PATH); // original path info
2735 
2736  p1 = WBCopyString(p2); // working copy
2737  if(!p1 || !*p1)
2738  {
2739  // TODO: get current working directory?
2740 
2741  // empty path implies empty list
2742  goto restore_cursor; // this will also re-paint
2743  }
2744  if(p1[strlen(p1) - 1] != '/')
2745  {
2746  WBCatString(&p1, "/*");
2747  }
2748  else
2749  {
2750  WBCatString(&p1, "*");
2751  }
2752 
2753  if(!p1 || !*p1) // in case of error
2754  {
2755  WB_ERROR_PRINT("%s - Memory allocation failure\n", __FUNCTION__);
2756 
2757  goto restore_cursor;
2758  }
2759 
2760  pV = WBAllocDirectoryList(p1);
2761 
2762  if(!pV) // in case of error
2763  {
2764  WB_ERROR_PRINT("%s - unable to get directory list for \"%s\"\n", __FUNCTION__, p1);
2765  WBFree(p1);
2766 
2767  goto restore_cursor;
2768  }
2769 
2770  WBFree(p1);
2771  p1 = NULL;
2772 
2773  // always add the '..' first, unless the path is '/'
2774  if(!p2 || p2[0] != '/' || p2[1])
2775  {
2776  strcpy(tbuf, "@.."); // directory '..'
2777  DLGAddControlListEntry(pDialogControl, tbuf, -1, ControlListIndex_INSERT_FIRST);
2778  }
2779 
2781  "%s - enumerating directory\n", __FUNCTION__);
2782 
2783  while(!WBNextDirectoryEntry(pV, tbuf + 1, sizeof(tbuf) - 1, &dw1))
2784  {
2785  if(S_ISDIR(dw1))
2786  {
2787  tbuf[0] = '@';
2788  }
2789  else if(S_ISLNK(dw1))
2790  {
2791  // TODO: determine what link's type is
2792  if(WBGetDirectoryListFileStat(pV, tbuf, &dw1))
2793  {
2794  tbuf[0] = '^'; // can't 'stat' so mark as 'symlink' (for now, maybe reject it?)
2795  }
2796  else if(S_ISDIR(dw1))
2797  {
2798  tbuf[0] = '@'; // treat symlink as a directory since it points to one
2799  }
2800  else
2801  {
2802  tbuf[0] = '~'; // assume it's a regular file
2803  }
2804  }
2805  else
2806  {
2807  tbuf[0] = '~';
2808  }
2809 
2810 // WB_WARN_PRINT("TEMPORARY - %s %d (%08xH)\n", tbuf, (int)dw1, (int)dw1);
2811 
2812  // TODO: directories are excluded
2813  DLGAddControlListEntry(pDialogControl, tbuf, -1, ControlListIndex_INSERT_LAST);
2814  }
2815 
2816 // WB_WARN_PRINT("TEMPORARY - DONE enumerating directory\n");
2817 
2819  DEBUG_DUMP_LIST(pDialogControl);
2820 
2821 restore_cursor:
2822 
2823  WBInvalidateGeom(wID, NULL, 1); // invalidate and generate expose message (always)
2824 
2825  if(pDialogControl->pOwner)
2826  {
2827  WBEndWaitCursor(pDialogControl->pOwner->wID);
2828  }
2829  else
2830  {
2831  WBEndWaitCursor(wID);
2832  }
2833 
2834  return 1;
2835  }
2836 
2837  // TODO: other stuff?
2838 
2839  // return 1; do this when it's handled by THIS proc
2840  }
2841 
2842  return list_callback(wID, pEvent); // allow base control class to handle it
2843 }
2844 
2845 
2846 static int tree_callback(Window wID, XEvent *pEvent)
2847 {
2848  int iRval = 0;
2849  Atom aNotification = None;
2850  Display *pDisplay = WBGetWindowDisplay(wID);
2851  WBDialogControl *pDialogControl = DLGGetDialogControlStruct(wID);
2852 // char tbuf[32];
2853 // int nChar = sizeof(tbuf);
2854 
2855 
2856  if(pDialogControl && pEvent->type == Expose)
2857  {
2858  return ListDoExposeEvent(&(pEvent->xexpose), pDisplay, wID, pDialogControl);
2859  }
2860 
2861  // special handling for 'destroy'
2862  if(pEvent->type == DestroyNotify &&
2863  pEvent->xdestroywindow.window == wID)
2864  {
2866  "%s - DestroyNotify\n", __FUNCTION__);
2867 
2868  WBSetWindowData(wID, 0, NULL);
2869 
2870  if(pDialogControl)
2871  {
2872  if(pDialogControl->pCaption)
2873  {
2874  WBFree(pDialogControl->pCaption);
2875  }
2876 
2877  if(pDialogControl->pPropList)
2878  {
2879  DLGCDestroyProperties(pDialogControl->pPropList);
2880  pDialogControl->pPropList = NULL; // as a matter of course
2881  }
2882 
2883  WBFree(pDialogControl);
2884  }
2885 
2886  return 1;
2887  }
2888 
2889  if(pEvent->type == FocusIn)
2890  {
2891  WB_ERROR_PRINT("TEMPORARY: %s - FocusIn should not be handled here\n", __FUNCTION__);
2892  }
2893 
2894 
2895  // TREE and LIST are similar
2896 
2897  // TODO: keystroke handling via WM_CHAR
2898 
2899 
2900  // CONTROL NOTIFICATIONS
2901 
2902  if(iRval && aNotification != None &&
2903  pDialogControl->pOwner &&
2904  pDialogControl->pDlgControlEntry)
2905  {
2906  DLGNotifyOwnerAsync(pDialogControl, aCONTROL_NOTIFY, aNotification,
2907  pDialogControl->pDlgControlEntry->iID,
2908  (long)pDialogControl, 0, 0);
2909 
2911  "%s:%d - Post Event: %08xH %08xH %08xH %pH\n", __FUNCTION__, __LINE__,
2912  (int)aCONTROL_NOTIFY, (int)aNotification,
2913  (int)pDialogControl->pDlgControlEntry->iID, pDialogControl);
2914  }
2915 
2916  return iRval; // 0 if not handled, 1 if handled
2917 }
2918 
2919 static int path_tree_callback(Window wID, XEvent *pEvent)
2920 {
2921 //Display *pDisplay = WBGetWindowDisplay(wID);
2922 WBDialogControl *pDialogControl = DLGGetDialogControlStruct(wID);
2923 char *p1;
2924 void *pV;
2925 unsigned long dw1;
2926 char tbuf[NAME_MAX + 4];
2927 
2928 
2929  if(pEvent->type == ClientMessage &&
2930  pEvent->xclient.type == aDLGC_PROP_NOTIFY)
2931  {
2933  "%s - DLGC_PROP_NOTIFY for window %d (%08xH)\n", __FUNCTION__,
2934  (int)wID, (int)wID);
2935 
2936  if(pEvent->xclient.data.l[0] == aDLGC_PATH) // TODO: include a 'DLGC_FILTER' property as well
2937  {
2939  "%s - DLGC_PROP_NOTIFY DLGC_PATH for window %d (%08xH)\n", __FUNCTION__,
2940  (int)wID, (int)wID);
2941 
2942  // property change notification for aPATH must refresh contents
2943  DLGDelControlListEntry(pDialogControl, ControlListIndex_DELETE_ALL); // delete all
2944 
2945  p1 = WBCopyString(WBDialogControlGetProperty(pDialogControl, aDLGC_PATH));
2946  if(!p1 || !*p1)
2947  {
2948  return 1; // empty path implies empty list
2949  }
2950  if(p1[strlen(p1) - 1] != '/')
2951  {
2952  WBCatString(&p1, "/*");
2953  }
2954  else
2955  {
2956  WBCatString(&p1, "*");
2957  }
2958 
2959  if(!p1 || !*p1) // in case of error
2960  {
2961  WB_ERROR_PRINT("%s - Memory allocation failure\n", __FUNCTION__);
2962  return 1;
2963  }
2964 
2965  pV = WBAllocDirectoryList(p1);
2966  WBFree(p1);
2967  p1 = NULL;
2968 
2969  if(!pV) // in case of error
2970  {
2971  WB_ERROR_PRINT("%s - unable to get directory list\n", __FUNCTION__);
2972  return 1;
2973  }
2974 
2975 // WB_WARN_PRINT("TEMPORARY - enumerating directory\n");
2976 
2977  while(!WBNextDirectoryEntry(pV, tbuf, sizeof(tbuf), &dw1))
2978  {
2979  if(S_ISDIR(dw1))
2980  {
2981  }
2982  else if(S_ISLNK(dw1))
2983  {
2984  // TODO: determine what link's type is
2985 
2986  if(WBStat(tbuf, &dw1) || !S_ISDIR(dw1))
2987  {
2988  continue;
2989  }
2990  }
2991  else
2992  {
2993  continue; // ignore regular files
2994  }
2995 
2996 // WB_WARN_PRINT("TEMPORARY - %s %d (%08xH)\n", tbuf, (int)dw1, (int)dw1);
2997 
2998  DLGAddControlListEntry(pDialogControl, tbuf, -1, ControlListIndex_INSERT_LAST);
2999  }
3000 
3001 // WB_WARN_PRINT("TEMPORARY - DONE enumerating directory\n");
3002 
3004  DEBUG_DUMP_LIST(pDialogControl);
3005 
3006  return 1;
3007  }
3008  }
3009 
3010  return tree_callback(wID, pEvent); // allow base control class to handle it
3011 }
3012 
3013 
3014 
3015 
3016 //*****************************
3017 // I N P U T H A N D L I N G
3018 //*****************************
3019 
3020 
3021 // NOTE: returns non-zero if text changes
3022 
3023 static int EditDoCharEvent(XClientMessageEvent *pEvent, Display *pDisplay,
3024  Window wID, WBDialogControl *pSelf)
3025 {
3026 WBEditControl *pPrivate = (WBEditControl *)pSelf;
3027 int iRval = 0, iACS, iKey, nChar;//, iLen;
3028 char *pBuf, *pData;
3029 WB_RECT rctTemp;
3030 
3031 
3032  iKey = pEvent->data.l[0]; // result from WBKeyEventProcessKey()
3033  iACS = pEvent->data.l[1];
3034  nChar = pEvent->data.l[2];
3035  pBuf = (char *)&(pEvent->data.l[3]);
3036 
3037  if(nChar > 0) // normal ASCII characters
3038  { // DebugLevel_Heavy
3040  "%s KEY RELEASE for KEY %d KEYCODE %d MASK=%d (%xH)\n",
3041  __FUNCTION__, iKey, ((XKeyEvent *)pEvent)->keycode,
3042  ((XKeyEvent *)pEvent)->state, ((XKeyEvent *)pEvent)->state);
3043 
3044  if(iKey == 8) // backspace
3045  {
3047  "%s - BACKSPACE key pressed, iACS=%d (%xH)\n", __FUNCTION__, iACS, iACS);
3048 
3049  if(pPrivate->xTextObject.vtable->has_select(&(pPrivate->xTextObject)))
3050  {
3051  pPrivate->xTextObject.vtable->del_select(&(pPrivate->xTextObject)); // delete selection
3052  }
3053  else
3054  {
3055  pPrivate->xTextObject.vtable->del_chars(&(pPrivate->xTextObject), -1); // delete the character to the left
3056  }
3057 
3058  // re-assign caption
3059  if(pSelf->pCaption)
3060  {
3061  WBFree(pSelf->pCaption);
3062  }
3063 
3064  pSelf->pCaption = pPrivate->xTextObject.vtable->get_text(&(pPrivate->xTextObject));
3065 
3066  iRval = 1; // text changed
3067  }
3068  else if(iKey == 13 || iKey == 10 // CR or LF (TODO: check flags to see if I should accept this)
3069  || iKey == 9) // tab (TODO: check flags to see if I should accept this)
3070  {
3071  // TODO: check for multi-line control, add line feeds as needed
3072 
3073  return 0; // LF may trigger default button, allow default handling
3074  // since the text was not changed return 0 _NOW_ so that WM_CHAR handling happens in default processing
3075  }
3076  else if(iKey == 1) // CTRL+A
3077  {
3079  "%s - CTRL+A key pressed, iACS=%d (%xH)\n", __FUNCTION__, iACS, iACS);
3080 
3081  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_CTRL) // control key will always be pressed, but no shift
3082  {
3083  rctTemp.left = rctTemp.top = 0;
3084  rctTemp.right = rctTemp.bottom = INT_MAX;
3085 
3086  pPrivate->xTextObject.vtable->set_select(&(pPrivate->xTextObject), &rctTemp);
3087  }
3088 
3089  iRval = -1; // text NOT changed
3090  }
3091  else if(iKey == 3) // CTRL+C
3092  {
3093  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_CTRL) // control key will always be pressed, but no shift
3094  {
3095  if(pPrivate->xTextObject.vtable->has_select(&(pPrivate->xTextObject)))
3096  {
3098  "%s - CTRL+C key pressed, iACS=%d (%xH)\n", __FUNCTION__, iACS, iACS);
3099 
3100  pData = pPrivate->xTextObject.vtable->get_sel_text(&(pPrivate->xTextObject), NULL);
3101 
3102  if(pData)
3103  {
3104  if(aUTF8_STRING != None)
3105  {
3106  WBSetClipboardData(pDisplay, aUTF8_STRING, 8, pData, strlen(pData) + 1);
3107  }
3108  else
3109  {
3110  WBSetClipboardData(pDisplay, aSTRING, 8, pData, strlen(pData) + 1);
3111  }
3112  WBFree(pData);
3113  pData = NULL; // by convention
3114  }
3115  else
3116  {
3117  XBell(pDisplay, -100);
3118  WB_ERROR_PRINT("TEMPORARY: NULL returned from get_sel_text()\n");
3119  }
3120  }
3121  else
3122  {
3123  XBell(pDisplay, -100);
3124  WB_ERROR_PRINT("TEMPORARY - %s - no selection, can't 'COPY'\n", __FUNCTION__);
3125  }
3126  }
3127 
3128  iRval = -1; // text NOT changed
3129  }
3130  else if(iKey == 22) // CTRL+V
3131  {
3133  "%s - CTRL+V key pressed, iACS=%d (%xH)\n", __FUNCTION__, iACS, iACS);
3134 
3135  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_CTRL) // control key will always be pressed, but no shift
3136  {
3137  Atom aType = aUTF8_STRING;
3138  int iFormat = 8;
3139  unsigned long nData = 0;
3140 
3141  if(aType == None)
3142  {
3143  aType = aSTRING;
3144  }
3145 
3146  WB_ERROR_PRINT("TEMPORARY %s - CTRL+V key pressed\n", __FUNCTION__);
3147 
3148 // pData = (char *)CHGetSelectionData(pDisplay, aCLIPBOARD, aUTF8_STRING, aUTF8_STRING, //aCLIPBOARD, aC_STRING, aSTRING,
3149 // &aType, &iFormat, &nData);
3150  pData = (char *)WBGetClipboardData(pDisplay, &aType, &iFormat, &nData);
3151 
3152  if(!pData || !nData)
3153  {
3154  if(pData)
3155  {
3156  WBFree(pData);
3157  }
3158 
3159  aType = aSTRING;
3160  pData = (char *)WBGetClipboardData(pDisplay, &aType, &iFormat, &nData);
3161  }
3162 
3163  if(pData && nData > 0)
3164  {
3165  if(!pData[nData])
3166  {
3167  nData--;
3168  }
3169  }
3170 
3171  if(pData)
3172  {
3173  // TODO: convert to correct format (ASCII)
3174  if(iFormat != 8)
3175  {
3176  if(iFormat == 8 * sizeof(wchar_t))
3177  {
3178  char *pNew = WBAlloc(sizeof(wchar_t) * (nData + 2));
3179  if(pNew)
3180  {
3181  memset(pNew, 0, sizeof(wchar_t) * (nData + 2));
3182  wcstombs(pNew, (const wchar_t *)pData, sizeof(wchar_t) * (nData + 2));
3183  }
3184 
3185  WBFree(pData);
3186  pData = pNew;
3187  }
3188  else
3189  {
3190  XBell(pDisplay, -100);
3191  WB_ERROR_PRINT("TEMPORARY - %s - clipboard format %d, can't 'PASTE'\n", __FUNCTION__, iFormat);
3192 
3193  WBFree(pData);
3194  pData = NULL;
3195  }
3196  }
3197 
3198  if(pData)
3199  {
3200  if(pPrivate->xTextObject.vtable->has_select(&(pPrivate->xTextObject)))
3201  {
3202  pPrivate->xTextObject.vtable->replace_select(&(pPrivate->xTextObject), pData, nData);
3203  }
3204  else
3205  {
3206  pPrivate->xTextObject.vtable->ins_chars(&(pPrivate->xTextObject), pData, nData);
3207  }
3208 
3209  WBFree(pData);
3210  pData = NULL; // by convention
3211  }
3212  else
3213  {
3214  XBell(pDisplay, -100);
3215  WB_ERROR_PRINT("TEMPORARY - %s - NULL pData, can't 'PASTE'\n", __FUNCTION__);
3216  }
3217  }
3218  else
3219  {
3220  XBell(pDisplay, -100);
3221  WB_ERROR_PRINT("TEMPORARY: NULL returned from WBGetClipboardData()\n");
3222  }
3223  }
3224 
3225  iRval = 1; // text changed
3226  }
3227  else if(iKey == 24) // CTRL+X
3228  {
3230  "%s - CTRL+X key pressed, iACS=%d (%xH)\n", __FUNCTION__, iACS, iACS);
3231 
3232  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_CTRL) // control key will always be pressed, but no shift
3233  {
3234  if(pPrivate->xTextObject.vtable->has_select(&(pPrivate->xTextObject)))
3235  {
3237  "%s - CTRL+C key pressed, iACS=%d (%xH)\n", __FUNCTION__, iACS, iACS);
3238 
3239  pData = pPrivate->xTextObject.vtable->get_sel_text(&(pPrivate->xTextObject), NULL);
3240 
3241  if(pData)
3242  {
3243  if(aUTF8_STRING != None)
3244  {
3245  WBSetClipboardData(pDisplay, aUTF8_STRING, 8, pData, strlen(pData) + 1);
3246  }
3247  else
3248  {
3249  WBSetClipboardData(pDisplay, aSTRING, 8, pData, strlen(pData) + 1);
3250  }
3251 
3252  WBFree(pData);
3253  pData = NULL; // by convention
3254 
3255  pPrivate->xTextObject.vtable->del_select(&(pPrivate->xTextObject)); // delete selection
3256  }
3257  else
3258  {
3259  XBell(pDisplay, -100);
3260  WB_ERROR_PRINT("TEMPORARY: NULL returned from get_sel_text()\n");
3261  }
3262  }
3263  else
3264  {
3265  XBell(pDisplay, -100);
3266  WB_ERROR_PRINT("TEMPORARY - %s - no selection, can't 'CUT'\n", __FUNCTION__);
3267  }
3268  }
3269 
3270  iRval = 1; // text changed
3271  }
3272  else if(iKey == 26) // ctrl+Z
3273  {
3275  "%s - CTRL+Z key pressed, iACS=%d (%xH)\n", __FUNCTION__, iACS, iACS);
3276 
3277  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_CTRL) // control key will always be pressed, but no shift
3278  {
3279  if(pPrivate->xTextObject.vtable->can_undo(&(pPrivate->xTextObject)))
3280  {
3281  pPrivate->xTextObject.vtable->undo(&(pPrivate->xTextObject));
3282  }
3283  else
3284  {
3285  XBell(pDisplay, -100);
3286  }
3287  }
3288  else if((iACS & WB_KEYEVENT_ACSMASK) == (WB_KEYEVENT_CTRL | WB_KEYEVENT_SHIFT)) // shift, no alt
3289  {
3290  if(pPrivate->xTextObject.vtable->can_redo(&(pPrivate->xTextObject)))
3291  {
3292  pPrivate->xTextObject.vtable->redo(&(pPrivate->xTextObject));
3293  }
3294  else
3295  {
3296  XBell(pDisplay, -100);
3297  }
3298  }
3299 
3300  iRval = 1; // text changed
3301  }
3302  else if(iKey == '\x1b') // ESC
3303  {
3305  "%s - ESC key pressed, iACS=%d (%xH)\n", __FUNCTION__, iACS, iACS);
3306 
3307  return 0; // not processed
3308  }
3309  else
3310  {
3311  if(pPrivate->xTextObject.vtable->has_select(&(pPrivate->xTextObject)))
3312  {
3313  pPrivate->xTextObject.vtable->replace_select(&(pPrivate->xTextObject), pBuf, nChar); // replace
3314  }
3315  else
3316  {
3317  // insert one or more ASCII characters
3318  pPrivate->xTextObject.vtable->ins_chars(&(pPrivate->xTextObject), pBuf, nChar);
3319  }
3320 
3321  // re-assign caption
3322  if(pSelf->pCaption)
3323  {
3324  WBFree(pSelf->pCaption);
3325  }
3326 
3327  pSelf->pCaption = pPrivate->xTextObject.vtable->get_text(&(pPrivate->xTextObject));
3328  }
3329 
3330  // update 'pCaption' with the complete text (cached value)
3331 
3332  if(pSelf->pCaption)
3333  {
3334  WBFree(pSelf->pCaption); // cached text
3335  }
3336 
3337  pSelf->pCaption = pPrivate->xTextObject.vtable->get_text(&(pPrivate->xTextObject));
3338 
3339  iRval = 1; // text changed
3340  }
3341  else
3342  {
3343  iRval = -1; // text normally doesn't change with these, but change to '1' if it does
3344 
3345  if(iACS & WB_KEYEVENT_KEYSYM)
3346  {
3347  // TODO: international, 'dead' and other KEYSYM key assignments
3348 #define KEYSYM_MATCH_CURSOR_NAME(X) (iKey == XK_##X || iKey == XK_KP_##X)
3349 
3350  if(KEYSYM_MATCH_CURSOR_NAME(Home))
3351  {
3353  "%s - Home key pressed iACS=%d (%08xH)\n", __FUNCTION__, iACS, iACS);
3354 
3355  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_SHIFT) // shift
3356  {
3357  pPrivate->xTextObject.vtable->begin_highlight(&(pPrivate->xTextObject));
3358  pPrivate->xTextObject.vtable->cursor_home(&(pPrivate->xTextObject));
3359  }
3360  else if((iACS & WB_KEYEVENT_ACSMASK) == (WB_KEYEVENT_CTRL | WB_KEYEVENT_SHIFT)) // shift+ctrl
3361  {
3362  pPrivate->xTextObject.vtable->begin_highlight(&(pPrivate->xTextObject));
3363  pPrivate->xTextObject.vtable->cursor_top(&(pPrivate->xTextObject));
3364  }
3365  else if((iACS & WB_KEYEVENT_ACSMASK) == 0) // normal
3366  {
3367  pPrivate->xTextObject.vtable->cursor_home(&(pPrivate->xTextObject));
3368  }
3369  else if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_CTRL) // ctrl+home
3370  {
3371  pPrivate->xTextObject.vtable->cursor_top(&(pPrivate->xTextObject));
3372  }
3373  }
3374  else if(KEYSYM_MATCH_CURSOR_NAME(End))
3375  {
3377  "%s - End key pressed iACS=%d (%08xH)\n", __FUNCTION__, iACS, iACS);
3378 
3379  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_SHIFT) // shift
3380  {
3381  pPrivate->xTextObject.vtable->begin_highlight(&(pPrivate->xTextObject));
3382  pPrivate->xTextObject.vtable->cursor_end(&(pPrivate->xTextObject));
3383  }
3384  else if((iACS & WB_KEYEVENT_ACSMASK) == (WB_KEYEVENT_CTRL | WB_KEYEVENT_SHIFT)) // shift+ctrl
3385  {
3386  pPrivate->xTextObject.vtable->begin_highlight(&(pPrivate->xTextObject));
3387  pPrivate->xTextObject.vtable->cursor_bottom(&(pPrivate->xTextObject));
3388  }
3389  else if((iACS & WB_KEYEVENT_ACSMASK) == 0) // normal
3390  {
3391  pPrivate->xTextObject.vtable->end_highlight(&(pPrivate->xTextObject));
3392  pPrivate->xTextObject.vtable->cursor_end(&(pPrivate->xTextObject));
3393  }
3394  else if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_CTRL) // ctrl+end
3395  {
3396  pPrivate->xTextObject.vtable->end_highlight(&(pPrivate->xTextObject));
3397  pPrivate->xTextObject.vtable->cursor_bottom(&(pPrivate->xTextObject));
3398  }
3399  }
3400  else if(KEYSYM_MATCH_CURSOR_NAME(Left))
3401  {
3403  "%s - Left key pressed iACS=%d (%08xH)\n", __FUNCTION__, iACS, iACS);
3404 
3405  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_SHIFT) // shift
3406  {
3407  pPrivate->xTextObject.vtable->begin_highlight(&(pPrivate->xTextObject));
3408  pPrivate->xTextObject.vtable->cursor_left(&(pPrivate->xTextObject));
3409  }
3410  else if((iACS & WB_KEYEVENT_ACSMASK) == (WB_KEYEVENT_CTRL | WB_KEYEVENT_SHIFT)) // shift+ctrl
3411  {
3412  // TODO: alter selection via selection method
3413  }
3414  else if((iACS & WB_KEYEVENT_ACSMASK) == 0) // normal
3415  {
3416  pPrivate->xTextObject.vtable->end_highlight(&(pPrivate->xTextObject));
3417  pPrivate->xTextObject.vtable->cursor_left(&(pPrivate->xTextObject));
3418  }
3419  else if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_CTRL) // ctrl+left
3420  {
3421  // TODO: previous word
3422  }
3423  }
3424  else if(KEYSYM_MATCH_CURSOR_NAME(Right))
3425  {
3427  "%s - Right key pressed iACS=%d (%08xH)\n", __FUNCTION__, iACS, iACS);
3428 
3429  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_SHIFT) // shift
3430  {
3431  pPrivate->xTextObject.vtable->begin_highlight(&(pPrivate->xTextObject));
3432  pPrivate->xTextObject.vtable->cursor_right(&(pPrivate->xTextObject));
3433  }
3434  else if((iACS & WB_KEYEVENT_ACSMASK) == (WB_KEYEVENT_CTRL | WB_KEYEVENT_SHIFT)) // shift+ctrl
3435  {
3436  // TODO: alter selection via selection method
3437  }
3438  else if((iACS & WB_KEYEVENT_ACSMASK) == 0) // normal
3439  {
3440  pPrivate->xTextObject.vtable->end_highlight(&(pPrivate->xTextObject));
3441  pPrivate->xTextObject.vtable->cursor_right(&(pPrivate->xTextObject));
3442  }
3443  else if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_CTRL) // ctrl+right
3444  {
3445  // TODO: next word
3446  }
3447  }
3448  else if(KEYSYM_MATCH_CURSOR_NAME(Up))
3449  {
3451  "%s - Up key pressed iACS=%d (%08xH)\n", __FUNCTION__, iACS, iACS);
3452 
3453  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_SHIFT) // shift
3454  {
3455  pPrivate->xTextObject.vtable->begin_highlight(&(pPrivate->xTextObject));
3456  pPrivate->xTextObject.vtable->cursor_up(&(pPrivate->xTextObject));
3457  }
3458  else if((iACS & WB_KEYEVENT_ACSMASK) == (WB_KEYEVENT_CTRL | WB_KEYEVENT_SHIFT)) // shift+ctrl
3459  {
3460  // TODO: alter selection via selection method
3461  XBell(pDisplay, -100);
3462  }
3463  else if((iACS & WB_KEYEVENT_ACSMASK) == 0) // normal
3464  {
3465  pPrivate->xTextObject.vtable->end_highlight(&(pPrivate->xTextObject));
3466  pPrivate->xTextObject.vtable->cursor_up(&(pPrivate->xTextObject));
3467  }
3468  else if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_CTRL) // ctrl+up
3469  {
3470  // TODO: what should this do?
3471  XBell(pDisplay, -100);
3472  }
3473  }
3474  else if(KEYSYM_MATCH_CURSOR_NAME(Down))
3475  {
3477  "%s - Down key pressed iACS=%d (%08xH)\n", __FUNCTION__, iACS, iACS);
3478 
3479  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_SHIFT) // shift
3480  {
3481  pPrivate->xTextObject.vtable->begin_highlight(&(pPrivate->xTextObject));
3482  pPrivate->xTextObject.vtable->cursor_down(&(pPrivate->xTextObject));
3483  }
3484  else if((iACS & WB_KEYEVENT_ACSMASK) == (WB_KEYEVENT_CTRL | WB_KEYEVENT_SHIFT)) // shift+ctrl
3485  {
3486  // TODO: alter selection via selection method
3487  XBell(pDisplay, -100);
3488  }
3489  else if((iACS & WB_KEYEVENT_ACSMASK) == 0) // normal
3490  {
3491  pPrivate->xTextObject.vtable->end_highlight(&(pPrivate->xTextObject));
3492  pPrivate->xTextObject.vtable->cursor_down(&(pPrivate->xTextObject));
3493  }
3494  else if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_CTRL) // ctrl+down
3495  {
3496  // TODO: what should this do?
3497  XBell(pDisplay, -100);
3498  }
3499  }
3500  else if(KEYSYM_MATCH_CURSOR_NAME(Page_Up))
3501  {
3503  "%s - Page Up key pressed iACS=%d (%08xH)\n", __FUNCTION__, iACS, iACS);
3504 
3505  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_SHIFT) // shift
3506  {
3507  pPrivate->xTextObject.vtable->begin_highlight(&(pPrivate->xTextObject));
3508  pPrivate->xTextObject.vtable->page_up(&(pPrivate->xTextObject));
3509  }
3510  else if((iACS & WB_KEYEVENT_ACSMASK) == (WB_KEYEVENT_CTRL | WB_KEYEVENT_SHIFT)) // shift+ctrl
3511  {
3512  pPrivate->xTextObject.vtable->begin_highlight(&(pPrivate->xTextObject));
3513  pPrivate->xTextObject.vtable->page_left(&(pPrivate->xTextObject));
3514  }
3515  else if((iACS & WB_KEYEVENT_ACSMASK) == 0) // normal
3516  {
3517  pPrivate->xTextObject.vtable->end_highlight(&(pPrivate->xTextObject));
3518  pPrivate->xTextObject.vtable->page_up(&(pPrivate->xTextObject));
3519  }
3520  else if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_CTRL) // ctrl+PgUp
3521  {
3522  pPrivate->xTextObject.vtable->end_highlight(&(pPrivate->xTextObject));
3523  pPrivate->xTextObject.vtable->page_left(&(pPrivate->xTextObject));
3524  }
3525  }
3526  else if(KEYSYM_MATCH_CURSOR_NAME(Page_Down))
3527  {
3529  "%s - Page Down key pressed iACS=%d (%08xH)\n", __FUNCTION__, iACS, iACS);
3530 
3531  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_SHIFT) // shift
3532  {
3533  pPrivate->xTextObject.vtable->begin_highlight(&(pPrivate->xTextObject));
3534  pPrivate->xTextObject.vtable->page_down(&(pPrivate->xTextObject));
3535  }
3536  else if((iACS & WB_KEYEVENT_ACSMASK) == (WB_KEYEVENT_CTRL | WB_KEYEVENT_SHIFT)) // shift+ctrl
3537  {
3538  pPrivate->xTextObject.vtable->begin_highlight(&(pPrivate->xTextObject));
3539  pPrivate->xTextObject.vtable->page_right(&(pPrivate->xTextObject));
3540  }
3541  else if((iACS & WB_KEYEVENT_ACSMASK) == 0) // normal
3542  {
3543  pPrivate->xTextObject.vtable->end_highlight(&(pPrivate->xTextObject));
3544  pPrivate->xTextObject.vtable->page_down(&(pPrivate->xTextObject));
3545  }
3546  else if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_CTRL) // ctrl+PgDown
3547  {
3548  pPrivate->xTextObject.vtable->end_highlight(&(pPrivate->xTextObject));
3549  pPrivate->xTextObject.vtable->page_right(&(pPrivate->xTextObject));
3550  }
3551  }
3552  else if(KEYSYM_MATCH_CURSOR_NAME(Begin)) // beginning of current line
3553  {
3555  "%s - Beginning Of Line key pressed iACS=%d (%08xH)\n", __FUNCTION__, iACS, iACS);
3556 
3557  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_SHIFT) // shift
3558  {
3559  pPrivate->xTextObject.vtable->begin_highlight(&(pPrivate->xTextObject));
3560  if(pPrivate->xTextObject.vtable->get_col(&(pPrivate->xTextObject)))
3561  {
3562  pPrivate->xTextObject.vtable->cursor_home(&(pPrivate->xTextObject));
3563  }
3564  }
3565  else if((iACS & WB_KEYEVENT_ACSMASK) == 0) // normal
3566  {
3567  pPrivate->xTextObject.vtable->end_highlight(&(pPrivate->xTextObject));
3568  if(pPrivate->xTextObject.vtable->get_col(&(pPrivate->xTextObject)))
3569  {
3570  pPrivate->xTextObject.vtable->cursor_home(&(pPrivate->xTextObject));
3571  }
3572  }
3573  }
3574  else if(KEYSYM_MATCH_CURSOR_NAME(Insert)) // toggle 'insert' mode
3575  {
3577  "%s - Insert key pressed iACS=%d (%08xH)\n", __FUNCTION__, iACS, iACS);
3578 
3579  if((iACS & WB_KEYEVENT_ACSMASK) == 0) // normal
3580  {
3581  if(pPrivate->xTextObject.vtable->get_insmode(&(pPrivate->xTextObject))
3583  {
3584  pPrivate->xTextObject.vtable->set_insmode(&(pPrivate->xTextObject), InsertMode_INSERT);
3585  }
3586  else
3587  {
3589  }
3590  }
3591  else if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_SHIFT) // SHIFT+insert (paste)
3592  {
3593  Atom aType = aUTF8_STRING;
3594  int iFormat = 8;
3595  unsigned long nData = 0;
3596 
3597  if(aType == None)
3598  {
3599  aType = aSTRING;
3600  }
3601 
3602  WB_ERROR_PRINT("TEMPORARY %s - SHIFT+insert key pressed\n", __FUNCTION__);
3603 
3604 // pData = (char *)CHGetSelectionData(pDisplay, aCLIPBOARD, aC_STRING, aC_STRING,
3605 // &aType, &iFormat, &nData);
3606  pData = (char *)WBGetClipboardData(pDisplay, &aType, &iFormat, &nData);
3607 
3608  if(pData && nData > 0)
3609  {
3610  if(!pData[nData])
3611  {
3612  nData--;
3613  }
3614  }
3615 
3616  if(pData)
3617  {
3618  // TODO: convert to correct format (ASCII)
3619  if(iFormat != 8)
3620  {
3621  if(iFormat == 8 * sizeof(wchar_t))
3622  {
3623  char *pNew = WBAlloc(sizeof(wchar_t) * (nData + 2));
3624  if(pNew)
3625  {
3626  memset(pNew, 0, sizeof(wchar_t) * (nData + 2));
3627  wcstombs(pNew, (const wchar_t *)pData, sizeof(wchar_t) * (nData + 2));
3628  }
3629 
3630  WBFree(pData);
3631  pData = pNew;
3632  }
3633  else
3634  {
3635  XBell(pDisplay, -100);
3636  WB_ERROR_PRINT("TEMPORARY - %s - clipboard format %d, can't 'PASTE'\n", __FUNCTION__, iFormat);
3637 
3638  WBFree(pData);
3639  pData = NULL;
3640  }
3641  }
3642 
3643  if(pData)
3644  {
3645  if(pPrivate->xTextObject.vtable->has_select(&(pPrivate->xTextObject)))
3646  {
3647  pPrivate->xTextObject.vtable->replace_select(&(pPrivate->xTextObject), pData, nData);
3648  }
3649  else
3650  {
3651  pPrivate->xTextObject.vtable->ins_chars(&(pPrivate->xTextObject), pData, nData);
3652  }
3653 
3654  WBFree(pData);
3655  pData = NULL; // by convention
3656  }
3657  else
3658  {
3659  XBell(pDisplay, -100);
3660  WB_ERROR_PRINT("TEMPORARY - %s - NULL pData, can't 'PASTE'\n", __FUNCTION__);
3661  }
3662  }
3663  else
3664  {
3665  XBell(pDisplay, -100);
3666  WB_ERROR_PRINT("TEMPORARY: NULL returned from WBGetClipboardData()\n");
3667  }
3668  }
3669  else if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_CTRL) // CTRL+insert (copy)
3670  {
3671  char *pData = pPrivate->xTextObject.vtable->get_sel_text(&(pPrivate->xTextObject), NULL);
3672 
3673  if(pData)
3674  {
3675  if(aUTF8_STRING != None)
3676  {
3677  WBSetClipboardData(pDisplay, aUTF8_STRING, 8, pData, strlen(pData) + 1);
3678  }
3679  else
3680  {
3681  WBSetClipboardData(pDisplay, aSTRING, 8, pData, strlen(pData) + 1);
3682  }
3683 
3684  WBFree(pData);
3685  }
3686  else
3687  {
3688  WB_ERROR_PRINT("TEMPORARY: NULL returned from get_sel_text()\n");
3689  }
3690  }
3691  }
3692  else if(KEYSYM_MATCH_CURSOR_NAME(Delete)) // delete key (keypad may use this)
3693  {
3695  "%s - Delete key pressed iACS=%d (%08xH)\n", __FUNCTION__, iACS, iACS);
3696 
3697  if((iACS & WB_KEYEVENT_ACSMASK) == WB_KEYEVENT_SHIFT) // shift+del (cut)
3698  {
3699  // perform 'CUT'
3700  }
3701  else if((iACS & WB_KEYEVENT_ACSMASK) == 0) // normal
3702  {
3703  if(pPrivate->xTextObject.vtable->has_select(&(pPrivate->xTextObject)))
3704  {
3705  pPrivate->xTextObject.vtable->del_select(&(pPrivate->xTextObject)); // delete selection
3706  }
3707  else
3708  {
3709  pPrivate->xTextObject.vtable->del_chars(&(pPrivate->xTextObject), 1); // delete the character to the right
3710  }
3711  }
3712 
3713  iRval = 1; // text changed
3714 
3715  // re-assign caption
3716  if(pSelf->pCaption)
3717  {
3718  WBFree(pSelf->pCaption);
3719  }
3720 
3721  pSelf->pCaption = pPrivate->xTextObject.vtable->get_text(&(pPrivate->xTextObject));
3722 
3723  iRval = 1; // text changed
3724  }
3725 #undef KEYSYM_MATCH_CURSOR_NAME
3726  else
3727  {
3728  // is it a cursor key? let's find out
3730  "%s - CURSOR KEY? %d (%08xH) %d (%08xH)\n",
3731  __FUNCTION__, iKey, iKey, iACS, iACS);
3732 
3733  iRval = 0; // force this for unknown key combinations
3734  }
3735  }
3736  }
3737 
3738  return iRval;
3739 }
3740 
3741 static int EditDoPointerEvent(XClientMessageEvent *pEvent, Display *pDisplay,
3742  Window wID, WBDialogControl *pSelf)
3743 {
3744 WBEditControl *pPrivate = (WBEditControl *)pSelf;
3745 int iACS;
3746 int iX, iY;
3747 
3748 
3749  if(pEvent->type != ClientMessage ||
3750  pEvent->message_type != aWM_POINTER)
3751  {
3752  return 0; // sanity check (temporary, remove later?)
3753  }
3754 
3755 
3756  // left-click - position cursor, cancel selection
3757  // shift-left-click - select from cursor to THIS point
3758  // (other modifiers, ignore the modifier key)
3759  // right-click - drop-down edit menu
3760  // middle click - select word? paste?
3761  // scroll wheel up/down - should be handled by scrollbar thingy
3762  // left-drag - select from starting position
3763  // right-drag - drag/drop?
3764  // middle-drag - ?
3765 
3766  // TODO: check which button I clicked - it's data.l[1] - WB_POINTER_BUTTON1 through WB_POINTER_BUTTON5 (bitmask)
3767 
3768  iACS = pEvent->data.l[2];
3769 
3770  iX = pEvent->data.l[3];
3771  iY = pEvent->data.l[4];
3772 
3773  switch(pEvent->data.l[0])
3774  {
3775  case WB_POINTER_CLICK:
3776 // WB_ERROR_PRINT("TEMPORARY - %s - WB_POINTER_CLICK\n", __FUNCTION__);
3777  pPrivate->xTextObject.vtable->mouse_click(&(pPrivate->xTextObject), iX, iY, pEvent->data.l[1], iACS);
3778  break;
3779 
3780  case WB_POINTER_DBLCLICK:
3781  WB_ERROR_PRINT("TEMPORARY - %s - WB_POINTER_DBLCLICK\n", __FUNCTION__);
3782  break;
3783 
3784  case WB_POINTER_DRAG:
3785 // WB_ERROR_PRINT("TEMPORARY - %s - WB_POINTER_DRAG\n", __FUNCTION__);
3786  pPrivate->xTextObject.vtable->begin_mouse_drag(&(pPrivate->xTextObject));
3787  return wID; // do this to support pointer drag
3788 
3789  case WB_POINTER_DROP:
3790 // WB_ERROR_PRINT("TEMPORARY - %s - WB_POINTER_DROP\n", __FUNCTION__);
3791  pPrivate->xTextObject.vtable->end_mouse_drag(&(pPrivate->xTextObject));
3792  break;
3793 
3794  case WB_POINTER_MOVE:
3795 // WB_ERROR_PRINT("TEMPORARY - %s - WB_POINTER_MOVE\n", __FUNCTION__);
3796  pPrivate->xTextObject.vtable->mouse_click(&(pPrivate->xTextObject), iX, iY, 0, 0); // mouse motion only
3797  break;
3798 
3799  case WB_POINTER_CANCEL:
3800  WB_ERROR_PRINT("TEMPORARY - %s - WB_POINTER_CANCEL\n", __FUNCTION__);
3801  break;
3802  case WB_POINTER_SCROLLUP:
3803  WB_ERROR_PRINT("TEMPORARY - %s - WB_POINTER_SCROLLUP\n", __FUNCTION__);
3804  break;
3805  case WB_POINTER_SCROLLDOWN:
3806  WB_ERROR_PRINT("TEMPORARY - %s - WB_POINTER_SCROLLDOWN\n", __FUNCTION__);
3807  break;
3808 
3809 
3810  default:
3811 
3812  WB_ERROR_PRINT("TEMPORARY - %s - unhandled mousie message\n", __FUNCTION__);
3813  break;
3814  }
3815 
3816  return 0; // for now (necessary to avoid warnings, and NOT do bad stuff)
3817 }
3818 
3819 
3820 static int ListDoCharEvent(XClientMessageEvent *pEvent, Display *pDisplay,
3821  Window wID, WBDialogControl *pSelf)
3822 {
3823 int iRval = 0, iACS, iKey, nChar;//, iLen;
3824 //char *pBuf;
3825 
3826 
3827  iKey = pEvent->data.l[0]; // result from WBKeyEventProcessKey()
3828  iACS = pEvent->data.l[1];
3829  nChar = pEvent->data.l[2];
3830 // pBuf = (char *)&(pEvent->data.l[3]);
3831 
3832  if(nChar > 0) // normal ASCII characters
3833  {
3835  "%s KEY RELEASE for KEY %d KEYCODE %d MASK=%d (%xH)\n",
3836  __FUNCTION__, iKey, ((XKeyEvent *)pEvent)->keycode,
3837  ((XKeyEvent *)pEvent)->state, ((XKeyEvent *)pEvent)->state);
3838 
3839  return 0; // for now
3840 #if 0
3841  if(iKey == 8) // backspace
3842  {
3843  // TODO: delete char before cursor or highlighted text
3844  // for now just delete the LAST character
3845 
3846  iLen = strlen(pSelf->pCaption);
3847 
3848  if(iLen > 0)
3849  {
3850  pSelf->pCaption[iLen - 1] = 0;
3851  }
3852 
3854  "%s - BACKSPACE key pressed\n", __FUNCTION__);
3855  }
3856  else if(iKey == 13 || iKey == 10 // CR or LF (TODO: check flags to see if I should accept this)
3857  || iKey == 9) // tab (TODO: check flags to see if I should accept this)
3858  {
3859  // TODO: check for multi-line control, add line feeds as needed
3860 
3861  return 0; // LF may trigger default button, allow default handling
3862  // since the text was not changed return 0 _NOW_ so that WM_CHAR handling happens in default processing
3863  }
3864  else
3865  {
3866  // TODO: insert or concatenate? for now concatenate
3867  WBCatString(&(pSelf->pCaption), pBuf);
3868  }
3869 
3870  iRval = 1; // text changed
3871 #endif // 0
3872  }
3873  else
3874  {
3875  if(iACS & WB_KEYEVENT_KEYSYM)
3876  {
3877  // TODO: international, 'dead' and other KEYSYM key assignments
3878 #define KEYSYM_MATCH_CURSOR_NAME(X) (iKey == XK_##X || iKey == XK_KP_##X)
3879 
3880  if(KEYSYM_MATCH_CURSOR_NAME(Home))
3881  {
3883  "%s - Home key pressed\n", __FUNCTION__);
3884  }
3885  else if(KEYSYM_MATCH_CURSOR_NAME(End))
3886  {
3888  "%s - End key pressed\n", __FUNCTION__);
3889  }
3890  else if(KEYSYM_MATCH_CURSOR_NAME(Left))
3891  {
3893  "%s - Left key pressed\n", __FUNCTION__);
3894  }
3895  else if(KEYSYM_MATCH_CURSOR_NAME(Right))
3896  {
3898  "%s - Right key pressed\n", __FUNCTION__);
3899  }
3900  else if(KEYSYM_MATCH_CURSOR_NAME(Up))
3901  {
3903  "%s - Up key pressed\n", __FUNCTION__);
3904  }
3905  else if(KEYSYM_MATCH_CURSOR_NAME(Down))
3906  {
3908  "%s - Down key pressed\n", __FUNCTION__);
3909  }
3910  else if(KEYSYM_MATCH_CURSOR_NAME(Page_Up))
3911  {
3913  "%s - Page Up key pressed\n", __FUNCTION__);
3914  }
3915  else if(KEYSYM_MATCH_CURSOR_NAME(Page_Down))
3916  {
3918  "%s - Page Down key pressed\n", __FUNCTION__);
3919  }
3920  else if(KEYSYM_MATCH_CURSOR_NAME(Begin)) // beginning of current line
3921  {
3923  "%s - Beginning Of Line key pressed\n", __FUNCTION__);
3924  }
3925  else if(KEYSYM_MATCH_CURSOR_NAME(Insert)) // toggle 'insert' mode
3926  {
3928  "%s - Insert key pressed\n", __FUNCTION__);
3929  }
3930  else if(KEYSYM_MATCH_CURSOR_NAME(Delete)) // delete key (keypad may use this)
3931  {
3933  "%s - Delete key pressed\n", __FUNCTION__);
3934 
3935  // TODO: delete character under cursor
3936 
3937 // iRval = 1; // text changed
3938  iRval = 0; // for now...
3939  }
3940 #undef KEYSYM_MATCH_CURSOR_NAME
3941  else
3942  {
3943  // is it a cursor key? let's find out
3945  "%s - CURSOR KEY? %d (%08xH) %d (%08xH)\n",
3946  __FUNCTION__, iKey, iKey, iACS, iACS);
3947 
3948  iRval = 0; // force this for unknown key combinations
3949  }
3950  }
3951  }
3952 
3953  return iRval;
3954 }
3955 
3956 
3957 
3958 
3959 
3960 //*************
3961 // E X P O S E
3962 //*************
3963 
3964 
3965 static int StaticDoExposeEvent(XExposeEvent *pEvent, Display *pDisplay,
3966  Window wID, WBDialogControl *pSelf)
3967 {
3968 int iHPos, iVPos, iType;
3969 XWindowAttributes xwa; /* Temp Get Window Attribute struct */
3970 XFontSet fontSet;
3971 XCharStruct xBounds;
3972 GC gc; // = WBGetWindowDefaultGC(wID);
3973 WB_GEOM geomPaint, geomBorder;
3974 
3975 
3977  "%s - Expose %d (%08xH)\n", __FUNCTION__, (int)wID, (int)wID);
3978 
3979  if (XGetWindowAttributes(pDisplay, wID, &xwa) == 0)
3980  {
3981  WB_WARN_PRINT("%s - * BUG * line %d\n", __FUNCTION__, __LINE__);
3982  return 0;
3983  }
3984 
3985  fontSet = WBGetWindowFontSet(wID);
3986 
3987  if(fontSet == None)
3988  {
3989  // TODO: get font from dialog info
3990  WB_WARN_PRINT("%s - * BUG * line %d\n", __FUNCTION__, __LINE__);
3991  return 0;
3992  }
3993 
3994  iHPos = WBFontSetAvgCharWidth(pDisplay, fontSet); // average char width for text border (TODO: RTL text ??)
3995  xBounds = WBFontSetMaxBounds(pDisplay, fontSet);
3996 
3997 
3998  // get graphics context copy and begin painting
3999  gc = WBBeginPaint(wID, pEvent, &geomPaint);
4000  if(!gc)
4001  {
4002  WB_WARN_PRINT("%s - * BUG * line %d\n", __FUNCTION__, __LINE__);
4003  return 0;
4004  }
4005 
4006  iType = pSelf->ulFlags & STATIC_TYPEMASK; // different types paint differently
4007 
4008  //------------------
4009  // ERASE BACKGROUND
4010  //------------------
4011 
4012  WBClearWindow(wID, gc);
4013 
4014  // 3D border
4015  if(iType == STATIC_Frame)
4016  {
4017  geomBorder.x = 0;
4018  geomBorder.y = xBounds.ascent + xBounds.descent + 2;
4019  geomBorder.width = xwa.width - geomBorder.x;
4020  geomBorder.height = xwa.height - geomBorder.y;
4021 
4022  // this text will apear in the upper left corner (sort of) of the border rectangle
4023 
4024  iVPos = xwa.border_width; // top of text at top of window
4025  iVPos += xBounds.ascent; // base of text
4026  }
4027  else
4028  {
4029  geomBorder.x = 0;
4030  geomBorder.y = 0;
4031  geomBorder.width = xwa.width - geomBorder.x;
4032  geomBorder.height = xwa.height - geomBorder.y;
4033 
4034  // vertically centered text (includes if it has a 3D border, below)
4035  iVPos = xBounds.ascent + xBounds.descent; // font height
4036  iVPos = (xwa.height - iVPos) >> 1; // half of the difference, now the top of text
4037  iVPos += xBounds.ascent; // add ascent back (base of text)
4038  }
4039 
4040  if(pSelf->ulFlags & STATIC_3DBorder)
4041  {
4043  {
4044  WBDrawBorderRect(pDisplay, wID, gc, &geomBorder,
4045  BlackPixel(pDisplay, DefaultScreen(pDisplay)));
4046 
4047  geomBorder.x++;
4048  geomBorder.y++;
4049  geomBorder.height -= 2;
4050  geomBorder.width -= 2;
4051  }
4052 
4053  WBDraw3DBorderRect(pDisplay, wID, gc, &geomBorder,
4054  pSelf->clrBD2.pixel, pSelf->clrBD3.pixel);
4055 
4056  }
4057 
4058  // painting the window text
4059 
4060  XSetForeground(pDisplay, gc, pSelf->clrFG.pixel);
4061 
4062  // where I put the text matters based on the control type (see iHPos, iVPos)
4063  if(iType == STATIC_Icon || iType == STATIC_Image)
4064  {
4065  Pixmap pixmap = ((struct _WB_PUSHBUTTON_CONTROL_ *)pSelf)->pixmap;
4066  Pixmap pixmap2 = ((struct _WB_PUSHBUTTON_CONTROL_ *)pSelf)->pixmap2;
4067 
4068  // TODO: get the image's geometry so I can properly center it (etc.)
4069 
4070  if(pixmap != None) // for now assume 36x36 icon only
4071  {
4072  if(pixmap2 != None) // an icon mask exists
4073  {
4074  GC gc2 = WBGetWindowCopyGC2(wID, gc);
4075 
4076  if(gc2 == None)
4077  {
4078  gc2 = WBGetWindowCopyGC(wID);
4079  }
4080 
4081  if(gc2 != None) // painting the icon the way I think it should be...
4082  {
4083  XSetClipOrigin(pDisplay, gc2, geomBorder.x + 2, geomBorder.y + 2);
4084  XSetClipMask(pDisplay, gc2, pixmap2);
4085  XCopyArea(pDisplay, pixmap, wID, gc2, 0, 0, 36, 36, geomBorder.x + 2, geomBorder.y + 2);
4086  XFreeGC(pDisplay, gc2);
4087  }
4088  else
4089  {
4090  WB_WARN_PRINT("%s - WARNING: unable to create GC copy to use bitmask to paint icon\n", __FUNCTION__);
4091  XCopyArea(pDisplay, pixmap, wID, gc, 0, 0, 36, 36, geomBorder.x + 2, geomBorder.y + 2);
4092  }
4093  }
4094  else
4095  {
4096  XCopyArea(pDisplay, pixmap, wID, gc, 0, 0, 36, 36, geomBorder.x + 2, geomBorder.y + 2);
4097  }
4098  }
4099  }
4100  else if(pSelf->pCaption)
4101  {
4102  WB_RECT rctText;
4103 
4104  rctText.left = geomBorder.x + iHPos;
4105  rctText.right = rctText.left + geomBorder.width - 2 * iHPos;
4106  rctText.top = geomBorder.y + xBounds.ascent / 2;
4107  rctText.bottom = rctText.top + geomBorder.height - xBounds.ascent;
4108 
4109  DTDrawMultiLineText(fontSet, pSelf->pCaption, pDisplay, gc, wID, DEFAULT_STATIC_TAB_WIDTH, 0,
4111  }
4112 
4113  // by convention, restore original objects/state
4114 
4116  XSetForeground(pDisplay, gc, WBGetWindowFGColor(wID)); // restore it at the end
4118 
4119  WBEndPaint(wID, gc);
4120 
4121  return 1; // processed
4122 }
4123 
4124 
4125 static int EditDoExposeEvent(XExposeEvent *pEvent, Display *pDisplay,
4126  Window wID, WBDialogControl *pSelf)
4127 {
4128 WBEditControl *pPrivate = (WBEditControl *)pSelf;
4129 XWindowAttributes xwa; /* Temp Get Window Attribute struct */
4130 XFontSet fontSet;
4131 GC gc; // = WBGetWindowDefaultGC(wID);
4132 WB_GEOM geomPaint, geomBorder;
4133 
4134 
4136  "%s - Expose %d (%08xH)\n", __FUNCTION__, (int)wID, (int)wID);
4137 
4138  if (XGetWindowAttributes(pDisplay, wID, &xwa) == 0)
4139  {
4140  WB_WARN_PRINT("%s - * BUG * line %d\n", __FUNCTION__, __LINE__);
4141  return 0;
4142  }
4143 
4144  fontSet = WBGetWindowFontSet(wID);
4145 
4146  if(fontSet == None)
4147  {
4148  // TODO: get font from dialog info
4149  WB_WARN_PRINT("%s - * BUG * line %d\n", __FUNCTION__, __LINE__);
4150  return 0;
4151  }
4152 
4153 
4154  // get graphics context copy and begin painting
4155  gc = WBBeginPaint(wID, pEvent, &geomPaint);
4156  if(!gc)
4157  {
4158  WB_WARN_PRINT("%s - * BUG * line %d\n", __FUNCTION__, __LINE__);
4159  return 0;
4160  }
4161 
4162  // font setup
4163  WBClearWindow(wID, gc);
4164 
4165  geomBorder.x = 0;
4166  geomBorder.y = 0;
4167  geomBorder.width = xwa.width - geomBorder.x;
4168  geomBorder.height = xwa.height - geomBorder.y;
4169 
4170  // TODO: scrollbars, 'display window' scroll position within physical display
4171 
4172 // TODO: uncomment these when iVPos needs to be used - linux gcc warning avoidance
4173 // // vertically centered text (for single-line version)
4174 // iVPos = pFont->max_bounds.ascent + pFont->max_bounds.descent; // font height
4175 // iVPos = (xwa.height - iVPos) >> 1; // half of the difference - top of text
4176 // iVPos += pFont->max_bounds.ascent;
4177 
4178  if(pSelf->pOwner && pSelf->pDlgControlEntry) //pSelf->ulFlags & STATIC_3DBorder)
4179  {
4181  {
4182  WBDraw3DBorderRect(pDisplay, wID, gc, &geomBorder,
4183  pSelf->clrBD2.pixel, pSelf->clrBD3.pixel);
4184  }
4185  else // draw a thin, 2D border
4186  {
4187  WBDrawBorderRect(pDisplay, wID, gc, &geomBorder,
4188  pSelf->clrBG.pixel);
4189  }
4190  }
4191 
4192 // WB_ERROR_PRINT("TEMPORARY: %s - caption is \"%s\"\n", __FUNCTION__, pSelf->pCaption);
4193 
4194  geomBorder.x += 4;
4195  geomBorder.y += 4;
4196  geomBorder.width -= 8;
4197  geomBorder.height -= 8;
4198 
4199  XSetForeground(pDisplay, gc, pSelf->clrFG.pixel);
4200 
4201  pPrivate->xTextObject.vtable->do_expose(&(pPrivate->xTextObject), pDisplay, wID,
4202  gc, &geomPaint, &geomBorder, fontSet);
4203 
4204 
4205 #if 0
4206 
4207  // painting the window text
4208 
4209  XSetForeground(pDisplay, gc, pSelf->clrFG.pixel);
4210 
4211  if(pSelf->pCaption) // use TEXT not caption
4212  {
4213  const char *szText = pSelf->pCaption;
4214  int iU1=0, iU2=0;
4215 
4216 // if(i1 == pSelf->iSelected) // text selection
4217 // {
4218 // XSetForeground(pDisplay, gc, pSelf->clrABG.pixel);
4219 // XSetBackground(pDisplay, gc, pSelf->clrABG.pixel);
4220 //
4221 // XFillRectangle(pDisplay, wID, gc, pItem->iPosition, pSelf->iY, pItem->iTextWidth, pSelf->iHeight + 2);
4222 //
4223 // XSetForeground(pDisplay, gc, pSelf->clrAFG.pixel);
4224 // }
4225 
4226 #if 0
4227  if(strchr(szText, '_')) // underline in text? TODO: use this info to set hotkey
4228  {
4229  char *p1;
4230  strcpy(tbuf, szText);
4231  p1 = tbuf;
4232  while(*p1)
4233  {
4234  if(*p1 == '_')
4235  {
4236  *p1 = 0;
4237 
4238  if(p1 == tbuf)
4239  iU1 = 0;
4240  else
4241  iU1 = XTextWidth(pFont, tbuf, p1 - tbuf);
4242 
4243  if(p1[1])
4244  {
4245  iU2 = XTextWidth(pFont, p1, 1);
4246  strcpy(p1, p1 + 1);
4247  }
4248  else
4249  iU2 = iU1; // shouldn't happen
4250  }
4251  p1++;
4252  }
4253 
4254  szText = tbuf;
4255  }
4256 #endif // 0
4257 
4258  // TODO: use DTDrawSingleLineText or DTDrawMultiLineText instead
4259 
4260  if(*szText)
4261  {
4262  XDrawString(pDisplay, wID, gc, iHPos, iVPos, szText, strlen(szText));
4263  }
4264 
4265 // if(i1 == pSelf->iSelected) // selected item
4266 // {
4267 // XSetForeground(pDisplay, gc, clrMenuFG.pixel);
4268 // XSetBackground(pDisplay, gc, clrMenuBG.pixel);
4269 // }
4270  }
4271 
4272 #endif // 0
4273 
4274  // by convention, restore original objects/state
4275 
4277  XSetForeground(pDisplay, gc, WBGetWindowFGColor(wID)); // restore it at the end
4279 
4280  WBEndPaint(wID, gc);
4281 
4282  return 1; // processed
4283 }
4284 
4285 
4286 static int ButtonDoExposeEvent(XExposeEvent *pEvent, Display *pDisplay,
4287  Window wID, WBDialogControl *pSelf)
4288 {
4289 int i2, iHPos;
4290 XWindowAttributes xwa; /* Temp Get Window Attribute struct */
4291 XFontSet fontSet;
4292 XCharStruct xBounds;
4293 GC gc; // = WBGetWindowDefaultGC(wID);
4294 WB_GEOM geomPaint, geomBorder;
4295 
4296 
4298  "%s - Expose %d (%08xH)\n", __FUNCTION__, (int)wID, (int)wID);
4299 
4300  if (XGetWindowAttributes(pDisplay, wID, &xwa) == 0)
4301  {
4302  WB_WARN_PRINT("%s - * BUG * line %d\n", __FUNCTION__, __LINE__);
4303  return 0;
4304  }
4305 
4306  fontSet = WBGetWindowFontSet(wID);
4307 
4308  if(fontSet == None)
4309  {
4310  // TODO: get font from dialog info
4311  WB_WARN_PRINT("%s - * BUG * line %d\n", __FUNCTION__, __LINE__);
4312  return 0;
4313  }
4314 
4315  xBounds = WBFontSetMaxBounds(pDisplay, fontSet);
4316 
4317  // get graphics context copy and begin painting
4318  gc = WBBeginPaint(wID, pEvent, &geomPaint);
4319  if(!gc)
4320  {
4321  WB_WARN_PRINT("%s - * BUG * line %d\n", __FUNCTION__, __LINE__);
4322  return 0;
4323  }
4324 
4325  WBClearWindow(wID, gc);
4326 
4327  // paint the 3D-looking border
4328 
4329  geomBorder.x = xwa.border_width;
4330  geomBorder.y = xwa.border_width;
4331  geomBorder.width = xwa.width - xwa.border_width;
4332  geomBorder.height = xwa.height - xwa.border_width;
4333 
4335  {
4336  WBDrawBorderRect(pDisplay, wID, gc, &geomBorder,
4337  BlackPixel(pDisplay, DefaultScreen(pDisplay)));
4338  geomBorder.x++;
4339  geomBorder.y++;
4340  geomBorder.height -= 2;
4341  geomBorder.width -= 2;
4342  }
4343 
4344  // note - if a button is 'pressed' I draw the colors inverted
4345 
4347  {
4348  WBDraw3DBorderRect(pDisplay, wID, gc, &geomBorder,
4349  pSelf->clrBD3.pixel, pSelf->clrBD2.pixel);
4350  }
4351  else
4352  {
4353  WBDraw3DBorderRect(pDisplay, wID, gc, &geomBorder,
4354  pSelf->clrBD2.pixel, pSelf->clrBD3.pixel);
4355  }
4356 
4357  // painting the window text
4358  i2 = WBFontSetAvgCharWidth(pDisplay, fontSet); // average char width for text border (TODO: RTL text ??)
4359  iHPos = xwa.border_width + i2; // position of beginning of text (left side + space)
4360 
4361 #if 0
4362  iVPos = pFont->max_bounds.ascent + pFont->max_bounds.descent; // font height
4363  iVPos = (xwa.height - iVPos) >> 1; // half of the difference - top of text
4364  iVPos += pFont->max_bounds.ascent;
4365 #endif // 0
4366 
4367  XSetForeground(pDisplay, gc, pSelf->clrFG.pixel);
4368 
4369  if(pSelf->pCaption)
4370  {
4371  WB_RECT rctText;
4372  const char *szText = pSelf->pCaption;
4373 
4374  XSetBackground(pDisplay, gc, pSelf->clrABG.pixel);
4375  XSetForeground(pDisplay, gc, pSelf->clrAFG.pixel);
4376 
4377  rctText.left = geomBorder.x + iHPos;
4378  rctText.right = rctText.left + geomBorder.width - 2 * iHPos;
4379  rctText.top = geomBorder.y + xBounds.ascent / 2
4380  - xBounds.descent / 2; // better centering
4381  rctText.bottom = rctText.top + geomBorder.height - xBounds.ascent;
4382 
4383  DTDrawMultiLineText(fontSet, szText, pDisplay, gc, wID, DEFAULT_BUTTON_TAB_WIDTH, 0,
4385  }
4386 
4387  // by convention, restore original objects/state
4388 
4390  XSetForeground(pDisplay, gc, WBGetWindowFGColor(wID)); // restore it at the end
4392 
4393  WBEndPaint(wID, gc);
4394 
4395  return 1; // processed
4396 }
4397 
4398 
4399 static int PushButtonDoExposeEvent(XExposeEvent *pEvent, Display *pDisplay,
4400  Window wID, WBDialogControl *pSelf)
4401 {
4402 int i2, iHPos;
4403 XWindowAttributes xwa; /* Temp Get Window Attribute struct */
4404 XFontSet fontSet;
4405 XCharStruct xBounds;
4406 GC gc; // = WBGetWindowDefaultGC(wID);
4407 WB_GEOM geomPaint, geomBorder;
4408 
4409 
4411  "%s - Expose %d (%08xH)\n", __FUNCTION__, (int)wID, (int)wID);
4412 
4413  if(XGetWindowAttributes(pDisplay, wID, &xwa) == 0)
4414  {
4415  WB_WARN_PRINT("%s - * BUG * line %d\n", __FUNCTION__, __LINE__);
4416  return 0;
4417  }
4418 
4419  fontSet = WBGetWindowFontSet(wID); // I assigned the "bold font" to this on create
4420 
4421  if(fontSet == None)
4422  {
4423  // TODO: get font from dialog info
4424  WB_ERROR_PRINT("%s - NO FONT, line %d\n", __FUNCTION__, __LINE__);
4425  return 0;
4426  }
4427 
4428  xBounds = WBFontSetMaxBounds(pDisplay, fontSet);
4429 
4430  // get graphics context copy and begin painting
4431  gc = WBBeginPaint(wID, pEvent, &geomPaint);
4432 
4433  if(!gc)
4434  {
4435  WB_WARN_PRINT("%s - * BUG * line %d\n", __FUNCTION__, __LINE__);
4436 
4437  return 0;
4438  }
4439 
4440  WBClearWindow(wID, gc);
4441 
4442  // paint the 3D-looking border
4443 
4444  geomBorder.x = xwa.border_width;
4445  geomBorder.y = xwa.border_width;
4446  geomBorder.width = xwa.width - xwa.border_width;
4447  geomBorder.height = xwa.height - xwa.border_width;
4448 
4449  if(pSelf->pDlgControlEntry->iFlags & WBDialogEntry_DEFAULT) // I am the default pushbutton
4450  {
4451  WBDrawBorderRect(pDisplay, wID, gc, &geomBorder,
4452  pSelf->clrBD3.pixel);
4453 
4454  geomBorder.x++;
4455  geomBorder.y++;
4456  geomBorder.height -= 2;
4457  geomBorder.width -= 2;
4458  }
4459 
4461  {
4462  WBDrawBorderRect(pDisplay, wID, gc, &geomBorder,
4463  BlackPixel(pDisplay, DefaultScreen(pDisplay)));
4464 
4465  geomBorder.x++;
4466  geomBorder.y++;
4467  geomBorder.height -= 2;
4468  geomBorder.width -= 2;
4469  }
4470 
4471  // note - if a button is 'pressed' I draw the colors inverted
4472 
4474  {
4475  WBDraw3DBorderRect(pDisplay, wID, gc, &geomBorder,
4476  pSelf->clrBD3.pixel, pSelf->clrBD2.pixel);
4477  }
4478  else
4479  {
4480  WBDraw3DBorderRect(pDisplay, wID, gc, &geomBorder,
4481  pSelf->clrBD2.pixel, pSelf->clrBD3.pixel);
4482  }
4483 
4484  // painting the window text
4485 
4486  i2 = WBFontSetAvgCharWidth(pDisplay, fontSet); // average char width for text border (TODO: RTL text ??)
4487  iHPos = xwa.border_width + i2; // position of beginning of text (left side + space)
4488 #if 0
4489  iVPos = pFont->max_bounds.ascent + pFont->max_bounds.descent; // font height
4490  iVPos = (xwa.height - iVPos) >> 1; // half of the difference - top of text
4491  iVPos += pFont->max_bounds.ascent;
4492 #endif // 0
4493 
4494  XSetForeground(pDisplay, gc, pSelf->clrFG.pixel);
4495 
4496  // TODO: icon button (rather than text) or combination icon/text
4497 
4498  if(pSelf->pCaption)
4499  {
4500  WB_RECT rctText;
4501  const char *szText = pSelf->pCaption;
4502 
4503  XSetBackground(pDisplay, gc, pSelf->clrABG.pixel);
4504  XSetForeground(pDisplay, gc, pSelf->clrAFG.pixel);
4505 
4506  rctText.left = geomBorder.x + iHPos;
4507  rctText.right = rctText.left + geomBorder.width - 2 * iHPos;
4508  rctText.top = geomBorder.y + xBounds.ascent / 2
4509  - xBounds.descent / 2; // better centering
4510  rctText.bottom = rctText.top + geomBorder.height - xBounds.ascent;
4511 
4512  DTDrawMultiLineText(fontSet, szText, pDisplay, gc, wID, DEFAULT_BUTTON_TAB_WIDTH, 0, &rctText,
4514  }
4515 
4516  // by convention, restore original objects/state
4517 
4519  XSetForeground(pDisplay, gc, WBGetWindowFGColor(wID)); // restore it at the end
4521 
4522  WBEndPaint(wID, gc);
4523 
4524  return 1; // processed
4525 }
4526 
4527 
4528 static int ListDoExposeEvent(XExposeEvent *pEvent, Display *pDisplay,
4529  Window wID, WBDialogControl *pSelf)
4530 {
4531 int i1, iVScrollWidth, iHScrollHeight;
4532 int nHeight, nItemHeight;
4533 XWindowAttributes xwa; /* Temp Get Window Attribute struct */
4534 XFontSet fontSet;
4535 XCharStruct xBounds;
4536 GC gc;
4537 Region rgnTemp;
4538 WB_GEOM geomPaint, geomBorder;
4539 WB_SCROLLINFO *pScrollInfo;
4540 LISTINFO *pListInfo;
4541 WB_DIALOG_PROP *pProp;
4542 
4543 
4545  "%s - Expose %d (%08xH)\n", __FUNCTION__, (int)wID, (int)wID);
4546 
4547  if(XGetWindowAttributes(pDisplay, wID, &xwa) == 0)
4548  {
4549  WB_WARN_PRINT("%s - * BUG * line %d\n", __FUNCTION__, __LINE__);
4550  return 0;
4551  }
4552 
4553  fontSet = WBGetWindowFontSet(wID);
4554 
4555  if(fontSet == None)
4556  {
4557  // TODO: get font from dialog info
4558  WB_WARN_PRINT("%s - * BUG * line %d\n", __FUNCTION__, __LINE__);
4559  return 0;
4560  }
4561 
4562  xBounds = WBFontSetMaxBounds(pDisplay, fontSet);
4563 
4564  // get graphics context copy and begin painting
4565  gc = WBBeginPaint(wID, pEvent, &geomPaint);
4566  if(!gc)
4567  {
4568  WB_WARN_PRINT("%s - * BUG * line %d\n", __FUNCTION__, __LINE__);
4569  return 0;
4570  }
4571 
4572 
4573  geomBorder.x = 0;
4574  geomBorder.y = 0;
4575  geomBorder.width = xwa.width - geomBorder.x;
4576  geomBorder.height = xwa.height - geomBorder.y;
4577 
4579  {
4580  WBDrawBorderRect(pDisplay, wID, gc, &geomBorder,
4581  BlackPixel(pDisplay, DefaultScreen(pDisplay)));
4582  }
4583  else
4584  {
4585  // use the background color for the dialog window itself
4586  WBDrawBorderRect(pDisplay, wID, gc, &geomBorder,
4587  pSelf->pOwner ? pSelf->pOwner->clrBG.pixel : pSelf->clrABG.pixel);
4588  }
4589 
4590  geomBorder.x++;
4591  geomBorder.y++;
4592  geomBorder.height -= 2;
4593  geomBorder.width -= 2;
4594 
4595  // calculate a few things
4596 
4597  iVScrollWidth = WBTextWidth(fontSet, "X", 1) * 2 + 4; // width of vertical scrollbar
4598  iHScrollHeight = xBounds.ascent + xBounds.descent + 4;
4599 
4601 
4602  if(pProp)
4603  {
4604  pListInfo = (LISTINFO *)pProp->pVal;
4605  }
4606  else
4607  {
4608  pListInfo = NULL;
4609 
4610  WBClearWindow(wID, gc);
4611 // XClearWindow(pDisplay, wID); // only necessary when there's no list info
4612  }
4613 
4614  // border
4615  WBDraw3DBorderRect(pDisplay, wID, gc, &geomBorder,
4616  pSelf->clrBD2.pixel, pSelf->clrBD3.pixel);
4617 
4618  // again reduce the size of the border rectangle by 1 pixel on all sides
4619  geomBorder.x++;
4620  geomBorder.y++;
4621  geomBorder.height -= 2;
4622  geomBorder.width -= 2;
4623 
4624  // painting the scrollbar (assume vertical only)
4625 
4627  if(!pScrollInfo)
4628  {
4629  pScrollInfo = (WB_SCROLLINFO *)WBAlloc(sizeof(*pScrollInfo));
4630  if(!pScrollInfo)
4631  {
4632  WB_ERROR_PRINT("%s:%d Out Of Memory\n", __FUNCTION__, __LINE__);
4633  return -1;
4634  }
4635 
4636  bzero(pScrollInfo, sizeof(*pScrollInfo));
4637  }
4638 
4639  // what is the height of the dialog box in 'items'? For now an item's height is
4640  // equal to pFont->max_bounds.ascent + pFont->max_bounds.descent + 6;
4641 
4642  nItemHeight = xBounds.ascent + xBounds.descent + 6;
4643  nHeight = (geomBorder.height - 4) / nItemHeight; // assume no horizontal scrollbar
4644 
4645  if(!pListInfo || pListInfo->nItems <= nHeight)
4646  {
4647  WBCalcVScrollBar(pScrollInfo, &geomBorder, iVScrollWidth, iHScrollHeight, 0, -1);
4648 
4649  if(pListInfo) // TODO: allocate empty list info
4650  {
4651  pListInfo->nTop = 0;
4652  pListInfo->nPos = -1; // "N/A"
4653  }
4654  }
4655  else
4656  {
4657  WBCalcVScrollBar(pScrollInfo, &geomBorder, iVScrollWidth, iHScrollHeight,
4658  pListInfo->nItems - nHeight + 1, pListInfo->nTop);
4659  if(pListInfo->nPos < 0)
4660  {
4661  pListInfo->nPos = pListInfo->nTop;
4662  }
4663  }
4664 
4665  if(pListInfo)
4666  {
4667  pListInfo->nHeight = nHeight; // cache this
4668  pListInfo->nItemHeight = nItemHeight;
4669  }
4670 
4671  WBDialogControlSetProperty2(pSelf, aDLGC_SCROLLINFO, pScrollInfo); // store updated scroll info (in case it changed)
4672 
4673  WBPaintVScrollBar(pScrollInfo, pDisplay, wID, gc, &geomBorder); // for now use 'border' geometry, later fix?
4674 
4675  // set the border rectangle so that it excludes scrollbar
4676  geomBorder.width = pScrollInfo->geomVBar.x
4677  - pScrollInfo->geomVBar.border
4678  - geomBorder.x;
4679 
4680  // painting the window text
4681  // FIRST, make sure that the bounding rectangle for painting
4682  // does NOT exceed geomBorder by setting the new clip region
4683 
4684  rgnTemp = WBGetPaintRegion(wID); // current paint region
4685 
4686  if(rgnTemp != None)
4687  {
4688  Region rgnTemp2 = WBGeomToRegion(&geomBorder);
4689  if(rgnTemp2 != None)
4690  {
4691  XIntersectRegion(rgnTemp2, rgnTemp, rgnTemp);
4692  XDestroyRegion(rgnTemp2);
4693 
4694  XSetRegion(pDisplay, gc, rgnTemp); // my new clipping region
4695  }
4696 
4697  XDestroyRegion(rgnTemp); // must destroy now
4698  }
4699 
4700  XSetForeground(pDisplay, gc, pSelf->clrFG.pixel);
4701 
4702  if(pListInfo) // use TEXT not caption
4703  {
4704  for(i1=pListInfo->nTop; i1 <= pListInfo->nTop + nHeight; i1++)
4705  {
4706  WB_GEOM geomItem;
4707 
4708  geomItem.x = geomBorder.x + 1;
4709  geomItem.width = geomBorder.width - 2;
4710  geomItem.y = geomBorder.y + nItemHeight * (i1 - pListInfo->nTop);
4711  geomItem.height = nItemHeight;
4712 
4713 // if(i1 == pListInfo->nPos) // the currently selected item
4714 // {
4715 // WB_ERROR_PRINT("TEMPORARY - drawing selected item %d\n", i1);
4716 // }
4717 // else
4718 // {
4719 // WB_ERROR_PRINT("TEMPORARY - NOT drawing selected item %d (%d)\n", i1, pListInfo->nPos);
4720 //
4721 // }
4722 
4723  if(i1 < pListInfo->nItems)
4724  {
4725  if(pListInfo->pfnDisplay)// void (*pfnDisplay)(WBDialogControl *pControl, void *pData, int iSelected, GC gcPaint, WB_GEOM *pGeom);
4726  {
4727  pListInfo->pfnDisplay(pSelf, pListInfo->aItems[i1], i1 == pListInfo->nPos, gc, &geomItem, fontSet);
4728  }
4729  else
4730  {
4731  DLGCDefaultListControlDisplayProc(pSelf, pListInfo->aItems[i1], i1 == pListInfo->nPos, gc, &geomItem, fontSet);
4732  }
4733  }
4734  else
4735  {
4736  if(pListInfo->pfnDisplay)// void (*pfnDisplay)(WBDialogControl *pControl, void *pData, int iSelected, GC gcPaint, WB_GEOM *pGeom);
4737  {
4738  pListInfo->pfnDisplay(pSelf, NULL, 0, gc, &geomItem, fontSet);
4739  }
4740  else
4741  {
4742  DLGCDefaultListControlDisplayProc(pSelf, NULL, 0, gc, &geomItem, fontSet);
4743  }
4744  }
4745  }
4746  }
4747 
4748  // by convention, restore original objects/state
4749 
4751  XSetForeground(pDisplay, gc, WBGetWindowFGColor(wID)); // restore it at the end
4753 
4754  WBEndPaint(wID, gc);
4755 
4756  return 1; // processed
4757 }
4758 
4759 
4760 
4761 
4762 // supporting functions
4763 
4764 static void FileListControlDisplayProc(WBDialogControl *pList, void *pData, int iSelected, GC gc, WB_GEOM *pGeom, XFontSet fontSet)
4765 {
4766 int iHPos;
4767 XCharStruct xBounds;
4768 Window wID = pList->wID;
4769 Display *pDisplay = WBGetWindowDisplay(wID);
4770 
4771 
4773  "%s - Expose %d (%08xH) pData=%p\n", __FUNCTION__, (int)wID, (int)wID, pData);
4774 
4775  if(fontSet == None)
4776  {
4777  fontSet = WBGetWindowFontSet(wID);
4778  if(fontSet == None)
4779  {
4780  fontSet = WBGetDefaultFontSet(pDisplay);
4781  if(fontSet == None)
4782  {
4783  WB_ERROR_PRINT("%s - NO FONT, line %d\n", __FUNCTION__, __LINE__);
4784  return;
4785  }
4786  }
4787  }
4788 
4789  if(pData && *(char *)pData == '@') // it's a directory
4790  {
4791  // get the BOLD version of the same font for directories
4792 
4793  if(((WBListControl *)pList)->fsBold != None)
4794  {
4795  fontSet = ((WBListControl *)pList)->fsBold;
4796  }
4797  else
4798  {
4799  ((WBListControl *)pList)->fsBold = WBCopyModifyFontSet(pDisplay, fontSet, 0, WBFontFlag_WT_BOLD); // BOLD version
4800  if(((WBListControl *)pList)->fsBold == None)
4801  {
4802  // TODO: make a copy without the 'bold' ??? (this should already have happened)
4803  WB_ERROR_PRINT("%s - * BUG * line %d\n", __FUNCTION__, __LINE__);
4804 
4805  return;
4806  }
4807  else
4808  {
4809  fontSet = ((WBListControl *)pList)->fsBold;
4810  }
4811  }
4812  }
4813 
4814  xBounds = WBFontSetMaxBounds(pDisplay, fontSet);
4815  iHPos = WBFontSetAvgCharWidth(pDisplay, fontSet); // average character width for hpos
4816 
4817  // font setup
4819 // XClearWindow(pDisplay, wID); // TODO: rather than erase background, see if I need to
4820  XSetForeground(pDisplay, gc, iSelected ? pList->clrHBG.pixel : pList->clrBG.pixel);
4821  XFillRectangle(pDisplay, wID, gc, pGeom->x, pGeom->y, pGeom->width, pGeom->height);
4823 
4824  if(iSelected)
4825  {
4826  WBDrawDashedRect(pDisplay, wID, gc, pGeom, pList->clrBD2.pixel);
4827  }
4828 
4829 
4830 // // vertically centered text
4831 // iVPos = xBounds.ascent + xBounds.descent; // font height
4832 // iVPos = (pGeom->height - iVPos) >> 1; // half of the difference - top of text
4833 // iVPos += xBounds.ascent; // base of the text
4834 
4835  // painting the window text
4836 
4837  if(pData)
4838  {
4839  if(((const char *)pData)[0] == '@' &&
4840  ((const char *)pData)[1] == '.' &&
4841  ((const char *)pData)[2] == '.' &&
4842  !((const char *)pData)[3])
4843  {
4844  XPoint aPoints[4];
4845 // int i1;
4846  // draw a left arrow using the highlight color
4847 
4848  XSetForeground(pDisplay, gc, iSelected ? pList->clrBG.pixel : pList->clrHBG.pixel);
4849 
4850  XFillRectangle(pDisplay, wID, gc,
4851  pGeom->x + iHPos + xBounds.ascent / 2,
4852  pGeom->y + pGeom->height / 2 - xBounds.ascent / 4, // half-way - ascent / 4
4853  iHPos * 2 - xBounds.ascent / 2,
4854  xBounds.ascent / 2); // half height of text
4855 
4856  // now make an 'arrow' (TODO: 3D-looking? I would need more colors...)
4857 
4858  aPoints[0].x = pGeom->x + iHPos;
4859  aPoints[0].y = pGeom->y + pGeom->height / 2;
4860  aPoints[1].x = aPoints[0].x + xBounds.ascent;
4861  aPoints[1].y = aPoints[0].y + xBounds.ascent * 2 / 3 - 1;
4862  aPoints[2].x = aPoints[1].x;
4863  aPoints[2].y = aPoints[0].y - xBounds.ascent * 2 / 3 + 1;
4864  aPoints[3].x = aPoints[0].x;
4865  aPoints[3].y = aPoints[0].y;
4866 
4867  XFillPolygon(pDisplay, wID, gc, aPoints, 4, Nonconvex, CoordModeOrigin);
4868 
4869  // 3D highlights for arrow
4870  XSetForeground(pDisplay, gc, iSelected ? pList->clrBD3.pixel : pList->clrBD2.pixel);
4871  XDrawLine(pDisplay, wID, gc, aPoints[0].x, aPoints[0].y, aPoints[1].x - 1, aPoints[1].y);
4872  XDrawLine(pDisplay, wID, gc, aPoints[1].x + 1, pGeom->y + pGeom->height / 2 - xBounds.ascent / 4,
4873  pGeom->x + iHPos * 3, pGeom->y + pGeom->height / 2 - xBounds.ascent / 4);
4874  XDrawLine(pDisplay, wID, gc, pGeom->x + iHPos * 3, pGeom->y + pGeom->height / 2 - xBounds.ascent / 4 + 1,
4875  pGeom->x + iHPos * 3, pGeom->y + pGeom->height / 2 + xBounds.ascent / 4 - 1);
4876 
4877  XSetForeground(pDisplay, gc, iSelected ? pList->clrBD2.pixel : pList->clrBD3.pixel);
4878  XDrawLine(pDisplay, wID, gc, aPoints[0].x, aPoints[0].y, aPoints[2].x - 1, aPoints[2].y);
4879  XDrawLine(pDisplay, wID, gc, aPoints[1].x + 1, pGeom->y + pGeom->height / 2 + xBounds.ascent / 4,
4880  pGeom->x + iHPos * 3, pGeom->y + pGeom->height / 2 + xBounds.ascent / 4);
4881  }
4882  else
4883  {
4884  const char *szText = (const char *)pData + 1;
4885 
4886  if(*(const char *)pData == '^') // symlink
4887  {
4888  XSetForeground(pDisplay, gc, iSelected ? pList->clrBG.pixel : pList->clrHBG.pixel);
4889  }
4890  else // regular file
4891  {
4892  XSetForeground(pDisplay, gc, iSelected ? pList->clrHFG.pixel : pList->clrFG.pixel);
4893  }
4894 
4895  XSetBackground(pDisplay, gc, iSelected ? pList->clrHBG.pixel : pList->clrBG.pixel);
4896 
4897  if(*szText)
4898  {
4899  WB_RECT rctBounds;
4900 
4901  rctBounds.top = pGeom->y;
4902  rctBounds.bottom = pGeom->y + pGeom->height;
4903  rctBounds.left = pGeom->x + iHPos;
4904  rctBounds.right = pGeom->x + pGeom->width - iHPos;
4905 
4906 // XDrawString(pDisplay, wID, gc, pGeom->x + iHPos, pGeom->y + iVPos, szText, strlen(szText));
4907  DTDrawSingleLineText(fontSet, szText, pDisplay, gc, wID, 0, 0, &rctBounds,
4909  }
4910 
4911  if(iSelected) // selected item
4912  {
4913  XSetForeground(pDisplay, gc, pList->clrFG.pixel);
4914  XSetBackground(pDisplay, gc, pList->clrBG.pixel);
4915  }
4916  }
4917  }
4918 
4919  // by convention, restore original objects/state
4920 
4922  XSetForeground(pDisplay, gc, WBGetWindowFGColor(wID)); // restore it at the end
4924 }
4925 
4926 
4927 
4928 
4929 
4930 
4931