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 * $Id: Draw.c,v 1.1 1999/09/11 01:25:37 fnevgeny Exp $
24 */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <Xm/Xm.h>
31 #include <Xm/XmP.h>
32 #if XmVersion > 1001
33 #include <Xm/DrawP.h>
34 #endif
35 #include <Xbae/MatrixP.h>
36 #include <Xbae/Utils.h>
37 #include <Xbae/Shadow.h>
38 #include <Xbae/Draw.h>
39 #include <stdlib.h>
40
41 static void xbaeDrawCellString P((XbaeMatrixWidget, int, int, int, int,
42 String, Pixel, Pixel));
43 #if CELL_WIDGETS
44 static void xbaeDrawCellWidget P((XbaeMatrixWidget, int, int, int, int,
45 Widget, Pixel, Pixel));
46 #endif
47 static void xbaeDrawCellPixmap P((XbaeMatrixWidget, int, int, int, int,
48 Pixmap, Pixmap, int, int, Pixel,
49 Pixel, int));
50
51 /*
52 * Draw a fixed or non-fixed cell. The coordinates are calculated relative
53 * to the correct window and pixmap is copied to that window.
54 */
55 static void
xbaeDrawCellPixmap(mw,row,column,x,y,pixmap,mask,width,height,bg,fg,depth)56 xbaeDrawCellPixmap(mw, row, column, x, y, pixmap, mask, width, height, bg,
57 fg, depth)
58 XbaeMatrixWidget mw;
59 int row;
60 int column;
61 int x;
62 int y;
63 Pixmap pixmap;
64 Pixmap mask;
65 int width;
66 int height;
67 Pixel bg;
68 Pixel fg;
69 int depth;
70 {
71 int src_x = 0, src_y, dest_x, dest_y;
72 int copy_width, copy_height;
73 int cell_height = ROW_HEIGHT(mw);
74 int cell_width = COLUMN_WIDTH(mw, column);
75 Widget w;
76 unsigned char alignment = mw->matrix.column_alignments ?
77 mw->matrix.column_alignments[column] : XmALIGNMENT_BEGINNING;
78 Display *display = XtDisplay(mw);
79 GC gc;
80 Window win = xbaeGetCellWindow(mw, &w, row, column);
81
82 if (!win)
83 return;
84
85 /*
86 * Convert the row/column to the coordinates relative to the correct
87 * window
88 */
89 dest_x = x + TEXT_WIDTH_OFFSET(mw);
90
91 gc = mw->matrix.pixmap_gc;
92
93 XSetForeground(display, gc, bg);
94
95 #if XmVersion >= 1002
96 /*
97 * If we are only changing the highlighting of a cell, we don't need
98 * to do anything other than draw (or undraw) the highlight
99 */
100 if (mw->matrix.highlighted_cells &&
101 mw->matrix.highlight_location != HighlightNone)
102 {
103 xbaeDrawCellHighlight(mw, win, gc, row, column, x, y, cell_width,
104 cell_height, mw->matrix.highlight_location);
105 return;
106 }
107 #endif
108
109 XFillRectangle(display, win, gc, x, y,
110 COLUMN_WIDTH(mw, column), ROW_HEIGHT(mw));
111
112 XSetForeground(display, gc, fg);
113 XSetBackground(display, gc, bg);
114
115 /*
116 * Adjust the x and y drawing destinations as appropriate. First the
117 * y value....
118 */
119 dest_y = y;
120 if (height > cell_height)
121 {
122 /* Adjust the starting location in the src image */
123 src_y = (height - cell_height) / 2;
124 copy_height = cell_height;
125 }
126 else
127 {
128 /* Adjust the destination point */
129 src_y = 0;
130 dest_y += ((cell_height - height) / 2);
131 copy_height = height;
132 }
133
134 /*
135 * Adjust the x value, paying attention to the columnAlignment
136 */
137 if (width > cell_width)
138 copy_width = cell_width;
139 else
140 copy_width = width;
141
142 switch (alignment)
143 {
144 case XmALIGNMENT_BEGINNING:
145 src_x = 0;
146 break;
147 case XmALIGNMENT_CENTER:
148 if (width > cell_width)
149 src_x = (width - cell_width) / 2;
150 else
151 {
152 src_x = 0;
153 dest_x += ((cell_width - width) / 2);
154 }
155 break;
156 case XmALIGNMENT_END:
157 if (width > cell_width)
158 src_x = width - cell_width;
159 else
160 {
161 src_x = 0;
162 dest_x = x + COLUMN_WIDTH(mw, column) - TEXT_WIDTH_OFFSET(mw) -
163 width;
164 }
165 break;
166 }
167
168 /*
169 * Draw the pixmap. Clip it, if necessary
170 */
171 if (pixmap)
172 {
173 if (depth > 1) /* A pixmap using xpm */
174 {
175 if (mask)
176 {
177 XSetClipMask(display, gc, mask);
178
179 XSetClipOrigin(display, gc, dest_x - src_x, dest_y - src_y);
180 }
181 XCopyArea(display, pixmap, win, gc, src_x, src_y, copy_width,
182 copy_height, dest_x, dest_y);
183 if (mask)
184 XSetClipMask(display, gc, None);
185 }
186 else /* A plain old bitmap */
187 XCopyPlane(display, pixmap, win, gc, src_x, src_y, copy_width,
188 copy_height, dest_x, dest_y, 1L);
189 }
190
191 /*
192 * If we need to fill the rest of the space, do so
193 */
194 if (IN_GRID_COLUMN_MODE(mw) && NEED_VERT_FILL(mw) &&
195 (row == (mw->matrix.rows - 1)))
196 {
197 int ax, ay;
198 int fill_width, fill_height;
199 /*
200 * Need to check the actual window we are drawing on to ensure
201 * the correct visual
202 */
203 xbaeCalcVertFill(mw, win, x, y, row, column, &ax, &ay,
204 &fill_width, &fill_height);
205 XFillRectangle(XtDisplay(mw), XtWindow(mw), gc,
206 ax, ay, fill_width, fill_height);
207 }
208 else if (IN_GRID_ROW_MODE(mw) && NEED_HORIZ_FILL(mw) &&
209 (column == (mw->matrix.columns - 1)))
210 {
211 int ax, ay;
212 int fill_width, fill_height;
213
214 xbaeCalcHorizFill(mw, win, x, y, row, column, &ax, &ay,
215 &fill_width, &fill_height);
216 XFillRectangle(XtDisplay(mw), XtWindow(mw), gc,
217 ax, ay, fill_width, fill_height);
218 }
219
220 #if XmVersion >= 1002
221 if (mw->matrix.highlighted_cells &&
222 mw->matrix.highlighted_cells[row][column])
223 {
224 xbaeDrawCellHighlight(mw, win, gc, row, column, x, y, cell_width,
225 cell_height, HIGHLIGHTING_SOMETHING);
226 }
227 #endif
228 xbaeDrawCellShadow(mw, win, row, column, x, y, cell_width,
229 cell_height, False, False, False);
230 }
231
232 /*
233 * Draw a fixed or non-fixed cell. The coordinates are calculated relative
234 * to the correct window and the cell is drawn in that window.
235 */
236 static void
xbaeDrawCellString(mw,row,column,x,y,string,bg,fg)237 xbaeDrawCellString(mw, row, column, x, y, string, bg, fg)
238 XbaeMatrixWidget mw;
239 int row, column;
240 String string;
241 Pixel bg, fg;
242 {
243 GC gc;
244 Widget w;
245 Window win = xbaeGetCellWindow(mw, &w, row, column);
246 Dimension column_width = COLUMN_WIDTH(mw, column);
247 Dimension row_height = ROW_HEIGHT(mw);
248 Dimension width = column_width;
249 Dimension height = row_height;
250 Boolean selected = mw->matrix.selected_cells ?
251 mw->matrix.selected_cells[row][column] : False;
252 String str = string;
253
254 if (!win)
255 return;
256
257 #if 0
258 /*
259 * Probably not needed - time will tell! If anybody gets a segv on
260 * ignoring this code, be sure to let me know - AL 11/96
261 *
262 * Make sure y coordinate is valid
263 */
264 if ((win == XtWindow(mw)) &&
265 ((y > (CLIP_VERT_VISIBLE_SPACE(mw) + ROW_LABEL_OFFSET(mw) - 1)) ||
266 (y < ROW_LABEL_OFFSET(mw))))
267 return;
268 #endif
269 gc = mw->matrix.draw_gc;
270 XSetForeground(XtDisplay(mw), gc, bg);
271
272 /*
273 * If we are only changing the highlighting of a cell, we don't need
274 * to do anything other than draw (or undraw) the highlight
275 */
276 if (mw->matrix.highlighted_cells &&
277 mw->matrix.highlight_location != HighlightNone)
278 {
279 xbaeDrawCellHighlight(mw, win, gc, row, column, x, y, width, height,
280 mw->matrix.highlight_location);
281 return;
282 }
283
284 /*
285 * Fill the cell's background if it can be done
286 * without duplicating work below
287 */
288 if ((XtWindow(mw) != win) ||
289 (!(IN_GRID_COLUMN_MODE(mw) && NEED_VERT_FILL(mw) &&
290 ((mw->matrix.rows - 1) == row)) &&
291 !(IN_GRID_ROW_MODE(mw) && NEED_HORIZ_FILL(mw) &&
292 ((mw->matrix.columns - 1) == column))))
293 XFillRectangle(XtDisplay(mw), win, gc, x, y,
294 column_width, row_height);
295
296 /*
297 * If we need to fill the rest of the space, do so
298 */
299 if (IN_GRID_COLUMN_MODE(mw) && NEED_VERT_FILL(mw) &&
300 ((mw->matrix.rows - 1) == row))
301 {
302 int ax, ay;
303 int fill_width, fill_height;
304 /*
305 * Need to check the actual window we are drawing on to ensure
306 * the correct visual
307 */
308 xbaeCalcVertFill(mw, win, x, y, row, column, &ax, &ay,
309 &fill_width, &fill_height);
310 XFillRectangle(XtDisplay(mw), XtWindow(mw), gc,
311 ax, ay, fill_width, fill_height);
312 }
313 else if (IN_GRID_ROW_MODE(mw) && NEED_HORIZ_FILL(mw) &&
314 (column == (mw->matrix.columns - 1)))
315 {
316 int ax, ay;
317 int fill_width, fill_height;
318
319 xbaeCalcHorizFill(mw, win, x, y, row, column, &ax, &ay,
320 &fill_width, &fill_height);
321 XFillRectangle(XtDisplay(mw), XtWindow(mw), gc,
322 ax, ay, fill_width, fill_height);
323 }
324
325 /*
326 * Draw the string in the cell.
327 */
328
329 xbaeDrawString(mw, win, gc, str, strlen(str),
330 x + TEXT_X_OFFSET(mw), y + TEXT_Y_OFFSET(mw),
331 mw->matrix.column_widths[column],
332 mw->matrix.column_alignments ?
333 mw->matrix.column_alignments[column] :
334 XmALIGNMENT_BEGINNING, selected,
335 False, False, False, fg);
336
337 #if XmVersion >= 1002
338 if (mw->matrix.highlighted_cells &&
339 mw->matrix.highlighted_cells[row][column])
340 {
341 xbaeDrawCellHighlight(mw, win, gc, row, column, x, y, width, height,
342 HIGHLIGHTING_SOMETHING);
343 }
344 #endif
345
346 xbaeDrawCellShadow(mw, win, row, column, x, y, COLUMN_WIDTH(mw, column),
347 ROW_HEIGHT(mw), False, False, False);
348 }
349 #if CELL_WIDGETS
350 /*
351 * Draw a user defined widget in the cell
352 */
353 static void
xbaeDrawCellWidget(mw,row,column,x,y,widget,bg,fg)354 xbaeDrawCellWidget(mw, row, column, x, y, widget, bg, fg)
355 XbaeMatrixWidget mw;
356 int row, column;
357 int x, y;
358 Widget widget;
359 Pixel bg, fg;
360 {
361 GC gc;
362 Widget w;
363 Window win = xbaeGetCellWindow(mw, &w, row, column);
364
365 if (!win)
366 return;
367
368 gc = mw->matrix.draw_gc;
369 XSetForeground(XtDisplay(mw), gc, bg);
370 XFillRectangle(XtDisplay(mw), win, gc, x, y,
371 COLUMN_WIDTH(mw, column), ROW_HEIGHT(mw));
372
373 /*
374 * Draw the widget in the cell.
375 */
376 XtMoveWidget(widget,
377 x + mw->matrix.cell_shadow_thickness +
378 mw->matrix.cell_highlight_thickness,
379 y + mw->matrix.cell_shadow_thickness +
380 mw->matrix.cell_highlight_thickness);
381
382 xbaeDrawCellShadow(mw, win, row, column, x, y, COLUMN_WIDTH(mw, column),
383 ROW_HEIGHT(mw), False, clipped, False);
384 }
385 #endif
386
387 /*
388 * Width in pixels of a character in a given font
389 */
390 #define charWidth(fs,c) ((fs)->per_char ? \
391 (fs)->per_char[((c) < (fs)->min_char_or_byte2 ? \
392 (fs)->default_char : \
393 (c) - \
394 (fs)->min_char_or_byte2)].width : \
395 (fs)->min_bounds.width)
396
397
398 /*
399 * Draw a string with specified attributes. We want to avoid having to
400 * use a GC clip_mask, so we clip by characters. This complicates the code.
401 */
402 void
403 #if NeedFunctionPrototypes
xbaeDrawString(XbaeMatrixWidget mw,Window win,GC gc,String string,int length,int x,int y,int maxlen,unsigned char alignment,Boolean highlight,Boolean bold,Boolean rowLabel,Boolean colLabel,Pixel color)404 xbaeDrawString(XbaeMatrixWidget mw, Window win, GC gc, String string,
405 int length, int x, int y, int maxlen, unsigned char alignment,
406 Boolean highlight, Boolean bold, Boolean rowLabel,
407 Boolean colLabel, Pixel color)
408 #else
409 xbaeDrawString(mw, win, gc, string, length, x, y, maxlen, alignment,
410 highlight, bold, rowLabel, colLabel, color)
411 XbaeMatrixWidget mw;
412 Window win;
413 GC gc;
414 String string;
415 int length;
416 int x;
417 int y;
418 int maxlen;
419 unsigned char alignment;
420 Boolean highlight;
421 Boolean bold;
422 Boolean rowLabel;
423 Boolean colLabel;
424 Pixel color;
425 #endif
426 {
427 int start, width, maxwidth;
428 XFontStruct *font_struct;
429 XFontSet font_set;
430 Boolean choppedStart = False;
431 Boolean choppedEnd = False;
432 XRectangle *ink_array = NULL;
433 XRectangle *logical_array = NULL;
434 int num_chars;
435 XRectangle overall_logical;
436
437 if (rowLabel || colLabel)
438 {
439 font_struct = mw->matrix.label_font_struct;
440 font_set = mw->matrix.label_font_set;
441 }
442 else
443 {
444 font_struct = mw->matrix.font_struct;
445 font_set = mw->matrix.font_set;
446 }
447 /*
448 * Initialize starting character in string
449 */
450 start = 0;
451
452 if (!rowLabel)
453 maxwidth = maxlen * FONT_WIDTH(mw);
454 else
455 maxwidth = maxlen * LABEL_WIDTH(mw);
456
457 if (font_set)
458 {
459 ink_array = (XRectangle*)XtMalloc(length * sizeof(XRectangle));
460 logical_array = (XRectangle*)XtMalloc(length * sizeof(XRectangle));
461
462 XmbTextPerCharExtents(font_set, string, length,
463 ink_array, logical_array, length, &num_chars,
464 NULL, &overall_logical);
465
466 /*
467 * If the width of the string is greater than the width of this cell,
468 * we need to clip. We don't want to use the server to clip because
469 * it is slow, so we truncate characters if we exceed a cells pixel
470 * width.
471 */
472 if (overall_logical.width > maxwidth)
473 {
474 switch (alignment)
475 {
476
477 case XmALIGNMENT_CENTER:
478 {
479 int startx;
480 int endx;
481 int i;
482 int end;
483
484 /*
485 * If we are going to draw arrows at both ends, allow for them.
486 */
487 if (mw->matrix.show_arrows)
488 {
489 maxwidth -= 2 * mw->matrix.font_width;
490 choppedStart = True;
491 choppedEnd = True;
492 }
493
494 /*
495 * Find limits of cell relative to the origin of the string.
496 */
497 startx = overall_logical.x + overall_logical.width / 2 -
498 maxwidth / 2;
499 endx = startx + maxwidth - 1;
500
501 /*
502 * Find the first character which fits into the cell.
503 */
504 for (i = 0; i < num_chars && logical_array[i].x < startx; ++i)
505 {
506 int cl = mblen(string + start, length);
507 start += cl;
508 length -= cl;
509 }
510
511 /*
512 * Find the last character which fits into the cell.
513 * At this point length represents the number of bytes
514 * between the end of the cell and the end of the full
515 * string. Note that the scan continues from above.
516 */
517 for (end = start; i < num_chars && (logical_array[i].x +
518 logical_array[i].width) <
519 endx; ++i)
520 {
521 int cl = mblen(string + end, length);
522 end += cl;
523 length -= cl;
524 }
525
526 /*
527 * Now reset length so that it represents the number of bytes
528 * in the string.
529 */
530 length = end - start;
531
532 break;
533 }
534
535 case XmALIGNMENT_END:
536 {
537 int startx;
538 int i;
539
540 /*
541 * We are going to an draw arrow at the end, allow for it.
542 */
543 if (mw->matrix.show_arrows)
544 {
545 maxwidth -= mw->matrix.font_width;
546 choppedEnd = True;
547 }
548
549 /*
550 * Find limits of cell relative to the origin of the string.
551 */
552 startx = overall_logical.x + overall_logical.width - maxwidth;
553
554 /*
555 * Find the first character which fits into the cell.
556 */
557 for (i = 0; i < num_chars && logical_array[i].x < startx; ++i)
558 {
559 int cl = mblen(string + start, length);
560 start += cl;
561 length -= cl;
562 }
563
564 break;
565 }
566
567 case XmALIGNMENT_BEGINNING:
568 default:
569 {
570 int endx;
571 int i;
572 int end;
573
574 /*
575 * We are going to an draw arrow at the start, allow for it.
576 */
577 if (mw->matrix.show_arrows)
578 {
579 maxwidth -= mw->matrix.font_width;
580 choppedStart = True;
581 }
582
583 /*
584 * Find limits of cell relative to the origin of the string.
585 */
586 endx = overall_logical.x + maxwidth - 1;
587
588 /*
589 * Find the last character which fits into the cell.
590 * At this point length represents the number of bytes
591 * between the end of the cell and the end of the full
592 * string.
593 */
594 for (i = 0, end = start;
595 i < num_chars && (logical_array[i].x +
596 logical_array[i].width) < endx; ++i)
597 {
598 int cl = mblen(string + end, length);
599 end += cl;
600 length -= cl;
601 choppedEnd = True;
602 }
603
604 /*
605 * Now reset length so that it represents the number of bytes
606 * in the string.
607 */
608 length = end - start;
609
610 break;
611 }
612 }
613
614 /*
615 * Having truncated string recalculate extents to find origin
616 */
617 XmbTextPerCharExtents(font_set, string, length,
618 ink_array, logical_array, length, &num_chars,
619 NULL, &overall_logical);
620 }
621 /*
622 * We fit inside our cell, so just compute the x of the start of
623 * our string
624 */
625 else
626 {
627 switch (alignment)
628 {
629
630 case XmALIGNMENT_CENTER:
631 x += maxwidth / 2 - overall_logical.width / 2;
632 break;
633
634 case XmALIGNMENT_END:
635 x += maxwidth - overall_logical.width;
636 break;
637
638 case XmALIGNMENT_BEGINNING:
639 default:
640 /*
641 * Leave x alone
642 */
643 break;
644 }
645 }
646
647 /*
648 * Don't worry, XSetForeground is smart about avoiding unnecessary
649 * protocol requests.
650 */
651 XSetForeground(XtDisplay(mw), gc, color);
652
653 if (mw->matrix.show_arrows && choppedStart)
654 {
655 XPoint points[ 3 ];
656 points[ 0 ].x = points[ 1 ].x = x + mw->matrix.font_width;
657 points[ 0 ].y = y + mw->matrix.font_y;
658 points[ 1 ].y = y + mw->matrix.font_y + mw->matrix.font_height;
659 points[ 2 ].x = x;
660 points[ 2 ].y = y + mw->matrix.font_y + mw->matrix.font_height / 2;
661
662 XFillPolygon(XtDisplay(mw), win, gc, points, 3,
663 Convex, CoordModeOrigin);
664
665 /* Offset the start point so as to not draw on the triangle */
666 x += FONT_WIDTH(mw);
667 }
668
669 if (mw->matrix.show_arrows && choppedEnd)
670 {
671 XPoint points[ 3 ];
672 points[ 0 ].x = points[ 1 ].x = x + overall_logical.width;
673 points[ 0 ].y = y + mw->matrix.font_y;
674 points[ 1 ].y = y + mw->matrix.font_y + mw->matrix.font_height;
675 points[ 2 ].x = x + overall_logical.width + mw->matrix.font_width;
676 points[ 2 ].y = y + mw->matrix.font_y + mw->matrix.font_height / 2;
677
678 XFillPolygon(XtDisplay(mw), win, gc, points, 3,
679 Convex, CoordModeOrigin);
680 }
681
682 /*
683 * Adjust x for origin of string.
684 */
685 x -= overall_logical.x;
686
687 /*
688 * Now draw the string at x starting at char 'start' and of
689 * length 'length'
690 */
691 XmbDrawString(XtDisplay(mw), win, font_set, gc, x, y, &string[start],
692 length);
693
694 /*
695 * If bold is on, draw the string again offset by 1 pixel (overstrike)
696 */
697 if (bold)
698 XmbDrawString(XtDisplay(mw), win, font_set, gc, x - 1, y,
699 &string[start], length);
700 if (ink_array)
701 XtFree((char*)ink_array);
702 if (logical_array)
703 XtFree((char*)logical_array);
704 }
705 else
706 {
707 width = XTextWidth(font_struct, string, length);
708
709 /*
710 * If the width of the string is greater than the width of this cell,
711 * we need to clip. We don't want to use the server to clip because
712 * it is slow, so we truncate characters if we exceed a cells pixel
713 * width.
714 */
715 if (width > maxwidth)
716 {
717 switch (alignment)
718 {
719
720 case XmALIGNMENT_CENTER:
721 {
722 int startx = x;
723 int endx = x + maxwidth - 1;
724 int newendx;
725
726 /*
727 * Figure out our x for the centered string. Then loop
728 * and chop characters off the front until we are within
729 * the cell.
730 *
731 * Adjust x, the starting character and the length of the
732 * string for each char.
733 */
734 x += maxwidth / 2 - width / 2;
735 while (x < startx)
736 {
737 int cw = charWidth(font_struct,
738 (unsigned char)string[start]);
739
740 x += cw;
741 width -= cw;
742 length--;
743 start++;
744 choppedStart = True;
745 }
746
747 /*
748 * Now figure out the end x of the string. Then loop and chop
749 * characters off the end until we are within the cell.
750 */
751 newendx = x + width - 1;
752 while (newendx > endx && *(string + start))
753 {
754 int cw = charWidth(font_struct,
755 (unsigned char)string[start]);
756
757 newendx -= cw;
758 width -= cw;
759 length--;
760 choppedEnd = True;
761 }
762
763 break;
764 }
765
766 case XmALIGNMENT_END:
767 {
768
769 /*
770 * Figure out our x for the right justified string.
771 * Then loop and chop characters off the front until we fit.
772 * Adjust x for each char lopped off. Also adjust the starting
773 * character and length of the string for each char.
774 */
775 x += maxwidth - width;
776 while (width > maxwidth)
777 {
778 int cw = charWidth(font_struct,
779 (unsigned char)string[start]);
780
781 width -= cw;
782 x += cw;
783 length--;
784 start++;
785 choppedStart = True;
786 }
787 break;
788 }
789
790 case XmALIGNMENT_BEGINNING:
791 default:
792 /*
793 * Leave x alone, but chop characters off the end until we fit
794 */
795 while (width > maxwidth)
796 {
797 width -= charWidth(font_struct,
798 (unsigned char)string[length - 1]);
799 length--;
800 choppedEnd = True;
801 }
802 break;
803 }
804 }
805
806 /*
807 * We fit inside our cell, so just compute the x of the start of
808 * our string
809 */
810 else
811 {
812 switch (alignment)
813 {
814
815 case XmALIGNMENT_CENTER:
816 x += maxwidth / 2 - width / 2;
817 break;
818
819 case XmALIGNMENT_END:
820 x += maxwidth - width;
821 break;
822
823 case XmALIGNMENT_BEGINNING:
824 default:
825 /*
826 * Leave x alone
827 */
828 break;
829 }
830 }
831
832 /*
833 * Don't worry, XSetForeground is smart about avoiding unnecessary
834 * protocol requests.
835 */
836 XSetForeground(XtDisplay(mw), gc, color);
837
838 if (mw->matrix.show_arrows && choppedEnd)
839 {
840 XPoint points[3];
841 points[0].x = points[1].x = x + width - mw->matrix.font_width;
842 points[0].y = y + mw->matrix.font_y;
843 points[1].y = y + mw->matrix.font_y + mw->matrix.font_height;
844 points[2].x = x + width;
845 points[2].y = y + mw->matrix.font_y + mw->matrix.font_height / 2;
846
847 XFillPolygon(XtDisplay(mw), win, gc, points, 3,
848 Convex, CoordModeOrigin);
849
850 /* Reduce the length to allow for our foreign character */
851 length--;
852 }
853 if (mw->matrix.show_arrows && choppedStart)
854 {
855 XPoint points[3];
856 points[0].x = points[1].x = x + mw->matrix.font_width;
857 points[0].y = y + mw->matrix.font_y;
858 points[1].y = y + mw->matrix.font_y + mw->matrix.font_height;
859 points[2].x = x;
860 points[2].y = y + mw->matrix.font_y + mw->matrix.font_height / 2;
861
862 XFillPolygon(XtDisplay(mw), win, gc, points, 3,
863 Convex, CoordModeOrigin);
864
865 /* Offset the start point so as to not draw on the triangle */
866 x += mw->matrix.font_width;
867 start++;
868 length--;
869 }
870
871 /*
872 * Now draw the string at x starting at char 'start' and of length
873 * 'length'
874 */
875 #ifdef NEED_WCHAR
876 if (TWO_BYTE_FONT(mw))
877 XDrawString16(XtDisplay(mw), win, gc, x, y, &string[start],
878 length);
879 else
880 #endif
881 XDrawString(XtDisplay(mw), win, gc, x, y, &string[start], length);
882
883 /*
884 * If bold is on, draw the string again offset by 1 pixel (overstrike)
885 */
886 if (bold)
887 #ifdef NEED_WCHAR
888 if (TWO_BYTE_FONT(mw))
889 XDrawString16(XtDisplay(mw), win, gc, x - 1, y,
890 &string[start], length);
891 else
892 #endif
893 XDrawString(XtDisplay(mw), win, gc, x - 1, y,
894 &string[start], length);
895 }
896 }
897
898 void
xbaeComputeCellColors(mw,row,column,fg,bg)899 xbaeComputeCellColors(mw, row, column, fg, bg)
900 XbaeMatrixWidget mw;
901 int row, column;
902 Pixel *fg, *bg;
903 {
904 Boolean alt = mw->matrix.alt_row_count ?
905 (row / mw->matrix.alt_row_count) % 2 : False;
906
907 /*
908 * Compute the background and foreground colours of the cell
909 */
910 if (mw->matrix.selected_cells && mw->matrix.selected_cells[row][column])
911 if (mw->matrix.reverse_select)
912 if (mw->matrix.colors)
913 *bg = mw->matrix.colors[row][column];
914 else
915 *bg = mw->manager.foreground;
916 else
917 *bg = mw->matrix.selected_background;
918 else if (mw->matrix.cell_background &&
919 mw->matrix.cell_background[row][column] !=
920 mw->core.background_pixel)
921 *bg = mw->matrix.cell_background[row][column];
922 else
923 {
924 if (alt)
925 *bg = mw->matrix.odd_row_background;
926 else
927 *bg = mw->matrix.even_row_background;
928 }
929
930 if (mw->matrix.selected_cells && mw->matrix.selected_cells[row][column])
931 if (mw->matrix.reverse_select)
932 if (mw->matrix.cell_background)
933 *fg = mw->matrix.cell_background[row][column];
934 else
935 *fg = mw->core.background_pixel;
936 else
937 *fg = mw->matrix.selected_foreground;
938 else if (mw->matrix.colors)
939 *fg = mw->matrix.colors[row][column];
940 else
941 *fg = mw->manager.foreground;
942 }
943
944 void
xbaeDrawCell(mw,row,column)945 xbaeDrawCell(mw, row, column)
946 XbaeMatrixWidget mw;
947 int row, column;
948 {
949 Pixel bg, fg;
950 String string;
951 int x, y;
952
953 if (mw->matrix.disable_redisplay || mw->matrix.rows == 0 ||
954 mw->matrix.columns == 0)
955 return;
956
957 /*
958 * Convert the row/column to the coordinates relative to the correct
959 * window
960 */
961 xbaeRowColToXY(mw, row, column, &x, &y);
962
963 xbaeComputeCellColors(mw, row, column, &fg, &bg);
964
965 #if CELL_WIDGETS
966 if (mw->matrix.cell_widgets[row][column])
967 xbaeDrawCellWidget(mw, row, column, x, y,
968 mw->matrix.cell_widgets[row][column], bg, fg);
969 else
970 #endif
971
972 if (!mw->matrix.draw_cell_callback)
973 {
974 if (row < mw->matrix.rows && column < mw->matrix.columns)
975 {
976 string = mw->matrix.cells ?
977 mw->matrix.cells[row][column] : "";
978 xbaeDrawCellString(mw, row, column, x, y, string, bg, fg);
979 }
980 }
981 else
982 {
983 Pixmap pixmap;
984 Pixmap mask;
985 XbaeCellType type;
986 int width, height;
987 int depth;
988
989 if (row < mw->matrix.rows && column < mw->matrix.columns)
990 {
991 type = xbaeGetDrawCellValue(mw, row, column, &string, &pixmap,
992 &mask, &width, &height, &bg, &fg,
993 &depth);
994 if (type == XbaeString)
995 xbaeDrawCellString(mw, row, column, x, y, string, bg, fg);
996 else if (type == XbaePixmap)
997 xbaeDrawCellPixmap(mw, row, column, x, y, pixmap, mask,
998 width, height, bg, fg, depth);
999 }
1000 }
1001 }
1002
1003 XbaeCellType
xbaeGetDrawCellValue(mw,row,column,string,pixmap,mask,width,height,bg,fg,depth)1004 xbaeGetDrawCellValue(mw, row, column, string, pixmap, mask, width,
1005 height, bg, fg, depth)
1006 XbaeMatrixWidget mw;
1007 int row;
1008 int column;
1009 String *string;
1010 Pixmap *pixmap;
1011 Pixmap *mask;
1012 int *width, *height;
1013 Pixel *bg, *fg;
1014 int *depth;
1015 {
1016 XbaeMatrixDrawCellCallbackStruct call_data;
1017
1018 call_data.reason = XbaeDrawCellReason;
1019 call_data.event = (XEvent *)NULL;
1020 call_data.row = row;
1021 call_data.column = column;
1022 call_data.width = COLUMN_WIDTH(mw, column) - TEXT_WIDTH_OFFSET(mw) * 2;
1023 call_data.height = ROW_HEIGHT(mw) - TEXT_HEIGHT_OFFSET(mw) * 2;
1024 call_data.type = XbaeString;
1025 call_data.string = "";
1026 call_data.pixmap = (Pixmap)NULL;
1027 call_data.mask = (Pixmap)NULL;
1028 call_data.foreground = *fg;
1029 call_data.background = *bg;
1030 call_data.depth = 0;
1031
1032 XtCallCallbackList((Widget)mw, mw->matrix.draw_cell_callback,
1033 (XtPointer) &call_data);
1034
1035 *pixmap = call_data.pixmap;
1036 *mask = call_data.mask;
1037 *string = call_data.string ? call_data.string : ""; /* Handle NULLs */
1038
1039 if (mw->matrix.reverse_select && mw->matrix.selected_cells &&
1040 mw->matrix.selected_cells[row][column])
1041 {
1042 /*
1043 * if colours were set by the draw cell callback, handle reverse
1044 * selection
1045 */
1046 if (*bg != call_data.background)
1047 {
1048 if (*fg != call_data.foreground)
1049 *bg = call_data.foreground;
1050 *fg = call_data.background;
1051 }
1052 else if (*fg != call_data.foreground)
1053 *bg = call_data.foreground;
1054 }
1055 else
1056 {
1057 *fg = call_data.foreground;
1058 *bg = call_data.background;
1059 }
1060 *width = call_data.width;
1061 *height = call_data.height;
1062 *depth = call_data.depth;
1063
1064 if (call_data.type == XbaePixmap)
1065 {
1066 if (*mask == XmUNSPECIFIED_PIXMAP || *mask == BadPixmap)
1067 call_data.mask = 0;
1068
1069 if (*pixmap == XmUNSPECIFIED_PIXMAP || *pixmap == BadPixmap)
1070 {
1071 XtAppWarningMsg(
1072 XtWidgetToApplicationContext((Widget)mw),
1073 "drawCellCallback", "Pixmap", "XbaeMatrix",
1074 "XbaeMatrix: Bad pixmap passed from drawCellCallback",
1075 NULL, 0);
1076 call_data.type = XbaeString;
1077 *string = "";
1078 }
1079 else if (!*depth)
1080 {
1081 /*
1082 * If we know the depth, width and height don't do a round
1083 * trip to find the
1084 * geometry
1085 */
1086 Window root_return;
1087 int x_return, y_return;
1088 unsigned int width_return, height_return;
1089 unsigned int border_width_return;
1090 unsigned int depth_return;
1091
1092 if (XGetGeometry(XtDisplay(mw), *pixmap, &root_return,
1093 &x_return, &y_return, &width_return,
1094 &height_return, &border_width_return,
1095 &depth_return))
1096 {
1097 *width = width_return;
1098 *height = height_return;
1099 *depth = depth_return;
1100 }
1101 }
1102 }
1103 return (call_data.type);
1104 }
1105
1106 /*
1107 * Draw the column label for the specified column. Handles labels in
1108 * fixed and non-fixed columns.
1109 */
1110 void
1111 #if NeedFunctionPrototypes
xbaeDrawColumnLabel(XbaeMatrixWidget mw,int column,Boolean pressed)1112 xbaeDrawColumnLabel(XbaeMatrixWidget mw, int column, Boolean pressed)
1113 #else
1114 xbaeDrawColumnLabel(mw, column, pressed)
1115 XbaeMatrixWidget mw;
1116 int column;
1117 Boolean pressed;
1118 #endif
1119 {
1120 String label;
1121 int labelX, labelY;
1122 int buttonX;
1123 int i;
1124 GC gc;
1125 Window win = XtWindow(mw);
1126 Boolean clipped = (column >= (int)mw->matrix.fixed_columns &&
1127 column < TRAILING_HORIZ_ORIGIN(mw));
1128
1129 Boolean button = mw->matrix.button_labels ||
1130 (mw->matrix.column_button_labels &&
1131 mw->matrix.column_button_labels[column]);
1132
1133 if (mw->matrix.column_labels[column][0] == '\0' && !button)
1134 return;
1135
1136 /*
1137 * If the column label is in a fixed column, we don't need to account
1138 * for the horiz_origin
1139 */
1140 if (column < (int)mw->matrix.fixed_columns)
1141 {
1142 labelX = COLUMN_LABEL_OFFSET(mw) + COLUMN_POSITION(mw, column) +
1143 TEXT_X_OFFSET(mw);
1144 buttonX = COLUMN_LABEL_OFFSET(mw) + COLUMN_POSITION(mw, column);
1145 }
1146 else if (column >= TRAILING_HORIZ_ORIGIN(mw))
1147 {
1148 labelX = TRAILING_FIXED_COLUMN_LABEL_OFFSET(mw) +
1149 COLUMN_POSITION(mw, column) -
1150 COLUMN_POSITION(mw, TRAILING_HORIZ_ORIGIN(mw)) +
1151 TEXT_X_OFFSET(mw);
1152 buttonX = TRAILING_FIXED_COLUMN_LABEL_OFFSET(mw) +
1153 COLUMN_POSITION(mw, column) -
1154 COLUMN_POSITION(mw, TRAILING_HORIZ_ORIGIN(mw));
1155 }
1156 else
1157 {
1158 labelX = COLUMN_LABEL_OFFSET(mw) +
1159 (COLUMN_POSITION(mw, column) - HORIZ_ORIGIN(mw)) +
1160 TEXT_X_OFFSET(mw);
1161 buttonX = COLUMN_LABEL_OFFSET(mw) + (COLUMN_POSITION(mw, column) -
1162 HORIZ_ORIGIN(mw));
1163 }
1164
1165 /*
1166 * Set our y to the baseline of the first line in this column
1167 */
1168 labelY = -mw->matrix.label_font_y +
1169 mw->matrix.cell_shadow_thickness +
1170 mw->matrix.cell_highlight_thickness +
1171 mw->matrix.cell_margin_height +
1172 mw->matrix.text_shadow_thickness +
1173 (mw->matrix.column_label_maxlines -
1174 mw->matrix.column_label_lines[column].lines) * LABEL_HEIGHT(mw) +
1175 HORIZ_SB_OFFSET(mw);
1176
1177 if (clipped)
1178 gc = mw->matrix.label_clip_gc;
1179 else
1180 gc = mw->matrix.label_gc;
1181
1182 if (button)
1183 {
1184 XSetForeground(XtDisplay(mw), gc, mw->matrix.button_label_background);
1185 XFillRectangle(XtDisplay(mw), win, gc, buttonX, HORIZ_SB_OFFSET(mw),
1186 COLUMN_WIDTH(mw, column), COLUMN_LABEL_HEIGHT(mw));
1187 }
1188
1189 XSetForeground(XtDisplay(mw), gc, mw->matrix.column_label_color);
1190 XSetBackground(XtDisplay(mw), gc, mw->matrix.button_label_background);
1191
1192 label = mw->matrix.column_labels[column];
1193
1194 if (label[0] != '\0')
1195 for (i = 0; i < mw->matrix.column_label_lines[column].lines; i++)
1196 {
1197 xbaeDrawString(mw, XtWindow(mw), gc, label,
1198 mw->matrix.column_label_lines[column].lengths[i],
1199 labelX, labelY, mw->matrix.column_widths[column],
1200 mw->matrix.column_label_alignments ?
1201 mw->matrix.column_label_alignments[column] :
1202 XmALIGNMENT_BEGINNING, False,
1203 mw->matrix.bold_labels, False, True,
1204 mw->matrix.column_label_color);
1205
1206 labelY += LABEL_HEIGHT(mw);
1207 label += mw->matrix.column_label_lines[column].lengths[i] + 1;
1208 }
1209 if (button)
1210 xbaeDrawCellShadow(mw, XtWindow(mw), -1, column,
1211 buttonX, HORIZ_SB_OFFSET(mw),
1212 COLUMN_WIDTH(mw, column),
1213 COLUMN_LABEL_HEIGHT(mw), True, clipped, pressed);
1214 }
1215
1216 /*
1217 * Draw the row label for the specified row. Handles labels in fixed and
1218 * non-fixed rows.
1219 */
1220 void
1221 #if NeedFunctionPrototypes
xbaeDrawRowLabel(XbaeMatrixWidget mw,int row,Boolean pressed)1222 xbaeDrawRowLabel(XbaeMatrixWidget mw, int row, Boolean pressed)
1223 #else
1224 xbaeDrawRowLabel(mw, row, pressed)
1225 XbaeMatrixWidget mw;
1226 int row;
1227 Boolean pressed;
1228 #endif
1229 {
1230 int y;
1231 GC gc;
1232 Window win = XtWindow(mw);
1233 Boolean clipped = (row >= (int)mw->matrix.fixed_rows &&
1234 row < TRAILING_VERT_ORIGIN(mw));
1235
1236 Boolean button = mw->matrix.button_labels ||
1237 (mw->matrix.row_button_labels && mw->matrix.row_button_labels[row]);
1238
1239 if (mw->matrix.row_labels[row][0] == '\0' && !button)
1240 return;
1241
1242 /*
1243 * If the row label is in a fixed row we don't need to account
1244 * for the vert_origin
1245 */
1246 if (row < (int)mw->matrix.fixed_rows)
1247 y = ROW_LABEL_OFFSET(mw) + ROW_HEIGHT(mw) * row + TEXT_Y_OFFSET(mw);
1248 else if (row >= TRAILING_VERT_ORIGIN(mw))
1249 y = TRAILING_FIXED_ROW_LABEL_OFFSET(mw) +
1250 ROW_HEIGHT(mw) * (row - TRAILING_VERT_ORIGIN(mw)) +
1251 TEXT_Y_OFFSET(mw);
1252 else
1253 y = ROW_LABEL_OFFSET(mw) + ROW_HEIGHT(mw) * (row - VERT_ORIGIN(mw)) +
1254 LABEL_Y_OFFSET(mw) - mw->matrix.first_row_offset;
1255
1256 if (clipped)
1257 gc = mw->matrix.label_clip_gc;
1258 else
1259 gc = mw->matrix.label_gc;
1260
1261 if (button)
1262 {
1263 XSetForeground(XtDisplay(mw), gc, mw->matrix.button_label_background);
1264 XFillRectangle(XtDisplay(mw), win, gc, VERT_SB_OFFSET(mw),
1265 y - TEXT_Y_OFFSET(mw), ROW_LABEL_WIDTH(mw),
1266 ROW_HEIGHT(mw));
1267 }
1268
1269 XSetForeground(XtDisplay(mw), gc, mw->matrix.row_label_color);
1270 XSetBackground(XtDisplay(mw), gc, mw->matrix.button_label_background);
1271
1272 if (mw->matrix.row_labels[row][0] != '\0')
1273 xbaeDrawString(mw, win, gc,
1274 mw->matrix.row_labels[row],
1275 strlen(mw->matrix.row_labels[row]),
1276 TEXT_X_OFFSET(mw) + VERT_SB_OFFSET(mw), y,
1277 mw->matrix.row_label_width,
1278 mw->matrix.row_label_alignment, False,
1279 mw->matrix.bold_labels, True, False,
1280 mw->matrix.row_label_color);
1281
1282 if (button)
1283 xbaeDrawCellShadow(mw, win, row, -1, VERT_SB_OFFSET(mw),
1284 y - TEXT_Y_OFFSET(mw), ROW_LABEL_WIDTH(mw),
1285 ROW_HEIGHT(mw), True, clipped, pressed);
1286 }
1287