8 #include <glib/gi18n.h>
10 #include <glib/gstdio.h>
12 #include "stuffkeeper-data-tag.h"
13 #include "stuffkeeper-data-item.h"
14 #include "stuffkeeper-data-schema.h"
18 typedef enum _SearchType {
21 SEARCH_TYPE_IS_NOT = 2,
22 SEARCH_TYPE_CONTAINS = 3,
23 SEARCH_TYPE_NOT_CONTAINS = 4,
24 SEARCH_TYPE_NUM_ITEMS = 5
28 typedef enum _SearchFieldType {
30 SEARCH_FIELD_SCHEMA = 1,
32 SEARCH_FIELD_TITLE = 3,
33 SEARCH_FIELD_VALUE = 4,
37 typedef struct _SearchField
41 SearchFieldType field_type;
49 #include "stuffkeeper-data-backend.h"
50 const char *SearchTypesNames[] =
58 const char *SearchFieldTypesNames[] =
69 class Stuffkeeper:Data:Item:Search from G:Object
71 private int is_dummy = {FALSE};
72 private int id = {-1};
73 private StuffkeeperDataBackend *skdb = {NULL};
74 private sqlite3 *sqlHandle = {NULL};
76 private GtkBuilder *xml = {NULL};
77 private GtkListStore *store = {NULL} destroywith g_object_unref;
78 private GtkListStore *type_store = {NULL} destroywith g_object_unref;
79 private GtkListStore *field_type_store = {NULL} destroywith g_object_unref;
81 private guint title_timeout = {0};
86 signal last NONE (POINTER)
88 search_changed(self, const SearchField *field)
90 if(field && self->_priv->store)
93 GtkTreeModel *model = GTK_TREE_MODEL(self->_priv->store);
94 if(gtk_tree_model_get_iter_first(model, &iter))
97 SearchField *field2 = NULL;
98 gtk_tree_model_get(model, &iter, 0, &field2, -1);
100 if(field2->id == field->id)
102 GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
103 gtk_tree_model_row_changed(model, path, &iter);
104 gtk_tree_path_free(path);
107 }while(gtk_tree_model_iter_next(model, &iter));
118 get_string(self, int searchtype, int fieldtype)
121 char *query = sqlite3_mprintf("SELECT value FROM 'SearchFields' WHERE SearchId=%i and SearchType=%i and FieldType=%i",
128 r = sqlite3_prepare_v2(self->_priv->sqlHandle, query, -1, &stmt, &tail);
129 if (r == SQLITE_OK) {
130 if((r = sqlite3_step(stmt)) == SQLITE_ROW) {
131 retv = g_strdup((gchar *)sqlite3_column_text(stmt, 0));
134 sqlite3_finalize(stmt);
141 set_string(self,int searchtype, int fieldtype, const gchar *title)
147 val = self_get_string(self, searchtype,fieldtype);
148 /* no field, create it */
151 query = sqlite3_mprintf("INSERT INTO 'SearchFields' ('SearchId','FieldType','SearchType','value')"
152 "values (%i,%i,%i,%Q);",
159 /* if they are identical, do nothing */
160 if(strcmp(title, val) == 0)
163 g_debug("not updating\n");
166 /* update the title */
167 query = sqlite3_mprintf("UPDATE 'SearchFields' SET value=%Q WHERE SearchId=%i and Fieldtype=%i and SearchType=%i",
174 result = sqlite3_exec(self->_priv->sqlHandle, query, NULL, NULL, &error);
175 if (result != SQLITE_OK) {
176 g_debug("failed: %s\n", error);
189 Self *self = GET_NEW;
190 self->_priv->is_dummy = TRUE;
191 return G_OBJECT(self);
196 open_from_id(G:Object *skdb, gint id)
198 Self *self = GET_NEW;
199 self->_priv->skdb = STUFFKEEPER_DATA_BACKEND(skdb);
200 self->_priv->id = id;
201 self->_priv->sqlHandle = stuffkeeper_data_backend_get_handle(self->_priv->skdb);
202 return G_OBJECT(self);
204 /* Create new StuffkeeperDataBackend */
206 StuffkeeperDataItemSearch *
214 obj->_priv->skdb = STUFFKEEPER_DATA_BACKEND(skdb);
215 obj->_priv->sqlHandle = stuffkeeper_data_backend_get_handle(obj->_priv->skdb);
217 * Insert the first item in the list
219 query = sqlite3_mprintf
220 ("INSERT INTO 'SearchFields' ('SearchType','FieldType','value')"
221 "values (%i,%i,%Q);",
222 SEARCH_TYPE_NONE, SEARCH_TITLE, "New Search");
224 result = sqlite3_exec(obj->_priv->sqlHandle, query, NULL, NULL, &error);
225 if (result != SQLITE_OK) {
226 g_debug("failed: %s\n", error);
230 /* Get the id of the insert, this is now the unique id for this field*/
231 obj->_priv->id = sqlite3_last_insert_rowid(obj->_priv->sqlHandle);
233 /* update the last inserted row */
234 query = sqlite3_mprintf
235 ("UPDATE 'SearchFields' SET SearchId=%i WHERE id=%i",
236 obj->_priv->id,obj->_priv->id);
237 result = sqlite3_exec(obj->_priv->sqlHandle, query, NULL, NULL, &error);
238 if (result != SQLITE_OK) {
239 g_debug("failed: %s\n", error);
248 free_search_field(SearchField*field)
252 g_free(field->value);
260 match(self, StuffkeeperDataItem *item)
262 GList *node,*list = NULL;
264 if(self->_priv->is_dummy == TRUE)
269 list = self_get_search_fields(self);
270 for(node = g_list_first(list);node && found;node = g_list_next(node))
272 SearchField *field = node->data;
276 if(field->field_type== SEARCH_FIELD_TITLE)
278 comp = stuffkeeper_data_item_get_title(item);
281 else if (field->field_type== SEARCH_FIELD_VALUE)
283 if(field->type == SEARCH_TYPE_IS || field->type == SEARCH_TYPE_IS_NOT)
285 found = stuffkeeper_data_item_has_value_exact(item, field->value);
287 if(field->type == SEARCH_TYPE_CONTAINS || field->type == SEARCH_TYPE_NOT_CONTAINS )
289 found = stuffkeeper_data_item_has_value(item, field->value);
291 /* invert result for the _NOT_ */
292 if(field->type == SEARCH_TYPE_NOT_CONTAINS || field->type == SEARCH_TYPE_IS_NOT) {
297 else if (field->field_type== SEARCH_FIELD_TAG)
299 GList *list2, *node2;
300 list2 = stuffkeeper_data_item_get_tags(item);
303 for(node2 = g_list_first(list2);node2 && !found;node2 = g_list_next(node2))
305 comp = stuffkeeper_data_tag_get_title(STUFFKEEPER_DATA_TAG(node2->data));
306 if(field->type == SEARCH_TYPE_IS || field->type == SEARCH_TYPE_IS_NOT)
308 found = (g_utf8_collate(field->value,comp) == 0);
310 if(field->type == SEARCH_TYPE_CONTAINS || field->type == SEARCH_TYPE_NOT_CONTAINS )
312 gchar *sb1 = g_utf8_casefold(field->value, -1);
313 gchar *sb = g_utf8_normalize(sb1,-1,G_NORMALIZE_ALL_COMPOSE);
314 gchar *sa1 = g_utf8_casefold(comp, -1);
315 gchar *sa = g_utf8_normalize(sa1,-1,G_NORMALIZE_ALL_COMPOSE);
316 found =(strstr(sa,sb) != NULL);
317 g_free(sa1);g_free(sb1);
318 g_free(sa);g_free(sb);
325 /* invert result for the _NOT_ */
326 if(field->type == SEARCH_TYPE_NOT_CONTAINS || field->type == SEARCH_TYPE_IS_NOT) {
331 else if (field->field_type== SEARCH_FIELD_SCHEMA)
333 comp = stuffkeeper_data_schema_get_title(stuffkeeper_data_item_get_schema(item));
338 if(field->type == SEARCH_TYPE_IS || field->type == SEARCH_TYPE_IS_NOT)
340 found = (g_utf8_collate(field->value,comp) == 0);
342 if(field->type == SEARCH_TYPE_CONTAINS || field->type == SEARCH_TYPE_NOT_CONTAINS )
344 gchar *sb1 = g_utf8_casefold(field->value, -1);
345 gchar *sb = g_utf8_normalize(sb1,-1,G_NORMALIZE_ALL_COMPOSE);
346 gchar *sa1 = g_utf8_casefold(comp, -1);
347 gchar *sa = g_utf8_normalize(sa1,-1,G_NORMALIZE_ALL_COMPOSE);
348 found =(strstr(sa,sb) != NULL);
349 g_free(sa1);g_free(sb1);
350 g_free(sa);g_free(sb);
354 /* invert result for the _NOT_ */
355 if(field->type == SEARCH_TYPE_NOT_CONTAINS || field->type == SEARCH_TYPE_IS_NOT) {
364 g_list_foreach(list,(GFunc)self_free_search_field,NULL);
376 get_search_fields(self)
379 char *query = sqlite3_mprintf("SELECT Id,SearchId,FieldType,SearchType,value FROM 'SearchFields' WHERE SearchId=%i and SearchType!=%i",
380 self->_priv->id, SEARCH_TYPE_NONE);
385 r = sqlite3_prepare_v2(self->_priv->sqlHandle, query, -1, &stmt, &tail);
386 if(r == SQLITE_OK ) {
387 while((r = sqlite3_step(stmt)) == SQLITE_ROW && found) {
388 SearchField *field = g_malloc0(sizeof(*field));
389 field->id = sqlite3_column_int(stmt, 0);
390 field->field_id = sqlite3_column_int(stmt, 1);
391 field->field_type = sqlite3_column_int(stmt, 2);
392 field->type = sqlite3_column_int(stmt, 3);
393 field->value = g_strdup((char *)sqlite3_column_text(stmt, 4));
394 list = g_list_append(list, field);
398 sqlite3_finalize(stmt);
404 edit_search_field(self, SearchField *field)
410 /* update the title */
411 query = sqlite3_mprintf("UPDATE 'SearchFields' SET value=%Q , SearchId=%i , Fieldtype=%i , SearchType=%i WHERE id=%i",
417 result = sqlite3_exec(self->_priv->sqlHandle, query, NULL, NULL, &error);
418 if (result != SQLITE_OK) {
419 g_debug("failed: %s\n", error);
422 self_search_changed(self, field);
426 remove_search_field(self, SearchField *field)
432 query = sqlite3_mprintf("DELETE FROM 'SearchFields' WHERE id=%i",field->id);
433 result = sqlite3_exec(self->_priv->sqlHandle, query, NULL, NULL, &error);
434 if (result != SQLITE_OK) {
435 g_debug("failed: %s\n", error);
442 new_search_field(self, SearchType searchtype, SearchFieldType fieldtype, const gchar *value)
444 SearchField *field = g_malloc0(sizeof(*field));
450 query = sqlite3_mprintf("INSERT INTO 'SearchFields' ('SearchId','SearchType','FieldType','value')"
451 "values (%i,%i,%i,%Q);",
457 result = sqlite3_exec(self->_priv->sqlHandle, query, NULL, NULL, &error);
458 if (result != SQLITE_OK) {
459 g_debug("failed: %s\n", error);
463 /* Get the id of the insert, this is now the unique id for this field*/
464 field->id = sqlite3_last_insert_rowid(self->_priv->sqlHandle);
465 field->field_id = self->_priv->id;
466 field->field_type = fieldtype;
467 field->type = searchtype;
468 field->value = g_strdup(value);
481 if(self->_priv->is_dummy == TRUE)
483 return g_strdup(("All the entries"));
485 retv = self_get_string(self,SEARCH_TYPE_NONE, SEARCH_TITLE);
487 retv = g_strdup("Failed to get title");
492 set_title(self, const char *title)
494 if(self->_priv->is_dummy == TRUE)
498 self_set_string(self, SEARCH_TYPE_NONE, SEARCH_TITLE, title);
499 self_search_changed(self, NULL);
506 return self->_priv->id;
511 delete_yourself(self)
517 query = sqlite3_mprintf("DELETE FROM 'SearchFields' WHERE SearchId=%i",self->_priv->id);
518 result = sqlite3_exec(self->_priv->sqlHandle, query, NULL, NULL, &error);
519 if (result != SQLITE_OK) {
520 g_debug("failed: %s\n", error);
534 style_set(self, GtkStyle *style, GtkWidget *wid)
536 g_signal_handlers_block_by_func(G_OBJECT(wid), self_style_set,self);
537 gtk_widget_modify_bg(wid,
539 &((wid)->style->bg[GTK_STATE_SELECTED]));
540 gtk_widget_modify_text((GtkWidget *)gtk_builder_get_object(self->_priv->xml, "title_label_search"),
542 &((wid)->style->text[GTK_STATE_SELECTED]));
543 gtk_widget_modify_fg((GtkWidget *)gtk_builder_get_object(self->_priv->xml, "title_label_search"),
545 &((wid)->style->fg[GTK_STATE_SELECTED]));
546 g_signal_handlers_unblock_by_func(G_OBJECT(wid), self_style_set,self);
552 search_title_changed_real(self)
554 GtkWidget *entry = (GtkWidget *)gtk_builder_get_object(self->_priv->xml, "title_entry");
555 const gchar *title = gtk_entry_get_text(GTK_ENTRY(entry));
556 if(self->_priv->title_timeout)
557 g_source_remove(self->_priv->title_timeout);
558 self->_priv->title_timeout = 0;
562 self_set_title(self,title);
570 search_title_changed(self, GtkWidget *entry)
572 if(self->_priv->title_timeout)
573 g_source_remove(self->_priv->title_timeout);
574 self->_priv->title_timeout = g_timeout_add(1000, (GSourceFunc)self_search_title_changed_real, self);
580 response(self, int response, GtkWidget *dialog )
582 GtkWidget *dialog = (GtkWidget *)gtk_builder_get_object(self->_priv->xml, "edit_search_dialog");
584 if(self->_priv->title_timeout)
586 self_search_title_changed_real(self);
589 gtk_widget_destroy(dialog);
590 g_object_unref(self->_priv->xml);
591 self->_priv->xml = NULL;
596 field_get_value(GtkTreeViewColumn *column, GtkCellRenderer *renderer,GtkTreeModel *model, GtkTreeIter *iter,gpointer data)
598 SearchField *field=NULL;
599 gtk_tree_model_get(model, iter, 0, &field, -1);
604 g_object_set (GTK_CELL_RENDERER (renderer),
605 "text", field->value,
613 field_set_value(self, gchar *path, gchar *new_text, GtkCellRendererText *renderer)
616 GtkTreeModel *model = GTK_TREE_MODEL(self->_priv->store);
619 if(gtk_tree_model_get_iter_from_string(model, &iter, path))
621 SearchField *field = NULL;
622 gtk_tree_model_get(model, &iter, 0, &field, -1);
623 if(field->value) g_free(field->value);
624 field->value = g_strdup(new_text);
625 self_edit_search_field(self, field);
631 field_type_combo_get_value(GtkTreeViewColumn *column, GtkCellRenderer *renderer,GtkTreeModel *model, GtkTreeIter *iter,gpointer data)
633 SearchField *field=NULL;
634 gtk_tree_model_get(model, iter, 0, &field, -1);
639 g_object_set (GTK_CELL_RENDERER (renderer),
640 "text", SearchFieldTypesNames[field->field_type],
649 field_combo_set_value( self,
652 GtkCellRendererCombo *renderer)
655 if(path && new_text){
656 GtkTreeModel *model = GTK_TREE_MODEL(self->_priv->store);
658 SearchField *field = NULL;
661 for(i=0;strcmp(new_text,SearchTypesNames[i]) && i < SEARCH_TYPE_NUM_ITEMS;i++);
662 if(gtk_tree_model_get_iter_from_string(model, &iter, path))
664 gtk_tree_model_get(model, &iter, 0, &field, -1);
666 self_edit_search_field(self, field);
672 field_type_combo_set_value( self,
675 GtkCellRendererCombo *renderer)
678 if(path && new_text){
679 GtkTreeModel *model = GTK_TREE_MODEL(self->_priv->store);
681 SearchField *field = NULL;
684 for(i=0;strcmp(new_text,SearchFieldTypesNames[i]) && i < NUM_SEARCH_FIELD;i++);
685 if(gtk_tree_model_get_iter_from_string(model, &iter, path))
687 gtk_tree_model_get(model, &iter, 0, &field, -1);
688 field->field_type = i;
689 self_edit_search_field(self, field);
697 field_combo_get_value(GtkTreeViewColumn *column, GtkCellRenderer *renderer,GtkTreeModel *model, GtkTreeIter *iter,gpointer data)
699 SearchField *field=NULL;
700 gtk_tree_model_get(model, iter, 0, &field, -1);
705 g_object_set (GTK_CELL_RENDERER (renderer),
706 "text", SearchTypesNames[field->type],
714 add_field(self,GtkWidget *button)
716 SearchField *field = NULL;
717 field = self_new_search_field(self,SEARCH_TYPE_IS,SEARCH_FIELD_TITLE ,_("New Search"));
719 if(self->_priv->store)
722 gtk_list_store_append(self->_priv->store, &iter);
723 gtk_list_store_set(self->_priv->store, &iter,0, field,-1);
726 self_search_changed(self, field);
731 remove_field(self,GtkWidget *button)
733 GtkWidget *tree = (GtkWidget *)gtk_builder_get_object(self->_priv->xml, "treeview_items");
735 GtkTreeModel *model = GTK_TREE_MODEL(self->_priv->store);
736 GtkTreeSelection *selec = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
737 if(gtk_tree_selection_get_selected(selec, &model, &iter))
739 SearchField *field=NULL;
740 gtk_tree_model_get(model, &iter, 0, &field, -1);
741 self_remove_search_field( self, field);
742 gtk_list_store_remove(self->_priv->store,&iter);
743 self_search_changed(self, NULL);
749 edit_search_gui(self)
751 GtkWidget *dialog = NULL;
752 GError *error = NULL;
758 GtkWidget *win = (GtkWidget *)gtk_builder_get_object(self->_priv->xml, "edit_search_dialog");
759 gtk_window_present(GTK_WINDOW(win));
764 self->_priv->xml = gtk_builder_new();
765 gtk_builder_add_from_file(self->_priv->xml, PACKAGE_DATADIR"/edit-search-dialog.ui",&error);
767 g_error("Failed to open ui file: %s\n", error->message);
771 dialog = (GtkWidget *)gtk_builder_get_object(self->_priv->xml, "edit_search_dialog");
773 title = self_get_title(self);
775 gtk_entry_set_text(GTK_ENTRY( gtk_builder_get_object(self->_priv->xml, "title_entry")), title);
779 if(self->_priv->store == NULL)
781 self->_priv->store = gtk_list_store_new(1, G_TYPE_POINTER);
782 list = self_get_search_fields(self);
785 for(node = g_list_first(list);node;node = g_list_next(node))
787 gtk_list_store_append(self->_priv->store, &iter);
788 gtk_list_store_set(self->_priv->store, &iter,0, node->data,-1);
793 if(!(self->_priv->type_store))
796 self->_priv->type_store = gtk_list_store_new(2, G_TYPE_INT,G_TYPE_STRING);
797 for(i=SEARCH_TYPE_IS;i<SEARCH_TYPE_NUM_ITEMS;i++)
799 gtk_list_store_append(self->_priv->type_store, &iter);
800 gtk_list_store_set(self->_priv->type_store, &iter,0,i,1, SearchTypesNames[i],-1);
803 if(!(self->_priv->field_type_store))
806 self->_priv->field_type_store = gtk_list_store_new(2, G_TYPE_INT,G_TYPE_STRING);
807 for(i=SEARCH_FIELD_SCHEMA;i<NUM_SEARCH_FIELD;i++)
809 gtk_list_store_append(self->_priv->field_type_store, &iter);
810 gtk_list_store_set(self->_priv->field_type_store, &iter,0,i,1, SearchFieldTypesNames[i],-1);
814 GtkCellRenderer *renderer;
816 renderer = gtk_cell_renderer_combo_new();
817 g_object_set(G_OBJECT(renderer), "model", GTK_TREE_MODEL(self->_priv->field_type_store), NULL);
818 g_object_set(G_OBJECT(renderer), "text-column", 1,"has-entry", FALSE, NULL);
819 g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL);
820 gtk_tree_view_insert_column_with_data_func(
821 GTK_TREE_VIEW(gtk_builder_get_object(self->_priv->xml, "treeview_items")),
825 self_field_type_combo_get_value,
828 g_signal_connect_swapped(G_OBJECT(renderer), "edited", G_CALLBACK(self_field_type_combo_set_value), self);
830 renderer = gtk_cell_renderer_combo_new();
831 g_object_set(G_OBJECT(renderer), "model", GTK_TREE_MODEL(self->_priv->type_store), NULL);
832 g_object_set(G_OBJECT(renderer), "text-column", 1,"has-entry", FALSE, NULL);
833 g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL);
834 gtk_tree_view_insert_column_with_data_func(
835 GTK_TREE_VIEW(gtk_builder_get_object(self->_priv->xml, "treeview_items")),
839 self_field_combo_get_value,
843 g_signal_connect_swapped(G_OBJECT(renderer), "edited", G_CALLBACK(self_field_combo_set_value), self);
848 renderer = gtk_cell_renderer_text_new();
849 gtk_tree_view_insert_column_with_data_func(
850 GTK_TREE_VIEW(gtk_builder_get_object(self->_priv->xml, "treeview_items")),
854 self_field_get_value,
858 g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL);
859 g_signal_connect_swapped(G_OBJECT(renderer), "edited", G_CALLBACK(self_field_set_value), self);
863 gtk_tree_view_set_model(GTK_TREE_VIEW(gtk_builder_get_object(self->_priv->xml, "treeview_items")), GTK_TREE_MODEL(self->_priv->store));
865 gtk_builder_connect_signals_full(self->_priv->xml, (GtkBuilderConnectFunc)self____builder_xml_connect_foreach, (gpointer)self);
866 gtk_widget_show(dialog);
868 g_signal_connect_swapped(G_OBJECT(dialog), "response", G_CALLBACK(self_response), self);
873 ___builder_xml_connect_foreach(
876 const gchar *signal_name,
877 const gchar *handler_name,
878 GObject *connect_object,
882 static GModule * allsymbols = NULL;
884 printf("%s - %s\n", signal_name, handler_name);
885 if (!allsymbols) allsymbols = g_module_open(NULL, 0);
887 gchar * func_name = g_strdup_printf("stuffkeeper_data_item_search_%s", handler_name);
890 if (!g_module_symbol(allsymbols, func_name, (gpointer)&func)){
891 if (!g_module_symbol(allsymbols, handler_name, (gpointer)&func)) {
892 g_warning("could not find signal handler '%s'.", func_name);
897 g_signal_connect_data(object, signal_name, func, user_data, NULL, G_CONNECT_SWAPPED | flags);