(EMACS_CLASS): Remove. Use generic define.
[bpt/emacs.git] / src / gtkutil.c
index d66b852..126cfc7 100644 (file)
@@ -151,7 +151,7 @@ xg_process_timeouts (timer)
 
 /* Start the xg_timer with an interval of 0.1 seconds, if not already started.
    xg_process_timeouts is called when the timer expires.  The timer
-   stared is continuous, i.e. runs until xg_stop_timer is called.  */
+   started is continuous, i.e. runs until xg_stop_timer is called.  */
 static void
 xg_start_timer ()
 {
@@ -293,11 +293,14 @@ void
 xg_frame_cleared (f)
      FRAME_PTR f;
 {
-  GtkWidget *wfixed = f->output_data.x->edit_widget;
+  GtkWidget *w = f->output_data.x->widget;
 
-  if (wfixed)
+  if (w)
     {
-      gtk_container_set_reallocate_redraws (GTK_CONTAINER (wfixed), TRUE);
+      gtk_container_set_reallocate_redraws (GTK_CONTAINER (w), TRUE);
+      gtk_container_foreach (GTK_CONTAINER (w),
+                             (GtkCallback) gtk_widget_queue_draw,
+                             0);
       gdk_window_process_all_updates ();
     }
 }
@@ -332,8 +335,6 @@ xg_resize_widgets (f, pixelwidth, pixelheight)
 
       gtk_widget_size_allocate (x->edit_widget, &all);
 
-      xg_frame_cleared (f);
-
       change_frame_size (f, rows, columns, 0, 1, 0);
       SET_FRAME_GARBAGED (f);
       cancel_mouse_face (f);
@@ -350,7 +351,7 @@ xg_frame_set_char_size (f, cols, rows)
 {
   int pixelheight = CHAR_TO_PIXEL_HEIGHT (f, rows)
     + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
-  int pixelwidth = CHAR_TO_PIXEL_WIDTH (f, cols);
+  int pixelwidth;
 
   /* Take into account the size of the scroll bar.  Always use the
      number of columns occupied by the scroll bar here otherwise we
@@ -365,11 +366,18 @@ xg_frame_set_char_size (f, cols, rows)
 
   compute_fringe_widths (f, 0);
 
+  /* CHAR_TO_PIXEL_WIDTH uses vertical_scroll_bar_extra, so call it
+     after calculating that value.  */
+  pixelwidth = CHAR_TO_PIXEL_WIDTH (f, cols);
+
   /* 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, pixelheight);
   xg_resize_widgets (f, pixelwidth, pixelheight);
+
+  SET_FRAME_GARBAGED (f);
+  cancel_mouse_face (f);
 }
 
 /* Convert an X Window WSESC to its corresponding GtkWidget.
@@ -409,6 +417,54 @@ xg_pix_to_gcolor (w, pixel, c)
   gdk_colormap_query_color (map, pixel, c);
 }
 
+/* Turning off double buffering for our GtkFixed widget has the side
+   effect of turning it off also for its children (scroll bars).
+   But we want those to be double buffered to not flicker so handle
+   expose manually here.
+   WIDGET is the GtkFixed widget that gets exposed.
+   EVENT is the expose event.
+   USER_DATA is unused.
+
+   Return TRUE to tell GTK that this expose event has been fully handeled
+   and that GTK shall do nothing more with it.  */
+static gboolean
+xg_fixed_handle_expose(GtkWidget *widget,
+                       GdkEventExpose *event,
+                       gpointer user_data)
+{
+  GList *iter;
+
+  for (iter = GTK_FIXED (widget)->children; iter; iter = g_list_next (iter))
+    {
+      GtkFixedChild *child_data = (GtkFixedChild *) iter->data;
+      GtkWidget *child = child_data->widget;
+      GdkWindow *window = child->window;
+      GdkRegion *region = gtk_widget_region_intersect (child, event->region);
+
+      if (! gdk_region_empty (region))
+        {
+          GdkEvent child_event;
+          child_event.expose = *event;
+          child_event.expose.region = region;
+
+          /* Turn on double buffering, i.e. draw to an off screen area.  */
+          gdk_window_begin_paint_region (window, region);
+
+          /* Tell child to redraw itself.  */
+          gdk_region_get_clipbox (region, &child_event.expose.area);
+          gtk_widget_send_expose (child, &child_event);
+          gdk_window_process_updates (window, TRUE);
+
+          /* Copy off screen area to the window.  */
+          gdk_window_end_paint (window);
+        }
+
+      gdk_region_destroy (region);
+     }
+
+  return TRUE;
+}
+
 /* Create and set up the GTK widgets for frame F.
    Return 0 if creation failed, non-zero otherwise.  */
 int
@@ -475,9 +531,19 @@ xg_create_frame_widgets (f)
   if (FRAME_EXTERNAL_TOOL_BAR (f) && FRAME_TOOLBAR_HEIGHT (f) == 0)
     FRAME_TOOLBAR_HEIGHT (f) = 34;
 
-  gtk_widget_set_double_buffered (wvbox, FALSE);
+
+  /* We don't want this widget double buffered, because we draw on it
+     with regular X drawing primitives, so from a GTK/GDK point of
+     view, the widget is totally blank.  When an expose comes, this
+     will make the widget blank, and then Emacs redraws it.  This flickers
+     a lot, so we turn off double buffering.  */
   gtk_widget_set_double_buffered (wfixed, FALSE);
-  gtk_widget_set_double_buffered (wtop, FALSE);
+
+  /* Turning off double buffering above has the side effect of turning
+     it off also for its children (scroll bars).  But we want those
+     to be double buffered to not flicker so handle expose manually.  */
+  g_signal_connect (G_OBJECT (wfixed), "expose-event",
+                    G_CALLBACK (xg_fixed_handle_expose), 0);
 
   /* GTK documents says use gtk_window_set_resizable.  But then a user
      can't shrink the window from its starting size.  */
@@ -1156,6 +1222,7 @@ make_widget_for_menu_item (utf8_label, utf8_key)
 
   gtk_widget_set_name (wlbl, MENU_ITEM_NAME);
   gtk_widget_set_name (wkey, MENU_ITEM_NAME);
+  gtk_widget_set_name (wbox, MENU_ITEM_NAME);
 
   return wbox;
 }
@@ -1214,16 +1281,51 @@ make_menu_item (utf8_label, utf8_key, item, group)
   return w;
 }
 
