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