Merge lp:~noskcaj/ubuntu/trusty/yelp/3.10.2 into lp:ubuntu/trusty/yelp

Proposed by Jackson Doak
Status: Merged
Merge reported by: Sebastien Bacher
Merged at revision: not available
Proposed branch: lp:~noskcaj/ubuntu/trusty/yelp/3.10.2
Merge into: lp:ubuntu/trusty/yelp
Diff against target: 7240 lines (+153/-6459)
21 files modified
.pc/07_disable_package_search.patch/libyelp/yelp-view.c (+4/-4)
.pc/applied-patches (+0/-1)
.pc/git_signal_handler.patch/libyelp/yelp-location-entry.c (+0/-1650)
.pc/git_signal_handler.patch/libyelp/yelp-view.c (+0/-2220)
.pc/git_signal_handler.patch/src/yelp-window.c (+0/-1402)
NEWS (+7/-0)
configure (+10/-10)
configure.ac (+1/-1)
debian/changelog (+7/-0)
debian/patches/git_signal_handler.patch (+0/-93)
debian/patches/series (+0/-1)
docs/libyelp/html/YelpLocationEntry.html (+2/-2)
docs/libyelp/html/YelpView.html (+2/-2)
docs/libyelp/html/ch01.html (+3/-3)
docs/libyelp/html/ch01s02.html (+1/-1)
docs/libyelp/html/index.html (+2/-2)
docs/libyelp/html/libyelp.devhelp (+1/-1)
docs/libyelp/html/libyelp.devhelp2 (+1/-1)
docs/libyelp/version.xml (+1/-1)
po/kn.po (+45/-999)
po/zh_CN.po (+66/-65)
To merge this branch: bzr merge lp:~noskcaj/ubuntu/trusty/yelp/3.10.2
Reviewer Review Type Date Requested Status
Sebastien Bacher Approve
Review via email: mp+210061@code.launchpad.net

Description of the change

New upstream bugfix release

To post a comment you must log in.
139. By Jackson Doak

target trusty

Revision history for this message
Sebastien Bacher (seb128) wrote :

