]> code.delx.au - gnu-emacs/blobdiff - src/gtkutil.c
Don't overflow if computing approximate percentage
[gnu-emacs] / src / gtkutil.c
index 694278a2b4c5f5e34087748f57e6f28abd99b4f2..a4b4331c0392582fabdfd19f797814d5eabe6a47 100644 (file)
@@ -50,12 +50,6 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "emacsgtkfixed.h"
 #endif
 
-/** #define FRAME_TOTAL_PIXEL_HEIGHT(f) \ **/
-/**   (FRAME_PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f)) **/
-
-/** #define FRAME_TOTAL_PIXEL_WIDTH(f) \ **/
-/**   (FRAME_PIXEL_WIDTH (f) + FRAME_TOOLBAR_WIDTH (f)) **/
-
 #ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW
 #define gtk_widget_set_has_window(w, b) \
   (gtk_fixed_set_has_window (GTK_FIXED (w), b))
@@ -193,7 +187,9 @@ xg_display_open (char *display_name, Display **dpy)
 {
   GdkDisplay *gdpy;
 
+  unrequest_sigio ();  // See comment in x_display_ok, xterm.c.
   gdpy = gdk_display_open (display_name);
+  request_sigio ();
   if (!gdpy_def && gdpy)
     {
       gdpy_def = gdpy;
@@ -583,9 +579,9 @@ xg_check_special_colors (struct frame *f,
       gtk_style_context_get_background_color (gsty, state, &col);
 
     sprintf (buf, "rgb:%04x/%04x/%04x",
-             (int)(col.red * 65535),
-             (int)(col.green * 65535),
-             (int)(col.blue * 65535));
+             (unsigned) (col.red * 65535),
+             (unsigned) (col.green * 65535),
+             (unsigned) (col.blue * 65535));
     success_p = (XParseColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f),
                              buf, color)
                 != 0);
@@ -851,27 +847,43 @@ xg_clear_under_internal_border (struct frame *f)
 {
   if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0)
     {
+#ifndef USE_CAIRO
       GtkWidget *wfixed = f->output_data.x->edit_widget;
 
       gtk_widget_queue_draw (wfixed);
       gdk_window_process_all_updates ();
-
-      x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), 0, 0,
+#endif
+      x_clear_area (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,
+      x_clear_area (f, 0, 0,
                    FRAME_INTERNAL_BORDER_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
 
-      x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), 0,
+      x_clear_area (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),
+      x_clear_area (f,
                    FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
                    0, FRAME_INTERNAL_BORDER_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
     }
 }
 
+static int
+xg_get_gdk_scale (void)
+{
+  const char *sscale = getenv ("GDK_SCALE");
+
+  if (sscale)
+    {
+      long scale = atol (sscale);
+      if (0 < scale)
+       return min (scale, INT_MAX);
+    }
+
+  return 1;
+}
+
 /* 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.
@@ -886,24 +898,23 @@ xg_frame_resized (struct frame *f, int pixelwidth, int pixelheight)
   if (pixelwidth == -1 && pixelheight == -1)
     {
       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,
-                                   &pixelwidth, &pixelheight);
-      else return;
+       gdk_window_get_geometry (gtk_widget_get_window (FRAME_GTK_WIDGET (f)),
+                                0, 0, &pixelwidth, &pixelheight);
+      else
+       return;
     }
 
-
   width = FRAME_PIXEL_TO_TEXT_WIDTH (f, pixelwidth);
   height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixelheight);
 
+  frame_size_history_add
+    (f, Qxg_frame_resized, width, height, Qnil);
+
   if (width != FRAME_TEXT_WIDTH (f)
       || height != FRAME_TEXT_HEIGHT (f)
       || pixelwidth != FRAME_PIXEL_WIDTH (f)
       || pixelheight != FRAME_PIXEL_HEIGHT (f))
     {
-      FRAME_PIXEL_WIDTH (f) = pixelwidth;
-      FRAME_PIXEL_HEIGHT (f) = pixelheight;
-
       xg_clear_under_internal_border (f);
       change_frame_size (f, width, height, 0, 1, 0, 1);
       SET_FRAME_GARBAGED (f);
@@ -921,24 +932,76 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
 {
   int pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
   int pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
+  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
+  gint gwidth, gheight;
+  int totalheight
+    = pixelheight + FRAME_TOOLBAR_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f);
+  int totalwidth = pixelwidth + FRAME_TOOLBAR_WIDTH (f);
 
   if (FRAME_PIXEL_HEIGHT (f) == 0)
     return;
 
+  gtk_window_get_size (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                      &gwidth, &gheight);
+
   /* Do this before resize, as we don't know yet if we will be resized.  */
   xg_clear_under_internal_border (f);
 
