--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/open-src/lib/libXaw4/sun-src/Xaw3_1TextSink.c Wed Jul 15 16:27:02 2009 -0700
@@ -0,0 +1,734 @@
+#if ( !defined(lint) && !defined(SABER) )
+static char Xrcsid[] = "$XConsortium: TextSink.c,v 1.9 89/11/21 15:53:22 swick Exp $";
+#endif
+
+/*
+ * Copyright 1989 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. M.I.T. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Peterson, MIT X Consortium.
+ *
+ * Much code taken from X11R3 AsciiSink.
+ */
+
+/*
+ * TextSink.c - TextSink object. (For use with the text widget).
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+#include <./Xaw3_1XawInit.h>
+#include <./Xaw3_1TextSinkP.h>
+#include <./Xaw3_1TextP.h>
+
+/****************************************************************
+ *
+ * Full class record constant
+ *
+ ****************************************************************/
+
+static void ClassPartInitialize(), Initialize(), Destroy();
+static Boolean SetValues();
+
+static int MaxHeight(), MaxLines();
+static void DisplayText(), InsertCursor(), ClearToBackground(), FindPosition();
+static void FindDistance(), Resolve(), SetTabs(), GetCursorBounds();
+
+#define offset(field) XtOffset(TextSinkObject, text_sink.field)
+static XtResource resources[] = {
+ {XtNfont, XtCFont, XtRFontStruct, sizeof (XFontStruct *),
+ offset(font), XtRString, XtDefaultFont},
+ {XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
+ offset(foreground), XtRString, XtDefaultForeground},
+ {XtNbackground, XtCBackground, XtRPixel, sizeof (Pixel),
+ offset(background), XtRString, XtDefaultBackground},
+};
+#undef offset
+
+#define SuperClass (&objectClassRec)
+TextSinkClassRec textSinkClassRec = {
+ {
+/* core_class fields */
+ /* superclass */ (WidgetClass) SuperClass,
+ /* class_name */ "TextSink",
+ /* widget_size */ sizeof(TextSinkRec),
+ /* class_initialize */ XawInitializeWidgetSet,
+ /* class_part_initialize */ ClassPartInitialize,
+ /* class_inited */ FALSE,
+ /* initialize */ Initialize,
+ /* initialize_hook */ NULL,
+ /* obj1 */ NULL,
+ /* obj2 */ NULL,
+ /* obj3 */ 0,
+ /* resources */ resources,
+ /* num_resources */ XtNumber(resources),
+ /* xrm_class */ NULLQUARK,
+ /* obj4 */ FALSE,
+ /* obj5 */ FALSE,
+ /* obj6 */ FALSE,
+ /* obj7 */ FALSE,
+ /* destroy */ Destroy,
+ /* obj8 */ NULL,
+ /* obj9 */ NULL,
+ /* set_values */ SetValues,
+ /* set_values_hook */ NULL,
+ /* obj10 */ NULL,
+ /* get_values_hook */ NULL,
+ /* obj11 */ NULL,
+ /* version */ XtVersion,
+ /* callback_private */ NULL,
+ /* obj12 */ NULL,
+ /* obj13 */ NULL,
+ /* obj14 */ NULL,
+ /* extension */ NULL
+ },
+/* textSink_class fields */
+ {
+ /* DisplayText */ DisplayText,
+ /* InsertCursor */ InsertCursor,
+ /* ClearToBackground */ ClearToBackground,
+ /* FindPosition */ FindPosition,
+ /* FindDistance */ FindDistance,
+ /* Resolve */ Resolve,
+ /* MaxLines */ MaxLines,
+ /* MaxHeight */ MaxHeight,
+ /* SetTabs */ SetTabs,
+ /* GetCursorBounds */ GetCursorBounds,
+ }
+};
+
+WidgetClass textSinkObjectClass = (WidgetClass)&textSinkClassRec;
+
+static void
+ClassPartInitialize(wc)
+WidgetClass wc;
+{
+ register TextSinkObjectClass t_src, superC;
+
+ t_src = (TextSinkObjectClass) wc;
+ superC = (TextSinkObjectClass) t_src->object_class.superclass;
+
+/*
+ * We don't need to check for null super since we'll get to TextSink
+ * eventually.
+ */
+
+ if (t_src->text_sink_class.DisplayText == XtInheritDisplayText)
+ t_src->text_sink_class.DisplayText = superC->text_sink_class.DisplayText;
+
+ if (t_src->text_sink_class.InsertCursor == XtInheritInsertCursor)
+ t_src->text_sink_class.InsertCursor =
+ superC->text_sink_class.InsertCursor;
+
+ if (t_src->text_sink_class.ClearToBackground== XtInheritClearToBackground)
+ t_src->text_sink_class.ClearToBackground =
+ superC->text_sink_class.ClearToBackground;
+
+ if (t_src->text_sink_class.FindPosition == XtInheritFindPosition)
+ t_src->text_sink_class.FindPosition =
+ superC->text_sink_class.FindPosition;
+
+ if (t_src->text_sink_class.FindDistance == XtInheritFindDistance)
+ t_src->text_sink_class.FindDistance =
+ superC->text_sink_class.FindDistance;
+
+ if (t_src->text_sink_class.Resolve == XtInheritResolve)
+ t_src->text_sink_class.Resolve = superC->text_sink_class.Resolve;
+
+ if (t_src->text_sink_class.MaxLines == XtInheritMaxLines)
+ t_src->text_sink_class.MaxLines = superC->text_sink_class.MaxLines;
+
+ if (t_src->text_sink_class.MaxHeight == XtInheritMaxHeight)
+ t_src->text_sink_class.MaxHeight = superC->text_sink_class.MaxHeight;
+
+ if (t_src->text_sink_class.SetTabs == XtInheritSetTabs)
+ t_src->text_sink_class.SetTabs = superC->text_sink_class.SetTabs;
+
+ if (t_src->text_sink_class.GetCursorBounds == XtInheritGetCursorBounds)
+ t_src->text_sink_class.GetCursorBounds =
+ superC->text_sink_class.GetCursorBounds;
+}
+
+/* Function Name: Initialize
+ * Description: Initializes the TextSink Object.
+ * Arguments: request, new - the requested and new values for the object
+ * instance.
+ * Returns: none.
+ *
+ */
+
+/* ARGSUSED */
+static void
+Initialize(request, new)
+Widget request, new;
+{
+ TextSinkObject sink = (TextSinkObject) new;
+
+ sink->text_sink.tab_count = 0; /* Initialize the tab stops. */
+ sink->text_sink.tabs = NULL;
+ sink->text_sink.char_tabs = NULL;
+}
+
+/* Function Name: Destroy
+ * Description: This function cleans up when the object is
+ * destroyed.
+ * Arguments: w - the TextSink Object.
+ * Returns: none.
+ */
+
+static void
+Destroy(w)
+{
+ TextSinkObject sink = (TextSinkObject) w;
+
+ if (sink->text_sink.tabs != NULL)
+ XtFree((char *) sink->text_sink.tabs);
+}
+
+/* Function Name: SetValues
+ * Description: Sets the values for the TextSink
+ * Arguments: current - current state of the object.
+ * request - what was requested.
+ * new - what the object will become.
+ * Returns: True if redisplay is needed.
+ */
+
+/* ARGSUSED */
+static Boolean
+SetValues(current, request, new)
+Widget current, request, new;
+{
+ TextSinkObject w = (TextSinkObject) new;
+ TextSinkObject old_w = (TextSinkObject) current;
+ TextSinkObjectClass class = (TextSinkObjectClass) w->object.widget_class;
+
+ if (w->text_sink.font != old_w->text_sink.font) {
+ (*class->text_sink_class.SetTabs)(new, w->text_sink.tab_count,
+ w->text_sink.char_tabs);
+ ((TextWidget)XtParent(new))->text.redisplay_needed = True;
+ } else {
+ if (w->text_sink.foreground != old_w->text_sink.foreground)
+ ((TextWidget)XtParent(new))->text.redisplay_needed = True;
+ }
+
+ return FALSE;
+}
+
+/************************************************************
+ *
+ * Class specific methods.
+ *
+ ************************************************************/
+
+/* Function Name: DisplayText
+ * Description: Stub function that in subclasses will display text.
+ * Arguments: w - the TextSink Object.
+ * x, y - location to start drawing text.
+ * pos1, pos2 - location of starting and ending points
+ * in the text buffer.
+ * highlight - hightlight this text?
+ * Returns: none.
+ *
+ * This function doesn't actually display anything, it is only a place
+ * holder.
+ */
+
+/* ARGSUSED */
+static void
+DisplayText(w, x, y, pos1, pos2, highlight)
+Widget w;
+Position x, y;
+Boolean highlight;
+XawTextPosition pos1, pos2;
+{
+ return;
+}
+
+/* Function Name: InsertCursor
+ * Description: Places the InsertCursor.
+ * Arguments: w - the TextSink Object.
+ * x, y - location for the cursor.
+ * staye - whether to turn the cursor on, or off.
+ * Returns: none.
+ *
+ * This function doesn't actually display anything, it is only a place
+ * holder.
+ */
+
+/* ARGSUSED */
+static void
+InsertCursor(w, x, y, state)
+Widget w;
+Position x, y;
+XawTextInsertState state;
+{
+ return;
+}
+
+/* Function Name: ClearToBackground
+ * Description: Clears a region of the sink to the background color.
+ * Arguments: w - the TextSink Object.
+ * x, y - location of area to clear.
+ * width, height - size of area to clear
+ * Returns: void.
+ *
+ */
+
+/* ARGSUSED */
+static void
+ClearToBackground (w, x, y, width, height)
+Widget w;
+Position x, y;
+Dimension width, height;
+{
+/*
+ * Don't clear in height or width are zero.
+ * XClearArea() has special semantic for these values.
+ */
+
+ if ( (height == 0) || (width == 0) ) return;
+ XClearArea(XtDisplayOfObject(w), XtWindowOfObject(w),
+ x, y, width, height, False);
+}
+
+/* Function Name: FindPosition
+ * Description: Finds a position in the text.
+ * Arguments: w - the TextSink Object.
+ * fromPos - reference position.
+ * fromX - reference location.
+ * width, - width of section to paint text.
+ * stopAtWordBreak - returned position is a word break?
+ * resPos - Position to return. *** RETURNED ***
+ * resWidth - Width actually used. *** RETURNED ***
+ * resHeight - Height actually used. *** RETURNED ***
+ * Returns: none (see above).
+ */
+
+/* ARGSUSED */
+static void
+FindPosition(w, fromPos, fromx, width, stopAtWordBreak,
+ resPos, resWidth, resHeight)
+Widget w;
+XawTextPosition fromPos;
+int fromx, width;
+Boolean stopAtWordBreak;
+XawTextPosition *resPos;
+int *resWidth, *resHeight;
+{
+ *resPos = fromPos;
+ *resHeight = *resWidth = 0;
+}
+
+/* Function Name: FindDistance
+ * Description: Find the Pixel Distance between two text Positions.
+ * Arguments: w - the TextSink Object.
+ * fromPos - starting Position.
+ * fromX - x location of starting Position.
+ * toPos - end Position.
+ * resWidth - Distance between fromPos and toPos.
+ * resPos - Acutal toPos used.
+ * resHeight - Height required by this text.
+ * Returns: none.
+ */
+
+/* ARGSUSED */
+static void
+FindDistance (w, fromPos, fromx, toPos, resWidth, resPos, resHeight)
+Widget w;
+XawTextPosition fromPos;
+int fromx;
+XawTextPosition toPos;
+int *resWidth;
+XawTextPosition *resPos;
+int *resHeight;
+{
+ *resWidth = *resHeight = 0;
+ *resPos = fromPos;
+}
+
+/* Function Name: Resolve
+ * Description: Resloves a location to a position.
+ * Arguments: w - the TextSink Object.
+ * pos - a reference Position.
+ * fromx - a reference Location.
+ * width - width to move.
+ * resPos - the resulting position.
+ * Returns: none
+ */
+
+/* ARGSUSED */
+static void
+Resolve (w, pos, fromx, width, resPos)
+Widget w;
+XawTextPosition pos;
+int fromx, width;
+XawTextPosition *resPos;
+{
+ *resPos = pos;
+}
+
+/* Function Name: MaxLines
+ * Description: Finds the Maximum number of lines that will fit in
+ * a given height.
+ * Arguments: w - the TextSink Object.
+ * height - height to fit lines into.
+ * Returns: the number of lines that will fit.
+ */
+
+/* ARGSUSED */
+static int
+MaxLines(w, height)
+Widget w;
+Dimension height;
+{
+ TextSinkObject sink = (TextSinkObject) w;
+ int font_height;
+
+ font_height = sink->text_sink.font->ascent + sink->text_sink.font->descent;
+ return( ((int) height) / font_height );
+}
+
+/* Function Name: MaxHeight
+ * Description: Finds the Minium height that will contain a given number
+ * lines.
+ * Arguments: w - the TextSink Object.
+ * lines - the number of lines.
+ * Returns: the height.
+ */
+
+/* ARGSUSED */
+static int
+MaxHeight(w, lines)
+Widget w;
+int lines;
+{
+ TextSinkObject sink = (TextSinkObject) w;
+
+ return(lines * (sink->text_sink.font->ascent +
+ sink->text_sink.font->descent));
+}
+
+/* Function Name: SetTabs
+ * Description: Sets the Tab stops.
+ * Arguments: w - the TextSink Object.
+ * tab_count - the number of tabs in the list.
+ * tabs - the text positions of the tabs.
+ * Returns: none
+ */
+
+static void
+SetTabs(w, tab_count, tabs)
+Widget w;
+int tab_count;
+short *tabs;
+{
+ TextSinkObject sink = (TextSinkObject) w;
+ int i;
+ Atom XA_FIGURE_WIDTH;
+ unsigned long figure_width = 0;
+ XFontStruct *font = sink->text_sink.font;
+
+/*
+ * Find the figure width of the current font.
+ */
+
+ XA_FIGURE_WIDTH = XInternAtom(XtDisplayOfObject(w), "FIGURE_WIDTH", FALSE);
+ if ( (XA_FIGURE_WIDTH != NULL) &&
+ ( (!XGetFontProperty(font, XA_FIGURE_WIDTH, &figure_width)) ||
+ (figure_width == 0)) )
+ if (font->per_char && font->min_char_or_byte2 <= '$' &&
+ font->max_char_or_byte2 >= '$')
+ figure_width = font->per_char['$' - font->min_char_or_byte2].width;
+ else
+ figure_width = font->max_bounds.width;
+
+ if (tab_count > sink->text_sink.tab_count) {
+ sink->text_sink.tabs = (Position *) XtRealloc((caddr_t)sink->text_sink.tabs,
+ (Cardinal) (tab_count * sizeof(Position)));
+ sink->text_sink.char_tabs = (short *) XtRealloc(
+ (caddr_t) sink->text_sink.char_tabs,
+ (Cardinal) (tab_count * sizeof(short)));
+ }
+
+ for ( i = 0 ; i < tab_count ; i++ ) {
+ sink->text_sink.tabs[i] = tabs[i] * figure_width;
+ sink->text_sink.char_tabs[i] = tabs[i];
+ }
+
+ sink->text_sink.tab_count = tab_count;
+}
+
+/* Function Name: GetCursorBounds
+ * Description: Finds the bounding box for the insert curor (caret).
+ * Arguments: w - the TextSinkObject.
+ * rect - an X rectance containing the cursor bounds.
+ * Returns: none (fills in rect).
+ */
+
+/* ARGSUSED */
+static void
+GetCursorBounds(w, rect)
+Widget w;
+XRectangle * rect;
+{
+ rect->x = rect->y = rect->width = rect->height = 0;
+}
+/************************************************************
+ *
+ * Public Functions.
+ *
+ ************************************************************/
+
+
+/* Function Name: XawTextSinkDisplayText
+ * Description: Stub function that in subclasses will display text.
+ * Arguments: w - the TextSink Object.
+ * x, y - location to start drawing text.
+ * pos1, pos2 - location of starting and ending points
+ * in the text buffer.
+ * highlight - hightlight this text?
+ * Returns: none.
+ *
+ * This function doesn't actually display anything, it is only a place
+ * holder.
+ */
+
+/* ARGSUSED */
+void
+XawTextSinkDisplayText(w, x, y, pos1, pos2, highlight)
+Widget w;
+Position x, y;
+Boolean highlight;
+XawTextPosition pos1, pos2;
+{
+ TextSinkObjectClass class = (TextSinkObjectClass) w->core.widget_class;
+
+ (*class->text_sink_class.DisplayText)(w, x, y, pos1, pos2, highlight);
+}
+
+/* Function Name: XawTextSinkInsertCursor
+ * Description: Places the InsertCursor.
+ * Arguments: w - the TextSink Object.
+ * x, y - location for the cursor.
+ * staye - whether to turn the cursor on, or off.
+ * Returns: none.
+ *
+ * This function doesn't actually display anything, it is only a place
+ * holder.
+ */
+
+/* ARGSUSED */
+void
+XawTextSinkInsertCursor(w, x, y, state)
+Widget w;
+Position x, y;
+XawTextInsertState state;
+{
+ TextSinkObjectClass class = (TextSinkObjectClass) w->core.widget_class;
+
+ (*class->text_sink_class.InsertCursor)(w, x, y, state);
+}
+
+
+/* Function Name: XawTextSinkClearToBackground
+ * Description: Clears a region of the sink to the background color.
+ * Arguments: w - the TextSink Object.
+ * x, y - location of area to clear.
+ * width, height - size of area to clear
+ * Returns: void.
+ *
+ * This function doesn't actually display anything, it is only a place
+ * holder.
+ */
+
+/* ARGSUSED */
+void
+XawTextSinkClearToBackground (w, x, y, width, height)
+Widget w;
+Position x, y;
+Dimension width, height;
+{
+ TextSinkObjectClass class = (TextSinkObjectClass) w->core.widget_class;
+
+ (*class->text_sink_class.ClearToBackground)(w, x, y, width, height);
+}
+
+/* Function Name: XawTextSinkFindPosition
+ * Description: Finds a position in the text.
+ * Arguments: w - the TextSink Object.
+ * fromPos - reference position.
+ * fromX - reference location.
+ * width, - width of section to paint text.
+ * stopAtWordBreak - returned position is a word break?
+ * resPos - Position to return. *** RETURNED ***
+ * resWidth - Width actually used. *** RETURNED ***
+ * resHeight - Height actually used. *** RETURNED ***
+ * Returns: none (see above).
+ */
+
+/* ARGSUSED */
+void
+XawTextSinkFindPosition(w, fromPos, fromx, width, stopAtWordBreak,
+ resPos, resWidth, resHeight)
+Widget w;
+XawTextPosition fromPos;
+int fromx, width;
+Boolean stopAtWordBreak;
+XawTextPosition *resPos;
+int *resWidth, *resHeight;
+{
+ TextSinkObjectClass class = (TextSinkObjectClass) w->core.widget_class;
+
+ (*class->text_sink_class.FindPosition)(w, fromPos, fromx, width,
+ stopAtWordBreak,
+ resPos, resWidth, resHeight);
+}
+
+/* Function Name: XawTextSinkFindDistance
+ * Description: Find the Pixel Distance between two text Positions.
+ * Arguments: w - the TextSink Object.
+ * fromPos - starting Position.
+ * fromX - x location of starting Position.
+ * toPos - end Position.
+ * resWidth - Distance between fromPos and toPos.
+ * resPos - Acutal toPos used.
+ * resHeight - Height required by this text.
+ * Returns: none.
+ */
+
+/* ARGSUSED */
+void
+XawTextSinkFindDistance (w, fromPos, fromx, toPos, resWidth, resPos, resHeight)
+Widget w;
+XawTextPosition fromPos, toPos, *resPos;
+int fromx, *resWidth, *resHeight;
+{
+ TextSinkObjectClass class = (TextSinkObjectClass) w->core.widget_class;
+
+ (*class->text_sink_class.FindDistance)(w, fromPos, fromx, toPos,
+ resWidth, resPos, resHeight);
+}
+
+/* Function Name: XawTextSinkResolve
+ * Description: Resloves a location to a position.
+ * Arguments: w - the TextSink Object.
+ * pos - a reference Position.
+ * fromx - a reference Location.
+ * width - width to move.
+ * resPos - the resulting position.
+ * Returns: none
+ */
+
+/* ARGSUSED */
+void
+XawTextSinkResolve(w, pos, fromx, width, resPos)
+Widget w;
+XawTextPosition pos;
+int fromx, width;
+XawTextPosition *resPos;
+{
+ TextSinkObjectClass class = (TextSinkObjectClass) w->core.widget_class;
+
+ (*class->text_sink_class.Resolve)(w, pos, fromx, width, resPos);
+}
+
+/* Function Name: XawTextSinkMaxLines
+ * Description: Finds the Maximum number of lines that will fit in
+ * a given height.
+ * Arguments: w - the TextSink Object.
+ * height - height to fit lines into.
+ * Returns: the number of lines that will fit.
+ */
+
+/* ARGSUSED */
+int
+XawTextSinkMaxLines(w, height)
+Widget w;
+Dimension height;
+{
+ TextSinkObjectClass class = (TextSinkObjectClass) w->core.widget_class;
+
+ return((*class->text_sink_class.MaxLines)(w, height));
+}
+
+/* Function Name: XawTextSinkMaxHeight
+ * Description: Finds the Minium height that will contain a given number
+ * lines.
+ * Arguments: w - the TextSink Object.
+ * lines - the number of lines.
+ * Returns: the height.
+ */
+
+/* ARGSUSED */
+int
+XawTextSinkMaxHeight(w, lines)
+Widget w;
+int lines;
+{
+ TextSinkObjectClass class = (TextSinkObjectClass) w->core.widget_class;
+
+ return((*class->text_sink_class.MaxHeight)(w, lines));
+}
+
+/* Function Name: XawTextSinkSetTabs
+ * Description: Sets the Tab stops.
+ * Arguments: w - the TextSink Object.
+ * tab_count - the number of tabs in the list.
+ * tabs - the text positions of the tabs.
+ * Returns: none
+ */
+
+void
+XawTextSinkSetTabs(w, tab_count, tabs)
+Widget w;
+int tab_count, *tabs;
+{
+ if (tab_count > 0) {
+ TextSinkObjectClass class = (TextSinkObjectClass) w->core.widget_class;
+ short *char_tabs = (short*)XtMalloc( (unsigned)tab_count*sizeof(short) );
+ register short *tab;
+ register int i;
+
+ for (i = tab_count, tab = char_tabs; i; i--) *tab++ = (short)*tabs++;
+
+ (*class->text_sink_class.SetTabs)(w, tab_count, char_tabs);
+ XtFree((XtPointer)char_tabs);
+ }
+}
+
+/* Function Name: XawTextSinkGetCursorBounds
+ * Description: Finds the bounding box for the insert curor (caret).
+ * Arguments: w - the TextSinkObject.
+ * rect - an X rectance containing the cursor bounds.
+ * Returns: none (fills in rect).
+ */
+
+/* ARGSUSED */
+void
+XawTextSinkGetCursorBounds(w, rect)
+Widget w;
+XRectangle * rect;
+{
+ TextSinkObjectClass class = (TextSinkObjectClass) w->core.widget_class;
+
+ (*class->text_sink_class.GetCursorBounds)(w, rect);
+}