~ubuntu-branches/ubuntu/karmic/grace/karmic

« back to all changes in this revision

Viewing changes to Xbae/Xbae/Actions.c

  • Committer: Bazaar Package Importer
  • Author(s): Torsten Werner
  • Date: 2002-03-19 14:19:58 UTC
  • Revision ID: james.westby@ubuntu.com-20020319141958-5gxna6vo1ek3zjml
Tags: upstream-5.1.7
ImportĀ upstreamĀ versionĀ 5.1.7

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright(c) 1992 Bell Communications Research, Inc. (Bellcore)
 
3
 * Copyright(c) 1995-99 Andrew Lister
 
4
 *
 
5
 *                        All rights reserved
 
6
 * Permission to use, copy, modify and distribute this material for
 
7
 * any purpose and without fee is hereby granted, provided that the
 
8
 * above copyright notice and this permission notice appear in all
 
9
 * copies, and that the name of Bellcore not be used in advertising
 
10
 * or publicity pertaining to this material without the specific,
 
11
 * prior written permission of an authorized representative of
 
12
 * Bellcore.
 
13
 *
 
14
 * BELLCORE MAKES NO REPRESENTATIONS AND EXTENDS NO WARRANTIES, EX-
 
15
 * PRESS OR IMPLIED, WITH RESPECT TO THE SOFTWARE, INCLUDING, BUT
 
16
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 
17
 * FITNESS FOR ANY PARTICULAR PURPOSE, AND THE WARRANTY AGAINST IN-
 
18
 * FRINGEMENT OF PATENTS OR OTHER INTELLECTUAL PROPERTY RIGHTS.  THE
 
19
 * SOFTWARE IS PROVIDED "AS IS", AND IN NO EVENT SHALL BELLCORE OR
 
20
 * ANY OF ITS AFFILIATES BE LIABLE FOR ANY DAMAGES, INCLUDING ANY
 
21
 * LOST PROFITS OR OTHER INCIDENTAL OR CONSEQUENTIAL DAMAGES RELAT-
 
22
 * ING TO THE SOFTWARE.
 
23
 *
 
24
 * $Id: Actions.c,v 1.1 1999/09/11 01:25:36 fnevgeny Exp $
 
25
 */
 
26
 
 
27
/*
 
28
 * Actions.c created by Andrew Lister (7 August, 1995)
 
29
 */
 
30
 
 
31
#ifdef HAVE_CONFIG_H
 
32
#include <config.h>
 
33
#endif
 
34
 
 
35
#include <Xm/Xm.h>
 
36
#include <Xm/XmP.h>
 
37
#if XmVersion > 1001
 
38
#include <Xm/DrawP.h>
 
39
#endif
 
40
#include <Xm/ScrollBar.h>
 
41
#include <Xbae/MatrixP.h>
 
42
#include <Xbae/Clip.h>
 
43
#include <Xbae/Draw.h>
 
44
#include <Xbae/Actions.h>
 
45
#include <Xbae/Utils.h>
 
46
#include <Xbae/ScrollMgr.h>
 
47
#include <Xbae/ClipP.h>
 
48
#include <X11/cursorfont.h>
 
49
 
 
50
#ifndef XlibSpecificationRelease
 
51
#define XrmPermStringToQuark XrmStringToQuark
 
52
#endif
 
53
 
 
54
#if !defined(DRAW_RESIZE_LINE) && !defined(DRAW_RESIZE_SHADOW)
 
55
/* One of DRAW_RESIZE_LINE and DRAW_RESIZE_SHADOW must be defined. */
 
56
#define DRAW_RESIZE_SHADOW
 
57
#endif
 
58
 
 
59
#ifndef DEFAULT_SCROLL_SPEED
 
60
#define DEFAULT_SCROLL_SPEED 500
 
61
#endif
 
62
 
 
63
typedef struct {
 
64
    XbaeMatrixWidget mw;
 
65
    GC gc;
 
66
    int row;
 
67
    int column;
 
68
    int startx;
 
69
    int lastx;
 
70
    int currentx;
 
71
    int y, height;
 
72
    short *columnWidths;
 
73
    Boolean grabbed;
 
74
    Boolean haveVSB;
 
75
} XbaeMatrixResizeColumnStruct;
 
76
 
 
77
typedef struct {
 
78
    XbaeMatrixWidget mw;
 
79
    int row;
 
80
    int column;
 
81
    Boolean pressed;
 
82
    Boolean grabbed;
 
83
} XbaeMatrixButtonPressedStruct;
 
84
 
 
85
typedef struct {
 
86
    XbaeMatrixWidget mw;
 
87
    XbaeClipWidget cw;
 
88
    XEvent *event;
 
89
    XtIntervalId timerID;
 
90
    XtAppContext app_context;
 
91
    unsigned long interval;
 
92
    Boolean inClip;
 
93
    Boolean grabbed;
 
94
    Boolean above;
 
95
    Boolean below;
 
96
    Boolean left;
 
97
    Boolean right;
 
98
} XbaeMatrixScrollStruct;
 
99
 
 
100
static int DoubleClick P((XbaeMatrixWidget, XEvent *, int, int));
 
101
static void DrawSlideColumn P((XbaeMatrixWidget, int));
 
102
static void SlideColumn P((Widget, XtPointer, XEvent *, Boolean *));
 
103
static void PushButton P((Widget, XtPointer, XEvent *, Boolean *));
 
104
static void updateScroll P((XtPointer));
 
105
static void checkScrollValues P((Widget, XtPointer, XEvent *, Boolean *));
 
106
static void callSelectCellAction P((XbaeMatrixWidget, XEvent *));
 
107
 
 
108
static int last_row = 0;
 
109
static int last_column = 0;
 
110
 
 
111
static int last_selected_row = 0;
 
112
static int last_selected_column = 0;
 
113
 
 
114
static Boolean scrolling = False;
 
115
 
 
116
/* ARGSUSED */
 
117
void
 
118
xbaeDefaultActionACT(w, event, params, nparams)
 
119
Widget w;
 
120
XEvent *event;
 
121
String *params;
 
122
Cardinal *nparams;
 
123
{
 
124
    XbaeMatrixWidget mw;
 
125
    int x, y;
 
126
    int row, column;
 
127
    CellType cell;
 
128
 
 
129
    /*
 
130
     * Get Matrix widget and make sure it is a Matrix subclass.
 
131
     * w could be Matrix, or the Clip or textField children of Matrix
 
132
     */
 
133
    if (XtIsSubclass(w, xbaeMatrixWidgetClass))
 
134
        mw = (XbaeMatrixWidget)w;
 
135
    else if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass))
 
136
        mw = (XbaeMatrixWidget)XtParent(w);
 
137
    else {
 
138
        XtAppWarningMsg(
 
139
            XtWidgetToApplicationContext(w),
 
140
            "defaultActionACT", "badWidget", "XbaeMatrix",
 
141
            "XbaeMatrix: Bad widget passed to DefaultAction action",
 
142
            NULL, 0);
 
143
        return;
 
144
    }
 
145
 
 
146
    if (!mw->matrix.default_action_callback)
 
147
        return;
 
148
 
 
149
    if (!xbaeEventToXY(mw, event, &x, &y, &cell))
 
150
        return;
 
151
 
 
152
    if (!xbaeXYToRowCol(mw, &x, &y, &row, &column, cell))
 
153
        return;
 
154
 
 
155
    if (DoubleClick(mw, event, row, column))
 
156
    {
 
157
        XbaeMatrixDefaultActionCallbackStruct call_data;
 
158
 
 
159
        call_data.reason = XbaeDefaultActionReason;
 
160
        call_data.event = event;
 
161
        call_data.row = row;
 
162
        call_data.column = column;
 
163
 
 
164
        XtCallCallbackList((Widget)mw, mw->matrix.default_action_callback,
 
165
                           (XtPointer)&call_data);
 
166
      
 
167
    }
 
168
}
 
169
 
 
170
static void
 
171
DrawSlideColumn(mw, x)
 
172
XbaeMatrixWidget mw;
 
173
int x;
 
