1 /* gtktext.c - A "view" widget for the GtkTextBuffer object
3 * Copyright (c) 1992-1994 The Regents of the University of California.
4 * Copyright (c) 1994-1996 Sun Microsystems, Inc.
5 * Copyright (c) 1999 by Scriptics Corporation.
6 * Copyright (c) 2000 Red Hat, Inc.
7 * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
9 * This software is copyrighted by the Regents of the University of
10 * California, Sun Microsystems, Inc., and other parties. The
11 * following terms apply to all files associated with the software
12 * unless explicitly disclaimed in individual files.
14 * The authors hereby grant permission to use, copy, modify,
15 * distribute, and license this software and its documentation for any
16 * purpose, provided that existing copyright notices are retained in
17 * all copies and that this notice is included verbatim in any
18 * distributions. No written agreement, license, or royalty fee is
19 * required for any of the authorized uses. Modifications to this
20 * software may be copyrighted by their authors and need not follow
21 * the licensing terms described here, provided that the new terms are
22 * clearly indicated on the first page of each file where they apply.
24 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
25 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
26 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
27 * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
28 * OF THE POSSIBILITY OF SUCH DAMAGE.
30 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
32 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
33 * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
34 * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
35 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
37 * GOVERNMENT USE: If you are acquiring this software on behalf of the
38 * U.S. government, the Government shall have only "Restricted Rights"
39 * in the software and related documentation as defined in the Federal
40 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
41 * are acquiring the software on behalf of the Department of Defense,
42 * the software shall be classified as "Commercial Computer Software"
43 * and the Government shall have only "Restricted Rights" as defined
44 * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
45 * foregoing, the authors grant the U.S. Government and others acting
46 * in its behalf permission to use and distribute the software in
47 * accordance with the terms specified in this license.
51 #include "gtkbindings.h"
54 #include "gtksignal.h"
56 #include "gtktextdisplay.h"
57 #include "gtktextview.h"
58 #include "gtkimmulticontext.h"
59 #include "gdk/gdkkeysyms.h"
70 SET_SCROLL_ADJUSTMENTS,
78 ARG_PIXELS_ABOVE_LINES,
79 ARG_PIXELS_BELOW_LINES,
80 ARG_PIXELS_INSIDE_WRAP,
86 static void gtk_text_view_init (GtkTextView *text_view);
87 static void gtk_text_view_class_init (GtkTextViewClass *klass);
88 static void gtk_text_view_destroy (GtkObject *object);
89 static void gtk_text_view_finalize (GObject *object);
90 static void gtk_text_view_set_arg (GtkObject *object,
93 static void gtk_text_view_get_arg (GtkObject *object,
96 static void gtk_text_view_size_request (GtkWidget *widget,
97 GtkRequisition *requisition);
98 static void gtk_text_view_size_allocate (GtkWidget *widget,
99 GtkAllocation *allocation);
100 static void gtk_text_view_realize (GtkWidget *widget);
101 static void gtk_text_view_unrealize (GtkWidget *widget);
102 static void gtk_text_view_style_set (GtkWidget *widget,
103 GtkStyle *previous_style);
104 static gint gtk_text_view_event (GtkWidget *widget,
106 static gint gtk_text_view_key_press_event (GtkWidget *widget,
108 static gint gtk_text_view_key_release_event (GtkWidget *widget,
110 static gint gtk_text_view_button_press_event (GtkWidget *widget,
111 GdkEventButton *event);
112 static gint gtk_text_view_button_release_event (GtkWidget *widget,
113 GdkEventButton *event);
114 static gint gtk_text_view_focus_in_event (GtkWidget *widget,
115 GdkEventFocus *event);
116 static gint gtk_text_view_focus_out_event (GtkWidget *widget,
117 GdkEventFocus *event);
118 static gint gtk_text_view_motion_event (GtkWidget *widget,
119 GdkEventMotion *event);
120 static void gtk_text_view_draw (GtkWidget *widget,
122 static gint gtk_text_view_expose_event (GtkWidget *widget,
123 GdkEventExpose *expose);
126 /* Source side drag signals */
127 static void gtk_text_view_drag_begin (GtkWidget *widget,
128 GdkDragContext *context);
129 static void gtk_text_view_drag_end (GtkWidget *widget,
130 GdkDragContext *context);
131 static void gtk_text_view_drag_data_get (GtkWidget *widget,
132 GdkDragContext *context,
133 GtkSelectionData *selection_data,
136 static void gtk_text_view_drag_data_delete (GtkWidget *widget,
137 GdkDragContext *context);
139 /* Target side drag signals */
140 static void gtk_text_view_drag_leave (GtkWidget *widget,
141 GdkDragContext *context,
143 static gboolean gtk_text_view_drag_motion (GtkWidget *widget,
144 GdkDragContext *context,
148 static gboolean gtk_text_view_drag_drop (GtkWidget *widget,
149 GdkDragContext *context,
153 static void gtk_text_view_drag_data_received (GtkWidget *widget,
154 GdkDragContext *context,
157 GtkSelectionData *selection_data,
161 static void gtk_text_view_set_scroll_adjustments (GtkTextView *text_view,
163 GtkAdjustment *vadj);
165 static void gtk_text_view_move_insert (GtkTextView *text_view,
166 GtkTextViewMovementStep step,
168 gboolean extend_selection);
169 static void gtk_text_view_set_anchor (GtkTextView *text_view);
170 static void gtk_text_view_scroll_text (GtkTextView *text_view,
171 GtkTextViewScrollType type);
172 static void gtk_text_view_delete_text (GtkTextView *text_view,
173 GtkTextViewDeleteType type,
175 static void gtk_text_view_cut_text (GtkTextView *text_view);
176 static void gtk_text_view_copy_text (GtkTextView *text_view);
177 static void gtk_text_view_paste_text (GtkTextView *text_view);
178 static void gtk_text_view_toggle_overwrite (GtkTextView *text_view);
181 static void gtk_text_view_validate_onscreen (GtkTextView *text_view);
182 static void gtk_text_view_get_first_para_iter (GtkTextView *text_view,
184 static void gtk_text_view_scroll_calc_now (GtkTextView *text_view);
185 static void gtk_text_view_set_values_from_style (GtkTextView *text_view,
186 GtkTextStyleValues *values,
188 static void gtk_text_view_ensure_layout (GtkTextView *text_view);
189 static void gtk_text_view_destroy_layout (GtkTextView *text_view);
190 static void gtk_text_view_start_selection_drag (GtkTextView *text_view,
191 const GtkTextIter *iter,
192 GdkEventButton *event);
193 static gboolean gtk_text_view_end_selection_drag (GtkTextView *text_view,
194 GdkEventButton *event);
195 static void gtk_text_view_start_selection_dnd (GtkTextView *text_view,
196 const GtkTextIter *iter,
197 GdkEventButton *event);
198 static void gtk_text_view_start_cursor_blink (GtkTextView *text_view);
199 static void gtk_text_view_stop_cursor_blink (GtkTextView *text_view);
201 static void gtk_text_view_value_changed (GtkAdjustment *adj,
203 static void gtk_text_view_commit_handler (GtkIMContext *context,
205 GtkTextView *text_view);
207 static void gtk_text_view_mark_set_handler (GtkTextBuffer *buffer,
208 const GtkTextIter *location,
209 const char *mark_name,
211 static void gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view,
214 static void gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view,
221 TARGET_COMPOUND_TEXT,
225 static GdkAtom clipboard_atom = GDK_NONE;
226 static GdkAtom text_atom = GDK_NONE;
227 static GdkAtom ctext_atom = GDK_NONE;
228 static GdkAtom utf8_atom = GDK_NONE;
231 static GtkTargetEntry target_table[] = {
232 { "UTF8_STRING", 0, TARGET_UTF8_STRING },
233 { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
234 { "TEXT", 0, TARGET_TEXT },
235 { "text/plain", 0, TARGET_STRING },
236 { "STRING", 0, TARGET_STRING }
239 static guint n_targets = sizeof (target_table) / sizeof (target_table[0]);
241 static GtkContainerClass *parent_class = NULL;
242 static guint signals[LAST_SIGNAL] = { 0 };
245 gtk_text_view_get_type (void)
247 static GtkType our_type = 0;
251 static const GtkTypeInfo our_info =
254 sizeof (GtkTextView),
255 sizeof (GtkTextViewClass),
256 (GtkClassInitFunc) gtk_text_view_class_init,
257 (GtkObjectInitFunc) gtk_text_view_init,
258 /* reserved_1 */ NULL,
259 /* reserved_2 */ NULL,
260 (GtkClassInitFunc) NULL
263 our_type = gtk_type_unique (GTK_TYPE_CONTAINER, &our_info);
270 add_move_insert_binding (GtkBindingSet *binding_set,
273 GtkTextViewMovementStep step,
276 g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
278 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
282 GTK_TYPE_BOOL, FALSE);
284 /* Selection-extending version */
285 gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
289 GTK_TYPE_BOOL, TRUE);
293 gtk_text_view_class_init (GtkTextViewClass *klass)
295 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
296 GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
297 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
298 GtkBindingSet *binding_set;
300 parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
305 gtk_object_add_arg_type ("GtkTextView::height_lines", GTK_TYPE_INT,
306 GTK_ARG_READWRITE, ARG_HEIGHT_LINES);
307 gtk_object_add_arg_type ("GtkTextView::width_columns", GTK_TYPE_INT,
308 GTK_ARG_READWRITE, ARG_WIDTH_COLUMNS);
309 gtk_object_add_arg_type ("GtkTextView::pixels_above_lines", GTK_TYPE_INT,
310 GTK_ARG_READWRITE, ARG_PIXELS_ABOVE_LINES);
311 gtk_object_add_arg_type ("GtkTextView::pixels_below_lines", GTK_TYPE_INT,
312 GTK_ARG_READWRITE, ARG_PIXELS_BELOW_LINES);
313 gtk_object_add_arg_type ("GtkTextView::pixels_inside_wrap", GTK_TYPE_INT,
314 GTK_ARG_READWRITE, ARG_PIXELS_INSIDE_WRAP);
315 gtk_object_add_arg_type ("GtkTextView::editable", GTK_TYPE_BOOL,
316 GTK_ARG_READWRITE, ARG_EDITABLE);
317 gtk_object_add_arg_type ("GtkTextView::wrap_mode", GTK_TYPE_ENUM,
318 GTK_ARG_READWRITE, ARG_WRAP_MODE);
325 signals[MOVE_INSERT] =
326 gtk_signal_new ("move_insert",
327 GTK_RUN_LAST | GTK_RUN_ACTION,
328 GTK_CLASS_TYPE (object_class),
329 GTK_SIGNAL_OFFSET (GtkTextViewClass, move_insert),
330 gtk_marshal_NONE__INT_INT_INT,
331 GTK_TYPE_NONE, 3, GTK_TYPE_ENUM, GTK_TYPE_INT, GTK_TYPE_BOOL);
333 signals[SET_ANCHOR] =
334 gtk_signal_new ("set_anchor",
335 GTK_RUN_LAST | GTK_RUN_ACTION,
336 GTK_CLASS_TYPE (object_class),
337 GTK_SIGNAL_OFFSET (GtkTextViewClass, set_anchor),
338 gtk_marshal_NONE__NONE,
341 signals[SCROLL_TEXT] =
342 gtk_signal_new ("scroll_text",
343 GTK_RUN_LAST | GTK_RUN_ACTION,
344 GTK_CLASS_TYPE (object_class),
345 GTK_SIGNAL_OFFSET (GtkTextViewClass, scroll_text),
346 gtk_marshal_NONE__INT,
347 GTK_TYPE_NONE, 1, GTK_TYPE_ENUM);
349 signals[DELETE_TEXT] =
350 gtk_signal_new ("delete_text",
351 GTK_RUN_LAST | GTK_RUN_ACTION,
352 GTK_CLASS_TYPE (object_class),
353 GTK_SIGNAL_OFFSET (GtkTextViewClass, delete_text),
354 gtk_marshal_NONE__INT_INT,
355 GTK_TYPE_NONE, 2, GTK_TYPE_ENUM, GTK_TYPE_INT);
358 gtk_signal_new ("cut_text",
359 GTK_RUN_LAST | GTK_RUN_ACTION,
360 GTK_CLASS_TYPE (object_class),
361 GTK_SIGNAL_OFFSET (GtkTextViewClass, cut_text),
362 gtk_marshal_NONE__NONE,
366 gtk_signal_new ("copy_text",
367 GTK_RUN_LAST | GTK_RUN_ACTION,
368 GTK_CLASS_TYPE (object_class),
369 GTK_SIGNAL_OFFSET (GtkTextViewClass, copy_text),
370 gtk_marshal_NONE__NONE,
373 signals[PASTE_TEXT] =
374 gtk_signal_new ("paste_text",
375 GTK_RUN_LAST | GTK_RUN_ACTION,
376 GTK_CLASS_TYPE (object_class),
377 GTK_SIGNAL_OFFSET (GtkTextViewClass, paste_text),
378 gtk_marshal_NONE__NONE,
381 signals[TOGGLE_OVERWRITE] =
382 gtk_signal_new ("toggle_overwrite",
383 GTK_RUN_LAST | GTK_RUN_ACTION,
384 GTK_CLASS_TYPE (object_class),
385 GTK_SIGNAL_OFFSET (GtkTextViewClass, toggle_overwrite),
386 gtk_marshal_NONE__NONE,
389 signals[SET_SCROLL_ADJUSTMENTS] = widget_class->set_scroll_adjustments_signal =
390 gtk_signal_new ("set_scroll_adjustments",
392 GTK_CLASS_TYPE (object_class),
393 GTK_SIGNAL_OFFSET (GtkTextViewClass, set_scroll_adjustments),
394 gtk_marshal_NONE__POINTER_POINTER,
395 GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
397 gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
403 binding_set = gtk_binding_set_by_class (klass);
405 /* Moving the insertion point */
406 add_move_insert_binding (binding_set, GDK_Right, 0,
407 GTK_TEXT_MOVEMENT_POSITIONS, 1);
409 add_move_insert_binding (binding_set, GDK_Left, 0,
410 GTK_TEXT_MOVEMENT_POSITIONS, -1);
412 add_move_insert_binding (binding_set, GDK_f, GDK_CONTROL_MASK,
413 GTK_TEXT_MOVEMENT_CHAR, 1);
415 add_move_insert_binding (binding_set, GDK_b, GDK_CONTROL_MASK,
416 GTK_TEXT_MOVEMENT_CHAR, -1);
418 add_move_insert_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
419 GTK_TEXT_MOVEMENT_WORD, 1);
421 add_move_insert_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
422 GTK_TEXT_MOVEMENT_WORD, -1);
424 /* Eventually we want to move by display lines, not paragraphs */
425 add_move_insert_binding (binding_set, GDK_Up, 0,
426 GTK_TEXT_MOVEMENT_LINE, -1);
428 add_move_insert_binding (binding_set, GDK_Down, 0,
429 GTK_TEXT_MOVEMENT_LINE, 1);
431 add_move_insert_binding (binding_set, GDK_p, GDK_CONTROL_MASK,
432 GTK_TEXT_MOVEMENT_LINE, -1);
434 add_move_insert_binding (binding_set, GDK_n, GDK_CONTROL_MASK,
435 GTK_TEXT_MOVEMENT_LINE, 1);
437 add_move_insert_binding (binding_set, GDK_a, GDK_CONTROL_MASK,
438 GTK_TEXT_MOVEMENT_PARAGRAPH_ENDS, -1);
440 add_move_insert_binding (binding_set, GDK_e, GDK_CONTROL_MASK,
441 GTK_TEXT_MOVEMENT_PARAGRAPH_ENDS, 1);
443 add_move_insert_binding (binding_set, GDK_f, GDK_MOD1_MASK,
444 GTK_TEXT_MOVEMENT_WORD, 1);
446 add_move_insert_binding (binding_set, GDK_b, GDK_MOD1_MASK,
447 GTK_TEXT_MOVEMENT_WORD, -1);
449 add_move_insert_binding (binding_set, GDK_Home, 0,
450 GTK_TEXT_MOVEMENT_BUFFER_ENDS, -1);
452 add_move_insert_binding (binding_set, GDK_End, 0,
453 GTK_TEXT_MOVEMENT_BUFFER_ENDS, 1);
455 /* Setting the cut/paste/copy anchor */
456 gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_CONTROL_MASK,
460 /* Scrolling around */
461 gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
463 GTK_TYPE_ENUM, GTK_TEXT_SCROLL_PAGE_UP);
465 gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
467 GTK_TYPE_ENUM, GTK_TEXT_SCROLL_PAGE_DOWN);
470 gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0,
472 GTK_TYPE_ENUM, GTK_TEXT_DELETE_CHAR,
475 gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
477 GTK_TYPE_ENUM, GTK_TEXT_DELETE_CHAR,
480 gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_CONTROL_MASK,
482 GTK_TYPE_ENUM, GTK_TEXT_DELETE_HALF_WORD,
485 gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK,
487 GTK_TYPE_ENUM, GTK_TEXT_DELETE_HALF_WORD,
490 gtk_binding_entry_add_signal (binding_set, GDK_k, GDK_CONTROL_MASK,
492 GTK_TYPE_ENUM, GTK_TEXT_DELETE_HALF_PARAGRAPH,
495 gtk_binding_entry_add_signal (binding_set, GDK_u, GDK_CONTROL_MASK,
497 GTK_TYPE_ENUM, GTK_TEXT_DELETE_WHOLE_PARAGRAPH,
500 gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
502 GTK_TYPE_ENUM, GTK_TEXT_DELETE_WHITESPACE_LEAVE_ONE,
505 gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_MOD1_MASK,
507 GTK_TYPE_ENUM, GTK_TEXT_DELETE_WHITESPACE,
512 gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK,
515 gtk_binding_entry_add_signal (binding_set, GDK_w, GDK_CONTROL_MASK,
518 gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
521 gtk_binding_entry_add_signal (binding_set, GDK_y, GDK_CONTROL_MASK,
525 gtk_binding_entry_add_signal (binding_set, GDK_Insert, 0,
526 "toggle_overwrite", 0);
529 * Default handlers and virtual methods
531 object_class->set_arg = gtk_text_view_set_arg;
532 object_class->get_arg = gtk_text_view_get_arg;
534 object_class->destroy = gtk_text_view_destroy;
535 gobject_class->finalize = gtk_text_view_finalize;
537 widget_class->realize = gtk_text_view_realize;
538 widget_class->unrealize = gtk_text_view_unrealize;
539 widget_class->style_set = gtk_text_view_style_set;
540 widget_class->size_request = gtk_text_view_size_request;
541 widget_class->size_allocate = gtk_text_view_size_allocate;
542 widget_class->event = gtk_text_view_event;
543 widget_class->key_press_event = gtk_text_view_key_press_event;
544 widget_class->key_release_event = gtk_text_view_key_release_event;
545 widget_class->button_press_event = gtk_text_view_button_press_event;
546 widget_class->button_release_event = gtk_text_view_button_release_event;
547 widget_class->focus_in_event = gtk_text_view_focus_in_event;
548 widget_class->focus_out_event = gtk_text_view_focus_out_event;
549 widget_class->motion_notify_event = gtk_text_view_motion_event;
550 widget_class->expose_event = gtk_text_view_expose_event;
551 widget_class->draw = gtk_text_view_draw;
553 widget_class->drag_begin = gtk_text_view_drag_begin;
554 widget_class->drag_end = gtk_text_view_drag_end;
555 widget_class->drag_data_get = gtk_text_view_drag_data_get;
556 widget_class->drag_data_delete = gtk_text_view_drag_data_delete;
558 widget_class->drag_leave = gtk_text_view_drag_leave;
559 widget_class->drag_motion = gtk_text_view_drag_motion;
560 widget_class->drag_drop = gtk_text_view_drag_drop;
561 widget_class->drag_data_received = gtk_text_view_drag_data_received;
563 klass->move_insert = gtk_text_view_move_insert;
564 klass->set_anchor = gtk_text_view_set_anchor;
565 klass->scroll_text = gtk_text_view_scroll_text;
566 klass->delete_text = gtk_text_view_delete_text;
567 klass->cut_text = gtk_text_view_cut_text;
568 klass->copy_text = gtk_text_view_copy_text;
569 klass->paste_text = gtk_text_view_paste_text;
570 klass->toggle_overwrite = gtk_text_view_toggle_overwrite;
571 klass->set_scroll_adjustments = gtk_text_view_set_scroll_adjustments;
575 gtk_text_view_init (GtkTextView *text_view)
579 widget = GTK_WIDGET (text_view);
581 GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
583 text_view->wrap_mode = GTK_WRAPMODE_NONE;
586 clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE);
589 text_atom = gdk_atom_intern ("TEXT", FALSE);
592 ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
595 utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
597 gtk_drag_dest_set (widget,
598 GTK_DEST_DEFAULT_DROP | GTK_DEST_DEFAULT_MOTION,
599 target_table, n_targets,
600 GDK_ACTION_COPY | GDK_ACTION_MOVE);
602 text_view->virtual_cursor_x = -1;
603 text_view->virtual_cursor_y = -1;
605 /* This object is completely private. No external entity can gain a reference
606 * to it; so we create it here and destroy it in finalize().
608 text_view->im_context = gtk_im_multicontext_new ();
610 gtk_signal_connect (GTK_OBJECT (text_view->im_context), "commit",
611 GTK_SIGNAL_FUNC (gtk_text_view_commit_handler), text_view);
615 gtk_text_view_new (void)
617 return GTK_WIDGET (gtk_type_new (gtk_text_view_get_type ()));
621 gtk_text_view_new_with_buffer (GtkTextBuffer *buffer)
623 GtkTextView *text_view;
625 text_view = (GtkTextView*)gtk_text_view_new ();
627 gtk_text_view_set_buffer (text_view, buffer);
629 return GTK_WIDGET (text_view);
633 gtk_text_view_set_buffer (GtkTextView *text_view,
634 GtkTextBuffer *buffer)
636 g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
637 g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
639 if (text_view->buffer == buffer)
642 if (text_view->buffer != NULL)
644 gtk_signal_disconnect_by_func (GTK_OBJECT (text_view->buffer),
645 gtk_text_view_mark_set_handler, text_view);
646 gtk_object_unref (GTK_OBJECT (text_view->buffer));
647 text_view->dnd_mark = NULL;
650 text_view->buffer = buffer;
658 gtk_object_ref (GTK_OBJECT (buffer));
659 gtk_object_sink (GTK_OBJECT (buffer));
661 if (text_view->layout)
662 gtk_text_layout_set_buffer (text_view->layout, buffer);
664 gtk_text_buffer_get_iter_at_char (text_view->buffer, &start, 0);
666 text_view->dnd_mark = gtk_text_buffer_create_mark (text_view->buffer,
670 /* Initialize. FIXME: Allow anonymous marks and use one here
672 mark_name = g_strdup_printf ("__first_para_%p", text_view);
673 text_view->first_para_mark = gtk_text_buffer_create_mark (text_view->buffer,
677 text_view->first_para_pixels = 0;
679 gtk_signal_connect (GTK_OBJECT (text_view->buffer), "mark_set",
680 gtk_text_view_mark_set_handler, text_view);
683 if (GTK_WIDGET_VISIBLE (text_view))
684 gtk_widget_queue_draw (GTK_WIDGET (text_view));
688 gtk_text_view_get_buffer (GtkTextView *text_view)
690 g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
692 return text_view->buffer;
696 gtk_text_view_get_iter_at_pixel (GtkTextView *text_view,
700 g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
701 g_return_if_fail (iter != NULL);
702 g_return_if_fail (text_view->layout != NULL);
704 gtk_text_layout_get_iter_at_pixel (text_view->layout,
706 x + text_view->xoffset,
707 y + text_view->yoffset);
712 set_adjustment_clamped (GtkAdjustment *adj, gfloat val)
714 /* We don't really want to clamp to upper; we want to clamp to
715 upper - page_size which is the highest value the scrollbar
716 will let us reach. */
717 if (val > (adj->upper - adj->page_size))
718 val = adj->upper - adj->page_size;
720 if (val < adj->lower)
723 gtk_adjustment_set_value (adj, val);
727 gtk_text_view_scroll_to_mark_adjusted (GtkTextView *text_view,
738 gboolean retval = FALSE;
741 gint current_x_scroll, current_y_scroll;
743 g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
744 g_return_val_if_fail (mark != NULL, FALSE);
746 widget = GTK_WIDGET (text_view);
748 if (!GTK_WIDGET_MAPPED (widget))
750 g_warning ("FIXME need to implement scroll_to_mark for unmapped GtkTextView?");
754 gtk_text_buffer_get_iter_at_mark (text_view->buffer, &iter, mark);
756 gtk_text_layout_get_iter_location (text_view->layout,
760 /* Be sure the scroll region is up-to-date */
761 gtk_text_view_scroll_calc_now (text_view);
763 current_x_scroll = text_view->xoffset;
764 current_y_scroll = text_view->yoffset;
766 screen.x = current_x_scroll;
767 screen.y = current_y_scroll;
768 screen.width = widget->allocation.width;
769 screen.height = widget->allocation.height;
772 /* Clamp margin so it's not too large. */
773 gint small_dimension = MIN (screen.width, screen.height);
776 if (margin > (small_dimension/2 - 5)) /* 5 is arbitrary */
777 margin = (small_dimension/2 - 5);
782 /* make sure rectangle fits in the leftover space */
784 max_rect_dim = MAX (rect.width, rect.height);
786 if (max_rect_dim > (small_dimension - margin*2))
787 margin -= max_rect_dim - (small_dimension - margin*2);
793 g_assert (margin >= 0);
798 screen.width -= margin*2;
799 screen.height -= margin*2;
801 screen_bottom = screen.y + screen.height;
802 screen_right = screen.x + screen.width;
804 /* Vertical scroll (only vertical gets adjusted) */
807 if (rect.y < screen.y)
809 gint scroll_dest = rect.y;
810 scroll_inc = (scroll_dest - screen.y) * percentage;
812 else if ((rect.y + rect.height) > screen_bottom)
814 gint scroll_dest = rect.y + rect.height;
815 scroll_inc = (scroll_dest - screen_bottom) * percentage;
820 set_adjustment_clamped (text_view->vadjustment,
821 current_y_scroll + scroll_inc);
825 /* Horizontal scroll */
828 if (rect.x < screen.x)
830 gint scroll_dest = rect.x;
831 scroll_inc = scroll_dest - screen.x;
833 else if ((rect.x + rect.width) > screen_right)
835 gint scroll_dest = rect.x + rect.width;
836 scroll_inc = scroll_dest - screen_right;
841 set_adjustment_clamped (text_view->hadjustment,
842 current_x_scroll + scroll_inc);
850 gtk_text_view_scroll_to_mark (GtkTextView *text_view,
852 gint mark_within_margin)
854 g_return_val_if_fail (mark_within_margin >= 0, FALSE);
856 return gtk_text_view_scroll_to_mark_adjusted (text_view, mark,
857 mark_within_margin, 1.0);
861 clamp_iter_onscreen (GtkTextView *text_view, GtkTextIter *iter)
863 GdkRectangle visible_rect;
864 gtk_text_view_get_visible_rect (text_view, &visible_rect);
866 return gtk_text_layout_clamp_iter_to_vrange (text_view->layout, iter,
868 visible_rect.y + visible_rect.height);
872 gtk_text_view_move_mark_onscreen (GtkTextView *text_view,
877 g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
878 g_return_val_if_fail (mark != NULL, FALSE);
880 gtk_text_buffer_get_iter_at_mark (text_view->buffer, &iter, mark);
882 if (clamp_iter_onscreen (text_view, &iter))
884 gtk_text_buffer_move_mark (text_view->buffer, mark, &iter);
892 gtk_text_view_get_visible_rect (GtkTextView *text_view,
893 GdkRectangle *visible_rect)
897 g_return_if_fail (text_view != NULL);
898 g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
900 widget = GTK_WIDGET (text_view);
904 visible_rect->x = text_view->xoffset;
905 visible_rect->y = text_view->yoffset;
906 visible_rect->width = widget->allocation.width;
907 visible_rect->height = widget->allocation.height;
912 gtk_text_view_set_wrap_mode (GtkTextView *text_view,
913 GtkWrapMode wrap_mode)
915 g_return_if_fail (text_view != NULL);
916 g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
918 if (text_view->wrap_mode != wrap_mode)
920 text_view->wrap_mode = wrap_mode;
922 if (text_view->layout)
924 text_view->layout->default_style->wrap_mode = wrap_mode;
925 gtk_text_layout_default_style_changed (text_view->layout);
931 gtk_text_view_get_wrap_mode (GtkTextView *text_view)
933 g_return_val_if_fail (text_view != NULL, GTK_WRAPMODE_NONE);
934 g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_WRAPMODE_NONE);
936 return text_view->wrap_mode;
940 gtk_text_view_place_cursor_onscreen (GtkTextView *text_view)
944 g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
946 gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert,
947 gtk_text_buffer_get_mark (text_view->buffer,
950 if (clamp_iter_onscreen (text_view, &insert))
952 gtk_text_buffer_place_cursor (text_view->buffer, &insert);
960 gtk_text_view_destroy (GtkObject *object)
962 GtkTextView *text_view;
964 text_view = GTK_TEXT_VIEW (object);
966 gtk_text_view_destroy_layout (text_view);
967 gtk_text_view_set_buffer (text_view, NULL);
969 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
973 gtk_text_view_finalize (GObject *object)
975 GtkTextView *text_view;
977 text_view = GTK_TEXT_VIEW (object);
979 gtk_object_unref (GTK_OBJECT (text_view->hadjustment));
980 gtk_object_unref (GTK_OBJECT (text_view->vadjustment));
981 gtk_object_unref (GTK_OBJECT (text_view->im_context));
983 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
987 gtk_text_view_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
989 GtkTextView *text_view;
991 text_view = GTK_TEXT_VIEW (object);
995 case ARG_HEIGHT_LINES:
998 case ARG_WIDTH_COLUMNS:
1001 case ARG_PIXELS_ABOVE_LINES:
1004 case ARG_PIXELS_BELOW_LINES:
1007 case ARG_PIXELS_INSIDE_WRAP:
1017 g_assert_not_reached ();
1023 gtk_text_view_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
1025 GtkTextView *text_view;
1027 text_view = GTK_TEXT_VIEW (object);
1031 case ARG_HEIGHT_LINES:
1034 case ARG_WIDTH_COLUMNS:
1037 case ARG_PIXELS_ABOVE_LINES:
1040 case ARG_PIXELS_BELOW_LINES:
1043 case ARG_PIXELS_INSIDE_WRAP:
1053 arg->type = GTK_TYPE_INVALID;
1059 gtk_text_view_size_request (GtkWidget *widget,
1060 GtkRequisition *requisition)
1062 GtkTextView *text_view = GTK_TEXT_VIEW (widget);
1065 requisition->width = 1;
1066 requisition->height = 1;
1068 /* Check to see if the widget direction has changed */
1070 if (text_view->layout)
1072 GtkTextDirection direction = gtk_widget_get_direction (widget);
1073 if (direction != text_view->layout->default_style->direction)
1075 text_view->layout->default_style->direction = direction;
1076 gtk_text_layout_default_style_changed (text_view->layout);
1082 gtk_text_view_size_allocate (GtkWidget *widget,
1083 GtkAllocation *allocation)
1085 GtkTextView *text_view;
1086 GtkTextIter first_para;
1088 GtkAdjustment *vadj;
1089 gboolean yoffset_changed = FALSE;
1091 text_view = GTK_TEXT_VIEW (widget);
1093 widget->allocation = *allocation;
1095 if (GTK_WIDGET_REALIZED (widget))
1097 gdk_window_move_resize (widget->window,
1098 allocation->x, allocation->y,
1099 allocation->width, allocation->height);
1101 gdk_window_resize (text_view->bin_window,
1102 allocation->width, allocation->height);
1105 gtk_text_view_ensure_layout (text_view);
1106 gtk_text_layout_set_screen_width (text_view->layout,
1107 GTK_WIDGET (text_view)->allocation.width);
1109 gtk_text_view_validate_onscreen (text_view);
1110 gtk_text_view_scroll_calc_now (text_view);
1112 /* Now adjust the value of the adjustment to keep the cursor at the same place in
1115 gtk_text_view_get_first_para_iter (text_view, &first_para);
1116 y = gtk_text_layout_get_line_y (text_view->layout, &first_para) + text_view->first_para_pixels;
1118 vadj = text_view->vadjustment;
1119 if (y > vadj->upper - vadj->page_size)
1120 y = MAX (0, vadj->upper - vadj->page_size);
1122 if (y != text_view->yoffset)
1124 vadj->value = text_view->yoffset = y;
1125 yoffset_changed = TRUE;
1128 text_view->hadjustment->page_size = allocation->width;
1129 text_view->hadjustment->page_increment = allocation->width / 2;
1130 text_view->hadjustment->lower = 0;
1131 text_view->hadjustment->upper = MAX (allocation->width, text_view->width);
1132 gtk_signal_emit_by_name (GTK_OBJECT (text_view->hadjustment), "changed");
1134 text_view->vadjustment->page_size = allocation->height;
1135 text_view->vadjustment->page_increment = allocation->height / 2;
1136 text_view->vadjustment->lower = 0;
1137 text_view->vadjustment->upper = MAX (allocation->height, text_view->height);
1138 gtk_signal_emit_by_name (GTK_OBJECT (text_view->vadjustment), "changed");
1140 if (yoffset_changed)
1141 gtk_adjustment_value_changed (vadj);
1145 gtk_text_view_get_first_para_iter (GtkTextView *text_view,
1148 gtk_text_buffer_get_iter_at_mark (text_view->buffer, iter,
1149 text_view->first_para_mark);
1153 gtk_text_view_validate_onscreen (GtkTextView *text_view)
1155 GtkWidget *widget = GTK_WIDGET (text_view);
1157 if (widget->allocation.height > 0)
1159 GtkTextIter first_para;
1160 gtk_text_view_get_first_para_iter (text_view, &first_para);
1161 gtk_text_layout_validate_yrange (text_view->layout,
1163 0, text_view->first_para_pixels + widget->allocation.height);
1168 first_validate_callback (gpointer data)
1170 GtkTextView *text_view = data;
1172 gtk_text_view_validate_onscreen (text_view);
1174 text_view->first_validate_idle = 0;
1179 incremental_validate_callback (gpointer data)
1181 GtkTextView *text_view = data;
1183 gtk_text_layout_validate (text_view->layout, 2000);
1184 if (gtk_text_layout_is_valid (text_view->layout))
1186 text_view->incremental_validate_idle = 0;
1194 invalidated_handler (GtkTextLayout *layout,
1197 GtkTextView *text_view;
1199 text_view = GTK_TEXT_VIEW (data);
1201 if (!text_view->first_validate_idle)
1202 text_view->first_validate_idle = g_idle_add_full (GTK_PRIORITY_RESIZE - 1, first_validate_callback, text_view, NULL);
1204 if (!text_view->incremental_validate_idle)
1205 text_view->incremental_validate_idle = g_idle_add_full (GDK_PRIORITY_REDRAW + 1, incremental_validate_callback, text_view, NULL);
1209 changed_handler (GtkTextLayout *layout,
1215 GtkTextView *text_view;
1217 GdkRectangle visible_rect;
1218 GdkRectangle redraw_rect;
1220 text_view = GTK_TEXT_VIEW (data);
1221 widget = GTK_WIDGET (data);
1223 if (GTK_WIDGET_REALIZED (text_view))
1225 gtk_text_view_get_visible_rect (text_view, &visible_rect);
1227 redraw_rect.x = visible_rect.x;
1228 redraw_rect.width = visible_rect.width;
1229 redraw_rect.y = start_y;
1230 redraw_rect.height = MAX (old_height, new_height);
1232 if (gdk_rectangle_intersect (&redraw_rect, &visible_rect, &redraw_rect))
1234 redraw_rect.y -= text_view->yoffset;
1235 gdk_window_invalidate_rect (text_view->bin_window, &redraw_rect, FALSE);
1239 if (old_height != new_height)
1241 gboolean yoffset_changed = FALSE;
1243 if (start_y + old_height <= text_view->yoffset - text_view->first_para_pixels)
1245 text_view->yoffset += new_height - old_height;
1246 text_view->vadjustment->value = text_view->yoffset;
1247 yoffset_changed = TRUE;
1250 gtk_text_view_scroll_calc_now (text_view);
1252 if (yoffset_changed)
1253 gtk_adjustment_value_changed (text_view->vadjustment);
1259 gtk_text_view_realize (GtkWidget *widget)
1261 GtkTextView *text_view;
1263 GdkWindowAttr attributes;
1264 gint attributes_mask;
1266 text_view = GTK_TEXT_VIEW (widget);
1267 GTK_WIDGET_SET_FLAGS (text_view, GTK_REALIZED);
1269 attributes.window_type = GDK_WINDOW_CHILD;
1270 attributes.x = widget->allocation.x;
1271 attributes.y = widget->allocation.y;
1272 attributes.width = widget->allocation.width;
1273 attributes.height = widget->allocation.height;
1274 attributes.wclass = GDK_INPUT_OUTPUT;
1275 attributes.visual = gtk_widget_get_visual (widget);
1276 attributes.colormap = gtk_widget_get_colormap (widget);
1277 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1279 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1281 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1282 &attributes, attributes_mask);
1283 gdk_window_set_user_data (widget->window, widget);
1287 attributes.width = widget->allocation.width;
1288 attributes.height = widget->allocation.height;
1289 attributes.event_mask = (GDK_EXPOSURE_MASK |
1291 GDK_KEY_PRESS_MASK |
1292 GDK_BUTTON_PRESS_MASK |
1293 GDK_BUTTON_RELEASE_MASK |
1294 GDK_POINTER_MOTION_MASK |
1295 GDK_POINTER_MOTION_HINT_MASK |
1296 gtk_widget_get_events (widget));
1298 text_view->bin_window = gdk_window_new (widget->window,
1299 &attributes, attributes_mask);
1300 gdk_window_show (text_view->bin_window);
1301 gdk_window_set_user_data (text_view->bin_window, widget);
1303 widget->style = gtk_style_attach (widget->style, widget->window);
1305 gdk_window_set_background (text_view->bin_window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
1307 gtk_text_view_ensure_layout (text_view);
1310 cursor = gdk_cursor_new (GDK_XTERM);
1311 gdk_window_set_cursor (text_view->bin_window, cursor);
1312 gdk_cursor_destroy (cursor);
1314 gtk_im_context_set_client_window (text_view->im_context, widget->window);
1318 gtk_text_view_unrealize (GtkWidget *widget)
1320 GtkTextView *text_view;
1322 text_view = GTK_TEXT_VIEW (widget);
1324 if (text_view->first_validate_idle)
1326 g_source_remove (text_view->first_validate_idle);
1327 text_view->first_validate_idle = 0;
1330 if (text_view->incremental_validate_idle)
1332 g_source_remove (text_view->incremental_validate_idle);
1333 text_view->incremental_validate_idle = 0;
1336 gtk_text_view_destroy_layout (text_view);
1338 gtk_im_context_set_client_window (text_view->im_context, NULL);
1340 gdk_window_set_user_data (text_view->bin_window, NULL);
1341 gdk_window_destroy (text_view->bin_window);
1342 text_view->bin_window = NULL;
1344 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1348 gtk_text_view_style_set (GtkWidget *widget,
1349 GtkStyle *previous_style)
1351 GtkTextView *text_view = GTK_TEXT_VIEW (widget);
1353 if (GTK_WIDGET_REALIZED (widget))
1355 gdk_window_set_background (text_view->bin_window,
1356 &widget->style->base[GTK_WIDGET_STATE (widget)]);
1358 gtk_text_view_set_values_from_style (text_view, text_view->layout->default_style, widget->style);
1359 gtk_text_layout_default_style_changed (text_view->layout);
1368 get_event_coordinates (GdkEvent *event, gint *x, gint *y)
1371 switch (event->type)
1373 case GDK_MOTION_NOTIFY:
1374 *x = event->motion.x;
1375 *y = event->motion.y;
1379 case GDK_BUTTON_PRESS:
1380 case GDK_2BUTTON_PRESS:
1381 case GDK_3BUTTON_PRESS:
1382 case GDK_BUTTON_RELEASE:
1383 *x = event->button.x;
1384 *y = event->button.y;
1389 case GDK_KEY_RELEASE:
1390 case GDK_ENTER_NOTIFY:
1391 case GDK_LEAVE_NOTIFY:
1392 case GDK_PROPERTY_NOTIFY:
1393 case GDK_SELECTION_CLEAR:
1394 case GDK_SELECTION_REQUEST:
1395 case GDK_SELECTION_NOTIFY:
1396 case GDK_PROXIMITY_IN:
1397 case GDK_PROXIMITY_OUT:
1398 case GDK_DRAG_ENTER:
1399 case GDK_DRAG_LEAVE:
1400 case GDK_DRAG_MOTION:
1401 case GDK_DRAG_STATUS:
1402 case GDK_DROP_START:
1403 case GDK_DROP_FINISHED:
1413 gtk_text_view_event (GtkWidget *widget, GdkEvent *event)
1415 GtkTextView *text_view;
1418 text_view = GTK_TEXT_VIEW (widget);
1420 if (text_view->layout == NULL ||
1421 text_view->buffer == NULL)
1424 /* FIXME eventually we really want to synthesize enter/leave
1425 events here as the canvas does for canvas items */
1427 if (get_event_coordinates (event, &x, &y))
1430 gint retval = FALSE;
1432 x += text_view->xoffset;
1433 y += text_view->yoffset;
1435 /* FIXME this is slow and we do it twice per event.
1436 My favorite solution is to have GtkTextLayout cache
1437 the last couple lookups. */
1438 gtk_text_layout_get_iter_at_pixel (text_view->layout,
1446 tags = gtk_text_buffer_get_tags (text_view->buffer, &iter);
1451 GtkTextTag *tag = tmp->data;
1453 if (gtk_text_tag_event (tag, GTK_OBJECT (widget), event, &iter))
1459 tmp = g_slist_next (tmp);
1462 g_slist_free (tags);
1472 gtk_text_view_key_press_event (GtkWidget *widget, GdkEventKey *event)
1474 GtkTextView *text_view;
1476 text_view = GTK_TEXT_VIEW (widget);
1478 if (text_view->layout == NULL ||
1479 text_view->buffer == NULL)
1482 if (GTK_WIDGET_CLASS (parent_class)->key_press_event &&
1483 GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
1486 if (gtk_im_context_filter_keypress (text_view->im_context, event))
1488 else if (event->keyval == GDK_Return)
1490 gtk_text_buffer_insert_at_cursor (text_view->buffer, "\n", 1);
1491 gtk_text_view_scroll_to_mark (text_view,
1492 gtk_text_buffer_get_mark (text_view->buffer,
1502 gtk_text_view_key_release_event (GtkWidget *widget, GdkEventKey *event)
1508 gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
1510 GtkTextView *text_view;
1512 text_view = GTK_TEXT_VIEW (widget);
1514 gtk_widget_grab_focus (widget);
1517 if (event->button == 3 && (event->state & GDK_CONTROL_MASK) != 0)
1518 gtk_text_buffer_spew (GTK_TEXT_VIEW (widget)->buffer);
1519 else if (event->button == 3)
1520 gtk_text_layout_spew (GTK_TEXT_VIEW (widget)->layout);
1522 if (event->type == GDK_BUTTON_PRESS)
1524 if (event->button == 1)
1526 /* If we're in the selection, start a drag copy/move of the
1527 selection; otherwise, start creating a new selection. */
1529 GtkTextIter start, end;
1531 gtk_text_layout_get_iter_at_pixel (text_view->layout,
1533 event->x + text_view->xoffset,
1534 event->y + text_view->yoffset);
1536 if (gtk_text_buffer_get_selection_bounds (text_view->buffer,
1538 gtk_text_iter_in_region (&iter, &start, &end))
1540 gtk_text_view_start_selection_dnd (text_view, &iter, event);
1544 gtk_text_view_start_selection_drag (text_view, &iter, event);
1549 else if (event->button == 2)
1553 gtk_text_layout_get_iter_at_pixel (text_view->layout,
1555 event->x + text_view->xoffset,
1556 event->y + text_view->yoffset);
1558 gtk_text_buffer_paste_primary_selection (text_view->buffer,
1563 else if (event->button == 3)
1565 if (gtk_text_view_end_selection_drag (text_view, event))
1576 gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event)
1578 if (event->button == 1)
1580 gtk_text_view_end_selection_drag (GTK_TEXT_VIEW (widget), event);
1588 gtk_text_view_focus_in_event (GtkWidget *widget, GdkEventFocus *event)
1590 GtkTextMark *insert;
1592 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
1594 insert = gtk_text_buffer_get_mark (GTK_TEXT_VIEW (widget)->buffer,
1596 gtk_text_mark_set_visible (insert, TRUE);
1598 gtk_text_view_start_cursor_blink (GTK_TEXT_VIEW (widget));
1600 gtk_im_context_focus_in (GTK_TEXT_VIEW (widget)->im_context);
1606 gtk_text_view_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
1608 GtkTextMark *insert;
1610 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
1612 insert = gtk_text_buffer_get_mark (GTK_TEXT_VIEW (widget)->buffer,
1614 gtk_text_mark_set_visible (insert, FALSE);
1616 gtk_text_view_stop_cursor_blink (GTK_TEXT_VIEW (widget));
1618 gtk_im_context_focus_out (GTK_TEXT_VIEW (widget)->im_context);
1624 gtk_text_view_motion_event (GtkWidget *widget, GdkEventMotion *event)
1631 gtk_text_view_paint (GtkWidget *widget, GdkRectangle *area)
1633 GtkTextView *text_view;
1635 text_view = GTK_TEXT_VIEW (widget);
1637 g_return_if_fail (text_view->layout != NULL);
1638 g_return_if_fail (text_view->xoffset >= 0);
1639 g_return_if_fail (text_view->yoffset >= 0);
1641 gtk_text_view_validate_onscreen (text_view);
1644 printf ("painting %d,%d %d x %d\n",
1646 area->width, area->height);
1649 gtk_text_layout_draw (text_view->layout,
1651 text_view->bin_window,
1652 text_view->xoffset, text_view->yoffset,
1654 area->width, area->height);
1658 gtk_text_view_draw (GtkWidget *widget, GdkRectangle *area)
1660 gtk_text_view_paint (widget, area);
1664 gtk_text_view_expose_event (GtkWidget *widget, GdkEventExpose *event)
1666 if (event->window == GTK_TEXT_VIEW (widget)->bin_window)
1667 gtk_text_view_paint (widget, &event->area);
1677 blink_cb (gpointer data)
1679 GtkTextView *text_view;
1680 GtkTextMark *insert;
1682 text_view = GTK_TEXT_VIEW (data);
1684 insert = gtk_text_buffer_get_mark (text_view->buffer,
1687 if (!GTK_WIDGET_HAS_FOCUS (text_view))
1689 /* paranoia, in case the user somehow mangles our
1690 focus_in/focus_out pairing. */
1691 gtk_text_mark_set_visible (insert, FALSE);
1692 text_view->blink_timeout = 0;
1697 gtk_text_mark_set_visible (insert,
1698 !gtk_text_mark_is_visible (insert));
1704 gtk_text_view_start_cursor_blink (GtkTextView *text_view)
1707 if (text_view->blink_timeout != 0)
1710 text_view->blink_timeout = gtk_timeout_add (500, blink_cb, text_view);
1714 gtk_text_view_stop_cursor_blink (GtkTextView *text_view)
1717 if (text_view->blink_timeout == 0)
1720 gtk_timeout_remove (text_view->blink_timeout);
1721 text_view->blink_timeout = 0;
1725 * Key binding handlers
1729 gtk_text_view_move_iter_by_lines (GtkTextView *text_view,
1730 GtkTextIter *newplace,
1735 gtk_text_layout_move_iter_to_previous_line (text_view->layout, newplace);
1741 gtk_text_layout_move_iter_to_next_line (text_view->layout, newplace);
1747 gtk_text_view_move_insert (GtkTextView *text_view,
1748 GtkTextViewMovementStep step,
1750 gboolean extend_selection)
1753 GtkTextIter newplace;
1755 gint cursor_x_pos = 0;
1757 gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert,
1758 gtk_text_buffer_get_mark (text_view->buffer,
1762 if (step == GTK_TEXT_MOVEMENT_LINE)
1763 gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, NULL);
1767 case GTK_TEXT_MOVEMENT_CHAR:
1768 gtk_text_iter_forward_chars (&newplace, count);
1771 case GTK_TEXT_MOVEMENT_POSITIONS:
1772 gtk_text_layout_move_iter_visually (text_view->layout,
1776 case GTK_TEXT_MOVEMENT_WORD:
1778 gtk_text_iter_backward_word_starts (&newplace, -count);
1780 gtk_text_iter_forward_word_ends (&newplace, count);
1783 case GTK_TEXT_MOVEMENT_LINE:
1784 gtk_text_view_move_iter_by_lines (text_view, &newplace, count);
1785 gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
1788 case GTK_TEXT_MOVEMENT_PARAGRAPH:
1789 /* This should almost certainly instead be doing the parallel thing to WORD */
1790 gtk_text_iter_down_lines (&newplace, count);
1793 case GTK_TEXT_MOVEMENT_PARAGRAPH_ENDS:
1795 gtk_text_iter_forward_to_newline (&newplace);
1797 gtk_text_iter_set_line_char (&newplace, 0);
1800 case GTK_TEXT_MOVEMENT_BUFFER_ENDS:
1802 gtk_text_buffer_get_last_iter (text_view->buffer, &newplace);
1804 gtk_text_buffer_get_iter_at_char (text_view->buffer, &newplace, 0);
1811 if (!gtk_text_iter_equal (&insert, &newplace))
1813 if (extend_selection)
1814 gtk_text_buffer_move_mark (text_view->buffer,
1815 gtk_text_buffer_get_mark (text_view->buffer,
1819 gtk_text_buffer_place_cursor (text_view->buffer, &newplace);
1821 gtk_text_view_scroll_to_mark (text_view,
1822 gtk_text_buffer_get_mark (text_view->buffer,
1825 if (step == GTK_TEXT_MOVEMENT_LINE)
1827 gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, -1);
1833 gtk_text_view_set_anchor (GtkTextView *text_view)
1837 gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert,
1838 gtk_text_buffer_get_mark (text_view->buffer,
1841 gtk_text_buffer_create_mark (text_view->buffer, "anchor", &insert, TRUE);
1845 gtk_text_view_scroll_text (GtkTextView *text_view,
1846 GtkTextViewScrollType type)
1850 gint cursor_x_pos, cursor_y_pos;
1851 GtkTextIter new_insert;
1855 g_return_if_fail (text_view->vadjustment != NULL);
1857 adj = text_view->vadjustment;
1859 /* Validate the region that will be brought into view by the cursor motion
1864 case GTK_TEXT_SCROLL_TO_TOP:
1865 gtk_text_buffer_get_iter_at_char (text_view->buffer, &anchor, 0);
1867 y1 = adj->page_size;
1870 case GTK_TEXT_SCROLL_TO_BOTTOM:
1871 gtk_text_buffer_get_last_iter (text_view->buffer, &anchor);
1872 y0 = -adj->page_size;
1873 y1 = adj->page_size;
1876 case GTK_TEXT_SCROLL_PAGE_DOWN:
1877 gtk_text_view_get_first_para_iter (text_view, &anchor);
1878 y0 = adj->page_size;
1879 y1 = adj->page_size + adj->page_increment;
1882 case GTK_TEXT_SCROLL_PAGE_UP:
1883 gtk_text_view_get_first_para_iter (text_view, &anchor);
1884 y0 = - adj->page_increment + adj->page_size;
1888 gtk_text_layout_validate_yrange (text_view->layout, &anchor, y0, y1);
1891 gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos);
1893 newval = adj->value;
1896 case GTK_TEXT_SCROLL_TO_TOP:
1897 newval = adj->lower;
1900 case GTK_TEXT_SCROLL_TO_BOTTOM:
1901 newval = adj->upper;
1904 case GTK_TEXT_SCROLL_PAGE_DOWN:
1905 newval += adj->page_increment;
1908 case GTK_TEXT_SCROLL_PAGE_UP:
1909 newval -= adj->page_increment;
1916 cursor_y_pos += newval - adj->value;
1917 set_adjustment_clamped (adj, newval);
1919 gtk_text_layout_get_iter_at_pixel (text_view->layout, &new_insert, cursor_x_pos, cursor_y_pos);
1920 clamp_iter_onscreen (text_view, &new_insert);
1921 gtk_text_buffer_place_cursor (text_view->buffer, &new_insert);
1923 gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, cursor_y_pos);
1925 /* Adjust to have the cursor _entirely_ onscreen, move_mark_onscreen
1926 * only guarantees 1 pixel onscreen.
1928 gtk_text_view_scroll_to_mark (text_view,
1929 gtk_text_buffer_get_mark (text_view->buffer,
1935 whitespace (GtkTextUniChar ch, gpointer user_data)
1937 return (ch == ' ' || ch == '\t');
1941 not_whitespace (GtkTextUniChar ch, gpointer user_data)
1943 return !whitespace (ch, user_data);
1947 find_whitepace_region (const GtkTextIter *center,
1948 GtkTextIter *start, GtkTextIter *end)
1953 if (gtk_text_iter_backward_find_char (start, not_whitespace, NULL))
1954 gtk_text_iter_forward_char (start); /* we want the first whitespace... */
1955 if (whitespace (gtk_text_iter_get_char (end), NULL))
1956 gtk_text_iter_forward_find_char (end, not_whitespace, NULL);
1958 return !gtk_text_iter_equal (start, end);
1962 gtk_text_view_delete_text (GtkTextView *text_view,
1963 GtkTextViewDeleteType type,
1969 gboolean leave_one = FALSE;
1971 if (type == GTK_TEXT_DELETE_CHAR)
1973 /* Char delete deletes the selection, if one exists */
1974 if (gtk_text_buffer_delete_selection (text_view->buffer))
1978 gtk_text_buffer_get_iter_at_mark (text_view->buffer,
1980 gtk_text_buffer_get_mark (text_view->buffer,
1988 case GTK_TEXT_DELETE_CHAR:
1989 gtk_text_iter_forward_chars (&end, count);
1992 case GTK_TEXT_DELETE_HALF_WORD:
1994 gtk_text_iter_forward_word_ends (&end, count);
1996 gtk_text_iter_backward_word_starts (&start, 0 - count);
1999 case GTK_TEXT_DELETE_WHOLE_WORD:
2002 case GTK_TEXT_DELETE_HALF_LINE:
2005 case GTK_TEXT_DELETE_WHOLE_LINE:
2008 case GTK_TEXT_DELETE_HALF_PARAGRAPH:
2011 if (!gtk_text_iter_forward_to_newline (&end))
2017 /* FIXME figure out what a negative count means
2021 case GTK_TEXT_DELETE_WHOLE_PARAGRAPH:
2024 gtk_text_iter_set_line_char (&start, 0);
2025 gtk_text_iter_forward_to_newline (&end);
2027 /* Do the lines beyond the first. */
2030 gtk_text_iter_forward_to_newline (&end);
2036 /* FIXME negative count? */
2040 case GTK_TEXT_DELETE_WHITESPACE_LEAVE_ONE:
2041 leave_one = TRUE; /* FALL THRU */
2042 case GTK_TEXT_DELETE_WHITESPACE:
2044 find_whitepace_region (&insert, &start, &end);
2052 if (!gtk_text_iter_equal (&start, &end))
2054 gtk_text_buffer_delete (text_view->buffer, &start, &end);
2057 gtk_text_buffer_insert_at_cursor (text_view->buffer, " ", 1);
2059 gtk_text_view_scroll_to_mark (text_view,
2060 gtk_text_buffer_get_mark (text_view->buffer, "insert"),
2066 gtk_text_view_cut_text (GtkTextView *text_view)
2068 gtk_text_buffer_cut (text_view->buffer, GDK_CURRENT_TIME);
2069 gtk_text_view_scroll_to_mark (text_view,
2070 gtk_text_buffer_get_mark (text_view->buffer,
2076 gtk_text_view_copy_text (GtkTextView *text_view)
2078 gtk_text_buffer_copy (text_view->buffer, GDK_CURRENT_TIME);
2079 gtk_text_view_scroll_to_mark (text_view,
2080 gtk_text_buffer_get_mark (text_view->buffer,
2086 gtk_text_view_paste_text (GtkTextView *text_view)
2088 gtk_text_buffer_paste_clipboard (text_view->buffer, GDK_CURRENT_TIME);
2089 gtk_text_view_scroll_to_mark (text_view,
2090 gtk_text_buffer_get_mark (text_view->buffer,
2096 gtk_text_view_toggle_overwrite (GtkTextView *text_view)
2098 text_view->overwrite_mode = !text_view->overwrite_mode;
2106 move_insert_to_pointer_and_scroll (GtkTextView *text_view, gboolean partial_scroll)
2109 GdkModifierType state;
2110 GtkTextIter newplace;
2112 gboolean in_threshold = FALSE;
2114 gdk_window_get_pointer (text_view->bin_window, &x, &y, &state);
2116 /* Adjust movement by how long we've been selecting, to
2117 get an acceleration effect. The exact numbers are
2118 pretty arbitrary. We have a threshold before we
2119 start to accelerate. */
2120 /* uncommenting this printf helps visualize how it works. */
2121 /* printf ("%d\n", text_view->scrolling_accel_factor); */
2123 if (text_view->scrolling_accel_factor > 10)
2124 adjust = (text_view->scrolling_accel_factor - 10) * 75;
2126 if (y < 0) /* scrolling upward */
2129 /* No adjust if the pointer has moved back inside the window for sure.
2130 Also I'm adding a small threshold where no adjust is added,
2131 in case you want to do a continuous slow scroll. */
2132 #define SLOW_SCROLL_TH 7
2133 if (x >= (0 - SLOW_SCROLL_TH) &&
2134 x < (GTK_WIDGET (text_view)->allocation.width + SLOW_SCROLL_TH) &&
2135 y >= (0 - SLOW_SCROLL_TH) &&
2136 y < (GTK_WIDGET (text_view)->allocation.height + SLOW_SCROLL_TH))
2139 in_threshold = TRUE;
2142 gtk_text_layout_get_iter_at_pixel (text_view->layout,
2144 x + text_view->xoffset,
2145 y + text_view->yoffset + adjust);
2148 gboolean scrolled = FALSE;
2149 GtkTextMark *insert_mark =
2150 gtk_text_buffer_get_mark (text_view->buffer, "insert");
2152 gtk_text_buffer_move_mark (text_view->buffer,
2157 scrolled = gtk_text_view_scroll_to_mark_adjusted (text_view, insert_mark, 0, 0.7);
2159 scrolled = gtk_text_view_scroll_to_mark_adjusted (text_view, insert_mark, 0, 1.0);
2163 /* We want to avoid rapid jump to super-accelerated when you
2164 leave the slow scroll threshold after scrolling for a
2165 while. So we slowly decrease accel when scrolling inside
2170 if (text_view->scrolling_accel_factor > 1)
2171 text_view->scrolling_accel_factor -= 2;
2174 text_view->scrolling_accel_factor += 1;
2178 /* If we don't scroll we're probably inside the window, but
2179 potentially just a bit outside. We decrease acceleration
2180 while the user is fooling around inside the window.
2181 Acceleration decreases faster than it increases. */
2182 if (text_view->scrolling_accel_factor > 4)
2183 text_view->scrolling_accel_factor -= 5;
2191 selection_scan_timeout (gpointer data)
2193 GtkTextView *text_view;
2195 text_view = GTK_TEXT_VIEW (data);
2197 if (move_insert_to_pointer_and_scroll (text_view, TRUE))
2199 return TRUE; /* remain installed. */
2203 text_view->selection_drag_scan_timeout = 0;
2204 return FALSE; /* remove ourselves */
2209 selection_motion_event_handler (GtkTextView *text_view, GdkEventMotion *event, gpointer data)
2211 if (move_insert_to_pointer_and_scroll (text_view, TRUE))
2213 /* If we had to scroll offscreen, insert a timeout to do so
2214 again. Note that in the timeout, even if the mouse doesn't
2215 move, due to this scroll xoffset/yoffset will have changed
2216 and we'll need to scroll again. */
2217 if (text_view->selection_drag_scan_timeout != 0) /* reset on every motion event */
2218 gtk_timeout_remove (text_view->selection_drag_scan_timeout);
2220 text_view->selection_drag_scan_timeout =
2221 gtk_timeout_add (50, selection_scan_timeout, text_view);
2228 gtk_text_view_start_selection_drag (GtkTextView *text_view,
2229 const GtkTextIter *iter,
2230 GdkEventButton *event)
2232 GtkTextIter newplace;
2234 g_return_if_fail (text_view->selection_drag_handler == 0);
2236 gtk_grab_add (GTK_WIDGET (text_view));
2238 text_view->scrolling_accel_factor = 0;
2242 gtk_text_buffer_place_cursor (text_view->buffer, &newplace);
2244 text_view->selection_drag_handler = gtk_signal_connect (GTK_OBJECT (text_view),
2245 "motion_notify_event",
2246 GTK_SIGNAL_FUNC (selection_motion_event_handler),
2250 /* returns whether we were really dragging */
2252 gtk_text_view_end_selection_drag (GtkTextView *text_view, GdkEventButton *event)
2254 if (text_view->selection_drag_handler == 0)
2257 gtk_signal_disconnect (GTK_OBJECT (text_view), text_view->selection_drag_handler);
2258 text_view->selection_drag_handler = 0;
2260 text_view->scrolling_accel_factor = 0;
2262 if (text_view->selection_drag_scan_timeout != 0)
2264 gtk_timeout_remove (text_view->selection_drag_scan_timeout);
2265 text_view->selection_drag_scan_timeout = 0;
2268 /* one last update to current position */
2269 move_insert_to_pointer_and_scroll (text_view, FALSE);
2271 gtk_grab_remove (GTK_WIDGET (text_view));
2281 gtk_text_view_set_adjustment_upper (GtkAdjustment *adj, gfloat upper)
2283 if (upper != adj->upper)
2285 gfloat min = MAX (0., upper - adj->page_size);
2286 gboolean value_changed = FALSE;
2290 if (adj->value > min)
2293 value_changed = TRUE;
2296 gtk_signal_emit_by_name (GTK_OBJECT (adj), "changed");
2298 gtk_signal_emit_by_name (GTK_OBJECT (adj), "value_changed");
2303 gtk_text_view_scroll_calc_now (GtkTextView *text_view)
2305 gint width = 0, height = 0;
2306 GtkWidget *widget = GTK_WIDGET (text_view);
2308 gtk_text_view_ensure_layout (text_view);
2311 gtk_text_layout_set_screen_width (text_view->layout,
2312 widget->allocation.width);
2314 gtk_text_layout_get_size (text_view->layout, &width, &height);
2317 /* If the width is less than the screen width (likely
2318 if we have wrapping turned on for the whole widget),
2319 then we want to set the scroll region to the screen
2320 width. If the width is greater (wrapping off) then we
2321 probably want to set the scroll region to the width
2322 of the layout. I guess.
2325 width = MAX (text_view->layout->screen_width, width);
2329 if (text_view->width != width || text_view->height != height)
2332 printf ("layout size set, widget width is %d\n",
2333 GTK_WIDGET (text_view)->allocation.width);
2335 text_view->width = width;
2336 text_view->height = height;
2338 gtk_text_view_set_adjustment_upper (text_view->hadjustment,
2339 MAX (widget->allocation.width, width));
2340 gtk_text_view_set_adjustment_upper (text_view->vadjustment,
2341 MAX (widget->allocation.height, height));
2343 /* Set up the step sizes; we'll say that a page is
2344 our allocation minus one step, and a step is
2345 1/10 of our allocation. */
2346 text_view->hadjustment->step_increment =
2347 GTK_WIDGET (text_view)->allocation.width/10.0;
2348 text_view->hadjustment->page_increment =
2349 GTK_WIDGET (text_view)->allocation.width *0.9;
2351 text_view->vadjustment->step_increment =
2352 GTK_WIDGET (text_view)->allocation.height/10.0;
2353 text_view->vadjustment->page_increment =
2354 GTK_WIDGET (text_view)->allocation.height *0.9;
2359 gtk_text_view_set_values_from_style (GtkTextView *text_view,
2360 GtkTextStyleValues *values,
2363 values->appearance.bg_color = style->base[GTK_STATE_NORMAL];
2364 values->appearance.fg_color = style->fg[GTK_STATE_NORMAL];
2366 if (values->font_desc)
2367 pango_font_description_free (values->font_desc);
2369 values->font_desc = pango_font_description_copy (style->font_desc);
2373 gtk_text_view_ensure_layout (GtkTextView *text_view)
2377 widget = GTK_WIDGET (text_view);
2379 if (text_view->layout == NULL)
2381 GtkTextStyleValues *style;
2382 PangoContext *ltr_context, *rtl_context;
2384 text_view->layout = gtk_text_layout_new ();
2386 gtk_signal_connect (GTK_OBJECT (text_view->layout),
2388 GTK_SIGNAL_FUNC (invalidated_handler),
2391 gtk_signal_connect (GTK_OBJECT (text_view->layout),
2393 GTK_SIGNAL_FUNC (changed_handler),
2396 if (text_view->buffer)
2397 gtk_text_layout_set_buffer (text_view->layout, text_view->buffer);
2399 ltr_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
2400 pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR);
2401 rtl_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
2402 pango_context_set_base_dir (rtl_context, PANGO_DIRECTION_RTL);
2404 gtk_text_layout_set_contexts (text_view->layout, ltr_context, rtl_context);
2406 g_object_unref (G_OBJECT (ltr_context));
2407 g_object_unref (G_OBJECT (rtl_context));
2409 style = gtk_text_view_style_values_new ();
2411 gtk_widget_ensure_style (widget);
2412 gtk_text_view_set_values_from_style (text_view, style, widget->style);
2414 style->pixels_above_lines = 2;
2415 style->pixels_below_lines = 2;
2416 style->pixels_inside_wrap = 1;
2418 style->wrap_mode = text_view->wrap_mode;
2419 style->justify = GTK_JUSTIFY_LEFT;
2420 style->direction = gtk_widget_get_direction (GTK_WIDGET (text_view));
2422 gtk_text_layout_set_default_style (text_view->layout, style);
2424 gtk_text_view_style_values_unref (style);
2429 gtk_text_view_destroy_layout (GtkTextView *text_view)
2431 if (text_view->layout)
2433 gtk_text_view_end_selection_drag (text_view, NULL);
2435 gtk_signal_disconnect_by_func (GTK_OBJECT (text_view->layout),
2436 invalidated_handler, text_view);
2437 gtk_signal_disconnect_by_func (GTK_OBJECT (text_view->layout),
2438 changed_handler, text_view);
2439 gtk_object_unref (GTK_OBJECT (text_view->layout));
2440 text_view->layout = NULL;
2450 gtk_text_view_start_selection_dnd (GtkTextView *text_view,
2451 const GtkTextIter *iter,
2452 GdkEventButton *event)
2454 GdkDragContext *context;
2455 GtkTargetList *target_list;
2457 /* FIXME we have to handle more formats for the selection,
2458 and do the conversions to/from UTF8 */
2460 /* FIXME not sure how this is memory-managed. */
2461 target_list = gtk_target_list_new (target_table, n_targets);
2463 context = gtk_drag_begin (GTK_WIDGET (text_view), target_list,
2464 GDK_ACTION_COPY | GDK_ACTION_MOVE,
2465 1, (GdkEvent*)event);
2467 gtk_drag_set_icon_default (context);
2469 /* We're inside the selection, so start without being able
2470 to accept the drag. */
2471 gdk_drag_status (context, 0, event->time);
2472 gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
2476 gtk_text_view_drag_begin (GtkWidget *widget,
2477 GdkDragContext *context)
2483 gtk_text_view_drag_end (GtkWidget *widget,
2484 GdkDragContext *context)
2486 GtkTextView *text_view;
2488 text_view = GTK_TEXT_VIEW (widget);
2490 gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
2494 gtk_text_view_drag_data_get (GtkWidget *widget,
2495 GdkDragContext *context,
2496 GtkSelectionData *selection_data,
2504 GtkTextView *text_view;
2506 text_view = GTK_TEXT_VIEW (widget);
2511 if (gtk_text_buffer_get_selection_bounds (text_view->buffer, &start, &end))
2513 /* Extract the selected text */
2514 str = gtk_text_iter_get_visible_text (&start, &end);
2516 length = strlen (str);
2521 if (info == TARGET_UTF8_STRING)
2524 gtk_selection_data_set (selection_data,
2526 8*sizeof (gchar), (guchar *)str, length);
2529 else if (info == TARGET_STRING ||
2530 info == TARGET_TEXT)
2534 latin1 = gtk_text_utf_to_latin1(str, length);
2536 gtk_selection_data_set (selection_data,
2537 GDK_SELECTION_TYPE_STRING,
2538 8*sizeof (gchar), latin1, strlen (latin1));
2541 else if (info == TARGET_COMPOUND_TEXT)
2543 /* FIXME convert UTF8 directly to current locale, not via
2552 latin1 = gtk_text_utf_to_latin1(str, length);
2554 gdk_string_to_compound_text (latin1, &encoding, &format, &text, &new_length);
2555 gtk_selection_data_set (selection_data, encoding, format, text, new_length);
2556 gdk_free_compound_text (text);
2566 gtk_text_view_drag_data_delete (GtkWidget *widget,
2567 GdkDragContext *context)
2569 gtk_text_buffer_delete_selection (GTK_TEXT_VIEW (widget)->buffer);
2573 gtk_text_view_drag_leave (GtkWidget *widget,
2574 GdkDragContext *context,
2582 gtk_text_view_drag_motion (GtkWidget *widget,
2583 GdkDragContext *context,
2588 GtkTextIter newplace;
2589 GtkTextView *text_view;
2593 text_view = GTK_TEXT_VIEW (widget);
2595 gtk_text_layout_get_iter_at_pixel (text_view->layout,
2597 x + text_view->xoffset,
2598 y + text_view->yoffset);
2600 if (gtk_text_buffer_get_selection_bounds (text_view->buffer,
2602 gtk_text_iter_in_region (&newplace, &start, &end))
2604 /* We're inside the selection. */
2605 gdk_drag_status (context, 0, time);
2606 gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
2610 gtk_text_mark_set_visible (text_view->dnd_mark, TRUE);
2612 gdk_drag_status (context, context->suggested_action, time);
2615 gtk_text_buffer_move_mark (text_view->buffer,
2616 gtk_text_buffer_get_mark (text_view->buffer,
2621 /* The effect of this is that the text scrolls if you're near
2622 the edge. We have to scroll whether or not we're inside
2626 margin = MIN (widget->allocation.width, widget->allocation.height);
2629 gtk_text_view_scroll_to_mark_adjusted (text_view,
2630 gtk_text_buffer_get_mark (text_view->buffer,
2639 gtk_text_view_drag_drop (GtkWidget *widget,
2640 GdkDragContext *context,
2646 /* called automatically. */
2647 if (context->targets)
2649 gtk_drag_get_data (widget, context,
2650 GPOINTER_TO_INT (context->targets->data),
2661 gtk_text_view_drag_data_received (GtkWidget *widget,
2662 GdkDragContext *context,
2665 GtkSelectionData *selection_data,
2669 GtkTextIter drop_point;
2670 GtkTextView *text_view;
2671 GtkTextMark *drag_target_mark;
2673 enum {INVALID, STRING, CTEXT, UTF8} type;
2675 text_view = GTK_TEXT_VIEW (widget);
2677 if (selection_data->type == GDK_TARGET_STRING)
2679 else if (selection_data->type == ctext_atom)
2681 else if (selection_data->type == utf8_atom)
2686 if (type == INVALID || selection_data->length < 0)
2688 /* I think the DND code automatically tries asking
2689 for the various formats. */
2693 drag_target_mark = gtk_text_buffer_get_mark (text_view->buffer,
2696 if (drag_target_mark == NULL)
2699 gtk_text_buffer_get_iter_at_mark (text_view->buffer,
2710 utf = gtk_text_latin1_to_utf ((const gchar*)selection_data->data,
2711 selection_data->length);
2712 gtk_text_buffer_insert (text_view->buffer, &drop_point,
2719 gtk_text_buffer_insert (text_view->buffer, &drop_point,
2720 (const gchar *)selection_data->data,
2721 selection_data->length);
2730 count = gdk_text_property_to_text_list (selection_data->type,
2731 selection_data->format,
2732 selection_data->data,
2733 selection_data->length,
2735 for (i=0; i<count; i++)
2737 /* FIXME this is broken, it assumes the CTEXT is latin1
2738 when it probably isn't. */
2741 utf = gtk_text_latin1_to_utf (list[i], strlen (list[i]));
2743 gtk_text_buffer_insert (text_view->buffer, &drop_point, utf, -1);
2749 gdk_free_text_list (list);
2753 case INVALID: /* quiet compiler */
2759 gtk_text_view_set_scroll_adjustments (GtkTextView *text_view,
2760 GtkAdjustment *hadj,
2761 GtkAdjustment *vadj)
2763 gboolean need_adjust = FALSE;
2765 g_return_if_fail (text_view != NULL);
2766 g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2769 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
2771 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
2773 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
2775 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
2777 if (text_view->hadjustment && (text_view->hadjustment != hadj))
2779 gtk_signal_disconnect_by_data (GTK_OBJECT (text_view->hadjustment), text_view);
2780 gtk_object_unref (GTK_OBJECT (text_view->hadjustment));
2783 if (text_view->vadjustment && (text_view->vadjustment != vadj))
2785 gtk_signal_disconnect_by_data (GTK_OBJECT (text_view->vadjustment), text_view);
2786 gtk_object_unref (GTK_OBJECT (text_view->vadjustment));
2789 if (text_view->hadjustment != hadj)
2791 text_view->hadjustment = hadj;
2792 gtk_object_ref (GTK_OBJECT (text_view->hadjustment));
2793 gtk_object_sink (GTK_OBJECT (text_view->hadjustment));
2795 gtk_signal_connect (GTK_OBJECT (text_view->hadjustment), "value_changed",
2796 (GtkSignalFunc) gtk_text_view_value_changed,
2801 if (text_view->vadjustment != vadj)
2803 text_view->vadjustment = vadj;
2804 gtk_object_ref (GTK_OBJECT (text_view->vadjustment));
2805 gtk_object_sink (GTK_OBJECT (text_view->vadjustment));
2807 gtk_signal_connect (GTK_OBJECT (text_view->vadjustment), "value_changed",
2808 (GtkSignalFunc) gtk_text_view_value_changed,
2814 gtk_text_view_value_changed (NULL, text_view);
2818 gtk_text_view_value_changed (GtkAdjustment *adj,
2819 GtkTextView *text_view)
2826 if (adj == text_view->hadjustment)
2828 dx = text_view->xoffset - (gint)adj->value;
2829 text_view->xoffset = adj->value;
2831 else if (adj == text_view->vadjustment)
2833 dy = text_view->yoffset - (gint)adj->value;
2834 text_view->yoffset = adj->value;
2836 if (text_view->layout)
2838 gtk_text_layout_get_line_at_y (text_view->layout, &iter, adj->value, &line_top);
2840 gtk_text_buffer_move_mark (text_view->buffer, text_view->first_para_mark, &iter);
2842 text_view->first_para_pixels = adj->value - line_top;
2846 if (dx != 0 || dy != 0)
2848 gdk_window_scroll (text_view->bin_window, dx, dy);
2849 gdk_window_process_updates (text_view->bin_window, TRUE);
2854 gtk_text_view_commit_handler (GtkIMContext *context,
2856 GtkTextView *text_view)
2858 gtk_text_buffer_delete_selection (text_view->buffer);
2860 if (!strcmp (str, "\n"))
2862 gtk_text_buffer_insert_at_cursor (text_view->buffer, "\n", 1);
2866 if (text_view->overwrite_mode)
2867 gtk_text_view_delete_text (text_view, GTK_TEXT_DELETE_CHAR, 1);
2868 gtk_text_buffer_insert_at_cursor (text_view->buffer, str, strlen (str));
2871 gtk_text_view_scroll_to_mark (text_view,
2872 gtk_text_buffer_get_mark (text_view->buffer,
2878 gtk_text_view_mark_set_handler (GtkTextBuffer *buffer,
2879 const GtkTextIter *location,
2880 const char *mark_name,
2883 GtkTextView *text_view = GTK_TEXT_VIEW (data);
2885 if (!strcmp (mark_name, "insert"))
2887 text_view->virtual_cursor_x = -1;
2888 text_view->virtual_cursor_y = -1;
2893 gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view,
2897 GdkRectangle strong_pos;
2900 gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert,
2901 gtk_text_buffer_get_mark (text_view->buffer,
2904 if ((x && text_view->virtual_cursor_x == -1) ||
2905 (y && text_view->virtual_cursor_y == -1))
2906 gtk_text_layout_get_cursor_locations (text_view->layout, &insert, &strong_pos, NULL);
2910 if (text_view->virtual_cursor_x != -1)
2911 *x = text_view->virtual_cursor_x;
2918 if (text_view->virtual_cursor_x != -1)
2919 *y = text_view->virtual_cursor_y;
2921 *y = strong_pos.y + strong_pos.height / 2;
2926 gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view,
2930 GdkRectangle strong_pos;
2933 gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert,
2934 gtk_text_buffer_get_mark (text_view->buffer,
2937 if (x == -1 || y == -1)
2938 gtk_text_layout_get_cursor_locations (text_view->layout, &insert, &strong_pos, NULL);
2940 text_view->virtual_cursor_x = (x == -1) ? strong_pos.x : x;
2941 text_view->virtual_cursor_y = (y == -1) ? strong_pos.y + strong_pos.height / 2 : y;