Updated Korean translation
[yelp.git] / src / yelp-window.c
blob5dbf6ebfb0e7c88f814fed0f13127dd52f2b8550
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Copyright (C) 2010 Shaun McCance <shaunm@gnome.org>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
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 GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18 * Author: Shaun McCance <shaunm@gnome.org>
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <math.h>
27 #include <gdk/gdkkeysyms.h>
28 #include <glib/gi18n.h>
29 #include <gio/gio.h>
30 #include <gtk/gtk.h>
32 #include "yelp-search-entry.h"
33 #include "yelp-settings.h"
34 #include "yelp-uri.h"
35 #include "yelp-view.h"
37 #include "yelp-application.h"
38 #include "yelp-window.h"
40 static void yelp_window_dispose (GObject *object);
41 static void yelp_window_finalize (GObject *object);
42 static void yelp_window_get_property (GObject *object,
43 guint prop_id,
44 GValue *value,
45 GParamSpec *pspec);
46 static void yelp_window_set_property (GObject *object,
47 guint prop_id,
48 const GValue *value,
49 GParamSpec *pspec);
51 static void window_construct (YelpWindow *window);
53 static gboolean window_map_event (YelpWindow *window,
54 GdkEvent *event,
55 gpointer user_data);
56 static gboolean window_configure_event (YelpWindow *window,
57 GdkEventConfigure *event,
58 gpointer user_data);
59 static void window_drag_received (YelpWindow *window,
60 GdkDragContext *context,
61 gint x,
62 gint y,
63 GtkSelectionData *data,
64 guint info,
65 guint time,
66 gpointer userdata);
67 static gboolean window_resize_signal (YelpWindow *window);
68 static gboolean window_key_press (YelpWindow *window,
69 GdkEventKey *event,
70 gpointer userdata);
72 static void bookmark_activated (GtkListBox *box,
73 GtkListBoxRow *row,
74 YelpWindow *window);
75 static void bookmark_removed (GtkButton *button,
76 YelpWindow *window);
77 static void bookmark_added (GtkButton *button,
78 YelpWindow *window);
80 static void app_bookmarks_changed (YelpApplication *app,
81 const gchar *doc_uri,
82 YelpWindow *window);
83 static void window_set_bookmarks (YelpWindow *window,
84 const gchar *doc_uri);
85 static void window_set_bookmark_buttons (YelpWindow *window);
86 static void window_search_mode (GtkSearchBar *search_bar,
87 GParamSpec *pspec,
88 YelpWindow *window);
90 static void action_new_window (GSimpleAction *action,
91 GVariant *parameter,
92 gpointer userdata);
93 static void action_close_window (GSimpleAction *action,
94 GVariant *parameter,
95 gpointer userdata);
96 static void action_search (GSimpleAction *action,
97 GVariant *parameter,
98 gpointer userdata);
99 static void action_find (GSimpleAction *action,
100 GVariant *parameter,
101 gpointer userdata);
102 static void action_go_all (GSimpleAction *action,
103 GVariant *parameter,
104 gpointer userdata);
105 static void action_ctrll (GSimpleAction *action,
106 GVariant *parameter,
107 gpointer userdata);
110 static gboolean find_entry_key_press (GtkEntry *entry,
111 GdkEventKey *event,
112 YelpWindow *window);
113 static void find_entry_changed (GtkEntry *entry,
114 YelpWindow *window);
115 static void find_prev_clicked (GtkButton *button,
116 YelpWindow *window);
117 static void find_next_clicked (GtkButton *button,
118 YelpWindow *window);
120 static void view_new_window (YelpView *view,
121 YelpUri *uri,
122 YelpWindow *window);
123 static void view_loaded (YelpView *view,
124 YelpWindow *window);
125 static void view_is_loading_changed (YelpView *view,
126 GParamSpec *pspec,
127 YelpWindow *window);
128 static void view_uri_selected (YelpView *view,
129 GParamSpec *pspec,
130 YelpWindow *window);
131 static void view_root_title (YelpView *view,
132 GParamSpec *pspec,
133 YelpWindow *window);
134 static void ctrll_entry_activate (GtkEntry *entry,
135 YelpWindow *window);
136 static gboolean ctrll_entry_key_press (GtkWidget *widget,
137 GdkEventKey *event,
138 YelpWindow *window);
140 enum {
141 PROP_0,
142 PROP_APPLICATION
145 enum {
146 RESIZE_EVENT,
147 LAST_SIGNAL
150 #define MAX_FIND_MATCHES 1000
152 static guint signals[LAST_SIGNAL] = { 0 };
154 G_DEFINE_TYPE (YelpWindow, yelp_window, GTK_TYPE_APPLICATION_WINDOW)
155 #define GET_PRIV(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_WINDOW, YelpWindowPrivate))
157 typedef struct _YelpWindowPrivate YelpWindowPrivate;
158 struct _YelpWindowPrivate {
159 YelpApplication *application;
161 gulong bookmarks_changed;
163 /* no refs on these, owned by containers */
164 GtkWidget *header;
165 GtkWidget *vbox_view;
166 GtkWidget *vbox_full;
167 GtkWidget *search_bar;
168 GtkWidget *search_entry;
169 GtkWidget *find_bar;
170 GtkWidget *find_entry;
171 GtkWidget *bookmark_menu;
172 GtkWidget *bookmark_sw;
173 GtkWidget *bookmark_list;
174 GtkWidget *bookmark_add;
175 GtkWidget *bookmark_remove;
176 YelpView *view;
178 GtkWidget *ctrll_entry;
180 gchar *doc_uri;
182 guint resize_signal;
183 gint width;
184 gint height;
186 gboolean configured;
188 gboolean use_header;
191 static void
192 yelp_window_init (YelpWindow *window)
194 g_signal_connect (window, "configure-event", G_CALLBACK (window_configure_event), NULL);
195 g_signal_connect (window, "map-event", G_CALLBACK (window_map_event), NULL);
198 static void
199 yelp_window_class_init (YelpWindowClass *klass)
201 GObjectClass *object_class = G_OBJECT_CLASS (klass);
203 object_class->dispose = yelp_window_dispose;
204 object_class->finalize = yelp_window_finalize;
205 object_class->get_property = yelp_window_get_property;
206 object_class->set_property = yelp_window_set_property;
208 g_object_class_install_property (object_class,
209 PROP_APPLICATION,
210 g_param_spec_object ("application",
211 _("Application"),
212 _("A YelpApplication instance that controls this window"),
213 YELP_TYPE_APPLICATION,
214 G_PARAM_CONSTRUCT_ONLY |
215 G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
216 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
218 signals[RESIZE_EVENT] =
219 g_signal_new ("resized",
220 G_OBJECT_CLASS_TYPE (klass),
221 G_SIGNAL_RUN_LAST,
222 0, NULL, NULL,
223 g_cclosure_marshal_VOID__VOID,
224 G_TYPE_NONE, 0);
226 g_type_class_add_private (klass, sizeof (YelpWindowPrivate));
229 static void
230 yelp_window_dispose (GObject *object)
232 YelpWindowPrivate *priv = GET_PRIV (object);
234 if (priv->bookmarks_changed) {
235 g_signal_handler_disconnect (priv->application, priv->bookmarks_changed);
236 priv->bookmarks_changed = 0;
239 if (priv->ctrll_entry) {
240 g_object_unref (priv->ctrll_entry);
241 priv->ctrll_entry = NULL;
244 G_OBJECT_CLASS (yelp_window_parent_class)->dispose (object);
247 static void
248 yelp_window_finalize (GObject *object)
250 YelpWindowPrivate *priv = GET_PRIV (object);
251 g_free (priv->doc_uri);
252 G_OBJECT_CLASS (yelp_window_parent_class)->finalize (object);
255 static void
256 yelp_window_get_property (GObject *object,
257 guint prop_id,
258 GValue *value,
259 GParamSpec *pspec)
261 YelpWindowPrivate *priv = GET_PRIV (object);
262 switch (prop_id) {
263 case PROP_APPLICATION:
264 g_value_set_object (value, priv->application);
265 break;
266 default:
267 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
268 break;
272 static void
273 yelp_window_set_property (GObject *object,
274 guint prop_id,
275 const GValue *value,
276 GParamSpec *pspec)
278 YelpWindowPrivate *priv = GET_PRIV (object);
279 switch (prop_id) {
280 case PROP_APPLICATION:
281 priv->application = g_value_get_object (value);
282 window_construct ((YelpWindow *) object);
283 break;
284 default:
285 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
286 break;
290 static void
291 window_construct (YelpWindow *window)
293 GtkWidget *box, *button;
294 GtkWidget *frame;
295 GtkCssProvider *css;
296 GtkSizeGroup *size_group;
297 GMenu *menu, *section;
298 YelpWindowPrivate *priv = GET_PRIV (window);
299 GtkStyleContext *headerbar_context;
301 const GActionEntry entries[] = {
302 { "yelp-window-new", action_new_window, NULL, NULL, NULL },
303 { "yelp-window-close", action_close_window, NULL, NULL, NULL },
304 { "yelp-window-search", action_search, NULL, NULL, NULL },
305 { "yelp-window-find", action_find, NULL, NULL, NULL },
306 { "yelp-window-go-all", action_go_all, NULL, NULL, NULL },
307 { "yelp-window-ctrll", action_ctrll, NULL, NULL, NULL },
310 gtk_window_set_icon_name (GTK_WINDOW (window), "help-browser");
312 g_object_get (gtk_settings_get_default (),
313 "gtk-dialogs-use-header", &priv->use_header,
314 NULL);
316 priv->view = (YelpView *) yelp_view_new ();
318 g_action_map_add_action_entries (G_ACTION_MAP (window),
319 entries, G_N_ELEMENTS (entries), window);
320 yelp_view_register_actions (priv->view, G_ACTION_MAP (window));
322 priv->vbox_full = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3);
323 gtk_container_add (GTK_CONTAINER (window), priv->vbox_full);
325 priv->header = gtk_header_bar_new ();
326 if (priv->use_header) {
327 gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (priv->header), TRUE);
328 gtk_window_set_titlebar (GTK_WINDOW (window), priv->header);
329 } else {
330 headerbar_context = gtk_widget_get_style_context (GTK_WIDGET (priv->header));
331 gtk_container_add (GTK_CONTAINER (priv->vbox_full), GTK_WIDGET (priv->header));
332 gtk_style_context_remove_class (headerbar_context, "header-bar");
333 gtk_style_context_add_class (headerbar_context, GTK_STYLE_CLASS_TOOLBAR);
334 gtk_style_context_add_class (headerbar_context, GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
337 /** Back/Forward **/
338 box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
339 gtk_style_context_add_class (gtk_widget_get_style_context (box), "linked");
340 gtk_header_bar_pack_start (GTK_HEADER_BAR (priv->header), box);
342 button = gtk_button_new_from_icon_name ("go-previous-symbolic", GTK_ICON_SIZE_MENU);
343 gtk_widget_set_tooltip_text (button, _("Back"));
344 gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
345 gtk_style_context_add_class (gtk_widget_get_style_context (button), "image-button");
346 gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
347 g_object_set (button, "action-name", "win.yelp-view-go-back", NULL);
349 button = gtk_button_new_from_icon_name ("go-next-symbolic", GTK_ICON_SIZE_MENU);
350 gtk_widget_set_tooltip_text (button, _("Forward"));
351 gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
352 gtk_style_context_add_class (gtk_widget_get_style_context (button), "image-button");
353 gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
354 g_object_set (button, "action-name", "win.yelp-view-go-forward", NULL);
356 /** Gear Menu **/
357 button = gtk_menu_button_new ();
358 gtk_menu_button_set_direction (GTK_MENU_BUTTON (button), GTK_ARROW_NONE);
359 gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
360 gtk_style_context_add_class (gtk_widget_get_style_context (button), "image-button");
361 gtk_widget_set_tooltip_text (button, _("Menu"));
362 gtk_header_bar_pack_end (GTK_HEADER_BAR (priv->header), button);
364 menu = g_menu_new ();
365 section = g_menu_new ();
366 g_menu_append (section, _("Find…"), "win.yelp-window-find");
367 g_menu_append (section, _("Print…"), "win.yelp-view-print");
368 g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
369 g_object_unref (section);
371 section = g_menu_new ();
372 g_menu_append (section, _("Previous Page"), "win.yelp-view-go-previous");
373 g_menu_append (section, _("Next Page"), "win.yelp-view-go-next");
374 g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
375 g_object_unref (section);
377 section = g_menu_new ();
378 g_menu_append (section, _("All Help"), "win.yelp-window-go-all");
379 g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
380 g_object_unref (section);
382 gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (button), G_MENU_MODEL (menu));
384 /** Search **/
385 priv->vbox_view = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
386 gtk_box_pack_start (GTK_BOX (priv->vbox_full), priv->vbox_view, TRUE, TRUE, 0);
388 priv->search_bar = gtk_search_bar_new ();
389 gtk_box_pack_start (GTK_BOX (priv->vbox_view), priv->search_bar, FALSE, FALSE, 0);
390 priv->search_entry = yelp_search_entry_new (priv->view,
391 YELP_BOOKMARKS (priv->application));
392 gtk_entry_set_width_chars (GTK_ENTRY (priv->search_entry), 50);
393 gtk_container_add (GTK_CONTAINER (priv->search_bar), priv->search_entry);
394 button = gtk_toggle_button_new ();
395 gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
396 gtk_style_context_add_class (gtk_widget_get_style_context (button), "image-button");
397 gtk_button_set_image (GTK_BUTTON (button),
398 gtk_image_new_from_icon_name ("edit-find-symbolic",
399 GTK_ICON_SIZE_MENU));
400 gtk_widget_set_tooltip_text (button, _("Search (Ctrl+S)"));
401 g_object_bind_property (button, "active",
402 priv->search_bar, "search-mode-enabled",
403 G_BINDING_BIDIRECTIONAL);
404 g_signal_connect (priv->search_bar, "notify::search-mode-enabled",
405 G_CALLBACK (window_search_mode), window);
406 gtk_header_bar_pack_end (GTK_HEADER_BAR (priv->header), button);
408 g_signal_connect (window, "key-press-event", G_CALLBACK (window_key_press), NULL);
410 /** Bookmarks **/
411 button = gtk_menu_button_new ();
412 gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
413 gtk_style_context_add_class (gtk_widget_get_style_context (button), "image-button");
414 gtk_button_set_image (GTK_BUTTON (button),
415 gtk_image_new_from_icon_name ("user-bookmarks-symbolic",
416 GTK_ICON_SIZE_MENU));
417 gtk_widget_set_tooltip_text (button, _("Bookmarks"));
418 gtk_header_bar_pack_end (GTK_HEADER_BAR (priv->header), button);
420 priv->bookmark_menu = gtk_popover_new (button);
421 g_object_set (priv->bookmark_menu, "border-width", 12, NULL);
422 gtk_menu_button_set_popover (GTK_MENU_BUTTON (button), priv->bookmark_menu);
424 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
425 gtk_container_add (GTK_CONTAINER (priv->bookmark_menu), box);
426 priv->bookmark_sw = gtk_scrolled_window_new (NULL, NULL);
427 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->bookmark_sw),
428 GTK_POLICY_NEVER,
429 GTK_POLICY_AUTOMATIC);
430 g_object_set (priv->bookmark_sw, "height-request", 200, NULL);
431 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->bookmark_sw), GTK_SHADOW_IN);
432 gtk_box_pack_start (GTK_BOX (box), priv->bookmark_sw, TRUE, TRUE, 0);
433 priv->bookmark_list = gtk_list_box_new ();
434 button = gtk_label_new (_("No bookmarks"));
435 gtk_widget_show (button);
436 gtk_list_box_set_placeholder (GTK_LIST_BOX (priv->bookmark_list), button);
437 g_object_set (priv->bookmark_list, "selection-mode", GTK_SELECTION_NONE, NULL);
438 g_signal_connect (priv->bookmark_list, "row-activated",
439 G_CALLBACK (bookmark_activated), window);
440 gtk_container_add (GTK_CONTAINER (priv->bookmark_sw), priv->bookmark_list);
442 priv->bookmark_add = gtk_button_new_with_label (_("Add Bookmark"));
443 g_signal_connect (priv->bookmark_add, "clicked",
444 G_CALLBACK (bookmark_added), window);
445 gtk_box_pack_end (GTK_BOX (box), priv->bookmark_add, FALSE, FALSE, 0);
446 gtk_widget_show_all (box);
448 priv->bookmark_remove = gtk_button_new_with_label (_("Remove Bookmark"));
449 g_signal_connect (priv->bookmark_remove, "clicked",
450 G_CALLBACK (bookmark_removed), window);
451 gtk_box_pack_end (GTK_BOX (box), priv->bookmark_remove, FALSE, FALSE, 0);
452 gtk_widget_show_all (box);
454 priv->bookmarks_changed =
455 g_signal_connect (priv->application, "bookmarks-changed",
456 G_CALLBACK (app_bookmarks_changed), window);
458 /** Find **/
459 css = gtk_css_provider_new ();
460 /* FIXME: Connect to parsing-error signal. */
461 gtk_css_provider_load_from_data (css,
462 ".yelp-find-frame {"
463 " background-color: @theme_base_color;"
464 " padding: 6px;"
465 " border-color: shade (@notebook_tab_gradient_b, 0.80);"
466 " border-radius: 0 0 3px 3px;"
467 " border-width: 0 1px 1px 1px;"
468 " border-style: solid;"
469 "}",
470 -1, NULL);
471 priv->find_bar = gtk_revealer_new ();
472 frame = gtk_frame_new (NULL);
473 gtk_style_context_add_class (gtk_widget_get_style_context (frame),
474 "yelp-find-frame");
475 gtk_style_context_add_provider (gtk_widget_get_style_context (frame),
476 GTK_STYLE_PROVIDER (css),
477 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
478 box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
479 g_object_set (priv->find_bar,
480 "halign", GTK_ALIGN_END,
481 "margin-end", 6,
482 "valign", GTK_ALIGN_START,
483 NULL);
484 gtk_style_context_add_class (gtk_widget_get_style_context (box), "linked");
485 gtk_container_add (GTK_CONTAINER (frame), box);
486 gtk_container_add (GTK_CONTAINER (priv->find_bar), frame);
488 g_object_unref (css);
490 size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
492 priv->find_entry = gtk_search_entry_new ();
493 gtk_entry_set_width_chars (GTK_ENTRY (priv->find_entry), 30);
494 gtk_size_group_add_widget (size_group, priv->find_entry);
495 gtk_box_pack_start (GTK_BOX (box), priv->find_entry, TRUE, TRUE, 0);
496 g_signal_connect (priv->find_entry, "changed",
497 G_CALLBACK (find_entry_changed), window);
498 g_signal_connect (priv->find_entry, "key-press-event",
499 G_CALLBACK (find_entry_key_press), window);
501 button = gtk_button_new_from_icon_name ("go-up-symbolic", GTK_ICON_SIZE_MENU);
502 gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
503 gtk_style_context_add_class (gtk_widget_get_style_context (button), "raised");
504 gtk_size_group_add_widget (size_group, button);
505 gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
506 g_signal_connect (button, "clicked", G_CALLBACK (find_prev_clicked), window);
508 button = gtk_button_new_from_icon_name ("go-down-symbolic", GTK_ICON_SIZE_MENU);
509 gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
510 gtk_style_context_add_class (gtk_widget_get_style_context (button), "raised");
511 gtk_size_group_add_widget (size_group, button);
512 gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
513 g_signal_connect (button, "clicked", G_CALLBACK (find_next_clicked), window);
515 g_object_unref (size_group);
517 /** View **/
518 box = gtk_overlay_new ();
519 gtk_overlay_add_overlay (GTK_OVERLAY (box), GTK_WIDGET (priv->find_bar));
521 gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (priv->view));
522 gtk_box_pack_start (GTK_BOX (priv->vbox_view), box, TRUE, TRUE, 0);
524 g_signal_connect (priv->view, "new-view-requested", G_CALLBACK (view_new_window), window);
525 g_signal_connect (priv->view, "loaded", G_CALLBACK (view_loaded), window);
526 g_signal_connect (priv->view, "notify::is-loading", G_CALLBACK (view_is_loading_changed), window);
527 g_signal_connect (priv->view, "notify::yelp-uri", G_CALLBACK (view_uri_selected), window);
528 g_signal_connect_swapped (priv->view, "notify::page-id",
529 G_CALLBACK (window_set_bookmark_buttons), window);
530 window_set_bookmark_buttons (window);
531 g_signal_connect (priv->view, "notify::root-title", G_CALLBACK (view_root_title), window);
532 gtk_widget_grab_focus (GTK_WIDGET (priv->view));
534 gtk_drag_dest_set (GTK_WIDGET (window),
535 GTK_DEST_DEFAULT_ALL,
536 NULL, 0,
537 GDK_ACTION_COPY);
538 gtk_drag_dest_add_uri_targets (GTK_WIDGET (window));
539 g_signal_connect (window, "drag-data-received",
540 G_CALLBACK (window_drag_received), NULL);
543 /******************************************************************************/
545 YelpWindow *
546 yelp_window_new (YelpApplication *app)
548 YelpWindow *window;
550 window = (YelpWindow *) g_object_new (YELP_TYPE_WINDOW, "application", app, NULL);
552 return window;
555 void
556 yelp_window_load_uri (YelpWindow *window,
557 YelpUri *uri)
559 YelpWindowPrivate *priv = GET_PRIV (window);
561 yelp_view_load_uri (priv->view, uri);
564 YelpUri *
565 yelp_window_get_uri (YelpWindow *window)
567 YelpUri *uri;
568 YelpWindowPrivate *priv = GET_PRIV (window);
569 g_object_get (G_OBJECT (priv->view), "yelp-uri", &uri, NULL);
570 return uri;
573 void
574 yelp_window_get_geometry (YelpWindow *window,
575 gint *width,
576 gint *height)
578 YelpWindowPrivate *priv = GET_PRIV (window);
579 *width = priv->width;
580 *height = priv->height;
583 /******************************************************************************/
585 static void
586 action_new_window (GSimpleAction *action,
587 GVariant *parameter,
588 gpointer userdata)
590 YelpUri *yuri;
591 gchar *uri = NULL;
592 YelpWindow *window = YELP_WINDOW (userdata);
593 YelpWindowPrivate *priv = GET_PRIV (window);
595 g_object_get (priv->view, "yelp-uri", &yuri, NULL);
596 uri = yelp_uri_get_document_uri (yuri);
598 yelp_application_new_window (priv->application, uri);
600 g_free (uri);
601 g_object_unref (yuri);
604 static void
605 action_close_window (GSimpleAction *action,
606 GVariant *parameter,
607 gpointer userdata)
609 gtk_window_close (GTK_WINDOW (userdata));
612 static void
613 action_search (GSimpleAction *action,
614 GVariant *parameter,
615 gpointer userdata)
617 YelpWindowPrivate *priv = GET_PRIV (userdata);
619 gtk_revealer_set_reveal_child (GTK_REVEALER (priv->find_bar), FALSE);
620 gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (priv->search_bar), TRUE);
621 gtk_widget_grab_focus (priv->search_entry);
624 static void
625 action_find (GSimpleAction *action,
626 GVariant *parameter,
627 gpointer userdata)
629 YelpWindowPrivate *priv = GET_PRIV (userdata);
631 gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (priv->search_bar), FALSE);
632 gtk_revealer_set_reveal_child (GTK_REVEALER (priv->find_bar), TRUE);
633 gtk_widget_grab_focus (priv->find_entry);
636 static void
637 action_go_all (GSimpleAction *action,
638 GVariant *parameter,
639 gpointer userdata)
641 YelpWindowPrivate *priv = GET_PRIV (userdata);
642 yelp_view_load (priv->view, "help-list:");
645 static void
646 action_ctrll (GSimpleAction *action,
647 GVariant *parameter,
648 gpointer userdata)
650 YelpWindowPrivate *priv = GET_PRIV (userdata);
651 YelpUri *yuri;
652 gchar *uri = NULL;
654 if (priv->ctrll_entry == NULL) {
655 priv->ctrll_entry = gtk_entry_new ();
656 g_object_ref_sink (priv->ctrll_entry);
658 g_signal_connect (priv->ctrll_entry, "activate",
659 G_CALLBACK (ctrll_entry_activate), userdata);
660 g_signal_connect (priv->ctrll_entry, "key-press-event",
661 G_CALLBACK (ctrll_entry_key_press), userdata);
664 g_object_set (priv->ctrll_entry, "width-request", priv->width / 2, NULL);
666 gtk_entry_set_text (GTK_ENTRY (priv->ctrll_entry), "");
668 gtk_header_bar_set_custom_title (GTK_HEADER_BAR (priv->header), priv->ctrll_entry);
669 gtk_widget_show (priv->ctrll_entry);
670 gtk_widget_grab_focus (priv->ctrll_entry);
672 g_object_get (priv->view, "yelp-uri", &yuri, NULL);
673 if (yuri) {
674 uri = yelp_uri_get_canonical_uri (yuri);
675 g_object_unref (yuri);
677 if (uri) {
678 gchar *c;
679 gtk_entry_set_text (GTK_ENTRY (priv->ctrll_entry), uri);
680 c = strchr (uri, ':');
681 if (c)
682 gtk_editable_select_region (GTK_EDITABLE (priv->ctrll_entry), c - uri + 1, -1);
683 else
684 gtk_editable_select_region (GTK_EDITABLE (priv->ctrll_entry), 5, -1);
685 g_free (uri);
690 /******************************************************************************/
692 static void
693 window_drag_received (YelpWindow *window,
694 GdkDragContext *context,
695 gint x,
696 gint y,
697 GtkSelectionData *data,
698 guint info,
699 guint time,
700 gpointer userdata)
702 gchar **uris = gtk_selection_data_get_uris (data);
703 if (uris && uris[0]) {
704 YelpUri *uri = yelp_uri_new (uris[0]);
705 yelp_window_load_uri (window, uri);
706 g_object_unref (uri);
707 g_strfreev (uris);
708 gtk_drag_finish (context, TRUE, FALSE, time);
710 gtk_drag_finish (context, FALSE, FALSE, time);
713 static gboolean
714 window_map_event (YelpWindow *window,
715 GdkEvent *event,
716 gpointer user_data)
718 YelpWindowPrivate *priv = GET_PRIV (window);
719 priv->configured = TRUE;
720 return FALSE;
723 static gboolean
724 window_configure_event (YelpWindow *window,
725 GdkEventConfigure *event,
726 gpointer user_data)
728 YelpWindowPrivate *priv = GET_PRIV (window);
729 gboolean skip = TRUE;
730 if (priv->width != event->width) {
731 skip = FALSE;
732 priv->width = event->width;
734 if (priv->height != event->height) {
735 skip = FALSE;
736 priv->height = event->height;
738 /* Skip the configure-event signals that GTK+ sends as it's mapping
739 * the window, and also skip if the event didn't change the size of
740 * the window (i.e. it was just a move).
742 if (!priv->configured || skip)
743 return FALSE;
745 if (priv->resize_signal > 0)
746 g_source_remove (priv->resize_signal);
747 priv->resize_signal = g_timeout_add (200,
748 (GSourceFunc) window_resize_signal,
749 window);
750 g_object_set (priv->find_entry, "width-request", 2 * priv->width / 3, NULL);
751 return FALSE;
754 static gboolean
755 window_resize_signal (YelpWindow *window)
757 YelpWindowPrivate *priv = GET_PRIV (window);
758 g_signal_emit (window, signals[RESIZE_EVENT], 0);
759 priv->resize_signal = 0;
760 return FALSE;
763 static gboolean
764 window_key_press (YelpWindow *window,
765 GdkEventKey *event,
766 gpointer userdata)
768 YelpWindowPrivate *priv = GET_PRIV (window);
770 if (gtk_revealer_get_reveal_child (GTK_REVEALER (priv->find_bar)))
771 return FALSE;
773 if (gtk_header_bar_get_custom_title (GTK_HEADER_BAR (priv->header)))
774 return FALSE;
776 return gtk_search_bar_handle_event (GTK_SEARCH_BAR (priv->search_bar),
777 (GdkEvent *) event);
780 static void
781 bookmark_activated (GtkListBox *box,
782 GtkListBoxRow *row,
783 YelpWindow *window)
785 YelpUri *base, *uri;
786 gchar *xref;
787 YelpWindowPrivate *priv = GET_PRIV (window);
789 gtk_widget_hide (priv->bookmark_menu);
791 xref = g_strconcat ("xref:",
792 (gchar *) g_object_get_data (G_OBJECT (row), "page-id"),
793 NULL);
794 g_object_get (priv->view, "yelp-uri", &base, NULL);
795 uri = yelp_uri_new_relative (base, xref);
797 yelp_view_load_uri (priv->view, uri);
799 g_object_unref (base);
800 g_object_unref (uri);
801 g_free (xref);
804 static void
805 bookmark_removed (GtkButton *button,
806 YelpWindow *window)
808 YelpUri *uri;
809 gchar *doc_uri;
810 gchar *page_id = NULL;
811 YelpWindowPrivate *priv = GET_PRIV (window);
813 g_object_get (priv->view, "yelp-uri", &uri, NULL);
814 doc_uri = yelp_uri_get_document_uri (uri);
816 /* The 'Remove Bookmark' button removes a bookmark for the current page.
817 The buttons next to each bookmark have page_id attached to them.
819 if ((gpointer) button == (gpointer) priv->bookmark_remove)
820 g_object_get (priv->view,
821 "page-id", &page_id,
822 NULL);
824 yelp_application_remove_bookmark (YELP_BOOKMARKS (priv->application),
825 doc_uri,
826 page_id ? page_id :
827 g_object_get_data (G_OBJECT (button), "page-id"));
828 if (page_id)
829 g_free (page_id);
830 g_free (doc_uri);
831 g_object_unref (uri);
834 static void
835 bookmark_added (GtkButton *button,
836 YelpWindow *window)
838 YelpUri *uri;
839 gchar *doc_uri, *page_id, *icon, *title;
840 YelpWindowPrivate *priv = GET_PRIV (window);
842 g_object_get (priv->view,
843 "yelp-uri", &uri,
844 "page-id", &page_id,
845 "page-icon", &icon,
846 "page-title", &title,
847 NULL);
848 doc_uri = yelp_uri_get_document_uri (uri);
849 yelp_application_add_bookmark (YELP_BOOKMARKS (priv->application),
850 doc_uri, page_id, icon, title);
851 g_free (doc_uri);
852 g_free (page_id);
853 g_free (icon);
854 g_free (title);
855 g_object_unref (uri);
858 static void
859 app_bookmarks_changed (YelpApplication *app,
860 const gchar *doc_uri,
861 YelpWindow *window)
863 YelpUri *uri;
864 gchar *this_doc_uri;
865 YelpWindowPrivate *priv = GET_PRIV (window);
867 g_object_get (priv->view, "yelp-uri", &uri, NULL);
868 this_doc_uri = yelp_uri_get_document_uri (uri);
870 if (g_str_equal (this_doc_uri, doc_uri)) {
871 window_set_bookmarks (window, doc_uri);
874 g_free (this_doc_uri);
875 g_object_unref (uri);
878 typedef struct _YelpMenuEntry YelpMenuEntry;
879 struct _YelpMenuEntry {
880 gchar *page_id;
881 gchar *icon;
882 gchar *title;
885 static gint
886 entry_compare (YelpMenuEntry *a, YelpMenuEntry *b)
888 gint ret = yelp_settings_cmp_icons (a->icon, b->icon);
889 if (ret != 0)
890 return ret;
892 if (a->title && b->title)
893 return g_utf8_collate (a->title, b->title);
894 else if (b->title == NULL)
895 return -1;
896 else if (a->title == NULL)
897 return 1;
899 return 0;
902 static void
903 window_set_bookmarks (YelpWindow *window,
904 const gchar *doc_uri)
906 GVariant *value;
907 GVariantIter *iter;
908 gchar *page_id, *icon, *title;
909 YelpWindowPrivate *priv = GET_PRIV (window);
910 GList *children, *cur;
911 GSList *entries = NULL;
913 window_set_bookmark_buttons (window);
915 children = gtk_container_get_children (GTK_CONTAINER (priv->bookmark_list));
916 for (cur = children ; cur != NULL; cur = cur->next) {
917 gtk_container_remove (GTK_CONTAINER (priv->bookmark_list),
918 GTK_WIDGET (cur->data));
920 g_list_free (children);
922 value = yelp_application_get_bookmarks (priv->application, doc_uri);
923 g_variant_get (value, "a(sss)", &iter);
924 while (g_variant_iter_loop (iter, "(&s&s&s)", &page_id, &icon, &title)) {
925 YelpMenuEntry *entry = g_new0 (YelpMenuEntry, 1);
926 entry->page_id = page_id;
927 entry->icon = g_strdup (icon);
928 entry->title = title;
929 entries = g_slist_insert_sorted (entries, entry, (GCompareFunc) entry_compare);
931 for ( ; entries != NULL; entries = g_slist_delete_link (entries, entries)) {
932 GtkWidget *row, *box, *button;
933 YelpMenuEntry *entry = (YelpMenuEntry *) entries->data;
935 row = gtk_list_box_row_new ();
936 g_object_set_data_full (G_OBJECT (row), "page-id",
937 g_strdup (entry->page_id), (GDestroyNotify) g_free);
938 box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
939 gtk_container_add (GTK_CONTAINER (row), box);
940 button = gtk_label_new (entry->title);
941 g_object_set (button, "halign", GTK_ALIGN_START, NULL);
942 gtk_box_pack_start (GTK_BOX (box), button, TRUE, TRUE, 0);
943 button = gtk_button_new_from_icon_name ("edit-delete-symbolic", GTK_ICON_SIZE_MENU);
944 g_object_set (button,
945 "relief", GTK_RELIEF_NONE,
946 "focus-on-click", FALSE,
947 NULL);
948 g_object_set_data_full (G_OBJECT (button), "page-id",
949 g_strdup (entry->page_id), (GDestroyNotify) g_free);
950 g_signal_connect (button, "clicked", G_CALLBACK (bookmark_removed), window);
951 gtk_box_pack_end (GTK_BOX (box), button, FALSE, FALSE, 0);
952 gtk_box_pack_end (GTK_BOX (box), gtk_separator_new (GTK_ORIENTATION_VERTICAL), FALSE, FALSE, 0);
953 gtk_widget_show_all (row);
954 gtk_container_add (GTK_CONTAINER (priv->bookmark_list), row);
955 g_free (entry->icon);
956 g_free (entry);
959 g_variant_iter_free (iter);
960 g_variant_unref (value);
963 static void
964 window_set_bookmark_buttons (YelpWindow *window)
966 YelpUri *uri = NULL;
967 gchar *doc_uri = NULL, *page_id = NULL;
968 gboolean bookmarked;
969 YelpWindowPrivate *priv = GET_PRIV (window);
972 g_object_get (priv->view,
973 "yelp-uri", &uri,
974 "page-id", &page_id,
975 NULL);
976 if (page_id == NULL || uri == NULL) {
977 gtk_widget_hide (priv->bookmark_add);
978 gtk_widget_hide (priv->bookmark_remove);
979 goto done;
981 doc_uri = yelp_uri_get_document_uri (uri);
982 bookmarked = yelp_application_is_bookmarked (YELP_BOOKMARKS (priv->application),
983 doc_uri, page_id);
985 gtk_widget_set_visible (priv->bookmark_add, !bookmarked);
986 gtk_widget_set_visible (priv->bookmark_remove, bookmarked);
988 done:
989 g_free (page_id);
990 g_free (doc_uri);
991 if (uri)
992 g_object_unref (uri);
995 static void
996 window_search_mode (GtkSearchBar *search_bar,
997 GParamSpec *pspec,
998 YelpWindow *window)
1000 YelpWindowPrivate *priv = GET_PRIV (window);
1002 if (gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (search_bar)))
1003 gtk_revealer_set_reveal_child (GTK_REVEALER (priv->find_bar), FALSE);
1006 static gboolean
1007 find_entry_key_press (GtkEntry *entry,
1008 GdkEventKey *event,
1009 YelpWindow *window)
1011 YelpWindowPrivate *priv = GET_PRIV (window);
1012 WebKitFindController *find_controller;
1014 find_controller = webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (priv->view));
1016 if (event->keyval == GDK_KEY_Escape) {
1017 gtk_revealer_set_reveal_child (GTK_REVEALER (priv->find_bar), FALSE);
1018 gtk_widget_grab_focus (GTK_WIDGET (priv->view));
1019 webkit_find_controller_search_finish (find_controller);
1020 return TRUE;
1023 if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter) {
1024 webkit_find_controller_search_next (find_controller);
1025 return TRUE;
1028 return FALSE;
1031 static void
1032 find_entry_changed (GtkEntry *entry,
1033 YelpWindow *window)
1035 gchar *text;
1036 YelpWindowPrivate *priv = GET_PRIV (window);
1037 WebKitFindController *find_controller;
1039 find_controller = webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (priv->view));
1041 text = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);
1043 webkit_find_controller_search (find_controller, text,
1044 WEBKIT_FIND_OPTIONS_WRAP_AROUND | WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE,
1045 MAX_FIND_MATCHES);
1047 g_free (text);
1050 static void
1051 find_prev_clicked (GtkButton *button,
1052 YelpWindow *window)
1054 YelpWindowPrivate *priv = GET_PRIV (window);
1055 WebKitFindController *find_controller;
1057 find_controller = webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (priv->view));
1058 webkit_find_controller_search_previous (find_controller);
1061 static void
1062 find_next_clicked (GtkButton *button,
1063 YelpWindow *window)
1065 YelpWindowPrivate *priv = GET_PRIV (window);
1066 WebKitFindController *find_controller;
1068 find_controller = webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (priv->view));
1069 webkit_find_controller_search_next (find_controller);
1072 static void
1073 view_new_window (YelpView *view,
1074 YelpUri *uri,
1075 YelpWindow *window)
1077 YelpWindowPrivate *priv = GET_PRIV (window);
1078 yelp_application_new_window_uri (priv->application, uri);
1081 static void
1082 view_loaded (YelpView *view,
1083 YelpWindow *window)
1086 YelpUri *uri;
1087 gchar *doc_uri;
1088 YelpViewState state;
1089 YelpWindowPrivate *priv = GET_PRIV (window);
1091 g_object_get (view,
1092 "yelp-uri", &uri,
1093 "state", &state,
1094 NULL);
1095 doc_uri = yelp_uri_get_document_uri (uri);
1097 if (state != YELP_VIEW_STATE_ERROR) {
1098 gchar *page_id, *icon, *title;
1099 g_object_get (view,
1100 "page-id", &page_id,
1101 "page-icon", &icon,
1102 "page-title", &title,
1103 NULL);
1104 if (!g_str_has_prefix (page_id, "search=")) {
1105 gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (priv->search_bar), FALSE);
1107 yelp_application_update_bookmarks (priv->application,
1108 doc_uri,
1109 page_id,
1110 icon,
1111 title);
1112 g_free (page_id);
1113 g_free (icon);
1114 g_free (title);
1117 g_free (doc_uri);
1118 g_object_unref (uri);
1121 static void
1122 view_is_loading_changed (YelpView *view,
1123 GParamSpec *pspec,
1124 YelpWindow *window)
1126 GdkWindow *gdkwin;
1128 gdkwin = gtk_widget_get_window (GTK_WIDGET (window));
1129 if (!gdkwin)
1130 return;
1132 if (webkit_web_view_is_loading (WEBKIT_WEB_VIEW (view))) {
1133 gdk_window_set_cursor (gdkwin,
1134 gdk_cursor_new_for_display (gdk_window_get_display (gdkwin),
1135 GDK_WATCH));
1136 } else {
1137 gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (window)), NULL);
1141 static void
1142 view_uri_selected (YelpView *view,
1143 GParamSpec *pspec,
1144 YelpWindow *window)
1146 YelpUri *uri;
1147 gchar *doc_uri;
1148 YelpWindowPrivate *priv = GET_PRIV (window);
1150 g_object_get (G_OBJECT (view), "yelp-uri", &uri, NULL);
1151 if (uri == NULL)
1152 return;
1154 doc_uri = yelp_uri_get_document_uri (uri);
1155 if (priv->doc_uri == NULL || !g_str_equal (priv->doc_uri, doc_uri)) {
1156 window_set_bookmarks (window, doc_uri);
1157 g_free (priv->doc_uri);
1158 priv->doc_uri = doc_uri;
1160 else {
1161 g_free (doc_uri);
1164 g_object_unref (uri);
1167 static void
1168 view_root_title (YelpView *view,
1169 GParamSpec *pspec,
1170 YelpWindow *window)
1172 YelpWindowPrivate *priv = GET_PRIV (window);
1173 gchar *root_title, *page_title;
1174 g_object_get (view, "root-title", &root_title, "page-title", &page_title, NULL);
1176 if (!priv->use_header) {
1177 if (root_title)
1178 gtk_window_set_title (GTK_WINDOW (window), root_title);
1179 else
1180 gtk_window_set_title (GTK_WINDOW (window), _("Help"));
1182 goto out;
1185 if (page_title)
1186 gtk_header_bar_set_title (GTK_HEADER_BAR (priv->header), page_title);
1187 else
1188 gtk_header_bar_set_title (GTK_HEADER_BAR (priv->header), _("Help"));
1190 if (root_title && (page_title == NULL || strcmp (root_title, page_title)))
1191 gtk_header_bar_set_subtitle (GTK_HEADER_BAR (priv->header), root_title);
1192 else
1193 gtk_header_bar_set_subtitle (GTK_HEADER_BAR (priv->header), NULL);
1195 out:
1196 g_free (root_title);
1197 g_free (page_title);
1200 static void
1201 ctrll_entry_activate (GtkEntry *entry,
1202 YelpWindow *window)
1204 YelpWindowPrivate *priv = GET_PRIV (window);
1205 YelpUri *uri = yelp_uri_new (gtk_entry_get_text (entry));
1207 yelp_window_load_uri (window, uri);
1208 g_object_unref (uri);
1210 gtk_header_bar_set_custom_title (GTK_HEADER_BAR (priv->header), NULL);
1213 static gboolean
1214 ctrll_entry_key_press (GtkWidget *widget,
1215 GdkEventKey *event,
1216 YelpWindow *window)
1218 YelpWindowPrivate *priv = GET_PRIV (window);
1220 if (event->keyval == GDK_KEY_Escape) {
1221 gtk_header_bar_set_custom_title (GTK_HEADER_BAR (priv->header), NULL);
1222 return TRUE;
1224 return FALSE;