174
{
 
175
#ifdef DRAW_RESIZE_SHADOW
 
176
    /* These values derived through that age-old process
 
177
     * of what looks good to me */
 
178
#define SHADOW_WIDTH 2
 
179
#define RESIZE_COLUMN_LINE_WIDTH 4
 
180
    Dimension width = RESIZE_COLUMN_LINE_WIDTH;
 
181
    Dimension shadow_width = SHADOW_WIDTH;
 
182
#endif
 
183
    Dimension height;
 
184
    Window win;
 
185
    Display *display = XtDisplay(mw);
 
186
    int column = xbaeXtoCol(mw, x - COLUMN_LABEL_OFFSET(mw));
 
187
    int top, bottom;
 
188
    int adjusted_x;
 
189
    int y;
 
190
#ifdef DRAW_RESIZE_LINE
 
191
    GC gc = mw->matrix.draw_gc;
 
192
#endif
 
193
    Boolean need_vert_dead_space_fill = NEED_VERT_DEAD_SPACE_FILL(mw);
 
194
    unsigned int clip_reason;
 
195
 
 
196
    /*
 
197
     * If the column being resized is a fixed one then we don't need to
 
198
     * bother with the clip region
 
199
     */
 
200
    if (column < (int)mw->matrix.fixed_columns)
 
201
    {
 
202
        y = ROW_LABEL_OFFSET(mw);
 
203
        height = VISIBLE_HEIGHT(mw) + FIXED_ROW_HEIGHT(mw) +
 
204
            TRAILING_FIXED_ROW_HEIGHT(mw);
 
205
        win = XtWindow (mw);
 
206
 
 
207
        if (need_vert_dead_space_fill)
 
208
            height += VERT_DEAD_SPACE_HEIGHT(mw);
 
209
 
 
210
#ifdef DRAW_RESIZE_LINE
 
211
        XDrawLine(display, win, gc, x, y, x, y + height);
 
212
        if (XtIsManaged(LeftClip(mw)))
 
213
            XDrawLine(display, XtWindow(LeftClip(mw)), gc,
 
214
                      x - COLUMN_LABEL_OFFSET(mw), 0,
 
215
                      x - COLUMN_LABEL_OFFSET(mw),
 
216
                      LeftClip(mw)->core.height);
 
217
#endif
 
218
#ifdef DRAW_RESIZE_SHADOW
 
219
        DRAW_SHADOW(display, win,
 
220
                    mw->matrix.resize_top_shadow_gc,
 
221
                    mw->matrix.resize_bottom_shadow_gc,
 
222
                    shadow_width, x, y, width, height, XmSHADOW_OUT);
 
223
        if (XtIsManaged(LeftClip(mw)))
 
224
            DRAW_SHADOW(display, XtWindow(LeftClip(mw)),
 
225
                        mw->matrix.resize_top_shadow_gc,
 
226
                        mw->matrix.resize_bottom_shadow_gc,
 
227
                        shadow_width, x - COLUMN_LABEL_OFFSET(mw),
 
228
                        0, width, LeftClip(mw)->core.height, XmSHADOW_OUT);
 
229
#endif
 
230
        return;
 
231
    }
 
232
 
 
233
    /*
 
234
     * Similarly for trailingFixedColumns - beware going off the clip child
 
235
     * here also
 
236
     */
 
237
    if (column >= TRAILING_HORIZ_ORIGIN(mw) ||
 
238
        x >= (int)(ClipChild(mw)->core.x + ClipChild(mw)->core.width))
 
239
    {
 
240
        y = ROW_LABEL_OFFSET(mw);
 
241
        height = VISIBLE_HEIGHT(mw) + FIXED_ROW_HEIGHT(mw) +
 
242
            TRAILING_FIXED_ROW_HEIGHT(mw);
 
243
        win = XtWindow(mw);
 
244
 
 
245
        if (need_vert_dead_space_fill)
 
246
            height += VERT_DEAD_SPACE_HEIGHT(mw);
 
247
 
 
248
#ifdef DRAW_RESIZE_LINE
 
249
        XDrawLine(display, win, gc, x, y, x, y + height);
 
250
        if (XtIsManaged(RightClip(mw)))
 
251
            XDrawLine(display, XtWindow(RightClip(mw)),
 
252
                      gc, x - TRAILING_FIXED_COLUMN_LABEL_OFFSET(mw), 0,
 
253
                      x - TRAILING_FIXED_COLUMN_LABEL_OFFSET(mw),
 
254
                      RightClip(mw)->core.height);
 
255
        
 
256
#endif
 
257
#ifdef DRAW_RESIZE_SHADOW
 
258
        DRAW_SHADOW(display, win,
 
259
                    mw->matrix.resize_top_shadow_gc,
 
260
                    mw->matrix.resize_bottom_shadow_gc,
 
261
                    shadow_width, x, y, width, height, XmSHADOW_OUT);
 
262
        if (XtIsManaged(RightClip(mw)))
 
263
            DRAW_SHADOW(display, XtWindow(RightClip(mw)),
 
264
                        mw->matrix.resize_top_shadow_gc,
 
265
                        mw->matrix.resize_bottom_shadow_gc,
 
266
                        shadow_width,
 
267
                        x - TRAILING_FIXED_COLUMN_LABEL_OFFSET(mw), 0,
 
268
                        width, RightClip(mw)->core.height, XmSHADOW_OUT);
 
269
#endif
 
270
        return;
 
271
    }
 
272
    
 
273
    xbaeGetVisibleRows(mw, &top, &bottom);
 
274
    /*
 
275
     * we need all non-fixed rows, so add 1 to bottom
 
276
     * to include the last one as the return values
 
277
     * are inclusive
 
278
     */
 
279
    bottom += 1;
 
280
 
 
281
    /*
 
282
     * The area between top and bottom rows are the non fixed rows.  They
 
283
     * fall on the ClipChild
 
284
     */
 
285
    y = -mw->matrix.cell_shadow_thickness; /* relative to clip */
 
286
 
 
287
    height = ROW_HEIGHT(mw) * (bottom - top) +
 
288
        2 * mw->matrix.cell_shadow_thickness;
 
289
 
 
290
    /*
 
291
     * If we are on the clip, the x location is offset by the
 
292
     * fixed column width, label offset and label width
 
293
     */
 
294
    adjusted_x = x - FIXED_COLUMN_LABEL_OFFSET(mw);
 
295
 
 
296
    win = XtWindow(ClipChild(mw));
 
297
 
 
298
#ifdef DRAW_RESIZE_LINE
 
299
    XDrawLine(display, win, gc, adjusted_x, y, adjusted_x, y + height);
 
300
#endif
 
301
#ifdef DRAW_RESIZE_SHADOW
 
302
    DRAW_SHADOW(display, win,
 
303
                mw->matrix.resize_top_shadow_gc,
 
304
                mw->matrix.resize_bottom_shadow_gc,
 
305
                shadow_width, adjusted_x, y, width, height, XmSHADOW_OUT);
 
306
#endif
 
307
    /*
 
308
     * Now draw the line (or shadow) on the non clipped region - that is
 
309
     * the fixed and trailingFixed rows.  First, do the leading rows.
 
310
     */
 
311
    if (mw->matrix.fixed_rows)
 
312
    {
 
313
        y = ROW_LABEL_OFFSET(mw);
 
314
        height = FIXED_ROW_HEIGHT(mw) + 2 * mw->matrix.cell_shadow_thickness;
 
315
        win = XtWindow(mw);
 
316
        xbaeSetClipMask(mw, CLIP_FIXED_ROWS);
 
317
 
 
318
#ifdef DRAW_RESIZE_LINE
 
319
        if (XtIsManaged(TopClip(mw)))
 
320
            XDrawLine(display, XtWindow(TopClip(mw)), gc, adjusted_x,
 
321
                      -mw->matrix.cell_shadow_thickness, adjusted_x, height);
 
322
#endif
 
323
#ifdef DRAW_RESIZE_SHADOW
 
324
        if (XtIsManaged(TopClip(mw)))
 
325
            DRAW_SHADOW(display, XtWindow(TopClip(mw)),
 
326
                        mw->matrix.resize_top_shadow_gc,
 
327
                        mw->matrix.resize_bottom_shadow_gc,
 
328
                        shadow_width, adjusted_x,
 
329
                        -mw->matrix.cell_shadow_thickness,
 
330
                        width, height, XmSHADOW_OUT);
 
331
#endif
 
332
        xbaeSetClipMask(mw, CLIP_NONE);                  
 
333
    }
 
334
    
 
335
    /*
 
336
     * The trailingFixedRows
 
337
     */
 
338
    if (mw->matrix.trailing_fixed_rows)
 
339
    {
 
340
        y = TRAILING_FIXED_ROW_LABEL_OFFSET(mw);
 
341
        height = TRAILING_FIXED_ROW_HEIGHT(mw) +
 
342
            2 * mw->matrix.cell_shadow_thickness;
 
343
 
 
344
        clip_reason = CLIP_TRAILING_FIXED_ROWS;
 
345
        if (IS_LEADING_FIXED_COLUMN(mw, column))
 
346
            clip_reason |= CLIP_FIXED_COLUMNS;
 
347
        else if (IS_TRAILING_FIXED_COLUMN(mw, column))
 
348
            clip_reason |= CLIP_TRAILING_FIXED_COLUMNS;
 
349
        
 
350
        xbaeSetClipMask(mw, clip_reason);
 
351
 
 
352
#ifdef DRAW_RESIZE_LINE
 
353
        if (XtIsManaged(BottomClip(mw)))
 
354
            XDrawLine(display, XtWindow(BottomClip(mw)), gc, adjusted_x,
 
355
                      -mw->matrix.cell_shadow_thickness, adjusted_x, height);
 
356
#endif
 
357
#ifdef DRAW_RESIZE_SHADOW
 
358
        if (XtIsManaged(BottomClip(mw)))
 
359
            DRAW_SHADOW(display, XtWindow(BottomClip(mw)),
 
360
                        mw->matrix.resize_top_shadow_gc,
 
361
                        mw->matrix.resize_bottom_shadow_gc,
 
362
                        shadow_width, adjusted_x,
 
363
                        -mw->matrix.cell_shadow_thickness,
 
364
                        width, height, XmSHADOW_OUT);
 
365
#endif
 
366
        xbaeSetClipMask(mw, CLIP_NONE);
 
367
    }
 
368
 
 
369
    if ((NEED_VERT_FILL(mw) && (! HAS_ATTACHED_TRAILING_ROWS(mw))) ||
 
370
        need_vert_dead_space_fill)
 
371
    {
 
372
        if (need_vert_dead_space_fill)
 
373
        {
 
374
            y = UNATTACHED_TRAILING_ROWS_OFFSET(mw) -
 
375
                mw->matrix.cell_shadow_thickness;
 
376
            height = 2 * mw->matrix.cell_shadow_thickness +
 
377
                VERT_DEAD_SPACE_HEIGHT(mw);
 
378
        }
 
379
        else
 
380
        {
 
381
            y = TRAILING_FIXED_ROW_LABEL_OFFSET(mw) +
 
382
                TRAILING_FIXED_ROW_HEIGHT(mw);
 
383
            height = FILL_VERT_HEIGHT(mw) - HORIZ_SB_SPACE(mw);
 
384
        }
 
385
 
 
386
#ifdef DRAW_RESIZE_LINE
 
387
        XDrawLine(display, XtWindow(mw), gc,
 
388
                  adjusted_x, y, adjusted_x, height);
 
389
#endif
 
390
#ifdef DRAW_RESIZE_SHADOW
 
391
        DRAW_SHADOW(display, XtWindow(mw),
 
392
                    mw->matrix.resize_top_shadow_gc,
 
393
                    mw->matrix.resize_bottom_shadow_gc,
 
394
                    shadow_width, x, y,
 
395
                    width, height, XmSHADOW_OUT);
 
396
#endif
 
397
    }
 
398
}
 
399
 
 
400
 
 
401
static void
 
402
SlideColumn(w, data, event, cont)
 
403
Widget w;
 
404
XtPointer data;
 
405
XEvent *event;
 
406
Boolean *cont;
 
