1 /***********************************************************************/
2 /* Open Visualization Data Explorer                                    */
3 /* (C) Copyright IBM Corp. 1989,1999                                   */
4 /* ALL RIGHTS RESERVED                                                 */
5 /* This code licensed under the                                        */
6 /*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
7 /***********************************************************************/
8 
9 #include <dxconfig.h>
10 #include "../base/defines.h"
11 
12 
13 
14 #include <stdio.h>
15 #include <stdlib.h>
16 #if defined (HAVE_CTYPE_H)
17 #include <ctype.h>
18 #endif
19 #if defined (HAVE_TYPES_H)
20 #include <types.h>
21 #endif
22 #include <X11/IntrinsicP.h>
23 #include <Xm/Xm.h>
24 #include <Xm/MessageB.h>
25 
26 
27 #include "MultiTextP.h"
28 
29 #if defined(intelnt)
30 /* winuser.h defines ScrollWindow which conflicts with code in this file */
31 #define ScrollWindow _ScrollWindow
32 #endif
33 
34 #define CR			13
35 #define TAB			9
36 #define BACKSPACE		8
37 #define SPACE			32
38 #define CTRL_C			3
39 
40 #define DEFAULT_WORD_SPACING	1
41 #define DEFAULT_LINE_SPACING	0
42 #define DEFAULT_FONT_NAME	"fixed"
43 #define MAX_BUFF_SIZE		32767
44 #define MAX_STR_LEN		1024
45 
46 #define UP			0
47 #define DOWN			1
48 #define LEFT			2
49 #define RIGHT			3
50 
51 #define TOP			1
52 #define BOTTOM			2
53 #define ON			TRUE
54 #define OFF			FALSE
55 
56 #ifndef ABS
57 #define ABS(a)			(((a) > 0) ? (a) : -(a))
58 #endif
59 
60 #define STRCMP(a,b)  ((a) ? ((b) ? strcmp(a,b) : strcmp(a,"")) : \
61 			    ((b) ? strcmp("",b) : 0))
62 
63 /*
64  * Forward declaration of methods:
65  */
66 static void ClassInitialize();
67 
68 static void Initialize(Widget, Widget, ArgList, Cardinal*);
69 
70 static void Destroy(Widget);
71 
72 static void Redisplay(Widget, XEvent*, Region);
73 
74 static void Resize(Widget);
75 
76 static Boolean SetValues();
77 
78 /*
79  * Private routines used by the XmMultiTextWidget.
80  */
81 static char* NewString(char*);
82 
83 static XtGeometryResult	ChangeHeight(XmMultiTextWidget,
84 				     Dimension);
85 
86 static void GetBoundingBox(XmMultiTextWidget,
87 			   char*,
88 			   XFontStruct*,
89 			   XCharStruct*,
90 			   int);
91 
92 static void ShiftFollowingLines(LineList, int);
93 
94 static Boolean          PtInLine                 (int x, int y, LineList lp);
95 static Boolean          PtInWord                 (int x, int y, WordList wp, LineList lp);
96 static Boolean          SameLinks                (WordList wp1, WordList wp2);
97 static void             SetCurrentFont           (XmMultiTextWidget w, WordList wp,
98 						  XFontStruct *font);
99 static void             SetCurrentColor          (XmMultiTextWidget w, WordList wp,
100 						  unsigned long color);
101 static void             DisplayImage             (XmMultiTextWidget w, WordList wp);
102 static void             DisplayWord              (XmMultiTextWidget w, WordList wp);
103 static void             DisplaySelectedWord      (XmMultiTextWidget w, WordList wp);
104 static void             DisplayDeselectedWord    (XmMultiTextWidget w, WordList wp);
105 static void             DrawLinkMarking          (XmMultiTextWidget w, WordList wp);
106 static void             CalculateSpacing         (Widget w, WordList wp, int *spaceBefore,
107 						  int *spaceAfter);
108 static void             DrawWord                 (XmMultiTextWidget w, WordList wp,
109 						  Boolean selected);
110 static void             DrawLine                 (XmMultiTextWidget w, LineList lp);
111 static void             ResizeTheLine            (XmMultiTextWidget w, LineList lp,
112 						  WordList theWord);
113 static void             FreeCharRecord           (XmMultiTextWidget w, CharList cp);
114 static void             FreeWordRecord           (XmMultiTextWidget w, WordList wp);
115 static void             FreeLineRecord           (XmMultiTextWidget w, LineList lp);
116 static int              NextTab                  (XmMultiTextWidget w, int *tabWidth,
117 						  int indent);
118 static unsigned long    Color                    (XmMultiTextWidget w, char *name);
119 static XFontStruct     *NamedFont                (XmMultiTextWidget w, char *name);
120 static LineList         NewLine                  (XmMultiTextWidget w, int indent, int yposn,
121 						  int lineSpacing);
122 static Boolean          StuffWordOntoCurrentLine (XmMultiTextWidget w, LineList lp, WordList wp,
123 						  int neededWidth, Boolean firstWordOfLine,
124 						  Boolean isTab, Boolean prevWordIsTab);
125 static int              GetNeededWidth           (XmMultiTextWidget w, WordList wPtr, int indent,
126 						  Boolean *firstWordOfLine, Boolean *isTab,
127 						  Boolean *prevWordIsTab);
128 static unsigned short   StoreWord                (XmMultiTextWidget w, WordList wordPtr,
129 						  int indent);
130 static unsigned int     AppendWordToTopLine      (XmMultiTextWidget cw, WordList wordPtr,
131 						  int indent);
132 static void             PositionCursor           (XmMultiTextWidget cw, int x, int y,
133 						  LineList *lp, int *actualX);
134 static void             UpdateCursor             (XmMultiTextWidget cw, LineList lp, int x,
135 						  int actualX, int actualY);
136 static void             MakeCursorPixmaps        (XmMultiTextWidget cw);
137 static void             BlinkCursor              (XmMultiTextWidget cw);
138 static void             TurnOnCursor             (XmMultiTextWidget cw);
139 static void             TurnOffCursor            (XmMultiTextWidget cw);
140 
141 
142 /* actions */
143 static void MoveTo(Widget, XEvent*, String*, Cardinal*);
144 static void BtnMoveTo     (Widget, XEvent*, String*, Cardinal*);
145 static void BtnMoveNew    (Widget, XEvent*, String*, Cardinal*);
146 static void BtnRelease    (Widget, XEvent*, String*, Cardinal*);
147 
148 static void KeyPush       (Widget, XEvent*, String*, Cardinal*);
149 static void NewLineCR     (Widget, XEvent*, String*, Cardinal*);
150 static void InsertSpace   (Widget, XEvent*, String*, Cardinal*);
151 static void MoveUp        (Widget, XEvent*, String*, Cardinal*);
152 static void MoveDown      (Widget, XEvent*, String*, Cardinal*);
153 static void MoveLeft      (Widget, XEvent*, String*, Cardinal*);
154 static void MoveRight     (Widget, XEvent*, String*, Cardinal*);
155 static void Enter         (Widget, XEvent*, String*, Cardinal*);
156 static void Leave         (Widget, XEvent*, String*, Cardinal*);
157 static void InFocus       (Widget, XEvent*, String*, Cardinal*);
158 static void OutFocus      (Widget, XEvent*, String*, Cardinal*);
159 static void DeleteLine    (Widget w, XKeyPressedEvent *event, String *argv, Cardinal *argc);
160 static void OpenLineTop   (Widget, XEvent*, String*, Cardinal*);
161 static void AddWordTop    (Widget, XEvent*, String*, Cardinal*);
162 static void Dummy         (Widget, XEvent*, String*, Cardinal*);
163 
164 #ifndef NODPS
165 extern void ShowScale     (Widget, XEvent*, String*, Cardinal*);
166 #endif
167 static void Refresh       (Widget, XEvent*, String*, Cardinal*);
168 static void Cut           (Widget, XEvent*, String*, Cardinal*);
169 
170 
171 /*
172  * Event handler?
173  */
174 static void ChangeFocus   (Widget, XEvent*, String*, Cardinal*);
175 
176 
177 /* Public routines defined external in public header file. */
178 void           XmMultiTextClearText         (Widget w);
179 int            XmMultiTextGetPosition       (Widget w);
180 int            XmMultiTextGetCursorPosition (Widget w);
181 void           XmMultiTextQueryCursor       (Widget w, int *lineNumber, int *y);
182 void           XmMultiTextDeselectAll       (Widget w);
183 void           XmMultiTextSetTabStops       (Widget w, int tabList[], int tabCount);
184 LinkInfoPtr    XmMultiTextMakeLinkRecord    (Widget w, LinkType linkType, LinkPosition linkPosition,
185 					     char *linkData);
186 void           XmMultiTextAppendNewLine     (Widget w, char *theWord, char *fontName, int indent);
187 unsigned short XmMultiTextAppendWord        (Widget w, char *theWord, char *fontName,
188 					     char *colorName,
189 					     int indent, LinkInfoPtr linkInfo);
190 unsigned short XmMultiTextAppendWordTop     (Widget w,  char *theWord, char *fontName,
191 					     char *colorName, int indent, LinkInfoPtr linkInfo);
192 void           XmMultiTextOpenLineTop       (Widget w, int indent);
193 int            XmMultiTextDeleteLineTop     (Widget w, int linesToDelete);
194 void           XmMultiTextDeleteLineBottom  (Widget w);
195 int            XmMultiTextLongestLineLength (Widget w);
196 unsigned short XmMultiTextAppendImage       (Widget w, char *theWord, char *fontName,
197 					     char *colorName,
198 					     int indent, Pixmap image, unsigned int width,
199 					     unsigned int height, LinkInfoPtr linkInfo);
200 unsigned short XmMultiTextAppendWidget      (Widget w, char *theWord, char *fontName,
201 					     char *colorName,
202 					     int indent, Widget newW);
203 unsigned short XmMultiTextAppendDPS         (Widget w, char *theWord, char *fontName,
204 					     char *colorName,
205 					     int indent, char *dpsFileName, unsigned int width,
206 					     unsigned int height, LinkInfoPtr linkInfo);
207 Boolean        XmMultiTextAppendChar        (Widget w, char str);
208 Boolean        XmMultiTextAppendLine        (Widget w, char *str);
209 
210 static XtResource resources[] =
211 {
212     {
213 	XmNdpsCapable,
214 	XmCDPSCapable,
215 	XmRBoolean,
216 	sizeof(Boolean),
217 	offset(multiText.dpsCapable),
218 	XmRImmediate,
219 	(XtPointer)TRUE
220     },
221     {
222 	XmNwordWrap,
223 	XmCWordWrap,
224 	XmRBoolean,
225 	sizeof(Boolean),
226 	offset(multiText.wordWrap),
227 	XmRImmediate,
228 	(XtPointer)TRUE
229     },
230     {
231 	XmNmarginWidth,
232 	XmCMarginWidth,
233 	XmRInt,
234 	sizeof(int),
235 	offset(multiText.marginWidth),
236 	XmRImmediate,
237 	(XtPointer)10
238     },
239     {
240 	XmNmarginHeight,
241 	XmCMarginHeight,
242 	XmRInt,
243 	sizeof(int),
244 	offset(multiText.marginHeight),
245 	XmRImmediate,
246 	(XtPointer)10
247     },
248     {
249 	XmNwaitCursorCount,
250 	XmCWaitCursorCount,
251 	XmRInt,
252 	sizeof(int),
253 	offset(multiText.waitCursorCount),
254 	XmRImmediate,
255 	(XtPointer)1
256     },
257     {
258 	XmNblinkRate,
259 	XmCBlinkRate,
260 	XmRInt,
261 	sizeof(int),
262 	offset(multiText.blinkRate),
263 	XmRImmediate,
264 	(XtPointer)250
265     },
266     {
267 	XmNshowCursor,
268 	XmCShowCursor,
269 	XmRBoolean,
270 	sizeof(Boolean),
271 	offset(multiText.showCursor),
272 	XmRImmediate,
273 	(XtPointer)TRUE
274     },
275     {
276 	XmNfocusSensitive,
277 	XmCFocusSensitive,
278 	XmRBoolean,
279 	sizeof(Boolean),
280 	offset(multiText.focusSensitive),
281 	XmRImmediate,
282 	(XtPointer)FALSE
283     },
284     {
285 	XmNcursorColor,
286 	XmCForeground,
287 	XmRPixel,
288 	sizeof(Pixel),
289 	offset(multiText.cursorColor),
290 	XmRString,
291 	"yellow"
292     },
293     {
294 	XmNscaleDPSpercent,
295 	XmCScaleDPSpercent,
296 	XmRInt,
297 	sizeof(int),
298 	offset(multiText.scaleDPSpercent),
299 	XmRImmediate,
300 	(XtPointer)100
301     },
302     {
303 	XmNsmartSpacing,
304 	XmCSmartSpacing, XmRBoolean,
305 	sizeof(Boolean),
306 	offset(multiText.smartSpacing),
307 	XmRImmediate,
308 	(XtPointer)TRUE
309     },
310     {
311 	XmNsmoothScroll,
312 	XmCSmoothScroll,
313 	XmRBoolean,
314 	sizeof(Boolean),
315 	offset(multiText.smoothScroll),
316 	XmRImmediate,
317 	(XtPointer)FALSE
318     },
319     {
320 	XmNexposeOnly,
321 	XmCExposeOnly,
322 	XmRBoolean,
323 	sizeof(Boolean),
324 	offset(multiText.exposeOnly),
325 	XmRImmediate,
326 	(XtPointer)FALSE
327     },
328     {
329 	XmNlinkCallback, XmCCallback, XmRCallback,
330 	sizeof(XtCallbackList), offset(multiText.linkCallback),
331 	XmRImmediate, (XtPointer) NULL
332     },
333     {
334 	XmNselectCallback,
335 	XmCCallback,
336 	XmRCallback,
337 	sizeof(XtCallbackList),
338 	offset(multiText.selectCallback),
339 	XmRImmediate,
340 	(XtPointer)NULL
341     }
342 };
343 
344 
345 /*
346  * Default Translation Table:
347  *  This table maps event sequences into action names.
348  */
349 #ifndef NODPS
350 static char defaultTranslations[] =
351     "Ctrl Shift<Btn1Down>:  ShowScale()           \n\
352      <Btn1Motion>:          BtnMoveTo()           \n\
353      <Btn1Up>:              BtnRelease()          \n\
354      <Btn1Down>:            MoveTo()              \n\
355      <Key>BackSpace:        Cut()                 \n\
356      <Key>Up:               MoveUp()              \n\
357       <Key>Down:             MoveDown()           \n\
358      <Key>Left:             MoveLeft()            \n\
359      <Key>Right:            MoveRight()           \n\
360      <Key>Delete:           Dummy()               \n\
361      <Key>Return:           NewLineCR()           \n\
362      <FocusIn>:             InFocus()             \n\
363      <FocusOut>:            OutFocus()            \n\
364      <Key>F1:               DeleteLine(1)         \n\
365      <Key>F2:               DeleteLine(2)         \n\
366      <Key>F3:               OpenLineTop()         \n\
367      <Key>F5:               AddWordTop()";
368 #else
369 static char defaultTranslations[] =
370     "<Btn1Motion>:          BtnMoveTo()           \n\
371      <Btn1Up>:              BtnRelease()          \n\
372      <Btn1Down>:            MoveTo()              \n\
373      <Key>BackSpace:        Cut()                 \n\
374      <Key>Up:               MoveUp()              \n\
375      <Key>Down:             MoveDown()            \n\
376      <Key>Left:             MoveLeft()            \n\
377      <Key>Right:            MoveRight()           \n\
378      <Key>Delete:           Dummy()               \n\
379      <Key>Return:           NewLineCR()           \n\
380      <FocusIn>:             InFocus()             \n\
381      <FocusOut>:            OutFocus()            \n\
382      <Key>F1:               DeleteLine(1)         \n\
383      <Key>F2:               DeleteLine(2)         \n\
384      <Key>F3:               OpenLineTop()         \n\
385      <Key>F5:               AddWordTop()";
386 #endif
387 
388 
389 /*=======================================================*
390  |                      Actions Table                    |
391  | the action table maps string action names into actual |
392  | action functions.                                     |
393  *=======================================================*/
394 
395 static XtActionsRec actions[] = {
396   {"MoveTo",      MoveTo      },
397   {"BtnMoveTo",   BtnMoveTo   },
398   {"BtnMoveNew",  BtnMoveNew  },
399   {"BtnRelease",  BtnRelease  },
400   {"Refresh",     Refresh     },
401   {"Cut",         Cut         },
402   {"KeyPush",     KeyPush     },
403   {"NewLineCR",   NewLineCR   },
404   {"InsertSpace", InsertSpace },
405 #ifndef NODPS
406   {"ShowScale",   ShowScale   },
407 #endif
408   {"MoveUp",      MoveUp      },
409   {"MoveDown",    MoveDown    },
410   {"MoveLeft",    MoveLeft    },
411   {"MoveRight",   MoveRight   },
412   {"Enter",       Enter       },
413   {"Leave",       Leave       },
414   {"InFocus",     InFocus     },
415   {"OutFocus",    OutFocus    },
416   {"DeleteLine",  (XtActionProc)DeleteLine  },
417   {"Dummy",       Dummy       },
418   {"OpenLineTop", OpenLineTop },
419   {"AddWordTop",  AddWordTop  },
420 };
421 
422 
423 
424 /*  The MultiText class record definition  */
425 
426 XmMultiTextClassRec xmMultiTextClassRec =
427 {
428     /*
429      * Core class part:
430      */
431     {
432 	(WidgetClass)&xmDrawingAreaClassRec,
433 					/* superclass			*/
434 	"XmMultiText",			/* class_name			*/
435 	sizeof(XmMultiTextRec),		/* widget_size			*/
436 	ClassInitialize,		/* class_initialize		*/
437 	NULL,				/* class_part_initialize 	*/
438 	FALSE,				/* class_inited			*/
439 	Initialize,			/* initialize			*/
440 	NULL,				/* initialize_hook		*/
441 	XtInheritRealize,		/* realize			*/
442 	actions,			/* actions			*/
443 	XtNumber(actions),		/* num_actions			*/
444 	resources,			/* resources			*/
445 	XtNumber(resources),		/* num_resources		*/
446 	NULLQUARK,			/* xrm_class			*/
447 	TRUE,				/* compress_motion		*/
448 	TRUE,				/* compress_exposure		*/
449 	TRUE,				/* compress_enterleave		*/
450 	FALSE,				/* visible_interest		*/
451 	Destroy,			/* destroy			*/
452 	Resize,				/* resize			*/
453 	Redisplay,			/* expose			*/
454 	(XtSetValuesFunc)SetValues,	/* set_values			*/
455 	NULL,				/* set_values_hook		*/
456 	XtInheritSetValuesAlmost,	/* set_values_almost		*/
457 	NULL,				/* get_values_hook		*/
458 	NULL,				/* accept_focus			*/
459 	XtVersion,			/* version			*/
460 	NULL,				/* callback private		*/
461 	defaultTranslations,		/* tm_table			*/
462 	XtInheritQueryGeometry,		/* query_geometry		*/
463 	NULL,				/* display_accelerator		*/
464 	NULL				/* extension			*/
465     },
466 
467     /*
468      * Compositie class part:
469      */
470     {
471 	XtInheritGeometryManager,	/* Geometry Manager		*/
472 	XtInheritChangeManaged,		/* Change Managed		*/
473 	XtInheritInsertChild,		/* Insert Child			*/
474 	XtInheritDeleteChild,		/* Delete Child			*/
475 	NULL				/* extension			*/
476     },
477 
478     /*
479      * Constraint class part:
480      */
481     {
482 	NULL,				/* resources			*/
483 	0,				/* num resources		*/
484 	0,				/* constraint record		*/
485 	NULL,				/* initialize			*/
486 	NULL,				/* destroy			*/
487 	NULL,				/* set values			*/
488 	NULL				/* extension			*/
489     },
490 
491    /*
492      * Manager class part:
493      */
494     {
495 	NULL,				/* default_translations		*/
496 	NULL,				/* get_resources		*/
497 	0,				/* num_get_resources		*/
498 	NULL,				/* get_cont_resources		*/
499 	0,				/* num_get_cont_resources	*/
500 	(XmParentProcessProc)NULL,      /* parent_process         	*/
501 	NULL				/* extension			*/
502     },
503 
504     /*
505      * DrawingArea class part:
506      */
507     {
508 	NULL				/* dummy field			*/
509     },
510 
511 
512     {
513 	NULL				/* extension			*/
514     },
515 };
516 
517 
518 WidgetClass xmMultiTextWidgetClass = (WidgetClass)&xmMultiTextClassRec;
519 
520 
521 
522 /*======================================================================*
523  |                      XmMultiText Widget Methods                      |
524  |                      --------------------------                      |
525  | These are private to XmMultiText and can't be accessed by the        |
526  | application programmer directly.                                     |
527  *======================================================================*/
528 
529 /*
530  * This routine creates an XFontStruct and a GC.  Both of these are put
531  * into the instance record of the MultiTextWidget.
532  */
533 static void
GetTextGC(XmMultiTextWidget cw)534 GetTextGC(XmMultiTextWidget cw)
535 {
536     XtGCMask     mask;
537     XGCValues    values;
538     XFontStruct* font;
539 
540     mask =
541 	GCForeground |
542 	GCBackground |
543 	GCFont       |
544         GCGraphicsExposures |
545         GCSubwindowMode;
546 
547     font = XLoadQueryFont(XtDisplay(cw), "fixed");
548 
549     values.font               = font->fid;
550     values.foreground         = cw->manager.foreground;
551     values.background         = cw->core.background_pixel;
552     values.graphics_exposures = False;
553     values.subwindow_mode     = ClipByChildren;
554 
555     cw->multiText.textGC =
556 	XCreateGC(XtDisplay(cw),
557 		  XRootWindowOfScreen(XtScreen(cw)),
558 		  mask,
559 		  &values);
560 
561     values.foreground = cw->multiText.cursorColor;
562     cw->multiText.cursorGC =
563       XCreateGC(XtDisplay(cw),
564 		XRootWindowOfScreen(XtScreen(cw)),
565 		mask,
566 		&values);
567 
568     XFreeFont(XtDisplay(cw), font);
569 }
570 
571 
572 
573 /*
574  * Only draws the lines that are effected by the refreshed area.
575  */
576 static void
DoRedrawText(XmMultiTextWidget cw,int y,int height)577 DoRedrawText(XmMultiTextWidget cw,
578 	     int               y,
579 	     int               height)
580 {
581     LineList lp = cw->multiText.firstLine;
582     int      y1;
583     int      y2;
584     int      ey1;
585     int      ey2;
586 
587 
588     if (cw->multiText.drawing)
589 	return;
590 
591     cw->multiText.drawing = TRUE;
592 
593     ey1 = y;
594     ey2 = y + height;
595 
596 
597     while (lp != NULL)
598     {
599 	y1 = lp->y;
600 	y2 = lp->y + lp->bbox.ascent + lp->bbox.descent;
601 
602 	if (!(((ey1 < y1) && (ey2 < y1)) || ((y1 < ey1) && (y2 < ey1))))
603 	{
604 	    DrawLine(cw, lp);
605 	}
606 
607 	lp = lp->next;
608     }
609 
610     cw->multiText.drawing = FALSE;
611 }
612 
613 
614 
615 static void
StringToFloatConverter(XrmValue * args,Cardinal * nargs,XrmValue * fromVal,XrmValue * toVal)616 StringToFloatConverter(XrmValue* args,
617 		       Cardinal* nargs,
618 		       XrmValue* fromVal,
619 		       XrmValue* toVal)
620 {
621     static float result;
622 
623     /*
624      * Make sure the number of args is correct.
625      */
626     if (*nargs != 0)
627     {
628 	XtWarning("String to Float conversion needs no arguments");
629     }
630 
631     /*
632      * Convert the string in the fromVal to a floating pt.
633      */
634     if (sscanf((char*)fromVal->addr, "%f", &result) == 1)
635     {
636 	/*
637 	 * Make the toVal point to the result.
638 	 */
639 	toVal->size = sizeof(float);
640 	toVal->addr = (void *)&result;
641     }
642 }
643 
644 
645 
646 static void
ClassInitialize(WidgetClass wc)647 ClassInitialize(WidgetClass wc)
648 {
649 #ifdef	intelnt /* Exceed on WINDOWS NT has _XmRegisterConverters()   */
650     _XmRegisterConverters();
651 #else
652     XmRegisterConverters();
653 #endif
654     XtAddConverter(XmRString, XmRFloat, StringToFloatConverter, NULL, 0);
655 }
656 
657 
658 
659 /*
660  * The instance record of each widget must be initialized at run time.
661  * This method is invoked when a new widget is created.
662  * 'newWidget' is the real widget, 'request' is a copy.  'request' contains
663  * the original resource values.  'newWidget' may have been modified by other
664  * initialize routines of superclasses.  All changes are made to 'newWidget'.
665  */
Initialize(Widget reqWidget,Widget newWidgetWidget,ArgList arg,Cardinal * argc)666 static void Initialize(Widget		 reqWidget,
667 		       Widget		 newWidgetWidget,
668 		       ArgList           arg,
669 		       Cardinal*         argc)
670 {
671     XmMultiTextWidget req = (XmMultiTextWidget)reqWidget;
672     XmMultiTextWidget newWidget = (XmMultiTextWidget)newWidgetWidget;
673     int   i;
674     char  buf[MAX_STR_LEN];
675 
676 
677 #ifndef NODPS
678 
679     int   firstEvent;
680     int   firstError;
681     int   majorOpCode;
682     if (!XQueryExtension(XtDisplay(req),
683 			 DPSNAME,
684 			 &majorOpCode,
685 			 &firstEvent,
686 			 &firstError))
687     {
688 	WarningDialog(newWidget, MSG7);
689     }
690 
691 #endif
692 
693     newWidget->manager.traversal_on = True;
694 
695     newWidget->multiText.firstLine     = NULL;
696     newWidget->multiText.currentLine   = NULL;
697     newWidget->multiText.lastLine      = NULL;
698 
699     newWidget->multiText.drawing       = FALSE;
700     newWidget->multiText.selecting     = FALSE;
701 
702     newWidget->multiText.actualX       = 0;
703     newWidget->multiText.cursorX       = 0;
704     newWidget->multiText.cursorY       = 0;
705     newWidget->multiText.oldX          = 0;
706     newWidget->multiText.oldY          = 0;
707     newWidget->multiText.cursorFg      = None;
708     newWidget->multiText.cursorBg      = None;
709     newWidget->multiText.blinkState    = FALSE;
710     newWidget->multiText.cursorAvailable = TRUE;
711     newWidget->multiText.blinkTimeOutID = 0;
712 
713     newWidget->multiText.wordSpacing   = DEFAULT_WORD_SPACING;
714 
715     newWidget->multiText.currentFontID = 0;
716     newWidget->multiText.currentColor  = 0;
717     newWidget->multiText.lastColorName = NewString("");
718     newWidget->multiText.numFonts      = 0;
719     newWidget->multiText.fontCache     = NULL;
720     newWidget->multiText.showCursor = req->multiText.showCursor;
721     if (newWidget->multiText.showCursor)
722     {
723 	TurnOnCursor(newWidget);
724     }
725     else
726     {
727 	TurnOffCursor(newWidget);
728     }
729 
730     for (i = 0; i < MAX_TAB_COUNT; i++) newWidget->multiText.tabs[i] = 0;
731     newWidget->multiText.tabCount = 0;
732 
733     newWidget->multiText.waitCursorIndex = 1;
734 
735     if ((newWidget->multiText.waitCursorCount < 1) ||
736 	(newWidget->multiText.waitCursorCount > CURSOR_COUNT))
737     {
738 	sprintf (buf, MSG1, CURSOR_COUNT);
739 	XtWarning(buf);
740 	newWidget->multiText.waitCursorCount = 1;
741     }
742 
743     if (newWidget->multiText.scaleDPSpercent <= 0)
744     {
745 	XtWarning(MSG2);
746 	newWidget->multiText.scaleDPSpercent = 1;
747     }
748 
749     GetTextGC(newWidget);
750 
751     XtAddEventHandler((Widget)newWidget, FocusChangeMask, FALSE, (XtEventHandler)ChangeFocus, (Opaque)NULL);
752     XtAddEventHandler((Widget)newWidget, EnterWindowMask, FALSE, (XtEventHandler)Enter,       (Opaque)NULL);
753     XtAddEventHandler((Widget)newWidget, LeaveWindowMask, FALSE, (XtEventHandler)Leave,       (Opaque)NULL);
754 }
755 
756 
757 
758 /*---------------------------================---------------------------*
759  |                           Redisplay method                           |
760  | This is where the widget gets redrawn when an Expose event occurs.   |
761  | Note that this routine can't be called 'Expose' since X.h defined    |
762  | 'Expose' as 12.                                                      |
763  | w      - defines the widget instance to be redisplayed.              |
764  | event  - defines a pointer to an Expose event.                       |
765  | region - is the region to be redrawn.  If compress_exposures is true |
766  |          then the region is the sum of rectangles reported on all    |
767  |          expose events, and the event parameter contains the bounding|
768  |          box of the region.  Otherwise, region is NULL.              |
769  *---------------------------================---------------------------*/
Redisplay(Widget w,XEvent * event,Region region)770 static void Redisplay (Widget w, XEvent *event, Region region)
771 {
772   XmMultiTextWidget cw = (XmMultiTextWidget)w;
773   XExposeEvent *expose = (XExposeEvent*)event;
774 
775   if (XtIsRealized((Widget)cw))
776     {
777       DoRedrawText(cw, expose->y, expose->height);
778 
779       if ((cw->multiText.cursorFg == None) && cw->multiText.cursorAvailable)
780 	{
781 	  if (cw->multiText.blinkTimeOutID != 0)
782 	    {
783 	      XtRemoveTimeOut(cw->multiText.blinkTimeOutID);
784 	      cw->multiText.blinkTimeOutID = 0;
785 	    }
786 	  BlinkCursor(cw);
787 	}
788     }
789 }
790 
791 
792 /*---------------------------================---------------------------*
793  |                           SetValues method                           |
794  | This method allows a widget to be notified when one of its resources |
795  | is set or changed.  This can be called when a widget is initialized  |
796  | by the resource manager, or when an application sets a resource with |
797  | XtSetValues().  The SetValues methods are chained and called from    |
798  | superclass to subclass order.                                        |
799  | current - the previous, unaltered state of the widget.               |
800  | request - the values requested for the widget.                       |
801  | newWidget     - the state of the widget after all superclass's SetValues   |
802  |           methods were called.  All changes must be made to newWidget.     |
803  | SetValues returns a Boolean value specifying whether or not the      |
804  | widget needs to be redrawn. If TRUE, the Intrinsics causes an Expose |
805  | event to be generated for the entire window.                         |
806  | Note that SetValues must not perform any graphics operations on the  |
807  | widget's window unless the widget is realized.                       |
808  *---------------------------================---------------------------*/
SetValues(XmMultiTextWidget current,XmMultiTextWidget request,XmMultiTextWidget newWidget)809 static Boolean SetValues (XmMultiTextWidget current, XmMultiTextWidget request,
810 			  XmMultiTextWidget newWidget)
811 {
812   Boolean redraw = FALSE;
813   char    buf[MAX_STR_LEN];
814 
815   if (current->multiText.waitCursorCount != newWidget->multiText.waitCursorCount)
816     if ((newWidget->multiText.waitCursorCount < 1) || (newWidget->multiText.waitCursorCount > CURSOR_COUNT))
817       {
818 	sprintf(buf, MSG1, CURSOR_COUNT);
819 	XtWarning(buf);
820 	newWidget->multiText.waitCursorCount = 1;
821       }
822 
823   /* there really isn't any way to screw-up the values of the MultiTextWidget, yet... */
824   /* however, it is possible to cause a redraw by changing the wordwrap value.        */
825 
826   if (current->multiText.wordWrap != newWidget->multiText.wordWrap)
827     redraw = TRUE;
828 
829   /* check that the DPSscaling percentage is correct. */
830   if (newWidget->multiText.scaleDPSpercent <= 0)
831     {
832       XtWarning(MSG2);
833       newWidget->multiText.scaleDPSpercent = 1;
834     }
835 
836   /* check if there's a change in the showCursor state. */
837   if (current->multiText.showCursor != newWidget->multiText.showCursor)
838     {
839       if (newWidget->multiText.showCursor)
840 	TurnOnCursor(newWidget);
841       else
842 	TurnOffCursor(newWidget);
843     }
844 
845   /* should add checking for margins here... */
846 
847   return redraw;
848 }
849 
850 
851 /*----------------------------==============----------------------------*
852  |                            Destroy method                            |
853  | The Destroy method is invoked when a widget is destroyed.  Chaining  |
854  | is in reverse order from other chaining; the widget's method is      |
855  | called first followed by its superclass's Destroy method.            |
856  *----------------------------==============----------------------------*/
Destroy(Widget w)857 static void Destroy (Widget w)
858 {
859   XmMultiTextWidget cw = (XmMultiTextWidget)w;
860   int i;
861 
862   /* free the font cache. */
863   for (i=0; i<cw->multiText.numFonts; i++ )
864     {
865       XFreeFont(XtDisplay(cw), cw->multiText.fontCache[i].font);
866 #ifdef COMMENT
867       XtFree(cw->multiText.fontCache[i].font);
868 #endif
869       XtFree(cw->multiText.fontCache[i].name);
870     }
871   XtFree((char*)cw->multiText.fontCache);
872   XtFree(cw->multiText.lastColorName);
873 
874   if (cw->multiText.textGC)   XFreeGC(XtDisplay(cw), cw->multiText.textGC);
875   if (cw->multiText.cursorGC) XFreeGC(XtDisplay(cw), cw->multiText.cursorGC);
876   if (cw->multiText.blinkTimeOutID != 0) XtRemoveTimeOut(cw->multiText.blinkTimeOutID);
877 }
878 
879 
880 /*-----------------------------=============----------------------------*
881  |                             Resize method                            |
882  | The Resize method is invoked when the widget's window is reconfigured|
883  | in any way.  Since the X server generates an Expose event if the     |
884  | contents of a window are corrupted, the Resize method only updates   |
885  | the data needed to allow the Redisplay method to redraw the widget   |
886  | correctly.                                                           |
887  *-----------------------------=============----------------------------*/
Resize(Widget w)888 static void Resize (Widget w)
889 {
890   XmMultiTextWidget cw = (XmMultiTextWidget)w;
891   if (cw->multiText.smoothScroll)
892     {
893       XRectangle recs[1];
894 
895       recs[0].x      = cw->multiText.marginWidth;
896       recs[0].y      = cw->multiText.marginHeight;
897       recs[0].width  = cw->core.width;
898       recs[0].height = cw->core.height;
899 
900       XSetClipRectangles(XtDisplay(cw), cw->multiText.textGC, 0, 0, recs, 1, Unsorted);
901     }
902 }
903 
904 
905 
906 
907 
908 /*======================================================================*
909  |                XmMultiText Widget Action Procedures                  |
910  |                ------------------------------------                  |
911  | The(se) routine(s) are defined at the beginning of the file in the   |
912  | list of actions.                                                     |
913  *======================================================================*/
914 
915 
916 /*----------------------------------------------------------------------*
917  |                               PtInWord                               |
918  *----------------------------------------------------------------------*/
PtInWord(int x,int y,WordList wp,LineList lp)919 static Boolean PtInWord (int x, int y, WordList wp, LineList lp)
920 {
921   if ((x >= lp->x + wp->x) && (x < lp->x + wp->x + wp->bbox.width))
922     return TRUE;
923   else
924 
925     /* now for a bit more complex - if this is a link then the bounding box */
926     /* is different depending if there is a link behind or after this word. */
927     /* in this case, we must account for the spacing between words as well  */
928     /* as the actual word itself. - yuk!                                    */
929   if (wp->linkInfo != NULL)
930     {
931       int x1 = lp->x + wp->x;
932       int x2 = lp->x + wp->x + wp->bbox.width;
933 
934       if ((wp->prev != NULL) && (wp->prev->linkInfo != NULL))
935 	x1 -= wp->spacing;
936 
937       if ((wp->next != NULL) && (wp->next->linkInfo != NULL))
938 	x2 += wp->spacing;
939 
940       /* now let's see if it's in the new larger bounding box. */
941       if ((x >= x1) && (x < x2))
942 	return TRUE;
943       else
944 	return FALSE;
945     }
946 
947   else
948     return FALSE;
949 }
950 
951 
952 
953 /*----------------------------------------------------------------------*
954  |                           HighlightWord                              |
955  *----------------------------------------------------------------------*/
HighlightWord(XmMultiTextWidget cw,LineList lp,WordList wp,int x2,int y2)956 static void HighlightWord (XmMultiTextWidget cw, LineList lp, WordList wp, int x2, int y2)
957 {
958   int     x1   = cw->multiText.startX;
959   int     y1   = cw->multiText.startY;
960   int     wpx1 = wp->x + lp->x;
961   int     wpy1 = lp->y;
962   int     wpx2 = wp->x + lp->x + wp->bbox.width;
963   int     wpy2 = lp->y + lp->bbox.ascent + lp->bbox.descent;
964   Boolean startAfter, startBefore, endAfter, endBefore, startInside, endInside;
965 
966 
967   startAfter  = (((x1  > wpx2) && (y1 >= wpy1))  ||  (y1 > wpy2));
968   startBefore = (((x1  < wpx1) && (y1 <= wpy2))  ||  (y1 < wpy1));
969   endAfter    = (((x2  > wpx2) && (y2 >  wpy1))  ||  (y2 > wpy2));
970   endBefore   = (((x2  < wpx1) && (y2 <  wpy2))  ||  (y2 <= wpy1));
971   startInside = ((wpx1 < x1)   && (x1 <= wpx2) && (wpy1 <  y1) && (y1 <= wpy2));
972   endInside   = ((wpx1 < x2)   && (x2 <= wpx2) && (wpy1 <  y2) && (y2 <=  wpy2));
973 
974   if ((startBefore && endAfter) || (startAfter && endBefore) || startInside || endInside)
975     {
976       if (!wp->selected)
977 	{
978 	  DrawWord(cw, wp, TRUE);
979 	  wp->selected = TRUE;
980 	}
981     }
982   else
983     if (wp->selected)
984       {
985 	DrawWord(cw, wp, FALSE);
986 	wp->selected = FALSE;
987       }
988 }
989 
990 
991 /*----------------------------------------------------------------------*
992  |                              BtnMoveTo                               |
993  | This gets call when the mouse button is pressed while the mouse is   |
994  | moved.                                                               |
995  *----------------------------------------------------------------------*/
BtnMoveTo(Widget w,XEvent * event,String * params,Cardinal * numParams)996 static void BtnMoveTo (Widget w, XEvent* event, String* params, Cardinal* numParams)
997 {
998   XmMultiTextWidget cw = (XmMultiTextWidget) w;
999   LineList          lp;
1000   WordList          wp;
1001 
1002 
1003   if (cw->multiText.selecting) /* selecting turns on when button is pressed. Off on release. */
1004     {
1005       /* go through each word and highlight if in region and unhighlight if not. */
1006       for (lp = cw->multiText.firstLine; lp != NULL; lp = lp->next)
1007 	for (wp = lp->firstWord; wp != NULL; wp = wp->next)
1008 	  HighlightWord(cw, lp, wp, event->xbutton.x, event->xbutton.y);
1009     }
1010 }
1011 
1012 
1013 
1014 /*----------------------------------------------------------------------*
1015  |                            BtnMoveNew                                |
1016  | This gets call when the mouse button is pressed while the mouse is   |
1017  | moved.                                                               |
1018  | This routine highlights the lines in the region - first find the line|
1019  | that the pointer is currently in, then find what line the selection  |
1020  | started in.  Highlight all lines between these line but not including|
1021  | these.  Finally highlight the region corresponding to what should be |
1022  | selected for each of the lines containing the start or end points of |
1023  | the selection region.                                                |
1024  *----------------------------------------------------------------------*/
BtnMoveNew(Widget w,XEvent * event,String * params,Cardinal * numParams)1025 static void BtnMoveNew (Widget w, XEvent* event, String* params, Cardinal* numParams)
1026 {
1027   XmMultiTextWidget cw = (XmMultiTextWidget) w;
1028   LineList          lp, lp1, lp2;
1029   int               y1, y2;
1030 
1031   /* find the line containing the starting point (the lowest of the y positions). */
1032   y1 = MIN(cw->multiText.startY, event->xbutton.y);
1033   y2 = MAX(cw->multiText.startY, event->xbutton.y);
1034 
1035   for (lp1 = cw->multiText.firstLine;
1036        (lp1 != NULL) &&  !((lp1->y <= y1) && (y1 <= lp1->y + lp1->bbox.ascent + lp1->bbox.descent));
1037        lp1 = lp1->next)
1038     /* move lp1 to start of selection region. */ ;
1039 
1040 
1041   /* now lp1 points to the line where the selection region is starting in. */
1042   /* find the line where the selection ends.                               */
1043 
1044   for (lp2 = lp1;
1045        (lp2 != NULL) &&  !((lp2->y <= y2) && (y2 <= lp2->y + lp2->bbox.ascent + lp2->bbox.descent));
1046        lp2 = lp2->next)
1047     /* lp2 points to where the selection ends. */;
1048 
1049 
1050   /* highlight each line between lp1 and lp2. */
1051   for (lp = lp1; lp != lp2; lp = lp->next)
1052     /* highlight each line... TBA */ ;
1053 }
1054 
1055 
1056 
1057 /*----------------------------------------------------------------------*
1058  |                           StuffOntoClipboard                         |
1059  *----------------------------------------------------------------------*/
ToClipboard(XmMultiTextWidget cw,char * data)1060 void ToClipboard (XmMultiTextWidget cw, char *data)
1061 {
1062   long  clipID = 0;
1063   int            status;
1064   XmString       clipLabel;
1065   char           buf[32];
1066   static int     cnt;
1067 
1068 
1069   sprintf(buf, "%s-%d", data, ++cnt);
1070   clipLabel = XmStringCreate(data, XmSTRING_DEFAULT_CHARSET);
1071 
1072   do
1073     status = XmClipboardStartCopy(XtDisplay(cw), XtWindow(cw),
1074 				  clipLabel, CurrentTime, NULL, NULL, &clipID);
1075   while (status == ClipboardLocked);
1076   XmStringFree(clipLabel);
1077 
1078   do
1079     status = XmClipboardCopy(XtDisplay(cw), XtWindow(cw),
1080 			     clipID, "STRING", buf, (long)strlen(buf)+1, cnt, NULL);
1081   while (status == ClipboardLocked);
1082 
1083   do
1084     status = XmClipboardEndCopy(XtDisplay(cw), XtWindow(cw), clipID);
1085   while (status == ClipboardLocked);
1086 }
1087 
1088 
1089 
1090 /*----------------------------------------------------------------------*
1091  |                               BtnRelease                             |
1092  *----------------------------------------------------------------------*/
BtnRelease(Widget w,XEvent * event,String * params,Cardinal * numParams)1093 static void BtnRelease (Widget w, XEvent* event, String* params, Cardinal* numParams)
1094 {
1095   XmMultiTextWidget                cw = (XmMultiTextWidget) w;
1096   static char                      mondoBuffer[MAX_BUFF_SIZE], *space = " ", *cr = "\n";
1097   LineList                         lp;
1098   WordList                         wp;
1099   XmMultiTextSelectCallbackStruct  callValue;
1100   Boolean                          foundSomething, textIsSelected = FALSE;
1101 
1102 
1103   if (cw->multiText.selecting)
1104     {
1105       cw->multiText.selecting = FALSE;
1106 
1107       strcpy(mondoBuffer,"");
1108 
1109       /* grab the selected text. */
1110       foundSomething = FALSE;
1111       for (lp = cw->multiText.firstLine; lp != NULL; lp = lp->next)
1112 	{
1113 	  foundSomething = FALSE;
1114 	  for (wp = lp->firstWord; wp != NULL; wp = wp->next)
1115 	    if (wp->selected)
1116 	      {
1117 		foundSomething = textIsSelected = TRUE;
1118 		strcat(mondoBuffer, wp->chars);
1119 		if (cw->multiText.smartSpacing) strcat(mondoBuffer, space);
1120 	      }
1121 	    else
1122 	      foundSomething = FALSE;
1123 	  if (foundSomething) strcat(mondoBuffer, cr);
1124 	}
1125 
1126       /* Check if the selection callback exists. */
1127       callValue.reason   = XmCR_SELECT;
1128       callValue.event    = event;
1129       callValue.data     = NewString(mondoBuffer);
1130 
1131       if (XtHasCallbacks((Widget)cw, XmNselectCallback) == XtCallbackHasSome)
1132 	{
1133 	  XFlush(XtDisplay(w));
1134 	  XtCallCallbacks((Widget)cw, XmNselectCallback, &callValue);
1135 	}
1136       XtFree(callValue.data);
1137     }
1138 
1139   /*
1140   if (textIsSelected) ToClipboard(cw, mondoBuffer);
1141   */
1142 
1143   cw->multiText.textIsSelected = textIsSelected;
1144 }
1145 
1146 
1147 
1148 
1149 
1150 /*----------------------------------------------------------------------*
1151  |                              Refresh                                 |
1152  | causes a complete redraw of the text widget.                         |
1153  *----------------------------------------------------------------------*/
Refresh(Widget w,XEvent * event,String * params,Cardinal * numParams)1154 static void Refresh (Widget w, XEvent* event, String* params, Cardinal* numParams)
1155 {
1156   XmMultiTextWidget cw = (XmMultiTextWidget) w;
1157   LineList          lp = cw->multiText.firstLine;
1158 
1159 
1160   XmMultiTextClearText((Widget)cw);
1161 
1162   while (lp != NULL)
1163     {
1164       DrawLine(cw, lp);
1165       lp = lp->next;
1166     }
1167 }
1168 
1169 
1170 
1171 /*----------------------------------------------------------------------*
1172  |                        DeleteLineFromWidget                          |
1173  *----------------------------------------------------------------------*/
DeleteLineFromWidget(XmMultiTextWidget cw,LineList * lpp)1174 void DeleteLineFromWidget (XmMultiTextWidget cw, LineList *lpp)
1175 {
1176   LineList temp;
1177 
1178 
1179   if (*lpp == NULL) return;
1180 
1181   if ((*lpp)->prev == NULL)
1182     cw->multiText.firstLine = NULL;
1183   else
1184     (*lpp)->prev->next = (*lpp)->next;
1185 
1186   if ((*lpp)->next == NULL)
1187     cw->multiText.lastLine = (*lpp)->prev;
1188   else
1189     (*lpp)->next->prev = (*lpp)->prev;
1190 
1191 
1192   if ((*lpp)->prev == NULL)
1193     temp = (*lpp)->next;
1194   else
1195     temp = (*lpp)->prev;
1196 
1197   FreeLineRecord(cw, *lpp);
1198 
1199   *lpp = temp;
1200 }
1201 
1202 
1203 
1204 
1205 /*----------------------------------------------------------------------*
1206  |                           LineTooLong                                |
1207  | A line is too long if it has more than one word AND doesn't fit into |
1208  | the widget AND the last word is allowed to be wrapped.               |
1209  *----------------------------------------------------------------------*/
LineTooLong(XmMultiTextWidget cw,LineList lp)1210 Boolean LineTooLong (XmMultiTextWidget cw, LineList lp)
1211 {
1212   /* if the line is null or empty, return false. */
1213   if ((lp == NULL) || (lp->lastWord == NULL))
1214     return (FALSE);
1215 
1216   /* if the line is too long and the last word is 'wrappable', then return true. */
1217   if (((lp->x + lp->bbox.width)  >  (cw->core.width - cw->multiText.marginWidth)) &&
1218       (lp->lastWord->wordWrapping && cw->multiText.wordWrap))
1219     return (TRUE);
1220   else
1221     return (FALSE);
1222 }
1223 
1224 
1225 /*----------------------------------------------------------------------*
1226  |                      WrapLastWordToNextLine                          |
1227  | this routine assumes that the last word is allowed to be wrapped. In |
1228  | other words - it just performs the wrapping and does what it's told. |
1229  *----------------------------------------------------------------------*/
WrapLastWordToNextLine(XmMultiTextWidget cw,LineList lp)1230 void WrapLastWordToNextLine (XmMultiTextWidget cw, LineList lp)
1231 {
1232   int      dx;
1233   WordList wp;
1234 
1235 
1236   /* let's do some simple checking - these errors should never occur... */
1237   if ((lp == NULL) || (lp->lastWord == NULL) || (lp->lastWord == lp->firstWord))
1238     return;
1239 
1240   wp              = lp->lastWord;
1241   lp->lastWord    = wp->prev;
1242   dx              = lp->bbox.width - lp->lastWord->x + lp->lastWord->bbox.width;
1243   lp->bbox.width -= dx;
1244   lp->remaining  += dx;
1245   lp->wordCount  --;
1246 
1247 
1248   /* if this is the last line, then open a new line after this one. */
1249 
1250   /*********
1251   if (lp->next == NULL) OpenNewLine();
1252   *********/
1253 
1254   wp->y           = lp->next->bbox.ascent - wp->bbox.ascent;
1255   wp->tabCount    = 0;
1256   wp->spaceCount  = 0;
1257   wp->linePtr     = lp->next;
1258 
1259   /* now shift horizontally all words in the line. */
1260   /*****
1261   if the a word's tabcount is < next word's tabcount, then the next char is a tab.
1262   do the same for the number of spaces...
1263   *****/
1264 
1265   /* now adjust the ascent or descent of the line if necessary */
1266 
1267 }
1268 
1269 
1270 
1271 /*----------------------------------------------------------------------*
1272  |                              Reflow                                  |
1273  | reflows all lines after the lineIndex so each line fits correctly    |
1274  | into the newly sized window.                                         |
1275  *----------------------------------------------------------------------*/
Reflow(XmMultiTextWidget cw,LineList reflp)1276 void Reflow (XmMultiTextWidget cw, LineList reflp)
1277 {
1278   LineList lp;
1279 
1280 
1281   /* start reflowing after the current line unless lp is set to NULL. */
1282   /* in that case, reflow all lines.                                  */
1283   if (reflp == NULL)
1284     lp = cw->multiText.firstLine;
1285   else
1286     lp = reflp->next;
1287 
1288   for (; lp != NULL; lp = lp->next)
1289     {
1290       while (LineTooLong(cw, lp))
1291 	WrapLastWordToNextLine(cw, lp);
1292     }
1293 }
1294 
1295 
1296 
1297 /*----------------------------------------------------------------------*
1298  |                        ShiftFollowingLines2                          |
1299  *----------------------------------------------------------------------*/
ShiftFollowingLines2(XmMultiTextWidget cw,LineList indexLine)1300 void ShiftFollowingLines2 (XmMultiTextWidget cw, LineList indexLine)
1301 {
1302   LineList lp, startLine;
1303 
1304   if (indexLine == NULL)
1305     startLine = cw->multiText.firstLine;
1306   else
1307     startLine = indexLine;
1308   if (startLine == NULL) return;
1309 
1310   for (lp = startLine; (lp != NULL) && (lp->next != NULL); lp = lp->next)
1311     {
1312       lp->next->y = lp->y + lp->bbox.ascent + lp->bbox.descent + lp->lineSpacing;
1313     }
1314 }
1315 
1316 
1317 /*----------------------------------------------------------------------*
1318  |                        DeleteWordFromLine                            |
1319  *----------------------------------------------------------------------*/
DeleteWordFromLine(XmMultiTextWidget cw,WordList wp)1320 void DeleteWordFromLine (XmMultiTextWidget cw, WordList wp)
1321 {
1322   if (wp == NULL) return;
1323 
1324 
1325   if (wp->prev == NULL)
1326     wp->linePtr->firstWord = wp->next;
1327   else
1328     wp->prev->next = wp->next;
1329 
1330   if (wp->next == NULL)
1331     wp->linePtr->lastWord = wp->prev;
1332   else
1333     wp->next->prev = wp->prev;
1334 
1335   wp->linePtr->wordCount --;
1336   wp->linePtr->remaining += wp->bbox.width;
1337 
1338   FreeWordRecord(cw, wp);
1339 }
1340 
1341 
1342 
1343 /*----------------------------------------------------------------------*
1344  |                               Cut                                    |
1345  *----------------------------------------------------------------------*/
1346 static void
Cut(Widget w,XEvent * event,String * params,Cardinal * numParams)1347 Cut (Widget w, XEvent* event, String* params, Cardinal* numParams)
1348 {
1349   XmMultiTextWidget  cw = (XmMultiTextWidget) w;
1350   LineList           lp;
1351   WordList           wp;
1352 
1353 
1354   /* go through all text and find the selected text. */
1355   /* this should probably not keep checking after the first chunk of selected text. */
1356   /* however, future versions may allow multiple selections. */
1357 
1358   for (lp = cw->multiText.firstLine; lp != NULL; lp = lp->next)
1359     for (wp = lp->firstWord; wp != NULL; wp = wp->next)
1360       if (wp->selected)
1361 	DeleteWordFromLine(cw, wp);
1362 
1363   Reflow(cw, NULL);
1364 }
1365 
1366 
1367 
1368 
1369 /*----------------------------------------------------------------------*
1370  |                            GetWholeLink                              |
1371  *----------------------------------------------------------------------*/
GetWholeLink(Widget w,WordList wp)1372 static char *GetWholeLink (Widget w, WordList wp)
1373 {
1374   WordList           wpStart, wpTemp;
1375   char              *buf = "\0";
1376   int                len = 0;;
1377 
1378   if (!wp->linkInfo) return(buf);
1379 
1380   wpStart = wp;
1381   while ((wpStart->prev != NULL) && (SameLinks(wpStart->prev, wp)))
1382     wpStart = wpStart->prev;
1383 
1384 
1385   /* scan forward in the line till the end of the line or the end of the line. */
1386   /* buf first find out how long this string is going to be.                   */
1387 
1388   wpTemp = wpStart;
1389   while ((wpTemp != NULL) && (SameLinks(wpTemp, wp)))
1390     {
1391       len += strlen(wpTemp->chars) + 1;
1392       wpTemp = wpTemp->next;
1393     }
1394   buf = (char *) XtMalloc(sizeof(char) * len + 1);
1395   strcpy(buf, "");
1396 
1397   while ((wpStart != NULL) && (SameLinks(wpStart, wp)))
1398     {
1399       if (strlen(buf))
1400 	sprintf(buf,"%s %s", buf, wpStart->chars);
1401       else
1402 	strcpy(buf, wpStart->chars);
1403       wpStart = wpStart->next;
1404     }
1405 
1406   return(buf);
1407 }
1408 
1409 
1410 
1411 /*----------------------------------------------------------------------*
1412  |                            DeselectAll                               |
1413  *----------------------------------------------------------------------*/
DeselectAll(XmMultiTextWidget cw)1414 static void DeselectAll (XmMultiTextWidget cw)
1415 {
1416   LineList lp;
1417   WordList wp;
1418 
1419   for (lp = cw->multiText.firstLine; lp != NULL; lp = lp->next)
1420     for (wp = lp->firstWord; wp != NULL; wp = wp->next)
1421       if (wp->selected)
1422 	{
1423 	  DrawWord(cw, wp, !wp->selected);
1424 	  wp->selected = FALSE;
1425 	}
1426 
1427   cw->multiText.textIsSelected = FALSE;
1428 }
1429 
1430 
1431 
1432 /*-----------------------=======================------------------------*
1433  |                       MoveTo action procedure                        |
1434  | This routine is invoked (default case) on a Btn1Down event occurs in |
1435  | the widget's window.  It then positions the cursor at the new        |
1436  | location.  Well, actually it does all sorts of things - none of which|
1437  | is permanent, so I really won't go into a full description here, yet.|
1438  *-----------------------=======================------------------------*/
MoveTo(Widget w,XEvent * event,String * params,Cardinal * numParams)1439 static void MoveTo (Widget    w,
1440 		    XEvent*   event,
1441 		    String*   params,
1442 		    Cardinal* numParams)
1443 {
1444     XmMultiTextWidget              cw        = (XmMultiTextWidget) w;
1445     LineList                       lp        = cw->multiText.firstLine;
1446     LineList                       clp;
1447     int                            actualX;
1448     WordList                       wp;
1449     Boolean                        foundLine = FALSE;
1450     Boolean                        foundWord = FALSE;
1451     XmMultiTextLinkCallbackStruct  callValue;
1452 
1453 
1454   /* First scan all the lines until correct line is found. */
1455   while ((lp != NULL) && !foundLine)
1456     {
1457       foundLine = PtInLine(event->xbutton.x, event->xbutton.y, lp);
1458       if (!foundLine) lp = lp->next;
1459     }
1460 
1461 
1462   /* Now scan the words to find the word in this line */
1463   if (foundLine)
1464     {
1465       wp = lp->firstWord;
1466 
1467       while ((wp != NULL) && !foundWord)
1468 	{
1469 	  foundWord = PtInWord(event->xbutton.x, event->xbutton.y, wp, lp);
1470 	  if (!foundWord) wp = wp->next;
1471 	}
1472 
1473       /* Check if this is a Link.  If so, setup the callback. */
1474       if ((foundWord) && (wp->linkInfo != NULL) && (wp->linkInfo->linkType != NOOP))
1475 	{
1476 	  callValue.reason   = XmCR_LINK;
1477 	  callValue.event    = event;
1478 	  callValue.type     = wp->linkInfo->linkType;
1479 	  callValue.posn     = NewString(wp->linkInfo->linkPosn);
1480 	  callValue.data     = NewString(wp->linkInfo->linkData);
1481 	  callValue.word     = GetWholeLink(w, wp);
1482 
1483 	  if (XtHasCallbacks((Widget)cw, XmNlinkCallback) == XtCallbackHasSome)
1484 	    {
1485 	      XFlush(XtDisplay(w));
1486 	      XtCallCallbacks((Widget)cw, XmNlinkCallback, &callValue);
1487 	      XtFree(callValue.posn); XtFree(callValue.data); XtFree(callValue.word);
1488 	      return;
1489 	    }
1490 	  XtFree(callValue.posn); XtFree(callValue.data); XtFree(callValue.word);
1491 	}
1492       else
1493 	DeselectAll((XmMultiTextWidget)cw);
1494 
1495       PositionCursor(cw, event->xbutton.x, event->xbutton.y, &clp, &actualX);
1496       UpdateCursor(cw, clp, event->xbutton.x, actualX, clp->y + clp->bbox.ascent);
1497     }
1498   else
1499     DeselectAll((XmMultiTextWidget)cw);
1500 
1501 
1502   /* now check for selections... */
1503   cw->multiText.selecting      = TRUE;
1504 
1505   /* set the widgets selection points. */
1506   cw->multiText.startX = event->xbutton.x;
1507   cw->multiText.startY = event->xbutton.y;
1508 
1509   /* that's all the setup that's necessary - actions take care of the rest. */
1510 }
1511 
1512 
1513 
1514 
1515 /*----------------------------------------------------------------------*
1516  |                             ChangeFocus                              |
1517  *----------------------------------------------------------------------*/
ChangeFocus(Widget w,XEvent * event,String * params,Cardinal * numParams)1518 static void ChangeFocus(Widget w, XEvent* event, String* params, Cardinal* numParams)
1519 {
1520 
1521 }
1522 
1523 
1524 
1525 /*----------------------------------------------------------------------*
1526  |                                Enter                                 |
1527  *----------------------------------------------------------------------*/
Enter(Widget w,XEvent * event,String * params,Cardinal * numParams)1528 static void Enter (Widget w, XEvent* event, String* params, Cardinal* numParams)
1529 {
1530 
1531 }
1532 
1533 
1534 
1535 /*----------------------------------------------------------------------*
1536  |                                Leave                                 |
1537  *----------------------------------------------------------------------*/
Leave(Widget w,XEvent * event,String * params,Cardinal * numParams)1538 static void Leave (Widget w, XEvent* event, String* params, Cardinal* numParams)
1539 {
1540 
1541 }
1542 
1543 
1544 
1545 /*----------------------------------------------------------------------*
1546  |                              InFocus                                 |
1547  *----------------------------------------------------------------------*/
InFocus(Widget w,XEvent * event,String * params,Cardinal * numParams)1548 static void InFocus (Widget w, XEvent* event, String* params, Cardinal* numParams)
1549 {
1550 
1551 }
1552 
1553 
1554 
1555 /*----------------------------------------------------------------------*
1556  |                              OutFocus                                |
1557  *----------------------------------------------------------------------*/
OutFocus(Widget w,XEvent * event,String * params,Cardinal * numParams)1558 static void OutFocus (Widget w, XEvent* event, String* params, Cardinal* numParams)
1559 {
1560 
1561 }
1562 
1563 
1564 
1565 
1566 /*----------------------------------------------------------------------*
1567  |                             DeleteLine                               |
1568  *----------------------------------------------------------------------*/
DeleteLine(Widget w,XKeyPressedEvent * event,String * argv,Cardinal * argc)1569 static void DeleteLine (Widget w, XKeyPressedEvent *event, String *argv, Cardinal *argc)
1570 {
1571   /* argv hold a single argument telling if the line should be deleted */
1572   /* from the TOP or the BOTTOM.                                       */
1573 
1574   switch (atoi(*argv))
1575     {
1576     case TOP:
1577       XmMultiTextDeleteLineTop(w, 1);
1578       break;
1579 
1580     case BOTTOM:
1581       XmMultiTextDeleteLineBottom(w);
1582       break;
1583 
1584     default: ;
1585     }
1586 }
1587 
1588 
1589 
1590 
1591 /*----------------------------------------------------------------------*
1592  |                              OpenLineTop                             |
1593  *----------------------------------------------------------------------*/
OpenLineTop(Widget w,XEvent * event,String * params,Cardinal * numParams)1594 static void OpenLineTop (Widget w, XEvent* event, String* params, Cardinal* numParams)
1595 {
1596   XmMultiTextOpenLineTop (w, 0);
1597 }
1598 
1599 
1600 
1601 
1602 /*----------------------------------------------------------------------*
1603  |                              AddWordTop                              |
1604  *----------------------------------------------------------------------*/
AddWordTop(Widget w,XEvent * event,String * params,Cardinal * numParams)1605 static void AddWordTop (Widget w, XEvent* event, String* params, Cardinal* numParams)
1606 {
1607   static int i=1;
1608   char *str="testing    ";
1609 
1610   sprintf(str, "testing%d", i++);
1611   XmMultiTextAppendWordTop (w, str, "hodges-24", "red", 0, NULL);
1612 }
1613 
1614 
1615 
1616 
1617 /*======================================================================*
1618  |                Xmmultitext Widget Public Procedures                  |
1619  |                ------------------------------------                  |
1620  | The(se) routine(s) are defined at the beginning of the file in the   |
1621  | list of actions.                                                     |
1622  *======================================================================*/
1623 
1624 
1625 /*-----------------------====================---------------------------*
1626  |                       XmMultiTextClearText                           |
1627  *-----------------------====================---------------------------*/
XmMultiTextClearText(Widget w)1628 void XmMultiTextClearText (Widget w)
1629 {
1630   XmMultiTextWidget  cw = (XmMultiTextWidget) w;
1631   LineList           lp, lpNext;
1632 
1633   lp = cw->multiText.firstLine;
1634 
1635   while (lp != NULL) {
1636     lpNext = lp->next;
1637     FreeLineRecord(cw, lp);
1638     XtFree((char*)lp);
1639     lp = lpNext;
1640   }
1641 
1642   cw->multiText.firstLine   = NULL;
1643   cw->multiText.currentLine = NULL;
1644   cw->multiText.lastLine    = NULL;
1645 
1646   /* clean up all the cursor stuff... */
1647   if (cw->multiText.cursorFg != None)
1648     {
1649       XFreePixmap(XtDisplay(cw), cw->multiText.cursorFg);
1650       XFreePixmap(XtDisplay(cw), cw->multiText.cursorBg);
1651       XFreePixmap(XtDisplay(cw), cw->multiText.cursorMask);
1652     }
1653   if (cw->multiText.blinkTimeOutID != 0) XtRemoveTimeOut(cw->multiText.blinkTimeOutID);
1654   cw->multiText.blinkTimeOutID = 0;
1655   cw->multiText.selecting      = FALSE;
1656   cw->multiText.textIsSelected = FALSE;
1657   cw->multiText.cursorFg       = None;
1658   cw->multiText.cursorBg       = None;
1659   cw->multiText.cursorMask     = None;
1660 
1661   XClearArea(XtDisplay(cw), XtWindow(cw), 0, 0, 0, 0, TRUE);
1662 
1663   XSync(XtDisplay(cw), False);
1664   ChangeHeight((XmMultiTextWidget)w, XtParent(w)->core.height);
1665 }
1666 
1667 
1668 
1669 /*----------------------======================--------------------------*
1670  |                      XmMultiTextGetPosition                          |
1671  | Returns the last position of the data already entered.  This tells   |
1672  | where new data will be appended.                                     |
1673  *----------------------======================--------------------------*/
XmMultiTextGetPosition(Widget w)1674 int XmMultiTextGetPosition (Widget w)
1675 {
1676   XmMultiTextWidget cw = (XmMultiTextWidget) w;
1677 
1678 
1679   if (cw->multiText.lastLine == NULL)
1680     return(0);
1681   else
1682     return(cw->multiText.lastLine->y);
1683 }
1684 
1685 
1686 
1687 /*-------------------============================-----------------------*
1688  |                   XmMultiTextGetCursorPosition                       |
1689  | Returns the last position of the data already entered.  This tells   |
1690  | where new data will be appended.                                     |
1691  *-------------------============================-----------------------*/
XmMultiTextGetCursorPosition(Widget w)1692 int XmMultiTextGetCursorPosition (Widget w)
1693 {
1694   XmMultiTextWidget cw = (XmMultiTextWidget) w;
1695 
1696   return(cw->multiText.cursorY);
1697 }
1698 
1699 
1700 
1701 /*-----------------------======================-------------------------*
1702  |                       XmMultiTextQueryCursor                         |
1703  *-----------------------======================-------------------------*/
XmMultiTextQueryCursor(Widget w,int * lineNumber,int * y)1704 void XmMultiTextQueryCursor (Widget w, int *lineNumber, int *y)
1705 {
1706   XmMultiTextWidget cw = (XmMultiTextWidget) w;
1707   LineList          lp;
1708 
1709   *lineNumber = 0;
1710 
1711   for (lp = cw->multiText.firstLine;  lp != NULL;  lp = lp->next)
1712     {
1713       if (lp->y + lp->bbox.ascent + lp->bbox.descent  >  cw->multiText.cursorY)
1714 	break;
1715       *lineNumber = *lineNumber + 1;
1716     }
1717 
1718   if (lp == NULL)
1719     if (cw->multiText.lastLine != NULL)
1720       *y = cw->multiText.lastLine->y;
1721     else
1722       {
1723 	*y          = 0;
1724 	*lineNumber = 0;
1725       }
1726   else
1727     *y = lp->y;
1728 }
1729 
1730 
1731 
1732 /*-----------------------======================-------------------------*
1733  |                       XmMultiTextDeselectAll                         |
1734  *-----------------------======================-------------------------*/
XmMultiTextDeselectAll(Widget w)1735 void XmMultiTextDeselectAll (Widget w)
1736 {
1737   XmMultiTextWidget   cw = (XmMultiTextWidget) w;
1738 
1739   if (XtIsRealized((Widget)cw))
1740     DeselectAll(cw);
1741 }
1742 
1743 
1744 
1745 /*----------------------======================--------------------------*
1746  |                      XmMultiTextSetTabStops                          |
1747  *----------------------======================--------------------------*/
XmMultiTextSetTabStops(Widget w,int tabList[],int tabCount)1748 void XmMultiTextSetTabStops (Widget w, int tabList[], int tabCount)
1749 {
1750   XmMultiTextWidget   cw = (XmMultiTextWidget) w;
1751   int                 i;
1752 
1753   /* enter the tabs into the widget. */
1754   for (i=0; i<tabCount; i++)
1755     cw->multiText.tabs[i] = tabList[i];
1756 
1757   cw->multiText.tabCount = tabCount;
1758 }
1759 
1760 
1761 
1762 
1763 /*---------------------=========================------------------------*
1764  |                     XmMultiTextMakeLinkRecord                        |
1765  *---------------------=========================------------------------*/
XmMultiTextMakeLinkRecord(Widget w,LinkType linkType,char * linkPosn,char * linkData)1766 LinkInfoPtr XmMultiTextMakeLinkRecord (Widget w, LinkType linkType, char *linkPosn, char *linkData)
1767 {
1768   LinkInfoPtr li;
1769 
1770   li = (LinkInfoPtr) XtMalloc(sizeof(struct LinkRec));
1771   if (li == NULL) XtError(MSG3);
1772   else
1773     {
1774       li->linkType = linkType;
1775       li->linkPosn = NewString(linkPosn);
1776       li->linkData = NewString(linkData);
1777     }
1778 
1779   return(li);
1780 }
1781 
1782 
1783 
1784 /*---------------------========================-------------------------*
1785  |                     XmMultiTextAppendNewLine                         |
1786  | (future version...) a new line should be treated as a word and should|
1787  | be assigned a font as any other word.  This font should be used to   |
1788  | determine the height of the new line. If a new word is inserted into |
1789  | the new line that is shorter, the line shouldn't shrink to match the |
1790  | new height until the font of the newline is changed.                 |
1791  *---------------------========================-------------------------*/
XmMultiTextAppendNewLine(Widget w,char * theWord,char * fontName,int indent)1792 void XmMultiTextAppendNewLine (Widget w, char *theWord, char *fontName, int indent)
1793 {
1794   XmMultiTextWidget  cw = (XmMultiTextWidget) w;
1795   LineList           lp;
1796   XFontStruct       *font;
1797   int                direction, fontAscent, fontDescent, yposn;
1798   XCharStruct        boundingBox;
1799 
1800 
1801   /* Create a new line with the height of the current font.                           */
1802 
1803   /* there are two cases, either the line before has some words in it, or it doesn't. */
1804   /* if there are words in the previous line, use its height to figure the new posn.  */
1805   /* if there are no words, then calculate the new posn using the current font.       */
1806 
1807   /* Case 0 - No lines in widget yet. */
1808   if (cw->multiText.firstLine == NULL)
1809     {
1810       font = NamedFont(cw, fontName);
1811       if (!font)
1812       {
1813 	return;
1814       }
1815       XTextExtents(font, theWord, strlen(theWord),
1816 		   &direction, &fontAscent, &fontDescent, &boundingBox);
1817       if (fontAscent  != boundingBox.ascent)  boundingBox.ascent  = fontAscent;
1818       if (fontDescent != boundingBox.descent) boundingBox.descent = fontDescent;
1819       yposn = cw->multiText.marginHeight;
1820 
1821       lp                        = NewLine(cw, indent, yposn, DEFAULT_LINE_SPACING);
1822       lp->bbox.ascent           = fontAscent;
1823       lp->bbox.descent          = fontDescent;
1824       cw->multiText.firstLine   = lp;
1825       cw->multiText.lastLine    = lp;
1826     }
1827   else
1828 
1829 
1830   /* Case 1 - Previous line has words in it already.                 */
1831   /* Use the previous word for height info - sorta logical, I guess. */
1832   if (cw->multiText.lastLine->firstWord != NULL)
1833     {
1834       yposn = cw->multiText.lastLine->y +
1835 	      cw->multiText.lastLine->bbox.ascent + cw->multiText.lastLine->bbox.descent +
1836 	      cw->multiText.lastLine->lineSpacing;
1837 
1838       lp = NewLine(cw, indent, yposn, cw->multiText.lastLine->lineSpacing);
1839       cw->multiText.lastLine->next = lp;
1840       lp->prev = cw->multiText.lastLine;
1841       cw->multiText.lastLine       = lp;
1842 
1843       font = NamedFont(cw, fontName);
1844       if (!font)
1845       {
1846 	return;
1847       }
1848       XTextExtents(font, theWord, strlen(theWord), &direction, &fontAscent, &fontDescent,
1849 		   &boundingBox);
1850       lp->bbox.ascent           = fontAscent;
1851       lp->bbox.descent          = fontDescent;
1852     }
1853 
1854 
1855   /* Case 2 - Previous line has no words in it - probably just a newline. */
1856   else
1857     {
1858       font = NamedFont(cw, fontName);
1859       if (!font)
1860       {
1861 	return;
1862       }
1863       XTextExtents(font, theWord, strlen(theWord), &direction, &fontAscent, &fontDescent,
1864 		   &boundingBox);
1865       if (fontAscent  != boundingBox.ascent)  boundingBox.ascent  = fontAscent;
1866       if (fontDescent != boundingBox.descent) boundingBox.descent = fontDescent;
1867       yposn = cw->multiText.lastLine->y +
1868 	      fontAscent + fontDescent +
1869 	      cw->multiText.lastLine->lineSpacing;
1870 
1871       lp = NewLine(cw, indent, yposn, cw->multiText.lastLine->lineSpacing);
1872       lp->bbox.ascent              = fontAscent;
1873       lp->bbox.descent             = fontDescent;
1874       cw->multiText.lastLine->next = lp;
1875       lp->prev = cw->multiText.lastLine;
1876       cw->multiText.lastLine       = lp;
1877     }
1878 
1879   cw->multiText.currentLine = cw->multiText.lastLine;  /* set this as the current line. */
1880   lp->newLine = TRUE;                          /* tag this newline as a 'hard' newline. */
1881 }
1882 
1883 
1884 
1885 
1886 
1887 /*-----------------------=====================--------------------------*
1888  |                       XmMultiTextAppendWord                          |
1889  | This is called from the outside world.  Words are appended to the    |
1890  | current cursor position and have the following associated traits:    |
1891  | Color, Font, Link.                                                   |
1892  |                                                                      |
1893  | From here, the word will be appended as a single word into the data- |
1894  | structure of the XmMultiTextWidget. Any justification and other text |
1895  | handling is not done here.                                           |
1896  |                                                                      |
1897  | This routine calls the internal routine StoreWord which enters the   |
1898  | new word structure to the current line.  StoreWord returns status    |
1899  | information depending on what occured when the new word was added to |
1900  | the line.                                                            |
1901  | A tab is entered as a complete word.  The next word shouldn't add    |
1902  | any wordspacing.                                                     |
1903  *-----------------------=====================--------------------------*/
XmMultiTextAppendWord(Widget w,char * theWord,char * fontName,char * colorName,int indent,LinkInfoPtr linkInfo)1904 unsigned short XmMultiTextAppendWord (Widget w, char *theWord, char *fontName, char *colorName,
1905 				      int indent, LinkInfoPtr linkInfo)
1906 {
1907   XmMultiTextWidget  cw = (XmMultiTextWidget) w;
1908   WordList           wordPtr;
1909   XFontStruct       *font;
1910   XCharStruct        boundingBox;
1911   unsigned short     status;
1912 
1913 
1914   font = NamedFont (cw, fontName);
1915   if (!font)
1916   {
1917     return 0;
1918   }
1919 
1920   /* get the bounding box for this word (or tab). */
1921   GetBoundingBox(cw, theWord, font, &boundingBox, indent);
1922 
1923   /* build the word record to add to the widget. */
1924   wordPtr                  = (WordList) XtMalloc(sizeof(struct WordRec));
1925   if (wordPtr == NULL) XtError(MSG6);
1926   wordPtr->charList        = NULL;
1927   wordPtr->bbox            = boundingBox;
1928   wordPtr->x               = 0;
1929   wordPtr->y               = 0;
1930   wordPtr->length          = strlen(theWord);
1931   wordPtr->chars           = NewString(theWord);
1932   wordPtr->font            = font;
1933   wordPtr->spacing         = 0;
1934   wordPtr->color           = Color (cw, colorName);
1935   wordPtr->linkInfo        = linkInfo;
1936   wordPtr->bbox.attributes = 0;
1937   wordPtr->image           = None;
1938   wordPtr->widget          = NULL;
1939   wordPtr->selected        = FALSE;
1940   wordPtr->linePtr         = NULL;
1941   wordPtr->next            = NULL;
1942   wordPtr->prev            = NULL;
1943 
1944   /* append the word structure on the widget's current line. */
1945   status = StoreWord(cw, wordPtr, indent);
1946 
1947   return (status);
1948 }
1949 
1950 
1951 /*---------------------========================-------------------------*
1952  |                     XmMultiTextAppendWordTop                         |
1953  | This routine adds a single word to the first line in the MultiText   |
1954  | widget.  It requires that there is a first line.  Additionally, word |
1955  | wrapping isn't yet supported and a warning is issued if the appended |
1956  | word is supposed to wrap.  In any case, the word will be appended as |
1957  | though word wrapping is off.                                         |
1958  | After the word is inserted, all lines after the current line must be |
1959  | updated to reflect the new height changes (if necessary).  The prev  |
1960  | pointer of the following line must also be changed.                  |
1961  *---------------------========================-------------------------*/
XmMultiTextAppendWordTop(Widget w,char * theWord,char * fontName,char * colorName,int indent,LinkInfoPtr linkInfo)1962 unsigned short XmMultiTextAppendWordTop (Widget w,  char *theWord, char *fontName,
1963 						char *colorName, int indent, LinkInfoPtr linkInfo)
1964 {
1965   XmMultiTextWidget  cw = (XmMultiTextWidget) w;
1966   WordList           wordPtr;
1967   XFontStruct       *font;
1968   XCharStruct        boundingBox;
1969   unsigned short     status;
1970 
1971 
1972   /* this routine won't work if there is no top-line to append the new word. */
1973   /* maybe later, I'll automatically create a new line in this case. But for */
1974   /* now, just return and don't append the new word.                         */
1975   if (cw->multiText.firstLine == NULL) {
1976     XtWarning(MSG9);
1977     return(APPEND_ERROR);
1978   }
1979 
1980 
1981   font = NamedFont (cw, fontName);
1982   if (!font)
1983   {
1984     return 0;
1985   }
1986 
1987   /* get the bounding box for this word (or tab). */
1988   GetBoundingBox(cw, theWord, font, &boundingBox, indent);
1989 
1990   /* build the word record to add to the widget. */
1991   wordPtr                  = (WordList) XtMalloc(sizeof(struct WordRec));
1992   if (wordPtr == NULL) XtError(MSG6);
1993   wordPtr->charList        = NULL;
1994   wordPtr->bbox            = boundingBox;
1995   wordPtr->x               = 0;
1996   wordPtr->y               = 0;
1997   wordPtr->length          = strlen(theWord);
1998   wordPtr->chars           = NewString(theWord);
1999   wordPtr->font            = font;
2000   wordPtr->spacing         = 0;
2001   wordPtr->color           = Color (cw, colorName);
2002   wordPtr->linkInfo        = linkInfo;
2003   wordPtr->bbox.attributes = 0;
2004   wordPtr->image           = None;
2005   wordPtr->widget          = NULL;
2006   wordPtr->selected        = FALSE;
2007   wordPtr->linePtr         = NULL;
2008   wordPtr->next            = NULL;
2009   wordPtr->prev            = NULL;
2010 
2011   /* append the word structure on the widget's first line. */
2012   status = AppendWordToTopLine(cw, wordPtr, indent);
2013 
2014 
2015   return (status);
2016 }
2017 
2018 
2019 
2020 /*-------------------============================-----------------------*
2021  |                   XmMultiTextAppendOpenLineTop                       |
2022  |                                                                      |
2023  | NOTE - THE NEW LINE IS EMPTY AND HAS NO ASCENT OR DESCENT. YOU MUST  |
2024  | PUT SOMETHING INTO IT OR IT WILL NOT SHOW UP.                        |
2025  |                                                                      |
2026  | (Future...) should handle cases with no lines in the widget yet.     |
2027  *-------------------============================-----------------------*/
XmMultiTextOpenLineTop(Widget w,int indent)2028 void XmMultiTextOpenLineTop (Widget w, int indent)
2029 {
2030   XmMultiTextWidget  cw = (XmMultiTextWidget) w;
2031   LineList           lp;
2032 
2033 
2034 
2035   lp = NewLine(cw, indent, cw->multiText.marginHeight, DEFAULT_LINE_SPACING);
2036 
2037   if (lp != NULL)
2038     {
2039       lp->next = cw->multiText.firstLine;
2040       if (lp->next != NULL) lp->next->prev = lp;
2041       cw->multiText.firstLine = lp;
2042 
2043       /* NOTE - THE ASCENT AND DESCENT OF THE LINE IS ZERO AT THIS POINT. */
2044     }
2045 }
2046 
2047 
2048 
2049 
2050 /*----------------------------------------------------------------------*
2051  |                            ScrollWindow                              |
2052  | Note that a negative dy value scrolls up and a positive scrolls down.|
2053  | This routine assumes that it is completely visible - ie must be a    |
2054  | child of a scrolled window (its working area).  In addition, the     |
2055  | scrolledWindow must be in "applicationDefined" mode.                 |
2056  *----------------------------------------------------------------------*/
ScrollWindow(XmMultiTextWidget cw,int dy)2057 static void ScrollWindow (XmMultiTextWidget cw, int dy)
2058 {
2059   if (!cw->multiText.exposeOnly) TurnOffCursor(cw);
2060 
2061   /*  XFlush(XtDisplay(cw));        */
2062   /*  XSync(XtDisplay(cw), False);  */
2063 
2064   if (dy > 0)      /* SCROLL DOWN - shift the contents of the screen down. */
2065     {
2066       XCopyArea(XtDisplay(cw), XtWindow(cw), XtWindow(cw), cw->multiText.textGC,
2067 		0,               cw->multiText.marginHeight,
2068 		cw->core.width,  cw->core.height - 2*cw->multiText.marginHeight - dy,
2069 		0,               cw->multiText.marginHeight + dy);
2070 
2071       /* draw the lines that fit into the uncovered region. */
2072       XClearArea(XtDisplay(cw), XtWindow(cw),
2073 		 0,              0,
2074 		 cw->core.width, cw->multiText.marginHeight + dy, FALSE);
2075       DoRedrawText(cw, cw->multiText.marginHeight-dy, 2*dy);
2076     }
2077   else
2078 
2079   if (dy < 0)      /* SCROLL UP - shift the contents of the screen up. */
2080     {
2081       XCopyArea(XtDisplay(cw), XtWindow(cw), XtWindow(cw), cw->multiText.textGC,
2082 		0,              cw->multiText.marginHeight + (-dy),
2083 		cw->core.width, cw->core.height - 2*cw->multiText.marginHeight - (-dy),
2084 		0,              cw->multiText.marginHeight);
2085 
2086       XClearArea(XtDisplay(cw), XtWindow(cw),
2087 		 0,              cw->core.height - cw->multiText.marginHeight - (-dy),
2088 		 cw->core.width, (-dy),  FALSE);
2089       DoRedrawText(cw, cw->core.height - cw->multiText.marginHeight - (-dy), (-dy));
2090     }
2091 
2092   if (!cw->multiText.exposeOnly) TurnOnCursor(cw);
2093 }
2094 
2095 
2096 
2097 /*---------------------========================-------------------------*
2098  |                     XmMultiTextDeleteLineTop                         |
2099  *---------------------========================-------------------------*/
XmMultiTextDeleteLineTop(Widget w,int linesToDelete)2100 int XmMultiTextDeleteLineTop (Widget w, int linesToDelete)
2101 {
2102   XmMultiTextWidget  cw = (XmMultiTextWidget) w;
2103   int                dy, i;
2104   LineList           lp = cw->multiText.firstLine;
2105 
2106 
2107   if ((cw->multiText.firstLine == NULL) || (linesToDelete <= 0)) return (0);
2108 
2109   for (i=0; (i<linesToDelete) && (lp != NULL); i++)
2110     {
2111       lp = cw->multiText.firstLine;
2112 
2113       cw->multiText.firstLine = lp->next;
2114       if (lp->next != NULL) lp->next->prev = NULL;
2115 
2116       /* find how much to shift all the lines... note, line's know about the margin height. */
2117       dy = cw->multiText.marginHeight - cw->multiText.firstLine->y;
2118 
2119       ShiftFollowingLines (lp, dy);
2120 
2121       if (cw->multiText.currentLine == lp)
2122 	if (cw->multiText.firstLine->wordCount > 0)
2123 	  UpdateCursor(cw, cw->multiText.firstLine,
2124 		       cw->multiText.firstLine->x + cw->multiText.firstLine->firstWord->x,
2125 		       cw->multiText.firstLine->x + cw->multiText.firstLine->firstWord->x,
2126 		       cw->multiText.firstLine->y + cw->multiText.firstLine->bbox.ascent);
2127 	else
2128 	  UpdateCursor(cw, cw->multiText.firstLine,
2129 		       cw->multiText.firstLine->x,
2130 		       cw->multiText.firstLine->x,
2131 		       cw->multiText.firstLine->y + cw->multiText.firstLine->bbox.ascent);
2132       else
2133 	cw->multiText.cursorY += dy;
2134 
2135       if (!cw->multiText.exposeOnly)
2136 	if (cw->multiText.smoothScroll) ScrollWindow(cw, dy);
2137 
2138       /* if you want to automatically update the widget's height, add that here... */
2139       /*
2140       XtSetArg(args[0], XmNheight, cw->core.height + dy);
2141       XtSetValues(cw, args, 1);
2142       */
2143 
2144       /* now free the memory held by the line (lp). */
2145       FreeLineRecord (cw, lp);
2146     }
2147 
2148   if (!cw->multiText.exposeOnly)
2149     if (!cw->multiText.smoothScroll)
2150       XClearArea(XtDisplay(cw), XtWindow(cw), 0, 0, 0, 0, TRUE);
2151 
2152   return(i);
2153 }
2154 
2155 
2156 
2157 /*-------------------===========================------------------------*
2158  |                   XmMultiTextDeleteLineBottom                        |
2159  *-------------------===========================------------------------*/
XmMultiTextDeleteLineBottom(Widget w)2160 void XmMultiTextDeleteLineBottom (Widget w)
2161 {
2162   XmMultiTextWidget  cw = (XmMultiTextWidget) w;
2163   LineList           lp = cw->multiText.firstLine;
2164 
2165 
2166   lp = cw->multiText.lastLine;
2167   if (lp != NULL)
2168     {
2169       cw->multiText.lastLine = lp->prev;
2170       if (lp->prev != NULL) lp->prev->next = NULL;
2171 
2172       /* if you want to automatically update the widget's height, add that here... */
2173       /*
2174 	dy = lp->bbox.ascent + lp->bbox.descent + lp->lineSpacing;
2175 	XtSetArg(args[0], XmNheight, cw->core.height - dy);
2176 	XtSetValues(cw, args, 1);
2177       */
2178 
2179       /* now free the memory held by the line (lp). */
2180       FreeLineRecord (cw, lp);
2181     }
2182 }
2183 
2184 
2185 
2186 
2187 /*------------------============================------------------------*
2188  |                  XmMultiTextLongestLineLength                        |
2189  *------------------============================------------------------*/
XmMultiTextLongestLineLength(Widget w)2190 int XmMultiTextLongestLineLength (Widget w)
2191 {
2192   XmMultiTextWidget  cw = (XmMultiTextWidget) w;
2193   LineList           lp;
2194   int                widestWidth = 0;
2195 
2196   for (lp = cw->multiText.firstLine; lp != NULL; lp = lp->next)
2197     if (widestWidth  <  (lp->x + lp->bbox.width) )
2198       widestWidth = lp->x + lp->bbox.width;
2199 
2200   return (widestWidth);
2201 }
2202 
2203 
2204 
2205 
2206 
2207 /*-----------------------======================-------------------------*
2208  |                       XmMultiTextAppendImage                         |
2209  *-----------------------======================-------------------------*/
XmMultiTextAppendImage(Widget w,char * theWord,char * fontName,char * colorName,int indent,Pixmap image,unsigned int width,unsigned int height,LinkInfoPtr linkInfo)2210 unsigned short XmMultiTextAppendImage (Widget w, char *theWord, char *fontName, char *colorName,
2211 				       int indent, Pixmap image, unsigned int width,
2212 				       unsigned int height, LinkInfoPtr linkInfo)
2213 {
2214   XmMultiTextWidget  cw = (XmMultiTextWidget) w;
2215   WordList           wordPtr;
2216   XFontStruct       *font;
2217   XCharStruct        boundingBox;
2218 
2219   font = NamedFont (cw, fontName);
2220   if (!font)
2221   {
2222     return 0;
2223   }
2224 
2225   /* get the bounding box for this word (or tab). */
2226   GetBoundingBox(cw, theWord, font, &boundingBox, indent);
2227 
2228   /* build the word record to add to the widget. */
2229   wordPtr                  = (WordList) XtMalloc(sizeof(struct WordRec));
2230   if (wordPtr == NULL) XtError(MSG6);
2231   wordPtr->charList        = NULL;
2232   wordPtr->x               = 0;
2233   wordPtr->y               = 0;
2234   wordPtr->length          = strlen(theWord);    /* doesn't really make sense for images...  */
2235   wordPtr->chars           = NewString(theWord); /* 'theWord' is value returned if selected. */
2236   wordPtr->font            = font;
2237   wordPtr->spacing         = 0;
2238   wordPtr->color           = Color (cw, colorName);
2239   wordPtr->linkInfo        = linkInfo;
2240   wordPtr->selected        = FALSE;
2241   wordPtr->linePtr         = NULL;
2242   wordPtr->next            = NULL;
2243   wordPtr->prev            = NULL;
2244 
2245   /* now setup the image information. */
2246   wordPtr->image           = image;
2247   wordPtr->widget          = NULL;
2248   wordPtr->bbox.lbearing   = 0;
2249   wordPtr->bbox.rbearing   = width;
2250   wordPtr->bbox.width      = width;
2251   wordPtr->bbox.ascent     = (height+1) / 2;
2252   wordPtr->bbox.descent    = height / 2;
2253   wordPtr->bbox.attributes = 0;
2254 
2255 
2256   /* append the word structure on the widget's current line. */
2257   StoreWord(cw, wordPtr, indent);
2258 
2259   return (1);
2260 }
2261 
2262 
2263 
2264 /*-----------------------=======================------------------------*
2265  |                       XmMultiTextAppendWidget                        |
2266  *-----------------------=======================------------------------*/
XmMultiTextAppendWidget(Widget w,char * theWord,char * fontName,char * colorName,int indent,Widget newW)2267 unsigned short XmMultiTextAppendWidget (Widget w, char *theWord, char *fontName, char *colorName,
2268 					int indent, Widget newW)
2269 {
2270   XmMultiTextWidget  cw = (XmMultiTextWidget) w;
2271   WordList           wordPtr;
2272   XFontStruct       *font;
2273   XCharStruct        boundingBox;
2274   unsigned short     status;
2275   int                n, width, height;
2276   Arg                args[5];
2277   int                xoffset;   /* line's indent + margin width */
2278   int                yposn;     /* top of current line          */
2279 
2280 
2281   font = NamedFont (cw, fontName);
2282   if (!font)
2283   {
2284     return 0;
2285   }
2286 
2287   /* get the bounding box for this word (or tab). */
2288   GetBoundingBox(cw, theWord, font, &boundingBox, indent);
2289 
2290   /* build the word record to add to the widget. */
2291   wordPtr                  = (WordList) XtMalloc(sizeof(struct WordRec));
2292   if (wordPtr == NULL) XtError(MSG6);
2293   wordPtr->charList        = NULL;
2294   wordPtr->x               = 0;
2295   wordPtr->y               = 0;
2296   wordPtr->length          = strlen(theWord);    /* doesn't really make sense for widgets... */
2297   wordPtr->chars           = NewString(theWord); /* 'theWord' is value returned if selected. */
2298   wordPtr->font            = font;
2299   wordPtr->spacing         = 0;
2300   wordPtr->color           = Color (cw, colorName);
2301   wordPtr->linkInfo        = NULL;               /* can't have a widget as a link... */
2302   wordPtr->selected        = FALSE;
2303   wordPtr->linePtr         = NULL;
2304   wordPtr->next            = NULL;
2305   wordPtr->prev            = NULL;
2306 
2307   /* find the size of the widget */
2308   n = 0;
2309   XtSetArg(args[n], XmNwidth,  &width);   n++;
2310   XtSetArg(args[n], XmNheight, &height);  n++;
2311   XtGetValues(newW, args, n);
2312 
2313   /* now setup the image information. */
2314   wordPtr->image           = None;
2315   wordPtr->widget          = newW;
2316   wordPtr->bbox.lbearing   = 0;
2317   wordPtr->bbox.rbearing   = width;
2318   wordPtr->bbox.width      = width;
2319   wordPtr->bbox.ascent     = (height+1) / 2;
2320   wordPtr->bbox.descent    = height / 2;
2321   wordPtr->bbox.attributes = 0;
2322 
2323 
2324   /* append the word structure on the widget's current line. */
2325   status = StoreWord(cw, wordPtr, indent);
2326 
2327 
2328   xoffset = wordPtr->linePtr->x;
2329   yposn   = wordPtr->linePtr->y + wordPtr->linePtr->bbox.ascent;
2330 
2331   XtMoveWidget(newW, xoffset + wordPtr->x, yposn - wordPtr->bbox.ascent);
2332   XtManageChild(newW);
2333 
2334   return (status);
2335 }
2336 
2337 
2338 
2339 
2340 /*-----------------------====================---------------------------*
2341  |                       XmMultiTextAppendDPS                           |
2342  *-----------------------====================---------------------------*/
XmMultiTextAppendDPS(Widget w,char * theWord,char * fontName,char * colorName,int indent,char * dpsFileName,unsigned int width,unsigned int height,LinkInfoPtr linkInfo)2343 unsigned short XmMultiTextAppendDPS (Widget w, char *theWord, char *fontName, char *colorName,
2344 				     int indent, char *dpsFileName, unsigned int width,
2345 				     unsigned int height, LinkInfoPtr linkInfo)
2346 {
2347   unsigned short     status;
2348 
2349 
2350 #ifdef NODPS
2351   /* if the systems doesn't support DPS then just replace it with a substitute string. */
2352   status = XmMultiTextAppendWord (w, "Graphics omitted from Online Documentation, please see the manual", fontName, colorName, indent, linkInfo);
2353 
2354 #else
2355 {
2356   XmMultiTextWidget  cw = (XmMultiTextWidget) w;
2357   Pixmap             image;
2358 
2359   if (cw->multiText.dpsCapable)
2360     {
2361       image = GetDPSImage(cw, dpsFileName, &width, &height);
2362       /* append the word structure on the widget's current line.          */
2363       /* Note: 'theWord' is the value returned when an image is selected. */
2364       status = XmMultiTextAppendImage (w, theWord, fontName, colorName, indent,
2365 				       image, width, height, linkInfo);
2366     }
2367   else
2368     {
2369       /* if the systems doesn't support DPS then just replace it with a substitute string. */
2370       status = XmMultiTextAppendWord (w, "Graphics omitted from Online Documentation, please see the manual", fontName, colorName, indent, linkInfo);
2371     }
2372 }
2373 #endif
2374 
2375   return (status != APPEND_ERROR);
2376 }
2377 
2378 
2379 
2380 /*-----------------------=====================--------------------------*
2381  |                       XmMultiTextAppendLine                          |
2382  *-----------------------=====================--------------------------*/
XmMultiTextAppendLine(Widget w,char * str)2383 Boolean XmMultiTextAppendLine (Widget w, char *str)
2384 {
2385   return True;
2386 }
2387 
2388 
2389 /*-----------------------=====================--------------------------*
2390  |                       XmMultiTextAppendChar                          |
2391  | add a character to the current word.  This will cause the current    |
2392  | word to redisplay.  Perhapse the best method is to just show the     |
2393  | character unless the character has a different format than the other |
2394  | letters.  How to manage that is still a mystery...                   |
2395  *-----------------------=====================--------------------------*/
XmMultiTextAppendChar(Widget w,char ch)2396 Boolean XmMultiTextAppendChar (Widget w, char ch)
2397 {
2398   XmMultiTextWidget  cw = (XmMultiTextWidget) w;
2399   WordList           wp;
2400   char              *newWord;
2401 
2402 
2403   if ((cw->multiText.firstLine == NULL) || (cw->multiText.lastLine->firstWord == NULL))
2404     XtWarning("Sorry - this isn't supported yet.");
2405 
2406   else
2407     {
2408       /* stuff this character onto the current word. */
2409       wp = cw->multiText.lastLine->lastWord;
2410       wp->length++;
2411       newWord = (char *) XtMalloc(sizeof(char) * wp->length + 2);
2412       if (newWord == NULL) XtError(MSG6);
2413       sprintf(newWord, "%s%c", wp->chars, ch);
2414       XtFree(wp->chars);
2415       wp->chars = newWord;
2416 
2417       GetBoundingBox(cw, newWord, wp->font, &wp->bbox, cw->multiText.lastLine->indent);
2418 
2419       XClearArea(XtDisplay(w), XtWindow(w),
2420 		 wp->x + cw->multiText.lastLine->x, wp->y + cw->multiText.lastLine->y,
2421 		 wp->bbox.width, wp->bbox.ascent + wp->bbox.descent, TRUE);
2422     }
2423 
2424   return True;
2425 }
2426 
2427 
2428 
2429 
2430 
2431 /*======================================================================*
2432  |               XmMultiText Widget Support Procedures                  |
2433  |               -------------------------------------                  |
2434  *======================================================================*/
2435 
2436 /*----------------------------------------------------------------------*
2437  |                              NewString                               |
2438  *----------------------------------------------------------------------*/
NewString(char * str)2439 static char *NewString(char *str)
2440 {
2441   char *retStr;
2442 
2443   retStr = (char *) XtMalloc(sizeof(char) * (str ? strlen(str) : 0) + 1);
2444   if (retStr == NULL) XtError(MSG6);
2445 
2446   if (str == NULL)
2447     retStr[0] = '\0';
2448   else
2449     strcpy(retStr, str);
2450 
2451   return(retStr);
2452 }
2453 
2454 
2455 /*----------------------------------------------------------------------*
2456  |                            ChangeHeight                              |
2457  *----------------------------------------------------------------------*/
ChangeHeight(XmMultiTextWidget w,Dimension newHeight)2458 static XtGeometryResult ChangeHeight (XmMultiTextWidget w, Dimension newHeight)
2459 {
2460   XtWidgetGeometry  request, replyReturn;
2461   XtGeometryResult  result;
2462 
2463   /* build the request */
2464   request.request_mode = CWHeight;
2465   request.height       = newHeight;
2466 
2467   result = XtMakeGeometryRequest((Widget)w, &request, &replyReturn);
2468 
2469   return (result);
2470 }
2471 
2472 
2473 
2474 /*----------------------------------------------------------------------*
2475  |                           GetBoundingBox                             |
2476  | sets the bounding box of the word.                                   |
2477  *----------------------------------------------------------------------*/
GetBoundingBox(XmMultiTextWidget cw,char * theWord,XFontStruct * font,XCharStruct * boundingBox,int indent)2478 static void GetBoundingBox(XmMultiTextWidget cw, char *theWord, XFontStruct *font,
2479 			   XCharStruct *boundingBox, int indent)
2480 {
2481   int direction, fontAscent, fontDescent, tabWidth;
2482 
2483   /* if the word is a tab, must build bbox from scratch. */
2484   if (theWord[0] == TAB)
2485     {
2486       /* calculate the bounding box depending on the current tab settings. */
2487       /*tabPos =*/ NextTab(cw, &tabWidth, indent);
2488       boundingBox->lbearing   = 0;
2489       boundingBox->rbearing   = tabWidth;
2490       boundingBox->width      = tabWidth;
2491       boundingBox->ascent     = 0;
2492       boundingBox->descent    = 0;
2493       boundingBox->attributes = 0;
2494     }
2495   else
2496     {
2497       XTextExtents(font, theWord, strlen(theWord), &direction, &fontAscent, &fontDescent,
2498 		   boundingBox);
2499       boundingBox->ascent  = fontAscent;
2500       boundingBox->descent = fontDescent;  /* make sure bounding box uses font's ascent,descent */
2501     }
2502 }
2503 
2504 
2505 /*----------------------------------------------------------------------*
2506  |                          ShiftFollowingLines                         |
2507  *----------------------------------------------------------------------*/
ShiftFollowingLines(LineList lines,int dy)2508 static void ShiftFollowingLines (LineList lines, int dy)
2509 {
2510   LineList lp;
2511   WordList wp;
2512 
2513 
2514   for (lp = lines;  lp != NULL;  lp = lp->next)
2515     {
2516       /* it shouldn't be necessary to adjust any of the words in the line */
2517       /* since they are relative to the line's position.                  */
2518 
2519       lp->y += dy;
2520 
2521       /* if the line has widgets - these may need moving.               */
2522       /* though it should probably be handled on redraw, it is faster   */
2523       /* if I handle it here.  That way the checking is done only once. */
2524 
2525       /* Scan though all words in the line and check if it's a widget.  */
2526       /* If so, move it vertically by dy.                               */
2527 
2528       for (wp = lp->firstWord;  wp != NULL;  wp = wp->next)
2529 	if (wp->widget != NULL)
2530 	  XtMoveWidget(wp->widget, wp->widget->core.x, dy + wp->widget->core.y);
2531     }
2532 }
2533 
2534 
2535 
2536 /*----------------------------------------------------------------------*
2537  |                               PtInLine                               |
2538  *----------------------------------------------------------------------*/
PtInLine(int x,int y,LineList lp)2539 static Boolean PtInLine (int x, int y, LineList lp)
2540 {
2541   if (lp == NULL) return FALSE;
2542 
2543   if ((y >= lp->y) && (y < lp->y + lp->bbox.ascent + lp->bbox.descent))
2544     return TRUE;
2545   else
2546     return FALSE;
2547 }
2548 
2549 
2550 
2551 /*----------------------------------------------------------------------*
2552  |                         CalculateSpacing                             |
2553  *----------------------------------------------------------------------*/
CalculateSpacing(Widget w,WordList wp,int * spaceBefore,int * spaceAfter)2554 static void CalculateSpacing (Widget w, WordList wp, int *spaceBefore, int *spaceAfter)
2555 {
2556   *spaceBefore = 0;   /* problems will occur if these aren't initialized. */
2557   *spaceAfter  = 0;
2558 
2559   if (wp->prev == NULL)
2560     *spaceBefore = 0;
2561   else
2562     *spaceBefore = (int)((wp->x - (wp->prev->x + wp->prev->bbox.width)) / 2);
2563 
2564   if (wp->next == NULL)
2565     *spaceAfter = 0;
2566   else
2567     *spaceAfter = (int)((wp->next->x - (wp->x + wp->bbox.width)) / 2 + 1);
2568 }
2569 
2570 
2571 
2572 /*----------------------------------------------------------------------*
2573  |                             SameLinks                                |
2574  | returns true if the links are the same, and false otherwise.         |
2575  *----------------------------------------------------------------------*/
SameLinks(WordList wp1,WordList wp2)2576 static Boolean SameLinks (WordList wp1, WordList wp2)
2577 {
2578   if ((wp1 == NULL) || (wp2 == NULL)) return(FALSE);
2579      else
2580   if ((wp1->linkInfo == NULL) || ((wp2->linkInfo == NULL))) return (FALSE);
2581      else
2582   if (wp1->linkInfo->linkType != wp2->linkInfo->linkType) return(FALSE);
2583      else
2584   if (STRCMP(wp1->linkInfo->linkPosn, wp2->linkInfo->linkPosn)) return(FALSE);
2585      else
2586   return(!STRCMP(wp1->linkInfo->linkData, wp2->linkInfo->linkData));
2587 }
2588 
2589 
2590 /*----------------------------------------------------------------------*
2591  |                           SetCurrentFont                             |
2592  *----------------------------------------------------------------------*/
SetCurrentFont(XmMultiTextWidget w,WordList wp,XFontStruct * font)2593 static void SetCurrentFont (XmMultiTextWidget w, WordList wp, XFontStruct *font)
2594 {
2595   /* to speed things up - only set the font if needed. */
2596   if (wp->font->fid != w->multiText.currentFontID)
2597     {
2598       XSetFont(XtDisplay(w), w->multiText.textGC, wp->font->fid);
2599       w->multiText.currentFontID = wp->font->fid;
2600     }
2601 }
2602 
2603 
2604 /*----------------------------------------------------------------------*
2605  |                           SetCurrentColor                            |
2606  *----------------------------------------------------------------------*/
SetCurrentColor(XmMultiTextWidget w,WordList wp,unsigned long color)2607 static void SetCurrentColor (XmMultiTextWidget w, WordList wp, unsigned long color)
2608 {
2609   /* to speed things up - only set the color if needed. */
2610   if (wp->color != w->multiText.currentColor)
2611     {
2612       XSetForeground(XtDisplay(w), w->multiText.textGC, wp->color);
2613       w->multiText.currentColor = wp->color;
2614     }
2615 }
2616 
2617 
2618 /*----------------------------------------------------------------------*
2619  |                            DisplayImage                              |
2620  *----------------------------------------------------------------------*/
DisplayImage(XmMultiTextWidget w,WordList wp)2621 static void DisplayImage (XmMultiTextWidget w, WordList wp)
2622 {
2623   int xoffset = wp->linePtr->x;				   /* line's indent + margine width */
2624   int yposn   = wp->linePtr->y + wp->linePtr->bbox.ascent; /* top of the current line.      */
2625 
2626 
2627   XCopyArea(XtDisplay(w), wp->image, XtWindow(w), w->multiText.textGC,
2628 	    0, 0, wp->bbox.width, wp->bbox.ascent + wp->bbox.descent,
2629 	    xoffset + wp->x, yposn - wp->bbox.ascent);
2630 }
2631 
2632 
2633 /*----------------------------------------------------------------------*
2634  |                            DisplayWord                               |
2635  | (future...) to fix the problem of jaged selection, erase the full    |
2636  | lineheight before drawing the string.                                |
2637  *----------------------------------------------------------------------*/
DisplayWord(XmMultiTextWidget w,WordList wp)2638 static void DisplayWord (XmMultiTextWidget w, WordList wp)
2639 {
2640   int xoffset = wp->linePtr->x;				   /* line's indent + margine width */
2641   int yposn   = wp->linePtr->y + wp->linePtr->bbox.ascent; /* baseline position             */
2642                                                            /* X always draws strings at the */
2643                                                            /* baseline, not upperleft corner*/
2644   SetCurrentFont(w, wp, wp->font);
2645   SetCurrentColor(w, wp, wp->color);
2646 
2647   if (wp->image != None)
2648     DisplayImage (w, wp);
2649     else
2650   if (wp->widget != NULL)
2651     /* do nothing */;
2652   else
2653     XDrawString(XtDisplay(w), XtWindow(w), w->multiText.textGC, wp->x + xoffset,
2654 		yposn, wp->chars, wp->length);
2655 }
2656 
2657 
2658 
2659 /*----------------------------------------------------------------------*
2660  |                        DisplaySelectedWord                           |
2661  | (future...) to fix the problem of jaged selection, fill the full line|
2662  | height on selections, not only the word's bounding box.              |
2663  | The selection region should eventually be the same as the fill rgn.  |
2664  *----------------------------------------------------------------------*/
DisplaySelectedWord(XmMultiTextWidget w,WordList wp)2665 static void DisplaySelectedWord (XmMultiTextWidget w, WordList wp)
2666 {
2667   Pixel fg      = wp->color;
2668   Pixel bg      = w->core.background_pixel;
2669   int   spaceBefore, spaceAfter;
2670   int   xoffset = wp->linePtr->x;			     /* line's indent + margine width */
2671   int   yposn   = wp->linePtr->y + wp->linePtr->bbox.ascent; /* baseline position             */
2672                                                              /* X always draws strings at the */
2673                                                              /* baseline, not upperleft corner*/
2674   SetCurrentFont(w, wp, wp->font);
2675   SetCurrentColor(w, wp, wp->color);
2676 
2677   CalculateSpacing((Widget)w, wp, &spaceBefore, &spaceAfter);
2678 
2679   /* clear the area first. */
2680   XClearArea(XtDisplay(w), XtWindow(w),
2681 	     xoffset + wp->x - spaceBefore, yposn - wp->bbox.ascent,
2682 	     wp->bbox.width + spaceBefore + spaceAfter,
2683 	     wp->bbox.ascent + wp->bbox.descent, FALSE);
2684 
2685   if (wp->image != None)
2686     DisplayImage (w, wp);
2687     else
2688   if (wp->widget != NULL)
2689     /* do nothing */;
2690   else
2691     XDrawString(XtDisplay(w), XtWindow(w), w->multiText.textGC, wp->x + xoffset,
2692 		yposn, wp->chars, wp->length);
2693 
2694   /* draw an xor box at the mouse position                          */
2695   /* should use... ->primitive.foreground ^ ->core.background_pixel */
2696 
2697   XSetForeground(XtDisplay(w), w->multiText.textGC, WhitePixelOfScreen(XtScreen(w)));
2698   XSetBackground(XtDisplay(w), w->multiText.textGC, BlackPixelOfScreen(XtScreen(w)));
2699   XSetFunction  (XtDisplay(w), w->multiText.textGC, GXxor);
2700 
2701   XFillRectangle(XtDisplay(w), XtWindow(w), w->multiText.textGC,
2702 		 xoffset + wp->x - spaceBefore,
2703 		 yposn - wp->bbox.ascent,
2704 		 wp->bbox.width + spaceBefore + spaceAfter,
2705 		 wp->bbox.ascent + wp->bbox.descent);
2706 
2707   XSetFunction  (XtDisplay(w), w->multiText.textGC, GXcopy);
2708   XSetForeground(XtDisplay(w), w->multiText.textGC, fg);
2709   XSetBackground(XtDisplay(w), w->multiText.textGC, bg);
2710 }
2711 
2712 
2713 /*----------------------------------------------------------------------*
2714  |                       DisplayDeselectedWord                          |
2715  | fix for selections goes here too...                                  |
2716  *----------------------------------------------------------------------*/
DisplayDeselectedWord(XmMultiTextWidget w,WordList wp)2717 static void DisplayDeselectedWord (XmMultiTextWidget w, WordList wp)
2718 {
2719   int   spaceBefore, spaceAfter;
2720   int   xoffset = wp->linePtr->x;			     /* line's indent + margine width */
2721   int   yposn   = wp->linePtr->y + wp->linePtr->bbox.ascent; /* baseline position             */
2722                                                              /* X always draws strings at the */
2723                                                              /* baseline, not upperleft corner*/
2724   SetCurrentFont(w, wp, wp->font);
2725   SetCurrentColor(w, wp, wp->color);
2726 
2727   CalculateSpacing((Widget)w, wp, &spaceBefore, &spaceAfter);
2728 
2729   XClearArea(XtDisplay(w), XtWindow(w),
2730 	     xoffset + wp->x - spaceBefore, yposn - wp->bbox.ascent,
2731 	     wp->bbox.width + spaceBefore + spaceAfter,
2732 	     wp->bbox.ascent + wp->bbox.descent, FALSE);
2733 
2734   if (wp->image != None)
2735     DisplayImage (w, wp);
2736     else
2737   if (wp->widget != NULL)
2738     {
2739       XtUnmapWidget(wp->widget);
2740       XtMapWidget(wp->widget);
2741     }
2742   else
2743     XDrawString(XtDisplay(w), XtWindow(w), w->multiText.textGC, wp->x + xoffset,
2744 		yposn, wp->chars, wp->length);
2745 }
2746 
2747 
2748 
2749 /*----------------------------------------------------------------------*
2750  |                          DrawLinkMarking                             |
2751  | this routine draws the rectangle around a link.                      |
2752  *----------------------------------------------------------------------*/
DrawLinkMarking(XmMultiTextWidget w,WordList wp)2753 static void DrawLinkMarking (XmMultiTextWidget w, WordList wp)
2754 {
2755   int x[4], y[4];
2756   int xoffset = wp->linePtr->x;				   /* line's indent + margine width */
2757   int yposn   = wp->linePtr->y + wp->linePtr->bbox.ascent; /* baseline position             */
2758                                                            /* X always draws strings at the */
2759                                                            /* baseline, not upperleft corner*/
2760   /* don't close the box if the next word is the same link */
2761 
2762   if (SameLinks(wp->next, wp))
2763     x[0] = xoffset + wp->next->x;
2764   else
2765     x[0] = xoffset + wp->x + wp->bbox.width;
2766 
2767   y[0] = yposn - wp->linePtr->bbox.ascent -1;
2768   x[1] = xoffset + wp->x;           y[1] = y[0];
2769   x[2] = x[1];                      y[2] = yposn + wp->linePtr->bbox.descent;
2770   x[3] = x[0];                      y[3] = y[2];
2771 
2772   XDrawLine(XtDisplay(w), XtWindow(w), w->multiText.textGC, x[0], y[0], x[1], y[1]);
2773   XDrawLine(XtDisplay(w), XtWindow(w), w->multiText.textGC, x[2], y[2], x[3], y[3]);
2774   if (!SameLinks(wp->next, wp)) XDrawLine(XtDisplay(w), XtWindow(w), w->multiText.textGC,
2775 					  x[3], y[3], x[0], y[0]);
2776   if (!SameLinks(wp, wp->prev)) XDrawLine(XtDisplay(w), XtWindow(w), w->multiText.textGC,
2777 					  x[1], y[1], x[2], y[2]);
2778 }
2779 
2780 
2781 
2782 /*----------------------------------------------------------------------*
2783  |                             DrawWord                                 |
2784  | xoffset is the left edge of the line.  yposn is the baseline of the  |
2785  | line for this word.                                                  |
2786  | the boolean 'selected' tells if the word becomes selected or not.    |
2787  | if it has the same value as the word's internal value, then don't    |
2788  | change it; ie: if it is not selected and 'selected' is false, then   |
2789  | don't bother clearing the word's bounding box before drawing it.     |
2790  *----------------------------------------------------------------------*/
DrawWord(XmMultiTextWidget w,WordList wp,Boolean selected)2791 static void DrawWord (XmMultiTextWidget w, WordList wp, Boolean selected)
2792 {
2793 
2794   /* this routine just calls the appropriate version of the drawing */
2795   /* routine depending on the type and style of the word.           */
2796 
2797   /* if this is a tab, return */
2798   if (wp->chars[0] == TAB) return;
2799 
2800   /* if you don't want the image to be highlighted when selected, uncomment these lines. */
2801   /*
2802   if (wp->image != NULL) DisplayImage(w, wp);
2803   else
2804   */
2805 
2806 
2807   /* if the word isn't selected and isn't becomming selected, draw normally */
2808   if (!wp->selected && !selected) DisplayWord(w, wp);
2809   else
2810 
2811   /* if the word isn't selected and it should become selected, draw it selected */
2812   if (!wp->selected && selected) DisplaySelectedWord(w, wp);
2813   else
2814 
2815   /* if the word is selected and should become deselected, unselect it. */
2816   if (wp->selected && !selected) DisplayDeselectedWord(w, wp);
2817   else
2818 
2819   /* and finally, if the word was selected and still is, draw it selected */
2820   if (wp->selected && selected) DisplaySelectedWord(w, wp);
2821   else
2822 
2823   /* the above cases should cover all possibilities! */
2824   XtError("DrawWord: detected inconsistency");
2825 
2826 
2827   /* now handle links.  Do whatever drawing is required */
2828   /* to show that this word is a link if needed.        */
2829   if ((wp->linkInfo != NULL) && (wp->linkInfo->linkType != NOOP))
2830     DrawLinkMarking(w, wp);
2831 }
2832 
2833 
2834 
2835 /*----------------------------------------------------------------------*
2836  |                             DrawLine                                 |
2837  | call DrawWord for each word in the line.                             |
2838  *----------------------------------------------------------------------*/
DrawLine(XmMultiTextWidget w,LineList lp)2839 static void DrawLine (XmMultiTextWidget w, LineList lp)
2840 {
2841   WordList wp;
2842   int      i;
2843 
2844   if (lp != NULL)
2845     {
2846       wp = lp->firstWord;
2847 
2848       for (i = 0; i < lp->wordCount; i++)
2849 	{
2850 	  DrawWord(w, wp, wp->selected);
2851 	  wp = wp->next;
2852 	}
2853     }
2854 }
2855 
2856 
2857 
2858 
2859 /*----------------------------------------------------------------------*
2860  |                           ResizeTheLine                              |
2861  | this routine will move each word in the line to the correct baseline |
2862  | position corresponding to its line.                                  |
2863  | theWord is can be passed to this routine to enable a line to be re-  |
2864  | sized before theWord is appended.  If you just want to resize the    |
2865  | line, pass theWord as NULL.                                          |
2866  *----------------------------------------------------------------------*/
ResizeTheLine(XmMultiTextWidget w,LineList lp,WordList theWord)2867 static void ResizeTheLine (XmMultiTextWidget w, LineList lp, WordList theWord)
2868 {
2869   WordList  wp;
2870   Dimension newHeight;
2871   int       xoffset, yposn, maxAscent, maxDescent;
2872 
2873 
2874   if (lp      == NULL) return;
2875   if (theWord == NULL) theWord = lp->firstWord;
2876   if (theWord == NULL)
2877     {
2878       lp->bbox.ascent  = 0;
2879       lp->bbox.descent = 0;
2880       return;
2881     }
2882 
2883 
2884   /* know that theWord points to a valid word - may be first in line or new word. */
2885 
2886   maxAscent  = theWord->bbox.ascent;
2887   maxDescent = theWord->bbox.descent;
2888   for (wp = lp->firstWord;  wp != NULL;  wp = wp->next)
2889     {
2890       maxAscent  = MAX(maxAscent,  wp->bbox.ascent);
2891       maxDescent = MAX(maxDescent, wp->bbox.descent);
2892     }
2893   lp->bbox.ascent  = maxAscent;
2894   lp->bbox.descent = maxDescent;
2895 
2896 
2897   wp = lp->firstWord;
2898   while (wp != NULL)
2899     {
2900       wp->y = lp->bbox.ascent - wp->bbox.ascent;
2901 
2902       /* check if this is a widget, then move the widget. */
2903       if (wp->widget != NULL)
2904 	{
2905 	  xoffset = wp->linePtr->x;
2906 	  yposn   = wp->linePtr->y + wp->linePtr->bbox.ascent;
2907 	  XtMoveWidget(wp->widget, xoffset + wp->x, yposn - wp->bbox.ascent);
2908 	}
2909 
2910       wp = wp->next;
2911     }
2912 
2913   /* if this line is below the bottom of the widget, then resize the widget to fit. */
2914   newHeight = lp->y + lp->bbox.ascent + lp->bbox.descent;
2915   if (w->core.height < newHeight + w->multiText.marginHeight)
2916     ChangeHeight(w, newHeight + (XtParent(w)->core.height/2));
2917 }
2918 
2919 
2920 
2921 
2922 /*----------------------------------------------------------------------*
2923  |                            FreeCharRecord                            |
2924  *----------------------------------------------------------------------*/
FreeCharRecord(XmMultiTextWidget w,CharList cp)2925 static void FreeCharRecord (XmMultiTextWidget w, CharList cp)
2926 {
2927   XtError("MultiText FreeCharRecord:  charlist not initialized correctly");
2928 }
2929 
2930 
2931 
2932 /*----------------------------------------------------------------------*
2933  |                            FreeWordRecord                            |
2934  *----------------------------------------------------------------------*/
FreeWordRecord(XmMultiTextWidget w,WordList wp)2935 static void FreeWordRecord (XmMultiTextWidget w, WordList wp)
2936 {
2937   CharList cp, cpNext;
2938 
2939   cp = wp->charList;
2940 
2941   while (cp != NULL) {
2942     cpNext = cp->next;
2943     FreeCharRecord(w, cp);
2944     XtFree((char*)cp);
2945     cp = cpNext;
2946   }
2947 
2948   if (wp->image    != None)  XFreePixmap(XtDisplay(w), wp->image);
2949   if (wp->widget   != NULL)  XtDestroyWidget(wp->widget);
2950   if (wp->chars    != NULL)  XtFree(wp->chars);
2951   if (wp->linkInfo != NULL)
2952     {
2953       XtFree(wp->linkInfo->linkPosn);
2954       XtFree(wp->linkInfo->linkData);
2955       XtFree((char*)wp->linkInfo);
2956     }
2957   /* if (wp->font     != NULL)  XtFree(wp->font);   -  Don't even think of doing this! */
2958 }
2959 
2960 
2961 
2962 /*----------------------------------------------------------------------*
2963  |                            FreeLineRecord                            |
2964  *----------------------------------------------------------------------*/
FreeLineRecord(XmMultiTextWidget w,LineList lp)2965 static void FreeLineRecord (XmMultiTextWidget w, LineList lp)
2966 {
2967   WordList wp, wpNext;
2968 
2969   wp = lp->firstWord;
2970 
2971   while (wp != NULL) {
2972     wpNext = wp->next;
2973     FreeWordRecord(w, wp);
2974     XtFree((char*)wp);
2975     wp = wpNext;
2976   }
2977 }
2978 
2979 
2980 
2981 /*----------------------------------------------------------------------*
2982  |                               NextTab                                |
2983  *----------------------------------------------------------------------*/
NextTab(XmMultiTextWidget w,int * tabWidth,int indent)2984 static int NextTab (XmMultiTextWidget w, int *tabWidth, int indent)
2985 {
2986   int i, nextTabPos, currentPos;
2987 
2988   /* first find out where we are. */
2989   if ((w->multiText.lastLine == NULL) || (w->multiText.lastLine->lastWord == NULL))
2990     currentPos = indent;
2991   else
2992     currentPos = w->multiText.lastLine->lastWord->x + w->multiText.lastLine->lastWord->bbox.width;
2993 
2994   /* there are no tabs in this widget.  nextTabPos is current posn. */
2995   if (w->multiText.tabCount == 0)
2996     {
2997       *tabWidth  = 0;
2998       nextTabPos = currentPos;
2999     }
3000   else
3001     {
3002       /* must hunt for the correct tab... */
3003       for (i=0; ((currentPos > w->multiText.tabs[i]) && (i < w->multiText.tabCount)); i++);
3004 
3005       /* if there was no tab after the current posn, ignore the tab. */
3006       if ((i == w->multiText.tabCount) && (currentPos > w->multiText.tabs[i]))
3007 	nextTabPos = currentPos;
3008       else
3009 	nextTabPos = w->multiText.tabs[i];
3010       *tabWidth  = w->multiText.tabs[i] - currentPos;
3011     }
3012 
3013   return (nextTabPos);
3014 }
3015 
3016 
3017 /*----------------------------------------------------------------------*
3018  |                                Color                                 |
3019  | Note this will only lookup a new color if the name is different than |
3020  | the last one.                                                        |
3021  *----------------------------------------------------------------------*/
Color(XmMultiTextWidget w,char * name)3022 static unsigned long Color (XmMultiTextWidget w, char *name)
3023 {
3024   Colormap      cmap;
3025 
3026   if (STRCMP(w->multiText.lastColorName, name) != 0)
3027     {
3028       cmap                     = DefaultColormap(XtDisplay(w), XScreenNumberOfScreen(XtScreen(w)));
3029       w->multiText.color.pixel = (unsigned long)0;
3030       XParseColor(XtDisplay(w), cmap, name, &w->multiText.color);
3031       XAllocColor(XtDisplay(w), cmap, &w->multiText.color);
3032 
3033       if (w->multiText.lastColorName != NULL) XtFree(w->multiText.lastColorName);
3034       w->multiText.lastColorName = NewString(name);
3035     }
3036 
3037   return (w->multiText.color.pixel);
3038 }
3039 
3040 
3041 /*----------------------------------------------------------------------*
3042  |                               NamedFont                              |
3043  | Note this will only lookup a new font if the name is different than  |
3044  | the last one.                                                        |
3045  *----------------------------------------------------------------------*/
NamedFont(XmMultiTextWidget w,char * name)3046 static XFontStruct *NamedFont (XmMultiTextWidget w, char *name)
3047 {
3048   int  width;
3049   char buf[MAX_STR_LEN];
3050   int  i=0;
3051 
3052 
3053   for (i = 0; i < w->multiText.numFonts; ++i)
3054   {
3055     if (STRCMP(w->multiText.fontCache[i].name, name) == 0)
3056       {
3057 	if (w->multiText.smartSpacing)
3058 	  {
3059 	    width = XTextWidth(w->multiText.fontCache[i].font, " ", 1);
3060 	    if (width != 0) w->multiText.wordSpacing = width;
3061 	  }
3062 	else
3063 	  w->multiText.wordSpacing = 0;
3064 
3065 	return(w->multiText.fontCache[i].font);
3066       }
3067   }
3068 
3069   /* We didn't find the font, get the info and add it to our cache */
3070   w->multiText.numFonts++;
3071   w->multiText.fontCache =
3072     (XmMultiTextFontCache)XtRealloc((char *)w->multiText.fontCache,
3073 				    (i + 1) * sizeof (XmMultiTextFontCacheRec));
3074   w->multiText.fontCache[i].name = NewString(name);
3075   w->multiText.fontCache[i].font = XLoadQueryFont(XtDisplay(w), name);
3076   if (w->multiText.fontCache[i].font == NULL)
3077     {
3078       sprintf(buf, MSG4, name, DEFAULT_FONT_NAME);
3079       XtWarning(buf);
3080 
3081       w->multiText.fontCache[i].font = XLoadQueryFont(XtDisplay(w), DEFAULT_FONT_NAME);
3082       if (w->multiText.fontCache[i].font == NULL)
3083 	{
3084 	  sprintf(buf, MSG5, DEFAULT_FONT_NAME);
3085 	  XtWarning(buf);
3086 	  return NULL;
3087 	}
3088     }
3089 
3090   if (w->multiText.smartSpacing)
3091     {
3092       width = XTextWidth(w->multiText.fontCache[i].font, " ", 1);
3093       if (width != 0) w->multiText.wordSpacing = width;
3094     }
3095   else
3096     w->multiText.wordSpacing = 0;
3097 
3098   return (w->multiText.fontCache[i].font);
3099 }
3100 
3101 
3102 
3103 /*----------------------------------------------------------------------*
3104  |                               NewLine                                |
3105  *----------------------------------------------------------------------*/
NewLine(XmMultiTextWidget cw,int indent,int yposn,int lineSpacing)3106 static LineList NewLine (XmMultiTextWidget cw, int indent, int yposn, int lineSpacing)
3107 {
3108   LineList  lp;
3109   Dimension newHeight;
3110 
3111 
3112   lp = (LineList) XtMalloc(sizeof(struct LineRec));
3113   if (lp == NULL) XtError(MSG6);
3114   else
3115     {
3116       lp->firstWord        = NULL;
3117       lp->lastWord         = NULL;
3118       lp->bbox.lbearing    = 0;
3119       lp->bbox.rbearing    = 0;
3120       lp->bbox.width       = 0;
3121       lp->bbox.ascent      = 0;
3122       lp->bbox.descent     = 0;
3123       lp->bbox.attributes  = 0;
3124       lp->x                = indent + cw->multiText.marginWidth;
3125       lp->y                = MAX(yposn, cw->multiText.marginHeight);
3126       lp->wordCount        = 0;
3127       lp->indent           = indent;
3128       lp->remaining        = cw->core.width - 2*cw->multiText.marginWidth - indent;
3129       lp->lineSpacing      = lineSpacing;
3130       lp->next             = NULL;
3131       lp->prev             = NULL;  /* don't want to force the prev to be the last line just yet. */
3132 
3133       /* if this line is below the bottom of the widget, the resize the widget to fit. */
3134       /* this is also done when an existing line is resized.                           */
3135       newHeight = (Dimension)(lp->y + lp->bbox.ascent + lp->bbox.descent);
3136       if (cw->core.height < newHeight + cw->multiText.marginHeight)
3137 	ChangeHeight(cw, newHeight + (XtParent(cw)->core.height/RESIZE_FRACTION));
3138     }
3139 
3140   return (lp);
3141 }
3142 
3143 
3144 
3145 /*----------------------------------------------------------------------*
3146  |                       StuffWordOntoCurrentLine                       |
3147  | each word is stored at an x,y location which is relative to the line |
3148  | that contains this word.  A word's x,y posn IS NOT the location in   |
3149  | the multitext window!                                                |
3150  | If any resizing of the line is needed, it will be handled here.  The |
3151  | calling routine will be notified if any resizing was done so that    |
3152  | any redrawing of the line can be performed.                          |
3153  *----------------------------------------------------------------------*/
StuffWordOntoCurrentLine(XmMultiTextWidget w,LineList lp,WordList wp,int neededWidth,Boolean firstWordOfLine,Boolean isTab,Boolean prevWordIsTab)3154 static Boolean StuffWordOntoCurrentLine (XmMultiTextWidget w, LineList lp, WordList wp,
3155 					 int neededWidth, Boolean firstWordOfLine, Boolean isTab,
3156 					 Boolean prevWordIsTab)
3157 {
3158   Boolean resizeLine = FALSE;
3159 
3160   if (lp->firstWord == NULL)
3161     /* zero, not indent since word offset from lp and is already indented correctly. */
3162     wp->x = 0;
3163   else
3164     /* move current posn to the end of the last word (no spacing included - yet!) */
3165     wp->x = lp->lastWord->x + lp->lastWord->bbox.width;
3166 
3167 
3168   /* now add the spacing that should go before this word.                     */
3169   /* wordspacing is always the spacing used by the font of the previous word. */
3170   if (!isTab && !prevWordIsTab && (lp->firstWord != NULL))
3171     {
3172       wp->x       += w->multiText.wordSpacing   /* lp->wordSpacing */;
3173       wp->spacing  = w->multiText.wordSpacing;
3174     }
3175 
3176 
3177   /* find out if resize is necessary. If so, resize the line. */
3178   if ((wp->bbox.ascent > lp->bbox.ascent) || (wp->bbox.descent > lp->bbox.descent)) {
3179     ResizeTheLine(w, lp, wp);
3180     resizeLine       = TRUE;
3181   }
3182 
3183 
3184   /* append the wordStructure to the line's structure. */
3185   wp->prev = lp->lastWord;  /* each word should know about the previous word. */
3186   if (lp->firstWord == NULL)
3187     lp->firstWord      = wp;
3188   else
3189     lp->lastWord->next = wp;
3190   lp->lastWord = wp;
3191 
3192   /* update the parameters of the line. */
3193   lp->bbox.width += neededWidth;
3194   lp->wordCount  ++;
3195   lp->remaining  -= neededWidth;
3196 
3197   /* update the parameters of the word. */
3198   wp->y           = lp->bbox.ascent - wp->bbox.ascent;
3199   wp->linePtr     = lp;
3200 
3201   return (resizeLine);
3202 }
3203 
3204 
3205 
3206 
3207 /*----------------------------------------------------------------------*
3208  |                            GetNeededWidth                            |
3209  *----------------------------------------------------------------------*/
GetNeededWidth(XmMultiTextWidget w,WordList wPtr,int indent,Boolean * firstWordOfLine,Boolean * isTab,Boolean * prevWordIsTab)3210 static int GetNeededWidth (XmMultiTextWidget w, WordList wPtr, int indent,
3211 			   Boolean *firstWordOfLine, Boolean *isTab, Boolean *prevWordIsTab)
3212 {
3213   int     neededWidth;
3214 
3215   /* lets start with the width of the word. */
3216   neededWidth = wPtr->bbox.width;
3217 
3218   /* special handling for: first word, tabs, and words following tabs. */
3219   *firstWordOfLine = ((w->multiText.lastLine == NULL) ||
3220 		      (w->multiText.lastLine->lastWord == NULL));
3221   *isTab           = (wPtr->chars[0] == TAB);
3222   *prevWordIsTab   = ((w->multiText.lastLine != NULL) &&
3223 		      (w->multiText.lastLine->lastWord != NULL) &&
3224 		      (w->multiText.lastLine->lastWord->chars[0] == TAB));
3225 
3226   /* if this isn't a tab, then add the indent to the amount that this word requires.            */
3227   /* this is added since indented lines are just shifted - bounding boxes don't keep this info. */
3228   if (!isTab) neededWidth += indent;
3229 
3230   if (!(*firstWordOfLine) && !(*prevWordIsTab) && !(*isTab))
3231     neededWidth += w->multiText.wordSpacing; /* spacing of current font is held by widget. */
3232 
3233 
3234   return(neededWidth);
3235 }
3236 
3237 
3238 
3239 /*----------------------------------------------------------------------*
3240  |                               StoreWord                              |
3241  | This routine stores the new word into the current line.  If this is  |
3242  | the first word in the document, then create a new line before        |
3243  | inserting it.  Before appending the current word to the line, check  |
3244  | to make sure that it will fit. The word's size is its bounding plus  |
3245  | the size of the space between it and the previous word.              |
3246  | Finally, if the word doesn't fit, print the previous full line and   |
3247  | create a new line for the word.                                      |
3248  *----------------------------------------------------------------------*/
StoreWord(XmMultiTextWidget w,WordList wordPtr,int indent)3249 static unsigned short StoreWord (XmMultiTextWidget w, WordList wordPtr, int indent)
3250 {
3251   LineList        lp;
3252   Boolean         resizeLine, firstWordOfLine, isTab, prevWordIsTab;
3253   unsigned short  returnVal=0;
3254   int             neededWidth, yposn;
3255 
3256 
3257   /* find the amount of space this word takes.  This includes any indents and wordspacing. */
3258   /* this is used to determine if this word fits onto this line.  (~Word width + indent)   */
3259   neededWidth = GetNeededWidth(w, wordPtr, indent, &firstWordOfLine, &isTab, &prevWordIsTab);
3260 
3261   /* case 1: no lines in the widget -> make a new one and add the word. */
3262   if (w->multiText.firstLine == NULL)
3263     {
3264       lp                     = NewLine(w, indent, 0, DEFAULT_LINE_SPACING);
3265       w->multiText.firstLine = lp;
3266       w->multiText.lastLine  = lp;
3267       resizeLine             = StuffWordOntoCurrentLine(w, lp, wordPtr, neededWidth,
3268 							firstWordOfLine, isTab, prevWordIsTab);
3269       returnVal              = APPEND_FIRST;
3270     }
3271   else
3272 
3273   /* case 2: word doesn't fit on this line and wordwrapping is on. */
3274   if ((neededWidth > w->multiText.lastLine->remaining) && w->multiText.wordWrap)
3275     {
3276       yposn = w->multiText.lastLine->y + w->multiText.lastLine->bbox.ascent +
3277 	      w->multiText.lastLine->bbox.descent + w->multiText.lastLine->lineSpacing;
3278       lp    = NewLine(w, indent, yposn, w->multiText.lastLine->lineSpacing);
3279       w->multiText.lastLine->next = lp;
3280       lp->prev                    = w->multiText.lastLine;
3281       resizeLine                  = StuffWordOntoCurrentLine(w, lp, wordPtr,  neededWidth,
3282 							     firstWordOfLine, isTab,
3283 							     prevWordIsTab);
3284       w->multiText.lastLine       = lp;
3285       returnVal                   = APPEND_FIRST;
3286     }
3287   else
3288 
3289   /* case 3: word doesn't fit and wordwrapping is off. */
3290   if ((neededWidth > w->multiText.lastLine->remaining) && !w->multiText.wordWrap)
3291     {
3292       resizeLine = StuffWordOntoCurrentLine(w, w->multiText.lastLine, wordPtr, neededWidth,
3293 					    firstWordOfLine, isTab, prevWordIsTab);
3294       returnVal  = APPEND_OK;
3295       if (resizeLine)
3296 	returnVal = APPEND_RESIZE;   /* this should probably be APPEND_CLIP_RESIZE... */
3297       else
3298 	returnVal  = APPEND_OK;      /* this should probably be APPEND_CLIP... */
3299     }
3300   else
3301 
3302   /* case 4: the word fits onto the current line. */
3303   if (neededWidth <= w->multiText.lastLine->remaining)
3304     {
3305       resizeLine = StuffWordOntoCurrentLine(w, w->multiText.lastLine, wordPtr, neededWidth,
3306 					    firstWordOfLine, isTab, prevWordIsTab);
3307       if (resizeLine)
3308 	returnVal = APPEND_RESIZE;
3309       else
3310 	returnVal  = APPEND_OK;
3311     }
3312 
3313 
3314   /* set the current line to the one that was just appended to. */
3315   UpdateCursor(w, w->multiText.lastLine,
3316 	       w->multiText.lastLine->x + w->multiText.lastLine->lastWord->x +
3317 	       w->multiText.lastLine->lastWord->bbox.width,
3318 	       w->multiText.lastLine->x + w->multiText.lastLine->lastWord->x +
3319 	       w->multiText.lastLine->lastWord->bbox.width,
3320 	       w->multiText.lastLine->y + w->multiText.lastLine->bbox.ascent);
3321 
3322   /* now draw what's necessary - Compile with -DREALTIME to see each word as it's appended. */
3323 #ifdef REALTIME
3324   if (XtIsRealized) DrawWord(w, wordPtr, FALSE);
3325 #endif
3326 
3327 
3328   return(returnVal);
3329 }
3330 
3331 
3332 
3333 
3334 /*----------------------------------------------------------------------*
3335  |                          AppendWordToTopLine                         |
3336  | This routine will append the given word to the first line in the     |
3337  | widget.  There must be a line to append to. In addition, the word is |
3338  | not allowed to wrap to the next line since this isn't supported. If  |
3339  | wordWrapping is off - no problem; however, if it is ON then there is |
3340  | only a problem if the word is too long for the line.                 |
3341  | All resizing of the top line and all following lines is handled in   |
3342  | this routine.                                                        |
3343  | THIS ~ASSUMES THAT THIS IS THE ONLY WORD OF THE LINE!!! SOME CLEANUP |
3344  | WILL BE NECESSARY FOR GENERAL CASES.                                 |
3345  *----------------------------------------------------------------------*/
AppendWordToTopLine(XmMultiTextWidget cw,WordList wordPtr,int indent)3346 static unsigned int AppendWordToTopLine(XmMultiTextWidget cw, WordList wordPtr, int indent)
3347 {
3348   int          maxAscent, maxDescent, dy;
3349   int          textBottomY;
3350   LineList     lp;
3351 
3352 
3353   /* this is redundant since this must be false if we are here... */
3354   if (cw->multiText.firstLine == NULL)
3355     {
3356       XtWarning(MSG9);
3357       return(APPEND_ERROR);
3358     }
3359 
3360 
3361   /* find the amount that all lines must be shifted down. */
3362   maxAscent  = MAX(wordPtr->bbox.ascent,  cw->multiText.firstLine->bbox.ascent);
3363   maxDescent = MAX(wordPtr->bbox.descent, cw->multiText.firstLine->bbox.descent);
3364   dy = (maxAscent + maxDescent) -
3365        (cw->multiText.firstLine->bbox.ascent + cw->multiText.firstLine->bbox.descent);
3366 
3367   /* if the text is too large for the widget, then resize to fit. (Bottom of last line is below window. */
3368   textBottomY = cw->multiText.lastLine->y + cw->multiText.lastLine->bbox.ascent + cw->multiText.lastLine->bbox.descent;
3369 
3370   if (cw->core.height - cw->multiText.marginHeight  <  dy + textBottomY)
3371     ChangeHeight(cw, dy + textBottomY + cw->multiText.marginHeight + (XtParent(cw)->core.height/2));
3372 
3373   lp = cw->multiText.firstLine;
3374   if ((dy > 0) && (lp->next != NULL))
3375     ShiftFollowingLines(lp->next, dy);
3376 
3377   /* now append the word and adjust the current line - all following lines are now correct.    */
3378   /* note that this line isn't really the correct format; it's newline parameter may be wrong. */
3379   if (lp->wordCount == 0)
3380     {
3381       wordPtr->x   = 0;
3382       lp->lastWord = lp->firstWord = wordPtr;
3383     }
3384   else
3385     {
3386       wordPtr->x         = lp->lastWord->x + lp->lastWord->bbox.width;  /* NOT GENERAL... */
3387       lp->lastWord->next = wordPtr;
3388       lp->lastWord       = wordPtr;
3389     }
3390 
3391   wordPtr->linePtr   = cw->multiText.firstLine;
3392   lp->bbox.ascent    = maxAscent;
3393   lp->bbox.descent   = maxDescent;
3394   lp->wordCount++;
3395 
3396 
3397   /* now check if we have a word wrapping problem - in any case, don't wrap! */
3398   if ((lp->remaining < wordPtr->bbox.width) && (wordPtr->wordWrapping))
3399     XtWarning(MSG8);
3400 
3401   /* currently, multiText isn't consistent with wordwrapping. Sometimes it uses  */
3402   /* resource value and sometimes it uses the word's value. THIS WILL HAVE TO BE */
3403   /* FIXED! however, in the mean time - check both values.                       */
3404   if ((lp->remaining < wordPtr->bbox.width) && (cw->multiText.wordWrap))
3405     XtWarning(MSG8);
3406 
3407   /* if, or if not wordwrapping, force the new word at the end of the first line. */
3408   lp->remaining  -= wordPtr->bbox.width;  /* NOT GENERAL... */
3409   lp->bbox.width += wordPtr->bbox.width;  /* NOT GENERAL... */
3410 
3411   TurnOffCursor(cw);
3412   cw->multiText.cursorY += dy;
3413   if (!cw->multiText.exposeOnly)
3414     if (dy > 0) ScrollWindow(cw, dy);
3415 
3416   return APPEND_OK;
3417 }
3418 
3419 
3420 /*----------------------------------------------------------------------*
3421  |                             PositionCursor                           |
3422  | This routine returns the correct position for a cursor given x, y.   |
3423  | Since the cursor position must be on a word boundry, the actual X    |
3424  | position may be different from the cursor X position.  The Y posn is |
3425  | always correct - or atleast it should be :-)                         |
3426  | Note - this routine DOES NOT change or move the cursor!              |
3427  *----------------------------------------------------------------------*/
PositionCursor(XmMultiTextWidget cw,int x,int y,LineList * currentLine,int * actualX)3428 static void PositionCursor (XmMultiTextWidget cw, int x, int y, LineList *currentLine, int *actualX)
3429 {
3430   LineList lp;
3431   WordList wp, closestWord;
3432   int      dx;
3433 
3434 
3435   if (cw->multiText.currentLine == NULL)
3436     {
3437       fprintf(stderr, "ERROR - NO-TEXT CASE Should not occur!\n");
3438       return;
3439     }
3440 
3441 
3442   /* find the line containing the new position.                         */
3443   /* for speed, check the immediate neighborhood (1 up or 1 down) first */
3444   if      (PtInLine(x, y, cw->multiText.currentLine))       lp = cw->multiText.currentLine;
3445   else if (PtInLine(x, y, cw->multiText.currentLine->next)) lp = cw->multiText.currentLine->next;
3446   else if (PtInLine(x, y, cw->multiText.currentLine->prev)) lp = cw->multiText.currentLine->prev;
3447   else
3448 
3449   /* else, find line containing the pt. */
3450   for (lp = cw->multiText.firstLine;  lp != NULL;  lp = lp->next)
3451     if (PtInLine(x, y, lp))
3452       break;
3453 
3454   /* if not in any line, then ring bell - went off screen. */
3455   if (lp == NULL)
3456     {
3457       XBell(XtDisplay(cw), 0);
3458 
3459       /* set cursor info to current positions. */
3460       *actualX     = cw->multiText.actualX;
3461       *currentLine = cw->multiText.currentLine;
3462 
3463       return;
3464     }
3465 
3466 
3467   /* now to find the horizontal location of the cursor. Either before, after, or middle of line. */
3468   if     ((x <= lp->x) || (lp->lastWord == NULL))
3469     *actualX = lp->x;
3470 
3471   else if (x >= (lp->x + lp->lastWord->x + lp->lastWord->bbox.width))
3472     *actualX = lp->x + lp->lastWord->x + lp->lastWord->bbox.width;
3473 
3474   else
3475     {
3476       dx = lp->bbox.width;
3477       closestWord = lp->firstWord;
3478       for (wp = lp->firstWord;  wp != NULL;  wp = wp->next)
3479 	if (dx > ABS((x - lp->x) - wp->x))
3480 	  {
3481 	    dx = ABS((x - lp->x) - wp->x);
3482 	    closestWord = wp;
3483 	  }
3484 
3485       *actualX = lp->x + closestWord->x;
3486     }
3487 
3488   *currentLine = lp;
3489 }
3490 
3491 
3492 
3493 /*----------------------------------------------------------------------*
3494  |                              UpdateCursor                            |
3495  *----------------------------------------------------------------------*/
UpdateCursor(XmMultiTextWidget cw,LineList lp,int x,int actualX,int actualY)3496 static void UpdateCursor (XmMultiTextWidget cw, LineList lp, int x, int actualX, int actualY)
3497 {
3498   cw->multiText.cursorX      = x;
3499   cw->multiText.cursorY      = actualY;
3500   cw->multiText.actualX      = actualX;
3501   cw->multiText.currentLine  = lp;
3502 }
3503 
3504 
3505 
3506 /*----------------------------------------------------------------------*
3507  |                                 MoveUp                               |
3508  | Moving up dowsn't change the horizontally stored position. That way  |
3509  | we always scroll back to the same position.                          |
3510  | Note, cursor is always at the baseline of the line.                  |
3511  *----------------------------------------------------------------------*/
MoveUp(Widget w,XEvent * event,String * params,Cardinal * numParams)3512 static void MoveUp (Widget w, XEvent* event, String* params, Cardinal* numParams)
3513 {
3514   XmMultiTextWidget cw = (XmMultiTextWidget) w;
3515   int               actualX;
3516   int               currentX = cw->multiText.cursorX;
3517   int               currentY;
3518   LineList          lp;
3519 
3520 
3521   if (cw->multiText.textIsSelected) DeselectAll(cw);
3522 
3523   lp = cw->multiText.currentLine;
3524   if (lp->prev == NULL)
3525     {
3526       currentY = cw->multiText.cursorY;
3527       XBell(XtDisplay(cw), 0);
3528     }
3529   else
3530     currentY = lp->prev->y + lp->prev->bbox.ascent;
3531 
3532   PositionCursor(cw, currentX, currentY, &lp, &actualX);
3533   UpdateCursor(cw, lp, currentX, actualX, currentY);
3534 }
3535 
3536 
3537 /*----------------------------------------------------------------------*
3538  |                                 MoveDown                             |
3539  | Note, cursor is always at the baseline of the line.                  |
3540  *----------------------------------------------------------------------*/
MoveDown(Widget w,XEvent * event,String * params,Cardinal * numParams)3541 static void MoveDown (Widget w, XEvent* event, String* params, Cardinal* numParams)
3542 {
3543   XmMultiTextWidget cw = (XmMultiTextWidget) w;
3544   int               actualX;
3545   int               currentX = cw->multiText.cursorX;
3546   int               currentY;
3547   LineList          lp;
3548 
3549 
3550   if (cw->multiText.textIsSelected) DeselectAll(cw);
3551 
3552   lp = cw->multiText.currentLine;
3553   if (lp->next == NULL)
3554     {
3555       currentY = cw->multiText.cursorY;
3556       XBell(XtDisplay(cw), 0);
3557     }
3558   else
3559     currentY = lp->next->y + lp->next->bbox.ascent;
3560 
3561   PositionCursor(cw, currentX, currentY, &lp, &actualX);
3562   UpdateCursor(cw, lp, currentX, actualX, currentY);;
3563 }
3564 
3565 
3566 
3567 /*----------------------------------------------------------------------*
3568  |                            ClosestPosition                           |
3569  | The closest position to a given location in a line can either be the |
3570  | left side of a word closest to the given position, or the end of the |
3571  | line. This routine is used to find where the cursor whould be placed |
3572  | given the reference position.                                        |
3573  | ClosestWord is returned as the word containing the reference posi-   |
3574  | tion.                                                                |
3575  *----------------------------------------------------------------------*/
ClosestPosition(XmMultiTextWidget cw,LineList lp,WordList * closestWord,int refPosn)3576 int ClosestPosition (XmMultiTextWidget cw, LineList lp,
3577 		     WordList *closestWord, int refPosn)
3578 {
3579   int      dx, x=0;
3580   WordList wp;
3581 
3582 
3583   /* find the current word in this line. It's the word closest to the actual position. */
3584   dx           = lp->bbox.width;
3585   *closestWord = lp->firstWord;
3586 
3587   for (wp = lp->firstWord;  wp != NULL;  wp = wp->next)
3588     if (dx > ABS((cw->multiText.actualX - lp->x) - wp->x))
3589       {
3590 	dx = ABS((cw->multiText.actualX - lp->x) - wp->x);
3591 	*closestWord = wp;
3592 	x = wp->x + lp->x;
3593       }
3594 
3595   /* now check if the end of the line is closer than the closest word. */
3596   if (dx > ABS(lp->x + lp->bbox.width - cw->multiText.actualX))
3597     {
3598       *closestWord = NULL;
3599       x = lp->x + lp->bbox.width;
3600     }
3601 
3602   return(x);
3603 }
3604 
3605 
3606 
3607 /*----------------------------------------------------------------------*
3608  |                                 MoveLeft                             |
3609  | Note, cursor is always at the baseline of the line.                  |
3610  *----------------------------------------------------------------------*/
MoveLeft(Widget w,XEvent * event,String * params,Cardinal * numParams)3611 static void MoveLeft (Widget w, XEvent* event, String* params, Cardinal* numParams)
3612 {
3613   XmMultiTextWidget cw = (XmMultiTextWidget) w;
3614   int               actualX;
3615   int               currentX;
3616   int               currentY = cw->multiText.cursorY;
3617   LineList          lp;
3618   WordList          closestWord;
3619 
3620 
3621   if (cw->multiText.textIsSelected) DeselectAll(cw);
3622 
3623   lp = cw->multiText.currentLine;
3624 
3625   /* find the current word in this line. It's the word closest to the actual position. */
3626   ClosestPosition(cw, lp, &closestWord, cw->multiText.actualX);
3627 
3628 
3629   /* if closestWord is null, then the cursor is off the right side of the line. */
3630   /* unless there are no words in this line.                                    */
3631 
3632   if ((closestWord == NULL) && (lp->wordCount > 0))  /* move to the left side of the right-most word. */
3633     {
3634       if (lp->lastWord != NULL)  /* go to the beginning of the last word. */
3635 	currentX = actualX = lp->x + lp->lastWord->x;
3636       else
3637 	currentX = actualX = lp->x;
3638     }
3639   else
3640     {
3641       if ((closestWord != NULL) && (closestWord->prev != NULL))
3642 	currentX = actualX = lp->x + closestWord->prev->x;
3643 
3644       /* should be here if we're in a line that has no words or this is the first line. */
3645       else
3646 	{
3647 	  if (lp->prev != NULL)
3648 	    {
3649 	      lp = lp->prev;	      /* go to prev line. */
3650 	      currentX = actualX = lp->x + lp->bbox.width;
3651 	      currentY = lp->y + lp->bbox.ascent;
3652 	    }
3653 	  else
3654 	    {
3655 	      /* already at first line. */
3656 	      currentX = actualX = lp->x;
3657 	      XBell(XtDisplay(cw), 0);
3658 	    }
3659 	}
3660     }
3661 
3662   UpdateCursor(cw, lp, currentX, actualX, currentY);
3663 }
3664 
3665 
3666 /*----------------------------------------------------------------------*
3667  |                                 MoveRight                            |
3668  | Note, cursor is always at the baseline of the line.                  |
3669  *----------------------------------------------------------------------*/
MoveRight(Widget w,XEvent * event,String * params,Cardinal * numParams)3670 static void MoveRight (Widget w, XEvent* event, String* params, Cardinal* numParams)
3671 {
3672   XmMultiTextWidget cw = (XmMultiTextWidget) w;
3673   int               actualX;
3674   int               currentX;
3675   int               currentY = cw->multiText.cursorY;
3676   LineList          lp;
3677   WordList          closestWord;
3678 
3679 
3680   if (cw->multiText.textIsSelected) DeselectAll(cw);
3681 
3682   lp = cw->multiText.currentLine;
3683 
3684   ClosestPosition(cw, lp, &closestWord, cw->multiText.actualX);
3685   if (closestWord != NULL)
3686     {
3687       if (closestWord->next != NULL)
3688 	currentX = actualX = closestWord->next->x + lp->x;
3689       else
3690 	currentX = actualX = lp->x + lp->bbox.width;
3691     }
3692   else
3693     {
3694       /* scrolled past end of line - move to beginning of next line if there is one. */
3695       if (lp->next == NULL)
3696 	{
3697 	  currentX = actualX = lp->x + lp->bbox.width;
3698 	  XBell(XtDisplay(w), 0);
3699 	}
3700       else
3701 	{
3702 	  lp = lp->next;	      /* go to next line. */
3703 	  currentX = actualX = lp->x;
3704 	  currentY = lp->y + lp->bbox.ascent;
3705 	}
3706     }
3707 
3708   UpdateCursor(cw, lp, currentX, actualX, currentY);
3709 }
3710 
3711 
3712 #define cursor_width  7
3713 #define cursor_height 5
3714 /*----------------------------------------------------------------------*
3715  |                           MakeCursorPixmaps                          |
3716  | this routine creates (or updates) the cursor's fg and bg pixmaps.    |
3717  *----------------------------------------------------------------------*/
MakeCursorPixmaps(XmMultiTextWidget cw)3718 static void MakeCursorPixmaps (XmMultiTextWidget cw)
3719 {
3720   if (cw->multiText.cursorFg == None)
3721     {
3722       static char cursor_bits[] = {0x08, 0x1c, 0x3e, 0x7f, 0x77};
3723 
3724       cw->multiText.cursorFg =
3725 	XCreatePixmapFromBitmapData(XtDisplay(cw), XtWindow(cw),
3726 				    cursor_bits, cursor_width, cursor_height,
3727 				    cw->multiText.cursorColor,
3728 				    None,
3729 				    DefaultDepthOfScreen(XtScreen(cw)));
3730       cw->multiText.cursorMask =
3731 	XCreatePixmapFromBitmapData(XtDisplay(cw), XtWindow(cw),
3732 				    cursor_bits, cursor_width, cursor_height,
3733 				    WhitePixelOfScreen(XtScreen(cw)),
3734 				    None,
3735 				    1);
3736     }
3737 
3738   if (cw->multiText.cursorBg == None)
3739     cw->multiText.cursorBg = XCreatePixmap(XtDisplay(cw), XtWindow(cw),
3740 					   cursor_width, cursor_height,
3741 					   DefaultDepthOfScreen(XtScreen(cw)));
3742 
3743   /* grab the region under the cursor and store it into the cursorBg field. */
3744   XCopyArea(XtDisplay(cw), XtWindow(cw), cw->multiText.cursorBg, cw->multiText.cursorGC,
3745 	    cw->multiText.actualX, cw->multiText.cursorY, cursor_width, cursor_height,
3746 	    0, 0);
3747 }
3748 
3749 
3750 
3751 /*----------------------------------------------------------------------*
3752  |                               BlinkCursor                            |
3753  *----------------------------------------------------------------------*/
BlinkCursor(XmMultiTextWidget cw)3754 static void BlinkCursor (XmMultiTextWidget cw)
3755 {
3756   cw->multiText.blinkTimeOutID = 0;
3757 
3758   if ((cw->multiText.showCursor) &&
3759       (XtIsRealized((Widget)cw) && !cw->multiText.textIsSelected && !cw->multiText.selecting))
3760     {
3761       /* this only gets called once. */
3762       if (cw->multiText.cursorFg == None)
3763 	{
3764 	  MakeCursorPixmaps(cw);
3765 	  cw->multiText.oldX = cw->multiText.actualX;
3766 	  cw->multiText.oldY = cw->multiText.cursorY;
3767 	}
3768 
3769       XCopyArea(XtDisplay(cw), cw->multiText.cursorBg, XtWindow(cw), cw->multiText.cursorGC,
3770 		0, 0, cursor_width, cursor_height,
3771 		cw->multiText.oldX, cw->multiText.oldY);
3772 
3773       if ((cw->multiText.oldX != cw->multiText.actualX) || (cw->multiText.oldY != cw->multiText.cursorY))
3774 	{
3775 	  cw->multiText.blinkState = FALSE;
3776 	  MakeCursorPixmaps(cw);
3777 	}
3778 
3779       if (!cw->multiText.blinkState)
3780 	{
3781 	  XSetClipMask  (XtDisplay(cw), cw->multiText.cursorGC, cw->multiText.cursorMask);
3782 	  XSetClipOrigin(XtDisplay(cw), cw->multiText.cursorGC, cw->multiText.actualX, cw->multiText.cursorY);
3783 
3784 	  XCopyArea(XtDisplay(cw), cw->multiText.cursorFg, XtWindow(cw), cw->multiText.cursorGC,
3785 		    0, 0, cursor_width, cursor_height,
3786 		    cw->multiText.actualX, cw->multiText.cursorY);
3787 	  XSetClipMask  (XtDisplay(cw), cw->multiText.cursorGC, None);
3788 
3789 	  cw->multiText.oldX = cw->multiText.actualX;
3790 	  cw->multiText.oldY = cw->multiText.cursorY;
3791 	}
3792 
3793       cw->multiText.blinkState = !cw->multiText.blinkState;
3794     }
3795 
3796 
3797   cw->multiText.blinkTimeOutID =
3798     XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)cw), cw->multiText.blinkRate, (XtTimerCallbackProc)BlinkCursor, cw);
3799 }
3800 
3801 
3802 
3803 /*----------------------------------------------------------------------*
3804  |                           TurnOffCursor                              |
3805  *----------------------------------------------------------------------*/
TurnOffCursor(XmMultiTextWidget cw)3806 static void TurnOffCursor (XmMultiTextWidget cw)
3807 {
3808   cw->multiText.cursorAvailable = FALSE;
3809 
3810   if (cw->multiText.blinkTimeOutID != 0)
3811     {
3812       XtRemoveTimeOut(cw->multiText.blinkTimeOutID);
3813       cw->multiText.blinkTimeOutID = 0;
3814     }
3815 
3816   if (cw->multiText.blinkState == ON)
3817     {
3818       BlinkCursor(cw);
3819       XtRemoveTimeOut(cw->multiText.blinkTimeOutID);
3820       cw->multiText.blinkTimeOutID = 0;
3821     }
3822 
3823   /* clearout the pixmaps. */
3824   if (cw->multiText.cursorFg != None)
3825     {
3826       XFreePixmap(XtDisplay(cw), cw->multiText.cursorFg);
3827       XFreePixmap(XtDisplay(cw), cw->multiText.cursorBg);
3828       XFreePixmap(XtDisplay(cw), cw->multiText.cursorMask);
3829     }
3830   cw->multiText.cursorFg       = None;
3831   cw->multiText.cursorBg       = None;
3832   cw->multiText.cursorMask     = None;
3833 }
3834 
3835 
3836 
3837 /*----------------------------------------------------------------------*
3838  |                           TurnOnCursor                               |
3839  *----------------------------------------------------------------------*/
TurnOnCursor(XmMultiTextWidget cw)3840 static void TurnOnCursor (XmMultiTextWidget cw)
3841 {
3842   cw->multiText.cursorAvailable = TRUE;
3843   if (cw->multiText.blinkTimeOutID != 0)
3844     {
3845       XtRemoveTimeOut(cw->multiText.blinkTimeOutID);
3846       cw->multiText.blinkTimeOutID = 0;
3847     }
3848   BlinkCursor(cw);
3849 }
3850 
3851 
3852 
3853 /*##########################################################################################*
3854  #             O U T P U T    R O U T I N E S    F O R    U S E R    I N P U T              #
3855  # ... future work...                                                                       #
3856  *##########################################################################################*/
3857 
3858 
3859 
3860 /*-----------------------========================-----------------------*
3861  |                       KeyPush action procedure                       |
3862  | This routine is active if the MultiText widget is editable.  There   |
3863  | is currently no checking for this case.                              |
3864  |                                                                      |
3865  *-----------------------========================-----------------------*/
KeyPush(Widget w,XEvent * event,String * params,Cardinal * numParams)3866 static void KeyPush (Widget w, XEvent* event, String* params, Cardinal* numParams)
3867 {
3868   KeySym            ks;
3869   char             *str = "  ";
3870 
3871   XLookupString(&event->xkey, str, 1, &ks, NULL);
3872 
3873   switch (*str)
3874     {
3875     case CR:
3876       printf("<CR>\n");  break;
3877     case TAB:
3878       printf("<TAB>"); break;
3879     case CTRL_C:
3880       printf("<CTRL_C>\n"); break;
3881     case SPACE:
3882       printf("<space>"); break;
3883     case BACKSPACE:
3884       printf("<backspace>"); break;
3885     default:
3886       if (isgraph(*str)) printf("%c", *str);
3887     }
3888 
3889 /*
3890   XmMultiTextAppendChar(w, str[0]);
3891 */
3892 }
3893 
3894 
3895 /*-----------------------------=========--------------------------------*
3896  |                             NewLineCR                                |
3897  |                                                                      |
3898  *-----------------------------=========--------------------------------*/
3899 static
NewLineCR(Widget w,XEvent * event,String * params,Cardinal * numParams)3900 void NewLineCR (Widget w, XEvent* event, String* params, Cardinal* numParams)
3901 {
3902 }
3903 
3904 
3905 /*----------------------------===========-------------------------------*
3906  |                            InsertSpace                               |
3907  |                                                                      |
3908  *----------------------------===========-------------------------------*/
3909 static
InsertSpace(Widget w,XEvent * event,String * params,Cardinal * numParams)3910 void InsertSpace (Widget w, XEvent* event, String* params, Cardinal* numParams)
3911 {
3912 }
3913 
3914 
3915 /*------------------------------=====-----------------------------------*
3916  |                              Dummy                                   |
3917  |                                                                      |
3918  *------------------------------=====-----------------------------------*/
3919 static
Dummy(Widget w,XEvent * event,String * params,Cardinal * numParams)3920 void Dummy (Widget w, XEvent* event, String* params, Cardinal* numParams)
3921 {
3922 }
3923