gtkutil.c 158 KB
Newer Older
Jan Djärv's avatar
Jan Djärv committed
1
/* Functions for creating and updating GTK widgets.
2

3
Copyright (C) 2003-2013 Free Software Foundation, Inc.
Jan Djärv's avatar
Jan Djärv committed
4 5 6

This file is part of GNU Emacs.

7
GNU Emacs is free software: you can redistribute it and/or modify
Jan Djärv's avatar
Jan Djärv committed
8
it under the terms of the GNU General Public License as published by
9 10
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jan Djärv's avatar
Jan Djärv committed
11 12 13 14 15 16 17

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
18
along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
Jan Djärv's avatar
Jan Djärv committed
19

20
#include <config.h>
Jan Djärv's avatar
Jan Djärv committed
21 22

#ifdef USE_GTK
23
#include <float.h>
24
#include <stdio.h>
25 26 27

#include <c-ctype.h>

Jan Djärv's avatar
Jan Djärv committed
28 29 30
#include "lisp.h"
#include "xterm.h"
#include "blockinput.h"
31
#include "syssignal.h"
Jan Djärv's avatar
Jan Djärv committed
32 33 34
#include "window.h"
#include "gtkutil.h"
#include "termhooks.h"
35 36 37
#include "keyboard.h"
#include "charset.h"
#include "coding.h"
38 39
#include "font.h"

Jan Djärv's avatar
Jan Djärv committed
40
#include <gdk/gdkkeysyms.h>
41
#include "xsettings.h"
Jan Djärv's avatar
Jan Djärv committed
42

43 44 45
#ifdef HAVE_XFT
#include <X11/Xft/Xft.h>
#endif
46

47 48
#ifdef HAVE_GTK3
#include <gtk/gtkx.h>
49
#include "emacsgtkfixed.h"
50 51
#endif

Jan Djärv's avatar
Jan Djärv committed
52
#define FRAME_TOTAL_PIXEL_HEIGHT(f) \
53
  (FRAME_PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
Jan Djärv's avatar
Jan Djärv committed
54

55 56 57
#define FRAME_TOTAL_PIXEL_WIDTH(f) \
  (FRAME_PIXEL_WIDTH (f) + FRAME_TOOLBAR_WIDTH (f))

58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
#ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW
#define gtk_widget_set_has_window(w, b) \
  (gtk_fixed_set_has_window (GTK_FIXED (w), b))
#endif
#ifndef HAVE_GTK_DIALOG_GET_ACTION_AREA
#define gtk_dialog_get_action_area(w) ((w)->action_area)
#define gtk_dialog_get_content_area(w) ((w)->vbox)
#endif
#ifndef HAVE_GTK_WIDGET_GET_SENSITIVE
#define gtk_widget_get_sensitive(w) (GTK_WIDGET_SENSITIVE (w))
#endif
#ifndef HAVE_GTK_ADJUSTMENT_GET_PAGE_SIZE
#define gtk_adjustment_set_page_size(w, s) ((w)->page_size = (s))
#define gtk_adjustment_set_page_increment(w, s) ((w)->page_increment = (s))
#define gtk_adjustment_get_step_increment(w) ((w)->step_increment)
#define gtk_adjustment_set_step_increment(w, s) ((w)->step_increment = (s))
#endif
75
#if GTK_CHECK_VERSION (2, 12, 0)
76 77 78 79 80
#define remove_submenu(w) gtk_menu_item_set_submenu ((w), NULL)
#else
#define remove_submenu(w) gtk_menu_item_remove_submenu ((w))
#endif

81
#if GTK_CHECK_VERSION (3, 2, 0)
82 83 84
#define USE_NEW_GTK_FONT_CHOOSER 1
#else
#define USE_NEW_GTK_FONT_CHOOSER 0
85 86 87 88 89 90 91 92
#define gtk_font_chooser_dialog_new(x, y) \
  gtk_font_selection_dialog_new (x)
#undef GTK_FONT_CHOOSER
#define GTK_FONT_CHOOSER(x) GTK_FONT_SELECTION_DIALOG (x)
#define  gtk_font_chooser_set_font(x, y) \
  gtk_font_selection_dialog_set_font_name (x, y)
#endif

93
#ifndef HAVE_GTK3
94
#ifdef USE_GTK_TOOLTIP
95
#define gdk_window_get_screen(w) gdk_drawable_get_screen (w)
96
#endif
97
#define gdk_window_get_geometry(w, a, b, c, d) \
98
  gdk_window_get_geometry (w, a, b, c, d, 0)
99 100
#define gdk_x11_window_lookup_for_display(d, w) \
  gdk_xid_table_lookup_for_display (d, w)
101 102 103 104 105 106
#define gtk_box_new(ori, spacing)                                       \
  ((ori) == GTK_ORIENTATION_HORIZONTAL                                  \
   ? gtk_hbox_new (FALSE, (spacing)) : gtk_vbox_new (FALSE, (spacing)))
#define gtk_scrollbar_new(ori, spacing)                                 \
  ((ori) == GTK_ORIENTATION_HORIZONTAL                                  \
   ? gtk_hscrollbar_new ((spacing)) : gtk_vscrollbar_new ((spacing)))
107
#ifndef GDK_KEY_g
108 109
#define GDK_KEY_g GDK_g
#endif
110
#endif /* HAVE_GTK3 */
111

