X11 Work Bench Toolkit  1.0
dialog_support.c
1 
2 // _ _ _ _ //
3 // __| |(_) __ _ | | ___ __ _ ___ _ _ _ __ _ __ ___ _ __ | |_ ___ //
4 // / _` || | / _` || | / _ \ / _` | / __|| | | || '_ \ | '_ \ / _ \ | '__|| __| / __| //
5 // | (_| || || (_| || || (_) || (_| | \__ \| |_| || |_) || |_) || (_) || | | |_ _| (__ //
6 // \__,_||_| \__,_||_| \___/ \__, |_____|___/ \__,_|| .__/ | .__/ \___/ |_| \__|(_)\___| //
7 // |___/|_____| |_| |_| //
8 // //
9 // //
10 // additional dialog control support //
11 // This file contains various support and functionality that is common to dialog controls //
12 // //
14 
15 
16 /*****************************************************************************
17 
18  X11workbench - X11 programmer's 'work bench' application and toolkit
19  Copyright (c) 2010-2016 by Bob Frazier (aka 'Big Bad Bombastic Bob')
20  all rights reserved
21 
22  DISCLAIMER: The X11workbench application and toolkit software are supplied
23  'as-is', with no warranties, either implied or explicit.
24  Any claims to alleged functionality or features should be
25  considered 'preliminary', and might not function as advertised.
26 
27  BSD-like license:
28 
29  There is no restriction as to what you can do with this software, so long
30  as you include the above copyright notice and DISCLAIMER for any distributed
31  work that is equal to or derived from this one, along with this paragraph
32  that explains the terms of the license if the source is also being made
33  available. A "derived work" describes a work that uses a significant portion
34  of the source files or algorithms that are included with this one.
35  Specifically excluded from this are files that were generated by the software,
36  or anything that is included with the software that is part of another package
37  (such as files that were created or added during the 'configure' process).
38  Specifically included is the use of part or all of any of the X11 workbench
39  toolkit source or header files in your distributed application. If you do not
40  ship the source, the above copyright statement is still required to be placed
41  in a reasonably prominent place, such as documentation, splash screens, and/or
42  'about the application' dialog boxes.
43 
44  Use and distribution are in accordance with GPL, LGPL, and/or the above
45  BSD-like license. See COPYING and README files for more information.
46 
47 
48  Additional information at http://sourceforge.net/projects/X11workbench
49 
50 ******************************************************************************/
51 
52 
53 #ifdef linux /* needed for debian, possibly others */
54 #define _GNU_SOURCE /* in case features.h is involved on linux */
55 #define __USE_GNU /* this enables a few more things in the headers like 'qsort_r' */
56 // TODO: consider making this a 'configure' thing
57 #endif // linux
58 
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <unistd.h>
62 #include <memory.h>
63 #include <string.h>
64 #include <strings.h>
65 #include <signal.h>
66 #include <time.h>
67 #include <sys/stat.h>
68 
69 #ifndef XK_Delete /* moslty for interix */
70 #define XK_MISCELLANY /* mostly for interix */
71 #include <X11/keysymdef.h> // some platforms don't automatically include this with X headers
72 #endif // XK_Delete
73 
74 #define DIALOG_SUPPORT_C /* prevents me from including the various Atom definitions */
75 
76 #include "window_helper.h"
77 #include "pixmap_helper.h" // pixmap helpers, including pre-defined icons
78 #include "dialog_window.h"
79 #include "dialog_controls.h"
80 #include "dialog_support.h" // internal-only definitions
81 #include "conf_help.h"
82 #include "file_help.h"
83 #include "draw_text.h"
84 #include "text_object.h"
85 #include "window_dressing.h"
86 
89 #define THIS_SUBSYSTEM DebugSubSystem_DialogCtrl
90 
91 
92 
93 
94 #define INIT_ATOM(X) if(a##X == None){ a##X = WBGetAtom(WBGetDefaultDisplay(),X##_STR); }
95 #define INIT_ATOM2(X,Y) if(X == None){ X = WBGetAtom(WBGetDefaultDisplay(),Y); }
96 
97 #define DEFAULT_LISTINFO_MAX 16384 /* default initial max # of items in LISTINFO */
98 
99 
100 
101 // atoms first
102 // TODO: make this documentation a bit better. for now the format is OK in doxygen, a little flakey here
103 
106 Atom aFRAME_CONTROL = None;
107 
109 Atom aTEXT_CONTROL = None;
110 
112 Atom aICON_CONTROL = None;
113 
115 Atom aIMAGE_CONTROL = None;
116 
118 Atom aEDIT_CONTROL = None;
119 
121 Atom aPUSHBUTTON_CONTROL = None;
122 
125 
128 
130 Atom aRADIOBUTTON_CONTROL = None;
131 
134 
136 Atom aCHECKBUTTON_CONTROL = None;
137 
140 
142 Atom aHSCROLL_CONTROL = None;
143 
145 Atom aVSCROLL_CONTROL = None;
146 
148 Atom aSLIDER_CONTROL = None;
149 
151 Atom aKNOB_CONTROL = None;
152 
154 Atom aLIST_CONTROL = None;
155 
157 Atom aCOMBO_CONTROL = None;
158 
160 Atom aTREE_CONTROL = None;
161 
163 Atom aCOMBOTREE_CONTROL = None;
164 
166 Atom aFILE_LIST_CONTROL = None;
167 
169 Atom aFILE_COMBO_CONTROL = None;
170 
172 Atom aPATH_TREE_CONTROL = None;
173 
175 Atom aTAB_CONTROL = None;
176 
177 
178 // NOTE: 'xatoms' is in the CORE group - consider moving this into CORE
203 Atom aSCROLL_NOTIFY = None; // specific notification for scrollbars
204 
206 
218 Atom aBUTTON_PRESS = None;
232 Atom aLIST_NOTIFY = None;
245 Atom aTEXT_CHANGED = None;
260 Atom aTEXTSELECT_CHANGE = None;
273 Atom aGOTFOCUS = None;
286 Atom aLOSTFOCUS = None;
299 Atom aMOUSE_DOWN = None;
312 Atom aMOUSE_UP = None;
325 Atom aMOUSE_DRAG = None;
338 Atom aKEY_DOWN = None;
351 Atom aKEY_UP = None;
364 Atom aKEYSTROKE = None;
365 
375 Atom aDIALOG_INIT = None;
376 
387 Atom aDLGC_PROP_NOTIFY = None;
388 
400 Atom aLIST_SELCHANGE = None; // list selection has changed (send to self)
401 
402 // internal-only properties (still have global scope for inline functions, etc.)
409 Atom aDLGC_TEXT = None; // dialog control 'text' property, i.e. 'GetText'
414 Atom aDLGC_CAPTION = None; // dialog control 'caption' property, i.e. 'GetCaption' (not an actual property, notification only)
421 Atom aDLGC_FONT = None;
430 Atom aDLGC_SCROLLINFO = None; // scrollbar info structure (horizontal AND vertical)
431  // (scrollbars, listboxes, combo boxes, multi-line edit)
440 Atom aDLGC_LISTINFO = None; // listbox info structure
441 
451 Atom aDLGC_PATH = None;
452 
453 
455 {
456  INIT_ATOM(FRAME_CONTROL);
457  INIT_ATOM(TEXT_CONTROL);
458  INIT_ATOM(ICON_CONTROL);
459  INIT_ATOM(IMAGE_CONTROL);
460  INIT_ATOM(EDIT_CONTROL);
461  INIT_ATOM(PUSHBUTTON_CONTROL);
462  INIT_ATOM(DEFPUSHBUTTON_CONTROL);
463  INIT_ATOM(CANCELBUTTON_CONTROL);
464  INIT_ATOM(RADIOBUTTON_CONTROL);
465  INIT_ATOM(FIRSTRADIOBUTTON_CONTROL);
466  INIT_ATOM(CHECKBUTTON_CONTROL);
467  INIT_ATOM(TRISTATEBUTTON_CONTROL);
468  INIT_ATOM(HSCROLL_CONTROL);
469  INIT_ATOM(VSCROLL_CONTROL);
470  INIT_ATOM(SLIDER_CONTROL);
471  INIT_ATOM(KNOB_CONTROL);
472  INIT_ATOM(LIST_CONTROL);
473  INIT_ATOM(COMBO_CONTROL);
474  INIT_ATOM(TREE_CONTROL);
475  INIT_ATOM(COMBOTREE_CONTROL);
476  INIT_ATOM(FILE_LIST_CONTROL);
477  INIT_ATOM(FILE_COMBO_CONTROL);
478  INIT_ATOM(PATH_TREE_CONTROL);
479  INIT_ATOM(TAB_CONTROL);
480 
481 // INIT_ATOM2(aCONTROL_NOTIFY,"ControlNotify");
482  INIT_ATOM2(aSCROLL_NOTIFY,"ScrollNotify");
483  INIT_ATOM2(aBUTTON_PRESS,"ButtonPress");
484  INIT_ATOM2(aLIST_NOTIFY,"ListNotify");
485  INIT_ATOM2(aTEXT_CHANGED,"TextChanged");
486  INIT_ATOM2(aTEXTSELECT_CHANGE,"TextSelectChange");
487  INIT_ATOM2(aGOTFOCUS,"GotFocus");
488  INIT_ATOM2(aLOSTFOCUS,"LostFocus");
489  INIT_ATOM2(aMOUSE_DOWN,"MouseDown");
490  INIT_ATOM2(aMOUSE_UP,"MouseUp");
491  INIT_ATOM2(aMOUSE_DRAG,"MouseDrag");
492  INIT_ATOM2(aKEY_DOWN,"KeyDown");
493  INIT_ATOM2(aKEY_UP,"KeyUp");
494  INIT_ATOM2(aKEYSTROKE,"Keystroke");
495 
496  INIT_ATOM2(aDLGC_TEXT, "DLGC_TEXT");
497  INIT_ATOM2(aDLGC_CAPTION, "DLGC_CAPTION");
498  INIT_ATOM2(aDLGC_FONT, "DLGC_FONT");
499  INIT_ATOM2(aDLGC_SCROLLINFO, "DLGC_SCROLLINFO");
500  INIT_ATOM2(aDLGC_LISTINFO, "DLGC_LISTINFO");
501  INIT_ATOM2(aDLGC_PATH, "DLGC_PATH");
502 
503 
504  // other dialog-related messages
505  INIT_ATOM2(aDIALOG_INIT,"DialogInit");
506 // INIT_ATOM2(aDIALOG_SETFOCUS,"DialogSetFocus");
507  INIT_ATOM2(aDLGC_PROP_NOTIFY, "DLGCPropNotify");
508  INIT_ATOM2(aLIST_SELCHANGE, "LIST_SELCHANGE");
509 
510 }
511 
512 
514 // D I A L O G C O N T R O L P R O P E R T I E S
516 
517 
518 void WBDialogControlSetCaption(WBDialogControl *pCtrl, const char *szCaption)
519 {
520 char *szOldCaption = pCtrl->pCaption;
521 
522  pCtrl->pCaption = WBCopyString(szCaption);
523 
524  if(szOldCaption)
525  {
526  WBFree(szOldCaption);
527  }
528 
529  WBInvalidateGeom(pCtrl->wID, NULL, 0);
530  WBUpdateWindow(pCtrl->wID); // schedule update (not immediate)
531 
532  DLGNotifySelf(pCtrl, aDLGC_PROP_NOTIFY, aDLGC_CAPTION, 0, 0, 0, 0);
533 }
534 
536 {
537  if(!pCtrl->pCaption)
538  {
539  return "";
540  }
541 
542  return pCtrl->pCaption;
543 }
544 
545 void WBDialogControlSetPixmap(WBDialogControl *pCtrl, Pixmap pixmap)
546 {
547 Display *pDisplay = NULL;
548 
549  if(!pCtrl ||
550  (pCtrl->aClass != aICON_CONTROL && pCtrl->aClass != aIMAGE_CONTROL
551  && pCtrl->aClass != aPUSHBUTTON_CONTROL && pCtrl->aClass != aDEFPUSHBUTTON_CONTROL
552  && pCtrl->aClass != aCANCELBUTTON_CONTROL))
553  {
554  return;
555  }
556 
557  pDisplay = WBGetWindowDisplay(pCtrl->wID);
558 
559  if(!pDisplay)
560  {
561  pDisplay = WBGetDefaultDisplay();
562  }
563 
564  if(pCtrl->aClass == aICON_CONTROL ||
565  pCtrl->aClass == aIMAGE_CONTROL)
566  {
567  Pixmap pxOld = ((struct _WB_IMAGE_CONTROL_ *)pCtrl)->pixmap;
568  Pixmap pxOld2 = ((struct _WB_IMAGE_CONTROL_ *)pCtrl)->pixmap2;
569 
570  ((struct _WB_IMAGE_CONTROL_ *)pCtrl)->pixmap = pixmap;
571  ((struct _WB_IMAGE_CONTROL_ *)pCtrl)->pixmap2 = None;
572 
574  if(pxOld != None)
575  {
576  XFreePixmap(pDisplay, pxOld);
577  }
578  if(pxOld2 != None)
579  {
580  XFreePixmap(pDisplay, pxOld2);
581  }
583  }
584 
585  if(pCtrl->aClass == aPUSHBUTTON_CONTROL || pCtrl->aClass == aDEFPUSHBUTTON_CONTROL
586  || pCtrl->aClass == aCANCELBUTTON_CONTROL)
587  {
588  Pixmap pxOld = ((struct _WB_PUSHBUTTON_CONTROL_ *)pCtrl)->pixmap;
589  Pixmap pxOld2 = ((struct _WB_PUSHBUTTON_CONTROL_ *)pCtrl)->pixmap2;
590 
591  ((struct _WB_PUSHBUTTON_CONTROL_ *)pCtrl)->pixmap = pixmap;
592  ((struct _WB_PUSHBUTTON_CONTROL_ *)pCtrl)->pixmap2 = None;
593 
595  if(pxOld != None)
596  {
597  XFreePixmap(pDisplay, pxOld);
598  }
599  if(pxOld2 != None)
600  {
601  XFreePixmap(pDisplay, pxOld2);
602  }
604  }
605 }
606 
608 {
609  if(!pCtrl)
610  {
611  return None;
612  }
613 
614  if(pCtrl->aClass == aICON_CONTROL ||
615  pCtrl->aClass == aIMAGE_CONTROL)
616  {
617  return ((struct _WB_IMAGE_CONTROL_ *)pCtrl)->pixmap;
618  }
619 
620  if(pCtrl->aClass == aPUSHBUTTON_CONTROL || pCtrl->aClass == aDEFPUSHBUTTON_CONTROL
621  || pCtrl->aClass == aCANCELBUTTON_CONTROL)
622  {
623  return ((struct _WB_PUSHBUTTON_CONTROL_ *)pCtrl)->pixmap;
624  }
625 
626  return None;
627 }
628 
630 {
631 Display *pDisplay = NULL;
632 
633  if(!pCtrl ||
634  (pCtrl->aClass != aICON_CONTROL && pCtrl->aClass != aIMAGE_CONTROL
635  && pCtrl->aClass != aPUSHBUTTON_CONTROL && pCtrl->aClass != aDEFPUSHBUTTON_CONTROL
636  && pCtrl->aClass != aCANCELBUTTON_CONTROL))
637  {
638  return;
639  }
640 
641  pDisplay = WBGetWindowDisplay(pCtrl->wID);
642 
643  if(!pDisplay)
644  {
645  pDisplay = WBGetDefaultDisplay();
646  }
647 
648  if(pCtrl->aClass == aICON_CONTROL ||
649  pCtrl->aClass == aIMAGE_CONTROL)
650  {
651  Pixmap pxOld = ((struct _WB_IMAGE_CONTROL_ *)pCtrl)->pixmap;
652  Pixmap pxOld2 = ((struct _WB_IMAGE_CONTROL_ *)pCtrl)->pixmap2;
653 
654  ((struct _WB_IMAGE_CONTROL_ *)pCtrl)->pixmap = pixmap;
655  ((struct _WB_IMAGE_CONTROL_ *)pCtrl)->pixmap2 = pixmap2;
656 
658  if(pxOld != None)
659  {
660  XFreePixmap(pDisplay, pxOld);
661  }
662  if(pxOld2 != None)
663  {
664  XFreePixmap(pDisplay, pxOld2);
665  }
667  }
668 
669  if(pCtrl->aClass == aPUSHBUTTON_CONTROL || pCtrl->aClass == aDEFPUSHBUTTON_CONTROL
670  || pCtrl->aClass == aCANCELBUTTON_CONTROL)
671  {
672  Pixmap pxOld = ((struct _WB_PUSHBUTTON_CONTROL_ *)pCtrl)->pixmap;
673  Pixmap pxOld2 = ((struct _WB_PUSHBUTTON_CONTROL_ *)pCtrl)->pixmap2;
674 
675  ((struct _WB_PUSHBUTTON_CONTROL_ *)pCtrl)->pixmap = pixmap;
676  ((struct _WB_PUSHBUTTON_CONTROL_ *)pCtrl)->pixmap2 = pixmap2;
677 
679  if(pxOld != None)
680  {
681  XFreePixmap(pDisplay, pxOld);
682  }
683  if(pxOld2 != None)
684  {
685  XFreePixmap(pDisplay, pxOld2);
686  }
688  }
689 }
690 
691 Pixmap WBDialogControlGetIconPixmap(WBDialogControl *pCtrl, Pixmap *pPixmap2)
692 {
693  if(!pCtrl)
694  {
695  return None;
696  }
697 
698  if(pCtrl->aClass == aICON_CONTROL ||
699  pCtrl->aClass == aIMAGE_CONTROL)
700  {
701  if(pPixmap2)
702  {
703  *pPixmap2 = ((struct _WB_PUSHBUTTON_CONTROL_ *)pCtrl)->pixmap2;
704  }
705 
706  return ((struct _WB_IMAGE_CONTROL_ *)pCtrl)->pixmap;
707  }
708 
709  if(pCtrl->aClass == aPUSHBUTTON_CONTROL || pCtrl->aClass == aDEFPUSHBUTTON_CONTROL
710  || pCtrl->aClass == aCANCELBUTTON_CONTROL)
711  {
712  if(pPixmap2)
713  {
714  *pPixmap2 = ((struct _WB_PUSHBUTTON_CONTROL_ *)pCtrl)->pixmap2;
715  }
716 
717  return ((struct _WB_PUSHBUTTON_CONTROL_ *)pCtrl)->pixmap;
718  }
719 
720  return None;
721 }
722 
723 
724 int WBDialogControlSetPropList(WBDialogControl *pCtrl, const char *szPropList)
725 {
726 // property<tab>value<newline>
727 
728  return 0; // for now (not being used yet)
729 }
730 
731 int WBDialogControlSetProperty(WBDialogControl *pCtrl, Atom aPropName, const char *szPropVal)
732 {
733  WB_DIALOG_PROP p;
734 
735  p.aProp = aPropName;
736  p.lVal = 0;
737  p.pVal = WBCopyString(szPropVal);
738 
739  if(!p.pVal)
740  {
741  return -1;
742  }
743 
744  WBDialogControlSetDialogProp(pCtrl, &p);
745  return 0;
746 }
747 
748 void WBDialogControlSetProperty2(WBDialogControl *pCtrl, Atom aPropName, void *pPropVal)
749 {
750  WB_DIALOG_PROP p;
751 
752  p.aProp = aPropName;
753  p.lVal = 0;
754  p.pVal = pPropVal;
755 
756  WBDialogControlSetDialogProp(pCtrl, &p);
757 }
758 
759 const char *WBDialogControlGetProperty(WBDialogControl *pCtrl, Atom aPropName)
760 {
761 const WB_DIALOG_PROP *pP = WBDialogControlGetDialogProp(pCtrl, aPropName);
762 
763  if(pP)
764  {
765  return pP->pVal; // TODO: if NULL, lVal as a string?
766  }
767 
768  return ""; // so it's not NULL
769 }
770 
771 void *WBDialogControlGetProperty2(WBDialogControl *pCtrl, Atom aPropName)
772 {
773 const WB_DIALOG_PROP *pP = WBDialogControlGetDialogProp(pCtrl, aPropName);
774 
775  if(pP)
776  {
777  return (void *)(pP->pVal); // TODO: if NULL, lVal as a string?
778  }
779 
780  return NULL;
781 }
782 
783 
784 
785 #define INITIAL_PROPERTY_MAX 64
786 
787 static WBDialogPropList * __ConstructPropList(void)
788 {
789  int iInitialSize = sizeof(WBDialogPropList) + INITIAL_PROPERTY_MAX * sizeof(WB_DIALOG_PROP);
790  WBDialogPropList *pRval = (WBDialogPropList *)WBAlloc(iInitialSize);
791 
792  if(pRval)
793  {
794  pRval->nProps = 0;
795  pRval->nMaxProps = INITIAL_PROPERTY_MAX;
796  WB_WARN_PRINT("TEMPORARY: WBDialogPropList constructor %p %d, %d\n", pRval, pRval->nProps, pRval->nMaxProps);
797  }
798  else
799  {
800  WB_ERROR_PRINT("%s - not enough memory\n", __FUNCTION__);
801  }
802 
803  return pRval;
804 }
805 
807 {
808 int i1;
809 WBDialogPropList *pPropList;
810 WB_DIALOG_PROP *pProp = NULL;
811 Atom aProp;
812 
813  if(!pCtrl || !pPropVal)
814  {
815  WB_ERROR_PRINT("%s - pCtrl %p or pPropVal %p\n", __FUNCTION__,
816  pCtrl, pPropVal);
817 
818  return -1;
819  }
820 
821  if(!pCtrl->pPropList)
822  {
823  if(!(pCtrl->pPropList = __ConstructPropList()))
824  {
825  WB_ERROR_PRINT("%s - unable to allocate property list for window %d (%08xH)\n",
826  __FUNCTION__, (int)pCtrl->wID, (int)pCtrl->wID);
827 
828  return -2;
829  }
830  }
831 
832  pPropList = pCtrl->pPropList;
833  aProp = pPropVal->aProp;
834 
835  for(i1=0; i1 < pPropList->nProps; i1++)
836  {
837  register WB_DIALOG_PROP *pProp0 = &(pPropList->aDlgProp[i1]);
838 
839  if(pProp0 && pProp0->aProp == aProp)
840  {
841  pProp = pProp0;
842  break;
843  }
844  }
845 
846  if(!pProp)
847  {
848  if((pPropList->nProps + 1) >= pPropList->nMaxProps)
849  {
850  int iNewSize = (pPropList->nMaxProps + INITIAL_PROPERTY_MAX / 2)
851  * sizeof(pPropList->aDlgProp[0])
852  + sizeof(*pPropList);
853 
854  pPropList = (WBDialogPropList *)WBReAlloc(pPropList, iNewSize);
855  if(!pPropList)
856  {
857  WB_ERROR_PRINT("%s - unable to RE-allocate property list for window %d (%08xH)\n",
858  __FUNCTION__, (int)pCtrl->wID, (int)pCtrl->wID);
859 
860  return -3;
861  }
862 
863  pCtrl->pPropList = pPropList;
864  pPropList->nMaxProps += INITIAL_PROPERTY_MAX / 2;
865 
866  if(pPropList->nProps >= pPropList->nMaxProps)
867  {
868  WB_ERROR_PRINT("%s - internal inconsistency, nProps=%d, nMaxProps=%d\n",
869  __FUNCTION__, pPropList->nProps, pPropList->nMaxProps);
870  return -4;
871  }
872  }
873 
874  pProp = &(pPropList->aDlgProp[pPropList->nProps]);
875  pPropList->nProps++;
876 
877  pProp->aProp = aProp;
878  pProp->lVal = 0;
879  pProp->pVal = NULL;
880  }
881 
882  pProp->lVal = pPropVal->lVal; // just copy the lVal
883 
884  if(pProp->pVal != pPropVal->pVal) // if it changes... and ONLY if it changes
885  {
886  if(pProp->pVal)
887  {
888  if(pProp->aProp == aDLGC_LISTINFO)
889  {
890  DLGCListInfoDestructor((LISTINFO *)(pProp->pVal));
891  }
892  else
893  {
894  WBFree(pProp->pVal);
895  }
896  }
897 
898  pProp->pVal = pPropVal->pVal;
899  }
900 
901  // last but not least, tell the dialog control its properties were altered
902  // by directly calling its callback function with the 'property change' event
903 
905  "%s - DLGC_PROP_NOTIFY (set) for window %d (%08xH)\n",
906  __FUNCTION__, (int)pCtrl->wID, (int)pCtrl->wID);
907 
908  DLGNotifySelf(pCtrl, aDLGC_PROP_NOTIFY, pPropVal->aProp, 0, 0, 0, 0);
909 
910  return 0; // success
911 }
912 
914 {
915 int i1, bNotify = 0;
916 WBDialogPropList *pPropList = pCtrl ? pCtrl->pPropList : NULL;
917 //WB_DIALOG_PROP *pProp2 = NULL;
918 WB_DIALOG_PROP sProp;
919 
920 
921  if(!pPropList)
922  {
923  return;
924  }
925 
926  for(i1=0; i1 < pPropList->nProps; i1++)
927  {
928  register WB_DIALOG_PROP *pProp = &(pPropList->aDlgProp[i1]);
929 
930  if(pProp && pProp->aProp == aProp)
931  {
932  if(pProp->pVal) // pointer?
933  {
934  if(pProp->aProp == aDLGC_LISTINFO)
935  {
936  DLGCListInfoDestructor((LISTINFO *)(pProp->pVal));
937  }
938  else
939  {
940  WBFree(pProp->pVal);
941  }
942 
943  pProp->pVal = NULL; // necessary, for the next step
944  }
945 
946  memcpy(&sProp, pProp, sizeof(WB_DIALOG_PROP)); // make a copy of the thing
947  bNotify = 1;
948 
949  // must remove this entry now
950  for(i1++; i1 < pPropList->nProps; i1++)
951  {
952  pPropList->aDlgProp[i1 - 1] = pPropList->aDlgProp[i1];
953  }
954 
955  pPropList->nProps--;
956  bzero(&(pPropList->aDlgProp[pPropList->nProps]), sizeof(pPropList->aDlgProp[pPropList->nProps]));
957 
958  break;
959  }
960  }
961 
962  // last but not least, tell the dialog control its properties were altered
963  // by directly calling its callback function with the 'property change' event
964 
965 // if(pProp2) NO NO NO! but this is how 'set' does it, since 'pProp2' is still valid
966 // {
967 // DLGNotifySelf(pCtrl, aDLGC_PROP_NOTIFY, pProp2->aProp, 0, 0, 0, 0);
968 // }
969 
970  // TODO: find a better way to notify for a property that was deleted.
971  // for now I copied the prop structure
972 
973  if(bNotify)
974  {
976  "%s - DLGC_PROP_NOTIFY (del) for window %d (%08xH)\n",
977  __FUNCTION__, (int)pCtrl->wID, (int)pCtrl->wID);
978 
979  DLGNotifySelf(pCtrl, aDLGC_PROP_NOTIFY, sProp.aProp, 0, 0, 0, 0);
980  }
981 
982 }
983 
985 {
986 int i1;
987 WBDialogPropList *pPropList = pCtrl ? pCtrl->pPropList : NULL;
988 
989  if(!pPropList)
990  {
991  return NULL; // for NULL 'pCtrl' will always be the case
992  }
993 
994  for(i1=0; i1 < pPropList->nProps; i1++)
995  {
996  register WB_DIALOG_PROP *pProp = &(pPropList->aDlgProp[i1]);
997 
998  if(pProp && pProp->aProp == aProp)
999  {
1000  return pProp;
1001  }
1002  }
1003 
1004  return NULL; // not found
1005 }
1006 
1008 {
1009 int i1;
1010 
1011  if(!pPropList)
1012  {
1013  return;
1014  }
1015 
1016  for(i1=0; i1 < pPropList->nProps; i1++)
1017  {
1018  /*register*/ WB_DIALOG_PROP *pProp = &(pPropList->aDlgProp[i1]);
1019 
1020  if(pProp->pVal) // pointer?
1021  {
1022  if(pProp->aProp == aDLGC_LISTINFO)
1023  {
1024  DLGCListInfoDestructor((LISTINFO *)(pProp->pVal));
1025  }
1026  else
1027  {
1028  WBFree(pProp->pVal);
1029  }
1030 
1031 #if 0
1032  { // debug code to prevent re-free problems with b0rked list
1033  int i2;
1034 
1035  for(i2=i1+1; i2 < pPropList->nProps; i2++)
1036  {
1037  register WB_DIALOG_PROP *pProp2 = &(pPropList->aDlgProp[i2]);
1038 
1039  if(pProp->pVal == pProp2->pVal)
1040  {
1041  if(pProp->aProp == pProp2->aProp)
1042  {
1044  "%s - %d %d matching property name %d and value %p detected\n",
1045  __FUNCTION__, i1, i2, (int)(pProp->aProp), pProp->pVal);
1046  }
1047 
1048  pProp2->pVal = NULL;
1049  pProp2->aProp = 0; // for now set this to zero as well
1050  }
1051  }
1052  }
1053 #endif // 0
1054  }
1055 
1056  bzero(pProp, sizeof(*pProp)); // zero out whatever used to be there (as a matter of course)
1057  }
1058 
1059  pPropList->nProps = 0; // matter of course
1060 
1061  WBFree(pPropList); // caller must ensure no duplicate destruction happens
1062 }
1063 
1064 
1065 
1067 // L I S T I N F O H A N D L E R S
1069 
1070 void * DLGCDefaultListInfoAllocator(const void *pData, int cbData)
1071 {
1072  void *pRval;
1073 
1074  if(cbData < 0)
1075  {
1076  if(pData)
1077  {
1078  cbData = strlen((const char *)pData) + 1;
1079  }
1080  else
1081  {
1082  return NULL;
1083  }
1084  }
1085 
1086  pRval = WBAlloc(cbData + 1);
1087 
1088  if(pRval)
1089  {
1090  if(cbData)
1091  {
1092  if(pData)
1093  {
1094  memcpy(pRval, pData, cbData);
1095  }
1096  else
1097  {
1098  bzero(pRval, cbData);
1099  }
1100  }
1101 
1102  ((char *)pRval)[cbData] = 0; // there will always be at least that one extra byte for the zero
1103  }
1104 
1105  return pRval;
1106 }
1107 
1109  void *(*pfnAllocator)(const void *,int), void (*pfnDestructor)(void *),
1110  void (*pfnDisplay)(WBDialogControl *, void *, int, GC, WB_GEOM *, XFontSet),
1111  int (*pfnSort)(const void *, const void *))
1112 {
1113 WB_DIALOG_PROP propTemp;
1114 int i1;
1115 
1116  if(!pfnDisplay)
1117  {
1118  pfnDisplay = DLGCDefaultListControlDisplayProc;
1119  }
1120 
1121  propTemp.aProp = aDLGC_LISTINFO;
1122  propTemp.lVal = 0;
1123  propTemp.pVal = DLGCListInfoConstructor(pCtrl->wID, DEFAULT_LISTINFO_MAX, nFlags,
1124  pfnAllocator, pfnDestructor, pfnDisplay, NULL);
1125 
1126  if(!propTemp.pVal)
1127  {
1128  WB_ERROR_PRINT("%s - DLGCListInfoConstructor returns NULL\n", __FUNCTION__);
1129  return -2;
1130  }
1131 
1132  i1 = WBDialogControlSetDialogProp(pCtrl, &propTemp);
1133 
1134  if(i1)
1135  {
1136  WB_ERROR_PRINT("%s - WBDialogControlSetDialogProp returns %d\n", __FUNCTION__, i1);
1137  DLGCListInfoDestructor(propTemp.pVal);
1138  }
1139 
1140  return i1; // 0 means 'success'
1141 }
1142 
1143 int DLGModifyControlListInfo(WBDialogControl *pCtrl, int bFlags, int nFlags,
1144  int bAllocator, void *(*pfnAllocator)(const void *,int),
1145  int bDestructor, void (*pfnDestructor)(void *),
1146  int bDisplay, void (*pfnDisplay)(WBDialogControl *, void *, int, GC, WB_GEOM *, XFontSet),
1147  int bSort, int (*pfnSort)(const void *, const void *))
1148 {
1149 WB_DIALOG_PROP propTemp;
1150 const WB_DIALOG_PROP *pProp;
1151 LISTINFO *pLI;
1152 //int i1;
1153 
1154 
1156 
1157  if(!pProp)
1158  {
1159  // see DLGInitControlListInfoDefault for canonical default values (this should match it actually)
1160 
1161  return DLGInitControlListInfo(pCtrl, bFlags ? nFlags : ListInfoFlags_SORTED,
1162  bAllocator ? pfnAllocator : DLGCDefaultListInfoAllocator,
1163  bDestructor ? pfnDestructor : WBFree,
1164  bDisplay ? pfnDisplay : NULL,
1165  bSort ? pfnSort : NULL);
1166  }
1167 
1168  memcpy(&propTemp, pProp, sizeof(propTemp));
1169 
1170  if(!propTemp.pVal)
1171  {
1172  WB_ERROR_PRINT("%s - DLGCListInfoConstructor returns NULL\n", __FUNCTION__);
1173  return -2;
1174  }
1175 
1176  pLI = (LISTINFO *)propTemp.pVal;
1177 
1178  // assuming it's ok, assign the various information as specified.
1179 
1180  if(bFlags)
1181  {
1182  pLI->nFlags = nFlags;
1183  }
1184 
1185  if(bAllocator)
1186  {
1187  pLI->pfnAllocator = pfnAllocator;
1188  }
1189 
1190  if(bDestructor)
1191  {
1192  pLI->pfnDestructor = pfnDestructor;
1193  }
1194 
1195  if(bDisplay)
1196  {
1197  if(pfnDisplay)
1198  {
1199  pLI->pfnDisplay = pfnDisplay;
1200  }
1201  else
1202  {
1204  }
1205  }
1206 
1207  if(bSort)
1208  {
1209  if(pfnSort)
1210  {
1211  pLI->pfnSort = pfnSort;
1212  }
1213  else
1214  {
1215  pLI->pfnSort = (int (*)(const void *,const void *))strcmp;
1216  }
1217  }
1218 
1219  return 0;
1220 }
1221 
1222 LISTINFO *DLGCListInfoConstructor(Window wOwner, int nMax, int nFlags,
1223  void *(*pfnAllocator)(const void *,int), void (*pfnDestructor)(void *),
1224  void (*pfnDisplay)(WBDialogControl *, void *, int, GC, WB_GEOM *, XFontSet),
1225  int (*pfnSort)(const void *, const void *))
1226 {
1227 LISTINFO *pRval = WBAlloc(sizeof(*pRval) + nMax * sizeof(const void *));
1228 
1229  if(pRval)
1230  {
1231  pRval->nItems = 0;
1232  pRval->nMaxItems = nMax;
1233  pRval->nPos = 0; // current scroll position (initially zero)
1234  pRval->nTop = 0; // index of top item that's visible
1235  pRval->nHeight = 0; // height of viewport in 'items'
1236  pRval->nFlags = nFlags;
1237  pRval->wOwner = wOwner;
1238  pRval->pfnAllocator = pfnAllocator;
1239  pRval->pfnDestructor = pfnDestructor;
1240  if(pfnDisplay)
1241  {
1242  pRval->pfnDisplay = pfnDisplay;
1243  }
1244  else
1245  {
1247  }
1248  if(pfnSort)
1249  {
1250  pRval->pfnSort = pfnSort;
1251  }
1252  else
1253  {
1254  pRval->pfnSort = (int (*)(const void *,const void *))strcmp;
1255  }
1256  }
1257 
1258  return pRval;
1259 }
1260 
1262 {
1263 int i1;
1264 void **paData;
1265 
1266  if(!pListInfo)
1267  {
1268  return;
1269  }
1270 
1271  paData = pListInfo->aItems;
1272 
1274  "%s - destroying %d items using %p for %p\n",
1275  __FUNCTION__, pListInfo->nItems, pListInfo->pfnDestructor, pListInfo->aItems);
1276 
1277  for(i1=0; i1 < pListInfo->nItems; i1++)
1278  {
1280  "%s - destroying item %d, value = %p\n",
1281  __FUNCTION__, i1, paData[i1]);
1282 
1283  if(pListInfo->pfnDestructor)
1284  {
1285  pListInfo->pfnDestructor(paData[i1]);
1286  }
1287  else if(paData[i1])
1288  {
1289  WBFree(paData[i1]);
1290  }
1291  }
1292 
1293  WBFree(pListInfo);
1294 }
1295 
1296 //typedef struct __LISTINFO__
1297 //{
1298 // int nItems, nMaxItems; // size/max size of aItems (must re-alloc to increase nMaxItems)
1299 // int nFlags; // flags (sorted, etc.)
1300 // Window wOwner; // owning window [to be notified on change]
1301 // void *(*pfnAllocator)(void *, int); // copy constructor to call for each item that's added
1302 // // typically this will call 'WBAlloc' followed by 'memcpy'
1303 // // if NULL, the caller-supplied pointer is assigned to 'aItems' as-is
1304 // void (*pfnDestructor)(void *); // destructor to call for each item that's removed
1305 // // typically this will point to 'WBFree'
1306 // // if NULL, the caller-supplied pointer is ignored
1307 //
1308 // void (*pfnDisplay)(WBDialogControl *pControl, void *pData, int iSelected, GC gcPaint, WB_GEOM *pGeom);
1309 // // generic function to display contents of item within 'pGeom' using GC
1310 // // typically one of the listbox 'display item' functions
1311 //
1312 // int (*pfnSort)(const void *, const void *); // sort proc (NULL implies strcmp)
1313 //
1314 // void *aItems[1]; // array of item data (remainder of struct)
1315 //} LISTINFO;
1316 
1317 const char * DLGGetControlListText(WBDialogControl *pCtrl, int iIndex)
1318 {
1319  const LISTINFO *pListInfo;
1321 
1322  if(!pProp || !pProp->pVal)
1323  {
1324  return NULL;
1325  }
1326 
1327  pListInfo = (LISTINFO *)(pProp->pVal);
1328 
1329  if(iIndex == ControlListIndex_LAST)
1330  {
1331  iIndex = pListInfo->nItems - 1;
1332  }
1333 
1334  if(iIndex < 0 || iIndex >= pListInfo->nItems)
1335  {
1336  return NULL;
1337  }
1338 
1339  if(!(pListInfo->aItems[iIndex]))
1340  {
1341  return ""; // always a non-NULL return if the item exists
1342  }
1343 
1344  return (const char *)(pListInfo->aItems[iIndex]);
1345 }
1346 
1347 const void * DLGGetControlListData(WBDialogControl *pCtrl, int iIndex)
1348 {
1349  const LISTINFO *pListInfo;
1351 
1352  if(!pProp || !pProp->pVal)
1353  {
1354  return NULL;
1355  }
1356 
1357  pListInfo = (LISTINFO *)(pProp->pVal);
1358 
1359  if(iIndex == ControlListIndex_LAST)
1360  {
1361  iIndex = pListInfo->nItems - 1;
1362  }
1363 
1364  if(iIndex < 0 || iIndex >= pListInfo->nItems)
1365  {
1366  return NULL;
1367  }
1368 
1369  return pListInfo->aItems[iIndex];
1370 }
1371 
1372 static DECLARE_SORT_FUNCTION(_actual_sort_proc,p0,p1,p2)
1373 //static int _actual_sort_proc(void *p0, const void *p1, const void *p2)
1374 {
1375  const void *p1a = *((const void * const *)p1);
1376  const void *p2a = *((const void * const *)p2);
1377  LISTINFO *pListInfo = (LISTINFO *)p0;
1378 
1379  if(pListInfo->pfnSort)
1380  {
1381  return pListInfo->pfnSort(p1a, p2a);
1382  }
1383 
1384  return strcmp(p1a, p2a);
1385 }
1386 
1387 int DLGAddControlListEntry(WBDialogControl *pCtrl, const char *pData, long cbData, int iIndex)
1388 {
1389  LISTINFO *pListInfo, *pListInfo0;
1391  WB_DIALOG_PROP propTemp;
1392  int i1;
1393  void *pTemp;
1394 
1395  if(!pProp || !pProp->pVal)
1396  {
1397  if(!(pCtrl->ulFlags & CONTROL_SupportListInfo))
1398  {
1399  // check first for the property, then return an error if it's
1400  // not supported (you never know...)
1401 
1402  WB_ERROR_PRINT("%s - control does not support 'LISTINFO'\n", __FUNCTION__);
1403  return -3;
1404  }
1405 
1406  pListInfo0 = NULL;
1407 
1408  // use the default settings to initialize the LISTINFO
1409  propTemp.aProp = aDLGC_LISTINFO;
1410  propTemp.lVal = 0;
1411  propTemp.pVal = DLGCListInfoConstructor(pCtrl->wID, DEFAULT_LISTINFO_MAX, ListInfoFlags_SORTED,
1413  WBFree,
1415  NULL);
1416 
1417  if(!propTemp.pVal)
1418  {
1419  WB_ERROR_PRINT("%s - unable to allocate 'LISTINFO'\n", __FUNCTION__);
1420  return -3; // error (unable to allocate LISTINFO)
1421  }
1422  }
1423  else
1424  {
1425  propTemp.aProp = pProp->aProp;
1426  propTemp.lVal = pProp->lVal;
1427  propTemp.pVal = pProp->pVal;
1428 
1429  pListInfo0 = (LISTINFO *)propTemp.pVal;
1430  }
1431 
1432  pListInfo = (LISTINFO *)propTemp.pVal;
1433 
1434  if(iIndex == ControlListIndex_INSERT_FIRST)
1435  {
1436  iIndex = -1; // note - this should really have no effect at all
1437  }
1438  else if(iIndex == ControlListIndex_INSERT_LAST)
1439  {
1440  iIndex = pListInfo->nItems - 1;
1441  }
1442  else if(iIndex < 0 || iIndex >= pListInfo->nItems)
1443  {
1444  if(!pListInfo0)
1445  {
1446  DLGCListInfoDestructor(propTemp.pVal);
1447  }
1448 
1449  WB_ERROR_PRINT("%s - invalid index %d\n", __FUNCTION__, iIndex);
1450 
1451  return -2;
1452  }
1453 
1454  if((iIndex + 1) >= pListInfo->nMaxItems)
1455  {
1456  int nItems = pListInfo->nMaxItems;
1457  // time to re-allocate
1458 
1459  if(nItems <= 0x40000)
1460  {
1461  nItems *= 2;
1462  }
1463  else
1464  {
1465  nItems += 0x40000;
1466  }
1467 
1468  i1 = sizeof(*pListInfo)
1469  + nItems * sizeof(pListInfo->aItems[0]);
1470 
1471  propTemp.pVal = WBReAlloc(pListInfo, i1);
1472 
1473  if(!propTemp.pVal)
1474  {
1475  if(!pListInfo0) // extremely unlikely, if not impossible, extremely defensive code
1476  {
1477  WB_ERROR_PRINT("%s:%d %s - this should never happen\n", __FILE__, __LINE__, __FUNCTION__);
1478  DLGCListInfoDestructor(pListInfo); // destroy the one I just created
1479  }
1480 
1481  return -4;
1482  }
1483 
1484  pListInfo->nMaxItems = nItems;
1485  // now I must go to the property list entry itself and forcibly assign this new pointer
1486 
1487  if(pProp)
1488  {
1489  pProp->pVal = propTemp.pVal; // this prevents memory leaks in case of an error
1490  }
1491 
1492  pListInfo = (LISTINFO *)propTemp.pVal; // re-assign to new value
1493  }
1494 
1495  if(!cbData)
1496  {
1497 // WB_ERROR_PRINT("TEMPORARY: %s:%s:%d NULL cbData\n", __FILE__, __FUNCTION__, __LINE__);
1498  pTemp = (void *)pData;
1499  }
1500  else if(pListInfo->pfnAllocator)
1501  {
1502  pTemp = pListInfo->pfnAllocator(pData, cbData);
1503 
1505 // if(pTemp == pData)
1506 // {
1507 // WB_ERROR_PRINT("%s:%s:%d pData == pTemp %p\n", __FILE__, __FUNCTION__, __LINE__, pTemp);
1508 // }
1509  }
1510  else
1511  {
1512  pTemp = NULL;
1513  }
1514 
1515  if(pListInfo->nFlags & ListInfoFlags_SORTED) // regardless of index, when sorted, do this
1516  {
1517  // add to the end to simplify the process, then qsort
1518  pListInfo->aItems[pListInfo->nItems] = (void *)pTemp;
1519 
1520  pListInfo->nItems++;
1521 
1522  // note: QSORT_R macro needed because qsort_r differs between linux and BSD
1523  QSORT_R(pListInfo->aItems, pListInfo->nItems, sizeof(void *), pListInfo, _actual_sort_proc);
1524  }
1525  else
1526  {
1527  if(iIndex < pListInfo->nItems)
1528  {
1529  for(i1=pListInfo->nItems; i1 >= iIndex; i1--)
1530  {
1531  pListInfo->aItems[i1 + 1] = pListInfo->aItems[i1];
1532  }
1533  }
1534 
1535  pListInfo->aItems[iIndex] = (void *)pTemp;
1536 
1537  pListInfo->nItems++;
1538  }
1539 
1540  i1 = WBDialogControlSetDialogProp(pCtrl, &propTemp);
1541 
1542  if(i1)
1543  {
1544  if(!pListInfo0) // attempted to add new item and failed?
1545  {
1546  DLGCListInfoDestructor(propTemp.pVal);
1547  }
1548 
1549  return i1;
1550  }
1551 
1552 // WB_ERROR_PRINT("TEMPORARY: %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);
1553 // DEBUG_DUMP_LIST(pCtrl);
1554 
1555  return 0; // meaning 'success'
1556 }
1557 
1558 void DLGDelControlListEntry(WBDialogControl *pCtrl, int iIndex)
1559 {
1560  LISTINFO *pListInfo;
1562  int i1;
1563 
1564  if(!pProp || !pProp->pVal)
1565  {
1566  WB_WARN_PRINT("%s - unable to get LISTINFO for window %d (%08xH), pProp=%p\n", __FUNCTION__,
1567  (int)pCtrl->wID, (int)pCtrl->wID, pProp);
1568  return;
1569  }
1570 
1571  pListInfo = (LISTINFO *)pProp->pVal;
1572 
1573  if(iIndex == ControlListIndex_LAST)
1574  {
1575  iIndex = pListInfo->nItems - 1;
1576  }
1577  else if(iIndex == ControlListIndex_DELETE_ALL)
1578  {
1579  if(pListInfo->pfnDestructor)
1580  {
1581  for(i1=0; i1 < pListInfo->nItems; i1++)
1582  {
1583  pListInfo->pfnDestructor(pListInfo->aItems[i1]);
1584  }
1585  }
1586 
1587  pListInfo->aItems[0] = NULL; // do this by convention
1588  pListInfo->nItems = 0;
1589 
1590  return;
1591  }
1592  else if(iIndex < 0 || iIndex >= pListInfo->nItems)
1593  {
1594  WB_ERROR_PRINT("%s - invalid index %d\n", __FUNCTION__, iIndex);
1595  return;
1596  }
1597 
1598  if(pListInfo->pfnDestructor)
1599  {
1600  pListInfo->pfnDestructor(pListInfo->aItems[iIndex]);
1601  }
1602 
1603  pListInfo->nItems--;
1604 
1605  for(i1=iIndex; i1 < pListInfo->nItems; i1++)
1606  {
1607  pListInfo->aItems[i1] = pListInfo->aItems[i1 + 1];
1608  }
1609 
1610  pListInfo->aItems[pListInfo->nItems] = NULL; // by convention
1611 
1612 // WB_ERROR_PRINT("TEMPORARY: %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);
1613 // DEBUG_DUMP_LIST(pCtrl);
1614 }
1615 
1616 
1617 //typedef struct _WB_LIST_CURSEL_
1618 //{
1619 // int iCurSel; // current selection
1620 // int iTopIndex; // index of item at top of window
1621 // int iHeight; // calculated height of window in "entries" (see next member)
1622 // int iEntryHeight; // cached display height of each entry (calculated by Expose handler)
1623 //} WBListCurSel; // intended to be member following WBDialogControl for "list" controls
1624 
1625 
1626 // getting "extension" structures from dialog control structure
1627 static WBListCurSel * __GetListCurSel(WBDialogControl *pCtrl)
1628 {
1629 WBListCurSel *pSel = (WBListCurSel *)(pCtrl + 1);
1630 
1631  if(!pCtrl || !(pCtrl->ulFlags & CONTROL_SupportListInfo)
1632  || (unsigned char *)(pSel + 1) > (((unsigned char *)pCtrl) + pCtrl->cbStructSize))
1633  {
1634  return NULL;
1635  }
1636 
1637  return pSel;
1638 }
1639 
1640 
1642 {
1644 WBListCurSel *pSel = __GetListCurSel(pCtrl);
1645 
1646  if(!pSel || !pProp || !pProp->pVal)
1647  {
1648  return ControlListIndex_NONE; // "no selection"
1649  }
1650 
1651  return pSel->iCurSel;
1652 }
1653 
1655 {
1657 WBListCurSel *pSel = __GetListCurSel(pCtrl);
1658 LISTINFO *pListInfo;
1659 int iOldIndex;
1660 
1661  if(!pSel || !pProp || !pProp->pVal)
1662  {
1663  WB_WARN_PRINT("%s - unable to get LISTINFO or 'selection info' for window %d (%08xH), pSel=%p, pProp=%p\n", __FUNCTION__,
1664  (int)pCtrl->wID, (int)pCtrl->wID, pSel, pProp);
1665  return;
1666  }
1667 
1668  pListInfo = (LISTINFO *)(pProp->pVal);
1669 
1670  iOldIndex = pSel->iCurSel;
1671 
1672  if(iIndex == ControlListIndex_LAST)
1673  {
1674  pSel->iCurSel = pListInfo->nItems - 1;
1675  }
1676  else if((iIndex >= 0 && iIndex < pListInfo->nItems)
1677  || iIndex == ControlListIndex_NONE)
1678  {
1679  pSel->iCurSel = iIndex;
1680  }
1681  else
1682  {
1683  WB_WARN_PRINT("%s - invalid index %d (nItems=%d)\n", __FUNCTION__, iIndex, pListInfo->nItems);
1684  return;
1685  }
1686 
1687  if(iOldIndex != pSel->iCurSel) // notify myself that something changed
1688  {
1689  DLGNotifySelf(pCtrl, aLIST_SELCHANGE, pSel->iCurSel, iOldIndex, 0, 0, 0);
1690  }
1691 }
1692 
1693 void DLGSetControlListSelectionValue(WBDialogControl *pCtrl, int iIndex, int iSelState)
1694 {
1695 }
1696 
1697 int DLGGetControlListSelectionBits(WBDialogControl *pCtrl, unsigned int *piBits, int nSize)
1698 {
1699  return 0; // for now (warning avoidance)
1700 }
1701 
1703 {
1704 int iSel = DLGGetControlListSelection(pCtrl);
1705 
1706  if(iSel >= 0)
1707  {
1708  return WBCopyString(DLGGetControlListText(pCtrl, iSel));
1709  }
1710 
1711  // TODO: multiple selections
1712 
1713  return NULL;
1714 }
1715 
1716 
1717 
1719 // the default list control display proc
1721 
1722 void DLGCDefaultListControlDisplayProc(WBDialogControl *pList, void *pData, int iSelected, GC gc, WB_GEOM *pGeom, XFontSet fontSet)
1723 {
1724 int iHPos;
1725 Window wID = pList->wID;
1726 Display *pDisplay = WBGetWindowDisplay(wID);
1727 
1728 
1730  "%s - Expose %d (%08xH) pData=%p\n", __FUNCTION__, (int)wID, (int)wID, pData);
1731 
1732  if(fontSet == None)
1733  {
1734  fontSet = WBGetWindowFontSet(wID);
1735 
1736  if(fontSet == None)
1737  {
1738  fontSet = WBGetDefaultFontSet(pDisplay);
1739  }
1740  }
1741 
1742  if(fontSet == None)
1743  {
1744  // TODO: get font from dialog info
1745  WB_WARN_PRINT("%s - * BUG * line %d\n", __FUNCTION__, __LINE__);
1746  return;
1747  }
1748 
1749  iHPos = WBFontSetAvgCharWidth(pDisplay, fontSet); // average character width is new horiz pos
1750 
1751  // font setup
1753 // XClearWindow(pDisplay, wID); // TODO: rather than erase background, see if I need to
1754  XSetForeground(pDisplay, gc, iSelected ? pList->clrHBG.pixel : pList->clrBG.pixel);
1755  XFillRectangle(pDisplay, wID, gc, pGeom->x, pGeom->y, pGeom->width, pGeom->height);
1757 
1758  if(iSelected)
1759  {
1760  WBDrawDashedRect(pDisplay, wID, gc, pGeom, pList->clrBD2.pixel);
1761  }
1762 
1763 
1764  // vertically centered text
1765 // iVPos = pFont->max_bounds.ascent + pFont->max_bounds.descent; // font height
1766 // iVPos = (pGeom->height - iVPos) >> 1; // half of the difference - top of text
1767 // iVPos += pFont->max_bounds.ascent;
1768 
1769  // painting the window text
1770 
1771  if(pData)
1772  {
1773  const char *szText = (const char *)pData;
1774  WB_RECT rctBounds;
1775 
1776  rctBounds.left = pGeom->x + iHPos;
1777  rctBounds.right = pGeom->x + pGeom->width - iHPos; // equal border on right side, too
1778  rctBounds.top = pGeom->y;
1779  rctBounds.bottom = pGeom->y + pGeom->height;
1780 
1781 
1782  XSetForeground(pDisplay, gc, iSelected ? pList->clrHFG.pixel : pList->clrFG.pixel);
1783  XSetBackground(pDisplay, gc, iSelected ? pList->clrHBG.pixel : pList->clrBG.pixel);
1784 
1785  if(*szText)
1786  {
1787  DTDrawSingleLineText(fontSet, szText, pDisplay, gc, wID, 0, 0, &rctBounds,
1789  }
1790 
1791  if(iSelected) // selected item
1792  {
1793  XSetForeground(pDisplay, gc, pList->clrFG.pixel);
1794  XSetBackground(pDisplay, gc, pList->clrBG.pixel);
1795  }
1796  }
1797 
1798  // by convention, restore original objects/state
1799 
1801  XSetForeground(pDisplay, gc, WBGetWindowFGColor(wID)); // restore it at the end
1803 }
1804 
1805 
1806 
1807 
1809 // S C R O L L B A R H A N D L E R
1811 
1812 int DLGScrollBarHandler(Window wID, WBDialogControl *pCtrl, XEvent *pEvent)
1813 {
1814 int iX, iY, iDirection, iPosition;
1815 WB_SCROLLINFO *pScrollInfo;
1816 
1817 
1818  if(pEvent->type != ClientMessage)
1819  {
1820  return 0;
1821  }
1822 
1823  // look for the following:
1824  // left-button click inside of scroll bar
1825  // left button click ON the knob
1826  // left button click above/below/right-of/left-of knob
1827  // left button click on top/bottom/left/right arrow
1828 
1829  if(pEvent->xclient.message_type == aWM_POINTER)
1830  {
1831  // pointer messages - cooked mousie clickie
1832  WB_ERROR_PRINT("TEMPORARY: %s mouse message %d (%08xH) %d %d %d %d %d\n",
1833  __FUNCTION__,
1834  (int)pEvent->xclient.window, (int)pEvent->xclient.window,
1835  (int)pEvent->xclient.data.l[0],
1836  (int)pEvent->xclient.data.l[1],
1837  (int)pEvent->xclient.data.l[2],
1838  (int)pEvent->xclient.data.l[3],
1839  (int)pEvent->xclient.data.l[4]);
1840 
1841  if(pEvent->xclient.data.l[0] == WB_POINTER_CLICK)
1842  {
1843  // TODO: handle shift-click, ctrl-click, alt-click
1844 
1845  if(pEvent->xclient.data.l[1] == WB_POINTER_BUTTON1 && // left button
1846  !pEvent->xclient.data.l[2])
1847  {
1848  iX = pEvent->xclient.data.l[3];
1849  iY = pEvent->xclient.data.l[4];
1850 
1852 
1853  if(WB_LIKELY(pScrollInfo != NULL))
1854  {
1855  iDirection = WB_SCROLL_NA;
1856  iPosition = 0;
1857 
1858  if(WBPointInGeom(iX, iY, pScrollInfo->geomVBar))
1859  {
1860  if(WBPointInGeom(iX, iY, pScrollInfo->geomVUp))
1861  {
1862  iDirection = WB_SCROLL_BACKWARD;
1863  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (up)\n", __FUNCTION__);
1864  }
1865  else if(WBPointInGeom(iX, iY, pScrollInfo->geomVDown))
1866  {
1867  iDirection = WB_SCROLL_FORWARD;
1868  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (down)\n", __FUNCTION__);
1869  }
1870  else if(WBPointInGeom(iX, iY, pScrollInfo->geomVKnob))
1871  {
1872  // ON THE KNOB - VScroll
1873 
1874  iDirection = WB_SCROLL_KNOB;
1875 // iPosition = pScrollInfo->iVMin; //pListInfo->nTop; // NO!
1876  iPosition = WBCalcVScrollDragPos(pScrollInfo, iY);
1877 
1878  if(iPosition < 0)
1879  {
1880  iPosition = pScrollInfo->iVMin; //pListInfo->nTop;
1881  }
1882 
1883  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (knob)\n", __FUNCTION__);
1884 
1885  // TODO: determine position of knob
1886  }
1887  else if(iY >= pScrollInfo->geomVUp.y + pScrollInfo->geomVUp.height &&
1888  iY < pScrollInfo->geomVKnob.y)
1889  {
1890  iDirection = WB_SCROLL_PAGEBACK;
1891  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (page up)\n", __FUNCTION__);
1892  }
1893  else if(iY >= pScrollInfo->geomVKnob.y + pScrollInfo->geomVKnob.height &&
1894  iY < pScrollInfo->geomVDown.y)
1895  {
1896  iDirection = WB_SCROLL_PAGEFWD;
1897  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (page down)\n", __FUNCTION__);
1898  }
1899  else
1900  {
1901  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (unknown)\n", __FUNCTION__);
1902  }
1903 
1904  DLGNotifySelf(pCtrl, aSCROLL_NOTIFY, WB_SCROLL_VERTICAL, iDirection, iPosition, 0, 0);
1905 
1906  return 1; // handled
1907  }
1908  else if(WBPointInGeom(iX, iY, pScrollInfo->geomHBar))
1909  {
1910  if(WBPointInGeom(iX, iY, pScrollInfo->geomHLeft))
1911  {
1912  iDirection = WB_SCROLL_BACKWARD;
1913  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (left)\n", __FUNCTION__);
1914  }
1915  else if(WBPointInGeom(iX, iY, pScrollInfo->geomHRight))
1916  {
1917  iDirection = WB_SCROLL_FORWARD;
1918  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (right)\n", __FUNCTION__);
1919  }
1920  else if(WBPointInGeom(iX, iY, pScrollInfo->geomHKnob))
1921  {
1922  // ON THE KNOB - HScroll
1923 
1924  iDirection = WB_SCROLL_KNOB;
1925 // iPosition = pScrollInfo->iHMin; // NO!
1926  iPosition = WBCalcHScrollDragPos(pScrollInfo, iY);
1927 
1928  if(iPosition < 0)
1929  {
1930  iPosition = pScrollInfo->iHMin; //pListInfo->nTop;
1931  }
1932 
1933  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (knob)\n", __FUNCTION__);
1934 
1935  // TODO: determine position of knob
1936  }
1937  else if(iX >= pScrollInfo->geomHLeft.x + pScrollInfo->geomHLeft.width &&
1938  iX < pScrollInfo->geomHKnob.x)
1939  {
1940  iDirection = WB_SCROLL_PAGEBACK;
1941  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (page left)\n", __FUNCTION__);
1942  }
1943  else if(iX >= pScrollInfo->geomHKnob.x + pScrollInfo->geomHKnob.width &&
1944  iX < pScrollInfo->geomHRight.x)
1945  {
1946  iDirection = WB_SCROLL_PAGEFWD;
1947  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (page right)\n", __FUNCTION__);
1948  }
1949  else
1950  {
1951  WB_ERROR_PRINT("TEMPORARY - %s Mouse click in scroll bar (unknown)\n", __FUNCTION__);
1952  }
1953 
1954  DLGNotifySelf(pCtrl, aSCROLL_NOTIFY, WB_SCROLL_HORIZONTAL, iDirection, iPosition, 0, 0);
1955 
1956  return 1; // handled
1957  }
1958  }
1959  }
1960  }
1961  else if(pEvent->xclient.data.l[0] == WB_POINTER_DBLCLICK)
1962  {
1963  if(pEvent->xclient.data.l[1] == WB_POINTER_BUTTON1 && // left button
1964  !pEvent->xclient.data.l[2])
1965  {
1966  iX = pEvent->xclient.data.l[3];
1967  iY = pEvent->xclient.data.l[4];
1968  // assume selection already done, so notify owner
1969 
1971 
1972  if(WB_LIKELY(pScrollInfo != NULL))
1973  {
1974  if(WBPointInGeom(iX, iY, pScrollInfo->geomVBar))
1975  {
1976  // if not within knob, re-post to self as single-click
1977  if(!WBPointInGeom(iX, iY, pScrollInfo->geomVKnob))
1978  {
1979  XClientMessageEvent evt;
1980  memcpy(&evt, pEvent, sizeof(evt));
1981  evt.data.l[0] = WB_POINTER_CLICK;
1982 
1983  return DLGScrollBarHandler(wID, pCtrl, (XEvent *)&evt);
1984  }
1985  }
1986  else if(WBPointInGeom(iX, iY, pScrollInfo->geomHBar))
1987  {
1988  // if not within knob, re-post to self as single-click
1989  if(!WBPointInGeom(iX, iY, pScrollInfo->geomHKnob))
1990  {
1991  XClientMessageEvent evt;
1992  memcpy(&evt, pEvent, sizeof(evt));
1993  evt.data.l[0] = WB_POINTER_CLICK;
1994 
1995  return DLGScrollBarHandler(wID, pCtrl, (XEvent *)&evt);
1996  }
1997  }
1998  }
1999  }
2000  }
2001  else if(pEvent->xclient.data.l[0] == WB_POINTER_CANCEL)
2002  {
2003  // canceling drag (as appropriate)
2004 
2006 
2007 // if(pScrollInfo &&
2008 // pEvent->xclient.data.l[1] == WB_POINTER_BUTTON1 && // left button
2009 // !pEvent->xclient.data.l[2])
2010 // {
2011 // pScrollInfo->iScrollState &= ~WBScrollState_LDRAG;
2012 // }
2013  if((pScrollInfo->iScrollState & WBScrollState_LDRAG) ||
2014  (pScrollInfo->iScrollState & WBScrollState_MDRAG) ||
2015  (pScrollInfo->iScrollState & WBScrollState_RDRAG))
2016  {
2018 
2019  return 1; // "handled"
2020  }
2021  }
2022  else if(pEvent->xclient.data.l[0] == WB_POINTER_DRAG ||
2023  pEvent->xclient.data.l[0] == WB_POINTER_MOVE)
2024  {
2025  if(pEvent->xclient.data.l[1] == WB_POINTER_BUTTON1 && // left button
2026  !pEvent->xclient.data.l[2])
2027  {
2028  iX = pEvent->xclient.data.l[3];
2029  iY = pEvent->xclient.data.l[4];
2030 
2032 
2033  if(WB_LIKELY(pScrollInfo != NULL))
2034  {
2035  if(!WBPointInGeom(iX, iY, pScrollInfo->geomVBar) &&
2036  !WBPointInGeom(iX, iY, pScrollInfo->geomHBar) &&
2037  (!(pScrollInfo->iScrollState & WBScrollState_LDRAG) ||
2038  pEvent->xclient.data.l[0] == WB_POINTER_DRAG))
2039  {
2040  // TODO: this is for multi-select listboxes, doing a drag-select
2041 
2042  pScrollInfo->iScrollState &= ~WBScrollState_LDRAG; // make sure
2043 
2044  return 0; // "not handled" (allow drag-select to be handled by scrollbar owner)
2045  }
2046  else if(WBPointInGeom(iX, iY, pScrollInfo->geomVKnob) ||
2047  WB_LIKELY(pEvent->xclient.data.l[0] == WB_POINTER_MOVE &&
2048  (pScrollInfo->iScrollState & WBScrollState_LDRAG)))
2049  {
2050  if(pEvent->xclient.data.l[0] == WB_POINTER_DRAG) // begin drag, return window ID
2051  {
2052  WB_ERROR_PRINT("TEMPORARY - %s Mouse drag in scroll bar (knob)\n", __FUNCTION__);
2053 
2054  pScrollInfo->iScrollState |= WBScrollState_LDRAG; // set the state bit for left-drag
2055  return((int)wID); // enabling the drag
2056  }
2057 
2058  iPosition = WBCalcVScrollDragPos(pScrollInfo, iY);
2059 
2060  if(iPosition < 0)
2061  {
2062  iPosition = pScrollInfo->iVMin; //pListInfo->nTop;
2063  }
2064 
2065  // track the mouse position along the center of the knob, if possible
2066 
2067  WB_ERROR_PRINT("TEMPORARY - %s Mouse motion in scroll bar (knob)\n", __FUNCTION__);
2068 
2070  WB_SCROLL_KNOB, iPosition, 0, 0);
2071  }
2072  else if(WBPointInGeom(iX, iY, pScrollInfo->geomHKnob) ||
2073  WB_LIKELY(pEvent->xclient.data.l[0] == WB_POINTER_MOVE &&
2074  (pScrollInfo->iScrollState & WBScrollState_HLDRAG)))
2075  {
2076  if(pEvent->xclient.data.l[0] == WB_POINTER_DRAG) // begin drag, return window ID
2077  {
2078  WB_ERROR_PRINT("TEMPORARY - %s Mouse drag in scroll bar (knob)\n", __FUNCTION__);
2079 
2080  pScrollInfo->iScrollState |= WBScrollState_HLDRAG; // set the state bit for left-drag
2081  return((int)wID); // enabling the drag
2082  }
2083 
2084  iPosition = WBCalcHScrollDragPos(pScrollInfo, iX);
2085 
2086  if(iPosition < 0)
2087  {
2088  iPosition = pScrollInfo->iHMin; // pListInfo->nTop;
2089  }
2090 
2091  // track the mouse position along the center of the knob, if possible
2092 
2093  WB_ERROR_PRINT("TEMPORARY - %s Mouse motion in scroll bar (knob)\n", __FUNCTION__);
2094 
2096  WB_SCROLL_KNOB, iPosition, 0, 0);
2097  }
2098  else
2099  {
2100  WB_ERROR_PRINT("TEMPORARY - %s mouse motion in scroll bar outside of knob\n", __FUNCTION__);
2101  }
2102  }
2103  }
2104  }
2105  else if(pEvent->xclient.data.l[0] == WB_POINTER_DROP)
2106  {
2108 
2109  if(WB_LIKELY(pScrollInfo != NULL))
2110  {
2111  if((pScrollInfo->iScrollState & WBScrollState_LDRAG) ||
2112  (pScrollInfo->iScrollState & WBScrollState_MDRAG) ||
2113  (pScrollInfo->iScrollState & WBScrollState_RDRAG))
2114  {
2116 
2117  return 1; // "handled" (just a notification anyway)
2118  }
2119  }
2120  }
2121  else if(pEvent->xclient.data.l[0] == WB_POINTER_SCROLLUP)
2122  {
2124  }
2125  else if(pEvent->xclient.data.l[0] == WB_POINTER_SCROLLDOWN)
2126  {
2128  }
2129  }
2130  else if(pEvent->xclient.message_type == aWM_CHAR)
2131  {
2132  // handle cursors only - up, down, left, right, home, end, page up, page down, etc.
2133 
2134  long lKey = pEvent->xclient.data.l[0]; // return from WBKeyEventProcessKey
2135  long lAltCtrlShift = pEvent->xclient.data.l[1]; // *piAltCtrlShift from WBKeyEventProcessKey
2136 #ifndef NO_DEBUG
2137  int nChar = (int)pEvent->xclient.data.l[2]; // # of characters decoded into pBuf (below)
2138  char *pBuf = (char *)&(pEvent->xclient.data.l[3]); // decode buffer (at least 8 chars in length)
2139 #endif // !NO_DEBUG
2140 
2141 
2142  WB_ERROR_PRINT("TEMPORARY: %s char message %lx %ld %d %s\n", __FUNCTION__, lAltCtrlShift, lKey, nChar, pBuf);
2143 
2144  if(lAltCtrlShift & WB_KEYEVENT_KEYSYM) // symbol keys only for this part
2145  {
2147 
2148  if(WB_LIKELY(pScrollInfo != NULL))
2149  {
2150  int iShift = lAltCtrlShift & WB_KEYEVENT_SHIFT;
2151  int iCtrl = lAltCtrlShift & WB_KEYEVENT_CTRL;
2152  int iAlt = lAltCtrlShift & WB_KEYEVENT_ALT;
2153  int iBar = WB_SCROLL_NA;
2154  int iDirection = WB_SCROLL_NA;
2155 
2156  switch(lKey)
2157  {
2158  case XK_Up:
2159  if(iShift || iCtrl || iAlt)
2160  {
2161  return 0;
2162  }
2163 
2164  iBar = WB_SCROLL_VERTICAL;
2165  iDirection = WB_SCROLL_BACKWARD;
2166  break;
2167 
2168  case XK_Down:
2169  if(iShift || iCtrl || iAlt)
2170  {
2171  return 0;
2172  }
2173 
2174  iBar = WB_SCROLL_VERTICAL;
2175  iDirection = WB_SCROLL_FORWARD;
2176  break;
2177 
2178  case XK_Left:
2179  if(iShift || iCtrl || iAlt)
2180  {
2181  return 0;
2182  }
2183 
2184  iBar = WB_SCROLL_HORIZONTAL;
2185  iDirection = WB_SCROLL_BACKWARD;
2186  break;
2187 
2188  case XK_Right:
2189  if(iShift || iCtrl || iAlt)
2190  {
2191  return 0;
2192  }
2193 
2194  iBar = WB_SCROLL_HORIZONTAL;
2195  iDirection = WB_SCROLL_FORWARD;
2196  break;
2197 
2198  case XK_Home:
2199 
2200  if(iShift || iAlt)
2201  {
2202  return 0;
2203  }
2204 
2205  if(iCtrl)
2206  {
2207  iBar = WB_SCROLL_VERTICAL;
2208  }
2209  else
2210  {
2211  iBar = WB_SCROLL_HORIZONTAL;
2212  }
2213 
2214  iDirection = WB_SCROLL_FIRST;
2215  break;
2216 
2217  case XK_End:
2218 
2219  if(iShift || iAlt)
2220  {
2221  return 0;
2222  }
2223 
2224  if(iCtrl)
2225  {
2226  iBar = WB_SCROLL_VERTICAL;
2227  }
2228  else
2229  {
2230  iBar = WB_SCROLL_HORIZONTAL;
2231  }
2232 
2233  iDirection = WB_SCROLL_LAST;
2234  break;
2235 
2236  case XK_Page_Up:
2237 
2238  if(iShift || iAlt)
2239  {
2240  return 0;
2241  }
2242 
2243  if(!iCtrl)
2244  {
2245  iBar = WB_SCROLL_VERTICAL;
2246  }
2247  else
2248  {
2249  iBar = WB_SCROLL_HORIZONTAL;
2250  }
2251 
2252  iDirection = WB_SCROLL_PAGEBACK;
2253  break;
2254 
2255  case XK_Page_Down:
2256 
2257  if(iShift || iAlt)
2258  {
2259  return 0;
2260  }
2261 
2262  if(!iCtrl)
2263  {
2264  iBar = WB_SCROLL_VERTICAL;
2265  }
2266  else
2267  {
2268  iBar = WB_SCROLL_HORIZONTAL;
2269  }
2270 
2271  iDirection = WB_SCROLL_PAGEFWD;
2272  break;
2273 
2274  default:
2275  return 0;
2276  }
2277 
2278  // see if the bar is actually visible first
2279  if(iBar == WB_SCROLL_HORIZONTAL)
2280  {
2281  if(pScrollInfo->geomHBar.width <= 0 || pScrollInfo->geomHBar.height <= 0)
2282  {
2283  return 0; // no vertical scroll bar
2284  }
2285  }
2286  else // vertical
2287  {
2288  if(pScrollInfo->geomVBar.width <= 0 || pScrollInfo->geomVBar.height <= 0)
2289  {
2290  return 0; // no vertical scroll bar
2291  }
2292  }
2293 
2294  DLGNotifySelf(pCtrl, aSCROLL_NOTIFY, iBar, iDirection, 0, 0, 0);
2295  return 1; // keystroke eaten
2296  }
2297  }
2298  }
2299 
2300  return 0; // NOT handled
2301 }
2302 
2303 
2304 #ifndef NO_DEBUG
2305 void DEBUG_DUMP_LIST(WBDialogControl *pCtrl)
2306 {
2307  LISTINFO *pListInfo;
2308  WB_DIALOG_PROP *pProp;
2309  int i1;
2310  const char *p1;
2311 
2312 
2315  {
2316  return;
2317  }
2318 
2320 
2321  if(!pProp || !pProp->pVal)
2322  {
2323  if(!(pCtrl->ulFlags & CONTROL_SupportListInfo))
2324  {
2325  WBDebugPrint("%s - control does not support 'LISTINFO'\n", __FUNCTION__);
2326  }
2327  else
2328  {
2329  WBDebugPrint("%s - 'LISTINFO' is empty\n", __FUNCTION__);
2330  }
2331 
2332  return;
2333  }
2334 
2335  pListInfo = (LISTINFO *)pProp->pVal;
2336 
2337  if(pListInfo->nFlags & ListInfoFlags_SORTED) // regardless of index, when sorted, do this
2338  {
2339  WBDebugPrint("%s - 'LISTINFO' is sorted\n", __FUNCTION__);
2340  }
2341 
2342  WBDebugPrint("%s - 'LISTINFO' %d items\n", __FUNCTION__, pListInfo->nItems);
2343 
2344  for(i1=0; i1 < pListInfo->nItems; i1++)
2345  {
2346  // for now assume string data
2347  WBDebugPrint(" %6d: %p %s\n", i1, pListInfo->aItems[i1], (char *)(pListInfo->aItems[i1]));
2348  }
2349 }
2350 #endif // NO_DEBUG
2351 
2352