2 * -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
5 * Copyright © 2002 Jorn Baayen <jorn@nl.linux.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * $Id: ephy-completion-model.c 7020 2007-05-01 17:13:10Z xan $
26 #include "ephy-completion-model.h"
27 #include "ephy-favicon-cache.h"
28 #include "ephy-node.h"
29 #include "ephy-shell.h"
30 #include "ephy-history.h"
34 static void ephy_completion_model_class_init (EphyCompletionModelClass
*
36 static void ephy_completion_model_init (EphyCompletionModel
* model
);
37 static void ephy_completion_model_tree_model_init (GtkTreeModelIface
* iface
);
38 static void ephy_completion_model_finalize (GObject
*object
);
40 #define EPHY_COMPLETION_MODEL_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_COMPLETION_MODEL, EphyCompletionModelPrivate))
42 struct _EphyCompletionModelPrivate
61 static GObjectClass
*parent_class
= NULL
;
64 ephy_completion_model_get_type (void)
66 static GType type
= 0;
68 if (G_UNLIKELY (type
== 0))
70 const GTypeInfo our_info
= {
71 sizeof (EphyCompletionModelClass
),
74 (GClassInitFunc
) ephy_completion_model_class_init
,
77 sizeof (EphyCompletionModel
),
79 (GInstanceInitFunc
) ephy_completion_model_init
82 const GInterfaceInfo tree_model_info
= {
84 ephy_completion_model_tree_model_init
,
89 type
= g_type_register_static (G_TYPE_OBJECT
,
90 "EphyCompletionModel",
93 g_type_add_interface_static (type
,
102 ephy_completion_model_class_init (EphyCompletionModelClass
* klass
)
104 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
106 object_class
->finalize
= ephy_completion_model_finalize
;
108 parent_class
= g_type_class_peek_parent (klass
);
110 g_type_class_add_private (object_class
,
111 sizeof (EphyCompletionModelPrivate
));
115 ephy_completion_model_finalize (GObject
*object
)
117 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (object
);
119 if (model
->priv
->history
)
120 g_array_free (model
->priv
->history
, TRUE
);
122 if (model
->priv
->bookmarks
)
123 g_array_free (model
->priv
->bookmarks
, TRUE
);
125 G_OBJECT_CLASS (parent_class
)->finalize (object
);
129 page_ids_bsearch_compare (const void *key
, const void *memberp
)
131 return (*(guint64
*) key
) - (*(guint64
*) memberp
);
135 get_page_index (GArray
* pages
, guint64 id
)
140 ptr
= bsearch (&id
, pages
->data
, pages
->len
, sizeof (guint64
),
141 page_ids_bsearch_compare
);
145 index
= (ptr
- pages
->data
) / sizeof (guint64
);
146 if (index
>= pages
->len
)
153 get_path_real (EphyCompletionModel
* model
, GArray
* pages
, guint64 id
)
158 index
= get_page_index (pages
, id
);
163 if (pages
== model
->priv
->bookmarks
)
165 index
+= model
->priv
->history
->len
;
168 retval
= gtk_tree_path_new ();
169 gtk_tree_path_append_index (retval
, index
);
175 iter_from_page (EphyCompletionModel
* model
,
176 GArray
* pages
, guint64 id
, GtkTreeIter
* iter
)
178 /* XXX: we need to free this some place? */
179 HistoryItem
*item
= g_new (HistoryItem
, 1);
182 iter
->stamp
= model
->priv
->stamp
;
183 iter
->user_data
= item
;
184 iter
->user_data2
= pages
;
185 iter
->user_data3
= NULL
;
189 get_pages (EphyCompletionModel
* model
, int *index
)
191 if (*index
>= model
->priv
->history
->len
)
193 *index
= *index
- model
->priv
->history
->len
;
195 if (*index
< model
->priv
->bookmarks
->len
)
197 return model
->priv
->bookmarks
;
206 return model
->priv
->history
;
211 history_page_added_cb (EphyHistory
* history
,
212 guint64 id
, EphyCompletionModel
* model
)
217 iter_from_page (model
, model
->priv
->history
, id
, &iter
);
218 g_array_append_val (model
->priv
->history
, id
);
220 path
= gtk_tree_path_new ();
221 gtk_tree_path_append_index (path
, model
->priv
->history
->len
- 1);
222 gtk_tree_model_row_inserted (GTK_TREE_MODEL (model
), path
, &iter
);
223 gtk_tree_path_free (path
);
227 history_page_removed_cb (EphyHistory
* history
,
228 guint64 id
, EphyCompletionModel
* model
)
233 index
= get_page_index (model
->priv
->history
, id
);
234 g_array_remove_index (model
->priv
->history
, index
);
236 path
= gtk_tree_path_new ();
237 gtk_tree_path_append_index (path
, index
);
238 gtk_tree_model_row_deleted (GTK_TREE_MODEL (model
), path
);
239 gtk_tree_path_free (path
);
243 history_page_updated_cb (EphyHistory
* history
,
244 guint64 id
, EphyCompletionModel
* model
)
249 iter_from_page (model
, model
->priv
->history
, id
, &iter
);
251 path
= get_path_real (model
, model
->priv
->history
, id
);
252 gtk_tree_model_row_changed (GTK_TREE_MODEL (model
), path
, &iter
);
253 gtk_tree_path_free (path
);
257 connect_signals (EphyCompletionModel
* model
, EphyHistory
* history
)
259 g_signal_connect (history
, "page-added",
260 G_CALLBACK (history_page_added_cb
),
262 g_signal_connect (history
, "page-removed",
263 G_CALLBACK (history_page_removed_cb
),
265 g_signal_connect (history
, "page-updated",
266 G_CALLBACK (history_page_updated_cb
),
271 ephy_completion_model_init (EphyCompletionModel
* model
)
273 EphyHistory
*history
;
275 model
->priv
= EPHY_COMPLETION_MODEL_GET_PRIVATE (model
);
276 model
->priv
->stamp
= g_random_int ();
278 history
= EPHY_HISTORY (ephy_embed_shell_get_global_history
280 model
->priv
->history
= ephy_history_get_page_ids (history
);
281 model
->priv
->eh
= history
;
282 connect_signals (model
, model
->priv
->eh
);
287 model
->priv
->bookmarks
= g_array_new (FALSE
, FALSE
, sizeof (guint64
));
292 EphyCompletionModel
*
293 ephy_completion_model_new (void)
295 EphyCompletionModel
*model
;
297 model
= EPHY_COMPLETION_MODEL (g_object_new
298 (EPHY_TYPE_COMPLETION_MODEL
, NULL
));
300 g_return_val_if_fail (model
->priv
!= NULL
, NULL
);
306 ephy_completion_model_get_n_columns (GtkTreeModel
* tree_model
)
312 ephy_completion_model_get_column_type (GtkTreeModel
* tree_model
, int index
)
318 case EPHY_COMPLETION_TEXT_COL
:
319 case EPHY_COMPLETION_ACTION_COL
:
320 case EPHY_COMPLETION_KEYWORDS_COL
:
321 case EPHY_COMPLETION_EXTRA_COL
:
322 type
= G_TYPE_STRING
;
324 case EPHY_COMPLETION_FAVICON_COL
:
325 type
= GDK_TYPE_PIXBUF
;
327 case EPHY_COMPLETION_RELEVANCE_COL
:
336 init_text_col (GValue
* value
, EphyHistoryPageNode
* page
, int group
)
342 case BOOKMARKS_GROUP
:
346 g_object_get (G_OBJECT (page
), "url", &text
, NULL
);
352 g_value_set_string (value
, text
);
356 init_action_col (GValue
* value
, EphyHistoryPageNode
* page
, int group
)
362 case BOOKMARKS_GROUP
:
366 g_object_get (G_OBJECT (page
), "url", &text
, NULL
);
372 g_value_set_string (value
, text
);
376 init_keywords_col (GValue
* value
, EphyHistoryPageNode
* node
, int group
)
378 const char *text
= NULL
;
382 case BOOKMARKS_GROUP
:
391 g_value_set_string (value
, text
);
395 init_favicon_col (GValue
* value
, EphyHistoryPageNode
* page
, int group
)
397 const char *icon_location
;
398 EphyFaviconCache
*cache
;
399 GdkPixbuf
*pixbuf
= NULL
;
401 cache
= EPHY_FAVICON_CACHE
402 (ephy_embed_shell_get_favicon_cache
403 (EPHY_EMBED_SHELL (ephy_shell
)));
407 case BOOKMARKS_GROUP
:
410 g_object_get (G_OBJECT (page
), "favicon-url",
411 &icon_location
, NULL
);
414 icon_location
= NULL
;
419 pixbuf
= ephy_favicon_cache_get (cache
, icon_location
);
422 g_value_take_object (value
, pixbuf
);
426 is_base_address (const char *address
)
433 while (*address
!= '\0')
441 * Base uris has 3 slashes like http://www.gnome.org/
445 return (*address
== '\0');
453 init_relevance_col (GValue
* value
, EphyHistoryPageNode
* page
, int group
)
458 * We have three ordered groups: history's base addresses,
459 * bookmarks, deep history addresses
462 if (group
== BOOKMARKS_GROUP
)
466 else if (group
== HISTORY_GROUP
)
471 g_object_get (G_OBJECT (page
), "url", &url
, "visit-count",
474 visits
= MIN (visits
, (1 << 5) - 1);
476 if (is_base_address (url
))
478 relevance
= visits
<< 10;
484 //g_print("url: %s visits: %u rel: %d\n", url, visits, relevance);
486 g_value_set_int (value
, relevance
);
490 init_url_col (GValue
* value
, EphyHistoryPageNode
* page
, int group
)
492 const char *url
= NULL
;
494 if (group
== BOOKMARKS_GROUP
)
498 else if (group
== HISTORY_GROUP
)
500 g_object_get (G_OBJECT (page
), "url", &url
, NULL
);
506 g_value_set_string (value
, url
);
510 ephy_completion_model_get_value (GtkTreeModel
* tree_model
,
512 int column
, GValue
* value
)
515 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
516 EphyHistoryPageNode
*page
;
519 g_return_if_fail (EPHY_IS_COMPLETION_MODEL (tree_model
));
520 g_return_if_fail (iter
!= NULL
);
521 g_return_if_fail (iter
->stamp
== model
->priv
->stamp
);
523 id
= ((HistoryItem
*) iter
->user_data
)->id
;
524 group
= (iter
->user_data2
== model
->priv
->history
) ?
525 HISTORY_GROUP
: BOOKMARKS_GROUP
;
527 /* XXX: use a better approach to cache the page info */
528 page
= iter
->user_data3
;
531 /* g_debug ("querying page info"); */
532 page
= ephy_history_get_page_by_id (model
->priv
->eh
, id
);
533 iter
->user_data3
= page
;
537 /* g_debug ("using cached page info"); */
542 case EPHY_COMPLETION_EXTRA_COL
:
543 g_value_init (value
, G_TYPE_STRING
);
545 * We set an additional text for the item title only for
546 * history, since we assume that people know the url of
549 if (group
== HISTORY_GROUP
)
552 g_object_get (G_OBJECT (page
), "title", &title
, NULL
);
553 g_value_set_string (value
, title
);
556 case EPHY_COMPLETION_TEXT_COL
:
557 g_value_init (value
, G_TYPE_STRING
);
558 init_text_col (value
, page
, group
);
560 case EPHY_COMPLETION_FAVICON_COL
:
561 g_value_init (value
, GDK_TYPE_PIXBUF
);
562 init_favicon_col (value
, page
, group
);
564 case EPHY_COMPLETION_ACTION_COL
:
565 g_value_init (value
, G_TYPE_STRING
);
566 init_action_col (value
, page
, group
);
568 case EPHY_COMPLETION_KEYWORDS_COL
:
569 g_value_init (value
, G_TYPE_STRING
);
570 init_keywords_col (value
, page
, group
);
572 case EPHY_COMPLETION_RELEVANCE_COL
:
573 g_value_init (value
, G_TYPE_INT
);
574 init_relevance_col (value
, page
, group
);
576 case EPHY_COMPLETION_URL_COL
:
577 g_value_init (value
, G_TYPE_STRING
);
578 init_url_col (value
, page
, group
);
583 static GtkTreeModelFlags
584 ephy_completion_model_get_flags (GtkTreeModel
* tree_model
)
586 return GTK_TREE_MODEL_ITERS_PERSIST
| GTK_TREE_MODEL_LIST_ONLY
;
590 ephy_completion_model_get_iter (GtkTreeModel
* tree_model
,
591 GtkTreeIter
* iter
, GtkTreePath
* path
)
593 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
598 g_return_val_if_fail (EPHY_IS_COMPLETION_MODEL (model
), FALSE
);
599 g_return_val_if_fail (gtk_tree_path_get_depth (path
) == 1, FALSE
);
601 index
= gtk_tree_path_get_indices (path
)[0];
603 pages
= get_pages (model
, &index
);
607 id
= g_array_index (pages
, guint64
, index
);
608 iter_from_page (model
, pages
, id
, iter
);
614 ephy_completion_model_get_path (GtkTreeModel
* tree_model
, GtkTreeIter
* iter
)
616 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
619 g_return_val_if_fail (iter
!= NULL
, NULL
);
620 g_return_val_if_fail (iter
->user_data
!= NULL
, NULL
);
621 g_return_val_if_fail (iter
->user_data2
!= NULL
, NULL
);
622 g_return_val_if_fail (iter
->stamp
== model
->priv
->stamp
, NULL
);
624 id
= ((HistoryItem
*) iter
->user_data
)->id
;
625 return get_path_real (model
, iter
->user_data2
, id
);
629 ephy_completion_model_iter_next (GtkTreeModel
* tree_model
,
632 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
637 g_return_val_if_fail (iter
!= NULL
, FALSE
);
638 g_return_val_if_fail (iter
->user_data
!= NULL
, FALSE
);
639 g_return_val_if_fail (iter
->user_data2
!= NULL
, FALSE
);
640 g_return_val_if_fail (iter
->stamp
== model
->priv
->stamp
, FALSE
);
642 id
= ((HistoryItem
*) iter
->user_data
)->id
;
643 pages
= (GArray
*) iter
->user_data2
;
645 index
= get_page_index (pages
, id
);
650 if (next
>= pages
->len
)
652 if (pages
== model
->priv
->history
653 && model
->priv
->bookmarks
->len
> 0)
655 pages
= model
->priv
->bookmarks
;
664 id
= g_array_index (pages
, guint64
, next
);
666 iter_from_page (model
, pages
, id
, iter
);
672 ephy_completion_model_iter_children (GtkTreeModel
* tree_model
,
673 GtkTreeIter
* iter
, GtkTreeIter
* parent
)
675 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
684 if (model
->priv
->history
->len
> 0)
686 pages
= model
->priv
->history
;
687 id
= g_array_index (pages
, guint64
, 0);
689 else if (model
->priv
->bookmarks
->len
> 0)
691 pages
= model
->priv
->bookmarks
;
692 id
= g_array_index (pages
, guint64
, 0);
699 iter_from_page (model
, pages
, id
, iter
);
705 ephy_completion_model_iter_has_child (GtkTreeModel
* tree_model
,
712 ephy_completion_model_iter_n_children (GtkTreeModel
* tree_model
,
715 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
717 g_return_val_if_fail (EPHY_IS_COMPLETION_MODEL (tree_model
), -1);
721 return model
->priv
->history
->len
+
722 model
->priv
->bookmarks
->len
;
725 g_return_val_if_fail (model
->priv
->stamp
== iter
->stamp
, -1);
731 ephy_completion_model_iter_nth_child (GtkTreeModel
* tree_model
,
733 GtkTreeIter
* parent
, int n
)
735 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
739 g_return_val_if_fail (EPHY_IS_COMPLETION_MODEL (tree_model
), FALSE
);
746 pages
= get_pages (model
, &n
);
750 id
= g_array_index (pages
, guint64
, n
);
751 iter_from_page (model
, pages
, id
, iter
);
757 ephy_completion_model_iter_parent (GtkTreeModel
* tree_model
,
758 GtkTreeIter
* iter
, GtkTreeIter
* child
)
764 ephy_completion_model_tree_model_init (GtkTreeModelIface
* iface
)
766 iface
->get_flags
= ephy_completion_model_get_flags
;
767 iface
->get_iter
= ephy_completion_model_get_iter
;
768 iface
->get_path
= ephy_completion_model_get_path
;
769 iface
->iter_next
= ephy_completion_model_iter_next
;
770 iface
->iter_children
= ephy_completion_model_iter_children
;
771 iface
->iter_has_child
= ephy_completion_model_iter_has_child
;
772 iface
->iter_n_children
= ephy_completion_model_iter_n_children
;
773 iface
->iter_nth_child
= ephy_completion_model_iter_nth_child
;
774 iface
->iter_parent
= ephy_completion_model_iter_parent
;
775 iface
->get_n_columns
= ephy_completion_model_get_n_columns
;
776 iface
->get_column_type
= ephy_completion_model_get_column_type
;
777 iface
->get_value
= ephy_completion_model_get_value
;