Crash fix: bgMenuUndo/RedoItem were memcopied when opening multiple tabs in
[nedit.git] / source / window.c
blob5ecfe462e1a36c82217b1332a99d47b577fb9430
1 static const char CVSID[] = "$Id: window.c,v 1.158 2004/06/09 17:52:58 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;
3400 window->bgMenuUndoItem = NULL;
3401 window->bgMenuRedoItem = NULL;
3403 if (window->fontList == NULL)
3404 XtVaGetValues(shellWindow->statsLine, XmNfontList,
3405 &window->fontList,NULL);
3407 getTextPaneDimension(shellWindow, &nRows, &nCols);
3409 /* Create pane that actaully holds the new document. As
3410 document is created in 'background', we need to hide
3411 it. If we leave it unmanaged without setting it to
3412 the XmNworkWindow of the mainWin, due to a unknown
3413 bug in Motif where splitpane's scrollWindow child
3414 somehow came up with a height taller than the splitpane,
3415 the bottom part of the text editing widget is obstructed
3416 when later brought up by RaiseDocument(). So we first
3417 manage it hidden, then unmanage it and reset XmNworkWindow,
3418 then let RaiseDocument() show it later. */
3419 pane = XtVaCreateWidget("pane",
3420 xmPanedWindowWidgetClass, window->mainWin,
3421 XmNmarginWidth, 0, XmNmarginHeight, 0, XmNseparatorOn, False,
3422 XmNspacing, 3, XmNsashIndent, -2,
3423 XmNmappedWhenManaged, False,
3424 NULL);
3425 XtVaSetValues(window->mainWin, XmNworkWindow, pane, NULL);
3426 XtManageChild(pane);
3427 window->splitPane = pane;
3429 /* Store a copy of document/window pointer in text pane to support
3430 action procedures. See also WidgetToWindow() for info. */
3431 XtVaSetValues(pane, XmNuserData, window, NULL);
3433 /* Patch around Motif's most idiotic "feature", that its menu accelerators
3434 recognize Caps Lock and Num Lock as modifiers, and don't trigger if
3435 they are engaged */
3436 AccelLockBugPatch(pane, window->menuBar);
3438 /* Create the first, and most permanent text area (other panes may
3439 be added & removed, but this one will never be removed */
3440 text = createTextArea(pane, window, nRows, nCols,
3441 GetPrefEmTabDist(PLAIN_LANGUAGE_MODE), GetPrefDelimiters(),
3442 GetPrefWrapMargin(), window->showLineNumbers?MIN_LINE_NUM_COLS:0);
3443 XtManageChild(text);
3444 window->textArea = text;
3445 window->lastFocus = text;
3447 /* Set the initial colors from the globals. */
3448 SetColors(window,
3449 GetPrefColorName(TEXT_FG_COLOR ),
3450 GetPrefColorName(TEXT_BG_COLOR ),
3451 GetPrefColorName(SELECT_FG_COLOR),
3452 GetPrefColorName(SELECT_BG_COLOR),
3453 GetPrefColorName(HILITE_FG_COLOR),
3454 GetPrefColorName(HILITE_BG_COLOR),
3455 GetPrefColorName(LINENO_FG_COLOR),
3456 GetPrefColorName(CURSOR_FG_COLOR));
3458 /* Create the right button popup menu (note: order is important here,
3459 since the translation for popping up this menu was probably already
3460 added in createTextArea, but CreateBGMenu requires window->textArea
3461 to be set so it can attach the menu to it (because menu shells are
3462 finicky about the kinds of widgets they are attached to)) */
3463 window->bgMenuPane = CreateBGMenu(window);
3465 /* cache user menus: init. user background menu cache */
3466 InitUserBGMenuCache(&window->userBGMenuCache);
3468 /* Create the text buffer rather than using the one created automatically
3469 with the text area widget. This is done so the syntax highlighting
3470 modify callback can be called to synchronize the style buffer BEFORE
3471 the text display's callback is called upon to display a modification */
3472 window->buffer = BufCreate();
3473 BufAddModifyCB(window->buffer, SyntaxHighlightModifyCB, window);
3475 /* Attach the buffer to the text widget, and add callbacks for modify */
3476 TextSetBuffer(text, window->buffer);
3477 BufAddModifyCB(window->buffer, modifiedCB, window);
3479 /* Designate the permanent text area as the owner for selections */
3480 HandleXSelections(text);
3482 /* Set the requested hardware tab distance and useTabs in the text buffer */
3483 BufSetTabDistance(window->buffer, GetPrefTabDist(PLAIN_LANGUAGE_MODE));
3484 window->buffer->useTabs = GetPrefInsertTabs();
3485 window->tab = addTab(window->tabBar, window, name);
3487 /* add the window to the global window list, update the Windows menus */
3488 InvalidateWindowMenus();
3489 addToWindowList(window);
3491 #ifdef LESSTIF_VERSION
3492 /* FIXME: Temporary workaround for disappearing-text-window bug
3493 when linking to Lesstif.
3495 After changes is made to statsAreaForm (parent of statsline,
3496 i-search line and tab bar) widget such as enabling/disabling
3497 the statsline, the XmForm widget enclosing the text widget
3498 somehow refused to resize to fit the text widget. Resizing
3499 the shell window or making changes [again] to the statsAreaForm
3500 appeared to bring out the text widget, though doesn't fix it for
3501 the subsequently added documents. Here we try to do the latter
3502 for all new documents created. */
3503 if (XtIsManaged(XtParent(window->statsLineForm))) {
3504 XtUnmanageChild(XtParent(window->statsLineForm));
3505 XtManageChild(XtParent(window->statsLineForm));
3507 #endif /* LESSTIF_VERSION */
3509 /* return the shell ownership to previous tabbed doc */
3510 XtVaSetValues(window->mainWin, XmNworkWindow, shellWindow->splitPane, NULL);
3511 XLowerWindow(TheDisplay, XtWindow(window->splitPane));
3512 XtUnmanageChild(window->splitPane);
3513 XtVaSetValues(window->splitPane, XmNmappedWhenManaged, True, NULL);
3515 return window;
3519 ** return the next/previous docment on the tab list.
3521 ** If <wrap> is true then the next tab of the rightmost tab will be the
3522 ** second tab from the right, and the the previous tab of the leftmost
3523 ** tab will be the second from the left. This is useful for getting
3524 ** the next tab after a tab detaches/closes and you don't want to wrap around.
3526 static WindowInfo *getNextTabWindow(WindowInfo *window, int direction,
3527 int crossWin, int wrap)
3529 WidgetList tabList, tabs;
3530 WindowInfo *win;
3531 int tabCount, tabTotalCount;
3532 int tabPos, nextPos;
3533 int i, n;
3534 int nBuf = crossWin? NWindows() : NDocuments(window);
3536 if (nBuf <= 1)
3537 return NULL;
3539 /* get the list of tabs */
3540 tabs = (WidgetList)XtMalloc(sizeof(Widget) * nBuf);
3541 tabTotalCount = 0;
3542 if (crossWin) {
3543 int n, nItems;
3544 WidgetList children;
3546 XtVaGetValues(TheAppShell, XmNchildren, &children,
3547 XmNnumChildren, &nItems, NULL);
3549 /* get list of tabs in all windows */
3550 for (n=0; n<nItems; n++) {
3551 if (strcmp(XtName(children[n]), "textShell") ||
3552 ((win = WidgetToWindow(children[n])) == NULL))
3553 continue; /* skip non-text-editor windows */
3555 XtVaGetValues(win->tabBar, XmNtabWidgetList, &tabList,
3556 XmNtabCount, &tabCount, NULL);
3558 for (i=0; i< tabCount; i++) {
3559 tabs[tabTotalCount++] = tabList[i];
3563 else {
3564 /* get list of tabs in this window */
3565 XtVaGetValues(window->tabBar, XmNtabWidgetList, &tabList,
3566 XmNtabCount, &tabCount, NULL);
3568 for (i=0; i< tabCount; i++) {
3569 if (TabToWindow(tabList[i])) /* make sure tab is valid */
3570 tabs[tabTotalCount++] = tabList[i];
3574 /* find the position of the tab in the tablist */
3575 tabPos = 0;
3576 for (n=0; n<tabTotalCount; n++) {
3577 if (tabs[n] == window->tab) {
3578 tabPos = n;
3579 break;
3583 /* calculate index position of next tab */
3584 nextPos = tabPos + direction;
3585 if (nextPos >= nBuf) {
3586 if (wrap)
3587 nextPos = 0;
3588 else
3589 nextPos = nBuf - 2;
3590 } else if (nextPos < 0) {
3591 if (wrap)
3592 nextPos = nBuf - 1;
3593 else
3594 nextPos = 1;
3597 /* return the document where the next tab belongs to */
3598 win = TabToWindow(tabs[nextPos]);
3599 XtFree((char *)tabs);
3600 return win;
3604 ** return the integer position of a tab in the tabbar it
3605 ** belongs to, or -1 if there's an error, somehow.
3607 static int getTabPosition(Widget tab)
3609 WidgetList tabList;
3610 int i, tabCount;
3611 Widget tabBar = XtParent(tab);
3613 XtVaGetValues(tabBar, XmNtabWidgetList, &tabList,
3614 XmNtabCount, &tabCount, NULL);
3616 for (i=0; i< tabCount; i++) {
3617 if (tab == tabList[i])
3618 return i;
3621 return -1; /* something is wrong! */
3625 ** update the tab label, etc. of a tab, per the states of it's document.
3627 void RefreshTabState(WindowInfo *win)
3629 XmString s1, tipString;
3630 char labelString[MAXPATHLEN];
3631 char *tag = XmFONTLIST_DEFAULT_TAG;
3632 unsigned char alignment;
3634 /* Set tab label to document's filename. Position of
3635 "*" (modified) will change per label alignment setting */
3636 XtVaGetValues(win->tab, XmNalignment, &alignment, NULL);
3637 if (alignment != XmALIGNMENT_END) {
3638 sprintf(labelString, "%s%s",
3639 win->fileChanged? "*" : "",
3640 win->filename);
3641 } else {
3642 sprintf(labelString, "%s%s",
3643 win->filename,
3644 win->fileChanged? "*" : "");
3647 /* Make the top document stand out a little more */
3648 if (IsTopDocument(win))
3649 tag = "BOLD";
3651 s1 = XmStringCreateLtoR(labelString, tag);
3653 if (GetPrefShowPathInWindowsMenu() && win->filenameSet) {
3654 strcat(labelString, " - ");
3655 strcat(labelString, win->path);
3657 tipString=XmStringCreateSimple(labelString);
3659 XtVaSetValues(win->tab,
3660 XltNbubbleString, tipString,
3661 XmNlabelString, s1,
3662 NULL);
3663 XmStringFree(s1);
3664 XmStringFree(tipString);
3668 ** close all the documents in a window
3670 int CloseAllDocumentInWindow(WindowInfo *window)
3672 WindowInfo *win;
3674 if (NDocuments(window) == 1) {
3675 /* only one document in the window */
3676 return CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE);
3678 else {
3679 Widget winShell = window->shell;
3680 WindowInfo *topDocument;
3682 /* close all _modified_ documents belong to this window */
3683 for (win = WindowList; win; ) {
3684 if (win->shell == winShell && win->fileChanged) {
3685 WindowInfo *next = win->next;
3686 if (!CloseFileAndWindow(win, PROMPT_SBC_DIALOG_RESPONSE))
3687 return False;
3688 win = next;
3690 else
3691 win = win->next;
3694 /* see there's still documents left in the window */
3695 for (win = WindowList; win; win=win->next)
3696 if (win->shell == winShell)
3697 break;
3699 if (win) {
3700 topDocument = GetTopDocument(winShell);
3702 /* close all non-top documents belong to this window */
3703 for (win = WindowList; win; ) {
3704 if (win->shell == winShell && win != topDocument) {
3705 WindowInfo *next = win->next;
3706 if (!CloseFileAndWindow(win, PROMPT_SBC_DIALOG_RESPONSE))
3707 return False;
3708 win = next;
3710 else
3711 win = win->next;
3714 /* close the last document and its window */
3715 if (!CloseFileAndWindow(topDocument, PROMPT_SBC_DIALOG_RESPONSE))
3716 return False;
3720 return True;
3723 static void CloseDocumentWindow(Widget w, WindowInfo *window, XtPointer callData)
3725 int nDocuments = NDocuments(window);
3727 if (nDocuments == NWindows()) {
3728 /* this is only window, then exit */
3729 XtCallActionProc(WindowList->lastFocus, "exit",
3730 ((XmAnyCallbackStruct *)callData)->event, NULL, 0);
3732 else {
3733 if (nDocuments == 1) {
3734 CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE);
3736 else {
3737 int resp = DialogF(DF_QUES, window->shell, 2, "Close Window",
3738 "Close ALL documents in this window?", "Close", "Cancel");
3740 if (resp == 1)
3741 CloseAllDocumentInWindow(window);
3746 static void cloneTextPane(WindowInfo *window, WindowInfo *orgWin)
3748 short paneHeights[MAX_PANES+1];
3749 int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1];
3750 int horizOffsets[MAX_PANES+1];
3751 int i, focusPane, emTabDist, wrapMargin, lineNumCols, totalHeight=0;
3752 char *delimiters;
3753 Widget text;
3754 selection sel;
3756 /* transfer the primary selection */
3757 memcpy(&sel, &orgWin->buffer->primary, sizeof(selection));
3759 if (sel.selected) {
3760 if (sel.rectangular)
3761 BufRectSelect(window->buffer, sel.start, sel.end,
3762 sel.rectStart, sel.rectEnd);
3763 else
3764 BufSelect(window->buffer, sel.start, sel.end);
3765 } else
3766 BufUnselect(window->buffer);
3768 /* Record the current heights, scroll positions, and insert positions
3769 of the existing panes, keyboard focus */
3770 focusPane = 0;
3771 for (i=0; i<=orgWin->nPanes; i++) {
3772 text = i==0 ? orgWin->textArea : orgWin->textPanes[i-1];
3773 insertPositions[i] = TextGetCursorPos(text);
3774 XtVaGetValues(containingPane(text), XmNheight, &paneHeights[i], NULL);
3775 totalHeight += paneHeights[i];
3776 TextGetScroll(text, &topLines[i], &horizOffsets[i]);
3777 if (text == orgWin->lastFocus)
3778 focusPane = i;
3781 window->nPanes = orgWin->nPanes;
3783 /* clone split panes, if any */
3784 if (window->nPanes) {
3785 /* Unmanage & remanage the panedWindow so it recalculates pane heights */
3786 XtUnmanageChild(window->splitPane);
3788 /* Create a text widget to add to the pane and set its buffer and
3789 highlight data to be the same as the other panes in the orgWin */
3790 XtVaGetValues(orgWin->textArea, textNemulateTabs, &emTabDist,
3791 textNwordDelimiters, &delimiters, textNwrapMargin, &wrapMargin,
3792 textNlineNumCols, &lineNumCols, NULL);
3794 for(i=0; i<orgWin->nPanes; i++) {
3795 text = createTextArea(window->splitPane, window, 1, 1, emTabDist,
3796 delimiters, wrapMargin, lineNumCols);
3797 TextSetBuffer(text, window->buffer);
3799 if (window->highlightData != NULL)
3800 AttachHighlightToWidget(text, window);
3801 XtManageChild(text);
3802 window->textPanes[i] = text;
3805 /* Set the minimum pane height in the new pane */
3806 UpdateMinPaneHeights(window);
3808 for (i=0; i<=window->nPanes; i++) {
3809 text = i==0 ? window->textArea : window->textPanes[i-1];
3810 setPaneDesiredHeight(containingPane(text), paneHeights[i]);
3813 /* Re-manage panedWindow to recalculate pane heights & reset selection */
3814 XtManageChild(window->splitPane);
3817 /* Reset all of the heights, scroll positions, etc. */
3818 for (i=0; i<=window->nPanes; i++) {
3819 textDisp *textD;
3821 text = i==0 ? window->textArea : window->textPanes[i-1];
3822 TextSetCursorPos(text, insertPositions[i]);
3823 TextSetScroll(text, topLines[i], horizOffsets[i]);
3825 /* dim the cursor */
3826 textD = ((TextWidget)text)->text.textD;
3827 TextDSetCursorStyle(textD, DIM_CURSOR);
3828 TextDUnblankCursor(textD);
3831 /* set the focus pane */
3832 for (i=0; i<=window->nPanes; i++) {
3833 text = i==0 ? window->textArea : window->textPanes[i-1];
3834 if(i == focusPane) {
3835 window->lastFocus = text;
3836 XmProcessTraversal(text, XmTRAVERSE_CURRENT);
3837 break;
3841 /* Update the window manager size hints after the sizes of the panes have
3842 been set (the widget heights are not yet readable here, but they will
3843 be by the time the event loop gets around to running this timer proc) */
3844 XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0,
3845 wmSizeUpdateProc, window);
3849 ** Refresh the menu entries per the settings of the
3850 ** top document.
3852 void RefreshMenuToggleStates(WindowInfo *window)
3854 WindowInfo *win;
3856 if (!IsTopDocument(window))
3857 return;
3859 /* File menu */
3860 XtSetSensitive(window->printSelItem, window->wasSelected);
3862 /* Edit menu */
3863 XtSetSensitive(window->undoItem, window->undo != NULL);
3864 XtSetSensitive(window->redoItem, window->redo != NULL);
3865 XtSetSensitive(window->printSelItem, window->wasSelected);
3866 XtSetSensitive(window->cutItem, window->wasSelected);
3867 XtSetSensitive(window->copyItem, window->wasSelected);
3868 XtSetSensitive(window->delItem, window->wasSelected);
3870 /* Preferences menu */
3871 XmToggleButtonSetState(window->statsLineItem, window->showStats, False);
3872 XmToggleButtonSetState(window->iSearchLineItem, window->showISearchLine, False);
3873 XmToggleButtonSetState(window->lineNumsItem, window->showLineNumbers, False);
3874 XmToggleButtonSetState(window->highlightItem, window->highlightSyntax, False);
3875 XtSetSensitive(window->highlightItem, window->languageMode != PLAIN_LANGUAGE_MODE);
3876 XmToggleButtonSetState(window->backlightCharsItem, window->backlightChars, False);
3877 XmToggleButtonSetState(window->saveLastItem, window->saveOldVersion, False);
3878 XmToggleButtonSetState(window->autoSaveItem, window->autoSave, False);
3879 XmToggleButtonSetState(window->overtypeModeItem, window->overstrike, False);
3880 XmToggleButtonSetState(window->matchSyntaxBasedItem, window->matchSyntaxBased, False);
3881 XmToggleButtonSetState(window->readOnlyItem, IS_USER_LOCKED(window->lockReasons), False);
3883 XtSetSensitive(window->smartIndentItem,
3884 SmartIndentMacrosAvailable(LanguageModeName(window->languageMode)));
3886 SetAutoIndent(window, window->indentStyle);
3887 SetAutoWrap(window, window->wrapMode);
3888 SetShowMatching(window, window->showMatchingStyle);
3889 SetLanguageMode(window, window->languageMode, FALSE);
3891 /* Windows Menu */
3892 XtSetSensitive(window->splitPaneItem, window->nPanes < MAX_PANES);
3893 XtSetSensitive(window->closePaneItem, window->nPanes > 0);
3894 XtSetSensitive(window->detachDocumentItem, NDocuments(window)>1);
3895 XtSetSensitive(window->contextDetachDocumentItem, NDocuments(window)>1);
3897 for (win=WindowList; win; win=win->next)
3898 if (win->shell != window->shell)
3899 break;
3900 XtSetSensitive(window->moveDocumentItem, win != NULL);
3904 ** Refresh the various settings/state of the shell window per the
3905 ** settings of the top document.
3907 static void refreshMenuBar(WindowInfo *window)
3909 RefreshMenuToggleStates(window);
3911 /* Add/remove language specific menu items */
3912 UpdateUserMenus(window);
3914 /* refresh selection-sensitive menus */
3915 DimSelectionDepUserMenuItems(window, window->wasSelected);
3919 ** remember the last document.
3921 WindowInfo *MarkLastDocument(WindowInfo *window)
3923 WindowInfo *prev = lastFocusDocument;
3925 if (window)
3926 lastFocusDocument = window;
3928 return prev;
3932 ** remember the active (top) document.
3934 WindowInfo *MarkActiveDocument(WindowInfo *window)
3936 WindowInfo *prev = inFocusDocument;
3938 if (window)
3939 inFocusDocument = window;
3941 return prev;
3945 ** Bring up the next window by tab order
3947 void NextDocument(WindowInfo *window)
3949 WindowInfo *win;
3951 if (WindowList->next == NULL)
3952 return;
3954 win = getNextTabWindow(window, 1, GetPrefGlobalTabNavigate(), 1);
3955 if (win == NULL)
3956 return;
3958 if (window->shell == win->shell)
3959 RaiseDocument(win);
3960 else
3961 RaiseDocumentWindow(win);
3965 ** Bring up the previous window by tab order
3967 void PreviousDocument(WindowInfo *window)
3969 WindowInfo *win;
3971 if (WindowList->next == NULL)
3972 return;
3974 win = getNextTabWindow(window, -1, GetPrefGlobalTabNavigate(), 1);
3975 if (win == NULL)
3976 return;
3978 if (window->shell == win->shell)
3979 RaiseDocument(win);
3980 else
3981 RaiseDocumentWindow(win);
3985 ** Bring up the last active window
3987 void LastDocument(WindowInfo *window)
3989 WindowInfo *win;
3991 for(win = WindowList; win; win=win->next)
3992 if (lastFocusDocument == win)
3993 break;
3995 if (!win)
3996 return;
3998 if (window->shell == win->shell)
3999 RaiseDocument(win);
4000 else
4001 RaiseDocumentWindow(win);
4006 ** make sure window is alive is kicking
4008 int IsValidWindow(WindowInfo *window)
4010 WindowInfo *win;
4012 for(win = WindowList; win; win=win->next)
4013 if (window == win)
4014 return True;
4017 return False;
4021 ** raise the document and its shell window
4023 void RaiseDocumentWindow(WindowInfo *window)
4025 if (!window)
4026 return;
4028 RaiseDocument(window);
4029 RaiseShellWindow(window->shell);
4033 ** Redisplay menu tearoffs previously hid by hideTearOffs()
4035 void redisplayTearOffs(Widget menuPane)
4037 WidgetList itemList;
4038 Widget subMenuID;
4039 Cardinal nItems;
4040 int n;
4042 /* redisplay all submenu tearoffs */
4043 XtVaGetValues(menuPane, XmNchildren, &itemList,
4044 XmNnumChildren, &nItems, NULL);
4045 for (n=0; n<(int)nItems; n++) {
4046 if (XtClass(itemList[n]) == xmCascadeButtonWidgetClass) {
4047 XtVaGetValues(itemList[n], XmNsubMenuId, &subMenuID, NULL);
4048 redisplayTearOffs(subMenuID);
4052 /* redisplay tearoff for this menu */
4053 if (!XmIsMenuShell(XtParent(menuPane)))
4054 ShowHiddenTearOff(menuPane);
4058 ** hide all the tearoffs spawned from this menu.
4059 ** It works recursively to close the tearoffs of the submenus
4061 static void hideTearOffs(Widget menuPane)
4063 WidgetList itemList;
4064 Widget subMenuID;
4065 Cardinal nItems;
4066 int n;
4068 /* hide all submenu tearoffs */
4069 XtVaGetValues(menuPane, XmNchildren, &itemList,
4070 XmNnumChildren, &nItems, NULL);
4071 for (n=0; n<(int)nItems; n++) {
4072 if (XtClass(itemList[n]) == xmCascadeButtonWidgetClass) {
4073 XtVaGetValues(itemList[n], XmNsubMenuId, &subMenuID, NULL);
4074 hideTearOffs(subMenuID);
4078 /* hide tearoff for this menu */
4079 if (!XmIsMenuShell(XtParent(menuPane)))
4080 XtUnmapWidget(XtParent(menuPane));
4084 ** Raise a tabbed document within its shell window.
4086 ** NB: use RaiseDocumentWindow() to raise the doc and
4087 ** its shell window.
4089 void RaiseDocument(WindowInfo *window)
4091 WindowInfo *win, *lastwin;
4093 if (!window || !WindowList)
4094 return;
4096 lastwin = MarkActiveDocument(window);
4097 if (lastwin != window && IsValidWindow(lastwin))
4098 MarkLastDocument(lastwin);
4100 /* document already on top? */
4101 XtVaGetValues(window->mainWin, XmNuserData, &win, NULL);
4102 if (win == window)
4103 return;
4105 /* set the document as top document */
4106 XtVaSetValues(window->mainWin, XmNuserData, window, NULL);
4108 /* show the new top document */
4109 XtVaSetValues(window->mainWin, XmNworkWindow, window->splitPane, NULL);
4110 XtManageChild(window->splitPane);
4111 XRaiseWindow(TheDisplay, XtWindow(window->splitPane));
4113 /* Turn on syntax highlight that might have been deferred.
4114 NB: this must be done after setting the document as
4115 XmNworkWindow and managed, else the parent shell
4116 window may shrink on some window-managers such as
4117 metacity, due to changes made in UpdateWMSizeHints().*/
4118 if (window->highlightSyntax && window->highlightData==NULL)
4119 StartHighlighting(window, False);
4121 /* put away the bg menu tearoffs of last active document */
4122 hideTearOffs(win->bgMenuPane);
4124 /* restore the bg menu tearoffs of active document */
4125 redisplayTearOffs(window->bgMenuPane);
4127 /* set tab as active */
4128 XmLFolderSetActiveTab(window->tabBar,
4129 getTabPosition(window->tab), False);
4131 /* set keyboard focus. Must be done before unmanaging previous
4132 top document, else lastFocus will be reset to textArea */
4133 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
4135 /* we only manage the top document, else the next time a document
4136 is raised again, it's textpane might not resize properly.
4137 Also, somehow (bug?) XtUnmanageChild() doesn't hide the
4138 splitPane, which obscure lower part of the statsform when
4139 we toggle its components, so we need to put the document at
4140 the back */
4141 XLowerWindow(TheDisplay, XtWindow(win->splitPane));
4142 XtUnmanageChild(win->splitPane);
4143 RefreshTabState(win);
4145 /* now refresh window state/info. RefreshWindowStates()
4146 has a lot of work to do, so we update the screen first so
4147 the document appears to switch swiftly. */
4148 XmUpdateDisplay(window->splitPane);
4149 RefreshWindowStates(window);
4150 RefreshTabState(window);
4152 /* put away the bg menu tearoffs of last active document */
4153 hideTearOffs(win->bgMenuPane);
4155 /* restore the bg menu tearoffs of active document */
4156 redisplayTearOffs(window->bgMenuPane);
4158 UpdateWMSizeHints(window);
4161 WindowInfo* GetTopDocument(Widget w)
4163 WindowInfo *window = WidgetToWindow(w);
4165 return WidgetToWindow(window->shell);
4168 Boolean IsTopDocument(const WindowInfo *window)
4170 return window == GetTopDocument(window->shell)? True : False;
4173 void DeleteDocument(WindowInfo *window)
4175 if (!window)
4176 return;
4178 XtDestroyWidget(window->splitPane);
4182 ** clone a document's states and settings into the other.
4184 static void cloneDocument(WindowInfo *window, WindowInfo *orgWin)
4186 char *orgDocument;
4187 char *params[4];
4188 int emTabDist;
4190 strcpy(window->path, orgWin->path);
4191 strcpy(window->filename, orgWin->filename);
4193 ShowLineNumbers(window, orgWin->showLineNumbers);
4195 window->ignoreModify = True;
4197 /* copy the text buffer */
4198 orgDocument = BufGetAll(orgWin->buffer);
4199 BufSetAll(window->buffer, orgDocument);
4200 XtFree(orgDocument);
4202 /* copy the tab preferences (here!) */
4203 BufSetTabDistance(window->buffer, orgWin->buffer->tabDist);
4204 window->buffer->useTabs = orgWin->buffer->useTabs;
4205 XtVaGetValues(orgWin->textArea, textNemulateTabs, &emTabDist, NULL);
4206 SetEmTabDist(window, emTabDist);
4208 window->ignoreModify = False;
4210 /* transfer text fonts */
4211 params[0] = orgWin->fontName;
4212 params[1] = orgWin->italicFontName;
4213 params[2] = orgWin->boldFontName;
4214 params[3] = orgWin->boldItalicFontName;
4215 XtCallActionProc(window->textArea, "set_fonts", NULL, params, 4);
4217 SetBacklightChars(window, orgWin->backlightCharTypes);
4219 /* syntax hilite */
4220 window->languageMode = orgWin->languageMode;
4221 window->highlightSyntax = orgWin->highlightSyntax;
4222 if (window->highlightSyntax)
4223 StartHighlighting(window, False);
4225 /* copy states of original document */
4226 window->filenameSet = orgWin->filenameSet;
4227 window->fileFormat = orgWin->fileFormat;
4228 window->lastModTime = orgWin->lastModTime;
4229 window->fileChanged = orgWin->fileChanged;
4230 window->fileMissing = orgWin->fileMissing;
4231 window->lockReasons = orgWin->lockReasons;
4232 window->nPanes = orgWin->nPanes;
4233 window->autoSaveCharCount = orgWin->autoSaveCharCount;
4234 window->autoSaveOpCount = orgWin->autoSaveOpCount;
4235 window->undoOpCount = orgWin->undoOpCount;
4236 window->undoMemUsed = orgWin->undoMemUsed;
4237 window->lockReasons = orgWin->lockReasons;
4238 window->autoSave = orgWin->autoSave;
4239 window->saveOldVersion = orgWin->saveOldVersion;
4240 window->wrapMode = orgWin->wrapMode;
4241 window->overstrike = orgWin->overstrike;
4242 window->showMatchingStyle = orgWin->showMatchingStyle;
4243 window->matchSyntaxBased = orgWin->matchSyntaxBased;
4244 #if 0
4245 window->showStats = orgWin->showStats;
4246 window->showISearchLine = orgWin->showISearchLine;
4247 window->showLineNumbers = orgWin->showLineNumbers;
4248 window->modeMessageDisplayed = orgWin->modeMessageDisplayed;
4249 window->ignoreModify = orgWin->ignoreModify;
4250 window->windowMenuValid = orgWin->windowMenuValid;
4251 window->prevOpenMenuValid = orgWin->prevOpenMenuValid;
4252 window->flashTimeoutID = orgWin->flashTimeoutID;
4253 window->wasSelected = orgWin->wasSelected;
4254 strcpy(window->fontName, orgWin->fontName);
4255 strcpy(window->italicFontName, orgWin->italicFontName);
4256 strcpy(window->boldFontName, orgWin->boldFontName);
4257 strcpy(window->boldItalicFontName, orgWin->boldItalicFontName);
4258 window->fontList = orgWin->fontList;
4259 window->italicFontStruct = orgWin->italicFontStruct;
4260 window->boldFontStruct = orgWin->boldFontStruct;
4261 window->boldItalicFontStruct = orgWin->boldItalicFontStruct;
4262 window->markTimeoutID = orgWin->markTimeoutID;
4263 window->highlightData = orgWin->highlightData;
4264 window->shellCmdData = orgWin->shellCmdData;
4265 window->macroCmdData = orgWin->macroCmdData;
4266 window->smartIndentData = orgWin->smartIndentData;
4267 #endif
4268 window->iSearchHistIndex = orgWin->iSearchHistIndex;
4269 window->iSearchStartPos = orgWin->iSearchStartPos;
4270 window->replaceLastRegexCase = orgWin->replaceLastRegexCase;
4271 window->replaceLastLiteralCase = orgWin->replaceLastLiteralCase;
4272 window->iSearchLastRegexCase = orgWin->iSearchLastRegexCase;
4273 window->iSearchLastLiteralCase = orgWin->iSearchLastLiteralCase;
4274 window->findLastRegexCase = orgWin->findLastRegexCase;
4275 window->findLastLiteralCase = orgWin->findLastLiteralCase;
4277 /* copy the text/split panes settings, cursor pos & selection */
4278 cloneTextPane(window, orgWin);
4280 /* copy undo & redo list */
4281 window->undo = cloneUndoItems(orgWin->undo);
4282 window->redo = cloneUndoItems(orgWin->redo);
4284 /* copy bookmarks */
4285 window->nMarks = orgWin->nMarks;
4286 memcpy(&window->markTable, &orgWin->markTable,
4287 sizeof(Bookmark)*window->nMarks);
4289 /* clone rangeset info */
4290 window->buffer->rangesetTable =
4291 RangesetTableClone(orgWin->buffer->rangesetTable, window->buffer);
4293 /* kick start the auto-indent engine */
4294 window->indentStyle = NO_AUTO_INDENT;
4295 SetAutoIndent(window, orgWin->indentStyle);
4297 /* synchronize window state to this document */
4298 RefreshWindowStates(window);
4301 static UndoInfo *cloneUndoItems(UndoInfo *orgList)
4303 UndoInfo *head = NULL, *undo, *clone, *last = NULL;
4305 for (undo = orgList; undo; undo = undo->next) {
4306 clone = (UndoInfo *)XtMalloc(sizeof(UndoInfo));
4307 memcpy(clone, undo, sizeof(UndoInfo));
4309 if (undo->oldText) {
4310 clone->oldText = XtMalloc(strlen(undo->oldText)+1);
4311 strcpy(clone->oldText, undo->oldText);
4313 clone->next = NULL;
4315 if (last)
4316 last->next = clone;
4317 else
4318 head = clone;
4320 last = clone;
4323 return head;
4327 ** return the number of documents owned by this shell window
4329 int NDocuments(WindowInfo *window)
4331 WindowInfo *win;
4332 int nDocument = 0;
4334 for (win = WindowList; win; win = win->next) {
4335 if (win->shell == window->shell)
4336 nDocument++;
4339 return nDocument;
4343 ** refresh window state for this document
4345 void RefreshWindowStates(WindowInfo *window)
4347 if (!IsTopDocument(window))
4348 return;
4350 if (window->modeMessageDisplayed)
4351 XmTextSetString(window->statsLine, window->modeMessage);
4352 else
4353 UpdateStatsLine(window);
4354 UpdateWindowReadOnly(window);
4355 UpdateWindowTitle(window);
4357 /* show/hide statsline as needed */
4358 if (window->modeMessageDisplayed && !XtIsManaged(window->statsLineForm)) {
4359 /* turn on statline to display mode message */
4360 showStats(window, True);
4362 else if (window->showStats && !XtIsManaged(window->statsLineForm)) {
4363 /* turn on statsline since it is enabled */
4364 showStats(window, True);
4366 else if (!window->showStats && !window->modeMessageDisplayed &&
4367 XtIsManaged(window->statsLineForm)) {
4368 /* turn off statsline since there's nothing to show */
4369 showStats(window, False);
4372 /* signal if macro/shell is running */
4373 if (window->shellCmdData || window->macroCmdData)
4374 BeginWait(window->shell);
4375 else
4376 EndWait(window->shell);
4378 /* we need to force the statsline to reveal itself */
4379 if (XtIsManaged(window->statsLineForm)) {
4380 XmTextSetCursorPosition(window->statsLine, 0); /* start of line */
4381 XmTextSetCursorPosition(window->statsLine, 9000); /* end of line */
4384 XmUpdateDisplay(window->statsLine);
4385 refreshMenuBar(window);
4389 ** spin off the document to a new window
4391 WindowInfo *DetachDocument(WindowInfo *window)
4393 WindowInfo *win, *cloneWin;
4394 char geometry[MAX_GEOM_STRING_LEN];
4395 int rows, cols;
4397 if (NDocuments(window) < 2)
4398 return NULL;
4400 /* raise another document in the same shell window */
4401 win = getNextTabWindow(window, 1, 0, 0);
4402 RaiseDocument(win);
4404 /* create new window in roughly the size of original window,
4405 to reduce flicker when the window is resized later */
4406 getTextPaneDimension(window, &rows, &cols);
4407 sprintf(geometry, "%dx%d", cols, rows);
4408 cloneWin = CreateWindow(window->filename, geometry, False);
4410 /* CreateWindow() simply adds the new window's pointer to the
4411 head of WindowList. We need to adjust the detached window's
4412 pointer, so that macro functions such as focus_window("last")
4413 will travel across the documents per the sequence they're
4414 opened. The new doc will appear to replace it's former self
4415 as the old doc is closed. */
4416 WindowList = cloneWin->next;
4417 cloneWin->next = window->next;
4418 window->next = cloneWin;
4420 /* these settings should follow the detached document.
4421 must be done before cloning window, else the height
4422 of split panes may not come out correctly */
4423 ShowISearchLine(cloneWin, window->showISearchLine);
4424 ShowStatsLine(cloneWin, window->showStats);
4426 /* clone the document & its pref settings */
4427 cloneDocument(cloneWin, window);
4429 /* remove the document from the old window */
4430 window->fileChanged = False;
4431 CloseFileAndWindow(window, NO_SBC_DIALOG_RESPONSE);
4433 /* refresh former host window */
4434 RefreshWindowStates(win);
4436 /* this should keep the new document window fresh */
4437 RefreshWindowStates(cloneWin);
4438 RefreshTabState(cloneWin);
4439 SortTabBar(cloneWin);
4441 return cloneWin;
4445 ** Move document to an other window.
4447 ** the moving document will receive certain window settings from
4448 ** its new host, i.e. the window size, stats and isearch lines.
4450 WindowInfo *MoveDocument(WindowInfo *toWindow, WindowInfo *window)
4452 WindowInfo *win, *cloneWin;
4454 /* raise another document to replace the document being moved */
4455 for (win = WindowList; win; win = win->next) {
4456 if (win->shell == window->shell && window != win)
4457 break;
4460 if (win)
4461 RaiseDocument(win);
4462 else
4463 XtUnmapWidget(window->shell);
4465 /* relocate the document to target window */
4466 cloneWin = CreateDocument(toWindow, window->filename, NULL, False);
4467 ShowTabBar(cloneWin, GetShowTabBar(cloneWin));
4468 cloneDocument(cloneWin, window);
4470 /* CreateDocument() simply adds the new window's pointer to the
4471 head of WindowList. We need to adjust the detached window's
4472 pointer, so that macro functions such as focus_window("last")
4473 will travel across the documents per the sequence they're
4474 opened. The new doc will appear to replace it's former self
4475 as the old doc is closed. */
4476 WindowList = cloneWin->next;
4477 cloneWin->next = window->next;
4478 window->next = cloneWin;
4480 /* remove the document from the old window */
4481 window->fileChanged = False;
4482 CloseFileAndWindow(window, NO_SBC_DIALOG_RESPONSE);
4484 /* some menu states might have changed when deleting document */
4485 if (win) {
4486 RefreshWindowStates(win);
4489 /* this should keep the new document window fresh */
4490 RaiseDocumentWindow(cloneWin);
4491 RefreshTabState(cloneWin);
4492 SortTabBar(cloneWin);
4494 return cloneWin;
4497 static void moveDocumentCB(Widget dialog, WindowInfo *window,
4498 XtPointer call_data)
4500 XmSelectionBoxCallbackStruct *cbs = (XmSelectionBoxCallbackStruct *) call_data;
4501 DoneWithMoveDocumentDialog = cbs->reason;
4505 ** present dialog for selecting a target window to move this document
4506 ** into. Do nothing if there is only one shell window opened.
4508 void MoveDocumentDialog(WindowInfo *window)
4510 WindowInfo *win, *targetWin, **shellWinList;
4511 int i, nList=0, nWindows=0, ac;
4512 char tmpStr[MAXPATHLEN+50];
4513 Widget parent, dialog, listBox, moveAllOption;
4514 XmString *list = NULL;
4515 XmString popupTitle, s1;
4516 Arg csdargs[20];
4517 int *position_list, position_count;
4519 /* get the list of available shell windows, not counting
4520 the document to be moved */
4521 nWindows = NWindows();
4522 list = (XmStringTable) XtMalloc(nWindows * sizeof(XmString *));
4523 shellWinList = (WindowInfo **) XtMalloc(nWindows * sizeof(WindowInfo *));
4525 for (win=WindowList; win; win=win->next) {
4526 if (!IsTopDocument(win) || win->shell == window->shell)
4527 continue;
4529 sprintf(tmpStr, "%s%s",
4530 win->filenameSet? win->path : "", win->filename);
4532 list[nList] = XmStringCreateSimple(tmpStr);
4533 shellWinList[nList] = win;
4534 nList++;
4537 /* stop here if there's no other window to move to */
4538 if (!nList) {
4539 XtFree((char *)list);
4540 return;
4543 /* create the dialog */
4544 parent = window->shell;
4545 popupTitle = XmStringCreateSimple("Move Document");
4546 sprintf(tmpStr, "Move %s into window of", window->filename);
4547 s1 = XmStringCreateSimple(tmpStr);
4548 ac = 0;
4549 XtSetArg(csdargs[ac], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); ac++;
4550 XtSetArg(csdargs[ac], XmNdialogTitle, popupTitle); ac++;
4551 XtSetArg(csdargs[ac], XmNlistLabelString, s1); ac++;
4552 XtSetArg(csdargs[ac], XmNlistItems, list); ac++;
4553 XtSetArg(csdargs[ac], XmNlistItemCount, nList); ac++;
4554 XtSetArg(csdargs[ac], XmNvisibleItemCount, 12); ac++;
4555 XtSetArg(csdargs[ac], XmNautoUnmanage, False); ac++;
4556 dialog = CreateSelectionDialog(parent,"moveDocument",csdargs,ac);
4557 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT));
4558 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
4559 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_SELECTION_LABEL));
4560 XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)moveDocumentCB, window);
4561 XtAddCallback(dialog, XmNapplyCallback, (XtCallbackProc)moveDocumentCB, window);
4562 XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)moveDocumentCB, window);
4563 XmStringFree(s1);
4564 XmStringFree(popupTitle);
4566 /* free the window list */
4567 for (i=0; i<nList; i++)
4568 XmStringFree(list[i]);
4569 XtFree((char *)list);
4571 /* create the option box for moving all documents */
4572 s1 = MKSTRING("Move all documents in this window");
4573 moveAllOption = XtVaCreateWidget("moveAll",
4574 xmToggleButtonWidgetClass, dialog,
4575 XmNlabelString, s1,
4576 XmNalignment, XmALIGNMENT_BEGINNING,
4577 NULL);
4578 XmStringFree(s1);
4580 if (NDocuments(window) >1)
4581 XtManageChild(moveAllOption);
4583 /* disable option if only one document in the window */
4584 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_APPLY_BUTTON));
4586 s1 = MKSTRING("Move");
4587 XtVaSetValues (dialog, XmNokLabelString, s1, NULL);
4588 XmStringFree(s1);
4590 /* default to the first window on the list */
4591 listBox = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);
4592 XmListSelectPos(listBox, 1, True);
4594 /* show the dialog */
4595 DoneWithMoveDocumentDialog = 0;
4596 ManageDialogCenteredOnPointer(dialog);
4597 while (!DoneWithMoveDocumentDialog)
4598 XtAppProcessEvent(XtWidgetToApplicationContext(parent), XtIMAll);
4600 /* get the window to move document into */
4601 XmListGetSelectedPos(listBox, &position_list, &position_count);
4602 targetWin = shellWinList[position_list[0]-1];
4603 XtFree((char *)position_list);
4605 /* now move document(s) */
4606 if (DoneWithMoveDocumentDialog == XmCR_OK) {
4607 /* move top document */
4608 if (XmToggleButtonGetState(moveAllOption)) {
4609 /* move all documents */
4610 for (win = WindowList; win; ) {
4611 if (win != window && win->shell == window->shell) {
4612 WindowInfo *next = win->next;
4613 MoveDocument(targetWin, win);
4614 win = next;
4616 else
4617 win = win->next;
4620 /* invoking document is the last to move */
4621 MoveDocument(targetWin, window);
4623 else {
4624 MoveDocument(targetWin, window);
4628 XtFree((char *)shellWinList);
4629 XtDestroyWidget(dialog);
4632 static void hideTooltip(Widget tab)
4634 Widget tooltip = XtNameToWidget(tab, "*BubbleShell");
4636 if (tooltip)
4637 XtPopdown(tooltip);
4640 static void closeTabProc(XtPointer clientData, XtIntervalId *id)
4642 CloseFileAndWindow((WindowInfo*)clientData, PROMPT_SBC_DIALOG_RESPONSE);
4646 ** callback to close-tab button.
4648 static void closeTabCB(Widget w, Widget mainWin, caddr_t callData)
4650 /* FIXME: XtRemoveActionHook() related coredump
4652 An unknown bug seems to be associated with the XtRemoveActionHook()
4653 call in FinishLearn(), which resulted in coredump if a tab was
4654 closed, in the middle of keystrokes learning, by clicking on the
4655 close-tab button.
4657 As evident to our accusation, the coredump may be surpressed by
4658 simply commenting out the XtRemoveActionHook() call. The bug was
4659 consistent on both Motif and Lesstif on various platforms.
4661 Closing the tab through either the "Close" menu or its accel key,
4662 however, was without any trouble.
4664 While its actual mechanism is not well understood, we somehow
4665 managed to workaround the bug by delaying the action of closing
4666 the tab. For now. */
4667 XtAppAddTimeOut(XtWidgetToApplicationContext(w), 0,
4668 closeTabProc, GetTopDocument(mainWin));
4672 ** callback to clicks on a tab to raise it's document.
4674 static void raiseTabCB(Widget w, XtPointer clientData, XtPointer callData)
4676 XmLFolderCallbackStruct *cbs = (XmLFolderCallbackStruct *)callData;
4677 WidgetList tabList;
4678 Widget tab;
4680 XtVaGetValues(w, XmNtabWidgetList, &tabList, NULL);
4681 tab = tabList[cbs->pos];
4682 RaiseDocument(TabToWindow(tab));
4685 static Widget containingPane(Widget w)
4687 /* The containing pane used to simply be the first parent, but with
4688 the introduction of an XmFrame, it's the grandparent. */
4689 return XtParent(XtParent(w));
4693 ** set/clear toggle menu state if the calling document is on top.
4695 void SetToggleButtonState(WindowInfo *window, Widget w, Boolean state,
4696 Boolean notify)
4698 if (IsTopDocument(window)) {
4699 XmToggleButtonSetState(w, state, notify);
4704 ** set/clear menu sensitivity if the calling document is on top.
4706 void SetSensitive(WindowInfo *window, Widget w, Boolean sensitive)
4708 if (IsTopDocument(window)) {
4709 XtSetSensitive(w, sensitive);
4714 ** Remove redundant expose events on tab bar.
4716 void CleanUpTabBarExposeQueue(WindowInfo *window)
4718 XEvent event;
4719 XExposeEvent ev;
4720 int count;
4722 if (window == NULL)
4723 return;
4725 /* remove redundant expose events on tab bar */
4726 count=0;
4727 while (XCheckTypedWindowEvent(TheDisplay, XtWindow(window->tabBar),
4728 Expose, &event))
4729 count++;
4731 /* now we can update tabbar */
4732 if (count) {
4733 ev.type = Expose;
4734 ev.display = TheDisplay;
4735 ev.window = XtWindow(window->tabBar);
4736 ev.x = 0;
4737 ev.y = 0;
4738 ev.width = XtWidth(window->tabBar);
4739 ev.height = XtHeight(window->tabBar);
4740 ev.count = 0;
4741 XSendEvent(TheDisplay, XtWindow(window->tabBar), False,
4742 ExposureMask, (XEvent *)&ev);