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, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Author: Shaun McCance <shaunm@gnome.org>
29 #include <gdk/gdkkeysyms.h>
30 #include <glib/gi18n.h>
33 #include "yelp-location-entry.h"
34 #include "yelp-settings.h"
36 #include "yelp-view.h"
38 #include "yelp-application.h"
39 #include "yelp-window.h"
41 static void yelp_window_init (YelpWindow
*window
);
42 static void yelp_window_class_init (YelpWindowClass
*klass
);
43 static void yelp_window_dispose (GObject
*object
);
44 static void yelp_window_finalize (GObject
*object
);
45 static void yelp_window_get_property (GObject
*object
,
49 static void yelp_window_set_property (GObject
*object
,
54 static void window_construct (YelpWindow
*window
);
55 static void window_new (GtkAction
*action
,
57 static gboolean
window_map_event (YelpWindow
*window
,
60 static gboolean
window_configure_event (YelpWindow
*window
,
61 GdkEventConfigure
*event
,
63 static void window_drag_received (YelpWindow
*window
,
64 GdkDragContext
*context
,
67 GtkSelectionData
*data
,
71 static gboolean
window_resize_signal (YelpWindow
*window
);
72 static void window_close (GtkAction
*action
,
74 static void window_go_all (GtkAction
*action
,
76 static void window_add_bookmark (GtkAction
*action
,
78 static void window_remove_bookmark (GtkAction
*action
,
80 static void window_load_bookmark (GtkAction
*action
,
82 static void window_find_in_page (GtkAction
*action
,
84 static void window_start_search (GtkAction
*action
,
86 static void window_open_location (GtkAction
*action
,
88 static void window_read_later (GtkAction
*action
,
90 static gboolean
read_later_clicked (GtkLinkButton
*button
,
92 static void app_read_later_changed (YelpApplication
*app
,
95 static void app_bookmarks_changed (YelpApplication
*app
,
98 static void window_set_bookmarks (YelpWindow
*window
,
99 const gchar
*doc_uri
);
100 static void window_set_bookmark_action (YelpWindow
*window
);
101 static gboolean
find_entry_focus_out (GtkEntry
*entry
,
102 GdkEventFocus
*event
,
104 static gboolean
find_entry_key_press (GtkEntry
*entry
,
107 static void find_entry_changed (GtkEntry
*entry
,
110 static gboolean
entry_focus_in (GtkEntry
*entry
,
111 GdkEventFocus
*event
,
113 static gboolean
entry_focus_out (YelpLocationEntry
*entry
,
114 GdkEventFocus
*event
,
117 static void view_new_window (YelpView
*view
,
120 static void view_loaded (YelpView
*view
,
122 static void view_uri_selected (YelpView
*view
,
125 static void view_root_title (YelpView
*view
,
128 static gboolean
view_is_xref_uri (YelpView
*view
,
133 static void hidden_entry_activate (GtkEntry
*entry
,
135 static void hidden_entry_hide (YelpWindow
*window
);
136 static gboolean
hidden_key_press (GtkWidget
*widget
,
150 static guint signals
[LAST_SIGNAL
] = { 0 };
152 G_DEFINE_TYPE (YelpWindow
, yelp_window
, GTK_TYPE_WINDOW
);
153 #define GET_PRIV(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_WINDOW, YelpWindowPrivate))
155 static const gchar
*YELP_UI
=
158 "<menu action='PageMenu'>"
159 "<menuitem action='NewWindow'/>"
160 "<menuitem action='Find'/>"
162 "<menuitem action='YelpViewPrint'/>"
164 "<menuitem action='CloseWindow'/>"
166 "<menu action='ViewMenu'>"
167 "<menuitem action='LargerText'/>"
168 "<menuitem action='SmallerText'/>"
170 "<menuitem action='ShowTextCursor'/>"
172 "<menu action='GoMenu'>"
173 "<menuitem action='YelpViewGoBack'/>"
174 "<menuitem action='YelpViewGoForward'/>"
176 "<menuitem action='YelpViewGoPrevious'/>"
177 "<menuitem action='YelpViewGoNext'/>"
179 "<menuitem action='GoAll'/>"
181 "<menu action='BookmarksMenu'>"
182 "<menuitem action='AddBookmark'/>"
183 "<menuitem action='RemoveBookmark'/>"
185 "<placeholder name='Bookmarks'/>"
188 "<accelerator action='Find'/>"
189 "<accelerator action='Search'/>"
190 "<accelerator action='OpenLocation'/>"
193 typedef struct _YelpWindowPrivate YelpWindowPrivate
;
194 struct _YelpWindowPrivate
{
195 GtkListStore
*history
;
196 GtkUIManager
*ui_manager
;
197 GtkActionGroup
*action_group
;
198 YelpApplication
*application
;
200 gulong bookmarks_changed
;
201 gulong read_later_changed
;
203 /* no refs on these, owned by containers */
205 GtkWidget
*vbox_view
;
206 GtkWidget
*vbox_full
;
208 YelpLocationEntry
*entry
;
209 GtkWidget
*hidden_entry
;
210 GtkWidget
*find_entry
;
211 GtkWidget
*find_label
;
212 GtkWidget
*read_later_vbox
;
214 /* refs because we dynamically add & remove */
216 GtkWidget
*align_location
;
217 GtkWidget
*align_hidden
;
218 GtkWidget
*read_later
;
222 GtkActionGroup
*bookmark_actions
;
223 guint bookmarks_merge_id
;
229 guint entry_color_animate
;
230 gfloat entry_color_step
;
235 static const GtkActionEntry entries
[] = {
236 { "PageMenu", NULL
, N_("_Page") },
237 { "ViewMenu", NULL
, N_("_View") },
238 { "GoMenu", NULL
, N_("_Go") },
239 { "BookmarksMenu", NULL
, N_("_Bookmarks") },
245 G_CALLBACK (window_new
) },
246 { "CloseWindow", NULL
,
250 G_CALLBACK (window_close
) },
252 N_("_All Documents"),
254 G_CALLBACK (window_go_all
) },
255 { "AddBookmark", NULL
,
259 G_CALLBACK (window_add_bookmark
) },
260 { "RemoveBookmark", NULL
,
261 N_("_Remove Bookmark"),
263 G_CALLBACK (window_remove_bookmark
) },
265 N_("Find in Page..."),
268 G_CALLBACK (window_find_in_page
) },
273 G_CALLBACK (window_start_search
) },
274 { "OpenLocation", NULL
,
278 G_CALLBACK (window_open_location
) }
282 yelp_window_init (YelpWindow
*window
)
284 g_signal_connect (window
, "configure-event", G_CALLBACK (window_configure_event
), NULL
);
285 g_signal_connect (window
, "map-event", G_CALLBACK (window_map_event
), NULL
);
289 yelp_window_class_init (YelpWindowClass
*klass
)
291 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
293 object_class
->dispose
= yelp_window_dispose
;
294 object_class
->finalize
= yelp_window_finalize
;
295 object_class
->get_property
= yelp_window_get_property
;
296 object_class
->set_property
= yelp_window_set_property
;
298 g_object_class_install_property (object_class
,
300 g_param_spec_object ("application",
302 _("A YelpApplication instance that controls this window"),
303 YELP_TYPE_APPLICATION
,
304 G_PARAM_CONSTRUCT_ONLY
|
305 G_PARAM_READWRITE
| G_PARAM_STATIC_NAME
|
306 G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
));
308 signals
[RESIZE_EVENT
] =
309 g_signal_new ("resized",
310 G_OBJECT_CLASS_TYPE (klass
),
313 g_cclosure_marshal_VOID__VOID
,
316 g_type_class_add_private (klass
, sizeof (YelpWindowPrivate
));
320 yelp_window_dispose (GObject
*object
)
322 YelpWindowPrivate
*priv
= GET_PRIV (object
);
325 g_object_unref (priv
->history
);
326 priv
->history
= NULL
;
329 if (priv
->action_group
) {
330 g_object_unref (priv
->action_group
);
331 priv
->action_group
= NULL
;
334 if (priv
->bookmark_actions
) {
335 g_object_unref (priv
->bookmark_actions
);
336 priv
->bookmark_actions
= NULL
;
339 if (priv
->read_later_changed
) {
340 g_source_remove (priv
->read_later_changed
);
341 priv
->read_later_changed
= 0;
344 if (priv
->bookmarks_changed
) {
345 g_source_remove (priv
->bookmarks_changed
);
346 priv
->bookmarks_changed
= 0;
349 if (priv
->align_location
) {
350 g_object_unref (priv
->align_location
);
351 priv
->align_location
= NULL
;
354 if (priv
->align_hidden
) {
355 g_object_unref (priv
->align_hidden
);
356 priv
->align_hidden
= NULL
;
359 if (priv
->find_bar
) {
360 g_object_unref (priv
->find_bar
);
361 priv
->find_bar
= NULL
;
364 if (priv
->entry_color_animate
!= 0) {
365 g_source_remove (priv
->entry_color_animate
);
366 priv
->entry_color_animate
= 0;
369 G_OBJECT_CLASS (yelp_window_parent_class
)->dispose (object
);
373 yelp_window_finalize (GObject
*object
)
375 YelpWindowPrivate
*priv
= GET_PRIV (object
);
376 g_free (priv
->doc_uri
);
377 G_OBJECT_CLASS (yelp_window_parent_class
)->finalize (object
);
381 yelp_window_get_property (GObject
*object
,
386 YelpWindowPrivate
*priv
= GET_PRIV (object
);
388 case PROP_APPLICATION
:
389 g_value_set_object (value
, priv
->application
);
392 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
398 yelp_window_set_property (GObject
*object
,
403 YelpWindowPrivate
*priv
= GET_PRIV (object
);
405 case PROP_APPLICATION
:
406 priv
->application
= g_value_get_object (value
);
407 window_construct ((YelpWindow
*) object
);
410 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
416 window_construct (YelpWindow
*window
)
419 GtkActionGroup
*view_actions
;
421 GtkWidget
*vbox
, *button
, *label
;
423 YelpWindowPrivate
*priv
= GET_PRIV (window
);
425 gtk_window_set_icon_name (GTK_WINDOW (window
), "help-browser");
427 priv
->view
= (YelpView
*) yelp_view_new ();
429 action
= gtk_action_new ("ReadLinkLater", _("Read Link _Later"), NULL
, NULL
);
430 g_signal_connect (action
, "activate", G_CALLBACK (window_read_later
), window
);
431 yelp_view_add_link_action (priv
->view
, action
,
432 (YelpViewActionValidFunc
) view_is_xref_uri
,
434 priv
->read_later_changed
=
435 g_signal_connect (priv
->application
, "read-later-changed",
436 G_CALLBACK (app_read_later_changed
), window
);
438 priv
->vbox_full
= gtk_vbox_new (FALSE
, 3);
439 gtk_container_add (GTK_CONTAINER (window
), priv
->vbox_full
);
441 priv
->vbox_view
= gtk_vbox_new (FALSE
, 0);
442 gtk_box_pack_start (GTK_BOX (priv
->vbox_full
), priv
->vbox_view
, TRUE
, TRUE
, 0);
444 priv
->action_group
= gtk_action_group_new ("YelpWindowActions");
445 gtk_action_group_set_translation_domain (priv
->action_group
, GETTEXT_PACKAGE
);
446 gtk_action_group_add_actions (priv
->action_group
,
447 entries
, G_N_ELEMENTS (entries
),
450 priv
->bookmark_actions
= gtk_action_group_new ("BookmarkActions");
451 gtk_action_group_set_translate_func (priv
->bookmark_actions
, NULL
, NULL
, NULL
);
453 priv
->ui_manager
= gtk_ui_manager_new ();
454 gtk_ui_manager_insert_action_group (priv
->ui_manager
, priv
->action_group
, 0);
455 gtk_ui_manager_insert_action_group (priv
->ui_manager
, priv
->bookmark_actions
, 1);
456 gtk_ui_manager_insert_action_group (priv
->ui_manager
,
457 yelp_application_get_action_group (priv
->application
),
459 view_actions
= yelp_view_get_action_group (priv
->view
);
460 gtk_ui_manager_insert_action_group (priv
->ui_manager
, view_actions
, 3);
461 gtk_window_add_accel_group (GTK_WINDOW (window
),
462 gtk_ui_manager_get_accel_group (priv
->ui_manager
));
463 gtk_ui_manager_add_ui_from_string (priv
->ui_manager
, YELP_UI
, -1, NULL
);
464 gtk_box_pack_start (GTK_BOX (priv
->vbox_view
),
465 gtk_ui_manager_get_widget (priv
->ui_manager
, "/ui/menubar"),
468 priv
->bookmarks_merge_id
= gtk_ui_manager_new_merge_id (priv
->ui_manager
);
469 priv
->bookmarks_changed
=
470 g_signal_connect (priv
->application
, "bookmarks-changed",
471 G_CALLBACK (app_bookmarks_changed
), window
);
473 priv
->hbox
= gtk_hbox_new (FALSE
, 0);
474 g_object_set (priv
->hbox
, "border-width", 2, NULL
);
475 gtk_box_pack_start (GTK_BOX (priv
->vbox_view
), priv
->hbox
, FALSE
, FALSE
, 0);
477 action
= gtk_action_group_get_action (view_actions
, "YelpViewGoBack");
478 button
= gtk_action_create_tool_item (action
);
479 gtk_box_pack_start (GTK_BOX (priv
->hbox
),
482 action
= gtk_action_group_get_action (view_actions
, "YelpViewGoForward");
483 button
= gtk_action_create_tool_item (action
);
484 gtk_box_pack_start (GTK_BOX (priv
->hbox
),
488 priv
->entry
= (YelpLocationEntry
*) yelp_location_entry_new (priv
->view
,
489 YELP_BOOKMARKS (priv
->application
));
490 g_signal_connect (gtk_bin_get_child (GTK_BIN (priv
->entry
)), "focus-in-event",
491 G_CALLBACK (entry_focus_in
), window
);
492 g_signal_connect (priv
->entry
, "focus-out-event",
493 G_CALLBACK (entry_focus_out
), window
);
495 priv
->align_location
= g_object_ref_sink (gtk_alignment_new (0.0, 0.5, 1.0, 0.0));
496 gtk_box_pack_start (GTK_BOX (priv
->hbox
),
497 GTK_WIDGET (priv
->align_location
),
499 gtk_container_add (GTK_CONTAINER (priv
->align_location
), GTK_WIDGET (priv
->entry
));
501 priv
->hidden_entry
= gtk_entry_new ();
502 priv
->align_hidden
= g_object_ref_sink (gtk_alignment_new (0.0, 0.5, 1.0, 0.0));
503 gtk_container_add (GTK_CONTAINER (priv
->align_hidden
), GTK_WIDGET (priv
->hidden_entry
));
505 g_signal_connect (priv
->hidden_entry
, "activate",
506 G_CALLBACK (hidden_entry_activate
), window
);
507 g_signal_connect_swapped (priv
->hidden_entry
, "focus-out-event",
508 G_CALLBACK (hidden_entry_hide
), window
);
509 g_signal_connect (priv
->hidden_entry
, "key-press-event",
510 G_CALLBACK (hidden_key_press
), window
);
512 scroll
= gtk_scrolled_window_new (NULL
, NULL
);
513 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll
),
514 GTK_POLICY_AUTOMATIC
,
515 GTK_POLICY_AUTOMATIC
);
516 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll
),
518 gtk_box_pack_start (GTK_BOX (priv
->vbox_view
), scroll
, TRUE
, TRUE
, 0);
520 priv
->find_bar
= g_object_ref_sink (gtk_hbox_new (FALSE
, 6));
521 g_object_set (priv
->find_bar
,
526 label
= gtk_label_new (_("Find:"));
527 gtk_box_pack_start (GTK_BOX (priv
->find_bar
), label
, FALSE
, FALSE
, 6);
529 priv
->find_entry
= gtk_entry_new ();
530 g_signal_connect (priv
->find_entry
, "changed",
531 G_CALLBACK (find_entry_changed
), window
);
532 g_signal_connect (priv
->find_entry
, "key-press-event",
533 G_CALLBACK (find_entry_key_press
), window
);
534 g_signal_connect (priv
->find_entry
, "focus-out-event",
535 G_CALLBACK (find_entry_focus_out
), window
);
536 g_object_set (priv
->find_entry
, "width-request", 300, NULL
);
537 gtk_box_pack_start (GTK_BOX (priv
->find_bar
), priv
->find_entry
, FALSE
, FALSE
, 0);
539 priv
->find_label
= gtk_label_new ("");
540 g_object_set (priv
->find_label
, "xalign", 0.0, NULL
);
541 gtk_box_pack_start (GTK_BOX (priv
->find_bar
), priv
->find_label
, FALSE
, FALSE
, 0);
543 priv
->read_later
= g_object_ref_sink (gtk_info_bar_new ());
544 vbox
= gtk_vbox_new (FALSE
, 0);
545 color
= yelp_settings_get_color (yelp_settings_get_default (),
546 YELP_SETTINGS_COLOR_TEXT_LIGHT
);
547 text
= g_markup_printf_escaped ("<span weight='bold' color='%s'>%s</span>",
548 color
, _("Read Later"));
549 button
= gtk_label_new (text
);
550 g_object_set (button
, "use-markup", TRUE
, "xalign", 0.0, NULL
);
553 gtk_box_pack_start (GTK_BOX (vbox
), button
, FALSE
, FALSE
, 0);
554 gtk_box_pack_start (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (priv
->read_later
))),
557 priv
->read_later_vbox
= gtk_vbox_new (FALSE
, 0);
558 gtk_box_pack_start (GTK_BOX (vbox
), priv
->read_later_vbox
, FALSE
, FALSE
, 0);
560 g_signal_connect (priv
->view
, "new-view-requested", G_CALLBACK (view_new_window
), window
);
561 g_signal_connect (priv
->view
, "loaded", G_CALLBACK (view_loaded
), window
);
562 g_signal_connect (priv
->view
, "notify::yelp-uri", G_CALLBACK (view_uri_selected
), window
);
563 g_signal_connect_swapped (priv
->view
, "notify::page-id",
564 G_CALLBACK (window_set_bookmark_action
), window
);
565 window_set_bookmark_action (window
);
566 g_signal_connect (priv
->view
, "notify::root-title", G_CALLBACK (view_root_title
), window
);
567 gtk_container_add (GTK_CONTAINER (scroll
), GTK_WIDGET (priv
->view
));
568 gtk_widget_grab_focus (GTK_WIDGET (priv
->view
));
570 gtk_drag_dest_set (GTK_WIDGET (window
),
571 GTK_DEST_DEFAULT_ALL
,
574 gtk_drag_dest_add_uri_targets (GTK_WIDGET (window
));
575 g_signal_connect (window
, "drag-data-received",
576 G_CALLBACK (window_drag_received
), NULL
);
579 /******************************************************************************/
582 yelp_window_new (YelpApplication
*app
)
586 window
= (YelpWindow
*) g_object_new (YELP_TYPE_WINDOW
, "application", app
, NULL
);
592 yelp_window_load_uri (YelpWindow
*window
,
595 YelpWindowPrivate
*priv
= GET_PRIV (window
);
597 yelp_view_load_uri (priv
->view
, uri
);
601 yelp_window_get_uri (YelpWindow
*window
)
604 YelpWindowPrivate
*priv
= GET_PRIV (window
);
605 g_object_get (G_OBJECT (priv
->view
), "yelp-uri", &uri
, NULL
);
610 yelp_window_get_geometry (YelpWindow
*window
,
614 YelpWindowPrivate
*priv
= GET_PRIV (window
);
615 *width
= priv
->width
;
616 *height
= priv
->height
;
619 /******************************************************************************/
622 window_new (GtkAction
*action
, YelpWindow
*window
)
626 YelpWindowPrivate
*priv
= GET_PRIV (window
);
628 g_object_get (priv
->view
, "yelp-uri", &yuri
, NULL
);
629 uri
= yelp_uri_get_document_uri (yuri
);
631 yelp_application_new_window (priv
->application
, uri
);
634 g_object_unref (yuri
);
638 window_drag_received (YelpWindow
*window
,
639 GdkDragContext
*context
,
642 GtkSelectionData
*data
,
647 gchar
**uris
= gtk_selection_data_get_uris (data
);
648 if (uris
&& uris
[0]) {
649 YelpUri
*uri
= yelp_uri_new (uris
[0]);
650 yelp_window_load_uri (window
, uri
);
651 g_object_unref (uri
);
653 gtk_drag_finish (context
, TRUE
, FALSE
, time
);
655 gtk_drag_finish (context
, FALSE
, FALSE
, time
);
659 window_map_event (YelpWindow
*window
,
663 YelpWindowPrivate
*priv
= GET_PRIV (window
);
664 priv
->configured
= TRUE
;
669 window_configure_event (YelpWindow
*window
,
670 GdkEventConfigure
*event
,
673 YelpWindowPrivate
*priv
= GET_PRIV (window
);
674 gboolean skip
= TRUE
;
675 if (priv
->width
!= event
->width
) {
677 priv
->width
= event
->width
;
679 if (priv
->height
!= event
->height
) {
681 priv
->height
= event
->height
;
683 /* Skip the configure-event signals that GTK+ sends as it's mapping
684 * the window, and also skip if the event didn't change the size of
685 * the window (i.e. it was just a move).
687 if (!priv
->configured
|| skip
)
690 if (priv
->resize_signal
> 0)
691 g_source_remove (priv
->resize_signal
);
692 priv
->resize_signal
= g_timeout_add (200,
693 (GSourceFunc
) window_resize_signal
,
695 g_object_set (priv
->find_entry
, "width-request", 2 * priv
->width
/ 3, NULL
);
700 window_resize_signal (YelpWindow
*window
)
702 YelpWindowPrivate
*priv
= GET_PRIV (window
);
703 g_signal_emit (window
, signals
[RESIZE_EVENT
], 0);
704 priv
->resize_signal
= 0;
709 window_close (GtkAction
*action
, YelpWindow
*window
)
712 g_signal_emit_by_name (window
, "delete-event", NULL
, &ret
);
713 gtk_widget_destroy (GTK_WIDGET (window
));
717 window_go_all (GtkAction
*action
,
720 YelpWindowPrivate
*priv
= GET_PRIV (window
);
721 yelp_view_load (priv
->view
, "help-list:");
725 window_add_bookmark (GtkAction
*action
,
729 gchar
*doc_uri
, *page_id
, *icon
, *title
;
730 YelpWindowPrivate
*priv
= GET_PRIV (window
);
732 g_object_get (priv
->view
,
736 "page-title", &title
,
738 doc_uri
= yelp_uri_get_document_uri (uri
);
739 yelp_application_add_bookmark (YELP_BOOKMARKS (priv
->application
),
740 doc_uri
, page_id
, icon
, title
);
745 g_object_unref (uri
);
749 window_remove_bookmark (GtkAction
*action
,
753 gchar
*doc_uri
, *page_id
;
754 YelpWindowPrivate
*priv
= GET_PRIV (window
);
756 g_object_get (priv
->view
,
760 doc_uri
= yelp_uri_get_document_uri (uri
);
761 yelp_application_remove_bookmark (YELP_BOOKMARKS (priv
->application
),
765 g_object_unref (uri
);
768 window_load_bookmark (GtkAction
*action
,
773 YelpWindowPrivate
*priv
= GET_PRIV (window
);
775 /* Bookmark action names are prefixed with 'LoadBookmark-' */
776 xref
= g_strconcat ("xref:", gtk_action_get_name (action
) + 13, NULL
);
777 g_object_get (priv
->view
, "yelp-uri", &base
, NULL
);
778 uri
= yelp_uri_new_relative (base
, xref
);
780 yelp_view_load_uri (priv
->view
, uri
);
782 g_object_unref (base
);
783 g_object_unref (uri
);
788 app_bookmarks_changed (YelpApplication
*app
,
789 const gchar
*doc_uri
,
794 YelpWindowPrivate
*priv
= GET_PRIV (window
);
796 g_object_get (priv
->view
, "yelp-uri", &uri
, NULL
);
797 this_doc_uri
= yelp_uri_get_document_uri (uri
);
799 if (g_str_equal (this_doc_uri
, doc_uri
)) {
800 window_set_bookmarks (window
, doc_uri
);
803 g_free (this_doc_uri
);
804 g_object_unref (uri
);
807 typedef struct _YelpMenuEntry YelpMenuEntry
;
808 struct _YelpMenuEntry
{
815 entry_compare (YelpMenuEntry
*a
, YelpMenuEntry
*b
)
817 gint ret
= yelp_settings_cmp_icons (a
->icon
, b
->icon
);
821 if (a
->title
&& b
->title
)
822 return g_utf8_collate (a
->title
, b
->title
);
823 else if (b
->title
== NULL
)
825 else if (a
->title
== NULL
)
832 window_set_bookmarks (YelpWindow
*window
,
833 const gchar
*doc_uri
)
837 gchar
*page_id
, *icon
, *title
;
838 YelpWindowPrivate
*priv
= GET_PRIV (window
);
839 GSList
*entries
= NULL
;
841 window_set_bookmark_action (window
);
843 gtk_ui_manager_remove_ui (priv
->ui_manager
, priv
->bookmarks_merge_id
);
845 value
= yelp_application_get_bookmarks (priv
->application
, doc_uri
);
846 g_variant_get (value
, "a(sss)", &iter
);
847 while (g_variant_iter_loop (iter
, "(&s&s&s)", &page_id
, &icon
, &title
)) {
848 YelpMenuEntry
*entry
= g_new0 (YelpMenuEntry
, 1);
849 entry
->page_id
= page_id
;
850 entry
->icon
= g_strdup (icon
);
851 entry
->title
= title
;
852 entries
= g_slist_insert_sorted (entries
, entry
, (GCompareFunc
) entry_compare
);
854 for ( ; entries
!= NULL
; entries
= g_slist_delete_link (entries
, entries
)) {
857 YelpMenuEntry
*entry
= (YelpMenuEntry
*) entries
->data
;
858 gchar
*action_id
= g_strconcat ("LoadBookmark-", entry
->page_id
, NULL
);
860 bookmark
= gtk_action_group_get_action (priv
->bookmark_actions
, action_id
);
862 /* The action might have been set by a different document using
863 * the same page ID. We can just reuse the action, since it's
864 * just a page ID relative to the current URI, but we need to
865 * reset the title and icon.
867 g_object_set (bookmark
,
868 "label", entry
->title
,
869 "icon-name", entry
->icon
,
872 bookmark
= gtk_action_new (action_id
, entry
->title
, NULL
, NULL
);
873 g_signal_connect (bookmark
, "activate",
874 G_CALLBACK (window_load_bookmark
), window
);
875 gtk_action_set_icon_name (bookmark
, entry
->icon
);
876 gtk_action_group_add_action (priv
->bookmark_actions
, bookmark
);
878 gtk_ui_manager_add_ui (priv
->ui_manager
,
879 priv
->bookmarks_merge_id
,
880 "ui/menubar/BookmarksMenu/Bookmarks",
881 action_id
, action_id
,
882 GTK_UI_MANAGER_MENUITEM
,
884 gtk_ui_manager_ensure_update (priv
->ui_manager
);
885 for (cur
= gtk_action_get_proxies (bookmark
); cur
!= NULL
; cur
= cur
->next
) {
886 if (GTK_IS_IMAGE_MENU_ITEM (cur
->data
))
887 g_object_set (cur
->data
, "always-show-image", TRUE
, NULL
);
890 g_free (entry
->icon
);
894 g_variant_iter_free (iter
);
895 g_variant_unref (value
);
899 window_set_bookmark_action (YelpWindow
*window
)
902 gchar
*doc_uri
= NULL
, *page_id
= NULL
;
903 GtkAction
*action_add
, *action_del
;
905 YelpWindowPrivate
*priv
= GET_PRIV (window
);
907 action_add
= gtk_action_group_get_action (priv
->action_group
, "AddBookmark");
908 action_del
= gtk_action_group_get_action (priv
->action_group
, "RemoveBookmark");
910 g_object_get (priv
->view
,
914 if (page_id
== NULL
|| uri
== NULL
) {
915 gtk_action_set_sensitive (action_add
, FALSE
);
916 gtk_action_set_sensitive (action_del
, FALSE
);
919 doc_uri
= yelp_uri_get_document_uri (uri
);
920 bookmarked
= yelp_application_is_bookmarked (YELP_BOOKMARKS (priv
->application
),
922 gtk_action_set_sensitive (action_add
, !bookmarked
);
923 gtk_action_set_sensitive (action_del
, bookmarked
);
929 g_object_unref (uri
);
933 window_start_search (GtkAction
*action
, YelpWindow
*window
)
935 YelpWindowPrivate
*priv
= GET_PRIV (window
);
937 yelp_location_entry_start_search (priv
->entry
);
941 window_open_location (GtkAction
*action
, YelpWindow
*window
)
943 YelpUri
*yuri
= NULL
;
947 YelpWindowPrivate
*priv
= GET_PRIV (window
);
949 gtk_container_remove (GTK_CONTAINER (priv
->hbox
),
950 priv
->align_location
);
951 gtk_box_pack_start (GTK_BOX (priv
->hbox
),
955 gtk_widget_show_all (priv
->align_hidden
);
956 gtk_entry_set_text (GTK_ENTRY (priv
->hidden_entry
), "");
957 gtk_widget_grab_focus (priv
->hidden_entry
);
959 color
= yelp_settings_get_color (yelp_settings_get_default (),
960 YELP_SETTINGS_COLOR_YELLOW_BASE
);
961 if (gdk_color_parse (color
, &yellow
)) {
962 gtk_widget_modify_base (priv
->hidden_entry
,
968 g_object_get (priv
->view
, "yelp-uri", &yuri
, NULL
);
970 uri
= yelp_uri_get_canonical_uri (yuri
);
971 g_object_unref (yuri
);
975 gtk_entry_set_text (GTK_ENTRY (priv
->hidden_entry
), uri
);
976 c
= strchr (uri
, ':');
978 gtk_editable_select_region (GTK_EDITABLE (priv
->hidden_entry
), c
- uri
+ 1, -1);
980 gtk_editable_select_region (GTK_EDITABLE (priv
->hidden_entry
), 5, -1);
986 read_later_resolved (YelpUri
*uri
,
990 const gchar
*text
= (const gchar
*) g_object_get_data ((GObject
*) uri
, "link-text");
991 YelpWindowPrivate
*priv
= GET_PRIV (window
);
995 g_object_get (priv
->view
, "yelp-uri", &base
, NULL
);
996 doc_uri
= yelp_uri_get_document_uri (uri
);
997 fulluri
= yelp_uri_get_canonical_uri (uri
);
999 yelp_application_add_read_later (priv
->application
, doc_uri
, fulluri
, text
);
1001 g_object_unref (base
);
1007 window_read_later (GtkAction
*action
,
1010 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1014 uri
= yelp_view_get_active_link_uri (priv
->view
);
1015 text
= yelp_view_get_active_link_text (priv
->view
);
1017 g_object_set_data_full ((GObject
*) uri
, "link-text", text
, g_free
);
1019 if (!yelp_uri_is_resolved (uri
)) {
1020 g_signal_connect (uri
, "resolved",
1021 G_CALLBACK (read_later_resolved
),
1023 yelp_uri_resolve (uri
);
1026 read_later_resolved (uri
, window
);
1031 read_later_clicked (GtkLinkButton
*button
,
1034 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1039 fulluri
= g_strdup (gtk_link_button_get_uri (button
));
1041 g_object_get (priv
->view
, "yelp-uri", &base
, NULL
);
1042 doc_uri
= yelp_uri_get_document_uri (base
);
1044 yelp_application_remove_read_later (priv
->application
, doc_uri
, fulluri
);
1046 g_object_unref (base
);
1049 yelp_view_load (priv
->view
, fulluri
);
1056 app_read_later_changed (YelpApplication
*app
,
1057 const gchar
*doc_uri
,
1061 GVariantIter
*viter
;
1062 gchar
*uri
, *title
; /* do not free */
1064 gboolean has_children
= FALSE
;
1065 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1067 children
= gtk_container_get_children (GTK_CONTAINER (priv
->read_later_vbox
));
1069 gtk_container_remove (GTK_CONTAINER (priv
->read_later_vbox
),
1070 GTK_WIDGET (children
->data
));
1071 children
= g_list_delete_link (children
, children
);
1074 value
= yelp_application_get_read_later (priv
->application
, doc_uri
);
1075 g_variant_get (value
, "a(ss)", &viter
);
1076 while (g_variant_iter_loop (viter
, "(&s&s)", &uri
, &title
)) {
1077 GtkWidget
*align
, *link
;
1079 align
= gtk_alignment_new (0.0, 0.0, 0.0, 0.0);
1080 g_object_set (align
, "left-padding", 6, NULL
);
1081 gtk_box_pack_start (GTK_BOX (priv
->read_later_vbox
), align
, FALSE
, FALSE
, 0);
1083 link
= gtk_link_button_new_with_label (uri
, title
);
1084 g_object_set (link
, "xalign", 0.0, NULL
);
1085 g_signal_connect (link
, "activate-link", G_CALLBACK (read_later_clicked
), window
);
1086 gtk_container_add (GTK_CONTAINER (align
), link
);
1088 gtk_widget_show_all (align
);
1089 has_children
= TRUE
;
1091 g_variant_iter_free (viter
);
1092 g_variant_unref (value
);
1095 if (gtk_widget_get_parent (priv
->read_later
) == NULL
) {
1096 gtk_box_pack_end (GTK_BOX (priv
->vbox_full
), priv
->read_later
, FALSE
, FALSE
, 0);
1097 gtk_widget_show_all (priv
->read_later
);
1101 if (gtk_widget_get_parent (priv
->read_later
) != NULL
)
1102 gtk_container_remove (GTK_CONTAINER (priv
->vbox_full
), priv
->read_later
);
1107 window_find_in_page (GtkAction
*action
,
1110 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1112 if (gtk_widget_get_parent (priv
->find_bar
) != NULL
) {
1113 gtk_widget_grab_focus (priv
->find_entry
);
1117 g_object_set (priv
->find_entry
, "width-request", 2 * priv
->width
/ 3, NULL
);
1119 gtk_box_pack_end (GTK_BOX (priv
->vbox_view
), priv
->find_bar
, FALSE
, FALSE
, 0);
1120 gtk_widget_show_all (priv
->find_bar
);
1121 gtk_widget_grab_focus (priv
->find_entry
);
1125 find_entry_key_press (GtkEntry
*entry
,
1129 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1131 if (event
->keyval
== GDK_KEY_Escape
) {
1132 gtk_widget_grab_focus (GTK_WIDGET (priv
->view
));
1136 if (event
->keyval
== GDK_KEY_Return
|| event
->keyval
== GDK_KEY_KP_Enter
) {
1137 gchar
*text
= gtk_editable_get_chars (GTK_EDITABLE (entry
), 0, -1);
1138 webkit_web_view_search_text (WEBKIT_WEB_VIEW (priv
->view
),
1139 text
, FALSE
, TRUE
, TRUE
);
1148 find_entry_focus_out (GtkEntry
*entry
,
1149 GdkEventFocus
*event
,
1152 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1153 webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (priv
->view
));
1154 webkit_web_view_set_highlight_text_matches (WEBKIT_WEB_VIEW (priv
->view
), FALSE
);
1155 if (gtk_widget_get_parent (priv
->find_bar
) != NULL
)
1156 gtk_container_remove (GTK_CONTAINER (priv
->vbox_view
), priv
->find_bar
);
1161 find_entry_changed (GtkEntry
*entry
,
1166 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1168 webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (priv
->view
));
1170 text
= gtk_editable_get_chars (GTK_EDITABLE (entry
), 0, -1);
1172 if (text
[0] == '\0') {
1173 gtk_label_set_text (GTK_LABEL (priv
->find_label
), "");
1177 count
= webkit_web_view_mark_text_matches (WEBKIT_WEB_VIEW (priv
->view
),
1180 gchar
*label
= g_strdup_printf (ngettext ("%i match", "%i matches", count
), count
);
1181 webkit_web_view_set_highlight_text_matches (WEBKIT_WEB_VIEW (priv
->view
), TRUE
);
1182 webkit_web_view_search_text (WEBKIT_WEB_VIEW (priv
->view
),
1183 text
, FALSE
, TRUE
, TRUE
);
1184 gtk_label_set_text (GTK_LABEL (priv
->find_label
), label
);
1188 webkit_web_view_set_highlight_text_matches (WEBKIT_WEB_VIEW (priv
->view
), FALSE
);
1189 gtk_label_set_text (GTK_LABEL (priv
->find_label
), _("No matches"));
1196 entry_color_animate (YelpWindow
*window
)
1199 GdkColor yellow
, base
;
1200 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1202 color
= yelp_settings_get_color (yelp_settings_get_default (),
1203 YELP_SETTINGS_COLOR_YELLOW_BASE
);
1204 gdk_color_parse (color
, &yellow
);
1207 color
= yelp_settings_get_color (yelp_settings_get_default (),
1208 YELP_SETTINGS_COLOR_BASE
);
1209 gdk_color_parse (color
, &base
);
1212 yellow
.red
= priv
->entry_color_step
* yellow
.red
+ (1.0 - priv
->entry_color_step
) * base
.red
;
1213 yellow
.green
= priv
->entry_color_step
* yellow
.green
+ (1.0 - priv
->entry_color_step
) * base
.green
;
1214 yellow
.blue
= priv
->entry_color_step
* yellow
.blue
+ (1.0 - priv
->entry_color_step
) * base
.blue
;
1216 gtk_widget_modify_base (gtk_bin_get_child (GTK_BIN (priv
->entry
)), GTK_STATE_NORMAL
, &yellow
);
1218 priv
->entry_color_step
-= 0.05;
1220 if (priv
->entry_color_step
< 0.0) {
1221 priv
->entry_color_animate
= 0;
1229 entry_focus_in (GtkEntry
*entry
,
1230 GdkEventFocus
*event
,
1233 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1235 if (priv
->entry_color_animate
!= 0)
1238 priv
->entry_color_step
= 1.0;
1239 priv
->entry_color_animate
= g_timeout_add (40, (GSourceFunc
) entry_color_animate
, window
);
1245 entry_focus_out (YelpLocationEntry
*entry
,
1246 GdkEventFocus
*event
,
1249 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1250 gtk_widget_grab_focus (GTK_WIDGET (priv
->view
));
1255 view_new_window (YelpView
*view
,
1259 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1260 yelp_application_new_window_uri (priv
->application
, uri
);
1264 view_loaded (YelpView
*view
,
1270 YelpViewState state
;
1272 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1278 doc_uri
= yelp_uri_get_document_uri (uri
);
1280 gdkwin
= gtk_widget_get_window (GTK_WIDGET (window
));
1282 gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (window
)), NULL
);
1284 if (state
!= YELP_VIEW_STATE_ERROR
) {
1285 gchar
*page_id
, *icon
, *title
;
1287 "page-id", &page_id
,
1289 "page-title", &title
,
1291 yelp_application_update_bookmarks (priv
->application
,
1296 app_read_later_changed (priv
->application
, doc_uri
, window
);
1303 g_object_unref (uri
);
1307 view_uri_selected (YelpView
*view
,
1314 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1316 g_object_get (G_OBJECT (view
), "yelp-uri", &uri
, NULL
);
1320 gdkwin
= gtk_widget_get_window (GTK_WIDGET (window
));
1322 gdk_window_set_cursor (gdkwin
,
1323 gdk_cursor_new_for_display (gdk_window_get_display (gdkwin
),
1326 doc_uri
= yelp_uri_get_document_uri (uri
);
1327 if (priv
->doc_uri
== NULL
|| !g_str_equal (priv
->doc_uri
, doc_uri
)) {
1328 window_set_bookmarks (window
, doc_uri
);
1329 g_free (priv
->doc_uri
);
1330 priv
->doc_uri
= doc_uri
;
1336 g_object_unref (uri
);
1340 view_is_xref_uri (YelpView
*view
,
1345 return g_str_has_prefix (uri
, "xref:");
1349 view_root_title (YelpView
*view
,
1354 g_object_get (view
, "root-title", &title
, NULL
);
1357 gtk_window_set_title (GTK_WINDOW (window
), title
);
1360 gtk_window_set_title (GTK_WINDOW (window
), _("Help"));
1365 hidden_entry_activate (GtkEntry
*entry
,
1368 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1369 YelpUri
*uri
= yelp_uri_new (gtk_entry_get_text (entry
));
1371 yelp_window_load_uri (window
, uri
);
1372 g_object_unref (uri
);
1374 gtk_widget_grab_focus (GTK_WIDGET (priv
->view
));
1378 hidden_entry_hide (YelpWindow
*window
)
1380 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1382 if (gtk_widget_get_parent (priv
->align_hidden
) != NULL
) {
1383 gtk_container_remove (GTK_CONTAINER (priv
->hbox
),
1384 priv
->align_hidden
);
1385 gtk_box_pack_start (GTK_BOX (priv
->hbox
),
1386 priv
->align_location
,
1392 hidden_key_press (GtkWidget
*widget
,
1396 YelpWindowPrivate
*priv
= GET_PRIV (window
);
1397 if (event
->keyval
== GDK_KEY_Escape
) {
1398 gtk_widget_grab_focus (GTK_WIDGET (priv
->view
));