Xft support under OpenMotif 2.3.3 - I've been using this for quite a while on
[nedit.git] / source / window.c
blob89473a89c89118244707bae7f9ad832d73bcb4d4
1 static const char CVSID[] = "$Id: window.c,v 1.204 2008/03/03 22:32:24 tringali Exp $";
2 /*******************************************************************************
3 * *
4 * window.c -- Nirvana Editor window creation/deletion *
5 * *
6 * Copyright (C) 1999 Mark Edel *
7 * *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. In addition, you may distribute version of this program linked to *
12 * Motif or Open Motif. See README for details. *
13 * *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
17 * for more details. *
18 * *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
22 * *
23 * Nirvana Text Editor *
24 * May 10, 1991 *
25 * *
26 * Written by Mark Edel *
27 * *
28 *******************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 #include "../config.h"
32 #endif
34 #include "window.h"
35 #include "textBuf.h"
36 #include "textSel.h"
37 #include "text.h"
38 #include "textDisp.h"
39 #include "textP.h"
40 #include "nedit.h"
41 #include "menu.h"
42 #include "file.h"
43 #include "search.h"
44 #include "undo.h"
45 #include "preferences.h"
46 #include "selection.h"
47 #include "server.h"
48 #include "shell.h"
49 #include "macro.h"
50 #include "highlight.h"
51 #include "smartIndent.h"
52 #include "userCmds.h"
53 #include "nedit.bm"
54 #include "n.bm"
55 #include "windowTitle.h"
56 #include "interpret.h"
57 #include "rangeset.h"
58 #include "../util/clearcase.h"
59 #include "../util/misc.h"
60 #include "../util/fileUtils.h"
61 #include "../util/utils.h"
62 #include "../util/fileUtils.h"
63 #include "../util/DialogF.h"
64 #include "../Xlt/BubbleButtonP.h"
65 #include "../Microline/XmL/Folder.h"
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
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 */
115 #ifdef HAVE_DEBUG_H
116 #include "../debug.h"
117 #endif
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
123 /* Thickness of 3D border around statistics and/or incremental search areas
124 below the main menu bar */
125 #define STAT_SHADOW_THICKNESS 1
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};
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};
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};
148 extern void _XmDismissTearOff(Widget, XtPointer, XtPointer);
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);
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);
202 /* From Xt, Shell.c, "BIGSIZE" */
203 static const Dimension XT_IGNORE_PPOSITION = 32767;
206 ** Create a new editor window
208 WindowInfo *CreateWindow(const char *name, char *geometry, int iconic)
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;
223 static Pixmap isrcFind = 0;
224 static Pixmap isrcClear = 0;
225 static Pixmap closeTabPixmap = 0;
227 /* Allocate some memory for the new window data structure */
228 window = (WindowInfo *)XtMalloc(sizeof(WindowInfo));
230 /* initialize window structure */
231 /* + Schwarzenberg: should a
232 memset(window, 0, sizeof(WindowInfo));
233 be added here ?
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 = XtMalloc(strlen(cTypes) + 1)))
287 strcpy(window->backlightCharTypes, cTypes);
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;
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;
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();
352 CreateGeometryString(newGeometry, x, y, 0, 0,
353 bitmask & ~(WidthValue | HeightValue));
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++;
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++;
373 if (newGeometry[0] == '\0')
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.
382 Alternate approach, relying on ShellP.h:
384 ((WMShellWidget)winShell)->shell.client_specified &= ~_XtShellPPositionOK;
387 XtSetArg(al[ac], XtNx, XT_IGNORE_PPOSITION); ac++;
388 XtSetArg(al[ac], XtNy, XT_IGNORE_PPOSITION); ac++;
391 winShell = CreateWidget(TheAppShell, "textShell",
392 topLevelShellWidgetClass, al, ac);
393 window->shell = winShell;
395 #ifdef EDITRES
396 XtAddEventHandler (winShell, (EventMask)0, True,
397 (XtEventHandler)_XEditResCheckMessages, NULL);
398 #endif /* EDITRES */
400 #ifndef SGI_CUSTOM
401 addWindowIcon(winShell);
402 #endif
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);
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);
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. */
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);
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. */
450 if (isrcFind == 0) {
451 isrcFind = createBitmapWithDepth(window->iSearchForm,
452 (char *)isrcFind_bits, isrcFind_width, isrcFind_height);
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);
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);
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);
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);
516 if (isrcClear == 0) {
517 isrcClear = createBitmapWithDepth(window->iSearchForm,
518 (char *)isrcClear_bits, isrcClear_width, isrcClear_height);
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);
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);
553 SetISearchTextCallbacks(window);
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);
566 /* button to close top document */
567 if (closeTabPixmap == 0) {
568 closeTabPixmap = createBitmapWithDepth(tabForm,
569 (char *)close_bits, close_width, close_height);
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);
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);
602 window->tabMenuPane = CreateTabContextMenu(window->tabBar, window);
603 AddTabContextMenuAction(window->tabBar);
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);
614 XtAddCallback(window->tabBar, XmNactivateCallback,
615 raiseTabCB, NULL);
617 window->tab = addTab(window->tabBar, name);
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);
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);
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;
680 /* Give the statsLine the same font as the statsLineColNo */
681 XtVaGetValues(window->statsLineColNo, XmNfontList, &statsFontList, NULL);
682 XtVaSetValues(window->statsLine, XmNfontList, statsFontList, NULL);
684 /* Manage the statsLineForm */
685 if(window->showStats)
686 XtManageChild(window->statsLineForm);
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);
693 /* Create the menu bar */
694 menuBar = CreateMenuBar(mainWin, window);
695 window->menuBar = menuBar;
696 XtManageChild(menuBar);
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);
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);
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);
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;
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));
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);
741 /* cache user menus: init. user background menu cache */
742 InitUserBGMenuCache(&window->userBGMenuCache);
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);
751 /* Attach the buffer to the text widget, and add callbacks for modify */
752 TextSetBuffer(text, window->buffer);
753 BufAddModifyCB(window->buffer, modifiedCB, window);
755 /* Designate the permanent text area as the owner for selections */
756 HandleXSelections(text);
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();
762 /* add the window to the global window list, update the Windows menus */
763 addToWindowList(window);
764 InvalidateWindowMenus();
766 showTabBar = GetShowTabBar(window);
767 if (showTabBar)
768 XtManageChild(tabForm);
770 manageToolBars(statsAreaForm);
772 if (showTabBar || window->showISearchLine ||
773 window->showStats)
774 XtManageChild(statsAreaForm);
776 /* realize all of the widgets in the new window */
777 RealizeWithoutForcingPosition(winShell);
778 XmProcessTraversal(text, XmTRAVERSE_CURRENT);
780 /* Make close command in window menu gracefully prompt for close */
781 AddMotifCloseCallback(winShell, (XtCallbackProc)closeCB, window);
783 /* Make window resizing work in nice character heights */
784 UpdateWMSizeHints(window);
786 /* Set the minimum pane height for the initial text pane */
787 UpdateMinPaneHeights(window);
789 /* create dialogs shared by all documents in a window */
790 CreateFindDlog(window->shell, window);
791 CreateReplaceDlog(window->shell, window);
792 CreateReplaceMultiFileDlog(window);
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);
803 return window;
807 ** ButtonPress event handler for tabs.
809 static void tabClickEH(Widget w, XtPointer clientData, XEvent *event)
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;
816 else {
817 hideTooltip(w);
822 ** add a tab to the tab bar for the new document.
824 static Widget addTab(Widget folder, const char *string)
826 Widget tooltipLabel, tab;
827 XmString s1;
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);
845 /* there's things to do as user click on the tab */
846 XtAddEventHandler(tab, ButtonPressMask, False,
847 (XtEventHandler)tabClickEH, (XtPointer)0);
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);
857 /* put borders around tooltip. BubbleButton use
858 transientShellWidgetClass as tooltip shell, which
859 came without borders */
860 XtVaSetValues(XtParent(tooltipLabel), XmNborderWidth, 1, NULL);
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 */
867 return tab;
871 ** Comparison function for sorting windows by title.
872 ** Windows are sorted by alphabetically by filename and then
873 ** alphabetically by path.
875 static int compareWindowNames(const void *windowA, const void *windowB)
877 int rc;
878 const WindowInfo *a = *((WindowInfo**)windowA);
879 const WindowInfo *b = *((WindowInfo**)windowB);
881 rc = strcmp(a->filename, b->filename);
882 if (rc != 0)
883 return rc;
884 rc = strcmp(a->path, b->path);
885 return rc;
889 ** Sort tabs in the tab bar alphabetically, if demanded so.
891 void SortTabBar(WindowInfo *window)
893 WindowInfo *w;
894 WindowInfo **windows;
895 WidgetList tabList;
896 int i, j, nDoc, tabCount;
898 if (!GetPrefSortTabs())
899 return;
901 /* need more than one tab to sort */
902 nDoc = NDocuments(window);
903 if (nDoc < 2)
904 return;
906 /* first sort the documents */
907 windows = (WindowInfo **)XtMalloc(sizeof(WindowInfo *) * nDoc);
908 for (w=WindowList, i=0; w!=NULL; w=w->next) {
909 if (window->shell == w->shell)
910 windows[i++] = w;
912 qsort(windows, nDoc, sizeof(WindowInfo *), compareWindowNames);
914 /* assign tabs to documents in sorted order */
915 XtVaGetValues(window->tabBar, XmNtabWidgetList, &tabList,
916 XmNtabCount, &tabCount, NULL);
918 for (i=0, j=0; i<tabCount && j<nDoc; i++) {
919 if (tabList[i]->core.being_destroyed)
920 continue;
922 /* set tab as active */
923 if (IsTopDocument(windows[j]))
924 XmLFolderSetActiveTab(window->tabBar, i, False);
926 windows[j]->tab = tabList[i];
927 RefreshTabState(windows[j]);
928 j++;
931 XtFree((char *)windows);
935 ** find which document a tab belongs to
937 WindowInfo *TabToWindow(Widget tab)
939 WindowInfo *win;
940 for (win=WindowList; win; win=win->next) {
941 if (win->tab == tab)
942 return win;
945 return NULL;
949 ** Close a document, or an editor window
951 void CloseWindow(WindowInfo *window)
953 int keepWindow, state;
954 char name[MAXPATHLEN];
955 WindowInfo *win, *topBuf = NULL, *nextBuf = NULL;
957 /* Free smart indent macro programs */
958 EndSmartIndent(window);
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);
965 #ifndef VMS
966 /* Kill shell sub-process and free related memory */
967 AbortShellCommand(window);
968 #endif /*VMS*/
970 /* Unload the default tips files for this language mode if necessary */
971 UnloadLanguageModeTipsFile(window);
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);
981 /* Destroy the file closed property for this file */
982 DeleteFileClosedProperty(window);
984 /* Remove any possibly pending callback which might fire after the
985 widget is gone. */
986 cancelTimeOut(&window->flashTimeoutID);
987 cancelTimeOut(&window->markTimeoutID);
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;
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;
1030 /* Free syntax highlighting patterns, if any. w/o redisplaying */
1031 FreeHighlightingData(window);
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);
1038 #ifdef ROWCOLPATCH
1039 patchRowCol(window->menuBar);
1040 #endif
1042 /* free the undo and redo lists */
1043 ClearUndoList(window);
1044 ClearRedoList(window);
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);
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. */
1066 /* remove the tab of the closing document from tab bar */
1067 XtDestroyWidget(window->tab);
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);
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);
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);
1095 /* free background menu cache for document */
1096 FreeUserBGMenuCache(&window->userBGMenuCache);
1098 /* destroy the document's pane, or the window */
1099 if (nextBuf || topBuf) {
1100 deleteDocument(window);
1101 } else
1103 /* free user menu cache for window */
1104 FreeUserMenuCache(window->userMenuCache);
1106 /* remove and deallocate all of the widgets associated with window */
1107 XtFree(window->backlightCharTypes); /* we made a copy earlier on */
1108 CloseAllPopupsFor(window->shell);
1109 XtDestroyWidget(window->shell);
1112 /* deallocate the window data structure */
1113 XtFree((char*)window);
1117 ** check if tab bar is to be shown on this window
1119 int GetShowTabBar(WindowInfo *window)
1121 if (!GetPrefTabBar())
1122 return False;
1123 else if (NDocuments(window) == 1)
1124 return !GetPrefTabBarHideOne();
1125 else
1126 return True;
1129 void ShowWindowTabBar(WindowInfo *window)
1131 if (GetPrefTabBar()) {
1132 if (GetPrefTabBarHideOne())
1133 ShowTabBar(window, NDocuments(window)>1);
1134 else
1135 ShowTabBar(window, True);
1137 else
1138 ShowTabBar(window, False);
1142 ** Check if there is already a window open for a given file
1144 WindowInfo *FindWindowWithFile(const char *name, const char *path)
1146 WindowInfo* window;
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())
1153 char fullname[MAXPATHLEN + 1];
1154 struct stat attribute;
1156 strncpy(fullname, path, MAXPATHLEN);
1157 strncat(fullname, name, MAXPATHLEN);
1158 fullname[MAXPATHLEN] = '\0';
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;
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. */
1170 #endif
1172 for (window = WindowList; window != NULL; window = window->next) {
1173 if (!strcmp(window->filename, name) && !strcmp(window->path, path)) {
1174 return window;
1177 return NULL;
1181 ** Add another independently scrollable pane to the current document,
1182 ** splitting the pane which currently has keyboard focus.
1184 void SplitPane(WindowInfo *window)
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;
1194 /* Don't create new panes if we're already at the limit */
1195 if (window->nPanes >= MAX_PANES)
1196 return;
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;
1211 /* Unmanage & remanage the panedWindow so it recalculates pane heights */
1212 XtUnmanageChild(window->splitPane);
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);
1222 TextSetBuffer(text, window->buffer);
1223 if (window->highlightData != NULL)
1224 AttachHighlightToWidget(text, window);
1225 if (window->backlightChars)
1227 XtVaSetValues(text, textNbacklightCharTypes,
1228 window->backlightCharTypes, NULL);
1230 XtManageChild(text);
1231 window->textPanes[window->nPanes++] = text;
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 );
1245 /* Set the minimum pane height in the new pane */
1246 UpdateMinPaneHeights(window);
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];
1255 paneHeights[focusPane] = paneHeights[focusPane]/2;
1256 paneHeights[focusPane+1] = paneHeights[focusPane];
1258 for (i=0; i<=window->nPanes; i++) {
1259 text = i==0 ? window->textArea : window->textPanes[i-1];
1260 setPaneDesiredHeight(containingPane(text), paneHeights[i]);
1263 /* Re-manage panedWindow to recalculate pane heights & reset selection */
1264 if (IsTopDocument(window))
1265 XtManageChild(window->splitPane);
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));
1275 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
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);
1284 Widget GetPaneByIndex(WindowInfo *window, int paneIndex)
1286 Widget text = NULL;
1287 if (paneIndex >= 0 && paneIndex <= window->nPanes) {
1288 text = (paneIndex == 0) ? window->textArea : window->textPanes[paneIndex - 1];
1290 return(text);
1293 int WidgetToPaneIndex(WindowInfo *window, Widget w)
1295 int i;
1296 Widget text;
1297 int paneIndex = 0;
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;
1305 return(paneIndex);
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)
1312 void ClosePane(WindowInfo *window)
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;
1320 /* Don't delete the last pane */
1321 if (window->nPanes <= 0)
1322 return;
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;
1337 /* Unmanage & remanage the panedWindow so it recalculates pane heights */
1338 XtUnmanageChild(window->splitPane);
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]));
1348 if (window->nPanes == 0)
1349 window->lastFocus = window->textArea;
1350 else if (focusPane > window->nPanes)
1351 window->lastFocus = window->textPanes[window->nPanes-1];
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];
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]);
1369 if (IsTopDocument(window))
1370 XtManageChild(window->splitPane);
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]);
1378 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
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);
1388 ** Turn on and off the display of line numbers
1390 void ShowLineNumbers(WindowInfo *window, int state)
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;
1399 if (window->showLineNumbers == state)
1400 return;
1401 window->showLineNumbers = state;
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);
1416 for (i=0; i<=window->nPanes; i++) {
1417 text = i==0 ? window->textArea : window->textPanes[i-1];
1418 XtVaSetValues(text, textNlineNumCols, 0, NULL);
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;
1428 win->showLineNumbers = state;
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);
1437 /* Tell WM that the non-expandable part of the window has changed size */
1438 UpdateWMSizeHints(window);
1441 void SetTabDist(WindowInfo *window, int tabDist)
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;
1449 window->ignoreModify = True;
1451 for (paneIndex = 0; paneIndex <= window->nPanes; ++paneIndex) {
1452 Widget w = GetPaneByIndex(window, paneIndex);
1453 textDisp *textD = ((TextWidget)w)->text.textD;
1455 TextGetScroll(w, &saveVScrollPositions[paneIndex], &saveHScrollPositions[paneIndex]);
1456 saveCursorPositions[paneIndex] = TextGetCursorPos(w);
1457 textD->modifyingTabDist = 1;
1460 BufSetTabDistance(window->buffer, tabDist);
1462 for (paneIndex = 0; paneIndex <= window->nPanes; ++paneIndex) {
1463 Widget w = GetPaneByIndex(window, paneIndex);
1464 textDisp *textD = ((TextWidget)w)->text.textD;
1466 textD->modifyingTabDist = 0;
1467 TextSetCursorPos(w, saveCursorPositions[paneIndex]);
1468 TextSetScroll(w, saveVScrollPositions[paneIndex], saveHScrollPositions[paneIndex]);
1471 window->ignoreModify = False;
1475 void SetEmTabDist(WindowInfo *window, int emTabDist)
1477 int i;
1479 XtVaSetValues(window->textArea, textNemulateTabs, emTabDist, NULL);
1480 for (i = 0; i < window->nPanes; ++i) {
1481 XtVaSetValues(window->textPanes[i], textNemulateTabs, emTabDist, NULL);
1486 ** Turn on and off the display of the statistics line
1488 void ShowStatsLine(WindowInfo *window, int state)
1490 WindowInfo *win;
1491 Widget text;
1492 int i;
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);
1502 window->showStats = state;
1503 showStats(window, state);
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;
1515 ** Displays and undisplays the statistics line (regardless of settings of
1516 ** window->showStats or window->modeMessageDisplayed)
1518 static void showStats(WindowInfo *window, int state)
1520 if (state) {
1521 XtManageChild(window->statsLineForm);
1522 showStatsForm(window);
1523 } else {
1524 XtUnmanageChild(window->statsLineForm);
1525 showStatsForm(window);
1528 /* Tell WM that the non-expandable part of the window has changed size */
1529 /* Already done in showStatsForm */
1530 /* UpdateWMSizeHints(window); */
1535 static void showTabBar(WindowInfo *window, int state)
1537 if (state) {
1538 XtManageChild(XtParent(window->tabBar));
1539 showStatsForm(window);
1540 } else {
1541 XtUnmanageChild(XtParent(window->tabBar));
1542 showStatsForm(window);
1548 void ShowTabBar(WindowInfo *window, int state)
1550 if (XtIsManaged(XtParent(window->tabBar)) == state)
1551 return;
1552 showTabBar(window, state);
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)
1559 void ShowISearchLine(WindowInfo *window, int state)
1561 WindowInfo *win;
1563 if (window->showISearchLine == state)
1564 return;
1565 window->showISearchLine = state;
1566 showISearch(window, state);
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;
1578 ** Temporarily show and hide the incremental search line if the line is not
1579 ** already up.
1581 void TempShowISearch(WindowInfo *window, int state)
1583 if (window->showISearchLine)
1584 return;
1585 if (XtIsManaged(window->iSearchForm) != state)
1586 showISearch(window, state);
1590 ** Put up or pop-down the incremental search line regardless of settings
1591 ** of showISearchLine or TempShowISearch
1593 static void showISearch(WindowInfo *window, int state)
1595 if (state) {
1596 XtManageChild(window->iSearchForm);
1597 showStatsForm(window);
1598 } else {
1599 XtUnmanageChild(window->iSearchForm);
1600 showStatsForm(window);
1603 /* Tell WM that the non-expandable part of the window has changed size */
1604 /* This is already done in showStatsForm */
1605 /* UpdateWMSizeHints(window); */
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
1612 static void showStatsForm(WindowInfo *window)
1614 Widget statsAreaForm = XtParent(window->statsLineForm);
1615 Widget mainW = XtParent(statsAreaForm);
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);
1642 /* Tell WM that the non-expandable part of the window has changed size */
1643 UpdateWMSizeHints(window);
1647 ** Display a special message in the stats line (show the stats line if it
1648 ** is not currently shown).
1650 void SetModeMessage(WindowInfo *window, const char *message)
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 XtFree(window->modeMessage);
1657 window->modeMessage = XtNewString(message);
1659 if (!IsTopDocument(window))
1660 return;
1662 XmTextSetString(window->statsLine, (char*)message);
1664 * Don't invoke the stats line again, if stats line is already displayed.
1666 if (!window->showStats)
1667 showStats(window, True);
1671 ** Clear special statistics line message set in SetModeMessage, returns
1672 ** the statistics line to its original state as set in window->showStats
1674 void ClearModeMessage(WindowInfo *window)
1676 if (!window->modeMessageDisplayed)
1677 return;
1679 window->modeMessageDisplayed = False;
1680 XtFree(window->modeMessage);
1681 window->modeMessage = NULL;
1683 if (!IsTopDocument(window))
1684 return;
1687 * Remove the stats line only if indicated by it's window state.
1689 if (!window->showStats)
1690 showStats(window, False);
1691 UpdateStatsLine(window);
1695 ** Count the windows
1697 int NWindows(void)
1699 WindowInfo *win;
1700 int n;
1702 for (win=WindowList, n=0; win!=NULL; win=win->next, n++);
1703 return n;
1707 ** Set autoindent state to one of NO_AUTO_INDENT, AUTO_INDENT, or SMART_INDENT.
1709 void SetAutoIndent(WindowInfo *window, int state)
1711 int autoIndent = state == AUTO_INDENT, smartIndent = state == SMART_INDENT;
1712 int i;
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);
1733 ** Set showMatching state to one of NO_FLASH, FLASH_DELIMIT or FLASH_RANGE.
1734 ** Update the menu to reflect the change of state.
1736 void SetShowMatching(WindowInfo *window, int state)
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);
1750 ** Update the "New (in X)" menu item to reflect the preferences
1752 void UpdateNewOppositeMenu(WindowInfo *window, int openInTab)
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);
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.
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.
1779 void SetFonts(WindowInfo *window, const char *fontName, const char *italicName,
1780 const char *boldName, const char *boldItalicName)
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;
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;
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;
1812 borderWidth = oldWindowWidth - oldTextWidth;
1813 borderHeight = oldWindowHeight - oldTextHeight;
1814 oldFontWidth = oldFont->max_bounds.width;
1815 oldFontHeight = textD->ascent + textD->descent;
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);
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);
1840 /* Change the primary font in all the widgets */
1841 if (primaryChanged) {
1842 font = GetDefaultFontStruct(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);
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);
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);
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(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);
1873 /* Change the minimum pane height */
1874 UpdateMinPaneHeights(window);
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)
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;
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 );
1919 /* Redo any syntax highlighting */
1920 if (window->highlightData != NULL)
1921 UpdateHighlightStyles(window);
1925 ** Set insert/overstrike mode
1927 void SetOverstrike(WindowInfo *window, int overstrike)
1929 int i;
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;
1938 ** Select auto-wrap mode, one of NO_WRAP, NEWLINE_WRAP, or CONTINUOUS_WRAP
1940 void SetAutoWrap(WindowInfo *window, int state)
1942 int i;
1943 int autoWrap = state == NEWLINE_WRAP, contWrap = state == CONTINUOUS_WRAP;
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;
1952 if (IsTopDocument(window)) {
1953 XmToggleButtonSetState(window->newlineWrapItem, autoWrap, False);
1954 XmToggleButtonSetState(window->continuousWrapItem, contWrap, False);
1955 XmToggleButtonSetState(window->noWrapItem, state == NO_WRAP, False);
1960 ** Set the auto-scroll margin
1962 void SetAutoScroll(WindowInfo *window, int margin)
1964 int i;
1966 XtVaSetValues(window->textArea, textNcursorVPadding, margin, NULL);
1967 for (i=0; i<window->nPanes; i++)
1968 XtVaSetValues(window->textPanes[i], textNcursorVPadding, margin, NULL);
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.
1978 ** To support action routine in tabbed windows, a copy of the window
1979 ** pointer is also store in the splitPane widget.
1981 WindowInfo *WidgetToWindow(Widget w)
1983 WindowInfo *window = NULL;
1984 Widget parent;
1986 while (True) {
1987 /* return window pointer of document */
1988 if (XtClass(w) == xmPanedWindowWidgetClass)
1989 break;
1991 if (XtClass(w) == topLevelShellWidgetClass) {
1992 WidgetList items;
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;
2001 parent = XtParent(w);
2002 if (parent == NULL)
2003 return NULL;
2005 /* make sure it is not a dialog shell */
2006 if (XtClass(parent) == topLevelShellWidgetClass &&
2007 XmIsMainWindow(w))
2008 break;
2010 w = parent;
2013 XtVaGetValues(w, XmNuserData, &window, NULL);
2015 return window;
2019 ** Change the window appearance and the window data structure to show
2020 ** that the file it contains has been modified
2022 void SetWindowModified(WindowInfo *window, int modified)
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);
2037 ** Update the window title to reflect the filename, read-only, and modified
2038 ** status of the window data structure
2040 void UpdateWindowTitle(const WindowInfo *window)
2042 char *iconTitle, *title;
2044 if (!IsTopDocument(window))
2045 return;
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());
2061 iconTitle = XtMalloc(strlen(window->filename) + 2); /* strlen("*")+1 */
2063 strcpy(iconTitle, window->filename);
2064 if (window->fileChanged)
2065 strcat(iconTitle, "*");
2066 XtVaSetValues(window->shell, XmNtitle, title, XmNiconName, iconTitle, NULL);
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);
2074 if (window->replaceDlog && XmToggleButtonGetState(window->replaceKeepBtn)) {
2075 sprintf(title, "Replace (in %s)", window->filename);
2076 XtVaSetValues(XtParent(window->replaceDlog), XmNtitle, title, NULL);
2078 XtFree(iconTitle);
2080 /* Update the Windows menus with the new name */
2081 InvalidateWindowMenus();
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.
2089 void UpdateWindowReadOnly(WindowInfo *window)
2091 int i, state;
2093 if (!IsTopDocument(window))
2094 return;
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));
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.
2110 int GetSimpleSelection(textBuffer *buf, int *left, int *right)
2112 int selStart, selEnd, isRect, rectStart, rectEnd, lineStart;
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);
2125 *left = selStart;
2126 *right = selEnd;
2127 return True;
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.
2137 void MakeSelectionVisible(WindowInfo *window, Widget textPane)
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;
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;
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);
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);
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);
2220 /* make sure that the statistics line is up to date */
2221 UpdateStatsLine(window);
2224 static Widget createTextArea(Widget parent, WindowInfo *window, int rows,
2225 int cols, int emTabDist, char *delimiters, int wrapMargin,
2226 int lineNumCols)
2228 Widget text, sw, hScrollBar, vScrollBar, frame;
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(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);
2261 XtVaSetValues(sw, XmNworkWindow, frame, XmNhorizontalScrollBar,
2262 hScrollBar, XmNverticalScrollBar, vScrollBar, NULL);
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);
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));
2278 /* compensate for Motif delete/backspace problem */
2279 RemapDeleteKey(text);
2281 /* Augment translation table for right button popup menu */
2282 AddBGMenuAction(text);
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);
2289 return text;
2292 static void movedCB(Widget w, WindowInfo *window, XtPointer callData)
2294 TextWidget textWidget = (TextWidget) w;
2296 if (window->ignoreModify)
2297 return;
2299 /* update line and column nubers in statistics line */
2300 UpdateStatsLine(window);
2302 /* Check the character before the cursor for matchable characters */
2303 FlashMatching(window, w);
2305 /* Check for changes to read-only status and/or file modifications */
2306 CheckForChangesToFile(window);
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)
2315 /* Start blinking the caret again. */
2316 ResetCursorBlink(textWidget, False);
2320 static void modifiedCB(int pos, int nInserted, int nDeleted, int nRestyled,
2321 const char *deletedText, void *cbArg)
2323 WindowInfo *window = (WindowInfo *)cbArg;
2324 int selected = window->buffer->primary.selected;
2326 /* update the table of bookmarks */
2327 if (!window->ignoreModify) {
2328 UpdateMarkTable(window, pos, nInserted, nDeleted);
2331 /* Check and dim/undim selection related menu items */
2332 if ((window->wasSelected && !selected) ||
2333 (!window->wasSelected && selected)) {
2334 window->wasSelected = selected;
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
2352 DimSelectionDepUserMenuItems(window, selected);
2353 if (window->replaceDlog != NULL && XtIsManaged(window->replaceDlog))
2355 UpdateReplaceActionButtons(window);
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;
2365 /* Make sure line number display is sufficient for new data */
2366 updateLineNumDisp(window);
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);
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;
2381 /* Indicate that the window has now been modified */
2382 SetWindowModified(window, TRUE);
2384 /* Update # of bytes, and line and col statistics */
2385 UpdateStatsLine(window);
2387 /* Check if external changes have been made to file and warn user */
2388 CheckForChangesToFile(window);
2391 static void focusCB(Widget w, WindowInfo *window, XtPointer callData)
2393 /* record which window pane last had the keyboard focus */
2394 window->lastFocus = w;
2396 /* update line number statistic to reflect current focus pane */
2397 UpdateStatsLine(window);
2399 /* finish off the current incremental search */
2400 EndISearch(window);
2402 /* Check for changes to read-only status and/or file modifications */
2403 CheckForChangesToFile(window);
2406 static void dragStartCB(Widget w, WindowInfo *window, XtPointer callData)
2408 /* don't record all of the intermediate drag steps for undo */
2409 window->ignoreModify = True;
2412 static void dragEndCB(Widget w, WindowInfo *window, dragEndCBStruct *callData)
2414 /* restore recording of undo information */
2415 window->ignoreModify = False;
2417 /* Do nothing if drag operation was canceled */
2418 if (callData->nCharsInserted == 0)
2419 return;
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);
2427 static void closeCB(Widget w, WindowInfo *window, XtPointer callData)
2429 window = WidgetToWindow(w);
2430 if (!WindowCanBeClosed(window)) {
2431 return;
2434 CloseDocumentWindow(w, window, callData);
2437 #ifndef NO_SESSION_RESTART
2438 static void saveYourselfCB(Widget w, Widget appShell, XtPointer callData)
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;
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++;
2459 argv = (char **)XtMalloc(maxArgc*sizeof(char *));
2460 revWindowList = (WindowInfo **)XtMalloc(sizeof(WindowInfo *)*nWindows);
2461 for (win=WindowList, i=nWindows-1; win!=NULL; win=win->next, i--)
2462 revWindowList[i] = win;
2464 /* Create command line arguments for restoring each window in the list */
2465 argv[argc++] = XtNewString(ArgV0);
2466 if (IsServer) {
2467 argv[argc++] = XtNewString("-server");
2468 if (GetPrefServerName()[0] != '\0') {
2469 argv[argc++] = XtNewString("-svrname");
2470 argv[argc++] = XtNewString(GetPrefServerName());
2474 /* editor windows are popup-shell children of top-level appShell */
2475 XtVaGetValues(appShell, XmNchildren, &children,
2476 XmNnumChildren, &nItems, NULL);
2478 for (n=nItems-1; n>=0; n--) {
2479 WidgetList tabs;
2480 int tabCount;
2482 if (strcmp(XtName(children[n]), "textShell") ||
2483 ((topWin = WidgetToWindow(children[n])) == NULL))
2484 continue; /* skip non-editor windows */
2486 /* create a group for each window */
2487 getGeometryString(topWin, geometry);
2488 argv[argc++] = XtNewString("-group");
2489 argv[argc++] = XtNewString("-geometry");
2490 argv[argc++] = XtNewString(geometry);
2491 if (IsIconic(topWin)) {
2492 argv[argc++] = XtNewString("-iconic");
2493 wasIconic = True;
2494 } else if (wasIconic) {
2495 argv[argc++] = XtNewString("-noiconic");
2496 wasIconic = False;
2499 /* add filename of each tab in window... */
2500 XtVaGetValues(topWin->tabBar, XmNtabWidgetList, &tabs,
2501 XmNtabCount, &tabCount, NULL);
2503 for (i=0; i< tabCount; i++) {
2504 win = TabToWindow(tabs[i]);
2505 if (win->filenameSet) {
2506 /* add filename */
2507 argv[argc] = XtMalloc(strlen(win->path) +
2508 strlen(win->filename) + 1);
2509 sprintf(argv[argc++], "%s%s", win->path, win->filename);
2514 XtFree((char *)revWindowList);
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 XtFree(argv[i]);
2520 XtFree((char *)argv);
2523 void AttachSessionMgrHandler(Widget appShell)
2525 static Atom wmpAtom, syAtom = 0;
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);
2533 XmAddProtocolCallback(appShell, wmpAtom, syAtom,
2534 (XtCallbackProc)saveYourselfCB, (XtPointer)appShell);
2536 #endif /* NO_SESSION_RESTART */
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).
2543 int IsIconic(WindowInfo *window)
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;
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 XtFree((char *)property);
2562 return result;
2566 ** Add a window to the the window list.
2568 static void addToWindowList(WindowInfo *window)
2570 WindowInfo *temp;
2572 temp = WindowList;
2573 WindowList = window;
2574 window->next = temp;
2578 ** Remove a window from the list of windows
2580 static void removeFromWindowList(WindowInfo *window)
2582 WindowInfo *temp;
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;
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.
2600 ** (Iteration taken from NDocuments(); is there a better way to do it?)
2602 static int updateGutterWidth(WindowInfo* window)
2604 WindowInfo* document;
2605 int reqCols = MIN_LINE_NUM_COLS;
2606 int newColsDiff = 0;
2607 int maxCols = 0;
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;
2615 XtVaGetValues(document->textArea,
2616 textNlineNumCols, &lineNumCols,
2617 NULL);
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. */
2623 if (lineNumCols > maxCols) {
2624 maxCols = lineNumCols;
2627 tmpReqCols = textD->nBufferLines < 1
2629 : (int) log10((double) textD->nBufferLines + 1) + 1;
2631 if (tmpReqCols > reqCols) {
2632 reqCols = tmpReqCols;
2637 if (reqCols != maxCols) {
2638 XFontStruct *fs;
2639 Dimension windowWidth;
2640 short fontWidth;
2642 newColsDiff = reqCols - maxCols;
2644 XtVaGetValues(window->textArea, textNfont, &fs, NULL);
2645 fontWidth = fs->max_bounds.width;
2647 XtVaGetValues(window->shell, XmNwidth, &windowWidth, NULL);
2648 XtVaSetValues(window->shell,
2649 XmNwidth, (Dimension) windowWidth + (newColsDiff * fontWidth),
2650 NULL);
2652 UpdateWMSizeHints(window);
2655 for (document = WindowList; NULL != document; document = document->next) {
2656 if (document->shell == window->shell) {
2657 Widget text;
2658 int i;
2659 int lineNumCols;
2661 XtVaGetValues(document->textArea,
2662 textNlineNumCols, &lineNumCols, NULL);
2664 if (lineNumCols == reqCols) {
2665 continue;
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);
2676 return reqCols;
2680 ** If necessary, enlarges the window and line number display area to make
2681 ** room for numbers.
2683 static int updateLineNumDisp(WindowInfo* window)
2685 if (!window->showLineNumbers) {
2686 return 0;
2689 /* Decide how wide the line number field has to be to display all
2690 possible line numbers */
2691 return updateGutterWidth(window);
2695 ** Update the optional statistics line.
2697 void UpdateStatsLine(WindowInfo *window)
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
2707 if (!IsTopDocument(window))
2708 return;
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;
2715 /* Compose the string to display. If line # isn't available, leave it off */
2716 pos = TextGetCursorPos(window->lastFocus);
2717 string = XtMalloc(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);
2735 /* Update the line/column number */
2736 xmslinecol = XmStringCreateSimple(slinecol);
2737 XtVaSetValues( window->statsLineColNo,
2738 XmNlabelString, xmslinecol, NULL );
2739 XmStringFree(xmslinecol);
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, '/');
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
2761 XtFree(string);
2763 /* Update the line/col display */
2764 xmslinecol = XmStringCreateSimple(slinecol);
2765 XtVaSetValues(window->statsLineColNo,
2766 XmNlabelString, xmslinecol, NULL);
2767 XmStringFree(xmslinecol);
2770 static Boolean currentlyBusy = False;
2771 static long busyStartTime = 0;
2772 static Boolean modeMessageSet = False;
2775 * Auxiliary function for measuring elapsed time during busy waits.
2777 static long getRelTimeInTenthsOfSeconds()
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
2790 void AllWindowsBusy(const char *message)
2792 WindowInfo *w;
2794 if (!currentlyBusy)
2796 busyStartTime = getRelTimeInTenthsOfSeconds();
2797 modeMessageSet = False;
2799 for (w=WindowList; w!=NULL; w=w->next)
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.
2809 BeginWait(w->shell);
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);
2817 modeMessageSet = True;
2819 BusyWait(WindowList->shell);
2821 currentlyBusy = True;
2824 void AllWindowsUnbusy(void)
2826 WindowInfo *w;
2828 for (w=WindowList; w!=NULL; w=w->next)
2830 ClearModeMessage(w);
2831 EndWait(w->shell);
2834 currentlyBusy = False;
2835 modeMessageSet = False;
2836 busyStartTime = 0;
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.
2847 static void setPaneDesiredHeight(Widget w, int height)
2849 ((XmPanedWindowConstraintPtr)w->core.constraints)->panedw.dheight = height;
2851 static void setPaneMinHeight(Widget w, int min)
2853 ((XmPanedWindowConstraintPtr)w->core.constraints)->panedw.min = min;
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.
2863 void UpdateWMSizeHints(WindowInfo *window)
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;
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;
2877 /* Find the base (non-expandable) width and height of the editor window.
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.
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;
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;
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);
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. */
2922 RemovePPositionHint(window->shell);
2926 ** Update the minimum allowable height for a split pane after a change
2927 ** to font or margin height.
2929 void UpdateMinPaneHeights(WindowInfo *window)
2931 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
2932 Dimension hsbHeight, swMarginHeight,frameShadowHeight;
2933 int i, marginHeight, minPaneHeight;
2934 Widget hScrollBar;
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;
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);
2954 /* Add an icon to an applicaction shell widget. addWindowIcon adds a large
2955 ** (primary window) icon, AddSmallIcon adds a small (secondary window) icon.
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.
2961 static void addWindowIcon(Widget shell)
2963 static Pixmap iconPixmap = 0, maskPixmap = 0;
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);
2973 XtVaSetValues(shell, XmNiconPixmap, iconPixmap, XmNiconMask, maskPixmap,
2974 NULL);
2976 void AddSmallIcon(Widget shell)
2978 static Pixmap iconPixmap = 0, maskPixmap = 0;
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);
2988 XtVaSetValues(shell, XmNiconPixmap, iconPixmap,
2989 XmNiconMask, maskPixmap, NULL);
2993 ** Create pixmap per the widget's color depth setting.
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.
3000 static Pixmap createBitmapWithDepth(Widget w, char *data, unsigned int width,
3001 unsigned int height)
3003 Pixmap pixmap;
3004 Pixel fg, bg;
3005 int depth;
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);
3013 return pixmap;
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.
3021 static void getGeometryString(WindowInfo *window, char *geomString)
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);
3028 /* Find the width and height from the window of the shell */
3029 XGetGeometry(dpy, w, &root, &x, &y, &width, &height, &bw, &depth);
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;
3044 XGetGeometry(dpy, w, &root, &x, &y, &dummyW, &dummyH, &bw, &depth);
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;
3054 /* Write the string */
3055 CreateGeometryString(geomString, x, y, width, height,
3056 XValue | YValue | WidthValue | HeightValue);
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.
3066 static void wmSizeUpdateProc(XtPointer clientData, XtIntervalId *id)
3068 UpdateWMSizeHints((WindowInfo *)clientData);
3071 #ifdef ROWCOLPATCH
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
3083 static void patchRowCol(Widget w)
3085 ((XmRowColumnClassRec *)XtClass(w))->composite_class.delete_child =
3086 patchedRemoveChild;
3088 static void patchedRemoveChild(Widget child)
3090 /* Call composite class method instead of broken row col delete_child
3091 method */
3092 (*((CompositeWidgetClass)compositeWidgetClass)->composite_class.
3093 delete_child) (child);
3095 #endif /* ROWCOLPATCH */
3098 ** Set the backlight character class string
3100 void SetBacklightChars(WindowInfo *window, char *applyBacklightTypes)
3102 int i;
3103 int is_applied = XmToggleButtonGetState(window->backlightCharsItem) ? 1 : 0;
3104 int do_apply = applyBacklightTypes ? 1 : 0;
3106 window->backlightChars = do_apply;
3108 XtFree(window->backlightCharTypes);
3109 if (window->backlightChars &&
3110 (window->backlightCharTypes = XtMalloc(strlen(applyBacklightTypes)+1)))
3111 strcpy(window->backlightCharTypes, applyBacklightTypes);
3112 else
3113 window->backlightCharTypes = NULL;
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);
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.
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.
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.
3138 ** Note: XtManage/XtUnmange the target child (toolbar) before calling this
3139 ** function.
3141 ** Returns the last toolbar widget managed.
3144 static Widget manageToolBars(Widget toolBarsForm)
3146 Widget topWidget = NULL;
3147 WidgetList children;
3148 int n, nItems=0;
3150 XtVaGetValues(toolBarsForm, XmNchildren, &children,
3151 XmNnumChildren, &nItems, NULL);
3153 for (n=0; n<nItems; n++) {
3154 Widget tbar = children[n];
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);
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);
3175 topWidget = tbar;
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]);
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);
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]);
3197 if (topWidget) {
3198 if (strcmp(XtName(topWidget), "TOOLBAR_SEP")) {
3199 XtVaSetValues(topWidget,
3200 XmNbottomAttachment, XmATTACH_FORM,
3201 XmNbottomOffset, STAT_SHADOW_THICKNESS,
3202 NULL);
3204 else {
3205 /* is a separator */
3206 Widget wgt;
3207 XtVaGetValues(topWidget, XmNtopWidget, &wgt, NULL);
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);
3218 return topWidget;
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.
3225 static void getTextPaneDimension(WindowInfo *window, int *nRows, int *nCols)
3227 Widget hScrollBar;
3228 Dimension hScrollBarHeight, paneHeight;
3229 int marginHeight, marginWidth, totalHeight, fontHeight;
3230 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
3232 /* width is the same for panes */
3233 XtVaGetValues(window->textArea, textNcolumns, nCols, NULL);
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;
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.
3253 WindowInfo* CreateDocument(WindowInfo* shellWindow, const char* name)
3255 Widget pane, text;
3256 WindowInfo *window;
3257 int nCols, nRows;
3259 /* Allocate some memory for the new window data structure */
3260 window = (WindowInfo *)XtMalloc(sizeof(WindowInfo));
3262 /* inherit settings and later reset those required */
3263 memcpy(window, shellWindow, sizeof(WindowInfo));
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
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 = XtMalloc(strlen(cTypes) + 1)))
3321 strcpy(window->backlightCharTypes, cTypes);
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;
3362 if (window->fontList == NULL)
3363 XtVaGetValues(shellWindow->statsLine, XmNfontList,
3364 &window->fontList,NULL);
3366 getTextPaneDimension(shellWindow, &nRows, &nCols);
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;
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);
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);
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;
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));
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);
3424 /* cache user menus: init. user background menu cache */
3425 InitUserBGMenuCache(&window->userBGMenuCache);
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);
3434 /* Attach the buffer to the text widget, and add callbacks for modify */
3435 TextSetBuffer(text, window->buffer);
3436 BufAddModifyCB(window->buffer, modifiedCB, window);
3438 /* Designate the permanent text area as the owner for selections */
3439 HandleXSelections(text);
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);
3446 /* add the window to the global window list, update the Windows menus */
3447 InvalidateWindowMenus();
3448 addToWindowList(window);
3450 #ifdef LESSTIF_VERSION
3451 /* FIXME: Temporary workaround for disappearing-text-window bug
3452 when linking to Lesstif.
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));
3466 #endif /* LESSTIF_VERSION */
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);
3474 return window;
3478 ** return the next/previous docment on the tab list.
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.
3485 static WindowInfo *getNextTabWindow(WindowInfo *window, int direction,
3486 int crossWin, int wrap)
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);
3495 if (nBuf <= 1)
3496 return NULL;
3498 /* get the list of tabs */
3499 tabs = (WidgetList)XtMalloc(sizeof(Widget) * nBuf);
3500 tabTotalCount = 0;
3501 if (crossWin) {
3502 int n, nItems;
3503 WidgetList children;
3505 XtVaGetValues(TheAppShell, XmNchildren, &children,
3506 XmNnumChildren, &nItems, NULL);
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 */
3514 XtVaGetValues(win->tabBar, XmNtabWidgetList, &tabList,
3515 XmNtabCount, &tabCount, NULL);
3517 for (i=0; i< tabCount; i++) {
3518 tabs[tabTotalCount++] = tabList[i];
3522 else {
3523 /* get list of tabs in this window */
3524 XtVaGetValues(window->tabBar, XmNtabWidgetList, &tabList,
3525 XmNtabCount, &tabCount, NULL);
3527 for (i=0; i< tabCount; i++) {
3528 if (TabToWindow(tabList[i])) /* make sure tab is valid */
3529 tabs[tabTotalCount++] = tabList[i];
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;
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;
3556 /* return the document where the next tab belongs to */
3557 win = TabToWindow(tabs[nextPos]);
3558 XtFree((char *)tabs);
3559 return win;
3563 ** return the integer position of a tab in the tabbar it
3564 ** belongs to, or -1 if there's an error, somehow.
3566 static int getTabPosition(Widget tab)
3568 WidgetList tabList;
3569 int i, tabCount;
3570 Widget tabBar = XtParent(tab);
3572 XtVaGetValues(tabBar, XmNtabWidgetList, &tabList,
3573 XmNtabCount, &tabCount, NULL);
3575 for (i=0; i< tabCount; i++) {
3576 if (tab == tabList[i])
3577 return i;
3580 return -1; /* something is wrong! */
3584 ** update the tab label, etc. of a tab, per the states of it's document.
3586 void RefreshTabState(WindowInfo *win)
3588 XmString s1, tipString;
3589 char labelString[MAXPATHLEN];
3590 char *tag = XmFONTLIST_DEFAULT_TAG;
3591 unsigned char alignment;
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? "*" : "");
3606 /* Make the top document stand out a little more */
3607 if (IsTopDocument(win))
3608 tag = "BOLD";
3610 s1 = XmStringCreateLtoR(labelString, tag);
3612 if (GetPrefShowPathInWindowsMenu() && win->filenameSet) {
3613 strcat(labelString, " - ");
3614 strcat(labelString, win->path);
3616 tipString=XmStringCreateSimple(labelString);
3618 XtVaSetValues(win->tab,
3619 XltNbubbleString, tipString,
3620 XmNlabelString, s1,
3621 NULL);
3622 XmStringFree(s1);
3623 XmStringFree(tipString);
3627 ** close all the documents in a window
3629 int CloseAllDocumentInWindow(WindowInfo *window)
3631 WindowInfo *win;
3633 if (NDocuments(window) == 1) {
3634 /* only one document in the window */
3635 return CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE);
3637 else {
3638 Widget winShell = window->shell;
3639 WindowInfo *topDocument;
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;
3649 else
3650 win = win->next;
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;
3658 if (win) {
3659 topDocument = GetTopDocument(winShell);
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;
3669 else
3670 win = win->next;
3673 /* close the last document and its window */
3674 if (!CloseFileAndWindow(topDocument, PROMPT_SBC_DIALOG_RESPONSE))
3675 return False;
3679 return True;
3682 static void CloseDocumentWindow(Widget w, WindowInfo *window, XtPointer callData)
3684 int nDocuments = NDocuments(window);
3686 if (nDocuments == NWindows()) {
3687 /* this is only window, then exit */
3688 XtCallActionProc(WindowList->lastFocus, "exit",
3689 ((XmAnyCallbackStruct *)callData)->event, NULL, 0);
3691 else {
3692 if (nDocuments == 1) {
3693 CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE);
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");
3701 if (resp == 1)
3702 CloseAllDocumentInWindow(window);
3708 ** Refresh the menu entries per the settings of the
3709 ** top document.
3711 void RefreshMenuToggleStates(WindowInfo *window)
3713 WindowInfo *win;
3715 if (!IsTopDocument(window))
3716 return;
3718 /* File menu */
3719 XtSetSensitive(window->printSelItem, window->wasSelected);
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);
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);
3744 XtSetSensitive(window->smartIndentItem,
3745 SmartIndentMacrosAvailable(LanguageModeName(window->languageMode)));
3747 SetAutoIndent(window, window->indentStyle);
3748 SetAutoWrap(window, window->wrapMode);
3749 SetShowMatching(window, window->showMatchingStyle);
3750 SetLanguageMode(window, window->languageMode, FALSE);
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);
3758 for (win=WindowList; win; win=win->next)
3759 if (win->shell != window->shell)
3760 break;
3761 XtSetSensitive(window->moveDocumentItem, win != NULL);
3765 ** Refresh the various settings/state of the shell window per the
3766 ** settings of the top document.
3768 static void refreshMenuBar(WindowInfo *window)
3770 RefreshMenuToggleStates(window);
3772 /* Add/remove language specific menu items */
3773 UpdateUserMenus(window);
3775 /* refresh selection-sensitive menus */
3776 DimSelectionDepUserMenuItems(window, window->wasSelected);
3780 ** remember the last document.
3782 WindowInfo *MarkLastDocument(WindowInfo *window)
3784 WindowInfo *prev = lastFocusDocument;
3786 if (window)
3787 lastFocusDocument = window;
3789 return prev;
3793 ** remember the active (top) document.
3795 WindowInfo *MarkActiveDocument(WindowInfo *window)
3797 WindowInfo *prev = inFocusDocument;
3799 if (window)
3800 inFocusDocument = window;
3802 return prev;
3806 ** Bring up the next window by tab order
3808 void NextDocument(WindowInfo *window)
3810 WindowInfo *win;
3812 if (WindowList->next == NULL)
3813 return;
3815 win = getNextTabWindow(window, 1, GetPrefGlobalTabNavigate(), 1);
3816 if (win == NULL)
3817 return;
3819 if (window->shell == win->shell)
3820 RaiseDocument(win);
3821 else
3822 RaiseFocusDocumentWindow(win, True);
3826 ** Bring up the previous window by tab order
3828 void PreviousDocument(WindowInfo *window)
3830 WindowInfo *win;
3832 if (WindowList->next == NULL)
3833 return;
3835 win = getNextTabWindow(window, -1, GetPrefGlobalTabNavigate(), 1);
3836 if (win == NULL)
3837 return;
3839 if (window->shell == win->shell)
3840 RaiseDocument(win);
3841 else
3842 RaiseFocusDocumentWindow(win, True);
3846 ** Bring up the last active window
3848 void LastDocument(WindowInfo *window)
3850 WindowInfo *win;
3852 for(win = WindowList; win; win=win->next)
3853 if (lastFocusDocument == win)
3854 break;
3856 if (!win)
3857 return;
3859 if (window->shell == win->shell)
3860 RaiseDocument(win);
3861 else
3862 RaiseFocusDocumentWindow(win, True);
3867 ** make sure window is alive is kicking
3869 int IsValidWindow(WindowInfo *window)
3871 WindowInfo *win;
3873 for(win = WindowList; win; win=win->next)
3874 if (window == win)
3875 return True;
3878 return False;
3882 ** raise the document and its shell window and focus depending on pref.
3884 void RaiseDocumentWindow(WindowInfo *window)
3886 if (!window)
3887 return;
3889 RaiseDocument(window);
3890 RaiseShellWindow(window->shell, GetPrefFocusOnRaise());
3894 ** raise the document and its shell window and optionally focus.
3896 void RaiseFocusDocumentWindow(WindowInfo *window, Boolean focus)
3898 if (!window)
3899 return;
3901 RaiseDocument(window);
3902 RaiseShellWindow(window->shell, focus);
3906 ** Redisplay menu tearoffs previously hid by hideTearOffs()
3908 static void redisplayTearOffs(Widget menuPane)
3910 WidgetList itemList;
3911 Widget subMenuID;
3912 Cardinal nItems;
3913 int n;
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);
3925 /* redisplay tearoff for this menu */
3926 if (!XmIsMenuShell(XtParent(menuPane)))
3927 ShowHiddenTearOff(menuPane);
3931 ** hide all the tearoffs spawned from this menu.
3932 ** It works recursively to close the tearoffs of the submenus
3934 static void hideTearOffs(Widget menuPane)
3936 WidgetList itemList;
3937 Widget subMenuID;
3938 Cardinal nItems;
3939 int n;
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);
3951 /* hide tearoff for this menu */
3952 if (!XmIsMenuShell(XtParent(menuPane)))
3953 XtUnmapWidget(XtParent(menuPane));
3957 ** Raise a tabbed document within its shell window.
3959 ** NB: use RaiseDocumentWindow() to raise the doc and
3960 ** its shell window.
3962 void RaiseDocument(WindowInfo *window)
3964 WindowInfo *win, *lastwin;
3966 if (!window || !WindowList)
3967 return;
3969 lastwin = MarkActiveDocument(window);
3970 if (lastwin != window && IsValidWindow(lastwin))
3971 MarkLastDocument(lastwin);
3973 /* document already on top? */
3974 XtVaGetValues(window->mainWin, XmNuserData, &win, NULL);
3975 if (win == window)
3976 return;
3978 /* set the document as top document */
3979 XtVaSetValues(window->mainWin, XmNuserData, window, NULL);
3981 /* show the new top document */
3982 XtVaSetValues(window->mainWin, XmNworkWindow, window->splitPane, NULL);
3983 XtManageChild(window->splitPane);
3984 XRaiseWindow(TheDisplay, XtWindow(window->splitPane));
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);
3994 /* put away the bg menu tearoffs of last active document */
3995 hideTearOffs(win->bgMenuPane);
3997 /* restore the bg menu tearoffs of active document */
3998 redisplayTearOffs(window->bgMenuPane);
4000 /* set tab as active */
4001 XmLFolderSetActiveTab(window->tabBar,
4002 getTabPosition(window->tab), False);
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);
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);
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);
4025 /* put away the bg menu tearoffs of last active document */
4026 hideTearOffs(win->bgMenuPane);
4028 /* restore the bg menu tearoffs of active document */
4029 redisplayTearOffs(window->bgMenuPane);
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))
4035 #ifdef REPLACE_SCOPE
4036 window->replaceScope = win->replaceScope;
4037 #endif
4038 UpdateReplaceActionButtons(window);
4041 UpdateWMSizeHints(window);
4044 WindowInfo* GetTopDocument(Widget w)
4046 WindowInfo *window = WidgetToWindow(w);
4048 return WidgetToWindow(window->shell);
4051 Boolean IsTopDocument(const WindowInfo *window)
4053 return window == GetTopDocument(window->shell)? True : False;
4056 static void deleteDocument(WindowInfo *window)
4058 if (NULL == window) {
4059 return;
4062 XtDestroyWidget(window->splitPane);
4066 ** return the number of documents owned by this shell window
4068 int NDocuments(WindowInfo *window)
4070 WindowInfo *win;
4071 int nDocument = 0;
4073 for (win = WindowList; win; win = win->next) {
4074 if (win->shell == window->shell)
4075 nDocument++;
4078 return nDocument;
4082 ** refresh window state for this document
4084 void RefreshWindowStates(WindowInfo *window)
4086 if (!IsTopDocument(window))
4087 return;
4089 if (window->modeMessageDisplayed)
4090 XmTextSetString(window->statsLine, window->modeMessage);
4091 else
4092 UpdateStatsLine(window);
4093 UpdateWindowReadOnly(window);
4094 UpdateWindowTitle(window);
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);
4101 else if (window->showStats && !XtIsManaged(window->statsLineForm)) {
4102 /* turn on statsline since it is enabled */
4103 showStats(window, True);
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);
4111 /* signal if macro/shell is running */
4112 if (window->shellCmdData || window->macroCmdData)
4113 BeginWait(window->shell);
4114 else
4115 EndWait(window->shell);
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 */
4123 XmUpdateDisplay(window->statsLine);
4124 refreshMenuBar(window);
4126 updateLineNumDisp(window);
4129 static void cloneTextPanes(WindowInfo *window, WindowInfo *orgWin)
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;
4140 /* transfer the primary selection */
4141 memcpy(&sel, &orgWin->buffer->primary, sizeof(selection));
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);
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;
4165 window->nPanes = orgWin->nPanes;
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);
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);
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 */
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);
4192 if (window->highlightData != NULL)
4193 AttachHighlightToWidget(text, window);
4194 XtManageChild(text);
4195 window->textPanes[i] = text;
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);
4207 /* Set the minimum pane height in the new pane */
4208 UpdateMinPaneHeights(window);
4210 for (i=0; i<=window->nPanes; i++) {
4211 text = i==0 ? window->textArea : window->textPanes[i-1];
4212 setPaneDesiredHeight(containingPane(text), paneHeights[i]);
4215 /* Re-manage panedWindow to recalculate pane heights & reset selection */
4216 XtManageChild(window->splitPane);
4219 /* Reset all of the heights, scroll positions, etc. */
4220 for (i=0; i<=window->nPanes; i++) {
4221 textDisp *textD;
4223 text = i==0 ? window->textArea : window->textPanes[i-1];
4224 TextSetCursorPos(text, insertPositions[i]);
4225 TextSetScroll(text, topLines[i], horizOffsets[i]);
4227 /* dim the cursor */
4228 textD = ((TextWidget)text)->text.textD;
4229 TextDSetCursorStyle(textD, DIM_CURSOR);
4230 TextDUnblankCursor(textD);
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;
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);
4251 ** clone a document's states and settings into the other.
4253 static void cloneDocument(WindowInfo *window, WindowInfo *orgWin)
4255 const char *orgDocument;
4256 char *params[4];
4257 int emTabDist;
4259 strcpy(window->path, orgWin->path);
4260 strcpy(window->filename, orgWin->filename);
4262 ShowLineNumbers(window, orgWin->showLineNumbers);
4264 window->ignoreModify = True;
4266 /* copy the text buffer */
4267 orgDocument = BufAsString(orgWin->buffer);
4268 BufSetAll(window->buffer, orgDocument);
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);
4276 window->ignoreModify = False;
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);
4285 SetBacklightChars(window, orgWin->backlightCharTypes);
4287 /* Clone rangeset info.
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.
4294 window->buffer->rangesetTable =
4295 RangesetTableClone(orgWin->buffer->rangesetTable, window->buffer);
4297 /* Syntax highlighting */
4298 window->languageMode = orgWin->languageMode;
4299 window->highlightSyntax = orgWin->highlightSyntax;
4300 if (window->highlightSyntax)
4301 StartHighlighting(window, False);
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;
4357 /* copy the text/split panes settings, cursor pos & selection */
4358 cloneTextPanes(window, orgWin);
4360 /* copy undo & redo list */
4361 window->undo = cloneUndoItems(orgWin->undo);
4362 window->redo = cloneUndoItems(orgWin->redo);
4364 /* copy bookmarks */
4365 window->nMarks = orgWin->nMarks;
4366 memcpy(&window->markTable, &orgWin->markTable,
4367 sizeof(Bookmark)*window->nMarks);
4369 /* kick start the auto-indent engine */
4370 window->indentStyle = NO_AUTO_INDENT;
4371 SetAutoIndent(window, orgWin->indentStyle);
4373 /* synchronize window state to this document */
4374 RefreshWindowStates(window);
4377 static UndoInfo *cloneUndoItems(UndoInfo *orgList)
4379 UndoInfo *head = NULL, *undo, *clone, *last = NULL;
4381 for (undo = orgList; undo; undo = undo->next) {
4382 clone = (UndoInfo *)XtMalloc(sizeof(UndoInfo));
4383 memcpy(clone, undo, sizeof(UndoInfo));
4385 if (undo->oldText) {
4386 clone->oldText = XtMalloc(strlen(undo->oldText)+1);
4387 strcpy(clone->oldText, undo->oldText);
4389 clone->next = NULL;
4391 if (last)
4392 last->next = clone;
4393 else
4394 head = clone;
4396 last = clone;
4399 return head;
4403 ** spin off the document to a new window
4405 WindowInfo *DetachDocument(WindowInfo *window)
4407 WindowInfo *win = NULL, *cloneWin;
4409 if (NDocuments(window) < 2)
4410 return NULL;
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);
4419 /* Create a new window */
4420 cloneWin = CreateWindow(window->filename, NULL, False);
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;
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);
4438 /* clone the document & its pref settings */
4439 cloneDocument(cloneWin, window);
4441 /* remove the document from the old window */
4442 window->fileChanged = False;
4443 CloseFileAndWindow(window, NO_SBC_DIALOG_RESPONSE);
4445 /* refresh former host window */
4446 if (win) {
4447 RefreshWindowStates(win);
4450 /* this should keep the new document window fresh */
4451 RefreshWindowStates(cloneWin);
4452 RefreshTabState(cloneWin);
4453 SortTabBar(cloneWin);
4455 return cloneWin;
4459 ** Move document to an other window.
4461 ** the moving document will receive certain window settings from
4462 ** its new host, i.e. the window size, stats and isearch lines.
4464 WindowInfo *MoveDocument(WindowInfo *toWindow, WindowInfo *window)
4466 WindowInfo *win = NULL, *cloneWin;
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);
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);
4479 /* relocate the document to target window */
4480 cloneWin = CreateDocument(toWindow, window->filename);
4481 ShowTabBar(cloneWin, GetShowTabBar(cloneWin));
4482 cloneDocument(cloneWin, window);
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;
4494 /* remove the document from the old window */
4495 window->fileChanged = False;
4496 CloseFileAndWindow(window, NO_SBC_DIALOG_RESPONSE);
4498 /* some menu states might have changed when deleting document */
4499 if (win)
4500 RefreshWindowStates(win);
4502 /* this should keep the new document window fresh */
4503 RaiseDocumentWindow(cloneWin);
4504 RefreshTabState(cloneWin);
4505 SortTabBar(cloneWin);
4507 return cloneWin;
4510 static void moveDocumentCB(Widget dialog, WindowInfo *window,
4511 XtPointer call_data)
4513 XmSelectionBoxCallbackStruct *cbs = (XmSelectionBoxCallbackStruct *) call_data;
4514 DoneWithMoveDocumentDialog = cbs->reason;
4518 ** present dialog for selecting a target window to move this document
4519 ** into. Do nothing if there is only one shell window opened.
4521 void MoveDocumentDialog(WindowInfo *window)
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;
4532 /* get the list of available shell windows, not counting
4533 the document to be moved */
4534 nWindows = NWindows();
4535 list = (XmStringTable) XtMalloc(nWindows * sizeof(XmString *));
4536 shellWinList = (WindowInfo **) XtMalloc(nWindows * sizeof(WindowInfo *));
4538 for (win=WindowList; win; win=win->next) {
4539 if (!IsTopDocument(win) || win->shell == window->shell)
4540 continue;
4542 sprintf(tmpStr, "%s%s",
4543 win->filenameSet? win->path : "", win->filename);
4545 list[nList] = XmStringCreateSimple(tmpStr);
4546 shellWinList[nList] = win;
4547 nList++;
4550 /* stop here if there's no other window to move to */
4551 if (!nList) {
4552 XtFree((char *)list);
4553 XtFree((char *)shellWinList);
4554 return;
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);
4580 /* free the window list */
4581 for (i=0; i<nList; i++)
4582 XmStringFree(list[i]);
4583 XtFree((char *)list);
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);
4594 if (NDocuments(window) >1)
4595 XtManageChild(moveAllOption);
4597 /* disable option if only one document in the window */
4598 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_APPLY_BUTTON));
4600 s1 = MKSTRING("Move");
4601 XtVaSetValues (dialog, XmNokLabelString, s1, NULL);
4602 XmStringFree(s1);
4604 /* default to the first window on the list */
4605 listBox = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);
4606 XmListSelectPos(listBox, 1, True);
4608 /* show the dialog */
4609 DoneWithMoveDocumentDialog = 0;
4610 ManageDialogCenteredOnPointer(dialog);
4611 while (!DoneWithMoveDocumentDialog)
4612 XtAppProcessEvent(XtWidgetToApplicationContext(parent), XtIMAll);
4614 /* get the window to move document into */
4615 XmListGetSelectedPos(listBox, &position_list, &position_count);
4616 targetWin = shellWinList[position_list[0]-1];
4617 XtFree((char *)position_list);
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;
4630 else
4631 win = win->next;
4634 /* invoking document is the last to move */
4635 MoveDocument(targetWin, window);
4637 else {
4638 MoveDocument(targetWin, window);
4642 XtFree((char *)shellWinList);
4643 XtDestroyWidget(dialog);
4646 static void hideTooltip(Widget tab)
4648 Widget tooltip = XtNameToWidget(tab, "*BubbleShell");
4650 if (tooltip)
4651 XtPopdown(tooltip);
4654 static void closeTabProc(XtPointer clientData, XtIntervalId *id)
4656 CloseFileAndWindow((WindowInfo*)clientData, PROMPT_SBC_DIALOG_RESPONSE);
4660 ** callback to close-tab button.
4662 static void closeTabCB(Widget w, Widget mainWin, caddr_t callData)
4664 /* FIXME: XtRemoveActionHook() related coredump
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.
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.
4675 Closing the tab through either the "Close" menu or its accel key,
4676 however, was without any trouble.
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));
4686 ** callback to clicks on a tab to raise it's document.
4688 static void raiseTabCB(Widget w, XtPointer clientData, XtPointer callData)
4690 XmLFolderCallbackStruct *cbs = (XmLFolderCallbackStruct *)callData;
4691 WidgetList tabList;
4692 Widget tab;
4694 XtVaGetValues(w, XmNtabWidgetList, &tabList, NULL);
4695 tab = tabList[cbs->pos];
4696 RaiseDocument(TabToWindow(tab));
4699 static Widget containingPane(Widget w)
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));
4706 static void cancelTimeOut(XtIntervalId *timer)
4708 if (*timer != 0)
4710 XtRemoveTimeOut(*timer);
4711 *timer = 0;
4716 ** set/clear toggle menu state if the calling document is on top.
4718 void SetToggleButtonState(WindowInfo *window, Widget w, Boolean state,
4719 Boolean notify)
4721 if (IsTopDocument(window)) {
4722 XmToggleButtonSetState(w, state, notify);
4727 ** set/clear menu sensitivity if the calling document is on top.
4729 void SetSensitive(WindowInfo *window, Widget w, Boolean sensitive)
4731 if (IsTopDocument(window)) {
4732 XtSetSensitive(w, sensitive);
4737 ** Remove redundant expose events on tab bar.
4739 void CleanUpTabBarExposeQueue(WindowInfo *window)
4741 XEvent event;
4742 XExposeEvent ev;
4743 int count;
4745 if (window == NULL)
4746 return;
4748 /* remove redundant expose events on tab bar */
4749 count=0;
4750 while (XCheckTypedWindowEvent(TheDisplay, XtWindow(window->tabBar),
4751 Expose, &event))
4752 count++;
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);