1 /*
2  * Copyright(c) 1992 Bell Communications Research, Inc. (Bellcore)
3  * Copyright(c) 1995-99 Andrew Lister
4  *                        All rights reserved
5  * Permission to use, copy, modify and distribute this material for
6  * any purpose and without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies, and that the name of Bellcore not be used in advertising
9  * or publicity pertaining to this material without the specific,
10  * prior written permission of an authorized representative of
11  * Bellcore.
12  *
13  * BELLCORE MAKES NO REPRESENTATIONS AND EXTENDS NO WARRANTIES, EX-
14  * PRESS OR IMPLIED, WITH RESPECT TO THE SOFTWARE, INCLUDING, BUT
15  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS FOR ANY PARTICULAR PURPOSE, AND THE WARRANTY AGAINST IN-
17  * FRINGEMENT OF PATENTS OR OTHER INTELLECTUAL PROPERTY RIGHTS.  THE
18  * SOFTWARE IS PROVIDED "AS IS", AND IN NO EVENT SHALL BELLCORE OR
19  * ANY OF ITS AFFILIATES BE LIABLE FOR ANY DAMAGES, INCLUDING ANY
20  * LOST PROFITS OR OTHER INCIDENTAL OR CONSEQUENTIAL DAMAGES RELAT-
21  * ING TO THE SOFTWARE.
22  *
23  * MatrixWidget Author: Andrew Wason, Bellcore, aw@bae.bellcore.com
24  *
25  * $Id: Utils.c,v 1.1 1999/09/11 01:25:38 fnevgeny Exp $
26  */
27 
28 /*
29  * Utils.c created by Andrew Lister (7 August, 1995)
30  */
31 
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35 
36 #include <Xm/Xm.h>
37 #include <Xbae/MatrixP.h>
38 #include <Xbae/Macros.h>
39 #include <Xbae/Utils.h>
40 #include <Xbae/Actions.h>
41 #include <Xm/ScrollBar.h>
42 /*
43  * Return the top and bottom-most visible non-fixed row
44  */
45 void
xbaeGetVisibleRows(mw,top_row,bottom_row)46 xbaeGetVisibleRows(mw, top_row, bottom_row)
47 XbaeMatrixWidget mw;
48 int *top_row, *bottom_row;
49 {
50     *top_row = VERT_ORIGIN(mw) + mw->matrix.fixed_rows;
51     *bottom_row = *top_row + (VISIBLE_HEIGHT(mw) - 1) / ROW_HEIGHT(mw);
52     SANITY_CHECK_ROW(mw, *bottom_row);
53 }
54 
55 /*
56  * Return the left and right-most visible non-fixed column
57  */
58 void
xbaeGetVisibleColumns(mw,left_column,right_column)59 xbaeGetVisibleColumns(mw, left_column, right_column)
60 XbaeMatrixWidget mw;
61 int *left_column, *right_column;
62 {
63     *left_column = xbaeXtoCol(mw, FIXED_COLUMN_WIDTH(mw) + HORIZ_ORIGIN(mw));
64     *right_column = xbaeXtoCol(mw, FIXED_COLUMN_WIDTH(mw) + HORIZ_ORIGIN(mw) +
65 			       VISIBLE_WIDTH(mw) - 1);
66 }
67 
68 /*
69  * Return the top and bottom row and left and right column of
70  * the visible non-fixed cells
71  */
72 void
xbaeGetVisibleCells(mw,top_row,bottom_row,left_column,right_column)73 xbaeGetVisibleCells(mw, top_row, bottom_row, left_column, right_column)
74 XbaeMatrixWidget mw;
75 int *top_row, *bottom_row, *left_column, *right_column;
76 {
77     xbaeGetVisibleRows(mw, top_row, bottom_row);
78     xbaeGetVisibleColumns(mw, left_column, right_column);
79 }
80 
81 /*
82  * Try to make the column specified by the leftColumn resource
83  * be the left column. The column is relative to fixed_columns - so 0 would
84  * be the first non-fixed column.
85  * If we can't make leftColumn the left column, make it as close as possible.
86  */
87 void
xbaeAdjustLeftColumn(mw)88 xbaeAdjustLeftColumn(mw)
89 XbaeMatrixWidget mw;
90 {
91     int y;
92     int i;
93     int required_width;
94     int visible_width = VISIBLE_WIDTH(mw);
95     int dynamic_columns;
96 
97     if (visible_width < 0)	/* will happen on initialisation */
98 	return;
99 
100     /* Adjust the column if it is out of bounds */
101 
102     dynamic_columns =  mw->matrix.columns - mw->matrix.fixed_columns -
103 	mw->matrix.trailing_fixed_columns;
104 
105     if (mw->matrix.left_column < 0)
106 	mw->matrix.left_column = 0;
107     else if (mw->matrix.left_column > dynamic_columns-1)
108 	mw->matrix.left_column =  dynamic_columns-1;
109 
110     /* Find out where the horiz_origin will be if we tried setting the
111        given left column and adjust till it all fits */
112     do
113     {
114 	required_width = 0;
115 	HORIZ_ORIGIN(mw) = 0;
116 	xbaeRowColToXY(mw, mw->matrix.fixed_rows,
117 		       mw->matrix.left_column + mw->matrix.fixed_columns,
118 		       &mw->matrix.horiz_origin, &y);
119 	/* Check how much space is remaining */
120 	for (i = mw->matrix.left_column + mw->matrix.fixed_columns;
121 	     i < mw->matrix.columns -
122 		 (int)mw->matrix.trailing_fixed_columns; i++)
123 	{
124 	    required_width += COLUMN_WIDTH(mw, i);
125  	    if (required_width >= visible_width)
126 		break;
127 	}
128 	if (required_width < visible_width)
129 	    mw->matrix.left_column--;
130     }
131     while (required_width < visible_width);
132 }
133 
134 /*
135  * Try to make the row specified by the topRow resource (VERT_ORIGIN)
136  * be the top row. The row is relative to fixed_rows - so 0 would
137  * be the first non-fixed row.
138  * If we can't make topRow the top row, make it as close as possible.
139  */
140 void
xbaeAdjustTopRow(mw)141 xbaeAdjustTopRow(mw)
142 XbaeMatrixWidget mw;
143 {
144     int rows_visible = VISIBLE_HEIGHT(mw) / ROW_HEIGHT(mw);
145 
146     /*
147      * If we have less than one full row visible, then count it as a full row
148      */
149     if (rows_visible <= 0)
150 	rows_visible = 1;
151     /*
152      * rows_visible might be inaccurate since Clip may not have been resized
153      */
154     else if (rows_visible > mw->matrix.rows)
155 	rows_visible = mw->matrix.rows;
156 
157     if (VERT_ORIGIN(mw) > (int)(mw->matrix.rows - rows_visible -
158 				mw->matrix.fixed_rows -
159 				mw->matrix.trailing_fixed_rows))
160 	mw->matrix.top_row = mw->matrix.rows - rows_visible -
161 	    mw->matrix.fixed_rows - mw->matrix.trailing_fixed_rows;
162     else if (VERT_ORIGIN(mw) < 0)
163 	mw->matrix.top_row = 0;
164 }
165 
166 /*
167  * Utility function to clear a cell so we can draw something new in it.
168  * Does not generate expose events on the cell.
169  * Does not check if the cell is actually visible before clearing it.
170  */
171 void
xbaeClearCell(mw,row,column)172 xbaeClearCell(mw, row, column)
173 XbaeMatrixWidget mw;
174 int row, column;
175 {
176     int x, y;
177     Boolean fixed = IS_FIXED(mw, row, column);
178     Window win = fixed ? XtWindow(mw) : XtWindow(ClipChild(mw));
179 
180     if (!win || mw->matrix.disable_redisplay)
181 	return;
182 
183     xbaeRowColToXY(mw, row, column, &x, &y);
184 
185     /*
186      * Make sure y coord is valid
187      */
188 #if 0
189     if ((win == XtWindow(mw)) &&
190 	((y > (int)(CLIP_VERT_VISIBLE_SPACE(mw) +
191 		    ROW_LABEL_OFFSET(mw) - 1)) ||
192 	 (y < (int)ROW_LABEL_OFFSET(mw))))
193 	return;
194 #endif
195     XClearArea(XtDisplay(mw), win, x, y, COLUMN_WIDTH(mw, column),
196 	       ROW_HEIGHT(mw), fixed);
197 }
198 
199 /*
200  * Return True if a row is visible on the screen (not scrolled totally off)
201  */
202 Boolean
xbaeIsRowVisible(mw,row)203 xbaeIsRowVisible(mw, row)
204 XbaeMatrixWidget mw;
205 int row;
206 {
207     /*
208      * If we are not in a fixed row or trailing fixed row,
209      * see if we are on the screen vertically
210      * (fixed rows are always on the screen)
211      */
212     if (! IS_FIXED_ROW(mw, row))
213     {
214 	row -= mw->matrix.fixed_rows;
215 
216 	if (row >= VERT_ORIGIN(mw))
217 	{
218 	    double height = ((double)ClipChild(mw)->core.height /
219 			     ROW_HEIGHT(mw)) + VERT_ORIGIN(mw);
220 	    if (row < height)
221 		return True;
222 
223 	    if ((int)ClipChild(mw)->core.height >
224 		(int)TEXT_HEIGHT_OFFSET(mw) &&
225 		(int)ClipChild(mw)->core.height < ROW_HEIGHT(mw) &&
226 		row == VERT_ORIGIN(mw))
227 
228 		return True;
229 	}
230     }
231     else
232 	return True;
233 
234     return False;
235 }
236 
237 /*
238  * Return True if a column is visible on the screen (not scrolled totally off)
239  */
240 Boolean
xbaeIsColumnVisible(mw,column)241 xbaeIsColumnVisible(mw, column)
242 XbaeMatrixWidget mw;
243 int column;
244 {
245     /*
246      * If we are not in a fixed column, see if we are on the screen
247      * horizontally (fixed columns are always on the screen)
248      */
249     if (! IS_FIXED_COLUMN(mw, column))
250     {
251 	int x;
252 
253 	/*
254 	 * Calculate the x endpoints of this column
255 	 */
256 	x = COLUMN_POSITION(mw, column) -
257 	    COLUMN_POSITION(mw, mw->matrix.fixed_columns);
258 
259 	/*
260 	 * Check if we are visible horizontally
261 	 */
262 	if (x + COLUMN_WIDTH(mw, column) > HORIZ_ORIGIN(mw) &&
263 	    x < (int)(ClipChild(mw)->core.width) + HORIZ_ORIGIN(mw))
264 	    return True;
265     }
266     else
267 	return True;
268 
269     return False;
270 }
271 
272 /*
273  * Return True if a cell is visible on the screen (not scrolled totally off)
274  */
275 Boolean
xbaeIsCellVisible(mw,row,column)276 xbaeIsCellVisible(mw, row, column)
277 XbaeMatrixWidget mw;
278 int row, column;
279 {
280     return xbaeIsRowVisible(mw, row) && xbaeIsColumnVisible(mw, column);
281 }
282 
283 /*
284  * Scroll a row so it is visible on the screen.
285  */
286 void
xbaeMakeRowVisible(mw,row)287 xbaeMakeRowVisible(mw, row)
288 XbaeMatrixWidget mw;
289 int row;
290 {
291     int rows_visible;
292     int value, slider_size, increment, page_increment, vert_value;
293 
294     /*
295      * If we are in a fixed row, we are already visible.
296      */
297     if (IS_FIXED_ROW(mw, row))
298 	return;
299 
300     /*
301      * Take into account fixed_rows.
302      * Calculate the number of rows visible. If less than one full
303      * row is visible, use one full row.
304      */
305     row -= mw->matrix.fixed_rows;
306     rows_visible = VISIBLE_HEIGHT(mw) / ROW_HEIGHT(mw);
307     if (rows_visible == 0)
308 	rows_visible = 1;
309 
310     /*
311      * Figure out the new value of the VSB to scroll this cell
312      * onto the screen (the VSB uses row coordinates instead of pixels)
313      */
314     if (row < VERT_ORIGIN(mw))
315 	vert_value = row;
316     else if (row >= rows_visible + VERT_ORIGIN(mw))
317 	vert_value = row - rows_visible + 1;
318     else
319 	vert_value = VERT_ORIGIN(mw);
320 
321     /*
322      * Give the VSB the new value and pass a flag to make it call
323      * our scroll callbacks
324      */
325     if (vert_value != VERT_ORIGIN(mw))
326     {
327 	XmScrollBarGetValues(VertScrollChild(mw), &value,
328 			     &slider_size, &increment, &page_increment);
329 	XmScrollBarSetValues(VertScrollChild(mw), vert_value,
330 			     slider_size, increment, page_increment, True);
331     }
332 }
333 
334 /*
335  * Scroll a column so it is visible on the screen.
336  */
337 void
xbaeMakeColumnVisible(mw,column)338 xbaeMakeColumnVisible(mw, column)
339 XbaeMatrixWidget mw;
340 int column;
341 {
342     int value, slider_size, increment, page_increment, x, horiz_value;
343 
344     /*
345      * If we are in a fixed column, we are already visible.
346      */
347     if (IS_FIXED_COLUMN(mw, column))
348 	return;
349 
350     /*
351      * Calculate the x position of this column
352      */
353     x = COLUMN_POSITION(mw, column) -
354 	COLUMN_POSITION(mw, mw->matrix.fixed_columns);
355 
356     /*
357      * Figure out the new value of the HSB to scroll this cell
358      * onto the screen. If the whole cell won't fit, scroll so its
359      * left edge is visible.
360      */
361     if (x < HORIZ_ORIGIN(mw))
362 	horiz_value = x;
363     else if (x + COLUMN_WIDTH(mw, column) >
364 	     VISIBLE_WIDTH(mw) + HORIZ_ORIGIN(mw))
365     {
366 	int off = (x + COLUMN_WIDTH(mw, column)) - (VISIBLE_WIDTH(mw) +
367 						    HORIZ_ORIGIN(mw));
368 
369 	if (x - off < HORIZ_ORIGIN(mw))
370 	    horiz_value = x;
371 	else
372 	    horiz_value = HORIZ_ORIGIN(mw) + off;
373     }
374     else
375 	horiz_value = HORIZ_ORIGIN(mw);
376 
377     /*
378      * Give the HSB the new value and pass a flag to make it
379      * call our scroll callbacks
380      */
381     if (horiz_value != HORIZ_ORIGIN(mw))
382     {
383 	XmScrollBarGetValues(HorizScrollChild(mw), &value,
384 			     &slider_size, &increment, &page_increment);
385 	XmScrollBarSetValues(HorizScrollChild(mw), horiz_value,
386 			     slider_size, increment, page_increment, True);
387     }
388 }
389 
390 /*
391  * Scrolls a fixed or non-fixed cell so it is visible on the screen.
392  */
393 void
xbaeMakeCellVisible(mw,row,column)394 xbaeMakeCellVisible(mw, row, column)
395 XbaeMatrixWidget mw;
396 int row, column;
397 {
398     if (!xbaeIsRowVisible(mw, row))
399 	xbaeMakeRowVisible(mw, row);
400     if (!xbaeIsColumnVisible(mw, column))
401 	xbaeMakeColumnVisible(mw, column);
402 }
403 
404 /*
405  * Set the clip_mask in our draw and shadow GCs.  This is necessary for
406  * drawing non-fixed column labels and fixed rows.
407  */
408 void
xbaeSetClipMask(mw,clip_reason)409 xbaeSetClipMask(mw, clip_reason)
410 XbaeMatrixWidget mw;
411 unsigned int clip_reason;
412 {
413     XRectangle r[2];
414     int n = 1;
415     /*
416      * Set new clip reason
417      */
418     mw->matrix.current_clip = clip_reason;
419 
420     /*
421      * XRectangle enclosing column labels and fixed rows
422      */
423     if ((CLIP_FIXED_COLUMNS & clip_reason) && mw->matrix.fixed_columns)
424     {
425 	r[0].x = COLUMN_LABEL_OFFSET(mw);
426 	r[0].width = FIXED_COLUMN_WIDTH(mw);
427     }
428     else if ((CLIP_TRAILING_FIXED_COLUMNS & clip_reason) &&
429 	     (mw->matrix.trailing_fixed_columns || mw->matrix.fill))
430     {
431 	r[0].x = TRAILING_FIXED_COLUMN_LABEL_OFFSET(mw);
432 	r[0].width = TRAILING_FIXED_COLUMN_WIDTH(mw);
433 	if (NEED_HORIZ_FILL(mw))
434 	    r[0].width += FILL_HORIZ_WIDTH(mw);
435     }
436     else
437     {
438 	r[0].x = FIXED_COLUMN_LABEL_OFFSET(mw);
439 	r[0].width = ClipChild(mw)->core.width;
440 	if (NEED_HORIZ_FILL(mw))
441 	    r[0].width += FILL_HORIZ_WIDTH(mw);
442     }
443 
444     if (CLIP_VISIBLE_HEIGHT & clip_reason)
445     {
446 	r[0].y = ROW_LABEL_OFFSET(mw);
447 	r[0].height = ClipChild(mw)->core.height +
448 	    FIXED_ROW_HEIGHT(mw) + TRAILING_FIXED_ROW_HEIGHT(mw);
449 	if (NEED_VERT_FILL(mw))
450   	    r[0].height += FILL_VERT_HEIGHT(mw);
451     }
452     else if ((CLIP_TRAILING_FIXED_ROWS & clip_reason) &&
453 	     (mw->matrix.trailing_fixed_rows || mw->matrix.fill))
454     {
455  	r[0].y = TRAILING_FIXED_ROW_LABEL_OFFSET(mw);
456  	r[0].height = TRAILING_FIXED_ROW_HEIGHT(mw);
457   	if (NEED_VERT_FILL(mw))
458   	    r[0].height += FILL_VERT_HEIGHT(mw);
459     }
460     else if (CLIP_BETWEEN_FIXED_ROWS & clip_reason)
461     {
462 	r[0].y = FIXED_ROW_LABEL_OFFSET(mw);
463 	r[0].height = ClipChild(mw)->core.height;
464     }
465     else    /* clip fixed rows & clip_reason */
466     {
467 	r[0].y = HORIZ_SB_OFFSET(mw);
468 	r[0].height = FIXED_ROW_LABEL_OFFSET(mw);
469     }
470     if (mw->matrix.row_labels)
471     {
472 	r[1].x = VERT_SB_OFFSET(mw);
473 	r[1].y = FIXED_ROW_LABEL_OFFSET(mw);
474 	r[1].width = ROW_LABEL_WIDTH(mw);
475 	r[1].height = ClipChild(mw)->core.height;
476 	n = 2;
477     }
478     /*
479      * Reset the clip_mask in our clipping GCs
480      */
481     XSetClipRectangles(XtDisplay(mw), mw->matrix.cell_grid_line_gc,
482 		       0, 0, r, n, Unsorted);
483     XSetClipRectangles(XtDisplay(mw), mw->matrix.cell_top_shadow_clip_gc,
484 		       0, 0, r, n, Unsorted);
485     XSetClipRectangles(XtDisplay(mw), mw->matrix.cell_bottom_shadow_clip_gc,
486 		       0, 0, r, n, Unsorted);
487     XSetClipRectangles(XtDisplay(mw), mw->matrix.label_clip_gc,
488 		       0, 0, r, n, Unsorted);
489 }
490 
491 
492 /*
493  * Get the total pixel width of the non-fixed cell area
494  */
495 void
xbaeGetCellTotalWidth(mw)496 xbaeGetCellTotalWidth(mw)
497 XbaeMatrixWidget mw;
498 {
499     int i, columns;
500 
501     /*
502      * Calculate width of non-fixed cell area.
503      */
504     columns = TRAILING_HORIZ_ORIGIN(mw);
505     for (i = mw->matrix.fixed_columns, mw->matrix.non_fixed_total_width = 0;
506 	 i < columns;
507 	 i++)
508 	mw->matrix.non_fixed_total_width += COLUMN_WIDTH(mw, i);
509 }
510 
511 /*
512  * Cache the pixel position of each column
513  */
514 void
xbaeGetColumnPositions(mw)515 xbaeGetColumnPositions(mw)
516 XbaeMatrixWidget mw;
517 {
518     int i, x;
519 
520     for (i = 0, x = 0;
521 	 i < mw->matrix.columns;
522 	 x += COLUMN_WIDTH(mw, i), i++)
523 	COLUMN_POSITION(mw, i) = x;
524 }
525 
526 void
527 #if NeedFunctionPrototypes
xbaeComputeSize(XbaeMatrixWidget mw,Boolean compute_width,Boolean compute_height)528 xbaeComputeSize(XbaeMatrixWidget mw, Boolean compute_width,
529 		Boolean compute_height)
530 #else
531 xbaeComputeSize(mw, compute_width, compute_height)
532 XbaeMatrixWidget mw;
533 Boolean compute_width;
534 Boolean compute_height;
535 #endif
536 {
537     unsigned long full_width = NON_FIXED_TOTAL_WIDTH(mw) +
538 	FIXED_COLUMN_WIDTH(mw) + TRAILING_FIXED_COLUMN_WIDTH(mw) +
539 	ROW_LABEL_WIDTH(mw) + 2 * mw->manager.shadow_thickness;
540     unsigned long full_height = CELL_TOTAL_HEIGHT(mw) +
541 	FIXED_ROW_HEIGHT(mw) + TRAILING_FIXED_ROW_HEIGHT(mw) +
542 	COLUMN_LABEL_HEIGHT(mw) + 2 * mw->manager.shadow_thickness;
543     unsigned long width, height;
544 
545     /*
546      * Calculate our width.
547      * If visible_columns is set, then base it on that.
548      * Otherwise, if the compute_width flag is set, then we are full width.
549      * Otherwise we keep whatever width we are.
550      */
551     if (mw->matrix.visible_columns)
552 	width = ROW_LABEL_WIDTH(mw) + 2 * mw->manager.shadow_thickness +
553 	    COLUMN_WIDTH(mw, (mw->matrix.visible_columns +
554 			      mw->matrix.fixed_columns) - 1) +
555 	    COLUMN_POSITION(mw, mw->matrix.fixed_columns +
556 			    mw->matrix.visible_columns - 1) +
557 	    TRAILING_FIXED_COLUMN_WIDTH(mw);
558     else if (compute_width)
559 	width = full_width;
560     else
561 	width = mw->core.width;
562 
563     /*
564      * Calculate our height.
565      * If visible_rows is set, then base it on that.
566      * Otherwise, if the compute_height flag is set, then we are full height.
567      * Otherwise we keep whatever height we are.
568      */
569     if (mw->matrix.visible_rows)
570 	height = mw->matrix.visible_rows * ROW_HEIGHT(mw) +
571 	    TRAILING_FIXED_ROW_HEIGHT(mw) + FIXED_ROW_HEIGHT(mw) +
572 	    COLUMN_LABEL_HEIGHT(mw) + 2 * mw->manager.shadow_thickness;
573     else if (compute_height)
574 	height = full_height;
575     else
576 	height = mw->core.height;
577 
578     /*
579      * Store our calculated size.
580      */
581     mw->core.width = width;
582     mw->core.height = height;
583 
584     /*
585      * If we are less than full width or our horizontal display policy is
586      * constant, then we need an HSB, so increment our height by the size
587      * of the HSB (if we are allowed to modify our height and we are allowed
588      * to have an HSB).
589      */
590     if (((width < full_width) ||
591 	 (XmDISPLAY_STATIC == mw->matrix.hsb_display_policy)) &&
592 	(compute_height || mw->matrix.visible_rows) &&
593 	(XmDISPLAY_NONE != mw->matrix.hsb_display_policy))
594 	mw->core.height += HORIZ_SB_HEIGHT(mw);
595 
596     /*
597      * If we are less than full height or our vertical display policy is
598      * constant, then we need a VSB, so increment our width by the size
599      * of the VSB (if we are allowed to modify our width and we are allowed
600      * to have a VSB).
601      */
602     if (((height < full_height) ||
603 	 (XmDISPLAY_STATIC == mw->matrix.vsb_display_policy)) &&
604 	(compute_width || mw->matrix.visible_columns) &&
605 	(XmDISPLAY_NONE != mw->matrix.vsb_display_policy))
606 	mw->core.width += VERT_SB_WIDTH(mw);
607 
608     /*
609      * Save our calculated size for use in our query_geometry method.
610      * This is the size we really want to be (not necessarily the size
611      * we will end up being).
612      */
613     mw->matrix.desired_width = mw->core.width;
614     mw->matrix.desired_height = mw->core.height;
615 }
616 
617 /*
618  * Return the length of the longest row label
619  */
620 short
xbaeMaxRowLabel(mw)621 xbaeMaxRowLabel(mw)
622 XbaeMatrixWidget mw;
623 {
624     int i;
625     short max = 0, len;
626 
627     /*
628      * Determine the length of the longest row label
629      */
630     for (i = 0; i < mw->matrix.rows; i++)
631     {
632 	len = strlen(mw->matrix.row_labels[i]);
633 	if (len > max)
634 	    max = len;
635     }
636     return max;
637 }
638 
639 void
xbaeParseColumnLabel(label,lines)640 xbaeParseColumnLabel(label, lines)
641 String label;
642 ColumnLabelLines lines;
643 {
644     char *nl;
645 
646     /*
647      * First count the number of lines in the label
648      */
649     lines->lines = 1;
650     nl = label;
651     while ((nl = strchr(nl, '\n')) != NULL)
652     {
653 	nl++;
654 	lines->lines++;
655     }
656 
657     /*
658      * Now malloc a lengths array of the correct size.
659      */
660     lines->lengths = (int *) XtMalloc(lines->lines * sizeof(int));
661 
662     /*
663      * An entry in the lengths array is the length of that line (substring).
664      */
665 
666     /*
667      * Handle the case of one line (no strchr() needed)
668      */
669     if (lines->lines == 1)
670 	lines->lengths[0] = strlen(label);
671     else
672     {
673 	int i;
674 
675 	nl = label;
676 	i = 0;
677 	while ((nl = strchr(nl, '\n')) != NULL)
678 	{
679 	    lines->lengths[i] = nl - label;
680 	    nl++;
681 	    label = nl;
682 	    i++;
683 	}
684 	lines->lengths[i] = strlen(label);
685     }
686 }
687 
688 /*
689  * Convert an x/y pixel position to the row/column cell position it picks.
690  * 'cell' specifies whether the x/y coord is relative to the fixed cells
691  * window or the non-fixed cells window.
692  * The coords x,y are adjusted so they are relative to the origin of the
693  * picked cell.
694  * If we are "out of bounds" on the ``low'' side, go ahead and return False
695  * to show an error, but also set the row/column to -1 to indicate this could
696  * be a row/column header.  This is currently undocumented behaviour, but
697  * may be useful nevertheless.
698  */
699 Boolean
xbaeXYToRowCol(mw,x,y,row,column,cell)700 xbaeXYToRowCol(mw, x, y, row, column, cell)
701 XbaeMatrixWidget mw;
702 int *x, *y;
703 int *row, *column;
704 CellType cell;
705 {
706     Rectangle rect;
707     unsigned int inBox = CLIP_NONE;
708     Boolean need_vert_dead_space_fill = NEED_VERT_DEAD_SPACE_FILL(mw);
709     int horiz_sb_offset = HORIZ_SB_OFFSET(mw);
710     int vert_sb_offset = VERT_SB_OFFSET(mw);
711     int column_label_height = COLUMN_LABEL_HEIGHT(mw);
712     int row_label_offset = ROW_LABEL_OFFSET(mw);
713     int row_label_width = ROW_LABEL_WIDTH(mw);
714     int fixed_row_label_offset = FIXED_ROW_LABEL_OFFSET(mw);
715     int trailing_fixed_row_label_offset = TRAILING_FIXED_ROW_LABEL_OFFSET(mw);
716     int trailing_fixed_row_height = TRAILING_FIXED_ROW_HEIGHT(mw);
717     int fixed_column_label_offset = FIXED_COLUMN_LABEL_OFFSET(mw);
718     int trailing_fixed_column_label_offset =
719 	TRAILING_FIXED_COLUMN_LABEL_OFFSET(mw);
720     int trailing_fixed_column_width = TRAILING_FIXED_COLUMN_WIDTH(mw);
721     int column_label_offset = COLUMN_LABEL_OFFSET(mw);
722     int row_height = ROW_HEIGHT(mw);
723     int unattached_trailing_rows_offset = UNATTACHED_TRAILING_ROWS_OFFSET(mw);
724 
725     *row = -1;
726     *column = -1;
727 
728     switch (cell)
729     {
730     case FixedCell:
731 	/*
732 	 * Check the various rectangles enclosing the cells in fixed rows
733 	 * or columns.
734 	 *
735 	 * If we don't have fixed rows or columns, then we didn't hit a cell.
736 	 */
737 
738 	/*
739 	 * Upper left
740 	 */
741 	if (!inBox && mw->matrix.fixed_columns && mw->matrix.fixed_rows)
742 	{
743 	    SETRECT(rect,
744 		    column_label_offset, row_label_offset,
745 		    fixed_column_label_offset - 1,
746 		    fixed_row_label_offset - 1);
747 
748 	    if (INBOX(rect, *x, *y)) inBox = CLIP_FIXED_COLUMNS |
749 					 CLIP_FIXED_ROWS;
750 	}
751 
752 	/*
753 	 * Lower left
754 	 */
755 	if (!inBox && mw->matrix.fixed_columns &&
756 	    mw->matrix.trailing_fixed_rows)
757 	{
758 	    SETRECT(rect,
759 		    column_label_offset,
760 		    trailing_fixed_row_label_offset,
761 		    fixed_column_label_offset - 1,
762 		    trailing_fixed_row_label_offset +
763 		    trailing_fixed_row_height - 1);
764 
765 	    if (INBOX(rect, *x, *y)) inBox = CLIP_FIXED_COLUMNS |
766 					 CLIP_TRAILING_FIXED_ROWS;
767 	}
768 
769 	/*
770 	 * Upper right
771 	 */
772 	if (!inBox && mw->matrix.trailing_fixed_columns &&
773 	    mw->matrix.fixed_rows)
774 	{
775 	    SETRECT(rect,
776 		    trailing_fixed_column_label_offset,
777 		    row_label_offset,
778 		    trailing_fixed_column_label_offset +
779 		    trailing_fixed_column_width - 1,
780 		    fixed_row_label_offset - 1);
781 
782 	    if (INBOX(rect, *x, *y)) inBox = CLIP_TRAILING_FIXED_COLUMNS |
783 					 CLIP_FIXED_ROWS;
784 	}
785 
786 	/*
787 	 * Lower right
788 	 */
789 	if (!inBox && mw->matrix.trailing_fixed_columns &&
790 	    mw->matrix.trailing_fixed_rows)
791 	{
792 	    SETRECT(rect,
793 		    trailing_fixed_column_label_offset,
794 		    trailing_fixed_row_label_offset,
795 		    trailing_fixed_column_label_offset +
796 		    trailing_fixed_column_width - 1,
797 		    trailing_fixed_row_label_offset +
798 		    trailing_fixed_row_height - 1);
799 
800 	    if (INBOX(rect, *x, *y)) inBox = CLIP_TRAILING_FIXED_COLUMNS |
801 					 CLIP_TRAILING_FIXED_ROWS;
802 	}
803 
804 	/*
805 	 * Right (mid)
806 	 */
807 	if (!inBox && mw->matrix.fixed_columns)
808 	{
809 	    SETRECT(rect,
810 		    column_label_offset, fixed_row_label_offset,
811 		    fixed_column_label_offset - 1,
812 		    need_vert_dead_space_fill ?
813 		    unattached_trailing_rows_offset :
814 		    trailing_fixed_row_label_offset - 1);
815 
816 	    if (INBOX(rect, *x, *y)) inBox = CLIP_FIXED_COLUMNS;
817 	}
818 
819 	/*
820 	 * Upper (mid)
821 	 */
822 	if (!inBox && mw->matrix.fixed_rows)
823 	{
824 	    SETRECT(rect,
825 		    fixed_column_label_offset, row_label_offset,
826 		    trailing_fixed_column_label_offset - 1,
827 		    fixed_row_label_offset - 1);
828 
829 	    if (INBOX(rect, *x, *y)) inBox = CLIP_FIXED_ROWS;
830 	}
831 
832 	/*
833 	 * Left (mid)
834 	 */
835 	if (!inBox && mw->matrix.trailing_fixed_columns)
836 	{
837 	    SETRECT(rect,
838 		    trailing_fixed_column_label_offset,
839 		    fixed_row_label_offset,
840 		    trailing_fixed_column_label_offset +
841 		    trailing_fixed_column_width - 1,
842 		    need_vert_dead_space_fill ?
843 		    unattached_trailing_rows_offset :
844 		    trailing_fixed_row_label_offset - 1);
845 
846 	    if (INBOX(rect, *x, *y)) inBox = CLIP_TRAILING_FIXED_COLUMNS;
847 	}
848 
849 	/*
850 	 * Lower (mid)
851 	 */
852 	if (!inBox && mw->matrix.trailing_fixed_rows)
853 	{
854 	    SETRECT(rect,
855 		    fixed_column_label_offset,
856 		    trailing_fixed_row_label_offset,
857 		    trailing_fixed_column_label_offset - 1,
858 		    trailing_fixed_row_label_offset +
859 		    trailing_fixed_row_height - 1);
860 
861 	    if (INBOX(rect, *x, *y)) inBox = CLIP_TRAILING_FIXED_ROWS;
862 	}
863 
864 	/*
865 	 * Trailing horizontal fill. This is trickier because
866 	 * even though we're in the fixed cell section, this really
867 	 * might not be a fixed cell.
868 	 */
869 	if (!inBox && IN_GRID_ROW_MODE(mw) && NEED_HORIZ_FILL(mw))
870 	{
871 	    int rx = trailing_fixed_column_label_offset +
872 		trailing_fixed_column_width;
873 
874 	    /*
875 	     * Upper fill
876 	     */
877 	    if (!inBox && mw->matrix.fixed_rows)
878 	    {
879 		SETRECT(rect,
880 			rx, row_label_offset,
881 			rx + FILL_HORIZ_WIDTH(mw) - 1,
882 			fixed_row_label_offset - 1);
883 
884 		if (INBOX(rect, *x, *y)) inBox = CLIP_TRAILING_HORIZ_FILL |
885 					     CLIP_FIXED_ROWS;
886 	    }
887 
888 	    /*
889 	     * Lower fill
890 	     */
891 	    if (!inBox && mw->matrix.trailing_fixed_rows)
892 	    {
893 		SETRECT(rect,
894 			rx, trailing_fixed_row_label_offset,
895 			rx + FILL_HORIZ_WIDTH(mw) - 1,
896 			trailing_fixed_row_label_offset +
897 			trailing_fixed_row_height - 1);
898 
899 		if (INBOX(rect, *x, *y)) inBox = CLIP_TRAILING_HORIZ_FILL |
900 					     CLIP_TRAILING_FIXED_ROWS;
901 	    }
902 
903 	    /*
904 	     * Mid fill
905 	     */
906 	    if (!inBox)
907 	    {
908 		SETRECT(rect,
909 			rx, row_label_offset,
910 			rx + FILL_HORIZ_WIDTH(mw) - 1,
911 			trailing_fixed_row_label_offset +
912 			trailing_fixed_row_height - 1);
913 
914 		if (INBOX(rect, *x, *y)) inBox = CLIP_TRAILING_HORIZ_FILL;
915 	    }
916 	}
917 
918 	/*
919 	 * Trailing vertical fill
920 	 */
921 	if (!inBox && IN_GRID_COLUMN_MODE(mw) && NEED_VERT_FILL(mw))
922 	{
923 	    int ry = trailing_fixed_row_label_offset +
924 		trailing_fixed_row_height;
925 
926 	    /*
927 	     * Left fill
928 	     */
929 	    if (mw->matrix.fixed_columns)
930 	    {
931 		SETRECT(rect,
932 			column_label_offset, ry,
933 			fixed_column_label_offset - 1,
934 			ry + FILL_VERT_HEIGHT(mw) - 1);
935 
936 		if (INBOX(rect, *x, *y)) inBox = CLIP_TRAILING_VERT_FILL |
937 					     CLIP_FIXED_COLUMNS;
938 	    }
939 
940 	    /*
941 	     * Right fill
942 	     */
943 	    if (!inBox && mw->matrix.trailing_fixed_columns)
944 	    {
945 		SETRECT(rect,
946 			trailing_fixed_column_label_offset, ry,
947 			trailing_fixed_column_label_offset +
948 			trailing_fixed_column_width - 1,
949 			ry + FILL_VERT_HEIGHT(mw) - 1);
950 
951 		if (INBOX(rect, *x, *y)) inBox = CLIP_TRAILING_VERT_FILL |
952 					     CLIP_TRAILING_FIXED_COLUMNS;
953 	    }
954 
955 	    /*
956 	     * Mid fill
957 	     */
958 	    if (!inBox)
959 	    {
960 		SETRECT(rect,
961 			column_label_offset, ry,
962 			trailing_fixed_column_label_offset +
963 			trailing_fixed_column_width - 1,
964 			ry + FILL_VERT_HEIGHT(mw) - 1);
965 
966 		if (INBOX(rect, *x, *y)) inBox = CLIP_TRAILING_VERT_FILL;
967 	    }
968 	}
969 
970 	/*
971 	 * If the point is in this rectangle, calculate the row/column
972 	 * it hits. Otherwise we didn't hit a cell.
973 	 */
974 	if (inBox)
975 	{
976 	    /*
977 	     * Translate the point to rect's coord system
978 	     */
979 	    if (mw->matrix.fixed_columns &&
980 		(CLIP_TRAILING_FIXED_ROWS == inBox ||
981 		 CLIP_FIXED_ROWS == inBox))
982 		*x -= column_label_offset;
983 	    else
984 		*x -= rect.x1;
985 	    *y -= rect.y1;
986 
987 	    /*
988 	     * Convert this point to a row/column.  We need to take into
989 	     * account the scrolling origins depending on which fixed
990 	     * areas the point is located in.
991 	     */
992 	    if (CLIP_TRAILING_VERT_FILL & inBox)
993 	    {
994 		*row = mw->matrix.rows - 1;
995 		*y += row_height;
996 	    }
997 	    else
998 		*row = YtoRow(mw, *y) +
999 		    ((((CLIP_FIXED_COLUMNS & inBox) ||
1000 		       (CLIP_TRAILING_FIXED_COLUMNS & inBox))) &&
1001 		     !((CLIP_FIXED_ROWS & inBox) ||
1002 		       (CLIP_TRAILING_FIXED_ROWS & inBox))
1003 		     ? mw->matrix.fixed_rows : 0) +
1004 		    ((CLIP_FIXED_ROWS & inBox) ? 0 : VERT_ORIGIN(mw)) +
1005 		    ((CLIP_TRAILING_FIXED_ROWS & inBox) ?
1006 		     TRAILING_VERT_ORIGIN(mw) - VERT_ORIGIN(mw) : 0);
1007 
1008 	    if ((CLIP_TRAILING_FIXED_COLUMNS & inBox) ||
1009 		(CLIP_TRAILING_HORIZ_FILL & inBox))
1010 		*column = xbaeXtoTrailingCol(mw, *x);
1011 	    else
1012 		*column = xbaeXtoCol(
1013 		    mw, *x + ((CLIP_FIXED_COLUMNS & inBox) ? 0 :
1014 			      HORIZ_ORIGIN(mw)));
1015 
1016 	    /*
1017 	     * Sanity check the result, making sure it is in fixed location
1018 	     */
1019 	    if (((*row < 0) || (*column < 0)) ||
1020 		(((mw->matrix.fixed_rows &&
1021 		   (*row >= (int)mw->matrix.fixed_rows) &&
1022 		   (!mw->matrix.trailing_fixed_rows ||
1023 		    (mw->matrix.trailing_fixed_rows &&
1024 		     (*row < TRAILING_VERT_ORIGIN(mw))))) ||
1025 		  (!mw->matrix.fixed_rows &&
1026 		   mw->matrix.trailing_fixed_rows &&
1027 		   (*row < TRAILING_VERT_ORIGIN(mw)))) &&
1028 		 ((mw->matrix.fixed_columns &&
1029 		   (*column >= (int)mw->matrix.fixed_columns) &&
1030 		   (!mw->matrix.trailing_fixed_columns ||
1031 		    (mw->matrix.trailing_fixed_columns &&
1032 		     (*column < TRAILING_HORIZ_ORIGIN(mw))))) ||
1033 		  (!mw->matrix.fixed_columns &&
1034 		   mw->matrix.trailing_fixed_columns &&
1035 		   (*column < TRAILING_HORIZ_ORIGIN(mw))))))
1036 		return False;
1037 
1038 	    /*
1039 	     * Adjust x,y so they are relative to this cells origin.
1040 	     */
1041 	    if (CLIP_TRAILING_HORIZ_FILL & inBox)
1042 		*x += COLUMN_WIDTH(mw, *column);
1043 	    else
1044 		*x -= COLUMN_POSITION(mw, *column) -
1045 		    (mw->matrix.fixed_columns ||
1046 		     (CLIP_TRAILING_FIXED_COLUMNS & inBox) ?
1047 		     0 : HORIZ_ORIGIN(mw)) -
1048 		    ((CLIP_TRAILING_FIXED_COLUMNS & inBox) ?
1049 		     COLUMN_POSITION(mw, TRAILING_HORIZ_ORIGIN(mw)) : 0);
1050 	    if (!(CLIP_TRAILING_VERT_FILL & inBox))
1051 		*y %= row_height;
1052 
1053 	    return True;
1054 	}
1055 	else
1056 	    return False;
1057 
1058 	/* NOTREACHED */
1059 	break;
1060 
1061     case RowLabelCell:
1062 	if (!inBox && mw->matrix.row_labels)
1063 	{
1064 	    /* Check the various regions of the matrix to make sure we
1065 	       get the correct row label */
1066 	    if (mw->matrix.fixed_rows)
1067 	    {
1068 		SETRECT(rect,
1069 			vert_sb_offset, row_label_offset,
1070 			row_label_width + vert_sb_offset,
1071 			fixed_row_label_offset - 1);
1072 		if (INBOX(rect, *x, *y)) inBox = CLIP_ROW_LABELS |
1073 					     CLIP_FIXED_ROWS;
1074 	    }
1075 
1076 	    if (!inBox && mw->matrix.trailing_fixed_rows)
1077 	    {
1078 		SETRECT(rect,
1079 			vert_sb_offset,
1080 			trailing_fixed_row_label_offset,
1081 			row_label_width + vert_sb_offset,
1082 			trailing_fixed_row_label_offset +
1083 			trailing_fixed_row_height - 1);
1084 
1085 		if (INBOX(rect, *x, *y)) inBox = CLIP_ROW_LABELS |
1086 					     CLIP_TRAILING_FIXED_ROWS;
1087 	    }
1088 
1089 	    /* check the rows in the non fixed regions */
1090 	    if (!inBox)
1091 	    {
1092 		SETRECT(rect,
1093 			vert_sb_offset,
1094 			fixed_row_label_offset,
1095 			row_label_width + vert_sb_offset,
1096 			trailing_fixed_row_label_offset - 1);
1097 
1098 		if (INBOX(rect, *x, *y)) inBox = CLIP_ROW_LABELS;
1099 	    }
1100 	}
1101 
1102 	if (CLIP_ROW_LABELS & inBox)
1103 	{
1104 	    *y -= row_label_offset;
1105 	    *row = YtoRow(mw, *y);
1106 	    *row += (CLIP_FIXED_ROWS & inBox ? 0 : mw->matrix.top_row);
1107 	    *row += ((CLIP_TRAILING_FIXED_ROWS & inBox) ?
1108 		     (CELL_TOTAL_HEIGHT(mw) - VISIBLE_HEIGHT(mw)) /
1109 		     row_height - mw->matrix.top_row: 0);
1110 
1111 	    *column = -1;
1112 	}
1113 	else
1114 	{
1115 	    *row = -1;
1116 	    *column = -1;
1117 	}
1118 
1119 	/* Sanity check - make sure we don't over step the mark */
1120 	if (*row >= mw->matrix.rows || *column >= mw->matrix.columns)
1121 	{
1122 	    *row = -1;
1123 	    *column = -1;
1124 	}
1125 
1126 	return False;
1127 
1128 	/*NOTREACHED*/
1129 	break;
1130 
1131     case ColumnLabelCell:
1132 	if (!inBox && mw->matrix.column_labels)
1133 	{
1134 	    if (mw->matrix.fixed_columns)
1135 	    {
1136 		SETRECT(rect,
1137 			column_label_offset, horiz_sb_offset,
1138 			fixed_column_label_offset - 1,
1139 			column_label_height + horiz_sb_offset);
1140 
1141 		if (INBOX(rect, *x, *y)) inBox = CLIP_FIXED_COLUMNS |
1142 					     CLIP_COLUMN_LABELS;
1143 	    }
1144 	    if (!inBox && mw->matrix.trailing_fixed_columns)
1145 	    {
1146 		SETRECT(rect,
1147 			trailing_fixed_column_label_offset,
1148 			horiz_sb_offset,
1149 			trailing_fixed_column_label_offset +
1150 			trailing_fixed_column_width - 1,
1151 			column_label_height + horiz_sb_offset);
1152 
1153 		if (INBOX(rect, *x, *y)) inBox = CLIP_COLUMN_LABELS |
1154 					     CLIP_TRAILING_FIXED_COLUMNS;
1155 	    }
1156 	    /* check the columns in the non fixed regions */
1157 	    if (!inBox)
1158 	    {
1159 		SETRECT(rect,
1160 			fixed_column_label_offset, horiz_sb_offset,
1161 			trailing_fixed_column_label_offset,
1162 			column_label_height + horiz_sb_offset);
1163 
1164 		if (INBOX(rect, *x, *y)) inBox = CLIP_COLUMN_LABELS;
1165 	    }
1166 	}
1167 
1168 	if (CLIP_COLUMN_LABELS & inBox)
1169 	{
1170 	    *x -= column_label_offset;
1171 
1172 	    if (CLIP_TRAILING_FIXED_COLUMNS & inBox)
1173 		*column = xbaeXtoTrailingCol(mw, *x);
1174 	    else
1175 		*column = xbaeXtoCol(mw, *x +
1176 				     ((CLIP_FIXED_COLUMNS & inBox) ? 0 :
1177 				      HORIZ_ORIGIN(mw)));
1178 	    *row = -1;
1179 	}
1180 	else
1181 	{
1182 	    *row = -1;
1183 	    *column = -1;
1184 	}
1185 	/* Sanity check - make sure we don't overstep the mark */
1186 	if (*row >= mw->matrix.rows || *column >= mw->matrix.columns)
1187 	{
1188 	    *row = -1;
1189 	    *column = -1;
1190 	}
1191 
1192 	return False;
1193 
1194 	/* NOTREACHED */
1195 	break;
1196 
1197     case NonFixedCell:
1198 
1199 	/*
1200 	 * Translate the point to take into account fixed rows or columns.
1201 	 */
1202 	*x += FIXED_COLUMN_WIDTH(mw);
1203 	*y += FIXED_ROW_HEIGHT(mw);
1204 
1205 	/*
1206 	 * Convert the new point to a row/column position
1207 	 */
1208 	*row = YtoRow(mw, *y + mw->matrix.first_row_offset) + VERT_ORIGIN(mw);
1209 	*column = xbaeXtoCol(mw, *x + HORIZ_ORIGIN(mw));
1210 
1211 	/*
1212 	 * Sanity check the result
1213 	 */
1214 	if (*row >= TRAILING_VERT_ORIGIN(mw) ||
1215 	    *column >= TRAILING_HORIZ_ORIGIN(mw) ||
1216 	    *row < 0 || *column < 0)
1217 	    return False;
1218 
1219 	/*
1220 	 * Adjust x,y so they are relative to this cell's origin.
1221 	 */
1222 	*x -= COLUMN_POSITION(mw, *column) - HORIZ_ORIGIN(mw);
1223 	*y %= row_height;
1224 
1225 	return True;
1226 
1227 	/* NOTREACHED */
1228 	break;
1229 
1230     default:
1231 	*row = -1;
1232 	*column = -1;
1233 	return False;
1234     }
1235 }
1236 
1237 /*
1238  * Convert the coordinates in an event to be relative to the Clip
1239  * window or the Matrix window.  Set the cell to indicate which one.
1240  * Used by some actions.
1241  */
1242 /* ARGSUSED */
1243 Boolean
xbaeEventToXY(mw,event,x,y,cell)1244 xbaeEventToXY(mw, event, x, y, cell)
1245 XbaeMatrixWidget mw;
1246 XEvent *event;
1247 int *x, *y;
1248 CellType *cell;
1249 {
1250     switch (event->type)
1251     {
1252     case ButtonPress:
1253     case ButtonRelease:
1254 	*x = event->xbutton.x;
1255 	*y = event->xbutton.y;
1256 	break;
1257     case KeyPress:
1258     case KeyRelease:
1259 	*x = event->xkey.x;
1260 	*y = event->xkey.y;
1261 	break;
1262     case MotionNotify:
1263 	*x = event->xmotion.x;
1264 	*y = event->xmotion.y;
1265 	break;
1266     default:
1267 	return False;
1268     }
1269 
1270     if (event->xbutton.subwindow == XtWindow(ClipChild(mw)))
1271     {
1272 	*cell = NonFixedCell;
1273 	*x -= FIXED_COLUMN_LABEL_OFFSET(mw);
1274 	*y -= FIXED_ROW_LABEL_OFFSET(mw);
1275     }
1276     else if (event->xbutton.window == XtWindow(mw))
1277 	if (*x < (int)COLUMN_LABEL_OFFSET(mw) &&
1278 	    *x > (int)VERT_SB_OFFSET(mw))
1279 	    *cell = RowLabelCell;
1280 	else if (*y < (int)ROW_LABEL_OFFSET(mw) &&
1281 		 *y > (int)HORIZ_SB_OFFSET(mw))
1282 	    *cell = ColumnLabelCell;
1283 	else
1284 	    *cell = FixedCell;
1285     else if (event->xbutton.window == XtWindow(ClipChild(mw)))
1286 	*cell = NonFixedCell;
1287     else if (event->xbutton.window == XtWindow(TextChild(mw)))
1288     {
1289 	Position tx, ty;
1290 
1291 	if (mw->matrix.current_parent == ClipChild(mw))
1292 	    *cell = NonFixedCell;
1293 	else if (mw->matrix.current_parent == (Widget)mw)
1294 	    *cell = FixedCell;
1295 	else			/* We're on one of the extra clips */
1296 	{
1297 	    *cell = FixedCell;
1298 	    *x += mw->matrix.current_parent->core.x;
1299 	    *y += mw->matrix.current_parent->core.y;
1300 	}
1301 
1302 	XtVaGetValues(TextChild(mw),
1303 		      XmNx, &tx,
1304 		      XmNy, &ty,
1305 		      NULL);
1306 	*x += tx;
1307 	*y += ty;
1308     }
1309     else
1310 	return False;
1311 
1312     return True;
1313 }
1314 
1315 /*
1316  * Convert a pixel position to the column it is contained in.
1317  */
1318 int
xbaeXtoCol(mw,x)1319 xbaeXtoCol(mw, x)
1320 XbaeMatrixWidget mw;
1321 int x;
1322 {
1323     int i;
1324 
1325     for (i = 0; i < mw->matrix.columns; i++)
1326 	if (COLUMN_POSITION(mw, i) > x)
1327 	    return i - 1;
1328     /*
1329      * I have seen cases where this function returned mw->matrix.columns
1330      * causing a crash as array bounds are read - AL
1331      */
1332     if (i > mw->matrix.columns)
1333 	return mw->matrix.columns - 1;
1334 
1335     return i - 1;
1336 }
1337 
1338 /*
1339  * Convert a pixel position to the trailing
1340  * fixed column it is contained in
1341  */
1342 int
xbaeXtoTrailingCol(mw,x)1343 xbaeXtoTrailingCol(mw, x)
1344 XbaeMatrixWidget mw;
1345 int x;
1346 {
1347     int i;
1348 
1349     x += COLUMN_POSITION(mw, TRAILING_HORIZ_ORIGIN(mw));
1350     for (i = TRAILING_HORIZ_ORIGIN(mw); i < mw->matrix.columns; i++)
1351     {
1352 	if (COLUMN_POSITION(mw, i) > x)
1353 	    return i - 1;
1354     }
1355 
1356     return i - 1;
1357 }
1358 
1359 /*
1360  * Convert a row/column cell position to the x/y of its upper left corner
1361  * wrt the window it will be drawn in (either the matrix window for
1362  * fixed cells, or the clip window for non-fixed).
1363  */
1364 void
xbaeRowColToXY(mw,row,column,x,y)1365 xbaeRowColToXY(mw, row, column, x, y)
1366 XbaeMatrixWidget mw;
1367 int row;
1368 int column;
1369 int *x;
1370 int *y;
1371 {
1372     /*
1373      * If we are in a fixed cell, calculate x/y relative to Matrixs
1374      * window (take into account labels etc)
1375      */
1376     if (IS_FIXED(mw, row, column))
1377     {
1378 	/*
1379 	 * Ignore horiz_origin if we are in a fixed column
1380 	 */
1381 	if (IS_LEADING_FIXED_COLUMN(mw, column))
1382 	{
1383 	    if (IS_FIXED_ROW(mw, row))
1384 		*x = COLUMN_LABEL_OFFSET(mw) + COLUMN_POSITION(mw, column);
1385 	    else
1386 		*x = COLUMN_POSITION(mw, column);	/* LeftClip */
1387 	}
1388 	else if (IS_TRAILING_FIXED_COLUMN(mw, column))
1389 	{
1390 	    int m;
1391 	    if (IS_FIXED_ROW(mw, row))
1392 		*x = TRAILING_FIXED_COLUMN_LABEL_OFFSET(mw);
1393 	    else
1394 		*x = 0;					/* RightClip */
1395 	    for (m = TRAILING_HORIZ_ORIGIN(mw); m < column; m++)
1396 		*x += COLUMN_WIDTH(mw, m);
1397 	}
1398 	else if (IS_FIXED_ROW(mw, row))
1399 	    *x = (COLUMN_POSITION(mw, column) -
1400 		  COLUMN_POSITION(mw, mw->matrix.fixed_columns)) -
1401 		HORIZ_ORIGIN(mw);
1402 	else
1403 	    *x = COLUMN_LABEL_OFFSET(mw) +
1404 		COLUMN_POSITION(mw, column) - HORIZ_ORIGIN(mw);
1405 
1406 	/*
1407 	 * Ignore vert_origin if we are in a fixed row
1408 	 */
1409 	if (IS_LEADING_FIXED_ROW(mw, row))
1410 	{
1411 	    if (IS_FIXED_COLUMN(mw, column))
1412 		*y = ROW_LABEL_OFFSET(mw) + ROW_HEIGHT(mw) * row;
1413 	    else
1414 		*y = ROW_HEIGHT(mw) * row;	/* TopClip */
1415 	}
1416 	else if (IS_TRAILING_FIXED_ROW(mw, row))
1417 	{
1418 	    int m;
1419 	    if (IS_FIXED_COLUMN(mw, column))
1420 		*y = TRAILING_FIXED_ROW_LABEL_OFFSET(mw);
1421 	    else
1422 		*y = 0;				/* BottomClip */
1423 	    for (m = TRAILING_VERT_ORIGIN(mw); m < row; m++)
1424 		*y += ROW_HEIGHT(mw);
1425 	}
1426 	else if (IS_FIXED_COLUMN(mw, column))
1427 
1428 	    *y = ROW_HEIGHT(mw) * ((row - (int)mw->matrix.fixed_rows) -
1429 				   VERT_ORIGIN(mw));
1430 	else
1431 	    *y = ROW_LABEL_OFFSET(mw) + ROW_HEIGHT(mw) *
1432 		(row - VERT_ORIGIN(mw));
1433     }
1434 
1435     /*
1436      * If we are not fixed we must account for fixed rows/columns
1437      * and scrolling origins.
1438      */
1439     else
1440     {
1441 	*x = (COLUMN_POSITION(mw, column) -
1442 	      COLUMN_POSITION(mw, mw->matrix.fixed_columns)) -
1443 	    HORIZ_ORIGIN(mw);
1444 	*y = ROW_HEIGHT(mw) * ((row - (int) mw->matrix.fixed_rows) -
1445 			       VERT_ORIGIN(mw));
1446 	*y -= mw->matrix.first_row_offset;
1447     }
1448 }
1449 
1450 #define FIXED_NONE	0
1451 #define FIXED_LEFT	1
1452 #define FIXED_RIGHT	2
1453 #define FIXED_TOP	4
1454 #define FIXED_BOTTOM	8
1455 #define FIXED_TOPLEFT	5
1456 #define FIXED_TOPRIGHT	6
1457 #define FIXED_BOTLEFT	9
1458 #define FIXED_BOTRIGHT	10
1459 /* Gotta love that numbering scheme! - AL */
1460 
1461 
1462 /*
1463  * Returns the window on which a cell is displayed, i.e. the matrix, the clip,
1464  * or one of the extra clips which handle the fixed row/col cells.
1465  */
1466 Window
xbaeGetCellWindow(mw,w,row,column)1467 xbaeGetCellWindow(mw, w, row, column)
1468 XbaeMatrixWidget mw;
1469 Widget *w;
1470 int row, column;
1471 {
1472     int posn;
1473     Window win;
1474 
1475     if (IS_LEADING_FIXED_ROW(mw, row))
1476 	posn = FIXED_TOP;
1477     else if (IS_TRAILING_FIXED_ROW(mw, row))
1478 	posn = FIXED_BOTTOM;
1479     else
1480 	posn = FIXED_NONE;
1481     if (IS_LEADING_FIXED_COLUMN(mw, column))
1482 	posn += FIXED_LEFT;
1483     else if (IS_TRAILING_FIXED_COLUMN(mw, column))
1484 	posn += FIXED_RIGHT;
1485     else
1486 	posn += FIXED_NONE;	/* add zero!? */
1487 
1488     switch(posn)
1489     {
1490     case FIXED_TOPLEFT:
1491     case FIXED_TOPRIGHT:
1492     case FIXED_BOTLEFT:
1493     case FIXED_BOTRIGHT:
1494 	/* total fixed cell - on parent matrix window */
1495 	*w = (Widget)mw;
1496 	win = XtWindow(mw);
1497 	break;
1498 
1499     case FIXED_NONE:
1500 	/* not fixed at all - on clip child */
1501 	*w = ClipChild(mw);
1502 	win = XtWindow(ClipChild(mw));
1503 	break;
1504 
1505     case FIXED_LEFT:
1506 	/* fixed col only - on left clip */
1507 	*w = LeftClip(mw);
1508 	win = XtWindow(LeftClip(mw));
1509 	break;
1510 
1511     case FIXED_RIGHT:
1512 	/* fixed trailing col only - on right clip */
1513 	win = XtWindow(RightClip(mw));
1514 	*w = RightClip(mw);
1515 	break;
1516 
1517     case FIXED_TOP:
1518 	/* fixed row only - on top clip */
1519 	win = XtWindow(TopClip(mw));
1520 	*w = TopClip(mw);
1521 	break;
1522 
1523     case FIXED_BOTTOM:
1524 	/* fixed trailing row only - on bottom clip */
1525 	win = XtWindow(BottomClip(mw));
1526 	*w = BottomClip(mw);
1527 	break;
1528     default:	/* bogus */
1529 	win = (Window)NULL;
1530 	*w = (Widget)NULL;
1531     }
1532     return win;
1533 }
1534 
1535 void
xbaeCalcVertFill(mw,win,x,y,row,column,ax,ay,width,height)1536 xbaeCalcVertFill(mw, win, x, y, row, column, ax, ay, width, height)
1537 XbaeMatrixWidget mw;
1538 Window win;
1539 int x;
1540 int y;
1541 int row;
1542 int column;
1543 int *ax;
1544 int *ay;
1545 int *width;
1546 int *height;
1547 {
1548     *ax = x;
1549     *width = COLUMN_WIDTH(mw, column);
1550 
1551     if (win == XtWindow(LeftClip(mw)))
1552     {
1553 	*ax += COLUMN_LABEL_OFFSET(mw);
1554 	*ay = LeftClip(mw)->core.y + LeftClip(mw)->core.height;
1555     }
1556     else if (win == XtWindow(RightClip(mw)))
1557     {
1558 	*ax += TRAILING_FIXED_COLUMN_LABEL_OFFSET(mw);
1559 	*ay = RightClip(mw)->core.y + RightClip(mw)->core.height;
1560     }
1561     else if (win == XtWindow(BottomClip(mw)))
1562     {
1563 	*ax += BottomClip(mw)->core.x;
1564 	*ay = BottomClip(mw)->core.y + BottomClip(mw)->core.height;
1565     }
1566     else if (win == XtWindow(ClipChild(mw)))
1567     {
1568 	if (XtIsManaged(LeftClip(mw)))
1569 	    *ax += FIXED_COLUMN_LABEL_OFFSET(mw);
1570 	else
1571 	    *ax += COLUMN_LABEL_OFFSET(mw);
1572 	*ay = ClipChild(mw)->core.y + ClipChild(mw)->core.height;
1573 	if (*ax < (int)COLUMN_LABEL_OFFSET(mw))
1574 	{
1575 	    *width += *ax - COLUMN_LABEL_OFFSET(mw);
1576 	    *ax = COLUMN_LABEL_OFFSET(mw);
1577 	}
1578     }
1579     else			/* must be in a corner */
1580 	*ay = y;
1581 
1582     *height = MATRIX_VERT_VISIBLE_SPACE(mw) + ROW_LABEL_OFFSET(mw) +
1583 	HORIZ_SB_OFFSET(mw) - *ay;
1584     /*
1585      * Unfortunately, on the filled area, we don't have the luxury
1586      * of the clip widgets to help us out with the edges of the area.
1587      * Check our width isn't going to draw outside the left or right clip
1588      */
1589 
1590     if (! IS_FIXED_COLUMN(mw, column))
1591     {
1592 	if (XtIsManaged(LeftClip(mw)) &&
1593 	    *ax < (int)FIXED_COLUMN_LABEL_OFFSET(mw))
1594 	{
1595 	    *width -= (FIXED_COLUMN_LABEL_OFFSET(mw) - *ax);
1596 	    *ax = FIXED_COLUMN_LABEL_OFFSET(mw);
1597 	}
1598 
1599 	if (XtIsManaged(RightClip(mw)) &&
1600 	    ((*ax + *width) > (int)RightClip(mw)->core.x))
1601 	    *width = RightClip(mw)->core.x - *ax;
1602 
1603 	if (win == XtWindow(BottomClip(mw)))
1604 	{
1605 	    if ((*ax + *width) >
1606 		 (int)(BottomClip(mw)->core.x + BottomClip(mw)->core.width))
1607 		*width = BottomClip(mw)->core.width +
1608 		    BottomClip(mw)->core.x - *ax;
1609 
1610 	    if (*ax < (int)COLUMN_LABEL_OFFSET(mw))
1611 	    {
1612 		*width += *ax - COLUMN_LABEL_OFFSET(mw);
1613 		*ax = COLUMN_LABEL_OFFSET(mw);
1614 	    }
1615 	}
1616 
1617 	if ((win == XtWindow(ClipChild(mw))) &&
1618 	    ((*ax + *width) >
1619 	      (int)(ClipChild(mw)->core.x + ClipChild(mw)->core.width)))
1620 	    *width = ClipChild(mw)->core.width +
1621 		ClipChild(mw)->core.x - *ax;
1622     }
1623 }
1624 
1625 void
xbaeCalcHorizFill(mw,win,x,y,row,column,ax,ay,width,height)1626 xbaeCalcHorizFill(mw, win, x, y, row, column, ax, ay, width, height)
1627 XbaeMatrixWidget mw;
1628 Window win;
1629 int x;
1630 int y;
1631 int row;
1632 int column;
1633 int *ax;
1634 int *ay;
1635 int *width;
1636 int *height;
1637 {
1638     *ay = y;
1639     *height = ROW_HEIGHT(mw);
1640 
1641     if (win == XtWindow(TopClip(mw)))
1642     {
1643 	*ax = TopClip(mw)->core.x + TopClip(mw)->core.width;
1644 	*ay += ROW_LABEL_OFFSET(mw);
1645     }
1646     else if (win == XtWindow(BottomClip(mw)))
1647     {
1648 	*ax = BottomClip(mw)->core.x + BottomClip(mw)->core.width;
1649 	*ay += TRAILING_FIXED_ROW_LABEL_OFFSET(mw);
1650     }
1651     else if (win == XtWindow(RightClip(mw)))
1652     {
1653 	*ax = RightClip(mw)->core.x + RightClip(mw)->core.width;
1654 	*ay += RightClip(mw)->core.y;
1655     }
1656     else if (win == XtWindow(ClipChild(mw)))
1657     {
1658 	if (XtIsManaged(TopClip(mw)))
1659 	    *ay += FIXED_ROW_LABEL_OFFSET(mw);
1660 	else
1661 	    *ay += ROW_LABEL_OFFSET(mw);
1662 	*ax = ClipChild(mw)->core.x + ClipChild(mw)->core.width;
1663 	if (*ay < (int)ROW_LABEL_OFFSET(mw))
1664 	    *ay = ROW_LABEL_OFFSET(mw);
1665     }
1666     else			/* must be in a corner */
1667 	*ax = x;
1668 
1669     *width = MATRIX_HORIZ_VISIBLE_SPACE(mw) + COLUMN_LABEL_OFFSET(mw) +
1670 	VERT_SB_OFFSET(mw) - *ax;
1671 
1672     /*
1673      * Unfortunately, on the filled area, we don't have the luxury
1674      * of the clip widgets to help us out with the edges of the area.
1675      * Check our width isn't going to draw outside the left or right clip,
1676      * or out past our matrix region.
1677      */
1678 
1679     if (! IS_FIXED_ROW(mw, row))
1680     {
1681 	if (XtIsManaged(LeftClip(mw)) &&
1682 	    (*ay < (int)FIXED_ROW_LABEL_OFFSET(mw)))
1683 	{
1684 	    *height -= (FIXED_ROW_LABEL_OFFSET(mw) - *ay);
1685 	    *ay = FIXED_ROW_LABEL_OFFSET(mw);
1686 	}
1687 
1688 	if (XtIsManaged(RightClip(mw)) &&
1689 	    ((*ay + *height) >
1690 	      (int)(RightClip(mw)->core.y + RightClip(mw)->core.height)))
1691 	    *height = RightClip(mw)->core.height +
1692 		RightClip(mw)->core.y - *ay;
1693 
1694 	if ((win == XtWindow(ClipChild(mw))) &&
1695 	    ((*ay + *height) >
1696 	      (int)(ClipChild(mw)->core.y + ClipChild(mw)->core.height)))
1697 	    *height = ClipChild(mw)->core.height +
1698 		ClipChild(mw)->core.y - *ay;
1699     }
1700 }
1701