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 $
25 #include "ephy-completion-model.h"
26 #include "ephy-history.h"
30 #include "ephy-favicon-cache.h"
31 #include "ephy-shell.h"
33 #include <gdk-pixbuf/gdk-pixdata.h>
36 static void ephy_completion_model_class_init (EphyCompletionModelClass
*
38 static void ephy_completion_model_init (EphyCompletionModel
* model
);
39 static void ephy_completion_model_tree_model_init (GtkTreeModelIface
* iface
);
40 static void ephy_completion_model_finalize (GObject
*object
);
42 #define EPHY_COMPLETION_MODEL_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_COMPLETION_MODEL, EphyCompletionModelPrivate))
44 struct _EphyCompletionModelPrivate
63 static GObjectClass
*parent_class
= NULL
;
66 ephy_completion_model_get_type (void)
68 static GType type
= 0;
70 if (G_UNLIKELY (type
== 0))
72 const GTypeInfo our_info
= {
73 sizeof (EphyCompletionModelClass
),
76 (GClassInitFunc
) ephy_completion_model_class_init
,
79 sizeof (EphyCompletionModel
),
81 (GInstanceInitFunc
) ephy_completion_model_init
84 const GInterfaceInfo tree_model_info
= {
86 ephy_completion_model_tree_model_init
,
91 type
= g_type_register_static (G_TYPE_OBJECT
,
92 "EphyCompletionModel",
95 g_type_add_interface_static (type
,
104 ephy_completion_model_class_init (EphyCompletionModelClass
* klass
)
106 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
108 object_class
->finalize
= ephy_completion_model_finalize
;
110 parent_class
= g_type_class_peek_parent (klass
);
112 g_type_class_add_private (object_class
,
113 sizeof (EphyCompletionModelPrivate
));
117 ephy_completion_model_finalize (GObject
*object
)
119 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (object
);
121 if (model
->priv
->history
)
122 g_array_free (model
->priv
->history
, TRUE
);
124 if (model
->priv
->bookmarks
)
125 g_array_free (model
->priv
->bookmarks
, TRUE
);
127 G_OBJECT_CLASS (parent_class
)->finalize (object
);
131 page_ids_bsearch_compare (const void *key
, const void *memberp
)
133 return (*(guint64
*) key
) - (*(guint64
*) memberp
);
137 get_page_index (GArray
* pages
, guint64 id
)
142 ptr
= bsearch (&id
, pages
->data
, pages
->len
, sizeof (guint64
),
143 page_ids_bsearch_compare
);
147 index
= (ptr
- pages
->data
) / sizeof (guint64
);
148 if (index
>= pages
->len
)
155 get_path_real (EphyCompletionModel
* model
, GArray
* pages
, guint64 id
)
160 index
= get_page_index (pages
, id
);
165 if (pages
== model
->priv
->bookmarks
)
167 index
+= model
->priv
->history
->len
;
170 retval
= gtk_tree_path_new ();
171 gtk_tree_path_append_index (retval
, index
);
177 iter_from_page (EphyCompletionModel
* model
,
178 GArray
* pages
, guint64 id
, GtkTreeIter
* iter
)
180 /* XXX: we need to free this some place? */
181 HistoryItem
*item
= g_new (HistoryItem
, 1);
184 iter
->stamp
= model
->priv
->stamp
;
185 iter
->user_data
= item
;
186 iter
->user_data2
= pages
;
187 iter
->user_data3
= NULL
;
191 get_pages (EphyCompletionModel
* model
, int *index
)
193 if (*index
>= model
->priv
->history
->len
)
195 *index
= *index
- model
->priv
->history
->len
;
197 if (*index
< model
->priv
->bookmarks
->len
)
199 return model
->priv
->bookmarks
;
208 return model
->priv
->history
;
213 history_page_added_cb (EphyHistory
* history
,
214 guint64 id
, EphyCompletionModel
* model
)
220 iter_from_page (model
, model
->priv
->history
, id
, &iter
);
221 gtk_tree_model_get_value (model
, &iter
, EPHY_COMPLETION_URL_COL
, &val
);
222 g_print("added_cb: %llu (%s) - > %d\n", id
, g_value_get_string (&val
), model
->priv
->history
->len
);
223 g_value_unset (&val
);
224 g_array_append_val (model
->priv
->history
, id
);
226 path
= gtk_tree_path_new ();
227 gtk_tree_path_append_index (path
, model
->priv
->history
->len
- 1);
228 gtk_tree_model_row_inserted (GTK_TREE_MODEL (model
), path
, &iter
);
229 gtk_tree_path_free (path
);
233 history_page_removed_cb (EphyHistory
* history
,
234 guint64 id
, EphyCompletionModel
* model
)
242 iter_from_page (model, model->priv->history, id, &iter);
243 gtk_tree_model_get_value (model, &iter, EPHY_COMPLETION_URL_COL, &val);
244 g_print("removed_cb: %llu (%s) - > %d\n", id, g_value_get_string (&val), model->priv->history->len);
245 g_value_unset (&val);
248 g_print("removed_cb: %llu (%d)\n", id
, model
->priv
->history
->len
);
249 index
= get_page_index (model
->priv
->history
, id
);
250 g_array_remove_index (model
->priv
->history
, index
);
252 path
= gtk_tree_path_new ();
253 gtk_tree_path_append_index (path
, index
);
254 gtk_tree_model_row_deleted (GTK_TREE_MODEL (model
), path
);
255 gtk_tree_path_free (path
);
259 history_page_updated_cb (EphyHistory
* history
,
260 guint64 id
, EphyCompletionModel
* model
)
265 g_print("updated_cb: %llu (%d)\n", id
, model
->priv
->history
->len
);
266 iter_from_page (model
, model
->priv
->history
, id
, &iter
);
268 path
= get_path_real (model
, model
->priv
->history
, id
);
269 gtk_tree_model_row_changed (GTK_TREE_MODEL (model
), path
, &iter
);
270 gtk_tree_path_free (path
);
274 connect_signals (EphyCompletionModel
* model
, EphyHistory
* history
)
276 g_signal_connect (history
, "page-added",
277 G_CALLBACK (history_page_added_cb
),
279 g_signal_connect (history
, "page-removed",
280 G_CALLBACK (history_page_removed_cb
),
282 g_signal_connect (history
, "page-updated",
283 G_CALLBACK (history_page_updated_cb
),
288 ephy_completion_model_init (EphyCompletionModel
* model
)
291 model
->priv
= EPHY_COMPLETION_MODEL_GET_PRIVATE (model
);
292 model
->priv
->stamp
= g_random_int ();
295 EphyHistory
*history
;
298 EPHY_HISTORY (ephy_embed_shell_get_global_history
300 model
->priv
->history
= ephy_history_get_page_ids (history
);
301 model
->priv
->eh
= history
;
302 connect_signals (model
, model
->priv
->eh
);
308 model
->priv
->bookmarks
= g_array_new (FALSE
, FALSE
, sizeof (guint64
));
313 EphyCompletionModel
*
315 ephy_completion_model_new (void)
317 ephy_completion_model_new (EphyHistory
* eh
)
320 EphyCompletionModel
*model
;
322 model
= EPHY_COMPLETION_MODEL (g_object_new
323 (EPHY_TYPE_COMPLETION_MODEL
, NULL
));
325 g_return_val_if_fail (model
->priv
!= NULL
, NULL
);
328 model
->priv
->eh
= eh
;
329 model
->priv
->history
= ephy_history_get_page_ids (eh
);
330 connect_signals (model
, model
->priv
->eh
);
337 ephy_completion_model_get_n_columns (GtkTreeModel
* tree_model
)
343 ephy_completion_model_get_column_type (GtkTreeModel
* tree_model
, int index
)
349 case EPHY_COMPLETION_TEXT_COL
:
350 case EPHY_COMPLETION_ACTION_COL
:
351 case EPHY_COMPLETION_KEYWORDS_COL
:
352 case EPHY_COMPLETION_EXTRA_COL
:
353 type
= G_TYPE_STRING
;
355 case EPHY_COMPLETION_FAVICON_COL
:
356 type
= GDK_TYPE_PIXBUF
;
358 case EPHY_COMPLETION_RELEVANCE_COL
:
367 init_text_col (GValue
* value
, EphyHistoryPageNode
* page
, int group
)
373 case BOOKMARKS_GROUP
:
377 g_object_get (G_OBJECT (page
), "url", &text
, NULL
);
383 g_value_set_string (value
, text
);
387 init_action_col (GValue
* value
, EphyHistoryPageNode
* page
, int group
)
393 case BOOKMARKS_GROUP
:
397 g_object_get (G_OBJECT (page
), "url", &text
, NULL
);
403 g_value_set_string (value
, text
);
407 init_keywords_col (GValue
* value
, EphyHistoryPageNode
* node
, int group
)
409 const char *text
= NULL
;
413 case BOOKMARKS_GROUP
:
422 g_value_set_string (value
, text
);
426 init_favicon_col (GValue
* value
, EphyHistoryPageNode
* page
, int group
)
428 const char *icon_location
;
430 EphyFaviconCache
*cache
;
431 GdkPixbuf
*pixbuf
= NULL
;
433 cache
= EPHY_FAVICON_CACHE
434 (ephy_embed_shell_get_favicon_cache
435 (EPHY_EMBED_SHELL (ephy_shell
)));
440 case BOOKMARKS_GROUP
:
443 g_object_get (G_OBJECT (page
), "favicon-url",
444 &icon_location
, NULL
);
447 icon_location
= NULL
;
453 pixbuf
= ephy_favicon_cache_get (cache
, icon_location
);
456 g_value_take_object (value
, pixbuf
);
458 g_value_take_object (value
, NULL
);
460 g_print("init_favicon_col: %s\n", icon_location
);
464 is_base_address (const char *address
)
471 while (*address
!= '\0')
479 * Base uris has 3 slashes like http://www.gnome.org/
483 return (*address
== '\0');
491 init_relevance_col (GValue
* value
, EphyHistoryPageNode
* page
, int group
)
496 * We have three ordered groups: history's base addresses,
497 * bookmarks, deep history addresses
500 if (group
== BOOKMARKS_GROUP
)
504 else if (group
== HISTORY_GROUP
)
509 g_object_get (G_OBJECT (page
), "url", &url
, "visit-count",
512 visits
= MIN (visits
, (1 << 5) - 1);
514 if (is_base_address (url
))
516 relevance
= visits
<< 10;
522 //g_print("url: %s visits: %u rel: %d\n", url, visits, relevance);
524 g_value_set_int (value
, relevance
);
528 init_url_col (GValue
* value
, EphyHistoryPageNode
* page
, int group
)
530 const char *url
= NULL
;
532 if (group
== BOOKMARKS_GROUP
)
536 else if (group
== HISTORY_GROUP
)
538 g_object_get (G_OBJECT (page
), "url", &url
, NULL
);
544 g_value_set_string (value
, url
);
548 ephy_completion_model_get_value (GtkTreeModel
* tree_model
,
550 int column
, GValue
* value
)
553 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
554 EphyHistoryPageNode
*page
;
557 g_return_if_fail (EPHY_IS_COMPLETION_MODEL (tree_model
));
558 g_return_if_fail (iter
!= NULL
);
559 g_return_if_fail (iter
->stamp
== model
->priv
->stamp
);
561 id
= ((HistoryItem
*) iter
->user_data
)->id
;
562 group
= (iter
->user_data2
== model
->priv
->history
) ?
563 HISTORY_GROUP
: BOOKMARKS_GROUP
;
565 /* XXX: use a better approach to cache the page info */
566 page
= iter
->user_data3
;
569 /* g_debug ("querying page info"); */
570 page
= ephy_history_get_page_by_id (model
->priv
->eh
, id
);
571 iter
->user_data3
= page
;
575 /* g_debug ("using cached page info"); */
580 case EPHY_COMPLETION_EXTRA_COL
:
581 g_value_init (value
, G_TYPE_STRING
);
583 * We set an additional text for the item title only for
584 * history, since we assume that people know the url of
587 if (group
== HISTORY_GROUP
)
590 g_object_get (G_OBJECT (page
), "title", &title
, NULL
);
591 g_value_set_string (value
, title
);
594 case EPHY_COMPLETION_TEXT_COL
:
595 g_value_init (value
, G_TYPE_STRING
);
596 init_text_col (value
, page
, group
);
598 case EPHY_COMPLETION_FAVICON_COL
:
599 g_value_init (value
, GDK_TYPE_PIXBUF
);
600 init_favicon_col (value
, page
, group
);
602 case EPHY_COMPLETION_ACTION_COL
:
603 g_value_init (value
, G_TYPE_STRING
);
604 init_action_col (value
, page
, group
);
606 case EPHY_COMPLETION_KEYWORDS_COL
:
607 g_value_init (value
, G_TYPE_STRING
);
608 init_keywords_col (value
, page
, group
);
610 case EPHY_COMPLETION_RELEVANCE_COL
:
611 g_value_init (value
, G_TYPE_INT
);
612 init_relevance_col (value
, page
, group
);
614 case EPHY_COMPLETION_URL_COL
:
615 g_value_init (value
, G_TYPE_STRING
);
616 init_url_col (value
, page
, group
);
621 static GtkTreeModelFlags
622 ephy_completion_model_get_flags (GtkTreeModel
* tree_model
)
624 return GTK_TREE_MODEL_ITERS_PERSIST
| GTK_TREE_MODEL_LIST_ONLY
;
628 ephy_completion_model_get_iter (GtkTreeModel
* tree_model
,
629 GtkTreeIter
* iter
, GtkTreePath
* path
)
631 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
636 g_return_val_if_fail (EPHY_IS_COMPLETION_MODEL (model
), FALSE
);
637 g_return_val_if_fail (gtk_tree_path_get_depth (path
) == 1, FALSE
);
639 index
= gtk_tree_path_get_indices (path
)[0];
641 pages
= get_pages (model
, &index
);
645 id
= g_array_index (pages
, guint64
, index
);
646 iter_from_page (model
, pages
, id
, iter
);
652 ephy_completion_model_get_path (GtkTreeModel
* tree_model
, GtkTreeIter
* iter
)
654 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
657 g_return_val_if_fail (iter
!= NULL
, NULL
);
658 g_return_val_if_fail (iter
->user_data
!= NULL
, NULL
);
659 g_return_val_if_fail (iter
->user_data2
!= NULL
, NULL
);
660 g_return_val_if_fail (iter
->stamp
== model
->priv
->stamp
, NULL
);
662 id
= ((HistoryItem
*) iter
->user_data
)->id
;
663 return get_path_real (model
, iter
->user_data2
, id
);
667 ephy_completion_model_iter_next (GtkTreeModel
* tree_model
,
670 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
675 g_return_val_if_fail (iter
!= NULL
, FALSE
);
676 g_return_val_if_fail (iter
->user_data
!= NULL
, FALSE
);
677 g_return_val_if_fail (iter
->user_data2
!= NULL
, FALSE
);
678 g_return_val_if_fail (iter
->stamp
== model
->priv
->stamp
, FALSE
);
680 id
= ((HistoryItem
*) iter
->user_data
)->id
;
681 pages
= (GArray
*) iter
->user_data2
;
683 index
= get_page_index (pages
, id
);
688 if (next
>= pages
->len
)
690 if (pages
== model
->priv
->history
691 && model
->priv
->bookmarks
->len
> 0)
693 pages
= model
->priv
->bookmarks
;
702 id
= g_array_index (pages
, guint64
, next
);
704 iter_from_page (model
, pages
, id
, iter
);
710 ephy_completion_model_iter_children (GtkTreeModel
* tree_model
,
711 GtkTreeIter
* iter
, GtkTreeIter
* parent
)
713 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
722 if (model
->priv
->history
->len
> 0)
724 pages
= model
->priv
->history
;
725 id
= g_array_index (pages
, guint64
, 0);
727 else if (model
->priv
->bookmarks
->len
> 0)
729 pages
= model
->priv
->bookmarks
;
730 id
= g_array_index (pages
, guint64
, 0);
737 iter_from_page (model
, pages
, id
, iter
);
743 ephy_completion_model_iter_has_child (GtkTreeModel
* tree_model
,
750 ephy_completion_model_iter_n_children (GtkTreeModel
* tree_model
,
753 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
755 g_return_val_if_fail (EPHY_IS_COMPLETION_MODEL (tree_model
), -1);
759 return model
->priv
->history
->len
+
760 model
->priv
->bookmarks
->len
;
763 g_return_val_if_fail (model
->priv
->stamp
== iter
->stamp
, -1);
769 ephy_completion_model_iter_nth_child (GtkTreeModel
* tree_model
,
771 GtkTreeIter
* parent
, int n
)
773 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
777 g_return_val_if_fail (EPHY_IS_COMPLETION_MODEL (tree_model
), FALSE
);
784 pages
= get_pages (model
, &n
);
788 id
= g_array_index (pages
, guint64
, n
);
789 iter_from_page (model
, pages
, id
, iter
);
795 ephy_completion_model_iter_parent (GtkTreeModel
* tree_model
,
796 GtkTreeIter
* iter
, GtkTreeIter
* child
)
802 ephy_completion_model_tree_model_init (GtkTreeModelIface
* iface
)
804 iface
->get_flags
= ephy_completion_model_get_flags
;
805 iface
->get_iter
= ephy_completion_model_get_iter
;
806 iface
->get_path
= ephy_completion_model_get_path
;
807 iface
->iter_next
= ephy_completion_model_iter_next
;
808 iface
->iter_children
= ephy_completion_model_iter_children
;
809 iface
->iter_has_child
= ephy_completion_model_iter_has_child
;
810 iface
->iter_n_children
= ephy_completion_model_iter_n_children
;
811 iface
->iter_nth_child
= ephy_completion_model_iter_nth_child
;
812 iface
->iter_parent
= ephy_completion_model_iter_parent
;
813 iface
->get_n_columns
= ephy_completion_model_get_n_columns
;
814 iface
->get_column_type
= ephy_completion_model_get_column_type
;
815 iface
->get_value
= ephy_completion_model_get_value
;