Remove hardcoded visual resources for tab widget, in favor of resources that
[nedit.git] / source / window.c
blob6b6276931dc54cde191de3b74205f8c62197c0be
1 static const char CVSID[] = "$Id: window.c,v 1.91 2003/12/28 17:25:32 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 /* Initial minimum height of a pane. Just a fallback in case setPaneMinHeight
116 (which may break in a future release) is not available */
117 #define PANE_MIN_HEIGHT 39
119 /* Thickness of 3D border around statistics and/or incremental search areas
120 below the main menu bar */
121 #define STAT_SHADOW_THICKNESS 1
123 /* buffer tabs configuration */
124 #define MIN_TAB_SLOTS 3
126 /* bitmap data for the close-tab button */
127 #define close_width 11
128 #define close_height 11
129 static unsigned char close_bits[] = {
130 0x00, 0x00, 0x00, 0x00, 0x8c, 0x01, 0xdc, 0x01, 0xf8, 0x00, 0x70, 0x00,
131 0xf8, 0x00, 0xdc, 0x01, 0x8c, 0x01, 0x00, 0x00, 0x00, 0x00};
133 static WindowInfo *getNextTabWindow(WindowInfo *window, int direction,
134 int crossWin);
135 static Widget addBufferTab(Widget folder, WindowInfo *window, const char *string);
136 static int getTabPosition(Widget tab);
137 static void setTabCloseButtonImage(Widget button);
138 static Widget manageToolBars(Widget toolBarsForm);
139 static void CloseBufferWindow(Widget w, WindowInfo *window, XtPointer callData);
140 static void closeTabCB(Widget w, Widget mainWin, caddr_t callData);
141 static void clickTabCB(Widget w, XtPointer *clientData, XtPointer callData);
142 static void raiseTabCB(Widget w, XtPointer *clientData, XtPointer callData);
143 static Widget createTextArea(Widget parent, WindowInfo *window, int rows,
144 int cols, int emTabDist, char *delimiters, int wrapMargin,
145 int lineNumCols);
146 static void showStats(WindowInfo *window, int state);
147 static void showISearch(WindowInfo *window, int state);
148 static void showStatsForm(WindowInfo *window, int state);
149 static void addToWindowList(WindowInfo *window);
150 static void removeFromWindowList(WindowInfo *window);
151 static void focusCB(Widget w, WindowInfo *window, XtPointer callData);
152 static void modifiedCB(int pos, int nInserted, int nDeleted, int nRestyled,
153 char *deletedText, void *cbArg);
154 static void movedCB(Widget w, WindowInfo *window, XtPointer callData);
155 static void dragStartCB(Widget w, WindowInfo *window, XtPointer callData);
156 static void dragEndCB(Widget w, WindowInfo *window, dragEndCBStruct *callData);
157 static void closeCB(Widget w, WindowInfo *window, XtPointer callData);
158 static void saveYourselfCB(Widget w, WindowInfo *window, XtPointer callData);
159 static void setPaneDesiredHeight(Widget w, int height);
160 static void setPaneMinHeight(Widget w, int min);
161 static void addWindowIcon(Widget shell);
162 static void wmSizeUpdateProc(XtPointer clientData, XtIntervalId *id);
163 static void getGeometryString(WindowInfo *window, char *geomString);
164 #ifdef ROWCOLPATCH
165 static void patchRowCol(Widget w);
166 static void patchedRemoveChild(Widget child);
167 #endif
168 static unsigned char* sanitizeVirtualKeyBindings();
169 static int sortAlphabetical(const void* k1, const void* k2);
170 static int virtKeyBindingsAreInvalid(const unsigned char* bindings);
171 static void restoreInsaneVirtualKeyBindings(unsigned char* bindings);
172 static void setBufferSharedPref(WindowInfo *window, WindowInfo *lastwin);
173 static void refreshBufferMenuBar(WindowInfo *window);
174 static void cloneBuffer(WindowInfo *window, WindowInfo *orgWin);
175 static void cloneTextPane(WindowInfo *window, WindowInfo *orgWin);
176 static UndoInfo *cloneUndoItems(UndoInfo *orgList);
177 static Widget containingPane(Widget w);
179 static WindowInfo *focusInBuffer = NULL; /* where we are now */
180 static WindowInfo *lastBuffer = NULL; /* where we came from */
181 static int DoneWithAttachBufferDialog;
185 ** Create a new editor window
187 WindowInfo *CreateWindow(const char *name, char *geometry, int iconic)
189 Widget winShell, mainWin, menuBar, pane, text, stats, statsAreaForm;
190 Widget iSearchLabel, closeTabBtn, tabForm, form;
191 WindowInfo *window;
192 Pixel bgpix, fgpix;
193 Arg al[20];
194 int ac;
195 XmString s1;
196 WindowInfo *win;
197 #ifdef SGI_CUSTOM
198 char sgi_title[MAXPATHLEN + 14 + SGI_WINDOW_TITLE_LEN] = SGI_WINDOW_TITLE;
199 #endif
200 char newGeometry[MAX_GEOM_STRING_LEN];
201 unsigned int rows, cols;
202 int x, y, bitmask;
203 static Atom wmpAtom, syAtom = 0;
204 static int firstTime = True;
205 unsigned char* invalidBindings = NULL;
206 XmFontList fontList;
207 int fontWidth, tabWidth;
208 XFontStruct *fs;
210 if (firstTime)
212 invalidBindings = sanitizeVirtualKeyBindings();
213 firstTime = False;
216 /* Allocate some memory for the new window data structure */
217 window = (WindowInfo *)XtMalloc(sizeof(WindowInfo));
219 /* initialize window structure */
220 /* + Schwarzenberg: should a
221 memset(window, 0, sizeof(WindowInfo));
222 be added here ?
224 window->replaceDlog = NULL;
225 window->replaceText = NULL;
226 window->replaceWithText = NULL;
227 window->replaceWordToggle = NULL;
228 window->replaceCaseToggle = NULL;
229 window->replaceRegexToggle = NULL;
230 window->findDlog = NULL;
231 window->findText = NULL;
232 window->findWordToggle = NULL;
233 window->findCaseToggle = NULL;
234 window->findRegexToggle = NULL;
235 window->replaceMultiFileDlog = NULL;
236 window->replaceMultiFilePathBtn = NULL;
237 window->replaceMultiFileList = NULL;
238 window->multiFileReplSelected = FALSE;
239 window->multiFileBusy = FALSE;
240 window->writableWindows = NULL;
241 window->nWritableWindows = 0;
242 window->fileChanged = FALSE;
243 window->fileMode = 0;
244 window->filenameSet = FALSE;
245 window->fileFormat = UNIX_FILE_FORMAT;
246 window->lastModTime = 0;
247 window->fileMissing = True;
248 strcpy(window->filename, name);
249 window->undo = NULL;
250 window->redo = NULL;
251 window->nPanes = 0;
252 window->autoSaveCharCount = 0;
253 window->autoSaveOpCount = 0;
254 window->undoOpCount = 0;
255 window->undoMemUsed = 0;
256 CLEAR_ALL_LOCKS(window->lockReasons);
257 window->indentStyle = GetPrefAutoIndent(PLAIN_LANGUAGE_MODE);
258 window->autoSave = GetPrefAutoSave();
259 window->saveOldVersion = GetPrefSaveOldVersion();
260 window->wrapMode = GetPrefWrap(PLAIN_LANGUAGE_MODE);
261 window->overstrike = False;
262 window->showMatchingStyle = GetPrefShowMatching();
263 window->matchSyntaxBased = GetPrefMatchSyntaxBased();
264 window->showTabBar = GetPrefTabBar() ? (GetPrefTabBarHideOne()? 0:1) : 0;
265 window->showStats = GetPrefStatsLine();
266 window->showISearchLine = GetPrefISearchLine();
267 window->showLineNumbers = GetPrefLineNums();
268 window->highlightSyntax = GetPrefHighlightSyntax();
269 window->backlightCharTypes = NULL;
270 window->backlightChars = GetPrefBacklightChars();
271 if (window->backlightChars) {
272 char *cTypes = GetPrefBacklightCharTypes();
273 if (cTypes && window->backlightChars) {
274 if ((window->backlightCharTypes = XtMalloc(strlen(cTypes) + 1)))
275 strcpy(window->backlightCharTypes, cTypes);
278 window->modeMessageDisplayed = FALSE;
279 window->ignoreModify = FALSE;
280 window->windowMenuValid = FALSE;
281 window->prevOpenMenuValid = FALSE;
282 window->flashTimeoutID = 0;
283 window->fileClosedAtom = None;
284 window->wasSelected = FALSE;
286 strcpy(window->fontName, GetPrefFontName());
287 strcpy(window->italicFontName, GetPrefItalicFontName());
288 strcpy(window->boldFontName, GetPrefBoldFontName());
289 strcpy(window->boldItalicFontName, GetPrefBoldItalicFontName());
290 window->colorDialog = NULL;
291 window->fontList = GetPrefFontList();
292 window->italicFontStruct = GetPrefItalicFont();
293 window->boldFontStruct = GetPrefBoldFont();
294 window->boldItalicFontStruct = GetPrefBoldItalicFont();
295 window->fontDialog = NULL;
296 window->nMarks = 0;
297 window->markTimeoutID = 0;
298 window->highlightData = NULL;
299 window->shellCmdData = NULL;
300 window->macroCmdData = NULL;
301 window->smartIndentData = NULL;
302 window->languageMode = PLAIN_LANGUAGE_MODE;
303 window->iSearchHistIndex = 0;
304 window->iSearchStartPos = -1;
305 window->replaceLastRegexCase = TRUE;
306 window->replaceLastLiteralCase = FALSE;
307 window->iSearchLastRegexCase = TRUE;
308 window->iSearchLastLiteralCase = FALSE;
309 window->findLastRegexCase = TRUE;
310 window->findLastLiteralCase = FALSE;
311 window->bufferTab = NULL;
313 /* If window geometry was specified, split it apart into a window position
314 component and a window size component. Create a new geometry string
315 containing the position component only. Rows and cols are stripped off
316 because we can't easily calculate the size in pixels from them until the
317 whole window is put together. Note that the preference resource is only
318 for clueless users who decide to specify the standard X geometry
319 application resource, which is pretty useless because width and height
320 are the same as the rows and cols preferences, and specifying a window
321 location will force all the windows to pile on top of one another */
322 if (geometry == NULL || geometry[0] == '\0')
323 geometry = GetPrefGeometry();
324 if (geometry == NULL || geometry[0] == '\0') {
325 rows = GetPrefRows();
326 cols = GetPrefCols();
327 newGeometry[0] = '\0';
328 } else {
329 bitmask = XParseGeometry(geometry, &x, &y, &cols, &rows);
330 if (bitmask == 0)
331 fprintf(stderr, "Bad window geometry specified: %s\n", geometry);
332 else {
333 if (!(bitmask & WidthValue))
334 cols = GetPrefCols();
335 if (!(bitmask & HeightValue))
336 rows = GetPrefRows();
338 CreateGeometryString(newGeometry, x, y, 0, 0,
339 bitmask & ~(WidthValue | HeightValue));
342 /* Create a new toplevel shell to hold the window */
343 ac = 0;
344 #ifdef SGI_CUSTOM
345 strcat(sgi_title, name);
346 XtSetArg(al[ac], XmNtitle, sgi_title); ac++;
347 XtSetArg(al[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
348 if (strncmp(name, "Untitled", 8) == 0) {
349 XtSetArg(al[ac], XmNiconName, APP_NAME); ac++;
350 } else {
351 XtSetArg(al[ac], XmNiconName, name); ac++;
353 #else
354 XtSetArg(al[ac], XmNtitle, name); ac++;
355 XtSetArg(al[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
356 XtSetArg(al[ac], XmNiconName, name); ac++;
357 #endif
358 XtSetArg(al[ac], XmNgeometry, newGeometry[0]=='\0'?NULL:newGeometry); ac++;
359 XtSetArg(al[ac], XmNinitialState,
360 iconic ? IconicState : NormalState); ac++;
362 winShell = CreateWidget(TheAppShell, "textShell",
363 topLevelShellWidgetClass, al, ac);
364 window->shell = winShell;
366 #ifdef EDITRES
367 XtAddEventHandler (winShell, (EventMask)0, True,
368 (XtEventHandler)_XEditResCheckMessages, NULL);
369 #endif /* EDITRES */
371 #ifndef SGI_CUSTOM
372 addWindowIcon(winShell);
373 #endif
375 /* Create a MainWindow to manage the menubar and text area, set the
376 userData resource to be used by WidgetToWindow to recover the
377 window pointer from the widget id of any of the window's widgets */
378 XtSetArg(al[ac], XmNuserData, window); ac++;
379 mainWin = XmCreateMainWindow(winShell, "main", al, ac);
380 window->mainWin = mainWin;
381 XtManageChild(mainWin);
383 /* The statsAreaForm holds the stats line and the I-Search line. */
384 statsAreaForm = XtVaCreateWidget("statsAreaForm",
385 xmFormWidgetClass, mainWin,
386 XmNmarginWidth, STAT_SHADOW_THICKNESS,
387 XmNmarginHeight, STAT_SHADOW_THICKNESS,
388 /* XmNautoUnmanage, False, */
389 NULL);
390 if (window->showTabBar || window->showISearchLine ||
391 window->showStats)
392 XtManageChild(statsAreaForm);
394 /* NOTE: due to a bug in openmotif 2.1.30, NEdit used to crash when
395 the i-search bar was active, and the i-search text widget was focussed,
396 and the window's width was resized to nearly zero.
397 In theory, it is possible to avoid this by imposing a minimum
398 width constraint on the nedit windows, but that width would have to
399 be at least 30 characters, which is probably unacceptable.
400 Amazingly, adding a top offset of 1 pixel to the toggle buttons of
401 the i-search bar, while keeping the the top offset of the text widget
402 to 0 seems to avoid avoid the crash. */
404 window->iSearchForm = XtVaCreateWidget("iSearchForm",
405 xmFormWidgetClass, statsAreaForm,
406 XmNshadowThickness, 0,
407 XmNleftAttachment, XmATTACH_FORM,
408 XmNleftOffset, STAT_SHADOW_THICKNESS,
409 XmNtopAttachment, XmATTACH_FORM,
410 XmNtopOffset, STAT_SHADOW_THICKNESS,
411 XmNrightAttachment, XmATTACH_FORM,
412 XmNrightOffset, STAT_SHADOW_THICKNESS,
413 XmNbottomOffset, STAT_SHADOW_THICKNESS, NULL);
414 if(window->showISearchLine)
415 XtManageChild(window->iSearchForm);
416 iSearchLabel = XtVaCreateManagedWidget("iSearchLabel",
417 xmLabelWidgetClass, window->iSearchForm,
418 XmNlabelString, s1=XmStringCreateSimple("Find:"),
419 XmNmarginHeight, 0,
420 XmNleftAttachment, XmATTACH_FORM,
421 XmNleftOffset, 5,
422 XmNtopAttachment, XmATTACH_FORM,
423 XmNtopOffset, 1, /* see openmotif note above, for aligment
424 with toggle buttons below */
425 XmNbottomAttachment, XmATTACH_FORM, NULL);
426 XmStringFree(s1);
428 /* Disable keyboard traversal of the toggle buttons. We were doing
429 this previously by forcing the keyboard focus back to the text
430 widget whenever a toggle changed. That causes an ugly focus flash
431 on screen. It's better just not to go there in the first place.
432 Plus, if the user really wants traversal, it's an X resource so it
433 can be enabled without too much pain and suffering. */
435 window->iSearchCaseToggle = XtVaCreateManagedWidget("iSearchCaseToggle",
436 xmToggleButtonWidgetClass, window->iSearchForm,
437 XmNlabelString, s1=XmStringCreateSimple("Case"),
438 XmNset, GetPrefSearch() == SEARCH_CASE_SENSE
439 || GetPrefSearch() == SEARCH_REGEX
440 || GetPrefSearch() == SEARCH_CASE_SENSE_WORD,
441 XmNtopAttachment, XmATTACH_FORM,
442 XmNbottomAttachment, XmATTACH_FORM,
443 XmNtopOffset, 1, /* see openmotif note above */
444 XmNrightAttachment, XmATTACH_FORM,
445 XmNmarginHeight, 0,
446 XmNtraversalOn, False,
447 NULL);
448 XmStringFree(s1);
450 window->iSearchRegexToggle = XtVaCreateManagedWidget("iSearchREToggle",
451 xmToggleButtonWidgetClass, window->iSearchForm,
452 XmNlabelString, s1=XmStringCreateSimple("RegExp"),
453 XmNset, GetPrefSearch() == SEARCH_REGEX_NOCASE
454 || GetPrefSearch() == SEARCH_REGEX,
455 XmNtopAttachment, XmATTACH_FORM,
456 XmNbottomAttachment, XmATTACH_FORM,
457 XmNtopOffset, 1, /* see openmotif note above */
458 XmNrightAttachment, XmATTACH_WIDGET,
459 XmNrightWidget, window->iSearchCaseToggle,
460 XmNmarginHeight, 0,
461 XmNtraversalOn, False,
462 NULL);
463 XmStringFree(s1);
465 window->iSearchRevToggle = XtVaCreateManagedWidget("iSearchRevToggle",
466 xmToggleButtonWidgetClass, window->iSearchForm,
467 XmNlabelString, s1=XmStringCreateSimple("Rev"),
468 XmNset, False,
469 XmNtopAttachment, XmATTACH_FORM,
470 XmNbottomAttachment, XmATTACH_FORM,
471 XmNtopOffset, 1, /* see openmotif note above */
472 XmNrightAttachment, XmATTACH_WIDGET,
473 XmNrightWidget, window->iSearchRegexToggle,
474 XmNmarginHeight, 0,
475 XmNtraversalOn, False,
476 NULL);
477 XmStringFree(s1);
479 window->iSearchText = XtVaCreateManagedWidget("iSearchText",
480 xmTextWidgetClass, window->iSearchForm,
481 XmNmarginHeight, 1,
482 XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
483 XmNleftAttachment, XmATTACH_WIDGET,
484 XmNleftWidget, iSearchLabel,
485 XmNrightAttachment, XmATTACH_WIDGET,
486 XmNrightWidget, window->iSearchRevToggle,
487 XmNrightOffset, 5,
488 XmNtopAttachment, XmATTACH_FORM,
489 XmNtopOffset, 0, /* see openmotif note above */
490 XmNbottomAttachment, XmATTACH_FORM,
491 XmNbottomOffset, 0, NULL);
492 RemapDeleteKey(window->iSearchText);
494 SetISearchTextCallbacks(window);
496 /* create the buffer tab bar */
497 tabForm = XtVaCreateWidget("tabForm",
498 xmFormWidgetClass, statsAreaForm,
499 XmNmarginHeight, 0,
500 XmNmarginWidth, 0,
501 XmNspacing, 0,
502 XmNresizable, False,
503 XmNleftAttachment, XmATTACH_FORM,
504 XmNrightAttachment, XmATTACH_FORM,
505 XmNshadowThickness, 0, NULL);
507 /* button to close top buffer */
508 closeTabBtn = XtVaCreateManagedWidget("closeTabBtn",
509 xmPushButtonWidgetClass, tabForm,
510 XmNmarginHeight, 0,
511 XmNmarginWidth, 0,
512 XmNhighlightThickness, 0,
513 XmNlabelType, XmPIXMAP,
514 XmNshadowThickness, 1,
515 XmNtraversalOn, False,
516 XmNrightAttachment, XmATTACH_FORM,
517 XmNrightOffset, 5,
518 XmNbottomAttachment, XmATTACH_FORM,
519 XmNbottomOffset, 3,
520 NULL);
522 XtAddCallback(closeTabBtn, XmNactivateCallback, (XtCallbackProc)closeTabCB,
523 mainWin);
524 setTabCloseButtonImage(closeTabBtn);
526 window->bufferTabBar = XtVaCreateManagedWidget("tabBar",
527 xmlFolderWidgetClass, tabForm,
528 XmNresizePolicy, XmRESIZE_PACK,
529 XmNleftAttachment, XmATTACH_FORM,
530 XmNleftOffset, 1,
531 XmNrightAttachment, XmATTACH_WIDGET,
532 XmNrightWidget, closeTabBtn,
533 XmNrightOffset, 10,
534 XmNbottomAttachment, XmATTACH_FORM,
535 XmNbottomOffset, 0,
536 XmNtopAttachment, XmATTACH_FORM,
537 NULL);
539 /* create an unmanaged composite widget to get the folder
540 widget to hide the 3D shadow for the manager area.
541 Note: this works only on the patched XmLFolder widget */
542 form = XtVaCreateWidget("form",
543 xmFormWidgetClass, window->bufferTabBar,
544 XmNheight, 1,
545 XmNresizable, False,
546 NULL);
548 XtAddCallback(window->bufferTabBar, XmNactivateCallback,
549 (XtCallbackProc)raiseTabCB, NULL);
551 window->bufferTab = addBufferTab(window->bufferTabBar, window, name);
553 /* set minimum space for filename on tabs */
554 XtVaGetValues(window->bufferTab, XmNfontList, &fontList, NULL);
555 fs = GetDefaultFontStruct(fontList);
556 fontWidth = (fs->min_bounds.width + fs->max_bounds.width)/2;
557 tabWidth = fontWidth * 20 + 5;
558 if (tabWidth < 150)
559 tabWidth = 150;
560 XtVaSetValues(window->bufferTabBar, XmNmaxTabWidth, tabWidth, NULL);
562 if (window->showTabBar)
563 XtManageChild(tabForm);
565 /* put a separating line below the tab bar */
566 XtVaCreateManagedWidget("TOOLBAR_SEP", xmSeparatorWidgetClass,
567 statsAreaForm,
568 XmNseparatorType, XmSHADOW_ETCHED_IN,
569 XmNmargin, 0,
570 XmNleftAttachment, XmATTACH_FORM,
571 XmNrightAttachment, XmATTACH_FORM,
572 NULL);
574 /* A form to hold the stats line text and line/col widgets */
575 window->statsLineForm = XtVaCreateWidget("statsLineForm",
576 xmFormWidgetClass, statsAreaForm,
577 XmNshadowThickness, 0,
578 XmNtopAttachment, window->showISearchLine ?
579 XmATTACH_WIDGET : XmATTACH_FORM,
580 XmNtopWidget, window->iSearchForm,
581 XmNrightAttachment, XmATTACH_FORM,
582 XmNleftAttachment, XmATTACH_FORM,
583 XmNbottomAttachment, XmATTACH_FORM,
584 XmNresizable, False, /* */
585 NULL);
587 /* A separate display of the line/column number */
588 window->statsLineColNo = XtVaCreateManagedWidget("statsLineColNo",
589 xmLabelWidgetClass, window->statsLineForm,
590 XmNlabelString, s1=XmStringCreateSimple("L: --- C: ---"),
591 XmNshadowThickness, 0,
592 XmNmarginHeight, 2,
593 XmNtraversalOn, False,
594 XmNtopAttachment, XmATTACH_FORM,
595 XmNrightAttachment, XmATTACH_FORM,
596 XmNbottomAttachment, XmATTACH_FORM, /* */
597 NULL);
598 XmStringFree(s1);
600 /* Create file statistics display area. Using a text widget rather than
601 a label solves a layout problem with the main window, which messes up
602 if the label is too long (we would need a resize callback to control
603 the length when the window changed size), and allows users to select
604 file names and line numbers. Colors are copied from parent
605 widget, because many users and some system defaults color text
606 backgrounds differently from other widgets. */
607 XtVaGetValues(statsAreaForm, XmNbackground, &bgpix, NULL);
608 XtVaGetValues(statsAreaForm, XmNforeground, &fgpix, NULL);
609 stats = XtVaCreateManagedWidget("statsLine",
610 xmTextWidgetClass, window->statsLineForm,
611 XmNbackground, bgpix,
612 XmNforeground, fgpix,
613 XmNshadowThickness, 0,
614 XmNhighlightColor, bgpix,
615 XmNhighlightThickness, 0, /* must be zero, for OM (2.1.30) to
616 aligns tatsLineColNo & statsLine */
617 XmNmarginHeight, 1, /* == statsLineColNo.marginHeight - 1,
618 to align with statsLineColNo */
619 XmNscrollHorizontal, False,
620 XmNeditMode, XmSINGLE_LINE_EDIT,
621 XmNeditable, False,
622 XmNtraversalOn, False,
623 XmNcursorPositionVisible, False,
624 XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, /* */
625 XmNtopWidget, window->statsLineColNo,
626 XmNleftAttachment, XmATTACH_FORM,
627 XmNrightAttachment, XmATTACH_WIDGET,
628 XmNrightWidget, window->statsLineColNo,
629 XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, /* */
630 XmNbottomWidget, window->statsLineColNo,
631 XmNrightOffset, 3,
632 NULL);
633 window->statsLine = stats;
635 /* Manage the statsLineForm */
636 if(window->showStats)
637 XtManageChild(window->statsLineForm);
639 /* If the fontList was NULL, use the magical default provided by Motif,
640 since it must have worked if we've gotten this far */
641 if (window->fontList == NULL)
642 XtVaGetValues(stats, XmNfontList, &window->fontList, NULL);
644 manageToolBars(statsAreaForm);
646 /* Create the menu bar */
647 menuBar = CreateMenuBar(mainWin, window);
648 window->menuBar = menuBar;
649 XtManageChild(menuBar);
651 /* Create paned window to manage split window behavior */
652 pane = XtVaCreateManagedWidget("pane", xmPanedWindowWidgetClass, mainWin,
653 XmNseparatorOn, False,
654 XmNspacing, 3, XmNsashIndent, -2, NULL);
655 window->splitPane = pane;
656 XmMainWindowSetAreas(mainWin, menuBar, statsAreaForm, NULL, NULL, pane);
658 /* buffer/window info should associate with text pane */
659 XtVaSetValues(pane, XmNuserData, window, NULL);
661 /* Patch around Motif's most idiotic "feature", that its menu accelerators
662 recognize Caps Lock and Num Lock as modifiers, and don't trigger if
663 they are engaged */
664 AccelLockBugPatch(pane, window->menuBar);
666 /* Create the first, and most permanent text area (other panes may
667 be added & removed, but this one will never be removed */
668 text = createTextArea(pane, window, rows,cols,
669 GetPrefEmTabDist(PLAIN_LANGUAGE_MODE), GetPrefDelimiters(),
670 GetPrefWrapMargin(), window->showLineNumbers?MIN_LINE_NUM_COLS:0);
671 XtManageChild(text);
672 window->textArea = text;
673 window->lastFocus = text;
675 /* Set the initial colors from the globals. */
676 SetColors(window,
677 GetPrefColorName(TEXT_FG_COLOR ),
678 GetPrefColorName(TEXT_BG_COLOR ),
679 GetPrefColorName(SELECT_FG_COLOR),
680 GetPrefColorName(SELECT_BG_COLOR),
681 GetPrefColorName(HILITE_FG_COLOR),
682 GetPrefColorName(HILITE_BG_COLOR),
683 GetPrefColorName(LINENO_FG_COLOR),
684 GetPrefColorName(CURSOR_FG_COLOR));
686 /* Create the right button popup menu (note: order is important here,
687 since the translation for popping up this menu was probably already
688 added in createTextArea, but CreateBGMenu requires window->textArea
689 to be set so it can attach the menu to it (because menu shells are
690 finicky about the kinds of widgets they are attached to)) */
691 window->bgMenuPane = CreateBGMenu(window);
693 /* Create the text buffer rather than using the one created automatically
694 with the text area widget. This is done so the syntax highlighting
695 modify callback can be called to synchronize the style buffer BEFORE
696 the text display's callback is called upon to display a modification */
697 window->buffer = BufCreate();
698 BufAddModifyCB(window->buffer, SyntaxHighlightModifyCB, window);
700 /* Attach the buffer to the text widget, and add callbacks for modify */
701 TextSetBuffer(text, window->buffer);
702 BufAddModifyCB(window->buffer, modifiedCB, window);
704 /* Designate the permanent text area as the owner for selections */
705 HandleXSelections(text);
707 /* Set the requested hardware tab distance and useTabs in the text buffer */
708 BufSetTabDistance(window->buffer, GetPrefTabDist(PLAIN_LANGUAGE_MODE));
709 window->buffer->useTabs = GetPrefInsertTabs();
711 /* add the window to the global window list, update the Windows menus */
712 addToWindowList(window);
713 InvalidateWindowMenus();
715 /* realize all of the widgets in the new window */
716 RealizeWithoutForcingPosition(winShell);
717 XmProcessTraversal(text, XmTRAVERSE_CURRENT);
719 /* Make close command in window menu gracefully prompt for close */
720 AddMotifCloseCallback(winShell, (XtCallbackProc)closeCB, window);
722 #ifndef NO_SESSION_RESTART
723 /* Add wm protocol callback for making nedit restartable by session
724 managers. Doesn't yet handle multiple-desktops or iconifying right. */
725 if (syAtom == 0) {
726 wmpAtom = XmInternAtom(TheDisplay, "WM_PROTOCOLS", FALSE);
727 syAtom = XmInternAtom(TheDisplay, "WM_SAVE_YOURSELF", FALSE);
729 XmAddProtocolCallback(winShell, wmpAtom, syAtom,
730 (XtCallbackProc)saveYourselfCB, (XtPointer)window);
731 #endif
733 /* Make window resizing work in nice character heights */
734 UpdateWMSizeHints(window);
736 /* Set the minimum pane height for the initial text pane */
737 UpdateMinPaneHeights(window);
739 restoreInsaneVirtualKeyBindings(invalidBindings);
741 /* create persistant dialog upfront, shared by all buffers
742 in a common shell window */
743 CreateFindDlog(window->shell, window);
744 CreateReplaceDlog(window->shell, window);
745 CreateReplaceMultiFileDlog(window);
747 /* tell the world there's a new window to move in */
748 if (GetPrefBufferMode()) {
749 for(win=WindowList; win; win=win->next) {
750 if (IsTopBuffer(win))
751 XtSetSensitive(win->attachBufferItem,
752 NBuffers(window) < NWindows());
756 return window;
760 ** add a new tab to the tab bar, where the [new] buffer belongs.
762 static Widget addBufferTab(Widget folder, WindowInfo *window, const char *string)
764 Widget tooltipLabel, tab;
765 XmString s1;
767 s1 = XmStringCreateSimple((char *)string);
768 tab = XtVaCreateManagedWidget("tab",
769 xrwsBubbleButtonWidgetClass, folder,
770 XmNmarginWidth, 0,
771 XmNmarginHeight, 0,
772 XmNalignment, XmALIGNMENT_BEGINNING,
773 XmNlabelString, s1,
774 XltNbubbleString, s1,
775 XltNshowBubble, GetPrefToolTips(),
776 XltNautoParkBubble, True,
777 XltNslidingBubble, False,
778 /* XltNdelay, 800,*/
779 /* XltNbubbleDuration, 8000,*/
780 NULL);
781 XmStringFree(s1);
783 XtAddCallback(tab, XmNactivateCallback,
784 (XtCallbackProc)clickTabCB, NULL);
786 /* BubbleButton simply use reversed video for tooltips,
787 we try to use the 'standard' color */
788 tooltipLabel = XtNameToWidget(tab, "*BubbleLabel");
789 XtVaSetValues(tooltipLabel,
790 XmNbackground, AllocateColor(tab, GetPrefTooltipBgColor()),
791 XmNforeground, AllocateColor(tab, NEDIT_DEFAULT_FG),
792 NULL);
794 /* put borders around tooltip. BubbleButton use
795 transientShellWidgetClass as tooltip shell, which
796 came without borders */
797 XtVaSetValues(XtParent(tooltipLabel), XmNborderWidth, 1, NULL);
799 return tab;
803 ** return the tab next to the closing tab.
805 ** tab close left to right (Mozilla 1.1 behavior), i.e
806 ** the 'right-hand' tab get the new seat, unless the
807 ** closing buffer is the right-most tab
809 static WindowInfo *replacementBuffer(WindowInfo *window)
811 int n, tabPos, nextPos;
812 WindowInfo **winList, *win;
813 int nBuf = NBuffers(window);
815 if (nBuf <= 1)
816 return NULL;
818 winList = (WindowInfo **)XtMalloc(sizeof(WindowInfo *) * nBuf);
819 n = nBuf -1;
820 for (win=WindowList; win && n>=0; win=win->next) {
821 if (win->shell == window->shell)
822 winList[n--] = win;
825 for (n=0; n<nBuf; n++) {
826 if (winList[n] == window) {
827 tabPos = n;
828 break;
832 nextPos = tabPos == nBuf-1? tabPos-1 : tabPos+1;
833 win = winList[nextPos];
834 XtFree((char*) winList);
835 return win;
839 * find which buffer/window a tab belongs to
841 WindowInfo *TabToWindow(Widget tab)
843 WindowInfo *win;
844 for (win=WindowList; win; win=win->next) {
845 if (win->bufferTab == tab)
846 return win;
849 return NULL;
853 ** Close an editor window
855 void CloseWindow(WindowInfo *window)
857 int keepWindow;
858 char name[MAXPATHLEN];
859 WindowInfo *win, *topBuf = NULL, *nextBuf = NULL;
861 /* turn off the tear-off menus for Shell & Macro to
862 minimized visual disturbance if these menus get
863 updated somehow, especial when CloseWindow() is
864 called from within macros. */
865 if (IsTopBuffer(window)) {
866 if (!XmIsMenuShell(XtParent(window->macroMenuPane)))
867 XtPopdown(XtParent(window->macroMenuPane));
869 if (!XmIsMenuShell(XtParent(window->shellMenuPane)))
870 XtPopdown(XtParent(window->shellMenuPane));
873 /* Free smart indent macro programs */
874 EndSmartIndent(window);
876 /* Clean up macro references to the doomed window. If a macro is
877 executing, stop it. If macro is calling this (closing its own
878 window), leave the window alive until the macro completes */
879 keepWindow = !MacroWindowCloseActions(window);
881 #ifndef VMS
882 /* Kill shell sub-process and free related memory */
883 AbortShellCommand(window);
884 #endif /*VMS*/
886 /* Unload the default tips files for this language mode if necessary */
887 UnloadLanguageModeTipsFile(window);
889 /* If a window is closed while it is on the multi-file replace dialog
890 list of any other window (or even the same one), we must update those
891 lists or we end up with dangling references. Normally, there can
892 be only one of those dialogs at the same time (application modal),
893 but LessTif doesn't even (always) honor application modalness, so
894 there can be more than one dialog. */
895 RemoveFromMultiReplaceDialog(window);
897 /* Destroy the file closed property for this file */
898 DeleteFileClosedProperty(window);
900 /* if this is the last window, or must be kept alive temporarily because
901 it's running the macro calling us, don't close it, make it Untitled */
902 if (keepWindow || (WindowList == window && window->next == NULL)) {
903 window->filename[0] = '\0';
904 UniqueUntitledName(name);
905 CLEAR_ALL_LOCKS(window->lockReasons);
906 window->fileMode = 0;
907 strcpy(window->filename, name);
908 strcpy(window->path, "");
909 window->ignoreModify = TRUE;
910 BufSetAll(window->buffer, "");
911 window->ignoreModify = FALSE;
912 window->nMarks = 0;
913 window->filenameSet = FALSE;
914 window->fileMissing = TRUE;
915 window->fileChanged = FALSE;
916 window->fileFormat = UNIX_FILE_FORMAT;
917 window->lastModTime = 0;
918 StopHighlighting(window);
919 EndSmartIndent(window);
920 UpdateWindowTitle(window);
921 UpdateWindowReadOnly(window);
922 XtSetSensitive(window->closeItem, FALSE);
923 XtSetSensitive(window->readOnlyItem, TRUE);
924 XmToggleButtonSetState(window->readOnlyItem, FALSE, FALSE);
925 ClearUndoList(window);
926 ClearRedoList(window);
927 XmTextSetString(window->statsLine, ""); /* resets scroll pos of stats
928 line from long file names */
929 UpdateStatsLine(window);
930 DetermineLanguageMode(window, True);
931 RefreshTabState(window);
932 return;
935 /* Free syntax highlighting patterns, if any. w/o redisplaying */
936 FreeHighlightingData(window);
938 /* remove the buffer modification callbacks so the buffer will be
939 deallocated when the last text widget is destroyed */
940 BufRemoveModifyCB(window->buffer, modifiedCB, window);
941 BufRemoveModifyCB(window->buffer, SyntaxHighlightModifyCB, window);
943 #ifdef ROWCOLPATCH
944 patchRowCol(window->menuBar);
945 #endif
947 /* free the undo and redo lists */
948 ClearUndoList(window);
949 ClearRedoList(window);
951 /* close window, or buffer */
952 if (NBuffers(window) > 1) {
953 if (MacroRunWindow() && MacroRunWindow() != window &&
954 MacroRunWindow()->shell == window->shell) {
955 nextBuf = MacroRunWindow();
957 else if (IsTopBuffer(window)) {
958 /* if this is the active buffer, then we need
959 to find its successor */
960 nextBuf = replacementBuffer(window);
962 else
963 topBuf = GetTopBuffer(window->shell);
966 /* remove the window from the global window list, update window menus */
967 removeFromWindowList(window);
968 InvalidateWindowMenus();
970 /* remove tab from tab bar */
971 XtDestroyWidget(window->bufferTab);
973 if (nextBuf) {
974 /* show the replacement buffer */
975 RaiseBuffer(nextBuf);
976 ShowWindowTabBar(nextBuf);
978 else if (topBuf) {
979 /* refresh tabbar after deleting a non-top buffer */
980 ShowWindowTabBar(topBuf);
983 /* tell the world there's one less window to move in */
984 if (GetPrefBufferMode()) {
985 for(win=WindowList; win; win=win->next) {
986 if (IsTopBuffer(win))
987 XtSetSensitive(win->attachBufferItem,
988 NBuffers(WindowList) < NWindows());
992 /* destroy the buffer pane, or window */
993 if (nextBuf || topBuf) {
994 DeleteBuffer(window);
996 else {
997 /* remove and deallocate all of the widgets associated with window */
998 XtFree(window->backlightCharTypes); /* we made a copy earlier on */
999 XtDestroyWidget(window->shell);
1002 /* deallocate the window data structure */
1003 XtFree((char *)window);
1006 void ShowWindowTabBar(WindowInfo *window)
1008 if (GetPrefTabBar()) {
1009 if (GetPrefTabBarHideOne())
1010 ShowBufferTabBar(window, NBuffers(window)>1);
1011 else
1012 ShowBufferTabBar(window, True);
1014 else
1015 ShowBufferTabBar(window, False);
1019 ** Check if there is already a window open for a given file
1021 WindowInfo *FindWindowWithFile(const char *name, const char *path)
1023 WindowInfo *w;
1025 for (w=WindowList; w!=NULL; w=w->next) {
1026 if (!strcmp(w->filename, name) && !strcmp(w->path, path)) {
1027 return w;
1030 return NULL;
1034 ** Add another independently scrollable window pane to the current window,
1035 ** splitting the pane which currently has keyboard focus.
1037 void SplitWindow(WindowInfo *window)
1039 short paneHeights[MAX_PANES+1];
1040 int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1];
1041 int horizOffsets[MAX_PANES+1];
1042 int i, focusPane, emTabDist, wrapMargin, lineNumCols, totalHeight=0;
1043 char *delimiters;
1044 Widget text;
1045 textDisp *textD, *newTextD;
1047 /* Don't create new panes if we're already at the limit */
1048 if (window->nPanes >= MAX_PANES)
1049 return;
1051 /* Record the current heights, scroll positions, and insert positions
1052 of the existing panes, keyboard focus */
1053 focusPane = 0;
1054 for (i=0; i<=window->nPanes; i++) {
1055 text = i==0 ? window->textArea : window->textPanes[i-1];
1056 insertPositions[i] = TextGetCursorPos(text);
1057 XtVaGetValues(containingPane(text),XmNheight,&paneHeights[i],NULL);
1058 totalHeight += paneHeights[i];
1059 TextGetScroll(text, &topLines[i], &horizOffsets[i]);
1060 if (text == window->lastFocus)
1061 focusPane = i;
1064 /* Unmanage & remanage the panedWindow so it recalculates pane heights */
1065 XtUnmanageChild(window->splitPane);
1067 /* Create a text widget to add to the pane and set its buffer and
1068 highlight data to be the same as the other panes in the window */
1069 XtVaGetValues(window->textArea, textNemulateTabs, &emTabDist,
1070 textNwordDelimiters, &delimiters, textNwrapMargin, &wrapMargin,
1071 textNlineNumCols, &lineNumCols, NULL);
1072 text = createTextArea(window->splitPane, window, 1, 1, emTabDist,
1073 delimiters, wrapMargin, lineNumCols);
1074 TextSetBuffer(text, window->buffer);
1075 if (window->highlightData != NULL)
1076 AttachHighlightToWidget(text, window);
1077 if (window->backlightChars)
1079 XtVaSetValues(text, textNbacklightCharTypes,
1080 window->backlightCharTypes, 0);
1082 XtManageChild(text);
1083 window->textPanes[window->nPanes++] = text;
1085 /* Fix up the colors */
1086 textD = ((TextWidget)window->textArea)->text.textD;
1087 newTextD = ((TextWidget)text)->text.textD;
1088 XtVaSetValues(text,
1089 XmNforeground, textD->fgPixel,
1090 XmNbackground, textD->bgPixel,
1091 NULL);
1092 TextDSetColors( newTextD, textD->fgPixel, textD->bgPixel,
1093 textD->selectFGPixel, textD->selectBGPixel, textD->highlightFGPixel,
1094 textD->highlightBGPixel, textD->lineNumFGPixel,
1095 textD->cursorFGPixel );
1097 /* Set the minimum pane height in the new pane */
1098 UpdateMinPaneHeights(window);
1100 /* adjust the heights, scroll positions, etc., to split the focus pane */
1101 for (i=window->nPanes; i>focusPane; i--) {
1102 insertPositions[i] = insertPositions[i-1];
1103 paneHeights[i] = paneHeights[i-1];
1104 topLines[i] = topLines[i-1];
1105 horizOffsets[i] = horizOffsets[i-1];
1107 paneHeights[focusPane] = paneHeights[focusPane]/2;
1108 paneHeights[focusPane+1] = paneHeights[focusPane];
1110 for (i=0; i<=window->nPanes; i++) {
1111 text = i==0 ? window->textArea : window->textPanes[i-1];
1112 setPaneDesiredHeight(containingPane(text), paneHeights[i]);
1115 /* Re-manage panedWindow to recalculate pane heights & reset selection */
1116 if (IsTopBuffer(window))
1117 XtManageChild(window->splitPane);
1119 /* Reset all of the heights, scroll positions, etc. */
1120 for (i=0; i<=window->nPanes; i++) {
1121 text = i==0 ? window->textArea : window->textPanes[i-1];
1122 TextSetCursorPos(text, insertPositions[i]);
1123 TextSetScroll(text, topLines[i], horizOffsets[i]);
1124 setPaneDesiredHeight(containingPane(text),
1125 totalHeight/(window->nPanes+1));
1127 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
1129 /* Update the window manager size hints after the sizes of the panes have
1130 been set (the widget heights are not yet readable here, but they will
1131 be by the time the event loop gets around to running this timer proc) */
1132 XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0,
1133 wmSizeUpdateProc, window);
1136 Widget GetPaneByIndex(WindowInfo *window, int paneIndex)
1138 Widget text = NULL;
1139 if (paneIndex >= 0 && paneIndex <= window->nPanes) {
1140 text = (paneIndex == 0) ? window->textArea : window->textPanes[paneIndex - 1];
1142 return(text);
1145 int WidgetToPaneIndex(WindowInfo *window, Widget w)
1147 int i;
1148 Widget text;
1149 int paneIndex = 0;
1151 for (i = 0; i <= window->nPanes; ++i) {
1152 text = (i == 0) ? window->textArea : window->textPanes[i - 1];
1153 if (text == w) {
1154 paneIndex = i;
1157 return(paneIndex);
1161 ** Close the window pane that last had the keyboard focus. (Actually, close
1162 ** the bottom pane and make it look like pane which had focus was closed)
1164 void ClosePane(WindowInfo *window)
1166 short paneHeights[MAX_PANES+1];
1167 int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1];
1168 int horizOffsets[MAX_PANES+1];
1169 int i, focusPane,totalHeight=0;
1170 Widget text;
1172 /* Don't delete the last pane */
1173 if (window->nPanes <= 0)
1174 return;
1176 /* Record the current heights, scroll positions, and insert positions
1177 of the existing panes, and the keyboard focus */
1178 focusPane = 0;
1179 for (i=0; i<=window->nPanes; i++) {
1180 text = i==0 ? window->textArea : window->textPanes[i-1];
1181 insertPositions[i] = TextGetCursorPos(text);
1182 XtVaGetValues(containingPane(text),
1183 XmNheight, &paneHeights[i], NULL);
1184 totalHeight += paneHeights[i];
1185 TextGetScroll(text, &topLines[i], &horizOffsets[i]);
1186 if (text == window->lastFocus)
1187 focusPane = i;
1190 /* Unmanage & remanage the panedWindow so it recalculates pane heights */
1191 XtUnmanageChild(window->splitPane);
1193 /* Destroy last pane, and make sure lastFocus points to an existing pane.
1194 Workaround for OM 2.1.30: text widget must be unmanaged for
1195 xmPanedWindowWidget to calculate the correct pane heights for
1196 the remaining panes, simply detroying it didn't seem enough */
1197 window->nPanes--;
1198 XtUnmanageChild(containingPane(window->textPanes[window->nPanes]));
1199 XtDestroyWidget(containingPane(window->textPanes[window->nPanes]));
1201 if (window->nPanes == 0)
1202 window->lastFocus = window->textArea;
1203 else if (focusPane > window->nPanes)
1204 window->lastFocus = window->textPanes[window->nPanes-1];
1206 /* adjust the heights, scroll positions, etc., to make it look
1207 like the pane with the input focus was closed */
1208 for (i=focusPane; i<=window->nPanes; i++) {
1209 insertPositions[i] = insertPositions[i+1];
1210 paneHeights[i] = paneHeights[i+1];
1211 topLines[i] = topLines[i+1];
1212 horizOffsets[i] = horizOffsets[i+1];
1215 /* set the desired heights and re-manage the paned window so it will
1216 recalculate pane heights */
1217 for (i=0; i<=window->nPanes; i++) {
1218 text = i==0 ? window->textArea : window->textPanes[i-1];
1219 setPaneDesiredHeight(containingPane(text), paneHeights[i]);
1222 if (IsTopBuffer(window))
1223 XtManageChild(window->splitPane);
1225 /* Reset all of the scroll positions, insert positions, etc. */
1226 for (i=0; i<=window->nPanes; i++) {
1227 text = i==0 ? window->textArea : window->textPanes[i-1];
1228 TextSetCursorPos(text, insertPositions[i]);
1229 TextSetScroll(text, topLines[i], horizOffsets[i]);
1231 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
1233 /* Update the window manager size hints after the sizes of the panes have
1234 been set (the widget heights are not yet readable here, but they will
1235 be by the time the event loop gets around to running this timer proc) */
1236 XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0,
1237 wmSizeUpdateProc, window);
1241 ** Turn on and off the display of line numbers
1243 void ShowLineNumbers(WindowInfo *window, int state)
1245 Widget text;
1246 int i, marginWidth;
1247 Dimension windowWidth;
1248 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
1250 if (window->showLineNumbers == state)
1251 return;
1252 window->showLineNumbers = state;
1254 /* Just setting window->showLineNumbers is sufficient to tell
1255 UpdateLineNumDisp to expand the line number areas and the window
1256 size for the number of lines required. To hide the line number
1257 display, set the width to zero, and contract the window width. */
1258 if (state) {
1259 UpdateLineNumDisp(window);
1260 } else {
1261 XtVaGetValues(window->shell, XmNwidth, &windowWidth, NULL);
1262 XtVaGetValues(window->textArea, textNmarginWidth, &marginWidth, NULL);
1263 XtVaSetValues(window->shell, XmNwidth,
1264 windowWidth - textD->left + marginWidth, NULL);
1265 for (i=0; i<=window->nPanes; i++) {
1266 text = i==0 ? window->textArea : window->textPanes[i-1];
1267 XtVaSetValues(text, textNlineNumCols, 0, NULL);
1271 /* Tell WM that the non-expandable part of the window has changed size */
1272 UpdateWMSizeHints(window);
1275 void SetTabDist(WindowInfo *window, int tabDist)
1277 if (window->buffer->tabDist != tabDist) {
1278 int saveCursorPositions[MAX_PANES + 1];
1279 int saveVScrollPositions[MAX_PANES + 1];
1280 int saveHScrollPositions[MAX_PANES + 1];
1281 int paneIndex;
1283 window->ignoreModify = True;
1285 for (paneIndex = 0; paneIndex <= window->nPanes; ++paneIndex) {
1286 Widget w = GetPaneByIndex(window, paneIndex);
1287 textDisp *textD = ((TextWidget)w)->text.textD;
1289 TextGetScroll(w, &saveVScrollPositions[paneIndex], &saveHScrollPositions[paneIndex]);
1290 saveCursorPositions[paneIndex] = TextGetCursorPos(w);
1291 textD->modifyingTabDist = 1;
1294 BufSetTabDistance(window->buffer, tabDist);
1296 for (paneIndex = 0; paneIndex <= window->nPanes; ++paneIndex) {
1297 Widget w = GetPaneByIndex(window, paneIndex);
1298 textDisp *textD = ((TextWidget)w)->text.textD;
1300 textD->modifyingTabDist = 0;
1301 TextSetCursorPos(w, saveCursorPositions[paneIndex]);
1302 TextSetScroll(w, saveVScrollPositions[paneIndex], saveHScrollPositions[paneIndex]);
1305 window->ignoreModify = False;
1309 void SetEmTabDist(WindowInfo *window, int emTabDist)
1311 int i;
1313 XtVaSetValues(window->textArea, textNemulateTabs, emTabDist, NULL);
1314 for (i = 0; i < window->nPanes; ++i) {
1315 XtVaSetValues(window->textPanes[i], textNemulateTabs, emTabDist, NULL);
1320 ** Turn on and off the display of the statistics line
1322 void ShowStatsLine(WindowInfo *window, int state)
1324 Widget text;
1325 int i;
1327 /* In continuous wrap mode, text widgets must be told to keep track of
1328 the top line number in absolute (non-wrapped) lines, because it can
1329 be a costly calculation, and is only needed for displaying line
1330 numbers, either in the stats line, or along the left margin */
1331 for (i=0; i<=window->nPanes; i++) {
1332 text = i==0 ? window->textArea : window->textPanes[i-1];
1333 TextDMaintainAbsLineNum(((TextWidget)text)->text.textD, state);
1335 window->showStats = state;
1336 showStats(window, state);
1340 ** Displays and undisplays the statistics line (regardless of settings of
1341 ** window->showStats or window->modeMessageDisplayed)
1343 static void showStats(WindowInfo *window, int state)
1345 if (state) {
1346 XtManageChild(window->statsLineForm);
1347 showStatsForm(window, True);
1348 } else {
1349 XtUnmanageChild(window->statsLineForm);
1350 showStatsForm(window, window->showISearchLine);
1353 /* Tell WM that the non-expandable part of the window has changed size */
1354 /* Already done in showStatsForm */
1355 /* UpdateWMSizeHints(window); */
1360 static void showTabBar(WindowInfo *window, int state)
1362 if (state) {
1363 XtManageChild(XtParent(window->bufferTabBar));
1364 showStatsForm(window, True);
1365 } else {
1366 XtUnmanageChild(XtParent(window->bufferTabBar));
1367 showStatsForm(window, False);
1373 void ShowBufferTabBar(WindowInfo *window, int state)
1375 if (XtIsManaged(XtParent(window->bufferTabBar)) == state)
1376 return;
1377 window->showTabBar = state;
1378 showTabBar(window, state);
1382 ** Turn on and off the continuing display of the incremental search line
1383 ** (when off, it is popped up and down as needed via TempShowISearch)
1385 void ShowISearchLine(WindowInfo *window, int state)
1387 if (window->showISearchLine == state)
1388 return;
1389 window->showISearchLine = state;
1390 showISearch(window, state);
1394 ** Temporarily show and hide the incremental search line if the line is not
1395 ** already up.
1397 void TempShowISearch(WindowInfo *window, int state)
1399 if (window->showISearchLine)
1400 return;
1401 if (XtIsManaged(window->iSearchForm) != state)
1402 showISearch(window, state);
1406 ** Put up or pop-down the incremental search line regardless of settings
1407 ** of showISearchLine or TempShowISearch
1409 static void showISearch(WindowInfo *window, int state)
1411 if (state) {
1412 XtManageChild(window->iSearchForm);
1413 showStatsForm(window, True);
1414 } else {
1415 XtUnmanageChild(window->iSearchForm);
1416 showStatsForm(window, window->showStats ||
1417 window->modeMessageDisplayed);
1420 /* Tell WM that the non-expandable part of the window has changed size */
1421 /* This is already done in showStatsForm */
1422 /* UpdateWMSizeHints(window); */
1426 ** Show or hide the extra display area under the main menu bar which
1427 ** optionally contains the status line and the incremental search bar
1429 static void showStatsForm(WindowInfo *window, int state)
1431 Widget statsAreaForm = XtParent(window->statsLineForm);
1432 Widget mainW = XtParent(statsAreaForm);
1434 /* The very silly use of XmNcommandWindowLocation and XmNshowSeparator
1435 below are to kick the main window widget to position and remove the
1436 status line when it is managed and unmanaged. At some Motif version
1437 level, the showSeparator trick backfires and leaves the separator
1438 shown, but fortunately the dynamic behavior is fixed, too so the
1439 workaround is no longer necessary, either. (... the version where
1440 this occurs may be earlier than 2.1. If the stats line shows
1441 double thickness shadows in earlier Motif versions, the #if XmVersion
1442 directive should be moved back to that earlier version) */
1443 if (manageToolBars(statsAreaForm)) {
1444 XtUnmanageChild(statsAreaForm); /*... will this fix Solaris 7??? */
1445 XtVaSetValues(mainW, XmNcommandWindowLocation,
1446 XmCOMMAND_ABOVE_WORKSPACE, NULL);
1447 #if XmVersion < 2001
1448 XtVaSetValues(mainW, XmNshowSeparator, True, NULL);
1449 #endif
1450 XtManageChild(statsAreaForm);
1451 XtVaSetValues(mainW, XmNshowSeparator, False, NULL);
1452 UpdateStatsLine(window);
1453 } else {
1454 XtUnmanageChild(statsAreaForm);
1455 XtVaSetValues(mainW, XmNcommandWindowLocation,
1456 XmCOMMAND_BELOW_WORKSPACE, NULL);
1459 /* Tell WM that the non-expandable part of the window has changed size */
1460 UpdateWMSizeHints(window);
1464 ** Display a special message in the stats line (show the stats line if it
1465 ** is not currently shown).
1467 void SetModeMessage(WindowInfo *window, const char *message)
1469 if (!IsTopBuffer(window))
1470 return;
1472 window->modeMessageDisplayed = True;
1473 XmTextSetString(window->statsLine, (char*)message);
1475 * Don't invoke the stats line again, if stats line is already displayed.
1477 if (!window->showStats)
1478 showStats(window, True);
1482 ** Clear special statistics line message set in SetModeMessage, returns
1483 ** the statistics line to its original state as set in window->showStats
1485 void ClearModeMessage(WindowInfo *window)
1487 if (!IsTopBuffer(window))
1488 return;
1490 window->modeMessageDisplayed = False;
1492 * Remove the stats line only if indicated by it's window state.
1494 if (!window->showStats)
1495 showStats(window, False);
1496 UpdateStatsLine(window);
1500 ** Count the windows
1502 int NWindows(void)
1504 WindowInfo *win;
1505 int n;
1507 for (win=WindowList, n=0; win!=NULL; win=win->next, n++);
1508 return n;
1512 ** Set autoindent state to one of NO_AUTO_INDENT, AUTO_INDENT, or SMART_INDENT.
1514 void SetAutoIndent(WindowInfo *window, int state)
1516 int autoIndent = state == AUTO_INDENT, smartIndent = state == SMART_INDENT;
1517 int i;
1519 if (window->indentStyle == SMART_INDENT && !smartIndent)
1520 EndSmartIndent(window);
1521 else if (smartIndent && window->indentStyle != SMART_INDENT)
1522 BeginSmartIndent(window, True);
1523 window->indentStyle = state;
1524 XtVaSetValues(window->textArea, textNautoIndent, autoIndent,
1525 textNsmartIndent, smartIndent, NULL);
1526 for (i=0; i<window->nPanes; i++)
1527 XtVaSetValues(window->textPanes[i], textNautoIndent, autoIndent,
1528 textNsmartIndent, smartIndent, NULL);
1529 XmToggleButtonSetState(window->smartIndentItem, smartIndent, False);
1530 XmToggleButtonSetState(window->autoIndentItem, autoIndent, False);
1531 XmToggleButtonSetState(window->autoIndentOffItem, state == NO_AUTO_INDENT,
1532 False);
1536 ** Set showMatching state to one of NO_FLASH, FLASH_DELIMIT or FLASH_RANGE.
1537 ** Update the menu to reflect the change of state.
1539 void SetShowMatching(WindowInfo *window, int state)
1541 window->showMatchingStyle = state;
1542 XmToggleButtonSetState(window->showMatchingOffItem,
1543 state == NO_FLASH, False);
1544 XmToggleButtonSetState(window->showMatchingDelimitItem,
1545 state == FLASH_DELIMIT, False);
1546 XmToggleButtonSetState(window->showMatchingRangeItem,
1547 state == FLASH_RANGE, False);
1551 ** Set the fonts for "window" from a font name, and updates the display.
1552 ** Also updates window->fontList which is used for statistics line.
1554 ** Note that this leaks memory and server resources. In previous NEdit
1555 ** versions, fontLists were carefully tracked and freed, but X and Motif
1556 ** have some kind of timing problem when widgets are distroyed, such that
1557 ** fonts may not be freed immediately after widget destruction with 100%
1558 ** safety. Rather than kludge around this with timerProcs, I have chosen
1559 ** to create new fontLists only when the user explicitly changes the font
1560 ** (which shouldn't happen much in normal NEdit operation), and skip the
1561 ** futile effort of freeing them.
1563 void SetFonts(WindowInfo *window, const char *fontName, const char *italicName,
1564 const char *boldName, const char *boldItalicName)
1566 XFontStruct *font, *oldFont;
1567 int i, oldFontWidth, oldFontHeight, fontWidth, fontHeight;
1568 int borderWidth, borderHeight, marginWidth, marginHeight;
1569 int primaryChanged, highlightChanged = False;
1570 Dimension oldWindowWidth, oldWindowHeight, oldTextWidth, oldTextHeight;
1571 Dimension textHeight, newWindowWidth, newWindowHeight;
1572 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
1574 /* Check which fonts have changed */
1575 primaryChanged = strcmp(fontName, window->fontName);
1576 if (strcmp(italicName, window->italicFontName)) highlightChanged = True;
1577 if (strcmp(boldName, window->boldFontName)) highlightChanged = True;
1578 if (strcmp(boldItalicName, window->boldItalicFontName))
1579 highlightChanged = True;
1580 if (!primaryChanged && !highlightChanged)
1581 return;
1583 /* Get information about the current window sizing, to be used to
1584 determine the correct window size after the font is changed */
1585 XtVaGetValues(window->shell, XmNwidth, &oldWindowWidth, XmNheight,
1586 &oldWindowHeight, NULL);
1587 XtVaGetValues(window->textArea, XmNheight, &textHeight,
1588 textNmarginHeight, &marginHeight, textNmarginWidth,
1589 &marginWidth, textNfont, &oldFont, NULL);
1590 oldTextWidth = textD->width + textD->lineNumWidth;
1591 oldTextHeight = textHeight - 2*marginHeight;
1592 for (i=0; i<window->nPanes; i++) {
1593 XtVaGetValues(window->textPanes[i], XmNheight, &textHeight, NULL);
1594 oldTextHeight += textHeight - 2*marginHeight;
1596 borderWidth = oldWindowWidth - oldTextWidth;
1597 borderHeight = oldWindowHeight - oldTextHeight;
1598 oldFontWidth = oldFont->max_bounds.width;
1599 oldFontHeight = textD->ascent + textD->descent;
1602 /* Change the fonts in the window data structure. If the primary font
1603 didn't work, use Motif's fallback mechanism by stealing it from the
1604 statistics line. Highlight fonts are allowed to be NULL, which
1605 is interpreted as "use the primary font" */
1606 if (primaryChanged) {
1607 strcpy(window->fontName, fontName);
1608 font = XLoadQueryFont(TheDisplay, fontName);
1609 if (font == NULL)
1610 XtVaGetValues(window->statsLine, XmNfontList, &window->fontList,
1611 NULL);
1612 else
1613 window->fontList = XmFontListCreate(font, XmSTRING_DEFAULT_CHARSET);
1615 if (highlightChanged) {
1616 strcpy(window->italicFontName, italicName);
1617 window->italicFontStruct = XLoadQueryFont(TheDisplay, italicName);
1618 strcpy(window->boldFontName, boldName);
1619 window->boldFontStruct = XLoadQueryFont(TheDisplay, boldName);
1620 strcpy(window->boldItalicFontName, boldItalicName);
1621 window->boldItalicFontStruct = XLoadQueryFont(TheDisplay, boldItalicName);
1624 /* Change the primary font in all the widgets */
1625 if (primaryChanged) {
1626 font = GetDefaultFontStruct(window->fontList);
1627 XtVaSetValues(window->textArea, textNfont, font, NULL);
1628 for (i=0; i<window->nPanes; i++)
1629 XtVaSetValues(window->textPanes[i], textNfont, font, NULL);
1632 /* Change the highlight fonts, even if they didn't change, because
1633 primary font is read through the style table for syntax highlighting */
1634 if (window->highlightData != NULL)
1635 UpdateHighlightStyles(window);
1637 /* Change the window manager size hints.
1638 Note: this has to be done _before_ we set the new sizes. ICCCM2
1639 compliant window managers (such as fvwm2) would otherwise resize
1640 the window twice: once because of the new sizes requested, and once
1641 because of the new size increments, resulting in an overshoot. */
1642 UpdateWMSizeHints(window);
1644 /* Use the information from the old window to re-size the window to a
1645 size appropriate for the new font */
1646 fontWidth = GetDefaultFontStruct(window->fontList)->max_bounds.width;
1647 fontHeight = textD->ascent + textD->descent;
1648 newWindowWidth = (oldTextWidth*fontWidth) / oldFontWidth + borderWidth;
1649 newWindowHeight = (oldTextHeight*fontHeight) / oldFontHeight + borderHeight;
1650 XtVaSetValues(window->shell, XmNwidth, newWindowWidth, XmNheight,
1651 newWindowHeight, NULL);
1653 /* Change the minimum pane height */
1654 UpdateMinPaneHeights(window);
1657 void SetColors(WindowInfo *window, const char *textFg, const char *textBg,
1658 const char *selectFg, const char *selectBg, const char *hiliteFg,
1659 const char *hiliteBg, const char *lineNoFg, const char *cursorFg)
1661 int i, dummy;
1662 Pixel textFgPix = AllocColor( window->textArea, textFg,
1663 &dummy, &dummy, &dummy),
1664 textBgPix = AllocColor( window->textArea, textBg,
1665 &dummy, &dummy, &dummy),
1666 selectFgPix = AllocColor( window->textArea, selectFg,
1667 &dummy, &dummy, &dummy),
1668 selectBgPix = AllocColor( window->textArea, selectBg,
1669 &dummy, &dummy, &dummy),
1670 hiliteFgPix = AllocColor( window->textArea, hiliteFg,
1671 &dummy, &dummy, &dummy),
1672 hiliteBgPix = AllocColor( window->textArea, hiliteBg,
1673 &dummy, &dummy, &dummy),
1674 lineNoFgPix = AllocColor( window->textArea, lineNoFg,
1675 &dummy, &dummy, &dummy),
1676 cursorFgPix = AllocColor( window->textArea, cursorFg,
1677 &dummy, &dummy, &dummy);
1678 textDisp *textD;
1680 /* Update the main pane */
1681 XtVaSetValues(window->textArea,
1682 XmNforeground, textFgPix,
1683 XmNbackground, textBgPix,
1684 NULL);
1685 textD = ((TextWidget)window->textArea)->text.textD;
1686 TextDSetColors( textD, textFgPix, textBgPix, selectFgPix, selectBgPix,
1687 hiliteFgPix, hiliteBgPix, lineNoFgPix, cursorFgPix );
1688 /* Update any additional panes */
1689 for (i=0; i<window->nPanes; i++) {
1690 XtVaSetValues(window->textPanes[i],
1691 XmNforeground, textFgPix,
1692 XmNbackground, textBgPix,
1693 NULL);
1694 textD = ((TextWidget)window->textPanes[i])->text.textD;
1695 TextDSetColors( textD, textFgPix, textBgPix, selectFgPix, selectBgPix,
1696 hiliteFgPix, hiliteBgPix, lineNoFgPix, cursorFgPix );
1699 /* Redo any syntax highlighting */
1700 if (window->highlightData != NULL)
1701 UpdateHighlightStyles(window);
1705 ** Set insert/overstrike mode
1707 void SetOverstrike(WindowInfo *window, int overstrike)
1709 int i;
1711 XtVaSetValues(window->textArea, textNoverstrike, overstrike, NULL);
1712 for (i=0; i<window->nPanes; i++)
1713 XtVaSetValues(window->textPanes[i], textNoverstrike, overstrike, NULL);
1714 window->overstrike = overstrike;
1718 ** Select auto-wrap mode, one of NO_WRAP, NEWLINE_WRAP, or CONTINUOUS_WRAP
1720 void SetAutoWrap(WindowInfo *window, int state)
1722 int i;
1723 int autoWrap = state == NEWLINE_WRAP, contWrap = state == CONTINUOUS_WRAP;
1725 XtVaSetValues(window->textArea, textNautoWrap, autoWrap,
1726 textNcontinuousWrap, contWrap, NULL);
1727 for (i=0; i<window->nPanes; i++)
1728 XtVaSetValues(window->textPanes[i], textNautoWrap, autoWrap,
1729 textNcontinuousWrap, contWrap, NULL);
1730 window->wrapMode = state;
1732 XmToggleButtonSetState(window->newlineWrapItem, autoWrap, False);
1733 XmToggleButtonSetState(window->continuousWrapItem, contWrap, False);
1734 XmToggleButtonSetState(window->noWrapItem, state == NO_WRAP, False);
1738 ** Set the wrap margin (0 == wrap at right edge of window)
1740 void SetWrapMargin(WindowInfo *window, int margin)
1742 int i;
1744 XtVaSetValues(window->textArea, textNwrapMargin, margin, NULL);
1745 for (i=0; i<window->nPanes; i++)
1746 XtVaSetValues(window->textPanes[i], textNwrapMargin, margin, NULL);
1750 ** Recover the window pointer from any widget in the window, by searching
1751 ** up the widget hierarcy for the top level container widget where the window
1752 ** pointer is stored in the userData field. In buffer mode, this is the window
1753 ** pointer of the top (active) buffer, which is returned if w is 'shell-level'
1754 ** widget - menus, find/replace dialogs, etc.
1756 ** To support action routine in buffer mode, a copy of the window pointer
1757 ** is also store in the splitPane widget.
1759 WindowInfo *WidgetToWindow(Widget w)
1761 WindowInfo *window = NULL;
1762 Widget parent;
1764 while (True) {
1765 /* return window pointer of buffer */
1766 if (XtClass(w) == xmPanedWindowWidgetClass)
1767 break;
1769 if (XtClass(w) == topLevelShellWidgetClass) {
1770 WidgetList items;
1772 /* there should be only 1 child for the shell -
1773 the main window widget */
1774 XtVaGetValues(w, XmNchildren, &items, NULL);
1775 w = items[0];
1776 break;
1779 parent = XtParent(w);
1780 if (parent == NULL)
1781 return NULL;
1783 /* make sure it is not a dialog shell */
1784 if (XtClass(parent) == topLevelShellWidgetClass &&
1785 XmIsMainWindow(w))
1786 break;
1788 w = parent;
1791 XtVaGetValues(w, XmNuserData, &window, NULL);
1793 return window;
1797 ** Change the window appearance and the window data structure to show
1798 ** that the file it contains has been modified
1800 void SetWindowModified(WindowInfo *window, int modified)
1802 if (window->fileChanged == FALSE && modified == TRUE) {
1803 XtSetSensitive(window->closeItem, TRUE);
1804 window->fileChanged = TRUE;
1805 UpdateWindowTitle(window);
1806 RefreshTabState(window);
1807 } else if (window->fileChanged == TRUE && modified == FALSE) {
1808 window->fileChanged = FALSE;
1809 UpdateWindowTitle(window);
1810 RefreshTabState(window);
1815 ** Update the window title to reflect the filename, read-only, and modified
1816 ** status of the window data structure
1818 void UpdateWindowTitle(const WindowInfo *window)
1820 char *iconTitle, *title;
1822 if (!IsTopBuffer((WindowInfo*)window))
1823 return;
1825 title = FormatWindowTitle(window->filename,
1826 window->path,
1827 #ifdef VMS
1828 NULL,
1829 #else
1830 GetClearCaseViewTag(),
1831 #endif /* VMS */
1832 GetPrefServerName(),
1833 IsServer,
1834 window->filenameSet,
1835 window->lockReasons,
1836 window->fileChanged,
1837 GetPrefTitleFormat());
1839 iconTitle = XtMalloc(strlen(window->filename) + 2); /* strlen("*")+1 */
1841 strcpy(iconTitle, window->filename);
1842 if (window->fileChanged)
1843 strcat(iconTitle, "*");
1844 XtVaSetValues(window->shell, XmNtitle, title, XmNiconName, iconTitle, NULL);
1846 /* If there's a find or replace dialog up in "Keep Up" mode, with a
1847 file name in the title, update it too */
1848 if (window->findDlog && XmToggleButtonGetState(window->findKeepBtn)) {
1849 sprintf(title, "Find (in %s)", window->filename);
1850 XtVaSetValues(XtParent(window->findDlog), XmNtitle, title, NULL);
1852 if (window->replaceDlog && XmToggleButtonGetState(window->replaceKeepBtn)) {
1853 sprintf(title, "Replace (in %s)", window->filename);
1854 XtVaSetValues(XtParent(window->replaceDlog), XmNtitle, title, NULL);
1856 XtFree(iconTitle);
1858 /* Update the Windows menus with the new name */
1859 InvalidateWindowMenus();
1863 ** Update the read-only state of the text area(s) in the window, and
1864 ** the ReadOnly toggle button in the File menu to agree with the state in
1865 ** the window data structure.
1867 void UpdateWindowReadOnly(WindowInfo *window)
1869 int i, state;
1871 if (!IsTopBuffer(window))
1872 return;
1874 state = IS_ANY_LOCKED(window->lockReasons);
1875 XtVaSetValues(window->textArea, textNreadOnly, state, NULL);
1876 for (i=0; i<window->nPanes; i++)
1877 XtVaSetValues(window->textPanes[i], textNreadOnly, state, NULL);
1878 XmToggleButtonSetState(window->readOnlyItem, state, FALSE);
1879 XtSetSensitive(window->readOnlyItem,
1880 !IS_ANY_LOCKED_IGNORING_USER(window->lockReasons));
1884 ** Get the start and end of the current selection. This routine is obsolete
1885 ** because it ignores rectangular selections, and reads from the widget
1886 ** instead of the buffer. Use BufGetSelectionPos.
1888 int GetSelection(Widget widget, int *left, int *right)
1890 return GetSimpleSelection(TextGetBuffer(widget), left, right);
1894 ** Find the start and end of a single line selection. Hides rectangular
1895 ** selection issues for older routines which use selections that won't
1896 ** span lines.
1898 int GetSimpleSelection(textBuffer *buf, int *left, int *right)
1900 int selStart, selEnd, isRect, rectStart, rectEnd, lineStart;
1902 /* get the character to match and its position from the selection, or
1903 the character before the insert point if nothing is selected.
1904 Give up if too many characters are selected */
1905 if (!BufGetSelectionPos(buf, &selStart, &selEnd, &isRect,
1906 &rectStart, &rectEnd))
1907 return False;
1908 if (isRect) {
1909 lineStart = BufStartOfLine(buf, selStart);
1910 selStart = BufCountForwardDispChars(buf, lineStart, rectStart);
1911 selEnd = BufCountForwardDispChars(buf, lineStart, rectEnd);
1913 *left = selStart;
1914 *right = selEnd;
1915 return True;
1919 ** Returns a range of text from a text widget (this routine is obsolete,
1920 ** get text from the buffer instead). Memory is allocated with
1921 ** XtMalloc and caller should free it.
1923 char *GetTextRange(Widget widget, int left, int right)
1925 return BufGetRange(TextGetBuffer(widget), left, right);
1929 ** If the selection (or cursor position if there's no selection) is not
1930 ** fully shown, scroll to bring it in to view. Note that as written,
1931 ** this won't work well with multi-line selections. Modest re-write
1932 ** of the horizontal scrolling part would be quite easy to make it work
1933 ** well with rectangular selections.
1935 void MakeSelectionVisible(WindowInfo *window, Widget textPane)
1937 int left, right, isRect, rectStart, rectEnd, horizOffset;
1938 int scrollOffset, leftX, rightX, y, rows, margin;
1939 int topLineNum, lastLineNum, rightLineNum, leftLineNum, linesToScroll;
1940 textDisp *textD = ((TextWidget)textPane)->text.textD;
1941 int topChar = TextFirstVisiblePos(textPane);
1942 int lastChar = TextLastVisiblePos(textPane);
1943 int targetLineNum;
1944 Dimension width;
1946 /* find out where the selection is */
1947 if (!BufGetSelectionPos(window->buffer, &left, &right, &isRect,
1948 &rectStart, &rectEnd)) {
1949 left = right = TextGetCursorPos(textPane);
1950 isRect = False;
1953 /* Check vertical positioning unless the selection is already shown or
1954 already covers the display. If the end of the selection is below
1955 bottom, scroll it in to view until the end selection is scrollOffset
1956 lines from the bottom of the display or the start of the selection
1957 scrollOffset lines from the top. Calculate a pleasing distance from the
1958 top or bottom of the window, to scroll the selection to (if scrolling is
1959 necessary), around 1/3 of the height of the window */
1960 if (!((left >= topChar && right <= lastChar) ||
1961 (left <= topChar && right >= lastChar))) {
1962 XtVaGetValues(textPane, textNrows, &rows, NULL);
1963 scrollOffset = rows/3;
1964 TextGetScroll(textPane, &topLineNum, &horizOffset);
1965 if (right > lastChar) {
1966 /* End of sel. is below bottom of screen */
1967 leftLineNum = topLineNum +
1968 TextDCountLines(textD, topChar, left, False);
1969 targetLineNum = topLineNum + scrollOffset;
1970 if (leftLineNum >= targetLineNum) {
1971 /* Start of sel. is not between top & target */
1972 linesToScroll = TextDCountLines(textD, lastChar, right, False) +
1973 scrollOffset;
1974 if (leftLineNum - linesToScroll < targetLineNum)
1975 linesToScroll = leftLineNum - targetLineNum;
1976 /* Scroll start of selection to the target line */
1977 TextSetScroll(textPane, topLineNum+linesToScroll, horizOffset);
1979 } else if (left < topChar) {
1980 /* Start of sel. is above top of screen */
1981 lastLineNum = topLineNum + rows;
1982 rightLineNum = lastLineNum -
1983 TextDCountLines(textD, right, lastChar, False);
1984 targetLineNum = lastLineNum - scrollOffset;
1985 if (rightLineNum <= targetLineNum) {
1986 /* End of sel. is not between bottom & target */
1987 linesToScroll = TextDCountLines(textD, left, topChar, False) +
1988 scrollOffset;
1989 if (rightLineNum + linesToScroll > targetLineNum)
1990 linesToScroll = targetLineNum - rightLineNum;
1991 /* Scroll end of selection to the target line */
1992 TextSetScroll(textPane, topLineNum-linesToScroll, horizOffset);
1997 /* If either end of the selection off screen horizontally, try to bring it
1998 in view, by making sure both end-points are visible. Using only end
1999 points of a multi-line selection is not a great idea, and disaster for
2000 rectangular selections, so this part of the routine should be re-written
2001 if it is to be used much with either. Note also that this is a second
2002 scrolling operation, causing the display to jump twice. It's done after
2003 vertical scrolling to take advantage of TextPosToXY which requires it's
2004 reqested position to be vertically on screen) */
2005 if ( TextPosToXY(textPane, left, &leftX, &y) &&
2006 TextPosToXY(textPane, right, &rightX, &y) && leftX <= rightX) {
2007 TextGetScroll(textPane, &topLineNum, &horizOffset);
2008 XtVaGetValues(textPane, XmNwidth, &width, textNmarginWidth, &margin,
2009 NULL);
2010 if (leftX < margin + textD->lineNumLeft + textD->lineNumWidth)
2011 horizOffset -=
2012 margin + textD->lineNumLeft + textD->lineNumWidth - leftX;
2013 else if (rightX > width - margin)
2014 horizOffset += rightX - (width - margin);
2015 TextSetScroll(textPane, topLineNum, horizOffset);
2018 /* make sure that the statistics line is up to date */
2019 UpdateStatsLine(window);
2022 static Widget createTextArea(Widget parent, WindowInfo *window, int rows,
2023 int cols, int emTabDist, char *delimiters, int wrapMargin,
2024 int lineNumCols)
2026 Widget text, sw, hScrollBar, vScrollBar, frame;
2028 /* Create a text widget inside of a scrolled window widget */
2029 sw = XtVaCreateManagedWidget("scrolledW", xmScrolledWindowWidgetClass,
2030 parent, XmNpaneMaximum, SHRT_MAX,
2031 XmNpaneMinimum, PANE_MIN_HEIGHT, XmNhighlightThickness, 0, NULL);
2032 hScrollBar = XtVaCreateManagedWidget("textHorScrollBar",
2033 xmScrollBarWidgetClass, sw, XmNorientation, XmHORIZONTAL,
2034 XmNrepeatDelay, 10, NULL);
2035 vScrollBar = XtVaCreateManagedWidget("textVertScrollBar",
2036 xmScrollBarWidgetClass, sw, XmNorientation, XmVERTICAL,
2037 XmNrepeatDelay, 10, NULL);
2038 frame = XtVaCreateManagedWidget("textFrame", xmFrameWidgetClass, sw,
2039 XmNshadowType, XmSHADOW_IN, NULL);
2040 text = XtVaCreateManagedWidget("text", textWidgetClass, frame,
2041 textNbacklightCharTypes, window->backlightCharTypes,
2042 textNrows, rows, textNcolumns, cols,
2043 textNlineNumCols, lineNumCols,
2044 textNemulateTabs, emTabDist,
2045 textNfont, GetDefaultFontStruct(window->fontList),
2046 textNhScrollBar, hScrollBar, textNvScrollBar, vScrollBar,
2047 textNreadOnly, IS_ANY_LOCKED(window->lockReasons),
2048 textNwordDelimiters, delimiters,
2049 textNwrapMargin, wrapMargin,
2050 textNautoIndent, window->indentStyle == AUTO_INDENT,
2051 textNsmartIndent, window->indentStyle == SMART_INDENT,
2052 textNautoWrap, window->wrapMode == NEWLINE_WRAP,
2053 textNcontinuousWrap, window->wrapMode == CONTINUOUS_WRAP,
2054 textNoverstrike, window->overstrike,
2055 textNhidePointer, (Boolean) GetPrefTypingHidesPointer(),
2056 NULL);
2058 XtVaSetValues(sw, XmNworkWindow, frame, XmNhorizontalScrollBar,
2059 hScrollBar, XmNverticalScrollBar, vScrollBar, NULL);
2061 /* add focus, drag, cursor tracking, and smart indent callbacks */
2062 XtAddCallback(text, textNfocusCallback, (XtCallbackProc)focusCB, window);
2063 XtAddCallback(text, textNcursorMovementCallback, (XtCallbackProc)movedCB,
2064 window);
2065 XtAddCallback(text, textNdragStartCallback, (XtCallbackProc)dragStartCB,
2066 window);
2067 XtAddCallback(text, textNdragEndCallback, (XtCallbackProc)dragEndCB,
2068 window);
2069 XtAddCallback(text, textNsmartIndentCallback, SmartIndentCB, window);
2071 /* This makes sure the text area initially has a the insert point shown
2072 ... (check if still true with the nedit text widget, probably not) */
2073 XmAddTabGroup(containingPane(text));
2075 /* compensate for Motif delete/backspace problem */
2076 RemapDeleteKey(text);
2078 /* Augment translation table for right button popup menu */
2079 AddBGMenuAction(text);
2081 /* If absolute line numbers will be needed for display in the statistics
2082 line, tell the widget to maintain them (otherwise, it's a costly
2083 operation and performance will be better without it) */
2084 TextDMaintainAbsLineNum(((TextWidget)text)->text.textD, window->showStats);
2086 return text;
2089 static void movedCB(Widget w, WindowInfo *window, XtPointer callData)
2091 if (window->ignoreModify)
2092 return;
2094 /* update line and column nubers in statistics line */
2095 UpdateStatsLine(window);
2097 /* Check the character before the cursor for matchable characters */
2098 FlashMatching(window, w);
2100 /* Check for changes to read-only status and/or file modifications */
2101 CheckForChangesToFile(window);
2104 static void modifiedCB(int pos, int nInserted, int nDeleted, int nRestyled,
2105 char *deletedText, void *cbArg)
2107 WindowInfo *window = (WindowInfo *)cbArg;
2108 int selected = window->buffer->primary.selected;
2110 /* update the table of bookmarks */
2111 if (!window->ignoreModify) {
2112 UpdateMarkTable(window, pos, nInserted, nDeleted);
2115 /* Check and dim/undim selection related menu items */
2116 if ((window->wasSelected && !selected) ||
2117 (!window->wasSelected && selected)) {
2118 window->wasSelected = selected;
2120 /* buffers may share a common shell window, menu-bar etc.
2121 we don't do much if things happen to the hidden ones */
2122 if (IsTopBuffer(window)) {
2123 XtSetSensitive(window->printSelItem, selected);
2124 XtSetSensitive(window->cutItem, selected);
2125 XtSetSensitive(window->copyItem, selected);
2126 XtSetSensitive(window->delItem, selected);
2127 /* Note we don't change the selection for items like
2128 "Open Selected" and "Find Selected". That's because
2129 it works on selections in external applications.
2130 Desensitizing it if there's no NEdit selection
2131 disables this feature. */
2132 #ifndef VMS
2133 XtSetSensitive(window->filterItem, selected);
2134 #endif
2136 DimSelectionDepUserMenuItems(window, selected);
2137 if (window->replaceDlog != NULL)
2139 UpdateReplaceActionButtons(window);
2144 /* Make sure line number display is sufficient for new data */
2145 UpdateLineNumDisp(window);
2147 /* When the program needs to make a change to a text area without without
2148 recording it for undo or marking file as changed it sets ignoreModify */
2149 if (window->ignoreModify || (nDeleted == 0 && nInserted == 0))
2150 return;
2152 /* Save information for undoing this operation (this call also counts
2153 characters and editing operations for triggering autosave */
2154 SaveUndoInformation(window, pos, nInserted, nDeleted, deletedText);
2156 /* Trigger automatic backup if operation or character limits reached */
2157 if (window->autoSave &&
2158 (window->autoSaveCharCount > AUTOSAVE_CHAR_LIMIT ||
2159 window->autoSaveOpCount > AUTOSAVE_OP_LIMIT)) {
2160 WriteBackupFile(window);
2161 window->autoSaveCharCount = 0;
2162 window->autoSaveOpCount = 0;
2165 /* Indicate that the window has now been modified */
2166 SetWindowModified(window, TRUE);
2168 /* Update # of bytes, and line and col statistics */
2169 UpdateStatsLine(window);
2171 /* Check if external changes have been made to file and warn user */
2172 CheckForChangesToFile(window);
2175 static void focusCB(Widget w, WindowInfo *window, XtPointer callData)
2177 /* record which window pane last had the keyboard focus */
2178 window->lastFocus = w;
2180 /* update line number statistic to reflect current focus pane */
2181 UpdateStatsLine(window);
2183 /* finish off the current incremental search */
2184 EndISearch(window);
2186 /* Check for changes to read-only status and/or file modifications */
2187 CheckForChangesToFile(window);
2190 static void dragStartCB(Widget w, WindowInfo *window, XtPointer callData)
2192 /* don't record all of the intermediate drag steps for undo */
2193 window->ignoreModify = True;
2196 static void dragEndCB(Widget w, WindowInfo *window, dragEndCBStruct *callData)
2198 /* restore recording of undo information */
2199 window->ignoreModify = False;
2201 /* Do nothing if drag operation was canceled */
2202 if (callData->nCharsInserted == 0)
2203 return;
2205 /* Save information for undoing this operation not saved while
2206 undo recording was off */
2207 modifiedCB(callData->startPos, callData->nCharsInserted,
2208 callData->nCharsDeleted, 0, callData->deletedText, window);
2211 static void closeCB(Widget w, WindowInfo *window, XtPointer callData)
2213 window = WidgetToWindow(w);
2215 if (GetPrefBufferMode()) {
2216 /* in window-buffer mode, we now have only one app window,
2217 to close is to quit */
2218 CloseBufferWindow(w, window, callData);
2220 else {
2221 /* close this window */
2222 if (WindowList->next == NULL) {
2223 if (!CheckPrefsChangesSaved(window->shell))
2224 return;
2225 if (!WindowList->fileChanged)
2226 exit(EXIT_SUCCESS);
2227 if (CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE))
2228 exit(EXIT_SUCCESS);
2229 } else
2230 CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE);
2234 static void saveYourselfCB(Widget w, WindowInfo *window, XtPointer callData)
2236 WindowInfo *win, **revWindowList;
2237 char geometry[MAX_GEOM_STRING_LEN];
2238 int argc = 0, maxArgc, nWindows, i;
2239 char **argv;
2240 int wasIconic = False;
2242 window = WidgetToWindow(w);
2244 /* Only post a restart command on the first window in the window list so
2245 session manager can restart the whole set of windows in one executable,
2246 rather than one nedit per file. Even if the restart command is not on
2247 this window, the protocol demands that we set the window's WM_COMMAND
2248 property in response to the "save yourself" message */
2249 if (window != WindowList) {
2250 XSetCommand(TheDisplay, XtWindow(window->shell), NULL, 0);
2251 return;
2254 /* Allocate memory for an argument list and for a reversed list of
2255 windows. The window list is reversed for IRIX 4DWM and any other
2256 window/session manager combination which uses window creation
2257 order for re-associating stored geometry information with
2258 new windows created by a restored application */
2259 maxArgc = 1;
2260 nWindows = 0;
2261 for (win=WindowList; win!=NULL; win=win->next) {
2262 maxArgc += 4;
2263 nWindows++;
2265 argv = (char **)XtMalloc(maxArgc*sizeof(char *));
2266 revWindowList = (WindowInfo **)XtMalloc(sizeof(WindowInfo *)*nWindows);
2267 for (win=WindowList, i=nWindows-1; win!=NULL; win=win->next, i--)
2268 revWindowList[i] = win;
2270 /* Create command line arguments for restoring each window in the list */
2271 argv[argc++] = XtNewString(ArgV0);
2272 if (IsServer) {
2273 argv[argc++] = XtNewString("-server");
2274 if (GetPrefServerName()[0] != '\0') {
2275 argv[argc++] = XtNewString("-svrname");
2276 argv[argc++] = XtNewString(GetPrefServerName());
2279 for (i=0; i<nWindows; i++) {
2280 win = revWindowList[i];
2281 getGeometryString(win, geometry);
2282 argv[argc++] = XtNewString("-geometry");
2283 argv[argc++] = XtNewString(geometry);
2284 if (IsIconic(win)) {
2285 argv[argc++] = XtNewString("-iconic");
2286 wasIconic = True;
2287 } else if (wasIconic) {
2288 argv[argc++] = XtNewString("-noiconic");
2289 wasIconic = False;
2291 if (win->filenameSet) {
2292 argv[argc] = XtMalloc(strlen(win->path) +
2293 strlen(win->filename) + 1);
2294 sprintf(argv[argc++], "%s%s", win->path, win->filename);
2297 XtFree((char *)revWindowList);
2299 /* Set the window's WM_COMMAND property to the created command line */
2300 XSetCommand(TheDisplay, XtWindow(window->shell), argv, argc);
2301 for (i=0; i<argc; i++)
2302 XtFree(argv[i]);
2303 XtFree((char *)argv);
2307 ** Returns true if window is iconic (as determined by the WM_STATE property
2308 ** on the shell window. I think this is the most reliable way to tell,
2309 ** but if someone has a better idea please send me a note).
2311 int IsIconic(WindowInfo *window)
2313 unsigned long *property = NULL;
2314 unsigned long nItems;
2315 unsigned long leftover;
2316 static Atom wmStateAtom = 0;
2317 Atom actualType;
2318 int actualFormat;
2319 int result;
2321 if (wmStateAtom == 0)
2322 wmStateAtom = XInternAtom (XtDisplay(window->shell), "WM_STATE", False);
2323 if (XGetWindowProperty(XtDisplay(window->shell), XtWindow(window->shell),
2324 wmStateAtom, 0L, 1L, False, wmStateAtom, &actualType, &actualFormat,
2325 &nItems, &leftover, (unsigned char **)&property) != Success ||
2326 nItems != 1 || property == NULL)
2327 return FALSE;
2328 result = *property == IconicState;
2329 XtFree((char *)property);
2330 return result;
2334 ** Add a window to the the window list.
2336 static void addToWindowList(WindowInfo *window)
2338 WindowInfo *temp;
2340 temp = WindowList;
2341 WindowList = window;
2342 window->next = temp;
2346 ** Remove a window from the list of windows
2348 static void removeFromWindowList(WindowInfo *window)
2350 WindowInfo *temp;
2352 if (WindowList == window)
2353 WindowList = window->next;
2354 else {
2355 for (temp = WindowList; temp != NULL; temp = temp->next) {
2356 if (temp->next == window) {
2357 temp->next = window->next;
2358 break;
2365 ** If necessary, enlarges the window and line number display area
2366 ** to make room for numbers.
2368 void UpdateLineNumDisp(WindowInfo *window)
2370 Dimension windowWidth;
2371 int i, fontWidth, reqCols, lineNumCols;
2372 int oldWidth, newWidth, marginWidth;
2373 Widget text;
2374 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
2376 if (!window->showLineNumbers)
2377 return;
2379 /* Decide how wide the line number field has to be to display all
2380 possible line numbers */
2381 reqCols = textD->nBufferLines<1 ? 1 : log10((double)textD->nBufferLines)+1;
2382 if (reqCols < MIN_LINE_NUM_COLS)
2383 reqCols = MIN_LINE_NUM_COLS;
2385 /* Is the width of the line number area sufficient to display all the
2386 line numbers in the file? If not, expand line number field, and the
2387 window width */
2388 XtVaGetValues(window->textArea, textNlineNumCols, &lineNumCols,
2389 textNmarginWidth, &marginWidth, NULL);
2390 if (lineNumCols < reqCols) {
2391 fontWidth = textD->fontStruct->max_bounds.width;
2392 oldWidth = textD->left - marginWidth;
2393 newWidth = reqCols * fontWidth + marginWidth;
2394 XtVaGetValues(window->shell, XmNwidth, &windowWidth, NULL);
2395 XtVaSetValues(window->shell, XmNwidth,
2396 windowWidth + newWidth-oldWidth, NULL);
2397 UpdateWMSizeHints(window);
2398 for (i=0; i<=window->nPanes; i++) {
2399 text = i==0 ? window->textArea : window->textPanes[i-1];
2400 XtVaSetValues(text, textNlineNumCols, reqCols, NULL);
2406 ** Update the optional statistics line.
2408 void UpdateStatsLine(WindowInfo *window)
2410 int line, pos, colNum;
2411 char *string, *format, slinecol[32];
2412 Widget statW = window->statsLine;
2413 XmString xmslinecol;
2414 #ifdef SGI_CUSTOM
2415 char *sleft, *smid, *sright;
2416 #endif
2418 if (!IsTopBuffer(window))
2419 return;
2421 /* This routine is called for each character typed, so its performance
2422 affects overall editor perfomance. Only update if the line is on. */
2423 if (!window->showStats)
2424 return;
2426 /* Compose the string to display. If line # isn't available, leave it off */
2427 pos = TextGetCursorPos(window->lastFocus);
2428 string = XtMalloc(strlen(window->filename) + strlen(window->path) + 45);
2429 format = window->fileFormat == DOS_FILE_FORMAT ? " DOS" :
2430 (window->fileFormat == MAC_FILE_FORMAT ? " Mac" : "");
2431 if (!TextPosToLineAndCol(window->lastFocus, pos, &line, &colNum)) {
2432 sprintf(string, "%s%s%s %d bytes", window->path, window->filename,
2433 format, window->buffer->length);
2434 sprintf(slinecol, "L: --- C: ---");
2435 } else {
2436 sprintf(slinecol, "L: %d C: %d", line, colNum);
2437 if (window->showLineNumbers)
2438 sprintf(string, "%s%s%s byte %d of %d", window->path,
2439 window->filename, format, pos,
2440 window->buffer->length);
2441 else
2442 sprintf(string, "%s%s%s %d bytes", window->path,
2443 window->filename, format, window->buffer->length);
2446 /* Update the line/column number */
2447 xmslinecol = XmStringCreateSimple(slinecol);
2448 XtVaSetValues( window->statsLineColNo,
2449 XmNlabelString, xmslinecol, NULL );
2450 XmStringFree(xmslinecol);
2452 /* Don't clobber the line if there's a special message being displayed */
2453 if (!window->modeMessageDisplayed) {
2454 /* Change the text in the stats line */
2455 #ifdef SGI_CUSTOM
2456 /* don't show full pathname, just dir and filename (+ byte info) */
2457 smid = strchr(string, '/');
2458 if ( smid != NULL ) {
2459 sleft = smid;
2460 sright = strrchr(string, '/');
2461 while (strcmp(smid, sright)) {
2462 sleft = smid;
2463 smid = strchr(sleft + 1, '/');
2465 XmTextReplace(statW, 0, XmTextGetLastPosition(statW), sleft + 1);
2466 } else
2467 XmTextReplace(statW, 0, XmTextGetLastPosition(statW), string);
2468 #else
2469 XmTextReplace(statW, 0, XmTextGetLastPosition(statW), string);
2470 #endif
2472 XtFree(string);
2474 /* Update the line/col display */
2475 xmslinecol = XmStringCreateSimple(slinecol);
2476 XtVaSetValues(window->statsLineColNo,
2477 XmNlabelString, xmslinecol, NULL);
2478 XmStringFree(xmslinecol);
2481 static Boolean currentlyBusy = False;
2482 static long busyStartTime = 0;
2483 static Boolean modeMessageSet = False;
2486 * Auxiliary function for measuring elapsed time during busy waits.
2488 static long getRelTimeInTenthsOfSeconds()
2490 #ifdef __unix__
2491 struct timeval current;
2492 gettimeofday(&current, NULL);
2493 return (current.tv_sec*10 + current.tv_usec/100000) & 0xFFFFFFFL;
2494 #else
2495 time_t current;
2496 time(&current);
2497 return (current*10) & 0xFFFFFFFL;
2498 #endif
2501 void AllWindowsBusy(const char *message)
2503 WindowInfo *w;
2505 if (!currentlyBusy)
2507 busyStartTime = getRelTimeInTenthsOfSeconds();
2508 modeMessageSet = False;
2510 for (w=WindowList; w!=NULL; w=w->next)
2512 /* We don't the display message here yet, but defer it for
2513 a while. If the wait is short, we don't want
2514 to have it flash on and off the screen. However,
2515 we can't use a time since in generally we are in
2516 a tight loop and only processing exposure events, so it's
2517 up to the caller to make sure that this routine is called
2518 at regular intervals.
2520 BeginWait(w->shell);
2522 } else if (!modeMessageSet && message &&
2523 getRelTimeInTenthsOfSeconds() - busyStartTime > 10) {
2524 /* Show the mode message when we've been busy for more than a second */
2525 for (w=WindowList; w!=NULL; w=w->next) {
2526 SetModeMessage(w, message);
2528 modeMessageSet = True;
2530 BusyWait(WindowList->shell);
2532 currentlyBusy = True;
2535 void AllWindowsUnbusy(void)
2537 WindowInfo *w;
2539 for (w=WindowList; w!=NULL; w=w->next)
2541 /* ClearModeMessage(w); */
2542 EndWait(w->shell);
2545 currentlyBusy = False;
2546 modeMessageSet = False;
2547 busyStartTime = 0;
2551 ** Paned windows are impossible to adjust after they are created, which makes
2552 ** them nearly useless for NEdit (or any application which needs to dynamically
2553 ** adjust the panes) unless you tweek some private data to overwrite the
2554 ** desired and minimum pane heights which were set at creation time. These
2555 ** will probably break in a future release of Motif because of dependence on
2556 ** private data.
2558 static void setPaneDesiredHeight(Widget w, int height)
2560 ((XmPanedWindowConstraintPtr)w->core.constraints)->panedw.dheight = height;
2562 static void setPaneMinHeight(Widget w, int min)
2564 ((XmPanedWindowConstraintPtr)w->core.constraints)->panedw.min = min;
2568 ** Update the window manager's size hints. These tell it the increments in
2569 ** which it is allowed to resize the window. While this isn't particularly
2570 ** important for NEdit (since it can tolerate any window size), setting these
2571 ** hints also makes the resize indicator show the window size in characters
2572 ** rather than pixels, which is very helpful to users.
2574 void UpdateWMSizeHints(WindowInfo *window)
2576 Dimension shellWidth, shellHeight, textHeight, hScrollBarHeight;
2577 int marginHeight, marginWidth, totalHeight;
2578 XFontStruct *fs;
2579 int i, baseWidth, baseHeight, fontHeight, fontWidth;
2580 Widget hScrollBar;
2581 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
2583 /* Find the base (non-expandable) width and height of the editor window */
2584 XtVaGetValues(window->textArea, XmNheight, &textHeight,
2585 textNmarginHeight, &marginHeight, textNmarginWidth, &marginWidth,
2586 NULL);
2587 totalHeight = textHeight - 2*marginHeight;
2588 for (i=0; i<window->nPanes; i++) {
2589 XtVaGetValues(window->textPanes[i], XmNheight, &textHeight,
2590 textNhScrollBar, &hScrollBar, NULL);
2591 totalHeight += textHeight - 2*marginHeight;
2592 if (!XtIsManaged(hScrollBar)) {
2593 XtVaGetValues(hScrollBar, XmNheight, &hScrollBarHeight, NULL);
2594 totalHeight -= hScrollBarHeight;
2597 XtVaGetValues(window->shell, XmNwidth, &shellWidth,
2598 XmNheight, &shellHeight, NULL);
2599 baseWidth = shellWidth - textD->width;
2600 baseHeight = shellHeight - totalHeight;
2602 /* Find the dimensions of a single character of the text font */
2603 XtVaGetValues(window->textArea, textNfont, &fs, NULL);
2604 fontHeight = textD->ascent + textD->descent;
2605 fontWidth = fs->max_bounds.width;
2607 /* Set the size hints in the shell widget */
2608 XtVaSetValues(window->shell, XmNwidthInc, fs->max_bounds.width,
2609 XmNheightInc, fontHeight,
2610 XmNbaseWidth, baseWidth, XmNbaseHeight, baseHeight,
2611 XmNminWidth, baseWidth + fontWidth,
2612 XmNminHeight, baseHeight + (1+window->nPanes) * fontHeight, NULL);
2616 ** Update the minimum allowable height for a split window pane after a change
2617 ** to font or margin height.
2619 void UpdateMinPaneHeights(WindowInfo *window)
2621 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
2622 Dimension hsbHeight, swMarginHeight,frameShadowHeight;
2623 int i, marginHeight, minPaneHeight;
2624 Widget hScrollBar;
2626 /* find the minimum allowable size for a pane */
2627 XtVaGetValues(window->textArea, textNhScrollBar, &hScrollBar, NULL);
2628 XtVaGetValues(containingPane(window->textArea),
2629 XmNscrolledWindowMarginHeight, &swMarginHeight, NULL);
2630 XtVaGetValues(XtParent(window->textArea),
2631 XmNshadowThickness, &frameShadowHeight, NULL);
2632 XtVaGetValues(window->textArea, textNmarginHeight, &marginHeight, NULL);
2633 XtVaGetValues(hScrollBar, XmNheight, &hsbHeight, NULL);
2634 minPaneHeight = textD->ascent + textD->descent + marginHeight*2 +
2635 swMarginHeight*2 + hsbHeight + 2*frameShadowHeight;
2637 /* Set it in all of the widgets in the window */
2638 setPaneMinHeight(containingPane(window->textArea), minPaneHeight);
2639 for (i=0; i<window->nPanes; i++)
2640 setPaneMinHeight(containingPane(window->textPanes[i]),
2641 minPaneHeight);
2644 /* Add an icon to an applicaction shell widget. addWindowIcon adds a large
2645 ** (primary window) icon, AddSmallIcon adds a small (secondary window) icon.
2647 ** Note: I would prefer that these were not hardwired, but yhere is something
2648 ** weird about the XmNiconPixmap resource that prevents it from being set
2649 ** from the defaults in the application resource database.
2651 static void addWindowIcon(Widget shell)
2653 static Pixmap iconPixmap = 0, maskPixmap = 0;
2655 if (iconPixmap == 0) {
2656 iconPixmap = XCreateBitmapFromData(TheDisplay,
2657 RootWindowOfScreen(XtScreen(shell)), (char *)iconBits,
2658 iconBitmapWidth, iconBitmapHeight);
2659 maskPixmap = XCreateBitmapFromData(TheDisplay,
2660 RootWindowOfScreen(XtScreen(shell)), (char *)maskBits,
2661 iconBitmapWidth, iconBitmapHeight);
2663 XtVaSetValues(shell, XmNiconPixmap, iconPixmap, XmNiconMask, maskPixmap,
2664 NULL);
2666 void AddSmallIcon(Widget shell)
2668 static Pixmap iconPixmap = 0, maskPixmap = 0;
2670 if (iconPixmap == 0) {
2671 iconPixmap = XCreateBitmapFromData(TheDisplay,
2672 RootWindowOfScreen(XtScreen(shell)), (char *)n_bits,
2673 n_width, n_height);
2674 maskPixmap = XCreateBitmapFromData(TheDisplay,
2675 RootWindowOfScreen(XtScreen(shell)), (char *)n_mask,
2676 n_width, n_height);
2678 XtVaSetValues(shell, XmNiconPixmap, iconPixmap,
2679 XmNiconMask, maskPixmap, NULL);
2682 static void setTabCloseButtonImage(Widget button)
2684 static Pixmap pixmap = 0;
2686 if (pixmap == 0) {
2687 Pixel fg, bg;
2688 int depth;
2690 /* create pixmap per the color depth setting. This fixes a
2691 BadMatch (X_CopyArea) error due to mismatching of color
2692 depth between the bitmap (depth of 1) and the screen,
2693 specifically on when linked to LessTif (0.93.x) on
2694 ASPLinux 7.1 */
2695 XtVaGetValues (button, XmNforeground, &fg, XmNbackground, &bg,
2696 XmNdepth, &depth, NULL);
2697 pixmap = XCreatePixmapFromBitmapData(TheDisplay,
2698 RootWindowOfScreen(XtScreen(button)), (char *)close_bits,
2699 close_width, close_height, fg, bg, depth);
2702 XtVaSetValues(button, XmNlabelPixmap, pixmap, NULL);
2706 ** Save the position and size of a window as an X standard geometry string.
2707 ** A string of at least MAX_GEOMETRY_STRING_LEN characters should be
2708 ** provided in the argument "geomString" to receive the result.
2710 static void getGeometryString(WindowInfo *window, char *geomString)
2712 int x, y, fontWidth, fontHeight, baseWidth, baseHeight;
2713 unsigned int width, height, dummyW, dummyH, bw, depth, nChild;
2714 Window parent, root, *child, w = XtWindow(window->shell);
2715 Display *dpy = XtDisplay(window->shell);
2717 /* Find the width and height from the window of the shell */
2718 XGetGeometry(dpy, w, &root, &x, &y, &width, &height, &bw, &depth);
2720 /* Find the top left corner (x and y) of the window decorations. (This
2721 is what's required in the geometry string to restore the window to it's
2722 original position, since the window manager re-parents the window to
2723 add it's title bar and menus, and moves the requested window down and
2724 to the left.) The position is found by traversing the window hier-
2725 archy back to the window to the last parent before the root window */
2726 for(;;) {
2727 XQueryTree(dpy, w, &root, &parent, &child, &nChild);
2728 XFree((char*)child);
2729 if (parent == root)
2730 break;
2731 w = parent;
2733 XGetGeometry(dpy, w, &root, &x, &y, &dummyW, &dummyH, &bw, &depth);
2735 /* Use window manager size hints (set by UpdateWMSizeHints) to
2736 translate the width and height into characters, as opposed to pixels */
2737 XtVaGetValues(window->shell, XmNwidthInc, &fontWidth,
2738 XmNheightInc, &fontHeight, XmNbaseWidth, &baseWidth,
2739 XmNbaseHeight, &baseHeight, NULL);
2740 width = (width-baseWidth) / fontWidth;
2741 height = (height-baseHeight) / fontHeight;
2743 /* Write the string */
2744 CreateGeometryString(geomString, x, y, width, height,
2745 XValue | YValue | WidthValue | HeightValue);
2749 ** Xt timer procedure for updating size hints. The new sizes of objects in
2750 ** the window are not ready immediately after adding or removing panes. This
2751 ** is a timer routine to be invoked with a timeout of 0 to give the event
2752 ** loop a chance to finish processing the size changes before reading them
2753 ** out for setting the window manager size hints.
2755 static void wmSizeUpdateProc(XtPointer clientData, XtIntervalId *id)
2757 UpdateWMSizeHints((WindowInfo *)clientData);
2760 #ifdef ROWCOLPATCH
2762 ** There is a bad memory reference in the delete_child method of the
2763 ** RowColumn widget in some Motif versions (so far, just Solaris with Motif
2764 ** 1.2.3) which appears durring the phase 2 destroy of the widget. This
2765 ** patch replaces the method with a call to the Composite widget's
2766 ** delete_child method. The composite delete_child method handles part,
2767 ** but not all of what would have been done by the original method, meaning
2768 ** that this is dangerous and should be used sparingly. Note that
2769 ** patchRowCol is called only in CloseWindow, before the widget is about to
2770 ** be destroyed, and only on systems where the bug has been observed
2772 static void patchRowCol(Widget w)
2774 ((XmRowColumnClassRec *)XtClass(w))->composite_class.delete_child =
2775 patchedRemoveChild;
2777 static void patchedRemoveChild(Widget child)
2779 /* Call composite class method instead of broken row col delete_child
2780 method */
2781 (*((CompositeWidgetClass)compositeWidgetClass)->composite_class.
2782 delete_child) (child);
2784 #endif /* ROWCOLPATCH */
2787 ** Set the backlight character class string
2789 void SetBacklightChars(WindowInfo *window, char *applyBacklightTypes)
2791 int i;
2792 int is_applied = XmToggleButtonGetState(window->backlightCharsItem) ? 1 : 0;
2793 int do_apply = applyBacklightTypes ? 1 : 0;
2795 window->backlightChars = do_apply;
2797 XtFree(window->backlightCharTypes);
2798 if (window->backlightChars &&
2799 (window->backlightCharTypes = XtMalloc(strlen(applyBacklightTypes)+1)))
2800 strcpy(window->backlightCharTypes, applyBacklightTypes);
2801 else
2802 window->backlightCharTypes = NULL;
2804 XtVaSetValues(window->textArea,
2805 textNbacklightCharTypes, window->backlightCharTypes, 0);
2806 for (i=0; i<window->nPanes; i++)
2807 XtVaSetValues(window->textPanes[i],
2808 textNbacklightCharTypes, window->backlightCharTypes, 0);
2809 if (is_applied != do_apply)
2810 XmToggleButtonSetState(window->backlightCharsItem, do_apply, False);
2813 static int sortAlphabetical(const void* k1, const void* k2)
2815 const char* key1 = *(const char**)k1;
2816 const char* key2 = *(const char**)k2;
2817 return strcmp(key1, key2);
2821 * Checks whether a given virtual key binding string is invalid.
2822 * A binding is considered invalid if there are duplicate key entries.
2824 static int virtKeyBindingsAreInvalid(const unsigned char* bindings)
2826 int maxCount = 1, i, count;
2827 const char *pos = (const char*)bindings;
2828 char *copy;
2829 char *pos2, *pos3;
2830 char **keys;
2831 /* First count the number of bindings; bindings are separated by \n
2832 strings. The number of bindings equals the number of \n + 1.
2833 Beware of leading and trailing \n; the number is actually an
2834 upper bound on the number of entries. */
2835 while ((pos = strstr(pos, "\n"))) { ++pos; ++maxCount; }
2837 if (maxCount == 1) return False; /* One binding is always ok */
2839 keys = (char**)malloc(maxCount*sizeof(char*));
2840 copy = XtNewString((const char*)bindings);
2841 i = 0;
2842 pos2 = copy;
2844 count = 0;
2845 while (i<maxCount && pos2 && *pos2)
2847 while (isspace((int) *pos2) || *pos2 == '\n') ++pos2;
2849 if (*pos2 == '!') /* Ignore comment lines */
2851 pos2 = strstr(pos2, "\n");
2852 continue; /* Go to the next line */
2855 if (*pos2)
2857 keys[i++] = pos2;
2858 ++count;
2859 pos3 = strstr(pos2, ":");
2860 if (pos3)
2862 *pos3++ = 0; /* Cut the string and jump to the next entry */
2863 pos2 = pos3;
2865 pos2 = strstr(pos2, "\n");
2869 if (count <= 1)
2871 free(keys);
2872 XtFree(copy);
2873 return False; /* No conflict */
2876 /* Sort the keys and look for duplicates */
2877 qsort((void*)keys, count, sizeof(const char*), sortAlphabetical);
2878 for (i=1; i<count; ++i)
2880 if (!strcmp(keys[i-1], keys[i]))
2882 /* Duplicate detected */
2883 free(keys);
2884 XtFree(copy);
2885 return True;
2888 free(keys);
2889 XtFree(copy);
2890 return False;
2894 * Optionally sanitizes the Motif default virtual key bindings.
2895 * Some applications install invalid bindings (attached to the root window),
2896 * which cause certain keys to malfunction in NEdit.
2897 * Through an X-resource, users can choose whether they want
2898 * - to always keep the existing bindings
2899 * - to override the bindings only if they are invalid
2900 * - to always override the existing bindings.
2903 static Atom virtKeyAtom;
2905 static unsigned char* sanitizeVirtualKeyBindings()
2907 int overrideBindings = GetPrefOverrideVirtKeyBindings();
2908 Window rootWindow;
2909 const char *virtKeyPropName = "_MOTIF_DEFAULT_BINDINGS";
2910 Atom dummyAtom;
2911 int getFmt;
2912 unsigned long dummyULong, nItems;
2913 unsigned char *insaneVirtKeyBindings = NULL;
2915 if (overrideBindings == VIRT_KEY_OVERRIDE_NEVER) return NULL;
2917 virtKeyAtom = XInternAtom(TheDisplay, virtKeyPropName, False);
2918 rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
2920 /* Remove the property, if it exists; we'll restore it later again */
2921 if (XGetWindowProperty(TheDisplay, rootWindow, virtKeyAtom, 0, INT_MAX,
2922 True, XA_STRING, &dummyAtom, &getFmt, &nItems,
2923 &dummyULong, &insaneVirtKeyBindings) != Success
2924 || nItems == 0)
2926 return NULL; /* No binding yet; nothing to do */
2929 if (overrideBindings == VIRT_KEY_OVERRIDE_AUTO)
2931 if (!virtKeyBindingsAreInvalid(insaneVirtKeyBindings))
2933 /* Restore the property immediately; it seems valid */
2934 XChangeProperty(TheDisplay, rootWindow, virtKeyAtom, XA_STRING, 8,
2935 PropModeReplace, insaneVirtKeyBindings,
2936 strlen((const char*)insaneVirtKeyBindings));
2937 XFree((char*)insaneVirtKeyBindings);
2938 return NULL; /* Prevent restoration */
2941 return insaneVirtKeyBindings;
2945 * NEdit should not mess with the bindings installed by other apps, so we
2946 * just restore whatever was installed, if necessary
2948 static void restoreInsaneVirtualKeyBindings(unsigned char *insaneVirtKeyBindings)
2950 if (insaneVirtKeyBindings)
2952 Window rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
2953 /* Restore the root window atom, such that we don't affect
2954 other apps. */
2955 XChangeProperty(TheDisplay, rootWindow, virtKeyAtom, XA_STRING, 8,
2956 PropModeReplace, insaneVirtKeyBindings,
2957 strlen((const char*)insaneVirtKeyBindings));
2958 XFree((char*)insaneVirtKeyBindings);
2963 ** perform generic management on the children (toolbars) of toolBarsForm,
2964 ** a.k.a. statsForm, by setting the form attachment of the managed child
2965 ** widgets per their position/order.
2967 ** You can optionally create separator after a toolbar widget with it's
2968 ** widget name set to "TOOLBAR_SEP", which will appear below the toolbar
2969 ** widget. These seperators will then be managed automatically by this
2970 ** routine along with the toolbars they 'attached' to.
2972 ** It also takes care of the attachment offset settings of the child
2973 ** widgets to keep the border lines of the parent form displayed, so
2974 ** you don't have set them before hand.
2976 ** Note: XtManage/XtUnmange the target child (toolbar) before calling this
2977 ** function.
2979 ** Returns the last toolbar widget managed.
2982 static Widget manageToolBars(Widget toolBarsForm)
2984 Widget topWidget = NULL;
2985 WidgetList children;
2986 int n, nItems=0;
2988 XtVaGetValues(toolBarsForm, XmNchildren, &children,
2989 XmNnumChildren, &nItems, NULL);
2991 for (n=0; n<nItems; n++) {
2992 Widget tbar = children[n];
2994 if (XtIsManaged(tbar)) {
2995 if (topWidget) {
2996 XtVaSetValues(tbar, XmNtopAttachment, XmATTACH_WIDGET,
2997 XmNtopWidget, topWidget,
2998 XmNbottomAttachment, XmATTACH_NONE,
2999 XmNleftOffset, STAT_SHADOW_THICKNESS,
3000 XmNrightOffset, STAT_SHADOW_THICKNESS,
3001 NULL);
3003 else {
3004 /* the very first toolbar on top */
3005 XtVaSetValues(tbar, XmNtopAttachment, XmATTACH_FORM,
3006 XmNbottomAttachment, XmATTACH_NONE,
3007 XmNleftOffset, STAT_SHADOW_THICKNESS,
3008 XmNtopOffset, STAT_SHADOW_THICKNESS,
3009 XmNrightOffset, STAT_SHADOW_THICKNESS,
3010 NULL);
3013 topWidget = tbar;
3015 /* if the next widget is a separator, turn it on */
3016 if (n+1<nItems && !strcmp(XtName(children[n+1]), "TOOLBAR_SEP")) {
3017 XtManageChild(children[n+1]);
3020 else {
3021 /* Remove top attachment to widget to avoid circular dependency.
3022 Attach bottom to form so that when the widget is redisplayed
3023 later, it will trigger the parent form to resize properly as
3024 if the widget is being inserted */
3025 XtVaSetValues(tbar, XmNtopAttachment, XmATTACH_NONE,
3026 XmNbottomAttachment, XmATTACH_FORM, NULL);
3028 /* if the next widget is a separator, turn it off */
3029 if (n+1<nItems && !strcmp(XtName(children[n+1]), "TOOLBAR_SEP")) {
3030 XtUnmanageChild(children[n+1]);
3035 if (topWidget) {
3036 if (strcmp(XtName(topWidget), "TOOLBAR_SEP")) {
3037 XtVaSetValues(topWidget,
3038 XmNbottomAttachment, XmATTACH_FORM,
3039 XmNbottomOffset, STAT_SHADOW_THICKNESS,
3040 NULL);
3042 else {
3043 /* is a separator */
3044 Widget wgt;
3045 XtVaGetValues(topWidget, XmNtopWidget, &wgt, NULL);
3047 /* don't need sep below bottom-most toolbar */
3048 XtUnmanageChild(topWidget);
3049 XtVaSetValues(wgt,
3050 XmNbottomAttachment, XmATTACH_FORM,
3051 XmNbottomOffset, STAT_SHADOW_THICKNESS,
3052 NULL);
3056 return topWidget;
3060 ** Calculate the dimension of the text area, in terms of rows & cols,
3061 ** as if there's only one single text pane in the window.
3063 static void getTextPaneDimension(WindowInfo *window, int *nRows, int *nCols)
3065 Widget hScrollBar;
3066 Dimension hScrollBarHeight, paneHeight;
3067 int marginHeight, marginWidth, totalHeight, fontHeight;
3068 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
3070 /* width is the same for panes */
3071 XtVaGetValues(window->textArea, textNcolumns, nCols, NULL);
3073 /* we have to work out the height, as the text area may have been split */
3074 XtVaGetValues(window->textArea, textNhScrollBar, &hScrollBar,
3075 textNmarginHeight, &marginHeight, textNmarginWidth, &marginWidth,
3076 NULL);
3077 XtVaGetValues(hScrollBar, XmNheight, &hScrollBarHeight, NULL);
3078 XtVaGetValues(window->splitPane, XmNheight, &paneHeight, NULL);
3079 totalHeight = paneHeight - 2*marginHeight -hScrollBarHeight;
3080 fontHeight = textD->ascent + textD->descent;
3081 *nRows = totalHeight/fontHeight;
3085 ** Create a new buffer in the shell window
3087 WindowInfo *CreateBuffer(WindowInfo *shellWindow, const char *name,
3088 char *geometry, int iconic)
3090 Widget pane, text;
3091 WindowInfo *window;
3092 int nCols, nRows;
3094 #ifdef SGI_CUSTOM
3095 char sgi_title[MAXPATHLEN + 14 + SGI_WINDOW_TITLE_LEN] = SGI_WINDOW_TITLE;
3096 #endif
3098 /* Allocate some memory for the new window data structure */
3099 window = (WindowInfo *)XtMalloc(sizeof(WindowInfo));
3100 memcpy(window, shellWindow, sizeof(WindowInfo));
3102 /* initialize window structure */
3103 /* + Schwarzenberg: should a
3104 memset(window, 0, sizeof(WindowInfo));
3105 be added here ?
3107 #if 0
3108 /* share these dialog items with parent shell */
3109 window->replaceDlog = NULL;
3110 window->replaceText = NULL;
3111 window->replaceWithText = NULL;
3112 window->replaceWordToggle = NULL;
3113 window->replaceCaseToggle = NULL;
3114 window->replaceRegexToggle = NULL;
3115 window->findDlog = NULL;
3116 window->findText = NULL;
3117 window->findWordToggle = NULL;
3118 window->findCaseToggle = NULL;
3119 window->findRegexToggle = NULL;
3120 window->replaceMultiFileDlog = NULL;
3121 window->replaceMultiFilePathBtn = NULL;
3122 window->replaceMultiFileList = NULL;
3123 #endif
3124 window->multiFileReplSelected = FALSE;
3125 window->multiFileBusy = FALSE;
3126 window->writableWindows = NULL;
3127 window->nWritableWindows = 0;
3128 window->fileChanged = FALSE;
3129 window->fileMissing = True;
3130 window->fileMode = 0;
3131 window->filenameSet = FALSE;
3132 window->fileFormat = UNIX_FILE_FORMAT;
3133 window->lastModTime = 0;
3134 strcpy(window->filename, name);
3135 window->undo = NULL;
3136 window->redo = NULL;
3137 window->nPanes = 0;
3138 window->autoSaveCharCount = 0;
3139 window->autoSaveOpCount = 0;
3140 window->undoOpCount = 0;
3141 window->undoMemUsed = 0;
3142 CLEAR_ALL_LOCKS(window->lockReasons);
3143 window->indentStyle = GetPrefAutoIndent(PLAIN_LANGUAGE_MODE);
3144 window->autoSave = GetPrefAutoSave();
3145 window->saveOldVersion = GetPrefSaveOldVersion();
3146 window->wrapMode = GetPrefWrap(PLAIN_LANGUAGE_MODE);
3147 window->overstrike = False;
3148 window->showMatchingStyle = GetPrefShowMatching();
3149 window->matchSyntaxBased = GetPrefMatchSyntaxBased();
3150 window->showStats = GetPrefStatsLine();
3151 window->showISearchLine = GetPrefISearchLine();
3152 window->showLineNumbers = GetPrefLineNums();
3153 window->highlightSyntax = GetPrefHighlightSyntax();
3154 window->backlightCharTypes = NULL;
3155 window->backlightChars = GetPrefBacklightChars();
3156 if (window->backlightChars) {
3157 char *cTypes = GetPrefBacklightCharTypes();
3158 if (cTypes && window->backlightChars) {
3159 if ((window->backlightCharTypes = XtMalloc(strlen(cTypes) + 1)))
3160 strcpy(window->backlightCharTypes, cTypes);
3163 window->modeMessageDisplayed = FALSE;
3164 window->ignoreModify = FALSE;
3165 window->windowMenuValid = FALSE;
3166 window->prevOpenMenuValid = FALSE;
3167 window->flashTimeoutID = 0;
3168 window->wasSelected = FALSE;
3169 strcpy(window->fontName, GetPrefFontName());
3170 strcpy(window->italicFontName, GetPrefItalicFontName());
3171 strcpy(window->boldFontName, GetPrefBoldFontName());
3172 strcpy(window->boldItalicFontName, GetPrefBoldItalicFontName());
3173 window->colorDialog = NULL;
3174 window->fontList = GetPrefFontList();
3175 window->italicFontStruct = GetPrefItalicFont();
3176 window->boldFontStruct = GetPrefBoldFont();
3177 window->boldItalicFontStruct = GetPrefBoldItalicFont();
3178 window->fontDialog = NULL;
3179 window->nMarks = 0;
3180 window->markTimeoutID = 0;
3181 window->highlightData = NULL;
3182 window->shellCmdData = NULL;
3183 window->macroCmdData = NULL;
3184 window->smartIndentData = NULL;
3185 window->languageMode = PLAIN_LANGUAGE_MODE;
3186 window->iSearchHistIndex = 0;
3187 window->iSearchStartPos = -1;
3188 window->replaceLastRegexCase = TRUE;
3189 window->replaceLastLiteralCase = FALSE;
3190 window->iSearchLastRegexCase = TRUE;
3191 window->iSearchLastLiteralCase = FALSE;
3192 window->findLastRegexCase = TRUE;
3193 window->findLastLiteralCase = FALSE;
3194 window->bufferTab = NULL;
3196 if (window->fontList == NULL)
3197 XtVaGetValues(shellWindow->statsLine, XmNfontList,
3198 &window->fontList,NULL);
3200 getTextPaneDimension(shellWindow, &nRows, &nCols);
3202 /* Create pane for new buffer. We defer mapping the pane widget
3203 to reduce flickers caused by its resizing when the text area
3204 is added to it */
3205 pane = XtVaCreateWidget("pane",
3206 xmPanedWindowWidgetClass, window->mainWin,
3207 XmNmarginWidth, 0, XmNmarginHeight, 0, XmNseparatorOn, False,
3208 XmNspacing, 3, XmNsashIndent, -2,
3209 XmNmappedWhenManaged, False,
3210 NULL);
3211 XtVaSetValues(window->mainWin, XmNworkWindow, pane, NULL);
3212 XtManageChild(pane);
3213 window->splitPane = pane;
3215 /* buffer/window info should associate with text pane */
3216 XtVaSetValues(pane, XmNuserData, window, NULL);
3218 /* Patch around Motif's most idiotic "feature", that its menu accelerators
3219 recognize Caps Lock and Num Lock as modifiers, and don't trigger if
3220 they are engaged */
3221 AccelLockBugPatch(pane, window->menuBar);
3223 /* Create the first, and most permanent text area (other panes may
3224 be added & removed, but this one will never be removed */
3225 text = createTextArea(pane, window, nRows, nCols,
3226 GetPrefEmTabDist(PLAIN_LANGUAGE_MODE), GetPrefDelimiters(),
3227 GetPrefWrapMargin(), window->showLineNumbers?MIN_LINE_NUM_COLS:0);
3228 XtManageChild(text);
3229 window->textArea = text;
3230 window->lastFocus = text;
3232 /* Set the initial colors from the globals. */
3233 SetColors(window,
3234 GetPrefColorName(TEXT_FG_COLOR ),
3235 GetPrefColorName(TEXT_BG_COLOR ),
3236 GetPrefColorName(SELECT_FG_COLOR),
3237 GetPrefColorName(SELECT_BG_COLOR),
3238 GetPrefColorName(HILITE_FG_COLOR),
3239 GetPrefColorName(HILITE_BG_COLOR),
3240 GetPrefColorName(LINENO_FG_COLOR),
3241 GetPrefColorName(CURSOR_FG_COLOR));
3243 /* map the new buffer pane but keep it hidden */
3244 XLowerWindow(TheDisplay, XtWindow(pane));
3245 XtMapWidget(pane);
3247 /* Create the right button popup menu (note: order is important here,
3248 since the translation for popping up this menu was probably already
3249 added in createTextArea, but CreateBGMenu requires window->textArea
3250 to be set so it can attach the menu to it (because menu shells are
3251 finicky about the kinds of widgets they are attached to)) */
3252 window->bgMenuPane = CreateBGMenu(window);
3254 /* Create the text buffer rather than using the one created automatically
3255 with the text area widget. This is done so the syntax highlighting
3256 modify callback can be called to synchronize the style buffer BEFORE
3257 the text display's callback is called upon to display a modification */
3258 window->buffer = BufCreate();
3259 BufAddModifyCB(window->buffer, SyntaxHighlightModifyCB, window);
3261 /* Attach the buffer to the text widget, and add callbacks for modify */
3262 TextSetBuffer(text, window->buffer);
3263 BufAddModifyCB(window->buffer, modifiedCB, window);
3265 /* Designate the permanent text area as the owner for selections */
3266 HandleXSelections(text);
3268 /* Set the requested hardware tab distance and useTabs in the text buffer */
3269 BufSetTabDistance(window->buffer, GetPrefTabDist(PLAIN_LANGUAGE_MODE));
3270 window->buffer->useTabs = GetPrefInsertTabs();
3272 /* add the window to the global window list, update the Windows menus */
3273 InvalidateWindowMenus();
3274 addToWindowList(window);
3276 window->bufferTab = addBufferTab(window->bufferTabBar, window, name);
3277 ShowBufferTabBar(window, GetPrefTabBar());
3278 return window;
3282 ** return the next window/buffer on the tab list.
3284 static WindowInfo *getNextTabWindow(WindowInfo *window, int direction,
3285 int crossWin)
3287 int n, tabPos, nextPos;
3288 WindowInfo **winList, *win;
3289 int nBuf = crossWin? NWindows() : NBuffers(window);
3291 if (nBuf <= 1)
3292 return NULL;
3294 winList = (WindowInfo **)XtMalloc(sizeof(WindowInfo *) * nBuf);
3295 n = nBuf -1;
3296 for (win=WindowList; win && n>=0; win=win->next) {
3297 if (crossWin)
3298 winList[n--] = win;
3299 else if (win->shell == window->shell)
3300 winList[n--] = win;
3303 for (n=0; n<nBuf; n++) {
3304 if (winList[n] == window) {
3305 tabPos = n;
3306 break;
3310 /* calculate index position of next tab */
3311 nextPos = tabPos + direction;
3312 if (nextPos >= nBuf)
3313 nextPos = 0;
3314 else if (nextPos < 0)
3315 nextPos = nBuf - 1;
3317 /* find which window next tab belongs to */
3318 win = winList[nextPos];
3319 XtFree((char *)winList);
3320 return win;
3324 ** return the integer position of a tab in the tabbar it
3325 ** belongs to, or -1 if there's an error, somehow.
3327 static int getTabPosition(Widget tab)
3329 WidgetList tabList;
3330 int i, tabCount;
3331 Widget tabBar = XtParent(tab);
3333 XtVaGetValues(tabBar, XmNtabWidgetList, &tabList,
3334 XmNtabCount, &tabCount, NULL);
3336 for (i=0; i< tabCount; i++) {
3337 if (tab == tabList[i])
3338 return i;
3341 return -1; /* something is wrong! */
3345 ** update the tab label, etc. of a tab per the states of it's buffer.
3347 void RefreshTabState(WindowInfo *win)
3349 XmString s1, tipString;
3350 char labelString[MAXPATHLEN];
3352 sprintf(labelString, "%s%s", win->fileChanged? "*" : "",
3353 win->filename);
3354 s1=XmStringCreateSimple(labelString);
3356 if (GetPrefShowPathInWindowsMenu() && win->filenameSet) {
3357 strcat(labelString, " - ");
3358 strcat(labelString, win->path);
3360 tipString=XmStringCreateSimple(labelString);
3362 XtVaSetValues(win->bufferTab,
3363 XltNbubbleString, tipString,
3364 XmNlabelString, s1,
3365 NULL);
3366 XmStringFree(s1);
3367 XmStringFree(tipString);
3371 ** close all the buffers in a shell window
3373 int CloseAllBufferInWindow(WindowInfo *window)
3375 WindowInfo *win;
3377 if (NBuffers(window) == 1) {
3378 /* the only buffer in the window */
3379 return CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE);
3381 else {
3382 Widget winShell = window->shell;
3383 WindowInfo *topBuffer;
3385 /* close all _modified_ buffers belong to this window */
3386 for (win = WindowList; win; ) {
3387 if (win->shell == winShell && win->fileChanged) {
3388 WindowInfo *next = win->next;
3389 if (!CloseFileAndWindow(win, PROMPT_SBC_DIALOG_RESPONSE))
3390 return False;
3391 win = next;
3393 else
3394 win = win->next;
3397 /* see there's still buffers left in the window */
3398 for (win = WindowList; win; win=win->next)
3399 if (win->shell == winShell)
3400 break;
3402 if (win) {
3403 topBuffer = GetTopBuffer(winShell);
3405 /* close all non-top buffers belong to this window */
3406 for (win = WindowList; win; ) {
3407 if (win->shell == winShell && win != topBuffer) {
3408 WindowInfo *next = win->next;
3409 if (!CloseFileAndWindow(win, PROMPT_SBC_DIALOG_RESPONSE))
3410 return False;
3411 win = next;
3413 else
3414 win = win->next;
3417 /* close the last buffer and its window */
3418 if (!CloseFileAndWindow(topBuffer, PROMPT_SBC_DIALOG_RESPONSE))
3419 return False;
3423 return True;
3426 static void CloseBufferWindow(Widget w, WindowInfo *window, XtPointer callData)
3428 int nBuffers = NBuffers(window);
3430 if (nBuffers == NWindows()) {
3431 /* this is only window, then exit */
3432 XtCallActionProc(WindowList->lastFocus, "exit",
3433 ((XmAnyCallbackStruct *)callData)->event, NULL, 0);
3435 else {
3436 if (nBuffers == 1) {
3437 CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE);
3439 else {
3440 int resp = DialogF(DF_QUES, window->shell, 2, "Close Window",
3441 "Close ALL buffers in this window?", "Close", "Cancel");
3443 if (resp == 1)
3444 CloseAllBufferInWindow(window);
3449 static void cloneTextPane(WindowInfo *window, WindowInfo *orgWin)
3451 short paneHeights[MAX_PANES+1];
3452 int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1];
3453 int horizOffsets[MAX_PANES+1];
3454 int i, focusPane, emTabDist, wrapMargin, lineNumCols, totalHeight=0;
3455 char *delimiters;
3456 Widget text;
3457 selection sel;
3459 /* old window must disown hilite data */
3460 orgWin->highlightData = NULL;
3462 /* transfer the primary selection */
3463 memcpy(&sel, &orgWin->buffer->primary, sizeof(selection));
3465 if (sel.selected) {
3466 if (sel.rectangular)
3467 BufRectSelect(window->buffer, sel.start, sel.end,
3468 sel.rectStart, sel.rectEnd);
3469 else
3470 BufSelect(window->buffer, sel.start, sel.end);
3471 } else
3472 BufUnselect(window->buffer);
3474 /* Record the current heights, scroll positions, and insert positions
3475 of the existing panes, keyboard focus */
3476 focusPane = 0;
3477 for (i=0; i<=orgWin->nPanes; i++) {
3478 text = i==0 ? orgWin->textArea : orgWin->textPanes[i-1];
3479 insertPositions[i] = TextGetCursorPos(text);
3480 XtVaGetValues(XtParent(text), XmNheight, &paneHeights[i], NULL);
3481 totalHeight += paneHeights[i];
3482 TextGetScroll(text, &topLines[i], &horizOffsets[i]);
3483 if (text == orgWin->lastFocus)
3484 focusPane = i;
3487 window->nPanes = orgWin->nPanes;
3489 /* clone split panes, if any */
3490 if (window->nPanes) {
3491 /* Unmanage & remanage the panedWindow so it recalculates pane heights */
3492 XtUnmanageChild(window->splitPane);
3494 /* Create a text widget to add to the pane and set its buffer and
3495 highlight data to be the same as the other panes in the orgWin */
3496 XtVaGetValues(orgWin->textArea, textNemulateTabs, &emTabDist,
3497 textNwordDelimiters, &delimiters, textNwrapMargin, &wrapMargin,
3498 textNlineNumCols, &lineNumCols, NULL);
3500 for(i=0; i<orgWin->nPanes; i++) {
3501 text = createTextArea(window->splitPane, window, 1, 1, emTabDist,
3502 delimiters, wrapMargin, lineNumCols);
3503 TextSetBuffer(text, window->buffer);
3505 if (window->highlightData != NULL)
3506 AttachHighlightToWidget(text, window);
3507 XtManageChild(text);
3508 window->textPanes[i] = text;
3511 /* Set the minimum pane height in the new pane */
3512 UpdateMinPaneHeights(window);
3514 for (i=0; i<=window->nPanes; i++) {
3515 text = i==0 ? window->textArea : window->textPanes[i-1];
3516 setPaneDesiredHeight(containingPane(text), paneHeights[i]);
3519 /* Re-manage panedWindow to recalculate pane heights & reset selection */
3520 XtManageChild(window->splitPane);
3523 /* Reset all of the heights, scroll positions, etc. */
3524 for (i=0; i<=window->nPanes; i++) {
3525 textDisp *textD;
3527 text = i==0 ? window->textArea : window->textPanes[i-1];
3528 TextSetCursorPos(text, insertPositions[i]);
3529 TextSetScroll(text, topLines[i], horizOffsets[i]);
3531 /* dim the cursor */
3532 textD = ((TextWidget)text)->text.textD;
3533 TextDSetCursorStyle(textD, DIM_CURSOR);
3534 TextDUnblankCursor(textD);
3537 /* set the focus pane */
3538 for (i=0; i<=window->nPanes; i++) {
3539 text = i==0 ? window->textArea : window->textPanes[i-1];
3540 if(i == focusPane) {
3541 window->lastFocus = text;
3542 XmProcessTraversal(text, XmTRAVERSE_CURRENT);
3543 break;
3547 /* Update the window manager size hints after the sizes of the panes have
3548 been set (the widget heights are not yet readable here, but they will
3549 be by the time the event loop gets around to running this timer proc) */
3550 XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0,
3551 wmSizeUpdateProc, window);
3555 ** Refresh the menu entries per the settings of the
3556 ** top/active buffer.
3558 void RefreshMenuToggleStates(WindowInfo *window)
3560 WindowInfo *win;
3562 /* File menu */
3563 XtSetSensitive(window->printSelItem, window->wasSelected);
3565 /* Edit menu */
3566 XtSetSensitive(window->undoItem, window->undo != NULL);
3567 XtSetSensitive(window->redoItem, window->redo != NULL);
3568 XtSetSensitive(window->printSelItem, window->wasSelected);
3569 XtSetSensitive(window->cutItem, window->wasSelected);
3570 XtSetSensitive(window->copyItem, window->wasSelected);
3571 XtSetSensitive(window->delItem, window->wasSelected);
3573 /* Preferences menu */
3574 XmToggleButtonSetState(window->statsLineItem, window->showStats, False);
3575 XmToggleButtonSetState(window->iSearchLineItem, window->showISearchLine, False);
3576 XmToggleButtonSetState(window->lineNumsItem, window->showLineNumbers, False);
3577 XmToggleButtonSetState(window->highlightItem, window->highlightSyntax, False);
3578 XtSetSensitive(window->highlightItem, window->languageMode != PLAIN_LANGUAGE_MODE);
3579 XmToggleButtonSetState(window->backlightCharsItem, window->backlightChars, False);
3580 XmToggleButtonSetState(window->saveLastItem, window->saveOldVersion, False);
3581 XmToggleButtonSetState(window->autoSaveItem, window->autoSave, False);
3582 XmToggleButtonSetState(window->overtypeModeItem, window->overstrike, False);
3583 XmToggleButtonSetState(window->matchSyntaxBasedItem, window->matchSyntaxBased, False);
3584 XmToggleButtonSetState(window->readOnlyItem, IS_USER_LOCKED(window->lockReasons), False);
3586 XtSetSensitive(window->smartIndentItem,
3587 SmartIndentMacrosAvailable(LanguageModeName(window->languageMode)));
3589 SetAutoIndent(window, window->indentStyle);
3590 SetAutoWrap(window, window->wrapMode);
3591 SetShowMatching(window, window->showMatchingStyle);
3592 SetLanguageMode(window, window->languageMode, FALSE);
3594 /* Windows Menu */
3595 XtSetSensitive(window->splitWindowItem, window->nPanes < MAX_PANES);
3596 XtSetSensitive(window->closePaneItem, window->nPanes > 0);
3597 XtSetSensitive(window->detachBufferItem, NBuffers(window)>1);
3599 for (win=WindowList; win; win=win->next)
3600 if (win->shell != window->shell)
3601 break;
3602 XtSetSensitive(window->attachBufferItem, win != NULL);
3606 ** Refresh the various settings/state of the shell window per the
3607 ** settings of the top/active buffer.
3609 static void refreshBufferMenuBar(WindowInfo *window)
3611 RefreshMenuToggleStates(window);
3613 /* Add/remove language specific menu items */
3614 #ifndef VMS
3615 UpdateShellMenu(window);
3616 #endif
3617 UpdateMacroMenu(window);
3618 UpdateBGMenu(window);
3620 /* refresh selection-sensitive menus */
3621 DimSelectionDepUserMenuItems(window, window->wasSelected);
3624 static void setBufferSharedPref(WindowInfo *window, WindowInfo *lastwin)
3626 window->showTabBar = lastwin->showTabBar;
3627 window->showStats = lastwin->showStats;
3628 window->showISearchLine = lastwin->showISearchLine;
3632 ** remember the last active buffer
3634 WindowInfo *MarkLastBuffer(WindowInfo *window)
3636 WindowInfo *prev = lastBuffer;
3638 if (window)
3639 lastBuffer = window;
3641 return prev;
3645 ** remember the active buffer
3647 WindowInfo *MarkActiveBuffer(WindowInfo *window)
3649 WindowInfo *prev = focusInBuffer;
3651 if (window)
3652 focusInBuffer = window;
3654 return prev;
3658 ** Bring up the next window by tab order
3660 void NextBuffer(WindowInfo *window)
3662 WindowInfo *win;
3664 if (WindowList->next == NULL)
3665 return;
3667 win = getNextTabWindow(window, 1, GetPrefGlobalTabNavigate());
3669 if (window->shell == win->shell)
3670 RaiseBuffer(win);
3671 else
3672 RaiseBufferWindow(win);
3676 ** Bring up the previous window by tab order
3678 void PreviousBuffer(WindowInfo *window)
3680 WindowInfo *win;
3682 if (WindowList->next == NULL)
3683 return;
3685 win = getNextTabWindow(window, -1, GetPrefGlobalTabNavigate());
3687 if (window->shell == win->shell)
3688 RaiseBuffer(win);
3689 else
3690 RaiseBufferWindow(win);
3694 ** Bring up the last active window
3696 void ToggleBuffer(WindowInfo *window)
3698 WindowInfo *win;
3700 for(win = WindowList; win; win=win->next)
3701 if (lastBuffer == win)
3702 break;
3704 if (!win)
3705 return;
3707 if (window->shell == win->shell)
3708 RaiseBuffer(win);
3709 else
3710 RaiseBufferWindow(win);
3715 ** make sure window is alive is kicking
3717 int IsValidWindow(WindowInfo *window)
3719 WindowInfo *win;
3721 for(win = WindowList; win; win=win->next)
3722 if (window == win)
3723 return True;
3726 return False;
3730 ** raise the buffer and its shell window
3732 void RaiseBufferWindow(WindowInfo *window)
3734 RaiseBuffer(window);
3735 RaiseShellWindow(window->shell);
3739 ** raise the buffer in its shell window
3741 void RaiseBuffer(WindowInfo *window)
3743 WindowInfo *win, *lastwin;
3745 if (!GetPrefBufferMode())
3746 return;
3748 lastwin = MarkActiveBuffer(window);
3749 if (lastwin != window && IsValidWindow(lastwin))
3750 MarkLastBuffer(lastwin);
3752 if (!GetPrefBufferMode() || !window || !WindowList)
3753 return;
3755 /* buffer already active? */
3756 XtVaGetValues(window->mainWin, XmNuserData, &win, NULL);
3758 if (win == window)
3759 return;
3761 /* refresh shared menu items */
3762 setBufferSharedPref(window, win);
3764 /* set the buffer as active */
3765 XtVaSetValues(window->mainWin, XmNuserData, window, NULL);
3767 /* show the new top buffer */
3768 XtVaSetValues(window->mainWin, XmNworkWindow, window->splitPane, NULL);
3769 XtManageChild(window->splitPane);
3770 XRaiseWindow(TheDisplay, XtWindow(window->splitPane));
3772 /* set tab as active */
3773 XmLFolderSetActiveTab(window->bufferTabBar,
3774 getTabPosition(window->bufferTab), False);
3776 /* set keyboard focus. Must be done before unmanaging previous
3777 top buffer, else lastFocus will be reset to textArea */
3778 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
3780 /* we only manage the top buffer, else the next time a buffer
3781 is raised again, it's textpane might not resize properly.
3782 Also, somehow (bug?) XtUnmanageChild() doesn't hide the
3783 splitPane, which obscure lower part of the statsform where
3784 we toggle its components, so we need to put the buffer at
3785 the back */
3786 XLowerWindow(TheDisplay, XtWindow(win->splitPane));
3787 XtUnmanageChild(win->splitPane);
3789 /* now refresh window state/info. RefreshBufferWindowState()
3790 has a lot of work to do, so we update the screen first so
3791 the buffers appear to switch immediately */
3792 XmUpdateDisplay(window->splitPane);
3793 RefreshBufferWindowState(window);
3796 WindowInfo* GetTopBuffer(Widget w)
3798 WindowInfo *window = WidgetToWindow(w);
3800 return WidgetToWindow(window->shell);
3803 Boolean IsTopBuffer(const WindowInfo *window)
3805 return window == GetTopBuffer(window->shell)? True : False;
3808 void DeleteBuffer(WindowInfo *window)
3810 if (!GetPrefBufferMode() || !window)
3811 return;
3813 XtDestroyWidget(window->splitPane);
3817 ** clone a buffer into the other.
3819 static void cloneBuffer(WindowInfo *window, WindowInfo *orgWin)
3821 char *orgBuffer;
3822 char *params[4];
3824 strcpy(window->path, orgWin->path);
3825 strcpy(window->filename, orgWin->filename);
3827 ShowLineNumbers(window, orgWin->showLineNumbers);
3829 /* copy the buffer */
3830 window->ignoreModify = True;
3831 orgBuffer = BufGetAll(orgWin->buffer);
3832 BufSetAll(window->buffer, orgBuffer);
3833 window->ignoreModify = False;
3834 XtFree(orgBuffer);
3836 /* transfer text fonts */
3837 params[0] = orgWin->fontName;
3838 params[1] = orgWin->italicFontName;
3839 params[2] = orgWin->boldFontName;
3840 params[3] = orgWin->boldItalicFontName;
3841 XtCallActionProc(window->textArea, "set_fonts", NULL, params, 4);
3843 SetBacklightChars(window, orgWin->backlightCharTypes);
3845 /* recycle the hilite data */
3846 window->languageMode = orgWin->languageMode;
3847 window->highlightData = orgWin->highlightData;
3848 if (window->highlightData != NULL)
3849 AttachHighlightToWidget(window->textArea, window);
3851 /* clone original buffer's states */
3852 window->filenameSet = orgWin->filenameSet;
3853 window->fileFormat = orgWin->fileFormat;
3854 window->lastModTime = orgWin->lastModTime;
3855 window->fileChanged = orgWin->fileChanged;
3856 window->fileMissing = orgWin->fileMissing;
3857 window->lockReasons = orgWin->lockReasons;
3858 window->nPanes = orgWin->nPanes;
3859 window->autoSaveCharCount = orgWin->autoSaveCharCount;
3860 window->autoSaveOpCount = orgWin->autoSaveOpCount;
3861 window->undoOpCount = orgWin->undoOpCount;
3862 window->undoMemUsed = orgWin->undoMemUsed;
3863 window->lockReasons = orgWin->lockReasons;
3864 window->autoSave = orgWin->autoSave;
3865 window->saveOldVersion = orgWin->saveOldVersion;
3866 window->wrapMode = orgWin->wrapMode;
3867 window->overstrike = orgWin->overstrike;
3868 window->showMatchingStyle = orgWin->showMatchingStyle;
3869 window->matchSyntaxBased = orgWin->matchSyntaxBased;
3870 window->highlightSyntax = orgWin->highlightSyntax;
3871 #if 0
3872 window->showStats = orgWin->showStats;
3873 window->showISearchLine = orgWin->showISearchLine;
3874 window->showLineNumbers = orgWin->showLineNumbers;
3875 window->modeMessageDisplayed = orgWin->modeMessageDisplayed;
3876 window->ignoreModify = orgWin->ignoreModify;
3877 window->windowMenuValid = orgWin->windowMenuValid;
3878 window->prevOpenMenuValid = orgWin->prevOpenMenuValid;
3879 window->flashTimeoutID = orgWin->flashTimeoutID;
3880 window->wasSelected = orgWin->wasSelected;
3881 strcpy(window->fontName, orgWin->fontName);
3882 strcpy(window->italicFontName, orgWin->italicFontName);
3883 strcpy(window->boldFontName, orgWin->boldFontName);
3884 strcpy(window->boldItalicFontName, orgWin->boldItalicFontName);
3885 window->fontList = orgWin->fontList;
3886 window->italicFontStruct = orgWin->italicFontStruct;
3887 window->boldFontStruct = orgWin->boldFontStruct;
3888 window->boldItalicFontStruct = orgWin->boldItalicFontStruct;
3889 window->nMarks = orgWin->nMarks;
3890 window->markTimeoutID = orgWin->markTimeoutID;
3891 window->highlightData = orgWin->highlightData;
3892 window->shellCmdData = orgWin->shellCmdData;
3893 window->macroCmdData = orgWin->macroCmdData;
3894 window->smartIndentData = orgWin->smartIndentData;
3895 #endif
3896 window->iSearchHistIndex = orgWin->iSearchHistIndex;
3897 window->iSearchStartPos = orgWin->iSearchStartPos;
3898 window->replaceLastRegexCase = orgWin->replaceLastRegexCase;
3899 window->replaceLastLiteralCase = orgWin->replaceLastLiteralCase;
3900 window->iSearchLastRegexCase = orgWin->iSearchLastRegexCase;
3901 window->iSearchLastLiteralCase = orgWin->iSearchLastLiteralCase;
3902 window->findLastRegexCase = orgWin->findLastRegexCase;
3903 window->findLastLiteralCase = orgWin->findLastLiteralCase;
3905 /* copy the text/split panes settings, cursor pos & selection */
3906 cloneTextPane(window, orgWin);
3908 /* copy undo & redo list */
3909 window->undo = cloneUndoItems(orgWin->undo);
3910 window->redo = cloneUndoItems(orgWin->redo);
3912 /* kick start the auto-indent engine */
3913 window->indentStyle = NO_AUTO_INDENT;
3914 SetAutoIndent(window, orgWin->indentStyle);
3916 /* synchronize window state to this buffer */
3917 RefreshBufferWindowState(window);
3920 static UndoInfo *cloneUndoItems(UndoInfo *orgList)
3922 UndoInfo *head = NULL, *undo, *clone, *last = NULL;
3924 for (undo = orgList; undo; undo = undo->next) {
3925 clone = (UndoInfo *)XtMalloc(sizeof(UndoInfo));
3926 memcpy(clone, undo, sizeof(UndoInfo));
3928 if (undo->oldText) {
3929 clone->oldText = XtMalloc(strlen(undo->oldText)+1);
3930 strcpy(clone->oldText, undo->oldText);
3932 clone->next = NULL;
3934 if (last)
3935 last->next = clone;
3936 else
3937 head = clone;
3939 last = clone;
3942 return head;
3946 ** return number of buffers own by this shell window
3948 int NBuffers(WindowInfo *window)
3950 WindowInfo *win;
3951 int nBuffer = 0;
3953 if (!GetPrefBufferMode())
3954 return 1;
3956 for (win = WindowList; win; win = win->next) {
3957 if (win->shell == window->shell)
3958 nBuffer++;
3961 return nBuffer;
3965 ** refresh window state for this buffer
3967 void RefreshBufferWindowState(WindowInfo *window)
3969 if (!GetPrefBufferMode() || !IsTopBuffer(window))
3970 return;
3972 UpdateStatsLine(window);
3973 UpdateWindowReadOnly(window);
3974 UpdateWindowTitle(window);
3976 /* we need to force the statsline to reveal itself */
3977 XmTextSetCursorPosition(window->statsLine, 0); /* start of line */
3978 XmTextSetCursorPosition(window->statsLine, 9000); /* end of line */
3980 XmUpdateDisplay(window->statsLine);
3981 refreshBufferMenuBar(window);
3985 ** spin off the buffer to a new window
3987 WindowInfo *DetachBuffer(WindowInfo *window)
3989 WindowInfo *win, *cloneWin;
3990 char *dim, geometry[MAX_GEOM_STRING_LEN];
3992 if (NBuffers(window) < 2)
3993 return NULL;
3995 /* raise another buffer in the same shell window */
3996 win = replacementBuffer(window);
3997 RaiseBuffer(win);
3999 /* create new window in roughly the size of original window,
4000 to reduce flicker when the window is resized later */
4001 getGeometryString(window, geometry);
4002 dim = strtok(geometry, "+-");
4003 cloneWin = CreateWindow(window->filename, dim, False);
4005 /* these settings should follow the detached buffer.
4006 must be done before cloning window, else the height
4007 of split panes may not come out correctly */
4008 ShowISearchLine(cloneWin, window->showISearchLine);
4009 ShowStatsLine(cloneWin, window->showStats);
4011 /* clone the buffer & its pref settings */
4012 cloneBuffer(cloneWin, window);
4014 /* remove the buffer from the old window */
4015 window->fileChanged = False;
4016 CloseFileAndWindow(window, NO_SBC_DIALOG_RESPONSE);
4018 /* some menu states might have changed when deleting buffer */
4019 RefreshBufferWindowState(win);
4021 /* this should keep the new buffer window fresh */
4022 RefreshBufferWindowState(cloneWin);
4023 RefreshTabState(cloneWin);
4025 return cloneWin;
4029 ** attach (move) a buffer to an other window.
4031 ** the attaching buffer will inherit the window settings from
4032 ** its new hosts, i.e. the window size, stats and isearch lines.
4034 WindowInfo *AttachBuffer(WindowInfo *toWindow, WindowInfo *window)
4036 WindowInfo *win, *cloneWin;
4038 /* raise another buffer in the window of attaching buffer */
4039 for (win = WindowList; win; win = win->next) {
4040 if (win->shell == window->shell && window != win)
4041 break;
4044 if (win)
4045 RaiseBuffer(win);
4046 else
4047 XtUnmapWidget(window->shell);
4049 /* relocate the buffer to target window */
4050 cloneWin = CreateBuffer(toWindow, window->filename, NULL, False);
4051 cloneBuffer(cloneWin, window);
4053 /* remove the buffer from the old window */
4054 window->fileChanged = False;
4055 CloseFileAndWindow(window, NO_SBC_DIALOG_RESPONSE);
4057 /* some menu states might have changed when deleting buffer */
4058 if (win) {
4059 RefreshBufferWindowState(win);
4062 /* this should keep the new buffer window fresh */
4063 RaiseBufferWindow(cloneWin);
4064 RefreshTabState(cloneWin);
4066 return cloneWin;
4069 static void attachBufferCB(Widget dialog, WindowInfo *parentWin,
4070 XtPointer call_data)
4072 XmSelectionBoxCallbackStruct *cbs = (XmSelectionBoxCallbackStruct *) call_data;
4073 DoneWithAttachBufferDialog = cbs->reason;
4077 ** present dialog to selecting target window for attaching
4078 ** buffers. Return immediately if there is only one shell window.
4080 void AttachBufferDialog(Widget parent)
4082 WindowInfo *parentWin = WidgetToWindow(parent);
4083 WindowInfo *win, *attachWin, **shellWinList;
4084 int i, nList=0, nWindows=0, ac;
4085 char tmpStr[MAXPATHLEN+50];
4086 Widget dialog, listBox, attachAllOption;
4087 XmString *list = NULL;
4088 XmString popupTitle, s1;
4089 Arg csdargs[20];
4090 int *position_list, position_count;
4092 /* create the window list */
4093 nWindows = NWindows();
4094 list = (XmStringTable) XtMalloc(nWindows * sizeof(XmString *));
4095 shellWinList = (WindowInfo **) XtMalloc(nWindows * sizeof(WindowInfo *));
4097 for (win=WindowList; win; win=win->next) {
4098 if (win->shell == parentWin->shell)
4099 continue;
4101 if (!IsTopBuffer(win))
4102 continue;
4104 sprintf(tmpStr, "%s%s",
4105 win->filenameSet? win->path : "", win->filename);
4107 list[nList] = XmStringCreateSimple(tmpStr);
4108 shellWinList[nList] = win;
4109 nList++;
4112 if (!nList) {
4113 XtFree((char *)list);
4114 return;
4117 sprintf(tmpStr, "Attach %s to:", parentWin->filename);
4118 popupTitle = XmStringCreateSimple(tmpStr);
4119 ac = 0;
4120 XtSetArg(csdargs[ac], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); ac++;
4121 XtSetArg(csdargs[ac], XmNlistLabelString, popupTitle); ac++;
4122 XtSetArg(csdargs[ac], XmNlistItems, list); ac++;
4123 XtSetArg(csdargs[ac], XmNlistItemCount, nList); ac++;
4124 XtSetArg(csdargs[ac], XmNvisibleItemCount, 12); ac++;
4125 XtSetArg(csdargs[ac], XmNautoUnmanage, False); ac++;
4126 dialog = CreateSelectionDialog(parent,"attachBuffer",csdargs,ac);
4127 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT));
4128 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
4129 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_SELECTION_LABEL));
4130 XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)attachBufferCB, parentWin);
4131 XtAddCallback(dialog, XmNapplyCallback, (XtCallbackProc)attachBufferCB, parentWin);
4132 XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)attachBufferCB, parentWin);
4133 XmStringFree(popupTitle);
4135 /* free the window list */
4136 for (i=0; i<nList; i++)
4137 XmStringFree(list[i]);
4138 XtFree((char *)list);
4140 /* create the option box for attaching all buffers */
4141 s1 = MKSTRING("Attach all buffers in window");
4142 attachAllOption = XtVaCreateWidget("attachAll",
4143 xmToggleButtonWidgetClass, dialog,
4144 XmNlabelString, s1,
4145 XmNalignment, XmALIGNMENT_BEGINNING,
4146 NULL);
4147 XmStringFree(s1);
4149 if (NBuffers(parentWin) >1)
4150 XtManageChild(attachAllOption);
4152 /* only one buffer in the window */
4153 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_APPLY_BUTTON));
4155 s1 = MKSTRING("Attach");
4156 XtVaSetValues (dialog, XmNokLabelString, s1, NULL);
4157 XmStringFree(s1);
4159 /* default to the first window on the list */
4160 listBox = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);
4161 XmListSelectPos(listBox, 1, True);
4162 /* show the dialog */
4163 DoneWithAttachBufferDialog = 0;
4164 ManageDialogCenteredOnPointer(dialog);
4165 while (!DoneWithAttachBufferDialog)
4166 XtAppProcessEvent(XtWidgetToApplicationContext(parent), XtIMAll);
4168 /* get window to attach buffer */
4169 XmListGetSelectedPos(listBox, &position_list, &position_count);
4170 attachWin = shellWinList[position_list[0]-1];
4171 XtFree((char *)position_list);
4173 /* now attach buffer(s) */
4174 if (DoneWithAttachBufferDialog == XmCR_OK) {
4175 /* attach top (active) buffer */
4176 if (XmToggleButtonGetState(attachAllOption)) {
4177 /* attach all buffers */
4178 for (win = WindowList; win; ) {
4179 if (win != parentWin && win->shell == parentWin->shell) {
4180 WindowInfo *next = win->next;
4181 AttachBuffer(attachWin, win);
4182 win = next;
4184 else
4185 win = win->next;
4188 /* top buffer is last to attach */
4189 AttachBuffer(attachWin, parentWin);
4191 else {
4192 AttachBuffer(attachWin, parentWin);
4196 XtFree((char *)shellWinList);
4197 XtDestroyWidget(dialog);
4200 static void hideTooltip(Widget tab)
4202 Widget tooltip = XtNameToWidget(tab, "*BubbleShell");
4204 if (tooltip)
4205 XtPopdown(tooltip);
4209 ** callback to close-tab button.
4211 static void closeTabCB(Widget w, Widget mainWin, caddr_t callData)
4213 CloseFileAndWindow(GetTopBuffer(mainWin), PROMPT_SBC_DIALOG_RESPONSE);
4217 ** callback to tab (button).
4219 static void clickTabCB(Widget w, XtPointer *clientData, XtPointer callData)
4221 hideTooltip(w);
4225 ** callback to tab (tabbar) that raise the buffer.
4227 static void raiseTabCB(Widget w, XtPointer *clientData, XtPointer callData)
4229 XmLFolderCallbackStruct *cbs = (XmLFolderCallbackStruct *)callData;
4230 WidgetList tabList;
4231 Widget tab;
4233 XtVaGetValues(w, XmNtabWidgetList, &tabList, NULL);
4234 tab = tabList[cbs->pos];
4235 RaiseBuffer(TabToWindow(tab));
4238 static Widget containingPane(Widget w)
4240 /* The containing pane used to simply be the first parent, but with
4241 the introduction of an XmFrame, it's the grandparent. */
4242 return XtParent(XtParent(w));