More fixes for signal simulation in the completion prototype
[ephy-history.git] / ephy-completion-model.c
blob022c2ba3a2f9fb95b1fb550434fe06935a303b96
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 <stdlib.h>
25 #include "ephy-completion-model.h"
26 #include "ephy-history.h"
28 #ifdef EPHY_MERGE
29 #include "config.h"
30 #include "ephy-favicon-cache.h"
31 #include "ephy-shell.h"
32 #else
33 #include <gdk-pixbuf/gdk-pixdata.h>
34 #endif
36 static void ephy_completion_model_class_init (EphyCompletionModelClass *
37 klass);
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
46 EphyHistory *eh;
47 GArray *history;
48 GArray *bookmarks;
49 int stamp;
52 enum
54 HISTORY_GROUP,
55 BOOKMARKS_GROUP
58 typedef struct
60 guint64 id;
61 } HistoryItem;
63 static GObjectClass *parent_class = NULL;
65 GType
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),
74 NULL,
75 NULL,
76 (GClassInitFunc) ephy_completion_model_class_init,
77 NULL,
78 NULL,
79 sizeof (EphyCompletionModel),
81 (GInstanceInitFunc) ephy_completion_model_init
84 const GInterfaceInfo tree_model_info = {
85 (GInterfaceInitFunc)
86 ephy_completion_model_tree_model_init,
87 NULL,
88 NULL
91 type = g_type_register_static (G_TYPE_OBJECT,
92 "EphyCompletionModel",
93 &our_info, 0);
95 g_type_add_interface_static (type,
96 GTK_TYPE_TREE_MODEL,
97 &tree_model_info);
100 return type;
103 static void
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));
116 static void
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);
130 static int
131 page_ids_bsearch_compare (const void *key, const void *memberp)
133 return (*(guint64 *) key) - (*(guint64 *) memberp);
136 static int
137 get_page_index (GArray * pages, guint64 id)
139 int index;
140 char *ptr;
142 ptr = bsearch (&id, pages->data, pages->len, sizeof (guint64),
143 page_ids_bsearch_compare);
144 if (ptr == NULL)
145 return -1;
147 index = (ptr - pages->data) / sizeof (guint64);
148 if (index >= pages->len)
149 return -1;
151 return index;
154 static GtkTreePath *
155 get_path_real (EphyCompletionModel * model, GArray * pages, guint64 id)
157 GtkTreePath *retval;
158 int index;
160 index = get_page_index (pages, id);
162 if (index == -1)
163 return NULL;
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);
173 return retval;
176 static void
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);
182 item->id = id;
184 iter->stamp = model->priv->stamp;
185 iter->user_data = item;
186 iter->user_data2 = pages;
187 iter->user_data3 = NULL;
190 static GArray *
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;
201 else
203 return NULL;
206 else
208 return model->priv->history;
212 static void
213 history_page_added_cb (EphyHistory * history,
214 guint64 id, EphyCompletionModel * model)
216 GtkTreePath *path;
217 GtkTreeIter iter;
218 GValue val = {0, };
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);
232 static void
233 history_page_removed_cb (EphyHistory * history,
234 guint64 id, EphyCompletionModel * model)
236 GtkTreePath *path;
237 guint index;
239 GValue val = {0, };
240 GtkTreeIter iter;
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);
258 static void
259 history_page_updated_cb (EphyHistory * history,
260 guint64 id, EphyCompletionModel * model)
262 GtkTreePath *path;
263 GtkTreeIter iter;
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);
273 static void
274 connect_signals (EphyCompletionModel * model, EphyHistory * history)
276 g_signal_connect (history, "page-added",
277 G_CALLBACK (history_page_added_cb),
278 G_OBJECT (model));
279 g_signal_connect (history, "page-removed",
280 G_CALLBACK (history_page_removed_cb),
281 G_OBJECT (model));
282 g_signal_connect (history, "page-updated",
283 G_CALLBACK (history_page_updated_cb),
284 G_OBJECT (model));
287 static void
288 ephy_completion_model_init (EphyCompletionModel * model)
291 model->priv = EPHY_COMPLETION_MODEL_GET_PRIVATE (model);
292 model->priv->stamp = g_random_int ();
294 #ifdef EPHY_MERGE
295 EphyHistory *history;
297 history =
298 EPHY_HISTORY (ephy_embed_shell_get_global_history
299 (embed_shell));
300 model->priv->history = ephy_history_get_page_ids (history);
301 model->priv->eh = history;
302 connect_signals (model, model->priv->eh);
303 #endif
306 * XXX: Bookmarks
308 model->priv->bookmarks = g_array_new (FALSE, FALSE, sizeof (guint64));
313 EphyCompletionModel *
314 #ifdef EPHY_MERGE
315 ephy_completion_model_new (void)
316 #else
317 ephy_completion_model_new (EphyHistory * eh)
318 #endif
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);
327 #ifndef EPHY_MERGE
328 model->priv->eh = eh;
329 model->priv->history = ephy_history_get_page_ids (eh);
330 connect_signals (model, model->priv->eh);
331 #endif
333 return model;
336 static int
337 ephy_completion_model_get_n_columns (GtkTreeModel * tree_model)
339 return N_COL;
342 static GType
343 ephy_completion_model_get_column_type (GtkTreeModel * tree_model, int index)
345 GType type = 0;
347 switch (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;
354 break;
355 case EPHY_COMPLETION_FAVICON_COL:
356 type = GDK_TYPE_PIXBUF;
357 break;
358 case EPHY_COMPLETION_RELEVANCE_COL:
359 type = G_TYPE_INT;
360 break;
363 return type;
366 static void
367 init_text_col (GValue * value, EphyHistoryPageNode * page, int group)
369 const char *text;
371 switch (group)
373 case BOOKMARKS_GROUP:
374 text = "";
375 break;
376 case HISTORY_GROUP:
377 g_object_get (G_OBJECT (page), "url", &text, NULL);
378 break;
379 default:
380 text = "";
383 g_value_set_string (value, text);
386 static void
387 init_action_col (GValue * value, EphyHistoryPageNode * page, int group)
389 const char *text;
391 switch (group)
393 case BOOKMARKS_GROUP:
394 text = "";
395 break;
396 case HISTORY_GROUP:
397 g_object_get (G_OBJECT (page), "url", &text, NULL);
398 break;
399 default:
400 text = "";
403 g_value_set_string (value, text);
406 static void
407 init_keywords_col (GValue * value, EphyHistoryPageNode * node, int group)
409 const char *text = NULL;
411 switch (group)
413 case BOOKMARKS_GROUP:
414 break;
417 if (text == NULL)
419 text = "";
422 g_value_set_string (value, text);
425 static void
426 init_favicon_col (GValue * value, EphyHistoryPageNode * page, int group)
428 const char *icon_location;
429 #ifdef EPHY_MERGE
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)));
436 #endif
438 switch (group)
440 case BOOKMARKS_GROUP:
441 break;
442 case HISTORY_GROUP:
443 g_object_get (G_OBJECT (page), "favicon-url",
444 &icon_location, NULL);
445 break;
446 default:
447 icon_location = NULL;
450 #ifdef EPHY_MERGE
451 if (icon_location)
453 pixbuf = ephy_favicon_cache_get (cache, icon_location);
456 g_value_take_object (value, pixbuf);
457 #else
458 g_value_take_object (value, NULL);
459 #endif
460 g_print("init_favicon_col: %s\n", icon_location);
463 static gboolean
464 is_base_address (const char *address)
466 int slashes = 0;
468 if (address == NULL)
469 return FALSE;
471 while (*address != '\0')
473 if (*address == '/')
474 slashes++;
476 address++;
479 * Base uris has 3 slashes like http://www.gnome.org/
481 if (slashes == 3)
483 return (*address == '\0');
487 return FALSE;
490 static void
491 init_relevance_col (GValue * value, EphyHistoryPageNode * page, int group)
493 int relevance = 0;
496 * We have three ordered groups: history's base addresses,
497 * bookmarks, deep history addresses
500 if (group == BOOKMARKS_GROUP)
502 relevance = 1 << 5;
504 else if (group == HISTORY_GROUP)
506 char *url;
507 guint visits;
509 g_object_get (G_OBJECT (page), "url", &url, "visit-count",
510 &visits, NULL);
512 visits = MIN (visits, (1 << 5) - 1);
514 if (is_base_address (url))
516 relevance = visits << 10;
518 else
520 relevance = visits;
522 //g_print("url: %s visits: %u rel: %d\n", url, visits, relevance);
524 g_value_set_int (value, relevance);
527 static void
528 init_url_col (GValue * value, EphyHistoryPageNode * page, int group)
530 const char *url = NULL;
532 if (group == BOOKMARKS_GROUP)
534 url = "";
536 else if (group == HISTORY_GROUP)
538 g_object_get (G_OBJECT (page), "url", &url, NULL);
540 else
542 url = "";
544 g_value_set_string (value, url);
547 static void
548 ephy_completion_model_get_value (GtkTreeModel * tree_model,
549 GtkTreeIter * iter,
550 int column, GValue * value)
552 int group;
553 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
554 EphyHistoryPageNode *page;
555 guint64 id;
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;
567 if (page == NULL)
569 /* g_debug ("querying page info"); */
570 page = ephy_history_get_page_by_id (model->priv->eh, id);
571 iter->user_data3 = page;
573 else
575 /* g_debug ("using cached page info"); */
578 switch (column)
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
585 * their bookmarks
587 if (group == HISTORY_GROUP)
589 char *title;
590 g_object_get (G_OBJECT (page), "title", &title, NULL);
591 g_value_set_string (value, title);
593 break;
594 case EPHY_COMPLETION_TEXT_COL:
595 g_value_init (value, G_TYPE_STRING);
596 init_text_col (value, page, group);
597 break;
598 case EPHY_COMPLETION_FAVICON_COL:
599 g_value_init (value, GDK_TYPE_PIXBUF);
600 init_favicon_col (value, page, group);
601 break;
602 case EPHY_COMPLETION_ACTION_COL:
603 g_value_init (value, G_TYPE_STRING);
604 init_action_col (value, page, group);
605 break;
606 case EPHY_COMPLETION_KEYWORDS_COL:
607 g_value_init (value, G_TYPE_STRING);
608 init_keywords_col (value, page, group);
609 break;
610 case EPHY_COMPLETION_RELEVANCE_COL:
611 g_value_init (value, G_TYPE_INT);
612 init_relevance_col (value, page, group);
613 break;
614 case EPHY_COMPLETION_URL_COL:
615 g_value_init (value, G_TYPE_STRING);
616 init_url_col (value, page, group);
617 break;
621 static GtkTreeModelFlags
622 ephy_completion_model_get_flags (GtkTreeModel * tree_model)
624 return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
627 static gboolean
628 ephy_completion_model_get_iter (GtkTreeModel * tree_model,
629 GtkTreeIter * iter, GtkTreePath * path)
631 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
632 GArray *pages;
633 int index;
634 guint64 id;
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);
642 if (pages == NULL)
643 return FALSE;
645 id = g_array_index (pages, guint64, index);
646 iter_from_page (model, pages, id, iter);
648 return TRUE;
651 static GtkTreePath *
652 ephy_completion_model_get_path (GtkTreeModel * tree_model, GtkTreeIter * iter)
654 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
655 guint64 id;
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);
666 static gboolean
667 ephy_completion_model_iter_next (GtkTreeModel * tree_model,
668 GtkTreeIter * iter)
670 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
671 GArray *pages;
672 guint64 id;
673 gint index, next;
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);
684 if (index == -1)
685 return FALSE;
687 next = index + 1;
688 if (next >= pages->len)
690 if (pages == model->priv->history
691 && model->priv->bookmarks->len > 0)
693 pages = model->priv->bookmarks;
694 next = 0;
696 else
698 return FALSE;
702 id = g_array_index (pages, guint64, next);
704 iter_from_page (model, pages, id, iter);
706 return TRUE;
709 static gboolean
710 ephy_completion_model_iter_children (GtkTreeModel * tree_model,
711 GtkTreeIter * iter, GtkTreeIter * parent)
713 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
714 GArray *pages;
715 guint64 id;
717 if (parent != NULL)
719 return FALSE;
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);
732 else
734 return FALSE;
737 iter_from_page (model, pages, id, iter);
739 return TRUE;
742 static gboolean
743 ephy_completion_model_iter_has_child (GtkTreeModel * tree_model,
744 GtkTreeIter * iter)
746 return FALSE;
749 static int
750 ephy_completion_model_iter_n_children (GtkTreeModel * tree_model,
751 GtkTreeIter * iter)
753 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
755 g_return_val_if_fail (EPHY_IS_COMPLETION_MODEL (tree_model), -1);
757 if (iter == NULL)
759 return model->priv->history->len +
760 model->priv->bookmarks->len;
763 g_return_val_if_fail (model->priv->stamp == iter->stamp, -1);
765 return 0;
768 static gboolean
769 ephy_completion_model_iter_nth_child (GtkTreeModel * tree_model,
770 GtkTreeIter * iter,
771 GtkTreeIter * parent, int n)
773 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
774 GArray *pages;
775 guint64 id;
777 g_return_val_if_fail (EPHY_IS_COMPLETION_MODEL (tree_model), FALSE);
779 if (parent != NULL)
781 return FALSE;
784 pages = get_pages (model, &n);
785 if (pages == NULL)
786 return FALSE;
788 id = g_array_index (pages, guint64, n);
789 iter_from_page (model, pages, id, iter);
791 return TRUE;
794 static gboolean
795 ephy_completion_model_iter_parent (GtkTreeModel * tree_model,
796 GtkTreeIter * iter, GtkTreeIter * child)
798 return FALSE;
801 static void
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;