upload tizen2.0 source
[framework/uifw/xorg/lib/libxaw.git] / src / Tip.c
1 /*
2  * Copyright (c) 1999 by The XFree86 Project, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19  * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  *
22  * Except as contained in this notice, the name of the XFree86 Project shall
23  * not be used in advertising or otherwise to promote the sale, use or other
24  * dealings in this Software without prior written authorization from the
25  * XFree86 Project.
26  *
27  * Author: Paulo César Pereira de Andrade
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33 #include <X11/IntrinsicP.h>
34 #include <X11/StringDefs.h>
35 #include <X11/Xos.h>
36 #include <X11/Xaw/TipP.h>
37 #include <X11/Xaw/XawInit.h>
38 #include <X11/Xmu/Converters.h>
39 #include "Private.h"
40
41 #define TIP_EVENT_MASK (ButtonPressMask   |     \
42                         ButtonReleaseMask |     \
43                         PointerMotionMask |     \
44                         ButtonMotionMask  |     \
45                         KeyPressMask      |     \
46                         KeyReleaseMask    |     \
47                         EnterWindowMask   |     \
48                         LeaveWindowMask)
49
50 /*
51  * Types
52  */
53 typedef struct _XawTipInfo {
54     Screen *screen;
55     TipWidget tip;
56     Widget widget;
57     Bool mapped;
58     struct _XawTipInfo *next;
59 } XawTipInfo;
60
61 /*
62  * Class Methods
63  */
64 static void XawTipClassInitialize(void);
65 static void XawTipInitialize(Widget, Widget, ArgList, Cardinal*);
66 static void XawTipDestroy(Widget);
67 static void XawTipExpose(Widget, XEvent*, Region);
68 static void XawTipRealize(Widget, Mask*, XSetWindowAttributes*);
69 static Boolean XawTipSetValues(Widget, Widget, Widget, ArgList, Cardinal*);
70
71 /*
72  * Prototypes
73  */
74 static void TipEventHandler(Widget, XtPointer, XEvent*, Boolean*);
75 static void TipShellEventHandler(Widget, XtPointer, XEvent*, Boolean*);
76 static XawTipInfo *CreateTipInfo(Widget);
77 static XawTipInfo *FindTipInfo(Widget);
78 static void ResetTip(XawTipInfo*, Bool);
79 static void TipTimeoutCallback(XtPointer, XtIntervalId*);
80 static void TipLayout(XawTipInfo*);
81 static void TipPosition(XawTipInfo*);
82
83 /*
84  * Initialization
85  */
86 #define offset(field) XtOffsetOf(TipRec, tip.field)
87 static XtResource resources[] = {
88   {
89     XtNforeground,
90     XtCForeground,
91     XtRPixel,
92     sizeof(Pixel),
93     offset(foreground),
94     XtRString,
95     XtDefaultForeground,
96   },
97   {
98     XtNfont,
99     XtCFont,
100     XtRFontStruct,
101     sizeof(XFontStruct*),
102     offset(font),
103     XtRString,
104     XtDefaultFont
105   },
106   {
107     XtNfontSet,
108     XtCFontSet,
109     XtRFontSet,
110     sizeof(XFontSet),
111     offset(fontset),
112     XtRString,
113     XtDefaultFontSet
114   },
115   {
116     XtNtopMargin,
117     XtCVerticalMargins,
118     XtRDimension,
119     sizeof(Dimension),
120     offset(top_margin),
121     XtRImmediate,
122     (XtPointer)2
123   },
124   {
125     XtNbottomMargin,
126     XtCVerticalMargins,
127     XtRDimension,
128     sizeof(Dimension),
129     offset(bottom_margin),
130     XtRImmediate,
131     (XtPointer)2
132   },
133   {
134     XtNleftMargin,
135     XtCHorizontalMargins,
136     XtRDimension,
137     sizeof(Dimension),
138     offset(left_margin),
139     XtRImmediate,
140     (XtPointer)6
141   },
142   {
143     XtNrightMargin,
144     XtCHorizontalMargins,
145     XtRDimension,
146     sizeof(Dimension),
147     offset(right_margin),
148     XtRImmediate,
149     (XtPointer)6
150   },
151   {
152     XtNbackingStore,
153     XtCBackingStore,
154     XtRBackingStore,
155     sizeof(int),
156     offset(backing_store),
157     XtRImmediate,
158     (XtPointer)(Always + WhenMapped + NotUseful)
159   },
160   {
161     XtNtimeout,
162     XtCTimeout,
163     XtRInt,
164     sizeof(int),
165     offset(timeout),
166     XtRImmediate,
167     (XtPointer)500
168   },
169   {
170     XawNdisplayList,
171     XawCDisplayList,
172     XawRDisplayList,
173     sizeof(XawDisplayList*),
174     offset(display_list),
175     XtRImmediate,
176     NULL
177   },
178 };
179 #undef offset
180
181 TipClassRec tipClassRec = {
182   /* core */
183   {
184     (WidgetClass)&widgetClassRec,       /* superclass */
185     "Tip",                              /* class_name */
186     sizeof(TipRec),                     /* widget_size */
187     XawTipClassInitialize,              /* class_initialize */
188     NULL,                               /* class_part_initialize */
189     False,                              /* class_inited */
190     XawTipInitialize,                   /* initialize */
191     NULL,                               /* initialize_hook */
192     XawTipRealize,                      /* realize */
193     NULL,                               /* actions */
194     0,                                  /* num_actions */
195     resources,                          /* resources */
196     XtNumber(resources),                /* num_resources */
197     NULLQUARK,                          /* xrm_class */
198     True,                               /* compress_motion */
199     True,                               /* compress_exposure */
200     True,                               /* compress_enterleave */
201     False,                              /* visible_interest */
202     XawTipDestroy,                      /* destroy */
203     NULL,                               /* resize */
204     XawTipExpose,                       /* expose */
205     XawTipSetValues,                    /* set_values */
206     NULL,                               /* set_values_hook */
207     XtInheritSetValuesAlmost,           /* set_values_almost */
208     NULL,                               /* get_values_hook */
209     NULL,                               /* accept_focus */
210     XtVersion,                          /* version */
211     NULL,                               /* callback_private */
212     NULL,                               /* tm_table */
213     XtInheritQueryGeometry,             /* query_geometry */
214     XtInheritDisplayAccelerator,        /* display_accelerator */
215     NULL,                               /* extension */
216   },
217   /* tip */
218   {
219     NULL,                               /* extension */
220   },
221 };
222
223 WidgetClass tipWidgetClass = (WidgetClass)&tipClassRec;
224
225 static XawTipInfo *first_tip;
226
227 /*
228  * Implementation
229  */
230 static void
231 XawTipClassInitialize(void)
232 {
233     XawInitializeWidgetSet();
234     XtAddConverter(XtRString, XtRBackingStore, XmuCvtStringToBackingStore,
235                    NULL, 0);
236     XtSetTypeConverter(XtRBackingStore, XtRString, XmuCvtBackingStoreToString,
237                        NULL, 0, XtCacheNone, NULL);
238 }
239
240 /*ARGSUSED*/
241 static void
242 XawTipInitialize(Widget req, Widget w, ArgList args, Cardinal *num_args)
243 {
244     TipWidget tip = (TipWidget)w;
245     XGCValues values;
246
247     if (!tip->tip.font) XtError("Aborting: no font found\n");
248     if (tip->tip.international && !tip->tip.fontset)
249         XtError("Aborting: no fontset found\n");
250
251     tip->tip.timer = 0;
252
253     values.foreground = tip->tip.foreground;
254     values.background = tip->core.background_pixel;
255     values.font = tip->tip.font->fid;
256     values.graphics_exposures = False;
257
258     tip->tip.gc = XtAllocateGC(w, 0, GCForeground | GCBackground | GCFont |
259                                GCGraphicsExposures, &values, GCFont, 0);
260 }
261
262 static void
263 XawTipDestroy(Widget w)
264 {
265     XawTipInfo *info = FindTipInfo(w);
266     TipWidget tip = (TipWidget)w;
267
268     if (tip->tip.timer)
269         XtRemoveTimeOut(tip->tip.timer);
270
271     XtReleaseGC(w, tip->tip.gc);
272
273     XtRemoveEventHandler(XtParent(w), KeyPressMask, False, TipShellEventHandler,
274                          (XtPointer)NULL);
275     if (info == first_tip)
276         first_tip = first_tip->next;
277     else {
278         XawTipInfo *p = first_tip;
279
280         while (p && p->next != info)
281             p = p->next;
282         if (p)
283             p->next = info->next;
284     }
285     XtFree((char*)info);
286 }
287
288 static void
289 XawTipRealize(Widget w, Mask *mask, XSetWindowAttributes *attr)
290 {
291     TipWidget tip = (TipWidget)w;
292
293     if (tip->tip.backing_store == Always ||
294         tip->tip.backing_store == NotUseful ||
295         tip->tip.backing_store == WhenMapped) {
296         *mask |= CWBackingStore;
297         attr->backing_store = tip->tip.backing_store;
298     }
299     else
300         *mask &= ~CWBackingStore;
301     *mask |= CWOverrideRedirect;
302     attr->override_redirect = True;
303
304     XtWindow(w) = XCreateWindow(DisplayOfScreen(XtScreen(w)),
305                                 RootWindowOfScreen(XtScreen(w)),
306                                 XtX(w), XtY(w),
307                                 XtWidth(w) ? XtWidth(w) : 1,
308                                 XtHeight(w) ? XtHeight(w) : 1,
309                                 XtBorderWidth(w),
310                                 DefaultDepthOfScreen(XtScreen(w)),
311                                 InputOutput,
312                                 (Visual *)CopyFromParent,
313                                 *mask, attr);
314 }
315
316 static void
317 XawTipExpose(Widget w, XEvent *event, Region region)
318 {
319     TipWidget tip = (TipWidget)w;
320     GC gc = tip->tip.gc;
321     char *nl, *label = tip->tip.label;
322     Position y = tip->tip.top_margin + tip->tip.font->max_bounds.ascent;
323     int len;
324
325     if (tip->tip.display_list)
326         XawRunDisplayList(w, tip->tip.display_list, event, region);
327
328     if (tip->tip.international == True) {
329         Position ksy = tip->tip.top_margin;
330         XFontSetExtents *ext = XExtentsOfFontSet(tip->tip.fontset);
331
332         ksy += XawAbs(ext->max_ink_extent.y);
333
334         while ((nl = index(label, '\n')) != NULL) {
335             XmbDrawString(XtDisplay(w), XtWindow(w), tip->tip.fontset,
336                           gc, tip->tip.left_margin, ksy, label,
337                           (int)(nl - label));
338             ksy += ext->max_ink_extent.height;
339             label = nl + 1;
340         }
341         len = strlen(label);
342         if (len)
343             XmbDrawString(XtDisplay(w), XtWindow(w), tip->tip.fontset, gc,
344                           tip->tip.left_margin, ksy, label, len);
345     }
346     else {
347         while ((nl = index(label, '\n')) != NULL) {
348             if (tip->tip.encoding)
349                 XDrawString16(XtDisplay(w), XtWindow(w), gc,
350                               tip->tip.left_margin, y,
351                               (XChar2b*)label, (int)(nl - label) >> 1);
352             else
353                 XDrawString(XtDisplay(w), XtWindow(w), gc,
354                             tip->tip.left_margin, y, label, (int)(nl - label));
355             y += tip->tip.font->max_bounds.ascent +
356                  tip->tip.font->max_bounds.descent;
357             label = nl + 1;
358         }
359         len = strlen(label);
360         if (len) {
361             if (tip->tip.encoding)
362                 XDrawString16(XtDisplay(w), XtWindow(w), gc,
363                               tip->tip.left_margin, y, (XChar2b*)label, len >> 1);
364             else
365                 XDrawString(XtDisplay(w), XtWindow(w), gc,
366                             tip->tip.left_margin, y, label, len);
367         }
368     }
369 }
370
371 /*ARGSUSED*/
372 static Boolean
373 XawTipSetValues(Widget current, Widget request, Widget cnew,
374                 ArgList args, Cardinal *num_args)
375 {
376     TipWidget curtip = (TipWidget)current;
377     TipWidget newtip = (TipWidget)cnew;
378     Boolean redisplay = False;
379
380     if (curtip->tip.font->fid != newtip->tip.font->fid ||
381         curtip->tip.foreground != newtip->tip.foreground) {
382         XGCValues values;
383
384         values.foreground = newtip->tip.foreground;
385         values.background = newtip->core.background_pixel;
386         values.font = newtip->tip.font->fid;
387         values.graphics_exposures = False;
388         XtReleaseGC(cnew, curtip->tip.gc);
389         newtip->tip.gc = XtAllocateGC(cnew, 0, GCForeground | GCBackground |
390                                       GCFont | GCGraphicsExposures, &values,
391                                       GCFont, 0);
392         redisplay = True;
393     }
394     if (curtip->tip.display_list != newtip->tip.display_list)
395         redisplay = True;
396
397     return (redisplay);
398 }
399
400 static void
401 TipLayout(XawTipInfo *info)
402 {
403     XFontStruct *fs = info->tip->tip.font;
404     int width = 0, height;
405     char *nl, *label = info->tip->tip.label;
406
407     if (info->tip->tip.international == True) {
408         XFontSet fset = info->tip->tip.fontset;
409         XFontSetExtents *ext = XExtentsOfFontSet(fset);
410
411         height = ext->max_ink_extent.height;
412         if ((nl = index(label, '\n')) != NULL) {
413             /*CONSTCOND*/
414             while (True) {
415                 int w = XmbTextEscapement(fset, label, (int)(nl - label));
416
417                 if (w > width)
418                     width = w;
419                 if (*nl == '\0')
420                     break;
421                 label = nl + 1;
422                 if (*label)
423                     height += ext->max_ink_extent.height;
424                 if ((nl = index(label, '\n')) == NULL)
425                     nl = index(label, '\0');
426             }
427         }
428         else
429             width = XmbTextEscapement(fset, label, strlen(label));
430     }
431     else {
432         height = fs->max_bounds.ascent + fs->max_bounds.descent;
433         if ((nl = index(label, '\n')) != NULL) {
434             /*CONSTCOND*/
435             while (True) {
436                 int w = info->tip->tip.encoding ?
437                     XTextWidth16(fs, (XChar2b*)label, (int)(nl - label) >> 1) :
438                     XTextWidth(fs, label, (int)(nl - label));
439                 if (w > width)
440                     width = w;
441                 if (*nl == '\0')
442                     break;
443                 label = nl + 1;
444                 if (*label)
445                     height += fs->max_bounds.ascent + fs->max_bounds.descent;
446                 if ((nl = index(label, '\n')) == NULL)
447                     nl = index(label, '\0');
448             }
449         }
450         else
451             width = info->tip->tip.encoding ?
452                 XTextWidth16(fs, (XChar2b*)label, strlen(label) >> 1) :
453                 XTextWidth(fs, label, strlen(label));
454     }
455     XtWidth(info->tip) = width + info->tip->tip.left_margin +
456                          info->tip->tip.right_margin;
457     XtHeight(info->tip) = height + info->tip->tip.top_margin +
458                           info->tip->tip.bottom_margin;
459 }
460
461 #define DEFAULT_TIP_Y_OFFSET    12
462 static void
463 TipPosition(XawTipInfo *info)
464 {
465     Window r, c;
466     int rx, ry, wx, wy;
467     unsigned mask;
468     Position x, y;
469
470     XQueryPointer(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip),
471                   &r, &c, &rx, &ry, &wx, &wy, &mask);
472     x = rx - (XtWidth(info->tip) >> 1);
473     y = ry + DEFAULT_TIP_Y_OFFSET;
474
475     if (x >= 0) {
476         int scr_width = WidthOfScreen(XtScreen(info->tip));
477
478         if (x + XtWidth(info->tip) + XtBorderWidth(info->tip) > scr_width)
479             x = scr_width - XtWidth(info->tip) - XtBorderWidth(info->tip);
480     }
481     if (x < 0)
482         x = 0;
483     if (y >= 0) {
484         int scr_height = HeightOfScreen(XtScreen(info->tip));
485
486         if (y + XtHeight(info->tip) + XtBorderWidth(info->tip) > scr_height)
487             y -= XtHeight(info->tip) + XtBorderWidth(info->tip) +
488                  (DEFAULT_TIP_Y_OFFSET << 1);
489     }
490     if (y < 0)
491         y = 0;
492
493     XMoveResizeWindow(XtDisplay(info->tip), XtWindow(info->tip),
494                       (int)(XtX(info->tip) = x), (int)(XtY(info->tip) = y),
495                       (unsigned)XtWidth(info->tip), (unsigned)XtHeight(info->tip));
496 }
497
498 static XawTipInfo *
499 CreateTipInfo(Widget w)
500 {
501     XawTipInfo *info = XtNew(XawTipInfo);
502     Widget shell = w;
503
504     info->screen = XtScreen(w);
505
506     while (XtParent(shell))
507         shell = XtParent(shell);
508
509     info->tip = (TipWidget)XtCreateWidget("tip", tipWidgetClass, shell, NULL, 0);
510     XtRealizeWidget((Widget)info->tip);
511     info->widget = NULL;
512     info->mapped = False;
513     info->next = NULL;
514     XtAddEventHandler(shell, KeyPressMask, False, TipShellEventHandler,
515                       (XtPointer)NULL);
516
517     return (info);
518 }
519
520 static XawTipInfo *
521 FindTipInfo(Widget w)
522 {
523     XawTipInfo *ptip, *tip = first_tip;
524     Screen *screen = XtScreenOfObject(w);
525
526     if (tip == NULL)
527         return (first_tip = tip = CreateTipInfo(w));
528
529     for (ptip = tip; tip; ptip = tip, tip = tip->next)
530         if (tip->screen == screen)
531             return (tip);
532
533     return (ptip->next = CreateTipInfo(w));
534 }
535
536 static void
537 ResetTip(XawTipInfo *info, Bool add_timeout)
538 {
539     if (info->tip->tip.timer) {
540         XtRemoveTimeOut(info->tip->tip.timer);
541         info->tip->tip.timer = 0;
542     }
543     if (info->mapped) {
544         XtRemoveGrab(XtParent((Widget)info->tip));
545         XUnmapWindow(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip));
546         info->mapped = False;
547     }
548     if (add_timeout) {
549         info->tip->tip.timer =
550             XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)info->tip),
551                             info->tip->tip.timeout, TipTimeoutCallback,
552                             (XtPointer)info);
553     }
554 }
555
556 static void
557 TipTimeoutCallback(XtPointer closure, XtIntervalId *id)
558 {
559     XawTipInfo *info = (XawTipInfo*)closure;
560     Arg args[3];
561
562     info->tip->tip.label = NULL;
563     info->tip->tip.international = False;
564     info->tip->tip.encoding = 0;
565     info->tip->tip.timer = 0;
566     XtSetArg(args[0], XtNtip, &info->tip->tip.label);
567     XtSetArg(args[1], XtNinternational, &info->tip->tip.international);
568     XtSetArg(args[2], XtNencoding, &info->tip->tip.encoding);
569     XtGetValues(info->widget, args, 3);
570
571     if (info->tip->tip.label) {
572         TipLayout(info);
573         TipPosition(info);
574         XMapRaised(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip));
575         XtAddGrab(XtParent((Widget)info->tip), True, True);
576         info->mapped = True;
577     }
578 }
579
580 /*ARGSUSED*/
581 static void
582 TipShellEventHandler(Widget w, XtPointer client_data, XEvent *event,
583                      Boolean *continue_to_dispatch)
584 {
585     ResetTip(FindTipInfo(w), False);
586 }
587
588 /*ARGSUSED*/
589 static void
590 TipEventHandler(Widget w, XtPointer client_data, XEvent *event,
591                 Boolean *continue_to_dispatch)
592 {
593     XawTipInfo *info = FindTipInfo(w);
594     Boolean add_timeout;
595
596     if (info->widget != w) {
597         ResetTip(info, False);
598         info->widget = w;
599     }
600
601     switch (event->type) {
602         case EnterNotify:
603             add_timeout = True;
604             break;
605         case MotionNotify:
606             /* If any button is pressed, timer is 0 */
607             if (info->mapped)
608                 return;
609             add_timeout = info->tip->tip.timer != 0;
610             break;
611         default:
612             add_timeout = False;
613             break;
614     }
615     ResetTip(info, add_timeout);
616 }
617
618 /*
619  * Public routines
620  */
621 void
622 XawTipEnable(Widget w)
623 {
624     XtAddEventHandler(w, TIP_EVENT_MASK, False, TipEventHandler,
625                       (XtPointer)NULL);
626 }
627
628 void
629 XawTipDisable(Widget w)
630 {
631     XawTipInfo *info = FindTipInfo(w);
632
633     XtRemoveEventHandler(w, TIP_EVENT_MASK, False, TipEventHandler,
634                          (XtPointer)NULL);
635     if (info->widget == w)
636         ResetTip(info, False);
637 }