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