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