407
{
 
408
    XbaeMatrixResizeColumnStruct *rd = (XbaeMatrixResizeColumnStruct *)data;
 
409
    XMotionEvent *motionEvent;
 
410
    Boolean relayout = False;
 
411
    int numCharacters;
 
412
    int i;
 
413
    
 
414
    if (event->type == ButtonRelease)
 
415
    {
 
416
        DrawSlideColumn(rd->mw, rd->lastx);
 
417
        XUngrabPointer(XtDisplay(w), CurrentTime);
 
418
        rd->grabbed = False;
 
419
        /*
 
420
         * Remanage the VSB if we unmapped it earlier
 
421
         */
 
422
        if (rd->haveVSB)
 
423
            XtManageChild(VertScrollChild(rd->mw));
 
424
 
 
425
        if (rd->mw->matrix.resize_column_callback)
 
426
        {
 
427
            XbaeMatrixResizeColumnCallbackStruct call_data;
 
428
 
 
429
            call_data.reason = XbaeResizeColumnReason;
 
430
            call_data.event = event;
 
431
            call_data.row = rd->row;
 
432
            call_data.column = rd->column - 1;
 
433
            call_data.which = rd->column - 1;
 
434
            call_data.columns = rd->mw->matrix.columns;
 
435
            call_data.column_widths  = rd->columnWidths;
 
436
            XtCallCallbackList ((Widget)rd->mw,
 
437
                                rd->mw->matrix.resize_column_callback,
 
438
                                (XtPointer)&call_data);     
 
439
        }
 
440
 
 
441
        for (i = 0; i < rd->mw->matrix.columns; i++)
 
442
            if (rd->columnWidths[i] != rd->mw->matrix.column_widths[i])
 
443
            {
 
444
                /* Make sure everything is handled correctly with SetValues */
 
445
                XtVaSetValues((Widget)rd->mw, XmNcolumnWidths,
 
446
                               rd->columnWidths, NULL);
 
447
                break;
 
448
            }
 
449
        /*
 
450
         * If maxColumnLengths are set and we have resized the column to
 
451
         * larger, reset the corresponding maxColumnLength
 
452
         */
 
453
        if (rd->mw->matrix.column_max_lengths &&
 
454
            rd->columnWidths[rd->column - 1] >
 
455
            rd->mw->matrix.column_max_lengths[rd->column - 1])
 
456
            rd->mw->matrix.column_max_lengths[rd->column - 1] =
 
457
                rd->columnWidths[rd->column - 1];
 
458
        XtFree((char *)rd->columnWidths);
 
459
        return;
 
460
    }
 
461
 
 
462
    if (event->type != MotionNotify) /* Double check! */
 
463
        return;
 
464
 
 
465
    motionEvent = (XMotionEvent *)event;
 
466
 
 
467
    if (rd->currentx - motionEvent->x > FONT_WIDTH(rd->mw))
 
468
    {
 
469
        /* If we're only one character wide, we cannae get any smaller */
 
470
        if (rd->columnWidths[rd->column - 1] == BAD_WIDTH + 1)
 
471
            return;     
 
472
        /*
 
473
         * Moved left a full character - update the column widths and force
 
474
         * a redisplay
 
475
         */     
 
476
        numCharacters = (rd->currentx - motionEvent->x) /
 
477
            FONT_WIDTH(rd->mw);
 
478
        if (numCharacters >= rd->columnWidths[rd->column - 1])
 
479
            /* Must keep a column at least one character wide */
 
480
            numCharacters = rd->columnWidths[rd->column - 1] - 1;
 
481
                
 
482
        rd->columnWidths[rd->column - 1] -= numCharacters;
 
483
        rd->currentx -= numCharacters * FONT_WIDTH(rd->mw);
 
484
        relayout = True;
 
485
    }   
 
486
    
 
487
    if (motionEvent->x - rd->currentx > FONT_WIDTH(rd->mw))
 
488
    {
 
489
        /*
 
490
         * Moved right a full character - update the column widths and force
 
491
         * a redisplay
 
492
         */
 
493
        numCharacters = (motionEvent->x - rd->currentx) /
 
494
            FONT_WIDTH(rd->mw);
 
495
        rd->columnWidths[rd->column - 1] += numCharacters;
 
496
        rd->currentx += numCharacters * FONT_WIDTH(rd->mw);
 
497
        relayout = True;
 
498
    }
 
499
 
 
500
    if (relayout)
 
501
    {
 
502
        /* Draw the marker line in the new location */
 
503
        if (rd->lastx != rd->currentx)
 
504
        {
 
505
            DrawSlideColumn(rd->mw, rd->currentx);
 
506
            DrawSlideColumn(rd->mw, rd->lastx);
 
507
 
 
508
            rd->lastx = rd->currentx;
 
509
        }
 
510
    }
 
511
}
 
512
 
 
513
/* ARGSUSED */
 
514
void
 
515
xbaeResizeColumnsACT(w, event, params, nparams)
 
516
Widget w;
 
517
XEvent *event;
 
518
String *params;
 
519
Cardinal *nparams;
 
520
{
 
521
    XbaeMatrixWidget mw;
 
522
    int x, y;
 
523
    int eventx;
 
524
    int i;
 
525
    int row, column;
 
526
    CellType cell;
 
527
    static Cursor cursor;
 
528
    XbaeMatrixResizeColumnStruct resizeData;
 
529
    XGCValues values;
 
530
    XtAppContext appcontext;
 
531
#ifdef DRAW_RESIZE_LINE
 
532
    XGCValues save;
 
533
#endif
 
534
    unsigned long gcmask, event_mask;
 
535
    Display *display = XtDisplay(w);
 
536
#define FUZZ_FACTOR     3
 
537
    int fuzzy = FUZZ_FACTOR;
 
538
#undef FUZZ_FACTOR
 
539
    
 
540
    /*
 
541
     * Get Matrix widget and make sure it is a Matrix subclass.
 
542
     * w could be Matrix, or the Clip or textField children of Matrix
 
543
     */
 
544
    if (XtIsSubclass(w, xbaeMatrixWidgetClass))
 
545
        mw = (XbaeMatrixWidget)w;
 
546
    else if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass))
 
547
        mw = (XbaeMatrixWidget)XtParent(w);
 
548
    else {
 
549
        XtAppWarningMsg(
 
550
            XtWidgetToApplicationContext(w),
 
551
            "resizeColumnsACT", "badWidget", "XbaeMatrix",
 
552
            "XbaeMatrix: Bad widget passed to ResizeColumns action",
 
553
            NULL, 0);
 
554
        return;
 
555
    }
 
556
    
 
557
    /*
 
558
     * If we won't allow dynamic column resize, leave.
 
559
     */
 
560
    if (!mw->matrix.allow_column_resize)
 
561
        return;
 
562
    
 
563
    if (!xbaeEventToXY(mw, event, &x, &y, &cell))
 
564
        return;
 
565
    
 
566
    eventx = x;
 
567
    
 
568
    if (!xbaeXYToRowCol(mw, &x, &y, &row, &column, cell))
 
569
        return;
 
570
 
 
571
    /*
 
572
     * Calculate if the x and y of the middle button event is on
 
573
     * a column border.  Allow the width of the shadow to be the
 
574
     * allowed delta.  x is modified in xbaeXYToRowCol() to be
 
575
     * the x distance from the cell's border
 
576
     */
 
577
    if ((int)mw->matrix.cell_shadow_thickness > fuzzy)
 
578
        fuzzy = mw->matrix.cell_shadow_thickness;
 
579
    
 
580
    if (x > fuzzy && COLUMN_WIDTH(mw, column) - x > fuzzy)
 
581
        return;
 
582
    
 
583
    /*
 
584
     * Looks like we hit a column border, determine the column that is
 
585
     * intended to be resized
 
586
     */
 
587
    if ((COLUMN_WIDTH(mw, column) - x) <= fuzzy)
 
588
        column++;
 
589
    
 
590
    /* Can't adjust the origin or should you be able to?? */
 
591
    if (column == 0)
 
592
        return;
 
593
    
 
594
    /*
 
595
     * Make it here and it's time to start the fun stuff!
 
596
     */
 
597
    
 
598
    /* Create the left / right cursor */
 
599
    if (!cursor)
 
600
        cursor = XCreateFontCursor(display, XC_sb_h_double_arrow);
 
601
    
 
602
    /* Commit any edit in progress and unmap the text field -
 
603
       it's just bad luck */
 
604
    (*((XbaeMatrixWidgetClass)XtClass(mw))->matrix_class.commit_edit)
 
605
        (mw, event, True);
 
606
 
 
607
    /*
 
608
     * Redraw the cell that had the text field in it or it might stay blank
 
609
     */
 
610
    xbaeDrawCell(mw, mw->matrix.current_row, mw->matrix.current_column);
 
611
 
 
612
    /*
 
613
     * Say goodbye to the Vertical ScrollBar -> it only gets in the way!
 
614
     */
 
615
    if ((resizeData.haveVSB = XtIsManaged(VertScrollChild(mw)) &&
 
616
           ((mw->matrix.scrollbar_placement == XmTOP_RIGHT) ||
 
617
            (mw->matrix.scrollbar_placement == XmBOTTOM_RIGHT))))
 
618
        XtUnmanageChild(VertScrollChild(mw));
 
619
    /*
 
620
     * Flush the commit events out to the server.  Otherwise, our changes
 
621
     * to the GCs below have a bad effect.
 
622
     */
 
623
    XSync(display, False);
 
624
    
 
625
    event_mask = PointerMotionMask | ButtonReleaseMask;
 
626
    XtAddEventHandler(w, event_mask,
 
627
                       True, (XtEventHandler)SlideColumn,
 
628
                       (XtPointer)&resizeData);
 
629
    
 
630
    XGrabPointer(display, XtWindow(w), True, event_mask,
 
631
                  GrabModeAsync, GrabModeAsync, XtWindow((Widget)mw),
 
632
                  cursor, CurrentTime);
 
633
    
 
634
    /* Copy the columnWidth array */
 
635
    resizeData.columnWidths =
 
636
         (short *)XtMalloc(mw->matrix.columns * sizeof(short));
 
637
    for (i = 0; i < mw->matrix.columns; i++)
 
638
        resizeData.columnWidths[i] = mw->matrix.column_widths[i];
 
639
    resizeData.grabbed = True;
 
640
    resizeData.mw = mw;
 
641
    resizeData.column = column;
 
642
    resizeData.startx = resizeData.currentx = resizeData.lastx =
 
643
        event->xbutton.x;
 
644
 
 
645
    gcmask = GCForeground | GCBackground | GCFunction;
 
646
    values.function = GXxor;
 
647
#ifdef DRAW_RESIZE_LINE
 
648
    XGetGCValues(display, mw->matrix.draw_gc, gcmask, &save);
 
649
    values.foreground = values.background = save.background;
 
650
 
 
651
    XChangeGC(display, mw->matrix.draw_gc, gcmask, &values);
 
652
#endif
 
653
    
 
654
    DrawSlideColumn(mw, resizeData.currentx);
 
655
 
 
656
    appcontext = XtWidgetToApplicationContext(w);
 
657
    
 
658
    while (resizeData.grabbed)
 
659
        XtAppProcessEvent(appcontext, XtIMAll);
 
660
    
 
661
    XtRemoveEventHandler(w, event_mask, True,
 
662
                           (XtEventHandler)SlideColumn,
 
663
                           (XtPointer)&resizeData);
 
664
 
 
665
#ifdef DRAW_RESIZE_LINE
 
666
    XSetFunction(display, mw->matrix.draw_gc, GXcopy);
 
667
#endif
 
668
}
 
669
 
 
670
/*
 
671
 * Action to process a drag out
 
672
 */
 
673
/* ARGSUSED */
 
674
void
 
675
xbaeProcessDragACT(w, event, params, nparams)
 
676
Widget w;
 
677
XEvent *event;
 
678
String *params;
 
679
Cardinal *nparams;
 
680
{
 
681
#if XmVersion > 1001
 
682
    XbaeMatrixWidget mw;
 
683
    int x, y;
 
684
    int row, column;
 
685
    CellType cell;
 
686
    XbaeMatrixProcessDragCallbackStruct call_data;
 
687
 
 
688
    /*
 
689
     * Get Matrix widget and make sure it is a Matrix subclass.
 
690
     * w could be Matrix, or the Clip or textField children of Matrix
 
691
     */
 
692
    if (XtIsSubclass(w, xbaeMatrixWidgetClass))
 
693
        mw = (XbaeMatrixWidget)w;
 
694
    else if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass))
 