112 113
#define XG_BIN_CHILD(x) gtk_bin_get_child (GTK_BIN (x))

114
static void update_theme_scrollbar_width (void);
115

116 117 118 119 120 121 122 123 124 125
#define TB_INFO_KEY "xg_frame_tb_info"
struct xg_frame_tb_info
{
  Lisp_Object last_tool_bar;
  Lisp_Object style;
  int n_last_items;
  int hmargin, vmargin;
  GtkTextDirection dir;
};

126 127 128 129 130

/***********************************************************************
                      Display handling functions
 ***********************************************************************/

131 132
/* Keep track of the default display, or NULL if there is none.  Emacs
   may close all its displays.  */
Jan Djärv's avatar
Jan Djärv committed
133 134 135

static GdkDisplay *gdpy_def;

136 137 138
/* When the GTK widget W is to be created on a display for F that
   is not the default display, set the display for W.
   W can be a GtkMenu or a GtkWindow widget.  */
139

140
static void
Dmitry Antipov's avatar
Dmitry Antipov committed
141
xg_set_screen (GtkWidget *w, struct frame *f)
142
{
143
  if (FRAME_X_DISPLAY (f) != DEFAULT_GDK_DISPLAY ())
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
    {
      GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
      GdkScreen *gscreen = gdk_display_get_default_screen (gdpy);

      if (GTK_IS_MENU (w))
        gtk_menu_set_screen (GTK_MENU (w), gscreen);
      else
        gtk_window_set_screen (GTK_WINDOW (w), gscreen);
    }
}


/* Open a display named by DISPLAY_NAME.  The display is returned in *DPY.
   *DPY is set to NULL if the display can't be opened.

   Returns non-zero if display could be opened, zero if display could not
   be opened, and less than zero if the GTK version doesn't support
Paul Eggert's avatar
Paul Eggert committed
161
   multiple displays.  */
162

163
void
164
xg_display_open (char *display_name, Display **dpy)
165 166 167 168
{
  GdkDisplay *gdpy;

  gdpy = gdk_display_open (display_name);
169 170 171 172 173 174
  if (!gdpy_def && gdpy)
    {
      gdpy_def = gdpy;
      gdk_display_manager_set_default_display (gdk_display_manager_get (),
					       gdpy);
    }
175

176
  *dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
177 178 179
}


180 181
/* Close display DPY.  */

182 183 184 185 186
void
xg_display_close (Display *dpy)
{
  GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);

187 188 189
  /* If this is the default display, try to change it before closing.
     If there is no other display to use, gdpy_def is set to NULL, and
     the next call to xg_display_open resets the default display.  */
190 191 192
  if (gdk_display_get_default () == gdpy)
    {
      struct x_display_info *dpyinfo;
193
      GdkDisplay *gdpy_new = NULL;
194 195 196 197 198

      /* Find another display.  */
      for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
        if (dpyinfo->display != dpy)
          {
199 200 201
	    gdpy_new = gdk_x11_lookup_xdisplay (dpyinfo->display);
	    gdk_display_manager_set_default_display (gdk_display_manager_get (),
						     gdpy_new);
202 203
            break;
          }
204
      gdpy_def = gdpy_new;
205 206
    }

207
#if GTK_CHECK_VERSION (2, 0, 0) && ! GTK_CHECK_VERSION (2, 10, 0)
208 209 210
  /* GTK 2.2-2.8 has a bug that makes gdk_display_close crash (bug
     http://bugzilla.gnome.org/show_bug.cgi?id=85715).  This way we
     can continue running, but there will be memory leaks.  */
211
  g_object_run_dispose (G_OBJECT (gdpy));
212
#else
213
  /* This seems to be fixed in GTK 2.10. */
214 215 216
  gdk_display_close (gdpy);
#endif
}
Jan Djärv's avatar
Jan Djärv committed
217

Jan Djärv's avatar
Jan Djärv committed
218 219 220 221 222 223 224 225 226 227 228 229

/***********************************************************************
                      Utility functions
 ***********************************************************************/
/* The next two variables and functions are taken from lwlib.  */
static widget_value *widget_value_free_list;
static int malloc_cpt;

/* Allocate a widget_value structure, either by taking one from the
   widget_value_free_list or by malloc:ing a new one.

   Return a pointer to the allocated structure.  */
230

Jan Djärv's avatar
Jan Djärv committed
231
widget_value *
232
malloc_widget_value (void)
Jan Djärv's avatar
Jan Djärv committed
233 234 235 236 237 238 239 240 241 242
{
  widget_value *wv;
  if (widget_value_free_list)
    {
      wv = widget_value_free_list;
      widget_value_free_list = wv->free_list;
      wv->free_list = 0;
    }
  else
    {
243
      wv = xmalloc (sizeof *wv);
Jan Djärv's avatar
Jan Djärv committed
244 245 246 247 248 249 250 251
      malloc_cpt++;
    }
  memset (wv, 0, sizeof (widget_value));
  return wv;
}

