1 | #include <X11/Xmp/COPY.h> |
---|
2 | /* |
---|
3 | * SCCS_data: %Z% %M% %I% %E% %U% |
---|
4 | * |
---|
5 | * Xmp - Motif Public Utilities for Wcl - Xmp.c |
---|
6 | * |
---|
7 | * This file contains Motif specific converters, actions, and convenience |
---|
8 | * functions for Motif and Xmp widgets. |
---|
9 | * |
---|
10 | ******************************************************************************* |
---|
11 | */ |
---|
12 | |
---|
13 | #include <X11/IntrinsicP.h> |
---|
14 | |
---|
15 | #ifdef sun |
---|
16 | #include <X11/ObjectP.h> /* why don't they just use X from mit!?! */ |
---|
17 | #include <X11/RectObjP.h> |
---|
18 | #endif |
---|
19 | |
---|
20 | #include <X11/StringDefs.h> |
---|
21 | #include <Xm/Xm.h> |
---|
22 | #include <Xm/RowColumnP.h> /* RC_Type */ |
---|
23 | #include <Xm/MenuShell.h> /* xmMenuShellWidgetClass */ |
---|
24 | #include <Xm/Text.h> /* xmTextWidgetClass */ |
---|
25 | #include <X11/Shell.h> /* wmShellWidgetClass */ |
---|
26 | #include <X11/Wc/WcCreateP.h> |
---|
27 | |
---|
28 | #include <X11/Xmp/Table.h> |
---|
29 | #include <X11/Xmp/XmpP.h> |
---|
30 | |
---|
31 | /****************************************************************************** |
---|
32 | * Private_macro_definitions. |
---|
33 | ******************************************************************************/ |
---|
34 | |
---|
35 | /* The `done' macro, used by the converters, is defined in WcCreateP.h */ |
---|
36 | |
---|
37 | /****************************************************************************** |
---|
38 | ** Motif specific resource converters |
---|
39 | ******************************************************************************/ |
---|
40 | |
---|
41 | /* |
---|
42 | -- Convert String To XmString |
---|
43 | ******************************************************************************* |
---|
44 | This conversion converts strings into XmStrings. This one overrides |
---|
45 | the one provided by Motif which calls XmCreateString. Instead, this |
---|
46 | one uses XmCreateStringLtoR which allows multi-line strings to be |
---|
47 | created by embedding "\n" in a resource specification. In order to |
---|
48 | allow this to be used, XmpRegAll MUST install this converter AFTER |
---|
49 | Motif has installed its broken converter. |
---|
50 | */ |
---|
51 | /*ARGSUSED*/ |
---|
52 | void XmpCvtStringToXmString (args, num_args, fromVal, toVal) |
---|
53 | XrmValue *args; |
---|
54 | Cardinal *num_args; |
---|
55 | XrmValue *fromVal; |
---|
56 | XrmValue *toVal; |
---|
57 | { |
---|
58 | XmString xmString; |
---|
59 | xmString = XmStringCreateLtoR( fromVal->addr, XmSTRING_DEFAULT_CHARSET ); |
---|
60 | done( XmString, xmString ); |
---|
61 | } |
---|
62 | |
---|
63 | /* -- Register all converters provided by Motif |
---|
64 | ******************************************************************************* |
---|
65 | */ |
---|
66 | |
---|
67 | void XmpAddMotifConverters ( app ) |
---|
68 | XtAppContext app; |
---|
69 | { |
---|
70 | ONCE_PER_XtAppContext( app ); |
---|
71 | |
---|
72 | XmRegisterConverters(); |
---|
73 | #ifdef XmRTearOffModel |
---|
74 | XmRepTypeInstallTearOffModelConverter(); |
---|
75 | #endif |
---|
76 | #ifndef VMS |
---|
77 | _XmRegisterPixmapConverters(); |
---|
78 | #endif |
---|
79 | XtAppAddConverter( app, XmRString, XmRUnitType, |
---|
80 | XmCvtStringToUnitType, |
---|
81 | (XtConvertArgList)0, (Cardinal)0 ); |
---|
82 | } |
---|
83 | |
---|
84 | /* -- Register all converters provided by Xmp |
---|
85 | ******************************************************************************* |
---|
86 | */ |
---|
87 | |
---|
88 | void XmpAddConverters ( app ) |
---|
89 | XtAppContext app; |
---|
90 | { |
---|
91 | ONCE_PER_XtAppContext( app ); |
---|
92 | |
---|
93 | /* Some, not all, old versions of Motif incorrectly say some resources |
---|
94 | ** which should be of type "Widget" are of type "Window" |
---|
95 | */ |
---|
96 | #ifdef XtSpecificationRelease |
---|
97 | /* Must use new style converter if available to avoid cache, |
---|
98 | * because the cache is not cleared when named widgets get destroyed! |
---|
99 | */ |
---|
100 | XtAppSetTypeConverter( app, XtRString, "Window", |
---|
101 | WcCvtStringToWidget, |
---|
102 | wcWidgetCvtArgs, wcWidgetCvtArgsCount, |
---|
103 | XtCacheNone, (XtDestructor)0 ); |
---|
104 | |
---|
105 | XtAppSetTypeConverter( app, XtRString, XmRMenuWidget, |
---|
106 | WcCvtStringToWidget, |
---|
107 | wcWidgetCvtArgs, wcWidgetCvtArgsCount, |
---|
108 | XtCacheNone, (XtDestructor)0 ); |
---|
109 | #else |
---|
110 | /* R3 does not cache conversions, so there is nothing to worry about. |
---|
111 | */ |
---|
112 | XtAppAddConverter( app, XtRString, "Window", |
---|
113 | WcCvtStringToWidget, |
---|
114 | wcWidgetCvtArgs, wcWidgetCvtArgsCount ); |
---|
115 | |
---|
116 | XtAppAddConverter( app, XtRString, XmRMenuWidget, |
---|
117 | WcCvtStringToWidget, |
---|
118 | wcWidgetCvtArgs, wcWidgetCvtArgsCount ); |
---|
119 | |
---|
120 | #endif |
---|
121 | |
---|
122 | XtAppAddConverter( app, XtRString, XmRXmString, |
---|
123 | XmpCvtStringToXmString, |
---|
124 | (XtConvertArgList)0, (Cardinal)0 ); |
---|
125 | } |
---|
126 | |
---|
127 | |
---|
128 | /* -- Action to fix broken translations in XmText widgets. |
---|
129 | ******************************************************************************* |
---|
130 | */ |
---|
131 | /*ARGSUSED*/ |
---|
132 | void XmpFixTranslationsACT( w, unusedEvent, params, num_params ) |
---|
133 | Widget w; |
---|
134 | XEvent *unusedEvent; |
---|
135 | String *params; |
---|
136 | Cardinal *num_params; |
---|
137 | { |
---|
138 | static int firstTime = 1; |
---|
139 | static XtTranslations transTable; |
---|
140 | static char fixedTranslations[] = "<Key>Return: newline()"; |
---|
141 | |
---|
142 | int i; |
---|
143 | |
---|
144 | if (firstTime) |
---|
145 | { |
---|
146 | firstTime = 0; |
---|
147 | transTable = XtParseTranslationTable(fixedTranslations); |
---|
148 | } |
---|
149 | |
---|
150 | if ( *num_params == 0 ) |
---|
151 | { |
---|
152 | WcWARN( w, "XmpFixTranslations", "usage", |
---|
153 | "Xmp Usage: XmpFixTranslations( widget [widgetName] ...)"); |
---|
154 | return; |
---|
155 | } |
---|
156 | |
---|
157 | for ( i = 0 ; i < *num_params ; i++ ) |
---|
158 | { |
---|
159 | Widget toFix = WcFullNameToWidget( w, params[i] ); |
---|
160 | |
---|
161 | if (!toFix) |
---|
162 | { |
---|
163 | WcWARN1( w, "XmpFixTranslations", "notFound", |
---|
164 | "Xmp Warning: XmpFixTranslations(%s) - Widget not found", |
---|
165 | params[i] ); |
---|
166 | return; |
---|
167 | } |
---|
168 | |
---|
169 | if (XtIsSubclass( toFix, xmTextWidgetClass )) |
---|
170 | XtOverrideTranslations( toFix, transTable ); |
---|
171 | else |
---|
172 | { |
---|
173 | WcWARN1( w, "XmpFixTranslations", "notText", |
---|
174 | "Xmp Warning: XmpFixTranslations(%s) - Not an XmText widget", |
---|
175 | params[i] ); |
---|
176 | return; |
---|
177 | } |
---|
178 | } |
---|
179 | } |
---|
180 | |
---|
181 | /*ARGSUSED*/ |
---|
182 | void XmpFixTranslationsCB( w, widgetNames, ignore ) |
---|
183 | Widget w; |
---|
184 | XtPointer widgetNames; |
---|
185 | XtPointer ignore; |
---|
186 | { |
---|
187 | WcInvokeAction( XmpFixTranslationsACT, w, widgetNames ); |
---|
188 | } |
---|
189 | |
---|
190 | |
---|
191 | /* -- Action to popup a Motif PopupMenu |
---|
192 | ******************************************************************************* |
---|
193 | Gotta do debugging with fprintf() because there is a server grab |
---|
194 | in effect, in all probability. |
---|
195 | */ |
---|
196 | |
---|
197 | void XmpPopupACT( w, event, params, num_params ) |
---|
198 | Widget w; |
---|
199 | XEvent *event; |
---|
200 | String *params; |
---|
201 | Cardinal *num_params; |
---|
202 | { |
---|
203 | Widget menuPane, menu; |
---|
204 | unsigned char rowColumnType; |
---|
205 | |
---|
206 | if (*num_params < 1) |
---|
207 | { |
---|
208 | WcWARN( w, "XmpPopup", "noParams", |
---|
209 | "Xmp Warning: XmpPopup() - No menu pane name provided." ); |
---|
210 | return; |
---|
211 | } |
---|
212 | |
---|
213 | if ( *num_params > 1 ) |
---|
214 | { |
---|
215 | WcWARN2( w, "XmpPopup", "tooManyParams", |
---|
216 | "Xmp Warning: XmpPopup( %s %s ... ) - Only one menu pane name allowed." , |
---|
217 | params[0], params[1] ); |
---|
218 | return; |
---|
219 | } |
---|
220 | |
---|
221 | menuPane = WcFullNameToWidget( w, params[0] ); |
---|
222 | |
---|
223 | if ( menuPane == (Widget)0 ) |
---|
224 | { |
---|
225 | String fullName = WcWidgetToFullName( w ); |
---|
226 | WcWARN3( w, "XmpPopup", "notFound", |
---|
227 | "Xmp Warning: XmpPopup( %s ) - Couldn't find menu pane widget `%s' from `%s'", |
---|
228 | params[0], params[0], fullName ); |
---|
229 | XtFree( fullName ); |
---|
230 | return; |
---|
231 | } |
---|
232 | |
---|
233 | if ( !XtIsSubclass( menuPane, xmRowColumnWidgetClass ) ) |
---|
234 | { |
---|
235 | String fullName = WcWidgetToFullName( menuPane ); |
---|
236 | WcWARN2( w, "XmpPopup", "notXmRowColumn", |
---|
237 | "Xmp Warning: XmpPopup( %s ) - %s is not menu pane (not XmRowColumn)", |
---|
238 | params[0], fullName ); |
---|
239 | XtFree( fullName ); |
---|
240 | return; |
---|
241 | } |
---|
242 | |
---|
243 | XtVaGetValues( menuPane, XmNrowColumnType, &rowColumnType, NULL ); |
---|
244 | |
---|
245 | if ( rowColumnType != XmMENU_POPUP ) |
---|
246 | { |
---|
247 | String fullName = WcWidgetToFullName( menuPane ); |
---|
248 | WcWARN2( w, "XmpPopup", "notPopupMenuRowColumn", |
---|
249 | "Xmp Warning: XmpPopup( %s ) - %s is not menu pane (not XmMENU_POPUP)", |
---|
250 | params[0], fullName ); |
---|
251 | XtFree( fullName ); |
---|
252 | return; |
---|
253 | } |
---|
254 | |
---|
255 | menu = XtParent( menuPane ); |
---|
256 | |
---|
257 | if ( !XtIsSubclass( menu, xmMenuShellWidgetClass ) ) |
---|
258 | { |
---|
259 | String fullName = WcWidgetToFullName( menuPane ); |
---|
260 | WcWARN2( w, "XmpPopup", "notChildOfXmMenu", |
---|
261 | "Xmp Warning: XmpPopup( %s ) - %s is not menu pane (child of XmMenuShell)", |
---|
262 | params[0], fullName ); |
---|
263 | XtFree( fullName ); |
---|
264 | return; |
---|
265 | } |
---|
266 | |
---|
267 | if ( event->type != ButtonPress ) |
---|
268 | { |
---|
269 | WcWARN2( w, "XmpPopup", "notButtonPressEvent", |
---|
270 | "Xmp Warning: XmpPopup( %s ) - event type `%s' is not ButtonPress event.", |
---|
271 | params[0], WcXEventName( event ) ); |
---|
272 | return; |
---|
273 | } |
---|
274 | |
---|
275 | /* OK! we have 1 named widget which resolves to a XmRowColumn of |
---|
276 | * type XmMENU_POPUP which is a child of a XmMenuShell, and a |
---|
277 | * ButtonPress event invoked this action. Therefore, lets go ahead. |
---|
278 | */ |
---|
279 | XmMenuPosition( menuPane, (XButtonPressedEvent*)event ); |
---|
280 | XtManageChild( menuPane ); |
---|
281 | } |
---|
282 | |
---|
283 | |
---|
284 | /* |
---|
285 | -- Provide `Close' callback |
---|
286 | ******************************************************************************* |
---|
287 | Provide a callback which gets invoked when the user select `Close' from |
---|
288 | the Mwm frame menu on the top level shell. MUST be done after shell widget |
---|
289 | is realized! |
---|
290 | */ |
---|
291 | |
---|
292 | void XmpAddMwmCloseCallback( shell, callback, clientData ) |
---|
293 | Widget shell; |
---|
294 | XtCallbackProc callback; |
---|
295 | XtPointer clientData; |
---|
296 | { |
---|
297 | Atom wmProtocol, wmDeleteWindow, wmSaveYourself; |
---|
298 | wmProtocol =XmInternAtom( XtDisplay(shell), "WM_PROTOCOLS", False ); |
---|
299 | |
---|
300 | wmDeleteWindow=XmInternAtom( XtDisplay(shell), "WM_DELETE_WINDOW", False ); |
---|
301 | wmSaveYourself=XmInternAtom( XtDisplay(shell), "WM_SAVE_YOURSELF", False ); |
---|
302 | XmAddProtocolCallback( shell, wmProtocol, wmDeleteWindow, |
---|
303 | callback, clientData ); |
---|
304 | XmAddProtocolCallback( shell, wmProtocol, wmSaveYourself, |
---|
305 | callback, clientData ); |
---|
306 | } |
---|
307 | |
---|
308 | /* |
---|
309 | -- Action and callback to invoke XmpAddMwmCloseCallback |
---|
310 | ******************************************************************************* |
---|
311 | */ |
---|
312 | /*ARGSUSED*/ |
---|
313 | void XmpAddMwmCloseCallbackCB( w, widget_callbackList, notUsed ) |
---|
314 | Widget w; |
---|
315 | XtPointer widget_callbackList; |
---|
316 | XtPointer notUsed; |
---|
317 | { |
---|
318 | char widgetName[MAX_XRMSTRING]; |
---|
319 | String callbackListString; |
---|
320 | Widget target; |
---|
321 | XtCallbackRec* callbackList; |
---|
322 | XtCallbackRec* cb; |
---|
323 | |
---|
324 | callbackListString = WcCleanName( (char*)widget_callbackList, widgetName ); |
---|
325 | callbackListString = WcSkipWhitespace_Comma( callbackListString ); |
---|
326 | |
---|
327 | target = WcFullNameToWidget( w, widgetName ); |
---|
328 | |
---|
329 | if ( (Widget)0 == (target = WcFullNameToWidget( w, widgetName )) ) |
---|
330 | { |
---|
331 | WcWARN1( w, "XmpAddMwmCloseCallback", "targetNotFound", |
---|
332 | "Xmp Warning: XmpAddMwmCloseCallback( %s ... ) - Target widget not found", |
---|
333 | widgetName ); |
---|
334 | return; |
---|
335 | } |
---|
336 | |
---|
337 | callbackList = WcStringToCallbackList( w, callbackListString ); |
---|
338 | |
---|
339 | for ( cb = callbackList ; |
---|
340 | cb != (XtCallbackRec*)0 && cb->callback != (XtCallbackProc)0 ; |
---|
341 | cb++ ) |
---|
342 | XmpAddMwmCloseCallback( target, cb->callback, cb->closure ); |
---|
343 | |
---|
344 | WcFreeCallbackList( callbackList ); |
---|
345 | } |
---|
346 | |
---|
347 | /*ARGSUSED*/ |
---|
348 | void XmpAddMwmCloseCallbackACT( w, unusedEvent, params, num_params ) |
---|
349 | Widget w; |
---|
350 | XEvent *unusedEvent; |
---|
351 | String *params; |
---|
352 | Cardinal *num_params; |
---|
353 | { |
---|
354 | WcInvokeCallback( XmpAddMwmCloseCallbackCB, w, params, num_params ); |
---|
355 | } |
---|
356 | |
---|
357 | /* -- Define a Widget as a Tab Group |
---|
358 | ******************************************************************************* |
---|
359 | If no widget is named, then use the widget argument. |
---|
360 | If the widget is a manager widget, then this means all the children |
---|
361 | are in a tab group together. If a widget is a primitive, then that |
---|
362 | widget is in its own tab group (this is the default). |
---|
363 | */ |
---|
364 | /*ARGSUSED*/ |
---|
365 | void XmpAddTabGroupACT( w, unusedEvent, params, num_params ) |
---|
366 | Widget w; |
---|
367 | XEvent *unusedEvent; |
---|
368 | String *params; |
---|
369 | Cardinal *num_params; |
---|
370 | { |
---|
371 | if (*num_params == 0) |
---|
372 | { |
---|
373 | XmAddTabGroup(w); |
---|
374 | } |
---|
375 | else if (*num_params == 1) |
---|
376 | { |
---|
377 | Widget target = WcFullNameToWidget( w, params[0] ); |
---|
378 | |
---|
379 | if ((Widget)0 != target) |
---|
380 | { |
---|
381 | XmAddTabGroup(w); |
---|
382 | } |
---|
383 | else |
---|
384 | { |
---|
385 | WcWARN1( w, "XmpAddTabGroup", "notFound", |
---|
386 | "Xmp Warning: XmpAddTabGroup(%s) - Widget not found", |
---|
387 | params[0] ); |
---|
388 | } |
---|
389 | |
---|
390 | } |
---|
391 | else |
---|
392 | { |
---|
393 | WcWARN( w, "XmpAddTabGroup", "usage", |
---|
394 | "Xmp Warning: XmpAddTabGroup() requires 0 or 1 widget name argument."); |
---|
395 | } |
---|
396 | } |
---|
397 | |
---|
398 | /*ARGSUSED*/ |
---|
399 | void XmpAddTabGroupCB( w, widgetName, notUsed ) |
---|
400 | Widget w; |
---|
401 | XtPointer widgetName; |
---|
402 | XtPointer notUsed; |
---|
403 | { |
---|
404 | WcInvokeAction( XmpAddTabGroupACT, w, widgetName ); |
---|
405 | } |
---|
406 | |
---|
407 | /* -- Specify a text widget to take Wcl messages |
---|
408 | ******************************************************************************* |
---|
409 | */ |
---|
410 | |
---|
411 | static void XmpxPrint _(( String, ... )); |
---|
412 | static void XmpxMessageWidgetDestroyCB _(( Widget, XtPointer, XtPointer )); |
---|
413 | static int XmpxLinesIn _(( String )); |
---|
414 | static int XmpxEntireMessageLen _(( String, va_list )); |
---|
415 | static String XmpxMakeEntireMessage _(( String, va_list )); |
---|
416 | |
---|
417 | static Widget xmpMessageWidget; /* an XmText widget */ |
---|
418 | |
---|
419 | static WcPrintProc XmpxDefaultWcPrintProc; |
---|
420 | |
---|
421 | typedef struct _MessageOptions { |
---|
422 | int maxLines; |
---|
423 | int columns; |
---|
424 | String linesLostMsg; |
---|
425 | int linesInLinesLostMsg; |
---|
426 | Boolean popup, raise, brokenXmTextShowPosition; |
---|
427 | } MessageOptionsRec, *MessageOptions; |
---|
428 | |
---|
429 | static MessageOptionsRec xmpMessageOptionsRec; |
---|
430 | static MessageOptions xmpMessageOptions = &xmpMessageOptionsRec; |
---|
431 | |
---|
432 | #ifdef XtOffsetOf |
---|
433 | #define FLD(n) XtOffsetOf(MessageOptionsRec,n) |
---|
434 | #else |
---|
435 | #define FLD(n) XtOffset(MessageOptions,n) |
---|
436 | #endif |
---|
437 | |
---|
438 | static XtResource xmxMessageRes[] = { |
---|
439 | { XmpNmessageMaxLines, XmpCMessageMaxLines, XtRInt, sizeof(int), |
---|
440 | FLD(maxLines), XtRString, (XtPointer)"100" |
---|
441 | }, |
---|
442 | { XmpNmessageColumns, XmpCMessageColumns, XtRInt, sizeof(int), |
---|
443 | FLD(columns), XtRString, (XtPointer)"80" |
---|
444 | }, |
---|
445 | { XmpNmessageLinesLostMsg, XmpCMessageLinesLostMsg, |
---|
446 | XtRString, sizeof(String), |
---|
447 | FLD(linesLostMsg), XtRString, (XtPointer)"... some lines lost...\n" |
---|
448 | }, |
---|
449 | { XmpNmessagePopup, XmpCMessagePopup, XtRBoolean, sizeof(Boolean), |
---|
450 | FLD(popup), XtRString, (XtPointer)"True" |
---|
451 | }, |
---|
452 | { XmpNmessageRaise, XmpCMessageRaise, XtRBoolean, sizeof(Boolean), |
---|
453 | FLD(raise), XtRString, (XtPointer)"False" |
---|
454 | }, |
---|
455 | { XmpNmessageBrokenXmTextShowPosition, |
---|
456 | XmpCMessageBrokenXmTextShowPosition, XtRBoolean, sizeof(Boolean), |
---|
457 | FLD(brokenXmTextShowPosition), XtRString, (XtPointer)"False" |
---|
458 | }, |
---|
459 | }; |
---|
460 | #undef FLD |
---|
461 | |
---|
462 | /* Set Messages to go to an XmText widget |
---|
463 | ******************************************************************************* |
---|
464 | Only get the default print proc once, but allow the message widget |
---|
465 | to be changed at any time. |
---|
466 | */ |
---|
467 | /*ARGSUSED*/ |
---|
468 | void XmpMessageWidgetCB( widget, client, call ) |
---|
469 | Widget widget; |
---|
470 | XtPointer client, call; |
---|
471 | { |
---|
472 | String name = (String)client; |
---|
473 | |
---|
474 | if ( WcNonNull( name ) ) |
---|
475 | { |
---|
476 | Widget messageWidget = WcFullNameToWidget( widget, name ); |
---|
477 | |
---|
478 | if ( messageWidget == (Widget)0 ) |
---|
479 | { |
---|
480 | String fullName = WcWidgetToFullName( widget ); |
---|
481 | WcWARN2( widget, "XmpMessageWidget", "notFound", |
---|
482 | "Xmp Warning: XmpMessageWidget could not find %s from %s\n", |
---|
483 | name, fullName ); |
---|
484 | XtFree(fullName); |
---|
485 | return; |
---|
486 | } |
---|
487 | |
---|
488 | XmpMessageWidget( messageWidget ); |
---|
489 | } |
---|
490 | else |
---|
491 | XmpMessageWidget( widget ); |
---|
492 | } |
---|
493 | |
---|
494 | /*ARGSUSED*/ |
---|
495 | void XmpMessageWidgetACT( widget, unused, params, num_params ) |
---|
496 | Widget widget; |
---|
497 | XEvent* unused; |
---|
498 | char** params; |
---|
499 | Cardinal* num_params; |
---|
500 | { |
---|
501 | if ( *num_params == 0 ) |
---|
502 | XmpMessageWidgetCB( widget, NULL, NULL ); |
---|
503 | else if ( *num_params == 1 ) |
---|
504 | XmpMessageWidgetCB( widget, params[0], NULL ); |
---|
505 | else |
---|
506 | WcWARN( widget, "XmpMessageWidget", "usage", |
---|
507 | "Xmp Usage: XmpMessageWidget( [messageWidgetName] )" ); |
---|
508 | } |
---|
509 | |
---|
510 | void XmpMessageWidget( widget ) |
---|
511 | Widget widget; |
---|
512 | { |
---|
513 | if ( widget == (Widget)0 && XmpxDefaultWcPrintProc != (WcPrintProc)0 ) |
---|
514 | { |
---|
515 | /* Reset back to initial state - use default WcPrint method |
---|
516 | */ |
---|
517 | WcPrint = XmpxDefaultWcPrintProc; |
---|
518 | XmpxDefaultWcPrintProc = (WcPrintProc)0; |
---|
519 | xmpMessageWidget = (Widget)0; |
---|
520 | return; |
---|
521 | } |
---|
522 | |
---|
523 | if ( !XmIsText( widget ) ) |
---|
524 | { |
---|
525 | /* Message widget MUST be an XmText widget - leave in current state. |
---|
526 | */ |
---|
527 | String fullName = WcWidgetToFullName( widget ); |
---|
528 | WcWARN1( widget, "XmpMessageWidget", "notXmText", |
---|
529 | "Xmp Warning: XmpMessageWidget(%s) - not an XmTextWidget", |
---|
530 | fullName ); |
---|
531 | XtFree( fullName ); |
---|
532 | return; |
---|
533 | } |
---|
534 | |
---|
535 | /* New XmText widget |
---|
536 | */ |
---|
537 | if ( XmpxDefaultWcPrintProc == (WcPrintProc)0 ) |
---|
538 | { |
---|
539 | XmpxDefaultWcPrintProc = WcPrint; |
---|
540 | WcPrint = XmpxPrint; |
---|
541 | } |
---|
542 | |
---|
543 | xmpMessageWidget = widget; |
---|
544 | |
---|
545 | XtGetApplicationResources( xmpMessageWidget, (XtPointer)xmpMessageOptions, |
---|
546 | xmxMessageRes, XtNumber( xmxMessageRes ), |
---|
547 | NULL, 0 ); |
---|
548 | |
---|
549 | xmpMessageOptions->linesInLinesLostMsg = |
---|
550 | XmpxLinesIn( xmpMessageOptions->linesLostMsg ); |
---|
551 | |
---|
552 | XtAddCallback( widget, XtNdestroyCallback, |
---|
553 | XmpxMessageWidgetDestroyCB, (XtPointer)0 ); |
---|
554 | } |
---|
555 | |
---|
556 | /* Send Messages to the Message Widget |
---|
557 | ******************************************************************************* |
---|
558 | Invoked via pointer to procedure WcPrint. Used to print Wcl messages |
---|
559 | and any other messages printed via WcWARN or WcPrint. |
---|
560 | |
---|
561 | Note: XmpMessageWidget must ensure that if WcPrint is XmpPrint, |
---|
562 | then xmpMessageWidget is an XmTextWidget. |
---|
563 | */ |
---|
564 | #if NeedFunctionPrototypes |
---|
565 | static void XmpxPrint( String msg, ... ) |
---|
566 | #else |
---|
567 | static void XmpxPrint( msg, va_alist ) |
---|
568 | String msg; |
---|
569 | va_dcl |
---|
570 | #endif |
---|
571 | { |
---|
572 | String entireMsg; |
---|
573 | va_list argPtr; |
---|
574 | |
---|
575 | Va_start( argPtr, msg ); /* point at 1st unnamed arg */ |
---|
576 | |
---|
577 | entireMsg = XmpxMakeEntireMessage( msg, argPtr ); |
---|
578 | |
---|
579 | if ( WcNonNull( entireMsg ) ) |
---|
580 | { |
---|
581 | /* Put the message into the XmTextWidget |
---|
582 | */ |
---|
583 | XmTextPosition chPos; /* a long int */ |
---|
584 | String existingText = XmTextGetString( xmpMessageWidget ); |
---|
585 | int linesInExistingText = XmpxLinesIn( existingText ); |
---|
586 | int linesInEntireMsg = XmpxLinesIn( entireMsg ); |
---|
587 | int lines = linesInExistingText + linesInEntireMsg; |
---|
588 | |
---|
589 | if ( lines >= xmpMessageOptions->maxLines ) |
---|
590 | { |
---|
591 | /* Too many lines: First line in XmText probably has the |
---|
592 | * "... some lines lost ..." message already. Therefore, |
---|
593 | * always throw that message away and enough to show entire |
---|
594 | * new message. |
---|
595 | */ |
---|
596 | int linesToReplace = xmpMessageOptions->linesInLinesLostMsg + |
---|
597 | (lines - xmpMessageOptions->maxLines); |
---|
598 | |
---|
599 | for ( lines = 0, chPos = 0 ; existingText[chPos] ; chPos++ ) |
---|
600 | { |
---|
601 | if ( existingText[chPos] == '\n' ) |
---|
602 | ++lines; |
---|
603 | if ( lines > linesToReplace ) |
---|
604 | break; |
---|
605 | } |
---|
606 | /* lines >= 2 because we must always replace at least one line |
---|
607 | * AFTER the linesLostMsg which is (except the 1st time) on the |
---|
608 | * first line. Replace the 1st lines with the linesLostMsg. |
---|
609 | */ |
---|
610 | XmTextReplace( xmpMessageWidget, (XmTextPosition)0, chPos, |
---|
611 | xmpMessageOptions->linesLostMsg ); |
---|
612 | } |
---|
613 | /* Find the new last position, append entireMsg to end. |
---|
614 | */ |
---|
615 | chPos = XmTextGetLastPosition( xmpMessageWidget ); |
---|
616 | |
---|
617 | if ( chPos != 0 ) |
---|
618 | XmTextInsert( xmpMessageWidget, chPos++, "\n" ); |
---|
619 | XmTextInsert( xmpMessageWidget, chPos, entireMsg ); |
---|
620 | |
---|
621 | if ( xmpMessageOptions->brokenXmTextShowPosition ) |
---|
622 | { |
---|
623 | /* Cause text to scroll to show beginning of new message. |
---|
624 | */ |
---|
625 | XmTextShowPosition( xmpMessageWidget, chPos ); |
---|
626 | } |
---|
627 | |
---|
628 | XtFree( existingText ); |
---|
629 | XtFree( entireMsg ); |
---|
630 | |
---|
631 | /* Maybe cause shell to appear, and maybe raise that shell. |
---|
632 | */ |
---|
633 | if ( xmpMessageOptions->popup || xmpMessageOptions->raise ) |
---|
634 | { |
---|
635 | Widget childOfShell = xmpMessageWidget; |
---|
636 | Widget shell = XtParent( childOfShell ); |
---|
637 | |
---|
638 | /* Go up widget tree to find nearest shell ancestor, and its child |
---|
639 | */ |
---|
640 | while ( !XtIsShell( shell ) ) |
---|
641 | { |
---|
642 | childOfShell = shell; |
---|
643 | shell = XtParent( childOfShell ); |
---|
644 | } |
---|
645 | |
---|
646 | if ( xmpMessageOptions->popup ) |
---|
647 | { |
---|
648 | if ( XmIsDialogShell( shell ) ) |
---|
649 | { |
---|
650 | XtManageChild( childOfShell ); |
---|
651 | } |
---|
652 | else |
---|
653 | { |
---|
654 | if ( !XtIsManaged( childOfShell ) ) |
---|
655 | { |
---|
656 | XtManageChild( childOfShell ); |
---|
657 | } |
---|
658 | XtPopup( shell, XtGrabNone ); |
---|
659 | } |
---|
660 | } |
---|
661 | |
---|
662 | if ( xmpMessageOptions->raise ) |
---|
663 | { |
---|
664 | if ( !XtIsManaged( childOfShell ) ) |
---|
665 | { |
---|
666 | XtManageChild( childOfShell ); |
---|
667 | } |
---|
668 | XtRealizeWidget( shell ); |
---|
669 | XRaiseWindow( XtDisplay(shell), XtWindow(shell) ); |
---|
670 | XFlush( XtDisplay(shell) ); |
---|
671 | } |
---|
672 | } |
---|
673 | } |
---|
674 | |
---|
675 | va_end( argPtr ); /* clean up */ |
---|
676 | } |
---|
677 | |
---|
678 | /*ARGSUSED*/ |
---|
679 | static void XmpxMessageWidgetDestroyCB( widget, client, call ) |
---|
680 | Widget widget; |
---|
681 | XtPointer client, call; |
---|
682 | { |
---|
683 | /* Reset back to initial state - use default WcPrint method |
---|
684 | */ |
---|
685 | WcPrint = XmpxDefaultWcPrintProc; |
---|
686 | XmpxDefaultWcPrintProc = (WcPrintProc)0; |
---|
687 | xmpMessageWidget = (Widget)0; |
---|
688 | } |
---|
689 | |
---|
690 | static int XmpxLinesIn( cp ) |
---|
691 | String cp; |
---|
692 | { |
---|
693 | int lines; |
---|
694 | |
---|
695 | for ( lines = 1 ; *cp ; ++cp ) |
---|
696 | { |
---|
697 | if ( *cp == '\n' ) |
---|
698 | ++lines; |
---|
699 | } |
---|
700 | return lines; |
---|
701 | } |
---|
702 | |
---|
703 | static int XmpxEntireMessageLen( msg, argPtr ) |
---|
704 | String msg; |
---|
705 | va_list argPtr; |
---|
706 | { |
---|
707 | int len = WcStrLen( msg ); |
---|
708 | |
---|
709 | if ( msg == NULL ) |
---|
710 | return 0; |
---|
711 | |
---|
712 | msg = va_arg( argPtr, String ); /* get unnamed string arg */ |
---|
713 | while ( msg != NULL ) |
---|
714 | { |
---|
715 | len += WcStrLen( msg ); |
---|
716 | msg = va_arg( argPtr, String ); /* get next unnamed string arg */ |
---|
717 | } |
---|
718 | |
---|
719 | return len; |
---|
720 | } |
---|
721 | |
---|
722 | static String XmpxMakeEntireMessage( msg, argPtr ) |
---|
723 | String msg; |
---|
724 | va_list argPtr; |
---|
725 | { |
---|
726 | int entireLen = XmpxEntireMessageLen( msg, argPtr ); |
---|
727 | String entireMsg = XtMalloc( entireLen + 1 ); |
---|
728 | |
---|
729 | if ( entireMsg == NULL ) |
---|
730 | return NULL; /* malloc failed */ |
---|
731 | |
---|
732 | strcpy( entireMsg, msg ); |
---|
733 | |
---|
734 | msg = va_arg( argPtr, String ); /* get unnamed string arg */ |
---|
735 | while ( msg != NULL ) |
---|
736 | { |
---|
737 | strcat( entireMsg, msg ); |
---|
738 | msg = va_arg( argPtr, String ); /* get next unnamed string arg */ |
---|
739 | } |
---|
740 | |
---|
741 | return entireMsg; |
---|
742 | } |
---|
743 | |
---|
744 | /* -- XmpTable Callbacks and Actions |
---|
745 | ******************************************************************************* |
---|
746 | These callbacks and actions make the XmpTable convenience functions |
---|
747 | accessible via the Xrm database. |
---|
748 | */ |
---|
749 | |
---|
750 | /*ARGSUSED*/ |
---|
751 | void XmpTableChildConfigCB( w, client, ignored ) |
---|
752 | Widget w; |
---|
753 | XtPointer client; |
---|
754 | XtPointer ignored; |
---|
755 | { |
---|
756 | WcInvokeAction( XmpTableChildConfigACT, w, client ); |
---|
757 | } |
---|
758 | |
---|
759 | /*ARGSUSED*/ |
---|
760 | void XmpTableChildConfigACT( w, unusedEvent, params, num_params ) |
---|
761 | Widget w; |
---|
762 | XEvent *unusedEvent; |
---|
763 | String *params; |
---|
764 | Cardinal *num_params; |
---|
765 | { |
---|
766 | Widget widget; |
---|
767 | Position col; |
---|
768 | Position row; |
---|
769 | Dimension hSpan = 1; /* optional arg, default */ |
---|
770 | Dimension vSpan = 1; /* optional arg, default */ |
---|
771 | XmpTableOpts opt = TBL_DEF_OPT; /* optional arg, default */ |
---|
772 | |
---|
773 | if (*num_params < 3 || 6 < *num_params ) |
---|
774 | { |
---|
775 | WcWARN( w, "XmpTableChildConfig", "wrongNumArgs", |
---|
776 | "Xmp Warning: XmpTableChildConfig( ... ) - Wrong number of arguments.\n\ |
---|
777 | Xmp Usage: XmpTableChildConfig( w col row [h_span [v_span [opts]]] )" ); |
---|
778 | return; |
---|
779 | } |
---|
780 | |
---|
781 | widget = WcFullNameToWidget( w, params[0] ); |
---|
782 | col = atoi( params[1] ); |
---|
783 | row = atoi( params[2] ); |
---|
784 | |
---|
785 | if ((Widget)0 == widget) |
---|
786 | { |
---|
787 | WcWARN1( w, "XmpTableChildConfig", "widgetNotFound", |
---|
788 | "Xmp Warning: XmpTableChildConfig( %s ... ) - Could not find widget.", |
---|
789 | params[0] ); |
---|
790 | return; |
---|
791 | } |
---|
792 | else if ( ! XtIsSubclass( XtParent(widget), xmpTableWidgetClass )) |
---|
793 | { |
---|
794 | WcWARN1( w, "XmpTableChildConfig", "notXmpTable", |
---|
795 | "Xmp Warning: XmpTableChildConfig( %s ... ) - Widget not child of an XmpTable.", |
---|
796 | params[0] ); |
---|
797 | return; |
---|
798 | } |
---|
799 | |
---|
800 | if ( 3 < *num_params ) |
---|
801 | hSpan = atoi( params[3] ); |
---|
802 | if ( 4 < *num_params ) |
---|
803 | vSpan = atoi( params[4] ); |
---|
804 | if ( 5 < *num_params ) |
---|
805 | opt = XmpTableOptsParse( params[5] ); |
---|
806 | |
---|
807 | XmpTableChildConfig( widget, col, row, hSpan, vSpan, opt ); |
---|
808 | } |
---|
809 | |
---|
810 | /*ARGSUSED*/ |
---|
811 | void XmpTableChildPositionCB( w, client, ignored ) |
---|
812 | Widget w; |
---|
813 | XtPointer client; |
---|
814 | XtPointer ignored; |
---|
815 | { |
---|
816 | WcInvokeAction( XmpTableChildPositionACT, w, client ); |
---|
817 | } |
---|
818 | |
---|
819 | /*ARGSUSED*/ |
---|
820 | void XmpTableChildPositionACT( w, unusedEvent, params, num_params ) |
---|
821 | Widget w; |
---|
822 | XEvent *unusedEvent; |
---|
823 | String *params; |
---|
824 | Cardinal *num_params; |
---|
825 | { |
---|
826 | Widget widget; |
---|
827 | Position col; |
---|
828 | Position row; |
---|
829 | |
---|
830 | if (*num_params != 3 ) |
---|
831 | { |
---|
832 | WcWARN( w, "XmpTableChildPosition", "wrongNumArgs", |
---|
833 | "Xmp Warning: XmpTableChildPosition( ... ) - Wrong number of arguments.\n\ |
---|
834 | Xmp Usage: XmpTableChildPosition( w col row )" ); |
---|
835 | return; |
---|
836 | } |
---|
837 | |
---|
838 | widget = WcFullNameToWidget( w, params[0] ); |
---|
839 | col = atoi( params[1] ); |
---|
840 | row = atoi( params[2] ); |
---|
841 | |
---|
842 | if ((Widget)0 == widget) |
---|
843 | { |
---|
844 | WcWARN1( w, "XmpTableChildPosition", "widgetNotFound", |
---|
845 | "Xmp Warning: XmpTableChildPosition( %s ... ) could not find widget.", |
---|
846 | params[0] ); |
---|
847 | return; |
---|
848 | } |
---|
849 | else if ( ! XtIsSubclass( XtParent(widget), xmpTableWidgetClass )) |
---|
850 | { |
---|
851 | WcWARN1( w, "XmpTableChildPosition", "notXmpTable", |
---|
852 | "Xmp Warning: XmpTableChildPosition( %s ... ) Widget not child of an XmpTable.", |
---|
853 | params[0] ); |
---|
854 | return; |
---|
855 | } |
---|
856 | |
---|
857 | XmpTableChildPosition( widget, col, row ); |
---|
858 | } |
---|
859 | |
---|
860 | /*ARGSUSED*/ |
---|
861 | void XmpTableChildResizeCB( w, client, ignored ) |
---|
862 | Widget w; |
---|
863 | XtPointer client; |
---|
864 | XtPointer ignored; |
---|
865 | { |
---|
866 | WcInvokeAction( XmpTableChildResizeACT, w, client ); |
---|
867 | } |
---|
868 | |
---|
869 | /*ARGSUSED*/ |
---|
870 | void XmpTableChildResizeACT( w, unusedEvent, params, num_params ) |
---|
871 | Widget w; |
---|
872 | XEvent *unusedEvent; |
---|
873 | String *params; |
---|
874 | Cardinal *num_params; |
---|
875 | { |
---|
876 | Widget widget; |
---|
877 | Dimension hSpan; |
---|
878 | Dimension vSpan; |
---|
879 | |
---|
880 | if (*num_params != 3 ) |
---|
881 | { |
---|
882 | WcWARN( w, "XmpTableChildResize", "wrongNumArgs", |
---|
883 | "Xmp Warning: XmpTableChildResize( ... ) Wrong number of arguments.\n\ |
---|
884 | Xmp Usage: XmpTableChildResize( w h_span v_span )" ); |
---|
885 | return; |
---|
886 | } |
---|
887 | |
---|
888 | widget = WcFullNameToWidget( w, params[0] ); |
---|
889 | hSpan = atoi( params[1] ); |
---|
890 | vSpan = atoi( params[2] ); |
---|
891 | |
---|
892 | if ((Widget)0 == widget) |
---|
893 | { |
---|
894 | WcWARN1( w, "XmpTableChildResize", "widgetNotFound", |
---|
895 | "Xmp Warning: XmpTableChildResize( %s ... ) - Widget not found.", |
---|
896 | params[0] ); |
---|
897 | return; |
---|
898 | } |
---|
899 | else if ( ! XtIsSubclass( XtParent(widget), xmpTableWidgetClass )) |
---|
900 | { |
---|
901 | WcWARN1( w, "XmpTableChildResize", "notXmpTable", |
---|
902 | "Xmp Warning: XmpTableChildResize( %s ... ) - Widget not child of an XmpTable.", |
---|
903 | params[0] ); |
---|
904 | return; |
---|
905 | } |
---|
906 | |
---|
907 | XmpTableChildResize( widget, hSpan, vSpan ); |
---|
908 | } |
---|
909 | |
---|
910 | /*ARGSUSED*/ |
---|
911 | void XmpTableChildOptionsCB( w, client, ignored ) |
---|
912 | Widget w; |
---|
913 | XtPointer client; |
---|
914 | XtPointer ignored; |
---|
915 | { |
---|
916 | WcInvokeAction( XmpTableChildOptionsACT, w, client ); |
---|
917 | } |
---|
918 | |
---|
919 | /*ARGSUSED*/ |
---|
920 | void XmpTableChildOptionsACT( w, unusedEvent, params, num_params ) |
---|
921 | Widget w; |
---|
922 | XEvent *unusedEvent; |
---|
923 | String *params; |
---|
924 | Cardinal *num_params; |
---|
925 | { |
---|
926 | Widget widget; |
---|
927 | XmpTableOpts opt; |
---|
928 | |
---|
929 | if (*num_params != 2 ) |
---|
930 | { |
---|
931 | WcWARN( w, "XmpTableChildOptions", "wrongNumArgs", |
---|
932 | "Xmp Warning: XmpTableChildOptions() - Wrong number of arguments.\n\ |
---|
933 | Xmp Usage: XmpTableChildOptions( w options )" ); |
---|
934 | return; |
---|
935 | } |
---|
936 | |
---|
937 | if ((Widget)0 == (widget = WcFullNameToWidget( w, params[0] )) ) |
---|
938 | { |
---|
939 | WcWARN1( w, "XmpTableChildOptions", "widgetNotFound", |
---|
940 | "Xmp Warning: XmpTableChildOptions( %s ... ) - Widget not found.", |
---|
941 | params[0] ); |
---|
942 | return; |
---|
943 | } |
---|
944 | else if ( ! XtIsSubclass( XtParent(widget), xmpTableWidgetClass )) |
---|
945 | { |
---|
946 | WcWARN1( w, "XmpTableChildOptions", "notXmpTable", |
---|
947 | "Xmp Warning: XmpTableChildOptions( %s ... ) Widget not child of an XmpTable.", |
---|
948 | params[0] ); |
---|
949 | return; |
---|
950 | } |
---|
951 | |
---|
952 | opt = XmpTableOptsParse( params[1] ); |
---|
953 | XmpTableChildOptions( widget, opt ); |
---|
954 | } |
---|
955 | |
---|
956 | /* -- Register all Xmp provided actions and callbacks. |
---|
957 | ******************************************************************************* |
---|
958 | */ |
---|
959 | |
---|
960 | void XmpAddActionsAndCallbacks ( app ) |
---|
961 | XtAppContext app; |
---|
962 | { |
---|
963 | static XtActionsRec XmpActions[] = { |
---|
964 | {"XmpPopup", XmpPopupACT }, |
---|
965 | {"XmpPopupACT", XmpPopupACT }, |
---|
966 | {"XmpFixTranslations", XmpFixTranslationsACT }, |
---|
967 | {"XmpFixTranslationsACT", XmpFixTranslationsACT }, |
---|
968 | {"XmpAddMwmCloseCallback", XmpAddMwmCloseCallbackACT }, |
---|
969 | {"XmpAddMwmCloseCallbackACT", XmpAddMwmCloseCallbackACT }, |
---|
970 | {"XmpAddTabGroup", XmpAddTabGroupACT }, |
---|
971 | {"XmpAddTabGroupACT", XmpAddTabGroupACT }, |
---|
972 | {"XmpMessageWidget", XmpMessageWidgetACT }, |
---|
973 | {"XmpMessageWidgetACT", XmpMessageWidgetACT }, |
---|
974 | {"XmpTableChildConfig", XmpTableChildConfigACT }, |
---|
975 | {"XmpTableChildConfigACT", XmpTableChildConfigACT }, |
---|
976 | {"XmpTableChildPosition", XmpTableChildPositionACT }, |
---|
977 | {"XmpTableChildPositionACT", XmpTableChildPositionACT }, |
---|
978 | {"XmpTableChildResize", XmpTableChildResizeACT }, |
---|
979 | {"XmpTableChildResizeACT", XmpTableChildResizeACT }, |
---|
980 | {"XmpTableChildOptions", XmpTableChildOptionsACT }, |
---|
981 | {"XmpTableChildOptionsACT", XmpTableChildOptionsACT }, |
---|
982 | /* -- compatibility: */ |
---|
983 | {"MriPopup", XmpPopupACT }, |
---|
984 | {"MriPopupACT", XmpPopupACT }, |
---|
985 | }; |
---|
986 | |
---|
987 | ONCE_PER_XtAppContext( app ); |
---|
988 | |
---|
989 | /* -- Register Motif specific action functions */ |
---|
990 | XtAppAddActions(app, XmpActions, XtNumber(XmpActions)); |
---|
991 | |
---|
992 | /* -- Register Motif specific callback functions */ |
---|
993 | #define RCALL( name, func ) WcRegisterCallback ( app, name, func, (XtPointer)0 ) |
---|
994 | |
---|
995 | RCALL("XmpFixTranslations", XmpFixTranslationsCB ); |
---|
996 | RCALL("XmpFixTranslationsCB", XmpFixTranslationsCB ); |
---|
997 | RCALL("XmpAddMwmCloseCallback", XmpAddMwmCloseCallbackCB ); |
---|
998 | RCALL("XmpAddMwmCloseCallbackCB", XmpAddMwmCloseCallbackCB ); |
---|
999 | RCALL("XmpAddTabGroup", XmpAddTabGroupCB ); |
---|
1000 | RCALL("XmpAddTabGroupCB", XmpAddTabGroupCB ); |
---|
1001 | RCALL("XmpMessageWidget", XmpMessageWidgetCB ); |
---|
1002 | RCALL("XmpMessageWidgetCB", XmpMessageWidgetCB ); |
---|
1003 | RCALL("XmpTableChildConfig", XmpTableChildConfigCB ); |
---|
1004 | RCALL("XmpTableChildConfigCB", XmpTableChildConfigCB ); |
---|
1005 | RCALL("XmpTableChildPosition", XmpTableChildPositionCB ); |
---|
1006 | RCALL("XmpTableChildPositionCB", XmpTableChildPositionCB ); |
---|
1007 | RCALL("XmpTableChildResize", XmpTableChildResizeCB ); |
---|
1008 | RCALL("XmpTableChildResizeCB", XmpTableChildResizeCB ); |
---|
1009 | RCALL("XmpTableChildOptions", XmpTableChildOptionsCB ); |
---|
1010 | RCALL("XmpTableChildOptionsCB", XmpTableChildOptionsCB ); |
---|
1011 | } |
---|