]> code.delx.au - gnu-emacs/blobdiff - src/gtkutil.c
* macterm.c: Remove consolidated defines and code.
[gnu-emacs] / src / gtkutil.c
index 2c6fb10ccb306c87a867b01d1d0d9a3d7823e1c9..61e797493541e724009bf9f569b1f29efba9494b 100644 (file)
@@ -35,6 +35,8 @@ Boston, MA 02111-1307, USA.  */
 
 #define FRAME_TOTAL_PIXEL_HEIGHT(f) \
   (PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
+
+
 \f
 /***********************************************************************
                       Utility functions
@@ -285,6 +287,24 @@ xg_resize_outer_widget (f, columns, rows)
   gdk_window_process_all_updates ();
 }
 
+/* This gets called after the frame F has been cleared.  Since that is
+   done with X calls, we need to redraw GTK widget (scroll bars).  */
+void
+xg_frame_cleared (f)
+     FRAME_PTR f;
+{
+  GtkWidget *w = f->output_data.x->widget;
+
+  if (w)
+    {
+      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 ();
+    }
+}
+
 /* Function to handle resize of our widgets.  Since Emacs has some layouts
    that does not fit well with GTK standard containers, we do most layout
    manually.
@@ -314,7 +334,8 @@ xg_resize_widgets (f, pixelwidth, pixelheight)
       all.height = pixelheight - mbheight - tbheight;
 
       gtk_widget_size_allocate (x->edit_widget, &all);
-      gdk_window_process_all_updates ();
+
+      xg_frame_cleared (f);
 
       change_frame_size (f, rows, columns, 0, 1, 0);
       SET_FRAME_GARBAGED (f);
@@ -345,7 +366,7 @@ xg_frame_set_char_size (f, cols, rows)
        : (FRAME_SCROLL_BAR_COLS (f)
           * FONT_WIDTH (f->output_data.x->font)));
 
-  x_compute_fringe_widths (f, 0);
+  compute_fringe_widths (f, 0);
 
   /* Must resize our top level widget.  Font size may have changed,
      but not rows/cols.  */
@@ -1344,6 +1365,10 @@ xg_create_one_menuitem (item, f, select_cb, highlight_cb, cl_data, group)
   return w;
 }
 
+static GtkWidget *create_menus P_ ((widget_value *, FRAME_PTR, GCallback,
+                                   GCallback, GCallback, int, int, int,
+                                   GtkWidget *, xg_menu_cb_data *, char *));
+
 /* Create a full menu tree specified by DATA.
    F is the frame the created menu belongs to.
    SELECT_CB is the callback to use when a menu item is selected.
@@ -2180,6 +2205,8 @@ xg_update_frame_menubar (f)
 
   SET_FRAME_GARBAGED (f);
   UNBLOCK_INPUT;
+
+  return 1;
 }
 
 /* Get rid of the menu bar of frame F, and free its storage.
@@ -2221,11 +2248,6 @@ free_frame_menubar (f)
    to indicate that callback should do nothing.  */
 int xg_ignore_gtk_scrollbar;
 
-/* After we send a scroll bar event,  x_set_toolkit_scroll_bar_thumb will
-   be called.  For some reason that needs to be debugged, it gets called
-   with bad values.  Thus, we set this variable to ignore those calls.  */
-int xg_ignore_next_thumb;
-
 /* SET_SCROLL_BAR_X_WINDOW assumes the second argument fits in
    32 bits.  But we want to store pointers, and they may be larger
    than 32 bits.  Keep a mapping from integer index to widget pointers
@@ -2324,7 +2346,7 @@ xg_get_scroll_id_for_window (wid)
 
 /* Callback invoked when scroll bar WIDGET is destroyed.
    DATA is the index into id_to_widget for WIDGET.
-   We free pointer to last scroll bar value here and remove the index.  */
+   We free pointer to last scroll bar values here and remove the index.  */
 static void
 xg_gtk_scroll_destroy (widget, data)
      GtkWidget *widget;
