15
* along with this program. If not, see <http://www.gnu.org/licenses/>.
15
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18
// Library: a musicsource which stores metadata in an SQL database.
18
/* library: a music_source which stores metadata in an SQL database.
20
// Entries are cached because querying one can mean querying a whole bunch of entries and it ends up working very slowly. One possible
20
* * Entries are cached because querying one can mean querying a whole bunch of entries and it
21
// is to filter which properties we return, but at the moment its easiest to just implement a cache for entries so that methods like
21
* ends up working very slowly. One possible is to filter which properties we return, but at
22
// musicsource::track-added, which is called every time a track is added and immediately queries everything that was just added, don't
22
* the moment its easiest to just implement a cache for entries so that methods like
23
// run slow to the point of death.
23
* music_source::track-added, which is called every time a track is added and immediately
24
//
24
* queries everything that was just added, don't run slow to the point of death.
26
/* Strange way of getting strptime */
27
/* Strange way of getting strptime */
27
#define _GNU_SOURCE
28
#define _GNU_SOURCE
61
GHashTable *cache[ENTRY_TYPE_COUNT];
62
GHashTable *cache[ENTRY_TYPE_COUNT];
64
enum { PROP_0, PROP_FILE_NAME };
65
enum { PROP_0, PROP_FILE_NAME };
66
static GObject *constructor(GType type, guint n_construct_properties, GObjectConstructParam *construct_params);
67
static GObject *constructor(GType type, guint n_construct_properties, GObjectConstructParam *construct_params);
67
static void finalize(GObject *self);
68
static void finalize(GObject *self);
93
static gboolean is_empty(MusicSource *source);
94
static gboolean is_empty(MusicSource *source);
94
static gboolean get_loaded(MusicSource *source);
95
static gboolean get_loaded(MusicSource *source);
95
static char *get_summary(MusicSource *self);
96
static char *get_summary(MusicSource *self);
96
static int get_n_entries(MusicSource *musicsource, EntryType type, int *highest_id, int *n_dead);
97
static int get_n_entries(MusicSource *music_source, EntryType type, int *highest_id, int *n_dead);
97
static gboolean is_valid_id(MusicSource *source, EntryType type, int id);
98
static gboolean is_valid_id(MusicSource *source, EntryType type, int id);
98
static Entry *query_entry(MusicSource *source, EntryType type, int id);
99
static Entry *query_entry(MusicSource *source, EntryType type, int id);
99
static int query_n_relations (MusicSource *music_source, int local_id, EntryType foreign_type,
100
static int query_n_relations (MusicSource *music_source, int local_id, EntryType foreign_type,
100
int foreign_property_id, int limit);
101
int foreign_property_id, int limit);
101
static GSList *query_entry_children(MusicSource *musicsource, int parent_id, EntryType child_entry_type, int child_property);
102
static GSList *query_entry_children(MusicSource *music_source, int parent_id, EntryType child_entry_type, int child_property);
102
static GSList *query_entry_children_ids(MusicSource *musicsource, int parent_id, EntryType child_entry_type, int child_property);
103
static GSList *query_entry_children_ids(MusicSource *music_source, int parent_id, EntryType child_entry_type, int child_property);
103
static GSList *query_relations(MusicSource *musicsource, EntryType local_type, int local_id, int relation_apid);
104
static GSList *query_relations(MusicSource *music_source, EntryType local_type, int local_id, int relation_apid);
104
static GSList *query_ids(MusicSource *source, EntryType entry_type);
105
static GSList *query_ids(MusicSource *source, EntryType entry_type);
105
static GSList *query_matching_except(MusicSource *musicsource, Entry *entry, int ignored_apid);
106
static GSList *query_matching_except(MusicSource *music_source, Entry *entry, int ignored_apid);
107
static MusicSourceView *create_view(MusicSource *musicsource, ViewConfig *config);
108
static MusicSourceView *create_view(MusicSource *music_source, ViewConfig *config);
109
static void begin_transaction(MusicSource *self);
110
static void begin_transaction(MusicSource *self);
110
static void end_transaction(MusicSource *self);
111
static void end_transaction(MusicSource *self);
111
static void update_entry_property (MusicSource *self, EntryType entry_type, int entry_id,
112
static void update_entry_property (MusicSource *self, EntryType entry_type, int entry_id,
112
int property_id, const void *value);
113
int property_id, const void *value);
113
static int add_entry(MusicSource *musicsource, Entry *entry);
114
static int add_entry(MusicSource *music_source, Entry *entry);
114
static void remove_entry(MusicSource *musicsource, EntryType type, int id);
115
static void remove_entry(MusicSource *music_source, EntryType type, int id);
115
static Entry *checkout_entry(MusicSource *musicsource, EntryType type, int id);
116
static Entry *checkout_entry(MusicSource *music_source, EntryType type, int id);
116
static void checkin_entry(MusicSource *musicsource, Entry *entry);
117
static void checkin_entry(MusicSource *music_source, Entry *entry);
117
static void flush(MusicSource *musicsource);
118
static void flush(MusicSource *music_source);
119
static void dump (MusicSource *musicsource, gboolean detailed);
120
static void dump (MusicSource *music_source, gboolean detailed);
121
//Entry *add_recursive(Library *self, Entry *entry, Entry *caller);
122
//Entry *add_recursive(Library *self, Entry *entry, Entry *caller);
122
//Entry *checkin_recursive(Library *self, Entry *entry, Entry *caller, gboolean touch_only,
123
//Entry *checkin_recursive(Library *self, Entry *entry, Entry *caller, gboolean touch_only,
166
g_type_class_add_private(cl, sizeof(LibraryPrivate));
167
g_type_class_add_private(cl, sizeof(LibraryPrivate));
169
static void library_init(Library *self) {
170
static void library_init (Library *self) {
170
SP=G_TYPE_INSTANCE_GET_PRIVATE(self, LIBRARY_TYPE, LibraryPrivate);
171
SP = G_TYPE_INSTANCE_GET_PRIVATE(self, LIBRARY_TYPE, LibraryPrivate);
172
SP->file_name=NULL;
173
SP->file_name=NULL;
173
SP->failed=FALSE;
174
SP->failed=FALSE;
175
cache_init(self);
176
cache_init(self);
178
static GObject *constructor(GType type, guint n_construct_properties, GObjectConstructParam *construct_params) {
179
static GObject *constructor (GType type, guint n_construct_properties,
180
GObjectConstructParam *construct_params) {
179
GObject *object = ((GObjectClass *)library_parent_class)->constructor(type, n_construct_properties, construct_params);
181
GObject *object = ((GObjectClass *)library_parent_class)->constructor(type, n_construct_properties, construct_params);
180
Library *self = LIBRARY(object);
182
Library *self = LIBRARY(object);
190
// Precalculate fill-entry queries.
192
// Precalculate fill-entry queries.
192
SP->fill_entry_query = //g_new(char *, ENTRY_TYPE_COUNT);
193
SP->fill_entry_query = g_new(sqlite3_stmt *, ENTRY_TYPE_COUNT);
194
SP->fill_entry_query = g_new(sqlite3_stmt *, ENTRY_TYPE_COUNT);
194
for (int i=0; i<ENTRY_TYPE_COUNT; i++) {
195
for (int i=0; i<ENTRY_TYPE_COUNT; i++) {
195
char *sql;
196
char *sql;
207
sql = g_strdup_printf("SELECT %s FROM _%s%s WHERE _%s.id=$id",
208
sql = g_strdup_printf("SELECT %s FROM _%s%s WHERE _%s.id=$id",
208
projection->str, entry_type_name[i],
209
projection->str, entry_type_name[i],
209
joins->str, entry_type_name[i]);
210
joins->str, entry_type_name[i]);
210
g_string_free (projection, TRUE);
211
g_string_free (projection, TRUE);
214
g_free (sql);
215
g_free (sql);
217
// ui_manager_add_actions(ui, actions, G_N_ELEMENTS(actions), self);
218
// SP->ui_merge_id=gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui), ui_descr, -1, NULL);
220
// This needs to be done after the database has loaded of course! Or the view can't populate from us.
221
// Do we want default view to be per source type, per source instance, random, what?
222
// Play orders are always the same atm. Don't want that.
223
//MusicStoreType view_type=gconf_get_enum(gconf, music_store_options, KEY_LIBRARY_DEFAULT_VIEW);
224
//music_source_set_view_type(MUSIC_SOURCE(self), view_type);
226
return object;
218
return object;
229
static void finalize(GObject *object) {
221
static void finalize (GObject *object) {
230
Library *self=LIBRARY(object);
222
Library *self = LIBRARY(object);
232
//MusicStoreType view_type=music_source_get_view_type(MUSIC_SOURCE(self));
233
//gconf_set_enum(gconf, music_store_options, KEY_LIBRARY_DEFAULT_VIEW, view_type);
234
for (int i=0; i<ENTRY_TYPE_COUNT; i++)
224
for (int i=0; i<ENTRY_TYPE_COUNT; i++)
235
sqlite3_finalize (SP->fill_entry_query[i]);
225
sqlite3_finalize (SP->fill_entry_query[i]);
236
g_free (SP->fill_entry_query);
226
g_free (SP->fill_entry_query);
238
cache_free(self);
228
cache_free(self);
239
db_free(self);
229
db_free(self);
241
//gtk_ui_manager_remove_ui(GTK_UI_MANAGER(ui), SP->ui_merge_id);
242
G_OBJECT_CLASS(library_parent_class)->finalize(object);
231
G_OBJECT_CLASS(library_parent_class)->finalize(object);
245
static void set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) {
234
static void set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) {
246
Library *self=LIBRARY(object);
235
Library *self=LIBRARY(object);
247
switch(prop_id) {
236
switch(prop_id) {
248
case PROP_FILE_NAME: {
237
case PROP_FILE_NAME: {
249
const char *file_name=g_value_get_string(value);
238
const char *file_name=g_value_get_string(value);
250
if (g_path_is_absolute(file_name))
239
if (g_path_is_absolute(file_name))
261
MusicSource *library_new(const char *file_name) {
250
MusicSource *library_new (const char *file_name) {
262
Library *self=g_object_new(LIBRARY_TYPE, "filename", file_name, NULL);
251
Library *self = g_object_new (LIBRARY_TYPE, "filename", file_name, NULL);
264
if (SP->failed) {
253
if (SP->failed) {
265
g_object_ref_sink(self); g_object_unref(self);
254
g_object_ref_sink (self); g_object_unref (self);
266
return NULL;
255
return NULL;
267
} else {
256
} else {
268
return MUSIC_SOURCE(self);
257
return MUSIC_SOURCE (self);
280
return SP->db;
269
return SP->db;
283
static void db_init(Library *self) {
272
static void db_init (Library *self) {
284
int result=sqlite3_open(SP->file_name, &SP->db);
273
int result=sqlite3_open(SP->file_name, &SP->db);
286
if (result!=SQLITE_OK) {
275
if (result!=SQLITE_OK) {
534
// FIXME: do these still help now code is rearranged? What others can we make ??
523
// FIXME: do these still help now code is rearranged? What others can we make ??
535
// FIXME: can we make more of these ??
524
// FIXME: can we make more of these ??
536
// These are dropped when a transaction starts and created when it ends, in the musicsource code below.
525
// These are dropped when a transaction starts and created when it ends, in the music_source code below.
537
//db_action(self, NULL, "CREATE INDEX track_idx ON track(release_id, number);");
526
//db_action(self, NULL, "CREATE INDEX track_idx ON track(release_id, number);");
538
//db_action(self, NULL, "CREATE INDEX release_idx ON release (album_id, date);");
527
//db_action(self, NULL, "CREATE INDEX release_idx ON release (album_id, date);");
1187
static char *make_property_match_string_except(Library *self, Entry *entry, int property, int ignored_apid) {
1176
static char *make_property_match_string_except(Library *self, Entry *entry, int property, int ignored_apid) {
1188
// Builds a SQL query string to find matching entries, ignoring a certain property. This is used for example
1177
// Builds a SQL query string to find matching entries, ignoring a certain property. This is used for example
1189
// (via music_source_query_matching_except) in musicsource::track_added_callback
1178
// (via music_source_query_matching_except) in music_source::track_added_callback
1190
// where we find all releases that match on everything but album artist.
1179
// where we find all releases that match on everything but album artist.
1192
if (entry->type==APID_GET_TYPE(ignored_apid) && property==APID_GET_PROPERTY_ID(ignored_apid)) return NULL;
1181
if (entry->type==APID_GET_TYPE(ignored_apid) && property==APID_GET_PROPERTY_ID(ignored_apid)) return NULL;
1310
static GSList *find_entries_matching_except(Library *self, Entry *entry, int ignored_apid, Entry *caller) {
1299
static GSList *find_entries_matching_except(Library *self, Entry *entry, int ignored_apid, Entry *caller) {
1311
// Finds matching entries, ignoring a certain property. This is used for example is musicsource::track_added_callback
1300
// Finds matching entries, ignoring a certain property. This is used for example is music_source::track_added_callback
1312
// where we find all releases that match on everything but album artist.
1301
// where we find all releases that match on everything but album artist.
1314
//printf("\tfind-entry: %s %i %X %i\n", entry_type_name[entry->type], entry->id, entry, entry->ref_count);fflush(stdout);
1303
//printf("\tfind-entry: %s %i %X %i\n", entry_type_name[entry->type], entry->id, entry, entry->ref_count);fflush(stdout);
1548
return TRUE;
1537
return TRUE;
1551
static char *get_summary(MusicSource *musicsource) {
1540
static char *get_summary(MusicSource *music_source) {
1552
Library *self=LIBRARY(musicsource);
1541
Library *self=LIBRARY(music_source);
1553
int files=db_expression(self, NULL, "SELECT COUNT(id) FROM _file");
1542
int files=db_expression(self, NULL, "SELECT COUNT(id) FROM _file");
1554
// FIXME: due to the screwy deletion code at the moment it is possible to
1543
// FIXME: due to the screwy deletion code at the moment it is possible to
1555
// have lots of orphan files, this prevents crash !(when there are files but no recordings I think)
1544
// have lots of orphan files, this prevents crash !(when there are files but no recordings I think)
1572
// FIXME: would be faster to track dead & highest ourself. Only a little though,
1561
// FIXME: would be faster to track dead & highest ourself. Only a little though,
1573
// the main benefit is this code could go in musicsource cos it would be the
1562
// the main benefit is this code could go in music_source cos it would be the
1574
// same as importing's.
1563
// same as importing's.
1575
// FIXME: precalculate these fellows (for a tiny exec time saving :)
1564
// FIXME: precalculate these fellows (for a tiny exec time saving :)
1576
static int get_n_entries (MusicSource *musicsource, EntryType type, int *highest_id, int *n_dead) {
1565
static int get_n_entries (MusicSource *music_source, EntryType type, int *highest_id, int *n_dead) {
1577
int _highest_id = db_expression_printf(LIBRARY(musicsource), NULL,
1566
int _highest_id = db_expression_printf(LIBRARY(music_source), NULL,
1578
"SELECT MAX(id) FROM _%s", entry_type_name[type]);
1567
"SELECT MAX(id) FROM _%s", entry_type_name[type]);
1579
int n_entries = db_expression_printf(LIBRARY(musicsource), NULL,
1568
int n_entries = db_expression_printf(LIBRARY(music_source), NULL,
1580
"SELECT COUNT(id) FROM _%s", entry_type_name[type]);
1569
"SELECT COUNT(id) FROM _%s", entry_type_name[type]);
1581
if (highest_id!=NULL) *highest_id = _highest_id;
1570
if (highest_id!=NULL) *highest_id = _highest_id;
1582
if (n_dead!=NULL) *n_dead = _highest_id - n_entries;
1571
if (n_dead!=NULL) *n_dead = _highest_id - n_entries;
1614
// Return list of entries of child_entry_type where child_property = parent_id
1603
// Return list of entries of child_entry_type where child_property = parent_id
1615
// FIXME: query_relations could do this.
1604
// FIXME: query_relations could do this.
1616
static GSList *query_entry_children (MusicSource *musicsource, int parent_id,
1605
static GSList *query_entry_children (MusicSource *music_source, int parent_id,
1617
EntryType child_entry_type, int child_property) {
1606
EntryType child_entry_type, int child_property) {
1618
int rows, columns; char **data;
1607
int rows, columns; char **data;
1619
GSList *list = NULL;
1608
GSList *list = NULL;
1620
data = db_query_printf(LIBRARY(musicsource), &rows, &columns, NULL,
1609
data = db_query_printf(LIBRARY(music_source), &rows, &columns, NULL,
1621
"SELECT id FROM _%s WHERE %s_id=%i", entry_type_name[child_entry_type],
1610
"SELECT id FROM _%s WHERE %s_id=%i", entry_type_name[child_entry_type],
1622
schema[child_entry_type][child_property].name, parent_id);
1611
schema[child_entry_type][child_property].name, parent_id);
1623
list = _query_entries(LIBRARY(musicsource), child_entry_type, data, rows, columns);
1612
list = _query_entries(LIBRARY(music_source), child_entry_type, data, rows, columns);
1624
sqlite3_free_table (data);
1613
sqlite3_free_table (data);
1625
return list;
1614
return list;
1628
static GSList *query_entry_children_ids(MusicSource *musicsource, int parent_id, EntryType child_entry_type, int child_property) {
1617
static GSList *query_entry_children_ids(MusicSource *music_source, int parent_id, EntryType child_entry_type, int child_property) {
1629
int rows, columns; char **data;
1618
int rows, columns; char **data;
1630
GSList *list=NULL;
1619
GSList *list=NULL;
1631
data = db_query_printf(LIBRARY(musicsource), &rows, &columns, NULL,
1620
data = db_query_printf(LIBRARY(music_source), &rows, &columns, NULL,
1632
"SELECT id FROM _%s WHERE %s_id=%i", entry_type_name[child_entry_type],
1621
"SELECT id FROM _%s WHERE %s_id=%i", entry_type_name[child_entry_type],
1633
schema[child_entry_type][child_property].name, parent_id);
1622
schema[child_entry_type][child_property].name, parent_id);
1716
return list;
1705
return list;
1719
static GSList *query_matching_except(MusicSource *musicsource, Entry *entry, int ignored_apid) {
1708
static GSList *query_matching_except (MusicSource *music_source, Entry *entry, int ignored_apid) {
1720
// Queries entries, ignoring a certain property. This is used for example is musicsource::track_added_callback
1709
// Queries entries, ignoring a certain property. This is used for example is music_source::track_added_callback
1721
// where we find all releases that match on everything but album artist.
1710
// where we find all releases that match on everything but album artist.
1723
// FIXME: this needs to be really fast, because it is called on
1712
// FIXME: this needs to be really fast, because it is called on
1724
// releases for every single track add !!
1713
// releases for every single track add !!
1725
Library *self=LIBRARY(musicsource);
1714
Library *self = LIBRARY(music_source);
1726
GSList *result=NULL;
1715
GSList *result=NULL;
1727
GSList *result_ids=find_entries_matching_except(self, entry, ignored_apid, entry);
1716
GSList *result_ids=find_entries_matching_except(self, entry, ignored_apid, entry);
1728
for (GSList *id_node=result_ids;id_node;id_node=id_node->next)
1717
for (GSList *id_node=result_ids;id_node;id_node=id_node->next)
1729
if (entry->source==musicsource && (int)id_node->data!=entry->id)
1718
if (entry->source==music_source && (int)id_node->data!=entry->id)
1730
result=g_slist_prepend(result, query_entry(musicsource, entry->type, (int)id_node->data));
1719
result=g_slist_prepend(result, query_entry(music_source, entry->type, (int)id_node->data));
1731
g_slist_free(result_ids);
1720
g_slist_free(result_ids);
1732
return result;
1721
return result;
1735
static MusicSourceView *create_view(MusicSource *musicsource, ViewConfig *config) {
1724
static MusicSourceView *create_view (MusicSource *music_source, ViewConfig *config) {
1736
MusicSourceView *view = g_object_new(LIBRARY_VIEW_TYPE, "source", musicsource,
1725
MusicSourceView *view = g_object_new(LIBRARY_VIEW_TYPE, "source", music_source,
1737
"config", config, NULL);
1726
"config", config, NULL);
1738
//printf("library_create_view done- %X\n", view);fflush(stdout);
1727
//printf("library_create_view done- %X\n", view);fflush(stdout);
1739
return view;
1728
return view;
1748
} _Removal;
1737
} _Removal;
1750
// FIXME: would make more sense for these to be db_add_entry etc.
1739
// FIXME: would make more sense for these to be db_add_entry etc.
1751
static int add_entry_internal(Library *self, Entry *entry) {
1740
static int add_entry_internal (Library *self, Entry *entry) {
1752
GString *columns=g_string_new(NULL), *values=g_string_new(NULL);
1741
GString *columns=g_string_new(NULL), *values=g_string_new(NULL);
1753
for (int i=0;i<entry_n_properties[entry->type];i++) {
1742
for (int i=0;i<entry_n_properties[entry->type];i++) {
1754
char *string = NULL;
1743
char *string = NULL;
1890
// above update_unique_entry_property for its child (our current_entry).
1879
// above update_unique_entry_property for its child (our current_entry).
1891
// We need to emit changed on the old value, since its children will have changed.
1880
// We need to emit changed on the old value, since its children will have changed.
1892
// In particular, for recordings (the only mergeable entry at the moment anyway) if they
1881
// In particular, for recordings (the only mergeable entry at the moment anyway) if they
1893
// have no children any more they are deleted in musicsource::recording-changed. FIXME:
1882
// have no children any more they are deleted in music_source::recording-changed. FIXME:
1894
// should we do this for all types of entry?
1883
// should we do this for all types of entry?
1896
// FIXME: sort this out.
1885
// FIXME: sort this out.
1946
// 'delete' flags if this entry should be deleted. 'will_be_unreffed' is true when this
1935
// 'delete' flags if this entry should be deleted. 'will_be_unreffed' is true when this
1947
// entry should be imagined as having one less reference.
1936
// entry should be imagined as having one less reference.
1949
// FIXME: library and musicsourceimporting share quite a bit of this code, we should merge it
1938
// FIXME: library and music_sourceimporting share quite a bit of this code, we should merge it
1950
static void touch (Library *self, EntryType type, int id, GSList **p_removal_queue, gboolean delete,
1939
static void touch (Library *self, EntryType type, int id, GSList **p_removal_queue, gboolean delete,
1951
gboolean will_be_unreffed, GSList **p_jetsam, EntryType caller_type,
1940
gboolean will_be_unreffed, GSList **p_jetsam, EntryType caller_type,
1952
int caller_id) {
1941
int caller_id) {
2089
// If the entry is merged, the return value here is the existing entry. Otherwise it is the new entry! In either
2078
// If the entry is merged, the return value here is the existing entry. Otherwise it is the new entry! In either
2090
// case the return value will have a reference to be taken over.
2079
// case the return value will have a reference to be taken over.
2092
Entry *add_recursive(Library *self, Entry *entry, Entry *caller) {
2081
Entry *add_recursive (Library *self, Entry *entry, Entry *caller) {
2093
// Adds the base entry, and then recursively adds or updates its children depending on whether they are in the db already.
2082
// Adds the base entry, and then recursively adds or updates its children depending on whether they are in the db already.
2094
MusicSource *musicsource=MUSIC_SOURCE(self);
2083
MusicSource *music_source = MUSIC_SOURCE(self);
2095
method_track_enter("library::add %s %i <%i> %s (caller %s %i)\n", entry_type_name[entry->type], entry->id, entry->ref_count,
2084
method_track_enter ("library::add %s %i <%i> %s (caller %s %i)\n", ENTRY_PF(entry),
2096
entry->source==self?"ours":(entry->source==NULL?"none":"ext"), entry_type_name[caller->type], caller->id);
2085
entry->ref_count,
2086
entry->source==self?"ours":(entry->source==NULL?"none":"ext"),
2098
if (entry->source!=NULL) {
2089
if (entry->source!=NULL) {
2099
if (entry->source==self) g_warning("Adding %s %i - already owned by library", entry_type_name[entry->type], entry->id);
2090
if (entry->source==self)
2100
else g_warning("Adding %s %i - already owned by another source", entry_type_name[entry->type], entry->id);
2091
g_warning("Adding %s %i - already owned by library", ENTRY_PF(entry));
2093
g_warning("Adding %s %i - already owned by another source", ENTRY_PF(entry));
2103
MUSIC_SOURCE_CLASS(library_parent_class)->add_entry(musicsource, entry);
2096
MUSIC_SOURCE_CLASS(library_parent_class)->add_entry (music_source, entry);
2105
// Go through every child entry and add/update this too. (Unless it was the caller)
2098
// Go through every child entry and add/update this too. (Unless it was the caller)
2107
for (int i=0;i<entry_n_properties[entry->type];i++) {
2100
for (int i=0; i<entry_n_properties[entry->type]; i++) {
2108
if (IS_FOREIGN_KEY(schema[entry->type][i].type)) {
2101
if (IS_FOREIGN_KEY(schema[entry->type][i].type)) {
2109
Entry *child=entry_get_property(entry, i);
2102
Entry *child=entry_get_property(entry, i);
2110
if (child!=NULL && (child->type!=caller->type || child->id!=caller->id)) {
2103
if (child!=NULL && (child->type!=caller->type || child->id!=caller->id)) {
2128
// Search for an existing entry to merge with. FIXME: make this faster ! indexing.
2121
// Search for an existing entry to merge with. FIXME: make this faster ! indexing.
2130
Entry *existing_entry=try_merge(self, entry); gboolean merged=TRUE;
2123
Entry *existing_entry = try_merge(self, entry);
2124
gboolean merged = TRUE;
2131
if (existing_entry==NULL) {
2125
if (existing_entry==NULL) {
2132
merged=FALSE;
2126
merged = FALSE;
2133
// We already require entry to be unowned for musicsourceimporting so we can just nab this for the cache
2127
// We already require entry to be unowned for music_sourceimporting so we can just nab this for the cache
2134
int id = add_entry_internal(self, entry);
2128
int id = add_entry_internal(self, entry);
2135
_entry_set_id (entry, id); entry->source = self;
2129
_entry_set_id (entry, id); entry->source = self;
2140
} else {
2134
} else {
2141
entry_take_last_ref (existing_entry, "library::add-entry");
2135
entry_take_last_ref (existing_entry, "library::add-entry");
2142
entry=existing_entry;
2136
entry = existing_entry;
2145
if (merged==TRUE);
2139
if (merged==TRUE);
2150
_music_source_queue_signal (MUSIC_SOURCE(self), MUSIC_SOURCE_SIGNAL_ENTRY_ADDED, NULL,
2144
_music_source_queue_signal (MUSIC_SOURCE(self), MUSIC_SOURCE_SIGNAL_ENTRY_ADDED, NULL,
2151
entry);
2145
entry);
2153
method_track_leave("done - add %s %i <%i>\n", entry_type_name[entry->type], entry->id, entry->ref_count);
2147
method_track_leave ("done - add %s %i <%i>\n", ENTRY_PF(entry), entry->ref_count);
2155
return entry;
2149
return entry;
2158
static int add_entry(MusicSource *musicsource, Entry *entry) {
2152
static int add_entry (MusicSource *music_source, Entry *entry) {
2159
musicsource->signal_blocking_stack++;
2153
music_source->signal_blocking_stack++;
2160
entry_take_last_ref (entry, "library::add-entry");
2154
entry_take_last_ref (entry, "library::add-entry");
2161
Entry *result=add_recursive(LIBRARY(musicsource), entry, entry);
2155
Entry *result = add_recursive(LIBRARY(music_source), entry, entry);
2162
int entry_id=result->id;
2156
int entry_id = result->id;
2164
entry_unref(entry, "library::add-entry");
2158
entry_unref (entry, "library::add-entry");
2165
if (result!=entry)
2159
if (result!=entry)
2166
entry_unref(result, "library::add-entry");
2160
entry_unref (result, "library::add-entry");
2168
musicsource->signal_blocking_stack--;
2162
music_source->signal_blocking_stack--;
2169
if (musicsource->signal_blocking_stack==0 && musicsource->signal_queue!=NULL)
2163
if (music_source->signal_blocking_stack==0 && music_source->signal_queue!=NULL)
2170
_music_source_emit_queued_signals (musicsource);
2164
_music_source_emit_queued_signals (music_source);
2171
return entry_id;
2165
return entry_id;
2224
self->signal_blocking_stack--;
2218
self->signal_blocking_stack--;
2225
// FIXME: should just be two neat methods on musicsource really to block & unblock signals
2219
// FIXME: should just be two neat methods on music_source really to block & unblock signals
2226
if (self->signal_blocking_stack==0 && self->signal_queue!=NULL)
2220
if (self->signal_blocking_stack==0 && self->signal_queue!=NULL)
2227
_music_source_emit_queued_signals (self);
2221
_music_source_emit_queued_signals (self);
2234
// is completely ignored).
2228
// is completely ignored).
2236
// FIXME: genericview remove_entry should work this way too.
2230
// FIXME: genericview remove_entry should work this way too.
2237
static void remove_entry (MusicSource *musicsource, EntryType type, int id) {
2231
static void remove_entry (MusicSource *music_source, EntryType type, int id) {
2238
method_track_enter ("library::remove-entry %s %i\n", entry_type_name[type], id);
2232
method_track_enter ("library::remove-entry %s %i\n", entry_type_name[type], id);
2239
Library *self = LIBRARY(musicsource);
2233
Library *self = LIBRARY(music_source);
2240
musicsource->signal_blocking_stack++;
2234
music_source->signal_blocking_stack++;
2242
MUSIC_SOURCE_CLASS(library_parent_class)->remove_entry (musicsource, type, id);
2236
MUSIC_SOURCE_CLASS(library_parent_class)->remove_entry (music_source, type, id);
2244
GSList *removal_queue = NULL;
2238
GSList *removal_queue = NULL;
2248
execute_removal_queue (self, removal_queue);
2242
execute_removal_queue (self, removal_queue);
2250
method_track_leave("done library::remove-entry %s %i\n", entry_type_name[type], id);
2244
method_track_leave ("done library::remove-entry %s %i\n", entry_type_name[type], id);
2252
musicsource->signal_blocking_stack--;
2246
music_source->signal_blocking_stack--;
2253
if (musicsource->signal_blocking_stack==0 && musicsource->signal_queue!=NULL)
2247
if (music_source->signal_blocking_stack==0 && music_source->signal_queue!=NULL)
2254
_music_source_emit_queued_signals (musicsource);
2248
_music_source_emit_queued_signals (music_source);
2257
static Entry *checkout_entry (MusicSource *music_source, EntryType root_type, int root_id) {
2251
static Entry *checkout_entry (MusicSource *music_source, EntryType root_type, int root_id) {