Remove memory leaks in BeginSmartIndent()
[nedit.git] / source / smartIndent.c
blob61c9867c3e6a6587b51cea02b61b33157c6c240d
1 static const char CVSID[] = "$Id: smartIndent.c,v 1.41 2008/10/08 08:37:41 lebert 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. In addition, you may distribute version of this program linked to *
12 * Motif or Open Motif. See README for details. *
13 * *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
17 * for more details. *
18 * *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
22 * *
23 * Nirvana Text Editor *
24 * July, 1997 *
25 * *
26 * Written by Mark Edel *
27 * *
28 *******************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 #include "../config.h"
32 #endif
34 #include "smartIndent.h"
35 #include "textBuf.h"
36 #include "nedit.h"
37 #include "text.h"
38 #include "preferences.h"
39 #include "interpret.h"
40 #include "macro.h"
41 #include "window.h"
42 #include "parse.h"
43 #include "shift.h"
44 #include "help.h"
45 #include "../util/DialogF.h"
46 #include "../util/misc.h"
48 #include <stdio.h>
49 #include <string.h>
50 #include <limits.h>
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>
69 #ifdef HAVE_DEBUG_H
70 #include "../debug.h"
71 #endif
74 static char MacroEndBoundary[] = "--End-of-Macro--";
76 typedef struct {
77 char *lmName;
78 char *initMacro;
79 char *newlineMacro;
80 char *modMacro;
81 } smartIndentRec;
83 typedef struct {
84 Program *newlineMacro;
85 int inNewLineMacro;
86 Program *modMacro;
87 int inModMacro;
88 } windowSmartIndentData;
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};
101 /* Common smart indent macros dialog information */
102 static struct {
103 Widget shell;
104 Widget text;
105 } CommonDialog = {NULL,NULL};
107 static int NSmartIndentSpecs = 0;
108 static smartIndentRec *SmartIndentSpecs[MAX_LANGUAGE_MODES];
109 static char *CommonMacros = NULL;
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);
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 == -1)\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\
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\
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\
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\
210 firstWord = get_range(startLine + indentLevel, FWend)\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\
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\
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}
249 static char DefaultCommonMacros[] = "#\n\
250 # C/C++ Style/tuning parameters\n\
251 #\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\
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\
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\
266 #\n\
267 # Find the start of the line containing position $1\n\
268 #\n\
269 define startOfLine {\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\
279 #\n\
280 # Find the indent level of the line containing character position $1\n\
281 #\n\
282 define measureIndent {\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\
298 #\n\
299 # Make a string to produce an indent of $1 characters\n\
300 #\n\
301 define makeIndentString {\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\
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\
325 if ($1 != \"default\")\n\
326 return $1\n\
327 if ($em_tab_dist != -1)\n\
328 return $em_tab_dist\n\
329 if ($tab_dist <= 8)\n\
330 return $tab_dist\n\
331 return 4\n\
332 }\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\
341 if ($1 != \"default\")\n\
342 return $1\n\
343 if ($em_tab_dist != -1)\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\
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\
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\
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\
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\
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\
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\
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\
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\
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\
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\
466 # A case statement by itself is an acceptable anchor\n\
467 if (isCase)\n\
468 return caseStart\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\
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\
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\
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\
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\
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\
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\
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\
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\
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\
550 # Find the indentation of that line\n\
551 anchorIndent = measureIndent(anchorPos)\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\
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\
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\
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\
605 anchorPos = $1\n\
606 maxPos = $2\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\
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\
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\
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\
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).
687 void BeginSmartIndent(WindowInfo *window, int warn)
689 windowSmartIndentData *winData;
690 smartIndentRec *indentMacros;
691 char *modeName, *stoppedAt, *errMsg;
692 static int initialized;
694 /* Find the window's language mode. If none is set, warn the user */
695 modeName = LanguageModeName(window->languageMode);
696 if (modeName == NULL)
698 if (warn)
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");
706 return;
709 /* Look up the appropriate smart-indent macros for the language */
710 indentMacros = findIndentSpec(modeName);
711 if (indentMacros == NULL)
713 if (warn)
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);
722 return;
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);
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;
739 if (indentMacros->initMacro != NULL) {
740 if (!ReadMacroString(window, indentMacros->initMacro,
741 "smart indent initialization macro"))
742 return;
745 /* Compile the newline and modify macros and attach them to the window */
746 winData = (windowSmartIndentData *)XtMalloc(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 XtFree((char *)winData);
753 ParseError(window->shell, indentMacros->newlineMacro, stoppedAt,
754 "newline macro", errMsg);
755 return;
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 XtFree((char *)winData);
765 ParseError(window->shell, indentMacros->modMacro, stoppedAt,
766 "smart indent modify macro", errMsg);
767 return;
770 window->smartIndentData = (void *)winData;
773 void EndSmartIndent(WindowInfo *window)
775 windowSmartIndentData *winData =
776 (windowSmartIndentData *)window->smartIndentData;
778 if (winData == NULL)
779 return;
781 /* Free programs and allocated data */
782 if (winData->modMacro != NULL)
783 FreeProgram(winData->modMacro);
784 FreeProgram(winData->newlineMacro);
785 XtFree((char *)winData);
786 window->smartIndentData = NULL;
790 ** Returns true if there are smart indent macros for a named language
792 int SmartIndentMacrosAvailable(char *languageModeName)
794 return findIndentSpec(languageModeName) != NULL;
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.
803 void SmartIndentCB(Widget w, XtPointer clientData, XtPointer callData)
805 WindowInfo *window = WidgetToWindow(w);
806 smartIndentCBStruct *cbInfo = (smartIndentCBStruct *)callData;
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);
817 ** Run the newline macro with information from the smart-indent callback
818 ** structure passed by the widget
820 static void executeNewlineMacro(WindowInfo *window, smartIndentCBStruct *cbInfo)
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;
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;
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);
844 /* Don't allow preemption or time limit. Must get return value */
845 while (stat == MACRO_TIME_LIMIT)
846 stat = ContinueMacro(continuation, &result, &errMsg);
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();
854 /* Process errors in macro execution */
855 if (stat == MACRO_PREEMPT || stat == MACRO_ERROR)
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;
866 /* Validate and return the result */
867 if (result.tag != INT_TAG || result.val.n < -1 || result.val.n > 1000)
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;
876 cbInfo->indentRequest = result.val.n;
880 Boolean InSmartIndentMacros(WindowInfo *window) {
881 windowSmartIndentData *winData =
882 (windowSmartIndentData *)window->smartIndentData;
884 return((winData && (winData->inModMacro || winData->inNewLineMacro)));
888 ** Run the modification macro with information from the smart-indent callback
889 ** structure passed by the widget
891 static void executeModMacro(WindowInfo *window,smartIndentCBStruct *cbInfo)
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;
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;
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);
915 inModCB = True;
916 ++(winData->inModMacro);
918 stat = ExecuteMacro(window, winData->modMacro, 2, args, &result,
919 &continuation, &errMsg);
920 while (stat == MACRO_TIME_LIMIT)
921 stat = ContinueMacro(continuation, &result, &errMsg);
923 --(winData->inModMacro);
924 inModCB = False;
926 /* Process errors in macro execution */
927 if (stat == MACRO_PREEMPT || stat == MACRO_ERROR)
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;
939 void EditSmartIndentMacros(WindowInfo *window)
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;
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;
958 if (LanguageModeName(0) == NULL)
960 DialogF(DF_WARN, window->shell, 1, "Language Mode",
961 "No Language Modes defined", "OK");
962 return;
965 /* Decide on an initial language mode */
966 lmName = LanguageModeName(window->languageMode == PLAIN_LANGUAGE_MODE ? 0 :
967 window->languageMode);
968 SmartIndentDialog.langModeName = XtNewString(lmName);
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);
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);
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;
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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, */
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);
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);
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);
1212 /* Set initial default button */
1213 XtVaSetValues(form, XmNdefaultButton, okBtn, NULL);
1214 XtVaSetValues(form, XmNcancelButton, closeBtn, NULL);
1216 /* Handle mnemonic selection of buttons and focus to dialog */
1217 AddDialogMnemonicHandler(form, FALSE);
1219 /* Fill in the dialog information for the selected language mode */
1220 setSmartIndentDialogData(findIndentSpec(lmName));
1221 SetLangModeMenu(SmartIndentDialog.lmOptMenu,SmartIndentDialog.langModeName);
1223 /* Realize all of the widgets in the new dialog */
1224 RealizeWithoutForcingPosition(SmartIndentDialog.shell);
1227 static void destroyCB(Widget w, XtPointer clientData, XtPointer callData)
1229 XtFree(SmartIndentDialog.langModeName);
1230 SmartIndentDialog.shell = NULL;
1233 static void langModeCB(Widget w, XtPointer clientData, XtPointer callData)
1235 char *modeName;
1236 int i, resp;
1237 static smartIndentRec emptyIndentSpec = {NULL, NULL, NULL, NULL};
1238 smartIndentRec *oldMacros, *newMacros;
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;
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];
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))
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);
1261 if (resp == 3)
1263 SetLangModeMenu(SmartIndentDialog.lmOptMenu,
1264 SmartIndentDialog.langModeName);
1265 return;
1266 } else if (resp == 1)
1268 if (checkSmartIndentDialogData())
1270 if (oldMacros == &emptyIndentSpec)
1272 SmartIndentSpecs[NSmartIndentSpecs++]
1273 = copyIndentSpec(newMacros);
1274 } else
1276 freeIndentSpec(oldMacros);
1277 SmartIndentSpecs[i] = copyIndentSpec(newMacros);
1279 } else
1281 SetLangModeMenu(SmartIndentDialog.lmOptMenu,
1282 SmartIndentDialog.langModeName);
1283 return;
1287 freeIndentSpec(newMacros);
1289 /* Fill the dialog with the new language mode information */
1290 SmartIndentDialog.langModeName = XtNewString(modeName);
1291 setSmartIndentDialogData(findIndentSpec(modeName));
1294 static void lmDialogCB(Widget w, XtPointer clientData, XtPointer callData)
1296 EditLanguageModes();
1299 static void commonDialogCB(Widget w, XtPointer clientData, XtPointer callData)
1301 EditCommonSmartIndentMacro();
1304 static void okCB(Widget w, XtPointer clientData, XtPointer callData)
1306 /* change the macro */
1307 if (!updateSmartIndentData())
1308 return;
1310 /* pop down and destroy the dialog */
1311 CloseAllPopupsFor(SmartIndentDialog.shell);
1312 XtDestroyWidget(SmartIndentDialog.shell);
1315 static void applyCB(Widget w, XtPointer clientData, XtPointer callData)
1317 /* change the patterns */
1318 updateSmartIndentData();
1321 static void checkCB(Widget w, XtPointer clientData, XtPointer callData)
1323 if (checkSmartIndentDialogData())
1324 DialogF(DF_INF, SmartIndentDialog.shell, 1, "Macro compiled",
1325 "Macros compiled without error", "OK");
1328 static void restoreCB(Widget w, XtPointer clientData, XtPointer callData)
1330 int i;
1331 smartIndentRec *defaultIS;
1333 /* Find the default indent spec */
1334 for (i=0; i<N_DEFAULT_INDENT_SPECS; i++)
1336 if (!strcmp(SmartIndentDialog.langModeName,
1337 DefaultIndentSpecs[i].lmName))
1339 break;
1343 if (i == N_DEFAULT_INDENT_SPECS)
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;
1350 defaultIS = &DefaultIndentSpecs[i];
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)
1358 return;
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);
1372 /* Update the dialog */
1373 setSmartIndentDialogData(defaultIS);
1376 static void deleteCB(Widget w, XtPointer clientData, XtPointer callData)
1378 int i;
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)
1385 return;
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--;
1399 /* Clear out the dialog */
1400 setSmartIndentDialogData(NULL);
1403 static void closeCB(Widget widget, XtPointer clientData, XtPointer callData)
1405 /* pop down and destroy the dialog */
1406 CloseAllPopupsFor(SmartIndentDialog.shell);
1407 XtDestroyWidget(SmartIndentDialog.shell);
1410 static void helpCB(Widget w, XtPointer clientData, XtPointer callData)
1412 Help(HELP_SMART_INDENT);
1415 static int checkSmartIndentDialogData(void)
1417 char *widgetText, *errMsg, *stoppedAt;
1418 Program *prog;
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 XtFree(widgetText);
1429 return False;
1431 XtFree(widgetText);
1434 /* Test compile the newline macro */
1435 if (TextWidgetIsBlank(SmartIndentDialog.newlineMacro))
1437 DialogF(DF_WARN, SmartIndentDialog.shell, 1, "Smart Indent",
1438 "Newline macro required", "OK");
1439 return False;
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 XtFree(widgetText);
1451 return False;
1453 XtFree(widgetText);
1454 FreeProgram(prog);
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 XtFree(widgetText);
1467 return False;
1469 XtFree(widgetText);
1470 FreeProgram(prog);
1472 return True;
1475 static smartIndentRec *getSmartIndentDialogData(void)
1477 smartIndentRec *is;
1479 is = (smartIndentRec *)XtMalloc(sizeof(smartIndentRec));
1480 is->lmName = XtNewString(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;
1490 static void setSmartIndentDialogData(smartIndentRec *is)
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);
1509 void EditCommonSmartIndentMacro(void)
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;
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;
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);
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);
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);
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);
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);
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);
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);
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);
1629 /* Set initial default button */
1630 XtVaSetValues(form, XmNdefaultButton, okBtn, NULL);
1631 XtVaSetValues(form, XmNcancelButton, closeBtn, NULL);
1633 /* Handle mnemonic selection of buttons and focus to dialog */
1634 AddDialogMnemonicHandler(form, FALSE);
1636 /* Realize all of the widgets in the new dialog */
1637 RealizeWithoutForcingPosition(CommonDialog.shell);
1640 static void comDestroyCB(Widget w, XtPointer clientData, XtPointer callData)
1642 CommonDialog.shell = NULL;
1645 static void comOKCB(Widget w, XtPointer clientData, XtPointer callData)
1647 /* change the macro */
1648 if (!updateSmartIndentCommonData())
1649 return;
1651 /* pop down and destroy the dialog */
1652 XtDestroyWidget(CommonDialog.shell);
1655 static void comApplyCB(Widget w, XtPointer clientData, XtPointer callData)
1657 /* change the macro */
1658 updateSmartIndentCommonData();
1661 static void comCheckCB(Widget w, XtPointer clientData, XtPointer callData)
1663 if (checkSmartIndentCommonDialogData())
1665 DialogF(DF_INF, CommonDialog.shell, 1, "Macro compiled",
1666 "Macros compiled without error", "OK");
1670 static void comRestoreCB(Widget w, XtPointer clientData, XtPointer callData)
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)
1676 return;
1679 /* replace common macros with default */
1680 XtFree(CommonMacros);
1681 CommonMacros = XtNewString(DefaultCommonMacros);
1683 /* Update the dialog */
1684 XmTextSetString(CommonDialog.text, CommonMacros);
1687 static void comCloseCB(Widget w, XtPointer clientData, XtPointer callData)
1689 /* pop down and destroy the dialog */
1690 XtDestroyWidget(CommonDialog.shell);
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.
1698 static int updateSmartIndentCommonData(void)
1700 WindowInfo *window;
1702 /* Make sure the patterns are valid and compile */
1703 if (!checkSmartIndentCommonDialogData())
1704 return False;
1706 /* Get the current data */
1707 CommonMacros = ensureNewline(XmTextGetString(CommonDialog.text));
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;
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);
1726 /* Note that preferences have been changed */
1727 MarkPrefsChanged();
1729 return True;
1732 static int checkSmartIndentCommonDialogData(void)
1734 char *widgetText, *stoppedAt;
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 XtFree(widgetText);
1743 return False;
1745 XtFree(widgetText);
1747 return True;
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.
1755 static int updateSmartIndentData(void)
1757 smartIndentRec *newMacros;
1758 WindowInfo *window;
1759 char *lmName;
1760 int i;
1762 /* Make sure the patterns are valid and compile */
1763 if (!checkSmartIndentDialogData())
1764 return False;
1766 /* Get the current data */
1767 newMacros = getSmartIndentDialogData();
1769 /* Find the original macros */
1770 for (i=0; i<NSmartIndentSpecs; i++)
1771 if (!strcmp(SmartIndentDialog.langModeName,SmartIndentSpecs[i]->lmName))
1772 break;
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;
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);
1797 /* Note that preferences have been changed */
1798 MarkPrefsChanged();
1800 return True;
1803 static int loadDefaultIndentSpec(char *lmName)
1805 int i;
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;
1814 return False;
1817 int LoadSmartIndentString(char *inString)
1819 char *errMsg, *inPtr = inString;
1820 smartIndentRec is, *isCopy;
1821 int i;
1823 for (;;) {
1825 /* skip over blank space */
1826 inPtr += strspn(inPtr, " \t\n");
1828 /* finished */
1829 if (*inPtr == '\0')
1830 return True;
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 XtFree(is.lmName);
1838 return siParseError(inString, inPtr, errMsg);
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 XtFree(is.lmName);
1847 return siParseError(inString, inPtr,
1848 "no default smart indent macros");
1850 XtFree(is.lmName);
1851 continue;
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 XtFree(is.lmName);
1859 return siParseError(inString, inPtr,
1860 "no end boundary to initialization macro");
1863 /* read the newline macro */
1864 is.newlineMacro = readSIMacro(&inPtr);
1865 if (is.newlineMacro == NULL) {
1866 XtFree(is.lmName);
1867 XtFree(is.initMacro);
1868 return siParseError(inString, inPtr,
1869 "no end boundary to newline macro");
1872 /* read the modify macro */
1873 is.modMacro = readSIMacro(&inPtr);
1874 if (is.modMacro == NULL) {
1875 XtFree(is.lmName);
1876 XtFree(is.initMacro);
1877 XtFree(is.newlineMacro);
1878 return siParseError(inString, inPtr,
1879 "no end boundary to modify macro");
1882 /* if there's no mod macro, make it null so it won't be executed */
1883 if (is.modMacro[0] == '\0') {
1884 XtFree(is.modMacro);
1885 is.modMacro = NULL;
1888 /* create a new data structure and add/change it in the list */
1889 isCopy = (smartIndentRec *)XtMalloc(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;
1898 if (i == NSmartIndentSpecs)
1899 SmartIndentSpecs[NSmartIndentSpecs++] = isCopy;
1903 int LoadSmartIndentCommonString(char *inString)
1905 int shiftedLen;
1906 char *inPtr = inString;
1908 /* If called from -import, can replace existing ones */
1909 XtFree(CommonMacros);
1911 /* skip over blank space */
1912 inPtr += strspn(inPtr, " \t\n");
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 = XtNewString(DefaultCommonMacros);
1918 return True;
1921 /* Remove leading tabs added by writer routine */
1922 CommonMacros = ShiftText(inPtr, SHIFT_LEFT, True, 8, 8, &shiftedLen);
1923 return True;
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.
1932 static char *readSIMacro(char **inPtr)
1934 char *retStr, *macroStr, *macroEnd;
1935 int shiftedLen;
1937 /* Strip leading newline */
1938 if (**inPtr == '\n')
1939 (*inPtr)++;
1941 /* Find the end of the macro */
1942 macroEnd = strstr(*inPtr, MacroEndBoundary);
1943 if (macroEnd == NULL)
1944 return NULL;
1946 /* Copy the macro */
1947 macroStr = XtMalloc(macroEnd - *inPtr + 1);
1948 strncpy(macroStr, *inPtr, macroEnd - *inPtr);
1949 macroStr[macroEnd - *inPtr] = '\0';
1951 /* Remove leading tabs added by writer routine */
1952 *inPtr = macroEnd + strlen(MacroEndBoundary);
1953 retStr = ShiftText(macroStr, SHIFT_LEFT, True, 8, 8, &shiftedLen);
1954 XtFree(macroStr);
1955 return retStr;
1958 static smartIndentRec *copyIndentSpec(smartIndentRec *is)
1960 smartIndentRec *ris = (smartIndentRec *)XtMalloc(sizeof(smartIndentRec));
1961 ris->lmName = XtNewString(is->lmName);
1962 ris->initMacro = XtNewString(is->initMacro);
1963 ris->newlineMacro = XtNewString(is->newlineMacro);
1964 ris->modMacro = XtNewString(is->modMacro);
1965 return ris;
1968 static void freeIndentSpec(smartIndentRec *is)
1970 XtFree(is->lmName);
1971 if (is->initMacro != NULL) XtFree(is->initMacro);
1972 XtFree(is->newlineMacro);
1973 if (is->modMacro != NULL)XtFree(is->modMacro);
1976 static int indentSpecsDiffer(smartIndentRec *is1, smartIndentRec *is2)
1978 return AllocatedStringsDiffer(is1->initMacro, is2->initMacro) ||
1979 AllocatedStringsDiffer(is1->newlineMacro, is2->newlineMacro) ||
1980 AllocatedStringsDiffer(is1->modMacro, is2->modMacro);
1983 static int siParseError(char *stringStart, char *stoppedAt, char *message)
1985 return ParseError(NULL, stringStart, stoppedAt,
1986 "smart indent specification", message);
1989 char *WriteSmartIndentString(void)
1991 int i;
1992 smartIndentRec *sis;
1993 textBuffer *outBuf;
1994 char *outStr, *escapedStr;
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);
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);
2015 /* Protect newlines and backslashes from translation by the resource
2016 reader */
2017 escapedStr = EscapeSensitiveChars(outStr);
2018 XtFree(outStr);
2019 return escapedStr;
2022 char *WriteSmartIndentCommonString(void)
2024 int len;
2025 char *outStr, *escapedStr;
2027 if (!strcmp(CommonMacros, DefaultCommonMacros))
2028 return XtNewString("Default");
2029 if (CommonMacros == NULL)
2030 return XtNewString("");
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);
2035 /* Protect newlines and backslashes from translation by the resource
2036 reader */
2037 escapedStr = EscapeSensitiveChars(outStr);
2038 XtFree(outStr);
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;
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.
2052 static void insertShiftedMacro(textBuffer *buf, char *macro)
2054 char *shiftedMacro;
2055 int shiftedLen;
2057 if (macro != NULL) {
2058 shiftedMacro = ShiftText(macro, SHIFT_RIGHT, True, 8, 8, &shiftedLen);
2059 BufInsert(buf, buf->length, shiftedMacro);
2060 XtFree(shiftedMacro);
2062 BufInsert(buf, buf->length, "\t");
2063 BufInsert(buf, buf->length, MacroEndBoundary);
2064 BufInsert(buf, buf->length, "\n");
2067 static int isDefaultIndentSpec(smartIndentRec *indentSpec)
2069 int i;
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;
2077 static smartIndentRec *findIndentSpec(const char *modeName)
2079 int i;
2081 if (modeName == NULL)
2082 return NULL;
2084 for (i=0; i<NSmartIndentSpecs; i++)
2085 if (!strcmp(modeName, SmartIndentSpecs[i]->lmName))
2086 return SmartIndentSpecs[i];
2087 return NULL;
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.)
2097 static char *ensureNewline(char *string)
2099 char *newString;
2100 int length;
2102 if (string == NULL)
2103 return NULL;
2104 length = strlen(string);
2105 if (length == 0 || string[length-1] == '\n')
2106 return string;
2107 newString = XtMalloc(length + 2);
2108 strcpy(newString, string);
2109 newString[length] = '\n';
2110 newString[length+1] = '\0';
2111 XtFree(string);
2112 return newString;
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,
2119 int LMHasSmartIndentMacros(const char *languageMode)
2121 if (findIndentSpec(languageMode) != NULL)
2122 return True;
2123 return SmartIndentDialog.shell!=NULL && !strcmp(SmartIndentDialog.langModeName,
2124 languageMode);
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.
2132 void RenameSmartIndentMacros(const char *oldName, const char *newName)
2134 int i;
2136 for (i=0; i<NSmartIndentSpecs; i++) {
2137 if (!strcmp(oldName, SmartIndentSpecs[i]->lmName)) {
2138 XtFree(SmartIndentSpecs[i]->lmName);
2139 SmartIndentSpecs[i]->lmName = XtNewString(newName);
2142 if (SmartIndentDialog.shell != NULL) {
2143 if (!strcmp(SmartIndentDialog.langModeName, oldName)) {
2144 XtFree(SmartIndentDialog.langModeName);
2145 SmartIndentDialog.langModeName = XtNewString(newName);
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)
2154 void UpdateLangModeMenuSmartIndent(void)
2156 Widget oldMenu;
2158 if (SmartIndentDialog.shell == NULL)
2159 return;
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);
2168 XtDestroyWidget(oldMenu);