UNIXworkcode

/** * * $Id: BubbleButton.c,v 1.9 2005/12/01 14:31:43 tringali Exp $ * * Copyright (C) 1996 Free Software Foundation, Inc. * Copyright © 1999-2001 by the LessTif developers. * * This file is part of the GNU LessTif Extension Library. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * **/ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include <unistd.h> #include <stdio.h> #include <X11/StringDefs.h> #include <Xm/XmosP.h> #include <Xm/MessageB.h> #include <Xm/Form.h> #include <Xm/TextF.h> #include <Xm/LabelP.h> #include <Xm/DialogS.h> #include <Xm/MenuShell.h> #include <Xm/Display.h> #include "BubbleButtonP.h" #include "SlideC.h" #include "../util/misc.h" #ifdef WITH_DMALLOC #include <dmalloc.h> #endif static const char rcsid[] = "$Id: BubbleButton.c,v 1.9 2005/12/01 14:31:43 tringali Exp $"; /* Widget methods, forward declarations */ static void class_initialize(void); static void class_part_initialize(WidgetClass widget_class); static void initialize(Widget request, Widget new_w, ArgList args, Cardinal *num_args); static void destroy(Widget w); static Boolean set_values(Widget old, Widget request, Widget new_w, ArgList args, Cardinal *num_args); static void _XmExportLabelString(Widget w, int offset, XtArgVal *value); /* Helper functions, forward declarations */ /* Widget default resources */ #define Offset(field) XtOffsetOf(XltBubbleButtonRec, bubble_button.field) static XtResource resources[] = { { XltNbubbleString, XltCBubbleString, XmRXmString, sizeof(XmString), Offset(BubbleString), XmRImmediate, (XtPointer)NULL }, { XltNshowBubble, XltCShowBubble, XmRBoolean, sizeof(Boolean), Offset(show_bubble), XmRImmediate, (XtPointer)True }, { XltNdelay, XltCDelay, XtRInt, sizeof(int), Offset(Delay), XtRImmediate, (XtPointer)1000 }, { XltNmouseOverString, XltCMouseOverString, XmRXmString, sizeof(XmString), Offset(MouseOverString), XtRImmediate, (XtPointer)NULL }, { XltNmouseOverPixmap, XltCMouseOverPixmap, XmRPrimForegroundPixmap, sizeof(Pixmap), Offset(MouseOverPixmap), XtRImmediate, (XtPointer)None }, { XltNbubbleDuration, XltCBubbleDuration, XtRInt, sizeof(int), Offset(Duration), XtRImmediate, (XtPointer)0 }, { XltNslidingBubble, XltCslidingBubble, XmRBoolean, sizeof(Boolean), Offset(slidingBubble), XmRImmediate, (XtPointer)True }, { XltNautoParkBubble, XltCautoParkBubble, XmRBoolean, sizeof(Boolean), Offset(autoParkBubble), XmRImmediate, (XtPointer)False }, }; static XmSyntheticResource syn_resources[] = { { XltNbubbleString, sizeof(XmString), Offset(BubbleString), _XmExportLabelString, NULL } }; #undef Offset /* Widget class record */ static void EnterWindow(Widget w, XEvent *event, String *params, Cardinal *num_params); static void LeaveWindow(Widget w, XEvent *event, String *params, Cardinal *num_params); static XtActionsRec actions[] = { {"Enter", EnterWindow}, {"Leave", LeaveWindow}, }; /* *INDENT-OFF* */ XltBubbleButtonClassRec xrwsBubbleButtonClassRec = { /* Core class part */ { /* superclass */ (WidgetClass) &xmPushButtonClassRec, /* class_name */ "XltBubbleButton", /* widget_size */ sizeof(XltBubbleButtonRec), /* class_initialize */ class_initialize, /* class_part_initialize */ class_part_initialize, /* class_inited */ False, /* initialize */ initialize, /* initialize_hook */ NULL, /* realize */ XtInheritRealize, /* actions */ actions, /* num_actions */ XtNumber(actions), /* resources */ resources, /* num_resources */ XtNumber(resources), /* xrm_class */ NULLQUARK, /* compress_motion */ True, /* compress_exposure */ XtExposeCompressMaximal, /* compress_enterleave */ True, /* visible_interest */ False, /* destroy */ destroy, /* resize */ XtInheritResize, /* expose */ XtInheritExpose, /* set_values */ set_values, /* set_values_hook */ NULL, /* set_values_almost */ XtInheritSetValuesAlmost, /* get_values_hook */ NULL, /* accept_focus */ NULL, /* version */ XtVersion, /* callback offsets */ NULL, /* tm_table */ NULL, /* query_geometry */ XtInheritQueryGeometry, /* display_accelerator */ NULL, /* extension */ (XtPointer)NULL }, /* Primitive Class part */ { /* border_highlight */ XmInheritBorderHighlight, /* border_unhighlight */ XmInheritBorderUnhighlight, /* translations */ XtInheritTranslations, /* arm_and_activate_proc */ XmInheritArmAndActivate, /* synthetic resources */ syn_resources, /* num syn res */ XtNumber(syn_resources), /* extension */ (XtPointer)NULL }, /* Label Class part */ { /* setOverrideCallback */ XmInheritSetOverrideCallback, /* menuProcs */ XmInheritMenuProc, /* translations */ XtInheritTranslations, /* extension */ NULL }, /* PushButton Class part */ { /* extension */ NULL }, /* BubbleButton Class part */ { /* leave_time */ 0, /* extension */ NULL } }; /* *INDENT-ON* */ WidgetClass xrwsBubbleButtonWidgetClass = (WidgetClass)&xrwsBubbleButtonClassRec; /* Helper routines */ /* Widget methods */ static void class_initialize(void) { xrwsBubbleButtonClassRec.bubble_button_class.leave_time = 0; } static void class_part_initialize(WidgetClass widget_class) { } static void initialize(Widget request, Widget new_w, ArgList args, Cardinal *num_args) { Widget Shell; Arg arg[10]; int argcnt = 0; BubbleButton_Timer(new_w) = (XtIntervalId)NULL; BubbleButton_DurationTimer(new_w) = (XtIntervalId)NULL; BubbleButton_Swapped(new_w) = False; BubbleButton_Slider(new_w) = NULL; Shell = CreatePopupShellWithBestVis("BubbleShell", transientShellWidgetClass, new_w, arg, argcnt); XtVaSetValues(Shell, XmNoverrideRedirect, True, NULL); if (BubbleButton_MouseOverString(new_w) != NULL) { BubbleButton_MouseOverString(new_w) = XmStringCopy(BubbleButton_MouseOverString(new_w)); } if (BubbleButton_BubbleString(new_w) == NULL) { XmString xmstring; #if XmVERSION >= 2 xmstring = XmeGetLocalizedString((char *)NULL, new_w, XmNlabelString, XtName(new_w)); #else xmstring = _XmOSGetLocalizedString((char *)NULL, new_w, XmNlabelString, XtName(new_w)); #endif BubbleButton_BubbleString(new_w) = xmstring; } else { BubbleButton_BubbleString(new_w) = XmStringCopy(BubbleButton_BubbleString(new_w)); } BubbleButton_Label(new_w) = XmCreateLabel(Shell, "BubbleLabel", NULL, 0); XtVaSetValues(BubbleButton_Label(new_w), XmNlabelString, BubbleButton_BubbleString(new_w), XmNforeground, ((XltBubbleButtonWidget)new_w)->core.background_pixel, XmNbackground, ((XltBubbleButtonWidget)new_w)->primitive.foreground, NULL); XtManageChild(BubbleButton_Label(new_w)); } static void destroy(Widget w) { if (BubbleButton_MouseOverString(w)) { XmStringFree(BubbleButton_MouseOverString(w)); } if (BubbleButton_Timer(w)) { XtRemoveTimeOut(BubbleButton_Timer(w)); } if (BubbleButton_DurationTimer(w)) { XtRemoveTimeOut(BubbleButton_DurationTimer(w)); } } static Boolean set_values(Widget old, Widget request, Widget new_w, ArgList args, Cardinal *num_args) { if (BubbleButton_MouseOverString(new_w) != BubbleButton_MouseOverString(old)) { XmStringFree(BubbleButton_MouseOverString(old)); BubbleButton_MouseOverString(new_w) = XmStringCopy(BubbleButton_MouseOverString(new_w)); } if (BubbleButton_BubbleString(new_w) != BubbleButton_BubbleString(old)) { XmStringFree(BubbleButton_BubbleString(old)); BubbleButton_BubbleString(new_w) = XmStringCopy(BubbleButton_BubbleString(new_w)); XtVaSetValues(BubbleButton_Label(new_w), XmNlabelString, BubbleButton_BubbleString(new_w), NULL); } if (XtIsSensitive(old) != XtIsSensitive(new_w)) { if (!XtIsSensitive(new_w)) { Cardinal num_params = 0; LeaveWindow(new_w, NULL, NULL, &num_params); } } return (False); } /* * Short-term solution. Doesn't belong here. See SF bug #923924. */ extern XmString _XmStringCreateExternal(XmFontList fontlist, _XmString cs); static void _XmExportLabelString(Widget w, int offset, XtArgVal *value) { _XmString str; XmString ret; str = *(_XmString *)(((char *)w) + offset); if (str) { if (XmIsLabel(w)) { ret = _XmStringCreateExternal(Lab_Font(w), str); } else { ret = NULL; } } else { ret = NULL; } *value = (XtArgVal)ret; } static void fadeOutFinish(Widget slide, Widget w, XtPointer call_data) { BubbleButton_Slider(w) = NULL; XtPopdown(XtParent(BubbleButton_Label(w))); } static void UnpostIt(Widget w) { BubbleButton_DurationTimer(w) = (XtIntervalId)NULL; if (BubbleButton_SlidingBubble(w)) { BubbleButton_Slider(w) = XtVaCreateWidget("Slide", xltSlideContextWidgetClass, XmGetXmDisplay(XtDisplay(w)), XltNslideWidget, XtParent(BubbleButton_Label(w)), XltNslideDestHeight, 1, NULL); XtAddCallback(BubbleButton_Slider(w), XltNslideFinishCallback, (XtCallbackProc)fadeOutFinish, w); } else { XtPopdown(XtParent(BubbleButton_Label(w))); } } static void fadeInFinish(Widget slide, Widget w, XtPointer call_data) { BubbleButton_Slider(w) = NULL; if (BubbleButton_Duration(w) > 0) { BubbleButton_DurationTimer(w) = XtAppAddTimeOut(XtWidgetToApplicationContext(w), BubbleButton_Duration(w), (XtTimerCallbackProc)UnpostIt, w); } } #define TOOLTIP_EDGE_GUARD 5 static void PostIt(Widget w) { int rx, ry, x, y; unsigned int key; Window root, child; Dimension xPos, yPos; XWindowAttributes screenAttr; BubbleButton_Timer(w) = (XtIntervalId)NULL; XQueryPointer(XtDisplay(w), XtWindow(w), &root, &child, &rx, &ry, &x, &y, &key); if (BubbleButton_DurationTimer(w) != (XtIntervalId)NULL) { XtRemoveTimeOut(BubbleButton_DurationTimer(w)); BubbleButton_DurationTimer(w) = (XtIntervalId)NULL; } { XtWidgetGeometry geo; XtQueryGeometry(BubbleButton_Label(w), NULL, &geo); xPos = rx - x + XtWidth(w) / 2 ; yPos = ry - y + XtHeight(w); if (BubbleButton_AutoParkBubble(w)) { xPos = rx + 3; yPos = ry + 15; /* keep tooltip within screen */ XGetWindowAttributes(XtDisplay(w), RootWindowOfScreen(XtScreen(w)), &screenAttr); if (xPos + geo.width >= screenAttr.width - TOOLTIP_EDGE_GUARD) xPos = screenAttr.width - geo.width - TOOLTIP_EDGE_GUARD; if (yPos + geo.height >= screenAttr.height - TOOLTIP_EDGE_GUARD) yPos = ry - 15 - geo.height; } if (BubbleButton_SlidingBubble(w)) { int xAdjust, yAdjust; /* FIXME: slider must avoid pointer */ xAdjust = rx < xPos? 1 : -1; yAdjust = ry < yPos? 1 : - geo.height/2; XtVaSetValues(XtParent(BubbleButton_Label(w)), XmNx, rx + xAdjust /*- x + XtWidth(w) / 2*/, XmNy, ry + yAdjust /*- y + XtHeight(w)*/, XmNheight, 1, XmNwidth, 1 /*geo.width*/, NULL); XtPopup(XtParent(BubbleButton_Label(w)), XtGrabNone); BubbleButton_Slider(w) = XtVaCreateWidget("Slide", xltSlideContextWidgetClass, XmGetXmDisplay(XtDisplay(w)), XltNslideWidget, XtParent(BubbleButton_Label(w)), XltNslideDestX, xPos, XltNslideDestY, yPos, XltNslideDestWidth, geo.width, XltNslideDestHeight, geo.height, NULL); XtAddCallback(BubbleButton_Slider(w), XltNslideFinishCallback, (XtCallbackProc)fadeInFinish, w); } else { XtVaSetValues(XtParent(BubbleButton_Label(w)), XmNx, xPos /*- x + XtWidth(w) / 2*/, XmNy, yPos /*- y + XtHeight(w)*/, XmNheight, geo.height, XmNwidth, geo.width /*geo.width*/, NULL); XtPopup(XtParent(BubbleButton_Label(w)), XtGrabNone); if (BubbleButton_Duration(w) > 0) { BubbleButton_DurationTimer(w) = XtAppAddTimeOut(XtWidgetToApplicationContext(w), BubbleButton_Duration(w), (XtTimerCallbackProc)UnpostIt, w); } } } } static void SwapLabels(Widget w) { XmString tmp = NULL; if (BubbleButton_MouseOverString(w)) { XtVaGetValues(w, XmNlabelString, &tmp, NULL); XtVaSetValues(w, XmNlabelString, BubbleButton_MouseOverString(w), NULL); XmStringFree(BubbleButton_MouseOverString(w)); BubbleButton_MouseOverString(w) = tmp; } } static void SwapPixmaps(Widget w) { Pixmap tmp; if (BubbleButton_MouseOverPixmap(w)) { XtVaGetValues(w, XmNlabelPixmap, &tmp, NULL); XtVaSetValues(w, XmNlabelPixmap, BubbleButton_MouseOverPixmap(w), NULL); BubbleButton_MouseOverPixmap(w) = tmp; } } static void Swap(Widget w) { if (Lab_IsText(w)) { SwapLabels(w); } else { SwapPixmaps(w); } BubbleButton_Swapped(w) = BubbleButton_Swapped(w) ? False : True; } static void EnterWindow(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (BubbleButton_ShowBubble(w) && !BubbleButton_Timer(w)) { unsigned long delay; if (event && (event->xcrossing.time - BubbleButtonClass_LeaveTime(w) < BubbleButton_Delay(w))) { delay = 0; } else { delay = BubbleButton_Delay(w); } BubbleButton_Timer(w) = XtAppAddTimeOut(XtWidgetToApplicationContext(w), delay, (XtTimerCallbackProc)PostIt, w); } if (!BubbleButton_Swapped(w)) { Swap(w); } } static void LeaveWindow(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (BubbleButton_Timer(w)) { XtRemoveTimeOut(BubbleButton_Timer(w)); BubbleButton_Timer(w) = (XtIntervalId)NULL; } else { if (BubbleButton_Slider(w) != NULL) { XtDestroyWidget(BubbleButton_Slider(w)); BubbleButton_Slider(w) = NULL; } XtPopdown(XtParent(BubbleButton_Label(w))); if (event) { if (BubbleButton_DurationTimer(w) || BubbleButton_Duration(w) == 0) { BubbleButtonClass_LeaveTime(w) = event->xcrossing.time; } } } if (BubbleButton_DurationTimer(w)) { XtRemoveTimeOut(BubbleButton_DurationTimer(w)); BubbleButton_DurationTimer(w) = (XtIntervalId)NULL; } if (BubbleButton_Swapped(w)) { Swap(w); } } /* Public functions */ Widget XltCreateBubbleButton(Widget parent, char *name, Arg *arglist, Cardinal argCount) { return XtCreateWidget(name, xrwsBubbleButtonWidgetClass, parent, arglist, argCount); }