Xft support under OpenMotif 2.3.3 - I've been using this for quite a while on
[nedit.git] / source / nedit.c
blobe825e326b435b4ea4421f100968043b94ac1cae4
1 static const char CVSID[] = "$Id: nedit.c,v 1.101 2012/10/25 14:10:25 tringali Exp $";
2 /*******************************************************************************
3 * *
4 * nedit.c -- Nirvana Editor main program *
5 * *
6 * Copyright (C) 1999 Mark Edel *
7 * *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. In addition, you may distribute versions of this program linked to *
12 * Motif or Open Motif. See README for details. *
13 * *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
17 * for more details. *
18 * *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
22 * *
23 * Nirvana Text Editor *
24 * May 10, 1991 *
25 * *
26 * Written by Mark Edel *
27 * *
28 * Modifications: *
29 * *
30 * 8/18/93 - Mark Edel & Joy Kyriakopulos - Ported to VMS *
31 * *
32 *******************************************************************************/
34 #ifdef HAVE_CONFIG_H
35 #include "../config.h"
36 #endif
38 #include "nedit.h"
39 /* #include "textBuf.h" */
40 #include "file.h"
41 #include "preferences.h"
42 #include "regularExp.h"
43 #include "selection.h"
44 #include "tags.h"
45 #include "menu.h"
46 #include "macro.h"
47 #include "server.h"
48 #include "interpret.h"
49 #include "parse.h"
50 #include "help.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"
57 #include <ctype.h>
58 #include <limits.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
64 #ifndef NO_XMIM
65 #include <X11/Xlocale.h>
66 #else
67 #include <locale.h>
68 #endif
70 #include <X11/Intrinsic.h>
71 #include <Xm/Xm.h>
72 #include <Xm/XmP.h>
74 #if XmVersion >= 1002
75 #include <Xm/RepType.h>
76 #endif
78 #ifdef VMS
79 #include <rmsdef.h>
80 #include "../util/VMSparam.h"
81 #include "../util/VMSUtils.h"
82 #else
84 #ifndef __MVS__
85 #include <sys/param.h>
86 #endif
87 #endif /*VMS*/
89 #ifdef HAVE_DEBUG_H
90 #include "../debug.h"
91 #endif
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;
111 char *ArgV0 = NULL;
112 Boolean IsServer = False;
113 Widget TheAppShell;
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.
157 If using OpenMotif 2.3.3 or better, support XFT fonts. XFT is
158 claimed supported in OpenMotif 2.3.0, but doesn't work very well,
159 as insensitive text buttons are blank. That bug is fixed in 2.3.3.
162 #if (XmVersion >= 2003 && XmUPDATE_LEVEL >= 3 && USE_XFT == 1)
163 "*buttonRenderTable: defaultRT",
164 "*labelRenderTable: defaultRT",
165 "*textRenderTable: fixedRT",
166 "*defaultRT.fontType: FONT_IS_XFT",
167 "*defaultRT.fontName: Sans",
168 "*defaultRT.fontSize: 9",
169 "*fixedRT.fontType: FONT_IS_XFT",
170 "*fixedRT.fontName: Monospace",
171 "*fixedRT.fontSize: 9",
172 #elif LESSTIF_VERSION
173 "*FontList: " NEDIT_DEFAULT_FONT,
174 "*XmText.FontList: " NEDIT_FIXED_FONT,
175 "*XmTextField.FontList: " NEDIT_FIXED_FONT,
176 "*XmList.FontList: " NEDIT_FIXED_FONT,
177 "*XmFileSelectionBox*XmList.FontList: " NEDIT_FIXED_FONT,
178 #else
179 "*buttonFontList: " NEDIT_DEFAULT_FONT,
180 "*labelFontList: " NEDIT_DEFAULT_FONT,
181 "*textFontList: " NEDIT_FIXED_FONT,
182 #endif
184 "*background: " NEDIT_DEFAULT_BG,
185 "*foreground: " NEDIT_DEFAULT_FG,
186 "*XmText.foreground: " NEDIT_DEFAULT_FG,
187 "*XmText.background: " NEDIT_DEFAULT_TEXT_BG,
188 "*XmList.foreground: " NEDIT_DEFAULT_FG,
189 "*XmList.background: " NEDIT_DEFAULT_TEXT_BG,
190 "*XmTextField.foreground: " NEDIT_DEFAULT_FG,
191 "*XmTextField.background: " NEDIT_DEFAULT_TEXT_BG,
193 /* Use baseTranslations as per Xt Programmer's Manual, 10.2.12 */
194 "*XmText.baseTranslations: " NEDIT_TEXT_TRANSLATIONS,
195 "*XmTextField.baseTranslations: " NEDIT_TEXT_TRANSLATIONS,
197 "*XmLFolder.highlightThickness: 0",
198 "*XmLFolder.shadowThickness: 1",
199 "*XmLFolder.maxTabWidth: 150",
200 "*XmLFolder.traversalOn: False",
201 "*XmLFolder.inactiveForeground: #666" ,
202 "*tab.alignment: XmALIGNMENT_BEGINNING",
203 "*tab.marginWidth: 0",
204 "*tab.marginHeight: 1",
206 /* Prevent the file selection box from acting stupid. */
207 "*XmFileSelectionBox.resizePolicy: XmRESIZE_NONE",
208 "*XmFileSelectionBox.textAccelerators:",
209 "*XmFileSelectionBox.pathMode: XmPATH_MODE_RELATIVE",
210 "*XmFileSelectionBox.width: 500",
211 "*XmFileSelectionBox.height: 400",
213 /* NEdit-specific widgets. Theses things should probably be manually
214 jammed into the database, rather than fallbacks. We really want
215 the accelerators to be there even if someone creates an app-defaults
216 file against our wishes. */
218 "*text.lineNumForeground: " NEDIT_DEFAULT_LINENO_FG,
219 "*text.background: " NEDIT_DEFAULT_TEXT_BG,
220 "*text.foreground: " NEDIT_DEFAULT_FG,
221 "*text.highlightForeground: " NEDIT_DEFAULT_HI_FG,
222 "*text.highlightBackground: " NEDIT_DEFAULT_HI_BG,
223 "*textFrame.shadowThickness: 1",
224 "*menuBar.marginHeight: 0",
225 "*menuBar.shadowThickness: 1",
226 "*pane.sashHeight: 11",
227 "*pane.sashWidth: 11",
228 "*pane.marginWidth: 0",
229 "*pane.marginHeight: 0",
230 "*scrolledW*spacing: 0",
231 "*text.selectionArrayCount: 3",
232 "*helpText.background: " NEDIT_DEFAULT_HELP_BG,
233 "*helpText.foreground: " NEDIT_DEFAULT_HELP_FG,
234 "*helpText.selectBackground: " NEDIT_DEFAULT_BG,
235 "*statsLine.background: " NEDIT_DEFAULT_BG,
236 "*statsLine.FontList: " NEDIT_DEFAULT_FONT,
237 "*calltip.background: LemonChiffon1",
238 "*calltip.foreground: black",
239 "*iSearchForm*highlightThickness: 1",
240 "*fileMenu.tearOffModel: XmTEAR_OFF_ENABLED",
241 "*editMenu.tearOffModel: XmTEAR_OFF_ENABLED",
242 "*searchMenu.tearOffModel: XmTEAR_OFF_ENABLED",
243 "*preferencesMenu.tearOffModel: XmTEAR_OFF_ENABLED",
244 "*windowsMenu.tearOffModel: XmTEAR_OFF_ENABLED",
245 "*shellMenu.tearOffModel: XmTEAR_OFF_ENABLED",
246 "*macroMenu.tearOffModel: XmTEAR_OFF_ENABLED",
247 "*helpMenu.tearOffModel: XmTEAR_OFF_ENABLED",
248 "*fileMenu.mnemonic: F",
249 "*fileMenu.new.accelerator: Ctrl<Key>n",
250 "*fileMenu.new.acceleratorText: Ctrl+N",
251 "*fileMenu.newOpposite.accelerator: Shift Ctrl<Key>n",
252 "*fileMenu.newOpposite.acceleratorText: Shift+Ctrl+N",
253 "*fileMenu.open.accelerator: Ctrl<Key>o",
254 "*fileMenu.open.acceleratorText: Ctrl+O",
255 "*fileMenu.openSelected.accelerator: Ctrl<Key>y",
256 "*fileMenu.openSelected.acceleratorText: Ctrl+Y",
257 "*fileMenu.close.accelerator: Ctrl<Key>w",
258 "*fileMenu.close.acceleratorText: Ctrl+W",
259 "*fileMenu.save.accelerator: Ctrl<Key>s",
260 "*fileMenu.save.acceleratorText: Ctrl+S",
261 "*fileMenu.includeFile.accelerator: Alt<Key>i",
262 "*fileMenu.includeFile.acceleratorText: Alt+I",
263 "*fileMenu.print.accelerator: Ctrl<Key>p",
264 "*fileMenu.print.acceleratorText: Ctrl+P",
265 "*fileMenu.exit.accelerator: Ctrl<Key>q",
266 "*fileMenu.exit.acceleratorText: Ctrl+Q",
267 "*editMenu.mnemonic: E",
268 "*editMenu.undo.accelerator: Ctrl<Key>z",
269 "*editMenu.undo.acceleratorText: Ctrl+Z",
270 "*editMenu.redo.accelerator: Shift Ctrl<Key>z",
271 "*editMenu.redo.acceleratorText: Shift+Ctrl+Z",
272 /* Clipboard accelerators prevent the use of the clipboard in iSearch's
273 XmText, so they are left out. Their job is done by translations in
274 the main text widget, so the acceleratorText is still kept. */
275 "*editMenu.cut.acceleratorText: Ctrl+X",
276 "*editMenu.copy.acceleratorText: Ctrl+C",
277 "*editMenu.paste.acceleratorText: Ctrl+V",
278 "*editMenu.pasteColumn.accelerator: Shift Ctrl<Key>v",
279 "*editMenu.pasteColumn.acceleratorText: Ctrl+Shift+V",
280 "*editMenu.delete.acceleratorText: Del",
281 "*editMenu.selectAll.accelerator: Ctrl<Key>a",
282 "*editMenu.selectAll.acceleratorText: Ctrl+A",
283 "*editMenu.shiftLeft.accelerator: Ctrl<Key>9",
284 "*editMenu.shiftLeft.acceleratorText: [Shift]Ctrl+9",
285 "*editMenu.shiftLeftShift.accelerator: Shift Ctrl<Key>9",
286 "*editMenu.shiftRight.accelerator: Ctrl<Key>0",
287 "*editMenu.shiftRight.acceleratorText: [Shift]Ctrl+0",
288 "*editMenu.shiftRightShift.accelerator: Shift Ctrl<Key>0",
289 "*editMenu.upperCase.accelerator: Ctrl<Key>6",
290 "*editMenu.upperCase.acceleratorText: Ctrl+6",
291 "*editMenu.lowerCase.accelerator: Shift Ctrl<Key>6",
292 "*editMenu.lowerCase.acceleratorText: Shift+Ctrl+6",
293 "*editMenu.fillParagraph.accelerator: Ctrl<Key>j",
294 "*editMenu.fillParagraph.acceleratorText: Ctrl+J",
295 "*editMenu.insertFormFeed.accelerator: Alt Ctrl<Key>l",
296 "*editMenu.insertFormFeed.acceleratorText: Alt+Ctrl+L",
297 "*editMenu.insertCtrlCode.accelerator: Alt Ctrl<Key>i",
298 "*editMenu.insertCtrlCode.acceleratorText: Alt+Ctrl+I",
299 "*searchMenu.mnemonic: S",
300 "*searchMenu.find.accelerator: Ctrl<Key>f",
301 "*searchMenu.find.acceleratorText: [Shift]Ctrl+F",
302 "*searchMenu.findShift.accelerator: Shift Ctrl<Key>f",
303 "*searchMenu.findAgain.accelerator: Ctrl<Key>g",
304 "*searchMenu.findAgain.acceleratorText: [Shift]Ctrl+G",
305 "*searchMenu.findAgainShift.accelerator: Shift Ctrl<Key>g",
306 "*searchMenu.findSelection.accelerator: Ctrl<Key>h",
307 "*searchMenu.findSelection.acceleratorText: [Shift]Ctrl+H",
308 "*searchMenu.findSelectionShift.accelerator: Shift Ctrl<Key>h",
309 "*searchMenu.findIncremental.accelerator: Ctrl<Key>i",
310 "*searchMenu.findIncrementalShift.accelerator: Shift Ctrl<Key>i",
311 "*searchMenu.findIncremental.acceleratorText: [Shift]Ctrl+I",
312 "*searchMenu.replace.accelerator: Ctrl<Key>r",
313 "*searchMenu.replace.acceleratorText: [Shift]Ctrl+R",
314 "*searchMenu.replaceShift.accelerator: Shift Ctrl<Key>r",
315 "*searchMenu.findReplace.accelerator: Ctrl<Key>r",
316 "*searchMenu.findReplace.acceleratorText: [Shift]Ctrl+R",
317 "*searchMenu.findReplaceShift.accelerator: Shift Ctrl<Key>r",
318 "*searchMenu.replaceFindAgain.accelerator: Ctrl<Key>t",
319 "*searchMenu.replaceFindAgain.acceleratorText: [Shift]Ctrl+T",
320 "*searchMenu.replaceFindAgainShift.accelerator: Shift Ctrl<Key>t",
321 "*searchMenu.replaceAgain.accelerator: Alt<Key>t",
322 "*searchMenu.replaceAgain.acceleratorText: [Shift]Alt+T",
323 "*searchMenu.replaceAgainShift.accelerator: Shift Alt<Key>t",
324 "*searchMenu.gotoLineNumber.accelerator: Ctrl<Key>l",
325 "*searchMenu.gotoLineNumber.acceleratorText: Ctrl+L",
326 "*searchMenu.gotoSelected.accelerator: Ctrl<Key>e",
327 "*searchMenu.gotoSelected.acceleratorText: Ctrl+E",
328 "*searchMenu.mark.accelerator: Alt<Key>m",
329 "*searchMenu.mark.acceleratorText: Alt+M a-z",
330 "*searchMenu.gotoMark.accelerator: Alt<Key>g",
331 "*searchMenu.gotoMark.acceleratorText: [Shift]Alt+G a-z",
332 "*searchMenu.gotoMarkShift.accelerator: Shift Alt<Key>g",
333 "*searchMenu.gotoMatching.accelerator: Ctrl<Key>m",
334 "*searchMenu.gotoMatching.acceleratorText: [Shift]Ctrl+M",
335 "*searchMenu.gotoMatchingShift.accelerator: Shift Ctrl<Key>m",
336 "*searchMenu.findDefinition.accelerator: Ctrl<Key>d",
337 "*searchMenu.findDefinition.acceleratorText: Ctrl+D",
338 "*searchMenu.showCalltip.accelerator: Ctrl<Key>apostrophe",
339 "*searchMenu.showCalltip.acceleratorText: Ctrl+'",
340 "*preferencesMenu.mnemonic: P",
341 "*preferencesMenu.statisticsLine.accelerator: Alt<Key>a",
342 "*preferencesMenu.statisticsLine.acceleratorText: Alt+A",
343 "*preferencesMenu.overtype.acceleratorText: Insert",
344 "*shellMenu.mnemonic: l",
345 "*shellMenu.filterSelection.accelerator: Alt<Key>r",
346 "*shellMenu.filterSelection.acceleratorText: Alt+R",
347 "*shellMenu.executeCommand.accelerator: Alt<Key>x",
348 "*shellMenu.executeCommand.acceleratorText: Alt+X",
349 "*shellMenu.executeCommandLine.accelerator: Ctrl<Key>KP_Enter",
350 "*shellMenu.executeCommandLine.acceleratorText: Ctrl+KP Enter",
351 "*shellMenu.cancelShellCommand.accelerator: Ctrl<Key>period",
352 "*shellMenu.cancelShellCommand.acceleratorText: Ctrl+.",
353 "*macroMenu.mnemonic: c",
354 "*macroMenu.learnKeystrokes.accelerator: Alt<Key>k",
355 "*macroMenu.learnKeystrokes.acceleratorText: Alt+K",
356 "*macroMenu.finishLearn.accelerator: Alt<Key>k",
357 "*macroMenu.finishLearn.acceleratorText: Alt+K",
358 "*macroMenu.cancelLearn.accelerator: Ctrl<Key>period",
359 "*macroMenu.cancelLearn.acceleratorText: Ctrl+.",
360 "*macroMenu.replayKeystrokes.accelerator: Ctrl<Key>k",
361 "*macroMenu.replayKeystrokes.acceleratorText: Ctrl+K",
362 "*macroMenu.repeat.accelerator: Ctrl<Key>comma",
363 "*macroMenu.repeat.acceleratorText: Ctrl+,",
364 "*windowsMenu.mnemonic: W",
365 "*windowsMenu.splitPane.accelerator: Ctrl<Key>2",
366 "*windowsMenu.splitPane.acceleratorText: Ctrl+2",
367 "*windowsMenu.closePane.accelerator: Ctrl<Key>1",
368 "*windowsMenu.closePane.acceleratorText: Ctrl+1",
369 "*helpMenu.mnemonic: H",
370 "nedit.help.helpForm.sw.helpText*baseTranslations: #override\
371 <Key>Tab:help-focus-buttons()\\n\
372 <Key>Return:help-button-action(\"close\")\\n\
373 Ctrl<Key>F:help-button-action(\"find\")\\n\
374 Ctrl<Key>G:help-button-action(\"findAgain\")\\n\
375 <KeyPress>osfCancel:help-button-action(\"close\")\\n\
376 ~Meta~Ctrl~Shift<Btn1Down>:\
377 grab-focus() help-hyperlink()\\n\
378 ~Meta~Ctrl~Shift<Btn1Up>:\
379 help-hyperlink(\"current\", \"process-cancel\", \"extend-end\")\\n\
380 ~Meta~Ctrl~Shift<Btn2Down>:\
381 process-bdrag() help-hyperlink()\\n\
382 ~Meta~Ctrl~Shift<Btn2Up>:\
383 help-hyperlink(\"new\", \"process-cancel\", \"copy-to\")",
384 NULL
387 static const char cmdLineHelp[] =
388 #ifndef VMS
389 "Usage: nedit [-read] [-create] [-line n | +n] [-server] [-do command]\n\
390 [-tags file] [-tabs n] [-wrap] [-nowrap] [-autowrap]\n\
391 [-autoindent] [-noautoindent] [-autosave] [-noautosave]\n\
392 [-lm languagemode] [-rows n] [-columns n] [-font font]\n\
393 [-geometry geometry] [-iconic] [-noiconic] [-svrname name]\n\
394 [-display [host]:server[.screen] [-xrm resourcestring]\n\
395 [-import file] [-background color] [-foreground color]\n\
396 [-tabbed] [-untabbed] [-group] [-V|-version] [-h|-help]\n\
397 [--] [file...]\n";
398 #else
399 "[Sorry, no on-line help available.]\n"; /* Why is that ? */
400 #endif /*VMS*/
402 int main(int argc, char **argv)
404 int i, lineNum, nRead, fileSpecified = FALSE, editFlags = CREATE;
405 int gotoLine = False, macroFileRead = False, opts = True;
406 int iconic=False, tabbed = -1, group = 0, isTabbed;
407 char *toDoCommand = NULL, *geometry = NULL, *langMode = NULL;
408 char filename[MAXPATHLEN], pathname[MAXPATHLEN];
409 XtAppContext context;
410 XrmDatabase prefDB;
411 WindowInfo *window = NULL, *lastFile = NULL;
412 static const char *protectedKeywords[] = {"-iconic", "-icon", "-geometry",
413 "-g", "-rv", "-reverse", "-bd", "-bordercolor", "-borderwidth",
414 "-bw", "-title", NULL};
415 unsigned char* invalidBindings = NULL;
417 /* Warn user if this has been compiled wrong. */
418 enum MotifStability stability = GetMotifStability();
419 if (stability == MotifKnownBad) {
420 fputs("nedit: WARNING: This version of NEdit is built incorrectly, and will be unstable.\n"
421 "nedit: Please get a stable version of NEdit from http://www.nedit.org.\n",
422 stderr);
425 /* Save the command which was used to invoke nedit for restart command */
426 ArgV0 = argv[0];
428 /* Set locale for C library, X, and Motif input functions.
429 Reverts to "C" if requested locale not available. */
430 XtSetLanguageProc(NULL, neditLanguageProc, NULL);
432 /* Initialize X toolkit (does not open display yet) */
433 XtToolkitInitialize();
434 context = XtCreateApplicationContext();
436 /* Set up a warning handler to trap obnoxious Xt grab warnings */
437 SuppressPassiveGrabWarnings();
439 /* Set up a handler to suppress X warning messages by default */
440 XtAppSetWarningHandler(context, noWarningFilter);
442 /* Set up default resources if no app-defaults file is found */
443 XtAppSetFallbackResources(context, fallbackResources);
445 #if XmVersion >= 1002
446 /* Allow users to change tear off menus with X resources */
447 XmRepTypeInstallTearOffModelConverter();
448 #endif
450 #ifdef VMS
451 /* Convert the command line to Unix style (This is not an ideal solution) */
452 ConvertVMSCommandLine(&argc, &argv);
453 #endif /*VMS*/
454 #ifdef __EMX__
455 /* expand wildcards if necessary */
456 _wildcard(&argc, &argv);
457 #endif
459 /* Read the preferences file and command line into a database */
460 prefDB = CreateNEditPrefDB(&argc, argv);
462 /* Open the display and read X database and remaining command line args.
463 XtOpenDisplay must be allowed to process some of the resource arguments
464 with its inaccessible internal option table, but others, like -geometry
465 and -iconic are per-window and it should not be allowed to consume them,
466 so we temporarily masked them out. */
467 maskArgvKeywords(argc, argv, protectedKeywords);
468 /* X.Org 6.8 and above add support for ARGB visuals (with alpha-channel),
469 typically with a 32-bit color depth. By default, NEdit uses the visual
470 with the largest color depth. However, both OpenMotif and Lesstif
471 cannot handle ARGB visuals (crashes, weird display effects, ...), so
472 NEdit should avoid selecting such a visual.
473 Unfortunatly, there appears to be no reliable way to identify
474 ARGB visuals that doesn't require some of the recent X.Org
475 extensions. Luckily, the X.Org developers have provided a mechanism
476 that can hide these problematic visuals from the application. This can
477 be achieved by setting the XLIB_SKIP_ARGB_VISUALS environment variable.
478 Users can set this variable before starting NEdit, but it is much
479 more convenient that NEdit takes care of this. This must be done before
480 the display is opened (empirically verified). */
481 putenv("XLIB_SKIP_ARGB_VISUALS=1");
482 TheDisplay = XtOpenDisplay (context, NULL, APP_NAME, APP_CLASS,
483 NULL, 0, &argc, argv);
484 unmaskArgvKeywords(argc, argv, protectedKeywords);
485 if (!TheDisplay) {
486 /* Respond to -V or -version even if there is no display */
487 for (i = 1; i < argc && strcmp(argv[i], "--"); i++)
489 if (0 == strcmp(argv[i], "-V") || 0 == strcmp(argv[i], "-version"))
491 PrintVersion();
492 exit(EXIT_SUCCESS);
495 fputs ("NEdit: Can't open display\n", stderr);
496 exit(EXIT_FAILURE);
499 /* Must be done before creating widgets */
500 fixupBrokenXKeysymDB();
501 patchResourcesForVisual();
502 patchResourcesForKDEbug();
504 /* Initialize global symbols and subroutines used in the macro language */
505 InitMacroGlobals();
506 RegisterMacroSubroutines();
508 /* Store preferences from the command line and .nedit file,
509 and set the appropriate preferences */
510 RestoreNEditPrefs(prefDB, XtDatabase(TheDisplay));
512 /* Intercept syntactically invalid virtual key bindings BEFORE we
513 create any shells. */
514 invalidBindings = sanitizeVirtualKeyBindings();
516 /* Create a hidden application shell that is the parent of all the
517 main editor windows. Realize it so it the window can act as
518 group leader. */
519 TheAppShell = CreateShellWithBestVis(APP_NAME,
520 APP_CLASS,
521 applicationShellWidgetClass,
522 TheDisplay,
523 NULL,
526 /* Restore the original bindings ASAP such that other apps are not affected. */
527 restoreInsaneVirtualKeyBindings(invalidBindings);
529 XtSetMappedWhenManaged(TheAppShell, False);
530 XtRealizeWidget(TheAppShell);
532 #ifndef NO_SESSION_RESTART
533 AttachSessionMgrHandler(TheAppShell);
534 #endif
536 /* More preference stuff */
537 LoadPrintPreferences(XtDatabase(TheDisplay), APP_NAME, APP_CLASS, True);
538 SetDeleteRemap(GetPrefMapDelete());
539 SetPointerCenteredDialogs(GetPrefRepositionDialogs());
540 SetGetEFTextFieldRemoval(!GetPrefStdOpenDialog());
542 /* Set up action procedures for menu item commands */
543 InstallMenuActions(context);
545 /* Add Actions for following hyperlinks in the help window */
546 InstallHelpLinkActions(context);
547 /* Add actions for mouse wheel support in scrolled windows (except text
548 area) */
549 InstallMouseWheelActions(context);
551 /* Install word delimiters for regular expression matching */
552 SetREDefaultWordDelimiters(GetPrefDelimiters());
554 /* Read the nedit dynamic database of files for the Open Previous
555 command (and eventually other information as well) */
556 ReadNEditDB();
558 /* Process -import command line argument before others which might
559 open windows (loading preferences doesn't update menu settings,
560 which would then be out of sync with the real preference settings) */
561 for (i=1; i<argc; i++) {
562 if(!strcmp(argv[i], "--")) {
563 break; /* treat all remaining arguments as filenames */
564 } else if (!strcmp(argv[i], "-import")) {
565 nextArg(argc, argv, &i);
566 ImportPrefFile(argv[i], False);
567 } else if (!strcmp(argv[i], "-importold")) {
568 nextArg(argc, argv, &i);
569 ImportPrefFile(argv[i], True);
573 /* Load the default tags file. Don't complain if it doesn't load, the tag
574 file resource is intended to be set and forgotten. Running nedit in a
575 directory without a tags should not cause it to spew out errors. */
576 if (*GetPrefTagFile() != '\0') {
577 AddTagsFile(GetPrefTagFile(), TAG);
580 if (strcmp(GetPrefServerName(), "") != 0) {
581 IsServer = True;
584 /* Process any command line arguments (-tags, -do, -read, -create,
585 +<line_number>, -line, -server, and files to edit) not already
586 processed by RestoreNEditPrefs. */
587 fileSpecified = FALSE;
588 for (i=1; i<argc; i++) {
589 if (opts && !strcmp(argv[i], "--")) {
590 opts = False; /* treat all remaining arguments as filenames */
591 continue;
592 } else if (opts && !strcmp(argv[i], "-tags")) {
593 nextArg(argc, argv, &i);
594 if (!AddTagsFile(argv[i], TAG))
595 fprintf(stderr, "NEdit: Unable to load tags file\n");
596 } else if (opts && !strcmp(argv[i], "-do")) {
597 nextArg(argc, argv, &i);
598 if (checkDoMacroArg(argv[i]))
599 toDoCommand = argv[i];
600 } else if (opts && !strcmp(argv[i], "-read")) {
601 editFlags |= PREF_READ_ONLY;
602 } else if (opts && !strcmp(argv[i], "-create")) {
603 editFlags |= SUPPRESS_CREATE_WARN;
604 } else if (opts && !strcmp(argv[i], "-tabbed")) {
605 tabbed = 1;
606 group = 0; /* override -group option */
607 } else if (opts && !strcmp(argv[i], "-untabbed")) {
608 tabbed = 0;
609 group = 0; /* override -group option */
610 } else if (opts && !strcmp(argv[i], "-group")) {
611 group = 2; /* 2: start new group, 1: in group */
612 } else if (opts && !strcmp(argv[i], "-line")) {
613 nextArg(argc, argv, &i);
614 nRead = sscanf(argv[i], "%d", &lineNum);
615 if (nRead != 1)
616 fprintf(stderr, "NEdit: argument to line should be a number\n");
617 else
618 gotoLine = True;
619 } else if (opts && (*argv[i] == '+')) {
620 nRead = sscanf((argv[i]+1), "%d", &lineNum);
621 if (nRead != 1)
622 fprintf(stderr, "NEdit: argument to + should be a number\n");
623 else
624 gotoLine = True;
625 } else if (opts && !strcmp(argv[i], "-server")) {
626 IsServer = True;
627 } else if (opts && !strcmp(argv[i], "-xwarn")) {
628 XtAppSetWarningHandler(context, showWarningFilter);
629 } else if (opts && (!strcmp(argv[i], "-iconic") ||
630 !strcmp(argv[i], "-icon"))) {
631 iconic = True;
632 } else if (opts && !strcmp(argv[i], "-noiconic")) {
633 iconic = False;
634 } else if (opts && (!strcmp(argv[i], "-geometry") ||
635 !strcmp(argv[i], "-g"))) {
636 nextArg(argc, argv, &i);
637 geometry = argv[i];
638 } else if (opts && !strcmp(argv[i], "-lm")) {
639 nextArg(argc, argv, &i);
640 langMode = argv[i];
641 } else if (opts && !strcmp(argv[i], "-import")) {
642 nextArg(argc, argv, &i); /* already processed, skip */
643 } else if (opts && (!strcmp(argv[i], "-V") ||
644 !strcmp(argv[i], "-version"))) {
645 PrintVersion();
646 exit(EXIT_SUCCESS);
647 } else if (opts && (!strcmp(argv[i], "-h") ||
648 !strcmp(argv[i], "-help"))) {
649 fprintf(stderr, "%s", cmdLineHelp);
650 exit(EXIT_SUCCESS);
651 } else if (opts && (*argv[i] == '-')) {
652 #ifdef VMS
653 *argv[i] = '/';
654 #endif /*VMS*/
655 fprintf(stderr, "nedit: Unrecognized option %s\n%s", argv[i],
656 cmdLineHelp);
657 exit(EXIT_FAILURE);
658 } else {
659 #ifdef VMS
660 int numFiles, j;
661 char **nameList = NULL;
662 /* Use VMS's LIB$FILESCAN for filename in argv[i] to process */
663 /* wildcards and to obtain a full VMS file specification */
664 numFiles = VMSFileScan(argv[i], &nameList, NULL, INCLUDE_FNF);
665 /* for each expanded file name do: */
666 for (j = 0; j < numFiles; ++j) {
667 if (ParseFilename(nameList[j], filename, pathname) == 0) {
668 /* determine if file is to be openned in new tab, by
669 factoring the options -group, -tabbed & -untabbed */
670 if (group == 2) {
671 isTabbed = 0; /* start a new window for new group */
672 group = 1; /* next file will be within group */
673 } else if (group == 1) {
674 isTabbed = 1; /* new tab for file in group */
675 } else { /* not in group */
676 isTabbed = tabbed==-1? GetPrefOpenInTab() : tabbed;
679 /* Files are opened in background to improve opening speed
680 by defering certain time consuiming task such as syntax
681 highlighting. At the end of the file-opening loop, the
682 last file opened will be raised to restore those deferred
683 items. The current file may also be raised if there're
684 macros to execute on. */
685 window = EditExistingFile(WindowList, filename, pathname,
686 editFlags, geometry, iconic, langMode, isTabbed,
687 True);
688 fileSpecified = TRUE;
690 if (window) {
691 CleanUpTabBarExposeQueue(window);
693 /* raise the last file of previous window */
694 if (lastFile && window->shell != lastFile->shell) {
695 CleanUpTabBarExposeQueue(lastFile);
696 RaiseDocument(lastFile);
699 if (!macroFileRead) {
700 ReadMacroInitFile(WindowList);
701 macroFileRead = True;
703 if (gotoLine)
704 SelectNumberedLine(window, lineNum);
705 if (toDoCommand != NULL) {
706 DoMacro(window, toDoCommand, "-do macro");
707 toDoCommand = NULL;
708 if (!IsValidWindow(window))
709 window = NULL; /* window closed by macro */
710 if (lastFile && !IsValidWindow(lastFile))
711 lastFile = NULL; /* window closed by macro */
715 /* register last opened file for later use */
716 if (window)
717 lastFile = window;
718 } else {
719 fprintf(stderr, "nedit: file name too long: %s\n", nameList[j]);
721 free(nameList[j]);
723 if (nameList != NULL)
724 free(nameList);
725 #else
726 if (ParseFilename(argv[i], filename, pathname) == 0 ) {
727 /* determine if file is to be openned in new tab, by
728 factoring the options -group, -tabbed & -untabbed */
729 if (group == 2) {
730 isTabbed = 0; /* start a new window for new group */
731 group = 1; /* next file will be within group */
732 } else if (group == 1) {
733 isTabbed = 1; /* new tab for file in group */
734 } else { /* not in group */
735 isTabbed = tabbed==-1? GetPrefOpenInTab() : tabbed;
738 /* Files are opened in background to improve opening speed
739 by defering certain time consuiming task such as syntax
740 highlighting. At the end of the file-opening loop, the
741 last file opened will be raised to restore those deferred
742 items. The current file may also be raised if there're
743 macros to execute on. */
744 window = EditExistingFile(WindowList, filename, pathname,
745 editFlags, geometry, iconic, langMode, isTabbed, True);
746 fileSpecified = TRUE;
747 if (window) {
748 CleanUpTabBarExposeQueue(window);
750 /* raise the last tab of previous window */
751 if (lastFile && window->shell != lastFile->shell) {
752 CleanUpTabBarExposeQueue(lastFile);
753 RaiseDocument(lastFile);
756 if (!macroFileRead) {
757 ReadMacroInitFile(WindowList);
758 macroFileRead = True;
760 if (gotoLine)
761 SelectNumberedLine(window, lineNum);
762 if (toDoCommand != NULL) {
763 DoMacro(window, toDoCommand, "-do macro");
764 toDoCommand = NULL;
765 if (!IsValidWindow(window))
766 window = NULL; /* window closed by macro */
767 if (lastFile && !IsValidWindow(lastFile))
768 lastFile = NULL; /* window closed by macro */
772 /* register last opened file for later use */
773 if (window)
774 lastFile = window;
775 } else {
776 fprintf(stderr, "nedit: file name too long: %s\n", argv[i]);
778 #endif /*VMS*/
781 #ifdef VMS
782 VMSFileScanDone();
783 #endif /*VMS*/
785 /* Raise the last file opened */
786 if (lastFile) {
787 CleanUpTabBarExposeQueue(lastFile);
788 RaiseDocument(lastFile);
790 CheckCloseDim();
792 /* If no file to edit was specified, open a window to edit "Untitled" */
793 if (!fileSpecified) {
794 EditNewFile(NULL, geometry, iconic, langMode, NULL);
795 ReadMacroInitFile(WindowList);
796 CheckCloseDim();
797 if (toDoCommand != NULL)
798 DoMacro(WindowList, toDoCommand, "-do macro");
801 /* Begin remembering last command invoked for "Repeat" menu item */
802 AddLastCommandActionHook(context);
804 /* Set up communication port and write ~/.nedit_server_process file */
805 if (IsServer)
806 InitServerCommunication();
808 /* Process events. */
809 if (IsServer)
810 ServerMainLoop(context);
811 else
812 XtAppMainLoop(context);
814 /* Not reached but this keeps some picky compilers happy */
815 return EXIT_SUCCESS;
818 static void nextArg(int argc, char **argv, int *argIndex)
820 if (*argIndex + 1 >= argc) {
821 #ifdef VMS
822 *argv[*argIndex] = '/';
823 #endif /*VMS*/
824 fprintf(stderr, "NEdit: %s requires an argument\n%s", argv[*argIndex],
825 cmdLineHelp);
826 exit(EXIT_FAILURE);
828 (*argIndex)++;
832 ** Return True if -do macro is valid, otherwise write an error on stderr
834 static int checkDoMacroArg(const char *macro)
836 Program *prog;
837 char *errMsg, *stoppedAt, *tMacro;
838 int macroLen;
840 /* Add a terminating newline (which command line users are likely to omit
841 since they are typically invoking a single routine) */
842 macroLen = strlen(macro);
843 tMacro = XtMalloc(strlen(macro)+2);
844 strncpy(tMacro, macro, macroLen);
845 tMacro[macroLen] = '\n';
846 tMacro[macroLen+1] = '\0';
848 /* Do a test parse */
849 prog = ParseMacro(tMacro, &errMsg, &stoppedAt);
850 XtFree(tMacro);
851 if (prog == NULL) {
852 ParseError(NULL, tMacro, stoppedAt, "argument to -do", errMsg);
853 return False;
855 FreeProgram(prog);
856 return True;
860 ** maskArgvKeywords and unmaskArgvKeywords mangle selected keywords by
861 ** replacing the '-' with a space, for the purpose of hiding them from
862 ** XtOpenDisplay's option processing. Why this silly scheme? XtOpenDisplay
863 ** really needs to see command line arguments, particularly -display, but
864 ** there's no way to change the option processing table it uses, to keep
865 ** it from consuming arguments which are meant to apply per-window, like
866 ** -geometry and -iconic.
868 static void maskArgvKeywords(int argc, char **argv, const char **maskArgs)
870 int i, k;
872 for (i=1; i<argc; i++)
873 for (k=0; maskArgs[k]!=NULL; k++)
874 if (!strcmp(argv[i], maskArgs[k]))
875 argv[i][0] = ' ';
879 static void unmaskArgvKeywords(int argc, char **argv, const char **maskArgs)
881 int i, k;
883 for (i=1; i<argc; i++)
884 for (k=0; maskArgs[k]!=NULL; k++)
885 if (argv[i][0]==' ' && !strcmp(&argv[i][1], &maskArgs[k][1]))
886 argv[i][0] = '-';
891 ** Some Linux distros ship with XKEYSYMDB set to a bogus filename which
892 ** breaks all Motif applications. Ignore that, and let X fall back on the
893 ** default which is far more likely to work.
895 static void fixupBrokenXKeysymDB(void)
897 const char *keysym = getenv("XKEYSYMDB");
899 if (keysym != NULL && access(keysym, F_OK) != 0)
900 putenv("XKEYSYMDB");
904 ** If we're not using the default visual, then some default resources in
905 ** the database (colors) are not valid, because they are indexes into the
906 ** default colormap. If we used them blindly, then we'd get "random"
907 ** unreadable colors. So we inspect the resource list, and use the
908 ** fallback "grey" color instead if this is the case.
910 static void patchResourcesForVisual(void)
912 Cardinal i;
913 Visual *visual;
914 int depth;
915 Colormap map;
916 Boolean usingDefaultVisual;
917 XrmDatabase db;
919 if (!TheDisplay)
920 return;
922 db = XtDatabase(TheDisplay);
924 usingDefaultVisual = FindBestVisual(TheDisplay,
925 APP_NAME,
926 APP_CLASS,
927 &visual,
928 &depth,
929 &map);
931 if (!usingDefaultVisual)
933 #ifndef LESSTIF_VERSION
935 For non-Lesstif versions, we have to put non-default visuals etc.
936 in the resource data base to make sure that all (shell) widgets
937 inherit them, especially Motif's own shells (eg, drag icons).
939 For Lesstif, this doesn't work, but luckily, Lesstif handles
940 non-default visuals etc. properly for its own shells and
941 we can take care of things for our shells (eg, call tips) through
942 our shell creation wrappers in misc.c.
945 XrmValue value;
946 value.addr = (XPointer)&visual;
947 value.size = sizeof(visual);
948 XrmPutResource(&db, "*visual", "Visual", &value);
949 value.addr = (XPointer)&map;
950 value.size = sizeof(map);
951 XrmPutResource(&db, "*colormap", "Colormap", &value);
952 value.addr = (XPointer)&depth;
953 value.size = sizeof(depth);
954 XrmPutResource(&db, "*depth", "Int", &value);
956 /* Drag-and-drop visuals do not work well when using a different
957 visual. One some systems, you'll just get a funny-looking icon
958 (maybe all-black) but on other systems it crashes with a BadMatch
959 error. This appears to be an OSF Motif bug. It would be nicer
960 to just disable the visual itself, instead of the entire drag
961 operation.
963 Update: this is no longer necessary since all problems with
964 non-default visuals should now be solved.
966 XrmPutStringResource(&db, "*dragInitiatorProtocolStyle", "DRAG_NONE");
968 #endif
970 for (i = 1; i < XtNumber(fallbackResources); ++i)
972 Cardinal resIndex = i - 1;
974 if (strstr(fallbackResources[resIndex], "*background:") ||
975 strstr(fallbackResources[resIndex], "*foreground:"))
977 /* Qualify by application name to prevent them from being
978 converted against the wrong colormap. */
979 char buf[1024] = "*" APP_NAME;
980 strcat(buf, fallbackResources[resIndex]);
981 XrmPutLineResource(&db, buf);
988 ** Several KDE version (2.x and 3.x) ship with a template application-default
989 ** file for NEdit in which several strings have to be substituted in order to
990 ** make it a valid .ad file. However, for some reason (a KDE bug?), the
991 ** template sometimes ends up in the resource db unmodified, such that several
992 ** invalid entries are present. This function checks for the presence of such
993 ** invalid entries and silently replaces them with NEdit's default values where
994 ** necessary. Without this, NEdit will typically write several warnings to
995 ** the terminal (Cannot convert string "FONTLIST" to type FontStruct etc) and
996 ** fall back on some really ugly colors and fonts.
998 static void patchResourcesForKDEbug(void)
1001 * These are the resources found the Nedit.ad template shipped with KDE 3.0.
1003 static const char* buggyResources[][3] = {
1004 { "*fontList", "FONTLIST", NEDIT_DEFAULT_FONT },
1005 { "*XmText.background", "BACKGROUND", NEDIT_DEFAULT_TEXT_BG },
1006 { "*XmText.foreground", "FOREGROUND", NEDIT_DEFAULT_FG },
1007 { "*XmTextField.background", "BACKGROUND", NEDIT_DEFAULT_TEXT_BG },
1008 { "*XmTextField.foreground", "FOREGROUND", NEDIT_DEFAULT_FG },
1009 { "*XmList.background", "BACKGROUND", NEDIT_DEFAULT_TEXT_BG },
1010 { "*XmList.foreground", "FOREGROUND", NEDIT_DEFAULT_FG },
1011 { "*helpText.background", "BACKGROUND", NEDIT_DEFAULT_HELP_BG },
1012 { "*helpText.foreground", "FOREGROUND", NEDIT_DEFAULT_HELP_FG },
1013 { "*background", "BACKGROUND", NEDIT_DEFAULT_BG },
1014 { "*foreground", "FOREGROUND", NEDIT_DEFAULT_FG, },
1015 { "*selectColor", "BACKGROUND", NEDIT_DEFAULT_SEL_BG },
1016 { "*highlightColor", "BACKGROUND", NEDIT_DEFAULT_HI_BG },
1017 { "*text.background", "WINDOW_BACKGROUND", NEDIT_DEFAULT_TEXT_BG },
1018 { "*text.foreground", "WINDOW_FOREGROUND", NEDIT_DEFAULT_FG },
1019 { "*text.selectBackground", "SELECT_BACKGROUND", NEDIT_DEFAULT_SEL_BG },
1020 { "*text.selectForeground", "SELECT_FOREGROUND", NEDIT_DEFAULT_SEL_FG },
1021 { "*text.cursorForeground", "WINDOW_FOREGROUND", NEDIT_DEFAULT_CURSOR_FG},
1022 /* { "*remapDeleteKey", "False", }, OK */
1023 /* { "!*text.heavyCursor", "True" }, OK */
1024 /* { "!*BlinkRate", "0" }, OK */
1025 /* { "*shell", "/bin/sh" }, OK */
1026 { "*statsLine.background", "BACKGROUND", NEDIT_DEFAULT_BG },
1027 { "*statsLine.foreground", "FOREGROUND", NEDIT_DEFAULT_FG },
1028 { NULL, NULL, NULL } };
1029 XrmDatabase db;
1030 int i;
1032 if (!TheDisplay)
1033 return;
1035 db = XtDatabase(TheDisplay);
1037 i = 0;
1038 while (buggyResources[i][0])
1040 const char* resource = buggyResources[i][0];
1041 const char* buggyValue = buggyResources[i][1];
1042 const char* defaultValue = buggyResources[i][2];
1043 char name[128] = APP_NAME;
1044 char class[128] = APP_CLASS;
1045 char* type;
1046 XrmValue resValue;
1048 strcat(name, resource);
1049 strcat(class, resource); /* Is this ok ? */
1051 if (XrmGetResource(db, name, class, &type, &resValue) &&
1052 !strcmp(type, XmRString))
1054 /* Buggy value? Replace by the default. */
1055 if (!strcmp(resValue.addr, buggyValue))
1057 XrmPutStringResource(&db, &name[0], (char*)defaultValue);
1060 ++i;
1065 ** It seems OSF Motif cannot handle locales with UTF-8 at the end, crashing
1066 ** in various places. The easiest one to find is to open the File Open
1067 ** dialog box. So we lop off UTF-8 if it's there and continue. Newer
1068 ** versions of Linux distros (e.g., RedHat 8) set the default language to
1069 ** to have "UTF-8" at the end, so users were seeing these crashes.
1071 ** LessTif and OpenMotif 2.3 don't appear to have this problem.
1073 ** Some versions OpenMotif 2.2.3 don't have this problem, but there's
1074 ** no way to tell which.
1077 static void patchLocaleForMotif(void)
1079 #if !(defined(LESSTIF_VERSION) || XmVersion >= 2003)
1080 const char *ctype;
1081 char ctypebuf[1024];
1082 char *utf_start;
1084 /* We have to check LC_CTYPE specifically here, because the system
1085 might specify different locales for different categories (why
1086 anyone would want to do this is beyond me). As far as I can
1087 tell, only LC_CTYPE causes OSF Motif to crash. If it turns out
1088 others do, we'll have to iterate over a list of locale cateogries
1089 and patch every one of them. */
1091 ctype = setlocale(LC_CTYPE, NULL);
1093 if (!ctype)
1094 return;
1096 strncpy(ctypebuf, ctype, sizeof ctypebuf);
1098 if ((utf_start = strstr(ctypebuf, ".utf8")) ||
1099 (utf_start = strstr(ctypebuf, ".UTF-8")))
1101 *utf_start = '\0'; /* Samurai chop */
1102 setlocale(LC_CTYPE, ctypebuf);
1104 #endif
1109 ** Same as the default X language procedure, except we check if Motif can
1110 ** handle the locale as well.
1113 static String neditLanguageProc(Display *dpy, String xnl, XtPointer closure)
1115 /* "xnl" will be set if the user passes in a new language via the
1116 "-xnllanguage" flag. If it's empty, then setlocale will get
1117 the default locale by some system-dependent means (usually,
1118 reading some environment variables). */
1120 if (! setlocale(LC_ALL, xnl)) {
1121 XtWarning("locale not supported by C library, locale unchanged");
1124 patchLocaleForMotif();
1126 if (! XSupportsLocale()) {
1127 XtWarning("locale not supported by Xlib, locale set to C");
1128 setlocale(LC_ALL, "C");
1130 if (! XSetLocaleModifiers(""))
1131 XtWarning("X locale modifiers not supported, using default");
1133 return setlocale(LC_ALL, NULL); /* re-query in case overwritten */
1136 static int sortAlphabetical(const void* k1, const void* k2)
1138 const char* key1 = *(const char**)k1;
1139 const char* key2 = *(const char**)k2;
1140 return strcmp(key1, key2);
1144 * Checks whether a given virtual key binding string is invalid.
1145 * A binding is considered invalid if there are duplicate key entries.
1147 static int virtKeyBindingsAreInvalid(const unsigned char* bindings)
1149 int maxCount = 1, i, count;
1150 const char *pos = (const char*)bindings;
1151 char *copy;
1152 char *pos2, *pos3;
1153 char **keys;
1155 /* First count the number of bindings; bindings are separated by \n
1156 strings. The number of bindings equals the number of \n + 1.
1157 Beware of leading and trailing \n; the number is actually an
1158 upper bound on the number of entries. */
1159 while ((pos = strstr(pos, "\n")))
1161 ++pos;
1162 ++maxCount;
1165 if (maxCount == 1)
1166 return False; /* One binding is always ok */
1168 keys = (char**)malloc(maxCount*sizeof(char*));
1169 copy = XtNewString((const char*)bindings);
1170 i = 0;
1171 pos2 = copy;
1173 count = 0;
1174 while (i<maxCount && pos2 && *pos2)
1176 while (isspace((int) *pos2) || *pos2 == '\n') ++pos2;
1178 if (*pos2 == '!') /* Ignore comment lines */
1180 pos2 = strstr(pos2, "\n");
1181 continue; /* Go to the next line */
1184 if (*pos2)
1186 keys[i++] = pos2;
1187 ++count;
1188 pos3 = strstr(pos2, ":");
1189 if (pos3)
1191 *pos3++ = 0; /* Cut the string and jump to the next entry */
1192 pos2 = pos3;
1194 pos2 = strstr(pos2, "\n");
1198 if (count <= 1)
1200 free(keys);
1201 XtFree(copy);
1202 return False; /* No conflict */
1205 /* Sort the keys and look for duplicates */
1206 qsort((void*)keys, count, sizeof(const char*), sortAlphabetical);
1207 for (i=1; i<count; ++i)
1209 if (!strcmp(keys[i-1], keys[i]))
1211 /* Duplicate detected */
1212 free(keys);
1213 XtFree(copy);
1214 return True;
1217 free(keys);
1218 XtFree(copy);
1219 return False;
1223 * Optionally sanitizes the Motif default virtual key bindings.
1224 * Some applications install invalid bindings (attached to the root window),
1225 * which cause certain keys to malfunction in NEdit.
1226 * Through an X-resource, users can choose whether they want
1227 * - to always keep the existing bindings
1228 * - to override the bindings only if they are invalid
1229 * - to always override the existing bindings.
1232 static Atom virtKeyAtom;
1234 static unsigned char* sanitizeVirtualKeyBindings(void)
1236 int overrideBindings = GetPrefOverrideVirtKeyBindings();
1237 Window rootWindow;
1238 const char *virtKeyPropName = "_MOTIF_DEFAULT_BINDINGS";
1239 Atom dummyAtom;
1240 int getFmt;
1241 unsigned long dummyULong, nItems;
1242 unsigned char *insaneVirtKeyBindings = NULL;
1244 if (overrideBindings == VIRT_KEY_OVERRIDE_NEVER) return NULL;
1246 virtKeyAtom = XInternAtom(TheDisplay, virtKeyPropName, False);
1247 rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
1249 /* Remove the property, if it exists; we'll restore it later again */
1250 if (XGetWindowProperty(TheDisplay, rootWindow, virtKeyAtom, 0, INT_MAX,
1251 True, XA_STRING, &dummyAtom, &getFmt, &nItems,
1252 &dummyULong, &insaneVirtKeyBindings) != Success
1253 || nItems == 0)
1255 return NULL; /* No binding yet; nothing to do */
1258 if (overrideBindings == VIRT_KEY_OVERRIDE_AUTO)
1260 if (!virtKeyBindingsAreInvalid(insaneVirtKeyBindings))
1262 /* Restore the property immediately; it seems valid */
1263 XChangeProperty(TheDisplay, rootWindow, virtKeyAtom, XA_STRING, 8,
1264 PropModeReplace, insaneVirtKeyBindings,
1265 strlen((const char*)insaneVirtKeyBindings));
1266 XFree((char*)insaneVirtKeyBindings);
1267 return NULL; /* Prevent restoration */
1270 return insaneVirtKeyBindings;
1274 * NEdit should not mess with the bindings installed by other apps, so we
1275 * just restore whatever was installed, if necessary
1277 static void restoreInsaneVirtualKeyBindings(unsigned char *insaneVirtKeyBindings)
1279 if (insaneVirtKeyBindings)
1281 Window rootWindow = RootWindow(TheDisplay, DefaultScreen(TheDisplay));
1282 /* Restore the root window atom, such that we don't affect
1283 other apps. */
1284 XChangeProperty(TheDisplay, rootWindow, virtKeyAtom, XA_STRING, 8,
1285 PropModeReplace, insaneVirtKeyBindings,
1286 strlen((const char*)insaneVirtKeyBindings));
1287 XFree((char*)insaneVirtKeyBindings);
1292 ** Warning handler that suppresses harmless but annoying warnings generated
1293 ** by non-production Lesstif versions.
1295 static void showWarningFilter(String message)
1297 const char* bogusMessages[] = {
1298 #ifdef LESSTIF_VERSION
1299 "XmFontListCreate() is an obsolete function!",
1300 "No type converter registered for 'String' to 'PathMode' conversion.",
1301 "XtRemoveGrab asked to remove a widget not on the list",
1302 #endif
1303 NULL
1305 const char **bogusMessage = &bogusMessages[0];
1307 while (*bogusMessage) {
1308 size_t bogusLen = strlen(*bogusMessage);
1309 if (strncmp(message, *bogusMessage, bogusLen) == 0) {
1310 #ifdef DEBUG_LESSTIF_WARNINGS
1311 /* Developers may want to see which messages are suppressed. */
1312 fprintf(stderr, "[SUPPRESSED] %s\n", message);
1313 #endif
1314 return;
1316 ++bogusMessage;
1319 /* An unknown message. Keep it. */
1320 fprintf(stderr, "%s\n", message);
1323 static void noWarningFilter(String message)
1325 return;