695
        mw = (XbaeMatrixWidget)XtParent(w);
 
696
    else {
 
697
        XtAppWarningMsg(
 
698
            XtWidgetToApplicationContext(w),
 
699
            "processDragACT", "badWidget", "XbaeMatrix",
 
700
            "XbaeMatrix: Bad widget passed to ProcessDrag action",
 
701
            NULL, 0);
 
702
        return;
 
703
    }
 
704
 
 
705
    if (!mw->matrix.process_drag_callback)
 
706
        return;
 
707
 
 
708
    if (!xbaeEventToXY(mw, event, &x, &y, &cell))
 
709
        return;
 
710
 
 
711
    if (!xbaeXYToRowCol(mw, &x, &y, &row, &column, cell))
 
712
        return;
 
713
 
 
714
    call_data.reason = XbaeProcessDragReason;
 
715
    call_data.event = event;
 
716
    call_data.row = row;
 
717
    call_data.column = column;
 
718
 
 
719
    if (mw->matrix.draw_cell_callback)
 
720
    {
 
721
        Pixel bgcolor, fgcolor;
 
722
        int width, height, depth;
 
723
        
 
724
        call_data.type = xbaeGetDrawCellValue(
 
725
            mw, row, column, &call_data.string, &call_data.pixmap,
 
726
            &call_data.mask, &width, &height, &bgcolor, &fgcolor, &depth);
 
727
    }
 
728
    else
 
729
        call_data.string = mw->matrix.cells ?
 
730
            mw->matrix.cells[row][column] : "";
 
731
    
 
732
    call_data.num_params = *nparams;
 
733
    call_data.params = params;
 
734
 
 
735
    XtCallCallbackList((Widget)mw, mw->matrix.process_drag_callback,
 
736
                       (XtPointer)&call_data);
 
737
#endif
 
738
}
 
739
 
 
740
/*
 
741
 * Action to edit a non-fixed cell.
 
742
 */
 
743
/* ARGSUSED */
 
744
void
 
745
xbaeEditCellACT(w, event, params, nparams)
 
746
Widget w;
 
747
XEvent *event;
 
748
String *params;
 
749
Cardinal *nparams;
 
750
{
 
751
    XbaeMatrixWidget mw;
 
752
    int row, column;
 
753
    XrmQuark q;
 
754
    static XrmQuark QPointer, QLeft, QRight, QUp, QDown;
 
755
    static Boolean haveQuarks = False;
 
756
    /*
 
757
     * Get static quarks for the parms we understand
 
758
     */
 
759
    if (!haveQuarks)
 
760
    {
 
761
        QPointer = XrmPermStringToQuark("Pointer");
 
762
        QLeft = XrmPermStringToQuark("Left");
 
763
        QRight = XrmPermStringToQuark("Right");
 
764
        QUp = XrmPermStringToQuark("Up");
 
765
        QDown = XrmPermStringToQuark("Down");
 
766
        haveQuarks = True;
 
767
    }
 
768
 
 
769
    /*
 
770
     * Get Matrix widget and make sure it is a Matrix subclass.
 
771
     * w could be Matrix, or the Clip or textField children of Matrix
 
772
     */
 
773
    if (XtIsSubclass(w, xbaeMatrixWidgetClass))
 
774
        mw = (XbaeMatrixWidget)w;
 
775
    else if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass))
 
776
        mw = (XbaeMatrixWidget)XtParent(w);
 
777
    else
 
778
    {
 
779
        XtAppWarningMsg(
 
780
            XtWidgetToApplicationContext(w),
 
781
            "editCellACT", "badWidget", "XbaeMatrix",
 
782
            "XbaeMatrix: Bad widget passed to EditCell action",
 
783
            NULL, 0);
 
784
        return;
 
785
    }
 
786
 
 
787
    /*
 
788
     * Make sure we have a single parm
 
789
     */
 
790
    if (*nparams != 1)
 
791
    {
 
792
        XtAppWarningMsg(
 
793
            XtWidgetToApplicationContext(w),
 
794
            "editCellACT", "badParms", "XbaeMatrix",
 
795
            "XbaeMatrix: Wrong params passed to EditCell action, needs 1",
 
796
            NULL, 0);
 
797
        return;
 
798
    }
 
799
 
 
800
    /*
 
801
     * Initialize row/column to the current position
 
802
     */
 
803
    row = mw->matrix.current_row;
 
804
    column = mw->matrix.current_column;
 
805
 
 
806
    /*
 
807
     * Quarkify the string param
 
808
     */
 
809
    q = XrmStringToQuark(params[0]);
 
810
 
 
811
    /*
 
812
     * If we aren't currently editing, then the only kind of traversal that
 
813
     * makes sense is pointer.
 
814
     */
 
815
    if (!XtIsManaged(TextChild(mw)) && q != QPointer)
 
816
        return;
 
817
 
 
818
    if (q == QPointer)
 
819
    {
 
820
        CellType cellType = NonFixedCell;
 
821
        int x, y;
 
822
 
 
823
        /*
 
824
         * Get the x,y point coordinate relative to the Clip window.
 
825
         * Return if this event did not occur in the Clip subwindow
 
826
         * (since we can only edit non-fixed cells).
 
827
         */
 
828
        switch(event->type)
 
829
        {
 
830
        case ButtonPress:
 
831
        case ButtonRelease:
 
832
            x = event->xbutton.x;
 
833
            y = event->xbutton.y;
 
834
            break;
 
835
        case KeyPress:
 
836
        case KeyRelease:
 
837
            x = event->xkey.x;
 
838
            y = event->xkey.y;
 
839
            break;
 
840
        case MotionNotify:
 
841
            x = event->xmotion.x;
 
842
            y = event->xmotion.y;
 
843
            break;
 
844
        default:
 
845
            return;
 
846
        }
 
847
 
 
848
        if (event->xbutton.subwindow == XtWindow(ClipChild(mw)))
 
849
        {
 
850
            x -= FIXED_COLUMN_LABEL_OFFSET(mw);
 
851
            y -= FIXED_ROW_LABEL_OFFSET(mw);
 
852
            cellType = NonFixedCell;
 
853
        }
 
854
        else if (event->xbutton.window != XtWindow(ClipChild(mw)))
 
855
        {
 
856
            if (!mw->matrix.traverse_fixed)
 
857
                return;
 
858
            cellType = FixedCell;
 
859
        }
 
860
 
 
861
        /*
 
862
         * Convert the point to a row,column. If it does not pick a valid
 
863
         * cell, then return.
 
864
         */
 
865
        if (!xbaeXYToRowCol(mw, &x, &y, &row, &column, cellType))
 
866
            return;
 
867
    }
 
868
    else if (q == QRight)
 
869
    {
 
870
        /*
 
871
         * If we are in the lower right corner, stay there.
 
872
         * Otherwise move over a column. If we move off to the right of the
 
873
         * final column to which traversing is allowed then move down a row
 
874
         * and back to the first column to which traversing is allowed.
 
875
         */
 
876
        if (!mw->matrix.traverse_fixed)
 
877
        {
 
878
            /* check scrollable boundary */
 
879
            if (mw->matrix.current_row != TRAILING_VERT_ORIGIN(mw) - 1 ||
 
880
                mw->matrix.current_column != TRAILING_HORIZ_ORIGIN(mw) - 1)
 
881
            {
 
882
                column++;
 
883
                if (IS_TRAILING_FIXED_COLUMN(mw, column))
 
884
                {
 
885
                    column = mw->matrix.fixed_columns;
 
886
                    row++;
 
887
                }
 
888
            }
 
889
        }
 
890
        else
 
891
        {
 
892
            /* check matrix boundary */
 
893
            if (mw->matrix.current_row != mw->matrix.rows - 1 ||
 
894
                mw->matrix.current_column != mw->matrix.columns - 1)
 
895
            {
 
896
                column++;
 
897
                if (column >= mw->matrix.columns)
 
898
                {
 
899
                    column = 0;
 
900
                    row++;
 
901
                }
 
902
            }
 
903
        }
 
904
    }
 
905
    else if (q == QLeft)
 
906
    {
 
907
        /*
 
908
         * If we are in the upper left corner, stay there.
 
909
         * Otherwise move back a column. If we move before the first column
 
910
         * to which traversing is allowed, move up a row and over to the last
 
911
         * column to which traversing is allowed.
 
912
         */
 
913
        if (!mw->matrix.traverse_fixed)
 
914
        {
 
915
            /* check scrollable boundary */
 
916
            if (mw->matrix.current_row != mw->matrix.fixed_rows ||
 
917
                mw->matrix.current_column != mw->matrix.fixed_columns)
 
918
            {
 
919
                column--;
 
920
                if (IS_LEADING_FIXED_COLUMN(mw, column))
 
921
                {
 
922
                    column = TRAILING_HORIZ_ORIGIN(mw) - 1;
 
923
                    row--;
 
924
                }
 
925
            }
 
926
        }
 
927
        else
 
928
        {
 
929
            if (mw->matrix.current_row != 0 || mw->matrix.current_column != 0)
 
930
            {
 
931
                column--;
 
932
                if (column < 0)
 
933
                {
 
934
                    column = mw->matrix.columns - 1;
 
935
                    row--;
 
936
                }
 
937
            }
 
938
        }
 
939
    }
 
940
    else if (q == QDown)
 
941
    {
 
942
        row++;
 
943
 
 
944
        /* adjust row for allowable traversable regions */
 
945
        if (!mw->matrix.traverse_fixed)
 
946
        {
 
947
            if (IS_TRAILING_FIXED_ROW(mw, row))
 
948
                row = mw->matrix.fixed_rows;
 
949
        }
 
950
        else
 
951
        {
 
952
            if (row >= mw->matrix.rows)
 
953
                row = 0;
 
954
        }
 
955
    }
 
956
    else if (q == QUp)
 
957
    {
 
958
        row--;
 
959
 
 
960
        if (!mw->matrix.traverse_fixed)
 
961
        {
 
962
            if (IS_LEADING_FIXED_ROW(mw, row))
 
963
                row = TRAILING_VERT_ORIGIN(mw) - 1;
 
964
        }
 
965
        else
 
966
        {
 
967
            if (row < 0)
 
968
                row = mw->matrix.rows - 1;
 
969
        }
 
970
    }
 
971
 
 
972
    /*
 
973
     * Call the traverseCellCallback to allow the application to
 
974
     * perform custom traversal.
 
975
     */
 
976
    if (mw->matrix.traverse_cell_callback)
 
