merged the tested and working ephy-history auto-completion prototype code
[ephy-soc.git] / src / ephy-completion-model.c
blob27e8ae983233c29e15a27b4f1ce6d667e046d5f2
1 /*
2 * -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 */
4 /*
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 $
24 #include "config.h"
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"
32 //#include <stdlib.h>
34 static void ephy_completion_model_class_init (EphyCompletionModelClass *
35 klass);
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
44 EphyHistory *eh;
45 GArray *history;
46 GArray *bookmarks;
47 int stamp;
50 enum
52 HISTORY_GROUP,
53 BOOKMARKS_GROUP
56 typedef struct
58 guint64 id;
59 } HistoryItem;
61 static GObjectClass *parent_class = NULL;
63 GType
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),
72 NULL,
73 NULL,
74 (GClassInitFunc) ephy_completion_model_class_init,
75 NULL,
76 NULL,
77 sizeof (EphyCompletionModel),
79 (GInstanceInitFunc) ephy_completion_model_init
82 const GInterfaceInfo tree_model_info = {
83 (GInterfaceInitFunc)
84 ephy_completion_model_tree_model_init,
85 NULL,
86 NULL
89 type = g_type_register_static (G_TYPE_OBJECT,
90 "EphyCompletionModel",
91 &our_info, 0);
93 g_type_add_interface_static (type,
94 GTK_TYPE_TREE_MODEL,
95 &tree_model_info);
98 return type;
101 static void
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));
114 static void
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);
128 static int
129 page_ids_bsearch_compare (const void *key, const void *memberp)
131 return (*(guint64 *) key) - (*(guint64 *) memberp);
134 static int
135 get_page_index (GArray * pages, guint64 id)
137 int index;
138 char *ptr;
140 ptr = bsearch (&id, pages->data, pages->len, sizeof (guint64),
141 page_ids_bsearch_compare);
142 if (ptr == NULL)
143 return -1;
145 index = (ptr - pages->data) / sizeof (guint64);
146 if (index >= pages->len)
147 return -1;
149 return index;
152 static GtkTreePath *
153 get_path_real (EphyCompletionModel * model, GArray * pages, guint64 id)
155 GtkTreePath *retval;
156 int index;
158 index = get_page_index (pages, id);
160 if (index == -1)
161 return NULL;
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);
171 return retval;
174 static void
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);
180 item->id = id;
182 iter->stamp = model->priv->stamp;
183 iter->user_data = item;
184 iter->user_data2 = pages;
185 iter->user_data3 = NULL;
188 static GArray *
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;
199 else
201 return NULL;
204 else
206 return model->priv->history;
210 static void
211 history_page_added_cb (EphyHistory * history,
212 guint64 id, EphyCompletionModel * model)
214 GtkTreePath *path;
215 GtkTreeIter iter;
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);
226 static void
227 history_page_removed_cb (EphyHistory * history,
228 guint64 id, EphyCompletionModel * model)
230 GtkTreePath *path;
231 guint index;
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);
242 static void
243 history_page_updated_cb (EphyHistory * history,
244 guint64 id, EphyCompletionModel * model)
246 GtkTreePath *path;
247 GtkTreeIter iter;
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);
256 static void
257 connect_signals (EphyCompletionModel * model, EphyHistory * history)
259 g_signal_connect (history, "page-added",
260 G_CALLBACK (history_page_added_cb),
261 G_OBJECT (model));
262 g_signal_connect (history, "page-removed",
263 G_CALLBACK (history_page_removed_cb),
264 G_OBJECT (model));
265 g_signal_connect (history, "page-updated",
266 G_CALLBACK (history_page_updated_cb),
267 G_OBJECT (model));
270 static void
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
279 (embed_shell));
280 model->priv->history = ephy_history_get_page_ids (history);
281 model->priv->eh = history;
282 connect_signals (model, model->priv->eh);
285 * XXX: Bookmarks
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);
302 return model;
305 static int
306 ephy_completion_model_get_n_columns (GtkTreeModel * tree_model)
308 return N_COL;
311 static GType
312 ephy_completion_model_get_column_type (GtkTreeModel * tree_model, int index)
314 GType type = 0;
316 switch (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;
323 break;
324 case EPHY_COMPLETION_FAVICON_COL:
325 type = GDK_TYPE_PIXBUF;
326 break;
327 case EPHY_COMPLETION_RELEVANCE_COL:
328 type = G_TYPE_INT;
329 break;
332 return type;
335 static void
336 init_text_col (GValue * value, EphyHistoryPageNode * page, int group)
338 const char *text;
340 switch (group)
342 case BOOKMARKS_GROUP:
343 text = "";
344 break;
345 case HISTORY_GROUP:
346 g_object_get (G_OBJECT (page), "url", &text, NULL);
347 break;
348 default:
349 text = "";
352 g_value_set_string (value, text);
355 static void
356 init_action_col (GValue * value, EphyHistoryPageNode * page, int group)
358 const char *text;
360 switch (group)
362 case BOOKMARKS_GROUP:
363 text = "";
364 break;
365 case HISTORY_GROUP:
366 g_object_get (G_OBJECT (page), "url", &text, NULL);
367 break;
368 default:
369 text = "";
372 g_value_set_string (value, text);
375 static void
376 init_keywords_col (GValue * value, EphyHistoryPageNode * node, int group)
378 const char *text = NULL;
380 switch (group)
382 case BOOKMARKS_GROUP:
383 break;
386 if (text == NULL)
388 text = "";
391 g_value_set_string (value, text);
394 static void
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)));
405 switch (group)
407 case BOOKMARKS_GROUP:
408 break;
409 case HISTORY_GROUP:
410 g_object_get (G_OBJECT (page), "favicon-url",
411 &icon_location, NULL);
412 break;
413 default:
414 icon_location = NULL;
417 if (icon_location)
419 pixbuf = ephy_favicon_cache_get (cache, icon_location);
422 g_value_take_object (value, pixbuf);
425 static gboolean
426 is_base_address (const char *address)
428 int slashes = 0;
430 if (address == NULL)
431 return FALSE;
433 while (*address != '\0')
435 if (*address == '/')
436 slashes++;
438 address++;
441 * Base uris has 3 slashes like http://www.gnome.org/
443 if (slashes == 3)
445 return (*address == '\0');
449 return FALSE;
452 static void
453 init_relevance_col (GValue * value, EphyHistoryPageNode * page, int group)
455 int relevance = 0;
458 * We have three ordered groups: history's base addresses,
459 * bookmarks, deep history addresses
462 if (group == BOOKMARKS_GROUP)
464 relevance = 1 << 5;
466 else if (group == HISTORY_GROUP)
468 char *url;
469 guint visits;
471 g_object_get (G_OBJECT (page), "url", &url, "visit-count",
472 &visits, NULL);
474 visits = MIN (visits, (1 << 5) - 1);
476 if (is_base_address (url))
478 relevance = visits << 10;
480 else
482 relevance = visits;
484 //g_print("url: %s visits: %u rel: %d\n", url, visits, relevance);
486 g_value_set_int (value, relevance);
489 static void
490 init_url_col (GValue * value, EphyHistoryPageNode * page, int group)
492 const char *url = NULL;
494 if (group == BOOKMARKS_GROUP)
496 url = "";
498 else if (group == HISTORY_GROUP)
500 g_object_get (G_OBJECT (page), "url", &url, NULL);
502 else
504 url = "";
506 g_value_set_string (value, url);
509 static void
510 ephy_completion_model_get_value (GtkTreeModel * tree_model,
511 GtkTreeIter * iter,
512 int column, GValue * value)
514 int group;
515 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
516 EphyHistoryPageNode *page;
517 guint64 id;
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;
529 if (page == NULL)
531 /* g_debug ("querying page info"); */
532 page = ephy_history_get_page_by_id (model->priv->eh, id);
533 iter->user_data3 = page;
535 else
537 /* g_debug ("using cached page info"); */
540 switch (column)
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
547 * their bookmarks
549 if (group == HISTORY_GROUP)
551 char *title;
552 g_object_get (G_OBJECT (page), "title", &title, NULL);
553 g_value_set_string (value, title);
555 break;
556 case EPHY_COMPLETION_TEXT_COL:
557 g_value_init (value, G_TYPE_STRING);
558 init_text_col (value, page, group);
559 break;
560 case EPHY_COMPLETION_FAVICON_COL:
561 g_value_init (value, GDK_TYPE_PIXBUF);
562 init_favicon_col (value, page, group);
563 break;
564 case EPHY_COMPLETION_ACTION_COL:
565 g_value_init (value, G_TYPE_STRING);
566 init_action_col (value, page, group);
567 break;
568 case EPHY_COMPLETION_KEYWORDS_COL:
569 g_value_init (value, G_TYPE_STRING);
570 init_keywords_col (value, page, group);
571 break;
572 case EPHY_COMPLETION_RELEVANCE_COL:
573 g_value_init (value, G_TYPE_INT);
574 init_relevance_col (value, page, group);
575 break;
576 case EPHY_COMPLETION_URL_COL:
577 g_value_init (value, G_TYPE_STRING);
578 init_url_col (value, page, group);
579 break;
583 static GtkTreeModelFlags
584 ephy_completion_model_get_flags (GtkTreeModel * tree_model)
586 return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
589 static gboolean
590 ephy_completion_model_get_iter (GtkTreeModel * tree_model,
591 GtkTreeIter * iter, GtkTreePath * path)
593 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
594 GArray *pages;
595 int index;
596 guint64 id;
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);
604 if (pages == NULL)
605 return FALSE;
607 id = g_array_index (pages, guint64, index);
608 iter_from_page (model, pages, id, iter);
610 return TRUE;
613 static GtkTreePath *
614 ephy_completion_model_get_path (GtkTreeModel * tree_model, GtkTreeIter * iter)
616 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
617 guint64 id;
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);
628 static gboolean
629 ephy_completion_model_iter_next (GtkTreeModel * tree_model,
630 GtkTreeIter * iter)
632 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
633 GArray *pages;
634 guint64 id;
635 gint index, next;
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);
646 if (index == -1)
647 return FALSE;
649 next = index + 1;
650 if (next >= pages->len)
652 if (pages == model->priv->history
653 && model->priv->bookmarks->len > 0)
655 pages = model->priv->bookmarks;
656 next = 0;
658 else
660 return FALSE;
664 id = g_array_index (pages, guint64, next);
666 iter_from_page (model, pages, id, iter);
668 return TRUE;
671 static gboolean
672 ephy_completion_model_iter_children (GtkTreeModel * tree_model,
673 GtkTreeIter * iter, GtkTreeIter * parent)
675 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
676 GArray *pages;
677 guint64 id;
679 if (parent != NULL)
681 return FALSE;
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);
694 else
696 return FALSE;
699 iter_from_page (model, pages, id, iter);
701 return TRUE;
704 static gboolean
705 ephy_completion_model_iter_has_child (GtkTreeModel * tree_model,
706 GtkTreeIter * iter)
708 return FALSE;
711 static int
712 ephy_completion_model_iter_n_children (GtkTreeModel * tree_model,
713 GtkTreeIter * iter)
715 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
717 g_return_val_if_fail (EPHY_IS_COMPLETION_MODEL (tree_model), -1);
719 if (iter == NULL)
721 return model->priv->history->len +
722 model->priv->bookmarks->len;
725 g_return_val_if_fail (model->priv->stamp == iter->stamp, -1);
727 return 0;
730 static gboolean
731 ephy_completion_model_iter_nth_child (GtkTreeModel * tree_model,
732 GtkTreeIter * iter,
733 GtkTreeIter * parent, int n)
735 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
736 GArray *pages;
737 guint64 id;
739 g_return_val_if_fail (EPHY_IS_COMPLETION_MODEL (tree_model), FALSE);
741 if (parent != NULL)
743 return FALSE;
746 pages = get_pages (model, &n);
747 if (pages == NULL)
748 return FALSE;
750 id = g_array_index (pages, guint64, n);
751 iter_from_page (model, pages, id, iter);
753 return TRUE;
756 static gboolean
757 ephy_completion_model_iter_parent (GtkTreeModel * tree_model,
758 GtkTreeIter * iter, GtkTreeIter * child)
760 return FALSE;
763 static void
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;