-/* Return non-zero if NAME specifies a separator (GTK only has one
+/* Return non-zero if LABEL specifies a separator (GTK only has one
    separator type)  */
 static int
-xg_separator_p (char *name)
-{
-  if (! name) return 0;
+xg_separator_p (char *label)
+{
+  if (! label) return 0;
+  else if (strlen (label) > 3
+          && strncmp (label, "--", 2) == 0
+          && label[2] != '-')
+    {
+      static char* separator_names[] = {
+        "space",
+       "no-line",
+       "single-line",
+       "double-line",
+       "single-dashed-line",
+       "double-dashed-line",
+       "shadow-etched-in",
+       "shadow-etched-out",
+       "shadow-etched-in-dash",
+       "shadow-etched-out-dash",
+       "shadow-double-etched-in",
+       "shadow-double-etched-out",
+       "shadow-double-etched-in-dash",
+       "shadow-double-etched-out-dash",
+        0,
+      };
+
+      int i;
+
+      label += 2;
+      for (i = 0; separator_names[i]; ++i)
+       if (strcmp (label, separator_names[i]) == 0)
+          return 1;
+    }
+  else
+    {
+      /* Old-style separator, maybe.  It's a separator if it contains
+        only dashes.  */
+      while (*label == '-')
+       ++label;
+      if (*label == 0) return 1;
+    }
 
-  return strcmp (name, "--") == 0
-    || strncmp (name, "--:", 3) == 0
-    || strcmp (name, "---") == 0;
+  return 0;
 }
 
 GtkWidget *xg_did_tearoff;
@@ -1281,8 +1383,6 @@ xg_keep_popup (menu, submenu)
                     G_CALLBACK (tearoff_remove), menu);
 }
 
-int xg_debug = 0;
-
 /* Create a menu item widget, and connect the callbacks.
    ITEM decribes the menu item.
    F is the frame the created menu belongs to.
@@ -2494,7 +2594,8 @@ xg_find_top_left_in_fixed (w, wfixed, left, top)
    TOP/LEFT are the new pixel positions where the bar shall appear.
    WIDTH, HEIGHT is the size in pixels the bar shall have.  */
 void
-xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height)
+xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height,
+                         real_left, canon_width)
      FRAME_PTR f;
      int scrollbar_id;
      int top;