@@ -2340,9 +2362,10 @@ xg_gtk_scroll_destroy (widget, data)
 
 /* Callback for button press/release events.  Used to start timer so that
    the scroll bar repetition timer in GTK gets handeled.
+   Also, sets bar->dragging to Qnil when dragging (button release) is done.
    WIDGET is the scroll bar widget the event is for (not used).
    EVENT contains the event.
-   USER_DATA is 0 (not used).
+   USER_DATA points to the struct scrollbar structure.
 
    Returns FALSE to tell GTK that it shall continue propagate the event
    to widgets.  */
@@ -2354,9 +2377,13 @@ scroll_bar_button_cb (widget, event, user_data)
 {
   if (event->type == GDK_BUTTON_PRESS && ! xg_timer)
     xg_start_timer ();
-  else if (event->type == GDK_BUTTON_RELEASE && xg_timer)
-    xg_stop_timer ();
-
+  else if (event->type == GDK_BUTTON_RELEASE)
+    {
+      struct scroll_bar *bar = (struct scroll_bar *) user_data;
+      if (xg_timer) xg_stop_timer ();
+      bar->dragging = Qnil;
+    }
+  
   return FALSE;
 }
 
@@ -2402,14 +2429,14 @@ xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name)
   g_signal_connect (G_OBJECT (wscroll),
                     "button-press-event",
                     G_CALLBACK (scroll_bar_button_cb),
-                    0);
+                    (gpointer)bar);
   g_signal_connect (G_OBJECT (wscroll),
                     "button-release-event",
                     G_CALLBACK (scroll_bar_button_cb),
-                    0);
+                    (gpointer)bar);
 
   gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget),
-                 wscroll, 0, 0);
+                 wscroll, -1, -1);
 
   /* Set the cursor to an arrow.  */
   xg_set_cursor (wscroll, &xg_left_ptr_cursor);
@@ -2441,6 +2468,29 @@ xg_remove_scroll_bar (f, scrollbar_id)
     }
 }
 
+/* Find left/top for widget W in GtkFixed widget WFIXED.  */
+static void
+xg_find_top_left_in_fixed (w, wfixed, left, top)
+     GtkWidget *w, *wfixed;
+     int *left, *top;
+{
+  GList *iter;
+
+  for (iter = GTK_FIXED (wfixed)->children; iter; iter = g_list_next (iter))
+    {
+      GtkFixedChild *child = (GtkFixedChild *) iter->data;
+
+      if (child->widget == w)
+        {
+          *left = child->x;
+          *top = child->y;
+          return;
+        }
+    }
+
+  /* Shall never end up here.  */
+  abort ();
+}
 
 /* Update the position of the vertical scroll bar represented by SCROLLBAR_ID
    in frame F.
@@ -2457,28 +2507,101 @@ 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;
 
-      gtk_fixed_move (GTK_FIXED (f->output_data.x->edit_widget),
-                      wscroll, left, top);
+      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);
+      oldbottom = oldtop + req.height;
+
+      /* Scroll bars in GTK has a fixed width, so if we say width 16, it
+         will only be its fixed width (14 is default) anyway, the rest is
+         blank.  We are drawing the mode line across scroll bars when
+         the frame is split:
+                               |bar| |fringe|
+                              ----------------
+                              mode line
+                              ----------------
+                               |bar| |fringe|
+
+         When we "unsplit" the frame:
+
+                               |bar| |fringe|
+                              -|   |-|      |
+                              m¦   |i|      |
+                              -|   |-|      |
+                               |   | |      |
+
+
+         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.  */
+
+      if (oldtop != -1 && oldleft != -1)
+        {
+          int gtkextra;
+          int xl, xr, wblank;
+          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;
+          bottomdiff = abs (oldbottom - bottom);
+          topdiff = abs (oldtop - top);
+
+          if (oldtop > top)
+            {
+              gdk_window_clear_area (wfixed->window, xl, top, wblank, topdiff);
+              gdk_window_clear_area (wfixed->window, xr, top, wblank, 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);
+            }
 
-      gtk_widget_set_size_request (wscroll, width, gheight);
+          if (oldbottom > bottom)
+            {
+              gdk_window_clear_area (wfixed->window, xl, bottom, wblank,
+                                     bottomdiff);
+              gdk_window_clear_area (wfixed->window, xr, bottom, wblank,
+                                     bottomdiff);
+            }
+          else if (oldbottom < bottom)
+            {
+              gdk_window_clear_area (wfixed->window, xl, oldbottom, wblank,
+                                     bottomdiff);
+              gdk_window_clear_area (wfixed->window, xr, oldbottom, wblank,
+                                     bottomdiff);
+            }
+        }
 
-      /* Must force out update so wscroll gets the resize.
-         Otherwise, the gdk_window_clear clears the old window size.  */
-      gdk_window_process_all_updates ();
+      /* Move and resize to new values.  */
+      gtk_fixed_move (GTK_FIXED (wfixed), wscroll, left, top);
+      gtk_widget_set_size_request (wscroll, width, gheight);
 
-      /* The scroll bar doesn't explicitly redraw the whole window
-         when a resize occurs.  Since the scroll bar seems to be fixed
-         in width it doesn't fill the space reserved, so we must clear
-         the whole window.  */
-      gdk_window_clear (wscroll->window);
+      gtk_container_set_reallocate_redraws (GTK_CONTAINER (wfixed), TRUE);
 
-      /* Since we are not using a pure gtk event loop, we must force out
-         pending update events with this call.  */
+      /* Make GTK draw the new sizes.  We are not using a pure GTK event
+         loop so we need to do this.  */
       gdk_window_process_all_updates ();
 
       SET_FRAME_GARBAGED (f);
@@ -2498,15 +2621,25 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
   FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
 
   BLOCK_INPUT;
-  if (wscroll && ! xg_ignore_next_thumb)
+
+  if (wscroll && NILP (bar->dragging))
     {
       GtkAdjustment *adj;
       gdouble shown;
       gdouble top;
       int size, value;
+      int new_upper, new_step;
 
       adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
 
+      /* We do the same as for MOTIF in xterm.c, assume 30 chars per line
+         rather than the real portion value.  This makes the thumb less likely
+         to resize and that looks better.  */
+      portion = XFASTINT (XWINDOW (bar->window)->height) * 30;
+      /* When the thumb is at the bottom, position == whole.
+         So we need to increase `whole' to make space for the thumb.  */
+      whole += portion;
+
       if (whole <= 0)
         top = 0, shown = 1;
       else
@@ -2523,26 +2656,40 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
       value = min (value, whole - size);
       value = max (value, XG_SB_MIN);
 
-      adj->upper = max (whole, size);
-      adj->page_size = (int)size;
-
-      /* Assume a page increment is about 95% of the page size  */
-      adj->page_increment = (int) (0.95*adj->page_size);
-
-      /* Assume all lines are equal.  */
-      adj->step_increment = portion / max (1, FRAME_HEIGHT (f));
-
       /* 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);
+
+      new_upper = max (whole, size);
+      new_step  =  portion / max (1, FRAME_HEIGHT (f));
+
+      if ((int) adj->page_size != size
+          || (int) adj->upper != new_upper
+          || (int) adj->step_increment != new_step)
+        {
+          adj->page_size = (int) size;
+
+          gtk_range_set_range (GTK_RANGE (wscroll), adj->lower,
+                               (gdouble) new_upper);
+
+          /* 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));
+          
+        }
+
+      if ((int) gtk_range_get_value (GTK_RANGE (wscroll)) != value)
+        gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value);
+
       xg_ignore_gtk_scrollbar = 0;
+
+      /* 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 ();
     }
 
-  /* Make sure the scroll bar is redrawn with new thumb  */
-  gtk_widget_queue_draw (wscroll);
-  gdk_window_process_all_updates ();
-  xg_ignore_next_thumb = 0;
   UNBLOCK_INPUT;
 }
 
@@ -2667,7 +2814,7 @@ xg_tool_bar_help_callback (w, event, client_data)
     }
 
   if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
-    return;
+    return FALSE;
 
   if (event->type == GDK_ENTER_NOTIFY)
     {
@@ -2990,7 +3137,6 @@ void
 xg_initialize ()
 {
   xg_ignore_gtk_scrollbar = 0;
-  xg_ignore_next_thumb = 0;
   xg_left_ptr_cursor = 0;
   xg_did_tearoff = 0;