1 /* The lwlib interface to Athena widgets.
3 Copyright (C) 1993 Chuck Thompson <cthomp@cs.uiuc.edu>
4 Copyright (C) 1994, 2001-2012 Free Software Foundation, Inc.
6 This file is part of the Lucid Widget Library.
8 The Lucid Widget Library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 1, or (at your option)
13 The Lucid Widget Library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GNU Emacs; see the file COPYING. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA. */
30 #include "lwlib-Xaw.h"
32 #include <X11/StringDefs.h>
33 #include <X11/IntrinsicP.h>
34 #include <X11/CoreP.h>
35 #include <X11/Shell.h>
38 #include <X11/Xaw3d/Scrollbar.h>
39 #include <X11/Xaw3d/Paned.h>
40 #include <X11/Xaw3d/Dialog.h>
41 #include <X11/Xaw3d/Form.h>
42 #include <X11/Xaw3d/Command.h>
43 #include <X11/Xaw3d/Label.h>
44 #else /* !HAVE_XAW3D */
45 #include <X11/Xaw/Scrollbar.h>
46 #include <X11/Xaw/Paned.h>
47 #include <X11/Xaw/Dialog.h>
48 #include <X11/Xaw/Form.h>
49 #include <X11/Xaw/Command.h>
50 #include <X11/Xaw/Label.h>
51 #endif /* HAVE_XAW3D */
53 #include <X11/Xatom.h>
56 #include <X11/Xft/Xft.h>
58 struct widget_xft_data
63 XftColor xft_fg
, xft_bg
;
64 int p_width
, p_height
;
71 static void xaw_generic_callback (Widget widget
,
77 lw_xaw_widget_p (Widget widget
)
79 return (XtIsSubclass (widget
, scrollbarWidgetClass
) ||
80 XtIsSubclass (widget
, dialogWidgetClass
));
86 fill_xft_data (struct widget_xft_data
*data
, Widget widget
, XftFont
*font
)
91 data
->widget
= widget
;
92 data
->xft_font
= font
;
93 XtVaGetValues (widget
,
98 colors
[0].pixel
= data
->xft_fg
.pixel
= fg
;
99 colors
[1].pixel
= data
->xft_bg
.pixel
= bg
;
100 XQueryColors (XtDisplay (widget
),
101 DefaultColormapOfScreen (XtScreen (widget
)),
104 data
->xft_fg
.color
.alpha
= 0xFFFF;
105 data
->xft_fg
.color
.red
= colors
[0].red
;
106 data
->xft_fg
.color
.green
= colors
[0].green
;
107 data
->xft_fg
.color
.blue
= colors
[0].blue
;
108 data
->xft_bg
.color
.alpha
= 0xFFFF;
109 data
->xft_bg
.color
.red
= colors
[1].red
;
110 data
->xft_bg
.color
.green
= colors
[1].green
;
111 data
->xft_bg
.color
.blue
= colors
[1].blue
;
115 data
->p_width
= data
->p_height
= 0;
119 openFont (Widget widget
, char *name
)
122 int screen
= XScreenNumberOfScreen (XtScreen (widget
));
123 int len
= strlen (fname
), i
= len
-1;
126 /* Try to convert Gtk-syntax (Sans 9) to Xft syntax Sans-9. */
127 while (i
> 0 && '0' <= fname
[i
] && fname
[i
] <= '9')
131 fname
= xstrdup (name
);
135 fn
= XftFontOpenName (XtDisplay (widget
), screen
, fname
);
136 if (fname
!= name
) xfree (fname
);
142 get_text_width_and_height (Widget widget
, char *text
,
149 while (bp
&& *bp
!= '\0')
152 char *cp
= strchr (bp
, '\n');
153 XftTextExtentsUtf8 (XtDisplay (widget
), xft_font
,
155 cp
? cp
- bp
: strlen (bp
),
157 bp
= cp
? cp
+ 1 : NULL
;
158 h
+= xft_font
->height
;
159 if (w
< gi
.width
) w
= gi
.width
;
167 draw_text (struct widget_xft_data
*data
, char *lbl
, int inverse
)
169 Screen
*sc
= XtScreen (data
->widget
);
170 int screen
= XScreenNumberOfScreen (sc
);
171 int y
= data
->xft_font
->ascent
;
172 int x
= inverse
? 0 : 2;
175 data
->xft_draw
= XftDrawCreate (XtDisplay (data
->widget
),
177 DefaultVisual (XtDisplay (data
->widget
),
179 DefaultColormapOfScreen (sc
));
180 XftDrawRect (data
->xft_draw
,
181 inverse
? &data
->xft_fg
: &data
->xft_bg
,
182 0, 0, data
->p_width
, data
->p_height
);
184 if (!inverse
) y
+= 2;
185 while (bp
&& *bp
!= '\0')
187 char *cp
= strchr (bp
, '\n');
188 XftDrawStringUtf8 (data
->xft_draw
,
189 inverse
? &data
->xft_bg
: &data
->xft_fg
,
190 data
->xft_font
, x
, y
,
192 cp
? cp
- bp
: strlen (bp
));
193 bp
= cp
? cp
+ 1 : NULL
;
194 /* 1.2 gives reasonable line spacing. */
195 y
+= data
->xft_font
->height
* 1.2;
202 set_text (struct widget_xft_data
*data
, Widget toplevel
, char *lbl
, int margin
)
206 width
= get_text_width_and_height (data
->widget
, lbl
, data
->xft_font
,
208 data
->p_width
= width
+ margin
;
209 data
->p_height
= height
+ margin
;
211 data
->p
= XCreatePixmap (XtDisplay (data
->widget
),
215 DefaultDepthOfScreen (XtScreen (data
->widget
)));
216 draw_text (data
, lbl
, 0);
217 XtVaSetValues (data
->widget
, XtNbitmap
, data
->p
, NULL
);
220 static struct widget_xft_data
*
221 find_xft_data (Widget widget
)
223 widget_instance
*inst
= NULL
;
224 Widget parent
= XtParent (widget
);
225 struct widget_xft_data
*data
= NULL
;
227 while (parent
&& !inst
)
229 inst
= lw_get_widget_instance (parent
);
230 parent
= XtParent (parent
);
232 if (!inst
|| !inst
->xft_data
|| !inst
->xft_data
[0].xft_font
) return 0;
234 for (nr
= 0; data
== NULL
&& nr
< inst
->nr_xft_data
; ++nr
)
236 if (inst
->xft_data
[nr
].widget
== widget
)
237 data
= &inst
->xft_data
[nr
];
244 command_press (Widget widget
,
247 Cardinal
*num_params
)
249 struct widget_xft_data
*data
= find_xft_data (widget
);
253 /* Since this isn't used for rectangle buttons, use it to for armed. */
254 XtVaSetValues (widget
, XtNcornerRoundPercent
, 1, NULL
);
256 XtVaGetValues (widget
, XtNlabel
, &lbl
, NULL
);
257 draw_text (data
, lbl
, 1);
262 command_reset (Widget widget
,
265 Cardinal
*num_params
)
267 struct widget_xft_data
*data
= find_xft_data (widget
);
271 XtVaGetValues (widget
, XtNcornerRoundPercent
, &cr
, NULL
);
275 XtVaSetValues (widget
, XtNcornerRoundPercent
, 0, NULL
);
276 XtVaGetValues (widget
, XtNlabel
, &lbl
, NULL
);
277 draw_text (data
, lbl
, 0);
286 xaw_update_one_widget (widget_instance
*instance
,
291 if (XtIsSubclass (widget
, dialogWidgetClass
))
295 if (instance
->xft_data
&& instance
->xft_data
[0].xft_font
)
297 set_text (&instance
->xft_data
[0], instance
->parent
,
298 val
->contents
->value
, 10);
301 XtVaSetValues (widget
, XtNlabel
, val
->contents
->value
, NULL
);
303 else if (XtIsSubclass (widget
, commandWidgetClass
))
309 XtVaGetValues (widget
, XtNborderWidth
, &bw
, NULL
);
311 /* Don't let buttons end up with 0 borderwidth, that's ugly...
312 Yeah, all this should really be done through app-defaults files
313 or fallback resources, but that's a whole different can of worms
314 that I don't feel like opening right now. Making Athena widgets
315 not look like shit is just entirely too much work.
318 XtSetArg (al
[0], XtNborderWidth
, 1);
319 XtSetValues (widget
, al
, 1);
322 XtSetSensitive (widget
, val
->enabled
);
323 XtSetArg (al
[ac
], XtNlabel
, val
->value
);ac
++;
324 /* Force centered button text. Se above. */
325 XtSetArg (al
[ac
], XtNjustify
, XtJustifyCenter
);ac
++;
327 if (instance
->xft_data
&& instance
->xft_data
[0].xft_font
)
331 for (nr
= 0; nr
< instance
->nr_xft_data
; ++nr
)
332 if (instance
->xft_data
[nr
].widget
== widget
)
334 if (nr
< instance
->nr_xft_data
)
336 set_text (&instance
->xft_data
[nr
], instance
->parent
,
339 /* Must set internalHeight to twice the highlight thickness,
340 or else it gets overwritten by our pixmap. Probably a bug. */
341 XtVaGetValues (widget
, XtNhighlightThickness
, &th
, NULL
);
342 XtSetArg (al
[ac
], XtNinternalHeight
, 2*th
);ac
++;
346 XtSetValues (widget
, al
, ac
);
347 XtRemoveAllCallbacks (widget
, XtNcallback
);
348 XtAddCallback (widget
, XtNcallback
, xaw_generic_callback
, instance
);
353 xaw_update_one_value (widget_instance
*instance
,
357 /* This function is not used by the scrollbars and those are the only
358 Athena widget implemented at the moment so do nothing. */
363 xaw_destroy_instance (widget_instance
*instance
)
366 if (instance
->xft_data
)
369 for (i
= 0; i
< instance
->nr_xft_data
; ++i
)
371 if (instance
->xft_data
[i
].xft_draw
)
372 XftDrawDestroy (instance
->xft_data
[i
].xft_draw
);
373 if (instance
->xft_data
[i
].p
!= None
)
375 XtVaSetValues (instance
->xft_data
[i
].widget
, XtNbitmap
, None
,
377 XFreePixmap (XtDisplay (instance
->widget
),
378 instance
->xft_data
[i
].p
);
381 if (instance
->xft_data
[0].xft_font
)
382 XftFontClose (XtDisplay (instance
->widget
),
383 instance
->xft_data
[0].xft_font
);
384 xfree (instance
->xft_data
);
387 if (XtIsSubclass (instance
->widget
, dialogWidgetClass
))
388 /* Need to destroy the Shell too. */
389 XtDestroyWidget (XtParent (instance
->widget
));
391 XtDestroyWidget (instance
->widget
);
395 xaw_popup_menu (Widget widget
, XEvent
*event
)
397 /* An Athena menubar has not been implemented. */
402 xaw_pop_instance (widget_instance
*instance
, Boolean up
)
404 Widget widget
= instance
->widget
;
408 if (XtIsSubclass (widget
, dialogWidgetClass
))
410 /* For dialogs, we need to call XtPopup on the parent instead
411 of calling XtManageChild on the widget.
412 Also we need to hack the shell's WM_PROTOCOLS to get it to
413 understand what the close box is supposed to do!!
415 Display
*dpy
= XtDisplay (widget
);
416 Widget shell
= XtParent (widget
);
419 props
[i
++] = XInternAtom (dpy
, "WM_DELETE_WINDOW", False
);
420 XChangeProperty (dpy
, XtWindow (shell
),
421 XInternAtom (dpy
, "WM_PROTOCOLS", False
),
422 XA_ATOM
, 32, PropModeAppend
,
423 (unsigned char *) props
, i
);
425 /* Center the widget in its parent. Why isn't this kind of crap
426 done automatically? I thought toolkits were supposed to make
430 unsigned int x
, y
, w
, h
;
431 Widget topmost
= instance
->parent
;
434 w
= shell
->core
.width
;
435 h
= shell
->core
.height
;
436 while (topmost
->core
.parent
&& XtIsRealized (topmost
->core
.parent
))
437 topmost
= topmost
->core
.parent
;
438 if (topmost
->core
.width
< w
) x
= topmost
->core
.x
;
439 else x
= topmost
->core
.x
+ ((topmost
->core
.width
- w
) / 2);
440 if (topmost
->core
.height
< h
) y
= topmost
->core
.y
;
441 else y
= topmost
->core
.y
+ ((topmost
->core
.height
- h
) / 2);
442 /* Using XtMoveWidget caused the widget to come
443 out in the wrong place with vtwm.
444 Question of virtual vs real coords, perhaps. */
445 XtSetArg (args
[0], XtNx
, x
);
446 XtSetArg (args
[1], XtNy
, y
);
447 XtSetValues (shell
, args
, 2);
450 /* Finally, pop it up. */
451 XtPopup (shell
, XtGrabNonexclusive
);
454 XtManageChild (widget
);
458 if (XtIsSubclass (widget
, dialogWidgetClass
))
459 XtUnmanageChild (XtParent (widget
));
461 XtUnmanageChild (widget
);
468 static char overrideTrans
[] =
469 "<Message>WM_PROTOCOLS: lwlib_delete_dialog()";
470 /* Dialogs pop down on any key press */
471 static char dialogOverride
[] =
472 "<KeyPress>Escape: lwlib_delete_dialog()";
473 static void wm_delete_window (Widget w
,
476 Cardinal
*num_params
);
477 static XtActionsRec xaw_actions
[] = {
478 {"lwlib_delete_dialog", wm_delete_window
}
480 static Boolean actions_initted
= False
;
483 static XtActionsRec button_actions
[] =
485 { "my_reset", command_reset
},
486 { "my_press", command_press
},
489 "<Leave>: reset() my_reset()\n"
490 "<Btn1Down>: set() my_press()\n"
491 "<Btn1Up>: my_reset() notify() unset()\n";
495 make_dialog (char* name
,
500 Boolean text_input_slot
,
505 widget_instance
*instance
)
510 char button_name
[255];
514 XtTranslations override
;
516 XftFont
*xft_font
= 0;
517 XtTranslations button_override
;
520 if (! pop_up_p
) abort (); /* not implemented */
521 if (text_input_slot
) abort (); /* not implemented */
522 if (radio_box
) abort (); /* not implemented */
523 if (list
) abort (); /* not implemented */
525 if (! actions_initted
)
527 XtAppContext app
= XtWidgetToApplicationContext (parent
);
528 XtAppAddActions (app
, xaw_actions
,
529 sizeof (xaw_actions
) / sizeof (xaw_actions
[0]));
531 XtAppAddActions (app
, button_actions
,
532 sizeof (button_actions
) / sizeof (button_actions
[0]));
534 actions_initted
= True
;
537 override
= XtParseTranslationTable (overrideTrans
);
540 XtSetArg (av
[ac
], XtNtitle
, shell_title
); ac
++;
541 XtSetArg (av
[ac
], XtNallowShellResize
, True
); ac
++;
543 /* Don't allow any geometry request from the user. */
544 XtSetArg (av
[ac
], XtNgeometry
, 0); ac
++;
546 shell
= XtCreatePopupShell ("dialog", transientShellWidgetClass
,
548 XtOverrideTranslations (shell
, override
);
551 dialog
= XtCreateManagedWidget (name
, dialogWidgetClass
, shell
, av
, ac
);
552 override
= XtParseTranslationTable (dialogOverride
);
553 XtOverrideTranslations (dialog
, override
);
560 XtVaGetValues (dialog
,
561 XtNnumChildren
, &num
,
562 XtNchildren
, &ch
, NULL
);
563 for (i
= 0; i
< num
; ++i
)
565 if (!XtIsSubclass (ch
[i
], commandWidgetClass
)
566 && XtIsSubclass (ch
[i
], labelWidgetClass
))
572 instance
->xft_data
= 0;
573 instance
->nr_xft_data
= 0;
577 { { "font", "Font", XtRString
, sizeof(String
), 0, XtRString
,
578 (XtPointer
)"Sans-10" }};
579 char *fontName
= NULL
;
580 XtVaGetSubresources (dialog
, &fontName
, "Dialog", "dialog",
581 rec
, 1, (String
)NULL
);
584 XFontStruct
*xfn
= XLoadQueryFont (XtDisplay (dialog
), fontName
);
586 xft_font
= openFont (dialog
, fontName
);
588 XFreeFont (XtDisplay (dialog
), xfn
);
593 instance
->nr_xft_data
= left_buttons
+ right_buttons
+ 1;
594 instance
->xft_data
= calloc (instance
->nr_xft_data
,
595 sizeof(*instance
->xft_data
));
597 fill_xft_data (&instance
->xft_data
[0], w
, xft_font
);
601 button_override
= XtParseTranslationTable (buttonTrans
);
607 for (i
= 0; i
< left_buttons
; i
++)
610 XtSetArg (av
[ac
], XtNfromHoriz
, button
); ac
++;
611 XtSetArg (av
[ac
], XtNleft
, XtChainLeft
); ac
++;
612 XtSetArg (av
[ac
], XtNright
, XtChainLeft
); ac
++;
613 XtSetArg (av
[ac
], XtNtop
, XtChainBottom
); ac
++;
614 XtSetArg (av
[ac
], XtNbottom
, XtChainBottom
); ac
++;
615 XtSetArg (av
[ac
], XtNresizable
, True
); ac
++;
617 if (DefaultDepthOfScreen (XtScreen (dialog
)) >= 16)
619 /* Turn of dithered shadow if we can. Looks bad */
620 XtSetArg (av
[ac
], "beNiceToColormap", False
); ac
++;
623 sprintf (button_name
, "button%d", ++bc
);
624 button
= XtCreateManagedWidget (button_name
, commandWidgetClass
,
629 fill_xft_data (&instance
->xft_data
[bc
], button
, xft_font
);
630 XtOverrideTranslations (button
, button_override
);
635 for (i
= 0; i
< right_buttons
; i
++)
638 XtSetArg (av
[ac
], XtNfromHoriz
, button
); ac
++;
641 /* Separator to the other buttons. */
642 XtSetArg (av
[ac
], XtNhorizDistance
, 30); ac
++;
644 XtSetArg (av
[ac
], XtNleft
, XtChainRight
); ac
++;
645 XtSetArg (av
[ac
], XtNright
, XtChainRight
); ac
++;
646 XtSetArg (av
[ac
], XtNtop
, XtChainBottom
); ac
++;
647 XtSetArg (av
[ac
], XtNbottom
, XtChainBottom
); ac
++;
648 XtSetArg (av
[ac
], XtNresizable
, True
); ac
++;
650 if (DefaultDepthOfScreen (XtScreen (dialog
)) >= 16)
652 /* Turn of dithered shadow if we can. Looks bad */
653 XtSetArg (av
[ac
], "beNiceToColormap", False
); ac
++;
656 sprintf (button_name
, "button%d", ++bc
);
657 button
= XtCreateManagedWidget (button_name
, commandWidgetClass
,
662 fill_xft_data (&instance
->xft_data
[bc
], button
, xft_font
);
663 XtOverrideTranslations (button
, button_override
);
672 xaw_create_dialog (widget_instance
*instance
)
674 char *name
= instance
->info
->type
;
675 Widget parent
= instance
->parent
;
677 Boolean pop_up_p
= instance
->pop_up_p
;
678 char *shell_name
= 0;
680 Boolean text_input_slot
= False
;
681 Boolean radio_box
= False
;
682 Boolean list
= False
;
684 int left_buttons
= 0;
685 int right_buttons
= 1;
689 icon_name
= "dbox-error";
690 shell_name
= "Error";
694 icon_name
= "dbox-info";
695 shell_name
= "Information";
700 icon_name
= "dbox-question";
701 shell_name
= "Prompt";
705 text_input_slot
= True
;
706 icon_name
= "dbox-question";
707 shell_name
= "Prompt";
711 icon_name
= "dbox-question";
712 shell_name
= "Question";
716 total_buttons
= name
[1] - '0';
718 if (name
[3] == 'T' || name
[3] == 't')
720 text_input_slot
= False
;
724 right_buttons
= name
[4] - '0';
726 left_buttons
= total_buttons
- right_buttons
;
728 widget
= make_dialog (name
, parent
, pop_up_p
,
729 shell_name
, icon_name
, text_input_slot
, radio_box
,
730 list
, left_buttons
, right_buttons
, instance
);
736 xaw_generic_callback (Widget widget
, XtPointer closure
, XtPointer call_data
)
738 widget_instance
*instance
= (widget_instance
*) closure
;
739 Widget instance_widget
;
743 lw_internal_update_other_instances (widget
, closure
, call_data
);
747 if (widget
->core
.being_destroyed
)
750 instance_widget
= instance
->widget
;
751 if (!instance_widget
)
754 id
= instance
->info
->id
;
756 /* Damn! Athena doesn't give us a way to hang our own data on the
757 buttons, so we have to go find it... I guess this assumes that
758 all instances of a button have the same call data. */
760 widget_value
*val
= instance
->info
->val
->contents
;
761 char *name
= XtName (widget
);
764 if (val
->name
&& !strcmp (val
->name
, name
))
769 user_data
= val
->call_data
;
772 if (instance
->info
->selection_cb
)
773 instance
->info
->selection_cb (widget
, id
, user_data
);
777 wm_delete_window (Widget w
,
780 Cardinal
*num_params
)
786 Widget widget
= 0, shell
;
788 if (XtIsSubclass (w
, dialogWidgetClass
))
789 shell
= XtParent (w
);
793 if (! XtIsSubclass (shell
, shellWidgetClass
))
795 XtVaGetValues (shell
, XtNnumChildren
, &nkids
, NULL
);
796 XtVaGetValues (shell
, XtNchildren
, &kids
, NULL
);
799 for (i
= 0; i
< nkids
; i
++)
802 if (XtIsSubclass (widget
, dialogWidgetClass
))
805 if (! widget
) return;
807 id
= lw_get_widget_id (widget
);
811 widget_info
*info
= lw_get_widget_info (id
);
812 if (! info
) abort ();
813 if (info
->selection_cb
)
814 info
->selection_cb (widget
, id
, (XtPointer
) -1);
817 lw_destroy_all_widgets (id
);
823 xaw_create_main (widget_instance
*instance
)
828 /* Create a vertical Paned to hold menubar */
830 XtSetArg (al
[ac
], XtNborderWidth
, 0); ac
++;
831 return XtCreateWidget (instance
->info
->name
, panedWidgetClass
,
832 instance
->parent
, al
, ac
);
835 widget_creation_entry
836 xaw_creation_table
[] =
838 {"main", xaw_create_main
},