-  /* Must resize our top level widget.  Font size may have changed,
-     but not rows/cols.  */
-  gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                     pixelwidth + FRAME_TOOLBAR_WIDTH (f),
-                    pixelheight + FRAME_TOOLBAR_HEIGHT (f)
-                    + FRAME_MENUBAR_HEIGHT (f));
-  x_wm_set_size_hint (f, 0, 0);
+  if (FRAME_VISIBLE_P (f))
+    {
+      int scale = xg_get_gdk_scale ();
+      totalheight /= scale;
+      totalwidth /= scale;
+    }
+
+  /* Resize the top level widget so rows and columns remain constant.
+
+     When the frame is fullheight and we only want to change the width
+     or it is fullwidth and we only want to change the height we should
+     be able to preserve the fullscreen property.  However, due to the
+     fact that we have to send a resize request anyway, the window
+     manager will abolish it.  At least the respective size should
+     remain unchanged but giving the frame back its normal size will
+     be broken ... */
+  if (EQ (fullscreen, Qfullwidth) && width == FRAME_TEXT_WIDTH (f))
+    {
+      frame_size_history_add
+       (f, Qxg_frame_set_char_size_1, width, height,
+        list2 (make_number (gheight),
+               make_number (totalheight)));
+
+      gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                        gwidth,
+                        totalheight);
+    }
+  else if (EQ (fullscreen, Qfullheight) && height == FRAME_TEXT_HEIGHT (f))
+    {
+      frame_size_history_add
+       (f, Qxg_frame_set_char_size_2, width, height,
+        list2 (make_number (gwidth),
+               make_number (totalwidth)));
+
+      gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                        totalwidth,
+                        gheight);
+    }
+  else
+    {
+      frame_size_history_add
+       (f, Qxg_frame_set_char_size_3, width, height,
+        list2 (make_number (totalwidth),
+               make_number (totalheight)));
+
+      gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                        totalwidth,
+                        totalheight);
+      fullscreen = Qnil;
+    }
 
   SET_FRAME_GARBAGED (f);
   cancel_mouse_face (f);
 
+  x_wm_set_size_hint (f, 0, 0);
   /* We can not call change_frame_size for a mapped frame,
      we can not set pixel width/height either.  The window manager may
      override our resize request, XMonad does this all the time.
@@ -952,9 +1015,17 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
       (void)gtk_events_pending ();
       gdk_flush ();
       x_wait_for_event (f, ConfigureNotify);
+
+      if (!NILP (fullscreen))
+       /* Try to restore fullscreen state.  */
+       {
+         store_frame_param (f, Qfullscreen, fullscreen);
+         x_set_fullscreen (f, fullscreen, fullscreen);
+       }
     }
   else
-    adjust_frame_size (f, -1, -1, 5, 0, Qxg_frame_set_char_size);
+    adjust_frame_size (f, width, height, 5, 0, Qxg_frame_set_char_size);
+
 }
 
 /* Handle height/width changes (i.e. add/remove/move menu/toolbar).
@@ -1075,18 +1146,6 @@ delete_cb (GtkWidget *widget,
            GdkEvent  *event,
            gpointer user_data)
 {
-#ifdef HAVE_GTK3
-  /* The event doesn't arrive in the normal event loop.  Send event
-     here.  */
-  struct frame *f = user_data;
-  struct input_event ie;
-
-  EVENT_INIT (ie);
-  ie.kind = DELETE_WINDOW_EVENT;
-  XSETFRAME (ie.frame_or_window, f);
-  kbd_buffer_store_event (&ie);
-#endif
-
   return TRUE;
 }
 
