1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright © 2002 Jorn Baayen <jorn@nl.linux.org>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
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
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * $Id: ephy-completion-model.c 7020 2007-05-01 17:13:10Z xan $
24 #include "ephy-completion-model.h"
25 #include "ephy-favicon-cache.h"
26 #include "ephy-node.h"
27 #include "ephy-shell.h"
28 #include "ephy-history.h"
30 static void ephy_completion_model_class_init (EphyCompletionModelClass
*klass
);
31 static void ephy_completion_model_init (EphyCompletionModel
*model
);
32 static void ephy_completion_model_tree_model_init (GtkTreeModelIface
*iface
);
34 #define EPHY_COMPLETION_MODEL_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_COMPLETION_MODEL, EphyCompletionModelPrivate))
36 struct _EphyCompletionModelPrivate
49 static GObjectClass
*parent_class
= NULL
;
51 #define ITER_GET_ID(iter) (GPOINTER_TO_UINT((iter)->user_data))
54 ephy_completion_model_get_type (void)
56 static GType type
= 0;
58 if (G_UNLIKELY (type
== 0))
60 const GTypeInfo our_info
=
62 sizeof (EphyCompletionModelClass
),
65 (GClassInitFunc
) ephy_completion_model_class_init
,
68 sizeof (EphyCompletionModel
),
70 (GInstanceInitFunc
) ephy_completion_model_init
73 const GInterfaceInfo tree_model_info
=
75 (GInterfaceInitFunc
) ephy_completion_model_tree_model_init
,
80 type
= g_type_register_static (G_TYPE_OBJECT
,
81 "EphyCompletionModel",
84 g_type_add_interface_static (type
,
93 ephy_completion_model_class_init (EphyCompletionModelClass
*klass
)
95 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
97 parent_class
= g_type_class_peek_parent (klass
);
99 g_type_class_add_private (object_class
, sizeof (EphyCompletionModelPrivate
));
103 page_ids_bsearch_compare (const void *key
, const void *memberp
)
105 guint member
= *(guint
*)memberp
;
106 return GPOINTER_TO_UINT(key
) - member
;
110 get_page_index (GArray
*pages
, guint id
)
115 ptr
= bsearch (GUINT_TO_POINTER(id
), pages
->data
, pages
->len
,
116 sizeof (guint
), page_ids_bsearch_compare
);
121 index
= (ptr
- pages
->data
) / sizeof (guint
);
122 if (index
> pages
->len
)
129 get_path_real (EphyCompletionModel
*model
,
136 retval
= gtk_tree_path_new ();
138 index
= get_page_index(pages
, id
);
143 if (pages
== model
->priv
->bookmarks
)
145 index
+= model
->priv
->history
->len
;
148 gtk_tree_path_append_index (retval
, index
);
154 iter_from_page (EphyCompletionModel
*model
,
159 iter
->stamp
= model
->priv
->stamp
;
160 iter
->user_data
= GUINT_TO_POINTER (id
);
161 iter
->user_data2
= pages
;
165 get_pages (EphyCompletionModel
*model
, int *index
)
168 if (*index
>= model
->priv
->history
->len
)
170 *index
= *index
- model
->priv
->history
->len
;
172 if (*index
< model
->priv
->bookmarks
->len
)
174 return model
->priv
->bookmarks
;
183 return model
->priv
->history
;
189 root_child_removed_cb (EphyNode
*node
,
192 EphyCompletionModel
*model
)
197 path
= gtk_tree_path_new ();
200 if (node
== model
->priv
->bookmarks
)
202 index
+= ephy_node_get_n_children (model
->priv
->history
);
204 gtk_tree_path_append_index (path
, index
);
206 gtk_tree_model_row_deleted (GTK_TREE_MODEL (model
), path
);
207 gtk_tree_path_free (path
);
211 root_child_added_cb (EphyNode
*node
,
213 EphyCompletionModel
*model
)
218 node_iter_from_node (model
, node
, child
, &iter
);
220 path
= get_path_real (model
, node
, child
);
221 gtk_tree_model_row_inserted (GTK_TREE_MODEL (model
), path
, &iter
);
222 gtk_tree_path_free (path
);
226 root_child_changed_cb (EphyNode
*node
,
229 EphyCompletionModel
*model
)
234 node_iter_from_node (model
, node
, child
, &iter
);
236 path
= get_path_real (model
, node
, child
);
237 gtk_tree_model_row_changed (GTK_TREE_MODEL (model
), path
, &iter
);
238 gtk_tree_path_free (path
);
242 connect_signals (EphyCompletionModel
*model
, EphyNode
*root
)
244 ephy_node_signal_connect_object (root
,
245 EPHY_NODE_CHILD_ADDED
,
246 (EphyNodeCallback
)root_child_added_cb
,
248 ephy_node_signal_connect_object (root
,
249 EPHY_NODE_CHILD_REMOVED
,
250 (EphyNodeCallback
)root_child_removed_cb
,
252 ephy_node_signal_connect_object (root
,
253 EPHY_NODE_CHILD_CHANGED
,
254 (EphyNodeCallback
)root_child_changed_cb
,
259 ephy_completion_model_init (EphyCompletionModel
*model
)
261 EphyHistory
*history
;
263 model
->priv
= EPHY_COMPLETION_MODEL_GET_PRIVATE (model
);
264 model
->priv
->stamp
= g_random_int ();
266 history
= EPHY_HISTORY (ephy_embed_shell_get_global_history (embed_shell
));
267 model
->priv
->history_ids
= ephy_history_get_page_ids (history
);
269 /* XXX: Implement signals in EphyHistory
270 connect_signals (model, model->priv->history);
276 EphyCompletionModel
*
277 ephy_completion_model_new (void)
279 EphyCompletionModel
*model
;
281 model
= EPHY_COMPLETION_MODEL (g_object_new (EPHY_TYPE_COMPLETION_MODEL
,
284 g_return_val_if_fail (model
->priv
!= NULL
, NULL
);
290 ephy_completion_model_get_n_columns (GtkTreeModel
*tree_model
)
296 ephy_completion_model_get_column_type (GtkTreeModel
*tree_model
,
303 case EPHY_COMPLETION_TEXT_COL
:
304 case EPHY_COMPLETION_ACTION_COL
:
305 case EPHY_COMPLETION_KEYWORDS_COL
:
306 case EPHY_COMPLETION_EXTRA_COL
:
307 type
= G_TYPE_STRING
;
309 case EPHY_COMPLETION_FAVICON_COL
:
310 type
= GDK_TYPE_PIXBUF
;
312 case EPHY_COMPLETION_RELEVANCE_COL
:
321 init_text_col (GValue
*value
, EphyNode
*node
, int group
)
327 case BOOKMARKS_GROUP
:
328 text
= ephy_node_get_property_string
329 (node
, EPHY_NODE_BMK_PROP_TITLE
);
332 text
= ephy_node_get_property_string
333 (node
, EPHY_NODE_PAGE_PROP_LOCATION
);
340 g_value_set_string (value
, text
);
344 init_action_col (GValue
*value
, EphyNode
*node
, int group
)
350 case BOOKMARKS_GROUP
:
351 text
= ephy_node_get_property_string
352 (node
, EPHY_NODE_BMK_PROP_LOCATION
);
355 text
= ephy_node_get_property_string
356 (node
, EPHY_NODE_PAGE_PROP_LOCATION
);
362 g_value_set_string (value
, text
);
366 init_keywords_col (GValue
*value
, EphyNode
*node
, int group
)
368 const char *text
= NULL
;
372 case BOOKMARKS_GROUP
:
373 text
= ephy_node_get_property_string
374 (node
, EPHY_NODE_BMK_PROP_KEYWORDS
);
383 g_value_set_string (value
, text
);
387 init_favicon_col (GValue
*value
, EphyNode
*node
, int group
)
389 EphyFaviconCache
*cache
;
390 const char *icon_location
;
391 GdkPixbuf
*pixbuf
= NULL
;
393 cache
= EPHY_FAVICON_CACHE
394 (ephy_embed_shell_get_favicon_cache (EPHY_EMBED_SHELL (ephy_shell
)));
398 case BOOKMARKS_GROUP
:
399 icon_location
= ephy_node_get_property_string
400 (node
, EPHY_NODE_BMK_PROP_ICON
);
403 icon_location
= ephy_node_get_property_string
404 (node
, EPHY_NODE_PAGE_PROP_ICON
);
407 icon_location
= NULL
;
412 pixbuf
= ephy_favicon_cache_get (cache
, icon_location
);
415 g_value_take_object (value
, pixbuf
);
419 is_base_address (const char *address
)
423 if (address
== NULL
) return FALSE
;
425 while (*address
!= '\0')
427 if (*address
== '/') slashes
++;
431 /* Base uris has 3 slashes like http://www.gnome.org/ */
434 return (*address
== '\0');
442 init_relevance_col (GValue
*value
, EphyNode
*node
, int group
)
446 /* We have three ordered groups: history's base
447 addresses, bookmarks, deep history addresses */
449 if (group
== BOOKMARKS_GROUP
)
453 else if (group
== HISTORY_GROUP
)
458 visits
= ephy_node_get_property_int
459 (node
, EPHY_NODE_PAGE_PROP_VISITS
);
460 address
= ephy_node_get_property_string
461 (node
, EPHY_NODE_PAGE_PROP_LOCATION
);
463 visits
= MIN (visits
, (1 << 5) - 1);
465 if (is_base_address (address
))
467 relevance
= visits
<< 10;
475 g_value_set_int (value
, relevance
);
479 init_url_col (GValue
*value
, EphyNode
*node
, int group
)
481 const char *url
= NULL
;
483 if (group
== BOOKMARKS_GROUP
)
485 url
= ephy_node_get_property_string
486 (node
, EPHY_NODE_BMK_PROP_LOCATION
);
488 else if (group
== HISTORY_GROUP
)
490 url
= ephy_node_get_property_string
491 (node
, EPHY_NODE_PAGE_PROP_LOCATION
);
498 g_value_set_string (value
, url
);
502 ephy_completion_model_get_value (GtkTreeModel
*tree_model
,
508 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
511 g_return_if_fail (EPHY_IS_COMPLETION_MODEL (tree_model
));
512 g_return_if_fail (iter
!= NULL
);
513 g_return_if_fail (iter
->stamp
== model
->priv
->stamp
);
515 node
= iter
->user_data
;
516 group
= (iter
->user_data2
== model
->priv
->history
) ?
517 HISTORY_GROUP
: BOOKMARKS_GROUP
;
521 case EPHY_COMPLETION_EXTRA_COL
:
522 g_value_init (value
, G_TYPE_STRING
);
523 /* We set an additional text for the item title only for history, since we assume that people know the url of their bookmarks */
524 if (group
== HISTORY_GROUP
)
527 text
= ephy_node_get_property_string
528 (node
, EPHY_NODE_PAGE_PROP_TITLE
);
529 g_value_set_string (value
, text
);
532 case EPHY_COMPLETION_TEXT_COL
:
533 g_value_init (value
, G_TYPE_STRING
);
534 init_text_col (value
, node
, group
);
536 case EPHY_COMPLETION_FAVICON_COL
:
537 g_value_init (value
, GDK_TYPE_PIXBUF
);
538 init_favicon_col (value
, node
, group
);
540 case EPHY_COMPLETION_ACTION_COL
:
541 g_value_init (value
, G_TYPE_STRING
);
542 init_action_col (value
, node
, group
);
544 case EPHY_COMPLETION_KEYWORDS_COL
:
545 g_value_init (value
, G_TYPE_STRING
);
546 init_keywords_col (value
, node
, group
);
548 case EPHY_COMPLETION_RELEVANCE_COL
:
549 g_value_init (value
, G_TYPE_INT
);
550 init_relevance_col (value
, node
, group
);
552 case EPHY_COMPLETION_URL_COL
:
553 g_value_init (value
, G_TYPE_STRING
);
554 init_url_col (value
, node
, group
);
559 static GtkTreeModelFlags
560 ephy_completion_model_get_flags (GtkTreeModel
*tree_model
)
562 return GTK_TREE_MODEL_ITERS_PERSIST
| GTK_TREE_MODEL_LIST_ONLY
;
566 ephy_completion_model_get_iter (GtkTreeModel
*tree_model
,
570 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
575 g_return_val_if_fail (EPHY_IS_COMPLETION_MODEL (model
), FALSE
);
576 g_return_val_if_fail (gtk_tree_path_get_depth (path
) == 1, FALSE
);
578 index
= gtk_tree_path_get_indices (path
)[0];
580 pages
= get_pages (model
, &index
);
584 id
= g_array_index (pages
, guint
, index
);
585 iter_from_page (model
, pages
, id
, iter
);
591 ephy_completion_model_get_path (GtkTreeModel
*tree_model
,
594 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
596 g_return_val_if_fail (iter
!= NULL
, NULL
);
597 g_return_val_if_fail (iter
->user_data
!= NULL
, NULL
);
598 g_return_val_if_fail (iter
->user_data2
!= NULL
, NULL
);
599 g_return_val_if_fail (iter
->stamp
== model
->priv
->stamp
, NULL
);
601 return get_path_real (model
, iter
->user_data2
, iter
->user_data
);
605 ephy_completion_model_iter_next (GtkTreeModel
*tree_model
,
608 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
613 g_return_val_if_fail (iter
!= NULL
, FALSE
);
614 g_return_val_if_fail (iter
->user_data
!= NULL
, FALSE
);
615 g_return_val_if_fail (iter
->user_data2
!= NULL
, FALSE
);
616 g_return_val_if_fail (iter
->stamp
== model
->priv
->stamp
, FALSE
);
618 id
= ITER_GET_ID(iter
);
619 pages
= iter
->user_data2
;
621 index
= get_page_index (pages
, id
);
623 if (next
>= pages
->len
&& pages
== model
->priv
->history
)
625 pages
= model
->priv
->history
;
626 index
= g_array_index (pages
, 0);
630 if (next
>= pages
->len
)
633 iter_from_page (model
, pages
, next
, iter
);
639 ephy_completion_model_iter_children (GtkTreeModel
*tree_model
,
643 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
652 pages
= model
->priv
->history
;
653 id
= g_array_index (pages
, guint
, 0);
657 pages
= model
->priv
->bookmarks
;
658 id
= g_array_index (pages
, guint
, 0);
666 iter_from_page (model
, root
, id
, iter
);
672 ephy_completion_model_iter_has_child (GtkTreeModel
*tree_model
,
679 ephy_completion_model_iter_n_children (GtkTreeModel
*tree_model
,
682 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
684 g_return_val_if_fail (EPHY_IS_COMPLETION_MODEL (tree_model
), -1);
688 return model
->priv
->history
->len
+ model
->priv
->bookmarks
->len
;
691 g_return_val_if_fail (model
->priv
->stamp
== iter
->stamp
, -1);
697 ephy_completion_model_iter_nth_child (GtkTreeModel
*tree_model
,
702 EphyCompletionModel
*model
= EPHY_COMPLETION_MODEL (tree_model
);
706 g_return_val_if_fail (EPHY_IS_COMPLETION_MODEL (tree_model
), FALSE
);
713 pages
= get_pages (tree_model
, &n
);
717 id
= g_array_index (pages
, guint
, n
);
718 iter_from_page (model
, pages
, id
, iter
);
724 ephy_completion_model_iter_parent (GtkTreeModel
*tree_model
,
732 ephy_completion_model_tree_model_init (GtkTreeModelIface
*iface
)
734 iface
->get_flags
= ephy_completion_model_get_flags
;
735 iface
->get_iter
= ephy_completion_model_get_iter
;
736 iface
->get_path
= ephy_completion_model_get_path
;
737 iface
->iter_next
= ephy_completion_model_iter_next
;
738 iface
->iter_children
= ephy_completion_model_iter_children
;
739 iface
->iter_has_child
= ephy_completion_model_iter_has_child
;
740 iface
->iter_n_children
= ephy_completion_model_iter_n_children
;
741 iface
->iter_nth_child
= ephy_completion_model_iter_nth_child
;
742 iface
->iter_parent
= ephy_completion_model_iter_parent
;
743 iface
->get_n_columns
= ephy_completion_model_get_n_columns
;
744 iface
->get_column_type
= ephy_completion_model_get_column_type
;
745 iface
->get_value
= ephy_completion_model_get_value
;