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