1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2001-2008 Imendio AB
4  *
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.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
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.
19  */
20 
21 /*
22  * Fullscreen mode code adapted from gedit
23  *  Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
24  *  Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi
25  *  Copyright (C) 2002-2005 Paolo Maggi
26  */
27 
28 #include "config.h"
29 #include <string.h>
30 #include <glib/gi18n-lib.h>
31 #include <gdk/gdkkeysyms.h>
32 #include <gtk/gtk.h>
33 #include <webkit/webkit.h>
34 
35 #ifdef GDK_WINDOWING_QUARTZ
36 #include <ige-mac-integration.h>
37 #endif
38 
39 #include "dh-book-tree.h"
40 #include "dh-book-manager.h"
41 #include "dh-book.h"
42 #include "dh-preferences.h"
43 #include "dh-search.h"
44 #include "dh-window.h"
45 #include "dh-util.h"
46 #include "dh-marshal.h"
47 #include "dh-enum-types.h"
48 #include "eggfindbar.h"
49 #include "ige-conf.h"
50 
51 #define FULLSCREEN_ANIMATION_SPEED 4
52 
53 struct _DhWindowPriv {
54         DhBase         *base;
55 
56         GtkWidget      *main_box;
57         GtkWidget      *menu_box;
58         GtkWidget      *hpaned;
59         GtkWidget      *control_notebook;
60         GtkWidget      *book_tree;
61         GtkWidget      *search;
62         GtkWidget      *notebook;
63 
64         GtkWidget      *vbox;
65         GtkWidget      *findbar;
66 
67         GtkWidget      *fullscreen_controls;
68         guint           fullscreen_animation_timeout_id;
69         gboolean        fullscreen_animation_enter;
70 
71         GtkUIManager   *manager;
72         GtkActionGroup *action_group;
73 
74         DhLink         *selected_search_link;
75         guint           find_source_id;
76 };
77 
78 enum {
79         OPEN_LINK,
80         LAST_SIGNAL
81 };
82 
83 static gint signals[LAST_SIGNAL] = { 0 };
84 
85 static guint tab_accel_keys[] = {
86         GDK_1, GDK_2, GDK_3, GDK_4, GDK_5,
87         GDK_6, GDK_7, GDK_8, GDK_9, GDK_0
88 };
89 
90 static const
91 struct
92 {
93         gchar *name;
94         int    level;
95 }
96 zoom_levels[] =
97 {
98         { N_("50%"), 70 },
99         { N_("75%"), 84 },
100         { N_("100%"), 100 },
101         { N_("125%"), 119 },
102         { N_("150%"), 141 },
103         { N_("175%"), 168 },
104         { N_("200%"), 200 },
105         { N_("300%"), 283 },
106         { N_("400%"), 400 }
107 };
108 
109 #define ZOOM_MINIMAL    (zoom_levels[0].level)
110 #define ZOOM_MAXIMAL    (zoom_levels[8].level)
111 #define ZOOM_DEFAULT    (zoom_levels[2].level)
112 
113 #if GTK_CHECK_VERSION (2,17,5)
114 #define ERRORS_IN_INFOBAR
115 #endif
116 
117 static void           dh_window_class_init           (DhWindowClass   *klass);
118 static void           dh_window_init                 (DhWindow        *window);
119 static void           window_populate                (DhWindow        *window);
120 static void           window_tree_link_selected_cb   (GObject         *ignored,
121                                                       DhLink          *link,
122                                                       DhWindow        *window);
123 static void           window_search_link_selected_cb (GObject         *ignored,
124                                                       DhLink          *link,
125                                                       DhWindow        *window);
126 static void           window_check_history           (DhWindow        *window,
127                                                       WebKitWebView   *web_view);
128 static void           window_web_view_tab_accel_cb   (GtkAccelGroup   *accel_group,
129                                                       GObject         *object,
130                                                       guint            key,
131                                                       GdkModifierType  mod,
132                                                       DhWindow        *window);
133 static void           window_find_search_changed_cb  (GObject         *object,
134                                                       GParamSpec      *arg1,
135                                                       DhWindow        *window);
136 static void           window_find_case_changed_cb    (GObject         *object,
137                                                       GParamSpec      *arg1,
138                                                       DhWindow        *window);
139 static void           window_find_previous_cb        (GtkEntry        *entry,
140                                                       DhWindow        *window);
141 static void           window_find_next_cb            (GtkEntry        *entry,
142                                                       DhWindow        *window);
143 static void           window_findbar_close_cb        (GtkWidget       *widget,
144                                                       DhWindow        *window);
145 static GtkWidget *    window_new_tab_label           (DhWindow        *window,
146                                                       const gchar     *label,
147                                                       const GtkWidget *parent);
148 static int            window_open_new_tab            (DhWindow        *window,
149                                                       const gchar     *location,
150                                                       gboolean         switch_focus);
151 static WebKitWebView *window_get_active_web_view     (DhWindow        *window);
152 #ifdef ERRORS_IN_INFOBAR
153 static GtkWidget *    window_get_active_info_bar     (DhWindow *window);
154 #endif
155 static void           window_update_title            (DhWindow        *window,
156                                                       WebKitWebView   *web_view,
157                                                       const gchar     *title);
158 static void           window_tab_set_title           (DhWindow        *window,
159                                                       WebKitWebView   *web_view,
160                                                       const gchar     *title);
161 static void           window_close_tab               (DhWindow *window,
162                                                       gint      page_num);
163 
164 G_DEFINE_TYPE (DhWindow, dh_window, GTK_TYPE_WINDOW);
165 
166 #define GET_PRIVATE(instance) G_TYPE_INSTANCE_GET_PRIVATE \
167   (instance, DH_TYPE_WINDOW, DhWindowPriv);
168 
169 static void
window_activate_new_window(GtkAction * action,DhWindow * window)170 window_activate_new_window (GtkAction *action,
171                             DhWindow  *window)
172 {
173         DhWindowPriv *priv;
174         GtkWidget    *new_window;
175 
176         priv = window->priv;
177 
178         new_window = dh_base_new_window (priv->base);
179         gtk_widget_show (new_window);
180 }
181 
182 static void
window_activate_new_tab(GtkAction * action,DhWindow * window)183 window_activate_new_tab (GtkAction *action,
184                          DhWindow  *window)
185 {
186         window_open_new_tab (window, NULL, TRUE);
187 }
188 
189 static void
window_activate_print(GtkAction * action,DhWindow * window)190 window_activate_print (GtkAction *action,
191                        DhWindow  *window)
192 {
193     WebKitWebView *web_view;
194 
195     web_view = window_get_active_web_view (window);
196     webkit_web_view_execute_script (web_view, "print();");
197 }
198 
199 static void
window_close_tab(DhWindow * window,gint page_num)200 window_close_tab (DhWindow *window,
201                   gint      page_num)
202 {
203         DhWindowPriv *priv;
204         gint          pages;
205 
206         priv = window->priv;
207 
208         gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), page_num);
209 
210         pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
211 
212         if (pages == 0) {
213                 gtk_widget_destroy (GTK_WIDGET (window));
214         }
215         else if (pages == 1) {
216                 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->notebook), FALSE);
217         }
218 }
219 
220 static void
window_activate_close(GtkAction * action,DhWindow * window)221 window_activate_close (GtkAction *action,
222                        DhWindow  *window)
223 {
224         gint          page_num;
225 
226         page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (window->priv->notebook));
227         window_close_tab (window, page_num);
228 }
229 
230 static void
window_activate_quit(GtkAction * action,DhWindow * window)231 window_activate_quit (GtkAction *action,
232                       DhWindow  *window)
233 {
234         dh_base_quit (window->priv->base);
235 }
236 
237 static void
window_activate_copy(GtkAction * action,DhWindow * window)238 window_activate_copy (GtkAction *action,
239                       DhWindow  *window)
240 {
241         GtkWidget *widget;
242         DhWindowPriv  *priv;
243 
244         priv = window->priv;
245 
246         widget = gtk_window_get_focus (GTK_WINDOW (window));
247 
248         if (GTK_IS_EDITABLE (widget)) {
249                 gtk_editable_copy_clipboard (GTK_EDITABLE (widget));
250         } else if (GTK_IS_TREE_VIEW (widget) &&
251                    gtk_widget_is_ancestor (widget, priv->search) &&
252                    priv->selected_search_link) {
253                 GtkClipboard *clipboard;
254                 clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
255                 gtk_clipboard_set_text (clipboard,
256                                 dh_link_get_name(priv->selected_search_link), -1);
257         } else {
258                 WebKitWebView *web_view;
259 
260                 web_view = window_get_active_web_view (window);
261                 webkit_web_view_copy_clipboard (web_view);
262         }
263 }
264 
265 static void
window_activate_find(GtkAction * action,DhWindow * window)266 window_activate_find (GtkAction *action,
267                       DhWindow  *window)
268 {
269         DhWindowPriv  *priv;
270         WebKitWebView *web_view;
271 
272         priv = window->priv;
273         web_view = window_get_active_web_view (window);
274 
275         gtk_widget_show (priv->findbar);
276         gtk_widget_grab_focus (priv->findbar);
277 
278         webkit_web_view_set_highlight_text_matches (web_view, TRUE);
279 }
280 
281 static int
window_get_current_zoom_level_index(DhWindow * window)282 window_get_current_zoom_level_index (DhWindow *window)
283 {
284         WebKitWebView *web_view;
285         float zoom_level;
286         int zoom_level_as_int = ZOOM_DEFAULT;
287         int i;
288 
289         web_view = window_get_active_web_view (window);
290         if (web_view) {
291                 g_object_get (web_view, "zoom-level", &zoom_level, NULL);
292                 zoom_level_as_int = (int)(zoom_level*100);
293         }
294 
295         for (i=0; zoom_levels[i].level != ZOOM_MAXIMAL; i++) {
296                 if (zoom_levels[i].level == zoom_level_as_int)
297                         return i;
298         }
299         return i;
300 }
301 
302 static void
window_update_zoom_actions_sensitiveness(DhWindow * window)303 window_update_zoom_actions_sensitiveness (DhWindow *window)
304 {
305         DhWindowPriv *priv;
306         GtkAction *zoom_in, *zoom_out, *zoom_default;
307         int zoom_level_idx;
308 
309         priv = window->priv;
310         zoom_in = gtk_action_group_get_action (priv->action_group, "ZoomIn");
311         zoom_out = gtk_action_group_get_action (priv->action_group, "ZoomOut");
312         zoom_default = gtk_action_group_get_action (priv->action_group, "ZoomDefault");
313 
314         zoom_level_idx = window_get_current_zoom_level_index (window);
315 
316         gtk_action_set_sensitive (zoom_in,
317                                   zoom_levels[zoom_level_idx].level < ZOOM_MAXIMAL);
318         gtk_action_set_sensitive (zoom_out,
319                                   zoom_levels[zoom_level_idx].level > ZOOM_MINIMAL);
320         gtk_action_set_sensitive (zoom_default,
321                                   zoom_levels[zoom_level_idx].level != ZOOM_DEFAULT);
322 }
323 
324 static void
window_activate_zoom_in(GtkAction * action,DhWindow * window)325 window_activate_zoom_in (GtkAction *action,
326                          DhWindow  *window)
327 {
328         int zoom_level_idx;
329 
330         zoom_level_idx = window_get_current_zoom_level_index (window);
331         if (zoom_levels[zoom_level_idx].level < ZOOM_MAXIMAL) {
332                 WebKitWebView *web_view;
333 
334                 web_view = window_get_active_web_view (window);
335                 g_object_set (web_view,
336                               "zoom-level", (float)(zoom_levels[zoom_level_idx+1].level)/100,
337                               NULL);
338                 window_update_zoom_actions_sensitiveness (window);
339         }
340 
341 }
342 
343 static void
window_activate_zoom_out(GtkAction * action,DhWindow * window)344 window_activate_zoom_out (GtkAction *action,
345                           DhWindow  *window)
346 {
347         int zoom_level_idx;
348 
349         zoom_level_idx = window_get_current_zoom_level_index (window);
350         if (zoom_levels[zoom_level_idx].level > ZOOM_MINIMAL) {
351                 WebKitWebView *web_view;
352 
353                 web_view = window_get_active_web_view (window);
354                 g_object_set (web_view,
355                               "zoom-level", (float)(zoom_levels[zoom_level_idx-1].level)/100,
356                               NULL);
357                 window_update_zoom_actions_sensitiveness (window);
358         }
359 }
360 
361 static void
window_activate_zoom_default(GtkAction * action,DhWindow * window)362 window_activate_zoom_default (GtkAction *action,
363                               DhWindow  *window)
364 {
365         WebKitWebView *web_view;
366 
367         web_view = window_get_active_web_view (window);
368         g_object_set (web_view, "zoom-level", (float)(ZOOM_DEFAULT)/100, NULL);
369         window_update_zoom_actions_sensitiveness (window);
370 }
371 
372 static gboolean
run_fullscreen_animation(gpointer data)373 run_fullscreen_animation (gpointer data)
374 {
375 	DhWindow *window = DH_WINDOW (data);
376 	GdkScreen *screen;
377 	GdkRectangle fs_rect;
378 	gint x, y;
379 
380 	screen = gtk_window_get_screen (GTK_WINDOW (window));
381 	gdk_screen_get_monitor_geometry (screen,
382 					 gdk_screen_get_monitor_at_window (screen,
383 									   gtk_widget_get_window (GTK_WIDGET (window))),
384 					 &fs_rect);
385 
386 	gtk_window_get_position (GTK_WINDOW (window->priv->fullscreen_controls),
387 				 &x, &y);
388 
389 	if (window->priv->fullscreen_animation_enter)
390 	{
391 		if (y == fs_rect.y)
392 		{
393 			window->priv->fullscreen_animation_timeout_id = 0;
394 			return FALSE;
395 		}
396 		else
397 		{
398 			gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls),
399 					 x, y + 1);
400 			return TRUE;
401 		}
402 	}
403 	else
404 	{
405 		gint w, h;
406 
407 		gtk_window_get_size (GTK_WINDOW (window->priv->fullscreen_controls),
408 				     &w, &h);
409 
410 		if (y == fs_rect.y - h + 1)
411 		{
412 			window->priv->fullscreen_animation_timeout_id = 0;
413 			return FALSE;
414 		}
415 		else
416 		{
417 			gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls),
418 					 x, y - 1);
419 			return TRUE;
420 		}
421 	}
422 }
423 
424 static void
show_hide_fullscreen_toolbar(DhWindow * window,gboolean show,gint height)425 show_hide_fullscreen_toolbar (DhWindow *window,
426                               gboolean     show,
427                               gint         height)
428 {
429         GtkSettings *settings;
430         gboolean enable_animations;
431 
432         settings = gtk_widget_get_settings (GTK_WIDGET (window));
433         g_object_get (G_OBJECT (settings),
434                       "gtk-enable-animations",
435                       &enable_animations,
436                       NULL);
437 
438         if (enable_animations)
439         {
440                 window->priv->fullscreen_animation_enter = show;
441 
442                 if (window->priv->fullscreen_animation_timeout_id == 0)
443                 {
444                         window->priv->fullscreen_animation_timeout_id =
445                                 g_timeout_add (FULLSCREEN_ANIMATION_SPEED,
446                                                (GSourceFunc) run_fullscreen_animation,
447                                                window);
448                 }
449         }
450         else
451         {
452                 GdkRectangle fs_rect;
453                 GdkScreen *screen;
454 
455                 screen = gtk_window_get_screen (GTK_WINDOW (window));
456                 gdk_screen_get_monitor_geometry (screen,
457                                                  gdk_screen_get_monitor_at_window (screen,
458                                                                                    gtk_widget_get_window (GTK_WIDGET (window))),
459                                                  &fs_rect);
460 
461                 if (show)
462                         gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls),
463                                  fs_rect.x, fs_rect.y);
464                 else
465                         gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls),
466                                          fs_rect.x, fs_rect.y - height + 1);
467         }
468 
469 }
470 
471 
472 static gboolean
on_fullscreen_controls_enter_notify_event(GtkWidget * widget,GdkEventCrossing * event,DhWindow * window)473 on_fullscreen_controls_enter_notify_event (GtkWidget        *widget,
474                                            GdkEventCrossing *event,
475                                            DhWindow      *window)
476 {
477         show_hide_fullscreen_toolbar (window, TRUE, 0);
478 
479         return FALSE;
480 }
481 
482 static gboolean
on_fullscreen_controls_leave_notify_event(GtkWidget * widget,GdkEventCrossing * event,DhWindow * window)483 on_fullscreen_controls_leave_notify_event (GtkWidget        *widget,
484                                            GdkEventCrossing *event,
485                                            DhWindow      *window)
486 {
487         GdkDisplay *display;
488         GdkScreen *screen;
489         gint w, h;
490         gint x, y;
491 
492         display = gdk_display_get_default ();
493         screen = gtk_window_get_screen (GTK_WINDOW (window));
494 
495         gtk_window_get_size (GTK_WINDOW (window->priv->fullscreen_controls), &w, &h);
496         gdk_display_get_pointer (display, &screen, &x, &y, NULL);
497 
498         /* gtk seems to emit leave notify when clicking on tool items,
499          * work around it by checking the coordinates
500          */
501         if (y >= h)
502         {
503                 show_hide_fullscreen_toolbar (window, FALSE, h);
504         }
505 
506         return FALSE;
507 }
508 
509 static gboolean
window_is_fullscreen(DhWindow * window)510 window_is_fullscreen (DhWindow *window)
511 {
512         GdkWindowState  state;
513 
514         g_return_val_if_fail (DH_IS_WINDOW (window), FALSE);
515 
516 #if GTK_CHECK_VERSION (2,14,0)
517         state = gdk_window_get_state (gtk_widget_get_window (GTK_WIDGET (window)));
518 #else
519         state = gdk_window_get_state (GTK_WIDGET (window)->window);
520 #endif
521 
522         return state & GDK_WINDOW_STATE_FULLSCREEN;
523 }
524 
525 static void
window_fullscreen_controls_build(DhWindow * window)526 window_fullscreen_controls_build (DhWindow *window)
527 {
528         GtkWidget *toolbar;
529         GtkAction *action;
530         DhWindowPriv  *priv;
531 
532         priv = window->priv;
533         if (priv->fullscreen_controls != NULL)
534                 return;
535 
536         priv->fullscreen_controls = gtk_window_new (GTK_WINDOW_POPUP);
537         gtk_window_set_transient_for (GTK_WINDOW (priv->fullscreen_controls),
538                                       GTK_WINDOW (window));
539 
540         toolbar = gtk_ui_manager_get_widget (priv->manager, "/FullscreenToolBar");
541         gtk_container_add (GTK_CONTAINER (priv->fullscreen_controls),
542                            toolbar);
543         action = gtk_action_group_get_action (priv->action_group,
544                                               "LeaveFullscreen");
545         g_object_set (action, "is-important", TRUE, NULL);
546 
547         /* Set the toolbar style */
548         gtk_toolbar_set_style (GTK_TOOLBAR (toolbar),
549                                GTK_TOOLBAR_BOTH_HORIZ);
550 
551         g_signal_connect (priv->fullscreen_controls, "enter-notify-event",
552                           G_CALLBACK (on_fullscreen_controls_enter_notify_event),
553                           window);
554         g_signal_connect (priv->fullscreen_controls, "leave-notify-event",
555                           G_CALLBACK (on_fullscreen_controls_leave_notify_event),
556                           window);
557 }
558 
559 static void
window_fullscreen_controls_show(DhWindow * window)560 window_fullscreen_controls_show (DhWindow *window)
561 {
562         GdkScreen *screen;
563         GdkRectangle fs_rect;
564         gint w, h;
565 
566         screen = gtk_window_get_screen (GTK_WINDOW (window));
567         gdk_screen_get_monitor_geometry (screen,
568                         gdk_screen_get_monitor_at_window (
569                                 screen,
570                                 gtk_widget_get_window (GTK_WIDGET (window))),
571                         &fs_rect);
572 
573         gtk_window_get_size (GTK_WINDOW (window->priv->fullscreen_controls), &w, &h);
574 
575         gtk_window_resize (GTK_WINDOW (window->priv->fullscreen_controls),
576                            fs_rect.width, h);
577 
578         gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls),
579                          fs_rect.x, fs_rect.y - h + 1);
580 
581         gtk_widget_show_all (window->priv->fullscreen_controls);
582 }
583 
584 static void
window_fullscreen(DhWindow * window)585 window_fullscreen (DhWindow *window)
586 {
587         if (window_is_fullscreen (window))
588                 return;
589 
590         gtk_window_fullscreen (GTK_WINDOW (window));
591         gtk_widget_hide (gtk_ui_manager_get_widget (window->priv->manager, "/MenuBar"));
592         gtk_widget_hide (gtk_ui_manager_get_widget (window->priv->manager, "/Toolbar"));
593 
594         window_fullscreen_controls_build (window);
595         window_fullscreen_controls_show (window);
596 }
597 
598 static void
window_unfullscreen(DhWindow * window)599 window_unfullscreen (DhWindow *window)
600 {
601         if (! window_is_fullscreen (window))
602                 return;
603 
604         gtk_window_unfullscreen (GTK_WINDOW (window));
605         gtk_widget_show (gtk_ui_manager_get_widget (window->priv->manager, "/MenuBar"));
606         gtk_widget_show (gtk_ui_manager_get_widget (window->priv->manager, "/Toolbar"));
607 
608         gtk_widget_hide (window->priv->fullscreen_controls);
609 }
610 
611 
612 static void
window_toggle_fullscreen_mode(GtkAction * action,DhWindow * window)613 window_toggle_fullscreen_mode (GtkAction *action,
614                                DhWindow  *window)
615 {
616         if (window_is_fullscreen (window)) {
617                 window_unfullscreen (window);
618         } else {
619                 window_fullscreen (window);
620         }
621 }
622 
623 static void
window_leave_fullscreen_mode(GtkAction * action,DhWindow * window)624 window_leave_fullscreen_mode (GtkAction *action,
625                               DhWindow *window)
626 {
627         GtkAction *view_action;
628 
629         view_action = gtk_action_group_get_action (window->priv->action_group,
630                                                    "ViewFullscreen");
631         g_signal_handlers_block_by_func (view_action,
632                         G_CALLBACK (window_toggle_fullscreen_mode),
633                         window);
634         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (view_action),
635                                       FALSE);
636         window_unfullscreen (window);
637         g_signal_handlers_unblock_by_func (view_action,
638                         G_CALLBACK (window_toggle_fullscreen_mode),
639                         window);
640 }
641 
642 static void
window_activate_preferences(GtkAction * action,DhWindow * window)643 window_activate_preferences (GtkAction *action,
644                              DhWindow  *window)
645 {
646         dh_preferences_show_dialog (GTK_WINDOW (window));
647 }
648 
649 static void
window_activate_back(GtkAction * action,DhWindow * window)650 window_activate_back (GtkAction *action,
651                       DhWindow  *window)
652 {
653         DhWindowPriv  *priv;
654         WebKitWebView *web_view;
655         GtkWidget     *frame;
656 
657         priv = window->priv;
658 
659         frame = gtk_notebook_get_nth_page (
660                 GTK_NOTEBOOK (priv->notebook),
661                 gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook)));
662         web_view = g_object_get_data (G_OBJECT (frame), "web_view");
663 
664         webkit_web_view_go_back (web_view);
665 }
666 
667 static void
window_activate_forward(GtkAction * action,DhWindow * window)668 window_activate_forward (GtkAction *action,
669                          DhWindow  *window)
670 {
671         DhWindowPriv  *priv;
672         WebKitWebView *web_view;
673         GtkWidget     *frame;
674 
675         priv = window->priv;
676 
677         frame = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook),
678                                            gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook))
679                                           );
680         web_view = g_object_get_data (G_OBJECT (frame), "web_view");
681 
682         webkit_web_view_go_forward (web_view);
683 }
684 
685 static void
window_activate_show_contents(GtkAction * action,DhWindow * window)686 window_activate_show_contents (GtkAction *action,
687                                DhWindow  *window)
688 {
689         DhWindowPriv *priv;
690 
691         priv = window->priv;
692 
693         gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->control_notebook), 0);
694         gtk_widget_grab_focus (priv->book_tree);
695 }
696 
697 static void
window_activate_show_search(GtkAction * action,DhWindow * window)698 window_activate_show_search (GtkAction *action,
699                              DhWindow  *window)
700 {
701         DhWindowPriv *priv;
702 
703         priv = window->priv;
704 
705         gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->control_notebook), 1);
706         gtk_widget_grab_focus (priv->search);
707 }
708 
709 static void
window_activate_about(GtkAction * action,DhWindow * window)710 window_activate_about (GtkAction *action,
711                        DhWindow  *window)
712 {
713         const gchar  *authors[] = {
714                 "Mikael Hallendal <micke@imendio.com>",
715                 "Richard Hult <richard@imendio.com>",
716                 "Johan Dahlin <johan@gnome.org>",
717                 "Ross Burton <ross@burtonini.com>",
718                 "Aleksander Morgado <aleksander@lanedo.com>",
719                 NULL
720         };
721         const gchar **documenters = NULL;
722         const gchar  *translator_credits = _("translator_credits");
723 
724         /* i18n: Please don't translate "Devhelp" (it's marked as translatable
725          * for transliteration only) */
726         gtk_show_about_dialog (GTK_WINDOW (window),
727                                "name", _("Devhelp"),
728                                "version", PACKAGE_VERSION,
729                                "comments", _("A developers' help browser for GNOME"),
730                                "authors", authors,
731                                "documenters", documenters,
732                                "translator-credits",
733                                strcmp (translator_credits, "translator_credits") != 0 ?
734                                translator_credits : NULL,
735                                "website", "http://live.gnome.org/devhelp",
736                                "logo-icon-name", "devhelp",
737                                NULL);
738 }
739 
740 static void
window_open_link_cb(DhWindow * window,const char * location,DhOpenLinkFlags flags)741 window_open_link_cb (DhWindow *window,
742                      const char *location,
743                      DhOpenLinkFlags flags)
744 {
745         DhWindowPriv *priv;
746         priv = window->priv;
747 
748         if (flags & DH_OPEN_LINK_NEW_TAB) {
749                 window_open_new_tab (window, location, FALSE);
750         }
751         else if (flags & DH_OPEN_LINK_NEW_WINDOW) {
752                 GtkWidget *new_window;
753                 new_window = dh_base_new_window (priv->base);
754                 gtk_widget_show (new_window);
755         }
756 }
757 
758 static const GtkActionEntry actions[] = {
759         { "FileMenu", NULL, N_("_File"), NULL, NULL, NULL },
760         { "EditMenu", NULL, N_("_Edit"), NULL, NULL, NULL },
761         { "ViewMenu", NULL, N_("_View"), NULL, NULL, NULL },
762         { "GoMenu",   NULL, N_("_Go"), NULL, NULL, NULL },
763         { "HelpMenu", NULL, N_("_Help"), NULL, NULL, NULL },
764 
765         /* File menu */
766         { "NewWindow", GTK_STOCK_NEW, N_("_New Window"), "<control>N", NULL,
767           G_CALLBACK (window_activate_new_window) },
768         { "NewTab", GTK_STOCK_NEW, N_("New _Tab"), "<control>T", NULL,
769           G_CALLBACK (window_activate_new_tab) },
770         { "Print", GTK_STOCK_PRINT, N_("_Print…"), "<control>P", NULL,
771           G_CALLBACK (window_activate_print) },
772         { "Close", GTK_STOCK_CLOSE, NULL, NULL, NULL,
773           G_CALLBACK (window_activate_close) },
774         { "Quit", GTK_STOCK_QUIT, NULL, NULL, NULL,
775           G_CALLBACK (window_activate_quit) },
776 
777         /* Edit menu */
778         { "Copy", GTK_STOCK_COPY, NULL, "<control>C", NULL,
779           G_CALLBACK (window_activate_copy) },
780         { "Find", GTK_STOCK_FIND, NULL, "<control>F", NULL,
781           G_CALLBACK (window_activate_find) },
782         { "Find Next", GTK_STOCK_GO_FORWARD, N_("Find Next"), "<control>G", NULL,
783           G_CALLBACK (window_find_next_cb) },
784         { "Find Previous", GTK_STOCK_GO_BACK, N_("Find Previous"), "<shift><control>G", NULL,
785           G_CALLBACK (window_find_previous_cb) },
786         { "Preferences", GTK_STOCK_PREFERENCES, NULL, NULL, NULL,
787           G_CALLBACK (window_activate_preferences) },
788 
789         /* Go menu */
790         { "Back", GTK_STOCK_GO_BACK, NULL, "<alt>Left",
791           N_("Go to the previous page"),
792           G_CALLBACK (window_activate_back) },
793         { "Forward", GTK_STOCK_GO_FORWARD, NULL, "<alt>Right",
794           N_("Go to the next page"),
795           G_CALLBACK (window_activate_forward) },
796 
797         { "ShowContentsTab", NULL, N_("_Contents Tab"), "<ctrl>B", NULL,
798           G_CALLBACK (window_activate_show_contents) },
799 
800         { "ShowSearchTab", NULL, N_("_Search Tab"), "<ctrl>S", NULL,
801           G_CALLBACK (window_activate_show_search) },
802 
803         /* View menu */
804         { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("_Larger Text"), "<ctrl>plus",
805           N_("Increase the text size"),
806           G_CALLBACK (window_activate_zoom_in) },
807         { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("S_maller Text"), "<ctrl>minus",
808           N_("Decrease the text size"),
809           G_CALLBACK (window_activate_zoom_out) },
810         { "ZoomDefault", GTK_STOCK_ZOOM_100, N_("_Normal Size"), "<ctrl>0",
811           N_("Use the normal text size"),
812           G_CALLBACK (window_activate_zoom_default) },
813 
814         /* About menu */
815         { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL,
816           G_CALLBACK (window_activate_about) },
817 
818         /* Fullscreen toolbar */
819         { "LeaveFullscreen", GTK_STOCK_LEAVE_FULLSCREEN, NULL,
820           NULL, N_("Leave fullscreen mode"),
821           G_CALLBACK (window_leave_fullscreen_mode) }
822 };
823 
824 static const GtkToggleActionEntry always_sensitive_toggle_menu_entries[] =
825 {
826         { "ViewFullscreen", GTK_STOCK_FULLSCREEN, NULL, "F11",
827           N_("Display in full screen"),
828           G_CALLBACK (window_toggle_fullscreen_mode), FALSE },
829 };
830 
831 static const gchar* important_actions[] = {
832         "Back",
833         "Forward"
834 };
835 
836 static void
window_finalize(GObject * object)837 window_finalize (GObject *object)
838 {
839         DhWindowPriv *priv = GET_PRIVATE (object);
840 
841         g_object_unref (priv->base);
842 
843         G_OBJECT_CLASS (dh_window_parent_class)->finalize (object);
844 }
845 
846 static void
dh_window_class_init(DhWindowClass * klass)847 dh_window_class_init (DhWindowClass *klass)
848 {
849         GObjectClass *object_class = G_OBJECT_CLASS (klass);
850 
851         object_class->finalize = window_finalize;
852 
853         signals[OPEN_LINK] =
854                 g_signal_new ("open-link",
855                               G_TYPE_FROM_CLASS (klass),
856                               G_SIGNAL_RUN_LAST,
857                               G_STRUCT_OFFSET (DhWindowClass, open_link),
858                               NULL, NULL,
859                               _dh_marshal_VOID__STRING_FLAGS,
860                               G_TYPE_NONE,
861                               2,
862                               G_TYPE_STRING,
863                               DH_TYPE_OPEN_LINK_FLAGS);
864 
865         gtk_rc_parse_string ("style \"devhelp-tab-close-button-style\"\n"
866                              "{\n"
867                              "GtkWidget::focus-padding = 0\n"
868                              "GtkWidget::focus-line-width = 0\n"
869                              "xthickness = 0\n"
870                              "ythickness = 0\n"
871                              "}\n"
872                              "widget \"*.devhelp-tab-close-button\" "
873                              "style \"devhelp-tab-close-button-style\"");
874 
875         g_type_class_add_private (klass, sizeof (DhWindowPriv));
876 }
877 
878 static void
dh_window_init(DhWindow * window)879 dh_window_init (DhWindow *window)
880 {
881         DhWindowPriv  *priv;
882         GtkAction     *action;
883         GtkAccelGroup *accel_group;
884         GClosure      *closure;
885         guint           i;
886 
887         priv = GET_PRIVATE (window);
888         window->priv = priv;
889 
890         priv->selected_search_link = NULL;
891 
892         priv->manager = gtk_ui_manager_new ();
893 
894         accel_group = gtk_ui_manager_get_accel_group (priv->manager);
895         gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
896 
897         priv->main_box = gtk_vbox_new (FALSE, 0);
898         gtk_widget_show (priv->main_box);
899 
900         priv->menu_box = gtk_vbox_new (FALSE, 0);
901         gtk_widget_show (priv->menu_box);
902         gtk_container_set_border_width (GTK_CONTAINER (priv->menu_box), 0);
903         gtk_box_pack_start (GTK_BOX (priv->main_box), priv->menu_box,
904                             FALSE, TRUE, 0);
905 
906         gtk_container_add (GTK_CONTAINER (window), priv->main_box);
907 
908         g_signal_connect (window,
909                           "open-link",
910                           G_CALLBACK (window_open_link_cb),
911                           window);
912 
913         priv->action_group = gtk_action_group_new ("MainWindow");
914 
915         gtk_action_group_set_translation_domain (priv->action_group,
916                                                  GETTEXT_PACKAGE);
917 
918         gtk_action_group_add_actions (priv->action_group,
919                                       actions,
920                                       G_N_ELEMENTS (actions),
921                                       window);
922         gtk_action_group_add_toggle_actions (priv->action_group,
923                                              always_sensitive_toggle_menu_entries,
924                                              G_N_ELEMENTS (always_sensitive_toggle_menu_entries),
925                                              window);
926 
927         for (i = 0; i < G_N_ELEMENTS (important_actions); i++) {
928                 action = gtk_action_group_get_action (priv->action_group,
929                                                       important_actions[i]);
930                 g_object_set (action, "is-important", TRUE, NULL);
931         }
932 
933         gtk_ui_manager_insert_action_group (priv->manager,
934                                             priv->action_group,
935                                             0);
936 
937         action = gtk_action_group_get_action (priv->action_group,
938                                               "Back");
939         g_object_set (action, "sensitive", FALSE, NULL);
940 
941         action = gtk_action_group_get_action (priv->action_group,
942                                               "Forward");
943         g_object_set (action, "sensitive", FALSE, NULL);
944 
945         action = gtk_action_group_get_action (priv->action_group, "ZoomIn");
946         /* Translators: This refers to text size */
947         g_object_set (action, "short_label", _("Larger"), NULL);
948         action = gtk_action_group_get_action (priv->action_group, "ZoomOut");
949         /* Translators: This refers to text size */
950         g_object_set (action, "short_label", _("Smaller"), NULL);
951 
952         accel_group = gtk_accel_group_new ();
953         gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
954 
955         for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) {
956                 closure =  g_cclosure_new (G_CALLBACK (window_web_view_tab_accel_cb),
957                                            window,
958                                            NULL);
959                 gtk_accel_group_connect (accel_group,
960                                          tab_accel_keys[i],
961                                          GDK_MOD1_MASK,
962                                          0,
963                                          closure);
964         }
965 }
966 
967 /* The ugliest hack. When switching tabs, the selection and cursor is changed
968  * for the tree view so the web_view content is changed. Block the signal during
969  * switch.
970  */
971 static void
window_control_switch_page_cb(GtkWidget * notebook,gpointer page,guint page_num,DhWindow * window)972 window_control_switch_page_cb (GtkWidget       *notebook,
973                                gpointer         page,
974                                guint            page_num,
975                                DhWindow        *window)
976 {
977         DhWindowPriv *priv;
978 
979         priv = window->priv;
980 
981         g_signal_handlers_block_by_func (priv->book_tree,
982                                          window_tree_link_selected_cb,
983                                          window);
984 }
985 
986 static void
window_control_after_switch_page_cb(GtkWidget * notebook,gpointer page,guint page_num,DhWindow * window)987 window_control_after_switch_page_cb (GtkWidget       *notebook,
988                                      gpointer         page,
989                                      guint            page_num,
990                                      DhWindow        *window)
991 {
992         DhWindowPriv *priv;
993 
994         priv = window->priv;
995 
996         g_signal_handlers_unblock_by_func (priv->book_tree,
997                                            window_tree_link_selected_cb,
998                                            window);
999 }
1000 
1001 static void
window_web_view_switch_page_cb(GtkNotebook * notebook,gpointer page,guint new_page_num,DhWindow * window)1002 window_web_view_switch_page_cb (GtkNotebook     *notebook,
1003                                 gpointer         page,
1004                                 guint            new_page_num,
1005                                 DhWindow        *window)
1006 {
1007         DhWindowPriv *priv;
1008         GtkWidget    *new_page;
1009 
1010         priv = window->priv;
1011 
1012         new_page = gtk_notebook_get_nth_page (notebook, new_page_num);
1013         if (new_page) {
1014                 WebKitWebView  *new_web_view;
1015                 WebKitWebFrame *web_frame;
1016                 const gchar    *location;
1017 
1018                 new_web_view = g_object_get_data (G_OBJECT (new_page), "web_view");
1019 
1020                 /* Sync the book tree. */
1021                 web_frame = webkit_web_view_get_main_frame (new_web_view);
1022                 location = webkit_web_frame_get_uri (web_frame);
1023 
1024                 if (location) {
1025                         dh_book_tree_select_uri (DH_BOOK_TREE (priv->book_tree),
1026                                                  location);
1027                 }
1028                 window_check_history (window, new_web_view);
1029 
1030                 window_update_title (window, new_web_view, NULL);
1031         } else {
1032                 /* i18n: Please don't translate "Devhelp" (it's marked as translatable
1033                  * for transliteration only) */
1034                 gtk_window_set_title (GTK_WINDOW (window), _("Devhelp"));
1035                 window_check_history (window, NULL);
1036         }
1037 }
1038 
1039 static void
window_web_view_switch_page_after_cb(GtkNotebook * notebook,gpointer page,guint new_page_num,DhWindow * window)1040 window_web_view_switch_page_after_cb (GtkNotebook     *notebook,
1041                                       gpointer         page,
1042                                       guint            new_page_num,
1043                                       DhWindow        *window)
1044 {
1045         window_update_zoom_actions_sensitiveness (window);
1046 }
1047 
1048 static void
window_populate(DhWindow * window)1049 window_populate (DhWindow *window)
1050 {
1051         DhWindowPriv  *priv;
1052         gchar         *path;
1053         GtkWidget     *book_tree_sw;
1054         DhBookManager *book_manager;
1055         GtkWidget     *menubar;
1056         GtkWidget     *toolbar;
1057 
1058         priv = window->priv;
1059 
1060         path = dh_util_build_data_filename ("devhelp", "ui", "window.ui", NULL);
1061         gtk_ui_manager_add_ui_from_file (priv->manager,
1062                                          path,
1063                                          NULL);
1064         g_free (path);
1065         gtk_ui_manager_ensure_update (priv->manager);
1066 
1067         menubar = gtk_ui_manager_get_widget (priv->manager, "/MenuBar");
1068         gtk_box_pack_start (GTK_BOX (priv->menu_box), menubar,
1069                             FALSE, FALSE, 0);
1070         toolbar = gtk_ui_manager_get_widget (priv->manager, "/Toolbar");
1071         gtk_box_pack_start (GTK_BOX (priv->menu_box), toolbar,
1072                             FALSE, FALSE, 0);
1073 
1074 #ifdef GDK_WINDOWING_QUARTZ
1075         {
1076                 GtkWidget       *widget;
1077                 IgeMacMenuGroup *group;
1078 
1079                 /* Hide toolbar labels. */
1080                 gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS);
1081 
1082                 /* Setup menubar. */
1083                 ige_mac_menu_set_menu_bar (GTK_MENU_SHELL (menubar));
1084                 gtk_widget_hide (menubar);
1085 
1086                 widget = gtk_ui_manager_get_widget (priv->manager, "/MenuBar/FileMenu/Quit");
1087                 ige_mac_menu_set_quit_menu_item (GTK_MENU_ITEM (widget));
1088 
1089                 group =  ige_mac_menu_add_app_menu_group ();
1090                 widget = gtk_ui_manager_get_widget (priv->manager, "/MenuBar/HelpMenu/About");
1091                 ige_mac_menu_add_app_menu_item (group, GTK_MENU_ITEM (widget),
1092                                                 /* i18n: please don't translate
1093                                                  * "Devhelp", it's a name, not a
1094                                                  * generic word. */
1095                                                 _("About Devhelp"));
1096 
1097                 group =  ige_mac_menu_add_app_menu_group ();
1098                 widget = gtk_ui_manager_get_widget (priv->manager, "/MenuBar/EditMenu/Preferences");
1099                 ige_mac_menu_add_app_menu_item (group, GTK_MENU_ITEM (widget),
1100                                                 _("Preferences…"));
1101 
1102                 ige_mac_menu_set_global_key_handler_enabled (TRUE);
1103         }
1104 #endif
1105 
1106         priv->hpaned = gtk_hpaned_new ();
1107 
1108         gtk_box_pack_start (GTK_BOX (priv->main_box), priv->hpaned, TRUE, TRUE, 0);
1109 
1110         /* Search and contents notebook. */
1111         priv->control_notebook = gtk_notebook_new ();
1112 
1113         gtk_paned_add1 (GTK_PANED (priv->hpaned), priv->control_notebook);
1114 
1115         g_signal_connect (priv->control_notebook,
1116                           "switch-page",
1117                           G_CALLBACK (window_control_switch_page_cb),
1118                           window);
1119 
1120         g_signal_connect_after (priv->control_notebook,
1121                                 "switch-page",
1122                                 G_CALLBACK (window_control_after_switch_page_cb),
1123                                 window);
1124 
1125         book_tree_sw = gtk_scrolled_window_new (NULL, NULL);
1126 
1127         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (book_tree_sw),
1128                                         GTK_POLICY_NEVER,
1129                                         GTK_POLICY_AUTOMATIC);
1130         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (book_tree_sw),
1131                                              GTK_SHADOW_IN);
1132         gtk_container_set_border_width (GTK_CONTAINER (book_tree_sw), 2);
1133 
1134         book_manager = dh_base_get_book_manager (priv->base);
1135 
1136         priv->book_tree = dh_book_tree_new (book_manager);
1137         gtk_container_add (GTK_CONTAINER (book_tree_sw),
1138                            priv->book_tree);
1139         dh_util_state_set_notebook_page_name (book_tree_sw, "content");
1140         gtk_notebook_append_page (GTK_NOTEBOOK (priv->control_notebook),
1141                                   book_tree_sw,
1142                                   gtk_label_new (_("Contents")));
1143         g_signal_connect (priv->book_tree,
1144                           "link-selected",
1145                           G_CALLBACK (window_tree_link_selected_cb),
1146                           window);
1147 
1148         priv->search = dh_search_new (book_manager);
1149         dh_util_state_set_notebook_page_name (priv->search, "search");
1150         gtk_notebook_append_page (GTK_NOTEBOOK (priv->control_notebook),
1151                                   priv->search,
1152                                   gtk_label_new (_("Search")));
1153         g_signal_connect (priv->search,
1154                           "link-selected",
1155                           G_CALLBACK (window_search_link_selected_cb),
1156                           window);
1157 
1158         priv->vbox = gtk_vbox_new (FALSE, 0);
1159         gtk_paned_add2 (GTK_PANED (priv->hpaned), priv->vbox);
1160 
1161         /* HTML tabs notebook. */
1162         priv->notebook = gtk_notebook_new ();
1163         gtk_container_set_border_width (GTK_CONTAINER (priv->notebook), 0);
1164         gtk_notebook_set_show_border (GTK_NOTEBOOK (priv->notebook), FALSE);
1165         gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
1166         gtk_box_pack_start (GTK_BOX (priv->vbox), priv->notebook, TRUE, TRUE, 0);
1167 
1168         g_signal_connect (priv->notebook,
1169                           "switch-page",
1170                           G_CALLBACK (window_web_view_switch_page_cb),
1171                           window);
1172         g_signal_connect_after (priv->notebook,
1173                                 "switch-page",
1174                                 G_CALLBACK (window_web_view_switch_page_after_cb),
1175                                 window);
1176 
1177 
1178         /* Create findbar. */
1179         priv->findbar = egg_find_bar_new ();
1180         gtk_widget_set_no_show_all (priv->findbar, TRUE);
1181         gtk_box_pack_start (GTK_BOX (priv->vbox), priv->findbar, FALSE, FALSE, 0);
1182 
1183         g_signal_connect (priv->findbar,
1184                           "notify::search-string",
1185                           G_CALLBACK(window_find_search_changed_cb),
1186                           window);
1187         g_signal_connect (priv->findbar,
1188                           "notify::case-sensitive",
1189                           G_CALLBACK (window_find_case_changed_cb),
1190                           window);
1191         g_signal_connect (priv->findbar,
1192                           "previous",
1193                           G_CALLBACK (window_find_previous_cb),
1194                           window);
1195         g_signal_connect (priv->findbar,
1196                           "next",
1197                           G_CALLBACK (window_find_next_cb),
1198                           window);
1199         g_signal_connect (priv->findbar,
1200                           "close",
1201                           G_CALLBACK (window_findbar_close_cb),
1202                           window);
1203 
1204         gtk_widget_show_all (priv->hpaned);
1205 
1206         window_update_zoom_actions_sensitiveness (window);
1207         window_open_new_tab (window, NULL, TRUE);
1208 }
1209 
1210 
1211 static gchar *
find_library_equivalent(DhWindow * window,const gchar * uri)1212 find_library_equivalent (DhWindow    *window,
1213                          const gchar *uri)
1214 {
1215         DhWindowPriv *priv;
1216         gchar **components;
1217         GList *iter;
1218         DhLink *link;
1219         DhBookManager *book_manager;
1220         gchar *book_id;
1221         gchar *filename;
1222         gchar *local_uri = NULL;
1223         GList *books;
1224 
1225         components = g_strsplit (uri, "/", 0);
1226         book_id = components[4];
1227         filename = components[6];
1228 
1229         priv = window->priv;
1230         book_manager = dh_base_get_book_manager (priv->base);
1231 
1232         /* use list pointer to iterate */
1233         for (books = dh_book_manager_get_books (book_manager);
1234              !local_uri && books;
1235              books = g_list_next (books)) {
1236                 DhBook *book = DH_BOOK (books->data);
1237 
1238                 for (iter = dh_book_get_keywords (book);
1239                      iter;
1240                      iter = g_list_next (iter)) {
1241                         link = iter->data;
1242                         if (g_strcmp0 (dh_link_get_book_id (link), book_id) != 0) {
1243                                 continue;
1244                         }
1245                         if (g_strcmp0 (dh_link_get_file_name (link), filename) != 0) {
1246                                 continue;
1247                         }
1248                         local_uri = dh_link_get_uri (link);
1249                         break;
1250                 }
1251         }
1252 
1253         g_strfreev (components);
1254 
1255         return local_uri;
1256 }
1257 
1258 
1259 static gboolean
window_web_view_navigation_policy_decision_requested(WebKitWebView * web_view,WebKitWebFrame * frame,WebKitNetworkRequest * request,WebKitWebNavigationAction * navigation_action,WebKitWebPolicyDecision * policy_decision,DhWindow * window)1260 window_web_view_navigation_policy_decision_requested (WebKitWebView             *web_view,
1261                                                       WebKitWebFrame            *frame,
1262                                                       WebKitNetworkRequest      *request,
1263                                                       WebKitWebNavigationAction *navigation_action,
1264                                                       WebKitWebPolicyDecision   *policy_decision,
1265                                                       DhWindow                  *window)
1266 {
1267         DhWindowPriv *priv;
1268         const char   *uri;
1269 
1270         priv = window->priv;
1271 
1272         uri = webkit_network_request_get_uri (request);
1273 
1274 #ifdef ERRORS_IN_INFOBAR
1275         /* make sure to hide the info bar on page change */
1276         gtk_widget_hide (window_get_active_info_bar (window));
1277 #endif
1278 
1279         if (webkit_web_navigation_action_get_button (navigation_action) == 2) { /* middle click */
1280                 webkit_web_policy_decision_ignore (policy_decision);
1281                 g_signal_emit (window, signals[OPEN_LINK], 0, uri, DH_OPEN_LINK_NEW_TAB);
1282                 return TRUE;
1283         }
1284 
1285         if (strcmp (uri, "about:blank") == 0) {
1286                 return FALSE;
1287         }
1288 
1289         if (strncmp (uri, "http://library.gnome.org/devel/", 31) == 0) {
1290                 gchar *local_uri = find_library_equivalent (window, uri);
1291                 if (local_uri) {
1292                         webkit_web_policy_decision_ignore (policy_decision);
1293                         _dh_window_display_uri (window, local_uri);
1294                         g_free (local_uri);
1295                         return TRUE;
1296                 }
1297         }
1298 
1299         if (strncmp (uri, "file://", 7) != 0) {
1300                 webkit_web_policy_decision_ignore (policy_decision);
1301                 gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, NULL);
1302                 return TRUE;
1303         }
1304 
1305         if (web_view == window_get_active_web_view (window)) {
1306                 dh_book_tree_select_uri (DH_BOOK_TREE (priv->book_tree), uri);
1307                 window_check_history (window, web_view);
1308         }
1309 
1310         return FALSE;
1311 }
1312 
1313 
1314 static gboolean
window_web_view_load_error_cb(WebKitWebView * web_view,WebKitWebFrame * frame,gchar * uri,gpointer * web_error,DhWindow * window)1315 window_web_view_load_error_cb (WebKitWebView  *web_view,
1316                                WebKitWebFrame *frame,
1317                                gchar          *uri,
1318                                gpointer       *web_error,
1319                                DhWindow       *window)
1320 {
1321 #ifdef ERRORS_IN_INFOBAR
1322         GtkWidget *info_bar;
1323         GtkWidget *content_area;
1324         GtkWidget *message_label;
1325         GList     *children;
1326         gchar     *markup;
1327 
1328         info_bar = window_get_active_info_bar (window);
1329         markup = g_strdup_printf ("<b>%s</b>",
1330                        _("Error opening the requested link."));
1331         message_label = gtk_label_new (markup);
1332         gtk_misc_set_alignment (GTK_MISC (message_label), 0, 0.5);
1333         gtk_label_set_use_markup (GTK_LABEL (message_label), TRUE);
1334         content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
1335         children = gtk_container_get_children (GTK_CONTAINER (content_area));
1336         if (children) {
1337                 gtk_container_remove (GTK_CONTAINER (content_area), children->data);
1338                 g_list_free (children);
1339         }
1340         gtk_container_add (GTK_CONTAINER (content_area), message_label);
1341         gtk_widget_show (message_label);
1342 
1343         gtk_widget_show (info_bar);
1344         g_free (markup);
1345 
1346         return TRUE;
1347 #else
1348 	return FALSE;
1349 #endif
1350 }
1351 
1352 static void
window_tree_link_selected_cb(GObject * ignored,DhLink * link,DhWindow * window)1353 window_tree_link_selected_cb (GObject  *ignored,
1354                               DhLink   *link,
1355                               DhWindow *window)
1356 {
1357         WebKitWebView *view;
1358         gchar         *uri;
1359 
1360         view = window_get_active_web_view (window);
1361 
1362         uri = dh_link_get_uri (link);
1363         webkit_web_view_load_uri (view, uri);
1364         g_free (uri);
1365 
1366         window_check_history (window, view);
1367 }
1368 
1369 static void
window_search_link_selected_cb(GObject * ignored,DhLink * link,DhWindow * window)1370 window_search_link_selected_cb (GObject  *ignored,
1371                                 DhLink   *link,
1372                                 DhWindow *window)
1373 {
1374         DhWindowPriv  *priv;
1375         WebKitWebView *view;
1376         gchar         *uri;
1377 
1378         priv = window->priv;
1379 
1380         priv->selected_search_link = link;
1381 
1382         view = window_get_active_web_view (window);
1383 
1384         uri = dh_link_get_uri (link);
1385         webkit_web_view_load_uri (view, uri);
1386         g_free (uri);
1387 
1388         window_check_history (window, view);
1389 }
1390 
1391 static void
window_check_history(DhWindow * window,WebKitWebView * web_view)1392 window_check_history (DhWindow      *window,
1393                       WebKitWebView *web_view)
1394 {
1395         DhWindowPriv *priv;
1396         GtkAction    *action;
1397 
1398         priv = window->priv;
1399 
1400         action = gtk_action_group_get_action (priv->action_group, "Forward");
1401         g_object_set (action,
1402                       "sensitive", web_view ? webkit_web_view_can_go_forward (web_view) : FALSE,
1403                       NULL);
1404 
1405         action = gtk_action_group_get_action (priv->action_group, "Back");
1406         g_object_set (action,
1407                       "sensitive", web_view ? webkit_web_view_can_go_back (web_view) : FALSE,
1408                       NULL);
1409 }
1410 
1411 static void
window_web_view_title_changed_cb(WebKitWebView * web_view,WebKitWebFrame * web_frame,const gchar * title,DhWindow * window)1412 window_web_view_title_changed_cb (WebKitWebView  *web_view,
1413                                   WebKitWebFrame *web_frame,
1414                                   const gchar    *title,
1415                                   DhWindow       *window)
1416 {
1417         if (web_view == window_get_active_web_view (window)) {
1418                 window_update_title (window, web_view, title);
1419         }
1420 
1421         window_tab_set_title (window, web_view, title);
1422 }
1423 
1424 static gboolean
window_web_view_button_press_event_cb(WebKitWebView * web_view,GdkEventButton * event,DhWindow * window)1425 window_web_view_button_press_event_cb (WebKitWebView  *web_view,
1426                                        GdkEventButton *event,
1427                                        DhWindow       *window)
1428 {
1429         if (event->button == 3) {
1430                 return TRUE;
1431         }
1432 
1433         return FALSE;
1434 }
1435 
1436 static gboolean
do_search(DhWindow * window)1437 do_search (DhWindow *window)
1438 {
1439         DhWindowPriv  *priv = window->priv;
1440         WebKitWebView *web_view;
1441 
1442         priv->find_source_id = 0;
1443 
1444         web_view = window_get_active_web_view (window);
1445 
1446         webkit_web_view_unmark_text_matches (web_view);
1447         webkit_web_view_mark_text_matches (
1448                 web_view,
1449                 egg_find_bar_get_search_string (EGG_FIND_BAR (priv->findbar)),
1450                 egg_find_bar_get_case_sensitive (EGG_FIND_BAR (priv->findbar)), 0);
1451         webkit_web_view_set_highlight_text_matches (web_view, TRUE);
1452 
1453         webkit_web_view_search_text (
1454                 web_view, egg_find_bar_get_search_string (EGG_FIND_BAR (priv->findbar)),
1455                 egg_find_bar_get_case_sensitive (EGG_FIND_BAR (priv->findbar)),
1456                 TRUE, TRUE);
1457 
1458 	return FALSE;
1459 }
1460 
1461 static void
window_find_search_changed_cb(GObject * object,GParamSpec * pspec,DhWindow * window)1462 window_find_search_changed_cb (GObject    *object,
1463                                GParamSpec *pspec,
1464                                DhWindow   *window)
1465 {
1466         DhWindowPriv *priv = window->priv;
1467 
1468         if (priv->find_source_id != 0) {
1469                 g_source_remove (priv->find_source_id);
1470                 priv->find_source_id = 0;
1471         }
1472 
1473         priv->find_source_id = g_timeout_add (300, (GSourceFunc)do_search, window);
1474 }
1475 
1476 static void
window_find_case_changed_cb(GObject * object,GParamSpec * pspec,DhWindow * window)1477 window_find_case_changed_cb (GObject    *object,
1478                              GParamSpec *pspec,
1479                              DhWindow   *window)
1480 {
1481         DhWindowPriv  *priv = window->priv;;
1482         WebKitWebView *view;
1483         const gchar   *string;
1484         gboolean       case_sensitive;
1485 
1486         view = window_get_active_web_view (window);
1487 
1488         string = egg_find_bar_get_search_string (EGG_FIND_BAR (priv->findbar));
1489         case_sensitive = egg_find_bar_get_case_sensitive (EGG_FIND_BAR (priv->findbar));
1490 
1491         webkit_web_view_unmark_text_matches (view);
1492         webkit_web_view_mark_text_matches (view, string, case_sensitive, 0);
1493         webkit_web_view_set_highlight_text_matches (view, TRUE);
1494 }
1495 
1496 static void
window_find_next_cb(GtkEntry * entry,DhWindow * window)1497 window_find_next_cb (GtkEntry *entry,
1498                      DhWindow *window)
1499 {
1500         DhWindowPriv  *priv = window->priv;
1501         WebKitWebView *view;
1502         const gchar   *string;
1503         gboolean       case_sensitive;
1504 
1505         view = window_get_active_web_view (window);
1506 
1507         gtk_widget_show (priv->findbar);
1508 
1509         string = egg_find_bar_get_search_string (EGG_FIND_BAR (priv->findbar));
1510         case_sensitive = egg_find_bar_get_case_sensitive (EGG_FIND_BAR (priv->findbar));
1511 
1512         webkit_web_view_search_text (view, string, case_sensitive, TRUE, TRUE);
1513 }
1514 
1515 static void
window_find_previous_cb(GtkEntry * entry,DhWindow * window)1516 window_find_previous_cb (GtkEntry *entry,
1517                          DhWindow *window)
1518 {
1519         DhWindowPriv  *priv = window->priv;
1520         WebKitWebView *view;
1521         const gchar   *string;
1522         gboolean       case_sensitive;
1523 
1524         view = window_get_active_web_view (window);
1525 
1526         gtk_widget_show (priv->findbar);
1527 
1528         string = egg_find_bar_get_search_string (EGG_FIND_BAR (priv->findbar));
1529         case_sensitive = egg_find_bar_get_case_sensitive (EGG_FIND_BAR (priv->findbar));
1530 
1531         webkit_web_view_search_text (view, string, case_sensitive, FALSE, TRUE);
1532 }
1533 
1534 static void
window_findbar_close_cb(GtkWidget * widget,DhWindow * window)1535 window_findbar_close_cb (GtkWidget *widget,
1536                          DhWindow  *window)
1537 {
1538         DhWindowPriv  *priv = window->priv;
1539         WebKitWebView *view;
1540 
1541         view = window_get_active_web_view (window);
1542 
1543         gtk_widget_hide (priv->findbar);
1544 
1545         webkit_web_view_set_highlight_text_matches (view, FALSE);
1546 }
1547 
1548 #if 0
1549 static void
1550 window_web_view_open_new_tab_cb (WebKitWebView *web_view,
1551                                  const gchar   *location,
1552                                  DhWindow      *window)
1553 {
1554         window_open_new_tab (window, location);
1555 }
1556 #endif
1557 
1558 static void
window_web_view_tab_accel_cb(GtkAccelGroup * accel_group,GObject * object,guint key,GdkModifierType mod,DhWindow * window)1559 window_web_view_tab_accel_cb (GtkAccelGroup   *accel_group,
1560                               GObject         *object,
1561                               guint            key,
1562                               GdkModifierType  mod,
1563                               DhWindow        *window)
1564 {
1565         DhWindowPriv *priv;
1566         gint          i, num;
1567 
1568         priv = window->priv;
1569 
1570         num = -1;
1571         for (i = 0; i < (gint) G_N_ELEMENTS (tab_accel_keys); i++) {
1572                 if (tab_accel_keys[i] == key) {
1573                         num = i;
1574                         break;
1575                 }
1576         }
1577 
1578         if (num != -1) {
1579                 gtk_notebook_set_current_page (
1580                         GTK_NOTEBOOK (priv->notebook), num);
1581         }
1582 }
1583 
1584 static int
window_open_new_tab(DhWindow * window,const gchar * location,gboolean switch_focus)1585 window_open_new_tab (DhWindow    *window,
1586                      const gchar *location,
1587                      gboolean     switch_focus)
1588 {
1589         DhWindowPriv *priv;
1590         GtkWidget    *view;
1591         GtkWidget    *vbox;
1592         GtkWidget    *scrolled_window;
1593         GtkWidget    *label;
1594         gint          num;
1595 #ifdef ERRORS_IN_INFOBAR
1596         GtkWidget    *info_bar;
1597 #endif
1598 
1599         priv = window->priv;
1600 
1601         /* Prepare the web view */
1602         view = webkit_web_view_new ();
1603         gtk_widget_show (view);
1604         dh_util_font_add_web_view (WEBKIT_WEB_VIEW (view));
1605 
1606 #ifdef ERRORS_IN_INFOBAR
1607         /* Prepare the info bar */
1608         info_bar = gtk_info_bar_new ();
1609         gtk_widget_set_no_show_all (info_bar, TRUE);
1610         gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
1611                                  GTK_STOCK_CLOSE, GTK_RESPONSE_OK);
1612         gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar),
1613                                        GTK_MESSAGE_ERROR);
1614         g_signal_connect (info_bar, "response",
1615                           G_CALLBACK (gtk_widget_hide), NULL);
1616 #endif
1617 
1618 #if 0
1619         /* Leave this in for now to make it easier to experiment. */
1620         {
1621                 WebKitWebSettings *settings;
1622                 settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view));
1623 
1624                 g_object_set (settings,
1625                               "user-stylesheet-uri", "file://" DATADIR "/devhelp/devhelp.css",
1626                               NULL);
1627         }
1628 #endif
1629 
1630         vbox = gtk_vbox_new (0, FALSE);
1631         gtk_widget_show (vbox);
1632 
1633         /* XXX: Really it would be much better to use real structures */
1634         g_object_set_data (G_OBJECT (vbox), "web_view", view);
1635 #ifdef ERRORS_IN_INFOBAR
1636         g_object_set_data (G_OBJECT (vbox), "info_bar", info_bar);
1637 
1638         gtk_box_pack_start (GTK_BOX(vbox), info_bar, FALSE, TRUE, 0);
1639 #endif
1640 
1641         scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1642         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
1643                                         GTK_POLICY_AUTOMATIC,
1644                                         GTK_POLICY_AUTOMATIC);
1645         gtk_container_add (GTK_CONTAINER (scrolled_window), view);
1646         gtk_widget_show (scrolled_window);
1647         gtk_box_pack_start (GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0);
1648 
1649         label = window_new_tab_label (window, _("Empty Page"), vbox);
1650         gtk_widget_show_all (label);
1651 
1652         g_signal_connect (view, "title-changed",
1653                           G_CALLBACK (window_web_view_title_changed_cb),
1654                           window);
1655         g_signal_connect (view, "button-press-event",
1656                           G_CALLBACK (window_web_view_button_press_event_cb),
1657                           window);
1658         g_signal_connect (view, "navigation-policy-decision-requested",
1659                           G_CALLBACK (window_web_view_navigation_policy_decision_requested),
1660                           window);
1661         g_signal_connect (view, "load-error",
1662                           G_CALLBACK (window_web_view_load_error_cb),
1663                           window);
1664 
1665         num = gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook),
1666                                         vbox, NULL);
1667 
1668         gtk_notebook_set_tab_label (GTK_NOTEBOOK (priv->notebook),
1669                                     vbox, label);
1670 
1671         if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook)) > 1) {
1672                 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->notebook), TRUE);
1673         } else {
1674                 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->notebook), FALSE);
1675         }
1676 
1677         if (location) {
1678                 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (view), location);
1679         } else {
1680                 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (view), "about:blank");
1681         }
1682 
1683         if (switch_focus) {
1684                 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), num);
1685         }
1686 
1687         return num;
1688 }
1689 
1690 #ifndef GDK_WINDOWING_QUARTZ
1691 static void
close_button_clicked_cb(GtkButton * button,DhWindow * window)1692 close_button_clicked_cb (GtkButton *button,
1693                          DhWindow  *window)
1694 {
1695         GtkWidget *parent_tab;
1696         gint       pages;
1697         gint       i;
1698 
1699         parent_tab = g_object_get_data (G_OBJECT (button), "parent_tab");
1700         pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->priv->notebook));
1701         for (i=0; i<pages; i++) {
1702                 if (gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->priv->notebook), i) == parent_tab) {
1703                         window_close_tab (window, i);
1704                         break;
1705                 }
1706         }
1707 }
1708 
1709 static void
tab_label_style_set_cb(GtkWidget * hbox,GtkStyle * previous_style,gpointer user_data)1710 tab_label_style_set_cb (GtkWidget *hbox,
1711                         GtkStyle  *previous_style,
1712                         gpointer   user_data)
1713 {
1714         PangoFontMetrics *metrics;
1715         PangoContext     *context;
1716         GtkWidget        *button;
1717         GtkStyle         *style;
1718         gint              char_width;
1719         gint              h, w;
1720 
1721         context = gtk_widget_get_pango_context (hbox);
1722         style = gtk_widget_get_style (hbox);
1723         metrics = pango_context_get_metrics (context,
1724                                              style->font_desc,
1725                                              pango_context_get_language (context));
1726 
1727         char_width = pango_font_metrics_get_approximate_digit_width (metrics);
1728         pango_font_metrics_unref (metrics);
1729 
1730         gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (hbox),
1731                                            GTK_ICON_SIZE_MENU, &w, &h);
1732 
1733         gtk_widget_set_size_request (hbox, 15 * PANGO_PIXELS (char_width) + 2 * w, -1);
1734 
1735         button = g_object_get_data (G_OBJECT (hbox), "close-button");
1736         gtk_widget_set_size_request (button, w + 2, h + 2);
1737 }
1738 #endif
1739 
1740 /* Don't create a close button on quartz, it looks very much out of
1741  * place.
1742  */
1743 static GtkWidget*
window_new_tab_label(DhWindow * window,const gchar * str,const GtkWidget * parent)1744 window_new_tab_label (DhWindow        *window,
1745                       const gchar     *str,
1746                       const GtkWidget *parent)
1747 {
1748         GtkWidget *label;
1749 #ifndef GDK_WINDOWING_QUARTZ
1750         GtkWidget *hbox;
1751         GtkWidget *close_button;
1752         GtkWidget *image;
1753 
1754         hbox = gtk_hbox_new (FALSE, 4);
1755 
1756         label = gtk_label_new (str);
1757         gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
1758         gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1759         gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
1760 
1761         close_button = gtk_button_new ();
1762         gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
1763         gtk_button_set_focus_on_click (GTK_BUTTON (close_button), FALSE);
1764         gtk_widget_set_name (close_button, "devhelp-tab-close-button");
1765         g_object_set_data (G_OBJECT (close_button), "parent_tab", (gpointer) parent);
1766 
1767         image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
1768         g_signal_connect (close_button, "clicked",
1769                           G_CALLBACK (close_button_clicked_cb),
1770                           window);
1771         gtk_container_add (GTK_CONTAINER (close_button), image);
1772 
1773         gtk_box_pack_start (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
1774 
1775         /* Set minimal size */
1776         g_signal_connect (hbox, "style-set",
1777                           G_CALLBACK (tab_label_style_set_cb),
1778                           NULL);
1779 
1780         g_object_set_data (G_OBJECT (hbox), "label", label);
1781         g_object_set_data (G_OBJECT (hbox), "close-button", close_button);
1782 
1783         return hbox;
1784 #else
1785         label = gtk_label_new (str);
1786         g_object_set_data (G_OBJECT (label), "label", label);
1787 
1788         return label;
1789 #endif
1790 }
1791 
1792 static WebKitWebView *
window_get_active_web_view(DhWindow * window)1793 window_get_active_web_view (DhWindow *window)
1794 {
1795         DhWindowPriv *priv;
1796         gint          page_num;
1797         GtkWidget    *page;
1798 
1799         priv = window->priv;
1800 
1801         page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1802         if (page_num == -1) {
1803                 return NULL;
1804         }
1805 
1806         page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), page_num);
1807 
1808         return g_object_get_data (G_OBJECT (page), "web_view");
1809 }
1810 
1811 #ifdef ERRORS_IN_INFOBAR
1812 static GtkWidget *
window_get_active_info_bar(DhWindow * window)1813 window_get_active_info_bar (DhWindow *window)
1814 {
1815         DhWindowPriv *priv;
1816         gint          page_num;
1817         GtkWidget    *page;
1818 
1819         priv = window->priv;
1820 
1821         page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
1822         if (page_num == -1) {
1823                 return NULL;
1824         }
1825 
1826         page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), page_num);
1827 
1828         return g_object_get_data (G_OBJECT (page), "info_bar");
1829 }
1830 #endif
1831 
1832 static void
window_update_title(DhWindow * window,WebKitWebView * web_view,const gchar * web_view_title)1833 window_update_title (DhWindow      *window,
1834                      WebKitWebView *web_view,
1835                      const gchar   *web_view_title)
1836 {
1837         DhWindowPriv *priv;
1838         const gchar  *book_title;
1839 
1840         priv = window->priv;
1841 
1842         if (!web_view_title) {
1843                 WebKitWebFrame *web_frame;
1844 
1845                 web_frame = webkit_web_view_get_main_frame (web_view);
1846                 web_view_title = webkit_web_frame_get_title (web_frame);
1847         }
1848 
1849         if (web_view_title && *web_view_title == '\0') {
1850                 web_view_title = NULL;
1851         }
1852 
1853         book_title = dh_book_tree_get_selected_book_title (DH_BOOK_TREE (priv->book_tree));
1854 
1855         /* Don't use both titles if they are the same. */
1856         if (book_title && web_view_title && strcmp (book_title, web_view_title) == 0) {
1857                 web_view_title = NULL;
1858         }
1859 
1860         if (!book_title) {
1861                 /* i18n: Please don't translate "Devhelp" (it's marked as translatable
1862                  * for transliteration only) */
1863                 book_title = _("Devhelp");
1864         }
1865 
1866         if (web_view_title) {
1867                 gchar *full_title;
1868                 full_title = g_strdup_printf ("%s - %s", book_title, web_view_title);
1869                 gtk_window_set_title (GTK_WINDOW (window), full_title);
1870                 g_free (full_title);
1871         } else {
1872                 gtk_window_set_title (GTK_WINDOW (window), book_title);
1873         }
1874 }
1875 
1876 static void
window_tab_set_title(DhWindow * window,WebKitWebView * web_view,const gchar * title)1877 window_tab_set_title (DhWindow      *window,
1878                       WebKitWebView *web_view,
1879                       const gchar   *title)
1880 {
1881         DhWindowPriv *priv;
1882         gint          num_pages, i;
1883         GtkWidget    *page;
1884         GtkWidget    *hbox;
1885         GtkWidget    *label;
1886         GtkWidget    *page_web_view;
1887 
1888         priv = window->priv;
1889 
1890         if (!title || title[0] == '\0') {
1891                 title = _("Empty Page");
1892         }
1893 
1894         num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1895         for (i = 0; i < num_pages; i++) {
1896                 page = gtk_notebook_get_nth_page (
1897                         GTK_NOTEBOOK (priv->notebook), i);
1898                 page_web_view = g_object_get_data (G_OBJECT (page), "web_view");
1899 
1900                 /* The web_view widget is inside a frame. */
1901                 if (page_web_view == GTK_WIDGET (web_view)) {
1902                         hbox = gtk_notebook_get_tab_label (
1903                                 GTK_NOTEBOOK (priv->notebook), page);
1904 
1905                         if (hbox) {
1906                                 label = g_object_get_data (G_OBJECT (hbox), "label");
1907                                 gtk_label_set_text (GTK_LABEL (label), title);
1908                         }
1909                         break;
1910                 }
1911         }
1912 }
1913 
1914 GtkWidget *
dh_window_new(DhBase * base)1915 dh_window_new (DhBase *base)
1916 {
1917         DhWindow     *window;
1918         DhWindowPriv *priv;
1919 
1920         window = g_object_new (DH_TYPE_WINDOW, NULL);
1921         priv = window->priv;
1922 
1923         priv->base = g_object_ref (base);
1924 
1925         window_populate (window);
1926 
1927         gtk_window_set_icon_name (GTK_WINDOW (window), "devhelp");
1928 
1929         dh_util_state_manage_window (GTK_WINDOW (window), "main/window");
1930         dh_util_state_manage_paned (GTK_PANED (priv->hpaned), "main/paned");
1931         dh_util_state_manage_notebook (GTK_NOTEBOOK (priv->control_notebook),
1932                                        "main/search_notebook",
1933                                        "content");
1934 
1935         return GTK_WIDGET (window);
1936 }
1937 
1938 void
dh_window_search(DhWindow * window,const gchar * str,const gchar * book_id)1939 dh_window_search (DhWindow    *window,
1940                   const gchar *str,
1941                   const gchar *book_id)
1942 {
1943         DhWindowPriv *priv;
1944 
1945         g_return_if_fail (DH_IS_WINDOW (window));
1946 
1947         priv = window->priv;
1948 
1949         gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->control_notebook), 1);
1950         dh_search_set_search_string (DH_SEARCH (priv->search), str, book_id);
1951 }
1952 
1953 void
dh_window_focus_search(DhWindow * window)1954 dh_window_focus_search (DhWindow *window)
1955 {
1956         DhWindowPriv *priv;
1957 
1958         g_return_if_fail (DH_IS_WINDOW (window));
1959 
1960         priv = window->priv;
1961 
1962         gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->control_notebook), 1);
1963         gtk_widget_grab_focus (priv->search);
1964 }
1965 
1966 /* Only call this with a URI that is known to be in the docs. */
1967 void
_dh_window_display_uri(DhWindow * window,const gchar * uri)1968 _dh_window_display_uri (DhWindow    *window,
1969                         const gchar *uri)
1970 {
1971         DhWindowPriv  *priv;
1972         WebKitWebView *web_view;
1973 
1974         g_return_if_fail (DH_IS_WINDOW (window));
1975         g_return_if_fail (uri != NULL);
1976 
1977         priv = window->priv;
1978 
1979         web_view = window_get_active_web_view (window);
1980         webkit_web_view_load_uri (web_view, uri);
1981         dh_book_tree_select_uri (DH_BOOK_TREE (priv->book_tree), uri);
1982 }
1983