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