UNIXworkcode

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