@@ -1305,6 +1364,7 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
   int min_rows = 0, min_cols = 0;
   int win_gravity = f->win_gravity;
   Lisp_Object fs_state, frame;
+  int scale = xg_get_gdk_scale ();
 
   /* Don't set size hints during initialization; that apparently leads
      to a race condition.  See the thread at
@@ -1384,6 +1444,11 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
       hint_flags |= GDK_HINT_USER_POS;
     }
 
+  size_hints.base_width /= scale;
+  size_hints.base_height /= scale;
+  size_hints.width_inc /= scale;
+  size_hints.height_inc /= scale;
+
   if (hint_flags != f->output_data.x->hint_flags
       || memcmp (&size_hints,
                 &f->output_data.x->size_hints,
@@ -2072,28 +2137,23 @@ xg_get_font (struct frame *f, const char *default_name)
 
       if (desc)
        {
-         Lisp_Object args[10];
          const char *name   = pango_font_description_get_family (desc);
          gint        size   = pango_font_description_get_size (desc);
          PangoWeight weight = pango_font_description_get_weight (desc);
          PangoStyle  style  = pango_font_description_get_style (desc);
 
-         args[0] = QCname;
-         args[1] = build_string (name);
-
-         args[2] = QCsize;
-         args[3] = make_float (pango_units_to_double (size));
-
-         args[4] = QCweight;
-         args[5] = XG_WEIGHT_TO_SYMBOL (weight);
-
-         args[6] = QCslant;
-         args[7] = XG_STYLE_TO_SYMBOL (style);
-
-         args[8] = QCtype;
-         args[9] = Qxft;
-
-         font = Ffont_spec (8, args);
+#ifdef USE_CAIRO
+#define FONT_TYPE_WANTED (Qftcr)
+#else
+#define FONT_TYPE_WANTED (Qxft)
+#endif
+         font = CALLN (Ffont_spec,
+                       QCname, build_string (name),
+                       QCsize, make_float (pango_units_to_double (size)),
+                       QCweight, XG_WEIGHT_TO_SYMBOL (weight),
+                       QCslant, XG_STYLE_TO_SYMBOL (style),
+                       QCtype,
+                        FONT_TYPE_WANTED);
 
          pango_font_description_free (desc);
          dupstring (&x_last_font_name, name);
@@ -3479,7 +3539,9 @@ update_theme_scrollbar_width (void)
   gtk_widget_destroy (wscroll);
   g_object_unref (G_OBJECT (wscroll));
   w += 2*b;
+#ifndef HAVE_GTK3
   if (w < 16) w = 16;
+#endif
   scroll_bar_width_for_theme = w;
 }
 
@@ -3508,14 +3570,14 @@ update_theme_scrollbar_height (void)
 int
 xg_get_default_scrollbar_width (void)
 {
-  return scroll_bar_width_for_theme;
+  return scroll_bar_width_for_theme * xg_get_gdk_scale ();
 }
 
 int
 xg_get_default_scrollbar_height (void)
 {
   /* Apparently there's no default height for themes.  */
-  return scroll_bar_width_for_theme;
+  return scroll_bar_width_for_theme * xg_get_gdk_scale ();
 }
 
 /* Return the scrollbar id for X Window WID on display DPY.
@@ -3714,14 +3776,18 @@ xg_update_scrollbar_pos (struct frame *f,
                          int width,
                          int height)
 {
-
   GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
-
   if (wscroll)
     {
       GtkWidget *wfixed = f->output_data.x->edit_widget;
       GtkWidget *wparent = gtk_widget_get_parent (wscroll);
       gint msl;
+      int scale = xg_get_gdk_scale ();
+
+      top /= scale;
+      left /= scale;
+      height /= scale;
+      left -= (scale - 1) * ((width / scale) >> 1);
 
       /* Clear out old position.  */
       int oldx = -1, oldy = -1, oldw, oldh;
