UNIXworkcode

1 /******************************************************************************* 2 * * 3 * window.c -- Nirvana Editor window creation/deletion * 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 * May 10, 1991 * 24 * * 25 * Written by Mark Edel * 26 * * 27 *******************************************************************************/ 28 29 #ifdef HAVE_CONFIG_H 30 #include "../config.h" 31 #endif 32 33 #include "window.h" 34 #include "textBuf.h" 35 #include "textSel.h" 36 #include "text.h" 37 #include "textDisp.h" 38 #include "textP.h" 39 #include "nedit.h" 40 #include "menu.h" 41 #include "file.h" 42 #include "search.h" 43 #include "undo.h" 44 #include "preferences.h" 45 #include "selection.h" 46 #include "server.h" 47 #include "shell.h" 48 #include "macro.h" 49 #include "highlight.h" 50 #include "smartIndent.h" 51 #include "userCmds.h" 52 #include "nedit.bm" 53 #include "n.bm" 54 #include "windowTitle.h" 55 #include "interpret.h" 56 #include "rangeset.h" 57 #include "highlightData.h" 58 #include "../util/clearcase.h" 59 #include "../util/misc.h" 60 #include "../util/fileUtils.h" 61 #include "../util/utils.h" 62 #include "../util/fileUtils.h" 63 #include "../util/DialogF.h" 64 #include "../util/dragAndDrop.h" 65 #include "../Xlt/BubbleButtonP.h" 66 #include "../Microline/XmL/Folder.h" 67 #include "../util/nedit_malloc.h" 68 #include "../util/textfield.h" 69 #include "../util/fontsel.h" 70 71 #include <stdio.h> 72 #include <stdlib.h> 73 #include <string.h> 74 #include <sys/stat.h> 75 #include <langinfo.h> 76 #include <sys/param.h> 77 #include <limits.h> 78 #include <math.h> 79 #include <ctype.h> 80 #include <time.h> 81 #ifdef __unix__ 82 #include <sys/time.h> 83 #endif 84 85 #include <X11/Intrinsic.h> 86 #include <X11/Shell.h> 87 #include <X11/Xatom.h> 88 #include <Xm/Xm.h> 89 #include <Xm/MainW.h> 90 #include <Xm/PanedW.h> 91 #include <Xm/PanedWP.h> 92 #include <Xm/RowColumnP.h> 93 #include <Xm/Separator.h> 94 #include <Xm/Text.h> 95 #include <Xm/ToggleB.h> 96 #include <Xm/PushB.h> 97 #include <Xm/Form.h> 98 #include <Xm/Frame.h> 99 #include <Xm/Label.h> 100 #include <Xm/SelectioB.h> 101 #include <Xm/List.h> 102 #include <Xm/Protocols.h> 103 #include <Xm/ScrolledW.h> 104 #include <Xm/ScrollBar.h> 105 #include <Xm/PrimitiveP.h> 106 #include <Xm/Frame.h> 107 #include <Xm/CascadeB.h> 108 109 #ifndef __sun 110 #include <Xm/DropDown.h> 111 #else 112 Widget XmCreateDropDown(Widget parent, String name, ArgList args, Cardinal argcount); 113 #endif 114 115 #ifdef EDITRES 116 #include <X11/Xmu/Editres.h> 117 /* extern void _XEditResCheckMessages(); */ 118 #endif /* EDITRES */ 119 120 #ifdef HAVE_DEBUG_H 121 #include "../debug.h" 122 #endif 123 124 /* Initial minimum height of a pane. Just a fallback in case setPaneMinHeight 125 (which may break in a future release) is not available */ 126 #define PANE_MIN_HEIGHT 39 127 128 /* Thickness of 3D border around statistics and/or incremental search areas 129 below the main menu bar */ 130 #define STAT_SHADOW_THICKNESS 1 131 132 /* bitmap data for the close-tab button */ 133 #define close_width 11 134 #define close_height 11 135 static unsigned char close_bits[] = { 136 0x00, 0x00, 0x00, 0x00, 0x8c, 0x01, 0xdc, 0x01, 0xf8, 0x00, 0x70, 0x00, 137 0xf8, 0x00, 0xdc, 0x01, 0x8c, 0x01, 0x00, 0x00, 0x00, 0x00}; 138 139 #define close_m_width 17 140 #define close_m_height 17 141 static unsigned char close_m_bits[] = { 142 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x00, 143 0x38, 0x38, 0x00, 0x70, 0x1c, 0x00, 0xe0, 0x0e, 0x00, 0xc0, 0x07, 0x00, 144 0x80, 0x03, 0x00, 0xc0, 0x07, 0x00, 0xe0, 0x0e, 0x00, 0x70, 0x1c, 0x00, 145 0x38, 0x38, 0x00, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 146 0x00, 0x00, 0x00 }; 147 148 #define close_l_width 23 149 #define close_l_height 23 150 static unsigned char close_l_bits[] = { 151 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x0f, 152 0xf8, 0x80, 0x0f, 0xf8, 0xc1, 0x0f, 0xf8, 0xe3, 0x0f, 0xf0, 0xf7, 0x07, 153 0xe0, 0xff, 0x03, 0xc0, 0xff, 0x01, 0x80, 0xff, 0x00, 0x00, 0x7f, 0x00, 154 0x80, 0xff, 0x00, 0xc0, 0xff, 0x01, 0xe0, 0xff, 0x03, 0xf0, 0xf7, 0x07, 155 0xf8, 0xe3, 0x0f, 0xf8, 0xc1, 0x0f, 0xf8, 0x80, 0x0f, 0x78, 0x00, 0x0f, 156 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 157 158 /* bitmap data for the isearch-find button */ 159 #define isrcFind_width 11 160 #define isrcFind_height 11 161 static unsigned char isrcFind_bits[] = { 162 0xe0, 0x01, 0x10, 0x02, 0xc8, 0x04, 0x08, 0x04, 0x08, 0x04, 0x00, 0x04, 163 0x18, 0x02, 0xdc, 0x01, 0x0e, 0x00, 0x07, 0x00, 0x03, 0x00}; 164 165 #define isrcFind_m_width 17 166 #define isrcFind_m_height 17 167 static unsigned char isrcFind_m_bits[] = { 168 0x00, 0x1f, 0x00, 0x80, 0x3f, 0x00, 0xc0, 0x60, 0x00, 0x60, 0xc0, 0x00, 169 0x20, 0x8e, 0x00, 0x20, 0x80, 0x00, 0x20, 0x80, 0x00, 0x00, 0x80, 0x00, 170 0x00, 0xc0, 0x00, 0xe0, 0x60, 0x00, 0xf0, 0x38, 0x00, 0xf8, 0x1e, 0x00, 171 0x7c, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x0f, 0x00, 0x00, 172 0x07, 0x00, 0x00 }; 173 174 #define isrcFind_l_width 23 175 #define isrcFind_l_height 23 176 static unsigned char isrcFind_l_bits[] = { 177 0x00, 0xe0, 0x01, 0x00, 0xf8, 0x07, 0x00, 0x1c, 0x0e, 0x00, 0x0e, 0x1c, 178 0x00, 0x07, 0x38, 0x00, 0xe3, 0x31, 0x80, 0xe1, 0x61, 0x80, 0x01, 0x60, 179 0x80, 0x01, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, 0x38, 180 0x80, 0x07, 0x1c, 0xc0, 0x07, 0x0e, 0xe0, 0xc7, 0x07, 0xf0, 0xc7, 0x01, 181 0xf8, 0x03, 0x00, 0xfc, 0x01, 0x00, 0xfe, 0x00, 0x00, 0x7f, 0x00, 0x00, 182 0x3f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x0f, 0x00, 0x00 }; 183 184 /* bitmap data for the isearch-clear button */ 185 #define isrcClear_width 11 186 #define isrcClear_height 11 187 static unsigned char isrcClear_bits[] = { 188 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x84, 0x01, 0xc4, 0x00, 0x64, 0x00, 189 0xc4, 0x00, 0x84, 0x01, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00}; 190 191 #define isrcClear_m_width 17 192 #define isrcClear_m_height 17 193 static unsigned char isrcClear_m_bits[] = { 194 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x20, 0x00, 195 0x18, 0x30, 0x00, 0x18, 0x18, 0x00, 0x18, 0x0c, 0x00, 0x18, 0x06, 0x00, 196 0x18, 0x03, 0x00, 0x18, 0x06, 0x00, 0x18, 0x0c, 0x00, 0x18, 0x18, 0x00, 197 0x18, 0x30, 0x00, 0x18, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 198 0x00, 0x00, 0x00 }; 199 200 #define isrcClear_l_width 23 201 #define isrcClear_l_height 23 202 static unsigned char isrcClear_l_bits[] = { 203 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 204 0x30, 0x00, 0x02, 0x30, 0x00, 0x03, 0x30, 0x80, 0x03, 0x30, 0xc0, 0x01, 205 0x30, 0xe0, 0x00, 0x30, 0x70, 0x00, 0x30, 0x38, 0x00, 0x30, 0x1c, 0x00, 206 0x30, 0x38, 0x00, 0x30, 0x70, 0x00, 0x30, 0xe0, 0x00, 0x30, 0xc0, 0x01, 207 0x30, 0x80, 0x03, 0x30, 0x00, 0x03, 0x30, 0x00, 0x02, 0x00, 0x00, 0x00, 208 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 209 210 extern void _XmDismissTearOff(Widget, XtPointer, XtPointer); 211 212 static void hideTooltip(Widget tab); 213 static Pixmap createBitmapWithDepth(Widget w, char *data, unsigned int width, 214 unsigned int height); 215 static void createSearchForm(WindowInfo *window); 216 static WindowInfo *getNextTabWindow(WindowInfo *window, int direction, 217 int crossWin, int wrap); 218 static Widget addTab(Widget folder, const char *string); 219 static int compareWindowNames(const void *windowA, const void *windowB); 220 static int getTabPosition(Widget tab); 221 static Widget manageToolBars(Widget toolBarsForm); 222 static void hideTearOffs(Widget menuPane); 223 static void CloseDocumentWindow(Widget w, WindowInfo *window, XtPointer callData); 224 static void closeTabCB(Widget w, Widget mainWin, caddr_t callData); 225 static void raiseTabCB(Widget w, XtPointer clientData, XtPointer callData); 226 static Widget createTextArea(Widget parent, WindowInfo *window, int rows, 227 int cols, int emTabDist, char *delimiters, int wrapMargin, 228 int lineNumCols); 229 static void showStats(WindowInfo *window, int state); 230 static void showISearch(WindowInfo *window, int state); 231 static void showStatsForm(WindowInfo *window); 232 static void addToWindowList(WindowInfo *window); 233 static void removeFromWindowList(WindowInfo *window); 234 static void focusCB(Widget w, WindowInfo *window, XtPointer callData); 235 static void modifiedCB(int pos, int nInserted, int nDeleted, int nRestyled, 236 const char *deletedText, void *cbArg); 237 static void beginModifyCB(void *cbArg); 238 static void endModifyCB(void *cbArg); 239 static void movedCB(Widget w, WindowInfo *window, XtPointer callData); 240 static void dragStartCB(Widget w, WindowInfo *window, XtPointer callData); 241 static void dragEndCB(Widget w, WindowInfo *window, dragEndCBStruct *callData); 242 static void closeCB(Widget w, WindowInfo *window, XtPointer callData); 243 static void saveYourselfCB(Widget w, Widget appShell, XtPointer callData); 244 static void setPaneDesiredHeight(Widget w, int height); 245 static void setPaneMinHeight(Widget w, int min); 246 static void addWindowIcon(Widget shell); 247 static void wmSizeUpdateProc(XtPointer clientData, XtIntervalId *id); 248 static void getGeometryString(WindowInfo *window, char *geomString); 249 #ifdef ROWCOLPATCH 250 static void patchRowCol(Widget w); 251 static void patchedRemoveChild(Widget child); 252 #endif 253 static void refreshMenuBar(WindowInfo *window); 254 static void cloneDocument(WindowInfo *window, WindowInfo *orgWin); 255 static void cloneTextPanes(WindowInfo *window, WindowInfo *orgWin); 256 static UndoInfo *cloneUndoItems(UndoInfo *orgList); 257 static Widget containingPane(Widget w); 258 259 static WindowInfo *inFocusDocument = NULL; /* where we are now */ 260 static WindowInfo *lastFocusDocument = NULL; /* where we came from */ 261 static int DoneWithMoveDocumentDialog; 262 static int updateLineNumDisp(WindowInfo* window); 263 static int updateGutterWidth(WindowInfo* window); 264 static void deleteDocument(WindowInfo *window); 265 static void cancelTimeOut(XtIntervalId *timer); 266 267 static void WindowTakeFocus(Widget shell, WindowInfo *window, XtPointer d); 268 269 static void closeInfoBarCB(Widget w, Widget mainWin, void *callData); 270 static void jumpToEncErrorCB(Widget w, Widget mainWin, XmComboBoxCallbackStruct *cb); 271 static void reloadCB(Widget w, Widget mainWin, void *callData); 272 static void windowStructureNotifyEventEH( 273 Widget widget, 274 XtPointer data, 275 XEvent *event, 276 Boolean *dispatch); 277 278 /* From Xt, Shell.c, "BIGSIZE" */ 279 static const Dimension XT_IGNORE_PPOSITION = 32767; 280 281 static Atom wm_take_focus; 282 static int take_focus_atom_is_init = 0; 283 284 static Bool CopyDBEntry( 285 XrmDatabase *db, 286 XrmBindingList bindings, 287 XrmQuarkList quarks, 288 XrmRepresentation *type, 289 XrmValuePtr value, 290 XPointer data) 291 { 292 XrmDatabase newDB = (XrmDatabase)data; 293 XrmQPutResource(&newDB, bindings, quarks, *type, value); 294 return 0; 295 } 296 297 void LoadColorProfileResources(Display *display, ColorProfile *profile) 298 { 299 if(profile->resourceFile) { 300 const char *fpath; 301 char *fpathFree = NULL; 302 if(profile->resourceFile[0] == '/') { 303 fpath = profile->resourceFile; 304 } else { 305 const char *xneditHome = GetRCFileName(XNEDIT_HOME); 306 size_t xneditHomeLen = strlen(xneditHome); 307 size_t resFileLen = strlen(profile->resourceFile); 308 fpathFree = NEditMalloc(xneditHomeLen + resFileLen + 1); 309 memcpy(fpathFree, xneditHome, xneditHomeLen); 310 memcpy(fpathFree+xneditHomeLen, profile->resourceFile, resFileLen); 311 fpathFree[xneditHomeLen+resFileLen] = 0; 312 fpath = fpathFree; 313 } 314 315 XrmDatabase defaultDB = GetDefaultResourceDB(); 316 XrmDatabase newDB = XrmGetStringDatabase(""); 317 XrmQuark empty = NULLQUARK; 318 XrmEnumerateDatabase(defaultDB, &empty, &empty, XrmEnumAllLevels, CopyDBEntry, (XPointer) newDB); 319 320 XrmDatabase resFileDB = XrmGetFileDatabase(fpath); 321 if(resFileDB) { 322 XrmMergeDatabases(resFileDB, &newDB); 323 } // TODO: else error 324 325 NEditFree(fpathFree); 326 327 profile->db = newDB; 328 329 } else { 330 profile->db = GetDefaultResourceDB(); 331 } 332 profile->resDBLoaded = True; 333 } 334 335 static Pixmap isrcFind = 0; 336 static Pixmap isrcClear = 0; 337 static Pixmap closeTabPixmap = 0; 338 339 /* 340 ** Create a new editor window 341 */ 342 WindowInfo *CreateWindow(const char *name, char *geometry, int iconic) 343 { 344 Widget winShell, mainWin, menuBar, pane, text, stats, statsAreaForm; 345 Widget closeTabBtn, tabForm, form; 346 WindowInfo *window; 347 Pixel bgpix, fgpix; 348 Arg al[20]; 349 int ac; 350 XmString s1; 351 XmFontList statsFontList; 352 WindowInfo *win; 353 char newGeometry[MAX_GEOM_STRING_LEN]; 354 unsigned int rows, cols; 355 int x = 0, y = 0, bitmask, showTabBar, state; 356 357 // Before creating any UI widgets, load custom color profile X resources 358 ColorProfile *colorProfile = GetDefaultColorProfile(); 359 if(!colorProfile->db) { 360 LoadColorProfileResources(TheDisplay, colorProfile); 361 } 362 XrmSetDatabase(TheDisplay, colorProfile->db); 363 364 /* Allocate some memory for the new window data structure */ 365 window = (WindowInfo *)NEditMalloc(sizeof(WindowInfo)); 366 window->opened = False; 367 window->colorProfile = colorProfile; 368 window->wrapModeNoneForced = False; 369 370 /* initialize window structure */ 371 /* + Schwarzenberg: should a 372 memset(window, 0, sizeof(WindowInfo)); 373 be added here ? 374 */ 375 window->replaceDlog = NULL; 376 window->replaceText = NULL; 377 window->replaceWithText = NULL; 378 window->replaceWordToggle = NULL; 379 window->replaceCaseToggle = NULL; 380 window->replaceRegexToggle = NULL; 381 window->findDlog = NULL; 382 window->findText = NULL; 383 window->findWordToggle = NULL; 384 window->findCaseToggle = NULL; 385 window->findRegexToggle = NULL; 386 window->replaceMultiFileDlog = NULL; 387 window->replaceMultiFilePathBtn = NULL; 388 window->replaceMultiFileList = NULL; 389 window->multiFileReplSelected = FALSE; 390 window->multiFileBusy = FALSE; 391 window->writableWindows = NULL; 392 window->nWritableWindows = 0; 393 window->fileChanged = FALSE; 394 window->fileMode = 0; 395 window->fileUid = 0; 396 window->fileGid = 0; 397 window->filenameSet = FALSE; 398 window->fileFormat = UNIX_FILE_FORMAT; 399 window->lastModTime = 0; 400 window->fileMissing = True; 401 strcpy(window->filename, name); 402 403 window->encErrors = NULL; 404 window->numEncErrors = 0; 405 window->posEncErrors = 0; 406 407 window->encoding[0] = '\0'; 408 window->filter = NULL; 409 const char *default_encoding = GetPrefDefaultCharset(); 410 if(default_encoding) { 411 size_t defenc_len = strlen(default_encoding); 412 if(strlen(default_encoding) < MAX_ENCODING_LENGTH) { 413 memcpy(window->encoding, default_encoding, defenc_len+1); 414 } 415 } 416 417 window->bom = FALSE; 418 window->undo = NULL; 419 window->redo = NULL; 420 window->undo_batch_begin = NULL; 421 window->undo_batch_count = 0; 422 window->undo_op_batch_size = 0; 423 window->nPanes = 0; 424 window->autoSaveCharCount = 0; 425 window->autoSaveOpCount = 0; 426 window->undoOpCount = 0; 427 window->undoMemUsed = 0; 428 CLEAR_ALL_LOCKS(window->lockReasons); 429 window->indentStyle = GetPrefAutoIndent(PLAIN_LANGUAGE_MODE); 430 window->autoSave = GetPrefAutoSave(); 431 window->saveOldVersion = GetPrefSaveOldVersion(); 432 window->wrapMode = GetPrefWrap(PLAIN_LANGUAGE_MODE); 433 window->overstrike = False; 434 window->showMatchingStyle = GetPrefShowMatching(); 435 window->matchSyntaxBased = GetPrefMatchSyntaxBased(); 436 window->showStats = GetPrefStatsLine(); 437 window->showISearchLine = GetPrefISearchLine(); 438 window->showLineNumbers = GetPrefLineNums(); 439 window->showInfoBar = False; 440 window->highlightSyntax = GetPrefHighlightSyntax(); 441 window->highlightCursorLine = GetPrefHighlightCursorLine(); 442 window->indentRainbow = GetPrefIndentRainbow(); 443 window->indentRainbowColors = NEditStrdup(GetPrefIndentRainbowColors()); 444 window->ansiColors = GetPrefAnsiColors(); 445 window->backlightCharTypes = NULL; 446 window->backlightChars = GetPrefBacklightChars(); 447 if (window->backlightChars) { 448 char *cTypes = GetPrefBacklightCharTypes(); 449 if (cTypes && window->backlightChars) { 450 if ((window->backlightCharTypes = (char*)NEditMalloc(strlen(cTypes) + 1))) 451 strcpy(window->backlightCharTypes, cTypes); 452 } 453 } 454 window->modeMessageDisplayed = FALSE; 455 window->modeMessage = NULL; 456 window->ignoreModify = FALSE; 457 window->windowMenuValid = FALSE; 458 window->flashTimeoutID = 0; 459 window->fileClosedAtom = None; 460 window->wasSelected = FALSE; 461 462 strcpy(window->fontName, GetPrefFontName()); 463 strcpy(window->italicFontName, GetPrefItalicFontName()); 464 strcpy(window->boldFontName, GetPrefBoldFontName()); 465 strcpy(window->boldItalicFontName, GetPrefBoldItalicFontName()); 466 window->colorDialog = NULL; 467 window->font = FontRef(GetPrefFont()); 468 window->italicFont = FontRef(GetPrefItalicFont()); 469 window->boldFont = FontRef(GetPrefBoldFont()); 470 window->boldItalicFont = FontRef(GetPrefBoldItalicFont()); 471 window->resizeOnFontChange = True; 472 window->zoom = 0; 473 474 window->fontDialog = NULL; 475 window->nMarks = 0; 476 window->markTimeoutID = 0; 477 window->highlightData = NULL; 478 window->shellCmdData = NULL; 479 window->macroCmdData = NULL; 480 window->smartIndentData = NULL; 481 window->languageMode = PLAIN_LANGUAGE_MODE; 482 window->iSearchHistIndex = 0; 483 window->iSearchStartPos = -1; 484 window->replaceLastRegexCase = TRUE; 485 window->replaceLastLiteralCase = FALSE; 486 window->iSearchLastRegexCase = TRUE; 487 window->iSearchLastLiteralCase = FALSE; 488 window->findLastRegexCase = TRUE; 489 window->findLastLiteralCase = FALSE; 490 window->tab = NULL; 491 window->bgMenuUndoItem = NULL; 492 window->bgMenuRedoItem = NULL; 493 window->device = 0; 494 window->inode = 0; 495 496 /* If window geometry was specified, split it apart into a window position 497 component and a window size component. Create a new geometry string 498 containing the position component only. Rows and cols are stripped off 499 because we can't easily calculate the size in pixels from them until the 500 whole window is put together. Note that the preference resource is only 501 for clueless users who decide to specify the standard X geometry 502 application resource, which is pretty useless because width and height 503 are the same as the rows and cols preferences, and specifying a window 504 location will force all the windows to pile on top of one another */ 505 if (geometry == NULL || geometry[0] == '\0') 506 geometry = GetPrefGeometry(); 507 if (geometry == NULL || geometry[0] == '\0') { 508 rows = GetPrefRows(); 509 cols = GetPrefCols(); 510 newGeometry[0] = '\0'; 511 } else { 512 bitmask = XParseGeometry(geometry, &x, &y, &cols, &rows); 513 if (bitmask == 0) 514 fprintf(stderr, "Bad window geometry specified: %s\n", geometry); 515 else { 516 if (!(bitmask & WidthValue)) 517 cols = GetPrefCols(); 518 if (!(bitmask & HeightValue)) 519 rows = GetPrefRows(); 520 } 521 CreateGeometryString(newGeometry, x, y, 0, 0, 522 bitmask & ~(WidthValue | HeightValue)); 523 } 524 525 /* Create a new toplevel shell to hold the window */ 526 ac = 0; 527 XtSetArg(al[ac], XmNtitle, name); ac++; 528 XtSetArg(al[ac], XmNdeleteResponse, XmDO_NOTHING); ac++; 529 #ifdef SGI_CUSTOM 530 if (strncmp(name, "Untitled", 8) == 0) { 531 XtSetArg(al[ac], XmNiconName, APP_NAME); ac++; 532 } else { 533 XtSetArg(al[ac], XmNiconName, name); ac++; 534 } 535 #else 536 XtSetArg(al[ac], XmNiconName, name); ac++; 537 #endif 538 XtSetArg(al[ac], XmNgeometry, newGeometry[0]=='\0'?NULL:newGeometry); ac++; 539 XtSetArg(al[ac], XmNinitialState, 540 iconic ? IconicState : NormalState); ac++; 541 542 if (newGeometry[0] == '\0') 543 { 544 /* Workaround to make Xt ignore Motif's bad PPosition size changes. Even 545 though we try to remove the PPosition in RealizeWithoutForcingPosition, 546 it is not sufficient. Motif will recompute the size hints some point 547 later and put PPosition back! If the window is mapped after that time, 548 then the window will again wind up at 0, 0. So, XEmacs does this, and 549 now we do. 550 551 Alternate approach, relying on ShellP.h: 552 553 ((WMShellWidget)winShell)->shell.client_specified &= ~_XtShellPPositionOK; 554 */ 555 556 XtSetArg(al[ac], XtNx, XT_IGNORE_PPOSITION); ac++; 557 XtSetArg(al[ac], XtNy, XT_IGNORE_PPOSITION); ac++; 558 } 559 560 winShell = CreateWidget(TheAppShell, "textShell", 561 topLevelShellWidgetClass, al, ac); 562 window->shell = winShell; 563 564 #ifdef EDITRES 565 XtAddEventHandler (winShell, (EventMask)0, True, 566 (XtEventHandler)_XEditResCheckMessages, NULL); 567 #endif /* EDITRES */ 568 569 #ifndef SGI_CUSTOM 570 addWindowIcon(winShell); 571 #endif 572 573 XtAddEventHandler( 574 winShell, 575 StructureNotifyMask, 576 False, 577 (XtEventHandler)windowStructureNotifyEventEH, 578 window); 579 580 /* Create a MainWindow to manage the menubar and text area, set the 581 userData resource to be used by WidgetToWindow to recover the 582 window pointer from the widget id of any of the window's widgets */ 583 XtSetArg(al[ac], XmNuserData, window); ac++; 584 mainWin = XmCreateMainWindow(winShell, "main", al, ac); 585 window->mainWin = mainWin; 586 XtManageChild(mainWin); 587 588 // The statsAreaForm holds the stats line, the I-Search line 589 // and the encodingInfoBar 590 statsAreaForm = XtVaCreateWidget("statsAreaForm", 591 xmFormWidgetClass, mainWin, 592 XmNmarginWidth, STAT_SHADOW_THICKNESS, 593 XmNmarginHeight, STAT_SHADOW_THICKNESS, 594 /* XmNautoUnmanage, False, */ 595 NULL); 596 597 /* NOTE: due to a bug in openmotif 2.1.30, NEdit used to crash when 598 the i-search bar was active, and the i-search text widget was focussed, 599 and the window's width was resized to nearly zero. 600 In theory, it is possible to avoid this by imposing a minimum 601 width constraint on the nedit windows, but that width would have to 602 be at least 30 characters, which is probably unacceptable. 603 Amazingly, adding a top offset of 1 pixel to the toggle buttons of 604 the i-search bar, while keeping the the top offset of the text widget 605 to 0 seems to avoid avoid the crash. */ 606 607 window->iSearchForm = XtVaCreateWidget("iSearchForm", 608 xmFormWidgetClass, statsAreaForm, 609 XmNshadowThickness, 0, 610 XmNleftAttachment, XmATTACH_FORM, 611 XmNleftOffset, STAT_SHADOW_THICKNESS, 612 XmNtopAttachment, XmATTACH_FORM, 613 XmNtopOffset, STAT_SHADOW_THICKNESS, 614 XmNrightAttachment, XmATTACH_FORM, 615 XmNrightOffset, STAT_SHADOW_THICKNESS, 616 XmNbottomOffset, STAT_SHADOW_THICKNESS, NULL); 617 if(window->showISearchLine) 618 XtManageChild(window->iSearchForm); 619 620 createSearchForm(window); 621 622 623 /* create the a form to house the tab bar and close-tab button */ 624 tabForm = XtVaCreateWidget("tabForm", 625 xmFormWidgetClass, statsAreaForm, 626 XmNmarginHeight, 0, 627 XmNmarginWidth, 0, 628 XmNspacing, 0, 629 XmNresizable, False, 630 XmNleftAttachment, XmATTACH_FORM, 631 XmNrightAttachment, XmATTACH_FORM, 632 XmNshadowThickness, 0, NULL); 633 634 /* button to close top document */ 635 if (closeTabPixmap == 0) { 636 switch(GetPrefCloseIconSize()) { 637 default: { 638 closeTabPixmap = createBitmapWithDepth(tabForm, 639 (char *)close_bits, close_width, close_height); 640 break; 641 } 642 case 1: { 643 closeTabPixmap = createBitmapWithDepth(tabForm, 644 (char *)close_m_bits, close_m_width, close_m_height); 645 break; 646 } 647 case 2: { 648 closeTabPixmap = createBitmapWithDepth(tabForm, 649 (char *)close_l_bits, close_l_width, close_l_height); 650 break; 651 } 652 } 653 } 654 closeTabBtn = XtVaCreateManagedWidget("closeTabBtn", 655 xmPushButtonWidgetClass, tabForm, 656 XmNmarginHeight, 0, 657 XmNmarginWidth, 0, 658 XmNhighlightThickness, 0, 659 XmNlabelType, XmPIXMAP, 660 XmNlabelPixmap, closeTabPixmap, 661 XmNshadowThickness, 1, 662 XmNtraversalOn, False, 663 XmNrightAttachment, XmATTACH_FORM, 664 XmNrightOffset, 3, 665 XmNbottomAttachment, XmATTACH_FORM, 666 XmNbottomOffset, 3, 667 NULL); 668 XtAddCallback(closeTabBtn, XmNactivateCallback, (XtCallbackProc)closeTabCB, 669 mainWin); 670 671 /* create the tab bar */ 672 window->tabBar = XtVaCreateManagedWidget("tabBar", 673 xmlFolderWidgetClass, tabForm, 674 XmNresizePolicy, XmRESIZE_PACK, 675 XmNleftAttachment, XmATTACH_FORM, 676 XmNleftOffset, 0, 677 XmNrightAttachment, XmATTACH_WIDGET, 678 XmNrightWidget, closeTabBtn, 679 XmNrightOffset, 5, 680 XmNbottomAttachment, XmATTACH_FORM, 681 XmNbottomOffset, 0, 682 XmNtopAttachment, XmATTACH_FORM, 683 NULL); 684 685 window->tabMenuPane = CreateTabContextMenu(window->tabBar, window); 686 AddTabContextMenuAction(window->tabBar); 687 688 /* create an unmanaged composite widget to get the folder 689 widget to hide the 3D shadow for the manager area. 690 Note: this works only on the patched XmLFolder widget */ 691 form = XtVaCreateWidget("form", 692 xmFormWidgetClass, window->tabBar, 693 XmNheight, 1, 694 XmNresizable, False, 695 NULL); 696 697 XtAddCallback(window->tabBar, XmNactivateCallback, 698 raiseTabCB, NULL); 699 700 window->tab = addTab(window->tabBar, name); 701 702 /* A form to hold the stats line text and line/col widgets */ 703 window->statsLineForm = XtVaCreateWidget("statsLineForm", 704 xmFormWidgetClass, statsAreaForm, 705 XmNshadowThickness, 0, 706 XmNtopAttachment, window->showISearchLine ? 707 XmATTACH_WIDGET : XmATTACH_FORM, 708 XmNtopWidget, window->iSearchForm, 709 XmNrightAttachment, XmATTACH_FORM, 710 XmNleftAttachment, XmATTACH_FORM, 711 //XmNbottomAttachment, XmATTACH_FORM, 712 XmNresizable, False, /* */ 713 NULL); 714 715 /* A separate display of the line/column number */ 716 window->statsLineColNo = XtVaCreateManagedWidget("statsLineColNo", 717 xmLabelWidgetClass, window->statsLineForm, 718 XmNlabelString, s1=XmStringCreateSimple("S: --- L: --- C: ---"), 719 XmNshadowThickness, 0, 720 XmNmarginHeight, 2, 721 XmNtraversalOn, False, 722 XmNtopAttachment, XmATTACH_FORM, 723 XmNrightAttachment, XmATTACH_FORM, 724 XmNbottomAttachment, XmATTACH_FORM, /* */ 725 NULL); 726 XmStringFree(s1); 727 728 /* Create file statistics display area. Using a text widget rather than 729 a label solves a layout problem with the main window, which messes up 730 if the label is too long (we would need a resize callback to control 731 the length when the window changed size), and allows users to select 732 file names and line numbers. Colors are copied from parent 733 widget, because many users and some system defaults color text 734 backgrounds differently from other widgets. */ 735 XtVaGetValues(window->statsLineForm, XmNbackground, &bgpix, NULL); 736 XtVaGetValues(window->statsLineForm, XmNforeground, &fgpix, NULL); 737 stats = XtVaCreateManagedWidget("statsLine", 738 xmTextWidgetClass, window->statsLineForm, 739 XmNbackground, bgpix, 740 XmNforeground, fgpix, 741 XmNshadowThickness, 0, 742 XmNhighlightColor, bgpix, 743 XmNhighlightThickness, 0, /* must be zero, for OM (2.1.30) to 744 aligns tatsLineColNo & statsLine */ 745 XmNmarginHeight, 1, /* == statsLineColNo.marginHeight - 1, 746 to align with statsLineColNo */ 747 XmNscrollHorizontal, False, 748 XmNeditMode, XmSINGLE_LINE_EDIT, 749 XmNeditable, False, 750 XmNtraversalOn, False, 751 XmNcursorPositionVisible, False, 752 XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, /* */ 753 XmNtopWidget, window->statsLineColNo, 754 XmNleftAttachment, XmATTACH_FORM, 755 XmNrightAttachment, XmATTACH_WIDGET, 756 XmNrightWidget, window->statsLineColNo, 757 XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, /* */ 758 XmNbottomWidget, window->statsLineColNo, 759 XmNrightOffset, 3, 760 NULL); 761 window->statsLine = stats; 762 763 /* Give the statsLine the same font as the statsLineColNo */ 764 XtVaGetValues(window->statsLineColNo, XmNfontList, &statsFontList, NULL); 765 XtVaSetValues(window->statsLine, XmNfontList, statsFontList, NULL); 766 767 /* Manage the statsLineForm */ 768 if(window->showStats) 769 XtManageChild(window->statsLineForm); 770 771 // Create encodingInfoBar form 772 window->encodingInfoBar = XtVaCreateWidget( 773 "infobar", 774 xmFormWidgetClass, 775 statsAreaForm, 776 XmNtopAttachment, XmATTACH_WIDGET, 777 XmNtopWidget, window->statsLineForm, 778 XmNbottomAttachment, XmATTACH_FORM, 779 XmNrightAttachment, XmATTACH_FORM, 780 XmNleftAttachment, XmATTACH_FORM, 781 NULL); 782 783 // create encoding dropdown list 784 ac = 0; 785 XtSetArg(al[ac], XmNcolumns, 15); ac++; 786 787 XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++; 788 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; 789 XtSetArg(al[ac], XmNhighlightThickness, 1); ac++; 790 window->encInfoBarList = XmCreateDropDownList( 791 window->encodingInfoBar, 792 "combobox", 793 al, 794 ac); 795 XtManageChild(window->encInfoBarList); 796 797 // infobar label 798 window->encInfoBarLabel = XtVaCreateManagedWidget( 799 "ibarlabel", 800 xmLabelWidgetClass, 801 window->encodingInfoBar, 802 XmNleftAttachment, XmATTACH_FORM, 803 XmNtopAttachment, XmATTACH_FORM, 804 XmNbottomAttachment, XmATTACH_WIDGET, 805 XmNbottomWidget, window->encInfoBarList, 806 NULL); 807 808 // error dropdown 809 ac = 0; 810 XtSetArg(al[ac], XmNcolumns, 15); ac++; 811 XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++; 812 XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; 813 XtSetArg(al[ac], XmNhighlightThickness, 1); ac++; 814 XtSetArg(al[ac], XmNvalue, "Errors"); ac++; 815 window->encInfoErrorList = XmCreateDropDownList( 816 window->encodingInfoBar, 817 "combobox", 818 al, 819 ac); 820 // don't manage encInfoErrorList here 821 822 823 XtAddCallback(window->encInfoErrorList, XmNselectionCallback, 824 (XtCallbackProc)jumpToEncErrorCB, mainWin); 825 826 Widget btnClose = XtVaCreateManagedWidget( 827 "ibarbutton", 828 xmPushButtonWidgetClass, 829 window->encodingInfoBar, 830 XmNlabelType, XmPIXMAP, 831 XmNlabelPixmap, closeTabPixmap, 832 XmNtopAttachment, XmATTACH_WIDGET, 833 XmNrightAttachment, XmATTACH_FORM, 834 XmNbottomAttachment, XmATTACH_WIDGET, 835 XmNbottomWidget, window->encInfoBarList, 836 XmNhighlightThickness, 1, 837 NULL); 838 XtAddCallback(btnClose, XmNactivateCallback, (XtCallbackProc)closeInfoBarCB, 839 mainWin); 840 841 s1 = XmStringCreateSimple("Reload"); 842 Widget btnReload = XtVaCreateManagedWidget( 843 "ibarbutton", 844 xmPushButtonWidgetClass, 845 window->encodingInfoBar, 846 XmNlabelString, s1, 847 XmNtopAttachment, XmATTACH_WIDGET, 848 XmNrightAttachment, XmATTACH_WIDGET, 849 XmNrightWidget, btnClose, 850 XmNbottomAttachment, XmATTACH_WIDGET, 851 XmNbottomWidget, window->encInfoBarList, 852 XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, 853 XmNleftWidget, window->encInfoBarList, 854 XmNhighlightThickness, 1, 855 NULL); 856 XtAddCallback(btnReload, XmNactivateCallback, (XtCallbackProc)reloadCB, 857 mainWin); 858 XmStringFree(s1); 859 860 /* Create the menu bar */ 861 menuBar = CreateMenuBar(mainWin, window); 862 window->menuBar = menuBar; 863 XtManageChild(menuBar); 864 865 /* Create paned window to manage split pane behavior */ 866 pane = XtVaCreateManagedWidget("pane", xmPanedWindowWidgetClass, mainWin, 867 XmNseparatorOn, False, 868 XmNspacing, 3, XmNsashIndent, -2, NULL); 869 window->splitPane = pane; 870 XmMainWindowSetAreas(mainWin, menuBar, statsAreaForm, NULL, NULL, pane); 871 872 /* Store a copy of document/window pointer in text pane to support 873 action procedures. See also WidgetToWindow() for info. */ 874 XtVaSetValues(pane, XmNuserData, window, NULL); 875 876 /* Patch around Motif's most idiotic "feature", that its menu accelerators 877 recognize Caps Lock and Num Lock as modifiers, and don't trigger if 878 they are engaged */ 879 AccelLockBugPatch(pane, window->menuBar); 880 881 /* Create the first, and most permanent text area (other panes may 882 be added & removed, but this one will never be removed */ 883 text = createTextArea(pane, window, rows,cols, 884 GetPrefEmTabDist(PLAIN_LANGUAGE_MODE), GetPrefDelimiters(), 885 GetPrefWrapMargin(), window->showLineNumbers?MIN_LINE_NUM_COLS:0); 886 XtManageChild(text); 887 window->textArea = text; 888 window->lastFocus = text; 889 890 /* Set the initial colors from the globals. */ 891 SetColorProfile(window, GetDefaultColorProfile()); 892 893 /* Create the right button popup menu (note: order is important here, 894 since the translation for popping up this menu was probably already 895 added in createTextArea, but CreateBGMenu requires window->textArea 896 to be set so it can attach the menu to it (because menu shells are 897 finicky about the kinds of widgets they are attached to)) */ 898 window->bgMenuPane = CreateBGMenu(window); 899 900 /* cache user menus: init. user background menu cache */ 901 InitUserBGMenuCache(&window->userBGMenuCache); 902 903 /* Create the text buffer rather than using the one created automatically 904 with the text area widget. This is done so the syntax highlighting 905 modify callback can be called to synchronize the style buffer BEFORE 906 the text display's callback is called upon to display a modification */ 907 window->buffer = BufCreate(); 908 BufAddModifyCB(window->buffer, SyntaxHighlightModifyCB, window); 909 910 /* Attach the buffer to the text widget, and add callbacks for modify */ 911 TextSetBuffer(text, window->buffer); 912 BufAddModifyCB(window->buffer, modifiedCB, window); 913 BufAddBeginModifyCB(window->buffer, beginModifyCB, window); 914 BufAddEndModifyCB(window->buffer, endModifyCB, window); 915 916 /* Designate the permanent text area as the owner for selections */ 917 HandleXSelections(text); 918 919 /* Set the requested hardware tab distance and useTabs in the text buffer */ 920 BufSetTabDistance(window->buffer, GetPrefTabDist(PLAIN_LANGUAGE_MODE)); 921 window->buffer->useTabs = GetPrefInsertTabs(); 922 923 /* add the window to the global window list, update the Windows menus */ 924 addToWindowList(window); 925 InvalidateWindowMenus(); 926 927 showTabBar = GetShowTabBar(window); 928 if (showTabBar) 929 XtManageChild(tabForm); 930 931 manageToolBars(statsAreaForm); 932 933 if (showTabBar || window->showISearchLine || 934 window->showStats || window->showInfoBar) 935 { 936 XtManageChild(statsAreaForm); 937 } 938 939 /* realize all of the widgets in the new window */ 940 RealizeWithoutForcingPosition(winShell); 941 XmProcessTraversal(text, XmTRAVERSE_CURRENT); 942 943 /* Make close command in window menu gracefully prompt for close */ 944 AddMotifCloseCallback(winShell, (XtCallbackProc)closeCB, window); 945 946 /* window open callback */ 947 if(!take_focus_atom_is_init) { 948 wm_take_focus = XmInternAtom( 949 XtDisplay(winShell), 950 "WM_TAKE_FOCUS", 951 0); 952 take_focus_atom_is_init = 1; 953 } 954 XmAddWMProtocolCallback( 955 winShell, 956 wm_take_focus, 957 (XtCallbackProc)WindowTakeFocus, 958 window); 959 960 /* Make window resizing work in nice character heights */ 961 UpdateWMSizeHints(window); 962 963 /* Set the minimum pane height for the initial text pane */ 964 UpdateMinPaneHeights(window); 965 966 /* create dialogs shared by all documents in a window */ 967 CreateFindDlog(window->shell, window); 968 CreateReplaceDlog(window->shell, window); 969 CreateReplaceMultiFileDlog(window); 970 971 /* dim/undim Attach_Tab menu items */ 972 state = NDocuments(window) < NWindows(); 973 for(win=WindowList; win; win=win->next) { 974 if (IsTopDocument(win)) { 975 XtSetSensitive(win->moveDocumentItem, state); 976 XtSetSensitive(win->contextMoveDocumentItem, state); 977 } 978 } 979 980 return window; 981 } 982 983 static void createSearchForm(WindowInfo *window) 984 { 985 XmString s1; 986 987 /* Disable keyboard traversal of the find, clear and toggle buttons. We 988 were doing this previously by forcing the keyboard focus back to the 989 text widget whenever a toggle changed. That causes an ugly focus flash 990 on screen. It's better just not to go there in the first place. 991 Plus, if the user really wants traversal, it's an X resource so it 992 can be enabled without too much pain and suffering. */ 993 994 if (isrcFind == 0) { 995 switch(GetPrefISrcFindIconSize()) { 996 default: { 997 isrcFind = createBitmapWithDepth(window->iSearchForm, 998 (char *)isrcFind_bits, isrcFind_width, isrcFind_height); 999 break; 1000 } 1001 case 1: { 1002 isrcFind = createBitmapWithDepth(window->iSearchForm, 1003 (char *)isrcFind_m_bits, isrcFind_m_width, isrcFind_m_height); 1004 break; 1005 } 1006 case 2: { 1007 isrcFind = createBitmapWithDepth(window->iSearchForm, 1008 (char *)isrcFind_l_bits, isrcFind_l_width, isrcFind_l_height); 1009 break; 1010 } 1011 } 1012 1013 } 1014 window->iSearchFindButton = XtVaCreateManagedWidget("iSearchFindButton", 1015 xmPushButtonWidgetClass, window->iSearchForm, 1016 XmNlabelString, s1=XmStringCreateSimple("Find"), 1017 XmNlabelType, XmPIXMAP, 1018 XmNlabelPixmap, isrcFind, 1019 XmNtraversalOn, False, 1020 XmNmarginHeight, 1, 1021 XmNmarginWidth, 1, 1022 XmNleftAttachment, XmATTACH_FORM, 1023 /* XmNleftOffset, 3, */ 1024 XmNleftOffset, 0, 1025 XmNtopAttachment, XmATTACH_FORM, 1026 XmNtopOffset, 1, 1027 XmNbottomAttachment, XmATTACH_FORM, 1028 XmNbottomOffset, 1, 1029 NULL); 1030 XmStringFree(s1); 1031 1032 window->iSearchCaseToggle = XtVaCreateManagedWidget("iSearchCaseToggle", 1033 xmToggleButtonWidgetClass, window->iSearchForm, 1034 XmNlabelString, s1=XmStringCreateSimple("Case"), 1035 XmNset, GetPrefSearch() == SEARCH_CASE_SENSE 1036 || GetPrefSearch() == SEARCH_REGEX 1037 || GetPrefSearch() == SEARCH_CASE_SENSE_WORD, 1038 XmNtopAttachment, XmATTACH_FORM, 1039 XmNbottomAttachment, XmATTACH_FORM, 1040 XmNtopOffset, 1, /* see openmotif note above */ 1041 XmNrightAttachment, XmATTACH_FORM, 1042 XmNmarginHeight, 0, 1043 XmNtraversalOn, False, 1044 NULL); 1045 XmStringFree(s1); 1046 1047 window->iSearchRegexToggle = XtVaCreateManagedWidget("iSearchREToggle", 1048 xmToggleButtonWidgetClass, window->iSearchForm, 1049 XmNlabelString, s1=XmStringCreateSimple("RegExp"), 1050 XmNset, GetPrefSearch() == SEARCH_REGEX_NOCASE 1051 || GetPrefSearch() == SEARCH_REGEX, 1052 XmNtopAttachment, XmATTACH_FORM, 1053 XmNbottomAttachment, XmATTACH_FORM, 1054 XmNtopOffset, 1, /* see openmotif note above */ 1055 XmNrightAttachment, XmATTACH_WIDGET, 1056 XmNrightWidget, window->iSearchCaseToggle, 1057 XmNmarginHeight, 0, 1058 XmNtraversalOn, False, 1059 NULL); 1060 XmStringFree(s1); 1061 1062 window->iSearchRevToggle = XtVaCreateManagedWidget("iSearchRevToggle", 1063 xmToggleButtonWidgetClass, window->iSearchForm, 1064 XmNlabelString, s1=XmStringCreateSimple("Rev"), 1065 XmNset, False, 1066 XmNtopAttachment, XmATTACH_FORM, 1067 XmNbottomAttachment, XmATTACH_FORM, 1068 XmNtopOffset, 1, /* see openmotif note above */ 1069 XmNrightAttachment, XmATTACH_WIDGET, 1070 XmNrightWidget, window->iSearchRegexToggle, 1071 XmNmarginHeight, 0, 1072 XmNtraversalOn, False, 1073 NULL); 1074 XmStringFree(s1); 1075 1076 if (isrcClear == 0) { 1077 switch(GetPrefISrcClearIconSize()) { 1078 default: { 1079 isrcClear = createBitmapWithDepth(window->iSearchForm, 1080 (char *)isrcClear_bits, isrcClear_width, isrcClear_height); 1081 break; 1082 } 1083 case 1: { 1084 isrcClear = createBitmapWithDepth(window->iSearchForm, 1085 (char *)isrcClear_m_bits, isrcClear_m_width, isrcClear_m_height); 1086 break; 1087 } 1088 case 2: { 1089 isrcClear = createBitmapWithDepth(window->iSearchForm, 1090 (char *)isrcClear_l_bits, isrcClear_l_width, isrcClear_l_height); 1091 break; 1092 } 1093 } 1094 } 1095 window->iSearchClearButton = XtVaCreateManagedWidget("iSearchClearButton", 1096 xmPushButtonWidgetClass, window->iSearchForm, 1097 XmNlabelString, s1=XmStringCreateSimple("<x"), 1098 XmNlabelType, XmPIXMAP, 1099 XmNlabelPixmap, isrcClear, 1100 XmNtraversalOn, False, 1101 XmNmarginHeight, 1, 1102 XmNmarginWidth, 1, 1103 XmNrightAttachment, XmATTACH_WIDGET, 1104 XmNrightWidget, window->iSearchRevToggle, 1105 XmNrightOffset, 2, 1106 XmNtopAttachment, XmATTACH_FORM, 1107 XmNtopOffset, 1, 1108 XmNbottomAttachment, XmATTACH_FORM, 1109 XmNbottomOffset, 1, 1110 NULL); 1111 XmStringFree(s1); 1112 1113 window->iSearchText = XtVaCreateManagedWidget("iSearchText", 1114 XNEtextfieldWidgetClass, window->iSearchForm, 1115 XmNmarginHeight, 1, 1116 XmNnavigationType, XmEXCLUSIVE_TAB_GROUP, 1117 XmNleftAttachment, XmATTACH_WIDGET, 1118 XmNleftWidget, window->iSearchFindButton, 1119 XmNrightAttachment, XmATTACH_WIDGET, 1120 XmNrightWidget, window->iSearchClearButton, 1121 /* XmNrightOffset, 5, */ 1122 XmNtopAttachment, XmATTACH_FORM, 1123 XmNtopOffset, 0, /* see openmotif note above */ 1124 XmNbottomAttachment, XmATTACH_FORM, 1125 XmNbottomOffset, 0, NULL); 1126 RemapDeleteKey(window->iSearchText); 1127 1128 SetISearchTextCallbacks(window); 1129 1130 EnableDefaultColorProfileResourceDB(XtDisplay(window->mainWin)); 1131 } 1132 1133 static Widget evTab; 1134 1135 static int motion_x; 1136 static int motion_y; 1137 static int pressed_x; 1138 static int pressed_y; 1139 static int drag_enabled = 0; 1140 1141 /* 1142 ** ButtonPress event handler for tabs. 1143 */ 1144 static void tabClickEH(Widget w, XtPointer clientData, XEvent *event, Boolean *dispatch) 1145 { 1146 if(event->type == MotionNotify) { 1147 motion_x = event->xmotion.x; 1148 motion_y = event->xmotion.y; 1149 1150 if(pressed_x != 0 && abs(pressed_x - motion_x) > 10) { 1151 drag_enabled = 1; 1152 } 1153 1154 if(drag_enabled) { 1155 int tx, ty; 1156 Window tw; 1157 XTranslateCoordinates(XtDisplay(w), XtWindow(w), XtWindow(XtParent(w)), motion_x, pressed_y, &tx, &ty, &tw); 1158 1159 Widget switchTab = XtWindowToWidget(XtDisplay(w), tw); 1160 1161 if((evTab != switchTab) && switchTab) { 1162 SwitchTabs(evTab, switchTab); 1163 evTab = switchTab; 1164 } 1165 } 1166 } else if(event->type == ButtonRelease) { 1167 drag_enabled = 0; 1168 pressed_x = 0; 1169 pressed_y = 0; 1170 if(event->xbutton.button == 2) { 1171 WindowInfo *window = TabToWindow(w); 1172 CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE); 1173 *dispatch = False; 1174 return; 1175 } 1176 } else if(event->type == ButtonPress) { 1177 evTab = w; 1178 pressed_x = motion_x; 1179 pressed_y = motion_y; 1180 drag_enabled = 0; 1181 1182 if(event->xbutton.button == 2) { 1183 *dispatch = False; 1184 } 1185 1186 // hide the tooltip when user clicks with any button. 1187 if (BubbleButton_Timer(w)) { 1188 XtRemoveTimeOut(BubbleButton_Timer(w)); 1189 BubbleButton_Timer(w) = (XtIntervalId)NULL; 1190 } 1191 else { 1192 hideTooltip(w); 1193 } 1194 } 1195 } 1196 1197 /* 1198 ** add a tab to the tab bar for the new document. 1199 */ 1200 static Widget addTab(Widget folder, const char *string) 1201 { 1202 Widget tooltipLabel, tab; 1203 XmString s1; 1204 1205 s1 = XmStringCreateSimple((char *)string); 1206 tab = XtVaCreateManagedWidget("tab", 1207 xrwsBubbleButtonWidgetClass, folder, 1208 /* XmNmarginWidth, <default@nedit.c>, */ 1209 /* XmNmarginHeight, <default@nedit.c>, */ 1210 /* XmNalignment, <default@nedit.c>, */ 1211 XmNlabelString, s1, 1212 XltNbubbleString, s1, 1213 XltNshowBubble, GetPrefToolTips(), 1214 XltNautoParkBubble, True, 1215 XltNslidingBubble, False, 1216 /* XltNdelay, 800,*/ 1217 /* XltNbubbleDuration, 8000,*/ 1218 NULL); 1219 XmStringFree(s1); 1220 1221 /* there's things to do as user click on the tab */ 1222 XtAddEventHandler(tab, PointerMotionMask|ButtonPressMask|ButtonReleaseMask|EnterWindowMask|LeaveWindowMask, False, 1223 (XtEventHandler)tabClickEH, (XtPointer)0); 1224 1225 /* BubbleButton simply use reversed video for tooltips, 1226 we try to use the 'standard' color */ 1227 tooltipLabel = XtNameToWidget(tab, "*BubbleLabel"); 1228 XtVaSetValues(tooltipLabel, 1229 XmNbackground, AllocateColor(tab, GetPrefTooltipBgColor()), 1230 XmNforeground, AllocateColor(tab, NEDIT_DEFAULT_FG), 1231 NULL); 1232 1233 /* put borders around tooltip. BubbleButton use 1234 transientShellWidgetClass as tooltip shell, which 1235 came without borders */ 1236 XtVaSetValues(XtParent(tooltipLabel), XmNborderWidth, 1, NULL); 1237 1238 #ifdef LESSTIF_VERSION 1239 /* If we don't do this, no popup when right-click on tabs */ 1240 AddTabContextMenuAction(tab); 1241 #endif /* LESSTIF_VERSION */ 1242 1243 return tab; 1244 } 1245 1246 /* 1247 ** Comparison function for sorting windows by title. 1248 ** Windows are sorted by alphabetically by filename and then 1249 ** alphabetically by path. 1250 */ 1251 static int compareWindowNames(const void *windowA, const void *windowB) 1252 { 1253 int rc; 1254 const WindowInfo *a = *((WindowInfo**)windowA); 1255 const WindowInfo *b = *((WindowInfo**)windowB); 1256 1257 rc = strcmp(a->filename, b->filename); 1258 if (rc != 0) 1259 return rc; 1260 rc = strcmp(a->path, b->path); 1261 return rc; 1262 } 1263 1264 /* 1265 ** Sort tabs in the tab bar alphabetically, if demanded so. 1266 */ 1267 void SortTabBar(WindowInfo *window) 1268 { 1269 WindowInfo *w; 1270 WindowInfo **windows; 1271 WidgetList tabList; 1272 int i, j, nDoc, tabCount; 1273 1274 if (!GetPrefSortTabs()) 1275 return; 1276 1277 /* need more than one tab to sort */ 1278 nDoc = NDocuments(window); 1279 if (nDoc < 2) 1280 return; 1281 1282 /* first sort the documents */ 1283 windows = (WindowInfo **)NEditMalloc(sizeof(WindowInfo *) * nDoc); 1284 for (w=WindowList, i=0; w!=NULL; w=w->next) { 1285 if (window->shell == w->shell) 1286 windows[i++] = w; 1287 } 1288 qsort(windows, nDoc, sizeof(WindowInfo *), compareWindowNames); 1289 1290 /* assign tabs to documents in sorted order */ 1291 XtVaGetValues(window->tabBar, XmNtabWidgetList, &tabList, 1292 XmNtabCount, &tabCount, NULL); 1293 1294 for (i=0, j=0; i<tabCount && j<nDoc; i++) { 1295 if (tabList[i]->core.being_destroyed) 1296 continue; 1297 1298 /* set tab as active */ 1299 if (IsTopDocument(windows[j])) 1300 XmLFolderSetActiveTab(window->tabBar, i, False); 1301 1302 windows[j]->tab = tabList[i]; 1303 RefreshTabState(windows[j]); 1304 j++; 1305 } 1306 1307 NEditFree(windows); 1308 } 1309 1310 /* 1311 * Switch document tabs 1312 */ 1313 void SwitchTabs(Widget from, Widget to) 1314 { 1315 WindowInfo *winFrom = NULL; 1316 WindowInfo *winTo = NULL; 1317 WidgetList tabList; 1318 int tabCount; 1319 1320 for(WindowInfo *w=WindowList;w;w=w->next) { 1321 if(w->tab == from) { 1322 winFrom = w; 1323 } 1324 if(w->tab == to) { 1325 winTo = w; 1326 } 1327 } 1328 1329 winTo->tab = from; 1330 winFrom->tab = to; 1331 RefreshTabState(winTo); 1332 RefreshTabState(winFrom); 1333 1334 XtVaGetValues(winFrom->tabBar, XmNtabWidgetList, &tabList, 1335 XmNtabCount, &tabCount, NULL); 1336 1337 for(int i=0;i<tabCount;i++) { 1338 if(tabList[i]->core.being_destroyed) { 1339 continue; 1340 } 1341 1342 // we are moving the active tab to a new position, therefore 1343 // set "to" to active 1344 if(tabList[i] == to) { 1345 XmLFolderSetActiveTab(winFrom->tabBar, i, False); 1346 } 1347 } 1348 1349 RaiseDocument(winFrom); 1350 } 1351 1352 /* 1353 ** find which document a tab belongs to 1354 */ 1355 WindowInfo *TabToWindow(Widget tab) 1356 { 1357 WindowInfo *win; 1358 for (win=WindowList; win; win=win->next) { 1359 if (win->tab == tab) 1360 return win; 1361 } 1362 1363 return NULL; 1364 } 1365 1366 /* 1367 ** Close a document, or an editor window 1368 */ 1369 void CloseWindow(WindowInfo *window) 1370 { 1371 int keepWindow, state; 1372 char name[MAXPATHLEN]; 1373 WindowInfo *win, *topBuf = NULL, *nextBuf = NULL; 1374 1375 /* Free smart indent macro programs */ 1376 EndSmartIndent(window); 1377 1378 /* Clean up macro references to the doomed window. If a macro is 1379 executing, stop it. If macro is calling this (closing its own 1380 window), leave the window alive until the macro completes */ 1381 keepWindow = !MacroWindowCloseActions(window); 1382 1383 /* Kill shell sub-process and free related memory */ 1384 AbortShellCommand(window); 1385 1386 /* Unload the default tips files for this language mode if necessary */ 1387 UnloadLanguageModeTipsFile(window); 1388 1389 /* If a window is closed while it is on the multi-file replace dialog 1390 list of any other window (or even the same one), we must update those 1391 lists or we end up with dangling references. Normally, there can 1392 be only one of those dialogs at the same time (application modal), 1393 but LessTif doesn't even (always) honor application modalness, so 1394 there can be more than one dialog. */ 1395 RemoveFromMultiReplaceDialog(window); 1396 1397 /* Destroy the file closed property for this file */ 1398 DeleteFileClosedProperty(window); 1399 1400 /* Remove any possibly pending callback which might fire after the 1401 widget is gone. */ 1402 cancelTimeOut(&window->flashTimeoutID); 1403 cancelTimeOut(&window->markTimeoutID); 1404 1405 /* if this is the last window, or must be kept alive temporarily because 1406 it's running the macro calling us, don't close it, make it Untitled */ 1407 if (keepWindow || (WindowList == window && window->next == NULL)) { 1408 window->filename[0] = '\0'; 1409 UniqueUntitledName(name); 1410 CLEAR_ALL_LOCKS(window->lockReasons); 1411 window->fileMode = 0; 1412 window->fileUid = 0; 1413 window->fileGid = 0; 1414 strcpy(window->filename, name); 1415 strcpy(window->path, ""); 1416 window->ignoreModify = TRUE; 1417 BufSetAll(window->buffer, ""); 1418 window->ignoreModify = FALSE; 1419 window->nMarks = 0; 1420 window->filenameSet = FALSE; 1421 window->fileMissing = TRUE; 1422 window->fileChanged = FALSE; 1423 window->fileFormat = UNIX_FILE_FORMAT; 1424 window->lastModTime = 0; 1425 window->device = 0; 1426 window->inode = 0; 1427 1428 StopHighlighting(window); 1429 EndSmartIndent(window); 1430 UpdateWindowTitle(window); 1431 UpdateWindowReadOnly(window); 1432 XtSetSensitive(window->closeItem, FALSE); 1433 XtSetSensitive(window->readOnlyItem, TRUE); 1434 XmToggleButtonSetState(window->readOnlyItem, FALSE, FALSE); 1435 ClearUndoList(window); 1436 ClearRedoList(window); 1437 XmTextSetString(window->statsLine, ""); /* resets scroll pos of stats 1438 line from long file names */ 1439 UpdateStatsLine(window); 1440 DetermineLanguageMode(window, True); 1441 RefreshTabState(window); 1442 updateLineNumDisp(window); 1443 return; 1444 } 1445 1446 /* Free syntax highlighting patterns, if any. w/o redisplaying */ 1447 FreeHighlightingData(window); 1448 1449 /* remove the buffer modification callbacks so the buffer will be 1450 deallocated when the last text widget is destroyed */ 1451 BufRemoveModifyCB(window->buffer, modifiedCB, window); 1452 BufRemoveModifyCB(window->buffer, SyntaxHighlightModifyCB, window); 1453 1454 #ifdef ROWCOLPATCH 1455 patchRowCol(window->menuBar); 1456 #endif 1457 1458 /* free the undo and redo lists */ 1459 ClearUndoList(window); 1460 ClearRedoList(window); 1461 1462 /* close the document/window */ 1463 if (NDocuments(window) > 1) { 1464 if (MacroRunWindow() && MacroRunWindow() != window 1465 && MacroRunWindow()->shell == window->shell) { 1466 nextBuf = MacroRunWindow(); 1467 RaiseDocument(nextBuf); 1468 } else if (IsTopDocument(window)) { 1469 /* need to find a successor before closing a top document */ 1470 nextBuf = getNextTabWindow(window, 1, 0, 0); 1471 RaiseDocument(nextBuf); 1472 } else { 1473 topBuf = GetTopDocument(window->shell); 1474 } 1475 } 1476 1477 /* remove the window from the global window list, update window menus */ 1478 removeFromWindowList(window); 1479 InvalidateWindowMenus(); 1480 CheckCloseDim(); /* Close of window running a macro may have been disabled. */ 1481 1482 /* remove the tab of the closing document from tab bar */ 1483 XtDestroyWidget(window->tab); 1484 1485 /* refresh tab bar after closing a document */ 1486 if (nextBuf) { 1487 ShowWindowTabBar(nextBuf); 1488 updateLineNumDisp(nextBuf); 1489 } else if (topBuf) { 1490 ShowWindowTabBar(topBuf); 1491 updateLineNumDisp(topBuf); 1492 } 1493 1494 /* dim/undim Detach_Tab menu items */ 1495 win = nextBuf? nextBuf : topBuf; 1496 if (win) { 1497 state = NDocuments(win) > 1; 1498 XtSetSensitive(win->detachDocumentItem, state); 1499 XtSetSensitive(win->contextDetachDocumentItem, state); 1500 } 1501 1502 /* dim/undim Attach_Tab menu items */ 1503 state = NDocuments(WindowList) < NWindows(); 1504 for(win=WindowList; win; win=win->next) { 1505 if (IsTopDocument(win)) { 1506 XtSetSensitive(win->moveDocumentItem, state); 1507 XtSetSensitive(win->contextMoveDocumentItem, state); 1508 } 1509 } 1510 1511 /* free background menu cache for document */ 1512 FreeUserBGMenuCache(&window->userBGMenuCache); 1513 1514 /* destroy the document's pane, or the window */ 1515 if (nextBuf || topBuf) { 1516 deleteDocument(window); 1517 } else 1518 { 1519 /* free user menu cache for window */ 1520 FreeUserMenuCache(window->userMenuCache); 1521 1522 /* remove and deallocate all of the widgets associated with window */ 1523 NEditFree(window->backlightCharTypes); /* we made a copy earlier on */ 1524 NEditFree(window->indentRainbowColors); 1525 CloseAllPopupsFor(window->shell); 1526 XtDestroyWidget(window->shell); 1527 } 1528 1529 /* unref window fonts */ 1530 FontUnref(window->font); 1531 FontUnref(window->boldFont); 1532 FontUnref(window->italicFont); 1533 FontUnref(window->boldItalicFont); 1534 1535 NEditFree(window->filter); 1536 1537 if(window->encErrors) { 1538 NEditFree(window->encErrors); 1539 } 1540 1541 /* deallocate the window data structure */ 1542 NEditFree(window); 1543 } 1544 1545 /* 1546 ** check if tab bar is to be shown on this window 1547 */ 1548 int GetShowTabBar(WindowInfo *window) 1549 { 1550 if (!GetPrefTabBar()) 1551 return False; 1552 else if (NDocuments(window) == 1) 1553 return !GetPrefTabBarHideOne(); 1554 else 1555 return True; 1556 } 1557 1558 void ShowWindowTabBar(WindowInfo *window) 1559 { 1560 if (GetPrefTabBar()) { 1561 if (GetPrefTabBarHideOne()) 1562 ShowTabBar(window, NDocuments(window)>1); 1563 else 1564 ShowTabBar(window, True); 1565 } 1566 else 1567 ShowTabBar(window, False); 1568 } 1569 1570 /* 1571 ** Check if there is already a window open for a given file 1572 */ 1573 WindowInfo *FindWindowWithFile(const char *name, const char *path) 1574 { 1575 WindowInfo* window; 1576 1577 if (!GetPrefHonorSymlinks()) 1578 { 1579 char fullname[MAXPATHLEN + 1]; 1580 struct stat attribute; 1581 1582 strncpy(fullname, path, MAXPATHLEN); 1583 strncat(fullname, name, MAXPATHLEN); 1584 fullname[MAXPATHLEN] = '\0'; 1585 1586 if (0 == stat(fullname, &attribute)) { 1587 for (window = WindowList; window != NULL; window = window->next) { 1588 if (attribute.st_dev == window->device 1589 && attribute.st_ino == window->inode) { 1590 return window; 1591 } 1592 } 1593 } /* else: Not an error condition, just a new file. Continue to check 1594 whether the filename is already in use for an unsaved document. */ 1595 } 1596 1597 for (window = WindowList; window != NULL; window = window->next) { 1598 if (!strcmp(window->filename, name) && !strcmp(window->path, path)) { 1599 return window; 1600 } 1601 } 1602 return NULL; 1603 } 1604 1605 /* 1606 ** Add another independently scrollable pane to the current document, 1607 ** splitting the pane which currently has keyboard focus. 1608 */ 1609 void SplitPane(WindowInfo *window) 1610 { 1611 short paneHeights[MAX_PANES+1]; 1612 int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1]; 1613 int horizOffsets[MAX_PANES+1]; 1614 int i, focusPane, emTabDist, wrapMargin, lineNumCols, totalHeight=0; 1615 char *delimiters; 1616 Widget text = NULL; 1617 textDisp *textD, *newTextD; 1618 1619 /* Don't create new panes if we're already at the limit */ 1620 if (window->nPanes >= MAX_PANES) 1621 return; 1622 1623 /* Record the current heights, scroll positions, and insert positions 1624 of the existing panes, keyboard focus */ 1625 focusPane = 0; 1626 for (i=0; i<=window->nPanes; i++) { 1627 text = i==0 ? window->textArea : window->textPanes[i-1]; 1628 insertPositions[i] = TextGetCursorPos(text); 1629 XtVaGetValues(containingPane(text),XmNheight,&paneHeights[i],NULL); 1630 totalHeight += paneHeights[i]; 1631 TextGetScroll(text, &topLines[i], &horizOffsets[i]); 1632 if (text == window->lastFocus) 1633 focusPane = i; 1634 } 1635 1636 /* Unmanage & remanage the panedWindow so it recalculates pane heights */ 1637 XtUnmanageChild(window->splitPane); 1638 1639 /* Create a text widget to add to the pane and set its buffer and 1640 highlight data to be the same as the other panes in the document */ 1641 XtVaGetValues(window->textArea, textNemulateTabs, &emTabDist, 1642 textNwordDelimiters, &delimiters, textNwrapMargin, &wrapMargin, 1643 textNlineNumCols, &lineNumCols, NULL); 1644 text = createTextArea(window->splitPane, window, 1, 1, emTabDist, 1645 delimiters, wrapMargin, lineNumCols); 1646 1647 TextSetBuffer(text, window->buffer); 1648 if (window->highlightData != NULL) 1649 AttachHighlightToWidget(text, window); 1650 if (window->backlightChars) 1651 { 1652 XtVaSetValues(text, textNbacklightCharTypes, 1653 window->backlightCharTypes, NULL); 1654 } 1655 XtManageChild(text); 1656 window->textPanes[window->nPanes++] = text; 1657 1658 /* Fix up the colors */ 1659 textD = ((TextWidget)window->textArea)->text.textD; 1660 newTextD = ((TextWidget)text)->text.textD; 1661 XtVaSetValues(text, 1662 XmNforeground, textD->colorProfile->textFgColor.pixel, 1663 XmNbackground, textD->colorProfile->textBgColor.pixel, 1664 NULL); 1665 TextDSetColorProfile(newTextD, textD->colorProfile); 1666 1667 /* Set the minimum pane height in the new pane */ 1668 UpdateMinPaneHeights(window); 1669 1670 /* adjust the heights, scroll positions, etc., to split the focus pane */ 1671 for (i=window->nPanes; i>focusPane; i--) { 1672 insertPositions[i] = insertPositions[i-1]; 1673 paneHeights[i] = paneHeights[i-1]; 1674 topLines[i] = topLines[i-1]; 1675 horizOffsets[i] = horizOffsets[i-1]; 1676 } 1677 paneHeights[focusPane] = paneHeights[focusPane]/2; 1678 paneHeights[focusPane+1] = paneHeights[focusPane]; 1679 1680 for (i=0; i<=window->nPanes; i++) { 1681 text = i==0 ? window->textArea : window->textPanes[i-1]; 1682 setPaneDesiredHeight(containingPane(text), paneHeights[i]); 1683 } 1684 1685 /* Re-manage panedWindow to recalculate pane heights & reset selection */ 1686 if (IsTopDocument(window)) 1687 XtManageChild(window->splitPane); 1688 1689 /* Reset all of the heights, scroll positions, etc. */ 1690 for (i=0; i<=window->nPanes; i++) { 1691 text = i==0 ? window->textArea : window->textPanes[i-1]; 1692 TextSetCursorPos(text, insertPositions[i]); 1693 TextSetScroll(text, topLines[i], horizOffsets[i]); 1694 setPaneDesiredHeight(containingPane(text), 1695 totalHeight/(window->nPanes+1)); 1696 } 1697 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT); 1698 1699 /* Update the window manager size hints after the sizes of the panes have 1700 been set (the widget heights are not yet readable here, but they will 1701 be by the time the event loop gets around to running this timer proc) */ 1702 XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0, 1703 wmSizeUpdateProc, window); 1704 } 1705 1706 Widget GetPaneByIndex(WindowInfo *window, int paneIndex) 1707 { 1708 Widget text = NULL; 1709 if (paneIndex >= 0 && paneIndex <= window->nPanes) { 1710 text = (paneIndex == 0) ? window->textArea : window->textPanes[paneIndex - 1]; 1711 } 1712 return(text); 1713 } 1714 1715 int WidgetToPaneIndex(WindowInfo *window, Widget w) 1716 { 1717 int i; 1718 Widget text; 1719 int paneIndex = 0; 1720 1721 for (i = 0; i <= window->nPanes; ++i) { 1722 text = (i == 0) ? window->textArea : window->textPanes[i - 1]; 1723 if (text == w) { 1724 paneIndex = i; 1725 } 1726 } 1727 return(paneIndex); 1728 } 1729 1730 /* 1731 ** Close the window pane that last had the keyboard focus. (Actually, close 1732 ** the bottom pane and make it look like pane which had focus was closed) 1733 */ 1734 void ClosePane(WindowInfo *window) 1735 { 1736 short paneHeights[MAX_PANES+1]; 1737 int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1]; 1738 int horizOffsets[MAX_PANES+1]; 1739 int i, focusPane; 1740 Widget text; 1741 1742 /* Don't delete the last pane */ 1743 if (window->nPanes <= 0) 1744 return; 1745 1746 /* Record the current heights, scroll positions, and insert positions 1747 of the existing panes, and the keyboard focus */ 1748 focusPane = 0; 1749 for (i=0; i<=window->nPanes; i++) { 1750 text = i==0 ? window->textArea : window->textPanes[i-1]; 1751 insertPositions[i] = TextGetCursorPos(text); 1752 XtVaGetValues(containingPane(text), 1753 XmNheight, &paneHeights[i], NULL); 1754 TextGetScroll(text, &topLines[i], &horizOffsets[i]); 1755 if (text == window->lastFocus) 1756 focusPane = i; 1757 } 1758 1759 /* Unmanage & remanage the panedWindow so it recalculates pane heights */ 1760 XtUnmanageChild(window->splitPane); 1761 1762 /* Destroy last pane, and make sure lastFocus points to an existing pane. 1763 Workaround for OM 2.1.30: text widget must be unmanaged for 1764 xmPanedWindowWidget to calculate the correct pane heights for 1765 the remaining panes, simply detroying it didn't seem enough */ 1766 window->nPanes--; 1767 XtUnmanageChild(containingPane(window->textPanes[window->nPanes])); 1768 XtDestroyWidget(containingPane(window->textPanes[window->nPanes])); 1769 1770 if (window->nPanes == 0) 1771 window->lastFocus = window->textArea; 1772 else if (focusPane > window->nPanes) 1773 window->lastFocus = window->textPanes[window->nPanes-1]; 1774 1775 /* adjust the heights, scroll positions, etc., to make it look 1776 like the pane with the input focus was closed */ 1777 for (i=focusPane; i<=window->nPanes; i++) { 1778 insertPositions[i] = insertPositions[i+1]; 1779 paneHeights[i] = paneHeights[i+1]; 1780 topLines[i] = topLines[i+1]; 1781 horizOffsets[i] = horizOffsets[i+1]; 1782 } 1783 1784 /* set the desired heights and re-manage the paned window so it will 1785 recalculate pane heights */ 1786 for (i=0; i<=window->nPanes; i++) { 1787 text = i==0 ? window->textArea : window->textPanes[i-1]; 1788 setPaneDesiredHeight(containingPane(text), paneHeights[i]); 1789 } 1790 1791 if (IsTopDocument(window)) 1792 XtManageChild(window->splitPane); 1793 1794 /* Reset all of the scroll positions, insert positions, etc. */ 1795 for (i=0; i<=window->nPanes; i++) { 1796 text = i==0 ? window->textArea : window->textPanes[i-1]; 1797 TextSetCursorPos(text, insertPositions[i]); 1798 TextSetScroll(text, topLines[i], horizOffsets[i]); 1799 } 1800 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT); 1801 1802 /* Update the window manager size hints after the sizes of the panes have 1803 been set (the widget heights are not yet readable here, but they will 1804 be by the time the event loop gets around to running this timer proc) */ 1805 XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0, 1806 wmSizeUpdateProc, window); 1807 } 1808 1809 /* 1810 ** Turn on and off the display of line numbers 1811 */ 1812 void ShowLineNumbers(WindowInfo *window, int state) 1813 { 1814 Widget text; 1815 int i, marginWidth; 1816 unsigned reqCols = 0; 1817 Dimension windowWidth; 1818 WindowInfo *win; 1819 textDisp *textD = ((TextWidget)window->textArea)->text.textD; 1820 1821 if (window->showLineNumbers == state) 1822 return; 1823 window->showLineNumbers = state; 1824 1825 /* Just setting window->showLineNumbers is sufficient to tell 1826 updateLineNumDisp() to expand the line number areas and the window 1827 size for the number of lines required. To hide the line number 1828 display, set the width to zero, and contract the window width. */ 1829 if (state) { 1830 reqCols = updateLineNumDisp(window); 1831 } else { 1832 XtVaGetValues(window->shell, XmNwidth, &windowWidth, NULL); 1833 XtVaGetValues(window->textArea, 1834 textNmarginWidth, &marginWidth, NULL); 1835 XtVaSetValues(window->shell, XmNwidth, 1836 windowWidth - textD->left + marginWidth, NULL); 1837 1838 for (i=0; i<=window->nPanes; i++) { 1839 text = i==0 ? window->textArea : window->textPanes[i-1]; 1840 XtVaSetValues(text, textNlineNumCols, 0, NULL); 1841 } 1842 } 1843 1844 /* line numbers panel is shell-level, hence other 1845 tabbed documents in the window should synch */ 1846 for (win=WindowList; win; win=win->next) { 1847 if (win->shell != window->shell || win == window) 1848 continue; 1849 1850 win->showLineNumbers = state; 1851 1852 for (i=0; i<=win->nPanes; i++) { 1853 text = i==0 ? win->textArea : win->textPanes[i-1]; 1854 /* reqCols should really be cast here, but into what? XmRInt? */ 1855 XtVaSetValues(text, textNlineNumCols, reqCols, NULL); 1856 } 1857 } 1858 1859 /* Tell WM that the non-expandable part of the window has changed size */ 1860 UpdateWMSizeHints(window); 1861 } 1862 1863 /* 1864 ** Turn on and off the display of the encoding infobar 1865 */ 1866 void ShowEncodingInfoBar(WindowInfo *window, int state) 1867 { 1868 if (window->showInfoBar == state && XtIsManaged(window->encodingInfoBar) == state) { 1869 return; 1870 } 1871 window->showInfoBar = state; 1872 1873 if(state == 0) { 1874 XtUnmanageChild(window->encodingInfoBar); 1875 showStatsForm(window); 1876 return; 1877 } 1878 1879 // current document encoding 1880 char *def = strlen(window->encoding) > 0 ? window->encoding : NULL; 1881 1882 int arraylen = 22; 1883 XmStringTable encodings = NEditCalloc(arraylen, sizeof(XmString)); 1884 const char *encStr; 1885 int i; 1886 int index = 0; 1887 1888 // add default encodings 1889 const char **default_encodings = FileDialogDefaultEncodings(); 1890 for(i=0;(encStr=default_encodings[i]);i++) { 1891 if(i >= arraylen) { 1892 arraylen *= 2; 1893 encodings = NEditRealloc(encodings, arraylen * sizeof(XmString)); 1894 } 1895 encodings[i] = XmStringCreateSimple((char*)encStr); 1896 1897 if(def) { 1898 if(!strcasecmp(def, encStr)) { 1899 def = NULL; 1900 index = i; 1901 } 1902 } 1903 } 1904 1905 // add document encoding, if it isn't already in the list 1906 if(def) { 1907 if(i >= arraylen) { 1908 arraylen += 2; 1909 encodings = NEditRealloc(encodings, arraylen * sizeof(XmString)); 1910 } 1911 encodings[i] = XmStringCreateSimple(def); 1912 index = i; 1913 i++; 1914 } 1915 1916 // set dropdownlist values 1917 XtVaSetValues( 1918 window->encInfoBarList, 1919 XmNitemCount, i, 1920 XmNitems, encodings, 1921 NULL); 1922 XmComboBoxSelectItem(window->encInfoBarList, encodings[index]); 1923 1924 // cleanup 1925 for(int j=0;j<i;j++) { 1926 XmStringFree(encodings[j]); 1927 } 1928 NEditFree(encodings); 1929 1930 // show infobar 1931 XtManageChild(window->encodingInfoBar); 1932 1933 showStatsForm(window); 1934 } 1935 1936 /* 1937 * Set the text message for the encoding infobar 1938 */ 1939 void SetEncodingInfoBarLabel(WindowInfo *window, char *message) 1940 { 1941 XmString s1 = XmStringCreateLocalized(message); 1942 XtVaSetValues(window->encInfoBarLabel, XmNlabelString, s1, NULL); 1943 XmStringFree(s1); 1944 } 1945 1946 1947 void SetTabDist(WindowInfo *window, int tabDist) 1948 { 1949 if (window->buffer->tabDist != tabDist) { 1950 int saveCursorPositions[MAX_PANES + 1]; 1951 int saveVScrollPositions[MAX_PANES + 1]; 1952 int saveHScrollPositions[MAX_PANES + 1]; 1953 int paneIndex; 1954 1955 window->ignoreModify = True; 1956 1957 for (paneIndex = 0; paneIndex <= window->nPanes; ++paneIndex) { 1958 Widget w = GetPaneByIndex(window, paneIndex); 1959 textDisp *textD = ((TextWidget)w)->text.textD; 1960 1961 TextGetScroll(w, &saveVScrollPositions[paneIndex], &saveHScrollPositions[paneIndex]); 1962 saveCursorPositions[paneIndex] = TextGetCursorPos(w); 1963 textD->modifyingTabDist = 1; 1964 } 1965 1966 BufSetTabDistance(window->buffer, tabDist); 1967 1968 for (paneIndex = 0; paneIndex <= window->nPanes; ++paneIndex) { 1969 Widget w = GetPaneByIndex(window, paneIndex); 1970 textDisp *textD = ((TextWidget)w)->text.textD; 1971 1972 textD->modifyingTabDist = 0; 1973 TextSetCursorPos(w, saveCursorPositions[paneIndex]); 1974 TextSetScroll(w, saveVScrollPositions[paneIndex], saveHScrollPositions[paneIndex]); 1975 } 1976 1977 window->ignoreModify = False; 1978 } 1979 } 1980 1981 void SetEmTabDist(WindowInfo *window, int emTabDist) 1982 { 1983 int i; 1984 1985 XtVaSetValues(window->textArea, textNemulateTabs, emTabDist, NULL); 1986 for (i = 0; i < window->nPanes; ++i) { 1987 XtVaSetValues(window->textPanes[i], textNemulateTabs, emTabDist, NULL); 1988 } 1989 } 1990 1991 /* 1992 ** Turn on and off the display of the statistics line 1993 */ 1994 void ShowStatsLine(WindowInfo *window, int state) 1995 { 1996 WindowInfo *win; 1997 Widget text; 1998 int i; 1999 2000 /* In continuous wrap mode, text widgets must be told to keep track of 2001 the top line number in absolute (non-wrapped) lines, because it can 2002 be a costly calculation, and is only needed for displaying line 2003 numbers, either in the stats line, or along the left margin */ 2004 for (i=0; i<=window->nPanes; i++) { 2005 text = i==0 ? window->textArea : window->textPanes[i-1]; 2006 TextDMaintainAbsLineNum(((TextWidget)text)->text.textD, state); 2007 } 2008 window->showStats = state; 2009 showStats(window, state); 2010 2011 /* i-search line is shell-level, hence other tabbed 2012 documents in the window should synch */ 2013 for (win=WindowList; win; win=win->next) { 2014 if (win->shell != window->shell || win == window) 2015 continue; 2016 win->showStats = state; 2017 } 2018 } 2019 2020 /* 2021 ** Displays and undisplays the statistics line (regardless of settings of 2022 ** window->showStats or window->modeMessageDisplayed) 2023 */ 2024 static void showStats(WindowInfo *window, int state) 2025 { 2026 if (state) { 2027 XtManageChild(window->statsLineForm); 2028 showStatsForm(window); 2029 } else { 2030 XtUnmanageChild(window->statsLineForm); 2031 showStatsForm(window); 2032 } 2033 2034 /* Tell WM that the non-expandable part of the window has changed size */ 2035 /* Already done in showStatsForm */ 2036 /* UpdateWMSizeHints(window); */ 2037 } 2038 2039 /* 2040 */ 2041 static void showTabBar(WindowInfo *window, int state) 2042 { 2043 if (state) { 2044 XtManageChild(XtParent(window->tabBar)); 2045 showStatsForm(window); 2046 } else { 2047 XtUnmanageChild(XtParent(window->tabBar)); 2048 showStatsForm(window); 2049 } 2050 } 2051 2052 /* 2053 */ 2054 void ShowTabBar(WindowInfo *window, int state) 2055 { 2056 if (XtIsManaged(XtParent(window->tabBar)) == state) 2057 return; 2058 showTabBar(window, state); 2059 } 2060 2061 /* 2062 ** Turn on and off the continuing display of the incremental search line 2063 ** (when off, it is popped up and down as needed via TempShowISearch) 2064 */ 2065 void ShowISearchLine(WindowInfo *window, int state) 2066 { 2067 WindowInfo *win; 2068 2069 if (window->showISearchLine == state) 2070 return; 2071 window->showISearchLine = state; 2072 showISearch(window, state); 2073 2074 /* i-search line is shell-level, hence other tabbed 2075 documents in the window should synch */ 2076 for (win=WindowList; win; win=win->next) { 2077 if (win->shell != window->shell || win == window) 2078 continue; 2079 win->showISearchLine = state; 2080 } 2081 } 2082 2083 /* 2084 ** Temporarily show and hide the incremental search line if the line is not 2085 ** already up. 2086 */ 2087 void TempShowISearch(WindowInfo *window, int state) 2088 { 2089 if (window->showISearchLine) 2090 return; 2091 if (XtIsManaged(window->iSearchForm) != state) 2092 showISearch(window, state); 2093 } 2094 2095 /* 2096 ** Put up or pop-down the incremental search line regardless of settings 2097 ** of showISearchLine or TempShowISearch 2098 */ 2099 static void showISearch(WindowInfo *window, int state) 2100 { 2101 if (state) { 2102 XtManageChild(window->iSearchForm); 2103 showStatsForm(window); 2104 } else { 2105 XtUnmanageChild(window->iSearchForm); 2106 showStatsForm(window); 2107 } 2108 2109 /* Tell WM that the non-expandable part of the window has changed size */ 2110 /* This is already done in showStatsForm */ 2111 /* UpdateWMSizeHints(window); */ 2112 } 2113 2114 /* 2115 ** Show or hide the extra display area under the main menu bar which 2116 ** optionally contains the status line and the incremental search bar 2117 */ 2118 static void showStatsForm(WindowInfo *window) 2119 { 2120 Widget statsAreaForm = XtParent(window->statsLineForm); 2121 Widget mainW = XtParent(statsAreaForm); 2122 2123 /* The very silly use of XmNcommandWindowLocation and XmNshowSeparator 2124 below are to kick the main window widget to position and remove the 2125 status line when it is managed and unmanaged. At some Motif version 2126 level, the showSeparator trick backfires and leaves the separator 2127 shown, but fortunately the dynamic behavior is fixed, too so the 2128 workaround is no longer necessary, either. (... the version where 2129 this occurs may be earlier than 2.1. If the stats line shows 2130 double thickness shadows in earlier Motif versions, the #if XmVersion 2131 directive should be moved back to that earlier version) */ 2132 if (manageToolBars(statsAreaForm)) { 2133 XtUnmanageChild(statsAreaForm); /*... will this fix Solaris 7??? */ 2134 XtVaSetValues(mainW, XmNcommandWindowLocation, 2135 XmCOMMAND_ABOVE_WORKSPACE, NULL); 2136 #if XmVersion < 2001 2137 XtVaSetValues(mainW, XmNshowSeparator, True, NULL); 2138 #endif 2139 XtManageChild(statsAreaForm); 2140 XtVaSetValues(mainW, XmNshowSeparator, False, NULL); 2141 UpdateStatsLine(window); 2142 } else { 2143 XtUnmanageChild(statsAreaForm); 2144 XtVaSetValues(mainW, XmNcommandWindowLocation, 2145 XmCOMMAND_BELOW_WORKSPACE, NULL); 2146 } 2147 2148 /* Tell WM that the non-expandable part of the window has changed size */ 2149 UpdateWMSizeHints(window); 2150 } 2151 2152 /* 2153 ** Display a special message in the stats line (show the stats line if it 2154 ** is not currently shown). 2155 */ 2156 void SetModeMessage(WindowInfo *window, const char *message) 2157 { 2158 /* this document may be hidden (not on top) or later made hidden, 2159 so we save a copy of the mode message, so we can restore the 2160 statsline when the document is raised to top again */ 2161 window->modeMessageDisplayed = True; 2162 NEditFree(window->modeMessage); 2163 window->modeMessage = NEditStrdup(message); 2164 2165 if (!IsTopDocument(window)) 2166 return; 2167 2168 XmTextSetString(window->statsLine, (char*)message); 2169 /* 2170 * Don't invoke the stats line again, if stats line is already displayed. 2171 */ 2172 if (!window->showStats) 2173 showStats(window, True); 2174 } 2175 2176 /* 2177 ** Clear special statistics line message set in SetModeMessage, returns 2178 ** the statistics line to its original state as set in window->showStats 2179 */ 2180 void ClearModeMessage(WindowInfo *window) 2181 { 2182 if (!window->modeMessageDisplayed) 2183 return; 2184 2185 window->modeMessageDisplayed = False; 2186 NEditFree(window->modeMessage); 2187 window->modeMessage = NULL; 2188 2189 if (!IsTopDocument(window)) 2190 return; 2191 2192 /* 2193 * Remove the stats line only if indicated by it's window state. 2194 */ 2195 if (!window->showStats) 2196 showStats(window, False); 2197 UpdateStatsLine(window); 2198 } 2199 2200 /* 2201 ** Count the windows 2202 */ 2203 int NWindows(void) 2204 { 2205 WindowInfo *win; 2206 int n; 2207 2208 for (win=WindowList, n=0; win!=NULL; win=win->next, n++); 2209 return n; 2210 } 2211 2212 /* 2213 ** Set autoindent state to one of NO_AUTO_INDENT, AUTO_INDENT, or SMART_INDENT. 2214 */ 2215 void SetAutoIndent(WindowInfo *window, IndentStyle state) 2216 { 2217 int autoIndent = state == AUTO_INDENT, smartIndent = state == SMART_INDENT; 2218 int i; 2219 2220 if (window->indentStyle == SMART_INDENT && !smartIndent) 2221 EndSmartIndent(window); 2222 else if (smartIndent && window->indentStyle != SMART_INDENT) 2223 BeginSmartIndent(window, True); 2224 window->indentStyle = state; 2225 XtVaSetValues(window->textArea, textNautoIndent, autoIndent, 2226 textNsmartIndent, smartIndent, NULL); 2227 for (i=0; i<window->nPanes; i++) 2228 XtVaSetValues(window->textPanes[i], textNautoIndent, autoIndent, 2229 textNsmartIndent, smartIndent, NULL); 2230 if (IsTopDocument(window)) { 2231 XmToggleButtonSetState(window->smartIndentItem, smartIndent, False); 2232 XmToggleButtonSetState(window->autoIndentItem, autoIndent, False); 2233 XmToggleButtonSetState(window->autoIndentOffItem, 2234 state == NO_AUTO_INDENT, False); 2235 } 2236 } 2237 2238 /* 2239 ** Set showMatching state to one of NO_FLASH, FLASH_DELIMIT or FLASH_RANGE. 2240 ** Update the menu to reflect the change of state. 2241 */ 2242 void SetShowMatching(WindowInfo *window, ShowMatchingStyle state) 2243 { 2244 window->showMatchingStyle = state; 2245 if (IsTopDocument(window)) { 2246 XmToggleButtonSetState(window->showMatchingOffItem, 2247 state == NO_FLASH, False); 2248 XmToggleButtonSetState(window->showMatchingDelimitItem, 2249 state == FLASH_DELIMIT, False); 2250 XmToggleButtonSetState(window->showMatchingRangeItem, 2251 state == FLASH_RANGE, False); 2252 } 2253 } 2254 2255 /* 2256 ** Update the "New (in X)" menu item to reflect the preferences 2257 */ 2258 void UpdateNewOppositeMenu(WindowInfo *window, int openInTab) 2259 { 2260 XmString lbl; 2261 if ( openInTab ) 2262 XtVaSetValues(window->newOppositeItem, 2263 XmNlabelString, lbl=XmStringCreateSimple("New Window"), 2264 XmNmnemonic, 'W', NULL); 2265 else 2266 XtVaSetValues(window->newOppositeItem, 2267 XmNlabelString, lbl=XmStringCreateSimple("New Tab"), 2268 XmNmnemonic, 'T', NULL); 2269 XmStringFree(lbl); 2270 } 2271 2272 /* 2273 ** Set the fonts for "window" from a font name, and updates the display. 2274 ** Also updates window->fontList which is used for statistics line. 2275 ** 2276 ** Note that this leaks memory and server resources. In previous NEdit 2277 ** versions, fontLists were carefully tracked and freed, but X and Motif 2278 ** have some kind of timing problem when widgets are distroyed, such that 2279 ** fonts may not be freed immediately after widget destruction with 100% 2280 ** safety. Rather than kludge around this with timerProcs, I have chosen 2281 ** to create new fontLists only when the user explicitly changes the font 2282 ** (which shouldn't happen much in normal NEdit operation), and skip the 2283 ** futile effort of freeing them. 2284 */ 2285 void SetFonts(WindowInfo *window, const char *fontName, const char *italicName, 2286 const char *boldName, const char *boldItalicName) 2287 { 2288 //XFontStruct *font, *oldFont; 2289 NFont *font; 2290 NFont *oldFont; 2291 int i, oldFontWidth, oldFontHeight, fontWidth, fontHeight; 2292 int borderWidth, borderHeight, marginWidth, marginHeight; 2293 int primaryChanged, highlightChanged = False; 2294 Dimension oldWindowWidth, oldWindowHeight, oldTextWidth, oldTextHeight; 2295 Dimension textHeight, newWindowWidth, newWindowHeight; 2296 textDisp *textD = ((TextWidget)window->textArea)->text.textD; 2297 2298 NFont *unrefFont = NULL; 2299 NFont *unrefItalic = NULL; 2300 NFont *unrefBold = NULL; 2301 NFont *unrefBoldItalic = NULL; 2302 2303 /* Check which fonts have changed */ 2304 primaryChanged = strcmp(fontName, window->fontName); 2305 if (strcmp(italicName, window->italicFontName)) highlightChanged = True; 2306 if (strcmp(boldName, window->boldFontName)) highlightChanged = True; 2307 if (strcmp(boldItalicName, window->boldItalicFontName)) 2308 highlightChanged = True; 2309 if (!primaryChanged && !highlightChanged) 2310 return; 2311 2312 /* Get information about the current window sizing, to be used to 2313 determine the correct window size after the font is changed */ 2314 XtVaGetValues(window->shell, XmNwidth, &oldWindowWidth, XmNheight, 2315 &oldWindowHeight, NULL); 2316 XtVaGetValues(window->textArea, XmNheight, &textHeight, 2317 textNmarginHeight, &marginHeight, textNmarginWidth, 2318 &marginWidth, textNXftFont, &oldFont, NULL); 2319 oldTextWidth = textD->width + textD->lineNumWidth; 2320 oldTextHeight = textHeight - 2*marginHeight; 2321 for (i=0; i<window->nPanes; i++) { 2322 XtVaGetValues(window->textPanes[i], XmNheight, &textHeight, NULL); 2323 oldTextHeight += textHeight - 2*marginHeight; 2324 } 2325 borderWidth = oldWindowWidth - oldTextWidth; 2326 borderHeight = oldWindowHeight - oldTextHeight; 2327 oldFontWidth = oldFont->maxWidth; 2328 oldFontHeight = textD->ascent + textD->descent; 2329 2330 2331 /* Change the fonts in the window data structure. If the primary font 2332 didn't work, use Motif's fallback mechanism by stealing it from the 2333 statistics line. Highlight fonts are allowed to be NULL, which 2334 is interpreted as "use the primary font" */ 2335 if (primaryChanged) { 2336 strcpy(window->fontName, fontName); 2337 //font = XLoadQueryFont(TheDisplay, fontName); 2338 font = FontFromName(TheDisplay, fontName); 2339 if (font == NULL) { 2340 //XtVaGetValues(window->statsLine, XmNfontList, &window->fontList, 2341 // NULL); 2342 printf("implement fallback font\n"); 2343 } else { 2344 unrefFont = window->font; 2345 window->font = font; 2346 } 2347 } 2348 if (highlightChanged) { 2349 NFont *newitalic = FontFromName(TheDisplay, italicName); 2350 if(newitalic) { 2351 strcpy(window->italicFontName, italicName); 2352 unrefItalic = window->italicFont; // unref later 2353 window->italicFont = newitalic; 2354 } 2355 2356 NFont *newbold = FontFromName(TheDisplay, boldName); 2357 if(newbold) { 2358 strcpy(window->boldFontName, boldName); 2359 unrefBold = window->boldFont; // unref later 2360 window->boldFont = newbold; 2361 } 2362 2363 NFont *newbolditalic = FontFromName(TheDisplay, boldItalicName); 2364 if(newbolditalic) { 2365 strcpy(window->boldItalicFontName, boldItalicName); 2366 unrefBoldItalic = window->boldItalicFont; 2367 window->boldItalicFont = newbolditalic; 2368 } 2369 } 2370 2371 /* Change the highlight fonts, even if they didn't change, because 2372 primary font is read through the style table for syntax highlighting */ 2373 if (window->highlightData != NULL) 2374 UpdateHighlightStyles(window, False); 2375 2376 /* Change the primary font in all the widgets */ 2377 if (primaryChanged) { 2378 //font = GetDefaultFontStruct(TheDisplay, window->fontList); 2379 font = window->font; 2380 XtVaSetValues(window->textArea, 2381 textNXftFont, font, 2382 textNXftBoldFont, window->boldFont, 2383 textNXftItalicFont, window->italicFont, 2384 textNXftBoldItalicFont, window->boldItalicFont, NULL); 2385 for (i=0; i<window->nPanes; i++) { 2386 XtVaSetValues(window->textPanes[i], 2387 textNXftFont, font, 2388 textNXftBoldFont, window->boldFont, 2389 textNXftItalicFont, window->italicFont, 2390 textNXftBoldItalicFont, window->boldItalicFont, NULL); 2391 } 2392 } 2393 2394 /* unref highlight fonts */ 2395 if(unrefFont) 2396 //FontUnref(unrefFont); 2397 if(unrefItalic) 2398 FontUnref(unrefItalic); 2399 if(unrefBold) 2400 FontUnref(unrefBold); 2401 if(unrefBoldItalic) 2402 FontUnref(unrefBoldItalic); 2403 2404 if(window->resizeOnFontChange) { 2405 /* Change the window manager size hints. 2406 Note: this has to be done _before_ we set the new sizes. ICCCM2 2407 compliant window managers (such as fvwm2) would otherwise resize 2408 the window twice: once because of the new sizes requested, and once 2409 because of the new size increments, resulting in an overshoot. */ 2410 UpdateWMSizeHints(window); 2411 2412 /* Use the information from the old window to re-size the window to a 2413 size appropriate for the new font, but only do so if there's only 2414 _one_ document in the window, in order to avoid growing-window bug */ 2415 if (NDocuments(window) == 1) { 2416 fontWidth = window->font->maxWidth; 2417 fontHeight = textD->ascent + textD->descent; 2418 newWindowWidth = (oldTextWidth*fontWidth) / oldFontWidth + borderWidth; 2419 newWindowHeight = (oldTextHeight*fontHeight) / oldFontHeight + 2420 borderHeight; 2421 XtVaSetValues(window->shell, XmNwidth, newWindowWidth, XmNheight, 2422 newWindowHeight, NULL); 2423 } 2424 } 2425 2426 /* Change the minimum pane height */ 2427 UpdateMinPaneHeights(window); 2428 } 2429 2430 void LoadColorProfile(Widget w, ColorProfile *profile) 2431 { 2432 Colormap cmap; 2433 Pixel foreground; 2434 int depth; 2435 XtVaGetValues(w, 2436 XtNcolormap, &cmap, 2437 XtNdepth, &depth, 2438 XtNforeground, &foreground, 2439 NULL); 2440 2441 Display *display = XtDisplay(w); 2442 2443 int i, dummy; 2444 Pixel textFgPix = AllocColor( w, profile->textFg, 2445 &dummy, &dummy, &dummy), 2446 textBgPix = AllocColor( w, profile->textBg, 2447 &dummy, &dummy, &dummy), 2448 selectFgPix = AllocColor( w, profile->selectFg, 2449 &dummy, &dummy, &dummy), 2450 selectBgPix = AllocColor( w, profile->selectBg, 2451 &dummy, &dummy, &dummy), 2452 hiliteFgPix = AllocColor( w, profile->hiliteFg, 2453 &dummy, &dummy, &dummy), 2454 hiliteBgPix = AllocColor( w, profile->hiliteBg, 2455 &dummy, &dummy, &dummy), 2456 lineNoFgPix = AllocColor( w, profile->lineNoFg, 2457 &dummy, &dummy, &dummy), 2458 lineNoBgPix = AllocColor( w, profile->lineNoBg, 2459 &dummy, &dummy, &dummy), 2460 cursorFgPix = AllocColor( w, profile->cursorFg, 2461 &dummy, &dummy, &dummy), 2462 lineHiBgPix = AllocColor( w, profile->lineHiBg, 2463 &dummy, &dummy, &dummy); 2464 textDisp *textD; 2465 2466 profile->textFgColor = PixelToColor(w, textFgPix); 2467 profile->textBgColor = PixelToColor(w, textBgPix); 2468 profile->selectFgColor = PixelToColor(w, selectFgPix); 2469 profile->selectBgColor = PixelToColor(w, selectBgPix); 2470 profile->hiliteFgColor = PixelToColor(w, hiliteFgPix); 2471 profile->hiliteBgColor = PixelToColor(w, hiliteBgPix); 2472 profile->lineNoFgColor = PixelToColor(w, lineNoFgPix); 2473 profile->lineNoBgColor = PixelToColor(w, lineNoBgPix); 2474 profile->cursorFgColor = PixelToColor(w, cursorFgPix); 2475 profile->lineHiBgColor = PixelToColor(w, lineHiBgPix); 2476 2477 if(profile->rainbowColorList) { 2478 ColorList rainbowColors = ParseColorList(profile->rainbowColorList, strlen(profile->rainbowColorList)); 2479 profile->rainbowColors = NEditCalloc(rainbowColors.ncolors, sizeof(XftColor)); 2480 for(int i=0;i<rainbowColors.ncolors;i++) { 2481 profile->rainbowColors[i] = ParseXftColor(display, cmap, foreground, depth, rainbowColors.colors[i]); 2482 } 2483 profile->numRainbowColors = rainbowColors.ncolors; 2484 free(rainbowColors.colors); 2485 free(rainbowColors.liststr); 2486 } 2487 2488 if(profile->ansiColorList) { 2489 ColorList ansiColors = ParseColorList(profile->ansiColorList, strlen(profile->ansiColorList)); 2490 profile->ansiColors = NEditCalloc(ansiColors.ncolors, sizeof(XftColor)); 2491 for(int i=0;i<ansiColors.ncolors;i++) { 2492 profile->ansiColors[i] = ParseXftColor(display, cmap, foreground, depth, ansiColors.colors[i]); 2493 } 2494 profile->numAnsiColors = ansiColors.ncolors; 2495 free(ansiColors.colors); 2496 free(ansiColors.liststr); 2497 } 2498 2499 if(!profile->db || !profile->resDBLoaded) { 2500 LoadColorProfileResources(display, profile); 2501 } 2502 2503 if(!profile->stylesLoaded) { 2504 ColorProfileLoadHighlightStyles(profile); 2505 } 2506 2507 profile->colorsLoaded = TRUE; 2508 } 2509 2510 void SetColorProfile(WindowInfo *window, ColorProfile *profile) 2511 { 2512 if(!profile->colorsLoaded) { 2513 LoadColorProfile(window->textArea, profile); 2514 } 2515 window->colorProfile = profile; 2516 2517 /* Update the main pane */ 2518 XtVaSetValues(window->textArea, 2519 XmNforeground, profile->textFgColor.pixel, 2520 XmNbackground, profile->textBgColor.pixel, 2521 NULL); 2522 textDisp *textD = ((TextWidget)window->textArea)->text.textD; 2523 TextDSetColorProfile( textD, profile); 2524 /* Update any additional panes */ 2525 for (int i=0; i<window->nPanes; i++) { 2526 XtVaSetValues(window->textPanes[i], 2527 XmNforeground, profile->textFgColor.pixel, 2528 XmNbackground, profile->textBgColor.pixel, 2529 NULL); 2530 textD = ((TextWidget)window->textPanes[i])->text.textD; 2531 TextDSetColorProfile( textD, profile); 2532 } 2533 2534 /* Redo any syntax highlighting */ 2535 if (window->highlightData != NULL) 2536 UpdateHighlightStyles(window, True); 2537 } 2538 2539 void EnableWindowResourceDB(const WindowInfo *window) 2540 { 2541 if(window->colorProfile && window->colorProfile->db) { 2542 XrmSetDatabase(XtDisplay(window->shell), window->colorProfile->db); 2543 } 2544 } 2545 2546 /* 2547 ** Set insert/overstrike mode 2548 */ 2549 void SetOverstrike(WindowInfo *window, int overstrike) 2550 { 2551 int i; 2552 2553 XtVaSetValues(window->textArea, textNoverstrike, overstrike, NULL); 2554 for (i=0; i<window->nPanes; i++) 2555 XtVaSetValues(window->textPanes[i], textNoverstrike, overstrike, NULL); 2556 window->overstrike = overstrike; 2557 } 2558 2559 /* 2560 ** Select auto-wrap mode, one of NO_WRAP, NEWLINE_WRAP, or CONTINUOUS_WRAP 2561 */ 2562 void SetAutoWrap(WindowInfo *window, WrapStyle state) 2563 { 2564 if(window->wrapModeNoneForced) { 2565 // this is only true if a large file was opened (for a short time) 2566 // it is set to False after the file is fully opened 2567 return; 2568 } 2569 2570 int i; 2571 int autoWrap = state == NEWLINE_WRAP, contWrap = state == CONTINUOUS_WRAP; 2572 2573 XtVaSetValues(window->textArea, textNautoWrap, autoWrap, 2574 textNcontinuousWrap, contWrap, NULL); 2575 for (i=0; i<window->nPanes; i++) 2576 XtVaSetValues(window->textPanes[i], textNautoWrap, autoWrap, 2577 textNcontinuousWrap, contWrap, NULL); 2578 window->wrapMode = state; 2579 2580 if (IsTopDocument(window)) { 2581 XmToggleButtonSetState(window->newlineWrapItem, autoWrap, False); 2582 XmToggleButtonSetState(window->continuousWrapItem, contWrap, False); 2583 XmToggleButtonSetState(window->noWrapItem, state == NO_WRAP, False); 2584 } 2585 } 2586 2587 /* 2588 ** Set the auto-scroll margin 2589 */ 2590 void SetAutoScroll(WindowInfo *window, int margin) 2591 { 2592 int i; 2593 2594 XtVaSetValues(window->textArea, textNcursorVPadding, margin, NULL); 2595 for (i=0; i<window->nPanes; i++) 2596 XtVaSetValues(window->textPanes[i], textNcursorVPadding, margin, NULL); 2597 } 2598 2599 /* 2600 ** Recover the window pointer from any widget in the window, by searching 2601 ** up the widget hierarcy for the top level container widget where the 2602 ** window pointer is stored in the userData field. In a tabbed window, 2603 ** this is the window pointer of the top (active) document, which is 2604 ** returned if w is 'shell-level' widget - menus, find/replace dialogs, etc. 2605 ** 2606 ** To support action routine in tabbed windows, a copy of the window 2607 ** pointer is also store in the splitPane widget. 2608 */ 2609 WindowInfo *WidgetToWindow(Widget w) 2610 { 2611 WindowInfo *window = NULL; 2612 Widget parent; 2613 2614 while (True) { 2615 /* return window pointer of document */ 2616 if (XtClass(w) == xmPanedWindowWidgetClass) 2617 break; 2618 2619 if (XtClass(w) == topLevelShellWidgetClass) { 2620 WidgetList items; 2621 2622 /* there should be only 1 child for the shell - 2623 the main window widget */ 2624 XtVaGetValues(w, XmNchildren, &items, NULL); 2625 w = items[0]; 2626 break; 2627 } 2628 2629 parent = XtParent(w); 2630 if (parent == NULL) 2631 return NULL; 2632 2633 /* make sure it is not a dialog shell */ 2634 if (XtClass(parent) == topLevelShellWidgetClass && 2635 XmIsMainWindow(w)) 2636 break; 2637 2638 w = parent; 2639 } 2640 2641 XtVaGetValues(w, XmNuserData, &window, NULL); 2642 2643 return window; 2644 } 2645 2646 /* 2647 ** Change the window appearance and the window data structure to show 2648 ** that the file it contains has been modified 2649 */ 2650 void SetWindowModified(WindowInfo *window, int modified) 2651 { 2652 if (window->fileChanged == FALSE && modified == TRUE) { 2653 SetSensitive(window, window->closeItem, TRUE); 2654 window->fileChanged = TRUE; 2655 UpdateWindowTitle(window); 2656 RefreshTabState(window); 2657 } else if (window->fileChanged == TRUE && modified == FALSE) { 2658 window->fileChanged = FALSE; 2659 UpdateWindowTitle(window); 2660 RefreshTabState(window); 2661 } 2662 } 2663 2664 static int utf8TitleAtomsInit = 0; 2665 static Atom utf8_string; 2666 static Atom net_wm_name; 2667 2668 static void setUtf8Title(Widget shell, const char *title) { 2669 Display *dp = XtDisplay(shell); 2670 if(!utf8TitleAtomsInit) { 2671 utf8_string = XInternAtom(dp, "UTF8_STRING", False); 2672 net_wm_name = XInternAtom(dp, "_NET_WM_NAME", False); 2673 utf8TitleAtomsInit = 1; 2674 } 2675 XChangeProperty( 2676 dp, 2677 XtWindow(shell), 2678 net_wm_name, 2679 utf8_string, 2680 8, 2681 PropModeReplace, 2682 (unsigned char*)title, 2683 strlen(title)); 2684 } 2685 2686 /* 2687 ** Update the window title to reflect the filename, read-only, and modified 2688 ** status of the window data structure 2689 */ 2690 void UpdateWindowTitle(const WindowInfo *window) 2691 { 2692 char *iconTitle, *title; 2693 2694 if (!IsTopDocument(window)) 2695 return; 2696 2697 title = FormatWindowTitle(window->filename, 2698 window->path, 2699 GetClearCaseViewTag(), 2700 GetPrefServerName(), 2701 window->encoding, 2702 IsServer, 2703 window->filenameSet, 2704 window->lockReasons, 2705 window->fileChanged, 2706 GetPrefTitleFormat()); 2707 2708 iconTitle = (char*)NEditMalloc(strlen(window->filename) + 2); /* strlen("*")+1 */ 2709 2710 strcpy(iconTitle, window->filename); 2711 if (window->fileChanged) { 2712 strcat(iconTitle, "*"); 2713 } 2714 if(XNEditDefaultCharsetIsUTF8()) { 2715 setUtf8Title(window->shell, title); 2716 } 2717 XtVaSetValues(window->shell, XmNtitle, title, XmNiconName, iconTitle, NULL); 2718 2719 /* If there's a find or replace dialog up in "Keep Up" mode, with a 2720 file name in the title, update it too */ 2721 if (window->findDlog && XmToggleButtonGetState(window->findKeepBtn)) { 2722 sprintf(title, "Find (in %s)", window->filename); 2723 XtVaSetValues(XtParent(window->findDlog), XmNtitle, title, NULL); 2724 } 2725 if (window->replaceDlog && XmToggleButtonGetState(window->replaceKeepBtn)) { 2726 sprintf(title, "Replace (in %s)", window->filename); 2727 XtVaSetValues(XtParent(window->replaceDlog), XmNtitle, title, NULL); 2728 } 2729 NEditFree(iconTitle); 2730 2731 /* Update the Windows menus with the new name */ 2732 InvalidateWindowMenus(); 2733 } 2734 2735 /* 2736 ** Update the read-only state of the text area(s) in the window, and 2737 ** the ReadOnly toggle button in the File menu to agree with the state in 2738 ** the window data structure. 2739 */ 2740 void UpdateWindowReadOnly(WindowInfo *window) 2741 { 2742 int i, state; 2743 2744 if (!IsTopDocument(window)) 2745 return; 2746 2747 state = IS_ANY_LOCKED(window->lockReasons); 2748 XtVaSetValues(window->textArea, textNreadOnly, state, NULL); 2749 for (i=0; i<window->nPanes; i++) 2750 XtVaSetValues(window->textPanes[i], textNreadOnly, state, NULL); 2751 XmToggleButtonSetState(window->readOnlyItem, state, FALSE); 2752 XtSetSensitive(window->readOnlyItem, 2753 !IS_ANY_LOCKED_IGNORING_USER(window->lockReasons)); 2754 } 2755 2756 /* 2757 ** Find the start and end of a single line selection. Hides rectangular 2758 ** selection issues for older routines which use selections that won't 2759 ** span lines. 2760 */ 2761 int GetSimpleSelection(textBuffer *buf, int *left, int *right) 2762 { 2763 int selStart, selEnd, isRect, rectStart, rectEnd, lineStart; 2764 2765 /* get the character to match and its position from the selection, or 2766 the character before the insert point if nothing is selected. 2767 Give up if too many characters are selected */ 2768 if (!BufGetSelectionPos(buf, &selStart, &selEnd, &isRect, 2769 &rectStart, &rectEnd)) 2770 return False; 2771 if (isRect) { 2772 lineStart = BufStartOfLine(buf, selStart); 2773 selStart = BufCountForwardDispChars(buf, lineStart, rectStart); 2774 selEnd = BufCountForwardDispChars(buf, lineStart, rectEnd); 2775 } 2776 *left = selStart; 2777 *right = selEnd; 2778 return True; 2779 } 2780 2781 /* 2782 ** If the selection (or cursor position if there's no selection) is not 2783 ** fully shown, scroll to bring it in to view. Note that as written, 2784 ** this won't work well with multi-line selections. Modest re-write 2785 ** of the horizontal scrolling part would be quite easy to make it work 2786 ** well with rectangular selections. 2787 */ 2788 void MakeSelectionVisible(WindowInfo *window, Widget textPane) 2789 { 2790 int left, right, isRect, rectStart, rectEnd, horizOffset; 2791 int scrollOffset, leftX, rightX, y, rows, margin; 2792 int topLineNum, lastLineNum, rightLineNum, leftLineNum, linesToScroll; 2793 textDisp *textD = ((TextWidget)textPane)->text.textD; 2794 int topChar = TextFirstVisiblePos(textPane); 2795 int lastChar = TextLastVisiblePos(textPane); 2796 int targetLineNum; 2797 Dimension width; 2798 2799 /* find out where the selection is */ 2800 if (!BufGetSelectionPos(window->buffer, &left, &right, &isRect, 2801 &rectStart, &rectEnd)) { 2802 left = right = TextGetCursorPos(textPane); 2803 isRect = False; 2804 } 2805 2806 /* Check vertical positioning unless the selection is already shown or 2807 already covers the display. If the end of the selection is below 2808 bottom, scroll it in to view until the end selection is scrollOffset 2809 lines from the bottom of the display or the start of the selection 2810 scrollOffset lines from the top. Calculate a pleasing distance from the 2811 top or bottom of the window, to scroll the selection to (if scrolling is 2812 necessary), around 1/3 of the height of the window */ 2813 if (!((left >= topChar && right <= lastChar) || 2814 (left <= topChar && right >= lastChar))) { 2815 XtVaGetValues(textPane, textNrows, &rows, NULL); 2816 scrollOffset = rows/3; 2817 TextGetScroll(textPane, &topLineNum, &horizOffset); 2818 if (right > lastChar) { 2819 /* End of sel. is below bottom of screen */ 2820 leftLineNum = topLineNum + 2821 TextDCountLines(textD, topChar, left, False); 2822 targetLineNum = topLineNum + scrollOffset; 2823 if (leftLineNum >= targetLineNum) { 2824 /* Start of sel. is not between top & target */ 2825 linesToScroll = TextDCountLines(textD, lastChar, right, False) + 2826 scrollOffset; 2827 if (leftLineNum - linesToScroll < targetLineNum) 2828 linesToScroll = leftLineNum - targetLineNum; 2829 /* Scroll start of selection to the target line */ 2830 TextSetScroll(textPane, topLineNum+linesToScroll, horizOffset); 2831 } 2832 } else if (left < topChar) { 2833 /* Start of sel. is above top of screen */ 2834 lastLineNum = topLineNum + rows; 2835 rightLineNum = lastLineNum - 2836 TextDCountLines(textD, right, lastChar, False); 2837 targetLineNum = lastLineNum - scrollOffset; 2838 if (rightLineNum <= targetLineNum) { 2839 /* End of sel. is not between bottom & target */ 2840 linesToScroll = TextDCountLines(textD, left, topChar, False) + 2841 scrollOffset; 2842 if (rightLineNum + linesToScroll > targetLineNum) 2843 linesToScroll = targetLineNum - rightLineNum; 2844 /* Scroll end of selection to the target line */ 2845 TextSetScroll(textPane, topLineNum-linesToScroll, horizOffset); 2846 } 2847 } 2848 } 2849 2850 /* If either end of the selection off screen horizontally, try to bring it 2851 in view, by making sure both end-points are visible. Using only end 2852 points of a multi-line selection is not a great idea, and disaster for 2853 rectangular selections, so this part of the routine should be re-written 2854 if it is to be used much with either. Note also that this is a second 2855 scrolling operation, causing the display to jump twice. It's done after 2856 vertical scrolling to take advantage of TextPosToXY which requires it's 2857 reqested position to be vertically on screen) */ 2858 if ( TextPosToXY(textPane, left, &leftX, &y) && 2859 TextPosToXY(textPane, right, &rightX, &y) && leftX <= rightX) { 2860 TextGetScroll(textPane, &topLineNum, &horizOffset); 2861 XtVaGetValues(textPane, XmNwidth, &width, textNmarginWidth, &margin, 2862 NULL); 2863 if (leftX < margin + textD->lineNumLeft + textD->lineNumWidth) 2864 horizOffset -= 2865 margin + textD->lineNumLeft + textD->lineNumWidth - leftX; 2866 else if (rightX > width - margin) 2867 horizOffset += rightX - (width - margin); 2868 TextSetScroll(textPane, topLineNum, horizOffset); 2869 } 2870 2871 /* make sure that the statistics line is up to date */ 2872 UpdateStatsLine(window); 2873 } 2874 2875 static Widget createTextArea(Widget parent, WindowInfo *window, int rows, 2876 int cols, int emTabDist, char *delimiters, int wrapMargin, 2877 int lineNumCols) 2878 { 2879 Widget text, sw, hScrollBar, vScrollBar, frame; 2880 2881 /* Create a text widget inside of a scrolled window widget */ 2882 sw = XtVaCreateManagedWidget("scrolledW", xmScrolledWindowWidgetClass, 2883 parent, XmNpaneMaximum, SHRT_MAX, 2884 XmNpaneMinimum, PANE_MIN_HEIGHT, XmNhighlightThickness, 0, NULL); 2885 hScrollBar = XtVaCreateManagedWidget("textHorScrollBar", 2886 xmScrollBarWidgetClass, sw, XmNorientation, XmHORIZONTAL, 2887 XmNrepeatDelay, 10, NULL); 2888 vScrollBar = XtVaCreateManagedWidget("textVertScrollBar", 2889 xmScrollBarWidgetClass, sw, XmNorientation, XmVERTICAL, 2890 XmNrepeatDelay, 10, NULL); 2891 frame = XtVaCreateManagedWidget("textFrame", xmFrameWidgetClass, sw, 2892 XmNshadowType, XmSHADOW_IN, NULL); 2893 text = XtVaCreateManagedWidget("text", textWidgetClass, frame, 2894 textNbacklightCharTypes, window->backlightCharTypes, 2895 textNhighlightCursorLine, window->highlightCursorLine, 2896 textNindentRainbow, window->indentRainbow, 2897 textNansiColors, window->ansiColors, 2898 textNrows, rows, textNcolumns, cols, 2899 textNlineNumCols, lineNumCols, 2900 textNemulateTabs, emTabDist, 2901 textNXftFont, window->font, 2902 textNXftBoldFont, window->boldFont, 2903 textNXftItalicFont, window->italicFont, 2904 textNXftBoldItalicFont, window->boldItalicFont, 2905 textNhScrollBar, hScrollBar, textNvScrollBar, vScrollBar, 2906 textNreadOnly, IS_ANY_LOCKED(window->lockReasons), 2907 textNwordDelimiters, delimiters, 2908 textNwrapMargin, wrapMargin, 2909 textNautoIndent, window->indentStyle == AUTO_INDENT, 2910 textNsmartIndent, window->indentStyle == SMART_INDENT, 2911 textNautoWrap, window->wrapMode == NEWLINE_WRAP, 2912 textNcontinuousWrap, window->wrapMode == CONTINUOUS_WRAP, 2913 textNoverstrike, window->overstrike, 2914 textNhidePointer, (Boolean) GetPrefTypingHidesPointer(), 2915 textNcursorVPadding, GetVerticalAutoScroll(), 2916 NULL); 2917 2918 XtVaSetValues(sw, XmNworkWindow, frame, XmNhorizontalScrollBar, 2919 hScrollBar, XmNverticalScrollBar, vScrollBar, NULL); 2920 2921 /* add focus, drag, cursor tracking, and smart indent callbacks */ 2922 XtAddCallback(text, textNfocusCallback, (XtCallbackProc)focusCB, window); 2923 XtAddCallback(text, textNcursorMovementCallback, (XtCallbackProc)movedCB, 2924 window); 2925 XtAddCallback(text, textNdragStartCallback, (XtCallbackProc)dragStartCB, 2926 window); 2927 XtAddCallback(text, textNdragEndCallback, (XtCallbackProc)dragEndCB, 2928 window); 2929 XtAddCallback(text, textNsmartIndentCallback, SmartIndentCB, window); 2930 2931 /* legacy dnd support */ 2932 neditDropWidget(text); 2933 2934 /* This makes sure the text area initially has a the insert point shown 2935 ... (check if still true with the nedit text widget, probably not) */ 2936 XmAddTabGroup(containingPane(text)); 2937 2938 /* compensate for Motif delete/backspace problem */ 2939 RemapDeleteKey(text); 2940 2941 /* Augment translation table for right button popup menu */ 2942 AddBGMenuAction(text); 2943 2944 /* If absolute line numbers will be needed for display in the statistics 2945 line, tell the widget to maintain them (otherwise, it's a costly 2946 operation and performance will be better without it) */ 2947 TextDMaintainAbsLineNum(((TextWidget)text)->text.textD, window->showStats); 2948 2949 return text; 2950 } 2951 2952 static void movedCB(Widget w, WindowInfo *window, XtPointer callData) 2953 { 2954 TextWidget textWidget = (TextWidget) w; 2955 2956 if (window->ignoreModify) 2957 return; 2958 2959 /* update line and column nubers in statistics line */ 2960 UpdateStatsLine(window); 2961 2962 /* Check the character before the cursor for matchable characters */ 2963 FlashMatching(window, w); 2964 2965 /* Check for changes to read-only status and/or file modifications */ 2966 CheckForChangesToFile(window); 2967 2968 /* This callback is not only called for focussed panes, but for newly 2969 created panes as well. So make sure that the cursor is left alone 2970 for unfocussed panes. 2971 TextWidget have no state per se about focus, so we use the related 2972 ID for the blink procedure. */ 2973 if (0 != textWidget->text.cursorBlinkProcID) 2974 { 2975 /* Start blinking the caret again. */ 2976 ResetCursorBlink(textWidget, False); 2977 } 2978 } 2979 2980 static void modifiedCB(int pos, int nInserted, int nDeleted, int nRestyled, 2981 const char *deletedText, void *cbArg) 2982 { 2983 WindowInfo *window = (WindowInfo *)cbArg; 2984 int selected = window->buffer->primary.selected; 2985 2986 /* update the table of bookmarks */ 2987 if (!window->ignoreModify) { 2988 UpdateMarkTable(window, pos, nInserted, nDeleted); 2989 } 2990 2991 /* Check and dim/undim selection related menu items */ 2992 if ((window->wasSelected && !selected) || 2993 (!window->wasSelected && selected)) { 2994 window->wasSelected = selected; 2995 2996 /* do not refresh shell-level items (window, menu-bar etc) 2997 when motifying non-top document */ 2998 if (IsTopDocument(window)) { 2999 XtSetSensitive(window->printSelItem, selected); 3000 XtSetSensitive(window->cutItem, selected); 3001 XtSetSensitive(window->copyItem, selected); 3002 XtSetSensitive(window->delItem, selected); 3003 /* Note we don't change the selection for items like 3004 "Open Selected" and "Find Selected". That's because 3005 it works on selections in external applications. 3006 Desensitizing it if there's no NEdit selection 3007 disables this feature. */ 3008 XtSetSensitive(window->filterItem, selected); 3009 3010 DimSelectionDepUserMenuItems(window, selected); 3011 if (window->replaceDlog != NULL && XtIsManaged(window->replaceDlog)) 3012 { 3013 UpdateReplaceActionButtons(window); 3014 } 3015 } 3016 } 3017 3018 /* When the program needs to make a change to a text area without without 3019 recording it for undo or marking file as changed it sets ignoreModify */ 3020 if (window->ignoreModify || (nDeleted == 0 && nInserted == 0)) 3021 return; 3022 3023 /* Make sure line number display is sufficient for new data */ 3024 updateLineNumDisp(window); 3025 3026 /* Save information for undoing this operation (this call also counts 3027 characters and editing operations for triggering autosave */ 3028 SaveUndoInformation(window, pos, nInserted, nDeleted, deletedText); 3029 3030 /* Trigger automatic backup if operation or character limits reached */ 3031 if (window->autoSave && 3032 (window->autoSaveCharCount > AUTOSAVE_CHAR_LIMIT || 3033 window->autoSaveOpCount > AUTOSAVE_OP_LIMIT)) { 3034 WriteBackupFile(window); 3035 window->autoSaveCharCount = 0; 3036 window->autoSaveOpCount = 0; 3037 } 3038 3039 /* Indicate that the window has now been modified */ 3040 SetWindowModified(window, TRUE); 3041 3042 /* Update # of bytes, and line and col statistics */ 3043 if(!window->undo_batch_begin) { 3044 UpdateStatsLine(window); 3045 } 3046 3047 /* Check if external changes have been made to file and warn user */ 3048 CheckForChangesToFile(window); 3049 3050 /* count modify operations per modification batch 3051 * this is only relevant if window->undo_batch_begin != NULL */ 3052 window->undo_batch_count++; 3053 } 3054 3055 static void beginModifyCB(void *cbArg) { 3056 WindowInfo *window = cbArg; 3057 window->undo_batch_begin = window->undo; 3058 window->undo_batch_count = 0; 3059 } 3060 3061 static void endModifyCB(void *cbArg) { 3062 WindowInfo *window = cbArg; 3063 if(window->undo_batch_begin && window->undo_batch_count > 1) { 3064 window->undo->numOp = window->undo_batch_count; 3065 } 3066 window->undo_batch_begin = NULL; 3067 window->undo_batch_count = 0; 3068 UpdateStatsLine(window); 3069 } 3070 3071 static void focusCB(Widget w, WindowInfo *window, XtPointer callData) 3072 { 3073 /* record which window pane last had the keyboard focus */ 3074 window->lastFocus = w; 3075 3076 /* update line number statistic to reflect current focus pane */ 3077 UpdateStatsLine(window); 3078 3079 /* finish off the current incremental search */ 3080 EndISearch(window); 3081 3082 /* Check for changes to read-only status and/or file modifications */ 3083 CheckForChangesToFile(window); 3084 } 3085 3086 static void dragStartCB(Widget w, WindowInfo *window, XtPointer callData) 3087 { 3088 /* don't record all of the intermediate drag steps for undo */ 3089 window->ignoreModify = True; 3090 } 3091 3092 static void dragEndCB(Widget w, WindowInfo *window, dragEndCBStruct *callData) 3093 { 3094 /* restore recording of undo information */ 3095 window->ignoreModify = False; 3096 3097 /* Do nothing if drag operation was canceled */ 3098 if (callData->nCharsInserted == 0) 3099 return; 3100 3101 /* Save information for undoing this operation not saved while 3102 undo recording was off */ 3103 modifiedCB(callData->startPos, callData->nCharsInserted, 3104 callData->nCharsDeleted, 0, callData->deletedText, window); 3105 } 3106 3107 static void closeCB(Widget w, WindowInfo *window, XtPointer callData) 3108 { 3109 window = WidgetToWindow(w); 3110 if (!WindowCanBeClosed(window)) { 3111 return; 3112 } 3113 3114 CloseDocumentWindow(w, window, callData); 3115 } 3116 3117 #ifndef NO_SESSION_RESTART 3118 static void saveYourselfCB(Widget w, Widget appShell, XtPointer callData) 3119 { 3120 WindowInfo *win, *topWin, **revWindowList; 3121 char geometry[MAX_GEOM_STRING_LEN]; 3122 int argc = 0, maxArgc, nWindows, i; 3123 char **argv; 3124 int wasIconic = False; 3125 int n, nItems; 3126 WidgetList children; 3127 3128 /* Allocate memory for an argument list and for a reversed list of 3129 windows. The window list is reversed for IRIX 4DWM and any other 3130 window/session manager combination which uses window creation 3131 order for re-associating stored geometry information with 3132 new windows created by a restored application */ 3133 maxArgc = 4; /* nedit -server -svrname name */ 3134 nWindows = 0; 3135 for (win=WindowList; win!=NULL; win=win->next) { 3136 maxArgc += 5; /* -iconic -group -geometry WxH+x+y filename */ 3137 nWindows++; 3138 } 3139 argv = (char **)NEditMalloc(maxArgc*sizeof(char *)); 3140 revWindowList = (WindowInfo **)NEditMalloc(sizeof(WindowInfo *)*nWindows); 3141 for (win=WindowList, i=nWindows-1; win!=NULL; win=win->next, i--) 3142 revWindowList[i] = win; 3143 3144 /* Create command line arguments for restoring each window in the list */ 3145 argv[argc++] = NEditStrdup(ArgV0); 3146 if (IsServer) { 3147 argv[argc++] = NEditStrdup("-server"); 3148 if (GetPrefServerName()[0] != '\0') { 3149 argv[argc++] = NEditStrdup("-svrname"); 3150 argv[argc++] = NEditStrdup(GetPrefServerName()); 3151 } 3152 } 3153 3154 /* editor windows are popup-shell children of top-level appShell */ 3155 XtVaGetValues(appShell, XmNchildren, &children, 3156 XmNnumChildren, &nItems, NULL); 3157 3158 for (n=nItems-1; n>=0; n--) { 3159 WidgetList tabs; 3160 int tabCount; 3161 3162 if (strcmp(XtName(children[n]), "textShell") || 3163 ((topWin = WidgetToWindow(children[n])) == NULL)) 3164 continue; /* skip non-editor windows */ 3165 3166 /* create a group for each window */ 3167 getGeometryString(topWin, geometry); 3168 argv[argc++] = NEditStrdup("-group"); 3169 argv[argc++] = NEditStrdup("-geometry"); 3170 argv[argc++] = NEditStrdup(geometry); 3171 if (IsIconic(topWin)) { 3172 argv[argc++] = NEditStrdup("-iconic"); 3173 wasIconic = True; 3174 } else if (wasIconic) { 3175 argv[argc++] = NEditStrdup("-noiconic"); 3176 wasIconic = False; 3177 } 3178 3179 /* add filename of each tab in window... */ 3180 XtVaGetValues(topWin->tabBar, XmNtabWidgetList, &tabs, 3181 XmNtabCount, &tabCount, NULL); 3182 3183 for (i=0; i< tabCount; i++) { 3184 win = TabToWindow(tabs[i]); 3185 if (win->filenameSet) { 3186 /* add filename */ 3187 argv[argc] = (char*)NEditMalloc(strlen(win->path) + 3188 strlen(win->filename) + 1); 3189 sprintf(argv[argc++], "%s%s", win->path, win->filename); 3190 } 3191 } 3192 } 3193 3194 NEditFree(revWindowList); 3195 3196 /* Set the window's WM_COMMAND property to the created command line */ 3197 XSetCommand(TheDisplay, XtWindow(appShell), argv, argc); 3198 for (i=0; i<argc; i++) 3199 NEditFree(argv[i]); 3200 NEditFree(argv); 3201 } 3202 3203 void AttachSessionMgrHandler(Widget appShell) 3204 { 3205 static Atom wmpAtom, syAtom = 0; 3206 3207 /* Add wm protocol callback for making nedit restartable by session 3208 managers. Doesn't yet handle multiple-desktops or iconifying right. */ 3209 if (syAtom == 0) { 3210 wmpAtom = XmInternAtom(TheDisplay, "WM_PROTOCOLS", FALSE); 3211 syAtom = XmInternAtom(TheDisplay, "WM_SAVE_YOURSELF", FALSE); 3212 } 3213 XmAddProtocolCallback(appShell, wmpAtom, syAtom, 3214 (XtCallbackProc)saveYourselfCB, (XtPointer)appShell); 3215 } 3216 #endif /* NO_SESSION_RESTART */ 3217 3218 /* 3219 ** Returns true if window is iconic (as determined by the WM_STATE property 3220 ** on the shell window. I think this is the most reliable way to tell, 3221 ** but if someone has a better idea please send me a note). 3222 */ 3223 int IsIconic(WindowInfo *window) 3224 { 3225 unsigned long *property = NULL; 3226 unsigned long nItems; 3227 unsigned long leftover; 3228 static Atom wmStateAtom = 0; 3229 Atom actualType; 3230 int actualFormat; 3231 int result; 3232 3233 if (wmStateAtom == 0) 3234 wmStateAtom = XInternAtom(XtDisplay(window->shell), "WM_STATE", False); 3235 if (XGetWindowProperty(XtDisplay(window->shell), XtWindow(window->shell), 3236 wmStateAtom, 0L, 1L, False, wmStateAtom, &actualType, &actualFormat, 3237 &nItems, &leftover, (unsigned char **)&property) != Success || 3238 nItems != 1 || property == NULL) 3239 return FALSE; 3240 result = *property == IconicState; 3241 NEditFree(property); 3242 return result; 3243 } 3244 3245 /* 3246 ** Add a window to the the window list. 3247 */ 3248 static void addToWindowList(WindowInfo *window) 3249 { 3250 WindowInfo *temp; 3251 3252 temp = WindowList; 3253 WindowList = window; 3254 window->next = temp; 3255 } 3256 3257 /* 3258 ** Remove a window from the list of windows 3259 */ 3260 static void removeFromWindowList(WindowInfo *window) 3261 { 3262 WindowInfo *temp; 3263 3264 if (WindowList == window) 3265 WindowList = window->next; 3266 else { 3267 for (temp = WindowList; temp != NULL; temp = temp->next) { 3268 if (temp->next == window) { 3269 temp->next = window->next; 3270 break; 3271 } 3272 } 3273 } 3274 } 3275 3276 /* 3277 ** Set the new gutter width in the window. Sadly, the only way to do this is 3278 ** to set it on every single document, so we have to iterate over them. 3279 ** 3280 ** (Iteration taken from NDocuments(); is there a better way to do it?) 3281 */ 3282 static int updateGutterWidth(WindowInfo* window) 3283 { 3284 WindowInfo* document; 3285 int reqCols = MIN_LINE_NUM_COLS; 3286 int newColsDiff = 0; 3287 int maxCols = 0; 3288 3289 for (document = WindowList; NULL != document; document = document->next) { 3290 if (document->shell == window->shell) { 3291 /* We found ourselves a document from this window. */ 3292 int lineNumCols, tmpReqCols; 3293 textDisp *textD = ((TextWidget) document->textArea)->text.textD; 3294 3295 XtVaGetValues(document->textArea, 3296 textNlineNumCols, &lineNumCols, 3297 NULL); 3298 3299 /* Is the width of the line number area sufficient to display all the 3300 line numbers in the file? If not, expand line number field, and the 3301 window width. */ 3302 3303 if (lineNumCols > maxCols) { 3304 maxCols = lineNumCols; 3305 } 3306 3307 tmpReqCols = textD->nBufferLines < 1 3308 ? 1 3309 : (int) log10((double) textD->nBufferLines + 1) + 1; 3310 3311 if (tmpReqCols > reqCols) { 3312 reqCols = tmpReqCols; 3313 } 3314 } 3315 } 3316 3317 if (reqCols != maxCols) { 3318 //XFontStruct *fs; 3319 NFont *fs; 3320 Dimension windowWidth; 3321 short fontWidth; 3322 3323 newColsDiff = reqCols - maxCols; 3324 3325 XtVaGetValues(window->textArea, textNXftFont, &fs, NULL); 3326 fontWidth = fs->maxWidth; 3327 3328 XtVaGetValues(window->shell, XmNwidth, &windowWidth, NULL); 3329 XtVaSetValues(window->shell, 3330 XmNwidth, (Dimension) windowWidth + (newColsDiff * fontWidth), 3331 NULL); 3332 3333 UpdateWMSizeHints(window); 3334 } 3335 3336 for (document = WindowList; NULL != document; document = document->next) { 3337 if (document->shell == window->shell) { 3338 Widget text; 3339 int i; 3340 int lineNumCols; 3341 3342 XtVaGetValues(document->textArea, 3343 textNlineNumCols, &lineNumCols, NULL); 3344 3345 if (lineNumCols == reqCols) { 3346 continue; 3347 } 3348 3349 /* Update all panes of this document. */ 3350 for (i = 0; i <= document->nPanes; i++) { 3351 text = 0==i ? document->textArea : document->textPanes[i-1]; 3352 XtVaSetValues(text, textNlineNumCols, reqCols, NULL); 3353 } 3354 } 3355 } 3356 3357 return reqCols; 3358 } 3359 3360 /* 3361 ** If necessary, enlarges the window and line number display area to make 3362 ** room for numbers. 3363 */ 3364 static int updateLineNumDisp(WindowInfo* window) 3365 { 3366 if (!window->showLineNumbers) { 3367 return 0; 3368 } 3369 3370 /* Decide how wide the line number field has to be to display all 3371 possible line numbers */ 3372 return updateGutterWidth(window); 3373 } 3374 3375 /* 3376 ** Update the optional statistics line. 3377 */ 3378 void UpdateStatsLine(WindowInfo *window) 3379 { 3380 int line, pos, colNum; 3381 int byteLength; 3382 long charCount = 0; 3383 long offset = 0; 3384 unsigned char current; 3385 3386 char * selection; 3387 char *string, *format, slinecol[42]; 3388 Widget statW = window->statsLine; 3389 XmString xmslinecol; 3390 #ifdef SGI_CUSTOM 3391 char *sleft, *smid, *sright; 3392 #endif 3393 3394 if (!IsTopDocument(window)) 3395 return; 3396 3397 /* This routine is called for each character typed, so its performance 3398 affects overall editor perfomance. Only update if the line is on. */ 3399 if (!window->showStats) 3400 return; 3401 3402 /* Compose the string to display. If line # isn't available, leave it off */ 3403 pos = TextGetCursorPos(window->lastFocus); 3404 string = (char*)NEditMalloc(strlen(window->filename) + strlen(window->path) + 45); 3405 format = window->fileFormat == DOS_FILE_FORMAT ? " DOS" : 3406 (window->fileFormat == MAC_FILE_FORMAT ? " Mac" : ""); 3407 int nCursors = TextNumCursors(window->lastFocus); 3408 if (!TextPosToLineAndCol(window->lastFocus, pos, &line, &colNum)) { 3409 sprintf(string, "%s%s%s %d bytes", window->path, window->filename, 3410 format, window->buffer->length); 3411 if(nCursors == 1) { 3412 snprintf(slinecol, 42, "S: --- L: --- C: ---"); 3413 } else { 3414 snprintf(slinecol, 42, "%d cursors", nCursors); 3415 } 3416 } else { 3417 if(nCursors == 1) { 3418 selection = BufGetSelectionText(window->buffer); 3419 if (selection != NULL) { 3420 byteLength = strlen(selection); 3421 while (offset < byteLength) { 3422 current = selection[offset]; 3423 3424 if(current >= 240) { 3425 offset += 4; 3426 } else if(current >= 224) { 3427 offset += 3; 3428 } else if(current > 192) { 3429 offset += 2; 3430 } else { 3431 offset ++; 3432 } 3433 charCount++; 3434 } 3435 3436 snprintf(slinecol, 42, "S: %ld L: %d C: %d", charCount, line, colNum); 3437 } else { 3438 snprintf(slinecol, 42, "S: --- L: %d C: %d", line, colNum); 3439 } 3440 NEditFree(selection); 3441 } else { 3442 snprintf(slinecol, 42, "%d cursors", nCursors); 3443 } 3444 if (window->showLineNumbers) 3445 sprintf(string, "%s%s%s byte %d of %d", window->path, 3446 window->filename, format, pos, 3447 window->buffer->length); 3448 else 3449 sprintf(string, "%s%s%s %d bytes", window->path, 3450 window->filename, format, window->buffer->length); 3451 } 3452 3453 /* Update the line/column number */ 3454 xmslinecol = XmStringCreateSimple(slinecol); 3455 XtVaSetValues( window->statsLineColNo, 3456 XmNlabelString, xmslinecol, NULL ); 3457 XmStringFree(xmslinecol); 3458 3459 /* Don't clobber the line if there's a special message being displayed */ 3460 if (!window->modeMessageDisplayed) { 3461 /* Change the text in the stats line */ 3462 #ifdef SGI_CUSTOM 3463 /* don't show full pathname, just dir and filename (+ byte info) */ 3464 smid = strchr(string, '/'); 3465 if ( smid != NULL ) { 3466 sleft = smid; 3467 sright = strrchr(string, '/'); 3468 while (strcmp(smid, sright)) { 3469 sleft = smid; 3470 smid = strchr(sleft + 1, '/'); 3471 } 3472 XmTextReplace(statW, 0, XmTextGetLastPosition(statW), sleft + 1); 3473 } else 3474 XmTextReplace(statW, 0, XmTextGetLastPosition(statW), string); 3475 #else 3476 XmTextReplace(statW, 0, XmTextGetLastPosition(statW), string); 3477 #endif 3478 } 3479 NEditFree(string); 3480 3481 /* Update the line/col display */ 3482 xmslinecol = XmStringCreateSimple(slinecol); 3483 XtVaSetValues(window->statsLineColNo, 3484 XmNlabelString, xmslinecol, NULL); 3485 XmStringFree(xmslinecol); 3486 } 3487 3488 static Boolean currentlyBusy = False; 3489 static long busyStartTime = 0; 3490 static Boolean modeMessageSet = False; 3491 3492 /* 3493 * Auxiliary function for measuring elapsed time during busy waits. 3494 */ 3495 static long getRelTimeInTenthsOfSeconds() 3496 { 3497 #ifdef __unix__ 3498 struct timeval current; 3499 gettimeofday(&current, NULL); 3500 return (current.tv_sec*10 + current.tv_usec/100000) & 0xFFFFFFFL; 3501 #else 3502 time_t current; 3503 time(&current); 3504 return (current*10) & 0xFFFFFFFL; 3505 #endif 3506 } 3507 3508 void AllWindowsBusy(const char *message) 3509 { 3510 WindowInfo *w; 3511 3512 if (!currentlyBusy) 3513 { 3514 busyStartTime = getRelTimeInTenthsOfSeconds(); 3515 modeMessageSet = False; 3516 3517 for (w=WindowList; w!=NULL; w=w->next) 3518 { 3519 /* We don't the display message here yet, but defer it for 3520 a while. If the wait is short, we don't want 3521 to have it flash on and off the screen. However, 3522 we can't use a time since in generally we are in 3523 a tight loop and only processing exposure events, so it's 3524 up to the caller to make sure that this routine is called 3525 at regular intervals. 3526 */ 3527 BeginWait(w->shell); 3528 } 3529 } else if (!modeMessageSet && message && 3530 getRelTimeInTenthsOfSeconds() - busyStartTime > 10) { 3531 /* Show the mode message when we've been busy for more than a second */ 3532 for (w=WindowList; w!=NULL; w=w->next) { 3533 SetModeMessage(w, message); 3534 } 3535 modeMessageSet = True; 3536 } 3537 BusyWait(WindowList->shell); 3538 3539 currentlyBusy = True; 3540 } 3541 3542 void AllWindowsUnbusy(void) 3543 { 3544 WindowInfo *w; 3545 3546 for (w=WindowList; w!=NULL; w=w->next) 3547 { 3548 ClearModeMessage(w); 3549 EndWait(w->shell); 3550 } 3551 3552 currentlyBusy = False; 3553 modeMessageSet = False; 3554 busyStartTime = 0; 3555 } 3556 3557 /* 3558 ** Paned windows are impossible to adjust after they are created, which makes 3559 ** them nearly useless for NEdit (or any application which needs to dynamically 3560 ** adjust the panes) unless you tweek some private data to overwrite the 3561 ** desired and minimum pane heights which were set at creation time. These 3562 ** will probably break in a future release of Motif because of dependence on 3563 ** private data. 3564 */ 3565 static void setPaneDesiredHeight(Widget w, int height) 3566 { 3567 ((XmPanedWindowConstraintPtr)w->core.constraints)->panedw.dheight = height; 3568 } 3569 static void setPaneMinHeight(Widget w, int min) 3570 { 3571 ((XmPanedWindowConstraintPtr)w->core.constraints)->panedw.min = min; 3572 } 3573 3574 /* 3575 ** Update the window manager's size hints. These tell it the increments in 3576 ** which it is allowed to resize the window. While this isn't particularly 3577 ** important for NEdit (since it can tolerate any window size), setting these 3578 ** hints also makes the resize indicator show the window size in characters 3579 ** rather than pixels, which is very helpful to users. 3580 */ 3581 void UpdateWMSizeHints(WindowInfo *window) 3582 { 3583 Dimension shellWidth, shellHeight, textHeight, hScrollBarHeight; 3584 int marginHeight, marginWidth, totalHeight, nCols, nRows; 3585 NFont *fs; 3586 XftFont *font; 3587 int i, baseWidth, baseHeight, fontHeight, fontWidth; 3588 Widget hScrollBar; 3589 textDisp *textD = ((TextWidget)window->textArea)->text.textD; 3590 3591 /* Find the dimensions of a single character of the text font */ 3592 XtVaGetValues(window->textArea, textNXftFont, &fs, NULL); 3593 font = FontDefault(fs); 3594 fontHeight = textD->ascent + textD->descent; 3595 fontWidth = fs->maxWidth; //font->max_advance_width; 3596 3597 /* Find the base (non-expandable) width and height of the editor window. 3598 3599 FIXME: 3600 To workaround the shrinking-window bug on some WM such as Metacity, 3601 which caused the window to shrink as we switch between documents 3602 using different font sizes on the documents in the same window, the 3603 base width, and similarly the base height, is ajusted such that: 3604 shellWidth = baseWidth + cols * textWidth 3605 There are two issues with this workaround: 3606 1. the right most characters may appear partially obsure 3607 2. the Col x Row info reported by the WM will be based on the fully 3608 display text. 3609 */ 3610 XtVaGetValues(window->textArea, XmNheight, &textHeight, 3611 textNmarginHeight, &marginHeight, textNmarginWidth, &marginWidth, 3612 NULL); 3613 totalHeight = textHeight - 2*marginHeight; 3614 for (i=0; i<window->nPanes; i++) { 3615 XtVaGetValues(window->textPanes[i], XmNheight, &textHeight, 3616 textNhScrollBar, &hScrollBar, NULL); 3617 totalHeight += textHeight - 2*marginHeight; 3618 if (!XtIsManaged(hScrollBar)) { 3619 XtVaGetValues(hScrollBar, XmNheight, &hScrollBarHeight, NULL); 3620 totalHeight -= hScrollBarHeight; 3621 } 3622 } 3623 3624 XtVaGetValues(window->shell, XmNwidth, &shellWidth, 3625 XmNheight, &shellHeight, NULL); 3626 nCols = textD->width / fontWidth; 3627 nRows = totalHeight / fontHeight; 3628 baseWidth = shellWidth - nCols * fontWidth; 3629 baseHeight = shellHeight - nRows * fontHeight; 3630 3631 /* Set the size hints in the shell widget */ 3632 XtVaSetValues(window->shell, XmNwidthInc, fs->maxWidth, 3633 XmNheightInc, fontHeight, 3634 XmNbaseWidth, baseWidth, XmNbaseHeight, baseHeight, 3635 XmNminWidth, baseWidth + fontWidth, 3636 XmNminHeight, baseHeight + (1+window->nPanes) * fontHeight, NULL); 3637 3638 /* Motif will keep placing this on the shell every time we change it, 3639 so it needs to be undone every single time. This only seems to 3640 happen on mult-head dispalys on screens 1 and higher. */ 3641 3642 RemovePPositionHint(window->shell); 3643 } 3644 3645 /* 3646 ** Update the minimum allowable height for a split pane after a change 3647 ** to font or margin height. 3648 */ 3649 void UpdateMinPaneHeights(WindowInfo *window) 3650 { 3651 textDisp *textD = ((TextWidget)window->textArea)->text.textD; 3652 Dimension hsbHeight, swMarginHeight,frameShadowHeight; 3653 int i, marginHeight, minPaneHeight; 3654 Widget hScrollBar; 3655 3656 /* find the minimum allowable size for a pane */ 3657 XtVaGetValues(window->textArea, textNhScrollBar, &hScrollBar, NULL); 3658 XtVaGetValues(containingPane(window->textArea), 3659 XmNscrolledWindowMarginHeight, &swMarginHeight, NULL); 3660 XtVaGetValues(XtParent(window->textArea), 3661 XmNshadowThickness, &frameShadowHeight, NULL); 3662 XtVaGetValues(window->textArea, textNmarginHeight, &marginHeight, NULL); 3663 XtVaGetValues(hScrollBar, XmNheight, &hsbHeight, NULL); 3664 minPaneHeight = textD->ascent + textD->descent + marginHeight*2 + 3665 swMarginHeight*2 + hsbHeight + 2*frameShadowHeight; 3666 3667 /* Set it in all of the widgets in the window */ 3668 setPaneMinHeight(containingPane(window->textArea), minPaneHeight); 3669 for (i=0; i<window->nPanes; i++) 3670 setPaneMinHeight(containingPane(window->textPanes[i]), 3671 minPaneHeight); 3672 } 3673 3674 /* Add an icon to an applicaction shell widget. addWindowIcon adds a large 3675 ** (primary window) icon, AddSmallIcon adds a small (secondary window) icon. 3676 ** 3677 ** Note: I would prefer that these were not hardwired, but yhere is something 3678 ** weird about the XmNiconPixmap resource that prevents it from being set 3679 ** from the defaults in the application resource database. 3680 */ 3681 static void addWindowIcon(Widget shell) 3682 { 3683 static Pixmap iconPixmap = 0, maskPixmap = 0; 3684 3685 if (iconPixmap == 0) { 3686 iconPixmap = XCreateBitmapFromData(TheDisplay, 3687 RootWindowOfScreen(XtScreen(shell)), (char *)iconBits, 3688 iconBitmapWidth, iconBitmapHeight); 3689 maskPixmap = XCreateBitmapFromData(TheDisplay, 3690 RootWindowOfScreen(XtScreen(shell)), (char *)maskBits, 3691 iconBitmapWidth, iconBitmapHeight); 3692 } 3693 XtVaSetValues(shell, XmNiconPixmap, iconPixmap, XmNiconMask, maskPixmap, 3694 NULL); 3695 } 3696 void AddSmallIcon(Widget shell) 3697 { 3698 static Pixmap iconPixmap = 0, maskPixmap = 0; 3699 3700 if (iconPixmap == 0) { 3701 iconPixmap = XCreateBitmapFromData(TheDisplay, 3702 RootWindowOfScreen(XtScreen(shell)), (char *)n_bits, 3703 n_width, n_height); 3704 maskPixmap = XCreateBitmapFromData(TheDisplay, 3705 RootWindowOfScreen(XtScreen(shell)), (char *)n_mask, 3706 n_width, n_height); 3707 } 3708 XtVaSetValues(shell, XmNiconPixmap, iconPixmap, 3709 XmNiconMask, maskPixmap, NULL); 3710 } 3711 3712 /* 3713 ** Create pixmap per the widget's color depth setting. 3714 ** 3715 ** This fixes a BadMatch (X_CopyArea) error due to mismatching of 3716 ** color depth between the bitmap (depth of 1) and the screen, 3717 ** specifically on when linked to LessTif v1.2 (release 0.93.18 3718 ** & 0.93.94 tested). LessTif v2.x showed no such problem. 3719 */ 3720 static Pixmap createBitmapWithDepth(Widget w, char *data, unsigned int width, 3721 unsigned int height) 3722 { 3723 Pixmap pixmap; 3724 Pixel fg, bg; 3725 int depth; 3726 3727 XtVaGetValues (w, XmNforeground, &fg, XmNbackground, &bg, 3728 XmNdepth, &depth, NULL); 3729 pixmap = XCreatePixmapFromBitmapData(XtDisplay(w), 3730 RootWindowOfScreen(XtScreen(w)), (char *)data, 3731 width, height, fg, bg, depth); 3732 3733 return pixmap; 3734 } 3735 3736 /* 3737 ** Save the position and size of a window as an X standard geometry string. 3738 ** A string of at least MAX_GEOMETRY_STRING_LEN characters should be 3739 ** provided in the argument "geomString" to receive the result. 3740 */ 3741 static void getGeometryString(WindowInfo *window, char *geomString) 3742 { 3743 int x, y, fontWidth, fontHeight, baseWidth, baseHeight; 3744 unsigned int width, height, dummyW, dummyH, bw, depth, nChild; 3745 Window parent, root, *child, w = XtWindow(window->shell); 3746 Display *dpy = XtDisplay(window->shell); 3747 3748 /* Find the width and height from the window of the shell */ 3749 XGetGeometry(dpy, w, &root, &x, &y, &width, &height, &bw, &depth); 3750 3751 /* Find the top left corner (x and y) of the window decorations. (This 3752 is what's required in the geometry string to restore the window to it's 3753 original position, since the window manager re-parents the window to 3754 add it's title bar and menus, and moves the requested window down and 3755 to the left.) The position is found by traversing the window hier- 3756 archy back to the window to the last parent before the root window */ 3757 for(;;) { 3758 XQueryTree(dpy, w, &root, &parent, &child, &nChild); 3759 XFree((char*)child); 3760 if (parent == root) 3761 break; 3762 w = parent; 3763 } 3764 XGetGeometry(dpy, w, &root, &x, &y, &dummyW, &dummyH, &bw, &depth); 3765 3766 /* Use window manager size hints (set by UpdateWMSizeHints) to 3767 translate the width and height into characters, as opposed to pixels */ 3768 XtVaGetValues(window->shell, XmNwidthInc, &fontWidth, 3769 XmNheightInc, &fontHeight, XmNbaseWidth, &baseWidth, 3770 XmNbaseHeight, &baseHeight, NULL); 3771 width = (width-baseWidth) / fontWidth; 3772 height = (height-baseHeight) / fontHeight; 3773 3774 /* Write the string */ 3775 CreateGeometryString(geomString, x, y, width, height, 3776 XValue | YValue | WidthValue | HeightValue); 3777 } 3778 3779 /* 3780 ** Xt timer procedure for updating size hints. The new sizes of objects in 3781 ** the window are not ready immediately after adding or removing panes. This 3782 ** is a timer routine to be invoked with a timeout of 0 to give the event 3783 ** loop a chance to finish processing the size changes before reading them 3784 ** out for setting the window manager size hints. 3785 */ 3786 static void wmSizeUpdateProc(XtPointer clientData, XtIntervalId *id) 3787 { 3788 UpdateWMSizeHints((WindowInfo *)clientData); 3789 } 3790 3791 #ifdef ROWCOLPATCH 3792 /* 3793 ** There is a bad memory reference in the delete_child method of the 3794 ** RowColumn widget in some Motif versions (so far, just Solaris with Motif 3795 ** 1.2.3) which appears durring the phase 2 destroy of the widget. This 3796 ** patch replaces the method with a call to the Composite widget's 3797 ** delete_child method. The composite delete_child method handles part, 3798 ** but not all of what would have been done by the original method, meaning 3799 ** that this is dangerous and should be used sparingly. Note that 3800 ** patchRowCol is called only in CloseWindow, before the widget is about to 3801 ** be destroyed, and only on systems where the bug has been observed 3802 */ 3803 static void patchRowCol(Widget w) 3804 { 3805 ((XmRowColumnClassRec *)XtClass(w))->composite_class.delete_child = 3806 patchedRemoveChild; 3807 } 3808 static void patchedRemoveChild(Widget child) 3809 { 3810 /* Call composite class method instead of broken row col delete_child 3811 method */ 3812 (*((CompositeWidgetClass)compositeWidgetClass)->composite_class. 3813 delete_child) (child); 3814 } 3815 #endif /* ROWCOLPATCH */ 3816 3817 void SetHighlightCursorLine(WindowInfo *window, Boolean state) 3818 { 3819 window->highlightCursorLine = state; 3820 3821 XtVaSetValues(window->textArea, 3822 textNhighlightCursorLine, state, NULL); 3823 for (int i=0; i<window->nPanes; i++) { 3824 XtVaSetValues(window->textPanes[i], textNhighlightCursorLine, state, NULL); 3825 } 3826 } 3827 3828 void SetIndentRainbow(WindowInfo *window, Boolean state) 3829 { 3830 window->indentRainbow = state; 3831 3832 XtVaSetValues(window->textArea, 3833 textNindentRainbow, state, NULL); 3834 for (int i=0; i<window->nPanes; i++) { 3835 XtVaSetValues(window->textPanes[i], textNindentRainbow, state, NULL); 3836 } 3837 } 3838 3839 void SetAnsiColors(WindowInfo *window, Boolean state) 3840 { 3841 window->ansiColors = state; 3842 3843 XtVaSetValues(window->textArea, 3844 textNansiColors, state, NULL); 3845 for (int i=0; i<window->nPanes; i++) { 3846 XtVaSetValues(window->textPanes[i], textNansiColors, state, NULL); 3847 } 3848 } 3849 3850 /* 3851 ** Set the backlight character class string 3852 */ 3853 void SetBacklightChars(WindowInfo *window, char *applyBacklightTypes) 3854 { 3855 int i; 3856 int is_applied = XmToggleButtonGetState(window->backlightCharsItem) ? 1 : 0; 3857 int do_apply = applyBacklightTypes ? 1 : 0; 3858 3859 window->backlightChars = do_apply; 3860 3861 NEditFree(window->backlightCharTypes); 3862 if (window->backlightChars && 3863 (window->backlightCharTypes = (char*)NEditMalloc(strlen(applyBacklightTypes)+1))) 3864 strcpy(window->backlightCharTypes, applyBacklightTypes); 3865 else 3866 window->backlightCharTypes = NULL; 3867 3868 XtVaSetValues(window->textArea, 3869 textNbacklightCharTypes, window->backlightCharTypes, NULL); 3870 for (i=0; i<window->nPanes; i++) 3871 XtVaSetValues(window->textPanes[i], 3872 textNbacklightCharTypes, window->backlightCharTypes, NULL); 3873 if (is_applied != do_apply) 3874 SetToggleButtonState(window, window->backlightCharsItem, do_apply, False); 3875 } 3876 3877 /* 3878 ** perform generic management on the children (toolbars) of toolBarsForm, 3879 ** a.k.a. statsForm, by setting the form attachment of the managed child 3880 ** widgets per their position/order. 3881 ** 3882 ** You can optionally create separator after a toolbar widget with it's 3883 ** widget name set to "TOOLBAR_SEP", which will appear below the toolbar 3884 ** widget. These seperators will then be managed automatically by this 3885 ** routine along with the toolbars they 'attached' to. 3886 ** 3887 ** It also takes care of the attachment offset settings of the child 3888 ** widgets to keep the border lines of the parent form displayed, so 3889 ** you don't have set them before hand. 3890 ** 3891 ** Note: XtManage/XtUnmange the target child (toolbar) before calling this 3892 ** function. 3893 ** 3894 ** Returns the last toolbar widget managed. 3895 ** 3896 */ 3897 static Widget manageToolBars(Widget toolBarsForm) 3898 { 3899 Widget topWidget = NULL; 3900 WidgetList children; 3901 int n, nItems=0; 3902 3903 XtVaGetValues(toolBarsForm, XmNchildren, &children, 3904 XmNnumChildren, &nItems, NULL); 3905 3906 for (n=0; n<nItems; n++) { 3907 Widget tbar = children[n]; 3908 3909 if (XtIsManaged(tbar)) { 3910 if (topWidget) { 3911 XtVaSetValues(tbar, XmNtopAttachment, XmATTACH_WIDGET, 3912 XmNtopWidget, topWidget, 3913 XmNbottomAttachment, XmATTACH_NONE, 3914 XmNleftOffset, STAT_SHADOW_THICKNESS, 3915 XmNrightOffset, STAT_SHADOW_THICKNESS, 3916 NULL); 3917 } 3918 else { 3919 /* the very first toolbar on top */ 3920 XtVaSetValues(tbar, XmNtopAttachment, XmATTACH_FORM, 3921 XmNbottomAttachment, XmATTACH_NONE, 3922 XmNleftOffset, STAT_SHADOW_THICKNESS, 3923 XmNtopOffset, STAT_SHADOW_THICKNESS, 3924 XmNrightOffset, STAT_SHADOW_THICKNESS, 3925 NULL); 3926 } 3927 3928 topWidget = tbar; 3929 3930 /* if the next widget is a separator, turn it on */ 3931 if (n+1<nItems && !strcmp(XtName(children[n+1]), "TOOLBAR_SEP")) { 3932 XtManageChild(children[n+1]); 3933 } 3934 } 3935 else { 3936 /* Remove top attachment to widget to avoid circular dependency. 3937 Attach bottom to form so that when the widget is redisplayed 3938 later, it will trigger the parent form to resize properly as 3939 if the widget is being inserted */ 3940 XtVaSetValues(tbar, XmNtopAttachment, XmATTACH_NONE, 3941 XmNbottomAttachment, XmATTACH_FORM, NULL); 3942 3943 /* if the next widget is a separator, turn it off */ 3944 if (n+1<nItems && !strcmp(XtName(children[n+1]), "TOOLBAR_SEP")) { 3945 XtUnmanageChild(children[n+1]); 3946 } 3947 } 3948 } 3949 3950 if (topWidget) { 3951 if (strcmp(XtName(topWidget), "TOOLBAR_SEP")) { 3952 XtVaSetValues(topWidget, 3953 XmNbottomAttachment, XmATTACH_FORM, 3954 XmNbottomOffset, STAT_SHADOW_THICKNESS, 3955 NULL); 3956 } 3957 else { 3958 /* is a separator */ 3959 Widget wgt; 3960 XtVaGetValues(topWidget, XmNtopWidget, &wgt, NULL); 3961 3962 /* don't need sep below bottom-most toolbar */ 3963 XtUnmanageChild(topWidget); 3964 XtVaSetValues(wgt, 3965 XmNbottomAttachment, XmATTACH_FORM, 3966 XmNbottomOffset, STAT_SHADOW_THICKNESS, 3967 NULL); 3968 } 3969 } 3970 3971 return topWidget; 3972 } 3973 3974 /* 3975 ** Calculate the dimension of the text area, in terms of rows & cols, 3976 ** as if there's only one single text pane in the window. 3977 */ 3978 static void getTextPaneDimension(WindowInfo *window, int *nRows, int *nCols) 3979 { 3980 Widget hScrollBar; 3981 Dimension hScrollBarHeight, paneHeight; 3982 int marginHeight, marginWidth, totalHeight, fontHeight; 3983 textDisp *textD = ((TextWidget)window->textArea)->text.textD; 3984 3985 /* width is the same for panes */ 3986 XtVaGetValues(window->textArea, textNcolumns, nCols, NULL); 3987 3988 /* we have to work out the height, as the text area may have been split */ 3989 XtVaGetValues(window->textArea, textNhScrollBar, &hScrollBar, 3990 textNmarginHeight, &marginHeight, textNmarginWidth, &marginWidth, 3991 NULL); 3992 XtVaGetValues(hScrollBar, XmNheight, &hScrollBarHeight, NULL); 3993 XtVaGetValues(window->splitPane, XmNheight, &paneHeight, NULL); 3994 totalHeight = paneHeight - 2*marginHeight -hScrollBarHeight; 3995 fontHeight = textD->ascent + textD->descent; 3996 *nRows = totalHeight/fontHeight; 3997 } 3998 3999 /* 4000 ** Create a new document in the shell window. 4001 ** Document are created in 'background' so that the user 4002 ** menus, ie. the Macro/Shell/BG menus, will not be updated 4003 ** unnecessarily; hence speeding up the process of opening 4004 ** multiple files. 4005 */ 4006 WindowInfo* CreateDocument(WindowInfo* shellWindow, const char* name) 4007 { 4008 Widget pane, text; 4009 WindowInfo *window; 4010 int nCols, nRows; 4011 4012 EnableWindowResourceDB(shellWindow); 4013 4014 /* Allocate some memory for the new window data structure */ 4015 window = (WindowInfo *)NEditMalloc(sizeof(WindowInfo)); 4016 4017 /* inherit settings and later reset those required */ 4018 memcpy(window, shellWindow, sizeof(WindowInfo)); 4019 4020 #if 0 4021 /* share these dialog items with parent shell */ 4022 window->replaceDlog = NULL; 4023 window->replaceText = NULL; 4024 window->replaceWithText = NULL; 4025 window->replaceWordToggle = NULL; 4026 window->replaceCaseToggle = NULL; 4027 window->replaceRegexToggle = NULL; 4028 window->findDlog = NULL; 4029 window->findText = NULL; 4030 window->findWordToggle = NULL; 4031 window->findCaseToggle = NULL; 4032 window->findRegexToggle = NULL; 4033 window->replaceMultiFileDlog = NULL; 4034 window->replaceMultiFilePathBtn = NULL; 4035 window->replaceMultiFileList = NULL; 4036 window->showLineNumbers = GetPrefLineNums(); 4037 window->showStats = GetPrefStatsLine(); 4038 window->showISearchLine = GetPrefISearchLine(); 4039 #endif 4040 4041 window->showInfoBar = FALSE; 4042 window->encErrors = NULL; 4043 window->numEncErrors = 0; 4044 window->posEncErrors = 0; 4045 window->multiFileReplSelected = FALSE; 4046 window->multiFileBusy = FALSE; 4047 window->writableWindows = NULL; 4048 window->nWritableWindows = 0; 4049 window->fileChanged = FALSE; 4050 window->fileMissing = True; 4051 window->fileMode = 0; 4052 window->fileUid = 0; 4053 window->fileGid = 0; 4054 window->filenameSet = FALSE; 4055 window->fileFormat = UNIX_FILE_FORMAT; 4056 window->lastModTime = 0; 4057 strcpy(window->filename, name); 4058 4059 window->encoding[0] = '\0'; 4060 window->filter = NULL; 4061 const char *default_encoding = GetPrefDefaultCharset(); 4062 if(default_encoding) { 4063 size_t defenc_len = strlen(default_encoding); 4064 if(strlen(default_encoding) < MAX_ENCODING_LENGTH) { 4065 memcpy(window->encoding, default_encoding, defenc_len+1); 4066 } 4067 } 4068 4069 window->undo = NULL; 4070 window->redo = NULL; 4071 window->nPanes = 0; 4072 window->autoSaveCharCount = 0; 4073 window->autoSaveOpCount = 0; 4074 window->undoOpCount = 0; 4075 window->undoMemUsed = 0; 4076 window->undo_op_batch_size = 0; 4077 CLEAR_ALL_LOCKS(window->lockReasons); 4078 window->indentStyle = GetPrefAutoIndent(PLAIN_LANGUAGE_MODE); 4079 window->autoSave = GetPrefAutoSave(); 4080 window->saveOldVersion = GetPrefSaveOldVersion(); 4081 window->wrapMode = GetPrefWrap(PLAIN_LANGUAGE_MODE); 4082 window->overstrike = False; 4083 window->showMatchingStyle = GetPrefShowMatching(); 4084 window->matchSyntaxBased = GetPrefMatchSyntaxBased(); 4085 window->highlightSyntax = GetPrefHighlightSyntax(); 4086 window->highlightCursorLine = GetPrefHighlightCursorLine(); 4087 window->indentRainbow = GetPrefIndentRainbow(); 4088 window->indentRainbowColors = NEditStrdup(GetPrefIndentRainbowColors()); 4089 window->ansiColors = GetPrefAnsiColors(); 4090 window->backlightCharTypes = NULL; 4091 window->backlightChars = GetPrefBacklightChars(); 4092 if (window->backlightChars) { 4093 char *cTypes = GetPrefBacklightCharTypes(); 4094 if (cTypes && window->backlightChars) { 4095 if ((window->backlightCharTypes = (char*)NEditMalloc(strlen(cTypes) + 1))) 4096 strcpy(window->backlightCharTypes, cTypes); 4097 } 4098 } 4099 window->modeMessageDisplayed = FALSE; 4100 window->modeMessage = NULL; 4101 window->ignoreModify = FALSE; 4102 window->windowMenuValid = FALSE; 4103 window->flashTimeoutID = 0; 4104 window->fileClosedAtom = None; 4105 window->wasSelected = FALSE; 4106 strcpy(window->fontName, GetPrefFontName()); 4107 strcpy(window->italicFontName, GetPrefItalicFontName()); 4108 strcpy(window->boldFontName, GetPrefBoldFontName()); 4109 strcpy(window->boldItalicFontName, GetPrefBoldItalicFontName()); 4110 window->colorDialog = NULL; 4111 window->font = FontRef(GetPrefFont()); 4112 window->italicFont = FontRef(GetPrefItalicFont()); 4113 window->boldFont = FontRef(GetPrefBoldFont()); 4114 window->boldItalicFont = FontRef(GetPrefBoldItalicFont()); 4115 window->zoom = 0; 4116 window->fontDialog = NULL; 4117 window->nMarks = 0; 4118 window->markTimeoutID = 0; 4119 window->highlightData = NULL; 4120 window->shellCmdData = NULL; 4121 window->macroCmdData = NULL; 4122 window->smartIndentData = NULL; 4123 window->languageMode = PLAIN_LANGUAGE_MODE; 4124 window->iSearchHistIndex = 0; 4125 window->iSearchStartPos = -1; 4126 window->replaceLastRegexCase = TRUE; 4127 window->replaceLastLiteralCase = FALSE; 4128 window->iSearchLastRegexCase = TRUE; 4129 window->iSearchLastLiteralCase = FALSE; 4130 window->findLastRegexCase = TRUE; 4131 window->findLastLiteralCase = FALSE; 4132 window->tab = NULL; 4133 window->bgMenuUndoItem = NULL; 4134 window->bgMenuRedoItem = NULL; 4135 window->device = 0; 4136 window->inode = 0; 4137 4138 getTextPaneDimension(shellWindow, &nRows, &nCols); 4139 4140 /* Create pane that actaully holds the new document. As 4141 document is created in 'background', we need to hide 4142 it. If we leave it unmanaged without setting it to 4143 the XmNworkWindow of the mainWin, due to a unknown 4144 bug in Motif where splitpane's scrollWindow child 4145 somehow came up with a height taller than the splitpane, 4146 the bottom part of the text editing widget is obstructed 4147 when later brought up by RaiseDocument(). So we first 4148 manage it hidden, then unmanage it and reset XmNworkWindow, 4149 then let RaiseDocument() show it later. */ 4150 pane = XtVaCreateWidget("pane", 4151 xmPanedWindowWidgetClass, window->mainWin, 4152 XmNmarginWidth, 0, XmNmarginHeight, 0, XmNseparatorOn, False, 4153 XmNspacing, 3, XmNsashIndent, -2, 4154 XmNmappedWhenManaged, False, 4155 NULL); 4156 XtVaSetValues(window->mainWin, XmNworkWindow, pane, NULL); 4157 XtManageChild(pane); 4158 window->splitPane = pane; 4159 4160 /* Store a copy of document/window pointer in text pane to support 4161 action procedures. See also WidgetToWindow() for info. */ 4162 XtVaSetValues(pane, XmNuserData, window, NULL); 4163 4164 /* Patch around Motif's most idiotic "feature", that its menu accelerators 4165 recognize Caps Lock and Num Lock as modifiers, and don't trigger if 4166 they are engaged */ 4167 AccelLockBugPatch(pane, window->menuBar); 4168 4169 /* Create the first, and most permanent text area (other panes may 4170 be added & removed, but this one will never be removed */ 4171 text = createTextArea(pane, window, nRows, nCols, 4172 GetPrefEmTabDist(PLAIN_LANGUAGE_MODE), GetPrefDelimiters(), 4173 GetPrefWrapMargin(), window->showLineNumbers?MIN_LINE_NUM_COLS:0); 4174 XtManageChild(text); 4175 window->textArea = text; 4176 window->lastFocus = text; 4177 4178 /* Set the initial colors from the globals. */ 4179 SetColorProfile(window, shellWindow->colorProfile); 4180 // TODO: remove XtVaSetValues here, changing the color profile is enough 4181 /* 4182 XtVaSetValues(window->textArea, 4183 textNansiColorList, window->ansiColorList, NULL); 4184 for (int i=0; i<window->nPanes; i++) { 4185 XtVaSetValues(window->textPanes[i], textNansiColorList, window->ansiColorList, NULL); 4186 } 4187 */ 4188 4189 /* Create the right button popup menu (note: order is important here, 4190 since the translation for popping up this menu was probably already 4191 added in createTextArea, but CreateBGMenu requires window->textArea 4192 to be set so it can attach the menu to it (because menu shells are 4193 finicky about the kinds of widgets they are attached to)) */ 4194 window->bgMenuPane = CreateBGMenu(window); 4195 4196 /* cache user menus: init. user background menu cache */ 4197 InitUserBGMenuCache(&window->userBGMenuCache); 4198 4199 /* Create the text buffer rather than using the one created automatically 4200 with the text area widget. This is done so the syntax highlighting 4201 modify callback can be called to synchronize the style buffer BEFORE 4202 the text display's callback is called upon to display a modification */ 4203 window->buffer = BufCreate(); 4204 BufAddModifyCB(window->buffer, SyntaxHighlightModifyCB, window); 4205 4206 /* Attach the buffer to the text widget, and add callbacks for modify */ 4207 TextSetBuffer(text, window->buffer); 4208 BufAddModifyCB(window->buffer, modifiedCB, window); 4209 4210 /* Designate the permanent text area as the owner for selections */ 4211 HandleXSelections(text); 4212 4213 /* Set the requested hardware tab distance and useTabs in the text buffer */ 4214 BufSetTabDistance(window->buffer, GetPrefTabDist(PLAIN_LANGUAGE_MODE)); 4215 window->buffer->useTabs = GetPrefInsertTabs(); 4216 window->tab = addTab(window->tabBar, name); 4217 4218 /* add the window to the global window list, update the Windows menus */ 4219 InvalidateWindowMenus(); 4220 addToWindowList(window); 4221 4222 #ifdef LESSTIF_VERSION 4223 /* FIXME: Temporary workaround for disappearing-text-window bug 4224 when linking to Lesstif. 4225 4226 After changes is made to statsAreaForm (parent of statsline, 4227 i-search line and tab bar) widget such as enabling/disabling 4228 the statsline, the XmForm widget enclosing the text widget 4229 somehow refused to resize to fit the text widget. Resizing 4230 the shell window or making changes [again] to the statsAreaForm 4231 appeared to bring out the text widget, though doesn't fix it for 4232 the subsequently added documents. Here we try to do the latter 4233 for all new documents created. */ 4234 if (XtIsManaged(XtParent(window->statsLineForm))) { 4235 XtUnmanageChild(XtParent(window->statsLineForm)); 4236 XtManageChild(XtParent(window->statsLineForm)); 4237 } 4238 #endif /* LESSTIF_VERSION */ 4239 4240 /* return the shell ownership to previous tabbed doc */ 4241 XtVaSetValues(window->mainWin, XmNworkWindow, shellWindow->splitPane, NULL); 4242 XLowerWindow(TheDisplay, XtWindow(window->splitPane)); 4243 XtUnmanageChild(window->splitPane); 4244 XtVaSetValues(window->splitPane, XmNmappedWhenManaged, True, NULL); 4245 4246 EnableDefaultColorProfileResourceDB(XtDisplay(window->mainWin)); 4247 4248 return window; 4249 } 4250 4251 /* 4252 ** return the next/previous docment on the tab list. 4253 ** 4254 ** If <wrap> is true then the next tab of the rightmost tab will be the 4255 ** second tab from the right, and the the previous tab of the leftmost 4256 ** tab will be the second from the left. This is useful for getting 4257 ** the next tab after a tab detaches/closes and you don't want to wrap around. 4258 */ 4259 static WindowInfo *getNextTabWindow(WindowInfo *window, int direction, 4260 int crossWin, int wrap) 4261 { 4262 WidgetList tabList, tabs; 4263 WindowInfo *win; 4264 int tabCount, tabTotalCount; 4265 int tabPos, nextPos; 4266 int i, n; 4267 int nBuf = crossWin? NWindows() : NDocuments(window); 4268 4269 if (nBuf <= 1) 4270 return NULL; 4271 4272 /* get the list of tabs */ 4273 tabs = (WidgetList)NEditMalloc(sizeof(Widget) * nBuf); 4274 tabTotalCount = 0; 4275 if (crossWin) { 4276 int n, nItems; 4277 WidgetList children; 4278 4279 XtVaGetValues(TheAppShell, XmNchildren, &children, 4280 XmNnumChildren, &nItems, NULL); 4281 4282 /* get list of tabs in all windows */ 4283 for (n=0; n<nItems; n++) { 4284 if (strcmp(XtName(children[n]), "textShell") || 4285 ((win = WidgetToWindow(children[n])) == NULL)) 4286 continue; /* skip non-text-editor windows */ 4287 4288 XtVaGetValues(win->tabBar, XmNtabWidgetList, &tabList, 4289 XmNtabCount, &tabCount, NULL); 4290 4291 for (i=0; i< tabCount; i++) { 4292 tabs[tabTotalCount++] = tabList[i]; 4293 } 4294 } 4295 } 4296 else { 4297 /* get list of tabs in this window */ 4298 XtVaGetValues(window->tabBar, XmNtabWidgetList, &tabList, 4299 XmNtabCount, &tabCount, NULL); 4300 4301 for (i=0; i< tabCount; i++) { 4302 if (TabToWindow(tabList[i])) /* make sure tab is valid */ 4303 tabs[tabTotalCount++] = tabList[i]; 4304 } 4305 } 4306 4307 /* find the position of the tab in the tablist */ 4308 tabPos = 0; 4309 for (n=0; n<tabTotalCount; n++) { 4310 if (tabs[n] == window->tab) { 4311 tabPos = n; 4312 break; 4313 } 4314 } 4315 4316 /* calculate index position of next tab */ 4317 nextPos = tabPos + direction; 4318 if (nextPos >= nBuf) { 4319 if (wrap) 4320 nextPos = 0; 4321 else 4322 nextPos = nBuf - 2; 4323 } else if (nextPos < 0) { 4324 if (wrap) 4325 nextPos = nBuf - 1; 4326 else 4327 nextPos = 1; 4328 } 4329 4330 /* return the document where the next tab belongs to */ 4331 win = TabToWindow(tabs[nextPos]); 4332 NEditFree(tabs); 4333 return win; 4334 } 4335 4336 /* 4337 ** return the integer position of a tab in the tabbar it 4338 ** belongs to, or -1 if there's an error, somehow. 4339 */ 4340 static int getTabPosition(Widget tab) 4341 { 4342 WidgetList tabList; 4343 int i, tabCount; 4344 Widget tabBar = XtParent(tab); 4345 4346 XtVaGetValues(tabBar, XmNtabWidgetList, &tabList, 4347 XmNtabCount, &tabCount, NULL); 4348 4349 for (i=0; i< tabCount; i++) { 4350 if (tab == tabList[i]) 4351 return i; 4352 } 4353 4354 return -1; /* something is wrong! */ 4355 } 4356 4357 /* 4358 ** update the tab label, etc. of a tab, per the states of it's document. 4359 */ 4360 void RefreshTabState(WindowInfo *win) 4361 { 4362 XmString s1, tipString; 4363 char labelString[2*MAXPATHLEN+4]; 4364 char *tag = XmFONTLIST_DEFAULT_TAG; 4365 unsigned char alignment; 4366 4367 /* Set tab label to document's filename. Position of 4368 "*" (modified) will change per label alignment setting */ 4369 XtVaGetValues(win->tab, XmNalignment, &alignment, NULL); 4370 if (alignment != XmALIGNMENT_END) { 4371 snprintf(labelString, sizeof(labelString),"%s%s", 4372 win->fileChanged? "*" : "", 4373 win->filename); 4374 } else { 4375 snprintf(labelString, sizeof(labelString),"%s%s", 4376 win->filename, 4377 win->fileChanged? "*" : ""); 4378 } 4379 4380 /* Make the top document stand out a little more */ 4381 if (IsTopDocument(win)) 4382 tag = "BOLD"; 4383 4384 s1 = XmStringCreateLtoR(labelString, tag); 4385 4386 if (GetPrefShowPathInWindowsMenu() && win->filenameSet) { 4387 strcat(labelString, " - "); 4388 strcat(labelString, win->path); 4389 } 4390 tipString=XmStringCreateSimple(labelString); 4391 4392 XtVaSetValues(win->tab, 4393 XltNbubbleString, tipString, 4394 XmNlabelString, s1, 4395 NULL); 4396 XmStringFree(s1); 4397 XmStringFree(tipString); 4398 } 4399 4400 4401 typedef struct SaveFilesData { 4402 Widget shell; 4403 4404 int status; /* 0: save, 1: don't save, 2: cancel */ 4405 int end; 4406 } SaveFilesData; 4407 4408 void savefiles_save(Widget w, SaveFilesData *data, XtPointer d) 4409 { 4410 data->status = 0; 4411 data->end = 1; 4412 } 4413 4414 void savefiles_dontsave(Widget w, SaveFilesData *data, XtPointer d) 4415 { 4416 data->status = 1; 4417 data->end = 1; 4418 } 4419 4420 void savefiles_cancel(Widget w, SaveFilesData *data, XtPointer d) 4421 { 4422 data->status = 2; 4423 data->end = 1; 4424 } 4425 4426 #define WIDGET_SPACING 5 4427 #define WINDOW_SPACING 8 4428 4429 /* 4430 * Shows a dialog, where all files, that should be saved, can be selected 4431 * If 'save' is clicked, all selected files will be saved 4432 */ 4433 int SaveFilesDialog(WindowInfo *window) 4434 { 4435 Arg args[32]; 4436 int n = 0; 4437 XmString str; 4438 4439 Widget winShell = window->shell; 4440 4441 Widget dialog = CreateDialogShell(window->shell, "Save Files", args, 0); 4442 4443 SaveFilesData data; 4444 memset(&data, 0, sizeof(SaveFilesData)); 4445 data.shell = dialog; 4446 4447 AddMotifCloseCallback(dialog, (XtCallbackProc)savefiles_cancel, &data); 4448 4449 // create dialog 4450 n = 0; 4451 XtSetArg(args[0], XmNshadowThickness, 0); n++; 4452 Widget form = XmCreateForm(dialog, "form", args, n); 4453 4454 4455 4456 // bottom buttons form 4457 n = 0; 4458 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 4459 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 4460 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 4461 XtSetArg(args[n], XmNshadowThickness, 1); n++; 4462 XtSetArg(args[n], XmNshadowType, XmSHADOW_ETCHED_OUT); n++; 4463 Widget buttons = XmCreateForm(form, "btnform", args, n); 4464 XtManageChild(buttons); 4465 4466 n = 0; 4467 str = XmStringCreateLocalized("Save"); 4468 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 4469 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 4470 XtSetArg(args[n], XmNrightOffset, WIDGET_SPACING); n++; 4471 XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++; 4472 XtSetArg(args[n], XmNrightPosition, 33); n++; 4473 XtSetArg(args[n], XmNlabelString, str); n++; 4474 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 4475 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 4476 XtSetArg(args[n], XmNtopOffset, WIDGET_SPACING); n++; 4477 XtSetArg(args[n], XmNbottomOffset, WINDOW_SPACING); n++; 4478 Widget btnSave = XmCreatePushButton(buttons, "button", args, n); 4479 XtManageChild(btnSave); 4480 XmStringFree(str); 4481 4482 n = 0; 4483 str = XmStringCreateLocalized("Don''t Save"); 4484 XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++; 4485 XtSetArg(args[n], XmNleftPosition, 33); n++; 4486 XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++; 4487 XtSetArg(args[n], XmNrightPosition, 66); n++; 4488 XtSetArg(args[n], XmNlabelString, str); n++; 4489 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 4490 XtSetArg(args[n], XmNrightOffset, WINDOW_SPACING); n++; 4491 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 4492 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 4493 XtSetArg(args[n], XmNtopOffset, WIDGET_SPACING); n++; 4494 XtSetArg(args[n], XmNbottomOffset, WINDOW_SPACING); n++; 4495 Widget btnDontSave = XmCreatePushButton(buttons, "button", args, n); 4496 XtManageChild(btnDontSave); 4497 XmStringFree(str); 4498 4499 n = 0; 4500 str = XmStringCreateLocalized("Cancel"); 4501 XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++; 4502 XtSetArg(args[n], XmNleftPosition, 66); n++; 4503 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 4504 XtSetArg(args[n], XmNlabelString, str); n++; 4505 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 4506 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 4507 XtSetArg(args[n], XmNtopOffset, WIDGET_SPACING); n++; 4508 XtSetArg(args[n], XmNbottomOffset, WINDOW_SPACING); n++; 4509 XtSetArg(args[n], XmNleftOffset, WIDGET_SPACING); n++; 4510 XtSetArg(args[n], XmNrightOffset, WINDOW_SPACING); n++; 4511 Widget btnCancel = XmCreatePushButton(buttons, "button", args, n); 4512 XtManageChild(btnCancel); 4513 XmStringFree(str); 4514 4515 n = 0; 4516 XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; 4517 XtSetArg(args[n], XmNleftWidget, btnDontSave); n++; 4518 XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; 4519 XtSetArg(args[n], XmNrightWidget, btnCancel); n++; 4520 XtSetArg(args[n], XmNrightOffset, WINDOW_SPACING); n++; 4521 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 4522 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 4523 XtSetArg(args[n], XmNtopOffset, WIDGET_SPACING); n++; 4524 XtSetArg(args[n], XmNbottomOffset, WIDGET_SPACING); n++; 4525 XtSetArg(args[n], XmNseparatorType, XmNO_LINE); n++; 4526 Widget space = XmCreateSeparator(buttons, "space", args, n); 4527 XtManageChild(space); 4528 4529 n = 0; 4530 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 4531 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 4532 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 4533 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; 4534 XtSetArg(args[n], XmNbottomWidget, buttons); n++; 4535 XtSetArg(args[n], XmNshadowThickness, 1); n++; 4536 XtSetArg(args[n], XmNshadowType, XmSHADOW_ETCHED_OUT); n++; 4537 Widget topForm = XmCreateForm(form, "frame", args, n); 4538 XtManageChild(topForm); 4539 4540 // top label 4541 n = 0; 4542 str = XmStringCreateLocalized("Save files before closing?"); 4543 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 4544 XtSetArg(args[n], XmNtopOffset, WINDOW_SPACING); n++; 4545 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 4546 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 4547 XtSetArg(args[n], XmNlabelString, str); n++; 4548 Widget label = XmCreateLabel(topForm, "label", args, n); 4549 XtManageChild(label); 4550 XmStringFree(str); 4551 4552 4553 // create the ScrolledWindow for the documents checkboxes 4554 n = 0; 4555 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; 4556 XtSetArg(args[n], XmNtopWidget, label); n++; 4557 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 4558 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; 4559 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 4560 XtSetArg(args[n], XmNtopOffset, WINDOW_SPACING); n++; 4561 XtSetArg(args[n], XmNleftOffset, WINDOW_SPACING); n++; 4562 XtSetArg(args[n], XmNrightOffset, WINDOW_SPACING); n++; 4563 XtSetArg(args[n], XmNbottomOffset, WINDOW_SPACING); n++; 4564 XtSetArg(args[n], XmNscrollBarDisplayPolicy, XmAS_NEEDED); n++; 4565 XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); n++; 4566 XtSetArg(args[n], XmNshadowThickness, 1); n++; 4567 Widget scrollW = XmCreateScrolledWindow(topForm, "scrolledwindow", args, n); 4568 XtManageChild(scrollW); 4569 4570 n = 0; 4571 XtSetArg(args[n], XmNshadowThickness, 0); n++; 4572 Widget docForm = XmCreateForm(scrollW, "form", args, n); 4573 4574 // create togglebuttons for unsaved documents 4575 size_t dalloc = 64; 4576 size_t dsize = 0; 4577 Widget *docButtons = NEditCalloc(dalloc, sizeof(Widget)); 4578 4579 n = 0; 4580 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; 4581 XtSetArg(args[n], XmNleftOffset, WIDGET_SPACING); n++; 4582 XtSetArg(args[n], XmNtopOffset, WIDGET_SPACING); n++; 4583 XtSetArg(args[n], XmNset, 1); n++; 4584 int k = n; 4585 Widget topWid = NULL; 4586 for (WindowInfo *win=WindowList;win;win=win->next) { 4587 if(win->shell == winShell && win->fileChanged) { 4588 n = k; 4589 str = XmStringCreateLocalized(win->filename); 4590 XtSetArg(args[n], XmNlabelString, str); n++; 4591 XtSetArg(args[n], XmNuserData, win); n++; 4592 if(topWid) { 4593 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++; 4594 XtSetArg(args[n], XmNtopWidget, topWid); n++; 4595 } else { 4596 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 4597 } 4598 topWid = XmCreateToggleButton(docForm, "sfbutton", args, n); 4599 XtManageChild(topWid); 4600 XmStringFree(str); 4601 4602 // add togglebutton to the array 4603 if(dsize >= dalloc) { 4604 dalloc += 64; 4605 docButtons = NEditRealloc(docButtons, dalloc * sizeof(Widget)); 4606 } 4607 docButtons[dsize++] = topWid; 4608 } 4609 } 4610 XtManageChild(docForm); 4611 4612 4613 // checkbox form 4614 n = 0; 4615 4616 XtVaSetValues( 4617 buttons, 4618 XmNdefaultButton, 4619 btnSave, 4620 XmNcancelButton, 4621 btnCancel, 4622 NULL); 4623 4624 // event handler 4625 XtAddCallback( 4626 btnSave, 4627 XmNactivateCallback, 4628 (XtCallbackProc)savefiles_save, 4629 &data); 4630 XtAddCallback( 4631 btnDontSave, 4632 XmNactivateCallback, 4633 (XtCallbackProc)savefiles_dontsave, 4634 &data); 4635 XtAddCallback( 4636 btnCancel, 4637 XmNactivateCallback, 4638 (XtCallbackProc)savefiles_cancel, 4639 &data); 4640 4641 // show dialog 4642 if(dsize > 0) { 4643 ManageDialogCenteredOnPointer(form); 4644 4645 XmProcessTraversal(buttons, XmTRAVERSE_CURRENT); 4646 4647 XtAppContext app = XtWidgetToApplicationContext(dialog); 4648 while(!data.end && !XtAppGetExitFlag(app)) { 4649 XEvent event; 4650 XtAppNextEvent(app, &event); 4651 XtDispatchEvent(&event); 4652 } 4653 } // else: no unsaved documents 4654 4655 XtUnmapWidget(dialog); 4656 4657 // data.status == 0 --> save selected files 4658 int save = data.status == 0 ? 4659 YES_SBC_DIALOG_RESPONSE : NO_SBC_DIALOG_RESPONSE; 4660 if(data.status != 2) { 4661 for(int i=0;i<dsize;i++) { 4662 Boolean set = True; 4663 WindowInfo *win = NULL; 4664 XtVaGetValues(docButtons[i], XmNset, &set, XmNuserData, &win, NULL); 4665 int saveDoc = save; 4666 if(!set) { 4667 saveDoc = NO_SBC_DIALOG_RESPONSE; 4668 } 4669 if(win) { 4670 // save document 4671 if(!CloseFileAndWindow(win, saveDoc)) { 4672 data.status = 2; // cancel 4673 break; 4674 } 4675 } 4676 } 4677 } 4678 NEditFree(docButtons); 4679 XtDestroyWidget(dialog); 4680 4681 return data.status == 2 ? True : False; 4682 } 4683 4684 4685 /* 4686 ** close all the documents in a window 4687 */ 4688 int CloseAllDocumentInWindow(WindowInfo *window) 4689 { 4690 WindowInfo *win; 4691 4692 if (NUnsavedDocuments(window) == 1) { 4693 /* only one document in the window */ 4694 return CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE); 4695 } 4696 else { 4697 Widget winShell = window->shell; 4698 WindowInfo *topDocument; 4699 4700 #ifndef OLD_CLOSE_FILE_DIALOG 4701 // open dialog for selecting files, that should be saved 4702 if(SaveFilesDialog(window)) { 4703 return False; 4704 } 4705 #else 4706 /* close all _modified_ documents belong to this window */ 4707 for (win = WindowList; win; ) { 4708 if (win->shell == winShell && win->fileChanged) { 4709 WindowInfo *next = win->next; 4710 if (!CloseFileAndWindow(win, PROMPT_SBC_DIALOG_RESPONSE)) 4711 return False; 4712 win = next; 4713 } 4714 else 4715 win = win->next; 4716 } 4717 #endif 4718 4719 /* see there's still documents left in the window */ 4720 for (win = WindowList; win; win=win->next) 4721 if (win->shell == winShell) 4722 break; 4723 4724 if (win) { 4725 topDocument = GetTopDocument(winShell); 4726 4727 /* close all non-top documents belong to this window */ 4728 for (win = WindowList; win; ) { 4729 if (win->shell == winShell && win != topDocument) { 4730 WindowInfo *next = win->next; 4731 if (!CloseFileAndWindow(win, PROMPT_SBC_DIALOG_RESPONSE)) 4732 return False; 4733 win = next; 4734 } 4735 else 4736 win = win->next; 4737 } 4738 4739 /* close the last document and its window */ 4740 if (!CloseFileAndWindow(topDocument, PROMPT_SBC_DIALOG_RESPONSE)) 4741 return False; 4742 } 4743 } 4744 4745 return True; 4746 } 4747 4748 static void CloseDocumentWindow(Widget w, WindowInfo *window, XtPointer callData) 4749 { 4750 int nDocuments = NDocuments(window); 4751 4752 if (nDocuments == NWindows()) { 4753 /* this is only window, then exit */ 4754 XtCallActionProc(WindowList->lastFocus, "exit", 4755 ((XmAnyCallbackStruct *)callData)->event, NULL, 0); 4756 } 4757 else { 4758 if (nDocuments == 1) { 4759 CloseFileAndWindow(window, PROMPT_SBC_DIALOG_RESPONSE); 4760 } 4761 else { 4762 int resp = 1; 4763 if (GetPrefWarnExit()) 4764 resp = DialogF(DF_QUES, window->shell, 2, "Close Window", 4765 "Close ALL documents in this window?", "Close", "Cancel"); 4766 4767 if (resp == 1) 4768 CloseAllDocumentInWindow(window); 4769 } 4770 } 4771 } 4772 4773 /* 4774 ** Refresh the menu entries per the settings of the 4775 ** top document. 4776 */ 4777 void RefreshMenuToggleStates(WindowInfo *window) 4778 { 4779 WindowInfo *win; 4780 4781 if (!IsTopDocument(window)) 4782 return; 4783 4784 /* File menu */ 4785 XtSetSensitive(window->printSelItem, window->wasSelected); 4786 4787 /* Edit menu */ 4788 XtSetSensitive(window->undoItem, window->undo != NULL); 4789 XtSetSensitive(window->redoItem, window->redo != NULL); 4790 XtSetSensitive(window->printSelItem, window->wasSelected); 4791 XtSetSensitive(window->cutItem, window->wasSelected); 4792 XtSetSensitive(window->copyItem, window->wasSelected); 4793 XtSetSensitive(window->delItem, window->wasSelected); 4794 4795 /* Preferences menu */ 4796 XmToggleButtonSetState(window->statsLineItem, window->showStats, False); 4797 XmToggleButtonSetState(window->iSearchLineItem, window->showISearchLine, False); 4798 XmToggleButtonSetState(window->lineNumsItem, window->showLineNumbers, False); 4799 XmToggleButtonSetState(window->highlightItem, window->highlightSyntax, False); 4800 XtSetSensitive(window->resetZoomItem, window->zoom == 0 ? False : True); 4801 XtSetSensitive(window->highlightItem, window->languageMode != PLAIN_LANGUAGE_MODE); 4802 XmToggleButtonSetState(window->backlightCharsItem, window->backlightChars, False); 4803 XmToggleButtonSetState(window->highlightCursorLineItem, window->highlightCursorLine, False); 4804 XmToggleButtonSetState(window->indentRainbowItem, window->indentRainbow, False); 4805 XmToggleButtonSetState(window->ansiColorsItem, window->ansiColors, False); 4806 XmToggleButtonSetState(window->saveLastItem, window->saveOldVersion, False); 4807 XmToggleButtonSetState(window->autoSaveItem, window->autoSave, False); 4808 XmToggleButtonSetState(window->overtypeModeItem, window->overstrike, False); 4809 XmToggleButtonSetState(window->matchSyntaxBasedItem, window->matchSyntaxBased, False); 4810 XmToggleButtonSetState(window->readOnlyItem, IS_USER_LOCKED(window->lockReasons), False); 4811 4812 XtSetSensitive(window->smartIndentItem, 4813 SmartIndentMacrosAvailable(LanguageModeName(window->languageMode))); 4814 4815 SetAutoIndent(window, window->indentStyle); 4816 SetAutoWrap(window, window->wrapMode); 4817 SetShowMatching(window, window->showMatchingStyle); 4818 SetLanguageMode(window, window->languageMode, FALSE); 4819 4820 /* Windows Menu */ 4821 XtSetSensitive(window->splitPaneItem, window->nPanes < MAX_PANES); 4822 XtSetSensitive(window->closePaneItem, window->nPanes > 0); 4823 XtSetSensitive(window->detachDocumentItem, NDocuments(window)>1); 4824 XtSetSensitive(window->contextDetachDocumentItem, NDocuments(window)>1); 4825 4826 for (win=WindowList; win; win=win->next) 4827 if (win->shell != window->shell) 4828 break; 4829 XtSetSensitive(window->moveDocumentItem, win != NULL); 4830 } 4831 4832 /* 4833 ** Refresh the various settings/state of the shell window per the 4834 ** settings of the top document. 4835 */ 4836 static void refreshMenuBar(WindowInfo *window) 4837 { 4838 RefreshMenuToggleStates(window); 4839 4840 /* Add/remove language specific menu items */ 4841 UpdateUserMenus(window); 4842 4843 /* refresh selection-sensitive menus */ 4844 DimSelectionDepUserMenuItems(window, window->wasSelected); 4845 } 4846 4847 /* 4848 ** remember the last document. 4849 */ 4850 WindowInfo *MarkLastDocument(WindowInfo *window) 4851 { 4852 WindowInfo *prev = lastFocusDocument; 4853 4854 if (window) 4855 lastFocusDocument = window; 4856 4857 return prev; 4858 } 4859 4860 /* 4861 ** remember the active (top) document. 4862 */ 4863 WindowInfo *MarkActiveDocument(WindowInfo *window) 4864 { 4865 WindowInfo *prev = inFocusDocument; 4866 4867 if (window) 4868 inFocusDocument = window; 4869 4870 return prev; 4871 } 4872 4873 /* 4874 ** Bring up the next window by tab order 4875 */ 4876 void NextDocument(WindowInfo *window) 4877 { 4878 WindowInfo *win; 4879 4880 if (WindowList->next == NULL) 4881 return; 4882 4883 win = getNextTabWindow(window, 1, GetPrefGlobalTabNavigate(), 1); 4884 if (win == NULL) 4885 return; 4886 4887 if (window->shell == win->shell) 4888 RaiseDocument(win); 4889 else 4890 RaiseFocusDocumentWindow(win, True); 4891 } 4892 4893 /* 4894 ** Bring up the previous window by tab order 4895 */ 4896 void PreviousDocument(WindowInfo *window) 4897 { 4898 WindowInfo *win; 4899 4900 if (WindowList->next == NULL) 4901 return; 4902 4903 win = getNextTabWindow(window, -1, GetPrefGlobalTabNavigate(), 1); 4904 if (win == NULL) 4905 return; 4906 4907 if (window->shell == win->shell) 4908 RaiseDocument(win); 4909 else 4910 RaiseFocusDocumentWindow(win, True); 4911 } 4912 4913 /* 4914 ** Bring up the last active window 4915 */ 4916 void LastDocument(WindowInfo *window) 4917 { 4918 WindowInfo *win; 4919 4920 for(win = WindowList; win; win=win->next) 4921 if (lastFocusDocument == win) 4922 break; 4923 4924 if (!win) 4925 return; 4926 4927 if (window->shell == win->shell) 4928 RaiseDocument(win); 4929 else 4930 RaiseFocusDocumentWindow(win, True); 4931 4932 } 4933 4934 /* 4935 ** make sure window is alive is kicking 4936 */ 4937 int IsValidWindow(WindowInfo *window) 4938 { 4939 WindowInfo *win; 4940 4941 for(win = WindowList; win; win=win->next) 4942 if (window == win) 4943 return True; 4944 4945 4946 return False; 4947 } 4948 4949 /* 4950 ** raise the document and its shell window and focus depending on pref. 4951 */ 4952 void RaiseDocumentWindow(WindowInfo *window) 4953 { 4954 if (!window) 4955 return; 4956 4957 RaiseDocument(window); 4958 RaiseShellWindow(window->shell, GetPrefFocusOnRaise()); 4959 } 4960 4961 /* 4962 ** raise the document and its shell window and optionally focus. 4963 */ 4964 void RaiseFocusDocumentWindow(WindowInfo *window, Boolean focus) 4965 { 4966 if (!window) 4967 return; 4968 4969 RaiseDocument(window); 4970 RaiseShellWindow(window->shell, focus); 4971 } 4972 4973 /* 4974 ** Redisplay menu tearoffs previously hid by hideTearOffs() 4975 */ 4976 static void redisplayTearOffs(Widget menuPane) 4977 { 4978 WidgetList itemList; 4979 Widget subMenuID; 4980 Cardinal nItems; 4981 int n; 4982 4983 /* redisplay all submenu tearoffs */ 4984 XtVaGetValues(menuPane, XmNchildren, &itemList, 4985 XmNnumChildren, &nItems, NULL); 4986 for (n=0; n<(int)nItems; n++) { 4987 if (XtClass(itemList[n]) == xmCascadeButtonWidgetClass) { 4988 XtVaGetValues(itemList[n], XmNsubMenuId, &subMenuID, NULL); 4989 redisplayTearOffs(subMenuID); 4990 } 4991 } 4992 4993 /* redisplay tearoff for this menu */ 4994 if (!XmIsMenuShell(XtParent(menuPane))) 4995 ShowHiddenTearOff(menuPane); 4996 } 4997 4998 /* 4999 ** hide all the tearoffs spawned from this menu. 5000 ** It works recursively to close the tearoffs of the submenus 5001 */ 5002 static void hideTearOffs(Widget menuPane) 5003 { 5004 WidgetList itemList; 5005 Widget subMenuID; 5006 Cardinal nItems; 5007 int n; 5008 5009 /* hide all submenu tearoffs */ 5010 XtVaGetValues(menuPane, XmNchildren, &itemList, 5011 XmNnumChildren, &nItems, NULL); 5012 for (n=0; n<(int)nItems; n++) { 5013 if (XtClass(itemList[n]) == xmCascadeButtonWidgetClass) { 5014 XtVaGetValues(itemList[n], XmNsubMenuId, &subMenuID, NULL); 5015 hideTearOffs(subMenuID); 5016 } 5017 } 5018 5019 /* hide tearoff for this menu */ 5020 if (!XmIsMenuShell(XtParent(menuPane))) 5021 XtUnmapWidget(XtParent(menuPane)); 5022 } 5023 5024 /* 5025 ** Raise a tabbed document within its shell window. 5026 ** 5027 ** NB: use RaiseDocumentWindow() to raise the doc and 5028 ** its shell window. 5029 */ 5030 void RaiseDocument(WindowInfo *window) 5031 { 5032 WindowInfo *win, *lastwin; 5033 5034 if (!window || !WindowList) 5035 return; 5036 5037 lastwin = MarkActiveDocument(window); 5038 if (lastwin != window && IsValidWindow(lastwin)) 5039 MarkLastDocument(lastwin); 5040 5041 /* document already on top? */ 5042 XtVaGetValues(window->mainWin, XmNuserData, &win, NULL); 5043 if (win == window) 5044 return; 5045 5046 /* set the document as top document */ 5047 XtVaSetValues(window->mainWin, XmNuserData, window, NULL); 5048 5049 /* show the new top document */ 5050 XtVaSetValues(window->mainWin, XmNworkWindow, window->splitPane, NULL); 5051 XtManageChild(window->splitPane); 5052 XRaiseWindow(TheDisplay, XtWindow(window->splitPane)); 5053 5054 /* Turn on syntax highlight that might have been deferred. 5055 NB: this must be done after setting the document as 5056 XmNworkWindow and managed, else the parent shell 5057 window may shrink on some window-managers such as 5058 metacity, due to changes made in UpdateWMSizeHints().*/ 5059 if (window->highlightSyntax && window->highlightData==NULL) 5060 StartHighlighting(window, False); 5061 5062 /* put away the bg menu tearoffs of last active document */ 5063 hideTearOffs(win->bgMenuPane); 5064 5065 /* restore the bg menu tearoffs of active document */ 5066 redisplayTearOffs(window->bgMenuPane); 5067 5068 /* set tab as active */ 5069 XmLFolderSetActiveTab(window->tabBar, 5070 getTabPosition(window->tab), False); 5071 5072 /* set keyboard focus. Must be done before unmanaging previous 5073 top document, else lastFocus will be reset to textArea */ 5074 XmProcessTraversal(window->lastFocus, XmTRAVERSE_CURRENT); 5075 5076 /* we only manage the top document, else the next time a document 5077 is raised again, it's textpane might not resize properly. 5078 Also, somehow (bug?) XtUnmanageChild() doesn't hide the 5079 splitPane, which obscure lower part of the statsform when 5080 we toggle its components, so we need to put the document at 5081 the back */ 5082 XLowerWindow(TheDisplay, XtWindow(win->splitPane)); 5083 XtUnmanageChild(win->splitPane); 5084 RefreshTabState(win); 5085 5086 /* now refresh window state/info. RefreshWindowStates() 5087 has a lot of work to do, so we update the screen first so 5088 the document appears to switch swiftly. */ 5089 XmUpdateDisplay(window->splitPane); 5090 RefreshWindowStates(window); 5091 RefreshTabState(window); 5092 5093 /* put away the bg menu tearoffs of last active document */ 5094 hideTearOffs(win->bgMenuPane); 5095 5096 /* restore the bg menu tearoffs of active document */ 5097 redisplayTearOffs(window->bgMenuPane); 5098 5099 /* Make sure that the "In Selection" button tracks the presence of a 5100 selection and that the window inherits the proper search scope. */ 5101 if (window->replaceDlog != NULL && XtIsManaged(window->replaceDlog)) 5102 { 5103 #ifdef REPLACE_SCOPE 5104 window->replaceScope = win->replaceScope; 5105 #endif 5106 UpdateReplaceActionButtons(window); 5107 } 5108 5109 UpdateWMSizeHints(window); 5110 } 5111 5112 WindowInfo* GetTopDocument(Widget w) 5113 { 5114 WindowInfo *window = WidgetToWindow(w); 5115 5116 return WidgetToWindow(window->shell); 5117 } 5118 5119 Boolean IsTopDocument(const WindowInfo *window) 5120 { 5121 return window == GetTopDocument(window->shell)? True : False; 5122 } 5123 5124 static void deleteDocument(WindowInfo *window) 5125 { 5126 if (NULL == window) { 5127 return; 5128 } 5129 5130 XtDestroyWidget(window->splitPane); 5131 } 5132 5133 /* 5134 ** return the number of documents owned by this shell window 5135 */ 5136 int NDocuments(WindowInfo *window) 5137 { 5138 WindowInfo *win; 5139 int nDocument = 0; 5140 5141 for (win = WindowList; win; win = win->next) { 5142 if (win->shell == window->shell) 5143 nDocument++; 5144 } 5145 5146 return nDocument; 5147 } 5148 5149 int NUnsavedDocuments(WindowInfo *window) 5150 { 5151 WindowInfo *win; 5152 Widget winShell = window->shell; 5153 int nDocument = 0; 5154 5155 for (win = WindowList; win; win = win->next) { 5156 if(win->shell == winShell && win->fileChanged) 5157 nDocument++; 5158 } 5159 5160 return nDocument; 5161 } 5162 5163 /* 5164 ** refresh window state for this document 5165 */ 5166 void RefreshWindowStates(WindowInfo *window) 5167 { 5168 int updateStatsFormStatus = 0; 5169 5170 if (!IsTopDocument(window)) 5171 return; 5172 5173 if(!window->showInfoBar && XtIsManaged(window->encodingInfoBar)) { 5174 XtUnmanageChild(window->encodingInfoBar); 5175 updateStatsFormStatus = 1; 5176 } 5177 5178 if (window->modeMessageDisplayed) { 5179 XmTextSetString(window->statsLine, window->modeMessage); 5180 } else { 5181 UpdateStatsLine(window); 5182 } 5183 5184 UpdateWindowReadOnly(window); 5185 UpdateWindowTitle(window); 5186 5187 /* show/hide statsline as needed */ 5188 if (window->modeMessageDisplayed && !XtIsManaged(window->statsLineForm)) { 5189 /* turn on statline to display mode message */ 5190 showStats(window, True); 5191 } 5192 else if (window->showStats && !XtIsManaged(window->statsLineForm)) { 5193 /* turn on statsline since it is enabled */ 5194 showStats(window, True); 5195 } 5196 else if (!window->showStats && !window->modeMessageDisplayed && 5197 XtIsManaged(window->statsLineForm)) { 5198 /* turn off statsline since there's nothing to show */ 5199 showStats(window, False); 5200 } else if(updateStatsFormStatus) { 5201 showStatsForm(window); 5202 } 5203 5204 ShowEncodingInfoBar(window, window->showInfoBar); 5205 5206 /* signal if macro/shell is running */ 5207 if (window->shellCmdData || window->macroCmdData) 5208 BeginWait(window->shell); 5209 else 5210 EndWait(window->shell); 5211 5212 /* we need to force the statsline to reveal itself */ 5213 if (XtIsManaged(window->statsLineForm)) { 5214 XmTextSetCursorPosition(window->statsLine, 0); /* start of line */ 5215 XmTextSetCursorPosition(window->statsLine, 9000); /* end of line */ 5216 } 5217 5218 XmUpdateDisplay(window->statsLine); 5219 refreshMenuBar(window); 5220 5221 updateLineNumDisp(window); 5222 } 5223 5224 static void cloneTextPanes(WindowInfo *window, WindowInfo *orgWin) 5225 { 5226 short paneHeights[MAX_PANES+1]; 5227 int insertPositions[MAX_PANES+1], topLines[MAX_PANES+1]; 5228 int horizOffsets[MAX_PANES+1]; 5229 int i, focusPane, emTabDist, wrapMargin, lineNumCols, totalHeight=0; 5230 char *delimiters; 5231 Widget text; 5232 selection sel; 5233 textDisp *textD, *newTextD; 5234 5235 /* transfer the primary selection */ 5236 memcpy(&sel, &orgWin->buffer->primary, sizeof(selection)); 5237 5238 if (sel.selected) { 5239 if (sel.rectangular) 5240 BufRectSelect(window->buffer, sel.start, sel.end, 5241 sel.rectStart, sel.rectEnd); 5242 else 5243 BufSelect(window->buffer, sel.start, sel.end); 5244 } else 5245 BufUnselect(window->buffer); 5246 5247 /* Record the current heights, scroll positions, and insert positions 5248 of the existing panes, keyboard focus */ 5249 focusPane = 0; 5250 for (i=0; i<=orgWin->nPanes; i++) { 5251 text = i==0 ? orgWin->textArea : orgWin->textPanes[i-1]; 5252 insertPositions[i] = TextGetCursorPos(text); 5253 XtVaGetValues(containingPane(text), XmNheight, &paneHeights[i], NULL); 5254 totalHeight += paneHeights[i]; 5255 TextGetScroll(text, &topLines[i], &horizOffsets[i]); 5256 if (text == orgWin->lastFocus) 5257 focusPane = i; 5258 } 5259 5260 window->nPanes = orgWin->nPanes; 5261 5262 /* Copy some parameters */ 5263 XtVaGetValues(orgWin->textArea, textNemulateTabs, &emTabDist, 5264 textNwordDelimiters, &delimiters, textNwrapMargin, &wrapMargin, 5265 NULL); 5266 lineNumCols = orgWin->showLineNumbers ? MIN_LINE_NUM_COLS : 0; 5267 XtVaSetValues(window->textArea, textNemulateTabs, emTabDist, 5268 textNwordDelimiters, delimiters, textNwrapMargin, wrapMargin, 5269 textNlineNumCols, lineNumCols, NULL); 5270 5271 5272 /* clone split panes, if any */ 5273 textD = ((TextWidget)window->textArea)->text.textD; 5274 if (window->nPanes) { 5275 /* Unmanage & remanage the panedWindow so it recalculates pane 5276 heights */ 5277 XtUnmanageChild(window->splitPane); 5278 5279 /* Create a text widget to add to the pane and set its buffer and 5280 highlight data to be the same as the other panes in the orgWin */ 5281 5282 for(i=0; i<orgWin->nPanes; i++) { 5283 text = createTextArea(window->splitPane, window, 1, 1, emTabDist, 5284 delimiters, wrapMargin, lineNumCols); 5285 TextSetBuffer(text, window->buffer); 5286 5287 if (window->highlightData != NULL) 5288 AttachHighlightToWidget(text, window); 5289 XtManageChild(text); 5290 window->textPanes[i] = text; 5291 5292 /* Fix up the colors */ 5293 newTextD = ((TextWidget)text)->text.textD; 5294 XtVaSetValues(text, XmNforeground, textD->colorProfile->textFgColor.pixel, 5295 XmNbackground, textD->colorProfile->textBgColor.pixel, 5296 textNansiColorList, window->ansiColorList, NULL); 5297 TextDSetColorProfile(newTextD, textD->colorProfile); 5298 } 5299 5300 /* Set the minimum pane height in the new pane */ 5301 UpdateMinPaneHeights(window); 5302 5303 for (i=0; i<=window->nPanes; i++) { 5304 text = i==0 ? window->textArea : window->textPanes[i-1]; 5305 setPaneDesiredHeight(containingPane(text), paneHeights[i]); 5306 } 5307 5308 /* Re-manage panedWindow to recalculate pane heights & reset selection */ 5309 XtManageChild(window->splitPane); 5310 } 5311 5312 /* Reset all of the heights, scroll positions, etc. */ 5313 for (i=0; i<=window->nPanes; i++) { 5314 textDisp *paneTextD; 5315 5316 text = i==0 ? window->textArea : window->textPanes[i-1]; 5317 TextSetCursorPos(text, insertPositions[i]); 5318 TextSetScroll(text, topLines[i], horizOffsets[i]); 5319 5320 /* dim the cursor */ 5321 paneTextD = ((TextWidget)text)->text.textD; 5322 TextDSetCursorStyle(paneTextD, DIM_CURSOR); 5323 TextDUnblankCursor(paneTextD); 5324 } 5325 5326 /* set the focus pane */ 5327 for (i=0; i<=window->nPanes; i++) { 5328 text = i==0 ? window->textArea : window->textPanes[i-1]; 5329 if(i == focusPane) { 5330 window->lastFocus = text; 5331 XmProcessTraversal(text, XmTRAVERSE_CURRENT); 5332 break; 5333 } 5334 } 5335 5336 /* Update the window manager size hints after the sizes of the panes have 5337 been set (the widget heights are not yet readable here, but they will 5338 be by the time the event loop gets around to running this timer proc) */ 5339 XtAppAddTimeOut(XtWidgetToApplicationContext(window->shell), 0, 5340 wmSizeUpdateProc, window); 5341 } 5342 5343 /* 5344 ** clone a document's states and settings into the other. 5345 */ 5346 static void cloneDocument(WindowInfo *window, WindowInfo *orgWin) 5347 { 5348 const char *orgDocument; 5349 char *params[4]; 5350 int emTabDist; 5351 5352 strcpy(window->path, orgWin->path); 5353 strcpy(window->filename, orgWin->filename); 5354 strcpy(window->encoding, orgWin->encoding); 5355 5356 if(orgWin->filter) { 5357 NEditFree(window->filter); 5358 window->filter = NEditStrdup(orgWin->filter); 5359 } 5360 5361 ShowLineNumbers(window, orgWin->showLineNumbers); 5362 5363 window->ignoreModify = True; 5364 5365 /* copy the text buffer */ 5366 orgDocument = BufAsString(orgWin->buffer); 5367 BufSetAll(window->buffer, orgDocument); 5368 5369 /* copy the tab preferences (here!) */ 5370 BufSetTabDistance(window->buffer, orgWin->buffer->tabDist); 5371 window->buffer->useTabs = orgWin->buffer->useTabs; 5372 XtVaGetValues(orgWin->textArea, textNemulateTabs, &emTabDist, NULL); 5373 SetEmTabDist(window, emTabDist); 5374 5375 window->ignoreModify = False; 5376 5377 /* transfer text fonts */ 5378 params[0] = orgWin->fontName; 5379 params[1] = orgWin->italicFontName; 5380 params[2] = orgWin->boldFontName; 5381 params[3] = orgWin->boldItalicFontName; 5382 XtCallActionProc(window->textArea, "set_fonts", NULL, params, 4); 5383 5384 window->zoom = orgWin->zoom; 5385 5386 SetHighlightCursorLine(window, orgWin->highlightCursorLine); 5387 SetIndentRainbow(window, orgWin->indentRainbow); 5388 SetAnsiColors(window, orgWin->ansiColors); 5389 SetBacklightChars(window, orgWin->backlightCharTypes); 5390 5391 /* Clone rangeset info. 5392 5393 FIXME: 5394 Cloning of rangesets must be done before syntax highlighting, 5395 else the rangesets do not be highlighted (colored) properly 5396 if syntax highlighting is on. 5397 */ 5398 window->buffer->rangesetTable = 5399 RangesetTableClone(orgWin->buffer->rangesetTable, window->buffer); 5400 5401 /* Syntax highlighting */ 5402 window->languageMode = orgWin->languageMode; 5403 window->highlightSyntax = orgWin->highlightSyntax; 5404 if (window->highlightSyntax) 5405 StartHighlighting(window, False); 5406 5407 /* copy states of original document */ 5408 window->filenameSet = orgWin->filenameSet; 5409 window->fileFormat = orgWin->fileFormat; 5410 window->lastModTime = orgWin->lastModTime; 5411 window->fileChanged = orgWin->fileChanged; 5412 window->fileMissing = orgWin->fileMissing; 5413 window->lockReasons = orgWin->lockReasons; 5414 window->autoSaveCharCount = orgWin->autoSaveCharCount; 5415 window->autoSaveOpCount = orgWin->autoSaveOpCount; 5416 window->undoOpCount = orgWin->undoOpCount; 5417 window->undoMemUsed = orgWin->undoMemUsed; 5418 window->autoSave = orgWin->autoSave; 5419 window->saveOldVersion = orgWin->saveOldVersion; 5420 window->wrapMode = orgWin->wrapMode; 5421 SetOverstrike(window, orgWin->overstrike); 5422 window->showMatchingStyle = orgWin->showMatchingStyle; 5423 window->matchSyntaxBased = orgWin->matchSyntaxBased; 5424 #if 0 5425 window->showStats = orgWin->showStats; 5426 window->showISearchLine = orgWin->showISearchLine; 5427 window->showLineNumbers = orgWin->showLineNumbers; 5428 window->modeMessageDisplayed = orgWin->modeMessageDisplayed; 5429 window->ignoreModify = orgWin->ignoreModify; 5430 window->windowMenuValid = orgWin->windowMenuValid; 5431 window->flashTimeoutID = orgWin->flashTimeoutID; 5432 window->wasSelected = orgWin->wasSelected; 5433 strcpy(window->fontName, orgWin->fontName); 5434 strcpy(window->italicFontName, orgWin->italicFontName); 5435 strcpy(window->boldFontName, orgWin->boldFontName); 5436 strcpy(window->boldItalicFontName, orgWin->boldItalicFontName); 5437 window->italicFontStruct = orgWin->italicFontStruct; 5438 window->boldFontStruct = orgWin->boldFontStruct; 5439 window->boldItalicFontStruct = orgWin->boldItalicFontStruct; 5440 window->markTimeoutID = orgWin->markTimeoutID; 5441 window->highlightData = orgWin->highlightData; 5442 window->shellCmdData = orgWin->shellCmdData; 5443 window->macroCmdData = orgWin->macroCmdData; 5444 window->smartIndentData = orgWin->smartIndentData; 5445 #endif 5446 window->iSearchHistIndex = orgWin->iSearchHistIndex; 5447 window->iSearchStartPos = orgWin->iSearchStartPos; 5448 window->replaceLastRegexCase = orgWin->replaceLastRegexCase; 5449 window->replaceLastLiteralCase = orgWin->replaceLastLiteralCase; 5450 window->iSearchLastRegexCase = orgWin->iSearchLastRegexCase; 5451 window->iSearchLastLiteralCase = orgWin->iSearchLastLiteralCase; 5452 window->findLastRegexCase = orgWin->findLastRegexCase; 5453 window->findLastLiteralCase = orgWin->findLastLiteralCase; 5454 window->device = orgWin->device; 5455 window->inode = orgWin->inode; 5456 window->fileClosedAtom = orgWin->fileClosedAtom; 5457 orgWin->fileClosedAtom = None; 5458 5459 /* copy the text/split panes settings, cursor pos & selection */ 5460 cloneTextPanes(window, orgWin); 5461 5462 /* copy undo & redo list */ 5463 window->undo = cloneUndoItems(orgWin->undo); 5464 window->redo = cloneUndoItems(orgWin->redo); 5465 5466 /* don't copy enc error list */ 5467 window->encErrors = NULL; 5468 window->numEncErrors = 0; 5469 window->posEncErrors = 0; 5470 5471 /* copy bookmarks */ 5472 window->nMarks = orgWin->nMarks; 5473 memcpy(&window->markTable, &orgWin->markTable, 5474 sizeof(Bookmark)*window->nMarks); 5475 5476 /* kick start the auto-indent engine */ 5477 window->indentStyle = NO_AUTO_INDENT; 5478 SetAutoIndent(window, orgWin->indentStyle); 5479 5480 /* synchronize window state to this document */ 5481 RefreshWindowStates(window); 5482 } 5483 5484 static UndoInfo *cloneUndoItems(UndoInfo *orgList) 5485 { 5486 UndoInfo *head = NULL, *undo, *clone, *last = NULL; 5487 5488 for (undo = orgList; undo; undo = undo->next) { 5489 clone = (UndoInfo *)NEditMalloc(sizeof(UndoInfo)); 5490 memcpy(clone, undo, sizeof(UndoInfo)); 5491 5492 if (undo->oldText) { 5493 clone->oldText = (char*)NEditMalloc(strlen(undo->oldText)+1); 5494 strcpy(clone->oldText, undo->oldText); 5495 } 5496 clone->next = NULL; 5497 5498 if (last) 5499 last->next = clone; 5500 else 5501 head = clone; 5502 5503 last = clone; 5504 } 5505 5506 return head; 5507 } 5508 5509 /* 5510 typedef struct WinGeometry { 5511 int cols; 5512 int rows; 5513 } WinGeometry; 5514 5515 static WinGeometry WindowGetGeometry(WindowInfo *window) 5516 { 5517 // get current window size 5518 Dimension twWidth, twHeight; 5519 int marginWidth, marginHeight, lineNumCols; 5520 NFont *font; 5521 XtVaGetValues( 5522 window->textArea, 5523 XmNwidth, &twWidth, 5524 XmNheight, &twHeight, 5525 textNXftFont, &font, 5526 textNmarginWidth, &marginWidth, 5527 textNmarginHeight, &marginHeight, 5528 textNlineNumCols, &lineNumCols, 5529 NULL); 5530 int charWidth = font->maxWidth; 5531 XftFont *xfont = FontDefault(font); 5532 5533 twWidth -= marginWidth*2 + (lineNumCols == 0 ? 0 : marginWidth + charWidth * lineNumCols); 5534 twHeight -= marginHeight * 2; 5535 5536 int currentCols = twWidth / charWidth; 5537 int currentRows = twHeight / (xfont->ascent + xfont->descent); 5538 5539 return (WinGeometry){ currentCols, currentRows }; 5540 } 5541 */ 5542 5543 /* 5544 ** spin off the document to a new window 5545 */ 5546 WindowInfo *DetachDocument(WindowInfo *window) 5547 { 5548 WindowInfo *win = NULL, *cloneWin; 5549 5550 if (NDocuments(window) < 2) 5551 return NULL; 5552 5553 /* raise another document in the same shell window if the window 5554 being detached is the top document */ 5555 if (IsTopDocument(window)) { 5556 win = getNextTabWindow(window, 1, 0, 0); 5557 RaiseDocument(win); 5558 } 5559 5560 //WinGeometry geometry = WindowGetGeometry(window); 5561 //char geometryStr[64]; 5562 //snprintf(geometryStr, 64, "%dx%d", geometry.cols, geometry.rows); 5563 5564 /* get current window size */ 5565 Dimension width, height; 5566 XtVaGetValues(window->shell, XmNwidth, &width, XmNheight, &height, NULL); 5567 5568 /* Create a new window */ 5569 cloneWin = CreateWindow(window->filename, NULL, False); 5570 5571 /* apply old window size to the new window */ 5572 XtVaSetValues(cloneWin->shell, XmNwidth, width, XmNheight, height, NULL); 5573 5574 /* CreateWindow() simply adds the new window's pointer to the 5575 head of WindowList. We need to adjust the detached window's 5576 pointer, so that macro functions such as focus_window("last") 5577 will travel across the documents per the sequence they're 5578 opened. The new doc will appear to replace it's former self 5579 as the old doc is closed. */ 5580 WindowList = cloneWin->next; 5581 cloneWin->next = window->next; 5582 window->next = cloneWin; 5583 5584 /* these settings should follow the detached document. 5585 must be done before cloning window, else the height 5586 of split panes may not come out correctly */ 5587 ShowISearchLine(cloneWin, window->showISearchLine); 5588 ShowStatsLine(cloneWin, window->showStats); 5589 5590 /* clone the document & its pref settings */ 5591 cloneDocument(cloneWin, window); 5592 5593 /* remove the document from the old window */ 5594 window->fileChanged = False; 5595 CloseFileAndWindow(window, NO_SBC_DIALOG_RESPONSE); 5596 5597 /* refresh former host window */ 5598 if (win) { 5599 RefreshWindowStates(win); 5600 } 5601 5602 /* this should keep the new document window fresh */ 5603 RefreshWindowStates(cloneWin); 5604 RefreshTabState(cloneWin); 5605 SortTabBar(cloneWin); 5606 5607 return cloneWin; 5608 } 5609 5610 /* 5611 ** Move document to an other window. 5612 ** 5613 ** the moving document will receive certain window settings from 5614 ** its new host, i.e. the window size, stats and isearch lines. 5615 */ 5616 WindowInfo *MoveDocument(WindowInfo *toWindow, WindowInfo *window) 5617 { 5618 WindowInfo *win = NULL, *cloneWin; 5619 5620 /* prepare to move document */ 5621 if (NDocuments(window) < 2) { 5622 /* hide the window to make it look like we are moving */ 5623 XtUnmapWidget(window->shell); 5624 } 5625 else if (IsTopDocument(window)) { 5626 /* raise another document to replace the document being moved */ 5627 win = getNextTabWindow(window, 1, 0, 0); 5628 RaiseDocument(win); 5629 } 5630 5631 /* relocate the document to target window */ 5632 cloneWin = CreateDocument(toWindow, window->filename); 5633 ShowTabBar(cloneWin, GetShowTabBar(cloneWin)); 5634 cloneDocument(cloneWin, window); 5635 5636 /* CreateDocument() simply adds the new window's pointer to the 5637 head of WindowList. We need to adjust the detached window's 5638 pointer, so that macro functions such as focus_window("last") 5639 will travel across the documents per the sequence they're 5640 opened. The new doc will appear to replace it's former self 5641 as the old doc is closed. */ 5642 WindowList = cloneWin->next; 5643 cloneWin->next = window->next; 5644 window->next = cloneWin; 5645 5646 /* remove the document from the old window */ 5647 window->fileChanged = False; 5648 CloseFileAndWindow(window, NO_SBC_DIALOG_RESPONSE); 5649 5650 /* some menu states might have changed when deleting document */ 5651 if (win) 5652 RefreshWindowStates(win); 5653 5654 /* this should keep the new document window fresh */ 5655 RaiseDocumentWindow(cloneWin); 5656 RefreshTabState(cloneWin); 5657 SortTabBar(cloneWin); 5658 5659 return cloneWin; 5660 } 5661 5662 static void moveDocumentCB(Widget dialog, WindowInfo *window, 5663 XtPointer call_data) 5664 { 5665 XmSelectionBoxCallbackStruct *cbs = (XmSelectionBoxCallbackStruct *) call_data; 5666 DoneWithMoveDocumentDialog = cbs->reason; 5667 } 5668 5669 /* 5670 ** present dialog for selecting a target window to move this document 5671 ** into. Do nothing if there is only one shell window opened. 5672 */ 5673 void MoveDocumentDialog(WindowInfo *window) 5674 { 5675 WindowInfo *win, *targetWin, **shellWinList; 5676 int i, nList=0, nWindows=0, ac; 5677 char tmpStr[2*MAXPATHLEN]; 5678 Widget parent, dialog, listBox, moveAllOption; 5679 XmString *list = NULL; 5680 XmString popupTitle, s1; 5681 Arg csdargs[20]; 5682 int *position_list, position_count; 5683 5684 /* get the list of available shell windows, not counting 5685 the document to be moved */ 5686 nWindows = NWindows(); 5687 list = (XmStringTable) NEditMalloc(nWindows * sizeof(XmString *)); 5688 shellWinList = (WindowInfo **) NEditMalloc(nWindows * sizeof(WindowInfo *)); 5689 5690 for (win=WindowList; win; win=win->next) { 5691 if (!IsTopDocument(win) || win->shell == window->shell) 5692 continue; 5693 5694 snprintf(tmpStr, sizeof(tmpStr), "%s%s", 5695 win->filenameSet? win->path : "", win->filename); 5696 5697 list[nList] = XmStringCreateSimple(tmpStr); 5698 shellWinList[nList] = win; 5699 nList++; 5700 } 5701 5702 /* stop here if there's no other window to move to */ 5703 if (!nList) { 5704 NEditFree(list); 5705 NEditFree(shellWinList); 5706 return; 5707 } 5708 5709 /* create the dialog */ 5710 parent = window->shell; 5711 popupTitle = XmStringCreateSimple("Move Document"); 5712 snprintf(tmpStr, sizeof(tmpStr), 5713 "Move %s into window of", window->filename); 5714 s1 = XmStringCreateSimple(tmpStr); 5715 ac = 0; 5716 XtSetArg(csdargs[ac], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); ac++; 5717 XtSetArg(csdargs[ac], XmNdialogTitle, popupTitle); ac++; 5718 XtSetArg(csdargs[ac], XmNlistLabelString, s1); ac++; 5719 XtSetArg(csdargs[ac], XmNlistItems, list); ac++; 5720 XtSetArg(csdargs[ac], XmNlistItemCount, nList); ac++; 5721 XtSetArg(csdargs[ac], XmNvisibleItemCount, 12); ac++; 5722 XtSetArg(csdargs[ac], XmNautoUnmanage, False); ac++; 5723 dialog = CreateSelectionDialog(parent,"moveDocument",csdargs,ac); 5724 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT)); 5725 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON)); 5726 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_SELECTION_LABEL)); 5727 XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)moveDocumentCB, window); 5728 XtAddCallback(dialog, XmNapplyCallback, (XtCallbackProc)moveDocumentCB, window); 5729 XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)moveDocumentCB, window); 5730 XmStringFree(s1); 5731 XmStringFree(popupTitle); 5732 5733 /* free the window list */ 5734 for (i=0; i<nList; i++) 5735 XmStringFree(list[i]); 5736 NEditFree(list); 5737 5738 /* create the option box for moving all documents */ 5739 s1 = MKSTRING("Move all documents in this window"); 5740 moveAllOption = XtVaCreateWidget("moveAll", 5741 xmToggleButtonWidgetClass, dialog, 5742 XmNlabelString, s1, 5743 XmNalignment, XmALIGNMENT_BEGINNING, 5744 NULL); 5745 XmStringFree(s1); 5746 5747 if (NDocuments(window) >1) 5748 XtManageChild(moveAllOption); 5749 5750 /* disable option if only one document in the window */ 5751 XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_APPLY_BUTTON)); 5752 5753 s1 = MKSTRING("Move"); 5754 XtVaSetValues (dialog, XmNokLabelString, s1, NULL); 5755 XmStringFree(s1); 5756 5757 /* default to the first window on the list */ 5758 listBox = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST); 5759 XmListSelectPos(listBox, 1, True); 5760 5761 /* show the dialog */ 5762 DoneWithMoveDocumentDialog = 0; 5763 ManageDialogCenteredOnPointer(dialog); 5764 while (!DoneWithMoveDocumentDialog) 5765 XtAppProcessEvent(XtWidgetToApplicationContext(parent), XtIMAll); 5766 5767 /* get the window to move document into */ 5768 XmListGetSelectedPos(listBox, &position_list, &position_count); 5769 targetWin = shellWinList[position_list[0]-1]; 5770 NEditFree(position_list); 5771 5772 /* now move document(s) */ 5773 if (DoneWithMoveDocumentDialog == XmCR_OK) { 5774 /* move top document */ 5775 if (XmToggleButtonGetState(moveAllOption)) { 5776 /* move all documents */ 5777 for (win = WindowList; win; ) { 5778 if (win != window && win->shell == window->shell) { 5779 WindowInfo *next = win->next; 5780 MoveDocument(targetWin, win); 5781 win = next; 5782 } 5783 else 5784 win = win->next; 5785 } 5786 5787 /* invoking document is the last to move */ 5788 MoveDocument(targetWin, window); 5789 } 5790 else { 5791 MoveDocument(targetWin, window); 5792 } 5793 } 5794 5795 NEditFree(shellWinList); 5796 XtDestroyWidget(dialog); 5797 } 5798 5799 static void hideTooltip(Widget tab) 5800 { 5801 Widget tooltip = XtNameToWidget(tab, "*BubbleShell"); 5802 5803 if (tooltip) 5804 XtPopdown(tooltip); 5805 } 5806 5807 static void closeTabProc(XtPointer clientData, XtIntervalId *id) 5808 { 5809 CloseFileAndWindow((WindowInfo*)clientData, PROMPT_SBC_DIALOG_RESPONSE); 5810 } 5811 5812 /* 5813 ** callback to close-tab button. 5814 */ 5815 static void closeTabCB(Widget w, Widget mainWin, caddr_t callData) 5816 { 5817 /* FIXME: XtRemoveActionHook() related coredump 5818 5819 An unknown bug seems to be associated with the XtRemoveActionHook() 5820 call in FinishLearn(), which resulted in coredump if a tab was 5821 closed, in the middle of keystrokes learning, by clicking on the 5822 close-tab button. 5823 5824 As evident to our accusation, the coredump may be surpressed by 5825 simply commenting out the XtRemoveActionHook() call. The bug was 5826 consistent on both Motif and Lesstif on various platforms. 5827 5828 Closing the tab through either the "Close" menu or its accel key, 5829 however, was without any trouble. 5830 5831 While its actual mechanism is not well understood, we somehow 5832 managed to workaround the bug by delaying the action of closing 5833 the tab. For now. */ 5834 XtAppAddTimeOut(XtWidgetToApplicationContext(w), 0, 5835 closeTabProc, GetTopDocument(mainWin)); 5836 } 5837 5838 /* 5839 ** callback to clicks on a tab to raise it's document. 5840 */ 5841 static void raiseTabCB(Widget w, XtPointer clientData, XtPointer callData) 5842 { 5843 XmLFolderCallbackStruct *cbs = (XmLFolderCallbackStruct *)callData; 5844 WidgetList tabList; 5845 Widget tab; 5846 5847 XtVaGetValues(w, XmNtabWidgetList, &tabList, NULL); 5848 tab = tabList[cbs->pos]; 5849 RaiseDocument(TabToWindow(tab)); 5850 } 5851 5852 static Widget containingPane(Widget w) 5853 { 5854 /* The containing pane used to simply be the first parent, but with 5855 the introduction of an XmFrame, it's the grandparent. */ 5856 return XtParent(XtParent(w)); 5857 } 5858 5859 static void cancelTimeOut(XtIntervalId *timer) 5860 { 5861 if (*timer != 0) 5862 { 5863 XtRemoveTimeOut(*timer); 5864 *timer = 0; 5865 } 5866 } 5867 5868 /* 5869 ** set/clear toggle menu state if the calling document is on top. 5870 */ 5871 void SetToggleButtonState(WindowInfo *window, Widget w, Boolean state, 5872 Boolean notify) 5873 { 5874 if (IsTopDocument(window)) { 5875 XmToggleButtonSetState(w, state, notify); 5876 } 5877 } 5878 5879 /* 5880 ** set/clear menu sensitivity if the calling document is on top. 5881 */ 5882 void SetSensitive(WindowInfo *window, Widget w, Boolean sensitive) 5883 { 5884 if (IsTopDocument(window)) { 5885 XtSetSensitive(w, sensitive); 5886 } 5887 } 5888 5889 /* 5890 ** Remove redundant expose events on tab bar. 5891 */ 5892 void CleanUpTabBarExposeQueue(WindowInfo *window) 5893 { 5894 XEvent event; 5895 XExposeEvent ev; 5896 int count; 5897 5898 if (window == NULL) 5899 return; 5900 5901 /* remove redundant expose events on tab bar */ 5902 count=0; 5903 while (XCheckTypedWindowEvent(TheDisplay, XtWindow(window->tabBar), 5904 Expose, &event)) 5905 count++; 5906 5907 /* now we can update tabbar */ 5908 if (count) { 5909 ev.type = Expose; 5910 ev.display = TheDisplay; 5911 ev.window = XtWindow(window->tabBar); 5912 ev.x = 0; 5913 ev.y = 0; 5914 ev.width = XtWidth(window->tabBar); 5915 ev.height = XtHeight(window->tabBar); 5916 ev.count = 0; 5917 XSendEvent(TheDisplay, XtWindow(window->tabBar), False, 5918 ExposureMask, (XEvent *)&ev); 5919 } 5920 } 5921 5922 void SetEncoding(WindowInfo *window, const char *encoding) 5923 { 5924 size_t len = strlen(encoding); 5925 if(len >= MAX_ENCODING_LENGTH) { 5926 fprintf(stderr, "Error: Encoding string too large\n"); 5927 return; 5928 } 5929 5930 if(encoding == window->encoding) { 5931 // noop 5932 return; 5933 } 5934 5935 memcpy(window->encoding, encoding, len); 5936 window->encoding[len] = '\0'; 5937 } 5938 5939 void SetFilter(WindowInfo *window, const char *filter) 5940 { 5941 if(window->filter == filter) { 5942 // noop 5943 return; 5944 } 5945 NEditFree(window->filter); 5946 window->filter = NEditStrdup(filter); 5947 } 5948 5949 #define MIN_FONT_SIZE 2 5950 #define MAX_FONT_SIZE 800 5951 void SetZoom(WindowInfo *window, int step) 5952 { 5953 int font_sz = window->font->size + step; 5954 int italic_sz = window->italicFont->size + step; 5955 int bold_sz = window->boldFont->size + step; 5956 int bolditalic_sz = window->italicFont->size + step; 5957 5958 if( 5959 font_sz < MIN_FONT_SIZE || italic_sz < MIN_FONT_SIZE || 5960 bold_sz < MIN_FONT_SIZE || bolditalic_sz < MIN_FONT_SIZE || 5961 font_sz > MAX_FONT_SIZE || italic_sz > MAX_FONT_SIZE || 5962 bold_sz > MAX_FONT_SIZE || bolditalic_sz > MAX_FONT_SIZE) 5963 { 5964 return; 5965 } 5966 5967 window->zoom += step; 5968 5969 char *font = ChangeFontSize(window->fontName, font_sz); 5970 char *italic = ChangeFontSize(window->italicFontName, italic_sz); 5971 char *bold = ChangeFontSize(window->boldFontName, italic_sz); 5972 char *bolditalic = ChangeFontSize(window->boldItalicFontName, italic_sz); 5973 5974 Boolean rz = window->resizeOnFontChange; 5975 window->resizeOnFontChange = False; // disable window resizing on font change 5976 SetFonts(window, font, italic, bold, bolditalic); 5977 window->resizeOnFontChange = rz; 5978 5979 XtSetSensitive(window->resetZoomItem, window->zoom == 0 ? False : True); 5980 5981 NEditFree(font); 5982 NEditFree(italic); 5983 NEditFree(bold); 5984 NEditFree(bolditalic); 5985 } 5986 5987 /* 5988 * set the encoding error list 5989 * note: this will not copy the array, so don't free the pointer outside 5990 * of the window 5991 */ 5992 void SetEncErrors(WindowInfo *window, EncError *errors, size_t numErrors) 5993 { 5994 window->encErrors = errors; 5995 window->numEncErrors = numErrors; 5996 5997 if(numErrors == 0) { 5998 XtVaSetValues(window->encInfoErrorList, XmNitemCount, 0, XmNitems, NULL, NULL); 5999 XtUnmanageChild(window->encInfoErrorList); 6000 return; 6001 } 6002 char buf[256]; 6003 6004 XmStringTable strErrors = NEditCalloc(numErrors, sizeof(XmString)); 6005 for(size_t i=0;i<numErrors;i++) { 6006 snprintf(buf, 256, "0x%02X", errors[i].c); 6007 strErrors[i] = XmStringCreateSimple(buf); 6008 } 6009 6010 XtVaSetValues( 6011 window->encInfoErrorList, 6012 XmNitemCount, numErrors, 6013 XmNitems, strErrors, 6014 NULL); 6015 6016 // cleanup 6017 for(size_t i=0;i<numErrors;i++) { 6018 XmStringFree(strErrors[i]); 6019 } 6020 NEditFree(strErrors); 6021 6022 XtManageChild(window->encInfoErrorList); 6023 } 6024 6025 static void WindowTakeFocus(Widget shell, WindowInfo *window, XtPointer d) 6026 { 6027 window->opened = True; 6028 6029 XmRemoveWMProtocolCallback( 6030 shell, 6031 wm_take_focus, 6032 (XtCallbackProc)WindowTakeFocus, 6033 window); 6034 } 6035 6036 6037 static void closeInfoBarCB(Widget w, Widget mainWin, void *callData) 6038 { 6039 WindowInfo *window = GetTopDocument(mainWin); 6040 if(!window) { 6041 return; 6042 } 6043 window->showInfoBar = FALSE; 6044 XtUnmanageChild(window->encodingInfoBar); 6045 showStatsForm(window); 6046 } 6047 6048 static void jumpToEncErrorCB(Widget w, Widget mainWin, XmComboBoxCallbackStruct *cb) 6049 { 6050 WindowInfo *window = GetTopDocument(mainWin); 6051 if(!window) { 6052 return; 6053 } 6054 6055 if(cb->item_position >= window->numEncErrors) { 6056 return; 6057 } 6058 6059 EncError e = window->encErrors[cb->item_position]; 6060 // +3 because the unicode replacement char is encoded with 3 bytes in utf8 6061 BufSelect(window->buffer, e.pos, e.pos+3); 6062 MakeSelectionVisible(window, window->lastFocus); 6063 TextSetCursorPos(window->lastFocus, e.pos); 6064 } 6065 6066 static void reloadCB(Widget w, Widget mainWin, void *callData) 6067 { 6068 WindowInfo *window = GetTopDocument(mainWin); 6069 if(!window) { 6070 return; 6071 } 6072 6073 if(window->fileChanged) { 6074 int b = DialogF(DF_QUES, window->shell, 2, "Discard Changes", 6075 "Discard changes to\n%s%s?", "OK", "Cancel", window->path, 6076 window->filename); 6077 if(b == 2) { 6078 return; 6079 } 6080 } 6081 6082 char *encoding = NULL; 6083 6084 XmString item; 6085 XtVaGetValues(window->encInfoBarList, XmNselectedItem, &item, NULL); 6086 if(!item) { 6087 return; 6088 } 6089 6090 // convert XmString to char* 6091 XmStringGetLtoR(item, XmFONTLIST_DEFAULT_TAG, &encoding); 6092 6093 // reload file with new encoding 6094 char *enc = strcmp(encoding, "detect") ? encoding : NULL; 6095 6096 RevertToSaved(window, enc); 6097 6098 // cleanup 6099 XmStringFree(item); 6100 XtFree(encoding); 6101 } 6102 6103 static void updateWindowMapStatus(Widget widget, Boolean status) { 6104 for (WindowInfo *w=WindowList; w!=NULL; w=w->next) { 6105 if(w->shell == widget) { 6106 w->mapped = status; 6107 } 6108 } 6109 } 6110 6111 static void windowStructureNotifyEventEH( 6112 Widget widget, 6113 XtPointer data, 6114 XEvent *event, 6115 Boolean *dispatch) 6116 { 6117 //WindowInfo *window = data; 6118 if(event->type == UnmapNotify) { 6119 updateWindowMapStatus(widget, False); 6120 InvalidateWindowMenus(); 6121 } else if(event->type == MapNotify) { 6122 updateWindowMapStatus(widget, True); 6123 InvalidateWindowMenus(); 6124 } 6125 } 6126 6127 6128 6129 typedef struct CPDummyWindow { 6130 Widget shell; 6131 Widget mainWin; 6132 Widget form; 6133 Widget menubar; 6134 Widget button; 6135 Widget togglebutton; 6136 Widget label; 6137 Widget textfield1; 6138 Widget textfield2; 6139 Widget textfield3; 6140 Widget scrollbar; 6141 Widget folder; 6142 Widget dropdown; 6143 } CPDummyWindow; 6144 6145 static void clearCompositeWidget(Widget w) 6146 { 6147 WidgetList children; 6148 Cardinal numChildren; 6149 XtVaGetValues(w, XmNchildren, &children, XmNnumChildren, &numChildren, NULL); 6150 for(int i=0;i<numChildren;i++) { 6151 XtUnmanageChild(children[i]); 6152 XtDestroyWidget(children[i]); 6153 } 6154 } 6155 6156 static void UpdateWidgetValues(Widget dst, Widget src) 6157 { 6158 if(dst->core.widget_class == textWidgetClass) { 6159 return; // textWidgetClass is updated separately 6160 } 6161 6162 Pixel background, foreground, topShadowColor, bottomShadowColor, highlightColor, armColor; 6163 Pixel selectBG, selectFG, troughColor, blankBackground, inactiveBackground, inactiveForeground; 6164 Dimension shadowThickness, highlightThickness; 6165 XtVaGetValues(src, 6166 XmNbackground, &background, 6167 XmNforeground, &foreground, 6168 XmNtopShadowColor, &topShadowColor, 6169 XmNbottomShadowColor, &bottomShadowColor, 6170 XmNhighlightColor, &highlightColor, 6171 XmNarmColor, &armColor, 6172 XmNshadowThickness, &shadowThickness, 6173 XmNhighlightThickness, &highlightThickness, 6174 XmNselectBackground, &selectBG, 6175 XmNselectForeground, &selectFG, 6176 XmNtroughColor, &troughColor, 6177 XmNblankBackground, &blankBackground, 6178 XmNinactiveBackground, &inactiveBackground, 6179 XmNinactiveForeground, &inactiveForeground, 6180 NULL); 6181 6182 XtVaSetValues(dst, 6183 XmNbackground, background, 6184 XmNforeground, foreground, 6185 XmNtopShadowColor, topShadowColor, 6186 XmNbottomShadowColor, bottomShadowColor, 6187 XmNhighlightColor, highlightColor, 6188 XmNarmColor, armColor, 6189 XmNshadowThickness, shadowThickness, 6190 XmNhighlightThickness, highlightThickness, 6191 XmNselectBackground, selectBG, 6192 XmNselectForeground, selectFG, 6193 XmNtroughColor, troughColor, 6194 XmNblankBackground, blankBackground, 6195 XmNinactiveBackground, inactiveBackground, 6196 XmNinactiveForeground, inactiveForeground, 6197 NULL); 6198 } 6199 6200 6201 6202 static void UpdateWidgetsHierarchy(Widget parent, Widget src, CPDummyWindow *template) 6203 { 6204 UpdateWidgetValues(parent, src); 6205 6206 Widget srcWidgets[] = { 6207 template->label, template->button, template->togglebutton, 6208 template->textfield1, template->textfield2, template->textfield3, 6209 template->scrollbar, template->folder, template->dropdown }; 6210 size_t numSrcWidgets = 9; 6211 6212 WidgetList children = NULL; 6213 Cardinal numChildren = 0; 6214 XtVaGetValues(parent, XmNchildren, &children, XmNnumChildren, &numChildren, NULL); 6215 for(int i=0;i<numChildren;i++) { 6216 Widget dst = children[i]; 6217 Widget src = template->togglebutton; 6218 for(int s=0;s<numSrcWidgets;s++) { 6219 if(children[i]->core.widget_class == srcWidgets[s]->core.widget_class) { 6220 src = srcWidgets[s]; 6221 break; 6222 } 6223 } 6224 UpdateWidgetsHierarchy(dst, src, template); 6225 } 6226 } 6227 6228 // Destroy the current horizontal and vertical scrollbar 6229 // and create a new pair of scrollbars for the given textArea 6230 static void RecreateTextareaScrollbar(Widget textArea) 6231 { 6232 Widget textAreaFrame = XtParent(textArea); 6233 Widget textAreaScrolledWindow = XtParent(textAreaFrame); 6234 6235 // get the scrollbars and the current values 6236 Widget oldHscrollbar = NULL; 6237 Widget oldVscrollbar = NULL; 6238 XtVaGetValues(textAreaScrolledWindow, XmNhorizontalScrollBar, &oldHscrollbar, XmNverticalScrollBar, &oldVscrollbar, NULL); 6239 if(!oldHscrollbar || !oldVscrollbar) { 6240 fprintf(stderr, "Error: cannot update scrollbars\n"); 6241 return; 6242 } 6243 int hincrement, hmin, hmax, hpageIncrement, hsliderSize; 6244 int vincrement, vmin, vmax, vpageIncrement, vsliderSize; 6245 XtVaGetValues(oldHscrollbar, XmNincrement, &hincrement, XmNminimum, &hmin, XmNmaximum, &hmax, XmNpageIncrement, &hpageIncrement, XmNsliderSize, &hsliderSize, NULL); 6246 XtVaGetValues(oldVscrollbar, XmNincrement, &vincrement, XmNminimum, &vmin, XmNmaximum, &vmax, XmNpageIncrement, &vpageIncrement, XmNsliderSize, &vsliderSize, NULL); 6247 6248 int hIsManaged = XtIsManaged(oldHscrollbar); 6249 int vIsManaged = XtIsManaged(oldVscrollbar); 6250 6251 // unmanage and destroy 6252 XtUnmanageChild(oldHscrollbar); 6253 XtUnmanageChild(oldVscrollbar); 6254 // when the old scrollbar is destroyed, the new scrollbar doesn't work correctly 6255 //XtDestroyWidget(oldHscrollbar); 6256 //XtDestroyWidget(oldVscrollbar); 6257 6258 // create new scrollbars 6259 Widget newHscrollbar = XtVaCreateManagedWidget( 6260 "textHorScrollBar", 6261 xmScrollBarWidgetClass, textAreaScrolledWindow, XmNorientation, XmHORIZONTAL, 6262 XmNrepeatDelay, 10, NULL); 6263 Widget newVscrollbar = XtVaCreateManagedWidget( 6264 "textVertScrollBar", 6265 xmScrollBarWidgetClass, textAreaScrolledWindow, XmNorientation, XmVERTICAL, 6266 XmNrepeatDelay, 10, NULL); 6267 XtVaSetValues(newHscrollbar, XmNincrement, hincrement, XmNminimum, hmin, XmNmaximum, hmax, XmNpageIncrement, hpageIncrement, XmNsliderSize, hsliderSize, NULL); 6268 XtVaSetValues(newVscrollbar, XmNincrement, vincrement, XmNminimum, vmin, XmNmaximum, vmax, XmNpageIncrement, vpageIncrement, XmNsliderSize, vsliderSize, NULL); 6269 6270 XtVaSetValues(textAreaScrolledWindow, XmNhorizontalScrollBar, 6271 newHscrollbar, XmNverticalScrollBar, newVscrollbar, NULL); 6272 XtVaSetValues(textArea, textNhScrollBar, newHscrollbar, textNvScrollBar, newVscrollbar, NULL); 6273 6274 if(!hIsManaged) { 6275 XtUnmanageChild(newHscrollbar); 6276 } 6277 if(!vIsManaged) { 6278 XtUnmanageChild(newVscrollbar); 6279 } 6280 } 6281 6282 void ReloadWindowResources(WindowInfo *window, Boolean updateMenuBar) 6283 { 6284 CPDummyWindow dw; 6285 dw.shell = CreateWidget(TheAppShell, "textShell", topLevelShellWidgetClass, NULL, 0); 6286 dw.mainWin = XmCreateMainWindow(dw.shell, "main", NULL, 0); 6287 dw.menubar = XmCreateMenuBar(dw.mainWin, "menuBar", NULL, 0); 6288 dw.form = XmCreateForm(dw.mainWin, "form", NULL, 0); 6289 dw.button = XmCreatePushButton(dw.form, "button", NULL, 0); 6290 dw.togglebutton = XmCreateToggleButton(dw.form, "togglebutton", NULL, 0); 6291 dw.label = XmCreateLabel(dw.form, "label", NULL, 0); 6292 dw.textfield1 = XmCreateTextField(dw.form, "textfield1", NULL, 0); 6293 dw.textfield2 = XmCreateText(dw.form, "textfield2", NULL, 0); 6294 dw.textfield3 = XNECreateTextField(dw.form, "textfield3", NULL, 0); 6295 dw.scrollbar = XmCreateScrollBar(dw.form, "scrollbar", NULL, 0); 6296 dw.folder = XtVaCreateManagedWidget("tabBar", xmlFolderWidgetClass, dw.form, NULL); 6297 dw.dropdown = XtVaCreateManagedWidget("dropDown", xmComboBoxWidgetClass, dw.form, NULL); 6298 6299 if(updateMenuBar) { 6300 UpdateWidgetValues(window->menuBar, dw.menubar); 6301 RecreateMenuBar(window->mainWin, window->menuBar, window, True); 6302 refreshMenuBar(window); 6303 } 6304 6305 UpdateWidgetValues(window->mainWin, dw.mainWin); 6306 Widget winForm = XtParent(window->iSearchForm); 6307 UpdateWidgetValues(winForm, dw.form); 6308 6309 UpdateWidgetsHierarchy(window->encodingInfoBar, dw.form, &dw); 6310 6311 UpdateWidgetsHierarchy(window->iSearchForm, dw.form, &dw); 6312 clearCompositeWidget(window->iSearchForm); 6313 createSearchForm(window); 6314 6315 Widget tabbar = window->tabBar; 6316 Widget tabform = XtParent(tabbar); 6317 UpdateWidgetValues(tabform, dw.form); 6318 6319 UpdateWidgetsHierarchy(window->statsLineForm, dw.form, &dw); 6320 UpdateWidgetsHierarchy(window->tabBar, dw.folder, &dw); 6321 UpdateWidgetsHierarchy(window->splitPane, dw.form, &dw); 6322 6323 XtDestroyWidget(dw.shell); 6324 } 6325