]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtktextview.c
Change FSF Address
[~andy/gtk] / gtk / gtktextview.c
index f06250c406dc09d08b271c4e096681f6dfb40118..a69040da0c60e5296c715e14395f7619cdad7eb7 100644 (file)
@@ -13,9 +13,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 /*
@@ -53,6 +51,7 @@
 #include "gtkscrollable.h"
 #include "gtktypebuiltins.h"
 
+#include "a11y/gtktextviewaccessible.h"
 
 /**
  * SECTION:gtktextview
@@ -133,7 +132,6 @@ struct _GtkTextViewPrivate
   GdkDevice *dnd_device;
 
   gulong selection_drag_handler;
-  guint scroll_timeout;
 
   GtkTextWindow *text_window;
   GtkTextWindow *left_window;
@@ -176,12 +174,16 @@ struct _GtkTextViewPrivate
   GtkTextMark *first_para_mark; /* Mark at the beginning of the first onscreen paragraph */
   gint first_para_pixels;       /* Offset of top of screen in the first onscreen paragraph */
 
-  GtkTextMark *dnd_mark;
   guint blink_timeout;
+  guint scroll_timeout;
 
   guint first_validate_idle;        /* Idle to revalidate onscreen portion, runs before resize */
   guint incremental_validate_idle;  /* Idle to revalidate offscreen portions, runs after redraw */
 
+  gint pending_place_cursor_button;
+
+  GtkTextMark *dnd_mark;
+
   GtkIMContext *im_context;
   GtkWidget *popup_menu;
 
@@ -192,8 +194,6 @@ struct _GtkTextViewPrivate
 
   GtkTextPendingScroll *pending_scroll;
 
-  gint pending_place_cursor_button;
-
   /* Default style settings */
   gint pixels_above_lines;
   gint pixels_below_lines;
@@ -209,7 +209,7 @@ struct _GtkTextViewPrivate
   guint overwrite_mode : 1;
   guint cursor_visible : 1;
 
-  /* if we have reset the IM since the last character entered */  
+  /* if we have reset the IM since the last character entered */
   guint need_im_reset : 1;
 
   guint accepts_tab : 1;
@@ -452,8 +452,6 @@ static void gtk_text_view_target_list_notify     (GtkTextBuffer     *buffer,
 static void gtk_text_view_paste_done_handler     (GtkTextBuffer     *buffer,
                                                   GtkClipboard      *clipboard,
                                                   gpointer           data);
-static void gtk_text_view_get_cursor_location    (GtkTextView       *text_view,
-                                                 GdkRectangle      *pos);
 static void gtk_text_view_get_virtual_cursor_pos (GtkTextView       *text_view,
                                                   GtkTextIter       *cursor,
                                                   gint              *x,
@@ -1352,6 +1350,8 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
                                GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
 
   g_type_class_add_private (gobject_class, sizeof (GtkTextViewPrivate));
+
+  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_TEXT_VIEW_ACCESSIBLE);
 }
 
 static void
