Bug fix: File->Close in the original window was disabled after calling
[nedit.git] / source / window.c
blob74ff133ccac3ee79d1a7be770323c766eb8538c5
1 static const char CVSID[] = "$Id: window.c,v 1.156 2004/05/03 11:59:04 edg 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. *
12 * *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
16 * for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
20 * Place, Suite 330, Boston, MA 02111-1307 USA *
21 * *
22 * Nirvana Text Editor *
23 * May 10, 1991 *
24 * *
25 * Written by Mark Edel *
26 * *
27 *******************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
33 #include "window.h"
34 #include "textBuf.h"
35 #include "textSel.h"
36 #include "text.h"
37 #include "textDisp.h"
38 #include "textP.h"
39 #include "nedit.h"
40 #include "menu.h"
41 #include "file.h"
42 #include "search.h"
43 #include "undo.h"
44 #include "preferences.h"
45 #include "selection.h"
46 #include "server.h"
47 #include "shell.h"
48 #include "macro.h"
49 #include "highlight.h"
50 #include "smartIndent.h"
51 #include "userCmds.h"
52 #include "nedit.bm"
53 #include "n.bm"
54 #include "windowTitle.h"
55 #include "interpret.h"
56 #include "rangeset.h"
57 #include "../util/clearcase.h"
58 #include "../util/misc.h"
59 #include "../util/fileUtils.h"
60 #include "../util/utils.h"
61 #include "../util/fileUtils.h"
62 #include "../util/DialogF.h"
63 #include "../Xlt/BubbleButtonP.h"
64 #include "../Microline/XmL/Folder.h"
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #ifdef VMS
70 #include "../util/VMSparam.h"
71 #else
72 #ifndef __MVS__
73 #include <sys/param.h>
74 #endif
75 #include "../util/clearcase.h"
76 #endif /*VMS*/
77 #include <limits.h>
78 #include <math.h>
79 #include <ctype.h>
80 #include <time.h>
81 #ifdef __unix__
82 #include <sys/time.h>
83 #endif
85 #include <X11/Intrinsic.h>
86 #include <X11/Shell.h>
87 #include <X11/Xatom.h>
88 #include <Xm/Xm.h>
89 #include <Xm/MainW.h>
90 #include <Xm/PanedW.h>
91 #include <Xm/PanedWP.h>
92 #include <Xm/RowColumnP.h>
93 #include <Xm/Separator.h>
94 #include <Xm/Text.h>
95 #include <Xm/ToggleB.h>
96 #include <Xm/PushB.h>
97 #include <Xm/Form.h>
98 #include <Xm/Frame.h>
99 #include <Xm/Label.h>
100 #include <Xm/SelectioB.h>
101 #include <Xm/List.h>
102 #include <Xm/Protocols.h>
103 #include <Xm/ScrolledW.h>
104 #include <Xm/ScrollBar.h>
105 #include <Xm/PrimitiveP.h>
106 #include <Xm/Frame.h>
107 #include <Xm/CascadeB.h>
108 #ifdef EDITRES
109 #include <X11/Xmu/Editres.h>
110 /* extern void _XEditResCheckMessages(); */
111 #endif /* EDITRES */
113 #ifdef HAVE_DEBUG_H
114 #include "../debug.h"
115 #endif
117 /* Initial minimum height of a pane. Just a fallback in case setPaneMinHeight
118 (which may break in a future release) is not available */
119 #define PANE_MIN_HEIGHT 39
121 /* Thickness of 3D border around statistics and/or incremental search areas
122 below the main menu bar */
123 #define STAT_SHADOW_THICKNESS 1
125 /* bitmap data for the close-tab button */
126 #define close_width 11
127 #define close_height 11
128 static unsigned char close_bits[] = {
129 0x00, 0x00, 0x00, 0x00, 0x8c, 0x01, 0xdc, 0x01, 0xf8, 0x00, 0x70, 0x00,
130 0xf8, 0x00, 0xdc, 0x01, 0x8c, 0x01, 0x00, 0x00, 0x00, 0x00};
132 /* bitmap data for the isearch-find button */
133 #define isrcFind_width 11
134 #define isrcFind_height 11
135 static unsigned char isrcFind_bits[] = {
136 0xe0, 0x01, 0x10, 0x02, 0xc8, 0x04, 0x08, 0x04, 0x08, 0x04, 0x00, 0x04,
137 0x18, 0x02, 0xdc, 0x01, 0x0e, 0x00, 0x07, 0x00, 0x03, 0x00};
139 /* bitmap data for the isearch-clear button */
140 #define isrcClear_width 11
141 #define isrcClear_height 11
142 static unsigned char isrcClear_bits[] = {
143 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x84, 0x01, 0xc4, 0x00, 0x64, 0x00,
144 0xc4, 0x00, 0x84, 0x01, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00};
146 extern void _XmDismissTearOff(Widget, XtPointer, XtPointer);
148 static void hideTooltip(Widget tab);
149 static Pixmap createBitmapWithDepth(Widget w, char *data, unsigned int width,
150 unsigned int height);
151 static WindowInfo *getNextTabWindow(WindowInfo *window, int direction,
152 int crossWin, int wrap);
153 static Widget addTab(Widget folder, WindowInfo *window, const char *string);
154 static int compareWindowNames(const void *windowA, const void *windowB);
155 static int getTabPosition(Widget tab);
156 static Widget manageToolBars(Widget toolBarsForm);
157 static void hideTearOffs(Widget menuPane);
158 static void CloseDocumentWindow(Widget w, WindowInfo *window, XtPointer callData);
159 static void closeTabCB(Widget w, Widget mainWin, caddr_t callData);
160 static void raiseTabCB(Widget w, XtPointer clientData, XtPointer callData);
161 static Widget createTextArea(Widget parent, WindowInfo *window, int rows,
162 int cols, int emTabDist, char *delimiters, int wrapMargin,
163 int lineNumCols);
164 static void showStats(WindowInfo *window, int state);
165 static void showISearch(WindowInfo *window, int state);
166 static void showStatsForm(WindowInfo *window, int state);
167 static void addToWindowList(WindowInfo *window);
168 static void removeFromWindowList(WindowInfo *window);
169 static int requestLineNumCols(textDisp *textD);
170 static void focusCB(Widget w, WindowInfo *window, XtPointer callData);
171 static void modifiedCB(int pos, int nInserted, int nDeleted, int nRestyled,
172 char *deletedText, void *cbArg);
173 static void movedCB(Widget w, WindowInfo *window, XtPointer callData);
174 static void dragStartCB(Widget w, WindowInfo *window, XtPointer callData);
175 static void dragEndCB(Widget w, WindowInfo *window, dragEndCBStruct *callData);
176 static void closeCB(Widget w, WindowInfo *window, XtPointer callData);
177 static void saveYourselfCB(Widget w, Widget appShell, XtPointer callData);
178 static void setPaneDesiredHeight(Widget w, int height);
179 static void setPaneMinHeight(Widget w, int min);
180 static void addWindowIcon(Widget shell);
181 static void wmSizeUpdateProc(XtPointer clientData, XtIntervalId *id);
182 static void getGeometryString(WindowInfo *window, char *geomString);
183 #ifdef ROWCOLPATCH
184 static void patchRowCol(Widget w);
185 static void patchedRemoveChild(Widget child);
186 #endif
187 static unsigned char* sanitizeVirtualKeyBindings();
188 static int sortAlphabetical(const void* k1, const void* k2);
189 static int virtKeyBindingsAreInvalid(const unsigned char* bindings);
190 static void restoreInsaneVirtualKeyBindings(unsigned char* bindings);
191 static void refreshMenuBar(WindowInfo *window);
192 static void cloneDocument(WindowInfo *window, WindowInfo *orgWin);
193 static void cloneTextPane(WindowInfo *window, WindowInfo *orgWin);
194 static UndoInfo *cloneUndoItems(UndoInfo *orgList);
195 static Widget containingPane(Widget w);
197 static WindowInfo *inFocusDocument = NULL; /* where we are now */
198 static WindowInfo *lastFocusDocument = NULL; /* where we came from */
199 static int DoneWithMoveDocumentDialog;
202 ** Create a new editor window
204 WindowInfo *CreateWindow(const char *name, char *geometry, int iconic)
206 Widget winShell, mainWin, menuBar, pane, text, stats, statsAreaForm;
207 Widget closeTabBtn, tabForm, form;
208 WindowInfo *window;
209 Pixel bgpix, fgpix;
210 Arg al[20];
211 int ac;
212 XmString s1;
213 WindowInfo *win;
214 #ifdef SGI_CUSTOM
215 char sgi_title[MAXPATHLEN + 14 + SGI_WINDOW_TITLE_LEN] = SGI_WINDOW_TITLE;
216 #endif
217 char newGeometry[MAX_GEOM_STRING_LEN];
218 unsigned int rows, cols;
219 int x = 0, y = 0, bitmask, showTabBar, state;
220 static int firstTime = True;
221 unsigned char* invalidBindings = NULL;
223 static Pixmap isrcFind = 0;
224 static Pixmap isrcClear = 0;
225 static Pixmap closeTabPixmap = 0;
227 if (firstTime)
229 invalidBindings = sanitizeVirtualKeyBindings();
230 firstTime = False;
233 /* Allocate some memory for the new window data structure */
234 window = (WindowInfo *)XtMalloc(sizeof(WindowInfo));
236 /* initialize window structure */
237 /* + Schwarzenberg: should a
238 memset(window, 0, sizeof(WindowInfo));
239 be added here ?
241 window->replaceDlog = NULL;
242 window->replaceText = NULL;
243 window->replaceWithText = NULL;
244 window->replaceWordToggle = NULL;
245 window->replaceCaseToggle = NULL;
246 window->replaceRegexToggle = NULL;
247 window->findDlog = NULL;
248 window->findText = NULL;
249 window->findWordToggle = NULL;
250 window->findCaseToggle = NULL;
251 window->findRegexToggle = NULL;
252 window->replaceMultiFileDlog = NULL;
253 window->replaceMultiFilePathBtn = NULL;
254 window->replaceMultiFileList = NULL;
255 window->multiFileReplSelected = FALSE;
256 window->multiFileBusy = FALSE;
257 window->writableWindows = NULL;
258 window->nWritableWindows = 0;
259 window->fileChanged = FALSE;
260 window->fileMode = 0;
261 window->filenameSet = FALSE;
262 window->fileFormat = UNIX_FILE_FORMAT;
263 window->lastModTime = 0;
264 window->fileMissing = True;
265 strcpy(window->filename, name);
266 window->undo = NULL;
267 window->redo = NULL;
268 window->nPanes = 0;
269 window->autoSaveCharCount = 0;
270 window->autoSaveOpCount = 0;
271 window->undoOpCount = 0;
272 window->undoMemUsed = 0;
273 CLEAR_ALL_LOCKS(window->lockReasons);
274 window->indentStyle = GetPrefAutoIndent(PLAIN_LANGUAGE_MODE);
275 window->autoSave = GetPrefAutoSave();
276 window->saveOldVersion = GetPrefSaveOldVersion();
277 window->wrapMode = GetPrefWrap(PLAIN_LANGUAGE_MODE);
278 window->overstrike = False;
279 window->showMatchingStyle = GetPrefShowMatching();
280 window->matchSyntaxBased = GetPrefMatchSyntaxBased();
281 window->showStats = GetPrefStatsLine();
282 window->showISearchLine = GetPrefISearchLine();
283 window->showLineNumbers = GetPrefLineNums();
284 window->highlightSyntax = GetPrefHighlightSyntax();
285 window->backlightCharTypes = NULL;
286 window->backlightChars = GetPrefBacklightChars();
287 if (window->backlightChars) {
288 char *cTypes = GetPrefBacklightCharTypes();
289 if (cTypes && window->backlightChars) {
290 if ((window->backlightCharTypes = XtMalloc(strlen(cTypes) + 1)))
291 strcpy(window->backlightCharTypes, cTypes);
294 window->modeMessageDisplayed = FALSE;
295 window->modeMessage = NULL;
296 window->ignoreModify = FALSE;
297 window->windowMenuValid = FALSE;
298 window->prevOpenMenuValid = FALSE;
299 window->flashTimeoutID = 0;
300 window->fileClosedAtom = None;
301 window->wasSelected = FALSE;
303 strcpy(window->fontName, GetPrefFontName());
304 strcpy(window->italicFontName, GetPrefItalicFontName());
305 strcpy(window->boldFontName, GetPrefBoldFontName());
306 strcpy(window->boldItalicFontName, GetPrefBoldItalicFontName());
307 window->colorDialog = NULL;
308 window->fontList = GetPrefFontList();
309 window->italicFontStruct = GetPrefItalicFont();
310 window->boldFontStruct = GetPrefBoldFont();
311 window->boldItalicFontStruct = GetPrefBoldItalicFont();
312 window->fontDialog = NULL;
313 window->nMarks = 0;
314 window->markTimeoutID = 0;
315 window->highlightData = NULL;
316 window->shellCmdData = NULL;
317 window->macroCmdData = NULL;
318 window->smartIndentData = NULL;
319 window->languageMode = PLAIN_LANGUAGE_MODE;
320 window->iSearchHistIndex = 0;
321 window->iSearchStartPos = -1;
322 window->replaceLastRegexCase = TRUE;
323 window->replaceLastLiteralCase = FALSE;
324 window->iSearchLastRegexCase = TRUE;
325 window->iSearchLastLiteralCase = FALSE;
326 window->findLastRegexCase = TRUE;
327 window->findLastLiteralCase = FALSE;
328 window->tab = NULL;
330 /* If window geometry was specified, split it apart into a window position
331 component and a window size component. Create a new geometry string
332 containing the position component only. Rows and cols are stripped off
333 because we can't easily calculate the size in pixels from them until the
334 whole window is put together. Note that the preference resource is only
335 for clueless users who decide to specify the standard X geometry
336 application resource, which is pretty useless because width and height
337 are the same as the rows and cols preferences, and specifying a window
338 location will force all the windows to pile on top of one another */
339 if (geometry == NULL || geometry[0] == '\0')
340 geometry = GetPrefGeometry();
341 if (geometry == NULL || geometry[0] == '\0') {
342 rows = GetPrefRows();
343 cols = GetPrefCols();
344 newGeometry[0] = '\0';
345 } else {
346 bitmask = XParseGeometry(geometry, &x, &y, &cols, &rows);
347 if (bitmask == 0)
348 fprintf(stderr, "Bad window geometry specified: %s\n", geometry);
349 else {
350 if (!(bitmask & WidthValue))
351 cols = GetPrefCols();
352 if (!(bitmask & HeightValue))
353 rows = GetPrefRows();
355 CreateGeometryString(newGeometry, x, y, 0, 0,
356 bitmask & ~(WidthValue | HeightValue));
359 /* Create a new toplevel shell to hold the window */
360 ac = 0;
361 #ifdef SGI_CUSTOM
362 strcat(sgi_title, name);
363 XtSetArg(al[ac], XmNtitle, sgi_title); ac++;
364 XtSetArg(al[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
365 if (strncmp(name, "Untitled", 8) == 0) {
366 XtSetArg(al[ac], XmNiconName, APP_NAME); ac++;
367 } else {
368 XtSetArg(al[ac], XmNiconName, name); ac++;
370 #else
371 XtSetArg(al[ac], XmNtitle, name); ac++;
372 XtSetArg(al[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
373 XtSetArg(al[ac], XmNiconName, name); ac++;
374 #endif
375 XtSetArg(al[ac], XmNgeometry, newGeometry[0]=='\0'?NULL:newGeometry); ac++;
376 XtSetArg(al[ac], XmNinitialState,
377 iconic ? IconicState : NormalState); ac++;
379 winShell = CreateWidget(TheAppShell, "textShell",
380 topLevelShellWidgetClass, al, ac);
381 window->shell = winShell;
383 #ifdef EDITRES
384 XtAddEventHandler (winShell, (EventMask)0, True,
385 (XtEventHandler)_XEditResCheckMessages, NULL);
386 #endif /* EDITRES */
388 #ifndef SGI_CUSTOM
389 addWindowIcon(winShell);
390 #endif
392 /* Create a MainWindow to manage the menubar and text area, set the
393 userData resource to be used by WidgetToWindow to recover the
394 window pointer from the widget id of any of the window's widgets */
395 XtSetArg(al[ac], XmNuserData, window); ac++;
396 mainWin = XmCreateMainWindow(winShell, "main", al, ac);
397 window->mainWin = mainWin;
398 XtManageChild(mainWin);
400 /* The statsAreaForm holds the stats line and the I-Search line. */
401 statsAreaForm = XtVaCreateWidget("statsAreaForm",
402 xmFormWidgetClass, mainWin,
403 XmNmarginWidth, STAT_SHADOW_THICKNESS,
404 XmNmarginHeight, STAT_SHADOW_THICKNESS,
405 /* XmNautoUnmanage, False, */
406 NULL);
408 /* NOTE: due to a bug in openmotif 2.1.30, NEdit used to crash when
409 the i-search bar was active, and the i-search text widget was focussed,
410 and the window's width was resized to nearly zero.
411 In theory, it is possible to avoid this by imposing a minimum
412 width constraint on the nedit windows, but that width would have to
413 be at least 30 characters, which is probably unacceptable.
414 Amazingly, adding a top offset of 1 pixel to the toggle buttons of
415 the i-search bar, while keeping the the top offset of the text widget
416 to 0 seems to avoid avoid the crash. */
418 window->iSearchForm = XtVaCreateWidget("iSearchForm",
419 xmFormWidgetClass, statsAreaForm,
420 XmNshadowThickness, 0,
421 XmNleftAttachment, XmATTACH_FORM,
422 XmNleftOffset, STAT_SHADOW_THICKNESS,
423 XmNtopAttachment, XmATTACH_FORM,
424 XmNtopOffset, STAT_SHADOW_THICKNESS,
425 XmNrightAttachment, XmATTACH_FORM,
426 XmNrightOffset, STAT_SHADOW_THICKNESS,
427 XmNbottomOffset, STAT_SHADOW_THICKNESS, NULL);
428 if(window->showISearchLine)
429 XtManageChild(window->iSearchForm);
431 /* Disable keyboard traversal of the find, clear and toggle buttons. We
432 were doing this previously by forcing the keyboard focus back to the
433 text widget whenever a toggle changed. That causes an ugly focus flash
434 on screen. It's better just not to go there in the first place.
435 Plus, if the user really wants traversal, it's an X resource so it
436 can be enabled without too much pain and suffering. */
438 if (isrcFind == 0) {
439 isrcFind = createBitmapWithDepth(window->iSearchForm,
440 (char *)isrcFind_bits, isrcFind_width, isrcFind_height);
442 window->iSearchFindButton = XtVaCreateManagedWidget("iSearchFindButton",
443 xmPushButtonWidgetClass, window->iSearchForm,
444 XmNlabelString, s1=XmStringCreateSimple("Find"),
445 XmNlabelType, XmPIXMAP,
446 XmNlabelPixmap, isrcFind,
447 XmNtraversalOn, False,
448 XmNmarginHeight, 1,
449 XmNmarginWidth, 1,
450 XmNleftAttachment, XmATTACH_FORM,
451 /* XmNleftOffset, 3, */
452 XmNleftOffset, 0,
453 XmNtopAttachment, XmATTACH_FORM,
454 XmNtopOffset, 1,
455 XmNbottomAttachment, XmATTACH_FORM,
456 XmNbottomOffset, 1,
457 NULL);
458 XmStringFree(s1);
460 window->iSearchCaseToggle = XtVaCreateManagedWidget("iSearchCaseToggle",
461 xmToggleButtonWidgetClass, window->iSearchForm,
462 XmNlabelString, s1=XmStringCreateSimple("Case"),
463 XmNset, GetPrefSearch() == SEARCH_CASE_SENSE
464 || GetPrefSearch() == SEARCH_REGEX
465 || GetPrefSearch() == SEARCH_CASE_SENSE_WORD,
466 XmNtopAttachment, XmATTACH_FORM,
467 XmNbottomAttachment, XmATTACH_FORM,
468 XmNtopOffset, 1, /* see openmotif note above */
469 XmNrightAttachment, XmATTACH_FORM,
470 XmNmarginHeight, 0,
471 XmNtraversalOn, False,
472 NULL);
473 XmStringFree(s1);
475 window->iSearchRegexToggle = XtVaCreateManagedWidget("iSearchREToggle",
476 xmToggleButtonWidgetClass, window->iSearchForm,
477 XmNlabelString, s1=XmStringCreateSimple("RegExp"),
478 XmNset, GetPrefSearch() == SEARCH_REGEX_NOCASE
479 || GetPrefSearch() == SEARCH_REGEX,
480 XmNtopAttachment, XmATTACH_FORM,
481 XmNbottomAttachment, XmATTACH_FORM,
482 XmNtopOffset, 1, /* see openmotif note above */
483 XmNrightAttachment, XmATTACH_WIDGET,
484 XmNrightWidget, window->iSearchCaseToggle,
485 XmNmarginHeight, 0,
486 XmNtraversalOn, False,
487 NULL);
488 XmStringFree(s1);
490 window->iSearchRevToggle = XtVaCreateManagedWidget("iSearchRevToggle",
491 xmToggleButtonWidgetClass, window->iSearchForm,
492 XmNlabelString, s1=XmStringCreateSimple("Rev"),
493 XmNset, False,
494 XmNtopAttachment, XmATTACH_FORM,
495 XmNbottomAttachment, XmATTACH_FORM,
496 XmNtopOffset, 1, /* see openmotif note above */
497 XmNrightAttachment, XmATTACH_WIDGET,
498 XmNrightWidget, window->iSearchRegexToggle,
499 XmNmarginHeight, 0,
500 XmNtraversalOn, False,
501 NULL);
502 XmStringFree(s1);
504 if (isrcClear == 0) {
505 isrcClear = createBitmapWithDepth(window->iSearchForm,
506 (char *)isrcClear_bits, isrcClear_width, isrcClear_height);
508 window->iSearchClearButton = XtVaCreateManagedWidget("iSearchClearButton",
509 xmPushButtonWidgetClass, window->iSearchForm,
510 XmNlabelString, s1=XmStringCreateSimple("<x"),
511 XmNlabelType, XmPIXMAP,
512 XmNlabelPixmap, isrcClear,
513 XmNtraversalOn, False,
514 XmNmarginHeight, 1,
515 XmNmarginWidth, 1,
516 XmNrightAttachment, XmATTACH_WIDGET,
517 XmNrightWidget, window->iSearchRevToggle,
518 XmNrightOffset, 2,
519 XmNtopAttachment, XmATTACH_FORM,
520 XmNtopOffset, 1,
521 XmNbottomAttachment, XmATTACH_FORM,
522 XmNbottomOffset, 1,
523 NULL);
524 XmStringFree(s1);
526 window->iSearchText = XtVaCreateManagedWidget("iSearchText",
527 xmTextWidgetClass, window->iSearchForm,
528 XmNmarginHeight, 1,
529 XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
530 XmNleftAttachment, XmATTACH_WIDGET,
531 XmNleftWidget, window->iSearchFindButton,
532 XmNrightAttachment, XmATTACH_WIDGET,
533 XmNrightWidget, window->iSearchClearButton,
534 /* XmNrightOffset, 5, */
535 XmNtopAttachment, XmATTACH_FORM,
536 XmNtopOffset, 0, /* see openmotif note above */
537 XmNbottomAttachment, XmATTACH_FORM,
538 XmNbottomOffset, 0, NULL);
539 RemapDeleteKey(window->iSearchText);
541 SetISearchTextCallbacks(window);
543 /* create the a form to house the tab bar and close-tab button */
544 tabForm = XtVaCreateWidget("tabForm",
545 xmFormWidgetClass, statsAreaForm,
546 XmNmarginHeight, 0,
547 XmNmarginWidth, 0,
548 XmNspacing, 0,
549 XmNresizable, False,
550 XmNleftAttachment, XmATTACH_FORM,
551 XmNrightAttachment, XmATTACH_FORM,
552 XmNshadowThickness, 0, NULL);
554 /* button to close top document */
555 if (closeTabPixmap == 0) {
556 closeTabPixmap = createBitmapWithDepth(tabForm,
557 (char *)close_bits, close_width, close_height);
559 closeTabBtn = XtVaCreateManagedWidget("closeTabBtn",
560 xmPushButtonWidgetClass, tabForm,
561 XmNmarginHeight, 0,
562 XmNmarginWidth, 0,
563 XmNhighlightThickness, 0,
564 XmNlabelType, XmPIXMAP,
565 XmNlabelPixmap, closeTabPixmap,
566 XmNshadowThickness, 1,
567 XmNtraversalOn, False,
568 XmNrightAttachment, XmATTACH_FORM,
569 XmNrightOffset, 3,
570 XmNbottomAttachment, XmATTACH_FORM,
571 XmNbottomOffset, 3,
572 NULL);
573 XtAddCallback(closeTabBtn, XmNactivateCallback, (XtCallbackProc)closeTabCB,
574 mainWin);
576 /* create the tab bar */
577 window->tabBar = XtVaCreateManagedWidget("tabBar",
578 xmlFolderWidgetClass, tabForm,
579 XmNresizePolicy, XmRESIZE_PACK,
580 XmNleftAttachment, XmATTACH_FORM,
581 XmNleftOffset, 0,
582 XmNrightAttachment, XmATTACH_WIDGET,
583 XmNrightWidget, closeTabBtn,
584 XmNrightOffset, 5,
585 XmNbottomAttachment, XmATTACH_FORM,
586 XmNbottomOffset, 0,
587 XmNtopAttachment, XmATTACH_FORM,
588 NULL);
590 window->tabMenuPane = CreateTabContextMenu(window->tabBar, window);
591 AddTabContextMenuAction(window->tabBar);
593 /* create an unmanaged composite widget to get the folder
594 widget to hide the 3D shadow for the manager area.
595 Note: this works only on the patched XmLFolder widget */
596 form = XtVaCreateWidget("form",
597 xmFormWidgetClass, window->tabBar,
598 XmNheight, 1,
599 XmNresizable, False,
600 NULL);
602 XtAddCallback(window->tabBar, XmNactivateCallback,
603 raiseTabCB, NULL);
605 window->tab = addTab(window->tabBar, window, name);
607 /* A form to hold the stats line text and line/col widgets */
608 window->statsLineForm = XtVaCreateWidget("statsLineForm",
609 xmFormWidgetClass, statsAreaForm,
610 XmNshadowThickness, 0,
611 XmNtopAttachment, window->showISearchLine ?
612 XmATTACH_WIDGET : XmATTACH_FORM,
613 XmNtopWidget, window->iSearchForm,
614 XmNrightAttachment, XmATTACH_FORM,
615 XmNleftAttachment, XmATTACH_FORM,
616 XmNbottomAttachment, XmATTACH_FORM,
617 XmNresizable, False, /* */
618 NULL);
620 /* A separate display of the line/column number */
621 window->statsLineColNo = XtVaCreateManagedWidget("statsLineColNo",
622 xmLabelWidgetClass, window->statsLineForm,
623 XmNlabelString, s1=XmStringCreateSimple("L: --- C: ---"),
624 XmNshadowThickness, 0,
625 XmNmarginHeight, 2,
626 XmNtraversalOn, False,
627 XmNtopAttachment, XmATTACH_FORM,
628 XmNrightAttachment, XmATTACH_FORM,
629 XmNbottomAttachment, XmATTACH_FORM, /* */
630 NULL);
631 XmStringFree(s1);
633 /* Create file statistics display area. Using a text widget rather than
634 a label solves a layout problem with the main window, which messes up
635 if the label is too long (we would need a resize callback to control
636 the length when the window changed size), and allows users to select
637 file names and line numbers. Colors are copied from parent
638 widget, because many users and some system defaults color text
639 backgrounds differently from other widgets. */
640 XtVaGetValues(window->statsLineForm, XmNbackground, &bgpix, NULL);
641 XtVaGetValues(window->statsLineForm, XmNforeground, &fgpix, NULL);
642 stats = XtVaCreateManagedWidget("statsLine",
643 xmTextWidgetClass, window->statsLineForm,
644 XmNbackground, bgpix,
645 XmNforeground, fgpix,
646 XmNshadowThickness, 0,
647 XmNhighlightColor, bgpix,
648 XmNhighlightThickness, 0, /* must be zero, for OM (2.1.30) to
649 aligns tatsLineColNo & statsLine */
650 XmNmarginHeight, 1, /* == statsLineColNo.marginHeight - 1,
651 to align with statsLineColNo */
652 XmNscrollHorizontal, False,
653 XmNeditMode, XmSINGLE_LINE_EDIT,
654 XmNeditable, False,
655 XmNtraversalOn, False,
656 XmNcursorPositionVisible, False,
657 XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, /* */
658 XmNtopWidget, window->statsLineColNo,
659 XmNleftAttachment, XmATTACH_FORM,
660 XmNrightAttachment, XmATTACH_WIDGET,
661 XmNrightWidget, window->statsLineColNo,
662 XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, /* */
663 XmNbottomWidget, window->statsLineColNo,
664 XmNrightOffset, 3,
665 NULL);
666 window->statsLine = stats;
668 /* Manage the statsLineForm */
669 if(window->showStats)
670 XtManageChild(window->statsLineForm);
672 /* If the fontList was NULL, use the magical default provided by Motif,
673 since it must have worked if we've gotten this far */
674 if (window->fontList == NULL)
675 XtVaGetValues(stats, XmNfontList, &window->fontList, NULL);
677 /* Create the menu bar */
678 menuBar = CreateMenuBar(mainWin, window);
679 window->menuBar = menuBar;
680 XtManageChild(menuBar);
682 /* Create paned window to manage split pane behavior */
683 pane = XtVaCreateManagedWidget("pane", xmPanedWindowWidgetClass, mainWin,
684 XmNseparatorOn, False,
685 XmNspacing, 3, XmNsashIndent, -2, NULL);
686 window->splitPane = pane;
687 XmMainWindowSetAreas(mainWin, menuBar, statsAreaForm, NULL, NULL, pane);
689 /* Store a copy of document/window pointer in text pane to support
690 action procedures. See also WidgetToWindow() for info. */
691 XtVaSetValues(pane, XmNuserData, window, NULL);
693 /* Patch around Motif's most idiotic "feature", that its menu accelerators
694 recognize Caps Lock and Num Lock as modifiers, and don't trigger if
695 they are engaged */
696 AccelLockBugPatch(pane, window->menuBar);
698 /* Create the first, and most permanent text area (other panes may
699 be added & removed, but this one will never be removed */
700 text = createTextArea(pane, window, rows,cols,
701 GetPrefEmTabDist(PLAIN_LANGUAGE_MODE), GetPrefDelimiters(),
702 GetPrefWrapMargin(), window->showLineNumbers?MIN_LINE_NUM_COLS:0);
703 XtManageChild(text);
704 window->textArea = text;
705 window->lastFocus = text;
707 /* Set the initial colors from the globals. */
708 SetColors(window,
709 GetPrefColorName(TEXT_FG_COLOR ),
710 GetPrefColorName(TEXT_BG_COLOR ),
711 GetPrefColorName(SELECT_FG_COLOR),
712 GetPrefColorName(SELECT_BG_COLOR),
713 GetPrefColorName(HILITE_FG_COLOR),
714 GetPrefColorName(HILITE_BG_COLOR),
715 GetPrefColorName(LINENO_FG_COLOR),
716 GetPrefColorName(CURSOR_FG_COLOR));
718 /* Create the right button popup menu (note: order is important here,
719 since the translation for popping up this menu was probably already
720 added in createTextArea, but CreateBGMenu requires window->textArea
721 to be set so it can attach the menu to it (because menu shells are
722 finicky about the kinds of widgets they are attached to)) */
723 window->bgMenuPane = CreateBGMenu(window);
725 /* cache user menus: init. user background menu cache */
726 InitUserBGMenuCache(&window->userBGMenuCache);
728 /* Create the text buffer rather than using the one created automatically
729 with the text area widget. This is done so the syntax highlighting
730 modify callback can be called to synchronize the style buffer BEFORE
731 the text display's callback is called upon to display a modification */
732 window->buffer = BufCreate();
733 BufAddModifyCB(window->buffer, SyntaxHighlightModifyCB, window);
735 /* Attach the buffer to the text widget, and add callbacks for modify */
736 TextSetBuffer(text, window->buffer);
737 BufAddModifyCB(window->buffer, modifiedCB, window);
739 /* Designate the permanent text area as the owner for selections */
740 HandleXSelections(text);
742 /* Set the requested hardware tab distance and useTabs in the text buffer */
743 BufSetTabDistance(window->buffer, GetPrefTabDist(PLAIN_LANGUAGE_MODE));
744 window->buffer->useTabs = GetPrefInsertTabs();
746 /* add the window to the global window list, update the Windows menus */
747 addToWindowList(window);
748 InvalidateWindowMenus();
750 showTabBar = GetShowTabBar(window);
751 if (showTabBar)
752 XtManageChild(tabForm);
754 manageToolBars(statsAreaForm);
756 if (showTabBar || window->showISearchLine ||
757 window->showStats)
758 XtManageChild(statsAreaForm);
760 /* realize all of the widgets in the new window */
761 RealizeWithoutForcingPosition(winShell);
762 XmProcessTraversal(text, XmTRAVERSE_CURRENT);
764 /* Make close command in window menu gracefully prompt for close */
765 AddMotifCloseCallback(winShell, (XtCallbackProc)closeCB, window);
767 /* Make window resizing work in nice character heights */
768 UpdateWMSizeHints(window);
770 /* Set the minimum pane height for the initial text pane */
771 UpdateMinPaneHeights(window);
773 restoreInsaneVirtualKeyBindings(invalidBindings);
775 /* create dialogs shared by all documents in a window */
776 CreateFindDlog(window->shell, window);
777 CreateReplaceDlog(window->shell, window);
778 CreateReplaceMultiFileDlog(window);
780 /* dim/undim Attach_Tab menu items */
781 state = NDocuments(window) < NWindows();
782 for(win=WindowList; win; win=win->next) {
783 if (IsTopDocument(win)) {
784 XtSetSensitive(win->moveDocumentItem, state);
785 XtSetSensitive(win->contextMoveDocumentItem, state);
789 return window;
793 ** ButtonPress event handler for tabs.
795 static void tabClickEH(Widget w, XtPointer clientData, XEvent *event)
797 /* hide the tooltip when user clicks with any button. */
798 if (BubbleButton_Timer(w)) {
799 XtRemoveTimeOut(BubbleButton_Timer(w));
800 BubbleButton_Timer(w) = (XtIntervalId)NULL;
802 else {
803 hideTooltip(w);
808 ** add a tab to the tab bar for the new document.
810 static Widget addTab(Widget folder, WindowInfo *window, const char *string)
812 Widget tooltipLabel, tab;
813 XmString s1;
815 s1 = XmStringCreateSimple((char *)string);
816 tab = XtVaCreateManagedWidget("tab",
817 xrwsBubbleButtonWidgetClass, folder,
818 /* XmNmarginWidth, <default@nedit.c>, */
819 /* XmNmarginHeight, <default@nedit.c>, */
820 /* XmNalignment, <default@nedit.c>, */
821 XmNlabelString, s1,
822 XltNbubbleString, s1,
823 XltNshowBubble, GetPrefToolTips(),
824 XltNautoParkBubble, True,
825 XltNslidingBubble, False,
826 /* XltNdelay, 800,*/
827 /* XltNbubbleDuration, 8000,*/
828 NULL);
829 XmStringFree(s1);
831 /* there's things to do as user click on the tab */
832 XtAddEventHandler(tab, ButtonPressMask, False,
833 (XtEventHandler)tabClickEH, (XtPointer)0);
835 /* BubbleButton simply use reversed video for tooltips,
836 we try to use the 'standard' color */
837 tooltipLabel = XtNameToWidget(tab, "*BubbleLabel");
838 XtVaSetValues(tooltipLabel,
839 XmNbackground, AllocateColor(tab, GetPrefTooltipBgColor()),
840 XmNforeground, AllocateColor(tab, NEDIT_DEFAULT_FG),
841 NULL);
843 /* put borders around tooltip. BubbleButton use
844 transientShellWidgetClass as tooltip shell, which
845 came without borders */
846 XtVaSetValues(XtParent(tooltipLabel), XmNborderWidth, 1, NULL);
848 #ifdef LESSTIF_VERSION
849 /* If we don't do this, no popup when right-click on tabs */
850 AddTabContextMenuAction(tab);
851 #endif /* LESSTIF_VERSION */
853 return tab;
857 ** Comparison function for sorting windows by title.
858 ** Windows are sorted by alphabetically by filename and then
859 ** alphabetically by path.
861 static int compareWindowNames(const void *windowA, const void *windowB)
863 int rc;
864 const WindowInfo *a = *((WindowInfo**)windowA);
865 const WindowInfo *b = *((WindowInfo**)windowB);
867 rc = strcmp(a->filename, b->filename);
868 if (rc != 0)
869 return rc;
870 rc = strcmp(a->path, b->path);
871 return rc;
875 ** Sort tabs in the tab bar alphabetically, if demanded so.
877 void SortTabBar(WindowInfo *window)
879 WindowInfo *w;
880 WindowInfo **windows;
881 WidgetList tabList;
882 int i, nDoc;
884 if (!GetPrefSortTabs())
885 return;
887 /* need more than one tab to sort */
888 nDoc = NDocuments(window);
889 if (nDoc < 2)
890 return;
892 /* first sort the documents */
893 windows = (WindowInfo **)XtMalloc(sizeof(WindowInfo *) * nDoc);
894 for (w=WindowList, i=0; w!=NULL; w=w->next) {
895 if (window->shell == w->shell)
896 windows[i++] = w;
898 qsort(windows, nDoc, sizeof(WindowInfo *), compareWindowNames);
900 /* assign tabs to documents in sorted order */
901 XtVaGetValues(window->tabBar, XmNtabWidgetList, &tabList, NULL);
902 for (i=0; i<nDoc; i++) {
903 if (windows[i]->tab == tabList[i])
904 continue;
906 /* set tab as active */
907 if (IsTopDocument(windows[i]))
908 XmLFolderSetActiveTab(window->tabBar, i, False);
910 windows[i]->tab = tabList[i];
911 RefreshTabState(windows[i]);
914 XtFree((char *)windows);
918 ** find which document a tab belongs to
920 WindowInfo *TabToWindow(Widget tab)
922 WindowInfo *win;
923 for (win=WindowList; win; win=win->next) {
924 if (win->tab == tab)
925 return win;
928 return NULL;
932 ** Close a document, or an editor window
934 void CloseWindow(WindowInfo *window)
936 int keepWindow, state;
937 char name[MAXPATHLEN];
938 WindowInfo *win, *topBuf = NULL, *nextBuf = NULL;
940 /* Free smart indent macro programs */
941 EndSmartIndent(window);
943 /* Clean up macro references to the doomed window. If a macro is
944 executing, stop it. If macro is calling this (closing its own
945 window), leave the window alive until the macro completes */
946 keepWindow = !MacroWindowCloseActions(window);
948 #ifndef VMS
949 /* Kill shell sub-process and free related memory */
950 AbortShellCommand(window);
951 #endif /*VMS*/
953 /* Unload the default tips files for this language mode if necessary */
954 UnloadLanguageModeTipsFile(window);
956 /* If a window is closed while it is on the multi-file replace dialog
957 list of any other window (or even the same one), we must update those
958 lists or we end up with dangling references. Normally, there can
959 be only one of those dialogs at the same time (application modal),
960 but LessTif doesn't even (always) honor application modalness, so
961 there can be more than one dialog. */
962 RemoveFromMultiReplaceDialog(window);
964 /* Destroy the file closed property for this file */
965 DeleteFileClosedProperty(window);
967 /* if this is the last window, or must be kept alive temporarily because
968 it's running the macro calling us, don't close it, make it Untitled */
969 if (keepWindow || (WindowList == window && window->next == NULL)) {
970 window->filename[0] = '\0';
971 UniqueUntitledName(name);
972 CLEAR_ALL_LOCKS(window->lockReasons);
973 window->fileMode = 0;
974 strcpy(window->filename, name);
975 strcpy(window->path, "");
976 window->ignoreModify = TRUE;
977 BufSetAll(window->buffer, "");
978 window->ignoreModify = FALSE;
979 window->nMarks = 0;
980 window->filenameSet = FALSE;
981 window->fileMissing = TRUE;
982 window->fileChanged = FALSE;
983 window->fileFormat = UNIX_FILE_FORMAT;
984 window->lastModTime = 0;
985 StopHighlighting(window);
986 EndSmartIndent(window);
987 UpdateWindowTitle(window);
988 UpdateWindowReadOnly(window);
989 XtSetSensitive(window->closeItem, FALSE);
990 XtSetSensitive(window->readOnlyItem, TRUE);
991 XmToggleButtonSetState(window->readOnlyItem, FALSE, FALSE);
992 ClearUndoList(window);
993 ClearRedoList(window);
994 XmTextSetString(window->statsLine, ""); /* resets scroll pos of stats
995 line from long file names */
996 UpdateStatsLine(window);
997 DetermineLanguageMode(window, True);
998 RefreshTabState(window);
999 return;
1002 /* Free syntax highlighting patterns, if any. w/o redisplaying */
1003 FreeHighlightingData(window);
1005 /* remove the buffer modification callbacks so the buffer will be
1006 deallocated when the last text widget is destroyed */
1007 BufRemoveModifyCB(window->buffer, modifiedCB, window);
1008 BufRemoveModifyCB(window->buffer, SyntaxHighlightModifyCB, window);
1010 #ifdef ROWCOLPATCH
1011 patchRowCol(window->menuBar);
1012 #endif
1014 /* free the undo and redo lists */
1015 ClearUndoList(window);
1016 ClearRedoList(window);
1018 /* close the document/window */
1019 if (NDocuments(window) > 1) {
1020 if (MacroRunWindow() && MacroRunWindow() != window &&
1021 MacroRunWindow()->shell == window->shell) {
1022 nextBuf = MacroRunWindow();
1023 RaiseDocument(nextBuf);
1025 else if (IsTopDocument(window)) {
1026 /* need to find a successor before closing a top document */
1027 nextBuf = getNextTabWindow(window, 1, 0, 0);
1028 RaiseDocument(nextBuf);
1030 else
1031 topBuf = GetTopDocument(window->shell);
1034 /* remove the window from the global window list, update window menus */
1035 removeFromWindowList(window);
1036 InvalidateWindowMenus();
1037 CheckCloseDim(); /* Close of window running a macro may have been disabled. */
1039 /* remove the tab of the closing document from tab bar */
1040 XtDestroyWidget(window->tab);
1042 /* refresh tab bar after closing a document */
1043 if (nextBuf)
1044 ShowWindowTabBar(nextBuf);
1045 else if (topBuf)
1046 ShowWindowTabBar(topBuf);
1048 /* dim/undim Detach_Tab menu items */
1049 win = nextBuf? nextBuf : topBuf;
1050 if (win) {
1051 state = NDocuments(win) > 1;
1052 XtSetSensitive(win->detachDocumentItem, state);
1053 XtSetSensitive(win->contextDetachDocumentItem, state);
1056 /* dim/undim Attach_Tab menu items */
1057 state = NDocuments(WindowList) < NWindows();
1058 for(win=WindowList; win; win=win->next) {
1059 if (IsTopDocument(win)) {
1060 XtSetSensitive(win->moveDocumentItem, state);
1061 XtSetSensitive(win->contextMoveDocumentItem, state);
1065 /* free background menu cache for document */
1066 FreeUserBGMenuCache(&window->userBGMenuCache);
1068 /* destroy the document's pane, or the window */
1069 if (nextBuf || topBuf) {
1070 DeleteDocument(window);
1072 else {
1073 /* free user menu cache for window */
1074 FreeUserMenuCache(window->userMenuCache);
1076 /* remove and deallocate all of the widgets associated with window */
1077 XtFree(window->backlightCharTypes); /* we made a copy earlier on */
1078 CloseAllPopupsFor(window->shell);
1079 XtDestroyWidget(window->shell);
1082 /* deallocate the window data structure */
1083 XtFree((char*)window);
1087 ** check if tab bar is to be shown on this window
1089 int GetShowTabBar(WindowInfo *window)
1091 if (!GetPrefTabBar())
1092 return False;
1093 else if (NDocuments(window) == 1)
1094 return !GetPrefTabBarHideOne();
1095 else
1096 return True;
1099 void ShowWindowTabBar(WindowInfo *window)
1101 if (GetPrefTabBar()) {
1102 if (GetPrefTabBarHideOne())
1103 ShowTabBar(window, NDocuments(window)>1);
1104 else
1105 ShowTabBar(window, True);
1107 else
1108 ShowTabBar(window, False);
1112 ** Check if there is already a window open for a given file
1114 WindowInfo *FindWindowWithFile(const char *name, const char *path)
1116 WindowInfo *w;
1118 for (w=WindowList; w!=NULL; w=w->next) {
1119 if (!strcmp(w->filename, name) && !strcmp(w->path, path)) {
1120 return w;
1123 return NULL;
1127 ** Add another independently scrollable pane to the current document,
1128 ** splitting the pane which currently has keyboard focus.
1130 void SplitPane(WindowInfo *window)
1132 short paneHeights[MAX_PANES+1];
1133 int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1];
1134 int horizOffsets[MAX_PANES+1];
1135 int i, focusPane, emTabDist, wrapMargin, lineNumCols, totalHeight=0;
1136 char *delimiters;
1137 Widget text;
1138 textDisp *textD, *newTextD;
1140 /* Don't create new panes if we're already at the limit */
1141 if (window->nPanes >= MAX_PANES)
1142 return;
1144 /* Record the current heights, scroll positions, and insert positions
1145 of the existing panes, keyboard focus */
1146 focusPane = 0;
1147 for (i=0; i<=window->nPanes; i++) {
1148 text = i==0 ? window->textArea : window->textPanes[i-1];
1149 insertPositions[i] = TextGetCursorPos(text);
1150 XtVaGetValues(containingPane(text),XmNheight,&paneHeights[i],NULL);
1151 totalHeight += paneHeights[i];
1152 TextGetScroll(text, &topLines[i], &horizOffsets[i]);
1153 if (text == window->lastFocus)
1154 focusPane = i;
1157 /* Unmanage & remanage the panedWindow so it recalculates pane heights */
1158 XtUnmanageChild(window->splitPane);
1160 /* Create a text widget to add to the pane and set its buffer and
1161 highlight data to be the same as the other panes in the document */
1162 XtVaGetValues(window->textArea, textNemulateTabs, &emTabDist,
1163 textNwordDelimiters, &delimiters, textNwrapMargin, &wrapMargin,
1164 textNlineNumCols, &lineNumCols, NULL);
1165 text = createTextArea(window->splitPane, window, 1, 1, emTabDist,
1166 delimiters, wrapMargin, lineNumCols);
1168 TextSetBuffer(text, window->buffer);
1169 if (window->highlightData != NULL)
1170 AttachHighlightToWidget(text, window);
1171 if (window->backlightChars)
1173 XtVaSetValues(text, textNbacklightCharTypes,
1174 window->backlightCharTypes, 0);
1176 XtManageChild(text);
1177 window->textPanes[window->nPanes++] = text;
1179 /* Fix up the colors */
1180 textD = ((TextWidget)window->textArea)->text.textD;
1181 newTextD = ((TextWidget)text)->text.textD;
1182 XtVaSetValues(text,
1183 XmNforeground, textD->fgPixel,
1184 XmNbackground, textD->bgPixel,
1185 NULL);
1186 TextDSetColors( newTextD, textD->fgPixel, textD->bgPixel,
1187 textD->selectFGPixel, textD->selectBGPixel, textD->highlightFGPixel,
1188 textD->highlightBGPixel, textD->lineNumFGPixel,
1189 textD->cursorFGPixel );
1191 /* Set the minimum pane height in the new pane */
1192 UpdateMinPaneHeights(window);
1194 /* adjust the heights, scroll positions, etc., to split the focus pane */
1195 for (i=window->nPanes; i>focusPane; i--) {
1196 insertPositions[i] = insertPositions[i-1];
1197 paneHeights[i] = paneHeights[i-1];
1198 topLines[i] = topLines[i-1];
1199 horizOffsets[i] = horizOffsets[i-1];
1201 paneHeights[focusPane] = paneHeights[focusPane]/2;
1202 paneHeights[focusPane+1] = paneHeights[focusPane];
1204 for (i=0; i<=window->nPanes; i++) {
1205 text = i==0 ? window->textArea : window->textPanes[i-1];
1206 setPaneDesiredHeight(containingPane(text), paneHeights[i]);
1209 /* Re-manage panedWindow to recalculate pane heights & reset selection */
1210 if (IsTopDocument(window))
1211 XtManageChild(window->splitPane);
1213 /* Reset all of the heights, scroll positions, etc. */
1214 for (i=0; i<=window->nPanes; i++) {
1215 text = i==0 ? window->textArea : window->textPanes[i-1];
1216 TextSetCursorPos(text, insertPositions[i]);
1217 TextSetScroll(text, topLines[i], horizOffsets[i]);
1218 setPaneDesiredHeight(containingPane(text),
1219 totalHeight/(window->nPanes+1));
1221 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
1223 /* Update the window manager size hints after the sizes of the panes have
1224 been set (the widget heights are not yet readable here, but they will
1225 be by the time the event loop gets around to running this timer proc) */
1226 XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0,
1227 wmSizeUpdateProc, window);
1230 Widget GetPaneByIndex(WindowInfo *window, int paneIndex)
1232 Widget text = NULL;
1233 if (paneIndex >= 0 && paneIndex <= window->nPanes) {
1234 text = (paneIndex == 0) ? window->textArea : window->textPanes[paneIndex - 1];
1236 return(text);
1239 int WidgetToPaneIndex(WindowInfo *window, Widget w)
1241 int i;
1242 Widget text;
1243 int paneIndex = 0;
1245 for (i = 0; i <= window->nPanes; ++i) {
1246 text = (i == 0) ? window->textArea : window->textPanes[i - 1];
1247 if (text == w) {
1248 paneIndex = i;
1251 return(paneIndex);
1255 ** Close the window pane that last had the keyboard focus. (Actually, close
1256 ** the bottom pane and make it look like pane which had focus was closed)
1258 void ClosePane(WindowInfo *window)
1260 short paneHeights[MAX_PANES+1];
1261 int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1];
1262 int horizOffsets[MAX_PANES+1];
1263 int i, focusPane,totalHeight=0;
1264 Widget text;
1266 /* Don't delete the last pane */
1267 if (window->nPanes <= 0)
1268 return;
1270 /* Record the current heights, scroll positions, and insert positions
1271 of the existing panes, and the keyboard focus */
1272 focusPane = 0;
1273 for (i=0; i<=window->nPanes; i++) {
1274 text = i==0 ? window->textArea : window->textPanes[i-1];
1275 insertPositions[i] = TextGetCursorPos(text);
1276 XtVaGetValues(containingPane(text),
1277 XmNheight, &paneHeights[i], NULL);
1278 totalHeight += paneHeights[i];
1279 TextGetScroll(text, &topLines[i], &horizOffsets[i]);
1280 if (text == window->lastFocus)
1281 focusPane = i;
1284 /* Unmanage & remanage the panedWindow so it recalculates pane heights */
1285 XtUnmanageChild(window->splitPane);
1287 /* Destroy last pane, and make sure lastFocus points to an existing pane.
1288 Workaround for OM 2.1.30: text widget must be unmanaged for
1289 xmPanedWindowWidget to calculate the correct pane heights for
1290 the remaining panes, simply detroying it didn't seem enough */
1291 window->nPanes--;
1292 XtUnmanageChild(containingPane(window->textPanes[window->nPanes]));
1293 XtDestroyWidget(containingPane(window->textPanes[window->nPanes]));
1295 if (window->nPanes == 0)
1296 window->lastFocus = window->textArea;
1297 else if (focusPane > window->nPanes)
1298 window->lastFocus = window->textPanes[window->nPanes-1];
1300 /* adjust the heights, scroll positions, etc., to make it look
1301 like the pane with the input focus was closed */
1302 for (i=focusPane; i<=window->nPanes; i++) {
1303 insertPositions[i] = insertPositions[i+1];
1304 paneHeights[i] = paneHeights[i+1];
1305 topLines[i] = topLines[i+1];
1306 horizOffsets[i] = horizOffsets[i+1];
1309 /* set the desired heights and re-manage the paned window so it will
1310 recalculate pane heights */
1311 for (i=0; i<=window->nPanes; i++) {
1312 text = i==0 ? window->textArea : window->textPanes[i-1];
1313 setPaneDesiredHeight(containingPane(text), paneHeights[i]);
1316 if (IsTopDocument(window))
1317 XtManageChild(window->splitPane);
1319 /* Reset all of the scroll positions, insert positions, etc. */
1320 for (i=0; i<=window->nPanes; i++) {
1321 text = i==0 ? window->textArea : window->textPanes[i-1];
1322 TextSetCursorPos(text, insertPositions[i]);
1323 TextSetScroll(text, topLines[i], horizOffsets[i]);
1325 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
1327 /* Update the window manager size hints after the sizes of the panes have
1328 been set (the widget heights are not yet readable here, but they will
1329 be by the time the event loop gets around to running this timer proc) */
1330 XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0,
1331 wmSizeUpdateProc, window);
1335 ** Turn on and off the display of line numbers
1337 void ShowLineNumbers(WindowInfo *window, int state)
1339 Widget text;
1340 int i, marginWidth, reqCols;
1341 Dimension windowWidth;
1342 WindowInfo *win;
1343 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
1345 if (window->showLineNumbers == state)
1346 return;
1347 window->showLineNumbers = state;
1349 /* Just setting window->showLineNumbers is sufficient to tell
1350 UpdateLineNumDisp to expand the line number areas and the window
1351 size for the number of lines required. To hide the line number
1352 display, set the width to zero, and contract the window width. */
1353 if (state) {
1354 UpdateLineNumDisp(window);
1355 } else {
1356 XtVaGetValues(window->shell, XmNwidth, &windowWidth, NULL);
1357 XtVaGetValues(window->textArea,
1358 textNmarginWidth, &marginWidth, NULL);
1359 XtVaSetValues(window->shell, XmNwidth,
1360 windowWidth - textD->left + marginWidth, NULL);
1362 for (i=0; i<=window->nPanes; i++) {
1363 text = i==0 ? window->textArea : window->textPanes[i-1];
1364 XtVaSetValues(text, textNlineNumCols, 0, NULL);
1368 /* line numbers panel is shell-level, hence other
1369 tabbed documents in the window should synch */
1370 for (win=WindowList; win; win=win->next) {
1371 if (win->shell != window->shell || win == window)
1372 continue;
1374 win->showLineNumbers = state;
1376 reqCols = state? requestLineNumCols(textD) : 0;
1377 for (i=0; i<=win->nPanes; i++) {
1378 text = i==0 ? win->textArea : win->textPanes[i-1];
1379 XtVaSetValues(text, textNlineNumCols, reqCols, NULL);
1383 /* Tell WM that the non-expandable part of the window has changed size */
1384 UpdateWMSizeHints(window);
1387 void SetTabDist(WindowInfo *window, int tabDist)
1389 if (window->buffer->tabDist != tabDist) {
1390 int saveCursorPositions[MAX_PANES + 1];
1391 int saveVScrollPositions[MAX_PANES + 1];
1392 int saveHScrollPositions[MAX_PANES + 1];
1393 int paneIndex;
1395 window->ignoreModify = True;
1397 for (paneIndex = 0; paneIndex <= window->nPanes; ++paneIndex) {
1398 Widget w = GetPaneByIndex(window, paneIndex);
1399 textDisp *textD = ((TextWidget)w)->text.textD;
1401 TextGetScroll(w, &saveVScrollPositions[paneIndex], &saveHScrollPositions[paneIndex]);
1402 saveCursorPositions[paneIndex] = TextGetCursorPos(w);
1403 textD->modifyingTabDist = 1;
1406 BufSetTabDistance(window->buffer, tabDist);
1408 for (paneIndex = 0; paneIndex <= window->nPanes; ++paneIndex) {
1409 Widget w = GetPaneByIndex(window, paneIndex);
1410 textDisp *textD = ((TextWidget)w)->text.textD;
1412 textD->modifyingTabDist = 0;
1413 TextSetCursorPos(w, saveCursorPositions[paneIndex]);
1414 TextSetScroll(w, saveVScrollPositions[paneIndex], saveHScrollPositions[paneIndex]);
1417 window->ignoreModify = False;
1421 void SetEmTabDist(WindowInfo *window, int emTabDist)
1423 int i;
1425 XtVaSetValues(window->textArea, textNemulateTabs, emTabDist, NULL);
1426 for (i = 0; i < window->nPanes; ++i) {
1427 XtVaSetValues(window->textPanes[i], textNemulateTabs, emTabDist, NULL);
1432 ** Turn on and off the display of the statistics line
1434 void ShowStatsLine(WindowInfo *window, int state)
1436 WindowInfo *win;
1437 Widget text;
1438 int i;
1440 /* In continuous wrap mode, text widgets must be told to keep track of
1441 the top line number in absolute (non-wrapped) lines, because it can
1442 be a costly calculation, and is only needed for displaying line
1443 numbers, either in the stats line, or along the left margin */
1444 for (i=0; i<=window->nPanes; i++) {
1445 text = i==0 ? window->textArea : window->textPanes[i-1];
1446 TextDMaintainAbsLineNum(((TextWidget)text)->text.textD, state);
1448 window->showStats = state;
1449 showStats(window, state);
1451 /* i-search line is shell-level, hence other tabbed
1452 documents in the window should synch */
1453 for (win=WindowList; win; win=win->next) {
1454 if (win->shell != window->shell || win == window)
1455 continue;
1456 win->showStats = state;
1461 ** Displays and undisplays the statistics line (regardless of settings of
1462 ** window->showStats or window->modeMessageDisplayed)
1464 static void showStats(WindowInfo *window, int state)
1466 if (state) {
1467 XtManageChild(window->statsLineForm);
1468 showStatsForm(window, True);
1469 } else {
1470 XtUnmanageChild(window->statsLineForm);
1471 showStatsForm(window, window->showISearchLine);
1474 /* Tell WM that the non-expandable part of the window has changed size */
1475 /* Already done in showStatsForm */
1476 /* UpdateWMSizeHints(window); */
1481 static void showTabBar(WindowInfo *window, int state)
1483 if (state) {
1484 XtManageChild(XtParent(window->tabBar));
1485 showStatsForm(window, True);
1486 } else {
1487 XtUnmanageChild(XtParent(window->tabBar));
1488 showStatsForm(window, False);
1494 void ShowTabBar(WindowInfo *window, int state)
1496 if (XtIsManaged(XtParent(window->tabBar)) == state)
1497 return;
1498 showTabBar(window, state);
1502 ** Turn on and off the continuing display of the incremental search line
1503 ** (when off, it is popped up and down as needed via TempShowISearch)
1505 void ShowISearchLine(WindowInfo *window, int state)
1507 WindowInfo *win;
1509 if (window->showISearchLine == state)
1510 return;
1511 window->showISearchLine = state;
1512 showISearch(window, state);
1514 /* i-search line is shell-level, hence other tabbed
1515 documents in the window should synch */
1516 for (win=WindowList; win; win=win->next) {
1517 if (win->shell != window->shell || win == window)
1518 continue;
1519 win->showISearchLine = state;
1524 ** Temporarily show and hide the incremental search line if the line is not
1525 ** already up.
1527 void TempShowISearch(WindowInfo *window, int state)
1529 if (window->showISearchLine)
1530 return;
1531 if (XtIsManaged(window->iSearchForm) != state)
1532 showISearch(window, state);
1536 ** Put up or pop-down the incremental search line regardless of settings
1537 ** of showISearchLine or TempShowISearch
1539 static void showISearch(WindowInfo *window, int state)
1541 if (state) {
1542 XtManageChild(window->iSearchForm);
1543 showStatsForm(window, True);
1544 } else {
1545 XtUnmanageChild(window->iSearchForm);
1546 showStatsForm(window, window->showStats ||
1547 window->modeMessageDisplayed);
1550 /* Tell WM that the non-expandable part of the window has changed size */
1551 /* This is already done in showStatsForm */
1552 /* UpdateWMSizeHints(window); */
1556 ** Show or hide the extra display area under the main menu bar which
1557 ** optionally contains the status line and the incremental search bar
1559 static void showStatsForm(WindowInfo *window, int state)
1561 Widget statsAreaForm = XtParent(window->statsLineForm);
1562 Widget mainW = XtParent(statsAreaForm);
1564 /* The very silly use of XmNcommandWindowLocation and XmNshowSeparator
1565 below are to kick the main window widget to position and remove the
1566 status line when it is managed and unmanaged. At some Motif version
1567 level, the showSeparator trick backfires and leaves the separator
1568 shown, but fortunately the dynamic behavior is fixed, too so the
1569 workaround is no longer necessary, either. (... the version where
1570 this occurs may be earlier than 2.1. If the stats line shows
1571 double thickness shadows in earlier Motif versions, the #if XmVersion
1572 directive should be moved back to that earlier version) */
1573 if (manageToolBars(statsAreaForm)) {
1574 XtUnmanageChild(statsAreaForm); /*... will this fix Solaris 7??? */
1575 XtVaSetValues(mainW, XmNcommandWindowLocation,
1576 XmCOMMAND_ABOVE_WORKSPACE, NULL);
1577 #if XmVersion < 2001
1578 XtVaSetValues(mainW, XmNshowSeparator, True, NULL);
1579 #endif
1580 XtManageChild(statsAreaForm);
1581 XtVaSetValues(mainW, XmNshowSeparator, False, NULL);
1582 UpdateStatsLine(window);
1583 } else {
1584 XtUnmanageChild(statsAreaForm);
1585 XtVaSetValues(mainW, XmNcommandWindowLocation,
1586 XmCOMMAND_BELOW_WORKSPACE, NULL);
1589 /* Tell WM that the non-expandable part of the window has changed size */
1590 UpdateWMSizeHints(window);
1594 ** Display a special message in the stats line (show the stats line if it
1595 ** is not currently shown).
1597 void SetModeMessage(WindowInfo *window, const char *message)
1599 /* this document may be hidden (not on top) or later made hidden,
1600 so we save a copy of the mode message, so we can restore the
1601 statsline when the document is raised to top again */
1602 window->modeMessageDisplayed = True;
1603 if (window->modeMessage)
1604 XtFree(window->modeMessage);
1605 window->modeMessage = XtNewString(message);
1607 if (!IsTopDocument(window))
1608 return;
1610 XmTextSetString(window->statsLine, (char*)message);
1612 * Don't invoke the stats line again, if stats line is already displayed.
1614 if (!window->showStats)
1615 showStats(window, True);
1619 ** Clear special statistics line message set in SetModeMessage, returns
1620 ** the statistics line to its original state as set in window->showStats
1622 void ClearModeMessage(WindowInfo *window)
1624 if (!window->modeMessageDisplayed)
1625 return;
1627 window->modeMessageDisplayed = False;
1628 XtFree(window->modeMessage);
1629 window->modeMessage = NULL;
1631 if (!IsTopDocument(window))
1632 return;
1635 * Remove the stats line only if indicated by it's window state.
1637 if (!window->showStats)
1638 showStats(window, False);
1639 UpdateStatsLine(window);
1643 ** Count the windows
1645 int NWindows(void)
1647 WindowInfo *win;
1648 int n;
1650 for (win=WindowList, n=0; win!=NULL; win=win->next, n++);
1651 return n;
1655 ** Set autoindent state to one of NO_AUTO_INDENT, AUTO_INDENT, or SMART_INDENT.
1657 void SetAutoIndent(WindowInfo *window, int state)
1659 int autoIndent = state == AUTO_INDENT, smartIndent = state == SMART_INDENT;
1660 int i;
1662 if (window->indentStyle == SMART_INDENT && !smartIndent)
1663 EndSmartIndent(window);
1664 else if (smartIndent && window->indentStyle != SMART_INDENT)
1665 BeginSmartIndent(window, True);
1666 window->indentStyle = state;
1667 XtVaSetValues(window->textArea, textNautoIndent, autoIndent,
1668 textNsmartIndent, smartIndent, NULL);
1669 for (i=0; i<window->nPanes; i++)
1670 XtVaSetValues(window->textPanes[i], textNautoIndent, autoIndent,
1671 textNsmartIndent, smartIndent, NULL);
1672 if (IsTopDocument(window)) {
1673 XmToggleButtonSetState(window->smartIndentItem, smartIndent, False);
1674 XmToggleButtonSetState(window->autoIndentItem, autoIndent, False);
1675 XmToggleButtonSetState(window->autoIndentOffItem,
1676 state == NO_AUTO_INDENT, False);
1681 ** Set showMatching state to one of NO_FLASH, FLASH_DELIMIT or FLASH_RANGE.
1682 ** Update the menu to reflect the change of state.
1684 void SetShowMatching(WindowInfo *window, int state)
1686 window->showMatchingStyle = state;
1687 if (IsTopDocument(window)) {
1688 XmToggleButtonSetState(window->showMatchingOffItem,
1689 state == NO_FLASH, False);
1690 XmToggleButtonSetState(window->showMatchingDelimitItem,
1691 state == FLASH_DELIMIT, False);
1692 XmToggleButtonSetState(window->showMatchingRangeItem,
1693 state == FLASH_RANGE, False);
1698 ** Set the fonts for "window" from a font name, and updates the display.
1699 ** Also updates window->fontList which is used for statistics line.
1701 ** Note that this leaks memory and server resources. In previous NEdit
1702 ** versions, fontLists were carefully tracked and freed, but X and Motif
1703 ** have some kind of timing problem when widgets are distroyed, such that
1704 ** fonts may not be freed immediately after widget destruction with 100%
1705 ** safety. Rather than kludge around this with timerProcs, I have chosen
1706 ** to create new fontLists only when the user explicitly changes the font
1707 ** (which shouldn't happen much in normal NEdit operation), and skip the
1708 ** futile effort of freeing them.
1710 void SetFonts(WindowInfo *window, const char *fontName, const char *italicName,
1711 const char *boldName, const char *boldItalicName)
1713 XFontStruct *font, *oldFont;
1714 int i, oldFontWidth, oldFontHeight, fontWidth, fontHeight;
1715 int borderWidth, borderHeight, marginWidth, marginHeight;
1716 int primaryChanged, highlightChanged = False;
1717 Dimension oldWindowWidth, oldWindowHeight, oldTextWidth, oldTextHeight;
1718 Dimension textHeight, newWindowWidth, newWindowHeight;
1719 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
1721 /* Check which fonts have changed */
1722 primaryChanged = strcmp(fontName, window->fontName);
1723 if (strcmp(italicName, window->italicFontName)) highlightChanged = True;
1724 if (strcmp(boldName, window->boldFontName)) highlightChanged = True;
1725 if (strcmp(boldItalicName, window->boldItalicFontName))
1726 highlightChanged = True;
1727 if (!primaryChanged && !highlightChanged)
1728 return;
1730 /* Get information about the current window sizing, to be used to
1731 determine the correct window size after the font is changed */
1732 XtVaGetValues(window->shell, XmNwidth, &oldWindowWidth, XmNheight,
1733 &oldWindowHeight, NULL);
1734 XtVaGetValues(window->textArea, XmNheight, &textHeight,
1735 textNmarginHeight, &marginHeight, textNmarginWidth,
1736 &marginWidth, textNfont, &oldFont, NULL);
1737 oldTextWidth = textD->width + textD->lineNumWidth;
1738 oldTextHeight = textHeight - 2*marginHeight;
1739 for (i=0; i<window->nPanes; i++) {
1740 XtVaGetValues(window->textPanes[i], XmNheight, &textHeight, NULL);
1741 oldTextHeight += textHeight - 2*marginHeight;
1743 borderWidth = oldWindowWidth - oldTextWidth;
1744 borderHeight = oldWindowHeight - oldTextHeight;
1745 oldFontWidth = oldFont->max_bounds.width;
1746 oldFontHeight = textD->ascent + textD->descent;
1749 /* Change the fonts in the window data structure. If the primary font
1750 didn't work, use Motif's fallback mechanism by stealing it from the
1751 statistics line. Highlight fonts are allowed to be NULL, which
1752 is interpreted as "use the primary font" */
1753 if (primaryChanged) {
1754 strcpy(window->fontName, fontName);
1755 font = XLoadQueryFont(TheDisplay, fontName);
1756 if (font == NULL)
1757 XtVaGetValues(window->statsLine, XmNfontList, &window->fontList,
1758 NULL);
1759 else
1760 window->fontList = XmFontListCreate(font, XmSTRING_DEFAULT_CHARSET);
1762 if (highlightChanged) {
1763 strcpy(window->italicFontName, italicName);
1764 window->italicFontStruct = XLoadQueryFont(TheDisplay, italicName);
1765 strcpy(window->boldFontName, boldName);
1766 window->boldFontStruct = XLoadQueryFont(TheDisplay, boldName);
1767 strcpy(window->boldItalicFontName, boldItalicName);
1768 window->boldItalicFontStruct = XLoadQueryFont(TheDisplay, boldItalicName);
1771 /* Change the primary font in all the widgets */
1772 if (primaryChanged) {
1773 font = GetDefaultFontStruct(window->fontList);
1774 XtVaSetValues(window->textArea, textNfont, font, NULL);
1775 for (i=0; i<window->nPanes; i++)
1776 XtVaSetValues(window->textPanes[i], textNfont, font, NULL);
1779 /* Change the highlight fonts, even if they didn't change, because
1780 primary font is read through the style table for syntax highlighting */
1781 if (window->highlightData != NULL)
1782 UpdateHighlightStyles(window);
1784 /* Change the window manager size hints.
1785 Note: this has to be done _before_ we set the new sizes. ICCCM2
1786 compliant window managers (such as fvwm2) would otherwise resize
1787 the window twice: once because of the new sizes requested, and once
1788 because of the new size increments, resulting in an overshoot. */
1789 UpdateWMSizeHints(window);
1791 /* Use the information from the old window to re-size the window to a
1792 size appropriate for the new font, but only do so if there's only
1793 _one_ document in the window, in order to avoid growing-window bug */
1794 if (NDocuments(window) == 1) {
1795 fontWidth = GetDefaultFontStruct(window->fontList)->max_bounds.width;
1796 fontHeight = textD->ascent + textD->descent;
1797 newWindowWidth = (oldTextWidth*fontWidth) / oldFontWidth + borderWidth;
1798 newWindowHeight = (oldTextHeight*fontHeight) / oldFontHeight +
1799 borderHeight;
1800 XtVaSetValues(window->shell, XmNwidth, newWindowWidth, XmNheight,
1801 newWindowHeight, NULL);
1804 /* Change the minimum pane height */
1805 UpdateMinPaneHeights(window);
1808 void SetColors(WindowInfo *window, const char *textFg, const char *textBg,
1809 const char *selectFg, const char *selectBg, const char *hiliteFg,
1810 const char *hiliteBg, const char *lineNoFg, const char *cursorFg)
1812 int i, dummy;
1813 Pixel textFgPix = AllocColor( window->textArea, textFg,
1814 &dummy, &dummy, &dummy),
1815 textBgPix = AllocColor( window->textArea, textBg,
1816 &dummy, &dummy, &dummy),
1817 selectFgPix = AllocColor( window->textArea, selectFg,
1818 &dummy, &dummy, &dummy),
1819 selectBgPix = AllocColor( window->textArea, selectBg,
1820 &dummy, &dummy, &dummy),
1821 hiliteFgPix = AllocColor( window->textArea, hiliteFg,
1822 &dummy, &dummy, &dummy),
1823 hiliteBgPix = AllocColor( window->textArea, hiliteBg,
1824 &dummy, &dummy, &dummy),
1825 lineNoFgPix = AllocColor( window->textArea, lineNoFg,
1826 &dummy, &dummy, &dummy),
1827 cursorFgPix = AllocColor( window->textArea, cursorFg,
1828 &dummy, &dummy, &dummy);
1829 textDisp *textD;
1831 /* Update the main pane */
1832 XtVaSetValues(window->textArea,
1833 XmNforeground, textFgPix,
1834 XmNbackground, textBgPix,
1835 NULL);
1836 textD = ((TextWidget)window->textArea)->text.textD;
1837 TextDSetColors( textD, textFgPix, textBgPix, selectFgPix, selectBgPix,
1838 hiliteFgPix, hiliteBgPix, lineNoFgPix, cursorFgPix );
1839 /* Update any additional panes */
1840 for (i=0; i<window->nPanes; i++) {
1841 XtVaSetValues(window->textPanes[i],
1842 XmNforeground, textFgPix,
1843 XmNbackground, textBgPix,
1844 NULL);
1845 textD = ((TextWidget)window->textPanes[i])->text.textD;
1846 TextDSetColors( textD, textFgPix, textBgPix, selectFgPix, selectBgPix,
1847 hiliteFgPix, hiliteBgPix, lineNoFgPix, cursorFgPix );
1850 /* Redo any syntax highlighting */
1851 if (window->highlightData != NULL)
1852 UpdateHighlightStyles(window);
1856 ** Set insert/overstrike mode
1858 void SetOverstrike(WindowInfo *window, int overstrike)
1860 int i;
1862 XtVaSetValues(window->textArea, textNoverstrike, overstrike, NULL);
1863 for (i=0; i<window->nPanes; i++)
1864 XtVaSetValues(window->textPanes[i], textNoverstrike, overstrike, NULL);
1865 window->overstrike = overstrike;
1869 ** Select auto-wrap mode, one of NO_WRAP, NEWLINE_WRAP, or CONTINUOUS_WRAP
1871 void SetAutoWrap(WindowInfo *window, int state)
1873 int i;
1874 int autoWrap = state == NEWLINE_WRAP, contWrap = state == CONTINUOUS_WRAP;
1876 XtVaSetValues(window->textArea, textNautoWrap, autoWrap,
1877 textNcontinuousWrap, contWrap, NULL);
1878 for (i=0; i<window->nPanes; i++)
1879 XtVaSetValues(window->textPanes[i], textNautoWrap, autoWrap,
1880 textNcontinuousWrap, contWrap, NULL);
1881 window->wrapMode = state;
1883 if (IsTopDocument(window)) {
1884 XmToggleButtonSetState(window->newlineWrapItem, autoWrap, False);
1885 XmToggleButtonSetState(window->continuousWrapItem, contWrap, False);
1886 XmToggleButtonSetState(window->noWrapItem, state == NO_WRAP, False);
1891 ** Set the wrap margin (0 == wrap at right edge of window)
1893 void SetWrapMargin(WindowInfo *window, int margin)
1895 int i;
1897 XtVaSetValues(window->textArea, textNwrapMargin, margin, NULL);
1898 for (i=0; i<window->nPanes; i++)
1899 XtVaSetValues(window->textPanes[i], textNwrapMargin, margin, NULL);
1903 ** Set the auto-scroll margin
1905 void SetAutoScroll(WindowInfo *window, int margin)
1907 int i;
1909 XtVaSetValues(window->textArea, textNcursorVPadding, margin, NULL);
1910 for (i=0; i<window->nPanes; i++)
1911 XtVaSetValues(window->textPanes[i], textNcursorVPadding, margin, NULL);
1915 ** Recover the window pointer from any widget in the window, by searching
1916 ** up the widget hierarcy for the top level container widget where the
1917 ** window pointer is stored in the userData field. In a tabbed window,
1918 ** this is the window pointer of the top (active) document, which is
1919 ** returned if w is 'shell-level' widget - menus, find/replace dialogs, etc.
1921 ** To support action routine in tabbed windows, a copy of the window
1922 ** pointer is also store in the splitPane widget.
1924 WindowInfo *WidgetToWindow(Widget w)
1926 WindowInfo *window = NULL;
1927 Widget parent;
1929 while (True) {
1930 /* return window pointer of document */
1931 if (XtClass(w) == xmPanedWindowWidgetClass)
1932 break;
1934 if (XtClass(w) == topLevelShellWidgetClass) {
1935 WidgetList items;
1937 /* there should be only 1 child for the shell -
1938 the main window widget */
1939 XtVaGetValues(w, XmNchildren, &items, NULL);
1940 w = items[0];
1941 break;
1944 parent = XtParent(w);
1945 if (parent == NULL)
1946 return NULL;
1948 /* make sure it is not a dialog shell */
1949 if (XtClass(parent) == topLevelShellWidgetClass &&
1950 XmIsMainWindow(w))
1951 break;
1953 w = parent;
1956 XtVaGetValues(w, XmNuserData, &window, NULL);
1958 return window;
1962 ** Change the window appearance and the window data structure to show
1963 ** that the file it contains has been modified
1965 void SetWindowModified(WindowInfo *window, int modified)
1967 if (window->fileChanged == FALSE && modified == TRUE) {
1968 SetSensitive(window, window->closeItem, TRUE);
1969 window->fileChanged = TRUE;
1970 UpdateWindowTitle(window);
1971 RefreshTabState(window);
1972 } else if (window->fileChanged == TRUE && modified == FALSE) {
1973 window->fileChanged = FALSE;
1974 UpdateWindowTitle(window);
1975 RefreshTabState(window);
1980 ** Update the window title to reflect the filename, read-only, and modified
1981 ** status of the window data structure
1983 void UpdateWindowTitle(const WindowInfo *window)
1985 char *iconTitle, *title;
1987 if (!IsTopDocument(window))
1988 return;
1990 title = FormatWindowTitle(window->filename,
1991 window->path,
1992 #ifdef VMS
1993 NULL,
1994 #else
1995 GetClearCaseViewTag(),
1996 #endif /* VMS */
1997 GetPrefServerName(),
1998 IsServer,
1999 window->filenameSet,
2000 window->lockReasons,
2001 window->fileChanged,
2002 GetPrefTitleFormat());
2004 iconTitle = XtMalloc(strlen(window->filename) + 2); /* strlen("*")+1 */
2006 strcpy(iconTitle, window->filename);
2007 if (window->fileChanged)
2008 strcat(iconTitle, "*");
2009 XtVaSetValues(window->shell, XmNtitle, title, XmNiconName, iconTitle, NULL);
2011 /* If there's a find or replace dialog up in "Keep Up" mode, with a
2012 file name in the title, update it too */
2013 if (window->findDlog && XmToggleButtonGetState(window->findKeepBtn)) {
2014 sprintf(title, "Find (in %s)", window->filename);
2015 XtVaSetValues(XtParent(window->findDlog), XmNtitle, title, NULL);
2017 if (window->replaceDlog && XmToggleButtonGetState(window->replaceKeepBtn)) {
2018 sprintf(title, "Replace (in %s)", window->filename);
2019 XtVaSetValues(XtParent(window->replaceDlog), XmNtitle, title, NULL);
2021 XtFree(iconTitle);
2023 /* Update the Windows menus with the new name */
2024 InvalidateWindowMenus();
2028 ** Update the read-only state of the text area(s) in the window, and
2029 ** the ReadOnly toggle button in the File menu to agree with the state in
2030 ** the window data structure.
2032 void UpdateWindowReadOnly(WindowInfo *window)
2034 int i, state;
2036 if (!IsTopDocument(window))
2037 return;
2039 state = IS_ANY_LOCKED(window->lockReasons);
2040 XtVaSetValues(window->textArea, textNreadOnly, state, NULL);
2041 for (i=0; i<window->nPanes; i++)
2042 XtVaSetValues(window->textPanes[i], textNreadOnly, state, NULL);
2043 XmToggleButtonSetState(window->readOnlyItem, state, FALSE);
2044 XtSetSensitive(window->readOnlyItem,
2045 !IS_ANY_LOCKED_IGNORING_USER(window->lockReasons));
2049 ** Get the start and end of the current selection. This routine is obsolete
2050 ** because it ignores rectangular selections, and reads from the widget
2051 ** instead of the buffer. Use BufGetSelectionPos.
2053 int GetSelection(Widget widget, int *left, int *right)
2055 return GetSimpleSelection(TextGetBuffer(widget), left, right);
2059 ** Find the start and end of a single line selection. Hides rectangular
2060 ** selection issues for older routines which use selections that won't
2061 ** span lines.
2063 int GetSimpleSelection(textBuffer *buf, int *left, int *right)
2065 int selStart, selEnd, isRect, rectStart, rectEnd, lineStart;
2067 /* get the character to match and its position from the selection, or
2068 the character before the insert point if nothing is selected.
2069 Give up if too many characters are selected */
2070 if (!BufGetSelectionPos(buf, &selStart, &selEnd, &isRect,
2071 &rectStart, &rectEnd))
2072 return False;
2073 if (isRect) {
2074 lineStart = BufStartOfLine(buf, selStart);
2075 selStart = BufCountForwardDispChars(buf, lineStart, rectStart);
2076 selEnd = BufCountForwardDispChars(buf, lineStart, rectEnd);
2078 *left = selStart;
2079 *right = selEnd;
2080 return True;
2084 ** Returns a range of text from a text widget (this routine is obsolete,
2085 ** get text from the buffer instead). Memory is allocated with
2086 ** XtMalloc and caller should free it.
2088 char *GetTextRange(Widget widget, int left, int right)
2090 return BufGetRange(TextGetBuffer(widget), left, right);
2094 ** If the selection (or cursor position if there's no selection) is not
2095 ** fully shown, scroll to bring it in to view. Note that as written,
2096 ** this won't work well with multi-line selections. Modest re-write
2097 ** of the horizontal scrolling part would be quite easy to make it work
2098 ** well with rectangular selections.
2100 void MakeSelectionVisible(WindowInfo *window, Widget textPane)
2102 int left, right, isRect, rectStart, rectEnd, horizOffset;
2103 int scrollOffset, leftX, rightX, y, rows, margin;
2104 int topLineNum, lastLineNum, rightLineNum, leftLineNum, linesToScroll;
2105 textDisp *textD = ((TextWidget)textPane)->text.textD;
2106 int topChar = TextFirstVisiblePos(textPane);
2107 int lastChar = TextLastVisiblePos(textPane);
2108 int targetLineNum;
2109 Dimension width;
2111 /* find out where the selection is */
2112 if (!BufGetSelectionPos(window->buffer, &left, &right, &isRect,
2113 &rectStart, &rectEnd)) {
2114 left = right = TextGetCursorPos(textPane);
2115 isRect = False;
2118 /* Check vertical positioning unless the selection is already shown or
2119 already covers the display. If the end of the selection is below
2120 bottom, scroll it in to view until the end selection is scrollOffset
2121 lines from the bottom of the display or the start of the selection
2122 scrollOffset lines from the top. Calculate a pleasing distance from the
2123 top or bottom of the window, to scroll the selection to (if scrolling is
2124 necessary), around 1/3 of the height of the window */
2125 if (!((left >= topChar && right <= lastChar) ||
2126 (left <= topChar && right >= lastChar))) {
2127 XtVaGetValues(textPane, textNrows, &rows, NULL);
2128 scrollOffset = rows/3;
2129 TextGetScroll(textPane, &topLineNum, &horizOffset);
2130 if (right > lastChar) {
2131 /* End of sel. is below bottom of screen */
2132 leftLineNum = topLineNum +
2133 TextDCountLines(textD, topChar, left, False);
2134 targetLineNum = topLineNum + scrollOffset;
2135 if (leftLineNum >= targetLineNum) {
2136 /* Start of sel. is not between top & target */
2137 linesToScroll = TextDCountLines(textD, lastChar, right, False) +
2138 scrollOffset;
2139 if (leftLineNum - linesToScroll < targetLineNum)
2140 linesToScroll = leftLineNum - targetLineNum;
2141 /* Scroll start of selection to the target line */
2142 TextSetScroll(textPane, topLineNum+linesToScroll, horizOffset);
2144 } else if (left < topChar) {
2145 /* Start of sel. is above top of screen */
2146 lastLineNum = topLineNum + rows;
2147 rightLineNum = lastLineNum -
2148 TextDCountLines(textD, right, lastChar, False);
2149 targetLineNum = lastLineNum - scrollOffset;
2150 if (rightLineNum <= targetLineNum) {
2151 /* End of sel. is not between bottom & target */
2152 linesToScroll = TextDCountLines(textD, left, topChar, False) +
2153 scrollOffset;
2154 if (rightLineNum + linesToScroll > targetLineNum)
2155 linesToScroll = targetLineNum - rightLineNum;
2156 /* Scroll end of selection to the target line */
2157 TextSetScroll(textPane, topLineNum-linesToScroll, horizOffset);
2162 /* If either end of the selection off screen horizontally, try to bring it
2163 in view, by making sure both end-points are visible. Using only end
2164 points of a multi-line selection is not a great idea, and disaster for
2165 rectangular selections, so this part of the routine should be re-written
2166 if it is to be used much with either. Note also that this is a second
2167 scrolling operation, causing the display to jump twice. It's done after
2168 vertical scrolling to take advantage of TextPosToXY which requires it's
2169 reqested position to be vertically on screen) */
2170 if ( TextPosToXY(textPane, left, &leftX, &y) &&
2171 TextPosToXY(textPane, right, &rightX, &y) && leftX <= rightX) {
2172 TextGetScroll(textPane, &topLineNum, &horizOffset);
2173 XtVaGetValues(textPane, XmNwidth, &width, textNmarginWidth, &margin,
2174 NULL);
2175 if (leftX < margin + textD->lineNumLeft + textD->lineNumWidth)
2176 horizOffset -=
2177 margin + textD->lineNumLeft + textD->lineNumWidth - leftX;
2178 else if (rightX > width - margin)
2179 horizOffset += rightX - (width - margin);
2180 TextSetScroll(textPane, topLineNum, horizOffset);
2183 /* make sure that the statistics line is up to date */
2184 UpdateStatsLine(window);
2187 static Widget createTextArea(Widget parent, WindowInfo *window, int rows,
2188 int cols, int emTabDist, char *delimiters, int wrapMargin,
2189 int lineNumCols)
2191 Widget text, sw, hScrollBar, vScrollBar, frame;
2193 /* Create a text widget inside of a scrolled window widget */
2194 sw = XtVaCreateManagedWidget("scrolledW", xmScrolledWindowWidgetClass,
2195 parent, XmNpaneMaximum, SHRT_MAX,
2196 XmNpaneMinimum, PANE_MIN_HEIGHT, XmNhighlightThickness, 0, NULL);
2197 hScrollBar = XtVaCreateManagedWidget("textHorScrollBar",
2198 xmScrollBarWidgetClass, sw, XmNorientation, XmHORIZONTAL,
2199 XmNrepeatDelay, 10, NULL);
2200 vScrollBar = XtVaCreateManagedWidget("textVertScrollBar",
2201 xmScrollBarWidgetClass, sw, XmNorientation, XmVERTICAL,
2202 XmNrepeatDelay, 10, NULL);
2203 frame = XtVaCreateManagedWidget("textFrame", xmFrameWidgetClass, sw,
2204 XmNshadowType, XmSHADOW_IN, NULL);
2205 text = XtVaCreateManagedWidget("text", textWidgetClass, frame,
2206 textNbacklightCharTypes, window->backlightCharTypes,
2207 textNrows, rows, textNcolumns, cols,
2208 textNlineNumCols, lineNumCols,
2209 textNemulateTabs, emTabDist,
2210 textNfont, GetDefaultFontStruct(window->fontList),
2211 textNhScrollBar, hScrollBar, textNvScrollBar, vScrollBar,
2212 textNreadOnly, IS_ANY_LOCKED(window->lockReasons),
2213 textNwordDelimiters, delimiters,
2214 textNwrapMargin, wrapMargin,
2215 textNautoIndent, window->indentStyle == AUTO_INDENT,
2216 textNsmartIndent, window->indentStyle == SMART_INDENT,
2217 textNautoWrap, window->wrapMode == NEWLINE_WRAP,
2218 textNcontinuousWrap, window->wrapMode == CONTINUOUS_WRAP,
2219 textNoverstrike, window->overstrike,
2220 textNhidePointer, (Boolean) GetPrefTypingHidesPointer(),
2221 textNcursorVPadding, GetVerticalAutoScroll(),
2222 NULL);
2224 XtVaSetValues(sw, XmNworkWindow, frame, XmNhorizontalScrollBar,
2225 hScrollBar, XmNverticalScrollBar, vScrollBar, NULL);
2227 /* add focus, drag, cursor tracking, and smart indent callbacks */
2228 XtAddCallback(text, textNfocusCallback, (XtCallbackProc)focusCB, window);
2229 XtAddCallback(text, textNcursorMovementCallback, (XtCallbackProc)movedCB,
2230 window);
2231 XtAddCallback(text, textNdragStartCallback, (XtCallbackProc)dragStartCB,
2232 window);
2233 XtAddCallback(text, textNdragEndCallback, (XtCallbackProc)dragEndCB,
2234 window);
2235 XtAddCallback(text, textNsmartIndentCallback, SmartIndentCB, window);
2237 /* This makes sure the text area initially has a the insert point shown
2238 ... (check if still true with the nedit text widget, probably not) */
2239 XmAddTabGroup(containingPane(text));
2241 /* compensate for Motif delete/backspace problem */
2242 RemapDeleteKey(text);
2244 /* Augment translation table for right button popup menu */
2245 AddBGMenuAction(text);
2247 /* If absolute line numbers will be needed for display in the statistics
2248 line, tell the widget to maintain them (otherwise, it's a costly
2249 operation and performance will be better without it) */
2250 TextDMaintainAbsLineNum(((TextWidget)text)->text.textD, window->showStats);
2252 return text;
2255 static void movedCB(Widget w, WindowInfo *window, XtPointer callData)
2257 if (window->ignoreModify)
2258 return;
2260 /* update line and column nubers in statistics line */
2261 UpdateStatsLine(window);
2263 /* Check the character before the cursor for matchable characters */
2264 FlashMatching(window, w);
2266 /* Check for changes to read-only status and/or file modifications */
2267 CheckForChangesToFile(window);
2270 static void modifiedCB(int pos, int nInserted, int nDeleted, int nRestyled,
2271 char *deletedText, void *cbArg)
2273 WindowInfo *window = (WindowInfo *)cbArg;
2274 int selected = window->buffer->primary.selected;
2276 /* update the table of bookmarks */
2277 if (!window->ignoreModify) {
2278 UpdateMarkTable(window, pos, nInserted, nDeleted);
2281 /* Check and dim/undim selection related menu items */
2282 if ((window->wasSelected && !selected) ||
2283 (!window->wasSelected && selected)) {
2284 window->wasSelected = selected;
2286 /* do not refresh shell-level items (window, menu-bar etc)
2287 when motifying non-top document */
2288 if (IsTopDocument(window)) {
2289 XtSetSensitive(window->printSelItem, selected);
2290 XtSetSensitive(window->cutItem, selected);
2291 XtSetSensitive(window->copyItem, selected);
2292 XtSetSensitive(window->delItem, selected);
2293 /* Note we don't change the selection for items like
2294 "Open Selected" and "Find Selected". That's because
2295 it works on selections in external applications.
2296 Desensitizing it if there's no NEdit selection
2297 disables this feature. */
2298 #ifndef VMS
2299 XtSetSensitive(window->filterItem, selected);
2300 #endif
2302 DimSelectionDepUserMenuItems(window, selected);
2303 if (window->replaceDlog != NULL)
2305 UpdateReplaceActionButtons(window);
2310 /* Make sure line number display is sufficient for new data */
2311 UpdateLineNumDisp(window);
2313 /* When the program needs to make a change to a text area without without
2314 recording it for undo or marking file as changed it sets ignoreModify */
2315 if (window->ignoreModify || (nDeleted == 0 && nInserted == 0))
2316 return;
2318 /* Save information for undoing this operation (this call also counts
2319 characters and editing operations for triggering autosave */
2320 SaveUndoInformation(window, pos, nInserted, nDeleted, deletedText);
2322 /* Trigger automatic backup if operation or character limits reached */
2323 if (window->autoSave &&
2324 (window->autoSaveCharCount > AUTOSAVE_CHAR_LIMIT ||
2325 window->autoSaveOpCount > AUTOSAVE_OP_LIMIT)) {
2326 WriteBackupFile(window);
2327 window->autoSaveCharCount = 0;
2328 window->autoSaveOpCount = 0;
2331 /* Indicate that the window has now been modified */
2332 SetWindowModified(window, TRUE);
2334 /* Update # of bytes, and line and col statistics */
2335 UpdateStatsLine(window);
2337 /* Check if external changes have been made to file and warn user */
2338 CheckForChangesToFile(window);
2341 static void focusCB(Widget w, WindowInfo *window, XtPointer callData)
2343 /* record which window pane last had the keyboard focus */
2344 window->lastFocus = w;
2346 /* update line number statistic to reflect current focus pane */
2347 UpdateStatsLine(window);
2349 /* finish off the current incremental search */
2350 EndISearch(window);
2352 /* Check for changes to read-only status and/or file modifications */
2353 CheckForChangesToFile(window);
2356 static void dragStartCB(Widget w, WindowInfo *window, XtPointer callData)
2358 /* don't record all of the intermediate drag steps for undo */
2359 window->ignoreModify = True;
2362 static void dragEndCB(Widget w, WindowInfo *window, dragEndCBStruct *callData)
2364 /* restore recording of undo information */
2365 window->ignoreModify = False;
2367 /* Do nothing if drag operation was canceled */
2368 if (callData->nCharsInserted == 0)
2369 return;
2371 /* Save information for undoing this operation not saved while
2372 undo recording was off */
2373 modifiedCB(callData->startPos, callData->nCharsInserted,
2374 callData->nCharsDeleted, 0, callData->deletedText, window);
2377 static void closeCB(Widget w, WindowInfo *window, XtPointer callData)
2379 window = WidgetToWindow(w);
2381 CloseDocumentWindow(w, window, callData);
2384 #ifndef NO_SESSION_RESTART
2385 static void saveYourselfCB(Widget w, Widget appShell, XtPointer callData)
2387 WindowInfo *win, *topWin, **revWindowList;
2388 char geometry[MAX_GEOM_STRING_LEN];
2389 int argc = 0, maxArgc, nWindows, i;
2390 char **argv;
2391 int wasIconic = False;
2392 int n, nItems;
2393 WidgetList children;
2395 /* Allocate memory for an argument list and for a reversed list of
2396 windows. The window list is reversed for IRIX 4DWM and any other
2397 window/session manager combination which uses window creation
2398 order for re-associating stored geometry information with
2399 new windows created by a restored application */
2400 maxArgc = 4; /* nedit -server -svrname name */
2401 nWindows = 0;
2402 for (win=WindowList; win!=NULL; win=win->next) {
2403 maxArgc += 5; /* -iconic -group -geometry WxH+x+y filename */
2404 nWindows++;
2406 argv = (char **)XtMalloc(maxArgc*sizeof(char *));
2407 revWindowList = (WindowInfo **)XtMalloc(sizeof(WindowInfo *)*nWindows);
2408 for (win=WindowList, i=nWindows-1; win!=NULL; win=win->next, i--)
2409 revWindowList[i] = win;
2411 /* Create command line arguments for restoring each window in the list */
2412 argv[argc++] = XtNewString(ArgV0);
2413 if (IsServer) {
2414 argv[argc++] = XtNewString("-server");
2415 if (GetPrefServerName()[0] != '\0') {
2416 argv[argc++] = XtNewString("-svrname");
2417 argv[argc++] = XtNewString(GetPrefServerName());
2421 /* editor windows are popup-shell children of top-level appShell */
2422 XtVaGetValues(appShell, XmNchildren, &children,
2423 XmNnumChildren, &nItems, NULL);
2425 for (n=nItems-1; n>=0; n--) {
2426 WidgetList tabs;
2427 int tabCount;
2429 if (strcmp(XtName(children[n]), "textShell") ||
2430 ((topWin = WidgetToWindow(children[n])) == NULL))
2431 continue; /* skip non-editor windows */
2433 /* create a group for each window */
2434 getGeometryString(topWin, geometry);
2435 argv[argc++] = XtNewString("-group");
2436 argv[argc++] = XtNewString("-geometry");
2437 argv[argc++] = XtNewString(geometry);
2438 if (IsIconic(topWin)) {
2439 argv[argc++] = XtNewString("-iconic");
2440 wasIconic = True;
2441 } else if (wasIconic) {
2442 argv[argc++] = XtNewString("-noiconic");
2443 wasIconic = False;
2446 /* add filename of each tab in window... */
2447 XtVaGetValues(topWin->tabBar, XmNtabWidgetList, &tabs,
2448 XmNtabCount, &tabCount, NULL);
2450 for (i=0; i< tabCount; i++) {
2451 win = TabToWindow(tabs[i]);
2452 if (win->filenameSet) {
2453 /* add filename */
2454 argv[argc] = XtMalloc(strlen(win->path) +
2455 strlen(win->filename) + 1);
2456 sprintf(argv[argc++], "%s%s", win->path, win->filename);
2461 XtFree((char *)revWindowList);
2463 /* Set the window's WM_COMMAND property to the created command line */
2464 XSetCommand(TheDisplay, XtWindow(appShell), argv, argc);
2465 for (i=0; i<argc; i++)
2466 XtFree(argv[i]);
2467 XtFree((char *)argv);
2470 void AttachSessionMgrHandler(Widget appShell)
2472 static Atom wmpAtom, syAtom = 0;
2474 /* Add wm protocol callback for making nedit restartable by session
2475 managers. Doesn't yet handle multiple-desktops or iconifying right. */
2476 if (syAtom == 0) {
2477 wmpAtom = XmInternAtom(TheDisplay, "WM_PROTOCOLS", FALSE);
2478 syAtom = XmInternAtom(TheDisplay, "WM_SAVE_YOURSELF", FALSE);
2480 XmAddProtocolCallback(appShell, wmpAtom, syAtom,
2481 (XtCallbackProc)saveYourselfCB, (XtPointer)appShell);
2483 #endif /* NO_SESSION_RESTART */
2486 ** Returns true if window is iconic (as determined by the WM_STATE property
2487 ** on the shell window. I think this is the most reliable way to tell,
2488 ** but if someone has a better idea please send me a note).
2490 int IsIconic(WindowInfo *window)
2492 unsigned long *property = NULL;
2493 unsigned long nItems;
2494 unsigned long leftover;
2495 static Atom wmStateAtom = 0;
2496 Atom actualType;
2497 int actualFormat;
2498 int result;
2500 if (wmStateAtom == 0)
2501 wmStateAtom = XInternAtom(XtDisplay(window->shell), "WM_STATE", False);
2502 if (XGetWindowProperty(XtDisplay(window->shell), XtWindow(window->shell),
2503 wmStateAtom, 0L, 1L, False, wmStateAtom, &actualType, &actualFormat,
2504 &nItems, &leftover, (unsigned char **)&property) != Success ||
2505 nItems != 1 || property == NULL)
2506 return FALSE;
2507 result = *property == IconicState;
2508 XtFree((char *)property);
2509 return result;
2513 ** Add a window to the the window list.
2515 static void addToWindowList(WindowInfo *window)
2517 WindowInfo *temp;
2519 temp = WindowList;
2520 WindowList = window;
2521 window->next = temp;
2525 ** Remove a window from the list of windows
2527 static void removeFromWindowList(WindowInfo *window)
2529 WindowInfo *temp;
2531 if (WindowList == window)
2532 WindowList = window->next;
2533 else {
2534 for (temp = WindowList; temp != NULL; temp = temp->next) {
2535 if (temp->next == window) {
2536 temp->next = window->next;
2537 break;
2544 ** Determine how wide the line number field has to be to
2545 ** display all possible line numbers in the text area
2547 static int requestLineNumCols(textDisp *textD)
2549 int reqCols;
2551 reqCols = textD->nBufferLines<1 ? 1 :
2552 log10((double)textD->nBufferLines)+1;
2554 if (reqCols < MIN_LINE_NUM_COLS)
2555 reqCols = MIN_LINE_NUM_COLS;
2557 return reqCols;
2561 ** If necessary, enlarges the window and line number display area
2562 ** to make room for numbers.
2564 void UpdateLineNumDisp(WindowInfo *window)
2566 Dimension windowWidth;
2567 int i, fontWidth, reqCols, lineNumCols;
2568 int oldWidth, newWidth, marginWidth;
2569 Widget text;
2570 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
2572 if (!window->showLineNumbers)
2573 return;
2575 /* Decide how wide the line number field has to be to display all
2576 possible line numbers */
2577 reqCols = requestLineNumCols(textD);
2579 /* Is the width of the line number area sufficient to display all the
2580 line numbers in the file? If not, expand line number field, and the
2581 window width. */
2582 XtVaGetValues(window->textArea, textNlineNumCols, &lineNumCols,
2583 textNmarginWidth, &marginWidth, NULL);
2584 if (lineNumCols < reqCols) {
2585 fontWidth = textD->fontStruct->max_bounds.width;
2586 oldWidth = textD->left - marginWidth;
2587 newWidth = reqCols * fontWidth + marginWidth;
2588 XtVaGetValues(window->shell, XmNwidth, &windowWidth, NULL);
2589 XtVaSetValues(window->shell, XmNwidth,
2590 windowWidth + newWidth-oldWidth, NULL);
2592 UpdateWMSizeHints(window);
2593 for (i=0; i<=window->nPanes; i++) {
2594 text = i==0 ? window->textArea : window->textPanes[i-1];
2595 XtVaSetValues(text, textNlineNumCols, reqCols, NULL);
2601 ** Update the optional statistics line.
2603 void UpdateStatsLine(WindowInfo *window)
2605 int line, pos, colNum;
2606 char *string, *format, slinecol[32];
2607 Widget statW = window->statsLine;
2608 XmString xmslinecol;
2609 #ifdef SGI_CUSTOM
2610 char *sleft, *smid, *sright;
2611 #endif
2613 if (!IsTopDocument(window))
2614 return;
2616 /* This routine is called for each character typed, so its performance
2617 affects overall editor perfomance. Only update if the line is on. */
2618 if (!window->showStats)
2619 return;
2621 /* Compose the string to display. If line # isn't available, leave it off */
2622 pos = TextGetCursorPos(window->lastFocus);
2623 string = XtMalloc(strlen(window->filename) + strlen(window->path) + 45);
2624 format = window->fileFormat == DOS_FILE_FORMAT ? " DOS" :
2625 (window->fileFormat == MAC_FILE_FORMAT ? " Mac" : "");
2626 if (!TextPosToLineAndCol(window->lastFocus, pos, &line, &colNum)) {
2627 sprintf(string, "%s%s%s %d bytes", window->path, window->filename,
2628 format, window->buffer->length);
2629 sprintf(slinecol, "L: --- C: ---");
2630 } else {
2631 sprintf(slinecol, "L: %d C: %d", line, colNum);
2632 if (window->showLineNumbers)
2633 sprintf(string, "%s%s%s byte %d of %d", window->path,
2634 window->filename, format, pos,
2635 window->buffer->length);
2636 else
2637 sprintf(string, "%s%s%s %d bytes", window->path,
2638 window->filename, format, window->buffer->length);
2641 /* Update the line/column number */
2642 xmslinecol = XmStringCreateSimple(slinecol);
2643 XtVaSetValues( window->statsLineColNo,
2644 XmNlabelString, xmslinecol, NULL );
2645 XmStringFree(xmslinecol);
2647 /* Don't clobber the line if there's a special message being displayed */
2648 if (!window->modeMessageDisplayed) {
2649 /* Change the text in the stats line */
2650 #ifdef SGI_CUSTOM
2651 /* don't show full pathname, just dir and filename (+ byte info) */
2652 smid = strchr(string, '/');
2653 if ( smid != NULL ) {
2654 sleft = smid;
2655 sright = strrchr(string, '/');
2656 while (strcmp(smid, sright)) {
2657 sleft = smid;
2658 smid = strchr(sleft + 1, '/');
2660 XmTextReplace(statW, 0, XmTextGetLastPosition(statW), sleft + 1);
2661 } else
2662 XmTextReplace(statW, 0, XmTextGetLastPosition(statW), string);
2663 #else
2664 XmTextReplace(statW, 0, XmTextGetLastPosition(statW), string);
2665 #endif
2667 XtFree(string);
2669 /* Update the line/col display */
2670 xmslinecol = XmStringCreateSimple(slinecol);
2671 XtVaSetValues(window->statsLineColNo,
2672 XmNlabelString, xmslinecol, NULL);
2673 XmStringFree(xmslinecol);
2676 static Boolean currentlyBusy = False;
2677 static long busyStartTime = 0;
2678 static Boolean modeMessageSet = False;
2681 * Auxiliary function for measuring elapsed time during busy waits.
2683 static long getRelTimeInTenthsOfSeconds()
2685 #ifdef __unix__
2686 struct timeval current;
2687 gettimeofday(&current, NULL);
2688 return (current.tv_sec*10 + current.tv_usec/100000) & 0xFFFFFFFL;
2689 #else
2690 time_t current;
2691 time(&current);
2692 return (current*10) & 0xFFFFFFFL;
2693 #endif
2696 void AllWindowsBusy(const char *message)
2698 WindowInfo *w;
2700 if (!currentlyBusy)
2702 busyStartTime = getRelTimeInTenthsOfSeconds();
2703 modeMessageSet = False;
2705 for (w=WindowList; w!=NULL; w=w->next)
2707 /* We don't the display message here yet, but defer it for
2708 a while. If the wait is short, we don't want
2709 to have it flash on and off the screen. However,
2710 we can't use a time since in generally we are in
2711 a tight loop and only processing exposure events, so it's
2712 up to the caller to make sure that this routine is called
2713 at regular intervals.
2715 BeginWait(w->shell);
2717 } else if (!modeMessageSet && message &&
2718 getRelTimeInTenthsOfSeconds() - busyStartTime > 10) {
2719 /* Show the mode message when we've been busy for more than a second */
2720 for (w=WindowList; w!=NULL; w=w->next) {
2721 SetModeMessage(w, message);
2723 modeMessageSet = True;
2725 BusyWait(WindowList->shell);
2727 currentlyBusy = True;
2730 void AllWindowsUnbusy(void)
2732 WindowInfo *w;
2734 for (w=WindowList; w!=NULL; w=w->next)
2736 ClearModeMessage(w);
2737 EndWait(w->shell);
2740 currentlyBusy = False;
2741 modeMessageSet = False;
2742 busyStartTime = 0;
2746 ** Paned windows are impossible to adjust after they are created, which makes
2747 ** them nearly useless for NEdit (or any application which needs to dynamically
2748 ** adjust the panes) unless you tweek some private data to overwrite the
2749 ** desired and minimum pane heights which were set at creation time. These
2750 ** will probably break in a future release of Motif because of dependence on
2751 ** private data.
2753 static void setPaneDesiredHeight(Widget w, int height)
2755 ((XmPanedWindowConstraintPtr)w->core.constraints)->panedw.dheight = height;
2757 static void setPaneMinHeight(Widget w, int min)
2759 ((XmPanedWindowConstraintPtr)w->core.constraints)->panedw.min = min;
2763 ** Update the window manager's size hints. These tell it the increments in
2764 ** which it is allowed to resize the window. While this isn't particularly
2765 ** important for NEdit (since it can tolerate any window size), setting these
2766 ** hints also makes the resize indicator show the window size in characters
2767 ** rather than pixels, which is very helpful to users.
2769 void UpdateWMSizeHints(WindowInfo *window)
2771 Dimension shellWidth, shellHeight, textHeight, hScrollBarHeight;
2772 int marginHeight, marginWidth, totalHeight;
2773 XFontStruct *fs;
2774 int i, baseWidth, baseHeight, fontHeight, fontWidth;
2775 Widget hScrollBar;
2776 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
2778 /* Find the base (non-expandable) width and height of the editor window */
2779 XtVaGetValues(window->textArea, XmNheight, &textHeight,
2780 textNmarginHeight, &marginHeight, textNmarginWidth, &marginWidth,
2781 NULL);
2782 totalHeight = textHeight - 2*marginHeight;
2783 for (i=0; i<window->nPanes; i++) {
2784 XtVaGetValues(window->textPanes[i], XmNheight, &textHeight,
2785 textNhScrollBar, &hScrollBar, NULL);
2786 totalHeight += textHeight - 2*marginHeight;
2787 if (!XtIsManaged(hScrollBar)) {
2788 XtVaGetValues(hScrollBar, XmNheight, &hScrollBarHeight, NULL);
2789 totalHeight -= hScrollBarHeight;
2792 XtVaGetValues(window->shell, XmNwidth, &shellWidth,
2793 XmNheight, &shellHeight, NULL);
2794 baseWidth = shellWidth - textD->width;
2795 baseHeight = shellHeight - totalHeight;
2797 /* Find the dimensions of a single character of the text font */
2798 XtVaGetValues(window->textArea, textNfont, &fs, NULL);
2799 fontHeight = textD->ascent + textD->descent;
2800 fontWidth = fs->max_bounds.width;
2802 /* Set the size hints in the shell widget */
2803 XtVaSetValues(window->shell, XmNwidthInc, fs->max_bounds.width,
2804 XmNheightInc, fontHeight,
2805 XmNbaseWidth, baseWidth, XmNbaseHeight, baseHeight,
2806 XmNminWidth, baseWidth + fontWidth,
2807 XmNminHeight, baseHeight + (1+window->nPanes) * fontHeight, NULL);
2811 ** Update the minimum allowable height for a split pane after a change
2812 ** to font or margin height.
2814 void UpdateMinPaneHeights(WindowInfo *window)
2816 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
2817 Dimension hsbHeight, swMarginHeight,frameShadowHeight;
2818 int i, marginHeight, minPaneHeight;
2819 Widget hScrollBar;
2821 /* find the minimum allowable size for a pane */
2822 XtVaGetValues(window->textArea, textNhScrollBar, &hScrollBar, NULL);
2823 XtVaGetValues(containingPane(window->textArea),
2824 XmNscrolledWindowMarginHeight, &swMarginHeight, NULL);
2825 XtVaGetValues(XtParent(window->textArea),
2826 XmNshadowThickness, &frameShadowHeight, NULL);
2827 XtVaGetValues(window->textArea, textNmarginHeight, &marginHeight, NULL);
2828 XtVaGetValues(hScrollBar, XmNheight, &hsbHeight, NULL);
2829 minPaneHeight = textD->ascent + textD->descent + marginHeight*2 +
2830 swMarginHeight*2 + hsbHeight + 2*frameShadowHeight;
2832 /* Set it in all of the widgets in the window */
2833 setPaneMinHeight(containingPane(window->textArea), minPaneHeight);
2834 for (i=0; i<window->nPanes; i++)
2835 setPaneMinHeight(containingPane(window->textPanes[i]),
2836 minPaneHeight);
2839 /* Add an icon to an applicaction shell widget. addWindowIcon adds a large
2840 ** (primary window) icon, AddSmallIcon adds a small (secondary window) icon.
2842 ** Note: I would prefer that these were not hardwired, but yhere is something
2843 ** weird about the XmNiconPixmap resource that prevents it from being set
2844 ** from the defaults in the application resource database.
2846 static void addWindowIcon(Widget shell)
2848 static Pixmap iconPixmap = 0, maskPixmap = 0;
2850 if (iconPixmap == 0) {
2851 iconPixmap = XCreateBitmapFromData(TheDisplay,
2852 RootWindowOfScreen(XtScreen(shell)), (char *)iconBits,
2853 iconBitmapWidth, iconBitmapHeight);
2854 maskPixmap = XCreateBitmapFromData(TheDisplay,
2855 RootWindowOfScreen(XtScreen(shell)), (char *)maskBits,
2856 iconBitmapWidth, iconBitmapHeight);
2858 XtVaSetValues(shell, XmNiconPixmap, iconPixmap, XmNiconMask, maskPixmap,
2859 NULL);
2861 void AddSmallIcon(Widget shell)
2863 static Pixmap iconPixmap = 0, maskPixmap = 0;
2865 if (iconPixmap == 0) {
2866 iconPixmap = XCreateBitmapFromData(TheDisplay,
2867 RootWindowOfScreen(XtScreen(shell)), (char *)n_bits,
2868 n_width, n_height);
2869 maskPixmap = XCreateBitmapFromData(TheDisplay,
2870 RootWindowOfScreen(XtScreen(shell)), (char *)n_mask,
2871 n_width, n_height);
2873 XtVaSetValues(shell, XmNiconPixmap, iconPixmap,
2874 XmNiconMask, maskPixmap, NULL);
2878 ** Create pixmap per the widget's color depth setting.
2880 ** This fixes a BadMatch (X_CopyArea) error due to mismatching of
2881 ** color depth between the bitmap (depth of 1) and the screen,
2882 ** specifically on when linked to LessTif v1.2 (release 0.93.18
2883 ** & 0.93.94 tested). LessTif v2.x showed no such problem.
2885 static Pixmap createBitmapWithDepth(Widget w, char *data, unsigned int width,
2886 unsigned int height)
2888 Pixmap pixmap;
2889 Pixel fg, bg;
2890 int depth;
2892 XtVaGetValues (w, XmNforeground, &fg, XmNbackground, &bg,
2893 XmNdepth, &depth, NULL);
2894 pixmap = XCreatePixmapFromBitmapData(XtDisplay(w),
2895 RootWindowOfScreen(XtScreen(w)), (char *)data,
2896 width, height, fg, bg, depth);
2898 return pixmap;
2902 ** Save the position and size of a window as an X standard geometry string.
2903 ** A string of at least MAX_GEOMETRY_STRING_LEN characters should be
2904 ** provided in the argument "geomString" to receive the result.
2906 static void getGeometryString(WindowInfo *window, char *geomString)
2908 int x, y, fontWidth, fontHeight, baseWidth, baseHeight;
2909 unsigned int width, height, dummyW, dummyH, bw, depth, nChild;
2910 Window parent, root, *child, w = XtWindow(window->shell);
2911 Display *dpy = XtDisplay(window->shell);
2913 /* Find the width and height from the window of the shell */
2914 XGetGeometry(dpy, w, &root, &x, &y, &width, &height, &bw, &depth);
2916 /* Find the top left corner (x and y) of the window decorations. (This
2917 is what's required in the geometry string to restore the window to it's
2918 original position, since the window manager re-parents the window to
2919 add it's title bar and menus, and moves the requested window down and
2920 to the left.) The position is found by traversing the window hier-
2921 archy back to the window to the last parent before the root window */
2922 for(;;) {
2923 XQueryTree(dpy, w, &root, &parent, &child, &nChild);
2924 XFree((char*)child);
2925 if (parent == root)
2926 break;
2927 w = parent;
2929 XGetGeometry(dpy, w, &root, &x, &y, &dummyW, &dummyH, &bw, &depth);
2931 /* Use window manager size hints (set by UpdateWMSizeHints) to
2932 translate the width and height into characters, as opposed to pixels */
2933 XtVaGetValues(window->shell, XmNwidthInc, &fontWidth,
2934 XmNheightInc, &fontHeight, XmNbaseWidth, &baseWidth,
2935 XmNbaseHeight, &baseHeight, NULL);
2936 width = (width-baseWidth) / fontWidth;
2937 height = (height-baseHeight) / fontHeight;
2939 /* Write the string */
2940 CreateGeometryString(geomString, x, y, width, height,
2941 XValue | YValue | WidthValue | HeightValue);
2945 ** Xt timer procedure for updating size hints. The new sizes of objects in
2946 ** the window are not ready immediately after adding or removing panes. This
2947 ** is a timer routine to be invoked with a timeout of 0 to give the event
2948 ** loop a chance to finish processing the size changes before reading them
2949 ** out for setting the window manager size hints.
2951 static void wmSizeUpdateProc(XtPointer clientData, XtIntervalId *id)
2953 UpdateWMSizeHints((WindowInfo *)clientData);
2956 #ifdef ROWCOLPATCH
2958 ** There is a bad memory reference in the delete_child method of the
2959 ** RowColumn widget in some Motif versions (so far, just Solaris with Motif
2960 ** 1.2.3) which appears durring the phase 2 destroy of the widget. This
2961 ** patch replaces the method with a call to the Composite widget's
2962 ** delete_child method. The composite delete_child method handles part,
2963 ** but not all of what would have been done by the original method, meaning
2964 ** that this is dangerous and should be used sparingly. Note that
2965 ** patchRowCol is called only in CloseWindow, before the widget is about to
2966 ** be destroyed, and only on systems where the bug has been observed
2968 static void patchRowCol(Widget w)
2970 ((XmRowColumnClassRec *)XtClass(w))->composite_class.delete_child =
2971 patchedRemoveChild;
2973 static void patchedRemoveChild(Widget child)
2975 /* Call composite class method instead of broken row col delete_child
2976 method */
2977 (*((CompositeWidgetClass)compositeWidgetClass)->composite_class.
2978 delete_child) (child);
2980 #endif /* ROWCOLPATCH */
2983 ** Set the backlight character class string
2985 void SetBacklightChars(WindowInfo *window, char *applyBacklightTypes)
2987 int i;
2988 int is_applied = XmToggleButtonGetState(window->backlightCharsItem) ? 1 : 0;
2989 int do_apply = applyBacklightTypes ? 1 : 0;
2991 window->backlightChars = do_apply;
2993 XtFree(window->backlightCharTypes);
2994 if (window->backlightChars &&
2995 (window->backlightCharTypes = XtMalloc(strlen(applyBacklightTypes)+1)))
2996 strcpy(window->backlightCharTypes, applyBacklightTypes);
2997 else
2998 window->backlightCharTypes = NULL;
3000 XtVaSetValues(window->textArea,
3001 textNbacklightCharTypes, window->backlightCharTypes, 0);
3002 for (i=0; i<window->nPanes; i++)
3003 XtVaSetValues(window->textPanes[i],
3004 textNbacklightCharTypes, window->backlightCharTypes, 0);
3005 if (is_applied != do_apply)
3006 SetToggleButtonState(window, window->backlightCharsItem, do_apply, False);
3009 static int sortAlphabetical(const void* k1, const void* k2)
3011 const char* key1 = *(const char**)k1;
3012 const char* key2 = *(const char**)k2;
3013 return strcmp(key1, key2);
3017 * Checks whether a given virtual key binding string is invalid.
3018 * A binding is considered invalid if there are duplicate key entries.
3020 static int virtKeyBindingsAreInvalid(const unsigned char* bindings)
3022 int maxCount = 1, i, count;
3023 const char *pos = (const char*)bindings;
3024 char *copy;
3025 char *pos2, *pos3;
3026 char **keys;
3028 /* First count the number of bindings; bindings are separated by \n
3029 strings. The number of bindings equals the number of \n + 1.
3030 Beware of leading and trailing \n; the number is actually an
3031 upper bound on the number of entries. */
3032 while ((pos = strstr(pos, "\n")))
3034 ++pos;
3035 ++maxCount;
3038 if (maxCount == 1)
3039 return False; /* One binding is always ok */
3041 keys = (char**)malloc(maxCount*sizeof(char*));
3042 copy = XtNewString((const char*)bindings);
3043 i = 0;
3044 pos2 = copy;
3046 count = 0;
3047 while (i<maxCount && pos2 && *pos2)
3049 while (isspace((int) *pos2) || *pos2 == '\n') ++pos2;
3051 if (*pos2 == '!') /* Ignore comment lines */
3053 pos2 = strstr(pos2, "\n");
3054 continue; /* Go to the next line */
3057 if (*pos2)
3059 keys[i++] = pos2;
3060 ++count;
3061 pos3 = strstr(pos2, ":");
3062 if (pos3)
3064 *pos3++ = 0; /* Cut the string and jump to the next entry */
3065 pos2 = pos3;
3067 pos2 = strstr(pos2, "\n");
3071 if (count <= 1)
3073 free(keys);
3074 XtFree(copy);
3075 return False; /* No conflict */
3078 /* Sort the keys and look for duplicates */
3079 qsort((void*)keys, count, sizeof(const char*), sortAlphabetical);
3080 for (i=1; i<count; ++i)
3082 if (!strcmp(keys[i-1], keys[i]))
3084 /* Duplicate detected */
3085 free(keys);
3086 XtFree(copy);
3087 return True;
3090 free(keys);
3091 XtFree(copy);
3092 return False;
3096 * Optionally sanitizes the Motif default virtual key bindings.
3097 * Some applications install invalid bindings (attached to the root window),
3098 * which cause certain keys to malfunction in NEdit.
3099 * Through an X-resource, users can choose whether they want
3100 * - to always keep the existing bindings
3101 * - to override the bindings only if they are invalid
3102 * - to always override the existing bindings.
3105 static Atom virtKeyAtom;
3107 static unsigned char* sanitizeVirtualKeyBindings()
3109 int overrideBindings = GetPrefOverrideVirtKeyBindings();
3110 Window rootWindow;
3111 const char *virtKeyPropName = "_MOTIF_DEFAULT_BINDINGS";
3112 Atom dummyAtom;
3113 int getFmt;
3114 unsigned long dummyULong, nItems;
3115 unsigned char *insaneVirtKeyBindings = NULL;
3117 if (overrideBindings == VIRT_KEY_OVERRIDE_NEVER) return NULL;
3119 virtKeyAtom = XInternAtom(TheDisplay, virtKeyPropName, False);
3120 rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
3122 /* Remove the property, if it exists; we'll restore it later again */
3123 if (XGetWindowProperty(TheDisplay, rootWindow, virtKeyAtom, 0, INT_MAX,
3124 True, XA_STRING, &dummyAtom, &getFmt, &nItems,
3125 &dummyULong, &insaneVirtKeyBindings) != Success
3126 || nItems == 0)
3128 return NULL; /* No binding yet; nothing to do */
3131 if (overrideBindings == VIRT_KEY_OVERRIDE_AUTO)
3133 if (!virtKeyBindingsAreInvalid(insaneVirtKeyBindings))
3135 /* Restore the property immediately; it seems valid */
3136 XChangeProperty(TheDisplay, rootWindow, virtKeyAtom, XA_STRING, 8,
3137 PropModeReplace, insaneVirtKeyBindings,
3138 strlen((const char*)insaneVirtKeyBindings));
3139 XFree((char*)insaneVirtKeyBindings);
3140 return NULL; /* Prevent restoration */
3143 return insaneVirtKeyBindings;
3147 * NEdit should not mess with the bindings installed by other apps, so we
3148 * just restore whatever was installed, if necessary
3150 static void restoreInsaneVirtualKeyBindings(unsigned char *insaneVirtKeyBindings)
3152 if (insaneVirtKeyBindings)
3154 Window rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
3155 /* Restore the root window atom, such that we don't affect
3156 other apps. */
3157 XChangeProperty(TheDisplay, rootWindow, virtKeyAtom, XA_STRING, 8,
3158 PropModeReplace, insaneVirtKeyBindings,
3159 strlen((const char*)insaneVirtKeyBindings));
3160 XFree((char*)insaneVirtKeyBindings);
3165 ** perform generic management on the children (toolbars) of toolBarsForm,
3166 ** a.k.a. statsForm, by setting the form attachment of the managed child
3167 ** widgets per their position/order.
3169 ** You can optionally create separator after a toolbar widget with it's
3170 ** widget name set to "TOOLBAR_SEP", which will appear below the toolbar
3171 ** widget. These seperators will then be managed automatically by this
3172 ** routine along with the toolbars they 'attached' to.
3174 ** It also takes care of the attachment offset settings of the child
3175 ** widgets to keep the border lines of the parent form displayed, so
3176 ** you don't have set them before hand.
3178 ** Note: XtManage/XtUnmange the target child (toolbar) before calling this
3179 ** function.
3181 ** Returns the last toolbar widget managed.
3184 static Widget manageToolBars(Widget toolBarsForm)
3186 Widget topWidget = NULL;
3187 WidgetList children;
3188 int n, nItems=0;
3190 XtVaGetValues(toolBarsForm, XmNchildren, &children,
3191 XmNnumChildren, &nItems, NULL);
3193 for (n=0; n<nItems; n++) {
3194 Widget tbar = children[n];
3196 if (XtIsManaged(tbar)) {
3197 if (topWidget) {
3198 XtVaSetValues(tbar, XmNtopAttachment, XmATTACH_WIDGET,
3199 XmNtopWidget, topWidget,
3200 XmNbottomAttachment, XmATTACH_NONE,
3201 XmNleftOffset, STAT_SHADOW_THICKNESS,
3202 XmNrightOffset, STAT_SHADOW_THICKNESS,
3203 NULL);
3205 else {
3206 /* the very first toolbar on top */
3207 XtVaSetValues(tbar, XmNtopAttachment, XmATTACH_FORM,
3208 XmNbottomAttachment, XmATTACH_NONE,
3209 XmNleftOffset, STAT_SHADOW_THICKNESS,
3210 XmNtopOffset, STAT_SHADOW_THICKNESS,
3211 XmNrightOffset, STAT_SHADOW_THICKNESS,
3212 NULL);
3215 topWidget = tbar;
3217 /* if the next widget is a separator, turn it on */
3218 if (n+1<nItems && !strcmp(XtName(children[n+1]), "TOOLBAR_SEP")) {
3219 XtManageChild(children[n+1]);
3222 else {
3223 /* Remove top attachment to widget to avoid circular dependency.
3224 Attach bottom to form so that when the widget is redisplayed
3225 later, it will trigger the parent form to resize properly as
3226 if the widget is being inserted */
3227 XtVaSetValues(tbar, XmNtopAttachment, XmATTACH_NONE,
3228 XmNbottomAttachment, XmATTACH_FORM, NULL);
3230 /* if the next widget is a separator, turn it off */
3231 if (n+1<nItems && !strcmp(XtName(children[n+1]), "TOOLBAR_SEP")) {
3232 XtUnmanageChild(children[n+1]);
3237 if (topWidget) {
3238 if (strcmp(XtName(topWidget), "TOOLBAR_SEP")) {
3239 XtVaSetValues(topWidget,
3240 XmNbottomAttachment, XmATTACH_FORM,
3241 XmNbottomOffset, STAT_SHADOW_THICKNESS,
3242 NULL);
3244 else {
3245 /* is a separator */
3246 Widget wgt;
3247 XtVaGetValues(topWidget, XmNtopWidget, &wgt, NULL);
3249 /* don't need sep below bottom-most toolbar */
3250 XtUnmanageChild(topWidget);
3251 XtVaSetValues(wgt,
3252 XmNbottomAttachment, XmATTACH_FORM,
3253 XmNbottomOffset, STAT_SHADOW_THICKNESS,
3254 NULL);
3258 return topWidget;
3262 ** Calculate the dimension of the text area, in terms of rows & cols,
3263 ** as if there's only one single text pane in the window.
3265 static void getTextPaneDimension(WindowInfo *window, int *nRows, int *nCols)
3267 Widget hScrollBar;
3268 Dimension hScrollBarHeight, paneHeight;
3269 int marginHeight, marginWidth, totalHeight, fontHeight;
3270 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
3272 /* width is the same for panes */
3273 XtVaGetValues(window->textArea, textNcolumns, nCols, NULL);
3275 /* we have to work out the height, as the text area may have been split */
3276 XtVaGetValues(window->textArea, textNhScrollBar, &hScrollBar,
3277 textNmarginHeight, &marginHeight, textNmarginWidth, &marginWidth,
3278 NULL);
3279 XtVaGetValues(hScrollBar, XmNheight, &hScrollBarHeight, NULL);
3280 XtVaGetValues(window->splitPane, XmNheight, &paneHeight, NULL);
3281 totalHeight = paneHeight - 2*marginHeight -hScrollBarHeight;
3282 fontHeight = textD->ascent + textD->descent;
3283 *nRows = totalHeight/fontHeight;
3287 ** Create a new document in the shell window.
3288 ** Document are created in 'background' so that the user
3289 ** menus, ie. the Macro/Shell/BG menus, will not be updated
3290 ** unnecessarily; hence speeding up the process of opening
3291 ** multiple files.
3293 WindowInfo *CreateDocument(WindowInfo *shellWindow, const char *name,
3294 char *geometry, int iconic)
3296 Widget pane, text;
3297 WindowInfo *window;
3298 int nCols, nRows;
3300 #ifdef SGI_CUSTOM
3301 char sgi_title[MAXPATHLEN + 14 + SGI_WINDOW_TITLE_LEN] = SGI_WINDOW_TITLE;
3302 #endif
3304 /* Allocate some memory for the new window data structure */
3305 window = (WindowInfo *)XtMalloc(sizeof(WindowInfo));
3307 /* inherit settings and later reset those required */
3308 memcpy(window, shellWindow, sizeof(WindowInfo));
3310 #if 0
3311 /* share these dialog items with parent shell */
3312 window->replaceDlog = NULL;
3313 window->replaceText = NULL;
3314 window->replaceWithText = NULL;
3315 window->replaceWordToggle = NULL;
3316 window->replaceCaseToggle = NULL;
3317 window->replaceRegexToggle = NULL;
3318 window->findDlog = NULL;
3319 window->findText = NULL;
3320 window->findWordToggle = NULL;
3321 window->findCaseToggle = NULL;
3322 window->findRegexToggle = NULL;
3323 window->replaceMultiFileDlog = NULL;
3324 window->replaceMultiFilePathBtn = NULL;
3325 window->replaceMultiFileList = NULL;
3326 window->showLineNumbers = GetPrefLineNums();
3327 window->showStats = GetPrefStatsLine();
3328 window->showISearchLine = GetPrefISearchLine();
3329 #endif
3331 window->multiFileReplSelected = FALSE;
3332 window->multiFileBusy = FALSE;
3333 window->writableWindows = NULL;
3334 window->nWritableWindows = 0;
3335 window->fileChanged = FALSE;
3336 window->fileMissing = True;
3337 window->fileMode = 0;
3338 window->filenameSet = FALSE;
3339 window->fileFormat = UNIX_FILE_FORMAT;
3340 window->lastModTime = 0;
3341 strcpy(window->filename, name);
3342 window->undo = NULL;
3343 window->redo = NULL;
3344 window->nPanes = 0;
3345 window->autoSaveCharCount = 0;
3346 window->autoSaveOpCount = 0;
3347 window->undoOpCount = 0;
3348 window->undoMemUsed = 0;
3349 CLEAR_ALL_LOCKS(window->lockReasons);
3350 window->indentStyle = GetPrefAutoIndent(PLAIN_LANGUAGE_MODE);
3351 window->autoSave = GetPrefAutoSave();
3352 window->saveOldVersion = GetPrefSaveOldVersion();
3353 window->wrapMode = GetPrefWrap(PLAIN_LANGUAGE_MODE);
3354 window->overstrike = False;
3355 window->showMatchingStyle = GetPrefShowMatching();
3356 window->matchSyntaxBased = GetPrefMatchSyntaxBased();
3357 window->highlightSyntax = GetPrefHighlightSyntax();
3358 window->backlightCharTypes = NULL;
3359 window->backlightChars = GetPrefBacklightChars();
3360 if (window->backlightChars) {
3361 char *cTypes = GetPrefBacklightCharTypes();
3362 if (cTypes && window->backlightChars) {
3363 if ((window->backlightCharTypes = XtMalloc(strlen(cTypes) + 1)))
3364 strcpy(window->backlightCharTypes, cTypes);
3367 window->modeMessageDisplayed = FALSE;
3368 window->modeMessage = NULL;
3369 window->ignoreModify = FALSE;
3370 window->windowMenuValid = FALSE;
3371 window->prevOpenMenuValid = FALSE;
3372 window->flashTimeoutID = 0;
3373 window->wasSelected = FALSE;
3374 strcpy(window->fontName, GetPrefFontName());
3375 strcpy(window->italicFontName, GetPrefItalicFontName());
3376 strcpy(window->boldFontName, GetPrefBoldFontName());
3377 strcpy(window->boldItalicFontName, GetPrefBoldItalicFontName());
3378 window->colorDialog = NULL;
3379 window->fontList = GetPrefFontList();
3380 window->italicFontStruct = GetPrefItalicFont();
3381 window->boldFontStruct = GetPrefBoldFont();
3382 window->boldItalicFontStruct = GetPrefBoldItalicFont();
3383 window->fontDialog = NULL;
3384 window->nMarks = 0;
3385 window->markTimeoutID = 0;
3386 window->highlightData = NULL;
3387 window->shellCmdData = NULL;
3388 window->macroCmdData = NULL;
3389 window->smartIndentData = NULL;
3390 window->languageMode = PLAIN_LANGUAGE_MODE;
3391 window->iSearchHistIndex = 0;
3392 window->iSearchStartPos = -1;
3393 window->replaceLastRegexCase = TRUE;
3394 window->replaceLastLiteralCase = FALSE;
3395 window->iSearchLastRegexCase = TRUE;
3396 window->iSearchLastLiteralCase = FALSE;
3397 window->findLastRegexCase = TRUE;
3398 window->findLastLiteralCase = FALSE;
3399 window->tab = NULL;
3401 if (window->fontList == NULL)
3402 XtVaGetValues(shellWindow->statsLine, XmNfontList,
3403 &window->fontList,NULL);
3405 getTextPaneDimension(shellWindow, &nRows, &nCols);
3407 /* Create pane that actaully holds the new document. As
3408 document is created in 'background', we need to hide
3409 it. If we leave it unmanaged without setting it to
3410 the XmNworkWindow of the mainWin, due to a unknown
3411 bug in Motif where splitpane's scrollWindow child
3412 somehow came up with a height taller than the splitpane,
3413 the bottom part of the text editing widget is obstructed
3414 when later brought up by RaiseDocument(). So we first
3415 manage it hidden, then unmanage it and reset XmNworkWindow,
3416 then let RaiseDocument() show it later. */
3417 pane = XtVaCreateWidget("pane",
3418 xmPanedWindowWidgetClass, window->mainWin,
3419 XmNmarginWidth, 0, XmNmarginHeight, 0, XmNseparatorOn, False,
3420 XmNspacing, 3, XmNsashIndent, -2,
3421 XmNmappedWhenManaged, False,
3422 NULL);
3423 XtVaSetValues(window->mainWin, XmNworkWindow, pane, NULL);
3424 XtManageChild(pane);
3425 window->splitPane = pane;
3427 /* Store a copy of document/window pointer in text pane to support
3428 action procedures. See also WidgetToWindow() for info. */
3429 XtVaSetValues(pane, XmNuserData, window, NULL);
3431 /* Patch around Motif's most idiotic "feature", that its menu accelerators
3432 recognize Caps Lock and Num Lock as modifiers, and don't trigger if
3433 they are engaged */
3434 AccelLockBugPatch(pane, window->menuBar);
3436 /* Create the first, and most permanent text area (other panes may
3437 be added & removed, but this one will never be removed */
3438 text = createTextArea(pane, window, nRows, nCols,
3439 GetPrefEmTabDist(PLAIN_LANGUAGE_MODE), GetPrefDelimiters(),
3440 GetPrefWrapMargin(), window->showLineNumbers?MIN_LINE_NUM_COLS:0);
3441 XtManageChild(text);
3442 window->textArea = text;
3443 window->lastFocus = text;
3445 /* Set the initial colors from the globals. */
3446 SetColors(window,
3447 GetPrefColorName(TEXT_FG_COLOR ),
3448 GetPrefColorName(TEXT_BG_COLOR ),
3449 GetPrefColorName(SELECT_FG_COLOR),
3450 GetPrefColorName(SELECT_BG_COLOR),
3451 GetPrefColorName(HILITE_FG_COLOR),
3452 GetPrefColorName(HILITE_BG_COLOR),
3453 GetPrefColorName(LINENO_FG_COLOR),
3454 GetPrefColorName(CURSOR_FG_COLOR));
3456 /* Create the right button popup menu (note: order is important here,
3457 since the translation for popping up this menu was probably already
3458 added in createTextArea, but CreateBGMenu requires window->textArea
3459 to be set so it can attach the menu to it (because menu shells are
3460 finicky about the kinds of widgets they are attached to)) */
3461 window->bgMenuPane = CreateBGMenu(window);
3463 /* cache user menus: init. user background menu cache */
3464 InitUserBGMenuCache(&window->userBGMenuCache);
3466 /* Create the text buffer rather than using the one created automatically
3467 with the text area widget. This is done so the syntax highlighting
3468 modify callback can be called to synchronize the style buffer BEFORE
3469 the text display's callback is called upon to display a modification */
3470 window->buffer = BufCreate();
3471 BufAddModifyCB(window->buffer, SyntaxHighlightModifyCB, window);
3473 /* Attach the buffer to the text widget, and add callbacks for modify */
3474 TextSetBuffer(text, window->buffer);
3475 BufAddModifyCB(window->buffer, modifiedCB, window);
3477 /* Designate the permanent text area as the owner for selections */
3478 HandleXSelections(text);
3480 /* Set the requested hardware tab distance and useTabs in the text buffer */
3481 BufSetTabDistance(window->buffer, GetPrefTabDist(PLAIN_LANGUAGE_MODE));
3482 window->buffer->useTabs = GetPrefInsertTabs();
3483 window->tab = addTab(window->tabBar, window, name);
3485 /* add the window to the global window list, update the Windows menus */
3486 InvalidateWindowMenus();
3487 addToWindowList(window);
3489 #ifdef LESSTIF_VERSION
3490 /* FIXME: Temporary workaround for disappearing-text-window bug
3491 when linking to Lesstif.
3493 After changes is made to statsAreaForm (parent of statsline,
3494 i-search line and tab bar) widget such as enabling/disabling
3495 the statsline, the XmForm widget enclosing the text widget
3496 somehow refused to resize to fit the text widget. Resizing
3497 the shell window or making changes [again] to the statsAreaForm
3498 appeared to bring out the text widget, though doesn't fix it for
3499 the subsequently added documents. Here we try to do the latter
3500 for all new documents created. */
3501 if (XtIsManaged(XtParent(window->statsLineForm))) {
3502 XtUnmanageChild(XtParent(window->statsLineForm));
3503 XtManageChild(XtParent(window->statsLineForm));
3505 #endif /* LESSTIF_VERSION */
3507 /* return the shell ownership to previous tabbed doc */
3508 XtVaSetValues(window->mainWin, XmNworkWindow, shellWindow->splitPane, NULL);
3509 XLowerWindow(TheDisplay, XtWindow(window->splitPane));
3510 XtUnmanageChild(window->splitPane);
3511 XtVaSetValues(window->splitPane, XmNmappedWhenManaged, True, NULL);
3513 return window;
3517 ** return the next/previous docment on the tab list.
3519 ** If <wrap> is true then the next tab of the rightmost tab will be the
3520 ** second tab from the right, and the the previous tab of the leftmost
3521 ** tab will be the second from the left. This is useful for getting
3522 ** the next tab after a tab detaches/closes and you don't want to wrap around.
3524 static WindowInfo *getNextTabWindow(WindowInfo *window, int direction,
3525 int crossWin, int wrap)
3527 WidgetList tabList, tabs;
3528 WindowInfo *win;
3529 int tabCount, tabTotalCount;
3530 int tabPos, nextPos;
3531 int i, n;
3532 int nBuf = crossWin? NWindows() : NDocuments(window);
3534 if (nBuf <= 1)
3535 return NULL;
3537 /* get the list of tabs */
3538 tabs = (WidgetList)XtMalloc(sizeof(Widget) * nBuf);
3539 tabTotalCount = 0;
3540 if (crossWin) {
3541 int n, nItems;
3542 WidgetList children;
3544 XtVaGetValues(TheAppShell, XmNchildren, &children,
3545 XmNnumChildren, &nItems, NULL);
3547 /* get list of tabs in all windows */
3548 for (n=0; n<nItems; n++) {
3549 if (strcmp(XtName(children[n]), "textShell") ||
3550 ((win = WidgetToWindow(children[n])) == NULL))
3551 continue; /* skip non-text-editor windows */
3553 XtVaGetValues(win->tabBar, XmNtabWidgetList, &tabList,
3554 XmNtabCount, &tabCount, NULL);
3556 for (i=0; i< tabCount; i++) {
3557 tabs[tabTotalCount++] = tabList[i];
3561 else {
3562 /* get list of tabs in this window */
3563 XtVaGetValues(window->tabBar, XmNtabWidgetList, &tabList,
3564 XmNtabCount, &tabCount, NULL);
3566 for (i=0; i< tabCount; i++) {
3567 tabs[tabTotalCount++] = tabList[i];
3571 /* find the position of the tab in the tablist */
3572 tabPos = 0;
3573 for (n=0; n<tabTotalCount; n++) {
3574 if (tabs[n] == window->tab) {
3575 tabPos = n;
3576 break;
3580 /* calculate index position of next tab */
3581 nextPos = tabPos + direction;
3582 if (nextPos >= nBuf) {
3583 if (wrap)
3584 nextPos = 0;
3585 else
3586 nextPos = nBuf - 2;
3587 } else if (nextPos < 0) {
3588 if (wrap)
3589 nextPos = nBuf - 1;
3590 else
3591 nextPos = 1;
3594 /* return the document where the next tab belongs to */
3595 win = TabToWindow(tabs[nextPos]);
3596 XtFree((char *)tabs);
3597 return win;
3601 ** return the integer position of a tab in the tabbar it
3602 ** belongs to, or -1 if there's an error, somehow.
3604 static int getTabPosition(Widget tab)
3606 WidgetList tabList;
3607 int i, tabCount;
3608 Widget tabBar = XtParent(tab);
3610 XtVaGetValues(tabBar, XmNtabWidgetList, &tabList,
3611 XmNtabCount, &tabCount, NULL);
3613 for (i=0; i< tabCount; i++) {
3614 if (tab == tabList[i])
3615 return i;
3618 return -1; /* something is wrong! */
3622 ** update the tab label, etc. of a tab, per the states of it's document.
3624 void RefreshTabState(WindowInfo *win)
3626 XmString s1, tipString;
3627 char labelString[MAXPATHLEN];
3628 char *tag = XmFONTLIST_DEFAULT_TAG;
3629 unsigned char alignment;
3631 /* Set tab label to document's filename. Position of
3632 "*" (modified) will change per label alignment setting */
3633 XtVaGetValues(win->tab, XmNalignment, &alignment, NULL);
3634 if (alignment != XmALIGNMENT_END) {
3635 sprintf(labelString, "%s%s",
3636 win->fileChanged? "*" : "",
3637 win->filename);
3638 } else {
3639 sprintf(labelString, "%s%s",
3640 win->filename,
3641 win->fileChanged? "*" : "");
3644 /* Make the top document stand out a little more */
3645 if (IsTopDocument(win))
3646 tag = "BOLD";
3648 s1 = XmStringCreateLtoR(labelString, tag);
3650 if (GetPrefShowPathInWindowsMenu() && win->filenameSet) {
3651 strcat(labelString, " - ");
3652 strcat(labelString, win->path);
3654 tipString=XmStringCreateSimple(labelString);
3656 XtVaSetValues(win->tab,
3657 XltNbubbleString, tipString,
3658 XmNlabelString, s1,
3659 NULL);
3660 XmStringFree(s1);
3661 XmStringFree(tipString);
3665 ** close all the documents in a window
3667 int CloseAllDocumentInWindow(WindowInfo *window)
3669 WindowInfo *win;
3671 if (NDocuments(window) == 1) {
3672 /* only one document in the window */
3673 return CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE);
3675 else {
3676 Widget winShell = window->shell;
3677 WindowInfo *topDocument;
3679 /* close all _modified_ documents belong to this window */
3680 for (win = WindowList; win; ) {
3681 if (win->shell == winShell && win->fileChanged) {
3682 WindowInfo *next = win->next;
3683 if (!CloseFileAndWindow(win, PROMPT_SBC_DIALOG_RESPONSE))
3684 return False;
3685 win = next;
3687 else
3688 win = win->next;
3691 /* see there's still documents left in the window */
3692 for (win = WindowList; win; win=win->next)
3693 if (win->shell == winShell)
3694 break;
3696 if (win) {
3697 topDocument = GetTopDocument(winShell);
3699 /* close all non-top documents belong to this window */
3700 for (win = WindowList; win; ) {
3701 if (win->shell == winShell && win != topDocument) {
3702 WindowInfo *next = win->next;
3703 if (!CloseFileAndWindow(win, PROMPT_SBC_DIALOG_RESPONSE))
3704 return False;
3705 win = next;
3707 else
3708 win = win->next;
3711 /* close the last document and its window */
3712 if (!CloseFileAndWindow(topDocument, PROMPT_SBC_DIALOG_RESPONSE))
3713 return False;
3717 return True;
3720 static void CloseDocumentWindow(Widget w, WindowInfo *window, XtPointer callData)
3722 int nDocuments = NDocuments(window);
3724 if (nDocuments == NWindows()) {
3725 /* this is only window, then exit */
3726 XtCallActionProc(WindowList->lastFocus, "exit",
3727 ((XmAnyCallbackStruct *)callData)->event, NULL, 0);
3729 else {
3730 if (nDocuments == 1) {
3731 CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE);
3733 else {
3734 int resp = DialogF(DF_QUES, window->shell, 2, "Close Window",
3735 "Close ALL documents in this window?", "Close", "Cancel");
3737 if (resp == 1)
3738 CloseAllDocumentInWindow(window);
3743 static void cloneTextPane(WindowInfo *window, WindowInfo *orgWin)
3745 short paneHeights[MAX_PANES+1];
3746 int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1];
3747 int horizOffsets[MAX_PANES+1];
3748 int i, focusPane, emTabDist, wrapMargin, lineNumCols, totalHeight=0;
3749 char *delimiters;
3750 Widget text;
3751 selection sel;
3753 /* transfer the primary selection */
3754 memcpy(&sel, &orgWin->buffer->primary, sizeof(selection));
3756 if (sel.selected) {
3757 if (sel.rectangular)
3758 BufRectSelect(window->buffer, sel.start, sel.end,
3759 sel.rectStart, sel.rectEnd);
3760 else
3761 BufSelect(window->buffer, sel.start, sel.end);
3762 } else
3763 BufUnselect(window->buffer);
3765 /* Record the current heights, scroll positions, and insert positions
3766 of the existing panes, keyboard focus */
3767 focusPane = 0;
3768 for (i=0; i<=orgWin->nPanes; i++) {
3769 text = i==0 ? orgWin->textArea : orgWin->textPanes[i-1];
3770 insertPositions[i] = TextGetCursorPos(text);
3771 XtVaGetValues(containingPane(text), XmNheight, &paneHeights[i], NULL);
3772 totalHeight += paneHeights[i];
3773 TextGetScroll(text, &topLines[i], &horizOffsets[i]);
3774 if (text == orgWin->lastFocus)
3775 focusPane = i;
3778 window->nPanes = orgWin->nPanes;
3780 /* clone split panes, if any */
3781 if (window->nPanes) {
3782 /* Unmanage & remanage the panedWindow so it recalculates pane heights */
3783 XtUnmanageChild(window->splitPane);
3785 /* Create a text widget to add to the pane and set its buffer and
3786 highlight data to be the same as the other panes in the orgWin */
3787 XtVaGetValues(orgWin->textArea, textNemulateTabs, &emTabDist,
3788 textNwordDelimiters, &delimiters, textNwrapMargin, &wrapMargin,
3789 textNlineNumCols, &lineNumCols, NULL);
3791 for(i=0; i<orgWin->nPanes; i++) {
3792 text = createTextArea(window->splitPane, window, 1, 1, emTabDist,
3793 delimiters, wrapMargin, lineNumCols);
3794 TextSetBuffer(text, window->buffer);
3796 if (window->highlightData != NULL)
3797 AttachHighlightToWidget(text, window);
3798 XtManageChild(text);
3799 window->textPanes[i] = text;
3802 /* Set the minimum pane height in the new pane */
3803 UpdateMinPaneHeights(window);
3805 for (i=0; i<=window->nPanes; i++) {
3806 text = i==0 ? window->textArea : window->textPanes[i-1];
3807 setPaneDesiredHeight(containingPane(text), paneHeights[i]);
3810 /* Re-manage panedWindow to recalculate pane heights & reset selection */
3811 XtManageChild(window->splitPane);
3814 /* Reset all of the heights, scroll positions, etc. */
3815 for (i=0; i<=window->nPanes; i++) {
3816 textDisp *textD;
3818 text = i==0 ? window->textArea : window->textPanes[i-1];
3819 TextSetCursorPos(text, insertPositions[i]);
3820 TextSetScroll(text, topLines[i], horizOffsets[i]);
3822 /* dim the cursor */
3823 textD = ((TextWidget)text)->text.textD;
3824 TextDSetCursorStyle(textD, DIM_CURSOR);
3825 TextDUnblankCursor(textD);
3828 /* set the focus pane */
3829 for (i=0; i<=window->nPanes; i++) {
3830 text = i==0 ? window->textArea : window->textPanes[i-1];
3831 if(i == focusPane) {
3832 window->lastFocus = text;
3833 XmProcessTraversal(text, XmTRAVERSE_CURRENT);
3834 break;
3838 /* Update the window manager size hints after the sizes of the panes have
3839 been set (the widget heights are not yet readable here, but they will
3840 be by the time the event loop gets around to running this timer proc) */
3841 XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0,
3842 wmSizeUpdateProc, window);
3846 ** Refresh the menu entries per the settings of the
3847 ** top document.
3849 void RefreshMenuToggleStates(WindowInfo *window)
3851 WindowInfo *win;
3853 if (!IsTopDocument(window))
3854 return;
3856 /* File menu */
3857 XtSetSensitive(window->printSelItem, window->wasSelected);
3859 /* Edit menu */
3860 XtSetSensitive(window->undoItem, window->undo != NULL);
3861 XtSetSensitive(window->redoItem, window->redo != NULL);
3862 XtSetSensitive(window->printSelItem, window->wasSelected);
3863 XtSetSensitive(window->cutItem, window->wasSelected);
3864 XtSetSensitive(window->copyItem, window->wasSelected);
3865 XtSetSensitive(window->delItem, window->wasSelected);
3867 /* Preferences menu */
3868 XmToggleButtonSetState(window->statsLineItem, window->showStats, False);
3869 XmToggleButtonSetState(window->iSearchLineItem, window->showISearchLine, False);
3870 XmToggleButtonSetState(window->lineNumsItem, window->showLineNumbers, False);
3871 XmToggleButtonSetState(window->highlightItem, window->highlightSyntax, False);
3872 XtSetSensitive(window->highlightItem, window->languageMode != PLAIN_LANGUAGE_MODE);
3873 XmToggleButtonSetState(window->backlightCharsItem, window->backlightChars, False);
3874 XmToggleButtonSetState(window->saveLastItem, window->saveOldVersion, False);
3875 XmToggleButtonSetState(window->autoSaveItem, window->autoSave, False);
3876 XmToggleButtonSetState(window->overtypeModeItem, window->overstrike, False);
3877 XmToggleButtonSetState(window->matchSyntaxBasedItem, window->matchSyntaxBased, False);
3878 XmToggleButtonSetState(window->readOnlyItem, IS_USER_LOCKED(window->lockReasons), False);
3880 XtSetSensitive(window->smartIndentItem,
3881 SmartIndentMacrosAvailable(LanguageModeName(window->languageMode)));
3883 SetAutoIndent(window, window->indentStyle);
3884 SetAutoWrap(window, window->wrapMode);
3885 SetShowMatching(window, window->showMatchingStyle);
3886 SetLanguageMode(window, window->languageMode, FALSE);
3888 /* Windows Menu */
3889 XtSetSensitive(window->splitPaneItem, window->nPanes < MAX_PANES);
3890 XtSetSensitive(window->closePaneItem, window->nPanes > 0);
3891 XtSetSensitive(window->detachDocumentItem, NDocuments(window)>1);
3892 XtSetSensitive(window->contextDetachDocumentItem, NDocuments(window)>1);
3894 for (win=WindowList; win; win=win->next)
3895 if (win->shell != window->shell)
3896 break;
3897 XtSetSensitive(window->moveDocumentItem, win != NULL);
3901 ** Refresh the various settings/state of the shell window per the
3902 ** settings of the top document.
3904 static void refreshMenuBar(WindowInfo *window)
3906 RefreshMenuToggleStates(window);
3908 /* Add/remove language specific menu items */
3909 UpdateUserMenus(window);
3911 /* refresh selection-sensitive menus */
3912 DimSelectionDepUserMenuItems(window, window->wasSelected);
3916 ** remember the last document.
3918 WindowInfo *MarkLastDocument(WindowInfo *window)
3920 WindowInfo *prev = lastFocusDocument;
3922 if (window)
3923 lastFocusDocument = window;
3925 return prev;
3929 ** remember the active (top) document.
3931 WindowInfo *MarkActiveDocument(WindowInfo *window)
3933 WindowInfo *prev = inFocusDocument;
3935 if (window)
3936 inFocusDocument = window;
3938 return prev;
3942 ** Bring up the next window by tab order
3944 void NextDocument(WindowInfo *window)
3946 WindowInfo *win;
3948 if (WindowList->next == NULL)
3949 return;
3951 win = getNextTabWindow(window, 1, GetPrefGlobalTabNavigate(), 1);
3952 if (win == NULL)
3953 return;
3955 if (window->shell == win->shell)
3956 RaiseDocument(win);
3957 else
3958 RaiseDocumentWindow(win);
3962 ** Bring up the previous window by tab order
3964 void PreviousDocument(WindowInfo *window)
3966 WindowInfo *win;
3968 if (WindowList->next == NULL)
3969 return;
3971 win = getNextTabWindow(window, -1, GetPrefGlobalTabNavigate(), 1);
3972 if (win == NULL)
3973 return;
3975 if (window->shell == win->shell)
3976 RaiseDocument(win);
3977 else
3978 RaiseDocumentWindow(win);
3982 ** Bring up the last active window
3984 void LastDocument(WindowInfo *window)
3986 WindowInfo *win;
3988 for(win = WindowList; win; win=win->next)
3989 if (lastFocusDocument == win)
3990 break;
3992 if (!win)
3993 return;
3995 if (window->shell == win->shell)
3996 RaiseDocument(win);
3997 else
3998 RaiseDocumentWindow(win);
4003 ** make sure window is alive is kicking
4005 int IsValidWindow(WindowInfo *window)
4007 WindowInfo *win;
4009 for(win = WindowList; win; win=win->next)
4010 if (window == win)
4011 return True;
4014 return False;
4018 ** raise the document and its shell window
4020 void RaiseDocumentWindow(WindowInfo *window)
4022 if (!window)
4023 return;
4025 RaiseDocument(window);
4026 RaiseShellWindow(window->shell);
4030 ** Redisplay menu tearoffs previously hid by hideTearOffs()
4032 void redisplayTearOffs(Widget menuPane)
4034 WidgetList itemList;
4035 Widget subMenuID;
4036 Cardinal nItems;
4037 int n;
4039 /* redisplay all submenu tearoffs */
4040 XtVaGetValues(menuPane, XmNchildren, &itemList,
4041 XmNnumChildren, &nItems, NULL);
4042 for (n=0; n<(int)nItems; n++) {
4043 if (XtClass(itemList[n]) == xmCascadeButtonWidgetClass) {
4044 XtVaGetValues(itemList[n], XmNsubMenuId, &subMenuID, NULL);
4045 redisplayTearOffs(subMenuID);
4049 /* redisplay tearoff for this menu */
4050 if (!XmIsMenuShell(XtParent(menuPane)))
4051 ShowHiddenTearOff(menuPane);
4055 ** hide all the tearoffs spawned from this menu.
4056 ** It works recursively to close the tearoffs of the submenus
4058 static void hideTearOffs(Widget menuPane)
4060 WidgetList itemList;
4061 Widget subMenuID;
4062 Cardinal nItems;
4063 int n;
4065 /* hide all submenu tearoffs */
4066 XtVaGetValues(menuPane, XmNchildren, &itemList,
4067 XmNnumChildren, &nItems, NULL);
4068 for (n=0; n<(int)nItems; n++) {
4069 if (XtClass(itemList[n]) == xmCascadeButtonWidgetClass) {
4070 XtVaGetValues(itemList[n], XmNsubMenuId, &subMenuID, NULL);
4071 hideTearOffs(subMenuID);
4075 /* hide tearoff for this menu */
4076 if (!XmIsMenuShell(XtParent(menuPane)))
4077 XtUnmapWidget(XtParent(menuPane));
4081 ** Raise a tabbed document within its shell window.
4083 ** NB: use RaiseDocumentWindow() to raise the doc and
4084 ** its shell window.
4086 void RaiseDocument(WindowInfo *window)
4088 WindowInfo *win, *lastwin;
4090 if (!window || !WindowList)
4091 return;
4093 lastwin = MarkActiveDocument(window);
4094 if (lastwin != window && IsValidWindow(lastwin))
4095 MarkLastDocument(lastwin);
4097 /* document already on top? */
4098 XtVaGetValues(window->mainWin, XmNuserData, &win, NULL);
4099 if (win == window)
4100 return;
4102 /* set the document as top document */
4103 XtVaSetValues(window->mainWin, XmNuserData, window, NULL);
4105 /* show the new top document */
4106 XtVaSetValues(window->mainWin, XmNworkWindow, window->splitPane, NULL);
4107 XtManageChild(window->splitPane);
4108 XRaiseWindow(TheDisplay, XtWindow(window->splitPane));
4110 /* Turn on syntax highlight that might have been deferred.
4111 NB: this must be done after setting the document as
4112 XmNworkWindow and managed, else the parent shell
4113 window may shrink on some window-managers such as
4114 metacity, due to changes made in UpdateWMSizeHints().*/
4115 if (window->highlightSyntax && window->highlightData==NULL)
4116 StartHighlighting(window, False);
4118 /* put away the bg menu tearoffs of last active document */
4119 hideTearOffs(win->bgMenuPane);
4121 /* restore the bg menu tearoffs of active document */
4122 redisplayTearOffs(window->bgMenuPane);
4124 /* set tab as active */
4125 XmLFolderSetActiveTab(window->tabBar,
4126 getTabPosition(window->tab), False);
4128 /* set keyboard focus. Must be done before unmanaging previous
4129 top document, else lastFocus will be reset to textArea */
4130 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
4132 /* we only manage the top document, else the next time a document
4133 is raised again, it's textpane might not resize properly.
4134 Also, somehow (bug?) XtUnmanageChild() doesn't hide the
4135 splitPane, which obscure lower part of the statsform when
4136 we toggle its components, so we need to put the document at
4137 the back */
4138 XLowerWindow(TheDisplay, XtWindow(win->splitPane));
4139 XtUnmanageChild(win->splitPane);
4140 RefreshTabState(win);
4142 /* now refresh window state/info. RefreshWindowStates()
4143 has a lot of work to do, so we update the screen first so
4144 the document appears to switch swiftly. */
4145 XmUpdateDisplay(window->splitPane);
4146 RefreshWindowStates(window);
4147 RefreshTabState(window);
4149 /* put away the bg menu tearoffs of last active document */
4150 hideTearOffs(win->bgMenuPane);
4152 /* restore the bg menu tearoffs of active document */
4153 redisplayTearOffs(window->bgMenuPane);
4155 UpdateWMSizeHints(window);
4158 WindowInfo* GetTopDocument(Widget w)
4160 WindowInfo *window = WidgetToWindow(w);
4162 return WidgetToWindow(window->shell);
4165 Boolean IsTopDocument(const WindowInfo *window)
4167 return window == GetTopDocument(window->shell)? True : False;
4170 void DeleteDocument(WindowInfo *window)
4172 if (!window)
4173 return;
4175 XtDestroyWidget(window->splitPane);
4179 ** clone a document's states and settings into the other.
4181 static void cloneDocument(WindowInfo *window, WindowInfo *orgWin)
4183 char *orgDocument;
4184 char *params[4];
4185 int emTabDist;
4187 strcpy(window->path, orgWin->path);
4188 strcpy(window->filename, orgWin->filename);
4190 ShowLineNumbers(window, orgWin->showLineNumbers);
4192 window->ignoreModify = True;
4194 /* copy the text buffer */
4195 orgDocument = BufGetAll(orgWin->buffer);
4196 BufSetAll(window->buffer, orgDocument);
4197 XtFree(orgDocument);
4199 /* copy the tab preferences (here!) */
4200 BufSetTabDistance(window->buffer, orgWin->buffer->tabDist);
4201 window->buffer->useTabs = orgWin->buffer->useTabs;
4202 XtVaGetValues(orgWin->textArea, textNemulateTabs, &emTabDist, NULL);
4203 SetEmTabDist(window, emTabDist);
4205 window->ignoreModify = False;
4207 /* transfer text fonts */
4208 params[0] = orgWin->fontName;
4209 params[1] = orgWin->italicFontName;
4210 params[2] = orgWin->boldFontName;
4211 params[3] = orgWin->boldItalicFontName;
4212 XtCallActionProc(window->textArea, "set_fonts", NULL, params, 4);
4214 SetBacklightChars(window, orgWin->backlightCharTypes);
4216 /* syntax hilite */
4217 window->languageMode = orgWin->languageMode;
4218 window->highlightSyntax = orgWin->highlightSyntax;
4219 if (window->highlightSyntax)
4220 StartHighlighting(window, False);
4222 /* copy states of original document */
4223 window->filenameSet = orgWin->filenameSet;
4224 window->fileFormat = orgWin->fileFormat;
4225 window->lastModTime = orgWin->lastModTime;
4226 window->fileChanged = orgWin->fileChanged;
4227 window->fileMissing = orgWin->fileMissing;
4228 window->lockReasons = orgWin->lockReasons;
4229 window->nPanes = orgWin->nPanes;
4230 window->autoSaveCharCount = orgWin->autoSaveCharCount;
4231 window->autoSaveOpCount = orgWin->autoSaveOpCount;
4232 window->undoOpCount = orgWin->undoOpCount;
4233 window->undoMemUsed = orgWin->undoMemUsed;
4234 window->lockReasons = orgWin->lockReasons;
4235 window->autoSave = orgWin->autoSave;
4236 window->saveOldVersion = orgWin->saveOldVersion;
4237 window->wrapMode = orgWin->wrapMode;
4238 window->overstrike = orgWin->overstrike;
4239 window->showMatchingStyle = orgWin->showMatchingStyle;
4240 window->matchSyntaxBased = orgWin->matchSyntaxBased;
4241 #if 0
4242 window->showStats = orgWin->showStats;
4243 window->showISearchLine = orgWin->showISearchLine;
4244 window->showLineNumbers = orgWin->showLineNumbers;
4245 window->modeMessageDisplayed = orgWin->modeMessageDisplayed;
4246 window->ignoreModify = orgWin->ignoreModify;
4247 window->windowMenuValid = orgWin->windowMenuValid;
4248 window->prevOpenMenuValid = orgWin->prevOpenMenuValid;
4249 window->flashTimeoutID = orgWin->flashTimeoutID;
4250 window->wasSelected = orgWin->wasSelected;
4251 strcpy(window->fontName, orgWin->fontName);
4252 strcpy(window->italicFontName, orgWin->italicFontName);
4253 strcpy(window->boldFontName, orgWin->boldFontName);
4254 strcpy(window->boldItalicFontName, orgWin->boldItalicFontName);
4255 window->fontList = orgWin->fontList;
4256 window->italicFontStruct = orgWin->italicFontStruct;
4257 window->boldFontStruct = orgWin->boldFontStruct;
4258 window->boldItalicFontStruct = orgWin->boldItalicFontStruct;
4259 window->markTimeoutID = orgWin->markTimeoutID;
4260 window->highlightData = orgWin->highlightData;
4261 window->shellCmdData = orgWin->shellCmdData;
4262 window->macroCmdData = orgWin->macroCmdData;
4263 window->smartIndentData = orgWin->smartIndentData;
4264 #endif
4265 window->iSearchHistIndex = orgWin->iSearchHistIndex;
4266 window->iSearchStartPos = orgWin->iSearchStartPos;
4267 window->replaceLastRegexCase = orgWin->replaceLastRegexCase;
4268 window->replaceLastLiteralCase = orgWin->replaceLastLiteralCase;
4269 window->iSearchLastRegexCase = orgWin->iSearchLastRegexCase;
4270 window->iSearchLastLiteralCase = orgWin->iSearchLastLiteralCase;
4271 window->findLastRegexCase = orgWin->findLastRegexCase;
4272 window->findLastLiteralCase = orgWin->findLastLiteralCase;
4274 /* copy the text/split panes settings, cursor pos & selection */
4275 cloneTextPane(window, orgWin);
4277 /* copy undo & redo list */
4278 window->undo = cloneUndoItems(orgWin->undo);
4279 window->redo = cloneUndoItems(orgWin->redo);
4281 /* copy bookmarks */
4282 window->nMarks = orgWin->nMarks;
4283 memcpy(&window->markTable, &orgWin->markTable,
4284 sizeof(Bookmark)*window->nMarks);
4286 /* clone rangeset info */
4287 window->buffer->rangesetTable =
4288 RangesetTableClone(orgWin->buffer->rangesetTable, window->buffer);
4290 /* kick start the auto-indent engine */
4291 window->indentStyle = NO_AUTO_INDENT;
4292 SetAutoIndent(window, orgWin->indentStyle);
4294 /* synchronize window state to this document */
4295 RefreshWindowStates(window);
4298 static UndoInfo *cloneUndoItems(UndoInfo *orgList)
4300 UndoInfo *head = NULL, *undo, *clone, *last = NULL;
4302 for (undo = orgList; undo; undo = undo->next) {
4303 clone = (UndoInfo *)XtMalloc(sizeof(UndoInfo));
4304 memcpy(clone, undo, sizeof(UndoInfo));
4306 if (undo->oldText) {
4307 clone->oldText = XtMalloc(strlen(undo->oldText)+1);
4308 strcpy(clone->oldText, undo->oldText);
4310 clone->next = NULL;
4312 if (last)
4313 last->next = clone;
4314 else
4315 head = clone;
4317 last = clone;
4320 return head;
4324 ** return the number of documents owned by this shell window
4326 int NDocuments(WindowInfo *window)
4328 WindowInfo *win;
4329 int nDocument = 0;
4331 for (win = WindowList; win; win = win->next) {
4332 if (win->shell == window->shell)
4333 nDocument++;
4336 return nDocument;
4340 ** refresh window state for this document
4342 void RefreshWindowStates(WindowInfo *window)
4344 if (!IsTopDocument(window))
4345 return;
4347 if (window->modeMessageDisplayed)
4348 XmTextSetString(window->statsLine, window->modeMessage);
4349 else
4350 UpdateStatsLine(window);
4351 UpdateWindowReadOnly(window);
4352 UpdateWindowTitle(window);
4354 /* show/hide statsline as needed */
4355 if (window->modeMessageDisplayed && !XtIsManaged(window->statsLineForm)) {
4356 /* turn on statline to display mode message */
4357 showStats(window, True);
4359 else if (window->showStats && !XtIsManaged(window->statsLineForm)) {
4360 /* turn on statsline since it is enabled */
4361 showStats(window, True);
4363 else if (!window->showStats && !window->modeMessageDisplayed &&
4364 XtIsManaged(window->statsLineForm)) {
4365 /* turn off statsline since there's nothing to show */
4366 showStats(window, False);
4369 /* signal if macro/shell is running */
4370 if (window->shellCmdData || window->macroCmdData)
4371 BeginWait(window->shell);
4372 else
4373 EndWait(window->shell);
4375 /* we need to force the statsline to reveal itself */
4376 if (XtIsManaged(window->statsLineForm)) {
4377 XmTextSetCursorPosition(window->statsLine, 0); /* start of line */
4378 XmTextSetCursorPosition(window->statsLine, 9000); /* end of line */
4381 XmUpdateDisplay(window->statsLine);
4382 refreshMenuBar(window);
4386 ** spin off the document to a new window
4388 WindowInfo *DetachDocument(WindowInfo *window)
4390 WindowInfo *win, *cloneWin;
4391 char geometry[MAX_GEOM_STRING_LEN];
4392 int rows, cols;
4394 if (NDocuments(window) < 2)
4395 return NULL;
4397 /* raise another document in the same shell window */
4398 win = getNextTabWindow(window, 1, 0, 0);
4399 RaiseDocument(win);
4401 /* create new window in roughly the size of original window,
4402 to reduce flicker when the window is resized later */
4403 getTextPaneDimension(window, &rows, &cols);
4404 sprintf(geometry, "%dx%d", cols, rows);
4405 cloneWin = CreateWindow(window->filename, geometry, False);
4407 /* CreateWindow() simply adds the new window's pointer to the
4408 head of WindowList. We need to adjust the detached window's
4409 pointer, so that macro functions such as focus_window("last")
4410 will travel across the documents per the sequence they're
4411 opened. The new doc will appear to replace it's former self
4412 as the old doc is closed. */
4413 WindowList = cloneWin->next;
4414 cloneWin->next = window->next;
4415 window->next = cloneWin;
4417 /* these settings should follow the detached document.
4418 must be done before cloning window, else the height
4419 of split panes may not come out correctly */
4420 ShowISearchLine(cloneWin, window->showISearchLine);
4421 ShowStatsLine(cloneWin, window->showStats);
4423 /* clone the document & its pref settings */
4424 cloneDocument(cloneWin, window);
4426 /* remove the document from the old window */
4427 window->fileChanged = False;
4428 CloseFileAndWindow(window, NO_SBC_DIALOG_RESPONSE);
4430 /* refresh former host window */
4431 RefreshWindowStates(win);
4433 /* this should keep the new document window fresh */
4434 RefreshWindowStates(cloneWin);
4435 RefreshTabState(cloneWin);
4436 SortTabBar(cloneWin);
4438 return cloneWin;
4442 ** Move document to an other window.
4444 ** the moving document will receive certain window settings from
4445 ** its new host, i.e. the window size, stats and isearch lines.
4447 WindowInfo *MoveDocument(WindowInfo *toWindow, WindowInfo *window)
4449 WindowInfo *win, *cloneWin;
4451 /* raise another document to replace the document being moved */
4452 for (win = WindowList; win; win = win->next) {
4453 if (win->shell == window->shell && window != win)
4454 break;
4457 if (win)
4458 RaiseDocument(win);
4459 else
4460 XtUnmapWidget(window->shell);
4462 /* relocate the document to target window */
4463 cloneWin = CreateDocument(toWindow, window->filename, NULL, False);
4464 ShowTabBar(cloneWin, GetShowTabBar(cloneWin));
4465 cloneDocument(cloneWin, window);
4467 /* CreateDocument() simply adds the new window's pointer to the
4468 head of WindowList. We need to adjust the detached window's
4469 pointer, so that macro functions such as focus_window("last")
4470 will travel across the documents per the sequence they're
4471 opened. The new doc will appear to replace it's former self
4472 as the old doc is closed. */
4473 WindowList = cloneWin->next;
4474 cloneWin->next = window->next;
4475 window->next = cloneWin;
4477 /* remove the document from the old window */
4478 window->fileChanged = False;
4479 CloseFileAndWindow(window, NO_SBC_DIALOG_RESPONSE);
4481 /* some menu states might have changed when deleting document */
4482 if (win) {
4483 RefreshWindowStates(win);
4486 /* this should keep the new document window fresh */
4487 RaiseDocumentWindow(cloneWin);
4488 RefreshTabState(cloneWin);
4489 SortTabBar(cloneWin);
4491 return cloneWin;
4494 static void moveDocumentCB(Widget dialog, WindowInfo *window,
4495 XtPointer call_data)
4497 XmSelectionBoxCallbackStruct *cbs = (XmSelectionBoxCallbackStruct *) call_data;
4498 DoneWithMoveDocumentDialog = cbs->reason;
4502 ** present dialog for selecting a target window to move this document
4503 ** into. Do nothing if there is only one shell window opened.
4505 void MoveDocumentDialog(WindowInfo *window)
4507 WindowInfo *win, *targetWin, **shellWinList;
4508 int i, nList=0, nWindows=0, ac;
4509 char tmpStr[MAXPATHLEN+50];
4510 Widget parent, dialog, listBox, moveAllOption;
4511 XmString *list = NULL;
4512 XmString popupTitle, s1;
4513 Arg csdargs[20];
4514 int *position_list, position_count;
4516 /* get the list of available shell windows, not counting
4517 the document to be moved */
4518 nWindows = NWindows();
4519 list = (XmStringTable) XtMalloc(nWindows * sizeof(XmString *));
4520 shellWinList = (WindowInfo **) XtMalloc(nWindows * sizeof(WindowInfo *));
4522 for (win=WindowList; win; win=win->next) {
4523 if (!IsTopDocument(win) || win->shell == window->shell)
4524 continue;
4526 sprintf(tmpStr, "%s%s",
4527 win->filenameSet? win->path : "", win->filename);
4529 list[nList] = XmStringCreateSimple(tmpStr);
4530 shellWinList[nList] = win;
4531 nList++;
4534 /* stop here if there's no other window to move to */
4535 if (!nList) {
4536 XtFree((char *)list);
4537 return;
4540 /* create the dialog */
4541 parent = window->shell;
4542 popupTitle = XmStringCreateSimple("Move Document");
4543 sprintf(tmpStr, "Move %s into window of", window->filename);
4544 s1 = XmStringCreateSimple(tmpStr);
4545 ac = 0;
4546 XtSetArg(csdargs[ac], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); ac++;
4547 XtSetArg(csdargs[ac], XmNdialogTitle, popupTitle); ac++;
4548 XtSetArg(csdargs[ac], XmNlistLabelString, s1); ac++;
4549 XtSetArg(csdargs[ac], XmNlistItems, list); ac++;
4550 XtSetArg(csdargs[ac], XmNlistItemCount, nList); ac++;
4551 XtSetArg(csdargs[ac], XmNvisibleItemCount, 12); ac++;
4552 XtSetArg(csdargs[ac], XmNautoUnmanage, False); ac++;
4553 dialog = CreateSelectionDialog(parent,"moveDocument",csdargs,ac);
4554 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT));
4555 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
4556 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_SELECTION_LABEL));
4557 XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)moveDocumentCB, window);
4558 XtAddCallback(dialog, XmNapplyCallback, (XtCallbackProc)moveDocumentCB, window);
4559 XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)moveDocumentCB, window);
4560 XmStringFree(s1);
4561 XmStringFree(popupTitle);
4563 /* free the window list */
4564 for (i=0; i<nList; i++)
4565 XmStringFree(list[i]);
4566 XtFree((char *)list);
4568 /* create the option box for moving all documents */
4569 s1 = MKSTRING("Move all documents in this window");
4570 moveAllOption = XtVaCreateWidget("moveAll",
4571 xmToggleButtonWidgetClass, dialog,
4572 XmNlabelString, s1,
4573 XmNalignment, XmALIGNMENT_BEGINNING,
4574 NULL);
4575 XmStringFree(s1);
4577 if (NDocuments(window) >1)
4578 XtManageChild(moveAllOption);
4580 /* disable option if only one document in the window */
4581 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_APPLY_BUTTON));
4583 s1 = MKSTRING("Move");
4584 XtVaSetValues (dialog, XmNokLabelString, s1, NULL);
4585 XmStringFree(s1);
4587 /* default to the first window on the list */
4588 listBox = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);
4589 XmListSelectPos(listBox, 1, True);
4591 /* show the dialog */
4592 DoneWithMoveDocumentDialog = 0;
4593 ManageDialogCenteredOnPointer(dialog);
4594 while (!DoneWithMoveDocumentDialog)
4595 XtAppProcessEvent(XtWidgetToApplicationContext(parent), XtIMAll);
4597 /* get the window to move document into */
4598 XmListGetSelectedPos(listBox, &position_list, &position_count);
4599 targetWin = shellWinList[position_list[0]-1];
4600 XtFree((char *)position_list);
4602 /* now move document(s) */
4603 if (DoneWithMoveDocumentDialog == XmCR_OK) {
4604 /* move top document */
4605 if (XmToggleButtonGetState(moveAllOption)) {
4606 /* move all documents */
4607 for (win = WindowList; win; ) {
4608 if (win != window && win->shell == window->shell) {
4609 WindowInfo *next = win->next;
4610 MoveDocument(targetWin, win);
4611 win = next;
4613 else
4614 win = win->next;
4617 /* invoking document is the last to move */
4618 MoveDocument(targetWin, window);
4620 else {
4621 MoveDocument(targetWin, window);
4625 XtFree((char *)shellWinList);
4626 XtDestroyWidget(dialog);
4629 static void hideTooltip(Widget tab)
4631 Widget tooltip = XtNameToWidget(tab, "*BubbleShell");
4633 if (tooltip)
4634 XtPopdown(tooltip);
4637 static void closeTabProc(XtPointer clientData, XtIntervalId *id)
4639 CloseFileAndWindow((WindowInfo*)clientData, PROMPT_SBC_DIALOG_RESPONSE);
4643 ** callback to close-tab button.
4645 static void closeTabCB(Widget w, Widget mainWin, caddr_t callData)
4647 /* FIXME: XtRemoveActionHook() related coredump
4649 An unknown bug seems to be associated with the XtRemoveActionHook()
4650 call in FinishLearn(), which resulted in coredump if a tab was
4651 closed, in the middle of keystrokes learning, by clicking on the
4652 close-tab button.
4654 As evident to our accusation, the coredump may be surpressed by
4655 simply commenting out the XtRemoveActionHook() call. The bug was
4656 consistent on both Motif and Lesstif on various platforms.
4658 Closing the tab through either the "Close" menu or its accel key,
4659 however, was without any trouble.
4661 While its actual mechanism is not well understood, we somehow
4662 managed to workaround the bug by delaying the action of closing
4663 the tab. For now. */
4664 XtAppAddTimeOut(XtWidgetToApplicationContext(w), 0,
4665 closeTabProc, GetTopDocument(mainWin));
4669 ** callback to clicks on a tab to raise it's document.
4671 static void raiseTabCB(Widget w, XtPointer clientData, XtPointer callData)
4673 XmLFolderCallbackStruct *cbs = (XmLFolderCallbackStruct *)callData;
4674 WidgetList tabList;
4675 Widget tab;
4677 XtVaGetValues(w, XmNtabWidgetList, &tabList, NULL);
4678 tab = tabList[cbs->pos];
4679 RaiseDocument(TabToWindow(tab));
4682 static Widget containingPane(Widget w)
4684 /* The containing pane used to simply be the first parent, but with
4685 the introduction of an XmFrame, it's the grandparent. */
4686 return XtParent(XtParent(w));
4690 ** set/clear toggle menu state if the calling document is on top.
4692 void SetToggleButtonState(WindowInfo *window, Widget w, Boolean state,
4693 Boolean notify)
4695 if (IsTopDocument(window)) {
4696 XmToggleButtonSetState(w, state, notify);
4701 ** set/clear menu sensitivity if the calling document is on top.
4703 void SetSensitive(WindowInfo *window, Widget w, Boolean sensitive)
4705 if (IsTopDocument(window)) {
4706 XtSetSensitive(w, sensitive);
4711 ** Remove redundant expose events on tab bar.
4713 void CleanUpTabBarExposeQueue(WindowInfo *window)
4715 XEvent event;
4716 XExposeEvent ev;
4717 int count;
4719 if (window == NULL)
4720 return;
4722 /* remove redundant expose events on tab bar */
4723 count=0;
4724 while (XCheckTypedWindowEvent(TheDisplay, XtWindow(window->tabBar),
4725 Expose, &event))
4726 count++;
4728 /* now we can update tabbar */
4729 if (count) {
4730 ev.type = Expose;
4731 ev.display = TheDisplay;
4732 ev.window = XtWindow(window->tabBar);
4733 ev.x = 0;
4734 ev.y = 0;
4735 ev.width = XtWidth(window->tabBar);
4736 ev.height = XtHeight(window->tabBar);
4737 ev.count = 0;
4738 XSendEvent(TheDisplay, XtWindow(window->tabBar), False,
4739 ExposureMask, (XEvent *)&ev);