/************************************************************************** * * * Copyright (C) 1991, Silicon Graphics, Inc. * * All Rights Reserved. * * * * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.; * * the contents of this file may not be disclosed to third parties, * * copied or duplicated in any form, in whole or in part, without the * * prior written permission of Silicon Graphics, Inc. * * * * RESTRICTED RIGHTS LEGEND: * * Use, duplication or disclosure by the Government is subject to * * restrictions as set forth in subdivision (c)(1)(ii) of the Rights * * in Technical Data and Computer Software clause at DFARS * * 252.227-7013, and/or in similar or successor clauses in the FAR, * * DOD or NASA FAR Supplement. Unpublished - rights reserved under * * the Copyright Laws of the United States. * ************************************************************************** * * File: TextView.c * * Description: Implementation file for the TextView multifont, file * text viewing widget. The basis for this widget comes from Doug * Young's 'fileviewer.c' example program in his book. * **************************************************************************/ #ident "$Revision: 1.4 $" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Formatter.h" #include "TextViewP.h" /* Amount of overlap (lines) in page up/down */ #define PAGE_OVERLAP 2 /* Number of milliseconds before redrawing text during a scrollbar drag */ #define SCROLL_DELAY 100 /* Text selection tokens */ #define SELECT 1 /* Select text */ #define DESELECT 0 /* Deselect text */ #define END_CHAR 1e+6 /* Position at right end of line */ #define LINE_END 2e+6 /* Position passed right end of line */ #define TEXT_END 1e+6 /* Position passed bottom of text */ #define BLANK_LINE_XSEL 10 /* Empty line select x position */ /* Keyboard navigation tokens */ #define FIRST_PAGE 0 #define LAST_PAGE 1 #define LINE_UP 2 #define LINE_DOWN 3 #define PAGE_UP 4 #define PAGE_DOWN 5 #define COL_LEFT 6 #define COL_RIGHT 7 /* Resource macros */ #define DEFARGS(num) Arg _args[num]; register int _n #define STARTARGS _n = 0 #define SETARG(r,v) XtSetArg(_args[_n], r, v); _n++ #define ARGLIST _args, _n /* Widget resouce list */ #define offset(field) XtOffsetOf(VwrTextViewRec, field) static XtResource resources[] = { /* Columns of text */ { VwrNtextColumns, VwrCTextColumns, XtRInt, sizeof(int), offset(textView.text_columns), XtRImmediate, (XtPointer)DEF_TEXT_COLS }, /* Rows of text */ { VwrNtextRows, VwrCTextRows, XtRInt, sizeof(int), offset(textView.text_rows), XtRImmediate, (XtPointer)DEF_TEXT_ROWS }, /* Normal font */ { VwrNnormFont, XtCFont, XtRFontStruct, sizeof(XFontStruct *), offset(textView.styles[NORMAL_STYLE].font), XtRString, DEF_NORM_FONT }, /* Underline font */ { VwrNunderlineFont, XtCFont, XtRFontStruct, sizeof(XFontStruct *), offset(textView.styles[UNDERLINE_STYLE].font), XtRString, DEF_UNDERLINE_FONT }, /* Bold font */ { VwrNboldFont, XtCFont, XtRFontStruct, sizeof(XFontStruct *), offset(textView.styles[BOLD_STYLE].font), XtRString, DEF_BOLD_FONT }, /* Bold-underline font */ { VwrNboldUnderFont, XtCFont, XtRFontStruct, sizeof(XFontStruct *), offset(textView.styles[BOLDUNDER_STYLE].font), XtRString, DEF_BOLDUNDER_FONT }, /* Normal text foreground color */ { VwrNnormFore, XtCForeground, XtRPixel, sizeof(Pixel), offset(textView.styles[NORMAL_STYLE].fore), XtRImmediate, (XtPointer)DEF_NORM_FORE }, /* Underline text foreground color */ { VwrNunderlineFore, XtCForeground, XtRPixel, sizeof(Pixel), offset(textView.styles[UNDERLINE_STYLE].fore), XtRImmediate, (XtPointer)DEF_UNDERLINE_FORE }, /* Bold text foreground color */ { VwrNboldFore, XtCForeground, XtRPixel, sizeof(Pixel), offset(textView.styles[BOLD_STYLE].fore), XtRImmediate, (XtPointer)DEF_BOLD_FORE }, /* Bold-underline text foreground color */ { VwrNboldUnderFore, XtCForeground, XtRPixel, sizeof(Pixel), offset(textView.styles[BOLDUNDER_STYLE].fore), XtRImmediate, (XtPointer)DEF_BOLDUNDER_FORE }, /* Selection foreground color */ { VwrNselectionFore, XtCBackground, XtRPixel, sizeof(Pixel), offset(textView.select_fore), XtRImmediate, (XtPointer)DEF_SELECT_FORE }, /* Selection background color */ { VwrNselectionBack, XtCForeground, XtRPixel, sizeof(Pixel), offset(textView.select_back), XtRImmediate, (XtPointer)DEF_SELECT_BACK }, /* Blank line compression */ { VwrNblankCompress, VwrCBlankCompress, XtRInt, sizeof(int), offset(textView.blank_compress), XtRImmediate, (XtPointer)DEF_BLANK_COMPRESS }, /* Text file to view */ { VwrNtextFile, VwrCTextFile, XtRString, sizeof(char*), offset(textView.filename), XtRImmediate, (XtPointer)NULL }, /* Vertical scrollbar to manage */ { VwrNverticalScrollBar, VwrCScrollBar, XtRWidget, sizeof(Widget), offset(textView.vsb), XtRImmediate, (XtPointer)NULL }, /* Horizontal scrollbar to manage */ { VwrNhorizontalScrollBar, VwrCScrollBar, XtRWidget, sizeof(Widget), offset(textView.hsb), XtRImmediate, (XtPointer)NULL }, /* Load Viewer callbacks */ { VwrNloadCallback, VwrCLoadCallback, XtRCallback, sizeof(XtPointer), offset(textView.load_callback), XtRCallback, (XtPointer)NULL }, }; /* Action function declarations */ static void first_page(Widget, XEvent*, String*, Cardinal*); static void last_page(Widget, XEvent*, String*, Cardinal*); static void line_up(Widget, XEvent*, String*, Cardinal*); static void line_down(Widget, XEvent*, String*, Cardinal*); static void col_left(Widget, XEvent*, String*, Cardinal*); static void col_right(Widget, XEvent*, String*, Cardinal*); static void page_up(Widget, XEvent*, String*, Cardinal*); static void page_down(Widget, XEvent*, String*, Cardinal*); static void next_tgroup(Widget, XEvent*, String*, Cardinal*); static void prev_tgroup(Widget, XEvent*, String*, Cardinal*); static void start_select(Widget, XEvent*, String*, Cardinal*); static void drag_select(Widget, XEvent*, String*, Cardinal*); static void complete_select(Widget, XEvent*, String*, Cardinal*); /* Translation table and action table definitions */ static char def_translations[] = ": StartSelect() \n\ : DragSelect() \n\ : CompleteSelect()\n\ Tab: NextTabGroup() \n\ CtrlTab: NextTabGroup() \n\ ShiftTab: PrevTabGroup() \n\ Ctrl ShiftTab: PrevTabGroup() \n\ osfBeginLine: FirstPage() \n\ KP_Home: FirstPage() \n\ KP_7: FirstPage() \n\ osfEndLine: LastPage() \n\ KP_End: LastPage() \n\ KP_1: LastPage() \n\ osfUp: LineUp() \n\ KP_Up: LineUp() \n\ KP_8: LineUp() \n\ osfDown: LineDown() \n\ KP_Down: LineDown() \n\ KP_2: LineDown() \n\ osfLeft: ColLeft() \n\ KP_Left: ColLeft() \n\ KP_4: ColLeft() \n\ osfRight: ColRight() \n\ KP_Right: ColRight() \n\ KP_6: ColRight() \n\ osfPageUp: PageUp() \n\ KP_Prior: PageUp() \n\ KP_9: PageUp() \n\ osfPageDown: PageDown() \n\ KP_Next: PageDown() \n\ KP_3: PageDown() \n\ space: PageDown()"; static XtActionsRec actions[] = { {"FirstPage", first_page}, {"LastPage", last_page}, {"LineUp", line_up}, {"LineDown", line_down}, {"ColLeft", col_left}, {"ColRight", col_right}, {"PageUp", page_up}, {"PageDown", page_down}, {"NextTabGroup", next_tgroup}, {"PrevTabGroup", prev_tgroup}, {"StartSelect", start_select}, {"DragSelect", drag_select}, {"CompleteSelect", complete_select}, }; /* Widget method declarations */ static void initialize(Widget, Widget, ArgList, Cardinal*); static XtGeometryResult query_geometry(Widget, XtWidgetGeometry*, XtWidgetGeometry*); static void expose(Widget, XEvent*, Region); static Boolean set_values(Widget, Widget, Widget, ArgList, Cardinal*); static void resize(Widget); static void realize(Widget, XtValueMask*, XSetWindowAttributes*); static void destroy(Widget); /* Widget private method declarations */ static void get_font_info(VwrTextViewPart*); static void create_gcs(VwrTextViewWidget); static void load_viewer(VwrTextViewWidget); static void clear_line(Display*, Window, VwrTextViewWidget, int, int, int); static void draw_line(Display*, Window, VwrTextViewWidget, int, int, int); static void vsb_dragged(Widget, VwrTextViewWidget, XmScrollBarCallbackStruct*); static void vsb_moved(Widget, VwrTextViewWidget, XmScrollBarCallbackStruct*); static void vert_scroll_text(VwrTextViewWidget, XtIntervalId*); static void horiz_scroll_text(Widget, VwrTextViewWidget, XmScrollBarCallbackStruct*); static void vsb_callbacks(VwrTextViewWidget); static void hsb_callbacks(VwrTextViewWidget); static void handle_keyboard(VwrTextViewWidget, int); static void goto_line(VwrTextViewWidget, int); static Boolean transfer_selection(Widget, Atom*, Atom*, Atom*, XtPointer*, unsigned long*, int*); static void transfer_complete(Widget, Atom*, Atom*); static char* get_selection(VwrTextViewPart*, unsigned long*); static void lose_selection(Widget, Atom*); static void xy2lc(VwrTextViewPart*, int, int, int*, int*); static void lc2seg(VwrTextViewPart*, int, int, vwr_text_seg_t**, int*); static int y2l(VwrTextViewPart*, int); static int x2c(VwrTextViewPart*, int, int); static int selcmp(VwrPositionStruct*, VwrPositionStruct*); static void highlight(VwrTextViewWidget, VwrPositionStruct*, VwrPositionStruct*, int); static vwr_text_seg_t* get_seg(VwrTextViewPart*, VwrPositionStruct*, int, vwr_text_line_t*); static Boolean select_segs(vwr_text_seg_t*, vwr_text_seg_t*, int); static void draw_segs(VwrTextViewWidget, int, vwr_text_seg_t*, vwr_text_seg_t*, int); static void compact_line(VwrTextViewPart*, int); static void lc_bounds(VwrTextViewPart*, VwrPositionStruct*); static void init_selection(VwrTextViewPart*); static void parse_escapes(register char*, register char*); /* External declarations */ extern char *__loc1; /* Callback data */ static VwrTextViewLoadCallbackStruct load_callback_data; /* Class record intialization */ VwrTextViewClassRec vwrTextViewClassRec = { { /* Core class fields */ /* superclass */ (WidgetClass) &xmPrimitiveClassRec, /* class_name */ "TextView", /* widget_size */ sizeof(VwrTextViewRec), /* class_initialize */ NULL, /* class_part_initialize */ NULL, /* class_inited */ False, /* initialize */ initialize, /* initialize_hook */ NULL, /* realize */ realize, /* actions */ actions, /* num_actions */ XtNumber(actions), /* resources */ resources, /* num_resources */ XtNumber(resources), /* xrm_class */ NULLQUARK, /* compress_motion */ True, /* compress_exposure */ XtExposeCompressMultiple, /* compress_enterleave */ True, /* visible_interest */ False, /* destroy */ destroy, /* resize */ resize, /* expose */ expose, /* set_values */ set_values, /* set_values_hook */ NULL, /* set_values_almost */ XtInheritSetValuesAlmost, /* get_values_hook */ NULL, /* accept_focus */ NULL, /* version */ XtVersion, /* callback_private */ NULL, /* tm_table */ def_translations, /* query_geometry */ query_geometry, /* display_accelerator */ XtInheritDisplayAccelerator, /* extension */ NULL, }, { /* Primitive class fields */ /* border_highlight */ (XtWidgetProc)_XtInherit, /* border_unhighlight */ (XtWidgetProc)_XtInherit, /* translations */ def_translations, /* arm_and_activate */ NULL, /* syn_resources */ NULL, /* num_syn_resources */ 0, /* extension */ NULL, }, { /* TextView class fields */ /* extension */ NULL, }, }; WidgetClass vwrTextViewWidgetClass = (WidgetClass) &vwrTextViewClassRec; /************************************************************************** * * Function: initialize * * Description: Widget intialization method * * Parameters: * treq (I) - widget as set by arg list, resource database and defaults * tnew (I) - new widget * args (I) - argument list in Va creation * num_args (I) - number of arguments * * * Return: none * **************************************************************************/ /* ARGSUSED */ static void initialize(Widget treq, Widget tnew, ArgList args, Cardinal *num_args) { VwrTextViewWidget new = (VwrTextViewWidget)tnew; VwrTextViewPart *tvp = &new->textView; register int min_dim; /* * Get the font statistics */ get_font_info(tvp); /* * Calculate required canvas height based on required number of * text columns and rows and the dimensions of the font. Set * the core class size to this canvas size if either core * dimension is less than or equal to the highlight thickness. * Note that we test against twice the highlight and shadow * thicknesses because there is a highlight and shadow on both * sides (e.g. top and bottom). */ tvp->def_height = tvp->text_rows * tvp->fontheight; tvp->def_width = tvp->text_columns * tvp->fontwidth; min_dim = 2 * new->primitive.highlight_thickness + 2 * new->primitive.shadow_thickness; if (new->core.height <= min_dim) new->core.height = tvp->def_height; if (new->core.width <= min_dim) new->core.width = tvp->def_width; /* * Nuke the border highlight and shadow */ new->primitive.highlight_thickness = 0; new->primitive.shadow_thickness = 0; new->core.border_width = 0; /* * Create the program's GCs */ create_gcs(new); /* * Init text buffer, top line index and scrolling related vars */ tvp->top = 0; tvp->top_pending = 0; tvp->left = 0; tvp->timer = NULL; VwrInitText(&tvp->text); /* * Init selection variables */ init_selection(tvp); /* * Init text search variables */ tvp->comp_exp = NULL; tvp->search_next = NULL; /* * Init Xmu selection atom caching */ (void)XmuInternAtom(XtDisplay(new), XmuMakeAtom("NULL")); /* * If a vertical scrollbar has been specified, register callbacks for it */ if (tvp->vsb) vsb_callbacks(new); /* * If a horizontal scrollbar has been specified, register callbacks for it */ if (tvp->hsb) hsb_callbacks(new); } /************************************************************************** * * Function: realize * * Description: Performs task at the time the widget is realised * * Parameters: * w (I) - this widget * mask (I) - mask for window attributes * attribs (I) - window attributes struct * * Return: none * **************************************************************************/ static void realize(Widget w, XtValueMask *mask, XSetWindowAttributes *attribs) { VwrTextViewWidget tv = (VwrTextViewWidget)w; VwrTextViewPart *tvp = &tv->textView; /* * Call the supreclass realize */ (*(&xmPrimitiveClassRec)->core_class.realize)(w, mask, attribs); /* * If a file has been specified, load it into the viewer */ if (tvp->filename) load_viewer(tv); } /************************************************************************** * * Function: query_geometry * * Description: Responds to a query for widget size changes * * Parameters: * w (I) - this widget * proposed (I) - proposed new geometry * answer (I) - suggested geometry * * Return: Response to geometry change request * **************************************************************************/ /* ARGSUSED */ static XtGeometryResult query_geometry(Widget w, XtWidgetGeometry *proposed, XtWidgetGeometry *answer) { unsigned int amask = 0, pmask; XtGeometryResult result; VwrTextViewWidget tv = (VwrTextViewWidget)w; pmask = proposed->request_mode; amask = CWWidth | CWHeight; if ((pmask & amask) == amask) { answer->width = (proposed->width < tv->textView.def_width) ? tv->textView.def_width: proposed->width; answer->height = (proposed->height < tv->textView.def_height) ? tv->textView.def_height: proposed->height; if ((proposed->width == answer->width) && (proposed->height == answer->height)) result = XtGeometryYes; else result = XtGeometryAlmost; } else { amask = 0; result = XtGeometryNo; } answer->request_mode = amask; return result; } /************************************************************************** * * Function: expose * * Description: Handles the expose events, redrawing the text viewer. * * Parameters: * w (I) - this widget * event (I) - expose event structure * region (I) - composite region of exposure * * Return: none * **************************************************************************/ /* ARGSUSED */ static void expose(Widget w, XEvent *event, Region region) { register int yloc, ind; VwrTextViewWidget tv = (VwrTextViewWidget)w; VwrTextViewPart *tvp = &tv->textView; /* * Verify that we are realized */ if (!XtIsRealized(tv)) return; /* * Loop through each line until the bottom of the window is reached, or * we run out of lines. Redraw any lines that intersect the exposed * region. */ ind = tvp->top; yloc = 0; while (ind < tvp->text.nlines && yloc < tv->core.height) { if (XRectInRegion(region, 0, yloc, tv->core.width, tvp->fontheight) != RectangleOut) draw_line(XtDisplay((Widget)tv), XtWindow(tv), tv, ind, tvp->left, yloc); yloc += tvp->fontheight; ind++; } } /************************************************************************** * * Function: resize * * Description: Handles the resizing of the text viewer. * * Parameters: * w (I) - this widget * * Return: none * **************************************************************************/ static void resize(Widget w) { DEFARGS(10); int delta; int slider_size, scroll_max, scroll_value; VwrTextViewWidget tv = (VwrTextViewWidget)w; VwrTextViewPart *tvp = &tv->textView; /* * Reset the vertical scrollbar slider to indicate the relative * proportion of text displayed and also the new page size. * We need to scroll the text down in those situations where * we are at the bottom of the text and the window grows in * in size. */ if (tvp->vsb) { if ((slider_size = tv->core.height / tvp->fontheight) < 1) slider_size = 1; STARTARGS; SETARG(XmNmaximum, &scroll_max); SETARG(XmNvalue, &scroll_value); XtGetValues(tvp->vsb, ARGLIST); if (slider_size > scroll_max || !tvp->text.nlines) slider_size = scroll_max; delta = scroll_max - slider_size; if (scroll_value > delta) tvp->top = scroll_value = (delta < 0) ? 0: delta; STARTARGS; SETARG(XmNsliderSize, slider_size); SETARG(XmNpageIncrement, slider_size); SETARG(XmNvalue, scroll_value); XtSetValues(tvp->vsb, ARGLIST); } /* * Reset the horizontal scrollbar slider to indicate the relative * proportion of text displayed and also the new page size. */ if (tvp->hsb) { if ((slider_size = tv->core.width) < tvp->fontwidth) slider_size = tvp->fontwidth; STARTARGS; SETARG(XmNmaximum, &scroll_max); SETARG(XmNvalue, &scroll_value); XtGetValues(tvp->hsb, ARGLIST); if (slider_size > scroll_max || !tvp->text.max_line_len) slider_size = scroll_max; delta = scroll_max - slider_size; if (scroll_value > delta) { scroll_value = (delta < 0) ? 0: delta; tvp->left = -scroll_value; } STARTARGS; SETARG(XmNsliderSize, slider_size); SETARG(XmNpageIncrement, slider_size); SETARG(XmNvalue, scroll_value); XtSetValues(tvp->hsb,ARGLIST); } } /************************************************************************** * * Function: set_values * * Description: Handles the setting of widget resources using the * SetValues Xt function * * Parameters: * cur (I) - current settings of widget variables * req (I) - request values if widget varaibles not processed yet * by superclass * new (I) - new widget values already processed by superclass * args (I) - arguments to SetValues function * num_args (I) - number of arguments in args * * Return: True if XCleaerArea to be called and expose event generated, * False if no redisplay requireno redisplay required. * **************************************************************************/ /* ARGSUSED */ static Boolean set_values(Widget cur, Widget req, Widget new, ArgList args, Cardinal *num_args) { Boolean do_expose = False; VwrTextViewWidget ctv = (VwrTextViewWidget) cur; VwrTextViewWidget ntv = (VwrTextViewWidget) new; /* * New text file to load */ if (ctv->textView.filename == NULL) { if (ntv->textView.filename != NULL) { load_viewer(ntv); do_expose = True; } } else if (ntv->textView.filename != NULL) { load_viewer(ntv); do_expose = True; } /* * Scrollbars */ if ((ctv->textView.vsb != ntv->textView.vsb) && (ntv->textView.vsb)) { vsb_callbacks(ntv); resize(new); do_expose = True; } if ((ctv->textView.hsb != ntv->textView.hsb) && (ntv->textView.hsb)) { hsb_callbacks(ntv); resize(new); do_expose = True; } return do_expose; } /************************************************************************** * * Function: destroy * * Description: Handles destruction of the widget * * Parameters: * w (I) - this widget * * Return: none * **************************************************************************/ static void destroy(Widget w) { int i; VwrTextViewWidget tv = (VwrTextViewWidget)w; /* * Free the GCs */ for (i = 0; i < NUM_STYLES; i++) XtReleaseGC(w, tv->textView.styles[i].gc); XtReleaseGC(w, tv->textView.fill_gc); XtReleaseGC(w, tv->textView.select_fill_gc); } /* ========================================================================== ACTION FUNCTIONS ========================================================================== */ /************************************************************************** * * Function: first_page, last_page, line_up, line_down, page_up, page_down, * next_tgroup, prev_tgroup * * Description: Keyboard action functions to navigate the document and * support tab groups * * Parameters: * w (I) - this widget * event (I) - event that triggered the action * args (I) - action parameters * nargs (I) - number of parameters * * Return: none * **************************************************************************/ /* ARGSUSED */ static void first_page(Widget w, XEvent *event, String *args, Cardinal *nargs) { handle_keyboard((VwrTextViewWidget)w, FIRST_PAGE); } /* ARGSUSED */ static void last_page(Widget w, XEvent *event, String *args, Cardinal *nargs) { handle_keyboard((VwrTextViewWidget)w, LAST_PAGE); } /* ARGSUSED */ static void line_up(Widget w, XEvent *event, String *args, Cardinal *nargs) { handle_keyboard((VwrTextViewWidget)w, LINE_UP); } /* ARGSUSED */ static void line_down(Widget w, XEvent *event, String *args, Cardinal *nargs) { handle_keyboard((VwrTextViewWidget)w, LINE_DOWN); } /* ARGSUSED */ static void col_left(Widget w, XEvent *event, String *args, Cardinal *nargs) { handle_keyboard((VwrTextViewWidget)w, COL_LEFT); } /* ARGSUSED */ static void col_right(Widget w, XEvent *event, String *args, Cardinal *nargs) { handle_keyboard((VwrTextViewWidget)w, COL_RIGHT); } /* ARGSUSED */ static void page_up(Widget w, XEvent *event, String *args, Cardinal *nargs) { handle_keyboard((VwrTextViewWidget)w, PAGE_UP); } /* ARGSUSED */ static void page_down(Widget w, XEvent *event, String *args, Cardinal *nargs) { handle_keyboard((VwrTextViewWidget)w, PAGE_DOWN); } /* ARGSUSED */ static void next_tgroup(Widget w, XEvent *event, String *args, Cardinal *nargs) { XmProcessTraversal(w, XmTRAVERSE_NEXT_TAB_GROUP); } /* ARGSUSED */ static void prev_tgroup(Widget w, XEvent *event, String *args, Cardinal *nargs) { XmProcessTraversal(w, XmTRAVERSE_PREV_TAB_GROUP); } /************************************************************************** * * Function: start_select * * Description: Begins a new ICCCM text selection * * Parameters: * w (I) - this widget * event (I) - event that triggered the action * args (I) - action parameters * nargs (I) - number of parameters * * Return: none * **************************************************************************/ /* ARGSUSED */ static void start_select(Widget w, XEvent *event, String *args, Cardinal *nargs) { VwrTextViewPart *tvp = &((VwrTextViewWidget)w)->textView; XButtonEvent *xbe = (XButtonEvent*)event; /* * Unhighlight any previous selection */ lose_selection(w, NULL); /* * Map the pointer coordinates into a line and column for * the mark location */ xy2lc(tvp, xbe->x, xbe->y, &tvp->sel_mark.line, &tvp->sel_mark.col); /* * Assume the mark and point start out at the same place */ tvp->sel_point = tvp->sel_mark; } /************************************************************************** * * Function: drag_select * * Description: Handles the dragging of the ICCCM selection * * Parameters: * w (I) - this widget * event (I) - event that triggered the action * args (I) - action parameters * nargs (I) - number of parameters * * Return: none * **************************************************************************/ /* ARGSUSED */ static void drag_select(Widget w, XEvent *event, String *args, Cardinal *nargs) { int cmp, cmp_new; VwrPositionStruct new_p; VwrTextViewWidget tv = (VwrTextViewWidget)w; VwrTextViewPart *tvp = &tv->textView; XMotionEvent *xme = (XMotionEvent*)event; /* * Map the pointer coordinates to line and column position * for the point position */ xy2lc(tvp, xme->x, xme->y, &new_p.line, &new_p.col); /* * This bit of machinery avoids the clearing and redrawing of * the entire selection every time the pointer is moved. Only * the part of the selection that is back over is cleared */ cmp = selcmp(&tvp->sel_point, &tvp->sel_mark); cmp_new = selcmp(&new_p, &tvp->sel_point); if (cmp < 0 && cmp_new > 0) highlight(tv, &tvp->sel_point, &new_p, DESELECT); else if (cmp > 0 && cmp_new < 0) { highlight(tv, &new_p, &tvp->sel_point, DESELECT); } /* * Assign the new position to be the point */ tvp->sel_point.line = new_p.line; tvp->sel_point.col = new_p.col; /* * Highlight the new selection. We make sure the we * pass the pistionally inferior point as the first * argument and the positionally superior point as * the second positional argument */ if ((cmp = selcmp(&tvp->sel_mark, &tvp->sel_point)) < 0) highlight(tv, &tvp->sel_mark, &tvp->sel_point, SELECT); else if (cmp > 0) highlight(tv, &tvp->sel_point, &tvp->sel_mark, SELECT); } /************************************************************************** * * Function: complete_select * * Description: Handles gaining ownership of the selection when the * selecting process is complete * * Parameters: * w (I) - this widget * event (I) - event that triggered the action * args (I) - action parameters * nargs (I) - number of parameters * * Return: none * **************************************************************************/ /* ARGSUSED */ static void complete_select(Widget w, XEvent *event, String *args, Cardinal *nargs) { VwrTextViewWidget tv = (VwrTextViewWidget)w; VwrTextViewPart *tvp = &tv->textView; XButtonEvent *xbe = (XButtonEvent*)event; /* * If there is a selection, attempt to own it. * If the ownership request fails print a warning * and unhighlight the selection */ if (selcmp(&tvp->sel_mark, &tvp->sel_point)) { if (XtOwnSelection(w, XA_PRIMARY, xbe->time, transfer_selection, lose_selection, transfer_complete) == False) { XtWarning("TextView - cannot own selection, try again\n"); lose_selection(w, NULL); } } } /* ========================================================================== PRIVATE FUNCTIONS ========================================================================== */ /************************************************************************** * * Function: get_font_info * * Description: Gets the font size statistics and stores and stores them * in the instance structure. * * Parameters: * tvp (I) - this widget's instance part * * Return: none * **************************************************************************/ static void get_font_info(VwrTextViewPart *tvp) { XFontStruct *norm_style, *under_style, *bold_style, *bunder_style; int norm_width, under_width, bold_width, bunder_width; /* * Get pointers to the style fonts */ norm_style = tvp->styles[NORMAL_STYLE].font; under_style = tvp->styles[UNDERLINE_STYLE].font; bold_style = tvp->styles[BOLD_STYLE].font; bunder_style = tvp->styles[BOLDUNDER_STYLE].font; /* * Determine maximum font height */ tvp->fontheight = TV_MAX(norm_style->ascent + norm_style->descent, under_style->ascent + under_style->descent); tvp->fontheight = TV_MAX(tvp->fontheight, bold_style->ascent + bold_style->descent); tvp->fontheight = TV_MAX(tvp->fontheight, bunder_style->ascent + bunder_style->descent); /* * Determine maximum font ascent */ tvp->fontascent = TV_MAX(norm_style->ascent, under_style->ascent); tvp->fontascent = TV_MAX(tvp->fontascent, bold_style->ascent); tvp->fontascent = TV_MAX(tvp->fontascent, bunder_style->ascent); /* * Determine maximum font width * * If the per_char field of the XFontStruct is NULL * then the font is fixed width and the max_bounds.width * is used to determine the width. If the per_char field * is non-NULL then we have a proportional font and we * use the width of the 'n' character to determine the * width (the max width for proportional fonts is too large). * * The max of the three font styles is used for the width. */ norm_width = TV_FWIDTH(norm_style); under_width = TV_FWIDTH(under_style); bold_width = TV_FWIDTH(bold_style); bunder_width = TV_FWIDTH(norm_style); tvp->fontwidth = TV_MAX(norm_width, under_width); tvp->fontwidth = TV_MAX(tvp->fontwidth, bold_width); tvp->fontwidth = TV_MAX(tvp->fontwidth, bunder_width); } /************************************************************************** * * Function: create_gcs * * Description: Creates the Graphics Contexts for the program * * Parameters: * tv (I) - this widget * * Return: none * **************************************************************************/ static void create_gcs(VwrTextViewWidget tv) { XGCValues gcv; XtGCMask mask; register int i; VwrTextViewPart *tvp = &tv->textView; /* * Create the GCs for drawing text in the various fonts */ mask = GCFont | GCForeground | GCBackground; gcv.background = tv->core.background_pixel; for (i = 0; i < NUM_NORM_STYLES; i++) { gcv.font = tvp->styles[i].font->fid; gcv.foreground = tvp->styles[i].fore; gcv.foreground = (tvp->styles[i].fore == DEF_COLOR) ? tv->primitive.foreground: tvp->styles[i].fore; tvp->styles[i].gc = XtGetGC((Widget)tv, mask, &gcv); } /* * Create the GCs for drawing selected text in the various fonts */ mask = GCFont | GCForeground | GCBackground; gcv.background = (tvp->select_back == DEF_COLOR) ? tv->primitive.foreground: tvp->select_back; gcv.foreground = (tvp->select_fore == DEF_COLOR) ? tv->core.background_pixel: tvp->select_fore; for (i = 0; i < NUM_NORM_STYLES; i++) { gcv.font = tvp->styles[i].font->fid; tvp->styles[i | SELECT_MASK].gc = XtGetGC((Widget)tv, mask, &gcv); tvp->styles[i | SELECT_MASK].font = tvp->styles[i].font; } /* * Create line clearing GCs */ mask = GCFont | GCForeground | GCBackground; gcv.foreground = gcv.background = tv->core.background_pixel; for (i = 0; i < NUM_NORM_STYLES; i++) { gcv.font = tvp->styles[i].font->fid; tvp->blanks[i] = XtGetGC((Widget)tv, mask, &gcv); } /* * Create the blank line and end of line fill GC */ mask = GCForeground; gcv.foreground = tv->core.background_pixel; tvp->fill_gc = XtGetGC((Widget)tv, mask, &gcv); gcv.foreground = (tvp->select_back == DEF_COLOR) ? tv->primitive.foreground: tvp->select_back; tvp->select_fill_gc = XtGetGC((Widget)tv, mask, &gcv); } /************************************************************************** * * Function: load_viewer * * Description: Reads into the viewer the file specified by the textFile * resource. * * Parameters: * tv (I) - this widget * * Return: none * **************************************************************************/ static void load_viewer(VwrTextViewWidget tv) { DEFARGS(15); int slider_size; VwrTextViewPart *tvp = &tv->textView; /* * Make sure we have a file to read */ if (tvp->filename == NULL) return; /* * The selection must be reinitialized so that we do not carry * any old selection information into a new text display where * the old information is meaningless (and can cause core dumps). */ init_selection(tvp); /* * Clear the text search start address */ tvp->search_next = NULL; /* * Read the nroff output text into the widget state structure * text buffer */ if (VwrReadText(tvp->filename, &tvp->text, tvp->styles, tvp->blank_compress) < 0) { XtWarning("TextView - cannot load text\n"); return; } /* * Position viewer at top of text and to the left edge */ tvp->top = 0; tvp->left = 0; /* * Set the vertical scrollbar so that movements are reported in terms * of lines of text. Set the scrolling increment to a single line, and * the page increment to the number of lines the canvas widget can hold. * Also set the slider size to be proportional. */ if (tvp->vsb) { if ((slider_size = tv->core.height / tvp->fontheight) < 1) slider_size = 1; if (slider_size > tvp->text.nlines) slider_size = tvp->text.nlines; STARTARGS; SETARG(XmNminimum, 0); SETARG(XmNmaximum, tvp->text.nlines); SETARG(XmNincrement, 1); SETARG(XmNsliderSize, slider_size); SETARG(XmNpageIncrement, slider_size); SETARG(XmNvalue, tvp->top); XtSetValues(tvp->vsb, ARGLIST); } /* * Set the horizontal scrollbar so that movements are reported in terms * of pixels. Set the scrolling increment to a single pixel, and * the page increment to the number of pixel the canvas widget can hold. * Also set the slider size to be proportional. */ if (tvp->hsb) { if ((slider_size = tv->core.width) < tvp->fontwidth) slider_size = tvp->fontwidth; if (tv->core.width >= tvp->text.max_line_len) slider_size = tvp->text.max_line_len; STARTARGS; SETARG(XmNminimum, 0); SETARG(XmNmaximum, tvp->text.max_line_len); SETARG(XmNincrement, tvp->fontwidth); SETARG(XmNsliderSize, slider_size); SETARG(XmNpageIncrement, slider_size); SETARG(XmNvalue, tvp->left); XtSetValues(tvp->hsb, ARGLIST); } /* * Call any callbacks that the user has registered for file load */ load_callback_data.filename = tvp->filename; XtCallCallbacks((Widget)tv, VwrNloadCallback, (XtPointer)&load_callback_data); } /************************************************************************** * * Function: vsb_dragged * * Description: Handles vertical scrollbar drags. * * Parameters: * w (I) - scrollbar widget that invoked the callback * tv (I) - text viewer widget * call_data (I) - standard scrollbar callback data structure * * Return: none * **************************************************************************/ static void vsb_dragged(Widget w, VwrTextViewWidget tv, XmScrollBarCallbackStruct *call_data) { VwrTextViewPart *tvp = &tv->textView; /* * We keep track of the scrollbar position and set a * timer. Only when the timer expires do we actually * redraw the text */ tvp->top_pending = call_data->value; if (!tvp->timer) tvp->timer = XtAppAddTimeOut(XtWidgetToApplicationContext(w), SCROLL_DELAY, (XtTimerCallbackProc)vert_scroll_text, (XtPointer)tv); } /************************************************************************** * * Function: vsb_moved * * Description: Handles vertical scrollbar value changes. * * Parameters: * w (I) - scrollbar widget that invoked the callback * tv (I) - text viewer widget * call_data (I) - standard scrollbar callback data structure * * Return: none * **************************************************************************/ /* ARGSUSED */ static void vsb_moved(Widget w, VwrTextViewWidget tv, XmScrollBarCallbackStruct *call_data) { VwrTextViewPart *tvp = &tv->textView; /* * Here we redraw the text on demand. We clear any * pending timers just in case */ if (tvp->timer) XtRemoveTimeOut(tvp->timer); tv->textView.top_pending = call_data->value; vert_scroll_text(tv, NULL); } /************************************************************************** * * Function: vert_scroll_text * * Description: Scrolls the text vertically in the text viewer * * Parameters: * tv (I) - this widget * timer (I) - ID of timer when called as timeout proc * * Return: none * **************************************************************************/ /* ARGSUSED */ static void vert_scroll_text(VwrTextViewWidget tv, XtIntervalId *timer) { register int yloc, ind, old_ind; VwrTextViewPart *tvp = &tv->textView; /* * Clear any timers */ tvp->timer = NULL; /* * Assign the pending scrollbar position to the scrollbar * value variable. Clean each line of the old text and draw * the new text */ if (tvp->top == tvp->top_pending) return; old_ind = tvp->top; ind = tvp->top = tvp->top_pending; yloc = 0; while (ind < tvp->text.nlines && yloc < tv->core.height) { clear_line(XtDisplay((Widget)tv), XtWindow(tv), tv, old_ind, tvp->left, yloc); draw_line(XtDisplay((Widget)tv), XtWindow(tv), tv, ind, tvp->left, yloc); yloc += tvp->fontheight; old_ind++; ind++; } /* * If the viewer is sized larger than the bottom of the text * then clear the gap at the bottom of junk */ if (yloc < tv->core.height) { XFillRectangle(XtDisplay((Widget)tv), XtWindow(tv), tvp->fill_gc, 0, yloc, tv->core.width, tv->core.height - yloc); } } /************************************************************************** * * Function: horiz_scroll_text * * Description: Handles horizontal scrollbar value changes. * * Parameters: * w (I) - scrollbar widget that invoked the callback * tv (I) - text viewer widget * call_data (I) - standard scrollbar callback data structure * * Return: none * **************************************************************************/ /* ARGSUSED */ static void horiz_scroll_text(Widget w, VwrTextViewWidget tv, XmScrollBarCallbackStruct *call_data) { VwrTextViewPart *tvp = &tv->textView; register int yloc, ind; /* * Assign the scrollbar position to the scrollbar * value variable. Clean each line of the old text and draw * the new text at scrolled position. */ if (tvp->left == -call_data->value) return; ind = tvp->top; yloc = 0; while (ind < tvp->text.nlines && yloc < tv->core.height) { clear_line(XtDisplay((Widget)tv), XtWindow(tv), tv, ind, tvp->left, yloc); draw_line(XtDisplay((Widget)tv), XtWindow(tv), tv, ind, -call_data->value, yloc); yloc += tvp->fontheight; ind++; } tvp->left = -call_data->value; /* * If the viewer is sized larger than the bottom of the text * then clear the gap at the bottom of junk */ if (yloc < tv->core.height) { XFillRectangle(XtDisplay((Widget)tv), XtWindow(tv), tvp->fill_gc, 0, yloc, tv->core.width, tv->core.height - yloc); } } /************************************************************************** * * Function: vsb_callbacks * * Description: Registers the widget vertical scrollbar management code * with the vertical scrollbar callbacks * * Parameters: * tv (I) - this widget * * Return: none * **************************************************************************/ static void vsb_callbacks(VwrTextViewWidget tv) { XtAddCallback(tv->textView.vsb, XmNdecrementCallback, (XtCallbackProc)vsb_moved, (XtPointer)tv); XtAddCallback(tv->textView.vsb, XmNincrementCallback, (XtCallbackProc)vsb_moved, (XtPointer)tv); XtAddCallback(tv->textView.vsb, XmNpageDecrementCallback, (XtCallbackProc)vsb_moved, (XtPointer)tv); XtAddCallback(tv->textView.vsb, XmNpageIncrementCallback, (XtCallbackProc)vsb_moved, (XtPointer)tv); XtAddCallback(tv->textView.vsb, XmNtoBottomCallback, (XtCallbackProc)vsb_moved, (XtPointer)tv); XtAddCallback(tv->textView.vsb, XmNtoTopCallback, (XtCallbackProc)vsb_moved, (XtPointer)tv); XtAddCallback(tv->textView.vsb, XmNdragCallback, (XtCallbackProc)vsb_dragged, (XtPointer)tv); } /************************************************************************** * * Function: hsb_callbacks * * Description: Registers the widget horizontal scrollbar management code * with the horizontal scrollbar callbacks * * Parameters: * tv (I) - this widget * * Return: none * **************************************************************************/ static void hsb_callbacks(VwrTextViewWidget tv) { XtAddCallback(tv->textView.hsb, XmNdecrementCallback, (XtCallbackProc)horiz_scroll_text, (XtPointer)tv); XtAddCallback(tv->textView.hsb, XmNincrementCallback, (XtCallbackProc)horiz_scroll_text, (XtPointer)tv); XtAddCallback(tv->textView.hsb, XmNpageDecrementCallback, (XtCallbackProc)horiz_scroll_text, (XtPointer)tv); XtAddCallback(tv->textView.hsb, XmNpageIncrementCallback, (XtCallbackProc)horiz_scroll_text, (XtPointer)tv); XtAddCallback(tv->textView.hsb, XmNtoBottomCallback, (XtCallbackProc)horiz_scroll_text, (XtPointer)tv); XtAddCallback(tv->textView.hsb, XmNtoTopCallback, (XtCallbackProc)horiz_scroll_text, (XtPointer)tv); XtAddCallback(tv->textView.hsb, XmNdragCallback, (XtCallbackProc)horiz_scroll_text, (XtPointer)tv); } /************************************************************************** * * Function: draw_line * * Description: Draws a single line of text made up of string segments. * Each segment has a different GC for changes in font and/or color. * * Parameters: * disp (I) - X server connection pointer * win (I) - window in which to draw * tv (I) - this widget * ind (I) - starting index in line array to start drawing text * xloc (I) - starting x location in viewer to start drawing text * yloc (I) - starting y location in viewer to start drawing text * * Return: none * **************************************************************************/ static void draw_line(Display *disp, Window win, VwrTextViewWidget tv, int ind, int xloc, int yloc) { register int x, y, fill_width; register char *cptr; register vwr_text_line_t *lptr; register vwr_text_seg_t *tsptr; register VwrTextViewPart *tvp = &tv->textView; if (ind < 0 || ind >= tvp->text.nlines) return; lptr = &tvp->text.lines[ind]; /* * If there is text on the line, draw it and fill any remaing * area after the text with the fill GC */ if (lptr->line_len) { x = xloc; y = yloc + tvp->fontascent; cptr = lptr->line_chars; for (tsptr = lptr->segs; tsptr; tsptr = tsptr->next) { XDrawImageString(disp, win, tvp->styles[tsptr->seg_type].gc, x, y, cptr, tsptr->seg_nchars); x += tsptr->seg_len; cptr += tsptr->seg_nchars; } if ((fill_width = tv->core.width - ((int)lptr->line_len + xloc)) > 0) { XFillRectangle(disp, win, (lptr->end_select)? tvp->select_fill_gc: tvp->fill_gc, lptr->line_len + xloc, yloc, fill_width, tvp->fontheight - 1); } /* * If there is no text on the line simply fill the line with the * fill GC */ } else { XFillRectangle(disp, win, (lptr->end_select)? tvp->select_fill_gc: tvp->fill_gc, 0, yloc, tv->core.width, tvp->fontheight - 1); } } /************************************************************************** * * Function: clear_line * * Description: Clears a line of text * * Parameters: * disp (I) - X server connection pointer * win (I) - window in which to draw * tv (I) - this widget * ind (I) - starting index in line array to start clearing text * xloc (I) - starting x location in viewer to start clearing text * yloc (I) - starting y location in viewer to start clearing text * * Return: none * **************************************************************************/ static void clear_line(Display *disp, Window win, VwrTextViewWidget tv, int ind, int xloc, int yloc) { register int x, y; register char *cptr; register vwr_text_line_t *lptr; register vwr_text_seg_t *tsptr; register VwrTextViewPart *tvp = &tv->textView; if (ind < 0 || ind >= tvp->text.nlines) return; lptr = &tvp->text.lines[ind]; /* * If there is text on the line, clear it */ if (lptr->line_len) { x = xloc; y = yloc + tvp->fontascent; cptr = lptr->line_chars; for (tsptr = lptr->segs; tsptr; tsptr = tsptr->next) { XDrawImageString(disp, win, tvp->blanks[tsptr->seg_type & ~SELECT_MASK], x, y, cptr, tsptr->seg_nchars); x += tsptr->seg_len; cptr += tsptr->seg_nchars; } } } /************************************************************************** * * Function: handle_keyboard * * Description: Handles keyboard navigation from the action functions * * Parameters: * tv (I) - this widget * nav (I) - direction token (FIRST_PAGE, etc.) * * Return: none * **************************************************************************/ /* ARGSUSED */ static void handle_keyboard(VwrTextViewWidget tv, int nav) { XEvent event; register int top_line, left_x; int old_top, max_top; int old_left_x; int page_amount; DEFARGS(5); VwrTextViewPart *tvp = &tv->textView; /* * If no lines, there is nothing to do */ if (!tvp->text.nlines) return; /* * Get current top line and compute page and doc size metrics */ old_top = top_line = tvp->top; page_amount = tv->core.height / tvp->fontheight; if ((max_top = tvp->text.nlines - page_amount) < 0) max_top = 0; /* * Get left x position */ old_left_x = left_x = tvp->left; switch (nav) { case FIRST_PAGE: top_line = 0; break; case LAST_PAGE: top_line = max_top; break; case LINE_UP: if (--top_line < 0) top_line = 0; break; case LINE_DOWN: if (++top_line > max_top) top_line = max_top; break; case COL_LEFT: left_x += tvp->fontwidth; if (left_x > 0) left_x = 0; break; case COL_RIGHT: left_x -= tvp->fontwidth; if ((tv->core.width - left_x) > tvp->text.max_line_len) left_x = tv->core.width - tvp->text.max_line_len; if (left_x > 0) left_x = 0; break; case PAGE_UP: top_line -= (page_amount - PAGE_OVERLAP); if (top_line < 0) top_line = 0; break; case PAGE_DOWN: top_line += (page_amount - PAGE_OVERLAP); if (top_line > max_top) top_line = max_top; break; default: /* Unknown nav token */ return; } /* * Flush pending KeyPress events from queue to minimize * run-on after auto-repeat */ while (XCheckMaskEvent(XtDisplay((Widget)tv), KeyPressMask, &event)); /* * Don't scroll vertically if no change there */ if (old_top != top_line) goto_line(tv, top_line); /* * Don't scroll horizontally if no change there */ if (old_left_x != left_x) { XmScrollBarCallbackStruct hsb_call_data; STARTARGS; SETARG(XmNvalue, -left_x); XtSetValues(tvp->hsb, ARGLIST); hsb_call_data.value = -left_x; horiz_scroll_text(tvp->hsb, tv, &hsb_call_data); } } /************************************************************************** * * Function: goto_line * * Description: Scrolls the text display so that the specified line number * is the top line in the display. No bounding or validity checking * is performed. * * Parameters: * tv (I) - this widget * line (I) - line number to place at the top of the viewer * * Return: none * **************************************************************************/ static void goto_line(VwrTextViewWidget tv, int line) { DEFARGS(5); XmScrollBarCallbackStruct vsb_call_data; VwrTextViewPart *tvp = &tv->textView; /* * Move scrollbar slider to new position * Fill a scrollbar callback struct with what is needed * Call the scrollbar movement handler */ STARTARGS; SETARG(XmNvalue, line); XtSetValues(tvp->vsb, ARGLIST); vsb_call_data.value = line; vsb_moved(tvp->vsb, tv, &vsb_call_data); } /************************************************************************** * * Function: transfer_selection * * Description: Transfers the selection to the requestor * * Parameters: * w (I) - this widget * selection (I) - type of selection requested * target (I) - type of target selection * type_return (O) - type of selection returned * value_return (O) - selection data * length_return (O) - length of returned selection data * format_return (O) - format of returned data selection * * Return: True if the transfer was completed, False otherwise. * **************************************************************************/ static Boolean transfer_selection(Widget w, Atom *selection, Atom *target, Atom *type_return, XtPointer *value_return, unsigned long *length_return, int *format_return) { VwrTextViewWidget tv = (VwrTextViewWidget)w; VwrTextViewPart *tvp = &tv->textView; XSelectionRequestEvent *req = XtGetSelectionRequest(w, *selection, (XtRequestId)NULL); static Atom text_atom = None; /* * Motif defines the TEXT atom */ if (text_atom == None) text_atom = XInternAtom(XtDisplay(tv), "TEXT", True); /* * Handle the XA_TARGETS ICCCM atom. This code * is direct from O'Reilly Vol 4. This is crazy and * should be wraped up - X really falls short here */ if (*target == XA_TARGETS(XtDisplay(tv))) { Atom *targetP; Atom *std_targets; unsigned long std_length; XmuConvertStandardSelection(w, req->time, selection, target, type_return, (caddr_t*)&std_targets, &std_length, format_return); *value_return = XtMalloc(sizeof(Atom) * (std_length + 1)); targetP = *(Atom**)value_return; *length_return = std_length + 1; *targetP++ = XA_STRING; bcopy((void*)std_targets, (void*)targetP, (int)(sizeof(Atom) * std_length)); XtFree((char*)std_targets); *type_return = XA_ATOM; *format_return = sizeof(Atom) * 8; return True; } /* * This handles actual useful requests for the selected text */ else if ((*target != None) && ((*target == XA_STRING) || (*target == text_atom))) { *value_return = tvp->sel_data = get_selection(tvp, length_return); *type_return = *target; *format_return = 8; return True; } /* * This is more ICCCM nonsense and is verbatim O'Reilly */ else { if (XmuConvertStandardSelection(w, CurrentTime, selection, target, type_return, (caddr_t*)value_return, length_return, format_return)) return True; else { XtWarning("TextView - unsupported selection requested\n"); return False; } } } /************************************************************************** * * Function: transfer_complete * * Description: Cleans up after a selection transfer * * Parameters: * w (I) - this widget * selection (I) - selection type * target (I) - selection target type * * Return: none * **************************************************************************/ /* ARGSUSED */ static void transfer_complete(Widget w, Atom *selection, Atom *target) { VwrTextViewWidget tv = (VwrTextViewWidget)w; VwrTextViewPart *tvp = &tv->textView; /* * Free the selection text buffer if one was allocated */ if (tvp->sel_data) { XtFree((char*)tvp->sel_data); tvp->sel_data = NULL; } } /************************************************************************** * * Function: get_selection * * Description: Places the currently selected text into a selection * buffer. * * Parameters: * tvp (I) - this widget's instance part * lenp (O) - amount of data in selection * * Return: Pointer to allocated selection buffer * **************************************************************************/ static char* get_selection(VwrTextViewPart *tvp, unsigned long *lenp) { int line1, line2; int cmp; char *data, *dptr, *cptr; register int i, j; register vwr_text_seg_t *seg; register vwr_text_line_t *lptr; /* * Determine the selection line span */ if ((cmp = selcmp(&tvp->sel_mark, &tvp->sel_point)) < 0) { line1 = tvp->sel_mark.line; line2 = tvp->sel_point.line; } else if (cmp > 0) { line1 = tvp->sel_point.line; line2 = tvp->sel_mark.line; } else { *lenp = 0; return NULL; } /* * Do some bounds checking on the line positions */ if (line1 == TEXT_END || line2 < 0) { *lenp = 0; return NULL; } if (line1 < 0) line1 = 0; if (line2 == TEXT_END) line2 = tvp->text.nlines - 1; /* * Determine the amount of data to be transfered */ *lenp = 0; for (i = line1; i <= line2; i++) { lptr = &tvp->text.lines[i]; for (seg = lptr->segs; seg; seg = seg->next) { if (seg->seg_type & SELECT_MASK) *lenp += seg->seg_nchars; } if (lptr->end_select) (*lenp)++; } /* * Allocate the buffer for the transfer data */ dptr = data = (char*)XtMalloc(*lenp + 1); /* * Fill the buffer with the selected data */ for (i = line1; i <= line2; i++) { lptr = &tvp->text.lines[i]; cptr = lptr->line_chars; for (seg = lptr->segs; seg; seg = seg->next) { if (seg->seg_type & SELECT_MASK) { for (j = 0; j < seg->seg_nchars; j++) { *dptr++ = *cptr++; } } else cptr += seg->seg_nchars; } if (lptr->end_select) *dptr++ = '\n'; } *dptr = '\0'; return data; } /************************************************************************** * * Function: lose_selection * * Description: Handles the loss of selection (unhighlighting). * * Parameters: * w (I) - this widget * atom (I) - atom that describes the selection type * * Return: * **************************************************************************/ /* ARGSUSED */ static void lose_selection(Widget w, Atom *sel_atom) { VwrTextViewWidget tv = (VwrTextViewWidget)w; VwrTextViewPart *tvp = &tv->textView; int cmp; /* * Make sure we pass in the inferior position as first * argument */ if ((cmp = selcmp(&tvp->sel_mark, &tvp->sel_point)) < 0) highlight(tv, &tvp->sel_mark, &tvp->sel_point, DESELECT); else if (cmp > 0) highlight(tv, &tvp->sel_point, &tvp->sel_mark, DESELECT); } /************************************************************************** * * Function: xy2lc * * Description: Converts a mouse pointer x,y coordinate pair to the * corresponding line,col pair for use in selections. * * Parameters: * tvp (I) - this widget's instance part * x,y (I) - pointer coordinates * linep (O) - line corresponding to y coordinate. If the y position * is less than 0 then linep will be set to -TEXT_END. If * y is greater than the viewer max y then linep will be * set to TEXT_END. * colp (O) - column corresponding to the x coordinate. colp will * be set to -LINE_END if x < 0 and LINE_END if X > line * length. Newline chars are handled by setting colp * to END_CHAR. * * Return: none * **************************************************************************/ static void xy2lc(VwrTextViewPart *tvp, int x, int y, int *linep, int *colp) { /* * Determine the line */ *linep = y2l(tvp, y); /* * Do bounds checking on the line */ if ((*linep == TEXT_END) || (*linep == -TEXT_END)) { *colp = LINE_END; return; } /* * Determine the column */ *colp = x2c(tvp, *linep, x); } /************************************************************************** * * Function: lc2seg * * Description: Converts a line,column pair into a segment and offset * into that segment. * * Parameters: * tvp (I) - this widget's isntance part * line (I) - line number (0 base) * col (I) - column number (0 base) * segp (O) - segment pointer * offp (O) - offset into segment * * Return: none * **************************************************************************/ static void lc2seg(VwrTextViewPart *tvp, int line, int col, vwr_text_seg_t **segp, int *offp) { vwr_text_line_t *lptr; register vwr_text_seg_t *tsptr; register int nchars; /* * Bounds checking */ if (line < 0 || line == TEXT_END || col < 0 || col == LINE_END || col == END_CHAR) { *segp = NULL; *offp = 0; return; } lptr = &tvp->text.lines[line]; /* * More bounds checking */ if (col == 0) { *segp = lptr->segs; *offp = 0; return; } /* * Search for the segment */ nchars = 0; for (tsptr = lptr->segs; tsptr; tsptr = tsptr->next) { if (nchars + tsptr->seg_nchars > col) { *segp = tsptr; *offp = col - nchars; break; } nchars += tsptr->seg_nchars; } } /************************************************************************** * * Function: y2l * * Description: Converts a y pixel coordinate into a 0 based line number. * * Parameters: * tvp (I) - this widget's instance part * y (I) - y pixel coordinate * * Return: The line number is returned. * **************************************************************************/ static int y2l(VwrTextViewPart *tvp, int y) { int line; /* * Calculate the line */ line = y / tvp->fontheight + tvp->top; /* * Do bounds checking on the line */ if (line >= tvp->text.nlines) return TEXT_END; if (line < 0) return -TEXT_END; return line; } /************************************************************************** * * Function: x2c * * Description: Converts an x pixel coordinate into a 0 based text line * character position (column number). * * Parameters: * tvp (I) - this widget's instance part * line (I) - line number * x (I) - y pixel coordinate * * Return: The column number is returned. * **************************************************************************/ static int x2c(VwrTextViewPart *tvp, int line, int x) { vwr_text_line_t *lptr; XFontStruct *font; int col; register char *cptr; register int xnew, xold, len; register vwr_text_seg_t *tsptr; /* * Compensate for horizontal scrolling */ x -= tvp->left; /* * Get a pointer to the line structure */ lptr = &tvp->text.lines[line]; /* * If the line is empty and we have not gone far enough * in x to select it return 0 */ if ((!lptr->line_len) && (x < BLANK_LINE_XSEL)) return 0; /* * Bounds checking */ if (x > (int)lptr->line_len) return LINE_END; if (x < 0) return -LINE_END; /* * Calculate the column. There is complication in that we * must decide which character is closer to the x coord. */ col = LINE_END; len = 0; cptr = lptr->line_chars; for (tsptr = lptr->segs; tsptr; tsptr = tsptr->next) { if (len + tsptr->seg_len > x) { font = tvp->styles[tsptr->seg_type].font; xold = xnew = len; while ((xnew += XTextWidth(font, cptr, 1)) <= x) { cptr++; xold = xnew; } if ((xnew - x) < (x - xold)) cptr++; col = (*cptr == '\n') ? END_CHAR: (int)(cptr - lptr->line_chars); break; } len += tsptr->seg_len; cptr += tsptr->seg_nchars; } return col; } /************************************************************************** * * Function: selcmp * * Description: Compares two selection points and returns a value less * than, equal to or greater than 0 depending on whether p1 is * positionally less than, equal to or greater than p2. * * Parameters: * p1, p2 - points to compare * * Return: p1 < p2 returns < 0; p1 == p2 returns 0; p1 > p2 returns > 0 * **************************************************************************/ static int selcmp(VwrPositionStruct *p1, VwrPositionStruct *p2) { register int delta; if ((delta = p1->line - p2->line) != 0) return delta; if ((delta = p1->col - p2->col) != 0) return delta; return 0; } /************************************************************************** * * Function: highlight * * Description: Highlights the text from point p1 to point p2. It is * assumed that point p2 is poisitionally greater than point p1. * * Parameters: * tv (I) - this widget * p1, p2 (I) - text span to highlight * sel_flag (I) - one of SELECT or DESELECT * * Return: none * **************************************************************************/ static void highlight(VwrTextViewWidget tv, VwrPositionStruct *p1, VwrPositionStruct *p2, int sel_flag) { register Boolean changed; register int i, yloc; register int line1, line2; register vwr_text_line_t *lptr, *lptr1, *lptr2; vwr_text_seg_t *start_seg, *end_seg; VwrTextViewPart *tvp = &tv->textView; /* * Bounds check the lines */ if (p1->line == TEXT_END || p2->line < 0) return; if (p1->line < 0) line1 = 0; else line1 = p1->line + 1; /* * Fill the lines between p1 and p2, if any */ if (line1 < tvp->text.nlines) { if (p2->line > 0) { if (p2->line == TEXT_END) line2 = tvp->text.nlines - 1; else line2 = p2->line - 1; yloc = (line1 - tvp->top) * tvp->fontheight; for (i = line1; i <= line2; i++) { lptr = &tvp->text.lines[i]; changed = select_segs(lptr->segs, NULL, sel_flag); if (changed || (lptr->end_select != sel_flag)) { lptr->end_select = sel_flag; compact_line(tvp, i); draw_line(XtDisplay((Widget)tv), XtWindow(tv), tv, i, tvp->left, yloc); } yloc += tvp->fontheight; } } } /* * Highlight the p1 and p2 lines */ start_seg = end_seg = NULL; line1 = (p1->line < 0) ? 0: p1->line; line2 = (p2->line == TEXT_END) ? tvp->text.nlines - 1: p2->line; lptr1 = &tvp->text.lines[line1]; lptr2 = &tvp->text.lines[line2]; start_seg = get_seg(tvp, p1, sel_flag, lptr1); /* * If the start line and end line are the same */ if (line1 == line2) { if (p2->col >= 0) { end_seg = get_seg(tvp, p2, sel_flag, lptr1); } draw_segs(tv, line1, start_seg, end_seg, sel_flag); /* * If the end line is greater than the start line * (end < start cannot happen) */ } else { lptr1->end_select = sel_flag; draw_segs(tv, line1, start_seg, end_seg, sel_flag); start_seg = end_seg = NULL; if (p2->col >= 0) { end_seg = get_seg(tvp, p2, sel_flag, lptr2); draw_segs(tv, line2, lptr2->segs, end_seg, sel_flag); } } } /************************************************************************** * * Function: get_seg * * Description: Locates the segment containing the specified point. * If the point lies at the beginning of the segment then that * segment is returned intact. If the point lies within the * segment, the segment is split and the new segment, which * starts with the point, is returned. The newly created * segment inherits the attributes of the old segment except for * character count and length. * * Parameters: * tvp (I) - this widget's instance part * point (I) - point to locate * sel_flag (I) - selection flag (SELECT or DESELECT) * lptr (I) - if the column is passed the end of the line * (col == LINE_END) then the segment returned is * NULL and the end select flag is set to sel_flag. * If the returned segment is non-NULL, this flag * is left unchanged. * * Return: Pointer to the segment that starts with the point. If the * col is LINE_END then the pointer returned is NULL and the * end_select flag is set to the specified sel_flag. If the * return value is non-NULL then the end_select is left * unchanged. * **************************************************************************/ static vwr_text_seg_t* get_seg(VwrTextViewPart *tvp, VwrPositionStruct *point, int sel_flag, vwr_text_line_t *lptr) { int off; vwr_text_seg_t *seg, *new_seg; /* * Determine if the column is passed the end of the line */ if (point->col == LINE_END) { lptr->end_select = sel_flag; return NULL; } /* * Find the segment and offset that corresponds to * the specified line and column */ lc2seg(tvp, point->line, (point->col < 0) ? 0: point->col, &seg, &off); /* * If there was an offset into segment then the col does * not occur at the start of a segment. We need to split * the segment into two so that a segment starts at the * specified col */ if (off) { lptr = &tvp->text.lines[point->line]; new_seg = (vwr_text_seg_t*)XtMalloc(sizeof(vwr_text_seg_t)); new_seg->next = seg->next; new_seg->seg_type = seg->seg_type; new_seg->seg_nchars = seg->seg_nchars - off; new_seg->seg_len = XTextWidth(tvp->styles[seg->seg_type].font, &lptr->line_chars[point->col], new_seg->seg_nchars); seg->next = new_seg; seg->seg_nchars = off; seg->seg_len -= new_seg->seg_len; seg = new_seg; } return seg; } /************************************************************************** * * Function: select_segs * * Description: Selects all segments starting at the specified start * segment, up to but not including the specified ending segment * * Parameters: * start_seg, end_seg (I) - segment span to select * sel_flag (I) - one of SELECT or DESELECT * * Return: False if all segments in the specified segment span were already * in the state specified by sel_flag. True if at least one segment * required a state change. * **************************************************************************/ static Boolean select_segs(vwr_text_seg_t *start_seg, vwr_text_seg_t *end_seg, int sel_flag) { register vwr_text_seg_t *tsptr; register Boolean changed = False; if (start_seg) { if (sel_flag) for (tsptr = start_seg; tsptr != end_seg; tsptr = tsptr->next) { if (!(tsptr->seg_type & SELECT_MASK)) { changed = True; tsptr->seg_type |= SELECT_MASK; } } else for (tsptr = start_seg; tsptr != end_seg; tsptr = tsptr->next) { if (tsptr->seg_type & SELECT_MASK) { changed = True; tsptr->seg_type &= ~SELECT_MASK; } } } return changed; } /************************************************************************** * * Function: draw_segs * * Description: Draws a selected line on the screen * * Parameters: * tv (I) - this widget * line (I) - line number (0 base) * start_seg (I) - starting segment to select * end_seg (I) - end segment to select * sel_flag (I) - one of SELECT or DESELECT * * Return: none * **************************************************************************/ static void draw_segs(VwrTextViewWidget tv, int line, vwr_text_seg_t *start_seg, vwr_text_seg_t *end_seg, int sel_flag) { VwrTextViewPart *tvp = &tv->textView; /* * Highlight or unhighlight the specified segments in the line */ (void)select_segs(start_seg, end_seg, sel_flag); /* * Coalesce segments fragmented by the selection drag process */ compact_line(tvp, line); /* * Draw the selected line */ draw_line(XtDisplay((Widget)tv), XtWindow(tv), tv, line, tvp->left, (line - tvp->top) * tvp->fontheight); } /************************************************************************** * * Function: compact_line * * Description: Coalesces the contiguous segments on a line that have * the same font and selection attributes. * * Parameters: * tvp (I) - this widget's instance part * line (I) - line number to compact * * Return: none * **************************************************************************/ static void compact_line(VwrTextViewPart *tvp, int line) { register vwr_text_line_t *lptr; register vwr_text_seg_t *seg, *next_seg; lptr = &tvp->text.lines[line]; if ((seg = lptr->segs) == NULL) return; while (seg->next) { next_seg = seg->next; if (seg->seg_type == next_seg->seg_type) { seg->seg_nchars += next_seg->seg_nchars; seg->seg_len += next_seg->seg_len; seg->next = next_seg->next; XtFree((char*)next_seg); } else seg = seg->next; } } /************************************************************************** * * Function: lc_bounds * * Description: Performs bounding of a line number and column number * pair based on the current text. * * Parameters: * tvp (I) - this widget's instance part * pos (I,O) - text position * * Return: none * **************************************************************************/ static void lc_bounds(VwrTextViewPart *tvp, VwrPositionStruct *pos) { register vwr_text_t *tptr = &(tvp->text); if (pos->line < 0) pos->line = -TEXT_END; else if (pos->line >= tptr->nlines) pos->line = TEXT_END; if (pos->line == -TEXT_END || pos->line == TEXT_END) pos->col = LINE_END; else if (pos->col < 0) pos->col = -LINE_END; else if (pos->col == tptr->lines[pos->line].line_nchars) pos->col = END_CHAR; else if (pos->col > tptr->lines[pos->line].line_nchars) pos->col = LINE_END; } /************************************************************************** * * Function: init_selection * * Description: Initializes the primary selection variables. * * Parameters: * tvp (I) - this widget's instance part * * Return: none * **************************************************************************/ static void init_selection(VwrTextViewPart *tvp) { tvp->sel_mark.line = -1; tvp->sel_mark.col = -1; tvp->sel_point.line = -1; tvp->sel_point.col = -1; tvp->sel_data = NULL; } /************************************************************************** * * Function: parse_escapes * * Description: Parses the specified string for escape sequences and * replace them with the corresponding escape characters. * * Parameters: * src (I) - source string * dst (O) - destination string * * Return: none * **************************************************************************/ static void parse_escapes(register char *src, register char *dst) { static char *esc_rep = "abfnrtv"; static char *esc_ch = "\a\b\f\n\r\t\v"; register char *lptr, ch; char *sptr; while (*src) { /* * If we have an escape sequence process it */ if (*src == '\\') { /* * If not a pre-define escape sequence check for * a numerical escape sequence */ if ((lptr = strchr(esc_rep, src[1])) == NULL) { if (src[1] == 'x') { ch = (char)strtol(&src[2], &sptr, 16); if (sptr == &src[2]) { *dst++ = *src++; *dst++ = *src++; } else { *dst++ = ch; src = sptr; } } else if (isdigit(src[1])) { *dst++ = (char)strtol(&src[1], &sptr, 8); src = sptr; } else { *dst++ = *src++; } } /* * Copy the proper escape char for the pre-defined sequence */ else { *dst++ = esc_ch[(int)(lptr - esc_rep)]; src += 2; } } /* * Otherwise just copy character to output buffer */ else *dst++ = *src++; } *dst = '\0'; } /* ========================================================================== PUBLIC CONVENIENCE AND UTILITY FUNCTIONS ========================================================================== */ /************************************************************************** * * Function: VwrCreateScrolledTextView * * Description: Convenience function to create a scrolled text viewer * * Parameters: * name (I) - text viewer instance name * parent (I) - text viewer's parent widget * vsb_flag (I) - if True, a vertical scrollbar is provided. * hsb_flag (I) - if True, a horizontal scrollbar is provided. * * Return: The created TextView widget. Applications need to use XtParent * to get the Frame around the viewer and an XtParent on that to * get the Scrolled Window widget that this text viewer is in. * **************************************************************************/ Widget VwrCreateScrolledTextView(Widget parent, char *name, Boolean vsb_flag, Boolean hsb_flag) { DEFARGS(10); char *buffer; Widget text_viewer, text_swin, text_frame; Widget text_vert_scrollbar = NULL, text_horiz_scrollbar = NULL; /* * Allocate space for the name buffer */ buffer = (char*)XtMalloc((strlen(name) + 20) * sizeof(char)); /* * Create a scrolled window to put the text viewer in */ (void)sprintf(buffer, "%sSW", name); text_swin = XtCreateManagedWidget(buffer, xmScrolledWindowWidgetClass, parent, NULL, 0); /* * Create the drawing surface and a frame for it. */ (void)sprintf(buffer, "%sFR", name); STARTARGS; SETARG(XmNshadowType, XmSHADOW_IN); text_frame = XtCreateManagedWidget(buffer, xmFrameWidgetClass, text_swin, ARGLIST); /* * Create the vertical scrollbar */ if (vsb_flag) { (void)sprintf(buffer, "%sVSB", name); text_vert_scrollbar = XtCreateManagedWidget(buffer, xmScrollBarWidgetClass, text_swin, NULL, 0); } /* * Create the horizontal scrollbar, if desired */ if (hsb_flag) { (void)sprintf(buffer, "%sHSB", name); STARTARGS; SETARG(XmNorientation, XmHORIZONTAL); text_horiz_scrollbar = XtCreateManagedWidget(buffer, xmScrollBarWidgetClass, text_swin, ARGLIST); } /* * Create the text viewing widget */ STARTARGS; SETARG(VwrNverticalScrollBar, text_vert_scrollbar); SETARG(VwrNhorizontalScrollBar, text_horiz_scrollbar); text_viewer = XtCreateManagedWidget(name, vwrTextViewWidgetClass, text_frame, ARGLIST); /* * Register the scrollbar(s) and work area for the ScrolledWindow widget. */ STARTARGS; SETARG(XmNverticalScrollBar, text_vert_scrollbar); SETARG(XmNhorizontalScrollBar, text_horiz_scrollbar); SETARG(XmNworkWindow, text_frame); XtSetValues(text_swin, ARGLIST); /* * Deallocate name buffer */ XtFree((char*)buffer); return text_viewer; } /************************************************************************** * * Function: VwrTextViewClearSelection * * Description: Clears the Primary Selection highlight in a text viewer. * * Parameters: * w (I) - TextView widget * * Return: none * **************************************************************************/ void VwrTextViewClearSelection(Widget w) { lose_selection(w, NULL); } /************************************************************************** * * Function: VwrTextViewSetSelection * * Description: Highlights text and optinally sets the Primary selection * The user may specify whether the primary selection is obtained. * In any case, the original primary selectionis lost. Note that row * and column numbers start at 0. * * Parameters: * w (I) - TextView widget * start_pos (I) - starting text position to highlight * end_pos (I) - ending position * primary_select (I) - if 1, indicates that primary selection is to * be set. If 0, only highlighting is performed. * * Return: None * **************************************************************************/ void VwrTextViewSetSelection(Widget w, VwrPositionStruct *start_pos, VwrPositionStruct *end_pos, Boolean primary_select) { VwrTextViewWidget tv = (VwrTextViewWidget)w; VwrTextViewPart *tvp = &tv->textView; VwrPositionStruct tp; int cmp, new_top, page_amount, sel_amount; /* * Clear the old selection if any */ lose_selection(w, NULL); /* * Assign the new selection */ tvp->sel_mark = *start_pos; tvp->sel_point = *end_pos; /* * Increment to include last character */ tvp->sel_point.col++; /* * Do bounds checking on the span */ lc_bounds(tvp, &tvp->sel_mark); lc_bounds(tvp, &tvp->sel_point); /* * Make sure point is greater than mark */ if ((cmp = selcmp(&tvp->sel_mark, &tvp->sel_point)) > 0) { tp = tvp->sel_mark; tvp->sel_mark = tvp->sel_point; tvp->sel_point = tp; } /* * Highlight the text and scroll to see it */ if (cmp) { highlight(tv, &tvp->sel_mark, &tvp->sel_point, SELECT); if (primary_select) { if (XtOwnSelection(w, XA_PRIMARY, XtLastTimestampProcessed(XtDisplay(w)), transfer_selection, lose_selection, transfer_complete) == False) { XtWarning("TextView - cannot own selection, try again\n"); lose_selection(w, NULL); } } page_amount = tv->core.height / tvp->fontheight; sel_amount = tvp->sel_point.line - tvp->sel_mark.line + 1; if (sel_amount <= page_amount) { if (tvp->sel_mark.line < tvp->top) new_top = tvp->sel_mark.line; else new_top = tvp->top; if (tvp->sel_point.line >= new_top + page_amount) new_top = tvp->sel_point.line - page_amount + 1; } else { new_top = tvp->sel_mark.line; } if (new_top < 0) new_top = 0; else if (new_top + page_amount > tvp->text.nlines) new_top = tvp->text.nlines - page_amount; goto_line(tv, new_top); } } /************************************************************************** * * Function: VwrTextViewReadSelection * * Description: Returns the currently highlighted text, if any. The * text need be highlighted but does not need to have been made * the primary selection. * * NOTE: This function allocates a buffer for storage of the * text selection. It is the users responsibility to free this * buffer using XtFree when this buffer is no longer needed. * * Parameters: * w (I) - this widget * lenp (O) - number of bytes in returned selection buffer. * * Return: A pointer to the selection buffer or NULL if no text is * highlighted. * **************************************************************************/ char* VwrTextViewReadSelection(Widget w, unsigned long *lenp) { return get_selection(&((VwrTextViewWidget)w)->textView, lenp); } /************************************************************************** * * Function: VwrTextViewSearch * * Description: Searches the current text for the specified regular * expression. * * Parameters: * w (I) - this widget * search_str (I) - regular expression search string. If this * string is NULL, the previously specified string will * be used. Specifying this as NULL saves recompilation * of the expression string. * from_top (I) - if True, search starts at beggining of text. If * False, search continues where it left off. * start_pos (O) - starting text position of found string * end_pos (O) - ending position * * Return: 1 is returned if the search string has been found. 0 is * returned if the search string has not been found. If 0 is * returned, the values of the line and column variables is * undefined. If -1 is returned an error has occurred in * compiling the regular expression. * **************************************************************************/ int VwrTextViewSearch(Widget w, char *search_str, Boolean from_top, VwrPositionStruct *start_pos, VwrPositionStruct *end_pos) { char *old_start, *buf; int start_off, end_off; register int char_count, i; VwrTextViewWidget tv = (VwrTextViewWidget)w; VwrTextViewPart *tvp = &tv->textView; /* * Compile the regular expression */ if (search_str) { /* * Free any previous compiled expression */ if (tvp->comp_exp) free((char*)tvp->comp_exp); /* * Copy the search expression and replace common escape * sequences with their character equivalents */ buf = (char*)malloc(strlen(search_str) + 1); parse_escapes(search_str, buf); /* * Compile the new expression */ if ((tvp->comp_exp = regcmp(buf, NULL)) == NULL) return -1; /* * Free the buffer */ free((char*)buf); } /* * Sanity check starting pointer */ if (!tvp->search_next || from_top) tvp->search_next = tvp->text.char_buf; /* * Search for the string */ /* Remember where we started */ old_start = tvp->search_next; if ((tvp->search_next = regex(tvp->comp_exp, tvp->search_next)) == NULL) { /* If we couldn't match and we started at the beginning, forget it */ if (old_start == tvp->text.char_buf) { return 0; } /* Otherwise start at the beginning and try for a match */ else { if ((tvp->search_next = regex(tvp->comp_exp, tvp->text.char_buf)) == NULL) return 0; } } /* * Map the match start and end character pointer addresses * to line and column numbers */ /* Convert address to character buffer offsets */ start_off = (int)(__loc1 - tvp->text.char_buf); end_off = (int)(tvp->search_next - 1 - tvp->text.char_buf); /* Convert offsets to line numbers */ char_count = 0; for (i = 0; i < tvp->text.nlines; i++) { char_count += tvp->text.lines[i].line_nchars + 1; if (start_off < char_count) { start_pos->line = i; break; } } for (i = start_pos->line; i < tvp->text.nlines; i++) { if (end_off < char_count) { end_pos->line = i; break; } char_count += tvp->text.lines[i].line_nchars + 1; } /* Convert line addresses to column numbers */ start_pos->col = (int)(__loc1 - tvp->text.lines[start_pos->line].line_chars); end_pos->col = (int)(tvp->search_next - 1 - tvp->text.lines[end_pos->line].line_chars); return 1; }