1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * anjuta-view.c
4  *
5  * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
6  * Copyright (C) 2000, 2002 Chema Celorio, Paolo Maggi
7  * Copyright (C) 2003-2005 Paolo Maggi
8  * Copyright (C) 2006 Johannes Schmid
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25 
26 /*
27  * Modified by the anjuta Team, 1998-2005. See the AUTHORS file for a
28  * list of people on the anjuta Team.
29  * See the ChangeLog files for a list of changes.
30  *
31  * $Id$
32  */
33 
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37 
38 #include <string.h>
39 #include <stdlib.h>
40 
41 #include <gdk/gdkkeysyms.h>
42 
43 #include <glib/gi18n.h>
44 #include <glib.h>
45 
46 #include <libanjuta/anjuta-debug.h>
47 #include <libanjuta/anjuta-encodings.h>
48 #include <libanjuta/anjuta-utils.h>
49 #include <libanjuta/interfaces/ianjuta-file-loader.h>
50 
51 #include "anjuta-view.h"
52 #include "sourceview.h"
53 #include "sourceview-private.h"
54 #include "anjuta-marshal.h"
55 
56 #define ANJUTA_VIEW_SCROLL_MARGIN 0.02
57 
58 #define TARGET_URI_LIST 100
59 #define TARGET_GLADE 101
60 
61 #define ANJUTA_VIEW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), ANJUTA_TYPE_VIEW, AnjutaViewPrivate))
62 
63 enum
64 {
65 	ANJUTA_VIEW_POPUP = 1,
66 	ANJUTA_VIEW_SOURCEVIEW
67 };
68 
69 struct _AnjutaViewPrivate
70 {
71 	GtkWidget* popup;
72 	guint scroll_idle;
73 	Sourceview* sv;
74 };
75 
76 static void	anjuta_view_dispose	(GObject       *object);
77 static gint anjuta_view_focus_out (GtkWidget       *widget,
78                                    GdkEventFocus   *event);
79 
80 static gboolean	anjuta_view_draw (GtkWidget       *widget,
81 	                              cairo_t* cr);
82 
83 static void anjuta_view_popup_menu_real (GtkWidget *widget, GdkEventButton *event);
84 static gboolean anjuta_view_popup_menu (GtkWidget *widget);
85 
86 static gboolean	anjuta_view_key_press_event	(GtkWidget         *widget,
87 			                                 GdkEventKey       *event);
88 static gboolean	anjuta_view_button_press_event (GtkWidget         *widget,
89 			                                    GdkEventButton       *event);
90 
G_DEFINE_TYPE(AnjutaView,anjuta_view,GTK_SOURCE_TYPE_VIEW)91 G_DEFINE_TYPE(AnjutaView, anjuta_view, GTK_SOURCE_TYPE_VIEW)
92 
93 static gboolean
94 scroll_to_cursor_real (AnjutaView *view)
95 {
96 	GtkTextBuffer* buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
97 	g_return_val_if_fail (buffer != NULL, FALSE);
98 
99 	gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (view),
100 	                                    gtk_text_buffer_get_insert (buffer));
101 
102 	view->priv->scroll_idle = 0;
103 	return FALSE;
104 }
105 
106 static void
anjuta_view_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)107 anjuta_view_set_property (GObject * object,
108                           guint property_id,
109                           const GValue * value, GParamSpec * pspec)
110 {
111 	AnjutaView *self = ANJUTA_VIEW (object);
112 	g_return_if_fail(value != NULL);
113 	g_return_if_fail(pspec != NULL);
114 
115 	switch (property_id)
116 	{
117 		case ANJUTA_VIEW_POPUP:
118 		{
119 			GtkWidget* widget;
120 			self->priv->popup = g_value_get_object (value);
121 			widget = gtk_menu_get_attach_widget(GTK_MENU(self->priv->popup));
122 			/* This is a very ugly hack, maybe somebody more familiar with gtk menus
123 			can fix this */
124 			if (widget != NULL)
125 				gtk_menu_detach(GTK_MENU(self->priv->popup));
126 			gtk_menu_attach_to_widget(GTK_MENU(self->priv->popup), GTK_WIDGET(self), NULL);
127 			break;
128 		}
129 		case ANJUTA_VIEW_SOURCEVIEW:
130 		{
131 			self->priv->sv = g_value_get_object (value);
132 			break;
133 		}
134 		default:
135 		{
136 			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
137 			break;
138 		}
139 	}
140 }
141 
142 static void
anjuta_view_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)143 anjuta_view_get_property (GObject * object,
144                           guint property_id,
145                           GValue * value, GParamSpec * pspec)
146 {
147 	AnjutaView *self = ANJUTA_VIEW (object);
148 
149 	g_return_if_fail(value != NULL);
150 	g_return_if_fail(pspec != NULL);
151 
152 	switch (property_id)
153 	{
154 		case ANJUTA_VIEW_POPUP:
155 		{
156 			g_value_set_object (value, self->priv->popup);
157 			break;
158 		}
159 		case ANJUTA_VIEW_SOURCEVIEW:
160 		{
161 			g_value_set_object (value, self->priv->sv);
162 			break;
163 		}
164 		default:
165 		{
166 			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
167 			break;
168 		}
169 	}
170 }
171 
172 static GdkAtom
drag_get_target(GtkWidget * widget,GdkDragContext * context)173 drag_get_target (GtkWidget      *widget,
174 		     GdkDragContext *context)
175 {
176 	GdkAtom target;
177 	GtkTargetList *tl;
178 
179 	tl = gtk_drag_dest_get_target_list (widget);
180 
181 	target = gtk_drag_dest_find_target (widget, context, tl);
182 
183 	return target;
184 }
185 
186 static gboolean
anjuta_view_drag_drop(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint timestamp)187 anjuta_view_drag_drop (GtkWidget      *widget,
188                        GdkDragContext *context,
189                        gint            x,
190                        gint            y,
191                        guint           timestamp)
192 {
193 	gboolean result;
194 	GdkAtom target;
195 
196 	/* If this is a URL, just get the drag data */
197 	target = drag_get_target (widget, context);
198 
199 	if (target != GDK_NONE)
200 	{
201 		gtk_drag_get_data (widget, context, target, timestamp);
202 		result = TRUE;
203 	}
204 	else
205 	{
206 		/* Chain up */
207 		result = GTK_WIDGET_CLASS (anjuta_view_parent_class)->drag_drop (widget, context, x, y, timestamp);
208 	}
209 
210 	return result;
211 }
212 
213 static SourceviewCell*
get_cell_from_position(GtkTextView * view,gint x,gint y)214 get_cell_from_position (GtkTextView* view,
215                         gint x,
216                         gint y)
217 {
218 	gint buffer_x, buffer_y;
219 	GtkTextIter iter;
220 	SourceviewCell* cell;
221 
222 	gtk_text_view_window_to_buffer_coords (view,
223 	                                       GTK_TEXT_WINDOW_WIDGET,
224 	                                       x, y, &buffer_x, &buffer_y);
225 
226 	gtk_text_view_get_iter_at_location (view,
227 	                                    &iter, buffer_x, buffer_y);
228 
229 	cell = sourceview_cell_new (&iter, view);
230 
231 	return cell;
232 }
233 
234 static gboolean
anjuta_view_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint timestamp)235 anjuta_view_drag_motion (GtkWidget        *widget,
236                          GdkDragContext   *context,
237                          gint              x,
238                          gint              y,
239                          guint  		   timestamp)
240 {
241 	AnjutaView* view = ANJUTA_VIEW (widget);
242 	SourceviewCell* cell;
243 	gboolean retval;
244 
245 	cell = get_cell_from_position (GTK_TEXT_VIEW(view->priv->sv->priv->view), x, y);
246 
247 	g_signal_emit_by_name (view->priv->sv,
248 	                       "drop-possible",
249 	                       cell, &retval);
250 
251 	g_object_unref (cell);
252 
253 	if (retval)
254 	{
255 		gdk_drag_status (context, GDK_ACTION_COPY, timestamp);
256 	}
257 
258 	GTK_WIDGET_CLASS (anjuta_view_parent_class)->drag_motion (widget, context, x, y, timestamp);
259 
260 	return retval;
261 }
262 
263 static void
anjuta_view_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint timestamp)264 anjuta_view_drag_data_received (GtkWidget        *widget,
265                                 GdkDragContext   *context,
266                                 gint              x,
267                                 gint              y,
268                                 GtkSelectionData *selection_data,
269                                 guint             info,
270                                 guint             timestamp)
271 {
272 	GSList* files;
273 	AnjutaView* view = ANJUTA_VIEW (widget);
274 	/* If this is an URL emit DROP_URIS, otherwise chain up the signal */
275 	if (info == TARGET_URI_LIST)
276 	{
277 		files = anjuta_utils_drop_get_files (selection_data);
278 
279 		if (files != NULL)
280 		{
281 			IAnjutaFileLoader* loader = anjuta_shell_get_interface (view->priv->sv->priv->plugin->shell,
282 			                                                        IAnjutaFileLoader, NULL);
283 			GSList* node;
284 			for (node = files; node != NULL; node = g_slist_next(node))
285 			{
286 				GFile* file = node->data;
287 				ianjuta_file_loader_load (loader, file, FALSE, NULL);
288 				g_object_unref (file);
289 			}
290 			g_slist_free (files);
291 			gtk_drag_finish (context, TRUE, FALSE, timestamp);
292 		}
293 		gtk_drag_finish (context, FALSE, FALSE, timestamp);
294 	}
295 	else if (info == TARGET_GLADE)
296 	{
297 		const gchar* signal_data = (gchar*) gtk_selection_data_get_data (selection_data);
298 		SourceviewCell* cell = get_cell_from_position (GTK_TEXT_VIEW (view->priv->sv->priv->view),
299 		                                               x, y);
300 		g_signal_emit_by_name (view->priv->sv,
301 		                       "drop",
302 		                       cell,
303 		                       signal_data);
304 		g_object_unref (cell);
305 		gtk_drag_finish (context, TRUE, FALSE, timestamp);
306 	}
307 	else
308 	{
309 		GTK_WIDGET_CLASS (anjuta_view_parent_class)->drag_data_received (widget, context, x, y, selection_data, info, timestamp);
310 	}
311 }
312 
313 static void
anjuta_view_class_init(AnjutaViewClass * klass)314 anjuta_view_class_init (AnjutaViewClass *klass)
315 {
316 	GObjectClass     *object_class = G_OBJECT_CLASS (klass);
317 	GtkWidgetClass   *widget_class = GTK_WIDGET_CLASS (klass);
318 
319 	object_class->dispose = anjuta_view_dispose;
320 	object_class->set_property = anjuta_view_set_property;
321 	object_class->get_property = anjuta_view_get_property;
322 
323 	widget_class->focus_out_event = anjuta_view_focus_out;
324 	widget_class->draw = anjuta_view_draw;
325 	widget_class->key_press_event = anjuta_view_key_press_event;
326 	widget_class->button_press_event = anjuta_view_button_press_event;
327 	widget_class->popup_menu = anjuta_view_popup_menu;
328 	widget_class->drag_drop = anjuta_view_drag_drop;
329 	widget_class->drag_data_received = anjuta_view_drag_data_received;
330 	widget_class->drag_motion = anjuta_view_drag_motion;
331 
332 	g_type_class_add_private (klass, sizeof (AnjutaViewPrivate));
333 
334 	g_object_class_install_property (object_class,
335 	                                 ANJUTA_VIEW_POPUP,
336 	                                 g_param_spec_object ("popup",
337 	                                                      "Popup menu",
338 	                                                      "The popup-menu to show",
339 	                                                      GTK_TYPE_WIDGET,
340 	                                                      G_PARAM_READWRITE));
341 	g_object_class_install_property (object_class,
342 	                                 ANJUTA_VIEW_SOURCEVIEW,
343 	                                 g_param_spec_object ("sourceview",
344 	                                                      "Sourceview object",
345 	                                                      "",
346 	                                                      ANJUTA_TYPE_SOURCEVIEW,
347 	                                                      G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
348 }
349 
350 static void
anjuta_view_init(AnjutaView * view)351 anjuta_view_init (AnjutaView *view)
352 {
353   GtkTargetList* tl;
354   view->priv = ANJUTA_VIEW_GET_PRIVATE (view);
355 
356   /* Drag and drop support */
357   tl = gtk_drag_dest_get_target_list (GTK_WIDGET (view));
358 
359   if (tl != NULL)
360   {
361 	GdkAtom target = gdk_atom_intern_static_string ("application/x-glade-signal");
362 	gtk_target_list_add_uri_targets (tl, TARGET_URI_LIST);
363 	gtk_target_list_add (tl, target, GTK_TARGET_OTHER_WIDGET, TARGET_GLADE);
364   }
365 }
366 
367 static void
anjuta_view_dispose(GObject * object)368 anjuta_view_dispose (GObject *object)
369 {
370 	AnjutaView *view;
371 
372 	view = ANJUTA_VIEW (object);
373 
374 	if (view->priv->scroll_idle > 0)
375 		g_source_remove (view->priv->scroll_idle);
376 
377 	if (view->priv->popup != NULL)
378 	{
379 	    GtkWidget* widget = gtk_menu_get_attach_widget (GTK_MENU (view->priv->popup));
380 	    if (widget != NULL)
381             gtk_menu_detach (GTK_MENU (view->priv->popup));
382 	}
383 
384 	(* G_OBJECT_CLASS (anjuta_view_parent_class)->dispose) (object);
385 }
386 
387 static gint
anjuta_view_focus_out(GtkWidget * widget,GdkEventFocus * event)388 anjuta_view_focus_out (GtkWidget *widget, GdkEventFocus *event)
389 {
390 	AnjutaView *view = ANJUTA_VIEW (widget);
391 	AssistTip* assist_tip = view->priv->sv->priv->assist_tip;
392 
393 	if (assist_tip)
394 		gtk_widget_destroy(GTK_WIDGET(assist_tip));
395 
396 	gtk_widget_queue_draw (widget);
397 
398 
399 	(* GTK_WIDGET_CLASS (anjuta_view_parent_class)->focus_out_event) (widget, event);
400 
401 	return FALSE;
402 }
403 
404 
405 /**
406  * anjuta_view_new:
407  * @sv: a #Sourceview
408  *
409  * Creates a new #AnjutaView object displaying the @sv->priv->doc document.
410  * @doc cannot be NULL.
411  *
412  * Return value: a new #AnjutaView
413  **/
414 GtkWidget *
anjuta_view_new(Sourceview * sv)415 anjuta_view_new (Sourceview *sv)
416 {
417 	GtkWidget *view;
418 
419 	/* Create widget with sane defaults */
420 	view = GTK_WIDGET (g_object_new (ANJUTA_TYPE_VIEW,
421 	                                 "buffer", GTK_TEXT_BUFFER (sv->priv->document),
422 	                                 "sourceview", sv,
423 	                                 "wrap-mode", FALSE,
424 	                                 "indent-on-tab", TRUE, /* Fix #388727 */
425 	                                 "smart-home-end", GTK_SOURCE_SMART_HOME_END_BEFORE,
426 	                                 NULL));
427 
428 	return view;
429 }
430 
431 void
anjuta_view_cut_clipboard(AnjutaView * view)432 anjuta_view_cut_clipboard (AnjutaView *view)
433 {
434 	GtkTextBuffer *buffer;
435 	GtkClipboard *clipboard;
436 
437 	g_return_if_fail (ANJUTA_IS_VIEW (view));
438 
439 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
440 	g_return_if_fail (buffer != NULL);
441 
442 	if (!gtk_text_view_get_editable (GTK_TEXT_VIEW(view)))
443 		return;
444 
445 	clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view),
446 					      GDK_SELECTION_CLIPBOARD);
447 
448 	/* FIXME: what is default editability of a buffer? */
449   	gtk_text_buffer_cut_clipboard (buffer,
450   				       clipboard,
451 				       TRUE);
452 
453 	gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (view),
454 	                                    gtk_text_buffer_get_insert (buffer));
455 }
456 
457 void
anjuta_view_copy_clipboard(AnjutaView * view)458 anjuta_view_copy_clipboard (AnjutaView *view)
459 {
460 	GtkTextBuffer *buffer;
461 	GtkClipboard *clipboard;
462 
463 	g_return_if_fail (ANJUTA_IS_VIEW (view));
464 
465 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
466 	g_return_if_fail (buffer != NULL);
467 
468 	clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view),
469 					      GDK_SELECTION_CLIPBOARD);
470 
471   	gtk_text_buffer_copy_clipboard (buffer, clipboard);
472 }
473 
474 void
anjuta_view_paste_clipboard(AnjutaView * view)475 anjuta_view_paste_clipboard (AnjutaView *view)
476 {
477   GtkTextBuffer *buffer;
478 	GtkClipboard *clipboard;
479 
480 	g_return_if_fail (ANJUTA_IS_VIEW (view));
481 
482 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
483 	g_return_if_fail (buffer != NULL);
484 
485 	clipboard = gtk_widget_get_clipboard (GTK_WIDGET (view),
486 					      GDK_SELECTION_CLIPBOARD);
487 
488   	gtk_text_buffer_paste_clipboard (buffer,
489 					 clipboard,
490 					 NULL,
491 					 TRUE);
492 
493 	gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (view),
494 	                                    gtk_text_buffer_get_insert (buffer));
495 }
496 
497 void
anjuta_view_delete_selection(AnjutaView * view)498 anjuta_view_delete_selection (AnjutaView *view)
499 {
500   	GtkTextBuffer *buffer = NULL;
501 
502 	g_return_if_fail (ANJUTA_IS_VIEW (view));
503 
504 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
505 	g_return_if_fail (buffer != NULL);
506 
507 	/* FIXME: what is default editability of a buffer? */
508 	gtk_text_buffer_delete_selection (buffer,
509 					  TRUE,
510 					  TRUE);
511 
512 	gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view),
513 				      gtk_text_buffer_get_insert (buffer),
514 				      ANJUTA_VIEW_SCROLL_MARGIN,
515 				      FALSE,
516 				      0.0,
517 				      0.0);
518 }
519 
520 void
anjuta_view_select_all(AnjutaView * view)521 anjuta_view_select_all (AnjutaView *view)
522 {
523 	GtkTextBuffer *buffer = NULL;
524 	GtkTextIter start, end;
525 
526 	g_return_if_fail (ANJUTA_IS_VIEW (view));
527 
528 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
529 	g_return_if_fail (buffer != NULL);
530 
531 	gtk_text_buffer_get_bounds (buffer, &start, &end);
532 	gtk_text_buffer_select_range (buffer, &start, &end);
533 }
534 
535 void
anjuta_view_scroll_to_cursor(AnjutaView * view)536 anjuta_view_scroll_to_cursor (AnjutaView *view)
537 {
538 	g_return_if_fail (ANJUTA_IS_VIEW (view));
539 
540 	view->priv->scroll_idle = g_idle_add ((GSourceFunc) scroll_to_cursor_real, view);
541 }
542 
543 void
anjuta_view_set_font(AnjutaView * view,gboolean def,const gchar * font_name)544 anjuta_view_set_font (AnjutaView   *view,
545 		     gboolean     def,
546 		     const gchar *font_name)
547 {
548 
549 	g_return_if_fail (ANJUTA_IS_VIEW (view));
550 
551 	if (!def)
552 	{
553 		PangoFontDescription *font_desc = NULL;
554 
555 		g_return_if_fail (font_name != NULL);
556 
557 		font_desc = pango_font_description_from_string (font_name);
558 		g_return_if_fail (font_desc != NULL);
559 
560 		gtk_widget_override_font (GTK_WIDGET (view), font_desc);
561 
562 		pango_font_description_free (font_desc);
563 	}
564 	else
565 	{
566 		gtk_widget_override_font (GTK_WIDGET (view), NULL);
567 	}
568 }
569 
570 static gboolean
anjuta_view_draw(GtkWidget * widget,cairo_t * cr)571 anjuta_view_draw (GtkWidget      *widget,
572                   cairo_t *cr)
573 {
574 	GtkTextView *text_view;
575 	text_view = GTK_TEXT_VIEW (widget);
576 
577 	if (gtk_cairo_should_draw_window (cr, gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT)))
578 	{
579 		GdkRectangle visible_rect;
580 		GtkTextIter iter1, iter2;
581 
582 		gtk_text_view_get_visible_rect (text_view, &visible_rect);
583 		gtk_text_view_get_line_at_y (text_view, &iter1,
584 					     visible_rect.y, NULL);
585 		gtk_text_view_get_line_at_y (text_view, &iter2,
586 					     visible_rect.y
587 					     + visible_rect.height, NULL);
588 		gtk_text_iter_forward_line (&iter2);
589 	}
590 
591 	return (* GTK_WIDGET_CLASS (anjuta_view_parent_class)->draw)(widget, cr);
592 }
593 
594 static gboolean
anjuta_view_key_press_event(GtkWidget * widget,GdkEventKey * event)595 anjuta_view_key_press_event		(GtkWidget *widget, GdkEventKey       *event)
596 {
597 	AnjutaView* view = ANJUTA_VIEW(widget);
598 	AssistTip* assist_tip;
599 
600 	assist_tip = view->priv->sv->priv->assist_tip;
601     switch (event->keyval)
602     {
603       case GDK_KEY_Escape:
604       case GDK_KEY_Up:
605       case GDK_KEY_Down:
606       case GDK_KEY_Page_Up:
607       case GDK_KEY_Page_Down:
608 				if (assist_tip)
609 				{
610 					gtk_widget_destroy (GTK_WIDGET(assist_tip));
611 					break;
612 				}
613 				break;
614 			case GDK_KEY_F7:
615 				/* F7 is used to toggle cursor visibility but we rather like to
616 				 * use it as shortcut for building (#611204)
617 				 */
618 				return FALSE;
619 			case GDK_KEY_Return:
620 				/* Ctrl-Return is used for autocompletion */
621 				if (event->state == GDK_CONTROL_MASK)
622 					return FALSE;
623 	}
624 	return (* GTK_WIDGET_CLASS (anjuta_view_parent_class)->key_press_event)(widget, event);
625 }
626 
627 static void
anjuta_view_popup_menu_real(GtkWidget * widget,GdkEventButton * event)628 anjuta_view_popup_menu_real (GtkWidget *widget, GdkEventButton *event)
629 {
630 	AnjutaView* view = ANJUTA_VIEW (widget);
631 	gint button;
632 	gint event_time;
633 
634 	if (event)
635 	{
636 		button = event->button;
637 		event_time = event->time;
638 	}
639 	else
640 	{
641 		button = 0;
642 		event_time = gtk_get_current_event_time ();
643 	}
644 
645 	gtk_menu_popup (GTK_MENU (view->priv->popup), NULL, NULL, NULL, NULL,
646 	                button, event_time);
647 }
648 
649 static gboolean
anjuta_view_popup_menu(GtkWidget * widget)650 anjuta_view_popup_menu (GtkWidget *widget)
651 {
652 	anjuta_view_popup_menu_real (widget, NULL);
653 
654 	return TRUE;
655 }
656 
657 static gboolean
anjuta_view_button_press_event(GtkWidget * widget,GdkEventButton * event)658 anjuta_view_button_press_event	(GtkWidget *widget, GdkEventButton *event)
659 {
660 	AnjutaView* view = ANJUTA_VIEW(widget);
661 
662   /* If we have a calltip shown - hide it */
663   AssistTip* assist_tip = view->priv->sv->priv->assist_tip;
664 	if (assist_tip)
665 	{
666     gtk_widget_destroy (GTK_WIDGET (assist_tip));
667   }
668 
669 	switch(event->button)
670 	{
671 		case 1: /* Handle double clicks to select complete word */
672 			if (event->type == GDK_2BUTTON_PRESS)
673 			{
674 				GtkTextIter start, end;
675 				anjuta_view_get_current_word (view, &start, &end);
676 				gtk_text_buffer_select_range (gtk_text_iter_get_buffer (&start),
677 				                              &start, &end);
678 				return TRUE;
679 			}
680 			break;
681 		case 3: /* Right Button */
682 		{
683 			GtkTextBuffer* buffer = GTK_TEXT_BUFFER (view->priv->sv->priv->document);
684 			if (!gtk_text_buffer_get_has_selection (buffer))
685 			{
686 				/* Move cursor to set breakpoints at correct line (#530689) */
687 				GtkTextIter iter;
688 				gint buffer_x, buffer_y;
689 				GtkTextWindowType type =  gtk_text_view_get_window_type (GTK_TEXT_VIEW (view),
690 																																 event->window);
691 				gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view),
692 																							 type,
693 																							 event->x,
694 																							 event->y,
695 																							 &buffer_x,
696 																							 &buffer_y);
697 				gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view),
698 																						&iter, buffer_x, buffer_y);
699 				gtk_text_buffer_place_cursor (buffer, &iter);
700 			}
701 			anjuta_view_popup_menu_real(widget, event);
702 			return TRUE;
703 		}
704 		default:
705 			break;
706 	}
707 	return (* GTK_WIDGET_CLASS (anjuta_view_parent_class)->button_press_event)(widget, event);
708 }
709 
710 static gboolean
wordcharacters_contains(gchar c)711 wordcharacters_contains (gchar c)
712 {
713 	if (g_ascii_isalnum(c) ||
714 	    c == '_')
715 		return TRUE;
716 	else
717 		return FALSE;
718 }
719 
720 void
anjuta_view_get_current_word(AnjutaView * view,GtkTextIter * start,GtkTextIter * end)721 anjuta_view_get_current_word (AnjutaView  *view,
722                               GtkTextIter *start,
723                               GtkTextIter *end)
724 {
725 	GtkTextBuffer *buffer =
726 		gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
727 	gchar c;
728 
729 	gtk_text_buffer_get_iter_at_mark (buffer, start,
730 	                                  gtk_text_buffer_get_insert (buffer));
731 	gtk_text_buffer_get_iter_at_mark (buffer, end,
732 	                                  gtk_text_buffer_get_insert (buffer));
733 
734 	do
735 	{
736 		gunichar uni = gtk_text_iter_get_char (start);
737 		gchar* outbuf = g_new0(gchar, 6);
738 		gint len = g_unichar_to_utf8 (uni, outbuf);
739 		/* Check for non-ascii */
740 		if (len > 1)
741 			break;
742 		c = outbuf[0];
743 		g_free (outbuf);
744 	}
745 	while (wordcharacters_contains (c) && gtk_text_iter_backward_char (start));
746 	do
747 	{
748 		gunichar uni = gtk_text_iter_get_char (end);
749 		gchar* outbuf = g_new0(gchar, 6);
750 		gint len = g_unichar_to_utf8 (uni, outbuf);
751 		/* Check for non-ascii */
752 		if (len > 1)
753 			break;
754 		c = outbuf[0];
755 		g_free (outbuf);
756 	}
757 	while (wordcharacters_contains (c) && gtk_text_iter_forward_char (end));
758 
759 	/* Point to the correct start character (not the character before) */
760 	gtk_text_iter_forward_char (start);
761 }
762