977
    {
 
978
        XbaeMatrixTraverseCellCallbackStruct call_data;
 
979
 
 
980
        call_data.reason = XbaeTraverseCellReason;
 
981
        call_data.event = event;
 
982
        call_data.row = mw->matrix.current_row;
 
983
        call_data.column = mw->matrix.current_column;
 
984
        call_data.next_row = row;
 
985
        call_data.next_column = column;
 
986
        call_data.fixed_rows = mw->matrix.fixed_rows;
 
987
        call_data.fixed_columns = mw->matrix.fixed_columns;
 
988
        call_data.trailing_fixed_rows = mw->matrix.trailing_fixed_rows;
 
989
        call_data.trailing_fixed_columns = mw->matrix.trailing_fixed_columns;
 
990
        call_data.num_rows = mw->matrix.rows;
 
991
        call_data.num_columns = mw->matrix.columns;
 
992
        call_data.param = params[0];
 
993
        call_data.qparam = q;
 
994
 
 
995
        XtCallCallbackList((Widget)mw, mw->matrix.traverse_cell_callback,
 
996
                           (XtPointer)&call_data);
 
997
 
 
998
        row = call_data.next_row;
 
999
        column = call_data.next_column;
 
1000
    }
 
1001
 
 
1002
    /*
 
1003
     * Attempt to edit the new cell using the edit_cell method.
 
1004
     * If we are editing a cell based on pointer position, we always
 
1005
     * call edit_cell.  Otherwise, we must be editing a new cell to
 
1006
     * call edit_cell.
 
1007
     */
 
1008
    if (q == QPointer || (row != mw->matrix.current_row ||
 
1009
                          column != mw->matrix.current_column))
 
1010
        (*((XbaeMatrixWidgetClass)XtClass(mw))->matrix_class.edit_cell)
 
1011
            (mw, event, row, column, params, *nparams);
 
1012
 
 
1013
    /*
 
1014
     * Traverse to the textField
 
1015
     */
 
1016
    (void)XmProcessTraversal(TextChild(mw), XmTRAVERSE_CURRENT);
 
1017
}
 
1018
 
 
1019
/*
 
1020
 * Action to unmap the textField and discard any edits made
 
1021
 */
 
1022
/* ARGSUSED */
 
1023
void
 
1024
xbaeCancelEditACT(w, event, params, nparams)
 
1025
Widget w;
 
1026
XEvent *event;
 
1027
String *params;
 
1028
Cardinal *nparams;
 
1029
{
 
1030
    XbaeMatrixWidget mw;
 
1031
    Boolean unmap;
 
1032
 
 
1033
    /*
 
1034
     * Get Matrix widget and make sure it is a Matrix subclass.
 
1035
     * w could be Matrix, or the Clip or textField children of Matrix
 
1036
     */
 
1037
    if (XtIsSubclass(w, xbaeMatrixWidgetClass))
 
1038
        mw = (XbaeMatrixWidget)w;
 
1039
    else if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass))
 
1040
        mw = (XbaeMatrixWidget)XtParent(w);
 
1041
    else
 
1042
    {
 
1043
        XtAppWarningMsg(
 
1044
            XtWidgetToApplicationContext(w),
 
1045
            "cancelEditACT", "badWidget", "XbaeMatrix",
 
1046
            "XbaeMatrix: Bad widget passed to CancelEdit action",
 
1047
            NULL, 0);
 
1048
        return;
 
1049
    }
 
1050
 
 
1051
    /*
 
1052
     * Make sure we have a single param
 
1053
     */
 
1054
    if (*nparams != 1)
 
1055
    {
 
1056
        XtAppWarningMsg(
 
1057
            XtWidgetToApplicationContext(w),
 
1058
            "cancelEditACT", "badParms", "XbaeMatrix",
 
1059
            "XbaeMatrix: Wrong params passed to CancelEdit action, needs 1",
 
1060
            NULL, 0);
 
1061
        return;
 
1062
    }
 
1063
 
 
1064
    /*
 
1065
     * Validate our param
 
1066
     */
 
1067
    if (!strcmp(params[0], "True"))
 
1068
        unmap = True;
 
1069
    else if (!strcmp(params[0], "False"))
 
1070
        unmap = False;
 
1071
    else
 
1072
    {
 
1073
        XtAppWarningMsg(
 
1074
            XtWidgetToApplicationContext(w),
 
1075
            "cancelEditACT", "badParm", "XbaeMatrix",
 
1076
            "XbaeMatrix: Bad parameter for CancelEdit action",
 
1077
            NULL, 0);
 
1078
        return;
 
1079
    }
 
1080
    /*
 
1081
     * Call the cancel_edit method
 
1082
     */
 
1083
    (*((XbaeMatrixWidgetClass)XtClass(mw))->matrix_class.cancel_edit)
 
1084
        (mw, unmap);
 
1085
}
 
1086
 
 
1087
/*
 
1088
 * Action save any edits made and unmap the textField if params[0] is True
 
1089
 */
 
1090
/* ARGSUSED */
 
1091
void
 
1092
xbaeCommitEditACT(w, event, params, nparams)
 
1093
Widget w;
 
1094
XEvent *event;
 
1095
String *params;
 
1096
Cardinal *nparams;
 
1097
{
 
1098
    XbaeMatrixWidget mw;
 
1099
    Boolean unmap;
 
1100
 
 
1101
    /*
 
1102
     * Get Matrix widget and make sure it is a Matrix subclass.
 
1103
     * w could be Matrix, or the Clip or textField children of Matrix
 
1104
     */
 
1105
    if (XtIsSubclass(w, xbaeMatrixWidgetClass))
 
1106
        mw = (XbaeMatrixWidget)w;
 
1107
    else if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass))
 
1108
        mw = (XbaeMatrixWidget)XtParent(w);
 
1109
    else
 
1110
    {
 
1111
        XtAppWarningMsg(
 
1112
            XtWidgetToApplicationContext(w),
 
1113
            "commitEditACT", "badWidget", "XbaeMatrix",
 
1114
            "XbaeMatrix: Bad widget passed to CommitEdit action",
 
1115
            NULL, 0);
 
1116
        return;
 
1117
    }
 
1118
 
 
1119
    /*
 
1120
     * Make sure we have a single param
 
1121
     */
 
1122
    if (*nparams != 1)
 
1123
    {
 
1124
        XtAppWarningMsg(
 
1125
            XtWidgetToApplicationContext(w),
 
1126
            "commitEditACT", "badParms", "XbaeMatrix",
 
1127
            "XbaeMatrix: Wrong params for CommitEdit action, needs 1",
 
1128
            NULL, 0);
 
1129
        return;
 
1130
    }
 
1131
 
 
1132
    /*
 
1133
     * Validate our param
 
1134
     */
 
1135
    if (!strcmp(params[0], "True"))
 
1136
        unmap = True;
 
1137
    else if (!strcmp(params[0], "False"))
 
1138
        unmap = False;
 
1139
    else
 
1140
    {
 
1141
        XtAppWarningMsg(
 
1142
            XtWidgetToApplicationContext(w),
 
1143
            "commitEditACT", "badParm", "XbaeMatrix",
 
1144
            "XbaeMatrix: Bad parameter for CommitEdit action",
 
1145
            NULL, 0);
 
1146
        return;
 
1147
    }
 
1148
 
 
1149
    (void)(*((XbaeMatrixWidgetClass)XtClass(mw))->matrix_class.commit_edit)
 
1150
        (mw, event, unmap);
 
1151
}
 
1152
 
 
1153
static int
 
1154
DoubleClick(mw, event, row, column)
 
1155
XbaeMatrixWidget mw;
 
1156
XEvent *event;
 
1157
int row;
 
1158
int column;
 
1159
{
 
1160
    /* A double click in this instance is two clicks in the
 
1161
       same cell in a time period < double_click_interval */
 
1162
    Time current_time;
 
1163
    unsigned long delta;
 
1164
    static int ret = 0;
 
1165
 
 
1166
    if (event->type == ButtonRelease)
 
1167
    {
 
1168
        /* If the button is released, store the current location and time -
 
1169
           next time through, if it's a button press event, we check for
 
1170
           double click */
 
1171
        mw->matrix.last_row = row;
 
1172
        mw->matrix.last_column = column;
 
1173
        if (ret)                /* just had a double click */
 
1174
            mw->matrix.last_click_time = (Time)0;
 
1175
        else
 
1176
            mw->matrix.last_click_time = event->xbutton.time;
 
1177
        ret = 0;
 
1178
        return ret;
 
1179
    }
 
1180
 
 
1181
    current_time = event->xbutton.time;
 
1182
    delta = current_time - mw->matrix.last_click_time;
 
1183
 
 
1184
    if (row == mw->matrix.last_row && column == mw->matrix.last_column &&
 
1185
        delta < (unsigned long)mw->matrix.double_click_interval)
 
1186
        ret = 1;
 
1187
    else
 
1188
        ret = 0;
 
1189
 
 
1190
    return ret;
 
1191
}
 
1192
 
 
1193
/*ARGSUSED*/
 
1194
static void
 
1195
PushButton(w, data, event, cont)
 
1196
Widget w;
 
1197
XtPointer data;
 
1198
XEvent *event;
 
1199
Boolean *cont;
 
1200
{
 
1201
    XbaeMatrixButtonPressedStruct *button =
 
1202
         (XbaeMatrixButtonPressedStruct *)data;
 
1203
    XMotionEvent *motionEvent;
 
1204
    int x, y;
 
1205
    int row, column;
 
1206
    Boolean pressed = button->pressed;
 
1207
    CellType cell;
 
1208
 
 
1209
    if (event->type == ButtonRelease)
 
1210
    {
 
1211
        button->grabbed = False;
 
1212
        XtRemoveGrab(w);
 
1213
        scrolling = False;
 
1214
 
 
1215
        if (button->pressed)
 
1216
        {
 
1217
            /* If the button is still pressed, it has been released in the
 
1218
               same button that was pressed.  "Unpress" it and call the
 
1219
               callbacks */     
 
1220
            if (button->column == -1)
 
1221
                xbaeDrawRowLabel(button->mw, button->row, False);
 
1222
            else if (button->row == -1)
 
1223
                xbaeDrawColumnLabel(button->mw, button->column, False);    
 
1224
 
 
1225
            if (button->mw->matrix.label_activate_callback)
 
1226
            {
 
1227
                XbaeMatrixLabelActivateCallbackStruct call_data;
 
1228
                
 
1229
                call_data.reason = XbaeLabelActivateReason;
 
1230
                call_data.event = event;
 
1231
                call_data.row_label = (button->column == -1);
 
1232
                call_data.row = button->row;
 
1233
                call_data.column = button->column;
 
1234
 
 
1235
                if (button->column == -1)
 
1236
                    call_data.label =
 
1237
                        button->mw->matrix.row_labels[button->row];
 
1238
                else
 
1239
                    call_data.label =
 
1240
                        button->mw->matrix.column_labels[button->column];
 
1241
 
 
1242
                XtCallCallbackList((Widget)button->mw,
 
1243
                                   button->mw->matrix.label_activate_callback,
 
1244
                                   (XtPointer)&call_data);
 
1245
            }
 
1246
        }
 
1247
        return;
 
1248
    }
 
1249
 
 
1250
    if (event->type != MotionNotify) /* We want to be sure about this! */
 
1251
        return;
 
1252
 
 
1253
    motionEvent = (XMotionEvent *)event;
 
1254
    x = motionEvent->x;
 
1255
    y = motionEvent->y;
 
1256
    
 
1257
    if (!xbaeEventToXY(button->mw, event, &x, &y, &cell))
 
1258
        return;
 
1259
    
 
1260
    if (xbaeXYToRowCol(button->mw, &x, &y, &row, &column, cell))
 
1261
        /* Moved off the labels */
 
1262
        pressed = False;
 
1263
    else
 
1264
    {
 
1265
        if (button->column != column || button->row != row)
 
1266
            /* Moved out of the button that was originally pressed */
 
1267
            pressed = False;
 
1268
        else if (button->column == column || button->row == row)
 
1269
            pressed = True;
 
1270
    }   
 
1271
    /* If the status of whether or not the button should be pressed has
 
1272
       changed, redraw the appropriate visual */
 
1273
    if (pressed != button->pressed)
 
1274
    {
 
1275
        if (button->column == -1)
 
1276
            xbaeDrawRowLabel(button->mw, button->row, pressed);
 
1277
        else if (button->row == -1)
 
1278
            xbaeDrawColumnLabel(button->mw, button->column, pressed);    
 
1279
        /* And set our struct's pressed member to the current setting */
 
1280
        button->pressed = pressed;
 
1281
    }
 
1282
}
 
