#include "gtkimmulticontext.h"
#include "gdk/gdkkeysyms.h"
#include "gtkprivate.h"
-#include "gtksizegroup.h" /* FIXME http://bugzilla.gnome.org/show_bug.cgi?id=72258 */
#include "gtktextutil.h"
#include "gtkwindow.h"
#include "gtkalias.h"
#define SPACE_FOR_CURSOR 1
+typedef struct _GtkTextViewPrivate GtkTextViewPrivate;
+
+#define GTK_TEXT_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_TEXT_VIEW, GtkTextViewPrivate))
+
+struct _GtkTextViewPrivate
+{
+ guint blink_time; /* time in msec the cursor has blinked since last user event */
+};
+
+
struct _GtkTextPendingScroll
{
GtkTextMark *mark;
GtkScrollStep step,
gint count);
static void gtk_text_view_set_anchor (GtkTextView *text_view);
-static void gtk_text_view_scroll_pages (GtkTextView *text_view,
+static gboolean gtk_text_view_scroll_pages (GtkTextView *text_view,
gint count,
gboolean extend_selection);
-static void gtk_text_view_scroll_hpages (GtkTextView *text_view,
+static gboolean gtk_text_view_scroll_hpages(GtkTextView *text_view,
gint count,
gboolean extend_selection);
static void gtk_text_view_insert_at_cursor (GtkTextView *text_view,
static void gtk_text_view_check_cursor_blink (GtkTextView *text_view);
static void gtk_text_view_pend_cursor_blink (GtkTextView *text_view);
static void gtk_text_view_stop_cursor_blink (GtkTextView *text_view);
+static void gtk_text_view_reset_blink_time (GtkTextView *text_view);
static void gtk_text_view_value_changed (GtkAdjustment *adj,
GtkTextView *view);
GTK_MOVEMENT_WORDS, -1);
add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
- GTK_MOVEMENT_WORDS, 1);
+ GTK_MOVEMENT_WORDS, -1);
add_move_binding (binding_set, GDK_Up, 0,
GTK_MOVEMENT_DISPLAY_LINES, -1);
gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
"move_focus", 1,
GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
+
+ g_type_class_add_private (gobject_class, sizeof (GtkTextViewPrivate));
}
static void
*/
gtk_text_view_flush_first_validate (text_view);
+ /* widget->window doesn't get auto-redrawn as the layout is computed, so has to
+ * be invalidated
+ */
if (size_changed && GTK_WIDGET_REALIZED (widget))
- {
- GtkTextBuffer *buffer;
-
- /* widget->window doesn't get auto-redrawn as the layout is
- * computed, so has to be invalidated
- */
- gdk_window_invalidate_rect (widget->window, NULL, FALSE);
-
- /* keep cursor visible */
- buffer = get_buffer (text_view);
- gtk_text_view_scroll_to_mark (text_view,
- gtk_text_buffer_get_mark (buffer, "insert"),
- 0.0, FALSE, 0.0, 0.0);
- }
+ gdk_window_invalidate_rect (widget->window, NULL, FALSE);
}
static void
{
GtkTextView *text_view = data;
- GDK_THREADS_ENTER ();
-
/* Note that some of this code is duplicated at the end of size_allocate,
* keep in sync with that.
*/
gtk_text_view_flush_first_validate (text_view);
- GDK_THREADS_LEAVE ();
-
return FALSE;
}
GtkTextView *text_view = data;
gboolean result = TRUE;
- GDK_THREADS_ENTER ();
-
DV(g_print(G_STRLOC"\n"));
gtk_text_layout_validate (text_view->layout, 2000);
result = FALSE;
}
- GDK_THREADS_LEAVE ();
-
return result;
}
if (!text_view->first_validate_idle)
{
- text_view->first_validate_idle = g_idle_add_full (GTK_PRIORITY_RESIZE - 2, first_validate_callback, text_view, NULL);
+ text_view->first_validate_idle = gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, first_validate_callback, text_view, NULL);
DV (g_print (G_STRLOC": adding first validate idle %d\n",
text_view->first_validate_idle));
}
if (!text_view->incremental_validate_idle)
{
- text_view->incremental_validate_idle = g_idle_add_full (GTK_TEXT_VIEW_PRIORITY_VALIDATE, incremental_validate_callback, text_view, NULL);
+ text_view->incremental_validate_idle = gdk_threads_add_idle_full (GTK_TEXT_VIEW_PRIORITY_VALIDATE, incremental_validate_callback, text_view, NULL);
DV (g_print (G_STRLOC": adding incremental validate idle %d\n",
text_view->incremental_validate_idle));
}
GtkStyle *previous_style)
{
GtkTextView *text_view = GTK_TEXT_VIEW (widget);
+ PangoContext *ltr_context, *rtl_context;
if (GTK_WIDGET_REALIZED (widget))
{
gtk_text_view_set_attributes_from_style (text_view,
text_view->layout->default_style,
widget->style);
- gtk_text_layout_default_style_changed (text_view->layout);
+
+
+ ltr_context = gtk_widget_create_pango_context (widget);
+ pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR);
+ rtl_context = gtk_widget_create_pango_context (widget);
+ pango_context_set_base_dir (rtl_context, PANGO_DIRECTION_RTL);
+
+ gtk_text_layout_set_contexts (text_view->layout, ltr_context, rtl_context);
+
+ g_object_unref (ltr_context);
+ g_object_unref (rtl_context);
}
}
if (text_view->layout)
{
text_view->layout->default_style->direction = gtk_widget_get_direction (widget);
+
gtk_text_layout_default_style_changed (text_view->layout);
}
}
if (obscure)
gtk_text_view_obscure_mouse_cursor (text_view);
+ gtk_text_view_reset_blink_time (text_view);
gtk_text_view_pend_cursor_blink (text_view);
+ if (!retval && event->length)
+ gtk_widget_error_bell (widget);
+
return retval;
}
return FALSE;
}
+ gtk_text_view_reset_blink_time (text_view);
+
#if 0
/* debug hack */
if (event->button == 3 && (event->state & GDK_CONTROL_MASK) != 0)
gtk_widget_queue_draw (widget);
DV(g_print (G_STRLOC": focus_in_event\n"));
-
+
+ gtk_text_view_reset_blink_time (text_view);
+
if (text_view->cursor_visible && text_view->layout)
{
gtk_text_layout_set_cursor_visible (text_view->layout, TRUE);
g_slist_free (copy);
}
-#define CURSOR_ON_MULTIPLIER 0.66
-#define CURSOR_OFF_MULTIPLIER 0.34
-#define CURSOR_PEND_MULTIPLIER 1.0
+#define CURSOR_ON_MULTIPLIER 2
+#define CURSOR_OFF_MULTIPLIER 1
+#define CURSOR_PEND_MULTIPLIER 3
+#define CURSOR_DIVIDER 3
static gboolean
cursor_blinks (GtkTextView *text_view)
return time;
}
+static gint
+get_cursor_blink_timeout (GtkTextView *text_view)
+{
+ GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
+ gint time;
+
+ g_object_get (settings, "gtk-cursor-blink-timeout", &time, NULL);
+
+ return time;
+}
+
+
/*
* Blink!
*/
blink_cb (gpointer data)
{
GtkTextView *text_view;
+ GtkTextViewPrivate *priv;
gboolean visible;
-
- GDK_THREADS_ENTER ();
+ gint blink_timeout;
text_view = GTK_TEXT_VIEW (data);
-
+ priv = GTK_TEXT_VIEW_GET_PRIVATE (text_view);
+
if (!GTK_WIDGET_HAS_FOCUS (text_view))
{
g_warning ("GtkTextView - did not receive focus-out-event. If you\n"
"connect a handler to this signal, it must return\n"
- "FALSE so the entry gets the event as well");
+ "FALSE so the text view gets the event as well");
}
g_assert (text_view->layout);
visible = gtk_text_layout_get_cursor_visible (text_view->layout);
- if (visible)
- text_view->blink_timeout = g_timeout_add (get_cursor_time (text_view) * CURSOR_OFF_MULTIPLIER,
- blink_cb,
- text_view);
- else
- text_view->blink_timeout = g_timeout_add (get_cursor_time (text_view) * CURSOR_ON_MULTIPLIER,
+ blink_timeout = get_cursor_blink_timeout (text_view);
+ if (priv->blink_time > 1000 * blink_timeout &&
+ blink_timeout < G_MAXINT/1000)
+ {
+ /* we've blinked enough without the user doing anything, stop blinking */
+ visible = 0;
+ text_view->blink_timeout = 0;
+ }
+ else if (visible)
+ text_view->blink_timeout = gdk_threads_add_timeout (get_cursor_time (text_view) * CURSOR_OFF_MULTIPLIER / CURSOR_DIVIDER,
blink_cb,
text_view);
+ else
+ {
+ text_view->blink_timeout = gdk_threads_add_timeout (get_cursor_time (text_view) * CURSOR_ON_MULTIPLIER / CURSOR_DIVIDER,
+ blink_cb,
+ text_view);
+ priv->blink_time += get_cursor_time (text_view);
+ }
/* Block changed_handler while changing the layout's cursor visibility
* because it would expose the whole paragraph. Instead, we expose
text_window_invalidate_cursors (text_view->text_window);
- GDK_THREADS_LEAVE ();
-
/* Remove ourselves */
return FALSE;
}
{
gtk_text_layout_set_cursor_visible (text_view->layout, TRUE);
- text_view->blink_timeout = g_timeout_add (get_cursor_time (text_view) * CURSOR_OFF_MULTIPLIER,
+ text_view->blink_timeout = gdk_threads_add_timeout (get_cursor_time (text_view) * CURSOR_OFF_MULTIPLIER / CURSOR_DIVIDER,
blink_cb,
text_view);
}
gtk_text_view_stop_cursor_blink (text_view);
gtk_text_layout_set_cursor_visible (text_view->layout, TRUE);
- text_view->blink_timeout = g_timeout_add (get_cursor_time (text_view) * CURSOR_PEND_MULTIPLIER,
+ text_view->blink_timeout = gdk_threads_add_timeout (get_cursor_time (text_view) * CURSOR_PEND_MULTIPLIER / CURSOR_DIVIDER,
blink_cb,
text_view);
}
}
+static void
+gtk_text_view_reset_blink_time (GtkTextView *text_view)
+{
+ GtkTextViewPrivate *priv;
+
+ priv = GTK_TEXT_VIEW_GET_PRIVATE (text_view);
+
+ priv->blink_time = 0;
+}
+
/*
* Key binding handlers
{
GtkTextIter insert;
GtkTextIter newplace;
-
gint cursor_x_pos = 0;
+ GtkDirectionType leave_direction = -1;
if (!text_view->cursor_visible)
{
if (step == GTK_MOVEMENT_PAGES)
{
- gtk_text_view_scroll_pages (text_view, count, extend_selection);
+ if (!gtk_text_view_scroll_pages (text_view, count, extend_selection))
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+
gtk_text_view_check_cursor_blink (text_view);
gtk_text_view_pend_cursor_blink (text_view);
return;
}
else if (step == GTK_MOVEMENT_HORIZONTAL_PAGES)
{
- gtk_text_view_scroll_hpages (text_view, count, extend_selection);
+ if (!gtk_text_view_scroll_hpages (text_view, count, extend_selection))
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+
gtk_text_view_check_cursor_blink (text_view);
gtk_text_view_pend_cursor_blink (text_view);
return;
case GTK_MOVEMENT_DISPLAY_LINES:
if (count < 0)
- {
- if (gtk_text_view_move_iter_by_lines (text_view, &newplace, count))
- gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
- else
- gtk_text_iter_set_line_offset (&newplace, 0);
- }
+ {
+ leave_direction = GTK_DIR_UP;
+
+ if (gtk_text_view_move_iter_by_lines (text_view, &newplace, count))
+ gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
+ else
+ gtk_text_iter_set_line_offset (&newplace, 0);
+ }
if (count > 0)
- {
- if (gtk_text_view_move_iter_by_lines (text_view, &newplace, count))
- gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
- else
- gtk_text_iter_forward_to_line_end (&newplace);
- }
+ {
+ leave_direction = GTK_DIR_DOWN;
+
+ if (gtk_text_view_move_iter_by_lines (text_view, &newplace, count))
+ gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
+ else
+ gtk_text_iter_forward_to_line_end (&newplace);
+ }
break;
case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
if (step == GTK_MOVEMENT_DISPLAY_LINES)
gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, -1);
}
+ else if (leave_direction != -1)
+ {
+ if (!gtk_widget_keynav_failed (GTK_WIDGET (text_view),
+ leave_direction))
+ {
+ gtk_text_view_move_focus (text_view, leave_direction);
+ }
+ }
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+ }
gtk_text_view_check_cursor_blink (text_view);
gtk_text_view_pend_cursor_blink (text_view);
gtk_text_buffer_create_mark (get_buffer (text_view), "anchor", &insert, TRUE);
}
-static void
+static gboolean
gtk_text_view_scroll_pages (GtkTextView *text_view,
gint count,
gboolean extend_selection)
gdouble oldval;
GtkAdjustment *adj;
gint cursor_x_pos, cursor_y_pos;
+ GtkTextIter old_insert;
GtkTextIter new_insert;
GtkTextIter anchor;
gint y0, y1;
- g_return_if_fail (text_view->vadjustment != NULL);
+ g_return_val_if_fail (text_view->vadjustment != NULL, FALSE);
adj = text_view->vadjustment;
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_mark (get_buffer (text_view),
"insert"));
-
-/* Validate the region that will be brought into view by the cursor motion
+
+ /* Validate the region that will be brought into view by the cursor motion
*/
+ gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
+ &old_insert,
+ gtk_text_buffer_get_mark (get_buffer (text_view), "insert"));
+
if (count < 0)
{
gtk_text_view_get_first_para_iter (text_view, &anchor);
gtk_text_layout_validate_yrange (text_view->layout, &anchor, y0, y1);
/* FIXME do we need to update the adjustment ranges here? */
+ new_insert = old_insert;
+
if (count < 0 && adj->value <= (adj->lower + 1e-12))
{
/* already at top, just be sure we are at offset 0 */
{
gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos);
- newval = adj->value;
oldval = adj->value;
-
+ newval = adj->value;
+
newval += count * adj->page_increment;
set_adjustment_clamped (adj, newval);
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_mark (get_buffer (text_view),
"insert"));
+
+ return !gtk_text_iter_equal (&old_insert, &new_insert);
}
-static void
+static gboolean
gtk_text_view_scroll_hpages (GtkTextView *text_view,
gint count,
gboolean extend_selection)
gdouble oldval;
GtkAdjustment *adj;
gint cursor_x_pos, cursor_y_pos;
+ GtkTextIter old_insert;
GtkTextIter new_insert;
gint y, height;
- g_return_if_fail (text_view->hadjustment != NULL);
+ g_return_val_if_fail (text_view->hadjustment != NULL, FALSE);
adj = text_view->hadjustment;
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_mark (get_buffer (text_view),
"insert"));
-
+
/* Validate the line that we're moving within.
*/
gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
- &new_insert,
+ &old_insert,
gtk_text_buffer_get_mark (get_buffer (text_view), "insert"));
gtk_text_layout_get_line_yrange (text_view->layout, &new_insert, &y, &height);
gtk_text_layout_validate_yrange (text_view->layout, &new_insert, y, y + height);
/* FIXME do we need to update the adjustment ranges here? */
-
+
+ new_insert = old_insert;
+
if (count < 0 && adj->value <= (adj->lower + 1e-12))
{
/* already at far left, just be sure we are at offset 0 */
{
gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos);
- newval = adj->value;
oldval = adj->value;
-
+ newval = adj->value;
+
newval += count * adj->page_increment;
set_adjustment_clamped (adj, newval);
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_mark (get_buffer (text_view),
"insert"));
+
+ return !gtk_text_iter_equal (&old_insert, &new_insert);
}
static gboolean
gtk_text_view_insert_at_cursor (GtkTextView *text_view,
const gchar *str)
{
- gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
- text_view->editable);
+ if (!gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
+ text_view->editable))
+ {
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+ }
}
static void
" ", 1,
text_view->editable);
}
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+ }
gtk_text_buffer_end_user_action (get_buffer (text_view));
gtk_text_view_set_virtual_cursor_pos (text_view, -1, -1);
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_mark (get_buffer (text_view), "insert"));
}
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+ }
}
static void
gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
&insert,
- gtk_text_buffer_get_mark (get_buffer (text_view),
- "insert"));
+ gtk_text_buffer_get_insert (get_buffer (text_view)));
if (gtk_text_buffer_backspace (get_buffer (text_view), &insert,
TRUE, text_view->editable))
gtk_text_view_set_virtual_cursor_pos (text_view, -1, -1);
DV(g_print (G_STRLOC": scrolling onscreen\n"));
gtk_text_view_scroll_mark_onscreen (text_view,
- gtk_text_buffer_get_mark (get_buffer (text_view), "insert"));
+ gtk_text_buffer_get_insert (get_buffer (text_view)));
+ }
+ else
+ {
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
}
}
text_view->editable);
DV(g_print (G_STRLOC": scrolling onscreen\n"));
gtk_text_view_scroll_mark_onscreen (text_view,
- gtk_text_buffer_get_mark (get_buffer (text_view),
- "insert"));
+ gtk_text_buffer_get_insert (get_buffer (text_view)));
}
static void
gtk_text_buffer_copy_clipboard (get_buffer (text_view),
clipboard);
- DV(g_print (G_STRLOC": scrolling onscreen\n"));
- gtk_text_view_scroll_mark_onscreen (text_view,
- gtk_text_buffer_get_mark (get_buffer (text_view),
- "insert"));
+
+ /* on copy do not scroll, we are already onscreen */
}
static void
text_view->editable);
DV(g_print (G_STRLOC": scrolling onscreen\n"));
gtk_text_view_scroll_mark_onscreen (text_view,
- gtk_text_buffer_get_mark (get_buffer (text_view),
- "insert"));
+ gtk_text_buffer_get_insert (get_buffer (text_view)));
}
static void
G_STRLOC, text_view->first_validate_idle));
}
-static gint
+static gboolean
selection_scan_timeout (gpointer data)
{
GtkTextView *text_view;
- GDK_THREADS_ENTER ();
-
text_view = GTK_TEXT_VIEW (data);
DV(g_print (G_STRLOC": calling move_mark_to_pointer_and_scroll\n"));
gtk_text_buffer_get_mark (get_buffer (text_view),
"insert"));
- GDK_THREADS_LEAVE ();
-
return TRUE; /* remain installed. */
}
GtkTextView *text_view;
GtkTextIter newplace;
- GDK_THREADS_ENTER ();
-
text_view = GTK_TEXT_VIEW (data);
get_iter_at_pointer (text_view, &newplace);
text_view->dnd_mark,
DND_SCROLL_MARGIN, FALSE, 0.0, 0.0);
- GDK_THREADS_LEAVE ();
-
return TRUE;
}
g_source_remove (text_view->scroll_timeout);
text_view->scroll_timeout =
- g_timeout_add (50, selection_scan_timeout, text_view);
+ gdk_threads_add_timeout (50, selection_scan_timeout, text_view);
return TRUE;
}
GtkTextBuffer *buffer;
SelectionData *data;
- g_assert (text_view->selection_drag_handler == 0);
+ if (text_view->selection_drag_handler != 0)
+ return;
data = g_new0 (SelectionData, 1);
target_list = gtk_text_buffer_get_copy_target_list (get_buffer (text_view));
- g_signal_connect (text_view, "drag-begin",
+ g_signal_connect (text_view, "drag_begin",
G_CALLBACK (drag_begin_cb), NULL);
gtk_drag_begin (GTK_WIDGET (text_view), target_list,
GDK_ACTION_COPY | GDK_ACTION_MOVE,
g_source_remove (text_view->scroll_timeout);
text_view->scroll_timeout =
- g_timeout_add (50, drag_scan_timeout, text_view);
+ gdk_threads_add_timeout (50, drag_scan_timeout, text_view);
/* TRUE return means don't propagate the drag motion to parent
* widgets that may also be drop sites.
if (str)
{
- gtk_text_buffer_insert_interactive (get_buffer (text_view),
- drop_point, (gchar *) str, -1,
- text_view->editable);
+ if (!gtk_text_buffer_insert_interactive (get_buffer (text_view),
+ drop_point, (gchar *) str, -1,
+ text_view->editable))
+ {
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+ }
+
g_free (str);
}
}
if (!strcmp (str, "\n"))
{
- gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), "\n", 1,
- text_view->editable);
+ if (!gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), "\n", 1,
+ text_view->editable))
+ {
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+ }
}
else
{
if (!gtk_text_iter_ends_line (&insert))
gtk_text_view_delete_from_cursor (text_view, GTK_DELETE_CHARS, 1);
}
- gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
- text_view->editable);
+
+ if (!gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
+ text_view->editable))
+ {
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
+ }
}
gtk_text_buffer_end_user_action (get_buffer (text_view));