| Line | Revision | Contents |
| 1 | 17 | /* Calliope Music Player |
| 2 | 185 | * Copyright 2005-09 Sam Thursfield <ssssam gmail.com> |
| 3 | 17 | * |
| 4 | * This program is free software: you can redistribute it and/or modify | |
| 5 | * it under the terms of the GNU General Public License as published by | |
| 6 | * the Free Software Foundation, either version 3 of the License, or | |
| 7 | * (at your option) any later version. | |
| 8 | * | |
| 9 | * This program is distributed in the hope that it will be useful, | |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 12 | * GNU General Public License for more details. | |
| 13 | * | |
| 14 | * You should have received a copy of the GNU General Public License | |
| 15 | 269 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 16 | 17 | */ |
| 17 | 269 | |
| 18 | 17 | // |
| 19 | // Musicsource is the base class for library, filesource, cdsource etc. It could | |
| 20 | // be thought of as the 'core' of calliope. | |
| 21 | // | |
| 22 | // Here is how the db code functions: see also entry & schema. Results & input | |
| 23 | 24 | // is done in source_views of Entry objects. Each entry can reference other entries. |
| 24 | 17 | // FIXME: this won't be right soon with checkin & checkout |
| 25 | // Implementations differ in how they work; in Library your query returns a new | |
| 26 | // tree representing the data you queried while CdSource and FileSource (and any | |
| 27 | // other descendents of MusicSourceImporting) return the actual entry objects | |
| 28 | // storted in the source. For this reason entries must not be read or written to | |
| 29 | // outside the GDK thread unless you are sure they come from library or something. | |
| 30 | // | |
| 31 | // FIXME: this makes multithreading a whole lot less use, it would be nice to be | |
| 32 | // able to copy entry trees or give them mutexes or something. Perhaps each | |
| 33 | // source should have its own mutex to save blocking on gdk lock. Actually this | |
| 34 | // is a pretty good idea !! | |
| 35 | // | |
| 36 | 269 | // Any query returns all the entries pointed to by the queried entry, but not |
| 37 | 17 | // those that point to it. For example, if you query a Recording object you will |
| 38 | // get its Composition, Artist and the composition's artist. You will not get | |
| 39 | // any of the Files or the Tracks for that recording, which would need to be | |
| 40 | // found using query_entry_children. | |
| 41 | // | |
| 42 | 269 | // Any queried entry should be unref'ed when done with. |
| 43 | 17 | // |
| 44 | 24 | |
| 45 | // music_source_add_entry accepts a tree of entries to be added. This | |
| 46 | 17 | // function checks if the entry exists in the db already and if not, adds it. |
| 47 | // The id field is totally ignored. Returns the new id of the entry. I know it | |
| 48 | // would be more useful to return the entry itself but in the case of library | |
| 49 | // for example, we could be passing an entry in an importing db to add-entry. | |
| 50 | // This clearly can't be modified so library would have to create a brand new | |
| 51 | // entry to return - which is pretty wasteful and its more efficient I think to | |
| 52 | 269 | // just return the id and you have to query it if you still need the entry. |
| 53 | 17 | // |
| 54 | ||
| 55 | 24 | // MusicSourceView provides a GtkTreeModel interface on the database, with a completely |
| 56 | // customisable structure. This is how sorting, etc is provided. | |
| 57 | // | |
| 58 | ||
| 59 | 17 | #ifndef _MUSIC_SOURCE_H |
| 60 | #define _MUSIC_SOURCE_H | |
| 61 | ||
| 62 | #include <glib-object.h> | |
| 63 | 24 | //#include "conftool.h" |
| 64 | 130 | #include "debug.h" |
| 65 | 17 | #include "entry.h" |
| 66 | ||
| 67 | #define MUSIC_SOURCE_TYPE (music_source_get_type ()) | |
| 68 | #define MUSIC_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MUSIC_SOURCE_TYPE, MusicSource)) | |
| 69 | #define MUSIC_SOURCE_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), MUSIC_SOURCE_TYPE, MusicSourceClass)) | |
| 70 | #define IS_MUSIC_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MUSIC_SOURCE_TYPE)) | |
| 71 | #define IS_MUSIC_SOURCE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), MUSIC_SOURCE_TYPE)) | |
| 72 | #define MUSIC_SOURCE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), MUSIC_SOURCE_TYPE, MusicSourceClass)) | |
| 73 | ||
| 74 | 24 | typedef struct _MusicSourceView MusicSourceView; |
| 75 | ||
| 76 | 17 | typedef struct MusicSourcePrivate MusicSourcePrivate; |
| 77 | typedef struct { | |
| 78 | GObject parent; | |
| 79 | MusicSourcePrivate *priv; | |
| 80 | 269 | } MusicSource; |
| 81 | 17 | |
| 82 | 215 | #include "viewconfig.h" |
| 83 | ||
| 84 | 17 | typedef struct { |
| 85 | GObjectClass parent; | |
| 86 | gboolean (*is_empty)(MusicSource *self); | |
| 87 | gboolean (*get_loaded)(MusicSource *self); | |
| 88 | char *(*get_summary)(MusicSource *self); | |
| 89 | ||
| 90 | 269 | int (*get_n_entries)(MusicSource *self, EntryType type, int *highest_id, int *dead); |
| 91 | int (*get_group_id)(MusicSource *self, int recording_id); | |
| 92 | 17 | gboolean (*is_valid_id)(MusicSource *self, EntryType type, int id); |
| 93 | ||
| 94 | Entry *(*query_entry)(MusicSource *self, EntryType type, int id); | |
| 95 | 92 | GSList *(*query_relations)(MusicSource *self, EntryType local_type, int local_id, int relation_apid); |
| 96 | 102 | int (*query_n_relations)(MusicSource *musicsource, int local_id, EntryType foreign_type, int foreign_property_id, int limit); |
| 97 | ||
| 98 | 17 | GSList *(*query_entry_children)(MusicSource *self, int parent_id, EntryType child_entry_type, int child_property); |
| 99 | GSList *(*query_entry_children_ids)(MusicSource *self, int parent_id, EntryType child_entry_type, int child_property); | |
| 100 | GSList *(*query_ids)(MusicSource *self, EntryType entry_type); | |
| 101 | GSList *(*query_matching_except)(MusicSource *musicsource, Entry *entry, int ignored_property_absolute_id); | |
| 102 | 269 | |
| 103 | 215 | MusicSourceView *(*create_view)(MusicSource *source, ViewConfig *config); |
| 104 | 17 | |
| 105 | 451 | void (*add_entry_internal) (MusicSource *self, Entry *entry); |
| 106 | 452 | void (*update_entry_internal) (MusicSource *self, Entry *entry, int shadow_override_id, |
| 107 | void *shadow_override_value); | |
| 108 | 451 | void (*remove_entry_internal) (MusicSource *self, EntryType type, int id); |
| 109 | void (*update_foreign_keys) (MusicSource *self, Entry *from_entry, Entry *to_entry); | |
| 110 | ||
| 111 | 17 | void (*begin_transaction)(MusicSource *self); |
| 112 | void (*end_transaction)(MusicSource *self); | |
| 113 | void (*update_entry_property)(MusicSource *self, EntryType entry_type, int entry_id, int property_id, const void *value); | |
| 114 | int (*add_entry)(MusicSource *self, Entry *entry); | |
| 115 | void (*remove_entry)(MusicSource *self, EntryType type, int id); | |
| 116 | Entry *(*checkout_entry)(MusicSource *self, EntryType type, int id); | |
| 117 | void (*checkin_entry)(MusicSource *self, Entry *entry); | |
| 118 | 95 | void (*flush)(MusicSource *self); |
| 119 | 17 | void (*wipe)(MusicSource *self); |
| 120 | 206 | void (*dump)(MusicSource *self, gboolean detailed); |
| 121 | 17 | void (*export_recordings)(MusicSource *self, GSList *recording_list, MusicSource *destination); |
| 122 | 451 | |
| 123 | /* Internal functions */ | |
| 124 | int (*find_entry) (MusicSource *self, Entry *entry, Entry *caller); | |
| 125 | 17 | } MusicSourceClass; |
| 126 | ||
| 127 | 269 | typedef enum { |
| 128 | 17 | MUSIC_SOURCE_SIGNAL_STATUS_CHANGED, |
| 129 | 144 | MUSIC_SOURCE_SIGNAL_BUSY, |
| 130 | 269 | |
| 131 | 17 | MUSIC_SOURCE_SIGNAL_WIPED, |
| 132 | MUSIC_SOURCE_SIGNAL_COUNT | |
| 133 | 269 | } MusicSourceSignal; |
| 134 | 17 | |
| 135 | GType music_source_get_type(void) G_GNUC_CONST; | |
| 136 | ||
| 137 | gboolean music_source_is_empty(MusicSource *self); | |
| 138 | // FIXME: is this still necessary ?? | |
| 139 | gboolean music_source_get_loaded(MusicSource *self); | |
| 140 | const char *music_source_get_title(MusicSource *self); | |
| 141 | char *music_source_get_summary(MusicSource *self); | |
| 142 | ||
| 143 | 358 | /* We can't use gsignal for the entry change notification because of the complex emission strategy |
| 144 | * it needs. This does take away the gsignal benefit of being cross-language, but also adds a lot | |
| 145 | * of speed. Perhaps if being monolingual ever becomes a problem a two-stage emission process is the | |
| 146 | * best answer where both C functions and glib closures can be connected. */ | |
| 147 | typedef void (*EntryNotifyFunc)(MusicSource *music_source, Entry *old_entry, Entry *new_entry, | |
| 148 | void *user_data); | |
| 149 | void music_source_connect_entry_notify (MusicSource *self, EntryType detail, | |
| 150 | const gboolean watch_only, EntryNotifyFunc callback, | |
| 151 | void *user_data); | |
| 152 | 393 | void music_source_disconnect_entry_notify (MusicSource *self, EntryType detail, |
| 153 | EntryNotifyFunc callback, void *user_data); | |
| 154 | void music_source_connect_entry_notify_object (MusicSource *self, EntryType detail, | |
| 155 | const gboolean watch_only, EntryNotifyFunc callback, | |
| 156 | GObject *object); | |
| 157 | 358 | |
| 158 | 17 | // music_source_get_n_entries returns the number of a given type of entries. |
| 159 | // highest_id and n_dead reflect the way removal of entries works: because it is | |
| 160 | // impractical to change entry id's during runtime if an entry is deleted it's | |
| 161 | // id is simply marked as dead. Therefore if you want to refer to every entry by | |
| 162 | // id or something like that (like how shuffle playorders work) don't be naive | |
| 163 | 269 | // and just use the total number of entries (which = highest_id-n_dead) |
| 164 | 17 | // |
| 165 | 269 | int music_source_get_n_entries(MusicSource *self, EntryType type, int *highest_id, int *dead); |
| 166 | 17 | |
| 167 | gboolean music_source_is_valid_id(MusicSource *source, EntryType type, int id); | |
| 168 | ||
| 169 | 130 | #ifdef ENTRY_TRACK_REFERENCE_OWNERS |
| 170 | 17 | Entry *music_source_query_entry_tracking(MusicSource *source, EntryType type, int id, const char *owner_name, const char *file, int line_no); |
| 171 | GSList *music_source_query_entry_children_tracking(MusicSource *source, int parent_id, EntryType child_entry_type, int child_property, const char *owner_name, const char *file, int line_no); | |
| 172 | 92 | GSList *music_source_query_relations_tracking(MusicSource *source, EntryType local_type, int local_id, int relation_apid, const char *owner_name, const char *file, int line_no); |
| 173 | 17 | GSList *music_source_query_matching_except_tracking(MusicSource *musicsource, Entry *entry, int ignored_property_absolute_id, const char *owner_name, const char *file, int line_no); |
| 174 | Entry *music_source_checkout_entry_tracking(MusicSource *self, EntryType type, int id, const char *owner_name, const char *file, int line_no); | |
| 175 | void music_source_checkin_entry_tracking(MusicSource *self, Entry *entry, const char *owner_name, const char *file, int line_no); | |
| 176 | ||
| 177 | #define music_source_query_entry(s, t, i, o) music_source_query_entry_tracking(s, t, i, o, __FILE__, __LINE__) | |
| 178 | #define music_source_query_entry_children(s, p, t, i, o) music_source_query_entry_children_tracking(s, p, t, i, o, __FILE__, __LINE__) | |
| 179 | 93 | #define music_source_query_relations(s, t, i, r, o) music_source_query_relations_tracking(s, t, i, r, o, __FILE__, __LINE__) |
| 180 | 17 | #define music_source_query_matching_except(s, e, i, o) music_source_query_matching_except_tracking(s, e, i, o, __FILE__, __LINE__) |
| 181 | #define music_source_checkout_entry(s, t, i, o) music_source_checkout_entry_tracking(s, t, i, o, __FILE__, __LINE__) | |
| 182 | #define music_source_checkin_entry(s, e, o) music_source_checkin_entry_tracking(s, e, o, __FILE__, __LINE__) | |
| 183 | #else | |
| 184 | Entry *music_source_query_entry_internal(MusicSource *source, EntryType type, int id); | |
| 185 | ||
| 186 | // Returns list of Entry * | |
| 187 | 92 | // FIXME: obsoleted by query_relations |
| 188 | 17 | GSList *music_source_query_entry_children_internal(MusicSource *source, int parent_id, EntryType child_entry_type, int child_property); |
| 189 | ||
| 190 | 92 | // Returns list of Entry * |
| 191 | GSList *music_source_query_relations_internal(MusicSource *self, EntryType local_type, int local_id, int relation_apid); | |
| 192 | ||
| 193 | 17 | // Finds all entries that match the given one, with the exception of one |
| 194 | // property. Remember all children are checked in matching so for example | |
| 195 | 269 | // you can find releases that match on everything but album artist. Will |
| 196 | 17 | // never return the given entry. |
| 197 | 269 | // |
| 198 | 17 | GSList *music_source_query_matching_except_internal(MusicSource *musicsource, Entry *entry, int ignored_property_absolute_id); |
| 199 | ||
| 200 | // music_source_checkout_entry & music_source_checkin_entry are for if you want | |
| 201 | // to edit an entry a whole lot, like if you just read the tags from a file for | |
| 202 | 269 | // example. checkout returns a source_view of entries similar to query_entry but these |
| 203 | // ones you can modify. Any entries in the source_view with id 0 will be added | |
| 204 | 17 | // automatically during checkin. For examples of use see files.c or cdsource. |
| 205 | 269 | // !! Once an entry has been checked in you cannot use it !!! |
| 206 | 17 | // !! You will have to query it again !!! |
| 207 | // | |
| 208 | Entry *music_source_checkout_entry_internal(MusicSource *self, EntryType type, int id); | |
| 209 | void music_source_checkin_entry_internal(MusicSource *self, Entry *entry); | |
| 210 | 269 | |
| 211 | 17 | #define music_source_query_entry(s, t, i, o) music_source_query_entry_internal(s, t, i) |
| 212 | #define music_source_query_entry_children(s, p, t, i, o) music_source_query_entry_children_internal(s, p, t, i) | |
| 213 | 92 | #define music_source_query_relations(s, t, i, r, o) music_source_query_relations_internal(s, t, i, r) |
| 214 | 269 | #define music_source_query_matching_except(s, e, i, o) music_source_query_matching_except_internal(s, e, i) |
| 215 | 17 | #define music_source_checkout_entry(s, t, i, o) music_source_checkout_entry_internal(s, t, i) |
| 216 | #define music_source_checkin_entry(s, e, o) music_source_checkin_entry_internal(s, e) | |
| 217 | #endif | |
| 218 | ||
| 219 | 24 | // FIXME: how many of these query ones do we need now there is views ?? |
| 220 | ||
| 221 | 102 | int music_source_query_n_relations(MusicSource *musicsource, int local_id, EntryType foreign_type, int foreign_property_id, int limit); |
| 222 | ||
| 223 | 17 | // Returns list of int |
| 224 | GSList *music_source_query_entry_children_ids(MusicSource *source, int parent_id, EntryType child_entry_type, int child_property); | |
| 225 | ||
| 226 | // Returns list of int | |
| 227 | GSList *music_source_query_ids(MusicSource *source, int entry_type); | |
| 228 | ||
| 229 | 215 | MusicSourceView *music_source_create_view(MusicSource *source, ViewConfig *config); |
| 230 | 24 | |
| 231 | 269 | // These only have an effect on the library. begin_transaction should be called before you make lots of writes to the database, and |
| 232 | 17 | // end_transaction should be called right after, otherwise it will go like 100x slower. |
| 233 | void music_source_begin_transaction(MusicSource *self); | |
| 234 | void music_source_end_transaction(MusicSource *self); | |
| 235 | ||
| 236 | int music_source_add_entry(MusicSource *self, Entry *entry); | |
| 237 | ||
| 238 | // music_source_remove_entry deletes a specific entry by ID. | |
| 239 | // NOTE: it is important !! that you don't hold any references to related entries | |
| 240 | // when you remove an entry - for example, if I remove the last track of an album | |
| 241 | // its refcount will drop to 1 and it will be removed as well. Unless I still | |
| 242 | // hold a reference somewhere else in which case it will hang around forever !! | |
| 243 | // This applies to importing sources anyway. | |
| 244 | // FIXME: I guess we should do something about this issue ? | |
| 245 | void music_source_remove_entry(MusicSource *self, EntryType type, int id); | |
| 246 | ||
| 247 | // music_source_update_entry_property changes one property of an entry. If lots | |
| 248 | 269 | // of changes are being made checkin/checkout should be used instead to prevent |
| 249 | 17 | // emitting the same changed signals over and over again. |
| 250 | // | |
| 251 | void music_source_update_entry_property(MusicSource *self, EntryType entry_type, int entry_id, int property_id, const void *value); | |
| 252 | ||
| 253 | 95 | void music_source_flush(MusicSource *self); |
| 254 | 17 | void music_source_wipe(MusicSource *self); |
| 255 | void music_source_export_recordings(MusicSource *self, GSList *recording_list, MusicSource *destination); | |
| 256 | ||
| 257 | #endif |
Loggerhead is a web-based interface for Bazaar branches