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 #include "DXStrings.h"
14 #include "Application.h"
15 #include "SetDecoratorTextDialog.h"
16 #include "LabelDecorator.h"
17 #include "Network.h"
18 #include <Xm/RowColumn.h>
19 #include <Xm/PushB.h>
20 #include <Xm/PushBG.h>
21 #include <Xm/Form.h>
22 #include <Xm/SeparatoG.h>
23 #include <Xm/Text.h>
24 #include <Xm/DrawingA.h>
25 #include <Xm/DialogS.h>
26 #include <Xm/ArrowB.h>
27 #include <Xm/Label.h>
28 
29 #include <ctype.h> // for ispunct
30 
31 #define ORIG_MINWIDTH 620
32 #define ARROW_RIGHTMOST_POS 60
33 
34 boolean SetDecoratorTextDialog::ClassInitialized = FALSE;
35 
36 String SetDecoratorTextDialog::DefaultResources[] =
37 {
38         "*dialogTitle:     		Control Panel comment...\n",
39 	"*justifyOM.labelString:	Justification:",
40 	"*justifyOM.sensitive:		False",
41 	"*colorOM.labelString:		Text Color:",
42 	"*fontOM.labelString:		Font:",
43 	"*editorText.columns:		40",
44 	"*XmPushButtonGadget.traversalOn: False",
45 	"*XmPushButton.traversalOn: 	False",
46 	"*arrowLabel.marginWidth:	0",
47 	"*arrowLabel.marginHeight:	0",
48 	"*arrowLabel.marginTop:		0",
49 	"*arrowLabel.marginLeft:	0",
50 	"*arrowLabel.marginRight:	0",
51 	"*arrowLabel.marginBottom:	0",
52 
53 	"*okButton.labelString:		OK",
54 	"*applyButton.labelString:	Apply",
55 	"*restoreButton.labelString:	Restore",
56 	"*reformatButton.labelString:	Reformat",
57 	"*cancelButton.labelString:	Cancel",
58 	"*okButton.width:		80",
59 	"*applyButton.width:		80",
60 	"*restoreButton.width:		80",
61 	"*cancelButton.width:		80",
62 	"*reformatButton.width:		80",
63 
64 // If the user defines osfActivate (normally to be the return key) in the
65 // virtual bindings (usually in ~/.motifbind), then the text widget doesn't handle
66 // the return key properly.  The return key triggers the widget's activate()
67 // method rather than the self-insert() or insert-string() methods.
68 // (Caution: If the translation is split into 2 lines, it breaks.)
69 // - Martin
70  "*editorText.translations: #override None<Key>osfActivate: insert-string(\"\\n\")",
71 
72         NULL
73 };
74 
75 
76 #define DIRTY_COLORS 1
77 #define DIRTY_JUSTIFY 2
78 #define DIRTY_TEXT 4
79 #define DIRTY_FONT 8
80 
SetDecoratorTextDialog(Widget parent,boolean,LabelDecorator * dec,const char * name)81 SetDecoratorTextDialog::SetDecoratorTextDialog(Widget parent, boolean ,
82 				LabelDecorator *dec, const char* name) :
83 				Dialog(name, parent)
84 {
85     this->decorator = dec;
86     this->dirty = DIRTY_COLORS | DIRTY_JUSTIFY | DIRTY_TEXT | DIRTY_FONT;
87 }
88 
SetDecoratorTextDialog(Widget parent,boolean,LabelDecorator * dec)89 SetDecoratorTextDialog::SetDecoratorTextDialog(Widget parent,
90 				boolean , LabelDecorator *dec) :
91 				Dialog("LabelAttributes",parent)
92 {
93     this->decorator = dec;
94     this->dirty = DIRTY_COLORS | DIRTY_JUSTIFY | DIRTY_TEXT | DIRTY_FONT;
95 
96     if (!SetDecoratorTextDialog::ClassInitialized) {
97 	SetDecoratorTextDialog::ClassInitialized = TRUE;
98         this->installDefaultResources(theApplication->getRootWidget());
99     }
100 }
101 
~SetDecoratorTextDialog()102 SetDecoratorTextDialog::~SetDecoratorTextDialog()
103 {
104 }
105 
106 //
107 // Install the proper values whenever the dialog box is put onto the screen.
108 //
109 void
post()110 SetDecoratorTextDialog::post()
111 {
112     this->Dialog::post();
113     this->restoreCallback (this);
114 }
115 
116 //
117 //
118 boolean
restoreCallback(Dialog *)119 SetDecoratorTextDialog::restoreCallback (Dialog * )
120 {
121     //
122     // text
123     //
124     if (this->dirty & DIRTY_TEXT) {
125 	const char* cp = "";
126 	if (this->decorator) cp = this->decorator->getLabelValue();
127 	//XtRemoveCallback (this->editorText, XmNmodifyVerifyCallback, (XtCallbackProc)
128 	//    SetDecoratorTextDialog_DirtyTextCB, (XtPointer)this);
129 	XmTextSetString (this->editorText, (char*)cp);
130 	//XtAddCallback (this->editorText, XmNmodifyVerifyCallback, (XtCallbackProc)
131 	//    SetDecoratorTextDialog_DirtyTextCB, (XtPointer)this);
132 	this->dirty&= ~DIRTY_TEXT;
133     }
134 
135     //
136     // alignment
137     //
138     if (this->dirty & DIRTY_JUSTIFY) {
139 	int i,nkids;
140 	Widget *kids;
141 	const char *align;
142 	char *cp;
143 	boolean isset = FALSE;
144 	if (this->decorator) isset = this->decorator->isResourceSet (XmNalignment);
145 	if (isset) {
146 	    align = this->decorator->getResourceString(XmNalignment);
147 	} else {
148 	    align = NULL;
149 	}
150 	XtVaGetValues (this->justifyPulldown,
151 	    XmNnumChildren, &nkids, XmNchildren, &kids, NULL);
152 	ASSERT (nkids > 0);
153 	if ((align) && (align[0])) {
154 	    for (i=0; i<nkids; i++) {
155 		XtVaGetValues (kids[i], XmNuserData, &cp, NULL);
156 		if (!strcmp(cp, align)) break;
157 	    }
158 	    if (i==nkids) i = nkids - 1;
159 	} else {
160 	    i = nkids - 1;
161 	}
162 
163 	Widget choice = kids[i];
164 	XtVaSetValues (this->justifyOM, XmNmenuHistory, kids[i], NULL);
165 	this->dirty&= ~DIRTY_JUSTIFY;
166 
167 	//
168 	// If justification is set to left, then enable the reformat button and
169 	// disable wordwrap.
170 	//
171 	ASSERT(choice);
172 	XtVaGetValues (choice, XmNuserData, &align, NULL);
173 	boolean enab =  (strcmp (align, "XmALIGNMENT_BEGINNING") == 0);
174 	XtSetSensitive (this->reformat, enab);
175     }
176 
177     //
178     // color
179     // The default will be kept in kids[nkids-1]
180     //
181     if (this->dirty & DIRTY_COLORS) {
182 	int i,nkids;
183 	Widget *kids;
184 	char *cp;
185 	const char *color;
186 	boolean isset = FALSE;
187 	if (this->decorator) isset = this->decorator->isResourceSet (XmNforeground);
188 	if (isset) {
189 	    color = this->decorator->getResourceString(XmNforeground);
190 	} else {
191 	    color = NULL;
192 	}
193 	XtVaGetValues (this->colorPulldown,
194 	    XmNnumChildren, &nkids, XmNchildren, &kids, NULL);
195 	ASSERT (nkids > 0);
196 	if ((color) && (color[0])) {
197 	    for (i=0; i<nkids; i++) {
198 		XtVaGetValues (kids[i], XmNuserData, &cp, NULL);
199 		if (!strcmp(cp, color)) break;
200 	    }
201 	    if (i==nkids) i = nkids - 1;
202 	} else {
203 	    i = nkids - 1;
204 	}
205 
206 	XtVaSetValues (this->colorOM, XmNmenuHistory, kids[i], NULL);
207 	this->dirty&= ~DIRTY_COLORS;
208     }
209 
210 
211     //
212     // font
213     // The default will be kept in kids[nkids-1]
214     //
215     if (this->dirty & DIRTY_FONT) {
216 	int i,nkids;
217 	Widget *kids;
218 	char *cp;
219 	const char *font_name = NUL(char*);
220 	if (this->decorator) font_name = this->decorator->getFont();
221 	XtVaGetValues (this->fontPulldown,
222 	    XmNnumChildren, &nkids, XmNchildren, &kids, NULL);
223 	ASSERT (nkids > 0);
224 	if ((font_name) && (font_name[0])) {
225 	    for (i=0; i<nkids; i++) {
226 		XtVaGetValues (kids[i], XmNuserData, &cp, NULL);
227 		if (!strcmp(cp, font_name)) break;
228 	    }
229 	    if (i==nkids) i = nkids - 1;
230 	} else {
231 	    i = nkids - 1;
232 	}
233 
234 	XtVaSetValues (this->fontOM, XmNmenuHistory, kids[i], NULL);
235 	this->dirty&= ~DIRTY_FONT;
236     }
237 
238     return TRUE;
239 }
240 
241 boolean
okCallback(Dialog *)242 SetDecoratorTextDialog::okCallback (Dialog* )
243 {
244 
245     if (this->dirty) {
246 	this->decorator->getNetwork()->setFileDirty();
247     }
248 
249     if (this->dirty & DIRTY_TEXT) {
250 	char *s = XmTextGetString (this->editorText);
251 	this->decorator->setLabel(s);
252 	XtFree(s);
253 	this->dirty&= ~DIRTY_TEXT;
254 	this->decorator->postTextGrowthWork();
255     }
256 
257     Widget choice;
258     if (this->dirty & DIRTY_JUSTIFY) {
259 	char *align;
260 	XtVaGetValues (this->justifyOM, XmNmenuHistory, &choice, NULL);
261 	ASSERT(choice);
262 	XtVaGetValues (choice, XmNuserData, &align, NULL);
263 	this->decorator->setResource (XmNalignment, align);
264 
265 	if (!strcmp(XtName(choice), DEFAULT_SETTING))
266 	    this->decorator->setResource(XmNalignment, NUL(const char *));
267 
268 	this->dirty&= ~DIRTY_JUSTIFY;
269     }
270 
271     if (this->dirty & DIRTY_COLORS) {
272 	char *color;
273 	XtVaGetValues (this->colorOM, XmNmenuHistory, &choice, NULL);
274 	ASSERT(choice);
275 	XtVaGetValues (choice, XmNuserData, &color, NULL);
276 	this->decorator->setResource (XmNforeground, color);
277 
278 	if (!strcmp(XtName(choice), DEFAULT_SETTING))
279 	    this->decorator->setResource (XmNforeground, NUL(const char *));
280 
281 	this->dirty&= ~DIRTY_COLORS;
282     }
283 
284 
285     if (this->dirty & DIRTY_FONT) {
286 	char *font_name;
287 	XtVaGetValues (this->fontOM, XmNmenuHistory, &choice, NULL);
288 	ASSERT(choice);
289 	XtVaGetValues (choice, XmNuserData, &font_name, NULL);
290 	if (!strcmp(XtName(choice), DEFAULT_SETTING)) {
291 	    this->decorator->setFont (NUL(const char *));
292 	} else {
293 	    this->decorator->setFont (font_name);
294 	}
295 	this->dirty&= ~DIRTY_FONT;
296     }
297 
298     return TRUE;
299 }
300 
301 Widget
createDialog(Widget parent)302 SetDecoratorTextDialog::createDialog(Widget parent)
303 {
304 int n = 0;
305 Widget form;
306 Arg args[20];
307 
308     n = 0;
309     XtSetArg (args[n], XmNautoUnmanage, False); n++;
310     XtSetArg (args[n], XmNwidth, ORIG_MINWIDTH); n++;
311     XtSetArg (args[n], XmNminWidth, /*290*/ORIG_MINWIDTH); n++;
312     form = this->CreateMainForm (parent, this->name, args, n);
313     this->negotiated = FALSE;
314 
315     n = 0;
316     XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
317     XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
318     XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
319     XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
320     XtSetArg (args[n], XmNrows,	6); n++;
321     XtSetArg (args[n], XmNcolumns, 40); n++;
322     XtSetArg (args[n], XmNbottomOffset,	130); n++;
323     XtSetArg (args[n], XmNtopOffset, 10); n++;
324     XtSetArg (args[n], XmNleftOffset, 6); n++;
325     XtSetArg (args[n], XmNrightOffset, ARROW_RIGHTMOST_POS); n++;
326     XtSetArg (args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++;
327     XtSetArg (args[n], XmNwordWrap, False); n++;
328     XtSetArg (args[n], XmNscrollHorizontal, False); n++;
329     XtSetArg (args[n], XmNmappedWhenManaged, False); n++;
330     this->editor_magic = XmCreateScrolledText (form, "editorText", args, n);
331     XtManageChild (this->editor_magic);
332 
333     n = 0;
334     XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
335     XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
336     XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
337     XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
338     XtSetArg (args[n], XmNrows,	6); n++;
339     XtSetArg (args[n], XmNcolumns, 40); n++;
340     XtSetArg (args[n], XmNbottomOffset,	130); n++;
341     XtSetArg (args[n], XmNtopOffset, 10); n++;
342     XtSetArg (args[n], XmNleftOffset, 6); n++;
343     XtSetArg (args[n], XmNrightOffset, 6); n++;
344     XtSetArg (args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++;
345     XtSetArg (args[n], XmNwordWrap, False); n++;
346     XtSetArg (args[n], XmNscrollHorizontal, False); n++;
347     this->editorText = XmCreateScrolledText (form, "editorText", args, n);
348     XtManageChild (this->editorText);
349     XtAddCallback (this->editorText, XmNmodifyVerifyCallback, (XtCallbackProc)
350 	SetDecoratorTextDialog_DirtyTextCB, (XtPointer)this);
351 
352 
353     n = 0;
354     XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
355     XtSetArg (args[n], XmNtopWidget, XtParent(this->editorText)); n++;
356     XtSetArg (args[n], XmNtopOffset, 30); n++;
357     XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
358     XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
359     Widget sep1 = XmCreateSeparatorGadget (form, "separator", args, n);
360     XtManageChild (sep1);
361 
362     //
363     // Add an arrow button for resizing the text.
364     //
365     n = 0;
366     XtSetArg (args[n], XmNarrowDirection, XmARROW_UP); n++;
367     XtSetArg (args[n], XmNshadowThickness, 0); n++;
368     XtSetArg (args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
369     XtSetArg (args[n], XmNbottomWidget, sep1); n++;
370     XtSetArg (args[n], XmNbottomOffset, 2); n++;
371     XtSetArg (args[n], XmNleftAttachment, XmATTACH_NONE); n++;
372     XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
373     XtSetArg (args[n], XmNrightOffset, ARROW_RIGHTMOST_POS); n++;
374     this->resize_arrow = XmCreateArrowButton (form, "resizeArrow", args, n);
375     XtManageChild (this->resize_arrow);
376     XtAddEventHandler (this->resize_arrow, Button1MotionMask, False,
377 	(XtEventHandler)SetDecoratorTextDialog_ArrowMotionEH, (XtPointer)this);
378     XtAddCallback (this->resize_arrow, XmNarmCallback, (XtCallbackProc)
379 	SetDecoratorTextDialog_StartPosCB, (XtPointer)this);
380     //XtAddCallback (this->resize_arrow, XmNactivateCallback, (XtCallbackProc)
381     //	SetDecoratorTextDialog_EndPosCB, (XtPointer)this);
382     XtAddCallback (this->resize_arrow, XmNdisarmCallback, (XtCallbackProc)
383 	SetDecoratorTextDialog_EndPosCB, (XtPointer)this);
384 
385     XmString xmstr = XmStringCreateLtoR ("margin\nposition", "small_bold");
386     n = 0;
387     XtSetArg (args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
388     XtSetArg (args[n], XmNbottomWidget, sep1); n++;
389     XtSetArg (args[n], XmNbottomOffset, 0); n++;
390     XtSetArg (args[n], XmNleftAttachment, XmATTACH_NONE); n++;
391     XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
392     XtSetArg (args[n], XmNrightOffset, 4); n++;
393     XtSetArg (args[n], XmNlabelString, xmstr); n++;
394     XtSetArg (args[n], XmNalignment, XmALIGNMENT_END); n++;
395     this->resize_arrow_label = XmCreateLabel (form, "arrowLabel", args, n);
396     XmStringFree(xmstr);
397     XtManageChild (this->resize_arrow_label);
398 
399 
400     n = 0;
401     XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
402     XtSetArg (args[n], XmNtopWidget, sep1); n++;
403     XtSetArg (args[n], XmNtopOffset, 10); n++;
404     XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
405     XtSetArg (args[n], XmNleftOffset, 6); n++;
406     XtSetArg (args[n], XmNsubMenuId, this->createColorMenu(form)); n++;
407     this->colorOM = XmCreateOptionMenu (form, "colorOM", args, n);
408     XtManageChild (this->colorOM);
409 
410     n = 0;
411     XtSetArg (args[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
412     XtSetArg (args[n], XmNtopWidget, this->colorOM); n++;
413     XtSetArg (args[n], XmNtopOffset, 0); n++;
414     XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
415     XtSetArg (args[n], XmNleftWidget, this->colorOM); n++;
416     XtSetArg (args[n], XmNleftOffset, 10); n++;
417     //XtSetArg (args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
418     //XtSetArg (args[n], XmNrightWidget, this->justifyOM); n++;
419     //XtSetArg (args[n], XmNrightOffset, 10); n++;
420     XtSetArg (args[n], XmNsubMenuId, this->createFontMenu(form)); n++;
421     this->fontOM = XmCreateOptionMenu (form, "fontOM", args, n);
422     XtManageChild (this->fontOM);
423 
424     n = 0;
425     XtSetArg (args[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
426     XtSetArg (args[n], XmNtopWidget, this->colorOM); n++;
427     XtSetArg (args[n], XmNtopOffset, 0); n++;
428     XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
429     XtSetArg (args[n], XmNrightOffset, 6); n++;
430     XtSetArg (args[n], XmNsubMenuId, this->createJustifyMenu(form)); n++;
431     this->justifyOM = XmCreateOptionMenu (form, "justifyOM", args, n);
432     XtManageChild (this->justifyOM);
433 
434     n = 0;
435     XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
436     XtSetArg (args[n], XmNtopWidget, colorOM); n++;
437     XtSetArg (args[n], XmNtopOffset, 10); n++;
438     XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
439     XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
440     Widget sep2 = XmCreateSeparatorGadget (form, "separator", args, n);
441     XtManageChild (sep2);
442 
443     n = 0;
444     XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
445     XtSetArg (args[n], XmNtopWidget, sep2); n++;
446     XtSetArg (args[n], XmNtopOffset, 2); n++;
447     XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
448     XtSetArg (args[n], XmNleftOffset, 2); n++;
449     XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
450     XtSetArg (args[n], XmNrightOffset, 2); n++;
451     XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
452     XtSetArg (args[n], XmNbottomOffset, 2); n++;
453     Widget button_form = XmCreateForm (form, "bForm", args, n);
454     XtManageChild (button_form);
455 
456     n = 0;
457     XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
458     XtSetArg (args[n], XmNtopOffset, 12); n++;
459     XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
460     XtSetArg (args[n], XmNleftOffset, 12); n++;
461     this->ok = XmCreatePushButton (button_form, "okButton", args, n);
462     XtManageChild (this->ok);
463 
464     n = 0;
465     XtSetArg (args[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
466     XtSetArg (args[n], XmNtopWidget, this->ok); n++;
467     XtSetArg (args[n], XmNtopOffset, 0); n++;
468     XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
469     XtSetArg (args[n], XmNleftWidget, this->ok); n++;
470     XtSetArg (args[n], XmNleftOffset, 10); n++;
471     this->apply = XmCreatePushButton (button_form, "applyButton", args, n);
472     XtManageChild (this->apply);
473     XtAddCallback (this->apply, XmNactivateCallback, (XtCallbackProc)
474 	SetDecoratorTextDialog_ApplyCB, (XtPointer)this);
475 
476     n = 0;
477     XtSetArg (args[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
478     XtSetArg (args[n], XmNtopWidget, this->ok); n++;
479     XtSetArg (args[n], XmNtopOffset, 0); n++;
480     XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
481     XtSetArg (args[n], XmNrightOffset, 12); n++;
482     this->cancel = XmCreatePushButton (button_form, "cancelButton", args, n);
483     XtManageChild (this->cancel);
484 
485     n = 0;
486     XtSetArg (args[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
487     XtSetArg (args[n], XmNtopWidget, this->cancel); n++;
488     XtSetArg (args[n], XmNtopOffset, 0); n++;
489     XtSetArg (args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
490     XtSetArg (args[n], XmNrightWidget, this->cancel); n++;
491     XtSetArg (args[n], XmNrightOffset, 10); n++;
492     this->restore = XmCreatePushButton (button_form, "restoreButton", args, n);
493     XtManageChild (this->restore);
494     XtAddCallback (this->restore, XmNactivateCallback, (XtCallbackProc)
495 	SetDecoratorTextDialog_RestoreCB, (XtPointer)this);
496 
497     n = 0;
498     XtSetArg (args[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
499     XtSetArg (args[n], XmNtopWidget, this->cancel); n++;
500     XtSetArg (args[n], XmNtopOffset, 0); n++;
501     XtSetArg (args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
502     XtSetArg (args[n], XmNrightWidget, this->restore); n++;
503     XtSetArg (args[n], XmNrightOffset, 10); n++;
504     this->reformat = XmCreatePushButton (button_form, "reformatButton", args, n);
505     XtManageChild (this->reformat);
506     XtAddCallback (this->reformat, XmNactivateCallback, (XtCallbackProc)
507 	SetDecoratorTextDialog_ReformatCB, (XtPointer)this);
508 
509     return form;
510 }
511 
512 //
513 // Things inside SHOW_IT_NOW ifdefs will set the corresponding resource value
514 // into the button so that the user sees it without having to set the value
515 // into the decorator.  These things are turned off right now because turning
516 // them on affects widget size which makes the option menus unequal in size.
517 //
518 Widget
createColorMenu(Widget parent)519 SetDecoratorTextDialog::createColorMenu(Widget parent)
520 {
521 Widget button;
522 const char **btn_names = this->decorator->getSupportedColorNames();
523 const char **color_values = this->decorator->getSupportedColorValues();
524 
525 Arg args[9];
526 int i,n;
527 
528     n = 0;
529     this->colorPulldown = XmCreatePulldownMenu (parent, "pulldownMenu", args, n);
530 
531 #   if SHOW_IT_NOW
532     Pixel fg;
533     XrmValue from, toinout;
534 #   endif
535 
536     i = 0;
537     while (btn_names[i]) {
538 	char *color_value, *btn_name;
539 	btn_name = new char[1+strlen(btn_names[i])];
540 	strcpy (btn_name, btn_names[i]);
541 	color_value = new char[1+strlen(color_values[i])];
542 	strcpy (color_value, color_values[i]);
543 
544 #	if SHOW_IT_NOW
545 	from.addr = color_values[i];
546 	from.size = 1+strlen(from.addr);
547         toinout.addr = (XPointer)&fg;
548 	toinout.size = sizeof(Pixel);
549         XtConvertAndStore(parent, XmRString, &from, XmRPixel, &toinout);
550 #	endif
551 
552 	XmString xmstr = XmStringCreateLtoR (btn_name, "bold");
553 
554 	n = 0;
555 #	if SHOW_IT_NOW
556 	XtSetArg (args[n], XmNforeground, fg); n++;
557 #	endif
558 	XtSetArg (args[n], XmNuserData, color_values[i]); n++;
559 	XtSetArg (args[n], XmNlabelString, xmstr); n++;
560 #	if SHOW_IT_NOW
561 	button = XmCreatePushButton (this->colorPulldown, btn_name, args, n);
562 #	else
563 	button = XmCreatePushButtonGadget (this->colorPulldown, btn_name, args, n);
564 #	endif
565 	XtManageChild (button);
566 	XtAddCallback (button, XmNactivateCallback, (XtCallbackProc)
567 	    SetDecoratorTextDialog_DirtyColorsCB, (XtPointer)this);
568 
569 	XmStringFree(xmstr);
570 	delete color_value;
571 	delete btn_name;
572 
573 	i++;
574     }
575 
576     return this->colorPulldown;
577 }
578 
579 
580 Widget
createFontMenu(Widget parent)581 SetDecoratorTextDialog::createFontMenu(Widget parent)
582 {
583 Widget button;
584 Arg args[9];
585 int i,n;
586 #if defined(aviion) || defined(hp700)
587 static
588 #endif
589 char *btn_names[] = {
590     "bold 12 point", 	"bold 14 point", 	"bold 18 point",	"bold 24 point",
591     "italic 12 point", 	"italic 14 point", 	"italic 18 point",	"italic 24 point",
592     "medium 12 point", 	"medium 14 point", 	"medium 18 point",	"medium 24 point",
593     DEFAULT_SETTING
594 };
595 
596 // These names must match those used for fontList in IMBApplication.C
597 #if defined(aviion) || defined(hp700)
598 static
599 #endif
600 char *font_names[] = {
601     "small_bold", 	"bold", 	"big_bold",	"huge_bold",
602     "small_oblique", 	"oblique", 	"big_oblique",	"huge_oblique",
603     "small_normal", 	"normal", 	"big_normal",	"huge_normal",
604     XmSTRING_DEFAULT_CHARSET
605 };
606 
607     n = 0;
608     this->fontPulldown = XmCreatePulldownMenu (parent, "pulldownMenu", args, n);
609 
610     for (i=0; i<XtNumber(btn_names); i++) {
611 #	if SHOW_IT_NOW
612 	XmString xmstr = XmStringCreateLtoR (btn_names[i], font_names[i]);
613 #	endif
614 
615 	n = 0;
616 	XtSetArg (args[n], XmNuserData, font_names[i]); n++;
617 #	if SHOW_IT_NOW
618 	XtSetArg (args[n], XmNlabelString, xmstr); n++;
619 #	endif
620 	button = XmCreatePushButton (this->fontPulldown, btn_names[i], args, n);
621 	XtManageChild (button);
622 	XtAddCallback (button, XmNactivateCallback, (XtCallbackProc)
623 	    SetDecoratorTextDialog_DirtyFontCB, (XtPointer)this);
624 
625 #	if SHOW_IT_NOW
626 	XmStringFree(xmstr);
627 #	endif
628     }
629 
630     return this->fontPulldown;
631 }
632 
633 Widget
createJustifyMenu(Widget parent)634 SetDecoratorTextDialog::createJustifyMenu (Widget parent)
635 {
636 Widget button;
637 Arg args[9];
638 int i,n;
639 
640 #if defined(aviion) || defined(hp700)
641 static
642 #endif
643 char *btn_names[] = {
644     "Left", 	"Center", 	"Right",
645     DEFAULT_SETTING
646 };
647 
648 
649     static char *align_names[4];
650     align_names[0] = "XmALIGNMENT_BEGINNING";
651     align_names[1] = "XmALIGNMENT_CENTER";
652     align_names[2] = "XmALIGNMENT_END";
653     align_names[3] = (char*)this->defaultJustifySetting();
654 
655     n = 0;
656     this->justifyPulldown = XmCreatePulldownMenu (parent, "pulldownMenu", args, n);
657 
658     for (i=0; i<XtNumber(btn_names); i++) {
659 	n = 0;
660 	XtSetArg (args[n], XmNuserData, align_names[i]); n++;
661 	button = XmCreatePushButtonGadget (this->justifyPulldown, btn_names[i], args, n);
662 	XtManageChild (button);
663 	XtAddCallback (button, XmNactivateCallback, (XtCallbackProc)
664 	    SetDecoratorTextDialog_DirtyJustifyCB, (XtPointer)this);
665     }
666 
667     return this->justifyPulldown;
668 }
669 
670 const char*
defaultJustifySetting()671 SetDecoratorTextDialog::defaultJustifySetting()
672 {
673 static char* def = "XmALIGNMENT_CENTER";
674     return def;
675 }
676 
677 extern "C" {
678 
679 void
SetDecoratorTextDialog_DirtyColorsCB(Widget,XtPointer cdata,XtPointer)680 SetDecoratorTextDialog_DirtyColorsCB (Widget , XtPointer cdata, XtPointer)
681 {
682 SetDecoratorTextDialog *sdt = (SetDecoratorTextDialog *)cdata;
683 
684     ASSERT(sdt);
685     sdt->dirty|= DIRTY_COLORS;
686 }
687 
688 
689 void
SetDecoratorTextDialog_DirtyJustifyCB(Widget,XtPointer cdata,XtPointer)690 SetDecoratorTextDialog_DirtyJustifyCB (Widget , XtPointer cdata, XtPointer)
691 {
692 SetDecoratorTextDialog *sdt = (SetDecoratorTextDialog *)cdata;
693 
694     ASSERT(sdt);
695     sdt->dirty|= DIRTY_JUSTIFY;
696 //
697 // ...a workaround for a bug in libXm in aix 3.2.5. If you
698 // set XmNalignment on anything of the XmLabel variety, the text won't
699 // move. But if you set other resources, then the text will move.
700 //
701 #if defined(ibm6000)
702     sdt->dirty|= DIRTY_TEXT;
703 #endif
704 
705 
706     //
707     // If justification is set to left, then enable the reformat button
708     //
709     Widget choice;
710     char *align;
711     XtVaGetValues (sdt->justifyOM, XmNmenuHistory, &choice, NULL);
712     ASSERT(choice);
713     XtVaGetValues (choice, XmNuserData, &align, NULL);
714     boolean enab =  (strcmp (align, "XmALIGNMENT_BEGINNING") == 0);
715     XtSetSensitive (sdt->reformat, enab);
716 
717     XtVaSetValues (sdt->resize_arrow, XmNmappedWhenManaged, (Boolean)enab, NULL);
718     XtVaSetValues (sdt->resize_arrow_label, XmNmappedWhenManaged, (Boolean)enab, NULL);
719 }
720 
721 void
SetDecoratorTextDialog_DirtyTextCB(Widget w,XtPointer cdata,XtPointer cbs)722 SetDecoratorTextDialog_DirtyTextCB (Widget w, XtPointer cdata, XtPointer cbs)
723 {
724 SetDecoratorTextDialog *sdt = (SetDecoratorTextDialog *)cdata;
725 XmTextVerifyCallbackStruct *tvcs = (XmTextVerifyCallbackStruct*)cbs;
726 XmTextBlockRec *tb = tvcs->text;
727 boolean enable_it, disable_it;
728 
729     ASSERT(sdt);
730     sdt->dirty|= DIRTY_TEXT;
731 
732     enable_it = disable_it = FALSE;
733     boolean isSensitive = XtIsSensitive(sdt->justifyOM);
734 
735     //
736     // If you're inserting a newline and the menu is insensitive...
737     // you're done and you can just turn on the menu.
738     //
739     if ((tb->length) && (tb->ptr) && (!isSensitive) &&
740 	(strchr(tb->ptr, (int)'\n')) &&
741 	(tvcs->startPos >= tvcs->currInsert)) {
742 	enable_it = True;
743 
744     //
745     // else if you're erasing a chunk and the menu is sensitive, then
746     // count the lines exclusive of the erased chunk.
747     // The chars between startPos and endPos are going away.
748     //
749     } else if ((tb->length==0) && (isSensitive)) {
750 	char *cp = XmTextGetString (w);
751 	int i,len = ((cp&&cp[0])?strlen(cp):0);
752 	int lines = 0;
753 
754 	for (i=0; i<len; i++) {
755 	    if ((i<tvcs->startPos) || (i>=tvcs->endPos))
756 		if (cp[i] == '\n') lines++;
757 	    if (lines>=1) break;
758 	}
759 
760 	if (lines<1) disable_it = TRUE;
761 	XtFree(cp);
762 
763     //
764     // else you're replacing a chunk and you must count lines exclusive
765     // of the erasure and inclusive of the insertion.
766     //
767     } else if (isSensitive) {
768 	char *cp = XmTextGetString (w);
769 	int i,len = ((cp&&cp[0])?strlen(cp):0);
770 	int lines = 0;
771 
772 	for (i=0; i<len; i++) {
773 	    if ((i<tvcs->startPos) || (i>=tvcs->endPos))
774 		if (cp[i] == '\n') lines++;
775 	}
776 
777 	for (i=0; i<tb->length; i++) {
778 	    if (tb->ptr[i] == '\n') lines++;
779 	    if (lines>=1) break;
780 	}
781 
782 	if (lines<1) disable_it = TRUE;
783 	XtFree(cp);
784     }
785 
786     ASSERT (!(enable_it && disable_it));
787     if (enable_it) {
788 	XtSetSensitive (sdt->justifyOM, True);
789 
790 	//
791 	// If justification is set to left, then enable the reformat button
792 	//
793 	Widget choice;
794 	char *align;
795 	XtVaGetValues (sdt->justifyOM, XmNmenuHistory, &choice, NULL);
796 	ASSERT(choice);
797 	XtVaGetValues (choice, XmNuserData, &align, NULL);
798 	boolean enab =  (strcmp (align, "XmALIGNMENT_BEGINNING") == 0);
799 	XtSetSensitive (sdt->reformat, enab);
800     } else if (disable_it) {
801 	int nkids;
802 	Widget *kids;
803 	XtVaGetValues (sdt->justifyPulldown,
804 	    XmNnumChildren, &nkids, XmNchildren, &kids, NULL);
805 	ASSERT (nkids > 0);
806 
807 	XtVaSetValues (sdt->justifyOM, XmNmenuHistory, kids[nkids-1], NULL);
808 	sdt->dirty|= DIRTY_JUSTIFY;
809 	XtSetSensitive (sdt->justifyOM, False);
810 	XtSetSensitive (sdt->reformat, False);
811     }
812 
813     tvcs->doit = True;
814 }
815 
816 void
SetDecoratorTextDialog_DirtyFontCB(Widget,XtPointer cdata,XtPointer)817 SetDecoratorTextDialog_DirtyFontCB (Widget , XtPointer cdata, XtPointer)
818 {
819 SetDecoratorTextDialog *sdt = (SetDecoratorTextDialog *)cdata;
820 
821     ASSERT(sdt);
822     sdt->dirty|= DIRTY_FONT;
823 }
824 
825 
826 void
SetDecoratorTextDialog_ApplyCB(Widget,XtPointer cdata,XtPointer)827 SetDecoratorTextDialog_ApplyCB (Widget , XtPointer cdata, XtPointer)
828 {
829 SetDecoratorTextDialog *sdt = (SetDecoratorTextDialog *)cdata;
830 
831     ASSERT(sdt);
832     sdt->okCallback (sdt);
833 }
834 
835 void
SetDecoratorTextDialog_RestoreCB(Widget,XtPointer cdata,XtPointer)836 SetDecoratorTextDialog_RestoreCB (Widget , XtPointer cdata, XtPointer)
837 {
838 SetDecoratorTextDialog *sdt = (SetDecoratorTextDialog *)cdata;
839 
840     ASSERT(sdt);
841     sdt->restoreCallback (sdt);
842 }
843 
844 
845 void
SetDecoratorTextDialog_ReformatCB(Widget,XtPointer cdata,XtPointer)846 SetDecoratorTextDialog_ReformatCB (Widget , XtPointer cdata, XtPointer)
847 {
848 SetDecoratorTextDialog *sdt = (SetDecoratorTextDialog *)cdata;
849 
850     ASSERT(sdt);
851     sdt->reformatCallback (sdt);
852 }
853 
854 
855 void
SetDecoratorTextDialog_ArrowMotionEH(Widget w,XtPointer cdata,XEvent * xev,Boolean * keep_going)856 SetDecoratorTextDialog_ArrowMotionEH
857     (Widget w, XtPointer cdata, XEvent *xev, Boolean *keep_going)
858 {
859 SetDecoratorTextDialog *sdt = (SetDecoratorTextDialog *)cdata;
860 
861     ASSERT(sdt);
862     *keep_going = True;
863 
864     if (!xev) return ;
865     if (xev->type != MotionNotify) return ;
866     XMotionEvent* xmo = (XMotionEvent*)xev;
867 
868     int diff = sdt->initial_xpos - xmo->x_root;
869     int newofs = sdt->start_pos + diff;
870     if ((newofs > 500) && (sdt->start_pos >= 500)) return ;
871     if ((newofs < ARROW_RIGHTMOST_POS) && (sdt->start_pos <= ARROW_RIGHTMOST_POS)) return ;
872     if (newofs < ARROW_RIGHTMOST_POS) {
873 	sdt->start_pos = ARROW_RIGHTMOST_POS;
874 	sdt->initial_xpos = xmo->x_root - (ARROW_RIGHTMOST_POS-newofs);
875     } else if (newofs > 500) {
876 	sdt->start_pos = 500;
877 	sdt->initial_xpos = xmo->x_root - (500-newofs);
878     } else {
879 	sdt->initial_xpos = xmo->x_root;
880 	sdt->start_pos = newofs;
881     }
882 
883     XtVaSetValues (sdt->resize_arrow, XmNrightOffset, sdt->start_pos, NULL);
884 }
885 
886 
887 void
SetDecoratorTextDialog_StartPosCB(Widget,XtPointer cdata,XtPointer cbs)888 SetDecoratorTextDialog_StartPosCB(Widget , XtPointer cdata, XtPointer cbs)
889 {
890 SetDecoratorTextDialog *sdt = (SetDecoratorTextDialog *)cdata;
891 XmArrowButtonCallbackStruct *abcs = (XmArrowButtonCallbackStruct*)cbs;
892 XEvent *xev = abcs->event;
893 
894     if (!xev) return ;
895     if (xev->type != ButtonPress) return ;
896     XButtonEvent* xbe = (XButtonEvent*)xev;
897     sdt->initial_xpos = xbe->x_root;
898 
899     ASSERT(sdt);
900     XtVaGetValues (sdt->resize_arrow, XmNrightOffset, &sdt->start_pos, NULL);
901     XtVaSetValues (sdt->getRootWidget(), XmNresizePolicy, XmRESIZE_NONE, NULL);
902 }
903 
904 
905 void
SetDecoratorTextDialog_EndPosCB(Widget,XtPointer cdata,XtPointer)906 SetDecoratorTextDialog_EndPosCB(Widget , XtPointer cdata, XtPointer)
907 {
908 SetDecoratorTextDialog *sdt = (SetDecoratorTextDialog *)cdata;
909 
910     ASSERT(sdt);
911     XtVaSetValues (XtParent(sdt->editor_magic), XmNrightOffset, sdt->start_pos, NULL);
912     sdt->reformatCallback(sdt);
913 }
914 
915 
916 } // extern C
917 
918 
919 //
920 // Install the default resources for this class.
921 //
installDefaultResources(Widget baseWidget)922 void SetDecoratorTextDialog::installDefaultResources(Widget  baseWidget)
923 {
924     this->setDefaultResources(baseWidget, SetDecoratorTextDialog::DefaultResources);
925     this->Dialog::installDefaultResources( baseWidget);
926 }
927 
928 
929 //
930 // Connect the dialog to a new decorator.  Update the displayed values.
931 //
setDecorator(LabelDecorator * new_lab)932 void SetDecoratorTextDialog::setDecorator(LabelDecorator* new_lab)
933 {
934     this->decorator = new_lab;
935     this->dirty = DIRTY_COLORS | DIRTY_JUSTIFY | DIRTY_TEXT | DIRTY_FONT;
936     this->restoreCallback(this);
937 
938     if (!this->decorator) {
939 	XtSetSensitive (this->ok, False);
940 	XtSetSensitive (this->restore, False);
941 	XtSetSensitive (this->apply, False);
942     } else {
943 	XtSetSensitive (this->ok, True);
944 	XtSetSensitive (this->restore, True);
945 	XtSetSensitive (this->apply, True);
946     }
947 }
948 
949 //
950 // 1) Make a copy of the text.
951 // 2) Replace all instances of a WhiteSpace which contain only 1 '\n', with one space
952 // 3) loop over new buffer jumping ahead XmNcolumns chars each time and then
953 //    back up until work boundary where you insert a '\n'.
954 // - When backing up, look for only a space.  This is the normal case.  If there is
955 //   no space, then settle for ispunct()==1 while scanning ahead.
956 //
957 // This doesn't account for variable width fonts.  The value in XmNcolumns is just
958 // an estimate of the number of chars we can accommodate.  There is in fact no such
959 // number.  In order to do this correctly, we would have to count the pixels used
960 // by each char on the line.  Motif supplies a mechanism for that sort of behavior.
961 // It's what you get when you set XmNwordWrap==True.  Unfortunately, there isn't
962 // any way to query for the results of Motif's wordwrapping.  If there were, then
963 // we would use that instead.
964 //
965 #define IsSpace(q) ((q==' ')||(q=='\t')||(q=='\n'))
reformatCallback(SetDecoratorTextDialog *)966 boolean SetDecoratorTextDialog::reformatCallback(SetDecoratorTextDialog*)
967 {
968 short cols;
969 
970     char *spare = XmTextGetString (this->editorText);
971     XtVaGetValues (this->editor_magic, XmNcolumns, &cols, NULL);
972 
973     cols+= (cols>>2);
974     if (!spare)
975 	return FALSE;
976     int len = strlen(spare);
977     if (!len) {
978 	XtFree(spare);
979 	return FALSE;
980     }
981 
982     //
983     // compress whitespace converting \n to ' '
984     //
985     char* no_extra_space = new char[1+len];
986     int i;
987     boolean was_space = TRUE;
988     boolean was_newline = FALSE;
989     int nxt = 0;
990     for (i=0; i<len; i++) {
991 	if (IsSpace(spare[i])) {
992 	    if (was_space == FALSE) {
993 		no_extra_space[nxt++] = ' ';
994 	    } else if (was_newline) {
995 		if (spare[i] == '\n') {
996 		    no_extra_space[nxt-1] = '\n';
997 		    no_extra_space[nxt++] = '\n';
998 		}
999 	    } else {
1000 	    }
1001 	    was_newline = (spare[i] == '\n');
1002 	    was_space = TRUE;
1003 	} else {
1004 	    no_extra_space[nxt++] = spare[i];
1005 	    was_space = FALSE;
1006 	    was_newline = FALSE;
1007 	}
1008     }
1009     no_extra_space[nxt] = '\0';
1010     XtFree(spare);
1011     spare = no_extra_space;
1012 
1013     //
1014     // adding back the \n
1015     //
1016     len = nxt;
1017     no_extra_space = new char[nxt<<1];
1018 
1019     nxt = 0;
1020     int copied_in_row = 0;
1021     int most_recent_space = 0;
1022     for (i=0; i<len; i++) {
1023 	if (copied_in_row == 0) most_recent_space = -1;
1024 	boolean line_break = (copied_in_row == cols);
1025 	if (line_break) {
1026 	    //
1027 	    // How far away is the next space?
1028 	    //
1029 #	    define HOW_FAR_TO_LOOK 8
1030 	    int to_next_space = HOW_FAR_TO_LOOK+1;
1031 	    int regress = most_recent_space-nxt;
1032 #	    define PREFER_BACKWARDS -(HOW_FAR_TO_LOOK+4)
1033 	    if (regress <= PREFER_BACKWARDS) {
1034 		int k = i;
1035 		while ((spare[k]) &&
1036 		    (to_next_space<HOW_FAR_TO_LOOK) &&
1037 		    (IsSpace(spare[k]) == FALSE)) {
1038 		    k++;
1039 		    to_next_space++;
1040 		}
1041 	    }
1042 
1043 	    if ((to_next_space < HOW_FAR_TO_LOOK) &&
1044 		(most_recent_space > 0) && (regress <= PREFER_BACKWARDS)) {
1045 	    } else if (most_recent_space > 0) {
1046 		//
1047 		// If we can regress to a space then do
1048 		//
1049 		nxt = most_recent_space;
1050 		i+= regress;
1051 	    }
1052 #	    undef HOW_FAR_TO_LOOK
1053 #	    undef PREFER_BACKWARDS
1054 
1055 	    //
1056 	    // This handles the case of... we've regressed or we must move ahead
1057 	    //
1058 	    while ((spare[i]) &&
1059 		    (IsSpace(spare[i]) == FALSE) &&
1060 		    (ispunct((int)spare[i]) == 0)) {
1061 		no_extra_space[nxt++] = spare[i++];
1062 	    }
1063 
1064 	    no_extra_space[nxt++] = '\n';
1065 	    if ((spare[i] != '\n') && (spare[i] != ' ')) {
1066 		no_extra_space[nxt++] = spare[i];
1067 	    }
1068 	    copied_in_row = 0;
1069 	} else {
1070 	    if ((spare[i] == ' ') || (spare[i] == '\t'))
1071 		most_recent_space = nxt;
1072 	    no_extra_space[nxt++] = spare[i];
1073 	    if (spare[i] == '\n')
1074 		copied_in_row = 0;
1075 	    else
1076 		copied_in_row++;
1077 	}
1078     }
1079     no_extra_space[nxt] = '\0';
1080     delete spare;
1081 
1082     //
1083     // Prepare for kerning
1084     //
1085     this->kern_lines = NULL;
1086     this->kern_lengths = NULL;
1087     this->line_count = 0;
1088 
1089     boolean default_justification = FALSE;
1090     Widget choice;
1091     XtVaGetValues (this->justifyOM, XmNmenuHistory, &choice, NULL);
1092     ASSERT(choice);
1093 
1094     if (!strcmp(XtName(choice), DEFAULT_SETTING))
1095 	default_justification = TRUE;
1096 
1097     if (default_justification) {
1098 	//
1099 	// Use the font in our menu for calculating padding.
1100 	//
1101 	char *font_name;
1102 	Widget choice;
1103 	XtVaGetValues (this->fontOM, XmNmenuHistory, &choice, NULL);
1104 	ASSERT(choice);
1105 	XtVaGetValues (choice, XmNuserData, &font_name, NULL);
1106 	spare = this->kern(no_extra_space, font_name);
1107     } else {
1108 	spare = no_extra_space;
1109     }
1110 
1111     XmTextSetString(this->editorText, spare);
1112     delete spare;
1113 
1114     if (this->line_count>1) {
1115 	for (i=0; i<this->line_count; i++)
1116 	    delete this->kern_lines[i];
1117 	delete this->kern_lines;
1118 	delete this->kern_lengths;
1119 	this->kern_lines = NULL;
1120 	this->kern_lengths = NULL;
1121 	this->line_count = 0;
1122     }
1123 
1124     return TRUE;
1125 }
1126 
1127 
kern(char * src,char * font)1128 char* SetDecoratorTextDialog::kern(char* src, char* font)
1129 {
1130 int i;
1131 
1132     if ((!src) || (!src[0])) return src;
1133 
1134     XmFontList xmfl = this->decorator->getFontList();
1135 
1136     //
1137     // Chop the input into individual lines and measure each one.
1138     //
1139     XmString str1 = XmStringCreateLtoR (src, font);
1140     boolean retVal = this->measureLines(str1, font);
1141     int max_pixel_len = XmStringWidth (xmfl, str1);
1142     XmStringFree(str1);
1143     if (!retVal) return src;
1144 
1145 
1146     //
1147     // Find the size of a ' ' character.
1148     //
1149     XmString s1 = XmStringCreateLtoR ("H i", font);
1150     XmString s2 = XmStringCreateLtoR ("H  i", font);
1151     Dimension w1 = XmStringWidth (xmfl, s1);
1152     Dimension w2 = XmStringWidth (xmfl, s2);
1153     XmStringFree(s1);
1154     XmStringFree(s2);
1155     int size_of_1_space = w2 - w1;
1156     if (size_of_1_space < 1) return src;
1157 
1158 
1159     //
1160     // If the maximum number of extra spaces required is greater than
1161     // threshold then skip the line.  If the line requires more than half
1162     // its length in padding, then skip the line.
1163     //
1164     float threshhold = max_pixel_len * 0.25;
1165 
1166     //
1167     // Pad each line appropriately.
1168     //
1169     for (i=0; i<this->line_count; i++) {
1170 	int pixel_diff = max_pixel_len - this->kern_lengths[i];
1171 	if (pixel_diff == 0) continue;
1172 	if (pixel_diff > threshhold) continue;
1173 	if (pixel_diff > (this->kern_lengths[i]>>1)) continue;
1174 	int needed_spaces = pixel_diff / size_of_1_space;
1175 	this->kern_lines[i] =
1176 	    SetDecoratorTextDialog::Pad (this->kern_lines[i], needed_spaces, 2);
1177     }
1178 
1179     //
1180     // reconnect the lines
1181     //
1182     int totlen = 0;
1183     for (i=0; i<this->line_count; i++)
1184 	totlen+= strlen(this->kern_lines[i]);
1185     char* newsrc = new char[totlen+1];
1186 
1187     totlen = 0;
1188     for (i=0; i<this->line_count; i++) {
1189 	int len = strlen(this->kern_lines[i]);
1190 	strcpy (&newsrc[totlen], this->kern_lines[i]);
1191 	totlen+= len;
1192 	newsrc[totlen] = '\0';
1193     }
1194 
1195     delete src;
1196     return newsrc;
1197 }
1198 
1199 char*
Pad(char * src,int pad,int every_nth)1200 SetDecoratorTextDialog::Pad(char* src, int pad, int every_nth)
1201 {
1202 static char sep_chars[] = {
1203     ',', '?', ';', '!', '@', '#', '*',
1204     '$', '&', '+', '{', '}', '[', ']',
1205     '|', '=', ' ', '\0'
1206 };
1207 
1208     ASSERT (src);
1209     if (pad == 0) return src;
1210     ASSERT (pad > 0);
1211     int pads_added = 0;
1212     int len = strlen(src);
1213     char* newsrc = new char[len+1+pad];
1214     int space_cnt = 1;
1215 
1216     boolean just_added = FALSE;
1217     int nxt = 0;
1218     int i;
1219     for (i=0; i<len; i++) {
1220 	newsrc[nxt++] = src[i];
1221 	if (pads_added < pad) {
1222 	    boolean is_separator = FALSE;
1223 	    if ((just_added == FALSE) && (every_nth)) {
1224 		if (space_cnt == every_nth) {
1225 		    is_separator = (src[i]==' ');
1226 		    space_cnt = 1;
1227 		} else
1228 		    space_cnt++;
1229 	    } else if (just_added == FALSE) {
1230 		int j = 0;
1231 		while ((is_separator == FALSE) && (sep_chars[j]))
1232 		    is_separator = (src[i] == sep_chars[j++]);
1233 	    }
1234 
1235 	    if (is_separator) {
1236 		newsrc[nxt++] = ' ';
1237 		pads_added++;
1238 		just_added = TRUE;
1239 	    } else
1240 		just_added = FALSE;
1241 	}
1242     }
1243     newsrc[nxt] = '\0';
1244 
1245     delete src;
1246     int remaining = pad - pads_added;
1247     if (pads_added == 0)
1248 	return newsrc;
1249     else
1250 	return SetDecoratorTextDialog::Pad(newsrc, remaining, (every_nth?every_nth-1:0));
1251 }
1252 
1253 //
1254 // null-terminated char * -- the XmNlabelStinrg
1255 //
measureLines(XmString srcString,char * font)1256 boolean SetDecoratorTextDialog::measureLines(XmString srcString, char* font)
1257 {
1258 XmStringContext cxt;
1259 char *text, *tag;
1260 XmStringDirection dir;
1261 int i;
1262 
1263     if (!srcString) return FALSE;
1264     this->line_count = XmStringLineCount(srcString);
1265     if (this->line_count<=1)
1266 	return FALSE;
1267 
1268     if (!XmStringInitContext (&cxt, srcString)) {
1269 	XtWarning ("Can't convert compound string.");
1270 	return FALSE;
1271     }
1272 
1273     this->kern_lines = new String[this->line_count];
1274     this->kern_lengths = new int[this->line_count];
1275     int next_kern_line = 0;
1276     for (i=0; i<this->line_count; i++) {
1277 	this->kern_lengths[i] = 0;
1278 	this->kern_lines[i] = NUL(String);
1279     }
1280 
1281     int os = 0;
1282     unsigned short ukl = 0;
1283     unsigned char* ukv = 0;
1284     XmStringComponentType uktag = 0;
1285     char* label_buf = NUL(char*);
1286     tag = NUL(char*);
1287     while (1) {
1288 	int len;
1289 	XmStringComponentType retVal =
1290 	    XmStringGetNextComponent (cxt, &text, &tag, &dir, &uktag, &ukl, &ukv);
1291 	if (retVal == XmSTRING_COMPONENT_END) break;
1292 	switch (retVal) {
1293 	    case XmSTRING_COMPONENT_TEXT:
1294 	    case XmSTRING_COMPONENT_LOCALE_TEXT:
1295 		ASSERT (next_kern_line < this->line_count);
1296 		len = strlen(text);
1297 		label_buf = this->kern_lines[next_kern_line++] =
1298 		    new char[len+2];
1299 		strcpy (&label_buf[os], text);
1300 		os+= len;
1301 		XtFree(text),text = NUL(char*);
1302 		break;
1303 	    case XmSTRING_COMPONENT_SEPARATOR:
1304 		ASSERT (next_kern_line <= this->line_count);
1305 		if (!label_buf) {
1306 		    label_buf = this->kern_lines[next_kern_line++] =
1307 			new char[8];
1308 		}
1309 		label_buf[os++] = '\n';
1310 		label_buf[os] = '\0';
1311 		os = 0;
1312 		label_buf = NUL(char*);
1313 		break;
1314 	    default:
1315 		if (tag) XtFree(tag),tag=NUL(char*);
1316 		break;
1317 	}
1318     }
1319     XmStringFreeContext(cxt);
1320 
1321     XmFontList xmfl = this->decorator->getFontList();
1322 
1323     for (i=0; i<this->line_count; i++) {
1324 	if (this->kern_lines[i] == NUL(char*)) {
1325 	    this->kern_lines[i] = new char[2];
1326 	    this->kern_lines[i][0]= '\0';
1327 	}
1328 
1329 	XmString xmstr = XmStringCreateLtoR (this->kern_lines[i], font);
1330 	this->kern_lengths[i] = XmStringWidth (xmfl, xmstr);
1331 	XmStringFree(xmstr);
1332     }
1333     return TRUE;
1334 }
1335 
1336