1 /*******************************************************************************
2 *									       *
3 * smartIndent.c -- Maintain, and allow user to edit, macros for smart indent   *
4 *									       *
5 * Copyright (C) 1999 Mark Edel						       *
6 *									       *
7 * This is free software; you can redistribute it and/or modify it under the    *
8 * terms of the GNU General Public License as published by the Free Software    *
9 * Foundation; either version 2 of the License, or (at your option) any later   *
10 * version. In addition, you may distribute version of this program linked to   *
11 * Motif or Open Motif. See README for details.                                 *
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 * July, 1997								       *
24 *									       *
25 * Written by Mark Edel							       *
26 *									       *
27 *******************************************************************************/
28 
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
32 
33 #include "smartIndent.h"
34 #include "textBuf.h"
35 #include "nedit.h"
36 #include "text.h"
37 #include "preferences.h"
38 #include "interpret.h"
39 #include "macro.h"
40 #include "window.h"
41 #include "parse.h"
42 #include "shift.h"
43 #include "help.h"
44 #include "../util/DialogF.h"
45 #include "../util/misc.h"
46 #include "../util/nedit_malloc.h"
47 
48 #include <stdio.h>
49 #include <string.h>
50 #include <limits.h>
51 
52 #include <Xm/Xm.h>
53 #ifdef VMS
54 #include "../util/VMSparam.h"
55 #else
56 #ifndef __MVS__
57 #include <sys/param.h>
58 #endif
59 #endif /*VMS*/
60 #include <Xm/Xm.h>
61 #include <Xm/Form.h>
62 #include <Xm/Text.h>
63 #include <Xm/LabelG.h>
64 #include <Xm/PushB.h>
65 #include <Xm/RowColumn.h>
66 #include <Xm/SeparatoG.h>
67 #include <Xm/PanedW.h>
68 
69 #ifdef HAVE_DEBUG_H
70 #include "../debug.h"
71 #endif
72 
73 
74 static char MacroEndBoundary[] = "--End-of-Macro--";
75 
76 typedef struct {
77     char *lmName;
78     char *initMacro;
79     char *newlineMacro;
80     char *modMacro;
81 } smartIndentRec;
82 
83 typedef struct {
84     Program *newlineMacro;
85     int inNewLineMacro;
86     Program *modMacro;
87     int inModMacro;
88 } windowSmartIndentData;
89 
90 /* Smart indent macros dialog information */
91 static struct {
92     Widget shell;
93     Widget lmOptMenu;
94     Widget lmPulldown;
95     Widget initMacro;
96     Widget newlineMacro;
97     Widget modMacro;
98     char *langModeName;
99 } SmartIndentDialog = {NULL,NULL,NULL,NULL,NULL,NULL,NULL};
100 
101 /* Common smart indent macros dialog information */
102 static struct {
103     Widget shell;
104     Widget text;
105 } CommonDialog = {NULL,NULL};
106 
107 static int NSmartIndentSpecs = 0;
108 static smartIndentRec *SmartIndentSpecs[MAX_LANGUAGE_MODES];
109 static char *CommonMacros = NULL;
110 
111 static void executeNewlineMacro(WindowInfo *window,smartIndentCBStruct *cbInfo);
112 static void executeModMacro(WindowInfo *window,smartIndentCBStruct *cbInfo);
113 static void insertShiftedMacro(textBuffer *buf, char *macro);
114 static int isDefaultIndentSpec(smartIndentRec *indentSpec);
115 static smartIndentRec *findIndentSpec(const char *modeName);
116 static char *ensureNewline(char *string);
117 static int loadDefaultIndentSpec(char *lmName);
118 static int siParseError(char *stringStart, char *stoppedAt, char *message);
119 static void destroyCB(Widget w, XtPointer clientData, XtPointer callData);
120 static void langModeCB(Widget w, XtPointer clientData, XtPointer callData);
121 static void commonDialogCB(Widget w, XtPointer clientData, XtPointer callData);
122 static void lmDialogCB(Widget w, XtPointer clientData, XtPointer callData);
123 static void okCB(Widget w, XtPointer clientData, XtPointer callData);
124 static void applyCB(Widget w, XtPointer clientData, XtPointer callData);
125 static void checkCB(Widget w, XtPointer clientData, XtPointer callData);
126 static void restoreCB(Widget w, XtPointer clientData, XtPointer callData);
127 static void deleteCB(Widget w, XtPointer clientData, XtPointer callData);
128 static void closeCB(Widget w, XtPointer clientData, XtPointer callData);
129 static void helpCB(Widget w, XtPointer clientData, XtPointer callData);
130 static int checkSmartIndentDialogData(void);
131 static smartIndentRec *getSmartIndentDialogData(void);
132 static void setSmartIndentDialogData(smartIndentRec *is);
133 static void comDestroyCB(Widget w, XtPointer clientData, XtPointer callData);
134 static void comOKCB(Widget w, XtPointer clientData, XtPointer callData);
135 static void comApplyCB(Widget w, XtPointer clientData, XtPointer callData);
136 static void comCheckCB(Widget w, XtPointer clientData, XtPointer callData);
137 static void comRestoreCB(Widget w, XtPointer clientData, XtPointer callData);
138 static void comCloseCB(Widget w, XtPointer clientData, XtPointer callData);
139 static int updateSmartIndentCommonData(void);
140 static int checkSmartIndentCommonDialogData(void);
141 static int updateSmartIndentData(void);
142 static char *readSIMacro(char **inPtr);
143 static smartIndentRec *copyIndentSpec(smartIndentRec *is);
144 static void freeIndentSpec(smartIndentRec *is);
145 static int indentSpecsDiffer(smartIndentRec *is1, smartIndentRec *is2);
146 
147 #define N_DEFAULT_INDENT_SPECS 4
148 static smartIndentRec DefaultIndentSpecs[N_DEFAULT_INDENT_SPECS] = {
149 {"C",
150 "# C Macros and tuning parameters are shared with C++, and are declared\n\
151 # in the common section.  Press Common / Shared Initialization above.\n",
152 "return cFindSmartIndentDist($1)\n",
153 "if ($2 == \"}\" || $2 == \"{\" || $2 == \"#\")\n\
154     cBraceOrPound($1, $2)\n"},
155 {"C++",
156 "# C++ Macros and tuning parameters are shared with C, and are declared\n\
157 # in the common section.  Press Common / Shared Initialization above.\n",
158 "return cFindSmartIndentDist($1)\n",
159 "if ($2 == \"}\" || $2 == \"{\" || $2 == \"#\")\n\
160     cBraceOrPound($1, $2)\n"},
161 {"Python",
162 "# Number of characters in a normal indent level.  May be a number, or the\n\
163 # string \"default\", meaning, guess the value from the current tab settings.\n\
164 $pyIndentDist = \"default\"\n",
165 "if (get_range($1-1, $1) != \":\")\n\
166     return -1\n\
167 return measureIndent($1) + defaultIndent($pyIndentDist)\n", NULL},
168 {"Matlab",
169 "# Number of spaces to indent \"case\" statements\n\
170 $caseDepth = 2\n\
171 define matlabNewlineMacro\n\
172 {\n\
173    if (!$em_tab_dist)\n\
174         tabsize = $tab_dist\n\
175    else\n\
176         tabsize = $em_tab_dist\n\
177    startLine = startOfLine($1)\n\
178    indentLevel = measureIndent($1)\n\
179 \n\
180    # If this line is continued on next, return default:\n\
181    lastLine = get_range(startLine, $1)\n\
182    if (search_string(lastLine, \"...\", 0) != -1) {\n\
183       if ($n_args == 2)\n\
184          return matlabNewlineMacro(startLine - 1, 1)\n\
185       else {\n\
186          return -1\n\
187       }\n\
188    }\n\
189 \n\
190    # Correct the indentLevel if this was a continued line.\n\
191    while (startLine > 1)\n\
192    {\n\
193       endLine = startLine - 1\n\
194       startLine = startOfLine(endLine)\n\
195       lastLine = get_range(startLine, endLine)\n\
196       # No \"...\" means we've found the root\n\
197       if (search_string(lastLine, \"...\", 0) == -1) {\n\
198          startLine = endLine + 1\n\
199          break\n\
200       }\n\
201    }\n\
202    indentLevel = measureIndent(startLine)\n\
203 \n\
204    # Get the first word of the indentLevel line\n\
205    FWend = search(\">|\\n\", startLine + indentLevel, \"regex\")\n\
206    # This search fails on EOF\n\
207    if (FWend == -1)\n\
208       FWend = $1\n\
209 \n\
210    firstWord = get_range(startLine + indentLevel, FWend)\n\
211 \n\
212    # How shall we change the indent level based on the first word?\n\
213    if (search_string(firstWord, \\\n\
214          \"<for>|<function>|<if>|<switch>|<try>|<while>\", 0, \"regex\") == 0) {\n\
215       return indentLevel + tabsize\n\
216    }\n\
217    else if ((firstWord == \"end\") || (search_string(firstWord, \\\n\
218             \"<case>|<catch>|<else>|<elseif>|<otherwise>\", 0, \"regex\") == 0)) {\n\
219       # Get the last indent level \n\
220       if (startLine > 0) # avoid infinite loop\n\
221 	 last_indent = matlabNewlineMacro(startLine - 1, 1)\n\
222       else\n\
223          last_indent = indentLevel\n\
224 \n\
225       # Re-indent this line\n\
226       if ($n_args == 1) {\n\
227          if (firstWord == \"case\" || firstWord == \"otherwise\")\n\
228             replace_range(startLine, startLine + indentLevel, \\\n\
229                           makeIndentString(last_indent - tabsize + $caseDepth))\n\
230          else\n\
231             replace_range(startLine, startLine + indentLevel, \\\n\
232                           makeIndentString(last_indent - tabsize))\n\
233       }\n\
234 \n\
235       if (firstWord == \"end\") {\n\
236          return max(last_indent - tabsize, 0)\n\
237       }\n\
238       else {\n\
239          return last_indent\n\
240       }\n\
241    } \n\
242    else {\n\
243       return indentLevel\n\
244    }\n\
245 }\n\
246 ", "return matlabNewlineMacro($1)\n", NULL}
247 };
248 
249 static char DefaultCommonMacros[] = "#\n\
250 # C/C++ Style/tuning parameters\n\
251 #\n\
252 \n\
253 # Number of characters in a normal indent level.  May be a number, or the\n\
254 # string \"default\", meaning, guess the value from the current tab settings.\n\
255 $cIndentDist = \"default\"\n\
256 \n\
257 # Number of characters in a line continuation.  May be a number or the\n\
258 # string \"default\", meaning, guess the value from the current tab settings.\n\
259 $cContinuationIndent = \"default\"\n\
260 \n\
261 # How far back from the current position to search for an anchoring position\n\
262 # on which to base indent.  When no reliable indicators of proper indent level\n\
263 # can be found within the requested distance, reverts to plain auto indent.\n\
264 $cMaxSearchBackLines = 10\n\
265 \n\
266 #\n\
267 # Find the start of the line containing position $1\n\
268 #\n\
269 define startOfLine {\n\
270 \n\
271     for (i=$1-1; ; i--) {\n\
272 	if (i <= 0)\n\
273 	    return 0\n\
274 	if (get_character(i) == \"\\n\")\n\
275 	    return i + 1\n\
276     }\n\
277 }\n\
278 \n\
279 #\n\
280 # Find the indent level of the line containing character position $1\n\
281 #\n\
282 define measureIndent {\n\
283     \n\
284     # measure the indentation to the first non-white character on the line\n\
285     indent = 0\n\
286     for (i=startOfLine($1); i < $text_length; i++) {\n\
287 	c = get_character(i)\n\
288 	if (c != \" \" && c != \"\\t\")\n\
289 	    break\n\
290 	if (c == \"\\t\")\n\
291 	    indent += $tab_dist - (indent % $tab_dist)\n\
292 	else\n\
293 	    indent++\n\
294     }\n\
295     return indent\n\
296 }\n\
297 \n\
298 #\n\
299 # Make a string to produce an indent of $1 characters\n\
300 #\n\
301 define makeIndentString {\n\
302 \n\
303     if ($use_tabs) {\n\
304 	nTabs = $1 / $tab_dist\n\
305 	nSpaces = $1 % $tab_dist\n\
306     } else {\n\
307 	nTabs = 0\n\
308 	nSpaces = $1\n\
309     }\n\
310     indentString = \"\"\n\
311     for (i=0; i<nTabs; i++)\n\
312 	indentString = indentString \"\\t\"\n\
313     for (i=0; i<nSpaces; i++)\n\
314 	indentString = indentString \" \"\n\
315     return indentString\n\
316 }\n\
317 \n\
318 #\n\
319 # If $1 is a number, just pass it on.  If it is the string \"default\",\n\
320 # figure out a reasonable indent distance for a structured languages\n\
321 # like C, based on how tabs are set.\n\
322 #\n\
323 define defaultIndent {\n\
324 \n\
325     if ($1 != \"default\")\n\
326     	return $1\n\
327     if ($em_tab_dist)\n\
328     	return $em_tab_dist\n\
329     if ($tab_dist <= 8)\n\
330     	return $tab_dist\n\
331     return 4\n\
332 }\n\
333    \n\
334 #\n\
335 # If $1 is a number, just pass it on.  If it is the string \"default\",\n\
336 # figure out a reasonable amount of indentation for continued lines\n\
337 # based on how tabs are set.\n\
338 #\n\
339 define defaultContIndent {\n\
340 \n\
341     if ($1 != \"default\")\n\
342     	return $1\n\
343     if ($em_tab_dist)\n\
344     	return $em_tab_dist * 2\n\
345     if ($tab_dist <= 8)\n\
346     	return $tab_dist * 2\n\
347     return 8\n\
348 }\n\
349 \n\
350 #\n\
351 # Find the end of the conditional part of if/while/for, by looking for balanced\n\
352 # parenthesis between $1 and $2.  returns -1 if parens don't balance before\n\
353 # $2, or if no parens are found\n\
354 #\n\
355 define findBalancingParen {\n\
356 \n\
357     openParens = 0\n\
358     parensFound = 0\n\
359     for (i=$1; i<$2; i++) {\n\
360 	c = get_character(i)\n\
361 	if (c == \"(\") {\n\
362 	    openParens++\n\
363 	    parensFound = 1\n\
364 	} else if (c == \")\")\n\
365 	    openParens--\n\
366 	else if (!parensFound && c != \" \" && c != \"\\t\")\n\
367 	    return -1\n\
368 	if (parensFound && openParens <=0)\n\
369 	    return i+1\n\
370     }\n\
371     return -1\n\
372 }\n\
373 \n\
374 #\n\
375 # Skip over blank space and comments and preprocessor directives from position\n\
376 # $1 to a maximum of $2.\n\
377 # if $3 is non-zero, newlines are considered blank space as well.  Return -1\n\
378 # if the maximum position ($2) is hit mid-comment or mid-directive\n\
379 #\n\
380 define cSkipBlankSpace {\n\
381     \n\
382     for (i=$1; i<$2; i++) {\n\
383 	c = get_character(i)\n\
384 	if (c == \"/\") {\n\
385 	    if (i+1 >= $2)\n\
386 		return i\n\
387 	    if (get_character(i+1) == \"*\") {\n\
388 		for (i=i+1; ; i++) {\n\
389 		    if (i+1 >= $2)\n\
390 			return -1\n\
391 		    if (get_character(i) == \"*\" && get_character(i+1) == \"/\") {\n\
392 			i++\n\
393 			break\n\
394 		    }\n\
395 		}\n\
396 	    } else if (get_character(i+1) == \"/\") {\n\
397 		for (i=i+1; i<$2; i++) {\n\
398 		    if (get_character(i) == \"\\n\") {\n\
399 			if (!$3)\n\
400 			    return i\n\
401 			break\n\
402 		    }\n\
403 		}\n\
404 	    }\n\
405 	} else if (c == \"#\" && $3) {\n\
406 	    for (i=i+1; ; i++) {\n\
407 		if (i >= $2) {\n\
408 		    if (get_character(i-1) == \"\\\\\")\n\
409 			return -1\n\
410 		    else\n\
411 			break\n\
412 		}\n\
413 		if (get_character(i) == \"\\n\" && get_character(i-1) != \"\\\\\")\n\
414 		    break\n\
415 	    }\n\
416 	} else if (!(c == \" \" || c == \"\\t\" || ($3 && c==\"\\n\")))\n\
417 	    return i\n\
418     }\n\
419     return $2\n\
420 }\n\
421 \n\
422 #\n\
423 # Search backward for an anchor point: a line ending brace, or semicolon\n\
424 # or case statement, followed (ignoring blank lines and comments) by what we\n\
425 # assume is a properly indented line, a brace on a line by itself, or a case\n\
426 # statement.  Returns the position of the first non-white, non comment\n\
427 # character on the line.  returns -1 if an anchor position can't be found\n\
428 # before $cMaxSearchBackLines.\n\
429 #\n\
430 define cFindIndentAnchorPoint {\n\
431 \n\
432     nLines = 0\n\
433     anchorPos = $1\n\
434     for (i=$1-1; i>0; i--) {\n\
435 	c = get_character(i)\n\
436 	if (c == \";\" || c == \"{\" || c == \"}\" || c == \":\") {\n\
437 \n\
438 	    # Verify that it's line ending\n\
439 	    lineEnd = cSkipBlankSpace(i+1, $1, 0)\n\
440 	    if (lineEnd == -1 || \\\n\
441 	    	    (lineEnd != $text_length && get_character(lineEnd) != \"\\n\"))\n\
442    		continue\n\
443 \n\
444 	    # if it's a colon, it's only meaningful if \"case\" begins the line\n\
445 	    if (c == \":\") {\n\
446 	    	lineStart = startOfLine(i)\n\
447 		caseStart = cSkipBlankSpace(lineStart, lineEnd, 0)\n\
448 		if (get_range(caseStart, caseStart+4) != \"case\")\n\
449 		    continue\n\
450 		delim = get_character(caseStart+4)\n\
451 		if (delim!=\" \" && delim!=\"\\t\" && delim!=\"(\" && delim!=\":\")\n\
452 		    continue\n\
453 		isCase = 1\n\
454 	    } else\n\
455 	    	isCase = 0\n\
456 \n\
457 	    # Move forward past blank lines and comment lines to find\n\
458 	    #    non-blank, non-comment line-start\n\
459 	    anchorPos = cSkipBlankSpace(lineEnd, $1, 1)\n\
460 \n\
461 	    # Accept if it's before the requested position, otherwise\n\
462 	    #    continue further back in the file and try again\n\
463 	    if (anchorPos != -1 && anchorPos < $1)\n\
464 		break\n\
465 \n\
466 	    # A case statement by itself is an acceptable anchor\n\
467 	    if (isCase)\n\
468 	    	return caseStart\n\
469 \n\
470 	    # A brace on a line by itself is an acceptable anchor, even\n\
471 	    #    if it doesn't follow a semicolon or another brace\n\
472 	    if (c == \"{\" || c == \"}\") {\n\
473 		for (j = i-1; ; j--) {\n\
474 		    if (j == 0)\n\
475 			return i\n\
476 		    ch = get_character(j)\n\
477 		    if (ch == \"\\n\")\n\
478 		       return i\n\
479 		    if (ch != \"\\t\" && ch != \" \")\n\
480 		       break\n\
481 		}\n\
482 	    }\n\
483 \n\
484 	} else if (c == \"\\n\")\n\
485 	    if (++nLines > $cMaxSearchBackLines)\n\
486 		return -1\n\
487     }\n\
488     if (i <= 0)\n\
489 	return -1\n\
490     return anchorPos\n\
491 }\n\
492 \n\
493 #\n\
494 # adjust the indent on a line about to recive either a right or left brace\n\
495 # or pound (#) character ($2) following position $1\n\
496 #\n\
497 define cBraceOrPound {\n\
498 \n\
499     # Find start of the line, and make sure there's nothing but white-space\n\
500     #   before the character.  If there's anything before it, do nothing\n\
501     for (i=$1-1; ; i--) {\n\
502 	if (i < 0) {\n\
503 	    lineStart = 0\n\
504 	    break\n\
505 	}\n\
506 	c = get_character(i)\n\
507 	if (c == \"\\n\") {\n\
508 	    lineStart = i + 1\n\
509 	    break\n\
510 	}\n\
511 	if (c != \" \" && c != \"\\t\")\n\
512 	    return\n\
513     }\n\
514 \n\
515     # If the character was a pound, drag it all the way to the left margin\n\
516     if ($2 == \"#\") {\n\
517 	replace_range(lineStart, $1, \"\")\n\
518 	return\n\
519     }\n\
520 \n\
521     # Find the position on which to base the indent\n\
522     indent = cFindSmartIndentDist($1 - 1, \"noContinue\")\n\
523     if (indent == -1)\n\
524 	return\n\
525     \n\
526     # Adjust the indent if it's a right brace (left needs no adjustment)\n\
527     if ($2 == \"}\") {\n\
528 	indent -= defaultIndent($cIndentDist)\n\
529         if (indent < 0)\n\
530 	    indent = 0\n\
531     }\n\
532 \n\
533     # Replace the current indent with the new indent string\n\
534     insertStr = makeIndentString(indent)\n\
535     replace_range(lineStart, $1, insertStr)\n\
536 }\n\
537 \n\
538 #\n\
539 # Find Smart Indent Distance for a newline character inserted at $1,\n\
540 # or return -1 to give up.  Adding the optional argument \"noContinue\"\n\
541 # will stop the routine from inserting line continuation indents\n\
542 #\n\
543 define cFindSmartIndentDist {\n\
544 \n\
545     # Find a known good indent to base the new indent upon\n\
546     anchorPos = cFindIndentAnchorPoint($1)\n\
547     if (anchorPos == -1)\n\
548 	return -1\n\
549 \n\
550     # Find the indentation of that line\n\
551     anchorIndent = measureIndent(anchorPos)\n\
552 \n\
553     # Look for special keywords which affect indent (for, if, else while, do)\n\
554     #    and modify the continuation indent distance to the normal indent\n\
555     #    distance when a completed statement of this type occupies the line.\n\
556     if ($n_args >= 2 && $2 == \"noContinue\") {\n\
557 	continueIndent = 0\n\
558 	$allowSemi = 0\n\
559     } else\n\
560 	continueIndent = cCalcContinueIndent(anchorPos, $1)\n\
561 \n\
562     # Move forward from anchor point, ignoring comments and blank lines,\n\
563     #   remembering the last non-white, non-comment character.  If $1 is\n\
564     #   in the middle of a comment, give up\n\
565     lastChar = get_character(anchorPos)\n\
566     if (anchorPos < $1) {\n\
567 	for (i=anchorPos;;) {\n\
568    	    i = cSkipBlankSpace(i, $1, 1)\n\
569 	    if (i == -1)\n\
570 		return -1\n\
571  	    if (i >= $1)\n\
572  		break\n\
573  	    lastChar = get_character(i++)\n\
574 	}\n\
575     }\n\
576 \n\
577     # Return the new indent based on the type of the last character.\n\
578     #   In a for stmt, however, last character may be a semicolon and not\n\
579     #   signal the end of the statement\n\
580     if (lastChar == \"{\")\n\
581 	return anchorIndent + defaultIndent($cIndentDist)\n\
582     else if (lastChar == \"}\")\n\
583 	return anchorIndent\n\
584     else if (lastChar == \";\") {\n\
585 	if ($allowSemi)\n\
586 	    return anchorIndent + continueIndent\n\
587 	else\n\
588 	    return anchorIndent\n\
589     } else if (lastChar == \":\" && get_range(anchorPos, anchorPos+4) == \"case\")\n\
590     	return anchorIndent + defaultIndent($cIndentDist)\n\
591     return anchorIndent + continueIndent\n\
592 }\n\
593 \n\
594 #\n\
595 # Calculate the continuation indent distance for statements not ending in\n\
596 # semicolons or braces.  This is not necessarily $continueIndent.  It may\n\
597 # be adjusted if the statement contains if, while, for, or else.\n\
598 #\n\
599 # As a side effect, also return $allowSemi to help distinguish statements\n\
600 # which might contain an embedded semicolon, which should not be interpreted\n\
601 # as an end of statement character.\n\
602 #\n\
603 define cCalcContinueIndent {\n\
604 \n\
605     anchorPos = $1\n\
606     maxPos = $2\n\
607 \n\
608     # Figure out if the anchor is on a keyword which changes indent.  A special\n\
609     #   case is made for elses nested in after braces\n\
610     anchorIsFor = 0\n\
611     $allowSemi = 0\n\
612     if (get_character(anchorPos) == \"}\") {\n\
613 	for (i=anchorPos+1; i<maxPos; i++) {\n\
614 	    c = get_character(i)\n\
615 	    if (c != \" \" && c != \"\\t\")\n\
616 		break\n\
617 	}\n\
618 	if (get_range(i, i+4) == \"else\") {\n\
619 	    keywordEnd = i + 4\n\
620 	    needsBalancedParens = 0\n\
621 	} else\n\
622 	    return defaultContIndent($cContinuationIndent)\n\
623     } else if (get_range(anchorPos, anchorPos + 4) == \"else\") {\n\
624 	keywordEnd = anchorPos + 4\n\
625 	needsBalancedParens = 0\n\
626     } else if (get_range(anchorPos, anchorPos + 2) == \"do\") {\n\
627 	keywordEnd = anchorPos + 2\n\
628 	needsBalancedParens = 0\n\
629     } else if (get_range(anchorPos, anchorPos + 3) == \"for\") {\n\
630 	keywordEnd = anchorPos + 3\n\
631 	anchorIsFor = 1\n\
632 	needsBalancedParens = 1\n\
633     } else if (get_range(anchorPos, anchorPos + 2) == \"if\") {\n\
634 	keywordEnd = anchorPos + 2\n\
635 	needsBalancedParens = 1\n\
636     } else if (get_range(anchorPos, anchorPos + 5) == \"while\") {\n\
637 	keywordEnd = anchorPos + 5\n\
638 	needsBalancedParens = 1\n\
639     } else\n\
640 	return defaultContIndent($cContinuationIndent)\n\
641 \n\
642     # If the keyword must be followed balanced parenthesis, find the end of\n\
643     # the statement by following balanced parens.  If the parens aren't\n\
644     # balanced by maxPos, continue the condition.  In the special case of\n\
645     # the for keyword, a semicolon can end the line and the caller should be\n\
646     # signaled to allow that\n\
647     if (needsBalancedParens) {\n\
648 	stmtEnd = findBalancingParen(keywordEnd, maxPos)\n\
649 	if (stmtEnd == -1) {\n\
650 	    $allowSemi = anchorIsFor\n\
651 	    return defaultContIndent($cContinuationIndent)\n\
652 	}\n\
653     } else\n\
654 	stmtEnd = keywordEnd\n\
655 \n\
656     # check if the statement ends the line\n\
657     lineEnd = cSkipBlankSpace(stmtEnd, maxPos, 0)\n\
658     if (lineEnd == -1)		    	    # ends in comment or preproc\n\
659 	return -1\n\
660     if (lineEnd == maxPos)  	    	    # maxPos happens at stmt end\n\
661 	return defaultIndent($cIndentDist)\n\
662     c = get_character(lineEnd)\n\
663     if (c != \"\\n\")   		    	    # something past last paren on line,\n\
664 	return defaultIndent($cIndentDist)  #   probably quoted or extra braces\n\
665 \n\
666     # stmt contintinues beyond matching paren && newline, we're in\n\
667     #   the conditional part, calculate the continue indent distance\n\
668     #   recursively, based on the anchor point of the new line\n\
669     newAnchor = cSkipBlankSpace(lineEnd+1, maxPos, 1)\n\
670     if (newAnchor == -1)\n\
671 	return -1\n\
672     if (newAnchor == maxPos)\n\
673 	return defaultIndent($cIndentDist)\n\
674     return cCalcContinueIndent(newAnchor, maxPos) + defaultIndent($cIndentDist)\n\
675 }\n\
676 ";
677 
678 /*
679 ** Turn on smart-indent (well almost).  Unfortunately, this doesn't do
680 ** everything.  It requires that the smart indent callback (SmartIndentCB)
681 ** is already attached to all of the text widgets in the window, and that the
682 ** smartIndent resource must be turned on in the widget.  These are done
683 ** separately, because they are required per-text widget, and therefore must
684 ** be repeated whenever a new text widget is created within this window
685 ** (a split-window command).
686 */
BeginSmartIndent(WindowInfo * window,int warn)687 void BeginSmartIndent(WindowInfo *window, int warn)
688 {
689     windowSmartIndentData *winData;
690     smartIndentRec *indentMacros;
691     char *modeName, *stoppedAt, *errMsg;
692     static int initialized;
693 
694     /* Find the window's language mode.  If none is set, warn the user */
695     modeName = LanguageModeName(window->languageMode);
696     if (modeName == NULL)
697     {
698         if (warn)
699         {
700             DialogF(DF_WARN, window->shell, 1, "Smart Indent",
701                     "No language-specific mode has been set for this file.\n\n"
702                     "To use smart indent in this window, please select a\n"
703                     "language from the Preferences -> Language Modes menu.",
704                     "OK");
705         }
706         return;
707     }
708 
709     /* Look up the appropriate smart-indent macros for the language */
710     indentMacros = findIndentSpec(modeName);
711     if (indentMacros == NULL)
712     {
713         if (warn)
714         {
715             DialogF(DF_WARN, window->shell, 1, "Smart Indent",
716                     "Smart indent is not available in languagemode\n%s.\n\n"
717                     "You can create new smart indent macros in the\n"
718                     "Preferences -> Default Settings -> Smart Indent\n"
719                     "dialog, or choose a different language mode from:\n"
720                     "Preferences -> Language Mode.", "OK", modeName);
721         }
722         return;
723     }
724 
725     /* Make sure that the initial macro file is loaded before we execute
726        any of the smart-indent macros. Smart-indent macros may reference
727        routines defined in that file. */
728     ReadMacroInitFile(window);
729 
730     /* Compile and run the common and language-specific initialization macros
731        (Note that when these return, the immediate commands in the file have not
732        necessarily been executed yet.  They are only SCHEDULED for execution) */
733     if (!initialized) {
734     	if (!ReadMacroString(window, CommonMacros,
735 	    	"smart indent common initialization macros"))
736     	    return;
737 	initialized = True;
738     }
739     if (indentMacros->initMacro != NULL) {
740 	if (!ReadMacroString(window, indentMacros->initMacro,
741     	    	"smart indent initialization macro"))
742     	    return;
743     }
744 
745     /* Compile the newline and modify macros and attach them to the window */
746     winData = (windowSmartIndentData *)NEditMalloc(sizeof(windowSmartIndentData));
747     winData->inNewLineMacro = 0;
748     winData->inModMacro = 0;
749     winData->newlineMacro = ParseMacro(indentMacros->newlineMacro, &errMsg,
750     	    &stoppedAt);
751     if (winData->newlineMacro == NULL) {
752         NEditFree(winData);
753     	ParseError(window->shell, indentMacros->newlineMacro, stoppedAt,
754     	    	"newline macro", errMsg);
755     	return;
756     }
757     if (indentMacros->modMacro == NULL)
758     	winData->modMacro = NULL;
759     else {
760     	winData->modMacro = ParseMacro(indentMacros->modMacro, &errMsg,
761     	    	&stoppedAt);
762     	if (winData->modMacro == NULL) {
763             FreeProgram(winData->newlineMacro);
764             NEditFree(winData);
765     	    ParseError(window->shell, indentMacros->modMacro, stoppedAt,
766     	    	    "smart indent modify macro", errMsg);
767     	    return;
768     	}
769     }
770     window->smartIndentData = (void *)winData;
771 }
772 
EndSmartIndent(WindowInfo * window)773 void EndSmartIndent(WindowInfo *window)
774 {
775     windowSmartIndentData *winData =
776     	    (windowSmartIndentData *)window->smartIndentData;
777 
778     if (winData == NULL)
779     	return;
780 
781     /* Free programs and allocated data */
782     if (winData->modMacro != NULL)
783     	FreeProgram(winData->modMacro);
784     FreeProgram(winData->newlineMacro);
785     NEditFree(winData);
786     window->smartIndentData = NULL;
787 }
788 
789 /*
790 ** Returns true if there are smart indent macros for a named language
791 */
SmartIndentMacrosAvailable(char * languageModeName)792 int SmartIndentMacrosAvailable(char *languageModeName)
793 {
794     return findIndentSpec(languageModeName) != NULL;
795 }
796 
797 /*
798 ** Attaches to the text widget's smart-indent callback to invoke a user
799 ** defined macro when the text widget requires an indent (not just when the
800 ** user types a newline, but also when the widget does an auto-wrap with
801 ** auto-indent on), or the user types some other character.
802 */
SmartIndentCB(Widget w,XtPointer clientData,XtPointer callData)803 void SmartIndentCB(Widget w, XtPointer clientData, XtPointer callData)
804 {
805     WindowInfo *window = WidgetToWindow(w);
806     smartIndentCBStruct *cbInfo = (smartIndentCBStruct *)callData;
807 
808     if (window->smartIndentData == NULL)
809     	return;
810     if (cbInfo->reason == CHAR_TYPED)
811 	executeModMacro(window, cbInfo);
812     else if (cbInfo->reason == NEWLINE_INDENT_NEEDED)
813 	executeNewlineMacro(window, cbInfo);
814 }
815 
816 /*
817 ** Run the newline macro with information from the smart-indent callback
818 ** structure passed by the widget
819 */
executeNewlineMacro(WindowInfo * window,smartIndentCBStruct * cbInfo)820 static void executeNewlineMacro(WindowInfo *window, smartIndentCBStruct *cbInfo)
821 {
822     windowSmartIndentData *winData =
823     	    (windowSmartIndentData *)window->smartIndentData;
824     /* posValue probably shouldn't be static due to re-entrance issues <slobasso> */
825     static DataValue posValue = {INT_TAG, {0}};
826     DataValue result;
827     RestartData *continuation;
828     char *errMsg;
829     int stat;
830 
831     /* Beware of recursion: the newline macro may insert a string which
832        triggers the newline macro to be called again and so on. Newline
833        macros shouldn't insert strings, but nedit must not crash either if
834        they do. */
835     if (winData->inNewLineMacro)
836 	return;
837 
838     /* Call newline macro with the position at which to add newline/indent */
839     posValue.val.n = cbInfo->pos;
840     ++(winData->inNewLineMacro);
841     stat = ExecuteMacro(window, winData->newlineMacro, 1, &posValue, &result,
842     	    &continuation, &errMsg);
843 
844     /* Don't allow preemption or time limit.  Must get return value */
845     while (stat == MACRO_TIME_LIMIT)
846     	stat = ContinueMacro(continuation, &result, &errMsg);
847 
848     --(winData->inNewLineMacro);
849     /* Collect Garbage.  Note that the mod macro does not collect garbage,
850        (because collecting per-line is more efficient than per-character)
851        but GC now depends on the newline macro being mandatory */
852     SafeGC();
853 
854     /* Process errors in macro execution */
855     if (stat == MACRO_PREEMPT || stat == MACRO_ERROR)
856     {
857         DialogF(DF_ERR, window->shell, 1, "Smart Indent",
858                 "Error in smart indent macro:\n%s", "OK",
859                 stat == MACRO_ERROR
860                         ? errMsg
861                         : "dialogs and shell commands not permitted");
862         EndSmartIndent(window);
863         return;
864     }
865 
866     /* Validate and return the result */
867     if (result.tag != INT_TAG || result.val.n < -1 || result.val.n > 1000)
868     {
869         DialogF(DF_ERR, window->shell, 1, "Smart Indent",
870                 "Smart indent macros must return\ninteger indent distance",
871                 "OK");
872         EndSmartIndent(window);
873         return;
874     }
875 
876     cbInfo->indentRequest = result.val.n;
877 }
878 
879 
InSmartIndentMacros(WindowInfo * window)880 Boolean InSmartIndentMacros(WindowInfo *window) {
881     windowSmartIndentData *winData =
882     	    (windowSmartIndentData *)window->smartIndentData;
883 
884 	return((winData && (winData->inModMacro || winData->inNewLineMacro)));
885 }
886 
887 /*
888 ** Run the modification macro with information from the smart-indent callback
889 ** structure passed by the widget
890 */
executeModMacro(WindowInfo * window,smartIndentCBStruct * cbInfo)891 static void executeModMacro(WindowInfo *window,smartIndentCBStruct *cbInfo)
892 {
893     windowSmartIndentData *winData =
894     	    (windowSmartIndentData *)window->smartIndentData;
895     /* args probably shouldn't be static due to future re-entrance issues <slobasso> */
896     static DataValue args[2] = {{INT_TAG, {0}}, {STRING_TAG, {0}}};
897     /* after 5.2 release remove inModCB and use new winData->inModMacro value */
898     static int inModCB = False;
899     DataValue result;
900     RestartData *continuation;
901     char *errMsg;
902     int stat;
903 
904     /* Check for inappropriate calls and prevent re-entering if the macro
905        makes a buffer modification */
906     if (winData == NULL || winData->modMacro == NULL || inModCB)
907     	return;
908 
909     /* Call modification macro with the position of the modification,
910        and the character(s) inserted.  Don't allow
911        preemption or time limit.  Execution must not overlap or re-enter */
912     args[0].val.n = cbInfo->pos;
913     AllocNStringCpy(&args[1].val.str, cbInfo->charsTyped);
914 
915     inModCB = True;
916     ++(winData->inModMacro);
917 
918     stat = ExecuteMacro(window, winData->modMacro, 2, args, &result,
919         &continuation, &errMsg);
920     while (stat == MACRO_TIME_LIMIT)
921         stat = ContinueMacro(continuation, &result, &errMsg);
922 
923     --(winData->inModMacro);
924     inModCB = False;
925 
926     /* Process errors in macro execution */
927     if (stat == MACRO_PREEMPT || stat == MACRO_ERROR)
928     {
929         DialogF(DF_ERR, window->shell, 1, "Smart Indent",
930                 "Error in smart indent modification macro:\n%s", "OK",
931                 stat == MACRO_ERROR
932                         ? errMsg
933                         : "dialogs and shell commands not permitted");
934         EndSmartIndent(window);
935         return;
936     }
937 }
938 
EditSmartIndentMacros(WindowInfo * window)939 void EditSmartIndentMacros(WindowInfo *window)
940 {
941 #define BORDER 4
942     Widget form, lmOptMenu, lmForm, lmBtn;
943     Widget okBtn, applyBtn, checkBtn, deleteBtn, commonBtn;
944     Widget closeBtn, helpBtn, restoreBtn, pane;
945     Widget initForm, newlineForm, modifyForm;
946     Widget initLbl, newlineLbl, modifyLbl;
947     XmString s1;
948     char *lmName;
949     Arg args[20];
950     int n;
951 
952     /* if the dialog is already displayed, just pop it to the top and return */
953     if (SmartIndentDialog.shell != NULL) {
954     	RaiseDialogWindow(SmartIndentDialog.shell);
955     	return;
956     }
957 
958     if (LanguageModeName(0) == NULL)
959     {
960         DialogF(DF_WARN, window->shell, 1, "Language Mode",
961                 "No Language Modes defined", "OK");
962         return;
963     }
964 
965     /* Decide on an initial language mode */
966     lmName = LanguageModeName(window->languageMode == PLAIN_LANGUAGE_MODE ? 0 :
967     	    window->languageMode);
968     SmartIndentDialog.langModeName = NEditStrdup(lmName);
969 
970     /* Create a form widget in an application shell */
971     n = 0;
972     XtSetArg(args[n], XmNdeleteResponse, XmDO_NOTHING); n++;
973     XtSetArg(args[n], XmNiconName, "NEdit Smart Indent Macros"); n++;
974     XtSetArg(args[n], XmNtitle, "Program Smart Indent Macros"); n++;
975     SmartIndentDialog.shell = CreateWidget(TheAppShell, "smartIndent",
976 	    topLevelShellWidgetClass, args, n);
977     AddSmallIcon(SmartIndentDialog.shell);
978     form = XtVaCreateManagedWidget("editSmartIndentMacros", xmFormWidgetClass,
979 	    SmartIndentDialog.shell, XmNautoUnmanage, False,
980 	    XmNresizePolicy, XmRESIZE_NONE, NULL);
981     XtAddCallback(form, XmNdestroyCallback, destroyCB, NULL);
982     AddMotifCloseCallback(SmartIndentDialog.shell, closeCB, NULL);
983 
984     lmForm = XtVaCreateManagedWidget("lmForm", xmFormWidgetClass,
985     	    form,
986 	    XmNleftAttachment, XmATTACH_POSITION,
987 	    XmNleftPosition, 1,
988 	    XmNtopAttachment, XmATTACH_POSITION,
989 	    XmNtopPosition, 1,
990 	    XmNrightAttachment, XmATTACH_POSITION,
991 	    XmNrightPosition, 99, NULL);
992 
993     SmartIndentDialog.lmPulldown = CreateLanguageModeMenu(lmForm, langModeCB,
994     	    NULL);
995     n = 0;
996     XtSetArg(args[n], XmNspacing, 0); n++;
997     XtSetArg(args[n], XmNmarginWidth, 0); n++;
998     XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
999     XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
1000     XtSetArg(args[n], XmNleftPosition, 50); n++;
1001     XtSetArg(args[n], XmNsubMenuId, SmartIndentDialog.lmPulldown); n++;
1002     lmOptMenu = XmCreateOptionMenu(lmForm, "langModeOptMenu", args, n);
1003     XtManageChild(lmOptMenu);
1004     SmartIndentDialog.lmOptMenu = lmOptMenu;
1005 
1006     XtVaCreateManagedWidget("lmLbl", xmLabelGadgetClass, lmForm,
1007     	    XmNlabelString, s1=XmStringCreateSimple("Language Mode:"),
1008     	    XmNmnemonic, 'L',
1009     	    XmNuserData, XtParent(SmartIndentDialog.lmOptMenu),
1010     	    XmNalignment, XmALIGNMENT_END,
1011 	    XmNrightAttachment, XmATTACH_POSITION,
1012 	    XmNrightPosition, 50,
1013 	    XmNtopAttachment, XmATTACH_FORM,
1014 	    XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET,
1015 	    XmNbottomWidget, lmOptMenu, NULL);
1016     XmStringFree(s1);
1017 
1018     lmBtn = XtVaCreateManagedWidget("lmBtn", xmPushButtonWidgetClass, lmForm,
1019     	    XmNlabelString, s1=MKSTRING("Add / Modify\nLanguage Mode..."),
1020     	    XmNmnemonic, 'A',
1021     	    XmNrightAttachment, XmATTACH_FORM,
1022     	    XmNtopAttachment, XmATTACH_FORM, NULL);
1023     XtAddCallback(lmBtn, XmNactivateCallback, lmDialogCB, NULL);
1024     XmStringFree(s1);
1025 
1026     commonBtn = XtVaCreateManagedWidget("commonBtn", xmPushButtonWidgetClass,
1027     	    lmForm,
1028     	    XmNlabelString, s1=MKSTRING("Common / Shared\nInitialization..."),
1029     	    XmNmnemonic, 'C',
1030     	    XmNleftAttachment, XmATTACH_FORM,
1031     	    XmNtopAttachment, XmATTACH_FORM, NULL);
1032     XtAddCallback(commonBtn, XmNactivateCallback, commonDialogCB, NULL);
1033     XmStringFree(s1);
1034 
1035     okBtn = XtVaCreateManagedWidget("ok", xmPushButtonWidgetClass, form,
1036             XmNlabelString, s1=XmStringCreateSimple("OK"),
1037             XmNmarginWidth, BUTTON_WIDTH_MARGIN,
1038     	    XmNleftAttachment, XmATTACH_POSITION,
1039     	    XmNleftPosition, 1,
1040     	    XmNrightAttachment, XmATTACH_POSITION,
1041     	    XmNrightPosition, 13,
1042     	    XmNbottomAttachment, XmATTACH_FORM,
1043     	    XmNbottomOffset, BORDER, NULL);
1044     XtAddCallback(okBtn, XmNactivateCallback, okCB, NULL);
1045     XmStringFree(s1);
1046 
1047     applyBtn = XtVaCreateManagedWidget("apply", xmPushButtonWidgetClass, form,
1048     	    XmNlabelString, s1=XmStringCreateSimple("Apply"),
1049     	    XmNmnemonic, 'y',
1050     	    XmNleftAttachment, XmATTACH_POSITION,
1051     	    XmNleftPosition, 13,
1052     	    XmNrightAttachment, XmATTACH_POSITION,
1053     	    XmNrightPosition, 26,
1054     	    XmNbottomAttachment, XmATTACH_FORM,
1055     	    XmNbottomOffset, BORDER, NULL);
1056     XtAddCallback(applyBtn, XmNactivateCallback, applyCB, NULL);
1057     XmStringFree(s1);
1058 
1059     checkBtn = XtVaCreateManagedWidget("check", xmPushButtonWidgetClass, form,
1060     	    XmNlabelString, s1=XmStringCreateSimple("Check"),
1061     	    XmNmnemonic, 'k',
1062     	    XmNleftAttachment, XmATTACH_POSITION,
1063     	    XmNleftPosition, 26,
1064     	    XmNrightAttachment, XmATTACH_POSITION,
1065     	    XmNrightPosition, 39,
1066     	    XmNbottomAttachment, XmATTACH_FORM,
1067     	    XmNbottomOffset, BORDER, NULL);
1068     XtAddCallback(checkBtn, XmNactivateCallback, checkCB, NULL);
1069     XmStringFree(s1);
1070 
1071     deleteBtn = XtVaCreateManagedWidget("delete", xmPushButtonWidgetClass, form,
1072     	    XmNlabelString, s1=XmStringCreateSimple("Delete"),
1073     	    XmNmnemonic, 'D',
1074     	    XmNleftAttachment, XmATTACH_POSITION,
1075     	    XmNleftPosition, 39,
1076     	    XmNrightAttachment, XmATTACH_POSITION,
1077     	    XmNrightPosition, 52,
1078     	    XmNbottomAttachment, XmATTACH_FORM,
1079     	    XmNbottomOffset, BORDER, NULL);
1080     XtAddCallback(deleteBtn, XmNactivateCallback, deleteCB, NULL);
1081     XmStringFree(s1);
1082 
1083     restoreBtn = XtVaCreateManagedWidget("restore", xmPushButtonWidgetClass, form,
1084     	    XmNlabelString, s1=XmStringCreateSimple("Restore Defaults"),
1085     	    XmNmnemonic, 'f',
1086     	    XmNleftAttachment, XmATTACH_POSITION,
1087     	    XmNleftPosition, 52,
1088     	    XmNrightAttachment, XmATTACH_POSITION,
1089     	    XmNrightPosition, 73,
1090     	    XmNbottomAttachment, XmATTACH_FORM,
1091     	    XmNbottomOffset, BORDER, NULL);
1092     XtAddCallback(restoreBtn, XmNactivateCallback, restoreCB, NULL);
1093     XmStringFree(s1);
1094 
1095     closeBtn = XtVaCreateManagedWidget("close", xmPushButtonWidgetClass,
1096     	    form,
1097             XmNlabelString, s1=XmStringCreateSimple("Close"),
1098     	    XmNleftAttachment, XmATTACH_POSITION,
1099     	    XmNleftPosition, 73,
1100     	    XmNrightAttachment, XmATTACH_POSITION,
1101     	    XmNrightPosition, 86,
1102     	    XmNbottomAttachment, XmATTACH_FORM,
1103     	    XmNbottomOffset, BORDER, NULL);
1104     XtAddCallback(closeBtn, XmNactivateCallback, closeCB, NULL);
1105     XmStringFree(s1);
1106 
1107     helpBtn = XtVaCreateManagedWidget("help", xmPushButtonWidgetClass,
1108     	    form,
1109     	    XmNlabelString, s1=XmStringCreateSimple("Help"),
1110     	    XmNmnemonic, 'H',
1111     	    XmNleftAttachment, XmATTACH_POSITION,
1112     	    XmNleftPosition, 86,
1113     	    XmNrightAttachment, XmATTACH_POSITION,
1114     	    XmNrightPosition, 99,
1115     	    XmNbottomAttachment, XmATTACH_FORM,
1116     	    XmNbottomOffset, BORDER, NULL);
1117     XtAddCallback(helpBtn, XmNactivateCallback, helpCB, NULL);
1118     XmStringFree(s1);
1119 
1120     pane = XtVaCreateManagedWidget("pane", xmPanedWindowWidgetClass,  form,
1121    	    XmNleftAttachment, XmATTACH_POSITION,
1122     	    XmNleftPosition, 1,
1123    	    XmNrightAttachment, XmATTACH_POSITION,
1124     	    XmNrightPosition, 99,
1125 	    XmNtopAttachment, XmATTACH_WIDGET,
1126 	    XmNtopWidget, lmForm,
1127 	    XmNbottomAttachment, XmATTACH_WIDGET,
1128 	    XmNbottomWidget, okBtn, NULL);
1129      	    /* XmNmarginWidth, 0, XmNmarginHeight, 0, XmNseparatorOn, False,
1130     	    XmNspacing, 3, XmNsashIndent, -2, */
1131 
1132     initForm = XtVaCreateManagedWidget("initForm", xmFormWidgetClass,
1133 	    pane, NULL);
1134     initLbl = XtVaCreateManagedWidget("initLbl", xmLabelGadgetClass, initForm,
1135     	    XmNlabelString, s1=XmStringCreateSimple(
1136     	     "Language Specific Initialization Macro Commands and Definitions"),
1137     	    XmNmnemonic, 'I', NULL);
1138     XmStringFree(s1);
1139     n = 0;
1140     XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++;
1141     XtSetArg(args[n], XmNrows, 5); n++;
1142     XtSetArg(args[n], XmNcolumns, 80); n++;
1143     XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
1144     XtSetArg(args[n], XmNtopWidget, initLbl); n++;
1145     XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
1146     XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
1147     XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
1148     SmartIndentDialog.initMacro = XmCreateScrolledText(initForm,
1149     	    "initMacro", args, n);
1150     AddMouseWheelSupport(SmartIndentDialog.initMacro);
1151     XtManageChild(SmartIndentDialog.initMacro);
1152     RemapDeleteKey(SmartIndentDialog.initMacro);
1153     XtVaSetValues(initLbl, XmNuserData, SmartIndentDialog.initMacro, NULL);
1154 
1155     newlineForm = XtVaCreateManagedWidget("newlineForm", xmFormWidgetClass,
1156 	    pane, NULL);
1157     newlineLbl = XtVaCreateManagedWidget("newlineLbl", xmLabelGadgetClass,
1158     	    newlineForm,
1159     	    XmNlabelString, s1=XmStringCreateSimple("Newline Macro"),
1160     	    XmNmnemonic, 'N', NULL);
1161     XmStringFree(s1);
1162     XtVaCreateManagedWidget("newlineArgsLbl", xmLabelGadgetClass,
1163     	    newlineForm, XmNalignment, XmALIGNMENT_END,
1164     	    XmNlabelString, s1=XmStringCreateSimple(
1165 	       "($1 is insert position, return indent request or -1)"),
1166 	    XmNrightAttachment, XmATTACH_FORM, NULL);
1167     XmStringFree(s1);
1168     n = 0;
1169     XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++;
1170     XtSetArg(args[n], XmNrows, 5); n++;
1171     XtSetArg(args[n], XmNcolumns, 80); n++;
1172     XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
1173     XtSetArg(args[n], XmNtopWidget, newlineLbl); n++;
1174     XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
1175     XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
1176     XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
1177     SmartIndentDialog.newlineMacro = XmCreateScrolledText(newlineForm,
1178     	    "newlineMacro", args, n);
1179     AddMouseWheelSupport(SmartIndentDialog.newlineMacro);
1180     XtManageChild(SmartIndentDialog.newlineMacro);
1181     RemapDeleteKey(SmartIndentDialog.newlineMacro);
1182     XtVaSetValues(newlineLbl, XmNuserData, SmartIndentDialog.newlineMacro,NULL);
1183 
1184     modifyForm = XtVaCreateManagedWidget("modifyForm", xmFormWidgetClass,
1185 	    pane, NULL);
1186     modifyLbl = XtVaCreateManagedWidget("modifyLbl", xmLabelGadgetClass,
1187     	    modifyForm, XmNlabelString,s1=XmStringCreateSimple("Type-in Macro"),
1188     	    XmNmnemonic, 'M', NULL);
1189     XmStringFree(s1);
1190     XtVaCreateManagedWidget("modifyArgsLbl", xmLabelGadgetClass,
1191     	    modifyForm, XmNalignment, XmALIGNMENT_END,
1192     	    XmNlabelString, s1=XmStringCreateSimple(
1193 	        "($1 is position, $2 is character to be inserted)"),
1194 	    XmNrightAttachment, XmATTACH_FORM, NULL);
1195     XmStringFree(s1);
1196     n = 0;
1197     XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++;
1198     XtSetArg(args[n], XmNrows, 5); n++;
1199     XtSetArg(args[n], XmNcolumns, 80); n++;
1200     XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
1201     XtSetArg(args[n], XmNtopWidget, modifyLbl); n++;
1202     XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
1203     XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
1204     XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
1205     SmartIndentDialog.modMacro = XmCreateScrolledText(modifyForm,
1206     	    "modifyMacro", args, n);
1207     AddMouseWheelSupport(SmartIndentDialog.modMacro);
1208     XtManageChild(SmartIndentDialog.modMacro);
1209     RemapDeleteKey(SmartIndentDialog.modMacro);
1210     XtVaSetValues(modifyLbl, XmNuserData, SmartIndentDialog.modMacro, NULL);
1211 
1212     /* Set initial default button */
1213     XtVaSetValues(form, XmNdefaultButton, okBtn, NULL);
1214     XtVaSetValues(form, XmNcancelButton, closeBtn, NULL);
1215 
1216     /* Handle mnemonic selection of buttons and focus to dialog */
1217     AddDialogMnemonicHandler(form, FALSE);
1218 
1219     /* Fill in the dialog information for the selected language mode */
1220     setSmartIndentDialogData(findIndentSpec(lmName));
1221     SetLangModeMenu(SmartIndentDialog.lmOptMenu,SmartIndentDialog.langModeName);
1222 
1223     /* Realize all of the widgets in the new dialog */
1224     RealizeWithoutForcingPosition(SmartIndentDialog.shell);
1225 }
1226 
destroyCB(Widget w,XtPointer clientData,XtPointer callData)1227 static void destroyCB(Widget w, XtPointer clientData, XtPointer callData)
1228 {
1229     NEditFree(SmartIndentDialog.langModeName);
1230     SmartIndentDialog.shell = NULL;
1231 }
1232 
langModeCB(Widget w,XtPointer clientData,XtPointer callData)1233 static void langModeCB(Widget w, XtPointer clientData, XtPointer callData)
1234 {
1235     char *modeName;
1236     int i, resp;
1237     static smartIndentRec emptyIndentSpec = {NULL, NULL, NULL, NULL};
1238     smartIndentRec *oldMacros, *newMacros;
1239 
1240     /* Get the newly selected mode name.  If it's the same, do nothing */
1241     XtVaGetValues(w, XmNuserData, &modeName, NULL);
1242     if (!strcmp(modeName, SmartIndentDialog.langModeName))
1243     	return;
1244 
1245     /* Find the original macros */
1246     for (i=0; i<NSmartIndentSpecs; i++)
1247     	if (!strcmp(SmartIndentDialog.langModeName,SmartIndentSpecs[i]->lmName))
1248 	    break;
1249     oldMacros = i == NSmartIndentSpecs ? &emptyIndentSpec : SmartIndentSpecs[i];
1250 
1251     /* Check if the macros have changed, if so allow user to apply, discard,
1252        or cancel */
1253     newMacros = getSmartIndentDialogData();
1254     if (indentSpecsDiffer(oldMacros, newMacros))
1255     {
1256         resp = DialogF(DF_QUES, SmartIndentDialog.shell, 3, "Smart Indent",
1257                 "Smart indent macros for language mode\n"
1258                 "%s were changed.  Apply changes?", "Apply", "Discard",
1259                 "Cancel", SmartIndentDialog.langModeName);
1260 
1261         if (resp == 3)
1262         {
1263             SetLangModeMenu(SmartIndentDialog.lmOptMenu,
1264             SmartIndentDialog.langModeName);
1265             return;
1266         } else if (resp == 1)
1267         {
1268             if (checkSmartIndentDialogData())
1269             {
1270                 if (oldMacros == &emptyIndentSpec)
1271                 {
1272                     SmartIndentSpecs[NSmartIndentSpecs++]
1273                             = copyIndentSpec(newMacros);
1274                 } else
1275                 {
1276                     freeIndentSpec(oldMacros);
1277                     SmartIndentSpecs[i] = copyIndentSpec(newMacros);
1278                 }
1279             } else
1280             {
1281                 SetLangModeMenu(SmartIndentDialog.lmOptMenu,
1282                 SmartIndentDialog.langModeName);
1283                 return;
1284             }
1285         }
1286     }
1287     freeIndentSpec(newMacros);
1288 
1289     /* Fill the dialog with the new language mode information */
1290     SmartIndentDialog.langModeName = NEditStrdup(modeName);
1291     setSmartIndentDialogData(findIndentSpec(modeName));
1292 }
1293 
lmDialogCB(Widget w,XtPointer clientData,XtPointer callData)1294 static void lmDialogCB(Widget w, XtPointer clientData, XtPointer callData)
1295 {
1296     EditLanguageModes();
1297 }
1298 
commonDialogCB(Widget w,XtPointer clientData,XtPointer callData)1299 static void commonDialogCB(Widget w, XtPointer clientData, XtPointer callData)
1300 {
1301     EditCommonSmartIndentMacro();
1302 }
1303 
okCB(Widget w,XtPointer clientData,XtPointer callData)1304 static void okCB(Widget w, XtPointer clientData, XtPointer callData)
1305 {
1306     /* change the macro */
1307     if (!updateSmartIndentData())
1308     	return;
1309 
1310     /* pop down and destroy the dialog */
1311     CloseAllPopupsFor(SmartIndentDialog.shell);
1312     XtDestroyWidget(SmartIndentDialog.shell);
1313 }
1314 
applyCB(Widget w,XtPointer clientData,XtPointer callData)1315 static void applyCB(Widget w, XtPointer clientData, XtPointer callData)
1316 {
1317     /* change the patterns */
1318     updateSmartIndentData();
1319 }
1320 
checkCB(Widget w,XtPointer clientData,XtPointer callData)1321 static void checkCB(Widget w, XtPointer clientData, XtPointer callData)
1322 {
1323     if (checkSmartIndentDialogData())
1324         DialogF(DF_INF, SmartIndentDialog.shell, 1, "Macro compiled",
1325                 "Macros compiled without error", "OK");
1326 }
1327 
restoreCB(Widget w,XtPointer clientData,XtPointer callData)1328 static void restoreCB(Widget w, XtPointer clientData, XtPointer callData)
1329 {
1330    int i;
1331    smartIndentRec *defaultIS;
1332 
1333     /* Find the default indent spec */
1334     for (i=0; i<N_DEFAULT_INDENT_SPECS; i++)
1335     {
1336         if (!strcmp(SmartIndentDialog.langModeName,
1337                 DefaultIndentSpecs[i].lmName))
1338         {
1339             break;
1340         }
1341     }
1342 
1343     if (i == N_DEFAULT_INDENT_SPECS)
1344     {
1345         DialogF(DF_WARN, SmartIndentDialog.shell, 1, "Smart Indent",
1346                 "There are no default indent macros\nfor language mode %s",
1347                 "OK", SmartIndentDialog.langModeName);
1348         return;
1349     }
1350     defaultIS = &DefaultIndentSpecs[i];
1351 
1352     if (DialogF(DF_WARN, SmartIndentDialog.shell, 2, "Discard Changes",
1353             "Are you sure you want to discard\n"
1354             "all changes to smart indent macros\n"
1355             "for language mode %s?", "Discard", "Cancel",
1356             SmartIndentDialog.langModeName) == 2)
1357     {
1358         return;
1359     }
1360 
1361     /* if a stored version of the indent macros exist, replace them, if not,
1362        add a new one */
1363     for (i=0; i<NSmartIndentSpecs; i++)
1364     	if (!strcmp(SmartIndentDialog.langModeName,SmartIndentSpecs[i]->lmName))
1365 	    break;
1366     if (i < NSmartIndentSpecs) {
1367      	freeIndentSpec(SmartIndentSpecs[i]);
1368    	SmartIndentSpecs[i] = copyIndentSpec(defaultIS);
1369     } else
1370     	SmartIndentSpecs[NSmartIndentSpecs++] = copyIndentSpec(defaultIS);
1371 
1372     /* Update the dialog */
1373     setSmartIndentDialogData(defaultIS);
1374 }
1375 
deleteCB(Widget w,XtPointer clientData,XtPointer callData)1376 static void deleteCB(Widget w, XtPointer clientData, XtPointer callData)
1377 {
1378     int i;
1379 
1380     if (DialogF(DF_WARN, SmartIndentDialog.shell, 2, "Delete Macros",
1381             "Are you sure you want to delete smart indent\n"
1382             "macros for language mode %s?", "Yes, Delete", "Cancel",
1383             SmartIndentDialog.langModeName) == 2)
1384     {
1385         return;
1386     }
1387 
1388     /* if a stored version of the pattern set exists, delete it from the list */
1389     for (i=0; i<NSmartIndentSpecs; i++)
1390     	if (!strcmp(SmartIndentDialog.langModeName,SmartIndentSpecs[i]->lmName))
1391 	    break;
1392     if (i < NSmartIndentSpecs) {
1393      	freeIndentSpec(SmartIndentSpecs[i]);
1394    	memmove(&SmartIndentSpecs[i], &SmartIndentSpecs[i+1],
1395    	    	(NSmartIndentSpecs-1 - i) * sizeof(smartIndentRec *));
1396     	NSmartIndentSpecs--;
1397     }
1398 
1399     /* Clear out the dialog */
1400     setSmartIndentDialogData(NULL);
1401 }
1402 
closeCB(Widget widget,XtPointer clientData,XtPointer callData)1403 static void closeCB(Widget widget, XtPointer clientData, XtPointer callData)
1404 {
1405     /* pop down and destroy the dialog */
1406     CloseAllPopupsFor(SmartIndentDialog.shell);
1407     XtDestroyWidget(SmartIndentDialog.shell);
1408 }
1409 
helpCB(Widget w,XtPointer clientData,XtPointer callData)1410 static void helpCB(Widget w, XtPointer clientData, XtPointer callData)
1411 {
1412     Help(HELP_SMART_INDENT);
1413 }
1414 
checkSmartIndentDialogData(void)1415 static int checkSmartIndentDialogData(void)
1416 {
1417     char *widgetText, *errMsg, *stoppedAt;
1418     Program *prog;
1419 
1420     /* Check the initialization macro */
1421     if (!TextWidgetIsBlank(SmartIndentDialog.initMacro)) {
1422 	widgetText =ensureNewline(XmTextGetString(SmartIndentDialog.initMacro));
1423 	if (!CheckMacroString(SmartIndentDialog.shell, widgetText,
1424 		"initialization macro", &stoppedAt)) {
1425     	    XmTextSetInsertionPosition(SmartIndentDialog.initMacro,
1426 		    stoppedAt - widgetText);
1427 	    XmProcessTraversal(SmartIndentDialog.initMacro, XmTRAVERSE_CURRENT);
1428 	    NEditFree(widgetText);
1429 	    return False;
1430 	}
1431 	NEditFree(widgetText);
1432     }
1433 
1434     /* Test compile the newline macro */
1435     if (TextWidgetIsBlank(SmartIndentDialog.newlineMacro))
1436     {
1437         DialogF(DF_WARN, SmartIndentDialog.shell, 1, "Smart Indent",
1438                 "Newline macro required", "OK");
1439         return False;
1440     }
1441 
1442     widgetText = ensureNewline(XmTextGetString(SmartIndentDialog.newlineMacro));
1443     prog = ParseMacro(widgetText, &errMsg, &stoppedAt);
1444     if (prog == NULL) {
1445  	ParseError(SmartIndentDialog.shell, widgetText, stoppedAt,
1446     	    	"newline macro", errMsg);
1447      	XmTextSetInsertionPosition(SmartIndentDialog.newlineMacro,
1448 		stoppedAt - widgetText);
1449 	XmProcessTraversal(SmartIndentDialog.newlineMacro, XmTRAVERSE_CURRENT);
1450 	NEditFree(widgetText);
1451     	return False;
1452     }
1453     NEditFree(widgetText);
1454     FreeProgram(prog);
1455 
1456     /* Test compile the modify macro */
1457     if (!TextWidgetIsBlank(SmartIndentDialog.modMacro)) {
1458     	widgetText = ensureNewline(XmTextGetString(SmartIndentDialog.modMacro));
1459     	prog = ParseMacro(widgetText, &errMsg, &stoppedAt);
1460 	if (prog == NULL) {
1461     	    ParseError(SmartIndentDialog.shell, widgetText, stoppedAt,
1462     	    	    "modify macro", errMsg);
1463      	    XmTextSetInsertionPosition(SmartIndentDialog.modMacro,
1464 		    stoppedAt - widgetText);
1465 	    XmProcessTraversal(SmartIndentDialog.modMacro, XmTRAVERSE_CURRENT);
1466 	    NEditFree(widgetText);
1467     	    return False;
1468     	}
1469 	NEditFree(widgetText);
1470 	FreeProgram(prog);
1471     }
1472     return True;
1473 }
1474 
getSmartIndentDialogData(void)1475 static smartIndentRec *getSmartIndentDialogData(void)
1476 {
1477     smartIndentRec *is;
1478 
1479     is = (smartIndentRec *)NEditMalloc(sizeof(smartIndentRec));
1480     is->lmName = NEditStrdup(SmartIndentDialog.langModeName);
1481     is->initMacro = TextWidgetIsBlank(SmartIndentDialog.initMacro) ? NULL :
1482 	    ensureNewline(XmTextGetString(SmartIndentDialog.initMacro));
1483     is->newlineMacro = TextWidgetIsBlank(SmartIndentDialog.newlineMacro) ? NULL:
1484 	    ensureNewline(XmTextGetString(SmartIndentDialog.newlineMacro));
1485     is->modMacro = TextWidgetIsBlank(SmartIndentDialog.modMacro) ? NULL :
1486 	    ensureNewline(XmTextGetString(SmartIndentDialog.modMacro));
1487     return is;
1488 }
1489 
setSmartIndentDialogData(smartIndentRec * is)1490 static void setSmartIndentDialogData(smartIndentRec *is)
1491 {
1492     if (is == NULL) {
1493 	XmTextSetString(SmartIndentDialog.initMacro, "");
1494 	XmTextSetString(SmartIndentDialog.newlineMacro, "");
1495 	XmTextSetString(SmartIndentDialog.modMacro, "");
1496     } else {
1497 	if (is->initMacro == NULL)
1498 	    XmTextSetString(SmartIndentDialog.initMacro, "");
1499 	else
1500 	    XmTextSetString(SmartIndentDialog.initMacro, is->initMacro);
1501 	XmTextSetString(SmartIndentDialog.newlineMacro, is->newlineMacro);
1502 	if (is->modMacro == NULL)
1503 	    XmTextSetString(SmartIndentDialog.modMacro, "");
1504 	else
1505 	    XmTextSetString(SmartIndentDialog.modMacro, is->modMacro);
1506     }
1507 }
1508 
EditCommonSmartIndentMacro(void)1509 void EditCommonSmartIndentMacro(void)
1510 {
1511 #define VERT_BORDER 4
1512     Widget form, topLbl;
1513     Widget okBtn, applyBtn, checkBtn;
1514     Widget closeBtn, restoreBtn;
1515     XmString s1;
1516     Arg args[20];
1517     int n;
1518 
1519     /* if the dialog is already displayed, just pop it to the top and return */
1520     if (CommonDialog.shell != NULL) {
1521     	RaiseDialogWindow(CommonDialog.shell);
1522     	return;
1523     }
1524 
1525     /* Create a form widget in an application shell */
1526     n = 0;
1527     XtSetArg(args[n], XmNdeleteResponse, XmDO_NOTHING); n++;
1528     XtSetArg(args[n], XmNiconName, "NEdit Common Smart Indent Macros"); n++;
1529     XtSetArg(args[n], XmNtitle, "Common Smart Indent Macros"); n++;
1530     CommonDialog.shell = CreateWidget(TheAppShell, "smartIndent",
1531 	    topLevelShellWidgetClass, args, n);
1532     AddSmallIcon(CommonDialog.shell);
1533     form = XtVaCreateManagedWidget("editCommonSIMacros", xmFormWidgetClass,
1534 	    CommonDialog.shell, XmNautoUnmanage, False,
1535 	    XmNresizePolicy, XmRESIZE_NONE, NULL);
1536     XtAddCallback(form, XmNdestroyCallback, comDestroyCB, NULL);
1537     AddMotifCloseCallback(CommonDialog.shell, comCloseCB, NULL);
1538 
1539     topLbl = XtVaCreateManagedWidget("topLbl", xmLabelGadgetClass, form,
1540     	    XmNlabelString, s1=XmStringCreateSimple(
1541 	        "Common Definitions for Smart Indent Macros"),
1542     	    XmNmnemonic, 'C',
1543 	    XmNtopAttachment, XmATTACH_FORM,
1544 	    XmNtopOffset, VERT_BORDER,
1545 	    XmNleftAttachment, XmATTACH_POSITION,
1546 	    XmNleftPosition, 1, NULL);
1547 
1548     okBtn = XtVaCreateManagedWidget("ok", xmPushButtonWidgetClass, form,
1549             XmNlabelString, s1=XmStringCreateSimple("OK"),
1550             XmNmarginWidth, BUTTON_WIDTH_MARGIN,
1551     	    XmNleftAttachment, XmATTACH_POSITION,
1552     	    XmNleftPosition, 6,
1553     	    XmNrightAttachment, XmATTACH_POSITION,
1554     	    XmNrightPosition, 18,
1555     	    XmNbottomAttachment, XmATTACH_FORM,
1556     	    XmNbottomOffset, VERT_BORDER, NULL);
1557     XtAddCallback(okBtn, XmNactivateCallback, comOKCB, NULL);
1558     XmStringFree(s1);
1559 
1560     applyBtn = XtVaCreateManagedWidget("apply", xmPushButtonWidgetClass, form,
1561     	    XmNlabelString, s1=XmStringCreateSimple("Apply"),
1562     	    XmNmnemonic, 'y',
1563     	    XmNleftAttachment, XmATTACH_POSITION,
1564     	    XmNleftPosition, 22,
1565     	    XmNrightAttachment, XmATTACH_POSITION,
1566     	    XmNrightPosition, 35,
1567     	    XmNbottomAttachment, XmATTACH_FORM,
1568     	    XmNbottomOffset, VERT_BORDER, NULL);
1569     XtAddCallback(applyBtn, XmNactivateCallback, comApplyCB, NULL);
1570     XmStringFree(s1);
1571 
1572     checkBtn = XtVaCreateManagedWidget("check", xmPushButtonWidgetClass, form,
1573     	    XmNlabelString, s1=XmStringCreateSimple("Check"),
1574     	    XmNmnemonic, 'k',
1575     	    XmNleftAttachment, XmATTACH_POSITION,
1576     	    XmNleftPosition, 39,
1577     	    XmNrightAttachment, XmATTACH_POSITION,
1578     	    XmNrightPosition, 52,
1579     	    XmNbottomAttachment, XmATTACH_FORM,
1580     	    XmNbottomOffset, VERT_BORDER, NULL);
1581     XtAddCallback(checkBtn, XmNactivateCallback, comCheckCB, NULL);
1582     XmStringFree(s1);
1583 
1584     restoreBtn = XtVaCreateManagedWidget("restore", xmPushButtonWidgetClass,
1585     form,
1586     	    XmNlabelString, s1=XmStringCreateSimple("Restore Default"),
1587     	    XmNmnemonic, 'f',
1588     	    XmNleftAttachment, XmATTACH_POSITION,
1589     	    XmNleftPosition, 56,
1590     	    XmNrightAttachment, XmATTACH_POSITION,
1591     	    XmNrightPosition, 77,
1592     	    XmNbottomAttachment, XmATTACH_FORM,
1593     	    XmNbottomOffset, VERT_BORDER, NULL);
1594     XtAddCallback(restoreBtn, XmNactivateCallback, comRestoreCB, NULL);
1595     XmStringFree(s1);
1596 
1597     closeBtn = XtVaCreateManagedWidget("close", xmPushButtonWidgetClass,
1598     	    form,
1599     	    XmNlabelString, s1=XmStringCreateSimple("Close"),
1600     	    XmNleftAttachment, XmATTACH_POSITION,
1601     	    XmNleftPosition, 81,
1602     	    XmNrightAttachment, XmATTACH_POSITION,
1603     	    XmNrightPosition, 94,
1604     	    XmNbottomAttachment, XmATTACH_FORM,
1605     	    XmNbottomOffset, VERT_BORDER, NULL);
1606     XtAddCallback(closeBtn, XmNactivateCallback, comCloseCB, NULL);
1607     XmStringFree(s1);
1608 
1609     n = 0;
1610     XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++;
1611     XtSetArg(args[n], XmNrows, 24); n++;
1612     XtSetArg(args[n], XmNcolumns, 80); n++;
1613     XtSetArg(args[n], XmNvalue, CommonMacros); n++;
1614     XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
1615     XtSetArg(args[n], XmNtopWidget, topLbl); n++;
1616     XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
1617     XtSetArg(args[n], XmNleftPosition, 1); n++;
1618     XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
1619     XtSetArg(args[n], XmNrightPosition, 99); n++;
1620     XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
1621     XtSetArg(args[n], XmNbottomWidget, okBtn); n++;
1622     XtSetArg(args[n], XmNbottomOffset, VERT_BORDER); n++;
1623     CommonDialog.text = XmCreateScrolledText(form, "commonText", args, n);
1624     AddMouseWheelSupport(CommonDialog.text);
1625     XtManageChild(CommonDialog.text);
1626     RemapDeleteKey(CommonDialog.text);
1627     XtVaSetValues(topLbl, XmNuserData, CommonDialog.text, NULL);
1628 
1629     /* Set initial default button */
1630     XtVaSetValues(form, XmNdefaultButton, okBtn, NULL);
1631     XtVaSetValues(form, XmNcancelButton, closeBtn, NULL);
1632 
1633     /* Handle mnemonic selection of buttons and focus to dialog */
1634     AddDialogMnemonicHandler(form, FALSE);
1635 
1636     /* Realize all of the widgets in the new dialog */
1637     RealizeWithoutForcingPosition(CommonDialog.shell);
1638 }
1639 
comDestroyCB(Widget w,XtPointer clientData,XtPointer callData)1640 static void comDestroyCB(Widget w, XtPointer clientData, XtPointer callData)
1641 {
1642     CommonDialog.shell = NULL;
1643 }
1644 
comOKCB(Widget w,XtPointer clientData,XtPointer callData)1645 static void comOKCB(Widget w, XtPointer clientData, XtPointer callData)
1646 {
1647     /* change the macro */
1648     if (!updateSmartIndentCommonData())
1649     	return;
1650 
1651     /* pop down and destroy the dialog */
1652     XtDestroyWidget(CommonDialog.shell);
1653 }
1654 
comApplyCB(Widget w,XtPointer clientData,XtPointer callData)1655 static void comApplyCB(Widget w, XtPointer clientData, XtPointer callData)
1656 {
1657     /* change the macro */
1658     updateSmartIndentCommonData();
1659 }
1660 
comCheckCB(Widget w,XtPointer clientData,XtPointer callData)1661 static void comCheckCB(Widget w, XtPointer clientData, XtPointer callData)
1662 {
1663     if (checkSmartIndentCommonDialogData())
1664     {
1665         DialogF(DF_INF, CommonDialog.shell, 1, "Macro compiled",
1666                 "Macros compiled without error", "OK");
1667     }
1668 }
1669 
comRestoreCB(Widget w,XtPointer clientData,XtPointer callData)1670 static void comRestoreCB(Widget w, XtPointer clientData, XtPointer callData)
1671 {
1672     if (DialogF(DF_WARN, CommonDialog.shell, 2, "Discard Changes",
1673             "Are you sure you want to discard all\n"
1674             "changes to common smart indent macros", "Discard", "Cancel") == 2)
1675     {
1676         return;
1677     }
1678 
1679     /* replace common macros with default */
1680     NEditFree(CommonMacros);
1681     CommonMacros = NEditStrdup(DefaultCommonMacros);
1682 
1683     /* Update the dialog */
1684     XmTextSetString(CommonDialog.text, CommonMacros);
1685 }
1686 
comCloseCB(Widget w,XtPointer clientData,XtPointer callData)1687 static void comCloseCB(Widget w, XtPointer clientData, XtPointer callData)
1688 {
1689     /* pop down and destroy the dialog */
1690     XtDestroyWidget(CommonDialog.shell);
1691 }
1692 
1693 /*
1694 ** Update the smart indent macros being edited in the dialog
1695 ** with the information that the dialog is currently displaying, and
1696 ** apply changes to any window which is currently using the macros.
1697 */
updateSmartIndentCommonData(void)1698 static int updateSmartIndentCommonData(void)
1699 {
1700     WindowInfo *window;
1701 
1702     /* Make sure the patterns are valid and compile */
1703     if (!checkSmartIndentCommonDialogData())
1704     	return False;
1705 
1706     /* Get the current data */
1707     CommonMacros = ensureNewline(XmTextGetString(CommonDialog.text));
1708 
1709     /* Re-execute initialization macros (macros require a window to function,
1710        since user could theoretically execute an action routine, but it
1711        probably won't be referenced in a smart indent initialization) */
1712     if (!ReadMacroString(WindowList, CommonMacros, "common macros"))
1713     	return False;
1714 
1715     /* Find windows that are currently using smart indent and
1716        re-initialize the smart indent macros (in case they have initialization
1717        data which depends on common data) */
1718     for (window=WindowList; window!=NULL; window=window->next) {
1719     	if (window->indentStyle == SMART_INDENT &&
1720     		window->languageMode != PLAIN_LANGUAGE_MODE) {
1721     	    EndSmartIndent(window);
1722     	    BeginSmartIndent(window, False);
1723     	}
1724     }
1725 
1726     /* Note that preferences have been changed */
1727     MarkPrefsChanged();
1728 
1729     return True;
1730 }
1731 
checkSmartIndentCommonDialogData(void)1732 static int checkSmartIndentCommonDialogData(void)
1733 {
1734     char *widgetText, *stoppedAt;
1735 
1736     if (!TextWidgetIsBlank(CommonDialog.text)) {
1737 	widgetText = ensureNewline(XmTextGetString(CommonDialog.text));
1738 	if (!CheckMacroString(CommonDialog.shell, widgetText,
1739 		"macros", &stoppedAt)) {
1740     	    XmTextSetInsertionPosition(CommonDialog.text, stoppedAt-widgetText);
1741 	    XmProcessTraversal(CommonDialog.text, XmTRAVERSE_CURRENT);
1742 	    NEditFree(widgetText);
1743 	    return False;
1744 	}
1745 	NEditFree(widgetText);
1746     }
1747     return True;
1748 }
1749 
1750 /*
1751 ** Update the smart indent macros being edited in the dialog
1752 ** with the information that the dialog is currently displaying, and
1753 ** apply changes to any window which is currently using the macros.
1754 */
updateSmartIndentData(void)1755 static int updateSmartIndentData(void)
1756 {
1757     smartIndentRec *newMacros;
1758     WindowInfo *window;
1759     char *lmName;
1760     int i;
1761 
1762     /* Make sure the patterns are valid and compile */
1763     if (!checkSmartIndentDialogData())
1764     	return False;
1765 
1766     /* Get the current data */
1767     newMacros = getSmartIndentDialogData();
1768 
1769     /* Find the original macros */
1770     for (i=0; i<NSmartIndentSpecs; i++)
1771     	if (!strcmp(SmartIndentDialog.langModeName,SmartIndentSpecs[i]->lmName))
1772 	    break;
1773 
1774     /* If it's a new language, add it at the end, otherwise free the
1775        existing macros and replace it */
1776     if (i == NSmartIndentSpecs) {
1777     	SmartIndentSpecs[NSmartIndentSpecs++] = newMacros;
1778     } else {
1779 	freeIndentSpec(SmartIndentSpecs[i]);
1780 	SmartIndentSpecs[i] = newMacros;
1781     }
1782 
1783     /* Find windows that are currently using this indent specification and
1784        re-do the smart indent macros */
1785     for (window=WindowList; window!=NULL; window=window->next) {
1786     	lmName = LanguageModeName(window->languageMode);
1787 	if (lmName != NULL && !strcmp(lmName, newMacros->lmName)) {
1788 	    SetSensitive(window, window->smartIndentItem, True);
1789     	    if (window->indentStyle == SMART_INDENT &&
1790     		    window->languageMode != PLAIN_LANGUAGE_MODE) {
1791     	    	EndSmartIndent(window);
1792     	    	BeginSmartIndent(window, False);
1793     	    }
1794     	}
1795     }
1796 
1797     /* Note that preferences have been changed */
1798     MarkPrefsChanged();
1799 
1800     return True;
1801 }
1802 
loadDefaultIndentSpec(char * lmName)1803 static int loadDefaultIndentSpec(char *lmName)
1804 {
1805     int i;
1806 
1807     for (i=0; i<N_DEFAULT_INDENT_SPECS; i++) {
1808     	if (!strcmp(lmName, DefaultIndentSpecs[i].lmName)) {
1809     	    SmartIndentSpecs[NSmartIndentSpecs++] =
1810 		    copyIndentSpec(&DefaultIndentSpecs[i]);
1811     	    return True;
1812     	}
1813     }
1814     return False;
1815 }
1816 
LoadSmartIndentString(char * inString)1817 int LoadSmartIndentString(char *inString)
1818 {
1819    char *errMsg, *inPtr = inString;
1820    smartIndentRec is, *isCopy;
1821    int i;
1822 
1823    for (;;) {
1824 
1825 	/* skip over blank space */
1826 	inPtr += strspn(inPtr, " \t\n");
1827 
1828 	/* finished */
1829 	if (*inPtr == '\0')
1830 	    return True;
1831 
1832 	/* read language mode name */
1833 	is.lmName = ReadSymbolicField(&inPtr);
1834 	if (is.lmName == NULL)
1835     	    return siParseError(inString, inPtr, "language mode name required");
1836 	if (!SkipDelimiter(&inPtr, &errMsg)) {
1837 	    NEditFree(is.lmName);
1838     	    return siParseError(inString, inPtr, errMsg);
1839     	}
1840 
1841 	/* look for "Default" keyword, and if it's there, return the default
1842 	   smart indent macros */
1843 	if (!strncmp(inPtr, "Default", 7)) {
1844     	    inPtr += 7;
1845     	    if (!loadDefaultIndentSpec(is.lmName)) {
1846     		NEditFree(is.lmName);
1847     		return siParseError(inString, inPtr,
1848     	    		"no default smart indent macros");
1849     	    }
1850 	    NEditFree(is.lmName);
1851     	    continue;
1852 	}
1853 
1854 	/* read the initialization macro (arbitrary text terminated by the
1855 	   macro end boundary string) */
1856 	is.initMacro = readSIMacro(&inPtr);
1857 	if (is.initMacro == NULL) {
1858     	    NEditFree(is.lmName);
1859     	    return siParseError(inString, inPtr,
1860     	    	    "no end boundary to initialization macro");
1861 	}
1862 
1863 	/* read the newline macro */
1864 	is.newlineMacro = readSIMacro(&inPtr);
1865 	if (is.newlineMacro == NULL) {
1866     	    NEditFree(is.lmName);
1867     	    NEditFree(is.initMacro);
1868     	    return siParseError(inString, inPtr,
1869     	    	    "no end boundary to newline macro");
1870 	}
1871 
1872 	/* read the modify macro */
1873 	is.modMacro = readSIMacro(&inPtr);
1874 	if (is.modMacro == NULL) {
1875     	    NEditFree(is.lmName);
1876     	    NEditFree(is.initMacro);
1877     	    NEditFree(is.newlineMacro);
1878     	    return siParseError(inString, inPtr,
1879     	    	    "no end boundary to modify macro");
1880 	}
1881 
1882 	/* if there's no mod macro, make it null so it won't be executed */
1883 	if (is.modMacro[0] == '\0') {
1884 	    NEditFree(is.modMacro);
1885             is.modMacro = NULL;
1886     	}
1887 
1888     	/* create a new data structure and add/change it in the list */
1889 	isCopy = (smartIndentRec *)NEditMalloc(sizeof(smartIndentRec));
1890 	*isCopy = is;
1891 	for (i=0; i<NSmartIndentSpecs; i++) {
1892 	    if (!strcmp(SmartIndentSpecs[i]->lmName, is.lmName)) {
1893 		freeIndentSpec(SmartIndentSpecs[i]);
1894 		SmartIndentSpecs[i] = isCopy;
1895 		break;
1896 	    }
1897 	}
1898 	if (i == NSmartIndentSpecs)
1899 	    SmartIndentSpecs[NSmartIndentSpecs++] = isCopy;
1900     }
1901 }
1902 
LoadSmartIndentCommonString(char * inString)1903 int LoadSmartIndentCommonString(char *inString)
1904 {
1905     int shiftedLen;
1906     char *inPtr = inString;
1907 
1908     /* If called from -import, can replace existing ones */
1909     NEditFree(CommonMacros);
1910 
1911     /* skip over blank space */
1912     inPtr += strspn(inPtr, " \t\n");
1913 
1914     /* look for "Default" keyword, and if it's there, return the default
1915        smart common macro */
1916     if (!strncmp(inPtr, "Default", 7)) {
1917     	CommonMacros = NEditStrdup(DefaultCommonMacros);
1918 	return True;
1919     }
1920 
1921     /* Remove leading tabs added by writer routine */
1922     CommonMacros = ShiftText(inPtr, SHIFT_LEFT, True, 8, 8, &shiftedLen);
1923     return True;
1924 }
1925 
1926 /*
1927 ** Read a macro (arbitrary text terminated by the macro end boundary string)
1928 ** from the position pointed to by *inPtr, trim off added tabs and return an
1929 ** allocated copy of the string, and advance *inPtr to the end of the macro.
1930 ** Returns NULL if the macro end boundary string is not found.
1931 */
readSIMacro(char ** inPtr)1932 static char *readSIMacro(char **inPtr)
1933 {
1934     char *retStr, *macroStr, *macroEnd;
1935     int shiftedLen;
1936 
1937     /* Strip leading newline */
1938     if (**inPtr == '\n')
1939     	(*inPtr)++;
1940 
1941     /* Find the end of the macro */
1942     macroEnd = strstr(*inPtr, MacroEndBoundary);
1943     if (macroEnd == NULL)
1944 	return NULL;
1945 
1946     /* Copy the macro */
1947     macroStr = (char*)NEditMalloc(macroEnd - *inPtr + 1);
1948     strncpy(macroStr, *inPtr, macroEnd - *inPtr);
1949     macroStr[macroEnd - *inPtr] = '\0';
1950 
1951     /* Remove leading tabs added by writer routine */
1952     *inPtr = macroEnd + strlen(MacroEndBoundary);
1953     retStr = ShiftText(macroStr, SHIFT_LEFT, True, 8, 8, &shiftedLen);
1954     NEditFree(macroStr);
1955     return retStr;
1956 }
1957 
copyIndentSpec(smartIndentRec * is)1958 static smartIndentRec *copyIndentSpec(smartIndentRec *is)
1959 {
1960     smartIndentRec *ris = (smartIndentRec *)NEditMalloc(sizeof(smartIndentRec));
1961     ris->lmName = NEditStrdup(is->lmName);
1962     ris->initMacro = NEditStrdup(is->initMacro);
1963     ris->newlineMacro = NEditStrdup(is->newlineMacro);
1964     ris->modMacro = NEditStrdup(is->modMacro);
1965     return ris;
1966 }
1967 
freeIndentSpec(smartIndentRec * is)1968 static void freeIndentSpec(smartIndentRec *is)
1969 {
1970     NEditFree(is->lmName);
1971     NEditFree(is->initMacro);
1972     NEditFree(is->newlineMacro);
1973     NEditFree(is->modMacro);
1974 }
1975 
indentSpecsDiffer(smartIndentRec * is1,smartIndentRec * is2)1976 static int indentSpecsDiffer(smartIndentRec *is1, smartIndentRec *is2)
1977 {
1978     return AllocatedStringsDiffer(is1->initMacro, is2->initMacro) ||
1979 	    AllocatedStringsDiffer(is1->newlineMacro, is2->newlineMacro) ||
1980 	    AllocatedStringsDiffer(is1->modMacro, is2->modMacro);
1981 }
1982 
siParseError(char * stringStart,char * stoppedAt,char * message)1983 static int siParseError(char *stringStart, char *stoppedAt, char *message)
1984 {
1985     return ParseError(NULL, stringStart, stoppedAt,
1986     	    "smart indent specification", message);
1987 }
1988 
WriteSmartIndentString(void)1989 char *WriteSmartIndentString(void)
1990 {
1991     int i;
1992     smartIndentRec *sis;
1993     textBuffer *outBuf;
1994     char *outStr, *escapedStr;
1995 
1996     outBuf = BufCreate();
1997     for (i=0; i<NSmartIndentSpecs; i++) {
1998     	sis = SmartIndentSpecs[i];
1999     	BufInsert(outBuf, outBuf->length, "\t");
2000     	BufInsert(outBuf, outBuf->length, sis->lmName);
2001     	BufInsert(outBuf, outBuf->length, ":");
2002     	if (isDefaultIndentSpec(sis))
2003     	    BufInsert(outBuf, outBuf->length, "Default\n");
2004     	else {
2005     	    insertShiftedMacro(outBuf, sis->initMacro);
2006     	    insertShiftedMacro(outBuf, sis->newlineMacro);
2007     	    insertShiftedMacro(outBuf, sis->modMacro);
2008     	}
2009     }
2010 
2011     /* Get the output string, and lop off the trailing newline */
2012     outStr = BufGetRange(outBuf, 0, outBuf->length > 0 ? outBuf->length-1 : 0);
2013     BufFree(outBuf);
2014 
2015     /* Protect newlines and backslashes from translation by the resource
2016        reader */
2017     escapedStr = EscapeSensitiveChars(outStr);
2018     NEditFree(outStr);
2019     return escapedStr;
2020 }
2021 
WriteSmartIndentCommonString(void)2022 char *WriteSmartIndentCommonString(void)
2023 {
2024     int len;
2025     char *outStr, *escapedStr;
2026 
2027     if (!strcmp(CommonMacros, DefaultCommonMacros))
2028     	return NEditStrdup("Default");
2029     if (CommonMacros == NULL)
2030     	return NEditStrdup("");
2031 
2032     /* Shift the macro over by a tab to keep .nedit file bright and clean */
2033     outStr = ShiftText(CommonMacros, SHIFT_RIGHT, True, 8, 8, &len);
2034 
2035     /* Protect newlines and backslashes from translation by the resource
2036        reader */
2037     escapedStr = EscapeSensitiveChars(outStr);
2038     NEditFree(outStr);
2039 
2040     /* If there's a trailing escaped newline, remove it */
2041     len = strlen(escapedStr);
2042     if (len > 1 && escapedStr[len-1] == '\n' && escapedStr[len-2] == '\\')
2043     	escapedStr[len-2] = '\0';
2044     return escapedStr;
2045 }
2046 
2047 /*
2048 ** Insert macro text "macro" into buffer "buf" shifted right by 8 characters
2049 ** (so it looks nice in the .nedit file), and terminated with a macro-end-
2050 ** boundary string.
2051 */
insertShiftedMacro(textBuffer * buf,char * macro)2052 static void insertShiftedMacro(textBuffer *buf, char  *macro)
2053 {
2054     char *shiftedMacro;
2055     int shiftedLen;
2056 
2057     if (macro != NULL) {
2058 	shiftedMacro = ShiftText(macro, SHIFT_RIGHT, True, 8, 8, &shiftedLen);
2059 	BufInsert(buf, buf->length, shiftedMacro);
2060 	NEditFree(shiftedMacro);
2061     }
2062     BufInsert(buf, buf->length, "\t");
2063     BufInsert(buf, buf->length, MacroEndBoundary);
2064     BufInsert(buf, buf->length, "\n");
2065 }
2066 
isDefaultIndentSpec(smartIndentRec * indentSpec)2067 static int isDefaultIndentSpec(smartIndentRec *indentSpec)
2068 {
2069     int i;
2070 
2071     for (i=0; i<N_DEFAULT_INDENT_SPECS; i++)
2072     	if (!strcmp(indentSpec->lmName, DefaultIndentSpecs[i].lmName))
2073     	    return !indentSpecsDiffer(indentSpec, &DefaultIndentSpecs[i]);
2074     return False;
2075 }
2076 
findIndentSpec(const char * modeName)2077 static smartIndentRec *findIndentSpec(const char *modeName)
2078 {
2079     int i;
2080 
2081     if (modeName == NULL)
2082     	return NULL;
2083 
2084     for (i=0; i<NSmartIndentSpecs; i++)
2085     	if (!strcmp(modeName, SmartIndentSpecs[i]->lmName))
2086     	    return SmartIndentSpecs[i];
2087     return NULL;
2088 }
2089 
2090 /*
2091 ** If "string" is not terminated with a newline character,  return a
2092 ** reallocated string which does end in a newline (otherwise, just pass on
2093 ** string as function value).  (The macro language requires newline terminators
2094 ** for statements, but the text widget doesn't force it like the NEdit text
2095 ** buffer does, so this might avoid some confusion.)
2096 */
ensureNewline(char * string)2097 static char *ensureNewline(char *string)
2098 {
2099     char *newString;
2100     int length;
2101 
2102     if (string == NULL)
2103 	return NULL;
2104     length = strlen(string);
2105     if (length == 0 || string[length-1] == '\n')
2106 	return string;
2107     newString = (char*)NEditMalloc(length + 2);
2108     strcpy(newString, string);
2109     newString[length] = '\n';
2110     newString[length+1] = '\0';
2111     NEditFree(string);
2112     return newString;
2113 }
2114 
2115 /*
2116 ** Returns True if there are smart indent macros, or potential macros
2117 ** not yet committed in the smart indent dialog for a language mode,
2118 */
LMHasSmartIndentMacros(const char * languageMode)2119 int LMHasSmartIndentMacros(const char *languageMode)
2120 {
2121     if (findIndentSpec(languageMode) != NULL)
2122     	return True;
2123     return SmartIndentDialog.shell!=NULL && !strcmp(SmartIndentDialog.langModeName,
2124     	    languageMode);
2125 }
2126 
2127 /*
2128 ** Change the language mode name of smart indent macro sets for language
2129 ** "oldName" to "newName" in both the stored macro sets, and the pattern set
2130 ** currently being edited in the dialog.
2131 */
RenameSmartIndentMacros(const char * oldName,const char * newName)2132 void RenameSmartIndentMacros(const char *oldName, const char *newName)
2133 {
2134     int i;
2135 
2136     for (i=0; i<NSmartIndentSpecs; i++) {
2137     	if (!strcmp(oldName, SmartIndentSpecs[i]->lmName)) {
2138     	    NEditFree(SmartIndentSpecs[i]->lmName);
2139     	    SmartIndentSpecs[i]->lmName = NEditStrdup(newName);
2140     	}
2141     }
2142     if (SmartIndentDialog.shell != NULL) {
2143     	if (!strcmp(SmartIndentDialog.langModeName, oldName)) {
2144     	    NEditFree(SmartIndentDialog.langModeName);
2145     	    SmartIndentDialog.langModeName = NEditStrdup(newName);
2146     	}
2147     }
2148 }
2149 
2150 /*
2151 ** If a smart indent dialog is up, ask to have the option menu for
2152 ** chosing language mode updated (via a call to CreateLanguageModeMenu)
2153 */
UpdateLangModeMenuSmartIndent(void)2154 void UpdateLangModeMenuSmartIndent(void)
2155 {
2156     Widget oldMenu;
2157 
2158     if (SmartIndentDialog.shell == NULL)
2159     	return;
2160 
2161     oldMenu = SmartIndentDialog.lmPulldown;
2162     SmartIndentDialog.lmPulldown = CreateLanguageModeMenu(
2163     	    XtParent(XtParent(oldMenu)), langModeCB, NULL);
2164     XtVaSetValues(XmOptionButtonGadget(SmartIndentDialog.lmOptMenu),
2165     	    XmNsubMenuId, SmartIndentDialog.lmPulldown, NULL);
2166     SetLangModeMenu(SmartIndentDialog.lmOptMenu, SmartIndentDialog.langModeName);
2167 
2168     XtDestroyWidget(oldMenu);
2169 }
2170