Edit

IABSD.fr/xenocara/lib/libXaw/src/Tip.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2006-11-25 16:53:47
    Hash : 59f6d906
    Message : import from X.Org 7.2RC2

  • lib/libXaw/src/Tip.c
  • /*
     * Copyright (c) 1999 by The XFree86 Project, Inc.
     *
     * Permission is hereby granted, free of charge, to any person obtaining a
     * copy of this software and associated documentation files (the "Software"),
     * to deal in the Software without restriction, including without limitation
     * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     * and/or sell copies of the Software, and to permit persons to whom the
     * Software is furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
     * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     * SOFTWARE.
     *
     * Except as contained in this notice, the name of the XFree86 Project shall
     * not be used in advertising or otherwise to promote the sale, use or other
     * dealings in this Software without prior written authorization from the
     * XFree86 Project.
     *
     * Author: Paulo César Pereira de Andrade
     */
    
    /* $XFree86: xc/lib/Xaw/Tip.c,v 1.4 1999/07/11 08:49:16 dawes Exp $ */
    
    #ifdef HAVE_CONFIG_H
    #include <config.h>
    #endif
    #include <X11/IntrinsicP.h>
    #include <X11/StringDefs.h>
    #include <X11/Xos.h>
    #include <X11/Xaw/TipP.h>
    #include <X11/Xaw/XawInit.h>
    #include <X11/Xmu/Converters.h>
    #include "Private.h"
    
    #define	TIP_EVENT_MASK (ButtonPressMask	  |	\
    			ButtonReleaseMask |	\
    			PointerMotionMask |	\
    			ButtonMotionMask  |	\
    			KeyPressMask	  |	\
    			KeyReleaseMask	  |	\
    			EnterWindowMask	  |	\
    			LeaveWindowMask)
    
    /*
     * Types
     */
    typedef struct _XawTipInfo {
        Screen *screen;
        TipWidget tip;
        Widget widget;
        Bool mapped;
        struct _XawTipInfo *next;
    } XawTipInfo;
    
    /*
     * Class Methods
     */
    static void XawTipClassInitialize(void);
    static void XawTipInitialize(Widget, Widget, ArgList, Cardinal*);
    static void XawTipDestroy(Widget);
    static void XawTipExpose(Widget, XEvent*, Region);
    static void XawTipRealize(Widget, Mask*, XSetWindowAttributes*);
    static Boolean XawTipSetValues(Widget, Widget, Widget, ArgList, Cardinal*);
    
    /*
     * Prototypes
     */
    static void TipEventHandler(Widget, XtPointer, XEvent*, Boolean*);
    static void TipShellEventHandler(Widget, XtPointer, XEvent*, Boolean*);
    static XawTipInfo *CreateTipInfo(Widget);
    static XawTipInfo *FindTipInfo(Widget);
    static void ResetTip(XawTipInfo*, Bool);
    static void TipTimeoutCallback(XtPointer, XtIntervalId*);
    static void TipLayout(XawTipInfo*);
    static void TipPosition(XawTipInfo*);
    
    /*
     * Initialization
     */
    #define offset(field) XtOffsetOf(TipRec, tip.field)
    static XtResource resources[] = {
      {
        XtNforeground,
        XtCForeground,
        XtRPixel,
        sizeof(Pixel),
        offset(foreground),
        XtRString,
        XtDefaultForeground,
      },
      {
        XtNfont,
        XtCFont,
        XtRFontStruct,
        sizeof(XFontStruct*),
        offset(font),
        XtRString,
        XtDefaultFont
      },
      {
        XtNfontSet,
        XtCFontSet,
        XtRFontSet,
        sizeof(XFontSet),
        offset(fontset),
        XtRString,
        XtDefaultFontSet
      },
      {
        XtNtopMargin,
        XtCVerticalMargins,
        XtRDimension,
        sizeof(Dimension),
        offset(top_margin),
        XtRImmediate,
        (XtPointer)2
      },
      {
        XtNbottomMargin,
        XtCVerticalMargins,
        XtRDimension,
        sizeof(Dimension),
        offset(bottom_margin),
        XtRImmediate,
        (XtPointer)2
      },
      {
        XtNleftMargin,
        XtCHorizontalMargins,
        XtRDimension,
        sizeof(Dimension),
        offset(left_margin),
        XtRImmediate,
        (XtPointer)6
      },
      {
        XtNrightMargin,
        XtCHorizontalMargins,
        XtRDimension,
        sizeof(Dimension),
        offset(right_margin),
        XtRImmediate,
        (XtPointer)6
      },
      {
        XtNbackingStore,
        XtCBackingStore,
        XtRBackingStore,
        sizeof(int),
        offset(backing_store),
        XtRImmediate,
        (XtPointer)(Always + WhenMapped + NotUseful)
      },
      {
        XtNtimeout,
        XtCTimeout,
        XtRInt,
        sizeof(int),
        offset(timeout),
        XtRImmediate,
        (XtPointer)500
      },
      {
        XawNdisplayList,
        XawCDisplayList,
        XawRDisplayList,
        sizeof(XawDisplayList*),
        offset(display_list),
        XtRImmediate,
        NULL
      },
    };
    #undef offset
    
    TipClassRec tipClassRec = {
      /* core */
      {
        (WidgetClass)&widgetClassRec,	/* superclass */
        "Tip",				/* class_name */
        sizeof(TipRec),			/* widget_size */
        XawTipClassInitialize,		/* class_initialize */
        NULL,				/* class_part_initialize */
        False,				/* class_inited */
        XawTipInitialize,			/* initialize */
        NULL,				/* initialize_hook */
        XawTipRealize,			/* realize */
        NULL,				/* actions */
        0,					/* num_actions */
        resources,				/* resources */
        XtNumber(resources),		/* num_resources */
        NULLQUARK,				/* xrm_class */
        True,				/* compress_motion */
        True,				/* compress_exposure */
        True,				/* compress_enterleave */
        False,				/* visible_interest */
        XawTipDestroy,			/* destroy */
        NULL,				/* resize */
        XawTipExpose,			/* expose */
        XawTipSetValues,			/* set_values */
        NULL,				/* set_values_hook */
        XtInheritSetValuesAlmost,		/* set_values_almost */
        NULL,				/* get_values_hook */
        NULL,				/* accept_focus */
        XtVersion,				/* version */
        NULL,				/* callback_private */
        NULL,				/* tm_table */
        XtInheritQueryGeometry,		/* query_geometry */
        XtInheritDisplayAccelerator,	/* display_accelerator */
        NULL,				/* extension */
      },
      /* tip */
      {
        NULL,				/* extension */
      },
    };
    
    WidgetClass tipWidgetClass = (WidgetClass)&tipClassRec;
    
    static XawTipInfo *first_tip;
    
    /*
     * Implementation
     */
    static void
    XawTipClassInitialize(void)
    {
        XawInitializeWidgetSet();
        XtAddConverter(XtRString, XtRBackingStore, XmuCvtStringToBackingStore,
    		   NULL, 0);
        XtSetTypeConverter(XtRBackingStore, XtRString, XmuCvtBackingStoreToString,
    		       NULL, 0, XtCacheNone, NULL);
    }
    
    /*ARGSUSED*/
    static void
    XawTipInitialize(Widget req, Widget w, ArgList args, Cardinal *num_args)
    {
        TipWidget tip = (TipWidget)w;
        XGCValues values;
    
        if (!tip->tip.font) XtError("Aborting: no font found\n");
        if (tip->tip.international && !tip->tip.fontset)
    	XtError("Aborting: no fontset found\n");
        
        tip->tip.timer = 0;
    
        values.foreground = tip->tip.foreground;
        values.background = tip->core.background_pixel;
        values.font = tip->tip.font->fid;
        values.graphics_exposures = False;
    
        tip->tip.gc = XtAllocateGC(w, 0, GCForeground | GCBackground | GCFont |
    			       GCGraphicsExposures, &values, GCFont, 0);
    }
    
    static void
    XawTipDestroy(Widget w)
    {
        XawTipInfo *info = FindTipInfo(w);
        TipWidget tip = (TipWidget)w;
    
        if (tip->tip.timer)
    	XtRemoveTimeOut(tip->tip.timer);
    
        XtReleaseGC(w, tip->tip.gc);
    
        XtRemoveEventHandler(XtParent(w), KeyPressMask, False, TipShellEventHandler,
    			 (XtPointer)NULL);
        if (info == first_tip)
    	first_tip = first_tip->next;
        else {
    	XawTipInfo *p = first_tip;
    
    	while (p && p->next != info)
    	    p = p->next;
    	if (p)
    	    p->next = info->next;
        }
        XtFree((char*)info);
    }
    
    static void
    XawTipRealize(Widget w, Mask *mask, XSetWindowAttributes *attr)
    {
        TipWidget tip = (TipWidget)w;
    
        if (tip->tip.backing_store == Always ||
    	tip->tip.backing_store == NotUseful ||
    	tip->tip.backing_store == WhenMapped) {
    	*mask |= CWBackingStore;
    	attr->backing_store = tip->tip.backing_store;
        }
        else
    	*mask &= ~CWBackingStore;
        *mask |= CWOverrideRedirect;
        attr->override_redirect = True;
    
        XtWindow(w) = XCreateWindow(DisplayOfScreen(XtScreen(w)),
    				RootWindowOfScreen(XtScreen(w)),
    				XtX(w), XtY(w),
    				XtWidth(w) ? XtWidth(w) : 1,
    				XtHeight(w) ? XtHeight(w) : 1,
    				XtBorderWidth(w),
    				DefaultDepthOfScreen(XtScreen(w)),
    				InputOutput,
    				CopyFromParent,
    				*mask, attr);
    }
    
    static void
    XawTipExpose(Widget w, XEvent *event, Region region)
    {
        TipWidget tip = (TipWidget)w;
        GC gc = tip->tip.gc;
        char *nl, *label = tip->tip.label;
        Position y = tip->tip.top_margin + tip->tip.font->max_bounds.ascent;
        int len;
    
        if (tip->tip.display_list)
    	XawRunDisplayList(w, tip->tip.display_list, event, region);
    
        if (tip->tip.international == True) {
    	Position ksy = tip->tip.top_margin;
    	XFontSetExtents *ext = XExtentsOfFontSet(tip->tip.fontset);
    
    	ksy += XawAbs(ext->max_ink_extent.y);
    
    	while ((nl = index(label, '\n')) != NULL) {
    	    XmbDrawString(XtDisplay(w), XtWindow(w), tip->tip.fontset,
    			  gc, tip->tip.left_margin, ksy, label,
    			  (int)(nl - label));
    	    ksy += ext->max_ink_extent.height;
    	    label = nl + 1;
    	}
    	len = strlen(label);
    	if (len)
    	    XmbDrawString(XtDisplay(w), XtWindow(w), tip->tip.fontset, gc,
    			  tip->tip.left_margin, ksy, label, len);
        }
        else {
    	while ((nl = index(label, '\n')) != NULL) {
    	    if (tip->tip.encoding)
    		XDrawString16(XtDisplay(w), XtWindow(w), gc,
    			      tip->tip.left_margin, y,
    			      (XChar2b*)label, (int)(nl - label) >> 1);
    	    else
    		XDrawString(XtDisplay(w), XtWindow(w), gc,
    			    tip->tip.left_margin, y, label, (int)(nl - label));
    	    y += tip->tip.font->max_bounds.ascent + 
    		 tip->tip.font->max_bounds.descent;
    	    label = nl + 1;
    	}
    	len = strlen(label);
    	if (len) {
    	    if (tip->tip.encoding)
    		XDrawString16(XtDisplay(w), XtWindow(w), gc,
    			      tip->tip.left_margin, y, (XChar2b*)label, len >> 1);
    	    else
    		XDrawString(XtDisplay(w), XtWindow(w), gc,
    			    tip->tip.left_margin, y, label, len);
    	}
        }
    }
    
    /*ARGSUSED*/
    static Boolean
    XawTipSetValues(Widget current, Widget request, Widget cnew,
    		ArgList args, Cardinal *num_args)
    {
        TipWidget curtip = (TipWidget)current;
        TipWidget newtip = (TipWidget)cnew;
        Boolean redisplay = False;
    
        if (curtip->tip.font->fid != newtip->tip.font->fid ||
    	curtip->tip.foreground != newtip->tip.foreground) {
    	XGCValues values;
    
    	values.foreground = newtip->tip.foreground;
    	values.background = newtip->core.background_pixel;
    	values.font = newtip->tip.font->fid;
    	values.graphics_exposures = False;
    	XtReleaseGC(cnew, curtip->tip.gc);
    	newtip->tip.gc = XtAllocateGC(cnew, 0, GCForeground | GCBackground |
    				      GCFont | GCGraphicsExposures, &values,
    				      GCFont, 0);
    	redisplay = True;
        }
        if (curtip->tip.display_list != newtip->tip.display_list)
    	redisplay = True;
    
        return (redisplay);
    }
    
    static void
    TipLayout(XawTipInfo *info)
    {
        XFontStruct	*fs = info->tip->tip.font;
        int width = 0, height;
        char *nl, *label = info->tip->tip.label;
    
        if (info->tip->tip.international == True) {
    	XFontSet fset = info->tip->tip.fontset;
    	XFontSetExtents *ext = XExtentsOfFontSet(fset);
    
    	height = ext->max_ink_extent.height;
    	if ((nl = index(label, '\n')) != NULL) {
    	    /*CONSTCOND*/
    	    while (True) {
    		int w = XmbTextEscapement(fset, label, (int)(nl - label));
    
    		if (w > width)
    		    width = w;
    		if (*nl == '\0')
    		    break;
    		label = nl + 1;
    		if (*label)
    		    height += ext->max_ink_extent.height;
    		if ((nl = index(label, '\n')) == NULL)
    		    nl = index(label, '\0');
    	    }
    	}
    	else
    	    width = XmbTextEscapement(fset, label, strlen(label));
        }
        else {
    	height = fs->max_bounds.ascent + fs->max_bounds.descent;
    	if ((nl = index(label, '\n')) != NULL) {
    	    /*CONSTCOND*/
    	    while (True) {
    		int w = info->tip->tip.encoding ?
    		    XTextWidth16(fs, (XChar2b*)label, (int)(nl - label) >> 1) :
    		    XTextWidth(fs, label, (int)(nl - label));
    		if (w > width)
    		    width = w;
    		if (*nl == '\0')
    		    break;
    		label = nl + 1;
    		if (*label)
    		    height += fs->max_bounds.ascent + fs->max_bounds.descent;
    		if ((nl = index(label, '\n')) == NULL)
    		    nl = index(label, '\0');
    	    }
    	}
    	else
    	    width = info->tip->tip.encoding ?
    		XTextWidth16(fs, (XChar2b*)label, strlen(label) >> 1) :
    		XTextWidth(fs, label, strlen(label));
        }
        XtWidth(info->tip) = width + info->tip->tip.left_margin +
    			 info->tip->tip.right_margin;
        XtHeight(info->tip) = height + info->tip->tip.top_margin +
    			  info->tip->tip.bottom_margin;
    }
    
    #define	DEFAULT_TIP_Y_OFFSET	12
    static void
    TipPosition(XawTipInfo *info)
    {
        Window r, c;
        int rx, ry, wx, wy;
        unsigned mask;
        Position x, y;
    
        XQueryPointer(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip),
    		  &r, &c, &rx, &ry, &wx, &wy, &mask);
        x = rx - (XtWidth(info->tip) >> 1);
        y = ry + DEFAULT_TIP_Y_OFFSET;
    
        if (x >= 0) {
    	int scr_width = WidthOfScreen(XtScreen(info->tip));
    
    	if (x + XtWidth(info->tip) + XtBorderWidth(info->tip) > scr_width)
    	    x = scr_width - XtWidth(info->tip) - XtBorderWidth(info->tip);
        }
        if (x < 0)
    	x = 0;
        if (y >= 0) {
    	int scr_height = HeightOfScreen(XtScreen(info->tip));
    
    	if (y + XtHeight(info->tip) + XtBorderWidth(info->tip) > scr_height)
    	    y -= XtHeight(info->tip) + XtBorderWidth(info->tip) +
    		 (DEFAULT_TIP_Y_OFFSET << 1);
        }
        if (y < 0)
    	y = 0;
    
        XMoveResizeWindow(XtDisplay(info->tip), XtWindow(info->tip),
    		      (int)(XtX(info->tip) = x), (int)(XtY(info->tip) = y),
    		      (unsigned)XtWidth(info->tip), (unsigned)XtHeight(info->tip));
    }
    
    static XawTipInfo *
    CreateTipInfo(Widget w)
    {
        XawTipInfo *info = XtNew(XawTipInfo);
        Widget shell = w;
    
        info->screen = XtScreen(w);
    
        while (XtParent(shell))
    	shell = XtParent(shell);
    
        info->tip = (TipWidget)XtCreateWidget("tip", tipWidgetClass, shell, NULL, 0);
        XtRealizeWidget((Widget)info->tip);
        info->widget = NULL;
        info->mapped = False;
        info->next = NULL;
        XtAddEventHandler(shell, KeyPressMask, False, TipShellEventHandler,
    		      (XtPointer)NULL);
    
        return (info);
    }
    
    static XawTipInfo *
    FindTipInfo(Widget w)
    {
        XawTipInfo *ptip, *tip = first_tip;
        Screen *screen = XtScreenOfObject(w);
    
        if (tip == NULL)
    	return (first_tip = tip = CreateTipInfo(w));
    
        for (ptip = tip; tip; ptip = tip, tip = tip->next)
    	if (tip->screen == screen)
    	    return (tip);
    
        return (ptip->next = CreateTipInfo(w));
    }
    
    static void
    ResetTip(XawTipInfo *info, Bool add_timeout)
    {
        if (info->tip->tip.timer) {
    	XtRemoveTimeOut(info->tip->tip.timer);
    	info->tip->tip.timer = 0;
        }
        if (info->mapped) {
    	XtRemoveGrab(XtParent((Widget)info->tip));
    	XUnmapWindow(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip));
    	info->mapped = False;
        }
        if (add_timeout) {
    	info->tip->tip.timer =
    	    XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)info->tip),
    			    info->tip->tip.timeout, TipTimeoutCallback,
    			    (XtPointer)info);
        }
    }
    
    static void
    TipTimeoutCallback(XtPointer closure, XtIntervalId *id)
    {
        XawTipInfo *info = (XawTipInfo*)closure;
        Arg args[3];
    
        info->tip->tip.label = NULL;
        info->tip->tip.international = False;
        info->tip->tip.encoding = 0;
        XtSetArg(args[0], XtNtip, &info->tip->tip.label);
        XtSetArg(args[1], XtNinternational, &info->tip->tip.international);
        XtSetArg(args[2], XtNencoding, &info->tip->tip.encoding);
        XtGetValues(info->widget, args, 3);
    
        if (info->tip->tip.label) {
    	TipLayout(info);
    	TipPosition(info);
    	XMapRaised(XtDisplay((Widget)info->tip), XtWindow((Widget)info->tip));
    	XtAddGrab(XtParent((Widget)info->tip), True, True);
    	info->mapped = True;
        }
    }
    
    /*ARGSUSED*/
    static void
    TipShellEventHandler(Widget w, XtPointer client_data, XEvent *event,
    		     Boolean *continue_to_dispatch)
    {
        ResetTip(FindTipInfo(w), False);
    }
    
    /*ARGSUSED*/
    static void
    TipEventHandler(Widget w, XtPointer client_data, XEvent *event,
    		Boolean *continue_to_dispatch)
    {
        XawTipInfo *info = FindTipInfo(w);
        Boolean add_timeout;
    
        if (info->widget != w) {
    	ResetTip(info, False);
    	info->widget = w;
        }
    
        switch (event->type) {
    	case EnterNotify:
    	    add_timeout = True;
    	    break;
    	case MotionNotify:
    	    /* If any button is pressed, timer is 0 */
    	    if (info->mapped)
    		return;
    	    add_timeout = info->tip->tip.timer != 0;
    	    break;
    	default:
    	    add_timeout = False;
    	    break;
        }
        ResetTip(info, add_timeout);
    }
    
    /*
     * Public routines
     */
    void
    XawTipEnable(Widget w)
    {
        XtAddEventHandler(w, TIP_EVENT_MASK, False, TipEventHandler,
    		      (XtPointer)NULL);
    }
    
    void
    XawTipDisable(Widget w)
    {
        XawTipInfo *info = FindTipInfo(w);
    
        XtRemoveEventHandler(w, TIP_EVENT_MASK, False, TipEventHandler,
    			 (XtPointer)NULL);
        if (info->widget == w)
    	ResetTip(info, False);
    }