Added bugs #559902, 561659, 526981, and 582469.
[nedit.git] / source / window.c
blob6874e71b2c11594a64af4bdf67fbeb255d2fca09
1 static const char CVSID[] = "$Id: window.c,v 1.82 2003/05/24 19:15:21 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 "../util/clearcase.h"
56 #include "../util/misc.h"
57 #include "../util/fileUtils.h"
58 #include "../util/utils.h"
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #ifdef VMS
64 #include "../util/VMSparam.h"
65 #else
66 #ifndef __MVS__
67 #include <sys/param.h>
68 #endif
69 #include "../util/clearcase.h"
70 #endif /*VMS*/
71 #include <limits.h>
72 #include <math.h>
73 #include <ctype.h>
74 #include <time.h>
75 #ifdef __unix__
76 #include <sys/time.h>
77 #endif
79 #include <X11/Intrinsic.h>
80 #include <X11/Shell.h>
81 #include <X11/Xatom.h>
82 #include <Xm/Xm.h>
83 #include <Xm/MainW.h>
84 #include <Xm/PanedW.h>
85 #include <Xm/PanedWP.h>
86 #include <Xm/RowColumnP.h>
87 #include <Xm/Text.h>
88 #include <Xm/ToggleB.h>
89 #include <Xm/Form.h>
90 #include <Xm/Label.h>
91 #include <Xm/Protocols.h>
92 #include <Xm/ScrolledW.h>
93 #include <Xm/ScrollBar.h>
94 #include <Xm/PrimitiveP.h>
95 #include <Xm/Frame.h>
96 #ifdef EDITRES
97 #include <X11/Xmu/Editres.h>
98 /* extern void _XEditResCheckMessages(); */
99 #endif /* EDITRES */
102 #ifdef HAVE_DEBUG_H
103 #include "../debug.h"
104 #endif
107 /* Initial minimum height of a pane. Just a fallback in case setPaneMinHeight
108 (which may break in a future release) is not available */
109 #define PANE_MIN_HEIGHT 39
111 /* Thickness of 3D border around statistics and/or incremental search areas
112 below the main menu bar */
113 #define STAT_SHADOW_THICKNESS 1
115 static Widget createTextArea(Widget parent, WindowInfo *window, int rows,
116 int cols, int emTabDist, char *delimiters, int wrapMargin,
117 int lineNumCols);
118 static void showStats(WindowInfo *window, int state);
119 static void showISearch(WindowInfo *window, int state);
120 static void showStatsForm(WindowInfo *window, int state);
121 static void addToWindowList(WindowInfo *window);
122 static void removeFromWindowList(WindowInfo *window);
123 static void focusCB(Widget w, WindowInfo *window, XtPointer callData);
124 static void modifiedCB(int pos, int nInserted, int nDeleted, int nRestyled,
125 char *deletedText, void *cbArg);
126 static void movedCB(Widget w, WindowInfo *window, XtPointer callData);
127 static void dragStartCB(Widget w, WindowInfo *window, XtPointer callData);
128 static void dragEndCB(Widget w, WindowInfo *window, dragEndCBStruct *callData);
129 static void closeCB(Widget w, WindowInfo *window, XtPointer callData);
130 static void saveYourselfCB(Widget w, WindowInfo *window, XtPointer callData);
131 static int isIconic(WindowInfo *window);
132 static void setPaneDesiredHeight(Widget w, int height);
133 static void setPaneMinHeight(Widget w, int min);
134 static void addWindowIcon(Widget shell);
135 static void wmSizeUpdateProc(XtPointer clientData, XtIntervalId *id);
136 static void getGeometryString(WindowInfo *window, char *geomString);
137 #ifdef ROWCOLPATCH
138 static void patchRowCol(Widget w);
139 static void patchedRemoveChild(Widget child);
140 #endif
141 static unsigned char* sanitizeVirtualKeyBindings();
142 static int sortAlphabetical(const void* k1, const void* k2);
143 static int virtKeyBindingsAreInvalid(const unsigned char* bindings);
144 static void restoreInsaneVirtualKeyBindings(unsigned char* bindings);
145 static Widget containingPane(Widget w);
147 /* Must be in same index order as enum enum colorTypes */
148 struct mapEntry
150 String resource;
151 enum colorTypes color;
154 static const struct mapEntry colorResourceMap[] =
156 { XmNforeground, TEXT_FG_COLOR },
157 { XmNbackground, TEXT_BG_COLOR },
158 { textNselectForeground, SELECT_FG_COLOR },
159 { textNselectBackground, SELECT_BG_COLOR },
160 { textNhighlightForeground, HILITE_FG_COLOR },
161 { textNhighlightBackground, HILITE_BG_COLOR },
162 { textNlineNumForeground, LINENO_FG_COLOR },
163 { textNcursorForeground, CURSOR_FG_COLOR },
167 ** Create a new editor window
169 WindowInfo *CreateWindow(const char *name, char *geometry, int iconic)
171 Widget appShell, mainWin, menuBar, pane, text, stats, statsAreaForm;
172 Widget iSearchLabel;
173 WindowInfo *window;
174 Pixel bgpix, fgpix;
175 Arg al[20];
176 int ac, c;
177 XmString s1;
178 #ifdef SGI_CUSTOM
179 char sgi_title[MAXPATHLEN + 14 + SGI_WINDOW_TITLE_LEN] = SGI_WINDOW_TITLE;
180 #endif
181 char newGeometry[MAX_GEOM_STRING_LEN];
182 unsigned int rows, cols;
183 int x, y, bitmask;
184 static Atom wmpAtom, syAtom = 0;
185 static int firstTime = True;
186 unsigned char* invalidBindings = NULL;
188 if (firstTime)
190 invalidBindings = sanitizeVirtualKeyBindings();
191 firstTime = False;
194 /* Allocate some memory for the new window data structure */
195 window = (WindowInfo *)XtMalloc(sizeof(WindowInfo));
197 /* initialize window structure */
198 /* + Schwarzenberg: should a
199 memset(window, 0, sizeof(WindowInfo));
200 be added here ?
202 window->replaceDlog = NULL;
203 window->replaceText = NULL;
204 window->replaceWithText = NULL;
205 window->replaceWordToggle = NULL;
206 window->replaceCaseToggle = NULL;
207 window->replaceRegexToggle = NULL;
208 window->findDlog = NULL;
209 window->findText = NULL;
210 window->findWordToggle = NULL;
211 window->findCaseToggle = NULL;
212 window->findRegexToggle = NULL;
213 window->replaceMultiFileDlog = NULL;
214 window->replaceMultiFilePathBtn = NULL;
215 window->replaceMultiFileList = NULL;
216 window->multiFileReplSelected = FALSE;
217 window->multiFileBusy = FALSE;
218 window->writableWindows = NULL;
219 window->nWritableWindows = 0;
220 window->fileChanged = FALSE;
221 window->fileMode = 0;
222 window->filenameSet = FALSE;
223 window->fileFormat = UNIX_FILE_FORMAT;
224 window->lastModTime = 0;
225 window->fileMissing = True;
226 strcpy(window->filename, name);
227 window->undo = NULL;
228 window->redo = NULL;
229 window->nPanes = 0;
230 window->autoSaveCharCount = 0;
231 window->autoSaveOpCount = 0;
232 window->undoOpCount = 0;
233 window->undoMemUsed = 0;
234 CLEAR_ALL_LOCKS(window->lockReasons);
235 window->indentStyle = GetPrefAutoIndent(PLAIN_LANGUAGE_MODE);
236 window->autoSave = GetPrefAutoSave();
237 window->saveOldVersion = GetPrefSaveOldVersion();
238 window->wrapMode = GetPrefWrap(PLAIN_LANGUAGE_MODE);
239 window->overstrike = False;
240 window->showMatchingStyle = GetPrefShowMatching();
241 window->matchSyntaxBased = GetPrefMatchSyntaxBased();
242 window->showStats = GetPrefStatsLine();
243 window->showISearchLine = GetPrefISearchLine();
244 window->showLineNumbers = GetPrefLineNums();
245 window->highlightSyntax = GetPrefHighlightSyntax();
246 window->backlightCharTypes = NULL;
247 window->backlightChars = GetPrefBacklightChars();
248 if (window->backlightChars) {
249 char *cTypes = GetPrefBacklightCharTypes();
250 if (cTypes && window->backlightChars) {
251 if ((window->backlightCharTypes = XtMalloc(strlen(cTypes) + 1)))
252 strcpy(window->backlightCharTypes, cTypes);
255 window->modeMessageDisplayed = FALSE;
256 window->ignoreModify = FALSE;
257 window->windowMenuValid = FALSE;
258 window->prevOpenMenuValid = FALSE;
259 window->flashTimeoutID = 0;
260 window->fileClosedAtom = None;
261 window->wasSelected = FALSE;
263 #if 0
264 for (c = 0; c < NUM_COLORS; c++) {
265 strncpy(window->colorNames[c], GetPrefColorName(c), MAX_COLOR_LEN);
267 #endif
269 strcpy(window->fontName, GetPrefFontName());
270 strcpy(window->italicFontName, GetPrefItalicFontName());
271 strcpy(window->boldFontName, GetPrefBoldFontName());
272 strcpy(window->boldItalicFontName, GetPrefBoldItalicFontName());
273 window->colorDialog = NULL;
274 window->fontList = GetPrefFontList();
275 window->italicFontStruct = GetPrefItalicFont();
276 window->boldFontStruct = GetPrefBoldFont();
277 window->boldItalicFontStruct = GetPrefBoldItalicFont();
278 window->fontDialog = NULL;
279 window->nMarks = 0;
280 window->markTimeoutID = 0;
281 window->highlightData = NULL;
282 window->shellCmdData = NULL;
283 window->macroCmdData = NULL;
284 window->smartIndentData = NULL;
285 window->languageMode = PLAIN_LANGUAGE_MODE;
286 window->iSearchHistIndex = 0;
287 window->iSearchStartPos = -1;
288 window->replaceLastRegexCase = TRUE;
289 window->replaceLastLiteralCase = FALSE;
290 window->iSearchLastRegexCase = TRUE;
291 window->iSearchLastLiteralCase = FALSE;
292 window->findLastRegexCase = TRUE;
293 window->findLastLiteralCase = FALSE;
295 /* If window geometry was specified, split it apart into a window position
296 component and a window size component. Create a new geometry string
297 containing the position component only. Rows and cols are stripped off
298 because we can't easily calculate the size in pixels from them until the
299 whole window is put together. Note that the preference resource is only
300 for clueless users who decide to specify the standard X geometry
301 application resource, which is pretty useless because width and height
302 are the same as the rows and cols preferences, and specifying a window
303 location will force all the windows to pile on top of one another */
304 if (geometry == NULL || geometry[0] == '\0')
305 geometry = GetPrefGeometry();
306 if (geometry == NULL || geometry[0] == '\0') {
307 rows = GetPrefRows();
308 cols = GetPrefCols();
309 newGeometry[0] = '\0';
310 } else {
311 bitmask = XParseGeometry(geometry, &x, &y, &cols, &rows);
312 if (bitmask == 0)
313 fprintf(stderr, "Bad window geometry specified: %s\n", geometry);
314 else {
315 if (!(bitmask & WidthValue))
316 cols = GetPrefCols();
317 if (!(bitmask & HeightValue))
318 rows = GetPrefRows();
320 CreateGeometryString(newGeometry, x, y, 0, 0,
321 bitmask & ~(WidthValue | HeightValue));
324 /* Create a new toplevel shell to hold the window */
325 ac = 0;
326 #ifdef SGI_CUSTOM
327 strcat(sgi_title, name);
328 XtSetArg(al[ac], XmNtitle, sgi_title); ac++;
329 XtSetArg(al[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
330 if (strncmp(name, "Untitled", 8) == 0) {
331 XtSetArg(al[ac], XmNiconName, APP_NAME); ac++;
332 } else {
333 XtSetArg(al[ac], XmNiconName, name); ac++;
335 #else
336 XtSetArg(al[ac], XmNtitle, name); ac++;
337 XtSetArg(al[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
338 XtSetArg(al[ac], XmNiconName, name); ac++;
339 #endif
340 XtSetArg(al[ac], XmNgeometry, newGeometry[0]=='\0'?NULL:newGeometry); ac++;
341 XtSetArg(al[ac], XmNinitialState,
342 iconic ? IconicState : NormalState); ac++;
344 appShell = CreateShellWithBestVis(APP_NAME, APP_CLASS,
345 applicationShellWidgetClass, TheDisplay, al, ac);
346 window->shell = appShell;
348 #ifdef EDITRES
349 XtAddEventHandler (appShell, (EventMask)0, True,
350 (XtEventHandler)_XEditResCheckMessages, NULL);
351 #endif /* EDITRES */
353 #ifndef SGI_CUSTOM
354 addWindowIcon(appShell);
355 #endif
357 /* Create a MainWindow to manage the menubar and text area, set the
358 userData resource to be used by WidgetToWindow to recover the
359 window pointer from the widget id of any of the window's widgets */
360 XtSetArg(al[ac], XmNuserData, window); ac++;
361 mainWin = XmCreateMainWindow(appShell, "main", al, ac);
362 XtManageChild(mainWin);
364 /* The statsAreaForm holds the stats line and the I-Search line. */
365 statsAreaForm = XtVaCreateWidget("statsAreaForm",
366 xmFormWidgetClass, mainWin,
367 XmNmarginWidth, STAT_SHADOW_THICKNESS,
368 XmNmarginHeight, STAT_SHADOW_THICKNESS,
369 /* XmNautoUnmanage, False, */
370 NULL);
371 if(window->showISearchLine || window->showStats)
372 XtManageChild(statsAreaForm);
374 /* NOTE: due to a bug in openmotif 2.1.30, NEdit used to crash when
375 the i-search bar was active, and the i-search text widget was focussed,
376 and the window's width was resized to nearly zero.
377 In theory, it is possible to avoid this by imposing a minimum
378 width constraint on the nedit windows, but that width would have to
379 be at least 30 characters, which is probably unacceptable.
380 Amazingly, adding a top offset of 1 pixel to the toggle buttons of
381 the i-search bar, while keeping the the top offset of the text widget
382 to 0 seems to avoid avoid the crash. */
384 window->iSearchForm = XtVaCreateWidget("iSearchForm",
385 xmFormWidgetClass, statsAreaForm,
386 XmNleftAttachment, XmATTACH_FORM,
387 XmNtopAttachment, XmATTACH_FORM,
388 XmNrightAttachment, XmATTACH_FORM,
389 XmNbottomAttachment, window->showStats ?
390 XmATTACH_NONE : XmATTACH_FORM,
391 NULL);
392 if(window->showISearchLine)
393 XtManageChild(window->iSearchForm);
394 iSearchLabel = XtVaCreateManagedWidget("iSearchLabel",
395 xmLabelWidgetClass, window->iSearchForm,
396 XmNlabelString, s1=XmStringCreateSimple("Find:"),
397 XmNmarginHeight, 0,
398 XmNleftAttachment, XmATTACH_FORM,
399 XmNleftOffset, 5,
400 XmNtopAttachment, XmATTACH_FORM,
401 XmNtopOffset, 1, /* see openmotif note above, for aligment
402 with toggle buttons below */
403 XmNbottomAttachment, XmATTACH_FORM, NULL);
404 XmStringFree(s1);
406 /* Disable keyboard traversal of the toggle buttons. We were doing
407 this previously by forcing the keyboard focus back to the text
408 widget whenever a toggle changed. That causes an ugly focus flash
409 on screen. It's better just not to go there in the first place.
410 Plus, if the user really wants traversal, it's an X resource so it
411 can be enabled without too much pain and suffering. */
413 window->iSearchCaseToggle = XtVaCreateManagedWidget("iSearchCaseToggle",
414 xmToggleButtonWidgetClass, window->iSearchForm,
415 XmNlabelString, s1=XmStringCreateSimple("Case"),
416 XmNset, GetPrefSearch() == SEARCH_CASE_SENSE
417 || GetPrefSearch() == SEARCH_REGEX
418 || GetPrefSearch() == SEARCH_CASE_SENSE_WORD,
419 XmNtopAttachment, XmATTACH_FORM,
420 XmNbottomAttachment, XmATTACH_FORM,
421 XmNtopOffset, 1, /* see openmotif note above */
422 XmNrightAttachment, XmATTACH_FORM,
423 XmNmarginHeight, 0,
424 XmNtraversalOn, False,
425 NULL);
426 XmStringFree(s1);
428 window->iSearchRegexToggle = XtVaCreateManagedWidget("iSearchREToggle",
429 xmToggleButtonWidgetClass, window->iSearchForm,
430 XmNlabelString, s1=XmStringCreateSimple("RegExp"),
431 XmNset, GetPrefSearch() == SEARCH_REGEX_NOCASE
432 || GetPrefSearch() == SEARCH_REGEX,
433 XmNtopAttachment, XmATTACH_FORM,
434 XmNbottomAttachment, XmATTACH_FORM,
435 XmNtopOffset, 1, /* see openmotif note above */
436 XmNrightAttachment, XmATTACH_WIDGET,
437 XmNrightWidget, window->iSearchCaseToggle,
438 XmNmarginHeight, 0,
439 XmNtraversalOn, False,
440 NULL);
441 XmStringFree(s1);
443 window->iSearchRevToggle = XtVaCreateManagedWidget("iSearchRevToggle",
444 xmToggleButtonWidgetClass, window->iSearchForm,
445 XmNlabelString, s1=XmStringCreateSimple("Rev"),
446 XmNset, False,
447 XmNtopAttachment, XmATTACH_FORM,
448 XmNbottomAttachment, XmATTACH_FORM,
449 XmNtopOffset, 1, /* see openmotif note above */
450 XmNrightAttachment, XmATTACH_WIDGET,
451 XmNrightWidget, window->iSearchRegexToggle,
452 XmNmarginHeight, 0,
453 XmNtraversalOn, False,
454 NULL);
455 XmStringFree(s1);
457 window->iSearchText = XtVaCreateManagedWidget("iSearchText",
458 xmTextWidgetClass, window->iSearchForm,
459 XmNmarginHeight, 1,
460 XmNnavigationType, XmEXCLUSIVE_TAB_GROUP,
461 XmNleftAttachment, XmATTACH_WIDGET,
462 XmNleftWidget, iSearchLabel,
463 XmNrightAttachment, XmATTACH_WIDGET,
464 XmNrightWidget, window->iSearchRevToggle,
465 XmNrightOffset, 5,
466 XmNtopAttachment, XmATTACH_FORM,
467 XmNtopOffset, 0, /* see openmotif note above */
468 XmNbottomAttachment, XmATTACH_FORM,
469 XmNbottomOffset, 0, NULL);
470 RemapDeleteKey(window->iSearchText);
472 SetISearchTextCallbacks(window);
474 /* A form to hold the stats line text and line/col widgets */
475 window->statsLineForm = XtVaCreateWidget("statsLineForm",
476 xmFormWidgetClass, statsAreaForm,
477 XmNshadowThickness, 0,
478 XmNtopAttachment, window->showISearchLine ?
479 XmATTACH_WIDGET : XmATTACH_FORM,
480 XmNtopWidget, window->iSearchForm,
481 XmNrightAttachment, XmATTACH_FORM,
482 XmNleftAttachment, XmATTACH_FORM,
483 XmNbottomAttachment, XmATTACH_FORM,
484 XmNresizable, False, /* */
485 NULL);
487 /* A separate display of the line/column number */
488 window->statsLineColNo = XtVaCreateManagedWidget("statsLineColNo",
489 xmLabelWidgetClass, window->statsLineForm,
490 XmNlabelString, s1=XmStringCreateSimple("L: --- C: ---"),
491 XmNshadowThickness, 0,
492 XmNmarginHeight, 0,
493 XmNmarginTop, 1, /* Help align with statsLine */
494 XmNtraversalOn, False,
495 XmNtopAttachment, XmATTACH_FORM,
496 XmNrightAttachment, XmATTACH_FORM,
497 XmNbottomAttachment, XmATTACH_FORM, /* */
498 NULL);
499 XmStringFree(s1);
501 /* Create file statistics display area. Using a text widget rather than
502 a label solves a layout problem with the main window, which messes up
503 if the label is too long (we would need a resize callback to control
504 the length when the window changed size), and allows users to select
505 file names and line numbers. Colors are copied from parent
506 widget, because many users and some system defaults color text
507 backgrounds differently from other widgets. */
509 XtVaGetValues(statsAreaForm, XmNbackground, &bgpix, NULL);
510 XtVaGetValues(statsAreaForm, XmNforeground, &fgpix, NULL);
511 stats = XtVaCreateManagedWidget("statsLine",
512 xmTextWidgetClass, window->statsLineForm,
513 XmNbackground, bgpix,
514 XmNforeground, fgpix,
515 XmNshadowThickness, 0,
516 XmNhighlightColor, bgpix,
517 XmNhighlightThickness, 1, /* Setting this to zero causes mysterious
518 problems with lesstif! */
519 XmNmarginHeight, 0,
520 XmNscrollHorizontal, False,
521 XmNeditMode, XmSINGLE_LINE_EDIT,
522 XmNeditable, False,
523 XmNtraversalOn, False,
524 XmNcursorPositionVisible, False,
525 XmNtopAttachment, XmATTACH_FORM,
526 XmNleftAttachment, XmATTACH_FORM,
527 XmNrightAttachment, XmATTACH_WIDGET,
528 XmNrightWidget, window->statsLineColNo,
529 XmNrightOffset, 3,
530 NULL);
531 window->statsLine = stats;
533 /* Manage the statsLineForm */
534 if(window->showStats)
535 XtManageChild(window->statsLineForm);
537 /* If the fontList was NULL, use the magical default provided by Motif,
538 since it must have worked if we've gotten this far */
539 if (window->fontList == NULL)
540 XtVaGetValues(stats, XmNfontList, &window->fontList, NULL);
542 /* Create the menu bar */
543 menuBar = CreateMenuBar(mainWin, window);
544 window->menuBar = menuBar;
545 XtManageChild(menuBar);
547 /* Create paned window to manage split window behavior */
548 pane = XtVaCreateManagedWidget("pane", xmPanedWindowWidgetClass, mainWin,
549 XmNseparatorOn, False,
550 XmNspacing, 3, XmNsashIndent, -2, NULL);
551 window->splitPane = pane;
552 XmMainWindowSetAreas(mainWin, menuBar, statsAreaForm, NULL, NULL, pane);
554 /* Patch around Motif's most idiotic "feature", that its menu accelerators
555 recognize Caps Lock and Num Lock as modifiers, and don't trigger if
556 they are engaged */
557 AccelLockBugPatch(pane, menuBar);
559 /* Create the first, and most permanent text area (other panes may
560 be added & removed, but this one will never be removed */
561 text = createTextArea(pane, window, rows,cols,
562 GetPrefEmTabDist(PLAIN_LANGUAGE_MODE), GetPrefDelimiters(),
563 GetPrefWrapMargin(), window->showLineNumbers?MIN_LINE_NUM_COLS:0);
564 XtManageChild(text);
565 window->textArea = text;
566 window->lastFocus = text;
568 /* Create the right button popup menu (note: order is important here,
569 since the translation for popping up this menu was probably already
570 added in createTextArea, but CreateBGMenu requires window->textArea
571 to be set so it can attach the menu to it (because menu shells are
572 finicky about the kinds of widgets they are attached to)) */
573 window->bgMenuPane = CreateBGMenu(window);
575 /* Create the text buffer rather than using the one created automatically
576 with the text area widget. This is done so the syntax highlighting
577 modify callback can be called to synchronize the style buffer BEFORE
578 the text display's callback is called upon to display a modification */
579 window->buffer = BufCreate();
580 BufAddModifyCB(window->buffer, SyntaxHighlightModifyCB, window);
582 /* Attach the buffer to the text widget, and add callbacks for modify */
583 TextSetBuffer(text, window->buffer);
584 BufAddModifyCB(window->buffer, modifiedCB, window);
586 /* Designate the permanent text area as the owner for selections */
587 HandleXSelections(text);
589 /* Set the requested hardware tab distance and useTabs in the text buffer */
590 BufSetTabDistance(window->buffer, GetPrefTabDist(PLAIN_LANGUAGE_MODE));
591 window->buffer->useTabs = GetPrefInsertTabs();
593 /* add the window to the global window list, update the Windows menus */
594 addToWindowList(window);
595 InvalidateWindowMenus();
597 /* realize all of the widgets in the new window */
598 RealizeWithoutForcingPosition(appShell);
599 XmProcessTraversal(text, XmTRAVERSE_CURRENT);
601 /* Make close command in window menu gracefully prompt for close */
602 AddMotifCloseCallback(appShell, (XtCallbackProc)closeCB, window);
604 #ifndef NO_SESSION_RESTART
605 /* Add wm protocol callback for making nedit restartable by session
606 managers. Doesn't yet handle multiple-desktops or iconifying right. */
607 if (syAtom == 0) {
608 wmpAtom = XmInternAtom(TheDisplay, "WM_PROTOCOLS", FALSE);
609 syAtom = XmInternAtom(TheDisplay, "WM_SAVE_YOURSELF", FALSE);
611 XmAddProtocolCallback(appShell, wmpAtom, syAtom,
612 (XtCallbackProc)saveYourselfCB, (XtPointer)window);
613 #endif
615 /* Make window resizing work in nice character heights */
616 UpdateWMSizeHints(window);
618 /* Set the minimum pane height for the initial text pane */
619 UpdateMinPaneHeights(window);
621 restoreInsaneVirtualKeyBindings(invalidBindings);
623 return window;
627 ** Close an editor window
629 void CloseWindow(WindowInfo *window)
631 int keepWindow;
632 char name[MAXPATHLEN];
634 /* Free smart indent macro programs */
635 EndSmartIndent(window);
637 /* Clean up macro references to the doomed window. If a macro is
638 executing, stop it. If macro is calling this (closing its own
639 window), leave the window alive until the macro completes */
640 keepWindow = !MacroWindowCloseActions(window);
642 #ifndef VMS
643 /* Kill shell sub-process and free related memory */
644 AbortShellCommand(window);
645 #endif /*VMS*/
647 /* Unload the default tips files for this language mode if necessary */
648 UnloadLanguageModeTipsFile(window);
650 /* If a window is closed while it is on the multi-file replace dialog
651 list of any other window (or even the same one), we must update those
652 lists or we end up with dangling references. Normally, there can
653 be only one of those dialogs at the same time (application modal),
654 but LessTif doesn't even (always) honor application modalness, so
655 there can be more than one dialog. */
656 RemoveFromMultiReplaceDialog(window);
658 /* Destroy the file closed property for this file */
659 DeleteFileClosedProperty(window);
661 /* if this is the last window, or must be kept alive temporarily because
662 it's running the macro calling us, don't close it, make it Untitled */
663 if (keepWindow || (WindowList == window && window->next == NULL)) {
664 window->filename[0] = '\0';
665 UniqueUntitledName(name);
666 CLEAR_ALL_LOCKS(window->lockReasons);
667 window->fileMode = 0;
668 strcpy(window->filename, name);
669 strcpy(window->path, "");
670 window->ignoreModify = TRUE;
671 BufSetAll(window->buffer, "");
672 window->ignoreModify = FALSE;
673 window->filenameSet = FALSE;
674 window->fileMissing = TRUE;
675 window->fileChanged = FALSE;
676 window->fileFormat = UNIX_FILE_FORMAT;
677 window->lastModTime = 0;
678 StopHighlighting(window);
679 EndSmartIndent(window);
680 UpdateWindowTitle(window);
681 UpdateWindowReadOnly(window);
682 XtSetSensitive(window->closeItem, FALSE);
683 XtSetSensitive(window->readOnlyItem, TRUE);
684 XmToggleButtonSetState(window->readOnlyItem, FALSE, FALSE);
685 ClearUndoList(window);
686 ClearRedoList(window);
687 XmTextSetString(window->statsLine, ""); /* resets scroll pos of stats
688 line from long file names */
689 UpdateStatsLine(window);
690 DetermineLanguageMode(window, True);
691 return;
694 /* Free syntax highlighting patterns, if any. w/o redisplaying */
695 FreeHighlightingData(window);
697 /* remove the buffer modification callbacks so the buffer will be
698 deallocated when the last text widget is destroyed */
699 BufRemoveModifyCB(window->buffer, modifiedCB, window);
700 BufRemoveModifyCB(window->buffer, SyntaxHighlightModifyCB, window);
702 #ifdef ROWCOLPATCH
703 patchRowCol(window->menuBar);
704 #endif
706 /* free the undo and redo lists */
707 ClearUndoList(window);
708 ClearRedoList(window);
710 /* remove and deallocate all of the widgets associated with window */
711 XtFree(window->backlightCharTypes); /* we made a copy earlier on */
712 XtDestroyWidget(window->shell);
714 /* remove the window from the global window list, update window menus */
715 removeFromWindowList(window);
716 InvalidateWindowMenus();
718 /* deallocate the window data structure */
719 XtFree((char *)window);
723 ** Check if there is already a window open for a given file
725 WindowInfo *FindWindowWithFile(const char *name, const char *path)
727 WindowInfo *w;
729 for (w=WindowList; w!=NULL; w=w->next) {
730 if (!strcmp(w->filename, name) && !strcmp(w->path, path)) {
731 return w;
734 return NULL;
738 ** Add another independently scrollable window pane to the current window,
739 ** splitting the pane which currently has keyboard focus.
741 void SplitWindow(WindowInfo *window)
743 short paneHeights[MAX_PANES+1];
744 int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1];
745 int horizOffsets[MAX_PANES+1];
746 int i, focusPane, emTabDist, wrapMargin, lineNumCols, totalHeight=0;
747 char *delimiters;
748 Widget text;
750 /* Don't create new panes if we're already at the limit */
751 if (window->nPanes >= MAX_PANES)
752 return;
754 /* Record the current heights, scroll positions, and insert positions
755 of the existing panes, keyboard focus */
756 focusPane = 0;
757 for (i=0; i<=window->nPanes; i++) {
758 text = i==0 ? window->textArea : window->textPanes[i-1];
759 insertPositions[i] = TextGetCursorPos(text);
760 XtVaGetValues(containingPane(text),XmNheight,&paneHeights[i],NULL);
761 totalHeight += paneHeights[i];
762 TextGetScroll(text, &topLines[i], &horizOffsets[i]);
763 if (text == window->lastFocus)
764 focusPane = i;
767 /* Unmanage & remanage the panedWindow so it recalculates pane heights */
768 XtUnmanageChild(window->splitPane);
770 /* Create a text widget to add to the pane and set its buffer and
771 highlight data to be the same as the other panes in the window */
772 XtVaGetValues(window->textArea, textNemulateTabs, &emTabDist,
773 textNwordDelimiters, &delimiters, textNwrapMargin, &wrapMargin,
774 textNlineNumCols, &lineNumCols, NULL);
775 text = createTextArea(window->splitPane, window, 1, 1, emTabDist,
776 delimiters, wrapMargin, lineNumCols);
777 TextSetBuffer(text, window->buffer);
778 if (window->highlightData != NULL)
779 AttachHighlightToWidget(text, window);
780 if (window->backlightChars)
782 XtVaSetValues(text, textNbacklightCharTypes,
783 window->backlightCharTypes, 0);
785 XtManageChild(text);
786 window->textPanes[window->nPanes++] = text;
788 /* Set the minimum pane height in the new pane */
789 UpdateMinPaneHeights(window);
791 /* adjust the heights, scroll positions, etc., to split the focus pane */
792 for (i=window->nPanes; i>focusPane; i--) {
793 insertPositions[i] = insertPositions[i-1];
794 paneHeights[i] = paneHeights[i-1];
795 topLines[i] = topLines[i-1];
796 horizOffsets[i] = horizOffsets[i-1];
798 paneHeights[focusPane] = paneHeights[focusPane]/2;
799 paneHeights[focusPane+1] = paneHeights[focusPane];
801 for (i=0; i<=window->nPanes; i++) {
802 text = i==0 ? window->textArea : window->textPanes[i-1];
803 setPaneDesiredHeight(containingPane(text), paneHeights[i]);
806 /* Re-manage panedWindow to recalculate pane heights & reset selection */
807 XtManageChild(window->splitPane);
809 /* Reset all of the heights, scroll positions, etc. */
810 for (i=0; i<=window->nPanes; i++) {
811 text = i==0 ? window->textArea : window->textPanes[i-1];
812 TextSetCursorPos(text, insertPositions[i]);
813 TextSetScroll(text, topLines[i], horizOffsets[i]);
814 setPaneDesiredHeight(containingPane(text),
815 totalHeight/(window->nPanes+1));
817 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
819 /* Update the window manager size hints after the sizes of the panes have
820 been set (the widget heights are not yet readable here, but they will
821 be by the time the event loop gets around to running this timer proc) */
822 XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0,
823 wmSizeUpdateProc, window);
826 Widget GetPaneByIndex(WindowInfo *window, int paneIndex)
828 Widget text = NULL;
829 if (paneIndex >= 0 && paneIndex <= window->nPanes) {
830 text = (paneIndex == 0) ? window->textArea : window->textPanes[paneIndex - 1];
832 return(text);
835 int WidgetToPaneIndex(WindowInfo *window, Widget w)
837 int i;
838 Widget text;
839 int paneIndex = 0;
841 for (i = 0; i <= window->nPanes; ++i) {
842 text = (i == 0) ? window->textArea : window->textPanes[i - 1];
843 if (text == w) {
844 paneIndex = i;
847 return(paneIndex);
851 ** Close the window pane that last had the keyboard focus. (Actually, close
852 ** the bottom pane and make it look like pane which had focus was closed)
854 void ClosePane(WindowInfo *window)
856 short paneHeights[MAX_PANES+1];
857 int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1];
858 int horizOffsets[MAX_PANES+1];
859 int i, focusPane,totalHeight=0;
860 Widget text;
862 /* Don't delete the last pane */
863 if (window->nPanes <= 0)
864 return;
866 /* Record the current heights, scroll positions, and insert positions
867 of the existing panes, and the keyboard focus */
868 focusPane = 0;
869 for (i=0; i<=window->nPanes; i++) {
870 text = i==0 ? window->textArea : window->textPanes[i-1];
871 insertPositions[i] = TextGetCursorPos(text);
872 XtVaGetValues(containingPane(text),
873 XmNheight, &paneHeights[i], NULL);
874 totalHeight += paneHeights[i];
875 TextGetScroll(text, &topLines[i], &horizOffsets[i]);
876 if (text == window->lastFocus)
877 focusPane = i;
880 /* Unmanage & remanage the panedWindow so it recalculates pane heights */
881 XtUnmanageChild(window->splitPane);
883 /* Destroy last pane, and make sure lastFocus points to an existing pane.
884 Workaround for OM 2.1.30: text widget must be unmanaged for
885 xmPanedWindowWidget to calculate the correct pane heights for
886 the remaining panes, simply detroying it didn't seem enough */
887 window->nPanes--;
888 XtUnmanageChild(containingPane(window->textPanes[window->nPanes]));
889 XtDestroyWidget(containingPane(window->textPanes[window->nPanes]));
891 if (window->nPanes == 0)
892 window->lastFocus = window->textArea;
893 else if (focusPane > window->nPanes)
894 window->lastFocus = window->textPanes[window->nPanes-1];
896 /* adjust the heights, scroll positions, etc., to make it look
897 like the pane with the input focus was closed */
898 for (i=focusPane; i<=window->nPanes; i++) {
899 insertPositions[i] = insertPositions[i+1];
900 paneHeights[i] = paneHeights[i+1];
901 topLines[i] = topLines[i+1];
902 horizOffsets[i] = horizOffsets[i+1];
905 /* set the desired heights and re-manage the paned window so it will
906 recalculate pane heights */
907 for (i=0; i<=window->nPanes; i++) {
908 text = i==0 ? window->textArea : window->textPanes[i-1];
909 setPaneDesiredHeight(containingPane(text), paneHeights[i]);
911 XtManageChild(window->splitPane);
913 /* Reset all of the scroll positions, insert positions, etc. */
914 for (i=0; i<=window->nPanes; i++) {
915 text = i==0 ? window->textArea : window->textPanes[i-1];
916 TextSetCursorPos(text, insertPositions[i]);
917 TextSetScroll(text, topLines[i], horizOffsets[i]);
919 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT);
921 /* Update the window manager size hints after the sizes of the panes have
922 been set (the widget heights are not yet readable here, but they will
923 be by the time the event loop gets around to running this timer proc) */
924 XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0,
925 wmSizeUpdateProc, window);
929 ** Turn on and off the display of line numbers
931 void ShowLineNumbers(WindowInfo *window, int state)
933 Widget text;
934 int i, marginWidth;
935 Dimension windowWidth;
936 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
938 if (window->showLineNumbers == state)
939 return;
940 window->showLineNumbers = state;
942 /* Just setting window->showLineNumbers is sufficient to tell
943 UpdateLineNumDisp to expand the line number areas and the window
944 size for the number of lines required. To hide the line number
945 display, set the width to zero, and contract the window width. */
946 if (state) {
947 UpdateLineNumDisp(window);
948 } else {
949 XtVaGetValues(window->shell, XmNwidth, &windowWidth, NULL);
950 XtVaGetValues(window->textArea, textNmarginWidth, &marginWidth, NULL);
951 XtVaSetValues(window->shell, XmNwidth,
952 windowWidth - textD->left + marginWidth, NULL);
953 for (i=0; i<=window->nPanes; i++) {
954 text = i==0 ? window->textArea : window->textPanes[i-1];
955 XtVaSetValues(text, textNlineNumCols, 0, NULL);
959 /* Tell WM that the non-expandable part of the window has changed size */
960 UpdateWMSizeHints(window);
963 void SetTabDist(WindowInfo *window, int tabDist)
965 if (window->buffer->tabDist != tabDist) {
966 int saveCursorPositions[MAX_PANES + 1];
967 int saveVScrollPositions[MAX_PANES + 1];
968 int saveHScrollPositions[MAX_PANES + 1];
969 int paneIndex;
971 window->ignoreModify = True;
973 for (paneIndex = 0; paneIndex <= window->nPanes; ++paneIndex) {
974 Widget w = GetPaneByIndex(window, paneIndex);
975 textDisp *textD = ((TextWidget)w)->text.textD;
977 TextGetScroll(w, &saveVScrollPositions[paneIndex], &saveHScrollPositions[paneIndex]);
978 saveCursorPositions[paneIndex] = TextGetCursorPos(w);
979 textD->modifyingTabDist = 1;
982 BufSetTabDistance(window->buffer, tabDist);
984 for (paneIndex = 0; paneIndex <= window->nPanes; ++paneIndex) {
985 Widget w = GetPaneByIndex(window, paneIndex);
986 textDisp *textD = ((TextWidget)w)->text.textD;
988 textD->modifyingTabDist = 0;
989 TextSetCursorPos(w, saveCursorPositions[paneIndex]);
990 TextSetScroll(w, saveVScrollPositions[paneIndex], saveHScrollPositions[paneIndex]);
993 window->ignoreModify = False;
997 void SetEmTabDist(WindowInfo *window, int emTabDist)
999 int i;
1001 XtVaSetValues(window->textArea, textNemulateTabs, emTabDist, NULL);
1002 for (i = 0; i < window->nPanes; ++i) {
1003 XtVaSetValues(window->textPanes[i], textNemulateTabs, emTabDist, NULL);
1008 ** Turn on and off the display of the statistics line
1010 void ShowStatsLine(WindowInfo *window, int state)
1012 Widget text;
1013 int i;
1015 /* In continuous wrap mode, text widgets must be told to keep track of
1016 the top line number in absolute (non-wrapped) lines, because it can
1017 be a costly calculation, and is only needed for displaying line
1018 numbers, either in the stats line, or along the left margin */
1019 for (i=0; i<=window->nPanes; i++) {
1020 text = i==0 ? window->textArea : window->textPanes[i-1];
1021 TextDMaintainAbsLineNum(((TextWidget)text)->text.textD, state);
1023 window->showStats = state;
1024 showStats(window, state);
1028 ** Displays and undisplays the statistics line (regardless of settings of
1029 ** window->showStats or window->modeMessageDisplayed)
1031 static void showStats(WindowInfo *window, int state)
1033 if (state) {
1034 Dimension ht;
1036 XtVaSetValues(window->iSearchForm,
1037 XmNbottomAttachment, XmATTACH_NONE, NULL);
1038 XtManageChild(window->statsLineForm);
1039 showStatsForm(window, True);
1041 /* workaround for Lesstif (0.93.0, maybe others) to reveal the
1042 statsline */
1043 XtVaGetValues(window->statsLine, XmNheight, &ht, NULL);
1044 XtVaSetValues(window->statsLineColNo, XmNheight, ht, NULL);
1045 } else {
1046 XtUnmanageChild(window->statsLineForm);
1047 XtVaSetValues(window->iSearchForm,
1048 XmNbottomAttachment, XmATTACH_FORM, NULL);
1049 showStatsForm(window, window->showISearchLine);
1052 /* Tell WM that the non-expandable part of the window has changed size */
1053 /* Already done in showStatsForm */
1054 /* UpdateWMSizeHints(window); */
1058 ** Turn on and off the continuing display of the incremental search line
1059 ** (when off, it is popped up and down as needed via TempShowISearch)
1061 void ShowISearchLine(WindowInfo *window, int state)
1063 if (window->showISearchLine == state)
1064 return;
1065 window->showISearchLine = state;
1066 showISearch(window, state);
1070 ** Temporarily show and hide the incremental search line if the line is not
1071 ** already up.
1073 void TempShowISearch(WindowInfo *window, int state)
1075 if (window->showISearchLine)
1076 return;
1077 if (XtIsManaged(window->iSearchForm) != state)
1078 showISearch(window, state);
1082 ** Put up or pop-down the incremental search line regardless of settings
1083 ** of showISearchLine or TempShowISearch
1085 static void showISearch(WindowInfo *window, int state)
1087 if (state) {
1088 XtManageChild(window->iSearchForm);
1089 XtVaSetValues(window->statsLineForm, XmNtopAttachment,
1090 XmATTACH_WIDGET, XmNtopWidget, window->iSearchForm, NULL);
1091 showStatsForm(window, True);
1092 } else {
1093 XtUnmanageChild(window->iSearchForm);
1094 XtVaSetValues(window->statsLineForm, XmNtopAttachment,
1095 XmATTACH_FORM, NULL);
1096 showStatsForm(window, window->showStats ||
1097 window->modeMessageDisplayed);
1100 /* Tell WM that the non-expandable part of the window has changed size */
1101 /* This is already done in showStatsForm */
1102 /* UpdateWMSizeHints(window); */
1106 ** Show or hide the extra display area under the main menu bar which
1107 ** optionally contains the status line and the incremental search bar
1109 static void showStatsForm(WindowInfo *window, int state)
1111 Widget statsAreaForm = XtParent(window->statsLineForm);
1112 Widget mainW = XtParent(statsAreaForm);
1114 /* The very silly use of XmNcommandWindowLocation and XmNshowSeparator
1115 below are to kick the main window widget to position and remove the
1116 status line when it is managed and unmanaged. At some Motif version
1117 level, the showSeparator trick backfires and leaves the separator
1118 shown, but fortunately the dynamic behavior is fixed, too so the
1119 workaround is no longer necessary, either. (... the version where
1120 this occurs may be earlier than 2.1. If the stats line shows
1121 double thickness shadows in earlier Motif versions, the #if XmVersion
1122 directive should be moved back to that earlier version) */
1123 if (state) {
1124 XtUnmanageChild(statsAreaForm); /*... will this fix Solaris 7??? */
1125 XtVaSetValues(mainW, XmNcommandWindowLocation,
1126 XmCOMMAND_ABOVE_WORKSPACE, NULL);
1127 #if XmVersion < 2001
1128 XtVaSetValues(mainW, XmNshowSeparator, True, NULL);
1129 #endif
1130 XtManageChild(statsAreaForm);
1131 XtVaSetValues(mainW, XmNshowSeparator, False, NULL);
1132 UpdateStatsLine(window);
1133 } else {
1134 XtUnmanageChild(statsAreaForm);
1135 XtVaSetValues(mainW, XmNcommandWindowLocation,
1136 XmCOMMAND_BELOW_WORKSPACE, NULL);
1139 /* Tell WM that the non-expandable part of the window has changed size */
1140 UpdateWMSizeHints(window);
1144 ** Display a special message in the stats line (show the stats line if it
1145 ** is not currently shown).
1147 void SetModeMessage(WindowInfo *window, const char *message)
1149 window->modeMessageDisplayed = True;
1150 XmTextSetString(window->statsLine, (char*)message);
1152 * Don't invoke the stats line again, if stats line is already displayed.
1154 if (!window->showStats)
1155 showStats(window, True);
1159 ** Clear special statistics line message set in SetModeMessage, returns
1160 ** the statistics line to its original state as set in window->showStats
1162 void ClearModeMessage(WindowInfo *window)
1164 window->modeMessageDisplayed = False;
1166 * Remove the stats line only if indicated by it's window state.
1168 if (!window->showStats)
1169 showStats(window, False);
1170 UpdateStatsLine(window);
1174 ** Count the windows
1176 int NWindows(void)
1178 WindowInfo *win;
1179 int n;
1181 for (win=WindowList, n=0; win!=NULL; win=win->next, n++);
1182 return n;
1186 ** Set autoindent state to one of NO_AUTO_INDENT, AUTO_INDENT, or SMART_INDENT.
1188 void SetAutoIndent(WindowInfo *window, int state)
1190 int autoIndent = state == AUTO_INDENT, smartIndent = state == SMART_INDENT;
1191 int i;
1193 if (window->indentStyle == SMART_INDENT && !smartIndent)
1194 EndSmartIndent(window);
1195 else if (smartIndent && window->indentStyle != SMART_INDENT)
1196 BeginSmartIndent(window, True);
1197 window->indentStyle = state;
1198 XtVaSetValues(window->textArea, textNautoIndent, autoIndent,
1199 textNsmartIndent, smartIndent, NULL);
1200 for (i=0; i<window->nPanes; i++)
1201 XtVaSetValues(window->textPanes[i], textNautoIndent, autoIndent,
1202 textNsmartIndent, smartIndent, NULL);
1203 XmToggleButtonSetState(window->smartIndentItem, smartIndent, False);
1204 XmToggleButtonSetState(window->autoIndentItem, autoIndent, False);
1205 XmToggleButtonSetState(window->autoIndentOffItem, state == NO_AUTO_INDENT,
1206 False);
1210 ** Set showMatching state to one of NO_FLASH, FLASH_DELIMIT or FLASH_RANGE.
1211 ** Update the menu to reflect the change of state.
1213 void SetShowMatching(WindowInfo *window, int state)
1215 window->showMatchingStyle = state;
1216 XmToggleButtonSetState(window->showMatchingOffItem,
1217 state == NO_FLASH, False);
1218 XmToggleButtonSetState(window->showMatchingDelimitItem,
1219 state == FLASH_DELIMIT, False);
1220 XmToggleButtonSetState(window->showMatchingRangeItem,
1221 state == FLASH_RANGE, False);
1225 ** Set the fonts for "window" from a font name, and updates the display.
1226 ** Also updates window->fontList which is used for statistics line.
1228 ** Note that this leaks memory and server resources. In previous NEdit
1229 ** versions, fontLists were carefully tracked and freed, but X and Motif
1230 ** have some kind of timing problem when widgets are distroyed, such that
1231 ** fonts may not be freed immediately after widget destruction with 100%
1232 ** safety. Rather than kludge around this with timerProcs, I have chosen
1233 ** to create new fontLists only when the user explicitly changes the font
1234 ** (which shouldn't happen much in normal NEdit operation), and skip the
1235 ** futile effort of freeing them.
1237 void SetFonts(WindowInfo *window, const char *fontName, const char *italicName,
1238 const char *boldName, const char *boldItalicName)
1240 XFontStruct *font, *oldFont;
1241 int i, oldFontWidth, oldFontHeight, fontWidth, fontHeight;
1242 int borderWidth, borderHeight, marginWidth, marginHeight;
1243 int primaryChanged, highlightChanged = False;
1244 Dimension oldWindowWidth, oldWindowHeight, oldTextWidth, oldTextHeight;
1245 Dimension textHeight, newWindowWidth, newWindowHeight;
1246 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
1248 /* Check which fonts have changed */
1249 primaryChanged = strcmp(fontName, window->fontName);
1250 if (strcmp(italicName, window->italicFontName)) highlightChanged = True;
1251 if (strcmp(boldName, window->boldFontName)) highlightChanged = True;
1252 if (strcmp(boldItalicName, window->boldItalicFontName))
1253 highlightChanged = True;
1254 if (!primaryChanged && !highlightChanged)
1255 return;
1257 /* Get information about the current window sizing, to be used to
1258 determine the correct window size after the font is changed */
1259 XtVaGetValues(window->shell, XmNwidth, &oldWindowWidth, XmNheight,
1260 &oldWindowHeight, NULL);
1261 XtVaGetValues(window->textArea, XmNheight, &textHeight,
1262 textNmarginHeight, &marginHeight, textNmarginWidth,
1263 &marginWidth, textNfont, &oldFont, NULL);
1264 oldTextWidth = textD->width + textD->lineNumWidth;
1265 oldTextHeight = textHeight - 2*marginHeight;
1266 for (i=0; i<window->nPanes; i++) {
1267 XtVaGetValues(window->textPanes[i], XmNheight, &textHeight, NULL);
1268 oldTextHeight += textHeight - 2*marginHeight;
1270 borderWidth = oldWindowWidth - oldTextWidth;
1271 borderHeight = oldWindowHeight - oldTextHeight;
1272 oldFontWidth = oldFont->max_bounds.width;
1273 oldFontHeight = textD->ascent + textD->descent;
1276 /* Change the fonts in the window data structure. If the primary font
1277 didn't work, use Motif's fallback mechanism by stealing it from the
1278 statistics line. Highlight fonts are allowed to be NULL, which
1279 is interpreted as "use the primary font" */
1280 if (primaryChanged) {
1281 strcpy(window->fontName, fontName);
1282 font = XLoadQueryFont(TheDisplay, fontName);
1283 if (font == NULL)
1284 XtVaGetValues(window->statsLine, XmNfontList, &window->fontList,
1285 NULL);
1286 else
1287 window->fontList = XmFontListCreate(font, XmSTRING_DEFAULT_CHARSET);
1289 if (highlightChanged) {
1290 strcpy(window->italicFontName, italicName);
1291 window->italicFontStruct = XLoadQueryFont(TheDisplay, italicName);
1292 strcpy(window->boldFontName, boldName);
1293 window->boldFontStruct = XLoadQueryFont(TheDisplay, boldName);
1294 strcpy(window->boldItalicFontName, boldItalicName);
1295 window->boldItalicFontStruct = XLoadQueryFont(TheDisplay, boldItalicName);
1298 /* Change the primary font in all the widgets */
1299 if (primaryChanged) {
1300 font = GetDefaultFontStruct(window->fontList);
1301 XtVaSetValues(window->textArea, textNfont, font, NULL);
1302 for (i=0; i<window->nPanes; i++)
1303 XtVaSetValues(window->textPanes[i], textNfont, font, NULL);
1306 /* Change the highlight fonts, even if they didn't change, because
1307 primary font is read through the style table for syntax highlighting */
1308 if (window->highlightData != NULL)
1309 UpdateHighlightStyles(window);
1311 /* Change the window manager size hints.
1312 Note: this has to be done _before_ we set the new sizes. ICCCM2
1313 compliant window managers (such as fvwm2) would otherwise resize
1314 the window twice: once because of the new sizes requested, and once
1315 because of the new size increments, resulting in an overshoot. */
1316 UpdateWMSizeHints(window);
1318 /* Use the information from the old window to re-size the window to a
1319 size appropriate for the new font */
1320 fontWidth = GetDefaultFontStruct(window->fontList)->max_bounds.width;
1321 fontHeight = textD->ascent + textD->descent;
1322 newWindowWidth = (oldTextWidth*fontWidth) / oldFontWidth + borderWidth;
1323 newWindowHeight = (oldTextHeight*fontHeight) / oldFontHeight + borderHeight;
1324 XtVaSetValues(window->shell, XmNwidth, newWindowWidth, XmNheight,
1325 newWindowHeight, NULL);
1327 /* Change the minimum pane height */
1328 UpdateMinPaneHeights(window);
1331 void SetColors(WindowInfo *window, const char *textFg, const char *textBg,
1332 const char *selectFg, const char *selectBg, const char *hiliteFg,
1333 const char *hiliteBg, const char *lineNoFg, const char *cursorFg)
1335 int i, dummy;
1336 Pixel textFgPix = AllocColor( window->textArea, textFg,
1337 &dummy, &dummy, &dummy),
1338 textBgPix = AllocColor( window->textArea, textBg,
1339 &dummy, &dummy, &dummy),
1340 selectFgPix = AllocColor( window->textArea, selectFg,
1341 &dummy, &dummy, &dummy),
1342 selectBgPix = AllocColor( window->textArea, selectBg,
1343 &dummy, &dummy, &dummy),
1344 hiliteFgPix = AllocColor( window->textArea, hiliteFg,
1345 &dummy, &dummy, &dummy),
1346 hiliteBgPix = AllocColor( window->textArea, hiliteBg,
1347 &dummy, &dummy, &dummy),
1348 lineNoFgPix = AllocColor( window->textArea, lineNoFg,
1349 &dummy, &dummy, &dummy),
1350 cursorFgPix = AllocColor( window->textArea, cursorFg,
1351 &dummy, &dummy, &dummy);
1352 textDisp *textD;
1354 #if 0
1355 /* Update the names in the WindowInfo */
1356 strncpy(window->colorNames[TEXT_FG_COLOR ], textFg , MAX_COLOR_LEN);
1357 strncpy(window->colorNames[TEXT_BG_COLOR ], textBg , MAX_COLOR_LEN);
1358 strncpy(window->colorNames[SELECT_FG_COLOR], selectFg, MAX_COLOR_LEN);
1359 strncpy(window->colorNames[SELECT_BG_COLOR], selectBg, MAX_COLOR_LEN);
1360 strncpy(window->colorNames[HILITE_FG_COLOR], hiliteFg, MAX_COLOR_LEN);
1361 strncpy(window->colorNames[HILITE_BG_COLOR], hiliteBg, MAX_COLOR_LEN);
1362 strncpy(window->colorNames[LINENO_FG_COLOR], lineNoFg, MAX_COLOR_LEN);
1363 strncpy(window->colorNames[CURSOR_FG_COLOR], cursorFg, MAX_COLOR_LEN);
1364 #endif
1366 /* Update the main pane */
1367 XtVaSetValues(window->textArea,
1368 XmNforeground, textFgPix,
1369 XmNbackground, textBgPix,
1370 NULL);
1371 textD = ((TextWidget)window->textArea)->text.textD;
1372 TextDSetColors( textD, textFgPix, textBgPix, selectFgPix, selectBgPix,
1373 hiliteFgPix, hiliteBgPix, lineNoFgPix, cursorFgPix );
1374 /* Update any additional panes */
1375 for (i=0; i<window->nPanes; i++) {
1376 XtVaSetValues(window->textPanes[i],
1377 XmNforeground, textFgPix,
1378 XmNbackground, textBgPix,
1379 NULL);
1380 textD = ((TextWidget)window->textPanes[i])->text.textD;
1381 TextDSetColors( textD, textFgPix, textBgPix, selectFgPix, selectBgPix,
1382 hiliteFgPix, hiliteBgPix, lineNoFgPix, cursorFgPix );
1385 /* Redo any syntax highlighting */
1386 if (window->highlightData != NULL)
1387 UpdateHighlightStyles(window);
1391 ** Set insert/overstrike mode
1393 void SetOverstrike(WindowInfo *window, int overstrike)
1395 int i;
1397 XtVaSetValues(window->textArea, textNoverstrike, overstrike, NULL);
1398 for (i=0; i<window->nPanes; i++)
1399 XtVaSetValues(window->textPanes[i], textNoverstrike, overstrike, NULL);
1400 window->overstrike = overstrike;
1404 ** Select auto-wrap mode, one of NO_WRAP, NEWLINE_WRAP, or CONTINUOUS_WRAP
1406 void SetAutoWrap(WindowInfo *window, int state)
1408 int i;
1409 int autoWrap = state == NEWLINE_WRAP, contWrap = state == CONTINUOUS_WRAP;
1411 XtVaSetValues(window->textArea, textNautoWrap, autoWrap,
1412 textNcontinuousWrap, contWrap, NULL);
1413 for (i=0; i<window->nPanes; i++)
1414 XtVaSetValues(window->textPanes[i], textNautoWrap, autoWrap,
1415 textNcontinuousWrap, contWrap, NULL);
1416 window->wrapMode = state;
1418 XmToggleButtonSetState(window->newlineWrapItem, autoWrap, False);
1419 XmToggleButtonSetState(window->continuousWrapItem, contWrap, False);
1420 XmToggleButtonSetState(window->noWrapItem, state == NO_WRAP, False);
1424 ** Set the wrap margin (0 == wrap at right edge of window)
1426 void SetWrapMargin(WindowInfo *window, int margin)
1428 int i;
1430 XtVaSetValues(window->textArea, textNwrapMargin, margin, NULL);
1431 for (i=0; i<window->nPanes; i++)
1432 XtVaSetValues(window->textPanes[i], textNwrapMargin, margin, NULL);
1436 ** Recover the window pointer from any widget in the window, by searching
1437 ** up the widget hierarcy for the top level container widget where the window
1438 ** pointer is stored in the userData field.
1440 WindowInfo *WidgetToWindow(Widget w)
1442 WindowInfo *window;
1443 Widget parent;
1445 while (True) {
1446 parent = XtParent(w);
1447 if (parent == NULL)
1448 return NULL;
1449 if (XtClass(parent) == applicationShellWidgetClass)
1450 break;
1451 w = parent;
1453 XtVaGetValues(w, XmNuserData, &window, NULL);
1454 return window;
1458 ** Change the window appearance and the window data structure to show
1459 ** that the file it contains has been modified
1461 void SetWindowModified(WindowInfo *window, int modified)
1463 if (window->fileChanged == FALSE && modified == TRUE) {
1464 XtSetSensitive(window->closeItem, TRUE);
1465 window->fileChanged = TRUE;
1466 UpdateWindowTitle(window);
1467 } else if (window->fileChanged == TRUE && modified == FALSE) {
1468 window->fileChanged = FALSE;
1469 UpdateWindowTitle(window);
1474 ** Update the window title to reflect the filename, read-only, and modified
1475 ** status of the window data structure
1477 void UpdateWindowTitle(const WindowInfo *window)
1479 char *title = FormatWindowTitle(window->filename,
1480 window->path,
1481 #ifdef VMS
1482 NULL,
1483 #else
1484 GetClearCaseViewTag(),
1485 #endif /* VMS */
1486 GetPrefServerName(),
1487 IsServer,
1488 window->filenameSet,
1489 window->lockReasons,
1490 window->fileChanged,
1491 GetPrefTitleFormat());
1493 char *iconTitle = XtMalloc(strlen(window->filename) + 2); /* strlen("*")+1 */
1495 strcpy(iconTitle, window->filename);
1496 if (window->fileChanged)
1497 strcat(iconTitle, "*");
1498 XtVaSetValues(window->shell, XmNtitle, title, XmNiconName, iconTitle, NULL);
1500 /* If there's a find or replace dialog up in "Keep Up" mode, with a
1501 file name in the title, update it too */
1502 if (window->findDlog && XmToggleButtonGetState(window->findKeepBtn)) {
1503 sprintf(title, "Find (in %s)", window->filename);
1504 XtVaSetValues(XtParent(window->findDlog), XmNtitle, title, NULL);
1506 if (window->replaceDlog && XmToggleButtonGetState(window->replaceKeepBtn)) {
1507 sprintf(title, "Replace (in %s)", window->filename);
1508 XtVaSetValues(XtParent(window->replaceDlog), XmNtitle, title, NULL);
1510 XtFree(iconTitle);
1512 /* Update the Windows menus with the new name */
1513 InvalidateWindowMenus();
1517 ** Update the read-only state of the text area(s) in the window, and
1518 ** the ReadOnly toggle button in the File menu to agree with the state in
1519 ** the window data structure.
1521 void UpdateWindowReadOnly(WindowInfo *window)
1523 int i, state;
1525 state = IS_ANY_LOCKED(window->lockReasons);
1526 XtVaSetValues(window->textArea, textNreadOnly, state, NULL);
1527 for (i=0; i<window->nPanes; i++)
1528 XtVaSetValues(window->textPanes[i], textNreadOnly, state, NULL);
1529 XmToggleButtonSetState(window->readOnlyItem, state, FALSE);
1530 XtSetSensitive(window->readOnlyItem,
1531 !IS_ANY_LOCKED_IGNORING_USER(window->lockReasons));
1535 ** Get the start and end of the current selection. This routine is obsolete
1536 ** because it ignores rectangular selections, and reads from the widget
1537 ** instead of the buffer. Use BufGetSelectionPos.
1539 int GetSelection(Widget widget, int *left, int *right)
1541 return GetSimpleSelection(TextGetBuffer(widget), left, right);
1545 ** Find the start and end of a single line selection. Hides rectangular
1546 ** selection issues for older routines which use selections that won't
1547 ** span lines.
1549 int GetSimpleSelection(textBuffer *buf, int *left, int *right)
1551 int selStart, selEnd, isRect, rectStart, rectEnd, lineStart;
1553 /* get the character to match and its position from the selection, or
1554 the character before the insert point if nothing is selected.
1555 Give up if too many characters are selected */
1556 if (!BufGetSelectionPos(buf, &selStart, &selEnd, &isRect,
1557 &rectStart, &rectEnd))
1558 return False;
1559 if (isRect) {
1560 lineStart = BufStartOfLine(buf, selStart);
1561 selStart = BufCountForwardDispChars(buf, lineStart, rectStart);
1562 selEnd = BufCountForwardDispChars(buf, lineStart, rectEnd);
1564 *left = selStart;
1565 *right = selEnd;
1566 return True;
1570 ** Returns a range of text from a text widget (this routine is obsolete,
1571 ** get text from the buffer instead). Memory is allocated with
1572 ** XtMalloc and caller should free it.
1574 char *GetTextRange(Widget widget, int left, int right)
1576 return BufGetRange(TextGetBuffer(widget), left, right);
1580 ** If the selection (or cursor position if there's no selection) is not
1581 ** fully shown, scroll to bring it in to view. Note that as written,
1582 ** this won't work well with multi-line selections. Modest re-write
1583 ** of the horizontal scrolling part would be quite easy to make it work
1584 ** well with rectangular selections.
1586 void MakeSelectionVisible(WindowInfo *window, Widget textPane)
1588 int left, right, isRect, rectStart, rectEnd, horizOffset;
1589 int scrollOffset, leftX, rightX, y, rows, margin;
1590 int topLineNum, lastLineNum, rightLineNum, leftLineNum, linesToScroll;
1591 textDisp *textD = ((TextWidget)textPane)->text.textD;
1592 int topChar = TextFirstVisiblePos(textPane);
1593 int lastChar = TextLastVisiblePos(textPane);
1594 int targetLineNum;
1595 Dimension width;
1597 /* find out where the selection is */
1598 if (!BufGetSelectionPos(window->buffer, &left, &right, &isRect,
1599 &rectStart, &rectEnd)) {
1600 left = right = TextGetCursorPos(textPane);
1601 isRect = False;
1604 /* Check vertical positioning unless the selection is already shown or
1605 already covers the display. If the end of the selection is below
1606 bottom, scroll it in to view until the end selection is scrollOffset
1607 lines from the bottom of the display or the start of the selection
1608 scrollOffset lines from the top. Calculate a pleasing distance from the
1609 top or bottom of the window, to scroll the selection to (if scrolling is
1610 necessary), around 1/3 of the height of the window */
1611 if (!((left >= topChar && right <= lastChar) ||
1612 (left <= topChar && right >= lastChar))) {
1613 XtVaGetValues(textPane, textNrows, &rows, NULL);
1614 scrollOffset = rows/3;
1615 TextGetScroll(textPane, &topLineNum, &horizOffset);
1616 if (right > lastChar) {
1617 /* End of sel. is below bottom of screen */
1618 leftLineNum = topLineNum +
1619 TextDCountLines(textD, topChar, left, False);
1620 targetLineNum = topLineNum + scrollOffset;
1621 if (leftLineNum >= targetLineNum) {
1622 /* Start of sel. is not between top & target */
1623 linesToScroll = TextDCountLines(textD, lastChar, right, False) +
1624 scrollOffset;
1625 if (leftLineNum - linesToScroll < targetLineNum)
1626 linesToScroll = leftLineNum - targetLineNum;
1627 /* Scroll start of selection to the target line */
1628 TextSetScroll(textPane, topLineNum+linesToScroll, horizOffset);
1630 } else if (left < topChar) {
1631 /* Start of sel. is above top of screen */
1632 lastLineNum = topLineNum + rows;
1633 rightLineNum = lastLineNum -
1634 TextDCountLines(textD, right, lastChar, False);
1635 targetLineNum = lastLineNum - scrollOffset;
1636 if (rightLineNum <= targetLineNum) {
1637 /* End of sel. is not between bottom & target */
1638 linesToScroll = TextDCountLines(textD, left, topChar, False) +
1639 scrollOffset;
1640 if (rightLineNum + linesToScroll > targetLineNum)
1641 linesToScroll = targetLineNum - rightLineNum;
1642 /* Scroll end of selection to the target line */
1643 TextSetScroll(textPane, topLineNum-linesToScroll, horizOffset);
1648 /* If either end of the selection off screen horizontally, try to bring it
1649 in view, by making sure both end-points are visible. Using only end
1650 points of a multi-line selection is not a great idea, and disaster for
1651 rectangular selections, so this part of the routine should be re-written
1652 if it is to be used much with either. Note also that this is a second
1653 scrolling operation, causing the display to jump twice. It's done after
1654 vertical scrolling to take advantage of TextPosToXY which requires it's
1655 reqested position to be vertically on screen) */
1656 if ( TextPosToXY(textPane, left, &leftX, &y) &&
1657 TextPosToXY(textPane, right, &rightX, &y) && leftX <= rightX) {
1658 TextGetScroll(textPane, &topLineNum, &horizOffset);
1659 XtVaGetValues(textPane, XmNwidth, &width, textNmarginWidth, &margin,
1660 NULL);
1661 if (leftX < margin + textD->lineNumLeft + textD->lineNumWidth)
1662 horizOffset -=
1663 margin + textD->lineNumLeft + textD->lineNumWidth - leftX;
1664 else if (rightX > width - margin)
1665 horizOffset += rightX - (width - margin);
1666 TextSetScroll(textPane, topLineNum, horizOffset);
1669 /* make sure that the statistics line is up to date */
1670 UpdateStatsLine(window);
1673 static Widget createTextArea(Widget parent, WindowInfo *window, int rows,
1674 int cols, int emTabDist, char *delimiters, int wrapMargin,
1675 int lineNumCols)
1677 Widget text, sw, hScrollBar, vScrollBar, frame;
1678 int dummy;
1679 int i;
1681 /* Create a text widget inside of a scrolled window widget */
1682 sw = XtVaCreateManagedWidget("scrolledW", xmScrolledWindowWidgetClass,
1683 parent, XmNpaneMaximum, SHRT_MAX,
1684 XmNpaneMinimum, PANE_MIN_HEIGHT, XmNhighlightThickness, 0, NULL);
1685 hScrollBar = XtVaCreateManagedWidget("textHorScrollBar",
1686 xmScrollBarWidgetClass, sw, XmNorientation, XmHORIZONTAL,
1687 XmNrepeatDelay, 10, NULL);
1688 vScrollBar = XtVaCreateManagedWidget("textVertScrollBar",
1689 xmScrollBarWidgetClass, sw, XmNorientation, XmVERTICAL,
1690 XmNrepeatDelay, 10, NULL);
1691 frame = XtVaCreateManagedWidget("textFrame", xmFrameWidgetClass, sw,
1692 XmNshadowType, XmSHADOW_IN, NULL);
1693 text = XtVaCreateManagedWidget("text", textWidgetClass, frame,
1694 textNbacklightCharTypes, window->backlightCharTypes,
1695 textNrows, rows, textNcolumns, cols,
1696 textNlineNumCols, lineNumCols,
1697 textNemulateTabs, emTabDist,
1698 textNfont, GetDefaultFontStruct(window->fontList),
1699 textNhScrollBar, hScrollBar, textNvScrollBar, vScrollBar,
1700 textNreadOnly, IS_ANY_LOCKED(window->lockReasons),
1701 textNwordDelimiters, delimiters,
1702 textNwrapMargin, wrapMargin,
1703 textNautoIndent, window->indentStyle == AUTO_INDENT,
1704 textNsmartIndent, window->indentStyle == SMART_INDENT,
1705 textNautoWrap, window->wrapMode == NEWLINE_WRAP,
1706 textNcontinuousWrap, window->wrapMode == CONTINUOUS_WRAP,
1707 textNoverstrike, window->overstrike,
1708 textNhidePointer, (Boolean) GetPrefTypingHidesPointer(),
1709 NULL);
1711 for (i = 0; i < XtNumber(colorResourceMap); i++)
1713 String colorName = GetPrefColorName(colorResourceMap[i].color);
1714 if (strcmp("None", colorName))
1716 Pixel color = AllocColor(sw, colorName, &dummy, &dummy, &dummy);
1717 XtVaSetValues(text, colorResourceMap[i].resource, color, NULL);
1721 XtVaSetValues(sw, XmNworkWindow, frame, XmNhorizontalScrollBar,
1722 hScrollBar, XmNverticalScrollBar, vScrollBar, NULL);
1724 /* add focus, drag, cursor tracking, and smart indent callbacks */
1725 XtAddCallback(text, textNfocusCallback, (XtCallbackProc)focusCB, window);
1726 XtAddCallback(text, textNcursorMovementCallback, (XtCallbackProc)movedCB,
1727 window);
1728 XtAddCallback(text, textNdragStartCallback, (XtCallbackProc)dragStartCB,
1729 window);
1730 XtAddCallback(text, textNdragEndCallback, (XtCallbackProc)dragEndCB,
1731 window);
1732 XtAddCallback(text, textNsmartIndentCallback, SmartIndentCB, window);
1734 /* This makes sure the text area initially has a the insert point shown
1735 ... (check if still true with the nedit text widget, probably not) */
1736 XmAddTabGroup(containingPane(text));
1738 /* compensate for Motif delete/backspace problem */
1739 RemapDeleteKey(text);
1741 /* Augment translation table for right button popup menu */
1742 AddBGMenuAction(text);
1744 /* If absolute line numbers will be needed for display in the statistics
1745 line, tell the widget to maintain them (otherwise, it's a costly
1746 operation and performance will be better without it) */
1747 TextDMaintainAbsLineNum(((TextWidget)text)->text.textD, window->showStats);
1749 return text;
1752 static void movedCB(Widget w, WindowInfo *window, XtPointer callData)
1754 if (window->ignoreModify)
1755 return;
1757 /* update line and column nubers in statistics line */
1758 UpdateStatsLine(window);
1760 /* Check the character before the cursor for matchable characters */
1761 FlashMatching(window, w);
1763 /* Check for changes to read-only status and/or file modifications */
1764 CheckForChangesToFile(window);
1767 static void modifiedCB(int pos, int nInserted, int nDeleted, int nRestyled,
1768 char *deletedText, void *cbArg)
1770 WindowInfo *window = (WindowInfo *)cbArg;
1771 int selected = window->buffer->primary.selected;
1773 /* update the table of bookmarks */
1774 UpdateMarkTable(window, pos, nInserted, nDeleted);
1776 /* Check and dim/undim selection related menu items */
1777 if ((window->wasSelected && !selected) ||
1778 (!window->wasSelected && selected)) {
1779 window->wasSelected = selected;
1780 XtSetSensitive(window->printSelItem, selected);
1781 XtSetSensitive(window->cutItem, selected);
1782 XtSetSensitive(window->copyItem, selected);
1783 XtSetSensitive(window->delItem, selected);
1784 /* Note we don't change the selection for items like
1785 "Open Selected" and "Find Selected". That's because
1786 it works on selections in external applications.
1787 Desensitizing it if there's no NEdit selection
1788 disables this feature. */
1789 #ifndef VMS
1790 XtSetSensitive(window->filterItem, selected);
1791 #endif
1793 DimSelectionDepUserMenuItems(window, selected);
1794 if (window->replaceDlog != NULL)
1796 UpdateReplaceActionButtons(window);
1800 /* Make sure line number display is sufficient for new data */
1801 UpdateLineNumDisp(window);
1803 /* When the program needs to make a change to a text area without without
1804 recording it for undo or marking file as changed it sets ignoreModify */
1805 if (window->ignoreModify || (nDeleted == 0 && nInserted == 0))
1806 return;
1808 /* Save information for undoing this operation (this call also counts
1809 characters and editing operations for triggering autosave */
1810 SaveUndoInformation(window, pos, nInserted, nDeleted, deletedText);
1812 /* Trigger automatic backup if operation or character limits reached */
1813 if (window->autoSave &&
1814 (window->autoSaveCharCount > AUTOSAVE_CHAR_LIMIT ||
1815 window->autoSaveOpCount > AUTOSAVE_OP_LIMIT)) {
1816 WriteBackupFile(window);
1817 window->autoSaveCharCount = 0;
1818 window->autoSaveOpCount = 0;
1821 /* Indicate that the window has now been modified */
1822 SetWindowModified(window, TRUE);
1824 /* Update # of bytes, and line and col statistics */
1825 UpdateStatsLine(window);
1827 /* Check if external changes have been made to file and warn user */
1828 CheckForChangesToFile(window);
1831 static void focusCB(Widget w, WindowInfo *window, XtPointer callData)
1833 /* record which window pane last had the keyboard focus */
1834 window->lastFocus = w;
1836 /* update line number statistic to reflect current focus pane */
1837 UpdateStatsLine(window);
1839 /* finish off the current incremental search */
1840 EndISearch(window);
1842 /* Check for changes to read-only status and/or file modifications */
1843 CheckForChangesToFile(window);
1846 static void dragStartCB(Widget w, WindowInfo *window, XtPointer callData)
1848 /* don't record all of the intermediate drag steps for undo */
1849 window->ignoreModify = True;
1852 static void dragEndCB(Widget w, WindowInfo *window, dragEndCBStruct *callData)
1854 /* restore recording of undo information */
1855 window->ignoreModify = False;
1857 /* Do nothing if drag operation was canceled */
1858 if (callData->nCharsInserted == 0)
1859 return;
1861 /* Save information for undoing this operation not saved while
1862 undo recording was off */
1863 modifiedCB(callData->startPos, callData->nCharsInserted,
1864 callData->nCharsDeleted, 0, callData->deletedText, window);
1867 static void closeCB(Widget w, WindowInfo *window, XtPointer callData)
1869 if (WindowList->next == NULL) {
1870 if (!CheckPrefsChangesSaved(window->shell))
1871 return;
1872 if (!WindowList->fileChanged)
1873 exit(EXIT_SUCCESS);
1874 if (CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE))
1875 exit(EXIT_SUCCESS);
1876 } else
1877 CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE);
1880 static void saveYourselfCB(Widget w, WindowInfo *window, XtPointer callData)
1882 WindowInfo *win, **revWindowList;
1883 char geometry[MAX_GEOM_STRING_LEN];
1884 int argc = 0, maxArgc, nWindows, i;
1885 char **argv;
1886 int wasIconic = False;
1888 /* Only post a restart command on the first window in the window list so
1889 session manager can restart the whole set of windows in one executable,
1890 rather than one nedit per file. Even if the restart command is not on
1891 this window, the protocol demands that we set the window's WM_COMMAND
1892 property in response to the "save yourself" message */
1893 if (window != WindowList) {
1894 XSetCommand(TheDisplay, XtWindow(window->shell), NULL, 0);
1895 return;
1898 /* Allocate memory for an argument list and for a reversed list of
1899 windows. The window list is reversed for IRIX 4DWM and any other
1900 window/session manager combination which uses window creation
1901 order for re-associating stored geometry information with
1902 new windows created by a restored application */
1903 maxArgc = 1;
1904 nWindows = 0;
1905 for (win=WindowList; win!=NULL; win=win->next) {
1906 maxArgc += 4;
1907 nWindows++;
1909 argv = (char **)XtMalloc(maxArgc*sizeof(char *));
1910 revWindowList = (WindowInfo **)XtMalloc(sizeof(WindowInfo *)*nWindows);
1911 for (win=WindowList, i=nWindows-1; win!=NULL; win=win->next, i--)
1912 revWindowList[i] = win;
1914 /* Create command line arguments for restoring each window in the list */
1915 argv[argc++] = XtNewString(ArgV0);
1916 if (IsServer) {
1917 argv[argc++] = XtNewString("-server");
1918 if (GetPrefServerName()[0] != '\0') {
1919 argv[argc++] = XtNewString("-svrname");
1920 argv[argc++] = XtNewString(GetPrefServerName());
1923 for (i=0; i<nWindows; i++) {
1924 win = revWindowList[i];
1925 getGeometryString(win, geometry);
1926 argv[argc++] = XtNewString("-geometry");
1927 argv[argc++] = XtNewString(geometry);
1928 if (isIconic(win)) {
1929 argv[argc++] = XtNewString("-iconic");
1930 wasIconic = True;
1931 } else if (wasIconic) {
1932 argv[argc++] = XtNewString("-noiconic");
1933 wasIconic = False;
1935 if (win->filenameSet) {
1936 argv[argc] = XtMalloc(strlen(win->path) +
1937 strlen(win->filename) + 1);
1938 sprintf(argv[argc++], "%s%s", win->path, win->filename);
1941 XtFree((char *)revWindowList);
1943 /* Set the window's WM_COMMAND property to the created command line */
1944 XSetCommand(TheDisplay, XtWindow(window->shell), argv, argc);
1945 for (i=0; i<argc; i++)
1946 XtFree(argv[i]);
1947 XtFree((char *)argv);
1951 ** Returns true if window is iconic (as determined by the WM_STATE property
1952 ** on the shell window. I think this is the most reliable way to tell,
1953 ** but if someone has a better idea please send me a note).
1955 static int isIconic(WindowInfo *window)
1957 unsigned long *property = NULL;
1958 unsigned long nItems;
1959 unsigned long leftover;
1960 static Atom wmStateAtom = 0;
1961 Atom actualType;
1962 int actualFormat;
1963 int result;
1965 if (wmStateAtom == 0)
1966 wmStateAtom = XInternAtom (XtDisplay(window->shell), "WM_STATE", False);
1967 if (XGetWindowProperty(XtDisplay(window->shell), XtWindow(window->shell),
1968 wmStateAtom, 0L, 1L, False, wmStateAtom, &actualType, &actualFormat,
1969 &nItems, &leftover, (unsigned char **)&property) != Success ||
1970 nItems != 1 || property == NULL)
1971 return FALSE;
1972 result = *property == IconicState;
1973 XtFree((char *)property);
1974 return result;
1978 ** Add a window to the the window list.
1980 static void addToWindowList(WindowInfo *window)
1982 WindowInfo *temp;
1984 temp = WindowList;
1985 WindowList = window;
1986 window->next = temp;
1990 ** Remove a window from the list of windows
1992 static void removeFromWindowList(WindowInfo *window)
1994 WindowInfo *temp;
1996 if (WindowList == window)
1997 WindowList = window->next;
1998 else {
1999 for (temp = WindowList; temp != NULL; temp = temp->next) {
2000 if (temp->next == window) {
2001 temp->next = window->next;
2002 break;
2009 ** If necessary, enlarges the window and line number display area
2010 ** to make room for numbers.
2012 void UpdateLineNumDisp(WindowInfo *window)
2014 Dimension windowWidth;
2015 int i, fontWidth, reqCols, lineNumCols;
2016 int oldWidth, newWidth, marginWidth;
2017 Widget text;
2018 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
2020 if (!window->showLineNumbers)
2021 return;
2023 /* Decide how wide the line number field has to be to display all
2024 possible line numbers */
2025 reqCols = textD->nBufferLines<1 ? 1 : log10((double)textD->nBufferLines)+1;
2026 if (reqCols < MIN_LINE_NUM_COLS)
2027 reqCols = MIN_LINE_NUM_COLS;
2029 /* Is the width of the line number area sufficient to display all the
2030 line numbers in the file? If not, expand line number field, and the
2031 window width */
2032 XtVaGetValues(window->textArea, textNlineNumCols, &lineNumCols,
2033 textNmarginWidth, &marginWidth, NULL);
2034 if (lineNumCols < reqCols) {
2035 fontWidth = textD->fontStruct->max_bounds.width;
2036 oldWidth = textD->left - marginWidth;
2037 newWidth = reqCols * fontWidth + marginWidth;
2038 XtVaGetValues(window->shell, XmNwidth, &windowWidth, NULL);
2039 XtVaSetValues(window->shell, XmNwidth,
2040 windowWidth + newWidth-oldWidth, NULL);
2041 UpdateWMSizeHints(window);
2042 for (i=0; i<=window->nPanes; i++) {
2043 text = i==0 ? window->textArea : window->textPanes[i-1];
2044 XtVaSetValues(text, textNlineNumCols, reqCols, NULL);
2050 ** Update the optional statistics line.
2052 void UpdateStatsLine(WindowInfo *window)
2054 int line, pos, colNum;
2055 char *string, *format, slinecol[32];
2056 Widget statW = window->statsLine;
2057 XmString xmslinecol;
2058 #ifdef SGI_CUSTOM
2059 char *sleft, *smid, *sright;
2060 #endif
2062 /* This routine is called for each character typed, so its performance
2063 affects overall editor perfomance. Only update if the line is on. */
2064 if (!window->showStats)
2065 return;
2067 /* Compose the string to display. If line # isn't available, leave it off */
2068 pos = TextGetCursorPos(window->lastFocus);
2069 string = XtMalloc(strlen(window->filename) + strlen(window->path) + 45);
2070 format = window->fileFormat == DOS_FILE_FORMAT ? " DOS" :
2071 (window->fileFormat == MAC_FILE_FORMAT ? " Mac" : "");
2072 if (!TextPosToLineAndCol(window->lastFocus, pos, &line, &colNum)) {
2073 sprintf(string, "%s%s%s %d bytes", window->path, window->filename,
2074 format, window->buffer->length);
2075 sprintf(slinecol, "L: --- C: ---");
2076 } else {
2077 sprintf(slinecol, "L: %d C: %d", line, colNum);
2078 if (window->showLineNumbers)
2079 sprintf(string, "%s%s%s byte %d of %d", window->path,
2080 window->filename, format, pos,
2081 window->buffer->length);
2082 else
2083 sprintf(string, "%s%s%s %d bytes", window->path,
2084 window->filename, format, window->buffer->length);
2087 /* Don't clobber the line if there's a special message being displayed */
2088 if (!window->modeMessageDisplayed) {
2089 /* Change the text in the stats line */
2090 #ifdef SGI_CUSTOM
2091 /* don't show full pathname, just dir and filename (+ byte info) */
2092 smid = strchr(string, '/');
2093 if ( smid != NULL ) {
2094 sleft = smid;
2095 sright = strrchr(string, '/');
2096 while (strcmp(smid, sright)) {
2097 sleft = smid;
2098 smid = strchr(sleft + 1, '/');
2100 XmTextReplace(statW, 0, XmTextGetLastPosition(statW), sleft + 1);
2101 } else
2102 XmTextReplace(statW, 0, XmTextGetLastPosition(statW), string);
2103 #else
2104 XmTextReplace(statW, 0, XmTextGetLastPosition(statW), string);
2105 #endif
2107 XtFree(string);
2109 /* Update the line/col display */
2110 xmslinecol = XmStringCreateSimple(slinecol);
2111 XtVaSetValues(window->statsLineColNo,
2112 XmNlabelString, xmslinecol, NULL);
2113 XmStringFree(xmslinecol);
2116 static Boolean currentlyBusy = False;
2117 static long busyStartTime = 0;
2118 static Boolean modeMessageSet = False;
2121 * Auxiliary function for measuring elapsed time during busy waits.
2123 static long getRelTimeInTenthsOfSeconds()
2125 #ifdef __unix__
2126 struct timeval current;
2127 gettimeofday(&current, NULL);
2128 return (current.tv_sec*10 + current.tv_usec/100000) & 0xFFFFFFFL;
2129 #else
2130 time_t current;
2131 time(&current);
2132 return (current*10) & 0xFFFFFFFL;
2133 #endif
2136 void AllWindowsBusy(const char *message)
2138 WindowInfo *w;
2140 if (!currentlyBusy)
2142 busyStartTime = getRelTimeInTenthsOfSeconds();
2143 modeMessageSet = False;
2145 for (w=WindowList; w!=NULL; w=w->next)
2147 /* We don't the display message here yet, but defer it for
2148 a while. If the wait is short, we don't want
2149 to have it flash on and off the screen. However,
2150 we can't use a time since in generally we are in
2151 a tight loop and only processing exposure events, so it's
2152 up to the caller to make sure that this routine is called
2153 at regular intervals.
2155 BeginWait(w->shell);
2157 } else if (!modeMessageSet && message &&
2158 getRelTimeInTenthsOfSeconds() - busyStartTime > 10) {
2159 /* Show the mode message when we've been busy for more than a second */
2160 for (w=WindowList; w!=NULL; w=w->next) {
2161 SetModeMessage(w, message);
2163 modeMessageSet = True;
2165 BusyWait(WindowList->shell);
2167 currentlyBusy = True;
2170 void AllWindowsUnbusy(void)
2172 WindowInfo *w;
2174 for (w=WindowList; w!=NULL; w=w->next)
2176 ClearModeMessage(w);
2177 EndWait(w->shell);
2180 currentlyBusy = False;
2181 modeMessageSet = False;
2182 busyStartTime = 0;
2186 ** Paned windows are impossible to adjust after they are created, which makes
2187 ** them nearly useless for NEdit (or any application which needs to dynamically
2188 ** adjust the panes) unless you tweek some private data to overwrite the
2189 ** desired and minimum pane heights which were set at creation time. These
2190 ** will probably break in a future release of Motif because of dependence on
2191 ** private data.
2193 static void setPaneDesiredHeight(Widget w, int height)
2195 ((XmPanedWindowConstraintPtr)w->core.constraints)->panedw.dheight = height;
2197 static void setPaneMinHeight(Widget w, int min)
2199 ((XmPanedWindowConstraintPtr)w->core.constraints)->panedw.min = min;
2203 ** Update the window manager's size hints. These tell it the increments in
2204 ** which it is allowed to resize the window. While this isn't particularly
2205 ** important for NEdit (since it can tolerate any window size), setting these
2206 ** hints also makes the resize indicator show the window size in characters
2207 ** rather than pixels, which is very helpful to users.
2209 void UpdateWMSizeHints(WindowInfo *window)
2211 Dimension shellWidth, shellHeight, textHeight, hScrollBarHeight;
2212 int marginHeight, marginWidth, totalHeight;
2213 XFontStruct *fs;
2214 int i, baseWidth, baseHeight, fontHeight, fontWidth;
2215 Widget hScrollBar;
2216 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
2218 /* Find the base (non-expandable) width and height of the editor window */
2219 XtVaGetValues(window->textArea, XmNheight, &textHeight,
2220 textNmarginHeight, &marginHeight, textNmarginWidth, &marginWidth,
2221 NULL);
2222 totalHeight = textHeight - 2*marginHeight;
2223 for (i=0; i<window->nPanes; i++) {
2224 XtVaGetValues(window->textPanes[i], XmNheight, &textHeight,
2225 textNhScrollBar, &hScrollBar, NULL);
2226 totalHeight += textHeight - 2*marginHeight;
2227 if (!XtIsManaged(hScrollBar)) {
2228 XtVaGetValues(hScrollBar, XmNheight, &hScrollBarHeight, NULL);
2229 totalHeight -= hScrollBarHeight;
2232 XtVaGetValues(window->shell, XmNwidth, &shellWidth,
2233 XmNheight, &shellHeight, NULL);
2234 baseWidth = shellWidth - textD->width;
2235 baseHeight = shellHeight - totalHeight;
2237 /* Find the dimensions of a single character of the text font */
2238 XtVaGetValues(window->textArea, textNfont, &fs, NULL);
2239 fontHeight = textD->ascent + textD->descent;
2240 fontWidth = fs->max_bounds.width;
2242 /* Set the size hints in the shell widget */
2243 XtVaSetValues(window->shell, XmNwidthInc, fs->max_bounds.width,
2244 XmNheightInc, fontHeight,
2245 XmNbaseWidth, baseWidth, XmNbaseHeight, baseHeight,
2246 XmNminWidth, baseWidth + fontWidth,
2247 XmNminHeight, baseHeight + (1+window->nPanes) * fontHeight, NULL);
2251 ** Update the minimum allowable height for a split window pane after a change
2252 ** to font or margin height.
2254 void UpdateMinPaneHeights(WindowInfo *window)
2256 textDisp *textD = ((TextWidget)window->textArea)->text.textD;
2257 Dimension hsbHeight, swMarginHeight,frameShadowHeight;
2258 int i, marginHeight, minPaneHeight;
2259 Widget hScrollBar;
2261 /* find the minimum allowable size for a pane */
2262 XtVaGetValues(window->textArea, textNhScrollBar, &hScrollBar, NULL);
2263 XtVaGetValues(containingPane(window->textArea),
2264 XmNscrolledWindowMarginHeight, &swMarginHeight, NULL);
2265 XtVaGetValues(XtParent(window->textArea),
2266 XmNshadowThickness, &frameShadowHeight, NULL);
2267 XtVaGetValues(window->textArea, textNmarginHeight, &marginHeight, NULL);
2268 XtVaGetValues(hScrollBar, XmNheight, &hsbHeight, NULL);
2269 minPaneHeight = textD->ascent + textD->descent + marginHeight*2 +
2270 swMarginHeight*2 + hsbHeight + 2*frameShadowHeight;
2272 /* Set it in all of the widgets in the window */
2273 setPaneMinHeight(containingPane(window->textArea), minPaneHeight);
2274 for (i=0; i<window->nPanes; i++)
2275 setPaneMinHeight(containingPane(window->textPanes[i]),
2276 minPaneHeight);
2279 /* Add an icon to an applicaction shell widget. addWindowIcon adds a large
2280 ** (primary window) icon, AddSmallIcon adds a small (secondary window) icon.
2282 ** Note: I would prefer that these were not hardwired, but yhere is something
2283 ** weird about the XmNiconPixmap resource that prevents it from being set
2284 ** from the defaults in the application resource database.
2286 static void addWindowIcon(Widget shell)
2288 static Pixmap iconPixmap = 0, maskPixmap = 0;
2290 if (iconPixmap == 0) {
2291 iconPixmap = XCreateBitmapFromData(TheDisplay,
2292 RootWindowOfScreen(XtScreen(shell)), (char *)iconBits,
2293 iconBitmapWidth, iconBitmapHeight);
2294 maskPixmap = XCreateBitmapFromData(TheDisplay,
2295 RootWindowOfScreen(XtScreen(shell)), (char *)maskBits,
2296 iconBitmapWidth, iconBitmapHeight);
2298 XtVaSetValues(shell, XmNiconPixmap, iconPixmap, XmNiconMask, maskPixmap,
2299 NULL);
2301 void AddSmallIcon(Widget shell)
2303 static Pixmap iconPixmap = 0, maskPixmap = 0;
2305 if (iconPixmap == 0) {
2306 iconPixmap = XCreateBitmapFromData(TheDisplay,
2307 RootWindowOfScreen(XtScreen(shell)), (char *)n_bits,
2308 n_width, n_height);
2309 maskPixmap = XCreateBitmapFromData(TheDisplay,
2310 RootWindowOfScreen(XtScreen(shell)), (char *)n_mask,
2311 n_width, n_height);
2313 XtVaSetValues(shell, XmNiconPixmap, iconPixmap,
2314 XmNiconMask, maskPixmap, NULL);
2318 ** Save the position and size of a window as an X standard geometry string.
2319 ** A string of at least MAX_GEOMETRY_STRING_LEN characters should be
2320 ** provided in the argument "geomString" to receive the result.
2322 static void getGeometryString(WindowInfo *window, char *geomString)
2324 int x, y, fontWidth, fontHeight, baseWidth, baseHeight;
2325 unsigned int width, height, dummyW, dummyH, bw, depth, nChild;
2326 Window parent, root, *child, w = XtWindow(window->shell);
2327 Display *dpy = XtDisplay(window->shell);
2329 /* Find the width and height from the window of the shell */
2330 XGetGeometry(dpy, w, &root, &x, &y, &width, &height, &bw, &depth);
2332 /* Find the top left corner (x and y) of the window decorations. (This
2333 is what's required in the geometry string to restore the window to it's
2334 original position, since the window manager re-parents the window to
2335 add it's title bar and menus, and moves the requested window down and
2336 to the left.) The position is found by traversing the window hier-
2337 archy back to the window to the last parent before the root window */
2338 for(;;) {
2339 XQueryTree(dpy, w, &root, &parent, &child, &nChild);
2340 XFree((char*)child);
2341 if (parent == root)
2342 break;
2343 w = parent;
2345 XGetGeometry(dpy, w, &root, &x, &y, &dummyW, &dummyH, &bw, &depth);
2347 /* Use window manager size hints (set by UpdateWMSizeHints) to
2348 translate the width and height into characters, as opposed to pixels */
2349 XtVaGetValues(window->shell, XmNwidthInc, &fontWidth,
2350 XmNheightInc, &fontHeight, XmNbaseWidth, &baseWidth,
2351 XmNbaseHeight, &baseHeight, NULL);
2352 width = (width-baseWidth) / fontWidth;
2353 height = (height-baseHeight) / fontHeight;
2355 /* Write the string */
2356 CreateGeometryString(geomString, x, y, width, height,
2357 XValue | YValue | WidthValue | HeightValue);
2361 ** Xt timer procedure for updating size hints. The new sizes of objects in
2362 ** the window are not ready immediately after adding or removing panes. This
2363 ** is a timer routine to be invoked with a timeout of 0 to give the event
2364 ** loop a chance to finish processing the size changes before reading them
2365 ** out for setting the window manager size hints.
2367 static void wmSizeUpdateProc(XtPointer clientData, XtIntervalId *id)
2369 UpdateWMSizeHints((WindowInfo *)clientData);
2372 #ifdef ROWCOLPATCH
2374 ** There is a bad memory reference in the delete_child method of the
2375 ** RowColumn widget in some Motif versions (so far, just Solaris with Motif
2376 ** 1.2.3) which appears durring the phase 2 destroy of the widget. This
2377 ** patch replaces the method with a call to the Composite widget's
2378 ** delete_child method. The composite delete_child method handles part,
2379 ** but not all of what would have been done by the original method, meaning
2380 ** that this is dangerous and should be used sparingly. Note that
2381 ** patchRowCol is called only in CloseWindow, before the widget is about to
2382 ** be destroyed, and only on systems where the bug has been observed
2384 static void patchRowCol(Widget w)
2386 ((XmRowColumnClassRec *)XtClass(w))->composite_class.delete_child =
2387 patchedRemoveChild;
2389 static void patchedRemoveChild(Widget child)
2391 /* Call composite class method instead of broken row col delete_child
2392 method */
2393 (*((CompositeWidgetClass)compositeWidgetClass)->composite_class.
2394 delete_child) (child);
2396 #endif /* ROWCOLPATCH */
2399 ** Set the backlight character class string
2401 void SetBacklightChars(WindowInfo *window, char *applyBacklightTypes)
2403 int i;
2404 int is_applied = XmToggleButtonGetState(window->backlightCharsItem) ? 1 : 0;
2405 int do_apply = applyBacklightTypes ? 1 : 0;
2407 window->backlightChars = do_apply;
2409 XtFree(window->backlightCharTypes);
2410 if (window->backlightChars &&
2411 (window->backlightCharTypes = XtMalloc(strlen(applyBacklightTypes)+1)))
2412 strcpy(window->backlightCharTypes, applyBacklightTypes);
2413 else
2414 window->backlightCharTypes = NULL;
2416 XtVaSetValues(window->textArea,
2417 textNbacklightCharTypes, window->backlightCharTypes, 0);
2418 for (i=0; i<window->nPanes; i++)
2419 XtVaSetValues(window->textPanes[i],
2420 textNbacklightCharTypes, window->backlightCharTypes, 0);
2421 if (is_applied != do_apply)
2422 XmToggleButtonSetState(window->backlightCharsItem, do_apply, False);
2425 static int sortAlphabetical(const void* k1, const void* k2)
2427 const char* key1 = *(const char**)k1;
2428 const char* key2 = *(const char**)k2;
2429 return strcmp(key1, key2);
2433 * Checks whether a given virtual key binding string is invalid.
2434 * A binding is considered invalid if there are duplicate key entries.
2436 static int virtKeyBindingsAreInvalid(const unsigned char* bindings)
2438 int maxCount = 1, i, count;
2439 const char *pos = (const char*)bindings;
2440 char *copy;
2441 char *pos2, *pos3;
2442 char **keys;
2443 /* First count the number of bindings; bindings are separated by \n
2444 strings. The number of bindings equals the number of \n + 1.
2445 Beware of leading and trailing \n; the number is actually an
2446 upper bound on the number of entries. */
2447 while ((pos = strstr(pos, "\n"))) { ++pos; ++maxCount; }
2449 if (maxCount == 1) return False; /* One binding is always ok */
2451 keys = (char**)malloc(maxCount*sizeof(char*));
2452 copy = XtNewString((const char*)bindings);
2453 i = 0;
2454 pos2 = copy;
2456 count = 0;
2457 while (i<maxCount && pos2 && *pos2)
2459 while (isspace((int) *pos2) || *pos2 == '\n') ++pos2;
2461 if (*pos2 == '!') /* Ignore comment lines */
2463 pos2 = strstr(pos2, "\n");
2464 continue; /* Go to the next line */
2467 if (*pos2)
2469 keys[i++] = pos2;
2470 ++count;
2471 pos3 = strstr(pos2, ":");
2472 if (pos3)
2474 *pos3++ = 0; /* Cut the string and jump to the next entry */
2475 pos2 = pos3;
2477 pos2 = strstr(pos2, "\n");
2481 if (count <= 1)
2483 free(keys);
2484 XtFree(copy);
2485 return False; /* No conflict */
2488 /* Sort the keys and look for duplicates */
2489 qsort((void*)keys, count, sizeof(const char*), sortAlphabetical);
2490 for (i=1; i<count; ++i)
2492 if (!strcmp(keys[i-1], keys[i]))
2494 /* Duplicate detected */
2495 free(keys);
2496 XtFree(copy);
2497 return True;
2500 free(keys);
2501 XtFree(copy);
2502 return False;
2506 * Optionally sanitizes the Motif default virtual key bindings.
2507 * Some applications install invalid bindings (attached to the root window),
2508 * which cause certain keys to malfunction in NEdit.
2509 * Through an X-resource, users can choose whether they want
2510 * - to always keep the existing bindings
2511 * - to override the bindings only if they are invalid
2512 * - to always override the existing bindings.
2515 static Atom virtKeyAtom;
2517 static unsigned char* sanitizeVirtualKeyBindings()
2519 int overrideBindings = GetPrefOverrideVirtKeyBindings();
2520 Window rootWindow;
2521 const char *virtKeyPropName = "_MOTIF_DEFAULT_BINDINGS";
2522 Atom dummyAtom;
2523 int getFmt;
2524 unsigned long dummyULong, nItems;
2525 unsigned char *insaneVirtKeyBindings = NULL;
2527 if (overrideBindings == VIRT_KEY_OVERRIDE_NEVER) return NULL;
2529 virtKeyAtom = XInternAtom(TheDisplay, virtKeyPropName, False);
2530 rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
2532 /* Remove the property, if it exists; we'll restore it later again */
2533 if (XGetWindowProperty(TheDisplay, rootWindow, virtKeyAtom, 0, INT_MAX,
2534 True, XA_STRING, &dummyAtom, &getFmt, &nItems,
2535 &dummyULong, &insaneVirtKeyBindings) != Success
2536 || nItems == 0)
2538 return NULL; /* No binding yet; nothing to do */
2541 if (overrideBindings == VIRT_KEY_OVERRIDE_AUTO)
2543 if (!virtKeyBindingsAreInvalid(insaneVirtKeyBindings))
2545 /* Restore the property immediately; it seems valid */
2546 XChangeProperty(TheDisplay, rootWindow, virtKeyAtom, XA_STRING, 8,
2547 PropModeReplace, insaneVirtKeyBindings,
2548 strlen((const char*)insaneVirtKeyBindings));
2549 XFree((char*)insaneVirtKeyBindings);
2550 return NULL; /* Prevent restoration */
2553 return insaneVirtKeyBindings;
2557 * NEdit should not mess with the bindings installed by other apps, so we
2558 * just restore whatever was installed, if necessary
2560 static void restoreInsaneVirtualKeyBindings(unsigned char *insaneVirtKeyBindings)
2562 if (insaneVirtKeyBindings)
2564 Window rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
2565 /* Restore the root window atom, such that we don't affect
2566 other apps. */
2567 XChangeProperty(TheDisplay, rootWindow, virtKeyAtom, XA_STRING, 8,
2568 PropModeReplace, insaneVirtKeyBindings,
2569 strlen((const char*)insaneVirtKeyBindings));
2570 XFree((char*)insaneVirtKeyBindings);
2574 static Widget containingPane(Widget w)
2576 /* The containing pane used to simply be the first parent, but with
2577 the introduction of an XmFrame, it's the grandparent. */
2578 return XtParent(XtParent(w));