1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Copyright (C) 2009-2020 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, see <http://www.gnu.org/licenses/>.
17 *
18 * Author: Shaun McCance <shaunm@gnome.org>
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <gdk/gdkkeysyms.h>
26 #include <gtk/gtk.h>
27 #include <glib/gi18n.h>
28
29 #include "yelp-search-entry.h"
30 #include "yelp-marshal.h"
31 #include "yelp-settings.h"
32
33 static void search_entry_constructed (GObject *object);
34 static void search_entry_dispose (GObject *object);
35 static void search_entry_finalize (GObject *object);
36 static void search_entry_get_property (GObject *object,
37 guint prop_id,
38 GValue *value,
39 GParamSpec *pspec);
40 static void search_entry_set_property (GObject *object,
41 guint prop_id,
42 const GValue *value,
43 GParamSpec *pspec);
44
45 /* Signals */
46 static void search_entry_search_activated (YelpSearchEntry *entry);
47 static void search_entry_bookmark_clicked (YelpSearchEntry *entry);
48
49
50 /* Utilities */
51 static void search_entry_set_completion (YelpSearchEntry *entry,
52 GtkTreeModel *model);
53
54
55 /* GtkEntry callbacks */
56 static void entry_activate_cb (GtkEntry *text_entry,
57 gpointer user_data);
58
59 /* GtkEntryCompletion callbacks */
60 static void cell_set_completion_bookmark_icon (GtkCellLayout *layout,
61 GtkCellRenderer *cell,
62 GtkTreeModel *model,
63 GtkTreeIter *iter,
64 YelpSearchEntry *entry);
65 static void cell_set_completion_text_cell (GtkCellLayout *layout,
66 GtkCellRenderer *cell,
67 GtkTreeModel *model,
68 GtkTreeIter *iter,
69 YelpSearchEntry *entry);
70 static gboolean entry_match_func (GtkEntryCompletion *completion,
71 const gchar *key,
72 GtkTreeIter *iter,
73 YelpSearchEntry *entry);
74 static gint entry_completion_sort (GtkTreeModel *model,
75 GtkTreeIter *iter1,
76 GtkTreeIter *iter2,
77 gpointer user_data);
78 static gboolean entry_match_selected (GtkEntryCompletion *completion,
79 GtkTreeModel *model,
80 GtkTreeIter *iter,
81 YelpSearchEntry *entry);
82 /* YelpView callbacks */
83 static void view_loaded (YelpView *view,
84 YelpSearchEntry *entry);
85
86
87 typedef struct _YelpSearchEntryPrivate YelpSearchEntryPrivate;
88 struct _YelpSearchEntryPrivate
89 {
90 YelpView *view;
91 YelpBookmarks *bookmarks;
92 gchar *completion_uri;
93
94 /* do not free below */
95 GtkEntryCompletion *completion;
96 };
97
98 enum {
99 COMPLETION_COL_TITLE,
100 COMPLETION_COL_DESC,
101 COMPLETION_COL_ICON,
102 COMPLETION_COL_PAGE,
103 COMPLETION_COL_FLAGS,
104 COMPLETION_COL_KEYWORDS
105 };
106
107 enum {
108 COMPLETION_FLAG_ACTIVATE_SEARCH = 1<<0
109 };
110
111 enum {
112 SEARCH_ACTIVATED,
113 LAST_SIGNAL
114 };
115
116 enum {
117 PROP_0,
118 PROP_VIEW,
119 PROP_BOOKMARKS
120 };
121
122 static GHashTable *completions;
123
124 static guint search_entry_signals[LAST_SIGNAL] = {0,};
125
G_DEFINE_TYPE_WITH_PRIVATE(YelpSearchEntry,yelp_search_entry,GTK_TYPE_SEARCH_ENTRY)126 G_DEFINE_TYPE_WITH_PRIVATE (YelpSearchEntry, yelp_search_entry, GTK_TYPE_SEARCH_ENTRY)
127
128 static void
129 yelp_search_entry_class_init (YelpSearchEntryClass *klass)
130 {
131 GObjectClass *object_class;
132
133 klass->search_activated = search_entry_search_activated;
134 klass->bookmark_clicked = search_entry_bookmark_clicked;
135
136 object_class = G_OBJECT_CLASS (klass);
137
138 object_class->constructed = search_entry_constructed;
139 object_class->dispose = search_entry_dispose;
140 object_class->finalize = search_entry_finalize;
141 object_class->get_property = search_entry_get_property;
142 object_class->set_property = search_entry_set_property;
143
144 /**
145 * YelpSearchEntry::search-activated
146 * @widget: The #YelpLocationEntry for which the signal was emitted.
147 * @text: The search text.
148 * @user_data: User data set when the handler was connected.
149 *
150 * This signal will be emitted whenever the user activates a search, generally
151 * by pressing <keycap>Enter</keycap> in the embedded text entry while @widget
152 * is in search mode.
153 **/
154 search_entry_signals[SEARCH_ACTIVATED] =
155 g_signal_new ("search-activated",
156 G_OBJECT_CLASS_TYPE (klass),
157 G_SIGNAL_RUN_LAST,
158 G_STRUCT_OFFSET (YelpSearchEntryClass, search_activated),
159 NULL, NULL,
160 g_cclosure_marshal_VOID__STRING,
161 G_TYPE_NONE, 1,
162 G_TYPE_STRING);
163
164 /**
165 * YelpLocationEntry:view
166 *
167 * The YelpView instance that this location entry controls.
168 **/
169 g_object_class_install_property (object_class,
170 PROP_VIEW,
171 g_param_spec_object ("view",
172 "View",
173 "A YelpView instance to control",
174 YELP_TYPE_VIEW,
175 G_PARAM_CONSTRUCT_ONLY |
176 G_PARAM_READWRITE |
177 G_PARAM_STATIC_STRINGS));
178
179 /**
180 * YelpLocationEntry:bookmarks
181 *
182 * An instance of an implementation of YelpBookmarks to provide
183 * bookmark information for this location entry.
184 **/
185 g_object_class_install_property (object_class,
186 PROP_BOOKMARKS,
187 g_param_spec_object ("bookmarks",
188 "Bookmarks",
189 "A YelpBookmarks implementation instance",
190 YELP_TYPE_BOOKMARKS,
191 G_PARAM_CONSTRUCT_ONLY |
192 G_PARAM_READWRITE |
193 G_PARAM_STATIC_STRINGS));
194
195 completions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
196 }
197
198 static void
yelp_search_entry_init(YelpSearchEntry * entry)199 yelp_search_entry_init (YelpSearchEntry *entry)
200 {
201 }
202
203 static void
search_entry_constructed(GObject * object)204 search_entry_constructed (GObject *object)
205 {
206 YelpSearchEntryPrivate *priv =
207 yelp_search_entry_get_instance_private (YELP_SEARCH_ENTRY (object));
208
209 g_signal_connect (object, "activate",
210 G_CALLBACK (entry_activate_cb), object);
211
212 g_signal_connect (priv->view, "loaded", G_CALLBACK (view_loaded), object);
213 }
214
215 static void
search_entry_dispose(GObject * object)216 search_entry_dispose (GObject *object)
217 {
218 YelpSearchEntryPrivate *priv =
219 yelp_search_entry_get_instance_private (YELP_SEARCH_ENTRY (object));
220
221 if (priv->view) {
222 g_object_unref (priv->view);
223 priv->view = NULL;
224 }
225
226 if (priv->bookmarks) {
227 g_object_unref (priv->bookmarks);
228 priv->bookmarks = NULL;
229 }
230
231 G_OBJECT_CLASS (yelp_search_entry_parent_class)->dispose (object);
232 }
233
234 static void
search_entry_finalize(GObject * object)235 search_entry_finalize (GObject *object)
236 {
237 YelpSearchEntryPrivate *priv =
238 yelp_search_entry_get_instance_private (YELP_SEARCH_ENTRY (object));
239
240 g_free (priv->completion_uri);
241
242 G_OBJECT_CLASS (yelp_search_entry_parent_class)->finalize (object);
243 }
244
245 static void
search_entry_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)246 search_entry_get_property (GObject *object,
247 guint prop_id,
248 GValue *value,
249 GParamSpec *pspec)
250 {
251 YelpSearchEntryPrivate *priv =
252 yelp_search_entry_get_instance_private (YELP_SEARCH_ENTRY (object));
253
254 switch (prop_id) {
255 case PROP_VIEW:
256 g_value_set_object (value, priv->view);
257 break;
258 case PROP_BOOKMARKS:
259 g_value_set_object (value, priv->bookmarks);
260 break;
261 default:
262 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
263 break;
264 }
265 }
266
267 static void
search_entry_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)268 search_entry_set_property (GObject *object,
269 guint prop_id,
270 const GValue *value,
271 GParamSpec *pspec)
272 {
273 YelpSearchEntryPrivate *priv =
274 yelp_search_entry_get_instance_private (YELP_SEARCH_ENTRY (object));
275
276 switch (prop_id) {
277 case PROP_VIEW:
278 priv->view = g_value_dup_object (value);
279 break;
280 case PROP_BOOKMARKS:
281 priv->bookmarks = g_value_dup_object (value);
282 break;
283 default:
284 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
285 break;
286 }
287 }
288
289 static void
search_entry_search_activated(YelpSearchEntry * entry)290 search_entry_search_activated (YelpSearchEntry *entry)
291 {
292 YelpUri *base, *uri;
293 YelpSearchEntryPrivate *priv = yelp_search_entry_get_instance_private (entry);
294
295 g_object_get (priv->view, "yelp-uri", &base, NULL);
296 if (base == NULL)
297 return;
298 uri = yelp_uri_new_search (base,
299 gtk_entry_get_text (GTK_ENTRY (entry)));
300 g_object_unref (base);
301 yelp_view_load_uri (priv->view, uri);
302 gtk_widget_grab_focus (GTK_WIDGET (priv->view));
303 }
304
305 static void
search_entry_bookmark_clicked(YelpSearchEntry * entry)306 search_entry_bookmark_clicked (YelpSearchEntry *entry)
307 {
308 YelpUri *uri;
309 gchar *doc_uri, *page_id;
310 YelpSearchEntryPrivate *priv = yelp_search_entry_get_instance_private (entry);
311
312 g_object_get (priv->view,
313 "yelp-uri", &uri,
314 "page-id", &page_id,
315 NULL);
316 doc_uri = yelp_uri_get_document_uri (uri);
317 if (priv->bookmarks && doc_uri && page_id) {
318 if (!yelp_bookmarks_is_bookmarked (priv->bookmarks, doc_uri, page_id)) {
319 gchar *icon, *title;
320 g_object_get (priv->view,
321 "page-icon", &icon,
322 "page-title", &title,
323 NULL);
324 yelp_bookmarks_add_bookmark (priv->bookmarks, doc_uri, page_id, icon, title);
325 g_free (icon);
326 g_free (title);
327 }
328 else {
329 yelp_bookmarks_remove_bookmark (priv->bookmarks, doc_uri, page_id);
330 }
331 }
332 g_free (doc_uri);
333 g_free (page_id);
334 g_object_unref (uri);
335 }
336
337 static void
search_entry_set_completion(YelpSearchEntry * entry,GtkTreeModel * model)338 search_entry_set_completion (YelpSearchEntry *entry,
339 GtkTreeModel *model)
340 {
341 YelpSearchEntryPrivate *priv = yelp_search_entry_get_instance_private (entry);
342 GList *cells;
343 GtkCellRenderer *icon_cell, *bookmark_cell;
344
345 priv->completion = gtk_entry_completion_new ();
346 gtk_entry_completion_set_minimum_key_length (priv->completion, 3);
347 gtk_entry_completion_set_model (priv->completion, model);
348 gtk_entry_completion_set_text_column (priv->completion, COMPLETION_COL_TITLE);
349 gtk_entry_completion_set_match_func (priv->completion,
350 (GtkEntryCompletionMatchFunc) entry_match_func,
351 entry, NULL);
352 g_signal_connect (priv->completion, "match-selected",
353 G_CALLBACK (entry_match_selected), entry);
354
355 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->completion));
356 g_object_set (cells->data, "xpad", 4, NULL);
357 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->completion),
358 GTK_CELL_RENDERER (cells->data),
359 (GtkCellLayoutDataFunc) cell_set_completion_text_cell,
360 entry, NULL);
361 g_object_set (cells->data, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
362 g_list_free (cells);
363
364 icon_cell = gtk_cell_renderer_pixbuf_new ();
365 g_object_set (icon_cell, "yalign", 0.2, NULL);
366 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->completion), icon_cell, FALSE);
367 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->completion), icon_cell, 0);
368 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (priv->completion),
369 icon_cell,
370 "icon-name",
371 COMPLETION_COL_ICON,
372 NULL);
373 if (priv->bookmarks) {
374 bookmark_cell = gtk_cell_renderer_pixbuf_new ();
375 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (priv->completion), bookmark_cell, FALSE);
376 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->completion),
377 bookmark_cell,
378 (GtkCellLayoutDataFunc) cell_set_completion_bookmark_icon,
379 entry, NULL);
380 }
381 gtk_entry_set_completion (GTK_ENTRY (entry), priv->completion);
382 }
383
384 static void
entry_activate_cb(GtkEntry * text_entry,gpointer user_data)385 entry_activate_cb (GtkEntry *text_entry,
386 gpointer user_data)
387 {
388 gchar *text = g_strdup (gtk_entry_get_text (text_entry));
389
390 if (text == NULL || strlen(text) == 0)
391 return;
392
393 g_signal_emit (user_data, search_entry_signals[SEARCH_ACTIVATED], 0, text);
394
395 g_free (text);
396 }
397
398 static void
cell_set_completion_bookmark_icon(GtkCellLayout * layout,GtkCellRenderer * cell,GtkTreeModel * model,GtkTreeIter * iter,YelpSearchEntry * entry)399 cell_set_completion_bookmark_icon (GtkCellLayout *layout,
400 GtkCellRenderer *cell,
401 GtkTreeModel *model,
402 GtkTreeIter *iter,
403 YelpSearchEntry *entry)
404 {
405 YelpSearchEntryPrivate *priv = yelp_search_entry_get_instance_private (entry);
406
407 if (priv->completion_uri) {
408 gchar *page_id = NULL;
409 gtk_tree_model_get (model, iter,
410 COMPLETION_COL_PAGE, &page_id,
411 -1);
412
413 if (page_id && yelp_bookmarks_is_bookmarked (priv->bookmarks,
414 priv->completion_uri, page_id))
415 g_object_set (cell, "icon-name", "user-bookmarks-symbolic", NULL);
416 else
417 g_object_set (cell, "icon-name", NULL, NULL);
418
419 g_free (page_id);
420 }
421 }
422
423 static void
cell_set_completion_text_cell(GtkCellLayout * layout,GtkCellRenderer * cell,GtkTreeModel * model,GtkTreeIter * iter,YelpSearchEntry * entry)424 cell_set_completion_text_cell (GtkCellLayout *layout,
425 GtkCellRenderer *cell,
426 GtkTreeModel *model,
427 GtkTreeIter *iter,
428 YelpSearchEntry *entry)
429 {
430 gchar *title;
431 gint flags;
432
433 gtk_tree_model_get (model, iter, COMPLETION_COL_FLAGS, &flags, -1);
434 if (flags & COMPLETION_FLAG_ACTIVATE_SEARCH) {
435 title = g_strdup_printf (_("Search for “%s”"),
436 gtk_entry_get_text (GTK_ENTRY (entry)));
437 g_object_set (cell, "text", title, NULL);
438 g_free (title);
439 return;
440 }
441
442 gtk_tree_model_get (model, iter,
443 COMPLETION_COL_TITLE, &title,
444 -1);
445 g_object_set (cell, "text", title, NULL);
446 g_free (title);
447 }
448
449 static gboolean
entry_match_func(GtkEntryCompletion * completion,const gchar * key,GtkTreeIter * iter,YelpSearchEntry * entry)450 entry_match_func (GtkEntryCompletion *completion,
451 const gchar *key,
452 GtkTreeIter *iter,
453 YelpSearchEntry *entry)
454 {
455 gint stri;
456 gchar *title, *desc, *keywords, *titlecase = NULL, *desccase = NULL, *keywordscase = NULL;
457 gboolean ret = FALSE;
458 gchar **strs;
459 gint flags;
460 GtkTreeModel *model = gtk_entry_completion_get_model (completion);
461 static GRegex *nonword = NULL;
462
463 if (nonword == NULL)
464 nonword = g_regex_new ("\\W", 0, 0, NULL);
465 if (nonword == NULL)
466 return FALSE;
467
468 gtk_tree_model_get (model, iter, COMPLETION_COL_FLAGS, &flags, -1);
469 if (flags & COMPLETION_FLAG_ACTIVATE_SEARCH)
470 return TRUE;
471
472 gtk_tree_model_get (model, iter,
473 COMPLETION_COL_TITLE, &title,
474 COMPLETION_COL_DESC, &desc,
475 COMPLETION_COL_KEYWORDS, &keywords,
476 -1);
477 if (title) {
478 titlecase = g_utf8_casefold (title, -1);
479 g_free (title);
480 }
481 if (desc) {
482 desccase = g_utf8_casefold (desc, -1);
483 g_free (desc);
484 }
485 if (keywords) {
486 keywordscase = g_utf8_casefold (keywords, -1);
487 g_free (keywords);
488 }
489
490 strs = g_regex_split (nonword, key, 0);
491 ret = TRUE;
492 for (stri = 0; strs[stri]; stri++) {
493 if (!titlecase || !strstr (titlecase, strs[stri])) {
494 if (!desccase || !strstr (desccase, strs[stri])) {
495 if (!keywordscase || !strstr (keywordscase, strs[stri])) {
496 ret = FALSE;
497 break;
498 }
499 }
500 }
501 }
502
503 g_free (titlecase);
504 g_free (desccase);
505 g_strfreev (strs);
506
507 return ret;
508 }
509
510 static gint
entry_completion_sort(GtkTreeModel * model,GtkTreeIter * iter1,GtkTreeIter * iter2,gpointer user_data)511 entry_completion_sort (GtkTreeModel *model,
512 GtkTreeIter *iter1,
513 GtkTreeIter *iter2,
514 gpointer user_data)
515 {
516 gint ret = 0;
517 gint flags1, flags2;
518 gchar *key1, *key2;
519
520 gtk_tree_model_get (model, iter1, COMPLETION_COL_FLAGS, &flags1, -1);
521 gtk_tree_model_get (model, iter2, COMPLETION_COL_FLAGS, &flags2, -1);
522 if (flags1 & COMPLETION_FLAG_ACTIVATE_SEARCH)
523 return 1;
524 else if (flags2 & COMPLETION_FLAG_ACTIVATE_SEARCH)
525 return -1;
526
527 gtk_tree_model_get (model, iter1, COMPLETION_COL_ICON, &key1, -1);
528 gtk_tree_model_get (model, iter2, COMPLETION_COL_ICON, &key2, -1);
529 ret = yelp_settings_cmp_icons (key1, key2);
530 g_free (key1);
531 g_free (key2);
532
533 if (ret)
534 return ret;
535
536 gtk_tree_model_get (model, iter1, COMPLETION_COL_TITLE, &key1, -1);
537 gtk_tree_model_get (model, iter2, COMPLETION_COL_TITLE, &key2, -1);
538 if (key1 && key2)
539 ret = g_utf8_collate (key1, key2);
540 else if (key2 == NULL)
541 return -1;
542 else if (key1 == NULL)
543 return 1;
544 g_free (key1);
545 g_free (key2);
546
547 return ret;
548 }
549
550 static gboolean
entry_match_selected(GtkEntryCompletion * completion,GtkTreeModel * model,GtkTreeIter * iter,YelpSearchEntry * entry)551 entry_match_selected (GtkEntryCompletion *completion,
552 GtkTreeModel *model,
553 GtkTreeIter *iter,
554 YelpSearchEntry *entry)
555 {
556 YelpUri *base, *uri;
557 gchar *page, *xref;
558 gint flags;
559 YelpSearchEntryPrivate *priv = yelp_search_entry_get_instance_private (entry);
560
561 gtk_tree_model_get (model, iter, COMPLETION_COL_FLAGS, &flags, -1);
562 if (flags & COMPLETION_FLAG_ACTIVATE_SEARCH) {
563 entry_activate_cb (GTK_ENTRY (entry), entry);
564 return TRUE;
565 }
566
567 g_object_get (priv->view, "yelp-uri", &base, NULL);
568 gtk_tree_model_get (model, iter, COMPLETION_COL_PAGE, &page, -1);
569
570 xref = g_strconcat ("xref:", page, NULL);
571 uri = yelp_uri_new_relative (base, xref);
572
573 yelp_view_load_uri (priv->view, uri);
574
575 g_free (page);
576 g_free (xref);
577 g_object_unref (uri);
578 g_object_unref (base);
579
580 gtk_widget_grab_focus (GTK_WIDGET (priv->view));
581 return TRUE;
582 }
583
584 static void
view_loaded(YelpView * view,YelpSearchEntry * entry)585 view_loaded (YelpView *view,
586 YelpSearchEntry *entry)
587 {
588 gchar **ids;
589 gint i;
590 GtkTreeIter iter;
591 YelpUri *uri;
592 gchar *doc_uri;
593 GtkTreeModel *completion;
594 YelpSearchEntryPrivate *priv = yelp_search_entry_get_instance_private (entry);
595 YelpDocument *document = yelp_view_get_document (view);
596
597 g_object_get (view, "yelp-uri", &uri, NULL);
598 doc_uri = yelp_uri_get_document_uri (uri);
599
600 if ((priv->completion_uri == NULL) ||
601 !g_str_equal (doc_uri, priv->completion_uri)) {
602 completion = (GtkTreeModel *) g_hash_table_lookup (completions, doc_uri);
603 if (completion == NULL) {
604 GtkListStore *base = gtk_list_store_new (6,
605 G_TYPE_STRING, /* title */
606 G_TYPE_STRING, /* desc */
607 G_TYPE_STRING, /* icon */
608 G_TYPE_STRING, /* uri */
609 G_TYPE_INT, /* flags */
610 G_TYPE_STRING /* keywords */
611 );
612 completion = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (base));
613 gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (completion),
614 entry_completion_sort,
615 NULL, NULL);
616 g_hash_table_insert (completions, g_strdup (doc_uri), completion);
617 if (document != NULL) {
618 ids = yelp_document_list_page_ids (document);
619 for (i = 0; ids[i]; i++) {
620 gchar *title, *desc, *icon, *keywords;
621 gtk_list_store_insert (GTK_LIST_STORE (base), &iter, 0);
622 title = yelp_document_get_page_title (document, ids[i]);
623 desc = yelp_document_get_page_desc (document, ids[i]);
624 icon = yelp_document_get_page_icon (document, ids[i]);
625 keywords = yelp_document_get_page_keywords (document, ids[i]);
626 gtk_list_store_set (base, &iter,
627 COMPLETION_COL_TITLE, title,
628 COMPLETION_COL_DESC, desc,
629 COMPLETION_COL_ICON, icon,
630 COMPLETION_COL_KEYWORDS, keywords,
631 COMPLETION_COL_PAGE, ids[i],
632 -1);
633 g_free (icon);
634 g_free (desc);
635 g_free (title);
636 }
637 g_strfreev (ids);
638 gtk_list_store_insert (GTK_LIST_STORE (base), &iter, 0);
639 gtk_list_store_set (base, &iter,
640 COMPLETION_COL_ICON, "edit-find-symbolic",
641 COMPLETION_COL_FLAGS, COMPLETION_FLAG_ACTIVATE_SEARCH,
642 -1);
643 }
644 g_object_unref (base);
645 }
646 g_free (priv->completion_uri);
647 priv->completion_uri = doc_uri;
648 search_entry_set_completion (entry, completion);
649 }
650
651 g_object_unref (uri);
652 }
653
654 /**
655 * yelp_search_entry_new:
656 * @view: A #YelpView.
657 *
658 * Creates a new #YelpSearchEntry widget to control @view.
659 *
660 * Returns: A new #YelpSearchEntry.
661 **/
662 GtkWidget*
yelp_search_entry_new(YelpView * view,YelpBookmarks * bookmarks)663 yelp_search_entry_new (YelpView *view,
664 YelpBookmarks *bookmarks)
665 {
666 GtkWidget *ret;
667 g_return_val_if_fail (YELP_IS_VIEW (view), NULL);
668
669 ret = GTK_WIDGET (g_object_new (YELP_TYPE_SEARCH_ENTRY,
670 "view", view,
671 "bookmarks", bookmarks,
672 NULL));
673
674 return ret;
675 }
676