Annotation of src/usr.bin/vim/gui_x11.c, Revision 1.1.1.1
1.1 downsj 1: /* $OpenBSD$ */
2: /* vi:set ts=4 sw=4:
3: *
4: * VIM - Vi IMproved by Bram Moolenaar
5: * GUI/Motif support by Robert Webb
6: *
7: * Do ":help uganda" in Vim to read copying and usage conditions.
8: * Do ":help credits" in Vim to see a list of people who contributed.
9: */
10:
11: #include <X11/keysym.h>
12: #include <X11/Xatom.h>
13: #include <X11/StringDefs.h>
14: #include <X11/Intrinsic.h>
15: #include <X11/Shell.h>
16:
17: #include "vim.h"
18: #include "globals.h"
19: #include "proto.h"
20: #include "option.h"
21: #include "ops.h"
22:
23: #define VIM_NAME "vim"
24: #define VIM_CLASS "Vim"
25:
26: /* Default resource values */
27: #define DFLT_FONT "7x13"
28: #define DFLT_MENU_BG_COLOR "gray77"
29: #define DFLT_MENU_FG_COLOR "black"
30: #define DFLT_SCROLL_BG_COLOR "gray60"
31: #define DFLT_SCROLL_FG_COLOR "gray77"
32:
33: Widget vimShell = (Widget)NULL;
34:
35: static XtAppContext app_context;
36: static Atom Atom_WM_DELETE_WINDOW;
37:
38: static Pixel gui_x11_get_color __ARGS((char_u *));
39: static void gui_x11_set_color __ARGS((GC, Pixel));
40: static void gui_x11_set_font __ARGS((GC, Font));
41: static void gui_x11_check_copy_area __ARGS((void));
42: static void gui_x11_update_menus_recurse __ARGS((GuiMenu *, int));
43:
44: static void gui_x11_request_selection_cb __ARGS((Widget, XtPointer,
45: Atom *, Atom *, XtPointer,
46: long_u *, int *));
47:
48: static Boolean gui_x11_convert_selection_cb __ARGS((Widget, Atom *, Atom *,
49: Atom *, XtPointer *,
50: long_u *, int *));
51:
52: static void gui_x11_lose_ownership_cb __ARGS((Widget, Atom *));
53:
54: static void gui_x11_wm_protocol_handler __ARGS((Widget, XtPointer,
55: XEvent *, Boolean *));
56:
57: static void gui_x11_invert_area __ARGS((int, int, int, int));
58: static void gui_x11_yank_selection __ARGS((int, int, int, int));
59: static void gui_x11_get_word_boundaries __ARGS((GuiSelection *, int, int));
60: static int gui_x11_get_line_end __ARGS((int));
61: static void gui_x11_update_selection __ARGS((GuiSelection *, int, int, int,
62: int));
63:
64: #define char_class(c) (c <= ' ' ? ' ' : iswordchar(c))
65:
66: #define invert_rectangle(r, c, nr, nc) \
67: XFillRectangle(gui.dpy, gui.wid, gui.invert_gc, \
68: FILL_X(c), FILL_Y(r), (nc) * gui.char_width, \
69: (nr) * gui.char_height)
70:
71:
72: static struct
73: {
74: KeySym key_sym;
75: char_u vim_code0;
76: char_u vim_code1;
77: } special_keys[] =
78: {
79: {XK_Up, 'k', 'u'},
80: {XK_Down, 'k', 'd'},
81: {XK_Left, 'k', 'l'},
82: {XK_Right, 'k', 'r'},
83:
84: {XK_F1, 'k', '1'},
85: {XK_F2, 'k', '2'},
86: {XK_F3, 'k', '3'},
87: {XK_F4, 'k', '4'},
88: {XK_F5, 'k', '5'},
89: {XK_F6, 'k', '6'},
90: {XK_F7, 'k', '7'},
91: {XK_F8, 'k', '8'},
92: {XK_F9, 'k', '9'},
93: {XK_F10, 'k', ';'},
94:
95: {XK_F11, 'F', '1'},
96: {XK_F12, 'F', '2'},
97: {XK_F13, 'F', '3'},
98: {XK_F14, 'F', '4'},
99: {XK_F15, 'F', '5'},
100: {XK_F16, 'F', '6'},
101: {XK_F17, 'F', '7'},
102: {XK_F18, 'F', '8'},
103: {XK_F19, 'F', '9'},
104: {XK_F20, 'F', 'A'},
105:
106: {XK_F21, 'F', 'B'},
107: {XK_F22, 'F', 'C'},
108: {XK_F23, 'F', 'D'},
109: {XK_F24, 'F', 'E'},
110: {XK_F25, 'F', 'F'},
111: {XK_F26, 'F', 'G'},
112: {XK_F27, 'F', 'H'},
113: {XK_F28, 'F', 'I'},
114: {XK_F29, 'F', 'J'},
115: {XK_F30, 'F', 'K'},
116:
117: {XK_F31, 'F', 'L'},
118: {XK_F32, 'F', 'M'},
119: {XK_F33, 'F', 'N'},
120: {XK_F34, 'F', 'O'},
121: {XK_F35, 'F', 'P'}, /* keysymdef.h defines up to F35 */
122:
123: {XK_Help, '%', '1'},
124: {XK_Undo, '&', '8'},
125: {XK_BackSpace, 'k', 'b'},
126: {XK_Insert, 'k', 'I'},
127: {XK_Delete, 'k', 'D'},
128: {XK_Home, 'k', 'h'},
129: {XK_End, '@', '7'},
130: {XK_Prior, 'k', 'P'},
131: {XK_Next, 'k', 'N'},
132: {XK_Print, '%', '9'},
133:
134: /* Keypad keys: */
135: #ifdef XK_KP_Left
136: {XK_KP_Left, 'k', 'l'},
137: {XK_KP_Right, 'k', 'r'},
138: {XK_KP_Up, 'k', 'u'},
139: {XK_KP_Down, 'k', 'd'},
140: {XK_KP_Insert, 'k', 'I'},
141: {XK_KP_Delete, 'k', 'D'},
142: {XK_KP_Home, 'k', 'h'},
143: {XK_KP_End, '@', '7'},
144: {XK_KP_Prior, 'k', 'P'},
145: {XK_KP_Next, 'k', 'N'},
146: #endif
147:
148: /* End of list marker: */
149: {(KeySym)0, 0, 0}
150: };
151:
152: #define XtNboldColor "boldColor"
153: #define XtCBoldColor "BoldColor"
154: #define XtNitalicColor "italicColor"
155: #define XtCItalicColor "ItalicColor"
156: #define XtNunderlineColor "underlineColor"
157: #define XtCUnderlineColor "UnderlineColor"
158: #define XtNcursorColor "cursorColor"
159: #define XtCCursorColor "CursorColor"
160: #define XtNboldFont "boldFont"
161: #define XtCBoldFont "BoldFont"
162: #define XtNitalicFont "italicFont"
163: #define XtCItalicFont "ItalicFont"
164: #define XtNboldItalicFont "boldItalicFont"
165: #define XtCBoldItalicFont "BoldItalicFont"
166: #define XtNscrollbarWidth "scrollbarWidth"
167: #define XtCScrollbarWidth "ScrollbarWidth"
168: #define XtNmenuHeight "menuHeight"
169: #define XtCMenuHeight "MenuHeight"
170:
171: /* Resources for setting the foreground and background colors of menus */
172: #define XtNmenuBackground "menuBackground"
173: #define XtCMenuBackground "MenuBackground"
174: #define XtNmenuForeground "menuForeground"
175: #define XtCMenuForeground "MenuForeground"
176:
177: /* Resources for setting the foreground and background colors of scrollbars */
178: #define XtNscrollBackground "scrollBackground"
179: #define XtCScrollBackground "ScrollBackground"
180: #define XtNscrollForeground "scrollForeground"
181: #define XtCScrollForeground "ScrollForeground"
182:
183: /*
184: * X Resources:
185: */
186: static XtResource vim_resources[] =
187: {
188: {
189: XtNforeground,
190: XtCForeground,
191: XtRPixel,
192: sizeof(Pixel),
193: XtOffsetOf(Gui, norm_pixel),
194: XtRString,
195: XtDefaultForeground
196: },
197: {
198: XtNbackground,
199: XtCBackground,
200: XtRPixel,
201: sizeof(Pixel),
202: XtOffsetOf(Gui, back_pixel),
203: XtRString,
204: XtDefaultBackground
205: },
206: {
207: XtNboldColor,
208: XtCBoldColor,
209: XtRPixel,
210: sizeof(Pixel),
211: XtOffsetOf(Gui, bold_pixel),
212: XtRString,
213: XtDefaultForeground
214: },
215: {
216: XtNitalicColor,
217: XtCItalicColor,
218: XtRPixel,
219: sizeof(Pixel),
220: XtOffsetOf(Gui, ital_pixel),
221: XtRString,
222: XtDefaultForeground
223: },
224: {
225: XtNunderlineColor,
226: XtCUnderlineColor,
227: XtRPixel,
228: sizeof(Pixel),
229: XtOffsetOf(Gui, underline_pixel),
230: XtRString,
231: XtDefaultForeground
232: },
233: {
234: XtNcursorColor,
235: XtCCursorColor,
236: XtRPixel,
237: sizeof(Pixel),
238: XtOffsetOf(Gui, cursor_pixel),
239: XtRString,
240: XtDefaultForeground
241: },
242: {
243: XtNfont,
244: XtCFont,
245: XtRString,
246: sizeof(String *),
247: XtOffsetOf(Gui, dflt_font),
248: XtRImmediate,
249: XtDefaultFont
250: },
251: {
252: XtNboldFont,
253: XtCBoldFont,
254: XtRString,
255: sizeof(String *),
256: XtOffsetOf(Gui, dflt_bold_fn),
257: XtRImmediate,
258: ""
259: },
260: {
261: XtNitalicFont,
262: XtCItalicFont,
263: XtRString,
264: sizeof(String *),
265: XtOffsetOf(Gui, dflt_ital_fn),
266: XtRImmediate,
267: ""
268: },
269: {
270: XtNboldItalicFont,
271: XtCBoldItalicFont,
272: XtRString,
273: sizeof(String *),
274: XtOffsetOf(Gui, dflt_boldital_fn),
275: XtRImmediate,
276: ""
277: },
278: {
279: XtNgeometry,
280: XtCGeometry,
281: XtRString,
282: sizeof(String *),
283: XtOffsetOf(Gui, geom),
284: XtRImmediate,
285: ""
286: },
287: {
288: XtNreverseVideo,
289: XtCReverseVideo,
290: XtRBool,
291: sizeof(Bool),
292: XtOffsetOf(Gui, rev_video),
293: XtRImmediate,
294: (XtPointer) False
295: },
296: {
297: XtNborderWidth,
298: XtCBorderWidth,
299: XtRInt,
300: sizeof(int),
301: XtOffsetOf(Gui, border_width),
302: XtRImmediate,
303: (XtPointer) 2
304: },
305: {
306: XtNscrollbarWidth,
307: XtCScrollbarWidth,
308: XtRInt,
309: sizeof(int),
310: XtOffsetOf(Gui, scrollbar_width),
311: XtRImmediate,
312: (XtPointer) SB_DEFAULT_WIDTH
313: },
314: {
315: XtNmenuHeight,
316: XtCMenuHeight,
317: XtRInt,
318: sizeof(int),
319: XtOffsetOf(Gui, menu_height),
320: XtRImmediate,
321: (XtPointer) MENU_DEFAULT_HEIGHT
322: },
323: {
324: XtNmenuForeground,
325: XtCMenuForeground,
326: XtRPixel,
327: sizeof(Pixel),
328: XtOffsetOf(Gui, menu_fg_pixel),
329: XtRString,
330: DFLT_MENU_FG_COLOR
331: },
332: {
333: XtNmenuBackground,
334: XtCMenuBackground,
335: XtRPixel,
336: sizeof(Pixel),
337: XtOffsetOf(Gui, menu_bg_pixel),
338: XtRString,
339: DFLT_MENU_BG_COLOR
340: },
341: {
342: XtNscrollForeground,
343: XtCScrollForeground,
344: XtRPixel,
345: sizeof(Pixel),
346: XtOffsetOf(Gui, scroll_fg_pixel),
347: XtRString,
348: DFLT_SCROLL_FG_COLOR
349: },
350: {
351: XtNscrollBackground,
352: XtCScrollBackground,
353: XtRPixel,
354: sizeof(Pixel),
355: XtOffsetOf(Gui, scroll_bg_pixel),
356: XtRString,
357: DFLT_SCROLL_BG_COLOR
358: },
359: };
360:
361: /*
362: * This table holds all the X GUI command line options allowed. This includes
363: * the standard ones so that we can skip them when vim is started without the
364: * GUI (but the GUI might start up later).
365: * When changing this, also update doc/vim_gui.txt and the usage message!!!
366: */
367: static XrmOptionDescRec cmdline_options[] =
368: {
369: /* We handle these options ourselves */
370: {"-bg", ".background", XrmoptionSepArg, NULL},
371: {"-background", ".background", XrmoptionSepArg, NULL},
372: {"-fg", ".foreground", XrmoptionSepArg, NULL},
373: {"-foreground", ".foreground", XrmoptionSepArg, NULL},
374: {"-bold", ".boldColor", XrmoptionSepArg, NULL},
375: {"-italic", ".italicColor", XrmoptionSepArg, NULL},
376: {"-ul", ".underlineColor", XrmoptionSepArg, NULL},
377: {"-underline", ".underlineColor", XrmoptionSepArg, NULL},
378: {"-cursor", ".cursorColor", XrmoptionSepArg, NULL},
379: {"-fn", ".font", XrmoptionSepArg, NULL},
380: {"-font", ".font", XrmoptionSepArg, NULL},
381: {"-boldfont", ".boldFont", XrmoptionSepArg, NULL},
382: {"-italicfont", ".italicFont", XrmoptionSepArg, NULL},
383: {"-geom", ".geometry", XrmoptionSepArg, NULL},
384: {"-geometry", ".geometry", XrmoptionSepArg, NULL},
385: {"-reverse", "*reverseVideo", XrmoptionNoArg, "True"},
386: {"-rv", "*reverseVideo", XrmoptionNoArg, "True"},
387: {"+reverse", "*reverseVideo", XrmoptionNoArg, "False"},
388: {"+rv", "*reverseVideo", XrmoptionNoArg, "False"},
389: {"-display", ".display", XrmoptionSepArg, NULL},
390: {"-iconic", "*iconic", XrmoptionNoArg, "True"},
391: {"-name", ".name", XrmoptionSepArg, NULL},
392: {"-bw", ".borderWidth", XrmoptionSepArg, NULL},
393: {"-borderwidth", ".borderWidth", XrmoptionSepArg, NULL},
394: {"-sw", ".scrollbarWidth", XrmoptionSepArg, NULL},
395: {"-scrollbarwidth", ".scrollbarWidth", XrmoptionSepArg, NULL},
396: {"-mh", ".menuHeight", XrmoptionSepArg, NULL},
397: {"-menuheight", ".menuHeight", XrmoptionSepArg, NULL},
398: {"-xrm", NULL, XrmoptionResArg, NULL}
399: };
400:
401: static int gui_argc = 0;
402: static char **gui_argv = NULL;
403:
404: /*
405: * Call-back routines.
406: */
407:
408: void
409: gui_x11_timer_cb(timed_out, interval_id)
410: XtPointer timed_out;
411: XtIntervalId *interval_id;
412: {
413: *((int *)timed_out) = TRUE;
414: }
415:
416: void
417: gui_x11_visibility_cb(w, dud, event, bool)
418: Widget w;
419: XtPointer dud;
420: XEvent *event;
421: Boolean *bool;
422: {
423: if (event->type != VisibilityNotify)
424: return;
425:
426: gui.visibility = event->xvisibility.state;
427:
428: /*
429: * When we do an XCopyArea(), and the window is partially obscured, we want
430: * to receive an event to tell us whether it worked or not.
431: */
432: XSetGraphicsExposures(gui.dpy, gui.text_gc,
433: gui.visibility != VisibilityUnobscured);
434: }
435:
436: void
437: gui_x11_expose_cb(w, dud, event, bool)
438: Widget w;
439: XtPointer dud;
440: XEvent *event;
441: Boolean *bool;
442: {
443: XExposeEvent *gevent;
444:
445: if (event->type != Expose)
446: return;
447:
448: gevent = (XExposeEvent *)event;
449: gui_redraw(gevent->x, gevent->y, gevent->width, gevent->height);
450:
451: /* Clear the border areas if needed */
452: if (gevent->x < FILL_X(0))
453: XClearArea(gui.dpy, gui.wid, 0, 0, FILL_X(0), 0, False);
454: if (gevent->y < FILL_Y(0))
455: XClearArea(gui.dpy, gui.wid, 0, 0, 0, FILL_Y(0), False);
456: if (gevent->x > FILL_X(Columns))
457: XClearArea(gui.dpy, gui.wid, FILL_X(Columns), 0, 0, 0, False);
458: if (gevent->y > FILL_Y(Rows))
459: XClearArea(gui.dpy, gui.wid, 0, FILL_Y(Rows), 0, 0, False);
460:
461: if (gui.selection.state != SELECT_CLEARED)
462: gui_x11_redraw_selection(gevent->x, gevent->y, gevent->width,
463: gevent->height);
464: }
465:
466: void
467: gui_x11_resize_window_cb(w, dud, event, bool)
468: Widget w;
469: XtPointer dud;
470: XEvent *event;
471: Boolean *bool;
472: {
473: if (event->type != ConfigureNotify)
474: return;
475:
476: gui_resize_window(event->xconfigure.width, event->xconfigure.height);
477:
478: /* Make sure the border strips on the right and bottom get cleared. */
479: XClearArea(gui.dpy, gui.wid, FILL_X(Columns), 0, 0, 0, False);
480: XClearArea(gui.dpy, gui.wid, 0, FILL_Y(Rows), 0, 0, False);
481: }
482:
483: void
484: gui_x11_focus_change_cb(w, data, event, bool)
485: Widget w;
486: XtPointer data;
487: XEvent *event;
488: Boolean *bool;
489: {
490: if (event->type == FocusIn)
491: gui.in_focus = TRUE;
492: else
493: gui.in_focus = FALSE;
494: if (gui.row == gui.cursor_row && gui.col == gui.cursor_col)
495: INVALIDATE_CURSOR();
496: gui_update_cursor();
497: }
498:
499: void
500: gui_x11_key_hit_cb(w, dud, event, bool)
501: Widget w;
502: XtPointer dud;
503: XEvent *event;
504: Boolean *bool;
505: {
506: XKeyPressedEvent *ev_press;
507: char_u string[3], string2[3];
508: KeySym key_sym;
509: int num, i;
510:
511: ev_press = (XKeyPressedEvent *)event;
512:
513: num = XLookupString(ev_press, (char *)string, sizeof(string),
514: &key_sym, NULL);
515:
516: if (key_sym == XK_space)
517: string[0] = ' '; /* Otherwise Ctrl-Space doesn't work */
518:
519: /* Check for Alt/Meta key (Mod1Mask) */
520: if (num == 1 && (ev_press->state & Mod1Mask))
521: {
522: /*
523: * Before we set the 8th bit, check to make sure the user doesn't
524: * already have a mapping defined for this sequence. We determine this
525: * by checking to see if the input would be the same without the
526: * Alt/Meta key.
527: */
528: ev_press->state &= ~Mod1Mask;
529: if (XLookupString(ev_press, (char *)string2, sizeof(string2),
530: &key_sym, NULL) == 1 && string[0] == string2[0])
531: string[0] |= 0x80;
532: ev_press->state |= Mod1Mask;
533: }
534:
535: #if 0
536: if (num == 1 && string[0] == CSI) /* this doesn't work yet */
537: {
538: string[1] = CSI;
539: string[2] = CSI;
540: num = 3;
541: }
542: #endif
543:
544: /* Check for special keys, making sure BS and DEL are recognised. */
545: if (num == 0 || key_sym == XK_BackSpace || key_sym == XK_Delete)
546: {
547: for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
548: {
549: if (special_keys[i].key_sym == key_sym)
550: {
551: string[0] = CSI;
552: string[1] = special_keys[i].vim_code0;
553: string[2] = special_keys[i].vim_code1;
554: num = 3;
555: }
556: }
557: }
558:
559: /* Unrecognised key */
560: if (num == 0)
561: return;
562:
563: /* Special keys (and a few others) may have modifiers */
564: if (num == 3 || key_sym == XK_space || key_sym == XK_Tab
565: || key_sym == XK_Return || key_sym == XK_Linefeed
566: || key_sym == XK_Escape)
567: {
568: string2[0] = CSI;
569: string2[1] = KS_MODIFIER;
570: string2[2] = 0;
571: if (ev_press->state & ShiftMask)
572: string2[2] |= MOD_MASK_SHIFT;
573: if (ev_press->state & ControlMask)
574: string2[2] |= MOD_MASK_CTRL;
575: if (ev_press->state & Mod1Mask)
576: string2[2] |= MOD_MASK_ALT;
577: if (string2[2] != 0)
578: add_to_input_buf(string2, 3);
579: }
580: if (num == 1 && string[0] == Ctrl('C'))
581: {
582: trash_input_buf();
583: got_int = TRUE;
584: }
585: add_to_input_buf(string, num);
586: }
587:
588: void
589: gui_x11_mouse_cb(w, dud, event, bool)
590: Widget w;
591: XtPointer dud;
592: XEvent *event;
593: Boolean *bool;
594: {
595: static XtIntervalId timer = (XtIntervalId)0;
596: static int timed_out = TRUE;
597:
598: int button;
599: int repeated_click = FALSE;
600: int x, y;
601: int_u x_modifiers;
602: int_u vim_modifiers;
603: int checkfor;
604:
605: if (event->type == MotionNotify)
606: {
607: x = event->xmotion.x;
608: y = event->xmotion.y;
609: button = MOUSE_DRAG;
610: x_modifiers = event->xmotion.state;
611: }
612: else
613: {
614: x = event->xbutton.x;
615: y = event->xbutton.y;
616: if (event->type == ButtonPress)
617: {
618: /* Handle multiple clicks */
619: if (!timed_out)
620: {
621: XtRemoveTimeOut(timer);
622: repeated_click = TRUE;
623: }
624: timed_out = FALSE;
625: timer = XtAppAddTimeOut(app_context, (long_u)p_mouset,
626: gui_x11_timer_cb, &timed_out);
627: switch (event->xbutton.button)
628: {
629: case Button1: button = MOUSE_LEFT; break;
630: case Button2: button = MOUSE_MIDDLE; break;
631: case Button3: button = MOUSE_RIGHT; break;
632: default:
633: return; /* Unknown button */
634: }
635: }
636: else if (event->type == ButtonRelease)
637: button = MOUSE_RELEASE;
638: else
639: return; /* Unknown mouse event type */
640:
641: x_modifiers = event->xbutton.state;
642: }
643:
644: vim_modifiers = 0x0;
645: if (x_modifiers & ShiftMask)
646: vim_modifiers |= MOUSE_SHIFT;
647: if (x_modifiers & ControlMask)
648: vim_modifiers |= MOUSE_CTRL;
649: if (x_modifiers & Mod1Mask) /* Alt or Meta key */
650: vim_modifiers |= MOUSE_ALT;
651:
652: /* If an x11 selection is in progress, finish it */
653: if (gui.selection.state == SELECT_IN_PROGRESS)
654: {
655: gui_x11_process_selection(button, x, y, repeated_click, vim_modifiers);
656: return;
657: }
658:
659: /* Determine which mouse settings to look for based on the current mode */
660: switch (State)
661: {
662: case NORMAL_BUSY:
663: case NORMAL: checkfor = MOUSE_NORMAL; break;
664: case VISUAL: checkfor = MOUSE_VISUAL; break;
665: case REPLACE:
666: case INSERT: checkfor = MOUSE_INSERT; break;
667: case HITRETURN: checkfor = MOUSE_RETURN; break;
668:
669: /*
670: * On the command line, use the X11 selection on all lines but the
671: * command line.
672: */
673: case CMDLINE:
674: if (Y_2_ROW(y) < cmdline_row)
675: checkfor = ' ';
676: else
677: checkfor = MOUSE_COMMAND;
678: break;
679:
680: default:
681: checkfor = ' ';
682: break;
683: };
684: /*
685: * Allow selection of text in the command line in "normal" modes.
686: */
687: if ((State == NORMAL || State == NORMAL_BUSY ||
688: State == INSERT || State == REPLACE) &&
689: Y_2_ROW(y) >= gui.num_rows - p_ch)
690: checkfor = ' ';
691:
692: /*
693: * If the mouse settings say to not use the mouse, use the X11 selection.
694: * But if Visual is active, assume that only the Visual area will be
695: * selected.
696: */
697: if (!mouse_has(checkfor) && !VIsual_active)
698: {
699: /* If the selection is done, allow the right button to extend it */
700: if (gui.selection.state == SELECT_DONE && button == MOUSE_RIGHT)
701: {
702: gui_x11_process_selection(button, x, y, repeated_click,
703: vim_modifiers);
704: return;
705: }
706:
707: /* Allow the left button to start the selection */
708: else if (button == MOUSE_LEFT)
709: {
710: gui_x11_start_selection(button, x, y, repeated_click,
711: vim_modifiers);
712: return;
713: }
714: }
715:
716: if (gui.selection.state != SELECT_CLEARED)
717: gui_mch_clear_selection();
718: gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
719: }
720:
721: /*
722: * End of call-back routines
723: */
724:
725: /*
726: * Parse the GUI related command-line arguments. Any arguments used are
727: * deleted from argv, and *argc is decremented accordingly. This is called
728: * when vim is started, whether or not the GUI has been started.
729: */
730: void
731: gui_mch_prepare(argc, argv)
732: int *argc;
733: char **argv;
734: {
735: int arg;
736: int i;
737:
738: /*
739: * Move all the entries in argv which are relevant to X into gui_argv.
740: */
741: gui_argc = 0;
742: gui_argv = (char **)lalloc(*argc * sizeof(char *), FALSE);
743: if (gui_argv == NULL)
744: return;
745: gui_argv[gui_argc++] = argv[0];
746: arg = 1;
747: while (arg < *argc)
748: {
749: /* Look for argv[arg] in cmdline_options[] table */
750: for (i = 0; i < XtNumber(cmdline_options); i++)
751: if (strcmp(argv[arg], cmdline_options[i].option) == 0)
752: break;
753:
754: if (i < XtNumber(cmdline_options))
755: {
756: /* Found match in table, so move it into gui_argv */
757: gui_argv[gui_argc++] = argv[arg];
758: if (--*argc > arg)
759: {
760: vim_memmove(&argv[arg], &argv[arg + 1], (*argc - arg)
761: * sizeof(char *));
762: if (cmdline_options[i].argKind != XrmoptionNoArg)
763: {
764: /* Move the options argument as well */
765: gui_argv[gui_argc++] = argv[arg];
766: if (--*argc > arg)
767: vim_memmove(&argv[arg], &argv[arg + 1], (*argc - arg)
768: * sizeof(char *));
769: }
770: }
771: }
772: else
773: arg++;
774: }
775: }
776:
777: /*
778: * Initialise the X GUI. Create all the windows, set up all the call-backs
779: * etc.
780: */
781: int
782: gui_mch_init()
783: {
784: Widget AppShell;
785: long_u gc_mask;
786: XGCValues gc_vals;
787: Pixel tmp_pixel;
788: int x, y, mask;
789: unsigned w, h;
790:
791: XtToolkitInitialize();
792: app_context = XtCreateApplicationContext();
793: gui.dpy = XtOpenDisplay(app_context, 0, VIM_NAME, VIM_CLASS,
794: cmdline_options, XtNumber(cmdline_options),
795: #ifndef XtSpecificationRelease
796: (Cardinal*)&gui_argc, gui_argv);
797: #else
798: #if XtSpecificationRelease == 4
799: (Cardinal*)&gui_argc, gui_argv);
800: #else
801: &gui_argc, gui_argv);
802: #endif
803: #endif
804:
805: vim_free(gui_argv);
806:
807: if (gui.dpy == NULL)
808: {
809: x = full_screen;
810: full_screen = FALSE; /* use fprintf() */
811: EMSG("cannot open display");
812: full_screen = x;
813: return FAIL;
814: }
815:
816: /* Uncomment this to enable synchronous mode for debugging */
817: /* XSynchronize(gui.dpy, True); */
818:
819: /*
820: * So converters work.
821: */
822: XtInitializeWidgetClass(applicationShellWidgetClass);
823: XtInitializeWidgetClass(topLevelShellWidgetClass);
824:
825: /*
826: * The applicationShell is created as an unrealized
827: * parent for multiple topLevelShells. The topLevelShells
828: * are created as popup children of the applicationShell.
829: * This is a recommendation of Paul Asente & Ralph Swick in
830: * _X_Window_System_Toolkit_ p. 677.
831: */
832: AppShell = XtVaAppCreateShell(VIM_NAME, VIM_CLASS,
833: applicationShellWidgetClass, gui.dpy,
834: NULL);
835:
836: /*
837: * Get the application resources
838: */
839: XtVaGetApplicationResources(AppShell, &gui,
840: vim_resources, XtNumber(vim_resources), NULL);
841:
842: /*
843: * Check validity of resources
844: */
845: if (gui.border_width < 0)
846: gui.border_width = 0;
847:
848: /* For reverse video, swap foreground and background colours */
849: if (gui.rev_video)
850: {
851: tmp_pixel = gui.norm_pixel;
852: gui.norm_pixel = gui.back_pixel;
853: gui.back_pixel = tmp_pixel;
854: }
855:
856: /* Create shell widget to put vim in */
857: vimShell = XtVaCreatePopupShell("VIM",
858: topLevelShellWidgetClass, AppShell,
859: XtNborderWidth, 0,
860: NULL);
861:
862: /*
863: * Check that none of the colours are the same as the background color
864: */
865: if (gui.norm_pixel == gui.back_pixel)
866: {
867: gui.norm_pixel = gui_x11_get_color((char_u *)"White");
868: if (gui.norm_pixel == gui.back_pixel)
869: gui.norm_pixel = gui_x11_get_color((char_u *)"Black");
870: }
871: if (gui.bold_pixel == gui.back_pixel)
872: gui.bold_pixel = gui.norm_pixel;
873: if (gui.ital_pixel == gui.back_pixel)
874: gui.ital_pixel = gui.norm_pixel;
875: if (gui.underline_pixel == gui.back_pixel)
876: gui.underline_pixel = gui.norm_pixel;
877: if (gui.cursor_pixel == gui.back_pixel)
878: gui.cursor_pixel = gui.norm_pixel;
879:
880: /*
881: * Set up the GCs. The font attributes will be set in gui_init_font().
882: */
883:
884: gc_mask = GCForeground | GCBackground;
885: gc_vals.foreground = gui.norm_pixel;
886: gc_vals.background = gui.back_pixel;
887: gui.text_gc = XCreateGC(gui.dpy, DefaultRootWindow(gui.dpy), gc_mask,
888: &gc_vals);
889:
890: gc_vals.foreground = gui.back_pixel;
891: gc_vals.background = gui.norm_pixel;
892: gui.back_gc = XCreateGC(gui.dpy, DefaultRootWindow(gui.dpy), gc_mask,
893: &gc_vals);
894:
895: gc_mask |= GCFunction;
896: gc_vals.foreground = gui.norm_pixel ^ gui.back_pixel;
897: gc_vals.background = gui.norm_pixel ^ gui.back_pixel;
898: gc_vals.function = GXxor;
899: gui.invert_gc = XCreateGC(gui.dpy, DefaultRootWindow(gui.dpy), gc_mask,
900: &gc_vals);
901:
902: gui.visibility = VisibilityUnobscured;
903: gui.selection.atom = XInternAtom(gui.dpy, "VIM_SELECTION", False);
904:
905: /*
906: * Set up the fonts. This call will also set the window to the correct
907: * size for the used font.
908: */
909: gui.norm_font = NULL;
910: gui.bold_font = NULL;
911: gui.ital_font = NULL;
912: gui.boldital_font = NULL;
913: if (gui_init_font() == FAIL)
914: return FAIL;
915:
916: /* Now adapt the supplied(?) geometry-settings */
917: /* Added by Kjetil Jacobsen <kjetilja@stud.cs.uit.no> */
918: if (gui.geom != NULL && *gui.geom != NUL)
919: {
920: mask = XParseGeometry((char *)gui.geom, &x, &y, &w, &h);
921: if (mask & WidthValue)
922: Columns = w;
923: if (mask & HeightValue)
924: Rows = h;
925: /*
926: * Set the (x,y) position of the main window only if specified in the
927: * users geometry, so we get good defaults when they don't. This needs
928: * to be done before the shell is popped up.
929: */
930: if (mask & (XValue|YValue))
931: XtVaSetValues(vimShell, XtNgeometry, gui.geom, NULL);
932: }
933: gui.num_cols = Columns;
934: gui.num_rows = Rows;
935: gui_reset_scroll_region();
936:
937: gui_mch_create_widgets();
938:
939: /* Configure the desired scrollbars */
940: gui_init_which_components(NULL);
941:
942: /* Actually open the window */
943: XtPopup(vimShell, XtGrabNone);
944:
945: gui_mch_set_winsize();
946:
947: gui.wid = gui_mch_get_wid();
948:
949: /* Add a callback for the Close item on the window managers menu */
950: Atom_WM_DELETE_WINDOW = XInternAtom(gui.dpy, "WM_DELETE_WINDOW", False);
951: XSetWMProtocols(gui.dpy, XtWindow(vimShell), &Atom_WM_DELETE_WINDOW, 1);
952: XtAddEventHandler(vimShell, NoEventMask, True, gui_x11_wm_protocol_handler,
953: NULL);
954:
955: #ifdef ENABLE_EDITRES
956: /*
957: * Enable editres protocol (see editres(1))
958: * Usually will need to add -lXmu to the linker line as well.
959: */
960: {
961: extern void _XEditResCheckMessages();
962: XtAddEventHandler(vimShell, 0, True, _XEditResCheckMessages,
963: (XtPointer)NULL);
964: }
965: #endif
966:
967: return OK;
968: }
969:
970: void
971: gui_mch_exit()
972: {
973: XtCloseDisplay(gui.dpy);
974: }
975:
976: /*
977: * While unmanaging and re-managing scrollbars etc, we don't want the resize
978: * callback to be called, so this function is used to disable this callback,
979: * and then re-enable it afterwards. XSync() makes sure we don't register the
980: * callback before the server has finished processing any resize events we have
981: * created, otherwise we can get in a loop where the window flashes between two
982: * sizes, since resize_window_cb() calls set_winsize().
983: */
984: void
985: gui_x11_use_resize_callback(widget, enabled)
986: Widget widget;
987: int enabled;
988: {
989: if (enabled)
990: {
991: XSync(gui.dpy, False);
992: XtAddEventHandler(widget, StructureNotifyMask, FALSE,
993: gui_x11_resize_window_cb, (XtPointer)0);
994: XtAddEventHandler(widget, ExposureMask, FALSE, gui_x11_expose_cb,
995: (XtPointer)0);
996: }
997: else
998: {
999: XtRemoveEventHandler(widget, StructureNotifyMask, FALSE,
1000: gui_x11_resize_window_cb, (XtPointer)0);
1001: XtRemoveEventHandler(widget, ExposureMask, FALSE, gui_x11_expose_cb,
1002: (XtPointer)0);
1003: XSync(gui.dpy, False);
1004: }
1005: }
1006:
1007: /*
1008: * Initialise vim to use the font with the given name. Return FAIL if the font
1009: * could not be loaded, OK otherwise.
1010: */
1011: int
1012: gui_mch_init_font(font_name)
1013: char_u *font_name;
1014: {
1015: XFontStruct *font = NULL;
1016:
1017: if (font_name == NULL)
1018: {
1019: /*
1020: * If none of the fonts in 'font' could be loaded, try the one set in
1021: * the X resource, and finally just try using DFLT_FONT, which will
1022: * hopefully always be there.
1023: */
1024: font = XLoadQueryFont(gui.dpy, (char *)gui.dflt_font);
1025: if (font == NULL)
1026: font_name = (char_u *)DFLT_FONT;
1027: font_name = (char_u *)DFLT_FONT;
1028: }
1029: if (font == NULL)
1030: font = XLoadQueryFont(gui.dpy, (char *)font_name);
1031: if (font == NULL)
1032: return FAIL;
1033: if (font->max_bounds.width != font->min_bounds.width)
1034: {
1035: sprintf((char *)IObuff, "Font \"%s\" is not fixed-width",
1036: (char *)font_name);
1037: emsg(IObuff);
1038: XFreeFont(gui.dpy, font);
1039: return FAIL;
1040: }
1041: if (gui.norm_font != NULL)
1042: XFreeFont(gui.dpy, gui.norm_font);
1043: gui.norm_font = font;
1044: gui.char_width = font->max_bounds.width;
1045: gui.char_height = font->ascent + font->descent;
1046: gui.char_ascent = font->ascent;
1047:
1048: #ifdef DEBUG
1049: printf("Font Information for '%s':\n", font_name);
1050: printf(" w = %d, h = %d, ascent = %d, descent = %d\n", gui.char_width,
1051: gui.char_height, gui.char_ascent, font->descent);
1052: printf(" max ascent = %d, max descent = %d, max h = %d\n",
1053: font->max_bounds.ascent, font->max_bounds.descent,
1054: font->max_bounds.ascent + font->max_bounds.descent);
1055: printf(" min lbearing = %d, min rbearing = %d\n",
1056: font->min_bounds.lbearing, font->min_bounds.rbearing);
1057: printf(" max lbearing = %d, max rbearing = %d\n",
1058: font->max_bounds.lbearing, font->max_bounds.rbearing);
1059: printf(" leftink = %d, rightink = %d\n",
1060: (font->min_bounds.lbearing < 0),
1061: (font->max_bounds.rbearing > gui.char_width));
1062: printf("\n");
1063: #endif
1064:
1065: /*
1066: * Try to load other fonts for bold, italic, and bold-italic.
1067: * We should also try to work out what font to use for these when they are
1068: * not specified by X resources, but we don't yet.
1069: */
1070: if (gui.dflt_bold_fn != NULL && *gui.dflt_bold_fn != NUL)
1071: gui.bold_font = XLoadQueryFont(gui.dpy, (char *)gui.dflt_bold_fn);
1072: if (gui.dflt_ital_fn != NULL && *gui.dflt_ital_fn != NUL)
1073: gui.ital_font = XLoadQueryFont(gui.dpy, (char *)gui.dflt_ital_fn);
1074: if (gui.dflt_boldital_fn != NULL && *gui.dflt_boldital_fn != NUL)
1075: gui.boldital_font = XLoadQueryFont(gui.dpy, (char *)gui.dflt_boldital_fn);
1076:
1077: /* Must set the appropriate fonts for all the GCs */
1078: gui_x11_set_font(gui.text_gc, gui.norm_font->fid);
1079: gui_x11_set_font(gui.back_gc, gui.norm_font->fid);
1080:
1081: /*
1082: * Set the window sizes if the window is already visible.
1083: */
1084: if (vimShell != (Widget)NULL && XtIsRealized(vimShell))
1085: {
1086: WIN *wp;
1087:
1088: gui_mch_set_winsize();
1089:
1090: /* Force the scrollbar heights to get updated when a font is changed */
1091: if (gui.which_scrollbars[SB_LEFT])
1092: {
1093: for (wp = firstwin; wp; wp = wp->w_next)
1094: wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_HEIGHT;
1095: gui_mch_update_scrollbars(SB_UPDATE_HEIGHT, SB_LEFT);
1096: }
1097: if (gui.which_scrollbars[SB_RIGHT])
1098: {
1099: for (wp = firstwin; wp; wp = wp->w_next)
1100: wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
1101: gui_mch_update_scrollbars(SB_UPDATE_HEIGHT, SB_RIGHT);
1102: }
1103: }
1104:
1105: return OK;
1106: }
1107:
1108: /*
1109: * Return OK if the key with the termcap name "name" is supported.
1110: */
1111: int
1112: gui_mch_haskey(name)
1113: char_u *name;
1114: {
1115: int i;
1116:
1117: for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
1118: if (name[0] == special_keys[i].vim_code0 &&
1119: name[1] == special_keys[i].vim_code1)
1120: return OK;
1121: return FAIL;
1122: }
1123:
1124: /*
1125: * Return the text window-id and display. Only required for X-based GUI's
1126: */
1127: int
1128: gui_get_x11_windis(win, dis)
1129: Window *win;
1130: Display **dis;
1131: {
1132: *win = XtWindow(vimShell);
1133: *dis = gui.dpy;
1134: return OK;
1135: }
1136:
1137: void
1138: gui_mch_beep()
1139: {
1140: XBell(gui.dpy, 0);
1141: }
1142:
1143: void
1144: gui_mch_flash()
1145: {
1146: /* Do a visual beep by reversing the foreground and background colors */
1147: XFillRectangle(gui.dpy, gui.wid, gui.invert_gc, 0, 0,
1148: FILL_X(Columns) + gui.border_offset,
1149: FILL_Y(Rows) + gui.border_offset);
1150: XSync(gui.dpy, False);
1151: mch_delay(20L, TRUE); /* wait 1/50 of a second */
1152: XFillRectangle(gui.dpy, gui.wid, gui.invert_gc, 0, 0,
1153: FILL_X(Columns) + gui.border_offset,
1154: FILL_Y(Rows) + gui.border_offset);
1155: }
1156:
1157: /*
1158: * Iconify the GUI window.
1159: */
1160: void
1161: gui_mch_iconify()
1162: {
1163: XIconifyWindow(gui.dpy, XtWindow(vimShell), DefaultScreen(gui.dpy));
1164: }
1165:
1166: /*
1167: * Return the Pixel value (color) for the given color name. This routine was
1168: * pretty much taken from example code in the Silicon Graphics OSF/Motif
1169: * Programmer's Guide.
1170: */
1171: static Pixel
1172: gui_x11_get_color(name)
1173: char_u *name;
1174: {
1175: XrmValue from, to;
1176:
1177: from.size = STRLEN(name) + 1;
1178: if (from.size < sizeof(String))
1179: from.size = sizeof(String);
1180: from.addr = (char *)name;
1181: to.addr = NULL;
1182: XtConvert(vimShell, XtRString, &from, XtRPixel, &to);
1183: if (to.addr != NULL)
1184: return (Pixel) *((Pixel *)to.addr);
1185: else
1186: return (Pixel)NULL;
1187: }
1188:
1189: /*
1190: * Set the current text font (gc should be either gui.text_gc or gui.back_gc)
1191: */
1192: static void
1193: gui_x11_set_font(gc, fid)
1194: GC gc;
1195: Font fid;
1196: {
1197: static Font prev_text_fid = (Font) -1;
1198: static Font prev_back_fid = (Font) -1;
1199:
1200: if (gc == gui.text_gc && fid != prev_text_fid)
1201: {
1202: XSetFont(gui.dpy, gc, fid);
1203: prev_text_fid = fid;
1204: }
1205: else if (gc == gui.back_gc && fid != prev_back_fid)
1206: {
1207: XSetFont(gui.dpy, gc, fid);
1208: prev_back_fid = fid;
1209: }
1210: }
1211:
1212: /*
1213: * Set the current text color (gc should be either gui.text_gc or gui.back_gc)
1214: */
1215: static void
1216: gui_x11_set_color(gc, pixel)
1217: GC gc;
1218: Pixel pixel;
1219: {
1220: static Pixel prev_text_pixel = (Pixel) -1;
1221: static Pixel prev_back_pixel = (Pixel) -1;
1222:
1223: if (gc == gui.text_gc && pixel != prev_text_pixel)
1224: {
1225: XSetForeground(gui.dpy, gc, pixel);
1226: prev_text_pixel = pixel;
1227: }
1228: else if (gc == gui.back_gc && pixel != prev_back_pixel)
1229: {
1230: XSetBackground(gui.dpy, gc, pixel);
1231: prev_back_pixel = pixel;
1232: }
1233: }
1234:
1235: /*
1236: * Draw the cursor.
1237: */
1238: void
1239: gui_mch_draw_cursor()
1240: {
1241: /* Only write to the screen after LinePointers[] has been initialized */
1242: if (screen_cleared && NextScreen != NULL)
1243: {
1244: /* Clear the selection if we are about to write over it */
1245: if (gui.selection.state == SELECT_DONE
1246: && gui.row >= gui.selection.start.lnum
1247: && gui.row <= gui.selection.end.lnum)
1248: gui_mch_clear_selection();
1249:
1250: if (gui.row < screen_Rows && gui.col < screen_Columns)
1251: gui_mch_outstr_nowrap(LinePointers[gui.row] + gui.col,
1252: 1, FALSE, TRUE);
1253: if (!gui.in_focus)
1254: {
1255: gui_x11_set_color(gui.text_gc, gui.cursor_pixel);
1256: XDrawRectangle(gui.dpy, gui.wid, gui.text_gc, FILL_X(gui.col),
1257: FILL_Y(gui.row), gui.char_width - 1, gui.char_height - 1);
1258: }
1259: }
1260: }
1261:
1262: /*
1263: * Catch up with any queued X events. This may put keyboard input into the
1264: * input buffer, call resize call-backs, trigger timers etc. If there is
1265: * nothing in the X event queue (& no timers pending), then we return
1266: * immediately.
1267: */
1268: void
1269: gui_mch_update()
1270: {
1271: while(XtAppPending(app_context) && !is_input_buf_full())
1272: XtAppProcessEvent(app_context, XtIMAll);
1273: }
1274:
1275: /*
1276: * The main GUI input routine. Waits for a character from the keyboard.
1277: * wtime == -1 Wait forever.
1278: * wtime == 0 Don't wait.
1279: * wtime > 0 Wait wtime milliseconds for a character.
1280: * Returns OK if a character was found to be available within the given time,
1281: * or FAIL otherwise.
1282: */
1283: int
1284: gui_mch_wait_for_chars(wtime)
1285: int wtime;
1286: {
1287: XtIntervalId timer = (XtIntervalId)0;
1288: XtIntervalId updatescript_timer = (XtIntervalId)0;
1289: /*
1290: * Make these static, in case gui_x11_timer_cb is called after leaving
1291: * this function (otherwise a random value on the stack may be changed).
1292: */
1293: static int timed_out;
1294: static int do_updatescript;
1295:
1296: timed_out = FALSE;
1297: do_updatescript = FALSE;
1298:
1299: /*
1300: * If we're going to wait a bit, update the menus for the current State.
1301: */
1302: if (wtime != 0)
1303: gui_x11_update_menus(0);
1304: gui_mch_update();
1305: if (!is_input_buf_empty()) /* Got char, return immediately */
1306: return OK;
1307: else if (wtime == 0) /* Don't wait for char */
1308: return FAIL;
1309: else if (wtime > 0)
1310: {
1311: timer = XtAppAddTimeOut(app_context, (long_u)wtime, gui_x11_timer_cb,
1312: &timed_out);
1313: }
1314: else
1315: {
1316: /* We are waiting for ever, so update swap files after p_ut msec's */
1317: updatescript_timer = XtAppAddTimeOut(app_context, (long_u)p_ut,
1318: gui_x11_timer_cb, &do_updatescript);
1319: }
1320:
1321: while (!timed_out)
1322: {
1323: /*
1324: * Don't use gui_mch_update() because then we will spin-lock until a
1325: * char arrives, instead we use XtAppProcessEvent() to hang until an
1326: * event arrives. No need to check for input_buf_full because we are
1327: * returning as soon as it contains a single char. Note that
1328: * XtAppNextEvent() may not be used because it will not return after a
1329: * timer event has arrived -- webb
1330: */
1331: XtAppProcessEvent(app_context, XtIMAll);
1332:
1333: /*
1334: * If no characters arrive within 'updatetime' milli-seconds, flush all
1335: * the swap files to disk.
1336: */
1337: if (do_updatescript)
1338: {
1339: updatescript(0);
1340: do_updatescript = FALSE;
1341: updatescript_timer = (XtIntervalId)0;
1342: }
1343: if (!is_input_buf_empty())
1344: {
1345: if (timer != (XtIntervalId)0 && !timed_out)
1346: XtRemoveTimeOut(timer);
1347: if (updatescript_timer != (XtIntervalId)0 && !do_updatescript)
1348: XtRemoveTimeOut(updatescript_timer);
1349: return OK;
1350: }
1351: }
1352: return FAIL;
1353: }
1354:
1355: /*
1356: * Output routines.
1357: */
1358:
1359: /* Flush any output to the screen */
1360: void
1361: gui_mch_flush()
1362: {
1363: XFlush(gui.dpy);
1364: }
1365:
1366: /*
1367: * Clear a rectangular region of the screen from text pos (row1, col1) to
1368: * (row2, col2) inclusive.
1369: */
1370: void
1371: gui_mch_clear_block(row1, col1, row2, col2)
1372: int row1;
1373: int col1;
1374: int row2;
1375: int col2;
1376: {
1377: /* Clear the selection if we are about to write over it */
1378: if (gui.selection.state == SELECT_DONE
1379: && row2 >= gui.selection.start.lnum
1380: && row1 <= gui.selection.end.lnum)
1381: gui_mch_clear_selection();
1382:
1383: XFillRectangle(gui.dpy, gui.wid, gui.back_gc, FILL_X(col1),
1384: FILL_Y(row1), (col2 - col1 + 1) * gui.char_width,
1385: (row2 - row1 + 1) * gui.char_height);
1386:
1387: /* Invalidate cursor if it was in this block */
1388: if (gui.cursor_row >= row1 && gui.cursor_row <= row2
1389: && gui.cursor_col >= col1 && gui.cursor_col <= col2)
1390: INVALIDATE_CURSOR();
1391: }
1392:
1393: /*
1394: * Output the given string at the current cursor position. If the string is
1395: * too long to fit on the line, then it is truncated. wrap_cursor may be
1396: * TRUE if the cursor position should be wrapped when the end of the line is
1397: * reached, however the string will still be truncated and not continue on the
1398: * next line. is_cursor should only be TRUE when this function is being called
1399: * to actually draw the cursor.
1400: */
1401: void
1402: gui_mch_outstr_nowrap(s, len, wrap_cursor, is_cursor)
1403: char_u *s;
1404: int len;
1405: int wrap_cursor;
1406: int is_cursor;
1407: {
1408: GC text_gc;
1409: long_u highlight_mask;
1410:
1411: if (len == 0)
1412: return;
1413:
1414: if (len < 0)
1415: len = STRLEN(s);
1416:
1417: if (is_cursor && gui.in_focus)
1418: highlight_mask = gui.highlight_mask | HL_INVERSE;
1419: else
1420: highlight_mask = gui.highlight_mask;
1421:
1422: if (highlight_mask & (HL_INVERSE | HL_STANDOUT))
1423: text_gc = gui.back_gc;
1424: else
1425: text_gc = gui.text_gc;
1426:
1427: /* Set the font */
1428: if ((highlight_mask & (HL_BOLD | HL_STANDOUT)) && gui.bold_font != NULL)
1429: if ((highlight_mask & HL_ITAL) && gui.boldital_font != NULL)
1430: gui_x11_set_font(text_gc, gui.boldital_font->fid);
1431: else
1432: gui_x11_set_font(text_gc, gui.bold_font->fid);
1433: else if ((highlight_mask & HL_ITAL) && gui.ital_font != NULL)
1434: gui_x11_set_font(text_gc, gui.ital_font->fid);
1435: else
1436: gui_x11_set_font(text_gc, gui.norm_font->fid);
1437:
1438: /* Set the color */
1439: if (is_cursor && gui.in_focus)
1440: gui_x11_set_color(text_gc, gui.cursor_pixel);
1441: else if (highlight_mask & (HL_BOLD | HL_STANDOUT))
1442: gui_x11_set_color(text_gc, gui.bold_pixel);
1443: else if (highlight_mask & HL_ITAL)
1444: gui_x11_set_color(text_gc, gui.ital_pixel);
1445: else if (highlight_mask & HL_UNDERLINE)
1446: gui_x11_set_color(text_gc, gui.underline_pixel);
1447: else
1448: gui_x11_set_color(text_gc, gui.norm_pixel);
1449:
1450: /* Clear the selection if we are about to write over it */
1451: if (gui.selection.state == SELECT_DONE
1452: && gui.row >= gui.selection.start.lnum
1453: && gui.row <= gui.selection.end.lnum)
1454: gui_mch_clear_selection();
1455:
1456: /* Draw the text */
1457: XDrawImageString(gui.dpy, gui.wid, text_gc,
1458: TEXT_X(gui.col), TEXT_Y(gui.row), (char *)s, len);
1459:
1460: /* No bold font, so fake it */
1461: if ((highlight_mask & (HL_BOLD | HL_STANDOUT)) && gui.bold_font == NULL)
1462: XDrawString(gui.dpy, gui.wid, text_gc,
1463: TEXT_X(gui.col) + 1, TEXT_Y(gui.row), (char *)s, len);
1464:
1465: /* Underline the text */
1466: if ((highlight_mask & HL_UNDERLINE)
1467: || ((highlight_mask & HL_ITAL) && gui.ital_font == NULL))
1468: XDrawLine(gui.dpy, gui.wid, text_gc, FILL_X(gui.col),
1469: FILL_Y(gui.row + 1) - 1, FILL_X(gui.col + len) - 1,
1470: FILL_Y(gui.row + 1) - 1);
1471:
1472: if (!is_cursor)
1473: {
1474: /* Invalidate the old physical cursor position if we wrote over it */
1475: if (gui.cursor_row == gui.row && gui.cursor_col >= gui.col
1476: && gui.cursor_col < gui.col + len)
1477: INVALIDATE_CURSOR();
1478:
1479: /* Update the cursor position */
1480: gui.col += len;
1481: if (wrap_cursor && gui.col >= Columns)
1482: {
1483: gui.col = 0;
1484: gui.row++;
1485: }
1486: }
1487: }
1488:
1489: /*
1490: * Delete the given number of lines from the given row, scrolling up any
1491: * text further down within the scroll region.
1492: */
1493: void
1494: gui_mch_delete_lines(row, num_lines)
1495: int row;
1496: int num_lines;
1497: {
1498: if (gui.visibility == VisibilityFullyObscured)
1499: return; /* Can't see the window */
1500:
1501: if (num_lines <= 0)
1502: return;
1503:
1504: if (row + num_lines > gui.scroll_region_bot)
1505: {
1506: /* Scrolled out of region, just blank the lines out */
1507: gui_mch_clear_block(row, 0, gui.scroll_region_bot, Columns - 1);
1508: }
1509: else
1510: {
1511: XCopyArea(gui.dpy, gui.wid, gui.wid, gui.text_gc,
1512: FILL_X(0), FILL_Y(row + num_lines),
1513: gui.char_width * Columns,
1514: gui.char_height * (gui.scroll_region_bot - row - num_lines + 1),
1515: FILL_X(0), FILL_Y(row));
1516:
1517: /* Update gui.cursor_row if the cursor scrolled or copied over */
1518: if (gui.cursor_row >= row)
1519: {
1520: if (gui.cursor_row < row + num_lines)
1521: INVALIDATE_CURSOR();
1522: else if (gui.cursor_row <= gui.scroll_region_bot)
1523: gui.cursor_row -= num_lines;
1524: }
1525:
1526: gui_mch_clear_block(gui.scroll_region_bot - num_lines + 1, 0,
1527: gui.scroll_region_bot, Columns - 1);
1528: gui_x11_check_copy_area();
1529: }
1530: }
1531:
1532: /*
1533: * Insert the given number of lines before the given row, scrolling down any
1534: * following text within the scroll region.
1535: */
1536: void
1537: gui_mch_insert_lines(row, num_lines)
1538: int row;
1539: int num_lines;
1540: {
1541: if (gui.visibility == VisibilityFullyObscured)
1542: return; /* Can't see the window */
1543:
1544: if (num_lines <= 0)
1545: return;
1546:
1547: if (row + num_lines > gui.scroll_region_bot)
1548: {
1549: /* Scrolled out of region, just blank the lines out */
1550: gui_mch_clear_block(row, 0, gui.scroll_region_bot, Columns - 1);
1551: }
1552: else
1553: {
1554: XCopyArea(gui.dpy, gui.wid, gui.wid, gui.text_gc,
1555: FILL_X(0), FILL_Y(row),
1556: gui.char_width * Columns,
1557: gui.char_height * (gui.scroll_region_bot - row - num_lines + 1),
1558: FILL_X(0), FILL_Y(row + num_lines));
1559:
1560: /* Update gui.cursor_row if the cursor scrolled or copied over */
1561: if (gui.cursor_row >= gui.row)
1562: {
1563: if (gui.cursor_row <= gui.scroll_region_bot - num_lines)
1564: gui.cursor_row += num_lines;
1565: else if (gui.cursor_row <= gui.scroll_region_bot)
1566: INVALIDATE_CURSOR();
1567: }
1568:
1569: gui_mch_clear_block(row, 0, row + num_lines - 1, Columns - 1);
1570: gui_x11_check_copy_area();
1571: }
1572: }
1573:
1574: /*
1575: * Scroll the text between gui.scroll_region_top & gui.scroll_region_bot by the
1576: * number of lines given. Positive scrolls down (text goes up) and negative
1577: * scrolls up (text goes down).
1578: */
1579: static void
1580: gui_x11_check_copy_area()
1581: {
1582: XEvent event;
1583: XGraphicsExposeEvent *gevent;
1584:
1585: if (gui.visibility != VisibilityPartiallyObscured)
1586: return;
1587:
1588: XFlush(gui.dpy);
1589:
1590: /* Wait to check whether the scroll worked or not */
1591: for (;;)
1592: {
1593: if (XCheckTypedEvent(gui.dpy, NoExpose, &event))
1594: return; /* The scroll worked. */
1595:
1596: if (XCheckTypedEvent(gui.dpy, GraphicsExpose, &event))
1597: {
1598: gevent = (XGraphicsExposeEvent *)&event;
1599: gui_redraw(gevent->x, gevent->y, gevent->width, gevent->height);
1600: if (gevent->count == 0)
1601: return; /* This was the last expose event */
1602: }
1603: XSync(gui.dpy, False);
1604: }
1605: }
1606:
1607: /*
1608: * X Selection stuff, for cutting and pasting text to other windows.
1609: */
1610:
1611: static void
1612: gui_x11_request_selection_cb(w, success, selection, type, value, length, format)
1613: Widget w;
1614: XtPointer success;
1615: Atom *selection;
1616: Atom *type;
1617: XtPointer value;
1618: long_u *length;
1619: int *format;
1620: {
1621: int motion_type;
1622: long_u len;
1623: char_u *p;
1624:
1625: if (value == NULL || *length == 0)
1626: {
1627: gui_free_selection(); /* ??? */
1628: *(int *)success = FALSE;
1629: return;
1630: }
1631: motion_type = MCHAR;
1632: p = (char_u *)value;
1633: len = *length;
1634: if (*type == gui.selection.atom)
1635: {
1636: motion_type = *p++;
1637: len--;
1638: }
1639: gui_yank_selection(motion_type, p, len);
1640:
1641: XtFree((char *)value);
1642: *(int *)success = TRUE;
1643: }
1644:
1645: void
1646: gui_request_selection()
1647: {
1648: XEvent event;
1649: Atom type = gui.selection.atom;
1650: int success;
1651: int i;
1652:
1653: for (i = 0; i < 2; i++)
1654: {
1655: XtGetSelectionValue(vimShell, XA_PRIMARY, type,
1656: gui_x11_request_selection_cb, (XtPointer)&success, CurrentTime);
1657:
1658: /* Do we need this?: */
1659: XFlush(gui.dpy);
1660:
1661: /*
1662: * Wait for result of selection request, otherwise if we type more
1663: * characters, then they will appear before the one that requested the
1664: * paste! Don't worry, we will catch up with any other events later.
1665: */
1666: for (;;)
1667: {
1668: if (XCheckTypedEvent(gui.dpy, SelectionNotify, &event))
1669: break;
1670:
1671: /* Do we need this?: */
1672: XSync(gui.dpy, False);
1673: }
1674: XtDispatchEvent(&event);
1675:
1676: if (success)
1677: return;
1678: type = XA_STRING;
1679: }
1680: }
1681:
1682: static Boolean
1683: gui_x11_convert_selection_cb(w, selection, target, type, value, length, format)
1684: Widget w;
1685: Atom *selection;
1686: Atom *target;
1687: Atom *type;
1688: XtPointer *value;
1689: long_u *length;
1690: int *format;
1691: {
1692: char_u *string;
1693: char_u *result;
1694: int motion_type;
1695:
1696: if (!gui.selection.owned)
1697: return False; /* Shouldn't ever happen */
1698:
1699: if (*target == gui.selection.atom)
1700: {
1701: gui_get_selection();
1702: motion_type = gui_convert_selection(&string, length);
1703: if (motion_type < 0)
1704: return False;
1705:
1706: (*length)++;
1707: *value = XtMalloc(*length);
1708: result = (char_u *)*value;
1709: if (result == NULL)
1710: return False;
1711: result[0] = motion_type;
1712: vim_memmove(result + 1, string, (size_t)(*length - 1));
1713: *type = *target;
1714: *format = 8; /* 8 bits per char */
1715: return True;
1716: }
1717: else if (*target == XA_STRING)
1718: {
1719: gui_get_selection();
1720: motion_type = gui_convert_selection(&string, length);
1721: if (motion_type < 0)
1722: return False;
1723:
1724: *value = XtMalloc(*length);
1725: result = (char_u *)*value;
1726: if (result == NULL)
1727: return False;
1728: vim_memmove(result, string, (size_t)(*length));
1729: *type = *target;
1730: *format = 8; /* 8 bits per char */
1731: return True;
1732: }
1733: else
1734: return False;
1735: }
1736:
1737: static void
1738: gui_x11_lose_ownership_cb(w, selection)
1739: Widget w;
1740: Atom *selection;
1741: {
1742: gui_lose_selection();
1743: }
1744:
1745: void
1746: gui_mch_lose_selection()
1747: {
1748: XtDisownSelection(vimShell, XA_PRIMARY, CurrentTime);
1749: gui_mch_clear_selection();
1750: }
1751:
1752: int
1753: gui_mch_own_selection()
1754: {
1755: if (XtOwnSelection(vimShell, XA_PRIMARY, CurrentTime,
1756: gui_x11_convert_selection_cb, gui_x11_lose_ownership_cb,
1757: NULL) == False)
1758: return FAIL;
1759:
1760: return OK;
1761: }
1762:
1763: /*
1764: * Menu stuff.
1765: */
1766:
1767: void
1768: gui_x11_menu_cb(w, client_data, call_data)
1769: Widget w;
1770: XtPointer client_data, call_data;
1771: {
1772: gui_menu_cb((GuiMenu *)client_data);
1773: }
1774:
1775: /*
1776: * Used recursively by gui_x11_update_menus (see below)
1777: */
1778: static void
1779: gui_x11_update_menus_recurse(menu, mode)
1780: GuiMenu *menu;
1781: int mode;
1782: {
1783: while (menu)
1784: {
1785: if (menu->modes & mode)
1786: {
1787: if (vim_strchr(p_guioptions, GO_GREY) != NULL)
1788: XtSetSensitive(menu->id, True);
1789: else
1790: XtManageChild(menu->id);
1791: gui_x11_update_menus_recurse(menu->children, mode);
1792: }
1793: else
1794: {
1795: if (vim_strchr(p_guioptions, GO_GREY) != NULL)
1796: XtSetSensitive(menu->id, False);
1797: else
1798: XtUnmanageChild(menu->id);
1799: }
1800: menu = menu->next;
1801: }
1802: }
1803:
1804: /*
1805: * Make sure only the valid menu items appear for this mode. If
1806: * force_menu_update is not TRUE, then we only do this if the mode has changed
1807: * since last time. If "modes" is not 0, then we use these modes instead.
1808: */
1809: void
1810: gui_x11_update_menus(modes)
1811: int modes;
1812: {
1813: static int prev_mode = -1;
1814:
1815: int mode = 0;
1816:
1817: if (modes != 0x0)
1818: mode = modes;
1819: else if (VIsual_active)
1820: mode = MENU_VISUAL_MODE;
1821: else if (State & NORMAL)
1822: mode = MENU_NORMAL_MODE;
1823: else if (State & INSERT)
1824: mode = MENU_INSERT_MODE;
1825: else if (State & CMDLINE)
1826: mode = MENU_CMDLINE_MODE;
1827:
1828: if (force_menu_update || mode != prev_mode)
1829: {
1830: gui_x11_update_menus_recurse(gui.root_menu, mode);
1831: prev_mode = mode;
1832: force_menu_update = FALSE;
1833: }
1834: }
1835:
1836: /*
1837: * Function called when window closed. Preserve files and exit.
1838: * Should put up a requester!
1839: */
1840: /*ARGSUSED*/
1841: static void
1842: gui_x11_wm_protocol_handler(w, client_data, event, bool)
1843: Widget w;
1844: XtPointer client_data;
1845: XEvent *event;
1846: Boolean *bool;
1847: {
1848: /*
1849: * On some HPUX system with Motif 1.2 this function is somehow called when
1850: * starting up. This if () avoids an unexpected exit.
1851: */
1852: if (event->type != ClientMessage ||
1853: ((XClientMessageEvent *)event)->data.l[0] != Atom_WM_DELETE_WINDOW)
1854: return;
1855:
1856: STRCPY(IObuff, "Vim: Window closed\n");
1857: preserve_exit(); /* preserve files and exit */
1858: }
1859:
1860: /*
1861: * Compare two screen positions ala strcmp()
1862: */
1863: static int
1864: gui_x11_compare_pos(row1, col1, row2, col2)
1865: short_u row1;
1866: short_u col1;
1867: short_u row2;
1868: short_u col2;
1869: {
1870: if (row1 > row2) return( 1);
1871: if (row1 < row2) return(-1);
1872: if (col1 > col2) return( 1);
1873: if (col1 < col2) return(-1);
1874: return( 0);
1875: }
1876:
1877: /*
1878: * Start out the selection
1879: */
1880: void
1881: gui_x11_start_selection(button, x, y, repeated_click, modifiers)
1882: int button;
1883: int x;
1884: int y;
1885: int repeated_click;
1886: int_u modifiers;
1887: {
1888: GuiSelection *gs = &gui.selection;
1889:
1890: if (gs->state == SELECT_DONE)
1891: gui_mch_clear_selection();
1892:
1893: gs->start.lnum = Y_2_ROW(y);
1894: gs->start.col = X_2_COL(x);
1895: gs->end = gs->start;
1896: gs->origin_row = gs->start.lnum;
1897: gs->state = SELECT_IN_PROGRESS;
1898:
1899: if (repeated_click)
1900: {
1901: if (++(gs->mode) > SELECT_MODE_LINE)
1902: gs->mode = SELECT_MODE_CHAR;
1903: }
1904: else
1905: gs->mode = SELECT_MODE_CHAR;
1906:
1907: /* clear the cursor until the selection is made */
1908: gui_undraw_cursor();
1909:
1910: switch (gs->mode)
1911: {
1912: case SELECT_MODE_CHAR:
1913: gs->origin_start_col = gs->start.col;
1914: gs->word_end_col = gui_x11_get_line_end(gs->start.lnum);
1915: break;
1916:
1917: case SELECT_MODE_WORD:
1918: gui_x11_get_word_boundaries(gs, gs->start.lnum, gs->start.col);
1919: gs->origin_start_col = gs->word_start_col;
1920: gs->origin_end_col = gs->word_end_col;
1921:
1922: gui_x11_invert_area(gs->start.lnum, gs->word_start_col,
1923: gs->end.lnum, gs->word_end_col);
1924: gs->start.col = gs->word_start_col;
1925: gs->end.col = gs->word_end_col;
1926: break;
1927:
1928: case SELECT_MODE_LINE:
1929: gui_x11_invert_area(gs->start.lnum, 0, gs->start.lnum,
1930: gui.num_cols);
1931: gs->start.col = 0;
1932: gs->end.col = gui.num_cols;
1933: break;
1934: }
1935:
1936: gs->prev = gs->start;
1937:
1938: #ifdef DEBUG_SELECTION
1939: printf("Selection started at (%u,%u)\n", gs->start.lnum, gs->start.col);
1940: #endif
1941: }
1942:
1943: /*
1944: * Continue processing the selection
1945: */
1946: void
1947: gui_x11_process_selection(button, x, y, repeated_click, modifiers)
1948: int button;
1949: int x;
1950: int y;
1951: int repeated_click;
1952: int_u modifiers;
1953: {
1954: GuiSelection *gs = &gui.selection;
1955: int row, col;
1956: int diff;
1957:
1958: if (button == MOUSE_RELEASE)
1959: {
1960: /* Check to make sure we have something selected */
1961: if (gs->start.lnum == gs->end.lnum && gs->start.col == gs->end.col)
1962: {
1963: gui_update_cursor();
1964: gs->state = SELECT_CLEARED;
1965: return;
1966: }
1967:
1968: #ifdef DEBUG_SELECTION
1969: printf("Selection ended: (%u,%u) to (%u,%u)\n", gs->start.lnum,
1970: gs->start.col, gs->end.lnum, gs->end.col);
1971: #endif
1972: gui_free_selection();
1973: gui_own_selection();
1974: gui_x11_yank_selection(gs->start.lnum, gs->start.col, gs->end.lnum,
1975: gs->end.col);
1976: gui_update_cursor();
1977:
1978: gs->state = SELECT_DONE;
1979: return;
1980: }
1981:
1982: row = Y_2_ROW(y);
1983: col = X_2_COL(x);
1984:
1985: row = check_row(row);
1986: col = check_col(col);
1987:
1988: if (col == gs->prev.col && row == gs->prev.lnum)
1989: return;
1990:
1991: /*
1992: * When extending the selection with the right mouse button, swap the
1993: * start and end if the position is before half the selection
1994: */
1995: if (gs->state == SELECT_DONE && button == MOUSE_RIGHT)
1996: {
1997: /*
1998: * If the click is before the start, or the click is inside the
1999: * selection and the start is the closest side, set the origin to the
2000: * end of the selection.
2001: */
2002: if (gui_x11_compare_pos(row, col, gs->start.lnum, gs->start.col) < 0 ||
2003: (gui_x11_compare_pos(row, col, gs->end.lnum, gs->end.col) < 0 &&
2004: (((gs->start.lnum == gs->end.lnum &&
2005: gs->end.col - col > col - gs->start.col)) ||
2006: ((diff = (gs->end.lnum - row) - (row - gs->start.lnum)) > 0 ||
2007: (diff == 0 && col < (gs->start.col + gs->end.col) / 2)))))
2008: {
2009: gs->origin_row = gs->end.lnum;
2010: gs->origin_start_col = gs->end.col - 1;
2011: gs->origin_end_col = gs->end.col;
2012: }
2013: else
2014: {
2015: gs->origin_row = gs->start.lnum;
2016: gs->origin_start_col = gs->start.col;
2017: gs->origin_end_col = gs->start.col;
2018: }
2019: if (gs->mode == SELECT_MODE_WORD)
2020: {
2021: gui_x11_get_word_boundaries(gs, gs->origin_row,
2022: gs->origin_start_col);
2023: gs->origin_start_col = gs->word_start_col;
2024: gs->origin_end_col = gs->word_end_col;
2025: }
2026: }
2027:
2028: /* set state, for when using the right mouse button */
2029: gs->state = SELECT_IN_PROGRESS;
2030:
2031: #ifdef DEBUG_SELECTION
2032: printf("Selection extending to (%d,%d)\n", row, col);
2033: #endif
2034:
2035: switch (gs->mode)
2036: {
2037: case SELECT_MODE_CHAR:
2038: /* If we're on a different line, find where the line ends */
2039: if (row != gs->prev.lnum)
2040: gs->word_end_col = gui_x11_get_line_end(row);
2041:
2042: /* See if we are before or after the origin of the selection */
2043: if (gui_x11_compare_pos(row, col, gs->origin_row,
2044: gs->origin_start_col) >= 0)
2045: {
2046: if (col >= (int)gs->word_end_col)
2047: gui_x11_update_selection(gs, gs->origin_row,
2048: gs->origin_start_col, row, gui.num_cols);
2049: else
2050: gui_x11_update_selection(gs, gs->origin_row,
2051: gs->origin_start_col, row, col + 1);
2052: }
2053: else
2054: {
2055: if (col >= (int)gs->word_end_col)
2056: gui_x11_update_selection(gs, row, gs->word_end_col,
2057: gs->origin_row, gs->origin_start_col + 1);
2058: else
2059: gui_x11_update_selection(gs, row, col, gs->origin_row,
2060: gs->origin_start_col + 1);
2061: }
2062: break;
2063:
2064: case SELECT_MODE_WORD:
2065: /* If we are still within the same word, do nothing */
2066: if (row == gs->prev.lnum && col >= (int)gs->word_start_col
2067: && col < (int)gs->word_end_col)
2068: return;
2069:
2070: /* Get new word boundaries */
2071: gui_x11_get_word_boundaries(gs, row, col);
2072:
2073: /* Handle being after the origin point of selection */
2074: if (gui_x11_compare_pos(row, col, gs->origin_row,
2075: gs->origin_start_col) >= 0)
2076: gui_x11_update_selection(gs, gs->origin_row,
2077: gs->origin_start_col, row, gs->word_end_col);
2078: else
2079: gui_x11_update_selection(gs, row, gs->word_start_col,
2080: gs->origin_row, gs->origin_end_col);
2081: break;
2082:
2083: case SELECT_MODE_LINE:
2084: if (row == gs->prev.lnum)
2085: return;
2086:
2087: if (gui_x11_compare_pos(row, col, gs->origin_row,
2088: gs->origin_start_col) >= 0)
2089: gui_x11_update_selection(gs, gs->origin_row, 0, row,
2090: gui.num_cols);
2091: else
2092: gui_x11_update_selection(gs, row, 0, gs->origin_row,
2093: gui.num_cols);
2094: break;
2095: }
2096:
2097: gs->prev.lnum = row;
2098: gs->prev.col = col;
2099:
2100: #ifdef DEBUG_SELECTION
2101: printf("Selection is: (%u,%u) to (%u,%u)\n", gs->start.lnum,
2102: gs->start.col, gs->end.lnum, gs->end.col);
2103: #endif
2104: }
2105:
2106: /*
2107: * Called after an Expose event to redraw the selection
2108: */
2109: void
2110: gui_x11_redraw_selection(x, y, w, h)
2111: int x;
2112: int y;
2113: int w;
2114: int h;
2115: {
2116: GuiSelection *gs = &gui.selection;
2117: int row1, col1, row2, col2;
2118: int row;
2119: int start;
2120: int end;
2121:
2122: if (gs->state == SELECT_CLEARED)
2123: return;
2124:
2125: row1 = Y_2_ROW(y);
2126: col1 = X_2_COL(x);
2127: row2 = Y_2_ROW(y + h - 1);
2128: col2 = X_2_COL(x + w - 1);
2129:
2130: /* Limit the rows that need to be re-drawn */
2131: if (gs->start.lnum > row1)
2132: row1 = gs->start.lnum;
2133: if (gs->end.lnum < row2)
2134: row2 = gs->end.lnum;
2135:
2136: /* Look at each row that might need to be re-drawn */
2137: for (row = row1; row <= row2; row++)
2138: {
2139: /* For the first selection row, use the starting selection column */
2140: if (row == gs->start.lnum)
2141: start = gs->start.col;
2142: else
2143: start = 0;
2144:
2145: /* For the last selection row, use the ending selection column */
2146: if (row == gs->end.lnum)
2147: end = gs->end.col;
2148: else
2149: end = gui.num_cols;
2150:
2151: if (col1 > start)
2152: start = col1;
2153:
2154: if (col2 < end)
2155: end = col2 + 1;
2156:
2157: if (end > start)
2158: invert_rectangle(row, start, 1, end - start);
2159: }
2160: }
2161:
2162: /*
2163: * Called from outside to clear selected region from the display
2164: */
2165: void
2166: gui_mch_clear_selection()
2167: {
2168: GuiSelection *gs = &gui.selection;
2169:
2170: if (gs->state == SELECT_CLEARED)
2171: return;
2172:
2173: gui_x11_invert_area(gs->start.lnum, gs->start.col, gs->end.lnum,
2174: gs->end.col);
2175: gs->state = SELECT_CLEARED;
2176: }
2177:
2178: /*
2179: * Invert a region of the display between a starting and ending row and column
2180: */
2181: static void
2182: gui_x11_invert_area(row1, col1, row2, col2)
2183: int row1;
2184: int col1;
2185: int row2;
2186: int col2;
2187: {
2188: /* Swap the from and to positions so the from is always before */
2189: if (gui_x11_compare_pos(row1, col1, row2, col2) > 0)
2190: {
2191: int tmp_row, tmp_col;
2192: tmp_row = row1;
2193: tmp_col = col1;
2194: row1 = row2;
2195: col1 = col2;
2196: row2 = tmp_row;
2197: col2 = tmp_col;
2198: }
2199:
2200: /* If all on the same line, do it the easy way */
2201: if (row1 == row2)
2202: {
2203: invert_rectangle(row1, col1, 1, col2 - col1);
2204: return;
2205: }
2206:
2207: /* Handle a piece of the first line */
2208: if (col1 > 0)
2209: {
2210: invert_rectangle(row1, col1, 1, gui.num_cols - col1);
2211: row1++;
2212: }
2213:
2214: /* Handle a piece of the last line */
2215: if (col2 < gui.num_cols - 1)
2216: {
2217: invert_rectangle(row2, 0, 1, col2);
2218: row2--;
2219: }
2220:
2221: /* Handle the rectangle thats left */
2222: if (row2 >= row1)
2223: invert_rectangle(row1, 0, row2 - row1 + 1, gui.num_cols);
2224: }
2225:
2226: /*
2227: * Yank the currently selected area into the special selection buffer so it
2228: * will be available for pasting.
2229: */
2230: static void
2231: gui_x11_yank_selection(row1, col1, row2, col2)
2232: int row1;
2233: int col1;
2234: int row2;
2235: int col2;
2236: {
2237: char_u *buffer;
2238: char_u *bufp;
2239: int row;
2240: int start_col;
2241: int end_col;
2242: int line_end_col;
2243: int add_newline_flag = FALSE;
2244:
2245: /* Create a temporary buffer for storing the text */
2246: buffer = lalloc((row2 - row1 + 1) * gui.num_cols + 1, TRUE);
2247: if (buffer == NULL)
2248: return; /* Should there be an error message here? */
2249:
2250: /* Process each row in the selection */
2251: for (bufp = buffer, row = row1; row <= row2; row++)
2252: {
2253: if (row == row1)
2254: start_col = col1;
2255: else
2256: start_col = 0;
2257:
2258: if (row == row2)
2259: end_col = col2;
2260: else
2261: end_col = gui.num_cols;
2262:
2263: line_end_col = gui_x11_get_line_end(row);
2264:
2265: /* See if we need to nuke some trailing whitespace */
2266: if (end_col >= gui.num_cols && (row < row2 || end_col > line_end_col))
2267: {
2268: /* Get rid of trailing whitespace */
2269: end_col = line_end_col;
2270: if (end_col < start_col)
2271: end_col = start_col;
2272:
2273: /* If the last line extended to the end, add an extra newline */
2274: if (row == row2)
2275: add_newline_flag = TRUE;
2276: }
2277:
2278: /* If after the first row, we need to always add a newline */
2279: if (row > row1)
2280: *bufp++ = NL;
2281:
2282: if (gui.row < screen_Rows && end_col <= screen_Columns)
2283: {
2284: STRNCPY(bufp, &LinePointers[row][start_col], end_col - start_col);
2285: bufp += end_col - start_col;
2286: }
2287: }
2288:
2289: /* Add a newline at the end if the selection ended there */
2290: if (add_newline_flag)
2291: *bufp++ = NL;
2292:
2293: gui_yank_selection(MCHAR, buffer, bufp - buffer);
2294: vim_free(buffer);
2295: }
2296:
2297: /*
2298: * Find the starting and ending positions of the word at the given row and
2299: * column.
2300: */
2301: static void
2302: gui_x11_get_word_boundaries(gs, row, col)
2303: GuiSelection *gs;
2304: int row;
2305: int col;
2306: {
2307: char start_class;
2308: int temp_col;
2309:
2310: if (row >= screen_Rows || col >= screen_Columns)
2311: return;
2312:
2313: start_class = char_class(LinePointers[row][col]);
2314:
2315: temp_col = col;
2316: for ( ; temp_col > 0; temp_col--)
2317: if (char_class(LinePointers[row][temp_col - 1]) != start_class)
2318: break;
2319:
2320: gs->word_start_col = temp_col;
2321:
2322: temp_col = col;
2323: for ( ; temp_col < screen_Columns; temp_col++)
2324: if (char_class(LinePointers[row][temp_col]) != start_class)
2325: break;
2326: gs->word_end_col = temp_col;
2327:
2328: #ifdef DEBUG_SELECTION
2329: printf("Current word: col %u to %u\n", gs->word_start_col,
2330: gs->word_end_col);
2331: #endif
2332: }
2333:
2334: /*
2335: * Find the column position for the last non-whitespace character on the given
2336: * line.
2337: */
2338: static int
2339: gui_x11_get_line_end(row)
2340: int row;
2341: {
2342: int i;
2343:
2344: if (row >= screen_Rows)
2345: return 0;
2346: for (i = screen_Columns; i > 0; i--)
2347: if (LinePointers[row][i - 1] != ' ')
2348: break;
2349: return i;
2350: }
2351:
2352: /*
2353: * Update the currently selected region by adding and/or subtracting from the
2354: * beginning or end and inverting the changed area(s).
2355: */
2356: static void
2357: gui_x11_update_selection(gs, row1, col1, row2, col2)
2358: GuiSelection *gs;
2359: int row1;
2360: int col1;
2361: int row2;
2362: int col2;
2363: {
2364: /* See if we changed at the beginning of the selection */
2365: if (row1 != gs->start.lnum || col1 != gs->start.col)
2366: {
2367: gui_x11_invert_area(row1, col1, gs->start.lnum, gs->start.col);
2368: gs->start.lnum = row1;
2369: gs->start.col = col1;
2370: }
2371:
2372: /* See if we changed at the end of the selection */
2373: if (row2 != gs->end.lnum || col2 != gs->end.col)
2374: {
2375: gui_x11_invert_area(row2, col2, gs->end.lnum, gs->end.col);
2376: gs->end.lnum = row2;
2377: gs->end.col = col2;
2378: }
2379: }