@@ -1477,6 +1477,7 @@ gtk_text_view_set_buffer (GtkTextView   *text_view,
                           GtkTextBuffer *buffer)
 {
   GtkTextViewPrivate *priv;
+  GtkTextBuffer *old_buffer;
 
   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
   g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
@@ -1486,6 +1487,7 @@ gtk_text_view_set_buffer (GtkTextView   *text_view,
   if (priv->buffer == buffer)
     return;
 
+  old_buffer = priv->buffer;
   if (priv->buffer != NULL)
     {
       /* Destroy all anchored children */
@@ -1529,7 +1531,6 @@ gtk_text_view_set_buffer (GtkTextView   *text_view,
       if (priv->layout)
         gtk_text_layout_set_buffer (priv->layout, NULL);
 
-      g_object_unref (priv->buffer);
       priv->dnd_mark = NULL;
       priv->first_para_mark = NULL;
       cancel_pending_scroll (text_view);
@@ -1579,6 +1580,10 @@ gtk_text_view_set_buffer (GtkTextView   *text_view,
        }
     }
 
+  _gtk_text_view_accessible_set_buffer (text_view, old_buffer);
+  if (old_buffer)
+    g_object_unref (old_buffer);
+
   g_object_notify (G_OBJECT (text_view), "buffer");
   
   if (gtk_widget_get_visible (GTK_WIDGET (text_view)))
@@ -1620,6 +1625,61 @@ gtk_text_view_get_buffer (GtkTextView *text_view)
   return get_buffer (text_view);
 }
 
+/**
+ * gtk_text_view_get_cursor_locations:
+ * @text_view: a #GtkTextView
+ * @iter: (allow-none): a #GtkTextIter
+ * @strong: (out) (allow-none): location to store the strong
+ *     cursor position (may be %NULL)
+ * @weak: (out) (allow-none): location to store the weak
+ *     cursor position (may be %NULL)
+ *
+ * Given an @iter within a text layout, determine the positions of the
+ * strong and weak cursors if the insertion point is at that
+ * iterator. The position of each cursor is stored as a zero-width
+ * rectangle. The strong cursor location is the location where
+ * characters of the directionality equal to the base direction of the
+ * paragraph are inserted.  The weak cursor location is the location
+ * where characters of the directionality opposite to the base
+ * direction of the paragraph are inserted.
+ *
+ * If @iter is %NULL, the actual cursor position is used.
+ *
+ * Note that if @iter happens to be the actual cursor position, and
+ * there is currently an IM preedit sequence being entered, the
+ * returned locations will be adjusted to account for the preedit
+ * cursor's offset within the preedit sequence.
+ *
+ * The rectangle position is in buffer coordinates; use
+ * gtk_text_view_buffer_to_window_coords() to convert these
+ * coordinates to coordinates for one of the windows in the text view.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_text_view_get_cursor_locations (GtkTextView       *text_view,
+                                    const GtkTextIter *iter,
+                                    GdkRectangle      *strong,
+                                    GdkRectangle      *weak)
+{
+  GtkTextIter insert;
+
+  g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
+  g_return_if_fail (iter == NULL ||
+                    gtk_text_iter_get_buffer (iter) == get_buffer (text_view));
+
+  gtk_text_view_ensure_layout (text_view);
+
+  if (iter)
+    insert = *iter;
+  else
+    gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
+                                      gtk_text_buffer_get_insert (get_buffer (text_view)));
+
+  gtk_text_layout_get_cursor_locations (text_view->priv->layout, &insert,
+                                        strong, weak);
+}
+
 /**
  * gtk_text_view_get_iter_at_location:
  * @text_view: a #GtkTextView
@@ -2119,13 +2179,13 @@ gtk_text_view_update_im_spot_location (GtkTextView *text_view)
   if (text_view->priv->layout == NULL)
     return;
   
-  gtk_text_view_get_cursor_location (text_view, &area);
+  gtk_text_view_get_cursor_locations (text_view, NULL, &area, NULL);
 
   area.x -= text_view->priv->xoffset;
   area.y -= text_view->priv->yoffset;
     
   /* Width returned by Pango indicates direction of cursor,
-   * by it's sign more than the size of cursor.
+   * by its sign more than the size of cursor.
    */
   area.width = 0;
 
@@ -2293,7 +2353,7 @@ gtk_text_view_move_mark_onscreen (GtkTextView *text_view,
 /**
  * gtk_text_view_get_visible_rect:
  * @text_view: a #GtkTextView
- * @visible_rect: rectangle to fill
+ * @visible_rect: (out): rectangle to fill
  *
  * Fills @visible_rect with the currently-visible
  * region of the buffer, in buffer coordinates. Convert to window coordinates
@@ -2978,10 +3038,11 @@ gtk_text_view_finalize (GObject *object)
   text_view = GTK_TEXT_VIEW (object);
   priv = text_view->priv;
 
-  g_assert (priv->buffer == NULL);
-
   gtk_text_view_destroy_layout (text_view);
   gtk_text_view_set_buffer (text_view, NULL);
+
+  /* at this point, no "notify::buffer" handler should recreate the buffer. */
+  g_assert (priv->buffer == NULL);
   
   cancel_pending_scroll (text_view);
 
@@ -3921,12 +3982,10 @@ gtk_text_view_realize (GtkWidget *widget)
   GtkTextView *text_view;
   GtkTextViewPrivate *priv;
   GtkStyleContext *context;
-  GtkStateFlags state;
   GdkWindow *window;
   GdkWindowAttr attributes;
   gint attributes_mask;
   GSList *tmp_list;
-  GdkRGBA color;
 
   text_view = GTK_TEXT_VIEW (widget);
   priv = text_view->priv;
@@ -3952,10 +4011,11 @@ gtk_text_view_realize (GtkWidget *widget)
   gdk_window_set_user_data (window, widget);
 
   context = gtk_widget_get_style_context (widget);
-  state = gtk_widget_get_state_flags (widget);
 
-  gtk_style_context_get_background_color (context, state, &color);
-  gdk_window_set_background_rgba (window, &color);
+  gtk_style_context_save (context);
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW);
+  gtk_style_context_set_background (context, window);
+  gtk_style_context_restore (context);
 
   text_window_realize (priv->text_window, widget);
 
@@ -3972,6 +4032,7 @@ gtk_text_view_realize (GtkWidget *widget)
     text_window_realize (priv->bottom_window, widget);
 
   gtk_text_view_ensure_layout (text_view);
+  gtk_text_view_invalidate (text_view);
 
   if (priv->buffer)
     {
@@ -4032,8 +4093,6 @@ gtk_text_view_unrealize (GtkWidget *widget)
   if (priv->bottom_window)
     text_window_unrealize (priv->bottom_window);
 
-  gtk_text_view_destroy_layout (text_view);
-
   GTK_WIDGET_CLASS (gtk_text_view_parent_class)->unrealize (widget);
 }
 
@@ -4056,16 +4115,14 @@ gtk_text_view_set_background (GtkTextView *text_view)
   gtk_style_context_save (context);
   gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW);
 
-  gtk_style_context_get_background_color (context, state, &color);
-  gdk_window_set_background_rgba (priv->text_window->bin_window, &color);
+  gtk_style_context_set_background (context, priv->text_window->bin_window);
+  gtk_style_context_set_background (context, gtk_widget_get_window (widget));
 
   gtk_style_context_restore (context);
 
   /* Set lateral panes background */
   gtk_style_context_get_background_color (context, state, &color);
 
-  gdk_window_set_background_rgba (gtk_widget_get_window (widget), &color);
-
   if (priv->left_window)
     gdk_window_set_background_rgba (priv->left_window->bin_window, &color);
 
@@ -4089,6 +4146,8 @@ gtk_text_view_style_updated (GtkWidget *widget)
   text_view = GTK_TEXT_VIEW (widget);
   priv = text_view->priv;
 
+  GTK_WIDGET_CLASS (gtk_text_view_parent_class)->style_updated (widget);
+
   if (gtk_widget_get_realized (widget))
     {
       gtk_text_view_set_background (text_view);
@@ -4209,8 +4268,15 @@ gtk_text_view_grab_notify (GtkWidget *widget,
   if (priv->grab_device &&
       gtk_widget_device_is_shadowed (widget, priv->grab_device))
     {
+      if (priv->drag_start_x >= 0)
+        {
+          priv->drag_start_x = -1;
+          priv->drag_start_y = -1;
+        }
+
       gtk_text_view_end_selection_drag (GTK_TEXT_VIEW (widget));
       gtk_text_view_unobscure_mouse_cursor (GTK_TEXT_VIEW (widget));
+      priv->grab_device = NULL;
     }
 }
 
@@ -4476,9 +4542,9 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
 
 #if 0
   /* debug hack */
-  if (event->button == 3 && (event->state & GDK_CONTROL_MASK) != 0)
+  if (event->button == GDK_BUTTON_SECONDARY && (event->state & GDK_CONTROL_MASK) != 0)
     _gtk_text_buffer_spew (GTK_TEXT_VIEW (widget)->buffer);
-  else if (event->button == 3)
+  else if (event->button == GDK_BUTTON_SECONDARY)
     gtk_text_layout_spew (GTK_TEXT_VIEW (widget)->layout);
 #endif
 
@@ -4486,7 +4552,12 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
     {
       gtk_text_view_reset_im_context (text_view);
 
-      if (event->button == 1)
+      if (gdk_event_triggers_context_menu ((GdkEvent *) event))
+        {
+         gtk_text_view_do_popup (text_view, event);
+         return TRUE;
+        }
+      else if (event->button == GDK_BUTTON_PRIMARY)
         {
           /* If we're in the selection, start a drag copy/move of the
            * selection; otherwise, start creating a new selection.
@@ -4502,8 +4573,11 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
           if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
                                                     &start, &end) &&
               gtk_text_iter_in_range (&iter, &start, &end) &&
-              !(event->state & GDK_SHIFT_MASK))
+              !(event->state &
+                gtk_widget_get_modifier_mask (widget,
+                                              GDK_MODIFIER_INTENT_EXTEND_SELECTION)))
             {
+              priv->grab_device = event->device;
               priv->drag_start_x = event->x;
               priv->drag_start_y = event->y;
               priv->pending_place_cursor_button = event->button;
@@ -4515,7 +4589,7 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
 
           return TRUE;
         }
-      else if (event->button == 2)
+      else if (event->button == GDK_BUTTON_MIDDLE)
         {
           GtkTextIter iter;
 
@@ -4534,15 +4608,10 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
                                           priv->editable);
           return TRUE;
         }
-      else if (event->button == 3)
-        {
-         gtk_text_view_do_popup (text_view, event);
-         return TRUE;
-        }
     }
   else if ((event->type == GDK_2BUTTON_PRESS ||
            event->type == GDK_3BUTTON_PRESS) &&
-          event->button == 1) 
+          event->button == GDK_BUTTON_PRIMARY)
     {
       GtkTextIter iter;
 
@@ -4572,7 +4641,7 @@ gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event)
   if (event->window != priv->text_window->bin_window)
     return FALSE;
 
-  if (event->button == 1)
+  if (event->button == GDK_BUTTON_PRIMARY)
     {
       if (priv->drag_start_x >= 0)
         {
@@ -4847,7 +4916,7 @@ gtk_text_view_draw_focus (GtkWidget *widget,
                        "interior-focus", &interior_focus,
                        NULL);
   
-  if (gtk_widget_has_focus (widget) && !interior_focus)
+  if (gtk_widget_has_visible_focus (widget) && !interior_focus)
     {
       GtkStyleContext *context;
 
@@ -5265,7 +5334,7 @@ gtk_text_view_move_cursor_internal (GtkTextView     *text_view,
       old_xpos = priv->xoffset;
       old_ypos = priv->yoffset;
       gtk_text_view_move_viewport (text_view, scroll_step, count);
-      if ((old_xpos != priv->xoffset || old_ypos != priv->yoffset) &&
+      if ((old_xpos == priv->xoffset && old_ypos == priv->yoffset) &&
           leave_direction != -1 &&
           !gtk_widget_keynav_failed (GTK_WIDGET (text_view),
                                      leave_direction))
@@ -6432,7 +6501,9 @@ gtk_text_view_start_selection_drag (GtkTextView       *text_view,
   orig_start = ins;
   orig_end = bound;
 
-  if (button->state & GDK_SHIFT_MASK)
+  if (button->state &
+      gtk_widget_get_modifier_mask (GTK_WIDGET (text_view),
+                                    GDK_MODIFIER_INTENT_EXTEND_SELECTION))
     {
       /* Extend selection */
       GtkTextIter old_ins, old_bound;
@@ -6672,8 +6743,6 @@ gtk_text_view_ensure_layout (GtkTextView *text_view)
 
           tmp_list = g_slist_next (tmp_list);
         }
-
-      gtk_text_view_invalidate (text_view);
     }
 }
 
@@ -7464,6 +7533,8 @@ adjust_allocation_recurse (GtkWidget *widget,
    * into widget->allocation if the widget is not realized.
    * FIXME someone figure out why this was.
    */
+  gtk_widget_get_allocation (widget, &allocation);
+
   if (!gtk_widget_get_realized (widget))
     {
       if (gtk_widget_get_visible (widget))
@@ -7510,7 +7581,7 @@ adjust_allocation (GtkWidget *widget,
   
   adjust_allocation_recurse (widget, &scroll_data);
 }
-            
+
 static void
 gtk_text_view_value_changed (GtkAdjustment *adjustment,
                              GtkTextView   *text_view)
@@ -7882,18 +7953,6 @@ gtk_text_view_target_list_notify (GtkTextBuffer    *buffer,
   gtk_target_list_unref (view_list);
 }
 
-static void
-gtk_text_view_get_cursor_location  (GtkTextView   *text_view,
-                                   GdkRectangle  *pos)
-{
-  GtkTextIter insert;
-  
-  gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
-                                    gtk_text_buffer_get_insert (get_buffer (text_view)));
-
-  gtk_text_layout_get_cursor_locations (text_view->priv->layout, &insert, pos, NULL);
-}
-
 static void
 gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view,
                                       GtkTextIter *cursor,
@@ -7944,7 +8003,7 @@ gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view,
     return;
 
   if (x == -1 || y == -1)
-    gtk_text_view_get_cursor_location (text_view, &pos);
+    gtk_text_view_get_cursor_locations (text_view, NULL, &pos, NULL);
 
   text_view->priv->virtual_cursor_x = (x == -1) ? pos.x : x;
   text_view->priv->virtual_cursor_y = (y == -1) ? pos.y + pos.height / 2 : y;
@@ -8093,7 +8152,7 @@ popup_position_func (GtkMenu   *menu,
 
   monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
   gtk_menu_set_monitor (menu, monitor_num);
-  gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+  gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
 
   *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
   *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
@@ -8106,6 +8165,7 @@ typedef struct
   GtkTextView *text_view;
   gint button;
   guint time;
+  GdkDevice *device;
 } PopupInfo;
 
 static gboolean
@@ -8206,6 +8266,8 @@ popup_targets_received (GtkClipboard     *clipboard,
       gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
 
       menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL);
+      gtk_widget_set_sensitive (menuitem,
+                                gtk_text_buffer_get_char_count (priv->buffer) > 0);
       g_signal_connect (menuitem, "activate",
                        G_CALLBACK (select_all_cb), text_view);
       gtk_widget_show (menuitem);
@@ -8257,9 +8319,9 @@ popup_targets_received (GtkClipboard     *clipboard,
                     0,
                     priv->popup_menu);
       
-      if (info->button)
-       gtk_menu_popup (GTK_MENU (priv->popup_menu), NULL, NULL,
-                       NULL, NULL,
+      if (info->device)
+       gtk_menu_popup_for_device (GTK_MENU (priv->popup_menu), 
+      info->device, NULL, NULL, NULL, NULL, NULL,
                        info->button, info->time);
       else
        {
@@ -8290,11 +8352,13 @@ gtk_text_view_do_popup (GtkTextView    *text_view,
     {
       info->button = event->button;
       info->time = event->time;
+      info->device = event->device;
     }
   else
     {
       info->button = 0;
       info->time = gtk_get_current_event_time ();
+      info->device = NULL;
     }
 
   gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (text_view),
@@ -8566,7 +8630,7 @@ text_window_invalidate_cursors (GtkTextWindow *win)
   gtk_text_layout_get_cursor_locations (priv->layout, &iter,
                                         &strong, &weak);
 
-  /* cursor width calculation as in gtkstyle.c:draw_insertion_cursor(),
+  /* cursor width calculation as in gtkstylecontext.c:draw_insertion_cursor(),
    * ignoring the text direction be exposing both sides of the cursor
    */