@@ -2504,19 +2605,17 @@ xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height)
 {
 
   GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
-  
+
   if (wscroll)
     {
       GtkWidget *wfixed = f->output_data.x->edit_widget;
-      int gheight = max (height, 1);
-      int canon_width = FRAME_SCROLL_BAR_COLS (f) * CANON_X_UNIT (f);
       int winextra = canon_width > width ? (canon_width - width) / 2 : 0;
-      int bottom = top + gheight;
+      int bottom = top + height;
 
       gint slider_width;
       int oldtop, oldleft, oldbottom;
       GtkRequisition req;
-      
+
       /* Get old values.  */
       xg_find_top_left_in_fixed (wscroll, wfixed, &oldleft, &oldtop);
       gtk_widget_size_request (wscroll, &req);
@@ -2544,61 +2643,66 @@ xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height)
          the remains of the mode line can be seen in these blank spaces.
          So we must clear them explicitly.
          GTK scroll bars should do that, but they don't.
-         Also, the scroll bar canonical width may be wider than the width
-         passed in here.  */
+         Also, the canonical width may be wider than the width for the
+         scroll bar so that there is some space (typically 1 pixel) between
+         the scroll bar and the edge of the window and between the scroll
+         bar and the fringe.  */
 
       if (oldtop != -1 && oldleft != -1)
         {
-          int gtkextra;
-          int xl, xr, wblank;
+          int gtkextral, gtkextrah;
+          int xl, xr, wbl, wbr;
           int bottomdiff, topdiff;
 
           gtk_widget_style_get (wscroll, "slider_width", &slider_width, NULL);
-          gtkextra = width > slider_width ? (width - slider_width) / 2 : 0;
-        
-          xl = left - winextra;
-          wblank = gtkextra + winextra;
-          xr = left + gtkextra + slider_width;
+          gtkextral = width > slider_width ? (width - slider_width) / 2 : 0;
+          gtkextrah = gtkextral ? (width - slider_width - gtkextral) : 0;
+
+          xl = real_left;
+          wbl = gtkextral + winextra;
+          wbr = gtkextrah + winextra;
+          xr = left + gtkextral + slider_width;
           bottomdiff = abs (oldbottom - bottom);
           topdiff = abs (oldtop - top);
 
+          if (oldleft != left)
+            {
+              gdk_window_clear_area (wfixed->window, xl, top, wbl, height);
+              gdk_window_clear_area (wfixed->window, xr, top, wbr, height);
+            }
+
           if (oldtop > top)
             {
-              gdk_window_clear_area (wfixed->window, xl, top, wblank, topdiff);
-              gdk_window_clear_area (wfixed->window, xr, top, wblank, topdiff);
+              gdk_window_clear_area (wfixed->window, xl, top, wbl, topdiff);
+              gdk_window_clear_area (wfixed->window, xr, top, wbr, topdiff);
             }
           else if (oldtop < top)
             {
-              gdk_window_clear_area (wfixed->window, xl, oldtop, wblank,
-                                     topdiff);
-              gdk_window_clear_area (wfixed->window, xr, oldtop, wblank,
-                                     topdiff);
+              gdk_window_clear_area (wfixed->window, xl, oldtop, wbl, topdiff);
+              gdk_window_clear_area (wfixed->window, xr, oldtop, wbr, topdiff);
             }
 
           if (oldbottom > bottom)
             {
-              gdk_window_clear_area (wfixed->window, xl, bottom, wblank,
+              gdk_window_clear_area (wfixed->window, xl, bottom, wbl,
                                      bottomdiff);
-              gdk_window_clear_area (wfixed->window, xr, bottom, wblank,
+              gdk_window_clear_area (wfixed->window, xr, bottom, wbr,
                                      bottomdiff);
             }
           else if (oldbottom < bottom)
             {
-              gdk_window_clear_area (wfixed->window, xl, oldbottom, wblank,
+              gdk_window_clear_area (wfixed->window, xl, oldbottom, wbl,
                                      bottomdiff);
-              gdk_window_clear_area (wfixed->window, xr, oldbottom, wblank,
+              gdk_window_clear_area (wfixed->window, xr, oldbottom, wbr,
                                      bottomdiff);
             }
         }
 
       /* Move and resize to new values.  */
       gtk_fixed_move (GTK_FIXED (wfixed), wscroll, left, top);
-      gtk_widget_set_size_request (wscroll, width, gheight);
-
-      gtk_container_set_reallocate_redraws (GTK_CONTAINER (wfixed), TRUE);
-
-      /* Make GTK draw the new sizes.  We are not using a pure GTK event
-         loop so we need to do this.  */
+      gtk_widget_set_size_request (wscroll, width, height);
+      
+      /* Must force out update so changed scroll bars gets redrawn.  */
       gdk_window_process_all_updates ();
 
       SET_FRAME_GARBAGED (f);
@@ -2617,14 +2721,14 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
 
   FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
 
-  BLOCK_INPUT;
-
   if (wscroll && NILP (bar->dragging))
     {
       GtkAdjustment *adj;
       gdouble shown;
       gdouble top;
       int size, value;
+      int new_step;
+      int changed = 0;
 
       adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
 
@@ -2640,41 +2744,51 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
         top = 0, shown = 1;
       else
         {
-          shown = (gdouble) portion / whole;
           top = (gdouble) position / whole;
+          shown = (gdouble) portion / whole;
         }
 
-      size = shown * whole;
-      size = min (size, whole);
+      size = shown * XG_SB_RANGE;
+      size = min (size, XG_SB_RANGE);
       size = max (size, 1);
 
-      value = top * whole;
-      value = min (value, whole - size);
+      value = top * XG_SB_RANGE;
+      value = min (value, XG_SB_MAX - size);
       value = max (value, XG_SB_MIN);
 
-      adj->page_size = (int)size;
+      /* Assume all lines are of equal size.  */
+      new_step = size / max (1, FRAME_HEIGHT (f));
+
+      if ((int) adj->page_size != size
+          || (int) adj->step_increment != new_step)
+        {
+          adj->page_size = size;
+          adj->step_increment = new_step;
+          /* Assume a page increment is about 95% of the page size  */
+          adj->page_increment = (int) (0.95*adj->page_size);
+          changed = 1;
+        }
 
-      /* gtk_range_set_value invokes the callback.  Set
-         ignore_gtk_scrollbar to make the callback do nothing  */
-      xg_ignore_gtk_scrollbar = 1;
+      if (changed || (int) gtk_range_get_value (GTK_RANGE (wscroll)) != value)
+      {
+        GtkWidget *wfixed = f->output_data.x->edit_widget;
 
-      gtk_range_set_range (GTK_RANGE (wscroll), adj->lower, max (whole, size));
+        BLOCK_INPUT;
 
-      /* Assume all lines are of equal size.  */
-      /* Assume a page increment is about 95% of the page size  */
-      gtk_range_set_increments (GTK_RANGE (wscroll),
-                                portion / max (1, FRAME_HEIGHT (f)),
-                                (int) (0.95*adj->page_size));
+        /* gtk_range_set_value invokes the callback.  Set
+           ignore_gtk_scrollbar to make the callback do nothing  */
+        xg_ignore_gtk_scrollbar = 1;
 
-      gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value);
-      xg_ignore_gtk_scrollbar = 0;
+        if ((int) gtk_range_get_value (GTK_RANGE (wscroll)) != value)
+          gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value);
+        else if (changed)
+          gtk_adjustment_changed (adj);
 
-      /* Make GTK draw the new thumb.  We are not using a pure GTK event
-         loop so we need to do this.  */
-      gdk_window_process_all_updates ();
-    }
+        xg_ignore_gtk_scrollbar = 0;
 
-  UNBLOCK_INPUT;
+        UNBLOCK_INPUT;
+      }
+    }
 }
 
 \f
@@ -2833,8 +2947,19 @@ xg_tool_bar_item_expose_callback (w, event, client_data)
      GdkEventExpose *event;
      gpointer client_data;
 {
-  event->area.x = event->area.y = 0;
-  event->area.width = event->area.height = 1000;
+  gint width, height;
+
+  gdk_drawable_get_size (event->window, &width, &height);
+
+  event->area.x -= width > event->area.width ? width-event->area.width : 0;
+  event->area.y -= height > event->area.height ? height-event->area.height : 0;
+
+  event->area.x = max(0, event->area.x);
+  event->area.y = max(0, event->area.y);
+  
+  event->area.width = max (width, event->area.width);
+  event->area.height = max (height, event->area.height);
+  
   return FALSE;
 }
 
@@ -3080,9 +3205,6 @@ update_frame_tool_bar (f)
       xg_resize_outer_widget (f, FRAME_WIDTH (f), FRAME_HEIGHT (f));
     }
 
-  /* Must force out update so changed images gets redrawn.  */
-  gdk_window_process_all_updates ();
-
   if (icon_list) g_list_free (icon_list);
 
   UNBLOCK_INPUT;