Thanks, but please be careful with packaging Vcs (those are indicated in debian/control or by apt-get source), the location for this one is ~ubuntu-desktop/yelp/ubuntu (debian dir only). I'm merging your changes by hand for this time but it would be nice if you could target the right vcs next time

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '.pc/07_disable_package_search.patch/libyelp/yelp-view.c'
--- .pc/07_disable_package_search.patch/libyelp/yelp-view.c 2013-12-17 10:40:39 +0000
+++ .pc/07_disable_package_search.patch/libyelp/yelp-view.c 2014-03-08 22:22:39 +0000
@@ -308,12 +308,12 @@
308 view_clear_load (YELP_VIEW (object));308 view_clear_load (YELP_VIEW (object));
309309
310 if (priv->vadjuster > 0) {310 if (priv->vadjuster > 0) {
311 g_source_remove (priv->vadjuster);311 g_signal_handler_disconnect (priv->vadjustment, priv->vadjuster);
312 priv->vadjuster = 0;312 priv->vadjuster = 0;
313 }313 }
314314
315 if (priv->hadjuster > 0) {315 if (priv->hadjuster > 0) {
316 g_source_remove (priv->hadjuster);316 g_signal_handler_disconnect (priv->hadjustment, priv->hadjuster);
317 priv->hadjuster = 0;317 priv->hadjuster = 0;
318 }318 }
319319
@@ -885,7 +885,7 @@
885 YelpViewPrivate *priv = GET_PRIV (view);885 YelpViewPrivate *priv = GET_PRIV (view);
886 priv->hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (view));886 priv->hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (view));
887 if (priv->hadjuster > 0)887 if (priv->hadjuster > 0)
888 g_source_remove (priv->hadjuster);888 g_signal_handler_disconnect (priv->hadjustment, priv->hadjuster);
889 priv->hadjuster = 0;889 priv->hadjuster = 0;
890 if (priv->hadjustment)890 if (priv->hadjustment)
891 priv->hadjuster = g_signal_connect (priv->hadjustment, "value-changed",891 priv->hadjuster = g_signal_connect (priv->hadjustment, "value-changed",
@@ -900,7 +900,7 @@
900 YelpViewPrivate *priv = GET_PRIV (view);900 YelpViewPrivate *priv = GET_PRIV (view);
901 priv->vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (view));901 priv->vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (view));
902 if (priv->vadjuster > 0)902 if (priv->vadjuster > 0)
903 g_source_remove (priv->vadjuster);903 g_signal_handler_disconnect (priv->vadjustment, priv->vadjuster);
904 priv->vadjuster = 0;904 priv->vadjuster = 0;
905 if (priv->vadjustment)905 if (priv->vadjustment)
906 priv->vadjuster = g_signal_connect (priv->vadjustment, "value-changed",906 priv->vadjuster = g_signal_connect (priv->vadjustment, "value-changed",
907907
=== modified file '.pc/applied-patches'
--- .pc/applied-patches 2014-02-27 13:28:05 +0000
+++ .pc/applied-patches 2014-03-08 22:22:39 +0000
@@ -2,4 +2,3 @@
205_menu_tooltip.patch205_menu_tooltip.patch
306_make_ubuntu_docs_default.patch306_make_ubuntu_docs_default.patch
407_disable_package_search.patch407_disable_package_search.patch
5git_signal_handler.patch
65
=== removed directory '.pc/git_signal_handler.patch'
=== removed directory '.pc/git_signal_handler.patch/libyelp'
=== removed file '.pc/git_signal_handler.patch/libyelp/yelp-location-entry.c'
--- .pc/git_signal_handler.patch/libyelp/yelp-location-entry.c 2014-02-27 13:28:05 +0000
+++ .pc/git_signal_handler.patch/libyelp/yelp-location-entry.c 1970-01-01 00:00:00 +0000
@@ -1,1650 +0,0 @@
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * Copyright (C) 2009 Shaun McCance <shaunm@gnome.org>
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 * Author: Shaun McCance <shaunm@gnome.org>
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <gdk/gdkkeysyms.h>
28#include <gtk/gtk.h>
29#include <glib/gi18n.h>
30
31#include "yelp-location-entry.h"
32#include "yelp-marshal.h"
33#include "yelp-settings.h"
34
35/**
36 * SECTION:yelp-location-entry
37 * @short_description: A location entry with history and search
38 * @include: yelp.h
39 *
40 * #YelpLocationEntry is a #GtkComboBox designed to show the current location,
41 * provide a drop-down menu of previous locations, and allow the user to perform
42 * searches.
43 *
44 * The #GtkTreeModel used by a #YelpLocationEntry is expected to have at least
45 * four columns: #GtkComboBox::entry-text-column contains the displayed name
46 * of the location, #YelpLocationEntry::desc-column contains a description
47 * for each entry, #YelpLocationEntry::icon-column contains an icon name for
48 * the location, and #YelpLocationEntry::flags-column contains a bit field
49 * of #YelpLocationEntryFlags. These columns are specified when creating a
50 * #YelpLocationEntry widget with yelp_location_entry_new_with_model().
51 *
52 * Usually, a single row in the #GtkTreeModel corresponds to a location. When
53 * a user selects a row from the drop-down menu, the icon and text for that
54 * row will be placed in the embedded text entry, and the
55 * #YelpLocationEntry:location-selected signal will be emitted.
56 *
57 * If a row has the %YELP_LOCATION_ENTRY_IS_SEARCH flag set, selecting that
58 * row will not emit the #YelpLocationEntry::location-selected signal. Instead,
59 * the #YelpLocationEntry widget will be placed into search mode, as if by a
60 * call to yelp_location_entry_start_search().
61 *
62 * When a row has the %YELP_LOCATION_ENTRY_CAN_BOOKMARK flag set, an icon
63 * will be displayed in the secondary icon position of the embedded text
64 * entry allowing the user to bookmark the location. Clicking this icon
65 * will cause the FIXME signal to be emitted.
66 **/
67
68static void location_entry_constructed (GObject *object);
69static void location_entry_dispose (GObject *object);
70static void location_entry_finalize (GObject *object);
71static void location_entry_get_property (GObject *object,
72 guint prop_id,
73 GValue *value,
74 GParamSpec *pspec);
75static void location_entry_set_property (GObject *object,
76 guint prop_id,
77 const GValue *value,
78 GParamSpec *pspec);
79
80/* Signals */
81static void location_entry_location_selected (YelpLocationEntry *entry);
82static void location_entry_search_activated (YelpLocationEntry *entry);
83static void location_entry_bookmark_clicked (YelpLocationEntry *entry);
84
85/* Utilities */
86static void location_entry_start_search (YelpLocationEntry *entry,
87 gboolean clear);
88static void location_entry_cancel_search (YelpLocationEntry *entry);
89static void location_entry_set_entry (YelpLocationEntry *entry,
90 gboolean emit);
91static gboolean location_entry_start_pulse (gpointer data);
92static gboolean location_entry_pulse (gpointer data);
93static void location_entry_set_completion (YelpLocationEntry *entry,
94 GtkTreeModel *model);
95
96
97/* GtkTreeModel callbacks */
98static void history_row_changed (GtkTreeModel *model,
99 GtkTreePath *path,
100 GtkTreeIter *iter,
101 gpointer user_data);
102/* GtkComboBox callbacks */
103static void combo_box_changed_cb (GtkComboBox *widget,
104 gpointer user_data);
105static gboolean combo_box_row_separator_func (GtkTreeModel *model,
106 GtkTreeIter *iter,
107 gpointer user_data);
108/* GtkEntry callbacks */
109static gboolean entry_focus_in_cb (GtkWidget *widget,
110 GdkEventFocus *event,
111 gpointer user_data);
112static gboolean entry_focus_out_cb (GtkWidget *widget,
113 GdkEventFocus *event,
114 gpointer user_data);
115static void entry_activate_cb (GtkEntry *text_entry,
116 gpointer user_data);
117static void entry_icon_press_cb (GtkEntry *gtkentry,
118 GtkEntryIconPosition icon_pos,
119 GdkEvent *event,
120 YelpLocationEntry *entry);
121static gboolean entry_key_press_cb (GtkWidget *widget,
122 GdkEventKey *event,
123 YelpLocationEntry *entry);
124/* GtkCellLayout callbacks */
125static void cell_set_text_cell (GtkCellLayout *layout,
126 GtkCellRenderer *cell,
127 GtkTreeModel *model,
128 GtkTreeIter *iter,
129 YelpLocationEntry *entry);
130static void cell_set_bookmark_icon (GtkCellLayout *layout,
131 GtkCellRenderer *cell,
132 GtkTreeModel *model,
133 GtkTreeIter *iter,
134 YelpLocationEntry *entry);
135/* GtkEntryCompletion callbacks */
136static void cell_set_completion_bookmark_icon (GtkCellLayout *layout,
137 GtkCellRenderer *cell,
138 GtkTreeModel *model,
139 GtkTreeIter *iter,
140 YelpLocationEntry *entry);
141static void cell_set_completion_text_cell (GtkCellLayout *layout,
142 GtkCellRenderer *cell,
143 GtkTreeModel *model,
144 GtkTreeIter *iter,
145 YelpLocationEntry *entry);
146static gboolean entry_match_func (GtkEntryCompletion *completion,
147 const gchar *key,
148 GtkTreeIter *iter,
149 YelpLocationEntry *entry);
150static gint entry_completion_sort (GtkTreeModel *model,
151 GtkTreeIter *iter1,
152 GtkTreeIter *iter2,
153 gpointer user_data);
154static gboolean entry_match_selected (GtkEntryCompletion *completion,
155 GtkTreeModel *model,
156 GtkTreeIter *iter,
157 YelpLocationEntry *entry);
158/* YelpView callbacks */
159static void view_loaded (YelpView *view,
160 YelpLocationEntry *entry);
161static void view_uri_selected (YelpView *view,
162 GParamSpec *pspec,
163 YelpLocationEntry *entry);
164static void view_page_title (YelpView *view,
165 GParamSpec *pspec,
166 YelpLocationEntry *entry);
167static void view_page_desc (YelpView *view,
168 GParamSpec *pspec,
169 YelpLocationEntry *entry);
170static void view_page_icon (YelpView *view,
171 GParamSpec *pspec,
172 YelpLocationEntry *entry);
173/* YelpBookmarks callbacks */
174static void bookmarks_changed (YelpBookmarks *bookmarks,
175 const gchar *doc_uri,
176 YelpLocationEntry *entry);
177
178
179
180typedef struct _YelpLocationEntryPrivate YelpLocationEntryPrivate;
181struct _YelpLocationEntryPrivate
182{
183 YelpView *view;
184 YelpBookmarks *bookmarks;
185 GtkTreeRowReference *row;
186 gchar *completion_uri;
187
188 /* do not free below */
189 GtkWidget *text_entry;
190 GtkCellRenderer *icon_cell;
191 GtkListStore *history;
192 GtkEntryCompletion *completion;
193
194 gboolean enable_search;
195 gboolean view_uri_selected;
196 gboolean search_mode;
197 guint pulse;
198 gulong bookmarks_changed;
199
200 gboolean icon_is_clear;
201};
202
203enum {
204 LOCATION_ENTRY_IS_LOADING = 1 << 0,
205 LOCATION_ENTRY_IS_SEPARATOR = 1 << 1,
206 LOCATION_ENTRY_IS_SEARCH = 1 << 2
207};
208
209enum {
210 HISTORY_COL_TITLE,
211 HISTORY_COL_DESC,
212 HISTORY_COL_ICON,
213 HISTORY_COL_URI,
214 HISTORY_COL_DOC,
215 HISTORY_COL_PAGE,
216 HISTORY_COL_FLAGS,
217 HISTORY_COL_TERMS
218};
219
220enum {
221 COMPLETION_COL_TITLE,
222 COMPLETION_COL_DESC,
223 COMPLETION_COL_ICON,
224 COMPLETION_COL_PAGE,
225 COMPLETION_COL_FLAGS
226};
227
228enum {
229 COMPLETION_FLAG_ACTIVATE_SEARCH = 1<<0
230};
231
232enum {
233 LOCATION_SELECTED,
234 SEARCH_ACTIVATED,
235 BOOKMARK_CLICKED,
236 LAST_SIGNAL
237};
238
239enum {
240 PROP_0,
241 PROP_VIEW,
242 PROP_BOOKMARKS,
243 PROP_ENABLE_SEARCH
244};
245
246static GHashTable *completions;
247
248static guint location_entry_signals[LAST_SIGNAL] = {0,};
249
250#define MAX_HISTORY 8
251
252G_DEFINE_TYPE (YelpLocationEntry, yelp_location_entry, GTK_TYPE_COMBO_BOX)
253#define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE((object), YELP_TYPE_LOCATION_ENTRY, YelpLocationEntryPrivate))
254
255static void
256yelp_location_entry_class_init (YelpLocationEntryClass *klass)
257{
258 GObjectClass *object_class;
259
260 klass->location_selected = location_entry_location_selected;
261 klass->search_activated = location_entry_search_activated;
262 klass->bookmark_clicked = location_entry_bookmark_clicked;
263
264 object_class = G_OBJECT_CLASS (klass);
265
266 object_class->constructed = location_entry_constructed;
267 object_class->dispose = location_entry_dispose;
268 object_class->finalize = location_entry_finalize;
269 object_class->get_property = location_entry_get_property;
270 object_class->set_property = location_entry_set_property;
271
272 /**
273 * YelpLocationEntry::location-selected
274 * @widget: The #YelpLocationEntry for which the signal was emitted.
275 * @user_data: User data set when the handler was connected.
276 *
277 * This signal will be emitted whenever a user selects a normal row from the
278 * drop-down menu. Note that if a row has the flag %YELP_LOCATION_ENTRY_IS_SEARCH,
279 * clicking the row will cause @widget to enter search mode, and this signal
280 * will not be emitted.
281 **/
282 location_entry_signals[LOCATION_SELECTED] =
283 g_signal_new ("location-selected",
284 G_OBJECT_CLASS_TYPE (klass),
285 G_SIGNAL_RUN_LAST,
286 G_STRUCT_OFFSET (YelpLocationEntryClass, location_selected),
287 NULL, NULL,
288 g_cclosure_marshal_VOID__VOID,
289 G_TYPE_NONE, 0);
290
291 /**
292 * YelpLocationEntry::search-activated
293 * @widget: The #YelpLocationEntry for which the signal was emitted.
294 * @text: The search text.
295 * @user_data: User data set when the handler was connected.
296 *
297 * This signal will be emitted whenever the user activates a search, generally
298 * by pressing <keycap>Enter</keycap> in the embedded text entry while @widget
299 * is in search mode.
300 **/
301 location_entry_signals[SEARCH_ACTIVATED] =
302 g_signal_new ("search-activated",
303 G_OBJECT_CLASS_TYPE (klass),
304 G_SIGNAL_RUN_LAST,
305 G_STRUCT_OFFSET (YelpLocationEntryClass, search_activated),
306 NULL, NULL,
307 g_cclosure_marshal_VOID__STRING,
308 G_TYPE_NONE, 1,
309 G_TYPE_STRING);
310
311 /**
312 * YelpLocationEntry::bookmark-clicked
313 * @widget: The #YelpLocationEntry for which the signal was emitted.
314 * @user_data: User data set when the handler was connected.
315 *
316 * This signal will be emitted whenever a user clicks the bookmark icon
317 * embedded in the location entry.
318 **/
319 location_entry_signals[BOOKMARK_CLICKED] =
320 g_signal_new ("bookmark-clicked",
321 G_OBJECT_CLASS_TYPE (klass),
322 G_SIGNAL_RUN_LAST,
323 G_STRUCT_OFFSET (YelpLocationEntryClass, bookmark_clicked),
324 NULL, NULL,
325 g_cclosure_marshal_VOID__VOID,
326 G_TYPE_NONE, 0);
327
328 /**
329 * YelpLocationEntry:view
330 *
331 * The YelpView instance that this location entry controls.
332 **/
333 g_object_class_install_property (object_class,
334 PROP_VIEW,
335 g_param_spec_object ("view",
336 _("View"),
337 _("A YelpView instance to control"),
338 YELP_TYPE_VIEW,
339 G_PARAM_CONSTRUCT_ONLY |
340 G_PARAM_READWRITE |
341 G_PARAM_STATIC_STRINGS));
342
343 /**
344 * YelpLocationEntry:bookmarks
345 *
346 * An instance of an implementation of YelpBookmarks to provide
347 * bookmark information for this location entry.
348 **/
349 g_object_class_install_property (object_class,
350 PROP_BOOKMARKS,
351 g_param_spec_object ("bookmarks",
352 _("Bookmarks"),
353 _("A YelpBookmarks implementation instance"),
354 YELP_TYPE_BOOKMARKS,
355 G_PARAM_CONSTRUCT_ONLY |
356 G_PARAM_READWRITE |
357 G_PARAM_STATIC_STRINGS));
358
359 /**
360 * YelpLocationEntry:enable-search
361 *
362 * Whether the location entry can act as a search entry. If search is not
363 * enabled, the user will not be able to initiate a search by clicking in
364 * the embedded text entry or selecting a search row in the drop-down menu.
365 **/
366 g_object_class_install_property (object_class,
367 PROP_ENABLE_SEARCH,
368 g_param_spec_boolean ("enable-search",
369 N_("Enable Search"),
370 N_("Whether the location entry can be used as a search field"),
371 TRUE,
372 G_PARAM_CONSTRUCT |
373 G_PARAM_READWRITE |
374 G_PARAM_STATIC_STRINGS));
375
376 g_type_class_add_private ((GObjectClass *) klass,
377 sizeof (YelpLocationEntryPrivate));
378
379 completions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
380}
381
382static void
383yelp_location_entry_init (YelpLocationEntry *entry)
384{
385 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
386
387 priv->search_mode = FALSE;
388 g_object_set (entry, "entry-text-column", HISTORY_COL_TITLE, NULL);
389}
390
391static void
392location_entry_constructed (GObject *object)
393{
394 GtkCellRenderer *bookmark_cell;
395 GList *cells;
396 GtkTreeIter iter;
397 YelpLocationEntryPrivate *priv = GET_PRIV (object);
398
399 /* Set up the text entry child */
400 priv->text_entry = gtk_bin_get_child (GTK_BIN (object));
401 gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
402 GTK_ENTRY_ICON_PRIMARY,
403 "help-browser");
404 gtk_entry_set_icon_activatable (GTK_ENTRY (priv->text_entry),
405 GTK_ENTRY_ICON_PRIMARY,
406 FALSE);
407 gtk_entry_set_icon_activatable (GTK_ENTRY (priv->text_entry),
408 GTK_ENTRY_ICON_SECONDARY,
409 TRUE);
410 gtk_editable_set_editable (GTK_EDITABLE (priv->text_entry),
411 priv->enable_search);
412 if (!priv->enable_search) {
413 priv->search_mode = FALSE;
414 location_entry_set_entry ((YelpLocationEntry *) object, FALSE);
415 }
416
417 /* Set up the history model */
418 priv->history = gtk_list_store_new (8,
419 G_TYPE_STRING, /* title */
420 G_TYPE_STRING, /* desc */
421 G_TYPE_STRING, /* icon */
422 G_TYPE_STRING, /* uri */
423 G_TYPE_STRING, /* doc */
424 G_TYPE_STRING, /* page */
425 G_TYPE_INT, /* flags */
426 G_TYPE_STRING /* search terms */
427 );
428 g_signal_connect (priv->history, "row-changed",
429 G_CALLBACK (history_row_changed),
430 object);
431 g_object_set (object, "model", priv->history, NULL);
432 if (priv->enable_search) {
433 gtk_list_store_append (priv->history, &iter);
434 gtk_list_store_set (priv->history, &iter,
435 HISTORY_COL_FLAGS, LOCATION_ENTRY_IS_SEPARATOR,
436 -1);
437 gtk_list_store_append (priv->history, &iter);
438 gtk_list_store_set (priv->history, &iter,
439 HISTORY_COL_ICON, "edit-find-symbolic",
440 HISTORY_COL_TITLE, _("Search..."),
441 HISTORY_COL_FLAGS, LOCATION_ENTRY_IS_SEARCH,
442 -1);
443 }
444
445 /* Set up the history drop-down */
446 /* Trying to get the text to line up with the text in the GtkEntry.
447 * The text cell is the zeroeth cell right now, since we haven't
448 * yet called reorder. I realize using a guesstimate pixel value
449 * won't be perfect all the time, but 4 looks niceish.
450 */
451 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (object));
452 g_object_set (cells->data, "xpad", 4, NULL);
453 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (object),
454 GTK_CELL_RENDERER (cells->data),
455 (GtkCellLayoutDataFunc) cell_set_text_cell,
456 object, NULL);
457 g_object_set (cells->data, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
458 g_list_free (cells);
459
460 priv->icon_cell = gtk_cell_renderer_pixbuf_new ();
461 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (object), priv->icon_cell, FALSE);
462 g_object_set (priv->icon_cell, "yalign", 0.2, NULL);
463 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (object),
464 priv->icon_cell,
465 "icon-name",
466 HISTORY_COL_ICON,
467 NULL);
468 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (object), priv->icon_cell, 0);
469
470 if (priv->bookmarks) {
471 bookmark_cell = gtk_cell_renderer_pixbuf_new ();
472 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (object), bookmark_cell, FALSE);
473 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (object),
474 bookmark_cell,
475 (GtkCellLayoutDataFunc) cell_set_bookmark_icon,
476 object, NULL);
477 }
478 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (object),
479 (GtkTreeViewRowSeparatorFunc)
480 combo_box_row_separator_func,
481 object, NULL);
482 /* Without this, you get a warning about the popup widget
483 * being NULL the firt time you click the arrow.
484 */
485 gtk_widget_show_all (GTK_WIDGET (object));
486
487 /* Connect signals */
488 g_signal_connect (object, "changed",
489 G_CALLBACK (combo_box_changed_cb), NULL);
490
491 g_signal_connect (priv->text_entry, "focus-in-event",
492 G_CALLBACK (entry_focus_in_cb), object);
493 g_signal_connect (priv->text_entry, "focus-out-event",
494 G_CALLBACK (entry_focus_out_cb), object);
495 g_signal_connect (priv->text_entry, "icon-press",
496 G_CALLBACK (entry_icon_press_cb), object);
497 g_signal_connect (priv->text_entry, "key-press-event",
498 G_CALLBACK (entry_key_press_cb), object);
499 g_signal_connect (priv->text_entry, "activate",
500 G_CALLBACK (entry_activate_cb), object);
501
502 if (priv->bookmarks)
503 priv->bookmarks_changed = g_signal_connect (priv->bookmarks,
504 "bookmarks-changed",
505 G_CALLBACK (bookmarks_changed),
506 object);
507
508 g_signal_connect (priv->view, "loaded", G_CALLBACK (view_loaded), object);
509 g_signal_connect (priv->view, "notify::yelp-uri", G_CALLBACK (view_uri_selected), object);
510 g_signal_connect (priv->view, "notify::page-title", G_CALLBACK (view_page_title), object);
511 g_signal_connect (priv->view, "notify::page-desc", G_CALLBACK (view_page_desc), object);
512 g_signal_connect (priv->view, "notify::page-icon", G_CALLBACK (view_page_icon), object);
513}
514
515static void
516location_entry_dispose (GObject *object)
517{
518 YelpLocationEntryPrivate *priv = GET_PRIV (object);
519
520 if (priv->view) {
521 g_object_unref (priv->view);
522 priv->view = NULL;
523 }
524
525 if (priv->bookmarks) {
526 g_object_unref (priv->bookmarks);
527 priv->bookmarks = NULL;
528 }
529
530 if (priv->bookmarks_changed) {
531 g_source_remove (priv->bookmarks_changed);
532 priv->bookmarks_changed = 0;
533 }
534
535 if (priv->row) {
536 gtk_tree_row_reference_free (priv->row);
537 priv->row = NULL;
538 }
539
540 if (priv->pulse > 0) {
541 g_source_remove (priv->pulse);
542 priv->pulse = 0;
543 }
544
545 G_OBJECT_CLASS (yelp_location_entry_parent_class)->dispose (object);
546}
547
548static void
549location_entry_finalize (GObject *object)
550{
551 YelpLocationEntryPrivate *priv = GET_PRIV (object);
552
553 g_free (priv->completion_uri);
554
555 G_OBJECT_CLASS (yelp_location_entry_parent_class)->finalize (object);
556}
557
558static void
559location_entry_get_property (GObject *object,
560 guint prop_id,
561 GValue *value,
562 GParamSpec *pspec)
563{
564 YelpLocationEntryPrivate *priv = GET_PRIV (object);
565
566 switch (prop_id) {
567 case PROP_VIEW:
568 g_value_set_object (value, priv->view);
569 break;
570 case PROP_BOOKMARKS:
571 g_value_set_object (value, priv->bookmarks);
572 break;
573 case PROP_ENABLE_SEARCH:
574 g_value_set_boolean (value, priv->enable_search);
575 break;
576 default:
577 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
578 break;
579 }
580}
581
582static void
583location_entry_set_property (GObject *object,
584 guint prop_id,
585 const GValue *value,
586 GParamSpec *pspec)
587{
588 YelpLocationEntryPrivate *priv = GET_PRIV (object);
589
590 switch (prop_id) {
591 case PROP_VIEW:
592 priv->view = g_value_dup_object (value);
593 break;
594 case PROP_BOOKMARKS:
595 priv->bookmarks = g_value_dup_object (value);
596 break;
597 case PROP_ENABLE_SEARCH:
598 priv->enable_search = g_value_get_boolean (value);
599 break;
600 default:
601 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
602 break;
603 }
604}
605
606static void
607location_entry_location_selected (YelpLocationEntry *entry)
608{
609 GtkTreeIter iter;
610 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
611
612 if (priv->view_uri_selected)
613 return;
614
615 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (entry), &iter)) {
616 gchar *uri;
617 gtk_tree_model_get (GTK_TREE_MODEL (priv->history), &iter,
618 HISTORY_COL_URI, &uri,
619 -1);
620 yelp_view_load (priv->view, uri);
621 g_free (uri);
622 }
623}
624
625static void
626location_entry_search_activated (YelpLocationEntry *entry)
627{
628 YelpUri *base, *uri;
629 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
630
631 g_object_get (priv->view, "yelp-uri", &base, NULL);
632 if (base == NULL)
633 return;
634 uri = yelp_uri_new_search (base,
635 gtk_entry_get_text (GTK_ENTRY (priv->text_entry)));
636 g_object_unref (base);
637 yelp_view_load_uri (priv->view, uri);
638 gtk_widget_grab_focus (GTK_WIDGET (priv->view));
639}
640
641static void
642location_entry_bookmark_clicked (YelpLocationEntry *entry)
643{
644 YelpUri *uri;
645 gchar *doc_uri, *page_id;
646 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
647
648 g_object_get (priv->view,
649 "yelp-uri", &uri,
650 "page-id", &page_id,
651 NULL);
652 doc_uri = yelp_uri_get_document_uri (uri);
653 if (priv->bookmarks && doc_uri && page_id) {
654 if (!yelp_bookmarks_is_bookmarked (priv->bookmarks, doc_uri, page_id)) {
655 gchar *icon, *title;
656 g_object_get (priv->view,
657 "page-icon", &icon,
658 "page-title", &title,
659 NULL);
660 yelp_bookmarks_add_bookmark (priv->bookmarks, doc_uri, page_id, icon, title);
661 g_free (icon);
662 g_free (title);
663 }
664 else {
665 yelp_bookmarks_remove_bookmark (priv->bookmarks, doc_uri, page_id);
666 }
667 }
668 g_free (doc_uri);
669 g_free (page_id);
670 g_object_unref (uri);
671}
672
673static void
674location_entry_start_search (YelpLocationEntry *entry,
675 gboolean clear)
676{
677 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
678
679 if (!priv->enable_search)
680 return;
681 if (clear && !priv->search_mode) {
682 const gchar *icon = gtk_entry_get_icon_name (GTK_ENTRY (priv->text_entry),
683 GTK_ENTRY_ICON_PRIMARY);
684 if (!g_str_equal (icon, "folder-saved-search"))
685 gtk_entry_set_text (GTK_ENTRY (priv->text_entry), "");
686 }
687 priv->search_mode = TRUE;
688 location_entry_set_entry (entry, FALSE);
689 gtk_widget_grab_focus (priv->text_entry);
690}
691
692static void
693location_entry_cancel_search (YelpLocationEntry *entry)
694{
695 gboolean ret;
696 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
697 GdkEventFocus *event = g_new0 (GdkEventFocus, 1);
698 priv->search_mode = FALSE;
699 location_entry_set_entry (entry, FALSE);
700 event->type = GDK_FOCUS_CHANGE;
701 event->window = gtk_widget_get_window (GTK_WIDGET (entry));
702 event->send_event = FALSE;
703 event->in = FALSE;
704 g_signal_emit_by_name (entry, "focus-out-event", event, &ret);
705 g_free (event);
706 /* Hack: This makes the popup disappear when you hit Esc. */
707 if (priv->completion) {
708 g_object_ref (priv->completion);
709 gtk_entry_set_completion (GTK_ENTRY (priv->text_entry), NULL);
710 gtk_entry_set_completion (GTK_ENTRY (priv->text_entry),
711 priv->completion);
712 g_object_unref (priv->completion);
713 }
714 gtk_editable_select_region (GTK_EDITABLE (priv->text_entry), 0, 0);
715}
716
717static void
718location_entry_set_completion (YelpLocationEntry *entry,
719 GtkTreeModel *model)
720{
721 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
722 GList *cells;
723 GtkCellRenderer *icon_cell, *bookmark_cell;
724
725 priv->completion = gtk_entry_completion_new ();
726 gtk_entry_completion_set_minimum_key_length (priv->completion, 3);
727 gtk_entry_completion_set_model (priv->completion, model);
728 gtk_entry_completion_set_text_column (priv->completion, COMPLETION_COL_TITLE);
729 gtk_entry_completion_set_match_func (priv->completion,
730 (GtkEntryCompletionMatchFunc) entry_match_func,
731 entry, NULL);
732 g_signal_connect (priv->completion, "match-selected",
733 G_CALLBACK (entry_match_selected), entry);
734
735 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->completion));
736 g_object_set (cells->data, "xpad", 4, NULL);
737 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->completion),
738 GTK_CELL_RENDERER (cells->data),
739 (GtkCellLayoutDataFunc) cell_set_completion_text_cell,
740 entry, NULL);
741 g_object_set (cells->data, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
742 g_list_free (cells);
743
744 icon_cell = gtk_cell_renderer_pixbuf_new ();
745 g_object_set (priv->icon_cell, "yalign", 0.2, NULL);
746 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->completion), icon_cell, FALSE);
747 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->completion), icon_cell, 0);
748 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (priv->completion),
749 icon_cell,
750 "icon-name",
751 COMPLETION_COL_ICON,
752 NULL);
753 if (priv->bookmarks) {
754 bookmark_cell = gtk_cell_renderer_pixbuf_new ();
755 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (priv->completion), bookmark_cell, FALSE);
756 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->completion),
757 bookmark_cell,
758 (GtkCellLayoutDataFunc) cell_set_completion_bookmark_icon,
759 entry, NULL);
760 }
761 gtk_entry_set_completion (GTK_ENTRY (priv->text_entry),
762 priv->completion);
763}
764
765static void
766location_entry_set_entry (YelpLocationEntry *entry, gboolean emit)
767{
768 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
769 GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (entry));
770 GtkTreePath *path = NULL;
771 GtkTreeIter iter;
772 gchar *icon_name;
773
774 if (priv->search_mode) {
775 gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
776 GTK_ENTRY_ICON_PRIMARY,
777 "edit-find-symbolic");
778 gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
779 GTK_ENTRY_ICON_SECONDARY,
780 "edit-clear-symbolic");
781 gtk_entry_set_icon_tooltip_text (GTK_ENTRY (priv->text_entry),
782 GTK_ENTRY_ICON_SECONDARY,
783 _("Clear the search text"));
784 priv->icon_is_clear = TRUE;
785 return;
786 }
787 else {
788 priv->icon_is_clear = FALSE;
789 }
790
791
792 if (priv->row)
793 path = gtk_tree_row_reference_get_path (priv->row);
794
795 if (path) {
796 gchar *text, *doc_uri, *page_id;
797 gint flags;
798 gtk_tree_model_get_iter (model, &iter, path);
799 gtk_tree_model_get (model, &iter,
800 HISTORY_COL_TITLE, &text,
801 HISTORY_COL_ICON, &icon_name,
802 HISTORY_COL_FLAGS, &flags,
803 HISTORY_COL_DOC, &doc_uri,
804 HISTORY_COL_PAGE, &page_id,
805 -1);
806 if (flags & LOCATION_ENTRY_IS_LOADING) {
807 /* Would be nice to have a "loading" icon. I was using image-loading,
808 * but it look out of place with symbolic page icons. Plus, using
809 * image-loading-symbolic shows a broken image, because GtkEntry
810 * doesn't correctly use symbolic icons as of GNOME 3.0.
811 */
812 gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
813 GTK_ENTRY_ICON_PRIMARY,
814 "yelp-page-symbolic");
815 if (priv->pulse == 0)
816 priv->pulse = g_timeout_add (500, location_entry_start_pulse, entry);
817 }
818 else {
819 gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
820 GTK_ENTRY_ICON_PRIMARY,
821 icon_name);
822 }
823 if (priv->bookmarks && doc_uri && page_id) {
824 GdkPixbuf *pixbuf;
825 if (!yelp_bookmarks_is_bookmarked (priv->bookmarks, doc_uri, page_id)) {
826 gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
827 GTK_ENTRY_ICON_SECONDARY,
828 "yelp-bookmark-add-symbolic");
829 gtk_entry_set_icon_tooltip_text (GTK_ENTRY (priv->text_entry),
830 GTK_ENTRY_ICON_SECONDARY,
831 _("Bookmark this page"));
832 }
833 else {
834 gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
835 GTK_ENTRY_ICON_SECONDARY,
836 "yelp-bookmark-remove-symbolic");
837 gtk_entry_set_icon_tooltip_text (GTK_ENTRY (priv->text_entry),
838 GTK_ENTRY_ICON_SECONDARY,
839 _("Remove bookmark"));
840 }
841 }
842 else {
843 gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
844 GTK_ENTRY_ICON_SECONDARY,
845 NULL);
846 }
847 g_free (doc_uri);
848 g_free (page_id);
849 gtk_entry_set_text (GTK_ENTRY (priv->text_entry), text);
850 if (emit)
851 g_signal_emit (entry, location_entry_signals[LOCATION_SELECTED], 0);
852 g_free (text);
853 gtk_tree_path_free (path);
854 }
855 else {
856 gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
857 GTK_ENTRY_ICON_PRIMARY,
858 "help-browser");
859 gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
860 GTK_ENTRY_ICON_SECONDARY,
861 NULL);
862 }
863}
864
865static gboolean
866location_entry_start_pulse (gpointer data)
867{
868 YelpLocationEntryPrivate *priv = GET_PRIV (data);
869 priv->pulse = g_timeout_add (80, location_entry_pulse, data);
870 return FALSE;
871}
872
873static gboolean
874location_entry_pulse (gpointer data)
875{
876 YelpLocationEntryPrivate *priv = GET_PRIV (data);
877 GtkTreeModel *model;
878 GtkTreePath *path;
879 GtkTreeIter iter;
880 gint flags;
881
882 model = gtk_tree_row_reference_get_model (priv->row);
883 path = gtk_tree_row_reference_get_path (priv->row);
884 if (path) {
885 gtk_tree_model_get_iter (model, &iter, path);
886 gtk_tree_model_get (model, &iter,
887 HISTORY_COL_FLAGS, &flags,
888 -1);
889 gtk_tree_path_free (path);
890 }
891
892 if (flags & LOCATION_ENTRY_IS_LOADING && !priv->search_mode) {
893 gtk_entry_progress_pulse (GTK_ENTRY (priv->text_entry));
894 }
895 else {
896 gtk_entry_set_progress_fraction (GTK_ENTRY (priv->text_entry), 0.0);
897 priv->pulse = 0;
898 }
899
900 return flags & LOCATION_ENTRY_IS_LOADING;
901}
902
903static void
904combo_box_changed_cb (GtkComboBox *widget,
905 gpointer user_data)
906{
907 YelpLocationEntryPrivate *priv = GET_PRIV (widget);
908 GtkTreeIter iter;
909 GtkTreeModel *model;
910
911 model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
912
913 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter)) {
914 gint flags;
915 gtk_tree_model_get (model, &iter,
916 HISTORY_COL_FLAGS, &flags,
917 -1);
918 if (flags & LOCATION_ENTRY_IS_SEARCH) {
919 location_entry_start_search ((YelpLocationEntry *) widget, TRUE);
920 }
921 else {
922 GtkTreePath *cur, *path;
923 path = gtk_tree_model_get_path (model, &iter);
924 if (priv->row) {
925 cur = gtk_tree_row_reference_get_path (priv->row);
926 if (gtk_tree_path_compare (cur, path) == 0) {
927 gtk_tree_path_free (cur);
928 gtk_tree_path_free (path);
929 return;
930 }
931 gtk_tree_path_free (cur);
932 gtk_tree_row_reference_free (priv->row);
933 }
934 priv->row = gtk_tree_row_reference_new (model, path);
935 gtk_tree_path_free (path);
936 priv->search_mode = FALSE;
937 location_entry_set_entry ((YelpLocationEntry *) widget, TRUE);
938 }
939 }
940}
941
942static gboolean
943combo_box_row_separator_func (GtkTreeModel *model,
944 GtkTreeIter *iter,
945 gpointer user_data)
946{
947 YelpLocationEntryPrivate *priv = GET_PRIV (user_data);
948 gint flags;
949 gtk_tree_model_get (model, iter,
950 HISTORY_COL_FLAGS, &flags,
951 -1);
952 return (flags & LOCATION_ENTRY_IS_SEPARATOR);
953}
954
955static void
956history_row_changed (GtkTreeModel *model,
957 GtkTreePath *path,
958 GtkTreeIter *iter,
959 gpointer user_data)
960{
961 /* FIXME: Should we bother checking path/iter against priv->row?
962 It doesn't really make a difference, since set_entry is pretty much
963 safe to call whenever. Does it make a performance impact?
964 */
965 location_entry_set_entry (YELP_LOCATION_ENTRY (user_data), FALSE);
966}
967
968static gboolean
969entry_focus_in_cb (GtkWidget *widget,
970 GdkEventFocus *event,
971 gpointer user_data)
972{
973 GtkEntry *text_entry = GTK_ENTRY (widget);
974 YelpLocationEntryPrivate *priv = GET_PRIV (user_data);
975
976 if (priv->enable_search && !priv->search_mode)
977 location_entry_start_search ((YelpLocationEntry *) user_data, TRUE);
978
979 return FALSE;
980}
981
982static gboolean
983entry_focus_out_cb (GtkWidget *widget,
984 GdkEventFocus *event,
985 gpointer user_data)
986{
987 YelpLocationEntryPrivate *priv = GET_PRIV (user_data);
988
989 if (gtk_entry_get_text_length (GTK_ENTRY (widget)) == 0) {
990 priv->search_mode = FALSE;
991 location_entry_set_entry ((YelpLocationEntry *) user_data, FALSE);
992 }
993
994 return FALSE;
995}
996
997static void
998entry_activate_cb (GtkEntry *text_entry,
999 gpointer user_data)
1000{
1001 YelpLocationEntryPrivate *priv = GET_PRIV (user_data);
1002 gchar *text = g_strdup (gtk_entry_get_text (text_entry));
1003
1004 if (!priv->enable_search)
1005 return;
1006
1007 if (!priv->search_mode || text == NULL || strlen(text) == 0)
1008 return;
1009
1010 g_signal_emit (user_data, location_entry_signals[SEARCH_ACTIVATED], 0, text);
1011
1012 g_free (text);
1013}
1014
1015static void
1016entry_icon_press_cb (GtkEntry *gtkentry,
1017 GtkEntryIconPosition icon_pos,
1018 GdkEvent *event,
1019 YelpLocationEntry *entry)
1020{
1021 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
1022
1023 if (icon_pos == GTK_ENTRY_ICON_SECONDARY) {
1024 if (priv->icon_is_clear)
1025 location_entry_cancel_search (entry);
1026 else
1027 g_signal_emit (entry, location_entry_signals[BOOKMARK_CLICKED], 0);
1028 }
1029}
1030
1031static gboolean
1032entry_key_press_cb (GtkWidget *widget,
1033 GdkEventKey *event,
1034 YelpLocationEntry *entry)
1035{
1036 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
1037 if (event->keyval == GDK_KEY_Escape) {
1038 location_entry_cancel_search (entry);
1039 return TRUE;
1040 }
1041 else if (!priv->search_mode) {
1042 location_entry_start_search (entry, FALSE);
1043 }
1044
1045 return FALSE;
1046}
1047
1048static void
1049cell_set_text_cell (GtkCellLayout *layout,
1050 GtkCellRenderer *cell,
1051 GtkTreeModel *model,
1052 GtkTreeIter *iter,
1053 YelpLocationEntry *entry)
1054{
1055 gchar *title, *desc, *color, *text;
1056 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
1057
1058 gtk_tree_model_get (model, iter,
1059 HISTORY_COL_TITLE, &title,
1060 HISTORY_COL_DESC, &desc,
1061 -1);
1062 if (desc) {
1063 color = yelp_settings_get_color (yelp_settings_get_default (),
1064 YELP_SETTINGS_COLOR_TEXT_LIGHT);
1065 text = g_markup_printf_escaped ("<span size='larger'>%s</span>\n<span color='%s'>%s</span>",
1066 title, color, desc);
1067 g_free (color);
1068 g_free (desc);
1069 }
1070 else {
1071 text = g_markup_printf_escaped ("<span size='larger'>%s</span>", title);
1072 }
1073
1074 g_object_set (cell, "markup", text, NULL);
1075 g_free (text);
1076 g_free (title);
1077}
1078
1079static void
1080cell_set_bookmark_icon (GtkCellLayout *layout,
1081 GtkCellRenderer *cell,
1082 GtkTreeModel *model,
1083 GtkTreeIter *iter,
1084 YelpLocationEntry *entry)
1085{
1086 gint flags;
1087 gchar *doc_uri, *page_id;
1088 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
1089
1090 if (priv->bookmarks == NULL) {
1091 g_object_set (cell, "icon-name", NULL, NULL);
1092 return;
1093 }
1094
1095 gtk_tree_model_get (model, iter, HISTORY_COL_FLAGS, &flags, -1);
1096 if (flags & (LOCATION_ENTRY_IS_SEPARATOR | LOCATION_ENTRY_IS_SEARCH)) {
1097 g_object_set (cell, "icon-name", NULL, NULL);
1098 return;
1099 }
1100
1101 gtk_tree_model_get (model, iter,
1102 HISTORY_COL_DOC, &doc_uri,
1103 HISTORY_COL_PAGE, &page_id,
1104 -1);
1105 if (doc_uri && page_id &&
1106 yelp_bookmarks_is_bookmarked (priv->bookmarks, doc_uri, page_id))
1107 g_object_set (cell, "icon-name", "yelp-bookmark-remove-symbolic", NULL);
1108 else
1109 g_object_set (cell, "icon-name", NULL, NULL);
1110
1111 g_free (doc_uri);
1112 g_free (page_id);
1113}
1114
1115static void
1116cell_set_completion_bookmark_icon (GtkCellLayout *layout,
1117 GtkCellRenderer *cell,
1118 GtkTreeModel *model,
1119 GtkTreeIter *iter,
1120 YelpLocationEntry *entry)
1121{
1122 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
1123
1124 if (priv->completion_uri) {
1125 gchar *page_id = NULL;
1126 gtk_tree_model_get (model, iter,
1127 COMPLETION_COL_PAGE, &page_id,
1128 -1);
1129
1130 if (page_id && yelp_bookmarks_is_bookmarked (priv->bookmarks,
1131 priv->completion_uri, page_id))
1132 g_object_set (cell, "icon-name", "yelp-bookmark-remove-symbolic", NULL);
1133 else
1134 g_object_set (cell, "icon-name", NULL, NULL);
1135
1136 g_free (page_id);
1137 }
1138}
1139
1140static void
1141cell_set_completion_text_cell (GtkCellLayout *layout,
1142 GtkCellRenderer *cell,
1143 GtkTreeModel *model,
1144 GtkTreeIter *iter,
1145 YelpLocationEntry *entry)
1146{
1147 gchar *title, *desc, *color, *text;
1148 gint flags;
1149 GList *cells, *cur;
1150 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
1151
1152 gtk_tree_model_get (model, iter, COMPLETION_COL_FLAGS, &flags, -1);
1153 if (flags & COMPLETION_FLAG_ACTIVATE_SEARCH) {
1154 title = g_strdup_printf (_("Search for “%s”"),
1155 gtk_entry_get_text (GTK_ENTRY (priv->text_entry)));
1156 text = g_markup_printf_escaped ("<span size='larger' font_weight='bold'>%s</span>", title);
1157 g_object_set (cell, "markup", text, NULL);
1158 g_free (text);
1159 g_free (title);
1160
1161 color = yelp_settings_get_color (yelp_settings_get_default (),
1162 YELP_SETTINGS_COLOR_YELLOW_BASE);
1163 cells = gtk_cell_layout_get_cells (layout);
1164 for (cur = cells; cur; cur = cur->next) {
1165 g_object_set (cur->data,
1166 "cell-background", color,
1167 "cell-background-set", TRUE,
1168 NULL);
1169 }
1170 g_list_free (cells);
1171 g_free (color);
1172 return;
1173 }
1174
1175 gtk_tree_model_get (model, iter,
1176 COMPLETION_COL_TITLE, &title,
1177 COMPLETION_COL_DESC, &desc,
1178 -1);
1179 if (desc) {
1180 color = yelp_settings_get_color (yelp_settings_get_default (),
1181 YELP_SETTINGS_COLOR_TEXT_LIGHT);
1182 text = g_markup_printf_escaped ("<span size='larger'>%s</span>\n<span color='%s'>%s</span>",
1183 title, color, desc);
1184 g_free (color);
1185 g_free (desc);
1186 }
1187 else {
1188 text = g_markup_printf_escaped ("<span size='larger'>%s</span>", title);
1189 }
1190
1191 g_object_set (cell, "markup", text, NULL);
1192 g_free (text);
1193 g_free (title);
1194
1195 cells = gtk_cell_layout_get_cells (layout);
1196 for (cur = cells; cur; cur = cur->next)
1197 g_object_set (cur->data, "cell-background-set", FALSE, NULL);
1198 g_list_free (cells);
1199}
1200
1201static gboolean
1202entry_match_func (GtkEntryCompletion *completion,
1203 const gchar *key,
1204 GtkTreeIter *iter,
1205 YelpLocationEntry *entry)
1206{
1207 gint stri;
1208 gchar *title, *desc, *titlecase = NULL, *desccase = NULL;
1209 gboolean ret = FALSE;
1210 gchar **strs;
1211 gint flags;
1212 GtkTreeModel *model = gtk_entry_completion_get_model (completion);
1213 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
1214 static GRegex *nonword = NULL;
1215
1216 if (nonword == NULL)
1217 nonword = g_regex_new ("\\W", 0, 0, NULL);
1218 if (nonword == NULL)
1219 return FALSE;
1220
1221 gtk_tree_model_get (model, iter, COMPLETION_COL_FLAGS, &flags, -1);
1222 if (flags & COMPLETION_FLAG_ACTIVATE_SEARCH)
1223 return TRUE;
1224
1225 gtk_tree_model_get (model, iter,
1226 COMPLETION_COL_TITLE, &title,
1227 COMPLETION_COL_DESC, &desc,
1228 -1);
1229 if (title) {
1230 titlecase = g_utf8_casefold (title, -1);
1231 g_free (title);
1232 }
1233 if (desc) {
1234 desccase = g_utf8_casefold (desc, -1);
1235 g_free (desc);
1236 }
1237
1238 strs = g_regex_split (nonword, key, 0);
1239 ret = TRUE;
1240 for (stri = 0; strs[stri]; stri++) {
1241 if (!titlecase || !strstr (titlecase, strs[stri])) {
1242 if (!desccase || !strstr (desccase, strs[stri])) {
1243 ret = FALSE;
1244 break;
1245 }
1246 }
1247 }
1248
1249 g_free (titlecase);
1250 g_free (desccase);
1251 g_strfreev (strs);
1252
1253 return ret;
1254}
1255
1256static gint
1257entry_completion_sort (GtkTreeModel *model,
1258 GtkTreeIter *iter1,
1259 GtkTreeIter *iter2,
1260 gpointer user_data)
1261{
1262 gint ret = 0;
1263 gint flags1, flags2;
1264 gchar *key1, *key2;
1265
1266 gtk_tree_model_get (model, iter1, COMPLETION_COL_FLAGS, &flags1, -1);
1267 gtk_tree_model_get (model, iter2, COMPLETION_COL_FLAGS, &flags2, -1);
1268 if (flags1 & COMPLETION_FLAG_ACTIVATE_SEARCH)
1269 return 1;
1270 else if (flags2 & COMPLETION_FLAG_ACTIVATE_SEARCH)
1271 return -1;
1272
1273 gtk_tree_model_get (model, iter1, COMPLETION_COL_ICON, &key1, -1);
1274 gtk_tree_model_get (model, iter2, COMPLETION_COL_ICON, &key2, -1);
1275 ret = yelp_settings_cmp_icons (key1, key2);
1276 g_free (key1);
1277 g_free (key2);
1278
1279 if (ret)
1280 return ret;
1281
1282 gtk_tree_model_get (model, iter1, COMPLETION_COL_TITLE, &key1, -1);
1283 gtk_tree_model_get (model, iter2, COMPLETION_COL_TITLE, &key2, -1);
1284 if (key1 && key2)
1285 ret = g_utf8_collate (key1, key2);
1286 else if (key2 == NULL)
1287 return -1;
1288 else if (key1 == NULL)
1289 return 1;
1290 g_free (key1);
1291 g_free (key2);
1292
1293 return ret;
1294}
1295
1296static gboolean
1297entry_match_selected (GtkEntryCompletion *completion,
1298 GtkTreeModel *model,
1299 GtkTreeIter *iter,
1300 YelpLocationEntry *entry)
1301{
1302 YelpUri *base, *uri;
1303 gchar *page, *xref;
1304 gint flags;
1305 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
1306
1307 gtk_tree_model_get (model, iter, COMPLETION_COL_FLAGS, &flags, -1);
1308 if (flags & COMPLETION_FLAG_ACTIVATE_SEARCH) {
1309 entry_activate_cb (GTK_ENTRY (priv->text_entry), entry);
1310 return TRUE;
1311 }
1312
1313 g_object_get (priv->view, "yelp-uri", &base, NULL);
1314 gtk_tree_model_get (model, iter, COMPLETION_COL_PAGE, &page, -1);
1315
1316 xref = g_strconcat ("xref:", page, NULL);
1317 uri = yelp_uri_new_relative (base, xref);
1318
1319 yelp_view_load_uri (priv->view, uri);
1320
1321 g_free (page);
1322 g_free (xref);
1323 g_object_unref (uri);
1324 g_object_unref (base);
1325
1326 gtk_widget_grab_focus (GTK_WIDGET (priv->view));
1327 return TRUE;
1328}
1329
1330static void
1331view_loaded (YelpView *view,
1332 YelpLocationEntry *entry)
1333{
1334 gchar **ids;
1335 gint i;
1336 GtkTreeIter iter;
1337 gint flags;
1338 YelpUri *uri;
1339 gchar *doc_uri, *page_id;
1340 GtkTreeModel *completion;
1341 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
1342 YelpDocument *document = yelp_view_get_document (view);
1343
1344 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->history), &iter);
1345 gtk_tree_model_get (GTK_TREE_MODEL (priv->history), &iter,
1346 HISTORY_COL_FLAGS, &flags,
1347 -1);
1348 if (flags & LOCATION_ENTRY_IS_LOADING) {
1349 flags = flags & ~LOCATION_ENTRY_IS_LOADING;
1350 gtk_list_store_set (priv->history, &iter, HISTORY_COL_FLAGS, flags, -1);
1351 }
1352
1353 g_object_get (view,
1354 "yelp-uri", &uri,
1355 "page-id", &page_id,
1356 NULL);
1357 doc_uri = yelp_uri_get_document_uri (uri);
1358 gtk_list_store_set (priv->history, &iter,
1359 HISTORY_COL_DOC, doc_uri,
1360 HISTORY_COL_PAGE, page_id,
1361 -1);
1362 g_free (page_id);
1363
1364 if ((priv->completion_uri == NULL) ||
1365 !g_str_equal (doc_uri, priv->completion_uri)) {
1366 completion = (GtkTreeModel *) g_hash_table_lookup (completions, doc_uri);
1367 if (completion == NULL) {
1368 GtkListStore *base = gtk_list_store_new (5,
1369 G_TYPE_STRING, /* title */
1370 G_TYPE_STRING, /* desc */
1371 G_TYPE_STRING, /* icon */
1372 G_TYPE_STRING, /* uri */
1373 G_TYPE_INT /* flags */
1374 );
1375 completion = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (base));
1376 gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (completion),
1377 entry_completion_sort,
1378 NULL, NULL);
1379 g_hash_table_insert (completions, g_strdup (doc_uri), completion);
1380 if (document != NULL) {
1381 GtkTreeIter iter;
1382 ids = yelp_document_list_page_ids (document);
1383 for (i = 0; ids[i]; i++) {
1384 gchar *title, *desc, *icon;
1385 gtk_list_store_insert (GTK_LIST_STORE (base), &iter, 0);
1386 title = yelp_document_get_page_title (document, ids[i]);
1387 desc = yelp_document_get_page_desc (document, ids[i]);
1388 icon = yelp_document_get_page_icon (document, ids[i]);
1389 gtk_list_store_set (base, &iter,
1390 COMPLETION_COL_TITLE, title,
1391 COMPLETION_COL_DESC, desc,
1392 COMPLETION_COL_ICON, icon,
1393 COMPLETION_COL_PAGE, ids[i],
1394 -1);
1395 g_free (icon);
1396 g_free (desc);
1397 g_free (title);
1398 }
1399 g_strfreev (ids);
1400 gtk_list_store_insert (GTK_LIST_STORE (base), &iter, 0);
1401 gtk_list_store_set (base, &iter,
1402 COMPLETION_COL_ICON, "edit-find-symbolic",
1403 COMPLETION_COL_FLAGS, COMPLETION_FLAG_ACTIVATE_SEARCH,
1404 -1);
1405 }
1406 g_object_unref (base);
1407 }
1408 g_free (priv->completion_uri);
1409 priv->completion_uri = doc_uri;
1410 location_entry_set_completion (entry, completion);
1411 }
1412
1413 g_object_unref (uri);
1414}
1415
1416static void
1417view_uri_selected (YelpView *view,
1418 GParamSpec *pspec,
1419 YelpLocationEntry *entry)
1420{
1421 GtkTreeIter iter;
1422 gchar *iter_uri;
1423 gboolean cont;
1424 YelpUri *uri;
1425 gchar *struri, *doc_uri;
1426 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
1427
1428 g_object_get (G_OBJECT (view), "yelp-uri", &uri, NULL);
1429 if (uri == NULL)
1430 return;
1431
1432 struri = yelp_uri_get_canonical_uri (uri);
1433
1434 cont = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->history), &iter);
1435 while (cont) {
1436 gtk_tree_model_get (GTK_TREE_MODEL (priv->history), &iter,
1437 HISTORY_COL_URI, &iter_uri,
1438 -1);
1439 if (iter_uri && g_str_equal (struri, iter_uri)) {
1440 g_free (iter_uri);
1441 break;
1442 }
1443 else {
1444 g_free (iter_uri);
1445 cont = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->history), &iter);
1446 }
1447 }
1448 if (cont) {
1449 GtkTreeIter first;
1450 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->history), &first);
1451 gtk_list_store_move_before (priv->history, &iter, &first);
1452 }
1453 else {
1454 gint num;
1455 GtkTreeIter last;
1456 gtk_list_store_prepend (priv->history, &iter);
1457 gtk_list_store_set (priv->history, &iter,
1458 HISTORY_COL_TITLE, _("Loading"),
1459 HISTORY_COL_ICON, "help-contents",
1460 HISTORY_COL_URI, struri,
1461 HISTORY_COL_FLAGS, LOCATION_ENTRY_IS_LOADING,
1462 -1);
1463 /* Limit to MAX_HISTORY entries. There are two extra for the search entry
1464 * and the separator above it.
1465 */
1466 num = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (priv->history), NULL);
1467 if (num > MAX_HISTORY + 2) {
1468 if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (priv->history),
1469 &last, NULL,
1470 num - 3)) {
1471 gtk_list_store_remove (priv->history, &last);
1472 }
1473 }
1474 }
1475 priv->view_uri_selected = TRUE;
1476 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (entry), &iter);
1477 priv->view_uri_selected = FALSE;
1478
1479 g_free (struri);
1480 g_object_unref (uri);
1481}
1482
1483static void
1484view_page_title (YelpView *view,
1485 GParamSpec *pspec,
1486 YelpLocationEntry *entry)
1487{
1488 GtkTreeIter first;
1489 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
1490
1491 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->history), &first)) {
1492 gchar *title, *frag = NULL;
1493 YelpViewState state;
1494 YelpUri *uri;
1495
1496 g_object_get (view,
1497 "page-title", &title,
1498 "state", &state,
1499 NULL);
1500 if (title == NULL)
1501 return;
1502
1503 if (state != YELP_VIEW_STATE_ERROR) {
1504 YelpUri *uri;
1505 g_object_get (view, "yelp-uri", &uri, NULL);
1506 frag = yelp_uri_get_frag_id (uri);
1507 g_object_unref (uri);
1508 }
1509
1510 if (frag) {
1511 gchar *tmp = g_strdup_printf ("%s (#%s)", title, frag);
1512 gtk_list_store_set (priv->history, &first,
1513 HISTORY_COL_TITLE, tmp,
1514 -1);
1515 g_free (tmp);
1516 g_free (frag);
1517 }
1518 else {
1519 gtk_list_store_set (priv->history, &first,
1520 HISTORY_COL_TITLE, title,
1521 -1);
1522 }
1523 g_free (title);
1524 }
1525}
1526
1527static void
1528view_page_desc (YelpView *view,
1529 GParamSpec *pspec,
1530 YelpLocationEntry *entry)
1531{
1532 GtkTreeIter first;
1533 gchar *desc;
1534 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
1535
1536 g_object_get (view, "page-desc", &desc, NULL);
1537 if (desc == NULL)
1538 return;
1539
1540 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->history), &first))
1541 gtk_list_store_set (priv->history, &first,
1542 HISTORY_COL_DESC, desc,
1543 -1);
1544 g_free (desc);
1545}
1546
1547static void
1548view_page_icon (YelpView *view,
1549 GParamSpec *pspec,
1550 YelpLocationEntry *entry)
1551{
1552 GtkTreeIter first;
1553 gchar *icon;
1554 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
1555
1556 g_object_get (view, "page-icon", &icon, NULL);
1557 if (icon == NULL)
1558 return;
1559
1560 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->history), &first))
1561 gtk_list_store_set (priv->history, &first,
1562 HISTORY_COL_ICON, icon,
1563 -1);
1564 g_free (icon);
1565}
1566
1567static void
1568bookmarks_changed (YelpBookmarks *bookmarks,
1569 const gchar *doc_uri,
1570 YelpLocationEntry *entry)
1571{
1572 GtkTreePath *path;
1573 YelpLocationEntryPrivate *priv = GET_PRIV (entry);
1574
1575 if (priv->row)
1576 path = gtk_tree_row_reference_get_path (priv->row);
1577
1578 if (path) {
1579 GtkTreeIter iter;
1580 gchar *this_uri, *page_id;
1581 gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->history), &iter, path);
1582 gtk_tree_model_get (GTK_TREE_MODEL (priv->history), &iter,
1583 HISTORY_COL_DOC, &this_uri,
1584 HISTORY_COL_PAGE, &page_id,
1585 -1);
1586 if (this_uri && g_str_equal (this_uri, doc_uri) && page_id) {
1587 if (!yelp_bookmarks_is_bookmarked (priv->bookmarks, doc_uri, page_id)) {
1588 gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
1589 GTK_ENTRY_ICON_SECONDARY,
1590 "yelp-bookmark-add-symbolic");
1591 gtk_entry_set_icon_tooltip_text (GTK_ENTRY (priv->text_entry),
1592 GTK_ENTRY_ICON_SECONDARY,
1593 _("Bookmark this page"));
1594 }
1595 else {
1596 gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
1597 GTK_ENTRY_ICON_SECONDARY,
1598 "yelp-bookmark-remove-symbolic");
1599 gtk_entry_set_icon_tooltip_text (GTK_ENTRY (priv->text_entry),
1600 GTK_ENTRY_ICON_SECONDARY,
1601 _("Remove bookmark"));
1602 }
1603 }
1604 else {
1605 gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->text_entry),
1606 GTK_ENTRY_ICON_SECONDARY,
1607 NULL);
1608 }
1609 g_free (this_uri);
1610 g_free (page_id);
1611 }
1612}
1613
1614/**
1615 * yelp_location_entry_new:
1616 * @view: A #YelpView.
1617 *
1618 * Creates a new #YelpLocationEntry widget to control @view.
1619 *
1620 * Returns: A new #YelpLocationEntry.
1621 **/
1622GtkWidget*
1623yelp_location_entry_new (YelpView *view,
1624 YelpBookmarks *bookmarks)
1625{
1626 GtkWidget *ret;
1627 g_return_val_if_fail (YELP_IS_VIEW (view), NULL);
1628
1629 ret = GTK_WIDGET (g_object_new (YELP_TYPE_LOCATION_ENTRY,
1630 "has-entry", TRUE,
1631 "view", view,
1632 "bookmarks", bookmarks,
1633 NULL));
1634
1635 return ret;
1636}
1637
1638/**
1639 * yelp_location_entry_start_search:
1640 * @entry: A #YelpLocationEntry.
1641 *
1642 * Puts @entry into search mode. This focuses the entry and clears its text
1643 * contents. When the user activates the search, the
1644 * #YelpLocationEntry::search-activated signal will be emitted.
1645 **/
1646void
1647yelp_location_entry_start_search (YelpLocationEntry *entry)
1648{
1649 location_entry_start_search (entry, TRUE);
1650}
16510
=== removed file '.pc/git_signal_handler.patch/libyelp/yelp-view.c'
--- .pc/git_signal_handler.patch/libyelp/yelp-view.c 2014-02-27 13:28:05 +0000
+++ .pc/git_signal_handler.patch/libyelp/yelp-view.c 1970-01-01 00:00:00 +0000
@@ -1,2220 +0,0 @@
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * Copyright (C) 2009 Shaun McCance <shaunm@gnome.org>
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 * Author: Shaun McCance <shaunm@gnome.org>
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <glib/gi18n.h>
28#include <glib-object.h>
29#include <gio/gio.h>
30#include <gtk/gtk.h>
31#include <gdk/gdkx.h>
32#include <webkit/webkit.h>
33
34#include "yelp-debug.h"
35#include "yelp-docbook-document.h"
36#include "yelp-error.h"
37#include "yelp-marshal.h"
38#include "yelp-settings.h"
39#include "yelp-types.h"
40#include "yelp-view.h"
41
42#define BOGUS_URI "file:///bogus/"
43#define BOGUS_URI_LEN 14
44
45static void yelp_view_init (YelpView *view);
46static void yelp_view_class_init (YelpViewClass *klass);
47static void yelp_view_dispose (GObject *object);
48static void yelp_view_finalize (GObject *object);
49static void yelp_view_get_property (GObject *object,
50 guint prop_id,
51 GValue *value,
52 GParamSpec *pspec);
53static void yelp_view_set_property (GObject *object,
54 guint prop_id,
55 const GValue *value,
56 GParamSpec *pspec);
57
58static gboolean view_external_uri (YelpView *view,
59 YelpUri *uri);
60static void view_install_uri (YelpView *view,
61 const gchar *uri);
62static void view_scrolled (GtkAdjustment *adjustment,
63 YelpView *view);
64static void view_set_hadjustment (YelpView *view,
65 GParamSpec *pspec,
66 gpointer data);
67static void view_set_vadjustment (YelpView *view,
68 GParamSpec *pspec,
69 gpointer data);
70static void popup_open_link (GtkMenuItem *item,
71 YelpView *view);
72static void popup_open_link_new (GtkMenuItem *item,
73 YelpView *view);
74static void popup_copy_link (GtkMenuItem *item,
75 YelpView *view);
76static void popup_save_image (GtkMenuItem *item,
77 YelpView *view);
78static void popup_send_image (GtkMenuItem *item,
79 YelpView *view);
80static void popup_copy_code (GtkMenuItem *item,
81 YelpView *view);
82static void popup_save_code (GtkMenuItem *item,
83 YelpView *view);
84static void view_populate_popup (YelpView *view,
85 GtkMenu *menu,
86 gpointer data);
87static void view_script_alert (YelpView *view,
88 WebKitWebFrame *frame,
89 gchar *message,
90 gpointer data);
91static gboolean view_navigation_requested (WebKitWebView *view,
92 WebKitWebFrame *frame,
93 WebKitNetworkRequest *request,
94 WebKitWebNavigationAction *action,
95 WebKitWebPolicyDecision *decision,
96 gpointer user_data);
97static void view_resource_request (WebKitWebView *view,
98 WebKitWebFrame *frame,
99 WebKitWebResource *resource,
100 WebKitNetworkRequest *request,
101 WebKitNetworkResponse *response,
102 gpointer user_data);
103static void view_document_loaded (WebKitWebView *view,
104 WebKitWebFrame *frame,
105 gpointer user_data);
106
107static void view_print (GtkAction *action,
108 YelpView *view);
109static void view_history_action (GtkAction *action,
110 YelpView *view);
111static void view_navigation_action (GtkAction *action,
112 YelpView *view);
113
114static void view_clear_load (YelpView *view);
115static void view_load_page (YelpView *view);
116static void view_show_error_page (YelpView *view,
117 GError *error);
118
119static void settings_set_fonts (YelpSettings *settings);
120static void settings_show_text_cursor (YelpSettings *settings);
121
122static void uri_resolved (YelpUri *uri,
123 YelpView *view);
124static void document_callback (YelpDocument *document,
125 YelpDocumentSignal signal,
126 YelpView *view,
127 GError *error);
128
129static const GtkActionEntry entries[] = {
130 {"YelpViewPrint", GTK_STOCK_PRINT,
131 N_("_Print..."),
132 "<Control>P",
133 NULL,
134 G_CALLBACK (view_print) },
135 {"YelpViewGoBack", GTK_STOCK_GO_BACK,
136 N_("_Back"),
137 "<Alt>Left",
138 NULL,
139 G_CALLBACK (view_history_action) },
140 {"YelpViewGoForward", GTK_STOCK_GO_FORWARD,
141 N_("_Forward"),
142 "<Alt>Right",
143 NULL,
144 G_CALLBACK (view_history_action) },
145 {"YelpViewGoPrevious", NULL,
146 N_("_Previous Page"),
147 "<Control>Page_Up",
148 NULL,
149 G_CALLBACK (view_navigation_action) },
150 {"YelpViewGoNext", NULL,
151 N_("_Next Page"),
152 "<Control>Page_Down",
153 NULL,
154 G_CALLBACK (view_navigation_action) }
155};
156
157static gchar *nautilus_sendto = NULL;
158
159enum {
160 PROP_0,
161 PROP_URI,
162 PROP_STATE,
163 PROP_PAGE_ID,
164 PROP_ROOT_TITLE,
165 PROP_PAGE_TITLE,
166 PROP_PAGE_DESC,
167 PROP_PAGE_ICON
168};
169
170enum {
171 NEW_VIEW_REQUESTED,
172 EXTERNAL_URI,
173 LOADED,
174 LAST_SIGNAL
175};
176static gint signals[LAST_SIGNAL] = { 0 };
177
178G_DEFINE_TYPE (YelpView, yelp_view, WEBKIT_TYPE_WEB_VIEW);
179#define GET_PRIV(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_VIEW, YelpViewPrivate))
180
181static WebKitWebSettings *websettings;
182
183typedef struct _YelpActionEntry YelpActionEntry;
184struct _YelpActionEntry {
185 GtkAction *action;
186 YelpViewActionValidFunc func;
187 gpointer data;
188};
189static void
190action_entry_free (YelpActionEntry *entry)
191{
192 if (entry == NULL)
193 return;
194 g_object_unref (entry->action);
195 g_free (entry);
196}
197
198typedef struct _YelpBackEntry YelpBackEntry;
199struct _YelpBackEntry {
200 YelpUri *uri;
201 gchar *title;
202 gchar *desc;
203 gdouble hadj;
204 gdouble vadj;
205};
206static void
207back_entry_free (YelpBackEntry *back)
208{
209 if (back == NULL)
210 return;
211 g_object_unref (back->uri);
212 g_free (back->title);
213 g_free (back->desc);
214 g_free (back);
215}
216
217typedef struct _YelpViewPrivate YelpViewPrivate;
218struct _YelpViewPrivate {
219 YelpUri *uri;
220 YelpUri *resolve_uri;
221 gulong uri_resolved;
222 gchar *bogus_uri;
223 YelpDocument *document;
224 GCancellable *cancellable;
225 GtkAdjustment *vadjustment;
226 GtkAdjustment *hadjustment;
227 gdouble vadjust;
228 gdouble hadjust;
229 gulong vadjuster;
230 gulong hadjuster;
231
232 gchar *popup_link_uri;
233 gchar *popup_link_text;
234 gchar *popup_image_uri;
235 WebKitDOMNode *popup_code_node;
236 WebKitDOMNode *popup_code_title;
237 gchar *popup_code_text;
238
239 YelpViewState state;
240 YelpViewState prevstate;
241
242 gchar *page_id;
243 gchar *root_title;
244 gchar *page_title;
245 gchar *page_desc;
246 gchar *page_icon;
247
248 GList *back_list;
249 GList *back_cur;
250 gboolean back_load;
251
252 GtkActionGroup *action_group;
253
254 GSList *link_actions;
255
256 gint navigation_requested;
257};
258
259#define TARGET_TYPE_URI_LIST "text/uri-list"
260enum {
261 TARGET_URI_LIST
262};
263
264static void
265yelp_view_init (YelpView *view)
266{
267 GtkAction *action;
268 YelpViewPrivate *priv = GET_PRIV (view);
269
270 g_object_set (view, "settings", websettings, NULL);
271
272 priv->cancellable = NULL;
273
274 priv->prevstate = priv->state = YELP_VIEW_STATE_BLANK;
275
276 priv->navigation_requested =
277 g_signal_connect (view, "navigation-policy-decision-requested",
278 G_CALLBACK (view_navigation_requested), NULL);
279 g_signal_connect (view, "resource-request-starting",
280 G_CALLBACK (view_resource_request), NULL);
281 g_signal_connect (view, "document-load-finished",
282 G_CALLBACK (view_document_loaded), NULL);
283 g_signal_connect (view, "notify::hadjustment",
284 G_CALLBACK (view_set_hadjustment), NULL);
285 g_signal_connect (view, "notify::vadjustment",
286 G_CALLBACK (view_set_vadjustment), NULL);
287 g_signal_connect (view, "populate-popup",
288 G_CALLBACK (view_populate_popup), NULL);
289 g_signal_connect (view, "script-alert",
290 G_CALLBACK (view_script_alert), NULL);
291
292 priv->action_group = gtk_action_group_new ("YelpView");
293 gtk_action_group_set_translation_domain (priv->action_group, GETTEXT_PACKAGE);
294 gtk_action_group_add_actions (priv->action_group,
295 entries, G_N_ELEMENTS (entries),
296 view);
297 action = gtk_action_group_get_action (priv->action_group, "YelpViewGoBack");
298 gtk_action_set_sensitive (action, FALSE);
299 action = gtk_action_group_get_action (priv->action_group, "YelpViewGoForward");
300 gtk_action_set_sensitive (action, FALSE);
301}
302
303static void
304yelp_view_dispose (GObject *object)
305{
306 YelpViewPrivate *priv = GET_PRIV (object);
307
308 view_clear_load (YELP_VIEW (object));
309
310 if (priv->vadjuster > 0) {
311 g_source_remove (priv->vadjuster);
312 priv->vadjuster = 0;
313 }
314
315 if (priv->hadjuster > 0) {
316 g_source_remove (priv->hadjuster);
317 priv->hadjuster = 0;
318 }
319
320 if (priv->action_group) {
321 g_object_unref (priv->action_group);
322 priv->action_group = NULL;
323 }
324
325 if (priv->document) {
326 g_object_unref (priv->document);
327 priv->document = NULL;
328 }
329
330 while (priv->link_actions) {
331 action_entry_free (priv->link_actions->data);
332 priv->link_actions = g_slist_delete_link (priv->link_actions, priv->link_actions);
333 }
334
335 priv->back_cur = NULL;
336 while (priv->back_list) {
337 back_entry_free ((YelpBackEntry *) priv->back_list->data);
338 priv->back_list = g_list_delete_link (priv->back_list, priv->back_list);
339 }
340
341 G_OBJECT_CLASS (yelp_view_parent_class)->dispose (object);
342}
343
344static void
345yelp_view_finalize (GObject *object)
346{
347 YelpViewPrivate *priv = GET_PRIV (object);
348
349 g_free (priv->popup_link_uri);
350 g_free (priv->popup_link_text);
351 g_free (priv->popup_image_uri);
352 g_free (priv->popup_code_text);
353
354 g_free (priv->page_id);
355 g_free (priv->root_title);
356 g_free (priv->page_title);
357 g_free (priv->page_desc);
358 g_free (priv->page_icon);
359
360 g_free (priv->bogus_uri);
361
362 G_OBJECT_CLASS (yelp_view_parent_class)->finalize (object);
363}
364
365static void
366yelp_view_class_init (YelpViewClass *klass)
367{
368 GObjectClass *object_class = G_OBJECT_CLASS (klass);
369 YelpSettings *settings = yelp_settings_get_default ();
370
371 nautilus_sendto = g_find_program_in_path ("nautilus-sendto");
372
373 websettings = webkit_web_settings_new ();
374 g_object_set (websettings, "enable-universal-access-from-file-uris", TRUE, NULL);
375 g_signal_connect (settings,
376 "fonts-changed",
377 G_CALLBACK (settings_set_fonts),
378 NULL);
379 settings_set_fonts (settings);
380 g_signal_connect (settings,
381 "notify::show-text-cursor",
382 G_CALLBACK (settings_show_text_cursor),
383 NULL);
384 settings_show_text_cursor (settings);
385
386 klass->external_uri = view_external_uri;
387
388 object_class->dispose = yelp_view_dispose;
389 object_class->finalize = yelp_view_finalize;
390 object_class->get_property = yelp_view_get_property;
391 object_class->set_property = yelp_view_set_property;
392
393 signals[NEW_VIEW_REQUESTED] =
394 g_signal_new ("new-view-requested",
395 G_TYPE_FROM_CLASS (klass),
396 G_SIGNAL_RUN_LAST,
397 0, NULL, NULL,
398 g_cclosure_marshal_VOID__OBJECT,
399 G_TYPE_NONE, 1, YELP_TYPE_URI);
400
401 signals[EXTERNAL_URI] =
402 g_signal_new ("external-uri",
403 G_TYPE_FROM_CLASS (klass),
404 G_SIGNAL_RUN_LAST,
405 G_STRUCT_OFFSET (YelpViewClass, external_uri),
406 g_signal_accumulator_true_handled, NULL,
407 yelp_marshal_BOOLEAN__OBJECT,
408 G_TYPE_BOOLEAN, 1, YELP_TYPE_URI);
409
410 signals[LOADED] =
411 g_signal_new ("loaded",
412 G_TYPE_FROM_CLASS (klass),
413 G_SIGNAL_RUN_LAST,
414 0, NULL, NULL,
415 g_cclosure_marshal_VOID__VOID,
416 G_TYPE_NONE, 0);
417
418 g_type_class_add_private (klass, sizeof (YelpViewPrivate));
419
420 g_object_class_install_property (object_class,
421 PROP_URI,
422 g_param_spec_object ("yelp-uri",
423 _("Yelp URI"),
424 _("A YelpUri with the current location"),
425 YELP_TYPE_URI,
426 G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
427 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
428
429 g_object_class_install_property (object_class,
430 PROP_STATE,
431 g_param_spec_enum ("state",
432 N_("Loading State"),
433 N_("The loading state of the view"),
434 YELP_TYPE_VIEW_STATE,
435 YELP_VIEW_STATE_BLANK,
436 G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
437 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
438
439 g_object_class_install_property (object_class,
440 PROP_PAGE_ID,
441 g_param_spec_string ("page-id",
442 N_("Page ID"),
443 N_("The ID of the root page of the page being viewed"),
444 NULL,
445 G_PARAM_READABLE |
446 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
447
448 g_object_class_install_property (object_class,
449 PROP_ROOT_TITLE,
450 g_param_spec_string ("root-title",
451 N_("Root Title"),
452 N_("The title of the root page of the page being viewed"),
453 NULL,
454 G_PARAM_READABLE |
455 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
456
457 g_object_class_install_property (object_class,
458 PROP_PAGE_TITLE,
459 g_param_spec_string ("page-title",
460 N_("Page Title"),
461 N_("The title of the page being viewed"),
462 NULL,
463 G_PARAM_READABLE |
464 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
465
466 g_object_class_install_property (object_class,
467 PROP_PAGE_DESC,
468 g_param_spec_string ("page-desc",
469 N_("Page Description"),
470 N_("The description of the page being viewed"),
471 NULL,
472 G_PARAM_READABLE |
473 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
474
475 g_object_class_install_property (object_class,
476 PROP_PAGE_ICON,
477 g_param_spec_string ("page-icon",
478 N_("Page Icon"),
479 N_("The icon of the page being viewed"),
480 NULL,
481 G_PARAM_READABLE |
482 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
483}
484
485static void
486yelp_view_get_property (GObject *object,
487 guint prop_id,
488 GValue *value,
489 GParamSpec *pspec)
490{
491 YelpViewPrivate *priv = GET_PRIV (object);
492
493 switch (prop_id)
494 {
495 case PROP_URI:
496 g_value_set_object (value, priv->uri);
497 break;
498 case PROP_PAGE_ID:
499 g_value_set_string (value, priv->page_id);
500 break;
501 case PROP_ROOT_TITLE:
502 g_value_set_string (value, priv->root_title);
503 break;
504 case PROP_PAGE_TITLE:
505 g_value_set_string (value, priv->page_title);
506 break;
507 case PROP_PAGE_DESC:
508 g_value_set_string (value, priv->page_desc);
509 break;
510 case PROP_PAGE_ICON:
511 if (priv->page_icon)
512 g_value_set_string (value, priv->page_icon);
513 else
514 g_value_set_string (value, "yelp-page-symbolic");
515 break;
516 case PROP_STATE:
517 g_value_set_enum (value, priv->state);
518 break;
519 default:
520 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
521 break;
522 }
523}
524
525static void
526yelp_view_set_property (GObject *object,
527 guint prop_id,
528 const GValue *value,
529 GParamSpec *pspec)
530{
531 YelpUri *uri;
532 YelpViewPrivate *priv = GET_PRIV (object);
533
534 switch (prop_id)
535 {
536 case PROP_URI:
537 uri = g_value_get_object (value);
538 yelp_view_load_uri (YELP_VIEW (object), uri);
539 g_object_unref (uri);
540 break;
541 case PROP_STATE:
542 priv->prevstate = priv->state;
543 priv->state = g_value_get_enum (value);
544 break;
545 default:
546 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
547 break;
548 }
549}
550
551/******************************************************************************/
552
553GtkWidget *
554yelp_view_new (void)
555{
556 return (GtkWidget *) g_object_new (YELP_TYPE_VIEW, NULL);
557}
558
559void
560yelp_view_load (YelpView *view,
561 const gchar *uri)
562{
563 YelpUri *yuri = yelp_uri_new (uri);
564 yelp_view_load_uri (view, yuri);
565 g_object_unref (yuri);
566}
567
568void
569yelp_view_load_uri (YelpView *view,
570 YelpUri *uri)
571{
572 YelpViewPrivate *priv = GET_PRIV (view);
573
574 g_object_set (view, "state", YELP_VIEW_STATE_LOADING, NULL);
575
576 gtk_action_set_sensitive (gtk_action_group_get_action (priv->action_group,
577 "YelpViewGoPrevious"),
578 FALSE);
579 gtk_action_set_sensitive (gtk_action_group_get_action (priv->action_group,
580 "YelpViewGoNext"),
581 FALSE);
582
583 if (!yelp_uri_is_resolved (uri)) {
584 if (priv->resolve_uri != NULL) {
585 if (priv->uri_resolved != 0) {
586 g_signal_handler_disconnect (priv->resolve_uri, priv->uri_resolved);
587 priv->uri_resolved = 0;
588 }
589 g_object_unref (priv->resolve_uri);
590 }
591 priv->resolve_uri = g_object_ref (uri);
592 priv->uri_resolved = g_signal_connect (uri, "resolved",
593 G_CALLBACK (uri_resolved),
594 view);
595 yelp_uri_resolve (uri);
596 }
597 else {
598 uri_resolved (uri, view);
599 }
600}
601
602void
603yelp_view_load_document (YelpView *view,
604 YelpUri *uri,
605 YelpDocument *document)
606{
607 GParamSpec *spec;
608 YelpViewPrivate *priv = GET_PRIV (view);
609
610 g_return_if_fail (yelp_uri_is_resolved (uri));
611
612 g_object_set (view, "state", YELP_VIEW_STATE_LOADING, NULL);
613
614 g_object_ref (uri);
615 view_clear_load (view);
616 priv->uri = uri;
617 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
618 "yelp-uri");
619 g_signal_emit_by_name (view, "notify::yelp-uri", spec);
620 g_object_ref (document);
621 if (priv->document)
622 g_object_unref (document);
623 priv->document = document;
624
625 view_load_page (view);
626}
627
628YelpDocument *
629yelp_view_get_document (YelpView *view)
630{
631 YelpViewPrivate *priv = GET_PRIV (view);
632 return priv->document;
633}
634
635GtkActionGroup *
636yelp_view_get_action_group (YelpView *view)
637{
638 YelpViewPrivate *priv = GET_PRIV (view);
639 return priv->action_group;
640}
641
642/******************************************************************************/
643
644void
645yelp_view_add_link_action (YelpView *view,
646 GtkAction *action,
647 YelpViewActionValidFunc func,
648 gpointer data)
649{
650 YelpActionEntry *entry;
651 YelpViewPrivate *priv = GET_PRIV (view);
652
653 entry = g_new0 (YelpActionEntry, 1);
654 entry->action = g_object_ref (action);
655 entry->func = func;
656 entry->data = data;
657
658 priv->link_actions = g_slist_append (priv->link_actions, entry);
659}
660
661YelpUri *
662yelp_view_get_active_link_uri (YelpView *view)
663{
664 YelpViewPrivate *priv = GET_PRIV (view);
665 YelpUri *uri;
666
667 if (g_str_has_prefix (priv->popup_link_uri, BOGUS_URI))
668 uri = yelp_uri_new_relative (priv->uri, priv->popup_link_uri + BOGUS_URI_LEN);
669 else
670 uri = yelp_uri_new_relative (priv->uri, priv->popup_link_uri);
671
672 return uri;
673}
674
675gchar *
676yelp_view_get_active_link_text (YelpView *view)
677{
678 YelpViewPrivate *priv = GET_PRIV (view);
679 return g_strdup (priv->popup_link_text);
680}
681
682/******************************************************************************/
683
684static gboolean
685view_external_uri (YelpView *view,
686 YelpUri *uri)
687{
688 gchar *struri = yelp_uri_get_canonical_uri (uri);
689 g_app_info_launch_default_for_uri (struri, NULL, NULL);
690 g_free (struri);
691 return TRUE;
692}
693
694typedef struct _YelpInstallInfo YelpInstallInfo;
695struct _YelpInstallInfo {
696 YelpView *view;
697 gchar *uri;
698};
699
700static void
701yelp_install_info_free (YelpInstallInfo *info)
702{
703 g_object_unref (info->view);
704 if (info->uri)
705 g_free (info->uri);
706 g_free (info);
707}
708
709static void
710view_install_installed (GDBusConnection *connection,
711 GAsyncResult *res,
712 YelpInstallInfo *info)
713{
714 GError *error = NULL;
715 g_dbus_connection_call_finish (connection, res, &error);
716 if (error) {
717 const gchar *err = NULL;
718 if (error->domain == G_DBUS_ERROR) {
719 if (error->code == G_DBUS_ERROR_SERVICE_UNKNOWN)
720 err = _("You do not have PackageKit. Package install links require PackageKit.");
721 else
722 err = error->message;
723 }
724 if (err != NULL) {
725 GtkWidget *dialog = gtk_message_dialog_new (NULL, 0,
726 GTK_MESSAGE_ERROR,
727 GTK_BUTTONS_CLOSE,
728 "%s", err);
729 gtk_dialog_run ((GtkDialog *) dialog);
730 gtk_widget_destroy (dialog);
731 }
732 g_error_free (error);
733 }
734 else if (info->uri) {
735 gchar *struri, *docuri;
736 YelpViewPrivate *priv = GET_PRIV (info->view);
737 docuri = yelp_uri_get_document_uri (priv->uri);
738 if (g_str_equal (docuri, info->uri)) {
739 struri = yelp_uri_get_canonical_uri (priv->uri);
740 yelp_view_load (info->view, struri);
741 g_free (struri);
742 }
743 g_free (docuri);
744 }
745
746 yelp_install_info_free (info);
747}
748
749static void
750view_install_uri (YelpView *view,
751 const gchar *uri)
752{
753 GDBusConnection *connection;
754 GError *error;
755 gboolean help = FALSE, ghelp = FALSE;
756 GVariantBuilder *strv;
757 YelpInstallInfo *info;
758 guint32 xid = 0;
759 YelpViewPrivate *priv = GET_PRIV (view);
760 GtkWidget *gtkwin;
761 GdkWindow *gdkwin;
762 /* do not free */
763 gchar *pkg, *confirm_search;
764
765 if (g_str_has_prefix (uri, "install-help:")) {
766 help = TRUE;
767 pkg = (gchar *) uri + 13;
768 }
769 else if (g_str_has_prefix (uri, "install-ghelp:")) {
770 ghelp = TRUE;
771 pkg = (gchar *) uri + 14;
772 }
773 else if (g_str_has_prefix (uri, "install:")) {
774 pkg = (gchar *) uri + 8;
775 }
776
777 connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
778 if (connection == NULL) {
779 g_warning ("Unable to connect to dbus: %s", error->message);
780 g_error_free (error);
781 return;
782 }
783
784 info = g_new0 (YelpInstallInfo, 1);
785 info->view = g_object_ref (view);
786
787 gtkwin = gtk_widget_get_toplevel (GTK_WIDGET (view));
788 if (gtkwin != NULL && gtk_widget_is_toplevel (gtkwin)) {
789 gdkwin = gtk_widget_get_window (gtkwin);
790 if (gdkwin != NULL)
791 xid = gdk_x11_window_get_xid (gdkwin);
792 }
793
794 if (priv->state == YELP_VIEW_STATE_ERROR)
795 confirm_search = "hide-confirm-search";
796 else
797 confirm_search = "";
798
799 if (help || ghelp) {
800 const gchar * const *datadirs = g_get_system_data_dirs ();
801 gint datadirs_i;
802 gchar *docbook, *fname;
803 docbook = g_strconcat (pkg, ".xml", NULL);
804 strv = g_variant_builder_new (G_VARIANT_TYPE ("as"));
805 for (datadirs_i = 0; datadirs[datadirs_i] != NULL; datadirs_i++) {
806 if (ghelp) {
807 fname = g_build_filename (datadirs[datadirs_i], "gnome", "help",
808 pkg, "C", "index.page", NULL);
809 g_variant_builder_add (strv, "s", fname);
810 g_free (fname);
811 fname = g_build_filename (datadirs[datadirs_i], "gnome", "help",
812 pkg, "C", docbook, NULL);
813 g_variant_builder_add (strv, "s", fname);
814 g_free (fname);
815 }
816 else {
817 fname = g_build_filename (datadirs[datadirs_i], "help", "C",
818 pkg, "index.page", NULL);
819 g_variant_builder_add (strv, "s", fname);
820 g_free (fname);
821 fname = g_build_filename (datadirs[datadirs_i], "help", "C",
822 pkg, "index.docbook", NULL);
823 g_variant_builder_add (strv, "s", fname);
824 g_free (fname);
825 }
826 }
827 g_free (docbook);
828 info->uri = g_strconcat (ghelp ? "ghelp:" : "help:", pkg, NULL);
829 g_dbus_connection_call (connection,
830 "org.freedesktop.PackageKit",
831 "/org/freedesktop/PackageKit",
832 "org.freedesktop.PackageKit.Modify",
833 "InstallProvideFiles",
834 g_variant_new ("(uass)", xid, strv, confirm_search),
835 NULL,
836 G_DBUS_CALL_FLAGS_NONE,
837 G_MAXINT, NULL,
838 (GAsyncReadyCallback) view_install_installed,
839 info);
840 g_variant_builder_unref (strv);
841 }
842 else {
843 gchar **pkgs;
844 gint i;
845 strv = g_variant_builder_new (G_VARIANT_TYPE ("as"));
846 pkgs = g_strsplit (pkg, ",", 0);
847 for (i = 0; pkgs[i]; i++)
848 g_variant_builder_add (strv, "s", pkgs[i]);
849 g_strfreev (pkgs);
850 g_dbus_connection_call (connection,
851 "org.freedesktop.PackageKit",
852 "/org/freedesktop/PackageKit",
853 "org.freedesktop.PackageKit.Modify",
854 "InstallPackageNames",
855 g_variant_new ("(uass)", xid, strv, confirm_search),
856 NULL,
857 G_DBUS_CALL_FLAGS_NONE,
858 G_MAXINT, NULL,
859 (GAsyncReadyCallback) view_install_installed,
860 info);
861 g_variant_builder_unref (strv);
862 }
863
864 g_object_unref (connection);
865}
866
867static void
868view_scrolled (GtkAdjustment *adjustment,
869 YelpView *view)
870{
871 YelpViewPrivate *priv = GET_PRIV (view);
872 if (priv->back_cur == NULL || priv->back_cur->data == NULL)
873 return;
874 if (adjustment == priv->vadjustment)
875 ((YelpBackEntry *) priv->back_cur->data)->vadj = gtk_adjustment_get_value (adjustment);
876 else if (adjustment = priv->hadjustment)
877 ((YelpBackEntry *) priv->back_cur->data)->hadj = gtk_adjustment_get_value (adjustment);
878}
879
880static void
881view_set_hadjustment (YelpView *view,
882 GParamSpec *pspec,
883 gpointer data)
884{
885 YelpViewPrivate *priv = GET_PRIV (view);
886 priv->hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (view));
887 if (priv->hadjuster > 0)
888 g_source_remove (priv->hadjuster);
889 priv->hadjuster = 0;
890 if (priv->hadjustment)
891 priv->hadjuster = g_signal_connect (priv->hadjustment, "value-changed",
892 G_CALLBACK (view_scrolled), view);
893}
894
895static void
896view_set_vadjustment (YelpView *view,
897 GParamSpec *pspec,
898 gpointer data)
899{
900 YelpViewPrivate *priv = GET_PRIV (view);
901 priv->vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (view));
902 if (priv->vadjuster > 0)
903 g_source_remove (priv->vadjuster);
904 priv->vadjuster = 0;
905 if (priv->vadjustment)
906 priv->vadjuster = g_signal_connect (priv->vadjustment, "value-changed",
907 G_CALLBACK (view_scrolled), view);
908}
909
910static void
911popup_open_link (GtkMenuItem *item,
912 YelpView *view)
913{
914 YelpViewPrivate *priv = GET_PRIV (view);
915 YelpUri *uri;
916
917 if (g_str_has_prefix (priv->popup_link_uri, BOGUS_URI))
918 uri = yelp_uri_new_relative (priv->uri, priv->popup_link_uri + BOGUS_URI_LEN);
919 else
920 uri = yelp_uri_new_relative (priv->uri, priv->popup_link_uri);
921
922 yelp_view_load_uri (view, uri);
923 g_object_unref (uri);
924
925 g_free (priv->popup_link_uri);
926 priv->popup_link_uri = NULL;
927
928 g_free (priv->popup_link_text);
929 priv->popup_link_text = NULL;
930}
931
932static void
933popup_open_link_new (GtkMenuItem *item,
934 YelpView *view)
935{
936 YelpViewPrivate *priv = GET_PRIV (view);
937 YelpUri *uri;
938
939 if (g_str_has_prefix (priv->popup_link_uri, BOGUS_URI))
940 uri = yelp_uri_new_relative (priv->uri, priv->popup_link_uri + BOGUS_URI_LEN);
941 else
942 uri = yelp_uri_new_relative (priv->uri, priv->popup_link_uri);
943
944 g_free (priv->popup_link_uri);
945 priv->popup_link_uri = NULL;
946
947 g_free (priv->popup_link_text);
948 priv->popup_link_text = NULL;
949
950 g_signal_emit (view, signals[NEW_VIEW_REQUESTED], 0, uri);
951 g_object_unref (uri);
952}
953
954static void
955popup_copy_link (GtkMenuItem *item,
956 YelpView *view)
957{
958 YelpViewPrivate *priv = GET_PRIV (view);
959 gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (view), GDK_SELECTION_CLIPBOARD),
960 priv->popup_link_uri,
961 -1);
962}
963
964typedef struct _YelpSaveData YelpSaveData;
965struct _YelpSaveData {
966 GFile *orig;
967 GFile *dest;
968 YelpView *view;
969 GtkWindow *window;
970};
971
972static void
973file_copied (GFile *file,
974 GAsyncResult *res,
975 YelpSaveData *data)
976{
977 GError *error = NULL;
978 if (!g_file_copy_finish (file, res, &error)) {
979 GtkWidget *dialog = gtk_message_dialog_new (gtk_widget_get_visible (GTK_WIDGET (data->window)) ? data->window : NULL,
980 GTK_DIALOG_DESTROY_WITH_PARENT,
981 GTK_MESSAGE_ERROR,
982 GTK_BUTTONS_OK,
983 "%s", error->message);
984 gtk_dialog_run (GTK_DIALOG (dialog));
985 gtk_widget_destroy (dialog);
986 }
987 g_object_unref (data->orig);
988 g_object_unref (data->dest);
989 g_object_unref (data->view);
990 g_object_unref (data->window);
991}
992
993static void
994popup_save_image (GtkMenuItem *item,
995 YelpView *view)
996{
997 YelpSaveData *data;
998 GtkWidget *dialog, *window;
999 gchar *basename;
1000 gint res;
1001 YelpViewPrivate *priv = GET_PRIV (view);
1002
1003 for (window = gtk_widget_get_parent (GTK_WIDGET (view));
1004 window && !GTK_IS_WINDOW (window);
1005 window = gtk_widget_get_parent (window));
1006
1007 data = g_new0 (YelpSaveData, 1);
1008 data->orig = g_file_new_for_uri (priv->popup_image_uri);
1009 data->view = g_object_ref (view);
1010 data->window = g_object_ref (window);
1011 g_free (priv->popup_image_uri);
1012 priv->popup_image_uri = NULL;
1013
1014 dialog = gtk_file_chooser_dialog_new (_("Save Image"),
1015 GTK_WINDOW (window),
1016 GTK_FILE_CHOOSER_ACTION_SAVE,
1017 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1018 GTK_STOCK_SAVE, GTK_RESPONSE_OK,
1019 NULL);
1020 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
1021 basename = g_file_get_basename (data->orig);
1022 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), basename);
1023 g_free (basename);
1024 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
1025 g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP));
1026
1027 res = gtk_dialog_run (GTK_DIALOG (dialog));
1028
1029 if (res == GTK_RESPONSE_OK) {
1030 data->dest = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
1031 g_file_copy_async (data->orig, data->dest,
1032 G_FILE_COPY_OVERWRITE,
1033 G_PRIORITY_DEFAULT,
1034 NULL, NULL, NULL,
1035 (GAsyncReadyCallback) file_copied,
1036 data);
1037 }
1038 else {
1039 g_object_unref (data->orig);
1040 g_object_unref (data->view);
1041 g_object_unref (data->window);
1042 g_free (data);
1043 }
1044
1045 gtk_widget_destroy (dialog);
1046}
1047
1048static void
1049popup_send_image (GtkMenuItem *item,
1050 YelpView *view)
1051{
1052 gchar *command;
1053 GAppInfo *app;
1054 GAppLaunchContext *context;
1055 GError *error = NULL;
1056 YelpViewPrivate *priv = GET_PRIV (view);
1057
1058 command = g_strdup_printf ("%s %s", nautilus_sendto, priv->popup_image_uri);
1059 context = (GAppLaunchContext *) gdk_display_get_app_launch_context (gtk_widget_get_display (GTK_WIDGET (item)));
1060
1061 app = g_app_info_create_from_commandline (command, NULL, 0, &error);
1062 if (app) {
1063 g_app_info_launch (app, NULL, context, &error);
1064 g_object_unref (app);
1065 }
1066
1067 if (error) {
1068 g_debug ("Could not launch nautilus-sendto: %s", error->message);
1069 g_error_free (error);
1070 }
1071
1072 g_object_unref (context);
1073 g_free (command);
1074 g_free (priv->popup_image_uri);
1075 priv->popup_image_uri = NULL;
1076}
1077
1078static void
1079popup_copy_code (GtkMenuItem *item,
1080 YelpView *view)
1081{
1082 YelpViewPrivate *priv = GET_PRIV (view);
1083 GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1084 gchar *content = webkit_dom_node_get_text_content (priv->popup_code_node);
1085 gtk_clipboard_set_text (clipboard, content, -1);
1086 g_free (content);
1087}
1088
1089static void
1090popup_save_code (GtkMenuItem *item,
1091 YelpView *view)
1092{
1093 YelpViewPrivate *priv = GET_PRIV (view);
1094 GtkWidget *dialog, *window;
1095 gint res;
1096
1097 g_free (priv->popup_code_text);
1098 priv->popup_code_text = webkit_dom_node_get_text_content (priv->popup_code_node);
1099 if (!g_str_has_suffix (priv->popup_code_text, "\n")) {
1100 gchar *tmp = g_strconcat (priv->popup_code_text, "\n", NULL);
1101 g_free (priv->popup_code_text);
1102 priv->popup_code_text = tmp;
1103 }
1104
1105 for (window = gtk_widget_get_parent (GTK_WIDGET (view));
1106 window && !GTK_IS_WINDOW (window);
1107 window = gtk_widget_get_parent (window));
1108
1109 dialog = gtk_file_chooser_dialog_new (_("Save Code"),
1110 GTK_WINDOW (window),
1111 GTK_FILE_CHOOSER_ACTION_SAVE,
1112 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1113 GTK_STOCK_SAVE, GTK_RESPONSE_OK,
1114 NULL);
1115 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
1116 if (priv->popup_code_title) {
1117 gchar *filename = webkit_dom_node_get_text_content (priv->popup_code_title);
1118 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), filename);
1119 g_free (filename);
1120 }
1121 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
1122 g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
1123
1124 res = gtk_dialog_run (GTK_DIALOG (dialog));
1125
1126 if (res == GTK_RESPONSE_OK) {
1127 GError *error = NULL;
1128 GFile *file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
1129 GFileOutputStream *stream = g_file_replace (file, NULL, FALSE,
1130 G_FILE_CREATE_NONE,
1131 NULL,
1132 &error);
1133 if (stream == NULL) {
1134 GtkWidget *dialog = gtk_message_dialog_new (gtk_widget_get_visible (window) ? GTK_WINDOW (window) : NULL,
1135 GTK_DIALOG_DESTROY_WITH_PARENT,
1136 GTK_MESSAGE_ERROR,
1137 GTK_BUTTONS_OK,
1138 "%s", error->message);
1139 gtk_dialog_run (GTK_DIALOG (dialog));
1140 gtk_widget_destroy (dialog);
1141 g_error_free (error);
1142 }
1143 else {
1144 /* FIXME: we should do this async */
1145 GDataOutputStream *datastream = g_data_output_stream_new (G_OUTPUT_STREAM (stream));
1146 if (!g_data_output_stream_put_string (datastream, priv->popup_code_text, NULL, &error)) {
1147 GtkWidget *dialog = gtk_message_dialog_new (gtk_widget_get_visible (window) ? GTK_WINDOW (window) : NULL,
1148 GTK_DIALOG_DESTROY_WITH_PARENT,
1149 GTK_MESSAGE_ERROR,
1150 GTK_BUTTONS_OK,
1151 "%s", error->message);
1152 gtk_dialog_run (GTK_DIALOG (dialog));
1153 gtk_widget_destroy (dialog);
1154 g_error_free (error);
1155 }
1156 g_object_unref (datastream);
1157 }
1158 g_object_unref (file);
1159 }
1160
1161 priv->popup_code_node = NULL;
1162 priv->popup_code_title = NULL;
1163 g_free (priv->popup_code_text);
1164 priv->popup_code_text = NULL;
1165
1166 gtk_widget_destroy (dialog);
1167}
1168
1169static void
1170view_populate_popup (YelpView *view,
1171 GtkMenu *menu,
1172 gpointer data)
1173{
1174 WebKitHitTestResult *result;
1175 WebKitHitTestResultContext context;
1176 GdkEvent *event;
1177 YelpViewPrivate *priv = GET_PRIV (view);
1178 GList *children;
1179 GtkWidget *item;
1180 WebKitDOMNode *node, *cur, *link_node = NULL, *code_node = NULL, *code_title_node = NULL;
1181
1182 children = gtk_container_get_children (GTK_CONTAINER (menu));
1183 while (children) {
1184 gtk_container_remove (GTK_CONTAINER (menu),
1185 GTK_WIDGET (children->data));
1186 children = children->next;
1187 }
1188 g_list_free (children);
1189
1190 event = gtk_get_current_event ();
1191
1192 result = webkit_web_view_get_hit_test_result (WEBKIT_WEB_VIEW (view), (GdkEventButton *) event);
1193 g_object_get (result,
1194 "context", &context,
1195 "inner-node", &node,
1196 NULL);
1197 for (cur = node; cur != NULL; cur = webkit_dom_node_get_parent_node (cur)) {
1198 if (WEBKIT_DOM_IS_ELEMENT (cur) &&
1199 webkit_dom_element_webkit_matches_selector ((WebKitDOMElement *) cur,
1200 "a", NULL))
1201 link_node = cur;
1202
1203 if (WEBKIT_DOM_IS_ELEMENT (cur) &&
1204 webkit_dom_element_webkit_matches_selector ((WebKitDOMElement *) cur,
1205 "div.code", NULL)) {
1206 WebKitDOMNode *title;
1207 code_node = (WebKitDOMNode *)
1208 webkit_dom_element_query_selector ((WebKitDOMElement *) cur,
1209 "pre.contents", NULL);
1210 title = webkit_dom_node_get_parent_node (cur);
1211 if (title != NULL && WEBKIT_DOM_IS_ELEMENT (title) &&
1212 webkit_dom_element_webkit_matches_selector ((WebKitDOMElement *) title,
1213 "div.contents", NULL)) {
1214 title = webkit_dom_node_get_previous_sibling (title);
1215 if (title != NULL && WEBKIT_DOM_IS_ELEMENT (title) &&
1216 webkit_dom_element_webkit_matches_selector ((WebKitDOMElement *) title,
1217 "div.title", NULL)) {
1218 code_title_node = title;
1219 }
1220 }
1221 }
1222 }
1223
1224 if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) {
1225 gchar *uri;
1226 g_object_get (result, "link-uri", &uri, NULL);
1227 g_free (priv->popup_link_uri);
1228 priv->popup_link_uri = uri;
1229
1230 g_free (priv->popup_link_text);
1231 priv->popup_link_text = NULL;
1232 if (link_node != NULL) {
1233 WebKitDOMNode *child;
1234 gchar *tmp;
1235 gint i, tmpi;
1236 gboolean ws;
1237
1238 child = (WebKitDOMNode *)
1239 webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (link_node),
1240 "span.title", NULL);
1241 if (child != NULL)
1242 priv->popup_link_text = webkit_dom_node_get_text_content (child);
1243
1244 if (priv->popup_link_text == NULL)
1245 priv->popup_link_text = webkit_dom_node_get_text_content (link_node);
1246
1247 tmp = g_new0 (gchar, strlen(priv->popup_link_text) + 1);
1248 ws = FALSE;
1249 for (i = 0, tmpi = 0; priv->popup_link_text[i] != '\0'; i++) {
1250 if (priv->popup_link_text[i] == ' ' || priv->popup_link_text[i] == '\n') {
1251 if (!ws) {
1252 tmp[tmpi] = ' ';
1253 tmpi++;
1254 ws = TRUE;
1255 }
1256 }
1257 else {
1258 tmp[tmpi] = priv->popup_link_text[i];
1259 tmpi++;
1260 ws = FALSE;
1261 }
1262 }
1263 tmp[tmpi] = '\0';
1264 g_free (priv->popup_link_text);
1265 priv->popup_link_text = tmp;
1266 }
1267 else {
1268 priv->popup_link_text = g_strdup (uri);
1269 }
1270
1271 if (g_str_has_prefix (priv->popup_link_uri, "mailto:")) {
1272 gchar *label = g_strdup_printf (_("Send email to %s"),
1273 priv->popup_link_uri + 7);
1274 /* Not using a mnemonic because underscores are common in email
1275 * addresses, and we'd have to escape them. There doesn't seem
1276 * to be a quick GTK+ function for this. In practice, there will
1277 * probably only be one menu item for mailto link popups anyway,
1278 * so the mnemonic's not that big of a deal.
1279 */
1280 item = gtk_menu_item_new_with_label (label);
1281 g_signal_connect (item, "activate",
1282 G_CALLBACK (popup_open_link), view);
1283 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1284 g_free (label);
1285 }
1286 else if (g_str_has_prefix (priv->popup_link_uri, "install:")) {
1287 item = gtk_menu_item_new_with_mnemonic (_("_Install Packages"));
1288 g_signal_connect (item, "activate",
1289 G_CALLBACK (popup_open_link), view);
1290 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1291 }
1292 else {
1293 GSList *cur;
1294
1295 item = gtk_menu_item_new_with_mnemonic (_("_Open Link"));
1296 g_signal_connect (item, "activate",
1297 G_CALLBACK (popup_open_link), view);
1298 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1299
1300 if (g_str_has_prefix (priv->popup_link_uri, "http://") ||
1301 g_str_has_prefix (priv->popup_link_uri, "https://")) {
1302 item = gtk_menu_item_new_with_mnemonic (_("_Copy Link Location"));
1303 g_signal_connect (item, "activate",
1304 G_CALLBACK (popup_copy_link), view);
1305 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1306 }
1307 else {
1308 item = gtk_menu_item_new_with_mnemonic (_("Open Link in New _Window"));
1309 g_signal_connect (item, "activate",
1310 G_CALLBACK (popup_open_link_new), view);
1311 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1312 }
1313
1314 for (cur = priv->link_actions; cur != NULL; cur = cur->next) {
1315 gboolean add;
1316 YelpActionEntry *entry = (YelpActionEntry *) cur->data;
1317 if (entry->func == NULL)
1318 add = TRUE;
1319 else
1320 add = (* entry->func) (view, entry->action,
1321 priv->popup_link_uri,
1322 entry->data);
1323 if (add) {
1324 item = gtk_action_create_menu_item (entry->action);
1325 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1326 }
1327 }
1328 }
1329 }
1330 else {
1331 item = gtk_action_create_menu_item (gtk_action_group_get_action (priv->action_group,
1332 "YelpViewGoBack"));
1333 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1334 item = gtk_action_create_menu_item (gtk_action_group_get_action (priv->action_group,
1335 "YelpViewGoForward"));
1336 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1337 }
1338
1339 if ((context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) ||
1340 (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA)) {
1341 /* This doesn't currently work for video with automatic controls,
1342 * because WebKit puts the hit test on the div with the controls.
1343 */
1344 gboolean image = context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE;
1345 gchar *uri;
1346 g_object_get (result, image ? "image-uri" : "media-uri", &uri, NULL);
1347 g_free (priv->popup_image_uri);
1348 if (g_str_has_prefix (uri, BOGUS_URI)) {
1349 priv->popup_image_uri = yelp_uri_locate_file_uri (priv->uri, uri + BOGUS_URI_LEN);
1350 g_free (uri);
1351 }
1352 else {
1353 priv->popup_image_uri = uri;
1354 }
1355
1356 item = gtk_separator_menu_item_new ();
1357 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1358
1359 if (image)
1360 item = gtk_menu_item_new_with_mnemonic (_("_Save Image As..."));
1361 else
1362 item = gtk_menu_item_new_with_mnemonic (_("_Save Video As..."));
1363 g_signal_connect (item, "activate",
1364 G_CALLBACK (popup_save_image), view);
1365 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1366
1367 if (nautilus_sendto) {
1368 if (image)
1369 item = gtk_menu_item_new_with_mnemonic (_("S_end Image To..."));
1370 else
1371 item = gtk_menu_item_new_with_mnemonic (_("S_end Video To..."));
1372 g_signal_connect (item, "activate",
1373 G_CALLBACK (popup_send_image), view);
1374 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1375 }
1376 }
1377
1378 if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_SELECTION) {
1379 item = gtk_separator_menu_item_new ();
1380 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1381
1382 item = gtk_menu_item_new_with_mnemonic (_("_Copy Text"));
1383 g_signal_connect_swapped (item, "activate",
1384 G_CALLBACK (webkit_web_view_copy_clipboard), view);
1385 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1386 }
1387
1388 if (code_node != NULL) {
1389 item = gtk_separator_menu_item_new ();
1390 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1391
1392 priv->popup_code_node = code_node;
1393 priv->popup_code_title = code_title_node;
1394
1395 item = gtk_menu_item_new_with_mnemonic (_("C_opy Code Block"));
1396 g_signal_connect (item, "activate",
1397 G_CALLBACK (popup_copy_code), view);
1398 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1399
1400 item = gtk_menu_item_new_with_mnemonic (_("Save Code _Block As..."));
1401 g_signal_connect (item, "activate",
1402 G_CALLBACK (popup_save_code), view);
1403 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1404 }
1405
1406 g_object_unref (result);
1407 gdk_event_free (event);
1408 gtk_widget_show_all (GTK_WIDGET (menu));
1409}
1410
1411static void
1412view_script_alert (YelpView *view,
1413 WebKitWebFrame *frame,
1414 gchar *message,
1415 gpointer data)
1416{
1417 printf ("\n\n===ALERT===\n%s\n\n", message);
1418}
1419
1420static gboolean
1421view_navigation_requested (WebKitWebView *view,
1422 WebKitWebFrame *frame,
1423 WebKitNetworkRequest *request,
1424 WebKitWebNavigationAction *action,
1425 WebKitWebPolicyDecision *decision,
1426 gpointer user_data)
1427{
1428 const gchar *requri = webkit_network_request_get_uri (request);
1429 YelpViewPrivate *priv = GET_PRIV (view);
1430 YelpUri *uri;
1431
1432 if (priv->bogus_uri &&
1433 g_str_has_prefix (requri, priv->bogus_uri) &&
1434 requri[strlen(priv->bogus_uri)] == '#') {
1435 gchar *tmp = g_strconcat("xref:", requri + strlen(priv->bogus_uri), NULL);
1436 uri = yelp_uri_new_relative (priv->uri, tmp);
1437 g_free (tmp);
1438 }
1439 else if (g_str_has_prefix (requri, BOGUS_URI)) {
1440 uri = yelp_uri_new_relative (priv->uri, requri + BOGUS_URI_LEN);
1441 }
1442 else
1443 uri = yelp_uri_new_relative (priv->uri, requri);
1444
1445 webkit_web_policy_decision_ignore (decision);
1446
1447 yelp_view_load_uri ((YelpView *) view, uri);
1448 g_object_unref (uri);
1449
1450 return TRUE;
1451}
1452
1453static void
1454view_resource_request (WebKitWebView *view,
1455 WebKitWebFrame *frame,
1456 WebKitWebResource *resource,
1457 WebKitNetworkRequest *request,
1458 WebKitNetworkResponse *response,
1459 gpointer user_data)
1460{
1461 YelpViewPrivate *priv = GET_PRIV (view);
1462 const gchar *requri = webkit_network_request_get_uri (request);
1463 gchar last;
1464 gchar *newpath;
1465
1466 if (!g_str_has_prefix (requri, BOGUS_URI))
1467 return;
1468
1469 /* We get this signal for the page itself. Ignore. */
1470 if (g_str_equal (requri, priv->bogus_uri))
1471 return;
1472
1473 newpath = yelp_uri_locate_file_uri (priv->uri, requri + BOGUS_URI_LEN);
1474 if (newpath != NULL) {
1475 webkit_network_request_set_uri (request, newpath);
1476 g_free (newpath);
1477 }
1478 else {
1479 webkit_network_request_set_uri (request, "about:blank");
1480 }
1481}
1482
1483static void
1484view_document_loaded (WebKitWebView *view,
1485 WebKitWebFrame *frame,
1486 gpointer user_data)
1487{
1488 YelpViewPrivate *priv = GET_PRIV (view);
1489 gchar *search_terms;
1490
1491 search_terms = yelp_uri_get_query (priv->uri, "terms");
1492
1493 if (search_terms) {
1494 WebKitDOMDocument *doc;
1495 WebKitDOMElement *body, *link;
1496 doc = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
1497 body = webkit_dom_document_query_selector (doc, "div.body", NULL);
1498 if (body) {
1499 gchar *tmp, *uri, *txt;
1500 link = webkit_dom_document_create_element (doc, "a", NULL);
1501 webkit_dom_element_set_attribute (link, "class", "fullsearch", NULL);
1502 tmp = g_uri_escape_string (search_terms, NULL, FALSE);
1503 uri = g_strconcat ("xref:search=", tmp, NULL);
1504 webkit_dom_element_set_attribute (link, "href", uri, NULL);
1505 g_free (tmp);
1506 g_free (uri);
1507 txt = g_strdup_printf (_("See all search results for “%s”"),
1508 search_terms);
1509 webkit_dom_node_set_text_content (WEBKIT_DOM_NODE (link), txt, NULL);
1510 g_free (txt);
1511 webkit_dom_node_insert_before (WEBKIT_DOM_NODE (body),
1512 WEBKIT_DOM_NODE (link),
1513 webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)),
1514 NULL);
1515 }
1516 g_free (search_terms);
1517 }
1518}
1519
1520static void
1521view_print (GtkAction *action, YelpView *view)
1522{
1523 webkit_web_frame_print (webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (view)));
1524}
1525
1526static void
1527view_history_action (GtkAction *action,
1528 YelpView *view)
1529{
1530 GList *newcur;
1531 YelpViewPrivate *priv = GET_PRIV (view);
1532
1533 if (priv->back_cur == NULL)
1534 return;
1535
1536 if (g_str_equal (gtk_action_get_name (action), "YelpViewGoBack"))
1537 newcur = priv->back_cur->next;
1538 else
1539 newcur = priv->back_cur->prev;
1540
1541 if (newcur == NULL)
1542 return;
1543
1544 priv->back_cur = newcur;
1545
1546 if (priv->back_cur->data == NULL)
1547 return;
1548
1549 priv->back_load = TRUE;
1550 yelp_view_load_uri (view, ((YelpBackEntry *) priv->back_cur->data)->uri);
1551 priv->vadjust = ((YelpBackEntry *) priv->back_cur->data)->vadj;
1552 priv->hadjust = ((YelpBackEntry *) priv->back_cur->data)->hadj;
1553}
1554
1555static void
1556view_navigation_action (GtkAction *action,
1557 YelpView *view)
1558{
1559 YelpViewPrivate *priv = GET_PRIV (view);
1560 gchar *page_id, *new_id, *xref;
1561 YelpUri *new_uri;
1562
1563 page_id = yelp_uri_get_page_id (priv->uri);
1564
1565 if (g_str_equal (gtk_action_get_name (action), "YelpViewGoPrevious"))
1566 new_id = yelp_document_get_prev_id (priv->document, page_id);
1567 else
1568 new_id = yelp_document_get_next_id (priv->document, page_id);
1569
1570 /* Just in case we screwed up somewhere */
1571 if (new_id == NULL) {
1572 gtk_action_set_sensitive (action, FALSE);
1573 return;
1574 }
1575
1576 xref = g_strconcat ("xref:", new_id, NULL);
1577 new_uri = yelp_uri_new_relative (priv->uri, xref);
1578 yelp_view_load_uri (view, new_uri);
1579
1580 g_free (xref);
1581 g_free (new_id);
1582 g_object_unref (new_uri);
1583}
1584
1585static void
1586view_clear_load (YelpView *view)
1587{
1588 YelpViewPrivate *priv = GET_PRIV (view);
1589
1590 if (priv->resolve_uri != NULL) {
1591 if (priv->uri_resolved != 0) {
1592 g_signal_handler_disconnect (priv->resolve_uri, priv->uri_resolved);
1593 priv->uri_resolved = 0;
1594 }
1595 g_object_unref (priv->resolve_uri);
1596 }
1597 priv->resolve_uri = NULL;
1598
1599 if (priv->uri) {
1600 g_object_unref (priv->uri);
1601 priv->uri = NULL;
1602 }
1603
1604 if (priv->cancellable) {
1605 g_cancellable_cancel (priv->cancellable);
1606 priv->cancellable = NULL;
1607 }
1608}
1609
1610static void
1611view_load_page (YelpView *view)
1612{
1613 YelpViewPrivate *priv = GET_PRIV (view);
1614 gchar *page_id;
1615
1616 debug_print (DB_FUNCTION, "entering\n");
1617
1618 g_return_if_fail (priv->cancellable == NULL);
1619
1620 if (priv->document == NULL) {
1621 GError *error;
1622 gchar *docuri;
1623 /* FIXME: and if priv->uri is NULL? */
1624 docuri = yelp_uri_get_document_uri (priv->uri);
1625 /* FIXME: CANT_READ isn't right */
1626 if (docuri) {
1627 error = g_error_new (YELP_ERROR, YELP_ERROR_CANT_READ,
1628 _("Could not load a document for ‘%s’"),
1629 docuri);
1630 g_free (docuri);
1631 }
1632 else {
1633 error = g_error_new (YELP_ERROR, YELP_ERROR_CANT_READ,
1634 _("Could not load a document"));
1635 }
1636 view_show_error_page (view, error);
1637 g_error_free (error);
1638 return;
1639 }
1640
1641 page_id = yelp_uri_get_page_id (priv->uri);
1642 priv->cancellable = g_cancellable_new ();
1643 yelp_document_request_page (priv->document,
1644 page_id,
1645 priv->cancellable,
1646 (YelpDocumentCallback) document_callback,
1647 view);
1648 g_free (page_id);
1649}
1650
1651static void
1652view_show_error_page (YelpView *view,
1653 GError *error)
1654{
1655 YelpViewPrivate *priv = GET_PRIV (view);
1656 static const gchar *errorpage =
1657 "<html><head>"
1658 "<style type='text/css'>"
1659 "body {"
1660 " margin: 1em;"
1661 " color: %s;"
1662 " background-color: %s;"
1663 " }\n"
1664 "p { margin: 1em 0 0 0; }\n"
1665 "div.note {"
1666 " padding: 6px;"
1667 " border-color: %s;"
1668 " border-top: solid 1px;"
1669 " border-bottom: solid 1px;"
1670 " background-color: %s;"
1671 " }\n"
1672 "div.note div.inner {"
1673 " margin: 0; padding: 0;"
1674 " background-image: url(%s);"
1675 " background-position: %s top;"
1676 " background-repeat: no-repeat;"
1677 " min-height: %ipx;"
1678 " }\n"
1679 "div.note div.contents {"
1680 " margin-%s: %ipx;"
1681 " }\n"
1682 "div.note div.title {"
1683 " margin-%s: %ipx;"
1684 " margin-bottom: 0.2em;"
1685 " font-weight: bold;"
1686 " color: %s;"
1687 " }\n"
1688 "a { color: %s; text-decoration: none; }\n"
1689 "</style>"
1690 "</head><body>"
1691 "<div class='note'><div class='inner'>"
1692 "%s<div class='contents'>%s%s</div>"
1693 "</div></div>"
1694 "</body></html>";
1695 YelpSettings *settings = yelp_settings_get_default ();
1696 gchar *page, *title = NULL, *link = NULL, *title_m, *content_beg, *content_end;
1697 gchar *textcolor, *bgcolor, *noteborder, *notebg, *titlecolor, *noteicon, *linkcolor;
1698 gint iconsize;
1699 GParamSpec *spec;
1700 gboolean doc404 = FALSE;
1701 const gchar *left = (gtk_widget_get_direction((GtkWidget *) view) == GTK_TEXT_DIR_RTL) ? "right" : "left";
1702
1703 if (priv->uri && yelp_uri_get_document_type (priv->uri) == YELP_URI_DOCUMENT_TYPE_NOT_FOUND)
1704 doc404 = TRUE;
1705 if (error->domain == YELP_ERROR)
1706 switch (error->code) {
1707 case YELP_ERROR_NOT_FOUND:
1708 if (doc404)
1709 title = _("Document Not Found");
1710 else
1711 title = _("Page Not Found");
1712 break;
1713 case YELP_ERROR_CANT_READ:
1714 title = _("Cannot Read");
1715 break;
1716 default:
1717 break;
1718 }
1719 if (title == NULL)
1720 title = _("Unknown Error");
1721 title_m = g_markup_printf_escaped ("<div class='title'>%s</div>", title);
1722
1723 content_beg = g_markup_printf_escaped ("<p>%s</p>", error->message);
1724 content_end = NULL;
1725 if (doc404) {
1726 gchar *struri = yelp_uri_get_document_uri (priv->uri);
1727 /* do not free */
1728 gchar *pkg = NULL, *scheme = NULL;
1729 if (g_str_has_prefix (struri, "help:")) {
1730 scheme = "help";
1731 pkg = struri + 5;
1732 }
1733 else if (g_str_has_prefix (struri, "ghelp:")) {
1734 scheme = "ghelp";
1735 pkg = struri + 6;
1736 }
1737 g_free (struri);
1738 }
1739
1740 textcolor = yelp_settings_get_color (settings, YELP_SETTINGS_COLOR_TEXT);
1741 bgcolor = yelp_settings_get_color (settings, YELP_SETTINGS_COLOR_BASE);
1742 noteborder = yelp_settings_get_color (settings, YELP_SETTINGS_COLOR_RED_BORDER);
1743 notebg = yelp_settings_get_color (settings, YELP_SETTINGS_COLOR_YELLOW_BASE);
1744 titlecolor = yelp_settings_get_color (settings, YELP_SETTINGS_COLOR_TEXT_LIGHT);
1745 linkcolor = yelp_settings_get_color (settings, YELP_SETTINGS_COLOR_LINK);
1746 noteicon = yelp_settings_get_icon (settings, YELP_SETTINGS_ICON_WARNING);
1747 iconsize = yelp_settings_get_icon_size (settings) + 6;
1748
1749 page = g_strdup_printf (errorpage,
1750 textcolor, bgcolor, noteborder, notebg, noteicon,
1751 left, iconsize, left, iconsize, left, iconsize,
1752 titlecolor, linkcolor, title_m, content_beg,
1753 (content_end != NULL) ? content_end : "");
1754
1755 g_object_set (view, "state", YELP_VIEW_STATE_ERROR, NULL);
1756
1757 if (doc404) {
1758 g_free (priv->root_title);
1759 priv->root_title = g_strdup (title);
1760 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
1761 "root-title");
1762 g_signal_emit_by_name (view, "notify::root-title", spec);
1763 g_free (priv->page_id);
1764 priv->page_id = g_strdup ("index");
1765 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
1766 "page-id");
1767 g_signal_emit_by_name (view, "notify::page-id", spec);
1768 }
1769
1770 g_free (priv->page_title);
1771 priv->page_title = g_strdup (title);
1772 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
1773 "page-title");
1774 g_signal_emit_by_name (view, "notify::page-title", spec);
1775
1776 g_free (priv->page_desc);
1777 priv->page_desc = NULL;
1778 if (priv->uri)
1779 priv->page_desc = yelp_uri_get_canonical_uri (priv->uri);
1780 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
1781 "page-desc");
1782 g_signal_emit_by_name (view, "notify::page-desc", spec);
1783
1784 g_free (priv->page_icon);
1785 priv->page_icon = g_strdup ("dialog-warning");
1786 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
1787 "page-icon");
1788 g_signal_emit_by_name (view, "notify::page-icon", spec);
1789
1790 g_signal_emit (view, signals[LOADED], 0);
1791 g_signal_handler_block (view, priv->navigation_requested);
1792 webkit_web_view_load_string (WEBKIT_WEB_VIEW (view),
1793 page,
1794 "text/html",
1795 "UTF-8",
1796 "file:///error/");
1797 g_signal_handler_unblock (view, priv->navigation_requested);
1798 g_free (title_m);
1799 g_free (content_beg);
1800 if (content_end != NULL)
1801 g_free (content_end);
1802 g_free (page);
1803}
1804
1805
1806static void
1807settings_set_fonts (YelpSettings *settings)
1808{
1809 gchar *family;
1810 gint size;
1811
1812 g_object_set (websettings,
1813 "default-encoding", "utf-8",
1814 "enable-private-browsing", TRUE,
1815 NULL);
1816
1817 family = yelp_settings_get_font_family (settings,
1818 YELP_SETTINGS_FONT_VARIABLE);
1819 size = yelp_settings_get_font_size (settings,
1820 YELP_SETTINGS_FONT_VARIABLE);
1821 g_object_set (websettings,
1822 "default-font-family", family,
1823 "sans-serif-font-family", family,
1824 "default-font-size", size,
1825 NULL);
1826 g_free (family);
1827
1828 family = yelp_settings_get_font_family (settings,
1829 YELP_SETTINGS_FONT_FIXED);
1830 size = yelp_settings_get_font_size (settings,
1831 YELP_SETTINGS_FONT_FIXED);
1832 g_object_set (websettings,
1833 "monospace-font-family", family,
1834 "default-monospace-font-size", size,
1835 NULL);
1836 g_free (family);
1837}
1838
1839static void
1840settings_show_text_cursor (YelpSettings *settings)
1841{
1842 g_object_set (websettings,
1843 "enable-caret-browsing",
1844 yelp_settings_get_show_text_cursor (settings),
1845 NULL);
1846}
1847
1848/******************************************************************************/
1849
1850static void
1851uri_resolved (YelpUri *uri,
1852 YelpView *view)
1853{
1854 YelpViewPrivate *priv = GET_PRIV (view);
1855 YelpDocument *document;
1856 YelpBackEntry *back;
1857 GtkAction *action;
1858 GSList *proxies, *cur;
1859 GError *error = NULL;
1860 gchar *struri;
1861 GParamSpec *spec;
1862
1863 if (yelp_uri_get_document_type (uri) != YELP_URI_DOCUMENT_TYPE_EXTERNAL) {
1864 g_object_ref (uri);
1865 view_clear_load (view);
1866 priv->uri = uri;
1867 }
1868
1869 switch (yelp_uri_get_document_type (uri)) {
1870 case YELP_URI_DOCUMENT_TYPE_EXTERNAL:
1871 g_object_set (view, "state", priv->prevstate, NULL);
1872 struri = yelp_uri_get_canonical_uri (uri);
1873 if (g_str_has_prefix (struri, "install:") ||
1874 g_str_has_prefix (struri, "install-ghelp:") ||
1875 g_str_has_prefix (struri, "install-help:")) {
1876 view_install_uri (view, struri);
1877 }
1878 else {
1879 gboolean result;
1880 g_signal_emit (view, signals[EXTERNAL_URI], 0, uri, &result);
1881 }
1882 g_free (struri);
1883 return;
1884 case YELP_URI_DOCUMENT_TYPE_NOT_FOUND:
1885 struri = yelp_uri_get_canonical_uri (uri);
1886 if (struri != NULL) {
1887 error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
1888 _("The URI ‘%s’ does not point to a valid page."),
1889 struri);
1890 g_free (struri);
1891 }
1892 else {
1893 error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
1894 _("The URI does not point to a valid page."));
1895 }
1896 break;
1897 case YELP_URI_DOCUMENT_TYPE_ERROR:
1898 struri = yelp_uri_get_canonical_uri (uri);
1899 error = g_error_new (YELP_ERROR, YELP_ERROR_PROCESSING,
1900 _("The URI ‘%s’ could not be parsed."),
1901 struri);
1902 g_free (struri);
1903 break;
1904 default:
1905 break;
1906 }
1907
1908 if (error == NULL) {
1909 document = yelp_document_get_for_uri (uri);
1910 if (priv->document)
1911 g_object_unref (priv->document);
1912 priv->document = document;
1913 }
1914 else {
1915 if (priv->document != NULL) {
1916 g_object_unref (priv->document);
1917 priv->document = NULL;
1918 }
1919 }
1920
1921 if (!priv->back_load) {
1922 back = g_new0 (YelpBackEntry, 1);
1923 back->uri = g_object_ref (uri);
1924 while (priv->back_list != priv->back_cur) {
1925 back_entry_free ((YelpBackEntry *) priv->back_list->data);
1926 priv->back_list = g_list_delete_link (priv->back_list, priv->back_list);
1927 }
1928 priv->back_list = g_list_prepend (priv->back_list, back);
1929 priv->back_cur = priv->back_list;
1930 }
1931 priv->back_load = FALSE;
1932
1933 action = gtk_action_group_get_action (priv->action_group, "YelpViewGoBack");
1934 gtk_action_set_sensitive (action, FALSE);
1935 proxies = gtk_action_get_proxies (action);
1936 if (priv->back_cur->next && priv->back_cur->next->data) {
1937 gchar *tooltip = "";
1938 back = priv->back_cur->next->data;
1939
1940 gtk_action_set_sensitive (action, TRUE);
1941 if (back->title && back->desc) {
1942 gchar *color;
1943 color = yelp_settings_get_color (yelp_settings_get_default (),
1944 YELP_SETTINGS_COLOR_TEXT_LIGHT);
1945 tooltip = g_markup_printf_escaped ("<span size='larger'>%s</span>\n<span color='%s'>%s</span>",
1946 back->title, color, back->desc);
1947 g_free (color);
1948 }
1949 else if (back->title)
1950 tooltip = g_markup_printf_escaped ("<span size='larger'>%s</span>",
1951 back->title);
1952 /* Can't seem to use markup on GtkAction tooltip */
1953 for (cur = proxies; cur != NULL; cur = cur->next)
1954 gtk_widget_set_tooltip_markup (GTK_WIDGET (cur->data), tooltip);
1955 }
1956 else {
1957 for (cur = proxies; cur != NULL; cur = cur->next)
1958 gtk_widget_set_tooltip_text (GTK_WIDGET (cur->data), "");
1959 }
1960
1961 action = gtk_action_group_get_action (priv->action_group, "YelpViewGoForward");
1962 gtk_action_set_sensitive (action, FALSE);
1963 proxies = gtk_action_get_proxies (action);
1964 if (priv->back_cur->prev && priv->back_cur->prev->data) {
1965 gchar *tooltip = "";
1966 back = priv->back_cur->prev->data;
1967
1968 gtk_action_set_sensitive (action, TRUE);
1969 if (back->title && back->desc) {
1970 gchar *color;
1971 color = yelp_settings_get_color (yelp_settings_get_default (),
1972 YELP_SETTINGS_COLOR_TEXT_LIGHT);
1973 tooltip = g_markup_printf_escaped ("<span size='larger'>%s</span>\n<span color='%s'>%s</span>",
1974 back->title, color, back->desc);
1975 g_free (color);
1976 }
1977 else if (back->title)
1978 tooltip = g_markup_printf_escaped ("<span size='larger'>%s</span>",
1979 back->title);
1980 /* Can't seem to use markup on GtkAction tooltip */
1981 for (cur = proxies; cur != NULL; cur = cur->next)
1982 gtk_widget_set_tooltip_markup (GTK_WIDGET (cur->data), tooltip);
1983 }
1984 else {
1985 for (cur = proxies; cur != NULL; cur = cur->next)
1986 gtk_widget_set_tooltip_text (GTK_WIDGET (cur->data), "");
1987 }
1988
1989 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
1990 "yelp-uri");
1991 g_signal_emit_by_name (view, "notify::yelp-uri", spec);
1992
1993 g_free (priv->page_id);
1994 priv->page_id = NULL;
1995 if (priv->uri != NULL)
1996 priv->page_id = yelp_uri_get_page_id (priv->uri);
1997 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
1998 "page-id");
1999 g_signal_emit_by_name (view, "notify::page-id", spec);
2000
2001 g_free (priv->root_title);
2002 g_free (priv->page_title);
2003 g_free (priv->page_desc);
2004 g_free (priv->page_icon);
2005 priv->root_title = NULL;
2006 priv->page_title = NULL;
2007 priv->page_desc = NULL;
2008 priv->page_icon = NULL;
2009
2010 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2011 "root-title");
2012 g_signal_emit_by_name (view, "notify::root-title", spec);
2013
2014 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2015 "page-title");
2016 g_signal_emit_by_name (view, "notify::page-title", spec);
2017
2018 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2019 "page-desc");
2020 g_signal_emit_by_name (view, "notify::page-desc", spec);
2021
2022 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2023 "page-icon");
2024 g_signal_emit_by_name (view, "notify::page-icon", spec);
2025
2026 if (error == NULL)
2027 view_load_page (view);
2028 else {
2029 view_show_error_page (view, error);
2030 g_error_free (error);
2031 }
2032}
2033
2034static void
2035document_callback (YelpDocument *document,
2036 YelpDocumentSignal signal,
2037 YelpView *view,
2038 GError *error)
2039{
2040 YelpViewPrivate *priv = GET_PRIV (view);
2041
2042 debug_print (DB_FUNCTION, "entering\n");
2043
2044 if (signal == YELP_DOCUMENT_SIGNAL_INFO) {
2045 gchar *prev_id, *next_id, *real_id;
2046 GtkAction *action;
2047 YelpBackEntry *back = NULL;
2048 GParamSpec *spec;
2049
2050 real_id = yelp_document_get_page_id (document, priv->page_id);
2051 if (priv->page_id && real_id && g_str_equal (real_id, priv->page_id)) {
2052 g_free (real_id);
2053 }
2054 else {
2055 GParamSpec *spec;
2056 g_free (priv->page_id);
2057 priv->page_id = real_id;
2058 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2059 "page-id");
2060 g_signal_emit_by_name (view, "notify::page-id", spec);
2061 }
2062
2063 g_free (priv->root_title);
2064 g_free (priv->page_title);
2065 g_free (priv->page_desc);
2066 g_free (priv->page_icon);
2067
2068 priv->root_title = yelp_document_get_root_title (document, priv->page_id);
2069 priv->page_title = yelp_document_get_page_title (document, priv->page_id);
2070 priv->page_desc = yelp_document_get_page_desc (document, priv->page_id);
2071 priv->page_icon = yelp_document_get_page_icon (document, priv->page_id);
2072
2073 if (priv->back_cur)
2074 back = priv->back_cur->data;
2075 if (back) {
2076 g_free (back->title);
2077 back->title = g_strdup (priv->page_title);
2078 g_free (back->desc);
2079 back->desc = g_strdup (priv->page_desc);
2080 }
2081
2082 prev_id = yelp_document_get_prev_id (document, priv->page_id);
2083 action = gtk_action_group_get_action (priv->action_group, "YelpViewGoPrevious");
2084 gtk_action_set_sensitive (action, prev_id != NULL);
2085 g_free (prev_id);
2086
2087 next_id = yelp_document_get_next_id (document, priv->page_id);
2088 action = gtk_action_group_get_action (priv->action_group, "YelpViewGoNext");
2089 gtk_action_set_sensitive (action, next_id != NULL);
2090 g_free (next_id);
2091
2092 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2093 "root-title");
2094 g_signal_emit_by_name (view, "notify::root-title", spec);
2095
2096 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2097 "page-title");
2098 g_signal_emit_by_name (view, "notify::page-title", spec);
2099
2100 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2101 "page-desc");
2102 g_signal_emit_by_name (view, "notify::page-desc", spec);
2103
2104 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2105 "page-icon");
2106 g_signal_emit_by_name (view, "notify::page-icon", spec);
2107 }
2108 else if (signal == YELP_DOCUMENT_SIGNAL_CONTENTS) {
2109 YelpUriDocumentType doctype;
2110 const gchar *contents;
2111 gchar *mime_type, *page_id, *frag_id, *full_uri;
2112 page_id = yelp_uri_get_page_id (priv->uri);
2113 debug_print (DB_ARG, " document.uri.page_id=\"%s\"\n", page_id);
2114 mime_type = yelp_document_get_mime_type (document, page_id);
2115 contents = yelp_document_read_contents (document, page_id);
2116 frag_id = yelp_uri_get_frag_id (priv->uri);
2117 g_free (priv->bogus_uri);
2118 /* We don't have actual page and frag IDs for DocBook. We just map IDs
2119 of block elements. The result is that we get xref:someid#someid.
2120 If someid is really the page ID, we just drop the frag reference.
2121 Otherwise, normal page views scroll past the link trail.
2122 */
2123 if (frag_id != NULL) {
2124 if (YELP_IS_DOCBOOK_DOCUMENT (document)) {
2125 gchar *real_id = yelp_document_get_page_id (document, page_id);
2126 if (g_str_equal (real_id, frag_id)) {
2127 g_free (frag_id);
2128 frag_id = NULL;
2129 }
2130 g_free (real_id);
2131 }
2132 }
2133 /* We have to give WebKit a URI in a scheme it understands, otherwise we
2134 won't get the resource-request-starting signal. So we can't use the
2135 canonical URI, because it might be something like ghelp. We also have
2136 to give it something unique, because WebKit ignores our load_string
2137 call if the URI isn't different. We could try to construct something
2138 based on actual file locations, but in fact it doesn't matter. So
2139 we just make a bogus URI that's easy to process later.
2140 */
2141 doctype = yelp_uri_get_document_type (priv->uri);
2142 full_uri = yelp_uri_get_canonical_uri (priv->uri);
2143 if (g_str_has_prefix (full_uri, "file:/") &&
2144 (doctype == YELP_URI_DOCUMENT_TYPE_TEXT ||
2145 doctype == YELP_URI_DOCUMENT_TYPE_HTML ||
2146 doctype == YELP_URI_DOCUMENT_TYPE_XHTML )) {
2147 priv->bogus_uri = full_uri;
2148 }
2149 else {
2150 g_free (full_uri);
2151 if (frag_id != NULL)
2152 priv->bogus_uri = g_strdup_printf ("%s%p#%s", BOGUS_URI, priv->uri, frag_id);
2153 else
2154 priv->bogus_uri = g_strdup_printf ("%s%p", BOGUS_URI, priv->uri);
2155 }
2156 g_signal_handler_block (view, priv->navigation_requested);
2157 webkit_web_view_load_string (WEBKIT_WEB_VIEW (view),
2158 contents,
2159 mime_type,
2160 "UTF-8",
2161 priv->bogus_uri);
2162 g_signal_handler_unblock (view, priv->navigation_requested);
2163 g_object_set (view, "state", YELP_VIEW_STATE_LOADED, NULL);
2164
2165 /* If we need to set the GtkAdjustment or trigger the page title
2166 * from what WebKit thinks it is (see comment below), we need to
2167 * let the main loop run through.
2168 */
2169 if (priv->vadjust > 0 || priv->hadjust > 0 || priv->page_title == NULL)
2170 while (g_main_context_pending (NULL)) {
2171 WebKitLoadStatus status;
2172 status = webkit_web_view_get_load_status (WEBKIT_WEB_VIEW (view));
2173 g_main_context_iteration (NULL, FALSE);
2174 /* Sometimes some runaway JavaScript causes there to always
2175 * be pending sources. Break out if the document is loaded.
2176 */
2177 if (status == WEBKIT_LOAD_FINISHED ||
2178 status == WEBKIT_LOAD_FAILED)
2179 break;
2180 }
2181
2182 /* Setting adjustments only work after the page is loaded. These
2183 * are set by view_history_action, and they're reset to 0 after
2184 * each load here.
2185 */
2186 if (priv->vadjust > 0) {
2187 if (priv->vadjustment)
2188 gtk_adjustment_set_value (priv->vadjustment, priv->vadjust);
2189 priv->vadjust = 0;
2190 }
2191 if (priv->hadjust > 0) {
2192 if (priv->hadjustment)
2193 gtk_adjustment_set_value (priv->hadjustment, priv->hadjust);
2194 priv->hadjust = 0;
2195 }
2196
2197 /* If the document didn't give us a page title, get it from WebKit.
2198 * We let the main loop run through so that WebKit gets the title
2199 * set so that we can send notify::page-title before loaded. It
2200 * simplifies things if YelpView consumers can assume the title
2201 * is set before loaded is triggered.
2202 */
2203 if (priv->page_title == NULL) {
2204 GParamSpec *spec;
2205 priv->page_title = g_strdup (webkit_web_view_get_title (WEBKIT_WEB_VIEW (view)));
2206 spec = g_object_class_find_property ((GObjectClass *) YELP_VIEW_GET_CLASS (view),
2207 "page-title");
2208 g_signal_emit_by_name (view, "notify::page-title", spec);
2209 }
2210
2211 g_free (frag_id);
2212 g_free (page_id);
2213 g_free (mime_type);
2214 yelp_document_finish_read (document, contents);
2215 g_signal_emit (view, signals[LOADED], 0);
2216 }
2217 else if (signal == YELP_DOCUMENT_SIGNAL_ERROR) {
2218 view_show_error_page (view, error);
2219 }
2220}
22210
=== removed directory '.pc/git_signal_handler.patch/src'
=== removed file '.pc/git_signal_handler.patch/src/yelp-window.c'
--- .pc/git_signal_handler.patch/src/yelp-window.c 2014-02-27 13:28:05 +0000
+++ .pc/git_signal_handler.patch/src/yelp-window.c 1970-01-01 00:00:00 +0000
@@ -1,1402 +0,0 @@
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * Copyright (C) 2010 Shaun McCance <shaunm@gnome.org>
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 * Author: Shaun McCance <shaunm@gnome.org>
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <math.h>
28
29#include <gdk/gdkkeysyms.h>
30#include <glib/gi18n.h>
31#include <gtk/gtk.h>
32
33#include "yelp-location-entry.h"
34#include "yelp-settings.h"
35#include "yelp-uri.h"
36#include "yelp-view.h"
37
38#include "yelp-application.h"
39#include "yelp-window.h"
40
41static void yelp_window_init (YelpWindow *window);
42static void yelp_window_class_init (YelpWindowClass *klass);
43static void yelp_window_dispose (GObject *object);
44static void yelp_window_finalize (GObject *object);
45static void yelp_window_get_property (GObject *object,
46 guint prop_id,
47 GValue *value,
48 GParamSpec *pspec);
49static void yelp_window_set_property (GObject *object,
50 guint prop_id,
51 const GValue *value,
52 GParamSpec *pspec);
53
54static void window_construct (YelpWindow *window);
55static void window_new (GtkAction *action,
56 YelpWindow *window);
57static gboolean window_map_event (YelpWindow *window,
58 GdkEvent *event,
59 gpointer user_data);
60static gboolean window_configure_event (YelpWindow *window,
61 GdkEventConfigure *event,
62 gpointer user_data);
63static void window_drag_received (YelpWindow *window,
64 GdkDragContext *context,
65 gint x,
66 gint y,
67 GtkSelectionData *data,
68 guint info,
69 guint time,
70 gpointer userdata);
71static gboolean window_resize_signal (YelpWindow *window);
72static void window_close (GtkAction *action,
73 YelpWindow *window);
74static void window_go_all (GtkAction *action,
75 YelpWindow *window);
76static void window_add_bookmark (GtkAction *action,
77 YelpWindow *window);
78static void window_remove_bookmark (GtkAction *action,
79 YelpWindow *window);
80static void window_load_bookmark (GtkAction *action,
81 YelpWindow *window);
82static void window_find_in_page (GtkAction *action,
83 YelpWindow *window);
84static void window_start_search (GtkAction *action,
85 YelpWindow *window);
86static void window_open_location (GtkAction *action,
87 YelpWindow *window);
88static void window_read_later (GtkAction *action,
89 YelpWindow *window);
90static gboolean read_later_clicked (GtkLinkButton *button,
91 YelpWindow *window);
92static void app_read_later_changed (YelpApplication *app,
93 const gchar *doc_uri,
94 YelpWindow *window);
95static void app_bookmarks_changed (YelpApplication *app,
96 const gchar *doc_uri,
97 YelpWindow *window);
98static void window_set_bookmarks (YelpWindow *window,
99 const gchar *doc_uri);
100static void window_set_bookmark_action (YelpWindow *window);
101static gboolean find_entry_focus_out (GtkEntry *entry,
102 GdkEventFocus *event,
103 YelpWindow *window);
104static gboolean find_entry_key_press (GtkEntry *entry,
105 GdkEventKey *event,
106 YelpWindow *window);
107static void find_entry_changed (GtkEntry *entry,
108 YelpWindow *window);
109
110static gboolean entry_focus_in (GtkEntry *entry,
111 GdkEventFocus *event,
112 YelpWindow *window);
113static gboolean entry_focus_out (YelpLocationEntry *entry,
114 GdkEventFocus *event,
115 YelpWindow *window);
116
117static void view_new_window (YelpView *view,
118 YelpUri *uri,
119 YelpWindow *window);
120static void view_loaded (YelpView *view,
121 YelpWindow *window);
122static void view_uri_selected (YelpView *view,
123 GParamSpec *pspec,
124 YelpWindow *window);
125static void view_root_title (YelpView *view,
126 GParamSpec *pspec,
127 YelpWindow *window);
128static gboolean view_is_xref_uri (YelpView *view,
129 GtkAction *action,
130 const gchar *uri,
131 YelpWindow *window);
132
133static void hidden_entry_activate (GtkEntry *entry,
134 YelpWindow *window);
135static void hidden_entry_hide (YelpWindow *window);
136static gboolean hidden_key_press (GtkWidget *widget,
137 GdkEventKey *event,
138 YelpWindow *window);
139
140enum {
141 PROP_0,
142 PROP_APPLICATION
143};
144
145enum {
146 RESIZE_EVENT,
147 LAST_SIGNAL
148};
149
150static guint signals[LAST_SIGNAL] = { 0 };
151
152G_DEFINE_TYPE (YelpWindow, yelp_window, GTK_TYPE_WINDOW);
153#define GET_PRIV(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_WINDOW, YelpWindowPrivate))
154
155static const gchar *YELP_UI =
156 "<ui>"
157 "<menubar>"
158 "<menu action='PageMenu'>"
159 "<menuitem action='NewWindow'/>"
160 "<menuitem action='Find'/>"
161 "<separator/>"
162 "<menuitem action='YelpViewPrint'/>"
163 "<separator/>"
164 "<menuitem action='CloseWindow'/>"
165 "</menu>"
166 "<menu action='ViewMenu'>"
167 "<menuitem action='LargerText'/>"
168 "<menuitem action='SmallerText'/>"
169 "<separator/>"
170 "<menuitem action='ShowTextCursor'/>"
171 "</menu>"
172 "<menu action='GoMenu'>"
173 "<menuitem action='YelpViewGoBack'/>"
174 "<menuitem action='YelpViewGoForward'/>"
175 "<separator/>"
176 "<menuitem action='YelpViewGoPrevious'/>"
177 "<menuitem action='YelpViewGoNext'/>"
178 "<separator/>"
179 "<menuitem action='GoAll'/>"
180 "</menu>"
181 "<menu action='BookmarksMenu'>"
182 "<menuitem action='AddBookmark'/>"
183 "<menuitem action='RemoveBookmark'/>"
184 "<separator/>"
185 "<placeholder name='Bookmarks'/>"
186 "</menu>"
187 "</menubar>"
188 "<accelerator action='Find'/>"
189 "<accelerator action='Search'/>"
190 "<accelerator action='OpenLocation'/>"
191 "</ui>";
192
193typedef struct _YelpWindowPrivate YelpWindowPrivate;
194struct _YelpWindowPrivate {
195 GtkListStore *history;
196 GtkUIManager *ui_manager;
197 GtkActionGroup *action_group;
198 YelpApplication *application;
199
200 gulong bookmarks_changed;
201 gulong read_later_changed;
202
203 /* no refs on these, owned by containers */
204 YelpView *view;
205 GtkWidget *vbox_view;
206 GtkWidget *vbox_full;
207 GtkWidget *hbox;
208 YelpLocationEntry *entry;
209 GtkWidget *hidden_entry;
210 GtkWidget *find_entry;
211 GtkWidget *find_label;
212 GtkWidget *read_later_vbox;
213
214 /* refs because we dynamically add & remove */
215 GtkWidget *find_bar;
216 GtkWidget *align_location;
217 GtkWidget *align_hidden;
218 GtkWidget *read_later;
219
220 gchar *doc_uri;
221
222 GtkActionGroup *bookmark_actions;
223 guint bookmarks_merge_id;
224
225 guint resize_signal;
226 gint width;
227 gint height;
228
229 guint entry_color_animate;
230 gfloat entry_color_step;
231
232 gboolean configured;
233};
234
235static const GtkActionEntry entries[] = {
236 { "PageMenu", NULL, N_("_Page") },
237 { "ViewMenu", NULL, N_("_View") },
238 { "GoMenu", NULL, N_("_Go") },
239 { "BookmarksMenu", NULL, N_("_Bookmarks") },
240
241 { "NewWindow", NULL,
242 N_("_New Window"),
243 "<Control>N",
244 NULL,
245 G_CALLBACK (window_new) },
246 { "CloseWindow", NULL,
247 N_("_Close"),
248 "<Control>W",
249 NULL,
250 G_CALLBACK (window_close) },
251 { "GoAll", NULL,
252 N_("_All Documents"),
253 NULL, NULL,
254 G_CALLBACK (window_go_all) },
255 { "AddBookmark", NULL,
256 N_("_Add Bookmark"),
257 "<Control>D",
258 NULL,
259 G_CALLBACK (window_add_bookmark) },
260 { "RemoveBookmark", NULL,
261 N_("_Remove Bookmark"),
262 NULL, NULL,
263 G_CALLBACK (window_remove_bookmark) },
264 { "Find", NULL,
265 N_("Find in Page..."),
266 "<Control>F",
267 NULL,
268 G_CALLBACK (window_find_in_page) },
269 { "Search", NULL,
270 N_("Search..."),
271 "<Control>S",
272 NULL,
273 G_CALLBACK (window_start_search) },
274 { "OpenLocation", NULL,
275 N_("Open Location"),
276 "<Control>L",
277 NULL,
278 G_CALLBACK (window_open_location) }
279};
280
281static void
282yelp_window_init (YelpWindow *window)
283{
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);
286}
287
288static void
289yelp_window_class_init (YelpWindowClass *klass)
290{
291 GObjectClass *object_class = G_OBJECT_CLASS (klass);
292
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;
297
298 g_object_class_install_property (object_class,
299 PROP_APPLICATION,
300 g_param_spec_object ("application",
301 _("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));
307
308 signals[RESIZE_EVENT] =
309 g_signal_new ("resized",
310 G_OBJECT_CLASS_TYPE (klass),
311 G_SIGNAL_RUN_LAST,
312 0, NULL, NULL,
313 g_cclosure_marshal_VOID__VOID,
314 G_TYPE_NONE, 0);
315
316 g_type_class_add_private (klass, sizeof (YelpWindowPrivate));
317}
318
319static void
320yelp_window_dispose (GObject *object)
321{
322 YelpWindowPrivate *priv = GET_PRIV (object);
323
324 if (priv->history) {
325 g_object_unref (priv->history);
326 priv->history = NULL;
327 }
328
329 if (priv->action_group) {
330 g_object_unref (priv->action_group);
331 priv->action_group = NULL;
332 }
333
334 if (priv->bookmark_actions) {
335 g_object_unref (priv->bookmark_actions);
336 priv->bookmark_actions = NULL;
337 }
338
339 if (priv->read_later_changed) {
340 g_source_remove (priv->read_later_changed);
341 priv->read_later_changed = 0;
342 }
343
344 if (priv->bookmarks_changed) {
345 g_source_remove (priv->bookmarks_changed);
346 priv->bookmarks_changed = 0;
347 }
348
349 if (priv->align_location) {
350 g_object_unref (priv->align_location);
351 priv->align_location = NULL;
352 }
353
354 if (priv->align_hidden) {
355 g_object_unref (priv->align_hidden);
356 priv->align_hidden = NULL;
357 }
358
359 if (priv->find_bar) {
360 g_object_unref (priv->find_bar);
361 priv->find_bar = NULL;
362 }
363
364 if (priv->entry_color_animate != 0) {
365 g_source_remove (priv->entry_color_animate);
366 priv->entry_color_animate = 0;
367 }
368
369 G_OBJECT_CLASS (yelp_window_parent_class)->dispose (object);
370}
371
372static void
373yelp_window_finalize (GObject *object)
374{
375 YelpWindowPrivate *priv = GET_PRIV (object);
376 g_free (priv->doc_uri);
377 G_OBJECT_CLASS (yelp_window_parent_class)->finalize (object);
378}
379
380static void
381yelp_window_get_property (GObject *object,
382 guint prop_id,
383 GValue *value,
384 GParamSpec *pspec)
385{
386 YelpWindowPrivate *priv = GET_PRIV (object);
387 switch (prop_id) {
388 case PROP_APPLICATION:
389 g_value_set_object (value, priv->application);
390 break;
391 default:
392 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
393 break;
394 }
395}
396
397static void
398yelp_window_set_property (GObject *object,
399 guint prop_id,
400 const GValue *value,
401 GParamSpec *pspec)
402{
403 YelpWindowPrivate *priv = GET_PRIV (object);
404 switch (prop_id) {
405 case PROP_APPLICATION:
406 priv->application = g_value_get_object (value);
407 window_construct ((YelpWindow *) object);
408 break;
409 default:
410 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
411 break;
412 }
413}
414
415static void
416window_construct (YelpWindow *window)
417{
418 GtkWidget *scroll;
419 GtkActionGroup *view_actions;
420 GtkAction *action;
421 GtkWidget *vbox, *button, *label;
422 gchar *color, *text;
423 YelpWindowPrivate *priv = GET_PRIV (window);
424
425 gtk_window_set_icon_name (GTK_WINDOW (window), "help-browser");
426
427 priv->view = (YelpView *) yelp_view_new ();
428
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,
433 window);
434 priv->read_later_changed =
435 g_signal_connect (priv->application, "read-later-changed",
436 G_CALLBACK (app_read_later_changed), window);
437
438 priv->vbox_full = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3);
439 gtk_container_add (GTK_CONTAINER (window), priv->vbox_full);
440
441 priv->vbox_view = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
442 gtk_box_pack_start (GTK_BOX (priv->vbox_full), priv->vbox_view, TRUE, TRUE, 0);
443
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),
448 window);
449
450 priv->bookmark_actions = gtk_action_group_new ("BookmarkActions");
451 gtk_action_group_set_translate_func (priv->bookmark_actions, NULL, NULL, NULL);
452
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),
458 2);
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"),
466 FALSE, FALSE, 0);
467
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);
472
473 priv->hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 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);
476
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),
480 button,
481 FALSE, FALSE, 0);
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),
485 button,
486 FALSE, FALSE, 0);
487
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);
494
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),
498 TRUE, TRUE, 0);
499 gtk_container_add (GTK_CONTAINER (priv->align_location), GTK_WIDGET (priv->entry));
500
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));
504
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);
511
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),
517 GTK_SHADOW_IN);
518 gtk_box_pack_start (GTK_BOX (priv->vbox_view), scroll, TRUE, TRUE, 0);
519
520 priv->find_bar = g_object_ref_sink (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6));
521 g_object_set (priv->find_bar,
522 "border-width", 2,
523 "margin-right", 16,
524 NULL);
525
526 label = gtk_label_new (_("Find:"));
527 gtk_box_pack_start (GTK_BOX (priv->find_bar), label, FALSE, FALSE, 6);
528
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);
538
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);
542
543 priv->read_later = g_object_ref_sink (gtk_info_bar_new ());
544 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 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);
551 g_free (color);
552 g_free (text);
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))),
555 vbox,
556 FALSE, FALSE, 0);
557 priv->read_later_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
558 gtk_box_pack_start (GTK_BOX (vbox), priv->read_later_vbox, FALSE, FALSE, 0);
559
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));
569
570 gtk_drag_dest_set (GTK_WIDGET (window),
571 GTK_DEST_DEFAULT_ALL,
572 NULL, 0,
573 GDK_ACTION_COPY);
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);
577}
578
579/******************************************************************************/
580
581YelpWindow *
582yelp_window_new (YelpApplication *app)
583{
584 YelpWindow *window;
585
586 window = (YelpWindow *) g_object_new (YELP_TYPE_WINDOW, "application", app, NULL);
587
588 return window;
589}
590
591void
592yelp_window_load_uri (YelpWindow *window,
593 YelpUri *uri)
594{
595 YelpWindowPrivate *priv = GET_PRIV (window);
596
597 yelp_view_load_uri (priv->view, uri);
598}
599
600YelpUri *
601yelp_window_get_uri (YelpWindow *window)
602{
603 YelpUri *uri;
604 YelpWindowPrivate *priv = GET_PRIV (window);
605 g_object_get (G_OBJECT (priv->view), "yelp-uri", &uri, NULL);
606 return uri;
607}
608
609void
610yelp_window_get_geometry (YelpWindow *window,
611 gint *width,
612 gint *height)
613{
614 YelpWindowPrivate *priv = GET_PRIV (window);
615 *width = priv->width;
616 *height = priv->height;
617}
618
619/******************************************************************************/
620
621static void
622window_new (GtkAction *action, YelpWindow *window)
623{
624 YelpUri *yuri;
625 gchar *uri = NULL;
626 YelpWindowPrivate *priv = GET_PRIV (window);
627
628 g_object_get (priv->view, "yelp-uri", &yuri, NULL);
629 uri = yelp_uri_get_document_uri (yuri);
630
631 yelp_application_new_window (priv->application, uri);
632
633 g_free (uri);
634 g_object_unref (yuri);
635}
636
637static void
638window_drag_received (YelpWindow *window,
639 GdkDragContext *context,
640 gint x,
641 gint y,
642 GtkSelectionData *data,
643 guint info,
644 guint time,
645 gpointer userdata)
646{
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);
652 g_strfreev (uris);
653 gtk_drag_finish (context, TRUE, FALSE, time);
654 }
655 gtk_drag_finish (context, FALSE, FALSE, time);
656}
657
658static gboolean
659window_map_event (YelpWindow *window,
660 GdkEvent *event,
661 gpointer user_data)
662{
663 YelpWindowPrivate *priv = GET_PRIV (window);
664 priv->configured = TRUE;
665 return FALSE;
666}
667
668static gboolean
669window_configure_event (YelpWindow *window,
670 GdkEventConfigure *event,
671 gpointer user_data)
672{
673 YelpWindowPrivate *priv = GET_PRIV (window);
674 gboolean skip = TRUE;
675 if (priv->width != event->width) {
676 skip = FALSE;
677 priv->width = event->width;
678 }
679 if (priv->height != event->height) {
680 skip = FALSE;
681 priv->height = event->height;
682 }
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).
686 */
687 if (!priv->configured || skip)
688 return FALSE;
689
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,
694 window);
695 g_object_set (priv->find_entry, "width-request", 2 * priv->width / 3, NULL);
696 return FALSE;
697}
698
699static gboolean
700window_resize_signal (YelpWindow *window)
701{
702 YelpWindowPrivate *priv = GET_PRIV (window);
703 g_signal_emit (window, signals[RESIZE_EVENT], 0);
704 priv->resize_signal = 0;
705 return FALSE;
706}
707
708static void
709window_close (GtkAction *action, YelpWindow *window)
710{
711 gboolean ret;
712 g_signal_emit_by_name (window, "delete-event", NULL, &ret);
713 gtk_widget_destroy (GTK_WIDGET (window));
714}
715
716static void
717window_go_all (GtkAction *action,
718 YelpWindow *window)
719{
720 YelpWindowPrivate *priv = GET_PRIV (window);
721 yelp_view_load (priv->view, "help-list:");
722}
723
724static void
725window_add_bookmark (GtkAction *action,
726 YelpWindow *window)
727{
728 YelpUri *uri;
729 gchar *doc_uri, *page_id, *icon, *title;
730 YelpWindowPrivate *priv = GET_PRIV (window);
731
732 g_object_get (priv->view,
733 "yelp-uri", &uri,
734 "page-id", &page_id,
735 "page-icon", &icon,
736 "page-title", &title,
737 NULL);
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);
741 g_free (doc_uri);
742 g_free (page_id);
743 g_free (icon);
744 g_free (title);
745 g_object_unref (uri);
746}
747
748static void
749window_remove_bookmark (GtkAction *action,
750 YelpWindow *window)
751{
752 YelpUri *uri;
753 gchar *doc_uri, *page_id;
754 YelpWindowPrivate *priv = GET_PRIV (window);
755
756 g_object_get (priv->view,
757 "yelp-uri", &uri,
758 "page-id", &page_id,
759 NULL);
760 doc_uri = yelp_uri_get_document_uri (uri);
761 yelp_application_remove_bookmark (YELP_BOOKMARKS (priv->application),
762 doc_uri, page_id);
763 g_free (doc_uri);
764 g_free (page_id);
765 g_object_unref (uri);
766}
767static void
768window_load_bookmark (GtkAction *action,
769 YelpWindow *window)
770{
771 YelpUri *base, *uri;
772 gchar *xref;
773 YelpWindowPrivate *priv = GET_PRIV (window);
774
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);
779
780 yelp_view_load_uri (priv->view, uri);
781
782 g_object_unref (base);
783 g_object_unref (uri);
784 g_free (xref);
785}
786
787static void
788app_bookmarks_changed (YelpApplication *app,
789 const gchar *doc_uri,
790 YelpWindow *window)
791{
792 YelpUri *uri;
793 gchar *this_doc_uri;
794 YelpWindowPrivate *priv = GET_PRIV (window);
795
796 g_object_get (priv->view, "yelp-uri", &uri, NULL);
797 this_doc_uri = yelp_uri_get_document_uri (uri);
798
799 if (g_str_equal (this_doc_uri, doc_uri)) {
800 window_set_bookmarks (window, doc_uri);
801 }
802
803 g_free (this_doc_uri);
804 g_object_unref (uri);
805}
806
807typedef struct _YelpMenuEntry YelpMenuEntry;
808struct _YelpMenuEntry {
809 gchar *page_id;
810 gchar *icon;
811 gchar *title;
812};
813
814static gint
815entry_compare (YelpMenuEntry *a, YelpMenuEntry *b)
816{
817 gint ret = yelp_settings_cmp_icons (a->icon, b->icon);
818 if (ret != 0)
819 return ret;
820
821 if (a->title && b->title)
822 return g_utf8_collate (a->title, b->title);
823 else if (b->title == NULL)
824 return -1;
825 else if (a->title == NULL)
826 return 1;
827
828 return 0;
829}
830
831static void
832window_set_bookmarks (YelpWindow *window,
833 const gchar *doc_uri)
834{
835 GVariant *value;
836 GVariantIter *iter;
837 gchar *page_id, *icon, *title;
838 YelpWindowPrivate *priv = GET_PRIV (window);
839 GSList *entries = NULL;
840
841 window_set_bookmark_action (window);
842
843 gtk_ui_manager_remove_ui (priv->ui_manager, priv->bookmarks_merge_id);
844
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);
853 }
854 for ( ; entries != NULL; entries = g_slist_delete_link (entries, entries)) {
855 GSList *cur;
856 GtkAction *bookmark;
857 YelpMenuEntry *entry = (YelpMenuEntry *) entries->data;
858 gchar *action_id = g_strconcat ("LoadBookmark-", entry->page_id, NULL);
859
860 bookmark = gtk_action_group_get_action (priv->bookmark_actions, action_id);
861 if (bookmark) {
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.
866 */
867 g_object_set (bookmark,
868 "label", entry->title,
869 "icon-name", entry->icon,
870 NULL);
871 } else {
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);
877 }
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,
883 FALSE);
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);
888 }
889 g_free (action_id);
890 g_free (entry->icon);
891 g_free (entry);
892 }
893
894 g_variant_iter_free (iter);
895 g_variant_unref (value);
896}
897
898static void
899window_set_bookmark_action (YelpWindow *window)
900{
901 YelpUri *uri = NULL;
902 gchar *doc_uri = NULL, *page_id = NULL;
903 GtkAction *action_add, *action_del;
904 gboolean bookmarked;
905 YelpWindowPrivate *priv = GET_PRIV (window);
906
907 action_add = gtk_action_group_get_action (priv->action_group, "AddBookmark");
908 action_del = gtk_action_group_get_action (priv->action_group, "RemoveBookmark");
909
910 g_object_get (priv->view,
911 "yelp-uri", &uri,
912 "page-id", &page_id,
913 NULL);
914 if (page_id == NULL || uri == NULL) {
915 gtk_action_set_sensitive (action_add, FALSE);
916 gtk_action_set_sensitive (action_del, FALSE);
917 goto done;
918 }
919 doc_uri = yelp_uri_get_document_uri (uri);
920 bookmarked = yelp_application_is_bookmarked (YELP_BOOKMARKS (priv->application),
921 doc_uri, page_id);
922 gtk_action_set_sensitive (action_add, !bookmarked);
923 gtk_action_set_sensitive (action_del, bookmarked);
924
925 done:
926 g_free (page_id);
927 g_free (doc_uri);
928 if (uri)
929 g_object_unref (uri);
930}
931
932static void
933window_start_search (GtkAction *action, YelpWindow *window)
934{
935 YelpWindowPrivate *priv = GET_PRIV (window);
936
937 yelp_location_entry_start_search (priv->entry);
938}
939
940static void
941window_open_location (GtkAction *action, YelpWindow *window)
942{
943 YelpUri *yuri = NULL;
944 gchar *uri = NULL;
945 GdkColor yellow;
946 gchar *color;
947 YelpWindowPrivate *priv = GET_PRIV (window);
948
949 gtk_container_remove (GTK_CONTAINER (priv->hbox),
950 priv->align_location);
951 gtk_box_pack_start (GTK_BOX (priv->hbox),
952 priv->align_hidden,
953 TRUE, TRUE, 0);
954
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);
958
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,
963 GTK_STATE_NORMAL,
964 &yellow);
965 }
966 g_free (color);
967
968 g_object_get (priv->view, "yelp-uri", &yuri, NULL);
969 if (yuri) {
970 uri = yelp_uri_get_canonical_uri (yuri);
971 g_object_unref (yuri);
972 }
973 if (uri) {
974 gchar *c;
975 gtk_entry_set_text (GTK_ENTRY (priv->hidden_entry), uri);
976 c = strchr (uri, ':');
977 if (c)
978 gtk_editable_select_region (GTK_EDITABLE (priv->hidden_entry), c - uri + 1, -1);
979 else
980 gtk_editable_select_region (GTK_EDITABLE (priv->hidden_entry), 5, -1);
981 g_free (uri);
982 }
983}
984
985static void
986read_later_resolved (YelpUri *uri,
987 YelpWindow *window)
988{
989 gchar *fulluri;
990 const gchar *text = (const gchar *) g_object_get_data ((GObject *) uri, "link-text");
991 YelpWindowPrivate *priv = GET_PRIV (window);
992 YelpUri *base;
993 gchar *doc_uri;
994
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);
998
999 yelp_application_add_read_later (priv->application, doc_uri, fulluri, text);
1000
1001 g_object_unref (base);
1002 g_free (doc_uri);
1003 g_free (fulluri);
1004}
1005
1006static void
1007window_read_later (GtkAction *action,
1008 YelpWindow *window)
1009{
1010 YelpWindowPrivate *priv = GET_PRIV (window);
1011 YelpUri *uri;
1012 gchar *text;
1013
1014 uri = yelp_view_get_active_link_uri (priv->view);
1015 text = yelp_view_get_active_link_text (priv->view);
1016
1017 g_object_set_data_full ((GObject *) uri, "link-text", text, g_free);
1018
1019 if (!yelp_uri_is_resolved (uri)) {
1020 g_signal_connect (uri, "resolved",
1021 G_CALLBACK (read_later_resolved),
1022 window);
1023 yelp_uri_resolve (uri);
1024 }
1025 else {
1026 read_later_resolved (uri, window);
1027 }
1028}
1029
1030static gboolean
1031read_later_clicked (GtkLinkButton *button,
1032 YelpWindow *window)
1033{
1034 YelpWindowPrivate *priv = GET_PRIV (window);
1035 YelpUri *base;
1036 gchar *doc_uri;
1037 gchar *fulluri;
1038
1039 fulluri = g_strdup (gtk_link_button_get_uri (button));
1040
1041 g_object_get (priv->view, "yelp-uri", &base, NULL);
1042 doc_uri = yelp_uri_get_document_uri (base);
1043
1044 yelp_application_remove_read_later (priv->application, doc_uri, fulluri);
1045
1046 g_object_unref (base);
1047 g_free (doc_uri);
1048
1049 yelp_view_load (priv->view, fulluri);
1050
1051 g_free (fulluri);
1052 return TRUE;
1053}
1054
1055static void
1056app_read_later_changed (YelpApplication *app,
1057 const gchar *doc_uri,
1058 YelpWindow *window)
1059{
1060 GVariant *value;
1061 GVariantIter *viter;
1062 gchar *uri, *title; /* do not free */
1063 GList *children;
1064 gboolean has_children = FALSE;
1065 YelpWindowPrivate *priv = GET_PRIV (window);
1066
1067 children = gtk_container_get_children (GTK_CONTAINER (priv->read_later_vbox));
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: