1 /***********************************************************************/
2 /* Open Visualization Data Explorer                                    */
3 /* (C) Copyright IBM Corp. 1989,1999                                   */
4 /* ALL RIGHTS RESERVED                                                 */
5 /* This code licensed under the                                        */
6 /*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
7 /***********************************************************************/
8 
9 #include <dxconfig.h>
10 #include "../base/defines.h"
11 
12 
13 
14 
15 #include <Xm/Xm.h>
16 #include <Xm/RowColumn.h>
17 #include <Xm/Form.h>
18 #include <Xm/PushB.h>
19 #include <Xm/CascadeB.h>
20 #include <X11/IntrinsicP.h>
21 #include "XmUtility.h"
22 
23 #include "SelectorInteractor.h"
24 #include "InteractorStyle.h"
25 #include "ListIterator.h"
26 
27 #include "SelectorNode.h"
28 
29 #include "SelectorInstance.h"
30 #include "DXApplication.h"
31 #include "ErrorDialogManager.h"
32 #include "../widgets/XmDX.h"
33 
34 boolean SelectorInteractor::SelectorInteractorClassInitialized = FALSE;
35 
36 String SelectorInteractor::DefaultResources[] =  {
37     "*allowHorizontalResizing:		True",
38     "*optionMenu.spacing:		0",
39     "*optionMenu.topOffset:		4",
40     "*optionMenu.leftOffset:		1",
41     "*optionMenu.rightOffset:		1",
42     "*optionMenu.bottomOffset:		4",
43     "*optionMenu.marginHeight:		1",
44     "*optionMenu.marginWidth:		0",
45     "*interactive_form.resizePolicy:	XmRESIZE_NONE",
46     NUL(char*)
47 };
48 
49 
SelectorInteractor(const char * name,InteractorInstance * ii)50 SelectorInteractor::SelectorInteractor(const char *name,
51 		 InteractorInstance *ii) : Interactor(name,ii)
52 {
53     this->pulldown = NULL;
54     this->optionMenu = NULL;
55     this->optionForm = NULL;
56     this->visinit = FALSE;
57 }
58 
59 //
60 // One time class initializations.
61 //
initialize()62 void SelectorInteractor::initialize()
63 {
64     //
65     // Initialize default resources (once only).
66     //
67     if (NOT SelectorInteractor::SelectorInteractorClassInitialized)
68     {
69         ASSERT(theApplication);
70         this->setDefaultResources(theApplication->getRootWidget(),
71                                   SelectorInteractor::DefaultResources);
72         this->setDefaultResources(theApplication->getRootWidget(),
73                                   Interactor::DefaultResources);
74         SelectorInteractor::SelectorInteractorClassInitialized = TRUE;
75     }
76 }
77 
78 
79 //
80 // Allocate an n-dimensional stepper for the given instance.
81 //
AllocateInteractor(const char * name,InteractorInstance * ii)82 Interactor *SelectorInteractor::AllocateInteractor(const char *name,
83 						InteractorInstance *ii)
84 {
85 
86     SelectorInteractor *si = new SelectorInteractor(name,ii);
87     return (Interactor*)si;
88 }
89 
90 //
91 // Accepts value changes and reflects them into other interactors, cdbs
92 // and off course the interactor node output.
93 //
SelectorInteractor_OptionMenuCB(Widget widget,XtPointer clientData,XtPointer callData)94 extern "C" void SelectorInteractor_OptionMenuCB(Widget                  widget,
95                XtPointer                clientData,
96                XtPointer                callData)
97 {
98     SelectorInteractor *si = (SelectorInteractor*)clientData;
99 
100     ASSERT(widget);
101     ASSERT(si);
102 
103 
104     int optnum = (int)(long)GetUserData(widget);
105     si->optionMenuCallback(widget, optnum, callData);
106 }
107 
108 //
109 // Perform anything that needs to be done after the parent of
110 // this->interactivePart has been managed.
111 //
completeInteractivePart()112 void SelectorInteractor::completeInteractivePart()
113 {
114 }
115 
116 //
117 // Build the selector interactor option menu.
118 // Note that we also use this to rebuild the list of options seen in the menu
119 // In this case the
120 //
createInteractivePart(Widget form)121 Widget SelectorInteractor::createInteractivePart(Widget form)
122 {
123     SelectorNode	*node;
124     SelectorInstance	*si = (SelectorInstance*)this->interactorInstance;
125 
126     ASSERT(si);
127 
128     node = (SelectorNode*)si->getNode();
129 
130     ASSERT(si->getStyle()->getStyleEnum() == SelectorOptionMenuStyle);
131     ASSERT(form);
132     ASSERT(node);
133     ASSERT(EqualString(node->getClassName(), ClassSelectorNode));
134 
135     this->optionForm = form;
136 
137     //
138     // Build the option menu
139     //
140     this->reloadMenuOptions();
141 
142     XtManageChild(this->optionForm);
143     return this->optionForm;
144 }
145 
146 
147 //
148 // [Re]load the options into this->pulldown.
149 //
reloadMenuOptions()150 void SelectorInteractor::reloadMenuOptions()
151 {
152     SelectorInstance *si = (SelectorInstance*) this->interactorInstance;
153     Widget sbutton;
154     int options, n, i;
155     Arg wargs[20];
156     Pixel bg, fg;
157     unsigned char rsp;
158 
159     XtVaGetValues (this->optionForm, XmNresizePolicy, &rsp, NULL);
160     XtVaSetValues (this->optionForm, XmNresizePolicy, XmRESIZE_GROW, NULL);
161 
162     ASSERT(this->optionForm);
163 
164     if (this->optionMenu)  {
165 	XtUnmanageChild (this->optionMenu);
166 	XtDestroyWidget (this->optionMenu);
167 	this->optionMenu = NULL;
168 	XtDestroyWidget (this->pulldown);
169     }
170 
171     /*
172      * The pulldown already exists so remove all its children,
173      * which are assumed to be contained in this->optionWidgets.
174      */
175     this->optionWidgets.clear();
176 
177 
178     //
179     // colors change with panel style.  It's better to start out with
180     // the proper colors than to switch to ther proper colors.
181     //
182     XtVaGetValues (this->optionForm, XmNforeground, &fg, XmNbackground, &bg, NULL);
183     n = 0;
184     XtSetArg (wargs[n], XmNbackground, bg); n++;
185     XtSetArg (wargs[n], XmNforeground, fg); n++;
186     this->pulldown = XmCreatePulldownMenu(this->optionForm, "pulldownMenu", wargs, n);
187 
188     /*
189      * Create the options in the pulldown menu according to specified
190      * option list.
191      */
192 
193     Dimension strw, strh;
194     int maxw = 0;
195     XmFontList xmfl = 0;
196     options = si->getOptionCount();
197     XmString xmstr;
198     if (options > 0) {
199 	int selectedOption = si->getSelectedOptionIndex();
200 	ASSERT(selectedOption <= options);
201 	sbutton = NULL;
202 	for (i = 1; i <= options; i++)
203 	{
204 	    char *optname = (char*)si->getOptionNameString(i);
205 	    ASSERT(optname);
206 	    Widget button;
207 	    xmstr = XmStringCreateLtoR(optname, "canvas");
208 	    n = 0;
209 	    XtSetArg(wargs[n], XmNuserData, i); n++;
210 	    XtSetArg(wargs[n], XmNlabelString, xmstr ); n++;
211 	    XtSetArg (wargs[n], XmNbackground, bg); n++;
212 	    XtSetArg (wargs[n], XmNforeground, fg); n++;
213 	    button = XmCreatePushButton(this->pulldown, optname, wargs, n);
214 
215 	    if (!xmfl)
216 		XtVaGetValues (button,
217 		    XmNfontList, &xmfl,
218 		NULL);
219 
220 	    XmStringExtent (xmfl, xmstr, &strw, &strh);
221 	    maxw = MAX(maxw, strw);
222 
223 	    XtAddCallback
224 		(button,
225 		 XmNactivateCallback,
226 		 (XtCallbackProc)SelectorInteractor_OptionMenuCB,
227 		 (XtPointer)this);
228 	    XtManageChild(button);
229 	    if (i == selectedOption) {
230 		sbutton = button;
231 	    }
232 	    this->appendOptionWidget(button);
233 	    delete optname;
234 	    XmStringFree(xmstr);
235 	}
236         ASSERT(sbutton);
237 	// Certainly not a solution to the problem, but using multi columns
238 	// makes marginally better those situations in which a huge number of
239 	// options makes the list go off the screen.
240 	// FIXME: MAX_OPTIONS must be determined at run time by comparing the
241 	// height of the XmStrings with the height of the display.  Besides
242 	// the number 40 is almost certainly wrong.
243 #define MAX_OPTIONS 40
244 	int columns = (options/MAX_OPTIONS) + ((options%MAX_OPTIONS)?1:0);
245 	if (columns > 1) {
246 	    XtVaSetValues (this->pulldown,
247 		XmNpacking, XmPACK_COLUMN,
248 		XmNnumColumns, columns,
249 	    NULL);
250 	}
251     } else {
252 	n = 0;
253 	xmstr = XmStringCreateLtoR("(empty)", "canvas");
254 	XtSetArg(wargs[n], XmNlabelString, xmstr); n++;
255 	sbutton = XmCreatePushButton(this->pulldown, "(empty)", wargs, n);
256 	XtManageChild(sbutton);
257 	if (!xmfl) XtVaGetValues (sbutton, XmNfontList, &xmfl, NULL);
258 	XmStringExtent (xmfl, xmstr, &strw, &strh);
259 	maxw = MAX(maxw, strw);
260 	this->appendOptionWidget(sbutton);
261 	XmStringFree(xmstr);
262     }
263 
264     /*
265      * Create the option menu itself or just reset the current selection
266      * if the menu already exists.
267      */
268     n = 0;
269     XtSetArg(wargs[n], XmNmenuHistory, sbutton);		n++;
270     if (!this->optionMenu) {
271 	XtSetArg(wargs[n], XmNsubMenuId,   this->pulldown);         n++;
272 	XtSetArg(wargs[n], XmNtopAttachment, XmATTACH_FORM);     n++;
273 	XtSetArg(wargs[n], XmNleftOffset, -(18 + (maxw>>1))); n++;
274 	XtSetArg(wargs[n], XmNrightAttachment, XmATTACH_FORM);      n++;
275 	XtSetArg(wargs[n], XmNbottomAttachment, XmATTACH_FORM);     n++;
276 	if (this->currentLayout & WorkSpaceComponent::Vertical) {
277 	    XtSetArg (wargs[n], XmNleftAttachment, XmATTACH_POSITION); n++;
278 	    XtSetArg (wargs[n], XmNleftPosition, 50); n++;
279 	} else {
280 	    XtSetArg (wargs[n], XmNleftAttachment, XmATTACH_NONE); n++;
281 	}
282 	XtSetArg(wargs[n], XmNbackground, bg); n++;
283 	XtSetArg(wargs[n], XmNforeground, fg); n++;
284 	this->optionMenu = XmCreateOptionMenu(this->optionForm,
285 						"optionMenu", wargs, n);
286 	Widget og = XmOptionButtonGadget(this->optionMenu);
287 	XtVaSetValues(og,
288 		XmNbackground, bg,
289 		XmNforeground, fg,
290 		NULL);
291 	//
292 	// Every motif option menu comes with its own label widget which we
293 	// never use.
294 	// For the sake of dgux, set the labelString to "", because it defaults
295 	// to "OptionLabel" unlike everywhere else.
296 	//
297 	xmstr = XmStringCreateLtoR ("", "canvas");
298 	Widget labelgadget = XmOptionLabelGadget (this->optionMenu);
299 	XtVaSetValues (labelgadget,
300 	    XmNlabelString, xmstr,
301 	    XmNbackground, bg,
302 	    XmNforeground, fg,
303 	NULL);
304 	XmStringFree (xmstr);
305 	XtManageChild(this->optionMenu);
306     }
307     if (options <= 0)
308 	XtSetSensitive(this->optionMenu,False);
309     else if (!XtIsSensitive (this->optionMenu))
310 	XtSetSensitive(this->optionMenu,True);
311 
312     XtVaSetValues (this->optionForm, XmNresizePolicy, rsp, NULL);
313 }
314 
315 //
316 // Accepts value changes and reflects them into other interactors, cdbs
317 // and off course the interactor node output.
318 //
optionMenuCallback(Widget w,int optnum,XtPointer cb)319 void SelectorInteractor::optionMenuCallback(Widget w, int optnum, XtPointer cb)
320 {
321     SelectorInstance *si = (SelectorInstance*)this->interactorInstance;
322 
323     ASSERT(w);
324     ASSERT(optnum > 0);
325 
326     si->setSelectedOptionIndex(optnum, TRUE);
327 }
328 
329 //
330 // Update the displayed values for this interactor.
331 //
updateDisplayedInteractorValue()332 void SelectorInteractor::updateDisplayedInteractorValue()
333 {
334     // FIXME: should check to make sure we have the correct class of node.
335     SelectorInstance *si = (SelectorInstance*)this->interactorInstance;
336     ASSERT(si);
337 
338     //
339     // There are no options, so there is nothing to update.
340     //
341     if (si->getOptionCount() <= 0)
342 	return;
343 
344     /*
345      * Just display the currently selected option.
346      */
347     int option = si->getSelectedOptionIndex();
348     if (option == 0)
349 	return;
350 
351     Widget w = this->getOptionWidget(option);
352     ASSERT(w);
353     XtVaSetValues(this->optionMenu, XmNmenuHistory, w, NULL);
354 }
355 
356 //
357 // Make sure the attributes match the resources for the widgets.
358 //
handleInteractivePartStateChange(InteractorInstance * src_ii,boolean major_change)359 void SelectorInteractor::handleInteractivePartStateChange(
360 					InteractorInstance *src_ii,
361 					boolean major_change)
362 {
363     this->reloadMenuOptions();
364     if (this->currentLayout & WorkSpaceComponent::Horizontal) {
365 	this->currentLayout|= WorkSpaceComponent::NotSet;
366 	this->layoutInteractor();
367     }
368 }
369 
layoutInteractorHorizontally()370 void SelectorInteractor::layoutInteractorHorizontally()
371 {
372     this->Interactor::layoutInteractorHorizontally();
373 
374     if (!this->optionMenu) return ;
375     XtVaSetValues (this->optionMenu,
376 	XmNleftAttachment, XmATTACH_NONE,
377     NULL);
378 }
379 
380 //
381 // The setting and resetting of marginHeight and topAttachment deals with the
382 // absence of a topAttachment.  XmNtopAttachment is NONE because it permits
383 // the optionmenu to wind up roughly in the middle of its parent even if a change
384 // in the implementation of the interactor causes us to crave a different height
385 // than is saved in the .cfg file. The button widget musn't change height but
386 // the rowcolumn can.
387 //
layoutInteractorVertically()388 void SelectorInteractor::layoutInteractorVertically()
389 {
390     this->Interactor::layoutInteractorVertically();
391 
392     if (!this->optionMenu) return ;
393     XtVaSetValues (this->optionMenu,
394 	XmNleftAttachment, XmATTACH_POSITION,
395 	XmNleftPosition, 50,
396     NULL);
397 }
398 
399 
setAppearance(boolean developer_style)400 void SelectorInteractor::setAppearance(boolean developer_style)
401 {
402     boolean changing = (developer_style != this->getAppearance());
403     this->Interactor::setAppearance(developer_style);
404     if ((changing) || (this->visinit == FALSE)) {
405 	this->reloadMenuOptions();
406 	this->visinit = TRUE;
407     }
408 }
409