Workaround for bug in OpenMotif 2.1 and 2.2. If you have an active tear-off
[nedit.git] / source / window.c
blob63c890668130cd93411703466cc339e3b0ea4b0a
1 static const char CVSID[] = "$Id: window.c,v 1.98 2004/01/13 14:46:26 tringali Exp $";
2 /*******************************************************************************
3 * *
4 * window.c -- Nirvana Editor window creation/deletion *
5 * *
6 * Copyright (C) 1999 Mark Edel *
7 * *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. *
12 * *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
16 * for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
20 * Place, Suite 330, Boston, MA 02111-1307 USA *
21 * *
22 * Nirvana Text Editor *
23 * May 10, 1991 *
24 * *
25 * Written by Mark Edel *
26 * *
27 *******************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
33 #include "window.h"
34 #include "textBuf.h"
35 #include "textSel.h"
36 #include "text.h"
37 #include "textDisp.h"
38 #include "textP.h"
39 #include "nedit.h"
40 #include "menu.h"
41 #include "file.h"
42 #include "search.h"
43 #include "undo.h"
44 #include "preferences.h"
45 #include "selection.h"
46 #include "server.h"
47 #include "shell.h"
48 #include "macro.h"
49 #include "highlight.h"
50 #include "smartIndent.h"
51 #include "userCmds.h"
52 #include "nedit.bm"
53 #include "n.bm"
54 #include "windowTitle.h"
55 #include "interpret.h"
56 #include "../util/clearcase.h"
57 #include "../util/misc.h"
58 #include "../util/fileUtils.h"
59 #include "../util/utils.h"
60 #include "../util/fileUtils.h"
61 #include "../util/DialogF.h"
62 #include "../Xlt/BubbleButton.h"
63 #include "../Microline/XmL/Folder.h"
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #ifdef VMS
69 #include "../util/VMSparam.h"
70 #else
71 #ifndef __MVS__
72 #include <sys/param.h>
73 #endif
74 #include "../util/clearcase.h"
75 #endif /*VMS*/
76 #include <limits.h>
77 #include <math.h>
78 #include <ctype.h>
79 #include <time.h>
80 #ifdef __unix__
81 #include <sys/time.h>
82 #endif
84 #include <X11/Intrinsic.h>
85 #include <X11/Shell.h>
86 #include <X11/Xatom.h>
87 #include <Xm/Xm.h>
88 #include <Xm/MainW.h>
89 #include <Xm/PanedW.h>
90 #include <Xm/PanedWP.h>
91 #include <Xm/RowColumnP.h>
92 #include <Xm/Separator.h>
93 #include <Xm/Text.h>
94 #include <Xm/ToggleB.h>
95 #include <Xm/PushB.h>
96 #include <Xm/Form.h>
97 #include <Xm/Frame.h>
98 #include <Xm/Label.h>
99 #include <Xm/SelectioB.h>
100 #include <Xm/List.h>
101 #include <Xm/Protocols.h>
102 #include <Xm/ScrolledW.h>
103 #include <Xm/ScrollBar.h>
104 #include <Xm/PrimitiveP.h>
105 #include <Xm/Frame.h>
106 #ifdef EDITRES
107 #include <X11/Xmu/Editres.h>
108 /* extern void _XEditResCheckMessages(); */
109 #endif /* EDITRES */
111 #ifdef HAVE_DEBUG_H
112 #include "../debug.h"
113 #endif
115 /* Workaround for OSF Motif tear-off bug; see CloseWindow */
116 #ifndef LESSTIF_VERSION
117 extern _XmDismissTearOff(Widget w, XtPointer call, XtPointer x);
118 #endif
120 /* Initial minimum height of a pane. Just a fallback in case setPaneMinHeight
121 (which may break in a future release) is not available */
122 #define PANE_MIN_HEIGHT 39
124 /* Thickness of 3D border around statistics and/or incremental search areas
125 below the main menu bar */
126 #define STAT_SHADOW_THICKNESS 1
128 /* buffer tabs configuration */
129 #define MIN_TAB_SLOTS 3
131 /* bitmap data for the close-tab button */
132 #define close_width 11
133 #define close_height 11
134 static unsigned char close_bits[] = {
135 0x00, 0x00, 0x00, 0x00, 0x8c, 0x01, 0xdc, 0x01, 0xf8, 0x00, 0x70, 0x00,
136 0xf8, 0x00, 0xdc, 0x01, 0x8c, 0x01, 0x00, 0x00, 0x00, 0x00};
138 static WindowInfo *getNextTabWindow(WindowInfo *window, int direction,
139 int crossWin);
140 static Widget addBufferTab(Widget folder, WindowInfo *window, const char *string);
141 static int getTabPosition(Widget tab);
142 static void setTabCloseButtonImage(Widget button);
143 static Widget manageToolBars(Widget toolBarsForm);
144 static void CloseBufferWindow(Widget w, WindowInfo *window, XtPointer callData);
145 static void closeTabCB(Widget w, Widget mainWin, caddr_t callData);
146 static void clickTabCB(Widget w, XtPointer *clientData, XtPointer callData);
147 static void raiseTabCB(Widget w, XtPointer *clientData, XtPointer callData);
148 static Widget createTextArea(Widget parent, WindowInfo *window, int rows,
149 int cols, int emTabDist, char *delimiters, int wrapMargin,
150 int lineNumCols);
151 static void showStats(WindowInfo *window, int state);
152 static void showISearch(WindowInfo *window, int state);
153 static void showStatsForm(WindowInfo *window, int state);
154 static void addToWindowList(WindowInfo *window);
155 static void removeFromWindowList(WindowInfo *window);
156 static void focusCB(Widget w, WindowInfo *window, XtPointer callData);
157 static void modifiedCB(int pos, int nInserted, int nDeleted, int nRestyled,
158 char *deletedText, void *cbArg);
159 static void movedCB(Widget w, WindowInfo *window, XtPointer callData);
160 static void dragStartCB(Widget w, WindowInfo *window, XtPointer callData);
161 static void dragEndCB(Widget w, WindowInfo *window, dragEndCBStruct *callData);
162 static void closeCB(Widget w, WindowInfo *window, XtPointer callData);
163 static void saveYourselfCB(Widget w, WindowInfo *window, XtPointer callData);
164 static void setPaneDesiredHeight(Widget w, int height);
165 static void setPaneMinHeight(Widget w, int min);
166 static void addWindowIcon(Widget shell);
167 static void wmSizeUpdateProc(XtPointer clientData, XtIntervalId *id);
168 static void getGeometryString(WindowInfo *window, char *geomString);
169 #ifdef ROWCOLPATCH
170 static void patchRowCol(Widget w);
171 static void patchedRemoveChild(Widget child);
172 #endif
173 static unsigned char* sanitizeVirtualKeyBindings();
174 static int sortAlphabetical(const void* k1, const void* k2);
175 static int virtKeyBindingsAreInvalid(const unsigned char* bindings);
176 static void restoreInsaneVirtualKeyBindings(unsigned char* bindings);
177 static void setBufferSharedPref(WindowInfo *window, WindowInfo *lastwin);
178 static void refreshBufferMenuBar(WindowInfo *window);
179 static void cloneBuffer(WindowInfo *window, WindowInfo *orgWin);
180 static void cloneTextPane(WindowInfo *window, WindowInfo *orgWin);
181 static UndoInfo *cloneUndoItems(UndoInfo *orgList);
182 static Widget containingPane(Widget w);
183 static void closeAllPopupsFor(Widget shell);
185 static WindowInfo *focusInBuffer = NULL; /* where we are now */
186 static WindowInfo *lastBuffer = NULL; /* where we came from */
187 static int DoneWithAttachBufferDialog;
191 ** Create a new editor window
193 WindowInfo *CreateWindow(const char *name, char *geometry, int iconic)
195 Widget winShell, mainWin, menuBar, pane, text, stats, statsAreaForm;
196 Widget iSearchLabel, closeTabBtn, tabForm, form;
197 WindowInfo *window;
198 Pixel bgpix, fgpix;
199 Arg al[20];
200 int ac;
201 XmString s1;
202 WindowInfo *win;
203 #ifdef SGI_CUSTOM
204 char sgi_title[MAXPATHLEN + 14 + SGI_WINDOW_TITLE_LEN] = SGI_WINDOW_TITLE;
205 #endif
206 char newGeometry[MAX_GEOM_STRING_LEN];
207 unsigned int rows, cols;
208 int x, y, bitmask;
209 static Atom wmpAtom, syAtom = 0;
210 static int firstTime = True;
211 unsigned char* invalidBindings = NULL;
212 XmFontList fontList;
213 int fontWidth, tabWidth;
214 XFontStruct *fs;
216 if (firstTime)
218 invalidBindings = sanitizeVirtualKeyBindings();
219 firstTime = False;
222 /* Allocate some memory for the new window data structure */
223 window = (WindowInfo *)XtMalloc(sizeof(WindowInfo));
225 /* initialize window structure */
226 /* + Schwarzenberg: should a
227 memset(window, 0, sizeof(WindowInfo));
228 be added here ?
230 window->replaceDlog = NULL;
231 window->replaceText = NULL;
232 window->replaceWithText = NULL;
233 window->replaceWordToggle = NULL;
234 window->replaceCaseToggle = NULL;
235 window->replaceRegexToggle = NULL;
236 window->findDlog = NULL;
237 window->findText = NULL;
238 window->findWordToggle = NULL;
239 window->findCaseToggle = NULL;
240 window->findRegexToggle = NULL;
241 window->replaceMultiFileDlog = NULL;
242 window->replaceMultiFilePathBtn = NULL;
243 window->replaceMultiFileList = NULL;
244 window->multiFileReplSelected = FALSE;
245 window->multiFileBusy = FALSE;
246 window->writableWindows = NULL;
247 window->nWritableWindows = 0;
248 window->fileChanged = FALSE;
249 window->fileMode = 0;
250 window->filenameSet = FALSE;
251 window->fileFormat = UNIX_FILE_FORMAT;
252 window->lastModTime = 0;
253 window->fileMissing = True;
254 strcpy(window->filename, name);
255 window->undo = NULL;
256 window->redo = NULL;
257 window->nPanes = 0;
258 window->autoSaveCharCount = 0;
259 window->autoSaveOpCount = 0;
260 window->undoOpCount = 0;
261 window->undoMemUsed = 0;
262 CLEAR_ALL_LOCKS(window->lockReasons);
263 window->indentStyle = GetPrefAutoIndent(PLAIN_LANGUAGE_MODE);
264 window->autoSave = GetPrefAutoSave();
265 window->saveOldVersion = GetPrefSaveOldVersion();
266 window->wrapMode = GetPrefWrap(PLAIN_LANGUAGE_MODE);
267 window->overstrike = False;
268 window->showMatchingStyle = GetPrefShowMatching();
269 window->matchSyntaxBased = GetPrefMatchSyntaxBased();
270 window->showTabBar = GetPrefTabBar() ? (GetPrefTabBarHideOne()? 0:1) : 0;
271 window->showStats = GetPrefStatsLine();
272 window->showISearchLine = GetPrefISearchLine();
273 window->showLineNumbers = GetPrefLineNums();
274 window->highlightSyntax = GetPrefHighlightSyntax();
275 window->backlightCharTypes = NULL;
276 window->backlightChars = GetPrefBacklightChars();
277 if (window->backlightChars) {
278 char *cTypes = GetPrefBacklightCharTypes();
279 if (cTypes && window->backlightChars) {
280 if ((window->backlightCharTypes = XtMalloc(strlen(cTypes) + 1)))
281 strcpy(window->backlightCharTypes, cTypes);
284 window->modeMessageDisplayed = FALSE;
285 window->ignoreModify = FALSE;
286 window->windowMenuValid = FALSE;
287 window->prevOpenMenuValid = FALSE;
288 window->flashTimeoutID = 0;
289 window->fileClosedAtom = None;
290 window->wasSelected = FALSE;
292 strcpy(window->fontName, GetPrefFontName());
293 strcpy(window->italicFontName, GetPrefItalicFontName());
294 strcpy(window->boldFontName, GetPrefBoldFontName());
295 strcpy(window->boldItalicFontName, GetPrefBoldItalicFontName());
296 window->colorDialog = NULL;
297 window->fontList = GetPrefFontList();
298 window->italicFontStruct = GetPrefItalicFont();
299 window->boldFontStruct = GetPrefBoldFont();
300 window->boldItalicFontStruct = GetPrefBoldItalicFont();
301 window->fontDialog = NULL;
302 window->nMarks = 0;
303 window->markTimeoutID = 0;
304 window->highlightData = NULL;
305 window->shellCmdData = NULL;
306 window->macroCmdData = NULL;
307 window->smartIndentData = NULL;
308 window->languageMode = PLAIN_LANGUAGE_MODE;
309 window->iSearchHistIndex = 0;
310 window->iSearchStartPos = -1;
311 window->replaceLastRegexCase = TRUE;
312 window->replaceLastLiteralCase = FALSE;
313 window->iSearchLastRegexCase = TRUE;
314 window->iSearchLastLiteralCase = FALSE;
315 window->findLastRegexCase = TRUE;
316 window->findLastLiteralCase = FALSE;
317 window->bufferTab = NULL;
319 /* If window geometry was specified, split it apart into a window position
320 component and a window size component. Create a new geometry string
321 containing the position component only. Rows and cols are stripped off
322 because we can't easily calculate the size in pixels from them until the
323 whole window is put together. Note that the preference resource is only
324 for clueless users who decide to specify the standard X geometry
325 application resource, which is pretty useless because width and height
326 are the same as the rows and cols preferences, and specifying a window
327 location will force all the windows to pile on top of one another */
328 if (geometry == NULL || geometry[0] == '\0')
329 geometry = GetPrefGeometry();
330 if (geometry == NULL || geometry[0] == '\0') {
331 rows = GetPrefRows();
332 cols = GetPrefCols();
333 newGeometry[0] = '\0';
334 } else {
335 bitmask = XParseGeometry(geometry, &x, &y, &cols, &rows);
336 if (bitmask == 0)
337 fprintf(stderr, "Bad window geometry specified: %s\n", geometry);
338 else {
339 if (!(bitmask & WidthValue))
340 cols = GetPrefCols();
341 if (!(bitmask & HeightValue))
342 rows = GetPrefRows();
344 CreateGeometryString(newGeometry, x, y, 0, 0,
345 bitmask & ~(WidthValue | HeightValue));
348 /* Create a new toplevel shell to hold the window */
349 ac = 0;
350 #ifdef SGI_CUSTOM
351 strcat(sgi_title, name);
352 XtSetArg(al[ac], XmNtitle, sgi_title); ac++;
353 XtSetArg(al[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
354 if (strncmp(name, "Untitled", 8) == 0) {
355 XtSetArg(al[ac], XmNiconName, APP_NAME); ac++;
356 } else {
357 XtSetArg(al[ac], XmNiconName, name); ac++;
359 #else
360 XtSetArg(al[ac], XmNtitle, name); ac++;
361 XtSetArg(al[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
362 XtSetArg(al[ac], XmNiconName, name); ac++;
363 #endif
364 XtSetArg(al[ac], XmNgeometry, newGeometry[0]=='\0'?NULL:newGeometry); ac++;
365 XtSetArg(al[ac], XmNinitialState,
366 iconic ? IconicState : NormalState); ac++;
368 winShell = CreateWidget(TheAppShell, "textShell",
369 topLevelShellWidgetClass, al, ac);
370 window->shell = winShell;
372 #ifdef EDITRES
373 XtAddEventHandler (winShell, (EventMask)0, True,
374 (XtEventHandler)_XEditResCheckMessages, NULL);
375 #endif /* EDITRES */
377 #ifndef SGI_CUSTOM
378 addWindowIcon(winShell);
379 #endif
381 /* Create a MainWindow to manage the menubar and text area, set the
382 userData resource to be used by WidgetToWindow to recover the
383 window pointer from the widget id of any of the window's widgets */
384 XtSetArg(al[ac], XmNuserData, window); ac++;
385 mainWin = XmCreateMainWindow(winShell, "main", al, ac);
386 window->mainWin = mainWin;
387 XtManageChild(mainWin);
389 /* The statsAreaForm holds the stats line and the I-Search line. */
390 statsAreaForm = XtVaCreateWidget("statsAreaForm",
391 xmFormWidgetClass, mainWin,
392 XmNmarginWidth, STAT_SHADOW_THICKNESS,
393 XmNmarginHeight, STAT_SHADOW_THICKNESS,
394 /* XmNautoUnmanage, False, */
395 NULL);
396 if (window->showTabBar || window->showISearchLine ||
397 window->showStats)
398 XtManageChild(statsAreaForm);
400 /* NOTE: due to a bug in openmotif 2.1.30, NEdit used to crash when
401 the i-search bar was active, and the i-search text widget was focussed,
402 and the window's width was resized to nearly zero.
403 In theory, it is possible to avoid this by imposing a minimum
404 width constraint on the nedit windows, but that width would have to
405 be at least 30 characters, which is probably unacceptable.
406 Amazingly, adding a top offset of 1 pixel to the toggle buttons of
407 the i-search bar, while keeping the the top offset of the text widget
408 to 0 seems to avoid avoid the crash. */
410 window->iSearchForm = XtVaCreateWidget("iSearchForm",
411 xmFormWidgetClass, statsAreaForm,
412 XmNshadowThickness, 0,
413 XmNleftAttachment, XmATTACH_FORM,
414 XmNleftOffset, STAT_SHADOW_THICKNESS,
415 XmNtopAttachment, XmATTACH_FORM,
416 XmNtopOffset, STAT_SHADOW_THICKNESS,
417 XmNrightAttachment, XmATTACH_FORM,
418 XmNrightOffset, STAT_SHADOW_THICKNESS,
419 XmNbottomOffset, STAT_SHADOW_THICKNESS, NULL);
420 if(window->showISearchLine)
421 XtManageChild(window->iSearchForm);
422 iSearchLabel = XtVaCreateManagedWidget("iSearchLabel",
423 xmLabelWidgetClass, window->iSearchForm,
424 XmNlabelString, s1=XmStringCreateSimple("Find:"),
425 XmNmarginHeight, 0,
426 XmNleftAttachment, XmATTACH_FORM,
427 XmNleftOffset, 5,
428 XmNtopAttachment, XmATTACH_FORM,
429 XmNtopOffset, 1, /* see openmotif note above, for aligment
430 with toggle buttons below */
431 XmNbottomAttachment, XmATTACH_FORM, NULL);
432 XmStringFree(s1);
434 /* Disable keyboard traversal of the toggle buttons. We were doing
435 this previously by forcing the keyboard focus back to the text
436 widget whenever a toggle changed. That causes an ugly focus flash
437 on screen. It's better just not to go there in the first place.
438 Plus, if the user really wants traversal, it's an X resource so it
439 can be enabled without too much pain and suffering. */
441 window->iSearchCaseToggle = XtVaCreateManagedWidget("iSearchCaseToggle",
442 xmToggleButtonWidgetClass, window->iSearchForm,
443 XmNlabelString, s1=XmStringCreateSimple("Case"),
444 XmNset, GetPrefSearch() == SEARCH_CASE_SENSE
445 || GetPrefSearch() == SEARCH_REGEX
446 || GetPrefSearch() == SEARCH_CASE_SENSE_WORD,
447 XmNtopAttachment, XmATTACH_FORM,
448 XmNbottomAttachment, XmATTACH_FORM,
449 XmNtopOffset, 1, /* see openmotif note above */
450 XmNrightAttachment, XmATTACH_FORM,
451 XmNmarginHeight, 0,
452 XmNtraversalOn, False,
453 NULL);
454 XmStringFree(s1);
456 window->iSearchRegexToggle = XtVaCreateManagedWidget("iSearchREToggle",
457 xmToggleButtonWidgetClass, window->iSearchForm,
458 XmNlabelString, s1=XmStringCreateSimple("RegExp"),
459 XmNset, GetPrefSearch() == SEARCH_REGEX_NOCASE
460 || GetPrefSearch() == SEARCH_REGEX,
461 XmNtopAttachment, XmATTACH_FORM,
462 XmNbottomAttachment, XmATTACH_FORM,
463 XmNtopOffset, 1, /* see openmotif note above */
464 XmNrightAttachment, XmATTACH_WIDGET,
465 XmNrightWidget, window->iSearchCaseToggle,
466 XmNmarginHeight, 0,
467 XmNtraversalOn, False,
468 NULL);
469 XmStringFree(s1);
471 window->iSearchRevToggle = XtVaCreateManagedWidget("iSearchRevToggle",
472 xmToggleButtonWidgetClass, window->iSearchForm,
473 XmNlabelString, s1=XmStringCreateSimple("Rev"),
474 XmNset, False,
475 XmNtopAttachment, XmATTACH_FORM,
476 XmNbottomAttachment, XmATTACH_FORM,
477 XmNtopOffset, 1, /* see openmotif note above */
478 XmNrightAttachment, XmATTACH_WIDGET,
479 XmNrightWidget, window->iSearchRegexToggle,
480 XmNmarginHeight, 0,
481 XmNtraversalOn, False,
482 NULL);
483 XmStringFree(s1);
485 window->iSearchText = XtVaCreateManagedWidget("iSearchText",
486 xmTextWidgetClass, window->iSearchForm,
487 XmNmarginHeight, 1,
488 XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
489 XmNleftAttachment, XmATTACH_WIDGET,
490 XmNleftWidget, iSearchLabel,
491 XmNrightAttachment, XmATTACH_WIDGET,
492 XmNrightWidget, window->iSearchRevToggle,
493 XmNrightOffset, 5,
494 XmNtopAttachment, XmATTACH_FORM,
495 XmNtopOffset, 0, /* see openmotif note above */
496 XmNbottomAttachment, XmATTACH_FORM,
497 XmNbottomOffset, 0, NULL);
498 RemapDeleteKey(window->iSearchText);
500 SetISearchTextCallbacks(window);
502 /* create the buffer tab bar */
503 tabForm = XtVaCreateWidget("tabForm",
504 xmFormWidgetClass, statsAreaForm,
505 XmNmarginHeight, 0,
506 XmNmarginWidth, 0,
507 XmNspacing, 0,
508 XmNresizable, False,
509 XmNleftAttachment, XmATTACH_FORM,
510 XmNrightAttachment, XmATTACH_FORM,
511 XmNshadowThickness, 0, NULL);
513 /* button to close top buffer */
514 closeTabBtn = XtVaCreateManagedWidget("closeTabBtn",
515 xmPushButtonWidgetClass, tabForm,
516 XmNmarginHeight, 0,
517 XmNmarginWidth, 0,
518 XmNhighlightThickness, 0,
519 XmNlabelType, XmPIXMAP,
520 XmNshadowThickness, 1,
521 XmNtraversalOn, False,
522 XmNrightAttachment, XmATTACH_FORM,
523 XmNrightOffset, 5,
524 XmNbottomAttachment, XmATTACH_FORM,
525 XmNbottomOffset, 3,
526 NULL);
528 XtAddCallback(closeTabBtn, XmNactivateCallback, (XtCallbackProc)closeTabCB,
529 mainWin);
530 setTabCloseButtonImage(closeTabBtn);
532 window->bufferTabBar = XtVaCreateManagedWidget("tabBar",
533 xmlFolderWidgetClass, tabForm,
534 XmNresizePolicy, XmRESIZE_PACK,
535 XmNleftAttachment, XmATTACH_FORM,
536 XmNleftOffset, 1,
537 XmNrightAttachment, XmATTACH_WIDGET,
538 XmNrightWidget, closeTabBtn,
539 XmNrightOffset, 10,
540 XmNbottomAttachment, XmATTACH_FORM,
541 XmNbottomOffset, 0,
542 XmNtopAttachment, XmATTACH_FORM,
543 NULL);
545 /* create an unmanaged composite widget to get the folder
546 widget to hide the 3D shadow for the manager area.
547 Note: this works only on the patched XmLFolder widget */
548 form = XtVaCreateWidget("form",
549 xmFormWidgetClass, window->bufferTabBar,
550 XmNheight, 1,
551 XmNresizable, False,
552 NULL);
554 XtAddCallback(window->bufferTabBar, XmNactivateCallback,
555 (XtCallbackProc)raiseTabCB, NULL);
557 window->bufferTab = addBufferTab(window->bufferTabBar, window, name);
559 /* set minimum space for filename on tabs */
560 XtVaGetValues(window->bufferTab, XmNfontList, &fontList, NULL);
561 fs = GetDefaultFontStruct(fontList);
562 fontWidth = (fs->min_bounds.width + fs->max_bounds.width)/2;
563 tabWidth = fontWidth * 20 + 5;
564 if (tabWidth < 150)
565 tabWidth = 150;
566 XtVaSetValues(window->bufferTabBar, XmNmaxTabWidth, tabWidth, NULL);
568 if (window->showTabBar)
569 XtManageChild(tabForm);
571 /* put a separating line below the tab bar */
572 XtVaCreateManagedWidget("TOOLBAR_SEP", xmSeparatorWidgetClass,
573 statsAreaForm,
574 XmNseparatorType, XmSHADOW_ETCHED_IN,
575 XmNmargin, 0,
576 XmNleftAttachment, XmATTACH_FORM,
577 XmNrightAttachment, XmATTACH_FORM,
578 NULL);
580 /* A form to hold the stats line text and line/col widgets */
581 window->statsLineForm = XtVaCreateWidget("statsLineForm",
582 xmFormWidgetClass, statsAreaForm,
583 XmNshadowThickness, 0,
584 XmNtopAttachment, window->showISearchLine ?
585 XmATTACH_WIDGET : XmATTACH_FORM,
586 XmNtopWidget, window->iSearchForm,
587 XmNrightAttachment, XmATTACH_FORM,
588 XmNleftAttachment, XmATTACH_FORM,
589 XmNbottomAttachment, XmATTACH_FORM,
590 XmNresizable, False, /* */
591 NULL);
593 /* A separate display of the line/column number */
594 window->statsLineColNo = XtVaCreateManagedWidget("statsLineColNo",
595 xmLabelWidgetClass, window->statsLineForm,
596 XmNlabelString, s1=XmStringCreateSimple("L: --- C: ---"),
597 XmNshadowThickness, 0,
598 XmNmarginHeight, 2,
599 XmNtraversalOn, False,
600 XmNtopAttachment, XmATTACH_FORM,
601 XmNrightAttachment, XmATTACH_FORM,
602 XmNbottomAttachment, XmATTACH_FORM, /* */
603 NULL);
604 XmStringFree(s1);
606 /* Create file statistics display area. Using a text widget rather than
607 a label solves a layout problem with the main window, which messes up
608 if the label is too long (we would need a resize callback to control
609 the length when the window changed size), and allows users to select
610 file names and line numbers. Colors are copied from parent
611 widget, because many users and some system defaults color text
612 backgrounds differently from other widgets. */
613 XtVaGetValues(statsAreaForm, XmNbackground, &bgpix, NULL);
614 XtVaGetValues(statsAreaForm, XmNforeground, &fgpix, NULL);
615 stats = XtVaCreateManagedWidget("statsLine",
616 xmTextWidgetClass, window->statsLineForm,
617 XmNbackground, bgpix,
618 XmNforeground, fgpix,
619 XmNshadowThickness, 0,
620 XmNhighlightColor, bgpix,
621 XmNhighlightThickness, 0, /* must be zero, for OM (2.1.30) to
622 aligns tatsLineColNo & statsLine */
623 XmNmarginHeight, 1, /* == statsLineColNo.marginHeight - 1,
624 to align with statsLineColNo */
625 XmNscrollHorizontal, False,
626 XmNeditMode, XmSINGLE_LINE_EDIT,
627 XmNeditable, False,
628 XmNtraversalOn, False,
629 XmNcursorPositionVisible, False,
630 XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, /* */
631 XmNtopWidget, window->statsLineColNo,
632 XmNleftAttachment, XmATTACH_FORM,
633 XmNrightAttachment, XmATTACH_WIDGET,
634 XmNrightWidget, window->statsLineColNo,
635 XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, /* */
636 XmNbottomWidget, window->statsLineColNo,
637 XmNrightOffset, 3,
638 NULL);
639 window->statsLine = stats;
641 /* Manage the statsLineForm */
642 if(window->showStats)
643 XtManageChild(window->statsLineForm);
645 /* If the fontList was NULL, use the magical default provided by Motif,
646 since it must have worked if we've gotten this far */
647 if (window->fontList == NULL)
648 XtVaGetValues(stats, XmNfontList, &window->fontList, NULL);
650 manageToolBars(statsAreaForm);
652 /* Create the menu bar */
653 menuBar = CreateMenuBar(mainWin, window);
654 window->menuBar = menuBar;
655 XtManageChild(menuBar);
657 /* Create paned window to manage split window behavior */
658 pane = XtVaCreateManagedWidget("pane", xmPanedWindowWidgetClass, mainWin,
659 XmNseparatorOn, False,
660 XmNspacing, 3, XmNsashIndent, -2, NULL);
661 window->splitPane = pane;
662 XmMainWindowSetAreas(mainWin, menuBar, statsAreaForm, NULL, NULL, pane);
664 /* buffer/window info should associate with text pane */
665 XtVaSetValues(pane, XmNuserData, window, NULL);
667 /* Patch around Motif's most idiotic "feature", that its menu accelerators
668 recognize Caps Lock and Num Lock as modifiers, and don't trigger if
669 they are engaged */
670 AccelLockBugPatch(pane, window->menuBar);
672 /* Create the first, and most permanent text area (other panes may
673 be added & removed, but this one will never be removed */
674 text = createTextArea(pane, window, rows,cols,
675 GetPrefEmTabDist(PLAIN_LANGUAGE_MODE), GetPrefDelimiters(),
676 GetPrefWrapMargin(), window->showLineNumbers?MIN_LINE_NUM_COLS:0);
677 XtManageChild(text);
678 window->textArea = text;
679 window->lastFocus = text;
681 /* Set the initial colors from the globals. */
682 SetColors(window,
683 GetPrefColorName(TEXT_FG_COLOR ),
684 GetPrefColorName(TEXT_BG_COLOR ),
685 GetPrefColorName(SELECT_FG_COLOR),
686 GetPrefColorName(SELECT_BG_COLOR),
687 GetPrefColorName(HILITE_FG_COLOR),
688 GetPrefColorName(HILITE_BG_COLOR),
689 GetPrefColorName(LINENO_FG_COLOR),
690 GetPrefColorName(CURSOR_FG_COLOR));
692 /* Create the right button popup menu (note: order is important here,
693 since the translation for popping up this menu was probably already
694 added in createTextArea, but CreateBGMenu requires window->textArea
695 to be set so it can attach the menu to it (because menu shells are
696 finicky about the kinds of widgets they are attached to)) */
697 window->bgMenuPane = CreateBGMenu(window);
699 /* Create the text buffer rather than using the one created automatically
700 with the text area widget. This is done so the syntax highlighting
701 modify callback can be called to synchronize the style buffer BEFORE
702 the text display's callback is called upon to display a modification */
703 window->buffer = BufCreate();
704 BufAddModifyCB(window->buffer, SyntaxHighlightModifyCB, window);
706 /* Attach the buffer to the text widget, and add callbacks for modify */
707 TextSetBuffer(text, window->buffer);
708 BufAddModifyCB(window->buffer, modifiedCB, window);
710 /* Designate the permanent text area as the owner for selections */
711 HandleXSelections(text);
713 /* Set the requested hardware tab distance and useTabs in the text buffer */
714 BufSetTabDistance(window->buffer, GetPrefTabDist(PLAIN_LANGUAGE_MODE));
715 window->buffer->useTabs = GetPrefInsertTabs();
717 /* add the window to the global window list, update the Windows menus */
718 addToWindowList(window);
719 InvalidateWindowMenus();
721 /* realize all of the widgets in the new window */
722 RealizeWithoutForcingPosition(winShell);
723 XmProcessTraversal(text, XmTRAVERSE_CURRENT);
725 /* Make close command in window menu gracefully prompt for close */
726 AddMotifCloseCallback(winShell, (XtCallbackProc)closeCB, window);
728 #ifndef NO_SESSION_RESTART
729 /* Add wm protocol callback for making nedit restartable by session
730 managers. Doesn't yet handle multiple-desktops or iconifying right. */
731 if (syAtom == 0) {
732 wmpAtom = XmInternAtom(TheDisplay, "WM_PROTOCOLS", FALSE);
733 syAtom = XmInternAtom(TheDisplay, "WM_SAVE_YOURSELF", FALSE);
735 XmAddProtocolCallback(winShell, wmpAtom, syAtom,
736 (XtCallbackProc)saveYourselfCB, (XtPointer)window);
737 #endif
739 /* Make window resizing work in nice character heights */
740 UpdateWMSizeHints(window);
742 /* Set the minimum pane height for the initial text pane */
743 UpdateMinPaneHeights(window);
745 restoreInsaneVirtualKeyBindings(invalidBindings);
747 /* create persistant dialog upfront, shared by all buffers
748 in a common shell window */
749 CreateFindDlog(window->shell, window);
750 CreateReplaceDlog(window->shell, window);
751 CreateReplaceMultiFileDlog(window);
753 /* tell the world there's a new window to move in */
754 if (GetPrefBufferMode()) {
755 for(win=WindowList; win; win=win->next) {
756 if (IsTopBuffer(win))
757 XtSetSensitive(win->attachBufferItem,
758 NBuffers(window) < NWindows());
762 return window;
766 ** add a new tab to the tab bar, where the [new] buffer belongs.
768 static Widget addBufferTab(Widget folder, WindowInfo *window, const char *string)
770 Widget tooltipLabel, tab;
771 XmString s1;
773 s1 = XmStringCreateSimple((char *)string);
774 tab = XtVaCreateManagedWidget("tab",
775 xrwsBubbleButtonWidgetClass, folder,
776 XmNmarginWidth, 0,
777 XmNmarginHeight, 0,
778 XmNalignment, XmALIGNMENT_BEGINNING,
779 XmNlabelString, s1,
780 XltNbubbleString, s1,
781 XltNshowBubble, GetPrefToolTips(),
782 XltNautoParkBubble, True,
783 XltNslidingBubble, False,
784 /* XltNdelay, 800,*/
785 /* XltNbubbleDuration, 8000,*/
786 NULL);
787 XmStringFree(s1);
789 XtAddCallback(tab, XmNactivateCallback,
790 (XtCallbackProc)clickTabCB, NULL);
792 /* BubbleButton simply use reversed video for tooltips,
793 we try to use the 'standard' color */
794 tooltipLabel = XtNameToWidget(tab, "*BubbleLabel");
795 XtVaSetValues(tooltipLabel,
796 XmNbackground, AllocateColor(tab, GetPrefTooltipBgColor()),
797 XmNforeground, AllocateColor(tab, NEDIT_DEFAULT_FG),
798 NULL);
800 /* put borders around tooltip. BubbleButton use
801 transientShellWidgetClass as tooltip shell, which
802 came without borders */
803 XtVaSetValues(XtParent(tooltipLabel), XmNborderWidth, 1, NULL);
805 return tab;
809 ** return the tab next to the closing tab.
811 ** tab close left to right (Mozilla 1.1 behavior), i.e
812 ** the 'right-hand' tab get the new seat, unless the
813 ** closing buffer is the right-most tab
815 static WindowInfo *replacementBuffer(WindowInfo *window)
817 int n, tabPos, nextPos;
818 WindowInfo **winList, *win;
819 int nBuf = NBuffers(window);
821 if (nBuf <= 1)
822 return NULL;
824 winList = (WindowInfo **)XtMalloc(sizeof(WindowInfo *) * nBuf);
825 n = nBuf -1;
826 for (win=WindowList; win && n>=0; win=win->next) {
827 if (win->shell == window->shell)
828 winList[n--] = win;
831 for (n=0; n<nBuf; n++) {
832 if (winList[n] == window) {
833 tabPos = n;
834 break;
838 nextPos = tabPos == nBuf-1? tabPos-1 : tabPos+1;
839 win = winList[nextPos];
840 XtFree((char*) winList);
841 return win;
845 * find which buffer/window a tab belongs to
847 WindowInfo *TabToWindow(Widget tab)
849 WindowInfo *win;
850 for (win=WindowList; win; win=win->next) {
851 if (win->bufferTab == tab)
852 return win;
855 return NULL;
859 ** Close an editor window
861 void CloseWindow(WindowInfo *window)
863 int keepWindow;
864 char name[MAXPATHLEN];
865 WindowInfo *win, *topBuf = NULL, *nextBuf = NULL;
867 /* Free smart indent macro programs */
868 EndSmartIndent(window);
870 /* Clean up macro references to the doomed window. If a macro is
871 executing, stop it. If macro is calling this (closing its own
872 window), leave the window alive until the macro completes */
873 keepWindow = !MacroWindowCloseActions(window);
875 #ifndef VMS
876 /* Kill shell sub-process and free related memory */
877 AbortShellCommand(window);
878 #endif /*VMS*/
880 /* Unload the default tips files for this language mode if necessary */
881 UnloadLanguageModeTipsFile(window);
883 /* If a window is closed while it is on the multi-file replace dialog
884 list of any other window (or even the same one), we must update those
885 lists or we end up with dangling references. Normally, there can
886 be only one of those dialogs at the same time (application modal),
887 but LessTif doesn't even (always) honor application modalness, so
888 there can be more than one dialog. */
889 RemoveFromMultiReplaceDialog(window);
891 /* Destroy the file closed property for this file */
892 DeleteFileClosedProperty(window);
894 /* if this is the last window, or must be kept alive temporarily because
895 it's running the macro calling us, don't close it, make it Untitled */
896 if (keepWindow || (WindowList == window && window->next == NULL)) {
897 window->filename[0] = '\0';
898 UniqueUntitledName(name);
899 CLEAR_ALL_LOCKS(window->lockReasons);
900 window->fileMode = 0;
901 strcpy(window->filename, name);
902 strcpy(window->path, "");
903 window->ignoreModify = TRUE;
904 BufSetAll(window->buffer, "");
905 window->ignoreModify = FALSE;
906 window->nMarks = 0;
907 window->filenameSet = FALSE;
908 window->fileMissing = TRUE;
909 window->fileChanged = FALSE;
910 window->fileFormat = UNIX_FILE_FORMAT;
911 window->lastModTime = 0;
912 StopHighlighting(window);
913 EndSmartIndent(window);
914 UpdateWindowTitle(window);
915 UpdateWindowReadOnly(window);
916 XtSetSensitive(window->closeItem, FALSE);
917 XtSetSensitive(window->readOnlyItem, TRUE);
918 XmToggleButtonSetState(window->readOnlyItem, FALSE, FALSE);
919 ClearUndoList(window);
920 ClearRedoList(window);
921 XmTextSetString(window->statsLine, ""); /* resets scroll pos of stats
922 line from long file names */
923 UpdateStatsLine(window);
924 DetermineLanguageMode(window, True);
925 RefreshTabState(window);
926 return;
929 /* Free syntax highlighting patterns, if any. w/o redisplaying */
930 FreeHighlightingData(window);
932 /* remove the buffer modification callbacks so the buffer will be
933 deallocated when the last text widget is destroyed */
934 BufRemoveModifyCB(window->buffer, modifiedCB, window);
935 BufRemoveModifyCB(window->buffer, SyntaxHighlightModifyCB, window);
937 #ifdef ROWCOLPATCH
938 patchRowCol(window->menuBar);
939 #endif
941 /* free the undo and redo lists */
942 ClearUndoList(window);
943 ClearRedoList(window);
945 /* close window, or buffer */
946 if (NBuffers(window) > 1) {
947 if (MacroRunWindow() && MacroRunWindow() != window &&
948 MacroRunWindow()->shell == window->shell) {
949 nextBuf = MacroRunWindow();
951 else if (IsTopBuffer(window)) {
952 /* if this is the active buffer, then we need
953 to find its successor */
954 nextBuf = replacementBuffer(window);
956 else
957 topBuf = GetTopBuffer(window->shell);
960 /* remove the window from the global window list, update window menus */
961 removeFromWindowList(window);
962 InvalidateWindowMenus();
964 /* remove tab from tab bar */
965 XtDestroyWidget(window->bufferTab);
967 if (nextBuf) {
968 /* show the replacement buffer */
969 RaiseBuffer(nextBuf);
970 ShowWindowTabBar(nextBuf);
972 else if (topBuf) {
973 /* refresh tabbar after deleting a non-top buffer */
974 ShowWindowTabBar(topBuf);
977 /* tell the world there's one less window to move in */
978 if (GetPrefBufferMode()) {
979 for(win=WindowList; win; win=win->next) {
980 if (IsTopBuffer(win))
981 XtSetSensitive(win->attachBufferItem,
982 NBuffers(WindowList) < NWindows());
986 /* destroy the buffer pane, or window */
987 if (nextBuf || topBuf) {
988 DeleteBuffer(window);
990 else {
991 /* remove and deallocate all of the widgets associated with window */
992 XtFree(window->backlightCharTypes); /* we made a copy earlier on */
993 closeAllPopupsFor(window->shell);
994 XtDestroyWidget(window->shell);
997 /* deallocate the window data structure */
998 XtFree((char*)window);
1001 void ShowWindowTabBar(WindowInfo *window)
1003 if (GetPrefTabBar()) {
1004 if (GetPrefTabBarHideOne())
1005 ShowBufferTabBar(window, NBuffers(window)>1);
1006 else
1007 ShowBufferTabBar(window, True);
1009 else
1010 ShowBufferTabBar(window, False);
1014 ** Check if there is already a window open for a given file
1016 WindowInfo *FindWindowWithFile(const char *name, const char *path)
1018 WindowInfo *w;
1020 for (w=WindowList; w!=NULL; w=w->next) {
1021 if (!strcmp(w->filename, name) && !strcmp(w->path, path)) {
1022 return w;
1025 return NULL;
1029 ** Add another independently scrollable window pane to the current window,
1030 ** splitting the pane which currently has keyboard focus.
1032 void SplitWindow(WindowInfo *window)
1034 short paneHeights[MAX_PANES+1];
1035 int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1];
1036 int horizOffsets[MAX_PANES+1];
1037 int i, focusPane, emTabDist, wrapMargin, lineNumCols, totalHeight=0;
1038 char *delimiters;
1039 Widget text;
1040 textDisp *textD, *newTextD;
1042 /* Don't create new panes if we're already at the limit */
1043 if (window->nPanes >= MAX_PANES)
1044 return;
1046 /* Record the current heights, scroll positions, and insert positions
1047 of the existing panes, keyboard focus */
1048 focusPane = 0;
1049 for (i=0; i<=window->nPanes; i++) {
1050 text = i==0 ? window->textArea : window->textPanes[i-1];
1051 insertPositions[i] = TextGetCursorPos(text);
1052 XtVaGetValues(containingPane(text),XmNheight,&paneHeights[i],NULL);
1053 totalHeight += paneHeights[i];
1054 TextGetScroll(text, &topLines[i], &horizOffsets[i]);
1055 if (text == window->lastFocus)
1056 focusPane = i;
1059 /* Unmanage & remanage the panedWindow so it recalculates pane heights */
1060 XtUnmanageChild(window->splitPane);
1062 /* Create a text widget to add to the pane and set its buffer and
1063 highlight data to be the same as the other panes in the window */
1064 XtVaGetValues(window->textArea, textNemulateTabs, &emTabDist,
1065 textNwordDelimiters, &delimiters, textNwrapMargin, &wrapMargin,
1066 textNlineNumCols, &lineNumCols, NULL);
1067 text = createTextArea(window->splitPane, window, 1, 1, emTabDist,
1068 delimiters, wrapMargin, lineNumCols);
1069 TextSetBuffer(text, window->buffer);
1070 if (window->highlightData != NULL)
1071 AttachHighlightToWidget(text, window);
1072 if (window->backlightChars)
1074 XtVaSetValues(text, textNbacklightCharTypes,
1075 window->backlightCharTypes, 0);
1077 XtManageChild(text);
1078 window->textPanes[window->nPanes++] = text;
1080 /* Fix up the colors */
1081 textD = ((TextWidget)window->textArea)->text.textD;
1082 newTextD = ((TextWidget)text)->text.textD;
1083 XtVaSetValues(text,
1084 XmNforeground, textD->fgPixel,
1085 XmNbackground, textD->bgPixel,
1086 NULL);
1087 TextDSetColors( newTextD, textD->fgPixel, textD->bgPixel,
1088 textD->selectFGPixel, textD->selectBGPixel, textD->highlightFGPixel,
1089 textD->highlightBGPixel, textD->lineNumFGPixel,
1090 textD->cursorFGPixel );
1092 /* Set the minimum pane height in the new pane */
1093 UpdateMinPaneHeights(window);
1095 /* adjust the heights, scroll positions, etc., to split the focus pane */
1096 for (i=window->nPanes; i>focusPane; i--) {
1097 insertPositions[i] = insertPositions[i-1];
1098 paneHeights[i] = paneHeights[i-1];
1099 topLines[i] = topLines[i-1];
1100 horizOffsets[i] = horizOffsets[i-1];
1102 paneHeights[focusPane] = paneHeights[focusPane]/2;
1103 paneHeights[focusPane+1] = paneHeights[focusPane];
1105 for (i=0; i<=window->nPanes; i++) {
1106 text = i==0 ? window->textArea : window->textPanes[i-1];
1107 setPaneDesiredHeight(containingPane(text), paneHeights[i]);
1110 /* Re-manage panedWindow to recalculate pane heights & reset selection */
1111 if (IsTopBuffer(window))
1112 XtManageChild(window->splitPane);
1114 /* Reset all of the heights, scroll positions, etc. */
1115 for (i=0; i<=window->nPanes; i++) {
1116 text = i==0 ? window->textArea : window->textPanes[i-1];
1117 TextSetCursorPos(text, insertPositions[i]);
1118 TextSetScroll(text, topLines[i], horizOffsets[i]);
1119 setPaneDesiredHeight(containingPane(text),
1120 totalHeight/(window->nPanes+1));
1122 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
1124 /* Update the window manager size hints after the sizes of the panes have
1125 been set (the widget heights are not yet readable here, but they will
1126 be by the time the event loop gets around to running this timer proc) */
1127 XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0,
1128 wmSizeUpdateProc, window);
1131 Widget GetPaneByIndex(WindowInfo *window, int paneIndex)
1133 Widget text = NULL;
1134 if (paneIndex >= 0 && paneIndex <= window->nPanes) {
1135 text = (paneIndex == 0) ? window->textArea : window->textPanes[paneIndex - 1];
1137 return(text);
1140 int WidgetToPaneIndex(WindowInfo *window, Widget w)
1142 int i;
1143 Widget text;
1144 int paneIndex = 0;
1146 for (i = 0; i <= window->nPanes; ++i) {
1147 text = (i == 0) ? window->textArea : window->textPanes[i - 1];
1148 if (text == w) {
1149 paneIndex = i;
1152 return(paneIndex);
1156 ** Close the window pane that last had the keyboard focus. (Actually, close
1157 ** the bottom pane and make it look like pane which had focus was closed)
1159 void ClosePane(WindowInfo *window)
1161 short paneHeights[MAX_PANES+1];
1162 int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1];
1163 int horizOffsets[MAX_PANES+1];
1164 int i, focusPane,totalHeight=0;
1165 Widget text;
1167 /* Don't delete the last pane */
1168 if (window->nPanes <= 0)
1169 return;
1171 /* Record the current heights, scroll positions, and insert positions
1172 of the existing panes, and the keyboard focus */
1173 focusPane = 0;
1174 for (i=0; i<=window->nPanes; i++) {
1175 text = i==0 ? window->textArea : window->textPanes[i-1];
1176 insertPositions[i] = TextGetCursorPos(text);
1177 XtVaGetValues(containingPane(text),
1178 XmNheight, &paneHeights[i], NULL);
1179 totalHeight += paneHeights[i];
1180 TextGetScroll(text, &topLines[i], &horizOffsets[i]);
1181 if (text == window->lastFocus)
1182 focusPane = i;
1185 /* Unmanage & remanage the panedWindow so it recalculates pane heights */
1186 XtUnmanageChild(window->splitPane);
1188 /* Destroy last pane, and make sure lastFocus points to an existing pane.
1189 Workaround for OM 2.1.30: text widget must be unmanaged for
1190 xmPanedWindowWidget to calculate the correct pane heights for
1191 the remaining panes, simply detroying it didn't seem enough */
1192 window->nPanes--;
1193 XtUnmanageChild(containingPane(window->textPanes[window->nPanes]));
1194 XtDestroyWidget(containingPane(window->textPanes[window->nPanes]));
1196 if (window->nPanes == 0)
1197 window->lastFocus = window->textArea;
1198 else if (focusPane > window->nPanes)
1199 window->lastFocus = window->textPanes[window->nPanes-1];
1201 /* adjust the heights, scroll positions, etc., to make it look
1202 like the pane with the input focus was closed */
1203 for (i=focusPane; i<=window->nPanes; i++) {
1204 insertPositions[i] = insertPositions[i+1];
1205 paneHeights[i] = paneHeights[i+1];
1206 topLines[i] = topLines[i+1];
1207 horizOffsets[i] = horizOffsets[i+1];
1210 /* set the desired heights and re-manage the paned window so it will
1211 recalculate pane heights */
1212 for (i=0; i<=window->nPanes; i++) {
1213 text = i==0 ? window->textArea : window->textPanes[i-1];
1214 setPaneDesiredHeight(containingPane(text), paneHeights[i]);
1217 if (IsTopBuffer(window))
1218 XtManageChild(window->splitPane);
1220 /* Reset all of the scroll positions, insert positions, etc. */
1221 for (i=0; i<=window->nPanes; i++) {
1222 text = i==0 ? window->textArea : window->textPanes[i-1];
1223 TextSetCursorPos(text, insertPositions[i]);
1224 TextSetScroll(text, topLines[i], horizOffsets[i]);
1226 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
1228 /* Update the window manager size hints after the sizes of the panes have
1229 been set (the widget heights are not yet readable here, but they will
1230 be by the time the event loop gets around to running this timer proc) */
1231 XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0,
1232 wmSizeUpdateProc, window);
1236 ** Turn on and off the display of line numbers
1238 void ShowLineNumbers(WindowInfo *window, int state)
1240 Widget text;
1241 int i, marginWidth;
1242 Dimension windowWidth;
1243 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
1245 if (window->showLineNumbers == state)
1246 return;
1247 window->showLineNumbers = state;
1249 /* Just setting window->showLineNumbers is sufficient to tell
1250 UpdateLineNumDisp to expand the line number areas and the window
1251 size for the number of lines required. To hide the line number
1252 display, set the width to zero, and contract the window width. */
1253 if (state) {
1254 UpdateLineNumDisp(window);
1255 } else {
1256 XtVaGetValues(window->shell, XmNwidth, &windowWidth, NULL);
1257 XtVaGetValues(window->textArea, textNmarginWidth, &marginWidth, NULL);
1258 XtVaSetValues(window->shell, XmNwidth,
1259 windowWidth - textD->left + marginWidth, NULL);
1260 for (i=0; i<=window->nPanes; i++) {
1261 text = i==0 ? window->textArea : window->textPanes[i-1];
1262 XtVaSetValues(text, textNlineNumCols, 0, NULL);
1266 /* Tell WM that the non-expandable part of the window has changed size */
1267 UpdateWMSizeHints(window);
1270 void SetTabDist(WindowInfo *window, int tabDist)
1272 if (window->buffer->tabDist != tabDist) {
1273 int saveCursorPositions[MAX_PANES + 1];
1274 int saveVScrollPositions[MAX_PANES + 1];
1275 int saveHScrollPositions[MAX_PANES + 1];
1276 int paneIndex;
1278 window->ignoreModify = True;
1280 for (paneIndex = 0; paneIndex <= window->nPanes; ++paneIndex) {
1281 Widget w = GetPaneByIndex(window, paneIndex);
1282 textDisp *textD = ((TextWidget)w)->text.textD;
1284 TextGetScroll(w, &saveVScrollPositions[paneIndex], &saveHScrollPositions[paneIndex]);
1285 saveCursorPositions[paneIndex] = TextGetCursorPos(w);
1286 textD->modifyingTabDist = 1;
1289 BufSetTabDistance(window->buffer, tabDist);
1291 for (paneIndex = 0; paneIndex <= window->nPanes; ++paneIndex) {
1292 Widget w = GetPaneByIndex(window, paneIndex);
1293 textDisp *textD = ((TextWidget)w)->text.textD;
1295 textD->modifyingTabDist = 0;
1296 TextSetCursorPos(w, saveCursorPositions[paneIndex]);
1297 TextSetScroll(w, saveVScrollPositions[paneIndex], saveHScrollPositions[paneIndex]);
1300 window->ignoreModify = False;
1304 void SetEmTabDist(WindowInfo *window, int emTabDist)
1306 int i;
1308 XtVaSetValues(window->textArea, textNemulateTabs, emTabDist, NULL);
1309 for (i = 0; i < window->nPanes; ++i) {
1310 XtVaSetValues(window->textPanes[i], textNemulateTabs, emTabDist, NULL);
1315 ** Turn on and off the display of the statistics line
1317 void ShowStatsLine(WindowInfo *window, int state)
1319 Widget text;
1320 int i;
1322 /* In continuous wrap mode, text widgets must be told to keep track of
1323 the top line number in absolute (non-wrapped) lines, because it can
1324 be a costly calculation, and is only needed for displaying line
1325 numbers, either in the stats line, or along the left margin */
1326 for (i=0; i<=window->nPanes; i++) {
1327 text = i==0 ? window->textArea : window->textPanes[i-1];
1328 TextDMaintainAbsLineNum(((TextWidget)text)->text.textD, state);
1330 window->showStats = state;
1331 showStats(window, state);
1335 ** Displays and undisplays the statistics line (regardless of settings of
1336 ** window->showStats or window->modeMessageDisplayed)
1338 static void showStats(WindowInfo *window, int state)
1340 if (state) {
1341 XtManageChild(window->statsLineForm);
1342 showStatsForm(window, True);
1343 } else {
1344 XtUnmanageChild(window->statsLineForm);
1345 showStatsForm(window, window->showISearchLine);
1348 /* Tell WM that the non-expandable part of the window has changed size */
1349 /* Already done in showStatsForm */
1350 /* UpdateWMSizeHints(window); */
1355 static void showTabBar(WindowInfo *window, int state)
1357 if (state) {
1358 XtManageChild(XtParent(window->bufferTabBar));
1359 showStatsForm(window, True);
1360 } else {
1361 XtUnmanageChild(XtParent(window->bufferTabBar));
1362 showStatsForm(window, False);
1368 void ShowBufferTabBar(WindowInfo *window, int state)
1370 if (XtIsManaged(XtParent(window->bufferTabBar)) == state)
1371 return;
1372 window->showTabBar = state;
1373 showTabBar(window, state);
1377 ** Turn on and off the continuing display of the incremental search line
1378 ** (when off, it is popped up and down as needed via TempShowISearch)
1380 void ShowISearchLine(WindowInfo *window, int state)
1382 if (window->showISearchLine == state)
1383 return;
1384 window->showISearchLine = state;
1385 showISearch(window, state);
1389 ** Temporarily show and hide the incremental search line if the line is not
1390 ** already up.
1392 void TempShowISearch(WindowInfo *window, int state)
1394 if (window->showISearchLine)
1395 return;
1396 if (XtIsManaged(window->iSearchForm) != state)
1397 showISearch(window, state);
1401 ** Put up or pop-down the incremental search line regardless of settings
1402 ** of showISearchLine or TempShowISearch
1404 static void showISearch(WindowInfo *window, int state)
1406 if (state) {
1407 XtManageChild(window->iSearchForm);
1408 showStatsForm(window, True);
1409 } else {
1410 XtUnmanageChild(window->iSearchForm);
1411 showStatsForm(window, window->showStats ||
1412 window->modeMessageDisplayed);
1415 /* Tell WM that the non-expandable part of the window has changed size */
1416 /* This is already done in showStatsForm */
1417 /* UpdateWMSizeHints(window); */
1421 ** Show or hide the extra display area under the main menu bar which
1422 ** optionally contains the status line and the incremental search bar
1424 static void showStatsForm(WindowInfo *window, int state)
1426 Widget statsAreaForm = XtParent(window->statsLineForm);
1427 Widget mainW = XtParent(statsAreaForm);
1429 /* The very silly use of XmNcommandWindowLocation and XmNshowSeparator
1430 below are to kick the main window widget to position and remove the
1431 status line when it is managed and unmanaged. At some Motif version
1432 level, the showSeparator trick backfires and leaves the separator
1433 shown, but fortunately the dynamic behavior is fixed, too so the
1434 workaround is no longer necessary, either. (... the version where
1435 this occurs may be earlier than 2.1. If the stats line shows
1436 double thickness shadows in earlier Motif versions, the #if XmVersion
1437 directive should be moved back to that earlier version) */
1438 if (manageToolBars(statsAreaForm)) {
1439 XtUnmanageChild(statsAreaForm); /*... will this fix Solaris 7??? */
1440 XtVaSetValues(mainW, XmNcommandWindowLocation,
1441 XmCOMMAND_ABOVE_WORKSPACE, NULL);
1442 #if XmVersion < 2001
1443 XtVaSetValues(mainW, XmNshowSeparator, True, NULL);
1444 #endif
1445 XtManageChild(statsAreaForm);
1446 XtVaSetValues(mainW, XmNshowSeparator, False, NULL);
1447 UpdateStatsLine(window);
1448 } else {
1449 XtUnmanageChild(statsAreaForm);
1450 XtVaSetValues(mainW, XmNcommandWindowLocation,
1451 XmCOMMAND_BELOW_WORKSPACE, NULL);
1454 /* Tell WM that the non-expandable part of the window has changed size */
1455 UpdateWMSizeHints(window);
1459 ** Display a special message in the stats line (show the stats line if it
1460 ** is not currently shown).
1462 void SetModeMessage(WindowInfo *window, const char *message)
1464 window->modeMessageDisplayed = True;
1466 if (!IsTopBuffer(window))
1467 return;
1469 XmTextSetString(window->statsLine, (char*)message);
1471 * Don't invoke the stats line again, if stats line is already displayed.
1473 if (!window->showStats)
1474 showStats(window, True);
1478 ** Clear special statistics line message set in SetModeMessage, returns
1479 ** the statistics line to its original state as set in window->showStats
1481 void ClearModeMessage(WindowInfo *window)
1483 window->modeMessageDisplayed = False;
1485 if (!IsTopBuffer(window))
1486 return;
1489 * Remove the stats line only if indicated by it's window state.
1491 if (!window->showStats)
1492 showStats(window, False);
1493 UpdateStatsLine(window);
1497 ** Count the windows
1499 int NWindows(void)
1501 WindowInfo *win;
1502 int n;
1504 for (win=WindowList, n=0; win!=NULL; win=win->next, n++);
1505 return n;
1509 ** Set autoindent state to one of NO_AUTO_INDENT, AUTO_INDENT, or SMART_INDENT.
1511 void SetAutoIndent(WindowInfo *window, int state)
1513 int autoIndent = state == AUTO_INDENT, smartIndent = state == SMART_INDENT;
1514 int i;
1516 if (window->indentStyle == SMART_INDENT && !smartIndent)
1517 EndSmartIndent(window);
1518 else if (smartIndent && window->indentStyle != SMART_INDENT)
1519 BeginSmartIndent(window, True);
1520 window->indentStyle = state;
1521 XtVaSetValues(window->textArea, textNautoIndent, autoIndent,
1522 textNsmartIndent, smartIndent, NULL);
1523 for (i=0; i<window->nPanes; i++)
1524 XtVaSetValues(window->textPanes[i], textNautoIndent, autoIndent,
1525 textNsmartIndent, smartIndent, NULL);
1526 XmToggleButtonSetState(window->smartIndentItem, smartIndent, False);
1527 XmToggleButtonSetState(window->autoIndentItem, autoIndent, False);
1528 XmToggleButtonSetState(window->autoIndentOffItem, state == NO_AUTO_INDENT,
1529 False);
1533 ** Set showMatching state to one of NO_FLASH, FLASH_DELIMIT or FLASH_RANGE.
1534 ** Update the menu to reflect the change of state.
1536 void SetShowMatching(WindowInfo *window, int state)
1538 window->showMatchingStyle = state;
1539 XmToggleButtonSetState(window->showMatchingOffItem,
1540 state == NO_FLASH, False);
1541 XmToggleButtonSetState(window->showMatchingDelimitItem,
1542 state == FLASH_DELIMIT, False);
1543 XmToggleButtonSetState(window->showMatchingRangeItem,
1544 state == FLASH_RANGE, False);
1548 ** Set the fonts for "window" from a font name, and updates the display.
1549 ** Also updates window->fontList which is used for statistics line.
1551 ** Note that this leaks memory and server resources. In previous NEdit
1552 ** versions, fontLists were carefully tracked and freed, but X and Motif
1553 ** have some kind of timing problem when widgets are distroyed, such that
1554 ** fonts may not be freed immediately after widget destruction with 100%
1555 ** safety. Rather than kludge around this with timerProcs, I have chosen
1556 ** to create new fontLists only when the user explicitly changes the font
1557 ** (which shouldn't happen much in normal NEdit operation), and skip the
1558 ** futile effort of freeing them.
1560 void SetFonts(WindowInfo *window, const char *fontName, const char *italicName,
1561 const char *boldName, const char *boldItalicName)
1563 XFontStruct *font, *oldFont;
1564 int i, oldFontWidth, oldFontHeight, fontWidth, fontHeight;
1565 int borderWidth, borderHeight, marginWidth, marginHeight;
1566 int primaryChanged, highlightChanged = False;
1567 Dimension oldWindowWidth, oldWindowHeight, oldTextWidth, oldTextHeight;
1568 Dimension textHeight, newWindowWidth, newWindowHeight;
1569 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
1571 /* Check which fonts have changed */
1572 primaryChanged = strcmp(fontName, window->fontName);
1573 if (strcmp(italicName, window->italicFontName)) highlightChanged = True;
1574 if (strcmp(boldName, window->boldFontName)) highlightChanged = True;
1575 if (strcmp(boldItalicName, window->boldItalicFontName))
1576 highlightChanged = True;
1577 if (!primaryChanged && !highlightChanged)
1578 return;
1580 /* Get information about the current window sizing, to be used to
1581 determine the correct window size after the font is changed */
1582 XtVaGetValues(window->shell, XmNwidth, &oldWindowWidth, XmNheight,
1583 &oldWindowHeight, NULL);
1584 XtVaGetValues(window->textArea, XmNheight, &textHeight,
1585 textNmarginHeight, &marginHeight, textNmarginWidth,
1586 &marginWidth, textNfont, &oldFont, NULL);
1587 oldTextWidth = textD->width + textD->lineNumWidth;
1588 oldTextHeight = textHeight - 2*marginHeight;
1589 for (i=0; i<window->nPanes; i++) {
1590 XtVaGetValues(window->textPanes[i], XmNheight, &textHeight, NULL);
1591 oldTextHeight += textHeight - 2*marginHeight;
1593 borderWidth = oldWindowWidth - oldTextWidth;
1594 borderHeight = oldWindowHeight - oldTextHeight;
1595 oldFontWidth = oldFont->max_bounds.width;
1596 oldFontHeight = textD->ascent + textD->descent;
1599 /* Change the fonts in the window data structure. If the primary font
1600 didn't work, use Motif's fallback mechanism by stealing it from the
1601 statistics line. Highlight fonts are allowed to be NULL, which
1602 is interpreted as "use the primary font" */
1603 if (primaryChanged) {
1604 strcpy(window->fontName, fontName);
1605 font = XLoadQueryFont(TheDisplay, fontName);
1606 if (font == NULL)
1607 XtVaGetValues(window->statsLine, XmNfontList, &window->fontList,
1608 NULL);
1609 else
1610 window->fontList = XmFontListCreate(font, XmSTRING_DEFAULT_CHARSET);
1612 if (highlightChanged) {
1613 strcpy(window->italicFontName, italicName);
1614 window->italicFontStruct = XLoadQueryFont(TheDisplay, italicName);
1615 strcpy(window->boldFontName, boldName);
1616 window->boldFontStruct = XLoadQueryFont(TheDisplay, boldName);
1617 strcpy(window->boldItalicFontName, boldItalicName);
1618 window->boldItalicFontStruct = XLoadQueryFont(TheDisplay, boldItalicName);
1621 /* Change the primary font in all the widgets */
1622 if (primaryChanged) {
1623 font = GetDefaultFontStruct(window->fontList);
1624 XtVaSetValues(window->textArea, textNfont, font, NULL);
1625 for (i=0; i<window->nPanes; i++)
1626 XtVaSetValues(window->textPanes[i], textNfont, font, NULL);
1629 /* Change the highlight fonts, even if they didn't change, because
1630 primary font is read through the style table for syntax highlighting */
1631 if (window->highlightData != NULL)
1632 UpdateHighlightStyles(window);
1634 /* Change the window manager size hints.
1635 Note: this has to be done _before_ we set the new sizes. ICCCM2
1636 compliant window managers (such as fvwm2) would otherwise resize
1637 the window twice: once because of the new sizes requested, and once
1638 because of the new size increments, resulting in an overshoot. */
1639 UpdateWMSizeHints(window);
1641 /* Use the information from the old window to re-size the window to a
1642 size appropriate for the new font */
1643 fontWidth = GetDefaultFontStruct(window->fontList)->max_bounds.width;
1644 fontHeight = textD->ascent + textD->descent;
1645 newWindowWidth = (oldTextWidth*fontWidth) / oldFontWidth + borderWidth;
1646 newWindowHeight = (oldTextHeight*fontHeight) / oldFontHeight + borderHeight;
1647 XtVaSetValues(window->shell, XmNwidth, newWindowWidth, XmNheight,
1648 newWindowHeight, NULL);
1650 /* Change the minimum pane height */
1651 UpdateMinPaneHeights(window);
1654 void SetColors(WindowInfo *window, const char *textFg, const char *textBg,
1655 const char *selectFg, const char *selectBg, const char *hiliteFg,
1656 const char *hiliteBg, const char *lineNoFg, const char *cursorFg)
1658 int i, dummy;
1659 Pixel textFgPix = AllocColor( window->textArea, textFg,
1660 &dummy, &dummy, &dummy),
1661 textBgPix = AllocColor( window->textArea, textBg,
1662 &dummy, &dummy, &dummy),
1663 selectFgPix = AllocColor( window->textArea, selectFg,
1664 &dummy, &dummy, &dummy),
1665 selectBgPix = AllocColor( window->textArea, selectBg,
1666 &dummy, &dummy, &dummy),
1667 hiliteFgPix = AllocColor( window->textArea, hiliteFg,
1668 &dummy, &dummy, &dummy),
1669 hiliteBgPix = AllocColor( window->textArea, hiliteBg,
1670 &dummy, &dummy, &dummy),
1671 lineNoFgPix = AllocColor( window->textArea, lineNoFg,
1672 &dummy, &dummy, &dummy),
1673 cursorFgPix = AllocColor( window->textArea, cursorFg,
1674 &dummy, &dummy, &dummy);
1675 textDisp *textD;
1677 /* Update the main pane */
1678 XtVaSetValues(window->textArea,
1679 XmNforeground, textFgPix,
1680 XmNbackground, textBgPix,
1681 NULL);
1682 textD = ((TextWidget)window->textArea)->text.textD;
1683 TextDSetColors( textD, textFgPix, textBgPix, selectFgPix, selectBgPix,
1684 hiliteFgPix, hiliteBgPix, lineNoFgPix, cursorFgPix );
1685 /* Update any additional panes */
1686 for (i=0; i<window->nPanes; i++) {
1687 XtVaSetValues(window->textPanes[i],
1688 XmNforeground, textFgPix,
1689 XmNbackground, textBgPix,
1690 NULL);
1691 textD = ((TextWidget)window->textPanes[i])->text.textD;
1692 TextDSetColors( textD, textFgPix, textBgPix, selectFgPix, selectBgPix,
1693 hiliteFgPix, hiliteBgPix, lineNoFgPix, cursorFgPix );
1696 /* Redo any syntax highlighting */
1697 if (window->highlightData != NULL)
1698 UpdateHighlightStyles(window);
1702 ** Set insert/overstrike mode
1704 void SetOverstrike(WindowInfo *window, int overstrike)
1706 int i;
1708 XtVaSetValues(window->textArea, textNoverstrike, overstrike, NULL);
1709 for (i=0; i<window->nPanes; i++)
1710 XtVaSetValues(window->textPanes[i], textNoverstrike, overstrike, NULL);
1711 window->overstrike = overstrike;
1715 ** Select auto-wrap mode, one of NO_WRAP, NEWLINE_WRAP, or CONTINUOUS_WRAP
1717 void SetAutoWrap(WindowInfo *window, int state)
1719 int i;
1720 int autoWrap = state == NEWLINE_WRAP, contWrap = state == CONTINUOUS_WRAP;
1722 XtVaSetValues(window->textArea, textNautoWrap, autoWrap,
1723 textNcontinuousWrap, contWrap, NULL);
1724 for (i=0; i<window->nPanes; i++)
1725 XtVaSetValues(window->textPanes[i], textNautoWrap, autoWrap,
1726 textNcontinuousWrap, contWrap, NULL);
1727 window->wrapMode = state;
1729 XmToggleButtonSetState(window->newlineWrapItem, autoWrap, False);
1730 XmToggleButtonSetState(window->continuousWrapItem, contWrap, False);
1731 XmToggleButtonSetState(window->noWrapItem, state == NO_WRAP, False);
1735 ** Set the wrap margin (0 == wrap at right edge of window)
1737 void SetWrapMargin(WindowInfo *window, int margin)
1739 int i;
1741 XtVaSetValues(window->textArea, textNwrapMargin, margin, NULL);
1742 for (i=0; i<window->nPanes; i++)
1743 XtVaSetValues(window->textPanes[i], textNwrapMargin, margin, NULL);
1747 ** Recover the window pointer from any widget in the window, by searching
1748 ** up the widget hierarcy for the top level container widget where the window
1749 ** pointer is stored in the userData field. In buffer mode, this is the window
1750 ** pointer of the top (active) buffer, which is returned if w is 'shell-level'
1751 ** widget - menus, find/replace dialogs, etc.
1753 ** To support action routine in buffer mode, a copy of the window pointer
1754 ** is also store in the splitPane widget.
1756 WindowInfo *WidgetToWindow(Widget w)
1758 WindowInfo *window = NULL;
1759 Widget parent;
1761 while (True) {
1762 /* return window pointer of buffer */
1763 if (XtClass(w) == xmPanedWindowWidgetClass)
1764 break;
1766 if (XtClass(w) == topLevelShellWidgetClass) {
1767 WidgetList items;
1769 /* there should be only 1 child for the shell -
1770 the main window widget */
1771 XtVaGetValues(w, XmNchildren, &items, NULL);
1772 w = items[0];
1773 break;
1776 parent = XtParent(w);
1777 if (parent == NULL)
1778 return NULL;
1780 /* make sure it is not a dialog shell */
1781 if (XtClass(parent) == topLevelShellWidgetClass &&
1782 XmIsMainWindow(w))
1783 break;
1785 w = parent;
1788 XtVaGetValues(w, XmNuserData, &window, NULL);
1790 return window;
1794 ** Change the window appearance and the window data structure to show
1795 ** that the file it contains has been modified
1797 void SetWindowModified(WindowInfo *window, int modified)
1799 if (window->fileChanged == FALSE && modified == TRUE) {
1800 XtSetSensitive(window->closeItem, TRUE);
1801 window->fileChanged = TRUE;
1802 UpdateWindowTitle(window);
1803 RefreshTabState(window);
1804 } else if (window->fileChanged == TRUE && modified == FALSE) {
1805 window->fileChanged = FALSE;
1806 UpdateWindowTitle(window);
1807 RefreshTabState(window);
1812 ** Update the window title to reflect the filename, read-only, and modified
1813 ** status of the window data structure
1815 void UpdateWindowTitle(const WindowInfo *window)
1817 char *iconTitle, *title;
1819 if (!IsTopBuffer((WindowInfo*)window))
1820 return;
1822 title = FormatWindowTitle(window->filename,
1823 window->path,
1824 #ifdef VMS
1825 NULL,
1826 #else
1827 GetClearCaseViewTag(),
1828 #endif /* VMS */
1829 GetPrefServerName(),
1830 IsServer,
1831 window->filenameSet,
1832 window->lockReasons,
1833 window->fileChanged,
1834 GetPrefTitleFormat());
1836 iconTitle = XtMalloc(strlen(window->filename) + 2); /* strlen("*")+1 */
1838 strcpy(iconTitle, window->filename);
1839 if (window->fileChanged)
1840 strcat(iconTitle, "*");
1841 XtVaSetValues(window->shell, XmNtitle, title, XmNiconName, iconTitle, NULL);
1843 /* If there's a find or replace dialog up in "Keep Up" mode, with a
1844 file name in the title, update it too */
1845 if (window->findDlog && XmToggleButtonGetState(window->findKeepBtn)) {
1846 sprintf(title, "Find (in %s)", window->filename);
1847 XtVaSetValues(XtParent(window->findDlog), XmNtitle, title, NULL);
1849 if (window->replaceDlog && XmToggleButtonGetState(window->replaceKeepBtn)) {
1850 sprintf(title, "Replace (in %s)", window->filename);
1851 XtVaSetValues(XtParent(window->replaceDlog), XmNtitle, title, NULL);
1853 XtFree(iconTitle);
1855 /* Update the Windows menus with the new name */
1856 InvalidateWindowMenus();
1860 ** Update the read-only state of the text area(s) in the window, and
1861 ** the ReadOnly toggle button in the File menu to agree with the state in
1862 ** the window data structure.
1864 void UpdateWindowReadOnly(WindowInfo *window)
1866 int i, state;
1868 if (!IsTopBuffer(window))
1869 return;
1871 state = IS_ANY_LOCKED(window->lockReasons);
1872 XtVaSetValues(window->textArea, textNreadOnly, state, NULL);
1873 for (i=0; i<window->nPanes; i++)
1874 XtVaSetValues(window->textPanes[i], textNreadOnly, state, NULL);
1875 XmToggleButtonSetState(window->readOnlyItem, state, FALSE);
1876 XtSetSensitive(window->readOnlyItem,
1877 !IS_ANY_LOCKED_IGNORING_USER(window->lockReasons));
1881 ** Get the start and end of the current selection. This routine is obsolete
1882 ** because it ignores rectangular selections, and reads from the widget
1883 ** instead of the buffer. Use BufGetSelectionPos.
1885 int GetSelection(Widget widget, int *left, int *right)
1887 return GetSimpleSelection(TextGetBuffer(widget), left, right);
1891 ** Find the start and end of a single line selection. Hides rectangular
1892 ** selection issues for older routines which use selections that won't
1893 ** span lines.
1895 int GetSimpleSelection(textBuffer *buf, int *left, int *right)
1897 int selStart, selEnd, isRect, rectStart, rectEnd, lineStart;
1899 /* get the character to match and its position from the selection, or
1900 the character before the insert point if nothing is selected.
1901 Give up if too many characters are selected */
1902 if (!BufGetSelectionPos(buf, &selStart, &selEnd, &isRect,
1903 &rectStart, &rectEnd))
1904 return False;
1905 if (isRect) {
1906 lineStart = BufStartOfLine(buf, selStart);
1907 selStart = BufCountForwardDispChars(buf, lineStart, rectStart);
1908 selEnd = BufCountForwardDispChars(buf, lineStart, rectEnd);
1910 *left = selStart;
1911 *right = selEnd;
1912 return True;
1916 ** Returns a range of text from a text widget (this routine is obsolete,
1917 ** get text from the buffer instead). Memory is allocated with
1918 ** XtMalloc and caller should free it.
1920 char *GetTextRange(Widget widget, int left, int right)
1922 return BufGetRange(TextGetBuffer(widget), left, right);
1926 ** If the selection (or cursor position if there's no selection) is not
1927 ** fully shown, scroll to bring it in to view. Note that as written,
1928 ** this won't work well with multi-line selections. Modest re-write
1929 ** of the horizontal scrolling part would be quite easy to make it work
1930 ** well with rectangular selections.
1932 void MakeSelectionVisible(WindowInfo *window, Widget textPane)
1934 int left, right, isRect, rectStart, rectEnd, horizOffset;
1935 int scrollOffset, leftX, rightX, y, rows, margin;
1936 int topLineNum, lastLineNum, rightLineNum, leftLineNum, linesToScroll;
1937 textDisp *textD = ((TextWidget)textPane)->text.textD;
1938 int topChar = TextFirstVisiblePos(textPane);
1939 int lastChar = TextLastVisiblePos(textPane);
1940 int targetLineNum;
1941 Dimension width;
1943 /* find out where the selection is */
1944 if (!BufGetSelectionPos(window->buffer, &left, &right, &isRect,
1945 &rectStart, &rectEnd)) {
1946 left = right = TextGetCursorPos(textPane);
1947 isRect = False;
1950 /* Check vertical positioning unless the selection is already shown or
1951 already covers the display. If the end of the selection is below
1952 bottom, scroll it in to view until the end selection is scrollOffset
1953 lines from the bottom of the display or the start of the selection
1954 scrollOffset lines from the top. Calculate a pleasing distance from the
1955 top or bottom of the window, to scroll the selection to (if scrolling is
1956 necessary), around 1/3 of the height of the window */
1957 if (!((left >= topChar && right <= lastChar) ||
1958 (left <= topChar && right >= lastChar))) {
1959 XtVaGetValues(textPane, textNrows, &rows, NULL);
1960 scrollOffset = rows/3;
1961 TextGetScroll(textPane, &topLineNum, &horizOffset);
1962 if (right > lastChar) {
1963 /* End of sel. is below bottom of screen */
1964 leftLineNum = topLineNum +
1965 TextDCountLines(textD, topChar, left, False);
1966 targetLineNum = topLineNum + scrollOffset;
1967 if (leftLineNum >= targetLineNum) {
1968 /* Start of sel. is not between top & target */
1969 linesToScroll = TextDCountLines(textD, lastChar, right, False) +
1970 scrollOffset;
1971 if (leftLineNum - linesToScroll < targetLineNum)
1972 linesToScroll = leftLineNum - targetLineNum;
1973 /* Scroll start of selection to the target line */
1974 TextSetScroll(textPane, topLineNum+linesToScroll, horizOffset);
1976 } else if (left < topChar) {
1977 /* Start of sel. is above top of screen */
1978 lastLineNum = topLineNum + rows;
1979 rightLineNum = lastLineNum -
1980 TextDCountLines(textD, right, lastChar, False);
1981 targetLineNum = lastLineNum - scrollOffset;
1982 if (rightLineNum <= targetLineNum) {
1983 /* End of sel. is not between bottom & target */
1984 linesToScroll = TextDCountLines(textD, left, topChar, False) +
1985 scrollOffset;
1986 if (rightLineNum + linesToScroll > targetLineNum)
1987 linesToScroll = targetLineNum - rightLineNum;
1988 /* Scroll end of selection to the target line */
1989 TextSetScroll(textPane, topLineNum-linesToScroll, horizOffset);
1994 /* If either end of the selection off screen horizontally, try to bring it
1995 in view, by making sure both end-points are visible. Using only end
1996 points of a multi-line selection is not a great idea, and disaster for
1997 rectangular selections, so this part of the routine should be re-written
1998 if it is to be used much with either. Note also that this is a second
1999 scrolling operation, causing the display to jump twice. It's done after
2000 vertical scrolling to take advantage of TextPosToXY which requires it's
2001 reqested position to be vertically on screen) */
2002 if ( TextPosToXY(textPane, left, &leftX, &y) &&
2003 TextPosToXY(textPane, right, &rightX, &y) && leftX <= rightX) {
2004 TextGetScroll(textPane, &topLineNum, &horizOffset);
2005 XtVaGetValues(textPane, XmNwidth, &width, textNmarginWidth, &margin,
2006 NULL);
2007 if (leftX < margin + textD->lineNumLeft + textD->lineNumWidth)
2008 horizOffset -=
2009 margin + textD->lineNumLeft + textD->lineNumWidth - leftX;
2010 else if (rightX > width - margin)
2011 horizOffset += rightX - (width - margin);
2012 TextSetScroll(textPane, topLineNum, horizOffset);
2015 /* make sure that the statistics line is up to date */
2016 UpdateStatsLine(window);
2019 static Widget createTextArea(Widget parent, WindowInfo *window, int rows,
2020 int cols, int emTabDist, char *delimiters, int wrapMargin,
2021 int lineNumCols)
2023 Widget text, sw, hScrollBar, vScrollBar, frame;
2025 /* Create a text widget inside of a scrolled window widget */
2026 sw = XtVaCreateManagedWidget("scrolledW", xmScrolledWindowWidgetClass,
2027 parent, XmNpaneMaximum, SHRT_MAX,
2028 XmNpaneMinimum, PANE_MIN_HEIGHT, XmNhighlightThickness, 0, NULL);
2029 hScrollBar = XtVaCreateManagedWidget("textHorScrollBar",
2030 xmScrollBarWidgetClass, sw, XmNorientation, XmHORIZONTAL,
2031 XmNrepeatDelay, 10, NULL);
2032 vScrollBar = XtVaCreateManagedWidget("textVertScrollBar",
2033 xmScrollBarWidgetClass, sw, XmNorientation, XmVERTICAL,
2034 XmNrepeatDelay, 10, NULL);
2035 frame = XtVaCreateManagedWidget("textFrame", xmFrameWidgetClass, sw,
2036 XmNshadowType, XmSHADOW_IN, NULL);
2037 text = XtVaCreateManagedWidget("text", textWidgetClass, frame,
2038 textNbacklightCharTypes, window->backlightCharTypes,
2039 textNrows, rows, textNcolumns, cols,
2040 textNlineNumCols, lineNumCols,
2041 textNemulateTabs, emTabDist,
2042 textNfont, GetDefaultFontStruct(window->fontList),
2043 textNhScrollBar, hScrollBar, textNvScrollBar, vScrollBar,
2044 textNreadOnly, IS_ANY_LOCKED(window->lockReasons),
2045 textNwordDelimiters, delimiters,
2046 textNwrapMargin, wrapMargin,
2047 textNautoIndent, window->indentStyle == AUTO_INDENT,
2048 textNsmartIndent, window->indentStyle == SMART_INDENT,
2049 textNautoWrap, window->wrapMode == NEWLINE_WRAP,
2050 textNcontinuousWrap, window->wrapMode == CONTINUOUS_WRAP,
2051 textNoverstrike, window->overstrike,
2052 textNhidePointer, (Boolean) GetPrefTypingHidesPointer(),
2053 NULL);
2055 XtVaSetValues(sw, XmNworkWindow, frame, XmNhorizontalScrollBar,
2056 hScrollBar, XmNverticalScrollBar, vScrollBar, NULL);
2058 /* add focus, drag, cursor tracking, and smart indent callbacks */
2059 XtAddCallback(text, textNfocusCallback, (XtCallbackProc)focusCB, window);
2060 XtAddCallback(text, textNcursorMovementCallback, (XtCallbackProc)movedCB,
2061 window);
2062 XtAddCallback(text, textNdragStartCallback, (XtCallbackProc)dragStartCB,
2063 window);
2064 XtAddCallback(text, textNdragEndCallback, (XtCallbackProc)dragEndCB,
2065 window);
2066 XtAddCallback(text, textNsmartIndentCallback, SmartIndentCB, window);
2068 /* This makes sure the text area initially has a the insert point shown
2069 ... (check if still true with the nedit text widget, probably not) */
2070 XmAddTabGroup(containingPane(text));
2072 /* compensate for Motif delete/backspace problem */
2073 RemapDeleteKey(text);
2075 /* Augment translation table for right button popup menu */
2076 AddBGMenuAction(text);
2078 /* If absolute line numbers will be needed for display in the statistics
2079 line, tell the widget to maintain them (otherwise, it's a costly
2080 operation and performance will be better without it) */
2081 TextDMaintainAbsLineNum(((TextWidget)text)->text.textD, window->showStats);
2083 return text;
2086 static void movedCB(Widget w, WindowInfo *window, XtPointer callData)
2088 if (window->ignoreModify)
2089 return;
2091 /* update line and column nubers in statistics line */
2092 UpdateStatsLine(window);
2094 /* Check the character before the cursor for matchable characters */
2095 FlashMatching(window, w);
2097 /* Check for changes to read-only status and/or file modifications */
2098 CheckForChangesToFile(window);
2101 static void modifiedCB(int pos, int nInserted, int nDeleted, int nRestyled,
2102 char *deletedText, void *cbArg)
2104 WindowInfo *window = (WindowInfo *)cbArg;
2105 int selected = window->buffer->primary.selected;
2107 /* update the table of bookmarks */
2108 if (!window->ignoreModify) {
2109 UpdateMarkTable(window, pos, nInserted, nDeleted);
2112 /* Check and dim/undim selection related menu items */
2113 if ((window->wasSelected && !selected) ||
2114 (!window->wasSelected && selected)) {
2115 window->wasSelected = selected;
2117 /* buffers may share a common shell window, menu-bar etc.
2118 we don't do much if things happen to the hidden ones */
2119 if (IsTopBuffer(window)) {
2120 XtSetSensitive(window->printSelItem, selected);
2121 XtSetSensitive(window->cutItem, selected);
2122 XtSetSensitive(window->copyItem, selected);
2123 XtSetSensitive(window->delItem, selected);
2124 /* Note we don't change the selection for items like
2125 "Open Selected" and "Find Selected". That's because
2126 it works on selections in external applications.
2127 Desensitizing it if there's no NEdit selection
2128 disables this feature. */
2129 #ifndef VMS
2130 XtSetSensitive(window->filterItem, selected);
2131 #endif
2133 DimSelectionDepUserMenuItems(window, selected);
2134 if (window->replaceDlog != NULL)
2136 UpdateReplaceActionButtons(window);
2141 /* Make sure line number display is sufficient for new data */
2142 UpdateLineNumDisp(window);
2144 /* When the program needs to make a change to a text area without without
2145 recording it for undo or marking file as changed it sets ignoreModify */
2146 if (window->ignoreModify || (nDeleted == 0 && nInserted == 0))
2147 return;
2149 /* Save information for undoing this operation (this call also counts
2150 characters and editing operations for triggering autosave */
2151 SaveUndoInformation(window, pos, nInserted, nDeleted, deletedText);
2153 /* Trigger automatic backup if operation or character limits reached */
2154 if (window->autoSave &&
2155 (window->autoSaveCharCount > AUTOSAVE_CHAR_LIMIT ||
2156 window->autoSaveOpCount > AUTOSAVE_OP_LIMIT)) {
2157 WriteBackupFile(window);
2158 window->autoSaveCharCount = 0;
2159 window->autoSaveOpCount = 0;
2162 /* Indicate that the window has now been modified */
2163 SetWindowModified(window, TRUE);
2165 /* Update # of bytes, and line and col statistics */
2166 UpdateStatsLine(window);
2168 /* Check if external changes have been made to file and warn user */
2169 CheckForChangesToFile(window);
2172 static void focusCB(Widget w, WindowInfo *window, XtPointer callData)
2174 /* record which window pane last had the keyboard focus */
2175 window->lastFocus = w;
2177 /* update line number statistic to reflect current focus pane */
2178 UpdateStatsLine(window);
2180 /* finish off the current incremental search */
2181 EndISearch(window);
2183 /* Check for changes to read-only status and/or file modifications */
2184 CheckForChangesToFile(window);
2187 static void dragStartCB(Widget w, WindowInfo *window, XtPointer callData)
2189 /* don't record all of the intermediate drag steps for undo */
2190 window->ignoreModify = True;
2193 static void dragEndCB(Widget w, WindowInfo *window, dragEndCBStruct *callData)
2195 /* restore recording of undo information */
2196 window->ignoreModify = False;
2198 /* Do nothing if drag operation was canceled */
2199 if (callData->nCharsInserted == 0)
2200 return;
2202 /* Save information for undoing this operation not saved while
2203 undo recording was off */
2204 modifiedCB(callData->startPos, callData->nCharsInserted,
2205 callData->nCharsDeleted, 0, callData->deletedText, window);
2208 static void closeCB(Widget w, WindowInfo *window, XtPointer callData)
2210 window = WidgetToWindow(w);
2212 if (GetPrefBufferMode()) {
2213 /* in window-buffer mode, we now have only one app window,
2214 to close is to quit */
2215 CloseBufferWindow(w, window, callData);
2217 else {
2218 /* close this window */
2219 if (WindowList->next == NULL) {
2220 if (!CheckPrefsChangesSaved(window->shell))
2221 return;
2222 if (!WindowList->fileChanged)
2223 exit(EXIT_SUCCESS);
2224 if (CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE))
2225 exit(EXIT_SUCCESS);
2226 } else
2227 CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE);
2231 static void saveYourselfCB(Widget w, WindowInfo *window, XtPointer callData)
2233 WindowInfo *win, **revWindowList;
2234 char geometry[MAX_GEOM_STRING_LEN];
2235 int argc = 0, maxArgc, nWindows, i;
2236 char **argv;
2237 int wasIconic = False;
2239 window = WidgetToWindow(w);
2241 /* Only post a restart command on the first window in the window list so
2242 session manager can restart the whole set of windows in one executable,
2243 rather than one nedit per file. Even if the restart command is not on
2244 this window, the protocol demands that we set the window's WM_COMMAND
2245 property in response to the "save yourself" message */
2246 if (window != WindowList) {
2247 XSetCommand(TheDisplay, XtWindow(window->shell), NULL, 0);
2248 return;
2251 /* Allocate memory for an argument list and for a reversed list of
2252 windows. The window list is reversed for IRIX 4DWM and any other
2253 window/session manager combination which uses window creation
2254 order for re-associating stored geometry information with
2255 new windows created by a restored application */
2256 maxArgc = 1;
2257 nWindows = 0;
2258 for (win=WindowList; win!=NULL; win=win->next) {
2259 maxArgc += 4;
2260 nWindows++;
2262 argv = (char **)XtMalloc(maxArgc*sizeof(char *));
2263 revWindowList = (WindowInfo **)XtMalloc(sizeof(WindowInfo *)*nWindows);
2264 for (win=WindowList, i=nWindows-1; win!=NULL; win=win->next, i--)
2265 revWindowList[i] = win;
2267 /* Create command line arguments for restoring each window in the list */
2268 argv[argc++] = XtNewString(ArgV0);
2269 if (IsServer) {
2270 argv[argc++] = XtNewString("-server");
2271 if (GetPrefServerName()[0] != '\0') {
2272 argv[argc++] = XtNewString("-svrname");
2273 argv[argc++] = XtNewString(GetPrefServerName());
2276 for (i=0; i<nWindows; i++) {
2277 win = revWindowList[i];
2278 getGeometryString(win, geometry);
2279 argv[argc++] = XtNewString("-geometry");
2280 argv[argc++] = XtNewString(geometry);
2281 if (IsIconic(win)) {
2282 argv[argc++] = XtNewString("-iconic");
2283 wasIconic = True;
2284 } else if (wasIconic) {
2285 argv[argc++] = XtNewString("-noiconic");
2286 wasIconic = False;
2288 if (win->filenameSet) {
2289 argv[argc] = XtMalloc(strlen(win->path) +
2290 strlen(win->filename) + 1);
2291 sprintf(argv[argc++], "%s%s", win->path, win->filename);
2294 XtFree((char *)revWindowList);
2296 /* Set the window's WM_COMMAND property to the created command line */
2297 XSetCommand(TheDisplay, XtWindow(window->shell), argv, argc);
2298 for (i=0; i<argc; i++)
2299 XtFree(argv[i]);
2300 XtFree((char *)argv);
2304 ** Returns true if window is iconic (as determined by the WM_STATE property
2305 ** on the shell window. I think this is the most reliable way to tell,
2306 ** but if someone has a better idea please send me a note).
2308 int IsIconic(WindowInfo *window)
2310 unsigned long *property = NULL;
2311 unsigned long nItems;
2312 unsigned long leftover;
2313 static Atom wmStateAtom = 0;
2314 Atom actualType;
2315 int actualFormat;
2316 int result;
2318 if (wmStateAtom == 0)
2319 wmStateAtom = XInternAtom (XtDisplay(window->shell), "WM_STATE", False);
2320 if (XGetWindowProperty(XtDisplay(window->shell), XtWindow(window->shell),
2321 wmStateAtom, 0L, 1L, False, wmStateAtom, &actualType, &actualFormat,
2322 &nItems, &leftover, (unsigned char **)&property) != Success ||
2323 nItems != 1 || property == NULL)
2324 return FALSE;
2325 result = *property == IconicState;
2326 XtFree((char *)property);
2327 return result;
2331 ** Add a window to the the window list.
2333 static void addToWindowList(WindowInfo *window)
2335 WindowInfo *temp;
2337 temp = WindowList;
2338 WindowList = window;
2339 window->next = temp;
2343 ** Remove a window from the list of windows
2345 static void removeFromWindowList(WindowInfo *window)
2347 WindowInfo *temp;
2349 if (WindowList == window)
2350 WindowList = window->next;
2351 else {
2352 for (temp = WindowList; temp != NULL; temp = temp->next) {
2353 if (temp->next == window) {
2354 temp->next = window->next;
2355 break;
2362 ** If necessary, enlarges the window and line number display area
2363 ** to make room for numbers.
2365 void UpdateLineNumDisp(WindowInfo *window)
2367 Dimension windowWidth;
2368 int i, fontWidth, reqCols, lineNumCols;
2369 int oldWidth, newWidth, marginWidth;
2370 Widget text;
2371 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
2373 if (!window->showLineNumbers)
2374 return;
2376 /* Decide how wide the line number field has to be to display all
2377 possible line numbers */
2378 reqCols = textD->nBufferLines<1 ? 1 : log10((double)textD->nBufferLines)+1;
2379 if (reqCols < MIN_LINE_NUM_COLS)
2380 reqCols = MIN_LINE_NUM_COLS;
2382 /* Is the width of the line number area sufficient to display all the
2383 line numbers in the file? If not, expand line number field, and the
2384 window width */
2385 XtVaGetValues(window->textArea, textNlineNumCols, &lineNumCols,
2386 textNmarginWidth, &marginWidth, NULL);
2387 if (lineNumCols < reqCols) {
2388 fontWidth = textD->fontStruct->max_bounds.width;
2389 oldWidth = textD->left - marginWidth;
2390 newWidth = reqCols * fontWidth + marginWidth;
2391 XtVaGetValues(window->shell, XmNwidth, &windowWidth, NULL);
2392 XtVaSetValues(window->shell, XmNwidth,
2393 windowWidth + newWidth-oldWidth, NULL);
2394 UpdateWMSizeHints(window);
2395 for (i=0; i<=window->nPanes; i++) {
2396 text = i==0 ? window->textArea : window->textPanes[i-1];
2397 XtVaSetValues(text, textNlineNumCols, reqCols, NULL);
2403 ** Update the optional statistics line.
2405 void UpdateStatsLine(WindowInfo *window)
2407 int line, pos, colNum;
2408 char *string, *format, slinecol[32];
2409 Widget statW = window->statsLine;
2410 XmString xmslinecol;
2411 #ifdef SGI_CUSTOM
2412 char *sleft, *smid, *sright;
2413 #endif
2415 if (!IsTopBuffer(window))
2416 return;
2418 /* This routine is called for each character typed, so its performance
2419 affects overall editor perfomance. Only update if the line is on. */
2420 if (!window->showStats)
2421 return;
2423 /* Compose the string to display. If line # isn't available, leave it off */
2424 pos = TextGetCursorPos(window->lastFocus);
2425 string = XtMalloc(strlen(window->filename) + strlen(window->path) + 45);
2426 format = window->fileFormat == DOS_FILE_FORMAT ? " DOS" :
2427 (window->fileFormat == MAC_FILE_FORMAT ? " Mac" : "");
2428 if (!TextPosToLineAndCol(window->lastFocus, pos, &line, &colNum)) {
2429 sprintf(string, "%s%s%s %d bytes", window->path, window->filename,
2430 format, window->buffer->length);
2431 sprintf(slinecol, "L: --- C: ---");
2432 } else {
2433 sprintf(slinecol, "L: %d C: %d", line, colNum);
2434 if (window->showLineNumbers)
2435 sprintf(string, "%s%s%s byte %d of %d", window->path,
2436 window->filename, format, pos,
2437 window->buffer->length);
2438 else
2439 sprintf(string, "%s%s%s %d bytes", window->path,
2440 window->filename, format, window->buffer->length);
2443 /* Update the line/column number */
2444 xmslinecol = XmStringCreateSimple(slinecol);
2445 XtVaSetValues( window->statsLineColNo,
2446 XmNlabelString, xmslinecol, NULL );
2447 XmStringFree(xmslinecol);
2449 /* Don't clobber the line if there's a special message being displayed */
2450 if (!window->modeMessageDisplayed) {
2451 /* Change the text in the stats line */
2452 #ifdef SGI_CUSTOM
2453 /* don't show full pathname, just dir and filename (+ byte info) */
2454 smid = strchr(string, '/');
2455 if ( smid != NULL ) {
2456 sleft = smid;
2457 sright = strrchr(string, '/');
2458 while (strcmp(smid, sright)) {
2459 sleft = smid;
2460 smid = strchr(sleft + 1, '/');
2462 XmTextReplace(statW, 0, XmTextGetLastPosition(statW), sleft + 1);
2463 } else
2464 XmTextReplace(statW, 0, XmTextGetLastPosition(statW), string);
2465 #else
2466 XmTextReplace(statW, 0, XmTextGetLastPosition(statW), string);
2467 #endif
2469 XtFree(string);
2471 /* Update the line/col display */
2472 xmslinecol = XmStringCreateSimple(slinecol);
2473 XtVaSetValues(window->statsLineColNo,
2474 XmNlabelString, xmslinecol, NULL);
2475 XmStringFree(xmslinecol);
2478 static Boolean currentlyBusy = False;
2479 static long busyStartTime = 0;
2480 static Boolean modeMessageSet = False;
2483 * Auxiliary function for measuring elapsed time during busy waits.
2485 static long getRelTimeInTenthsOfSeconds()
2487 #ifdef __unix__
2488 struct timeval current;
2489 gettimeofday(&current, NULL);
2490 return (current.tv_sec*10 + current.tv_usec/100000) & 0xFFFFFFFL;
2491 #else
2492 time_t current;
2493 time(&current);
2494 return (current*10) & 0xFFFFFFFL;
2495 #endif
2498 void AllWindowsBusy(const char *message)
2500 WindowInfo *w;
2502 if (!currentlyBusy)
2504 busyStartTime = getRelTimeInTenthsOfSeconds();
2505 modeMessageSet = False;
2507 for (w=WindowList; w!=NULL; w=w->next)
2509 /* We don't the display message here yet, but defer it for
2510 a while. If the wait is short, we don't want
2511 to have it flash on and off the screen. However,
2512 we can't use a time since in generally we are in
2513 a tight loop and only processing exposure events, so it's
2514 up to the caller to make sure that this routine is called
2515 at regular intervals.
2517 BeginWait(w->shell);
2519 } else if (!modeMessageSet && message &&
2520 getRelTimeInTenthsOfSeconds() - busyStartTime > 10) {
2521 /* Show the mode message when we've been busy for more than a second */
2522 for (w=WindowList; w!=NULL; w=w->next) {
2523 SetModeMessage(w, message);
2525 modeMessageSet = True;
2527 BusyWait(WindowList->shell);
2529 currentlyBusy = True;
2532 void AllWindowsUnbusy(void)
2534 WindowInfo *w;
2536 for (w=WindowList; w!=NULL; w=w->next)
2538 /* ClearModeMessage(w); */
2539 EndWait(w->shell);
2542 currentlyBusy = False;
2543 modeMessageSet = False;
2544 busyStartTime = 0;
2548 ** Paned windows are impossible to adjust after they are created, which makes
2549 ** them nearly useless for NEdit (or any application which needs to dynamically
2550 ** adjust the panes) unless you tweek some private data to overwrite the
2551 ** desired and minimum pane heights which were set at creation time. These
2552 ** will probably break in a future release of Motif because of dependence on
2553 ** private data.
2555 static void setPaneDesiredHeight(Widget w, int height)
2557 ((XmPanedWindowConstraintPtr)w->core.constraints)->panedw.dheight = height;
2559 static void setPaneMinHeight(Widget w, int min)
2561 ((XmPanedWindowConstraintPtr)w->core.constraints)->panedw.min = min;
2565 ** Update the window manager's size hints. These tell it the increments in
2566 ** which it is allowed to resize the window. While this isn't particularly
2567 ** important for NEdit (since it can tolerate any window size), setting these
2568 ** hints also makes the resize indicator show the window size in characters
2569 ** rather than pixels, which is very helpful to users.
2571 void UpdateWMSizeHints(WindowInfo *window)
2573 Dimension shellWidth, shellHeight, textHeight, hScrollBarHeight;
2574 int marginHeight, marginWidth, totalHeight;
2575 XFontStruct *fs;
2576 int i, baseWidth, baseHeight, fontHeight, fontWidth;
2577 Widget hScrollBar;
2578 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
2580 /* Find the base (non-expandable) width and height of the editor window */
2581 XtVaGetValues(window->textArea, XmNheight, &textHeight,
2582 textNmarginHeight, &marginHeight, textNmarginWidth, &marginWidth,
2583 NULL);
2584 totalHeight = textHeight - 2*marginHeight;
2585 for (i=0; i<window->nPanes; i++) {
2586 XtVaGetValues(window->textPanes[i], XmNheight, &textHeight,
2587 textNhScrollBar, &hScrollBar, NULL);
2588 totalHeight += textHeight - 2*marginHeight;
2589 if (!XtIsManaged(hScrollBar)) {
2590 XtVaGetValues(hScrollBar, XmNheight, &hScrollBarHeight, NULL);
2591 totalHeight -= hScrollBarHeight;
2594 XtVaGetValues(window->shell, XmNwidth, &shellWidth,
2595 XmNheight, &shellHeight, NULL);
2596 baseWidth = shellWidth - textD->width;
2597 baseHeight = shellHeight - totalHeight;
2599 /* Find the dimensions of a single character of the text font */
2600 XtVaGetValues(window->textArea, textNfont, &fs, NULL);
2601 fontHeight = textD->ascent + textD->descent;
2602 fontWidth = fs->max_bounds.width;
2604 /* Set the size hints in the shell widget */
2605 XtVaSetValues(window->shell, XmNwidthInc, fs->max_bounds.width,
2606 XmNheightInc, fontHeight,
2607 XmNbaseWidth, baseWidth, XmNbaseHeight, baseHeight,
2608 XmNminWidth, baseWidth + fontWidth,
2609 XmNminHeight, baseHeight + (1+window->nPanes) * fontHeight, NULL);
2613 ** Update the minimum allowable height for a split window pane after a change
2614 ** to font or margin height.
2616 void UpdateMinPaneHeights(WindowInfo *window)
2618 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
2619 Dimension hsbHeight, swMarginHeight,frameShadowHeight;
2620 int i, marginHeight, minPaneHeight;
2621 Widget hScrollBar;
2623 /* find the minimum allowable size for a pane */
2624 XtVaGetValues(window->textArea, textNhScrollBar, &hScrollBar, NULL);
2625 XtVaGetValues(containingPane(window->textArea),
2626 XmNscrolledWindowMarginHeight, &swMarginHeight, NULL);
2627 XtVaGetValues(XtParent(window->textArea),
2628 XmNshadowThickness, &frameShadowHeight, NULL);
2629 XtVaGetValues(window->textArea, textNmarginHeight, &marginHeight, NULL);
2630 XtVaGetValues(hScrollBar, XmNheight, &hsbHeight, NULL);
2631 minPaneHeight = textD->ascent + textD->descent + marginHeight*2 +
2632 swMarginHeight*2 + hsbHeight + 2*frameShadowHeight;
2634 /* Set it in all of the widgets in the window */
2635 setPaneMinHeight(containingPane(window->textArea), minPaneHeight);
2636 for (i=0; i<window->nPanes; i++)
2637 setPaneMinHeight(containingPane(window->textPanes[i]),
2638 minPaneHeight);
2641 /* Add an icon to an applicaction shell widget. addWindowIcon adds a large
2642 ** (primary window) icon, AddSmallIcon adds a small (secondary window) icon.
2644 ** Note: I would prefer that these were not hardwired, but yhere is something
2645 ** weird about the XmNiconPixmap resource that prevents it from being set
2646 ** from the defaults in the application resource database.
2648 static void addWindowIcon(Widget shell)
2650 static Pixmap iconPixmap = 0, maskPixmap = 0;
2652 if (iconPixmap == 0) {
2653 iconPixmap = XCreateBitmapFromData(TheDisplay,
2654 RootWindowOfScreen(XtScreen(shell)), (char *)iconBits,
2655 iconBitmapWidth, iconBitmapHeight);
2656 maskPixmap = XCreateBitmapFromData(TheDisplay,
2657 RootWindowOfScreen(XtScreen(shell)), (char *)maskBits,
2658 iconBitmapWidth, iconBitmapHeight);
2660 XtVaSetValues(shell, XmNiconPixmap, iconPixmap, XmNiconMask, maskPixmap,
2661 NULL);
2663 void AddSmallIcon(Widget shell)
2665 static Pixmap iconPixmap = 0, maskPixmap = 0;
2667 if (iconPixmap == 0) {
2668 iconPixmap = XCreateBitmapFromData(TheDisplay,
2669 RootWindowOfScreen(XtScreen(shell)), (char *)n_bits,
2670 n_width, n_height);
2671 maskPixmap = XCreateBitmapFromData(TheDisplay,
2672 RootWindowOfScreen(XtScreen(shell)), (char *)n_mask,
2673 n_width, n_height);
2675 XtVaSetValues(shell, XmNiconPixmap, iconPixmap,
2676 XmNiconMask, maskPixmap, NULL);
2679 static void setTabCloseButtonImage(Widget button)
2681 static Pixmap pixmap = 0;
2683 if (pixmap == 0) {
2684 Pixel fg, bg;
2685 int depth;
2687 /* create pixmap per the color depth setting. This fixes a
2688 BadMatch (X_CopyArea) error due to mismatching of color
2689 depth between the bitmap (depth of 1) and the screen,
2690 specifically on when linked to LessTif (0.93.x) on
2691 ASPLinux 7.1 */
2692 XtVaGetValues (button, XmNforeground, &fg, XmNbackground, &bg,
2693 XmNdepth, &depth, NULL);
2694 pixmap = XCreatePixmapFromBitmapData(TheDisplay,
2695 RootWindowOfScreen(XtScreen(button)), (char *)close_bits,
2696 close_width, close_height, fg, bg, depth);
2699 XtVaSetValues(button, XmNlabelPixmap, pixmap, NULL);
2703 ** Save the position and size of a window as an X standard geometry string.
2704 ** A string of at least MAX_GEOMETRY_STRING_LEN characters should be
2705 ** provided in the argument "geomString" to receive the result.
2707 static void getGeometryString(WindowInfo *window, char *geomString)
2709 int x, y, fontWidth, fontHeight, baseWidth, baseHeight;
2710 unsigned int width, height, dummyW, dummyH, bw, depth, nChild;
2711 Window parent, root, *child, w = XtWindow(window->shell);
2712 Display *dpy = XtDisplay(window->shell);
2714 /* Find the width and height from the window of the shell */
2715 XGetGeometry(dpy, w, &root, &x, &y, &width, &height, &bw, &depth);
2717 /* Find the top left corner (x and y) of the window decorations. (This
2718 is what's required in the geometry string to restore the window to it's
2719 original position, since the window manager re-parents the window to
2720 add it's title bar and menus, and moves the requested window down and
2721 to the left.) The position is found by traversing the window hier-
2722 archy back to the window to the last parent before the root window */
2723 for(;;) {
2724 XQueryTree(dpy, w, &root, &parent, &child, &nChild);
2725 XFree((char*)child);
2726 if (parent == root)
2727 break;
2728 w = parent;
2730 XGetGeometry(dpy, w, &root, &x, &y, &dummyW, &dummyH, &bw, &depth);
2732 /* Use window manager size hints (set by UpdateWMSizeHints) to
2733 translate the width and height into characters, as opposed to pixels */
2734 XtVaGetValues(window->shell, XmNwidthInc, &fontWidth,
2735 XmNheightInc, &fontHeight, XmNbaseWidth, &baseWidth,
2736 XmNbaseHeight, &baseHeight, NULL);
2737 width = (width-baseWidth) / fontWidth;
2738 height = (height-baseHeight) / fontHeight;
2740 /* Write the string */
2741 CreateGeometryString(geomString, x, y, width, height,
2742 XValue | YValue | WidthValue | HeightValue);
2746 ** Xt timer procedure for updating size hints. The new sizes of objects in
2747 ** the window are not ready immediately after adding or removing panes. This
2748 ** is a timer routine to be invoked with a timeout of 0 to give the event
2749 ** loop a chance to finish processing the size changes before reading them
2750 ** out for setting the window manager size hints.
2752 static void wmSizeUpdateProc(XtPointer clientData, XtIntervalId *id)
2754 UpdateWMSizeHints((WindowInfo *)clientData);
2757 #ifdef ROWCOLPATCH
2759 ** There is a bad memory reference in the delete_child method of the
2760 ** RowColumn widget in some Motif versions (so far, just Solaris with Motif
2761 ** 1.2.3) which appears durring the phase 2 destroy of the widget. This
2762 ** patch replaces the method with a call to the Composite widget's
2763 ** delete_child method. The composite delete_child method handles part,
2764 ** but not all of what would have been done by the original method, meaning
2765 ** that this is dangerous and should be used sparingly. Note that
2766 ** patchRowCol is called only in CloseWindow, before the widget is about to
2767 ** be destroyed, and only on systems where the bug has been observed
2769 static void patchRowCol(Widget w)
2771 ((XmRowColumnClassRec *)XtClass(w))->composite_class.delete_child =
2772 patchedRemoveChild;
2774 static void patchedRemoveChild(Widget child)
2776 /* Call composite class method instead of broken row col delete_child
2777 method */
2778 (*((CompositeWidgetClass)compositeWidgetClass)->composite_class.
2779 delete_child) (child);
2781 #endif /* ROWCOLPATCH */
2784 ** Set the backlight character class string
2786 void SetBacklightChars(WindowInfo *window, char *applyBacklightTypes)
2788 int i;
2789 int is_applied = XmToggleButtonGetState(window->backlightCharsItem) ? 1 : 0;
2790 int do_apply = applyBacklightTypes ? 1 : 0;
2792 window->backlightChars = do_apply;
2794 XtFree(window->backlightCharTypes);
2795 if (window->backlightChars &&
2796 (window->backlightCharTypes = XtMalloc(strlen(applyBacklightTypes)+1)))
2797 strcpy(window->backlightCharTypes, applyBacklightTypes);
2798 else
2799 window->backlightCharTypes = NULL;
2801 XtVaSetValues(window->textArea,
2802 textNbacklightCharTypes, window->backlightCharTypes, 0);
2803 for (i=0; i<window->nPanes; i++)
2804 XtVaSetValues(window->textPanes[i],
2805 textNbacklightCharTypes, window->backlightCharTypes, 0);
2806 if (is_applied != do_apply)
2807 XmToggleButtonSetState(window->backlightCharsItem, do_apply, False);
2810 static int sortAlphabetical(const void* k1, const void* k2)
2812 const char* key1 = *(const char**)k1;
2813 const char* key2 = *(const char**)k2;
2814 return strcmp(key1, key2);
2818 * Checks whether a given virtual key binding string is invalid.
2819 * A binding is considered invalid if there are duplicate key entries.
2821 static int virtKeyBindingsAreInvalid(const unsigned char* bindings)
2823 int maxCount = 1, i, count;
2824 const char *pos = (const char*)bindings;
2825 char *copy;
2826 char *pos2, *pos3;
2827 char **keys;
2828 /* First count the number of bindings; bindings are separated by \n
2829 strings. The number of bindings equals the number of \n + 1.
2830 Beware of leading and trailing \n; the number is actually an
2831 upper bound on the number of entries. */
2832 while ((pos = strstr(pos, "\n"))) { ++pos; ++maxCount; }
2834 if (maxCount == 1) return False; /* One binding is always ok */
2836 keys = (char**)malloc(maxCount*sizeof(char*));
2837 copy = XtNewString((const char*)bindings);
2838 i = 0;
2839 pos2 = copy;
2841 count = 0;
2842 while (i<maxCount && pos2 && *pos2)
2844 while (isspace((int) *pos2) || *pos2 == '\n') ++pos2;
2846 if (*pos2 == '!') /* Ignore comment lines */
2848 pos2 = strstr(pos2, "\n");
2849 continue; /* Go to the next line */
2852 if (*pos2)
2854 keys[i++] = pos2;
2855 ++count;
2856 pos3 = strstr(pos2, ":");
2857 if (pos3)
2859 *pos3++ = 0; /* Cut the string and jump to the next entry */
2860 pos2 = pos3;
2862 pos2 = strstr(pos2, "\n");
2866 if (count <= 1)
2868 free(keys);
2869 XtFree(copy);
2870 return False; /* No conflict */
2873 /* Sort the keys and look for duplicates */
2874 qsort((void*)keys, count, sizeof(const char*), sortAlphabetical);
2875 for (i=1; i<count; ++i)
2877 if (!strcmp(keys[i-1], keys[i]))
2879 /* Duplicate detected */
2880 free(keys);
2881 XtFree(copy);
2882 return True;
2885 free(keys);
2886 XtFree(copy);
2887 return False;
2891 * Optionally sanitizes the Motif default virtual key bindings.
2892 * Some applications install invalid bindings (attached to the root window),
2893 * which cause certain keys to malfunction in NEdit.
2894 * Through an X-resource, users can choose whether they want
2895 * - to always keep the existing bindings
2896 * - to override the bindings only if they are invalid
2897 * - to always override the existing bindings.
2900 static Atom virtKeyAtom;
2902 static unsigned char* sanitizeVirtualKeyBindings()
2904 int overrideBindings = GetPrefOverrideVirtKeyBindings();
2905 Window rootWindow;
2906 const char *virtKeyPropName = "_MOTIF_DEFAULT_BINDINGS";
2907 Atom dummyAtom;
2908 int getFmt;
2909 unsigned long dummyULong, nItems;
2910 unsigned char *insaneVirtKeyBindings = NULL;
2912 if (overrideBindings == VIRT_KEY_OVERRIDE_NEVER) return NULL;
2914 virtKeyAtom = XInternAtom(TheDisplay, virtKeyPropName, False);
2915 rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
2917 /* Remove the property, if it exists; we'll restore it later again */
2918 if (XGetWindowProperty(TheDisplay, rootWindow, virtKeyAtom, 0, INT_MAX,
2919 True, XA_STRING, &dummyAtom, &getFmt, &nItems,
2920 &dummyULong, &insaneVirtKeyBindings) != Success
2921 || nItems == 0)
2923 return NULL; /* No binding yet; nothing to do */
2926 if (overrideBindings == VIRT_KEY_OVERRIDE_AUTO)
2928 if (!virtKeyBindingsAreInvalid(insaneVirtKeyBindings))
2930 /* Restore the property immediately; it seems valid */
2931 XChangeProperty(TheDisplay, rootWindow, virtKeyAtom, XA_STRING, 8,
2932 PropModeReplace, insaneVirtKeyBindings,
2933 strlen((const char*)insaneVirtKeyBindings));
2934 XFree((char*)insaneVirtKeyBindings);
2935 return NULL; /* Prevent restoration */
2938 return insaneVirtKeyBindings;
2942 * NEdit should not mess with the bindings installed by other apps, so we
2943 * just restore whatever was installed, if necessary
2945 static void restoreInsaneVirtualKeyBindings(unsigned char *insaneVirtKeyBindings)
2947 if (insaneVirtKeyBindings)
2949 Window rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
2950 /* Restore the root window atom, such that we don't affect
2951 other apps. */
2952 XChangeProperty(TheDisplay, rootWindow, virtKeyAtom, XA_STRING, 8,
2953 PropModeReplace, insaneVirtKeyBindings,
2954 strlen((const char*)insaneVirtKeyBindings));
2955 XFree((char*)insaneVirtKeyBindings);
2960 ** perform generic management on the children (toolbars) of toolBarsForm,
2961 ** a.k.a. statsForm, by setting the form attachment of the managed child
2962 ** widgets per their position/order.
2964 ** You can optionally create separator after a toolbar widget with it's
2965 ** widget name set to "TOOLBAR_SEP", which will appear below the toolbar
2966 ** widget. These seperators will then be managed automatically by this
2967 ** routine along with the toolbars they 'attached' to.
2969 ** It also takes care of the attachment offset settings of the child
2970 ** widgets to keep the border lines of the parent form displayed, so
2971 ** you don't have set them before hand.
2973 ** Note: XtManage/XtUnmange the target child (toolbar) before calling this
2974 ** function.
2976 ** Returns the last toolbar widget managed.
2979 static Widget manageToolBars(Widget toolBarsForm)
2981 Widget topWidget = NULL;
2982 WidgetList children;
2983 int n, nItems=0;
2985 XtVaGetValues(toolBarsForm, XmNchildren, &children,
2986 XmNnumChildren, &nItems, NULL);
2988 for (n=0; n<nItems; n++) {
2989 Widget tbar = children[n];
2991 if (XtIsManaged(tbar)) {
2992 if (topWidget) {
2993 XtVaSetValues(tbar, XmNtopAttachment, XmATTACH_WIDGET,
2994 XmNtopWidget, topWidget,
2995 XmNbottomAttachment, XmATTACH_NONE,
2996 XmNleftOffset, STAT_SHADOW_THICKNESS,
2997 XmNrightOffset, STAT_SHADOW_THICKNESS,
2998 NULL);
3000 else {
3001 /* the very first toolbar on top */
3002 XtVaSetValues(tbar, XmNtopAttachment, XmATTACH_FORM,
3003 XmNbottomAttachment, XmATTACH_NONE,
3004 XmNleftOffset, STAT_SHADOW_THICKNESS,
3005 XmNtopOffset, STAT_SHADOW_THICKNESS,
3006 XmNrightOffset, STAT_SHADOW_THICKNESS,
3007 NULL);
3010 topWidget = tbar;
3012 /* if the next widget is a separator, turn it on */
3013 if (n+1<nItems && !strcmp(XtName(children[n+1]), "TOOLBAR_SEP")) {
3014 XtManageChild(children[n+1]);
3017 else {
3018 /* Remove top attachment to widget to avoid circular dependency.
3019 Attach bottom to form so that when the widget is redisplayed
3020 later, it will trigger the parent form to resize properly as
3021 if the widget is being inserted */
3022 XtVaSetValues(tbar, XmNtopAttachment, XmATTACH_NONE,
3023 XmNbottomAttachment, XmATTACH_FORM, NULL);
3025 /* if the next widget is a separator, turn it off */
3026 if (n+1<nItems && !strcmp(XtName(children[n+1]), "TOOLBAR_SEP")) {
3027 XtUnmanageChild(children[n+1]);
3032 if (topWidget) {
3033 if (strcmp(XtName(topWidget), "TOOLBAR_SEP")) {
3034 XtVaSetValues(topWidget,
3035 XmNbottomAttachment, XmATTACH_FORM,
3036 XmNbottomOffset, STAT_SHADOW_THICKNESS,
3037 NULL);
3039 else {
3040 /* is a separator */
3041 Widget wgt;
3042 XtVaGetValues(topWidget, XmNtopWidget, &wgt, NULL);
3044 /* don't need sep below bottom-most toolbar */
3045 XtUnmanageChild(topWidget);
3046 XtVaSetValues(wgt,
3047 XmNbottomAttachment, XmATTACH_FORM,
3048 XmNbottomOffset, STAT_SHADOW_THICKNESS,
3049 NULL);
3053 return topWidget;
3057 ** Calculate the dimension of the text area, in terms of rows & cols,
3058 ** as if there's only one single text pane in the window.
3060 static void getTextPaneDimension(WindowInfo *window, int *nRows, int *nCols)
3062 Widget hScrollBar;
3063 Dimension hScrollBarHeight, paneHeight;
3064 int marginHeight, marginWidth, totalHeight, fontHeight;
3065 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
3067 /* width is the same for panes */
3068 XtVaGetValues(window->textArea, textNcolumns, nCols, NULL);
3070 /* we have to work out the height, as the text area may have been split */
3071 XtVaGetValues(window->textArea, textNhScrollBar, &hScrollBar,
3072 textNmarginHeight, &marginHeight, textNmarginWidth, &marginWidth,
3073 NULL);
3074 XtVaGetValues(hScrollBar, XmNheight, &hScrollBarHeight, NULL);
3075 XtVaGetValues(window->splitPane, XmNheight, &paneHeight, NULL);
3076 totalHeight = paneHeight - 2*marginHeight -hScrollBarHeight;
3077 fontHeight = textD->ascent + textD->descent;
3078 *nRows = totalHeight/fontHeight;
3082 ** Create a new buffer in the shell window
3084 WindowInfo *CreateBuffer(WindowInfo *shellWindow, const char *name,
3085 char *geometry, int iconic)
3087 Widget pane, text;
3088 WindowInfo *window;
3089 int nCols, nRows;
3091 #ifdef SGI_CUSTOM
3092 char sgi_title[MAXPATHLEN + 14 + SGI_WINDOW_TITLE_LEN] = SGI_WINDOW_TITLE;
3093 #endif
3095 /* Allocate some memory for the new window data structure */
3096 window = (WindowInfo *)XtMalloc(sizeof(WindowInfo));
3097 memcpy(window, shellWindow, sizeof(WindowInfo));
3099 /* initialize window structure */
3100 /* + Schwarzenberg: should a
3101 memset(window, 0, sizeof(WindowInfo));
3102 be added here ?
3104 #if 0
3105 /* share these dialog items with parent shell */
3106 window->replaceDlog = NULL;
3107 window->replaceText = NULL;
3108 window->replaceWithText = NULL;
3109 window->replaceWordToggle = NULL;
3110 window->replaceCaseToggle = NULL;
3111 window->replaceRegexToggle = NULL;
3112 window->findDlog = NULL;
3113 window->findText = NULL;
3114 window->findWordToggle = NULL;
3115 window->findCaseToggle = NULL;
3116 window->findRegexToggle = NULL;
3117 window->replaceMultiFileDlog = NULL;
3118 window->replaceMultiFilePathBtn = NULL;
3119 window->replaceMultiFileList = NULL;
3120 #endif
3121 window->multiFileReplSelected = FALSE;
3122 window->multiFileBusy = FALSE;
3123 window->writableWindows = NULL;
3124 window->nWritableWindows = 0;
3125 window->fileChanged = FALSE;
3126 window->fileMissing = True;
3127 window->fileMode = 0;
3128 window->filenameSet = FALSE;
3129 window->fileFormat = UNIX_FILE_FORMAT;
3130 window->lastModTime = 0;
3131 strcpy(window->filename, name);
3132 window->undo = NULL;
3133 window->redo = NULL;
3134 window->nPanes = 0;
3135 window->autoSaveCharCount = 0;
3136 window->autoSaveOpCount = 0;
3137 window->undoOpCount = 0;
3138 window->undoMemUsed = 0;
3139 CLEAR_ALL_LOCKS(window->lockReasons);
3140 window->indentStyle = GetPrefAutoIndent(PLAIN_LANGUAGE_MODE);
3141 window->autoSave = GetPrefAutoSave();
3142 window->saveOldVersion = GetPrefSaveOldVersion();
3143 window->wrapMode = GetPrefWrap(PLAIN_LANGUAGE_MODE);
3144 window->overstrike = False;
3145 window->showMatchingStyle = GetPrefShowMatching();
3146 window->matchSyntaxBased = GetPrefMatchSyntaxBased();
3147 window->showStats = GetPrefStatsLine();
3148 window->showISearchLine = GetPrefISearchLine();
3149 window->showLineNumbers = GetPrefLineNums();
3150 window->highlightSyntax = GetPrefHighlightSyntax();
3151 window->backlightCharTypes = NULL;
3152 window->backlightChars = GetPrefBacklightChars();
3153 if (window->backlightChars) {
3154 char *cTypes = GetPrefBacklightCharTypes();
3155 if (cTypes && window->backlightChars) {
3156 if ((window->backlightCharTypes = XtMalloc(strlen(cTypes) + 1)))
3157 strcpy(window->backlightCharTypes, cTypes);
3160 window->modeMessageDisplayed = FALSE;
3161 window->ignoreModify = FALSE;
3162 window->windowMenuValid = FALSE;
3163 window->prevOpenMenuValid = FALSE;
3164 window->flashTimeoutID = 0;
3165 window->wasSelected = FALSE;
3166 strcpy(window->fontName, GetPrefFontName());
3167 strcpy(window->italicFontName, GetPrefItalicFontName());
3168 strcpy(window->boldFontName, GetPrefBoldFontName());
3169 strcpy(window->boldItalicFontName, GetPrefBoldItalicFontName());
3170 window->colorDialog = NULL;
3171 window->fontList = GetPrefFontList();
3172 window->italicFontStruct = GetPrefItalicFont();
3173 window->boldFontStruct = GetPrefBoldFont();
3174 window->boldItalicFontStruct = GetPrefBoldItalicFont();
3175 window->fontDialog = NULL;
3176 window->nMarks = 0;
3177 window->markTimeoutID = 0;
3178 window->highlightData = NULL;
3179 window->shellCmdData = NULL;
3180 window->macroCmdData = NULL;
3181 window->smartIndentData = NULL;
3182 window->languageMode = PLAIN_LANGUAGE_MODE;
3183 window->iSearchHistIndex = 0;
3184 window->iSearchStartPos = -1;
3185 window->replaceLastRegexCase = TRUE;
3186 window->replaceLastLiteralCase = FALSE;
3187 window->iSearchLastRegexCase = TRUE;
3188 window->iSearchLastLiteralCase = FALSE;
3189 window->findLastRegexCase = TRUE;
3190 window->findLastLiteralCase = FALSE;
3191 window->bufferTab = NULL;
3193 if (window->fontList == NULL)
3194 XtVaGetValues(shellWindow->statsLine, XmNfontList,
3195 &window->fontList,NULL);
3197 getTextPaneDimension(shellWindow, &nRows, &nCols);
3199 /* Create pane for new buffer. We defer mapping the pane widget
3200 to reduce flickers caused by its resizing when the text area
3201 is added to it */
3202 pane = XtVaCreateWidget("pane",
3203 xmPanedWindowWidgetClass, window->mainWin,
3204 XmNmarginWidth, 0, XmNmarginHeight, 0, XmNseparatorOn, False,
3205 XmNspacing, 3, XmNsashIndent, -2,
3206 XmNmappedWhenManaged, False,
3207 NULL);
3208 XtVaSetValues(window->mainWin, XmNworkWindow, pane, NULL);
3209 XtManageChild(pane);
3210 window->splitPane = pane;
3212 /* buffer/window info should associate with text pane */
3213 XtVaSetValues(pane, XmNuserData, window, NULL);
3215 /* Patch around Motif's most idiotic "feature", that its menu accelerators
3216 recognize Caps Lock and Num Lock as modifiers, and don't trigger if
3217 they are engaged */
3218 AccelLockBugPatch(pane, window->menuBar);
3220 /* Create the first, and most permanent text area (other panes may
3221 be added & removed, but this one will never be removed */
3222 text = createTextArea(pane, window, nRows, nCols,
3223 GetPrefEmTabDist(PLAIN_LANGUAGE_MODE), GetPrefDelimiters(),
3224 GetPrefWrapMargin(), window->showLineNumbers?MIN_LINE_NUM_COLS:0);
3225 XtManageChild(text);
3226 window->textArea = text;
3227 window->lastFocus = text;
3229 /* Set the initial colors from the globals. */
3230 SetColors(window,
3231 GetPrefColorName(TEXT_FG_COLOR ),
3232 GetPrefColorName(TEXT_BG_COLOR ),
3233 GetPrefColorName(SELECT_FG_COLOR),
3234 GetPrefColorName(SELECT_BG_COLOR),
3235 GetPrefColorName(HILITE_FG_COLOR),
3236 GetPrefColorName(HILITE_BG_COLOR),
3237 GetPrefColorName(LINENO_FG_COLOR),
3238 GetPrefColorName(CURSOR_FG_COLOR));
3240 /* map the new buffer pane but keep it hidden */
3241 XLowerWindow(TheDisplay, XtWindow(pane));
3242 XtMapWidget(pane);
3244 /* Create the right button popup menu (note: order is important here,
3245 since the translation for popping up this menu was probably already
3246 added in createTextArea, but CreateBGMenu requires window->textArea
3247 to be set so it can attach the menu to it (because menu shells are
3248 finicky about the kinds of widgets they are attached to)) */
3249 window->bgMenuPane = CreateBGMenu(window);
3251 /* Create the text buffer rather than using the one created automatically
3252 with the text area widget. This is done so the syntax highlighting
3253 modify callback can be called to synchronize the style buffer BEFORE
3254 the text display's callback is called upon to display a modification */
3255 window->buffer = BufCreate();
3256 BufAddModifyCB(window->buffer, SyntaxHighlightModifyCB, window);
3258 /* Attach the buffer to the text widget, and add callbacks for modify */
3259 TextSetBuffer(text, window->buffer);
3260 BufAddModifyCB(window->buffer, modifiedCB, window);
3262 /* Designate the permanent text area as the owner for selections */
3263 HandleXSelections(text);
3265 /* Set the requested hardware tab distance and useTabs in the text buffer */
3266 BufSetTabDistance(window->buffer, GetPrefTabDist(PLAIN_LANGUAGE_MODE));
3267 window->buffer->useTabs = GetPrefInsertTabs();
3269 /* add the window to the global window list, update the Windows menus */
3270 InvalidateWindowMenus();
3271 addToWindowList(window);
3273 window->bufferTab = addBufferTab(window->bufferTabBar, window, name);
3274 ShowBufferTabBar(window, GetPrefTabBar());
3276 #ifdef LESSTIF_VERSION
3277 /* FIXME: Temporary workaround for disappearing-text-window bug
3278 when linking to Lesstif.
3280 After changes is made to statsAreaForm (parent of statsline,
3281 i-search line and tab bar) widget such as enabling/disabling
3282 the statsline, the XmForm widget enclosing the text widget
3283 somehow refused to resize to fit the text widget. Resizing
3284 the shell window or making changes [again] to the statsAreaForm
3285 appeared to bring out the text widget, though doesn't fix it for
3286 the subsequently added buffers. Here we try to do the latter
3287 for all new buffer created. */
3288 if (XtIsManaged(XtParent(window->statsLineForm))) {
3289 XtUnmanageChild(XtParent(window->statsLineForm));
3290 XtManageChild(XtParent(window->statsLineForm));
3292 #endif /* LESSTIF_VERSION */
3294 return window;
3298 ** return the next window/buffer on the tab list.
3300 static WindowInfo *getNextTabWindow(WindowInfo *window, int direction,
3301 int crossWin)
3303 int n, tabPos, nextPos;
3304 WindowInfo **winList, *win;
3305 int nBuf = crossWin? NWindows() : NBuffers(window);
3307 if (nBuf <= 1)
3308 return NULL;
3310 winList = (WindowInfo **)XtMalloc(sizeof(WindowInfo *) * nBuf);
3311 n = nBuf -1;
3312 for (win=WindowList; win && n>=0; win=win->next) {
3313 if (crossWin)
3314 winList[n--] = win;
3315 else if (win->shell == window->shell)
3316 winList[n--] = win;
3319 for (n=0; n<nBuf; n++) {
3320 if (winList[n] == window) {
3321 tabPos = n;
3322 break;
3326 /* calculate index position of next tab */
3327 nextPos = tabPos + direction;
3328 if (nextPos >= nBuf)
3329 nextPos = 0;
3330 else if (nextPos < 0)
3331 nextPos = nBuf - 1;
3333 /* find which window next tab belongs to */
3334 win = winList[nextPos];
3335 XtFree((char *)winList);
3336 return win;
3340 ** return the integer position of a tab in the tabbar it
3341 ** belongs to, or -1 if there's an error, somehow.
3343 static int getTabPosition(Widget tab)
3345 WidgetList tabList;
3346 int i, tabCount;
3347 Widget tabBar = XtParent(tab);
3349 XtVaGetValues(tabBar, XmNtabWidgetList, &tabList,
3350 XmNtabCount, &tabCount, NULL);
3352 for (i=0; i< tabCount; i++) {
3353 if (tab == tabList[i])
3354 return i;
3357 return -1; /* something is wrong! */
3361 ** update the tab label, etc. of a tab per the states of it's buffer.
3363 void RefreshTabState(WindowInfo *win)
3365 XmString s1, tipString;
3366 char labelString[MAXPATHLEN];
3368 sprintf(labelString, "%s%s", win->fileChanged? "*" : "",
3369 win->filename);
3370 s1=XmStringCreateSimple(labelString);
3372 if (GetPrefShowPathInWindowsMenu() && win->filenameSet) {
3373 strcat(labelString, " - ");
3374 strcat(labelString, win->path);
3376 tipString=XmStringCreateSimple(labelString);
3378 XtVaSetValues(win->bufferTab,
3379 XltNbubbleString, tipString,
3380 XmNlabelString, s1,
3381 NULL);
3382 XmStringFree(s1);
3383 XmStringFree(tipString);
3387 ** close all the buffers in a shell window
3389 int CloseAllBufferInWindow(WindowInfo *window)
3391 WindowInfo *win;
3393 if (NBuffers(window) == 1) {
3394 /* the only buffer in the window */
3395 return CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE);
3397 else {
3398 Widget winShell = window->shell;
3399 WindowInfo *topBuffer;
3401 /* close all _modified_ buffers belong to this window */
3402 for (win = WindowList; win; ) {
3403 if (win->shell == winShell && win->fileChanged) {
3404 WindowInfo *next = win->next;
3405 if (!CloseFileAndWindow(win, PROMPT_SBC_DIALOG_RESPONSE))
3406 return False;
3407 win = next;
3409 else
3410 win = win->next;
3413 /* see there's still buffers left in the window */
3414 for (win = WindowList; win; win=win->next)
3415 if (win->shell == winShell)
3416 break;
3418 if (win) {
3419 topBuffer = GetTopBuffer(winShell);
3421 /* close all non-top buffers belong to this window */
3422 for (win = WindowList; win; ) {
3423 if (win->shell == winShell && win != topBuffer) {
3424 WindowInfo *next = win->next;
3425 if (!CloseFileAndWindow(win, PROMPT_SBC_DIALOG_RESPONSE))
3426 return False;
3427 win = next;
3429 else
3430 win = win->next;
3433 /* close the last buffer and its window */
3434 if (!CloseFileAndWindow(topBuffer, PROMPT_SBC_DIALOG_RESPONSE))
3435 return False;
3439 return True;
3442 static void CloseBufferWindow(Widget w, WindowInfo *window, XtPointer callData)
3444 int nBuffers = NBuffers(window);
3446 if (nBuffers == NWindows()) {
3447 /* this is only window, then exit */
3448 XtCallActionProc(WindowList->lastFocus, "exit",
3449 ((XmAnyCallbackStruct *)callData)->event, NULL, 0);
3451 else {
3452 if (nBuffers == 1) {
3453 CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE);
3455 else {
3456 int resp = DialogF(DF_QUES, window->shell, 2, "Close Window",
3457 "Close ALL buffers in this window?", "Close", "Cancel");
3459 if (resp == 1)
3460 CloseAllBufferInWindow(window);
3465 static void cloneTextPane(WindowInfo *window, WindowInfo *orgWin)
3467 short paneHeights[MAX_PANES+1];
3468 int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1];
3469 int horizOffsets[MAX_PANES+1];
3470 int i, focusPane, emTabDist, wrapMargin, lineNumCols, totalHeight=0;
3471 char *delimiters;
3472 Widget text;
3473 selection sel;
3475 /* old window must disown hilite data */
3476 orgWin->highlightData = NULL;
3478 /* transfer the primary selection */
3479 memcpy(&sel, &orgWin->buffer->primary, sizeof(selection));
3481 if (sel.selected) {
3482 if (sel.rectangular)
3483 BufRectSelect(window->buffer, sel.start, sel.end,
3484 sel.rectStart, sel.rectEnd);
3485 else
3486 BufSelect(window->buffer, sel.start, sel.end);
3487 } else
3488 BufUnselect(window->buffer);
3490 /* Record the current heights, scroll positions, and insert positions
3491 of the existing panes, keyboard focus */
3492 focusPane = 0;
3493 for (i=0; i<=orgWin->nPanes; i++) {
3494 text = i==0 ? orgWin->textArea : orgWin->textPanes[i-1];
3495 insertPositions[i] = TextGetCursorPos(text);
3496 XtVaGetValues(XtParent(text), XmNheight, &paneHeights[i], NULL);
3497 totalHeight += paneHeights[i];
3498 TextGetScroll(text, &topLines[i], &horizOffsets[i]);
3499 if (text == orgWin->lastFocus)
3500 focusPane = i;
3503 window->nPanes = orgWin->nPanes;
3505 /* clone split panes, if any */
3506 if (window->nPanes) {
3507 /* Unmanage & remanage the panedWindow so it recalculates pane heights */
3508 XtUnmanageChild(window->splitPane);
3510 /* Create a text widget to add to the pane and set its buffer and
3511 highlight data to be the same as the other panes in the orgWin */
3512 XtVaGetValues(orgWin->textArea, textNemulateTabs, &emTabDist,
3513 textNwordDelimiters, &delimiters, textNwrapMargin, &wrapMargin,
3514 textNlineNumCols, &lineNumCols, NULL);
3516 for(i=0; i<orgWin->nPanes; i++) {
3517 text = createTextArea(window->splitPane, window, 1, 1, emTabDist,
3518 delimiters, wrapMargin, lineNumCols);
3519 TextSetBuffer(text, window->buffer);
3521 if (window->highlightData != NULL)
3522 AttachHighlightToWidget(text, window);
3523 XtManageChild(text);
3524 window->textPanes[i] = text;
3527 /* Set the minimum pane height in the new pane */
3528 UpdateMinPaneHeights(window);
3530 for (i=0; i<=window->nPanes; i++) {
3531 text = i==0 ? window->textArea : window->textPanes[i-1];
3532 setPaneDesiredHeight(containingPane(text), paneHeights[i]);
3535 /* Re-manage panedWindow to recalculate pane heights & reset selection */
3536 XtManageChild(window->splitPane);
3539 /* Reset all of the heights, scroll positions, etc. */
3540 for (i=0; i<=window->nPanes; i++) {
3541 textDisp *textD;
3543 text = i==0 ? window->textArea : window->textPanes[i-1];
3544 TextSetCursorPos(text, insertPositions[i]);
3545 TextSetScroll(text, topLines[i], horizOffsets[i]);
3547 /* dim the cursor */
3548 textD = ((TextWidget)text)->text.textD;
3549 TextDSetCursorStyle(textD, DIM_CURSOR);
3550 TextDUnblankCursor(textD);
3553 /* set the focus pane */
3554 for (i=0; i<=window->nPanes; i++) {
3555 text = i==0 ? window->textArea : window->textPanes[i-1];
3556 if(i == focusPane) {
3557 window->lastFocus = text;
3558 XmProcessTraversal(text, XmTRAVERSE_CURRENT);
3559 break;
3563 /* Update the window manager size hints after the sizes of the panes have
3564 been set (the widget heights are not yet readable here, but they will
3565 be by the time the event loop gets around to running this timer proc) */
3566 XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0,
3567 wmSizeUpdateProc, window);
3571 ** Refresh the menu entries per the settings of the
3572 ** top/active buffer.
3574 void RefreshMenuToggleStates(WindowInfo *window)
3576 WindowInfo *win;
3578 /* File menu */
3579 XtSetSensitive(window->printSelItem, window->wasSelected);
3581 /* Edit menu */
3582 XtSetSensitive(window->undoItem, window->undo != NULL);
3583 XtSetSensitive(window->redoItem, window->redo != NULL);
3584 XtSetSensitive(window->printSelItem, window->wasSelected);
3585 XtSetSensitive(window->cutItem, window->wasSelected);
3586 XtSetSensitive(window->copyItem, window->wasSelected);
3587 XtSetSensitive(window->delItem, window->wasSelected);
3589 /* Preferences menu */
3590 XmToggleButtonSetState(window->statsLineItem, window->showStats, False);
3591 XmToggleButtonSetState(window->iSearchLineItem, window->showISearchLine, False);
3592 XmToggleButtonSetState(window->lineNumsItem, window->showLineNumbers, False);
3593 XmToggleButtonSetState(window->highlightItem, window->highlightSyntax, False);
3594 XtSetSensitive(window->highlightItem, window->languageMode != PLAIN_LANGUAGE_MODE);
3595 XmToggleButtonSetState(window->backlightCharsItem, window->backlightChars, False);
3596 XmToggleButtonSetState(window->saveLastItem, window->saveOldVersion, False);
3597 XmToggleButtonSetState(window->autoSaveItem, window->autoSave, False);
3598 XmToggleButtonSetState(window->overtypeModeItem, window->overstrike, False);
3599 XmToggleButtonSetState(window->matchSyntaxBasedItem, window->matchSyntaxBased, False);
3600 XmToggleButtonSetState(window->readOnlyItem, IS_USER_LOCKED(window->lockReasons), False);
3602 XtSetSensitive(window->smartIndentItem,
3603 SmartIndentMacrosAvailable(LanguageModeName(window->languageMode)));
3605 SetAutoIndent(window, window->indentStyle);
3606 SetAutoWrap(window, window->wrapMode);
3607 SetShowMatching(window, window->showMatchingStyle);
3608 SetLanguageMode(window, window->languageMode, FALSE);
3610 /* Windows Menu */
3611 XtSetSensitive(window->splitWindowItem, window->nPanes < MAX_PANES);
3612 XtSetSensitive(window->closePaneItem, window->nPanes > 0);
3613 XtSetSensitive(window->detachBufferItem, NBuffers(window)>1);
3615 for (win=WindowList; win; win=win->next)
3616 if (win->shell != window->shell)
3617 break;
3618 XtSetSensitive(window->attachBufferItem, win != NULL);
3622 ** Refresh the various settings/state of the shell window per the
3623 ** settings of the top/active buffer.
3625 static void refreshBufferMenuBar(WindowInfo *window)
3627 RefreshMenuToggleStates(window);
3629 /* Add/remove language specific menu items */
3630 #ifndef VMS
3631 InvalidateShellMenus(window);
3632 #endif
3633 InvalidateMacroMenus(window);
3634 UpdateBGMenu(window);
3636 /* refresh selection-sensitive menus */
3637 DimSelectionDepUserMenuItems(window, window->wasSelected);
3640 static void setBufferSharedPref(WindowInfo *window, WindowInfo *lastwin)
3642 window->showTabBar = lastwin->showTabBar;
3643 window->showStats = lastwin->showStats;
3644 window->showISearchLine = lastwin->showISearchLine;
3648 ** remember the last active buffer
3650 WindowInfo *MarkLastBuffer(WindowInfo *window)
3652 WindowInfo *prev = lastBuffer;
3654 if (window)
3655 lastBuffer = window;
3657 return prev;
3661 ** remember the active buffer
3663 WindowInfo *MarkActiveBuffer(WindowInfo *window)
3665 WindowInfo *prev = focusInBuffer;
3667 if (window)
3668 focusInBuffer = window;
3670 return prev;
3674 ** Bring up the next window by tab order
3676 void NextBuffer(WindowInfo *window)
3678 WindowInfo *win;
3680 if (WindowList->next == NULL)
3681 return;
3683 win = getNextTabWindow(window, 1, GetPrefGlobalTabNavigate());
3684 if (win == NULL)
3685 return;
3687 if (window->shell == win->shell)
3688 RaiseBuffer(win);
3689 else
3690 RaiseBufferWindow(win);
3694 ** Bring up the previous window by tab order
3696 void PreviousBuffer(WindowInfo *window)
3698 WindowInfo *win;
3700 if (WindowList->next == NULL)
3701 return;
3703 win = getNextTabWindow(window, -1, GetPrefGlobalTabNavigate());
3704 if (win == NULL)
3705 return;
3707 if (window->shell == win->shell)
3708 RaiseBuffer(win);
3709 else
3710 RaiseBufferWindow(win);
3714 ** Bring up the last active window
3716 void ToggleBuffer(WindowInfo *window)
3718 WindowInfo *win;
3720 for(win = WindowList; win; win=win->next)
3721 if (lastBuffer == win)
3722 break;
3724 if (!win)
3725 return;
3727 if (window->shell == win->shell)
3728 RaiseBuffer(win);
3729 else
3730 RaiseBufferWindow(win);
3735 ** make sure window is alive is kicking
3737 int IsValidWindow(WindowInfo *window)
3739 WindowInfo *win;
3741 for(win = WindowList; win; win=win->next)
3742 if (window == win)
3743 return True;
3746 return False;
3750 ** raise the buffer and its shell window
3752 void RaiseBufferWindow(WindowInfo *window)
3754 RaiseBuffer(window);
3755 RaiseShellWindow(window->shell);
3759 ** raise the buffer in its shell window
3761 void RaiseBuffer(WindowInfo *window)
3763 WindowInfo *win, *lastwin;
3765 if (!GetPrefBufferMode())
3766 return;
3768 lastwin = MarkActiveBuffer(window);
3769 if (lastwin != window && IsValidWindow(lastwin))
3770 MarkLastBuffer(lastwin);
3772 if (!GetPrefBufferMode() || !window || !WindowList)
3773 return;
3775 /* buffer already active? */
3776 XtVaGetValues(window->mainWin, XmNuserData, &win, NULL);
3778 if (win == window)
3779 return;
3781 /* refresh shared menu items */
3782 setBufferSharedPref(window, win);
3784 /* set the buffer as active */
3785 XtVaSetValues(window->mainWin, XmNuserData, window, NULL);
3787 /* show the new top buffer */
3788 XtVaSetValues(window->mainWin, XmNworkWindow, window->splitPane, NULL);
3789 XtManageChild(window->splitPane);
3790 XRaiseWindow(TheDisplay, XtWindow(window->splitPane));
3792 /* set tab as active */
3793 XmLFolderSetActiveTab(window->bufferTabBar,
3794 getTabPosition(window->bufferTab), False);
3796 /* set keyboard focus. Must be done before unmanaging previous
3797 top buffer, else lastFocus will be reset to textArea */
3798 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
3800 /* we only manage the top buffer, else the next time a buffer
3801 is raised again, it's textpane might not resize properly.
3802 Also, somehow (bug?) XtUnmanageChild() doesn't hide the
3803 splitPane, which obscure lower part of the statsform where
3804 we toggle its components, so we need to put the buffer at
3805 the back */
3806 XLowerWindow(TheDisplay, XtWindow(win->splitPane));
3807 XtUnmanageChild(win->splitPane);
3809 /* now refresh window state/info. RefreshBufferWindowState()
3810 has a lot of work to do, so we update the screen first so
3811 the buffers appear to switch immediately */
3812 XmUpdateDisplay(window->splitPane);
3813 RefreshBufferWindowState(window);
3815 UpdateWMSizeHints(window);
3818 WindowInfo* GetTopBuffer(Widget w)
3820 WindowInfo *window = WidgetToWindow(w);
3822 return WidgetToWindow(window->shell);
3825 Boolean IsTopBuffer(const WindowInfo *window)
3827 return window == GetTopBuffer(window->shell)? True : False;
3830 void DeleteBuffer(WindowInfo *window)
3832 if (!GetPrefBufferMode() || !window)
3833 return;
3835 XtDestroyWidget(window->splitPane);
3839 ** clone a buffer into the other.
3841 static void cloneBuffer(WindowInfo *window, WindowInfo *orgWin)
3843 char *orgBuffer;
3844 char *params[4];
3846 strcpy(window->path, orgWin->path);
3847 strcpy(window->filename, orgWin->filename);
3849 ShowLineNumbers(window, orgWin->showLineNumbers);
3851 /* copy the buffer */
3852 window->ignoreModify = True;
3853 orgBuffer = BufGetAll(orgWin->buffer);
3854 BufSetAll(window->buffer, orgBuffer);
3855 window->ignoreModify = False;
3856 XtFree(orgBuffer);
3858 /* transfer text fonts */
3859 params[0] = orgWin->fontName;
3860 params[1] = orgWin->italicFontName;
3861 params[2] = orgWin->boldFontName;
3862 params[3] = orgWin->boldItalicFontName;
3863 XtCallActionProc(window->textArea, "set_fonts", NULL, params, 4);
3865 SetBacklightChars(window, orgWin->backlightCharTypes);
3867 /* recycle the hilite data */
3868 window->languageMode = orgWin->languageMode;
3869 window->highlightData = orgWin->highlightData;
3870 if (window->highlightData != NULL)
3871 AttachHighlightToWidget(window->textArea, window);
3873 /* clone original buffer's states */
3874 window->filenameSet = orgWin->filenameSet;
3875 window->fileFormat = orgWin->fileFormat;
3876 window->lastModTime = orgWin->lastModTime;
3877 window->fileChanged = orgWin->fileChanged;
3878 window->fileMissing = orgWin->fileMissing;
3879 window->lockReasons = orgWin->lockReasons;
3880 window->nPanes = orgWin->nPanes;
3881 window->autoSaveCharCount = orgWin->autoSaveCharCount;
3882 window->autoSaveOpCount = orgWin->autoSaveOpCount;
3883 window->undoOpCount = orgWin->undoOpCount;
3884 window->undoMemUsed = orgWin->undoMemUsed;
3885 window->lockReasons = orgWin->lockReasons;
3886 window->autoSave = orgWin->autoSave;
3887 window->saveOldVersion = orgWin->saveOldVersion;
3888 window->wrapMode = orgWin->wrapMode;
3889 window->overstrike = orgWin->overstrike;
3890 window->showMatchingStyle = orgWin->showMatchingStyle;
3891 window->matchSyntaxBased = orgWin->matchSyntaxBased;
3892 window->highlightSyntax = orgWin->highlightSyntax;
3893 #if 0
3894 window->showStats = orgWin->showStats;
3895 window->showISearchLine = orgWin->showISearchLine;
3896 window->showLineNumbers = orgWin->showLineNumbers;
3897 window->modeMessageDisplayed = orgWin->modeMessageDisplayed;
3898 window->ignoreModify = orgWin->ignoreModify;
3899 window->windowMenuValid = orgWin->windowMenuValid;
3900 window->prevOpenMenuValid = orgWin->prevOpenMenuValid;
3901 window->flashTimeoutID = orgWin->flashTimeoutID;
3902 window->wasSelected = orgWin->wasSelected;
3903 strcpy(window->fontName, orgWin->fontName);
3904 strcpy(window->italicFontName, orgWin->italicFontName);
3905 strcpy(window->boldFontName, orgWin->boldFontName);
3906 strcpy(window->boldItalicFontName, orgWin->boldItalicFontName);
3907 window->fontList = orgWin->fontList;
3908 window->italicFontStruct = orgWin->italicFontStruct;
3909 window->boldFontStruct = orgWin->boldFontStruct;
3910 window->boldItalicFontStruct = orgWin->boldItalicFontStruct;
3911 window->markTimeoutID = orgWin->markTimeoutID;
3912 window->highlightData = orgWin->highlightData;
3913 window->shellCmdData = orgWin->shellCmdData;
3914 window->macroCmdData = orgWin->macroCmdData;
3915 window->smartIndentData = orgWin->smartIndentData;
3916 #endif
3917 window->iSearchHistIndex = orgWin->iSearchHistIndex;
3918 window->iSearchStartPos = orgWin->iSearchStartPos;
3919 window->replaceLastRegexCase = orgWin->replaceLastRegexCase;
3920 window->replaceLastLiteralCase = orgWin->replaceLastLiteralCase;
3921 window->iSearchLastRegexCase = orgWin->iSearchLastRegexCase;
3922 window->iSearchLastLiteralCase = orgWin->iSearchLastLiteralCase;
3923 window->findLastRegexCase = orgWin->findLastRegexCase;
3924 window->findLastLiteralCase = orgWin->findLastLiteralCase;
3926 /* copy the text/split panes settings, cursor pos & selection */
3927 cloneTextPane(window, orgWin);
3929 /* copy undo & redo list */
3930 window->undo = cloneUndoItems(orgWin->undo);
3931 window->redo = cloneUndoItems(orgWin->redo);
3933 /* copy bookmarks */
3934 window->nMarks = orgWin->nMarks;
3935 memcpy(&window->markTable, &orgWin->markTable,
3936 sizeof(Bookmark)*window->nMarks);
3938 /* kick start the auto-indent engine */
3939 window->indentStyle = NO_AUTO_INDENT;
3940 SetAutoIndent(window, orgWin->indentStyle);
3942 /* synchronize window state to this buffer */
3943 RefreshBufferWindowState(window);
3946 static UndoInfo *cloneUndoItems(UndoInfo *orgList)
3948 UndoInfo *head = NULL, *undo, *clone, *last = NULL;
3950 for (undo = orgList; undo; undo = undo->next) {
3951 clone = (UndoInfo *)XtMalloc(sizeof(UndoInfo));
3952 memcpy(clone, undo, sizeof(UndoInfo));
3954 if (undo->oldText) {
3955 clone->oldText = XtMalloc(strlen(undo->oldText)+1);
3956 strcpy(clone->oldText, undo->oldText);
3958 clone->next = NULL;
3960 if (last)
3961 last->next = clone;
3962 else
3963 head = clone;
3965 last = clone;
3968 return head;
3972 ** return number of buffers own by this shell window
3974 int NBuffers(WindowInfo *window)
3976 WindowInfo *win;
3977 int nBuffer = 0;
3979 if (!GetPrefBufferMode())
3980 return 1;
3982 for (win = WindowList; win; win = win->next) {
3983 if (win->shell == window->shell)
3984 nBuffer++;
3987 return nBuffer;
3991 ** refresh window state for this buffer
3993 void RefreshBufferWindowState(WindowInfo *window)
3995 if (!GetPrefBufferMode() || !IsTopBuffer(window))
3996 return;
3998 UpdateStatsLine(window);
3999 UpdateWindowReadOnly(window);
4000 UpdateWindowTitle(window);
4002 /* show/hide statsline as need */
4003 if (window->modeMessageDisplayed && !XtIsManaged(window->statsLineForm))
4004 showStats(window, True);
4005 else if (!window->showStats && XtIsManaged(window->statsLineForm))
4006 showStats(window, False);
4007 else if (window->showStats && !XtIsManaged(window->statsLineForm))
4008 showStats(window, True);
4010 /* signal if macro/shell is running */
4011 if (window->shellCmdData || window->macroCmdData) {
4012 if (window->showStats || window->modeMessageDisplayed) {
4013 if (window->shellCmdData)
4014 MakeShellBanner(window);
4015 else if (window->macroCmdData)
4016 MakeMacroBanner(window);
4019 BeginWait(window->shell);
4021 else {
4022 EndWait(window->shell);
4025 /* we need to force the statsline to reveal itself */
4026 if (XtIsManaged(window->statsLineForm)) {
4027 XmTextSetCursorPosition(window->statsLine, 0); /* start of line */
4028 XmTextSetCursorPosition(window->statsLine, 9000); /* end of line */
4031 XmUpdateDisplay(window->statsLine);
4032 refreshBufferMenuBar(window);
4036 ** spin off the buffer to a new window
4038 WindowInfo *DetachBuffer(WindowInfo *window)
4040 WindowInfo *win, *cloneWin;
4041 char *dim, geometry[MAX_GEOM_STRING_LEN];
4043 if (NBuffers(window) < 2)
4044 return NULL;
4046 /* raise another buffer in the same shell window */
4047 win = replacementBuffer(window);
4048 RaiseBuffer(win);
4050 /* create new window in roughly the size of original window,
4051 to reduce flicker when the window is resized later */
4052 getGeometryString(window, geometry);
4053 dim = strtok(geometry, "+-");
4054 cloneWin = CreateWindow(window->filename, dim, False);
4056 /* these settings should follow the detached buffer.
4057 must be done before cloning window, else the height
4058 of split panes may not come out correctly */
4059 ShowISearchLine(cloneWin, window->showISearchLine);
4060 ShowStatsLine(cloneWin, window->showStats);
4062 /* clone the buffer & its pref settings */
4063 cloneBuffer(cloneWin, window);
4065 /* remove the buffer from the old window */
4066 window->fileChanged = False;
4067 CloseFileAndWindow(window, NO_SBC_DIALOG_RESPONSE);
4069 /* some menu states might have changed when deleting buffer */
4070 RefreshBufferWindowState(win);
4072 /* this should keep the new buffer window fresh */
4073 RefreshBufferWindowState(cloneWin);
4074 RefreshTabState(cloneWin);
4076 return cloneWin;
4080 ** attach (move) a buffer to an other window.
4082 ** the attaching buffer will inherit the window settings from
4083 ** its new hosts, i.e. the window size, stats and isearch lines.
4085 WindowInfo *AttachBuffer(WindowInfo *toWindow, WindowInfo *window)
4087 WindowInfo *win, *cloneWin;
4089 /* raise another buffer in the window of attaching buffer */
4090 for (win = WindowList; win; win = win->next) {
4091 if (win->shell == window->shell && window != win)
4092 break;
4095 if (win)
4096 RaiseBuffer(win);
4097 else
4098 XtUnmapWidget(window->shell);
4100 /* relocate the buffer to target window */
4101 cloneWin = CreateBuffer(toWindow, window->filename, NULL, False);
4102 cloneBuffer(cloneWin, window);
4104 /* remove the buffer from the old window */
4105 window->fileChanged = False;
4106 CloseFileAndWindow(window, NO_SBC_DIALOG_RESPONSE);
4108 /* some menu states might have changed when deleting buffer */
4109 if (win) {
4110 RefreshBufferWindowState(win);
4113 /* this should keep the new buffer window fresh */
4114 RaiseBufferWindow(cloneWin);
4115 RefreshTabState(cloneWin);
4117 return cloneWin;
4120 static void attachBufferCB(Widget dialog, WindowInfo *parentWin,
4121 XtPointer call_data)
4123 XmSelectionBoxCallbackStruct *cbs = (XmSelectionBoxCallbackStruct *) call_data;
4124 DoneWithAttachBufferDialog = cbs->reason;
4128 ** present dialog to selecting target window for attaching
4129 ** buffers. Return immediately if there is only one shell window.
4131 void AttachBufferDialog(Widget parent)
4133 WindowInfo *parentWin = WidgetToWindow(parent);
4134 WindowInfo *win, *attachWin, **shellWinList;
4135 int i, nList=0, nWindows=0, ac;
4136 char tmpStr[MAXPATHLEN+50];
4137 Widget dialog, listBox, attachAllOption;
4138 XmString *list = NULL;
4139 XmString popupTitle, s1;
4140 Arg csdargs[20];
4141 int *position_list, position_count;
4143 /* create the window list */
4144 nWindows = NWindows();
4145 list = (XmStringTable) XtMalloc(nWindows * sizeof(XmString *));
4146 shellWinList = (WindowInfo **) XtMalloc(nWindows * sizeof(WindowInfo *));
4148 for (win=WindowList; win; win=win->next) {
4149 if (win->shell == parentWin->shell)
4150 continue;
4152 if (!IsTopBuffer(win))
4153 continue;
4155 sprintf(tmpStr, "%s%s",
4156 win->filenameSet? win->path : "", win->filename);
4158 list[nList] = XmStringCreateSimple(tmpStr);
4159 shellWinList[nList] = win;
4160 nList++;
4163 if (!nList) {
4164 XtFree((char *)list);
4165 return;
4168 sprintf(tmpStr, "Attach %s to:", parentWin->filename);
4169 popupTitle = XmStringCreateSimple(tmpStr);
4170 ac = 0;
4171 XtSetArg(csdargs[ac], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); ac++;
4172 XtSetArg(csdargs[ac], XmNlistLabelString, popupTitle); ac++;
4173 XtSetArg(csdargs[ac], XmNlistItems, list); ac++;
4174 XtSetArg(csdargs[ac], XmNlistItemCount, nList); ac++;
4175 XtSetArg(csdargs[ac], XmNvisibleItemCount, 12); ac++;
4176 XtSetArg(csdargs[ac], XmNautoUnmanage, False); ac++;
4177 dialog = CreateSelectionDialog(parent,"attachBuffer",csdargs,ac);
4178 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT));
4179 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
4180 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_SELECTION_LABEL));
4181 XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)attachBufferCB, parentWin);
4182 XtAddCallback(dialog, XmNapplyCallback, (XtCallbackProc)attachBufferCB, parentWin);
4183 XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)attachBufferCB, parentWin);
4184 XmStringFree(popupTitle);
4186 /* free the window list */
4187 for (i=0; i<nList; i++)
4188 XmStringFree(list[i]);
4189 XtFree((char *)list);
4191 /* create the option box for attaching all buffers */
4192 s1 = MKSTRING("Attach all buffers in window");
4193 attachAllOption = XtVaCreateWidget("attachAll",
4194 xmToggleButtonWidgetClass, dialog,
4195 XmNlabelString, s1,
4196 XmNalignment, XmALIGNMENT_BEGINNING,
4197 NULL);
4198 XmStringFree(s1);
4200 if (NBuffers(parentWin) >1)
4201 XtManageChild(attachAllOption);
4203 /* only one buffer in the window */
4204 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_APPLY_BUTTON));
4206 s1 = MKSTRING("Attach");
4207 XtVaSetValues (dialog, XmNokLabelString, s1, NULL);
4208 XmStringFree(s1);
4210 /* default to the first window on the list */
4211 listBox = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);
4212 XmListSelectPos(listBox, 1, True);
4213 /* show the dialog */
4214 DoneWithAttachBufferDialog = 0;
4215 ManageDialogCenteredOnPointer(dialog);
4216 while (!DoneWithAttachBufferDialog)
4217 XtAppProcessEvent(XtWidgetToApplicationContext(parent), XtIMAll);
4219 /* get window to attach buffer */
4220 XmListGetSelectedPos(listBox, &position_list, &position_count);
4221 attachWin = shellWinList[position_list[0]-1];
4222 XtFree((char *)position_list);
4224 /* now attach buffer(s) */
4225 if (DoneWithAttachBufferDialog == XmCR_OK) {
4226 /* attach top (active) buffer */
4227 if (XmToggleButtonGetState(attachAllOption)) {
4228 /* attach all buffers */
4229 for (win = WindowList; win; ) {
4230 if (win != parentWin && win->shell == parentWin->shell) {
4231 WindowInfo *next = win->next;
4232 AttachBuffer(attachWin, win);
4233 win = next;
4235 else
4236 win = win->next;
4239 /* top buffer is last to attach */
4240 AttachBuffer(attachWin, parentWin);
4242 else {
4243 AttachBuffer(attachWin, parentWin);
4247 XtFree((char *)shellWinList);
4248 XtDestroyWidget(dialog);
4251 static void hideTooltip(Widget tab)
4253 Widget tooltip = XtNameToWidget(tab, "*BubbleShell");
4255 if (tooltip)
4256 XtPopdown(tooltip);
4260 ** callback to close-tab button.
4262 static void closeTabCB(Widget w, Widget mainWin, caddr_t callData)
4264 CloseFileAndWindow(GetTopBuffer(mainWin), PROMPT_SBC_DIALOG_RESPONSE);
4268 ** callback to tab (button).
4270 static void clickTabCB(Widget w, XtPointer *clientData, XtPointer callData)
4272 hideTooltip(w);
4276 ** callback to tab (tabbar) that raise the buffer.
4278 static void raiseTabCB(Widget w, XtPointer *clientData, XtPointer callData)
4280 XmLFolderCallbackStruct *cbs = (XmLFolderCallbackStruct *)callData;
4281 WidgetList tabList;
4282 Widget tab;
4284 XtVaGetValues(w, XmNtabWidgetList, &tabList, NULL);
4285 tab = tabList[cbs->pos];
4286 RaiseBuffer(TabToWindow(tab));
4289 static Widget containingPane(Widget w)
4291 /* The containing pane used to simply be the first parent, but with
4292 the introduction of an XmFrame, it's the grandparent. */
4293 return XtParent(XtParent(w));
4296 /* Workaround for bug in OpenMotif 2.1 and 2.2. If you have an active tear-off
4297 ** menu from a TopLevelShell that is a child of an ApplicationShell, and then
4298 ** close the parent window, Motif crashes. The problem doesn't
4299 ** happen if you close the tear-offs first, so, we programatically close them
4300 ** before destroying the shell widget.
4302 static void closeAllPopupsFor(Widget shell)
4304 #ifndef LESSTIF_VERSION
4305 /* Doesn't happen in LessTif. The tear-off menus are popup-children of
4306 * of the TopLevelShell there, which just works. Motif wants to make
4307 * them popup-children of the ApplicationShell, where it seems to get
4308 * into trouble. */
4310 Widget app = XtParent(shell);
4311 int i;
4313 for (i = 0; i < app->core.num_popups; i++) {
4314 Widget pop = app->core.popup_list[i];
4315 Widget shellFor;
4317 XtVaGetValues(pop, XtNtransientFor, &shellFor, NULL);
4318 if (shell == shellFor)
4319 _XmDismissTearOff(pop, NULL, NULL);
4321 #endif