1 /* The lwlib interface to Athena widgets.
2 Copyright (C) 1993 Chuck Thompson <cthomp@cs.uiuc.edu>
4 This file is part of the Lucid Widget Library.
6 The Lucid Widget Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 1, or (at your option)
11 The Lucid Widget Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
22 #include "lwlib-Xaw.h"
24 #include <X11/StringDefs.h>
25 #include <X11/IntrinsicP.h>
26 #include <X11/CoreP.h>
27 #include <X11/Shell.h>
29 #include <X11/Xaw/Scrollbar.h>
30 #include <X11/Xaw/Paned.h>
31 #include <X11/Xaw/Dialog.h>
32 #include <X11/Xaw/Form.h>
33 #include <X11/Xaw/Command.h>
34 #include <X11/Xaw/Label.h>
36 #include <X11/Xatom.h>
38 static void xaw_generic_callback (Widget
, XtPointer
, XtPointer
);
42 lw_xaw_widget_p (widget
)
45 return (XtIsSubclass (widget
, scrollbarWidgetClass
) ||
46 XtIsSubclass (widget
, dialogWidgetClass
));
50 xaw_update_scrollbar (instance
, widget
, val
)
51 widget_instance
*instance
;
56 if (val
->scrollbar_data
)
58 scrollbar_values
*data
= val
->scrollbar_data
;
59 Dimension height
, width
;
60 Dimension pos_x
, pos_y
;
61 int widget_shown
, widget_topOfThumb
;
62 float new_shown
, new_topOfThumb
;
64 XtVaGetValues (widget
,
69 XtNtopOfThumb
, &widget_topOfThumb
,
70 XtNshown
, &widget_shown
,
74 * First size and position the scrollbar widget.
75 * We need to position it to second-guess the Paned widget's notion
76 * of what should happen when the WMShell gets resized.
78 if (height
!= data
->scrollbar_height
|| pos_y
!= data
->scrollbar_pos
)
80 XtConfigureWidget (widget
, pos_x
, data
->scrollbar_pos
,
81 width
, data
->scrollbar_height
, 0);
83 XtVaSetValues (widget
,
84 XtNlength
, data
->scrollbar_height
,
90 * Now the size the scrollbar's slider.
92 new_shown
= (float) data
->slider_size
/
93 (float) (data
->maximum
- data
->minimum
);
95 new_topOfThumb
= (float) (data
->slider_position
- data
->minimum
) /
96 (float) (data
->maximum
- data
->minimum
);
103 if (new_topOfThumb
> 1.0)
104 new_topOfThumb
= 1.0;
105 if (new_topOfThumb
< 0)
108 if (new_shown
!= widget_shown
|| new_topOfThumb
!= widget_topOfThumb
)
109 XawScrollbarSetThumb (widget
, new_topOfThumb
, new_shown
);
115 xaw_update_one_widget (instance
, widget
, val
, deep_p
)
116 widget_instance
*instance
;
122 if (XtIsSubclass (widget
, scrollbarWidgetClass
))
124 xaw_update_scrollbar (instance
, widget
, val
);
127 if (XtIsSubclass (widget
, dialogWidgetClass
))
131 XtSetArg (al
[ac
], XtNlabel
, val
->contents
->value
); ac
++;
132 XtSetValues (widget
, al
, ac
);
134 else if (XtIsSubclass (widget
, commandWidgetClass
))
139 XtVaGetValues (widget
, XtNborderWidth
, &bw
, 0);
141 /* Don't let buttons end up with 0 borderwidth, that's ugly...
142 Yeah, all this should really be done through app-defaults files
143 or fallback resources, but that's a whole different can of worms
144 that I don't feel like opening right now. Making Athena widgets
145 not look like shit is just entirely too much work.
148 XtSetArg (al
[0], XtNborderWidth
, 1);
149 XtSetValues (widget
, al
, 1);
152 XtSetArg (al
[0], XtNlabel
, val
->value
);
153 XtSetArg (al
[1], XtNsensitive
, val
->enabled
);
154 /* Force centered button text. Se above. */
155 XtSetArg (al
[2], XtNjustify
, XtJustifyCenter
);
156 XtSetValues (widget
, al
, 3);
157 XtRemoveAllCallbacks (widget
, XtNcallback
);
158 XtAddCallback (widget
, XtNcallback
, xaw_generic_callback
, instance
);
163 xaw_update_one_value (instance
, widget
, val
)
164 widget_instance
*instance
;
168 /* This function is not used by the scrollbars and those are the only
169 Athena widget implemented at the moment so do nothing. */
174 xaw_destroy_instance (instance
)
175 widget_instance
*instance
;
177 if (XtIsSubclass (instance
->widget
, dialogWidgetClass
))
178 /* Need to destroy the Shell too. */
179 XtDestroyWidget (XtParent (instance
->widget
));
181 XtDestroyWidget (instance
->widget
);
185 xaw_popup_menu (widget
)
188 /* An Athena menubar has not been implemented. */
193 xaw_pop_instance (instance
, up
)
194 widget_instance
*instance
;
197 Widget widget
= instance
->widget
;
201 if (XtIsSubclass (widget
, dialogWidgetClass
))
203 /* For dialogs, we need to call XtPopup on the parent instead
204 of calling XtManageChild on the widget.
205 Also we need to hack the shell's WM_PROTOCOLS to get it to
206 understand what the close box is supposed to do!!
208 Display
*dpy
= XtDisplay (widget
);
209 Widget shell
= XtParent (widget
);
212 props
[i
++] = XInternAtom (dpy
, "WM_DELETE_WINDOW", False
);
213 XChangeProperty (dpy
, XtWindow (shell
),
214 XInternAtom (dpy
, "WM_PROTOCOLS", False
),
215 XA_ATOM
, 32, PropModeAppend
,
216 (unsigned char *) props
, i
);
218 /* Center the widget in its parent. Why isn't this kind of crap
219 done automatically? I thought toolkits were supposed to make
224 Widget topmost
= instance
->parent
;
225 w
= shell
->core
.width
;
226 h
= shell
->core
.height
;
227 while (topmost
->core
.parent
&& XtIsRealized (topmost
->core
.parent
))
228 topmost
= topmost
->core
.parent
;
229 if (topmost
->core
.width
< w
) x
= topmost
->core
.x
;
230 else x
= topmost
->core
.x
+ ((topmost
->core
.width
- w
) / 2);
231 if (topmost
->core
.height
< h
) y
= topmost
->core
.y
;
232 else y
= topmost
->core
.y
+ ((topmost
->core
.height
- h
) / 2);
233 XtMoveWidget (shell
, x
, y
);
236 /* Finally, pop it up. */
237 XtPopup (shell
, XtGrabNonexclusive
);
240 XtManageChild (widget
);
244 if (XtIsSubclass (widget
, dialogWidgetClass
))
245 XtUnmanageChild (XtParent (widget
));
247 XtUnmanageChild (widget
);
254 static char overrideTrans
[] =
255 "<Message>WM_PROTOCOLS: lwlib_delete_dialog()";
256 static void wm_delete_window();
257 static XtActionsRec xaw_actions
[] = {
258 {"lwlib_delete_dialog", wm_delete_window
}
260 static Boolean actions_initted
= False
;
263 make_dialog (name
, parent
, pop_up_p
, shell_title
, icon_name
, text_input_slot
, radio_box
, list
, left_buttons
, right_buttons
)
269 Boolean text_input_slot
;
278 char button_name
[255];
282 XtTranslations override
;
284 if (! pop_up_p
) abort (); /* not implemented */
285 if (text_input_slot
) abort (); /* not implemented */
286 if (radio_box
) abort (); /* not implemented */
287 if (list
) abort (); /* not implemented */
289 if (! actions_initted
)
291 XtAppContext app
= XtWidgetToApplicationContext (parent
);
292 XtAppAddActions (app
, xaw_actions
,
293 sizeof (xaw_actions
) / sizeof (xaw_actions
[0]));
294 actions_initted
= True
;
297 override
= XtParseTranslationTable (overrideTrans
);
300 XtSetArg (av
[ac
], XtNtitle
, shell_title
); ac
++;
301 XtSetArg (av
[ac
], XtNallowShellResize
, True
); ac
++;
303 /* Don't allow any geometry request from the user. */
304 XtSetArg (av
[ac
], XtNgeometry
, 0); ac
++;
306 shell
= XtCreatePopupShell ("dialog", transientShellWidgetClass
,
308 XtOverrideTranslations (shell
, override
);
311 dialog
= XtCreateManagedWidget (name
, dialogWidgetClass
, shell
, av
, ac
);
315 for (i
= 0; i
< left_buttons
; i
++)
318 XtSetArg (av
[ac
], XtNfromHoriz
, button
); ac
++;
319 XtSetArg (av
[ac
], XtNleft
, XtChainLeft
); ac
++;
320 XtSetArg (av
[ac
], XtNright
, XtChainLeft
); ac
++;
321 XtSetArg (av
[ac
], XtNtop
, XtChainBottom
); ac
++;
322 XtSetArg (av
[ac
], XtNbottom
, XtChainBottom
); ac
++;
323 XtSetArg (av
[ac
], XtNresizable
, True
); ac
++;
324 sprintf (button_name
, "button%d", ++bc
);
325 button
= XtCreateManagedWidget (button_name
, commandWidgetClass
,
330 /* Create a separator
332 I want the separator to take up the slack between the buttons on
333 the right and the buttons on the left (that is I want the buttons
334 after the separator to be packed against the right edge of the
335 window) but I can't seem to make it do it.
338 XtSetArg (av
[ac
], XtNfromHoriz
, button
); ac
++;
339 /* XtSetArg (av [ac], XtNfromVert, XtNameToWidget (dialog, "label")); ac++; */
340 XtSetArg (av
[ac
], XtNleft
, XtChainLeft
); ac
++;
341 XtSetArg (av
[ac
], XtNright
, XtChainRight
); ac
++;
342 XtSetArg (av
[ac
], XtNtop
, XtChainBottom
); ac
++;
343 XtSetArg (av
[ac
], XtNbottom
, XtChainBottom
); ac
++;
344 XtSetArg (av
[ac
], XtNlabel
, ""); ac
++;
345 XtSetArg (av
[ac
], XtNwidth
, 30); ac
++; /* #### aaack!! */
346 XtSetArg (av
[ac
], XtNborderWidth
, 0); ac
++;
347 XtSetArg (av
[ac
], XtNshapeStyle
, XmuShapeRectangle
); ac
++;
348 XtSetArg (av
[ac
], XtNresizable
, False
); ac
++;
349 XtSetArg (av
[ac
], XtNsensitive
, False
); ac
++;
350 button
= XtCreateManagedWidget ("separator",
351 /* labelWidgetClass, */
352 /* This has to be Command to fake out
353 the Dialog widget... */
357 for (i
= 0; i
< right_buttons
; i
++)
360 XtSetArg (av
[ac
], XtNfromHoriz
, button
); ac
++;
361 XtSetArg (av
[ac
], XtNleft
, XtChainRight
); ac
++;
362 XtSetArg (av
[ac
], XtNright
, XtChainRight
); ac
++;
363 XtSetArg (av
[ac
], XtNtop
, XtChainBottom
); ac
++;
364 XtSetArg (av
[ac
], XtNbottom
, XtChainBottom
); ac
++;
365 XtSetArg (av
[ac
], XtNresizable
, True
); ac
++;
366 sprintf (button_name
, "button%d", ++bc
);
367 button
= XtCreateManagedWidget (button_name
, commandWidgetClass
,
375 xaw_create_dialog (instance
)
376 widget_instance
* instance
;
378 char *name
= instance
->info
->type
;
379 Widget parent
= instance
->parent
;
381 Boolean pop_up_p
= instance
->pop_up_p
;
382 char *shell_name
= 0;
384 Boolean text_input_slot
= False
;
385 Boolean radio_box
= False
;
386 Boolean list
= False
;
388 int left_buttons
= 0;
389 int right_buttons
= 1;
393 icon_name
= "dbox-error";
394 shell_name
= "Error";
398 icon_name
= "dbox-info";
399 shell_name
= "Information";
404 icon_name
= "dbox-question";
405 shell_name
= "Prompt";
409 text_input_slot
= True
;
410 icon_name
= "dbox-question";
411 shell_name
= "Prompt";
415 icon_name
= "dbox-question";
416 shell_name
= "Question";
420 total_buttons
= name
[1] - '0';
422 if (name
[3] == 'T' || name
[3] == 't')
424 text_input_slot
= False
;
428 right_buttons
= name
[4] - '0';
430 left_buttons
= total_buttons
- right_buttons
;
432 widget
= make_dialog (name
, parent
, pop_up_p
,
433 shell_name
, icon_name
, text_input_slot
, radio_box
,
434 list
, left_buttons
, right_buttons
);
441 xaw_generic_callback (widget
, closure
, call_data
)
446 widget_instance
*instance
= (widget_instance
*) closure
;
447 Widget instance_widget
;
451 lw_internal_update_other_instances (widget
, closure
, call_data
);
455 if (widget
->core
.being_destroyed
)
458 instance_widget
= instance
->widget
;
459 if (!instance_widget
)
462 id
= instance
->info
->id
;
466 XtVaGetValues (widget
, XtNuserData
, &user_data
, 0);
468 /* Damn! Athena doesn't give us a way to hang our own data on the
469 buttons, so we have to go find it... I guess this assumes that
470 all instances of a button have the same call data. */
472 widget_value
*val
= instance
->info
->val
->contents
;
473 char *name
= XtName (widget
);
476 if (val
->name
&& !strcmp (val
->name
, name
))
481 user_data
= val
->call_data
;
485 if (instance
->info
->selection_cb
)
486 instance
->info
->selection_cb (widget
, id
, user_data
);
490 wm_delete_window (shell
, closure
, call_data
)
498 if (! XtIsSubclass (shell
, shellWidgetClass
))
500 XtVaGetValues (shell
, XtNchildren
, &kids
, 0);
504 if (! XtIsSubclass (widget
, dialogWidgetClass
))
506 id
= lw_get_widget_id (widget
);
510 widget_info
*info
= lw_get_widget_info (id
);
511 if (! info
) abort ();
512 if (info
->selection_cb
)
513 info
->selection_cb (widget
, id
, (XtPointer
) -1);
516 lw_destroy_all_widgets (id
);
523 xaw_scrollbar_scroll (widget
, closure
, call_data
)
529 widget_instance
*instance
= (widget_instance
*) closure
;
531 scroll_event event_data
;
533 if (!instance
|| widget
->core
.being_destroyed
)
536 id
= instance
->info
->id
;
537 event_data
.slider_value
= 0;
540 if ((int) call_data
> 0)
541 event_data
.action
= SCROLLBAR_PAGE_DOWN
;
543 event_data
.action
= SCROLLBAR_PAGE_UP
;
545 if (instance
->info
->pre_activate_cb
)
546 instance
->info
->pre_activate_cb (widget
, id
, (XtPointer
) &event_data
);
551 xaw_scrollbar_jump (widget
, closure
, call_data
)
557 widget_instance
*instance
= (widget_instance
*) closure
;
559 scroll_event event_data
;
560 scrollbar_values
*val
=
561 (scrollbar_values
*) instance
->info
->val
->scrollbar_data
;
564 if (!instance
|| widget
->core
.being_destroyed
)
567 id
= instance
->info
->id
;
569 percent
= * (float *) call_data
;
570 event_data
.slider_value
=
571 (int) (percent
* (float) (val
->maximum
- val
->minimum
)) + val
->minimum
;
574 event_data
.action
= SCROLLBAR_DRAG
;
576 if (instance
->info
->pre_activate_cb
)
577 instance
->info
->pre_activate_cb (widget
, id
, (XtPointer
) &event_data
);
582 xaw_create_scrollbar (instance
)
583 widget_instance
*instance
;
591 XtVaGetValues (instance
->parent
, XtNwidth
, &width
, 0);
593 XtSetArg (av
[ac
], XtNshowGrip
, 0); ac
++;
594 XtSetArg (av
[ac
], XtNresizeToPreferred
, 1); ac
++;
595 XtSetArg (av
[ac
], XtNallowResize
, True
); ac
++;
596 XtSetArg (av
[ac
], XtNskipAdjust
, True
); ac
++;
597 XtSetArg (av
[ac
], XtNwidth
, width
); ac
++;
598 XtSetArg (av
[ac
], XtNmappedWhenManaged
, True
); ac
++;
601 XtCreateWidget (instance
->info
->name
, scrollbarWidgetClass
,
602 instance
->parent
, av
, ac
);
604 /* We have to force the border width to be 0 otherwise the
605 geometry manager likes to start looping for awhile... */
606 XtVaSetValues (scrollbar
, XtNborderWidth
, 0, 0);
608 XtRemoveAllCallbacks (scrollbar
, "jumpProc");
609 XtRemoveAllCallbacks (scrollbar
, "scrollProc");
611 XtAddCallback (scrollbar
, "jumpProc", xaw_scrollbar_jump
,
612 (XtPointer
) instance
);
613 XtAddCallback (scrollbar
, "scrollProc", xaw_scrollbar_scroll
,
614 (XtPointer
) instance
);
620 widget_creation_entry
621 xaw_creation_table
[] =
623 {"scrollbar", xaw_create_scrollbar
},