/* This is analogous to free.  It frees only what was allocated
   by malloc_widget_value, and no substructures.  */
252

Jan Djärv's avatar
Jan Djärv committed
253
void
254
free_widget_value (widget_value *wv)
Jan Djärv's avatar
Jan Djärv committed
255 256
{
  if (wv->free_list)
257
    emacs_abort ();
Jan Djärv's avatar
Jan Djärv committed
258 259 260 261 262

  if (malloc_cpt > 25)
    {
      /* When the number of already allocated cells is too big,
	 We free it.  */
263
      xfree (wv);
Jan Djärv's avatar
Jan Djärv committed
264 265 266 267 268 269 270 271 272 273
      malloc_cpt--;
    }
  else
    {
      wv->free_list = widget_value_free_list;
      widget_value_free_list = wv;
    }
}


274 275
/* Create and return the cursor to be used for popup menus and
   scroll bars on display DPY.  */
276

277
GdkCursor *
278
xg_create_default_cursor (Display *dpy)
279 280 281 282 283
{
  GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
  return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR);
}

284
static GdkPixbuf *
Dmitry Antipov's avatar
Dmitry Antipov committed
285
xg_get_pixbuf_from_pixmap (struct frame *f, Pixmap pix)
286 287 288 289 290 291 292 293 294 295 296 297 298 299
{
  int iunused;
  GdkPixbuf *tmp_buf;
  Window wunused;
  unsigned int width, height, uunused;
  XImage *xim;

  XGetGeometry (FRAME_X_DISPLAY (f), pix, &wunused, &iunused, &iunused,
                &width, &height, &uunused, &uunused);

  xim = XGetImage (FRAME_X_DISPLAY (f), pix, 0, 0, width, height,
                   ~0, XYPixmap);
  if (!xim) return 0;

300
  tmp_buf = gdk_pixbuf_new_from_data ((guchar *) xim->data,
301 302 303
                                      GDK_COLORSPACE_RGB,
                                      FALSE,
                                      xim->bitmap_unit,
Paul Eggert's avatar
Paul Eggert committed
304 305
                                      width,
                                      height,
306 307 308 309 310 311 312
                                      xim->bytes_per_line,
                                      NULL,
                                      NULL);
  XDestroyImage (xim);
  return tmp_buf;
}

313 314
/* Apply GMASK to GPIX and return a GdkPixbuf with an alpha channel.  */

315
static GdkPixbuf *
Dmitry Antipov's avatar
Dmitry Antipov committed
316
xg_get_pixbuf_from_pix_and_mask (struct frame *f,
317 318
                                 Pixmap pix,
                                 Pixmap mask)
319
{
320
  int width, height;
321 322
  GdkPixbuf *icon_buf, *tmp_buf;

323
  tmp_buf = xg_get_pixbuf_from_pixmap (f, pix);
324 325 326
  icon_buf = gdk_pixbuf_add_alpha (tmp_buf, FALSE, 0, 0, 0);
  g_object_unref (G_OBJECT (tmp_buf));

327 328
  width = gdk_pixbuf_get_width (icon_buf);
  height = gdk_pixbuf_get_height (icon_buf);
329

330
  if (mask)
331
    {
332
      GdkPixbuf *mask_buf = xg_get_pixbuf_from_pixmap (f, mask);
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
      guchar *pixels = gdk_pixbuf_get_pixels (icon_buf);
      guchar *mask_pixels = gdk_pixbuf_get_pixels (mask_buf);
      int rowstride = gdk_pixbuf_get_rowstride (icon_buf);
      int mask_rowstride = gdk_pixbuf_get_rowstride (mask_buf);
      int y;

      for (y = 0; y < height; ++y)
        {
          guchar *iconptr, *maskptr;
          int x;

          iconptr = pixels + y * rowstride;
          maskptr = mask_pixels + y * mask_rowstride;

          for (x = 0; x < width; ++x)
            {
              /* In a bitmap, RGB is either 255/255/255 or 0/0/0.  Checking
                 just R is sufficient.  */
              if (maskptr[0] == 0)
                iconptr[3] = 0; /* 0, 1, 2 is R, G, B.  3 is alpha.  */

              iconptr += rowstride/width;
              maskptr += mask_rowstride/width;
            }
        }

      g_object_unref (G_OBJECT (mask_buf));
    }

  return icon_buf;
}

365
static Lisp_Object
366
file_for_image (Lisp_Object image)
367 368 369 370 371 372 373 374 375 376 377 378 379
{
  Lisp_Object specified_file = Qnil;
  Lisp_Object tail;

  for (tail = XCDR (image);
       NILP (specified_file) && CONSP (tail) && CONSP (XCDR (tail));
       tail = XCDR (XCDR (tail)))
    if (EQ (XCAR (tail), QCfile))
      specified_file = XCAR (XCDR (tail));

  return specified_file;
}