1283
 
 
1284
/*ARGSUSED*/
 
1285
void
 
1286
xbaeHandleClick(w, data, event, cont)
 
1287
Widget w;
 
1288
XtPointer data;
 
1289
XEvent *event;
 
1290
Boolean *cont;
 
1291
{
 
1292
    XbaeMatrixWidget mw = (XbaeMatrixWidget)data;
 
1293
    int x, y;
 
1294
    CellType cell;
 
1295
    int row, column;
 
1296
    Boolean translation;
 
1297
    
 
1298
    /* if we have a double click and a callback - break out! */
 
1299
    if (event->type != ButtonPress && event->type != ButtonRelease)
 
1300
        return;
 
1301
 
 
1302
    if (!xbaeEventToXY(mw, event, &x, &y, &cell))
 
1303
        return;
 
1304
 
 
1305
    translation = xbaeXYToRowCol(mw, &x, &y, &row, &column, cell);
 
1306
 
 
1307
    if (!translation &&
 
1308
        (mw->matrix.button_labels ||
 
1309
         (row == -1 && mw->matrix.column_button_labels &&
 
1310
          mw->matrix.column_button_labels[column]) ||
 
1311
         (column == -1 && mw->matrix.row_button_labels &&
 
1312
          mw->matrix.row_button_labels[row])) &&
 
1313
        ((row == -1) ^ (column == -1)))
 
1314
    {
 
1315
        unsigned long event_mask;
 
1316
        XtAppContext appcontext;
 
1317
        XbaeMatrixButtonPressedStruct button;
 
1318
        
 
1319
        /* If the row and column are invalid, return. If it is ButtonRelease
 
1320
           event, also return - the ButtonRelease events are handled in the
 
1321
           event handler loop below */
 
1322
        if (event->type != ButtonPress)
 
1323
            return;
 
1324
 
 
1325
        if (column == -1 && event->type == ButtonPress)
 
1326
            /* row label */
 
1327
            xbaeDrawRowLabel(mw, row, True);
 
1328
        else if (row == -1 && event->type == ButtonPress)
 
1329
            /* Column label */
 
1330
            xbaeDrawColumnLabel(mw, column, True);
 
1331
 
 
1332
        /* Action stations! */
 
1333
        event_mask = ButtonReleaseMask | PointerMotionMask;
 
1334
 
 
1335
        scrolling = True;
 
1336
 
 
1337
        XtAddGrab(w, True, False);
 
1338
        /* Copy the data needed to be passed to the event handler */
 
1339
        button.mw = mw;
 
1340
        button.row = row;
 
1341
        button.column = column;
 
1342
        button.pressed = True;
 
1343
        button.grabbed = True;
 
1344
                
 
1345
        XtAddEventHandler(w, event_mask,
 
1346
                           True, (XtEventHandler)PushButton,
 
1347
                           (XtPointer)&button);
 
1348
        XtAddEventHandler(TextChild(mw), event_mask,
 
1349
                           True, (XtEventHandler)PushButton,
 
1350
                           (XtPointer)&button);
 
1351
    
 
1352
        appcontext = XtWidgetToApplicationContext(w);
 
1353
 
 
1354
        while (button.grabbed)
 
1355
            XtAppProcessEvent(appcontext, XtIMAll);
 
1356
 
 
1357
        XtRemoveEventHandler(w, event_mask, True,
 
1358
                              (XtEventHandler)PushButton,
 
1359
                              (XtPointer)&button);
 
1360
        XtRemoveEventHandler(TextChild(mw), event_mask, True,
 
1361
                              (XtEventHandler)PushButton,
 
1362
                              (XtPointer)&button);
 
1363
 
 
1364
    }
 
1365
    else if (translation && mw->matrix.default_action_callback &&
 
1366
             w != (Widget)mw &&
 
1367
             DoubleClick(mw, event, mw->matrix.current_row,
 
1368
                         mw->matrix.current_column))
 
1369
    {
 
1370
        /* Put this as an else -> we don't want double clicks on labels
 
1371
           to be recognised */
 
1372
        XbaeMatrixDefaultActionCallbackStruct call_data;
 
1373
 
 
1374
        if (row == -1 || column == -1)
 
1375
            return;
 
1376
        
 
1377
        call_data.reason = XbaeDefaultActionReason;
 
1378
        call_data.event = event;
 
1379
        call_data.row = row;
 
1380
        call_data.column = column;
 
1381
 
 
1382
        XtCallCallbackList((Widget)mw, mw->matrix.default_action_callback,
 
1383
                           (XtPointer)&call_data);
 
1384
    }   
 
1385
}
 
1386
 
 
1387
/* ARGSUSED */
 
1388
void
 
1389
xbaeSelectCellACT(w, event, params, nparams)
 
1390
Widget w;
 
1391
XEvent *event;
 
1392
String *params;
 
1393
Cardinal *nparams;
 
1394
{
 
1395
    XbaeMatrixWidget mw;
 
1396
    int x, y;
 
1397
    int row, column;
 
1398
    CellType cell;
 
1399
    XbaeMatrixSelectCellCallbackStruct call_data;
 
1400
 
 
1401
    /*
 
1402
     * Get Matrix widget and make sure it is a Matrix subclass.
 
1403
     * w could be Matrix, or the Clip or textField children of Matrix
 
1404
     */
 
1405
    if (XtIsSubclass(w, xbaeMatrixWidgetClass))
 
1406
        mw = (XbaeMatrixWidget)w;
 
1407
    else if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass))
 
1408
        mw = (XbaeMatrixWidget)XtParent(w);
 
1409
    else
 
1410
    {
 
1411
        XtAppWarningMsg(
 
1412
            XtWidgetToApplicationContext(w),
 
1413
            "xbaeSelectCellACT", "badWidget", "XbaeMatrix",
 
1414
            "XbaeMatrix: Bad widget passed to SelectCell action",
 
1415
            NULL, 0);
 
1416
        return;
 
1417
    }
 
1418
 
 
1419
    /*
 
1420
     * If we don't have a selectCellCallback, then return now
 
1421
     */
 
1422
    if (!mw->matrix.select_cell_callback)
 
1423
        return;
 
1424
 
 
1425
    if (!xbaeEventToXY(mw, event, &x, &y, &cell))
 
1426
        return;
 
1427
 
 
1428
    /*
 
1429
     * Convert the point to a row,column. If it does not pick a valid
 
1430
     * cell, then return. If button up then use the last selected cell
 
1431
     * to make sure a valid button up event occurs when dragging out of
 
1432
     * the matrix
 
1433
     */
 
1434
    if (!xbaeXYToRowCol(mw, &x, &y, &row, &column, cell))
 
1435
    {
 
1436
        if (event->type == ButtonRelease)
 
1437
        {
 
1438
            column = last_selected_column;
 
1439
            row = last_selected_row;
 
1440
        }
 
1441
        else
 
1442
        {
 
1443
            return;
 
1444
        }
 
1445
    }
 
1446
 
 
1447
    /*
 
1448
     * Call our select_cell callbacks
 
1449
     */
 
1450
    call_data.reason = XbaeSelectCellReason;
 
1451
    call_data.event = event;
 
1452
 
 
1453
    if (scrolling)
 
1454
    {
 
1455
        call_data.row = last_row;
 
1456
        call_data.column = last_column;
 
1457
    }
 
1458
    else
 
1459
    {
 
1460
        call_data.row = row;
 
1461
        call_data.column = column;
 
1462
    }
 
1463
    
 
1464
    last_selected_column = call_data.column;
 
1465
    last_selected_row = call_data.row;
 
1466
    
 
1467
    call_data.selected_cells = mw->matrix.selected_cells;
 
1468
    call_data.cells = mw->matrix.cells;
 
1469
    call_data.num_params = *nparams;
 
1470
    call_data.params = params;
 
1471
 
 
1472
    XtCallCallbackList((Widget)mw, mw->matrix.select_cell_callback,
 
1473
                       (XtPointer)&call_data);
 
1474
}
 
1475
 
 
1476
 
 
1477
/* ARGSUSED */
 
1478
void
 
1479
xbaeTraverseNextACT(w, event, params, nparams)
 
1480
Widget w;
 
1481
XEvent *event;
 
1482
String *params;
 
1483
Cardinal *nparams;
 
1484
{
 
1485
    XbaeMatrixWidget mw;
 
1486
 
 
1487
    /*
 
1488
     * Get Matrix widget and make sure it is a Matrix subclass.
 
1489
     * w should be the textField widget.
 
1490
     */
 
1491
    if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass))
 
1492
        mw = (XbaeMatrixWidget)XtParent(w);
 
1493
    else
 
1494
    {
 
1495
        XtAppWarningMsg(
 
1496
            XtWidgetToApplicationContext(w),
 
1497
            "traverseNextACT", "badWidget", "XbaeMatrix",
 
1498
            "XbaeMatrix: Bad widget passed to TraverseNext action",
 
1499
            NULL, 0);
 
1500
        return;
 
1501
    }
 
1502
 
 
1503
    /*
 
1504
     * Set the traversing direction flag.  XmProcessTraversal may traverse
 
1505
     * to the Clip widget. If it does, then we will see this flag in
 
1506
     * the Clip focusCallback, TraverseInCB, and we will continue to traverse
 
1507
     * on out of the mw.  yuck!
 
1508
     */
 
1509
    mw->matrix.traversing = XmTRAVERSE_NEXT_TAB_GROUP;
 
1510
    (void)XmProcessTraversal(TextChild(mw), XmTRAVERSE_NEXT_TAB_GROUP);
 
