1 static const char CVSID
[] = "$Id: nedit.c,v 1.100 2007/11/29 22:18:09 tringali Exp $";
2 /*******************************************************************************
4 * nedit.c -- Nirvana Editor main program *
6 * Copyright (C) 1999 Mark Edel *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. In addition, you may distribute versions of this program linked to *
12 * Motif or Open Motif. See README for details. *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
23 * Nirvana Text Editor *
26 * Written by Mark Edel *
30 * 8/18/93 - Mark Edel & Joy Kyriakopulos - Ported to VMS *
32 *******************************************************************************/
35 #include "../config.h"
39 /* #include "textBuf.h" */
41 #include "preferences.h"
42 #include "regularExp.h"
43 #include "selection.h"
48 #include "interpret.h"
51 #include "../util/misc.h"
52 #include "../util/printUtils.h"
53 #include "../util/fileUtils.h"
54 #include "../util/getfiles.h"
55 #include "../util/motif.h"
65 #include <X11/Xlocale.h>
70 #include <X11/Intrinsic.h>
75 #include <Xm/RepType.h>
80 #include "../util/VMSparam.h"
81 #include "../util/VMSUtils.h"
85 #include <sys/param.h>
93 static void nextArg(int argc
, char **argv
, int *argIndex
);
94 static int checkDoMacroArg(const char *macro
);
95 static String
neditLanguageProc(Display
*dpy
, String xnl
, XtPointer closure
);
96 static void maskArgvKeywords(int argc
, char **argv
, const char **maskArgs
);
97 static void unmaskArgvKeywords(int argc
, char **argv
, const char **maskArgs
);
98 static void fixupBrokenXKeysymDB(void);
99 static void patchResourcesForVisual(void);
100 static void patchResourcesForKDEbug(void);
101 static void patchLocaleForMotif(void);
102 static unsigned char* sanitizeVirtualKeyBindings(void);
103 static int sortAlphabetical(const void* k1
, const void* k2
);
104 static int virtKeyBindingsAreInvalid(const unsigned char* bindings
);
105 static void restoreInsaneVirtualKeyBindings(unsigned char* bindings
);
106 static void noWarningFilter(String
);
107 static void showWarningFilter(String
);
109 WindowInfo
*WindowList
= NULL
;
110 Display
*TheDisplay
= NULL
;
112 Boolean IsServer
= False
;
115 /* Reasons for choice of default font qualifications:
117 iso8859 appears to be necessary for newer versions of XFree86 that
118 default to Unicode encoding, which doesn't quite work with Motif.
119 Otherwise Motif puts up garbage (square blocks).
121 (This of course, is a stupid default because there are far more iso8859
122 apps than Unicode apps. But the X folks insist it's a client bug. Hah.)
124 RedHat 7.3 won't default to '-1' for an encoding, if left with a *,
125 and so reverts to "fixed". Yech. */
127 #define NEDIT_DEFAULT_FONT "-*-helvetica-medium-r-normal-*-*-120-*-*-*-*-iso8859-1," \
128 "-*-helvetica-bold-r-normal-*-*-120-*-*-*-*-iso8859-1=BOLD," \
129 "-*-helvetica-medium-o-normal-*-*-120-*-*-*-*-iso8859-1=ITALIC"
131 #define NEDIT_FIXED_FONT "-*-courier-medium-r-normal-*-*-120-*-*-*-*-iso8859-1," \
132 "-*-courier-bold-r-normal-*-*-120-*-*-*-*-iso8859-1=BOLD," \
133 "-*-courier-medium-o-normal-*-*-120-*-*-*-*-iso8859-1=ITALIC"
135 #define NEDIT_DEFAULT_BG "#b3b3b3"
137 #define NEDIT_TEXT_TRANSLATIONS "#override\\n" \
138 "Ctrl~Alt~Meta<KeyPress>v: paste-clipboard()\\n" \
139 "Ctrl~Alt~Meta<KeyPress>c: copy-clipboard()\\n" \
140 "Ctrl~Alt~Meta<KeyPress>x: cut-clipboard()\\n" \
141 "Ctrl~Alt~Meta<KeyPress>u: delete-to-start-of-line()\\n"
143 static char *fallbackResources
[] = {
144 /* Try to avoid Motif's horrificly ugly default colors and fonts,
145 if the user's environment provides no usable defaults. We try
146 to choose a Windows-y default color setting here. Editable text
147 fields are forced to a fixed-pitch font for usability.
149 By using the VendorShell fontList resources, Motif automatically
150 groups the fonts into the right classes. It's then easier for
151 the user or environment to override this sensibly:
153 nedit -xrm '*textFontList: myfont'
155 This is broken in recent versions of LessTif.
158 #ifdef LESSTIF_VERSION
159 "*FontList: " NEDIT_DEFAULT_FONT
,
160 "*XmText.FontList: " NEDIT_FIXED_FONT
,
161 "*XmTextField.FontList: " NEDIT_FIXED_FONT
,
162 "*XmList.FontList: " NEDIT_FIXED_FONT
,
163 "*XmFileSelectionBox*XmList.FontList: " NEDIT_FIXED_FONT
,
165 "*buttonFontList: " NEDIT_DEFAULT_FONT
,
166 "*labelFontList: " NEDIT_DEFAULT_FONT
,
167 "*textFontList: " NEDIT_FIXED_FONT
,
170 "*background: " NEDIT_DEFAULT_BG
,
171 "*foreground: " NEDIT_DEFAULT_FG
,
172 "*XmText.foreground: " NEDIT_DEFAULT_FG
,
173 "*XmText.background: " NEDIT_DEFAULT_TEXT_BG
,
174 "*XmList.foreground: " NEDIT_DEFAULT_FG
,
175 "*XmList.background: " NEDIT_DEFAULT_TEXT_BG
,
176 "*XmTextField.foreground: " NEDIT_DEFAULT_FG
,
177 "*XmTextField.background: " NEDIT_DEFAULT_TEXT_BG
,
179 /* Use baseTranslations as per Xt Programmer's Manual, 10.2.12 */
180 "*XmText.baseTranslations: " NEDIT_TEXT_TRANSLATIONS
,
181 "*XmTextField.baseTranslations: " NEDIT_TEXT_TRANSLATIONS
,
183 "*XmLFolder.highlightThickness: 0",
184 "*XmLFolder.shadowThickness: 1",
185 "*XmLFolder.maxTabWidth: 150",
186 "*XmLFolder.traversalOn: False",
187 "*XmLFolder.inactiveForeground: #666" ,
188 "*tab.alignment: XmALIGNMENT_BEGINNING",
189 "*tab.marginWidth: 0",
190 "*tab.marginHeight: 1",
192 /* Prevent the file selection box from acting stupid. */
193 "*XmFileSelectionBox.resizePolicy: XmRESIZE_NONE",
194 "*XmFileSelectionBox.textAccelerators:",
195 "*XmFileSelectionBox.pathMode: XmPATH_MODE_RELATIVE",
196 "*XmFileSelectionBox.width: 500",
197 "*XmFileSelectionBox.height: 400",
199 /* NEdit-specific widgets. Theses things should probably be manually
200 jammed into the database, rather than fallbacks. We really want
201 the accelerators to be there even if someone creates an app-defaults
202 file against our wishes. */
204 "*text.lineNumForeground: " NEDIT_DEFAULT_LINENO_FG
,
205 "*text.background: " NEDIT_DEFAULT_TEXT_BG
,
206 "*text.foreground: " NEDIT_DEFAULT_FG
,
207 "*text.highlightForeground: " NEDIT_DEFAULT_HI_FG
,
208 "*text.highlightBackground: " NEDIT_DEFAULT_HI_BG
,
209 "*textFrame.shadowThickness: 1",
210 "*menuBar.marginHeight: 0",
211 "*menuBar.shadowThickness: 1",
212 "*pane.sashHeight: 11",
213 "*pane.sashWidth: 11",
214 "*pane.marginWidth: 0",
215 "*pane.marginHeight: 0",
216 "*scrolledW*spacing: 0",
217 "*text.selectionArrayCount: 3",
218 "*helpText.background: " NEDIT_DEFAULT_HELP_BG
,
219 "*helpText.foreground: " NEDIT_DEFAULT_HELP_FG
,
220 "*helpText.selectBackground: " NEDIT_DEFAULT_BG
,
221 "*statsLine.background: " NEDIT_DEFAULT_BG
,
222 "*statsLine.FontList: " NEDIT_DEFAULT_FONT
,
223 "*calltip.background: LemonChiffon1",
224 "*calltip.foreground: black",
225 "*iSearchForm*highlightThickness: 1",
226 "*fileMenu.tearOffModel: XmTEAR_OFF_ENABLED",
227 "*editMenu.tearOffModel: XmTEAR_OFF_ENABLED",
228 "*searchMenu.tearOffModel: XmTEAR_OFF_ENABLED",
229 "*preferencesMenu.tearOffModel: XmTEAR_OFF_ENABLED",
230 "*windowsMenu.tearOffModel: XmTEAR_OFF_ENABLED",
231 "*shellMenu.tearOffModel: XmTEAR_OFF_ENABLED",
232 "*macroMenu.tearOffModel: XmTEAR_OFF_ENABLED",
233 "*helpMenu.tearOffModel: XmTEAR_OFF_ENABLED",
234 "*fileMenu.mnemonic: F",
235 "*fileMenu.new.accelerator: Ctrl<Key>n",
236 "*fileMenu.new.acceleratorText: Ctrl+N",
237 "*fileMenu.newOpposite.accelerator: Shift Ctrl<Key>n",
238 "*fileMenu.newOpposite.acceleratorText: Shift+Ctrl+N",
239 "*fileMenu.open.accelerator: Ctrl<Key>o",
240 "*fileMenu.open.acceleratorText: Ctrl+O",
241 "*fileMenu.openSelected.accelerator: Ctrl<Key>y",
242 "*fileMenu.openSelected.acceleratorText: Ctrl+Y",
243 "*fileMenu.close.accelerator: Ctrl<Key>w",
244 "*fileMenu.close.acceleratorText: Ctrl+W",
245 "*fileMenu.save.accelerator: Ctrl<Key>s",
246 "*fileMenu.save.acceleratorText: Ctrl+S",
247 "*fileMenu.includeFile.accelerator: Alt<Key>i",
248 "*fileMenu.includeFile.acceleratorText: Alt+I",
249 "*fileMenu.print.accelerator: Ctrl<Key>p",
250 "*fileMenu.print.acceleratorText: Ctrl+P",
251 "*fileMenu.exit.accelerator: Ctrl<Key>q",
252 "*fileMenu.exit.acceleratorText: Ctrl+Q",
253 "*editMenu.mnemonic: E",
254 "*editMenu.undo.accelerator: Ctrl<Key>z",
255 "*editMenu.undo.acceleratorText: Ctrl+Z",
256 "*editMenu.redo.accelerator: Shift Ctrl<Key>z",
257 "*editMenu.redo.acceleratorText: Shift+Ctrl+Z",
258 /* Clipboard accelerators prevent the use of the clipboard in iSearch's
259 XmText, so they are left out. Their job is done by translations in
260 the main text widget, so the acceleratorText is still kept. */
261 "*editMenu.cut.acceleratorText: Ctrl+X",
262 "*editMenu.copy.acceleratorText: Ctrl+C",
263 "*editMenu.paste.acceleratorText: Ctrl+V",
264 "*editMenu.pasteColumn.accelerator: Shift Ctrl<Key>v",
265 "*editMenu.pasteColumn.acceleratorText: Ctrl+Shift+V",
266 "*editMenu.delete.acceleratorText: Del",
267 "*editMenu.selectAll.accelerator: Ctrl<Key>a",
268 "*editMenu.selectAll.acceleratorText: Ctrl+A",
269 "*editMenu.shiftLeft.accelerator: Ctrl<Key>9",
270 "*editMenu.shiftLeft.acceleratorText: [Shift]Ctrl+9",
271 "*editMenu.shiftLeftShift.accelerator: Shift Ctrl<Key>9",
272 "*editMenu.shiftRight.accelerator: Ctrl<Key>0",
273 "*editMenu.shiftRight.acceleratorText: [Shift]Ctrl+0",
274 "*editMenu.shiftRightShift.accelerator: Shift Ctrl<Key>0",
275 "*editMenu.upperCase.accelerator: Ctrl<Key>6",
276 "*editMenu.upperCase.acceleratorText: Ctrl+6",
277 "*editMenu.lowerCase.accelerator: Shift Ctrl<Key>6",
278 "*editMenu.lowerCase.acceleratorText: Shift+Ctrl+6",
279 "*editMenu.fillParagraph.accelerator: Ctrl<Key>j",
280 "*editMenu.fillParagraph.acceleratorText: Ctrl+J",
281 "*editMenu.insertFormFeed.accelerator: Alt Ctrl<Key>l",
282 "*editMenu.insertFormFeed.acceleratorText: Alt+Ctrl+L",
283 "*editMenu.insertCtrlCode.accelerator: Alt Ctrl<Key>i",
284 "*editMenu.insertCtrlCode.acceleratorText: Alt+Ctrl+I",
285 "*searchMenu.mnemonic: S",
286 "*searchMenu.find.accelerator: Ctrl<Key>f",
287 "*searchMenu.find.acceleratorText: [Shift]Ctrl+F",
288 "*searchMenu.findShift.accelerator: Shift Ctrl<Key>f",
289 "*searchMenu.findAgain.accelerator: Ctrl<Key>g",
290 "*searchMenu.findAgain.acceleratorText: [Shift]Ctrl+G",
291 "*searchMenu.findAgainShift.accelerator: Shift Ctrl<Key>g",
292 "*searchMenu.findSelection.accelerator: Ctrl<Key>h",
293 "*searchMenu.findSelection.acceleratorText: [Shift]Ctrl+H",
294 "*searchMenu.findSelectionShift.accelerator: Shift Ctrl<Key>h",
295 "*searchMenu.findIncremental.accelerator: Ctrl<Key>i",
296 "*searchMenu.findIncrementalShift.accelerator: Shift Ctrl<Key>i",
297 "*searchMenu.findIncremental.acceleratorText: [Shift]Ctrl+I",
298 "*searchMenu.replace.accelerator: Ctrl<Key>r",
299 "*searchMenu.replace.acceleratorText: [Shift]Ctrl+R",
300 "*searchMenu.replaceShift.accelerator: Shift Ctrl<Key>r",
301 "*searchMenu.findReplace.accelerator: Ctrl<Key>r",
302 "*searchMenu.findReplace.acceleratorText: [Shift]Ctrl+R",
303 "*searchMenu.findReplaceShift.accelerator: Shift Ctrl<Key>r",
304 "*searchMenu.replaceFindAgain.accelerator: Ctrl<Key>t",
305 "*searchMenu.replaceFindAgain.acceleratorText: [Shift]Ctrl+T",
306 "*searchMenu.replaceFindAgainShift.accelerator: Shift Ctrl<Key>t",
307 "*searchMenu.replaceAgain.accelerator: Alt<Key>t",
308 "*searchMenu.replaceAgain.acceleratorText: [Shift]Alt+T",
309 "*searchMenu.replaceAgainShift.accelerator: Shift Alt<Key>t",
310 "*searchMenu.gotoLineNumber.accelerator: Ctrl<Key>l",
311 "*searchMenu.gotoLineNumber.acceleratorText: Ctrl+L",
312 "*searchMenu.gotoSelected.accelerator: Ctrl<Key>e",
313 "*searchMenu.gotoSelected.acceleratorText: Ctrl+E",
314 "*searchMenu.mark.accelerator: Alt<Key>m",
315 "*searchMenu.mark.acceleratorText: Alt+M a-z",
316 "*searchMenu.gotoMark.accelerator: Alt<Key>g",
317 "*searchMenu.gotoMark.acceleratorText: [Shift]Alt+G a-z",
318 "*searchMenu.gotoMarkShift.accelerator: Shift Alt<Key>g",
319 "*searchMenu.gotoMatching.accelerator: Ctrl<Key>m",
320 "*searchMenu.gotoMatching.acceleratorText: [Shift]Ctrl+M",
321 "*searchMenu.gotoMatchingShift.accelerator: Shift Ctrl<Key>m",
322 "*searchMenu.findDefinition.accelerator: Ctrl<Key>d",
323 "*searchMenu.findDefinition.acceleratorText: Ctrl+D",
324 "*searchMenu.showCalltip.accelerator: Ctrl<Key>apostrophe",
325 "*searchMenu.showCalltip.acceleratorText: Ctrl+'",
326 "*preferencesMenu.mnemonic: P",
327 "*preferencesMenu.statisticsLine.accelerator: Alt<Key>a",
328 "*preferencesMenu.statisticsLine.acceleratorText: Alt+A",
329 "*preferencesMenu.overtype.acceleratorText: Insert",
330 "*shellMenu.mnemonic: l",
331 "*shellMenu.filterSelection.accelerator: Alt<Key>r",
332 "*shellMenu.filterSelection.acceleratorText: Alt+R",
333 "*shellMenu.executeCommand.accelerator: Alt<Key>x",
334 "*shellMenu.executeCommand.acceleratorText: Alt+X",
335 "*shellMenu.executeCommandLine.accelerator: Ctrl<Key>KP_Enter",
336 "*shellMenu.executeCommandLine.acceleratorText: Ctrl+KP Enter",
337 "*shellMenu.cancelShellCommand.accelerator: Ctrl<Key>period",
338 "*shellMenu.cancelShellCommand.acceleratorText: Ctrl+.",
339 "*macroMenu.mnemonic: c",
340 "*macroMenu.learnKeystrokes.accelerator: Alt<Key>k",
341 "*macroMenu.learnKeystrokes.acceleratorText: Alt+K",
342 "*macroMenu.finishLearn.accelerator: Alt<Key>k",
343 "*macroMenu.finishLearn.acceleratorText: Alt+K",
344 "*macroMenu.cancelLearn.accelerator: Ctrl<Key>period",
345 "*macroMenu.cancelLearn.acceleratorText: Ctrl+.",
346 "*macroMenu.replayKeystrokes.accelerator: Ctrl<Key>k",
347 "*macroMenu.replayKeystrokes.acceleratorText: Ctrl+K",
348 "*macroMenu.repeat.accelerator: Ctrl<Key>comma",
349 "*macroMenu.repeat.acceleratorText: Ctrl+,",
350 "*windowsMenu.mnemonic: W",
351 "*windowsMenu.splitPane.accelerator: Ctrl<Key>2",
352 "*windowsMenu.splitPane.acceleratorText: Ctrl+2",
353 "*windowsMenu.closePane.accelerator: Ctrl<Key>1",
354 "*windowsMenu.closePane.acceleratorText: Ctrl+1",
355 "*helpMenu.mnemonic: H",
356 "nedit.help.helpForm.sw.helpText*baseTranslations: #override\
357 <Key>Tab:help-focus-buttons()\\n\
358 <Key>Return:help-button-action(\"close\")\\n\
359 Ctrl<Key>F:help-button-action(\"find\")\\n\
360 Ctrl<Key>G:help-button-action(\"findAgain\")\\n\
361 <KeyPress>osfCancel:help-button-action(\"close\")\\n\
362 ~Meta~Ctrl~Shift<Btn1Down>:\
363 grab-focus() help-hyperlink()\\n\
364 ~Meta~Ctrl~Shift<Btn1Up>:\
365 help-hyperlink(\"current\", \"process-cancel\", \"extend-end\")\\n\
366 ~Meta~Ctrl~Shift<Btn2Down>:\
367 process-bdrag() help-hyperlink()\\n\
368 ~Meta~Ctrl~Shift<Btn2Up>:\
369 help-hyperlink(\"new\", \"process-cancel\", \"copy-to\")",
373 static const char cmdLineHelp
[] =
375 "Usage: nedit [-read] [-create] [-line n | +n] [-server] [-do command]\n\
376 [-tags file] [-tabs n] [-wrap] [-nowrap] [-autowrap]\n\
377 [-autoindent] [-noautoindent] [-autosave] [-noautosave]\n\
378 [-lm languagemode] [-rows n] [-columns n] [-font font]\n\
379 [-geometry geometry] [-iconic] [-noiconic] [-svrname name]\n\
380 [-display [host]:server[.screen] [-xrm resourcestring]\n\
381 [-import file] [-background color] [-foreground color]\n\
382 [-tabbed] [-untabbed] [-group] [-V|-version] [-h|-help]\n\
385 "[Sorry, no on-line help available.]\n"; /* Why is that ? */
388 int main(int argc
, char **argv
)
390 int i
, lineNum
, nRead
, fileSpecified
= FALSE
, editFlags
= CREATE
;
391 int gotoLine
= False
, macroFileRead
= False
, opts
= True
;
392 int iconic
=False
, tabbed
= -1, group
= 0, isTabbed
;
393 char *toDoCommand
= NULL
, *geometry
= NULL
, *langMode
= NULL
;
394 char filename
[MAXPATHLEN
], pathname
[MAXPATHLEN
];
395 XtAppContext context
;
397 WindowInfo
*window
= NULL
, *lastFile
= NULL
;
398 static const char *protectedKeywords
[] = {"-iconic", "-icon", "-geometry",
399 "-g", "-rv", "-reverse", "-bd", "-bordercolor", "-borderwidth",
400 "-bw", "-title", NULL
};
401 unsigned char* invalidBindings
= NULL
;
403 /* Warn user if this has been compiled wrong. */
404 enum MotifStability stability
= GetMotifStability();
405 if (stability
== MotifKnownBad
) {
406 fputs("nedit: WARNING: This version of NEdit is built incorrectly, and will be unstable.\n"
407 "nedit: Please get a stable version of NEdit from http://www.nedit.org.\n",
411 /* Save the command which was used to invoke nedit for restart command */
414 /* Set locale for C library, X, and Motif input functions.
415 Reverts to "C" if requested locale not available. */
416 XtSetLanguageProc(NULL
, neditLanguageProc
, NULL
);
418 /* Initialize X toolkit (does not open display yet) */
419 XtToolkitInitialize();
420 context
= XtCreateApplicationContext();
422 /* Set up a warning handler to trap obnoxious Xt grab warnings */
423 SuppressPassiveGrabWarnings();
425 /* Set up a handler to suppress X warning messages by default */
426 XtAppSetWarningHandler(context
, noWarningFilter
);
428 /* Set up default resources if no app-defaults file is found */
429 XtAppSetFallbackResources(context
, fallbackResources
);
431 #if XmVersion >= 1002
432 /* Allow users to change tear off menus with X resources */
433 XmRepTypeInstallTearOffModelConverter();
437 /* Convert the command line to Unix style (This is not an ideal solution) */
438 ConvertVMSCommandLine(&argc
, &argv
);
441 /* expand wildcards if necessary */
442 _wildcard(&argc
, &argv
);
445 /* Read the preferences file and command line into a database */
446 prefDB
= CreateNEditPrefDB(&argc
, argv
);
448 /* Open the display and read X database and remaining command line args.
449 XtOpenDisplay must be allowed to process some of the resource arguments
450 with its inaccessible internal option table, but others, like -geometry
451 and -iconic are per-window and it should not be allowed to consume them,
452 so we temporarily masked them out. */
453 maskArgvKeywords(argc
, argv
, protectedKeywords
);
454 /* X.Org 6.8 and above add support for ARGB visuals (with alpha-channel),
455 typically with a 32-bit color depth. By default, NEdit uses the visual
456 with the largest color depth. However, both OpenMotif and Lesstif
457 cannot handle ARGB visuals (crashes, weird display effects, ...), so
458 NEdit should avoid selecting such a visual.
459 Unfortunatly, there appears to be no reliable way to identify
460 ARGB visuals that doesn't require some of the recent X.Org
461 extensions. Luckily, the X.Org developers have provided a mechanism
462 that can hide these problematic visuals from the application. This can
463 be achieved by setting the XLIB_SKIP_ARGB_VISUALS environment variable.
464 Users can set this variable before starting NEdit, but it is much
465 more convenient that NEdit takes care of this. This must be done before
466 the display is opened (empirically verified). */
467 putenv("XLIB_SKIP_ARGB_VISUALS=1");
468 TheDisplay
= XtOpenDisplay (context
, NULL
, APP_NAME
, APP_CLASS
,
469 NULL
, 0, &argc
, argv
);
470 unmaskArgvKeywords(argc
, argv
, protectedKeywords
);
472 /* Respond to -V or -version even if there is no display */
473 for (i
= 1; i
< argc
&& strcmp(argv
[i
], "--"); i
++)
475 if (0 == strcmp(argv
[i
], "-V") || 0 == strcmp(argv
[i
], "-version"))
481 fputs ("NEdit: Can't open display\n", stderr
);
485 /* Must be done before creating widgets */
486 fixupBrokenXKeysymDB();
487 patchResourcesForVisual();
488 patchResourcesForKDEbug();
490 /* Initialize global symbols and subroutines used in the macro language */
492 RegisterMacroSubroutines();
494 /* Store preferences from the command line and .nedit file,
495 and set the appropriate preferences */
496 RestoreNEditPrefs(prefDB
, XtDatabase(TheDisplay
));
498 /* Intercept syntactically invalid virtual key bindings BEFORE we
499 create any shells. */
500 invalidBindings
= sanitizeVirtualKeyBindings();
502 /* Create a hidden application shell that is the parent of all the
503 main editor windows. Realize it so it the window can act as
505 TheAppShell
= CreateShellWithBestVis(APP_NAME
,
507 applicationShellWidgetClass
,
512 /* Restore the original bindings ASAP such that other apps are not affected. */
513 restoreInsaneVirtualKeyBindings(invalidBindings
);
515 XtSetMappedWhenManaged(TheAppShell
, False
);
516 XtRealizeWidget(TheAppShell
);
518 #ifndef NO_SESSION_RESTART
519 AttachSessionMgrHandler(TheAppShell
);
522 /* More preference stuff */
523 LoadPrintPreferences(XtDatabase(TheDisplay
), APP_NAME
, APP_CLASS
, True
);
524 SetDeleteRemap(GetPrefMapDelete());
525 SetPointerCenteredDialogs(GetPrefRepositionDialogs());
526 SetGetEFTextFieldRemoval(!GetPrefStdOpenDialog());
528 /* Set up action procedures for menu item commands */
529 InstallMenuActions(context
);
531 /* Add Actions for following hyperlinks in the help window */
532 InstallHelpLinkActions(context
);
533 /* Add actions for mouse wheel support in scrolled windows (except text
535 InstallMouseWheelActions(context
);
537 /* Install word delimiters for regular expression matching */
538 SetREDefaultWordDelimiters(GetPrefDelimiters());
540 /* Read the nedit dynamic database of files for the Open Previous
541 command (and eventually other information as well) */
544 /* Process -import command line argument before others which might
545 open windows (loading preferences doesn't update menu settings,
546 which would then be out of sync with the real preference settings) */
547 for (i
=1; i
<argc
; i
++) {
548 if(!strcmp(argv
[i
], "--")) {
549 break; /* treat all remaining arguments as filenames */
550 } else if (!strcmp(argv
[i
], "-import")) {
551 nextArg(argc
, argv
, &i
);
552 ImportPrefFile(argv
[i
], False
);
553 } else if (!strcmp(argv
[i
], "-importold")) {
554 nextArg(argc
, argv
, &i
);
555 ImportPrefFile(argv
[i
], True
);
559 /* Load the default tags file. Don't complain if it doesn't load, the tag
560 file resource is intended to be set and forgotten. Running nedit in a
561 directory without a tags should not cause it to spew out errors. */
562 if (*GetPrefTagFile() != '\0') {
563 AddTagsFile(GetPrefTagFile(), TAG
);
566 if (strcmp(GetPrefServerName(), "") != 0) {
570 /* Process any command line arguments (-tags, -do, -read, -create,
571 +<line_number>, -line, -server, and files to edit) not already
572 processed by RestoreNEditPrefs. */
573 fileSpecified
= FALSE
;
574 for (i
=1; i
<argc
; i
++) {
575 if (opts
&& !strcmp(argv
[i
], "--")) {
576 opts
= False
; /* treat all remaining arguments as filenames */
578 } else if (opts
&& !strcmp(argv
[i
], "-tags")) {
579 nextArg(argc
, argv
, &i
);
580 if (!AddTagsFile(argv
[i
], TAG
))
581 fprintf(stderr
, "NEdit: Unable to load tags file\n");
582 } else if (opts
&& !strcmp(argv
[i
], "-do")) {
583 nextArg(argc
, argv
, &i
);
584 if (checkDoMacroArg(argv
[i
]))
585 toDoCommand
= argv
[i
];
586 } else if (opts
&& !strcmp(argv
[i
], "-read")) {
587 editFlags
|= PREF_READ_ONLY
;
588 } else if (opts
&& !strcmp(argv
[i
], "-create")) {
589 editFlags
|= SUPPRESS_CREATE_WARN
;
590 } else if (opts
&& !strcmp(argv
[i
], "-tabbed")) {
592 group
= 0; /* override -group option */
593 } else if (opts
&& !strcmp(argv
[i
], "-untabbed")) {
595 group
= 0; /* override -group option */
596 } else if (opts
&& !strcmp(argv
[i
], "-group")) {
597 group
= 2; /* 2: start new group, 1: in group */
598 } else if (opts
&& !strcmp(argv
[i
], "-line")) {
599 nextArg(argc
, argv
, &i
);
600 nRead
= sscanf(argv
[i
], "%d", &lineNum
);
602 fprintf(stderr
, "NEdit: argument to line should be a number\n");
605 } else if (opts
&& (*argv
[i
] == '+')) {
606 nRead
= sscanf((argv
[i
]+1), "%d", &lineNum
);
608 fprintf(stderr
, "NEdit: argument to + should be a number\n");
611 } else if (opts
&& !strcmp(argv
[i
], "-server")) {
613 } else if (opts
&& !strcmp(argv
[i
], "-xwarn")) {
614 XtAppSetWarningHandler(context
, showWarningFilter
);
615 } else if (opts
&& (!strcmp(argv
[i
], "-iconic") ||
616 !strcmp(argv
[i
], "-icon"))) {
618 } else if (opts
&& !strcmp(argv
[i
], "-noiconic")) {
620 } else if (opts
&& (!strcmp(argv
[i
], "-geometry") ||
621 !strcmp(argv
[i
], "-g"))) {
622 nextArg(argc
, argv
, &i
);
624 } else if (opts
&& !strcmp(argv
[i
], "-lm")) {
625 nextArg(argc
, argv
, &i
);
627 } else if (opts
&& !strcmp(argv
[i
], "-import")) {
628 nextArg(argc
, argv
, &i
); /* already processed, skip */
629 } else if (opts
&& (!strcmp(argv
[i
], "-V") ||
630 !strcmp(argv
[i
], "-version"))) {
633 } else if (opts
&& (!strcmp(argv
[i
], "-h") ||
634 !strcmp(argv
[i
], "-help"))) {
635 fprintf(stderr
, "%s", cmdLineHelp
);
637 } else if (opts
&& (*argv
[i
] == '-')) {
641 fprintf(stderr
, "nedit: Unrecognized option %s\n%s", argv
[i
],
647 char **nameList
= NULL
;
648 /* Use VMS's LIB$FILESCAN for filename in argv[i] to process */
649 /* wildcards and to obtain a full VMS file specification */
650 numFiles
= VMSFileScan(argv
[i
], &nameList
, NULL
, INCLUDE_FNF
);
651 /* for each expanded file name do: */
652 for (j
= 0; j
< numFiles
; ++j
) {
653 if (ParseFilename(nameList
[j
], filename
, pathname
) == 0) {
654 /* determine if file is to be openned in new tab, by
655 factoring the options -group, -tabbed & -untabbed */
657 isTabbed
= 0; /* start a new window for new group */
658 group
= 1; /* next file will be within group */
659 } else if (group
== 1) {
660 isTabbed
= 1; /* new tab for file in group */
661 } else { /* not in group */
662 isTabbed
= tabbed
==-1? GetPrefOpenInTab() : tabbed
;
665 /* Files are opened in background to improve opening speed
666 by defering certain time consuiming task such as syntax
667 highlighting. At the end of the file-opening loop, the
668 last file opened will be raised to restore those deferred
669 items. The current file may also be raised if there're
670 macros to execute on. */
671 window
= EditExistingFile(WindowList
, filename
, pathname
,
672 editFlags
, geometry
, iconic
, langMode
, isTabbed
,
674 fileSpecified
= TRUE
;
677 CleanUpTabBarExposeQueue(window
);
679 /* raise the last file of previous window */
680 if (lastFile
&& window
->shell
!= lastFile
->shell
) {
681 CleanUpTabBarExposeQueue(lastFile
);
682 RaiseDocument(lastFile
);
685 if (!macroFileRead
) {
686 ReadMacroInitFile(WindowList
);
687 macroFileRead
= True
;
690 SelectNumberedLine(window
, lineNum
);
691 if (toDoCommand
!= NULL
) {
692 DoMacro(window
, toDoCommand
, "-do macro");
694 if (!IsValidWindow(window
))
695 window
= NULL
; /* window closed by macro */
696 if (lastFile
&& !IsValidWindow(lastFile
))
697 lastFile
= NULL
; /* window closed by macro */
701 /* register last opened file for later use */
705 fprintf(stderr
, "nedit: file name too long: %s\n", nameList
[j
]);
709 if (nameList
!= NULL
)
712 if (ParseFilename(argv
[i
], filename
, pathname
) == 0 ) {
713 /* determine if file is to be openned in new tab, by
714 factoring the options -group, -tabbed & -untabbed */
716 isTabbed
= 0; /* start a new window for new group */
717 group
= 1; /* next file will be within group */
718 } else if (group
== 1) {
719 isTabbed
= 1; /* new tab for file in group */
720 } else { /* not in group */
721 isTabbed
= tabbed
==-1? GetPrefOpenInTab() : tabbed
;
724 /* Files are opened in background to improve opening speed
725 by defering certain time consuiming task such as syntax
726 highlighting. At the end of the file-opening loop, the
727 last file opened will be raised to restore those deferred
728 items. The current file may also be raised if there're
729 macros to execute on. */
730 window
= EditExistingFile(WindowList
, filename
, pathname
,
731 editFlags
, geometry
, iconic
, langMode
, isTabbed
, True
);
732 fileSpecified
= TRUE
;
734 CleanUpTabBarExposeQueue(window
);
736 /* raise the last tab of previous window */
737 if (lastFile
&& window
->shell
!= lastFile
->shell
) {
738 CleanUpTabBarExposeQueue(lastFile
);
739 RaiseDocument(lastFile
);
742 if (!macroFileRead
) {
743 ReadMacroInitFile(WindowList
);
744 macroFileRead
= True
;
747 SelectNumberedLine(window
, lineNum
);
748 if (toDoCommand
!= NULL
) {
749 DoMacro(window
, toDoCommand
, "-do macro");
751 if (!IsValidWindow(window
))
752 window
= NULL
; /* window closed by macro */
753 if (lastFile
&& !IsValidWindow(lastFile
))
754 lastFile
= NULL
; /* window closed by macro */
758 /* register last opened file for later use */
762 fprintf(stderr
, "nedit: file name too long: %s\n", argv
[i
]);
771 /* Raise the last file opened */
773 CleanUpTabBarExposeQueue(lastFile
);
774 RaiseDocument(lastFile
);
778 /* If no file to edit was specified, open a window to edit "Untitled" */
779 if (!fileSpecified
) {
780 EditNewFile(NULL
, geometry
, iconic
, langMode
, NULL
);
781 ReadMacroInitFile(WindowList
);
783 if (toDoCommand
!= NULL
)
784 DoMacro(WindowList
, toDoCommand
, "-do macro");
787 /* Begin remembering last command invoked for "Repeat" menu item */
788 AddLastCommandActionHook(context
);
790 /* Set up communication port and write ~/.nedit_server_process file */
792 InitServerCommunication();
794 /* Process events. */
796 ServerMainLoop(context
);
798 XtAppMainLoop(context
);
800 /* Not reached but this keeps some picky compilers happy */
804 static void nextArg(int argc
, char **argv
, int *argIndex
)
806 if (*argIndex
+ 1 >= argc
) {
808 *argv
[*argIndex
] = '/';
810 fprintf(stderr
, "NEdit: %s requires an argument\n%s", argv
[*argIndex
],
818 ** Return True if -do macro is valid, otherwise write an error on stderr
820 static int checkDoMacroArg(const char *macro
)
823 char *errMsg
, *stoppedAt
, *tMacro
;
826 /* Add a terminating newline (which command line users are likely to omit
827 since they are typically invoking a single routine) */
828 macroLen
= strlen(macro
);
829 tMacro
= XtMalloc(strlen(macro
)+2);
830 strncpy(tMacro
, macro
, macroLen
);
831 tMacro
[macroLen
] = '\n';
832 tMacro
[macroLen
+1] = '\0';
834 /* Do a test parse */
835 prog
= ParseMacro(tMacro
, &errMsg
, &stoppedAt
);
838 ParseError(NULL
, tMacro
, stoppedAt
, "argument to -do", errMsg
);
846 ** maskArgvKeywords and unmaskArgvKeywords mangle selected keywords by
847 ** replacing the '-' with a space, for the purpose of hiding them from
848 ** XtOpenDisplay's option processing. Why this silly scheme? XtOpenDisplay
849 ** really needs to see command line arguments, particularly -display, but
850 ** there's no way to change the option processing table it uses, to keep
851 ** it from consuming arguments which are meant to apply per-window, like
852 ** -geometry and -iconic.
854 static void maskArgvKeywords(int argc
, char **argv
, const char **maskArgs
)
858 for (i
=1; i
<argc
; i
++)
859 for (k
=0; maskArgs
[k
]!=NULL
; k
++)
860 if (!strcmp(argv
[i
], maskArgs
[k
]))
865 static void unmaskArgvKeywords(int argc
, char **argv
, const char **maskArgs
)
869 for (i
=1; i
<argc
; i
++)
870 for (k
=0; maskArgs
[k
]!=NULL
; k
++)
871 if (argv
[i
][0]==' ' && !strcmp(&argv
[i
][1], &maskArgs
[k
][1]))
877 ** Some Linux distros ship with XKEYSYMDB set to a bogus filename which
878 ** breaks all Motif applications. Ignore that, and let X fall back on the
879 ** default which is far more likely to work.
881 static void fixupBrokenXKeysymDB(void)
883 const char *keysym
= getenv("XKEYSYMDB");
885 if (keysym
!= NULL
&& access(keysym
, F_OK
) != 0)
890 ** If we're not using the default visual, then some default resources in
891 ** the database (colors) are not valid, because they are indexes into the
892 ** default colormap. If we used them blindly, then we'd get "random"
893 ** unreadable colors. So we inspect the resource list, and use the
894 ** fallback "grey" color instead if this is the case.
896 static void patchResourcesForVisual(void)
902 Boolean usingDefaultVisual
;
908 db
= XtDatabase(TheDisplay
);
910 usingDefaultVisual
= FindBestVisual(TheDisplay
,
917 if (!usingDefaultVisual
)
919 #ifndef LESSTIF_VERSION
921 For non-Lesstif versions, we have to put non-default visuals etc.
922 in the resource data base to make sure that all (shell) widgets
923 inherit them, especially Motif's own shells (eg, drag icons).
925 For Lesstif, this doesn't work, but luckily, Lesstif handles
926 non-default visuals etc. properly for its own shells and
927 we can take care of things for our shells (eg, call tips) through
928 our shell creation wrappers in misc.c.
932 value
.addr
= (XPointer
)&visual
;
933 value
.size
= sizeof(visual
);
934 XrmPutResource(&db
, "*visual", "Visual", &value
);
935 value
.addr
= (XPointer
)&map
;
936 value
.size
= sizeof(map
);
937 XrmPutResource(&db
, "*colormap", "Colormap", &value
);
938 value
.addr
= (XPointer
)&depth
;
939 value
.size
= sizeof(depth
);
940 XrmPutResource(&db
, "*depth", "Int", &value
);
942 /* Drag-and-drop visuals do not work well when using a different
943 visual. One some systems, you'll just get a funny-looking icon
944 (maybe all-black) but on other systems it crashes with a BadMatch
945 error. This appears to be an OSF Motif bug. It would be nicer
946 to just disable the visual itself, instead of the entire drag
949 Update: this is no longer necessary since all problems with
950 non-default visuals should now be solved.
952 XrmPutStringResource(&db, "*dragInitiatorProtocolStyle", "DRAG_NONE");
956 for (i
= 1; i
< XtNumber(fallbackResources
); ++i
)
958 Cardinal resIndex
= i
- 1;
960 if (strstr(fallbackResources
[resIndex
], "*background:") ||
961 strstr(fallbackResources
[resIndex
], "*foreground:"))
963 /* Qualify by application name to prevent them from being
964 converted against the wrong colormap. */
965 char buf
[1024] = "*" APP_NAME
;
966 strcat(buf
, fallbackResources
[resIndex
]);
967 XrmPutLineResource(&db
, buf
);
974 ** Several KDE version (2.x and 3.x) ship with a template application-default
975 ** file for NEdit in which several strings have to be substituted in order to
976 ** make it a valid .ad file. However, for some reason (a KDE bug?), the
977 ** template sometimes ends up in the resource db unmodified, such that several
978 ** invalid entries are present. This function checks for the presence of such
979 ** invalid entries and silently replaces them with NEdit's default values where
980 ** necessary. Without this, NEdit will typically write several warnings to
981 ** the terminal (Cannot convert string "FONTLIST" to type FontStruct etc) and
982 ** fall back on some really ugly colors and fonts.
984 static void patchResourcesForKDEbug(void)
987 * These are the resources found the Nedit.ad template shipped with KDE 3.0.
989 static const char* buggyResources
[][3] = {
990 { "*fontList", "FONTLIST", NEDIT_DEFAULT_FONT
},
991 { "*XmText.background", "BACKGROUND", NEDIT_DEFAULT_TEXT_BG
},
992 { "*XmText.foreground", "FOREGROUND", NEDIT_DEFAULT_FG
},
993 { "*XmTextField.background", "BACKGROUND", NEDIT_DEFAULT_TEXT_BG
},
994 { "*XmTextField.foreground", "FOREGROUND", NEDIT_DEFAULT_FG
},
995 { "*XmList.background", "BACKGROUND", NEDIT_DEFAULT_TEXT_BG
},
996 { "*XmList.foreground", "FOREGROUND", NEDIT_DEFAULT_FG
},
997 { "*helpText.background", "BACKGROUND", NEDIT_DEFAULT_HELP_BG
},
998 { "*helpText.foreground", "FOREGROUND", NEDIT_DEFAULT_HELP_FG
},
999 { "*background", "BACKGROUND", NEDIT_DEFAULT_BG
},
1000 { "*foreground", "FOREGROUND", NEDIT_DEFAULT_FG
, },
1001 { "*selectColor", "BACKGROUND", NEDIT_DEFAULT_SEL_BG
},
1002 { "*highlightColor", "BACKGROUND", NEDIT_DEFAULT_HI_BG
},
1003 { "*text.background", "WINDOW_BACKGROUND", NEDIT_DEFAULT_TEXT_BG
},
1004 { "*text.foreground", "WINDOW_FOREGROUND", NEDIT_DEFAULT_FG
},
1005 { "*text.selectBackground", "SELECT_BACKGROUND", NEDIT_DEFAULT_SEL_BG
},
1006 { "*text.selectForeground", "SELECT_FOREGROUND", NEDIT_DEFAULT_SEL_FG
},
1007 { "*text.cursorForeground", "WINDOW_FOREGROUND", NEDIT_DEFAULT_CURSOR_FG
},
1008 /* { "*remapDeleteKey", "False", }, OK */
1009 /* { "!*text.heavyCursor", "True" }, OK */
1010 /* { "!*BlinkRate", "0" }, OK */
1011 /* { "*shell", "/bin/sh" }, OK */
1012 { "*statsLine.background", "BACKGROUND", NEDIT_DEFAULT_BG
},
1013 { "*statsLine.foreground", "FOREGROUND", NEDIT_DEFAULT_FG
},
1014 { NULL
, NULL
, NULL
} };
1021 db
= XtDatabase(TheDisplay
);
1024 while (buggyResources
[i
][0])
1026 const char* resource
= buggyResources
[i
][0];
1027 const char* buggyValue
= buggyResources
[i
][1];
1028 const char* defaultValue
= buggyResources
[i
][2];
1029 char name
[128] = APP_NAME
;
1030 char class[128] = APP_CLASS
;
1034 strcat(name
, resource
);
1035 strcat(class, resource
); /* Is this ok ? */
1037 if (XrmGetResource(db
, name
, class, &type
, &resValue
) &&
1038 !strcmp(type
, XmRString
))
1040 /* Buggy value? Replace by the default. */
1041 if (!strcmp(resValue
.addr
, buggyValue
))
1043 XrmPutStringResource(&db
, &name
[0], (char*)defaultValue
);
1051 ** It seems OSF Motif cannot handle locales with UTF-8 at the end, crashing
1052 ** in various places. The easiest one to find is to open the File Open
1053 ** dialog box. So we lop off UTF-8 if it's there and continue. Newer
1054 ** versions of Linux distros (e.g., RedHat 8) set the default language to
1055 ** to have "UTF-8" at the end, so users were seeing these crashes.
1057 ** LessTif and OpenMotif 2.3 don't appear to have this problem.
1059 ** Some versions OpenMotif 2.2.3 don't have this problem, but there's
1060 ** no way to tell which.
1063 static void patchLocaleForMotif(void)
1065 #if !(defined(LESSTIF_VERSION) || XmVersion >= 2003)
1067 char ctypebuf
[1024];
1070 /* We have to check LC_CTYPE specifically here, because the system
1071 might specify different locales for different categories (why
1072 anyone would want to do this is beyond me). As far as I can
1073 tell, only LC_CTYPE causes OSF Motif to crash. If it turns out
1074 others do, we'll have to iterate over a list of locale cateogries
1075 and patch every one of them. */
1077 ctype
= setlocale(LC_CTYPE
, NULL
);
1082 strncpy(ctypebuf
, ctype
, sizeof ctypebuf
);
1084 if ((utf_start
= strstr(ctypebuf
, ".utf8")) ||
1085 (utf_start
= strstr(ctypebuf
, ".UTF-8")))
1087 *utf_start
= '\0'; /* Samurai chop */
1088 setlocale(LC_CTYPE
, ctypebuf
);
1095 ** Same as the default X language procedure, except we check if Motif can
1096 ** handle the locale as well.
1099 static String
neditLanguageProc(Display
*dpy
, String xnl
, XtPointer closure
)
1101 /* "xnl" will be set if the user passes in a new language via the
1102 "-xnllanguage" flag. If it's empty, then setlocale will get
1103 the default locale by some system-dependent means (usually,
1104 reading some environment variables). */
1106 if (! setlocale(LC_ALL
, xnl
)) {
1107 XtWarning("locale not supported by C library, locale unchanged");
1110 patchLocaleForMotif();
1112 if (! XSupportsLocale()) {
1113 XtWarning("locale not supported by Xlib, locale set to C");
1114 setlocale(LC_ALL
, "C");
1116 if (! XSetLocaleModifiers(""))
1117 XtWarning("X locale modifiers not supported, using default");
1119 return setlocale(LC_ALL
, NULL
); /* re-query in case overwritten */
1122 static int sortAlphabetical(const void* k1
, const void* k2
)
1124 const char* key1
= *(const char**)k1
;
1125 const char* key2
= *(const char**)k2
;
1126 return strcmp(key1
, key2
);
1130 * Checks whether a given virtual key binding string is invalid.
1131 * A binding is considered invalid if there are duplicate key entries.
1133 static int virtKeyBindingsAreInvalid(const unsigned char* bindings
)
1135 int maxCount
= 1, i
, count
;
1136 const char *pos
= (const char*)bindings
;
1141 /* First count the number of bindings; bindings are separated by \n
1142 strings. The number of bindings equals the number of \n + 1.
1143 Beware of leading and trailing \n; the number is actually an
1144 upper bound on the number of entries. */
1145 while ((pos
= strstr(pos
, "\n")))
1152 return False
; /* One binding is always ok */
1154 keys
= (char**)malloc(maxCount
*sizeof(char*));
1155 copy
= XtNewString((const char*)bindings
);
1160 while (i
<maxCount
&& pos2
&& *pos2
)
1162 while (isspace((int) *pos2
) || *pos2
== '\n') ++pos2
;
1164 if (*pos2
== '!') /* Ignore comment lines */
1166 pos2
= strstr(pos2
, "\n");
1167 continue; /* Go to the next line */
1174 pos3
= strstr(pos2
, ":");
1177 *pos3
++ = 0; /* Cut the string and jump to the next entry */
1180 pos2
= strstr(pos2
, "\n");
1188 return False
; /* No conflict */
1191 /* Sort the keys and look for duplicates */
1192 qsort((void*)keys
, count
, sizeof(const char*), sortAlphabetical
);
1193 for (i
=1; i
<count
; ++i
)
1195 if (!strcmp(keys
[i
-1], keys
[i
]))
1197 /* Duplicate detected */
1209 * Optionally sanitizes the Motif default virtual key bindings.
1210 * Some applications install invalid bindings (attached to the root window),
1211 * which cause certain keys to malfunction in NEdit.
1212 * Through an X-resource, users can choose whether they want
1213 * - to always keep the existing bindings
1214 * - to override the bindings only if they are invalid
1215 * - to always override the existing bindings.
1218 static Atom virtKeyAtom
;
1220 static unsigned char* sanitizeVirtualKeyBindings(void)
1222 int overrideBindings
= GetPrefOverrideVirtKeyBindings();
1224 const char *virtKeyPropName
= "_MOTIF_DEFAULT_BINDINGS";
1227 unsigned long dummyULong
, nItems
;
1228 unsigned char *insaneVirtKeyBindings
= NULL
;
1230 if (overrideBindings
== VIRT_KEY_OVERRIDE_NEVER
) return NULL
;
1232 virtKeyAtom
= XInternAtom(TheDisplay
, virtKeyPropName
, False
);
1233 rootWindow
= RootWindow(TheDisplay
, DefaultScreen(TheDisplay
));
1235 /* Remove the property, if it exists; we'll restore it later again */
1236 if (XGetWindowProperty(TheDisplay
, rootWindow
, virtKeyAtom
, 0, INT_MAX
,
1237 True
, XA_STRING
, &dummyAtom
, &getFmt
, &nItems
,
1238 &dummyULong
, &insaneVirtKeyBindings
) != Success
1241 return NULL
; /* No binding yet; nothing to do */
1244 if (overrideBindings
== VIRT_KEY_OVERRIDE_AUTO
)
1246 if (!virtKeyBindingsAreInvalid(insaneVirtKeyBindings
))
1248 /* Restore the property immediately; it seems valid */
1249 XChangeProperty(TheDisplay
, rootWindow
, virtKeyAtom
, XA_STRING
, 8,
1250 PropModeReplace
, insaneVirtKeyBindings
,
1251 strlen((const char*)insaneVirtKeyBindings
));
1252 XFree((char*)insaneVirtKeyBindings
);
1253 return NULL
; /* Prevent restoration */
1256 return insaneVirtKeyBindings
;
1260 * NEdit should not mess with the bindings installed by other apps, so we
1261 * just restore whatever was installed, if necessary
1263 static void restoreInsaneVirtualKeyBindings(unsigned char *insaneVirtKeyBindings
)
1265 if (insaneVirtKeyBindings
)
1267 Window rootWindow
= RootWindow(TheDisplay
, DefaultScreen(TheDisplay
));
1268 /* Restore the root window atom, such that we don't affect
1270 XChangeProperty(TheDisplay
, rootWindow
, virtKeyAtom
, XA_STRING
, 8,
1271 PropModeReplace
, insaneVirtKeyBindings
,
1272 strlen((const char*)insaneVirtKeyBindings
));
1273 XFree((char*)insaneVirtKeyBindings
);
1278 ** Warning handler that suppresses harmless but annoying warnings generated
1279 ** by non-production Lesstif versions.
1281 static void showWarningFilter(String message
)
1283 const char* bogusMessages
[] = {
1284 #ifdef LESSTIF_VERSION
1285 "XmFontListCreate() is an obsolete function!",
1286 "No type converter registered for 'String' to 'PathMode' conversion.",
1287 "XtRemoveGrab asked to remove a widget not on the list",
1291 const char **bogusMessage
= &bogusMessages
[0];
1293 while (*bogusMessage
) {
1294 size_t bogusLen
= strlen(*bogusMessage
);
1295 if (strncmp(message
, *bogusMessage
, bogusLen
) == 0) {
1296 #ifdef DEBUG_LESSTIF_WARNINGS
1297 /* Developers may want to see which messages are suppressed. */
1298 fprintf(stderr
, "[SUPPRESSED] %s\n", message
);
1305 /* An unknown message. Keep it. */
1306 fprintf(stderr
, "%s\n", message
);
1309 static void noWarningFilter(String message
)