1 /*******************************************************************************
2 *                                                                              *
3 * window.c -- Nirvana Editor window creation/deletion                          *
4 *                                                                              *
5 * Copyright (C) 1999 Mark Edel                                                 *
6 *                                                                              *
7 * This is free software; you can redistribute it and/or modify it under the    *
8 * terms of the GNU General Public License as published by the Free Software    *
9 * Foundation; either version 2 of the License, or (at your option) any later   *
10 * version. In addition, you may distribute version of this program linked to   *
11 * Motif or Open Motif. See README for details.                                 *
12 *                                                                              *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or        *
15 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License        *
16 * for more details.                                                            *
17 *                                                                              *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple     *
20 * Place, Suite 330, Boston, MA  02111-1307 USA                                 *
21 *                                                                              *
22 * Nirvana Text Editor                                                          *
23 * May 10, 1991                                                                 *
24 *                                                                              *
25 * Written by Mark Edel                                                         *
26 *                                                                              *
27 *******************************************************************************/
28 
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
32 
33 #include "window.h"
34 #include "textBuf.h"
35 #include "textSel.h"
36 #include "text.h"
37 #include "textDisp.h"
38 #include "textP.h"
39 #include "nedit.h"
40 #include "menu.h"
41 #include "file.h"
42 #include "search.h"
43 #include "undo.h"
44 #include "preferences.h"
45 #include "selection.h"
46 #include "server.h"
47 #include "shell.h"
48 #include "macro.h"
49 #include "highlight.h"
50 #include "smartIndent.h"
51 #include "userCmds.h"
52 #include "nedit.bm"
53 #include "n.bm"
54 #include "windowTitle.h"
55 #include "interpret.h"
56 #include "rangeset.h"
57 #include "../util/clearcase.h"
58 #include "../util/misc.h"
59 #include "../util/fileUtils.h"
60 #include "../util/utils.h"
61 #include "../util/fileUtils.h"
62 #include "../util/DialogF.h"
63 #include "../Xlt/BubbleButtonP.h"
64 #include "../Microline/XmL/Folder.h"
65 #include "../util/nedit_malloc.h"
66 
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <sys/stat.h>
71 #ifdef VMS
72 #include "../util/VMSparam.h"
73 #else
74 #ifndef __MVS__
75 #include <sys/param.h>
76 #endif
77 #include "../util/clearcase.h"
78 #endif /*VMS*/
79 #include <limits.h>
80 #include <math.h>
81 #include <ctype.h>
82 #include <time.h>
83 #ifdef __unix__
84 #include <sys/time.h>
85 #endif
86 
87 #include <X11/Intrinsic.h>
88 #include <X11/Shell.h>
89 #include <X11/Xatom.h>
90 #include <Xm/Xm.h>
91 #include <Xm/MainW.h>
92 #include <Xm/PanedW.h>
93 #include <Xm/PanedWP.h>
94 #include <Xm/RowColumnP.h>
95 #include <Xm/Separator.h>
96 #include <Xm/Text.h>
97 #include <Xm/ToggleB.h>
98 #include <Xm/PushB.h>
99 #include <Xm/Form.h>
100 #include <Xm/Frame.h>
101 #include <Xm/Label.h>
102 #include <Xm/SelectioB.h>
103 #include <Xm/List.h>
104 #include <Xm/Protocols.h>
105 #include <Xm/ScrolledW.h>
106 #include <Xm/ScrollBar.h>
107 #include <Xm/PrimitiveP.h>
108 #include <Xm/Frame.h>
109 #include <Xm/CascadeB.h>
110 #ifdef EDITRES
111 #include <X11/Xmu/Editres.h>
112 /* extern void _XEditResCheckMessages(); */
113 #endif /* EDITRES */
114 
115 #ifdef HAVE_DEBUG_H
116 #include "../debug.h"
117 #endif
118 
119 /* Initial minimum height of a pane.  Just a fallback in case setPaneMinHeight
120    (which may break in a future release) is not available */
121 #define PANE_MIN_HEIGHT 39
122 
123 /* Thickness of 3D border around statistics and/or incremental search areas
124    below the main menu bar */
125 #define STAT_SHADOW_THICKNESS 1
126 
127 /* bitmap data for the close-tab button */
128 #define close_width 11
129 #define close_height 11
130 static unsigned char close_bits[] = {
131    0x00, 0x00, 0x00, 0x00, 0x8c, 0x01, 0xdc, 0x01, 0xf8, 0x00, 0x70, 0x00,
132    0xf8, 0x00, 0xdc, 0x01, 0x8c, 0x01, 0x00, 0x00, 0x00, 0x00};
133 
134 /* bitmap data for the isearch-find button */
135 #define isrcFind_width 11
136 #define isrcFind_height 11
137 static unsigned char isrcFind_bits[] = {
138    0xe0, 0x01, 0x10, 0x02, 0xc8, 0x04, 0x08, 0x04, 0x08, 0x04, 0x00, 0x04,
139    0x18, 0x02, 0xdc, 0x01, 0x0e, 0x00, 0x07, 0x00, 0x03, 0x00};
140 
141 /* bitmap data for the isearch-clear button */
142 #define isrcClear_width 11
143 #define isrcClear_height 11
144 static unsigned char isrcClear_bits[] = {
145    0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x84, 0x01, 0xc4, 0x00, 0x64, 0x00,
146    0xc4, 0x00, 0x84, 0x01, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00};
147 
148 extern void _XmDismissTearOff(Widget, XtPointer, XtPointer);
149 
150 static void hideTooltip(Widget tab);
151 static Pixmap createBitmapWithDepth(Widget w, char *data, unsigned int width,
152 	unsigned int height);
153 static WindowInfo *getNextTabWindow(WindowInfo *window, int direction,
154         int crossWin, int wrap);
155 static Widget addTab(Widget folder, const char *string);
156 static int compareWindowNames(const void *windowA, const void *windowB);
157 static int getTabPosition(Widget tab);
158 static Widget manageToolBars(Widget toolBarsForm);
159 static void hideTearOffs(Widget menuPane);
160 static void CloseDocumentWindow(Widget w, WindowInfo *window, XtPointer callData);
161 static void closeTabCB(Widget w, Widget mainWin, caddr_t callData);
162 static void raiseTabCB(Widget w, XtPointer clientData, XtPointer callData);
163 static Widget createTextArea(Widget parent, WindowInfo *window, int rows,
164         int cols, int emTabDist, char *delimiters, int wrapMargin,
165         int lineNumCols);
166 static void showStats(WindowInfo *window, int state);
167 static void showISearch(WindowInfo *window, int state);
168 static void showStatsForm(WindowInfo *window);
169 static void addToWindowList(WindowInfo *window);
170 static void removeFromWindowList(WindowInfo *window);
171 static void focusCB(Widget w, WindowInfo *window, XtPointer callData);
172 static void modifiedCB(int pos, int nInserted, int nDeleted, int nRestyled,
173         const char *deletedText, void *cbArg);
174 static void movedCB(Widget w, WindowInfo *window, XtPointer callData);
175 static void dragStartCB(Widget w, WindowInfo *window, XtPointer callData);
176 static void dragEndCB(Widget w, WindowInfo *window, dragEndCBStruct *callData);
177 static void closeCB(Widget w, WindowInfo *window, XtPointer callData);
178 static void saveYourselfCB(Widget w, Widget appShell, XtPointer callData);
179 static void setPaneDesiredHeight(Widget w, int height);
180 static void setPaneMinHeight(Widget w, int min);
181 static void addWindowIcon(Widget shell);
182 static void wmSizeUpdateProc(XtPointer clientData, XtIntervalId *id);
183 static void getGeometryString(WindowInfo *window, char *geomString);
184 #ifdef ROWCOLPATCH
185 static void patchRowCol(Widget w);
186 static void patchedRemoveChild(Widget child);
187 #endif
188 static void refreshMenuBar(WindowInfo *window);
189 static void cloneDocument(WindowInfo *window, WindowInfo *orgWin);
190 static void cloneTextPanes(WindowInfo *window, WindowInfo *orgWin);
191 static UndoInfo *cloneUndoItems(UndoInfo *orgList);
192 static Widget containingPane(Widget w);
193 
194 static WindowInfo *inFocusDocument = NULL;  	/* where we are now */
195 static WindowInfo *lastFocusDocument = NULL;	    	/* where we came from */
196 static int DoneWithMoveDocumentDialog;
197 static int updateLineNumDisp(WindowInfo* window);
198 static int updateGutterWidth(WindowInfo* window);
199 static void deleteDocument(WindowInfo *window);
200 static void cancelTimeOut(XtIntervalId *timer);
201 
202 /* From Xt, Shell.c, "BIGSIZE" */
203 static const Dimension XT_IGNORE_PPOSITION = 32767;
204 
205 /*
206 ** Create a new editor window
207 */
CreateWindow(const char * name,char * geometry,int iconic)208 WindowInfo *CreateWindow(const char *name, char *geometry, int iconic)
209 {
210     Widget winShell, mainWin, menuBar, pane, text, stats, statsAreaForm;
211     Widget closeTabBtn, tabForm, form;
212     WindowInfo *window;
213     Pixel bgpix, fgpix;
214     Arg al[20];
215     int ac;
216     XmString s1;
217     XmFontList statsFontList;
218     WindowInfo *win;
219     char newGeometry[MAX_GEOM_STRING_LEN];
220     unsigned int rows, cols;
221     int x = 0, y = 0, bitmask, showTabBar, state;
222 
223     static Pixmap isrcFind = 0;
224     static Pixmap isrcClear = 0;
225     static Pixmap closeTabPixmap = 0;
226 
227     /* Allocate some memory for the new window data structure */
228     window = (WindowInfo *)NEditMalloc(sizeof(WindowInfo));
229 
230     /* initialize window structure */
231     /* + Schwarzenberg: should a
232       memset(window, 0, sizeof(WindowInfo));
233          be added here ?
234     */
235     window->replaceDlog = NULL;
236     window->replaceText = NULL;
237     window->replaceWithText = NULL;
238     window->replaceWordToggle = NULL;
239     window->replaceCaseToggle = NULL;
240     window->replaceRegexToggle = NULL;
241     window->findDlog = NULL;
242     window->findText = NULL;
243     window->findWordToggle = NULL;
244     window->findCaseToggle = NULL;
245     window->findRegexToggle = NULL;
246     window->replaceMultiFileDlog = NULL;
247     window->replaceMultiFilePathBtn = NULL;
248     window->replaceMultiFileList = NULL;
249     window->multiFileReplSelected = FALSE;
250     window->multiFileBusy = FALSE;
251     window->writableWindows = NULL;
252     window->nWritableWindows = 0;
253     window->fileChanged = FALSE;
254     window->fileMode = 0;
255     window->fileUid = 0;
256     window->fileGid = 0;
257     window->filenameSet = FALSE;
258     window->fileFormat = UNIX_FILE_FORMAT;
259     window->lastModTime = 0;
260     window->fileMissing = True;
261     strcpy(window->filename, name);
262     window->undo = NULL;
263     window->redo = NULL;
264     window->nPanes = 0;
265     window->autoSaveCharCount = 0;
266     window->autoSaveOpCount = 0;
267     window->undoOpCount = 0;
268     window->undoMemUsed = 0;
269     CLEAR_ALL_LOCKS(window->lockReasons);
270     window->indentStyle = GetPrefAutoIndent(PLAIN_LANGUAGE_MODE);
271     window->autoSave = GetPrefAutoSave();
272     window->saveOldVersion = GetPrefSaveOldVersion();
273     window->wrapMode = GetPrefWrap(PLAIN_LANGUAGE_MODE);
274     window->overstrike = False;
275     window->showMatchingStyle = GetPrefShowMatching();
276     window->matchSyntaxBased = GetPrefMatchSyntaxBased();
277     window->showStats = GetPrefStatsLine();
278     window->showISearchLine = GetPrefISearchLine();
279     window->showLineNumbers = GetPrefLineNums();
280     window->highlightSyntax = GetPrefHighlightSyntax();
281     window->backlightCharTypes = NULL;
282     window->backlightChars = GetPrefBacklightChars();
283     if (window->backlightChars) {
284         char *cTypes = GetPrefBacklightCharTypes();
285         if (cTypes && window->backlightChars) {
286             if ((window->backlightCharTypes = (char*)NEditMalloc(strlen(cTypes) + 1)))
287                 strcpy(window->backlightCharTypes, cTypes);
288         }
289     }
290     window->modeMessageDisplayed = FALSE;
291     window->modeMessage = NULL;
292     window->ignoreModify = FALSE;
293     window->windowMenuValid = FALSE;
294     window->flashTimeoutID = 0;
295     window->fileClosedAtom = None;
296     window->wasSelected = FALSE;
297 
298     strcpy(window->fontName, GetPrefFontName());
299     strcpy(window->italicFontName, GetPrefItalicFontName());
300     strcpy(window->boldFontName, GetPrefBoldFontName());
301     strcpy(window->boldItalicFontName, GetPrefBoldItalicFontName());
302     window->colorDialog = NULL;
303     window->fontList = GetPrefFontList();
304     window->italicFontStruct = GetPrefItalicFont();
305     window->boldFontStruct = GetPrefBoldFont();
306     window->boldItalicFontStruct = GetPrefBoldItalicFont();
307     window->fontDialog = NULL;
308     window->nMarks = 0;
309     window->markTimeoutID = 0;
310     window->highlightData = NULL;
311     window->shellCmdData = NULL;
312     window->macroCmdData = NULL;
313     window->smartIndentData = NULL;
314     window->languageMode = PLAIN_LANGUAGE_MODE;
315     window->iSearchHistIndex = 0;
316     window->iSearchStartPos = -1;
317     window->replaceLastRegexCase   = TRUE;
318     window->replaceLastLiteralCase = FALSE;
319     window->iSearchLastRegexCase   = TRUE;
320     window->iSearchLastLiteralCase = FALSE;
321     window->findLastRegexCase      = TRUE;
322     window->findLastLiteralCase    = FALSE;
323     window->tab = NULL;
324     window->device = 0;
325     window->inode = 0;
326 
327     /* If window geometry was specified, split it apart into a window position
328        component and a window size component.  Create a new geometry string
329        containing the position component only.  Rows and cols are stripped off
330        because we can't easily calculate the size in pixels from them until the
331        whole window is put together.  Note that the preference resource is only
332        for clueless users who decide to specify the standard X geometry
333        application resource, which is pretty useless because width and height
334        are the same as the rows and cols preferences, and specifying a window
335        location will force all the windows to pile on top of one another */
336     if (geometry == NULL || geometry[0] == '\0')
337         geometry = GetPrefGeometry();
338     if (geometry == NULL || geometry[0] == '\0') {
339         rows = GetPrefRows();
340         cols = GetPrefCols();
341         newGeometry[0] = '\0';
342     } else {
343         bitmask = XParseGeometry(geometry, &x, &y, &cols, &rows);
344         if (bitmask == 0)
345             fprintf(stderr, "Bad window geometry specified: %s\n", geometry);
346         else {
347             if (!(bitmask & WidthValue))
348                 cols = GetPrefCols();
349             if (!(bitmask & HeightValue))
350                 rows = GetPrefRows();
351         }
352         CreateGeometryString(newGeometry, x, y, 0, 0,
353                 bitmask & ~(WidthValue | HeightValue));
354     }
355 
356     /* Create a new toplevel shell to hold the window */
357     ac = 0;
358     XtSetArg(al[ac], XmNtitle, name); ac++;
359     XtSetArg(al[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
360 #ifdef SGI_CUSTOM
361     if (strncmp(name, "Untitled", 8) == 0) {
362         XtSetArg(al[ac], XmNiconName, APP_NAME); ac++;
363     } else {
364         XtSetArg(al[ac], XmNiconName, name); ac++;
365     }
366 #else
367     XtSetArg(al[ac], XmNiconName, name); ac++;
368 #endif
369     XtSetArg(al[ac], XmNgeometry, newGeometry[0]=='\0'?NULL:newGeometry); ac++;
370     XtSetArg(al[ac], XmNinitialState,
371             iconic ? IconicState : NormalState); ac++;
372 
373     if (newGeometry[0] == '\0')
374     {
375         /* Workaround to make Xt ignore Motif's bad PPosition size changes. Even
376            though we try to remove the PPosition in RealizeWithoutForcingPosition,
377            it is not sufficient.  Motif will recompute the size hints some point
378            later and put PPosition back! If the window is mapped after that time,
379            then the window will again wind up at 0, 0.  So, XEmacs does this, and
380            now we do.
381 
382            Alternate approach, relying on ShellP.h:
383 
384            ((WMShellWidget)winShell)->shell.client_specified &= ~_XtShellPPositionOK;
385          */
386 
387         XtSetArg(al[ac], XtNx, XT_IGNORE_PPOSITION); ac++;
388         XtSetArg(al[ac], XtNy, XT_IGNORE_PPOSITION); ac++;
389     }
390 
391     winShell = CreateWidget(TheAppShell, "textShell",
392                 topLevelShellWidgetClass, al, ac);
393     window->shell = winShell;
394 
395 #ifdef EDITRES
396     XtAddEventHandler (winShell, (EventMask)0, True,
397                 (XtEventHandler)_XEditResCheckMessages, NULL);
398 #endif /* EDITRES */
399 
400 #ifndef SGI_CUSTOM
401     addWindowIcon(winShell);
402 #endif
403 
404     /* Create a MainWindow to manage the menubar and text area, set the
405        userData resource to be used by WidgetToWindow to recover the
406        window pointer from the widget id of any of the window's widgets */
407     XtSetArg(al[ac], XmNuserData, window); ac++;
408     mainWin = XmCreateMainWindow(winShell, "main", al, ac);
409     window->mainWin = mainWin;
410     XtManageChild(mainWin);
411 
412     /* The statsAreaForm holds the stats line and the I-Search line. */
413     statsAreaForm = XtVaCreateWidget("statsAreaForm",
414             xmFormWidgetClass, mainWin,
415             XmNmarginWidth, STAT_SHADOW_THICKNESS,
416             XmNmarginHeight, STAT_SHADOW_THICKNESS,
417             /* XmNautoUnmanage, False, */
418             NULL);
419 
420     /* NOTE: due to a bug in openmotif 2.1.30, NEdit used to crash when
421        the i-search bar was active, and the i-search text widget was focussed,
422        and the window's width was resized to nearly zero.
423        In theory, it is possible to avoid this by imposing a minimum
424        width constraint on the nedit windows, but that width would have to
425        be at least 30 characters, which is probably unacceptable.
426        Amazingly, adding a top offset of 1 pixel to the toggle buttons of
427        the i-search bar, while keeping the the top offset of the text widget
428        to 0 seems to avoid avoid the crash. */
429 
430     window->iSearchForm = XtVaCreateWidget("iSearchForm",
431        	    xmFormWidgetClass, statsAreaForm,
432 	    XmNshadowThickness, 0,
433 	    XmNleftAttachment, XmATTACH_FORM,
434 	    XmNleftOffset, STAT_SHADOW_THICKNESS,
435 	    XmNtopAttachment, XmATTACH_FORM,
436 	    XmNtopOffset, STAT_SHADOW_THICKNESS,
437 	    XmNrightAttachment, XmATTACH_FORM,
438 	    XmNrightOffset, STAT_SHADOW_THICKNESS,
439 	    XmNbottomOffset, STAT_SHADOW_THICKNESS, NULL);
440     if(window->showISearchLine)
441         XtManageChild(window->iSearchForm);
442 
443     /* Disable keyboard traversal of the find, clear and toggle buttons.  We
444        were doing this previously by forcing the keyboard focus back to the
445        text widget whenever a toggle changed.  That causes an ugly focus flash
446        on screen.  It's better just not to go there in the first place.
447        Plus, if the user really wants traversal, it's an X resource so it
448        can be enabled without too much pain and suffering. */
449 
450     if (isrcFind == 0) {
451         isrcFind = createBitmapWithDepth(window->iSearchForm,
452                 (char *)isrcFind_bits, isrcFind_width, isrcFind_height);
453     }
454     window->iSearchFindButton = XtVaCreateManagedWidget("iSearchFindButton",
455             xmPushButtonWidgetClass, window->iSearchForm,
456             XmNlabelString, s1=XmStringCreateSimple("Find"),
457             XmNlabelType, XmPIXMAP,
458             XmNlabelPixmap, isrcFind,
459             XmNtraversalOn, False,
460             XmNmarginHeight, 1,
461             XmNmarginWidth, 1,
462             XmNleftAttachment, XmATTACH_FORM,
463             /* XmNleftOffset, 3, */
464             XmNleftOffset, 0,
465             XmNtopAttachment, XmATTACH_FORM,
466             XmNtopOffset, 1,
467             XmNbottomAttachment, XmATTACH_FORM,
468             XmNbottomOffset, 1,
469             NULL);
470     XmStringFree(s1);
471 
472     window->iSearchCaseToggle = XtVaCreateManagedWidget("iSearchCaseToggle",
473             xmToggleButtonWidgetClass, window->iSearchForm,
474             XmNlabelString, s1=XmStringCreateSimple("Case"),
475             XmNset, GetPrefSearch() == SEARCH_CASE_SENSE
476             || GetPrefSearch() == SEARCH_REGEX
477             || GetPrefSearch() == SEARCH_CASE_SENSE_WORD,
478             XmNtopAttachment, XmATTACH_FORM,
479             XmNbottomAttachment, XmATTACH_FORM,
480             XmNtopOffset, 1, /* see openmotif note above */
481             XmNrightAttachment, XmATTACH_FORM,
482             XmNmarginHeight, 0,
483             XmNtraversalOn, False,
484             NULL);
485     XmStringFree(s1);
486 
487     window->iSearchRegexToggle = XtVaCreateManagedWidget("iSearchREToggle",
488             xmToggleButtonWidgetClass, window->iSearchForm,
489             XmNlabelString, s1=XmStringCreateSimple("RegExp"),
490             XmNset, GetPrefSearch() == SEARCH_REGEX_NOCASE
491             || GetPrefSearch() == SEARCH_REGEX,
492             XmNtopAttachment, XmATTACH_FORM,
493             XmNbottomAttachment, XmATTACH_FORM,
494             XmNtopOffset, 1, /* see openmotif note above */
495             XmNrightAttachment, XmATTACH_WIDGET,
496             XmNrightWidget, window->iSearchCaseToggle,
497             XmNmarginHeight, 0,
498             XmNtraversalOn, False,
499             NULL);
500     XmStringFree(s1);
501 
502     window->iSearchRevToggle = XtVaCreateManagedWidget("iSearchRevToggle",
503             xmToggleButtonWidgetClass, window->iSearchForm,
504             XmNlabelString, s1=XmStringCreateSimple("Rev"),
505             XmNset, False,
506             XmNtopAttachment, XmATTACH_FORM,
507             XmNbottomAttachment, XmATTACH_FORM,
508             XmNtopOffset, 1, /* see openmotif note above */
509             XmNrightAttachment, XmATTACH_WIDGET,
510             XmNrightWidget, window->iSearchRegexToggle,
511             XmNmarginHeight, 0,
512             XmNtraversalOn, False,
513             NULL);
514     XmStringFree(s1);
515 
516     if (isrcClear == 0) {
517         isrcClear = createBitmapWithDepth(window->iSearchForm,
518                 (char *)isrcClear_bits, isrcClear_width, isrcClear_height);
519     }
520     window->iSearchClearButton = XtVaCreateManagedWidget("iSearchClearButton",
521             xmPushButtonWidgetClass, window->iSearchForm,
522             XmNlabelString, s1=XmStringCreateSimple("<x"),
523             XmNlabelType, XmPIXMAP,
524             XmNlabelPixmap, isrcClear,
525             XmNtraversalOn, False,
526             XmNmarginHeight, 1,
527             XmNmarginWidth, 1,
528             XmNrightAttachment, XmATTACH_WIDGET,
529             XmNrightWidget, window->iSearchRevToggle,
530             XmNrightOffset, 2,
531             XmNtopAttachment, XmATTACH_FORM,
532             XmNtopOffset, 1,
533             XmNbottomAttachment, XmATTACH_FORM,
534             XmNbottomOffset, 1,
535             NULL);
536     XmStringFree(s1);
537 
538     window->iSearchText = XtVaCreateManagedWidget("iSearchText",
539             xmTextWidgetClass, window->iSearchForm,
540             XmNmarginHeight, 1,
541             XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
542             XmNleftAttachment, XmATTACH_WIDGET,
543             XmNleftWidget, window->iSearchFindButton,
544             XmNrightAttachment, XmATTACH_WIDGET,
545             XmNrightWidget, window->iSearchClearButton,
546             /* XmNrightOffset, 5, */
547             XmNtopAttachment, XmATTACH_FORM,
548             XmNtopOffset, 0, /* see openmotif note above */
549             XmNbottomAttachment, XmATTACH_FORM,
550             XmNbottomOffset, 0, NULL);
551     RemapDeleteKey(window->iSearchText);
552 
553     SetISearchTextCallbacks(window);
554 
555     /* create the a form to house the tab bar and close-tab button */
556     tabForm = XtVaCreateWidget("tabForm",
557        	    xmFormWidgetClass, statsAreaForm,
558 	    XmNmarginHeight, 0,
559 	    XmNmarginWidth, 0,
560 	    XmNspacing, 0,
561     	    XmNresizable, False,
562             XmNleftAttachment, XmATTACH_FORM,
563             XmNrightAttachment, XmATTACH_FORM,
564 	    XmNshadowThickness, 0, NULL);
565 
566     /* button to close top document */
567     if (closeTabPixmap == 0) {
568         closeTabPixmap = createBitmapWithDepth(tabForm,
569 	        (char *)close_bits, close_width, close_height);
570     }
571     closeTabBtn = XtVaCreateManagedWidget("closeTabBtn",
572       	    xmPushButtonWidgetClass, tabForm,
573 	    XmNmarginHeight, 0,
574 	    XmNmarginWidth, 0,
575     	    XmNhighlightThickness, 0,
576 	    XmNlabelType, XmPIXMAP,
577 	    XmNlabelPixmap, closeTabPixmap,
578     	    XmNshadowThickness, 1,
579             XmNtraversalOn, False,
580             XmNrightAttachment, XmATTACH_FORM,
581             XmNrightOffset, 3,
582             XmNbottomAttachment, XmATTACH_FORM,
583             XmNbottomOffset, 3,
584 	    NULL);
585     XtAddCallback(closeTabBtn, XmNactivateCallback, (XtCallbackProc)closeTabCB,
586 	    mainWin);
587 
588     /* create the tab bar */
589     window->tabBar = XtVaCreateManagedWidget("tabBar",
590        	    xmlFolderWidgetClass, tabForm,
591 	    XmNresizePolicy, XmRESIZE_PACK,
592 	    XmNleftAttachment, XmATTACH_FORM,
593             XmNleftOffset, 0,
594 	    XmNrightAttachment, XmATTACH_WIDGET,
595 	    XmNrightWidget, closeTabBtn,
596             XmNrightOffset, 5,
597             XmNbottomAttachment, XmATTACH_FORM,
598             XmNbottomOffset, 0,
599             XmNtopAttachment, XmATTACH_FORM,
600 	    NULL);
601 
602     window->tabMenuPane = CreateTabContextMenu(window->tabBar, window);
603     AddTabContextMenuAction(window->tabBar);
604 
605     /* create an unmanaged composite widget to get the folder
606        widget to hide the 3D shadow for the manager area.
607        Note: this works only on the patched XmLFolder widget */
608     form = XtVaCreateWidget("form",
609 	    xmFormWidgetClass, window->tabBar,
610 	    XmNheight, 1,
611 	    XmNresizable, False,
612 	    NULL);
613 
614     XtAddCallback(window->tabBar, XmNactivateCallback,
615     	    raiseTabCB, NULL);
616 
617     window->tab = addTab(window->tabBar, name);
618 
619     /* A form to hold the stats line text and line/col widgets */
620     window->statsLineForm = XtVaCreateWidget("statsLineForm",
621             xmFormWidgetClass, statsAreaForm,
622             XmNshadowThickness, 0,
623             XmNtopAttachment, window->showISearchLine ?
624                 XmATTACH_WIDGET : XmATTACH_FORM,
625             XmNtopWidget, window->iSearchForm,
626             XmNrightAttachment, XmATTACH_FORM,
627             XmNleftAttachment, XmATTACH_FORM,
628             XmNbottomAttachment, XmATTACH_FORM,
629             XmNresizable, False,    /*  */
630             NULL);
631 
632     /* A separate display of the line/column number */
633     window->statsLineColNo = XtVaCreateManagedWidget("statsLineColNo",
634             xmLabelWidgetClass, window->statsLineForm,
635             XmNlabelString, s1=XmStringCreateSimple("L: ---  C: ---"),
636             XmNshadowThickness, 0,
637             XmNmarginHeight, 2,
638             XmNtraversalOn, False,
639             XmNtopAttachment, XmATTACH_FORM,
640             XmNrightAttachment, XmATTACH_FORM,
641             XmNbottomAttachment, XmATTACH_FORM, /*  */
642             NULL);
643     XmStringFree(s1);
644 
645     /* Create file statistics display area.  Using a text widget rather than
646        a label solves a layout problem with the main window, which messes up
647        if the label is too long (we would need a resize callback to control
648        the length when the window changed size), and allows users to select
649        file names and line numbers.  Colors are copied from parent
650        widget, because many users and some system defaults color text
651        backgrounds differently from other widgets. */
652     XtVaGetValues(window->statsLineForm, XmNbackground, &bgpix, NULL);
653     XtVaGetValues(window->statsLineForm, XmNforeground, &fgpix, NULL);
654     stats = XtVaCreateManagedWidget("statsLine",
655             xmTextWidgetClass,  window->statsLineForm,
656             XmNbackground, bgpix,
657             XmNforeground, fgpix,
658             XmNshadowThickness, 0,
659             XmNhighlightColor, bgpix,
660             XmNhighlightThickness, 0,  /* must be zero, for OM (2.1.30) to
661 	                                  aligns tatsLineColNo & statsLine */
662             XmNmarginHeight, 1,        /* == statsLineColNo.marginHeight - 1,
663 	                                  to align with statsLineColNo */
664             XmNscrollHorizontal, False,
665             XmNeditMode, XmSINGLE_LINE_EDIT,
666             XmNeditable, False,
667             XmNtraversalOn, False,
668             XmNcursorPositionVisible, False,
669             XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, /*  */
670             XmNtopWidget, window->statsLineColNo,
671             XmNleftAttachment, XmATTACH_FORM,
672             XmNrightAttachment, XmATTACH_WIDGET,
673             XmNrightWidget, window->statsLineColNo,
674             XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, /*  */
675             XmNbottomWidget, window->statsLineColNo,
676             XmNrightOffset, 3,
677             NULL);
678     window->statsLine = stats;
679 
680     /* Give the statsLine the same font as the statsLineColNo */
681     XtVaGetValues(window->statsLineColNo, XmNfontList, &statsFontList, NULL);
682     XtVaSetValues(window->statsLine, XmNfontList, statsFontList, NULL);
683 
684     /* Manage the statsLineForm */
685     if(window->showStats)
686         XtManageChild(window->statsLineForm);
687 
688     /* If the fontList was NULL, use the magical default provided by Motif,
689        since it must have worked if we've gotten this far */
690     if (window->fontList == NULL)
691         XtVaGetValues(stats, XmNfontList, &window->fontList, NULL);
692 
693     /* Create the menu bar */
694     menuBar = CreateMenuBar(mainWin, window);
695     window->menuBar = menuBar;
696     XtManageChild(menuBar);
697 
698     /* Create paned window to manage split pane behavior */
699     pane = XtVaCreateManagedWidget("pane", xmPanedWindowWidgetClass,  mainWin,
700             XmNseparatorOn, False,
701             XmNspacing, 3, XmNsashIndent, -2, NULL);
702     window->splitPane = pane;
703     XmMainWindowSetAreas(mainWin, menuBar, statsAreaForm, NULL, NULL, pane);
704 
705     /* Store a copy of document/window pointer in text pane to support
706        action procedures. See also WidgetToWindow() for info. */
707     XtVaSetValues(pane, XmNuserData, window, NULL);
708 
709     /* Patch around Motif's most idiotic "feature", that its menu accelerators
710        recognize Caps Lock and Num Lock as modifiers, and don't trigger if
711        they are engaged */
712     AccelLockBugPatch(pane, window->menuBar);
713 
714     /* Create the first, and most permanent text area (other panes may
715        be added & removed, but this one will never be removed */
716     text = createTextArea(pane, window, rows,cols,
717             GetPrefEmTabDist(PLAIN_LANGUAGE_MODE), GetPrefDelimiters(),
718             GetPrefWrapMargin(), window->showLineNumbers?MIN_LINE_NUM_COLS:0);
719     XtManageChild(text);
720     window->textArea = text;
721     window->lastFocus = text;
722 
723     /* Set the initial colors from the globals. */
724     SetColors(window,
725               GetPrefColorName(TEXT_FG_COLOR  ),
726               GetPrefColorName(TEXT_BG_COLOR  ),
727               GetPrefColorName(SELECT_FG_COLOR),
728               GetPrefColorName(SELECT_BG_COLOR),
729               GetPrefColorName(HILITE_FG_COLOR),
730               GetPrefColorName(HILITE_BG_COLOR),
731               GetPrefColorName(LINENO_FG_COLOR),
732               GetPrefColorName(CURSOR_FG_COLOR));
733 
734     /* Create the right button popup menu (note: order is important here,
735        since the translation for popping up this menu was probably already
736        added in createTextArea, but CreateBGMenu requires window->textArea
737        to be set so it can attach the menu to it (because menu shells are
738        finicky about the kinds of widgets they are attached to)) */
739     window->bgMenuPane = CreateBGMenu(window);
740 
741     /* cache user menus: init. user background menu cache */
742     InitUserBGMenuCache(&window->userBGMenuCache);
743 
744     /* Create the text buffer rather than using the one created automatically
745        with the text area widget.  This is done so the syntax highlighting
746        modify callback can be called to synchronize the style buffer BEFORE
747        the text display's callback is called upon to display a modification */
748     window->buffer = BufCreate();
749     BufAddModifyCB(window->buffer, SyntaxHighlightModifyCB, window);
750 
751     /* Attach the buffer to the text widget, and add callbacks for modify */
752     TextSetBuffer(text, window->buffer);
753     BufAddModifyCB(window->buffer, modifiedCB, window);
754 
755     /* Designate the permanent text area as the owner for selections */
756     HandleXSelections(text);
757 
758     /* Set the requested hardware tab distance and useTabs in the text buffer */
759     BufSetTabDistance(window->buffer, GetPrefTabDist(PLAIN_LANGUAGE_MODE));
760     window->buffer->useTabs = GetPrefInsertTabs();
761 
762     /* add the window to the global window list, update the Windows menus */
763     addToWindowList(window);
764     InvalidateWindowMenus();
765 
766     showTabBar = GetShowTabBar(window);
767     if (showTabBar)
768     	XtManageChild(tabForm);
769 
770     manageToolBars(statsAreaForm);
771 
772     if (showTabBar || window->showISearchLine ||
773     	    window->showStats)
774         XtManageChild(statsAreaForm);
775 
776     /* realize all of the widgets in the new window */
777     RealizeWithoutForcingPosition(winShell);
778     XmProcessTraversal(text, XmTRAVERSE_CURRENT);
779 
780     /* Make close command in window menu gracefully prompt for close */
781     AddMotifCloseCallback(winShell, (XtCallbackProc)closeCB, window);
782 
783     /* Make window resizing work in nice character heights */
784     UpdateWMSizeHints(window);
785 
786     /* Set the minimum pane height for the initial text pane */
787     UpdateMinPaneHeights(window);
788 
789     /* create dialogs shared by all documents in a window */
790     CreateFindDlog(window->shell, window);
791     CreateReplaceDlog(window->shell, window);
792     CreateReplaceMultiFileDlog(window);
793 
794     /* dim/undim Attach_Tab menu items */
795     state = NDocuments(window) < NWindows();
796     for(win=WindowList; win; win=win->next) {
797     	if (IsTopDocument(win)) {
798     	    XtSetSensitive(win->moveDocumentItem, state);
799     	    XtSetSensitive(win->contextMoveDocumentItem, state);
800 	}
801     }
802 
803     return window;
804 }
805 
806 /*
807 ** ButtonPress event handler for tabs.
808 */
tabClickEH(Widget w,XtPointer clientData,XEvent * event)809 static void tabClickEH(Widget w, XtPointer clientData, XEvent *event)
810 {
811     /* hide the tooltip when user clicks with any button. */
812     if (BubbleButton_Timer(w)) {
813     	XtRemoveTimeOut(BubbleButton_Timer(w));
814     	BubbleButton_Timer(w) = (XtIntervalId)NULL;
815     }
816     else {
817         hideTooltip(w);
818     }
819 }
820 
821 /*
822 ** add a tab to the tab bar for the new document.
823 */
addTab(Widget folder,const char * string)824 static Widget addTab(Widget folder, const char *string)
825 {
826     Widget tooltipLabel, tab;
827     XmString s1;
828 
829     s1 = XmStringCreateSimple((char *)string);
830     tab = XtVaCreateManagedWidget("tab",
831 	    xrwsBubbleButtonWidgetClass, folder,
832 	    /* XmNmarginWidth, <default@nedit.c>, */
833 	    /* XmNmarginHeight, <default@nedit.c>, */
834   	    /* XmNalignment, <default@nedit.c>, */
835   	    XmNlabelString, s1,
836   	    XltNbubbleString, s1,
837 	    XltNshowBubble, GetPrefToolTips(),
838 	    XltNautoParkBubble, True,
839 	    XltNslidingBubble, False,
840 	    /* XltNdelay, 800,*/
841 	    /* XltNbubbleDuration, 8000,*/
842 	    NULL);
843     XmStringFree(s1);
844 
845     /* there's things to do as user click on the tab */
846     XtAddEventHandler(tab, ButtonPressMask, False,
847             (XtEventHandler)tabClickEH, (XtPointer)0);
848 
849     /* BubbleButton simply use reversed video for tooltips,
850        we try to use the 'standard' color */
851     tooltipLabel = XtNameToWidget(tab, "*BubbleLabel");
852     XtVaSetValues(tooltipLabel,
853     	    XmNbackground, AllocateColor(tab, GetPrefTooltipBgColor()),
854     	    XmNforeground, AllocateColor(tab, NEDIT_DEFAULT_FG),
855 	    NULL);
856 
857     /* put borders around tooltip. BubbleButton use
858        transientShellWidgetClass as tooltip shell, which
859        came without borders */
860     XtVaSetValues(XtParent(tooltipLabel), XmNborderWidth, 1, NULL);
861 
862 #ifdef LESSTIF_VERSION
863     /* If we don't do this, no popup when right-click on tabs */
864     AddTabContextMenuAction(tab);
865 #endif /* LESSTIF_VERSION */
866 
867     return tab;
868 }
869 
870 /*
871 ** Comparison function for sorting windows by title.
872 ** Windows are sorted by alphabetically by filename and then
873 ** alphabetically by path.
874 */
compareWindowNames(const void * windowA,const void * windowB)875 static int compareWindowNames(const void *windowA, const void *windowB)
876 {
877     int rc;
878     const WindowInfo *a = *((WindowInfo**)windowA);
879     const WindowInfo *b = *((WindowInfo**)windowB);
880 
881     rc = strcmp(a->filename, b->filename);
882     if (rc != 0)
883 	 return rc;
884     rc = strcmp(a->path, b->path);
885     return rc;
886 }
887 
888 /*
889 ** Sort tabs in the tab bar alphabetically, if demanded so.
890 */
SortTabBar(WindowInfo * window)891 void SortTabBar(WindowInfo *window)
892 {
893     WindowInfo *w;
894     WindowInfo **windows;
895     WidgetList tabList;
896     int i, j, nDoc, tabCount;
897 
898     if (!GetPrefSortTabs())
899     	return;
900 
901     /* need more than one tab to sort */
902     nDoc = NDocuments(window);
903     if (nDoc < 2)
904         return;
905 
906     /* first sort the documents */
907     windows = (WindowInfo **)NEditMalloc(sizeof(WindowInfo *) * nDoc);
908     for (w=WindowList, i=0; w!=NULL; w=w->next) {
909     	if (window->shell == w->shell)
910     	    windows[i++] = w;
911     }
912     qsort(windows, nDoc, sizeof(WindowInfo *), compareWindowNames);
913 
914     /* assign tabs to documents in sorted order */
915     XtVaGetValues(window->tabBar, XmNtabWidgetList, &tabList,
916                   XmNtabCount, &tabCount, NULL);
917 
918     for (i=0, j=0; i<tabCount && j<nDoc; i++) {
919         if (tabList[i]->core.being_destroyed)
920             continue;
921 
922         /* set tab as active */
923         if (IsTopDocument(windows[j]))
924             XmLFolderSetActiveTab(window->tabBar, i, False);
925 
926         windows[j]->tab = tabList[i];
927         RefreshTabState(windows[j]);
928         j++;
929     }
930 
931     NEditFree(windows);
932 }
933 
934 /*
935 ** find which document a tab belongs to
936 */
TabToWindow(Widget tab)937 WindowInfo *TabToWindow(Widget tab)
938 {
939     WindowInfo *win;
940     for (win=WindowList; win; win=win->next) {
941     	if (win->tab == tab)
942 	    return win;
943     }
944 
945     return NULL;
946 }
947 
948 /*
949 ** Close a document, or an editor window
950 */
CloseWindow(WindowInfo * window)951 void CloseWindow(WindowInfo *window)
952 {
953     int keepWindow, state;
954     char name[MAXPATHLEN];
955     WindowInfo *win, *topBuf = NULL, *nextBuf = NULL;
956 
957     /* Free smart indent macro programs */
958     EndSmartIndent(window);
959 
960     /* Clean up macro references to the doomed window.  If a macro is
961        executing, stop it.  If macro is calling this (closing its own
962        window), leave the window alive until the macro completes */
963     keepWindow = !MacroWindowCloseActions(window);
964 
965 #ifndef VMS
966     /* Kill shell sub-process and free related memory */
967     AbortShellCommand(window);
968 #endif /*VMS*/
969 
970     /* Unload the default tips files for this language mode if necessary */
971     UnloadLanguageModeTipsFile(window);
972 
973     /* If a window is closed while it is on the multi-file replace dialog
974        list of any other window (or even the same one), we must update those
975        lists or we end up with dangling references. Normally, there can
976        be only one of those dialogs at the same time (application modal),
977        but LessTif doesn't even (always) honor application modalness, so
978        there can be more than one dialog. */
979     RemoveFromMultiReplaceDialog(window);
980 
981     /* Destroy the file closed property for this file */
982     DeleteFileClosedProperty(window);
983 
984     /* Remove any possibly pending callback which might fire after the
985        widget is gone. */
986     cancelTimeOut(&window->flashTimeoutID);
987     cancelTimeOut(&window->markTimeoutID);
988 
989     /* if this is the last window, or must be kept alive temporarily because
990        it's running the macro calling us, don't close it, make it Untitled */
991     if (keepWindow || (WindowList == window && window->next == NULL)) {
992         window->filename[0] = '\0';
993         UniqueUntitledName(name);
994         CLEAR_ALL_LOCKS(window->lockReasons);
995         window->fileMode = 0;
996         window->fileUid = 0;
997         window->fileGid = 0;
998         strcpy(window->filename, name);
999         strcpy(window->path, "");
1000         window->ignoreModify = TRUE;
1001         BufSetAll(window->buffer, "");
1002         window->ignoreModify = FALSE;
1003         window->nMarks = 0;
1004         window->filenameSet = FALSE;
1005         window->fileMissing = TRUE;
1006         window->fileChanged = FALSE;
1007         window->fileFormat = UNIX_FILE_FORMAT;
1008         window->lastModTime = 0;
1009         window->device = 0;
1010         window->inode = 0;
1011 
1012         StopHighlighting(window);
1013         EndSmartIndent(window);
1014         UpdateWindowTitle(window);
1015         UpdateWindowReadOnly(window);
1016         XtSetSensitive(window->closeItem, FALSE);
1017         XtSetSensitive(window->readOnlyItem, TRUE);
1018         XmToggleButtonSetState(window->readOnlyItem, FALSE, FALSE);
1019         ClearUndoList(window);
1020         ClearRedoList(window);
1021         XmTextSetString(window->statsLine, ""); /* resets scroll pos of stats
1022                                                    line from long file names */
1023         UpdateStatsLine(window);
1024         DetermineLanguageMode(window, True);
1025 	RefreshTabState(window);
1026         updateLineNumDisp(window);
1027         return;
1028     }
1029 
1030     /* Free syntax highlighting patterns, if any. w/o redisplaying */
1031     FreeHighlightingData(window);
1032 
1033     /* remove the buffer modification callbacks so the buffer will be
1034        deallocated when the last text widget is destroyed */
1035     BufRemoveModifyCB(window->buffer, modifiedCB, window);
1036     BufRemoveModifyCB(window->buffer, SyntaxHighlightModifyCB, window);
1037 
1038 #ifdef ROWCOLPATCH
1039     patchRowCol(window->menuBar);
1040 #endif
1041 
1042     /* free the undo and redo lists */
1043     ClearUndoList(window);
1044     ClearRedoList(window);
1045 
1046     /* close the document/window */
1047     if (NDocuments(window) > 1) {
1048         if (MacroRunWindow() && MacroRunWindow() != window
1049                 && MacroRunWindow()->shell == window->shell) {
1050             nextBuf = MacroRunWindow();
1051             RaiseDocument(nextBuf);
1052         } else if (IsTopDocument(window)) {
1053             /* need to find a successor before closing a top document */
1054             nextBuf = getNextTabWindow(window, 1, 0, 0);
1055             RaiseDocument(nextBuf);
1056         } else {
1057             topBuf = GetTopDocument(window->shell);
1058         }
1059     }
1060 
1061     /* remove the window from the global window list, update window menus */
1062     removeFromWindowList(window);
1063     InvalidateWindowMenus();
1064     CheckCloseDim(); /* Close of window running a macro may have been disabled. */
1065 
1066     /* remove the tab of the closing document from tab bar */
1067     XtDestroyWidget(window->tab);
1068 
1069     /* refresh tab bar after closing a document */
1070     if (nextBuf) {
1071 	ShowWindowTabBar(nextBuf);
1072         updateLineNumDisp(nextBuf);
1073     } else if (topBuf) {
1074 	ShowWindowTabBar(topBuf);
1075         updateLineNumDisp(topBuf);
1076     }
1077 
1078     /* dim/undim Detach_Tab menu items */
1079     win = nextBuf? nextBuf : topBuf;
1080     if (win) {
1081 	state = NDocuments(win) > 1;
1082     	XtSetSensitive(win->detachDocumentItem, state);
1083     	XtSetSensitive(win->contextDetachDocumentItem, state);
1084     }
1085 
1086     /* dim/undim Attach_Tab menu items */
1087     state = NDocuments(WindowList) < NWindows();
1088     for(win=WindowList; win; win=win->next) {
1089     	if (IsTopDocument(win)) {
1090     	    XtSetSensitive(win->moveDocumentItem, state);
1091     	    XtSetSensitive(win->contextMoveDocumentItem, state);
1092 	}
1093     }
1094 
1095     /* free background menu cache for document */
1096     FreeUserBGMenuCache(&window->userBGMenuCache);
1097 
1098     /* destroy the document's pane, or the window */
1099     if (nextBuf || topBuf) {
1100         deleteDocument(window);
1101     } else
1102     {
1103         /* free user menu cache for window */
1104         FreeUserMenuCache(window->userMenuCache);
1105 
1106 	/* remove and deallocate all of the widgets associated with window */
1107     	NEditFree(window->backlightCharTypes); /* we made a copy earlier on */
1108 	CloseAllPopupsFor(window->shell);
1109     	XtDestroyWidget(window->shell);
1110     }
1111 
1112     /* deallocate the window data structure */
1113     NEditFree(window);
1114 }
1115 
1116 /*
1117 ** check if tab bar is to be shown on this window
1118 */
GetShowTabBar(WindowInfo * window)1119 int GetShowTabBar(WindowInfo *window)
1120 {
1121     if (!GetPrefTabBar())
1122      	return False;
1123     else if (NDocuments(window) == 1)
1124     	return !GetPrefTabBarHideOne();
1125     else
1126     	return True;
1127 }
1128 
ShowWindowTabBar(WindowInfo * window)1129 void ShowWindowTabBar(WindowInfo *window)
1130 {
1131     if (GetPrefTabBar()) {
1132 	if (GetPrefTabBarHideOne())
1133 	    ShowTabBar(window, NDocuments(window)>1);
1134 	else
1135 	    ShowTabBar(window, True);
1136     }
1137     else
1138 	ShowTabBar(window, False);
1139 }
1140 
1141 /*
1142 ** Check if there is already a window open for a given file
1143 */
FindWindowWithFile(const char * name,const char * path)1144 WindowInfo *FindWindowWithFile(const char *name, const char *path)
1145 {
1146     WindowInfo* window;
1147 
1148 /* I don't think this algorithm will work on vms so I am
1149    disabling it for now */
1150 #ifndef VMS
1151     if (!GetPrefHonorSymlinks())
1152     {
1153         char fullname[MAXPATHLEN + 1];
1154         struct stat attribute;
1155 
1156         strncpy(fullname, path, MAXPATHLEN);
1157         strncat(fullname, name, MAXPATHLEN);
1158         fullname[MAXPATHLEN] = '\0';
1159 
1160         if (0 == stat(fullname, &attribute)) {
1161             for (window = WindowList; window != NULL; window = window->next) {
1162                 if (attribute.st_dev == window->device
1163                         && attribute.st_ino == window->inode) {
1164                     return window;
1165                 }
1166             }
1167         }   /*  else:  Not an error condition, just a new file. Continue to check
1168                 whether the filename is already in use for an unsaved document.  */
1169     }
1170 #endif
1171 
1172     for (window = WindowList; window != NULL; window = window->next) {
1173         if (!strcmp(window->filename, name) && !strcmp(window->path, path)) {
1174             return window;
1175         }
1176     }
1177     return NULL;
1178 }
1179 
1180 /*
1181 ** Add another independently scrollable pane to the current document,
1182 ** splitting the pane which currently has keyboard focus.
1183 */
SplitPane(WindowInfo * window)1184 void SplitPane(WindowInfo *window)
1185 {
1186     short paneHeights[MAX_PANES+1];
1187     int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1];
1188     int horizOffsets[MAX_PANES+1];
1189     int i, focusPane, emTabDist, wrapMargin, lineNumCols, totalHeight=0;
1190     char *delimiters;
1191     Widget text = NULL;
1192     textDisp *textD, *newTextD;
1193 
1194     /* Don't create new panes if we're already at the limit */
1195     if (window->nPanes >= MAX_PANES)
1196         return;
1197 
1198     /* Record the current heights, scroll positions, and insert positions
1199        of the existing panes, keyboard focus */
1200     focusPane = 0;
1201     for (i=0; i<=window->nPanes; i++) {
1202         text = i==0 ? window->textArea : window->textPanes[i-1];
1203         insertPositions[i] = TextGetCursorPos(text);
1204         XtVaGetValues(containingPane(text),XmNheight,&paneHeights[i],NULL);
1205         totalHeight += paneHeights[i];
1206         TextGetScroll(text, &topLines[i], &horizOffsets[i]);
1207         if (text == window->lastFocus)
1208             focusPane = i;
1209     }
1210 
1211     /* Unmanage & remanage the panedWindow so it recalculates pane heights */
1212     XtUnmanageChild(window->splitPane);
1213 
1214     /* Create a text widget to add to the pane and set its buffer and
1215        highlight data to be the same as the other panes in the document */
1216     XtVaGetValues(window->textArea, textNemulateTabs, &emTabDist,
1217             textNwordDelimiters, &delimiters, textNwrapMargin, &wrapMargin,
1218             textNlineNumCols, &lineNumCols, NULL);
1219     text = createTextArea(window->splitPane, window, 1, 1, emTabDist,
1220             delimiters, wrapMargin, lineNumCols);
1221 
1222     TextSetBuffer(text, window->buffer);
1223     if (window->highlightData != NULL)
1224     	AttachHighlightToWidget(text, window);
1225     if (window->backlightChars)
1226     {
1227         XtVaSetValues(text, textNbacklightCharTypes,
1228                 window->backlightCharTypes, NULL);
1229     }
1230     XtManageChild(text);
1231     window->textPanes[window->nPanes++] = text;
1232 
1233     /* Fix up the colors */
1234     textD = ((TextWidget)window->textArea)->text.textD;
1235     newTextD = ((TextWidget)text)->text.textD;
1236     XtVaSetValues(text,
1237                 XmNforeground, textD->fgPixel,
1238                 XmNbackground, textD->bgPixel,
1239                 NULL);
1240     TextDSetColors( newTextD, textD->fgPixel, textD->bgPixel,
1241             textD->selectFGPixel, textD->selectBGPixel, textD->highlightFGPixel,
1242             textD->highlightBGPixel, textD->lineNumFGPixel,
1243             textD->cursorFGPixel );
1244 
1245     /* Set the minimum pane height in the new pane */
1246     UpdateMinPaneHeights(window);
1247 
1248     /* adjust the heights, scroll positions, etc., to split the focus pane */
1249     for (i=window->nPanes; i>focusPane; i--) {
1250         insertPositions[i] = insertPositions[i-1];
1251         paneHeights[i] = paneHeights[i-1];
1252         topLines[i] = topLines[i-1];
1253         horizOffsets[i] = horizOffsets[i-1];
1254     }
1255     paneHeights[focusPane] = paneHeights[focusPane]/2;
1256     paneHeights[focusPane+1] = paneHeights[focusPane];
1257 
1258     for (i=0; i<=window->nPanes; i++) {
1259         text = i==0 ? window->textArea : window->textPanes[i-1];
1260         setPaneDesiredHeight(containingPane(text), paneHeights[i]);
1261     }
1262 
1263     /* Re-manage panedWindow to recalculate pane heights & reset selection */
1264     if (IsTopDocument(window))
1265     	XtManageChild(window->splitPane);
1266 
1267     /* Reset all of the heights, scroll positions, etc. */
1268     for (i=0; i<=window->nPanes; i++) {
1269         text = i==0 ? window->textArea : window->textPanes[i-1];
1270         TextSetCursorPos(text, insertPositions[i]);
1271         TextSetScroll(text, topLines[i], horizOffsets[i]);
1272         setPaneDesiredHeight(containingPane(text),
1273                             totalHeight/(window->nPanes+1));
1274     }
1275     XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
1276 
1277     /* Update the window manager size hints after the sizes of the panes have
1278        been set (the widget heights are not yet readable here, but they will
1279        be by the time the event loop gets around to running this timer proc) */
1280     XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0,
1281             wmSizeUpdateProc, window);
1282 }
1283 
GetPaneByIndex(WindowInfo * window,int paneIndex)1284 Widget GetPaneByIndex(WindowInfo *window, int paneIndex)
1285 {
1286     Widget text = NULL;
1287     if (paneIndex >= 0 && paneIndex <= window->nPanes) {
1288         text = (paneIndex == 0) ? window->textArea : window->textPanes[paneIndex - 1];
1289     }
1290     return(text);
1291 }
1292 
WidgetToPaneIndex(WindowInfo * window,Widget w)1293 int WidgetToPaneIndex(WindowInfo *window, Widget w)
1294 {
1295     int i;
1296     Widget text;
1297     int paneIndex = 0;
1298 
1299     for (i = 0; i <= window->nPanes; ++i) {
1300         text = (i == 0) ? window->textArea : window->textPanes[i - 1];
1301         if (text == w) {
1302             paneIndex = i;
1303         }
1304     }
1305     return(paneIndex);
1306 }
1307 
1308 /*
1309 ** Close the window pane that last had the keyboard focus.  (Actually, close
1310 ** the bottom pane and make it look like pane which had focus was closed)
1311 */
ClosePane(WindowInfo * window)1312 void ClosePane(WindowInfo *window)
1313 {
1314     short paneHeights[MAX_PANES+1];
1315     int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1];
1316     int horizOffsets[MAX_PANES+1];
1317     int i, focusPane;
1318     Widget text;
1319 
1320     /* Don't delete the last pane */
1321     if (window->nPanes <= 0)
1322         return;
1323 
1324     /* Record the current heights, scroll positions, and insert positions
1325        of the existing panes, and the keyboard focus */
1326     focusPane = 0;
1327     for (i=0; i<=window->nPanes; i++) {
1328         text = i==0 ? window->textArea : window->textPanes[i-1];
1329         insertPositions[i] = TextGetCursorPos(text);
1330         XtVaGetValues(containingPane(text),
1331                             XmNheight, &paneHeights[i], NULL);
1332         TextGetScroll(text, &topLines[i], &horizOffsets[i]);
1333         if (text == window->lastFocus)
1334             focusPane = i;
1335     }
1336 
1337     /* Unmanage & remanage the panedWindow so it recalculates pane heights */
1338     XtUnmanageChild(window->splitPane);
1339 
1340     /* Destroy last pane, and make sure lastFocus points to an existing pane.
1341        Workaround for OM 2.1.30: text widget must be unmanaged for
1342        xmPanedWindowWidget to calculate the correct pane heights for
1343        the remaining panes, simply detroying it didn't seem enough */
1344     window->nPanes--;
1345     XtUnmanageChild(containingPane(window->textPanes[window->nPanes]));
1346     XtDestroyWidget(containingPane(window->textPanes[window->nPanes]));
1347 
1348     if (window->nPanes == 0)
1349         window->lastFocus = window->textArea;
1350     else if (focusPane > window->nPanes)
1351         window->lastFocus = window->textPanes[window->nPanes-1];
1352 
1353     /* adjust the heights, scroll positions, etc., to make it look
1354        like the pane with the input focus was closed */
1355     for (i=focusPane; i<=window->nPanes; i++) {
1356         insertPositions[i] = insertPositions[i+1];
1357         paneHeights[i] = paneHeights[i+1];
1358         topLines[i] = topLines[i+1];
1359         horizOffsets[i] = horizOffsets[i+1];
1360     }
1361 
1362     /* set the desired heights and re-manage the paned window so it will
1363        recalculate pane heights */
1364     for (i=0; i<=window->nPanes; i++) {
1365         text = i==0 ? window->textArea : window->textPanes[i-1];
1366         setPaneDesiredHeight(containingPane(text), paneHeights[i]);
1367     }
1368 
1369     if (IsTopDocument(window))
1370     	XtManageChild(window->splitPane);
1371 
1372     /* Reset all of the scroll positions, insert positions, etc. */
1373     for (i=0; i<=window->nPanes; i++) {
1374         text = i==0 ? window->textArea : window->textPanes[i-1];
1375         TextSetCursorPos(text, insertPositions[i]);
1376         TextSetScroll(text, topLines[i], horizOffsets[i]);
1377     }
1378     XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
1379 
1380     /* Update the window manager size hints after the sizes of the panes have
1381        been set (the widget heights are not yet readable here, but they will
1382        be by the time the event loop gets around to running this timer proc) */
1383     XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0,
1384             wmSizeUpdateProc, window);
1385 }
1386 
1387 /*
1388 ** Turn on and off the display of line numbers
1389 */
ShowLineNumbers(WindowInfo * window,int state)1390 void ShowLineNumbers(WindowInfo *window, int state)
1391 {
1392     Widget text;
1393     int i, marginWidth;
1394     unsigned reqCols = 0;
1395     Dimension windowWidth;
1396     WindowInfo *win;
1397     textDisp *textD = ((TextWidget)window->textArea)->text.textD;
1398 
1399     if (window->showLineNumbers == state)
1400         return;
1401     window->showLineNumbers = state;
1402 
1403     /* Just setting window->showLineNumbers is sufficient to tell
1404        updateLineNumDisp() to expand the line number areas and the window
1405        size for the number of lines required.  To hide the line number
1406        display, set the width to zero, and contract the window width. */
1407     if (state) {
1408         reqCols = updateLineNumDisp(window);
1409     } else {
1410         XtVaGetValues(window->shell, XmNwidth, &windowWidth, NULL);
1411         XtVaGetValues(window->textArea,
1412 	        textNmarginWidth, &marginWidth, NULL);
1413         XtVaSetValues(window->shell, XmNwidth,
1414                 windowWidth - textD->left + marginWidth, NULL);
1415 
1416         for (i=0; i<=window->nPanes; i++) {
1417             text = i==0 ? window->textArea : window->textPanes[i-1];
1418             XtVaSetValues(text, textNlineNumCols, 0, NULL);
1419         }
1420     }
1421 
1422     /* line numbers panel is shell-level, hence other
1423        tabbed documents in the window should synch */
1424     for (win=WindowList; win; win=win->next) {
1425     	if (win->shell != window->shell || win == window)
1426 	    continue;
1427 
1428     	win->showLineNumbers = state;
1429 
1430         for (i=0; i<=win->nPanes; i++) {
1431             text = i==0 ? win->textArea : win->textPanes[i-1];
1432             /*  reqCols should really be cast here, but into what? XmRInt?  */
1433             XtVaSetValues(text, textNlineNumCols, reqCols, NULL);
1434         }
1435     }
1436 
1437     /* Tell WM that the non-expandable part of the window has changed size */
1438     UpdateWMSizeHints(window);
1439 }
1440 
SetTabDist(WindowInfo * window,int tabDist)1441 void SetTabDist(WindowInfo *window, int tabDist)
1442 {
1443     if (window->buffer->tabDist != tabDist) {
1444         int saveCursorPositions[MAX_PANES + 1];
1445         int saveVScrollPositions[MAX_PANES + 1];
1446         int saveHScrollPositions[MAX_PANES + 1];
1447         int paneIndex;
1448 
1449         window->ignoreModify = True;
1450 
1451         for (paneIndex = 0; paneIndex <= window->nPanes; ++paneIndex) {
1452             Widget w = GetPaneByIndex(window, paneIndex);
1453             textDisp *textD = ((TextWidget)w)->text.textD;
1454 
1455             TextGetScroll(w, &saveVScrollPositions[paneIndex], &saveHScrollPositions[paneIndex]);
1456             saveCursorPositions[paneIndex] = TextGetCursorPos(w);
1457             textD->modifyingTabDist = 1;
1458         }
1459 
1460         BufSetTabDistance(window->buffer, tabDist);
1461 
1462         for (paneIndex = 0; paneIndex <= window->nPanes; ++paneIndex) {
1463             Widget w = GetPaneByIndex(window, paneIndex);
1464             textDisp *textD = ((TextWidget)w)->text.textD;
1465 
1466             textD->modifyingTabDist = 0;
1467             TextSetCursorPos(w, saveCursorPositions[paneIndex]);
1468             TextSetScroll(w, saveVScrollPositions[paneIndex], saveHScrollPositions[paneIndex]);
1469         }
1470 
1471         window->ignoreModify = False;
1472     }
1473 }
1474 
SetEmTabDist(WindowInfo * window,int emTabDist)1475 void SetEmTabDist(WindowInfo *window, int emTabDist)
1476 {
1477     int i;
1478 
1479     XtVaSetValues(window->textArea, textNemulateTabs, emTabDist, NULL);
1480     for (i = 0; i < window->nPanes; ++i) {
1481         XtVaSetValues(window->textPanes[i], textNemulateTabs, emTabDist, NULL);
1482     }
1483 }
1484 
1485 /*
1486 ** Turn on and off the display of the statistics line
1487 */
ShowStatsLine(WindowInfo * window,int state)1488 void ShowStatsLine(WindowInfo *window, int state)
1489 {
1490     WindowInfo *win;
1491     Widget text;
1492     int i;
1493 
1494     /* In continuous wrap mode, text widgets must be told to keep track of
1495        the top line number in absolute (non-wrapped) lines, because it can
1496        be a costly calculation, and is only needed for displaying line
1497        numbers, either in the stats line, or along the left margin */
1498     for (i=0; i<=window->nPanes; i++) {
1499         text = i==0 ? window->textArea : window->textPanes[i-1];
1500         TextDMaintainAbsLineNum(((TextWidget)text)->text.textD, state);
1501     }
1502     window->showStats = state;
1503     showStats(window, state);
1504 
1505     /* i-search line is shell-level, hence other tabbed
1506        documents in the window should synch */
1507     for (win=WindowList; win; win=win->next) {
1508     	if (win->shell != window->shell || win == window)
1509 	    continue;
1510 	win->showStats = state;
1511     }
1512 }
1513 
1514 /*
1515 ** Displays and undisplays the statistics line (regardless of settings of
1516 ** window->showStats or window->modeMessageDisplayed)
1517 */
showStats(WindowInfo * window,int state)1518 static void showStats(WindowInfo *window, int state)
1519 {
1520     if (state) {
1521         XtManageChild(window->statsLineForm);
1522         showStatsForm(window);
1523     } else {
1524         XtUnmanageChild(window->statsLineForm);
1525         showStatsForm(window);
1526     }
1527 
1528     /* Tell WM that the non-expandable part of the window has changed size */
1529     /* Already done in showStatsForm */
1530     /* UpdateWMSizeHints(window); */
1531 }
1532 
1533 /*
1534 */
showTabBar(WindowInfo * window,int state)1535 static void showTabBar(WindowInfo *window, int state)
1536 {
1537     if (state) {
1538 	XtManageChild(XtParent(window->tabBar));
1539 	showStatsForm(window);
1540     } else {
1541 	XtUnmanageChild(XtParent(window->tabBar));
1542 	showStatsForm(window);
1543     }
1544 }
1545 
1546 /*
1547 */
ShowTabBar(WindowInfo * window,int state)1548 void ShowTabBar(WindowInfo *window, int state)
1549 {
1550     if (XtIsManaged(XtParent(window->tabBar)) == state)
1551         return;
1552     showTabBar(window, state);
1553 }
1554 
1555 /*
1556 ** Turn on and off the continuing display of the incremental search line
1557 ** (when off, it is popped up and down as needed via TempShowISearch)
1558 */
ShowISearchLine(WindowInfo * window,int state)1559 void ShowISearchLine(WindowInfo *window, int state)
1560 {
1561     WindowInfo *win;
1562 
1563     if (window->showISearchLine == state)
1564         return;
1565     window->showISearchLine = state;
1566     showISearch(window, state);
1567 
1568     /* i-search line is shell-level, hence other tabbed
1569        documents in the window should synch */
1570     for (win=WindowList; win; win=win->next) {
1571     	if (win->shell != window->shell || win == window)
1572 	    continue;
1573 	win->showISearchLine = state;
1574     }
1575 }
1576 
1577 /*
1578 ** Temporarily show and hide the incremental search line if the line is not
1579 ** already up.
1580 */
TempShowISearch(WindowInfo * window,int state)1581 void TempShowISearch(WindowInfo *window, int state)
1582 {
1583     if (window->showISearchLine)
1584         return;
1585     if (XtIsManaged(window->iSearchForm) != state)
1586         showISearch(window, state);
1587 }
1588 
1589 /*
1590 ** Put up or pop-down the incremental search line regardless of settings
1591 ** of showISearchLine or TempShowISearch
1592 */
showISearch(WindowInfo * window,int state)1593 static void showISearch(WindowInfo *window, int state)
1594 {
1595     if (state) {
1596 	XtManageChild(window->iSearchForm);
1597 	showStatsForm(window);
1598     } else {
1599 	XtUnmanageChild(window->iSearchForm);
1600 	showStatsForm(window);
1601     }
1602 
1603     /* Tell WM that the non-expandable part of the window has changed size */
1604     /* This is already done in showStatsForm */
1605     /* UpdateWMSizeHints(window); */
1606 }
1607 
1608 /*
1609 ** Show or hide the extra display area under the main menu bar which
1610 ** optionally contains the status line and the incremental search bar
1611 */
showStatsForm(WindowInfo * window)1612 static void showStatsForm(WindowInfo *window)
1613 {
1614     Widget statsAreaForm = XtParent(window->statsLineForm);
1615     Widget mainW = XtParent(statsAreaForm);
1616 
1617     /* The very silly use of XmNcommandWindowLocation and XmNshowSeparator
1618        below are to kick the main window widget to position and remove the
1619        status line when it is managed and unmanaged.  At some Motif version
1620        level, the showSeparator trick backfires and leaves the separator
1621        shown, but fortunately the dynamic behavior is fixed, too so the
1622        workaround is no longer necessary, either.  (... the version where
1623        this occurs may be earlier than 2.1.  If the stats line shows
1624        double thickness shadows in earlier Motif versions, the #if XmVersion
1625        directive should be moved back to that earlier version) */
1626     if (manageToolBars(statsAreaForm)) {
1627         XtUnmanageChild(statsAreaForm);    /*... will this fix Solaris 7??? */
1628         XtVaSetValues(mainW, XmNcommandWindowLocation,
1629                 XmCOMMAND_ABOVE_WORKSPACE, NULL);
1630 #if XmVersion < 2001
1631         XtVaSetValues(mainW, XmNshowSeparator, True, NULL);
1632 #endif
1633         XtManageChild(statsAreaForm);
1634         XtVaSetValues(mainW, XmNshowSeparator, False, NULL);
1635         UpdateStatsLine(window);
1636     } else {
1637         XtUnmanageChild(statsAreaForm);
1638         XtVaSetValues(mainW, XmNcommandWindowLocation,
1639                 XmCOMMAND_BELOW_WORKSPACE, NULL);
1640     }
1641 
1642     /* Tell WM that the non-expandable part of the window has changed size */
1643     UpdateWMSizeHints(window);
1644 }
1645 
1646 /*
1647 ** Display a special message in the stats line (show the stats line if it
1648 ** is not currently shown).
1649 */
SetModeMessage(WindowInfo * window,const char * message)1650 void SetModeMessage(WindowInfo *window, const char *message)
1651 {
1652     /* this document may be hidden (not on top) or later made hidden,
1653        so we save a copy of the mode message, so we can restore the
1654        statsline when the document is raised to top again */
1655     window->modeMessageDisplayed = True;
1656     NEditFree(window->modeMessage);
1657     window->modeMessage = NEditStrdup(message);
1658 
1659     if (!IsTopDocument(window))
1660     	return;
1661 
1662     XmTextSetString(window->statsLine, (char*)message);
1663     /*
1664      * Don't invoke the stats line again, if stats line is already displayed.
1665      */
1666     if (!window->showStats)
1667 	showStats(window, True);
1668 }
1669 
1670 /*
1671 ** Clear special statistics line message set in SetModeMessage, returns
1672 ** the statistics line to its original state as set in window->showStats
1673 */
ClearModeMessage(WindowInfo * window)1674 void ClearModeMessage(WindowInfo *window)
1675 {
1676     if (!window->modeMessageDisplayed)
1677     	return;
1678 
1679     window->modeMessageDisplayed = False;
1680     NEditFree(window->modeMessage);
1681     window->modeMessage = NULL;
1682 
1683     if (!IsTopDocument(window))
1684     	return;
1685 
1686     /*
1687      * Remove the stats line only if indicated by it's window state.
1688      */
1689     if (!window->showStats)
1690         showStats(window, False);
1691     UpdateStatsLine(window);
1692 }
1693 
1694 /*
1695 ** Count the windows
1696 */
NWindows(void)1697 int NWindows(void)
1698 {
1699     WindowInfo *win;
1700     int n;
1701 
1702     for (win=WindowList, n=0; win!=NULL; win=win->next, n++);
1703     return n;
1704 }
1705 
1706 /*
1707 ** Set autoindent state to one of  NO_AUTO_INDENT, AUTO_INDENT, or SMART_INDENT.
1708 */
SetAutoIndent(WindowInfo * window,IndentStyle state)1709 void SetAutoIndent(WindowInfo *window, IndentStyle state)
1710 {
1711     int autoIndent = state == AUTO_INDENT, smartIndent = state == SMART_INDENT;
1712     int i;
1713 
1714     if (window->indentStyle == SMART_INDENT && !smartIndent)
1715         EndSmartIndent(window);
1716     else if (smartIndent && window->indentStyle != SMART_INDENT)
1717         BeginSmartIndent(window, True);
1718     window->indentStyle = state;
1719     XtVaSetValues(window->textArea, textNautoIndent, autoIndent,
1720             textNsmartIndent, smartIndent, NULL);
1721     for (i=0; i<window->nPanes; i++)
1722         XtVaSetValues(window->textPanes[i], textNautoIndent, autoIndent,
1723                 textNsmartIndent, smartIndent, NULL);
1724     if (IsTopDocument(window)) {
1725 	XmToggleButtonSetState(window->smartIndentItem, smartIndent, False);
1726 	XmToggleButtonSetState(window->autoIndentItem, autoIndent, False);
1727 	XmToggleButtonSetState(window->autoIndentOffItem,
1728 	        state == NO_AUTO_INDENT, False);
1729     }
1730 }
1731 
1732 /*
1733 ** Set showMatching state to one of NO_FLASH, FLASH_DELIMIT or FLASH_RANGE.
1734 ** Update the menu to reflect the change of state.
1735 */
SetShowMatching(WindowInfo * window,ShowMatchingStyle state)1736 void SetShowMatching(WindowInfo *window, ShowMatchingStyle state)
1737 {
1738     window->showMatchingStyle = state;
1739     if (IsTopDocument(window)) {
1740 	XmToggleButtonSetState(window->showMatchingOffItem,
1741             state == NO_FLASH, False);
1742 	XmToggleButtonSetState(window->showMatchingDelimitItem,
1743             state == FLASH_DELIMIT, False);
1744 	XmToggleButtonSetState(window->showMatchingRangeItem,
1745             state == FLASH_RANGE, False);
1746     }
1747 }
1748 
1749 /*
1750 ** Update the "New (in X)" menu item to reflect the preferences
1751 */
UpdateNewOppositeMenu(WindowInfo * window,int openInTab)1752 void UpdateNewOppositeMenu(WindowInfo *window, int openInTab)
1753 {
1754     XmString lbl;
1755     if ( openInTab )
1756         XtVaSetValues(window->newOppositeItem,
1757                 XmNlabelString, lbl=XmStringCreateSimple("New Window"),
1758                 XmNmnemonic, 'W', NULL);
1759     else
1760         XtVaSetValues(window->newOppositeItem,
1761                 XmNlabelString, lbl=XmStringCreateSimple("New Tab"),
1762                 XmNmnemonic, 'T', NULL);
1763     XmStringFree(lbl);
1764 }
1765 
1766 /*
1767 ** Set the fonts for "window" from a font name, and updates the display.
1768 ** Also updates window->fontList which is used for statistics line.
1769 **
1770 ** Note that this leaks memory and server resources.  In previous NEdit
1771 ** versions, fontLists were carefully tracked and freed, but X and Motif
1772 ** have some kind of timing problem when widgets are distroyed, such that
1773 ** fonts may not be freed immediately after widget destruction with 100%
1774 ** safety.  Rather than kludge around this with timerProcs, I have chosen
1775 ** to create new fontLists only when the user explicitly changes the font
1776 ** (which shouldn't happen much in normal NEdit operation), and skip the
1777 ** futile effort of freeing them.
1778 */
SetFonts(WindowInfo * window,const char * fontName,const char * italicName,const char * boldName,const char * boldItalicName)1779 void SetFonts(WindowInfo *window, const char *fontName, const char *italicName,
1780         const char *boldName, const char *boldItalicName)
1781 {
1782     XFontStruct *font, *oldFont;
1783     int i, oldFontWidth, oldFontHeight, fontWidth, fontHeight;
1784     int borderWidth, borderHeight, marginWidth, marginHeight;
1785     int primaryChanged, highlightChanged = False;
1786     Dimension oldWindowWidth, oldWindowHeight, oldTextWidth, oldTextHeight;
1787     Dimension textHeight, newWindowWidth, newWindowHeight;
1788     textDisp *textD = ((TextWidget)window->textArea)->text.textD;
1789 
1790     /* Check which fonts have changed */
1791     primaryChanged = strcmp(fontName, window->fontName);
1792     if (strcmp(italicName, window->italicFontName)) highlightChanged = True;
1793     if (strcmp(boldName, window->boldFontName)) highlightChanged = True;
1794     if (strcmp(boldItalicName, window->boldItalicFontName))
1795         highlightChanged = True;
1796     if (!primaryChanged && !highlightChanged)
1797         return;
1798 
1799     /* Get information about the current window sizing, to be used to
1800        determine the correct window size after the font is changed */
1801     XtVaGetValues(window->shell, XmNwidth, &oldWindowWidth, XmNheight,
1802             &oldWindowHeight, NULL);
1803     XtVaGetValues(window->textArea, XmNheight, &textHeight,
1804             textNmarginHeight, &marginHeight, textNmarginWidth,
1805             &marginWidth, textNfont, &oldFont, NULL);
1806     oldTextWidth = textD->width + textD->lineNumWidth;
1807     oldTextHeight = textHeight - 2*marginHeight;
1808     for (i=0; i<window->nPanes; i++) {
1809         XtVaGetValues(window->textPanes[i], XmNheight, &textHeight, NULL);
1810         oldTextHeight += textHeight - 2*marginHeight;
1811     }
1812     borderWidth = oldWindowWidth - oldTextWidth;
1813     borderHeight = oldWindowHeight - oldTextHeight;
1814     oldFontWidth = oldFont->max_bounds.width;
1815     oldFontHeight = textD->ascent + textD->descent;
1816 
1817 
1818     /* Change the fonts in the window data structure.  If the primary font
1819        didn't work, use Motif's fallback mechanism by stealing it from the
1820        statistics line.  Highlight fonts are allowed to be NULL, which
1821        is interpreted as "use the primary font" */
1822     if (primaryChanged) {
1823         strcpy(window->fontName, fontName);
1824         font = XLoadQueryFont(TheDisplay, fontName);
1825         if (font == NULL)
1826             XtVaGetValues(window->statsLine, XmNfontList, &window->fontList,
1827                     NULL);
1828         else
1829             window->fontList = XmFontListCreate(font, XmSTRING_DEFAULT_CHARSET);
1830     }
1831     if (highlightChanged) {
1832         strcpy(window->italicFontName, italicName);
1833         window->italicFontStruct = XLoadQueryFont(TheDisplay, italicName);
1834         strcpy(window->boldFontName, boldName);
1835         window->boldFontStruct = XLoadQueryFont(TheDisplay, boldName);
1836         strcpy(window->boldItalicFontName, boldItalicName);
1837         window->boldItalicFontStruct = XLoadQueryFont(TheDisplay, boldItalicName);
1838     }
1839 
1840     /* Change the primary font in all the widgets */
1841     if (primaryChanged) {
1842         font = GetDefaultFontStruct(TheDisplay, window->fontList);
1843         XtVaSetValues(window->textArea, textNfont, font, NULL);
1844         for (i=0; i<window->nPanes; i++)
1845             XtVaSetValues(window->textPanes[i], textNfont, font, NULL);
1846     }
1847 
1848     /* Change the highlight fonts, even if they didn't change, because
1849        primary font is read through the style table for syntax highlighting */
1850     if (window->highlightData != NULL)
1851         UpdateHighlightStyles(window);
1852 
1853     /* Change the window manager size hints.
1854        Note: this has to be done _before_ we set the new sizes. ICCCM2
1855        compliant window managers (such as fvwm2) would otherwise resize
1856        the window twice: once because of the new sizes requested, and once
1857        because of the new size increments, resulting in an overshoot. */
1858     UpdateWMSizeHints(window);
1859 
1860     /* Use the information from the old window to re-size the window to a
1861        size appropriate for the new font, but only do so if there's only
1862        _one_ document in the window, in order to avoid growing-window bug */
1863     if (NDocuments(window) == 1) {
1864 	fontWidth = GetDefaultFontStruct(TheDisplay, window->fontList)->max_bounds.width;
1865 	fontHeight = textD->ascent + textD->descent;
1866 	newWindowWidth = (oldTextWidth*fontWidth) / oldFontWidth + borderWidth;
1867 	newWindowHeight = (oldTextHeight*fontHeight) / oldFontHeight +
1868 	        borderHeight;
1869 	XtVaSetValues(window->shell, XmNwidth, newWindowWidth, XmNheight,
1870         	newWindowHeight, NULL);
1871     }
1872 
1873     /* Change the minimum pane height */
1874     UpdateMinPaneHeights(window);
1875 }
1876 
SetColors(WindowInfo * window,const char * textFg,const char * textBg,const char * selectFg,const char * selectBg,const char * hiliteFg,const char * hiliteBg,const char * lineNoFg,const char * cursorFg)1877 void SetColors(WindowInfo *window, const char *textFg, const char *textBg,
1878         const char *selectFg, const char *selectBg, const char *hiliteFg,
1879         const char *hiliteBg, const char *lineNoFg, const char *cursorFg)
1880 {
1881     int i, dummy;
1882     Pixel   textFgPix   = AllocColor( window->textArea, textFg,
1883                     &dummy, &dummy, &dummy),
1884             textBgPix   = AllocColor( window->textArea, textBg,
1885                     &dummy, &dummy, &dummy),
1886             selectFgPix = AllocColor( window->textArea, selectFg,
1887                     &dummy, &dummy, &dummy),
1888             selectBgPix = AllocColor( window->textArea, selectBg,
1889                     &dummy, &dummy, &dummy),
1890             hiliteFgPix = AllocColor( window->textArea, hiliteFg,
1891                     &dummy, &dummy, &dummy),
1892             hiliteBgPix = AllocColor( window->textArea, hiliteBg,
1893                     &dummy, &dummy, &dummy),
1894             lineNoFgPix = AllocColor( window->textArea, lineNoFg,
1895                     &dummy, &dummy, &dummy),
1896             cursorFgPix = AllocColor( window->textArea, cursorFg,
1897                     &dummy, &dummy, &dummy);
1898     textDisp *textD;
1899 
1900     /* Update the main pane */
1901     XtVaSetValues(window->textArea,
1902             XmNforeground, textFgPix,
1903             XmNbackground, textBgPix,
1904             NULL);
1905     textD = ((TextWidget)window->textArea)->text.textD;
1906     TextDSetColors( textD, textFgPix, textBgPix, selectFgPix, selectBgPix,
1907             hiliteFgPix, hiliteBgPix, lineNoFgPix, cursorFgPix );
1908     /* Update any additional panes */
1909     for (i=0; i<window->nPanes; i++) {
1910         XtVaSetValues(window->textPanes[i],
1911                 XmNforeground, textFgPix,
1912                 XmNbackground, textBgPix,
1913                 NULL);
1914         textD = ((TextWidget)window->textPanes[i])->text.textD;
1915         TextDSetColors( textD, textFgPix, textBgPix, selectFgPix, selectBgPix,
1916                 hiliteFgPix, hiliteBgPix, lineNoFgPix, cursorFgPix );
1917     }
1918 
1919     /* Redo any syntax highlighting */
1920     if (window->highlightData != NULL)
1921         UpdateHighlightStyles(window);
1922 }
1923 
1924 /*
1925 ** Set insert/overstrike mode
1926 */
SetOverstrike(WindowInfo * window,int overstrike)1927 void SetOverstrike(WindowInfo *window, int overstrike)
1928 {
1929     int i;
1930 
1931     XtVaSetValues(window->textArea, textNoverstrike, overstrike, NULL);
1932     for (i=0; i<window->nPanes; i++)
1933         XtVaSetValues(window->textPanes[i], textNoverstrike, overstrike, NULL);
1934     window->overstrike = overstrike;
1935 }
1936 
1937 /*
1938 ** Select auto-wrap mode, one of NO_WRAP, NEWLINE_WRAP, or CONTINUOUS_WRAP
1939 */
SetAutoWrap(WindowInfo * window,WrapStyle state)1940 void SetAutoWrap(WindowInfo *window, WrapStyle state)
1941 {
1942     int i;
1943     int autoWrap = state == NEWLINE_WRAP, contWrap = state == CONTINUOUS_WRAP;
1944 
1945     XtVaSetValues(window->textArea, textNautoWrap, autoWrap,
1946             textNcontinuousWrap, contWrap, NULL);
1947     for (i=0; i<window->nPanes; i++)
1948         XtVaSetValues(window->textPanes[i], textNautoWrap, autoWrap,
1949                 textNcontinuousWrap, contWrap, NULL);
1950     window->wrapMode = state;
1951 
1952     if (IsTopDocument(window)) {
1953 	XmToggleButtonSetState(window->newlineWrapItem, autoWrap, False);
1954 	XmToggleButtonSetState(window->continuousWrapItem, contWrap, False);
1955 	XmToggleButtonSetState(window->noWrapItem, state == NO_WRAP, False);
1956     }
1957 }
1958 
1959 /*
1960 ** Set the auto-scroll margin
1961 */
SetAutoScroll(WindowInfo * window,int margin)1962 void SetAutoScroll(WindowInfo *window, int margin)
1963 {
1964     int i;
1965 
1966     XtVaSetValues(window->textArea, textNcursorVPadding, margin, NULL);
1967     for (i=0; i<window->nPanes; i++)
1968         XtVaSetValues(window->textPanes[i], textNcursorVPadding, margin, NULL);
1969 }
1970 
1971 /*
1972 ** Recover the window pointer from any widget in the window, by searching
1973 ** up the widget hierarcy for the top level container widget where the
1974 ** window pointer is stored in the userData field. In a tabbed window,
1975 ** this is the window pointer of the top (active) document, which is
1976 ** returned if w is 'shell-level' widget - menus, find/replace dialogs, etc.
1977 **
1978 ** To support action routine in tabbed windows, a copy of the window
1979 ** pointer is also store in the splitPane widget.
1980 */
WidgetToWindow(Widget w)1981 WindowInfo *WidgetToWindow(Widget w)
1982 {
1983     WindowInfo *window = NULL;
1984     Widget parent;
1985 
1986     while (True) {
1987     	/* return window pointer of document */
1988     	if (XtClass(w) == xmPanedWindowWidgetClass)
1989 	    break;
1990 
1991 	if (XtClass(w) == topLevelShellWidgetClass) {
1992     	    WidgetList items;
1993 
1994 	    /* there should be only 1 child for the shell -
1995 	       the main window widget */
1996     	    XtVaGetValues(w, XmNchildren, &items, NULL);
1997 	    w = items[0];
1998 	    break;
1999 	}
2000 
2001     	parent = XtParent(w);
2002     	if (parent == NULL)
2003     	    return NULL;
2004 
2005 	/* make sure it is not a dialog shell */
2006     	if (XtClass(parent) == topLevelShellWidgetClass &&
2007 	    	XmIsMainWindow(w))
2008     	    break;
2009 
2010     	w = parent;
2011     }
2012 
2013     XtVaGetValues(w, XmNuserData, &window, NULL);
2014 
2015     return window;
2016 }
2017 
2018 /*
2019 ** Change the window appearance and the window data structure to show
2020 ** that the file it contains has been modified
2021 */
SetWindowModified(WindowInfo * window,int modified)2022 void SetWindowModified(WindowInfo *window, int modified)
2023 {
2024     if (window->fileChanged == FALSE && modified == TRUE) {
2025     	SetSensitive(window, window->closeItem, TRUE);
2026     	window->fileChanged = TRUE;
2027     	UpdateWindowTitle(window);
2028 	RefreshTabState(window);
2029     } else if (window->fileChanged == TRUE && modified == FALSE) {
2030     	window->fileChanged = FALSE;
2031     	UpdateWindowTitle(window);
2032 	RefreshTabState(window);
2033     }
2034 }
2035 
2036 /*
2037 ** Update the window title to reflect the filename, read-only, and modified
2038 ** status of the window data structure
2039 */
UpdateWindowTitle(const WindowInfo * window)2040 void UpdateWindowTitle(const WindowInfo *window)
2041 {
2042     char *iconTitle, *title;
2043 
2044     if (!IsTopDocument(window))
2045     	return;
2046 
2047     title = FormatWindowTitle(window->filename,
2048                                     window->path,
2049 #ifdef VMS
2050                                     NULL,
2051 #else
2052                                     GetClearCaseViewTag(),
2053 #endif /* VMS */
2054                                     GetPrefServerName(),
2055                                     IsServer,
2056                                     window->filenameSet,
2057                                     window->lockReasons,
2058                                     window->fileChanged,
2059                                     GetPrefTitleFormat());
2060 
2061     iconTitle = (char*)NEditMalloc(strlen(window->filename) + 2); /* strlen("*")+1 */
2062 
2063     strcpy(iconTitle, window->filename);
2064     if (window->fileChanged)
2065         strcat(iconTitle, "*");
2066     XtVaSetValues(window->shell, XmNtitle, title, XmNiconName, iconTitle, NULL);
2067 
2068     /* If there's a find or replace dialog up in "Keep Up" mode, with a
2069        file name in the title, update it too */
2070     if (window->findDlog && XmToggleButtonGetState(window->findKeepBtn)) {
2071         sprintf(title, "Find (in %s)", window->filename);
2072         XtVaSetValues(XtParent(window->findDlog), XmNtitle, title, NULL);
2073     }
2074     if (window->replaceDlog && XmToggleButtonGetState(window->replaceKeepBtn)) {
2075         sprintf(title, "Replace (in %s)", window->filename);
2076         XtVaSetValues(XtParent(window->replaceDlog), XmNtitle, title, NULL);
2077     }
2078     NEditFree(iconTitle);
2079 
2080     /* Update the Windows menus with the new name */
2081     InvalidateWindowMenus();
2082 }
2083 
2084 /*
2085 ** Update the read-only state of the text area(s) in the window, and
2086 ** the ReadOnly toggle button in the File menu to agree with the state in
2087 ** the window data structure.
2088 */
UpdateWindowReadOnly(WindowInfo * window)2089 void UpdateWindowReadOnly(WindowInfo *window)
2090 {
2091     int i, state;
2092 
2093     if (!IsTopDocument(window))
2094     	return;
2095 
2096     state = IS_ANY_LOCKED(window->lockReasons);
2097     XtVaSetValues(window->textArea, textNreadOnly, state, NULL);
2098     for (i=0; i<window->nPanes; i++)
2099         XtVaSetValues(window->textPanes[i], textNreadOnly, state, NULL);
2100     XmToggleButtonSetState(window->readOnlyItem, state, FALSE);
2101     XtSetSensitive(window->readOnlyItem,
2102         !IS_ANY_LOCKED_IGNORING_USER(window->lockReasons));
2103 }
2104 
2105 /*
2106 ** Find the start and end of a single line selection.  Hides rectangular
2107 ** selection issues for older routines which use selections that won't
2108 ** span lines.
2109 */
GetSimpleSelection(textBuffer * buf,int * left,int * right)2110 int GetSimpleSelection(textBuffer *buf, int *left, int *right)
2111 {
2112     int selStart, selEnd, isRect, rectStart, rectEnd, lineStart;
2113 
2114     /* get the character to match and its position from the selection, or
2115        the character before the insert point if nothing is selected.
2116        Give up if too many characters are selected */
2117     if (!BufGetSelectionPos(buf, &selStart, &selEnd, &isRect,
2118             &rectStart, &rectEnd))
2119         return False;
2120     if (isRect) {
2121         lineStart = BufStartOfLine(buf, selStart);
2122         selStart = BufCountForwardDispChars(buf, lineStart, rectStart);
2123         selEnd = BufCountForwardDispChars(buf, lineStart, rectEnd);
2124     }
2125     *left = selStart;
2126     *right = selEnd;
2127     return True;
2128 }
2129 
2130 /*
2131 ** If the selection (or cursor position if there's no selection) is not
2132 ** fully shown, scroll to bring it in to view.  Note that as written,
2133 ** this won't work well with multi-line selections.  Modest re-write
2134 ** of the horizontal scrolling part would be quite easy to make it work
2135 ** well with rectangular selections.
2136 */
MakeSelectionVisible(WindowInfo * window,Widget textPane)2137 void MakeSelectionVisible(WindowInfo *window, Widget textPane)
2138 {
2139     int left, right, isRect, rectStart, rectEnd, horizOffset;
2140     int scrollOffset, leftX, rightX, y, rows, margin;
2141     int topLineNum, lastLineNum, rightLineNum, leftLineNum, linesToScroll;
2142     textDisp *textD = ((TextWidget)textPane)->text.textD;
2143     int topChar = TextFirstVisiblePos(textPane);
2144     int lastChar = TextLastVisiblePos(textPane);
2145     int targetLineNum;
2146     Dimension width;
2147 
2148     /* find out where the selection is */
2149     if (!BufGetSelectionPos(window->buffer, &left, &right, &isRect,
2150             &rectStart, &rectEnd)) {
2151         left = right = TextGetCursorPos(textPane);
2152         isRect = False;
2153     }
2154 
2155     /* Check vertical positioning unless the selection is already shown or
2156        already covers the display.  If the end of the selection is below
2157        bottom, scroll it in to view until the end selection is scrollOffset
2158        lines from the bottom of the display or the start of the selection
2159        scrollOffset lines from the top.  Calculate a pleasing distance from the
2160        top or bottom of the window, to scroll the selection to (if scrolling is
2161        necessary), around 1/3 of the height of the window */
2162     if (!((left >= topChar && right <= lastChar) ||
2163             (left <= topChar && right >= lastChar))) {
2164         XtVaGetValues(textPane, textNrows, &rows, NULL);
2165         scrollOffset = rows/3;
2166         TextGetScroll(textPane, &topLineNum, &horizOffset);
2167         if (right > lastChar) {
2168             /* End of sel. is below bottom of screen */
2169             leftLineNum = topLineNum +
2170                     TextDCountLines(textD, topChar, left, False);
2171             targetLineNum = topLineNum + scrollOffset;
2172             if (leftLineNum >= targetLineNum) {
2173                 /* Start of sel. is not between top & target */
2174                 linesToScroll = TextDCountLines(textD, lastChar, right, False) +
2175                         scrollOffset;
2176                 if (leftLineNum - linesToScroll < targetLineNum)
2177                     linesToScroll = leftLineNum - targetLineNum;
2178                 /* Scroll start of selection to the target line */
2179                 TextSetScroll(textPane, topLineNum+linesToScroll, horizOffset);
2180             }
2181         } else if (left < topChar) {
2182             /* Start of sel. is above top of screen */
2183             lastLineNum = topLineNum + rows;
2184             rightLineNum = lastLineNum -
2185                     TextDCountLines(textD, right, lastChar, False);
2186             targetLineNum = lastLineNum - scrollOffset;
2187             if (rightLineNum <= targetLineNum) {
2188                 /* End of sel. is not between bottom & target */
2189                 linesToScroll = TextDCountLines(textD, left, topChar, False) +
2190                         scrollOffset;
2191                 if (rightLineNum + linesToScroll > targetLineNum)
2192                     linesToScroll = targetLineNum - rightLineNum;
2193                 /* Scroll end of selection to the target line */
2194                 TextSetScroll(textPane, topLineNum-linesToScroll, horizOffset);
2195             }
2196         }
2197     }
2198 
2199     /* If either end of the selection off screen horizontally, try to bring it
2200        in view, by making sure both end-points are visible.  Using only end
2201        points of a multi-line selection is not a great idea, and disaster for
2202        rectangular selections, so this part of the routine should be re-written
2203        if it is to be used much with either.  Note also that this is a second
2204        scrolling operation, causing the display to jump twice.  It's done after
2205        vertical scrolling to take advantage of TextPosToXY which requires it's
2206        reqested position to be vertically on screen) */
2207     if (    TextPosToXY(textPane, left, &leftX, &y) &&
2208             TextPosToXY(textPane, right, &rightX, &y) && leftX <= rightX) {
2209         TextGetScroll(textPane, &topLineNum, &horizOffset);
2210         XtVaGetValues(textPane, XmNwidth, &width, textNmarginWidth, &margin,
2211                 NULL);
2212         if (leftX < margin + textD->lineNumLeft + textD->lineNumWidth)
2213             horizOffset -=
2214                     margin + textD->lineNumLeft + textD->lineNumWidth - leftX;
2215         else if (rightX > width - margin)
2216             horizOffset += rightX - (width - margin);
2217         TextSetScroll(textPane, topLineNum, horizOffset);
2218     }
2219 
2220     /* make sure that the statistics line is up to date */
2221     UpdateStatsLine(window);
2222 }
2223 
createTextArea(Widget parent,WindowInfo * window,int rows,int cols,int emTabDist,char * delimiters,int wrapMargin,int lineNumCols)2224 static Widget createTextArea(Widget parent, WindowInfo *window, int rows,
2225         int cols, int emTabDist, char *delimiters, int wrapMargin,
2226         int lineNumCols)
2227 {
2228     Widget text, sw, hScrollBar, vScrollBar, frame;
2229 
2230     /* Create a text widget inside of a scrolled window widget */
2231     sw = XtVaCreateManagedWidget("scrolledW", xmScrolledWindowWidgetClass,
2232             parent, XmNpaneMaximum, SHRT_MAX,
2233             XmNpaneMinimum, PANE_MIN_HEIGHT, XmNhighlightThickness, 0, NULL);
2234     hScrollBar = XtVaCreateManagedWidget("textHorScrollBar",
2235             xmScrollBarWidgetClass, sw, XmNorientation, XmHORIZONTAL,
2236             XmNrepeatDelay, 10, NULL);
2237     vScrollBar = XtVaCreateManagedWidget("textVertScrollBar",
2238             xmScrollBarWidgetClass, sw, XmNorientation, XmVERTICAL,
2239             XmNrepeatDelay, 10, NULL);
2240     frame = XtVaCreateManagedWidget("textFrame", xmFrameWidgetClass, sw,
2241             XmNshadowType, XmSHADOW_IN, NULL);
2242     text = XtVaCreateManagedWidget("text", textWidgetClass, frame,
2243             textNbacklightCharTypes, window->backlightCharTypes,
2244             textNrows, rows, textNcolumns, cols,
2245             textNlineNumCols, lineNumCols,
2246             textNemulateTabs, emTabDist,
2247             textNfont, GetDefaultFontStruct(TheDisplay, window->fontList),
2248             textNhScrollBar, hScrollBar, textNvScrollBar, vScrollBar,
2249             textNreadOnly, IS_ANY_LOCKED(window->lockReasons),
2250             textNwordDelimiters, delimiters,
2251             textNwrapMargin, wrapMargin,
2252             textNautoIndent, window->indentStyle == AUTO_INDENT,
2253             textNsmartIndent, window->indentStyle == SMART_INDENT,
2254             textNautoWrap, window->wrapMode == NEWLINE_WRAP,
2255             textNcontinuousWrap, window->wrapMode == CONTINUOUS_WRAP,
2256             textNoverstrike, window->overstrike,
2257             textNhidePointer, (Boolean) GetPrefTypingHidesPointer(),
2258             textNcursorVPadding, GetVerticalAutoScroll(),
2259             NULL);
2260 
2261     XtVaSetValues(sw, XmNworkWindow, frame, XmNhorizontalScrollBar,
2262                     hScrollBar, XmNverticalScrollBar, vScrollBar, NULL);
2263 
2264     /* add focus, drag, cursor tracking, and smart indent callbacks */
2265     XtAddCallback(text, textNfocusCallback, (XtCallbackProc)focusCB, window);
2266     XtAddCallback(text, textNcursorMovementCallback, (XtCallbackProc)movedCB,
2267             window);
2268     XtAddCallback(text, textNdragStartCallback, (XtCallbackProc)dragStartCB,
2269             window);
2270     XtAddCallback(text, textNdragEndCallback, (XtCallbackProc)dragEndCB,
2271             window);
2272     XtAddCallback(text, textNsmartIndentCallback, SmartIndentCB, window);
2273 
2274     /* This makes sure the text area initially has a the insert point shown
2275        ... (check if still true with the nedit text widget, probably not) */
2276     XmAddTabGroup(containingPane(text));
2277 
2278     /* compensate for Motif delete/backspace problem */
2279     RemapDeleteKey(text);
2280 
2281     /* Augment translation table for right button popup menu */
2282     AddBGMenuAction(text);
2283 
2284     /* If absolute line numbers will be needed for display in the statistics
2285        line, tell the widget to maintain them (otherwise, it's a costly
2286        operation and performance will be better without it) */
2287     TextDMaintainAbsLineNum(((TextWidget)text)->text.textD, window->showStats);
2288 
2289     return text;
2290 }
2291 
movedCB(Widget w,WindowInfo * window,XtPointer callData)2292 static void movedCB(Widget w, WindowInfo *window, XtPointer callData)
2293 {
2294     TextWidget textWidget = (TextWidget) w;
2295 
2296     if (window->ignoreModify)
2297         return;
2298 
2299     /* update line and column nubers in statistics line */
2300     UpdateStatsLine(window);
2301 
2302     /* Check the character before the cursor for matchable characters */
2303     FlashMatching(window, w);
2304 
2305     /* Check for changes to read-only status and/or file modifications */
2306     CheckForChangesToFile(window);
2307 
2308     /*  This callback is not only called for focussed panes, but for newly
2309         created panes as well. So make sure that the cursor is left alone
2310         for unfocussed panes.
2311         TextWidget have no state per se about focus, so we use the related
2312         ID for the blink procedure.  */
2313     if (0 != textWidget->text.cursorBlinkProcID)
2314     {
2315         /*  Start blinking the caret again.  */
2316         ResetCursorBlink(textWidget, False);
2317     }
2318 }
2319 
modifiedCB(int pos,int nInserted,int nDeleted,int nRestyled,const char * deletedText,void * cbArg)2320 static void modifiedCB(int pos, int nInserted, int nDeleted, int nRestyled,
2321         const char *deletedText, void *cbArg)
2322 {
2323     WindowInfo *window = (WindowInfo *)cbArg;
2324     int selected = window->buffer->primary.selected;
2325 
2326     /* update the table of bookmarks */
2327     if (!window->ignoreModify) {
2328         UpdateMarkTable(window, pos, nInserted, nDeleted);
2329     }
2330 
2331     /* Check and dim/undim selection related menu items */
2332     if ((window->wasSelected && !selected) ||
2333         (!window->wasSelected && selected)) {
2334     	window->wasSelected = selected;
2335 
2336 	/* do not refresh shell-level items (window, menu-bar etc)
2337 	   when motifying non-top document */
2338         if (IsTopDocument(window)) {
2339     	    XtSetSensitive(window->printSelItem, selected);
2340     	    XtSetSensitive(window->cutItem, selected);
2341     	    XtSetSensitive(window->copyItem, selected);
2342             XtSetSensitive(window->delItem, selected);
2343             /* Note we don't change the selection for items like
2344                "Open Selected" and "Find Selected".  That's because
2345                it works on selections in external applications.
2346                Desensitizing it if there's no NEdit selection
2347                disables this feature. */
2348 #ifndef VMS
2349             XtSetSensitive(window->filterItem, selected);
2350 #endif
2351 
2352             DimSelectionDepUserMenuItems(window, selected);
2353             if (window->replaceDlog != NULL && XtIsManaged(window->replaceDlog))
2354             {
2355         	UpdateReplaceActionButtons(window);
2356             }
2357 	}
2358     }
2359 
2360     /* When the program needs to make a change to a text area without without
2361        recording it for undo or marking file as changed it sets ignoreModify */
2362     if (window->ignoreModify || (nDeleted == 0 && nInserted == 0))
2363         return;
2364 
2365     /* Make sure line number display is sufficient for new data */
2366     updateLineNumDisp(window);
2367 
2368     /* Save information for undoing this operation (this call also counts
2369        characters and editing operations for triggering autosave */
2370     SaveUndoInformation(window, pos, nInserted, nDeleted, deletedText);
2371 
2372     /* Trigger automatic backup if operation or character limits reached */
2373     if (window->autoSave &&
2374             (window->autoSaveCharCount > AUTOSAVE_CHAR_LIMIT ||
2375              window->autoSaveOpCount > AUTOSAVE_OP_LIMIT)) {
2376         WriteBackupFile(window);
2377         window->autoSaveCharCount = 0;
2378         window->autoSaveOpCount = 0;
2379     }
2380 
2381     /* Indicate that the window has now been modified */
2382     SetWindowModified(window, TRUE);
2383 
2384     /* Update # of bytes, and line and col statistics */
2385     UpdateStatsLine(window);
2386 
2387     /* Check if external changes have been made to file and warn user */
2388     CheckForChangesToFile(window);
2389 }
2390 
focusCB(Widget w,WindowInfo * window,XtPointer callData)2391 static void focusCB(Widget w, WindowInfo *window, XtPointer callData)
2392 {
2393     /* record which window pane last had the keyboard focus */
2394     window->lastFocus = w;
2395 
2396     /* update line number statistic to reflect current focus pane */
2397     UpdateStatsLine(window);
2398 
2399     /* finish off the current incremental search */
2400     EndISearch(window);
2401 
2402     /* Check for changes to read-only status and/or file modifications */
2403     CheckForChangesToFile(window);
2404 }
2405 
dragStartCB(Widget w,WindowInfo * window,XtPointer callData)2406 static void dragStartCB(Widget w, WindowInfo *window, XtPointer callData)
2407 {
2408     /* don't record all of the intermediate drag steps for undo */
2409     window->ignoreModify = True;
2410 }
2411 
dragEndCB(Widget w,WindowInfo * window,dragEndCBStruct * callData)2412 static void dragEndCB(Widget w, WindowInfo *window, dragEndCBStruct *callData)
2413 {
2414     /* restore recording of undo information */
2415     window->ignoreModify = False;
2416 
2417     /* Do nothing if drag operation was canceled */
2418     if (callData->nCharsInserted == 0)
2419         return;
2420 
2421     /* Save information for undoing this operation not saved while
2422        undo recording was off */
2423     modifiedCB(callData->startPos, callData->nCharsInserted,
2424             callData->nCharsDeleted, 0, callData->deletedText, window);
2425 }
2426 
closeCB(Widget w,WindowInfo * window,XtPointer callData)2427 static void closeCB(Widget w, WindowInfo *window, XtPointer callData)
2428 {
2429     window = WidgetToWindow(w);
2430     if (!WindowCanBeClosed(window)) {
2431         return;
2432     }
2433 
2434     CloseDocumentWindow(w, window, callData);
2435 }
2436 
2437 #ifndef NO_SESSION_RESTART
saveYourselfCB(Widget w,Widget appShell,XtPointer callData)2438 static void saveYourselfCB(Widget w, Widget appShell, XtPointer callData)
2439 {
2440     WindowInfo *win, *topWin, **revWindowList;
2441     char geometry[MAX_GEOM_STRING_LEN];
2442     int argc = 0, maxArgc, nWindows, i;
2443     char **argv;
2444     int wasIconic = False;
2445     int n, nItems;
2446     WidgetList children;
2447 
2448     /* Allocate memory for an argument list and for a reversed list of
2449        windows.  The window list is reversed for IRIX 4DWM and any other
2450        window/session manager combination which uses window creation
2451        order for re-associating stored geometry information with
2452        new windows created by a restored application */
2453     maxArgc = 4;  /* nedit -server -svrname name */
2454     nWindows = 0;
2455     for (win=WindowList; win!=NULL; win=win->next) {
2456         maxArgc += 5;  /* -iconic -group -geometry WxH+x+y filename */
2457         nWindows++;
2458     }
2459     argv = (char **)NEditMalloc(maxArgc*sizeof(char *));
2460     revWindowList = (WindowInfo **)NEditMalloc(sizeof(WindowInfo *)*nWindows);
2461     for (win=WindowList, i=nWindows-1; win!=NULL; win=win->next, i--)
2462         revWindowList[i] = win;
2463 
2464     /* Create command line arguments for restoring each window in the list */
2465     argv[argc++] = NEditStrdup(ArgV0);
2466     if (IsServer) {
2467         argv[argc++] = NEditStrdup("-server");
2468         if (GetPrefServerName()[0] != '\0') {
2469             argv[argc++] = NEditStrdup("-svrname");
2470             argv[argc++] = NEditStrdup(GetPrefServerName());
2471         }
2472     }
2473 
2474     /* editor windows are popup-shell children of top-level appShell */
2475     XtVaGetValues(appShell, XmNchildren, &children,
2476     	    XmNnumChildren, &nItems, NULL);
2477 
2478     for (n=nItems-1; n>=0; n--) {
2479     	WidgetList tabs;
2480 	int tabCount;
2481 
2482 	if (strcmp(XtName(children[n]), "textShell") ||
2483 	  ((topWin = WidgetToWindow(children[n])) == NULL))
2484 	    continue;   /* skip non-editor windows */
2485 
2486 	/* create a group for each window */
2487         getGeometryString(topWin, geometry);
2488         argv[argc++] = NEditStrdup("-group");
2489         argv[argc++] = NEditStrdup("-geometry");
2490         argv[argc++] = NEditStrdup(geometry);
2491         if (IsIconic(topWin)) {
2492             argv[argc++] = NEditStrdup("-iconic");
2493             wasIconic = True;
2494         } else if (wasIconic) {
2495             argv[argc++] = NEditStrdup("-noiconic");
2496             wasIconic = False;
2497 	}
2498 
2499 	/* add filename of each tab in window... */
2500     	XtVaGetValues(topWin->tabBar, XmNtabWidgetList, &tabs,
2501             	XmNtabCount, &tabCount, NULL);
2502 
2503     	for (i=0; i< tabCount; i++) {
2504 	    win = TabToWindow(tabs[i]);
2505             if (win->filenameSet) {
2506 		/* add filename */
2507         	argv[argc] = (char*)NEditMalloc(strlen(win->path) +
2508                 	strlen(win->filename) + 1);
2509         	sprintf(argv[argc++], "%s%s", win->path, win->filename);
2510             }
2511 	}
2512     }
2513 
2514     NEditFree(revWindowList);
2515 
2516     /* Set the window's WM_COMMAND property to the created command line */
2517     XSetCommand(TheDisplay, XtWindow(appShell), argv, argc);
2518     for (i=0; i<argc; i++)
2519         NEditFree(argv[i]);
2520     NEditFree(argv);
2521 }
2522 
AttachSessionMgrHandler(Widget appShell)2523 void AttachSessionMgrHandler(Widget appShell)
2524 {
2525     static Atom wmpAtom, syAtom = 0;
2526 
2527     /* Add wm protocol callback for making nedit restartable by session
2528        managers.  Doesn't yet handle multiple-desktops or iconifying right. */
2529     if (syAtom == 0) {
2530         wmpAtom = XmInternAtom(TheDisplay, "WM_PROTOCOLS", FALSE);
2531         syAtom = XmInternAtom(TheDisplay, "WM_SAVE_YOURSELF", FALSE);
2532     }
2533     XmAddProtocolCallback(appShell, wmpAtom, syAtom,
2534             (XtCallbackProc)saveYourselfCB, (XtPointer)appShell);
2535 }
2536 #endif /* NO_SESSION_RESTART */
2537 
2538 /*
2539 ** Returns true if window is iconic (as determined by the WM_STATE property
2540 ** on the shell window.  I think this is the most reliable way to tell,
2541 ** but if someone has a better idea please send me a note).
2542 */
IsIconic(WindowInfo * window)2543 int IsIconic(WindowInfo *window)
2544 {
2545     unsigned long *property = NULL;
2546     unsigned long nItems;
2547     unsigned long leftover;
2548     static Atom wmStateAtom = 0;
2549     Atom actualType;
2550     int actualFormat;
2551     int result;
2552 
2553     if (wmStateAtom == 0)
2554         wmStateAtom = XInternAtom(XtDisplay(window->shell), "WM_STATE", False);
2555     if (XGetWindowProperty(XtDisplay(window->shell), XtWindow(window->shell),
2556             wmStateAtom, 0L, 1L, False, wmStateAtom, &actualType, &actualFormat,
2557             &nItems, &leftover, (unsigned char **)&property) != Success ||
2558             nItems != 1 || property == NULL)
2559         return FALSE;
2560     result = *property == IconicState;
2561     NEditFree(property);
2562     return result;
2563 }
2564 
2565 /*
2566 ** Add a window to the the window list.
2567 */
addToWindowList(WindowInfo * window)2568 static void addToWindowList(WindowInfo *window)
2569 {
2570     WindowInfo *temp;
2571 
2572     temp = WindowList;
2573     WindowList = window;
2574     window->next = temp;
2575 }
2576 
2577 /*
2578 ** Remove a window from the list of windows
2579 */
removeFromWindowList(WindowInfo * window)2580 static void removeFromWindowList(WindowInfo *window)
2581 {
2582     WindowInfo *temp;
2583 
2584     if (WindowList == window)
2585         WindowList = window->next;
2586     else {
2587         for (temp = WindowList; temp != NULL; temp = temp->next) {
2588             if (temp->next == window) {
2589                 temp->next = window->next;
2590                 break;
2591             }
2592         }
2593     }
2594 }
2595 
2596 /*
2597 **  Set the new gutter width in the window. Sadly, the only way to do this is
2598 **  to set it on every single document, so we have to iterate over them.
2599 **
2600 **  (Iteration taken from NDocuments(); is there a better way to do it?)
2601 */
updateGutterWidth(WindowInfo * window)2602 static int updateGutterWidth(WindowInfo* window)
2603 {
2604     WindowInfo* document;
2605     int reqCols = MIN_LINE_NUM_COLS;
2606     int newColsDiff = 0;
2607     int maxCols = 0;
2608 
2609     for (document = WindowList; NULL != document; document = document->next) {
2610         if (document->shell == window->shell) {
2611             /*  We found ourselves a document from this window.  */
2612             int lineNumCols, tmpReqCols;
2613             textDisp *textD = ((TextWidget) document->textArea)->text.textD;
2614 
2615             XtVaGetValues(document->textArea,
2616                     textNlineNumCols, &lineNumCols,
2617                     NULL);
2618 
2619             /* Is the width of the line number area sufficient to display all the
2620                line numbers in the file?  If not, expand line number field, and the
2621                window width. */
2622 
2623             if (lineNumCols > maxCols) {
2624                 maxCols = lineNumCols;
2625             }
2626 
2627             tmpReqCols = textD->nBufferLines < 1
2628                     ? 1
2629                     : (int) log10((double) textD->nBufferLines + 1) + 1;
2630 
2631             if (tmpReqCols > reqCols) {
2632                 reqCols = tmpReqCols;
2633             }
2634         }
2635     }
2636 
2637     if (reqCols != maxCols) {
2638         XFontStruct *fs;
2639         Dimension windowWidth;
2640         short fontWidth;
2641 
2642         newColsDiff = reqCols - maxCols;
2643 
2644         XtVaGetValues(window->textArea, textNfont, &fs, NULL);
2645         fontWidth = fs->max_bounds.width;
2646 
2647         XtVaGetValues(window->shell, XmNwidth, &windowWidth, NULL);
2648         XtVaSetValues(window->shell,
2649                 XmNwidth, (Dimension) windowWidth + (newColsDiff * fontWidth),
2650                 NULL);
2651 
2652         UpdateWMSizeHints(window);
2653     }
2654 
2655     for (document = WindowList; NULL != document; document = document->next) {
2656         if (document->shell == window->shell) {
2657             Widget text;
2658             int i;
2659             int lineNumCols;
2660 
2661             XtVaGetValues(document->textArea,
2662                 textNlineNumCols, &lineNumCols, NULL);
2663 
2664             if (lineNumCols == reqCols) {
2665                 continue;
2666             }
2667 
2668             /*  Update all panes of this document.  */
2669             for (i = 0; i <= document->nPanes; i++) {
2670                 text = 0==i ? document->textArea : document->textPanes[i-1];
2671                 XtVaSetValues(text, textNlineNumCols, reqCols, NULL);
2672             }
2673         }
2674     }
2675 
2676     return reqCols;
2677 }
2678 
2679 /*
2680 **  If necessary, enlarges the window and line number display area to make
2681 **  room for numbers.
2682 */
updateLineNumDisp(WindowInfo * window)2683 static int updateLineNumDisp(WindowInfo* window)
2684 {
2685     if (!window->showLineNumbers) {
2686         return 0;
2687     }
2688 
2689     /* Decide how wide the line number field has to be to display all
2690        possible line numbers */
2691     return updateGutterWidth(window);
2692 }
2693 
2694 /*
2695 ** Update the optional statistics line.
2696 */
UpdateStatsLine(WindowInfo * window)2697 void UpdateStatsLine(WindowInfo *window)
2698 {
2699     int line, pos, colNum;
2700     char *string, *format, slinecol[32];
2701     Widget statW = window->statsLine;
2702     XmString xmslinecol;
2703 #ifdef SGI_CUSTOM
2704     char *sleft, *smid, *sright;
2705 #endif
2706 
2707     if (!IsTopDocument(window))
2708       return;
2709 
2710     /* This routine is called for each character typed, so its performance
2711        affects overall editor perfomance.  Only update if the line is on. */
2712     if (!window->showStats)
2713         return;
2714 
2715     /* Compose the string to display. If line # isn't available, leave it off */
2716     pos = TextGetCursorPos(window->lastFocus);
2717     string = (char*)NEditMalloc(strlen(window->filename) + strlen(window->path) + 45);
2718     format = window->fileFormat == DOS_FILE_FORMAT ? " DOS" :
2719             (window->fileFormat == MAC_FILE_FORMAT ? " Mac" : "");
2720     if (!TextPosToLineAndCol(window->lastFocus, pos, &line, &colNum)) {
2721         sprintf(string, "%s%s%s %d bytes", window->path, window->filename,
2722                 format, window->buffer->length);
2723         sprintf(slinecol, "L: ---  C: ---");
2724     } else {
2725         sprintf(slinecol, "L: %d  C: %d", line, colNum);
2726         if (window->showLineNumbers)
2727             sprintf(string, "%s%s%s byte %d of %d", window->path,
2728                     window->filename, format, pos,
2729                     window->buffer->length);
2730         else
2731             sprintf(string, "%s%s%s %d bytes", window->path,
2732                     window->filename, format, window->buffer->length);
2733     }
2734 
2735     /* Update the line/column number */
2736     xmslinecol = XmStringCreateSimple(slinecol);
2737     XtVaSetValues( window->statsLineColNo,
2738             XmNlabelString, xmslinecol, NULL );
2739     XmStringFree(xmslinecol);
2740 
2741     /* Don't clobber the line if there's a special message being displayed */
2742     if (!window->modeMessageDisplayed) {
2743         /* Change the text in the stats line */
2744 #ifdef SGI_CUSTOM
2745         /* don't show full pathname, just dir and filename (+ byte info) */
2746         smid = strchr(string, '/');
2747         if ( smid != NULL ) {
2748             sleft = smid;
2749             sright = strrchr(string, '/');
2750             while (strcmp(smid, sright)) {
2751                     sleft = smid;
2752                     smid = strchr(sleft + 1, '/');
2753             }
2754             XmTextReplace(statW, 0, XmTextGetLastPosition(statW), sleft + 1);
2755         } else
2756             XmTextReplace(statW, 0, XmTextGetLastPosition(statW), string);
2757 #else
2758         XmTextReplace(statW, 0, XmTextGetLastPosition(statW), string);
2759 #endif
2760     }
2761     NEditFree(string);
2762 
2763     /* Update the line/col display */
2764     xmslinecol = XmStringCreateSimple(slinecol);
2765     XtVaSetValues(window->statsLineColNo,
2766             XmNlabelString, xmslinecol, NULL);
2767     XmStringFree(xmslinecol);
2768 }
2769 
2770 static Boolean currentlyBusy = False;
2771 static long busyStartTime = 0;
2772 static Boolean modeMessageSet = False;
2773 
2774 /*
2775  * Auxiliary function for measuring elapsed time during busy waits.
2776  */
getRelTimeInTenthsOfSeconds()2777 static long getRelTimeInTenthsOfSeconds()
2778 {
2779 #ifdef __unix__
2780     struct timeval current;
2781     gettimeofday(&current, NULL);
2782     return (current.tv_sec*10 + current.tv_usec/100000) & 0xFFFFFFFL;
2783 #else
2784     time_t current;
2785     time(&current);
2786     return (current*10) & 0xFFFFFFFL;
2787 #endif
2788 }
2789 
AllWindowsBusy(const char * message)2790 void AllWindowsBusy(const char *message)
2791 {
2792     WindowInfo *w;
2793 
2794     if (!currentlyBusy)
2795     {
2796 	busyStartTime = getRelTimeInTenthsOfSeconds();
2797 	modeMessageSet = False;
2798 
2799         for (w=WindowList; w!=NULL; w=w->next)
2800         {
2801             /* We don't the display message here yet, but defer it for
2802                a while. If the wait is short, we don't want
2803                to have it flash on and off the screen.  However,
2804                we can't use a time since in generally we are in
2805                a tight loop and only processing exposure events, so it's
2806                up to the caller to make sure that this routine is called
2807                at regular intervals.
2808             */
2809             BeginWait(w->shell);
2810         }
2811     } else if (!modeMessageSet && message &&
2812 		getRelTimeInTenthsOfSeconds() - busyStartTime > 10) {
2813 	/* Show the mode message when we've been busy for more than a second */
2814 	for (w=WindowList; w!=NULL; w=w->next) {
2815 	    SetModeMessage(w, message);
2816 	}
2817 	modeMessageSet = True;
2818     }
2819     BusyWait(WindowList->shell);
2820 
2821     currentlyBusy = True;
2822 }
2823 
AllWindowsUnbusy(void)2824 void AllWindowsUnbusy(void)
2825 {
2826     WindowInfo *w;
2827 
2828     for (w=WindowList; w!=NULL; w=w->next)
2829     {
2830         ClearModeMessage(w);
2831         EndWait(w->shell);
2832     }
2833 
2834     currentlyBusy = False;
2835     modeMessageSet = False;
2836     busyStartTime = 0;
2837 }
2838 
2839 /*
2840 ** Paned windows are impossible to adjust after they are created, which makes
2841 ** them nearly useless for NEdit (or any application which needs to dynamically
2842 ** adjust the panes) unless you tweek some private data to overwrite the
2843 ** desired and minimum pane heights which were set at creation time.  These
2844 ** will probably break in a future release of Motif because of dependence on
2845 ** private data.
2846 */
setPaneDesiredHeight(Widget w,int height)2847 static void setPaneDesiredHeight(Widget w, int height)
2848 {
2849     ((XmPanedWindowConstraintPtr)w->core.constraints)->panedw.dheight = height;
2850 }
setPaneMinHeight(Widget w,int min)2851 static void setPaneMinHeight(Widget w, int min)
2852 {
2853     ((XmPanedWindowConstraintPtr)w->core.constraints)->panedw.min = min;
2854 }
2855 
2856 /*
2857 ** Update the window manager's size hints.  These tell it the increments in
2858 ** which it is allowed to resize the window.  While this isn't particularly
2859 ** important for NEdit (since it can tolerate any window size), setting these
2860 ** hints also makes the resize indicator show the window size in characters
2861 ** rather than pixels, which is very helpful to users.
2862 */
UpdateWMSizeHints(WindowInfo * window)2863 void UpdateWMSizeHints(WindowInfo *window)
2864 {
2865     Dimension shellWidth, shellHeight, textHeight, hScrollBarHeight;
2866     int marginHeight, marginWidth, totalHeight, nCols, nRows;
2867     XFontStruct *fs;
2868     int i, baseWidth, baseHeight, fontHeight, fontWidth;
2869     Widget hScrollBar;
2870     textDisp *textD = ((TextWidget)window->textArea)->text.textD;
2871 
2872     /* Find the dimensions of a single character of the text font */
2873     XtVaGetValues(window->textArea, textNfont, &fs, NULL);
2874     fontHeight = textD->ascent + textD->descent;
2875     fontWidth = fs->max_bounds.width;
2876 
2877     /* Find the base (non-expandable) width and height of the editor window.
2878 
2879        FIXME:
2880        To workaround the shrinking-window bug on some WM such as Metacity,
2881        which caused the window to shrink as we switch between documents
2882        using different font sizes on the documents in the same window, the
2883        base width, and similarly the base height, is ajusted such that:
2884             shellWidth = baseWidth + cols * textWidth
2885        There are two issues with this workaround:
2886        1. the right most characters may appear partially obsure
2887        2. the Col x Row info reported by the WM will be based on the fully
2888           display text.
2889     */
2890     XtVaGetValues(window->textArea, XmNheight, &textHeight,
2891             textNmarginHeight, &marginHeight, textNmarginWidth, &marginWidth,
2892             NULL);
2893     totalHeight = textHeight - 2*marginHeight;
2894     for (i=0; i<window->nPanes; i++) {
2895         XtVaGetValues(window->textPanes[i], XmNheight, &textHeight,
2896                 textNhScrollBar, &hScrollBar, NULL);
2897         totalHeight += textHeight - 2*marginHeight;
2898         if (!XtIsManaged(hScrollBar)) {
2899             XtVaGetValues(hScrollBar, XmNheight, &hScrollBarHeight, NULL);
2900             totalHeight -= hScrollBarHeight;
2901         }
2902     }
2903 
2904     XtVaGetValues(window->shell, XmNwidth, &shellWidth,
2905             XmNheight, &shellHeight, NULL);
2906     nCols = textD->width / fontWidth;
2907     nRows = totalHeight / fontHeight;
2908     baseWidth = shellWidth - nCols * fontWidth;
2909     baseHeight = shellHeight - nRows * fontHeight;
2910 
2911     /* Set the size hints in the shell widget */
2912     XtVaSetValues(window->shell, XmNwidthInc, fs->max_bounds.width,
2913             XmNheightInc, fontHeight,
2914             XmNbaseWidth, baseWidth, XmNbaseHeight, baseHeight,
2915             XmNminWidth, baseWidth + fontWidth,
2916             XmNminHeight, baseHeight + (1+window->nPanes) * fontHeight, NULL);
2917 
2918     /* Motif will keep placing this on the shell every time we change it,
2919        so it needs to be undone every single time.  This only seems to
2920        happen on mult-head dispalys on screens 1 and higher. */
2921 
2922     RemovePPositionHint(window->shell);
2923 }
2924 
2925 /*
2926 ** Update the minimum allowable height for a split pane after a change
2927 ** to font or margin height.
2928 */
UpdateMinPaneHeights(WindowInfo * window)2929 void UpdateMinPaneHeights(WindowInfo *window)
2930 {
2931     textDisp *textD = ((TextWidget)window->textArea)->text.textD;
2932     Dimension hsbHeight, swMarginHeight,frameShadowHeight;
2933     int i, marginHeight, minPaneHeight;
2934     Widget hScrollBar;
2935 
2936     /* find the minimum allowable size for a pane */
2937     XtVaGetValues(window->textArea, textNhScrollBar, &hScrollBar, NULL);
2938     XtVaGetValues(containingPane(window->textArea),
2939             XmNscrolledWindowMarginHeight, &swMarginHeight, NULL);
2940     XtVaGetValues(XtParent(window->textArea),
2941             XmNshadowThickness, &frameShadowHeight, NULL);
2942     XtVaGetValues(window->textArea, textNmarginHeight, &marginHeight, NULL);
2943     XtVaGetValues(hScrollBar, XmNheight, &hsbHeight, NULL);
2944     minPaneHeight = textD->ascent + textD->descent + marginHeight*2 +
2945             swMarginHeight*2 + hsbHeight + 2*frameShadowHeight;
2946 
2947     /* Set it in all of the widgets in the window */
2948     setPaneMinHeight(containingPane(window->textArea), minPaneHeight);
2949     for (i=0; i<window->nPanes; i++)
2950         setPaneMinHeight(containingPane(window->textPanes[i]),
2951                    minPaneHeight);
2952 }
2953 
2954 /* Add an icon to an applicaction shell widget.  addWindowIcon adds a large
2955 ** (primary window) icon, AddSmallIcon adds a small (secondary window) icon.
2956 **
2957 ** Note: I would prefer that these were not hardwired, but yhere is something
2958 ** weird about the  XmNiconPixmap resource that prevents it from being set
2959 ** from the defaults in the application resource database.
2960 */
addWindowIcon(Widget shell)2961 static void addWindowIcon(Widget shell)
2962 {
2963     static Pixmap iconPixmap = 0, maskPixmap = 0;
2964 
2965     if (iconPixmap == 0) {
2966         iconPixmap = XCreateBitmapFromData(TheDisplay,
2967                 RootWindowOfScreen(XtScreen(shell)), (char *)iconBits,
2968                 iconBitmapWidth, iconBitmapHeight);
2969         maskPixmap = XCreateBitmapFromData(TheDisplay,
2970                 RootWindowOfScreen(XtScreen(shell)), (char *)maskBits,
2971                 iconBitmapWidth, iconBitmapHeight);
2972     }
2973     XtVaSetValues(shell, XmNiconPixmap, iconPixmap, XmNiconMask, maskPixmap,
2974             NULL);
2975 }
AddSmallIcon(Widget shell)2976 void AddSmallIcon(Widget shell)
2977 {
2978     static Pixmap iconPixmap = 0, maskPixmap = 0;
2979 
2980     if (iconPixmap == 0) {
2981         iconPixmap = XCreateBitmapFromData(TheDisplay,
2982                 RootWindowOfScreen(XtScreen(shell)), (char *)n_bits,
2983                 n_width, n_height);
2984         maskPixmap = XCreateBitmapFromData(TheDisplay,
2985                 RootWindowOfScreen(XtScreen(shell)), (char *)n_mask,
2986                 n_width, n_height);
2987     }
2988     XtVaSetValues(shell, XmNiconPixmap, iconPixmap,
2989             XmNiconMask, maskPixmap, NULL);
2990 }
2991 
2992 /*
2993 ** Create pixmap per the widget's color depth setting.
2994 **
2995 ** This fixes a BadMatch (X_CopyArea) error due to mismatching of
2996 ** color depth between the bitmap (depth of 1) and the screen,
2997 ** specifically on when linked to LessTif v1.2 (release 0.93.18
2998 ** & 0.93.94 tested).  LessTif v2.x showed no such problem.
2999 */
createBitmapWithDepth(Widget w,char * data,unsigned int width,unsigned int height)3000 static Pixmap createBitmapWithDepth(Widget w, char *data, unsigned int width,
3001 	unsigned int height)
3002 {
3003     Pixmap pixmap;
3004     Pixel fg, bg;
3005     int depth;
3006 
3007     XtVaGetValues (w, XmNforeground, &fg, XmNbackground, &bg,
3008 	    XmNdepth, &depth, NULL);
3009     pixmap = XCreatePixmapFromBitmapData(XtDisplay(w),
3010             RootWindowOfScreen(XtScreen(w)), (char *)data,
3011             width, height, fg, bg, depth);
3012 
3013     return pixmap;
3014 }
3015 
3016 /*
3017 ** Save the position and size of a window as an X standard geometry string.
3018 ** A string of at least MAX_GEOMETRY_STRING_LEN characters should be
3019 ** provided in the argument "geomString" to receive the result.
3020 */
getGeometryString(WindowInfo * window,char * geomString)3021 static void getGeometryString(WindowInfo *window, char *geomString)
3022 {
3023     int x, y, fontWidth, fontHeight, baseWidth, baseHeight;
3024     unsigned int width, height, dummyW, dummyH, bw, depth, nChild;
3025     Window parent, root, *child, w = XtWindow(window->shell);
3026     Display *dpy = XtDisplay(window->shell);
3027 
3028     /* Find the width and height from the window of the shell */
3029     XGetGeometry(dpy, w, &root, &x, &y, &width, &height, &bw, &depth);
3030 
3031     /* Find the top left corner (x and y) of the window decorations.  (This
3032        is what's required in the geometry string to restore the window to it's
3033        original position, since the window manager re-parents the window to
3034        add it's title bar and menus, and moves the requested window down and
3035        to the left.)  The position is found by traversing the window hier-
3036        archy back to the window to the last parent before the root window */
3037     for(;;) {
3038         XQueryTree(dpy, w, &root, &parent,  &child, &nChild);
3039         XFree((char*)child);
3040         if (parent == root)
3041             break;
3042         w = parent;
3043     }
3044     XGetGeometry(dpy, w, &root, &x, &y, &dummyW, &dummyH, &bw, &depth);
3045 
3046     /* Use window manager size hints (set by UpdateWMSizeHints) to
3047        translate the width and height into characters, as opposed to pixels */
3048     XtVaGetValues(window->shell, XmNwidthInc, &fontWidth,
3049             XmNheightInc, &fontHeight, XmNbaseWidth, &baseWidth,
3050             XmNbaseHeight, &baseHeight, NULL);
3051     width = (width-baseWidth) / fontWidth;
3052     height = (height-baseHeight) / fontHeight;
3053 
3054     /* Write the string */
3055     CreateGeometryString(geomString, x, y, width, height,
3056                 XValue | YValue | WidthValue | HeightValue);
3057 }
3058 
3059 /*
3060 ** Xt timer procedure for updating size hints.  The new sizes of objects in
3061 ** the window are not ready immediately after adding or removing panes.  This
3062 ** is a timer routine to be invoked with a timeout of 0 to give the event
3063 ** loop a chance to finish processing the size changes before reading them
3064 ** out for setting the window manager size hints.
3065 */
wmSizeUpdateProc(XtPointer clientData,XtIntervalId * id)3066 static void wmSizeUpdateProc(XtPointer clientData, XtIntervalId *id)
3067 {
3068     UpdateWMSizeHints((WindowInfo *)clientData);
3069 }
3070 
3071 #ifdef ROWCOLPATCH
3072 /*
3073 ** There is a bad memory reference in the delete_child method of the
3074 ** RowColumn widget in some Motif versions (so far, just Solaris with Motif
3075 ** 1.2.3) which appears durring the phase 2 destroy of the widget. This
3076 ** patch replaces the method with a call to the Composite widget's
3077 ** delete_child method.  The composite delete_child method handles part,
3078 ** but not all of what would have been done by the original method, meaning
3079 ** that this is dangerous and should be used sparingly.  Note that
3080 ** patchRowCol is called only in CloseWindow, before the widget is about to
3081 ** be destroyed, and only on systems where the bug has been observed
3082 */
patchRowCol(Widget w)3083 static void patchRowCol(Widget w)
3084 {
3085     ((XmRowColumnClassRec *)XtClass(w))->composite_class.delete_child =
3086             patchedRemoveChild;
3087 }
patchedRemoveChild(Widget child)3088 static void patchedRemoveChild(Widget child)
3089 {
3090     /* Call composite class method instead of broken row col delete_child
3091        method */
3092     (*((CompositeWidgetClass)compositeWidgetClass)->composite_class.
3093                 delete_child) (child);
3094 }
3095 #endif /* ROWCOLPATCH */
3096 
3097 /*
3098 ** Set the backlight character class string
3099 */
SetBacklightChars(WindowInfo * window,char * applyBacklightTypes)3100 void SetBacklightChars(WindowInfo *window, char *applyBacklightTypes)
3101 {
3102     int i;
3103     int is_applied = XmToggleButtonGetState(window->backlightCharsItem) ? 1 : 0;
3104     int do_apply = applyBacklightTypes ? 1 : 0;
3105 
3106     window->backlightChars = do_apply;
3107 
3108     NEditFree(window->backlightCharTypes);
3109     if (window->backlightChars &&
3110       (window->backlightCharTypes = (char*)NEditMalloc(strlen(applyBacklightTypes)+1)))
3111       strcpy(window->backlightCharTypes, applyBacklightTypes);
3112     else
3113       window->backlightCharTypes = NULL;
3114 
3115     XtVaSetValues(window->textArea,
3116           textNbacklightCharTypes, window->backlightCharTypes, NULL);
3117     for (i=0; i<window->nPanes; i++)
3118       XtVaSetValues(window->textPanes[i],
3119               textNbacklightCharTypes, window->backlightCharTypes, NULL);
3120     if (is_applied != do_apply)
3121       SetToggleButtonState(window, window->backlightCharsItem, do_apply, False);
3122 }
3123 
3124 /*
3125 ** perform generic management on the children (toolbars) of toolBarsForm,
3126 ** a.k.a. statsForm, by setting the form attachment of the managed child
3127 ** widgets per their position/order.
3128 **
3129 ** You can optionally create separator after a toolbar widget with it's
3130 ** widget name set to "TOOLBAR_SEP", which will appear below the toolbar
3131 ** widget. These seperators will then be managed automatically by this
3132 ** routine along with the toolbars they 'attached' to.
3133 **
3134 ** It also takes care of the attachment offset settings of the child
3135 ** widgets to keep the border lines of the parent form displayed, so
3136 ** you don't have set them before hand.
3137 **
3138 ** Note: XtManage/XtUnmange the target child (toolbar) before calling this
3139 **       function.
3140 **
3141 ** Returns the last toolbar widget managed.
3142 **
3143 */
manageToolBars(Widget toolBarsForm)3144 static Widget manageToolBars(Widget toolBarsForm)
3145 {
3146     Widget topWidget = NULL;
3147     WidgetList children;
3148     int n, nItems=0;
3149 
3150     XtVaGetValues(toolBarsForm, XmNchildren, &children,
3151     	    XmNnumChildren, &nItems, NULL);
3152 
3153     for (n=0; n<nItems; n++) {
3154     	Widget tbar = children[n];
3155 
3156     	if (XtIsManaged(tbar)) {
3157 	    if (topWidget) {
3158 		XtVaSetValues(tbar, XmNtopAttachment, XmATTACH_WIDGET,
3159 			XmNtopWidget, topWidget,
3160 		    	XmNbottomAttachment, XmATTACH_NONE,
3161 	    	    	XmNleftOffset, STAT_SHADOW_THICKNESS,
3162 	    	    	XmNrightOffset, STAT_SHADOW_THICKNESS,
3163 			NULL);
3164 	    }
3165 	    else {
3166 	    	/* the very first toolbar on top */
3167 	    	XtVaSetValues(tbar, XmNtopAttachment, XmATTACH_FORM,
3168 		    	XmNbottomAttachment, XmATTACH_NONE,
3169 	    	    	XmNleftOffset, STAT_SHADOW_THICKNESS,
3170 	    	    	XmNtopOffset, STAT_SHADOW_THICKNESS,
3171 	    	    	XmNrightOffset, STAT_SHADOW_THICKNESS,
3172 			NULL);
3173 	    }
3174 
3175 	    topWidget = tbar;
3176 
3177 	    /* if the next widget is a separator, turn it on */
3178 	    if (n+1<nItems && !strcmp(XtName(children[n+1]), "TOOLBAR_SEP")) {
3179     	    	XtManageChild(children[n+1]);
3180 	    }
3181 	}
3182 	else {
3183 	    /* Remove top attachment to widget to avoid circular dependency.
3184 	       Attach bottom to form so that when the widget is redisplayed
3185 	       later, it will trigger the parent form to resize properly as
3186 	       if the widget is being inserted */
3187 	    XtVaSetValues(tbar, XmNtopAttachment, XmATTACH_NONE,
3188 		    XmNbottomAttachment, XmATTACH_FORM, NULL);
3189 
3190 	    /* if the next widget is a separator, turn it off */
3191 	    if (n+1<nItems && !strcmp(XtName(children[n+1]), "TOOLBAR_SEP")) {
3192     	    	XtUnmanageChild(children[n+1]);
3193 	    }
3194 	}
3195     }
3196 
3197     if (topWidget) {
3198     	if (strcmp(XtName(topWidget), "TOOLBAR_SEP")) {
3199 	    XtVaSetValues(topWidget,
3200 		    XmNbottomAttachment, XmATTACH_FORM,
3201 		    XmNbottomOffset, STAT_SHADOW_THICKNESS,
3202 		    NULL);
3203 	}
3204 	else {
3205 	    /* is a separator */
3206 	    Widget wgt;
3207 	    XtVaGetValues(topWidget, XmNtopWidget, &wgt, NULL);
3208 
3209 	    /* don't need sep below bottom-most toolbar */
3210 	    XtUnmanageChild(topWidget);
3211 	    XtVaSetValues(wgt,
3212 		    XmNbottomAttachment, XmATTACH_FORM,
3213 		    XmNbottomOffset, STAT_SHADOW_THICKNESS,
3214 		    NULL);
3215 	}
3216     }
3217 
3218     return topWidget;
3219 }
3220 
3221 /*
3222 ** Calculate the dimension of the text area, in terms of rows & cols,
3223 ** as if there's only one single text pane in the window.
3224 */
getTextPaneDimension(WindowInfo * window,int * nRows,int * nCols)3225 static void getTextPaneDimension(WindowInfo *window, int *nRows, int *nCols)
3226 {
3227     Widget hScrollBar;
3228     Dimension hScrollBarHeight, paneHeight;
3229     int marginHeight, marginWidth, totalHeight, fontHeight;
3230     textDisp *textD = ((TextWidget)window->textArea)->text.textD;
3231 
3232     /* width is the same for panes */
3233     XtVaGetValues(window->textArea, textNcolumns, nCols, NULL);
3234 
3235     /* we have to work out the height, as the text area may have been split */
3236     XtVaGetValues(window->textArea, textNhScrollBar, &hScrollBar,
3237             textNmarginHeight, &marginHeight, textNmarginWidth, &marginWidth,
3238 	    NULL);
3239     XtVaGetValues(hScrollBar, XmNheight, &hScrollBarHeight, NULL);
3240     XtVaGetValues(window->splitPane, XmNheight, &paneHeight, NULL);
3241     totalHeight = paneHeight - 2*marginHeight -hScrollBarHeight;
3242     fontHeight = textD->ascent + textD->descent;
3243     *nRows = totalHeight/fontHeight;
3244 }
3245 
3246 /*
3247 ** Create a new document in the shell window.
3248 ** Document are created in 'background' so that the user
3249 ** menus, ie. the Macro/Shell/BG menus, will not be updated
3250 ** unnecessarily; hence speeding up the process of opening
3251 ** multiple files.
3252 */
CreateDocument(WindowInfo * shellWindow,const char * name)3253 WindowInfo* CreateDocument(WindowInfo* shellWindow, const char* name)
3254 {
3255     Widget pane, text;
3256     WindowInfo *window;
3257     int nCols, nRows;
3258 
3259     /* Allocate some memory for the new window data structure */
3260     window = (WindowInfo *)NEditMalloc(sizeof(WindowInfo));
3261 
3262     /* inherit settings and later reset those required */
3263     memcpy(window, shellWindow, sizeof(WindowInfo));
3264 
3265 #if 0
3266     /* share these dialog items with parent shell */
3267     window->replaceDlog = NULL;
3268     window->replaceText = NULL;
3269     window->replaceWithText = NULL;
3270     window->replaceWordToggle = NULL;
3271     window->replaceCaseToggle = NULL;
3272     window->replaceRegexToggle = NULL;
3273     window->findDlog = NULL;
3274     window->findText = NULL;
3275     window->findWordToggle = NULL;
3276     window->findCaseToggle = NULL;
3277     window->findRegexToggle = NULL;
3278     window->replaceMultiFileDlog = NULL;
3279     window->replaceMultiFilePathBtn = NULL;
3280     window->replaceMultiFileList = NULL;
3281     window->showLineNumbers = GetPrefLineNums();
3282     window->showStats = GetPrefStatsLine();
3283     window->showISearchLine = GetPrefISearchLine();
3284 #endif
3285 
3286     window->multiFileReplSelected = FALSE;
3287     window->multiFileBusy = FALSE;
3288     window->writableWindows = NULL;
3289     window->nWritableWindows = 0;
3290     window->fileChanged = FALSE;
3291     window->fileMissing = True;
3292     window->fileMode = 0;
3293     window->fileUid = 0;
3294     window->fileGid = 0;
3295     window->filenameSet = FALSE;
3296     window->fileFormat = UNIX_FILE_FORMAT;
3297     window->lastModTime = 0;
3298     strcpy(window->filename, name);
3299     window->undo = NULL;
3300     window->redo = NULL;
3301     window->nPanes = 0;
3302     window->autoSaveCharCount = 0;
3303     window->autoSaveOpCount = 0;
3304     window->undoOpCount = 0;
3305     window->undoMemUsed = 0;
3306     CLEAR_ALL_LOCKS(window->lockReasons);
3307     window->indentStyle = GetPrefAutoIndent(PLAIN_LANGUAGE_MODE);
3308     window->autoSave = GetPrefAutoSave();
3309     window->saveOldVersion = GetPrefSaveOldVersion();
3310     window->wrapMode = GetPrefWrap(PLAIN_LANGUAGE_MODE);
3311     window->overstrike = False;
3312     window->showMatchingStyle = GetPrefShowMatching();
3313     window->matchSyntaxBased = GetPrefMatchSyntaxBased();
3314     window->highlightSyntax = GetPrefHighlightSyntax();
3315     window->backlightCharTypes = NULL;
3316     window->backlightChars = GetPrefBacklightChars();
3317     if (window->backlightChars) {
3318       char *cTypes = GetPrefBacklightCharTypes();
3319       if (cTypes && window->backlightChars) {
3320           if ((window->backlightCharTypes = (char*)NEditMalloc(strlen(cTypes) + 1)))
3321               strcpy(window->backlightCharTypes, cTypes);
3322       }
3323     }
3324     window->modeMessageDisplayed = FALSE;
3325     window->modeMessage = NULL;
3326     window->ignoreModify = FALSE;
3327     window->windowMenuValid = FALSE;
3328     window->flashTimeoutID = 0;
3329     window->fileClosedAtom = None;
3330     window->wasSelected = FALSE;
3331     strcpy(window->fontName, GetPrefFontName());
3332     strcpy(window->italicFontName, GetPrefItalicFontName());
3333     strcpy(window->boldFontName, GetPrefBoldFontName());
3334     strcpy(window->boldItalicFontName, GetPrefBoldItalicFontName());
3335     window->colorDialog = NULL;
3336     window->fontList = GetPrefFontList();
3337     window->italicFontStruct = GetPrefItalicFont();
3338     window->boldFontStruct = GetPrefBoldFont();
3339     window->boldItalicFontStruct = GetPrefBoldItalicFont();
3340     window->fontDialog = NULL;
3341     window->nMarks = 0;
3342     window->markTimeoutID = 0;
3343     window->highlightData = NULL;
3344     window->shellCmdData = NULL;
3345     window->macroCmdData = NULL;
3346     window->smartIndentData = NULL;
3347     window->languageMode = PLAIN_LANGUAGE_MODE;
3348     window->iSearchHistIndex = 0;
3349     window->iSearchStartPos = -1;
3350     window->replaceLastRegexCase   = TRUE;
3351     window->replaceLastLiteralCase = FALSE;
3352     window->iSearchLastRegexCase   = TRUE;
3353     window->iSearchLastLiteralCase = FALSE;
3354     window->findLastRegexCase      = TRUE;
3355     window->findLastLiteralCase    = FALSE;
3356     window->tab = NULL;
3357     window->bgMenuUndoItem = NULL;
3358     window->bgMenuRedoItem = NULL;
3359     window->device = 0;
3360     window->inode = 0;
3361 
3362     if (window->fontList == NULL)
3363         XtVaGetValues(shellWindow->statsLine, XmNfontList,
3364     	    	&window->fontList,NULL);
3365 
3366     getTextPaneDimension(shellWindow, &nRows, &nCols);
3367 
3368     /* Create pane that actaully holds the new document. As
3369        document is created in 'background', we need to hide
3370        it. If we leave it unmanaged without setting it to
3371        the XmNworkWindow of the mainWin, due to a unknown
3372        bug in Motif where splitpane's scrollWindow child
3373        somehow came up with a height taller than the splitpane,
3374        the bottom part of the text editing widget is obstructed
3375        when later brought up by  RaiseDocument(). So we first
3376        manage it hidden, then unmanage it and reset XmNworkWindow,
3377        then let RaiseDocument() show it later. */
3378     pane = XtVaCreateWidget("pane",
3379     	    xmPanedWindowWidgetClass, window->mainWin,
3380     	    XmNmarginWidth, 0, XmNmarginHeight, 0, XmNseparatorOn, False,
3381     	    XmNspacing, 3, XmNsashIndent, -2,
3382 	    XmNmappedWhenManaged, False,
3383 	    NULL);
3384     XtVaSetValues(window->mainWin, XmNworkWindow, pane, NULL);
3385     XtManageChild(pane);
3386     window->splitPane = pane;
3387 
3388     /* Store a copy of document/window pointer in text pane to support
3389        action procedures. See also WidgetToWindow() for info. */
3390     XtVaSetValues(pane, XmNuserData, window, NULL);
3391 
3392     /* Patch around Motif's most idiotic "feature", that its menu accelerators
3393        recognize Caps Lock and Num Lock as modifiers, and don't trigger if
3394        they are engaged */
3395     AccelLockBugPatch(pane, window->menuBar);
3396 
3397     /* Create the first, and most permanent text area (other panes may
3398        be added & removed, but this one will never be removed */
3399     text = createTextArea(pane, window, nRows, nCols,
3400     	    GetPrefEmTabDist(PLAIN_LANGUAGE_MODE), GetPrefDelimiters(),
3401 	    GetPrefWrapMargin(), window->showLineNumbers?MIN_LINE_NUM_COLS:0);
3402     XtManageChild(text);
3403     window->textArea = text;
3404     window->lastFocus = text;
3405 
3406     /* Set the initial colors from the globals. */
3407     SetColors(window,
3408               GetPrefColorName(TEXT_FG_COLOR  ),
3409               GetPrefColorName(TEXT_BG_COLOR  ),
3410               GetPrefColorName(SELECT_FG_COLOR),
3411               GetPrefColorName(SELECT_BG_COLOR),
3412               GetPrefColorName(HILITE_FG_COLOR),
3413               GetPrefColorName(HILITE_BG_COLOR),
3414               GetPrefColorName(LINENO_FG_COLOR),
3415               GetPrefColorName(CURSOR_FG_COLOR));
3416 
3417     /* Create the right button popup menu (note: order is important here,
3418        since the translation for popping up this menu was probably already
3419        added in createTextArea, but CreateBGMenu requires window->textArea
3420        to be set so it can attach the menu to it (because menu shells are
3421        finicky about the kinds of widgets they are attached to)) */
3422     window->bgMenuPane = CreateBGMenu(window);
3423 
3424     /* cache user menus: init. user background menu cache */
3425     InitUserBGMenuCache(&window->userBGMenuCache);
3426 
3427     /* Create the text buffer rather than using the one created automatically
3428        with the text area widget.  This is done so the syntax highlighting
3429        modify callback can be called to synchronize the style buffer BEFORE
3430        the text display's callback is called upon to display a modification */
3431     window->buffer = BufCreate();
3432     BufAddModifyCB(window->buffer, SyntaxHighlightModifyCB, window);
3433 
3434     /* Attach the buffer to the text widget, and add callbacks for modify */
3435     TextSetBuffer(text, window->buffer);
3436     BufAddModifyCB(window->buffer, modifiedCB, window);
3437 
3438     /* Designate the permanent text area as the owner for selections */
3439     HandleXSelections(text);
3440 
3441     /* Set the requested hardware tab distance and useTabs in the text buffer */
3442     BufSetTabDistance(window->buffer, GetPrefTabDist(PLAIN_LANGUAGE_MODE));
3443     window->buffer->useTabs = GetPrefInsertTabs();
3444     window->tab = addTab(window->tabBar, name);
3445 
3446     /* add the window to the global window list, update the Windows menus */
3447     InvalidateWindowMenus();
3448     addToWindowList(window);
3449 
3450 #ifdef LESSTIF_VERSION
3451     /* FIXME: Temporary workaround for disappearing-text-window bug
3452               when linking to Lesstif.
3453 
3454        After changes is made to statsAreaForm (parent of statsline,
3455        i-search line and tab bar) widget such as enabling/disabling
3456        the statsline, the XmForm widget enclosing the text widget
3457        somehow refused to resize to fit the text widget. Resizing
3458        the shell window or making changes [again] to the statsAreaForm
3459        appeared to bring out the text widget, though doesn't fix it for
3460        the subsequently added documents. Here we try to do the latter
3461        for all new documents created. */
3462     if (XtIsManaged(XtParent(window->statsLineForm))) {
3463     	XtUnmanageChild(XtParent(window->statsLineForm));
3464     	XtManageChild(XtParent(window->statsLineForm));
3465     }
3466 #endif /* LESSTIF_VERSION */
3467 
3468     /* return the shell ownership to previous tabbed doc */
3469     XtVaSetValues(window->mainWin, XmNworkWindow, shellWindow->splitPane, NULL);
3470     XLowerWindow(TheDisplay, XtWindow(window->splitPane));
3471     XtUnmanageChild(window->splitPane);
3472     XtVaSetValues(window->splitPane, XmNmappedWhenManaged, True, NULL);
3473 
3474     return window;
3475 }
3476 
3477 /*
3478 ** return the next/previous docment on the tab list.
3479 **
3480 ** If <wrap> is true then the next tab of the rightmost tab will be the
3481 ** second tab from the right, and the the previous tab of the leftmost
3482 ** tab will be the second from the left.  This is useful for getting
3483 ** the next tab after a tab detaches/closes and you don't want to wrap around.
3484 */
getNextTabWindow(WindowInfo * window,int direction,int crossWin,int wrap)3485 static WindowInfo *getNextTabWindow(WindowInfo *window, int direction,
3486         int crossWin, int wrap)
3487 {
3488     WidgetList tabList, tabs;
3489     WindowInfo *win;
3490     int tabCount, tabTotalCount;
3491     int tabPos, nextPos;
3492     int i, n;
3493     int nBuf = crossWin? NWindows() : NDocuments(window);
3494 
3495     if (nBuf <= 1)
3496     	return NULL;
3497 
3498     /* get the list of tabs */
3499     tabs = (WidgetList)NEditMalloc(sizeof(Widget) * nBuf);
3500     tabTotalCount = 0;
3501     if (crossWin) {
3502 	int n, nItems;
3503 	WidgetList children;
3504 
3505 	XtVaGetValues(TheAppShell, XmNchildren, &children,
3506     		XmNnumChildren, &nItems, NULL);
3507 
3508 	/* get list of tabs in all windows */
3509     	for (n=0; n<nItems; n++) {
3510 	    if (strcmp(XtName(children[n]), "textShell") ||
3511 	      ((win = WidgetToWindow(children[n])) == NULL))
3512 	    	continue;   /* skip non-text-editor windows */
3513 
3514     	    XtVaGetValues(win->tabBar, XmNtabWidgetList, &tabList,
3515             	    XmNtabCount, &tabCount, NULL);
3516 
3517     	    for (i=0; i< tabCount; i++) {
3518 	    	tabs[tabTotalCount++] = tabList[i];
3519 	    }
3520 	}
3521     }
3522     else {
3523 	/* get list of tabs in this window */
3524     	XtVaGetValues(window->tabBar, XmNtabWidgetList, &tabList,
3525             	XmNtabCount, &tabCount, NULL);
3526 
3527     	for (i=0; i< tabCount; i++) {
3528 	    if (TabToWindow(tabList[i]))    /* make sure tab is valid */
3529 	    	tabs[tabTotalCount++] = tabList[i];
3530 	}
3531     }
3532 
3533     /* find the position of the tab in the tablist */
3534     tabPos = 0;
3535     for (n=0; n<tabTotalCount; n++) {
3536     	if (tabs[n] == window->tab) {
3537 	    tabPos = n;
3538 	    break;
3539 	}
3540     }
3541 
3542     /* calculate index position of next tab */
3543     nextPos = tabPos + direction;
3544     if (nextPos >= nBuf) {
3545         if (wrap)
3546             nextPos = 0;
3547         else
3548             nextPos = nBuf - 2;
3549     } else if (nextPos < 0) {
3550         if (wrap)
3551     	    nextPos = nBuf - 1;
3552         else
3553             nextPos = 1;
3554     }
3555 
3556     /* return the document where the next tab belongs to */
3557     win = TabToWindow(tabs[nextPos]);
3558     NEditFree(tabs);
3559     return win;
3560 }
3561 
3562 /*
3563 ** return the integer position of a tab in the tabbar it
3564 ** belongs to, or -1 if there's an error, somehow.
3565 */
getTabPosition(Widget tab)3566 static int getTabPosition(Widget tab)
3567 {
3568     WidgetList tabList;
3569     int i, tabCount;
3570     Widget tabBar = XtParent(tab);
3571 
3572     XtVaGetValues(tabBar, XmNtabWidgetList, &tabList,
3573             XmNtabCount, &tabCount, NULL);
3574 
3575     for (i=0; i< tabCount; i++) {
3576     	if (tab == tabList[i])
3577 	    return i;
3578     }
3579 
3580     return -1; /* something is wrong! */
3581 }
3582 
3583 /*
3584 ** update the tab label, etc. of a tab, per the states of it's document.
3585 */
RefreshTabState(WindowInfo * win)3586 void RefreshTabState(WindowInfo *win)
3587 {
3588     XmString s1, tipString;
3589     char labelString[MAXPATHLEN];
3590     char *tag = XmFONTLIST_DEFAULT_TAG;
3591     unsigned char alignment;
3592 
3593     /* Set tab label to document's filename. Position of
3594        "*" (modified) will change per label alignment setting */
3595     XtVaGetValues(win->tab, XmNalignment, &alignment, NULL);
3596     if (alignment != XmALIGNMENT_END) {
3597        sprintf(labelString, "%s%s",
3598                win->fileChanged? "*" : "",
3599                win->filename);
3600     } else {
3601        sprintf(labelString, "%s%s",
3602                win->filename,
3603                win->fileChanged? "*" : "");
3604     }
3605 
3606     /* Make the top document stand out a little more */
3607     if (IsTopDocument(win))
3608         tag = "BOLD";
3609 
3610     s1 = XmStringCreateLtoR(labelString, tag);
3611 
3612     if (GetPrefShowPathInWindowsMenu() && win->filenameSet) {
3613        strcat(labelString, " - ");
3614        strcat(labelString, win->path);
3615     }
3616     tipString=XmStringCreateSimple(labelString);
3617 
3618     XtVaSetValues(win->tab,
3619 	    XltNbubbleString, tipString,
3620 	    XmNlabelString, s1,
3621 	    NULL);
3622     XmStringFree(s1);
3623     XmStringFree(tipString);
3624 }
3625 
3626 /*
3627 ** close all the documents in a window
3628 */
CloseAllDocumentInWindow(WindowInfo * window)3629 int CloseAllDocumentInWindow(WindowInfo *window)
3630 {
3631     WindowInfo *win;
3632 
3633     if (NDocuments(window) == 1) {
3634     	/* only one document in the window */
3635     	return CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE);
3636     }
3637     else {
3638 	Widget winShell = window->shell;
3639 	WindowInfo *topDocument;
3640 
3641     	/* close all _modified_ documents belong to this window */
3642 	for (win = WindowList; win; ) {
3643     	    if (win->shell == winShell && win->fileChanged) {
3644 	    	WindowInfo *next = win->next;
3645     	    	if (!CloseFileAndWindow(win, PROMPT_SBC_DIALOG_RESPONSE))
3646 		    return False;
3647 		win = next;
3648 	    }
3649 	    else
3650 	    	win = win->next;
3651 	}
3652 
3653     	/* see there's still documents left in the window */
3654 	for (win = WindowList; win; win=win->next)
3655 	    if (win->shell == winShell)
3656 	    	break;
3657 
3658 	if (win) {
3659 	    topDocument = GetTopDocument(winShell);
3660 
3661     	    /* close all non-top documents belong to this window */
3662 	    for (win = WindowList; win; ) {
3663     		if (win->shell == winShell && win != topDocument) {
3664 	    	    WindowInfo *next = win->next;
3665     	    	    if (!CloseFileAndWindow(win, PROMPT_SBC_DIALOG_RESPONSE))
3666 			return False;
3667 		    win = next;
3668 		}
3669 		else
3670 	    	    win = win->next;
3671 	    }
3672 
3673 	    /* close the last document and its window */
3674     	    if (!CloseFileAndWindow(topDocument, PROMPT_SBC_DIALOG_RESPONSE))
3675 		return False;
3676 	}
3677     }
3678 
3679     return True;
3680 }
3681 
CloseDocumentWindow(Widget w,WindowInfo * window,XtPointer callData)3682 static void CloseDocumentWindow(Widget w, WindowInfo *window, XtPointer callData)
3683 {
3684     int nDocuments = NDocuments(window);
3685 
3686     if (nDocuments == NWindows()) {
3687     	/* this is only window, then exit */
3688 	XtCallActionProc(WindowList->lastFocus, "exit",
3689     		((XmAnyCallbackStruct *)callData)->event, NULL, 0);
3690     }
3691     else {
3692         if (nDocuments == 1) {
3693 	    CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE);
3694 	}
3695     	else {
3696             int resp = 1;
3697             if (GetPrefWarnExit())
3698                 resp = DialogF(DF_QUES, window->shell, 2, "Close Window",
3699 	    	    "Close ALL documents in this window?", "Close", "Cancel");
3700 
3701             if (resp == 1)
3702     		CloseAllDocumentInWindow(window);
3703 	}
3704     }
3705 }
3706 
3707 /*
3708 ** Refresh the menu entries per the settings of the
3709 ** top document.
3710 */
RefreshMenuToggleStates(WindowInfo * window)3711 void RefreshMenuToggleStates(WindowInfo *window)
3712 {
3713     WindowInfo *win;
3714 
3715     if (!IsTopDocument(window))
3716 	return;
3717 
3718     /* File menu */
3719     XtSetSensitive(window->printSelItem, window->wasSelected);
3720 
3721     /* Edit menu */
3722     XtSetSensitive(window->undoItem, window->undo != NULL);
3723     XtSetSensitive(window->redoItem, window->redo != NULL);
3724     XtSetSensitive(window->printSelItem, window->wasSelected);
3725     XtSetSensitive(window->cutItem, window->wasSelected);
3726     XtSetSensitive(window->copyItem, window->wasSelected);
3727     XtSetSensitive(window->delItem, window->wasSelected);
3728 
3729     /* Preferences menu */
3730     XmToggleButtonSetState(window->statsLineItem, window->showStats, False);
3731     XmToggleButtonSetState(window->iSearchLineItem, window->showISearchLine, False);
3732     XmToggleButtonSetState(window->lineNumsItem, window->showLineNumbers, False);
3733     XmToggleButtonSetState(window->highlightItem, window->highlightSyntax, False);
3734     XtSetSensitive(window->highlightItem, window->languageMode != PLAIN_LANGUAGE_MODE);
3735     XmToggleButtonSetState(window->backlightCharsItem, window->backlightChars, False);
3736 #ifndef VMS
3737     XmToggleButtonSetState(window->saveLastItem, window->saveOldVersion, False);
3738 #endif
3739     XmToggleButtonSetState(window->autoSaveItem, window->autoSave, False);
3740     XmToggleButtonSetState(window->overtypeModeItem, window->overstrike, False);
3741     XmToggleButtonSetState(window->matchSyntaxBasedItem, window->matchSyntaxBased, False);
3742     XmToggleButtonSetState(window->readOnlyItem, IS_USER_LOCKED(window->lockReasons), False);
3743 
3744     XtSetSensitive(window->smartIndentItem,
3745             SmartIndentMacrosAvailable(LanguageModeName(window->languageMode)));
3746 
3747     SetAutoIndent(window, window->indentStyle);
3748     SetAutoWrap(window, window->wrapMode);
3749     SetShowMatching(window, window->showMatchingStyle);
3750     SetLanguageMode(window, window->languageMode, FALSE);
3751 
3752     /* Windows Menu */
3753     XtSetSensitive(window->splitPaneItem, window->nPanes < MAX_PANES);
3754     XtSetSensitive(window->closePaneItem, window->nPanes > 0);
3755     XtSetSensitive(window->detachDocumentItem, NDocuments(window)>1);
3756     XtSetSensitive(window->contextDetachDocumentItem, NDocuments(window)>1);
3757 
3758     for (win=WindowList; win; win=win->next)
3759     	if (win->shell != window->shell)
3760 	    break;
3761     XtSetSensitive(window->moveDocumentItem, win != NULL);
3762 }
3763 
3764 /*
3765 ** Refresh the various settings/state of the shell window per the
3766 ** settings of the top document.
3767 */
refreshMenuBar(WindowInfo * window)3768 static void refreshMenuBar(WindowInfo *window)
3769 {
3770     RefreshMenuToggleStates(window);
3771 
3772     /* Add/remove language specific menu items */
3773     UpdateUserMenus(window);
3774 
3775     /* refresh selection-sensitive menus */
3776     DimSelectionDepUserMenuItems(window, window->wasSelected);
3777 }
3778 
3779 /*
3780 ** remember the last document.
3781 */
MarkLastDocument(WindowInfo * window)3782 WindowInfo *MarkLastDocument(WindowInfo *window)
3783 {
3784     WindowInfo *prev = lastFocusDocument;
3785 
3786     if (window)
3787     	lastFocusDocument = window;
3788 
3789     return prev;
3790 }
3791 
3792 /*
3793 ** remember the active (top) document.
3794 */
MarkActiveDocument(WindowInfo * window)3795 WindowInfo *MarkActiveDocument(WindowInfo *window)
3796 {
3797     WindowInfo *prev = inFocusDocument;
3798 
3799     if (window)
3800     	inFocusDocument = window;
3801 
3802     return prev;
3803 }
3804 
3805 /*
3806 ** Bring up the next window by tab order
3807 */
NextDocument(WindowInfo * window)3808 void NextDocument(WindowInfo *window)
3809 {
3810     WindowInfo *win;
3811 
3812     if (WindowList->next == NULL)
3813     	return;
3814 
3815     win = getNextTabWindow(window, 1, GetPrefGlobalTabNavigate(), 1);
3816     if (win == NULL)
3817     	return;
3818 
3819     if (window->shell == win->shell)
3820 	RaiseDocument(win);
3821     else
3822     	RaiseFocusDocumentWindow(win, True);
3823 }
3824 
3825 /*
3826 ** Bring up the previous window by tab order
3827 */
PreviousDocument(WindowInfo * window)3828 void PreviousDocument(WindowInfo *window)
3829 {
3830     WindowInfo *win;
3831 
3832     if (WindowList->next == NULL)
3833     	return;
3834 
3835     win = getNextTabWindow(window, -1, GetPrefGlobalTabNavigate(), 1);
3836     if (win == NULL)
3837     	return;
3838 
3839     if (window->shell == win->shell)
3840 	RaiseDocument(win);
3841     else
3842     	RaiseFocusDocumentWindow(win, True);
3843 }
3844 
3845 /*
3846 ** Bring up the last active window
3847 */
LastDocument(WindowInfo * window)3848 void LastDocument(WindowInfo *window)
3849 {
3850     WindowInfo *win;
3851 
3852     for(win = WindowList; win; win=win->next)
3853     	if (lastFocusDocument == win)
3854 	    break;
3855 
3856     if (!win)
3857     	return;
3858 
3859     if (window->shell == win->shell)
3860 	RaiseDocument(win);
3861     else
3862     	RaiseFocusDocumentWindow(win, True);
3863 
3864 }
3865 
3866 /*
3867 ** make sure window is alive is kicking
3868 */
IsValidWindow(WindowInfo * window)3869 int IsValidWindow(WindowInfo *window)
3870 {
3871     WindowInfo *win;
3872 
3873     for(win = WindowList; win; win=win->next)
3874     	if (window == win)
3875 	    return True;
3876 
3877 
3878     return False;
3879 }
3880 
3881 /*
3882 ** raise the document and its shell window and focus depending on pref.
3883 */
RaiseDocumentWindow(WindowInfo * window)3884 void RaiseDocumentWindow(WindowInfo *window)
3885 {
3886     if (!window)
3887     	return;
3888 
3889     RaiseDocument(window);
3890     RaiseShellWindow(window->shell, GetPrefFocusOnRaise());
3891 }
3892 
3893 /*
3894 ** raise the document and its shell window and optionally focus.
3895 */
RaiseFocusDocumentWindow(WindowInfo * window,Boolean focus)3896 void RaiseFocusDocumentWindow(WindowInfo *window, Boolean focus)
3897 {
3898     if (!window)
3899     	return;
3900 
3901     RaiseDocument(window);
3902     RaiseShellWindow(window->shell, focus);
3903 }
3904 
3905 /*
3906 ** Redisplay menu tearoffs previously hid by hideTearOffs()
3907 */
redisplayTearOffs(Widget menuPane)3908 static void redisplayTearOffs(Widget menuPane)
3909 {
3910     WidgetList itemList;
3911     Widget subMenuID;
3912     Cardinal nItems;
3913     int n;
3914 
3915     /* redisplay all submenu tearoffs */
3916     XtVaGetValues(menuPane, XmNchildren, &itemList,
3917             XmNnumChildren, &nItems, NULL);
3918     for (n=0; n<(int)nItems; n++) {
3919     	if (XtClass(itemList[n]) == xmCascadeButtonWidgetClass) {
3920 	    XtVaGetValues(itemList[n], XmNsubMenuId, &subMenuID, NULL);
3921 	    redisplayTearOffs(subMenuID);
3922 	}
3923     }
3924 
3925     /* redisplay tearoff for this menu */
3926     if (!XmIsMenuShell(XtParent(menuPane)))
3927     	ShowHiddenTearOff(menuPane);
3928 }
3929 
3930 /*
3931 ** hide all the tearoffs spawned from this menu.
3932 ** It works recursively to close the tearoffs of the submenus
3933 */
hideTearOffs(Widget menuPane)3934 static void hideTearOffs(Widget menuPane)
3935 {
3936     WidgetList itemList;
3937     Widget subMenuID;
3938     Cardinal nItems;
3939     int n;
3940 
3941     /* hide all submenu tearoffs */
3942     XtVaGetValues(menuPane, XmNchildren, &itemList,
3943             XmNnumChildren, &nItems, NULL);
3944     for (n=0; n<(int)nItems; n++) {
3945     	if (XtClass(itemList[n]) == xmCascadeButtonWidgetClass) {
3946 	    XtVaGetValues(itemList[n], XmNsubMenuId, &subMenuID, NULL);
3947 	    hideTearOffs(subMenuID);
3948 	}
3949     }
3950 
3951     /* hide tearoff for this menu */
3952     if (!XmIsMenuShell(XtParent(menuPane)))
3953     	XtUnmapWidget(XtParent(menuPane));
3954 }
3955 
3956 /*
3957 ** Raise a tabbed document within its shell window.
3958 **
3959 ** NB: use RaiseDocumentWindow() to raise the doc and
3960 **     its shell window.
3961 */
RaiseDocument(WindowInfo * window)3962 void RaiseDocument(WindowInfo *window)
3963 {
3964     WindowInfo *win, *lastwin;
3965 
3966     if (!window || !WindowList)
3967     	return;
3968 
3969     lastwin = MarkActiveDocument(window);
3970     if (lastwin != window && IsValidWindow(lastwin))
3971     	MarkLastDocument(lastwin);
3972 
3973     /* document already on top? */
3974     XtVaGetValues(window->mainWin, XmNuserData, &win, NULL);
3975     if (win == window)
3976     	return;
3977 
3978     /* set the document as top document */
3979     XtVaSetValues(window->mainWin, XmNuserData, window, NULL);
3980 
3981     /* show the new top document */
3982     XtVaSetValues(window->mainWin, XmNworkWindow, window->splitPane, NULL);
3983     XtManageChild(window->splitPane);
3984     XRaiseWindow(TheDisplay, XtWindow(window->splitPane));
3985 
3986     /* Turn on syntax highlight that might have been deferred.
3987        NB: this must be done after setting the document as
3988            XmNworkWindow and managed, else the parent shell
3989 	   window may shrink on some window-managers such as
3990 	   metacity, due to changes made in UpdateWMSizeHints().*/
3991     if (window->highlightSyntax && window->highlightData==NULL)
3992     	StartHighlighting(window, False);
3993 
3994     /* put away the bg menu tearoffs of last active document */
3995     hideTearOffs(win->bgMenuPane);
3996 
3997     /* restore the bg menu tearoffs of active document */
3998     redisplayTearOffs(window->bgMenuPane);
3999 
4000     /* set tab as active */
4001     XmLFolderSetActiveTab(window->tabBar,
4002     	    getTabPosition(window->tab), False);
4003 
4004     /* set keyboard focus. Must be done before unmanaging previous
4005        top document, else lastFocus will be reset to textArea */
4006     XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
4007 
4008     /* we only manage the top document, else the next time a document
4009        is raised again, it's textpane might not resize properly.
4010        Also, somehow (bug?) XtUnmanageChild() doesn't hide the
4011        splitPane, which obscure lower part of the statsform when
4012        we toggle its components, so we need to put the document at
4013        the back */
4014     XLowerWindow(TheDisplay, XtWindow(win->splitPane));
4015     XtUnmanageChild(win->splitPane);
4016     RefreshTabState(win);
4017 
4018     /* now refresh window state/info. RefreshWindowStates()
4019        has a lot of work to do, so we update the screen first so
4020        the document appears to switch swiftly. */
4021     XmUpdateDisplay(window->splitPane);
4022     RefreshWindowStates(window);
4023     RefreshTabState(window);
4024 
4025     /* put away the bg menu tearoffs of last active document */
4026     hideTearOffs(win->bgMenuPane);
4027 
4028     /* restore the bg menu tearoffs of active document */
4029     redisplayTearOffs(window->bgMenuPane);
4030 
4031     /* Make sure that the "In Selection" button tracks the presence of a
4032        selection and that the window inherits the proper search scope. */
4033     if (window->replaceDlog != NULL && XtIsManaged(window->replaceDlog))
4034     {
4035 #ifdef REPLACE_SCOPE
4036         window->replaceScope = win->replaceScope;
4037 #endif
4038         UpdateReplaceActionButtons(window);
4039     }
4040 
4041     UpdateWMSizeHints(window);
4042 }
4043 
GetTopDocument(Widget w)4044 WindowInfo* GetTopDocument(Widget w)
4045 {
4046     WindowInfo *window = WidgetToWindow(w);
4047 
4048     return WidgetToWindow(window->shell);
4049 }
4050 
IsTopDocument(const WindowInfo * window)4051 Boolean IsTopDocument(const WindowInfo *window)
4052 {
4053     return window == GetTopDocument(window->shell)? True : False;
4054 }
4055 
deleteDocument(WindowInfo * window)4056 static void deleteDocument(WindowInfo *window)
4057 {
4058     if (NULL == window) {
4059         return;
4060     }
4061 
4062     XtDestroyWidget(window->splitPane);
4063 }
4064 
4065 /*
4066 ** return the number of documents owned by this shell window
4067 */
NDocuments(WindowInfo * window)4068 int NDocuments(WindowInfo *window)
4069 {
4070     WindowInfo *win;
4071     int nDocument = 0;
4072 
4073     for (win = WindowList; win; win = win->next) {
4074     	if (win->shell == window->shell)
4075 	    nDocument++;
4076     }
4077 
4078     return nDocument;
4079 }
4080 
4081 /*
4082 ** refresh window state for this document
4083 */
RefreshWindowStates(WindowInfo * window)4084 void RefreshWindowStates(WindowInfo *window)
4085 {
4086     if (!IsTopDocument(window))
4087     	return;
4088 
4089     if (window->modeMessageDisplayed)
4090     	XmTextSetString(window->statsLine, window->modeMessage);
4091     else
4092     	UpdateStatsLine(window);
4093     UpdateWindowReadOnly(window);
4094     UpdateWindowTitle(window);
4095 
4096     /* show/hide statsline as needed */
4097     if (window->modeMessageDisplayed && !XtIsManaged(window->statsLineForm)) {
4098     	/* turn on statline to display mode message */
4099     	showStats(window, True);
4100     }
4101     else if (window->showStats && !XtIsManaged(window->statsLineForm)) {
4102     	/* turn on statsline since it is enabled */
4103     	showStats(window, True);
4104     }
4105     else if (!window->showStats && !window->modeMessageDisplayed &&
4106              XtIsManaged(window->statsLineForm)) {
4107     	/* turn off statsline since there's nothing to show */
4108     	showStats(window, False);
4109     }
4110 
4111     /* signal if macro/shell is running */
4112     if (window->shellCmdData || window->macroCmdData)
4113     	BeginWait(window->shell);
4114     else
4115     	EndWait(window->shell);
4116 
4117     /* we need to force the statsline to reveal itself */
4118     if (XtIsManaged(window->statsLineForm)) {
4119 	XmTextSetCursorPosition(window->statsLine, 0);     /* start of line */
4120 	XmTextSetCursorPosition(window->statsLine, 9000);  /* end of line */
4121     }
4122 
4123     XmUpdateDisplay(window->statsLine);
4124     refreshMenuBar(window);
4125 
4126     updateLineNumDisp(window);
4127 }
4128 
cloneTextPanes(WindowInfo * window,WindowInfo * orgWin)4129 static void cloneTextPanes(WindowInfo *window, WindowInfo *orgWin)
4130 {
4131     short paneHeights[MAX_PANES+1];
4132     int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1];
4133     int horizOffsets[MAX_PANES+1];
4134     int i, focusPane, emTabDist, wrapMargin, lineNumCols, totalHeight=0;
4135     char *delimiters;
4136     Widget text;
4137     selection sel;
4138     textDisp *textD, *newTextD;
4139 
4140     /* transfer the primary selection */
4141     memcpy(&sel, &orgWin->buffer->primary, sizeof(selection));
4142 
4143     if (sel.selected) {
4144     	if (sel.rectangular)
4145     	    BufRectSelect(window->buffer, sel.start, sel.end,
4146 		    sel.rectStart, sel.rectEnd);
4147     	else
4148     	    BufSelect(window->buffer, sel.start, sel.end);
4149     } else
4150     	BufUnselect(window->buffer);
4151 
4152     /* Record the current heights, scroll positions, and insert positions
4153        of the existing panes, keyboard focus */
4154     focusPane = 0;
4155     for (i=0; i<=orgWin->nPanes; i++) {
4156     	text = i==0 ? orgWin->textArea : orgWin->textPanes[i-1];
4157     	insertPositions[i] = TextGetCursorPos(text);
4158     	XtVaGetValues(containingPane(text), XmNheight, &paneHeights[i], NULL);
4159     	totalHeight += paneHeights[i];
4160     	TextGetScroll(text, &topLines[i], &horizOffsets[i]);
4161     	if (text == orgWin->lastFocus)
4162     	    focusPane = i;
4163     }
4164 
4165     window->nPanes = orgWin->nPanes;
4166 
4167     /* Copy some parameters */
4168     XtVaGetValues(orgWin->textArea, textNemulateTabs, &emTabDist,
4169     	    textNwordDelimiters, &delimiters, textNwrapMargin, &wrapMargin,
4170             NULL);
4171     lineNumCols = orgWin->showLineNumbers ? MIN_LINE_NUM_COLS : 0;
4172     XtVaSetValues(window->textArea, textNemulateTabs, emTabDist,
4173     	    textNwordDelimiters, delimiters, textNwrapMargin, wrapMargin,
4174 	    textNlineNumCols, lineNumCols, NULL);
4175 
4176 
4177     /* clone split panes, if any */
4178     textD = ((TextWidget)window->textArea)->text.textD;
4179     if (window->nPanes) {
4180 	/* Unmanage & remanage the panedWindow so it recalculates pane
4181            heights */
4182     	XtUnmanageChild(window->splitPane);
4183 
4184 	/* Create a text widget to add to the pane and set its buffer and
4185 	   highlight data to be the same as the other panes in the orgWin */
4186 
4187 	for(i=0; i<orgWin->nPanes; i++) {
4188 	    text = createTextArea(window->splitPane, window, 1, 1, emTabDist,
4189     		    delimiters, wrapMargin, lineNumCols);
4190 	    TextSetBuffer(text, window->buffer);
4191 
4192 	    if (window->highlightData != NULL)
4193     		AttachHighlightToWidget(text, window);
4194 	    XtManageChild(text);
4195 	    window->textPanes[i] = text;
4196 
4197             /* Fix up the colors */
4198             newTextD = ((TextWidget)text)->text.textD;
4199             XtVaSetValues(text, XmNforeground, textD->fgPixel,
4200                     XmNbackground, textD->bgPixel, NULL);
4201             TextDSetColors(newTextD, textD->fgPixel, textD->bgPixel,
4202                     textD->selectFGPixel, textD->selectBGPixel,
4203                     textD->highlightFGPixel,textD->highlightBGPixel,
4204                     textD->lineNumFGPixel, textD->cursorFGPixel);
4205 	}
4206 
4207 	/* Set the minimum pane height in the new pane */
4208 	UpdateMinPaneHeights(window);
4209 
4210 	for (i=0; i<=window->nPanes; i++) {
4211     	    text = i==0 ? window->textArea : window->textPanes[i-1];
4212     	    setPaneDesiredHeight(containingPane(text), paneHeights[i]);
4213 	}
4214 
4215 	/* Re-manage panedWindow to recalculate pane heights & reset selection */
4216     	XtManageChild(window->splitPane);
4217     }
4218 
4219     /* Reset all of the heights, scroll positions, etc. */
4220     for (i=0; i<=window->nPanes; i++) {
4221     	textDisp *textD;
4222 
4223     	text = i==0 ? window->textArea : window->textPanes[i-1];
4224 	TextSetCursorPos(text, insertPositions[i]);
4225 	TextSetScroll(text, topLines[i], horizOffsets[i]);
4226 
4227 	/* dim the cursor */
4228     	textD = ((TextWidget)text)->text.textD;
4229 	TextDSetCursorStyle(textD, DIM_CURSOR);
4230 	TextDUnblankCursor(textD);
4231     }
4232 
4233     /* set the focus pane */
4234     for (i=0; i<=window->nPanes; i++) {
4235     	text = i==0 ? window->textArea : window->textPanes[i-1];
4236 	if(i == focusPane) {
4237 	    window->lastFocus = text;
4238     	    XmProcessTraversal(text, XmTRAVERSE_CURRENT);
4239 	    break;
4240 	}
4241     }
4242 
4243     /* Update the window manager size hints after the sizes of the panes have
4244        been set (the widget heights are not yet readable here, but they will
4245        be by the time the event loop gets around to running this timer proc) */
4246     XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0,
4247     	    wmSizeUpdateProc, window);
4248 }
4249 
4250 /*
4251 ** clone a document's states and settings into the other.
4252 */
cloneDocument(WindowInfo * window,WindowInfo * orgWin)4253 static void cloneDocument(WindowInfo *window, WindowInfo *orgWin)
4254 {
4255     const char *orgDocument;
4256     char *params[4];
4257     int emTabDist;
4258 
4259     strcpy(window->path, orgWin->path);
4260     strcpy(window->filename, orgWin->filename);
4261 
4262     ShowLineNumbers(window, orgWin->showLineNumbers);
4263 
4264     window->ignoreModify = True;
4265 
4266     /* copy the text buffer */
4267     orgDocument = BufAsString(orgWin->buffer);
4268     BufSetAll(window->buffer, orgDocument);
4269 
4270     /* copy the tab preferences (here!) */
4271     BufSetTabDistance(window->buffer, orgWin->buffer->tabDist);
4272     window->buffer->useTabs = orgWin->buffer->useTabs;
4273     XtVaGetValues(orgWin->textArea, textNemulateTabs, &emTabDist, NULL);
4274     SetEmTabDist(window, emTabDist);
4275 
4276     window->ignoreModify = False;
4277 
4278     /* transfer text fonts */
4279     params[0] = orgWin->fontName;
4280     params[1] = orgWin->italicFontName;
4281     params[2] = orgWin->boldFontName;
4282     params[3] = orgWin->boldItalicFontName;
4283     XtCallActionProc(window->textArea, "set_fonts", NULL, params, 4);
4284 
4285     SetBacklightChars(window, orgWin->backlightCharTypes);
4286 
4287     /* Clone rangeset info.
4288 
4289        FIXME:
4290        Cloning of rangesets must be done before syntax highlighting,
4291        else the rangesets do not be highlighted (colored) properly
4292        if syntax highlighting is on.
4293     */
4294     window->buffer->rangesetTable =
4295 	    RangesetTableClone(orgWin->buffer->rangesetTable, window->buffer);
4296 
4297     /* Syntax highlighting */
4298     window->languageMode = orgWin->languageMode;
4299     window->highlightSyntax = orgWin->highlightSyntax;
4300     if (window->highlightSyntax)
4301     	StartHighlighting(window, False);
4302 
4303     /* copy states of original document */
4304     window->filenameSet = orgWin->filenameSet;
4305     window->fileFormat = orgWin->fileFormat;
4306     window->lastModTime = orgWin->lastModTime;
4307     window->fileChanged = orgWin->fileChanged;
4308     window->fileMissing = orgWin->fileMissing;
4309     window->lockReasons = orgWin->lockReasons;
4310     window->autoSaveCharCount = orgWin->autoSaveCharCount;
4311     window->autoSaveOpCount = orgWin->autoSaveOpCount;
4312     window->undoOpCount = orgWin->undoOpCount;
4313     window->undoMemUsed = orgWin->undoMemUsed;
4314     window->lockReasons = orgWin->lockReasons;
4315     window->autoSave = orgWin->autoSave;
4316     window->saveOldVersion = orgWin->saveOldVersion;
4317     window->wrapMode = orgWin->wrapMode;
4318     SetOverstrike(window, orgWin->overstrike);
4319     window->showMatchingStyle = orgWin->showMatchingStyle;
4320     window->matchSyntaxBased = orgWin->matchSyntaxBased;
4321 #if 0
4322     window->showStats = orgWin->showStats;
4323     window->showISearchLine = orgWin->showISearchLine;
4324     window->showLineNumbers = orgWin->showLineNumbers;
4325     window->modeMessageDisplayed = orgWin->modeMessageDisplayed;
4326     window->ignoreModify = orgWin->ignoreModify;
4327     window->windowMenuValid = orgWin->windowMenuValid;
4328     window->flashTimeoutID = orgWin->flashTimeoutID;
4329     window->wasSelected = orgWin->wasSelected;
4330     strcpy(window->fontName, orgWin->fontName);
4331     strcpy(window->italicFontName, orgWin->italicFontName);
4332     strcpy(window->boldFontName, orgWin->boldFontName);
4333     strcpy(window->boldItalicFontName, orgWin->boldItalicFontName);
4334     window->fontList = orgWin->fontList;
4335     window->italicFontStruct = orgWin->italicFontStruct;
4336     window->boldFontStruct = orgWin->boldFontStruct;
4337     window->boldItalicFontStruct = orgWin->boldItalicFontStruct;
4338     window->markTimeoutID = orgWin->markTimeoutID;
4339     window->highlightData = orgWin->highlightData;
4340     window->shellCmdData = orgWin->shellCmdData;
4341     window->macroCmdData = orgWin->macroCmdData;
4342     window->smartIndentData = orgWin->smartIndentData;
4343 #endif
4344     window->iSearchHistIndex = orgWin->iSearchHistIndex;
4345     window->iSearchStartPos = orgWin->iSearchStartPos;
4346     window->replaceLastRegexCase = orgWin->replaceLastRegexCase;
4347     window->replaceLastLiteralCase = orgWin->replaceLastLiteralCase;
4348     window->iSearchLastRegexCase = orgWin->iSearchLastRegexCase;
4349     window->iSearchLastLiteralCase = orgWin->iSearchLastLiteralCase;
4350     window->findLastRegexCase = orgWin->findLastRegexCase;
4351     window->findLastLiteralCase = orgWin->findLastLiteralCase;
4352     window->device = orgWin->device;
4353     window->inode = orgWin->inode;
4354     window->fileClosedAtom = orgWin->fileClosedAtom;
4355     orgWin->fileClosedAtom = None;
4356 
4357     /* copy the text/split panes settings, cursor pos & selection */
4358     cloneTextPanes(window, orgWin);
4359 
4360     /* copy undo & redo list */
4361     window->undo = cloneUndoItems(orgWin->undo);
4362     window->redo = cloneUndoItems(orgWin->redo);
4363 
4364     /* copy bookmarks */
4365     window->nMarks = orgWin->nMarks;
4366     memcpy(&window->markTable, &orgWin->markTable,
4367             sizeof(Bookmark)*window->nMarks);
4368 
4369     /* kick start the auto-indent engine */
4370     window->indentStyle = NO_AUTO_INDENT;
4371     SetAutoIndent(window, orgWin->indentStyle);
4372 
4373     /* synchronize window state to this document */
4374     RefreshWindowStates(window);
4375 }
4376 
cloneUndoItems(UndoInfo * orgList)4377 static UndoInfo *cloneUndoItems(UndoInfo *orgList)
4378 {
4379     UndoInfo *head = NULL, *undo, *clone, *last = NULL;
4380 
4381     for (undo = orgList; undo; undo = undo->next) {
4382 	clone = (UndoInfo *)NEditMalloc(sizeof(UndoInfo));
4383 	memcpy(clone, undo, sizeof(UndoInfo));
4384 
4385 	if (undo->oldText) {
4386 	    clone->oldText = (char*)NEditMalloc(strlen(undo->oldText)+1);
4387 	    strcpy(clone->oldText, undo->oldText);
4388 	}
4389 	clone->next = NULL;
4390 
4391 	if (last)
4392 	    last->next = clone;
4393 	else
4394 	    head = clone;
4395 
4396 	last = clone;
4397     }
4398 
4399     return head;
4400 }
4401 
4402 /*
4403 ** spin off the document to a new window
4404 */
DetachDocument(WindowInfo * window)4405 WindowInfo *DetachDocument(WindowInfo *window)
4406 {
4407     WindowInfo *win = NULL, *cloneWin;
4408 
4409     if (NDocuments(window) < 2)
4410     	return NULL;
4411 
4412     /* raise another document in the same shell window if the window
4413        being detached is the top document */
4414     if (IsTopDocument(window)) {
4415     	win = getNextTabWindow(window, 1, 0, 0);
4416     	RaiseDocument(win);
4417     }
4418 
4419     /* Create a new window */
4420     cloneWin = CreateWindow(window->filename, NULL, False);
4421 
4422     /* CreateWindow() simply adds the new window's pointer to the
4423        head of WindowList. We need to adjust the detached window's
4424        pointer, so that macro functions such as focus_window("last")
4425        will travel across the documents per the sequence they're
4426        opened. The new doc will appear to replace it's former self
4427        as the old doc is closed. */
4428     WindowList = cloneWin->next;
4429     cloneWin->next = window->next;
4430     window->next = cloneWin;
4431 
4432     /* these settings should follow the detached document.
4433        must be done before cloning window, else the height
4434        of split panes may not come out correctly */
4435     ShowISearchLine(cloneWin, window->showISearchLine);
4436     ShowStatsLine(cloneWin, window->showStats);
4437 
4438     /* clone the document & its pref settings */
4439     cloneDocument(cloneWin, window);
4440 
4441     /* remove the document from the old window */
4442     window->fileChanged = False;
4443     CloseFileAndWindow(window, NO_SBC_DIALOG_RESPONSE);
4444 
4445     /* refresh former host window */
4446     if (win) {
4447         RefreshWindowStates(win);
4448     }
4449 
4450     /* this should keep the new document window fresh */
4451     RefreshWindowStates(cloneWin);
4452     RefreshTabState(cloneWin);
4453     SortTabBar(cloneWin);
4454 
4455     return cloneWin;
4456 }
4457 
4458 /*
4459 ** Move document to an other window.
4460 **
4461 ** the moving document will receive certain window settings from
4462 ** its new host, i.e. the window size, stats and isearch lines.
4463 */
MoveDocument(WindowInfo * toWindow,WindowInfo * window)4464 WindowInfo *MoveDocument(WindowInfo *toWindow, WindowInfo *window)
4465 {
4466     WindowInfo *win = NULL, *cloneWin;
4467 
4468     /* prepare to move document */
4469     if (NDocuments(window) < 2) {
4470     	/* hide the window to make it look like we are moving */
4471     	XtUnmapWidget(window->shell);
4472     }
4473     else if (IsTopDocument(window)) {
4474     	/* raise another document to replace the document being moved */
4475     	win = getNextTabWindow(window, 1, 0, 0);
4476     	RaiseDocument(win);
4477     }
4478 
4479     /* relocate the document to target window */
4480     cloneWin = CreateDocument(toWindow, window->filename);
4481     ShowTabBar(cloneWin, GetShowTabBar(cloneWin));
4482     cloneDocument(cloneWin, window);
4483 
4484     /* CreateDocument() simply adds the new window's pointer to the
4485        head of WindowList. We need to adjust the detached window's
4486        pointer, so that macro functions such as focus_window("last")
4487        will travel across the documents per the sequence they're
4488        opened. The new doc will appear to replace it's former self
4489        as the old doc is closed. */
4490     WindowList = cloneWin->next;
4491     cloneWin->next = window->next;
4492     window->next = cloneWin;
4493 
4494     /* remove the document from the old window */
4495     window->fileChanged = False;
4496     CloseFileAndWindow(window, NO_SBC_DIALOG_RESPONSE);
4497 
4498     /* some menu states might have changed when deleting document */
4499     if (win)
4500     	RefreshWindowStates(win);
4501 
4502     /* this should keep the new document window fresh */
4503     RaiseDocumentWindow(cloneWin);
4504     RefreshTabState(cloneWin);
4505     SortTabBar(cloneWin);
4506 
4507     return cloneWin;
4508 }
4509 
moveDocumentCB(Widget dialog,WindowInfo * window,XtPointer call_data)4510 static void moveDocumentCB(Widget dialog, WindowInfo *window,
4511 	XtPointer call_data)
4512 {
4513     XmSelectionBoxCallbackStruct *cbs = (XmSelectionBoxCallbackStruct *) call_data;
4514     DoneWithMoveDocumentDialog = cbs->reason;
4515 }
4516 
4517 /*
4518 ** present dialog for selecting a target window to move this document
4519 ** into. Do nothing if there is only one shell window opened.
4520 */
MoveDocumentDialog(WindowInfo * window)4521 void MoveDocumentDialog(WindowInfo *window)
4522 {
4523     WindowInfo *win, *targetWin, **shellWinList;
4524     int i, nList=0, nWindows=0, ac;
4525     char tmpStr[MAXPATHLEN+50];
4526     Widget parent, dialog, listBox, moveAllOption;
4527     XmString *list = NULL;
4528     XmString popupTitle, s1;
4529     Arg csdargs[20];
4530     int *position_list, position_count;
4531 
4532     /* get the list of available shell windows, not counting
4533        the document to be moved */
4534     nWindows = NWindows();
4535     list = (XmStringTable) NEditMalloc(nWindows * sizeof(XmString *));
4536     shellWinList = (WindowInfo **) NEditMalloc(nWindows * sizeof(WindowInfo *));
4537 
4538     for (win=WindowList; win; win=win->next) {
4539 	if (!IsTopDocument(win) || win->shell == window->shell)
4540 	    continue;
4541 
4542 	sprintf(tmpStr, "%s%s",
4543 		win->filenameSet? win->path : "", win->filename);
4544 
4545 	list[nList] = XmStringCreateSimple(tmpStr);
4546 	shellWinList[nList] = win;
4547 	nList++;
4548     }
4549 
4550     /* stop here if there's no other window to move to */
4551     if (!nList) {
4552         NEditFree(list);
4553         NEditFree(shellWinList);
4554         return;
4555     }
4556 
4557     /* create the dialog */
4558     parent = window->shell;
4559     popupTitle = XmStringCreateSimple("Move Document");
4560     sprintf(tmpStr, "Move %s into window of", window->filename);
4561     s1 = XmStringCreateSimple(tmpStr);
4562     ac = 0;
4563     XtSetArg(csdargs[ac], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); ac++;
4564     XtSetArg(csdargs[ac], XmNdialogTitle, popupTitle); ac++;
4565     XtSetArg(csdargs[ac], XmNlistLabelString, s1); ac++;
4566     XtSetArg(csdargs[ac], XmNlistItems, list); ac++;
4567     XtSetArg(csdargs[ac], XmNlistItemCount, nList); ac++;
4568     XtSetArg(csdargs[ac], XmNvisibleItemCount, 12); ac++;
4569     XtSetArg(csdargs[ac], XmNautoUnmanage, False); ac++;
4570     dialog = CreateSelectionDialog(parent,"moveDocument",csdargs,ac);
4571     XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT));
4572     XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
4573     XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_SELECTION_LABEL));
4574     XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)moveDocumentCB, window);
4575     XtAddCallback(dialog, XmNapplyCallback, (XtCallbackProc)moveDocumentCB, window);
4576     XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)moveDocumentCB, window);
4577     XmStringFree(s1);
4578     XmStringFree(popupTitle);
4579 
4580     /* free the window list */
4581     for (i=0; i<nList; i++)
4582 	XmStringFree(list[i]);
4583     NEditFree(list);
4584 
4585     /* create the option box for moving all documents */
4586     s1 = MKSTRING("Move all documents in this window");
4587     moveAllOption =  XtVaCreateWidget("moveAll",
4588     	    xmToggleButtonWidgetClass, dialog,
4589 	    XmNlabelString, s1,
4590 	    XmNalignment, XmALIGNMENT_BEGINNING,
4591 	    NULL);
4592     XmStringFree(s1);
4593 
4594     if (NDocuments(window) >1)
4595 	XtManageChild(moveAllOption);
4596 
4597     /* disable option if only one document in the window */
4598     XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_APPLY_BUTTON));
4599 
4600     s1 = MKSTRING("Move");
4601     XtVaSetValues (dialog, XmNokLabelString, s1, NULL);
4602     XmStringFree(s1);
4603 
4604     /* default to the first window on the list */
4605     listBox = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);
4606     XmListSelectPos(listBox, 1, True);
4607 
4608     /* show the dialog */
4609     DoneWithMoveDocumentDialog = 0;
4610     ManageDialogCenteredOnPointer(dialog);
4611     while (!DoneWithMoveDocumentDialog)
4612         XtAppProcessEvent(XtWidgetToApplicationContext(parent), XtIMAll);
4613 
4614     /* get the window to move document into */
4615     XmListGetSelectedPos(listBox, &position_list, &position_count);
4616     targetWin = shellWinList[position_list[0]-1];
4617     NEditFree(position_list);
4618 
4619     /* now move document(s) */
4620     if (DoneWithMoveDocumentDialog == XmCR_OK) {
4621     	/* move top document */
4622 	if (XmToggleButtonGetState(moveAllOption)) {
4623     	    /* move all documents */
4624 	    for (win = WindowList; win; ) {
4625     		if (win != window && win->shell == window->shell) {
4626 	    	    WindowInfo *next = win->next;
4627     	    	    MoveDocument(targetWin, win);
4628 		    win = next;
4629 		}
4630 		else
4631 	    	    win = win->next;
4632 	    }
4633 
4634 	    /* invoking document is the last to move */
4635     	    MoveDocument(targetWin, window);
4636 	}
4637 	else {
4638     	    MoveDocument(targetWin, window);
4639 	}
4640     }
4641 
4642     NEditFree(shellWinList);
4643     XtDestroyWidget(dialog);
4644 }
4645 
hideTooltip(Widget tab)4646 static void hideTooltip(Widget tab)
4647 {
4648     Widget tooltip = XtNameToWidget(tab, "*BubbleShell");
4649 
4650     if (tooltip)
4651     	XtPopdown(tooltip);
4652 }
4653 
closeTabProc(XtPointer clientData,XtIntervalId * id)4654 static void closeTabProc(XtPointer clientData, XtIntervalId *id)
4655 {
4656     CloseFileAndWindow((WindowInfo*)clientData, PROMPT_SBC_DIALOG_RESPONSE);
4657 }
4658 
4659 /*
4660 ** callback to close-tab button.
4661 */
closeTabCB(Widget w,Widget mainWin,caddr_t callData)4662 static void closeTabCB(Widget w, Widget mainWin, caddr_t callData)
4663 {
4664     /* FIXME: XtRemoveActionHook() related coredump
4665 
4666        An unknown bug seems to be associated with the XtRemoveActionHook()
4667        call in FinishLearn(), which resulted in coredump if a tab was
4668        closed, in the middle of keystrokes learning, by clicking on the
4669        close-tab button.
4670 
4671        As evident to our accusation, the coredump may be surpressed by
4672        simply commenting out the XtRemoveActionHook() call. The bug was
4673        consistent on both Motif and Lesstif on various platforms.
4674 
4675        Closing the tab through either the "Close" menu or its accel key,
4676        however, was without any trouble.
4677 
4678        While its actual mechanism is not well understood, we somehow
4679        managed to workaround the bug by delaying the action of closing
4680        the tab. For now. */
4681     XtAppAddTimeOut(XtWidgetToApplicationContext(w), 0,
4682     	    closeTabProc, GetTopDocument(mainWin));
4683 }
4684 
4685 /*
4686 ** callback to clicks on a tab to raise it's document.
4687 */
raiseTabCB(Widget w,XtPointer clientData,XtPointer callData)4688 static void raiseTabCB(Widget w, XtPointer clientData, XtPointer callData)
4689 {
4690     XmLFolderCallbackStruct *cbs = (XmLFolderCallbackStruct *)callData;
4691     WidgetList tabList;
4692     Widget tab;
4693 
4694     XtVaGetValues(w, XmNtabWidgetList, &tabList, NULL);
4695     tab = tabList[cbs->pos];
4696     RaiseDocument(TabToWindow(tab));
4697 }
4698 
containingPane(Widget w)4699 static Widget containingPane(Widget w)
4700 {
4701     /* The containing pane used to simply be the first parent, but with
4702        the introduction of an XmFrame, it's the grandparent. */
4703     return XtParent(XtParent(w));
4704 }
4705 
cancelTimeOut(XtIntervalId * timer)4706 static void cancelTimeOut(XtIntervalId *timer)
4707 {
4708     if (*timer != 0)
4709     {
4710         XtRemoveTimeOut(*timer);
4711         *timer = 0;
4712     }
4713 }
4714 
4715 /*
4716 ** set/clear toggle menu state if the calling document is on top.
4717 */
SetToggleButtonState(WindowInfo * window,Widget w,Boolean state,Boolean notify)4718 void SetToggleButtonState(WindowInfo *window, Widget w, Boolean state,
4719         Boolean notify)
4720 {
4721     if (IsTopDocument(window)) {
4722     	XmToggleButtonSetState(w, state, notify);
4723     }
4724 }
4725 
4726 /*
4727 ** set/clear menu sensitivity if the calling document is on top.
4728 */
SetSensitive(WindowInfo * window,Widget w,Boolean sensitive)4729 void SetSensitive(WindowInfo *window, Widget w, Boolean sensitive)
4730 {
4731     if (IsTopDocument(window)) {
4732     	XtSetSensitive(w, sensitive);
4733     }
4734 }
4735 
4736 /*
4737 ** Remove redundant expose events on tab bar.
4738 */
CleanUpTabBarExposeQueue(WindowInfo * window)4739 void CleanUpTabBarExposeQueue(WindowInfo *window)
4740 {
4741     XEvent event;
4742     XExposeEvent ev;
4743     int count;
4744 
4745     if (window == NULL)
4746     	return;
4747 
4748     /* remove redundant expose events on tab bar */
4749     count=0;
4750     while (XCheckTypedWindowEvent(TheDisplay, XtWindow(window->tabBar),
4751 	   Expose, &event))
4752 	count++;
4753 
4754     /* now we can update tabbar */
4755     if (count) {
4756 	ev.type = Expose;
4757 	ev.display = TheDisplay;
4758 	ev.window = XtWindow(window->tabBar);
4759 	ev.x = 0;
4760 	ev.y = 0;
4761 	ev.width = XtWidth(window->tabBar);
4762 	ev.height = XtHeight(window->tabBar);
4763 	ev.count = 0;
4764 	XSendEvent(TheDisplay, XtWindow(window->tabBar), False,
4765 		ExposureMask, (XEvent *)&ev);
4766     }
4767 }
4768