/* * Cvs: $Id: Side.c,v 1.16 1996/02/09 11:16:24 erikb Exp $ * * HtSide is a widget used for manoeuvring. It is intended to work in * situations where a ScrollBar normally is used. HtSide is typically * used inside some Manager class widget adjacent to the displayed data. * * Copyright (C) 1995. Author: Erik Borälv (Erik.Boralv@cmd.uu.se). * Center for Human-Computer Studies (CMD), Uppsala University. * * Center for Human-Computer Studies * Uppsala University * att: Erik Boralv * Lagerhyddvagen 18 * S-752 37 UPPSALA * SWEDEN * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * * IN NO EVENT SHALL CMD BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, * INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS * SOFTWARE AND ITS DOCUMENTATION, EVEN IF CMD HAS BEEN ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * CMD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND CMD * HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, * OR MODIFICATIONS. */ #include #include #include #include #include /* * Default settings for marker lengths. * * The "one" markers are 5 pixels long; any longer looks stupid... * Every fifth marker is 1.5 times the "one" marker. * Every tenth is 2 times the "one" marker. */ #define DEFAULT_WIDTH 5 #define FIFTH 1.5 #define TENTH 2 /********************************************************************* * * Convenience macros. * *********************************************************************/ #define HtCoreWidth(w) XtWidth(w) #define HtCoreHeight(w) XtHeight(w) #define HtSideBack(w) XtBackground(w) #define HtSideVisible(w) ((w)->core.visible) #define HtSideFore(w) ((w)->primitive.foreground) #define HtSideNavigation(w) ((w)->primitive.navigation_type) #define HtSideShadow(w) ((w)->primitive.shadow_thickness) #define HtSideHighlight(w) ((w)->primitive.highlight_thickness) #define HtSideHighlighted(w) ((w)->primitive.highlighted) #define HtSideMark(w) ((w)->side.markers) #define HtSideMarkLen(w) ((w)->side.marker_length) #define HtSidePos(w) ((w)->side.position) #define HtSideCall(w) ((w)->side.select) #define HtSideSeg(w) ((w)->side.segments) #define HtSideColor(w) ((w)->side.marker_color) #define HtSideGC(w) ((w)->side.side_GC) #define HtSideMarkGC(w) ((w)->side.marker_GC) #define HtSideInvGC(w) ((w)->side.inverse_GC) #define HtSideMargin(w) (HtSideShadow(w) + HtSideHighlight(w)) #define HtSideMargins(w) (2 * HtSideMargin(w)) #define HtSideHeight(w) (HtCoreHeight(w) - HtSideMargins(w)) #define HtSideWidth(w) (HtCoreWidth(w) - HtSideMargins(w)) #define HtSide5Len(w) (int)(HtSideMarkLen(w) * FIFTH) #define HtSide10Len(w) (int)(HtSideMarkLen(w) * TENTH) #define HtSideMinWidth(w) (HtSide10Len(w) + HtSideMargins(w) + 1) #define HtSideFraction(w) (float)(((float)HtSideHeight(w)) / \ ((float)HtSideMark(w))) /* * Numbering starts at 1, not 0 - therefore the value of 'nr' is reduced by * one. See usage. */ #define HtMarkerNrToPos(w,nr) (int)((nr-0.5)*HtSideFraction(w)+HtSideMargin(w)) #define HtMarkerBelow(nr,w) (int)((nr+0) *HtSideFraction(w)+HtSideMargin(w)) #define HtMarkerAbove(nr,w) (int)((nr-1) *HtSideFraction(w)+HtSideMargin(w)) #ifdef EBUG /* Print str and line number, for debugging purposes. */ #define debug(str) ((void)printf("Line %d:\t%s\n", __LINE__, (str))) #endif /* Returns True if 'x' divides 'y'. */ #define DIV(x,y) (int)((((x) % (y)) == 0) ? 1 : 0) /* Lazy way of writing some code in ClassPartInitialize. */ #define INHERIT(wclass, super, method, resource)\ if ((wclass)->side_class.method == resource)\ (wclass)->side_class.method = (super)->side_class.method; /******************************************************************** * * Translations - arrows and Emacs-style. * ********************************************************************/ static char defaultTranslations[] = "\ : PrimitiveEnter()\n\ : PrimitiveLeave()\n\ Ctrl osfUp: HtTraverse(PrevFast)\n\ Ctrl osfDown: HtTraverse(NextFast)\n\ osfUp: HtTraverse(Prev)\n\ osfDown: HtTraverse(Next)\n\ osfLeft: HtTraverse(First)\n\ osfRight: HtTraverse(Last)\n\ Ctrl p: HtTraverse(Prev)\n\ Ctrl n: HtTraverse(Next)\n\ Ctrl a: HtTraverse(First)\n\ Ctrl e: HtTraverse(Last)\n\ : HtTraverse(Pointer)"; /* * Primitive translations. * We are a "tab group control" because we need the * arrow keys for navigation and control. */ static char primitiveTranslations[] = "\ ~s ~m ~a Return: PrimitiveParentActivate()\n\ osfActivate: PrimitiveParentActivate()\n\ osfCancel: PrimitiveParentCancel()\n\ osfHelp: PrimitiveHelp()\n\ s ~m ~a Tab: PrimitivePrevTabGroup()\n\ ~m ~a Tab: PrimitiveNextTabGroup()\n\ : PrimitiveFocusIn()\n\ : PrimitiveFocusOut()\n\ : PrimitiveUnmap()"; /* * Actions. */ #ifdef NeedFunctionPrototypes static void HtTraverse(Widget w, XEvent *event, String *params, Cardinal *nparams); #else static void HtTraverse(); #endif static XtActionsRec actions[] = { {"HtTraverse", HtTraverse}, }; /******************************************************************** * * Resources. * ********************************************************************/ static XtResource resources[] = { { HtNmarkerColor, HtCMarkerColor, XmRPixel, sizeof(Pixel), XtOffsetOf(struct _HtSideRec, side.marker_color), XtRCallProc, (XtPointer)_XmHighlightColorDefault}, { HtNmarkerLength, HtCMarkerLength, XtRInt, sizeof(int), XtOffsetOf(struct _HtSideRec, side.marker_length), XmRImmediate, (XtPointer)DEFAULT_WIDTH}, { HtNmarkers, HtCMarkers, XtRInt, sizeof(int), XtOffsetOf(struct _HtSideRec, side.markers), XmRImmediate, (XtPointer)1}, { /* Override, we need the Arrows for navigation. */ XmNnavigationType, XmCNavigationType, XmRNavigationType, sizeof(unsigned char), XtOffsetOf(struct _HtSideRec, primitive.navigation_type), XmRImmediate, (XtPointer)XmTAB_GROUP}, { HtNposition, XmCPosition, XtRInt, sizeof(int), XtOffsetOf(struct _HtSideRec, side.position), XmRImmediate, (XtPointer)1}, { HtNselectCallback, HtCSelectCallback, XtRCallback, sizeof(caddr_t), XtOffsetOf(struct _HtSideRec, side.select), XtRCallback, (XtPointer)NULL}, }; /* * XmPrimitive class extension record. */ static XmPrimitiveClassExtRec primClassExtRec = { NULL, /* next_extension */ NULLQUARK, /* record_type */ XmPrimitiveClassExtVersion, /* version */ sizeof(XmPrimitiveClassExtRec), /* record_size */ NULL, /* widget_baseline */ WidgetDisplayRect, /* widget_display_rect */ NULL, /* widget_margins */ }; /********************************************************************* * * Full class record constant. * *********************************************************************/ externaldef(htsideclassrec) HtSideClassRec htSideClassRec = { { /* core_class fields */ (WidgetClass) &xmPrimitiveClassRec, /* superclass */ "HtSide", /* class_name */ sizeof(HtSideRec), /* widget_size */ NULL, /* class_initialize */ ClassPartInitialize, /* class_part_init */ FALSE, /* class_inited */ (XtInitProc)Initialize, /* initialize */ NULL, /* initialize_hook */ XtInheritRealize, /* realize */ actions, /* actions */ XtNumber(actions), /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ XtExposeCompressMaximal, /* compress_exposure */ TRUE, /* compress_enterlv */ TRUE, /* visible_interest */ (XtWidgetProc)Destroy, /* destroy */ (XtWidgetProc)Resize, /* resize */ (XtExposeProc)Redisplay, /* expose */ (XtSetValuesFunc)SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ XtInheritAcceptFocus, /* enter_focus */ XtVersion, /* version */ NULL, /* callback_private */ defaultTranslations, /* tm_table */ XtInheritQueryGeometry, /* query_geometry */ NULL, /* display_accelerator */ NULL, /* extension */ }, { /* XmPrimitive */ XmInheritBorderHighlight, /* border_highlight */ XmInheritBorderUnhighlight, /* border_unhighlight */ primitiveTranslations, /* translations */ NULL, /* arm_and_activate */ NULL, /* syn_resources */ 0, /* num_syn_resources */ (XtPointer)&primClassExtRec, /* extension */ }, { /* side_class field */ CreateGC, /* create gc's */ DestroyGC, /* destroy gc's */ DrawMarkers, /* draw all markers */ EraseCurrent, /* erase position */ DrawCurrent, /* draw position */ Goto, /* go to position */ Next, /* next position */ Prev, /* previous position */ First, /* first position */ Last, /* last position */ NULL, /* extension */ } }; externaldef(htsidewidgetclass) WidgetClass htSideWidgetClass = (WidgetClass) &htSideClassRec; /* * Process method inheritance for subclasses of HtSide. */ static void #ifdef NeedFunctionPrototypes ClassPartInitialize(WidgetClass widgetClass) #else ClassPartInitialize(widgetClass) WidgetClass widgetClass; #endif /* NeedFunctionPrototypes */ { HtSideWidgetClass wc = (HtSideWidgetClass)widgetClass; HtSideWidgetClass sc = (HtSideWidgetClass)wc->core_class.superclass; #ifdef EBUG debug("ClassPartInitialize"); #endif INHERIT(wc, sc, create_gc, HtInheritCreateGC); INHERIT(wc, sc, destroy_gc, HtInheritDestroyGC); INHERIT(wc, sc, draw_markers, HtInheritDrawMarkers); INHERIT(wc, sc, draw_current, HtInheritDrawCurrent); INHERIT(wc, sc, erase_current, HtInheritEraseCurrent); INHERIT(wc, sc, go_to, HtInheritGoto); INHERIT(wc, sc, next, HtInheritNext); INHERIT(wc, sc, prev, HtInheritPrev); INHERIT(wc, sc, first, HtInheritFirst); INHERIT(wc, sc, last, HtInheritLast); } /* end ClassPartInitialize */ static void #ifdef NeedFunctionPrototypes Initialize(Widget request_w, Widget new_w, ArgList args, Cardinal *num_args) #else Initialize(request_w, new_w, args, num_args) Widget request_w; Widget new_w; ArgList args; Cardinal *num_args; #endif /* NeedFunctionPrototypes */ { HtSideWidgetClass wc = (HtSideWidgetClass)XtClass(new_w); HtSideWidget nw = (HtSideWidget)new_w; HtSideWidget rq = (HtSideWidget)request_w; #ifdef EBUG debug("Initialize"); #endif if (HtSidePos(nw) < 1) { XtWarning("Position must be at least 1."); HtSidePos(nw) = 1; } if (HtSideMark(nw) < 1) { XtWarning("Must be at least 1 marker."); HtSideMark(nw) = 1; } if (HtSideMarkLen(nw) < 1) { XtWarning("Marker must be at least 1 pixel long."); HtSideMarkLen(nw) = DEFAULT_WIDTH; } if (HtCoreWidth(rq) == 0) HtCoreWidth(nw) = (int)HtSideMinWidth(nw); if (HtCoreHeight(rq) == 0) HtCoreHeight(nw) = (int)(3 * HtSideMark(nw) + HtSideMargins(nw)); /* * Ensure correct setting of position. */ if (HtSidePos(nw) > HtSideMark(nw)) { XtWarning("Position exceeds maximum value."); HtSidePos(nw) = HtSideMark(nw); } if (wc->side_class.create_gc) (void)(*(wc->side_class.create_gc))((Widget)nw); /* * Allocate two XPoints for each line; a start and an end. */ if ((HtSideSeg(nw) = (XPoint*)malloc((unsigned) ((sizeof(XPoint)) * 2 * HtSideMark(nw)))) == NULL) { XtWarning("Side: fatal malloc problem!"); } /* * We are Tab Group Control. */ HtSideNavigation(nw) = XmTAB_GROUP; Resize(nw); } /* end Initialize */ static void #ifdef NeedFunctionPrototypes Destroy(Widget w) #else Destroy(w) Widget w; #endif /* NeedFunctionPrototypes */ { HtSideWidget side = (HtSideWidget)w; HtSideWidgetClass wc = (HtSideWidgetClass)XtClass(w); #ifdef EBUG debug("Destroy"); #endif /* * Let go of GC's and callbacks, * free any space we have allocated. */ if (wc->side_class.destroy_gc) (void)(*(wc->side_class.destroy_gc))(w); XtRemoveAllCallbacks(w, HtNselectCallback); XtFree((char*)HtSideSeg(side)); } /* end Destroy */ static void #ifdef NeedFunctionPrototypes Resize(HtSideWidget wid) #else Resize(wid) HtSideWidget wid; #endif /* NeedFunctionPrototypes */ { int i; int pos; int margin = HtSideMargin(wid); int len10 = margin + HtSide10Len(wid); int len5 = margin + HtSide5Len(wid); int len1 = margin + HtSideMarkLen(wid); XPoint *ptr = HtSideSeg(wid); #ifdef EBUG debug("Resize"); #endif /* * NOT OPTIMAL CODE, BUT QUITE OK. * No minimum vertical size. * * NOTE: we can shrink almost without limits - * the behaviour should still be ok! * * Get pointer to segments and re-calulate marker positions. * + Use pre-calculated values in the loop to speed things up. * + Loop from 1 (instead of from 0) to minimize the number of * calculations using the variable 'i'. */ for (i = 1; i <= HtSideMark(wid); i++) { pos = HtMarkerNrToPos(wid, i); ptr->x = margin; /* NOTE */ ptr++->y = pos; if (DIV(i,10)) { ptr->x = len10; } else if (DIV(i,5)) { ptr->x = len5; } else { ptr->x = len1; } ptr++->y = pos; } } /* end Resize */ /* * Redisplay does not care about regions (yet). I am unsure if this * is ok? Anyway, it is easier, and actually quite fast to redraw * the whole lot. This does not keep down the number of XDraw calls * and should obviously be optimized in some way. * Would it be possible (better, that is) to use the region as a clip * list in the GC? */ static void #ifdef NeedFunctionPrototypes Redisplay(Widget w, XEvent *event, Region region) #else Redisplay(w, event, region) Widget w; XEvent *event; Region region; #endif /* NeedFunctionPrototypes */ { HtSideWidgetClass wc = (HtSideWidgetClass)XtClass(w); HtSideWidget side = (HtSideWidget)w; #ifdef EBUG debug("Redisplay"); #endif /* * We redraw ALL markers. */ if (wc->side_class.draw_markers) (void)(*(wc->side_class.draw_markers))(w); if (wc->side_class.draw_current) (void)(*(wc->side_class.draw_current))(w); /* * Our responsibilities as a Primitive subclass. A better solution * than calling the functions explicitly would be to invoke the expose * method of Primitive's core part. I tried using the code below, but * it doesn't work - I get a core dump. Any suggestions? * * (*(xmPrimitiveClassRec.core_class.expose))(w, event, region); * */ if (HtSideHighlighted(side)) { if (wc->primitive_class.border_highlight) (*wc->primitive_class.border_highlight)(w); } else { if (wc->primitive_class.border_unhighlight) (*wc->primitive_class.border_unhighlight)(w); } } /* end Redisplay */ static void #ifdef NeedFunctionPrototypes CreateGC(Widget w) #else CreateGC(w) Widget w; #endif /* NeedFunctionPrototypes */ { HtSideWidget nw = (HtSideWidget)w; XGCValues values; XtGCMask valueMask; #ifdef EBUG debug("CreateGC"); #endif /* * Use XCreateGC or XtGetGC? Can I be sure my lines gets drawn the way * they should be when using XtGetGC? * * XCreateGC(XtDisplay(nw), * RootWindowOfScreen(XtScreen(nw)), * valueMask, * &values); */ valueMask = GCForeground | GCBackground | GCLineWidth; values.line_width = 1; values.foreground = HtSideFore(nw); values.background = HtSideBack(nw); HtSideGC(nw) = XtGetGC(w, valueMask, &values); valueMask = GCForeground | GCBackground | GCLineWidth; values.line_width = 1; values.background = HtSideBack(nw); values.foreground = HtSideColor(nw); HtSideMarkGC(nw) = XtGetGC(w, valueMask, &values); valueMask = GCForeground | GCBackground | GCLineWidth; values.line_width = 1; values.foreground = HtSideBack(nw); values.background = HtSideBack(nw); HtSideInvGC(nw) = XtGetGC(w, valueMask, &values); } /* end CreateGC */ static void #ifdef NeedFunctionPrototypes DestroyGC(Widget w) #else DestroyGC(w) Widget w; #endif /* NeedFunctionPrototypes */ { HtSideWidget side = (HtSideWidget)w; #ifdef EBUG debug("DestroyGC"); #endif (void)XtReleaseGC(w, HtSideGC(side)); (void)XtReleaseGC(w, HtSideMarkGC(side)); (void)XtReleaseGC(w, HtSideInvGC(side)); } /* end DestroyGC */ static void #ifdef NeedFunctionPrototypes DrawMarkers(Widget w) #else DrawMarkers(w) Widget w; #endif /* NeedFunctionPrototypes */ { #ifdef EBUG debug("DrawMarkers"); #endif if (HtSideVisible(w)) { HtSideWidget side = (HtSideWidget)w; (void)XDrawSegments(XtDisplay(w), XtWindow(w), HtSideGC(side), (XSegment *)HtSideSeg(side), HtSideMark(side)); } } /* end DrawMarkers */ static void #ifdef NeedFunctionPrototypes EraseCurrent(Widget w) #else EraseCurrent(w) Widget w; #endif /* NeedFunctionPrototypes */ { #ifdef EBUG debug("EraseCurrent"); #endif if (HtSideVisible(w)) { int pos; HtSideWidget side = (HtSideWidget)w; /* * Convert position to offset within widget and erase current marker. * Remember to take care of the lines hidden behind the current marker. */ pos = HtMarkerNrToPos(side, HtSidePos(side)); (void)XDrawRectangle(XtDisplay(w), XtWindow(w), HtSideInvGC(side), (HtSideMarkLen(side) + HtSideMargin(side)), pos - 1, 2, 2); /* * Check if we destroyed some other underlying line. */ if (DIV(HtSidePos(side), 10)) { (void)XDrawLine(XtDisplay(w), XtWindow(w), HtSideGC(side), HtSideMargin(side), pos, (HtSide10Len(side) + HtSideMargin(side)), pos); } else if (DIV(HtSidePos(side), 5)) { (void)XDrawLine(XtDisplay(w), XtWindow(w), HtSideGC(side), HtSideMargin(side), pos, (HtSide5Len(side) + HtSideMargin(side)), pos); } else { (void)XDrawLine(XtDisplay(w), XtWindow(w), HtSideGC(side), HtSideMargin(side), pos, (HtSideMarkLen(side) + HtSideMargin(side)), pos); } /* * If HtSide is packed too tight, then the current-marker will draw * on-top of some other markers. Avoid this through a partial redrawing * of the widget. */ if ((HtSideHeight(side)/HtSideMark(side)) < 2) { int min, max; XPoint *ptr; /* * Find exactly which markers need to be redrawn. */ htFindMinAndMax(w, HtSidePos(side), &min, &max); ptr = HtSideSeg(side) + (2 * (min - 1)); (void)XDrawSegments(XtDisplay(side), XtWindow(side), HtSideGC(side), (XSegment *)ptr, (1 + max - min)); } } } /* end EraseCurrent */ static void #ifdef NeedFunctionPrototypes DrawCurrent(Widget w) #else DrawCurrent(w) Widget w; #endif /* NeedFunctionPrototypes */ { #ifdef EBUG debug("DrawCurrent"); #endif if (HtSideVisible(w)) { int pos; HtSideWidget side = (HtSideWidget)w; /* * Convert current position to offset within widget and draw marker. */ pos = HtMarkerNrToPos(side, HtSidePos(side)); (void)XDrawRectangle(XtDisplay(w), XtWindow(w), HtSideMarkGC(side), (HtSideMarkLen(side) + HtSideMargin(side)), pos - 1, 2, 2); } } /* end DrawCurrent */ static Boolean #ifdef NeedFunctionPrototypes WidgetDisplayRect(Widget w, XRectangle *displayrect) #else WidgetDisplayRect(w, displayrect) Widget w; XRectangle *displayrect; #endif /* NeedFunctionPrototypes */ { HtSideWidget side = (HtSideWidget)w; if (HtSideVisible(side)) { /* * The widget is currently displaying a visual. Write the dimensions * of the visual's bounding box into displayrect. */ displayrect->x = 0; displayrect->y = 0; displayrect->width = HtSideWidth(side); displayrect->height = HtSideHeight(side); /* * Yes, this widget contains something displayable. */ return True; } else { /* * No, the widget is not currently displaying a visual. */ return False; } } /* end WidgetDisplayRect */ static Boolean #ifdef NeedFunctionPrototypes SetValues(HtSideWidget cw, HtSideWidget rw, HtSideWidget nw) #else SetValues(cw, rw, nw) HtSideWidget cw; HtSideWidget rw; HtSideWidget nw; #endif /* NeedFunctionPrototypes */ { HtSideWidgetClass wc = (HtSideWidgetClass)XtClass(nw); Boolean redraw = False; Boolean resize = False; #ifdef EBUG debug("SetValues"); #endif /* * Values that must not be zero. If a bad value, we do not * set something new but rather keep the previous one. * This means nothing happens when the user tries to set an * incorrect value - should give an ok behaviour? */ if (HtSidePos(nw) < 1) { XtAppWarningMsg(XtWidgetToApplicationContext((Widget)nw), "SetValues", "bad side number", "Side", "Side: Side must be at least one", NULL, NULL); HtSidePos(nw) = HtSidePos(cw); } if (HtSideMark(nw) < 1) { XtAppWarningMsg(XtWidgetToApplicationContext((Widget)nw), "SetValues", "bad number of markers", "Side", "Side: must have at least one marker", NULL, NULL); HtSideMark(nw) = HtSideMark(cw); } if (HtSideMarkLen(nw) < 1) { XtAppWarningMsg(XtWidgetToApplicationContext((Widget)nw), "SetValues", "bad marker length", "Side", "Side: marker lenght must be at least one", NULL, NULL); HtSideMarkLen(nw) = HtSideMarkLen(cw); } /* * Check for valid position. */ if ((HtSidePos(nw)) > (HtSideMark(nw))) { XtAppWarningMsg(XtWidgetToApplicationContext((Widget)nw), "SetValues", "bad side number", "Side", "Side: Side exceeds maximum value", NULL, NULL); HtSidePos(nw) = HtSidePos(cw); } /* * Do not allow horizontal size less than our minimum. */ if ((HtSideWidth(nw) != HtSideWidth(cw)) && (HtCoreWidth(nw) < HtSideMinWidth(nw))) { XtAppWarningMsg(XtWidgetToApplicationContext((Widget)nw), "SetValues", "bad width", "Side", "Side: too small width", NULL, NULL); HtCoreWidth(nw) = HtSideMinWidth(nw); } /* * A change of colors; we need to destroy old GCs and create new ones. */ if ((HtSideColor(nw) != HtSideColor(cw)) || (HtSideBack(nw) != HtSideBack(cw)) || (HtSideFore(nw) != HtSideFore(cw))) { if (wc->side_class.destroy_gc) (void)(*(wc->side_class.destroy_gc))((Widget)nw); if (wc->side_class.create_gc) (void)(*(wc->side_class.create_gc))((Widget)nw); redraw = True; } /* * We must re-position the markers if the borders change. */ if ((HtSideHighlight(nw) != HtSideHighlight(cw)) || (HtSideShadow(nw) != HtSideShadow(cw))) { resize = True; redraw = True; } /* * Check if changed #markers; if so allocate suitable space * and make both a resize and a redraw. */ if (HtSideMark(nw) != HtSideMark(cw)) { /* * First make sure that current position would still be ok * with the new number of markers. If not ok, user should * re-position current first, and then change #markers. We * will just ignore this kind of an error. */ if (HtSidePos(nw) > HtSideMark(nw)) { XtAppWarningMsg(XtWidgetToApplicationContext((Widget)nw), "SetValues", "bad marker/postion combination", "Side", "Side: position exceeds requested number of markers", NULL, NULL); HtSideMark(nw) = HtSideMark(cw); } else { if ((HtSideSeg(nw) = (XPoint*)realloc((char*)HtSideSeg(cw), (unsigned)((sizeof(XPoint)) * 2 * HtSideMark(nw)))) == NULL) { XtWarning("Side: fatal malloc problem!"); } resize = True; redraw = True; } } /* * Check the width of the widget when the marker length changes. */ if (HtSideMarkLen(nw) != HtSideMarkLen(cw)) { if (HtSideWidth(nw) < HtSideMinWidth(nw)) HtCoreWidth(nw) = HtSideMinWidth(nw); resize = True; redraw = True; } /* * Use 'resize' to minimize the number of calls to Resize. */ if (resize == True) Resize(nw); /* * We have a change of visible side. */ if ((HtSidePos(nw)) != (HtSidePos(cw))) { /* * If the position changes, we need to redraw to position marker. * This could be done by returning 'True' from SetValues. However, * returning True from SetValues also causes the widget to be cleared * (XClearArea). Thus, if one moves the position by calling SetValues * repeatedly, this would result in a flickering effect. * Several ways of avoiding this exists, but no good (IMHO). If the user * uses the conveniance functions (or actions) provided, the flickering * will not occur. This is avoided through direct manipulation of the * widget's internal resources, and not through SetValues. Personally I * don't like this solution. All changes ought to be made via SetValues! * Another really, really ugly solution would be to make the redrawing * itself from SetValues - this is so horrible that I don't care to * comment further. (This option is enabled by a compiling flag, see * below...) * The last option is to return False from SetValues, and to explicitly * call the expose method ("Redraw", that is). This requires a transfer * of additional information to Redraw so it won't redraw everything. * This information could be transferred through internal (private) * widget variables. I tried this solution once, and it was ok. * The conclusion is: the best way to move the marker around is to use * the conveniance functions. Using SetValues results in a flickering * effect. */ #ifdef IRTY /* * If someone desperately needs to use XtSetValues to change * the position of the current marker, and doesn't want to have * the flickering effect (explained above) - use this define when * re-compiling. */ if ((redraw != True) && (XtIsRealized(nw)) && (HtSideVisible(nw))) { HtSideWidgetClass wc = (HtSideWidgetClass)XtClass(nw); if (wc->side_class.erase_current) (void)(*(wc->side_class.erase_current))((Widget)cw); if (wc->side_class.draw_current) (void)(*(wc->side_class.draw_current))((Widget)nw); } else redraw = True; #else /* IRTY */ redraw = True; #endif /* IRTY */ /* * Do not call callbacks if non-existent. */ if (XtHasCallbacks((Widget)nw, HtNselectCallback) == XtCallbackHasSome) { static htsideCallbackStruct cb; cb.reason = HtVOID; cb.position = HtSidePos(nw); XtCallCallbacks((Widget)nw, HtNselectCallback, &cb); } } return(redraw); } /* end SetValues */ static Boolean #ifdef NeedFunctionPrototypes Next(Widget w) #else Next(w) Widget w; #endif /* NeedFunctionPrototypes */ { HtSideWidget side = (HtSideWidget)w; HtSideWidgetClass wc = (HtSideWidgetClass)XtClass(w); #ifdef EBUG debug("Next"); #endif if (wc->side_class.go_to) return(*(wc->side_class.go_to))(w, (HtSidePos(side) + 1), HtNEXT); else return(False); } /* end Next */ static Boolean #ifdef NeedFunctionPrototypes Prev(Widget w) #else Prev(w) Widget w; #endif /* NeedFunctionPrototypes */ { HtSideWidget side = (HtSideWidget)w; HtSideWidgetClass wc = (HtSideWidgetClass)XtClass(w); #ifdef EBUG debug("Prev"); #endif if (wc->side_class.go_to) return(*(wc->side_class.go_to))(w, (HtSidePos(side) - 1), HtPREV); else return(False); } /* end Prev */ static Boolean #ifdef NeedFunctionPrototypes Goto(Widget w, int pos, HtReason reason) #else Goto(w, pos,reason) Widget w; int pos; HtReason reason; #endif /* NeedFunctionPrototypes */ { HtSideWidget side = (HtSideWidget)w; HtSideWidgetClass wc = (HtSideWidgetClass)XtClass(w); #ifdef EBUG debug("Goto"); #endif /* * Make sure position is correct. * Ignore requests that result in no change of position. */ if ((pos < 1) || (pos > HtSideMark(side)) || (HtSidePos(side) == pos)) { return(False); } /* * Erase old marker, re-position and draw the new one. */ if (wc->side_class.erase_current) (void)(*(wc->side_class.erase_current))(w); HtSidePos(side) = pos; if (wc->side_class.draw_current) (void)(*(wc->side_class.draw_current))(w); /* * Do not call callbacks if non-existent. */ if (XtHasCallbacks(w, HtNselectCallback) == XtCallbackHasSome) { static htsideCallbackStruct cb; cb.reason = reason; cb.position = HtSidePos(side); XtCallCallbacks(w, HtNselectCallback, &cb); } return(True); } /* end Goto */ static void #ifdef NeedFunctionPrototypes First(Widget w) #else First(w) Widget w; #endif /* NeedFunctionPrototypes */ { HtSideWidgetClass wc = (HtSideWidgetClass)XtClass(w); #ifdef EBUG debug("First"); #endif if (wc->side_class.go_to) (void)(*(wc->side_class.go_to))(w, 1, HtFIRST); } /* end First */ static void #ifdef NeedFunctionPrototypes Last(Widget w) #else Last(w) Widget w; #endif /* NeedFunctionPrototypes */ { HtSideWidget side = (HtSideWidget)w; HtSideWidgetClass wc = (HtSideWidgetClass)XtClass(w); #ifdef EBUG debug("Last"); #endif if (wc->side_class.go_to) (void)(*(wc->side_class.go_to))(w, HtSideMark(side), HtLAST); } /* end Last */ /* * Internal utility function. Used to find out which * makers that are 'hidden' by the current indicator. * Return values are stored in "min" and "max". * The markers hidden range from "min" to "max". */ static void #ifdef NeedFunctionPrototypes htFindMinAndMax(Widget w, int pos, int *min, int *max) #else htFindMinAndMax(w, pos, min, max) Widget w; int pos; int *min; int *max; #endif /* NeedFunctionPrototypes */ { int tmp; int i; XPoint *ptr; HtSideWidget side = (HtSideWidget)w; int y_pos = HtMarkerNrToPos(side, pos); #ifdef EBUG debug("htFindMinAndMax"); #endif /* * We want to find the top and bottom marker that lie * under the "current" indicator. This can vary a lot * if the Side widget is shrunk a lot. E.g. if we have * 100 markers, and the widget height is only 20 pixels. */ (*min) = 1; (*max) = HtSideMark(side); ptr = HtSideSeg(side); for (i = 0; i < HtSideMark(side); i++) { tmp = ptr++->y; /* y-coord for this pos */ if (tmp < (y_pos - 1)) (*min) = i; if (tmp <= (y_pos + 1)) (*max) = i+1; ptr++; } } /* end htFindMinAndMax */ /* * The only Action defined for the HtSide widget. */ static void #ifdef NeedFunctionPrototypes HtTraverse(Widget w, XEvent *event, String *params, Cardinal *nparams) #else HtTraverse(w, event, params, nparams) Widget w; XEvent *event; String *params; Cardinal *nparams; #endif { HtSideWidgetClass wc = (HtSideWidgetClass)XtClass(w); HtSideWidget side; XrmQuark q; static XrmQuark QPrev, QNext, QPrevFast, QNextFast, QPointer, QFirst, QLast; static Boolean haveQuarks = False; #ifdef EBUG debug("HtTraverse"); #endif XmProcessTraversal (w, XmTRAVERSE_CURRENT); /* * Get static quarks for the parmeters we understand. */ if (!haveQuarks) { QPrev = XrmPermStringToQuark("Prev"); QNext = XrmPermStringToQuark("Next"); QPrevFast = XrmPermStringToQuark("PrevFast"); QNextFast = XrmPermStringToQuark("NextFast"); QPointer = XrmPermStringToQuark("Pointer"); QFirst = XrmPermStringToQuark("First"); QLast = XrmPermStringToQuark("Last"); haveQuarks = True; } /* * Make sure it is a Side subclass. */ if (XtIsSubclass(w, htSideWidgetClass)) side = (HtSideWidget)w; else if (XtIsSubclass(XtParent(w), htSideWidgetClass)) side = (HtSideWidget)XtParent(w); else { XtAppWarningMsg(XtWidgetToApplicationContext(w), "HtTraverse", "badWidget", "HtSide", "HtSide: Bad widget passed to traverse action", NULL, 0); return; } wc = (HtSideWidgetClass)XtClass(w); /* * Make sure we have a single parmeter. */ if (*nparams != 1) { XtAppWarningMsg(XtWidgetToApplicationContext(w), "HtTraverse", "badParms", "HtSide", "HtSide: Wrong number of parameters passed to traverse action, needs 1", NULL, 0); return; } /* * Quarkify the string parameters. */ q = XrmStringToQuark(params[0]); if (q == QPointer) { int i, y_pos, new_pos = 0; if ((event->type == ButtonPress) || (event->type == MotionNotify)) { /* * Get position. */ y_pos = (int)(event->xbutton.y); /* * Make sure position is within the borders of the widget. */ if ((y_pos < (HtSideShadow(side) + HtSideHighlight(side))) || (y_pos >= (HtCoreHeight(side) - (HtSideShadow(side) + HtSideHighlight(side))))) return; /* * Get and set new position. Positions range from 1, not 0. */ for (i = 1; i <= HtSideMark(side); i++) { if ((HtMarkerAbove(i,side) < y_pos) && (HtMarkerBelow(i,side) >= y_pos)) { new_pos = i; } } /* * Position is y_pos, discrete position is new_pos. */ if (wc->side_class.go_to) (void)(*(wc->side_class.go_to))(w, new_pos, HtPOINTER); } } else if (q == QPrev) { if (wc->side_class.prev) (void)(*(wc->side_class.prev))(w); } else if (q == QNext) { if (wc->side_class.next) (void)(*(wc->side_class.next))(w); } else if (q == QPrevFast) { if (wc->side_class.prev) { (void)(*(wc->side_class.prev))(w); (void)(*(wc->side_class.prev))(w); } } else if (q == QNextFast) { if (wc->side_class.next) { (void)(*(wc->side_class.next))(w); (void)(*(wc->side_class.next))(w); } } else if (q == QFirst) { if (wc->side_class.first) (void)(*(wc->side_class.first))(w); } else if (q == QLast) { if (wc->side_class.last) (void)(*(wc->side_class.last))(w); } else { return; } } /* end HtTraverse */ /********************************************************************* * * Convenience functions. * *********************************************************************/ Boolean #ifdef NeedFunctionPrototypes HtSideNext(HtSideWidget w) #else HtSideNext(w) HtSideWidget w; #endif /* NeedFunctionPrototypes */ { HtSideWidgetClass wc = (HtSideWidgetClass)XtClass(w); if (wc->side_class.next) return((*(wc->side_class.next))((Widget)w)); else return(False); } /* end HtSideNext */ Boolean #ifdef NeedFunctionPrototypes HtSidePrev(HtSideWidget w) #else HtSidePrev(w) HtSideWidget w; #endif /* NeedFunctionPrototypes */ { HtSideWidgetClass wc = (HtSideWidgetClass)XtClass(w); if (wc->side_class.prev) return((*(wc->side_class.prev))((Widget)w)); else return(False); } /* end HtSidePrev */ void #ifdef NeedFunctionPrototypes HtSideFirst(HtSideWidget w) #else HtSideFirst(w) HtSideWidget w; #endif /* NeedFunctionPrototypes */ { HtSideWidgetClass wc = (HtSideWidgetClass)XtClass(w); if (wc->side_class.first) (void)((*(wc->side_class.first))((Widget)w)); } /* end HtSideFirst */ void #ifdef NeedFunctionPrototypes HtSideLast(HtSideWidget w) #else HtSideLast(w) HtSideWidget w; #endif /* NeedFunctionPrototypes */ { HtSideWidgetClass wc = (HtSideWidgetClass)XtClass(w); if (wc->side_class.last) (void)((*(wc->side_class.last))((Widget)w)); } /* end HtSideLast */ void #ifdef NeedFunctionPrototypes HtSidePosition(HtSideWidget w, int pos) #else HtSidePosition(w, pos) HtSideWidget w; int pos; #endif /* NeedFunctionPrototypes */ { HtSideWidgetClass wc = (HtSideWidgetClass)XtClass(w); if (wc->side_class.go_to) (void)(*(wc->side_class.go_to))((Widget)w, pos, HtVOID); } /* end HtSidePosition */ Widget #ifdef NeedFunctionPrototypes HtCreateSide(Widget parent, char *name, Arg *arglist, Cardinal argCount) #else HtCreateSide(parent, name, arglist, argCount) Widget parent; char *name; Arg *arglist; Cardinal argCount; #endif { return(XtCreateWidget(name, htSideWidgetClass, parent, arglist, argCount)); } /* end HtCreateSide */ /********************************************************************* * * THE END. * *********************************************************************/