@@ -3747,14 +3813,19 @@ xg_update_scrollbar_pos (struct frame *f,
           gtk_widget_show_all (wparent);
           gtk_widget_set_size_request (wscroll, width, height);
         }
+#ifndef USE_CAIRO
       gtk_widget_queue_draw (wfixed);
       gdk_window_process_all_updates ();
+#endif
       if (oldx != -1 && oldw > 0 && oldh > 0)
-       /* Clear under old scroll bar position.  This must be done after
-          the gtk_widget_queue_draw and gdk_window_process_all_updates
-          above.  */
-       x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
-                     oldx, oldy, oldw, oldh);
+        {
+          /* Clear under old scroll bar position.  This must be done after
+             the gtk_widget_queue_draw and gdk_window_process_all_updates
+             above.  */
+         oldw += (scale - 1) * oldw;
+         oldx -= (scale - 1) * oldw;
+          x_clear_area (f, oldx, oldy, oldw, oldh);
+        }
 
       /* GTK does not redraw until the main loop is entered again, but
          if there are no X events pending we will not enter it.  So we sync
@@ -3819,7 +3890,7 @@ xg_update_horizontal_scrollbar_pos (struct frame *f,
        /* Clear under old scroll bar position.  This must be done after
           the gtk_widget_queue_draw and gdk_window_process_all_updates
           above.  */
-       x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+       x_clear_area (f,
                      oldx, oldy, oldw, oldh);
 
       /* GTK does not redraw until the main loop is entered again, but
@@ -3996,6 +4067,108 @@ xg_event_is_for_scrollbar (struct frame *f, const XEvent *event)
   return retval;
 }
 
+\f
+/***********************************************************************
+                              Printing
+ ***********************************************************************/
+#ifdef USE_CAIRO
+static GtkPrintSettings *print_settings = NULL;
+static GtkPageSetup *page_setup = NULL;
+
+void
+xg_page_setup_dialog (void)
+{
+  GtkPageSetup *new_page_setup = NULL;
+
+  if (print_settings == NULL)
+    print_settings = gtk_print_settings_new ();
+  new_page_setup = gtk_print_run_page_setup_dialog (NULL, page_setup,
+                                                   print_settings);
+  if (page_setup)
+    g_object_unref (page_setup);
+  page_setup = new_page_setup;
+}
+
+Lisp_Object
+xg_get_page_setup (void)
+{
+  Lisp_Object result, orientation_symbol;
+  GtkPageOrientation orientation;
+
+  if (page_setup == NULL)
+    page_setup = gtk_page_setup_new ();
+  result = list4 (Fcons (Qleft_margin,
+                        make_float (gtk_page_setup_get_left_margin (page_setup,
+                                                                    GTK_UNIT_POINTS))),
+                 Fcons (Qright_margin,
+                        make_float (gtk_page_setup_get_right_margin (page_setup,
+                                                                     GTK_UNIT_POINTS))),
+                 Fcons (Qtop_margin,
+                        make_float (gtk_page_setup_get_top_margin (page_setup,
+                                                                   GTK_UNIT_POINTS))),
+                 Fcons (Qbottom_margin,
+                        make_float (gtk_page_setup_get_bottom_margin (page_setup,
+                                                                      GTK_UNIT_POINTS))));
+  result = Fcons (Fcons (Qheight,
+                        make_float (gtk_page_setup_get_page_height (page_setup,
+                                                                    GTK_UNIT_POINTS))),
+                 result);
+  result = Fcons (Fcons (Qwidth,
+                        make_float (gtk_page_setup_get_page_width (page_setup,
+                                                                   GTK_UNIT_POINTS))),
+                 result);
+  orientation = gtk_page_setup_get_orientation (page_setup);
+  if (orientation == GTK_PAGE_ORIENTATION_PORTRAIT)
+    orientation_symbol = Qportrait;
+  else if (orientation == GTK_PAGE_ORIENTATION_LANDSCAPE)
+    orientation_symbol = Qlandscape;
+  else if (orientation == GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT)
+    orientation_symbol = Qreverse_portrait;
+  else if (orientation == GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE)
+    orientation_symbol = Qreverse_landscape;
+  result = Fcons (Fcons (Qorientation, orientation_symbol), result);
+
+  return result;
+}
+
+static void
+draw_page (GtkPrintOperation *operation, GtkPrintContext *context,
+          gint page_nr, gpointer user_data)
+{
+  Lisp_Object frames = *((Lisp_Object *) user_data);
+  struct frame *f = XFRAME (Fnth (make_number (page_nr), frames));
+  cairo_t *cr = gtk_print_context_get_cairo_context (context);
+
+  x_cr_draw_frame (cr, f);
+}
+
+void
+xg_print_frames_dialog (Lisp_Object frames)
+{
+  GtkPrintOperation *print;
+  GtkPrintOperationResult res;
+
+  print = gtk_print_operation_new ();
+  if (print_settings != NULL)
+    gtk_print_operation_set_print_settings (print, print_settings);
+  if (page_setup != NULL)
+    gtk_print_operation_set_default_page_setup (print, page_setup);
+  gtk_print_operation_set_n_pages (print, XINT (Flength (frames)));
+  g_signal_connect (print, "draw-page", G_CALLBACK (draw_page), &frames);
+  res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
+                                 NULL, NULL);
+  if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
+    {
+      if (print_settings != NULL)
+        g_object_unref (print_settings);
+      print_settings =
+       g_object_ref (gtk_print_operation_get_print_settings (print));
+    }
+  g_object_unref (print);
+}
+
+#endif /* USE_CAIRO */
+
 
 \f
 /***********************************************************************
@@ -4225,8 +4398,12 @@ tb_size_cb (GtkWidget    *widget,
      allocated between widgets, it may get another.  So we must update
      size hints if tool bar size changes.  Seen on Fedora 18 at least.  */
   struct frame *f = user_data;
