dtcalc: change from obsoleted MAXFLOAT to FLT_MAX from std C
[oweals/cde.git] / cde / examples / template / template.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: template.c /main/6 1999/09/20 15:48:19 mgreess $ */
24 /*
25  * (c) Copyright 1993, 1994 Hewlett-Packard Company     
26  * (c) Copyright 1993, 1994 International Business Machines Corp.
27  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.
28  * (c) Copyright 1993, 1994 Novell, Inc.
29  */
30
31
32 /*
33  * template.c
34  *
35  * Example code for typical Dt application
36  *
37  * Simple spray-can drawing tool
38  */
39
40 #include <X11/IntrinsicP.h>
41 #include <X11/CoreP.h>
42 #include <nl_types.h>
43 #include <Xm/XmAll.h>
44 #include <Dt/Dnd.h>
45 #include <Dt/Dts.h>
46 #include <Dt/HelpDialog.h>
47 #include <Tt/tttk.h>
48
49 /* default brush - used when the brush bitmap file is not found at run-time. */
50 #include "default_brush.bm"
51
52 /* Portability for catopen */
53 #if !defined(NL_CAT_LOCALE)
54 #define NL_CAT_LOCALE 0
55 #endif
56
57
58 #define ApplicationClass "Template"
59 #define MessageCatalog "template.cat"
60 #define HelpVolume "template"
61 #define HelpTopic "_hometopic"
62 #define IconPixmap "template_icon.pm"
63 #define IconMask "template_icon_m.bm"
64 #define Suffix ".template"
65 #define SuffixLength 9
66 #define UnnamedFile "unnamed.template"
67 #define SearchPattern "*.template"
68 #define DataType "TemplateData"
69 #define ToolTalkPType "DT_Example_Template"
70 #define FileSignature "@template@\n"
71
72 #define MallocInc 10
73
74 typedef struct _WindowData {
75     int                 npoints;
76     int                 nalloc;
77     XPoint              *points;
78     Widget              shell;
79     Widget              openDialog;
80     Widget              saveDialog;
81     char                *name;          /* NULL if no file */
82     struct _WindowData  *next;
83 } WindowData;
84
85 static XtAppContext appContext;
86 static nl_catd msgCatalog;
87 static Widget appShell;
88 static WindowData *windowList = NULL;
89 static XContext wdContext;
90 static Atom WM_DELETE_WINDOW;
91 static Atom WM_SAVE_YOURSELF;
92 static char *appnameString;
93 static char *separatorString;
94 static char *untitledString;
95 static char *argv0;
96 static char *programName;
97
98 /* ToolTalk stuff */
99 static int ttfd;
100 static char *procid;
101 static Tt_pattern *ttpat;
102 static Tt_status ttrc;
103 static Tt_message HandleTtMedia(Tt_message, void *, Tttk_op, Tt_status,
104         unsigned char *, int, char *, char *);
105
106 #define DrawingTranslations "#replace\
107         <Btn1Down>: DrawingAreaInput()\n\
108         <Btn1Motion>: DrawingAreaInput()"
109
110 static void ClearCb(Widget, XtPointer, XtPointer);
111 static void HelpCb(Widget, XtPointer, XtPointer);
112 static void NewCb(Widget, XtPointer, XtPointer);
113 static void OpenCb(Widget, XtPointer, XtPointer);
114 static void OpenOkCb(Widget, XtPointer, XtPointer);
115 static void SaveCb(Widget, XtPointer, XtPointer);
116 static void SaveOkCb(Widget, XtPointer, XtPointer);
117 static void PrintCb(Widget, XtPointer, XtPointer);
118 static void ExitCb(Widget, XtPointer, XtPointer);
119 static void ExposeCb(Widget, XtPointer, XtPointer);
120 static void InputCb(Widget, XtPointer, XtPointer);
121 static void DropTransferCb(Widget, XtPointer, XtPointer);
122 static void SaveSessionCb(Widget, XtPointer, XtPointer);
123
124 typedef enum {
125     LOAD_EMPTY, LOAD_FILE, LOAD_BUFFER
126 } LoadType;
127
128 static void Fatal(char*);
129 static void ReallyExit(int);
130 static void SetTitle(Widget,char*);
131 static Boolean NewWindow(LoadType,char*,int);
132 static WindowData *NewData(void);
133 static void AssocData(WindowData*,Widget);
134 static WindowData *FindData(Widget);
135 static void DestroyData(WindowData*);
136 static Boolean LoadFile(WindowData*,char*);
137 static Boolean LoadBuffer(WindowData*,void*,int);
138 static void FreeData(WindowData*);
139 static void PrintData(WindowData*);
140 static void AddPoint(WindowData*, int, int);
141 static void DrawPoint(Widget, int, int);
142 static char* AppendString(char*,char*);
143
144 static XtCallbackRec DropTransferCbList[] = {
145         { DropTransferCb, NULL},
146         { NULL, NULL}
147 };
148
149 static XrmOptionDescRec optionTable[] = {
150     {"-print",    ".printMode",   XrmoptionIsArg, NULL},
151     {"-server",   ".serverMode",  XrmoptionIsArg, NULL},
152 };
153
154 typedef struct {
155     String printMode;
156     String serverMode;
157 } appResourceRec;
158
159 static XtResource appResources[] = {
160     { "printMode", "PrintMode", XtRString, sizeof(String),
161         XtOffsetOf(appResourceRec, printMode), XtRString, NULL },
162     { "serverMode", "ServerMode", XtRString, sizeof(String),
163         XtOffsetOf(appResourceRec, serverMode), XtRString, NULL },
164 };
165
166 main(int argc, char **argv)
167 {
168     int i;
169     appResourceRec argvals;
170     char *errfmt, *errmsg, *statmsg;
171
172     argv0 = argv[0];
173     programName = strrchr(argv[0], '/');
174     if (programName == NULL)
175         programName = argv[0];
176     else
177         ++programName;
178
179     XtSetLanguageProc(NULL, NULL, NULL);
180
181     appShell = XtAppInitialize(&appContext, ApplicationClass,
182                                optionTable, XtNumber(optionTable),
183                                &argc, argv, NULL, NULL, 0);
184
185     msgCatalog = catopen(MessageCatalog, NL_CAT_LOCALE);
186
187     XtGetApplicationResources(appShell, &argvals,
188                               appResources, XtNumber(appResources),
189                               NULL, 0);
190
191     wdContext = XUniqueContext();
192
193     WM_DELETE_WINDOW = XmInternAtom(XtDisplay(appShell), "WM_DELETE_WINDOW",
194                                     False);
195     WM_SAVE_YOURSELF = XmInternAtom(XtDisplay(appShell), "WM_SAVE_YOURSELF",
196                                     False);
197
198     appnameString = catgets(msgCatalog, 1, 1, "Template");
199     separatorString = catgets(msgCatalog, 1, 5, " - ");
200     untitledString = catgets(msgCatalog, 1, 6, "(untitled)");
201
202     if (argvals.printMode != NULL) {
203         /* Load up each file and print it, then exit */
204         WindowData *wd = NewData();
205         for (i = 1; i < argc; ++i) {
206             if (LoadFile(wd, argv[i]))
207                 PrintData(wd);
208             else
209                 fprintf(stderr,
210                     catgets(msgCatalog, 1, 10, "template: can't open %s\n"),
211                     argv[i]);
212         }
213         DestroyData(wd);
214         exit(0);
215     }
216
217     /* Initialize Data Typing and ToolTalk */
218
219     DtDtsLoadDataTypes();
220
221     procid = ttdt_open(&ttfd, appnameString, "CDE", "1.0", True);
222     if ((ttrc = tt_ptr_error(procid)) != TT_OK) {
223         errfmt = catgets(msgCatalog, 1, 7, "ttdt_open failed:\n%s");
224         statmsg = tt_status_message(ttrc);
225         errmsg = XtMalloc(strlen(errfmt) + strlen(statmsg) + 2);
226         sprintf(errmsg, errfmt, statmsg);
227
228         Fatal(errmsg);
229         XtFree(errmsg);
230     }
231
232     ttrc = ttmedia_ptype_declare(ToolTalkPType, 0, HandleTtMedia,
233                                  NULL, True);
234     if (tt_is_err(ttrc)) {
235         errfmt = catgets(msgCatalog, 1, 8, "ttmedia_ptype_declare failed:\n%s");
236         statmsg = tt_status_message(status);
237         errmsg = XtMalloc(strlen(errfmt) + strlen(statmsg) + 2);
238         sprintf(errmsg, errfmt, statmsg);
239
240         Fatal(errmsg);
241         XtFree(errmsg);
242     }
243
244     ttpat = ttdt_session_join(NULL, NULL, NULL, NULL, True);
245     if ((ttrc = tt_ptr_error(ttpat)) != TT_OK) {
246         errfmt = catgets(msgCatalog, 1, 9, "ttdt_session_join failed:\n%s");
247         statmsg = tt_status_message(status);
248         errmsg = XtMalloc(strlen(errfmt) + strlen(statmsg) + 2);
249         sprintf(errmsg, errfmt, statmsg);
250
251         Fatal(errmsg);
252         XtFree(errmsg);
253     }
254
255     XtAppAddInput(appContext, ttfd, (XtPointer)XtInputReadMask,
256                   tttk_Xt_input_handler, NULL);
257
258     if (argvals.serverMode != NULL) {
259         /*
260          * We're in server mode.  Thus do nothing until requested to do so
261          * through ToolTalk.
262          */
263     } else if (argc < 2) {
264         /* No files given, so put up an untitled window. */
265         (void) NewWindow(LOAD_EMPTY, NULL, 0);
266     } else {
267         /* Load each file into its own window. */
268         for (i = 1; i < argc; ++i)
269             (void) NewWindow(LOAD_FILE, argv[i], 0);
270     }
271
272     /*
273      * Start the GUI.  Note that we explicitly do not realize the appShell
274      * widget, since it is the unmapped parent of all of the top-level shells 
275      * we pop up.
276      */
277
278     XtAppMainLoop(appContext);
279     ReallyExit(0);
280 }
281
282
283 /*
284  * Close ToolTalk and exit.
285  */
286 static void ReallyExit(int rc)
287 {
288     tt_close();
289     exit(rc);
290 }
291
292
293 /*
294  * Issue an error message and exit.
295  */
296 static void Fatal(char *msg)
297 {
298     fprintf(stderr, "%s: %s\n", programName, msg);
299     exit(1);
300 }
301
302
303 /*
304  * Create a new top-level window.  If loadtype is LOAD_EMPTY, name_or_buf and 
305  * len are ignored.  If loadtype is LOAD_FILE, name_or_buf should point to the 
306  * name of the file to load, and len is ignored.  If loadtype is LOAD_BUFFER,
307  * name_or_buf is a pointer to the data buffer and len is its length.
308  */
309 static Boolean
310 NewWindow(LoadType loadtype, char *name_or_buf, int len)
311 {
312     Widget toplevel, mainWindow, menuBar, frame1, frame2,
313         drawingArea, pd, cb, pb;
314     char *title;
315     XmString labelString;
316     XtTranslations drawingTranslations;
317     Pixmap iconPixmap;
318     Pixmap iconMask;
319     Pixel fg, bg;
320     Arg args[20];
321     int n;
322     WindowData *wd;
323
324     wd = NewData();
325
326     n = 0;
327     toplevel = XtCreatePopupShell("foo", topLevelShellWidgetClass,
328                 appShell, args, n);
329
330     /* Create main window */
331
332     n = 0;
333     mainWindow = XmCreateMainWindow(toplevel, "mainWindow", args, n);
334     XtManageChild(mainWindow);
335
336     /* Set window manager title and icon */
337
338     XtVaGetValues(mainWindow, XmNforeground, &fg, XmNbackground, &bg, NULL);
339     iconPixmap = XmGetPixmap(XtScreen(toplevel), IconPixmap, fg, bg);
340     iconMask = XmGetPixmapByDepth(XtScreen(toplevel), IconMask, 1, 0, 1);
341     XtVaSetValues(toplevel,
342                 XmNiconName, appnameString,
343                 XmNiconPixmap, iconPixmap,
344                 XmNiconMask, iconMask,
345                 NULL);
346
347     /* Create the GUI */
348
349     menuBar = XmCreateMenuBar(mainWindow, "menuBar", NULL, 0);
350     XtManageChild(menuBar);
351
352     /* File menu */
353
354     pd = XmCreatePulldownMenu(menuBar, "fileMenu", NULL, 0);
355
356     labelString = XmStringCreateLocalized(catgets(msgCatalog, 2, 1, "File"));
357     n = 0;
358     XtSetArg(args[n], XmNlabelString, labelString); n++;
359     XtSetArg(args[n], XmNmnemonic, 'F'); n++;
360     XtSetArg(args[n], XmNsubMenuId, pd); n++;
361     cb = XmCreateCascadeButton(menuBar, "fileCascade", args, n);
362     XtManageChild(cb);
363     XmStringFree(labelString);
364
365     labelString = XmStringCreateLocalized(catgets(msgCatalog, 2, 9, "New..."));
366     n = 0;
367     XtSetArg(args[n], XmNlabelString, labelString); n++;
368     XtSetArg(args[n], XmNmnemonic, 'N'); n++;
369     pb = XmCreatePushButton(pd, "newButton", args, n);
370     XtManageChild(pb);
371     XtAddCallback(pb, XmNactivateCallback, NewCb, NULL);
372     XmStringFree(labelString);
373
374     labelString = XmStringCreateLocalized(catgets(msgCatalog, 2, 2, "Open..."));
375     n = 0;
376     XtSetArg(args[n], XmNlabelString, labelString); n++;
377     XtSetArg(args[n], XmNmnemonic, 'O'); n++;
378     pb = XmCreatePushButton(pd, "openButton", args, n);
379     XtManageChild(pb);
380     XtAddCallback(pb, XmNactivateCallback, OpenCb, (XtPointer)wd);
381     XmStringFree(labelString);
382
383     labelString = XmStringCreateLocalized(catgets(msgCatalog, 2, 3,
384                                                                 "Save As..."));
385     n = 0;
386     XtSetArg(args[n], XmNlabelString, labelString); n++;
387     XtSetArg(args[n], XmNmnemonic, 'S'); n++;
388     pb = XmCreatePushButton(pd, "saveButton", args, n);
389     XtManageChild(pb);
390     XtAddCallback(pb, XmNactivateCallback, SaveCb, (XtPointer)wd);
391     XmStringFree(labelString);
392
393     labelString = XmStringCreateLocalized(catgets(msgCatalog, 2, 4, "Print"));
394     n = 0;
395     XtSetArg(args[n], XmNlabelString, labelString); n++;
396     XtSetArg(args[n], XmNmnemonic, 'P'); n++;
397     pb = XmCreatePushButton(pd, "printButton", args, n);
398     XtManageChild(pb);
399     XtAddCallback(pb, XmNactivateCallback, PrintCb, (XtPointer)wd);
400     XmStringFree(labelString);
401
402     labelString = XmStringCreateLocalized(catgets(msgCatalog, 2, 5, "Clear"));
403     n = 0;
404     XtSetArg(args[n], XmNlabelString, labelString); n++;
405     XtSetArg(args[n], XmNmnemonic, 'C'); n++;
406     pb = XmCreatePushButton(pd, "clearButton", args, n);
407     XtManageChild(pb);
408     XtAddCallback(pb, XmNactivateCallback, ClearCb, (XtPointer)wd);
409     XmStringFree(labelString);
410
411     labelString = XmStringCreateLocalized(catgets(msgCatalog, 2, 6, "Exit"));
412     n = 0;
413     XtSetArg(args[n], XmNlabelString, labelString); n++;
414     XtSetArg(args[n], XmNmnemonic, 'E'); n++;
415     pb = XmCreatePushButton(pd, "exitButton", args, n);
416     XtManageChild(pb);
417     XtAddCallback(pb, XmNactivateCallback, ExitCb, (XtPointer)wd);
418     XmStringFree(labelString);
419
420     /* Help menu */
421
422     pd = XmCreatePulldownMenu(menuBar, "helpMenu", NULL, 0);
423
424     labelString = XmStringCreateLocalized(catgets(msgCatalog, 2, 7, "Help"));
425     n = 0;
426     XtSetArg(args[n], XmNlabelString, labelString); n++;
427     XtSetArg(args[n], XmNmnemonic, 'H'); n++;
428     XtSetArg(args[n], XmNsubMenuId, pd); n++;
429     cb = XmCreateCascadeButton(menuBar, "helpCascade", args, n);
430     XtManageChild(cb);
431     XmStringFree(labelString);
432
433     XtVaSetValues(menuBar, XmNmenuHelpWidget, cb, NULL);
434
435     labelString = XmStringCreateLocalized(catgets(msgCatalog,2,8, "Overview..."));
436     n = 0;
437     XtSetArg(args[n], XmNlabelString, labelString); n++;
438     XtSetArg(args[n], XmNmnemonic, 'O'); n++;
439     pb = XmCreatePushButton(pd, "helpButton", args, n);
440     XtManageChild(pb);
441     XtAddCallback(pb, XmNactivateCallback, HelpCb, NULL);
442     XmStringFree(labelString);
443
444     /* Drawing work area */
445
446     n = 0;
447     XtSetArg(args[n], XmNshadowThickness, 0); n++;
448     XtSetArg(args[n], XmNmarginWidth, 20); n++;
449     XtSetArg(args[n], XmNmarginHeight, 20); n++;
450     frame1 = XmCreateFrame(mainWindow, "frame1", args, n);
451     XtManageChild(frame1);
452
453     n = 0;
454     XtSetArg(args[n], XmNshadowType, XmSHADOW_ETCHED_OUT); n++;
455     frame2 = XmCreateFrame(frame1, "frame2", args, n);
456     XtManageChild(frame2);
457
458     drawingTranslations = XtParseTranslationTable(DrawingTranslations);
459
460     n = 0;
461     XtSetArg(args[n], XmNunitType, Xm100TH_MILLIMETERS); n++;
462     XtSetArg(args[n], XmNwidth, 12000); n++;
463     XtSetArg(args[n], XmNheight, 12000); n++;
464     XtSetArg(args[n], XmNtranslations, drawingTranslations); n++;
465     drawingArea = XmCreateDrawingArea(frame2, "drawingArea", args, n);
466     XtManageChild(drawingArea);
467     XtAddCallback(drawingArea, XmNexposeCallback, ExposeCb, NULL);
468     XtAddCallback(drawingArea, XmNinputCallback, InputCb, NULL);
469
470     DtDndDropRegister(drawingArea,
471                       DtDND_FILENAME_TRANSFER | DtDND_BUFFER_TRANSFER,
472                       XmDROP_COPY, DropTransferCbList, NULL, 0);
473
474     XmAddWMProtocolCallback(toplevel, WM_DELETE_WINDOW, ExitCb, (XtPointer)wd);
475     XmAddWMProtocolCallback(toplevel, WM_SAVE_YOURSELF, SaveSessionCb,
476                             (XtPointer)NULL);
477
478     XtRealizeWidget(toplevel);
479     XtPopup(toplevel, XtGrabNone);
480
481     AssocData(wd, toplevel);
482
483     switch (loadtype) {
484     case LOAD_EMPTY:
485         SetTitle(toplevel, NULL);
486         return True;
487     case LOAD_FILE:
488         return LoadFile(wd, name_or_buf);
489     case LOAD_BUFFER:
490         return LoadBuffer(wd, name_or_buf, len);
491     }
492     /*NOTREACHED*/
493 }
494
495
496 /*
497  * Display the help system. On the first call, create the help widget.
498  */
499
500 static void HelpCb(Widget w, XtPointer cd, XtPointer cb)
501 {
502     static Widget helpDialog = NULL;
503
504     if (helpDialog == NULL) {
505         char *title;
506         Arg args[10];
507         int n;
508
509         n = 0;
510         XtSetArg(args[n], DtNhelpVolume, HelpVolume); n++;
511         XtSetArg(args[n], DtNhelpType, DtHELP_TYPE_TOPIC); n++;
512         XtSetArg(args[n], DtNlocationId, HelpTopic); n++;
513         helpDialog = DtCreateHelpDialog(appShell, "helpDialog", args, n);
514
515         title = catgets(msgCatalog, 1, 4, "Template Help");
516         XtVaSetValues(XtParent(helpDialog), XmNtitle, title, NULL);
517     } else {
518         XtVaSetValues(helpDialog,
519                       DtNhelpVolume, HelpVolume,
520                       DtNlocationId, HelpTopic,
521                       NULL);
522     }
523     XtManageChild(helpDialog);
524 }
525
526
527 /*
528  * Clear the display and drawing data.
529  */
530
531 static void ClearCb(Widget w, XtPointer cd, XtPointer cb)
532 {
533     WindowData *wd = (WindowData *)cd;
534     Widget drawingArea = XtNameToWidget(wd->shell, "*drawingArea");
535
536     FreeData(wd);
537     XClearWindow(XtDisplay(drawingArea), XtWindow(drawingArea));
538 }
539
540
541 /*
542  * Create a new top-level window.
543  */
544 static void NewCb(Widget w, XtPointer cd, XtPointer cb)
545 {
546     (void) NewWindow(LOAD_EMPTY, NULL, 0);
547 }
548
549
550 /*
551  * Display a File dialog. On the first call, create the dialog.
552  */
553
554 static void OpenCb(Widget w, XtPointer cd, XtPointer cb)
555 {
556     WindowData *wd = (WindowData *)cd;
557
558     if (wd->openDialog == NULL) {
559         XmString pattern;
560         XmString dialogTitle;
561         Arg args[20];
562         int n;
563
564         dialogTitle = XmStringCreateLocalized(catgets(msgCatalog, 1, 2,
565                                         "Template Open"));
566         pattern = XmStringCreateLocalized(SearchPattern);
567         n = 0;
568         XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL);n++;
569         XtSetArg(args[n], XmNautoUnmanage, True); n++;
570         XtSetArg(args[n], XmNpattern, pattern); n++;
571         XtSetArg(args[n], XmNdialogTitle, dialogTitle); n++;
572         wd->openDialog = XmCreateFileSelectionDialog(wd->shell, "openDialog",
573                                                      args, n);
574         XtUnmanageChild(XtNameToWidget(wd->openDialog, "*Help"));
575         XtAddCallback(wd->openDialog, XmNokCallback, OpenOkCb, cd);
576         XmStringFree(pattern);
577         XmStringFree(dialogTitle);
578     }
579     XtManageChild(wd->openDialog);
580 }
581
582 static void OpenOkCb(Widget w, XtPointer cd, XtPointer cb)
583 {
584     char *fileName;
585     WindowData *wd = (WindowData *)cd;
586     XmFileSelectionBoxCallbackStruct *fsbcs =
587         (XmFileSelectionBoxCallbackStruct *) cb;
588     Widget drawingArea = XtNameToWidget(wd->shell, "*drawingArea");
589
590     fileName = XmStringUnparse(fsbcs->value, NULL, XmCHARSET_TEXT,
591                                XmCHARSET_TEXT, NULL, 0, XmOUTPUT_ALL);
592     (void) LoadFile(wd, fileName);
593     XtFree(fileName);
594
595     XClearArea(XtDisplay(drawingArea), XtWindow(drawingArea), 0, 0, 0, 0, True);
596 }
597
598
599 /*
600  * Display a File dialog. On the first call, create the dialog.
601  */
602
603 static void SaveCb(Widget w, XtPointer cd, XtPointer cb)
604 {
605     WindowData *wd = (WindowData *)cd;
606
607     if (wd->saveDialog == NULL) {
608         XmString pattern;
609         XmString dialogTitle;
610         Arg args[20];
611         int n;
612
613         dialogTitle = XmStringCreateLocalized(catgets(msgCatalog, 1, 3,
614                                         "Template Save As"));
615         pattern = XmStringCreateLocalized(SearchPattern);
616         n = 0;
617         XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL);n++;
618         XtSetArg(args[n], XmNautoUnmanage, True); n++;
619         XtSetArg(args[n], XmNpattern, pattern); n++;
620         XtSetArg(args[n], XmNdialogTitle, dialogTitle); n++;
621         wd->saveDialog = XmCreateFileSelectionDialog(appShell, "saveAsDialog",
622                                                                 args, n);
623         XtUnmanageChild(XtNameToWidget(wd->saveDialog, "*Help"));
624         XtAddCallback(wd->saveDialog, XmNokCallback, SaveOkCb, cd);
625         XmStringFree(pattern);
626         XmStringFree(dialogTitle);
627     }
628
629     XtManageChild(wd->saveDialog);
630 }
631
632 static void SaveOkCb(Widget w, XtPointer cd, XtPointer cb)
633 {
634     FILE *fp;
635     int i;
636     char * fileName;
637     int fileLength;
638     WindowData *wd = (WindowData *)cd;
639
640     XmFileSelectionBoxCallbackStruct *fsbcs =
641         (XmFileSelectionBoxCallbackStruct *) cb;
642     XmStringGetLtoR(fsbcs->value, XmFONTLIST_DEFAULT_TAG, &fileName);
643     fileLength = strlen(fileName);
644
645     if (fileName[fileLength-1]== '/') {
646         fileName = AppendString(fileName, UnnamedFile);
647         fileLength = strlen(fileName);
648     }
649
650     if (fileLength > SuffixLength) {
651         if (strcmp(fileName+fileLength-SuffixLength, Suffix) != 0) {
652             fileName = AppendString(fileName,Suffix);
653         }
654     }
655     else {
656         fileName = AppendString(fileName, Suffix);
657     }
658
659     if ((fp = fopen(fileName, "w")) != NULL) {
660         fputs(FileSignature, fp);
661         for (i=0; i < wd->npoints; i++)
662             fprintf(fp, "%d %d\n", wd->points[i].x, wd->points[i].y);
663         fclose(fp);
664         XtFree(wd->name);
665         wd->name = XtNewString(fileName);
666         SetTitle(wd->shell, fileName);
667     }
668
669     XtFree(fileName);
670 }
671
672 static char* AppendString(char *base, char *suffix)
673 {
674     char *file;
675
676     file = XtMalloc(strlen(base)+strlen(suffix)+1);
677     strcpy(file, base);
678     strcat(file, suffix);
679     XtFree(base);
680     return(file);
681 }
682
683
684 /*
685  * Respond to the TemplatePrint action by printing the data file
686  * specified to the '-print' option.
687  */
688
689 static void PrintCb(Widget w, XtPointer cd, XtPointer cb)
690 {
691     WindowData *wd = (WindowData *)cd;
692     PrintData(wd);
693 }
694
695
696 /*
697  * Redraw the display when exposed.
698  */
699
700 static void ExposeCb(Widget w, XtPointer cd, XtPointer cb)
701 {
702     XmDrawingAreaCallbackStruct *dcb = (XmDrawingAreaCallbackStruct*)cb;
703     WindowData *wd;
704     int i;
705
706     if (dcb->event != NULL && dcb->event->xexpose.count > 0) return;
707
708     wd = FindData(w);
709
710     for (i = 0; i < wd->npoints; i++)
711         DrawPoint(w, wd->points[i].x, wd->points[i].y);
712 }
713
714
715 /*
716  * Process mouse input.
717  */
718
719 static void InputCb(Widget w, XtPointer cd, XtPointer cb)
720 {
721     XmDrawingAreaCallbackStruct *dcb = (XmDrawingAreaCallbackStruct*)cb;
722     WindowData *wd;
723
724     if (dcb->event->xany.type != ButtonPress &&
725         dcb->event->xany.type != MotionNotify)
726         return;
727
728     wd = FindData(w);
729
730     AddPoint(wd, dcb->event->xbutton.x, dcb->event->xbutton.y);
731     DrawPoint(w, dcb->event->xbutton.x, dcb->event->xbutton.y);
732 }
733
734
735 /*
736  * Delete the current window.  If there are no more windows, exit the 
737  * application.
738  */
739
740 static void ExitCb(Widget w, XtPointer cd, XtPointer cb)
741 {
742     WindowData *wd = (WindowData *)cd;
743     DestroyData(wd);
744     if (windowList == NULL)
745         ReallyExit(0);
746 }
747
748
749 /*
750  * Handle WM_SAVE_YOURSELF by updating the command line with all the files
751  * currently being edited.
752  */
753 static void SaveSessionCb(Widget w, XtPointer cd, XtPointer cb)
754 {
755     char **command;
756     int argcount = 1;           /* starts at 1 for command name */
757     WindowData *wd = windowList;
758     int i;
759     Widget first;
760
761     /* count the number of windows bound to files */
762     for (wd = windowList; wd != NULL; wd = wd->next) {
763         if (wd->name != NULL)
764             ++argcount;
765     }
766
767     command = (char **)XtMalloc(argcount*sizeof(char*));
768     command[0] = argv0;
769     i = 1;
770     for (wd = windowList; wd != NULL; wd = wd->next) {
771         if (wd->name != NULL)
772             command[i++] = wd->name;
773     }
774
775     first = windowList->shell;
776         XSetCommand(XtDisplay(first), XtWindow(first), command, i);
777     if (w != first)
778         XChangeProperty(XtDisplay(w), XtWindow(w), XA_WM_COMMAND, XA_STRING, 8,
779                         PropModeReplace, NULL, 0);
780     XtFree((char *)command);
781 }
782
783
784 /*
785  * Accept .template files dropped on the drawing window.
786  */
787
788 static void DropTransferCb(Widget drawingArea, XtPointer cd, XtPointer cb)
789 {
790     DtDndTransferCallbackStruct *dcb = (DtDndTransferCallbackStruct*)cb;
791     WindowData *wd = FindData(drawingArea);
792     char *fileName;
793     char *dataType;
794     void *bufPtr;
795     int bufLen;
796
797     dcb->status = DtDND_FAILURE;
798
799     if (dcb->dropData->numItems > 1)
800         return;
801
802     switch (dcb->dropData->protocol) {
803
804     case DtDND_FILENAME_TRANSFER:
805         fileName = dcb->dropData->data.files[0];
806         dataType = DtDtsFileToDataType(fileName);
807         if (strcmp(dataType, DataType) != 0) {
808             DtDtsFreeDataType(dataType);
809             return;
810         }
811         if (LoadFile(wd, fileName))
812             dcb->status = DtDND_SUCCESS;
813         DtDtsFreeDataType(dataType);
814         break;
815
816     case DtDND_BUFFER_TRANSFER:
817         bufPtr   = dcb->dropData->data.buffers[0].bp;
818         bufLen   = dcb->dropData->data.buffers[0].size;
819         dataType = DtDtsBufferToDataType(bufPtr, bufLen, NULL);
820         if (strcmp(dataType, DataType) != 0) {
821             DtDtsFreeDataType(dataType);
822             return;
823         }
824         if (LoadBuffer(wd, bufPtr, bufLen))
825             dcb->status = DtDND_SUCCESS;
826         DtDtsFreeDataType(dataType);
827         break;
828     }
829
830     XClearArea(XtDisplay(drawingArea), XtWindow(drawingArea),
831         0, 0, 0, 0, True);
832 }
833
834
835 /*
836  * Set the widget's title to the program name followed by a separator followed
837  * by the trailing pathname component of the filename.  The widget must be a
838  * shell.  If name is NULL, uses "(untitled)" instead.
839  */
840 static void
841 SetTitle(Widget w, char *name)
842 {
843     char buf[1000];
844     char *p;
845
846     if (name == NULL) {
847         p = untitledString;
848     } else {
849         p = strrchr(name, '/');
850         if (p == NULL)
851             p = name;
852         else
853             p++;
854     }
855     sprintf(buf, "%s%s%s", appnameString, separatorString, p);
856     XtVaSetValues(w, XtNtitle, buf, NULL);
857 }
858
859
860 /*
861  * Create a new WindowData structure.  The widget passed in should be the 
862  * shell associated with this data.  It must be realized.
863  */
864 static WindowData *
865 NewData(void)
866 {
867     WindowData *wd;
868
869     wd = XtNew(WindowData);
870     wd->npoints = 0;
871     wd->nalloc = 0;
872     wd->points = NULL;
873     wd->shell = NULL;
874     wd->openDialog = NULL;
875     wd->saveDialog = NULL;
876     wd->name = NULL;
877
878     /* push it onto the front of the global list */
879     wd->next = windowList;
880     windowList = wd;
881
882     return wd;
883 }
884
885
886 /*
887  * Associate a top-level shell with a WindowData structure.  The shell must be 
888  * realized.
889  */
890 static void
891 AssocData(WindowData *wd, Widget w)
892 {
893     wd->shell = w;
894     XSaveContext(XtDisplay(w), XtWindow(w), wdContext, (XPointer)wd);
895 }
896
897
898 /*
899  * Given a widget, find the WindowData structure associated with it.  First it 
900  * finds the shell ancestor of this widget, and then it extracts the window 
901  * data from using the X Context Manager.
902  */
903 static WindowData *
904 FindData(Widget w)
905 {
906     WindowData *wd = NULL;
907
908     while (! XtIsShell(w))
909         w = XtParent(w);
910     XFindContext(XtDisplay(w), XtWindow(w), wdContext, &wd);
911     return wd;
912 }
913
914
915 /*
916  * Destroy a WindowData structure.
917  */
918 static void
919 DestroyData(WindowData *wd)
920 {
921     WindowData **p;
922
923     FreeData(wd);
924
925     if (wd->shell != NULL)
926         XtDestroyWidget(wd->shell);
927     if (wd->openDialog != NULL)
928         XtDestroyWidget(wd->openDialog);
929     if (wd->saveDialog != NULL)
930         XtDestroyWidget(wd->saveDialog);
931
932     /* remove from the global list */
933
934     p = &windowList;
935     while (*p != NULL) {
936         if (*p == wd) {
937             *p = wd->next;
938             break;
939         }
940         p = &((*p)->next);
941     }
942
943     XtFree((char *)wd);
944 }
945
946 /*
947  * Load a .template data file
948  */
949
950 static Boolean LoadFile(WindowData *wd, char *fileName)
951 {
952     FILE *fp;
953     int np, i, x, y;
954     char sig[100];
955
956     if ((fp = fopen(fileName, "r")) == NULL)
957         return False;
958
959     if (fgets(sig, sizeof(sig), fp) == NULL) {
960         fclose(fp);
961         return False;
962     }
963
964     if (strcmp(sig, FileSignature) != 0) {
965         fclose(fp);
966         return False;
967     }
968
969     FreeData(wd);
970     while (fscanf(fp, "%d %d", &x, &y) != EOF)
971         AddPoint(wd, x, y);
972     fclose(fp);
973     if (wd->shell != NULL)
974         SetTitle(wd->shell, fileName);
975     wd->name = XtNewString(fileName);
976     return True;
977 }
978
979
980 /*
981  * Load a .template buffer
982  */
983
984 static Boolean LoadBuffer(WindowData *wd, void *buf, int len)
985 {
986     char *bufp = (char *)buf;
987     char *endp = bufp + len;
988     int r, x, y, l;
989     char sig[100];
990
991     (void) strncpy(sig, bufp, sizeof(FileSignature) - 1);
992     sig[sizeof(FileSignature) - 1] = '\0';
993     if (strcmp(sig, FileSignature) != 0)
994         return False;
995     bufp += sizeof(FileSignature) - 1;
996
997     FreeData(wd);
998     while (bufp < endp) {
999         if (sscanf(bufp, "%d %d\n%n", &x, &y, &l) != 2)
1000             return False;
1001         AddPoint(wd, x, y);
1002         bufp += l;
1003     }
1004     if (wd->shell != NULL)
1005         SetTitle(wd->shell, NULL);
1006     return True;
1007 }
1008
1009
1010 static Tt_message
1011 HandleTtMedia(
1012     Tt_message          msg,
1013     void                *clientdata,
1014     Tttk_op             op,
1015     Tt_status           diagnosis,
1016     unsigned char       *contents,
1017     int                 len,
1018     char                *file,
1019     char                *docname)
1020 {
1021     int mark = tt_mark();
1022     char *opstr = tt_message_op(msg);
1023
1024     if (strcmp(opstr, "Edit") == 0) {
1025         if (file != NULL) {
1026             if (NewWindow(LOAD_FILE, file, 0))
1027                 tt_message_reply(msg);
1028             else
1029                 tttk_message_fail(msg, TT_ERR_OP, "open failed", False);
1030         } else if (contents != NULL && len > 0) {
1031             if (NewWindow(LOAD_BUFFER, (char *)contents, len))
1032                 tt_message_reply(msg);
1033             else
1034                 tttk_message_fail(msg, TT_ERR_OP, "load buffer failed", False);
1035         } else {
1036             tttk_message_fail(msg, TT_ERR_OP, "no file or buffer", False);
1037         }
1038     } else {
1039         tttk_message_fail(msg, TT_ERR_OP, "unsupported message", False);
1040     }
1041
1042     tt_release(mark);
1043     tt_free((char *)contents);
1044     tt_free(file);
1045     tt_free(docname);
1046     tt_message_destroy(msg);
1047     return 0;
1048 }
1049
1050
1051 /*
1052  * Print the drawing data.
1053  *
1054  * This function is intentionally left blank.
1055  */
1056
1057 static void PrintData(WindowData *wd)
1058 {
1059     FILE *PS;
1060     int i;
1061
1062     PS = popen("/usr/bin/lp", "w");
1063
1064     fputs("%!\n", PS);
1065     fputs("clippath pathbbox 0 exch translate pop pop pop\n", PS);
1066     fputs("1 -1 scale 72 72 translate\n", PS);
1067     fputs("newpath 0.5 setgray\n", PS);
1068     fputs("/S { 10 0 360 arc fill } def\n", PS);
1069
1070     for (i = 0; i < wd->npoints; i++)
1071         fprintf(PS, "%d %d S\n", wd->points[i].x, wd->points[i].y);
1072
1073     fputs("showpage\n", PS);
1074     (void) pclose(PS);
1075 }
1076
1077
1078 /*
1079  * Free drawing data structure
1080  */
1081
1082 static void FreeData(WindowData *wd)
1083 {
1084     XtFree((char *)wd->points);
1085     XtFree(wd->name);
1086     wd->points = NULL;
1087     wd->name = NULL;
1088     wd->npoints = 0;
1089     wd->nalloc = 0;
1090     if (wd->shell != NULL)
1091         SetTitle(wd->shell, NULL);
1092 }
1093
1094
1095 /*
1096  * Add a point to the end of the drawing data structure.
1097  */
1098
1099 static void AddPoint(WindowData *wd, int x, int y)
1100 {
1101     if (wd->npoints == wd->nalloc) {
1102         wd->nalloc += MallocInc;
1103         wd->points =
1104             (XPoint*)XtRealloc((char*)wd->points, wd->nalloc * sizeof(XPoint));
1105     }
1106     wd->points[wd->npoints].x = x;
1107     wd->points[wd->npoints].y = y;
1108     wd->npoints += 1;
1109 }
1110
1111
1112 /*
1113  * Draw an airbrush at (x,y)
1114  */
1115
1116 static void DrawPoint(Widget w, int x, int y)
1117 {
1118     static Boolean initialized = False;
1119     static GC gc;
1120     static Pixmap pixmap;
1121     static unsigned int pixmapWidth, pixmapHeight;
1122
1123     if (XtIsRealized(w) == False) return;
1124
1125     if (initialized == False) {
1126         Pixel fg, bg;
1127         unsigned int tmpu;
1128         Window tmpw;
1129         int tmpi;
1130         XGCValues gcv;
1131         unsigned long gcm;
1132
1133         XtVaGetValues(w, XmNforeground, &fg, XmNbackground, &bg, NULL);
1134         pixmap = XmGetPixmapByDepth(XtScreen(w), "template-brush.bm", 1, 0, 1);
1135         if (pixmap == XmUNSPECIFIED_PIXMAP) {
1136             XtAppWarning(appContext, "template:template-brush.bm not installed!");
1137             /* use hardcoded fallback bitmap */
1138             pixmap = XCreatePixmapFromBitmapData(XtDisplay(w), XtWindow(w),
1139                                 (char*)default_brush_bits,
1140                                 default_brush_width,
1141                                 default_brush_height,
1142                                 1, 0, 1);
1143         }
1144         XGetGeometry(XtDisplay(w), pixmap, &tmpw, &tmpi, &tmpi,
1145                         &pixmapWidth, &pixmapHeight, &tmpu, &tmpu);
1146
1147         gcm = GCForeground | GCBackground | GCFillStyle | GCStipple;
1148         gcv.foreground = fg;
1149         gcv.background = bg;
1150         gcv.fill_style = FillStippled;
1151         gcv.stipple = pixmap;
1152         gc = XCreateGC(XtDisplay(w), XtWindow(w), gcm, &gcv);
1153
1154         initialized = True;
1155     };
1156
1157     XSetTSOrigin(XtDisplay(w), gc, x-pixmapWidth/2, y-pixmapWidth/2);
1158     XFillRectangle(XtDisplay(w), XtWindow(w), gc,
1159                         x-pixmapWidth/2, y-pixmapWidth/2,
1160                         pixmapWidth, pixmapHeight);
1161 }
1162