* 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/>.
*/
/*
#include "gtkwindow.h"
#include "gtkscrollable.h"
#include "gtktypebuiltins.h"
+#include "gtktexthandleprivate.h"
+#include "gtkstylecontextprivate.h"
+#include "gtkcssstylepropertyprivate.h"
+#include "a11y/gtktextviewaccessibleprivate.h"
/**
* SECTION:gtktextview
GdkDevice *dnd_device;
gulong selection_drag_handler;
+ GtkTextHandle *text_handle;
GtkTextWindow *text_window;
GtkTextWindow *left_window;
* driving the scrollable adjustment values */
guint hscroll_policy : 1;
guint vscroll_policy : 1;
+ guint cursor_handle_dragged : 1;
+ guint selection_handle_dragged : 1;
};
struct _GtkTextPendingScroll
PROP_HADJUSTMENT,
PROP_VADJUSTMENT,
PROP_HSCROLL_POLICY,
- PROP_VSCROLL_POLICY
+ PROP_VSCROLL_POLICY,
+ PROP_INPUT_PURPOSE,
+ PROP_INPUT_HINTS
};
static void gtk_text_view_finalize (GObject *object);
GtkDirectionType direction);
static void gtk_text_view_select_all (GtkWidget *widget,
gboolean select);
-
+static gboolean get_middle_click_paste (GtkTextView *text_view);
/* Source side drag signals */
static void gtk_text_view_drag_begin (GtkWidget *widget,
static void gtk_text_view_paste_done_handler (GtkTextBuffer *buffer,
GtkClipboard *clipboard,
gpointer data);
+static void gtk_text_view_buffer_changed_handler (GtkTextBuffer *buffer,
+ gpointer data);
static void gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view,
GtkTextIter *cursor,
gint *x,
GtkCallback callback,
gpointer callback_data);
+/* GtkTextHandle handlers */
+static void gtk_text_view_handle_dragged (GtkTextHandle *handle,
+ GtkTextHandlePosition pos,
+ gint x,
+ gint y,
+ GtkTextView *text_view);
+static void gtk_text_view_update_handles (GtkTextView *text_view,
+ GtkTextHandleMode mode);
+
/* FIXME probably need the focus methods. */
typedef struct _GtkTextViewChild GtkTextViewChild;
static guint signals[LAST_SIGNAL] = { 0 };
+static gboolean test_touchscreen = FALSE;
G_DEFINE_TYPE_WITH_CODE (GtkTextView, gtk_text_view, GTK_TYPE_CONTAINER,
G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
/**
* GtkTextView:im-module:
*
- * Which IM (input method) module should be used for this entry.
+ * Which IM (input method) module should be used for this text_view.
* See #GtkIMContext.
*
* Setting this to a non-%NULL value overrides the
NULL,
GTK_PARAM_READWRITE));
+ /**
+ * GtkTextView:input-purpose:
+ *
+ * The purpose of this text field.
+ *
+ * This property can be used by on-screen keyboards and other input
+ * methods to adjust their behaviour.
+ *
+ * Since: 3.6
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_INPUT_PURPOSE,
+ g_param_spec_enum ("input-purpose",
+ P_("Purpose"),
+ P_("Purpose of the text field"),
+ GTK_TYPE_INPUT_PURPOSE,
+ GTK_INPUT_PURPOSE_FREE_FORM,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GtkTextView:input-hints:
+ *
+ * Additional hints (beyond #GtkTextView:input-purpose) that
+ * allow input methods to fine-tune their behaviour.
+ *
+ * Since: 3.6
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_INPUT_HINTS,
+ g_param_spec_flags ("input-hints",
+ P_("hints"),
+ P_("Hints for the text field behaviour"),
+ GTK_TYPE_INPUT_HINTS,
+ GTK_INPUT_HINT_NONE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
/* GtkScrollable interface */
g_object_class_override_property (gobject_class, PROP_HADJUSTMENT, "hadjustment");
g_object_class_override_property (gobject_class, PROP_VADJUSTMENT, "vadjustment");
/**
* GtkTextView::populate-popup:
- * @entry: The text view on which the signal is emitted
+ * @text_view: The text view on which the signal is emitted
* @menu: the menu that is being populated
*
* The ::populate-popup signal gets emitted before showing the
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);
+ test_touchscreen = g_getenv ("GTK_TEST_TOUCHSCREEN") != NULL;
}
static void
/* We handle all our own redrawing */
gtk_widget_set_redraw_on_allocate (widget, FALSE);
+
+ priv->text_handle = _gtk_text_handle_new (widget);
+ g_signal_connect (priv->text_handle, "handle-dragged",
+ G_CALLBACK (gtk_text_view_handle_dragged), 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));
if (priv->buffer == buffer)
return;
+ old_buffer = priv->buffer;
if (priv->buffer != NULL)
{
/* Destroy all anchored children */
g_signal_handlers_disconnect_by_func (priv->buffer,
gtk_text_view_paste_done_handler,
text_view);
+ g_signal_handlers_disconnect_by_func (priv->buffer,
+ gtk_text_view_buffer_changed_handler,
+ text_view);
if (gtk_widget_get_realized (GTK_WIDGET (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);
g_signal_connect (priv->buffer, "paste-done",
G_CALLBACK (gtk_text_view_paste_done_handler),
text_view);
+ g_signal_connect (priv->buffer, "changed",
+ G_CALLBACK (gtk_text_view_buffer_changed_handler),
+ text_view);
gtk_text_view_target_list_notify (priv->buffer, NULL, text_view);
GDK_SELECTION_PRIMARY);
gtk_text_buffer_add_selection_clipboard (priv->buffer, clipboard);
}
+
+ gtk_text_view_update_handles (text_view, GTK_TEXT_HANDLE_MODE_NONE);
}
+ _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)))
if (priv->bottom_window)
text_window_free (priv->bottom_window);
+ g_object_unref (priv->text_handle);
g_object_unref (priv->im_context);
g_free (priv->im_module);
gtk_widget_queue_resize (GTK_WIDGET (text_view));
break;
+ case PROP_INPUT_PURPOSE:
+ gtk_text_view_set_input_purpose (text_view, g_value_get_enum (value));
+ break;
+
+ case PROP_INPUT_HINTS:
+ gtk_text_view_set_input_hints (text_view, g_value_get_flags (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
g_value_set_enum (value, priv->vscroll_policy);
break;
+ case PROP_INPUT_PURPOSE:
+ g_value_set_enum (value, gtk_text_view_get_input_purpose (text_view));
+ break;
+
+ case PROP_INPUT_HINTS:
+ g_value_set_flags (value, gtk_text_view_get_input_hints (text_view));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
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;
window = gdk_window_new (gtk_widget_get_parent_window (widget),
&attributes, attributes_mask);
gtk_widget_set_window (widget, window);
- gdk_window_set_user_data (window, widget);
+ gtk_widget_register_window (widget, window);
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);
/* Ensure updating the spot location. */
gtk_text_view_update_im_spot_location (text_view);
+
+ _gtk_text_handle_set_relative_to (priv->text_handle, priv->text_window->window);
}
static void
if (priv->bottom_window)
text_window_unrealize (priv->bottom_window);
+ _gtk_text_handle_set_relative_to (priv->text_handle, NULL);
+
GTK_WIDGET_CLASS (gtk_text_view_parent_class)->unrealize (widget);
}
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);
GtkTextView *text_view;
GtkTextViewPrivate *priv;
PangoContext *ltr_context, *rtl_context;
+ GtkStyleContext *style_context;
+ const GtkBitmask *changes;
text_view = GTK_TEXT_VIEW (widget);
priv = text_view->priv;
gtk_text_view_set_background (text_view);
}
- if (priv->layout && priv->layout->default_style)
+
+ style_context = gtk_widget_get_style_context (widget);
+ changes = _gtk_style_context_get_changes (style_context);
+ if ((changes == NULL || _gtk_css_style_property_changes_affect_font (changes)) &&
+ priv->layout && priv->layout->default_style)
{
gtk_text_view_set_attributes_from_style (text_view,
priv->layout->default_style);
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;
}
}
return retval;
}
+static void
+gtk_text_view_set_handle_position (GtkTextView *text_view,
+ GtkTextIter *iter,
+ GtkTextHandlePosition pos)
+{
+ GtkTextViewPrivate *priv;
+ GdkRectangle rect;
+ gint x, y;
+
+ priv = text_view->priv;
+ gtk_text_view_get_cursor_locations (text_view, iter, &rect, NULL);
+
+ x = rect.x - priv->xoffset;
+ y = rect.y - priv->yoffset;
+
+ if (!_gtk_text_handle_get_is_dragged (priv->text_handle, pos) &&
+ (x < 0 || x > SCREEN_WIDTH (text_view) ||
+ y < 0 || y > SCREEN_HEIGHT (text_view)))
+ {
+ /* Hide the handle if it's not being manipulated
+ * and fell outside of the visible text area.
+ */
+ _gtk_text_handle_set_visible (priv->text_handle, pos, FALSE);
+ }
+ else
+ {
+ _gtk_text_handle_set_visible (priv->text_handle, pos, TRUE);
+
+ rect.x = CLAMP (x, 0, SCREEN_WIDTH (text_view));
+ rect.y = CLAMP (y, 0, SCREEN_HEIGHT (text_view));
+ _gtk_text_handle_set_position (priv->text_handle, pos, &rect);
+ }
+}
+
+static void
+gtk_text_view_handle_dragged (GtkTextHandle *handle,
+ GtkTextHandlePosition pos,
+ gint x,
+ gint y,
+ GtkTextView *text_view)
+{
+ GtkTextViewPrivate *priv;
+ GtkTextIter old_cursor, old_bound;
+ GtkTextIter cursor, bound, iter;
+ GtkTextIter *min, *max;
+ GtkTextHandleMode mode;
+ GtkTextBuffer *buffer;
+ GtkTextHandlePosition cursor_pos;
+
+ priv = text_view->priv;
+ buffer = get_buffer (text_view);
+ mode = _gtk_text_handle_get_mode (handle);
+
+ gtk_text_layout_get_iter_at_pixel (priv->layout, &iter,
+ x + priv->xoffset,
+ y + priv->yoffset);
+ gtk_text_buffer_get_iter_at_mark (buffer, &old_cursor,
+ gtk_text_buffer_get_insert (buffer));
+ gtk_text_buffer_get_iter_at_mark (buffer, &old_bound,
+ gtk_text_buffer_get_selection_bound (buffer));
+ cursor = old_cursor;
+ bound = old_bound;
+
+ if (mode == GTK_TEXT_HANDLE_MODE_CURSOR ||
+ gtk_text_iter_compare (&cursor, &bound) >= 0)
+ {
+ cursor_pos = GTK_TEXT_HANDLE_POSITION_CURSOR;
+ max = &cursor;
+ min = &bound;
+ }
+ else
+ {
+ cursor_pos = GTK_TEXT_HANDLE_POSITION_SELECTION_START;
+ max = &bound;
+ min = &cursor;
+ }
+
+ if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END)
+ {
+ if (mode == GTK_TEXT_HANDLE_MODE_SELECTION &&
+ gtk_text_iter_compare (&iter, min) <= 0)
+ {
+ iter = *min;
+ gtk_text_iter_forward_char (&iter);
+ }
+
+ *max = iter;
+ gtk_text_view_set_handle_position (text_view, &iter, pos);
+ }
+ else
+ {
+ if (mode == GTK_TEXT_HANDLE_MODE_SELECTION &&
+ gtk_text_iter_compare (&iter, max) >= 0)
+ {
+ iter = *max;
+ gtk_text_iter_backward_char (&iter);
+ }
+
+ *min = iter;
+ gtk_text_view_set_handle_position (text_view, &iter, pos);
+ }
+
+ if (gtk_text_iter_compare (&old_cursor, &cursor) != 0 ||
+ gtk_text_iter_compare (&old_bound, &bound) != 0)
+ {
+ if (mode == GTK_TEXT_HANDLE_MODE_CURSOR)
+ gtk_text_buffer_place_cursor (buffer, &cursor);
+ else
+ gtk_text_buffer_select_range (buffer, &cursor, &bound);
+
+ if (_gtk_text_handle_get_is_dragged (priv->text_handle, cursor_pos))
+ gtk_text_view_scroll_mark_onscreen (text_view,
+ gtk_text_buffer_get_insert (buffer));
+ else
+ gtk_text_view_scroll_mark_onscreen (text_view,
+ gtk_text_buffer_get_selection_bound (buffer));
+ }
+}
+
+static void
+gtk_text_view_update_handles (GtkTextView *text_view,
+ GtkTextHandleMode mode)
+{
+ GtkTextViewPrivate *priv = text_view->priv;
+ GtkTextIter cursor, bound, min, max;
+ GtkTextBuffer *buffer;
+
+ buffer = get_buffer (text_view);
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
+ gtk_text_buffer_get_insert (buffer));
+ gtk_text_buffer_get_iter_at_mark (buffer, &bound,
+ gtk_text_buffer_get_selection_bound (buffer));
+
+ if (mode == GTK_TEXT_HANDLE_MODE_SELECTION &&
+ gtk_text_iter_compare (&cursor, &bound) == 0)
+ {
+ mode = GTK_TEXT_HANDLE_MODE_CURSOR;
+ }
+
+ if (mode == GTK_TEXT_HANDLE_MODE_CURSOR &&
+ (!gtk_widget_is_sensitive (GTK_WIDGET (text_view)) || !priv->cursor_visible))
+ {
+ mode = GTK_TEXT_HANDLE_MODE_NONE;
+ }
+
+ _gtk_text_handle_set_mode (priv->text_handle, mode);
+
+ if (gtk_text_iter_compare (&cursor, &bound) >= 0)
+ {
+ min = bound;
+ max = cursor;
+ }
+ else
+ {
+ min = cursor;
+ max = bound;
+ }
+
+ if (mode != GTK_TEXT_HANDLE_MODE_NONE)
+ gtk_text_view_set_handle_position (text_view, &max,
+ GTK_TEXT_HANDLE_POSITION_SELECTION_END);
+
+ if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
+ gtk_text_view_set_handle_position (text_view, &min,
+ GTK_TEXT_HANDLE_POSITION_SELECTION_START);
+}
+
static gint
gtk_text_view_event (GtkWidget *widget, GdkEvent *event)
{
gtk_text_view_reset_blink_time (text_view);
gtk_text_view_pend_cursor_blink (text_view);
+ _gtk_text_handle_set_mode (priv->text_handle,
+ GTK_TEXT_HANDLE_MODE_NONE);
+
return retval;
}
{
GtkTextView *text_view;
GtkTextViewPrivate *priv;
+ GdkDevice *device;
+ gboolean is_touchscreen;
text_view = GTK_TEXT_VIEW (widget);
priv = text_view->priv;
#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
+ device = gdk_event_get_source_device ((GdkEvent *) event);
+ is_touchscreen = test_touchscreen ||
+ gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN;
+
if (event->type == GDK_BUTTON_PRESS)
{
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.
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;
}
else
{
+ GtkTextHandleMode mode;
+
gtk_text_view_start_selection_drag (text_view, &iter, event);
+
+ if (gtk_widget_is_sensitive (widget) && is_touchscreen)
+ mode = GTK_TEXT_HANDLE_MODE_CURSOR;
+ else
+ mode = GTK_TEXT_HANDLE_MODE_NONE;
+
+ gtk_text_view_update_handles (text_view, mode);
}
return TRUE;
}
- else if (event->button == 2)
+ else if (event->button == GDK_BUTTON_MIDDLE &&
+ get_middle_click_paste (text_view))
{
GtkTextIter iter;
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;
+ GtkTextHandleMode mode;
gtk_text_view_end_selection_drag (text_view);
&iter,
event->x + priv->xoffset,
event->y + priv->yoffset);
-
+
gtk_text_view_start_selection_drag (text_view, &iter, event);
+
+ if (gtk_widget_is_sensitive (widget) && is_touchscreen)
+ mode = GTK_TEXT_HANDLE_MODE_SELECTION;
+ else
+ mode = GTK_TEXT_HANDLE_MODE_NONE;
+
+ gtk_text_view_update_handles (text_view, mode);
return TRUE;
}
-
+
return FALSE;
}
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)
{
return TRUE;
else if (priv->pending_place_cursor_button == event->button)
{
+ GtkTextHandleMode mode;
+ GdkDevice *device;
GtkTextIter iter;
/* Unselect everything; we clicked inside selection, but
gtk_text_buffer_place_cursor (get_buffer (text_view), &iter);
gtk_text_view_check_cursor_blink (text_view);
-
+
+ device = gdk_event_get_source_device ((GdkEvent *) event);
+
+ if (gtk_widget_is_sensitive (widget) &&
+ (test_touchscreen ||
+ gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN))
+ mode = GTK_TEXT_HANDLE_MODE_CURSOR;
+ else
+ mode = GTK_TEXT_HANDLE_MODE_NONE;
+
+ gtk_text_view_update_handles (text_view, mode);
priv->pending_place_cursor_button = 0;
-
+
return FALSE;
}
}
g_signal_handlers_disconnect_by_func (gdk_keymap_get_for_display (gtk_widget_get_display (widget)),
keymap_direction_changed,
text_view);
+ _gtk_text_handle_set_mode (priv->text_handle,
+ GTK_TEXT_HANDLE_MODE_NONE);
if (priv->editable)
{
{
GtkTextView *text_view;
GtkTextViewPrivate *priv;
- GList *child_exposes;
- GList *tmp_list;
text_view = GTK_TEXT_VIEW (widget);
priv = text_view->priv;
area->width, area->height);
#endif
- child_exposes = NULL;
-
cairo_save (cr);
cairo_translate (cr, -priv->xoffset, -priv->yoffset);
gtk_text_layout_draw (priv->layout,
widget,
cr,
- &child_exposes);
+ NULL);
cairo_restore (cr);
-
- tmp_list = child_exposes;
- while (tmp_list != NULL)
- {
- GtkWidget *child = tmp_list->data;
-
- gtk_container_propagate_draw (GTK_CONTAINER (text_view),
- child,
- cr);
-
- g_object_unref (child);
-
- tmp_list = tmp_list->next;
- }
-
- g_list_free (child_exposes);
}
static gboolean
/* propagate_draw checks that event->window matches
* child->window
*/
- if (!vc->anchor)
- gtk_container_propagate_draw (GTK_CONTAINER (widget),
- vc->widget,
- cr);
+ gtk_container_propagate_draw (GTK_CONTAINER (widget),
+ vc->widget,
+ cr);
tmp_list = tmp_list->next;
}
"interior-focus", &interior_focus,
NULL);
- if (gtk_widget_has_focus (widget) && !interior_focus)
+ if (gtk_widget_has_visible_focus (widget) && !interior_focus)
{
GtkStyleContext *context;
{
GtkContainer *container;
gboolean result;
-
- container = GTK_CONTAINER (widget);
+
+ container = GTK_CONTAINER (widget);
if (!gtk_widget_is_focus (widget) &&
gtk_container_get_focus_child (container) == NULL)
{
- gtk_widget_grab_focus (widget);
- return TRUE;
+ if (gtk_widget_get_can_focus (widget))
+ {
+ gtk_widget_grab_focus (widget);
+ return TRUE;
+ }
+
+ return FALSE;
}
else
{
+ gboolean can_focus;
/*
* Unset CAN_FOCUS flag so that gtk_container_focus() allows
* children to get the focus
*/
+ can_focus = gtk_widget_get_can_focus (widget);
gtk_widget_set_can_focus (widget, FALSE);
result = GTK_WIDGET_CLASS (gtk_text_view_parent_class)->focus (widget, direction);
- gtk_widget_set_can_focus (widget, TRUE);
+ gtk_widget_set_can_focus (widget, can_focus);
return result;
}
return FALSE;
}
+static gboolean
+get_middle_click_paste (GtkTextView *text_view)
+{
+ GtkSettings *settings;
+ gboolean paste;
+
+ settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
+ g_object_get (settings, "gtk-enable-primary-paste", &paste, NULL);
+
+ return paste;
+}
+
static gint
get_cursor_time (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))
priv->scroll_after_paste = TRUE;
}
+static void
+gtk_text_view_buffer_changed_handler (GtkTextBuffer *buffer,
+ gpointer data)
+{
+ GtkTextView *text_view = data;
+ gtk_text_view_update_handles (text_view, GTK_TEXT_HANDLE_MODE_NONE);
+}
+
static void
gtk_text_view_toggle_overwrite (GtkTextView *text_view)
{
}
static void
-move_mark_to_pointer_and_scroll (GtkTextView *text_view,
- const gchar *mark_name,
- GdkDevice *device)
+move_mark_to_pointer_and_scroll (GtkTextView *text_view,
+ const gchar *mark_name,
+ GdkDevice *device,
+ GdkInputSource source)
{
GtkTextIter newplace;
+ GtkTextBuffer *buffer;
GtkTextMark *mark;
+ buffer = get_buffer (text_view);
get_iter_at_pointer (text_view, device, &newplace, NULL, NULL);
-
- mark = gtk_text_buffer_get_mark (get_buffer (text_view), mark_name);
-
+
+ mark = gtk_text_buffer_get_mark (buffer, mark_name);
+
/* This may invalidate the layout */
DV(g_print (G_STRLOC": move mark\n"));
-
- gtk_text_buffer_move_mark (get_buffer (text_view),
- mark,
- &newplace);
-
+
+ gtk_text_buffer_move_mark (buffer, mark, &newplace);
+
DV(g_print (G_STRLOC": scrolling onscreen\n"));
gtk_text_view_scroll_mark_onscreen (text_view, mark);
SelectionData *data)
{
GtkTextViewPrivate *priv;
+ GdkInputSource input_source;
+ GdkDevice *device;
priv = text_view->priv;
gdk_event_request_motions (event);
+ device = gdk_event_get_source_device ((GdkEvent *) event);
+ input_source = gdk_device_get_source (device);
+
if (priv->grab_device != event->device)
return FALSE;
if (data->granularity == SELECT_CHARACTERS)
{
- move_mark_to_pointer_and_scroll (text_view, "insert", event->device);
+ move_mark_to_pointer_and_scroll (text_view, "insert",
+ event->device, input_source);
}
else
{
else
gtk_text_buffer_select_range (buffer, &end, &orig_start);
- gtk_text_view_scroll_mark_onscreen (text_view,
+ gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_insert (buffer));
}
text_view->priv->scroll_timeout =
gdk_threads_add_timeout (50, selection_scan_timeout, text_view);
+ if (test_touchscreen || input_source == GDK_SOURCE_TOUCHSCREEN)
+ gtk_text_view_update_handles (text_view, GTK_TEXT_HANDLE_MODE_SELECTION);
+
return TRUE;
}
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;
if (values->font)
pango_font_description_free (values->font);
- values->font = pango_font_description_copy (gtk_style_context_get_font (context, state));
+ gtk_style_context_get (context, state, "font", &values->font, NULL);
gtk_style_context_restore (context);
}
* 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))
adjust_allocation_recurse (widget, &scroll_data);
}
-
+
static void
gtk_text_view_value_changed (GtkAdjustment *adjustment,
GtkTextView *text_view)
*/
gtk_text_view_validate_onscreen (text_view);
- /* process exposes */
- if (gtk_widget_get_realized (GTK_WIDGET (text_view)))
- {
- DV (g_print ("Processing updates (%s)\n", G_STRLOC));
-
- if (priv->left_window)
- gdk_window_process_updates (priv->left_window->bin_window, TRUE);
-
- if (priv->right_window)
- gdk_window_process_updates (priv->right_window->bin_window, TRUE);
-
- if (priv->top_window)
- gdk_window_process_updates (priv->top_window->bin_window, TRUE);
-
- if (priv->bottom_window)
- gdk_window_process_updates (priv->bottom_window->bin_window, TRUE);
-
- gdk_window_process_updates (priv->text_window->bin_window, TRUE);
- }
-
/* If this got installed, get rid of it, it's just a waste of time. */
if (priv->first_validate_idle != 0)
{
* changes made by the validation are pushed through.
*/
gtk_text_view_update_im_spot_location (text_view);
-
+
+ gtk_text_view_update_handles (text_view,
+ _gtk_text_handle_get_mode (priv->text_handle));
+
DV(g_print(">End scroll offset changed handler ("G_STRLOC")\n"));
}
}
if (need_reset)
- gtk_text_view_reset_im_context (text_view);
+ {
+ gtk_text_view_reset_im_context (text_view);
+ gtk_text_view_update_handles (text_view,
+ _gtk_text_handle_get_mode (text_view->priv->text_handle));
+ }
}
static void
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));
GtkTextView *text_view;
gint button;
guint time;
+ GdkDevice *device;
} PopupInfo;
static gboolean
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
{
{
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),
&attributes, attributes_mask);
gdk_window_show (win->window);
- gdk_window_set_user_data (win->window, win->widget);
+ gtk_widget_register_window (win->widget, win->window);
gdk_window_lower (win->window);
attributes.x = 0;
attributes.height = win->allocation.height;
attributes.event_mask = (GDK_EXPOSURE_MASK |
GDK_SCROLL_MASK |
+ GDK_SMOOTH_SCROLL_MASK |
GDK_KEY_PRESS_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
attributes_mask);
gdk_window_show (win->bin_window);
- gdk_window_set_user_data (win->bin_window, win->widget);
+ gtk_widget_register_window (win->widget, win->bin_window);
context = gtk_widget_get_style_context (widget);
state = gtk_widget_get_state_flags (widget);
NULL);
}
- gdk_window_set_user_data (win->window, NULL);
- gdk_window_set_user_data (win->bin_window, NULL);
+ gtk_widget_unregister_window (win->widget, win->window);
+ gtk_widget_unregister_window (win->widget, win->bin_window);
gdk_window_destroy (win->bin_window);
gdk_window_destroy (win->window);
win->window = NULL;
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
*/
return gtk_text_layout_move_iter_visually (text_view->priv->layout, iter, count);
}
+
+/**
+ * gtk_text_view_set_input_purpose:
+ * @text_view: a #GtkTextView
+ * @purpose: the purpose
+ *
+ * Sets the #GtkTextView:input-purpose property which
+ * can be used by on-screen keyboards and other input
+ * methods to adjust their behaviour.
+ *
+ * Since: 3.6
+ */
+
+void
+gtk_text_view_set_input_purpose (GtkTextView *text_view,
+ GtkInputPurpose purpose)
+
+{
+ g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
+
+ if (gtk_text_view_get_input_purpose (text_view) != purpose)
+ {
+ g_object_set (G_OBJECT (text_view->priv->im_context),
+ "input-purpose", purpose,
+ NULL);
+
+ g_object_notify (G_OBJECT (text_view), "input-purpose");
+ }
+}
+
+/**
+ * gtk_text_view_get_input_purpose:
+ * @text_view: a #GtkTextView
+ *
+ * Gets the value of the #GtkTextView:input-purpose property.
+ *
+ * Since: 3.6
+ */
+
+GtkInputPurpose
+gtk_text_view_get_input_purpose (GtkTextView *text_view)
+{
+ GtkInputPurpose purpose;
+
+ g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_INPUT_PURPOSE_FREE_FORM);
+
+ g_object_get (G_OBJECT (text_view->priv->im_context),
+ "input-purpose", &purpose,
+ NULL);
+
+ return purpose;
+}
+
+/**
+ * gtk_text_view_set_input_hints:
+ * @text_view: a #GtkTextView
+ * @hints: the hints
+ *
+ * Sets the #GtkTextView:input-hints property, which
+ * allows input methods to fine-tune their behaviour.
+ *
+ * Since: 3.6
+ */
+
+void
+gtk_text_view_set_input_hints (GtkTextView *text_view,
+ GtkInputHints hints)
+
+{
+ g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
+
+ if (gtk_text_view_get_input_hints (text_view) != hints)
+ {
+ g_object_set (G_OBJECT (text_view->priv->im_context),
+ "input-hints", hints,
+ NULL);
+
+ g_object_notify (G_OBJECT (text_view), "input-hints");
+ }
+}
+
+/**
+ * gtk_text_view_get_input_hints:
+ * @text_view: a #GtkTextView
+ *
+ * Gets the value of the #GtkTextView:input-hints property.
+ *
+ * Since: 3.6
+ */
+
+GtkInputHints
+gtk_text_view_get_input_hints (GtkTextView *text_view)
+{
+ GtkInputHints hints;
+
+ g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_INPUT_HINT_NONE);
+
+ g_object_get (G_OBJECT (text_view->priv->im_context),
+ "input-hints", &hints,
+ NULL);
+
+ return hints;
+}