1 /*
2 
3 Copyright (c) 2001-2007 Michael Terry
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 
19 */
20 
21 #include "../config.h"
22 #include <glib/gi18n.h>
23 #include <gdk/gdkkeysyms.h>
24 #include <string.h>
25 #include "fio.h"
26 #include "help.h"
27 #include "xpad-app.h"
28 #include "xpad-pad.h"
29 #include "xpad-pad-properties.h"
30 #include "xpad-preferences.h"
31 #include "xpad-settings.h"
32 #include "xpad-text-buffer.h"
33 #include "xpad-text-view.h"
34 #include "xpad-toolbar.h"
35 #include "xpad-tray.h"
36 
37 G_DEFINE_TYPE(XpadPad, xpad_pad, GTK_TYPE_WINDOW)
38 #define XPAD_PAD_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), XPAD_TYPE_PAD, XpadPadPrivate))
39 
40 struct XpadPadPrivate
41 {
42 	/* saved values */
43 	gint x, y, width, height;
44 	gboolean location_valid;
45 	gchar *infoname;
46 	gchar *contentname;
47 	gboolean sticky;
48 
49 	/* selected child widgets */
50 	GtkWidget *textview;
51 	GtkWidget *scrollbar;
52 
53 	/* toolbar stuff */
54 	GtkWidget *toolbar;
55 	guint toolbar_timeout;
56 	gint toolbar_height;
57 	gboolean toolbar_expanded;
58 	gboolean toolbar_pad_resized;
59 
60 	/* properties window */
61 	GtkWidget *properties;
62 
63 	/* menus */
64 	GtkWidget *menu;
65 	GtkWidget *highlight_menu;
66 
67 	XpadPadGroup *group;
68 };
69 
70 enum
71 {
72 	CLOSED,
73 	LAST_SIGNAL
74 };
75 
76 enum
77 {
78   PROP_0,
79   PROP_GROUP,
80   LAST_PROP
81 };
82 
83 static void load_info (XpadPad *pad, gboolean *show);
84 static void load_content (XpadPad *pad);
85 static void save_content (XpadPad *pad);
86 static GtkWidget *menu_get_popup_highlight (XpadPad *pad, GtkAccelGroup *accel_group);
87 static GtkWidget *menu_get_popup_no_highlight (XpadPad *pad, GtkAccelGroup *accel_group);
88 static void xpad_pad_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
89 static void xpad_pad_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
90 static void xpad_pad_dispose (GObject *object);
91 static void xpad_pad_finalize (GObject *object);
92 static void xpad_pad_show (XpadPad *pad);
93 static gboolean xpad_pad_configure_event (XpadPad *pad, GdkEventConfigure *event);
94 static gboolean xpad_pad_toolbar_size_allocate (XpadPad *pad, GtkAllocation *event);
95 static gboolean xpad_pad_window_state_event (XpadPad *pad, GdkEventWindowState *event);
96 static gboolean xpad_pad_delete_event (XpadPad *pad, GdkEvent *event);
97 static gboolean xpad_pad_popup_menu (XpadPad *pad);
98 static void xpad_pad_popup_deactivate (GtkWidget *menu, XpadPad *pad);
99 static gboolean xpad_pad_button_press_event (XpadPad *pad, GdkEventButton *event);
100 static gboolean xpad_pad_text_view_button_press_event (GtkWidget *text_view, GdkEventButton *event, XpadPad *pad);
101 static void xpad_pad_text_changed (XpadPad *pad, GtkTextBuffer *buffer);
102 static void xpad_pad_notify_has_scrollbar (XpadPad *pad);
103 static void xpad_pad_notify_has_decorations (XpadPad *pad);
104 static void xpad_pad_notify_has_toolbar (XpadPad *pad);
105 static void xpad_pad_notify_autohide_toolbar (XpadPad *pad);
106 static void xpad_pad_hide_toolbar (XpadPad *pad);
107 static void xpad_pad_show_toolbar (XpadPad *pad);
108 static void xpad_pad_popup (XpadPad *pad, GdkEventButton *event);
109 static void xpad_pad_spawn (XpadPad *pad);
110 static void xpad_pad_clear (XpadPad *pad);
111 static void xpad_pad_delete (XpadPad *pad);
112 static void xpad_pad_open_properties (XpadPad *pad);
113 static void xpad_pad_open_preferences (XpadPad *pad);
114 static void xpad_pad_quit (XpadPad *pad);
115 static void xpad_pad_close_all (XpadPad *pad);
116 static void xpad_pad_sync_title (XpadPad *pad);
117 static void xpad_pad_set_group (XpadPad *pad, XpadPadGroup *group);
118 static gboolean xpad_pad_leave_notify_event (GtkWidget *pad, GdkEventCrossing *event);
119 static gboolean xpad_pad_enter_notify_event (GtkWidget *pad, GdkEventCrossing *event);
120 static void xpad_pad_toolbar_popup (GtkWidget *toolbar, GtkMenu *menu, XpadPad *pad);
121 static void xpad_pad_toolbar_popdown (GtkWidget *toolbar, GtkMenu *menu, XpadPad *pad);
122 static XpadPadGroup *xpad_pad_get_group (XpadPad *pad);
123 
124 static guint signals[LAST_SIGNAL] = { 0 };
125 
126 GtkWidget *
xpad_pad_new(XpadPadGroup * group)127 xpad_pad_new (XpadPadGroup *group)
128 {
129 	return GTK_WIDGET (g_object_new (XPAD_TYPE_PAD, "group", group, NULL));
130 }
131 
132 GtkWidget *
xpad_pad_new_with_info(XpadPadGroup * group,const gchar * info_filename,gboolean * show)133 xpad_pad_new_with_info (XpadPadGroup *group, const gchar *info_filename, gboolean *show)
134 {
135 	GtkWidget *pad = GTK_WIDGET (g_object_new (XPAD_TYPE_PAD, "group", group, NULL));
136 
137 	XPAD_PAD (pad)->priv->infoname = g_strdup (info_filename);
138 	load_info (XPAD_PAD (pad), show);
139 	load_content (XPAD_PAD (pad));
140 	gtk_window_set_role (GTK_WINDOW (pad), XPAD_PAD (pad)->priv->infoname);
141 
142 	return pad;
143 }
144 
145 GtkWidget *
xpad_pad_new_from_file(XpadPadGroup * group,const gchar * filename)146 xpad_pad_new_from_file (XpadPadGroup *group, const gchar *filename)
147 {
148 	GtkWidget *pad = NULL;
149 	gchar *content;
150 
151 	content = fio_get_file (filename);
152 
153 	if (!content)
154 	{
155 		gchar *usertext = g_strdup_printf (_("Could not read file %s."), filename);
156 		xpad_app_error (NULL, usertext, NULL);
157 		g_free (usertext);
158 	}
159 	else
160 	{
161 		GtkTextBuffer *buffer;
162 
163 		pad = GTK_WIDGET (g_object_new (XPAD_TYPE_PAD, "group", group, NULL));
164 		buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (XPAD_PAD (pad)->priv->textview));
165 
166 		g_signal_handlers_block_by_func (buffer, xpad_pad_text_changed, pad);
167 
168 		xpad_text_buffer_set_text_with_tags (XPAD_TEXT_BUFFER (buffer), content ? content : "");
169 		g_free (content);
170 
171 		g_signal_handlers_unblock_by_func (buffer, xpad_pad_text_changed, pad);
172 		xpad_pad_text_changed(XPAD_PAD(pad), buffer);
173 	}
174 
175 	return pad;
176 }
177 
178 static void
xpad_pad_class_init(XpadPadClass * klass)179 xpad_pad_class_init (XpadPadClass *klass)
180 {
181 	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
182 
183 	gobject_class->dispose = xpad_pad_dispose;
184 	gobject_class->finalize = xpad_pad_finalize;
185 	gobject_class->set_property = xpad_pad_set_property;
186 	gobject_class->get_property = xpad_pad_get_property;
187 
188 	signals[CLOSED] =
189 		g_signal_new ("closed",
190 		              G_OBJECT_CLASS_TYPE (gobject_class),
191 		              G_SIGNAL_RUN_FIRST,
192 		              G_STRUCT_OFFSET (XpadPadClass, closed),
193 		              NULL, NULL,
194 		              g_cclosure_marshal_VOID__VOID,
195 		              G_TYPE_NONE,
196 		              0);
197 
198 	/* Properties */
199 
200 	g_object_class_install_property (gobject_class,
201 	                                 PROP_GROUP,
202 	                                 g_param_spec_pointer ("group",
203 	                                                       "Pad Group",
204 	                                                       "Pad group for this pad",
205 	                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
206 
207 	g_type_class_add_private (gobject_class, sizeof (XpadPadPrivate));
208 }
209 
210 static void
xpad_pad_init(XpadPad * pad)211 xpad_pad_init (XpadPad *pad)
212 {
213 	GtkWidget *vbox;
214 	GtkAccelGroup *accel_group;
215 
216 	pad->priv = XPAD_PAD_GET_PRIVATE (pad);
217 
218 	pad->priv->x = 0;
219 	pad->priv->y = 0;
220 	pad->priv->location_valid = FALSE;
221 	pad->priv->width = xpad_settings_get_width (xpad_settings ());
222 	pad->priv->height = xpad_settings_get_height (xpad_settings ());
223 	pad->priv->infoname = NULL;
224 	pad->priv->contentname = NULL;
225 	pad->priv->sticky = xpad_settings_get_sticky (xpad_settings ());
226 	pad->priv->textview = NULL;
227 	pad->priv->scrollbar = NULL;
228 	pad->priv->toolbar = NULL;
229 	pad->priv->toolbar_timeout = 0;
230 	pad->priv->toolbar_height = 0;
231 	pad->priv->toolbar_expanded = FALSE;
232 	pad->priv->toolbar_pad_resized = TRUE;
233 	pad->priv->properties = NULL;
234 	pad->priv->group = NULL;
235 
236 	pad->priv->textview = GTK_WIDGET (g_object_new (XPAD_TYPE_TEXT_VIEW,
237 		"follow-font-style", TRUE,
238 		"follow-color-style", TRUE,
239 		NULL));
240 
241 	pad->priv->scrollbar = GTK_WIDGET (g_object_new (GTK_TYPE_SCROLLED_WINDOW,
242 		"hadjustment", NULL,
243 		"hscrollbar-policy", GTK_POLICY_NEVER,
244 		"shadow-type", GTK_SHADOW_NONE,
245 		"vadjustment", NULL,
246 		"vscrollbar-policy", GTK_POLICY_NEVER,
247 		"child", pad->priv->textview,
248 		NULL));
249 
250 	pad->priv->toolbar = GTK_WIDGET (g_object_new (XPAD_TYPE_TOOLBAR,
251 		NULL));
252 
253 	accel_group = gtk_accel_group_new ();
254 	gtk_window_add_accel_group (GTK_WINDOW (pad), accel_group);
255 	g_object_unref (G_OBJECT (accel_group));
256 	pad->priv->menu = menu_get_popup_no_highlight (pad, accel_group);
257 	pad->priv->highlight_menu = menu_get_popup_highlight (pad, accel_group);
258 	gtk_accel_group_connect (accel_group, GDK_Q, GDK_CONTROL_MASK, 0,
259 	                         g_cclosure_new_swap (G_CALLBACK (xpad_pad_quit), pad, NULL));
260 
261 
262 	vbox = GTK_WIDGET (g_object_new (GTK_TYPE_VBOX,
263 		"homogeneous", FALSE,
264 		"spacing", 0,
265 		"child", pad->priv->scrollbar,
266 		"child", pad->priv->toolbar,
267 		NULL));
268 	gtk_container_child_set (GTK_CONTAINER (vbox), pad->priv->toolbar, "expand", FALSE, NULL);
269 
270 	g_object_set (G_OBJECT (pad),
271 		"decorated", xpad_settings_get_has_decorations (xpad_settings ()),
272 		"default-height", xpad_settings_get_height (xpad_settings ()),
273 		"default-width", xpad_settings_get_width (xpad_settings ()),
274 		"gravity", GDK_GRAVITY_STATIC, /* static gravity makes saving pad x,y work */
275 		"skip-pager-hint", !xpad_settings_get_has_decorations (xpad_settings ()),
276 		"skip-taskbar-hint", !xpad_settings_get_has_decorations (xpad_settings ()),
277 		/*"type", GTK_WINDOW_TOPLEVEL,*/
278 		"type-hint", GDK_WINDOW_TYPE_HINT_NORMAL,
279 		"window-position", GTK_WIN_POS_MOUSE,
280 		"child", vbox,
281 		NULL);
282 
283 	xpad_pad_notify_has_scrollbar (pad);
284 
285 	/* Set up signals */
286 	gtk_widget_add_events (GTK_WIDGET (pad), GDK_BUTTON_PRESS_MASK | GDK_PROPERTY_CHANGE_MASK);
287 	gtk_widget_add_events (pad->priv->toolbar, GDK_ALL_EVENTS_MASK);
288 	g_signal_connect (pad->priv->textview, "button-press-event", G_CALLBACK (xpad_pad_text_view_button_press_event), pad);
289 	g_signal_connect_swapped (pad->priv->textview, "popup-menu", G_CALLBACK (xpad_pad_popup_menu), pad);
290 	g_signal_connect_swapped (pad->priv->toolbar, "size-allocate", G_CALLBACK (xpad_pad_toolbar_size_allocate), pad);
291 	g_signal_connect (pad, "button-press-event", G_CALLBACK (xpad_pad_button_press_event), NULL);
292 	g_signal_connect (pad, "configure-event", G_CALLBACK (xpad_pad_configure_event), NULL);
293 	g_signal_connect (pad, "delete-event", G_CALLBACK (xpad_pad_delete_event), NULL);
294 	g_signal_connect (pad, "popup-menu", G_CALLBACK (xpad_pad_popup_menu), NULL);
295 	g_signal_connect (pad, "show", G_CALLBACK (xpad_pad_show), NULL);
296 	g_signal_connect (pad, "window-state-event", G_CALLBACK (xpad_pad_window_state_event), NULL);
297 	g_signal_connect_swapped (gtk_text_view_get_buffer (GTK_TEXT_VIEW (pad->priv->textview)), "changed", G_CALLBACK (xpad_pad_text_changed), pad);
298 
299 	g_signal_connect (pad, "enter-notify-event", G_CALLBACK (xpad_pad_enter_notify_event), NULL);
300 	g_signal_connect (pad, "leave-notify-event", G_CALLBACK (xpad_pad_leave_notify_event), NULL);
301 
302 	g_signal_connect_swapped (xpad_settings (), "notify::has-decorations", G_CALLBACK (xpad_pad_notify_has_decorations), pad);
303 	g_signal_connect_swapped (xpad_settings (), "notify::has-toolbar", G_CALLBACK (xpad_pad_notify_has_toolbar), pad);
304 	g_signal_connect_swapped (xpad_settings (), "notify::autohide-toolbar", G_CALLBACK (xpad_pad_notify_autohide_toolbar), pad);
305 	g_signal_connect_swapped (xpad_settings (), "notify::has-scrollbar", G_CALLBACK (xpad_pad_notify_has_scrollbar), pad);
306 
307 	g_signal_connect_swapped (pad->priv->toolbar, "activate-new", G_CALLBACK (xpad_pad_spawn), pad);
308 	g_signal_connect_swapped (pad->priv->toolbar, "activate-clear", G_CALLBACK (xpad_pad_clear), pad);
309 	g_signal_connect_swapped (pad->priv->toolbar, "activate-close", G_CALLBACK (xpad_pad_close), pad);
310 	g_signal_connect_swapped (pad->priv->toolbar, "activate-delete", G_CALLBACK (xpad_pad_delete), pad);
311 	g_signal_connect_swapped (pad->priv->toolbar, "activate-properties", G_CALLBACK (xpad_pad_open_properties), pad);
312 	g_signal_connect_swapped (pad->priv->toolbar, "activate-preferences", G_CALLBACK (xpad_pad_open_preferences), pad);
313 	g_signal_connect_swapped (pad->priv->toolbar, "activate-quit", G_CALLBACK (xpad_pad_close_all), pad);
314 
315 	g_signal_connect (pad->priv->toolbar, "popup", G_CALLBACK (xpad_pad_toolbar_popup), pad);
316 	g_signal_connect (pad->priv->toolbar, "popdown", G_CALLBACK (xpad_pad_toolbar_popdown), pad);
317 
318 	g_signal_connect (pad->priv->menu, "deactivate", G_CALLBACK (xpad_pad_popup_deactivate), pad);
319 	g_signal_connect (pad->priv->highlight_menu, "deactivate", G_CALLBACK (xpad_pad_popup_deactivate), pad);
320 
321 	if (pad->priv->sticky)
322 		gtk_window_stick (GTK_WINDOW (pad));
323 	else
324 		gtk_window_unstick (GTK_WINDOW (pad));
325 
326 	xpad_pad_sync_title (pad);
327 
328 	gtk_widget_show_all (vbox);
329 
330 	gtk_widget_hide (pad->priv->toolbar);
331 	xpad_pad_notify_has_toolbar (pad);
332 }
333 
334 static void
xpad_pad_show(XpadPad * pad)335 xpad_pad_show (XpadPad *pad)
336 {
337 	/* Some wm's might not acknowledge our request for a specific
338 	   location before we are shown.  What we do here is a little gimpy
339 	   and not very respectful of wms' sovereignty, but it has the effect
340 	   of making pads' locations very dependable.  We just move the pad
341 	   again here after being shown.  This may create a visual effect if
342 	   the wm did ignore us, but is better than being in the wrong
343 	   place, I guess. */
344 	if (pad->priv->location_valid)
345 		gtk_window_move (GTK_WINDOW (pad), pad->priv->x, pad->priv->y);
346 
347 /*	g_object_set (G_OBJECT (pad),
348 		"gravity", GDK_GRAVITY_STATIC,
349 		"skip-pager-hint", TRUE,
350 		"skip-taskbar-hint", TRUE,
351 		NULL);*/
352 
353 	if (pad->priv->sticky)
354 		gtk_window_stick (GTK_WINDOW (pad));
355 	else
356 		gtk_window_unstick (GTK_WINDOW (pad));
357 
358 /*	xpad_pad_sync_title (pad);*/
359 }
360 
361 static void
xpad_pad_dispose(GObject * object)362 xpad_pad_dispose (GObject *object)
363 {
364 	XpadPad *pad = XPAD_PAD (object);
365 
366 	if (pad->priv->toolbar_timeout)
367 	{
368 		g_source_remove (pad->priv->toolbar_timeout);
369 		pad->priv->toolbar_timeout = 0;
370 	}
371 
372 	if (pad->priv->properties)
373 		gtk_widget_destroy (pad->priv->properties);
374 
375 	gtk_widget_destroy (pad->priv->menu);
376 	gtk_widget_destroy (pad->priv->highlight_menu);
377 
378 	G_OBJECT_CLASS (xpad_pad_parent_class)->dispose (object);
379 }
380 
381 static void
xpad_pad_finalize(GObject * object)382 xpad_pad_finalize (GObject *object)
383 {
384 	XpadPad *pad = XPAD_PAD (object);
385 
386 	g_free (pad->priv->infoname);
387 	g_free (pad->priv->contentname);
388 
389 	g_signal_handlers_disconnect_matched (xpad_settings (), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, pad);
390 
391 	G_OBJECT_CLASS (xpad_pad_parent_class)->finalize (object);
392 }
393 
394 static void
xpad_pad_notify_has_scrollbar(XpadPad * pad)395 xpad_pad_notify_has_scrollbar (XpadPad *pad)
396 {
397 	if (xpad_settings_get_has_scrollbar (xpad_settings ()))
398 	{
399 		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (pad->priv->scrollbar),
400 			GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
401 	}
402 	else
403 	{
404 		GtkAdjustment *v, *h;
405 
406 		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (pad->priv->scrollbar),
407 			GTK_POLICY_NEVER, GTK_POLICY_NEVER);
408 
409 		/* now we need to adjust view so that user can see whole pad */
410 		h = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (pad->priv->scrollbar));
411 		v = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (pad->priv->scrollbar));
412 
413 		gtk_adjustment_set_value (h, 0);
414 		gtk_adjustment_set_value (v, 0);
415 	}
416 }
417 
418 static void
xpad_pad_notify_has_decorations(XpadPad * pad)419 xpad_pad_notify_has_decorations (XpadPad *pad)
420 {
421 	gboolean decorations = xpad_settings_get_has_decorations (xpad_settings ());
422 
423 	/**
424 	 *  There are two modes of operation:  a normal mode and a 'stealth' mode.
425 	 *  If decorations are disabled, we also don't show up in the taskbar or pager.
426 	 */
427 	gtk_window_set_decorated (GTK_WINDOW (pad), decorations);
428 	gtk_window_set_skip_taskbar_hint (GTK_WINDOW (pad), !decorations);
429 	gtk_window_set_skip_pager_hint (GTK_WINDOW (pad), !decorations);
430 
431 	/* reshow_with_initial_size() seems to set the window back to a never-shown state.
432 	   This is good, as some WMs don't like us changing the above parameters mid-run,
433 	   even if we do a hide/show cycle. */
434 	gtk_window_set_default_size (GTK_WINDOW (pad), pad->priv->width, pad->priv->height);
435 	gtk_window_reshow_with_initial_size (GTK_WINDOW (pad));
436 }
437 
438 static gint
xpad_pad_text_and_toolbar_height(XpadPad * pad)439 xpad_pad_text_and_toolbar_height (XpadPad *pad)
440 {
441 	GdkRectangle rec;
442 	gint textx, texty, x, y;
443 	GtkTextIter iter;
444 
445 	gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(pad->priv->textview), &rec);
446 	gtk_text_buffer_get_end_iter(gtk_text_view_get_buffer(GTK_TEXT_VIEW(pad->priv->textview)), &iter);
447 	gtk_text_view_get_iter_location(GTK_TEXT_VIEW(pad->priv->textview), &iter, &rec);
448 	gtk_text_view_buffer_to_window_coords(GTK_TEXT_VIEW(pad->priv->textview),
449 		GTK_TEXT_WINDOW_WIDGET, rec.x + rec.width, rec.y + rec.height,
450 		&textx, &texty);
451 	gtk_widget_translate_coordinates(pad->priv->textview, GTK_WIDGET(pad), textx, texty, &x, &y);
452 
453 	return y + pad->priv->toolbar_height + gtk_container_get_border_width(GTK_CONTAINER(pad->priv->textview));
454 }
455 
456 static void
xpad_pad_show_toolbar(XpadPad * pad)457 xpad_pad_show_toolbar (XpadPad *pad)
458 {
459 	if (!GTK_WIDGET_VISIBLE (pad->priv->toolbar))
460 	{
461 		GtkRequisition req;
462 
463 		if (GTK_WIDGET (pad)->window)
464 			gdk_window_freeze_updates (GTK_WIDGET (pad)->window);
465 		gtk_widget_show (pad->priv->toolbar);
466 		if (!pad->priv->toolbar_height)
467 		{
468 			gtk_widget_size_request (pad->priv->toolbar, &req);
469 			pad->priv->toolbar_height = req.height;
470 		}
471 
472 		/* Do we have room for the toolbar without covering text? */
473 		if (xpad_pad_text_and_toolbar_height (pad) > pad->priv->height)
474 		{
475 			pad->priv->toolbar_expanded = TRUE;
476 			pad->priv->height += pad->priv->toolbar_height;
477 			gtk_window_resize (GTK_WINDOW (pad), pad->priv->width, pad->priv->height);
478 		}
479 		else
480 			pad->priv->toolbar_expanded = FALSE;
481 
482 		pad->priv->toolbar_pad_resized = FALSE;
483 
484 		if (GTK_WIDGET (pad)->window)
485 			gdk_window_thaw_updates (GTK_WIDGET (pad)->window);
486 	}
487 }
488 
489 static void
xpad_pad_hide_toolbar(XpadPad * pad)490 xpad_pad_hide_toolbar (XpadPad *pad)
491 {
492 	if (GTK_WIDGET_VISIBLE (pad->priv->toolbar))
493 	{
494 		if (GTK_WIDGET (pad)->window)
495 			gdk_window_freeze_updates (GTK_WIDGET (pad)->window);
496 		gtk_widget_hide (pad->priv->toolbar);
497 
498 		if (pad->priv->toolbar_expanded ||
499 		    (pad->priv->toolbar_pad_resized && xpad_pad_text_and_toolbar_height (pad) >= pad->priv->height))
500 		{
501 				pad->priv->height -= pad->priv->toolbar_height;
502 				gtk_window_resize (GTK_WINDOW (pad), pad->priv->width, pad->priv->height);
503 				pad->priv->toolbar_expanded = FALSE;
504 		}
505 		if (GTK_WIDGET (pad)->window)
506 			gdk_window_thaw_updates (GTK_WIDGET (pad)->window);
507 	}
508 }
509 
510 static void
xpad_pad_notify_has_toolbar(XpadPad * pad)511 xpad_pad_notify_has_toolbar (XpadPad *pad)
512 {
513 	if (xpad_settings_get_has_toolbar (xpad_settings ()))
514 	{
515 		if (!xpad_settings_get_autohide_toolbar (xpad_settings ()))
516 			xpad_pad_show_toolbar (pad);
517 	}
518 	else
519 		xpad_pad_hide_toolbar (pad);
520 }
521 
522 static gboolean
toolbar_timeout(XpadPad * pad)523 toolbar_timeout (XpadPad *pad)
524 {
525 	if (pad->priv->toolbar_timeout &&
526 	    xpad_settings_get_autohide_toolbar (xpad_settings ()) &&
527 	    xpad_settings_get_has_toolbar (xpad_settings ()))
528 		xpad_pad_hide_toolbar (pad);
529 
530 	pad->priv->toolbar_timeout = 0;
531 
532 	return FALSE;
533 }
534 
535 static void
xpad_pad_notify_autohide_toolbar(XpadPad * pad)536 xpad_pad_notify_autohide_toolbar (XpadPad *pad)
537 {
538 	if (xpad_settings_get_autohide_toolbar (xpad_settings ()))
539 	{
540 		/* Likely not to be in pad when turning setting on */
541 		if (!pad->priv->toolbar_timeout)
542 			pad->priv->toolbar_timeout = g_timeout_add (1000, (GSourceFunc) toolbar_timeout, pad);
543 	}
544 	else
545 	{
546 		if (xpad_settings_get_has_toolbar (xpad_settings ()))
547 			xpad_pad_show_toolbar(pad);
548 	}
549 }
550 
551 static gboolean
xpad_pad_enter_notify_event(GtkWidget * pad,GdkEventCrossing * event)552 xpad_pad_enter_notify_event (GtkWidget *pad, GdkEventCrossing *event)
553 {
554 	if (xpad_settings_get_has_toolbar (xpad_settings ()) &&
555 	    xpad_settings_get_autohide_toolbar (xpad_settings ()) &&
556 	    event->detail != GDK_NOTIFY_INFERIOR &&
557 	    event->mode == GDK_CROSSING_NORMAL)
558 	{
559 		XPAD_PAD (pad)->priv->toolbar_timeout = 0;
560 		xpad_pad_show_toolbar (XPAD_PAD (pad));
561 	}
562 
563 	return FALSE;
564 }
565 
566 static gboolean
xpad_pad_leave_notify_event(GtkWidget * pad,GdkEventCrossing * event)567 xpad_pad_leave_notify_event (GtkWidget *pad, GdkEventCrossing *event)
568 {
569 	if (xpad_settings_get_has_toolbar (xpad_settings ()) &&
570 	    xpad_settings_get_autohide_toolbar (xpad_settings ()) &&
571 	    event->detail != GDK_NOTIFY_INFERIOR &&
572 	    event->mode == GDK_CROSSING_NORMAL)
573 	{
574 		if (!XPAD_PAD (pad)->priv->toolbar_timeout)
575 			XPAD_PAD (pad)->priv->toolbar_timeout = g_timeout_add (1000, (GSourceFunc) toolbar_timeout, pad);
576 	}
577 
578 	return FALSE;
579 }
580 
581 static void
xpad_pad_spawn(XpadPad * pad)582 xpad_pad_spawn (XpadPad *pad)
583 {
584 	GtkWidget *newpad = xpad_pad_new (pad->priv->group);
585 	gtk_widget_show (newpad);
586 }
587 
588 static void
xpad_pad_clear(XpadPad * pad)589 xpad_pad_clear (XpadPad *pad)
590 {
591 	GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (pad->priv->textview));
592 	gtk_text_buffer_set_text (buffer, "", -1);
593 }
594 
595 void
xpad_pad_close(XpadPad * pad)596 xpad_pad_close (XpadPad *pad)
597 {
598 	gtk_widget_hide (GTK_WIDGET (pad));
599 
600 	/* If no tray and this is the last pad, we don't want to record this
601 	   pad as closed, we want to start with just this pad next open.  So
602 	   quit before we record. */
603 	if (!xpad_tray_is_open () &&
604 	    xpad_pad_group_num_visible_pads (pad->priv->group) == 0)
605 	{
606 		xpad_pad_quit (pad);
607 		return;
608 	}
609 
610 	if (pad->priv->properties)
611 		gtk_widget_destroy (pad->priv->properties);
612 
613 	xpad_pad_save_info (pad);
614 
615 	g_signal_emit (pad, signals[CLOSED], 0);
616 }
617 
618 static gboolean
should_confirm_delete(XpadPad * pad)619 should_confirm_delete (XpadPad *pad)
620 {
621 	GtkTextBuffer *buffer;
622 	GtkTextIter s, e;
623 	gchar *content;
624 	gboolean confirm;
625 
626 	if (!xpad_settings_get_confirm_destroy (xpad_settings ()))
627 		return FALSE;
628 
629 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (pad->priv->textview));
630 	gtk_text_buffer_get_bounds (buffer, &s, &e);
631 	content = gtk_text_buffer_get_text (buffer, &s, &e, FALSE);
632 
633 	confirm = strcmp (g_strstrip (content), "") != 0;
634 
635 	g_free (content);
636 
637 	return confirm;
638 }
639 
640 static void
xpad_pad_delete(XpadPad * pad)641 xpad_pad_delete (XpadPad *pad)
642 {
643 	if (should_confirm_delete (pad))
644 	{
645 		GtkWidget *dialog;
646 		gint response;
647 
648 		dialog = xpad_app_alert_new (GTK_WINDOW (pad), GTK_STOCK_DIALOG_WARNING,
649 			_("Delete this pad?"),
650 			_("All text of this pad will be irrevocably lost."));
651 
652 		if (!dialog)
653 			return;
654 
655 		gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, 1, GTK_STOCK_DELETE, 2, NULL);
656 
657 		response = gtk_dialog_run (GTK_DIALOG (dialog));
658 
659 		gtk_widget_destroy (dialog);
660 
661 		if (response != 2)
662 			return;
663 	}
664 
665 	if (pad->priv->infoname)
666 		fio_remove_file (pad->priv->infoname);
667 	if (pad->priv->contentname)
668 		fio_remove_file (pad->priv->contentname);
669 
670 	gtk_widget_destroy (GTK_WIDGET (pad));
671 }
672 
673 static void
pad_properties_sync_title(XpadPad * pad)674 pad_properties_sync_title (XpadPad *pad)
675 {
676 	gchar *title;
677 
678 	if (!pad->priv->properties)
679 		return;
680 
681 	title = g_strdup_printf (_("'%s' Properties"), gtk_window_get_title (GTK_WINDOW (pad)));
682 	gtk_window_set_title (GTK_WINDOW (pad->priv->properties), title);
683 	g_free (title);
684 }
685 
686 static void
pad_properties_destroyed(XpadPad * pad)687 pad_properties_destroyed (XpadPad *pad)
688 {
689 	if (!pad->priv->properties)
690 		return;
691 
692 	g_signal_handlers_disconnect_by_func (pad, (gpointer) pad_properties_sync_title, NULL);
693 	pad->priv->properties = NULL;
694 }
695 
696 static void
prop_notify_follow_font(XpadPad * pad)697 prop_notify_follow_font (XpadPad *pad)
698 {
699 	XpadPadProperties *prop = XPAD_PAD_PROPERTIES (pad->priv->properties);
700 
701 	xpad_text_view_set_follow_font_style (XPAD_TEXT_VIEW (pad->priv->textview), xpad_pad_properties_get_follow_font_style (prop));
702 
703 	if (!xpad_pad_properties_get_follow_font_style (prop))
704 	{
705 		const gchar *font = xpad_pad_properties_get_fontname (prop);
706 		PangoFontDescription *fontdesc;
707 
708 		fontdesc = font ? pango_font_description_from_string (font) : NULL;
709 		gtk_widget_modify_font (pad->priv->textview, fontdesc);
710 		if (fontdesc)
711 			pango_font_description_free (fontdesc);
712 	}
713 
714 	xpad_pad_save_info (pad);
715 }
716 
717 static void
prop_notify_follow_color(XpadPad * pad)718 prop_notify_follow_color (XpadPad *pad)
719 {
720 	XpadPadProperties *prop = XPAD_PAD_PROPERTIES (pad->priv->properties);
721 
722 	xpad_text_view_set_follow_color_style (XPAD_TEXT_VIEW (pad->priv->textview), xpad_pad_properties_get_follow_color_style (prop));
723 
724 	if (!xpad_pad_properties_get_follow_color_style (prop))
725 	{
726 		gtk_widget_modify_base (pad->priv->textview, GTK_STATE_NORMAL, xpad_pad_properties_get_back_color (prop));
727 		gtk_widget_modify_text (pad->priv->textview, GTK_STATE_NORMAL, xpad_pad_properties_get_text_color (prop));
728 	}
729 
730 	xpad_pad_save_info (pad);
731 }
732 
733 static void
prop_notify_text(XpadPad * pad)734 prop_notify_text (XpadPad *pad)
735 {
736 	XpadPadProperties *prop = XPAD_PAD_PROPERTIES (pad->priv->properties);
737 
738 	gtk_widget_modify_text (pad->priv->textview, GTK_STATE_NORMAL, xpad_pad_properties_get_text_color (prop));
739 
740 	xpad_pad_save_info (pad);
741 }
742 
743 static void
prop_notify_back(XpadPad * pad)744 prop_notify_back (XpadPad *pad)
745 {
746 	XpadPadProperties *prop = XPAD_PAD_PROPERTIES (pad->priv->properties);
747 
748 	gtk_widget_modify_base (pad->priv->textview, GTK_STATE_NORMAL, xpad_pad_properties_get_back_color (prop));
749 
750 	xpad_pad_save_info (pad);
751 }
752 
753 static void
prop_notify_font(XpadPad * pad)754 prop_notify_font (XpadPad *pad)
755 {
756 	XpadPadProperties *prop = XPAD_PAD_PROPERTIES (pad->priv->properties);
757 
758 	const gchar *font = xpad_pad_properties_get_fontname (prop);
759 	PangoFontDescription *fontdesc;
760 
761 	fontdesc = font ? pango_font_description_from_string (font) : NULL;
762 	gtk_widget_modify_font (pad->priv->textview, fontdesc);
763 	if (fontdesc)
764 		pango_font_description_free (fontdesc);
765 
766 	xpad_pad_save_info (pad);
767 }
768 
769 static void
xpad_pad_open_properties(XpadPad * pad)770 xpad_pad_open_properties (XpadPad *pad)
771 {
772 	GtkStyle *style;
773 	gchar *fontname;
774 
775 	if (pad->priv->properties)
776 	{
777 		gtk_window_present (GTK_WINDOW (pad->priv->properties));
778 		return;
779 	}
780 
781 	pad->priv->properties = xpad_pad_properties_new ();
782 
783 	gtk_window_set_transient_for (GTK_WINDOW (pad->priv->properties), GTK_WINDOW (pad));
784 	gtk_window_set_resizable (GTK_WINDOW (pad->priv->properties), FALSE);
785 
786 	g_signal_connect_swapped (pad->priv->properties, "destroy", G_CALLBACK (pad_properties_destroyed), pad);
787 	g_signal_connect (pad, "notify::title", G_CALLBACK (pad_properties_sync_title), NULL);
788 
789 	style = gtk_widget_get_style (pad->priv->textview);
790 	fontname = style->font_desc ? pango_font_description_to_string (style->font_desc) : NULL;
791 	g_object_set (G_OBJECT (pad->priv->properties),
792 		"follow-font-style", xpad_text_view_get_follow_font_style (XPAD_TEXT_VIEW (pad->priv->textview)),
793 		"follow-color-style", xpad_text_view_get_follow_color_style (XPAD_TEXT_VIEW (pad->priv->textview)),
794 		"back-color", &style->base[GTK_STATE_NORMAL],
795 		"text-color", &style->text[GTK_STATE_NORMAL],
796 		"fontname", fontname,
797 		NULL);
798 	g_free (fontname);
799 
800 	g_signal_connect_swapped (pad->priv->properties, "notify::follow-font-style", G_CALLBACK (prop_notify_follow_font), pad);
801 	g_signal_connect_swapped (pad->priv->properties, "notify::follow-color-style", G_CALLBACK (prop_notify_follow_color), pad);
802 	g_signal_connect_swapped (pad->priv->properties, "notify::text-color", G_CALLBACK (prop_notify_text), pad);
803 	g_signal_connect_swapped (pad->priv->properties, "notify::back-color", G_CALLBACK (prop_notify_back), pad);
804 	g_signal_connect_swapped (pad->priv->properties, "notify::fontname", G_CALLBACK (prop_notify_font), pad);
805 
806 	pad_properties_sync_title (pad);
807 
808 	gtk_widget_show (pad->priv->properties);
809 }
810 
811 static void
xpad_pad_open_preferences(XpadPad * pad)812 xpad_pad_open_preferences (XpadPad *pad)
813 {
814 	xpad_preferences_open ();
815 }
816 
817 static void
xpad_pad_quit(XpadPad * pad)818 xpad_pad_quit (XpadPad *pad)
819 {
820 	gtk_main_quit ();
821 }
822 
823 static void
xpad_pad_text_changed(XpadPad * pad,GtkTextBuffer * buffer)824 xpad_pad_text_changed (XpadPad *pad, GtkTextBuffer *buffer)
825 {
826 	/* set title */
827 	xpad_pad_sync_title (pad);
828 
829 	/* record change */
830 	save_content (pad);
831 }
832 
833 static gboolean
xpad_pad_toolbar_size_allocate(XpadPad * pad,GtkAllocation * event)834 xpad_pad_toolbar_size_allocate (XpadPad *pad, GtkAllocation *event)
835 {
836 	pad->priv->toolbar_height = event->height;
837 	return FALSE;
838 }
839 
840 static gboolean
xpad_pad_configure_event(XpadPad * pad,GdkEventConfigure * event)841 xpad_pad_configure_event (XpadPad *pad, GdkEventConfigure *event)
842 {
843 	if (!GTK_WIDGET_VISIBLE (pad))
844 		return FALSE;
845 
846 	if (pad->priv->width != event->width || pad->priv->height != event->height)
847 		pad->priv->toolbar_pad_resized = TRUE;
848 
849 	pad->priv->x = event->x;
850 	pad->priv->y = event->y;
851 	pad->priv->width = event->width;
852 	pad->priv->height = event->height;
853 	pad->priv->location_valid = TRUE;
854 
855 	xpad_pad_save_info (pad);
856 
857 	/* Sometimes when moving, if the toolbar tries to hide itself,
858 	   the window manager will not resize it correctly.  So, we make
859 	   sure not to end the timeout while moving. */
860 	if (pad->priv->toolbar_timeout)
861 	{
862 		g_source_remove (pad->priv->toolbar_timeout);
863 		pad->priv->toolbar_timeout = g_timeout_add (1000, (GSourceFunc) toolbar_timeout, pad);
864 	}
865 
866 	return FALSE;
867 }
868 
869 static gboolean
xpad_pad_window_state_event(XpadPad * pad,GdkEventWindowState * event)870 xpad_pad_window_state_event (XpadPad *pad, GdkEventWindowState *event)
871 {
872 	if (event->changed_mask & GDK_WINDOW_STATE_STICKY) {
873 		if (GTK_WIDGET_VISIBLE (pad))
874 		{
875 			pad->priv->sticky = (event->new_window_state & GDK_WINDOW_STATE_STICKY) ? TRUE : FALSE;
876 			xpad_pad_save_info (pad);
877 		}
878 	}
879 
880 	return FALSE;
881 }
882 
883 static gboolean
xpad_pad_delete_event(XpadPad * pad,GdkEvent * event)884 xpad_pad_delete_event (XpadPad *pad, GdkEvent *event)
885 {
886 	xpad_pad_close (pad);
887 
888 	return TRUE;
889 }
890 
891 static gboolean
xpad_pad_popup_menu(XpadPad * pad)892 xpad_pad_popup_menu (XpadPad *pad)
893 {
894 	xpad_pad_popup (pad, NULL);
895 
896 	return TRUE;
897 }
898 
899 static gboolean
xpad_pad_text_view_button_press_event(GtkWidget * text_view,GdkEventButton * event,XpadPad * pad)900 xpad_pad_text_view_button_press_event (GtkWidget *text_view, GdkEventButton *event, XpadPad *pad)
901 {
902 	if (event->type == GDK_BUTTON_PRESS)
903 	{
904 		switch (event->button)
905 		{
906 		case 1:
907 			if ((event->state & gtk_accelerator_get_default_mod_mask ()) == GDK_CONTROL_MASK)
908 			{
909 				gtk_window_begin_move_drag (GTK_WINDOW (pad), event->button, event->x_root, event->y_root, event->time);
910 				return TRUE;
911 			}
912 			break;
913 
914 		case 3:
915 			if ((event->state & gtk_accelerator_get_default_mod_mask ()) == GDK_CONTROL_MASK)
916 			{
917 				GdkWindowEdge edge;
918 
919 				if (gtk_widget_get_direction (GTK_WIDGET (pad)) == GTK_TEXT_DIR_LTR)
920 					edge = GDK_WINDOW_EDGE_SOUTH_EAST;
921 				else
922 					edge = GDK_WINDOW_EDGE_SOUTH_WEST;
923 
924 				gtk_window_begin_resize_drag (GTK_WINDOW (pad), edge, event->button, event->x_root, event->y_root, event->time);
925 			}
926 			else
927 			{
928 				xpad_pad_popup (pad, event);
929 			}
930 			return TRUE;
931 		}
932 	}
933 
934 	return FALSE;
935 }
936 
937 static gboolean
xpad_pad_button_press_event(XpadPad * pad,GdkEventButton * event)938 xpad_pad_button_press_event (XpadPad *pad, GdkEventButton *event)
939 {
940 	if (event->type == GDK_BUTTON_PRESS)
941 	{
942 		switch (event->button)
943 		{
944 		case 1:
945 			gtk_window_begin_move_drag (GTK_WINDOW (pad), event->button, event->x_root, event->y_root, event->time);
946 			return TRUE;
947 
948 		case 3:
949 			if ((event->state & gtk_accelerator_get_default_mod_mask ()) == GDK_CONTROL_MASK)
950 			{
951 				GdkWindowEdge edge;
952 
953 				if (gtk_widget_get_direction (GTK_WIDGET (pad)) == GTK_TEXT_DIR_LTR)
954 					edge = GDK_WINDOW_EDGE_SOUTH_EAST;
955 				else
956 					edge = GDK_WINDOW_EDGE_SOUTH_WEST;
957 
958 				gtk_window_begin_resize_drag (GTK_WINDOW (pad), edge, event->button, event->x_root, event->y_root, event->time);
959 			}
960 			else
961 			{
962 				xpad_pad_popup (pad, event);
963 			}
964 			return TRUE;
965 		}
966 	}
967 
968 	return FALSE;
969 }
970 
971 static void
xpad_pad_sync_title(XpadPad * pad)972 xpad_pad_sync_title (XpadPad *pad)
973 {
974 	GtkTextBuffer *buffer;
975 	GtkTextIter s, e;
976 	gchar *content, *end;
977 
978 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (pad->priv->textview));
979 	gtk_text_buffer_get_bounds (buffer, &s, &e);
980 	content = gtk_text_buffer_get_text (buffer, &s, &e, FALSE);
981 	end = g_utf8_strchr (content, -1, '\n');
982 	if (end)
983 		*end = '\0';
984 
985 	gtk_window_set_title (GTK_WINDOW (pad), g_strstrip (content));
986 
987 	g_free (content);
988 }
989 
990 static void
xpad_pad_set_group(XpadPad * pad,XpadPadGroup * group)991 xpad_pad_set_group (XpadPad *pad, XpadPadGroup *group)
992 {
993 	pad->priv->group = group;
994 	if (group)
995 		xpad_pad_group_add (group, GTK_WIDGET (pad));
996 }
997 
998 static XpadPadGroup *
xpad_pad_get_group(XpadPad * pad)999 xpad_pad_get_group (XpadPad *pad)
1000 {
1001 	return pad->priv->group;
1002 }
1003 
1004 static void
xpad_pad_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1005 xpad_pad_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
1006 {
1007 	XpadPad *pad;
1008 
1009 	pad = XPAD_PAD (object);
1010 
1011 	switch (prop_id)
1012 	{
1013 	case PROP_GROUP:
1014 		xpad_pad_set_group (pad, g_value_get_pointer (value));
1015 		break;
1016 
1017 	default:
1018 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1019 		break;
1020 	}
1021 }
1022 
1023 static void
xpad_pad_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1024 xpad_pad_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
1025 {
1026 	XpadPad *pad;
1027 
1028 	pad = XPAD_PAD (object);
1029 
1030 	switch (prop_id)
1031 	{
1032 	case PROP_GROUP:
1033 		g_value_set_pointer (value, xpad_pad_get_group (pad));
1034 		break;
1035 
1036 	default:
1037 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1038 		break;
1039 	}
1040 }
1041 
1042 static void
load_content(XpadPad * pad)1043 load_content (XpadPad *pad)
1044 {
1045 	gchar *content;
1046 	GtkTextBuffer *buffer;
1047 
1048 	if (!pad->priv->contentname)
1049 		return;
1050 
1051 	content = fio_get_file (pad->priv->contentname);
1052 
1053 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (pad->priv->textview));
1054 
1055 	g_signal_handlers_block_by_func (buffer, xpad_pad_text_changed, pad);
1056 
1057 	xpad_text_buffer_set_text_with_tags (XPAD_TEXT_BUFFER (buffer), content ? content : "");
1058 	g_free (content);
1059 
1060 	g_signal_handlers_unblock_by_func (buffer, xpad_pad_text_changed, pad);
1061 	xpad_pad_text_changed(pad, buffer);
1062 }
1063 
1064 static void
save_content(XpadPad * pad)1065 save_content (XpadPad *pad)
1066 {
1067 	gchar *content;
1068 	GtkTextBuffer *buffer;
1069 
1070 	/* create content file if it doesn't exist yet */
1071 	if (!pad->priv->contentname)
1072 	{
1073 		pad->priv->contentname = fio_unique_name ("content-");
1074 		if (!pad->priv->contentname)
1075 			return;
1076 	}
1077 
1078 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (pad->priv->textview));
1079 	content = xpad_text_buffer_get_text_with_tags (XPAD_TEXT_BUFFER (buffer));
1080 
1081 	fio_set_file (pad->priv->contentname, content);
1082 
1083 	g_free (content);
1084 }
1085 
1086 static void
load_info(XpadPad * pad,gboolean * show)1087 load_info (XpadPad *pad, gboolean *show)
1088 {
1089 	gboolean locked = FALSE, follow_font = TRUE, follow_color = TRUE;
1090 	gboolean hidden = FALSE;
1091 	GdkColor text = {0}, back = {0};
1092 	gchar *fontname = NULL, *oldcontentprefix;
1093 
1094 	if (!pad->priv->infoname)
1095 		return;
1096 
1097 	if (fio_get_values_from_file (pad->priv->infoname,
1098 		"i|width", &pad->priv->width,
1099 		"i|height", &pad->priv->height,
1100 		"i|x", &pad->priv->x,
1101 		"i|y", &pad->priv->y,
1102 		"b|locked", &locked,
1103 		"b|follow_font", &follow_font,
1104 		"b|follow_color", &follow_color,
1105 		"b|sticky", &pad->priv->sticky,
1106 		"b|hidden", &hidden,
1107 		"h|back_red", &back.red,
1108 		"h|back_green", &back.green,
1109 		"h|back_blue", &back.blue,
1110 		"h|text_red", &text.red,
1111 		"h|text_green", &text.green,
1112 		"h|text_blue", &text.blue,
1113 		"s|fontname", &fontname,
1114 		"s|content", &pad->priv->contentname,
1115 		NULL))
1116 		return;
1117 
1118 	pad->priv->location_valid = TRUE;
1119 	if (xpad_settings_get_has_toolbar (xpad_settings ()) &&
1120 	    !xpad_settings_get_autohide_toolbar (xpad_settings ()))
1121 	{
1122 		pad->priv->toolbar_height = 0;
1123 		xpad_pad_hide_toolbar (pad);
1124 		xpad_pad_show_toolbar (pad); /* these will resize pad at correct height */
1125 	}
1126 	else
1127 		gtk_window_resize (GTK_WINDOW (pad), pad->priv->width, pad->priv->height);
1128 	gtk_window_move (GTK_WINDOW (pad), pad->priv->x, pad->priv->y);
1129 
1130 	xpad_text_view_set_follow_font_style (XPAD_TEXT_VIEW (pad->priv->textview), follow_font);
1131 	xpad_text_view_set_follow_color_style (XPAD_TEXT_VIEW (pad->priv->textview), follow_color);
1132 
1133 	/* obsolete setting, no longer written as of xpad-2.0-b2 */
1134 	if (locked)
1135 	{
1136 		xpad_text_view_set_follow_font_style (XPAD_TEXT_VIEW (pad->priv->textview), FALSE);
1137 		xpad_text_view_set_follow_color_style (XPAD_TEXT_VIEW (pad->priv->textview), FALSE);
1138 	}
1139 
1140 	if (!xpad_text_view_get_follow_color_style (XPAD_TEXT_VIEW (pad->priv->textview)))
1141 	{
1142 		gtk_widget_modify_text (pad->priv->textview, GTK_STATE_NORMAL, &text);
1143 		gtk_widget_modify_base (pad->priv->textview, GTK_STATE_NORMAL, &back);
1144 	}
1145 
1146 	if (!xpad_text_view_get_follow_font_style (XPAD_TEXT_VIEW (pad->priv->textview)))
1147 	{
1148 		PangoFontDescription *font_desc = pango_font_description_from_string (fontname);
1149 		gtk_widget_modify_font (pad->priv->textview, font_desc);
1150 		pango_font_description_free (font_desc);
1151 	}
1152 
1153 	if (pad->priv->sticky)
1154 		gtk_window_stick (GTK_WINDOW (pad));
1155 	else
1156 		gtk_window_unstick (GTK_WINDOW (pad));
1157 
1158 	/* Special check for contentname being absolute.  A while back,
1159 	   xpad had absolute pathnames, pointing to ~/.xpad/content-*.
1160 	   Now, files are kept in ~/.config/xpad, so using old config
1161 	   files with a new xpad will break pads.  We check to see if
1162 	   contentname is old pointer and then make it relative. */
1163 	oldcontentprefix = g_build_filename (g_get_home_dir (), ".xpad", "content-", NULL);
1164 	if (g_str_has_prefix (pad->priv->contentname, oldcontentprefix))
1165 	{
1166 		gchar *oldcontent = pad->priv->contentname;
1167 		pad->priv->contentname = g_path_get_basename (oldcontent);
1168 		g_free (oldcontent);
1169 	}
1170 	g_free (oldcontentprefix);
1171 
1172 	if (show)
1173 		*show = !hidden;
1174 }
1175 
1176 void
xpad_pad_save_info(XpadPad * pad)1177 xpad_pad_save_info (XpadPad *pad)
1178 {
1179 	gint height;
1180 	GtkStyle *style;
1181 	gchar *fontname;
1182 
1183 	/* Must create pad info file if it doesn't exist yet */
1184 	if (!pad->priv->infoname)
1185 	{
1186 		pad->priv->infoname = fio_unique_name ("info-");
1187 		if (!pad->priv->infoname)
1188 			return;
1189 		gtk_window_set_role (GTK_WINDOW (pad), pad->priv->infoname);
1190 	}
1191 	/* create content file if it doesn't exist yet */
1192 	if (!pad->priv->contentname)
1193 	{
1194 		pad->priv->contentname = fio_unique_name ("content-");
1195 		if (!pad->priv->contentname)
1196 			return;
1197 	}
1198 
1199 	height = pad->priv->height;
1200 	if (GTK_WIDGET_VISIBLE (pad->priv->toolbar) && pad->priv->toolbar_expanded)
1201 		height -= pad->priv->toolbar_height;
1202 
1203 	style = gtk_widget_get_style (pad->priv->textview);
1204 	fontname = pango_font_description_to_string (style->font_desc);
1205 
1206 	fio_set_values_to_file (pad->priv->infoname,
1207 		"i|width", pad->priv->width,
1208 		"i|height", height,
1209 		"i|x", pad->priv->x,
1210 		"i|y", pad->priv->y,
1211 		"b|follow_font", xpad_text_view_get_follow_font_style (XPAD_TEXT_VIEW (pad->priv->textview)),
1212 		"b|follow_color", xpad_text_view_get_follow_color_style (XPAD_TEXT_VIEW (pad->priv->textview)),
1213 		"b|sticky", pad->priv->sticky,
1214 		"b|hidden", !GTK_WIDGET_VISIBLE (pad),
1215 		"h|back_red", style->base[GTK_STATE_NORMAL].red,
1216 		"h|back_green", style->base[GTK_STATE_NORMAL].green,
1217 		"h|back_blue", style->base[GTK_STATE_NORMAL].blue,
1218 		"h|text_red", style->text[GTK_STATE_NORMAL].red,
1219 		"h|text_green", style->text[GTK_STATE_NORMAL].green,
1220 		"h|text_blue", style->text[GTK_STATE_NORMAL].blue,
1221 		"s|fontname", fontname,
1222 		"s|content", pad->priv->contentname,
1223 		NULL);
1224 
1225 	g_free (fontname);
1226 }
1227 
1228 static void
menu_about(XpadPad * pad)1229 menu_about (XpadPad *pad)
1230 {
1231 	const gchar *artists[] = {"Michael Terry <mike@mterry.name>", NULL};
1232 	const gchar *authors[] = {"Michael Terry <mike@mterry.name>", "Jeroen Vermeulen <jtv@xs4all.nl>", NULL};
1233 	const gchar *comments = _("Sticky notes");
1234 	const gchar *copyright = "© 2001-2007 Michael Terry";
1235 	/* we use g_strdup_printf because C89 has size limits on static strings */
1236 	gchar *license = g_strdup_printf ("%s\n%s\n%s",
1237 "This program is free software; you can redistribute it and/or\n"
1238 "modify it under the terms of the GNU General Public License\n"
1239 "as published by the Free Software Foundation; either version 3\n"
1240 "of the License, or (at your option) any later version.\n"
1241 ,
1242 "This program is distributed in the hope that it will be useful,\n"
1243 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1244 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
1245 "GNU General Public License for more details.\n"
1246 ,
1247 "You should have received a copy of the GNU General Public License\n"
1248 "along with this program; if not, write to the Free Software\n"
1249 "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.");
1250 	/* Translators: please translate this as your own name and optionally email
1251 	   like so: "Your Name <your@email.com>" */
1252 	const gchar *translator_credits = _("translator-credits");
1253 	const gchar *website = "http://xpad.sourceforge.net/";
1254 
1255 	gtk_show_about_dialog (GTK_WINDOW (pad),
1256 		"artists", artists,
1257 		"authors", authors,
1258 		"comments", comments,
1259 		"copyright", copyright,
1260 		"license", license,
1261 		"logo-icon-name", PACKAGE,
1262 		"translator-credits", translator_credits,
1263 		"version", VERSION,
1264 		"website", website,
1265 		NULL);
1266 
1267 	g_free (license);
1268 }
1269 
1270 static void
menu_cut(XpadPad * pad)1271 menu_cut (XpadPad *pad)
1272 {
1273 	gtk_text_buffer_cut_clipboard (
1274 		gtk_text_view_get_buffer (GTK_TEXT_VIEW (pad->priv->textview)),
1275 		gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
1276 		TRUE);
1277 }
1278 
1279 static void
menu_copy(XpadPad * pad)1280 menu_copy (XpadPad *pad)
1281 {
1282 	gtk_text_buffer_copy_clipboard (
1283 		gtk_text_view_get_buffer (GTK_TEXT_VIEW (pad->priv->textview)),
1284 		gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1285 }
1286 
1287 static void
menu_paste(XpadPad * pad)1288 menu_paste (XpadPad *pad)
1289 {
1290 	gtk_text_buffer_paste_clipboard (
1291 		gtk_text_view_get_buffer (GTK_TEXT_VIEW (pad->priv->textview)),
1292 		gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
1293 		NULL,
1294 		TRUE);
1295 }
1296 
1297 static void
menu_show_all(XpadPad * pad)1298 menu_show_all (XpadPad *pad)
1299 {
1300 	GSList *pads, *i;
1301 
1302 	if (!pad->priv->group)
1303 		return;
1304 
1305 	pads = xpad_pad_group_get_pads (pad->priv->group);
1306 
1307 	for (i = pads; i; i = i->next)
1308 	{
1309 		if (XPAD_PAD (i->data) != pad)
1310 			gtk_window_present (GTK_WINDOW (i->data));
1311 	}
1312 	gtk_window_present (GTK_WINDOW (pad));
1313 
1314 	g_slist_free (pads);
1315 }
1316 
1317 static void
xpad_pad_close_all(XpadPad * pad)1318 xpad_pad_close_all (XpadPad *pad)
1319 {
1320 	if (!pad->priv->group)
1321 		return;
1322 
1323 	/**
1324 	 * The logic is different here depending on whether the tray is open.
1325 	 * If it is open, we just close each pad individually.  If it isn't
1326 	 * open, we do a quit.  This way, when xpad is run again, only the
1327 	 * pads open during the last 'close all' will open again.
1328 	 */
1329 	if (xpad_tray_is_open ())
1330 		xpad_pad_group_close_all (pad->priv->group);
1331 	else
1332 		xpad_pad_quit (pad);
1333 }
1334 
1335 static void
menu_show(XpadPad * pad)1336 menu_show (XpadPad *pad)
1337 {
1338 	gtk_window_present (GTK_WINDOW (pad));
1339 }
1340 
1341 static void
menu_toggle_tag(XpadPad * pad,const gchar * name)1342 menu_toggle_tag (XpadPad *pad, const gchar *name)
1343 {
1344 	GtkTextBuffer *buffer;
1345 	GtkTextTagTable *table;
1346 	GtkTextTag *tag;
1347 	GtkTextIter start, end, i;
1348 	gboolean all_tagged;
1349 
1350 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (pad->priv->textview));
1351 	table = gtk_text_buffer_get_tag_table (buffer);
1352 	tag = gtk_text_tag_table_lookup (table, name);
1353 	gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
1354 
1355 	if (!tag)
1356 	{
1357 		g_print ("Tag not found in table %p\n", (void *) table);
1358 		return;
1359 	}
1360 
1361 	for (all_tagged = TRUE, i = start; !gtk_text_iter_equal (&i, &end); gtk_text_iter_forward_char (&i))
1362 	{
1363 		if (!gtk_text_iter_has_tag (&i, tag))
1364 		{
1365 			all_tagged = FALSE;
1366 			break;
1367 		}
1368 	}
1369 
1370 	if (all_tagged)
1371 		gtk_text_buffer_remove_tag (buffer, tag, &start, &end);
1372 	else
1373 		gtk_text_buffer_apply_tag (buffer, tag, &start, &end);
1374 
1375 	save_content (pad);
1376 }
1377 
1378 static void
menu_bold(XpadPad * pad)1379 menu_bold (XpadPad *pad)
1380 {
1381 	menu_toggle_tag (pad, "bold");
1382 }
1383 
1384 static void
menu_italic(XpadPad * pad)1385 menu_italic (XpadPad *pad)
1386 {
1387 	menu_toggle_tag (pad, "italic");
1388 }
1389 
1390 static void
menu_underline(XpadPad * pad)1391 menu_underline (XpadPad *pad)
1392 {
1393 	menu_toggle_tag (pad, "underline");
1394 }
1395 
1396 static void
menu_strikethrough(XpadPad * pad)1397 menu_strikethrough (XpadPad *pad)
1398 {
1399 	menu_toggle_tag (pad, "strikethrough");
1400 }
1401 
1402 static void
menu_sticky(XpadPad * pad,GtkCheckMenuItem * check)1403 menu_sticky (XpadPad *pad, GtkCheckMenuItem *check)
1404 {
1405 	if (gtk_check_menu_item_get_active (check))
1406 		gtk_window_stick (GTK_WINDOW (pad));
1407 	else
1408 		gtk_window_unstick (GTK_WINDOW (pad));
1409 }
1410 
1411 static void
menu_toolbar(XpadPad * pad,GtkCheckMenuItem * check)1412 menu_toolbar (XpadPad *pad, GtkCheckMenuItem *check)
1413 {
1414 	xpad_settings_set_has_toolbar (xpad_settings (), gtk_check_menu_item_get_active (check));
1415 }
1416 
1417 static void
menu_scrollbar(XpadPad * pad,GtkCheckMenuItem * check)1418 menu_scrollbar (XpadPad *pad, GtkCheckMenuItem *check)
1419 {
1420 	xpad_settings_set_has_scrollbar (xpad_settings (), gtk_check_menu_item_get_active (check));
1421 }
1422 
1423 static void
menu_autohide(XpadPad * pad,GtkCheckMenuItem * check)1424 menu_autohide (XpadPad *pad, GtkCheckMenuItem *check)
1425 {
1426 	xpad_settings_set_autohide_toolbar (xpad_settings (), gtk_check_menu_item_get_active (check));
1427 }
1428 
1429 static void
menu_decorated(XpadPad * pad,GtkCheckMenuItem * check)1430 menu_decorated (XpadPad *pad, GtkCheckMenuItem *check)
1431 {
1432 	xpad_settings_set_has_decorations (xpad_settings (), gtk_check_menu_item_get_active (check));
1433 }
1434 
1435 static gint
menu_title_compare(GtkWindow * a,GtkWindow * b)1436 menu_title_compare (GtkWindow *a, GtkWindow *b)
1437 {
1438 	gchar *title_a = g_utf8_casefold (gtk_window_get_title (a), -1);
1439 	gchar *title_b = g_utf8_casefold (gtk_window_get_title (b), -1);
1440 
1441 	gint rv = g_utf8_collate (title_a, title_b);
1442 
1443 	g_free (title_a);
1444 	g_free (title_b);
1445 
1446 	return rv;
1447 }
1448 
1449 #define MENU_ADD(mnemonic, image, key, mask, callback) {\
1450 	item = gtk_image_menu_item_new_with_mnemonic (mnemonic);\
1451 	if (image) {\
1452 		GtkWidget *imgwidget = gtk_image_new_from_stock (image, GTK_ICON_SIZE_MENU);\
1453 		gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), imgwidget);\
1454 	}\
1455 	g_signal_connect_swapped (item, "activate", G_CALLBACK (callback), pad);\
1456 	if (key)\
1457 		gtk_widget_add_accelerator(item, "activate", accel_group, key, mask, GTK_ACCEL_VISIBLE);\
1458 	gtk_container_add (GTK_CONTAINER (menu), item);\
1459 	gtk_widget_show (item);\
1460 	}
1461 
1462 #define MENU_ADD_STOCK(stock, callback) {\
1463 	item = gtk_image_menu_item_new_from_stock (stock, accel_group);\
1464 	g_signal_connect_swapped (item, "activate", G_CALLBACK (callback), pad);\
1465 	gtk_container_add (GTK_CONTAINER (menu), item);\
1466 	gtk_widget_show (item);\
1467 	}
1468 
1469 #define MENU_ADD_CHECK(mnemonic, active, callback) {\
1470 	item = gtk_check_menu_item_new_with_mnemonic (mnemonic);\
1471 	gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), active);\
1472 	g_signal_connect_swapped (item, "toggled", G_CALLBACK (callback), pad);\
1473 	gtk_container_add (GTK_CONTAINER (menu), item);\
1474 	gtk_widget_show (item);\
1475 	}
1476 
1477 #define MENU_ADD_SEP() {\
1478 	item = gtk_separator_menu_item_new ();\
1479 	gtk_container_add (GTK_CONTAINER (menu), item);\
1480 	gtk_widget_show (item);\
1481 	}
1482 
1483 static GtkWidget *
menu_get_popup_no_highlight(XpadPad * pad,GtkAccelGroup * accel_group)1484 menu_get_popup_no_highlight (XpadPad *pad, GtkAccelGroup *accel_group)
1485 {
1486 	GtkWidget *uppermenu, *menu, *item;
1487 
1488 	uppermenu = gtk_menu_new ();
1489 	gtk_menu_set_accel_group (GTK_MENU (uppermenu), accel_group);
1490 
1491 	item = gtk_menu_item_new_with_mnemonic (_("_Pad"));
1492 	gtk_container_add (GTK_CONTAINER (uppermenu), item);
1493 	gtk_widget_show (item);
1494 
1495 	menu = gtk_menu_new ();
1496 	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
1497 
1498 	MENU_ADD_STOCK (GTK_STOCK_NEW, xpad_pad_spawn);
1499 	MENU_ADD_SEP ();
1500 	MENU_ADD_CHECK (_("Show on _All Workspaces"), pad->priv->sticky, menu_sticky);
1501 	g_object_set_data (G_OBJECT (uppermenu), "sticky", item);
1502 	MENU_ADD_STOCK (GTK_STOCK_PROPERTIES, xpad_pad_open_properties);
1503 	MENU_ADD_SEP ();
1504 	MENU_ADD_STOCK (GTK_STOCK_CLOSE, xpad_pad_close);
1505 	MENU_ADD_STOCK (GTK_STOCK_DELETE, xpad_pad_delete);
1506 
1507 
1508 	item = gtk_menu_item_new_with_mnemonic (_("_Edit"));
1509 	gtk_container_add (GTK_CONTAINER (uppermenu), item);
1510 	gtk_widget_show (item);
1511 
1512 	menu = gtk_menu_new ();
1513 	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
1514 
1515 	MENU_ADD_STOCK (GTK_STOCK_PASTE, menu_paste);
1516 	g_object_set_data (G_OBJECT (uppermenu), "paste", item);
1517 	MENU_ADD_SEP ();
1518 	MENU_ADD_STOCK (GTK_STOCK_PREFERENCES, xpad_pad_open_preferences);
1519 
1520 
1521 	item = gtk_menu_item_new_with_mnemonic (_("_View"));
1522 	gtk_container_add (GTK_CONTAINER (uppermenu), item);
1523 	gtk_widget_show (item);
1524 
1525 	menu = gtk_menu_new ();
1526 	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
1527 
1528 	MENU_ADD_CHECK (_("_Toolbar"), xpad_settings_get_has_toolbar (xpad_settings ()), menu_toolbar);
1529 	MENU_ADD_CHECK (_("_Autohide Toolbar"), xpad_settings_get_autohide_toolbar (xpad_settings ()), menu_autohide);
1530 	gtk_widget_set_sensitive (item, xpad_settings_get_has_toolbar (xpad_settings ()));
1531 	MENU_ADD_CHECK (_("_Scrollbar"), xpad_settings_get_has_scrollbar (xpad_settings ()), menu_scrollbar);
1532 	MENU_ADD_CHECK (_("_Window Decorations"), xpad_settings_get_has_decorations (xpad_settings ()), menu_decorated);
1533 
1534 
1535 	item = gtk_menu_item_new_with_mnemonic (_("_Notes"));
1536 	gtk_container_add (GTK_CONTAINER (uppermenu), item);
1537 	gtk_widget_show (item);
1538 
1539 	menu = gtk_menu_new ();
1540 	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
1541 	g_object_set_data (G_OBJECT (uppermenu), "notes-menu", menu);
1542 
1543 	MENU_ADD (_("_Show All"), NULL, 0, 0, menu_show_all);
1544 	MENU_ADD (_("_Close All"), NULL, 0, 0, xpad_pad_close_all);
1545 
1546 	/* The rest of the notes menu will get set up in the prep function below */
1547 
1548 	item = gtk_menu_item_new_with_mnemonic (_("_Help"));
1549 	gtk_container_add (GTK_CONTAINER (uppermenu), item);
1550 	gtk_widget_show (item);
1551 
1552 	menu = gtk_menu_new ();
1553 	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
1554 
1555 	MENU_ADD (_("_Contents"), GTK_STOCK_HELP, GDK_F1, 0, show_help);
1556 	MENU_ADD (_("_About"), GTK_STOCK_ABOUT, 0, 0, menu_about);
1557 
1558 	return uppermenu;
1559 }
1560 
1561 static void
menu_prep_popup_no_highlight(XpadPad * current_pad,GtkWidget * uppermenu)1562 menu_prep_popup_no_highlight (XpadPad *current_pad, GtkWidget *uppermenu)
1563 {
1564 	GtkWidget *menu, *item;
1565 	GtkClipboard *clipboard;
1566 
1567 	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1568 
1569 	item = g_object_get_data (G_OBJECT (uppermenu), "paste");
1570 	if (item)
1571 		gtk_widget_set_sensitive (item, gtk_clipboard_wait_is_text_available (clipboard));
1572 
1573 	item = g_object_get_data (G_OBJECT (uppermenu), "sticky");
1574 	if (item) {
1575 		g_signal_handlers_block_by_func (item, menu_sticky, current_pad);
1576 		gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), current_pad->priv->sticky);
1577 		g_signal_handlers_unblock_by_func (item, menu_sticky, current_pad);
1578 	}
1579 
1580 	menu = g_object_get_data (G_OBJECT (uppermenu), "notes-menu");
1581 	if (menu)
1582 	{
1583 		gint n = 1;
1584 		gchar *key;
1585 
1586 		/* Remove old menu */
1587 		item = g_object_get_data (G_OBJECT (menu), "notes-sep");
1588 		while (item)
1589 		{
1590 			gtk_container_remove (GTK_CONTAINER (menu), item);
1591 			key = g_strdup_printf ("notes-%i", n++);
1592 			item = g_object_get_data (G_OBJECT (menu), key);
1593 			g_free (key);
1594 		}
1595 	}
1596 	if (menu && current_pad->priv->group)
1597 	{
1598 		GSList *pads, *l;
1599 		gint n;
1600 		GtkAccelGroup *accel_group = gtk_menu_get_accel_group (GTK_MENU (uppermenu));
1601 
1602 		MENU_ADD_SEP ();
1603 		g_object_set_data (G_OBJECT (menu), "notes-sep", item);
1604 
1605 		/**
1606 		 * Order pads according to title.
1607 		 */
1608 		pads = xpad_pad_group_get_pads (current_pad->priv->group);
1609 
1610 		pads = g_slist_sort (pads, (GCompareFunc) menu_title_compare);
1611 
1612 		/**
1613 		 * Populate list of windows.
1614 		 */
1615 		for (l = pads, n = 1; l; l = l->next, n++)
1616 		{
1617 			gchar *title;
1618 			gchar *tmp_title;
1619 			gchar *key;
1620 			GtkWidget *pad = GTK_WIDGET (l->data);
1621 
1622 			key = g_strdup_printf ("notes-%i", n);
1623 			tmp_title = g_strdup (gtk_window_get_title (GTK_WINDOW (pad)));
1624 			str_replace_tokens (&tmp_title, '_', "__");
1625 			if (n < 10)
1626 				title = g_strdup_printf ("_%i. %s", n, tmp_title);
1627 			else
1628 				title = g_strdup_printf ("%i. %s", n, tmp_title);
1629 			g_free (tmp_title);
1630 
1631 			MENU_ADD (title, NULL, 0, 0, menu_show);
1632 			g_object_set_data (G_OBJECT (menu), key, item);
1633 
1634 			g_free (title);
1635 			g_free (key);
1636 		}
1637 		g_slist_free (pads);
1638 	}
1639 }
1640 
1641 static GtkWidget *
menu_get_popup_highlight(XpadPad * pad,GtkAccelGroup * accel_group)1642 menu_get_popup_highlight (XpadPad *pad, GtkAccelGroup *accel_group)
1643 {
1644 	GtkWidget *menu, *item;
1645 
1646 	menu = gtk_menu_new ();
1647 	gtk_menu_set_accel_group (GTK_MENU (menu), accel_group);
1648 
1649 	MENU_ADD_STOCK (GTK_STOCK_CUT, menu_cut);
1650 	MENU_ADD_STOCK (GTK_STOCK_COPY, menu_copy);
1651 	MENU_ADD_STOCK (GTK_STOCK_PASTE, menu_paste);
1652 	g_object_set_data (G_OBJECT (menu), "paste", item);
1653 	MENU_ADD_SEP ();
1654 	MENU_ADD_STOCK (GTK_STOCK_BOLD, menu_bold);
1655 	MENU_ADD_STOCK (GTK_STOCK_ITALIC, menu_italic);
1656 	MENU_ADD_STOCK (GTK_STOCK_UNDERLINE, menu_underline);
1657 	MENU_ADD_STOCK (GTK_STOCK_STRIKETHROUGH, menu_strikethrough);
1658 
1659 	return menu;
1660 }
1661 
1662 static void
menu_prep_popup_highlight(XpadPad * pad,GtkWidget * menu)1663 menu_prep_popup_highlight (XpadPad *pad, GtkWidget *menu)
1664 {
1665 	GtkWidget *item;
1666 	GtkClipboard *clipboard;
1667 
1668 	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1669 
1670 	item = g_object_get_data (G_OBJECT (menu), "paste");
1671 	if (item)
1672 		gtk_widget_set_sensitive (item, gtk_clipboard_wait_is_text_available (clipboard));
1673 }
1674 
1675 static void
menu_popup(GtkWidget * menu,XpadPad * pad)1676 menu_popup (GtkWidget *menu, XpadPad *pad)
1677 {
1678 	g_signal_handlers_block_matched (pad, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, (gpointer) xpad_pad_leave_notify_event, NULL);
1679 	pad->priv->toolbar_timeout = 0;
1680 }
1681 
1682 static void
menu_popdown(GtkWidget * menu,XpadPad * pad)1683 menu_popdown (GtkWidget *menu, XpadPad *pad)
1684 {
1685 	GdkRectangle rect;
1686 
1687 	g_signal_handlers_unblock_matched (pad, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, (gpointer) xpad_pad_leave_notify_event, NULL);
1688 
1689 	/**
1690 	 * We must check if we disabled off of pad and start the timeout if so.
1691 	 */
1692 	gdk_window_get_pointer (GTK_WIDGET (pad)->window, &rect.x, &rect.y, NULL);
1693 	rect.width = 1;
1694 	rect.height = 1;
1695 
1696 	if (!pad->priv->toolbar_timeout &&
1697 	    !gtk_widget_intersect (GTK_WIDGET (pad), &rect, NULL))
1698 		pad->priv->toolbar_timeout = g_timeout_add (1000, (GSourceFunc) toolbar_timeout, pad);
1699 }
1700 
1701 static void
xpad_pad_popup_deactivate(GtkWidget * menu,XpadPad * pad)1702 xpad_pad_popup_deactivate (GtkWidget *menu, XpadPad *pad)
1703 {
1704 	menu_popdown (GTK_WIDGET (menu), pad);
1705 }
1706 
1707 static void
xpad_pad_toolbar_popup(GtkWidget * toolbar,GtkMenu * menu,XpadPad * pad)1708 xpad_pad_toolbar_popup (GtkWidget *toolbar, GtkMenu *menu, XpadPad *pad)
1709 {
1710 	menu_popup (GTK_WIDGET (menu), pad);
1711 }
1712 
1713 static void
xpad_pad_toolbar_popdown(GtkWidget * toolbar,GtkMenu * menu,XpadPad * pad)1714 xpad_pad_toolbar_popdown (GtkWidget *toolbar, GtkMenu *menu, XpadPad *pad)
1715 {
1716 	menu_popdown (GTK_WIDGET (menu), pad);
1717 }
1718 
1719 static void
xpad_pad_popup(XpadPad * pad,GdkEventButton * event)1720 xpad_pad_popup (XpadPad *pad, GdkEventButton *event)
1721 {
1722 	GtkTextBuffer *buffer;
1723 	GtkWidget *menu;
1724 
1725 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (pad->priv->textview));
1726 
1727 	if (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL))
1728 	{
1729 		menu = pad->priv->highlight_menu;
1730 		menu_prep_popup_highlight (pad, menu);
1731 	}
1732 	else
1733 	{
1734 		menu = pad->priv->menu;
1735 		menu_prep_popup_no_highlight (pad, menu);
1736 	}
1737 
1738 	if (!menu)
1739 		return;
1740 
1741 	menu_popup (GTK_WIDGET (menu), pad);
1742 
1743 	if (event)
1744 		gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, event->button, event->time);
1745 	else
1746 		gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ());
1747 }
1748