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