380 381
/* For the image defined in IMG, make and return a GtkImage.  For displays with
   8 planes or less we must make a GdkPixbuf and apply the mask manually.
Paul Eggert's avatar
Paul Eggert committed
382
   Otherwise the highlighting and dimming the tool bar code in GTK does
383 384 385 386 387 388 389
   will look bad.  For display with more than 8 planes we just use the
   pixmap and mask directly.  For monochrome displays, GTK doesn't seem
   able to use external pixmaps, it looks bad whatever we do.
   The image is defined on the display where frame F is.
   WIDGET is used to find the GdkColormap to use for the GdkPixbuf.
   If OLD_WIDGET is NULL, a new widget is constructed and returned.
   If OLD_WIDGET is not NULL, that widget is modified.  */
390

391
static GtkWidget *
Dmitry Antipov's avatar
Dmitry Antipov committed
392
xg_get_image_for_pixmap (struct frame *f,
393 394 395
                         struct image *img,
                         GtkWidget *widget,
                         GtkImage *old_widget)
396
{
397
  GdkPixbuf *icon_buf;
398

399
  /* If we have a file, let GTK do all the image handling.
400
     This seems to be the only way to make insensitive and activated icons
401
     look good in all cases.  */
402
  Lisp_Object specified_file = file_for_image (img->spec);
403
  Lisp_Object file;
404

405 406 407
  /* We already loaded the image once before calling this
     function, so this only fails if the image file has been removed.
     In that case, use the pixmap already loaded.  */
408

409 410 411
  if (STRINGP (specified_file)
      && STRINGP (file = x_find_image_file (specified_file)))
    {
412 413 414 415 416 417
      if (! old_widget)
        old_widget = GTK_IMAGE (gtk_image_new_from_file (SSDATA (file)));
      else
        gtk_image_set_from_file (old_widget, SSDATA (file));

      return GTK_WIDGET (old_widget);
418
    }
419

420 421 422 423
  /* No file, do the image handling ourselves.  This will look very bad
     on a monochrome display, and sometimes bad on all displays with
     certain themes.  */

424 425 426 427 428 429 430 431 432
  /* This is a workaround to make icons look good on pseudo color
     displays.  Apparently GTK expects the images to have an alpha
     channel.  If they don't, insensitive and activated icons will
     look bad.  This workaround does not work on monochrome displays,
     and is strictly not needed on true color/static color displays (i.e.
     16 bits and higher).  But we do it anyway so we get a pixbuf that is
     not associated with the img->pixmap.  The img->pixmap may be removed
     by clearing the image cache and then the tool bar redraw fails, since
     Gtk+ assumes the pixmap is always there.  */
433
  icon_buf = xg_get_pixbuf_from_pix_and_mask (f, img->pixmap, img->mask);
434

435
  if (icon_buf)
436 437 438 439 440
    {
      if (! old_widget)
        old_widget = GTK_IMAGE (gtk_image_new_from_pixbuf (icon_buf));
      else
        gtk_image_set_from_pixbuf (old_widget, icon_buf);
441

442 443
      g_object_unref (G_OBJECT (icon_buf));
    }
444

445
  return GTK_WIDGET (old_widget);
446 447 448 449 450 451
}


/* Set CURSOR on W and all widgets W contain.  We must do like this
   for scroll bars and menu because they create widgets internally,
   and it is those widgets that are visible.  */
452

453
static void
454
xg_set_cursor (GtkWidget *w, GdkCursor *cursor)
Jan Djärv's avatar
Jan Djärv committed
455
{
Juanma Barranquero's avatar
Juanma Barranquero committed
456
  GdkWindow *window = gtk_widget_get_window (w);
457
  GList *children = gdk_window_peek_children (window);
Jan Djärv's avatar
Jan Djärv committed
458

459
  gdk_window_set_cursor (window, cursor);
Jan Djärv's avatar
Jan Djärv committed
460 461 462 463 464 465 466

  /* The scroll bar widget has more than one GDK window (had to look at
     the source to figure this out), and there is no way to set cursor
     on widgets in GTK.  So we must set the cursor for all GDK windows.
     Ditto for menus.  */

  for ( ; children; children = g_list_next (children))
467
    gdk_window_set_cursor (GDK_WINDOW (children->data), cursor);
Jan Djärv's avatar
Jan Djärv committed
468 469 470
}

/* Insert NODE into linked LIST.  */
471

Jan Djärv's avatar
Jan Djärv committed
472 473 474 475
static void
xg_list_insert (xg_list_node *list, xg_list_node *node)
{
  xg_list_node *list_start = list->next;
476

Jan Djärv's avatar
Jan Djärv committed
477 478 479 480 481 482 483
  if (list_start) list_start->prev = node;
  node->next = list_start;
  node->prev = 0;
  list->next = node;
}

/* Remove NODE from linked LIST.  */
484

