1 static const char CVSID
[] = "$Id: smartIndent.c,v 1.36 2004/08/01 10:06:11 yooden Exp $";
2 /*******************************************************************************
4 * smartIndent.c -- Maintain, and allow user to edit, macros for smart indent *
6 * Copyright (C) 1999 Mark Edel *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. In addition, you may distribute version of this program linked to *
12 * Motif or Open Motif. See README for details. *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
23 * Nirvana Text Editor *
26 * Written by Mark Edel *
28 *******************************************************************************/
31 #include "../config.h"
34 #include "smartIndent.h"
38 #include "preferences.h"
39 #include "interpret.h"
45 #include "../util/DialogF.h"
46 #include "../util/misc.h"
54 #include "../util/VMSparam.h"
57 #include <sys/param.h>
63 #include <Xm/LabelG.h>
65 #include <Xm/RowColumn.h>
66 #include <Xm/SeparatoG.h>
67 #include <Xm/PanedW.h>
74 static char MacroEndBoundary
[] = "--End-of-Macro--";
84 Program
*newlineMacro
;
88 } windowSmartIndentData
;
90 /* Smart indent macros dialog information */
99 } SmartIndentDialog
= {NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
};
101 /* Common smart indent macros dialog information */
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
] = {
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"},
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"},
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\
167 return measureIndent($1) + defaultIndent($pyIndentDist)\n", NULL
},
169 "# Number of spaces to indent \"case\" statements\n\
171 define matlabNewlineMacro\n\
173 if ($em_tab_dist == -1)\n\
174 tabsize = $tab_dist\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\
184 return matlabNewlineMacro(startLine - 1, 1)\n\
190 # Correct the indentLevel if this was a continued line.\n\
191 while (startLine > 1)\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\
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\
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\
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\
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\
231 replace_range(startLine, startLine + indentLevel, \\\n\
232 makeIndentString(last_indent - tabsize))\n\
235 if (firstWord == \"end\") {\n\
236 return max(last_indent - tabsize, 0)\n\
239 return last_indent\n\
243 return indentLevel\n\
245 }", "return matlabNewlineMacro($1)\n", NULL
}
248 static char DefaultCommonMacros
[] = "#\n\
249 # C/C++ Style/tuning parameters\n\
252 # Number of characters in a normal indent level. May be a number, or the\n\
253 # string \"default\", meaning, guess the value from the current tab settings.\n\
254 $cIndentDist = \"default\"\n\
256 # Number of characters in a line continuation. May be a number or the\n\
257 # string \"default\", meaning, guess the value from the current tab settings.\n\
258 $cContinuationIndent = \"default\"\n\
260 # How far back from the current position to search for an anchoring position\n\
261 # on which to base indent. When no reliable indicators of proper indent level\n\
262 # can be found within the requested distance, reverts to plain auto indent.\n\
263 $cMaxSearchBackLines = 10\n\
266 # Find the start of the line containing position $1\n\
268 define startOfLine {\n\
270 for (i=$1-1; ; i--) {\n\
273 if (get_character(i) == \"\\n\")\n\
279 # Find the indent level of the line containing character position $1\n\
281 define measureIndent {\n\
283 # measure the indentation to the first non-white character on the line\n\
285 for (i=startOfLine($1); i < $text_length; i++) {\n\
286 c = get_character(i)\n\
287 if (c != \" \" && c != \"\\t\")\n\
290 indent += $tab_dist - (indent % $tab_dist)\n\
298 # Make a string to produce an indent of $1 characters\n\
300 define makeIndentString {\n\
303 nTabs = $1 / $tab_dist\n\
304 nSpaces = $1 % $tab_dist\n\
309 indentString = \"\"\n\
310 for (i=0; i<nTabs; i++)\n\
311 indentString = indentString \"\\t\"\n\
312 for (i=0; i<nSpaces; i++)\n\
313 indentString = indentString \" \"\n\
314 return indentString\n\
318 # If $1 is a number, just pass it on. If it is the string \"default\",\n\
319 # figure out a reasonable indent distance for a structured languages\n\
320 # like C, based on how tabs are set.\n\
322 define defaultIndent {\n\
324 if ($1 != \"default\")\n\
326 if ($em_tab_dist != -1)\n\
327 return $em_tab_dist\n\
328 if ($tab_dist <= 8)\n\
334 # If $1 is a number, just pass it on. If it is the string \"default\",\n\
335 # figure out a reasonable amount of indentation for continued lines\n\
336 # based on how tabs are set.\n\
338 define defaultContIndent {\n\
340 if ($1 != \"default\")\n\
342 if ($em_tab_dist != -1)\n\
343 return $em_tab_dist * 2\n\
344 if ($tab_dist <= 8)\n\
345 return $tab_dist * 2\n\
350 # Find the end of the conditional part of if/while/for, by looking for balanced\n\
351 # parenthesis between $1 and $2. returns -1 if parens don't balance before\n\
352 # $2, or if no parens are found\n\
354 define findBalancingParen {\n\
358 for (i=$1; i<$2; i++) {\n\
359 c = get_character(i)\n\
363 } else if (c == \")\")\n\
365 else if (!parensFound && c != \" \" && c != \"\\t\")\n\
367 if (parensFound && openParens <=0)\n\
374 # Skip over blank space and comments and preprocessor directives from position\n\
375 # $1 to a maximum of $2.\n\
376 # if $3 is non-zero, newlines are considered blank space as well. Return -1\n\
377 # if the maximum position ($2) is hit mid-comment or mid-directive\n\
379 define cSkipBlankSpace {\n\
381 for (i=$1; i<$2; i++) {\n\
382 c = get_character(i)\n\
386 if (get_character(i+1) == \"*\") {\n\
387 for (i=i+1; ; i++) {\n\
390 if (get_character(i) == \"*\" && get_character(i+1) == \"/\") {\n\
395 } else if (get_character(i+1) == \"/\") {\n\
396 for (i=i+1; i<$2; i++) {\n\
397 if (get_character(i) == \"\\n\") {\n\
404 } else if (c == \"#\" && $3) {\n\
405 for (i=i+1; ; i++) {\n\
407 if (get_character(i-1) == \"\\\\\")\n\
412 if (get_character(i) == \"\\n\" && get_character(i-1) != \"\\\\\")\n\
415 } else if (!(c == \" \" || c == \"\\t\" || ($3 && c==\"\\n\")))\n\
422 # Search backward for an anchor point: a line ending brace, or semicolon\n\
423 # or case statement, followed (ignoring blank lines and comments) by what we\n\
424 # assume is a properly indented line, a brace on a line by itself, or a case\n\
425 # statement. Returns the position of the first non-white, non comment\n\
426 # character on the line. returns -1 if an anchor position can't be found\n\
427 # before $cMaxSearchBackLines.\n\
429 define cFindIndentAnchorPoint {\n\
433 for (i=$1-1; i>0; i--) {\n\
434 c = get_character(i)\n\
435 if (c == \";\" || c == \"{\" || c == \"}\" || c == \":\") {\n\
437 # Verify that it's line ending\n\
438 lineEnd = cSkipBlankSpace(i+1, $1, 0)\n\
439 if (lineEnd == -1 || \\\n\
440 (lineEnd != $text_length && get_character(lineEnd) != \"\\n\"))\n\
443 # if it's a colon, it's only meaningful if \"case\" begins the line\n\
445 lineStart = startOfLine(i)\n\
446 caseStart = cSkipBlankSpace(lineStart, lineEnd, 0)\n\
447 if (get_range(caseStart, caseStart+4) != \"case\")\n\
449 delim = get_character(caseStart+4)\n\
450 if (delim!=\" \" && delim!=\"\\t\" && delim!=\"(\" && delim!=\":\")\n\
456 # Move forward past blank lines and comment lines to find\n\
457 # non-blank, non-comment line-start\n\
458 anchorPos = cSkipBlankSpace(lineEnd, $1, 1)\n\
460 # Accept if it's before the requested position, otherwise\n\
461 # continue further back in the file and try again\n\
462 if (anchorPos != -1 && anchorPos < $1)\n\
465 # A case statement by itself is an acceptable anchor\n\
469 # A brace on a line by itself is an acceptable anchor, even\n\
470 # if it doesn't follow a semicolon or another brace\n\
471 if (c == \"{\" || c == \"}\") {\n\
472 for (j = i-1; ; j--) {\n\
475 ch = get_character(j)\n\
476 if (ch == \"\\n\")\n\
478 if (ch != \"\\t\" && ch != \" \")\n\
483 } else if (c == \"\\n\")\n\
484 if (++nLines > $cMaxSearchBackLines)\n\
493 # adjust the indent on a line about to recive either a right or left brace\n\
494 # or pound (#) character ($2) following position $1\n\
496 define cBraceOrPound {\n\
498 # Find start of the line, and make sure there's nothing but white-space\n\
499 # before the character. If there's anything before it, do nothing\n\
500 for (i=$1-1; ; i--) {\n\
505 c = get_character(i)\n\
506 if (c == \"\\n\") {\n\
510 if (c != \" \" && c != \"\\t\")\n\
514 # If the character was a pound, drag it all the way to the left margin\n\
515 if ($2 == \"#\") {\n\
516 replace_range(lineStart, $1, \"\")\n\
520 # Find the position on which to base the indent\n\
521 indent = cFindSmartIndentDist($1 - 1, \"noContinue\")\n\
525 # Adjust the indent if it's a right brace (left needs no adjustment)\n\
526 if ($2 == \"}\") {\n\
527 indent -= defaultIndent($cIndentDist)\n\
532 # Replace the current indent with the new indent string\n\
533 insertStr = makeIndentString(indent)\n\
534 replace_range(lineStart, $1, insertStr)\n\
538 # Find Smart Indent Distance for a newline character inserted at $1,\n\
539 # or return -1 to give up. Adding the optional argument \"noContinue\"\n\
540 # will stop the routine from inserting line continuation indents\n\
542 define cFindSmartIndentDist {\n\
544 # Find a known good indent to base the new indent upon\n\
545 anchorPos = cFindIndentAnchorPoint($1)\n\
546 if (anchorPos == -1)\n\
549 # Find the indentation of that line\n\
550 anchorIndent = measureIndent(anchorPos)\n\
552 # Look for special keywords which affect indent (for, if, else while, do)\n\
553 # and modify the continuation indent distance to the normal indent\n\
554 # distance when a completed statement of this type occupies the line.\n\
555 if ($n_args >= 2 && $2 == \"noContinue\") {\n\
556 continueIndent = 0\n\
559 continueIndent = cCalcContinueIndent(anchorPos, $1)\n\
561 # Move forward from anchor point, ignoring comments and blank lines,\n\
562 # remembering the last non-white, non-comment character. If $1 is\n\
563 # in the middle of a comment, give up\n\
564 lastChar = get_character(anchorPos)\n\
565 if (anchorPos < $1) {\n\
566 for (i=anchorPos;;) {\n\
567 i = cSkipBlankSpace(i, $1, 1)\n\
572 lastChar = get_character(i++)\n\
576 # Return the new indent based on the type of the last character.\n\
577 # In a for stmt, however, last character may be a semicolon and not\n\
578 # signal the end of the statement\n\
579 if (lastChar == \"{\")\n\
580 return anchorIndent + defaultIndent($cIndentDist)\n\
581 else if (lastChar == \"}\")\n\
582 return anchorIndent\n\
583 else if (lastChar == \";\") {\n\
585 return anchorIndent + continueIndent\n\
587 return anchorIndent\n\
588 } else if (lastChar == \":\" && get_range(anchorPos, anchorPos+4) == \"case\")\n\
589 return anchorIndent + defaultIndent($cIndentDist)\n\
590 return anchorIndent + continueIndent\n\
594 # Calculate the continuation indent distance for statements not ending in\n\
595 # semicolons or braces. This is not necessarily $continueIndent. It may\n\
596 # be adjusted if the statement contains if, while, for, or else.\n\
598 # As a side effect, also return $allowSemi to help distinguish statements\n\
599 # which might contain an embedded semicolon, which should not be interpreted\n\
600 # as an end of statement character.\n\
602 define cCalcContinueIndent {\n\
607 # Figure out if the anchor is on a keyword which changes indent. A special\n\
608 # case is made for elses nested in after braces\n\
611 if (get_character(anchorPos) == \"}\") {\n\
612 for (i=anchorPos+1; i<maxPos; i++) {\n\
613 c = get_character(i)\n\
614 if (c != \" \" && c != \"\\t\")\n\
617 if (get_range(i, i+4) == \"else\") {\n\
618 keywordEnd = i + 4\n\
619 needsBalancedParens = 0\n\
621 return defaultContIndent($cContinuationIndent)\n\
622 } else if (get_range(anchorPos, anchorPos + 4) == \"else\") {\n\
623 keywordEnd = anchorPos + 4\n\
624 needsBalancedParens = 0\n\
625 } else if (get_range(anchorPos, anchorPos + 2) == \"do\") {\n\
626 keywordEnd = anchorPos + 2\n\
627 needsBalancedParens = 0\n\
628 } else if (get_range(anchorPos, anchorPos + 3) == \"for\") {\n\
629 keywordEnd = anchorPos + 3\n\
631 needsBalancedParens = 1\n\
632 } else if (get_range(anchorPos, anchorPos + 2) == \"if\") {\n\
633 keywordEnd = anchorPos + 2\n\
634 needsBalancedParens = 1\n\
635 } else if (get_range(anchorPos, anchorPos + 5) == \"while\") {\n\
636 keywordEnd = anchorPos + 5\n\
637 needsBalancedParens = 1\n\
639 return defaultContIndent($cContinuationIndent)\n\
641 # If the keyword must be followed balanced parenthesis, find the end of\n\
642 # the statement by following balanced parens. If the parens aren't\n\
643 # balanced by maxPos, continue the condition. In the special case of\n\
644 # the for keyword, a semicolon can end the line and the caller should be\n\
645 # signaled to allow that\n\
646 if (needsBalancedParens) {\n\
647 stmtEnd = findBalancingParen(keywordEnd, maxPos)\n\
648 if (stmtEnd == -1) {\n\
649 $allowSemi = anchorIsFor\n\
650 return defaultContIndent($cContinuationIndent)\n\
653 stmtEnd = keywordEnd\n\
655 # check if the statement ends the line\n\
656 lineEnd = cSkipBlankSpace(stmtEnd, maxPos, 0)\n\
657 if (lineEnd == -1) # ends in comment or preproc\n\
659 if (lineEnd == maxPos) # maxPos happens at stmt end\n\
660 return defaultIndent($cIndentDist)\n\
661 c = get_character(lineEnd)\n\
662 if (c != \"\\n\") # something past last paren on line,\n\
663 return defaultIndent($cIndentDist) # probably quoted or extra braces\n\
665 # stmt contintinues beyond matching paren && newline, we're in\n\
666 # the conditional part, calculate the continue indent distance\n\
667 # recursively, based on the anchor point of the new line\n\
668 newAnchor = cSkipBlankSpace(lineEnd+1, maxPos, 1)\n\
669 if (newAnchor == -1)\n\
671 if (newAnchor == maxPos)\n\
672 return defaultIndent($cIndentDist)\n\
673 return cCalcContinueIndent(newAnchor, maxPos) + defaultIndent($cIndentDist)\n\
678 ** Turn on smart-indent (well almost). Unfortunately, this doesn't do
679 ** everything. It requires that the smart indent callback (SmartIndentCB)
680 ** is already attached to all of the text widgets in the window, and that the
681 ** smartIndent resource must be turned on in the widget. These are done
682 ** separately, because they are required per-text widget, and therefore must
683 ** be repeated whenever a new text widget is created within this window
684 ** (a split-window command).
686 void BeginSmartIndent(WindowInfo
*window
, int warn
)
688 windowSmartIndentData
*winData
;
689 smartIndentRec
*indentMacros
;
690 char *modeName
, *stoppedAt
, *errMsg
;
691 static int initialized
;
693 /* Find the window's language mode. If none is set, warn the user */
694 modeName
= LanguageModeName(window
->languageMode
);
695 if (modeName
== NULL
)
699 DialogF(DF_WARN
, window
->shell
, 1, "Smart Indent",
700 "No language-specific mode has been set for this file.\n\n"
701 "To use smart indent in this window, please select a\n"
702 "language from the Preferences -> Language Modes menu.",
708 /* Look up the appropriate smart-indent macros for the language */
709 indentMacros
= findIndentSpec(modeName
);
710 if (indentMacros
== NULL
)
714 DialogF(DF_WARN
, window
->shell
, 1, "Smart Indent",
715 "Smart indent is not available in languagemode\n%s.\n\n"
716 "You can create new smart indent macros in the\n"
717 "Preferences -> Default Settings -> Smart Indent\n"
718 "dialog, or choose a different language mode from:\n"
719 "Preferences -> Language Mode.", "OK", modeName
);
724 /* Make sure that the initial macro file is loaded before we execute
725 any of the smart-indent macros. Smart-indent macros may reference
726 routines defined in that file. */
727 ReadMacroInitFile(window
);
729 /* Compile and run the common and language-specific initialization macros
730 (Note that when these return, the immediate commands in the file have not
731 necessarily been executed yet. They are only SCHEDULED for execution) */
733 if (!ReadMacroString(window
, CommonMacros
,
734 "smart indent common initialization macros"))
738 if (indentMacros
->initMacro
!= NULL
) {
739 if (!ReadMacroString(window
, indentMacros
->initMacro
,
740 "smart indent initialization macro"))
744 /* Compile the newline and modify macros and attach them to the window */
745 winData
= (windowSmartIndentData
*)XtMalloc(sizeof(windowSmartIndentData
));
746 winData
->inNewLineMacro
= 0;
747 winData
->inModMacro
= 0;
748 winData
->newlineMacro
= ParseMacro(indentMacros
->newlineMacro
, &errMsg
,
750 if (winData
->newlineMacro
== NULL
) {
751 ParseError(window
->shell
, indentMacros
->newlineMacro
, stoppedAt
,
752 "newline macro", errMsg
);
755 if (indentMacros
->modMacro
== NULL
)
756 winData
->modMacro
= NULL
;
758 winData
->modMacro
= ParseMacro(indentMacros
->modMacro
, &errMsg
,
760 if (winData
->modMacro
== NULL
) {
761 ParseError(window
->shell
, indentMacros
->modMacro
, stoppedAt
,
762 "smart indent modify macro", errMsg
);
766 window
->smartIndentData
= (void *)winData
;
769 void EndSmartIndent(WindowInfo
*window
)
771 windowSmartIndentData
*winData
=
772 (windowSmartIndentData
*)window
->smartIndentData
;
777 /* Free programs and allocated data */
778 if (winData
->modMacro
!= NULL
)
779 FreeProgram(winData
->modMacro
);
780 FreeProgram(winData
->newlineMacro
);
781 XtFree((char *)winData
);
782 window
->smartIndentData
= NULL
;
786 ** Returns true if there are smart indent macros for a named language
788 int SmartIndentMacrosAvailable(char *languageModeName
)
790 return findIndentSpec(languageModeName
) != NULL
;
794 ** Attaches to the text widget's smart-indent callback to invoke a user
795 ** defined macro when the text widget requires an indent (not just when the
796 ** user types a newline, but also when the widget does an auto-wrap with
797 ** auto-indent on), or the user types some other character.
799 void SmartIndentCB(Widget w
, XtPointer clientData
, XtPointer callData
)
801 WindowInfo
*window
= WidgetToWindow(w
);
802 smartIndentCBStruct
*cbInfo
= (smartIndentCBStruct
*)callData
;
804 if (window
->smartIndentData
== NULL
)
806 if (cbInfo
->reason
== CHAR_TYPED
)
807 executeModMacro(window
, cbInfo
);
808 else if (cbInfo
->reason
== NEWLINE_INDENT_NEEDED
)
809 executeNewlineMacro(window
, cbInfo
);
813 ** Run the newline macro with information from the smart-indent callback
814 ** structure passed by the widget
816 static void executeNewlineMacro(WindowInfo
*window
, smartIndentCBStruct
*cbInfo
)
818 windowSmartIndentData
*winData
=
819 (windowSmartIndentData
*)window
->smartIndentData
;
820 /* posValue probably shouldn't be static due to re-entrance issues <slobasso> */
821 static DataValue posValue
= {INT_TAG
, {0}};
823 RestartData
*continuation
;
827 /* Beware of recursion: the newline macro may insert a string which
828 triggers the newline macro to be called again and so on. Newline
829 macros shouldn't insert strings, but nedit must not crash either if
831 if (winData
->inNewLineMacro
)
834 /* Call newline macro with the position at which to add newline/indent */
835 posValue
.val
.n
= cbInfo
->pos
;
836 ++(winData
->inNewLineMacro
);
837 stat
= ExecuteMacro(window
, winData
->newlineMacro
, 1, &posValue
, &result
,
838 &continuation
, &errMsg
);
840 /* Don't allow preemption or time limit. Must get return value */
841 while (stat
== MACRO_TIME_LIMIT
)
842 stat
= ContinueMacro(continuation
, &result
, &errMsg
);
844 --(winData
->inNewLineMacro
);
845 /* Collect Garbage. Note that the mod macro does not collect garbage,
846 (because collecting per-line is more efficient than per-character)
847 but GC now depends on the newline macro being mandatory */
850 /* Process errors in macro execution */
851 if (stat
== MACRO_PREEMPT
|| stat
== MACRO_ERROR
)
853 DialogF(DF_ERR
, window
->shell
, 1, "Smart Indent",
854 "Error in smart indent macro:\n%s", "OK",
857 : "dialogs and shell commands not permitted");
858 EndSmartIndent(window
);
862 /* Validate and return the result */
863 if (result
.tag
!= INT_TAG
|| result
.val
.n
< -1 || result
.val
.n
> 1000)
865 DialogF(DF_ERR
, window
->shell
, 1, "Smart Indent",
866 "Smart indent macros must return\ninteger indent distance",
868 EndSmartIndent(window
);
872 cbInfo
->indentRequest
= result
.val
.n
;
876 Boolean
InSmartIndentMacros(WindowInfo
*window
) {
877 windowSmartIndentData
*winData
=
878 (windowSmartIndentData
*)window
->smartIndentData
;
880 return((winData
&& (winData
->inModMacro
|| winData
->inNewLineMacro
)));
884 ** Run the modification macro with information from the smart-indent callback
885 ** structure passed by the widget
887 static void executeModMacro(WindowInfo
*window
,smartIndentCBStruct
*cbInfo
)
889 windowSmartIndentData
*winData
=
890 (windowSmartIndentData
*)window
->smartIndentData
;
891 /* args probably shouldn't be static due to future re-entrance issues <slobasso> */
892 static DataValue args
[2] = {{INT_TAG
, {0}}, {STRING_TAG
, {0}}};
893 /* after 5.2 release remove inModCB and use new winData->inModMacro value */
894 static int inModCB
= False
;
896 RestartData
*continuation
;
900 /* Check for inappropriate calls and prevent re-entering if the macro
901 makes a buffer modification */
902 if (winData
== NULL
|| winData
->modMacro
== NULL
|| inModCB
)
905 /* Call modification macro with the position of the modification,
906 and the character(s) inserted. Don't allow
907 preemption or time limit. Execution must not overlap or re-enter */
908 args
[0].val
.n
= cbInfo
->pos
;
909 AllocNStringCpy(&args
[1].val
.str
, cbInfo
->charsTyped
);
912 ++(winData
->inModMacro
);
914 stat
= ExecuteMacro(window
, winData
->modMacro
, 2, args
, &result
,
915 &continuation
, &errMsg
);
916 while (stat
== MACRO_TIME_LIMIT
)
917 stat
= ContinueMacro(continuation
, &result
, &errMsg
);
919 --(winData
->inModMacro
);
922 /* Process errors in macro execution */
923 if (stat
== MACRO_PREEMPT
|| stat
== MACRO_ERROR
)
925 DialogF(DF_ERR
, window
->shell
, 1, "Smart Indent",
926 "Error in smart indent modification macro:\n%s", "OK",
929 : "dialogs and shell commands not permitted");
930 EndSmartIndent(window
);
935 void EditSmartIndentMacros(WindowInfo
*window
)
938 Widget form
, lmOptMenu
, lmForm
, lmBtn
;
939 Widget okBtn
, applyBtn
, checkBtn
, deleteBtn
, commonBtn
;
940 Widget closeBtn
, helpBtn
, restoreBtn
, pane
;
941 Widget initForm
, newlineForm
, modifyForm
;
942 Widget initLbl
, newlineLbl
, modifyLbl
;
948 /* if the dialog is already displayed, just pop it to the top and return */
949 if (SmartIndentDialog
.shell
!= NULL
) {
950 RaiseShellWindow(SmartIndentDialog
.shell
);
954 if (LanguageModeName(0) == NULL
)
956 DialogF(DF_WARN
, window
->shell
, 1, "Language Mode",
957 "No Language Modes defined", "OK");
961 /* Decide on an initial language mode */
962 lmName
= LanguageModeName(window
->languageMode
== PLAIN_LANGUAGE_MODE
? 0 :
963 window
->languageMode
);
964 SmartIndentDialog
.langModeName
= XtNewString(lmName
);
966 /* Create a form widget in an application shell */
968 XtSetArg(args
[n
], XmNdeleteResponse
, XmDO_NOTHING
); n
++;
969 XtSetArg(args
[n
], XmNiconName
, "NEdit Smart Indent Macros"); n
++;
970 XtSetArg(args
[n
], XmNtitle
, "Program Smart Indent Macros"); n
++;
971 SmartIndentDialog
.shell
= CreateWidget(TheAppShell
, "smartIndent",
972 topLevelShellWidgetClass
, args
, n
);
973 AddSmallIcon(SmartIndentDialog
.shell
);
974 form
= XtVaCreateManagedWidget("editSmartIndentMacros", xmFormWidgetClass
,
975 SmartIndentDialog
.shell
, XmNautoUnmanage
, False
,
976 XmNresizePolicy
, XmRESIZE_NONE
, NULL
);
977 XtAddCallback(form
, XmNdestroyCallback
, destroyCB
, NULL
);
978 AddMotifCloseCallback(SmartIndentDialog
.shell
, closeCB
, NULL
);
980 lmForm
= XtVaCreateManagedWidget("lmForm", xmFormWidgetClass
,
982 XmNleftAttachment
, XmATTACH_POSITION
,
984 XmNtopAttachment
, XmATTACH_POSITION
,
986 XmNrightAttachment
, XmATTACH_POSITION
,
987 XmNrightPosition
, 99, NULL
);
989 SmartIndentDialog
.lmPulldown
= CreateLanguageModeMenu(lmForm
, langModeCB
,
992 XtSetArg(args
[n
], XmNspacing
, 0); n
++;
993 XtSetArg(args
[n
], XmNmarginWidth
, 0); n
++;
994 XtSetArg(args
[n
], XmNtopAttachment
, XmATTACH_FORM
); n
++;
995 XtSetArg(args
[n
], XmNleftAttachment
, XmATTACH_POSITION
); n
++;
996 XtSetArg(args
[n
], XmNleftPosition
, 50); n
++;
997 XtSetArg(args
[n
], XmNsubMenuId
, SmartIndentDialog
.lmPulldown
); n
++;
998 lmOptMenu
= XmCreateOptionMenu(lmForm
, "langModeOptMenu", args
, n
);
999 XtManageChild(lmOptMenu
);
1000 SmartIndentDialog
.lmOptMenu
= lmOptMenu
;
1002 XtVaCreateManagedWidget("lmLbl", xmLabelGadgetClass
, lmForm
,
1003 XmNlabelString
, s1
=XmStringCreateSimple("Language Mode:"),
1005 XmNuserData
, XtParent(SmartIndentDialog
.lmOptMenu
),
1006 XmNalignment
, XmALIGNMENT_END
,
1007 XmNrightAttachment
, XmATTACH_POSITION
,
1008 XmNrightPosition
, 50,
1009 XmNtopAttachment
, XmATTACH_FORM
,
1010 XmNbottomAttachment
, XmATTACH_OPPOSITE_WIDGET
,
1011 XmNbottomWidget
, lmOptMenu
, NULL
);
1014 lmBtn
= XtVaCreateManagedWidget("lmBtn", xmPushButtonWidgetClass
, lmForm
,
1015 XmNlabelString
, s1
=MKSTRING("Add / Modify\nLanguage Mode..."),
1017 XmNrightAttachment
, XmATTACH_FORM
,
1018 XmNtopAttachment
, XmATTACH_FORM
, NULL
);
1019 XtAddCallback(lmBtn
, XmNactivateCallback
, lmDialogCB
, NULL
);
1022 commonBtn
= XtVaCreateManagedWidget("commonBtn", xmPushButtonWidgetClass
,
1024 XmNlabelString
, s1
=MKSTRING("Common / Shared\nInitialization..."),
1026 XmNleftAttachment
, XmATTACH_FORM
,
1027 XmNtopAttachment
, XmATTACH_FORM
, NULL
);
1028 XtAddCallback(commonBtn
, XmNactivateCallback
, commonDialogCB
, NULL
);
1031 okBtn
= XtVaCreateManagedWidget("ok", xmPushButtonWidgetClass
, form
,
1032 XmNlabelString
, s1
=XmStringCreateSimple("OK"),
1033 XmNmarginWidth
, BUTTON_WIDTH_MARGIN
,
1034 XmNleftAttachment
, XmATTACH_POSITION
,
1036 XmNrightAttachment
, XmATTACH_POSITION
,
1037 XmNrightPosition
, 13,
1038 XmNbottomAttachment
, XmATTACH_FORM
,
1039 XmNbottomOffset
, BORDER
, NULL
);
1040 XtAddCallback(okBtn
, XmNactivateCallback
, okCB
, NULL
);
1043 applyBtn
= XtVaCreateManagedWidget("apply", xmPushButtonWidgetClass
, form
,
1044 XmNlabelString
, s1
=XmStringCreateSimple("Apply"),
1046 XmNleftAttachment
, XmATTACH_POSITION
,
1047 XmNleftPosition
, 13,
1048 XmNrightAttachment
, XmATTACH_POSITION
,
1049 XmNrightPosition
, 26,
1050 XmNbottomAttachment
, XmATTACH_FORM
,
1051 XmNbottomOffset
, BORDER
, NULL
);
1052 XtAddCallback(applyBtn
, XmNactivateCallback
, applyCB
, NULL
);
1055 checkBtn
= XtVaCreateManagedWidget("check", xmPushButtonWidgetClass
, form
,
1056 XmNlabelString
, s1
=XmStringCreateSimple("Check"),
1058 XmNleftAttachment
, XmATTACH_POSITION
,
1059 XmNleftPosition
, 26,
1060 XmNrightAttachment
, XmATTACH_POSITION
,
1061 XmNrightPosition
, 39,
1062 XmNbottomAttachment
, XmATTACH_FORM
,
1063 XmNbottomOffset
, BORDER
, NULL
);
1064 XtAddCallback(checkBtn
, XmNactivateCallback
, checkCB
, NULL
);
1067 deleteBtn
= XtVaCreateManagedWidget("delete", xmPushButtonWidgetClass
, form
,
1068 XmNlabelString
, s1
=XmStringCreateSimple("Delete"),
1070 XmNleftAttachment
, XmATTACH_POSITION
,
1071 XmNleftPosition
, 39,
1072 XmNrightAttachment
, XmATTACH_POSITION
,
1073 XmNrightPosition
, 52,
1074 XmNbottomAttachment
, XmATTACH_FORM
,
1075 XmNbottomOffset
, BORDER
, NULL
);
1076 XtAddCallback(deleteBtn
, XmNactivateCallback
, deleteCB
, NULL
);
1079 restoreBtn
= XtVaCreateManagedWidget("restore", xmPushButtonWidgetClass
, form
,
1080 XmNlabelString
, s1
=XmStringCreateSimple("Restore Defaults"),
1082 XmNleftAttachment
, XmATTACH_POSITION
,
1083 XmNleftPosition
, 52,
1084 XmNrightAttachment
, XmATTACH_POSITION
,
1085 XmNrightPosition
, 73,
1086 XmNbottomAttachment
, XmATTACH_FORM
,
1087 XmNbottomOffset
, BORDER
, NULL
);
1088 XtAddCallback(restoreBtn
, XmNactivateCallback
, restoreCB
, NULL
);
1091 closeBtn
= XtVaCreateManagedWidget("close", xmPushButtonWidgetClass
,
1093 XmNlabelString
, s1
=XmStringCreateSimple("Close"),
1094 XmNleftAttachment
, XmATTACH_POSITION
,
1095 XmNleftPosition
, 73,
1096 XmNrightAttachment
, XmATTACH_POSITION
,
1097 XmNrightPosition
, 86,
1098 XmNbottomAttachment
, XmATTACH_FORM
,
1099 XmNbottomOffset
, BORDER
, NULL
);
1100 XtAddCallback(closeBtn
, XmNactivateCallback
, closeCB
, NULL
);
1103 helpBtn
= XtVaCreateManagedWidget("help", xmPushButtonWidgetClass
,
1105 XmNlabelString
, s1
=XmStringCreateSimple("Help"),
1107 XmNleftAttachment
, XmATTACH_POSITION
,
1108 XmNleftPosition
, 86,
1109 XmNrightAttachment
, XmATTACH_POSITION
,
1110 XmNrightPosition
, 99,
1111 XmNbottomAttachment
, XmATTACH_FORM
,
1112 XmNbottomOffset
, BORDER
, NULL
);
1113 XtAddCallback(helpBtn
, XmNactivateCallback
, helpCB
, NULL
);
1116 pane
= XtVaCreateManagedWidget("pane", xmPanedWindowWidgetClass
, form
,
1117 XmNleftAttachment
, XmATTACH_POSITION
,
1119 XmNrightAttachment
, XmATTACH_POSITION
,
1120 XmNrightPosition
, 99,
1121 XmNtopAttachment
, XmATTACH_WIDGET
,
1122 XmNtopWidget
, lmForm
,
1123 XmNbottomAttachment
, XmATTACH_WIDGET
,
1124 XmNbottomWidget
, okBtn
, NULL
);
1125 /* XmNmarginWidth, 0, XmNmarginHeight, 0, XmNseparatorOn, False,
1126 XmNspacing, 3, XmNsashIndent, -2, */
1128 initForm
= XtVaCreateManagedWidget("initForm", xmFormWidgetClass
,
1130 initLbl
= XtVaCreateManagedWidget("initLbl", xmLabelGadgetClass
, initForm
,
1131 XmNlabelString
, s1
=XmStringCreateSimple(
1132 "Language Specific Initialization Macro Commands and Definitions"),
1133 XmNmnemonic
, 'I', NULL
);
1136 XtSetArg(args
[n
], XmNeditMode
, XmMULTI_LINE_EDIT
); n
++;
1137 XtSetArg(args
[n
], XmNrows
, 5); n
++;
1138 XtSetArg(args
[n
], XmNcolumns
, 80); n
++;
1139 XtSetArg(args
[n
], XmNtopAttachment
, XmATTACH_WIDGET
); n
++;
1140 XtSetArg(args
[n
], XmNtopWidget
, initLbl
); n
++;
1141 XtSetArg(args
[n
], XmNleftAttachment
, XmATTACH_FORM
); n
++;
1142 XtSetArg(args
[n
], XmNrightAttachment
, XmATTACH_FORM
); n
++;
1143 XtSetArg(args
[n
], XmNbottomAttachment
, XmATTACH_FORM
); n
++;
1144 SmartIndentDialog
.initMacro
= XmCreateScrolledText(initForm
,
1145 "initMacro", args
, n
);
1146 AddMouseWheelSupport(SmartIndentDialog
.initMacro
);
1147 XtManageChild(SmartIndentDialog
.initMacro
);
1148 RemapDeleteKey(SmartIndentDialog
.initMacro
);
1149 XtVaSetValues(initLbl
, XmNuserData
, SmartIndentDialog
.initMacro
, NULL
);
1151 newlineForm
= XtVaCreateManagedWidget("newlineForm", xmFormWidgetClass
,
1153 newlineLbl
= XtVaCreateManagedWidget("newlineLbl", xmLabelGadgetClass
,
1155 XmNlabelString
, s1
=XmStringCreateSimple("Newline Macro"),
1156 XmNmnemonic
, 'N', NULL
);
1158 XtVaCreateManagedWidget("newlineArgsLbl", xmLabelGadgetClass
,
1159 newlineForm
, XmNalignment
, XmALIGNMENT_END
,
1160 XmNlabelString
, s1
=XmStringCreateSimple(
1161 "($1 is insert position, return indent request or -1)"),
1162 XmNrightAttachment
, XmATTACH_FORM
, NULL
);
1165 XtSetArg(args
[n
], XmNeditMode
, XmMULTI_LINE_EDIT
); n
++;
1166 XtSetArg(args
[n
], XmNrows
, 5); n
++;
1167 XtSetArg(args
[n
], XmNcolumns
, 80); n
++;
1168 XtSetArg(args
[n
], XmNtopAttachment
, XmATTACH_WIDGET
); n
++;
1169 XtSetArg(args
[n
], XmNtopWidget
, newlineLbl
); n
++;
1170 XtSetArg(args
[n
], XmNleftAttachment
, XmATTACH_FORM
); n
++;
1171 XtSetArg(args
[n
], XmNrightAttachment
, XmATTACH_FORM
); n
++;
1172 XtSetArg(args
[n
], XmNbottomAttachment
, XmATTACH_FORM
); n
++;
1173 SmartIndentDialog
.newlineMacro
= XmCreateScrolledText(newlineForm
,
1174 "newlineMacro", args
, n
);
1175 AddMouseWheelSupport(SmartIndentDialog
.newlineMacro
);
1176 XtManageChild(SmartIndentDialog
.newlineMacro
);
1177 RemapDeleteKey(SmartIndentDialog
.newlineMacro
);
1178 XtVaSetValues(newlineLbl
, XmNuserData
, SmartIndentDialog
.newlineMacro
,NULL
);
1180 modifyForm
= XtVaCreateManagedWidget("modifyForm", xmFormWidgetClass
,
1182 modifyLbl
= XtVaCreateManagedWidget("modifyLbl", xmLabelGadgetClass
,
1183 modifyForm
, XmNlabelString
,s1
=XmStringCreateSimple("Type-in Macro"),
1184 XmNmnemonic
, 'M', NULL
);
1186 XtVaCreateManagedWidget("modifyArgsLbl", xmLabelGadgetClass
,
1187 modifyForm
, XmNalignment
, XmALIGNMENT_END
,
1188 XmNlabelString
, s1
=XmStringCreateSimple(
1189 "($1 is position, $2 is character just inserted)"),
1190 XmNrightAttachment
, XmATTACH_FORM
, NULL
);
1193 XtSetArg(args
[n
], XmNeditMode
, XmMULTI_LINE_EDIT
); n
++;
1194 XtSetArg(args
[n
], XmNrows
, 5); n
++;
1195 XtSetArg(args
[n
], XmNcolumns
, 80); n
++;
1196 XtSetArg(args
[n
], XmNtopAttachment
, XmATTACH_WIDGET
); n
++;
1197 XtSetArg(args
[n
], XmNtopWidget
, modifyLbl
); n
++;
1198 XtSetArg(args
[n
], XmNleftAttachment
, XmATTACH_FORM
); n
++;
1199 XtSetArg(args
[n
], XmNrightAttachment
, XmATTACH_FORM
); n
++;
1200 XtSetArg(args
[n
], XmNbottomAttachment
, XmATTACH_FORM
); n
++;
1201 SmartIndentDialog
.modMacro
= XmCreateScrolledText(modifyForm
,
1202 "modifyMacro", args
, n
);
1203 AddMouseWheelSupport(SmartIndentDialog
.modMacro
);
1204 XtManageChild(SmartIndentDialog
.modMacro
);
1205 RemapDeleteKey(SmartIndentDialog
.modMacro
);
1206 XtVaSetValues(modifyLbl
, XmNuserData
, SmartIndentDialog
.modMacro
, NULL
);
1208 /* Set initial default button */
1209 XtVaSetValues(form
, XmNdefaultButton
, okBtn
, NULL
);
1210 XtVaSetValues(form
, XmNcancelButton
, closeBtn
, NULL
);
1212 /* Handle mnemonic selection of buttons and focus to dialog */
1213 AddDialogMnemonicHandler(form
, FALSE
);
1215 /* Fill in the dialog information for the selected language mode */
1216 setSmartIndentDialogData(findIndentSpec(lmName
));
1217 SetLangModeMenu(SmartIndentDialog
.lmOptMenu
,SmartIndentDialog
.langModeName
);
1219 /* Realize all of the widgets in the new dialog */
1220 RealizeWithoutForcingPosition(SmartIndentDialog
.shell
);
1223 static void destroyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1225 XtFree(SmartIndentDialog
.langModeName
);
1226 SmartIndentDialog
.shell
= NULL
;
1229 static void langModeCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1233 static smartIndentRec emptyIndentSpec
= {NULL
, NULL
, NULL
, NULL
};
1234 smartIndentRec
*oldMacros
, *newMacros
;
1236 /* Get the newly selected mode name. If it's the same, do nothing */
1237 XtVaGetValues(w
, XmNuserData
, &modeName
, NULL
);
1238 if (!strcmp(modeName
, SmartIndentDialog
.langModeName
))
1241 /* Find the original macros */
1242 for (i
=0; i
<NSmartIndentSpecs
; i
++)
1243 if (!strcmp(SmartIndentDialog
.langModeName
,SmartIndentSpecs
[i
]->lmName
))
1245 oldMacros
= i
== NSmartIndentSpecs
? &emptyIndentSpec
: SmartIndentSpecs
[i
];
1247 /* Check if the macros have changed, if so allow user to apply, discard,
1249 newMacros
= getSmartIndentDialogData();
1250 if (indentSpecsDiffer(oldMacros
, newMacros
))
1252 resp
= DialogF(DF_QUES
, SmartIndentDialog
.shell
, 3, "Smart Indent",
1253 "Smart indent macros for language mode\n"
1254 "%s were changed. Apply changes?", "Apply", "Discard",
1255 "Cancel", SmartIndentDialog
.langModeName
);
1259 SetLangModeMenu(SmartIndentDialog
.lmOptMenu
,
1260 SmartIndentDialog
.langModeName
);
1262 } else if (resp
== 1)
1264 if (checkSmartIndentDialogData())
1266 if (oldMacros
== &emptyIndentSpec
)
1268 SmartIndentSpecs
[NSmartIndentSpecs
++]
1269 = copyIndentSpec(newMacros
);
1272 freeIndentSpec(oldMacros
);
1273 SmartIndentSpecs
[i
] = copyIndentSpec(newMacros
);
1277 SetLangModeMenu(SmartIndentDialog
.lmOptMenu
,
1278 SmartIndentDialog
.langModeName
);
1283 freeIndentSpec(newMacros
);
1285 /* Fill the dialog with the new language mode information */
1286 SmartIndentDialog
.langModeName
= XtNewString(modeName
);
1287 setSmartIndentDialogData(findIndentSpec(modeName
));
1290 static void lmDialogCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1292 EditLanguageModes();
1295 static void commonDialogCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1297 EditCommonSmartIndentMacro();
1300 static void okCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1302 /* change the macro */
1303 if (!updateSmartIndentData())
1306 /* pop down and destroy the dialog */
1307 CloseAllPopupsFor(SmartIndentDialog
.shell
);
1308 XtDestroyWidget(SmartIndentDialog
.shell
);
1311 static void applyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1313 /* change the patterns */
1314 updateSmartIndentData();
1317 static void checkCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1319 if (checkSmartIndentDialogData())
1320 DialogF(DF_INF
, SmartIndentDialog
.shell
, 1, "Macro compiled",
1321 "Macros compiled without error", "OK");
1324 static void restoreCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1327 smartIndentRec
*defaultIS
;
1329 /* Find the default indent spec */
1330 for (i
=0; i
<N_DEFAULT_INDENT_SPECS
; i
++)
1332 if (!strcmp(SmartIndentDialog
.langModeName
,
1333 DefaultIndentSpecs
[i
].lmName
))
1339 if (i
== N_DEFAULT_INDENT_SPECS
)
1341 DialogF(DF_WARN
, SmartIndentDialog
.shell
, 1, "Smart Indent",
1342 "There are no default indent macros\nfor language mode %s",
1343 "OK", SmartIndentDialog
.langModeName
);
1346 defaultIS
= &DefaultIndentSpecs
[i
];
1348 if (DialogF(DF_WARN
, SmartIndentDialog
.shell
, 2, "Discard Changes",
1349 "Are you sure you want to discard\n"
1350 "all changes to smart indent macros\n"
1351 "for language mode %s?", "Discard", "Cancel",
1352 SmartIndentDialog
.langModeName
) == 2)
1357 /* if a stored version of the indent macros exist, replace them, if not,
1359 for (i
=0; i
<NSmartIndentSpecs
; i
++)
1360 if (!strcmp(SmartIndentDialog
.langModeName
,SmartIndentSpecs
[i
]->lmName
))
1362 if (i
< NSmartIndentSpecs
) {
1363 freeIndentSpec(SmartIndentSpecs
[i
]);
1364 SmartIndentSpecs
[i
] = copyIndentSpec(defaultIS
);
1366 SmartIndentSpecs
[NSmartIndentSpecs
++] = copyIndentSpec(defaultIS
);
1368 /* Update the dialog */
1369 setSmartIndentDialogData(defaultIS
);
1372 static void deleteCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1376 if (DialogF(DF_WARN
, SmartIndentDialog
.shell
, 2, "Delete Macros",
1377 "Are you sure you want to delete smart indent\n"
1378 "macros for language mode %s?", "Yes, Delete", "Cancel",
1379 SmartIndentDialog
.langModeName
) == 2)
1384 /* if a stored version of the pattern set exists, delete it from the list */
1385 for (i
=0; i
<NSmartIndentSpecs
; i
++)
1386 if (!strcmp(SmartIndentDialog
.langModeName
,SmartIndentSpecs
[i
]->lmName
))
1388 if (i
< NSmartIndentSpecs
) {
1389 freeIndentSpec(SmartIndentSpecs
[i
]);
1390 memmove(&SmartIndentSpecs
[i
], &SmartIndentSpecs
[i
+1],
1391 (NSmartIndentSpecs
-1 - i
) * sizeof(smartIndentRec
*));
1392 NSmartIndentSpecs
--;
1395 /* Clear out the dialog */
1396 setSmartIndentDialogData(NULL
);
1399 static void closeCB(Widget widget
, XtPointer clientData
, XtPointer callData
)
1401 /* pop down and destroy the dialog */
1402 CloseAllPopupsFor(SmartIndentDialog
.shell
);
1403 XtDestroyWidget(SmartIndentDialog
.shell
);
1406 static void helpCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1408 Help(HELP_SMART_INDENT
);
1411 static int checkSmartIndentDialogData(void)
1413 char *widgetText
, *errMsg
, *stoppedAt
;
1416 /* Check the initialization macro */
1417 if (!TextWidgetIsBlank(SmartIndentDialog
.initMacro
)) {
1418 widgetText
=ensureNewline(XmTextGetString(SmartIndentDialog
.initMacro
));
1419 if (!CheckMacroString(SmartIndentDialog
.shell
, widgetText
,
1420 "initialization macro", &stoppedAt
)) {
1421 XmTextSetInsertionPosition(SmartIndentDialog
.initMacro
,
1422 stoppedAt
- widgetText
);
1423 XmProcessTraversal(SmartIndentDialog
.initMacro
, XmTRAVERSE_CURRENT
);
1430 /* Test compile the newline macro */
1431 if (TextWidgetIsBlank(SmartIndentDialog
.newlineMacro
))
1433 DialogF(DF_WARN
, SmartIndentDialog
.shell
, 1, "Smart Indent",
1434 "Newline macro required", "OK");
1438 widgetText
= ensureNewline(XmTextGetString(SmartIndentDialog
.newlineMacro
));
1439 prog
= ParseMacro(widgetText
, &errMsg
, &stoppedAt
);
1441 ParseError(SmartIndentDialog
.shell
, widgetText
, stoppedAt
,
1442 "newline macro", errMsg
);
1443 XmTextSetInsertionPosition(SmartIndentDialog
.newlineMacro
,
1444 stoppedAt
- widgetText
);
1445 XmProcessTraversal(SmartIndentDialog
.newlineMacro
, XmTRAVERSE_CURRENT
);
1452 /* Test compile the modify macro */
1453 if (!TextWidgetIsBlank(SmartIndentDialog
.modMacro
)) {
1454 widgetText
= ensureNewline(XmTextGetString(SmartIndentDialog
.modMacro
));
1455 prog
= ParseMacro(widgetText
, &errMsg
, &stoppedAt
);
1457 ParseError(SmartIndentDialog
.shell
, widgetText
, stoppedAt
,
1458 "modify macro", errMsg
);
1459 XmTextSetInsertionPosition(SmartIndentDialog
.modMacro
,
1460 stoppedAt
- widgetText
);
1461 XmProcessTraversal(SmartIndentDialog
.modMacro
, XmTRAVERSE_CURRENT
);
1471 static smartIndentRec
*getSmartIndentDialogData(void)
1475 is
= (smartIndentRec
*)XtMalloc(sizeof(smartIndentRec
));
1476 is
->lmName
= XtNewString(SmartIndentDialog
.langModeName
);
1477 is
->initMacro
= TextWidgetIsBlank(SmartIndentDialog
.initMacro
) ? NULL
:
1478 ensureNewline(XmTextGetString(SmartIndentDialog
.initMacro
));
1479 is
->newlineMacro
= TextWidgetIsBlank(SmartIndentDialog
.newlineMacro
) ? NULL
:
1480 ensureNewline(XmTextGetString(SmartIndentDialog
.newlineMacro
));
1481 is
->modMacro
= TextWidgetIsBlank(SmartIndentDialog
.modMacro
) ? NULL
:
1482 ensureNewline(XmTextGetString(SmartIndentDialog
.modMacro
));
1486 static void setSmartIndentDialogData(smartIndentRec
*is
)
1489 XmTextSetString(SmartIndentDialog
.initMacro
, "");
1490 XmTextSetString(SmartIndentDialog
.newlineMacro
, "");
1491 XmTextSetString(SmartIndentDialog
.modMacro
, "");
1493 if (is
->initMacro
== NULL
)
1494 XmTextSetString(SmartIndentDialog
.initMacro
, "");
1496 XmTextSetString(SmartIndentDialog
.initMacro
, is
->initMacro
);
1497 XmTextSetString(SmartIndentDialog
.newlineMacro
, is
->newlineMacro
);
1498 if (is
->modMacro
== NULL
)
1499 XmTextSetString(SmartIndentDialog
.modMacro
, "");
1501 XmTextSetString(SmartIndentDialog
.modMacro
, is
->modMacro
);
1505 void EditCommonSmartIndentMacro(void)
1507 #define VERT_BORDER 4
1508 Widget form
, topLbl
;
1509 Widget okBtn
, applyBtn
, checkBtn
;
1510 Widget closeBtn
, restoreBtn
;
1515 /* if the dialog is already displayed, just pop it to the top and return */
1516 if (CommonDialog
.shell
!= NULL
) {
1517 RaiseShellWindow(CommonDialog
.shell
);
1521 /* Create a form widget in an application shell */
1523 XtSetArg(args
[n
], XmNdeleteResponse
, XmDO_NOTHING
); n
++;
1524 XtSetArg(args
[n
], XmNiconName
, "NEdit Common Smart Indent Macros"); n
++;
1525 XtSetArg(args
[n
], XmNtitle
, "Common Smart Indent Macros"); n
++;
1526 CommonDialog
.shell
= CreateWidget(TheAppShell
, "smartIndent",
1527 topLevelShellWidgetClass
, args
, n
);
1528 AddSmallIcon(CommonDialog
.shell
);
1529 form
= XtVaCreateManagedWidget("editCommonSIMacros", xmFormWidgetClass
,
1530 CommonDialog
.shell
, XmNautoUnmanage
, False
,
1531 XmNresizePolicy
, XmRESIZE_NONE
, NULL
);
1532 XtAddCallback(form
, XmNdestroyCallback
, comDestroyCB
, NULL
);
1533 AddMotifCloseCallback(CommonDialog
.shell
, comCloseCB
, NULL
);
1535 topLbl
= XtVaCreateManagedWidget("topLbl", xmLabelGadgetClass
, form
,
1536 XmNlabelString
, s1
=XmStringCreateSimple(
1537 "Common Definitions for Smart Indent Macros"),
1539 XmNtopAttachment
, XmATTACH_FORM
,
1540 XmNtopOffset
, VERT_BORDER
,
1541 XmNleftAttachment
, XmATTACH_POSITION
,
1542 XmNleftPosition
, 1, NULL
);
1544 okBtn
= XtVaCreateManagedWidget("ok", xmPushButtonWidgetClass
, form
,
1545 XmNlabelString
, s1
=XmStringCreateSimple("OK"),
1546 XmNmarginWidth
, BUTTON_WIDTH_MARGIN
,
1547 XmNleftAttachment
, XmATTACH_POSITION
,
1549 XmNrightAttachment
, XmATTACH_POSITION
,
1550 XmNrightPosition
, 18,
1551 XmNbottomAttachment
, XmATTACH_FORM
,
1552 XmNbottomOffset
, VERT_BORDER
, NULL
);
1553 XtAddCallback(okBtn
, XmNactivateCallback
, comOKCB
, NULL
);
1556 applyBtn
= XtVaCreateManagedWidget("apply", xmPushButtonWidgetClass
, form
,
1557 XmNlabelString
, s1
=XmStringCreateSimple("Apply"),
1559 XmNleftAttachment
, XmATTACH_POSITION
,
1560 XmNleftPosition
, 22,
1561 XmNrightAttachment
, XmATTACH_POSITION
,
1562 XmNrightPosition
, 35,
1563 XmNbottomAttachment
, XmATTACH_FORM
,
1564 XmNbottomOffset
, VERT_BORDER
, NULL
);
1565 XtAddCallback(applyBtn
, XmNactivateCallback
, comApplyCB
, NULL
);
1568 checkBtn
= XtVaCreateManagedWidget("check", xmPushButtonWidgetClass
, form
,
1569 XmNlabelString
, s1
=XmStringCreateSimple("Check"),
1571 XmNleftAttachment
, XmATTACH_POSITION
,
1572 XmNleftPosition
, 39,
1573 XmNrightAttachment
, XmATTACH_POSITION
,
1574 XmNrightPosition
, 52,
1575 XmNbottomAttachment
, XmATTACH_FORM
,
1576 XmNbottomOffset
, VERT_BORDER
, NULL
);
1577 XtAddCallback(checkBtn
, XmNactivateCallback
, comCheckCB
, NULL
);
1580 restoreBtn
= XtVaCreateManagedWidget("restore", xmPushButtonWidgetClass
,
1582 XmNlabelString
, s1
=XmStringCreateSimple("Restore Default"),
1584 XmNleftAttachment
, XmATTACH_POSITION
,
1585 XmNleftPosition
, 56,
1586 XmNrightAttachment
, XmATTACH_POSITION
,
1587 XmNrightPosition
, 77,
1588 XmNbottomAttachment
, XmATTACH_FORM
,
1589 XmNbottomOffset
, VERT_BORDER
, NULL
);
1590 XtAddCallback(restoreBtn
, XmNactivateCallback
, comRestoreCB
, NULL
);
1593 closeBtn
= XtVaCreateManagedWidget("close", xmPushButtonWidgetClass
,
1595 XmNlabelString
, s1
=XmStringCreateSimple("Close"),
1596 XmNleftAttachment
, XmATTACH_POSITION
,
1597 XmNleftPosition
, 81,
1598 XmNrightAttachment
, XmATTACH_POSITION
,
1599 XmNrightPosition
, 94,
1600 XmNbottomAttachment
, XmATTACH_FORM
,
1601 XmNbottomOffset
, VERT_BORDER
, NULL
);
1602 XtAddCallback(closeBtn
, XmNactivateCallback
, comCloseCB
, NULL
);
1606 XtSetArg(args
[n
], XmNeditMode
, XmMULTI_LINE_EDIT
); n
++;
1607 XtSetArg(args
[n
], XmNrows
, 24); n
++;
1608 XtSetArg(args
[n
], XmNcolumns
, 80); n
++;
1609 XtSetArg(args
[n
], XmNvalue
, CommonMacros
); n
++;
1610 XtSetArg(args
[n
], XmNtopAttachment
, XmATTACH_WIDGET
); n
++;
1611 XtSetArg(args
[n
], XmNtopWidget
, topLbl
); n
++;
1612 XtSetArg(args
[n
], XmNleftAttachment
, XmATTACH_POSITION
); n
++;
1613 XtSetArg(args
[n
], XmNleftPosition
, 1); n
++;
1614 XtSetArg(args
[n
], XmNrightAttachment
, XmATTACH_POSITION
); n
++;
1615 XtSetArg(args
[n
], XmNrightPosition
, 99); n
++;
1616 XtSetArg(args
[n
], XmNbottomAttachment
, XmATTACH_WIDGET
); n
++;
1617 XtSetArg(args
[n
], XmNbottomWidget
, okBtn
); n
++;
1618 XtSetArg(args
[n
], XmNbottomOffset
, VERT_BORDER
); n
++;
1619 CommonDialog
.text
= XmCreateScrolledText(form
, "commonText", args
, n
);
1620 AddMouseWheelSupport(CommonDialog
.text
);
1621 XtManageChild(CommonDialog
.text
);
1622 RemapDeleteKey(CommonDialog
.text
);
1623 XtVaSetValues(topLbl
, XmNuserData
, CommonDialog
.text
, NULL
);
1625 /* Set initial default button */
1626 XtVaSetValues(form
, XmNdefaultButton
, okBtn
, NULL
);
1627 XtVaSetValues(form
, XmNcancelButton
, closeBtn
, NULL
);
1629 /* Handle mnemonic selection of buttons and focus to dialog */
1630 AddDialogMnemonicHandler(form
, FALSE
);
1632 /* Realize all of the widgets in the new dialog */
1633 RealizeWithoutForcingPosition(CommonDialog
.shell
);
1636 static void comDestroyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1638 CommonDialog
.shell
= NULL
;
1641 static void comOKCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1643 /* change the macro */
1644 if (!updateSmartIndentCommonData())
1647 /* pop down and destroy the dialog */
1648 XtDestroyWidget(CommonDialog
.shell
);
1651 static void comApplyCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1653 /* change the macro */
1654 updateSmartIndentCommonData();
1657 static void comCheckCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1659 if (checkSmartIndentCommonDialogData())
1661 DialogF(DF_INF
, CommonDialog
.shell
, 1, "Macro compiled",
1662 "Macros compiled without error", "OK");
1666 static void comRestoreCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1668 if (DialogF(DF_WARN
, CommonDialog
.shell
, 2, "Discard Changes",
1669 "Are you sure you want to discard all\n"
1670 "changes to common smart indent macros", "Discard", "Cancel") == 2)
1675 /* replace common macros with default */
1676 if (CommonMacros
!= NULL
)
1677 XtFree(CommonMacros
);
1678 CommonMacros
= XtNewString(DefaultCommonMacros
);
1680 /* Update the dialog */
1681 XmTextSetString(CommonDialog
.text
, CommonMacros
);
1684 static void comCloseCB(Widget w
, XtPointer clientData
, XtPointer callData
)
1686 /* pop down and destroy the dialog */
1687 XtDestroyWidget(CommonDialog
.shell
);
1691 ** Update the smart indent macros being edited in the dialog
1692 ** with the information that the dialog is currently displaying, and
1693 ** apply changes to any window which is currently using the macros.
1695 static int updateSmartIndentCommonData(void)
1699 /* Make sure the patterns are valid and compile */
1700 if (!checkSmartIndentCommonDialogData())
1703 /* Get the current data */
1704 CommonMacros
= ensureNewline(XmTextGetString(CommonDialog
.text
));
1706 /* Re-execute initialization macros (macros require a window to function,
1707 since user could theoretically execute an action routine, but it
1708 probably won't be referenced in a smart indent initialization) */
1709 if (!ReadMacroString(WindowList
, CommonMacros
, "common macros"))
1712 /* Find windows that are currently using smart indent and
1713 re-initialize the smart indent macros (in case they have initialization
1714 data which depends on common data) */
1715 for (window
=WindowList
; window
!=NULL
; window
=window
->next
) {
1716 if (window
->indentStyle
== SMART_INDENT
&&
1717 window
->languageMode
!= PLAIN_LANGUAGE_MODE
) {
1718 EndSmartIndent(window
);
1719 BeginSmartIndent(window
, False
);
1723 /* Note that preferences have been changed */
1729 static int checkSmartIndentCommonDialogData(void)
1731 char *widgetText
, *stoppedAt
;
1733 if (!TextWidgetIsBlank(CommonDialog
.text
)) {
1734 widgetText
= ensureNewline(XmTextGetString(CommonDialog
.text
));
1735 if (!CheckMacroString(CommonDialog
.shell
, widgetText
,
1736 "macros", &stoppedAt
)) {
1737 XmTextSetInsertionPosition(CommonDialog
.text
, stoppedAt
-widgetText
);
1738 XmProcessTraversal(CommonDialog
.text
, XmTRAVERSE_CURRENT
);
1748 ** Update the smart indent macros being edited in the dialog
1749 ** with the information that the dialog is currently displaying, and
1750 ** apply changes to any window which is currently using the macros.
1752 static int updateSmartIndentData(void)
1754 smartIndentRec
*newMacros
;
1759 /* Make sure the patterns are valid and compile */
1760 if (!checkSmartIndentDialogData())
1763 /* Get the current data */
1764 newMacros
= getSmartIndentDialogData();
1766 /* Find the original macros */
1767 for (i
=0; i
<NSmartIndentSpecs
; i
++)
1768 if (!strcmp(SmartIndentDialog
.langModeName
,SmartIndentSpecs
[i
]->lmName
))
1771 /* If it's a new language, add it at the end, otherwise free the
1772 existing macros and replace it */
1773 if (i
== NSmartIndentSpecs
) {
1774 SmartIndentSpecs
[NSmartIndentSpecs
++] = newMacros
;
1776 freeIndentSpec(SmartIndentSpecs
[i
]);
1777 SmartIndentSpecs
[i
] = newMacros
;
1780 /* Find windows that are currently using this indent specification and
1781 re-do the smart indent macros */
1782 for (window
=WindowList
; window
!=NULL
; window
=window
->next
) {
1783 lmName
= LanguageModeName(window
->languageMode
);
1784 if (lmName
!= NULL
&& !strcmp(lmName
, newMacros
->lmName
)) {
1785 SetSensitive(window
, window
->smartIndentItem
, True
);
1786 if (window
->indentStyle
== SMART_INDENT
&&
1787 window
->languageMode
!= PLAIN_LANGUAGE_MODE
) {
1788 EndSmartIndent(window
);
1789 BeginSmartIndent(window
, False
);
1794 /* Note that preferences have been changed */
1800 static int loadDefaultIndentSpec(char *lmName
)
1804 for (i
=0; i
<N_DEFAULT_INDENT_SPECS
; i
++) {
1805 if (!strcmp(lmName
, DefaultIndentSpecs
[i
].lmName
)) {
1806 SmartIndentSpecs
[NSmartIndentSpecs
++] =
1807 copyIndentSpec(&DefaultIndentSpecs
[i
]);
1814 int LoadSmartIndentString(char *inString
)
1816 char *errMsg
, *inPtr
= inString
;
1817 smartIndentRec is
, *isCopy
;
1822 /* skip over blank space */
1823 inPtr
+= strspn(inPtr
, " \t\n");
1829 /* read language mode name */
1830 is
.lmName
= ReadSymbolicField(&inPtr
);
1831 if (is
.lmName
== NULL
)
1832 return siParseError(inString
, inPtr
, "language mode name required");
1833 if (!SkipDelimiter(&inPtr
, &errMsg
)) {
1835 return siParseError(inString
, inPtr
, errMsg
);
1838 /* look for "Default" keyword, and if it's there, return the default
1839 smart indent macros */
1840 if (!strncmp(inPtr
, "Default", 7)) {
1842 if (!loadDefaultIndentSpec(is
.lmName
)) {
1844 return siParseError(inString
, inPtr
,
1845 "no default smart indent macros");
1851 /* read the initialization macro (arbitrary text terminated by the
1852 macro end boundary string) */
1853 is
.initMacro
= readSIMacro(&inPtr
);
1854 if (is
.initMacro
== NULL
) {
1856 return siParseError(inString
, inPtr
,
1857 "no end boundary to initialization macro");
1860 /* read the newline macro */
1861 is
.newlineMacro
= readSIMacro(&inPtr
);
1862 if (is
.newlineMacro
== NULL
) {
1864 XtFree(is
.initMacro
);
1865 return siParseError(inString
, inPtr
,
1866 "no end boundary to newline macro");
1869 /* read the modify macro */
1870 is
.modMacro
= readSIMacro(&inPtr
);
1871 if (is
.modMacro
== NULL
) {
1873 XtFree(is
.initMacro
);
1874 XtFree(is
.newlineMacro
);
1875 return siParseError(inString
, inPtr
,
1876 "no end boundary to modify macro");
1879 /* if there's no mod macro, make it null so it won't be executed */
1880 if (is
.modMacro
[0] == '\0') {
1881 XtFree(is
.modMacro
);
1885 /* create a new data structure and add/change it in the list */
1886 isCopy
= (smartIndentRec
*)XtMalloc(sizeof(smartIndentRec
));
1888 for (i
=0; i
<NSmartIndentSpecs
; i
++) {
1889 if (!strcmp(SmartIndentSpecs
[i
]->lmName
, is
.lmName
)) {
1890 freeIndentSpec(SmartIndentSpecs
[i
]);
1891 SmartIndentSpecs
[i
] = isCopy
;
1895 if (i
== NSmartIndentSpecs
)
1896 SmartIndentSpecs
[NSmartIndentSpecs
++] = isCopy
;
1900 int LoadSmartIndentCommonString(char *inString
)
1903 char *inPtr
= inString
;
1905 /* If called from -import, can replace existing ones */
1906 if (CommonMacros
!= NULL
)
1907 XtFree(CommonMacros
);
1909 /* skip over blank space */
1910 inPtr
+= strspn(inPtr
, " \t\n");
1912 /* look for "Default" keyword, and if it's there, return the default
1913 smart common macro */
1914 if (!strncmp(inPtr
, "Default", 7)) {
1915 CommonMacros
= XtNewString(DefaultCommonMacros
);
1919 /* Remove leading tabs added by writer routine */
1920 CommonMacros
= ShiftText(inPtr
, SHIFT_LEFT
, True
, 8, 8, &shiftedLen
);
1925 ** Read a macro (arbitrary text terminated by the macro end boundary string)
1926 ** from the position pointed to by *inPtr, trim off added tabs and return an
1927 ** allocated copy of the string, and advance *inPtr to the end of the macro.
1928 ** Returns NULL if the macro end boundary string is not found.
1930 static char *readSIMacro(char **inPtr
)
1932 char *retStr
, *macroStr
, *macroEnd
;
1935 /* Strip leading newline */
1936 if (**inPtr
== '\n')
1939 /* Find the end of the macro */
1940 macroEnd
= strstr(*inPtr
, MacroEndBoundary
);
1941 if (macroEnd
== NULL
)
1944 /* Copy the macro */
1945 macroStr
= XtMalloc(macroEnd
- *inPtr
+ 1);
1946 strncpy(macroStr
, *inPtr
, macroEnd
- *inPtr
);
1947 macroStr
[macroEnd
- *inPtr
] = '\0';
1949 /* Remove leading tabs added by writer routine */
1950 *inPtr
= macroEnd
+ strlen(MacroEndBoundary
);
1951 retStr
= ShiftText(macroStr
, SHIFT_LEFT
, True
, 8, 8, &shiftedLen
);
1956 static smartIndentRec
*copyIndentSpec(smartIndentRec
*is
)
1958 smartIndentRec
*ris
= (smartIndentRec
*)XtMalloc(sizeof(smartIndentRec
));
1959 ris
->lmName
= XtNewString(is
->lmName
);
1960 ris
->initMacro
= XtNewString(is
->initMacro
);
1961 ris
->newlineMacro
= XtNewString(is
->newlineMacro
);
1962 ris
->modMacro
= XtNewString(is
->modMacro
);
1966 static void freeIndentSpec(smartIndentRec
*is
)
1969 if (is
->initMacro
!= NULL
) XtFree(is
->initMacro
);
1970 XtFree(is
->newlineMacro
);
1971 if (is
->modMacro
!= NULL
)XtFree(is
->modMacro
);
1974 static int indentSpecsDiffer(smartIndentRec
*is1
, smartIndentRec
*is2
)
1976 return AllocatedStringsDiffer(is1
->initMacro
, is2
->initMacro
) ||
1977 AllocatedStringsDiffer(is1
->newlineMacro
, is2
->newlineMacro
) ||
1978 AllocatedStringsDiffer(is1
->modMacro
, is2
->modMacro
);
1981 static int siParseError(char *stringStart
, char *stoppedAt
, char *message
)
1983 return ParseError(NULL
, stringStart
, stoppedAt
,
1984 "smart indent specification", message
);
1987 char *WriteSmartIndentString(void)
1990 smartIndentRec
*sis
;
1992 char *outStr
, *escapedStr
;
1994 outBuf
= BufCreate();
1995 for (i
=0; i
<NSmartIndentSpecs
; i
++) {
1996 sis
= SmartIndentSpecs
[i
];
1997 BufInsert(outBuf
, outBuf
->length
, "\t");
1998 BufInsert(outBuf
, outBuf
->length
, sis
->lmName
);
1999 BufInsert(outBuf
, outBuf
->length
, ":");
2000 if (isDefaultIndentSpec(sis
))
2001 BufInsert(outBuf
, outBuf
->length
, "Default\n");
2003 insertShiftedMacro(outBuf
, sis
->initMacro
);
2004 insertShiftedMacro(outBuf
, sis
->newlineMacro
);
2005 insertShiftedMacro(outBuf
, sis
->modMacro
);
2009 /* Get the output string, and lop off the trailing newline */
2010 outStr
= BufGetRange(outBuf
, 0, outBuf
->length
> 0 ? outBuf
->length
-1 : 0);
2013 /* Protect newlines and backslashes from translation by the resource
2015 escapedStr
= EscapeSensitiveChars(outStr
);
2020 char *WriteSmartIndentCommonString(void)
2023 char *outStr
, *escapedStr
;
2025 if (!strcmp(CommonMacros
, DefaultCommonMacros
))
2026 return XtNewString("Default");
2027 if (CommonMacros
== NULL
)
2028 return XtNewString("");
2030 /* Shift the macro over by a tab to keep .nedit file bright and clean */
2031 outStr
= ShiftText(CommonMacros
, SHIFT_RIGHT
, True
, 8, 8, &len
);
2033 /* Protect newlines and backslashes from translation by the resource
2035 escapedStr
= EscapeSensitiveChars(outStr
);
2038 /* If there's a trailing escaped newline, remove it */
2039 len
= strlen(escapedStr
);
2040 if (len
> 1 && escapedStr
[len
-1] == '\n' && escapedStr
[len
-2] == '\\')
2041 escapedStr
[len
-2] = '\0';
2046 ** Insert macro text "macro" into buffer "buf" shifted right by 8 characters
2047 ** (so it looks nice in the .nedit file), and terminated with a macro-end-
2050 static void insertShiftedMacro(textBuffer
*buf
, char *macro
)
2055 if (macro
!= NULL
) {
2056 shiftedMacro
= ShiftText(macro
, SHIFT_RIGHT
, True
, 8, 8, &shiftedLen
);
2057 BufInsert(buf
, buf
->length
, shiftedMacro
);
2058 XtFree(shiftedMacro
);
2060 BufInsert(buf
, buf
->length
, "\t");
2061 BufInsert(buf
, buf
->length
, MacroEndBoundary
);
2062 BufInsert(buf
, buf
->length
, "\n");
2065 static int isDefaultIndentSpec(smartIndentRec
*indentSpec
)
2069 for (i
=0; i
<N_DEFAULT_INDENT_SPECS
; i
++)
2070 if (!strcmp(indentSpec
->lmName
, DefaultIndentSpecs
[i
].lmName
))
2071 return !indentSpecsDiffer(indentSpec
, &DefaultIndentSpecs
[i
]);
2075 static smartIndentRec
*findIndentSpec(const char *modeName
)
2079 if (modeName
== NULL
)
2082 for (i
=0; i
<NSmartIndentSpecs
; i
++)
2083 if (!strcmp(modeName
, SmartIndentSpecs
[i
]->lmName
))
2084 return SmartIndentSpecs
[i
];
2089 ** If "string" is not terminated with a newline character, return a
2090 ** reallocated string which does end in a newline (otherwise, just pass on
2091 ** string as function value). (The macro language requires newline terminators
2092 ** for statements, but the text widget doesn't force it like the NEdit text
2093 ** buffer does, so this might avoid some confusion.)
2095 static char *ensureNewline(char *string
)
2102 length
= strlen(string
);
2103 if (length
== 0 || string
[length
-1] == '\n')
2105 newString
= XtMalloc(length
+ 2);
2106 strcpy(newString
, string
);
2107 newString
[length
] = '\n';
2108 newString
[length
+1] = '\0';
2114 ** Returns True if there are smart indent macros, or potential macros
2115 ** not yet committed in the smart indent dialog for a language mode,
2117 int LMHasSmartIndentMacros(const char *languageMode
)
2119 if (findIndentSpec(languageMode
) != NULL
)
2121 return SmartIndentDialog
.shell
!=NULL
&& !strcmp(SmartIndentDialog
.langModeName
,
2126 ** Change the language mode name of smart indent macro sets for language
2127 ** "oldName" to "newName" in both the stored macro sets, and the pattern set
2128 ** currently being edited in the dialog.
2130 void RenameSmartIndentMacros(const char *oldName
, const char *newName
)
2134 for (i
=0; i
<NSmartIndentSpecs
; i
++) {
2135 if (!strcmp(oldName
, SmartIndentSpecs
[i
]->lmName
)) {
2136 XtFree(SmartIndentSpecs
[i
]->lmName
);
2137 SmartIndentSpecs
[i
]->lmName
= XtNewString(newName
);
2140 if (SmartIndentDialog
.shell
!= NULL
) {
2141 if (!strcmp(SmartIndentDialog
.langModeName
, oldName
)) {
2142 XtFree(SmartIndentDialog
.langModeName
);
2143 SmartIndentDialog
.langModeName
= XtNewString(newName
);
2149 ** If a smart indent dialog is up, ask to have the option menu for
2150 ** chosing language mode updated (via a call to CreateLanguageModeMenu)
2152 void UpdateLangModeMenuSmartIndent(void)
2156 if (SmartIndentDialog
.shell
== NULL
)
2159 oldMenu
= SmartIndentDialog
.lmPulldown
;
2160 SmartIndentDialog
.lmPulldown
= CreateLanguageModeMenu(
2161 XtParent(XtParent(oldMenu
)), langModeCB
, NULL
);
2162 XtVaSetValues(XmOptionButtonGadget(SmartIndentDialog
.lmOptMenu
),
2163 XmNsubMenuId
, SmartIndentDialog
.lmPulldown
, NULL
);
2164 SetLangModeMenu(SmartIndentDialog
.lmOptMenu
, SmartIndentDialog
.langModeName
);
2166 XtDestroyWidget(oldMenu
);