1511
    mw->matrix.traversing = NOT_TRAVERSING;
 
1512
}
 
1513
 
 
1514
/* ARGSUSED */
 
1515
void
 
1516
xbaeTraversePrevACT(w, event, params, nparams)
 
1517
Widget w;
 
1518
XEvent *event;
 
1519
String *params;
 
1520
Cardinal *nparams;
 
1521
{
 
1522
    XbaeMatrixWidget mw;
 
1523
 
 
1524
    /*
 
1525
     * Get Matrix widget and make sure it is a Matrix subclass.
 
1526
     * w should be the textField widget.
 
1527
     */
 
1528
    if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass))
 
1529
        mw = (XbaeMatrixWidget)XtParent(w);
 
1530
    else
 
1531
    {
 
1532
        XtAppWarningMsg(
 
1533
            XtWidgetToApplicationContext(w),
 
1534
            "traversePrevACT", "badWidget", "XbaeMatrix",
 
1535
            "XbaeMatrix: Bad widget passed to TraversePrev action",
 
1536
            NULL, 0);
 
1537
        return;
 
1538
    }
 
1539
 
 
1540
    /*
 
1541
     * Set the traversing direction flag.  XmProcessTraversal may traverse
 
1542
     * to the Clip widget. If it does, then we will see this flag in
 
1543
     * the Clip focusCallback, TraverseInCB, and we will continue to traverse
 
1544
     * on out of the mw.  yuck!
 
1545
     */
 
1546
    mw->matrix.traversing = (int)XmTRAVERSE_PREV_TAB_GROUP;
 
1547
    (void)XmProcessTraversal(TextChild(mw), XmTRAVERSE_PREV_TAB_GROUP);
 
1548
    mw->matrix.traversing = NOT_TRAVERSING;
 
1549
}
 
1550
 
 
1551
static void
 
1552
callSelectCellAction(mw, event)
 
1553
XbaeMatrixWidget mw;
 
1554
XEvent *event;
 
1555
{
 
1556
    XbaeMatrixSelectCellCallbackStruct call_data;
 
1557
    Boolean old_scroll_select = mw->matrix.scroll_select;
 
1558
    
 
1559
    mw->matrix.scroll_select = False;
 
1560
 
 
1561
    call_data.reason = XbaeSelectCellReason;
 
1562
    call_data.event = event;
 
1563
    call_data.row = last_row;
 
1564
    call_data.column = last_column;
 
1565
    call_data.selected_cells = mw->matrix.selected_cells;
 
1566
    call_data.cells = mw->matrix.cells;
 
1567
    call_data.num_params = 1;
 
1568
    call_data.params = (char **)XtMalloc(sizeof(char *));
 
1569
    call_data.params[0] = "extend";
 
1570
    
 
1571
    XtCallCallbackList(
 
1572
         (Widget)mw, mw->matrix.select_cell_callback,
 
1573
         (XtPointer)&call_data);
 
1574
 
 
1575
     (void)XtFree((char *)call_data.params);
 
1576
 
 
1577
     mw->matrix.scroll_select = old_scroll_select;
 
1578
}
 
1579
 
 
1580
 
 
1581
/*ARGSUSED*/
 
1582
static void
 
1583
checkScrollValues(w, data, event, cont)
 
1584
Widget w;
 
1585
XtPointer data;
 
1586
XEvent *event;
 
1587
Boolean *cont;
 
1588
{
 
1589
    XbaeMatrixScrollStruct *ss = (XbaeMatrixScrollStruct *)data;
 
1590
    XMotionEvent *motionEvent;
 
1591
    int x, y;
 
1592
    CellType cell;
 
1593
    Boolean inMatrix;
 
1594
    int distance = 0;
 
1595
    int halfRows;
 
1596
    int denom = 1;
 
1597
    int row, column;
 
1598
    int i;
 
1599
 
 
1600
    ss->event = event;
 
1601
 
 
1602
    if (event->type == ButtonRelease)
 
1603
    {
 
1604
        XtRemoveTimeOut(ss->timerID);
 
1605
        ss->grabbed = False;
 
1606
 
 
1607
        if (ss->mw->matrix.selection_policy == XmMULTIPLE_SELECT ||
 
1608
            ss->mw->matrix.selection_policy == XmEXTENDED_SELECT)
 
1609
            callSelectCellAction(ss->mw, ss->event);
 
1610
 
 
1611
        return;
 
1612
    }
 
1613
 
 
1614
    if (!xbaeEventToXY(ss->mw, event, &x, &y, &cell))
 
1615
        return;
 
1616
 
 
1617
    motionEvent = (XMotionEvent *)event;
 
1618
 
 
1619
    /*
 
1620
     * In this instance, we don't care if a valid row and column are
 
1621
     * returned as we'll be the judge of the result
 
1622
     */
 
1623
    inMatrix = xbaeXYToRowCol(ss->mw, &x, &y, &row, &column, cell);
 
1624
 
 
1625
    /*
 
1626
     * Reset the flags, so the matrix stops scrolling when the
 
1627
     * pointer is moved back into the fixed columns/rows after a drag
 
1628
     * select in the fixed columns/rows which caused the matrix to
 
1629
     * scroll vertically/horizontally. 
 
1630
     */
 
1631
    ss->below = False;
 
1632
    ss->above = False;
 
1633
    ss->left = False;
 
1634
    ss->right = False;
 
1635
 
 
1636
    if (inMatrix && cell == NonFixedCell)
 
1637
    {
 
1638
        ss->inClip = True;
 
1639
        return;
 
1640
    }
 
1641
    else
 
1642
    {
 
1643
        /*
 
1644
         * Calculate our position relative to the clip and adjust.
 
1645
         */
 
1646
        if (motionEvent->y >= (int)(ss->cw->core.y + ss->cw->core.height))
 
1647
        {
 
1648
            /* Below the matrix */
 
1649
            distance = motionEvent->y - ss->cw->core.y - ss->cw->core.height;
 
1650
            ss->below = True;
 
1651
            ss->above = False;
 
1652
            /*
 
1653
             * If we are below the matrix, the current column may have
 
1654
             * still changed from horizontal motion.
 
1655
             */
 
1656
            i = 0;
 
1657
            while (COLUMN_POSITION(ss->mw, i) < HORIZ_ORIGIN(ss->mw) +
 
1658
                   motionEvent->x)
 
1659
                i++;
 
1660
 
 
1661
            if (i <= ss->mw->matrix.columns && i > 0)
 
1662
                last_column = i - 1;
 
1663
        }
 
1664
        else if (motionEvent->y <= ss->cw->core.y)
 
1665
        {
 
1666
            /*
 
1667
             * Above the matrix - can't be both above and below at the same
 
1668
             * time unless we have two mouses!
 
1669
             */
 
1670
            distance = ss->cw->core.y - motionEvent->y;
 
1671
            ss->below = False;
 
1672
            ss->above = True;
 
1673
            i = 0;
 
1674
            while (COLUMN_POSITION(ss->mw, i) < HORIZ_ORIGIN(ss->mw) +
 
1675
                   motionEvent->x)
 
1676
                i++;
 
1677
            if (i > 0 && i <= ss->mw->matrix.columns)
 
1678
                last_column = i - 1;
 
1679
        }
 
1680
        if (motionEvent->x <= ss->cw->core.x)
 
1681
        {
 
1682
            /* To the left */
 
1683
            ss->left = True;
 
1684
            ss->right = False;
 
1685
            distance = Min(distance, ss->cw->core.x - motionEvent->x);
 
1686
            /*
 
1687
             * Check for any vertical motion
 
1688
             */
 
1689
            if (!ss->below && !ss->above)
 
1690
            {
 
1691
                last_row = YtoRow(ss->mw, motionEvent->y -
 
1692
                                   COLUMN_LABEL_HEIGHT(ss->mw)) +
 
1693
                    VERT_ORIGIN(ss->mw);
 
1694
                SANITY_CHECK_ROW(ss->mw, last_row);
 
1695
            }
 
1696
        }
 
1697
        else if (motionEvent->x >= (int)(ss->cw->core.x + ss->cw->core.width))
 
1698
        {
 
1699
            /* To the right */
 
1700
            ss->left = False;
 
1701
            ss->right = True;
 
1702
            distance = Min(distance, (int)(motionEvent->x - ss->cw->core.x -
 
1703
                            ss->cw->core.width));
 
1704
            if (!ss->below && !ss->above)
 
1705
            {
 
1706
                last_row = YtoRow(ss->mw, motionEvent->y -
 
1707
                                   COLUMN_LABEL_HEIGHT(ss->mw)) +
 
1708
                    VERT_ORIGIN(ss->mw);
 
1709
                SANITY_CHECK_ROW(ss->mw, last_row);
 
1710
            }
 
1711
        }
 
1712
        /*
 
1713
         * Adjust the value of the update interval based on the distance we
 
1714
         * are away from the matrix
 
1715
         */
 
1716
        halfRows = distance / (ROW_HEIGHT(ss->mw) / 2);
 
1717
        /*
 
1718
         * Avoid use of the math library by doing a simple calculation
 
1719
         */
 
1720
        for (i = 0; i < halfRows; i++)
 
1721
            denom *= 2;
 
1722
 
 
1723
        ss->interval = DEFAULT_SCROLL_SPEED / (denom > 0 ? denom : 1);
 
1724
 
 
1725
        if (ss->interval <= 0)  /* Just to be on the safe side */
 
1726
            ss->interval = 1;
 
1727
    }
 
1728
}
 
1729
 
 
1730
static void
 
1731
updateScroll(data)
 
1732
XtPointer data;
 