Jan Djärv's avatar
Jan Djärv committed
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
static void
xg_list_remove (xg_list_node *list, xg_list_node *node)
{
  xg_list_node *list_start = list->next;
  if (node == list_start)
    {
      list->next = node->next;
      if (list->next) list->next->prev = 0;
    }
  else
    {
      node->prev->next = node->next;
      if (node->next) node->next->prev = node->prev;
    }
}

/* Allocate and return a utf8 version of STR.  If STR is already
502 503
   utf8 or NULL, just return a copy of STR.
   A new string is allocated and the caller must free the result
Jan Djärv's avatar
Jan Djärv committed
504
   with g_free.  */
505

Jan Djärv's avatar
Jan Djärv committed
506
static char *
507
get_utf8_string (const char *str)
Jan Djärv's avatar
Jan Djärv committed
508
{
509
  char *utf8_str;
510

511 512
  if (!str) return NULL;

Jan Djärv's avatar
Jan Djärv committed
513
  /* If not UTF-8, try current locale.  */
514
  if (!g_utf8_validate (str, -1, NULL))
Jan Djärv's avatar
Jan Djärv committed
515
    utf8_str = g_locale_to_utf8 (str, -1, 0, 0, 0);
516 517
  else
    return g_strdup (str);
Jan Djärv's avatar
Jan Djärv committed
518

Juanma Barranquero's avatar
Juanma Barranquero committed
519
  if (!utf8_str)
520 521
    {
      /* Probably some control characters in str.  Escape them. */
522 523
      ptrdiff_t len;
      ptrdiff_t nr_bad = 0;
524 525 526 527
      gsize bytes_read;
      gsize bytes_written;
      unsigned char *p = (unsigned char *)str;
      char *cp, *up;
528
      GError *err = NULL;
529

530
      while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read,
531 532
                                       &bytes_written, &err))
             && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
533 534 535
        {
          ++nr_bad;
          p += bytes_written+1;
536 537
          g_error_free (err);
          err = NULL;
538 539
        }

540
      if (err)
541
        {
542 543
          g_error_free (err);
          err = NULL;
544 545 546
        }
      if (cp) g_free (cp);

547
      len = strlen (str);
548
      if ((min (PTRDIFF_MAX, SIZE_MAX) - len - 1) / 4 < nr_bad)
549 550
	memory_full (SIZE_MAX);
      up = utf8_str = xmalloc (len + nr_bad * 4 + 1);
551
      p = (unsigned char *)str;
552

553
      while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read,
554 555
                                       &bytes_written, &err))
             && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
556
        {
557
          memcpy (up, p, bytes_written);
558 559 560
          sprintf (up + bytes_written, "\\%03o", p[bytes_written]);
          up += bytes_written+4;
          p += bytes_written+1;
561 562
          g_error_free (err);
          err = NULL;
563 564
        }

Juanma Barranquero's avatar
Juanma Barranquero committed
565
      if (cp)
566 567 568 569
        {
          strcat (utf8_str, cp);
          g_free (cp);
        }
570
      if (err)
571
        {
572 573
          g_error_free (err);
          err = NULL;
574 575
        }
    }
Jan Djärv's avatar
Jan Djärv committed
576 577 578
  return utf8_str;
}

579 580
/* Check for special colors used in face spec for region face.
   The colors are fetched from the Gtk+ theme.
581
   Return true if color was found, false if not.  */
582

583
bool
584 585 586 587
xg_check_special_colors (struct frame *f,
                         const char *color_name,
                         XColor *color)
{
588 589 590
  bool success_p = 0;
  bool get_bg = strcmp ("gtk_selection_bg_color", color_name) == 0;
  bool get_fg = !get_bg && strcmp ("gtk_selection_fg_color", color_name) == 0;
591 592 593 594

  if (! FRAME_GTK_WIDGET (f) || ! (get_bg || get_fg))
    return success_p;

595
  block_input ();
596 597 598 599 600
  {
#ifdef HAVE_GTK3
    GtkStyleContext *gsty
      = gtk_widget_get_style_context (FRAME_GTK_OUTER_WIDGET (f));
    GdkRGBA col;
601
    char buf[sizeof "rgbi://" + 3 * (DBL_MAX_10_EXP + sizeof "-1.000000" - 1)];
602 603 604 605 606 607 608
    int state = GTK_STATE_FLAG_SELECTED|GTK_STATE_FLAG_FOCUSED;
    if (get_fg)
      gtk_style_context_get_color (gsty, state, &col);
    else
      gtk_style_context_get_background_color (gsty, state, &col);

    sprintf (buf, "rgbi:%lf/%lf/%lf", col.red, col.green, col.blue);
609 610 611
    success_p = (XParseColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f),
			      buf, color)
		 != 0);
612 613 614 615 616 617 618 619 620 621 622 623
#else
    GtkStyle *gsty = gtk_widget_get_style (FRAME_GTK_WIDGET (f));
    GdkColor *grgb = get_bg
      ? &gsty->bg[GTK_STATE_SELECTED]
      : &gsty->fg[GTK_STATE_SELECTED];

    color->red = grgb->red;
    color->green = grgb->green;
    color->blue = grgb->blue;
    color->pixel = grgb->pixel;
    success_p = 1;
