/* * Copyright(c) 1992 Bell Communications Research, Inc. (Bellcore) * Copyright(c) 1995-99 Andrew Lister * * All rights reserved * Permission to use, copy, modify and distribute this material for * any purpose and without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all * copies, and that the name of Bellcore not be used in advertising * or publicity pertaining to this material without the specific, * prior written permission of an authorized representative of * Bellcore. * * BELLCORE MAKES NO REPRESENTATIONS AND EXTENDS NO WARRANTIES, EX- * PRESS OR IMPLIED, WITH RESPECT TO THE SOFTWARE, INCLUDING, BUT * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR ANY PARTICULAR PURPOSE, AND THE WARRANTY AGAINST IN- * FRINGEMENT OF PATENTS OR OTHER INTELLECTUAL PROPERTY RIGHTS. THE * SOFTWARE IS PROVIDED "AS IS", AND IN NO EVENT SHALL BELLCORE OR * ANY OF ITS AFFILIATES BE LIABLE FOR ANY DAMAGES, INCLUDING ANY * LOST PROFITS OR OTHER INCIDENTAL OR CONSEQUENTIAL DAMAGES RELAT- * ING TO THE SOFTWARE. * * $Id: Actions.c,v 1.1.1.1 2001/07/18 11:05:59 root Exp $ */ /* * Actions.c created by Andrew Lister (7 August, 1995) */ #ifdef HAVE_CONFIG_H #include #endif #include #include #if XmVersion > 1001 #include #endif #include #include "MatrixP.h" #include "Clip.h" #include "Draw.h" #include "Actions.h" #include "Utils.h" #include "ScrollMgr.h" #include "ClipP.h" #include #ifndef XlibSpecificationRelease #define XrmPermStringToQuark XrmStringToQuark #endif #if !defined(DRAW_RESIZE_LINE) && !defined(DRAW_RESIZE_SHADOW) /* One of DRAW_RESIZE_LINE and DRAW_RESIZE_SHADOW must be defined. */ #define DRAW_RESIZE_SHADOW #endif #ifndef DEFAULT_SCROLL_SPEED #define DEFAULT_SCROLL_SPEED 500 #endif typedef struct { XbaeMatrixWidget mw; GC gc; int row; int column; int startx; int lastx; int currentx; int y, height; short *columnWidths; Boolean grabbed; Boolean haveVSB; } XbaeMatrixResizeColumnStruct; typedef struct { XbaeMatrixWidget mw; int row; int column; Boolean pressed; Boolean grabbed; } XbaeMatrixButtonPressedStruct; typedef struct { XbaeMatrixWidget mw; XbaeClipWidget cw; XEvent *event; XtIntervalId timerID; XtAppContext app_context; unsigned long interval; Boolean inClip; Boolean grabbed; Boolean above; Boolean below; Boolean left; Boolean right; } XbaeMatrixScrollStruct; static int DoubleClick P((XbaeMatrixWidget, XEvent *, int, int)); static void DrawSlideColumn P((XbaeMatrixWidget, int)); static void SlideColumn P((Widget, XtPointer, XEvent *, Boolean *)); static void PushButton P((Widget, XtPointer, XEvent *, Boolean *)); static void updateScroll P((XtPointer)); static void checkScrollValues P((Widget, XtPointer, XEvent *, Boolean *)); static void callSelectCellAction P((XbaeMatrixWidget, XEvent *)); static int last_row = 0; static int last_column = 0; static int last_selected_row = 0; static int last_selected_column = 0; static Boolean scrolling = False; /* ARGSUSED */ void xbaeDefaultActionACT(w, event, params, nparams) Widget w; XEvent *event; String *params; Cardinal *nparams; { XbaeMatrixWidget mw; int x, y; int row, column; CellType cell; /* * Get Matrix widget and make sure it is a Matrix subclass. * w could be Matrix, or the Clip or textField children of Matrix */ if (XtIsSubclass(w, xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)w; else if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)XtParent(w); else { XtAppWarningMsg( XtWidgetToApplicationContext(w), "defaultActionACT", "badWidget", "XbaeMatrix", "XbaeMatrix: Bad widget passed to DefaultAction action", NULL, 0); return; } if (!mw->matrix.default_action_callback) return; if (!xbaeEventToXY(mw, event, &x, &y, &cell)) return; if (!xbaeXYToRowCol(mw, &x, &y, &row, &column, cell)) return; if (DoubleClick(mw, event, row, column)) { XbaeMatrixDefaultActionCallbackStruct call_data; call_data.reason = XbaeDefaultActionReason; call_data.event = event; call_data.row = row; call_data.column = column; XtCallCallbackList((Widget)mw, mw->matrix.default_action_callback, (XtPointer)&call_data); } } static void DrawSlideColumn(mw, x) XbaeMatrixWidget mw; int x; { #ifdef DRAW_RESIZE_SHADOW /* These values derived through that age-old process * of what looks good to me */ #define SHADOW_WIDTH 2 #define RESIZE_COLUMN_LINE_WIDTH 4 Dimension width = RESIZE_COLUMN_LINE_WIDTH; Dimension shadow_width = SHADOW_WIDTH; #endif Dimension height; Window win; Display *display = XtDisplay(mw); int column = xbaeXtoCol(mw, x - COLUMN_LABEL_OFFSET(mw)); int top, bottom; int adjusted_x; int y; #ifdef DRAW_RESIZE_LINE GC gc = mw->matrix.draw_gc; #endif Boolean need_vert_dead_space_fill = NEED_VERT_DEAD_SPACE_FILL(mw); unsigned int clip_reason; /* * If the column being resized is a fixed one then we don't need to * bother with the clip region */ if (column < (int)mw->matrix.fixed_columns) { y = ROW_LABEL_OFFSET(mw); height = VISIBLE_HEIGHT(mw) + FIXED_ROW_HEIGHT(mw) + TRAILING_FIXED_ROW_HEIGHT(mw); win = XtWindow (mw); if (need_vert_dead_space_fill) height += VERT_DEAD_SPACE_HEIGHT(mw); #ifdef DRAW_RESIZE_LINE XDrawLine(display, win, gc, x, y, x, y + height); if (XtIsManaged(LeftClip(mw))) XDrawLine(display, XtWindow(LeftClip(mw)), gc, x - COLUMN_LABEL_OFFSET(mw), 0, x - COLUMN_LABEL_OFFSET(mw), LeftClip(mw)->core.height); #endif #ifdef DRAW_RESIZE_SHADOW DRAW_SHADOW(display, win, mw->matrix.resize_top_shadow_gc, mw->matrix.resize_bottom_shadow_gc, shadow_width, x, y, width, height, XmSHADOW_OUT); if (XtIsManaged(LeftClip(mw))) DRAW_SHADOW(display, XtWindow(LeftClip(mw)), mw->matrix.resize_top_shadow_gc, mw->matrix.resize_bottom_shadow_gc, shadow_width, x - COLUMN_LABEL_OFFSET(mw), 0, width, LeftClip(mw)->core.height, XmSHADOW_OUT); #endif return; } /* * Similarly for trailingFixedColumns - beware going off the clip child * here also */ if (column >= TRAILING_HORIZ_ORIGIN(mw) || x >= (int)(ClipChild(mw)->core.x + ClipChild(mw)->core.width)) { y = ROW_LABEL_OFFSET(mw); height = VISIBLE_HEIGHT(mw) + FIXED_ROW_HEIGHT(mw) + TRAILING_FIXED_ROW_HEIGHT(mw); win = XtWindow(mw); if (need_vert_dead_space_fill) height += VERT_DEAD_SPACE_HEIGHT(mw); #ifdef DRAW_RESIZE_LINE XDrawLine(display, win, gc, x, y, x, y + height); if (XtIsManaged(RightClip(mw))) XDrawLine(display, XtWindow(RightClip(mw)), gc, x - TRAILING_FIXED_COLUMN_LABEL_OFFSET(mw), 0, x - TRAILING_FIXED_COLUMN_LABEL_OFFSET(mw), RightClip(mw)->core.height); #endif #ifdef DRAW_RESIZE_SHADOW DRAW_SHADOW(display, win, mw->matrix.resize_top_shadow_gc, mw->matrix.resize_bottom_shadow_gc, shadow_width, x, y, width, height, XmSHADOW_OUT); if (XtIsManaged(RightClip(mw))) DRAW_SHADOW(display, XtWindow(RightClip(mw)), mw->matrix.resize_top_shadow_gc, mw->matrix.resize_bottom_shadow_gc, shadow_width, x - TRAILING_FIXED_COLUMN_LABEL_OFFSET(mw), 0, width, RightClip(mw)->core.height, XmSHADOW_OUT); #endif return; } xbaeGetVisibleRows(mw, &top, &bottom); /* * we need all non-fixed rows, so add 1 to bottom * to include the last one as the return values * are inclusive */ bottom += 1; /* * The area between top and bottom rows are the non fixed rows. They * fall on the ClipChild */ y = -mw->matrix.cell_shadow_thickness; /* relative to clip */ height = ROW_HEIGHT(mw) * (bottom - top) + 2 * mw->matrix.cell_shadow_thickness; /* * If we are on the clip, the x location is offset by the * fixed column width, label offset and label width */ adjusted_x = x - FIXED_COLUMN_LABEL_OFFSET(mw); win = XtWindow(ClipChild(mw)); #ifdef DRAW_RESIZE_LINE XDrawLine(display, win, gc, adjusted_x, y, adjusted_x, y + height); #endif #ifdef DRAW_RESIZE_SHADOW DRAW_SHADOW(display, win, mw->matrix.resize_top_shadow_gc, mw->matrix.resize_bottom_shadow_gc, shadow_width, adjusted_x, y, width, height, XmSHADOW_OUT); #endif /* * Now draw the line (or shadow) on the non clipped region - that is * the fixed and trailingFixed rows. First, do the leading rows. */ if (mw->matrix.fixed_rows) { y = ROW_LABEL_OFFSET(mw); height = FIXED_ROW_HEIGHT(mw) + 2 * mw->matrix.cell_shadow_thickness; win = XtWindow(mw); xbaeSetClipMask(mw, CLIP_FIXED_ROWS); #ifdef DRAW_RESIZE_LINE if (XtIsManaged(TopClip(mw))) XDrawLine(display, XtWindow(TopClip(mw)), gc, adjusted_x, -mw->matrix.cell_shadow_thickness, adjusted_x, height); #endif #ifdef DRAW_RESIZE_SHADOW if (XtIsManaged(TopClip(mw))) DRAW_SHADOW(display, XtWindow(TopClip(mw)), mw->matrix.resize_top_shadow_gc, mw->matrix.resize_bottom_shadow_gc, shadow_width, adjusted_x, -mw->matrix.cell_shadow_thickness, width, height, XmSHADOW_OUT); #endif xbaeSetClipMask(mw, CLIP_NONE); } /* * The trailingFixedRows */ if (mw->matrix.trailing_fixed_rows) { y = TRAILING_FIXED_ROW_LABEL_OFFSET(mw); height = TRAILING_FIXED_ROW_HEIGHT(mw) + 2 * mw->matrix.cell_shadow_thickness; clip_reason = CLIP_TRAILING_FIXED_ROWS; if (IS_LEADING_FIXED_COLUMN(mw, column)) clip_reason |= CLIP_FIXED_COLUMNS; else if (IS_TRAILING_FIXED_COLUMN(mw, column)) clip_reason |= CLIP_TRAILING_FIXED_COLUMNS; xbaeSetClipMask(mw, clip_reason); #ifdef DRAW_RESIZE_LINE if (XtIsManaged(BottomClip(mw))) XDrawLine(display, XtWindow(BottomClip(mw)), gc, adjusted_x, -mw->matrix.cell_shadow_thickness, adjusted_x, height); #endif #ifdef DRAW_RESIZE_SHADOW if (XtIsManaged(BottomClip(mw))) DRAW_SHADOW(display, XtWindow(BottomClip(mw)), mw->matrix.resize_top_shadow_gc, mw->matrix.resize_bottom_shadow_gc, shadow_width, adjusted_x, -mw->matrix.cell_shadow_thickness, width, height, XmSHADOW_OUT); #endif xbaeSetClipMask(mw, CLIP_NONE); } if ((NEED_VERT_FILL(mw) && (! HAS_ATTACHED_TRAILING_ROWS(mw))) || need_vert_dead_space_fill) { if (need_vert_dead_space_fill) { y = UNATTACHED_TRAILING_ROWS_OFFSET(mw) - mw->matrix.cell_shadow_thickness; height = 2 * mw->matrix.cell_shadow_thickness + VERT_DEAD_SPACE_HEIGHT(mw); } else { y = TRAILING_FIXED_ROW_LABEL_OFFSET(mw) + TRAILING_FIXED_ROW_HEIGHT(mw); height = FILL_VERT_HEIGHT(mw) - HORIZ_SB_SPACE(mw); } #ifdef DRAW_RESIZE_LINE XDrawLine(display, XtWindow(mw), gc, adjusted_x, y, adjusted_x, height); #endif #ifdef DRAW_RESIZE_SHADOW DRAW_SHADOW(display, XtWindow(mw), mw->matrix.resize_top_shadow_gc, mw->matrix.resize_bottom_shadow_gc, shadow_width, x, y, width, height, XmSHADOW_OUT); #endif } } static void SlideColumn(w, data, event, cont) Widget w; XtPointer data; XEvent *event; Boolean *cont; { XbaeMatrixResizeColumnStruct *rd = (XbaeMatrixResizeColumnStruct *)data; XMotionEvent *motionEvent; Boolean relayout = False; int numCharacters; int i; if (event->type == ButtonRelease) { DrawSlideColumn(rd->mw, rd->lastx); XUngrabPointer(XtDisplay(w), CurrentTime); rd->grabbed = False; /* * Remanage the VSB if we unmapped it earlier */ if (rd->haveVSB) XtManageChild(VertScrollChild(rd->mw)); if (rd->mw->matrix.resize_column_callback) { XbaeMatrixResizeColumnCallbackStruct call_data; call_data.reason = XbaeResizeColumnReason; call_data.event = event; call_data.row = rd->row; call_data.column = rd->column - 1; call_data.which = rd->column - 1; call_data.columns = rd->mw->matrix.columns; call_data.column_widths = rd->columnWidths; XtCallCallbackList ((Widget)rd->mw, rd->mw->matrix.resize_column_callback, (XtPointer)&call_data); } for (i = 0; i < rd->mw->matrix.columns; i++) if (rd->columnWidths[i] != rd->mw->matrix.column_widths[i]) { /* Make sure everything is handled correctly with SetValues */ XtVaSetValues((Widget)rd->mw, XmNcolumnWidths, rd->columnWidths, NULL); break; } /* * If maxColumnLengths are set and we have resized the column to * larger, reset the corresponding maxColumnLength */ if (rd->mw->matrix.column_max_lengths && rd->columnWidths[rd->column - 1] > rd->mw->matrix.column_max_lengths[rd->column - 1]) rd->mw->matrix.column_max_lengths[rd->column - 1] = rd->columnWidths[rd->column - 1]; XtFree((char *)rd->columnWidths); return; } if (event->type != MotionNotify) /* Double check! */ return; motionEvent = (XMotionEvent *)event; if (rd->currentx - motionEvent->x > FONT_WIDTH(rd->mw)) { /* If we're only one character wide, we cannae get any smaller */ if (rd->columnWidths[rd->column - 1] == BAD_WIDTH + 1) return; /* * Moved left a full character - update the column widths and force * a redisplay */ numCharacters = (rd->currentx - motionEvent->x) / FONT_WIDTH(rd->mw); if (numCharacters >= rd->columnWidths[rd->column - 1]) /* Must keep a column at least one character wide */ numCharacters = rd->columnWidths[rd->column - 1] - 1; rd->columnWidths[rd->column - 1] -= numCharacters; rd->currentx -= numCharacters * FONT_WIDTH(rd->mw); relayout = True; } if (motionEvent->x - rd->currentx > FONT_WIDTH(rd->mw)) { /* * Moved right a full character - update the column widths and force * a redisplay */ numCharacters = (motionEvent->x - rd->currentx) / FONT_WIDTH(rd->mw); rd->columnWidths[rd->column - 1] += numCharacters; rd->currentx += numCharacters * FONT_WIDTH(rd->mw); relayout = True; } if (relayout) { /* Draw the marker line in the new location */ if (rd->lastx != rd->currentx) { DrawSlideColumn(rd->mw, rd->currentx); DrawSlideColumn(rd->mw, rd->lastx); rd->lastx = rd->currentx; } } } /* ARGSUSED */ void xbaeResizeColumnsACT(w, event, params, nparams) Widget w; XEvent *event; String *params; Cardinal *nparams; { XbaeMatrixWidget mw; int x, y; int eventx; int i; int row, column; CellType cell; static Cursor cursor; XbaeMatrixResizeColumnStruct resizeData; XGCValues values; XtAppContext appcontext; #ifdef DRAW_RESIZE_LINE XGCValues save; #endif unsigned long gcmask, event_mask; Display *display = XtDisplay(w); #define FUZZ_FACTOR 3 int fuzzy = FUZZ_FACTOR; #undef FUZZ_FACTOR /* * Get Matrix widget and make sure it is a Matrix subclass. * w could be Matrix, or the Clip or textField children of Matrix */ if (XtIsSubclass(w, xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)w; else if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)XtParent(w); else { XtAppWarningMsg( XtWidgetToApplicationContext(w), "resizeColumnsACT", "badWidget", "XbaeMatrix", "XbaeMatrix: Bad widget passed to ResizeColumns action", NULL, 0); return; } /* * If we won't allow dynamic column resize, leave. */ if (!mw->matrix.allow_column_resize) return; if (!xbaeEventToXY(mw, event, &x, &y, &cell)) return; eventx = x; if (!xbaeXYToRowCol(mw, &x, &y, &row, &column, cell)) return; /* * Calculate if the x and y of the middle button event is on * a column border. Allow the width of the shadow to be the * allowed delta. x is modified in xbaeXYToRowCol() to be * the x distance from the cell's border */ if ((int)mw->matrix.cell_shadow_thickness > fuzzy) fuzzy = mw->matrix.cell_shadow_thickness; if (x > fuzzy && COLUMN_WIDTH(mw, column) - x > fuzzy) return; /* * Looks like we hit a column border, determine the column that is * intended to be resized */ if ((COLUMN_WIDTH(mw, column) - x) <= fuzzy) column++; /* Can't adjust the origin or should you be able to?? */ if (column == 0) return; /* * Make it here and it's time to start the fun stuff! */ /* Create the left / right cursor */ if (!cursor) cursor = XCreateFontCursor(display, XC_sb_h_double_arrow); /* Commit any edit in progress and unmap the text field - it's just bad luck */ (*((XbaeMatrixWidgetClass)XtClass(mw))->matrix_class.commit_edit) (mw, event, True); /* * Redraw the cell that had the text field in it or it might stay blank */ xbaeDrawCell(mw, mw->matrix.current_row, mw->matrix.current_column); /* * Say goodbye to the Vertical ScrollBar -> it only gets in the way! */ if ((resizeData.haveVSB = XtIsManaged(VertScrollChild(mw)) && ((mw->matrix.scrollbar_placement == XmTOP_RIGHT) || (mw->matrix.scrollbar_placement == XmBOTTOM_RIGHT)))) XtUnmanageChild(VertScrollChild(mw)); /* * Flush the commit events out to the server. Otherwise, our changes * to the GCs below have a bad effect. */ XSync(display, False); event_mask = PointerMotionMask | ButtonReleaseMask; XtAddEventHandler(w, event_mask, True, (XtEventHandler)SlideColumn, (XtPointer)&resizeData); XGrabPointer(display, XtWindow(w), True, event_mask, GrabModeAsync, GrabModeAsync, XtWindow((Widget)mw), cursor, CurrentTime); /* Copy the columnWidth array */ resizeData.columnWidths = (short *)XtMalloc(mw->matrix.columns * sizeof(short)); for (i = 0; i < mw->matrix.columns; i++) resizeData.columnWidths[i] = mw->matrix.column_widths[i]; resizeData.grabbed = True; resizeData.mw = mw; resizeData.column = column; resizeData.startx = resizeData.currentx = resizeData.lastx = event->xbutton.x; gcmask = GCForeground | GCBackground | GCFunction; values.function = GXxor; #ifdef DRAW_RESIZE_LINE XGetGCValues(display, mw->matrix.draw_gc, gcmask, &save); values.foreground = values.background = save.background; XChangeGC(display, mw->matrix.draw_gc, gcmask, &values); #endif DrawSlideColumn(mw, resizeData.currentx); appcontext = XtWidgetToApplicationContext(w); while (resizeData.grabbed) XtAppProcessEvent(appcontext, XtIMAll); XtRemoveEventHandler(w, event_mask, True, (XtEventHandler)SlideColumn, (XtPointer)&resizeData); #ifdef DRAW_RESIZE_LINE XSetFunction(display, mw->matrix.draw_gc, GXcopy); #endif } /* * Action to process a drag out */ /* ARGSUSED */ void xbaeProcessDragACT(w, event, params, nparams) Widget w; XEvent *event; String *params; Cardinal *nparams; { #if XmVersion > 1001 XbaeMatrixWidget mw; int x, y; int row, column; CellType cell; XbaeMatrixProcessDragCallbackStruct call_data; /* * Get Matrix widget and make sure it is a Matrix subclass. * w could be Matrix, or the Clip or textField children of Matrix */ if (XtIsSubclass(w, xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)w; else if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)XtParent(w); else { XtAppWarningMsg( XtWidgetToApplicationContext(w), "processDragACT", "badWidget", "XbaeMatrix", "XbaeMatrix: Bad widget passed to ProcessDrag action", NULL, 0); return; } if (!mw->matrix.process_drag_callback) return; if (!xbaeEventToXY(mw, event, &x, &y, &cell)) return; if (!xbaeXYToRowCol(mw, &x, &y, &row, &column, cell)) return; call_data.reason = XbaeProcessDragReason; call_data.event = event; call_data.row = row; call_data.column = column; if (mw->matrix.draw_cell_callback) { Pixel bgcolor, fgcolor; int width, height, depth; call_data.type = xbaeGetDrawCellValue( mw, row, column, &call_data.string, &call_data.pixmap, &call_data.mask, &width, &height, &bgcolor, &fgcolor, &depth); } else call_data.string = mw->matrix.cells ? mw->matrix.cells[row][column] : ""; call_data.num_params = *nparams; call_data.params = params; XtCallCallbackList((Widget)mw, mw->matrix.process_drag_callback, (XtPointer)&call_data); #endif } /* * Action to edit a non-fixed cell. */ /* ARGSUSED */ void xbaeEditCellACT(w, event, params, nparams) Widget w; XEvent *event; String *params; Cardinal *nparams; { XbaeMatrixWidget mw; int row, column; XrmQuark q; static XrmQuark QPointer, QLeft, QRight, QUp, QDown; static Boolean haveQuarks = False; /* * Get static quarks for the parms we understand */ if (!haveQuarks) { QPointer = XrmPermStringToQuark("Pointer"); QLeft = XrmPermStringToQuark("Left"); QRight = XrmPermStringToQuark("Right"); QUp = XrmPermStringToQuark("Up"); QDown = XrmPermStringToQuark("Down"); haveQuarks = True; } /* * Get Matrix widget and make sure it is a Matrix subclass. * w could be Matrix, or the Clip or textField children of Matrix */ if (XtIsSubclass(w, xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)w; else if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)XtParent(w); else { XtAppWarningMsg( XtWidgetToApplicationContext(w), "editCellACT", "badWidget", "XbaeMatrix", "XbaeMatrix: Bad widget passed to EditCell action", NULL, 0); return; } /* * Make sure we have a single parm */ if (*nparams != 1) { XtAppWarningMsg( XtWidgetToApplicationContext(w), "editCellACT", "badParms", "XbaeMatrix", "XbaeMatrix: Wrong params passed to EditCell action, needs 1", NULL, 0); return; } /* * Initialize row/column to the current position */ row = mw->matrix.current_row; column = mw->matrix.current_column; /* * Quarkify the string param */ q = XrmStringToQuark(params[0]); /* * If we aren't currently editing, then the only kind of traversal that * makes sense is pointer. */ if (!XtIsManaged(TextChild(mw)) && q != QPointer) return; if (q == QPointer) { CellType cellType = NonFixedCell; int x, y; /* * Get the x,y point coordinate relative to the Clip window. * Return if this event did not occur in the Clip subwindow * (since we can only edit non-fixed cells). */ switch(event->type) { case ButtonPress: case ButtonRelease: x = event->xbutton.x; y = event->xbutton.y; break; case KeyPress: case KeyRelease: x = event->xkey.x; y = event->xkey.y; break; case MotionNotify: x = event->xmotion.x; y = event->xmotion.y; break; default: return; } if (event->xbutton.subwindow == XtWindow(ClipChild(mw))) { x -= FIXED_COLUMN_LABEL_OFFSET(mw); y -= FIXED_ROW_LABEL_OFFSET(mw); cellType = NonFixedCell; } else if (event->xbutton.window != XtWindow(ClipChild(mw))) { if (!mw->matrix.traverse_fixed) return; cellType = FixedCell; } /* * Convert the point to a row,column. If it does not pick a valid * cell, then return. */ if (!xbaeXYToRowCol(mw, &x, &y, &row, &column, cellType)) return; } else if (q == QRight) { /* * If we are in the lower right corner, stay there. * Otherwise move over a column. If we move off to the right of the * final column to which traversing is allowed then move down a row * and back to the first column to which traversing is allowed. */ if (!mw->matrix.traverse_fixed) { /* check scrollable boundary */ if (mw->matrix.current_row != TRAILING_VERT_ORIGIN(mw) - 1 || mw->matrix.current_column != TRAILING_HORIZ_ORIGIN(mw) - 1) { column++; if (IS_TRAILING_FIXED_COLUMN(mw, column)) { column = mw->matrix.fixed_columns; row++; } } } else { /* check matrix boundary */ if (mw->matrix.current_row != mw->matrix.rows - 1 || mw->matrix.current_column != mw->matrix.columns - 1) { column++; if (column >= mw->matrix.columns) { column = 0; row++; } } } } else if (q == QLeft) { /* * If we are in the upper left corner, stay there. * Otherwise move back a column. If we move before the first column * to which traversing is allowed, move up a row and over to the last * column to which traversing is allowed. */ if (!mw->matrix.traverse_fixed) { /* check scrollable boundary */ if (mw->matrix.current_row != mw->matrix.fixed_rows || mw->matrix.current_column != mw->matrix.fixed_columns) { column--; if (IS_LEADING_FIXED_COLUMN(mw, column)) { column = TRAILING_HORIZ_ORIGIN(mw) - 1; row--; } } } else { if (mw->matrix.current_row != 0 || mw->matrix.current_column != 0) { column--; if (column < 0) { column = mw->matrix.columns - 1; row--; } } } } else if (q == QDown) { row++; /* adjust row for allowable traversable regions */ if (!mw->matrix.traverse_fixed) { if (IS_TRAILING_FIXED_ROW(mw, row)) row = mw->matrix.fixed_rows; } else { if (row >= mw->matrix.rows) row = 0; } } else if (q == QUp) { row--; if (!mw->matrix.traverse_fixed) { if (IS_LEADING_FIXED_ROW(mw, row)) row = TRAILING_VERT_ORIGIN(mw) - 1; } else { if (row < 0) row = mw->matrix.rows - 1; } } /* * Call the traverseCellCallback to allow the application to * perform custom traversal. */ if (mw->matrix.traverse_cell_callback) { XbaeMatrixTraverseCellCallbackStruct call_data; call_data.reason = XbaeTraverseCellReason; call_data.event = event; call_data.row = mw->matrix.current_row; call_data.column = mw->matrix.current_column; call_data.next_row = row; call_data.next_column = column; call_data.fixed_rows = mw->matrix.fixed_rows; call_data.fixed_columns = mw->matrix.fixed_columns; call_data.trailing_fixed_rows = mw->matrix.trailing_fixed_rows; call_data.trailing_fixed_columns = mw->matrix.trailing_fixed_columns; call_data.num_rows = mw->matrix.rows; call_data.num_columns = mw->matrix.columns; call_data.param = params[0]; call_data.qparam = q; XtCallCallbackList((Widget)mw, mw->matrix.traverse_cell_callback, (XtPointer)&call_data); row = call_data.next_row; column = call_data.next_column; } /* * Attempt to edit the new cell using the edit_cell method. * If we are editing a cell based on pointer position, we always * call edit_cell. Otherwise, we must be editing a new cell to * call edit_cell. */ if (q == QPointer || (row != mw->matrix.current_row || column != mw->matrix.current_column)) (*((XbaeMatrixWidgetClass)XtClass(mw))->matrix_class.edit_cell) (mw, event, row, column, params, *nparams); /* * Traverse to the textField */ (void)XmProcessTraversal(TextChild(mw), XmTRAVERSE_CURRENT); } /* * Action to unmap the textField and discard any edits made */ /* ARGSUSED */ void xbaeCancelEditACT(w, event, params, nparams) Widget w; XEvent *event; String *params; Cardinal *nparams; { XbaeMatrixWidget mw; Boolean unmap; /* * Get Matrix widget and make sure it is a Matrix subclass. * w could be Matrix, or the Clip or textField children of Matrix */ if (XtIsSubclass(w, xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)w; else if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)XtParent(w); else { XtAppWarningMsg( XtWidgetToApplicationContext(w), "cancelEditACT", "badWidget", "XbaeMatrix", "XbaeMatrix: Bad widget passed to CancelEdit action", NULL, 0); return; } /* * Make sure we have a single param */ if (*nparams != 1) { XtAppWarningMsg( XtWidgetToApplicationContext(w), "cancelEditACT", "badParms", "XbaeMatrix", "XbaeMatrix: Wrong params passed to CancelEdit action, needs 1", NULL, 0); return; } /* * Validate our param */ if (!strcmp(params[0], "True")) unmap = True; else if (!strcmp(params[0], "False")) unmap = False; else { XtAppWarningMsg( XtWidgetToApplicationContext(w), "cancelEditACT", "badParm", "XbaeMatrix", "XbaeMatrix: Bad parameter for CancelEdit action", NULL, 0); return; } /* * Call the cancel_edit method */ (*((XbaeMatrixWidgetClass)XtClass(mw))->matrix_class.cancel_edit) (mw, unmap); } /* * Action save any edits made and unmap the textField if params[0] is True */ /* ARGSUSED */ void xbaeCommitEditACT(w, event, params, nparams) Widget w; XEvent *event; String *params; Cardinal *nparams; { XbaeMatrixWidget mw; Boolean unmap; /* * Get Matrix widget and make sure it is a Matrix subclass. * w could be Matrix, or the Clip or textField children of Matrix */ if (XtIsSubclass(w, xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)w; else if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)XtParent(w); else { XtAppWarningMsg( XtWidgetToApplicationContext(w), "commitEditACT", "badWidget", "XbaeMatrix", "XbaeMatrix: Bad widget passed to CommitEdit action", NULL, 0); return; } /* * Make sure we have a single param */ if (*nparams != 1) { XtAppWarningMsg( XtWidgetToApplicationContext(w), "commitEditACT", "badParms", "XbaeMatrix", "XbaeMatrix: Wrong params for CommitEdit action, needs 1", NULL, 0); return; } /* * Validate our param */ if (!strcmp(params[0], "True")) unmap = True; else if (!strcmp(params[0], "False")) unmap = False; else { XtAppWarningMsg( XtWidgetToApplicationContext(w), "commitEditACT", "badParm", "XbaeMatrix", "XbaeMatrix: Bad parameter for CommitEdit action", NULL, 0); return; } (void)(*((XbaeMatrixWidgetClass)XtClass(mw))->matrix_class.commit_edit) (mw, event, unmap); } static int DoubleClick(mw, event, row, column) XbaeMatrixWidget mw; XEvent *event; int row; int column; { /* A double click in this instance is two clicks in the same cell in a time period < double_click_interval */ Time current_time; unsigned long delta; static int ret = 0; if (event->type == ButtonRelease) { /* If the button is released, store the current location and time - next time through, if it's a button press event, we check for double click */ mw->matrix.last_row = row; mw->matrix.last_column = column; if (ret) /* just had a double click */ mw->matrix.last_click_time = (Time)0; else mw->matrix.last_click_time = event->xbutton.time; ret = 0; return ret; } current_time = event->xbutton.time; delta = current_time - mw->matrix.last_click_time; if (row == mw->matrix.last_row && column == mw->matrix.last_column && delta < (unsigned long)mw->matrix.double_click_interval) ret = 1; else ret = 0; return ret; } /*ARGSUSED*/ static void PushButton(w, data, event, cont) Widget w; XtPointer data; XEvent *event; Boolean *cont; { XbaeMatrixButtonPressedStruct *button = (XbaeMatrixButtonPressedStruct *)data; XMotionEvent *motionEvent; int x, y; int row, column; Boolean pressed = button->pressed; CellType cell; if (event->type == ButtonRelease) { button->grabbed = False; XtRemoveGrab(w); scrolling = False; if (button->pressed) { /* If the button is still pressed, it has been released in the same button that was pressed. "Unpress" it and call the callbacks */ if (button->column == -1) xbaeDrawRowLabel(button->mw, button->row, False); else if (button->row == -1) xbaeDrawColumnLabel(button->mw, button->column, False); if (button->mw->matrix.label_activate_callback) { XbaeMatrixLabelActivateCallbackStruct call_data; call_data.reason = XbaeLabelActivateReason; call_data.event = event; call_data.row_label = (button->column == -1); call_data.row = button->row; call_data.column = button->column; if (button->column == -1) call_data.label = button->mw->matrix.row_labels[button->row]; else call_data.label = button->mw->matrix.column_labels[button->column]; XtCallCallbackList((Widget)button->mw, button->mw->matrix.label_activate_callback, (XtPointer)&call_data); } } return; } if (event->type != MotionNotify) /* We want to be sure about this! */ return; motionEvent = (XMotionEvent *)event; x = motionEvent->x; y = motionEvent->y; if (!xbaeEventToXY(button->mw, event, &x, &y, &cell)) return; if (xbaeXYToRowCol(button->mw, &x, &y, &row, &column, cell)) /* Moved off the labels */ pressed = False; else { if (button->column != column || button->row != row) /* Moved out of the button that was originally pressed */ pressed = False; else if (button->column == column || button->row == row) pressed = True; } /* If the status of whether or not the button should be pressed has changed, redraw the appropriate visual */ if (pressed != button->pressed) { if (button->column == -1) xbaeDrawRowLabel(button->mw, button->row, pressed); else if (button->row == -1) xbaeDrawColumnLabel(button->mw, button->column, pressed); /* And set our struct's pressed member to the current setting */ button->pressed = pressed; } } /*ARGSUSED*/ void xbaeHandleClick(w, data, event, cont) Widget w; XtPointer data; XEvent *event; Boolean *cont; { XbaeMatrixWidget mw = (XbaeMatrixWidget)data; int x, y; CellType cell; int row, column; Boolean translation; /* if we have a double click and a callback - break out! */ if (event->type != ButtonPress && event->type != ButtonRelease) return; if (!xbaeEventToXY(mw, event, &x, &y, &cell)) return; translation = xbaeXYToRowCol(mw, &x, &y, &row, &column, cell); if (!translation && (mw->matrix.button_labels || (row == -1 && mw->matrix.column_button_labels && mw->matrix.column_button_labels[column]) || (column == -1 && mw->matrix.row_button_labels && mw->matrix.row_button_labels[row])) && ((row == -1) ^ (column == -1))) { unsigned long event_mask; XtAppContext appcontext; XbaeMatrixButtonPressedStruct button; /* If the row and column are invalid, return. If it is ButtonRelease event, also return - the ButtonRelease events are handled in the event handler loop below */ if (event->type != ButtonPress) return; if (column == -1 && event->type == ButtonPress) /* row label */ xbaeDrawRowLabel(mw, row, True); else if (row == -1 && event->type == ButtonPress) /* Column label */ xbaeDrawColumnLabel(mw, column, True); /* Action stations! */ event_mask = ButtonReleaseMask | PointerMotionMask; scrolling = True; XtAddGrab(w, True, False); /* Copy the data needed to be passed to the event handler */ button.mw = mw; button.row = row; button.column = column; button.pressed = True; button.grabbed = True; XtAddEventHandler(w, event_mask, True, (XtEventHandler)PushButton, (XtPointer)&button); XtAddEventHandler(TextChild(mw), event_mask, True, (XtEventHandler)PushButton, (XtPointer)&button); appcontext = XtWidgetToApplicationContext(w); while (button.grabbed) XtAppProcessEvent(appcontext, XtIMAll); XtRemoveEventHandler(w, event_mask, True, (XtEventHandler)PushButton, (XtPointer)&button); XtRemoveEventHandler(TextChild(mw), event_mask, True, (XtEventHandler)PushButton, (XtPointer)&button); } else if (translation && mw->matrix.default_action_callback && w != (Widget)mw && DoubleClick(mw, event, mw->matrix.current_row, mw->matrix.current_column)) { /* Put this as an else -> we don't want double clicks on labels to be recognised */ XbaeMatrixDefaultActionCallbackStruct call_data; if (row == -1 || column == -1) return; call_data.reason = XbaeDefaultActionReason; call_data.event = event; call_data.row = row; call_data.column = column; XtCallCallbackList((Widget)mw, mw->matrix.default_action_callback, (XtPointer)&call_data); } } /* ARGSUSED */ void xbaeSelectCellACT(w, event, params, nparams) Widget w; XEvent *event; String *params; Cardinal *nparams; { XbaeMatrixWidget mw; int x, y; int row, column; CellType cell; XbaeMatrixSelectCellCallbackStruct call_data; /* * Get Matrix widget and make sure it is a Matrix subclass. * w could be Matrix, or the Clip or textField children of Matrix */ if (XtIsSubclass(w, xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)w; else if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)XtParent(w); else { XtAppWarningMsg( XtWidgetToApplicationContext(w), "xbaeSelectCellACT", "badWidget", "XbaeMatrix", "XbaeMatrix: Bad widget passed to SelectCell action", NULL, 0); return; } /* * If we don't have a selectCellCallback, then return now */ if (!mw->matrix.select_cell_callback) return; if (!xbaeEventToXY(mw, event, &x, &y, &cell)) return; /* * Convert the point to a row,column. If it does not pick a valid * cell, then return. If button up then use the last selected cell * to make sure a valid button up event occurs when dragging out of * the matrix */ if (!xbaeXYToRowCol(mw, &x, &y, &row, &column, cell)) { if (event->type == ButtonRelease) { column = last_selected_column; row = last_selected_row; } else { return; } } /* * Call our select_cell callbacks */ call_data.reason = XbaeSelectCellReason; call_data.event = event; if (scrolling) { call_data.row = last_row; call_data.column = last_column; } else { call_data.row = row; call_data.column = column; } last_selected_column = call_data.column; last_selected_row = call_data.row; call_data.selected_cells = mw->matrix.selected_cells; call_data.cells = mw->matrix.cells; call_data.num_params = *nparams; call_data.params = params; XtCallCallbackList((Widget)mw, mw->matrix.select_cell_callback, (XtPointer)&call_data); } /* ARGSUSED */ void xbaeTraverseNextACT(w, event, params, nparams) Widget w; XEvent *event; String *params; Cardinal *nparams; { XbaeMatrixWidget mw; /* * Get Matrix widget and make sure it is a Matrix subclass. * w should be the textField widget. */ if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)XtParent(w); else { XtAppWarningMsg( XtWidgetToApplicationContext(w), "traverseNextACT", "badWidget", "XbaeMatrix", "XbaeMatrix: Bad widget passed to TraverseNext action", NULL, 0); return; } /* * Set the traversing direction flag. XmProcessTraversal may traverse * to the Clip widget. If it does, then we will see this flag in * the Clip focusCallback, TraverseInCB, and we will continue to traverse * on out of the mw. yuck! */ mw->matrix.traversing = XmTRAVERSE_NEXT_TAB_GROUP; (void)XmProcessTraversal(TextChild(mw), XmTRAVERSE_NEXT_TAB_GROUP); mw->matrix.traversing = NOT_TRAVERSING; } /* ARGSUSED */ void xbaeTraversePrevACT(w, event, params, nparams) Widget w; XEvent *event; String *params; Cardinal *nparams; { XbaeMatrixWidget mw; /* * Get Matrix widget and make sure it is a Matrix subclass. * w should be the textField widget. */ if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)XtParent(w); else { XtAppWarningMsg( XtWidgetToApplicationContext(w), "traversePrevACT", "badWidget", "XbaeMatrix", "XbaeMatrix: Bad widget passed to TraversePrev action", NULL, 0); return; } /* * Set the traversing direction flag. XmProcessTraversal may traverse * to the Clip widget. If it does, then we will see this flag in * the Clip focusCallback, TraverseInCB, and we will continue to traverse * on out of the mw. yuck! */ mw->matrix.traversing = (int)XmTRAVERSE_PREV_TAB_GROUP; (void)XmProcessTraversal(TextChild(mw), XmTRAVERSE_PREV_TAB_GROUP); mw->matrix.traversing = NOT_TRAVERSING; } static void callSelectCellAction(mw, event) XbaeMatrixWidget mw; XEvent *event; { XbaeMatrixSelectCellCallbackStruct call_data; Boolean old_scroll_select = mw->matrix.scroll_select; mw->matrix.scroll_select = False; call_data.reason = XbaeSelectCellReason; call_data.event = event; call_data.row = last_row; call_data.column = last_column; call_data.selected_cells = mw->matrix.selected_cells; call_data.cells = mw->matrix.cells; call_data.num_params = 1; call_data.params = (char **)XtMalloc(sizeof(char *)); call_data.params[0] = "extend"; XtCallCallbackList( (Widget)mw, mw->matrix.select_cell_callback, (XtPointer)&call_data); (void)XtFree((char *)call_data.params); mw->matrix.scroll_select = old_scroll_select; } /*ARGSUSED*/ static void checkScrollValues(w, data, event, cont) Widget w; XtPointer data; XEvent *event; Boolean *cont; { XbaeMatrixScrollStruct *ss = (XbaeMatrixScrollStruct *)data; XMotionEvent *motionEvent; int x, y; CellType cell; Boolean inMatrix; int distance = 0; int halfRows; int denom = 1; int row, column; int i; ss->event = event; if (event->type == ButtonRelease) { XtRemoveTimeOut(ss->timerID); ss->grabbed = False; if (ss->mw->matrix.selection_policy == XmMULTIPLE_SELECT || ss->mw->matrix.selection_policy == XmEXTENDED_SELECT) callSelectCellAction(ss->mw, ss->event); return; } if (!xbaeEventToXY(ss->mw, event, &x, &y, &cell)) return; motionEvent = (XMotionEvent *)event; /* * In this instance, we don't care if a valid row and column are * returned as we'll be the judge of the result */ inMatrix = xbaeXYToRowCol(ss->mw, &x, &y, &row, &column, cell); /* * Reset the flags, so the matrix stops scrolling when the * pointer is moved back into the fixed columns/rows after a drag * select in the fixed columns/rows which caused the matrix to * scroll vertically/horizontally. */ ss->below = False; ss->above = False; ss->left = False; ss->right = False; if (inMatrix && cell == NonFixedCell) { ss->inClip = True; return; } else { /* * Calculate our position relative to the clip and adjust. */ if (motionEvent->y >= (int)(ss->cw->core.y + ss->cw->core.height)) { /* Below the matrix */ distance = motionEvent->y - ss->cw->core.y - ss->cw->core.height; ss->below = True; ss->above = False; /* * If we are below the matrix, the current column may have * still changed from horizontal motion. */ i = 0; while (COLUMN_POSITION(ss->mw, i) < HORIZ_ORIGIN(ss->mw) + motionEvent->x) i++; if (i <= ss->mw->matrix.columns && i > 0) last_column = i - 1; } else if (motionEvent->y <= ss->cw->core.y) { /* * Above the matrix - can't be both above and below at the same * time unless we have two mouses! */ distance = ss->cw->core.y - motionEvent->y; ss->below = False; ss->above = True; i = 0; while (COLUMN_POSITION(ss->mw, i) < HORIZ_ORIGIN(ss->mw) + motionEvent->x) i++; if (i > 0 && i <= ss->mw->matrix.columns) last_column = i - 1; } if (motionEvent->x <= ss->cw->core.x) { /* To the left */ ss->left = True; ss->right = False; distance = Min(distance, ss->cw->core.x - motionEvent->x); /* * Check for any vertical motion */ if (!ss->below && !ss->above) { last_row = YtoRow(ss->mw, motionEvent->y - COLUMN_LABEL_HEIGHT(ss->mw)) + VERT_ORIGIN(ss->mw); SANITY_CHECK_ROW(ss->mw, last_row); } } else if (motionEvent->x >= (int)(ss->cw->core.x + ss->cw->core.width)) { /* To the right */ ss->left = False; ss->right = True; distance = Min(distance, (int)(motionEvent->x - ss->cw->core.x - ss->cw->core.width)); if (!ss->below && !ss->above) { last_row = YtoRow(ss->mw, motionEvent->y - COLUMN_LABEL_HEIGHT(ss->mw)) + VERT_ORIGIN(ss->mw); SANITY_CHECK_ROW(ss->mw, last_row); } } /* * Adjust the value of the update interval based on the distance we * are away from the matrix */ halfRows = distance / (ROW_HEIGHT(ss->mw) / 2); /* * Avoid use of the math library by doing a simple calculation */ for (i = 0; i < halfRows; i++) denom *= 2; ss->interval = DEFAULT_SCROLL_SPEED / (denom > 0 ? denom : 1); if (ss->interval <= 0) /* Just to be on the safe side */ ss->interval = 1; } } static void updateScroll(data) XtPointer data; { XbaeMatrixScrollStruct *ss = (XbaeMatrixScrollStruct *)data; Boolean callCallback = False; static int my_last_row = -1, my_last_column = -1; if (!scrolling) return; if (my_last_column != last_column || my_last_row != last_row) callCallback = True; my_last_row = last_row; my_last_column = last_column; /* * Off the clip widget - check there are cells that could * be scrolled into a visible position. If there are, * scroll them into view. If not, start setting the fixed * rows and columns as the current row and column. */ if (ss->below && last_row < TRAILING_VERT_ORIGIN(ss->mw) - 1) { xbaeMakeRowVisible(ss->mw, ++last_row); callCallback = True; } else if (ss->above && last_row > (int)ss->mw->matrix.fixed_rows) { xbaeMakeRowVisible(ss->mw, --last_row); callCallback = True; } if (ss->right && last_column < TRAILING_HORIZ_ORIGIN(ss->mw) - 1) { xbaeMakeColumnVisible(ss->mw, ++last_column); callCallback = True; } else if (ss->left && last_column > (int)ss->mw->matrix.fixed_columns) { xbaeMakeColumnVisible(ss->mw, --last_column); callCallback = True; } if (callCallback && (ss->mw->matrix.selection_policy == XmMULTIPLE_SELECT || ss->mw->matrix.selection_policy == XmEXTENDED_SELECT)) callSelectCellAction(ss->mw, ss->event); /* * Flush the updates out to the server so we don't end up lagging * behind too far and end up with a million redraw requests. * Particularly for higher update speeds */ XFlush(XtDisplay((Widget)ss->mw)); ss->timerID = XtAppAddTimeOut( ss->app_context, ss->interval, (XtTimerCallbackProc)updateScroll, (XtPointer)ss); } /* ARGSUSED */ void xbaeHandleMotionACT(w, event, params, nparams) Widget w; XEvent *event; String *params; Cardinal *nparams; { XbaeMatrixWidget mw; XbaeClipWidget cw; XMotionEvent *motionEvent; XButtonEvent *buttonEvent; int x, y, row, column; CellType cell; Boolean inMatrix; if (scrolling) return; /* * Get Matrix widget and make sure it is a Matrix subclass. * w could be Matrix, or the Clip or textField children of Matrix */ if (XtIsSubclass(w, xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)w; else if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)XtParent(w); else { XtAppWarningMsg( XtWidgetToApplicationContext(w), "handleMotionACT", "badWidget", "XbaeMatrix", "XbaeMatrix: Bad widget passed to HandleMotion action", NULL, 0); return; } motionEvent = (XMotionEvent *)event; buttonEvent = (XButtonEvent *)event; cw = (XbaeClipWidget)ClipChild(mw); if (!xbaeEventToXY(mw, event, &x, &y, &cell)) return; /* * In this instance, we don't care if a valid row and column are * returned as we'll be the judge of the result */ inMatrix = xbaeXYToRowCol(mw, &x, &y, &row, &column, cell); if (inMatrix && cell == NonFixedCell) { /* * If we are in a NonFixedCell, then we're cruisin'. Just * update our position */ if (row != last_row || column != last_column) { if (row < mw->matrix.rows && column < mw->matrix.columns) { last_row = row; last_column = column; if (mw->matrix.selection_policy == XmMULTIPLE_SELECT || mw->matrix.selection_policy == XmEXTENDED_SELECT) callSelectCellAction(mw, event); } } } else { XbaeMatrixScrollStruct scrollData; Boolean cont; /* * Grab the pointer and add a timeout routine to start modifying * the current row and/or column in the matrix. Also add an * event handler to monitor the current distance outside the * matrix so we can adjust the timeout routine to go faster when * the pointer is further away from the matrix. */ scrolling = True; XtAddGrab(w, True, False); scrollData.mw = mw; scrollData.cw = cw; scrollData.event = event; scrollData.interval = DEFAULT_SCROLL_SPEED; scrollData.inClip = False; scrollData.grabbed = True; scrollData.app_context = XtWidgetToApplicationContext(w); scrollData.above = scrollData.below = False; scrollData.left = scrollData.right = False; XtAddEventHandler(w, PointerMotionMask | ButtonReleaseMask, True, (XtEventHandler)checkScrollValues, (XtPointer)&scrollData); /* * Call checkScrollValues() to find out where exactly we are in * relation to the clip widget */ checkScrollValues(w, (XtPointer)&scrollData, event, &cont); /* * The above / below / left / right members of the scrollData struct * should now be set so we know where we should be moving. Let's * get on with it, eh? */ updateScroll((XtPointer)&scrollData); while (scrollData.grabbed && !scrollData.inClip) XtAppProcessEvent(scrollData.app_context, XtIMAll); XtRemoveEventHandler(w, PointerMotionMask | ButtonReleaseMask, True, (XtEventHandler)checkScrollValues, (XtPointer)&scrollData); XtRemoveGrab(w); /* * We don't want the timeout getting called again as, in two lines, * we'll be way out of scope! */ XtRemoveTimeOut(scrollData.timerID); scrolling = False; } } /* ARGSUSED */ void xbaePageDownACT(w, event, params, nparams) Widget w; XEvent *event; String *params; Cardinal *nparams; { XbaeMatrixWidget mw; char *down = "0"; int top; /* * Get Matrix widget and make sure it is a Matrix subclass. * w should be the textField widget. */ if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)XtParent(w); else { XtAppWarningMsg( XtWidgetToApplicationContext(w), "pageDownACT", "badWidget", "XbaeMatrix", "XbaeMatrix: Bad widget passed to PageDown action", NULL, 0); return; } if (!XtIsManaged(VertScrollChild(mw))) return; /* * Save the top row - if scrolling occurs, the text widget needs * to be moved */ top = VERT_ORIGIN(mw); XtCallActionProc(VertScrollChild(mw), "PageDownOrRight", event, &down, 1); if (VERT_ORIGIN(mw) != top) /* * Position the cursor at the top most non fixed row if there was * a page down */ XbaeMatrixEditCell((Widget)mw, VERT_ORIGIN(mw) + mw->matrix.fixed_rows, mw->matrix.current_column); } /* ARGSUSED */ void xbaePageUpACT(w, event, params, nparams) Widget w; XEvent *event; String *params; Cardinal *nparams; { XbaeMatrixWidget mw; char *up = "0"; int top; /* * Get Matrix widget and make sure it is a Matrix subclass. * w should be the textField widget. */ if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass)) mw = (XbaeMatrixWidget)XtParent(w); else { XtAppWarningMsg( XtWidgetToApplicationContext(w), "pageUpACT", "badWidget", "XbaeMatrix", "XbaeMatrix: Bad widget passed to PageUp action", NULL, 0); return; } if (!XtIsManaged(VertScrollChild(mw))) return; /* * Save the top row - if scrolling occurs, the text widget needs * to be moved */ top = VERT_ORIGIN(mw); XtCallActionProc(VertScrollChild(mw), "PageUpOrLeft", event, &up, 1); if (VERT_ORIGIN(mw) != top) /* * Position the cursor at the top most non fixed row if there was * a page down */ XbaeMatrixEditCell((Widget)mw, VERT_ORIGIN(mw) + mw->matrix.fixed_rows, mw->matrix.current_column); }