1733
{
 
1734
    XbaeMatrixScrollStruct *ss = (XbaeMatrixScrollStruct *)data;
 
1735
    Boolean callCallback = False;
 
1736
    static int my_last_row = -1, my_last_column = -1;
 
1737
 
 
1738
    if (!scrolling)
 
1739
        return;
 
1740
    
 
1741
    if (my_last_column != last_column || my_last_row != last_row)
 
1742
        callCallback = True;
 
1743
 
 
1744
    my_last_row = last_row;
 
1745
    my_last_column = last_column;
 
1746
    /*
 
1747
     * Off the clip widget - check there are cells that could
 
1748
     * be scrolled into a visible position.  If there are,
 
1749
     * scroll them into view.  If not, start setting the fixed
 
1750
     * rows and columns as the current row and column.
 
1751
     */
 
1752
    if (ss->below && last_row < TRAILING_VERT_ORIGIN(ss->mw) - 1)
 
1753
    {
 
1754
        xbaeMakeRowVisible(ss->mw, ++last_row);
 
1755
        callCallback = True;
 
1756
    }
 
1757
    else if (ss->above && last_row > (int)ss->mw->matrix.fixed_rows)
 
1758
    {
 
1759
        xbaeMakeRowVisible(ss->mw, --last_row);
 
1760
        callCallback = True;
 
1761
    }
 
1762
    if (ss->right && last_column < TRAILING_HORIZ_ORIGIN(ss->mw) - 1)
 
1763
    {
 
1764
        xbaeMakeColumnVisible(ss->mw, ++last_column);
 
1765
        callCallback = True;
 
1766
    }
 
1767
    else if (ss->left && last_column > (int)ss->mw->matrix.fixed_columns)
 
1768
    {
 
1769
        xbaeMakeColumnVisible(ss->mw, --last_column);
 
1770
        callCallback = True;
 
1771
    }
 
1772
 
 
1773
    if (callCallback &&
 
1774
         (ss->mw->matrix.selection_policy == XmMULTIPLE_SELECT ||
 
1775
          ss->mw->matrix.selection_policy == XmEXTENDED_SELECT))
 
1776
        callSelectCellAction(ss->mw, ss->event);
 
1777
    
 
1778
    /*
 
1779
     * Flush the updates out to the server so we don't end up lagging
 
1780
     * behind too far and end up with a million redraw requests.
 
1781
     * Particularly for higher update speeds
 
1782
     */
 
1783
    XFlush(XtDisplay((Widget)ss->mw));
 
1784
 
 
1785
    ss->timerID = XtAppAddTimeOut(
 
1786
        ss->app_context, ss->interval, (XtTimerCallbackProc)updateScroll,
 
1787
        (XtPointer)ss);    
 
1788
}
 
1789
 
 
1790
/* ARGSUSED */
 
1791
void
 
1792
xbaeHandleMotionACT(w, event, params, nparams)
 
1793
Widget w;
 
1794
XEvent *event;
 
1795
String *params;
 
1796
Cardinal *nparams;
 
1797
{
 
1798
    XbaeMatrixWidget mw;
 
1799
    XbaeClipWidget cw;
 
1800
    XMotionEvent *motionEvent;
 
1801
    XButtonEvent *buttonEvent;
 
1802
    int x, y, row, column;
 
1803
    CellType cell;
 
1804
    Boolean inMatrix;
 
1805
 
 
1806
    if (scrolling)
 
1807
        return;
 
1808
 
 
1809
    /*
 
1810
     * Get Matrix widget and make sure it is a Matrix subclass.
 
1811
     * w could be Matrix, or the Clip or textField children of Matrix
 
1812
     */
 
1813
    if (XtIsSubclass(w, xbaeMatrixWidgetClass))
 
1814
        mw = (XbaeMatrixWidget)w;
 
1815
    else if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass))
 
1816
        mw = (XbaeMatrixWidget)XtParent(w);
 
1817
    else {
 
1818
        XtAppWarningMsg(
 
1819
            XtWidgetToApplicationContext(w),
 
1820
            "handleMotionACT", "badWidget", "XbaeMatrix",
 
1821
            "XbaeMatrix: Bad widget passed to HandleMotion action",
 
1822
            NULL, 0);
 
1823
        return;
 
1824
    }
 
1825
    motionEvent = (XMotionEvent *)event;
 
1826
    buttonEvent = (XButtonEvent *)event;
 
1827
 
 
1828
    cw = (XbaeClipWidget)ClipChild(mw);
 
1829
 
 
1830
    if (!xbaeEventToXY(mw, event, &x, &y, &cell))
 
1831
        return;
 
1832
 
 
1833
    /*
 
1834
     * In this instance, we don't care if a valid row and column are
 
1835
     * returned as we'll be the judge of the result
 
1836
     */
 
1837
    inMatrix = xbaeXYToRowCol(mw, &x, &y, &row, &column, cell);
 
1838
 
 
1839
    if (inMatrix && cell == NonFixedCell)
 
1840
    {
 
1841
        /*
 
1842
         * If we are in a NonFixedCell, then we're cruisin'.  Just
 
1843
         * update our position
 
1844
         */
 
1845
        if (row != last_row || column != last_column)
 
1846
        {
 
1847
            if (row < mw->matrix.rows && column < mw->matrix.columns)
 
1848
            {
 
1849
                last_row = row;
 
1850
                last_column = column;
 
1851
 
 
1852
                if (mw->matrix.selection_policy == XmMULTIPLE_SELECT ||
 
1853
                    mw->matrix.selection_policy == XmEXTENDED_SELECT)
 
1854
                    callSelectCellAction(mw, event);
 
1855
            }
 
1856
        }
 
1857
    }
 
1858
    else
 
1859
    {
 
1860
        XbaeMatrixScrollStruct scrollData;
 
1861
        Boolean cont;
 
1862
 
 
1863
        /*
 
1864
         * Grab the pointer and add a timeout routine to start modifying
 
1865
         * the current row and/or column in the matrix.  Also add an
 
1866
         * event handler to monitor the current distance outside the
 
1867
         * matrix so we can adjust the timeout routine to go faster when
 
1868
         * the pointer is further away from the matrix.
 
1869
         */
 
1870
 
 
1871
        scrolling = True;
 
1872
 
 
1873
        XtAddGrab(w, True, False);
 
1874
 
 
1875
        scrollData.mw = mw;
 
1876
        scrollData.cw = cw;
 
1877
        scrollData.event = event;
 
1878
        scrollData.interval = DEFAULT_SCROLL_SPEED;
 
1879
        scrollData.inClip = False;
 
1880
        scrollData.grabbed = True;
 
1881
        scrollData.app_context = XtWidgetToApplicationContext(w);
 
1882
        scrollData.above = scrollData.below = False;
 
1883
        scrollData.left = scrollData.right = False;
 
1884
        
 
1885
        XtAddEventHandler(w, PointerMotionMask | ButtonReleaseMask,
 
1886
                           True, (XtEventHandler)checkScrollValues,
 
1887
                           (XtPointer)&scrollData);
 
1888
        /*
 
1889
         * Call checkScrollValues() to find out where exactly we are in
 
1890
         * relation to the clip widget
 
1891
         */
 
1892
        checkScrollValues(w, (XtPointer)&scrollData, event, &cont);
 
1893
 
 
1894
        /*
 
1895
         * The above / below / left / right members of the scrollData struct
 
1896
         * should now be set so we know where we should be moving.  Let's
 
1897
         * get on with it, eh?
 
1898
         */
 
1899
        updateScroll((XtPointer)&scrollData);
 
1900
 
 
1901
        while (scrollData.grabbed && !scrollData.inClip)
 
1902
            XtAppProcessEvent(scrollData.app_context, XtIMAll);
 
1903
 
 
1904
        XtRemoveEventHandler(w, PointerMotionMask | ButtonReleaseMask,
 
1905
                              True, (XtEventHandler)checkScrollValues,
 
1906
                              (XtPointer)&scrollData);
 
1907
 
 
1908
        XtRemoveGrab(w);
 
1909
        /*
 
1910
         * We don't want the timeout getting called again as, in two lines,
 
1911
         * we'll be way out of scope!
 
1912
         */
 
1913
        XtRemoveTimeOut(scrollData.timerID);
 
1914
        scrolling = False;
 
1915
    }
 
1916
}
 
1917
 
 
1918
/* ARGSUSED */
 
1919
void
 
1920
xbaePageDownACT(w, event, params, nparams)
 
1921
Widget w;
 
1922
XEvent *event;
 
1923
String *params;
 
1924
Cardinal *nparams;
 
1925
{
 
1926
    XbaeMatrixWidget mw;
 
1927
    char *down = "0";
 
1928
    int top;
 
1929
    /*
 
1930
     * Get Matrix widget and make sure it is a Matrix subclass.
 
1931
     * w should be the textField widget.
 
1932
     */
 
1933
    if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass))
 
1934
        mw = (XbaeMatrixWidget)XtParent(w);
 
1935
    else
 
1936
    {
 
1937
        XtAppWarningMsg(
 
1938
            XtWidgetToApplicationContext(w),
 
1939
            "pageDownACT", "badWidget", "XbaeMatrix",
 
1940
            "XbaeMatrix: Bad widget passed to PageDown action",
 
1941
            NULL, 0);
 
1942
        return;
 
1943
    }
 
1944
 
 
1945
    if (!XtIsManaged(VertScrollChild(mw)))
 
1946
        return;
 
1947
 
 
1948
    /*
 
1949
     * Save the top row - if scrolling occurs, the text widget needs
 
1950
     * to be moved
 
1951
     */
 
1952
    top = VERT_ORIGIN(mw);
 
1953
 
 
1954
    XtCallActionProc(VertScrollChild(mw), "PageDownOrRight",
 
1955
                     event, &down, 1);
 
1956
 
 
1957
    if (VERT_ORIGIN(mw) != top)
 
1958
        /*
 
1959
         * Position the cursor at the top most non fixed row if there was
 
1960
         * a page down
 
1961
         */
 
1962
        XbaeMatrixEditCell((Widget)mw, VERT_ORIGIN(mw) +
 
1963
                           mw->matrix.fixed_rows, mw->matrix.current_column);
 
1964
}
 
1965
 
 
1966
/* ARGSUSED */
 
1967
void
 
1968
xbaePageUpACT(w, event, params, nparams)
 
1969
Widget w;
 
1970
XEvent *event;
 
1971
String *params;
 
1972
Cardinal *nparams;
 
1973
{
 
1974
    XbaeMatrixWidget mw;
 
1975
    char *up = "0";
 
1976
    int top;
 
1977
 
 
1978
    /*
 
1979
     * Get Matrix widget and make sure it is a Matrix subclass.
 
1980
     * w should be the textField widget.
 
1981
     */
 
1982
    if (XtIsSubclass(XtParent(w), xbaeMatrixWidgetClass))
 
1983
        mw = (XbaeMatrixWidget)XtParent(w);
 
1984
    else
 
1985
    {
 
1986
        XtAppWarningMsg(
 
1987
            XtWidgetToApplicationContext(w),
 
1988
            "pageUpACT", "badWidget", "XbaeMatrix",
 
1989
            "XbaeMatrix: Bad widget passed to PageUp action",
 
1990
            NULL, 0);
 
1991
        return;
 
1992
    }
 
1993
 
 
1994
    if (!XtIsManaged(VertScrollChild(mw)))
 
1995
        return;
 
1996
 
 
1997
    /*
 
1998
     * Save the top row - if scrolling occurs, the text widget needs
 
1999
     * to be moved
 
2000
     */
 
2001
    top = VERT_ORIGIN(mw);
 
2002
 
 
2003
    XtCallActionProc(VertScrollChild(mw), "PageUpOrLeft",
 
2004
                     event, &up, 1);
 
2005
 
 
2006
    if (VERT_ORIGIN(mw) != top)
 
2007
        /*
 
2008
         * Position the cursor at the top most non fixed row if there was
 
2009
         * a page down
 
2010
         */
 
2011
        XbaeMatrixEditCell((Widget)mw, VERT_ORIGIN(mw) +
 
2012
                           mw->matrix.fixed_rows, mw->matrix.current_column);
 
2013
}
 
2014