#endif
624

625
  }
626
  unblock_input ();
627 628 629
  return success_p;
}

Jan Djärv's avatar
Jan Djärv committed
630

631 632 633 634 635 636 637 638

/***********************************************************************
                              Tooltips
 ***********************************************************************/
/* Gtk+ calls this callback when the parent of our tooltip dummy changes.
   We use that to pop down the tooltip.  This happens if Gtk+ for some
   reason wants to change or hide the tooltip.  */

639 640
#ifdef USE_GTK_TOOLTIP

641 642 643 644 645
static void
hierarchy_ch_cb (GtkWidget *widget,
                 GtkWidget *previous_toplevel,
                 gpointer   user_data)
{
Paul Eggert's avatar
Paul Eggert committed
646
  struct frame *f = user_data;
647 648
  struct x_output *x = f->output_data.x;
  GtkWidget *top = gtk_widget_get_toplevel (x->ttip_lbl);
Chong Yidong's avatar
Chong Yidong committed
649

650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
  if (! top || ! GTK_IS_WINDOW (top))
      gtk_widget_hide (previous_toplevel);
}

/* Callback called when Gtk+ thinks a tooltip should be displayed.
   We use it to get the tooltip window and the tooltip widget so
   we can manipulate the ourselves.

   Return FALSE ensures that the tooltip is not shown.  */

static gboolean
qttip_cb (GtkWidget  *widget,
          gint        xpos,
          gint        ypos,
          gboolean    keyboard_mode,
          GtkTooltip *tooltip,
          gpointer    user_data)
{
Paul Eggert's avatar
Paul Eggert committed
668
  struct frame *f = user_data;
669
  struct x_output *x = f->output_data.x;
Chong Yidong's avatar
Chong Yidong committed
670
  if (x->ttip_widget == NULL)
671
    {
672 673 674
      GtkWidget *p;
      GList *list, *iter;

675 676 677 678 679 680 681
      g_object_set (G_OBJECT (widget), "has-tooltip", FALSE, NULL);
      x->ttip_widget = tooltip;
      g_object_ref (G_OBJECT (tooltip));
      x->ttip_lbl = gtk_label_new ("");
      g_object_ref (G_OBJECT (x->ttip_lbl));
      gtk_tooltip_set_custom (tooltip, x->ttip_lbl);
      x->ttip_window = GTK_WINDOW (gtk_widget_get_toplevel (x->ttip_lbl));
682 683 684 685 686 687 688 689 690 691 692 693

      /* Change stupid Gtk+ default line wrapping.  */
      p = gtk_widget_get_parent (x->ttip_lbl);
      list = gtk_container_get_children (GTK_CONTAINER (p));
      for (iter = list; iter; iter = g_list_next (iter))
        {
          GtkWidget *w = GTK_WIDGET (iter->data);
          if (GTK_IS_LABEL (w))
            gtk_label_set_line_wrap (GTK_LABEL (w), FALSE);
        }
      g_list_free (list);

694 695
      /* ATK needs an empty title for some reason.  */
      gtk_window_set_title (x->ttip_window, "");
696 697 698 699 700 701 702 703 704 705
      /* Realize so we can safely get screen later on.  */
      gtk_widget_realize (GTK_WIDGET (x->ttip_window));
      gtk_widget_realize (x->ttip_lbl);

      g_signal_connect (x->ttip_lbl, "hierarchy-changed",
                        G_CALLBACK (hierarchy_ch_cb), f);
    }
  return FALSE;
}

706 707
#endif /* USE_GTK_TOOLTIP */

708
/* Prepare a tooltip to be shown, i.e. calculate WIDTH and HEIGHT.
709
   Return true if a system tooltip is available.  */
710

