Software:
GWT
GWTOAuthLogin
X/Motif
ansi xterm
grabc
mdgclock
miv
mplaymidi
mppp
mxascii
mcmap
mxcmap
mxconsole
mxkill
mxshowfont
qtip
xmastm
yrolo
Web
mhttpd
web counter
upload.pl
TimeTrack.pl
mod_auth_ldap
Games
fltkmm
iphonemm
Java
cdcl
cdclgwt
jdgclock
Libraries
libcalen
libmcfg
libsll
libmsock
Misc
bangla font
dpr
genmake
hod
smtp.pl
vhtml
phones_ldap
showpic_ldap
mbasecalc
fluid_hack
kdialppp
strip2csv
googlecode-upload
MS Windows
mwinclip.pl
mbasecalc
mailsend
wiv
|
Balloon Help in Motif
Introduction
This article is about writing balloon help for X/Motif applications
under X Window System.
In many graphical applications you probably noticed, when the mouse
pointer is moved over a graphic button and if the pointer remains on the button
for a short period of time, a small rectangular window pops up with a one
line help message.
This kind of help is known as balloon help, bubble help or quick tip.
Quick tip is particularly helpful for push buttons with graphic bitmaps,
especially if the bitmaps are not very obvious.
If you are a X/Motif programmer, you may have thought about adding this
kind of help to your applications but was not quite sure how to do that.
In this article, I will describe how to write balloon help for X/Motif
applications.
A C routine Qtip() (stands for Quick tip) with source code,
documentation and examples is also supplied.
This routine can be used to add quick tip to any X/Motif application.
Writing balloon help
As mentioned in the introduction, balloon help
is useful for push buttons with graphic bitmaps. In order to display quick
tip, when a mouse pointer is moved over the widget, we want to wait for a
short amount of time, then pop-up the tip window. The pop-up delay is
important because it is not desirable to pop windows when someone just
tracks the mouse over the push buttons. So, how do we detect that the mouse
pointer is moved over a widget or it is moved out from a widget?
We can detect it by registering an X toolkit event handler for the widget.
Note we also want to make sure that the quick tip is displayed only if
the mouse pointer stays over the widget for a short period of time. We can
do this by registering an X toolkit timeout routine for the widget.
We will write the Qtip library in C. Let declare an internal data structure
with two members, an object of type Widget and a pointer to char:
typedef struct _QtipData
{
Widget widget; /* widget to bind help for */
char *text; /* help text, can be NULL */
} QtipData;
Here, text is the help text (can be NULL) and widget
is the push button or any other type of widget to bind the help for.
Let's write the Qtip() function now.
Note, you do not need to type the code described below, a library with
source code (commented) and documentation is supplied.
1 void Qtip(Widget widget,char *text)
2 {
3 QtipData *data=(QtipData *)
4 XtNew(QtipData);
5
6 if (text != (char *) NULL)
7 data->text=XtNewString(text);
8 else
9 data->text=(char *) NULL;
10
11 data->widget=widget;
12
13 /*
14 ** check if the widget is a subclass of Core Widget class
15 */
16 if (XtIsWidget(widget) == False)
17 {
18 return;
19 }
20
21 XtAddEventHandler(widget,
22 EnterWindowMask | LeaveWindowMask,
23 False,
24 (XtEventHandler) QtipCb,
25 (XtPointer) data);
26 }
At line 3, we allocate enough memory for the structure.
From line 6 to 11, we filled the members of the structure
QtipData .
At line 16, we make sure that the widget passed to the function is not a gadget.
If it is a gadget, the function will return silently.
We can not use gadgets, because among other limitations, gadgets can not
have event handlers.
At line 21, we registered an event handler for the widget. This event handler
detects when the mouse pointer is moved in and out of the widget.
At line 22, EnterWindowMask | LeaveWindowMask indicates that the
function QtipCb() (line 24) will be called when the mouse pointer
enters or leave the widget area.
Notice (XtPointer) data at line
25, here we are passing our own data structure to the event handler routine
QtipCb() .
If you look at the prototype of the X toolkit function
XtAddEventHandler() , you will notice that the 5th argument takes
a data type of XtPointer .
XtPointer is nothing but
a void * , therefore, any type of data can be passed to the
event handler routine through this argument.
Now let's look at the event handler routine QtipCb() .
1 static void QtipCb(Widget widget,XtPointer client_data,XEvent *event)
2 {
3 switch(event->type)
4 {
5 case EnterNotify:
6 {
7 if (!s_timerid)
8 {
9 timeout=1;
10 s_timerid=XtAppAddTimeOut(
11 XtWidgetToApplicationContext(widget),
12 QTIP_HELP_DELAY,
13 (XtTimerCallbackProc) PopQtip,
14 client_data);
15 }
16 break;
17 }
18
19 case LeaveNotify:
20 {
21 if (timeout == 1)
22 {
23 timeout=0;
24 XtRemoveTimeOut(s_timerid);
25 s_timerid=0L;
26 }
27 if (ShelpShellW != (Widget) NULL)
28 {
29 if (XtIsManaged(ShelpShellW))
30 XtUnmanageChild(ShelpShellW);
31 }
32 break;
33 }
34 }
35}
At line 3, we check for the type of events received.
When the mouse pointer enters the widget, the code segment from line 5-17
will be executed.
At line 10, we register a timeout routine,that is a routine will be called
after a certain time is elapsed.
At line 12, QTIP_HELP_DELAY indicates the delay in milli
seconds (it is defined in qtip.h as 500 milli seconds or half a second in
our case).
At line 13, PopQip is
the function which is called as soon as the timeout (500 ms in our
case) is reached. At line 14, we are again passing our own data structure to the
function PopQtip() .
The code segment from line 19-33 gets executed when the mouse pointer is moved
out of the widget. At line 24, if timeout is still not reached, we remove
the timeout. At line 29, if the tip window is up, we bring it down.
Now let us look at some segments of the function PopQtip() .
This function does all the work of creating the help window and placing
it properly.
1 static void PopQtip(XtPointer client_data)
2 {
3 static XmFontList
4 flist= NULL;
5
6 XmString
7
8 lab_str;
9
10 Dimension
11 lab_w,lab_h,w,h;
12
13 Position
14 rx,ry;
15
16 QtipData
17 *data;
18
19 data=(QtipData *) client_data;
20 s_timerid=0L;
21 timeout=0;
22
23 if (ShelpShellW != (Widget) NULL)
24 {
25 if (XtIsManaged(ShelpShellW))
26 XtUnmanageChild(ShelpShellW);
27 }
28
29 /*
30 ** first time
31 */
32 if (ShelpShellW == (Widget) NULL)
33 {
34 /*
35 ** create the shell, set the XmNoverrideRedirect to True, so that
36 ** the window manger won't intervene
37 */
38 ShelpShellW=XtVaCreatePopupShell("tipShell",
39 topLevelShellWidgetClass,GetTopShell(data->widget),
40 XmNoverrideRedirect, True,
41 XmNresizePolicy,XmRESIZE_ANY,
42 NULL);
43
44 /*
45 ** create the label
46 ** we must make the label widget static.
47 */
48 SlabelW=XtVaCreateManagedWidget("tipLabel",
49 xmLabelWidgetClass, ShelpShellW,
50 NULL);
51 }
52
53 if (flist == NULL)
54 {
55 XtVaGetValues(SlabelW,
56 XmNfontList,&flist,
57 NULL);
58 }
59
60 if (data->text != (char *) NULL)
61 {
62 lab_str=XmStringCreateSimple(data->text);
63 }
64 else
65 XtVaGetValues(data->widget,XmNlabelString,&lab_str,NULL);
66
67
68 if (flist)
69 {
70 lab_w=XmStringWidth(flist,lab_str)+6;
71 lab_h=XmStringHeight(flist,lab_str)+6;
72 }
73
74 /*
75 ** determine widgets width & height
76 */
77 XtVaGetValues(data->widget,
78 XmNwidth,&w,
79 XmNheight,&h,
80 NULL);
81
82 /*
83 ** we will pop the qtip window 2 pixels below the widget's edge and
84 ** halfway from the widget's left edge. So, we will translate these
85 ** coordinate from widget coordinates to root window coordinates
86 */
87 XtTranslateCoords(data->widget,
88 (Position) (w/2),
89 (Position) (h+2),
90 &rx,&ry);
91
92 XtVaSetValues(ShelpShellW,
93 XmNx,rx,
94 XmNy,ry,
95 XmNwidth,lab_w,
96 XmNheight,lab_h,
97 NULL);
98 XtVaSetValues(SlabelW,
99 XmNlabelString,lab_str,
100 NULL);
101
102 /*
103 ** don't need the string, free it
104 */
105 XmStringFree(lab_str);
106
107 if (ShelpShellW != (Widget) NULL)
108 XtManageChild(ShelpShellW);
109 }
At line 38, we create a top level shell.
Please look carefully at line 40, there,
we set the resource XmNoverrideRedirect to True .
This resource instructs the window manager not to intervene.
If we do not set this resource to True , the window manager
will put the title bar and frame around this shell (the whole process is
called reparenting).
At line 48, we create a label widget on the shell widget.
At line 60, we check if a help text is passed, if so, create a motif compound
string out of it (line 62).
If no help text is passed, we use the push button's label text as the help
message (line 65).
At line 70 and 71, we determine the width and height of the help message.
Note the variable flist , it is the XmFontList
of the label widget we created at line 48.
Therefore, the font used in the label will be used for the help message as
well. We will talk later how to specify a font for the label via a X
resource.
|
Figure 1: Widget coordinates
|
At line 78 and 79, we obtain the width (w) and height (h) of the push
button widget respectively.
Please refer to Figure 1. In Figure 1, the window with message
Open File is a tip window.
I decided to position the tip window at half way (w/2) from widget's left
edget and 2 pixels below (h+2) the widget's bottom edge.
At line 87, the Xt function XtTranslateCoords() is called to
translate the widget coordinate (w/2,h+2) to root coordinate (rx,ry).
We will use (rx,ry) to position the tip window.
At line 92, we call XtVaSetValue() to place the tip shell at the
coordinate (rx,ry) and set the width and height of the tip shell as well.
At line 98, we set the help text to the label widget. If you remember, we
created the label on top of the tip shell.
At line 105, we free the help text (compound string), because motif library
already made a copy of it when we set the text to the label at line 98.
If we do not free it, it will create a "memory leak".
And finally we manage the tip shell widget at line 108, so that it shows up on
our screen.
Qtip library
All the code described above is used in the Qtip library. It is written in
in C and interfaces to Motif library. You will need a C compiler (ANSI or
non-ANSI), X11 and at least Motif 1.1 in order to compile. The library has
only one function call Qtip() . You can use the C file
qtip.c directly but I think it is cleaner to use it as a
library. You must include the header file qtip.h in your code
before calling the function Qtip() . The synopsis of the routine
is shown below:
#include <qtip.h>
void Qtip(Widget widget,char *text)
where,
widget specifies the widget to bind help for
text specifies the quick tip text (can be NULL)
If text is passed as NULL, the library will try to use the
widget's label text as help message. If help text is passed to the function,
it can not be changed, therefore, it is better to pass NULL. This way, the
help message can be changed any time just by changing the X resources.
Obtaining source
* Download
Source
|
File:
|
qtip1.1.tar.gz
|
Size:
|
27349 bytes
|
MD5 Checksum:
|
995d54367de5f0c257dd18883490f02c
|
Last updated:
|
?
|
qtip1.1.tar.gz is gzip compressed tar file.
If you do not have gzip, it is available at
ftp://prep.ai.mit.edu/pub/gnu/gzip-1.2.4.tar
Steps to compile
After down-loading the archive, at the shell prompt, type:
guzip < qtip1.1.tar.gz | tar xvf -
To compile the library, at the shell prompt, type:
cd qtip1.1
xmkmf
make
To compile the demo program, type:
cd demo
xmkmf
make
To run the demo program, type:
./qtip_demo
|
Figure 2: Screen shot of qtip_demo
|
Example
Here is an example using the Qtip library. It creates one push button,
sets pixmap to it and adds the balloon help.
/*
** an example of using Qtip() function call to add balloon help
** to compile:
** cc qtip_simple.c -o qtip_simple -I.. -L.. -lQtip -lXm -lXt -lX11
*/
#include <qtip.h>
/* bitmap for push button */
#define open_width 20
#define open_height 20
static char open_bits[] = {
0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x30, 0x0a, 0xfc, 0x00, 0x0a,
0xfc, 0x10, 0x0c, 0xff, 0x7f, 0x0f, 0xff, 0x7f, 0x00, 0xff, 0x7f, 0x00,
0xff, 0x7f, 0x00, 0xff, 0xff, 0x0f, 0xbf, 0xaa, 0x0a, 0x3f, 0x55, 0x05,
0xdf, 0xaa, 0x06, 0x4f, 0x55, 0x03, 0xaf, 0xaa, 0x02, 0xa7, 0xaa, 0x01,
0x57, 0x55, 0x01, 0xab, 0xaa, 0x00, 0x55, 0xd5, 0x00, 0xff, 0x7f, 0x00};
/* fallback resources */
static char *app_defaults[]=
{
"Foo*background: gray",
"Foo*keyboardFocusPolicy: pointer",
"Foo*tipLabel*fontList: -*-helvetica-medium-r-*-*-*-120-*-*-*-*-iso8859-*",
"Foo*tipShell.borderWidth: 1",
"Foo*tipShell.borderColor: red",
"Foo*tipLabel.background: #FFFFCC",
"Foo*tipLabel.foreground: black",
NULL
};
int main(int argc,char **argv)
{
Widget
toplevelW,
pushbW;
XtAppContext
app;
Pixmap
pixmap;
Display
*display;
unsigned int
depth;
int
screen;
Window
rootw;
Pixel
bg,
fg;
/*
** create toplevel shell, note the class of the app is Foo
*/
toplevelW=XtVaAppInitialize(&app,"Foo",
NULL,0,&argc,argv,app_defaults,NULL);
display=XtDisplay(toplevelW);
rootw=XDefaultRootWindow(display);
screen=XDefaultScreen(display);
depth=XDefaultDepth(display,screen);
/* create the push button widget (not gadget) */
pushbW=XtVaCreateManagedWidget("Open File",
xmPushButtonWidgetClass,toplevelW,
NULL);
/* get the background and foreground color */
XtVaGetValues(pushbW,
XmNforeground,&fg,
XmNbackground,&bg,
NULL);
/* create pixmap from X11 bitmap */
pixmap=XCreatePixmapFromBitmapData(display,rootw,
open_bits,
open_width,
open_height,
(unsigned long) fg,
(unsigned long) bg,
depth);
/* set the pixmap to the button */
if (pixmap != (Pixmap) NULL)
{
XtVaSetValues(pushbW,
XmNlabelType,XmPIXMAP,
XmNlabelPixmap,pixmap,
NULL);
}
/*
** now call Qtip() to bind balloon help.
** Note, we'r passing the text argument as NULL, so the
** function will use the label text of the button as
** help message
*/
Qtip(pushbW, (char *) NULL);
XtManageChild(pushbW);
XtRealizeWidget(toplevelW);
XtAppMainLoop(app);
return (0); /* won't be here*/
}
X Resources
The appearance of the quick tip window can be controlled via several X
resources. If no X resource is set, the appearance will be inherited from
the application's top level shell and XmLabel.
Here is an example of an X resources file of an application whose class name
is Foo.
Foo*tipLabel*fontList: -*-helvetica-medium-r-*-*-*-120-*-*-*-*-iso8859-*
Foo*tipShell.borderWidth: 1
Foo*tipShell.borderColor: red
Foo*tipLabel.background: #FFFFCC
Foo*tipLabel.foreground: black
License - BSD
URL of this page: http://www.muquit.com/muquit/software/qtip/qtip.html
|