1/* vi:set ts=8 sts=4 sw=4: 2 * 3 * VIM - Vi IMproved by Bram Moolenaar 4 * 5 * Do ":help uganda" in Vim to read copying and usage conditions. 6 * Do ":help credits" in Vim to see a list of people who contributed. 7 * See README.txt for an overview of the Vim source code. 8 */ 9 10/* 11 * Porting to GTK+ was done by: 12 * 13 * (C) 1998,1999,2000 by Marcin Dalecki <martin@dalecki.de> 14 * 15 * With GREAT support and continuous encouragements by Andy Kahn and of 16 * course Bram Moolenaar! 17 * 18 * Support for GTK+ 2 was added by: 19 * 20 * (C) 2002,2003 Jason Hildebrand <jason@peaceworks.ca> 21 * Daniel Elstner <daniel.elstner@gmx.net> 22 */ 23 24#include "vim.h" 25 26#ifdef FEAT_GUI_GNOME 27/* Gnome redefines _() and N_(). Grrr... */ 28# ifdef _ 29# undef _ 30# endif 31# ifdef N_ 32# undef N_ 33# endif 34# ifdef textdomain 35# undef textdomain 36# endif 37# ifdef bindtextdomain 38# undef bindtextdomain 39# endif 40# ifdef bind_textdomain_codeset 41# undef bind_textdomain_codeset 42# endif 43# if defined(FEAT_GETTEXT) && !defined(ENABLE_NLS) 44# define ENABLE_NLS /* so the texts in the dialog boxes are translated */ 45# endif 46# include <gnome.h> 47# include "version.h" 48/* missing prototype in bonobo-dock-item.h */ 49extern void bonobo_dock_item_set_behavior(BonoboDockItem *dock_item, BonoboDockItemBehavior beh); 50#endif 51 52#if !defined(FEAT_GUI_GTK) && defined(PROTO) 53/* When generating prototypes we don't want syntax errors. */ 54# define GdkAtom int 55# define GdkEventExpose int 56# define GdkEventFocus int 57# define GdkEventVisibility int 58# define GdkEventProperty int 59# define GtkContainer int 60# define GtkTargetEntry int 61# define GtkType int 62# define GtkWidget int 63# define gint int 64# define gpointer int 65# define guint int 66# define GdkEventKey int 67# define GdkEventSelection int 68# define GtkSelectionData int 69# define GdkEventMotion int 70# define GdkEventButton int 71# define GdkDragContext int 72# define GdkEventConfigure int 73# define GdkEventClient int 74#else 75# include <gdk/gdkkeysyms.h> 76# include <gdk/gdk.h> 77# ifdef WIN3264 78# include <gdk/gdkwin32.h> 79# else 80# include <gdk/gdkx.h> 81# endif 82 83# include <gtk/gtk.h> 84# include "gui_gtk_f.h" 85#endif 86 87#ifdef HAVE_X11_SUNKEYSYM_H 88# include <X11/Sunkeysym.h> 89static guint32 clipboard_event_time = CurrentTime; 90#endif 91 92/* 93 * Easy-to-use macro for multihead support. 94 */ 95#ifdef HAVE_GTK_MULTIHEAD 96# define GET_X_ATOM(atom) gdk_x11_atom_to_xatom_for_display( \ 97 gtk_widget_get_display(gui.mainwin), atom) 98#else 99# define GET_X_ATOM(atom) ((Atom)(atom)) 100#endif 101 102/* Selection type distinguishers */ 103enum 104{ 105 TARGET_TYPE_NONE, 106 TARGET_UTF8_STRING, 107 TARGET_STRING, 108 TARGET_COMPOUND_TEXT, 109 TARGET_HTML, 110 TARGET_TEXT, 111 TARGET_TEXT_URI_LIST, 112 TARGET_TEXT_PLAIN, 113 TARGET_VIM, 114 TARGET_VIMENC 115}; 116 117/* 118 * Table of selection targets supported by Vim. 119 * Note: Order matters, preferred types should come first. 120 */ 121static const GtkTargetEntry selection_targets[] = 122{ 123 {VIMENC_ATOM_NAME, 0, TARGET_VIMENC}, 124 {VIM_ATOM_NAME, 0, TARGET_VIM}, 125 {"text/html", 0, TARGET_HTML}, 126 {"UTF8_STRING", 0, TARGET_UTF8_STRING}, 127 {"COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT}, 128 {"TEXT", 0, TARGET_TEXT}, 129 {"STRING", 0, TARGET_STRING} 130}; 131#define N_SELECTION_TARGETS (sizeof(selection_targets) / sizeof(selection_targets[0])) 132 133#ifdef FEAT_DND 134/* 135 * Table of DnD targets supported by Vim. 136 * Note: Order matters, preferred types should come first. 137 */ 138static const GtkTargetEntry dnd_targets[] = 139{ 140 {"text/uri-list", 0, TARGET_TEXT_URI_LIST}, 141 {"text/html", 0, TARGET_HTML}, 142 {"UTF8_STRING", 0, TARGET_UTF8_STRING}, 143 {"STRING", 0, TARGET_STRING}, 144 {"text/plain", 0, TARGET_TEXT_PLAIN} 145}; 146# define N_DND_TARGETS (sizeof(dnd_targets) / sizeof(dnd_targets[0])) 147#endif 148 149 150/* 151 * "Monospace" is a standard font alias that should be present 152 * on all proper Pango/fontconfig installations. 153 */ 154# define DEFAULT_FONT "Monospace 10" 155 156#if !(defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION)) 157/* 158 * Atoms used to communicate save-yourself from the X11 session manager. There 159 * is no need to move them into the GUI struct, since they should be constant. 160 */ 161static GdkAtom wm_protocols_atom = GDK_NONE; 162static GdkAtom save_yourself_atom = GDK_NONE; 163#endif 164 165/* 166 * Atoms used to control/reference X11 selections. 167 */ 168static GdkAtom html_atom = GDK_NONE; 169static GdkAtom utf8_string_atom = GDK_NONE; 170static GdkAtom vim_atom = GDK_NONE; /* Vim's own special selection format */ 171static GdkAtom vimenc_atom = GDK_NONE; /* Vim's extended selection format */ 172 173/* 174 * Keycodes recognized by vim. 175 * NOTE: when changing this, the table in gui_x11.c probably needs the same 176 * change! 177 */ 178static struct special_key 179{ 180 guint key_sym; 181 char_u code0; 182 char_u code1; 183} 184const special_keys[] = 185{ 186 {GDK_Up, 'k', 'u'}, 187 {GDK_Down, 'k', 'd'}, 188 {GDK_Left, 'k', 'l'}, 189 {GDK_Right, 'k', 'r'}, 190 {GDK_F1, 'k', '1'}, 191 {GDK_F2, 'k', '2'}, 192 {GDK_F3, 'k', '3'}, 193 {GDK_F4, 'k', '4'}, 194 {GDK_F5, 'k', '5'}, 195 {GDK_F6, 'k', '6'}, 196 {GDK_F7, 'k', '7'}, 197 {GDK_F8, 'k', '8'}, 198 {GDK_F9, 'k', '9'}, 199 {GDK_F10, 'k', ';'}, 200 {GDK_F11, 'F', '1'}, 201 {GDK_F12, 'F', '2'}, 202 {GDK_F13, 'F', '3'}, 203 {GDK_F14, 'F', '4'}, 204 {GDK_F15, 'F', '5'}, 205 {GDK_F16, 'F', '6'}, 206 {GDK_F17, 'F', '7'}, 207 {GDK_F18, 'F', '8'}, 208 {GDK_F19, 'F', '9'}, 209 {GDK_F20, 'F', 'A'}, 210 {GDK_F21, 'F', 'B'}, 211 {GDK_Pause, 'F', 'B'}, /* Pause == F21 according to netbeans.txt */ 212 {GDK_F22, 'F', 'C'}, 213 {GDK_F23, 'F', 'D'}, 214 {GDK_F24, 'F', 'E'}, 215 {GDK_F25, 'F', 'F'}, 216 {GDK_F26, 'F', 'G'}, 217 {GDK_F27, 'F', 'H'}, 218 {GDK_F28, 'F', 'I'}, 219 {GDK_F29, 'F', 'J'}, 220 {GDK_F30, 'F', 'K'}, 221 {GDK_F31, 'F', 'L'}, 222 {GDK_F32, 'F', 'M'}, 223 {GDK_F33, 'F', 'N'}, 224 {GDK_F34, 'F', 'O'}, 225 {GDK_F35, 'F', 'P'}, 226#ifdef SunXK_F36 227 {SunXK_F36, 'F', 'Q'}, 228 {SunXK_F37, 'F', 'R'}, 229#endif 230 {GDK_Help, '%', '1'}, 231 {GDK_Undo, '&', '8'}, 232 {GDK_BackSpace, 'k', 'b'}, 233 {GDK_Insert, 'k', 'I'}, 234 {GDK_Delete, 'k', 'D'}, 235 {GDK_3270_BackTab, 'k', 'B'}, 236 {GDK_Clear, 'k', 'C'}, 237 {GDK_Home, 'k', 'h'}, 238 {GDK_End, '@', '7'}, 239 {GDK_Prior, 'k', 'P'}, 240 {GDK_Next, 'k', 'N'}, 241 {GDK_Print, '%', '9'}, 242 /* Keypad keys: */ 243 {GDK_KP_Left, 'k', 'l'}, 244 {GDK_KP_Right, 'k', 'r'}, 245 {GDK_KP_Up, 'k', 'u'}, 246 {GDK_KP_Down, 'k', 'd'}, 247 {GDK_KP_Insert, KS_EXTRA, (char_u)KE_KINS}, 248 {GDK_KP_Delete, KS_EXTRA, (char_u)KE_KDEL}, 249 {GDK_KP_Home, 'K', '1'}, 250 {GDK_KP_End, 'K', '4'}, 251 {GDK_KP_Prior, 'K', '3'}, /* page up */ 252 {GDK_KP_Next, 'K', '5'}, /* page down */ 253 254 {GDK_KP_Add, 'K', '6'}, 255 {GDK_KP_Subtract, 'K', '7'}, 256 {GDK_KP_Divide, 'K', '8'}, 257 {GDK_KP_Multiply, 'K', '9'}, 258 {GDK_KP_Enter, 'K', 'A'}, 259 {GDK_KP_Decimal, 'K', 'B'}, 260 261 {GDK_KP_0, 'K', 'C'}, 262 {GDK_KP_1, 'K', 'D'}, 263 {GDK_KP_2, 'K', 'E'}, 264 {GDK_KP_3, 'K', 'F'}, 265 {GDK_KP_4, 'K', 'G'}, 266 {GDK_KP_5, 'K', 'H'}, 267 {GDK_KP_6, 'K', 'I'}, 268 {GDK_KP_7, 'K', 'J'}, 269 {GDK_KP_8, 'K', 'K'}, 270 {GDK_KP_9, 'K', 'L'}, 271 272 /* End of list marker: */ 273 {0, 0, 0} 274}; 275 276/* 277 * Flags for command line options table below. 278 */ 279#define ARG_FONT 1 280#define ARG_GEOMETRY 2 281#define ARG_REVERSE 3 282#define ARG_NOREVERSE 4 283#define ARG_BACKGROUND 5 284#define ARG_FOREGROUND 6 285#define ARG_ICONIC 7 286#define ARG_ROLE 8 287#define ARG_NETBEANS 9 288#define ARG_XRM 10 /* ignored */ 289#define ARG_MENUFONT 11 /* ignored */ 290#define ARG_INDEX_MASK 0x00ff 291#define ARG_HAS_VALUE 0x0100 /* a value is expected after the argument */ 292#define ARG_NEEDS_GUI 0x0200 /* need to initialize the GUI for this */ 293#define ARG_FOR_GTK 0x0400 /* argument is handled by GTK+ or GNOME */ 294#define ARG_COMPAT_LONG 0x0800 /* accept -foo but substitute with --foo */ 295#define ARG_KEEP 0x1000 /* don't remove argument from argv[] */ 296 297/* 298 * This table holds all the X GUI command line options allowed. This includes 299 * the standard ones so that we can skip them when Vim is started without the 300 * GUI (but the GUI might start up later). 301 * 302 * When changing this, also update doc/gui_x11.txt and the usage message!!! 303 */ 304typedef struct 305{ 306 const char *name; 307 unsigned int flags; 308} 309cmdline_option_T; 310 311static const cmdline_option_T cmdline_options[] = 312{ 313 /* We handle these options ourselves */ 314 {"-fn", ARG_FONT|ARG_HAS_VALUE}, 315 {"-font", ARG_FONT|ARG_HAS_VALUE}, 316 {"-geom", ARG_GEOMETRY|ARG_HAS_VALUE}, 317 {"-geometry", ARG_GEOMETRY|ARG_HAS_VALUE}, 318 {"-rv", ARG_REVERSE}, 319 {"-reverse", ARG_REVERSE}, 320 {"+rv", ARG_NOREVERSE}, 321 {"+reverse", ARG_NOREVERSE}, 322 {"-bg", ARG_BACKGROUND|ARG_HAS_VALUE}, 323 {"-background", ARG_BACKGROUND|ARG_HAS_VALUE}, 324 {"-fg", ARG_FOREGROUND|ARG_HAS_VALUE}, 325 {"-foreground", ARG_FOREGROUND|ARG_HAS_VALUE}, 326 {"-iconic", ARG_ICONIC}, 327 {"--role", ARG_ROLE|ARG_HAS_VALUE}, 328#ifdef FEAT_NETBEANS_INTG 329 {"-nb", ARG_NETBEANS}, /* non-standard value format */ 330 {"-xrm", ARG_XRM|ARG_HAS_VALUE}, /* not implemented */ 331 {"-mf", ARG_MENUFONT|ARG_HAS_VALUE}, /* not implemented */ 332 {"-menufont", ARG_MENUFONT|ARG_HAS_VALUE}, /* not implemented */ 333#endif 334 /* Arguments handled by GTK (and GNOME) internally. */ 335 {"--g-fatal-warnings", ARG_FOR_GTK}, 336 {"--gdk-debug", ARG_FOR_GTK|ARG_HAS_VALUE}, 337 {"--gdk-no-debug", ARG_FOR_GTK|ARG_HAS_VALUE}, 338 {"--gtk-debug", ARG_FOR_GTK|ARG_HAS_VALUE}, 339 {"--gtk-no-debug", ARG_FOR_GTK|ARG_HAS_VALUE}, 340 {"--gtk-module", ARG_FOR_GTK|ARG_HAS_VALUE}, 341 {"--sync", ARG_FOR_GTK}, 342 {"--display", ARG_FOR_GTK|ARG_HAS_VALUE|ARG_COMPAT_LONG}, 343 {"--name", ARG_FOR_GTK|ARG_HAS_VALUE|ARG_COMPAT_LONG}, 344 {"--class", ARG_FOR_GTK|ARG_HAS_VALUE|ARG_COMPAT_LONG}, 345 {"--screen", ARG_FOR_GTK|ARG_HAS_VALUE}, 346 {"--gxid-host", ARG_FOR_GTK|ARG_HAS_VALUE}, 347 {"--gxid-port", ARG_FOR_GTK|ARG_HAS_VALUE}, 348#ifdef FEAT_GUI_GNOME 349 {"--load-modules", ARG_FOR_GTK|ARG_HAS_VALUE}, 350 {"--sm-client-id", ARG_FOR_GTK|ARG_HAS_VALUE}, 351 {"--sm-config-prefix", ARG_FOR_GTK|ARG_HAS_VALUE}, 352 {"--sm-disable", ARG_FOR_GTK}, 353 {"--oaf-ior-fd", ARG_FOR_GTK|ARG_HAS_VALUE}, 354 {"--oaf-activate-iid", ARG_FOR_GTK|ARG_HAS_VALUE}, 355 {"--oaf-private", ARG_FOR_GTK}, 356 {"--enable-sound", ARG_FOR_GTK}, 357 {"--disable-sound", ARG_FOR_GTK}, 358 {"--espeaker", ARG_FOR_GTK|ARG_HAS_VALUE}, 359 {"-?", ARG_FOR_GTK|ARG_NEEDS_GUI}, 360 {"--help", ARG_FOR_GTK|ARG_NEEDS_GUI|ARG_KEEP}, 361 {"--usage", ARG_FOR_GTK|ARG_NEEDS_GUI}, 362# if 0 /* conflicts with Vim's own --version argument */ 363 {"--version", ARG_FOR_GTK|ARG_NEEDS_GUI}, 364# endif 365 {"--disable-crash-dialog", ARG_FOR_GTK}, 366#endif 367 {NULL, 0} 368}; 369 370static int gui_argc = 0; 371static char **gui_argv = NULL; 372 373static const char *role_argument = NULL; 374#if defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION) 375static const char *restart_command = NULL; 376static char *abs_restart_command = NULL; 377#endif 378static int found_iconic_arg = FALSE; 379 380#ifdef FEAT_GUI_GNOME 381/* 382 * Can't use Gnome if --socketid given 383 */ 384static int using_gnome = 0; 385#else 386# define using_gnome 0 387#endif 388 389/* 390 * Parse the GUI related command-line arguments. Any arguments used are 391 * deleted from argv, and *argc is decremented accordingly. This is called 392 * when vim is started, whether or not the GUI has been started. 393 */ 394 void 395gui_mch_prepare(int *argc, char **argv) 396{ 397 const cmdline_option_T *option; 398 int i = 0; 399 int len = 0; 400 401#if defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION) 402 /* 403 * Determine the command used to invoke Vim, to be passed as restart 404 * command to the session manager. If argv[0] contains any directory 405 * components try building an absolute path, otherwise leave it as is. 406 */ 407 restart_command = argv[0]; 408 409 if (strchr(argv[0], G_DIR_SEPARATOR) != NULL) 410 { 411 char_u buf[MAXPATHL]; 412 413 if (mch_FullName((char_u *)argv[0], buf, (int)sizeof(buf), TRUE) == OK) 414 { 415 abs_restart_command = (char *)vim_strsave(buf); 416 restart_command = abs_restart_command; 417 } 418 } 419#endif 420 421 /* 422 * Move all the entries in argv which are relevant to GTK+ and GNOME 423 * into gui_argv. Freed later in gui_mch_init(). 424 */ 425 gui_argc = 0; 426 gui_argv = (char **)alloc((unsigned)((*argc + 1) * sizeof(char *))); 427 428 g_return_if_fail(gui_argv != NULL); 429 430 gui_argv[gui_argc++] = argv[i++]; 431 432 while (i < *argc) 433 { 434 /* Don't waste CPU cycles on non-option arguments. */ 435 if (argv[i][0] != '-' && argv[i][0] != '+') 436 { 437 ++i; 438 continue; 439 } 440 441 /* Look for argv[i] in cmdline_options[] table. */ 442 for (option = &cmdline_options[0]; option->name != NULL; ++option) 443 { 444 len = strlen(option->name); 445 446 if (strncmp(argv[i], option->name, len) == 0) 447 { 448 if (argv[i][len] == '\0') 449 break; 450 /* allow --foo=bar style */ 451 if (argv[i][len] == '=' && (option->flags & ARG_HAS_VALUE)) 452 break; 453#ifdef FEAT_NETBEANS_INTG 454 /* darn, -nb has non-standard syntax */ 455 if (vim_strchr((char_u *)":=", argv[i][len]) != NULL 456 && (option->flags & ARG_INDEX_MASK) == ARG_NETBEANS) 457 break; 458#endif 459 } 460 else if ((option->flags & ARG_COMPAT_LONG) 461 && strcmp(argv[i], option->name + 1) == 0) 462 { 463 /* Replace the standard X arguments "-name" and "-display" 464 * with their GNU-style long option counterparts. */ 465 argv[i] = (char *)option->name; 466 break; 467 } 468 } 469 if (option->name == NULL) /* no match */ 470 { 471 ++i; 472 continue; 473 } 474 475 if (option->flags & ARG_FOR_GTK) 476 { 477 /* Move the argument into gui_argv, which 478 * will later be passed to gtk_init_check() */ 479 gui_argv[gui_argc++] = argv[i]; 480 } 481 else 482 { 483 char *value = NULL; 484 485 /* Extract the option's value if there is one. 486 * Accept both "--foo bar" and "--foo=bar" style. */ 487 if (option->flags & ARG_HAS_VALUE) 488 { 489 if (argv[i][len] == '=') 490 value = &argv[i][len + 1]; 491 else if (i + 1 < *argc && strcmp(argv[i + 1], "--") != 0) 492 value = argv[i + 1]; 493 } 494 495 /* Check for options handled by Vim itself */ 496 switch (option->flags & ARG_INDEX_MASK) 497 { 498 case ARG_REVERSE: 499 found_reverse_arg = TRUE; 500 break; 501 case ARG_NOREVERSE: 502 found_reverse_arg = FALSE; 503 break; 504 case ARG_FONT: 505 font_argument = value; 506 break; 507 case ARG_GEOMETRY: 508 if (value != NULL) 509 gui.geom = vim_strsave((char_u *)value); 510 break; 511 case ARG_BACKGROUND: 512 background_argument = value; 513 break; 514 case ARG_FOREGROUND: 515 foreground_argument = value; 516 break; 517 case ARG_ICONIC: 518 found_iconic_arg = TRUE; 519 break; 520 case ARG_ROLE: 521 role_argument = value; /* used later in gui_mch_open() */ 522 break; 523#ifdef FEAT_NETBEANS_INTG 524 case ARG_NETBEANS: 525 gui.dofork = FALSE; /* don't fork() when starting GUI */ 526 netbeansArg = argv[i]; 527 break; 528#endif 529 default: 530 break; 531 } 532 } 533 534 /* These arguments make gnome_program_init() print a message and exit. 535 * Must start the GUI for this, otherwise ":gui" will exit later! */ 536 if (option->flags & ARG_NEEDS_GUI) 537 gui.starting = TRUE; 538 539 if (option->flags & ARG_KEEP) 540 ++i; 541 else 542 { 543 /* Remove the flag from the argument vector. */ 544 if (--*argc > i) 545 { 546 int n_strip = 1; 547 548 /* Move the argument's value as well, if there is one. */ 549 if ((option->flags & ARG_HAS_VALUE) 550 && argv[i][len] != '=' 551 && strcmp(argv[i + 1], "--") != 0) 552 { 553 ++n_strip; 554 --*argc; 555 if (option->flags & ARG_FOR_GTK) 556 gui_argv[gui_argc++] = argv[i + 1]; 557 } 558 559 if (*argc > i) 560 mch_memmove(&argv[i], &argv[i + n_strip], 561 (*argc - i) * sizeof(char *)); 562 } 563 argv[*argc] = NULL; 564 } 565 } 566 567 gui_argv[gui_argc] = NULL; 568} 569 570#if defined(EXITFREE) || defined(PROTO) 571 void 572gui_mch_free_all() 573{ 574 vim_free(gui_argv); 575#if defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION) 576 vim_free(abs_restart_command); 577#endif 578} 579#endif 580 581/* 582 * This should be maybe completely removed. 583 * Doesn't seem possible, since check_copy_area() relies on 584 * this information. --danielk 585 */ 586 static gint 587visibility_event(GtkWidget *widget UNUSED, 588 GdkEventVisibility *event, 589 gpointer data UNUSED) 590{ 591 gui.visibility = event->state; 592 /* 593 * When we do an gdk_window_copy_area(), and the window is partially 594 * obscured, we want to receive an event to tell us whether it worked 595 * or not. 596 */ 597 if (gui.text_gc != NULL) 598 gdk_gc_set_exposures(gui.text_gc, 599 gui.visibility != GDK_VISIBILITY_UNOBSCURED); 600 return FALSE; 601} 602 603/* 604 * Redraw the corresponding portions of the screen. 605 */ 606 static gint 607expose_event(GtkWidget *widget UNUSED, 608 GdkEventExpose *event, 609 gpointer data UNUSED) 610{ 611 /* Skip this when the GUI isn't set up yet, will redraw later. */ 612 if (gui.starting) 613 return FALSE; 614 615 out_flush(); /* make sure all output has been processed */ 616 gui_redraw(event->area.x, event->area.y, 617 event->area.width, event->area.height); 618 619 /* Clear the border areas if needed */ 620 if (event->area.x < FILL_X(0)) 621 gdk_window_clear_area(gui.drawarea->window, 0, 0, FILL_X(0), 0); 622 if (event->area.y < FILL_Y(0)) 623 gdk_window_clear_area(gui.drawarea->window, 0, 0, 0, FILL_Y(0)); 624 if (event->area.x > FILL_X(Columns)) 625 gdk_window_clear_area(gui.drawarea->window, 626 FILL_X((int)Columns), 0, 0, 0); 627 if (event->area.y > FILL_Y(Rows)) 628 gdk_window_clear_area(gui.drawarea->window, 0, FILL_Y((int)Rows), 0, 0); 629 630 return FALSE; 631} 632 633#ifdef FEAT_CLIENTSERVER 634/* 635 * Handle changes to the "Comm" property 636 */ 637 static gint 638property_event(GtkWidget *widget, 639 GdkEventProperty *event, 640 gpointer data UNUSED) 641{ 642 if (event->type == GDK_PROPERTY_NOTIFY 643 && event->state == (int)GDK_PROPERTY_NEW_VALUE 644 && GDK_WINDOW_XWINDOW(event->window) == commWindow 645 && GET_X_ATOM(event->atom) == commProperty) 646 { 647 XEvent xev; 648 649 /* Translate to XLib */ 650 xev.xproperty.type = PropertyNotify; 651 xev.xproperty.atom = commProperty; 652 xev.xproperty.window = commWindow; 653 xev.xproperty.state = PropertyNewValue; 654 serverEventProc(GDK_WINDOW_XDISPLAY(widget->window), &xev); 655 } 656 return FALSE; 657} 658#endif 659 660 661/**************************************************************************** 662 * Focus handlers: 663 */ 664 665 666/* 667 * This is a simple state machine: 668 * BLINK_NONE not blinking at all 669 * BLINK_OFF blinking, cursor is not shown 670 * BLINK_ON blinking, cursor is shown 671 */ 672 673#define BLINK_NONE 0 674#define BLINK_OFF 1 675#define BLINK_ON 2 676 677static int blink_state = BLINK_NONE; 678static long_u blink_waittime = 700; 679static long_u blink_ontime = 400; 680static long_u blink_offtime = 250; 681static guint blink_timer = 0; 682 683 void 684gui_mch_set_blinking(long waittime, long on, long off) 685{ 686 blink_waittime = waittime; 687 blink_ontime = on; 688 blink_offtime = off; 689} 690 691/* 692 * Stop the cursor blinking. Show the cursor if it wasn't shown. 693 */ 694 void 695gui_mch_stop_blink(void) 696{ 697 if (blink_timer) 698 { 699 gtk_timeout_remove(blink_timer); 700 blink_timer = 0; 701 } 702 if (blink_state == BLINK_OFF) 703 gui_update_cursor(TRUE, FALSE); 704 blink_state = BLINK_NONE; 705} 706 707 static gint 708blink_cb(gpointer data UNUSED) 709{ 710 if (blink_state == BLINK_ON) 711 { 712 gui_undraw_cursor(); 713 blink_state = BLINK_OFF; 714 blink_timer = gtk_timeout_add((guint32)blink_offtime, 715 (GtkFunction) blink_cb, NULL); 716 } 717 else 718 { 719 gui_update_cursor(TRUE, FALSE); 720 blink_state = BLINK_ON; 721 blink_timer = gtk_timeout_add((guint32)blink_ontime, 722 (GtkFunction) blink_cb, NULL); 723 } 724 725 return FALSE; /* don't happen again */ 726} 727 728/* 729 * Start the cursor blinking. If it was already blinking, this restarts the 730 * waiting time and shows the cursor. 731 */ 732 void 733gui_mch_start_blink(void) 734{ 735 if (blink_timer) 736 gtk_timeout_remove(blink_timer); 737 /* Only switch blinking on if none of the times is zero */ 738 if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus) 739 { 740 blink_timer = gtk_timeout_add((guint32)blink_waittime, 741 (GtkFunction) blink_cb, NULL); 742 blink_state = BLINK_ON; 743 gui_update_cursor(TRUE, FALSE); 744 } 745} 746 747 static gint 748enter_notify_event(GtkWidget *widget UNUSED, 749 GdkEventCrossing *event UNUSED, 750 gpointer data UNUSED) 751{ 752 if (blink_state == BLINK_NONE) 753 gui_mch_start_blink(); 754 755 /* make sure keyboard input goes there */ 756 if (gtk_socket_id == 0 || !GTK_WIDGET_HAS_FOCUS(gui.drawarea)) 757 gtk_widget_grab_focus(gui.drawarea); 758 759 return FALSE; 760} 761 762 static gint 763leave_notify_event(GtkWidget *widget UNUSED, 764 GdkEventCrossing *event UNUSED, 765 gpointer data UNUSED) 766{ 767 if (blink_state != BLINK_NONE) 768 gui_mch_stop_blink(); 769 770 return FALSE; 771} 772 773 static gint 774focus_in_event(GtkWidget *widget, 775 GdkEventFocus *event UNUSED, 776 gpointer data UNUSED) 777{ 778 gui_focus_change(TRUE); 779 780 if (blink_state == BLINK_NONE) 781 gui_mch_start_blink(); 782 783 /* make sure keyboard input goes to the draw area (if this is focus for a 784 * window) */ 785 if (widget != gui.drawarea) 786 gtk_widget_grab_focus(gui.drawarea); 787 788 return TRUE; 789} 790 791 static gint 792focus_out_event(GtkWidget *widget UNUSED, 793 GdkEventFocus *event UNUSED, 794 gpointer data UNUSED) 795{ 796 gui_focus_change(FALSE); 797 798 if (blink_state != BLINK_NONE) 799 gui_mch_stop_blink(); 800 801 return TRUE; 802} 803 804 805/* 806 * Translate a GDK key value to UTF-8 independently of the current locale. 807 * The output is written to string, which must have room for at least 6 bytes 808 * plus the NUL terminator. Returns the length in bytes. 809 * 810 * This function is used in the GTK+ 2 GUI only. The GTK+ 1 code makes use 811 * of GdkEventKey::string instead. But event->string is evil; see here why: 812 * http://developer.gnome.org/doc/API/2.0/gdk/gdk-Event-Structures.html#GdkEventKey 813 */ 814 static int 815keyval_to_string(unsigned int keyval, unsigned int state, char_u *string) 816{ 817 int len; 818 guint32 uc; 819 820 uc = gdk_keyval_to_unicode(keyval); 821 if (uc != 0) 822 { 823 /* Check for CTRL-foo */ 824 if ((state & GDK_CONTROL_MASK) && uc >= 0x20 && uc < 0x80) 825 { 826 /* These mappings look arbitrary at the first glance, but in fact 827 * resemble quite exactly the behaviour of the GTK+ 1.2 GUI on my 828 * machine. The only difference is BS vs. DEL for CTRL-8 (makes 829 * more sense and is consistent with usual terminal behaviour). */ 830 if (uc >= '@') 831 string[0] = uc & 0x1F; 832 else if (uc == '2') 833 string[0] = NUL; 834 else if (uc >= '3' && uc <= '7') 835 string[0] = uc ^ 0x28; 836 else if (uc == '8') 837 string[0] = BS; 838 else if (uc == '?') 839 string[0] = DEL; 840 else 841 string[0] = uc; 842 len = 1; 843 } 844 else 845 { 846 /* Translate a normal key to UTF-8. This doesn't work for dead 847 * keys of course, you _have_ to use an input method for that. */ 848 len = utf_char2bytes((int)uc, string); 849 } 850 } 851 else 852 { 853 /* Translate keys which are represented by ASCII control codes in Vim. 854 * There are only a few of those; most control keys are translated to 855 * special terminal-like control sequences. */ 856 len = 1; 857 switch (keyval) 858 { 859 case GDK_Tab: case GDK_KP_Tab: case GDK_ISO_Left_Tab: 860 string[0] = TAB; 861 break; 862 case GDK_Linefeed: 863 string[0] = NL; 864 break; 865 case GDK_Return: case GDK_ISO_Enter: case GDK_3270_Enter: 866 string[0] = CAR; 867 break; 868 case GDK_Escape: 869 string[0] = ESC; 870 break; 871 default: 872 len = 0; 873 break; 874 } 875 } 876 string[len] = NUL; 877 878 return len; 879} 880 881 static int 882modifiers_gdk2vim(guint state) 883{ 884 int modifiers = 0; 885 886 if (state & GDK_SHIFT_MASK) 887 modifiers |= MOD_MASK_SHIFT; 888 if (state & GDK_CONTROL_MASK) 889 modifiers |= MOD_MASK_CTRL; 890 if (state & GDK_MOD1_MASK) 891 modifiers |= MOD_MASK_ALT; 892#if GTK_CHECK_VERSION(2,10,0) 893 if (state & GDK_SUPER_MASK) 894 modifiers |= MOD_MASK_META; 895#endif 896 if (state & GDK_MOD4_MASK) 897 modifiers |= MOD_MASK_META; 898 899 return modifiers; 900} 901 902 static int 903modifiers_gdk2mouse(guint state) 904{ 905 int modifiers = 0; 906 907 if (state & GDK_SHIFT_MASK) 908 modifiers |= MOUSE_SHIFT; 909 if (state & GDK_CONTROL_MASK) 910 modifiers |= MOUSE_CTRL; 911 if (state & GDK_MOD1_MASK) 912 modifiers |= MOUSE_ALT; 913 914 return modifiers; 915} 916 917/* 918 * Main keyboard handler: 919 */ 920 static gint 921key_press_event(GtkWidget *widget UNUSED, 922 GdkEventKey *event, 923 gpointer data UNUSED) 924{ 925 /* For GTK+ 2 we know for sure how large the string might get. 926 * (That is, up to 6 bytes + NUL + CSI escapes + safety measure.) */ 927 char_u string[32], string2[32]; 928 guint key_sym; 929 int len; 930 int i; 931 int modifiers; 932 int key; 933 guint state; 934 char_u *s, *d; 935 936 clipboard_event_time = event->time; 937 key_sym = event->keyval; 938 state = event->state; 939 940#ifdef FEAT_XIM 941 if (xim_queue_key_press_event(event, TRUE)) 942 return TRUE; 943#endif 944 945#ifdef FEAT_HANGULIN 946 if (key_sym == GDK_space && (state & GDK_SHIFT_MASK)) 947 { 948 hangul_input_state_toggle(); 949 return TRUE; 950 } 951#endif 952 953#ifdef SunXK_F36 954 /* 955 * These keys have bogus lookup strings, and trapping them here is 956 * easier than trying to XRebindKeysym() on them with every possible 957 * combination of modifiers. 958 */ 959 if (key_sym == SunXK_F36 || key_sym == SunXK_F37) 960 len = 0; 961 else 962#endif 963 { 964 len = keyval_to_string(key_sym, state, string2); 965 966 /* Careful: convert_input() doesn't handle the NUL character. 967 * No need to convert pure ASCII anyway, thus the len > 1 check. */ 968 if (len > 1 && input_conv.vc_type != CONV_NONE) 969 len = convert_input(string2, len, sizeof(string2)); 970 971 s = string2; 972 d = string; 973 for (i = 0; i < len; ++i) 974 { 975 *d++ = s[i]; 976 if (d[-1] == CSI && d + 2 < string + sizeof(string)) 977 { 978 /* Turn CSI into K_CSI. */ 979 *d++ = KS_EXTRA; 980 *d++ = (int)KE_CSI; 981 } 982 } 983 len = d - string; 984 } 985 986 /* Shift-Tab results in Left_Tab, but we want <S-Tab> */ 987 if (key_sym == GDK_ISO_Left_Tab) 988 { 989 key_sym = GDK_Tab; 990 state |= GDK_SHIFT_MASK; 991 } 992 993#ifdef FEAT_MENU 994 /* If there is a menu and 'wak' is "yes", or 'wak' is "menu" and the key 995 * is a menu shortcut, we ignore everything with the ALT modifier. */ 996 if ((state & GDK_MOD1_MASK) 997 && gui.menu_is_active 998 && (*p_wak == 'y' 999 || (*p_wak == 'm' 1000 && len == 1 1001 && gui_is_menu_shortcut(string[0])))) 1002 /* For GTK2 we return false to signify that we haven't handled the 1003 * keypress, so that gtk will handle the mnemonic or accelerator. */ 1004 return FALSE; 1005#endif 1006 1007 /* Check for Alt/Meta key (Mod1Mask), but not for a BS, DEL or character 1008 * that already has the 8th bit set. 1009 * Don't do this for <S-M-Tab>, that should become K_S_TAB with ALT. 1010 * Don't do this for double-byte encodings, it turns the char into a lead 1011 * byte. */ 1012 if (len == 1 1013 && ((state & GDK_MOD1_MASK) 1014#if GTK_CHECK_VERSION(2,10,0) 1015 || (state & GDK_SUPER_MASK) 1016#endif 1017 ) 1018 && !(key_sym == GDK_BackSpace || key_sym == GDK_Delete) 1019 && (string[0] & 0x80) == 0 1020 && !(key_sym == GDK_Tab && (state & GDK_SHIFT_MASK)) 1021 && !enc_dbcs 1022 ) 1023 { 1024 string[0] |= 0x80; 1025 state &= ~GDK_MOD1_MASK; /* don't use it again */ 1026 if (enc_utf8) /* convert to utf-8 */ 1027 { 1028 string[1] = string[0] & 0xbf; 1029 string[0] = ((unsigned)string[0] >> 6) + 0xc0; 1030 if (string[1] == CSI) 1031 { 1032 string[2] = KS_EXTRA; 1033 string[3] = (int)KE_CSI; 1034 len = 4; 1035 } 1036 else 1037 len = 2; 1038 } 1039 } 1040 1041 /* Check for special keys. Also do this when len == 1 (key has an ASCII 1042 * value) to detect backspace, delete and keypad keys. */ 1043 if (len == 0 || len == 1) 1044 { 1045 for (i = 0; special_keys[i].key_sym != 0; i++) 1046 { 1047 if (special_keys[i].key_sym == key_sym) 1048 { 1049 string[0] = CSI; 1050 string[1] = special_keys[i].code0; 1051 string[2] = special_keys[i].code1; 1052 len = -3; 1053 break; 1054 } 1055 } 1056 } 1057 1058 if (len == 0) /* Unrecognized key */ 1059 return TRUE; 1060 1061 /* Special keys (and a few others) may have modifiers. Also when using a 1062 * double-byte encoding (can't set the 8th bit). */ 1063 if (len == -3 || key_sym == GDK_space || key_sym == GDK_Tab 1064 || key_sym == GDK_Return || key_sym == GDK_Linefeed 1065 || key_sym == GDK_Escape || key_sym == GDK_KP_Tab 1066 || key_sym == GDK_ISO_Enter || key_sym == GDK_3270_Enter 1067 || (enc_dbcs && len == 1 && ((state & GDK_MOD1_MASK) 1068#if GTK_CHECK_VERSION(2,10,0) 1069 || (state & GDK_SUPER_MASK) 1070#endif 1071 ))) 1072 { 1073 modifiers = modifiers_gdk2vim(state); 1074 1075 /* 1076 * For some keys a shift modifier is translated into another key 1077 * code. 1078 */ 1079 if (len == -3) 1080 key = TO_SPECIAL(string[1], string[2]); 1081 else 1082 key = string[0]; 1083 1084 key = simplify_key(key, &modifiers); 1085 if (key == CSI) 1086 key = K_CSI; 1087 if (IS_SPECIAL(key)) 1088 { 1089 string[0] = CSI; 1090 string[1] = K_SECOND(key); 1091 string[2] = K_THIRD(key); 1092 len = 3; 1093 } 1094 else 1095 { 1096 string[0] = key; 1097 len = 1; 1098 } 1099 1100 if (modifiers != 0) 1101 { 1102 string2[0] = CSI; 1103 string2[1] = KS_MODIFIER; 1104 string2[2] = modifiers; 1105 add_to_input_buf(string2, 3); 1106 } 1107 } 1108 1109 if (len == 1 && ((string[0] == Ctrl_C && ctrl_c_interrupts) 1110 || (string[0] == intr_char && intr_char != Ctrl_C))) 1111 { 1112 trash_input_buf(); 1113 got_int = TRUE; 1114 } 1115 1116 add_to_input_buf(string, len); 1117 1118 /* blank out the pointer if necessary */ 1119 if (p_mh) 1120 gui_mch_mousehide(TRUE); 1121 1122 return TRUE; 1123} 1124 1125#if defined(FEAT_XIM) 1126 static gboolean 1127key_release_event(GtkWidget *widget UNUSED, 1128 GdkEventKey *event, 1129 gpointer data UNUSED) 1130{ 1131 clipboard_event_time = event->time; 1132 /* 1133 * GTK+ 2 input methods may do fancy stuff on key release events too. 1134 * With the default IM for instance, you can enter any UCS code point 1135 * by holding down CTRL-SHIFT and typing hexadecimal digits. 1136 */ 1137 return xim_queue_key_press_event(event, FALSE); 1138} 1139#endif 1140 1141 1142/**************************************************************************** 1143 * Selection handlers: 1144 */ 1145 1146 static gint 1147selection_clear_event(GtkWidget *widget UNUSED, 1148 GdkEventSelection *event, 1149 gpointer user_data UNUSED) 1150{ 1151 if (event->selection == clip_plus.gtk_sel_atom) 1152 clip_lose_selection(&clip_plus); 1153 else 1154 clip_lose_selection(&clip_star); 1155 1156 return TRUE; 1157} 1158 1159#define RS_NONE 0 /* selection_received_cb() not called yet */ 1160#define RS_OK 1 /* selection_received_cb() called and OK */ 1161#define RS_FAIL 2 /* selection_received_cb() called and failed */ 1162static int received_selection = RS_NONE; 1163 1164 static void 1165selection_received_cb(GtkWidget *widget UNUSED, 1166 GtkSelectionData *data, 1167 guint time_ UNUSED, 1168 gpointer user_data UNUSED) 1169{ 1170 VimClipboard *cbd; 1171 char_u *text; 1172 char_u *tmpbuf = NULL; 1173 guchar *tmpbuf_utf8 = NULL; 1174 int len; 1175 int motion_type; 1176 1177 if (data->selection == clip_plus.gtk_sel_atom) 1178 cbd = &clip_plus; 1179 else 1180 cbd = &clip_star; 1181 1182 text = (char_u *)data->data; 1183 len = data->length; 1184 motion_type = MCHAR; 1185 1186 if (text == NULL || len <= 0) 1187 { 1188 received_selection = RS_FAIL; 1189 /* clip_free_selection(cbd); ??? */ 1190 1191 return; 1192 } 1193 1194 if (data->type == vim_atom) 1195 { 1196 motion_type = *text++; 1197 --len; 1198 } 1199 1200 else if (data->type == vimenc_atom) 1201 { 1202 char_u *enc; 1203 vimconv_T conv; 1204 1205 motion_type = *text++; 1206 --len; 1207 1208 enc = text; 1209 text += STRLEN(text) + 1; 1210 len -= text - enc; 1211 1212 /* If the encoding of the text is different from 'encoding', attempt 1213 * converting it. */ 1214 conv.vc_type = CONV_NONE; 1215 convert_setup(&conv, enc, p_enc); 1216 if (conv.vc_type != CONV_NONE) 1217 { 1218 tmpbuf = string_convert(&conv, text, &len); 1219 if (tmpbuf != NULL) 1220 text = tmpbuf; 1221 convert_setup(&conv, NULL, NULL); 1222 } 1223 } 1224 1225 /* gtk_selection_data_get_text() handles all the nasty details 1226 * and targets and encodings etc. This rocks so hard. */ 1227 else 1228 { 1229 tmpbuf_utf8 = gtk_selection_data_get_text(data); 1230 if (tmpbuf_utf8 != NULL) 1231 { 1232 len = STRLEN(tmpbuf_utf8); 1233 if (input_conv.vc_type != CONV_NONE) 1234 { 1235 tmpbuf = string_convert(&input_conv, tmpbuf_utf8, &len); 1236 if (tmpbuf != NULL) 1237 text = tmpbuf; 1238 } 1239 else 1240 text = tmpbuf_utf8; 1241 } 1242 else if (len >= 2 && text[0] == 0xff && text[1] == 0xfe) 1243 { 1244 vimconv_T conv; 1245 1246 /* UTF-16, we get this for HTML */ 1247 conv.vc_type = CONV_NONE; 1248 convert_setup_ext(&conv, (char_u *)"utf-16le", FALSE, p_enc, TRUE); 1249 1250 if (conv.vc_type != CONV_NONE) 1251 { 1252 text += 2; 1253 len -= 2; 1254 tmpbuf = string_convert(&conv, text, &len); 1255 convert_setup(&conv, NULL, NULL); 1256 } 1257 if (tmpbuf != NULL) 1258 text = tmpbuf; 1259 } 1260 } 1261 1262 /* Chop off any traiing NUL bytes. OpenOffice sends these. */ 1263 while (len > 0 && text[len - 1] == NUL) 1264 --len; 1265 1266 clip_yank_selection(motion_type, text, (long)len, cbd); 1267 received_selection = RS_OK; 1268 vim_free(tmpbuf); 1269 g_free(tmpbuf_utf8); 1270} 1271 1272/* 1273 * Prepare our selection data for passing it to the external selection 1274 * client. 1275 */ 1276 static void 1277selection_get_cb(GtkWidget *widget UNUSED, 1278 GtkSelectionData *selection_data, 1279 guint info, 1280 guint time_ UNUSED, 1281 gpointer user_data UNUSED) 1282{ 1283 char_u *string; 1284 char_u *tmpbuf; 1285 long_u tmplen; 1286 int length; 1287 int motion_type; 1288 GdkAtom type; 1289 VimClipboard *cbd; 1290 1291 if (selection_data->selection == clip_plus.gtk_sel_atom) 1292 cbd = &clip_plus; 1293 else 1294 cbd = &clip_star; 1295 1296 if (!cbd->owned) 1297 return; /* Shouldn't ever happen */ 1298 1299 if (info != (guint)TARGET_STRING 1300 && (!clip_html || info != (guint)TARGET_HTML) 1301 && info != (guint)TARGET_UTF8_STRING 1302 && info != (guint)TARGET_VIMENC 1303 && info != (guint)TARGET_VIM 1304 && info != (guint)TARGET_COMPOUND_TEXT 1305 && info != (guint)TARGET_TEXT) 1306 return; 1307 1308 /* get the selection from the '*'/'+' register */ 1309 clip_get_selection(cbd); 1310 1311 motion_type = clip_convert_selection(&string, &tmplen, cbd); 1312 if (motion_type < 0 || string == NULL) 1313 return; 1314 /* Due to int arguments we can't handle more than G_MAXINT. Also 1315 * reserve one extra byte for NUL or the motion type; just in case. 1316 * (Not that pasting 2G of text is ever going to work, but... ;-) */ 1317 length = MIN(tmplen, (long_u)(G_MAXINT - 1)); 1318 1319 if (info == (guint)TARGET_VIM) 1320 { 1321 tmpbuf = alloc((unsigned)length + 1); 1322 if (tmpbuf != NULL) 1323 { 1324 tmpbuf[0] = motion_type; 1325 mch_memmove(tmpbuf + 1, string, (size_t)length); 1326 } 1327 /* For our own format, the first byte contains the motion type */ 1328 ++length; 1329 vim_free(string); 1330 string = tmpbuf; 1331 type = vim_atom; 1332 } 1333 1334 else if (info == (guint)TARGET_HTML) 1335 { 1336 vimconv_T conv; 1337 1338 /* Since we get utf-16, we probably should set it as well. */ 1339 conv.vc_type = CONV_NONE; 1340 convert_setup_ext(&conv, p_enc, TRUE, (char_u *)"utf-16le", FALSE); 1341 if (conv.vc_type != CONV_NONE) 1342 { 1343 tmpbuf = string_convert(&conv, string, &length); 1344 convert_setup(&conv, NULL, NULL); 1345 vim_free(string); 1346 string = tmpbuf; 1347 } 1348 1349 /* Prepend the BOM: "fffe" */ 1350 if (string != NULL) 1351 { 1352 tmpbuf = alloc(length + 2); 1353 tmpbuf[0] = 0xff; 1354 tmpbuf[1] = 0xfe; 1355 mch_memmove(tmpbuf + 2, string, (size_t)length); 1356 vim_free(string); 1357 string = tmpbuf; 1358 length += 2; 1359 1360 selection_data->type = selection_data->target; 1361 selection_data->format = 16; /* 16 bits per char */ 1362 gtk_selection_data_set(selection_data, html_atom, 16, 1363 string, length); 1364 vim_free(string); 1365 } 1366 return; 1367 } 1368 else if (info == (guint)TARGET_VIMENC) 1369 { 1370 int l = STRLEN(p_enc); 1371 1372 /* contents: motion_type 'encoding' NUL text */ 1373 tmpbuf = alloc((unsigned)length + l + 2); 1374 if (tmpbuf != NULL) 1375 { 1376 tmpbuf[0] = motion_type; 1377 STRCPY(tmpbuf + 1, p_enc); 1378 mch_memmove(tmpbuf + l + 2, string, (size_t)length); 1379 } 1380 length += l + 2; 1381 vim_free(string); 1382 string = tmpbuf; 1383 type = vimenc_atom; 1384 } 1385 1386 /* gtk_selection_data_set_text() handles everything for us. This is 1387 * so easy and simple and cool, it'd be insane not to use it. */ 1388 else 1389 { 1390 if (output_conv.vc_type != CONV_NONE) 1391 { 1392 tmpbuf = string_convert(&output_conv, string, &length); 1393 vim_free(string); 1394 if (tmpbuf == NULL) 1395 return; 1396 string = tmpbuf; 1397 } 1398 /* Validate the string to avoid runtime warnings */ 1399 if (g_utf8_validate((const char *)string, (gssize)length, NULL)) 1400 { 1401 gtk_selection_data_set_text(selection_data, 1402 (const char *)string, length); 1403 } 1404 vim_free(string); 1405 return; 1406 } 1407 1408 if (string != NULL) 1409 { 1410 selection_data->type = selection_data->target; 1411 selection_data->format = 8; /* 8 bits per char */ 1412 1413 gtk_selection_data_set(selection_data, type, 8, string, length); 1414 vim_free(string); 1415 } 1416} 1417 1418/* 1419 * Check if the GUI can be started. Called before gvimrc is sourced. 1420 * Return OK or FAIL. 1421 */ 1422 int 1423gui_mch_init_check(void) 1424{ 1425#ifdef FEAT_GUI_GNOME 1426 if (gtk_socket_id == 0) 1427 using_gnome = 1; 1428#endif 1429 1430 /* Don't use gtk_init() or gnome_init(), it exits on failure. */ 1431 if (!gtk_init_check(&gui_argc, &gui_argv)) 1432 { 1433 gui.dying = TRUE; 1434 EMSG(_((char *)e_opendisp)); 1435 return FAIL; 1436 } 1437 1438 return OK; 1439} 1440 1441 1442/**************************************************************************** 1443 * Mouse handling callbacks 1444 */ 1445 1446 1447static guint mouse_click_timer = 0; 1448static int mouse_timed_out = TRUE; 1449 1450/* 1451 * Timer used to recognize multiple clicks of the mouse button 1452 */ 1453 static gint 1454mouse_click_timer_cb(gpointer data) 1455{ 1456 /* we don't use this information currently */ 1457 int *timed_out = (int *) data; 1458 1459 *timed_out = TRUE; 1460 return FALSE; /* don't happen again */ 1461} 1462 1463static guint motion_repeat_timer = 0; 1464static int motion_repeat_offset = FALSE; 1465static gint motion_repeat_timer_cb(gpointer); 1466 1467 static void 1468process_motion_notify(int x, int y, GdkModifierType state) 1469{ 1470 int button; 1471 int_u vim_modifiers; 1472 1473 button = (state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | 1474 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | 1475 GDK_BUTTON5_MASK)) 1476 ? MOUSE_DRAG : ' '; 1477 1478 /* If our pointer is currently hidden, then we should show it. */ 1479 gui_mch_mousehide(FALSE); 1480 1481 /* Just moving the rodent above the drawing area without any button 1482 * being pressed. */ 1483 if (button != MOUSE_DRAG) 1484 { 1485 gui_mouse_moved(x, y); 1486 return; 1487 } 1488 1489 /* translate modifier coding between the main engine and GTK */ 1490 vim_modifiers = modifiers_gdk2mouse(state); 1491 1492 /* inform the editor engine about the occurrence of this event */ 1493 gui_send_mouse_event(button, x, y, FALSE, vim_modifiers); 1494 1495 /* 1496 * Auto repeat timer handling. 1497 */ 1498 if (x < 0 || y < 0 1499 || x >= gui.drawarea->allocation.width 1500 || y >= gui.drawarea->allocation.height) 1501 { 1502 1503 int dx; 1504 int dy; 1505 int offshoot; 1506 int delay = 10; 1507 1508 /* Calculate the maximal distance of the cursor from the drawing area. 1509 * (offshoot can't become negative here!). 1510 */ 1511 dx = x < 0 ? -x : x - gui.drawarea->allocation.width; 1512 dy = y < 0 ? -y : y - gui.drawarea->allocation.height; 1513 1514 offshoot = dx > dy ? dx : dy; 1515 1516 /* Make a linearly decaying timer delay with a threshold of 5 at a 1517 * distance of 127 pixels from the main window. 1518 * 1519 * One could think endlessly about the most ergonomic variant here. 1520 * For example it could make sense to calculate the distance from the 1521 * drags start instead... 1522 * 1523 * Maybe a parabolic interpolation would suite us better here too... 1524 */ 1525 if (offshoot > 127) 1526 { 1527 /* 5 appears to be somehow near to my perceptual limits :-). */ 1528 delay = 5; 1529 } 1530 else 1531 { 1532 delay = (130 * (127 - offshoot)) / 127 + 5; 1533 } 1534 1535 /* shoot again */ 1536 if (!motion_repeat_timer) 1537 motion_repeat_timer = gtk_timeout_add((guint32)delay, 1538 motion_repeat_timer_cb, NULL); 1539 } 1540} 1541 1542/* 1543 * Timer used to recognize multiple clicks of the mouse button. 1544 */ 1545 static gint 1546motion_repeat_timer_cb(gpointer data UNUSED) 1547{ 1548 int x; 1549 int y; 1550 GdkModifierType state; 1551 1552 gdk_window_get_pointer(gui.drawarea->window, &x, &y, &state); 1553 1554 if (!(state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | 1555 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | 1556 GDK_BUTTON5_MASK))) 1557 { 1558 motion_repeat_timer = 0; 1559 return FALSE; 1560 } 1561 1562 /* If there already is a mouse click in the input buffer, wait another 1563 * time (otherwise we would create a backlog of clicks) */ 1564 if (vim_used_in_input_buf() > 10) 1565 return TRUE; 1566 1567 motion_repeat_timer = 0; 1568 1569 /* 1570 * Fake a motion event. 1571 * Trick: Pretend the mouse moved to the next character on every other 1572 * event, otherwise drag events will be discarded, because they are still 1573 * in the same character. 1574 */ 1575 if (motion_repeat_offset) 1576 x += gui.char_width; 1577 1578 motion_repeat_offset = !motion_repeat_offset; 1579 process_motion_notify(x, y, state); 1580 1581 /* Don't happen again. We will get reinstalled in the synthetic event 1582 * if needed -- thus repeating should still work. */ 1583 return FALSE; 1584} 1585 1586 static gint 1587motion_notify_event(GtkWidget *widget, 1588 GdkEventMotion *event, 1589 gpointer data UNUSED) 1590{ 1591 if (event->is_hint) 1592 { 1593 int x; 1594 int y; 1595 GdkModifierType state; 1596 1597 gdk_window_get_pointer(widget->window, &x, &y, &state); 1598 process_motion_notify(x, y, state); 1599 } 1600 else 1601 { 1602 process_motion_notify((int)event->x, (int)event->y, 1603 (GdkModifierType)event->state); 1604 } 1605 1606 return TRUE; /* handled */ 1607} 1608 1609 1610/* 1611 * Mouse button handling. Note please that we are capturing multiple click's 1612 * by our own timeout mechanism instead of the one provided by GTK+ itself. 1613 * This is due to the way the generic VIM code is recognizing multiple clicks. 1614 */ 1615 static gint 1616button_press_event(GtkWidget *widget, 1617 GdkEventButton *event, 1618 gpointer data UNUSED) 1619{ 1620 int button; 1621 int repeated_click = FALSE; 1622 int x, y; 1623 int_u vim_modifiers; 1624 1625 clipboard_event_time = event->time; 1626 1627 /* Make sure we have focus now we've been selected */ 1628 if (gtk_socket_id != 0 && !GTK_WIDGET_HAS_FOCUS(widget)) 1629 gtk_widget_grab_focus(widget); 1630 1631 /* 1632 * Don't let additional events about multiple clicks send by GTK to us 1633 * after the initial button press event confuse us. 1634 */ 1635 if (event->type != GDK_BUTTON_PRESS) 1636 return FALSE; 1637 1638 x = event->x; 1639 y = event->y; 1640 1641 /* Handle multiple clicks */ 1642 if (!mouse_timed_out && mouse_click_timer) 1643 { 1644 gtk_timeout_remove(mouse_click_timer); 1645 mouse_click_timer = 0; 1646 repeated_click = TRUE; 1647 } 1648 1649 mouse_timed_out = FALSE; 1650 mouse_click_timer = gtk_timeout_add((guint32)p_mouset, 1651 mouse_click_timer_cb, &mouse_timed_out); 1652 1653 switch (event->button) 1654 { 1655 case 1: 1656 button = MOUSE_LEFT; 1657 break; 1658 case 2: 1659 button = MOUSE_MIDDLE; 1660 break; 1661 case 3: 1662 button = MOUSE_RIGHT; 1663 break; 1664 default: 1665 return FALSE; /* Unknown button */ 1666 } 1667 1668#ifdef FEAT_XIM 1669 /* cancel any preediting */ 1670 if (im_is_preediting()) 1671 xim_reset(); 1672#endif 1673 1674 vim_modifiers = modifiers_gdk2mouse(event->state); 1675 1676 gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers); 1677 1678 return TRUE; 1679} 1680 1681/* 1682 * GTK+ 2 abstracts scrolling via the GdkEventScroll. 1683 */ 1684 static gboolean 1685scroll_event(GtkWidget *widget, 1686 GdkEventScroll *event, 1687 gpointer data UNUSED) 1688{ 1689 int button; 1690 int_u vim_modifiers; 1691 1692 if (gtk_socket_id != 0 && !GTK_WIDGET_HAS_FOCUS(widget)) 1693 gtk_widget_grab_focus(widget); 1694 1695 switch (event->direction) 1696 { 1697 case GDK_SCROLL_UP: 1698 button = MOUSE_4; 1699 break; 1700 case GDK_SCROLL_DOWN: 1701 button = MOUSE_5; 1702 break; 1703 case GDK_SCROLL_LEFT: 1704 button = MOUSE_7; 1705 break; 1706 case GDK_SCROLL_RIGHT: 1707 button = MOUSE_6; 1708 break; 1709 default: /* This shouldn't happen */ 1710 return FALSE; 1711 } 1712 1713# ifdef FEAT_XIM 1714 /* cancel any preediting */ 1715 if (im_is_preediting()) 1716 xim_reset(); 1717# endif 1718 1719 vim_modifiers = modifiers_gdk2mouse(event->state); 1720 1721 gui_send_mouse_event(button, (int)event->x, (int)event->y, 1722 FALSE, vim_modifiers); 1723 1724 return TRUE; 1725} 1726 1727 1728 static gint 1729button_release_event(GtkWidget *widget UNUSED, 1730 GdkEventButton *event, 1731 gpointer data UNUSED) 1732{ 1733 int x, y; 1734 int_u vim_modifiers; 1735 1736 clipboard_event_time = event->time; 1737 1738 /* Remove any motion "machine gun" timers used for automatic further 1739 extension of allocation areas if outside of the applications window 1740 area .*/ 1741 if (motion_repeat_timer) 1742 { 1743 gtk_timeout_remove(motion_repeat_timer); 1744 motion_repeat_timer = 0; 1745 } 1746 1747 x = event->x; 1748 y = event->y; 1749 1750 vim_modifiers = modifiers_gdk2mouse(event->state); 1751 1752 gui_send_mouse_event(MOUSE_RELEASE, x, y, FALSE, vim_modifiers); 1753 1754 return TRUE; 1755} 1756 1757 1758#ifdef FEAT_DND 1759/**************************************************************************** 1760 * Drag aNd Drop support handlers. 1761 */ 1762 1763/* 1764 * Count how many items there may be and separate them with a NUL. 1765 * Apparently the items are separated with \r\n. This is not documented, 1766 * thus be careful not to go past the end. Also allow separation with 1767 * NUL characters. 1768 */ 1769 static int 1770count_and_decode_uri_list(char_u *out, char_u *raw, int len) 1771{ 1772 int i; 1773 char_u *p = out; 1774 int count = 0; 1775 1776 for (i = 0; i < len; ++i) 1777 { 1778 if (raw[i] == NUL || raw[i] == '\n' || raw[i] == '\r') 1779 { 1780 if (p > out && p[-1] != NUL) 1781 { 1782 ++count; 1783 *p++ = NUL; 1784 } 1785 } 1786 else if (raw[i] == '%' && i + 2 < len && hexhex2nr(raw + i + 1) > 0) 1787 { 1788 *p++ = hexhex2nr(raw + i + 1); 1789 i += 2; 1790 } 1791 else 1792 *p++ = raw[i]; 1793 } 1794 if (p > out && p[-1] != NUL) 1795 { 1796 *p = NUL; /* last item didn't have \r or \n */ 1797 ++count; 1798 } 1799 return count; 1800} 1801 1802/* 1803 * Parse NUL separated "src" strings. Make it an array "outlist" form. On 1804 * this process, URI which protocol is not "file:" are removed. Return 1805 * length of array (less than "max"). 1806 */ 1807 static int 1808filter_uri_list(char_u **outlist, int max, char_u *src) 1809{ 1810 int i, j; 1811 1812 for (i = j = 0; i < max; ++i) 1813 { 1814 outlist[i] = NULL; 1815 if (STRNCMP(src, "file:", 5) == 0) 1816 { 1817 src += 5; 1818 if (STRNCMP(src, "//localhost", 11) == 0) 1819 src += 11; 1820 while (src[0] == '/' && src[1] == '/') 1821 ++src; 1822 outlist[j++] = vim_strsave(src); 1823 } 1824 src += STRLEN(src) + 1; 1825 } 1826 return j; 1827} 1828 1829 static char_u ** 1830parse_uri_list(int *count, char_u *data, int len) 1831{ 1832 int n = 0; 1833 char_u *tmp = NULL; 1834 char_u **array = NULL;; 1835 1836 if (data != NULL && len > 0 && (tmp = (char_u *)alloc(len + 1)) != NULL) 1837 { 1838 n = count_and_decode_uri_list(tmp, data, len); 1839 if (n > 0 && (array = (char_u **)alloc(n * sizeof(char_u *))) != NULL) 1840 n = filter_uri_list(array, n, tmp); 1841 } 1842 vim_free(tmp); 1843 *count = n; 1844 return array; 1845} 1846 1847 static void 1848drag_handle_uri_list(GdkDragContext *context, 1849 GtkSelectionData *data, 1850 guint time_, 1851 GdkModifierType state, 1852 gint x, 1853 gint y) 1854{ 1855 char_u **fnames; 1856 int nfiles = 0; 1857 1858 fnames = parse_uri_list(&nfiles, data->data, data->length); 1859 1860 if (fnames != NULL && nfiles > 0) 1861 { 1862 int_u modifiers; 1863 1864 gtk_drag_finish(context, TRUE, FALSE, time_); /* accept */ 1865 1866 modifiers = modifiers_gdk2mouse(state); 1867 1868 gui_handle_drop(x, y, modifiers, fnames, nfiles); 1869 } 1870 else 1871 vim_free(fnames); 1872} 1873 1874 static void 1875drag_handle_text(GdkDragContext *context, 1876 GtkSelectionData *data, 1877 guint time_, 1878 GdkModifierType state) 1879{ 1880 char_u dropkey[6] = {CSI, KS_MODIFIER, 0, CSI, KS_EXTRA, (char_u)KE_DROP}; 1881 char_u *text; 1882 int len; 1883 char_u *tmpbuf = NULL; 1884 1885 text = data->data; 1886 len = data->length; 1887 1888 if (data->type == utf8_string_atom) 1889 { 1890 if (input_conv.vc_type != CONV_NONE) 1891 tmpbuf = string_convert(&input_conv, text, &len); 1892 if (tmpbuf != NULL) 1893 text = tmpbuf; 1894 } 1895 1896 dnd_yank_drag_data(text, (long)len); 1897 gtk_drag_finish(context, TRUE, FALSE, time_); /* accept */ 1898 vim_free(tmpbuf); 1899 1900 dropkey[2] = modifiers_gdk2vim(state); 1901 1902 if (dropkey[2] != 0) 1903 add_to_input_buf(dropkey, (int)sizeof(dropkey)); 1904 else 1905 add_to_input_buf(dropkey + 3, (int)(sizeof(dropkey) - 3)); 1906} 1907 1908/* 1909 * DND receiver. 1910 */ 1911 static void 1912drag_data_received_cb(GtkWidget *widget, 1913 GdkDragContext *context, 1914 gint x, 1915 gint y, 1916 GtkSelectionData *data, 1917 guint info, 1918 guint time_, 1919 gpointer user_data UNUSED) 1920{ 1921 GdkModifierType state; 1922 1923 /* Guard against trash */ 1924 if (data->data == NULL 1925 || data->length <= 0 1926 || data->format != 8 1927 || data->data[data->length] != '\0') 1928 { 1929 gtk_drag_finish(context, FALSE, FALSE, time_); 1930 return; 1931 } 1932 1933 /* Get the current modifier state for proper distinguishment between 1934 * different operations later. */ 1935 gdk_window_get_pointer(widget->window, NULL, NULL, &state); 1936 1937 /* Not sure about the role of "text/plain" here... */ 1938 if (info == (guint)TARGET_TEXT_URI_LIST) 1939 drag_handle_uri_list(context, data, time_, state, x, y); 1940 else 1941 drag_handle_text(context, data, time_, state); 1942 1943} 1944#endif /* FEAT_DND */ 1945 1946 1947#if defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION) 1948/* 1949 * GnomeClient interact callback. Check for unsaved buffers that cannot 1950 * be abandoned and pop up a dialog asking the user for confirmation if 1951 * necessary. 1952 */ 1953 static void 1954sm_client_check_changed_any(GnomeClient *client UNUSED, 1955 gint key, 1956 GnomeDialogType type UNUSED, 1957 gpointer data UNUSED) 1958{ 1959 cmdmod_T save_cmdmod; 1960 gboolean shutdown_cancelled; 1961 1962 save_cmdmod = cmdmod; 1963 1964# ifdef FEAT_BROWSE 1965 cmdmod.browse = TRUE; 1966# endif 1967# if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) 1968 cmdmod.confirm = TRUE; 1969# endif 1970 /* 1971 * If there are changed buffers, present the user with 1972 * a dialog if possible, otherwise give an error message. 1973 */ 1974 shutdown_cancelled = check_changed_any(FALSE); 1975 1976 exiting = FALSE; 1977 cmdmod = save_cmdmod; 1978 setcursor(); /* position the cursor */ 1979 out_flush(); 1980 /* 1981 * If the user hit the [Cancel] button the whole shutdown 1982 * will be cancelled. Wow, quite powerful feature (: 1983 */ 1984 gnome_interaction_key_return(key, shutdown_cancelled); 1985} 1986 1987/* 1988 * Generate a script that can be used to restore the current editing session. 1989 * Save the value of v:this_session before running :mksession in order to make 1990 * automagic session save fully transparent. Return TRUE on success. 1991 */ 1992 static int 1993write_session_file(char_u *filename) 1994{ 1995 char_u *escaped_filename; 1996 char *mksession_cmdline; 1997 unsigned int save_ssop_flags; 1998 int failed; 1999 2000 /* 2001 * Build an ex command line to create a script that restores the current 2002 * session if executed. Escape the filename to avoid nasty surprises. 2003 */ 2004 escaped_filename = vim_strsave_escaped(filename, escape_chars); 2005 if (escaped_filename == NULL) 2006 return FALSE; 2007 mksession_cmdline = g_strconcat("mksession ", (char *)escaped_filename, 2008 NULL); 2009 vim_free(escaped_filename); 2010 2011 /* 2012 * Use a reasonable hardcoded set of 'sessionoptions' flags to avoid 2013 * unpredictable effects when the session is saved automatically. Also, 2014 * we definitely need SSOP_GLOBALS to be able to restore v:this_session. 2015 * Don't use SSOP_BUFFERS to prevent the buffer list from becoming 2016 * enormously large if the GNOME session feature is used regularly. 2017 */ 2018 save_ssop_flags = ssop_flags; 2019 ssop_flags = (SSOP_BLANK|SSOP_CURDIR|SSOP_FOLDS|SSOP_GLOBALS 2020 |SSOP_HELP|SSOP_OPTIONS|SSOP_WINSIZE|SSOP_TABPAGES); 2021 2022 do_cmdline_cmd((char_u *)"let Save_VV_this_session = v:this_session"); 2023 failed = (do_cmdline_cmd((char_u *)mksession_cmdline) == FAIL); 2024 do_cmdline_cmd((char_u *)"let v:this_session = Save_VV_this_session"); 2025 do_unlet((char_u *)"Save_VV_this_session", TRUE); 2026 2027 ssop_flags = save_ssop_flags; 2028 g_free(mksession_cmdline); 2029 /* 2030 * Reopen the file and append a command to restore v:this_session, 2031 * as if this save never happened. This is to avoid conflicts with 2032 * the user's own sessions. FIXME: It's probably less hackish to add 2033 * a "stealth" flag to 'sessionoptions' -- gotta ask Bram. 2034 */ 2035 if (!failed) 2036 { 2037 FILE *fd; 2038 2039 fd = open_exfile(filename, TRUE, APPENDBIN); 2040 2041 failed = (fd == NULL 2042 || put_line(fd, "let v:this_session = Save_VV_this_session") == FAIL 2043 || put_line(fd, "unlet Save_VV_this_session") == FAIL); 2044 2045 if (fd != NULL && fclose(fd) != 0) 2046 failed = TRUE; 2047 2048 if (failed) 2049 mch_remove(filename); 2050 } 2051 2052 return !failed; 2053} 2054 2055/* 2056 * "save_yourself" signal handler. Initiate an interaction to ask the user 2057 * for confirmation if necessary. Save the current editing session and tell 2058 * the session manager how to restart Vim. 2059 */ 2060 static gboolean 2061sm_client_save_yourself(GnomeClient *client, 2062 gint phase UNUSED, 2063 GnomeSaveStyle save_style UNUSED, 2064 gboolean shutdown UNUSED, 2065 GnomeInteractStyle interact_style, 2066 gboolean fast UNUSED, 2067 gpointer data UNUSED) 2068{ 2069 static const char suffix[] = "-session.vim"; 2070 char *session_file; 2071 unsigned int len; 2072 gboolean success; 2073 2074 /* Always request an interaction if possible. check_changed_any() 2075 * won't actually show a dialog unless any buffers have been modified. 2076 * There doesn't seem to be an obvious way to check that without 2077 * automatically firing the dialog. Anyway, it works just fine. */ 2078 if (interact_style == GNOME_INTERACT_ANY) 2079 gnome_client_request_interaction(client, GNOME_DIALOG_NORMAL, 2080 &sm_client_check_changed_any, 2081 NULL); 2082 out_flush(); 2083 ml_sync_all(FALSE, FALSE); /* preserve all swap files */ 2084 2085 /* The path is unique for each session save. We do neither know nor care 2086 * which session script will actually be used later. This decision is in 2087 * the domain of the session manager. */ 2088 session_file = gnome_config_get_real_path( 2089 gnome_client_get_config_prefix(client)); 2090 len = strlen(session_file); 2091 2092 if (len > 0 && session_file[len-1] == G_DIR_SEPARATOR) 2093 --len; /* get rid of the superfluous trailing '/' */ 2094 2095 session_file = g_renew(char, session_file, len + sizeof(suffix)); 2096 memcpy(session_file + len, suffix, sizeof(suffix)); 2097 2098 success = write_session_file((char_u *)session_file); 2099 2100 if (success) 2101 { 2102 const char *argv[8]; 2103 int i; 2104 2105 /* Tell the session manager how to wipe out the stored session data. 2106 * This isn't as dangerous as it looks, don't worry :) session_file 2107 * is a unique absolute filename. Usually it'll be something like 2108 * `/home/user/.gnome2/vim-XXXXXX-session.vim'. */ 2109 i = 0; 2110 argv[i++] = "rm"; 2111 argv[i++] = session_file; 2112 argv[i] = NULL; 2113 2114 gnome_client_set_discard_command(client, i, (char **)argv); 2115 2116 /* Tell the session manager how to restore the just saved session. 2117 * This is easily done thanks to Vim's -S option. Pass the -f flag 2118 * since there's no need to fork -- it might even cause confusion. 2119 * Also pass the window role to give the WM something to match on. 2120 * The role is set in gui_mch_open(), thus should _never_ be NULL. */ 2121 i = 0; 2122 argv[i++] = restart_command; 2123 argv[i++] = "-f"; 2124 argv[i++] = "-g"; 2125 argv[i++] = "--role"; 2126 argv[i++] = gtk_window_get_role(GTK_WINDOW(gui.mainwin)); 2127 argv[i++] = "-S"; 2128 argv[i++] = session_file; 2129 argv[i] = NULL; 2130 2131 gnome_client_set_restart_command(client, i, (char **)argv); 2132 gnome_client_set_clone_command(client, 0, NULL); 2133 } 2134 2135 g_free(session_file); 2136 2137 return success; 2138} 2139 2140/* 2141 * Called when the session manager wants us to die. There isn't much to save 2142 * here since "save_yourself" has been emitted before (unless serious trouble 2143 * is happening). 2144 */ 2145 static void 2146sm_client_die(GnomeClient *client UNUSED, gpointer data UNUSED) 2147{ 2148 /* Don't write messages to the GUI anymore */ 2149 full_screen = FALSE; 2150 2151 vim_strncpy(IObuff, (char_u *) 2152 _("Vim: Received \"die\" request from session manager\n"), 2153 IOSIZE - 1); 2154 preserve_exit(); 2155} 2156 2157/* 2158 * Connect our signal handlers to be notified on session save and shutdown. 2159 */ 2160 static void 2161setup_save_yourself(void) 2162{ 2163 GnomeClient *client; 2164 2165 client = gnome_master_client(); 2166 2167 if (client != NULL) 2168 { 2169 /* Must use the deprecated gtk_signal_connect() for compatibility 2170 * with GNOME 1. Arrgh, zombies! */ 2171 gtk_signal_connect(GTK_OBJECT(client), "save_yourself", 2172 GTK_SIGNAL_FUNC(&sm_client_save_yourself), NULL); 2173 gtk_signal_connect(GTK_OBJECT(client), "die", 2174 GTK_SIGNAL_FUNC(&sm_client_die), NULL); 2175 } 2176} 2177 2178#else /* !(FEAT_GUI_GNOME && FEAT_SESSION) */ 2179 2180# ifdef USE_XSMP 2181/* 2182 * GTK tells us that XSMP needs attention 2183 */ 2184 static gboolean 2185local_xsmp_handle_requests(source, condition, data) 2186 GIOChannel *source UNUSED; 2187 GIOCondition condition; 2188 gpointer data; 2189{ 2190 if (condition == G_IO_IN) 2191 { 2192 /* Do stuff; maybe close connection */ 2193 if (xsmp_handle_requests() == FAIL) 2194 g_io_channel_unref((GIOChannel *)data); 2195 return TRUE; 2196 } 2197 /* Error */ 2198 g_io_channel_unref((GIOChannel *)data); 2199 xsmp_close(); 2200 return TRUE; 2201} 2202# endif /* USE_XSMP */ 2203 2204/* 2205 * Setup the WM_PROTOCOLS to indicate we want the WM_SAVE_YOURSELF event. 2206 * This is an ugly use of X functions. GTK doesn't offer an alternative. 2207 */ 2208 static void 2209setup_save_yourself(void) 2210{ 2211 Atom *existing_atoms = NULL; 2212 int count = 0; 2213 2214#ifdef USE_XSMP 2215 if (xsmp_icefd != -1) 2216 { 2217 /* 2218 * Use XSMP is preference to legacy WM_SAVE_YOURSELF; 2219 * set up GTK IO monitor 2220 */ 2221 GIOChannel *g_io = g_io_channel_unix_new(xsmp_icefd); 2222 2223 g_io_add_watch(g_io, G_IO_IN | G_IO_ERR | G_IO_HUP, 2224 local_xsmp_handle_requests, (gpointer)g_io); 2225 } 2226 else 2227#endif 2228 { 2229 /* Fall back to old method */ 2230 2231 /* first get the existing value */ 2232 if (XGetWMProtocols(GDK_WINDOW_XDISPLAY(gui.mainwin->window), 2233 GDK_WINDOW_XWINDOW(gui.mainwin->window), 2234 &existing_atoms, &count)) 2235 { 2236 Atom *new_atoms; 2237 Atom save_yourself_xatom; 2238 int i; 2239 2240 save_yourself_xatom = GET_X_ATOM(save_yourself_atom); 2241 2242 /* check if WM_SAVE_YOURSELF isn't there yet */ 2243 for (i = 0; i < count; ++i) 2244 if (existing_atoms[i] == save_yourself_xatom) 2245 break; 2246 2247 if (i == count) 2248 { 2249 /* allocate an Atoms array which is one item longer */ 2250 new_atoms = (Atom *)alloc((unsigned)((count + 1) 2251 * sizeof(Atom))); 2252 if (new_atoms != NULL) 2253 { 2254 memcpy(new_atoms, existing_atoms, count * sizeof(Atom)); 2255 new_atoms[count] = save_yourself_xatom; 2256 XSetWMProtocols(GDK_WINDOW_XDISPLAY(gui.mainwin->window), 2257 GDK_WINDOW_XWINDOW(gui.mainwin->window), 2258 new_atoms, count + 1); 2259 vim_free(new_atoms); 2260 } 2261 } 2262 XFree(existing_atoms); 2263 } 2264 } 2265} 2266 2267/* 2268 * Installing a global event filter seems to be the only way to catch 2269 * client messages of type WM_PROTOCOLS without overriding GDK's own 2270 * client message event filter. Well, that's still better than trying 2271 * to guess what the GDK filter had done if it had been invoked instead 2272 * 2273 * GTK2_FIXME: This doesn't seem to work. For some reason we never 2274 * receive WM_SAVE_YOURSELF even though everything is set up correctly. 2275 * I have the nasty feeling modern session managers just don't send this 2276 * deprecated message anymore. Addition: confirmed by several people. 2277 * 2278 * The GNOME session support is much cooler anyway. Unlike this ugly 2279 * WM_SAVE_YOURSELF hack it actually stores the session... And yes, 2280 * it should work with KDE as well. 2281 */ 2282 static GdkFilterReturn 2283global_event_filter(GdkXEvent *xev, 2284 GdkEvent *event UNUSED, 2285 gpointer data UNUSED) 2286{ 2287 XEvent *xevent = (XEvent *)xev; 2288 2289 if (xevent != NULL 2290 && xevent->type == ClientMessage 2291 && xevent->xclient.message_type == GET_X_ATOM(wm_protocols_atom) 2292 && (long_u)xevent->xclient.data.l[0] 2293 == GET_X_ATOM(save_yourself_atom)) 2294 { 2295 out_flush(); 2296 ml_sync_all(FALSE, FALSE); /* preserve all swap files */ 2297 /* 2298 * Set the window's WM_COMMAND property, to let the window manager 2299 * know we are done saving ourselves. We don't want to be 2300 * restarted, thus set argv to NULL. 2301 */ 2302 XSetCommand(GDK_WINDOW_XDISPLAY(gui.mainwin->window), 2303 GDK_WINDOW_XWINDOW(gui.mainwin->window), 2304 NULL, 0); 2305 return GDK_FILTER_REMOVE; 2306 } 2307 2308 return GDK_FILTER_CONTINUE; 2309} 2310#endif /* !(FEAT_GUI_GNOME && FEAT_SESSION) */ 2311 2312 2313/* 2314 * Setup the window icon & xcmdsrv comm after the main window has been realized. 2315 */ 2316 static void 2317mainwin_realize(GtkWidget *widget UNUSED, gpointer data UNUSED) 2318{ 2319/* If you get an error message here, you still need to unpack the runtime 2320 * archive! */ 2321#ifdef magick 2322# undef magick 2323#endif 2324 /* A bit hackish, but avoids casting later and allows optimization */ 2325# define static static const 2326#define magick vim32x32 2327#include "../runtime/vim32x32.xpm" 2328#undef magick 2329#define magick vim16x16 2330#include "../runtime/vim16x16.xpm" 2331#undef magick 2332#define magick vim48x48 2333#include "../runtime/vim48x48.xpm" 2334#undef magick 2335# undef static 2336 2337 /* When started with "--echo-wid" argument, write window ID on stdout. */ 2338 if (echo_wid_arg) 2339 { 2340 printf("WID: %ld\n", (long)GDK_WINDOW_XWINDOW(gui.mainwin->window)); 2341 fflush(stdout); 2342 } 2343 2344 if (vim_strchr(p_go, GO_ICON) != NULL) 2345 { 2346 /* 2347 * Add an icon to the main window. For fun and convenience of the user. 2348 */ 2349 GList *icons = NULL; 2350 2351 icons = g_list_prepend(icons, gdk_pixbuf_new_from_xpm_data(vim16x16)); 2352 icons = g_list_prepend(icons, gdk_pixbuf_new_from_xpm_data(vim32x32)); 2353 icons = g_list_prepend(icons, gdk_pixbuf_new_from_xpm_data(vim48x48)); 2354 2355 gtk_window_set_icon_list(GTK_WINDOW(gui.mainwin), icons); 2356 2357 g_list_foreach(icons, (GFunc)&g_object_unref, NULL); 2358 g_list_free(icons); 2359 } 2360 2361#if !(defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION)) 2362 /* Register a handler for WM_SAVE_YOURSELF with GDK's low-level X I/F */ 2363 gdk_window_add_filter(NULL, &global_event_filter, NULL); 2364#endif 2365 /* Setup to indicate to the window manager that we want to catch the 2366 * WM_SAVE_YOURSELF event. For GNOME, this connects to the session 2367 * manager instead. */ 2368#if defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION) 2369 if (using_gnome) 2370#endif 2371 setup_save_yourself(); 2372 2373#ifdef FEAT_CLIENTSERVER 2374 if (serverName == NULL && serverDelayedStartName != NULL) 2375 { 2376 /* This is a :gui command in a plain vim with no previous server */ 2377 commWindow = GDK_WINDOW_XWINDOW(gui.mainwin->window); 2378 2379 (void)serverRegisterName(GDK_WINDOW_XDISPLAY(gui.mainwin->window), 2380 serverDelayedStartName); 2381 } 2382 else 2383 { 2384 /* 2385 * Cannot handle "XLib-only" windows with gtk event routines, we'll 2386 * have to change the "server" registration to that of the main window 2387 * If we have not registered a name yet, remember the window 2388 */ 2389 serverChangeRegisteredWindow(GDK_WINDOW_XDISPLAY(gui.mainwin->window), 2390 GDK_WINDOW_XWINDOW(gui.mainwin->window)); 2391 } 2392 gtk_widget_add_events(gui.mainwin, GDK_PROPERTY_CHANGE_MASK); 2393 gtk_signal_connect(GTK_OBJECT(gui.mainwin), "property_notify_event", 2394 GTK_SIGNAL_FUNC(property_event), NULL); 2395#endif 2396} 2397 2398 static GdkCursor * 2399create_blank_pointer(void) 2400{ 2401 GdkWindow *root_window = NULL; 2402 GdkPixmap *blank_mask; 2403 GdkCursor *cursor; 2404 GdkColor color = { 0, 0, 0, 0 }; 2405 char blank_data[] = { 0x0 }; 2406 2407#ifdef HAVE_GTK_MULTIHEAD 2408 root_window = gtk_widget_get_root_window(gui.mainwin); 2409#endif 2410 2411 /* Create a pseudo blank pointer, which is in fact one pixel by one pixel 2412 * in size. */ 2413 blank_mask = gdk_bitmap_create_from_data(root_window, blank_data, 1, 1); 2414 cursor = gdk_cursor_new_from_pixmap(blank_mask, blank_mask, 2415 &color, &color, 0, 0); 2416 gdk_bitmap_unref(blank_mask); 2417 2418 return cursor; 2419} 2420 2421#ifdef HAVE_GTK_MULTIHEAD 2422 static void 2423mainwin_screen_changed_cb(GtkWidget *widget, 2424 GdkScreen *previous_screen UNUSED, 2425 gpointer data UNUSED) 2426{ 2427 if (!gtk_widget_has_screen(widget)) 2428 return; 2429 2430 /* 2431 * Recreate the invisible mouse cursor. 2432 */ 2433 if (gui.blank_pointer != NULL) 2434 gdk_cursor_unref(gui.blank_pointer); 2435 2436 gui.blank_pointer = create_blank_pointer(); 2437 2438 if (gui.pointer_hidden && gui.drawarea->window != NULL) 2439 gdk_window_set_cursor(gui.drawarea->window, gui.blank_pointer); 2440 2441 /* 2442 * Create a new PangoContext for this screen, and initialize it 2443 * with the current font if necessary. 2444 */ 2445 if (gui.text_context != NULL) 2446 g_object_unref(gui.text_context); 2447 2448 gui.text_context = gtk_widget_create_pango_context(widget); 2449 pango_context_set_base_dir(gui.text_context, PANGO_DIRECTION_LTR); 2450 2451 if (gui.norm_font != NULL) 2452 { 2453 gui_mch_init_font(p_guifont, FALSE); 2454 gui_set_shellsize(FALSE, FALSE, RESIZE_BOTH); 2455 } 2456} 2457#endif /* HAVE_GTK_MULTIHEAD */ 2458 2459/* 2460 * After the drawing area comes up, we calculate all colors and create the 2461 * dummy blank cursor. 2462 * 2463 * Don't try to set any VIM scrollbar sizes anywhere here. I'm relying on the 2464 * fact that the main VIM engine doesn't take them into account anywhere. 2465 */ 2466 static void 2467drawarea_realize_cb(GtkWidget *widget, gpointer data UNUSED) 2468{ 2469 GtkWidget *sbar; 2470 2471#ifdef FEAT_XIM 2472 xim_init(); 2473#endif 2474 gui_mch_new_colors(); 2475 gui.text_gc = gdk_gc_new(gui.drawarea->window); 2476 2477 gui.blank_pointer = create_blank_pointer(); 2478 if (gui.pointer_hidden) 2479 gdk_window_set_cursor(widget->window, gui.blank_pointer); 2480 2481 /* get the actual size of the scrollbars, if they are realized */ 2482 sbar = firstwin->w_scrollbars[SBAR_LEFT].id; 2483 if (!sbar || (!gui.which_scrollbars[SBAR_LEFT] 2484 && firstwin->w_scrollbars[SBAR_RIGHT].id)) 2485 sbar = firstwin->w_scrollbars[SBAR_RIGHT].id; 2486 if (sbar && GTK_WIDGET_REALIZED(sbar) && sbar->allocation.width) 2487 gui.scrollbar_width = sbar->allocation.width; 2488 2489 sbar = gui.bottom_sbar.id; 2490 if (sbar && GTK_WIDGET_REALIZED(sbar) && sbar->allocation.height) 2491 gui.scrollbar_height = sbar->allocation.height; 2492} 2493 2494/* 2495 * Properly clean up on shutdown. 2496 */ 2497 static void 2498drawarea_unrealize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED) 2499{ 2500 /* Don't write messages to the GUI anymore */ 2501 full_screen = FALSE; 2502 2503#ifdef FEAT_XIM 2504 im_shutdown(); 2505#endif 2506 if (gui.ascii_glyphs != NULL) 2507 { 2508 pango_glyph_string_free(gui.ascii_glyphs); 2509 gui.ascii_glyphs = NULL; 2510 } 2511 if (gui.ascii_font != NULL) 2512 { 2513 g_object_unref(gui.ascii_font); 2514 gui.ascii_font = NULL; 2515 } 2516 g_object_unref(gui.text_context); 2517 gui.text_context = NULL; 2518 2519 g_object_unref(gui.text_gc); 2520 gui.text_gc = NULL; 2521 2522 gdk_cursor_unref(gui.blank_pointer); 2523 gui.blank_pointer = NULL; 2524} 2525 2526 static void 2527drawarea_style_set_cb(GtkWidget *widget UNUSED, 2528 GtkStyle *previous_style UNUSED, 2529 gpointer data UNUSED) 2530{ 2531 gui_mch_new_colors(); 2532} 2533 2534/* 2535 * Callback routine for the "delete_event" signal on the toplevel window. 2536 * Tries to vim gracefully, or refuses to exit with changed buffers. 2537 */ 2538 static gint 2539delete_event_cb(GtkWidget *widget UNUSED, 2540 GdkEventAny *event UNUSED, 2541 gpointer data UNUSED) 2542{ 2543 gui_shell_closed(); 2544 return TRUE; 2545} 2546 2547#if defined(FEAT_MENU) || defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE) 2548 static int 2549get_item_dimensions(GtkWidget *widget, GtkOrientation orientation) 2550{ 2551 GtkOrientation item_orientation = GTK_ORIENTATION_HORIZONTAL; 2552 2553#ifdef FEAT_GUI_GNOME 2554 if (using_gnome && widget != NULL) 2555 { 2556 GtkWidget *parent; 2557 BonoboDockItem *dockitem; 2558 2559 parent = gtk_widget_get_parent(widget); 2560 if (G_TYPE_FROM_INSTANCE(parent) == BONOBO_TYPE_DOCK_ITEM) 2561 { 2562 /* Only menu & toolbar are dock items. Could tabline be? 2563 * Seem to be only the 2 defined in GNOME */ 2564 widget = parent; 2565 dockitem = BONOBO_DOCK_ITEM(widget); 2566 2567 if (dockitem == NULL || dockitem->is_floating) 2568 return 0; 2569 item_orientation = bonobo_dock_item_get_orientation(dockitem); 2570 } 2571 } 2572#endif 2573 if (widget != NULL 2574 && item_orientation == orientation 2575 && GTK_WIDGET_REALIZED(widget) 2576 && GTK_WIDGET_VISIBLE(widget)) 2577 { 2578 if (orientation == GTK_ORIENTATION_HORIZONTAL) 2579 return widget->allocation.height; 2580 else 2581 return widget->allocation.width; 2582 } 2583 return 0; 2584} 2585#endif 2586 2587 static int 2588get_menu_tool_width(void) 2589{ 2590 int width = 0; 2591 2592#ifdef FEAT_GUI_GNOME /* these are never vertical without GNOME */ 2593# ifdef FEAT_MENU 2594 width += get_item_dimensions(gui.menubar, GTK_ORIENTATION_VERTICAL); 2595# endif 2596# ifdef FEAT_TOOLBAR 2597 width += get_item_dimensions(gui.toolbar, GTK_ORIENTATION_VERTICAL); 2598# endif 2599# ifdef FEAT_GUI_TABLINE 2600 if (gui.tabline != NULL) 2601 width += get_item_dimensions(gui.tabline, GTK_ORIENTATION_VERTICAL); 2602# endif 2603#endif 2604 2605 return width; 2606} 2607 2608 static int 2609get_menu_tool_height(void) 2610{ 2611 int height = 0; 2612 2613#ifdef FEAT_MENU 2614 height += get_item_dimensions(gui.menubar, GTK_ORIENTATION_HORIZONTAL); 2615#endif 2616#ifdef FEAT_TOOLBAR 2617 height += get_item_dimensions(gui.toolbar, GTK_ORIENTATION_HORIZONTAL); 2618#endif 2619#ifdef FEAT_GUI_TABLINE 2620 if (gui.tabline != NULL) 2621 height += get_item_dimensions(gui.tabline, GTK_ORIENTATION_HORIZONTAL); 2622#endif 2623 2624 return height; 2625} 2626 2627/* This controls whether we can set the real window hints at 2628 * start-up when in a GtkPlug. 2629 * 0 = normal processing (default) 2630 * 1 = init. hints set, no-one's tried to reset since last check 2631 * 2 = init. hints set, attempt made to change hints 2632 */ 2633static int init_window_hints_state = 0; 2634 2635 static void 2636update_window_manager_hints(int force_width, int force_height) 2637{ 2638 static int old_width = 0; 2639 static int old_height = 0; 2640 static int old_min_width = 0; 2641 static int old_min_height = 0; 2642 static int old_char_width = 0; 2643 static int old_char_height = 0; 2644 2645 int width; 2646 int height; 2647 int min_width; 2648 int min_height; 2649 2650 /* At start-up, don't try to set the hints until the initial 2651 * values have been used (those that dictate our initial size) 2652 * Let forced (i.e., correct) values through always. 2653 */ 2654 if (!(force_width && force_height) && init_window_hints_state > 0) 2655 { 2656 /* Don't do it! */ 2657 init_window_hints_state = 2; 2658 return; 2659 } 2660 2661 /* This also needs to be done when the main window isn't there yet, 2662 * otherwise the hints don't work. */ 2663 width = gui_get_base_width(); 2664 height = gui_get_base_height(); 2665# ifdef FEAT_MENU 2666 height += tabline_height() * gui.char_height; 2667# endif 2668 width += get_menu_tool_width(); 2669 height += get_menu_tool_height(); 2670 2671 /* GtkSockets use GtkPlug's [gui,mainwin] min-size hints to determine 2672 * their actual widget size. When we set our size ourselves (e.g., 2673 * 'set columns=' or init. -geom) we briefly set the min. to the size 2674 * we wish to be instead of the legitimate minimum so that we actually 2675 * resize correctly. 2676 */ 2677 if (force_width && force_height) 2678 { 2679 min_width = force_width; 2680 min_height = force_height; 2681 } 2682 else 2683 { 2684 min_width = width + MIN_COLUMNS * gui.char_width; 2685 min_height = height + MIN_LINES * gui.char_height; 2686 } 2687 2688 /* Avoid an expose event when the size didn't change. */ 2689 if (width != old_width 2690 || height != old_height 2691 || min_width != old_min_width 2692 || min_height != old_min_height 2693 || gui.char_width != old_char_width 2694 || gui.char_height != old_char_height) 2695 { 2696 GdkGeometry geometry; 2697 GdkWindowHints geometry_mask; 2698 2699 geometry.width_inc = gui.char_width; 2700 geometry.height_inc = gui.char_height; 2701 geometry.base_width = width; 2702 geometry.base_height = height; 2703 geometry.min_width = min_width; 2704 geometry.min_height = min_height; 2705 geometry_mask = GDK_HINT_BASE_SIZE|GDK_HINT_RESIZE_INC 2706 |GDK_HINT_MIN_SIZE; 2707 /* Using gui.formwin as geometry widget doesn't work as expected 2708 * with GTK+ 2 -- dunno why. Presumably all the resizing hacks 2709 * in Vim confuse GTK+. */ 2710 gtk_window_set_geometry_hints(GTK_WINDOW(gui.mainwin), gui.mainwin, 2711 &geometry, geometry_mask); 2712 old_width = width; 2713 old_height = height; 2714 old_min_width = min_width; 2715 old_min_height = min_height; 2716 old_char_width = gui.char_width; 2717 old_char_height = gui.char_height; 2718 } 2719} 2720 2721#ifdef FEAT_TOOLBAR 2722 2723/* 2724 * This extra effort wouldn't be necessary if we only used stock icons in the 2725 * toolbar, as we do for all builtin icons. But user-defined toolbar icons 2726 * shouldn't be treated differently, thus we do need this. 2727 */ 2728 static void 2729icon_size_changed_foreach(GtkWidget *widget, gpointer user_data) 2730{ 2731 if (GTK_IS_IMAGE(widget)) 2732 { 2733 GtkImage *image = (GtkImage *)widget; 2734 2735 /* User-defined icons are stored in a GtkIconSet */ 2736 if (gtk_image_get_storage_type(image) == GTK_IMAGE_ICON_SET) 2737 { 2738 GtkIconSet *icon_set; 2739 GtkIconSize icon_size; 2740 2741 gtk_image_get_icon_set(image, &icon_set, &icon_size); 2742 icon_size = (GtkIconSize)(long)user_data; 2743 2744 gtk_icon_set_ref(icon_set); 2745 gtk_image_set_from_icon_set(image, icon_set, icon_size); 2746 gtk_icon_set_unref(icon_set); 2747 } 2748 } 2749 else if (GTK_IS_CONTAINER(widget)) 2750 { 2751 gtk_container_foreach((GtkContainer *)widget, 2752 &icon_size_changed_foreach, 2753 user_data); 2754 } 2755} 2756 2757 static void 2758set_toolbar_style(GtkToolbar *toolbar) 2759{ 2760 GtkToolbarStyle style; 2761 GtkIconSize size; 2762 GtkIconSize oldsize; 2763 2764 if ((toolbar_flags & (TOOLBAR_TEXT | TOOLBAR_ICONS | TOOLBAR_HORIZ)) 2765 == (TOOLBAR_TEXT | TOOLBAR_ICONS | TOOLBAR_HORIZ)) 2766 style = GTK_TOOLBAR_BOTH_HORIZ; 2767 else if ((toolbar_flags & (TOOLBAR_TEXT | TOOLBAR_ICONS)) 2768 == (TOOLBAR_TEXT | TOOLBAR_ICONS)) 2769 style = GTK_TOOLBAR_BOTH; 2770 else if (toolbar_flags & TOOLBAR_TEXT) 2771 style = GTK_TOOLBAR_TEXT; 2772 else 2773 style = GTK_TOOLBAR_ICONS; 2774 2775 gtk_toolbar_set_style(toolbar, style); 2776 gtk_toolbar_set_tooltips(toolbar, (toolbar_flags & TOOLBAR_TOOLTIPS) != 0); 2777 2778 switch (tbis_flags) 2779 { 2780 case TBIS_TINY: size = GTK_ICON_SIZE_MENU; break; 2781 case TBIS_SMALL: size = GTK_ICON_SIZE_SMALL_TOOLBAR; break; 2782 case TBIS_MEDIUM: size = GTK_ICON_SIZE_BUTTON; break; 2783 case TBIS_LARGE: size = GTK_ICON_SIZE_LARGE_TOOLBAR; break; 2784 default: size = GTK_ICON_SIZE_INVALID; break; 2785 } 2786 oldsize = gtk_toolbar_get_icon_size(toolbar); 2787 2788 if (size == GTK_ICON_SIZE_INVALID) 2789 { 2790 /* Let global user preferences decide the icon size. */ 2791 gtk_toolbar_unset_icon_size(toolbar); 2792 size = gtk_toolbar_get_icon_size(toolbar); 2793 } 2794 if (size != oldsize) 2795 { 2796 gtk_container_foreach(GTK_CONTAINER(toolbar), 2797 &icon_size_changed_foreach, 2798 GINT_TO_POINTER((int)size)); 2799 } 2800 gtk_toolbar_set_icon_size(toolbar, size); 2801} 2802 2803#endif /* FEAT_TOOLBAR */ 2804 2805#if defined(FEAT_GUI_TABLINE) || defined(PROTO) 2806static int ignore_tabline_evt = FALSE; 2807static GtkWidget *tabline_menu; 2808static GtkTooltips *tabline_tooltip; 2809static int clicked_page; /* page clicked in tab line */ 2810 2811/* 2812 * Handle selecting an item in the tab line popup menu. 2813 */ 2814 static void 2815tabline_menu_handler(GtkMenuItem *item UNUSED, gpointer user_data) 2816{ 2817 /* Add the string cmd into input buffer */ 2818 send_tabline_menu_event(clicked_page, (int)(long)user_data); 2819} 2820 2821 static void 2822add_tabline_menu_item(GtkWidget *menu, char_u *text, int resp) 2823{ 2824 GtkWidget *item; 2825 char_u *utf_text; 2826 2827 utf_text = CONVERT_TO_UTF8(text); 2828 item = gtk_menu_item_new_with_label((const char *)utf_text); 2829 gtk_widget_show(item); 2830 CONVERT_TO_UTF8_FREE(utf_text); 2831 2832 gtk_container_add(GTK_CONTAINER(menu), item); 2833 gtk_signal_connect(GTK_OBJECT(item), "activate", 2834 GTK_SIGNAL_FUNC(tabline_menu_handler), 2835 (gpointer)(long)resp); 2836} 2837 2838/* 2839 * Create a menu for the tab line. 2840 */ 2841 static GtkWidget * 2842create_tabline_menu(void) 2843{ 2844 GtkWidget *menu; 2845 2846 menu = gtk_menu_new(); 2847 add_tabline_menu_item(menu, (char_u *)_("Close"), TABLINE_MENU_CLOSE); 2848 add_tabline_menu_item(menu, (char_u *)_("New tab"), TABLINE_MENU_NEW); 2849 add_tabline_menu_item(menu, (char_u *)_("Open Tab..."), TABLINE_MENU_OPEN); 2850 2851 return menu; 2852} 2853 2854 static gboolean 2855on_tabline_menu(GtkWidget *widget, GdkEvent *event) 2856{ 2857 /* Was this button press event ? */ 2858 if (event->type == GDK_BUTTON_PRESS) 2859 { 2860 GdkEventButton *bevent = (GdkEventButton *)event; 2861 int x = bevent->x; 2862 int y = bevent->y; 2863 GtkWidget *tabwidget; 2864 GdkWindow *tabwin; 2865 2866 /* When ignoring events return TRUE so that the selected page doesn't 2867 * change. */ 2868 if (hold_gui_events 2869# ifdef FEAT_CMDWIN 2870 || cmdwin_type != 0 2871# endif 2872 ) 2873 return TRUE; 2874 2875 tabwin = gdk_window_at_pointer(&x, &y); 2876 gdk_window_get_user_data(tabwin, (gpointer)&tabwidget); 2877 clicked_page = (int)(long)gtk_object_get_user_data( 2878 GTK_OBJECT(tabwidget)); 2879 2880 /* If the event was generated for 3rd button popup the menu. */ 2881 if (bevent->button == 3) 2882 { 2883 gtk_menu_popup(GTK_MENU(widget), NULL, NULL, NULL, NULL, 2884 bevent->button, bevent->time); 2885 /* We handled the event. */ 2886 return TRUE; 2887 } 2888 else if (bevent->button == 1) 2889 { 2890 if (clicked_page == 0) 2891 { 2892 /* Click after all tabs moves to next tab page. When "x" is 2893 * small guess it's the left button. */ 2894 send_tabline_event(x < 50 ? -1 : 0); 2895 } 2896 } 2897 } 2898 2899 /* We didn't handle the event. */ 2900 return FALSE; 2901} 2902 2903/* 2904 * Handle selecting one of the tabs. 2905 */ 2906 static void 2907on_select_tab( 2908 GtkNotebook *notebook UNUSED, 2909 GtkNotebookPage *page UNUSED, 2910 gint idx, 2911 gpointer data UNUSED) 2912{ 2913 if (!ignore_tabline_evt) 2914 { 2915 send_tabline_event(idx + 1); 2916 } 2917} 2918 2919/* 2920 * Show or hide the tabline. 2921 */ 2922 void 2923gui_mch_show_tabline(int showit) 2924{ 2925 if (gui.tabline == NULL) 2926 return; 2927 2928 if (!showit != !gtk_notebook_get_show_tabs(GTK_NOTEBOOK(gui.tabline))) 2929 { 2930 /* Note: this may cause a resize event */ 2931 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gui.tabline), showit); 2932 update_window_manager_hints(0, 0); 2933 if (showit) 2934 GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(gui.tabline), GTK_CAN_FOCUS); 2935 } 2936 2937 gui_mch_update(); 2938} 2939 2940/* 2941 * Return TRUE when tabline is displayed. 2942 */ 2943 int 2944gui_mch_showing_tabline(void) 2945{ 2946 return gui.tabline != NULL 2947 && gtk_notebook_get_show_tabs(GTK_NOTEBOOK(gui.tabline)); 2948} 2949 2950/* 2951 * Update the labels of the tabline. 2952 */ 2953 void 2954gui_mch_update_tabline(void) 2955{ 2956 GtkWidget *page; 2957 GtkWidget *event_box; 2958 GtkWidget *label; 2959 tabpage_T *tp; 2960 int nr = 0; 2961 int tab_num; 2962 int curtabidx = 0; 2963 char_u *labeltext; 2964 2965 if (gui.tabline == NULL) 2966 return; 2967 2968 ignore_tabline_evt = TRUE; 2969 2970 /* Add a label for each tab page. They all contain the same text area. */ 2971 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr) 2972 { 2973 if (tp == curtab) 2974 curtabidx = nr; 2975 2976 tab_num = nr + 1; 2977 2978 page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(gui.tabline), nr); 2979 if (page == NULL) 2980 { 2981 /* Add notebook page */ 2982 page = gtk_vbox_new(FALSE, 0); 2983 gtk_widget_show(page); 2984 event_box = gtk_event_box_new(); 2985 gtk_widget_show(event_box); 2986 label = gtk_label_new("-Empty-"); 2987 gtk_misc_set_padding(GTK_MISC(label), 2, 2); 2988 gtk_container_add(GTK_CONTAINER(event_box), label); 2989 gtk_widget_show(label); 2990 gtk_notebook_insert_page(GTK_NOTEBOOK(gui.tabline), 2991 page, 2992 event_box, 2993 nr++); 2994 } 2995 2996 event_box = gtk_notebook_get_tab_label(GTK_NOTEBOOK(gui.tabline), page); 2997 gtk_object_set_user_data(GTK_OBJECT(event_box), 2998 (gpointer)(long)tab_num); 2999 label = GTK_BIN(event_box)->child; 3000 get_tabline_label(tp, FALSE); 3001 labeltext = CONVERT_TO_UTF8(NameBuff); 3002 gtk_label_set_text(GTK_LABEL(label), (const char *)labeltext); 3003 CONVERT_TO_UTF8_FREE(labeltext); 3004 3005 get_tabline_label(tp, TRUE); 3006 labeltext = CONVERT_TO_UTF8(NameBuff); 3007 gtk_tooltips_set_tip(GTK_TOOLTIPS(tabline_tooltip), event_box, 3008 (const char *)labeltext, NULL); 3009 CONVERT_TO_UTF8_FREE(labeltext); 3010 } 3011 3012 /* Remove any old labels. */ 3013 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(gui.tabline), nr) != NULL) 3014 gtk_notebook_remove_page(GTK_NOTEBOOK(gui.tabline), nr); 3015 3016 if (gtk_notebook_current_page(GTK_NOTEBOOK(gui.tabline)) != curtabidx) 3017 gtk_notebook_set_page(GTK_NOTEBOOK(gui.tabline), curtabidx); 3018 3019 /* Make sure everything is in place before drawing text. */ 3020 gui_mch_update(); 3021 3022 ignore_tabline_evt = FALSE; 3023} 3024 3025/* 3026 * Set the current tab to "nr". First tab is 1. 3027 */ 3028 void 3029gui_mch_set_curtab(nr) 3030 int nr; 3031{ 3032 if (gui.tabline == NULL) 3033 return; 3034 3035 ignore_tabline_evt = TRUE; 3036 if (gtk_notebook_current_page(GTK_NOTEBOOK(gui.tabline)) != nr - 1) 3037 gtk_notebook_set_page(GTK_NOTEBOOK(gui.tabline), nr - 1); 3038 ignore_tabline_evt = FALSE; 3039} 3040 3041#endif /* FEAT_GUI_TABLINE */ 3042 3043/* 3044 * Add selection targets for PRIMARY and CLIPBOARD selections. 3045 */ 3046 void 3047gui_gtk_set_selection_targets(void) 3048{ 3049 int i, j = 0; 3050 int n_targets = N_SELECTION_TARGETS; 3051 GtkTargetEntry targets[N_SELECTION_TARGETS]; 3052 3053 for (i = 0; i < (int)N_SELECTION_TARGETS; ++i) 3054 { 3055 /* OpenOffice tries to use TARGET_HTML and fails when it doesn't 3056 * return something, instead of trying another target. Therefore only 3057 * offer TARGET_HTML when it works. */ 3058 if (!clip_html && selection_targets[i].info == TARGET_HTML) 3059 n_targets--; 3060 else 3061 targets[j++] = selection_targets[i]; 3062 } 3063 3064 gtk_selection_clear_targets(gui.drawarea, (GdkAtom)GDK_SELECTION_PRIMARY); 3065 gtk_selection_clear_targets(gui.drawarea, (GdkAtom)clip_plus.gtk_sel_atom); 3066 gtk_selection_add_targets(gui.drawarea, 3067 (GdkAtom)GDK_SELECTION_PRIMARY, 3068 targets, n_targets); 3069 gtk_selection_add_targets(gui.drawarea, 3070 (GdkAtom)clip_plus.gtk_sel_atom, 3071 targets, n_targets); 3072} 3073 3074/* 3075 * Set up for receiving DND items. 3076 */ 3077 void 3078gui_gtk_set_dnd_targets(void) 3079{ 3080 int i, j = 0; 3081 int n_targets = N_DND_TARGETS; 3082 GtkTargetEntry targets[N_DND_TARGETS]; 3083 3084 for (i = 0; i < (int)N_DND_TARGETS; ++i) 3085 { 3086 if (!clip_html && selection_targets[i].info == TARGET_HTML) 3087 n_targets--; 3088 else 3089 targets[j++] = dnd_targets[i]; 3090 } 3091 3092 gtk_drag_dest_unset(gui.drawarea); 3093 gtk_drag_dest_set(gui.drawarea, 3094 GTK_DEST_DEFAULT_ALL, 3095 targets, n_targets, 3096 GDK_ACTION_COPY); 3097} 3098 3099/* 3100 * Initialize the GUI. Create all the windows, set up all the callbacks etc. 3101 * Returns OK for success, FAIL when the GUI can't be started. 3102 */ 3103 int 3104gui_mch_init(void) 3105{ 3106 GtkWidget *vbox; 3107 3108#ifdef FEAT_GUI_GNOME 3109 /* Initialize the GNOME libraries. gnome_program_init()/gnome_init() 3110 * exits on failure, but that's a non-issue because we already called 3111 * gtk_init_check() in gui_mch_init_check(). */ 3112 if (using_gnome) 3113 gnome_program_init(VIMPACKAGE, VIM_VERSION_SHORT, 3114 LIBGNOMEUI_MODULE, gui_argc, gui_argv, NULL); 3115#endif 3116 vim_free(gui_argv); 3117 gui_argv = NULL; 3118 3119#if GLIB_CHECK_VERSION(2,1,3) 3120 /* Set the human-readable application name */ 3121 g_set_application_name("Vim"); 3122#endif 3123 /* 3124 * Force UTF-8 output no matter what the value of 'encoding' is. 3125 * did_set_string_option() in option.c prohibits changing 'termencoding' 3126 * to something else than UTF-8 if the GUI is in use. 3127 */ 3128 set_option_value((char_u *)"termencoding", 0L, (char_u *)"utf-8", 0); 3129 3130#ifdef FEAT_TOOLBAR 3131 gui_gtk_register_stock_icons(); 3132#endif 3133 /* FIXME: Need to install the classic icons and a gtkrc.classic file. 3134 * The hard part is deciding install locations and the Makefile magic. */ 3135#if 0 3136 gtk_rc_parse("gtkrc"); 3137#endif 3138 3139 /* Initialize values */ 3140 gui.border_width = 2; 3141 gui.scrollbar_width = SB_DEFAULT_WIDTH; 3142 gui.scrollbar_height = SB_DEFAULT_WIDTH; 3143 /* LINTED: avoid warning: conversion to 'unsigned long' */ 3144 gui.fgcolor = g_new0(GdkColor, 1); 3145 /* LINTED: avoid warning: conversion to 'unsigned long' */ 3146 gui.bgcolor = g_new0(GdkColor, 1); 3147 /* LINTED: avoid warning: conversion to 'unsigned long' */ 3148 gui.spcolor = g_new0(GdkColor, 1); 3149 3150 /* Initialise atoms */ 3151 html_atom = gdk_atom_intern("text/html", FALSE); 3152 utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE); 3153 3154 /* Set default foreground and background colors. */ 3155 gui.norm_pixel = gui.def_norm_pixel; 3156 gui.back_pixel = gui.def_back_pixel; 3157 3158 if (gtk_socket_id != 0) 3159 { 3160 GtkWidget *plug; 3161 3162 /* Use GtkSocket from another app. */ 3163#ifdef HAVE_GTK_MULTIHEAD 3164 plug = gtk_plug_new_for_display(gdk_display_get_default(), 3165 gtk_socket_id); 3166#else 3167 plug = gtk_plug_new(gtk_socket_id); 3168#endif 3169 if (plug != NULL && GTK_PLUG(plug)->socket_window != NULL) 3170 { 3171 gui.mainwin = plug; 3172 } 3173 else 3174 { 3175 g_warning("Connection to GTK+ socket (ID %u) failed", 3176 (unsigned int)gtk_socket_id); 3177 /* Pretend we never wanted it if it failed (get own window) */ 3178 gtk_socket_id = 0; 3179 } 3180 } 3181 3182 if (gtk_socket_id == 0) 3183 { 3184#ifdef FEAT_GUI_GNOME 3185 if (using_gnome) 3186 { 3187 gui.mainwin = gnome_app_new("Vim", NULL); 3188# ifdef USE_XSMP 3189 /* Use the GNOME save-yourself functionality now. */ 3190 xsmp_close(); 3191# endif 3192 } 3193 else 3194#endif 3195 gui.mainwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); 3196 } 3197 3198 gtk_widget_set_name(gui.mainwin, "vim-main-window"); 3199 3200 /* Create the PangoContext used for drawing all text. */ 3201 gui.text_context = gtk_widget_create_pango_context(gui.mainwin); 3202 pango_context_set_base_dir(gui.text_context, PANGO_DIRECTION_LTR); 3203 3204 gtk_container_border_width(GTK_CONTAINER(gui.mainwin), 0); 3205 gtk_widget_add_events(gui.mainwin, GDK_VISIBILITY_NOTIFY_MASK); 3206 3207 gtk_signal_connect(GTK_OBJECT(gui.mainwin), "delete_event", 3208 GTK_SIGNAL_FUNC(&delete_event_cb), NULL); 3209 3210 gtk_signal_connect(GTK_OBJECT(gui.mainwin), "realize", 3211 GTK_SIGNAL_FUNC(&mainwin_realize), NULL); 3212#ifdef HAVE_GTK_MULTIHEAD 3213 g_signal_connect(G_OBJECT(gui.mainwin), "screen_changed", 3214 G_CALLBACK(&mainwin_screen_changed_cb), NULL); 3215#endif 3216 gui.accel_group = gtk_accel_group_new(); 3217 gtk_window_add_accel_group(GTK_WINDOW(gui.mainwin), gui.accel_group); 3218 3219 /* A vertical box holds the menubar, toolbar and main text window. */ 3220 vbox = gtk_vbox_new(FALSE, 0); 3221 3222#ifdef FEAT_GUI_GNOME 3223 if (using_gnome) 3224 { 3225# if defined(FEAT_MENU) 3226 /* automagically restore menubar/toolbar placement */ 3227 gnome_app_enable_layout_config(GNOME_APP(gui.mainwin), TRUE); 3228# endif 3229 gnome_app_set_contents(GNOME_APP(gui.mainwin), vbox); 3230 } 3231 else 3232#endif 3233 { 3234 gtk_container_add(GTK_CONTAINER(gui.mainwin), vbox); 3235 gtk_widget_show(vbox); 3236 } 3237 3238#ifdef FEAT_MENU 3239 /* 3240 * Create the menubar and handle 3241 */ 3242 gui.menubar = gtk_menu_bar_new(); 3243 gtk_widget_set_name(gui.menubar, "vim-menubar"); 3244 3245 /* Avoid that GTK takes <F10> away from us. */ 3246 { 3247 GtkSettings *gtk_settings; 3248 3249 gtk_settings = gtk_settings_get_for_screen(gdk_screen_get_default()); 3250 g_object_set(gtk_settings, "gtk-menu-bar-accel", NULL, NULL); 3251 } 3252 3253 3254# ifdef FEAT_GUI_GNOME 3255 if (using_gnome) 3256 { 3257 BonoboDockItem *dockitem; 3258 3259 gnome_app_set_menus(GNOME_APP(gui.mainwin), GTK_MENU_BAR(gui.menubar)); 3260 dockitem = gnome_app_get_dock_item_by_name(GNOME_APP(gui.mainwin), 3261 GNOME_APP_MENUBAR_NAME); 3262 /* We don't want the menu to float. */ 3263 bonobo_dock_item_set_behavior(dockitem, 3264 bonobo_dock_item_get_behavior(dockitem) 3265 | BONOBO_DOCK_ITEM_BEH_NEVER_FLOATING); 3266 gui.menubar_h = GTK_WIDGET(dockitem); 3267 } 3268 else 3269# endif /* FEAT_GUI_GNOME */ 3270 { 3271 /* Always show the menubar, otherwise <F10> doesn't work. It may be 3272 * disabled in gui_init() later. */ 3273 gtk_widget_show(gui.menubar); 3274 gtk_box_pack_start(GTK_BOX(vbox), gui.menubar, FALSE, FALSE, 0); 3275 } 3276#endif /* FEAT_MENU */ 3277 3278#ifdef FEAT_TOOLBAR 3279 /* 3280 * Create the toolbar and handle 3281 */ 3282 /* some aesthetics on the toolbar */ 3283 gtk_rc_parse_string( 3284 "style \"vim-toolbar-style\" {\n" 3285 " GtkToolbar::button_relief = GTK_RELIEF_NONE\n" 3286 "}\n" 3287 "widget \"*.vim-toolbar\" style \"vim-toolbar-style\"\n"); 3288 gui.toolbar = gtk_toolbar_new(); 3289 gtk_widget_set_name(gui.toolbar, "vim-toolbar"); 3290 set_toolbar_style(GTK_TOOLBAR(gui.toolbar)); 3291 3292# ifdef FEAT_GUI_GNOME 3293 if (using_gnome) 3294 { 3295 BonoboDockItem *dockitem; 3296 3297 gnome_app_set_toolbar(GNOME_APP(gui.mainwin), GTK_TOOLBAR(gui.toolbar)); 3298 dockitem = gnome_app_get_dock_item_by_name(GNOME_APP(gui.mainwin), 3299 GNOME_APP_TOOLBAR_NAME); 3300 gui.toolbar_h = GTK_WIDGET(dockitem); 3301 /* When the toolbar is floating it gets stuck. So long as that isn't 3302 * fixed let's disallow floating. */ 3303 bonobo_dock_item_set_behavior(dockitem, 3304 bonobo_dock_item_get_behavior(dockitem) 3305 | BONOBO_DOCK_ITEM_BEH_NEVER_FLOATING); 3306 gtk_container_set_border_width(GTK_CONTAINER(gui.toolbar), 0); 3307 } 3308 else 3309# endif /* FEAT_GUI_GNOME */ 3310 { 3311 if (vim_strchr(p_go, GO_TOOLBAR) != NULL 3312 && (toolbar_flags & (TOOLBAR_TEXT | TOOLBAR_ICONS))) 3313 gtk_widget_show(gui.toolbar); 3314 gtk_box_pack_start(GTK_BOX(vbox), gui.toolbar, FALSE, FALSE, 0); 3315 } 3316#endif /* FEAT_TOOLBAR */ 3317 3318#ifdef FEAT_GUI_TABLINE 3319 /* 3320 * Use a Notebook for the tab pages labels. The labels are hidden by 3321 * default. 3322 */ 3323 gui.tabline = gtk_notebook_new(); 3324 gtk_widget_show(gui.tabline); 3325 gtk_box_pack_start(GTK_BOX(vbox), gui.tabline, FALSE, FALSE, 0); 3326 gtk_notebook_set_show_border(GTK_NOTEBOOK(gui.tabline), FALSE); 3327 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gui.tabline), FALSE); 3328 gtk_notebook_set_scrollable(GTK_NOTEBOOK(gui.tabline), TRUE); 3329 gtk_notebook_set_tab_border(GTK_NOTEBOOK(gui.tabline), FALSE); 3330 3331 tabline_tooltip = gtk_tooltips_new(); 3332 gtk_tooltips_enable(GTK_TOOLTIPS(tabline_tooltip)); 3333 3334 { 3335 GtkWidget *page, *label, *event_box; 3336 3337 /* Add the first tab. */ 3338 page = gtk_vbox_new(FALSE, 0); 3339 gtk_widget_show(page); 3340 gtk_container_add(GTK_CONTAINER(gui.tabline), page); 3341 label = gtk_label_new("-Empty-"); 3342 gtk_widget_show(label); 3343 event_box = gtk_event_box_new(); 3344 gtk_widget_show(event_box); 3345 gtk_object_set_user_data(GTK_OBJECT(event_box), (gpointer)1L); 3346 gtk_misc_set_padding(GTK_MISC(label), 2, 2); 3347 gtk_container_add(GTK_CONTAINER(event_box), label); 3348 gtk_notebook_set_tab_label(GTK_NOTEBOOK(gui.tabline), page, event_box); 3349 } 3350 3351 gtk_signal_connect(GTK_OBJECT(gui.tabline), "switch_page", 3352 GTK_SIGNAL_FUNC(on_select_tab), NULL); 3353 3354 /* Create a popup menu for the tab line and connect it. */ 3355 tabline_menu = create_tabline_menu(); 3356 gtk_signal_connect_object(GTK_OBJECT(gui.tabline), "button_press_event", 3357 GTK_SIGNAL_FUNC(on_tabline_menu), GTK_OBJECT(tabline_menu)); 3358#endif 3359 3360 gui.formwin = gtk_form_new(); 3361 gtk_container_border_width(GTK_CONTAINER(gui.formwin), 0); 3362 gtk_widget_set_events(gui.formwin, GDK_EXPOSURE_MASK); 3363 3364 gui.drawarea = gtk_drawing_area_new(); 3365 3366 /* Determine which events we will filter. */ 3367 gtk_widget_set_events(gui.drawarea, 3368 GDK_EXPOSURE_MASK | 3369 GDK_ENTER_NOTIFY_MASK | 3370 GDK_LEAVE_NOTIFY_MASK | 3371 GDK_BUTTON_PRESS_MASK | 3372 GDK_BUTTON_RELEASE_MASK | 3373 GDK_SCROLL_MASK | 3374 GDK_KEY_PRESS_MASK | 3375 GDK_KEY_RELEASE_MASK | 3376 GDK_POINTER_MOTION_MASK | 3377 GDK_POINTER_MOTION_HINT_MASK); 3378 3379 gtk_widget_show(gui.drawarea); 3380 gtk_form_put(GTK_FORM(gui.formwin), gui.drawarea, 0, 0); 3381 gtk_widget_show(gui.formwin); 3382 gtk_box_pack_start(GTK_BOX(vbox), gui.formwin, TRUE, TRUE, 0); 3383 3384 /* For GtkSockets, key-presses must go to the focus widget (drawarea) 3385 * and not the window. */ 3386 gtk_signal_connect((gtk_socket_id == 0) ? GTK_OBJECT(gui.mainwin) 3387 : GTK_OBJECT(gui.drawarea), 3388 "key_press_event", 3389 GTK_SIGNAL_FUNC(key_press_event), NULL); 3390#if defined(FEAT_XIM) 3391 /* Also forward key release events for the benefit of GTK+ 2 input 3392 * modules. Try CTRL-SHIFT-xdigits to enter a Unicode code point. */ 3393 g_signal_connect((gtk_socket_id == 0) ? G_OBJECT(gui.mainwin) 3394 : G_OBJECT(gui.drawarea), 3395 "key_release_event", 3396 G_CALLBACK(&key_release_event), NULL); 3397#endif 3398 gtk_signal_connect(GTK_OBJECT(gui.drawarea), "realize", 3399 GTK_SIGNAL_FUNC(drawarea_realize_cb), NULL); 3400 gtk_signal_connect(GTK_OBJECT(gui.drawarea), "unrealize", 3401 GTK_SIGNAL_FUNC(drawarea_unrealize_cb), NULL); 3402 3403 gtk_signal_connect_after(GTK_OBJECT(gui.drawarea), "style_set", 3404 GTK_SIGNAL_FUNC(&drawarea_style_set_cb), NULL); 3405 3406 gui.visibility = GDK_VISIBILITY_UNOBSCURED; 3407 3408#if !(defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION)) 3409 wm_protocols_atom = gdk_atom_intern("WM_PROTOCOLS", FALSE); 3410 save_yourself_atom = gdk_atom_intern("WM_SAVE_YOURSELF", FALSE); 3411#endif 3412 3413 if (gtk_socket_id != 0) 3414 /* make sure keyboard input can go to the drawarea */ 3415 GTK_WIDGET_SET_FLAGS(gui.drawarea, GTK_CAN_FOCUS); 3416 3417 /* 3418 * Set clipboard specific atoms 3419 */ 3420 vim_atom = gdk_atom_intern(VIM_ATOM_NAME, FALSE); 3421 vimenc_atom = gdk_atom_intern(VIMENC_ATOM_NAME, FALSE); 3422 clip_star.gtk_sel_atom = GDK_SELECTION_PRIMARY; 3423 clip_plus.gtk_sel_atom = gdk_atom_intern("CLIPBOARD", FALSE); 3424 3425 /* 3426 * Start out by adding the configured border width into the border offset. 3427 */ 3428 gui.border_offset = gui.border_width; 3429 3430 gtk_signal_connect(GTK_OBJECT(gui.mainwin), "visibility_notify_event", 3431 GTK_SIGNAL_FUNC(visibility_event), NULL); 3432 gtk_signal_connect(GTK_OBJECT(gui.drawarea), "expose_event", 3433 GTK_SIGNAL_FUNC(expose_event), NULL); 3434 3435 /* 3436 * Only install these enter/leave callbacks when 'p' in 'guioptions'. 3437 * Only needed for some window managers. 3438 */ 3439 if (vim_strchr(p_go, GO_POINTER) != NULL) 3440 { 3441 gtk_signal_connect(GTK_OBJECT(gui.drawarea), "leave_notify_event", 3442 GTK_SIGNAL_FUNC(leave_notify_event), NULL); 3443 gtk_signal_connect(GTK_OBJECT(gui.drawarea), "enter_notify_event", 3444 GTK_SIGNAL_FUNC(enter_notify_event), NULL); 3445 } 3446 3447 /* Real windows can get focus ... GtkPlug, being a mere container can't, 3448 * only its widgets. Arguably, this could be common code and we not use 3449 * the window focus at all, but let's be safe. 3450 */ 3451 if (gtk_socket_id == 0) 3452 { 3453 gtk_signal_connect(GTK_OBJECT(gui.mainwin), "focus_out_event", 3454 GTK_SIGNAL_FUNC(focus_out_event), NULL); 3455 gtk_signal_connect(GTK_OBJECT(gui.mainwin), "focus_in_event", 3456 GTK_SIGNAL_FUNC(focus_in_event), NULL); 3457 } 3458 else 3459 { 3460 gtk_signal_connect(GTK_OBJECT(gui.drawarea), "focus_out_event", 3461 GTK_SIGNAL_FUNC(focus_out_event), NULL); 3462 gtk_signal_connect(GTK_OBJECT(gui.drawarea), "focus_in_event", 3463 GTK_SIGNAL_FUNC(focus_in_event), NULL); 3464#ifdef FEAT_GUI_TABLINE 3465 gtk_signal_connect(GTK_OBJECT(gui.tabline), "focus_out_event", 3466 GTK_SIGNAL_FUNC(focus_out_event), NULL); 3467 gtk_signal_connect(GTK_OBJECT(gui.tabline), "focus_in_event", 3468 GTK_SIGNAL_FUNC(focus_in_event), NULL); 3469#endif /* FEAT_GUI_TABLINE */ 3470 } 3471 3472 gtk_signal_connect(GTK_OBJECT(gui.drawarea), "motion_notify_event", 3473 GTK_SIGNAL_FUNC(motion_notify_event), NULL); 3474 gtk_signal_connect(GTK_OBJECT(gui.drawarea), "button_press_event", 3475 GTK_SIGNAL_FUNC(button_press_event), NULL); 3476 gtk_signal_connect(GTK_OBJECT(gui.drawarea), "button_release_event", 3477 GTK_SIGNAL_FUNC(button_release_event), NULL); 3478 g_signal_connect(G_OBJECT(gui.drawarea), "scroll_event", 3479 G_CALLBACK(&scroll_event), NULL); 3480 3481 /* 3482 * Add selection handler functions. 3483 */ 3484 gtk_signal_connect(GTK_OBJECT(gui.drawarea), "selection_clear_event", 3485 GTK_SIGNAL_FUNC(selection_clear_event), NULL); 3486 gtk_signal_connect(GTK_OBJECT(gui.drawarea), "selection_received", 3487 GTK_SIGNAL_FUNC(selection_received_cb), NULL); 3488 3489 gui_gtk_set_selection_targets(); 3490 3491 gtk_signal_connect(GTK_OBJECT(gui.drawarea), "selection_get", 3492 GTK_SIGNAL_FUNC(selection_get_cb), NULL); 3493 3494 /* Pretend we don't have input focus, we will get an event if we do. */ 3495 gui.in_focus = FALSE; 3496 3497 return OK; 3498} 3499 3500#if (defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION)) || defined(PROTO) 3501/* 3502 * This is called from gui_start() after a fork() has been done. 3503 * We have to tell the session manager our new PID. 3504 */ 3505 void 3506gui_mch_forked(void) 3507{ 3508 if (using_gnome) 3509 { 3510 GnomeClient *client; 3511 3512 client = gnome_master_client(); 3513 3514 if (client != NULL) 3515 gnome_client_set_process_id(client, getpid()); 3516 } 3517} 3518#endif /* FEAT_GUI_GNOME && FEAT_SESSION */ 3519 3520/* 3521 * Called when the foreground or background color has been changed. 3522 * This used to change the graphics contexts directly but we are 3523 * currently manipulating them where desired. 3524 */ 3525 void 3526gui_mch_new_colors(void) 3527{ 3528 if (gui.drawarea != NULL && gui.drawarea->window != NULL) 3529 { 3530 GdkColor color = { 0, 0, 0, 0 }; 3531 3532 color.pixel = gui.back_pixel; 3533 gdk_window_set_background(gui.drawarea->window, &color); 3534 } 3535} 3536 3537/* 3538 * This signal informs us about the need to rearrange our sub-widgets. 3539 */ 3540 static gint 3541form_configure_event(GtkWidget *widget UNUSED, 3542 GdkEventConfigure *event, 3543 gpointer data UNUSED) 3544{ 3545 int usable_height = event->height; 3546 3547 /* When in a GtkPlug, we can't guarantee valid heights (as a round 3548 * no. of char-heights), so we have to manually sanitise them. 3549 * Widths seem to sort themselves out, don't ask me why. 3550 */ 3551 if (gtk_socket_id != 0) 3552 usable_height -= (gui.char_height - (gui.char_height/2)); /* sic. */ 3553 3554 gtk_form_freeze(GTK_FORM(gui.formwin)); 3555 gui_resize_shell(event->width, usable_height); 3556 gtk_form_thaw(GTK_FORM(gui.formwin)); 3557 3558 return TRUE; 3559} 3560 3561/* 3562 * Function called when window already closed. 3563 * We can't do much more here than to trying to preserve what had been done, 3564 * since the window is already inevitably going away. 3565 */ 3566 static void 3567mainwin_destroy_cb(GtkObject *object UNUSED, gpointer data UNUSED) 3568{ 3569 /* Don't write messages to the GUI anymore */ 3570 full_screen = FALSE; 3571 3572 gui.mainwin = NULL; 3573 gui.drawarea = NULL; 3574 3575 if (!exiting) /* only do anything if the destroy was unexpected */ 3576 { 3577 vim_strncpy(IObuff, 3578 (char_u *)_("Vim: Main window unexpectedly destroyed\n"), 3579 IOSIZE - 1); 3580 preserve_exit(); 3581 } 3582} 3583 3584 3585/* 3586 * Bit of a hack to ensure we start GtkPlug windows with the correct window 3587 * hints (and thus the required size from -geom), but that after that we 3588 * put the hints back to normal (the actual minimum size) so we may 3589 * subsequently be resized smaller. GtkSocket (the parent end) uses the 3590 * plug's window 'min hints to set *it's* minimum size, but that's also the 3591 * only way we have of making ourselves bigger (by set lines/columns). 3592 * Thus set hints at start-up to ensure correct init. size, then a 3593 * second after the final attempt to reset the real minimum hinst (done by 3594 * scrollbar init.), actually do the standard hinst and stop the timer. 3595 * We'll not let the default hints be set while this timer's active. 3596 */ 3597 static gboolean 3598check_startup_plug_hints(gpointer data UNUSED) 3599{ 3600 if (init_window_hints_state == 1) 3601 { 3602 /* Safe to use normal hints now */ 3603 init_window_hints_state = 0; 3604 update_window_manager_hints(0, 0); 3605 return FALSE; /* stop timer */ 3606 } 3607 3608 /* Keep on trying */ 3609 init_window_hints_state = 1; 3610 return TRUE; 3611} 3612 3613/* 3614 * Open the GUI window which was created by a call to gui_mch_init(). 3615 */ 3616 int 3617gui_mch_open(void) 3618{ 3619 guicolor_T fg_pixel = INVALCOLOR; 3620 guicolor_T bg_pixel = INVALCOLOR; 3621 guint pixel_width; 3622 guint pixel_height; 3623 3624 /* 3625 * Allow setting a window role on the command line, or invent one 3626 * if none was specified. This is mainly useful for GNOME session 3627 * support; allowing the WM to restore window placement. 3628 */ 3629 if (role_argument != NULL) 3630 { 3631 gtk_window_set_role(GTK_WINDOW(gui.mainwin), role_argument); 3632 } 3633 else 3634 { 3635 char *role; 3636 3637 /* Invent a unique-enough ID string for the role */ 3638 role = g_strdup_printf("vim-%u-%u-%u", 3639 (unsigned)mch_get_pid(), 3640 (unsigned)g_random_int(), 3641 (unsigned)time(NULL)); 3642 3643 gtk_window_set_role(GTK_WINDOW(gui.mainwin), role); 3644 g_free(role); 3645 } 3646 3647 if (gui_win_x != -1 && gui_win_y != -1) 3648 gtk_window_move(GTK_WINDOW(gui.mainwin), gui_win_x, gui_win_y); 3649 3650 /* Determine user specified geometry, if present. */ 3651 if (gui.geom != NULL) 3652 { 3653 int mask; 3654 unsigned int w, h; 3655 int x = 0; 3656 int y = 0; 3657 3658 mask = XParseGeometry((char *)gui.geom, &x, &y, &w, &h); 3659 3660 if (mask & WidthValue) 3661 Columns = w; 3662 if (mask & HeightValue) 3663 { 3664 if (p_window > (long)h - 1 || !option_was_set((char_u *)"window")) 3665 p_window = h - 1; 3666 Rows = h; 3667 } 3668 3669 pixel_width = (guint)(gui_get_base_width() + Columns * gui.char_width); 3670 pixel_height = (guint)(gui_get_base_height() + Rows * gui.char_height); 3671 3672 pixel_width += get_menu_tool_width(); 3673 pixel_height += get_menu_tool_height(); 3674 3675 if (mask & (XValue | YValue)) 3676 { 3677 int ww, hh; 3678 gui_mch_get_screen_dimensions(&ww, &hh); 3679 hh += p_ghr + get_menu_tool_height(); 3680 ww += get_menu_tool_width(); 3681 if (mask & XNegative) 3682 x += ww - pixel_width; 3683 if (mask & YNegative) 3684 y += hh - pixel_height; 3685 gtk_window_move(GTK_WINDOW(gui.mainwin), x, y); 3686 } 3687 vim_free(gui.geom); 3688 gui.geom = NULL; 3689 3690 /* From now until everyone's stopped trying to set the window hints 3691 * to their correct minimum values, stop them being set as we need 3692 * them to remain at our required size for the parent GtkSocket to 3693 * give us the right initial size. 3694 */ 3695 if (gtk_socket_id != 0 && (mask & WidthValue || mask & HeightValue)) 3696 { 3697 update_window_manager_hints(pixel_width, pixel_height); 3698 init_window_hints_state = 1; 3699 g_timeout_add(1000, check_startup_plug_hints, NULL); 3700 } 3701 } 3702 3703 pixel_width = (guint)(gui_get_base_width() + Columns * gui.char_width); 3704 pixel_height = (guint)(gui_get_base_height() + Rows * gui.char_height); 3705 /* For GTK2 changing the size of the form widget doesn't cause window 3706 * resizing. */ 3707 if (gtk_socket_id == 0) 3708 gtk_window_resize(GTK_WINDOW(gui.mainwin), pixel_width, pixel_height); 3709 update_window_manager_hints(0, 0); 3710 3711 if (foreground_argument != NULL) 3712 fg_pixel = gui_get_color((char_u *)foreground_argument); 3713 if (fg_pixel == INVALCOLOR) 3714 fg_pixel = gui_get_color((char_u *)"Black"); 3715 3716 if (background_argument != NULL) 3717 bg_pixel = gui_get_color((char_u *)background_argument); 3718 if (bg_pixel == INVALCOLOR) 3719 bg_pixel = gui_get_color((char_u *)"White"); 3720 3721 if (found_reverse_arg) 3722 { 3723 gui.def_norm_pixel = bg_pixel; 3724 gui.def_back_pixel = fg_pixel; 3725 } 3726 else 3727 { 3728 gui.def_norm_pixel = fg_pixel; 3729 gui.def_back_pixel = bg_pixel; 3730 } 3731 3732 /* Get the colors from the "Normal" and "Menu" group (set in syntax.c or 3733 * in a vimrc file) */ 3734 set_normal_colors(); 3735 3736 /* Check that none of the colors are the same as the background color */ 3737 gui_check_colors(); 3738 3739 /* Get the colors for the highlight groups (gui_check_colors() might have 3740 * changed them). */ 3741 highlight_gui_started(); /* re-init colors and fonts */ 3742 3743 gtk_signal_connect(GTK_OBJECT(gui.mainwin), "destroy", 3744 GTK_SIGNAL_FUNC(mainwin_destroy_cb), NULL); 3745 3746#ifdef FEAT_HANGULIN 3747 hangul_keyboard_set(); 3748#endif 3749 3750 /* 3751 * Notify the fixed area about the need to resize the contents of the 3752 * gui.formwin, which we use for random positioning of the included 3753 * components. 3754 * 3755 * We connect this signal deferred finally after anything is in place, 3756 * since this is intended to handle resizements coming from the window 3757 * manager upon us and should not interfere with what VIM is requesting 3758 * upon startup. 3759 */ 3760 gtk_signal_connect(GTK_OBJECT(gui.formwin), "configure_event", 3761 GTK_SIGNAL_FUNC(form_configure_event), NULL); 3762 3763#ifdef FEAT_DND 3764 /* Set up for receiving DND items. */ 3765 gui_gtk_set_dnd_targets(); 3766 3767 gtk_signal_connect(GTK_OBJECT(gui.drawarea), "drag_data_received", 3768 GTK_SIGNAL_FUNC(drag_data_received_cb), NULL); 3769#endif 3770 3771 /* With GTK+ 2, we need to iconify the window before calling show() 3772 * to avoid mapping the window for a short time. */ 3773 if (found_iconic_arg && gtk_socket_id == 0) 3774 gui_mch_iconify(); 3775 3776 { 3777#if defined(FEAT_GUI_GNOME) && defined(FEAT_MENU) 3778 unsigned long menu_handler = 0; 3779# ifdef FEAT_TOOLBAR 3780 unsigned long tool_handler = 0; 3781# endif 3782 /* 3783 * Urgh hackish :/ For some reason BonoboDockLayout always forces a 3784 * show when restoring the saved layout configuration. We can't just 3785 * hide the widgets again after gtk_widget_show(gui.mainwin) since it's 3786 * a toplevel window and thus will be realized immediately. Instead, 3787 * connect signal handlers to hide the widgets just after they've been 3788 * marked visible, but before the main window is realized. 3789 */ 3790 if (using_gnome && vim_strchr(p_go, GO_MENUS) == NULL) 3791 menu_handler = g_signal_connect_after(gui.menubar_h, "show", 3792 G_CALLBACK(>k_widget_hide), 3793 NULL); 3794# ifdef FEAT_TOOLBAR 3795 if (using_gnome && vim_strchr(p_go, GO_TOOLBAR) == NULL 3796 && (toolbar_flags & (TOOLBAR_TEXT | TOOLBAR_ICONS))) 3797 tool_handler = g_signal_connect_after(gui.toolbar_h, "show", 3798 G_CALLBACK(>k_widget_hide), 3799 NULL); 3800# endif 3801#endif 3802 gtk_widget_show(gui.mainwin); 3803 3804#if defined(FEAT_GUI_GNOME) && defined(FEAT_MENU) 3805 if (menu_handler != 0) 3806 g_signal_handler_disconnect(gui.menubar_h, menu_handler); 3807# ifdef FEAT_TOOLBAR 3808 if (tool_handler != 0) 3809 g_signal_handler_disconnect(gui.toolbar_h, tool_handler); 3810# endif 3811#endif 3812 } 3813 3814 return OK; 3815} 3816 3817 3818 void 3819gui_mch_exit(int rc UNUSED) 3820{ 3821 if (gui.mainwin != NULL) 3822 gtk_widget_destroy(gui.mainwin); 3823} 3824 3825/* 3826 * Get the position of the top left corner of the window. 3827 */ 3828 int 3829gui_mch_get_winpos(int *x, int *y) 3830{ 3831 gtk_window_get_position(GTK_WINDOW(gui.mainwin), x, y); 3832 return OK; 3833} 3834 3835/* 3836 * Set the position of the top left corner of the window to the given 3837 * coordinates. 3838 */ 3839 void 3840gui_mch_set_winpos(int x, int y) 3841{ 3842 gtk_window_move(GTK_WINDOW(gui.mainwin), x, y); 3843} 3844 3845#if 0 3846static int resize_idle_installed = FALSE; 3847/* 3848 * Idle handler to force resize. Used by gui_mch_set_shellsize() to ensure 3849 * the shell size doesn't exceed the window size, i.e. if the window manager 3850 * ignored our size request. Usually this happens if the window is maximized. 3851 * 3852 * FIXME: It'd be nice if we could find a little more orthodox solution. 3853 * See also the remark below in gui_mch_set_shellsize(). 3854 * 3855 * DISABLED: When doing ":set lines+=1" this function would first invoke 3856 * gui_resize_shell() with the old size, then the normal callback would 3857 * report the new size through form_configure_event(). That caused the window 3858 * layout to be messed up. 3859 */ 3860 static gboolean 3861force_shell_resize_idle(gpointer data) 3862{ 3863 if (gui.mainwin != NULL 3864 && GTK_WIDGET_REALIZED(gui.mainwin) 3865 && GTK_WIDGET_VISIBLE(gui.mainwin)) 3866 { 3867 int width; 3868 int height; 3869 3870 gtk_window_get_size(GTK_WINDOW(gui.mainwin), &width, &height); 3871 3872 width -= get_menu_tool_width(); 3873 height -= get_menu_tool_height(); 3874 3875 gui_resize_shell(width, height); 3876 } 3877 3878 resize_idle_installed = FALSE; 3879 return FALSE; /* don't call me again */ 3880} 3881#endif 3882 3883/* 3884 * Return TRUE if the main window is maximized. 3885 */ 3886 int 3887gui_mch_maximized() 3888{ 3889 return (gui.mainwin != NULL && gui.mainwin->window != NULL 3890 && (gdk_window_get_state(gui.mainwin->window) 3891 & GDK_WINDOW_STATE_MAXIMIZED)); 3892} 3893 3894/* 3895 * Unmaximize the main window 3896 */ 3897 void 3898gui_mch_unmaximize() 3899{ 3900 if (gui.mainwin != NULL) 3901 gtk_window_unmaximize(GTK_WINDOW(gui.mainwin)); 3902} 3903 3904/* 3905 * Set the windows size. 3906 */ 3907 void 3908gui_mch_set_shellsize(int width, int height, 3909 int min_width UNUSED, int min_height UNUSED, 3910 int base_width UNUSED, int base_height UNUSED, 3911 int direction UNUSED) 3912{ 3913 /* give GTK+ a chance to put all widget's into place */ 3914 gui_mch_update(); 3915 3916 /* this will cause the proper resizement to happen too */ 3917 if (gtk_socket_id == 0) 3918 update_window_manager_hints(0, 0); 3919 3920 /* With GTK+ 2, changing the size of the form widget doesn't resize 3921 * the window. So let's do it the other way around and resize the 3922 * main window instead. */ 3923 width += get_menu_tool_width(); 3924 height += get_menu_tool_height(); 3925 3926 if (gtk_socket_id == 0) 3927 gtk_window_resize(GTK_WINDOW(gui.mainwin), width, height); 3928 else 3929 update_window_manager_hints(width, height); 3930 3931# if 0 3932 if (!resize_idle_installed) 3933 { 3934 g_idle_add_full(GDK_PRIORITY_EVENTS + 10, 3935 &force_shell_resize_idle, NULL, NULL); 3936 resize_idle_installed = TRUE; 3937 } 3938# endif 3939 /* 3940 * Wait until all events are processed to prevent a crash because the 3941 * real size of the drawing area doesn't reflect Vim's internal ideas. 3942 * 3943 * This is a bit of a hack, since Vim is a terminal application with a GUI 3944 * on top, while the GUI expects to be the boss. 3945 */ 3946 gui_mch_update(); 3947} 3948 3949 3950/* 3951 * The screen size is used to make sure the initial window doesn't get bigger 3952 * than the screen. This subtracts some room for menubar, toolbar and window 3953 * decorations. 3954 */ 3955 void 3956gui_mch_get_screen_dimensions(int *screen_w, int *screen_h) 3957{ 3958#ifdef HAVE_GTK_MULTIHEAD 3959 GdkScreen* screen; 3960 3961 if (gui.mainwin != NULL && gtk_widget_has_screen(gui.mainwin)) 3962 screen = gtk_widget_get_screen(gui.mainwin); 3963 else 3964 screen = gdk_screen_get_default(); 3965 3966 *screen_w = gdk_screen_get_width(screen); 3967 *screen_h = gdk_screen_get_height(screen) - p_ghr; 3968#else 3969 *screen_w = gdk_screen_width(); 3970 /* Subtract 'guiheadroom' from the height to allow some room for the 3971 * window manager (task list and window title bar). */ 3972 *screen_h = gdk_screen_height() - p_ghr; 3973#endif 3974 3975 /* 3976 * FIXME: dirty trick: Because the gui_get_base_height() doesn't include 3977 * the toolbar and menubar for GTK, we subtract them from the screen 3978 * height, so that the window size can be made to fit on the screen. 3979 * This should be completely changed later. 3980 */ 3981 *screen_w -= get_menu_tool_width(); 3982 *screen_h -= get_menu_tool_height(); 3983} 3984 3985#if defined(FEAT_TITLE) || defined(PROTO) 3986 void 3987gui_mch_settitle(char_u *title, char_u *icon UNUSED) 3988{ 3989 if (title != NULL && output_conv.vc_type != CONV_NONE) 3990 title = string_convert(&output_conv, title, NULL); 3991 3992 gtk_window_set_title(GTK_WINDOW(gui.mainwin), (const char *)title); 3993 3994 if (output_conv.vc_type != CONV_NONE) 3995 vim_free(title); 3996} 3997#endif /* FEAT_TITLE */ 3998 3999#if defined(FEAT_MENU) || defined(PROTO) 4000 void 4001gui_mch_enable_menu(int showit) 4002{ 4003 GtkWidget *widget; 4004 4005# ifdef FEAT_GUI_GNOME 4006 if (using_gnome) 4007 widget = gui.menubar_h; 4008 else 4009# endif 4010 widget = gui.menubar; 4011 4012 /* Do not disable the menu while starting up, otherwise F10 doesn't work. */ 4013 if (!showit != !GTK_WIDGET_VISIBLE(widget) && !gui.starting) 4014 { 4015 if (showit) 4016 gtk_widget_show(widget); 4017 else 4018 gtk_widget_hide(widget); 4019 4020 update_window_manager_hints(0, 0); 4021 } 4022} 4023#endif /* FEAT_MENU */ 4024 4025#if defined(FEAT_TOOLBAR) || defined(PROTO) 4026 void 4027gui_mch_show_toolbar(int showit) 4028{ 4029 GtkWidget *widget; 4030 4031 if (gui.toolbar == NULL) 4032 return; 4033 4034# ifdef FEAT_GUI_GNOME 4035 if (using_gnome) 4036 widget = gui.toolbar_h; 4037 else 4038# endif 4039 widget = gui.toolbar; 4040 4041 if (showit) 4042 set_toolbar_style(GTK_TOOLBAR(gui.toolbar)); 4043 4044 if (!showit != !GTK_WIDGET_VISIBLE(widget)) 4045 { 4046 if (showit) 4047 gtk_widget_show(widget); 4048 else 4049 gtk_widget_hide(widget); 4050 4051 update_window_manager_hints(0, 0); 4052 } 4053} 4054#endif /* FEAT_TOOLBAR */ 4055 4056/* 4057 * Check if a given font is a CJK font. This is done in a very crude manner. It 4058 * just see if U+04E00 for zh and ja and U+AC00 for ko are covered in a given 4059 * font. Consequently, this function cannot be used as a general purpose check 4060 * for CJK-ness for which fontconfig APIs should be used. This is only used by 4061 * gui_mch_init_font() to deal with 'CJK fixed width fonts'. 4062 */ 4063 static int 4064is_cjk_font(PangoFontDescription *font_desc) 4065{ 4066 static const char * const cjk_langs[] = 4067 {"zh_CN", "zh_TW", "zh_HK", "ja", "ko"}; 4068 4069 PangoFont *font; 4070 unsigned i; 4071 int is_cjk = FALSE; 4072 4073 font = pango_context_load_font(gui.text_context, font_desc); 4074 4075 if (font == NULL) 4076 return FALSE; 4077 4078 for (i = 0; !is_cjk && i < G_N_ELEMENTS(cjk_langs); ++i) 4079 { 4080 PangoCoverage *coverage; 4081 gunichar uc; 4082 4083 coverage = pango_font_get_coverage( 4084 font, pango_language_from_string(cjk_langs[i])); 4085 4086 if (coverage != NULL) 4087 { 4088 uc = (cjk_langs[i][0] == 'k') ? 0xAC00 : 0x4E00; 4089 is_cjk = (pango_coverage_get(coverage, uc) == PANGO_COVERAGE_EXACT); 4090 pango_coverage_unref(coverage); 4091 } 4092 } 4093 4094 g_object_unref(font); 4095 4096 return is_cjk; 4097} 4098 4099/* 4100 * Adjust gui.char_height (after 'linespace' was changed). 4101 */ 4102 int 4103gui_mch_adjust_charheight(void) 4104{ 4105 PangoFontMetrics *metrics; 4106 int ascent; 4107 int descent; 4108 4109 metrics = pango_context_get_metrics(gui.text_context, gui.norm_font, 4110 pango_context_get_language(gui.text_context)); 4111 ascent = pango_font_metrics_get_ascent(metrics); 4112 descent = pango_font_metrics_get_descent(metrics); 4113 4114 pango_font_metrics_unref(metrics); 4115 4116 gui.char_height = (ascent + descent + PANGO_SCALE - 1) / PANGO_SCALE 4117 + p_linespace; 4118 /* LINTED: avoid warning: bitwise operation on signed value */ 4119 gui.char_ascent = PANGO_PIXELS(ascent + p_linespace * PANGO_SCALE / 2); 4120 4121 /* A not-positive value of char_height may crash Vim. Only happens 4122 * if 'linespace' is negative (which does make sense sometimes). */ 4123 gui.char_ascent = MAX(gui.char_ascent, 0); 4124 gui.char_height = MAX(gui.char_height, gui.char_ascent + 1); 4125 4126 return OK; 4127} 4128 4129/* 4130 * Put up a font dialog and return the selected font name in allocated memory. 4131 * "oldval" is the previous value. Return NULL when cancelled. 4132 * This should probably go into gui_gtk.c. Hmm. 4133 * FIXME: 4134 * The GTK2 font selection dialog has no filtering API. So we could either 4135 * a) implement our own (possibly copying the code from somewhere else) or 4136 * b) just live with it. 4137 */ 4138 char_u * 4139gui_mch_font_dialog(char_u *oldval) 4140{ 4141 GtkWidget *dialog; 4142 int response; 4143 char_u *fontname = NULL; 4144 char_u *oldname; 4145 4146 dialog = gtk_font_selection_dialog_new(NULL); 4147 4148 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(gui.mainwin)); 4149 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE); 4150 4151 if (oldval != NULL && oldval[0] != NUL) 4152 { 4153 if (output_conv.vc_type != CONV_NONE) 4154 oldname = string_convert(&output_conv, oldval, NULL); 4155 else 4156 oldname = oldval; 4157 4158 /* Annoying bug in GTK (or Pango): if the font name does not include a 4159 * size, zero is used. Use default point size ten. */ 4160 if (!vim_isdigit(oldname[STRLEN(oldname) - 1])) 4161 { 4162 char_u *p = vim_strnsave(oldname, STRLEN(oldname) + 3); 4163 4164 if (p != NULL) 4165 { 4166 STRCPY(p + STRLEN(p), " 10"); 4167 if (oldname != oldval) 4168 vim_free(oldname); 4169 oldname = p; 4170 } 4171 } 4172 4173 gtk_font_selection_dialog_set_font_name( 4174 GTK_FONT_SELECTION_DIALOG(dialog), (const char *)oldname); 4175 4176 if (oldname != oldval) 4177 vim_free(oldname); 4178 } 4179 else 4180 gtk_font_selection_dialog_set_font_name( 4181 GTK_FONT_SELECTION_DIALOG(dialog), DEFAULT_FONT); 4182 4183 response = gtk_dialog_run(GTK_DIALOG(dialog)); 4184 4185 if (response == GTK_RESPONSE_OK) 4186 { 4187 char *name; 4188 4189 name = gtk_font_selection_dialog_get_font_name( 4190 GTK_FONT_SELECTION_DIALOG(dialog)); 4191 if (name != NULL) 4192 { 4193 char_u *p; 4194 4195 /* Apparently some font names include a comma, need to escape 4196 * that, because in 'guifont' it separates names. */ 4197 p = vim_strsave_escaped((char_u *)name, (char_u *)","); 4198 g_free(name); 4199 if (p != NULL && input_conv.vc_type != CONV_NONE) 4200 { 4201 fontname = string_convert(&input_conv, p, NULL); 4202 vim_free(p); 4203 } 4204 else 4205 fontname = p; 4206 } 4207 } 4208 4209 if (response != GTK_RESPONSE_NONE) 4210 gtk_widget_destroy(dialog); 4211 4212 return fontname; 4213} 4214 4215/* 4216 * Some monospace fonts don't support a bold weight, and fall back 4217 * silently to the regular weight. But this is no good since our text 4218 * drawing function can emulate bold by overstriking. So let's try 4219 * to detect whether bold weight is actually available and emulate it 4220 * otherwise. 4221 * 4222 * Note that we don't need to check for italic style since Xft can 4223 * emulate italic on its own, provided you have a proper fontconfig 4224 * setup. We wouldn't be able to emulate it in Vim anyway. 4225 */ 4226 static void 4227get_styled_font_variants(void) 4228{ 4229 PangoFontDescription *bold_font_desc; 4230 PangoFont *plain_font; 4231 PangoFont *bold_font; 4232 4233 gui.font_can_bold = FALSE; 4234 4235 plain_font = pango_context_load_font(gui.text_context, gui.norm_font); 4236 4237 if (plain_font == NULL) 4238 return; 4239 4240 bold_font_desc = pango_font_description_copy_static(gui.norm_font); 4241 pango_font_description_set_weight(bold_font_desc, PANGO_WEIGHT_BOLD); 4242 4243 bold_font = pango_context_load_font(gui.text_context, bold_font_desc); 4244 /* 4245 * The comparison relies on the unique handle nature of a PangoFont*, 4246 * i.e. it's assumed that a different PangoFont* won't refer to the 4247 * same font. Seems to work, and failing here isn't critical anyway. 4248 */ 4249 if (bold_font != NULL) 4250 { 4251 gui.font_can_bold = (bold_font != plain_font); 4252 g_object_unref(bold_font); 4253 } 4254 4255 pango_font_description_free(bold_font_desc); 4256 g_object_unref(plain_font); 4257} 4258 4259static PangoEngineShape *default_shape_engine = NULL; 4260 4261/* 4262 * Create a map from ASCII characters in the range [32,126] to glyphs 4263 * of the current font. This is used by gui_gtk2_draw_string() to skip 4264 * the itemize and shaping process for the most common case. 4265 */ 4266 static void 4267ascii_glyph_table_init(void) 4268{ 4269 char_u ascii_chars[128]; 4270 PangoAttrList *attr_list; 4271 GList *item_list; 4272 int i; 4273 4274 if (gui.ascii_glyphs != NULL) 4275 pango_glyph_string_free(gui.ascii_glyphs); 4276 if (gui.ascii_font != NULL) 4277 g_object_unref(gui.ascii_font); 4278 4279 gui.ascii_glyphs = NULL; 4280 gui.ascii_font = NULL; 4281 4282 /* For safety, fill in question marks for the control characters. */ 4283 for (i = 0; i < 32; ++i) 4284 ascii_chars[i] = '?'; 4285 for (; i < 127; ++i) 4286 ascii_chars[i] = i; 4287 ascii_chars[i] = '?'; 4288 4289 attr_list = pango_attr_list_new(); 4290 item_list = pango_itemize(gui.text_context, (const char *)ascii_chars, 4291 0, sizeof(ascii_chars), attr_list, NULL); 4292 4293 if (item_list != NULL && item_list->next == NULL) /* play safe */ 4294 { 4295 PangoItem *item; 4296 int width; 4297 4298 item = (PangoItem *)item_list->data; 4299 width = gui.char_width * PANGO_SCALE; 4300 4301 /* Remember the shape engine used for ASCII. */ 4302 default_shape_engine = item->analysis.shape_engine; 4303 4304 gui.ascii_font = item->analysis.font; 4305 g_object_ref(gui.ascii_font); 4306 4307 gui.ascii_glyphs = pango_glyph_string_new(); 4308 4309 pango_shape((const char *)ascii_chars, sizeof(ascii_chars), 4310 &item->analysis, gui.ascii_glyphs); 4311 4312 g_return_if_fail(gui.ascii_glyphs->num_glyphs == sizeof(ascii_chars)); 4313 4314 for (i = 0; i < gui.ascii_glyphs->num_glyphs; ++i) 4315 { 4316 PangoGlyphGeometry *geom; 4317 4318 geom = &gui.ascii_glyphs->glyphs[i].geometry; 4319 geom->x_offset += MAX(0, width - geom->width) / 2; 4320 geom->width = width; 4321 } 4322 } 4323 4324 g_list_foreach(item_list, (GFunc)&pango_item_free, NULL); 4325 g_list_free(item_list); 4326 pango_attr_list_unref(attr_list); 4327} 4328 4329/* 4330 * Initialize Vim to use the font or fontset with the given name. 4331 * Return FAIL if the font could not be loaded, OK otherwise. 4332 */ 4333 int 4334gui_mch_init_font(char_u *font_name, int fontset UNUSED) 4335{ 4336 PangoFontDescription *font_desc; 4337 PangoLayout *layout; 4338 int width; 4339 4340 /* If font_name is NULL, this means to use the default, which should 4341 * be present on all proper Pango/fontconfig installations. */ 4342 if (font_name == NULL) 4343 font_name = (char_u *)DEFAULT_FONT; 4344 4345 font_desc = gui_mch_get_font(font_name, FALSE); 4346 4347 if (font_desc == NULL) 4348 return FAIL; 4349 4350 gui_mch_free_font(gui.norm_font); 4351 gui.norm_font = font_desc; 4352 4353 pango_context_set_font_description(gui.text_context, font_desc); 4354 4355 layout = pango_layout_new(gui.text_context); 4356 pango_layout_set_text(layout, "MW", 2); 4357 pango_layout_get_size(layout, &width, NULL); 4358 /* 4359 * Set char_width to half the width obtained from pango_layout_get_size() 4360 * for CJK fixed_width/bi-width fonts. An unpatched version of Xft leads 4361 * Pango to use the same width for both non-CJK characters (e.g. Latin 4362 * letters and numbers) and CJK characters. This results in 's p a c e d 4363 * o u t' rendering when a CJK 'fixed width' font is used. To work around 4364 * that, divide the width returned by Pango by 2 if cjk_width is equal to 4365 * width for CJK fonts. 4366 * 4367 * For related bugs, see: 4368 * http://bugzilla.gnome.org/show_bug.cgi?id=106618 4369 * http://bugzilla.gnome.org/show_bug.cgi?id=106624 4370 * 4371 * With this, for all four of the following cases, Vim works fine: 4372 * guifont=CJK_fixed_width_font 4373 * guifont=Non_CJK_fixed_font 4374 * guifont=Non_CJK_fixed_font,CJK_Fixed_font 4375 * guifont=Non_CJK_fixed_font guifontwide=CJK_fixed_font 4376 */ 4377 if (is_cjk_font(gui.norm_font)) 4378 { 4379 int cjk_width; 4380 4381 /* Measure the text extent of U+4E00 and U+4E8C */ 4382 pango_layout_set_text(layout, "\344\270\200\344\272\214", -1); 4383 pango_layout_get_size(layout, &cjk_width, NULL); 4384 4385 if (width == cjk_width) /* Xft not patched */ 4386 width /= 2; 4387 } 4388 g_object_unref(layout); 4389 4390 gui.char_width = (width / 2 + PANGO_SCALE - 1) / PANGO_SCALE; 4391 4392 /* A zero width may cause a crash. Happens for semi-invalid fontsets. */ 4393 if (gui.char_width <= 0) 4394 gui.char_width = 8; 4395 4396 gui_mch_adjust_charheight(); 4397 4398 /* Set the fontname, which will be used for information purposes */ 4399 hl_set_font_name(font_name); 4400 4401 get_styled_font_variants(); 4402 ascii_glyph_table_init(); 4403 4404 /* Avoid unnecessary overhead if 'guifontwide' is equal to 'guifont'. */ 4405 if (gui.wide_font != NULL 4406 && pango_font_description_equal(gui.norm_font, gui.wide_font)) 4407 { 4408 pango_font_description_free(gui.wide_font); 4409 gui.wide_font = NULL; 4410 } 4411 4412 if (gui_mch_maximized()) 4413 { 4414 int w, h; 4415 4416 /* Update lines and columns in accordance with the new font, keep the 4417 * window maximized. */ 4418 gtk_window_get_size(GTK_WINDOW(gui.mainwin), &w, &h); 4419 w -= get_menu_tool_width(); 4420 h -= get_menu_tool_height(); 4421 gui_resize_shell(w, h); 4422 } 4423 else 4424 { 4425 /* Preserve the logical dimensions of the screen. */ 4426 update_window_manager_hints(0, 0); 4427 } 4428 4429 return OK; 4430} 4431 4432/* 4433 * Get a reference to the font "name". 4434 * Return zero for failure. 4435 */ 4436 GuiFont 4437gui_mch_get_font(char_u *name, int report_error) 4438{ 4439 PangoFontDescription *font; 4440 4441 /* can't do this when GUI is not running */ 4442 if (!gui.in_use || name == NULL) 4443 return NULL; 4444 4445 if (output_conv.vc_type != CONV_NONE) 4446 { 4447 char_u *buf; 4448 4449 buf = string_convert(&output_conv, name, NULL); 4450 if (buf != NULL) 4451 { 4452 font = pango_font_description_from_string((const char *)buf); 4453 vim_free(buf); 4454 } 4455 else 4456 font = NULL; 4457 } 4458 else 4459 font = pango_font_description_from_string((const char *)name); 4460 4461 if (font != NULL) 4462 { 4463 PangoFont *real_font; 4464 4465 /* pango_context_load_font() bails out if no font size is set */ 4466 if (pango_font_description_get_size(font) <= 0) 4467 pango_font_description_set_size(font, 10 * PANGO_SCALE); 4468 4469 real_font = pango_context_load_font(gui.text_context, font); 4470 4471 if (real_font == NULL) 4472 { 4473 pango_font_description_free(font); 4474 font = NULL; 4475 } 4476 else 4477 g_object_unref(real_font); 4478 } 4479 4480 if (font == NULL) 4481 { 4482 if (report_error) 4483 EMSG2(_((char *)e_font), name); 4484 return NULL; 4485 } 4486 4487 return font; 4488} 4489 4490#if defined(FEAT_EVAL) || defined(PROTO) 4491/* 4492 * Return the name of font "font" in allocated memory. 4493 */ 4494 char_u * 4495gui_mch_get_fontname(GuiFont font, char_u *name UNUSED) 4496{ 4497 if (font != NOFONT) 4498 { 4499 char *pangoname = pango_font_description_to_string(font); 4500 4501 if (pangoname != NULL) 4502 { 4503 char_u *s = vim_strsave((char_u *)pangoname); 4504 4505 g_free(pangoname); 4506 return s; 4507 } 4508 } 4509 return NULL; 4510} 4511#endif 4512 4513/* 4514 * If a font is not going to be used, free its structure. 4515 */ 4516 void 4517gui_mch_free_font(GuiFont font) 4518{ 4519 if (font != NOFONT) 4520 pango_font_description_free(font); 4521} 4522 4523/* 4524 * Return the Pixel value (color) for the given color name. This routine was 4525 * pretty much taken from example code in the Silicon Graphics OSF/Motif 4526 * Programmer's Guide. 4527 * Return INVALCOLOR for error. 4528 */ 4529 guicolor_T 4530gui_mch_get_color(char_u *name) 4531{ 4532 /* A number of colors that some X11 systems don't have */ 4533 static const char *const vimnames[][2] = 4534 { 4535 {"LightRed", "#FFBBBB"}, 4536 {"LightGreen", "#88FF88"}, 4537 {"LightMagenta","#FFBBFF"}, 4538 {"DarkCyan", "#008888"}, 4539 {"DarkBlue", "#0000BB"}, 4540 {"DarkRed", "#BB0000"}, 4541 {"DarkMagenta", "#BB00BB"}, 4542 {"DarkGrey", "#BBBBBB"}, 4543 {"DarkYellow", "#BBBB00"}, 4544 {"Gray10", "#1A1A1A"}, 4545 {"Grey10", "#1A1A1A"}, 4546 {"Gray20", "#333333"}, 4547 {"Grey20", "#333333"}, 4548 {"Gray30", "#4D4D4D"}, 4549 {"Grey30", "#4D4D4D"}, 4550 {"Gray40", "#666666"}, 4551 {"Grey40", "#666666"}, 4552 {"Gray50", "#7F7F7F"}, 4553 {"Grey50", "#7F7F7F"}, 4554 {"Gray60", "#999999"}, 4555 {"Grey60", "#999999"}, 4556 {"Gray70", "#B3B3B3"}, 4557 {"Grey70", "#B3B3B3"}, 4558 {"Gray80", "#CCCCCC"}, 4559 {"Grey80", "#CCCCCC"}, 4560 {"Gray90", "#E5E5E5"}, 4561 {"Grey90", "#E5E5E5"}, 4562 {NULL, NULL} 4563 }; 4564 4565 if (!gui.in_use) /* can't do this when GUI not running */ 4566 return INVALCOLOR; 4567 4568 while (name != NULL) 4569 { 4570 GdkColor color; 4571 int parsed; 4572 int i; 4573 4574 parsed = gdk_color_parse((const char *)name, &color); 4575 4576 if (parsed) 4577 { 4578 gdk_colormap_alloc_color(gtk_widget_get_colormap(gui.drawarea), 4579 &color, FALSE, TRUE); 4580 return (guicolor_T)color.pixel; 4581 } 4582 /* add a few builtin names and try again */ 4583 for (i = 0; ; ++i) 4584 { 4585 if (vimnames[i][0] == NULL) 4586 { 4587 name = NULL; 4588 break; 4589 } 4590 if (STRICMP(name, vimnames[i][0]) == 0) 4591 { 4592 name = (char_u *)vimnames[i][1]; 4593 break; 4594 } 4595 } 4596 } 4597 4598 return INVALCOLOR; 4599} 4600 4601/* 4602 * Set the current text foreground color. 4603 */ 4604 void 4605gui_mch_set_fg_color(guicolor_T color) 4606{ 4607 gui.fgcolor->pixel = (unsigned long)color; 4608} 4609 4610/* 4611 * Set the current text background color. 4612 */ 4613 void 4614gui_mch_set_bg_color(guicolor_T color) 4615{ 4616 gui.bgcolor->pixel = (unsigned long)color; 4617} 4618 4619/* 4620 * Set the current text special color. 4621 */ 4622 void 4623gui_mch_set_sp_color(guicolor_T color) 4624{ 4625 gui.spcolor->pixel = (unsigned long)color; 4626} 4627 4628/* 4629 * Function-like convenience macro for the sake of efficiency. 4630 */ 4631#define INSERT_PANGO_ATTR(Attribute, AttrList, Start, End) \ 4632 G_STMT_START{ \ 4633 PangoAttribute *tmp_attr_; \ 4634 tmp_attr_ = (Attribute); \ 4635 tmp_attr_->start_index = (Start); \ 4636 tmp_attr_->end_index = (End); \ 4637 pango_attr_list_insert((AttrList), tmp_attr_); \ 4638 }G_STMT_END 4639 4640 static void 4641apply_wide_font_attr(char_u *s, int len, PangoAttrList *attr_list) 4642{ 4643 char_u *start = NULL; 4644 char_u *p; 4645 int uc; 4646 4647 for (p = s; p < s + len; p += utf_byte2len(*p)) 4648 { 4649 uc = utf_ptr2char(p); 4650 4651 if (start == NULL) 4652 { 4653 if (uc >= 0x80 && utf_char2cells(uc) == 2) 4654 start = p; 4655 } 4656 else if (uc < 0x80 /* optimization shortcut */ 4657 || (utf_char2cells(uc) != 2 && !utf_iscomposing(uc))) 4658 { 4659 INSERT_PANGO_ATTR(pango_attr_font_desc_new(gui.wide_font), 4660 attr_list, start - s, p - s); 4661 start = NULL; 4662 } 4663 } 4664 4665 if (start != NULL) 4666 INSERT_PANGO_ATTR(pango_attr_font_desc_new(gui.wide_font), 4667 attr_list, start - s, len); 4668} 4669 4670 static int 4671count_cluster_cells(char_u *s, PangoItem *item, 4672 PangoGlyphString* glyphs, int i, 4673 int *cluster_width, 4674 int *last_glyph_rbearing) 4675{ 4676 char_u *p; 4677 int next; /* glyph start index of next cluster */ 4678 int start, end; /* string segment of current cluster */ 4679 int width; /* real cluster width in Pango units */ 4680 int uc; 4681 int cellcount = 0; 4682 4683 width = glyphs->glyphs[i].geometry.width; 4684 4685 for (next = i + 1; next < glyphs->num_glyphs; ++next) 4686 { 4687 if (glyphs->glyphs[next].attr.is_cluster_start) 4688 break; 4689 else if (glyphs->glyphs[next].geometry.width > width) 4690 width = glyphs->glyphs[next].geometry.width; 4691 } 4692 4693 start = item->offset + glyphs->log_clusters[i]; 4694 end = item->offset + ((next < glyphs->num_glyphs) ? 4695 glyphs->log_clusters[next] : item->length); 4696 4697 for (p = s + start; p < s + end; p += utf_byte2len(*p)) 4698 { 4699 uc = utf_ptr2char(p); 4700 if (uc < 0x80) 4701 ++cellcount; 4702 else if (!utf_iscomposing(uc)) 4703 cellcount += utf_char2cells(uc); 4704 } 4705 4706 if (last_glyph_rbearing != NULL 4707 && cellcount > 0 && next == glyphs->num_glyphs) 4708 { 4709 PangoRectangle ink_rect; 4710 /* 4711 * If a certain combining mark had to be taken from a non-monospace 4712 * font, we have to compensate manually by adapting x_offset according 4713 * to the ink extents of the previous glyph. 4714 */ 4715 pango_font_get_glyph_extents(item->analysis.font, 4716 glyphs->glyphs[i].glyph, 4717 &ink_rect, NULL); 4718 4719 if (PANGO_RBEARING(ink_rect) > 0) 4720 *last_glyph_rbearing = PANGO_RBEARING(ink_rect); 4721 } 4722 4723 if (cellcount > 0) 4724 *cluster_width = width; 4725 4726 return cellcount; 4727} 4728 4729/* 4730 * If there are only combining characters in the cluster, we cannot just 4731 * change the width of the previous glyph since there is none. Therefore 4732 * some guesswork is needed. 4733 * 4734 * If ink_rect.x is negative Pango apparently has taken care of the composing 4735 * by itself. Actually setting x_offset = 0 should be sufficient then, but due 4736 * to problems with composing from different fonts we still need to fine-tune 4737 * x_offset to avoid ugliness. 4738 * 4739 * If ink_rect.x is not negative, force overstriking by pointing x_offset to 4740 * the position of the previous glyph. Apparently this happens only with old 4741 * X fonts which don't provide the special combining information needed by 4742 * Pango. 4743 */ 4744 static void 4745setup_zero_width_cluster(PangoItem *item, PangoGlyphInfo *glyph, 4746 int last_cellcount, int last_cluster_width, 4747 int last_glyph_rbearing) 4748{ 4749 PangoRectangle ink_rect; 4750 PangoRectangle logical_rect; 4751 int width; 4752 4753 width = last_cellcount * gui.char_width * PANGO_SCALE; 4754 glyph->geometry.x_offset = -width + MAX(0, width - last_cluster_width) / 2; 4755 glyph->geometry.width = 0; 4756 4757 pango_font_get_glyph_extents(item->analysis.font, 4758 glyph->glyph, 4759 &ink_rect, &logical_rect); 4760 if (ink_rect.x < 0) 4761 { 4762 glyph->geometry.x_offset += last_glyph_rbearing; 4763 glyph->geometry.y_offset = logical_rect.height 4764 - (gui.char_height - p_linespace) * PANGO_SCALE; 4765 } 4766 else 4767 /* If the accent width is smaller than the cluster width, position it 4768 * in the middle. */ 4769 glyph->geometry.x_offset = -width + MAX(0, width - ink_rect.width) / 2; 4770} 4771 4772 static void 4773draw_glyph_string(int row, int col, int num_cells, int flags, 4774 PangoFont *font, PangoGlyphString *glyphs) 4775{ 4776 if (!(flags & DRAW_TRANSP)) 4777 { 4778 gdk_gc_set_foreground(gui.text_gc, gui.bgcolor); 4779 4780 gdk_draw_rectangle(gui.drawarea->window, 4781 gui.text_gc, 4782 TRUE, 4783 FILL_X(col), 4784 FILL_Y(row), 4785 num_cells * gui.char_width, 4786 gui.char_height); 4787 } 4788 4789 gdk_gc_set_foreground(gui.text_gc, gui.fgcolor); 4790 4791 gdk_draw_glyphs(gui.drawarea->window, 4792 gui.text_gc, 4793 font, 4794 TEXT_X(col), 4795 TEXT_Y(row), 4796 glyphs); 4797 4798 /* redraw the contents with an offset of 1 to emulate bold */ 4799 if ((flags & DRAW_BOLD) && !gui.font_can_bold) 4800 gdk_draw_glyphs(gui.drawarea->window, 4801 gui.text_gc, 4802 font, 4803 TEXT_X(col) + 1, 4804 TEXT_Y(row), 4805 glyphs); 4806} 4807 4808/* 4809 * Draw underline and undercurl at the bottom of the character cell. 4810 */ 4811 static void 4812draw_under(int flags, int row, int col, int cells) 4813{ 4814 int i; 4815 int offset; 4816 static const int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 }; 4817 int y = FILL_Y(row + 1) - 1; 4818 4819 /* Undercurl: draw curl at the bottom of the character cell. */ 4820 if (flags & DRAW_UNDERC) 4821 { 4822 gdk_gc_set_foreground(gui.text_gc, gui.spcolor); 4823 for (i = FILL_X(col); i < FILL_X(col + cells); ++i) 4824 { 4825 offset = val[i % 8]; 4826 gdk_draw_point(gui.drawarea->window, gui.text_gc, i, y - offset); 4827 } 4828 gdk_gc_set_foreground(gui.text_gc, gui.fgcolor); 4829 } 4830 4831 /* Underline: draw a line at the bottom of the character cell. */ 4832 if (flags & DRAW_UNDERL) 4833 { 4834 /* When p_linespace is 0, overwrite the bottom row of pixels. 4835 * Otherwise put the line just below the character. */ 4836 if (p_linespace > 1) 4837 y -= p_linespace - 1; 4838 gdk_draw_line(gui.drawarea->window, gui.text_gc, 4839 FILL_X(col), y, 4840 FILL_X(col + cells) - 1, y); 4841 } 4842} 4843 4844 int 4845gui_gtk2_draw_string(int row, int col, char_u *s, int len, int flags) 4846{ 4847 GdkRectangle area; /* area for clip mask */ 4848 PangoGlyphString *glyphs; /* glyphs of current item */ 4849 int column_offset = 0; /* column offset in cells */ 4850 int i; 4851 char_u *conv_buf = NULL; /* result of UTF-8 conversion */ 4852 char_u *new_conv_buf; 4853 int convlen; 4854 char_u *sp, *bp; 4855 int plen; 4856 4857 if (gui.text_context == NULL || gui.drawarea->window == NULL) 4858 return len; 4859 4860 if (output_conv.vc_type != CONV_NONE) 4861 { 4862 /* 4863 * Convert characters from 'encoding' to 'termencoding', which is set 4864 * to UTF-8 by gui_mch_init(). did_set_string_option() in option.c 4865 * prohibits changing this to something else than UTF-8 if the GUI is 4866 * in use. 4867 */ 4868 convlen = len; 4869 conv_buf = string_convert(&output_conv, s, &convlen); 4870 g_return_val_if_fail(conv_buf != NULL, len); 4871 4872 /* Correct for differences in char width: some chars are 4873 * double-wide in 'encoding' but single-wide in utf-8. Add a space to 4874 * compensate for that. */ 4875 for (sp = s, bp = conv_buf; sp < s + len && bp < conv_buf + convlen; ) 4876 { 4877 plen = utf_ptr2len(bp); 4878 if ((*mb_ptr2cells)(sp) == 2 && utf_ptr2cells(bp) == 1) 4879 { 4880 new_conv_buf = alloc(convlen + 2); 4881 if (new_conv_buf == NULL) 4882 return len; 4883 plen += bp - conv_buf; 4884 mch_memmove(new_conv_buf, conv_buf, plen); 4885 new_conv_buf[plen] = ' '; 4886 mch_memmove(new_conv_buf + plen + 1, conv_buf + plen, 4887 convlen - plen + 1); 4888 vim_free(conv_buf); 4889 conv_buf = new_conv_buf; 4890 ++convlen; 4891 bp = conv_buf + plen; 4892 plen = 1; 4893 } 4894 sp += (*mb_ptr2len)(sp); 4895 bp += plen; 4896 } 4897 s = conv_buf; 4898 len = convlen; 4899 } 4900 4901 /* 4902 * Restrict all drawing to the current screen line in order to prevent 4903 * fuzzy font lookups from messing up the screen. 4904 */ 4905 area.x = gui.border_offset; 4906 area.y = FILL_Y(row); 4907 area.width = gui.num_cols * gui.char_width; 4908 area.height = gui.char_height; 4909 4910 gdk_gc_set_clip_origin(gui.text_gc, 0, 0); 4911 gdk_gc_set_clip_rectangle(gui.text_gc, &area); 4912 4913 glyphs = pango_glyph_string_new(); 4914 4915 /* 4916 * Optimization hack: If possible, skip the itemize and shaping process 4917 * for pure ASCII strings. This optimization is particularly effective 4918 * because Vim draws space characters to clear parts of the screen. 4919 */ 4920 if (!(flags & DRAW_ITALIC) 4921 && !((flags & DRAW_BOLD) && gui.font_can_bold) 4922 && gui.ascii_glyphs != NULL) 4923 { 4924 char_u *p; 4925 4926 for (p = s; p < s + len; ++p) 4927 if (*p & 0x80) 4928 goto not_ascii; 4929 4930 pango_glyph_string_set_size(glyphs, len); 4931 4932 for (i = 0; i < len; ++i) 4933 { 4934 glyphs->glyphs[i] = gui.ascii_glyphs->glyphs[s[i]]; 4935 glyphs->log_clusters[i] = i; 4936 } 4937 4938 draw_glyph_string(row, col, len, flags, gui.ascii_font, glyphs); 4939 4940 column_offset = len; 4941 } 4942 else 4943not_ascii: 4944 { 4945 PangoAttrList *attr_list; 4946 GList *item_list; 4947 int cluster_width; 4948 int last_glyph_rbearing; 4949 int cells = 0; /* cells occupied by current cluster */ 4950 4951 /* Safety check: pango crashes when invoked with invalid utf-8 4952 * characters. */ 4953 if (!utf_valid_string(s, s + len)) 4954 { 4955 column_offset = len; 4956 goto skipitall; 4957 } 4958 4959 /* original width of the current cluster */ 4960 cluster_width = PANGO_SCALE * gui.char_width; 4961 4962 /* right bearing of the last non-composing glyph */ 4963 last_glyph_rbearing = PANGO_SCALE * gui.char_width; 4964 4965 attr_list = pango_attr_list_new(); 4966 4967 /* If 'guifontwide' is set then use that for double-width characters. 4968 * Otherwise just go with 'guifont' and let Pango do its thing. */ 4969 if (gui.wide_font != NULL) 4970 apply_wide_font_attr(s, len, attr_list); 4971 4972 if ((flags & DRAW_BOLD) && gui.font_can_bold) 4973 INSERT_PANGO_ATTR(pango_attr_weight_new(PANGO_WEIGHT_BOLD), 4974 attr_list, 0, len); 4975 if (flags & DRAW_ITALIC) 4976 INSERT_PANGO_ATTR(pango_attr_style_new(PANGO_STYLE_ITALIC), 4977 attr_list, 0, len); 4978 /* 4979 * Break the text into segments with consistent directional level 4980 * and shaping engine. Pure Latin text needs only a single segment, 4981 * so there's no need to worry about the loop's efficiency. Better 4982 * try to optimize elsewhere, e.g. reducing exposes and stuff :) 4983 */ 4984 item_list = pango_itemize(gui.text_context, 4985 (const char *)s, 0, len, attr_list, NULL); 4986 4987 while (item_list != NULL) 4988 { 4989 PangoItem *item; 4990 int item_cells = 0; /* item length in cells */ 4991 4992 item = (PangoItem *)item_list->data; 4993 item_list = g_list_delete_link(item_list, item_list); 4994 /* 4995 * Increment the bidirectional embedding level by 1 if it is not 4996 * even. An odd number means the output will be RTL, but we don't 4997 * want that since Vim handles right-to-left text on its own. It 4998 * would probably be sufficient to just set level = 0, but you can 4999 * never know :) 5000 * 5001 * Unfortunately we can't take advantage of Pango's ability to 5002 * render both LTR and RTL at the same time. In order to support 5003 * that, Vim's main screen engine would have to make use of Pango 5004 * functionality. 5005 */ 5006 item->analysis.level = (item->analysis.level + 1) & (~1U); 5007 5008 /* HACK: Overrule the shape engine, we don't want shaping to be 5009 * done, because drawing the cursor would change the display. */ 5010 item->analysis.shape_engine = default_shape_engine; 5011 5012 pango_shape((const char *)s + item->offset, item->length, 5013 &item->analysis, glyphs); 5014 /* 5015 * Fixed-width hack: iterate over the array and assign a fixed 5016 * width to each glyph, thus overriding the choice made by the 5017 * shaping engine. We use utf_char2cells() to determine the 5018 * number of cells needed. 5019 * 5020 * Also perform all kind of dark magic to get composing 5021 * characters right (and pretty too of course). 5022 */ 5023 for (i = 0; i < glyphs->num_glyphs; ++i) 5024 { 5025 PangoGlyphInfo *glyph; 5026 5027 glyph = &glyphs->glyphs[i]; 5028 5029 if (glyph->attr.is_cluster_start) 5030 { 5031 int cellcount; 5032 5033 cellcount = count_cluster_cells( 5034 s, item, glyphs, i, &cluster_width, 5035 (item_list != NULL) ? &last_glyph_rbearing : NULL); 5036 5037 if (cellcount > 0) 5038 { 5039 int width; 5040 5041 width = cellcount * gui.char_width * PANGO_SCALE; 5042 glyph->geometry.x_offset += 5043 MAX(0, width - cluster_width) / 2; 5044 glyph->geometry.width = width; 5045 } 5046 else 5047 { 5048 /* If there are only combining characters in the 5049 * cluster, we cannot just change the width of the 5050 * previous glyph since there is none. Therefore 5051 * some guesswork is needed. */ 5052 setup_zero_width_cluster(item, glyph, cells, 5053 cluster_width, 5054 last_glyph_rbearing); 5055 } 5056 5057 item_cells += cellcount; 5058 cells = cellcount; 5059 } 5060 else if (i > 0) 5061 { 5062 int width; 5063 5064 /* There is a previous glyph, so we deal with combining 5065 * characters the canonical way. 5066 * In some circumstances Pango uses a positive x_offset, 5067 * then use the width of the previous glyph for this one 5068 * and set the previous width to zero. 5069 * Otherwise we get a negative x_offset, Pango has already 5070 * positioned the combining char, keep the widths as they 5071 * are. 5072 * For both adjust the x_offset to position the glyph in 5073 * the middle. */ 5074 if (glyph->geometry.x_offset >= 0) 5075 { 5076 glyphs->glyphs[i].geometry.width = 5077 glyphs->glyphs[i - 1].geometry.width; 5078 glyphs->glyphs[i - 1].geometry.width = 0; 5079 } 5080 width = cells * gui.char_width * PANGO_SCALE; 5081 glyph->geometry.x_offset += 5082 MAX(0, width - cluster_width) / 2; 5083 } 5084 else /* i == 0 "cannot happen" */ 5085 { 5086 glyph->geometry.width = 0; 5087 } 5088 } 5089 5090 /*** Aaaaand action! ***/ 5091 draw_glyph_string(row, col + column_offset, item_cells, 5092 flags, item->analysis.font, glyphs); 5093 5094 pango_item_free(item); 5095 5096 column_offset += item_cells; 5097 } 5098 5099 pango_attr_list_unref(attr_list); 5100 } 5101 5102skipitall: 5103 /* Draw underline and undercurl. */ 5104 draw_under(flags, row, col, column_offset); 5105 5106 pango_glyph_string_free(glyphs); 5107 vim_free(conv_buf); 5108 5109 gdk_gc_set_clip_rectangle(gui.text_gc, NULL); 5110 5111 return column_offset; 5112} 5113 5114/* 5115 * Return OK if the key with the termcap name "name" is supported. 5116 */ 5117 int 5118gui_mch_haskey(char_u *name) 5119{ 5120 int i; 5121 5122 for (i = 0; special_keys[i].key_sym != 0; i++) 5123 if (name[0] == special_keys[i].code0 5124 && name[1] == special_keys[i].code1) 5125 return OK; 5126 return FAIL; 5127} 5128 5129#if defined(FEAT_TITLE) \ 5130 || defined(PROTO) 5131/* 5132 * Return the text window-id and display. Only required for X-based GUI's 5133 */ 5134 int 5135gui_get_x11_windis(Window *win, Display **dis) 5136{ 5137 if (gui.mainwin != NULL && gui.mainwin->window != NULL) 5138 { 5139 *dis = GDK_WINDOW_XDISPLAY(gui.mainwin->window); 5140 *win = GDK_WINDOW_XWINDOW(gui.mainwin->window); 5141 return OK; 5142 } 5143 5144 *dis = NULL; 5145 *win = 0; 5146 return FAIL; 5147} 5148#endif 5149 5150#if defined(FEAT_CLIENTSERVER) \ 5151 || (defined(FEAT_X11) && defined(FEAT_CLIPBOARD)) || defined(PROTO) 5152 5153 Display * 5154gui_mch_get_display(void) 5155{ 5156 if (gui.mainwin != NULL && gui.mainwin->window != NULL) 5157 return GDK_WINDOW_XDISPLAY(gui.mainwin->window); 5158 else 5159 return NULL; 5160} 5161#endif 5162 5163 void 5164gui_mch_beep(void) 5165{ 5166#ifdef HAVE_GTK_MULTIHEAD 5167 GdkDisplay *display; 5168 5169 if (gui.mainwin != NULL && GTK_WIDGET_REALIZED(gui.mainwin)) 5170 display = gtk_widget_get_display(gui.mainwin); 5171 else 5172 display = gdk_display_get_default(); 5173 5174 if (display != NULL) 5175 gdk_display_beep(display); 5176#else 5177 gdk_beep(); 5178#endif 5179} 5180 5181 void 5182gui_mch_flash(int msec) 5183{ 5184 GdkGCValues values; 5185 GdkGC *invert_gc; 5186 5187 if (gui.drawarea->window == NULL) 5188 return; 5189 5190 values.foreground.pixel = gui.norm_pixel ^ gui.back_pixel; 5191 values.background.pixel = gui.norm_pixel ^ gui.back_pixel; 5192 values.function = GDK_XOR; 5193 invert_gc = gdk_gc_new_with_values(gui.drawarea->window, 5194 &values, 5195 GDK_GC_FOREGROUND | 5196 GDK_GC_BACKGROUND | 5197 GDK_GC_FUNCTION); 5198 gdk_gc_set_exposures(invert_gc, 5199 gui.visibility != GDK_VISIBILITY_UNOBSCURED); 5200 /* 5201 * Do a visual beep by changing back and forth in some undetermined way, 5202 * the foreground and background colors. This is due to the fact that 5203 * there can't be really any prediction about the effects of XOR on 5204 * arbitrary X11 servers. However this seems to be enough for what we 5205 * intend it to do. 5206 */ 5207 gdk_draw_rectangle(gui.drawarea->window, invert_gc, 5208 TRUE, 5209 0, 0, 5210 FILL_X((int)Columns) + gui.border_offset, 5211 FILL_Y((int)Rows) + gui.border_offset); 5212 5213 gui_mch_flush(); 5214 ui_delay((long)msec, TRUE); /* wait so many msec */ 5215 5216 gdk_draw_rectangle(gui.drawarea->window, invert_gc, 5217 TRUE, 5218 0, 0, 5219 FILL_X((int)Columns) + gui.border_offset, 5220 FILL_Y((int)Rows) + gui.border_offset); 5221 5222 gdk_gc_destroy(invert_gc); 5223} 5224 5225/* 5226 * Invert a rectangle from row r, column c, for nr rows and nc columns. 5227 */ 5228 void 5229gui_mch_invert_rectangle(int r, int c, int nr, int nc) 5230{ 5231 GdkGCValues values; 5232 GdkGC *invert_gc; 5233 5234 if (gui.drawarea->window == NULL) 5235 return; 5236 5237 values.foreground.pixel = gui.norm_pixel ^ gui.back_pixel; 5238 values.background.pixel = gui.norm_pixel ^ gui.back_pixel; 5239 values.function = GDK_XOR; 5240 invert_gc = gdk_gc_new_with_values(gui.drawarea->window, 5241 &values, 5242 GDK_GC_FOREGROUND | 5243 GDK_GC_BACKGROUND | 5244 GDK_GC_FUNCTION); 5245 gdk_gc_set_exposures(invert_gc, gui.visibility != 5246 GDK_VISIBILITY_UNOBSCURED); 5247 gdk_draw_rectangle(gui.drawarea->window, invert_gc, 5248 TRUE, 5249 FILL_X(c), FILL_Y(r), 5250 (nc) * gui.char_width, (nr) * gui.char_height); 5251 gdk_gc_destroy(invert_gc); 5252} 5253 5254/* 5255 * Iconify the GUI window. 5256 */ 5257 void 5258gui_mch_iconify(void) 5259{ 5260 gtk_window_iconify(GTK_WINDOW(gui.mainwin)); 5261} 5262 5263#if defined(FEAT_EVAL) || defined(PROTO) 5264/* 5265 * Bring the Vim window to the foreground. 5266 */ 5267 void 5268gui_mch_set_foreground(void) 5269{ 5270 gtk_window_present(GTK_WINDOW(gui.mainwin)); 5271} 5272#endif 5273 5274/* 5275 * Draw a cursor without focus. 5276 */ 5277 void 5278gui_mch_draw_hollow_cursor(guicolor_T color) 5279{ 5280 int i = 1; 5281 5282 if (gui.drawarea->window == NULL) 5283 return; 5284 5285 gui_mch_set_fg_color(color); 5286 5287 gdk_gc_set_foreground(gui.text_gc, gui.fgcolor); 5288 if (mb_lefthalve(gui.row, gui.col)) 5289 i = 2; 5290 gdk_draw_rectangle(gui.drawarea->window, gui.text_gc, 5291 FALSE, 5292 FILL_X(gui.col), FILL_Y(gui.row), 5293 i * gui.char_width - 1, gui.char_height - 1); 5294} 5295 5296/* 5297 * Draw part of a cursor, "w" pixels wide, and "h" pixels high, using 5298 * color "color". 5299 */ 5300 void 5301gui_mch_draw_part_cursor(int w, int h, guicolor_T color) 5302{ 5303 if (gui.drawarea->window == NULL) 5304 return; 5305 5306 gui_mch_set_fg_color(color); 5307 5308 gdk_gc_set_foreground(gui.text_gc, gui.fgcolor); 5309 gdk_draw_rectangle(gui.drawarea->window, gui.text_gc, 5310 TRUE, 5311#ifdef FEAT_RIGHTLEFT 5312 /* vertical line should be on the right of current point */ 5313 CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w : 5314#endif 5315 FILL_X(gui.col), 5316 FILL_Y(gui.row) + gui.char_height - h, 5317 w, h); 5318} 5319 5320 5321/* 5322 * Catch up with any queued X11 events. This may put keyboard input into the 5323 * input buffer, call resize call-backs, trigger timers etc. If there is 5324 * nothing in the X11 event queue (& no timers pending), then we return 5325 * immediately. 5326 */ 5327 void 5328gui_mch_update(void) 5329{ 5330 while (g_main_context_pending(NULL) && !vim_is_input_buf_full()) 5331 g_main_context_iteration(NULL, TRUE); 5332} 5333 5334 static gint 5335input_timer_cb(gpointer data) 5336{ 5337 int *timed_out = (int *) data; 5338 5339 /* Just inform the caller about the occurrence of it */ 5340 *timed_out = TRUE; 5341 5342 return FALSE; /* don't happen again */ 5343} 5344 5345#ifdef FEAT_SNIFF 5346/* 5347 * Callback function, used when data is available on the SNiFF connection. 5348 */ 5349 static void 5350sniff_request_cb( 5351 gpointer data, 5352 gint source_fd, 5353 GdkInputCondition condition) 5354{ 5355 static char_u bytes[3] = {CSI, (int)KS_EXTRA, (int)KE_SNIFF}; 5356 5357 add_to_input_buf(bytes, 3); 5358} 5359#endif 5360 5361/* 5362 * GUI input routine called by gui_wait_for_chars(). Waits for a character 5363 * from the keyboard. 5364 * wtime == -1 Wait forever. 5365 * wtime == 0 This should never happen. 5366 * wtime > 0 Wait wtime milliseconds for a character. 5367 * Returns OK if a character was found to be available within the given time, 5368 * or FAIL otherwise. 5369 */ 5370 int 5371gui_mch_wait_for_chars(long wtime) 5372{ 5373 int focus; 5374 guint timer; 5375 static int timed_out; 5376#ifdef FEAT_SNIFF 5377 static int sniff_on = 0; 5378 static gint sniff_input_id = 0; 5379#endif 5380 5381#ifdef FEAT_SNIFF 5382 if (sniff_on && !want_sniff_request) 5383 { 5384 if (sniff_input_id) 5385 gdk_input_remove(sniff_input_id); 5386 sniff_on = 0; 5387 } 5388 else if (!sniff_on && want_sniff_request) 5389 { 5390 /* Add fd_from_sniff to watch for available data in main loop. */ 5391 sniff_input_id = gdk_input_add(fd_from_sniff, 5392 GDK_INPUT_READ, sniff_request_cb, NULL); 5393 sniff_on = 1; 5394 } 5395#endif 5396 5397 timed_out = FALSE; 5398 5399 /* this timeout makes sure that we will return if no characters arrived in 5400 * time */ 5401 5402 if (wtime > 0) 5403 timer = gtk_timeout_add((guint32)wtime, input_timer_cb, &timed_out); 5404 else 5405 timer = 0; 5406 5407 focus = gui.in_focus; 5408 5409 do 5410 { 5411 /* Stop or start blinking when focus changes */ 5412 if (gui.in_focus != focus) 5413 { 5414 if (gui.in_focus) 5415 gui_mch_start_blink(); 5416 else 5417 gui_mch_stop_blink(); 5418 focus = gui.in_focus; 5419 } 5420 5421#if defined(FEAT_NETBEANS_INTG) 5422 /* Process the queued netbeans messages. */ 5423 netbeans_parse_messages(); 5424#endif 5425 5426 /* 5427 * Loop in GTK+ processing until a timeout or input occurs. 5428 * Skip this if input is available anyway (can happen in rare 5429 * situations, sort of race condition). 5430 */ 5431 if (!input_available()) 5432 g_main_context_iteration(NULL, TRUE); 5433 5434 /* Got char, return immediately */ 5435 if (input_available()) 5436 { 5437 if (timer != 0 && !timed_out) 5438 gtk_timeout_remove(timer); 5439 return OK; 5440 } 5441 } while (wtime < 0 || !timed_out); 5442 5443 /* 5444 * Flush all eventually pending (drawing) events. 5445 */ 5446 gui_mch_update(); 5447 5448 return FAIL; 5449} 5450 5451 5452/**************************************************************************** 5453 * Output drawing routines. 5454 ****************************************************************************/ 5455 5456 5457/* Flush any output to the screen */ 5458 void 5459gui_mch_flush(void) 5460{ 5461#ifdef HAVE_GTK_MULTIHEAD 5462 if (gui.mainwin != NULL && GTK_WIDGET_REALIZED(gui.mainwin)) 5463 gdk_display_sync(gtk_widget_get_display(gui.mainwin)); 5464#else 5465 gdk_flush(); /* historical misnomer: calls XSync(), not XFlush() */ 5466#endif 5467 /* This happens to actually do what gui_mch_flush() is supposed to do, 5468 * according to the comment above. */ 5469 if (gui.drawarea != NULL && gui.drawarea->window != NULL) 5470 gdk_window_process_updates(gui.drawarea->window, FALSE); 5471} 5472 5473/* 5474 * Clear a rectangular region of the screen from text pos (row1, col1) to 5475 * (row2, col2) inclusive. 5476 */ 5477 void 5478gui_mch_clear_block(int row1, int col1, int row2, int col2) 5479{ 5480 GdkColor color; 5481 5482 if (gui.drawarea->window == NULL) 5483 return; 5484 5485 color.pixel = gui.back_pixel; 5486 5487 gdk_gc_set_foreground(gui.text_gc, &color); 5488 5489 /* Clear one extra pixel at the far right, for when bold characters have 5490 * spilled over to the window border. */ 5491 gdk_draw_rectangle(gui.drawarea->window, gui.text_gc, TRUE, 5492 FILL_X(col1), FILL_Y(row1), 5493 (col2 - col1 + 1) * gui.char_width 5494 + (col2 == Columns - 1), 5495 (row2 - row1 + 1) * gui.char_height); 5496} 5497 5498 void 5499gui_mch_clear_all(void) 5500{ 5501 if (gui.drawarea->window != NULL) 5502 gdk_window_clear(gui.drawarea->window); 5503} 5504 5505/* 5506 * Redraw any text revealed by scrolling up/down. 5507 */ 5508 static void 5509check_copy_area(void) 5510{ 5511 GdkEvent *event; 5512 int expose_count; 5513 5514 if (gui.visibility != GDK_VISIBILITY_PARTIAL) 5515 return; 5516 5517 /* Avoid redrawing the cursor while scrolling or it'll end up where 5518 * we don't want it to be. I'm not sure if it's correct to call 5519 * gui_dont_update_cursor() at this point but it works as a quick 5520 * fix for now. */ 5521 gui_dont_update_cursor(); 5522 5523 do 5524 { 5525 /* Wait to check whether the scroll worked or not. */ 5526 event = gdk_event_get_graphics_expose(gui.drawarea->window); 5527 5528 if (event == NULL) 5529 break; /* received NoExpose event */ 5530 5531 gui_redraw(event->expose.area.x, event->expose.area.y, 5532 event->expose.area.width, event->expose.area.height); 5533 5534 expose_count = event->expose.count; 5535 gdk_event_free(event); 5536 } 5537 while (expose_count > 0); /* more events follow */ 5538 5539 gui_can_update_cursor(); 5540} 5541 5542/* 5543 * Delete the given number of lines from the given row, scrolling up any 5544 * text further down within the scroll region. 5545 */ 5546 void 5547gui_mch_delete_lines(int row, int num_lines) 5548{ 5549 if (gui.visibility == GDK_VISIBILITY_FULLY_OBSCURED) 5550 return; /* Can't see the window */ 5551 5552 gdk_gc_set_foreground(gui.text_gc, gui.fgcolor); 5553 gdk_gc_set_background(gui.text_gc, gui.bgcolor); 5554 5555 /* copy one extra pixel, for when bold has spilled over */ 5556 gdk_window_copy_area(gui.drawarea->window, gui.text_gc, 5557 FILL_X(gui.scroll_region_left), FILL_Y(row), 5558 gui.drawarea->window, 5559 FILL_X(gui.scroll_region_left), 5560 FILL_Y(row + num_lines), 5561 gui.char_width * (gui.scroll_region_right 5562 - gui.scroll_region_left + 1) + 1, 5563 gui.char_height * (gui.scroll_region_bot - row - num_lines + 1)); 5564 5565 gui_clear_block(gui.scroll_region_bot - num_lines + 1, 5566 gui.scroll_region_left, 5567 gui.scroll_region_bot, gui.scroll_region_right); 5568 check_copy_area(); 5569} 5570 5571/* 5572 * Insert the given number of lines before the given row, scrolling down any 5573 * following text within the scroll region. 5574 */ 5575 void 5576gui_mch_insert_lines(int row, int num_lines) 5577{ 5578 if (gui.visibility == GDK_VISIBILITY_FULLY_OBSCURED) 5579 return; /* Can't see the window */ 5580 5581 gdk_gc_set_foreground(gui.text_gc, gui.fgcolor); 5582 gdk_gc_set_background(gui.text_gc, gui.bgcolor); 5583 5584 /* copy one extra pixel, for when bold has spilled over */ 5585 gdk_window_copy_area(gui.drawarea->window, gui.text_gc, 5586 FILL_X(gui.scroll_region_left), FILL_Y(row + num_lines), 5587 gui.drawarea->window, 5588 FILL_X(gui.scroll_region_left), FILL_Y(row), 5589 gui.char_width * (gui.scroll_region_right 5590 - gui.scroll_region_left + 1) + 1, 5591 gui.char_height * (gui.scroll_region_bot - row - num_lines + 1)); 5592 5593 gui_clear_block(row, gui.scroll_region_left, 5594 row + num_lines - 1, gui.scroll_region_right); 5595 check_copy_area(); 5596} 5597 5598/* 5599 * X Selection stuff, for cutting and pasting text to other windows. 5600 */ 5601 void 5602clip_mch_request_selection(VimClipboard *cbd) 5603{ 5604 GdkAtom target; 5605 unsigned i; 5606 time_t start; 5607 5608 for (i = 0; i < N_SELECTION_TARGETS; ++i) 5609 { 5610 if (!clip_html && selection_targets[i].info == TARGET_HTML) 5611 continue; 5612 received_selection = RS_NONE; 5613 target = gdk_atom_intern(selection_targets[i].target, FALSE); 5614 5615 gtk_selection_convert(gui.drawarea, 5616 cbd->gtk_sel_atom, target, 5617 (guint32)GDK_CURRENT_TIME); 5618 5619 /* Hack: Wait up to three seconds for the selection. A hang was 5620 * noticed here when using the netrw plugin combined with ":gui" 5621 * during the FocusGained event. */ 5622 start = time(NULL); 5623 while (received_selection == RS_NONE && time(NULL) < start + 3) 5624 g_main_context_iteration(NULL, TRUE); /* wait for selection_received_cb */ 5625 5626 if (received_selection != RS_FAIL) 5627 return; 5628 } 5629 5630 /* Final fallback position - use the X CUT_BUFFER0 store */ 5631 yank_cut_buffer0(GDK_WINDOW_XDISPLAY(gui.mainwin->window), cbd); 5632} 5633 5634/* 5635 * Disown the selection. 5636 */ 5637 void 5638clip_mch_lose_selection(VimClipboard *cbd UNUSED) 5639{ 5640 /* WEIRD: when using NULL to actually disown the selection, we lose the 5641 * selection the first time we own it. */ 5642 /* 5643 gtk_selection_owner_set(NULL, cbd->gtk_sel_atom, (guint32)GDK_CURRENT_TIME); 5644 gui_mch_update(); 5645 */ 5646} 5647 5648/* 5649 * Own the selection and return OK if it worked. 5650 */ 5651 int 5652clip_mch_own_selection(VimClipboard *cbd) 5653{ 5654 int success; 5655 5656 success = gtk_selection_owner_set(gui.drawarea, cbd->gtk_sel_atom, 5657 clipboard_event_time); 5658 gui_mch_update(); 5659 return (success) ? OK : FAIL; 5660} 5661 5662/* 5663 * Send the current selection to the clipboard. Do nothing for X because we 5664 * will fill in the selection only when requested by another app. 5665 */ 5666 void 5667clip_mch_set_selection(VimClipboard *cbd UNUSED) 5668{ 5669} 5670 5671 5672#if defined(FEAT_MENU) || defined(PROTO) 5673/* 5674 * Make a menu item appear either active or not active (grey or not grey). 5675 */ 5676 void 5677gui_mch_menu_grey(vimmenu_T *menu, int grey) 5678{ 5679 if (menu->id == NULL) 5680 return; 5681 5682 if (menu_is_separator(menu->name)) 5683 grey = TRUE; 5684 5685 gui_mch_menu_hidden(menu, FALSE); 5686 /* Be clever about bitfields versus true booleans here! */ 5687 if (!GTK_WIDGET_SENSITIVE(menu->id) == !grey) 5688 { 5689 gtk_widget_set_sensitive(menu->id, !grey); 5690 gui_mch_update(); 5691 } 5692} 5693 5694/* 5695 * Make menu item hidden or not hidden. 5696 */ 5697 void 5698gui_mch_menu_hidden(vimmenu_T *menu, int hidden) 5699{ 5700 if (menu->id == 0) 5701 return; 5702 5703 if (hidden) 5704 { 5705 if (GTK_WIDGET_VISIBLE(menu->id)) 5706 { 5707 gtk_widget_hide(menu->id); 5708 gui_mch_update(); 5709 } 5710 } 5711 else 5712 { 5713 if (!GTK_WIDGET_VISIBLE(menu->id)) 5714 { 5715 gtk_widget_show(menu->id); 5716 gui_mch_update(); 5717 } 5718 } 5719} 5720 5721/* 5722 * This is called after setting all the menus to grey/hidden or not. 5723 */ 5724 void 5725gui_mch_draw_menubar(void) 5726{ 5727 /* just make sure that the visual changes get effect immediately */ 5728 gui_mch_update(); 5729} 5730#endif /* FEAT_MENU */ 5731 5732/* 5733 * Scrollbar stuff. 5734 */ 5735 void 5736gui_mch_enable_scrollbar(scrollbar_T *sb, int flag) 5737{ 5738 if (sb->id == NULL) 5739 return; 5740 5741 if (flag) 5742 gtk_widget_show(sb->id); 5743 else 5744 gtk_widget_hide(sb->id); 5745 5746 update_window_manager_hints(0, 0); 5747} 5748 5749 5750/* 5751 * Return the RGB value of a pixel as long. 5752 */ 5753 long_u 5754gui_mch_get_rgb(guicolor_T pixel) 5755{ 5756 GdkColor color; 5757 gdk_colormap_query_color(gtk_widget_get_colormap(gui.drawarea), 5758 (unsigned long)pixel, &color); 5759 5760 return (((unsigned)color.red & 0xff00) << 8) 5761 | ((unsigned)color.green & 0xff00) 5762 | (((unsigned)color.blue & 0xff00) >> 8); 5763} 5764 5765/* 5766 * Get current mouse coordinates in text window. 5767 */ 5768 void 5769gui_mch_getmouse(int *x, int *y) 5770{ 5771 gdk_window_get_pointer(gui.drawarea->window, x, y, NULL); 5772} 5773 5774 void 5775gui_mch_setmouse(int x, int y) 5776{ 5777 /* Sorry for the Xlib call, but we can't avoid it, since there is no 5778 * internal GDK mechanism present to accomplish this. (and for good 5779 * reason...) */ 5780 XWarpPointer(GDK_WINDOW_XDISPLAY(gui.drawarea->window), 5781 (Window)0, GDK_WINDOW_XWINDOW(gui.drawarea->window), 5782 0, 0, 0U, 0U, x, y); 5783} 5784 5785 5786#ifdef FEAT_MOUSESHAPE 5787/* The last set mouse pointer shape is remembered, to be used when it goes 5788 * from hidden to not hidden. */ 5789static int last_shape = 0; 5790#endif 5791 5792/* 5793 * Use the blank mouse pointer or not. 5794 * 5795 * hide: TRUE = use blank ptr, FALSE = use parent ptr 5796 */ 5797 void 5798gui_mch_mousehide(int hide) 5799{ 5800 if (gui.pointer_hidden != hide) 5801 { 5802 gui.pointer_hidden = hide; 5803 if (gui.drawarea->window && gui.blank_pointer != NULL) 5804 { 5805 if (hide) 5806 gdk_window_set_cursor(gui.drawarea->window, gui.blank_pointer); 5807 else 5808#ifdef FEAT_MOUSESHAPE 5809 mch_set_mouse_shape(last_shape); 5810#else 5811 gdk_window_set_cursor(gui.drawarea->window, NULL); 5812#endif 5813 } 5814 } 5815} 5816 5817#if defined(FEAT_MOUSESHAPE) || defined(PROTO) 5818 5819/* Table for shape IDs. Keep in sync with the mshape_names[] table in 5820 * misc2.c! */ 5821static const int mshape_ids[] = 5822{ 5823 GDK_LEFT_PTR, /* arrow */ 5824 GDK_CURSOR_IS_PIXMAP, /* blank */ 5825 GDK_XTERM, /* beam */ 5826 GDK_SB_V_DOUBLE_ARROW, /* updown */ 5827 GDK_SIZING, /* udsizing */ 5828 GDK_SB_H_DOUBLE_ARROW, /* leftright */ 5829 GDK_SIZING, /* lrsizing */ 5830 GDK_WATCH, /* busy */ 5831 GDK_X_CURSOR, /* no */ 5832 GDK_CROSSHAIR, /* crosshair */ 5833 GDK_HAND1, /* hand1 */ 5834 GDK_HAND2, /* hand2 */ 5835 GDK_PENCIL, /* pencil */ 5836 GDK_QUESTION_ARROW, /* question */ 5837 GDK_RIGHT_PTR, /* right-arrow */ 5838 GDK_CENTER_PTR, /* up-arrow */ 5839 GDK_LEFT_PTR /* last one */ 5840}; 5841 5842 void 5843mch_set_mouse_shape(int shape) 5844{ 5845 int id; 5846 GdkCursor *c; 5847 5848 if (gui.drawarea->window == NULL) 5849 return; 5850 5851 if (shape == MSHAPE_HIDE || gui.pointer_hidden) 5852 gdk_window_set_cursor(gui.drawarea->window, gui.blank_pointer); 5853 else 5854 { 5855 if (shape >= MSHAPE_NUMBERED) 5856 { 5857 id = shape - MSHAPE_NUMBERED; 5858 if (id >= GDK_LAST_CURSOR) 5859 id = GDK_LEFT_PTR; 5860 else 5861 id &= ~1; /* they are always even (why?) */ 5862 } 5863 else if (shape < (int)(sizeof(mshape_ids) / sizeof(int))) 5864 id = mshape_ids[shape]; 5865 else 5866 return; 5867# ifdef HAVE_GTK_MULTIHEAD 5868 c = gdk_cursor_new_for_display( 5869 gtk_widget_get_display(gui.drawarea), (GdkCursorType)id); 5870# else 5871 c = gdk_cursor_new((GdkCursorType)id); 5872# endif 5873 gdk_window_set_cursor(gui.drawarea->window, c); 5874 gdk_cursor_destroy(c); /* Unref, actually. Bloody GTK+ 1. */ 5875 } 5876 if (shape != MSHAPE_HIDE) 5877 last_shape = shape; 5878} 5879#endif /* FEAT_MOUSESHAPE */ 5880 5881 5882#if defined(FEAT_SIGN_ICONS) || defined(PROTO) 5883/* 5884 * Signs are currently always 2 chars wide. With GTK+ 2, the image will be 5885 * scaled down if the current font is not big enough, or scaled up if the image 5886 * size is less than 3/4 of the maximum sign size. With GTK+ 1, the pixmap 5887 * will be cut off if the current font is not big enough, or centered if it's 5888 * too small. 5889 */ 5890# define SIGN_WIDTH (2 * gui.char_width) 5891# define SIGN_HEIGHT (gui.char_height) 5892# define SIGN_ASPECT ((double)SIGN_HEIGHT / (double)SIGN_WIDTH) 5893 5894 void 5895gui_mch_drawsign(int row, int col, int typenr) 5896{ 5897 GdkPixbuf *sign; 5898 5899 sign = (GdkPixbuf *)sign_get_image(typenr); 5900 5901 if (sign != NULL && gui.drawarea != NULL && gui.drawarea->window != NULL) 5902 { 5903 int width; 5904 int height; 5905 int xoffset; 5906 int yoffset; 5907 int need_scale; 5908 5909 width = gdk_pixbuf_get_width(sign); 5910 height = gdk_pixbuf_get_height(sign); 5911 /* 5912 * Decide whether we need to scale. Allow one pixel of border 5913 * width to be cut off, in order to avoid excessive scaling for 5914 * tiny differences in font size. 5915 */ 5916 need_scale = (width > SIGN_WIDTH + 2 5917 || height > SIGN_HEIGHT + 2 5918 || (width < 3 * SIGN_WIDTH / 4 5919 && height < 3 * SIGN_HEIGHT / 4)); 5920 if (need_scale) 5921 { 5922 double aspect; 5923 5924 /* Keep the original aspect ratio */ 5925 aspect = (double)height / (double)width; 5926 width = (double)SIGN_WIDTH * SIGN_ASPECT / aspect; 5927 width = MIN(width, SIGN_WIDTH); 5928 height = (double)width * aspect; 5929 5930 /* This doesn't seem to be worth caching, and doing so 5931 * would complicate the code quite a bit. */ 5932 sign = gdk_pixbuf_scale_simple(sign, width, height, 5933 GDK_INTERP_BILINEAR); 5934 if (sign == NULL) 5935 return; /* out of memory */ 5936 } 5937 5938 /* The origin is the upper-left corner of the pixmap. Therefore 5939 * these offset may become negative if the pixmap is smaller than 5940 * the 2x1 cells reserved for the sign icon. */ 5941 xoffset = (width - SIGN_WIDTH) / 2; 5942 yoffset = (height - SIGN_HEIGHT) / 2; 5943 5944 gdk_gc_set_foreground(gui.text_gc, gui.bgcolor); 5945 5946 gdk_draw_rectangle(gui.drawarea->window, 5947 gui.text_gc, 5948 TRUE, 5949 FILL_X(col), 5950 FILL_Y(row), 5951 SIGN_WIDTH, 5952 SIGN_HEIGHT); 5953 5954 gdk_pixbuf_render_to_drawable_alpha(sign, 5955 gui.drawarea->window, 5956 MAX(0, xoffset), 5957 MAX(0, yoffset), 5958 FILL_X(col) - MIN(0, xoffset), 5959 FILL_Y(row) - MIN(0, yoffset), 5960 MIN(width, SIGN_WIDTH), 5961 MIN(height, SIGN_HEIGHT), 5962 GDK_PIXBUF_ALPHA_BILEVEL, 5963 127, 5964 GDK_RGB_DITHER_NORMAL, 5965 0, 0); 5966 if (need_scale) 5967 g_object_unref(sign); 5968 } 5969} 5970 5971 void * 5972gui_mch_register_sign(char_u *signfile) 5973{ 5974 if (signfile[0] != NUL && signfile[0] != '-' && gui.in_use) 5975 { 5976 GdkPixbuf *sign; 5977 GError *error = NULL; 5978 char_u *message; 5979 5980 sign = gdk_pixbuf_new_from_file((const char *)signfile, &error); 5981 5982 if (error == NULL) 5983 return sign; 5984 5985 message = (char_u *)error->message; 5986 5987 if (message != NULL && input_conv.vc_type != CONV_NONE) 5988 message = string_convert(&input_conv, message, NULL); 5989 5990 if (message != NULL) 5991 { 5992 /* The error message is already translated and will be more 5993 * descriptive than anything we could possibly do ourselves. */ 5994 EMSG2("E255: %s", message); 5995 5996 if (input_conv.vc_type != CONV_NONE) 5997 vim_free(message); 5998 } 5999 g_error_free(error); 6000 } 6001 6002 return NULL; 6003} 6004 6005 void 6006gui_mch_destroy_sign(void *sign) 6007{ 6008 if (sign != NULL) 6009 g_object_unref(sign); 6010} 6011 6012#endif /* FEAT_SIGN_ICONS */ 6013