711
bool
Dmitry Antipov's avatar
Dmitry Antipov committed
712
xg_prepare_tooltip (struct frame *f,
Jan Djärv's avatar
Jan Djärv committed
713 714
                    Lisp_Object string,
                    int *width,
715 716
                    int *height)
{
717 718 719
#ifndef USE_GTK_TOOLTIP
  return 0;
#else
720 721 722 723 724 725 726 727 728 729 730
  struct x_output *x = f->output_data.x;
  GtkWidget *widget;
  GdkWindow *gwin;
  GdkScreen *screen;
  GtkSettings *settings;
  gboolean tt_enabled = TRUE;
  GtkRequisition req;
  Lisp_Object encoded_string;

  if (!x->ttip_lbl) return 0;

731
  block_input ();
732 733 734
  encoded_string = ENCODE_UTF_8 (string);
  widget = GTK_WIDGET (x->ttip_lbl);
  gwin = gtk_widget_get_window (GTK_WIDGET (x->ttip_window));
735
  screen = gdk_window_get_screen (gwin);
736 737
  settings = gtk_settings_get_for_screen (screen);
  g_object_get (settings, "gtk-enable-tooltips", &tt_enabled, NULL);
Chong Yidong's avatar
Chong Yidong committed
738
  if (tt_enabled)
739 740 741 742 743 744
    {
      g_object_set (settings, "gtk-enable-tooltips", FALSE, NULL);
      /* Record that we disabled it so it can be enabled again.  */
      g_object_set_data (G_OBJECT (x->ttip_window), "restore-tt",
                         (gpointer)f);
    }
Chong Yidong's avatar
Chong Yidong committed
745

746 747 748 749 750
  /* Prevent Gtk+ from hiding tooltip on mouse move and such.  */
  g_object_set_data (G_OBJECT
                     (gtk_widget_get_display (GTK_WIDGET (x->ttip_window))),
                     "gdk-display-current-tooltip", NULL);

Jan Djärv's avatar
Jan Djärv committed
751
  /* Put our dummy widget in so we can get callbacks for unrealize and
752 753
     hierarchy-changed.  */
  gtk_tooltip_set_custom (x->ttip_widget, widget);
754
  gtk_tooltip_set_text (x->ttip_widget, SSDATA (encoded_string));
755
  gtk_widget_get_preferred_size (GTK_WIDGET (x->ttip_window), NULL, &req);
756 757
  if (width) *width = req.width;
  if (height) *height = req.height;
Chong Yidong's avatar
Chong Yidong committed
758

759
  unblock_input ();
760 761

  return 1;
762
#endif /* USE_GTK_TOOLTIP */
763 764 765 766 767 768
}

/* Show the tooltip at ROOT_X and ROOT_Y.
   xg_prepare_tooltip must have been called before this function.  */

void
Dmitry Antipov's avatar
Dmitry Antipov committed
769
xg_show_tooltip (struct frame *f, int root_x, int root_y)
770
{
771
#ifdef USE_GTK_TOOLTIP
772 773 774
  struct x_output *x = f->output_data.x;
  if (x->ttip_window)
    {
775
      block_input ();
776 777
      gtk_window_move (x->ttip_window, root_x, root_y);
      gtk_widget_show_all (GTK_WIDGET (x->ttip_window));
778
      unblock_input ();
779
    }
780
#endif
781 782 783
}

/* Hide tooltip if shown.  Do nothing if not shown.
784
   Return true if tip was hidden, false if not (i.e. not using
785 786
   system tooltips).  */

787
bool
Dmitry Antipov's avatar
Dmitry Antipov committed
788
xg_hide_tooltip (struct frame *f)
789
{
790
  bool ret = 0;
791
#ifdef USE_GTK_TOOLTIP
792 793 794
  if (f->output_data.x->ttip_window)
    {
      GtkWindow *win = f->output_data.x->ttip_window;
795
      block_input ();
796 797 798 799 800
      gtk_widget_hide (GTK_WIDGET (win));

      if (g_object_get_data (G_OBJECT (win), "restore-tt"))
        {
          GdkWindow *gwin = gtk_widget_get_window (GTK_WIDGET (win));
801
          GdkScreen *screen = gdk_window_get_screen (gwin);
802 803 804
          GtkSettings *settings = gtk_settings_get_for_screen (screen);
          g_object_set (settings, "gtk-enable-tooltips", TRUE, NULL);
        }
805
      unblock_input ();
806 807 808

      ret = 1;
    }
809
#endif
810 811 812
  return ret;
}

Jan Djärv's avatar
Jan Djärv committed
813 814 815 816 817

/***********************************************************************
    General functions for creating widgets, resizing, events, e.t.c.
 ***********************************************************************/

818 819 820 821 822 823 824 825
static void
my_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
		const gchar *msg, gpointer user_data)
{
  if (!strstr (msg, "visible children"))
    fprintf (stderr, "XX %s-WARNING **: %s\n", log_domain, msg);
}

Jan Djärv's avatar
Jan Djärv committed
826 827 828 829
/* Make a geometry string and pass that to GTK.  It seems this is the
   only way to get geometry position right if the user explicitly
   asked for a position when starting Emacs.
   F is the frame we shall set geometry for.  */
830

Jan Djärv's avatar
Jan Djärv committed
831
static void
Dmitry Antipov's avatar
Dmitry Antipov committed
832
xg_set_geometry (struct frame *f)
Jan Djärv's avatar
Jan Djärv committed
833
{
834
  if (f->size_hint_flags & (USPosition | PPosition))
835 836 837 838 839
    {
      int left = f->left_pos;
      int xneg = f->size_hint_flags & XNegative;
      int top = f->top_pos;
      int yneg = f->size_hint_flags & YNegative;
840
      char geom_str[sizeof "=x--" + 4 * INT_STRLEN_BOUND (int)];
841
      guint id;
842 843 844 845 846 847

      if (xneg)
        left = -left;
      if (yneg)
        top = -top;

848 849 850
      sprintf (geom_str, "=%dx%d%c%d%c%d",
               FRAME_PIXEL_WIDTH (f),
               FRAME_PIXEL_HEIGHT (f),
851 852 853
               (xneg ? '-' : '+'), left,
               (yneg ? '-' : '+'), top);

854 855 856 857
      /* Silence warning about visible children.  */
      id = g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL
                              | G_LOG_FLAG_RECURSION, my_log_handler, NULL);

858 859 860
      if (!gtk_window_parse_geometry (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
                                      geom_str))
        fprintf (stderr, "Failed to parse: '%s'\n", geom_str);
861 862

      g_log_remove_handler ("Gtk", id);
863
    }
