1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  *
3  *  Copyright (C) 2008  Jonathan Matthew  <jonathan@d14n.org>
4  *
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.
9  *
10  *  The Rhythmbox authors hereby grant permission for non-GPL compatible
11  *  GStreamer plugins to be used and distributed together with GStreamer
12  *  and Rhythmbox. This permission is above and beyond the permissions granted
13  *  by the GPL license by which Rhythmbox is covered. If you modify this code
14  *  you may extend this exception to your version of the code, but you are not
15  *  obligated to do so. If you do not wish to do so, delete this exception
16  *  statement from your version.
17  *
18  *  This program is distributed in the hope that it will be useful,
19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *  GNU General Public License for more details.
22  *
23  *  You should have received a copy of the GNU General Public License
24  *  along with this program; if not, write to the Free Software
25  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
26  *
27  */
28 
29 /**
30  * SECTION:rb-source-search
31  * @short_description: Base class for source search implementations
32  *
33  * These translate the text in the search entry box into a
34  * RhythmDBQuery.  The basic implementation will return
35  * a query like RHYTHMDB_QUERY_PROP_LIKE, RHYTHMDB_PROP_SEARCH_MATCH,
36  * text.  Simple variants can restrict the search to single
37  * properties (artist, album, genre).  More complicated searches
38  * could implement something like the Xesam User Query spec.
39  *
40  * The source header finds the search instance to use by looking
41  * for the 'rb-source-search' data item on the active search
42  * action.
43  */
44 
45 #include "config.h"
46 
47 #include "rb-source.h"
48 #include "rb-source-search.h"
49 
50 static void	rb_source_search_class_init (RBSourceSearchClass *klass);
51 static void	rb_source_search_init (RBSourceSearch *search);
52 
G_DEFINE_TYPE(RBSourceSearch,rb_source_search,G_TYPE_OBJECT)53 G_DEFINE_TYPE (RBSourceSearch, rb_source_search, G_TYPE_OBJECT)
54 
55 #define RB_SOURCE_SEARCH_DATA_ITEM	"rb-source-search"
56 
57 static gboolean
58 default_is_subset (RBSourceSearch *search, const char *current, const char *next)
59 {
60 	/* the most common searches will return a strict subset if the
61 	 * next search is the current search with a suffix.
62 	 */
63 	return (current != NULL && g_str_has_prefix (next, current));
64 }
65 
66 static char *
default_get_description(RBSourceSearch * search)67 default_get_description (RBSourceSearch *search)
68 {
69 	return g_strdup ("");
70 }
71 
72 static void
rb_source_search_class_init(RBSourceSearchClass * klass)73 rb_source_search_class_init (RBSourceSearchClass *klass)
74 {
75 	klass->searches = g_hash_table_new (g_str_hash, g_str_equal);
76 
77 	klass->is_subset = default_is_subset;
78 	klass->get_description = default_get_description;
79 }
80 
81 static void
rb_source_search_init(RBSourceSearch * search)82 rb_source_search_init (RBSourceSearch *search)
83 {
84 	/* nothing */
85 }
86 
87 /**
88  * rb_source_search_get_by_name:
89  * @name: name to look up
90  *
91  * Finds the registered search instance with the specified name
92  *
93  * Returns: (transfer none): search instance, or NULL if not found
94  */
95 RBSourceSearch *
rb_source_search_get_by_name(const char * name)96 rb_source_search_get_by_name (const char *name)
97 {
98 	RBSourceSearchClass *klass;
99 	klass = RB_SOURCE_SEARCH_CLASS (g_type_class_peek (RB_TYPE_SOURCE_SEARCH));
100 	return g_hash_table_lookup (klass->searches, name);
101 }
102 
103 /**
104  * rb_source_search_register:
105  * @search: search instance to register
106  * @name: name to register
107  *
108  * Registers a named search instance that can be used in menus and
109  * search action states.
110  */
111 void
rb_source_search_register(RBSourceSearch * search,const char * name)112 rb_source_search_register (RBSourceSearch *search, const char *name)
113 {
114 	RBSourceSearchClass *klass;
115 	klass = RB_SOURCE_SEARCH_CLASS (g_type_class_peek (RB_TYPE_SOURCE_SEARCH));
116 	g_hash_table_insert (klass->searches, g_strdup (name), search);
117 }
118 
119 /**
120  * rb_source_search_is_subset:
121  * @search: a #RBSourceSearch
122  * @current: the current search text (or NULL if the current search was done with a different
123  *    search implementation and so cannot be considered)
124  * @next: the new search text
125  *
126  * Determines whether the new search text will result in a
127  * subset of entries matched by the previous search.  This is
128  * used to optimise the search query.
129  *
130  * Return value: TRUE iff the new search text will match a subset of those matched by the current search.
131  */
132 gboolean
rb_source_search_is_subset(RBSourceSearch * search,const char * current,const char * next)133 rb_source_search_is_subset (RBSourceSearch *search, const char *current, const char *next)
134 {
135 	RBSourceSearchClass *klass = RB_SOURCE_SEARCH_GET_CLASS (search);
136 	return klass->is_subset (search, current, next);
137 }
138 
139 /**
140  * rb_source_search_get_description:
141  * @search: a #RBSourceSearch
142  *
143  * Returns a description of the search suitable for displaying in a menu
144  *
145  * Return value: description string
146  */
147 char *
rb_source_search_get_description(RBSourceSearch * search)148 rb_source_search_get_description (RBSourceSearch *search)
149 {
150 	RBSourceSearchClass *klass = RB_SOURCE_SEARCH_GET_CLASS (search);
151 	return klass->get_description (search);
152 }
153 
154 /**
155  * rb_source_search_create_query:
156  * @search: a #RBSourceSearch
157  * @db: the #RhythmDB
158  * @search_text: the search text
159  *
160  * Creates a #RhythmDBQuery from the user's search text.
161  *
162  * Return value: (transfer full): #RhythmDBQuery for the source to use
163  */
164 RhythmDBQuery *
rb_source_search_create_query(RBSourceSearch * search,RhythmDB * db,const char * search_text)165 rb_source_search_create_query (RBSourceSearch *search, RhythmDB *db, const char *search_text)
166 {
167 	RBSourceSearchClass *klass = RB_SOURCE_SEARCH_GET_CLASS (search);
168 	g_assert (klass->create_query);
169 	return klass->create_query (search, db, search_text);
170 }
171 
172 /**
173  * _rb_source_search_create_simple_query:
174  * @search: the #RBSourceSearch
175  * @db: the #RhythmDB
176  * @search_text: the search text such as RHYTHMDB_PROP_SEARCH_MATCH
177  * @search_prop: the search property
178  *
179  * Creates a basic search query.
180  *
181  * Return value: (transfer full): the #RhythmDBQuery for the search text and property, or NULL
182  *   if no search text is specified.
183  */
184 RhythmDBQuery *
_rb_source_search_create_simple_query(RBSourceSearch * search,RhythmDB * db,const char * search_text,RhythmDBPropType search_prop)185 _rb_source_search_create_simple_query (RBSourceSearch *search, RhythmDB *db, const char *search_text, RhythmDBPropType search_prop)
186 {
187 	if (search_text[0] == '\0')
188 		return NULL;
189 
190 	return rhythmdb_query_parse (db,
191 				     RHYTHMDB_QUERY_PROP_LIKE,
192 				     search_prop,
193 				     search_text,
194 				     RHYTHMDB_QUERY_END);
195 }
196 
197 /**
198  * rb_source_search_add_to_menu:
199  * @menu: #GMenu instance to populate
200  * @action_namespace: muxer namespace for the action ("app" or "win")
201  * @action: search action to attach the menu item to
202  * @name: name of the search instance to add
203  *
204  * Adds a registered search instance to a search menu.
205  */
206 void
rb_source_search_add_to_menu(GMenu * menu,const char * action_namespace,GAction * action,const char * name)207 rb_source_search_add_to_menu (GMenu *menu, const char *action_namespace, GAction *action, const char *name)
208 {
209 	GMenuItem *item;
210 	RBSourceSearch *search;
211 	char *action_name;
212 
213 	search = rb_source_search_get_by_name (name);
214 	g_assert (search != NULL);
215 
216 	if (action_namespace != NULL) {
217 		action_name = g_strdup_printf ("%s.%s", action_namespace, g_action_get_name (action));
218 	} else {
219 		action_name = g_strdup (g_action_get_name (action));
220 	}
221 
222 	item = g_menu_item_new (rb_source_search_get_description (search), NULL);
223 	g_menu_item_set_action_and_target (item, action_name, "s", name);
224 	g_menu_append_item (menu, item);
225 
226 	g_free (action_name);
227 }
228