1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
27 #include <gdk/gdkkeysyms.h>
28 #include <glib/gi18n.h>
32 #include "yelp-search-entry.h"
33 #include "yelp-settings.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
,
46 static void yelp_window_set_property (GObject
*object
,
51 static void window_construct (YelpWindow
*window
);
53 static gboolean
window_map_event (YelpWindow
*window
,
56 static gboolean
window_configure_event (YelpWindow
*window
,
57 GdkEventConfigure
*event
,
59 static void window_drag_received (YelpWindow
*window
,
60 GdkDragContext
*context
,
63 GtkSelectionData
*data
,
67 static gboolean
window_resize_signal (YelpWindow
*window
);
68 static gboolean
window_key_press (YelpWindow
*window
,
72 static void bookmark_activated (GtkListBox
*box
,
75 static void bookmark_removed (GtkButton
*button
,
77 static void bookmark_added (GtkButton
*button
,
80 static void app_bookmarks_changed (YelpApplication
*app
,
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
,
90 static void action_new_window (GSimpleAction
*action
,
93 static void action_close_window (GSimpleAction
*action
,
96 static void action_search (GSimpleAction
*action
,
99 static void action_find (GSimpleAction
*action
,
102 static void action_go_all (GSimpleAction
*action
,
105 static void action_ctrll (GSimpleAction
*action
,
110 static gboolean
find_entry_key_press (GtkEntry
*entry
,
113 static void find_entry_changed (GtkEntry
*entry
,
115 static void find_prev_clicked (GtkButton
*button
,
117 static void find_next_clicked (GtkButton
*button
,
120 static void view_new_window (YelpView
*view
,
123 static void view_loaded (YelpView
*view
,
125 static void view_is_loading_changed (YelpView
*view
,
128 static void view_uri_selected (YelpView
*view
,
131 static void view_root_title (YelpView
*view
,
134 static void ctrll_entry_activate (GtkEntry
*entry
,
136 static gboolean
ctrll_entry_key_press (GtkWidget
*widget
,
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 */
165 GtkWidget
*vbox_view
;
166 GtkWidget
*vbox_full
;
167 GtkWidget
*search_bar
;
168 GtkWidget
*search_entry
;
170 GtkWidget
*find_entry
;
171 GtkWidget
*bookmark_menu
;
172 GtkWidget
*bookmark_sw
;
173 GtkWidget
*bookmark_list
;
174 GtkWidget
*bookmark_add
;
175 GtkWidget
*bookmark_remove
;
178 GtkWidget
*ctrll_entry
;
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
);
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
,
210 g_param_spec_object ("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
),
223 g_cclosure_marshal_VOID__VOID
,
226 g_type_class_add_private (klass
, sizeof (YelpWindowPrivate
));
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
);
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
);
256 yelp_window_get_property (GObject
*object
,
261 YelpWindowPrivate
*priv
= GET_PRIV (object
);
263 case PROP_APPLICATION
:
264 g_value_set_object (value
, priv
->application
);
267 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
273 yelp_window_set_property (GObject
*object
,
278 YelpWindowPrivate
*priv
= GET_PRIV (object
);
280 case PROP_APPLICATION
:
281 priv
->application
= g_value_get_object (value
);
282 window_construct ((YelpWindow
*) object
);
285 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
291 window_construct (YelpWindow
*window
)
293 GtkWidget
*box
, *button
;
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
,
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
);
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
);
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
);
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
));
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
);
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
),
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
);
459 css
= gtk_css_provider_new ();
460 /* FIXME: Connect to parsing-error signal. */
461 gtk_css_provider_load_from_data (css
,
463 " background-color: @theme_base_color;"
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;"
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
),
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
,
482 "valign", GTK_ALIGN_START
,
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
);
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
,
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 /******************************************************************************/
546 yelp_window_new (YelpApplication
*app
)
550 window
= (YelpWindow
*) g_object_new (YELP_TYPE_WINDOW
, "application", app
, NULL
);
556 yelp_window_load_uri (YelpWindow
*window
,
559 YelpWindowPrivate
*priv
= GET_PRIV (window
);
561 yelp_view_load_uri (priv
->view
, uri
);
565 yelp_window_get_uri (YelpWindow
*window
)
568 YelpWindowPrivate
*priv
= GET_PRIV (window
);
569 g_object_get (G_OBJECT (priv
->view
), "yelp-uri", &uri
, NULL
);
574 yelp_window_get_geometry (YelpWindow
*window
,
578 YelpWindowPrivate
*priv
= GET_PRIV (window
);
579 *width
= priv
->width
;
580 *height
= priv
->height
;
583 /******************************************************************************/
586 action_new_window (GSimpleAction
*action
,
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
);
601 g_object_unref (yuri
);
605 action_close_window (GSimpleAction
*action
,
609 gtk_window_close (GTK_WINDOW (userdata
));
613 action_search (GSimpleAction
*action
,
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
);
625 action_find (GSimpleAction
*action
,
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
);
637 action_go_all (GSimpleAction
*action
,
641 YelpWindowPrivate
*priv
= GET_PRIV (userdata
);
642 yelp_view_load (priv
->view
, "help-list:");
646 action_ctrll (GSimpleAction
*action
,
650 YelpWindowPrivate
*priv
= GET_PRIV (userdata
);
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
);
674 uri
= yelp_uri_get_canonical_uri (yuri
);
675 g_object_unref (yuri
);
679 gtk_entry_set_text (GTK_ENTRY (priv
->ctrll_entry
), uri
);
680 c
= strchr (uri
, ':');
682 gtk_editable_select_region (GTK_EDITABLE (priv
->ctrll_entry
), c
- uri
+ 1, -1);
684 gtk_editable_select_region (GTK_EDITABLE (priv
->ctrll_entry
), 5, -1);
690 /******************************************************************************/
693 window_drag_received (YelpWindow
*window
,
694 GdkDragContext
*context
,
697 GtkSelectionData
*data
,
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
);
708 gtk_drag_finish (context
, TRUE
, FALSE
, time
);
710 gtk_drag_finish (context
, FALSE
, FALSE
, time
);
714 window_map_event (YelpWindow
*window
,
718 YelpWindowPrivate
*priv
= GET_PRIV (window
);
719 priv
->configured
= TRUE
;
724 window_configure_event (YelpWindow
*window
,
725 GdkEventConfigure
*event
,
728 YelpWindowPrivate
*priv
= GET_PRIV (window
);
729 gboolean skip
= TRUE
;
730 if (priv
->width
!= event
->width
) {
732 priv
->width
= event
->width
;
734 if (priv
->height
!= event
->height
) {
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
)
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
,
750 g_object_set (priv
->find_entry
, "width-request", 2 * priv
->width
/ 3, NULL
);
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;
764 window_key_press (YelpWindow
*window
,
768 YelpWindowPrivate
*priv
= GET_PRIV (window
);
770 if (gtk_revealer_get_reveal_child (GTK_REVEALER (priv
->find_bar
)))
773 if (gtk_header_bar_get_custom_title (GTK_HEADER_BAR (priv
->header
)))
776 return gtk_search_bar_handle_event (GTK_SEARCH_BAR (priv
->search_bar
),
781 bookmark_activated (GtkListBox
*box
,
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"),
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
);
805 bookmark_removed (GtkButton
*button
,
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
,
824 yelp_application_remove_bookmark (YELP_BOOKMARKS (priv
->application
),
827 g_object_get_data (G_OBJECT (button
), "page-id"));
831 g_object_unref (uri
);
835 bookmark_added (GtkButton
*button
,
839 gchar
*doc_uri
, *page_id
, *icon
, *title
;
840 YelpWindowPrivate
*priv
= GET_PRIV (window
);
842 g_object_get (priv
->view
,
846 "page-title", &title
,
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
);
855 g_object_unref (uri
);
859 app_bookmarks_changed (YelpApplication
*app
,
860 const gchar
*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
{
886 entry_compare (YelpMenuEntry
*a
, YelpMenuEntry
*b
)
888 gint ret
= yelp_settings_cmp_icons (a
->icon
, b
->icon
);
892 if (a
->title
&& b
->title
)
893 return g_utf8_collate (a
->title
, b
->title
);
894 else if (b
->title
== NULL
)
896 else if (a
->title
== NULL
)
903 window_set_bookmarks (YelpWindow
*window
,
904 const gchar
*doc_uri
)
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
,
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
);
959 g_variant_iter_free (iter
);
960 g_variant_unref (value
);
964 window_set_bookmark_buttons (YelpWindow
*window
)
967 gchar
*doc_uri
= NULL
, *page_id
= NULL
;
969 YelpWindowPrivate
*priv
= GET_PRIV (window
);
972 g_object_get (priv
->view
,
976 if (page_id
== NULL
|| uri
== NULL
) {
977 gtk_widget_hide (priv
->bookmark_add
);
978 gtk_widget_hide (priv
->bookmark_remove
);
981 doc_uri
= yelp_uri_get_document_uri (uri
);
982 bookmarked
= yelp_application_is_bookmarked (YELP_BOOKMARKS (priv
->application
),
985 gtk_widget_set_visible (priv
->bookmark_add
, !bookmarked
);
986 gtk_widget_set_visible (priv
->bookmark_remove
, bookmarked
);
992 g_object_unref (uri
);
996 window_search_mode (GtkSearchBar
*search_bar
,
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
);
1007 find_entry_key_press (GtkEntry
*entry
,
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
);
1023 if (event
->keyval
== GDK_KEY_Return
|| event
->keyval
== GDK_KEY_KP_Enter
) {
1024 webkit_find_controller_search_next (find_controller
);
1032 find_entry_changed (GtkEntry
*entry
,
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
,
1051 find_prev_clicked (GtkButton
*button
,
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
);
1062 find_next_clicked (GtkButton
*button
,
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
);
1073 view_new_window (YelpView
*view
,
1077 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1078 yelp_application_new_window_uri (priv
->application
, uri
);
1082 view_loaded (YelpView
*view
,
1088 YelpViewState state
;
1089 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1095 doc_uri
= yelp_uri_get_document_uri (uri
);
1097 if (state
!= YELP_VIEW_STATE_ERROR
) {
1098 gchar
*page_id
, *icon
, *title
;
1100 "page-id", &page_id
,
1102 "page-title", &title
,
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
,
1118 g_object_unref (uri
);
1122 view_is_loading_changed (YelpView
*view
,
1128 gdkwin
= gtk_widget_get_window (GTK_WIDGET (window
));
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
),
1137 gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (window
)), NULL
);
1142 view_uri_selected (YelpView
*view
,
1148 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1150 g_object_get (G_OBJECT (view
), "yelp-uri", &uri
, NULL
);
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
;
1164 g_object_unref (uri
);
1168 view_root_title (YelpView
*view
,
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
) {
1178 gtk_window_set_title (GTK_WINDOW (window
), root_title
);
1180 gtk_window_set_title (GTK_WINDOW (window
), _("Help"));
1186 gtk_header_bar_set_title (GTK_HEADER_BAR (priv
->header
), page_title
);
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
);
1193 gtk_header_bar_set_subtitle (GTK_HEADER_BAR (priv
->header
), NULL
);
1196 g_free (root_title
);
1197 g_free (page_title
);
1201 ctrll_entry_activate (GtkEntry
*entry
,
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
);
1214 ctrll_entry_key_press (GtkWidget
*widget
,
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
);