+
   if (xg_update_tool_bar_sizes (f))
-    adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
+    {
+      frame_size_history_add (f, Qtb_size_cb, 0, 0, Qnil);
+      adjust_frame_size (f, -1, -1, 5, 0, Qtool_bar_lines);
+    }
 }
 
 /* Create a tool bar for frame F.  */
@@ -4500,10 +4677,11 @@ xg_update_tool_bar_sizes (struct frame *f)
       FRAME_TOOLBAR_RIGHT_WIDTH (f) = nr;
       FRAME_TOOLBAR_TOP_HEIGHT (f) = nt;
       FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = nb;
-      return 1;
-    }
 
-  return 0;
+      return true;
+    }
+  else
+    return false;
 }
 
 static char *
@@ -4826,7 +5004,10 @@ update_frame_tool_bar (struct frame *f)
         xg_pack_tool_bar (f, FRAME_TOOL_BAR_POSITION (f));
       gtk_widget_show_all (x->toolbar_widget);
       if (xg_update_tool_bar_sizes (f))
-       adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
+       {
+         frame_size_history_add (f, Qupdate_frame_tool_bar, 0, 0, Qnil);
+         adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
+       }
     }
 
   unblock_input ();
@@ -4874,6 +5055,7 @@ free_frame_tool_bar (struct frame *f)
                              NULL);
         }
 
+      frame_size_history_add (f, Qfree_frame_tool_bar, 0, 0, Qnil);
       adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
 
       unblock_input ();
@@ -4903,8 +5085,13 @@ xg_change_toolbar_position (struct frame *f, Lisp_Object pos)
 
   xg_pack_tool_bar (f, pos);
   g_object_unref (top_widget);
+
   if (xg_update_tool_bar_sizes (f))
-    adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
+    {
+      frame_size_history_add (f, Qxg_change_toolbar_position, 0, 0, Qnil);
+      adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
+    }
+
 
   unblock_input ();
 }