Initial stab at merging sqlite3 prototype with auto-completion (incomplete)
[ephy-soc.git] / src / ephy-completion-model.c
blobda4982091085a9ef87202a17f2d54cf789e2f126
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
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 $
22 #include "config.h"
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
38 GArray *history;
39 GArray *bookmarks;
40 int stamp;
43 enum
45 HISTORY_GROUP,
46 BOOKMARKS_GROUP
49 static GObjectClass *parent_class = NULL;
51 #define ITER_GET_ID(iter) (GPOINTER_TO_UINT((iter)->user_data))
53 GType
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),
63 NULL,
64 NULL,
65 (GClassInitFunc) ephy_completion_model_class_init,
66 NULL,
67 NULL,
68 sizeof (EphyCompletionModel),
70 (GInstanceInitFunc) ephy_completion_model_init
73 const GInterfaceInfo tree_model_info =
75 (GInterfaceInitFunc) ephy_completion_model_tree_model_init,
76 NULL,
77 NULL
80 type = g_type_register_static (G_TYPE_OBJECT,
81 "EphyCompletionModel",
82 &our_info, 0);
84 g_type_add_interface_static (type,
85 GTK_TYPE_TREE_MODEL,
86 &tree_model_info);
89 return type;
92 static void
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));
102 static guint
103 page_ids_bsearch_compare (const void *key, const void *memberp)
105 guint member = *(guint *)memberp;
106 return GPOINTER_TO_UINT(key) - member;
109 static int
110 get_page_index (GArray *pages, guint id)
112 int index;
113 char *ptr;
115 ptr = bsearch (GUINT_TO_POINTER(id), pages->data, pages->len,
116 sizeof (guint), page_ids_bsearch_compare);
118 if (ptr == NULL)
119 return -1;
121 index = (ptr - pages->data) / sizeof (guint);
122 if (index > pages->len)
123 return -1;
125 return index;
128 static GtkTreePath *
129 get_path_real (EphyCompletionModel *model,
130 GArray *pages,
131 guint id)
133 GtkTreePath *retval;
134 int index;
136 retval = gtk_tree_path_new ();
138 index = get_page_index(pages, id);
140 if (index == -1)
141 return NULL;
143 if (pages == model->priv->bookmarks)
145 index += model->priv->history->len;
148 gtk_tree_path_append_index (retval, index);
150 return retval;
153 static void
154 iter_from_page (EphyCompletionModel *model,
155 GArray *pages,
156 guint id,
157 GtkTreeIter *iter)
159 iter->stamp = model->priv->stamp;
160 iter->user_data = GUINT_TO_POINTER (id);
161 iter->user_data2 = pages;
164 static GArray *
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;
176 else
178 return NULL;
181 else
183 return model->priv->history;
185 return TRUE;
188 static void
189 root_child_removed_cb (EphyNode *node,
190 EphyNode *child,
191 guint old_index,
192 EphyCompletionModel *model)
194 GtkTreePath *path;
195 guint index;
197 path = gtk_tree_path_new ();
199 index = old_index;
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);
210 static void
211 root_child_added_cb (EphyNode *node,
212 EphyNode *child,
213 EphyCompletionModel *model)
215 GtkTreePath *path;
216 GtkTreeIter iter;
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);
225 static void
226 root_child_changed_cb (EphyNode *node,
227 EphyNode *child,
228 guint property_id,
229 EphyCompletionModel *model)
231 GtkTreePath *path;
232 GtkTreeIter iter;
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);
241 static void
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,
247 G_OBJECT (model));
248 ephy_node_signal_connect_object (root,
249 EPHY_NODE_CHILD_REMOVED,
250 (EphyNodeCallback)root_child_removed_cb,
251 G_OBJECT (model));
252 ephy_node_signal_connect_object (root,
253 EPHY_NODE_CHILD_CHANGED,
254 (EphyNodeCallback)root_child_changed_cb,
255 G_OBJECT (model));
258 static void
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);
273 /* XXX: Bookmarks */
276 EphyCompletionModel *
277 ephy_completion_model_new (void)
279 EphyCompletionModel *model;
281 model = EPHY_COMPLETION_MODEL (g_object_new (EPHY_TYPE_COMPLETION_MODEL,
282 NULL));
284 g_return_val_if_fail (model->priv != NULL, NULL);
286 return model;
289 static int
290 ephy_completion_model_get_n_columns (GtkTreeModel *tree_model)
292 return N_COL;
295 static GType
296 ephy_completion_model_get_column_type (GtkTreeModel *tree_model,
297 int index)
299 GType type = 0;
301 switch (index)
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;
308 break;
309 case EPHY_COMPLETION_FAVICON_COL:
310 type = GDK_TYPE_PIXBUF;
311 break;
312 case EPHY_COMPLETION_RELEVANCE_COL:
313 type = G_TYPE_INT;
314 break;
317 return type;
320 static void
321 init_text_col (GValue *value, EphyNode *node, int group)
323 const char *text;
325 switch (group)
327 case BOOKMARKS_GROUP:
328 text = ephy_node_get_property_string
329 (node, EPHY_NODE_BMK_PROP_TITLE);
330 break;
331 case HISTORY_GROUP:
332 text = ephy_node_get_property_string
333 (node, EPHY_NODE_PAGE_PROP_LOCATION);
334 break;
336 default:
337 text = "";
340 g_value_set_string (value, text);
343 static void
344 init_action_col (GValue *value, EphyNode *node, int group)
346 const char *text;
348 switch (group)
350 case BOOKMARKS_GROUP:
351 text = ephy_node_get_property_string
352 (node, EPHY_NODE_BMK_PROP_LOCATION);
353 break;
354 case HISTORY_GROUP:
355 text = ephy_node_get_property_string
356 (node, EPHY_NODE_PAGE_PROP_LOCATION);
357 break;
358 default:
359 text = "";
362 g_value_set_string (value, text);
365 static void
366 init_keywords_col (GValue *value, EphyNode *node, int group)
368 const char *text = NULL;
370 switch (group)
372 case BOOKMARKS_GROUP:
373 text = ephy_node_get_property_string
374 (node, EPHY_NODE_BMK_PROP_KEYWORDS);
375 break;
378 if (text == NULL)
380 text = "";
383 g_value_set_string (value, text);
386 static void
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)));
396 switch (group)
398 case BOOKMARKS_GROUP:
399 icon_location = ephy_node_get_property_string
400 (node, EPHY_NODE_BMK_PROP_ICON);
401 break;
402 case HISTORY_GROUP:
403 icon_location = ephy_node_get_property_string
404 (node, EPHY_NODE_PAGE_PROP_ICON);
405 break;
406 default:
407 icon_location = NULL;
410 if (icon_location)
412 pixbuf = ephy_favicon_cache_get (cache, icon_location);
415 g_value_take_object (value, pixbuf);
418 static gboolean
419 is_base_address (const char *address)
421 int slashes = 0;
423 if (address == NULL) return FALSE;
425 while (*address != '\0')
427 if (*address == '/') slashes++;
429 address++;
431 /* Base uris has 3 slashes like http://www.gnome.org/ */
432 if (slashes == 3)
434 return (*address == '\0');
438 return FALSE;
441 static void
442 init_relevance_col (GValue *value, EphyNode *node, int group)
444 int relevance = 0;
446 /* We have three ordered groups: history's base
447 addresses, bookmarks, deep history addresses */
449 if (group == BOOKMARKS_GROUP)
451 relevance = 1 << 5;
453 else if (group == HISTORY_GROUP)
455 const char *address;
456 int visits;
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;
469 else
471 relevance = visits;
475 g_value_set_int (value, relevance);
478 static void
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);
493 else
495 url = "";
498 g_value_set_string (value, url);
501 static void
502 ephy_completion_model_get_value (GtkTreeModel *tree_model,
503 GtkTreeIter *iter,
504 int column,
505 GValue *value)
507 int group;
508 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
509 EphyNode *node;
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;
519 switch (column)
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)
526 const char *text;
527 text = ephy_node_get_property_string
528 (node, EPHY_NODE_PAGE_PROP_TITLE);
529 g_value_set_string (value, text);
531 break;
532 case EPHY_COMPLETION_TEXT_COL:
533 g_value_init (value, G_TYPE_STRING);
534 init_text_col (value, node, group);
535 break;
536 case EPHY_COMPLETION_FAVICON_COL:
537 g_value_init (value, GDK_TYPE_PIXBUF);
538 init_favicon_col (value, node, group);
539 break;
540 case EPHY_COMPLETION_ACTION_COL:
541 g_value_init (value, G_TYPE_STRING);
542 init_action_col (value, node, group);
543 break;
544 case EPHY_COMPLETION_KEYWORDS_COL:
545 g_value_init (value, G_TYPE_STRING);
546 init_keywords_col (value, node, group);
547 break;
548 case EPHY_COMPLETION_RELEVANCE_COL:
549 g_value_init (value, G_TYPE_INT);
550 init_relevance_col (value, node, group);
551 break;
552 case EPHY_COMPLETION_URL_COL:
553 g_value_init (value, G_TYPE_STRING);
554 init_url_col (value, node, group);
555 break;
559 static GtkTreeModelFlags
560 ephy_completion_model_get_flags (GtkTreeModel *tree_model)
562 return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
565 static gboolean
566 ephy_completion_model_get_iter (GtkTreeModel *tree_model,
567 GtkTreeIter *iter,
568 GtkTreePath *path)
570 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
571 GArray *pages;
572 int index;
573 guint id;
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);
581 if (pages == NULL)
582 return FALSE;
584 id = g_array_index (pages, guint, index);
585 iter_from_page (model, pages, id, iter);
587 return TRUE;
590 static GtkTreePath *
591 ephy_completion_model_get_path (GtkTreeModel *tree_model,
592 GtkTreeIter *iter)
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);
604 static gboolean
605 ephy_completion_model_iter_next (GtkTreeModel *tree_model,
606 GtkTreeIter *iter)
608 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
609 GArray *pages;
610 guint id;
611 gint index, next;
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);
622 next = index + 1;
623 if (next >= pages->len && pages == model->priv->history)
625 pages = model->priv->history;
626 index = g_array_index (pages, 0);
627 next = index + 1;
630 if (next >= pages->len )
631 return FALSE;
633 iter_from_page (model, pages, next, iter);
635 return TRUE;
638 static gboolean
639 ephy_completion_model_iter_children (GtkTreeModel *tree_model,
640 GtkTreeIter *iter,
641 GtkTreeIter *parent)
643 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
644 GArray *pages;
645 guint id;
647 if (parent != NULL)
649 return FALSE;
652 pages = model->priv->history;
653 id = g_array_index (pages, guint, 0);
655 if (!id)
657 pages = model->priv->bookmarks;
658 id = g_array_index (pages, guint, 0);
661 if (!id)
663 return FALSE;
666 iter_from_page (model, root, id, iter);
668 return TRUE;
671 static gboolean
672 ephy_completion_model_iter_has_child (GtkTreeModel *tree_model,
673 GtkTreeIter *iter)
675 return FALSE;
678 static int
679 ephy_completion_model_iter_n_children (GtkTreeModel *tree_model,
680 GtkTreeIter *iter)
682 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
684 g_return_val_if_fail (EPHY_IS_COMPLETION_MODEL (tree_model), -1);
686 if (iter == NULL)
688 return model->priv->history->len + model->priv->bookmarks->len;
691 g_return_val_if_fail (model->priv->stamp == iter->stamp, -1);
693 return 0;
696 static gboolean
697 ephy_completion_model_iter_nth_child (GtkTreeModel *tree_model,
698 GtkTreeIter *iter,
699 GtkTreeIter *parent,
700 int n)
702 EphyCompletionModel *model = EPHY_COMPLETION_MODEL (tree_model);
703 GArray *pages;
704 guint id;
706 g_return_val_if_fail (EPHY_IS_COMPLETION_MODEL (tree_model), FALSE);
708 if (parent != NULL)
710 return FALSE;
713 pages = get_pages (tree_model, &n);
714 if (pages == NULL)
715 return FALSE;
717 id = g_array_index (pages, guint, n);
718 iter_from_page (model, pages, id, iter);
720 return TRUE;
723 static gboolean
724 ephy_completion_model_iter_parent (GtkTreeModel *tree_model,
725 GtkTreeIter *iter,
726 GtkTreeIter *child)
728 return FALSE;
731 static void
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;