Jan Djärv's avatar
Jan Djärv committed
864 865
}

866 867 868 869
/* Clear under internal border if any.  As we use a mix of Gtk+ and X calls
   and use a GtkFixed widget, this doesn't happen automatically.  */

static void
Dmitry Antipov's avatar
Dmitry Antipov committed
870
xg_clear_under_internal_border (struct frame *f)
871 872 873 874
{
  if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0)
    {
      GtkWidget *wfixed = f->output_data.x->edit_widget;
875

876 877
      gtk_widget_queue_draw (wfixed);
      gdk_window_process_all_updates ();
878 879 880 881 882 883 884 885 886 887 888 889 890 891

      x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), 0, 0,
		    FRAME_PIXEL_WIDTH (f), FRAME_INTERNAL_BORDER_WIDTH (f));

      x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), 0, 0,
		    FRAME_INTERNAL_BORDER_WIDTH (f), FRAME_PIXEL_HEIGHT (f));

      x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), 0,
		    FRAME_PIXEL_HEIGHT (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
		    FRAME_PIXEL_WIDTH (f), FRAME_INTERNAL_BORDER_WIDTH (f));

      x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
		    FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
		    0, FRAME_INTERNAL_BORDER_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
892 893 894
    }
}

895 896 897
/* Function to handle resize of our frame.  As we have a Gtk+ tool bar
   and a Gtk+ menu bar, we get resize events for the edit part of the
   frame only.  We let Gtk+ deal with the Gtk+ parts.
Jan Djärv's avatar
Jan Djärv committed
898 899
   F is the frame to resize.
   PIXELWIDTH, PIXELHEIGHT is the new size in pixels.  */
900

Jan Djärv's avatar
Jan Djärv committed
901
void
Dmitry Antipov's avatar
Dmitry Antipov committed
902
xg_frame_resized (struct frame *f, int pixelwidth, int pixelheight)
Jan Djärv's avatar
Jan Djärv committed
903
{
904 905 906 907
  int rows, columns;

  if (pixelwidth == -1 && pixelheight == -1)
    {
908 909 910
      if (FRAME_GTK_WIDGET (f) && gtk_widget_get_mapped (FRAME_GTK_WIDGET (f)))
          gdk_window_get_geometry (gtk_widget_get_window (FRAME_GTK_WIDGET (f)),
                                   0, 0,
911
                                   &pixelwidth, &pixelheight);
912 913
      else return;
    }
Chong Yidong's avatar
Chong Yidong committed
914

915 916 917 918 919 920 921 922

  rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, pixelheight);
  columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth);

  if (columns != FRAME_COLS (f)
      || rows != FRAME_LINES (f)
      || pixelwidth != FRAME_PIXEL_WIDTH (f)
      || pixelheight != FRAME_PIXEL_HEIGHT (f))
Jan Djärv's avatar
Jan Djärv committed
923
    {
924 925
      FRAME_PIXEL_WIDTH (f) = pixelwidth;
      FRAME_PIXEL_HEIGHT (f) = pixelheight;
Jan Djärv's avatar
Jan Djärv committed
926

927
      xg_clear_under_internal_border (f);
928 929 930
      change_frame_size (f, rows, columns, 0, 1, 0);
      SET_FRAME_GARBAGED (f);
      cancel_mouse_face (f);
931 932
    }
}
Jan Djärv's avatar
Jan Djärv committed
933

Paul Eggert's avatar
Paul Eggert committed
934
/* Resize the outer window of frame F after changing the height.
935
   COLUMNS/ROWS is the size the edit area shall have after the resize.  */
936

Jan Djärv's avatar
Jan Djärv committed
937
void
Dmitry Antipov's avatar
Dmitry Antipov committed
938
xg_frame_set_char_size (struct frame *f, int cols, int rows)
Jan Djärv's avatar
Jan Djärv committed
939
{
940
  int pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows)
Jan Djärv's avatar
Jan Djärv committed
941
    + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
942
  int pixelwidth;
943

944 945 946
  if (FRAME_PIXEL_HEIGHT (f) == 0)
    return;

Jan Djärv's avatar
Jan Djärv committed
947 948 949 950 951
  /* Take into account the size of the scroll bar.  Always use the
     number of columns occupied by the scroll bar here otherwise we
     might end up with a frame width that is not a multiple of the
     frame's character width which is bad for vertically split
     windows.  */
952 953
  f->scroll_bar_actual_width
    = FRAME_SCROLL_BAR_COLS (f) * FRAME_COLUMN_WIDTH (f);
Jan Djärv's avatar
Jan Djärv committed
954

955
  compute_fringe_widths (f, 0);