UNIXworkcode

1 /******************************************************************************* 2 * * 3 * text.c - Display text from a text buffer * 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 * June 15, 1995 * 24 * * 25 *******************************************************************************/ 26 27 #ifdef HAVE_CONFIG_H 28 #include "../config.h" 29 #endif 30 31 #include "text.h" 32 #include "textP.h" 33 #include "textBuf.h" 34 #include "textDisp.h" 35 #include "textSel.h" 36 #include "textDrag.h" 37 #include "nedit.h" 38 #include "calltips.h" 39 #include "../util/DialogF.h" 40 #include "../util/xdnd.h" 41 #include "window.h" 42 #include "preferences.h" 43 #include "../util/nedit_malloc.h" 44 #include "help.h" 45 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <limits.h> 49 #include <string.h> 50 #include <ctype.h> 51 #include <sys/param.h> 52 #include <limits.h> 53 54 #include <X11/Intrinsic.h> 55 #include <X11/IntrinsicP.h> 56 #include <X11/StringDefs.h> 57 #include <X11/cursorfont.h> 58 #include <Xm/Xm.h> 59 #include <Xm/XmP.h> 60 #if XmVersion >= 1002 61 #include <Xm/PrimitiveP.h> 62 #endif 63 64 #ifndef XK_Control_L 65 #include <X11/keysym.h> 66 #endif 67 68 #ifdef HAVE_DEBUG_H 69 #include "../debug.h" 70 #endif 71 72 73 #ifdef UNICOS 74 #define XtOffset(p_type,field) ((size_t)__INTADDR__(&(((p_type)0)->field))) 75 #endif 76 77 /* Number of pixels of motion from the initial (grab-focus) button press 78 required to begin recognizing a mouse drag for the purpose of making a 79 selection */ 80 #define SELECT_THRESHOLD 5 81 82 /* Length of delay in milliseconds for vertical autoscrolling */ 83 #define VERTICAL_SCROLL_DELAY 50 84 85 static void initialize(TextWidget request, TextWidget new); 86 static void handleHidePointer(Widget w, XtPointer unused, 87 XEvent *event, Boolean *continue_to_dispatch); 88 static void handleShowPointer(Widget w, XtPointer unused, 89 XEvent *event, Boolean *continue_to_dispatch); 90 static void redisplay(TextWidget w, XEvent *event, Region region); 91 static void redisplayGE(TextWidget w, XtPointer client_data, 92 XEvent *event, Boolean *continue_to_dispatch_return); 93 static void hscrollEH(TextWidget w, XtPointer client_data, 94 XEvent *event, Boolean *continue_to_dispatch_return); 95 static void destroy(TextWidget w); 96 static void resize(TextWidget w); 97 static Boolean setValues(TextWidget current, TextWidget request, 98 TextWidget new); 99 static void realize(Widget w, XtValueMask *valueMask, 100 XSetWindowAttributes *attributes); 101 static XtGeometryResult queryGeometry(Widget w, XtWidgetGeometry *proposed, 102 XtWidgetGeometry *answer); 103 static void grabFocusAP(Widget w, XEvent *event, String *args, 104 Cardinal *n_args); 105 static void cancelAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); 106 static void moveDestinationAP(Widget w, XEvent *event, String *args, 107 Cardinal *nArgs); 108 static void extendAdjustAP(Widget w, XEvent *event, String *args, 109 Cardinal *nArgs); 110 static void extendStartAP(Widget w, XEvent *event, String *args, 111 Cardinal *nArgs); 112 static void extendEndAP(Widget w, XEvent *event, String *args, 113 Cardinal *nArgs); 114 static void processCancelAP(Widget w, XEvent *event, String *args, 115 Cardinal *nArgs); 116 static void secondaryStartAP(Widget w, XEvent *event, String *args, 117 Cardinal *nArgs); 118 static void secondaryOrDragStartAP(Widget w, XEvent *event, String *args, 119 Cardinal *nArgs); 120 static void secondaryAdjustAP(Widget w, XEvent *event, String *args, 121 Cardinal *nArgs); 122 static void secondaryOrDragAdjustAP(Widget w, XEvent *event, String *args, 123 Cardinal *nArgs); 124 static void copyToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); 125 static void copyToOrEndDragAP(Widget w, XEvent *event, String *args, 126 Cardinal *nArgs); 127 static void copyPrimaryAP(Widget w, XEvent *event, String *args, 128 Cardinal *nArgs); 129 static void cutPrimaryAP(Widget w, XEvent *event, String *args, 130 Cardinal *nArgs); 131 static void moveToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); 132 static void moveToOrEndDragAP(Widget w, XEvent *event, String *args, 133 Cardinal *nArgs); 134 static void endDragAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); 135 static void exchangeAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); 136 static void mousePanAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); 137 static void pasteClipboardAP(Widget w, XEvent *event, String *args, 138 Cardinal *nArgs); 139 static void copyClipboardAP(Widget w, XEvent *event, String *args, 140 Cardinal *nArgs); 141 static void cutClipboardAP(Widget w, XEvent *event, String *args, 142 Cardinal *nArgs); 143 static void insertStringAP(Widget w, XEvent *event, String *args, 144 Cardinal *nArgs); 145 static void selfInsertAP(Widget w, XEvent *event, String *args, 146 Cardinal *n_args); 147 static void newlineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); 148 static void newlineAndIndentAP(Widget w, XEvent *event, String *args, 149 Cardinal *nArgs); 150 static void newlineNoIndentAP(Widget w, XEvent *event, String *args, 151 Cardinal *nArgs); 152 static void processTabAP(Widget w, XEvent *event, String *args, 153 Cardinal *nArgs); 154 static void endOfLineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); 155 static void beginningOfLineAP(Widget w, XEvent *event, String *args, 156 Cardinal *nArgs); 157 static void deleteSelectionAP(Widget w, XEvent *event, String *args, 158 Cardinal *nArgs); 159 static void deletePreviousCharacterAP(Widget w, XEvent *event, String *args, 160 Cardinal *nArgs); 161 static void deleteNextCharacterAP(Widget w, XEvent *event, String *args, 162 Cardinal *nArgs); 163 static void deletePreviousWordAP(Widget w, XEvent *event, String *args, 164 Cardinal *nArgs); 165 static void deleteNextWordAP(Widget w, XEvent *event, String *args, 166 Cardinal *nArgs); 167 static void deleteToStartOfLineAP(Widget w, XEvent *event, String *args, 168 Cardinal *nArgs); 169 static void deleteToEndOfLineAP(Widget w, XEvent *event, String *args, 170 Cardinal *nArgs); 171 static void forwardCharacterAP(Widget w, XEvent *event, String *args, 172 Cardinal *nArgs); 173 static void backwardCharacterAP(Widget w, XEvent *event, String *args, 174 Cardinal *nArgs); 175 static void forwardWordAP(Widget w, XEvent *event, String *args, 176 Cardinal *nArgs); 177 static void backwardWordAP(Widget w, XEvent *event, String *args, 178 Cardinal *nArgs); 179 static void forwardParagraphAP(Widget w, XEvent *event, String *args, 180 Cardinal *nArgs); 181 static void backwardParagraphAP(Widget w, XEvent *event, String *args, 182 Cardinal *nArgs); 183 static void keySelectAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); 184 static void processUpAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); 185 static void processShiftUpAP(Widget w, XEvent *event, String *args, 186 Cardinal *nArgs); 187 static void processDownAP(Widget w, XEvent *event, String *args, 188 Cardinal *nArgs); 189 static void processShiftDownAP(Widget w, XEvent *event, String *args, 190 Cardinal *nArgs); 191 static void beginningOfFileAP(Widget w, XEvent *event, String *args, 192 Cardinal *nArgs); 193 static void endOfFileAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); 194 static void nextPageAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); 195 static void previousPageAP(Widget w, XEvent *event, String *args, 196 Cardinal *nArgs); 197 static void pageLeftAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); 198 static void pageRightAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); 199 static void toggleOverstrikeAP(Widget w, XEvent *event, String *args, 200 Cardinal *nArgs); 201 static void scrollUpAP(Widget w, XEvent *event, String *args, 202 Cardinal *nArgs); 203 static void scrollDownAP(Widget w, XEvent *event, String *args, 204 Cardinal *nArgs); 205 static void scrollLeftAP(Widget w, XEvent *event, String *args, 206 Cardinal *nArgs); 207 static void scrollRightAP(Widget w, XEvent *event, String *args, 208 Cardinal *nArgs); 209 static void scrollToLineAP(Widget w, XEvent *event, String *args, 210 Cardinal *nArgs); 211 static void selectAllAP(Widget w, XEvent *event, String *args, 212 Cardinal *nArgs); 213 static void deselectAllAP(Widget w, XEvent *event, String *args, 214 Cardinal *nArgs); 215 static void focusInAP(Widget w, XEvent *event, String *args, 216 Cardinal *nArgs); 217 static void focusOutAP(Widget w, XEvent *event, String *args, 218 Cardinal *nArgs); 219 static void zoomInAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); 220 static void zoomOutAP(Widget w, XEvent *event, String *args, Cardinal *nArgs); 221 #ifndef DISABLE_MULTICURSOR 222 static void addCursorUpAP(Widget w, XEvent *event, String *args, 223 Cardinal *nArgs); 224 static void addCursorDownAP(Widget w, XEvent *event, String *args, 225 Cardinal *nArgs); 226 #endif 227 228 static void checkMoveSelectionChange(Widget w, XEvent *event, int startPos, 229 String *args, Cardinal *nArgs); 230 static void keyMoveExtendSelection(Widget w, XEvent *event, int startPos, 231 int rectangular); 232 static void checkAutoShowInsertPos(Widget w); 233 static int checkReadOnly(Widget w); 234 static void simpleInsertAtCursor(Widget w, char *chars, XEvent *event, 235 int allowPendingDelete); 236 static int pendingSelection(Widget w); 237 static int deletePendingSelection(Widget w, XEvent *event); 238 static int deleteEmulatedTab(Widget w, XEvent *event); 239 static void selectWord(Widget w, int pointerX); 240 static int spanForward(textBuffer *buf, int startPos, char *searchChars, 241 int ignoreSpace, int *foundPos); 242 static int spanBackward(textBuffer *buf, int startPos, char *searchChars, int 243 ignoreSpace, int *foundPos); 244 static void selectLine(Widget w); 245 static int startOfWord(TextWidget w, int pos); 246 static int endOfWord(TextWidget w, int pos); 247 static void checkAutoScroll(TextWidget w, int x, int y); 248 static void endDrag(Widget w); 249 static void cancelDrag(Widget w); 250 static void callCursorMovementCBs(Widget w, XEvent *event); 251 static void adjustSelection(TextWidget tw, int x, int y); 252 static void adjustSecondarySelection(TextWidget tw, int x, int y); 253 static void autoScrollTimerProc(XtPointer clientData, XtIntervalId *id); 254 static char *wrapText(TextWidget tw, char *startLine, char *text, int bufOffset, 255 int wrapMargin, int *breakBefore); 256 static int wrapLine(TextWidget tw, textBuffer *buf, int bufOffset, 257 int lineStartPos, int lineEndPos, int limitPos, int *breakAt, 258 int *charsAdded); 259 static char *createIndentString(TextWidget tw, textBuffer *buf, int bufOffset, 260 int lineStartPos, int lineEndPos, int *length, int *column); 261 static void cursorBlinkTimerProc(XtPointer clientData, XtIntervalId *id); 262 static int hasKey(const char *key, const String *args, const Cardinal *nArgs); 263 static int max(int i1, int i2); 264 static int min(int i1, int i2); 265 static void ringIfNecessary(Boolean silent, Widget w); 266 267 static XftColor defaultAnsiColors[16]; 268 269 static char defaultTranslations[] = 270 /* Home */ 271 "~Shift ~Ctrl Alt<Key>osfBeginLine: last_document()\n" 272 273 "<KeyPress>osfCancel: cancel()\n" 274 "Alt<KeyPress>osfCancel: cancel()\n" 275 276 /* Keypad */ 277 ":<Key>KP_7: self_insert()\n" 278 ":<Key>KP_8: self_insert()\n" 279 ":<Key>KP_9: self_insert()\n" 280 ":<Key>KP_4: self_insert()\n" 281 ":<Key>KP_6: self_insert()\n" 282 ":<Key>KP_1: self_insert()\n" 283 ":<Key>KP_2: self_insert()\n" 284 ":<Key>KP_3: self_insert()\n" 285 ":<Key>KP_0: self_insert()\n" 286 ":<Key>KP_Separator: self_insert()\n" 287 288 /* Backspace */ 289 "Ctrl<KeyPress>osfBackSpace: delete_previous_word()\n" 290 "<KeyPress>osfBackSpace: delete_previous_character()\n" 291 292 /* Delete */ 293 "Alt Shift Ctrl<KeyPress>osfDelete: cut_primary(\"rect\")\n" 294 "Meta Shift Ctrl<KeyPress>osfDelete: cut_primary(\"rect\")\n" 295 "Shift Ctrl<KeyPress>osfDelete: cut_primary()\n" 296 "Ctrl<KeyPress>osfDelete: delete_to_end_of_line()\n" 297 "Shift<KeyPress>osfDelete: cut_clipboard()\n" 298 "<KeyPress>osfDelete: delete_next_character()\n" 299 300 /* Insert */ 301 "Alt Shift Ctrl<KeyPress>osfInsert: copy_primary(\"rect\")\n" 302 "Meta Shift Ctrl<KeyPress>osfInsert: copy_primary(\"rect\")\n" 303 "Shift Ctrl<KeyPress>osfInsert: copy_primary()\n" 304 "Shift<KeyPress>osfInsert: paste_clipboard()\n" 305 "Ctrl<KeyPress>osfInsert: copy_clipboard()\n" 306 "~Shift ~Ctrl<KeyPress>osfInsert: set_overtype_mode()\n" 307 308 /* Cut/Copy/Paste */ 309 "Shift Ctrl<KeyPress>osfCut: cut_primary()\n" 310 "<KeyPress>osfCut: cut_clipboard()\n" 311 "<KeyPress>osfCopy: copy_clipboard()\n" 312 "<KeyPress>osfPaste: paste_clipboard()\n" 313 "<KeyPress>osfPrimaryPaste: copy_primary()\n" 314 315 /* BeginLine */ 316 "Alt Shift Ctrl<KeyPress>osfBeginLine: beginning_of_file(\"extend\", \"rect\")\n" 317 "Meta Shift Ctrl<KeyPress>osfBeginLine: beginning_of_file(\"extend\" \"rect\")\n" 318 "Alt Shift<KeyPress>osfBeginLine: beginning_of_line(\"extend\", \"rect\")\n" 319 "Meta Shift<KeyPress>osfBeginLine: beginning_of_line(\"extend\", \"rect\")\n" 320 "Shift Ctrl<KeyPress>osfBeginLine: beginning_of_file(\"extend\")\n" 321 "Ctrl<KeyPress>osfBeginLine: beginning_of_file()\n" 322 "Shift<KeyPress>osfBeginLine: beginning_of_line(\"extend\")\n" 323 "~Alt~Shift~Ctrl~Meta<KeyPress>osfBeginLine: beginning_of_line()\n" 324 325 /* EndLine */ 326 "Alt Shift Ctrl<KeyPress>osfEndLine: end_of_file(\"extend\", \"rect\")\n" 327 "Meta Shift Ctrl<KeyPress>osfEndLine: end_of_file(\"extend\", \"rect\")\n" 328 "Alt Shift<KeyPress>osfEndLine: end_of_line(\"extend\", \"rect\")\n" 329 "Meta Shift<KeyPress>osfEndLine: end_of_line(\"extend\", \"rect\")\n" 330 "Shift Ctrl<KeyPress>osfEndLine: end_of_file(\"extend\")\n" 331 "Ctrl<KeyPress>osfEndLine: end_of_file()\n" 332 "Shift<KeyPress>osfEndLine: end_of_line(\"extend\")\n" 333 "~Alt~Shift~Ctrl~Meta<KeyPress>osfEndLine: end_of_line()\n" 334 335 /* Left */ 336 "Alt Shift Ctrl<KeyPress>osfLeft: backward_word(\"extend\", \"rect\")\n" 337 "Meta Shift Ctrl<KeyPress>osfLeft: backward_word(\"extend\", \"rect\")\n" 338 "Alt Shift<KeyPress>osfLeft: key_select(\"left\", \"rect\")\n" 339 "Meta Shift<KeyPress>osfLeft: key_select(\"left\", \"rect\")\n" 340 "Shift Ctrl<KeyPress>osfLeft: backward_word(\"extend\")\n" 341 "Ctrl<KeyPress>osfLeft: backward_word()\n" 342 "Shift<KeyPress>osfLeft: key_select(\"left\")\n" 343 "~Alt~Shift~Ctrl~Meta<KeyPress>osfLeft: backward_character()\n" 344 345 /* Right */ 346 "Alt Shift Ctrl<KeyPress>osfRight: forward_word(\"extend\", \"rect\")\n" 347 "Meta Shift Ctrl<KeyPress>osfRight: forward_word(\"extend\", \"rect\")\n" 348 "Alt Shift<KeyPress>osfRight: key_select(\"right\", \"rect\")\n" 349 "Meta Shift<KeyPress>osfRight: key_select(\"right\", \"rect\")\n" 350 "Shift Ctrl<KeyPress>osfRight: forward_word(\"extend\")\n" 351 "Ctrl<KeyPress>osfRight: forward_word()\n" 352 "Shift<KeyPress>osfRight: key_select(\"right\")\n" 353 "~Alt~Shift~Ctrl~Meta<KeyPress>osfRight: forward_character()\n" 354 355 /* Up */ 356 #ifndef DISABLE_MULTICURSOR 357 "Ctrl Super<KeyPress>osfUp: add_cursor_up()\n" 358 #endif 359 "Alt Shift Ctrl<KeyPress>osfUp: backward_paragraph(\"extend\", \"rect\")\n" 360 "Meta Shift Ctrl<KeyPress>osfUp: backward_paragraph(\"extend\", \"rect\")\n" 361 "Alt Shift<KeyPress>osfUp: process_shift_up(\"rect\")\n" 362 "Meta Shift<KeyPress>osfUp: process_shift_up(\"rect\")\n" 363 "Shift Ctrl<KeyPress>osfUp: backward_paragraph(\"extend\")\n" 364 "Ctrl<KeyPress>osfUp: backward_paragraph()\n" 365 "Shift<KeyPress>osfUp: process_shift_up()\n" 366 "~Alt~Shift~Ctrl~Meta<KeyPress>osfUp: process_up()\n" 367 368 /* Down */ 369 #ifndef DISABLE_MULTICURSOR 370 "Ctrl Super<KeyPress>osfDown: add_cursor_down()\n" 371 #endif 372 "Alt Shift Ctrl<KeyPress>osfDown: forward_paragraph(\"extend\", \"rect\")\n" 373 "Meta Shift Ctrl<KeyPress>osfDown: forward_paragraph(\"extend\", \"rect\")\n" 374 "Alt Shift<KeyPress>osfDown: process_shift_down(\"rect\")\n" 375 "Meta Shift<KeyPress>osfDown: process_shift_down(\"rect\")\n" 376 "Shift Ctrl<KeyPress>osfDown: forward_paragraph(\"extend\")\n" 377 "Ctrl<KeyPress>osfDown: forward_paragraph()\n" 378 "Shift<KeyPress>osfDown: process_shift_down()\n" 379 "~Alt~Shift~Ctrl~Meta<KeyPress>osfDown: process_down()\n" 380 381 /* PageUp */ 382 "Alt Shift Ctrl<KeyPress>osfPageUp: page_left(\"extend\", \"rect\")\n" 383 "Meta Shift Ctrl<KeyPress>osfPageUp: page_left(\"extend\", \"rect\")\n" 384 "Alt Shift<KeyPress>osfPageUp: previous_page(\"extend\", \"rect\")\n" 385 "Meta Shift<KeyPress>osfPageUp: previous_page(\"extend\", \"rect\")\n" 386 "Shift Ctrl<KeyPress>osfPageUp: page_left(\"extend\")\n" 387 "Ctrl<KeyPress>osfPageUp: previous_document()\n" 388 "Shift<KeyPress>osfPageUp: previous_page(\"extend\")\n" 389 "~Alt ~Shift ~Ctrl ~Meta<KeyPress>osfPageUp: previous_page()\n" 390 391 /* PageDown */ 392 "Alt Shift Ctrl<KeyPress>osfPageDown: page_right(\"extend\", \"rect\")\n" 393 "Meta Shift Ctrl<KeyPress>osfPageDown: page_right(\"extend\", \"rect\")\n" 394 "Alt Shift<KeyPress>osfPageDown: next_page(\"extend\", \"rect\")\n" 395 "Meta Shift<KeyPress>osfPageDown: next_page(\"extend\", \"rect\")\n" 396 "Shift Ctrl<KeyPress>osfPageDown: page_right(\"extend\")\n" 397 "Ctrl<KeyPress>osfPageDown: next_document()\n" 398 "Shift<KeyPress>osfPageDown: next_page(\"extend\")\n" 399 "~Alt ~Shift ~Ctrl ~Meta<KeyPress>osfPageDown: next_page()\n" 400 401 /* PageLeft and PageRight are placed later than the PageUp/PageDown 402 bindings. Some systems map osfPageLeft to Ctrl-PageUp. 403 Overloading this single key gives problems, and we want to give 404 priority to the normal version. */ 405 406 /* PageLeft */ 407 "Alt Shift<KeyPress>osfPageLeft: page_left(\"extend\", \"rect\")\n" 408 "Meta Shift<KeyPress>osfPageLeft: page_left(\"extend\", \"rect\")\n" 409 "Shift<KeyPress>osfPageLeft: page_left(\"extend\")\n" 410 "~Alt ~Shift ~Ctrl ~Meta<KeyPress>osfPageLeft: page_left()\n" 411 412 /* PageRight */ 413 "Alt Shift<KeyPress>osfPageRight: page_right(\"extend\", \"rect\")\n" 414 "Meta Shift<KeyPress>osfPageRight: page_right(\"extend\", \"rect\")\n" 415 "Shift<KeyPress>osfPageRight: page_right(\"extend\")\n" 416 "~Alt ~Shift ~Ctrl ~Meta<KeyPress>osfPageRight: page_right()\n" 417 418 /* change font size */ 419 "Ctrl <Key>+: zoom_in()\n" 420 "Ctrl <Key>-: zoom_out()\n" 421 "Ctrl <Key>KP_Add: zoom_in()\n" 422 "Ctrl <Key>KP_Subtract: zoom_out()\n" 423 424 "Shift<KeyPress>osfSelect: key_select()\n" 425 "<KeyPress>osfCancel: process_cancel()\n" 426 "Ctrl~Alt~Meta<KeyPress>v: paste_clipboard()\n" 427 "Ctrl~Alt~Meta<KeyPress>c: copy_clipboard()\n" 428 "Ctrl~Alt~Meta<KeyPress>x: cut_clipboard()\n" 429 "Ctrl~Alt~Meta<KeyPress>u: delete_to_start_of_line()\n" 430 "Ctrl<KeyPress>Return: newline_and_indent()\n" 431 "Shift<KeyPress>Return: newline_no_indent()\n" 432 "<KeyPress>Return: newline()\n" 433 /* KP_Enter = osfActivate 434 Note: Ctrl+KP_Enter is already bound to Execute Command Line... */ 435 "Shift<KeyPress>osfActivate: newline_no_indent()\n" 436 "<KeyPress>osfActivate: newline()\n" 437 "Ctrl<KeyPress>Tab: self_insert()\n" 438 "<KeyPress>Tab: process_tab()\n" 439 "Alt Shift Ctrl<KeyPress>space: key_select(\"rect\")\n" 440 "Meta Shift Ctrl<KeyPress>space: key_select(\"rect\")\n" 441 "Shift Ctrl~Meta~Alt<KeyPress>space: key_select()\n" 442 "Ctrl~Meta~Alt<KeyPress>slash: select_all()\n" 443 "Ctrl~Meta~Alt<KeyPress>backslash: deselect_all()\n" 444 "<KeyPress>: self_insert()\n" 445 "Alt Ctrl<Btn1Down>: move_destination()\n" 446 "Meta Ctrl<Btn1Down>: move_destination()\n" 447 "Shift Ctrl<Btn1Down>: extend_start(\"rect\")\n" 448 "Shift<Btn1Down>: extend_start()\n" 449 #ifndef DISABLE_MULTICURSOR 450 "Ctrl<Btn1Down>: grab_focus(\"mc\")\n" 451 #endif 452 "<Btn1Down>: grab_focus()\n" 453 "Button1 Ctrl<MotionNotify>: extend_adjust(\"rect\")\n" 454 "Button1~Ctrl<MotionNotify>: extend_adjust()\n" 455 "<Btn1Up>: extend_end()\n" 456 "<Btn2Down>: secondary_or_drag_start()\n" 457 "Shift Ctrl Button2<MotionNotify>: secondary_or_drag_adjust(\"rect\", \"copy\", \"overlay\")\n" 458 "Shift Button2<MotionNotify>: secondary_or_drag_adjust(\"copy\")\n" 459 "Ctrl Button2<MotionNotify>: secondary_or_drag_adjust(\"rect\", \"overlay\")\n" 460 "Button2<MotionNotify>: secondary_or_drag_adjust()\n" 461 "Shift Ctrl<Btn2Up>: move_to_or_end_drag(\"copy\", \"overlay\")\n" 462 "Shift <Btn2Up>: move_to_or_end_drag(\"copy\")\n" 463 "Alt<Btn2Up>: exchange()\n" 464 "Meta<Btn2Up>: exchange()\n" 465 "Ctrl<Btn2Up>: copy_to_or_end_drag(\"overlay\")\n" 466 "<Btn2Up>: copy_to_or_end_drag()\n" 467 "Ctrl~Meta~Alt<Btn3Down>: mouse_pan()\n" 468 "Ctrl~Meta~Alt Button3<MotionNotify>: mouse_pan()\n" 469 "<Btn3Up>: end_drag()\n" 470 "<FocusIn>: focusIn()\n" 471 "<FocusOut>: focusOut()\n" 472 /* Support for mouse wheel in XFree86 */ 473 "Shift<Btn4Down>,<Btn4Up>: scroll_up(1)\n" 474 "Shift<Btn5Down>,<Btn5Up>: scroll_down(1)\n" 475 "Ctrl<Btn4Down>,<Btn4Up>: scroll_up(1, pages)\n" 476 "Ctrl<Btn5Down>,<Btn5Up>: scroll_down(1, pages)\n" 477 "<Btn4Down>,<Btn4Up>,<MotionNotify>(1+): extend_adjust()\n" 478 "<Btn5Down>,<Btn5Up>,<MotionNotify>(1+): extend_adjust()\n" 479 "<Btn4Down>,<Btn4Up>: scroll_up(5)\n" 480 "<Btn5Down>,<Btn5Up>: scroll_down(5)\n"; 481 /* some of the translations from the Motif text widget were not picked up: 482 :<KeyPress>osfSelect: set-anchor()\n\ 483 :<KeyPress>osfActivate: activate()\n\ 484 ~Shift Ctrl~Meta~Alt<KeyPress>Return: activate()\n\ 485 ~Shift Ctrl~Meta~Alt<KeyPress>space: set-anchor()\n\ 486 :<KeyPress>osfClear: clear-selection()\n\ 487 ~Shift~Ctrl~Meta~Alt<KeyPress>Return: process-return()\n\ 488 Shift~Meta~Alt<KeyPress>Tab: prev-tab-group()\n\ 489 Ctrl~Meta~Alt<KeyPress>Tab: next-tab-group()\n\ 490 <UnmapNotify>: unmap()\n\ 491 <EnterNotify>: enter()\n\ 492 <LeaveNotify>: leave()\n 493 */ 494 495 496 static XtActionsRec actionsList[] = { 497 {"cancel", cancelAP}, 498 {"self-insert", selfInsertAP}, 499 {"self_insert", selfInsertAP}, 500 {"grab-focus", grabFocusAP}, 501 {"grab_focus", grabFocusAP}, 502 {"extend-adjust", extendAdjustAP}, 503 {"extend_adjust", extendAdjustAP}, 504 {"extend-start", extendStartAP}, 505 {"extend_start", extendStartAP}, 506 {"extend-end", extendEndAP}, 507 {"extend_end", extendEndAP}, 508 {"secondary-adjust", secondaryAdjustAP}, 509 {"secondary_adjust", secondaryAdjustAP}, 510 {"secondary-or-drag-adjust", secondaryOrDragAdjustAP}, 511 {"secondary_or_drag_adjust", secondaryOrDragAdjustAP}, 512 {"secondary-start", secondaryStartAP}, 513 {"secondary_start", secondaryStartAP}, 514 {"secondary-or-drag-start", secondaryOrDragStartAP}, 515 {"secondary_or_drag_start", secondaryOrDragStartAP}, 516 {"process-bdrag", secondaryOrDragStartAP}, 517 {"process_bdrag", secondaryOrDragStartAP}, 518 {"move-destination", moveDestinationAP}, 519 {"move_destination", moveDestinationAP}, 520 {"move-to", moveToAP}, 521 {"move_to", moveToAP}, 522 {"move-to-or-end-drag", moveToOrEndDragAP}, 523 {"move_to_or_end_drag", moveToOrEndDragAP}, 524 {"end_drag", endDragAP}, 525 {"copy-to", copyToAP}, 526 {"copy_to", copyToAP}, 527 {"copy-to-or-end-drag", copyToOrEndDragAP}, 528 {"copy_to_or_end_drag", copyToOrEndDragAP}, 529 {"exchange", exchangeAP}, 530 {"process-cancel", processCancelAP}, 531 {"process_cancel", processCancelAP}, 532 {"paste-clipboard", pasteClipboardAP}, 533 {"paste_clipboard", pasteClipboardAP}, 534 {"copy-clipboard", copyClipboardAP}, 535 {"copy_clipboard", copyClipboardAP}, 536 {"cut-clipboard", cutClipboardAP}, 537 {"cut_clipboard", cutClipboardAP}, 538 {"copy-primary", copyPrimaryAP}, 539 {"copy_primary", copyPrimaryAP}, 540 {"cut-primary", cutPrimaryAP}, 541 {"cut_primary", cutPrimaryAP}, 542 {"newline", newlineAP}, 543 {"newline-and-indent", newlineAndIndentAP}, 544 {"newline_and_indent", newlineAndIndentAP}, 545 {"newline-no-indent", newlineNoIndentAP}, 546 {"newline_no_indent", newlineNoIndentAP}, 547 {"delete-selection", deleteSelectionAP}, 548 {"delete_selection", deleteSelectionAP}, 549 {"delete-previous-character", deletePreviousCharacterAP}, 550 {"delete_previous_character", deletePreviousCharacterAP}, 551 {"delete-next-character", deleteNextCharacterAP}, 552 {"delete_next_character", deleteNextCharacterAP}, 553 {"delete-previous-word", deletePreviousWordAP}, 554 {"delete_previous_word", deletePreviousWordAP}, 555 {"delete-next-word", deleteNextWordAP}, 556 {"delete_next_word", deleteNextWordAP}, 557 {"delete-to-start-of-line", deleteToStartOfLineAP}, 558 {"delete_to_start_of_line", deleteToStartOfLineAP}, 559 {"delete-to-end-of-line", deleteToEndOfLineAP}, 560 {"delete_to_end_of_line", deleteToEndOfLineAP}, 561 {"forward-character", forwardCharacterAP}, 562 {"forward_character", forwardCharacterAP}, 563 {"backward-character", backwardCharacterAP}, 564 {"backward_character", backwardCharacterAP}, 565 {"key-select", keySelectAP}, 566 {"key_select", keySelectAP}, 567 {"process-up", processUpAP}, 568 {"process_up", processUpAP}, 569 {"process-down", processDownAP}, 570 {"process_down", processDownAP}, 571 {"process-shift-up", processShiftUpAP}, 572 {"process_shift_up", processShiftUpAP}, 573 {"process-shift-down", processShiftDownAP}, 574 {"process_shift_down", processShiftDownAP}, 575 {"process-home", beginningOfLineAP}, 576 {"process_home", beginningOfLineAP}, 577 {"forward-word", forwardWordAP}, 578 {"forward_word", forwardWordAP}, 579 {"backward-word", backwardWordAP}, 580 {"backward_word", backwardWordAP}, 581 {"forward-paragraph", forwardParagraphAP}, 582 {"forward_paragraph", forwardParagraphAP}, 583 {"backward-paragraph", backwardParagraphAP}, 584 {"backward_paragraph", backwardParagraphAP}, 585 {"beginning-of-line", beginningOfLineAP}, 586 {"beginning_of_line", beginningOfLineAP}, 587 {"end-of-line", endOfLineAP}, 588 {"end_of_line", endOfLineAP}, 589 {"beginning-of-file", beginningOfFileAP}, 590 {"beginning_of_file", beginningOfFileAP}, 591 {"end-of-file", endOfFileAP}, 592 {"end_of_file", endOfFileAP}, 593 {"next-page", nextPageAP}, 594 {"next_page", nextPageAP}, 595 {"previous-page", previousPageAP}, 596 {"previous_page", previousPageAP}, 597 {"page-left", pageLeftAP}, 598 {"page_left", pageLeftAP}, 599 {"page-right", pageRightAP}, 600 {"page_right", pageRightAP}, 601 {"toggle-overstrike", toggleOverstrikeAP}, 602 {"toggle_overstrike", toggleOverstrikeAP}, 603 {"scroll-up", scrollUpAP}, 604 {"scroll_up", scrollUpAP}, 605 {"scroll-down", scrollDownAP}, 606 {"scroll_down", scrollDownAP}, 607 {"scroll_left", scrollLeftAP}, 608 {"scroll_right", scrollRightAP}, 609 {"scroll-to-line", scrollToLineAP}, 610 {"scroll_to_line", scrollToLineAP}, 611 {"select-all", selectAllAP}, 612 {"select_all", selectAllAP}, 613 {"deselect-all", deselectAllAP}, 614 {"deselect_all", deselectAllAP}, 615 {"focusIn", focusInAP}, 616 {"focusOut", focusOutAP}, 617 {"process-return", selfInsertAP}, 618 {"process_return", selfInsertAP}, 619 {"process-tab", processTabAP}, 620 {"process_tab", processTabAP}, 621 {"insert-string", insertStringAP}, 622 {"insert_string", insertStringAP}, 623 {"mouse_pan", mousePanAP}, 624 {"zoom_in", zoomInAP}, 625 {"zoom_out", zoomOutAP}, 626 #ifndef DISABLE_MULTICURSOR 627 {"add_cursor_up", addCursorUpAP}, 628 {"add-cursor-up", addCursorUpAP}, 629 {"add_cursor_down", addCursorDownAP}, 630 {"add-cursor-down", addCursorDownAP} 631 #endif 632 }; 633 634 /* The motif text widget defined a bunch of actions which the nedit text 635 widget as-of-yet does not support: 636 637 Actions which were not bound to keys (for emacs emulation, some of 638 them should probably be supported: 639 640 kill-next-character() 641 kill-next-word() 642 kill-previous-character() 643 kill-previous-word() 644 kill-selection() 645 kill-to-end-of-line() 646 kill-to-start-of-line() 647 unkill() 648 next-line() 649 newline-and-backup() 650 beep() 651 redraw-display() 652 scroll-one-line-down() 653 scroll-one-line-up() 654 set-insertion-point() 655 656 Actions which are not particularly useful: 657 658 set-anchor() 659 activate() 660 clear-selection() -> this is a wierd one 661 do-quick-action() -> don't think this ever worked 662 Help() 663 next-tab-group() 664 select-adjust() 665 select-start() 666 select-end() 667 */ 668 669 static NFont *defaultFont; 670 671 static XtResource resources[] = { 672 {XmNhighlightThickness, XmCHighlightThickness, XmRDimension, 673 sizeof(Dimension), XtOffset(TextWidget, primitive.highlight_thickness), 674 XmRInt, 0}, 675 {XmNshadowThickness, XmCShadowThickness, XmRDimension, sizeof(Dimension), 676 XtOffset(TextWidget, primitive.shadow_thickness), XmRInt, 0}, 677 {textNXftFont, textCXftFont, textTXftFont, sizeof(NFont *), 678 XtOffset(TextWidget, text.font), textTXftFont, &defaultFont}, 679 {textNXftBoldFont, textCXftBoldFont, textTXftFont, sizeof(NFont *), 680 XtOffset(TextWidget, text.boldFont), textTXftFont, &defaultFont}, 681 {textNXftItalicFont, textCXftItalicFont, textTXftFont, sizeof(NFont *), 682 XtOffset(TextWidget, text.italicFont), textTXftFont, &defaultFont}, 683 {textNXftBoldItalicFont, textCXftBoldItalicFont, textTXftFont, sizeof(NFont *), 684 XtOffset(TextWidget, text.boldItalicFont), textTXftFont, &defaultFont}, 685 {textNselectForeground, textCSelectForeground, XmRPixel, sizeof(Pixel), 686 XtOffset(TextWidget, text.selectFGPixel), XmRString, 687 NEDIT_DEFAULT_SEL_FG}, 688 {textNselectBackground, textCSelectBackground, XmRPixel, sizeof(Pixel), 689 XtOffset(TextWidget, text.selectBGPixel), XmRString, 690 NEDIT_DEFAULT_SEL_BG}, 691 {textNhighlightForeground, textCHighlightForeground, XmRPixel,sizeof(Pixel), 692 XtOffset(TextWidget, text.highlightFGPixel), XmRString, 693 NEDIT_DEFAULT_HI_FG}, 694 {textNhighlightBackground, textCHighlightBackground, XmRPixel,sizeof(Pixel), 695 XtOffset(TextWidget, text.highlightBGPixel), XmRString, 696 NEDIT_DEFAULT_HI_BG}, 697 {textNlineNumForeground, textCLineNumForeground, XmRPixel,sizeof(Pixel), 698 XtOffset(TextWidget, text.lineNumFGPixel), XmRString, 699 NEDIT_DEFAULT_LINENO_FG}, 700 {textNlineNumBackground, textCLineNumBackground, XmRPixel,sizeof(Pixel), 701 XtOffset(TextWidget, text.lineNumBGPixel), XmRString, 702 NEDIT_DEFAULT_LINENO_BG}, 703 {textNcursorForeground, textCCursorForeground, XmRPixel,sizeof(Pixel), 704 XtOffset(TextWidget, text.cursorFGPixel), XmRString, 705 NEDIT_DEFAULT_CURSOR_FG}, 706 {textNcalltipForeground, textCcalltipForeground, XmRPixel,sizeof(Pixel), 707 XtOffset(TextWidget, text.calltipFGPixel), XmRString, 708 NEDIT_DEFAULT_CALLTIP_FG}, 709 {textNcalltipBackground, textCcalltipBackground, XmRPixel,sizeof(Pixel), 710 XtOffset(TextWidget, text.calltipBGPixel), XmRString, 711 NEDIT_DEFAULT_CALLTIP_BG}, 712 {textNlineHighlightBackground, textClineHighlightBackground, XmRPixel,sizeof(Pixel), 713 XtOffset(TextWidget, text.lineHighlightBGPixel), XmRString, 714 NEDIT_DEFAULT_CURSOR_LINE_BG}, 715 {textNansiColors, textCansiColors, XmRBoolean, sizeof(Boolean), 716 XtOffset(TextWidget, text.ansiColors), XmRString, "False"}, 717 {textNansiColorList, textCansiColorList, XmRPointer, sizeof(void*), 718 XtOffset(TextWidget, text.ansiColorList), XmRPointer, defaultAnsiColors}, 719 {textNhighlightCursorLine, textChighlightCursorLine, XmRBoolean, sizeof(Boolean), 720 XtOffset(TextWidget, text.highlightCursorLine), XmRString, "False"}, 721 {textNindentRainbow, textCindentRainbow, XmRBoolean, sizeof(Boolean), 722 XtOffset(TextWidget, text.indentRainbow), XmRString, "False"}, 723 {textNbacklightCharTypes,textCBacklightCharTypes,XmRString,sizeof(XmString), 724 XtOffset(TextWidget, text.backlightCharTypes), XmRString, NULL}, 725 {textNrows, textCRows, XmRInt,sizeof(int), 726 XtOffset(TextWidget, text.rows), XmRString, "24"}, 727 {textNcolumns, textCColumns, XmRInt, sizeof(int), 728 XtOffset(TextWidget, text.columns), XmRString, "80"}, 729 {textNmarginWidth, textCMarginWidth, XmRInt, sizeof(int), 730 XtOffset(TextWidget, text.marginWidth), XmRString, "5"}, 731 {textNmarginHeight, textCMarginHeight, XmRInt, sizeof(int), 732 XtOffset(TextWidget, text.marginHeight), XmRString, "5"}, 733 {textNpendingDelete, textCPendingDelete, XmRBoolean, sizeof(Boolean), 734 XtOffset(TextWidget, text.pendingDelete), XmRString, "True"}, 735 {textNautoWrap, textCAutoWrap, XmRBoolean, sizeof(Boolean), 736 XtOffset(TextWidget, text.autoWrap), XmRString, "True"}, 737 {textNcontinuousWrap, textCContinuousWrap, XmRBoolean, sizeof(Boolean), 738 XtOffset(TextWidget, text.continuousWrap), XmRString, "True"}, 739 {textNautoIndent, textCAutoIndent, XmRBoolean, sizeof(Boolean), 740 XtOffset(TextWidget, text.autoIndent), XmRString, "True"}, 741 {textNsmartIndent, textCSmartIndent, XmRBoolean, sizeof(Boolean), 742 XtOffset(TextWidget, text.smartIndent), XmRString, "False"}, 743 {textNoverstrike, textCOverstrike, XmRBoolean, sizeof(Boolean), 744 XtOffset(TextWidget, text.overstrike), XmRString, "False"}, 745 {textNheavyCursor, textCHeavyCursor, XmRBoolean, sizeof(Boolean), 746 XtOffset(TextWidget, text.heavyCursor), XmRString, "False"}, 747 {textNreadOnly, textCReadOnly, XmRBoolean, sizeof(Boolean), 748 XtOffset(TextWidget, text.readOnly), XmRString, "False"}, 749 {textNhidePointer, textCHidePointer, XmRBoolean, sizeof(Boolean), 750 XtOffset(TextWidget, text.hidePointer), XmRString, "False"}, 751 {textNwrapMargin, textCWrapMargin, XmRInt, sizeof(int), 752 XtOffset(TextWidget, text.wrapMargin), XmRString, "0"}, 753 {textNhScrollBar, textCHScrollBar, XmRWidget, sizeof(Widget), 754 XtOffset(TextWidget, text.hScrollBar), XmRString, ""}, 755 {textNvScrollBar, textCVScrollBar, XmRWidget, sizeof(Widget), 756 XtOffset(TextWidget, text.vScrollBar), XmRString, ""}, 757 {textNlineNumCols, textCLineNumCols, XmRInt, sizeof(int), 758 XtOffset(TextWidget, text.lineNumCols), XmRString, "0"}, 759 {textNautoShowInsertPos, textCAutoShowInsertPos, XmRBoolean, 760 sizeof(Boolean), XtOffset(TextWidget, text.autoShowInsertPos), 761 XmRString, "True"}, 762 {textNautoWrapPastedText, textCAutoWrapPastedText, XmRBoolean, 763 sizeof(Boolean), XtOffset(TextWidget, text.autoWrapPastedText), 764 XmRString, "False"}, 765 {textNwordDelimiters, textCWordDelimiters, XmRString, sizeof(char *), 766 XtOffset(TextWidget, text.delimiters), XmRString, 767 ".,/\\`''!@#%^&*()-=+{}[]\":;<>?"}, 768 {textNblinkRate, textCBlinkRate, XmRInt, sizeof(int), 769 XtOffset(TextWidget, text.cursorBlinkRate), XmRString, "500"}, 770 {textNemulateTabs, textCEmulateTabs, XmRInt, sizeof(int), 771 XtOffset(TextWidget, text.emulateTabs), XmRString, "0"}, 772 {textNfocusCallback, textCFocusCallback, XmRCallback, sizeof(caddr_t), 773 XtOffset(TextWidget, text.focusInCB), XtRCallback, NULL}, 774 {textNlosingFocusCallback, textCLosingFocusCallback, XmRCallback, 775 sizeof(caddr_t), XtOffset(TextWidget, text.focusOutCB), XtRCallback,NULL}, 776 {textNcursorMovementCallback, textCCursorMovementCallback, XmRCallback, 777 sizeof(caddr_t), XtOffset(TextWidget, text.cursorCB), XtRCallback, NULL}, 778 {textNdragStartCallback, textCDragStartCallback, XmRCallback, 779 sizeof(caddr_t), XtOffset(TextWidget, text.dragStartCB), XtRCallback, 780 NULL}, 781 {textNdragEndCallback, textCDragEndCallback, XmRCallback, 782 sizeof(caddr_t), XtOffset(TextWidget, text.dragEndCB), XtRCallback, NULL}, 783 {textNsmartIndentCallback, textCSmartIndentCallback, XmRCallback, 784 sizeof(caddr_t), XtOffset(TextWidget, text.smartIndentCB), XtRCallback, 785 NULL}, 786 {textNcursorVPadding, textCCursorVPadding, XtRCardinal, sizeof(Cardinal), 787 XtOffset(TextWidget, text.cursorVPadding), XmRString, "0"} 788 }; 789 790 static TextClassRec textClassRec = { 791 /* CoreClassPart */ 792 { 793 (WidgetClass) &xmPrimitiveClassRec, /* superclass */ 794 "Text", /* class_name */ 795 sizeof(TextRec), /* widget_size */ 796 NULL, /* class_initialize */ 797 NULL, /* class_part_initialize */ 798 FALSE, /* class_inited */ 799 (XtInitProc)initialize, /* initialize */ 800 NULL, /* initialize_hook */ 801 realize, /* realize */ 802 actionsList, /* actions */ 803 XtNumber(actionsList), /* num_actions */ 804 resources, /* resources */ 805 XtNumber(resources), /* num_resources */ 806 NULLQUARK, /* xrm_class */ 807 TRUE, /* compress_motion */ 808 TRUE, /* compress_exposure */ 809 TRUE, /* compress_enterleave */ 810 FALSE, /* visible_interest */ 811 (XtWidgetProc)destroy, /* destroy */ 812 (XtWidgetProc)resize, /* resize */ 813 (XtExposeProc)redisplay, /* expose */ 814 (XtSetValuesFunc)setValues, /* set_values */ 815 NULL, /* set_values_hook */ 816 XtInheritSetValuesAlmost, /* set_values_almost */ 817 NULL, /* get_values_hook */ 818 NULL, /* accept_focus */ 819 XtVersion, /* version */ 820 NULL, /* callback private */ 821 defaultTranslations, /* tm_table */ 822 queryGeometry, /* query_geometry */ 823 NULL, /* display_accelerator */ 824 NULL, /* extension */ 825 }, 826 /* Motif primitive class fields */ 827 { 828 (XtWidgetProc)_XtInherit, /* Primitive border_highlight */ 829 (XtWidgetProc)_XtInherit, /* Primitive border_unhighlight */ 830 NULL, /*XtInheritTranslations,*/ /* translations */ 831 NULL, /* arm_and_activate */ 832 NULL, /* get resources */ 833 0, /* num get_resources */ 834 NULL, /* extension */ 835 }, 836 /* Text class part */ 837 { 838 0, /* ignored */ 839 } 840 }; 841 842 WidgetClass textWidgetClass = (WidgetClass)&textClassRec; 843 #define NEDIT_HIDE_CURSOR_MASK (KeyPressMask) 844 #define NEDIT_SHOW_CURSOR_MASK (FocusChangeMask | PointerMotionMask | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask) 845 static char empty_bits[] = {0x00, 0x00, 0x00, 0x00}; 846 static Cursor empty_cursor = 0; 847 848 #define FALLBACK_FONTNAME "Monospace:size=10" 849 850 // workaround: clicking somewhere and pasting text with ctrl+v can accidentally 851 // lead to ctrl+btn1, v which would add a new cursor and pasting text twice 852 // to prevent this, we save the time of the last ctrl key press 853 // and check this when pasting text 854 // (works only with standard translations) 855 static Time mod_ctrl_last_event = 0; 856 static Time mod_ctrl_mc_last_event = 0; 857 858 859 /* 860 * must be called at program start 861 */ 862 void TextWidgetClassInit(Display *dp, const char *fontname) 863 { 864 defaultFont = FontFromName(dp, fontname); 865 if(!defaultFont) { 866 if(strcmp(fontname, FALLBACK_FONTNAME)) { 867 TextWidgetClassInit(dp, FALLBACK_FONTNAME); 868 } else { 869 fprintf(stderr, "Cannot open default font\n"); 870 exit(1); 871 } 872 } 873 } 874 875 /* 876 ** Widget initialize method 877 */ 878 static void initialize(TextWidget request, TextWidget new) 879 { 880 if(new->text.font) { 881 FontRef(new->text.font); 882 } 883 if(new->text.boldFont) { 884 FontRef(new->text.boldFont); 885 } 886 if(new->text.italicFont) { 887 FontRef(new->text.italicFont); 888 } 889 if(new->text.boldItalicFont) { 890 FontRef(new->text.boldItalicFont); 891 } 892 893 NFont *font = new->text.font; 894 XftFont *xfont = FontDefault(font); 895 char *delimiters; 896 textBuffer *buf; 897 Pixel white, black; 898 int textLeft; 899 int charWidth; 900 int marginWidth; 901 int lineNumCols; 902 903 charWidth = font->maxWidth; 904 marginWidth = new->text.marginWidth; 905 lineNumCols = new->text.lineNumCols; 906 907 /* Set the initial window size based on the rows and columns resources */ 908 if (request->core.width == 0) 909 new->core.width = charWidth * new->text.columns + marginWidth*2 + 910 (lineNumCols == 0 ? 0 : marginWidth + charWidth * lineNumCols); 911 if (request->core.height == 0) 912 new->core.height = (xfont->ascent + xfont->descent) * new->text.rows + 913 new->text.marginHeight * 2; 914 915 /* The default colors work for B&W as well as color, except for 916 selectFGPixel and selectBGPixel, where color highlighting looks 917 much better without reverse video, so if we get here, and the 918 selection is totally unreadable because of the bad default colors, 919 swap the colors and make the selection reverse video */ 920 white = WhitePixelOfScreen(XtScreen((Widget)new)); 921 black = BlackPixelOfScreen(XtScreen((Widget)new)); 922 if ( new->text.selectBGPixel == white && 923 new->core.background_pixel == white && 924 new->text.selectFGPixel == black && 925 new->primitive.foreground == black) { 926 new->text.selectBGPixel = black; 927 new->text.selectFGPixel = white; 928 } 929 930 new->text.xim = NULL; 931 new->text.xic = NULL; 932 933 934 /* Create the initial text buffer for the widget to display (which can 935 be replaced later with TextSetBuffer) */ 936 buf = BufCreate(); 937 938 if(!new->text.ansiColorList) new->text.ansiColorList = defaultAnsiColors; 939 940 /* Create and initialize the text-display part of the widget */ 941 int fontWidth = new->text.font->minWidth; 942 textLeft = fontWidth/3 + 1 + 943 (lineNumCols == 0 ? 0 : marginWidth + charWidth * lineNumCols); 944 new->text.textD = TextDCreate((Widget)new, new->text.hScrollBar, 945 new->text.vScrollBar, textLeft, new->text.marginHeight, 946 new->core.width - marginWidth - textLeft, 947 new->core.height - new->text.marginHeight * 2, 948 lineNumCols == 0 ? 0 : marginWidth, 949 lineNumCols == 0 ? 0 : lineNumCols*charWidth, new->text.marginWidth, 950 buf,new->text.font, new->text.boldFont, new->text.italicFont, 951 new->text.boldItalicFont,GetDefaultColorProfile(), 952 new->text.continuousWrap, new->text.wrapMargin, 953 new->text.backlightCharTypes, new->text.calltipFGPixel, 954 new->text.calltipBGPixel, 0, new->text.indentRainbow, 955 new->text.highlightCursorLine, new->text.ansiColors); 956 957 /* Add mandatory delimiters blank, tab, and newline to the list of 958 delimiters. The memory use scheme here is that new values are 959 always copied, and can therefore be safely freed on subsequent 960 set-values calls or destroy */ 961 delimiters = (char*)NEditMalloc(strlen(new->text.delimiters) + 4); 962 sprintf(delimiters, "%s%s", " \t\n", new->text.delimiters); 963 new->text.delimiters = delimiters; 964 965 /* Start with the cursor blanked (widgets don't have focus on creation, 966 the initial FocusIn event will unblank it and get blinking started) */ 967 new->text.textD->cursorOn = False; 968 969 /* Initialize the widget variables */ 970 new->text.autoScrollProcID = 0; 971 new->text.cursorBlinkProcID = 0; 972 new->text.dragState = NOT_CLICKED; 973 new->text.multiClickState = NO_CLICKS; 974 new->text.lastBtnDown = 0; 975 new->text.selectionOwner = False; 976 new->text.motifDestOwner = False; 977 new->text.emTabsBeforeCursor = 0; 978 979 #if !defined(NO_XMIM) && defined(LESSTIF_VERSION) 980 /* Register the widget to the input manager */ 981 XmImRegister((Widget)new, 0); 982 /* In case some Resources for the IC need to be set, add them below */ 983 XmImVaSetValues((Widget)new, NULL); 984 #endif 985 986 XtAddEventHandler((Widget)new, GraphicsExpose, True, 987 (XtEventHandler)redisplayGE, (Opaque)NULL); 988 XtAddEventHandler((Widget)new, ButtonPressMask , True, 989 (XtEventHandler)hscrollEH, (Opaque)NULL); 990 991 if (new->text.hidePointer) { 992 Display *theDisplay; 993 Pixmap empty_pixmap; 994 XColor black_color; 995 /* Set up the empty Cursor */ 996 if (empty_cursor == 0) { 997 theDisplay = XtDisplay((Widget)new); 998 empty_pixmap = XCreateBitmapFromData(theDisplay, 999 RootWindowOfScreen(XtScreen((Widget)new)), empty_bits, 1, 1); 1000 XParseColor(theDisplay, DefaultColormapOfScreen(XtScreen((Widget)new)), 1001 "black", &black_color); 1002 empty_cursor = XCreatePixmapCursor(theDisplay, empty_pixmap, 1003 empty_pixmap, &black_color, &black_color, 0, 0); 1004 } 1005 1006 /* Add event handler to hide the pointer on keypresses */ 1007 XtAddEventHandler((Widget)new, NEDIT_HIDE_CURSOR_MASK, False, 1008 handleHidePointer, (Opaque)NULL); 1009 } 1010 1011 new->text.last_keyevent_serial = 0; 1012 new->text.last_keyevent_keycode = 0; 1013 new->text.last_keyevent_time = 0; 1014 } 1015 1016 /* Hide the pointer while the user is typing */ 1017 static void handleHidePointer(Widget w, XtPointer unused, 1018 XEvent *event, Boolean *continue_to_dispatch) { 1019 TextWidget tw = (TextWidget) w; 1020 ShowHidePointer(tw, True); 1021 } 1022 1023 /* Restore the pointer if the mouse moves or focus changes */ 1024 static void handleShowPointer(Widget w, XtPointer unused, 1025 XEvent *event, Boolean *continue_to_dispatch) { 1026 TextWidget tw = (TextWidget) w; 1027 ShowHidePointer(tw, False); 1028 } 1029 1030 void ShowHidePointer(TextWidget w, Boolean hidePointer) 1031 { 1032 if (w->text.hidePointer) { 1033 if (hidePointer != w->text.textD->pointerHidden) { 1034 if (hidePointer) { 1035 /* Don't listen for keypresses any more */ 1036 XtRemoveEventHandler((Widget)w, NEDIT_HIDE_CURSOR_MASK, False, 1037 handleHidePointer, (Opaque)NULL); 1038 /* Switch to empty cursor */ 1039 XDefineCursor(XtDisplay(w), XtWindow(w), empty_cursor); 1040 1041 w->text.textD->pointerHidden = True; 1042 1043 /* Listen to mouse movement, focus change, and button presses */ 1044 XtAddEventHandler((Widget)w, NEDIT_SHOW_CURSOR_MASK, 1045 False, handleShowPointer, (Opaque)NULL); 1046 } 1047 else { 1048 /* Don't listen to mouse/focus events any more */ 1049 XtRemoveEventHandler((Widget)w, NEDIT_SHOW_CURSOR_MASK, 1050 False, handleShowPointer, (Opaque)NULL); 1051 /* Switch to regular cursor */ 1052 XUndefineCursor(XtDisplay(w), XtWindow(w)); 1053 1054 w->text.textD->pointerHidden = False; 1055 1056 /* Listen for keypresses now */ 1057 XtAddEventHandler((Widget)w, NEDIT_HIDE_CURSOR_MASK, False, 1058 handleHidePointer, (Opaque)NULL); 1059 } 1060 } 1061 } 1062 } 1063 1064 /* 1065 ** Widget destroy method 1066 */ 1067 static void destroy(TextWidget w) 1068 { 1069 textBuffer *buf; 1070 1071 /* Free the text display and possibly the attached buffer. The buffer 1072 is freed only if after removing all of the modify procs (by calling 1073 StopHandlingXSelections and TextDFree) there are no modify procs 1074 left */ 1075 StopHandlingXSelections((Widget)w); 1076 buf = w->text.textD->buffer; 1077 TextDFree(w->text.textD); 1078 if (buf->nModifyProcs == 0) 1079 BufFree(buf); 1080 1081 if (w->text.cursorBlinkProcID != 0) 1082 XtRemoveTimeOut(w->text.cursorBlinkProcID); 1083 NEditFree(w->text.delimiters); 1084 XtRemoveAllCallbacks((Widget)w, textNfocusCallback); 1085 XtRemoveAllCallbacks((Widget)w, textNlosingFocusCallback); 1086 XtRemoveAllCallbacks((Widget)w, textNcursorMovementCallback); 1087 XtRemoveAllCallbacks((Widget)w, textNdragStartCallback); 1088 XtRemoveAllCallbacks((Widget)w, textNdragEndCallback); 1089 1090 FontUnref(w->text.font); 1091 FontUnref(w->text.boldFont); 1092 FontUnref(w->text.italicFont); 1093 FontUnref(w->text.boldItalicFont); 1094 1095 #ifndef NO_XMIM 1096 /* Unregister the widget from the input manager */ 1097 XmImUnregister((Widget)w); 1098 #endif 1099 } 1100 1101 /* 1102 ** Widget resize method. Called when the size of the widget changes 1103 */ 1104 static void resize(TextWidget w) 1105 { 1106 XftFont *fs = FontDefault(w->text.font); 1107 NFont *font = w->text.font; 1108 int height = w->core.height, width = w->core.width; 1109 int marginWidth = w->text.marginWidth, marginHeight = w->text.marginHeight; 1110 int lineNumAreaWidth = w->text.lineNumCols == 0 ? 0 : w->text.marginWidth + 1111 font->maxWidth * w->text.lineNumCols; 1112 1113 //int test_max_advance_width = fs->max_advance_width; 1114 w->text.columns = (width - marginWidth*2 - lineNumAreaWidth) / 1115 font->maxWidth /* fs->max_advance_width */; 1116 w->text.rows = (height - marginHeight*2) / (fs->ascent + fs->descent); 1117 1118 /* Reject widths and heights less than a character, which the text 1119 display can't tolerate. This is not strictly legal, but I've seen 1120 it done in other widgets and it seems to do no serious harm. NEdit 1121 prevents panes from getting smaller than one line, but sometimes 1122 splitting windows on Linux 2.0 systems (same Motif, why the change in 1123 behavior?), causes one or two resize calls with < 1 line of height. 1124 Fixing it here is 100x easier than re-designing textDisp.c */ 1125 if (w->text.columns < 1) { 1126 w->text.columns = 1; 1127 w->core.width = width = font->maxWidth + marginWidth*2 + 1128 lineNumAreaWidth; 1129 } 1130 if (w->text.rows < 1) { 1131 w->text.rows = 1; 1132 w->core.height = height = fs->ascent + fs->descent + marginHeight*2; 1133 } 1134 1135 /* Resize the text display that the widget uses to render text */ 1136 TextDResize(w->text.textD, width - marginWidth*2 - lineNumAreaWidth, 1137 height - marginHeight*2); 1138 1139 /* if the window became shorter or narrower, there may be text left 1140 in the bottom or right margin area, which must be cleaned up */ 1141 if (XtIsRealized((Widget)w)) { 1142 XClearArea(XtDisplay(w), XtWindow(w), 0, height-marginHeight, 1143 width, marginHeight, False); 1144 XClearArea(XtDisplay(w), XtWindow(w),width-marginWidth, 1145 0, marginWidth, height, False); 1146 } 1147 } 1148 1149 /* 1150 ** Widget redisplay method 1151 */ 1152 static void redisplay(TextWidget w, XEvent *event, Region region) 1153 { 1154 XExposeEvent *e = &event->xexpose; 1155 1156 TextDRedisplayRect(w->text.textD, e->x, e->y, e->width, e->height); 1157 } 1158 1159 static Bool findGraphicsExposeOrNoExposeEvent(Display *theDisplay, XEvent *event, XPointer arg) 1160 { 1161 if ((theDisplay == event->xany.display) && 1162 (event->type == GraphicsExpose || event->type == NoExpose) && 1163 ((Widget)arg == XtWindowToWidget(event->xany.display, event->xany.window))) { 1164 return(True); 1165 } 1166 else { 1167 return(False); 1168 } 1169 } 1170 1171 static void adjustRectForGraphicsExposeOrNoExposeEvent(TextWidget w, XEvent *event, 1172 Boolean *first, int *left, int *top, int *width, int *height) 1173 { 1174 Boolean removeQueueEntry = False; 1175 1176 if (event->type == GraphicsExpose) { 1177 XGraphicsExposeEvent *e = &event->xgraphicsexpose; 1178 int x = e->x, y = e->y; 1179 1180 TextDImposeGraphicsExposeTranslation(w->text.textD, &x, &y); 1181 if (*first) { 1182 *left = x; 1183 *top = y; 1184 *width = e->width; 1185 *height = e->height; 1186 1187 *first = False; 1188 } 1189 else { 1190 int prev_left = *left; 1191 int prev_top = *top; 1192 1193 *left = min(*left, x); 1194 *top = min(*top, y); 1195 *width = max(prev_left + *width, x + e->width) - *left; 1196 *height = max(prev_top + *height, y + e->height) - *top; 1197 } 1198 if (e->count == 0) { 1199 removeQueueEntry = True; 1200 } 1201 } 1202 else if (event->type == NoExpose) { 1203 removeQueueEntry = True; 1204 } 1205 if (removeQueueEntry) { 1206 TextDPopGraphicExposeQueueEntry(w->text.textD); 1207 } 1208 } 1209 1210 static void redisplayGE(TextWidget w, XtPointer client_data, 1211 XEvent *event, Boolean *continue_to_dispatch_return) 1212 { 1213 if (event->type == GraphicsExpose || event->type == NoExpose) { 1214 HandleAllPendingGraphicsExposeNoExposeEvents(w, event); 1215 } 1216 } 1217 1218 static void hscrollEH(TextWidget w, XtPointer client_data, 1219 XEvent *event, Boolean *continue_to_dispatch_return) 1220 { 1221 int topLineNum, horizOffset, step; 1222 int button = event->xbutton.button; 1223 textDisp *textD = w->text.textD; 1224 1225 if(button != 6 && button != 7) { 1226 return; 1227 } 1228 1229 int charWidth = 8; 1230 if(textD->font) { 1231 charWidth = textD->font->maxWidth; 1232 } 1233 step = (button == 6 ? -1 : 1) * charWidth; 1234 1235 1236 TextDGetScroll(textD, &topLineNum, &horizOffset); 1237 TextDSetScroll(textD, topLineNum, horizOffset + step); 1238 } 1239 1240 void HandleAllPendingGraphicsExposeNoExposeEvents(TextWidget w, XEvent *event) 1241 { 1242 XEvent foundEvent; 1243 int left; 1244 int top; 1245 int width; 1246 int height; 1247 Boolean invalidRect = True; 1248 1249 if (event) { 1250 adjustRectForGraphicsExposeOrNoExposeEvent(w, event, &invalidRect, &left, &top, &width, &height); 1251 } 1252 while (XCheckIfEvent(XtDisplay(w), &foundEvent, findGraphicsExposeOrNoExposeEvent, (XPointer)w)) { 1253 adjustRectForGraphicsExposeOrNoExposeEvent(w, &foundEvent, &invalidRect, &left, &top, &width, &height); 1254 } 1255 if (!invalidRect) { 1256 TextDRedisplayRect(w->text.textD, left, top, width, height); 1257 } 1258 } 1259 1260 /* 1261 ** Widget setValues method 1262 */ 1263 static Boolean setValues(TextWidget current, TextWidget request, 1264 TextWidget new) 1265 { 1266 Boolean redraw = False, reconfigure = False; 1267 NFont *font = new->text.font; 1268 1269 if (new->text.overstrike != current->text.overstrike) { 1270 if (current->text.textD->cursorStyle == BLOCK_CURSOR) 1271 TextDSetCursorStyle(current->text.textD, 1272 current->text.heavyCursor ? HEAVY_CURSOR : NORMAL_CURSOR); 1273 else if (current->text.textD->cursorStyle == NORMAL_CURSOR || 1274 current->text.textD->cursorStyle == HEAVY_CURSOR) 1275 TextDSetCursorStyle(current->text.textD, BLOCK_CURSOR); 1276 } 1277 1278 if (new->text.boldFont != current->text.boldFont) { 1279 FontUnref(current->text.boldFont); 1280 FontRef(new->text.boldFont); 1281 TextDSetBoldFont(new->text.textD, new->text.boldFont); 1282 } 1283 if (new->text.italicFont != current->text.italicFont) { 1284 FontUnref(current->text.italicFont); 1285 FontRef(new->text.italicFont); 1286 TextDSetItalicFont(new->text.textD, new->text.italicFont); 1287 } 1288 if (new->text.boldItalicFont != current->text.boldItalicFont) { 1289 FontUnref(current->text.boldItalicFont); 1290 FontRef(new->text.boldItalicFont); 1291 TextDSetBoldItalicFont(new->text.textD, new->text.boldItalicFont); 1292 } 1293 if (new->text.font != current->text.font) { 1294 FontUnref(current->text.font); 1295 FontRef(new->text.font); 1296 if (new->text.lineNumCols != 0) 1297 reconfigure = True; 1298 1299 int fontWidth = new->text.font->minWidth; 1300 int textLeft = fontWidth/3 + 1; 1301 if(new->text.lineNumCols == 0) { 1302 current->text.textD->left = textLeft; 1303 } else { 1304 current->text.textD->left = textLeft + new->text.marginWidth + current->text.textD->lineNumWidth; 1305 } 1306 TextDSetFont(current->text.textD, new->text.font); 1307 } 1308 1309 if (new->text.wrapMargin != current->text.wrapMargin || 1310 new->text.continuousWrap != current->text.continuousWrap) 1311 TextDSetWrapMode(current->text.textD, new->text.continuousWrap, 1312 new->text.wrapMargin); 1313 1314 /* When delimiters are changed, copy the memory, so that the caller 1315 doesn't have to manage it, and add mandatory delimiters blank, 1316 tab, and newline to the list */ 1317 if (new->text.delimiters != current->text.delimiters) { 1318 char *delimiters = (char*)NEditMalloc(strlen(new->text.delimiters) + 4); 1319 NEditFree(current->text.delimiters); 1320 sprintf(delimiters, "%s%s", " \t\n", new->text.delimiters); 1321 new->text.delimiters = delimiters; 1322 } 1323 1324 /* Setting the lineNumCols resource tells the text widget to hide or 1325 show, or change the number of columns of the line number display, 1326 which requires re-organizing the x coordinates of both the line 1327 number display and the main text display */ 1328 if (new->text.lineNumCols != current->text.lineNumCols || reconfigure) 1329 { 1330 int marginWidth = new->text.marginWidth; 1331 int charWidth = font->maxWidth; 1332 int lineNumCols = new->text.lineNumCols; 1333 int fontWidth = new->text.font->minWidth; 1334 int textLeft = fontWidth/3 + 1; 1335 if (lineNumCols == 0) 1336 { 1337 TextDSetLineNumberArea(new->text.textD, 0, 0, textLeft); 1338 new->text.columns = (new->core.width - marginWidth*2) / charWidth; 1339 } else 1340 { 1341 TextDSetLineNumberArea(new->text.textD, marginWidth, 1342 charWidth * lineNumCols, 1343 textLeft + marginWidth + charWidth * lineNumCols); 1344 new->text.columns = (new->core.width - marginWidth*3 - charWidth 1345 * lineNumCols) / charWidth; 1346 } 1347 } 1348 1349 if (new->text.backlightCharTypes != current->text.backlightCharTypes) 1350 { 1351 TextDSetupBGClasses((Widget)new, new->text.backlightCharTypes, 1352 &new->text.textD->bgClassPixel, &new->text.textD->bgClass, 1353 new->text.textD->colorProfile->textBgColor); 1354 redraw = True; 1355 } 1356 1357 if (new->text.highlightCursorLine != current->text.highlightCursorLine) 1358 { 1359 TextDSetHighlightCursorLine(new->text.textD, new->text.highlightCursorLine); 1360 redraw = True; 1361 } 1362 1363 if (new->text.indentRainbow != current->text.indentRainbow) 1364 { 1365 TextDSetIndentRainbow(new->text.textD, new->text.indentRainbow); 1366 redraw = True; 1367 } 1368 1369 if (new->text.ansiColors != current->text.ansiColors) { 1370 TextDSetAnsiColors(new->text.textD, new->text.ansiColors); 1371 redraw = True; 1372 } 1373 1374 if (new->text.ansiColorList != current->text.ansiColorList) { 1375 redraw = True; 1376 } 1377 1378 if(new->text.hScrollBar != current->text.hScrollBar) { 1379 new->text.textD->hScrollBar = new->text.hScrollBar; 1380 } 1381 if(new->text.vScrollBar != current->text.vScrollBar) { 1382 new->text.textD->vScrollBar = new->text.vScrollBar; 1383 } 1384 1385 return redraw; 1386 } 1387 1388 /* 1389 void TextPrintXIMError(void) { 1390 static int xim_err = 0; 1391 if(!xim_err) { 1392 fprintf(stderr, "Cannot get X Input Manager\n"); 1393 } 1394 xim_err = 1; 1395 } 1396 */ 1397 1398 /* 1399 ** Widget realize method 1400 */ 1401 static void realize(Widget w, XtValueMask *valueMask, 1402 XSetWindowAttributes *attributes) 1403 { 1404 /* Set bit gravity window attribute. This saves a full blank and redraw 1405 on window resizing */ 1406 *valueMask |= CWBitGravity; 1407 attributes->bit_gravity = NorthWestGravity; 1408 1409 /* Continue with realize method from superclass */ 1410 (xmPrimitiveClassRec.core_class.realize)(w, valueMask, attributes); 1411 1412 /* create X input context */ 1413 TextWidget text = (TextWidget)w; 1414 text->text.xim = XmImGetXIM(w); 1415 if(text->text.xim) { 1416 Window win = XtWindow(w); 1417 XIMStyle style = XIMPreeditNothing | XIMStatusNothing; 1418 text->text.xic = XCreateIC( 1419 text->text.xim, 1420 XNInputStyle, 1421 style, 1422 XNClientWindow, 1423 win, 1424 XNFocusWindow, 1425 win, 1426 NULL); 1427 } 1428 1429 TextDInitXft(text->text.textD); 1430 1431 XdndEnable(w); 1432 } 1433 1434 /* 1435 ** Widget query geometry method ... unless asked to negotiate a different size simply return current size. 1436 */ 1437 static XtGeometryResult queryGeometry(Widget w, XtWidgetGeometry *proposed, 1438 XtWidgetGeometry *answer) 1439 { 1440 TextWidget tw = (TextWidget)w; 1441 1442 int curHeight = tw->core.height; 1443 int curWidth = tw->core.width; 1444 XftFont *font = FontDefault(tw->text.textD->font); 1445 int fontWidth = tw->text.textD->font->maxWidth; 1446 int fontHeight = font->height; 1447 int marginHeight = tw->text.marginHeight; 1448 int propWidth = (proposed->request_mode & CWWidth) ? proposed->width : 0; 1449 int propHeight = (proposed->request_mode & CWHeight) ? proposed->height : 0; 1450 1451 answer->request_mode = CWHeight | CWWidth; 1452 1453 if(proposed->request_mode & CWWidth) 1454 /* Accept a width no smaller than 10 chars */ 1455 answer->width = max(fontWidth * 10, proposed->width); 1456 else 1457 answer->width = curWidth; 1458 1459 if(proposed->request_mode & CWHeight) 1460 /* Accept a height no smaller than an exact multiple of the line height 1461 and at least one line high */ 1462 answer->height = max(1, ((propHeight - 2*marginHeight) / fontHeight)) * 1463 fontHeight + 2*marginHeight; 1464 else 1465 answer->height = curHeight; 1466 1467 /*printf("propWidth %d, propHeight %d, ansWidth %d, ansHeight %d\n", 1468 propWidth, propHeight, answer->width, answer->height);*/ 1469 if (propWidth == answer->width && propHeight == answer->height) 1470 return XtGeometryYes; 1471 else if (answer->width == curWidth && answer->height == curHeight) 1472 return XtGeometryNo; 1473 else 1474 return XtGeometryAlmost; 1475 } 1476 1477 /* 1478 ** Set the text buffer which this widget will display and interact with. 1479 ** The currently attached buffer is automatically freed, ONLY if it has 1480 ** no additional modify procs attached (as it would if it were being 1481 ** displayed by another text widget). 1482 */ 1483 void TextSetBuffer(Widget w, textBuffer *buffer) 1484 { 1485 textBuffer *oldBuf = ((TextWidget)w)->text.textD->buffer; 1486 1487 StopHandlingXSelections(w); 1488 TextDSetBuffer(((TextWidget)w)->text.textD, buffer); 1489 if (oldBuf->nModifyProcs == 0) 1490 BufFree(oldBuf); 1491 } 1492 1493 /* 1494 ** Get the buffer associated with this text widget. Note that attaching 1495 ** additional modify callbacks to the buffer will prevent it from being 1496 ** automatically freed when the widget is destroyed. 1497 */ 1498 textBuffer *TextGetBuffer(Widget w) 1499 { 1500 return ((TextWidget)w)->text.textD->buffer; 1501 } 1502 1503 /* 1504 ** Translate a line number and column into a position 1505 */ 1506 int TextLineAndColToPos(Widget w, int lineNum, int column) 1507 { 1508 return TextDLineAndColToPos(((TextWidget)w)->text.textD, lineNum, column ); 1509 } 1510 1511 /* 1512 ** Translate a position into a line number (if the position is visible, 1513 ** if it's not, return False 1514 */ 1515 int TextPosToLineAndCol(Widget w, int pos, int *lineNum, int *column) 1516 { 1517 return TextDPosToLineAndCol(((TextWidget)w)->text.textD, pos, lineNum, 1518 column); 1519 } 1520 1521 /* 1522 ** Translate a buffer text position to the XY location where the center 1523 ** of the cursor would be positioned to point to that character. Returns 1524 ** False if the position is not displayed because it is VERTICALLY out 1525 ** of view. If the position is horizontally out of view, returns the 1526 ** x coordinate where the position would be if it were visible. 1527 */ 1528 int TextPosToXY(Widget w, int pos, int *x, int *y) 1529 { 1530 return TextDPositionToXY(((TextWidget)w)->text.textD, pos, x, y); 1531 } 1532 1533 /* 1534 ** Return the cursor position 1535 */ 1536 int TextGetCursorPos(Widget w) 1537 { 1538 return TextDGetInsertPosition(((TextWidget)w)->text.textD); 1539 } 1540 1541 /* 1542 ** Set the cursor position 1543 */ 1544 void TextSetCursorPos(Widget w, int pos) 1545 { 1546 TextDSetInsertPosition(((TextWidget)w)->text.textD, pos); 1547 checkAutoShowInsertPos(w); 1548 callCursorMovementCBs(w, NULL); 1549 1550 } 1551 1552 // set multiple cursors 1553 void TextSetCursors(Widget w, size_t *cursors, size_t ncursors) 1554 { 1555 textDisp *textD = ((TextWidget)w)->text.textD; 1556 TextDSetCursors(textD, cursors, ncursors); 1557 callCursorMovementCBs(w, NULL); 1558 } 1559 1560 int TextGetLastCursorPos(Widget w) 1561 { 1562 textDisp *textD = ((TextWidget)w)->text.textD; 1563 return textD->multicursor[textD->mcursorSize-1].cursorPos; 1564 } 1565 1566 void TextSetLastCursorPos(Widget w, int pos) 1567 { 1568 textDisp *textD = ((TextWidget)w)->text.textD; 1569 size_t mcursorSize = textD->mcursorSize; 1570 1571 // emulate single cursor 1572 textD->mcursorSize = 1; 1573 textD->cursor = &textD->multicursor[mcursorSize-1]; 1574 TextSetCursorPos(w, pos); 1575 1576 // restore mcursor 1577 textD->mcursorSize = mcursorSize; 1578 } 1579 1580 int TextNumCursors(Widget w) 1581 { 1582 textDisp *textD = ((TextWidget)w)->text.textD; 1583 return (int)textD->mcursorSizeReal; 1584 } 1585 1586 void TextChangeCursors(Widget w, int startPos, int diff) 1587 { 1588 TextDChangeCursors(((TextWidget)w)->text.textD, startPos, diff); 1589 callCursorMovementCBs(w, NULL); 1590 TextDBlankCursor(((TextWidget)w)->text.textD); 1591 } 1592 1593 void TextClearMultiCursors(Widget w) 1594 { 1595 textDisp *textD = ((TextWidget)w)->text.textD; 1596 if(TextDClearMultiCursor(textD)) { 1597 TextDRedisplayRect(textD, 0, textD->top, textD->width + textD->left, textD->height); 1598 } 1599 } 1600 1601 /* 1602 ** Return the horizontal and vertical scroll positions of the widget 1603 */ 1604 void TextGetScroll(Widget w, int *topLineNum, int *horizOffset) 1605 { 1606 TextDGetScroll(((TextWidget)w)->text.textD, topLineNum, horizOffset); 1607 } 1608 1609 /* 1610 ** Set the horizontal and vertical scroll positions of the widget 1611 */ 1612 void TextSetScroll(Widget w, int topLineNum, int horizOffset) 1613 { 1614 TextDSetScroll(((TextWidget)w)->text.textD, topLineNum, horizOffset); 1615 } 1616 1617 int TextGetMinFontWidth(Widget w, Boolean considerStyles) 1618 { 1619 return(TextDMinFontWidth(((TextWidget)w)->text.textD, considerStyles)); 1620 } 1621 1622 int TextGetMaxFontWidth(Widget w, Boolean considerStyles) 1623 { 1624 return(TextDMaxFontWidth(((TextWidget)w)->text.textD, considerStyles)); 1625 } 1626 1627 /* 1628 ** Set this widget to be the owner of selections made in it's attached 1629 ** buffer (text buffers may be shared among several text widgets). 1630 */ 1631 void TextHandleXSelections(Widget w) 1632 { 1633 HandleXSelections(w); 1634 } 1635 1636 void TextPasteClipboard(Widget w, Time time) 1637 { 1638 cancelDrag(w); 1639 if (checkReadOnly(w)) 1640 return; 1641 TakeMotifDestination(w, time); 1642 InsertClipboard(w, False); 1643 callCursorMovementCBs(w, NULL); 1644 } 1645 1646 void TextColPasteClipboard(Widget w, Time time) 1647 { 1648 cancelDrag(w); 1649 if (checkReadOnly(w)) 1650 return; 1651 TakeMotifDestination(w, time); 1652 InsertClipboard(w, True); 1653 callCursorMovementCBs(w, NULL); 1654 } 1655 1656 void TextCopyClipboard(Widget w, Time time) 1657 { 1658 cancelDrag(w); 1659 if (!((TextWidget)w)->text.textD->buffer->primary.selected) { 1660 XBell(XtDisplay(w), 0); 1661 return; 1662 } 1663 CopyToClipboard(w, time); 1664 } 1665 1666 void TextCutClipboard(Widget w, Time time) 1667 { 1668 textDisp *textD = ((TextWidget)w)->text.textD; 1669 1670 cancelDrag(w); 1671 if (checkReadOnly(w)) 1672 return; 1673 if (!textD->buffer->primary.selected) { 1674 XBell(XtDisplay(w), 0); 1675 return; 1676 } 1677 TakeMotifDestination(w, time); 1678 CopyToClipboard (w, time); 1679 BufRemoveSelected(textD->buffer); 1680 TextDSetInsertPosition(textD, textD->buffer->cursorPosHint); 1681 checkAutoShowInsertPos(w); 1682 } 1683 1684 int TextFirstVisibleLine(Widget w) 1685 { 1686 return(((TextWidget)w)->text.textD->topLineNum); 1687 } 1688 1689 int TextNumVisibleLines(Widget w) 1690 { 1691 return(((TextWidget)w)->text.textD->nVisibleLines); 1692 } 1693 1694 int TextVisibleWidth(Widget w) 1695 { 1696 return(((TextWidget)w)->text.textD->width); 1697 } 1698 1699 int TextFirstVisiblePos(Widget w) 1700 { 1701 return ((TextWidget)w)->text.textD->firstChar; 1702 } 1703 1704 int TextLastVisiblePos(Widget w) 1705 { 1706 return ((TextWidget)w)->text.textD->lastChar; 1707 } 1708 1709 /* 1710 ** Insert text "chars" at the cursor position, respecting pending delete 1711 ** selections, overstrike, and handling cursor repositioning as if the text 1712 ** had been typed. If autoWrap is on wraps the text to fit within the wrap 1713 ** margin, auto-indenting where the line was wrapped (but nowhere else). 1714 ** "allowPendingDelete" controls whether primary selections in the widget are 1715 ** treated as pending delete selections (True), or ignored (False). "event" 1716 ** is optional and is just passed on to the cursor movement callbacks. 1717 */ 1718 void TextInsertAtCursor(Widget w, char *chars, XEvent *event, 1719 int allowPendingDelete, int allowWrap) 1720 { 1721 int wrapMargin, colNum, lineStartPos, cursorPos; 1722 char *c, *lineStartText, *wrappedText; 1723 TextWidget tw = (TextWidget)w; 1724 textDisp *textD = tw->text.textD; 1725 textBuffer *buf = textD->buffer; 1726 int fontWidth = textD->font->maxWidth; 1727 int replaceSel, singleLine, breakAt = 0; 1728 1729 /* Don't wrap if auto-wrap is off or suppressed, or it's just a newline */ 1730 if (!allowWrap || !tw->text.autoWrap || 1731 (chars[0] == '\n' && chars[1] == '\0')) { 1732 simpleInsertAtCursor(w, chars, event, allowPendingDelete); 1733 return; 1734 } 1735 1736 /* If this is going to be a pending delete operation, the real insert 1737 position is the start of the selection. This will make rectangular 1738 selections wrap strangely, but this routine should rarely be used for 1739 them, and even more rarely when they need to be wrapped. */ 1740 replaceSel = allowPendingDelete && pendingSelection(w); 1741 cursorPos = replaceSel ? buf->primary.start : TextDGetInsertPosition(textD); 1742 1743 /* If the text is only one line and doesn't need to be wrapped, just insert 1744 it and be done (for efficiency only, this routine is called for each 1745 character typed). (Of course, it may not be significantly more efficient 1746 than the more general code below it, so it may be a waste of time!) */ 1747 wrapMargin = tw->text.wrapMargin != 0 ? tw->text.wrapMargin : 1748 textD->width / fontWidth; 1749 lineStartPos = BufStartOfLine(buf, cursorPos); 1750 colNum = BufCountDispChars(buf, lineStartPos, cursorPos); 1751 for (c=chars; *c!='\0' && *c!='\n'; c++) 1752 colNum += BufCharWidth(*c, colNum, buf->tabDist, buf->nullSubsChar); 1753 singleLine = *c == '\0'; 1754 if (colNum < wrapMargin && singleLine) { 1755 simpleInsertAtCursor(w, chars, event, True); 1756 return; 1757 } 1758 1759 /* Wrap the text */ 1760 lineStartText = BufGetRange(buf, lineStartPos, cursorPos); 1761 wrappedText = wrapText(tw, lineStartText, chars, lineStartPos, wrapMargin, 1762 replaceSel ? NULL : &breakAt); 1763 NEditFree(lineStartText); 1764 1765 /* Insert the text. Where possible, use TextDInsert which is optimized 1766 for less redraw. */ 1767 if (replaceSel) { 1768 BufReplaceSelected(buf, wrappedText); 1769 TextDSetInsertPosition(textD, buf->cursorPosHint); 1770 } else if (tw->text.overstrike) { 1771 if (breakAt == 0 && singleLine) 1772 TextDOverstrike(textD, wrappedText); 1773 else { 1774 BufReplace(buf, cursorPos-breakAt, cursorPos, wrappedText); 1775 TextDSetInsertPosition(textD, buf->cursorPosHint); 1776 } 1777 } else { 1778 if (breakAt == 0) { 1779 TextDInsert(textD, wrappedText); 1780 } else { 1781 BufReplace(buf, cursorPos-breakAt, cursorPos, wrappedText); 1782 TextDSetInsertPosition(textD, buf->cursorPosHint); 1783 } 1784 } 1785 NEditFree(wrappedText); 1786 checkAutoShowInsertPos(w); 1787 callCursorMovementCBs(w, event); 1788 } 1789 1790 /* 1791 ** Fetch text from the widget's buffer, adding wrapping newlines to emulate 1792 ** effect acheived by wrapping in the text display in continuous wrap mode. 1793 */ 1794 char *TextGetWrapped(Widget w, int startPos, int endPos, int *outLen) 1795 { 1796 textDisp *textD = ((TextWidget)w)->text.textD; 1797 textBuffer *buf = textD->buffer; 1798 textBuffer *outBuf; 1799 int fromPos, toPos, outPos; 1800 char c, *outString; 1801 1802 if (!((TextWidget)w)->text.continuousWrap || startPos == endPos) { 1803 *outLen = endPos - startPos; 1804 return BufGetRange(buf, startPos, endPos); 1805 } 1806 1807 /* Create a text buffer with a good estimate of the size that adding 1808 newlines will expand it to. Since it's a text buffer, if we guess 1809 wrong, it will fail softly, and simply expand the size */ 1810 outBuf = BufCreatePreallocated((endPos-startPos) + (endPos-startPos)/5); 1811 outPos = 0; 1812 1813 /* Go (displayed) line by line through the buffer, adding newlines where 1814 the text is wrapped at some character other than an existing newline */ 1815 fromPos = startPos; 1816 toPos = TextDCountForwardNLines(textD, startPos, 1, False); 1817 while (toPos < endPos) { 1818 BufCopyFromBuf(buf, outBuf, fromPos, toPos, outPos); 1819 outPos += toPos - fromPos; 1820 c = BufGetCharacter(outBuf, outPos-1); 1821 if (c == ' ' || c == '\t') 1822 BufReplace(outBuf, outPos-1, outPos, "\n"); 1823 else if (c != '\n') { 1824 BufInsert(outBuf, outPos, "\n"); 1825 outPos++; 1826 } 1827 fromPos = toPos; 1828 toPos = TextDCountForwardNLines(textD, fromPos, 1, True); 1829 } 1830 BufCopyFromBuf(buf, outBuf, fromPos, endPos, outPos); 1831 1832 /* return the contents of the output buffer as a string */ 1833 outString = BufGetAll(outBuf); 1834 *outLen = outBuf->length; 1835 BufFree(outBuf); 1836 return outString; 1837 } 1838 1839 /* 1840 ** Return the (statically allocated) action table for menu item actions. 1841 ** 1842 ** Warning: This routine can only be used before the first text widget is 1843 ** created! After that, apparently, Xt takes over the table and overwrites 1844 ** it with its own version. XtGetActionList is preferable, but is not 1845 ** available before X11R5. 1846 */ 1847 XtActionsRec *TextGetActions(int *nActions) 1848 { 1849 *nActions = XtNumber(actionsList); 1850 return actionsList; 1851 } 1852 1853 static void grabFocusAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) 1854 { 1855 XButtonEvent *e = &event->xbutton; 1856 TextWidget tw = (TextWidget)w; 1857 textDisp *textD = tw->text.textD; 1858 Time lastBtnDown = tw->text.lastBtnDown; 1859 int row, column; 1860 1861 /* Indicate state for future events, PRIMARY_CLICKED indicates that 1862 the proper initialization has been done for primary dragging and/or 1863 multi-clicking. Also record the timestamp for multi-click processing */ 1864 tw->text.dragState = PRIMARY_CLICKED; 1865 tw->text.lastBtnDown = e->time; 1866 1867 /* Become owner of the MOTIF_DESTINATION selection, making this widget 1868 the designated recipient of secondary quick actions in Motif XmText 1869 widgets and in other NEdit text widgets */ 1870 TakeMotifDestination(w, e->time); 1871 1872 /* Check for possible multi-click sequence in progress */ 1873 if (tw->text.multiClickState != NO_CLICKS) { 1874 if (e->time < lastBtnDown + XtGetMultiClickTime(XtDisplay(w))) { 1875 if (tw->text.multiClickState == ONE_CLICK) { 1876 selectWord(w, e->x); 1877 callCursorMovementCBs(w, event); 1878 return; 1879 } else if (tw->text.multiClickState == TWO_CLICKS) { 1880 selectLine(w); 1881 callCursorMovementCBs(w, event); 1882 return; 1883 } else if (tw->text.multiClickState == THREE_CLICKS) { 1884 BufSelect(textD->buffer, 0, textD->buffer->length); 1885 return; 1886 } else if (tw->text.multiClickState > THREE_CLICKS) 1887 tw->text.multiClickState = NO_CLICKS; 1888 } else 1889 tw->text.multiClickState = NO_CLICKS; 1890 } 1891 1892 /* Clear any existing selections */ 1893 BufUnselect(textD->buffer); 1894 1895 /* Move the cursor to the pointer location */ 1896 moveDestinationAP(w, event, args, nArgs); 1897 1898 /* Record the site of the initial button press and the initial character 1899 position so subsequent motion events and clicking can decide when and 1900 where to begin a primary selection */ 1901 tw->text.btnDownX = e->x; 1902 tw->text.btnDownY = e->y; 1903 tw->text.anchor = TextDGetInsertPosition(textD); 1904 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column); 1905 column = TextDOffsetWrappedColumn(textD, row, column); 1906 tw->text.rectAnchor = column; 1907 } 1908 1909 static void cancelAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) { 1910 TextClearMultiCursors(w); 1911 callCursorMovementCBs(w, event); 1912 } 1913 1914 static void moveDestinationAP(Widget w, XEvent *event, String *args, 1915 Cardinal *nArgs) 1916 { 1917 XButtonEvent *e = &event->xbutton; 1918 textDisp *textD = ((TextWidget)w)->text.textD; 1919 1920 /* Get input focus */ 1921 XmProcessTraversal(w, XmTRAVERSE_CURRENT); 1922 1923 /* Move the cursor */ 1924 int cursorPos = TextDXYToPosition(textD, e->x, e->y); 1925 if(*nArgs == 0) { 1926 TextDSetInsertPosition(textD, cursorPos); 1927 checkAutoShowInsertPos(w); 1928 } else if (!strcmp(args[0], "mc")) { 1929 int cursorIndex = TextDAddCursor(textD, cursorPos); 1930 mod_ctrl_mc_last_event = mod_ctrl_last_event; 1931 if(cursorIndex >= 0) { 1932 TextDBlankCursor(textD); 1933 TextDRemoveCursor(textD, cursorIndex); 1934 } 1935 TextDUnblankCursor(textD); 1936 } 1937 callCursorMovementCBs(w, event); 1938 } 1939 1940 static void extendAdjustAP(Widget w, XEvent *event, String *args, 1941 Cardinal *nArgs) 1942 { 1943 TextWidget tw = (TextWidget)w; 1944 XMotionEvent *e = &event->xmotion; 1945 int dragState = tw->text.dragState; 1946 int rectDrag = hasKey("rect", args, nArgs); 1947 1948 /* Make sure the proper initialization was done on mouse down */ 1949 if (dragState != PRIMARY_DRAG && dragState != PRIMARY_CLICKED && 1950 dragState != PRIMARY_RECT_DRAG) 1951 return; 1952 1953 /* If the selection hasn't begun, decide whether the mouse has moved 1954 far enough from the initial mouse down to be considered a drag */ 1955 if (tw->text.dragState == PRIMARY_CLICKED) { 1956 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD || 1957 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD) 1958 tw->text.dragState = rectDrag ? PRIMARY_RECT_DRAG : PRIMARY_DRAG; 1959 else 1960 return; 1961 } 1962 1963 /* If "rect" argument has appeared or disappeared, keep dragState up 1964 to date about which type of drag this is */ 1965 tw->text.dragState = rectDrag ? PRIMARY_RECT_DRAG : PRIMARY_DRAG; 1966 1967 /* Record the new position for the autoscrolling timer routine, and 1968 engage or disengage the timer if the mouse is in/out of the window */ 1969 checkAutoScroll(tw, e->x, e->y); 1970 1971 /* Adjust the selection and move the cursor */ 1972 adjustSelection(tw, e->x, e->y); 1973 } 1974 1975 static void extendStartAP(Widget w, XEvent *event, String *args, 1976 Cardinal *nArgs) 1977 { 1978 XMotionEvent *e = &event->xmotion; 1979 textDisp *textD = ((TextWidget)w)->text.textD; 1980 textBuffer *buf = textD->buffer; 1981 selection *sel = &buf->primary; 1982 int anchor, rectAnchor, anchorLineStart, newPos, row, column; 1983 1984 /* Find the new anchor point for the rest of this drag operation */ 1985 newPos = TextDXYToPosition(textD, e->x, e->y); 1986 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column); 1987 column = TextDOffsetWrappedColumn(textD, row, column); 1988 if (sel->selected) { 1989 if (sel->rectangular) { 1990 rectAnchor = column < (sel->rectEnd + sel->rectStart) / 2 ? 1991 sel->rectEnd : sel->rectStart; 1992 anchorLineStart = BufStartOfLine(buf, newPos < 1993 (sel->end + sel->start) / 2 ? sel->end : sel->start); 1994 anchor = BufCountForwardDispChars(buf, anchorLineStart, rectAnchor); 1995 } else { 1996 if (abs(newPos - sel->start) < abs(newPos - sel->end)) 1997 anchor = sel->end; 1998 else 1999 anchor = sel->start; 2000 anchorLineStart = BufStartOfLine(buf, anchor); 2001 rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor); 2002 } 2003 } else { 2004 anchor = TextDGetInsertPosition(textD); 2005 anchorLineStart = BufStartOfLine(buf, anchor); 2006 rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor); 2007 } 2008 ((TextWidget)w)->text.anchor = anchor; 2009 ((TextWidget)w)->text.rectAnchor = rectAnchor; 2010 2011 /* Make the new selection */ 2012 if (hasKey("rect", args, nArgs)) 2013 BufRectSelect(buf, BufStartOfLine(buf, min(anchor, newPos)), 2014 BufEndOfLine(buf, max(anchor, newPos)), 2015 min(rectAnchor, column), max(rectAnchor, column)); 2016 else 2017 BufSelect(buf, min(anchor, newPos), max(anchor, newPos)); 2018 2019 /* Never mind the motion threshold, go right to dragging since 2020 extend-start is unambiguously the start of a selection */ 2021 ((TextWidget)w)->text.dragState = PRIMARY_DRAG; 2022 2023 /* Don't do by-word or by-line adjustment, just by character */ 2024 ((TextWidget)w)->text.multiClickState = NO_CLICKS; 2025 2026 /* Move the cursor */ 2027 TextDSetInsertPosition(textD, newPos); 2028 callCursorMovementCBs(w, event); 2029 } 2030 2031 static void extendEndAP(Widget w, XEvent *event, String *args, 2032 Cardinal *nArgs) 2033 { 2034 XButtonEvent *e = &event->xbutton; 2035 TextWidget tw = (TextWidget)w; 2036 2037 if (tw->text.dragState == PRIMARY_CLICKED && 2038 tw->text.lastBtnDown <= e->time + XtGetMultiClickTime(XtDisplay(w))) 2039 tw->text.multiClickState++; 2040 endDrag(w); 2041 } 2042 2043 static void processCancelAP(Widget w, XEvent *event, String *args, 2044 Cardinal *nArgs) 2045 { 2046 int dragState = ((TextWidget)w)->text.dragState; 2047 textBuffer *buf = ((TextWidget)w)->text.textD->buffer; 2048 textDisp *textD = ((TextWidget)w)->text.textD; 2049 2050 /* If there's a calltip displayed, kill it. */ 2051 TextDKillCalltip(textD, 0); 2052 2053 if (dragState == PRIMARY_DRAG || dragState == PRIMARY_RECT_DRAG) 2054 BufUnselect(buf); 2055 cancelDrag(w); 2056 } 2057 2058 static void secondaryStartAP(Widget w, XEvent *event, String *args, 2059 Cardinal *nArgs) 2060 { 2061 XMotionEvent *e = &event->xmotion; 2062 textDisp *textD = ((TextWidget)w)->text.textD; 2063 textBuffer *buf = textD->buffer; 2064 selection *sel = &buf->secondary; 2065 int anchor, pos, row, column; 2066 2067 /* Find the new anchor point and make the new selection */ 2068 pos = TextDXYToPosition(textD, e->x, e->y); 2069 if (sel->selected) { 2070 if (abs(pos - sel->start) < abs(pos - sel->end)) 2071 anchor = sel->end; 2072 else 2073 anchor = sel->start; 2074 BufSecondarySelect(buf, anchor, pos); 2075 } else 2076 anchor = pos; 2077 2078 /* Record the site of the initial button press and the initial character 2079 position so subsequent motion events can decide when to begin a 2080 selection, (and where the selection began) */ 2081 ((TextWidget)w)->text.btnDownX = e->x; 2082 ((TextWidget)w)->text.btnDownY = e->y; 2083 ((TextWidget)w)->text.anchor = pos; 2084 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column); 2085 column = TextDOffsetWrappedColumn(textD, row, column); 2086 ((TextWidget)w)->text.rectAnchor = column; 2087 ((TextWidget)w)->text.dragState = SECONDARY_CLICKED; 2088 } 2089 2090 static void secondaryOrDragStartAP(Widget w, XEvent *event, String *args, 2091 Cardinal *nArgs) 2092 { 2093 XMotionEvent *e = &event->xmotion; 2094 textDisp *textD = ((TextWidget)w)->text.textD; 2095 textBuffer *buf = textD->buffer; 2096 2097 /* If the click was outside of the primary selection, this is not 2098 a drag, start a secondary selection */ 2099 if (!buf->primary.selected || !TextDInSelection(textD, e->x, e->y)) { 2100 secondaryStartAP(w, event, args, nArgs); 2101 return; 2102 } 2103 2104 if (checkReadOnly(w)) 2105 return; 2106 2107 /* Record the site of the initial button press and the initial character 2108 position so subsequent motion events can decide when to begin a 2109 drag, and where to drag to */ 2110 ((TextWidget)w)->text.btnDownX = e->x; 2111 ((TextWidget)w)->text.btnDownY = e->y; 2112 ((TextWidget)w)->text.dragState = CLICKED_IN_SELECTION; 2113 } 2114 2115 static void secondaryAdjustAP(Widget w, XEvent *event, String *args, 2116 Cardinal *nArgs) 2117 { 2118 TextWidget tw = (TextWidget)w; 2119 XMotionEvent *e = &event->xmotion; 2120 int dragState = tw->text.dragState; 2121 int rectDrag = hasKey("rect", args, nArgs); 2122 2123 /* Make sure the proper initialization was done on mouse down */ 2124 if (dragState != SECONDARY_DRAG && dragState != SECONDARY_RECT_DRAG && 2125 dragState != SECONDARY_CLICKED) 2126 return; 2127 2128 /* If the selection hasn't begun, decide whether the mouse has moved 2129 far enough from the initial mouse down to be considered a drag */ 2130 if (tw->text.dragState == SECONDARY_CLICKED) { 2131 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD || 2132 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD) 2133 tw->text.dragState = rectDrag ? SECONDARY_RECT_DRAG: SECONDARY_DRAG; 2134 else 2135 return; 2136 } 2137 2138 /* If "rect" argument has appeared or disappeared, keep dragState up 2139 to date about which type of drag this is */ 2140 tw->text.dragState = rectDrag ? SECONDARY_RECT_DRAG : SECONDARY_DRAG; 2141 2142 /* Record the new position for the autoscrolling timer routine, and 2143 engage or disengage the timer if the mouse is in/out of the window */ 2144 checkAutoScroll(tw, e->x, e->y); 2145 2146 /* Adjust the selection */ 2147 adjustSecondarySelection(tw, e->x, e->y); 2148 } 2149 2150 static void secondaryOrDragAdjustAP(Widget w, XEvent *event, String *args, 2151 Cardinal *nArgs) 2152 { 2153 TextWidget tw = (TextWidget)w; 2154 XMotionEvent *e = &event->xmotion; 2155 int dragState = tw->text.dragState; 2156 2157 /* Only dragging of blocks of text is handled in this action proc. 2158 Otherwise, defer to secondaryAdjust to handle the rest */ 2159 if (dragState != CLICKED_IN_SELECTION && dragState != PRIMARY_BLOCK_DRAG) { 2160 secondaryAdjustAP(w, event, args, nArgs); 2161 return; 2162 } 2163 2164 /* Decide whether the mouse has moved far enough from the 2165 initial mouse down to be considered a drag */ 2166 if (tw->text.dragState == CLICKED_IN_SELECTION) { 2167 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD || 2168 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD) 2169 BeginBlockDrag(tw); 2170 else 2171 return; 2172 } 2173 2174 /* Record the new position for the autoscrolling timer routine, and 2175 engage or disengage the timer if the mouse is in/out of the window */ 2176 checkAutoScroll(tw, e->x, e->y); 2177 2178 /* Adjust the selection */ 2179 BlockDragSelection(tw, e->x, e->y, hasKey("overlay", args, nArgs) ? 2180 (hasKey("copy", args, nArgs) ? DRAG_OVERLAY_COPY : DRAG_OVERLAY_MOVE) : 2181 (hasKey("copy", args, nArgs) ? DRAG_COPY : DRAG_MOVE)); 2182 } 2183 2184 static void copyToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) 2185 { 2186 XButtonEvent *e = &event->xbutton; 2187 TextWidget tw = (TextWidget)w; 2188 textDisp *textD = tw->text.textD; 2189 int dragState = tw->text.dragState; 2190 textBuffer *buf = textD->buffer; 2191 selection *secondary = &buf->secondary, *primary = &buf->primary; 2192 int rectangular = secondary->rectangular; 2193 char *textToCopy; 2194 int insertPos, lineStart, column; 2195 2196 endDrag(w); 2197 if (!((dragState == SECONDARY_DRAG && secondary->selected) || 2198 (dragState == SECONDARY_RECT_DRAG && secondary->selected) || 2199 dragState == SECONDARY_CLICKED || dragState == NOT_CLICKED)) 2200 return; 2201 if (!(secondary->selected && !((TextWidget)w)->text.motifDestOwner)) { 2202 if (checkReadOnly(w)) { 2203 BufSecondaryUnselect(buf); 2204 return; 2205 } 2206 } 2207 if (secondary->selected) { 2208 if (tw->text.motifDestOwner) { 2209 TextDBlankCursor(textD); 2210 textToCopy = BufGetSecSelectText(buf); 2211 if (primary->selected && rectangular) { 2212 insertPos = TextDGetInsertPosition(textD); 2213 BufReplaceSelected(buf, textToCopy); 2214 TextDSetInsertPosition(textD, buf->cursorPosHint); 2215 } else if (rectangular) { 2216 insertPos = TextDGetInsertPosition(textD); 2217 lineStart = BufStartOfLine(buf, insertPos); 2218 column = BufCountDispChars(buf, lineStart, insertPos); 2219 BufInsertCol(buf, column, lineStart, textToCopy, NULL, NULL); 2220 TextDSetInsertPosition(textD, buf->cursorPosHint); 2221 } else 2222 TextInsertAtCursor(w, textToCopy, event, True, 2223 tw->text.autoWrapPastedText); 2224 NEditFree(textToCopy); 2225 BufSecondaryUnselect(buf); 2226 TextDUnblankCursor(textD); 2227 } else 2228 SendSecondarySelection(w, e->time, False); 2229 } else if (primary->selected) { 2230 textToCopy = BufGetSelectionText(buf); 2231 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y)); 2232 TextInsertAtCursor(w, textToCopy, event, False, 2233 tw->text.autoWrapPastedText); 2234 NEditFree(textToCopy); 2235 } else { 2236 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y)); 2237 InsertPrimarySelection(w, e->time, False); 2238 } 2239 } 2240 2241 static void copyToOrEndDragAP(Widget w, XEvent *event, String *args, 2242 Cardinal *nArgs) 2243 { 2244 int dragState = ((TextWidget)w)->text.dragState; 2245 2246 if (dragState != PRIMARY_BLOCK_DRAG) { 2247 copyToAP(w, event, args, nArgs); 2248 return; 2249 } 2250 2251 FinishBlockDrag((TextWidget)w); 2252 } 2253 2254 static void moveToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) 2255 { 2256 XButtonEvent *e = &event->xbutton; 2257 textDisp *textD = ((TextWidget)w)->text.textD; 2258 int dragState = ((TextWidget)w)->text.dragState; 2259 textBuffer *buf = textD->buffer; 2260 selection *secondary = &buf->secondary, *primary = &buf->primary; 2261 int insertPos, rectangular = secondary->rectangular; 2262 int column, lineStart; 2263 char *textToCopy; 2264 2265 endDrag(w); 2266 if (!((dragState == SECONDARY_DRAG && secondary->selected) || 2267 (dragState == SECONDARY_RECT_DRAG && secondary->selected) || 2268 dragState == SECONDARY_CLICKED || dragState == NOT_CLICKED)) 2269 return; 2270 if (checkReadOnly(w)) { 2271 BufSecondaryUnselect(buf); 2272 return; 2273 } 2274 2275 if (secondary->selected) { 2276 if (((TextWidget)w)->text.motifDestOwner) { 2277 textToCopy = BufGetSecSelectText(buf); 2278 if (primary->selected && rectangular) { 2279 insertPos = TextDGetInsertPosition(textD); 2280 BufReplaceSelected(buf, textToCopy); 2281 TextDSetInsertPosition(textD, buf->cursorPosHint); 2282 } else if (rectangular) { 2283 insertPos = TextDGetInsertPosition(textD); 2284 lineStart = BufStartOfLine(buf, insertPos); 2285 column = BufCountDispChars(buf, lineStart, insertPos); 2286 BufInsertCol(buf, column, lineStart, textToCopy, NULL, NULL); 2287 TextDSetInsertPosition(textD, buf->cursorPosHint); 2288 } else 2289 TextInsertAtCursor(w, textToCopy, event, True, 2290 ((TextWidget)w)->text.autoWrapPastedText); 2291 NEditFree(textToCopy); 2292 BufRemoveSecSelect(buf); 2293 BufSecondaryUnselect(buf); 2294 } else 2295 SendSecondarySelection(w, e->time, True); 2296 } else if (primary->selected) { 2297 textToCopy = BufGetRange(buf, primary->start, primary->end); 2298 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y)); 2299 TextInsertAtCursor(w, textToCopy, event, False, 2300 ((TextWidget)w)->text.autoWrapPastedText); 2301 NEditFree(textToCopy); 2302 BufRemoveSelected(buf); 2303 BufUnselect(buf); 2304 } else { 2305 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y)); 2306 MovePrimarySelection(w, e->time, False); 2307 } 2308 } 2309 2310 static void moveToOrEndDragAP(Widget w, XEvent *event, String *args, 2311 Cardinal *nArgs) 2312 { 2313 int dragState = ((TextWidget)w)->text.dragState; 2314 2315 if (dragState != PRIMARY_BLOCK_DRAG) { 2316 moveToAP(w, event, args, nArgs); 2317 return; 2318 } 2319 2320 FinishBlockDrag((TextWidget)w); 2321 } 2322 2323 static void endDragAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) 2324 { 2325 if (((TextWidget)w)->text.dragState == PRIMARY_BLOCK_DRAG) 2326 FinishBlockDrag((TextWidget)w); 2327 else 2328 endDrag(w); 2329 } 2330 2331 static void exchangeAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) 2332 { 2333 XButtonEvent *e = &event->xbutton; 2334 textDisp *textD = ((TextWidget)w)->text.textD; 2335 textBuffer *buf = textD->buffer; 2336 selection *sec = &buf->secondary, *primary = &buf->primary; 2337 char *primaryText, *secText; 2338 int newPrimaryStart, newPrimaryEnd, secWasRect; 2339 int dragState = ((TextWidget)w)->text.dragState; /* save before endDrag */ 2340 int silent = hasKey("nobell", args, nArgs); 2341 2342 endDrag(w); 2343 if (checkReadOnly(w)) 2344 return; 2345 2346 /* If there's no secondary selection here, or the primary and secondary 2347 selection overlap, just beep and return */ 2348 if (!sec->selected || (primary->selected && 2349 ((primary->start <= sec->start && primary->end > sec->start) || 2350 (sec->start <= primary->start && sec->end > primary->start)))) 2351 { 2352 BufSecondaryUnselect(buf); 2353 ringIfNecessary(silent, w); 2354 /* If there's no secondary selection, but the primary selection is 2355 being dragged, we must not forget to finish the dragging. 2356 Otherwise, modifications aren't recorded. */ 2357 if (dragState == PRIMARY_BLOCK_DRAG) 2358 FinishBlockDrag((TextWidget)w); 2359 return; 2360 } 2361 2362 /* if the primary selection is in another widget, use selection routines */ 2363 if (!primary->selected) { 2364 ExchangeSelections(w, e->time); 2365 return; 2366 } 2367 2368 /* Both primary and secondary are in this widget, do the exchange here */ 2369 primaryText = BufGetSelectionText(buf); 2370 secText = BufGetSecSelectText(buf); 2371 secWasRect = sec->rectangular; 2372 BufReplaceSecSelect(buf, primaryText); 2373 newPrimaryStart = primary->start; 2374 BufReplaceSelected(buf, secText); 2375 newPrimaryEnd = newPrimaryStart + strlen(secText); 2376 NEditFree(primaryText); 2377 NEditFree(secText); 2378 BufSecondaryUnselect(buf); 2379 if (secWasRect) { 2380 TextDSetInsertPosition(textD, buf->cursorPosHint); 2381 } else { 2382 BufSelect(buf, newPrimaryStart, newPrimaryEnd); 2383 TextDSetInsertPosition(textD, newPrimaryEnd); 2384 } 2385 checkAutoShowInsertPos(w); 2386 } 2387 2388 static void copyPrimaryAP(Widget w, XEvent *event, String *args, 2389 Cardinal *nArgs) 2390 { 2391 XKeyEvent *e = &event->xkey; 2392 TextWidget tw = (TextWidget)w; 2393 textDisp *textD = tw->text.textD; 2394 textBuffer *buf = textD->buffer; 2395 selection *primary = &buf->primary; 2396 int rectangular = hasKey("rect", args, nArgs); 2397 char *textToCopy; 2398 int insertPos, col; 2399 2400 cancelDrag(w); 2401 if (checkReadOnly(w)) 2402 return; 2403 if (primary->selected && rectangular) { 2404 textToCopy = BufGetSelectionText(buf); 2405 insertPos = TextDGetInsertPosition(textD); 2406 col = BufCountDispChars(buf, BufStartOfLine(buf, insertPos), insertPos); 2407 BufInsertCol(buf, col, insertPos, textToCopy, NULL, NULL); 2408 TextDSetInsertPosition(textD, buf->cursorPosHint); 2409 NEditFree(textToCopy); 2410 checkAutoShowInsertPos(w); 2411 } else if (primary->selected) { 2412 textToCopy = BufGetSelectionText(buf); 2413 insertPos = TextDGetInsertPosition(textD); 2414 BufInsert(buf, insertPos, textToCopy); 2415 TextDSetInsertPosition(textD, insertPos + strlen(textToCopy)); 2416 NEditFree(textToCopy); 2417 checkAutoShowInsertPos(w); 2418 } else if (rectangular) { 2419 if (!TextDPositionToXY(textD, TextDGetInsertPosition(textD), 2420 &tw->text.btnDownX, &tw->text.btnDownY)) 2421 return; /* shouldn't happen */ 2422 InsertPrimarySelection(w, e->time, True); 2423 } else 2424 InsertPrimarySelection(w, e->time, False); 2425 } 2426 2427 static void cutPrimaryAP(Widget w, XEvent *event, String *args, 2428 Cardinal *nArgs) 2429 { 2430 XKeyEvent *e = &event->xkey; 2431 textDisp *textD = ((TextWidget)w)->text.textD; 2432 textBuffer *buf = textD->buffer; 2433 selection *primary = &buf->primary; 2434 char *textToCopy; 2435 int rectangular = hasKey("rect", args, nArgs); 2436 int insertPos, col; 2437 2438 cancelDrag(w); 2439 if (checkReadOnly(w)) 2440 return; 2441 if (primary->selected && rectangular) { 2442 textToCopy = BufGetSelectionText(buf); 2443 insertPos = TextDGetInsertPosition(textD); 2444 col = BufCountDispChars(buf, BufStartOfLine(buf, insertPos), insertPos); 2445 BufInsertCol(buf, col, insertPos, textToCopy, NULL, NULL); 2446 TextDSetInsertPosition(textD, buf->cursorPosHint); 2447 NEditFree(textToCopy); 2448 BufRemoveSelected(buf); 2449 checkAutoShowInsertPos(w); 2450 } else if (primary->selected) { 2451 textToCopy = BufGetSelectionText(buf); 2452 insertPos = TextDGetInsertPosition(textD); 2453 BufInsert(buf, insertPos, textToCopy); 2454 TextDSetInsertPosition(textD, insertPos + strlen(textToCopy)); 2455 NEditFree(textToCopy); 2456 BufRemoveSelected(buf); 2457 checkAutoShowInsertPos(w); 2458 } else if (rectangular) { 2459 if (!TextDPositionToXY(textD, TextDGetInsertPosition(textD), 2460 &((TextWidget)w)->text.btnDownX, 2461 &((TextWidget)w)->text.btnDownY)) 2462 return; /* shouldn't happen */ 2463 MovePrimarySelection(w, e->time, True); 2464 } else { 2465 MovePrimarySelection(w, e->time, False); 2466 } 2467 } 2468 2469 static void mousePanAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) 2470 { 2471 XButtonEvent *e = &event->xbutton; 2472 TextWidget tw = (TextWidget)w; 2473 textDisp *textD = tw->text.textD; 2474 int lineHeight = textD->ascent + textD->descent; 2475 int topLineNum, horizOffset; 2476 static Cursor panCursor = 0; 2477 2478 if (tw->text.dragState == MOUSE_PAN) { 2479 TextDSetScroll(textD, 2480 (tw->text.btnDownY - e->y + lineHeight/2) / lineHeight, 2481 tw->text.btnDownX - e->x); 2482 } else if (tw->text.dragState == NOT_CLICKED) { 2483 TextDGetScroll(textD, &topLineNum, &horizOffset); 2484 tw->text.btnDownX = e->x + horizOffset; 2485 tw->text.btnDownY = e->y + topLineNum * lineHeight; 2486 tw->text.dragState = MOUSE_PAN; 2487 if (!panCursor) 2488 panCursor = XCreateFontCursor(XtDisplay(w), XC_fleur); 2489 XGrabPointer(XtDisplay(w), XtWindow(w), False, 2490 ButtonMotionMask | ButtonReleaseMask, GrabModeAsync, 2491 GrabModeAsync, None, panCursor, CurrentTime); 2492 } else 2493 cancelDrag(w); 2494 } 2495 2496 static void pasteClipboardAP(Widget w, XEvent *event, String *args, 2497 Cardinal *nArgs) 2498 { 2499 if(mod_ctrl_last_event == mod_ctrl_mc_last_event) { 2500 textDisp *textD = ((TextWidget)w)->text.textD; 2501 TextDSetInsertPosition(textD, textD->newcursor->cursorPos); 2502 } 2503 2504 if (hasKey("rect", args, nArgs)) 2505 TextColPasteClipboard(w, event->xkey.time); 2506 else 2507 TextPasteClipboard(w, event->xkey.time); 2508 } 2509 2510 static void copyClipboardAP(Widget w, XEvent *event, String *args, 2511 Cardinal *nArgs) 2512 { 2513 TextCopyClipboard(w, event->xkey.time); 2514 } 2515 2516 static void cutClipboardAP(Widget w, XEvent *event, String *args, 2517 Cardinal *nArgs) 2518 { 2519 TextCutClipboard(w, event->xkey.time); 2520 } 2521 2522 static void insertStringAP(Widget w, XEvent *event, String *args, 2523 Cardinal *nArgs) 2524 { 2525 smartIndentCBStruct smartIndent; 2526 textDisp *textD = ((TextWidget)w)->text.textD; 2527 2528 if (*nArgs == 0) 2529 return; 2530 cancelDrag(w); 2531 if (checkReadOnly(w)) 2532 return; 2533 if (((TextWidget)w)->text.smartIndent) { 2534 smartIndent.reason = CHAR_TYPED; 2535 smartIndent.pos = TextDGetInsertPosition(textD); 2536 smartIndent.indentRequest = 0; 2537 smartIndent.charsTyped = args[0]; 2538 XtCallCallbacks(w, textNsmartIndentCallback, (XtPointer)&smartIndent); 2539 } 2540 TextInsertAtCursor(w, args[0], event, True, True); 2541 BufUnselect((((TextWidget)w)->text.textD)->buffer); 2542 } 2543 2544 static void selfInsertAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) 2545 { 2546 WindowInfo* window = WidgetToWindow(w); 2547 2548 #ifdef NO_XMIM 2549 static XComposeStatus compose = {NULL, 0}; 2550 #else 2551 int status; 2552 #endif 2553 XKeyEvent *e = &event->xkey; 2554 char chars[512]; 2555 KeySym keysym; 2556 int nChars; 2557 smartIndentCBStruct smartIndent; 2558 textDisp *textD = ((TextWidget)w)->text.textD; 2559 2560 nChars = TextLookupString(w, e, chars, 511, &keysym, &status); 2561 if (nChars == 0 || status == XLookupNone || 2562 status == XLookupKeySym || status == XBufferOverflow) 2563 { 2564 if(keysym == XK_Control_L || keysym == XK_Control_R) { 2565 mod_ctrl_last_event = e->time; 2566 } 2567 return; 2568 } 2569 2570 cancelDrag(w); 2571 if (checkReadOnly(w)) 2572 return; 2573 TakeMotifDestination(w, e->time); 2574 chars[nChars] = '\0'; 2575 2576 if (!BufSubstituteNullChars(chars, nChars, window->buffer)) { 2577 DialogF(DF_ERR, window->shell, 1, "Error", "Too much binary data", "OK"); 2578 return; 2579 } 2580 2581 /* If smart indent is on, call the smart indent callback to check the 2582 inserted character */ 2583 if (((TextWidget)w)->text.smartIndent) { 2584 smartIndent.reason = CHAR_TYPED; 2585 smartIndent.pos = TextDGetInsertPosition(textD); 2586 smartIndent.indentRequest = 0; 2587 smartIndent.charsTyped = chars; 2588 XtCallCallbacks(w, textNsmartIndentCallback, (XtPointer)&smartIndent); 2589 } 2590 TextInsertAtCursor(w, chars, event, True, True); 2591 BufUnselect(textD->buffer); 2592 } 2593 2594 static void newlineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) 2595 { 2596 if (((TextWidget)w)->text.autoIndent || ((TextWidget)w)->text.smartIndent) 2597 newlineAndIndentAP(w, event, args, nArgs); 2598 else 2599 newlineNoIndentAP(w, event, args, nArgs); 2600 } 2601 2602 static void newlineNoIndentAP(Widget w, XEvent *event, String *args, 2603 Cardinal *nArgs) 2604 { 2605 XKeyEvent *e = &event->xkey; 2606 2607 cancelDrag(w); 2608 if (checkReadOnly(w)) 2609 return; 2610 TakeMotifDestination(w, e->time); 2611 simpleInsertAtCursor(w, "\n", event, True); 2612 BufUnselect((((TextWidget)w)->text.textD)->buffer); 2613 } 2614 2615 static void newlineAndIndentAP(Widget w, XEvent *event, String *args, 2616 Cardinal *nArgs) 2617 { 2618 XKeyEvent *e = &event->xkey; 2619 TextWidget tw = (TextWidget)w; 2620 textDisp *textD = tw->text.textD; 2621 textBuffer *buf = textD->buffer; 2622 char *indentStr; 2623 int cursorPos, lineStartPos, column; 2624 2625 if (checkReadOnly(w)) 2626 return; 2627 cancelDrag(w); 2628 TakeMotifDestination(w, e->time); 2629 2630 /* Create a string containing a newline followed by auto or smart 2631 indent string */ 2632 cursorPos = TextDGetInsertPosition(textD); 2633 lineStartPos = BufStartOfLine(buf, cursorPos); 2634 indentStr = createIndentString(tw, buf, 0, lineStartPos, 2635 cursorPos, NULL, &column); 2636 2637 /* Insert it at the cursor */ 2638 simpleInsertAtCursor(w, indentStr, event, True); 2639 NEditFree(indentStr); 2640 2641 if (tw->text.emulateTabs > 0) { 2642 /* If emulated tabs are on, make the inserted indent deletable by 2643 tab. Round this up by faking the column a bit to the right to 2644 let the user delete half-tabs with one keypress. */ 2645 column += tw->text.emulateTabs - 1; 2646 tw->text.emTabsBeforeCursor = column / tw->text.emulateTabs; 2647 } 2648 2649 BufUnselect(buf); 2650 } 2651 2652 static void processTabAP(Widget w, XEvent *event, String *args, 2653 Cardinal *nArgs) 2654 { 2655 textDisp *textD = ((TextWidget)w)->text.textD; 2656 textBuffer *buf = textD->buffer; 2657 selection *sel = &buf->primary; 2658 int emTabDist = ((TextWidget)w)->text.emulateTabs; 2659 int emTabsBeforeCursor = ((TextWidget)w)->text.emTabsBeforeCursor; 2660 int insertPos, indent, startIndent, toIndent, lineStart, tabWidth; 2661 char *outStr, *outPtr; 2662 2663 if (checkReadOnly(w)) 2664 return; 2665 cancelDrag(w); 2666 TakeMotifDestination(w, event->xkey.time); 2667 2668 /* If emulated tabs are off, just insert a tab */ 2669 if (emTabDist <= 0) { 2670 TextInsertAtCursor(w, "\t", event, True, True); 2671 return; 2672 } 2673 2674 /* Find the starting and ending indentation. If the tab is to 2675 replace an existing selection, use the start of the selection 2676 instead of the cursor position as the indent. When replacing 2677 rectangular selections, tabs are automatically recalculated as 2678 if the inserted text began at the start of the line */ 2679 insertPos = pendingSelection(w) ? 2680 sel->start : TextDGetInsertPosition(textD); 2681 lineStart = BufStartOfLine(buf, insertPos); 2682 if (pendingSelection(w) && sel->rectangular) 2683 insertPos = BufCountForwardDispChars(buf, lineStart, sel->rectStart); 2684 startIndent = BufCountDispChars(buf, lineStart, insertPos); 2685 toIndent = startIndent + emTabDist - (startIndent % emTabDist); 2686 if (pendingSelection(w) && sel->rectangular) { 2687 toIndent -= startIndent; 2688 startIndent = 0; 2689 } 2690 2691 /* Allocate a buffer assuming all the inserted characters will be spaces */ 2692 outStr = (char*)NEditMalloc(toIndent - startIndent + 1); 2693 2694 /* Add spaces and tabs to outStr until it reaches toIndent */ 2695 outPtr = outStr; 2696 indent = startIndent; 2697 while (indent < toIndent) { 2698 tabWidth = BufCharWidth('\t', indent, buf->tabDist, buf->nullSubsChar); 2699 if (buf->useTabs && tabWidth > 1 && indent + tabWidth <= toIndent) { 2700 *outPtr++ = '\t'; 2701 indent += tabWidth; 2702 } else { 2703 *outPtr++ = ' '; 2704 indent++; 2705 } 2706 } 2707 *outPtr = '\0'; 2708 2709 /* Insert the emulated tab */ 2710 TextInsertAtCursor(w, outStr, event, True, True); 2711 NEditFree(outStr); 2712 2713 /* Restore and ++ emTabsBeforeCursor cleared by TextInsertAtCursor */ 2714 ((TextWidget)w)->text.emTabsBeforeCursor = emTabsBeforeCursor + 1; 2715 2716 BufUnselect(buf); 2717 } 2718 2719 static void deleteSelectionAP(Widget w, XEvent *event, String *args, 2720 Cardinal *nArgs) 2721 { 2722 XKeyEvent *e = &event->xkey; 2723 2724 cancelDrag(w); 2725 if (checkReadOnly(w)) 2726 return; 2727 TakeMotifDestination(w, e->time); 2728 deletePendingSelection(w, event); 2729 } 2730 2731 static int deletePreviousCharacter(Widget w, XEvent *event, textDisp *textD, int silent, int insertPos) { 2732 char c; 2733 2734 if (insertPos == 0) { 2735 ringIfNecessary(silent, w); 2736 return 0; 2737 } 2738 2739 if (deleteEmulatedTab(w, event)) 2740 return 0; 2741 2742 int prevPos = BufLeftPos(textD->buffer, insertPos); 2743 if (((TextWidget)w)->text.overstrike) { 2744 c = BufGetCharacter(textD->buffer, prevPos); 2745 if (c == '\n') 2746 BufRemove(textD->buffer, prevPos, insertPos); 2747 else if (c != '\t') 2748 BufReplace(textD->buffer, prevPos, insertPos, " "); 2749 } else { 2750 BufRemove(textD->buffer, prevPos, insertPos); 2751 } 2752 2753 return prevPos - insertPos; // diff 2754 } 2755 2756 static void deletePreviousCharacterAP(Widget w, XEvent *event, String *args, 2757 Cardinal *nArgs) 2758 { 2759 XKeyEvent *e = &event->xkey; 2760 textDisp *textD = ((TextWidget)w)->text.textD; 2761 int silent = hasKey("nobell", args, nArgs); 2762 2763 cancelDrag(w); 2764 if (checkReadOnly(w)) 2765 return; 2766 2767 TakeMotifDestination(w, e->time); 2768 if (deletePendingSelection(w, event)) 2769 return; 2770 2771 size_t mcursorSize = textD->mcursorSize; 2772 Bool batch = False; 2773 if(mcursorSize > 1) { 2774 BufBeginModifyBatch(textD->buffer); 2775 batch = True; 2776 } 2777 2778 int diff = 0; 2779 int prevPos = -1; 2780 for(int i=0;i<mcursorSize;i++) { 2781 //textD->multicursor[i].cursorPos += diff; 2782 textD->cursor = textD->multicursor + i; 2783 diff += deletePreviousCharacter(w, event, textD, silent, textD->cursor->cursorPos); 2784 if(textD->cursor->cursorPos == prevPos) { 2785 TextDRemoveCursor(textD, i); 2786 mcursorSize--; 2787 i--; 2788 } else { 2789 prevPos = textD->cursor->cursorPos; 2790 } 2791 } 2792 2793 2794 checkAutoShowInsertPos(w); 2795 if(batch) { 2796 BufEndModifyBatch(textD->buffer); 2797 } 2798 } 2799 2800 static int deleteNextCharacter(Widget w, textDisp *textD, int silent, int insertPos) { 2801 if (insertPos == textD->buffer->length) { 2802 ringIfNecessary(silent, w); 2803 return 0; 2804 } 2805 int nextPos = BufRightPos(textD->buffer, insertPos); 2806 BufRemove(textD->buffer, insertPos , nextPos); 2807 return insertPos - nextPos; 2808 } 2809 2810 static void deleteNextCharacterAP(Widget w, XEvent *event, String *args, 2811 Cardinal *nArgs) 2812 { 2813 XKeyEvent *e = &event->xkey; 2814 textDisp *textD = ((TextWidget)w)->text.textD; 2815 int silent = hasKey("nobell", args, nArgs); 2816 2817 cancelDrag(w); 2818 if (checkReadOnly(w)) 2819 return; 2820 TakeMotifDestination(w, e->time); 2821 if (deletePendingSelection(w, event)) 2822 return; 2823 2824 int diff = 0; 2825 int prevPos = -1; 2826 size_t mcursorSize = textD->mcursorSize; 2827 Bool batch = False; 2828 if(mcursorSize > 1) { 2829 BufBeginModifyBatch(textD->buffer); 2830 batch = True; 2831 } 2832 2833 for(int i=0;i<mcursorSize;i++) { 2834 textD->multicursor[i].cursorPos += diff; 2835 textD->cursor = textD->multicursor + i; 2836 diff += deleteNextCharacter(w, textD, silent, textD->cursor->cursorPos); 2837 if(textD->cursor->cursorPos == prevPos) { 2838 TextDRemoveCursor(textD, i); 2839 mcursorSize--; 2840 i--; 2841 } else { 2842 prevPos = textD->cursor->cursorPos; 2843 } 2844 callCursorMovementCBs(w, event); 2845 } 2846 2847 checkAutoShowInsertPos(w); 2848 2849 if(batch) { 2850 BufEndModifyBatch(textD->buffer); 2851 } 2852 } 2853 2854 static void deletePreviousWordAP(Widget w, XEvent *event, String *args, 2855 Cardinal *nArgs) 2856 { 2857 XKeyEvent *e = &event->xkey; 2858 textDisp *textD = ((TextWidget)w)->text.textD; 2859 textBuffer *buffer = textD->buffer; 2860 int insertPos ; 2861 int pos, lineStart; 2862 char *delimiters = ((TextWidget)w)->text.delimiters; 2863 int silent = hasKey("nobell", args, nArgs); 2864 2865 cancelDrag(w); 2866 if (checkReadOnly(w)) { 2867 return; 2868 } 2869 2870 TakeMotifDestination(w, e->time); 2871 if (deletePendingSelection(w, event)) { 2872 return; 2873 } 2874 2875 size_t mcursorSize = textD->mcursorSize; 2876 Bool batch = False; 2877 if(mcursorSize > 1) { 2878 BufBeginModifyBatch(textD->buffer); 2879 batch = True; 2880 } 2881 2882 int diff = 0; 2883 int notMoved = 0; 2884 for(int i=0;i<mcursorSize;i++) { 2885 textD->cursor = textD->multicursor + i; 2886 2887 insertPos = textD->cursor->cursorPos; 2888 lineStart = BufStartOfLine(buffer, insertPos); 2889 2890 if (insertPos == lineStart) { 2891 notMoved = 1; 2892 continue; 2893 } 2894 2895 pos = max(insertPos - 1, 0); 2896 while(strchr(delimiters, BufGetCharacter(buffer, pos)) != NULL && 2897 pos != lineStart) 2898 { 2899 pos--; 2900 } 2901 2902 pos = startOfWord((TextWidget)w, pos); 2903 BufRemove(buffer, pos, insertPos); 2904 diff -= insertPos - pos; 2905 2906 if(i > 0 && textD->multicursor[i].cursorPos == textD->multicursor[i-1].cursorPos) { 2907 TextDRemoveCursor(textD, i); 2908 mcursorSize--; 2909 i--; 2910 } 2911 2912 callCursorMovementCBs(w, event); 2913 } 2914 2915 checkAutoShowInsertPos(w); 2916 if(batch) { 2917 BufEndModifyBatch(textD->buffer); 2918 } 2919 2920 if(notMoved) { 2921 ringIfNecessary(silent, w); 2922 } 2923 } 2924 2925 static void deleteNextWordAP(Widget w, XEvent *event, String *args, 2926 Cardinal *nArgs) 2927 { 2928 XKeyEvent *e = &event->xkey; 2929 textDisp *textD = ((TextWidget)w)->text.textD; 2930 textBuffer *buffer = textD->buffer; 2931 int insertPos; 2932 int pos, lineEnd; 2933 char *delimiters = ((TextWidget)w)->text.delimiters; 2934 int silent = hasKey("nobell", args, nArgs); 2935 2936 cancelDrag(w); 2937 if (checkReadOnly(w)) { 2938 return; 2939 } 2940 2941 TakeMotifDestination(w, e->time); 2942 if (deletePendingSelection(w, event)) { 2943 return; 2944 } 2945 2946 int diff = 0; 2947 int notMoved = 0; 2948 size_t mcursorSize = textD->mcursorSize; 2949 Bool batch = False; 2950 if(mcursorSize > 1) { 2951 BufBeginModifyBatch(textD->buffer); 2952 batch = True; 2953 } 2954 2955 for(int i=0;i<mcursorSize;i++) { 2956 textD->multicursor[i].cursorPos += diff; 2957 textD->cursor = textD->multicursor + i; 2958 2959 insertPos = TextDGetInsertPosition(textD); 2960 lineEnd = BufEndOfLine(buffer, insertPos); 2961 2962 if (insertPos == lineEnd) { 2963 notMoved = 1; 2964 continue; 2965 } 2966 2967 pos = insertPos; 2968 while(strchr(delimiters, BufGetCharacter(buffer, pos)) != NULL && 2969 pos != lineEnd) 2970 { 2971 pos++; 2972 } 2973 2974 pos = endOfWord((TextWidget)w, pos); 2975 BufRemove(textD->buffer, insertPos, pos); 2976 diff += insertPos - pos; 2977 2978 if(i > 0 && textD->multicursor[i].cursorPos == textD->multicursor[i-1].cursorPos) { 2979 TextDRemoveCursor(textD, i); 2980 mcursorSize--; 2981 i--; 2982 } 2983 2984 callCursorMovementCBs(w, event); 2985 } 2986 2987 checkAutoShowInsertPos(w); 2988 if(batch) { 2989 BufEndModifyBatch(textD->buffer); 2990 } 2991 2992 if(notMoved) { 2993 ringIfNecessary(silent, w); 2994 } 2995 } 2996 2997 static void deleteToEndOfLineAP(Widget w, XEvent *event, String *args, 2998 Cardinal *nArgs) 2999 { 3000 XKeyEvent *e = &event->xkey; 3001 textDisp *textD = ((TextWidget)w)->text.textD; 3002 int insertPos = TextDGetInsertPosition(textD); 3003 int endOfLine; 3004 int silent = 0; 3005 3006 silent = hasKey("nobell", args, nArgs); 3007 if (hasKey("absolute", args, nArgs)) 3008 endOfLine = BufEndOfLine(textD->buffer, insertPos); 3009 else 3010 endOfLine = TextDEndOfLine(textD, insertPos, False); 3011 cancelDrag(w); 3012 if (checkReadOnly(w)) 3013 return; 3014 TakeMotifDestination(w, e->time); 3015 if (deletePendingSelection(w, event)) 3016 return; 3017 if (insertPos == endOfLine) { 3018 ringIfNecessary(silent, w); 3019 return; 3020 } 3021 BufRemove(textD->buffer, insertPos, endOfLine); 3022 checkAutoShowInsertPos(w); 3023 callCursorMovementCBs(w, event); 3024 } 3025 3026 static void deleteToStartOfLineAP(Widget w, XEvent *event, String *args, 3027 Cardinal *nArgs) 3028 { 3029 XKeyEvent *e = &event->xkey; 3030 textDisp *textD = ((TextWidget)w)->text.textD; 3031 int insertPos = TextDGetInsertPosition(textD); 3032 int startOfLine; 3033 int silent = 0; 3034 3035 silent = hasKey("nobell", args, nArgs); 3036 if (hasKey("wrap", args, nArgs)) 3037 startOfLine = TextDStartOfLine(textD, insertPos); 3038 else 3039 startOfLine = BufStartOfLine(textD->buffer, insertPos); 3040 cancelDrag(w); 3041 if (checkReadOnly(w)) 3042 return; 3043 TakeMotifDestination(w, e->time); 3044 if (deletePendingSelection(w, event)) 3045 return; 3046 if (insertPos == startOfLine) { 3047 ringIfNecessary(silent, w); 3048 return; 3049 } 3050 BufRemove(textD->buffer, startOfLine, insertPos); 3051 checkAutoShowInsertPos(w); 3052 callCursorMovementCBs(w, event); 3053 } 3054 3055 static void forwardCharacterAP(Widget w, XEvent *event, String *args, 3056 Cardinal *nArgs) 3057 { 3058 int silent = hasKey("nobell", args, nArgs); 3059 3060 cancelDrag(w); 3061 3062 textDisp *textD = ((TextWidget)w)->text.textD; 3063 size_t mcursorSize = textD->mcursorSize; 3064 // we have to fake single cursor mode for correct cursor rendering here 3065 textD->mcursorSize = 1; 3066 int notMoved = 0; 3067 for(int i=mcursorSize-1;i>=0;i--) { 3068 textD->cursor = textD->multicursor + i; 3069 int insertPos = textD->cursor->cursorPos; 3070 3071 int prevNotMoved = notMoved; 3072 if (!TextDMoveRight(((TextWidget)w)->text.textD)) { 3073 notMoved = 1; 3074 } 3075 3076 if(prevNotMoved) { 3077 // if a cursor was not moved, we have to make sure, that another 3078 // cursor doesn't have the same position 3079 if(textD->multicursor[i].cursorPos == textD->multicursor[i+1].cursorPos) { 3080 // because we simulate single-cursor mode, we have to set 3081 // mcursorSize to the correct value here 3082 textD->mcursorSize = mcursorSize; 3083 TextDRemoveCursor(textD, i); 3084 textD->mcursorSize = 1; // reactivate single-cursor simulation 3085 mcursorSize--; 3086 } 3087 } 3088 3089 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3090 //callCursorMovementCBs(w, event); 3091 } 3092 textD->mcursorSize = mcursorSize; 3093 // better to call this outside of the mcursor loop 3094 callCursorMovementCBs(w, event); 3095 3096 checkAutoShowInsertPos(w); 3097 if(notMoved) { 3098 ringIfNecessary(silent, w); 3099 } 3100 } 3101 3102 static void backwardCharacterAP(Widget w, XEvent *event, String *args, 3103 Cardinal *nArgs) 3104 { 3105 int silent = hasKey("nobell", args, nArgs); 3106 3107 cancelDrag(w); 3108 3109 textDisp *textD = ((TextWidget)w)->text.textD; 3110 size_t mcursorSize = textD->mcursorSize; 3111 textD->mcursorSize = 1; // emulate single-cursor mode 3112 int notMoved = 0; 3113 for(int i=0;i<mcursorSize;i++) { 3114 textD->cursor = textD->multicursor + i; 3115 int insertPos = textD->cursor->cursorPos; 3116 3117 int prevNotMoved = notMoved; 3118 if (!TextDMoveLeft(((TextWidget)w)->text.textD)) { 3119 notMoved = 1; 3120 } 3121 3122 if(prevNotMoved) { 3123 if(textD->multicursor[i].cursorPos == textD->multicursor[i-1].cursorPos) { 3124 textD->mcursorSize = mcursorSize; 3125 TextDRemoveCursor(textD, i); 3126 textD->mcursorSize = 1; // reactivate single-cursor simulation 3127 mcursorSize--; 3128 i--; 3129 } 3130 } 3131 3132 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3133 } 3134 textD->mcursorSize = mcursorSize; 3135 callCursorMovementCBs(w, event); 3136 3137 checkAutoShowInsertPos(w); 3138 if(notMoved) { 3139 ringIfNecessary(silent, w); 3140 } 3141 } 3142 3143 static void forwardWordAP(Widget w, XEvent *event, String *args, 3144 Cardinal *nArgs) 3145 { 3146 textDisp *textD = ((TextWidget)w)->text.textD; 3147 textBuffer *buf = textD->buffer; 3148 int pos, insertPos; // = TextDGetInsertPosition(textD); 3149 char *delimiters = ((TextWidget)w)->text.delimiters; 3150 int silent = hasKey("nobell", args, nArgs); 3151 int tail = hasKey("tail", args, nArgs); 3152 3153 cancelDrag(w); 3154 3155 size_t mcursorSize = textD->mcursorSize; 3156 // emulate single-cursor mode 3157 textD->mcursorSize = 1; 3158 int notMoved = 0; 3159 for(int i=mcursorSize-1;i>=0;i--) { 3160 textD->cursor = textD->multicursor + i; 3161 insertPos = textD->cursor->cursorPos; 3162 3163 if (insertPos == buf->length) { 3164 notMoved = 1; 3165 continue; 3166 } 3167 pos = insertPos; 3168 3169 if(tail) { 3170 for (; pos<buf->length; pos++) { 3171 if (NULL == strchr(delimiters, BufGetCharacter(buf, pos))) { 3172 break; 3173 } 3174 } 3175 if (NULL == strchr(delimiters, BufGetCharacter(buf, pos))) { 3176 pos = endOfWord((TextWidget)w, pos); 3177 } 3178 } else { 3179 if (NULL == strchr(delimiters, BufGetCharacter(buf, pos))) { 3180 pos = endOfWord((TextWidget)w, pos); 3181 } 3182 for (; pos<buf->length; pos++) { 3183 if (NULL == strchr(delimiters, BufGetCharacter(buf, pos))) { 3184 break; 3185 } 3186 } 3187 } 3188 3189 TextDSetInsertPosition(textD, pos); 3190 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3191 3192 if(notMoved) { 3193 // if a cursor was not moved, we have to make sure, that another 3194 // cursor doesn't have the same position 3195 if(pos == textD->multicursor[i+1].cursorPos) { 3196 // because we simulate single-cursor mode, we have to set 3197 // mcursorSize to the correct value here 3198 textD->mcursorSize = mcursorSize; 3199 TextDRemoveCursor(textD, i); 3200 textD->mcursorSize = 1; // reactivate single-cursor simulation 3201 mcursorSize--; 3202 } 3203 } 3204 } 3205 textD->mcursorSize = mcursorSize; 3206 callCursorMovementCBs(w, event); 3207 3208 checkAutoShowInsertPos(w); 3209 3210 if(notMoved) { 3211 ringIfNecessary(silent, w); 3212 } 3213 } 3214 3215 static void backwardWordAP(Widget w, XEvent *event, String *args, 3216 Cardinal *nArgs) 3217 { 3218 textDisp *textD = ((TextWidget)w)->text.textD; 3219 textBuffer *buf = textD->buffer; 3220 int pos; //TextDGetInsertPosition(textD); 3221 char *delimiters = ((TextWidget)w)->text.delimiters; 3222 int silent = hasKey("nobell", args, nArgs); 3223 3224 cancelDrag(w); 3225 3226 size_t mcursorSize = textD->mcursorSize; 3227 // emulate single-cursor mode 3228 textD->mcursorSize = 1; 3229 int notMoved = 0; 3230 3231 for(int i=0;i<mcursorSize;i++) { 3232 textD->cursor = textD->multicursor + i; 3233 int insertPos = textD->cursor->cursorPos; 3234 3235 if (insertPos == 0) { 3236 notMoved = 1; 3237 continue; 3238 } 3239 3240 pos = max(insertPos - 1, 0); 3241 while (strchr(delimiters, BufGetCharacter(buf, pos)) != NULL && pos > 0) 3242 pos--; 3243 pos = startOfWord((TextWidget)w, pos); 3244 3245 TextDSetInsertPosition(textD, pos); 3246 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3247 3248 //callCursorMovementCBs(w, event); 3249 3250 if(notMoved) { 3251 // check for cursor dup 3252 if(textD->multicursor[i].cursorPos == textD->multicursor[i-1].cursorPos) { 3253 // because we simulate single-cursor mode, we have to set 3254 // mcursorSize to the correct value here 3255 textD->mcursorSize = mcursorSize; 3256 TextDRemoveCursor(textD, i); 3257 textD->mcursorSize = 1; // reactivate single-cursor simulation 3258 mcursorSize--; 3259 i--; 3260 } 3261 } 3262 } 3263 textD->mcursorSize = mcursorSize; 3264 callCursorMovementCBs(w, event); 3265 3266 checkAutoShowInsertPos(w); 3267 if(notMoved) { 3268 ringIfNecessary(silent, w); 3269 } 3270 } 3271 3272 static void forwardParagraphAP(Widget w, XEvent *event, String *args, 3273 Cardinal *nArgs) 3274 { 3275 textDisp *textD = ((TextWidget)w)->text.textD; 3276 int pos; 3277 textBuffer *buf = textD->buffer; 3278 char c; 3279 static char whiteChars[] = " \t"; 3280 int silent = hasKey("nobell", args, nArgs); 3281 3282 cancelDrag(w); 3283 3284 size_t mcursorSize = textD->mcursorSize; 3285 // we have to fake single cursor mode for correct cursor rendering here 3286 textD->mcursorSize = 1; 3287 int notMoved = 0; 3288 for(int i=mcursorSize-1;i>=0;i--) { 3289 textD->cursor = textD->multicursor + i; 3290 int insertPos = textD->cursor->cursorPos; 3291 3292 if (insertPos == buf->length) { 3293 notMoved = 1; 3294 continue; 3295 } 3296 3297 pos = min(BufEndOfLine(buf, insertPos)+1, buf->length); 3298 while (pos < buf->length) { 3299 c = BufGetCharacter(buf, pos); 3300 if (c == '\n') 3301 break; 3302 if (strchr(whiteChars, c) != NULL) 3303 pos++; 3304 else 3305 pos = min(BufEndOfLine(buf, pos)+1, buf->length); 3306 } 3307 TextDSetInsertPosition(textD, min(pos+1, buf->length)); 3308 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3309 //callCursorMovementCBs(w, event); 3310 3311 if(notMoved) { 3312 // if a cursor was not moved, we have to make sure, that another 3313 // cursor doesn't have the same position 3314 if(pos == textD->multicursor[i+1].cursorPos) { 3315 // because we simulate single-cursor mode, we have to set 3316 // mcursorSize to the correct value here 3317 textD->mcursorSize = mcursorSize; 3318 TextDRemoveCursor(textD, i); 3319 textD->mcursorSize = 1; // reactivate single-cursor simulation 3320 mcursorSize--; 3321 } 3322 } 3323 } 3324 textD->mcursorSize = mcursorSize; 3325 callCursorMovementCBs(w, event); 3326 3327 checkAutoShowInsertPos(w); 3328 if(notMoved) { 3329 ringIfNecessary(silent, w); 3330 } 3331 } 3332 3333 static void backwardParagraphAP(Widget w, XEvent *event, String *args, 3334 Cardinal *nArgs) 3335 { 3336 textDisp *textD = ((TextWidget)w)->text.textD; 3337 int parStart, pos; 3338 textBuffer *buf = textD->buffer; 3339 char c; 3340 static char whiteChars[] = " \t"; 3341 int silent = hasKey("nobell", args, nArgs); 3342 3343 cancelDrag(w); 3344 3345 size_t mcursorSize = textD->mcursorSize; 3346 // emulate single-cursor mode 3347 textD->mcursorSize = 1; 3348 int notMoved = 0; 3349 for(int i=0;i<mcursorSize;i++) { 3350 textD->cursor = textD->multicursor + i; 3351 int insertPos = textD->cursor->cursorPos; 3352 3353 if (insertPos == 0) { 3354 notMoved = 1; 3355 continue; 3356 } 3357 parStart = BufStartOfLine(buf, max(insertPos-1, 0)); 3358 pos = max(parStart - 2, 0); 3359 while (pos > 0) { 3360 c = BufGetCharacter(buf, pos); 3361 if (c == '\n') 3362 break; 3363 if (strchr(whiteChars, c) != NULL) 3364 pos--; 3365 else { 3366 parStart = BufStartOfLine(buf, pos); 3367 pos = max(parStart - 2, 0); 3368 } 3369 } 3370 TextDSetInsertPosition(textD, parStart); 3371 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3372 //callCursorMovementCBs(w, event); 3373 3374 if(notMoved) { 3375 // check for cursor dup 3376 if(textD->multicursor[i].cursorPos == textD->multicursor[i-1].cursorPos) { 3377 // because we simulate single-cursor mode, we have to set 3378 // mcursorSize to the correct value here 3379 textD->mcursorSize = mcursorSize; 3380 TextDRemoveCursor(textD, i); 3381 textD->mcursorSize = 1; // reactivate single-cursor simulation 3382 mcursorSize--; 3383 i--; 3384 } 3385 } 3386 } 3387 textD->mcursorSize = mcursorSize; 3388 callCursorMovementCBs(w, event); 3389 3390 checkAutoShowInsertPos(w); 3391 if(notMoved) { 3392 ringIfNecessary(silent, w); 3393 } 3394 } 3395 3396 static void keySelectAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) 3397 { 3398 textDisp *textD = ((TextWidget)w)->text.textD; 3399 int stat, insertPos = TextDGetInsertPosition(textD); 3400 int silent = hasKey("nobell", args, nArgs); 3401 3402 cancelDrag(w); 3403 if (hasKey("left", args, nArgs)) stat = TextDMoveLeft(textD); 3404 else if (hasKey("right", args, nArgs)) stat = TextDMoveRight(textD); 3405 else if (hasKey("up", args, nArgs)) stat = TextDMoveUp(textD, 0); 3406 else if (hasKey("down", args, nArgs)) stat = TextDMoveDown(textD, 0); 3407 else { 3408 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args,nArgs)); 3409 return; 3410 } 3411 if (!stat) { 3412 ringIfNecessary(silent, w); 3413 } 3414 else { 3415 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args,nArgs)); 3416 checkAutoShowInsertPos(w); 3417 callCursorMovementCBs(w, event); 3418 } 3419 } 3420 3421 static void processUpAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) 3422 { 3423 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD); 3424 int silent = hasKey("nobell", args, nArgs); 3425 int abs = hasKey("absolute", args, nArgs); 3426 3427 cancelDrag(w); 3428 3429 textDisp *textD = ((TextWidget)w)->text.textD; 3430 size_t mcursorSize = textD->mcursorSize; 3431 textD->mcursorSize = 1; // emulate single-cursor 3432 int notMoved = 0; 3433 for(int i=0;i<mcursorSize;i++) { 3434 textD->cursor = textD->multicursor + i; 3435 insertPos = textD->cursor->cursorPos; 3436 3437 if (!TextDMoveUp(((TextWidget)w)->text.textD, abs)) { 3438 notMoved = 1; 3439 } 3440 3441 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3442 checkAutoShowInsertPos(w); 3443 3444 // unlike other process<> functions, we don't remove the possible 3445 // cursor duplicates here and use TextDCheckCursorDuplicates later 3446 // that function is a workaround for a redraw bug in combination 3447 // with Highlight Cursor Line 3448 } 3449 textD->mcursorSize = mcursorSize; 3450 textD->mcursorSizeReal = textD->mcursorSize; 3451 callCursorMovementCBs(w, event); 3452 3453 checkAutoShowInsertPos(w); 3454 if(notMoved) { 3455 TextDCheckCursorDuplicates(((TextWidget)w)->text.textD); 3456 ringIfNecessary(silent, w); 3457 } 3458 } 3459 3460 static void processShiftUpAP(Widget w, XEvent *event, String *args, 3461 Cardinal *nArgs) 3462 { 3463 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD); 3464 int silent = hasKey("nobell", args, nArgs); 3465 int abs = hasKey("absolute", args, nArgs); 3466 3467 cancelDrag(w); 3468 if (!TextDMoveUp(((TextWidget)w)->text.textD, abs)) 3469 ringIfNecessary(silent, w); 3470 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args, nArgs)); 3471 checkAutoShowInsertPos(w); 3472 callCursorMovementCBs(w, event); 3473 } 3474 3475 static void processDownAP(Widget w, XEvent *event, String *args, 3476 Cardinal *nArgs) 3477 { 3478 int insertPos; 3479 int silent = hasKey("nobell", args, nArgs); 3480 int abs = hasKey("absolute", args, nArgs); 3481 3482 cancelDrag(w); 3483 3484 textDisp *textD = ((TextWidget)w)->text.textD; 3485 size_t mcursorSize = textD->mcursorSize; 3486 textD->mcursorSize = 1; // emulate single-cursor 3487 int notMoved = 0; 3488 for(int i=mcursorSize-1;i>=0;i--) { 3489 textD->cursor = textD->multicursor + i; 3490 insertPos = textD->cursor->cursorPos; 3491 3492 if (!TextDMoveDown(((TextWidget)w)->text.textD, abs)) { 3493 notMoved = 1; 3494 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3495 callCursorMovementCBs(w, event); 3496 continue; 3497 } 3498 3499 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3500 3501 if(notMoved) { 3502 // if a cursor was not moved, we have to make sure, that another 3503 // cursor doesn't have the same position 3504 if(textD->multicursor[i].cursorPos == textD->multicursor[i+1].cursorPos) { 3505 // because we simulate single-cursor mode, we have to set 3506 // mcursorSize to the correct value here 3507 textD->mcursorSize = mcursorSize; 3508 TextDRemoveCursor(textD, i); 3509 textD->mcursorSize = 1; // reactivate single-cursor simulation 3510 mcursorSize--; 3511 } 3512 } 3513 } 3514 textD->mcursorSize = mcursorSize; 3515 callCursorMovementCBs(w, event); 3516 3517 checkAutoShowInsertPos(w); 3518 if(notMoved) { 3519 ringIfNecessary(silent, w); 3520 } 3521 } 3522 3523 static void processShiftDownAP(Widget w, XEvent *event, String *args, 3524 Cardinal *nArgs) 3525 { 3526 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD); 3527 int silent = hasKey("nobell", args, nArgs); 3528 int abs = hasKey("absolute", args, nArgs); 3529 3530 cancelDrag(w); 3531 if (!TextDMoveDown(((TextWidget)w)->text.textD, abs)) 3532 ringIfNecessary(silent, w); 3533 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args, nArgs)); 3534 checkAutoShowInsertPos(w); 3535 callCursorMovementCBs(w, event); 3536 } 3537 3538 #ifndef DISABLE_MULTICURSOR 3539 static void addCursorUpAP(Widget w, XEvent *event, String *args, 3540 Cardinal *nArgs) 3541 { 3542 textDisp *textD = ((TextWidget)w)->text.textD; 3543 size_t mcursorSize = textD->mcursorSize; 3544 textD->mcursorSize = 1; // emulate single-cursor 3545 3546 // get the first cursor (textD->multicursor is sorted) 3547 textD->cursor = &textD->multicursor[0]; 3548 int oldPos = textD->cursor->cursorPos; 3549 3550 // move last cursor down 3551 int moved = 0; 3552 if(TextDMoveUp(textD, 0)) { 3553 moved = 1; 3554 } 3555 textD->mcursorSize = mcursorSize; 3556 3557 // if the last cursor was moved, re-add the previous position 3558 if(moved) { 3559 TextDAddCursor(textD, oldPos); 3560 } 3561 3562 callCursorMovementCBs(w, event); 3563 } 3564 3565 static void addCursorDownAP(Widget w, XEvent *event, String *args, 3566 Cardinal *nArgs) 3567 { 3568 textDisp *textD = ((TextWidget)w)->text.textD; 3569 size_t mcursorSize = textD->mcursorSize; 3570 textD->mcursorSize = 1; // emulate single-cursor 3571 3572 // get the last cursor (textD->multicursor is sorted) 3573 textD->cursor = &textD->multicursor[mcursorSize-1]; 3574 int oldPos = textD->cursor->cursorPos; 3575 3576 // move last cursor down 3577 int moved = 0; 3578 if(TextDMoveDown(textD, 0)) { 3579 moved = 1; 3580 } 3581 textD->mcursorSize = mcursorSize; 3582 3583 // if the last cursor was moved, re-add the previous position 3584 if(moved) { 3585 TextDAddCursor(textD, oldPos); 3586 } 3587 3588 callCursorMovementCBs(w, event); 3589 } 3590 #endif 3591 3592 static void beginningOfLineAP(Widget w, XEvent *event, String *args, 3593 Cardinal *nArgs) 3594 { 3595 textDisp *textD = ((TextWidget)w)->text.textD; 3596 int absolute = hasKey("absolute", args, nArgs); 3597 3598 cancelDrag(w); 3599 3600 size_t mcursorSize = textD->mcursorSize; 3601 textD->mcursorSize = 1; // emulate single-cursor 3602 for(int i=0;i<mcursorSize;i++) { 3603 textD->cursor = textD->multicursor + i; 3604 int insertPos = textD->cursor->cursorPos; 3605 3606 if(absolute) { 3607 TextDSetInsertPosition(textD, BufStartOfLine(textD->buffer, insertPos)); 3608 } else { 3609 TextDSetInsertPosition(textD, TextDStartOfLine(textD, insertPos)); 3610 } 3611 3612 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3613 textD->cursor->cursorPreferredCol = 0; 3614 } 3615 textD->mcursorSize = mcursorSize; 3616 callCursorMovementCBs(w, event); 3617 3618 checkAutoShowInsertPos(w); 3619 } 3620 3621 static void endOfLineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) 3622 { 3623 textDisp *textD = ((TextWidget)w)->text.textD; 3624 int absolute = hasKey("absolute", args, nArgs); 3625 3626 cancelDrag(w); 3627 3628 size_t mcursorSize = textD->mcursorSize; 3629 textD->mcursorSize = 1; // emulate single-cursor 3630 for(int i=0;i<mcursorSize;i++) { 3631 textD->cursor = textD->multicursor + i; 3632 int insertPos = textD->cursor->cursorPos; 3633 3634 if(absolute) { 3635 TextDSetInsertPosition(textD, BufEndOfLine(textD->buffer, insertPos)); 3636 } else { 3637 TextDSetInsertPosition(textD, TextDEndOfLine(textD, insertPos, False)); 3638 } 3639 3640 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3641 textD->cursor->cursorPreferredCol = -1; 3642 } 3643 textD->mcursorSize = mcursorSize; 3644 callCursorMovementCBs(w, event); 3645 3646 checkAutoShowInsertPos(w); 3647 } 3648 3649 static void beginningOfFileAP(Widget w, XEvent *event, String *args, 3650 Cardinal *nArgs) 3651 { 3652 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD); 3653 textDisp *textD = ((TextWidget)w)->text.textD; 3654 3655 cancelDrag(w); 3656 if (hasKey("scrollbar", args, nArgs)) { 3657 if (textD->topLineNum != 1) { 3658 TextDSetScroll(textD, 1, textD->horizOffset); 3659 } 3660 } 3661 else { 3662 TextDSetInsertPosition(((TextWidget)w)->text.textD, 0); 3663 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3664 checkAutoShowInsertPos(w); 3665 callCursorMovementCBs(w, event); 3666 } 3667 } 3668 3669 static void endOfFileAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) 3670 { 3671 textDisp *textD = ((TextWidget)w)->text.textD; 3672 int insertPos = TextDGetInsertPosition(textD); 3673 int lastTopLine; 3674 3675 cancelDrag(w); 3676 if (hasKey("scrollbar", args, nArgs)) { 3677 lastTopLine = max(1, 3678 textD->nBufferLines - (textD->nVisibleLines - 2) + 3679 ((TextWidget)w)->text.cursorVPadding); 3680 if (lastTopLine != textD->topLineNum) { 3681 TextDSetScroll(textD, lastTopLine, textD->horizOffset); 3682 } 3683 } 3684 else { 3685 TextDSetInsertPosition(textD, textD->buffer->length); 3686 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3687 checkAutoShowInsertPos(w); 3688 callCursorMovementCBs(w, event); 3689 } 3690 } 3691 3692 static void nextPageAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) 3693 { 3694 textDisp *textD = ((TextWidget)w)->text.textD; 3695 textBuffer *buf = textD->buffer; 3696 int lastTopLine = max(1, 3697 textD->nBufferLines - (textD->nVisibleLines - 2) + 3698 ((TextWidget)w)->text.cursorVPadding ); 3699 int insertPos = TextDGetInsertPosition(textD); 3700 int column = 0, visLineNum, lineStartPos; 3701 int pos, targetLine; 3702 int pageForwardCount = max(1, textD->nVisibleLines - 1); 3703 int maintainColumn = 0; 3704 int silent = hasKey("nobell", args, nArgs); 3705 3706 maintainColumn = hasKey("column", args, nArgs); 3707 cancelDrag(w); 3708 if (hasKey("scrollbar", args, nArgs)) { /* scrollbar only */ 3709 targetLine = min(textD->topLineNum + pageForwardCount, lastTopLine); 3710 3711 if (targetLine == textD->topLineNum) { 3712 ringIfNecessary(silent, w); 3713 return; 3714 } 3715 TextDSetScroll(textD, targetLine, textD->horizOffset); 3716 } 3717 else if (hasKey("stutter", args, nArgs)) { /* Mac style */ 3718 /* move to bottom line of visible area */ 3719 /* if already there, page down maintaining preferrred column */ 3720 targetLine = max(min(textD->nVisibleLines - 1, textD->nBufferLines), 0); 3721 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos); 3722 if (lineStartPos == textD->lineStarts[targetLine]) { 3723 if (insertPos >= buf->length || textD->topLineNum == lastTopLine) { 3724 ringIfNecessary(silent, w); 3725 return; 3726 } 3727 targetLine = min(textD->topLineNum + pageForwardCount, lastTopLine); 3728 pos = TextDCountForwardNLines(textD, insertPos, pageForwardCount, False); 3729 if (maintainColumn) { 3730 pos = TextDPosOfPreferredCol(textD, column, pos); 3731 } 3732 TextDSetInsertPosition(textD, pos); 3733 TextDSetScroll(textD, targetLine, textD->horizOffset); 3734 } 3735 else { 3736 pos = textD->lineStarts[targetLine]; 3737 while (targetLine > 0 && pos == -1) { 3738 --targetLine; 3739 pos = textD->lineStarts[targetLine]; 3740 } 3741 if (lineStartPos == pos) { 3742 ringIfNecessary(silent, w); 3743 return; 3744 } 3745 if (maintainColumn) { 3746 pos = TextDPosOfPreferredCol(textD, column, pos); 3747 } 3748 TextDSetInsertPosition(textD, pos); 3749 } 3750 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3751 checkAutoShowInsertPos(w); 3752 callCursorMovementCBs(w, event); 3753 if (maintainColumn) { 3754 textD->cursor->cursorPreferredCol = column; 3755 } 3756 else { 3757 textD->cursor->cursorPreferredCol = -1; 3758 } 3759 } 3760 else { /* "standard" */ 3761 if (insertPos >= buf->length && textD->topLineNum == lastTopLine) { 3762 ringIfNecessary(silent, w); 3763 return; 3764 } 3765 if (maintainColumn) { 3766 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos); 3767 } 3768 targetLine = textD->topLineNum + textD->nVisibleLines - 1; 3769 if (targetLine < 1) targetLine = 1; 3770 if (targetLine > lastTopLine) targetLine = lastTopLine; 3771 pos = TextDCountForwardNLines(textD, insertPos, textD->nVisibleLines-1, False); 3772 if (maintainColumn) { 3773 pos = TextDPosOfPreferredCol(textD, column, pos); 3774 } 3775 TextDSetInsertPosition(textD, pos); 3776 TextDSetScroll(textD, targetLine, textD->horizOffset); 3777 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3778 checkAutoShowInsertPos(w); 3779 callCursorMovementCBs(w, event); 3780 if (maintainColumn) { 3781 textD->cursor->cursorPreferredCol = column; 3782 } 3783 else { 3784 textD->cursor->cursorPreferredCol = -1; 3785 } 3786 } 3787 } 3788 3789 static void previousPageAP(Widget w, XEvent *event, String *args, 3790 Cardinal *nArgs) 3791 { 3792 textDisp *textD = ((TextWidget)w)->text.textD; 3793 int insertPos = TextDGetInsertPosition(textD); 3794 int column = 0, visLineNum, lineStartPos; 3795 int pos, targetLine; 3796 int pageBackwardCount = max(1, textD->nVisibleLines - 1); 3797 int maintainColumn = 0; 3798 int silent = hasKey("nobell", args, nArgs); 3799 3800 maintainColumn = hasKey("column", args, nArgs); 3801 cancelDrag(w); 3802 if (hasKey("scrollbar", args, nArgs)) { /* scrollbar only */ 3803 targetLine = max(textD->topLineNum - pageBackwardCount, 1); 3804 3805 if (targetLine == textD->topLineNum) { 3806 ringIfNecessary(silent, w); 3807 return; 3808 } 3809 TextDSetScroll(textD, targetLine, textD->horizOffset); 3810 } 3811 else if (hasKey("stutter", args, nArgs)) { /* Mac style */ 3812 /* move to top line of visible area */ 3813 /* if already there, page up maintaining preferrred column if required */ 3814 targetLine = 0; 3815 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos); 3816 if (lineStartPos == textD->lineStarts[targetLine]) { 3817 if (textD->topLineNum == 1 && (maintainColumn || column == 0)) { 3818 ringIfNecessary(silent, w); 3819 return; 3820 } 3821 targetLine = max(textD->topLineNum - pageBackwardCount, 1); 3822 pos = TextDCountBackwardNLines(textD, insertPos, pageBackwardCount); 3823 if (maintainColumn) { 3824 pos = TextDPosOfPreferredCol(textD, column, pos); 3825 } 3826 TextDSetInsertPosition(textD, pos); 3827 TextDSetScroll(textD, targetLine, textD->horizOffset); 3828 } 3829 else { 3830 pos = textD->lineStarts[targetLine]; 3831 if (maintainColumn) { 3832 pos = TextDPosOfPreferredCol(textD, column, pos); 3833 } 3834 TextDSetInsertPosition(textD, pos); 3835 } 3836 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3837 checkAutoShowInsertPos(w); 3838 callCursorMovementCBs(w, event); 3839 if (maintainColumn) { 3840 textD->cursor->cursorPreferredCol = column; 3841 } 3842 else { 3843 textD->cursor->cursorPreferredCol = -1; 3844 } 3845 } 3846 else { /* "standard" */ 3847 if (insertPos <= 0 && textD->topLineNum == 1) { 3848 ringIfNecessary(silent, w); 3849 return; 3850 } 3851 if (maintainColumn) { 3852 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos); 3853 } 3854 targetLine = textD->topLineNum - (textD->nVisibleLines - 1); 3855 if (targetLine < 1) targetLine = 1; 3856 pos = TextDCountBackwardNLines(textD, insertPos, textD->nVisibleLines-1); 3857 if (maintainColumn) { 3858 pos = TextDPosOfPreferredCol(textD, column, pos); 3859 } 3860 TextDSetInsertPosition(textD, pos); 3861 TextDSetScroll(textD, targetLine, textD->horizOffset); 3862 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3863 checkAutoShowInsertPos(w); 3864 callCursorMovementCBs(w, event); 3865 if (maintainColumn) { 3866 textD->cursor->cursorPreferredCol = column; 3867 } 3868 else { 3869 textD->cursor->cursorPreferredCol = -1; 3870 } 3871 } 3872 } 3873 3874 static void pageLeftAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) 3875 { 3876 textDisp *textD = ((TextWidget)w)->text.textD; 3877 textBuffer *buf = textD->buffer; 3878 int insertPos = TextDGetInsertPosition(textD); 3879 int maxCharWidth = textD->font->maxWidth; 3880 int lineStartPos, indent, pos; 3881 int horizOffset; 3882 int silent = hasKey("nobell", args, nArgs); 3883 3884 cancelDrag(w); 3885 if (hasKey("scrollbar", args, nArgs)) { 3886 if (textD->horizOffset == 0) { 3887 ringIfNecessary(silent, w); 3888 return; 3889 } 3890 horizOffset = max(0, textD->horizOffset - textD->width); 3891 TextDSetScroll(textD, textD->topLineNum, horizOffset); 3892 } 3893 else { 3894 lineStartPos = BufStartOfLine(buf, insertPos); 3895 if (insertPos == lineStartPos && textD->horizOffset == 0) { 3896 ringIfNecessary(silent, w); 3897 return; 3898 } 3899 indent = BufCountDispChars(buf, lineStartPos, insertPos); 3900 pos = BufCountForwardDispChars(buf, lineStartPos, 3901 max(0, indent - textD->width / maxCharWidth)); 3902 TextDSetInsertPosition(textD, pos); 3903 TextDSetScroll(textD, textD->topLineNum, 3904 max(0, textD->horizOffset - textD->width)); 3905 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3906 checkAutoShowInsertPos(w); 3907 callCursorMovementCBs(w, event); 3908 } 3909 } 3910 3911 static void pageRightAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) 3912 { 3913 textDisp *textD = ((TextWidget)w)->text.textD; 3914 textBuffer *buf = textD->buffer; 3915 int insertPos = TextDGetInsertPosition(textD); 3916 int maxCharWidth = textD->font->maxWidth; 3917 int oldHorizOffset = textD->horizOffset; 3918 int lineStartPos, indent, pos; 3919 int horizOffset, sliderSize, sliderMax; 3920 int silent = hasKey("nobell", args, nArgs); 3921 3922 cancelDrag(w); 3923 if (hasKey("scrollbar", args, nArgs)) { 3924 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax, 3925 XmNsliderSize, &sliderSize, NULL); 3926 horizOffset = min(textD->horizOffset + textD->width, sliderMax - sliderSize); 3927 if (textD->horizOffset == horizOffset) { 3928 ringIfNecessary(silent, w); 3929 return; 3930 } 3931 TextDSetScroll(textD, textD->topLineNum, horizOffset); 3932 } 3933 else { 3934 lineStartPos = BufStartOfLine(buf, insertPos); 3935 indent = BufCountDispChars(buf, lineStartPos, insertPos); 3936 pos = BufCountForwardDispChars(buf, lineStartPos, 3937 indent + textD->width / maxCharWidth); 3938 TextDSetInsertPosition(textD, pos); 3939 TextDSetScroll(textD, textD->topLineNum, textD->horizOffset + textD->width); 3940 if (textD->horizOffset == oldHorizOffset && insertPos == pos) 3941 ringIfNecessary(silent, w); 3942 checkMoveSelectionChange(w, event, insertPos, args, nArgs); 3943 checkAutoShowInsertPos(w); 3944 callCursorMovementCBs(w, event); 3945 } 3946 } 3947 3948 static void toggleOverstrikeAP(Widget w, XEvent *event, String *args, 3949 Cardinal *nArgs) 3950 { 3951 TextWidget tw = (TextWidget)w; 3952 3953 if (tw->text.overstrike) { 3954 tw->text.overstrike = False; 3955 TextDSetCursorStyle(tw->text.textD, 3956 tw->text.heavyCursor ? HEAVY_CURSOR : NORMAL_CURSOR); 3957 } else { 3958 tw->text.overstrike = True; 3959 if ( tw->text.textD->cursorStyle == NORMAL_CURSOR || 3960 tw->text.textD->cursorStyle == HEAVY_CURSOR) 3961 TextDSetCursorStyle(tw->text.textD, BLOCK_CURSOR); 3962 } 3963 } 3964 3965 static void scrollUpAP(Widget w, XEvent *event, String *args, 3966 Cardinal *nArgs) 3967 { 3968 textDisp *textD = ((TextWidget)w)->text.textD; 3969 int topLineNum, horizOffset, nLines; 3970 3971 if (*nArgs == 0 || sscanf(args[0], "%d", &nLines) != 1) 3972 return; 3973 if (*nArgs == 2) { 3974 /* Allow both 'page' and 'pages' */ 3975 if (strncmp(args[1], "page", 4) == 0) 3976 nLines *= textD->nVisibleLines; 3977 3978 /* 'line' or 'lines' is the only other valid possibility */ 3979 else if (strncmp(args[1], "line", 4) != 0) 3980 return; 3981 } 3982 TextDGetScroll(textD, &topLineNum, &horizOffset); 3983 TextDSetScroll(textD, topLineNum-nLines, horizOffset); 3984 } 3985 3986 static void scrollDownAP(Widget w, XEvent *event, String *args, 3987 Cardinal *nArgs) 3988 { 3989 textDisp *textD = ((TextWidget)w)->text.textD; 3990 int topLineNum, horizOffset, nLines; 3991 3992 if (*nArgs == 0 || sscanf(args[0], "%d", &nLines) != 1) 3993 return; 3994 if (*nArgs == 2) { 3995 /* Allow both 'page' and 'pages' */ 3996 if (strncmp(args[1], "page", 4) == 0) 3997 nLines *= textD->nVisibleLines; 3998 3999 /* 'line' or 'lines' is the only other valid possibility */ 4000 else if (strncmp(args[1], "line", 4) != 0) 4001 return; 4002 } 4003 TextDGetScroll(textD, &topLineNum, &horizOffset); 4004 TextDSetScroll(textD, topLineNum+nLines, horizOffset); 4005 } 4006 4007 static void scrollLeftAP(Widget w, XEvent *event, String *args, 4008 Cardinal *nArgs) 4009 { 4010 textDisp *textD = ((TextWidget)w)->text.textD; 4011 int horizOffset, nPixels; 4012 int sliderMax, sliderSize; 4013 4014 if (*nArgs == 0 || sscanf(args[0], "%d", &nPixels) != 1) 4015 return; 4016 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax, 4017 XmNsliderSize, &sliderSize, NULL); 4018 horizOffset = min(max(0, textD->horizOffset - nPixels), sliderMax - sliderSize); 4019 if (textD->horizOffset != horizOffset) { 4020 TextDSetScroll(textD, textD->topLineNum, horizOffset); 4021 } 4022 } 4023 4024 static void scrollRightAP(Widget w, XEvent *event, String *args, 4025 Cardinal *nArgs) 4026 { 4027 textDisp *textD = ((TextWidget)w)->text.textD; 4028 int horizOffset, nPixels; 4029 int sliderMax, sliderSize; 4030 4031 if (*nArgs == 0 || sscanf(args[0], "%d", &nPixels) != 1) 4032 return; 4033 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax, 4034 XmNsliderSize, &sliderSize, NULL); 4035 horizOffset = min(max(0, textD->horizOffset + nPixels), sliderMax - sliderSize); 4036 if (textD->horizOffset != horizOffset) { 4037 TextDSetScroll(textD, textD->topLineNum, horizOffset); 4038 } 4039 } 4040 4041 static void scrollToLineAP(Widget w, XEvent *event, String *args, 4042 Cardinal *nArgs) 4043 { 4044 textDisp *textD = ((TextWidget)w)->text.textD; 4045 int topLineNum, horizOffset, lineNum; 4046 4047 if (*nArgs == 0 || sscanf(args[0], "%d", &lineNum) != 1) 4048 return; 4049 TextDGetScroll(textD, &topLineNum, &horizOffset); 4050 TextDSetScroll(textD, lineNum, horizOffset); 4051 } 4052 4053 static void selectAllAP(Widget w, XEvent *event, String *args, 4054 Cardinal *nArgs) 4055 { 4056 textBuffer *buf = ((TextWidget)w)->text.textD->buffer; 4057 4058 cancelDrag(w); 4059 BufSelect(buf, 0, buf->length); 4060 } 4061 4062 static void deselectAllAP(Widget w, XEvent *event, String *args, 4063 Cardinal *nArgs) 4064 { 4065 cancelDrag(w); 4066 BufUnselect(((TextWidget)w)->text.textD->buffer); 4067 } 4068 4069 /* 4070 ** Called on the Intrinsic FocusIn event. 4071 ** 4072 ** Note that the widget has no internal state about the focus, ie. it does 4073 ** not know whether it has the focus or not. 4074 */ 4075 static void focusInAP(Widget widget, XEvent* event, String* unused1, 4076 Cardinal* unused2) 4077 { 4078 TextWidget textwidget = (TextWidget) widget; 4079 textDisp* textD = textwidget->text.textD; 4080 4081 /* I don't entirely understand the traversal mechanism in Motif widgets, 4082 particularly, what leads to this widget getting a focus-in event when 4083 it does not actually have the input focus. The temporary solution is 4084 to do the comparison below, and not show the cursor when Motif says 4085 we don't have focus, but keep looking for the real answer */ 4086 #if XmVersion >= 1002 4087 if (widget != XmGetFocusWidget(widget)) 4088 return; 4089 #endif 4090 4091 /* If the timer is not already started, start it */ 4092 if (textwidget->text.cursorBlinkRate != 0 4093 && textwidget->text.cursorBlinkProcID == 0) { 4094 textwidget->text.cursorBlinkProcID 4095 = XtAppAddTimeOut(XtWidgetToApplicationContext(widget), 4096 textwidget->text.cursorBlinkRate, cursorBlinkTimerProc, 4097 widget); 4098 } 4099 4100 /* Change the cursor to active style */ 4101 if (textwidget->text.overstrike) 4102 TextDSetCursorStyle(textD, BLOCK_CURSOR); 4103 else 4104 TextDSetCursorStyle(textD, (textwidget->text.heavyCursor 4105 ? HEAVY_CURSOR 4106 : NORMAL_CURSOR)); 4107 TextDUnblankCursor(textD); 4108 4109 #ifndef NO_XMIM 4110 /* Notify Motif input manager that widget has focus */ 4111 XmImVaSetFocusValues(widget, NULL); 4112 #endif 4113 4114 /* Call any registered focus-in callbacks */ 4115 XtCallCallbacks((Widget) widget, textNfocusCallback, (XtPointer) event); 4116 4117 XSetICFocus(textwidget->text.xic); 4118 } 4119 4120 static void focusOutAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) 4121 { 4122 textDisp *textD = ((TextWidget)w)->text.textD; 4123 4124 /* Remove the cursor blinking timer procedure */ 4125 if (((TextWidget)w)->text.cursorBlinkProcID != 0) 4126 XtRemoveTimeOut(((TextWidget)w)->text.cursorBlinkProcID); 4127 ((TextWidget)w)->text.cursorBlinkProcID = 0; 4128 4129 /* Leave a dim or destination cursor */ 4130 TextDSetCursorStyle(textD, ((TextWidget)w)->text.motifDestOwner ? 4131 CARET_CURSOR : DIM_CURSOR); 4132 TextDUnblankCursor(textD); 4133 4134 /* If there's a calltip displayed, kill it. */ 4135 TextDKillCalltip(textD, 0); 4136 4137 /* Call any registered focus-out callbacks */ 4138 XtCallCallbacks((Widget)w, textNlosingFocusCallback, (XtPointer)event); 4139 } 4140 4141 4142 static void zoom(Widget w, int step) { 4143 WindowInfo *win = WidgetToWindow(w); 4144 if(win) { 4145 SetZoom(win, step); 4146 } else { 4147 SetHelpZoom(w, step); 4148 } 4149 } 4150 4151 static void zoomInAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) { 4152 zoom(w, GetZoomStep()); 4153 } 4154 4155 static void zoomOutAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) { 4156 zoom(w, -GetZoomStep()); 4157 } 4158 4159 /* 4160 ** For actions involving cursor movement, "extend" keyword means incorporate 4161 ** the new cursor position in the selection, and lack of an "extend" keyword 4162 ** means cancel the existing selection 4163 */ 4164 static void checkMoveSelectionChange(Widget w, XEvent *event, int startPos, 4165 String *args, Cardinal *nArgs) 4166 { 4167 if (hasKey("extend", args, nArgs)) 4168 keyMoveExtendSelection(w, event, startPos, hasKey("rect", args, nArgs)); 4169 else 4170 BufUnselect((((TextWidget)w)->text.textD)->buffer); 4171 } 4172 4173 /* 4174 ** If a selection change was requested via a keyboard command for moving 4175 ** the insertion cursor (usually with the "extend" keyword), adjust the 4176 ** selection to include the new cursor position, or begin a new selection 4177 ** between startPos and the new cursor position with anchor at startPos. 4178 */ 4179 static void keyMoveExtendSelection(Widget w, XEvent *event, int origPos, 4180 int rectangular) 4181 { 4182 XKeyEvent *e = &event->xkey; 4183 TextWidget tw = (TextWidget)w; 4184 textDisp *textD = tw->text.textD; 4185 textBuffer *buf = textD->buffer; 4186 selection *sel = &buf->primary; 4187 int newPos = TextDGetInsertPosition(textD); 4188 int startPos, endPos, startCol, endCol, newCol, origCol; 4189 int anchor, rectAnchor, anchorLineStart; 4190 4191 /* Moving the cursor does not take the Motif destination, but as soon as 4192 the user selects something, grab it (I'm not sure if this distinction 4193 actually makes sense, but it's what Motif was doing, back when their 4194 secondary selections actually worked correctly) */ 4195 TakeMotifDestination(w, e->time); 4196 4197 if ((sel->selected || sel->zeroWidth) && sel->rectangular && rectangular) { 4198 /* rect -> rect */ 4199 newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos); 4200 startCol = min(tw->text.rectAnchor, newCol); 4201 endCol = max(tw->text.rectAnchor, newCol); 4202 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos)); 4203 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos)); 4204 BufRectSelect(buf, startPos, endPos, startCol, endCol); 4205 } else if (sel->selected && rectangular) { /* plain -> rect */ 4206 newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos); 4207 if (abs(newPos - sel->start) < abs(newPos - sel->end)) 4208 anchor = sel->end; 4209 else 4210 anchor = sel->start; 4211 anchorLineStart = BufStartOfLine(buf, anchor); 4212 rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor); 4213 tw->text.anchor = anchor; 4214 tw->text.rectAnchor = rectAnchor; 4215 BufRectSelect(buf, BufStartOfLine(buf, min(anchor, newPos)), 4216 BufEndOfLine(buf, max(anchor, newPos)), 4217 min(rectAnchor, newCol), max(rectAnchor, newCol)); 4218 } else if (sel->selected && sel->rectangular) { /* rect -> plain */ 4219 startPos = BufCountForwardDispChars(buf, 4220 BufStartOfLine(buf, sel->start), sel->rectStart); 4221 endPos = BufCountForwardDispChars(buf, 4222 BufStartOfLine(buf, sel->end), sel->rectEnd); 4223 if (abs(origPos - startPos) < abs(origPos - endPos)) 4224 anchor = endPos; 4225 else 4226 anchor = startPos; 4227 BufSelect(buf, anchor, newPos); 4228 } else if (sel->selected) { /* plain -> plain */ 4229 if (abs(origPos - sel->start) < abs(origPos - sel->end)) 4230 anchor = sel->end; 4231 else 4232 anchor = sel->start; 4233 BufSelect(buf, anchor, newPos); 4234 } else if (rectangular) { /* no sel -> rect */ 4235 origCol = BufCountDispChars(buf, BufStartOfLine(buf, origPos), origPos); 4236 newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos); 4237 startCol = min(newCol, origCol); 4238 endCol = max(newCol, origCol); 4239 startPos = BufStartOfLine(buf, min(origPos, newPos)); 4240 endPos = BufEndOfLine(buf, max(origPos, newPos)); 4241 tw->text.anchor = origPos; 4242 tw->text.rectAnchor = origCol; 4243 BufRectSelect(buf, startPos, endPos, startCol, endCol); 4244 } else { /* no sel -> plain */ 4245 tw->text.anchor = origPos; 4246 tw->text.rectAnchor = BufCountDispChars(buf, 4247 BufStartOfLine(buf, origPos), origPos); 4248 BufSelect(buf, tw->text.anchor, newPos); 4249 } 4250 } 4251 4252 static void checkAutoShowInsertPos(Widget w) 4253 { 4254 if (((TextWidget)w)->text.autoShowInsertPos) 4255 TextDMakeInsertPosVisible(((TextWidget)w)->text.textD); 4256 } 4257 4258 static int checkReadOnly(Widget w) 4259 { 4260 if (((TextWidget)w)->text.readOnly) { 4261 XBell(XtDisplay(w), 0); 4262 return True; 4263 } 4264 return False; 4265 } 4266 4267 4268 static void simpleInsertAtCursorPos(Widget w, textDisp *textD, char *chars) { 4269 char *c; 4270 if (((TextWidget)w)->text.overstrike) { 4271 for (c=chars; *c!='\0' && *c!='\n'; c++) {} 4272 if (*c == '\n') { 4273 TextDInsert(textD, chars); 4274 } else { 4275 TextDOverstrike(textD, chars); 4276 } 4277 } else { 4278 TextDInsert(textD, chars); 4279 } 4280 } 4281 4282 /* 4283 ** Insert text "chars" at the cursor position, as if the text had been 4284 ** typed. Same as TextInsertAtCursor, but without the complicated auto-wrap 4285 ** scanning and re-formatting. 4286 */ 4287 static void simpleInsertAtCursor(Widget w, char *chars, XEvent *event, 4288 int allowPendingDelete) 4289 { 4290 textDisp *textD = ((TextWidget)w)->text.textD; 4291 textBuffer *buf = textD->buffer; 4292 4293 if (allowPendingDelete && pendingSelection(w)) { 4294 // force redraw of cursorline by TexDSetInsertPosition 4295 textD->redrawCursorLine = True; 4296 BufReplaceSelected(buf, chars); 4297 TextDSetInsertPosition(textD, buf->cursorPosHint); 4298 textD->redrawCursorLine = False; 4299 } else { 4300 if(textD->mcursorSize == 1) { 4301 simpleInsertAtCursorPos(w, textD, chars); 4302 } else { 4303 BufBeginModifyBatch(buf); 4304 TextDBlankCursor(textD); 4305 4306 int diff = 0; 4307 size_t mcursorSize = textD->mcursorSize; 4308 for(int i=0;i<mcursorSize;i++) { 4309 textD->multicursor[i].cursorPos += diff; 4310 textD->cursor = textD->multicursor + i; 4311 int prevPos = textD->cursor->cursorPos; 4312 simpleInsertAtCursorPos(w, textD, chars); 4313 diff += textD->cursor->cursorPos - prevPos; 4314 } 4315 4316 TextDUnblankCursor(textD); 4317 BufEndModifyBatch(buf); 4318 } 4319 } 4320 4321 callCursorMovementCBs(w, event); 4322 checkAutoShowInsertPos(w); 4323 } 4324 4325 /* 4326 ** If there's a selection, delete it and position the cursor where the 4327 ** selection was deleted. (Called by routines which do deletion to check 4328 ** first for and do possible selection delete) 4329 */ 4330 static int deletePendingSelection(Widget w, XEvent *event) 4331 { 4332 textDisp *textD = ((TextWidget)w)->text.textD; 4333 textBuffer *buf = textD->buffer; 4334 4335 if (((TextWidget)w)->text.textD->buffer->primary.selected) { 4336 // force redraw of cursorline by TexDSetInsertPosition 4337 textD->redrawCursorLine = True; 4338 4339 BufRemoveSelected(buf); 4340 TextDSetInsertPosition(textD, buf->cursorPosHint); 4341 checkAutoShowInsertPos(w); 4342 callCursorMovementCBs(w, event); 4343 textD->redrawCursorLine = False; 4344 return True; 4345 } else 4346 return False; 4347 } 4348 4349 /* 4350 ** Return true if pending delete is on and there's a selection contiguous 4351 ** with the cursor ready to be deleted. These criteria are used to decide 4352 ** if typing a character or inserting something should delete the selection 4353 ** first. 4354 */ 4355 static int pendingSelection(Widget w) 4356 { 4357 selection *sel = &((TextWidget)w)->text.textD->buffer->primary; 4358 int pos = TextDGetInsertPosition(((TextWidget)w)->text.textD); 4359 4360 return ((TextWidget)w)->text.pendingDelete && sel->selected && 4361 pos >= sel->start && pos <= sel->end; 4362 } 4363 4364 /* 4365 ** Check if tab emulation is on and if there are emulated tabs before the 4366 ** cursor, and if so, delete an emulated tab as a unit. Also finishes up 4367 ** by calling checkAutoShowInsertPos and callCursorMovementCBs, so the 4368 ** calling action proc can just return (this is necessary to preserve 4369 ** emTabsBeforeCursor which is otherwise cleared by callCursorMovementCBs). 4370 */ 4371 static int deleteEmulatedTab(Widget w, XEvent *event) 4372 { 4373 textDisp *textD = ((TextWidget)w)->text.textD; 4374 textBuffer *buf = ((TextWidget)w)->text.textD->buffer; 4375 int emTabDist = ((TextWidget)w)->text.emulateTabs; 4376 int emTabsBeforeCursor = ((TextWidget)w)->text.emTabsBeforeCursor; 4377 int startIndent, toIndent, insertPos, startPos, lineStart; 4378 int pos, indent, startPosIndent; 4379 char c, *spaceString; 4380 4381 if (emTabDist <= 0 || emTabsBeforeCursor <= 0) 4382 return False; 4383 4384 /* Find the position of the previous tab stop */ 4385 insertPos = TextDGetInsertPosition(textD); 4386 lineStart = BufStartOfLine(buf, insertPos); 4387 startIndent = BufCountDispChars(buf, lineStart, insertPos); 4388 toIndent = (startIndent-1) - ((startIndent-1) % emTabDist); 4389 4390 /* Find the position at which to begin deleting (stop at non-whitespace 4391 characters) */ 4392 startPosIndent = indent = 0; 4393 startPos = lineStart; 4394 for (pos=lineStart; pos < insertPos; pos++) { 4395 c = BufGetCharacter(buf, pos); 4396 indent += BufCharWidth(c, indent, buf->tabDist, buf->nullSubsChar); 4397 if (indent > toIndent) 4398 break; 4399 startPosIndent = indent; 4400 startPos = pos + 1; 4401 } 4402 4403 /* Just to make sure, check that we're not deleting any non-white chars */ 4404 for (pos=insertPos-1; pos>=startPos; pos--) { 4405 c = BufGetCharacter(buf, pos); 4406 if (c != ' ' && c != '\t') { 4407 startPos = pos + 1; 4408 break; 4409 } 4410 } 4411 4412 /* Do the text replacement and reposition the cursor. If any spaces need 4413 to be inserted to make up for a deleted tab, do a BufReplace, otherwise, 4414 do a BufRemove. */ 4415 if (startPosIndent < toIndent) { 4416 spaceString = (char*)NEditMalloc(toIndent - startPosIndent + 1); 4417 memset(spaceString, ' ', toIndent-startPosIndent); 4418 spaceString[toIndent - startPosIndent] = '\0'; 4419 BufReplace(buf, startPos, insertPos, spaceString); 4420 TextDSetInsertPosition(textD, startPos + toIndent - startPosIndent); 4421 NEditFree(spaceString); 4422 } else { 4423 BufRemove(buf, startPos, insertPos); 4424 TextDSetInsertPosition(textD, startPos); 4425 } 4426 4427 /* The normal cursor movement stuff would usually be called by the action 4428 routine, but this wraps around it to restore emTabsBeforeCursor */ 4429 checkAutoShowInsertPos(w); 4430 callCursorMovementCBs(w, event); 4431 4432 /* Decrement and restore the marker for consecutive emulated tabs, which 4433 would otherwise have been zeroed by callCursorMovementCBs */ 4434 ((TextWidget)w)->text.emTabsBeforeCursor = emTabsBeforeCursor - 1; 4435 return True; 4436 } 4437 4438 /* 4439 ** Select the word or whitespace adjacent to the cursor, and move the cursor 4440 ** to its end. pointerX is used as a tie-breaker, when the cursor is at the 4441 ** boundary between a word and some white-space. If the cursor is on the 4442 ** left, the word or space on the left is used. If it's on the right, that 4443 ** is used instead. 4444 */ 4445 static void selectWord(Widget w, int pointerX) 4446 { 4447 TextWidget tw = (TextWidget)w; 4448 textBuffer *buf = tw->text.textD->buffer; 4449 int x, y, insertPos = TextDGetInsertPosition(tw->text.textD); 4450 4451 TextPosToXY(w, insertPos, &x, &y); 4452 if (pointerX < x && insertPos > 0 && BufGetCharacter(buf, insertPos-1) != '\n') 4453 insertPos--; 4454 BufSelect(buf, startOfWord(tw, insertPos), endOfWord(tw, insertPos)); 4455 } 4456 4457 static int startOfWord(TextWidget w, int pos) 4458 { 4459 int startPos; 4460 textBuffer *buf = w->text.textD->buffer; 4461 char *delimiters=w->text.delimiters; 4462 char c = BufGetCharacter(buf, pos); 4463 4464 if (c == ' ' || c== '\t') { 4465 if (!spanBackward(buf, pos, " \t", False, &startPos)) 4466 return 0; 4467 } else if (strchr(delimiters, c)) { 4468 if (!spanBackward(buf, pos, delimiters, True, &startPos)) 4469 return 0; 4470 } else { 4471 if (!BufSearchBackward(buf, pos, delimiters, &startPos)) 4472 return 0; 4473 } 4474 return min(pos, startPos+1); 4475 4476 } 4477 4478 static int endOfWord(TextWidget w, int pos) 4479 { 4480 int endPos; 4481 textBuffer *buf = w->text.textD->buffer; 4482 char *delimiters=w->text.delimiters; 4483 char c = BufGetCharacter(buf, pos); 4484 4485 if (c == ' ' || c== '\t') { 4486 if (!spanForward(buf, pos, " \t", False, &endPos)) 4487 return buf->length; 4488 } else if (strchr(delimiters, c)) { 4489 if (!spanForward(buf, pos, delimiters, True, &endPos)) 4490 return buf->length; 4491 } else { 4492 if (!BufSearchForward(buf, pos, delimiters, &endPos)) 4493 return buf->length; 4494 } 4495 return endPos; 4496 } 4497 4498 /* 4499 ** Search forwards in buffer "buf" for the first character NOT in 4500 ** "searchChars", starting with the character "startPos", and returning the 4501 ** result in "foundPos" returns True if found, False if not. If ignoreSpace 4502 ** is set, then Space, Tab, and Newlines are ignored in searchChars. 4503 */ 4504 static int spanForward(textBuffer *buf, int startPos, char *searchChars, 4505 int ignoreSpace, int *foundPos) 4506 { 4507 int pos; 4508 char *c; 4509 4510 pos = startPos; 4511 while (pos < buf->length) { 4512 for (c=searchChars; *c!='\0'; c++) 4513 if(!(ignoreSpace && (*c==' ' || *c=='\t' || *c=='\n'))) 4514 if (BufGetCharacter(buf, pos) == *c) 4515 break; 4516 if(*c == 0) { 4517 *foundPos = pos; 4518 return True; 4519 } 4520 pos++; 4521 } 4522 *foundPos = buf->length; 4523 return False; 4524 } 4525 4526 /* 4527 ** Search backwards in buffer "buf" for the first character NOT in 4528 ** "searchChars", starting with the character BEFORE "startPos", returning the 4529 ** result in "foundPos" returns True if found, False if not. If ignoreSpace is 4530 ** set, then Space, Tab, and Newlines are ignored in searchChars. 4531 */ 4532 static int spanBackward(textBuffer *buf, int startPos, char *searchChars, int 4533 ignoreSpace, int *foundPos) 4534 { 4535 int pos; 4536 char *c; 4537 4538 if (startPos == 0) { 4539 *foundPos = 0; 4540 return False; 4541 } 4542 pos = startPos == 0 ? 0 : startPos - 1; 4543 while (pos >= 0) { 4544 for (c=searchChars; *c!='\0'; c++) 4545 if(!(ignoreSpace && (*c==' ' || *c=='\t' || *c=='\n'))) 4546 if (BufGetCharacter(buf, pos) == *c) 4547 break; 4548 if(*c == 0) { 4549 *foundPos = pos; 4550 return True; 4551 } 4552 pos--; 4553 } 4554 *foundPos = 0; 4555 return False; 4556 } 4557 4558 /* 4559 ** Select the line containing the cursor, including the terminating newline, 4560 ** and move the cursor to its end. 4561 */ 4562 static void selectLine(Widget w) 4563 { 4564 textDisp *textD = ((TextWidget)w)->text.textD; 4565 int insertPos = TextDGetInsertPosition(textD); 4566 int endPos, startPos; 4567 4568 endPos = BufEndOfLine(textD->buffer, insertPos); 4569 startPos = BufStartOfLine(textD->buffer, insertPos); 4570 BufSelect(textD->buffer, startPos, min(endPos + 1, textD->buffer->length)); 4571 TextDSetInsertPosition(textD, endPos); 4572 } 4573 4574 /* 4575 ** Given a new mouse pointer location, pass the position on to the 4576 ** autoscroll timer routine, and make sure the timer is on when it's 4577 ** needed and off when it's not. 4578 */ 4579 static void checkAutoScroll(TextWidget w, int x, int y) 4580 { 4581 int inWindow; 4582 4583 /* Is the pointer in or out of the window? */ 4584 inWindow = x >= w->text.textD->left && 4585 x < w->core.width - w->text.marginWidth && 4586 y >= w->text.marginHeight && 4587 y < w->core.height - w->text.marginHeight; 4588 4589 /* If it's in the window, cancel the timer procedure */ 4590 if (inWindow) { 4591 if (w->text.autoScrollProcID != 0) 4592 XtRemoveTimeOut(w->text.autoScrollProcID);; 4593 w->text.autoScrollProcID = 0; 4594 return; 4595 } 4596 4597 /* If the timer is not already started, start it */ 4598 if (w->text.autoScrollProcID == 0) { 4599 w->text.autoScrollProcID = XtAppAddTimeOut( 4600 XtWidgetToApplicationContext((Widget)w), 4601 0, autoScrollTimerProc, w); 4602 } 4603 4604 /* Pass on the newest mouse location to the autoscroll routine */ 4605 w->text.mouseX = x; 4606 w->text.mouseY = y; 4607 } 4608 4609 /* 4610 ** Reset drag state and cancel the auto-scroll timer 4611 */ 4612 static void endDrag(Widget w) 4613 { 4614 if (((TextWidget)w)->text.autoScrollProcID != 0) 4615 XtRemoveTimeOut(((TextWidget)w)->text.autoScrollProcID); 4616 ((TextWidget)w)->text.autoScrollProcID = 0; 4617 if (((TextWidget)w)->text.dragState == MOUSE_PAN) 4618 XUngrabPointer(XtDisplay(w), CurrentTime); 4619 ((TextWidget)w)->text.dragState = NOT_CLICKED; 4620 } 4621 4622 /* 4623 ** Cancel any drag operation that might be in progress. Should be included 4624 ** in nearly every key event to cleanly end any dragging before edits are made 4625 ** which might change the insert position or the content of the buffer during 4626 ** a drag operation) 4627 */ 4628 static void cancelDrag(Widget w) 4629 { 4630 int dragState = ((TextWidget)w)->text.dragState; 4631 4632 if (((TextWidget)w)->text.autoScrollProcID != 0) 4633 XtRemoveTimeOut(((TextWidget)w)->text.autoScrollProcID); 4634 if (dragState == SECONDARY_DRAG || dragState == SECONDARY_RECT_DRAG) 4635 BufSecondaryUnselect(((TextWidget)w)->text.textD->buffer); 4636 if (dragState == PRIMARY_BLOCK_DRAG) 4637 CancelBlockDrag((TextWidget)w); 4638 if (dragState == MOUSE_PAN) 4639 XUngrabPointer(XtDisplay(w), CurrentTime); 4640 if (dragState != NOT_CLICKED) 4641 ((TextWidget)w)->text.dragState = DRAG_CANCELED; 4642 } 4643 4644 /* 4645 ** Do operations triggered by cursor movement: Call cursor movement callback 4646 ** procedure(s), and cancel marker indicating that the cursor is after one or 4647 ** more just-entered emulated tabs (spaces to be deleted as a unit). 4648 */ 4649 static void callCursorMovementCBs(Widget w, XEvent *event) 4650 { 4651 ((TextWidget)w)->text.emTabsBeforeCursor = 0; 4652 XtCallCallbacks((Widget)w, textNcursorMovementCallback, (XtPointer)event); 4653 } 4654 4655 /* 4656 ** Adjust the selection as the mouse is dragged to position: (x, y). 4657 */ 4658 static void adjustSelection(TextWidget tw, int x, int y) 4659 { 4660 textDisp *textD = tw->text.textD; 4661 textBuffer *buf = textD->buffer; 4662 int row, col, startCol, endCol, startPos, endPos; 4663 int newPos = TextDXYToPosition(textD, x, y); 4664 4665 /* Adjust the selection */ 4666 if (tw->text.dragState == PRIMARY_RECT_DRAG) { 4667 TextDXYToUnconstrainedPosition(textD, x, y, &row, &col); 4668 col = TextDOffsetWrappedColumn(textD, row, col); 4669 startCol = min(tw->text.rectAnchor, col); 4670 endCol = max(tw->text.rectAnchor, col); 4671 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos)); 4672 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos)); 4673 BufRectSelect(buf, startPos, endPos, startCol, endCol); 4674 } else if (tw->text.multiClickState == ONE_CLICK) { 4675 startPos = startOfWord(tw, min(tw->text.anchor, newPos)); 4676 endPos = endOfWord(tw, max(tw->text.anchor, newPos)); 4677 BufSelect(buf, startPos, endPos); 4678 newPos = newPos < tw->text.anchor ? startPos : endPos; 4679 } else if (tw->text.multiClickState == TWO_CLICKS) { 4680 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos)); 4681 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos)); 4682 BufSelect(buf, startPos, min(endPos+1, buf->length)); 4683 newPos = newPos < tw->text.anchor ? startPos : endPos; 4684 } else 4685 BufSelect(buf, tw->text.anchor, newPos); 4686 4687 /* Move the cursor */ 4688 TextDSetInsertPosition(textD, newPos); 4689 callCursorMovementCBs((Widget)tw, NULL); 4690 } 4691 4692 static void adjustSecondarySelection(TextWidget tw, int x, int y) 4693 { 4694 textDisp *textD = tw->text.textD; 4695 textBuffer *buf = textD->buffer; 4696 int row, col, startCol, endCol, startPos, endPos; 4697 int newPos = TextDXYToPosition(textD, x, y); 4698 4699 if (tw->text.dragState == SECONDARY_RECT_DRAG) { 4700 TextDXYToUnconstrainedPosition(textD, x, y, &row, &col); 4701 col = TextDOffsetWrappedColumn(textD, row, col); 4702 startCol = min(tw->text.rectAnchor, col); 4703 endCol = max(tw->text.rectAnchor, col); 4704 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos)); 4705 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos)); 4706 BufSecRectSelect(buf, startPos, endPos, startCol, endCol); 4707 } else 4708 BufSecondarySelect(textD->buffer, tw->text.anchor, newPos); 4709 } 4710 4711 /* 4712 ** Wrap multi-line text in argument "text" to be inserted at the end of the 4713 ** text on line "startLine" and return the result. If "breakBefore" is 4714 ** non-NULL, allow wrapping to extend back into "startLine", in which case 4715 ** the returned text will include the wrapped part of "startLine", and 4716 ** "breakBefore" will return the number of characters at the end of 4717 ** "startLine" that were absorbed into the returned string. "breakBefore" 4718 ** will return zero if no characters were absorbed into the returned string. 4719 ** The buffer offset of text in the widget's text buffer is needed so that 4720 ** smart indent (which can be triggered by wrapping) can search back farther 4721 ** in the buffer than just the text in startLine. 4722 */ 4723 static char *wrapText(TextWidget tw, char *startLine, char *text, int bufOffset, 4724 int wrapMargin, int *breakBefore) 4725 { 4726 textBuffer *wrapBuf, *buf = tw->text.textD->buffer; 4727 int startLineLen = strlen(startLine); 4728 int colNum, pos, lineStartPos, limitPos, breakAt, charsAdded; 4729 int firstBreak = -1, tabDist = buf->tabDist; 4730 char c, *wrappedText; 4731 4732 /* Create a temporary text buffer and load it with the strings */ 4733 wrapBuf = BufCreate(); 4734 BufInsert(wrapBuf, 0, startLine); 4735 BufInsert(wrapBuf, wrapBuf->length, text); 4736 4737 /* Scan the buffer for long lines and apply wrapLine when wrapMargin is 4738 exceeded. limitPos enforces no breaks in the "startLine" part of the 4739 string (if requested), and prevents re-scanning of long unbreakable 4740 lines for each character beyond the margin */ 4741 colNum = 0; 4742 pos = 0; 4743 lineStartPos = 0; 4744 limitPos = breakBefore == NULL ? startLineLen : 0; 4745 while (pos < wrapBuf->length) { 4746 c = BufGetCharacter(wrapBuf, pos); 4747 if (c == '\n') { 4748 lineStartPos = limitPos = pos + 1; 4749 colNum = 0; 4750 } else { 4751 colNum += BufCharWidth(c, colNum, tabDist, buf->nullSubsChar); 4752 if (colNum > wrapMargin) { 4753 if (!wrapLine(tw, wrapBuf, bufOffset, lineStartPos, pos, 4754 limitPos, &breakAt, &charsAdded)) { 4755 limitPos = max(pos, limitPos); 4756 } else { 4757 lineStartPos = limitPos = breakAt+1; 4758 pos += charsAdded; 4759 colNum = BufCountDispChars(wrapBuf, lineStartPos, pos+1); 4760 if (firstBreak == -1) 4761 firstBreak = breakAt; 4762 } 4763 } 4764 } 4765 pos++; 4766 } 4767 4768 /* Return the wrapped text, possibly including part of startLine */ 4769 if (breakBefore == NULL) 4770 wrappedText = BufGetRange(wrapBuf, startLineLen, wrapBuf->length); 4771 else { 4772 *breakBefore = firstBreak != -1 && firstBreak < startLineLen ? 4773 startLineLen - firstBreak : 0; 4774 wrappedText = BufGetRange(wrapBuf, startLineLen - *breakBefore, 4775 wrapBuf->length); 4776 } 4777 BufFree(wrapBuf); 4778 return wrappedText; 4779 } 4780 4781 /* 4782 ** Wraps the end of a line beginning at lineStartPos and ending at lineEndPos 4783 ** in "buf", at the last white-space on the line >= limitPos. (The implicit 4784 ** assumption is that just the last character of the line exceeds the wrap 4785 ** margin, and anywhere on the line we can wrap is correct). Returns False if 4786 ** unable to wrap the line. "breakAt", returns the character position at 4787 ** which the line was broken, 4788 ** 4789 ** Auto-wrapping can also trigger auto-indent. The additional parameter 4790 ** bufOffset is needed when auto-indent is set to smart indent and the smart 4791 ** indent routines need to scan far back in the buffer. "charsAdded" returns 4792 ** the number of characters added to acheive the auto-indent. wrapMargin is 4793 ** used to decide whether auto-indent should be skipped because the indent 4794 ** string itself would exceed the wrap margin. 4795 */ 4796 static int wrapLine(TextWidget tw, textBuffer *buf, int bufOffset, 4797 int lineStartPos, int lineEndPos, int limitPos, int *breakAt, 4798 int *charsAdded) 4799 { 4800 int p, length, column; 4801 char c, *indentStr; 4802 4803 /* Scan backward for whitespace or BOL. If BOL, return False, no 4804 whitespace in line at which to wrap */ 4805 for (p=lineEndPos; ; p--) { 4806 if (p < lineStartPos || p < limitPos) 4807 return False; 4808 c = BufGetCharacter(buf, p); 4809 if (c == '\t' || c == ' ') 4810 break; 4811 } 4812 4813 /* Create an auto-indent string to insert to do wrap. If the auto 4814 indent string reaches the wrap position, slice the auto-indent 4815 back off and return to the left margin */ 4816 if (tw->text.autoIndent || tw->text.smartIndent) { 4817 indentStr = createIndentString(tw, buf, bufOffset, lineStartPos, 4818 lineEndPos, &length, &column); 4819 if (column >= p-lineStartPos) 4820 indentStr[1] = '\0'; 4821 } else { 4822 indentStr = "\n"; 4823 length = 1; 4824 } 4825 4826 /* Replace the whitespace character with the auto-indent string 4827 and return the stats */ 4828 BufReplace(buf, p, p+1, indentStr); 4829 if (tw->text.autoIndent || tw->text.smartIndent) 4830 NEditFree(indentStr); 4831 *breakAt = p; 4832 *charsAdded = length-1; 4833 return True; 4834 } 4835 4836 /* 4837 ** Create and return an auto-indent string to add a newline at lineEndPos to a 4838 ** line starting at lineStartPos in buf. "buf" may or may not be the real 4839 ** text buffer for the widget. If it is not the widget's text buffer it's 4840 ** offset position from the real buffer must be specified in "bufOffset" to 4841 ** allow the smart-indent routines to scan back as far as necessary. The 4842 ** string length is returned in "length" (or "length" can be passed as NULL, 4843 ** and the indent column is returned in "column" (if non NULL). 4844 */ 4845 static char *createIndentString(TextWidget tw, textBuffer *buf, int bufOffset, 4846 int lineStartPos, int lineEndPos, int *length, int *column) 4847 { 4848 textDisp *textD = tw->text.textD; 4849 int pos, indent = -1, tabDist = textD->buffer->tabDist; 4850 int i, useTabs = textD->buffer->useTabs; 4851 char *indentPtr, *indentStr, c; 4852 smartIndentCBStruct smartIndent; 4853 4854 /* If smart indent is on, call the smart indent callback. It is not 4855 called when multi-line changes are being made (lineStartPos != 0), 4856 because smart indent needs to search back an indeterminate distance 4857 through the buffer, and reconciling that with wrapping changes made, 4858 but not yet committed in the buffer, would make programming smart 4859 indent more difficult for users and make everything more complicated */ 4860 if (tw->text.smartIndent && (lineStartPos == 0 || buf == textD->buffer)) { 4861 smartIndent.reason = NEWLINE_INDENT_NEEDED; 4862 smartIndent.pos = lineEndPos + bufOffset; 4863 smartIndent.indentRequest = 0; 4864 smartIndent.charsTyped = NULL; 4865 XtCallCallbacks((Widget)tw, textNsmartIndentCallback, 4866 (XtPointer)&smartIndent); 4867 indent = smartIndent.indentRequest; 4868 } 4869 4870 /* If smart indent wasn't used, measure the indent distance of the line */ 4871 if (indent == -1) { 4872 indent = 0; 4873 for (pos=lineStartPos; pos<lineEndPos; pos++) { 4874 c = BufGetCharacter(buf, pos); 4875 if (c != ' ' && c != '\t') 4876 break; 4877 if (c == '\t') 4878 indent += tabDist - (indent % tabDist); 4879 else 4880 indent++; 4881 } 4882 } 4883 4884 /* Allocate and create a string of tabs and spaces to achieve the indent */ 4885 indentPtr = indentStr = (char*)NEditMalloc(indent + 2); 4886 *indentPtr++ = '\n'; 4887 if (useTabs) { 4888 for (i=0; i < indent / tabDist; i++) 4889 *indentPtr++ = '\t'; 4890 for (i=0; i < indent % tabDist; i++) 4891 *indentPtr++ = ' '; 4892 } else { 4893 for (i=0; i < indent; i++) 4894 *indentPtr++ = ' '; 4895 } 4896 *indentPtr = '\0'; 4897 4898 /* Return any requested stats */ 4899 if (length != NULL) 4900 *length = indentPtr - indentStr; 4901 if (column != NULL) 4902 *column = indent; 4903 4904 return indentStr; 4905 } 4906 4907 /* 4908 ** Xt timer procedure for autoscrolling 4909 */ 4910 static void autoScrollTimerProc(XtPointer clientData, XtIntervalId *id) 4911 { 4912 TextWidget w = (TextWidget)clientData; 4913 textDisp *textD = w->text.textD; 4914 XftFont *xftFont = FontDefault(textD->font); 4915 int topLineNum, horizOffset, newPos, cursorX, y; 4916 int fontWidth = textD->font->maxWidth; 4917 int fontHeight = xftFont->ascent + xftFont->descent; 4918 4919 /* For vertical autoscrolling just dragging the mouse outside of the top 4920 or bottom of the window is sufficient, for horizontal (non-rectangular) 4921 scrolling, see if the position where the CURSOR would go is outside */ 4922 newPos = TextDXYToPosition(textD, w->text.mouseX, w->text.mouseY); 4923 if (w->text.dragState == PRIMARY_RECT_DRAG) 4924 cursorX = w->text.mouseX; 4925 else if (!TextDPositionToXY(textD, newPos, &cursorX, &y)) 4926 cursorX = w->text.mouseX; 4927 4928 /* Scroll away from the pointer, 1 character (horizontal), or 1 character 4929 for each fontHeight distance from the mouse to the text (vertical) */ 4930 TextDGetScroll(textD, &topLineNum, &horizOffset); 4931 if (cursorX >= (int)w->core.width - w->text.marginWidth) 4932 horizOffset += fontWidth; 4933 else if (w->text.mouseX < textD->left) 4934 horizOffset -= fontWidth; 4935 if (w->text.mouseY >= (int)w->core.height - w->text.marginHeight) 4936 topLineNum += 1 + ((w->text.mouseY - (int)w->core.height - 4937 w->text.marginHeight) / fontHeight) + 1; 4938 else if (w->text.mouseY < w->text.marginHeight) 4939 topLineNum -= 1 + ((w->text.marginHeight-w->text.mouseY) / fontHeight); 4940 TextDSetScroll(textD, topLineNum, horizOffset); 4941 4942 /* Continue the drag operation in progress. If none is in progress 4943 (safety check) don't continue to re-establish the timer proc */ 4944 if (w->text.dragState == PRIMARY_DRAG) { 4945 adjustSelection(w, w->text.mouseX, w->text.mouseY); 4946 } else if (w->text.dragState == PRIMARY_RECT_DRAG) { 4947 adjustSelection(w, w->text.mouseX, w->text.mouseY); 4948 } else if (w->text.dragState == SECONDARY_DRAG) { 4949 adjustSecondarySelection(w, w->text.mouseX, w->text.mouseY); 4950 } else if (w->text.dragState == SECONDARY_RECT_DRAG) { 4951 adjustSecondarySelection(w, w->text.mouseX, w->text.mouseY); 4952 } else if (w->text.dragState == PRIMARY_BLOCK_DRAG) { 4953 BlockDragSelection(w, w->text.mouseX, w->text.mouseY, USE_LAST); 4954 } else { 4955 w->text.autoScrollProcID = 0; 4956 return; 4957 } 4958 4959 /* re-establish the timer proc (this routine) to continue processing */ 4960 w->text.autoScrollProcID = XtAppAddTimeOut( 4961 XtWidgetToApplicationContext((Widget)w), 4962 w->text.mouseY >= w->text.marginHeight && 4963 w->text.mouseY < w->core.height - w->text.marginHeight ? 4964 (VERTICAL_SCROLL_DELAY*fontWidth) / fontHeight : 4965 VERTICAL_SCROLL_DELAY, autoScrollTimerProc, w); 4966 } 4967 4968 /* 4969 ** Xt timer procedure for cursor blinking 4970 */ 4971 static void cursorBlinkTimerProc(XtPointer clientData, XtIntervalId *id) 4972 { 4973 TextWidget w = (TextWidget)clientData; 4974 textDisp *textD = w->text.textD; 4975 4976 /* Blink the cursor */ 4977 if (textD->cursorOn) 4978 TextDBlankCursor(textD); 4979 else 4980 TextDUnblankCursor(textD); 4981 4982 /* re-establish the timer proc (this routine) to continue processing */ 4983 w->text.cursorBlinkProcID = XtAppAddTimeOut( 4984 XtWidgetToApplicationContext((Widget)w), 4985 w->text.cursorBlinkRate, cursorBlinkTimerProc, w); 4986 } 4987 4988 /* 4989 ** Sets the caret to on or off and restart the caret blink timer. 4990 ** This could be used by other modules to modify the caret's blinking. 4991 */ 4992 void ResetCursorBlink(TextWidget textWidget, Boolean startsBlanked) 4993 { 4994 if (0 != textWidget->text.cursorBlinkRate) 4995 { 4996 if (0 != textWidget->text.cursorBlinkProcID) 4997 { 4998 XtRemoveTimeOut(textWidget->text.cursorBlinkProcID); 4999 } 5000 5001 if (startsBlanked) 5002 { 5003 TextDBlankCursor(textWidget->text.textD); 5004 } else 5005 { 5006 TextDUnblankCursor(textWidget->text.textD); 5007 } 5008 5009 textWidget->text.cursorBlinkProcID 5010 = XtAppAddTimeOut(XtWidgetToApplicationContext((Widget) textWidget), 5011 textWidget->text.cursorBlinkRate, cursorBlinkTimerProc, 5012 textWidget); 5013 } 5014 } 5015 5016 XftColor TextGetFGColor(Widget w) 5017 { 5018 TextWidget textWidget = (TextWidget)w; 5019 return textWidget->text.textD->colorProfile->textFgColor; 5020 } 5021 5022 XftColor TextGetBGColor(Widget w) 5023 { 5024 TextWidget textWidget = (TextWidget)w; 5025 return textWidget->text.textD->colorProfile->textBgColor; 5026 } 5027 5028 int TextLookupString( 5029 Widget w, 5030 XKeyPressedEvent *event, 5031 char* buffer_return, 5032 int bytes_buffer, 5033 KeySym* keysym_return, 5034 Status* status) 5035 { 5036 TextWidget textWidget = (TextWidget)w; 5037 5038 // maybe it is enough to just check event->serial 5039 if (event->serial == textWidget->text.last_keyevent_serial && 5040 event->keycode == textWidget->text.last_keyevent_keycode && 5041 event->time == textWidget->text.last_keyevent_time) 5042 { 5043 // return cached values 5044 if(textWidget->text.xim_lookup_nchars > bytes_buffer) { 5045 *status = XBufferOverflow; 5046 return 0; 5047 } 5048 5049 memcpy(buffer_return, textWidget->text.xim_lookup_cache, textWidget->text.xim_lookup_nchars); 5050 *status = textWidget->text.xim_lookup_status; 5051 *keysym_return = textWidget->text.xim_lookup_keysym; 5052 return textWidget->text.xim_lookup_nchars; 5053 } 5054 5055 int nChars; 5056 5057 #ifdef NO_XMIM 5058 static XComposeStatus compose = {NULL, 0}; 5059 nChars = XLookupString(event, buffer_return, bytes_buffer, keysym_return, &compose); 5060 *status = 0; 5061 #else 5062 if(textWidget->text.xic) { 5063 #ifdef X_HAVE_UTF8_STRING 5064 nChars = Xutf8LookupString(textWidget->text.xic, event, buffer_return, bytes_buffer, keysym_return, status); 5065 #else 5066 nChars = XmbLookupString(textWidget->text.xic, event, buffer_return, bytes_buffer, keysym_return, status); 5067 #endif 5068 } else { 5069 nChars = XmImMbLookupString(w, event, buffer_return, bytes_buffer, keysym_return, status); 5070 } 5071 #endif 5072 5073 // cache result 5074 int n = nChars; 5075 if(nChars > TEXTWIDGET_XIM_LOOKUP_BUFSIZE) { 5076 fprintf(stderr, "%s", "Warning: XIM LookupString too large\n"); 5077 n = TEXTWIDGET_XIM_LOOKUP_BUFSIZE; 5078 } 5079 memcpy(textWidget->text.xim_lookup_cache, buffer_return, n); 5080 textWidget->text.xim_lookup_keysym = *keysym_return; 5081 textWidget->text.xim_lookup_status = *status; 5082 textWidget->text.xim_lookup_nchars = n; 5083 5084 textWidget->text.last_keyevent_serial = event->serial; 5085 textWidget->text.last_keyevent_keycode = event->keycode; 5086 textWidget->text.last_keyevent_time = event->time; 5087 5088 return nChars; 5089 } 5090 5091 /* 5092 ** look at an action procedure's arguments to see if argument "key" has been 5093 ** specified in the argument list 5094 */ 5095 static int hasKey(const char *key, const String *args, const Cardinal *nArgs) 5096 { 5097 int i; 5098 5099 for (i=0; i<(int)*nArgs; i++) 5100 if (!strcasecmp(args[i], key)) 5101 return True; 5102 return False; 5103 } 5104 5105 static int max(int i1, int i2) 5106 { 5107 return i1 >= i2 ? i1 : i2; 5108 } 5109 5110 static int min(int i1, int i2) 5111 { 5112 return i1 <= i2 ? i1 : i2; 5113 } 5114 5115 static void ringIfNecessary(Boolean silent, Widget w) 5116 { 5117 if (!silent) 5118 XBell(XtDisplay(w), 0); 5119 } 5120