RSS

(root)/calliope : 449

Sam Thursfield
2009-09-17 18:41:35
Revision ID: sam@candylion-20090917184135-lglttkgselvyvnme
Library: tidy code.

collapse all collapse all

added added

removed removed

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/>.
16
 */
16
 */
17
 
17
 
18
// Library: a musicsource which stores metadata in an SQL database.
18
/* library: a music_source which stores metadata in an SQL database.
19
//
19
 *
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.
 
 
25
 */
25
 
26
 
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];
62
};
63
};
63
 
64
 
64
enum {  PROP_0, PROP_FILE_NAME  };
65
enum {  PROP_0, PROP_FILE_NAME  };
65
 
66
 
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);
106
 
107
 
107
static MusicSourceView *create_view(MusicSource *musicsource, ViewConfig *config);
108
static MusicSourceView *create_view(MusicSource *music_source, ViewConfig *config);
108
 
109
 
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);
118
 
119
 
119
static void dump (MusicSource *musicsource, gboolean detailed);
120
static void dump (MusicSource *music_source, gboolean detailed);
120
 
121
 
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));
167
}
168
}
168
 
169
 
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);
171
 
172
 
172
        SP->file_name=NULL;
173
        SP->file_name=NULL;
173
        SP->failed=FALSE;
174
        SP->failed=FALSE;
174
 
175
 
175
        cache_init(self);
176
        cache_init(self);
176
}
177
}
177
 
178
 
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);
181
 
183
 
189
 
191
 
190
        // Precalculate fill-entry queries.
192
        // Precalculate fill-entry queries.
191
        //
193
        //
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;
206
 
207
 
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);
211
                };
212
                };
212
 
213
 
214
                g_free (sql);
215
                g_free (sql);
215
        };
216
        };
216
 
217
 
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);
 
 
219
 
 
 
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);
 
 
225
 
 
 
226
        return object;
218
        return object;
227
};
219
};
228
 
220
 
229
static void finalize(GObject *object) {
221
static void finalize (GObject *object) {
230
        Library *self=LIBRARY(object);
222
        Library *self = LIBRARY(object);
231
 
223
 
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);
240
 
230
 
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);
243
};
232
};
244
 
233
 
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))
258
    };
247
    };
259
};
248
};
260
 
249
 
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);
263
 
252
 
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);
269
        };
258
        };
270
};
259
};
271
 
260
 
280
        return SP->db;
269
        return SP->db;
281
};
270
};
282
 
271
 
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);
285
 
274
 
286
        if (result!=SQLITE_OK) {
275
        if (result!=SQLITE_OK) {
533
 
522
 
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);");
539
};
528
};
1186
//
1175
//
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.
1191
//
1180
//
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;
1308
 
1297
 
1309
 
1298
 
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.
1313
//
1302
//
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;
1549
};
1538
};
1550
 
1539
 
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)
1570
};
1559
};
1571
 
1560
 
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;
1613
 
1602
 
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;
1626
};
1615
};
1627
 
1616
 
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);
1634
 
1623
 
1716
        return list;
1705
        return list;
1717
};
1706
};
1718
 
1707
 
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.
1722
//
1711
//
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;
1733
};
1722
};
1734
 
1723
 
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;
1749
 
1738
 
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?
1895
 
1884
 
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.
1948
//
1937
//
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.
2091
//
2080
//
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"),
 
 
2087
                            ENTRY_PF(caller));
2097
 
2088
 
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));
 
 
2092
                else
 
 
2093
                        g_warning("Adding %s %i - already owned by another source", ENTRY_PF(entry));
2101
        };
2094
        };
2102
 
2095
 
2103
        MUSIC_SOURCE_CLASS(library_parent_class)->add_entry(musicsource, entry);
2096
        MUSIC_SOURCE_CLASS(library_parent_class)->add_entry (music_source, entry);
2104
 
2097
 
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)
2106
        //
2099
        //
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)) {
2127
 
2120
 
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.
2129
        //
2122
        //
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;
2136
 
2130
 
2139
                #endif
2133
                #endif
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;
2143
        };
2137
        };
2144
 
2138
 
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);
2152
 
2146
 
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);
2154
 
2148
 
2155
        return entry;
2149
        return entry;
2156
}
2150
}
2157
 
2151
 
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;
2163
 
2157
 
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");
2167
 
2161
 
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;
2172
};
2166
};
2173
 
2167
 
2222
        };
2216
        };
2223
 
2217
 
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);
2228
};
2222
};
2234
// is completely ignored).
2228
// is completely ignored).
2235
//
2229
//
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++;
2241
 
2235
 
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);
2243
 
2237
 
2244
        GSList *removal_queue = NULL;
2238
        GSList *removal_queue = NULL;
2245
 
2239
 
2247
 
2241
 
2248
        execute_removal_queue (self, removal_queue);
2242
        execute_removal_queue (self, removal_queue);
2249
 
2243
 
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);
2251
 
2245
 
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);
2255
};
2249
};
2256
 
2250
 
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) {
20
#define _LIBRARY_H
20
#define _LIBRARY_H
21
 
21
 
22
#include <glib-object.h>
22
#include <glib-object.h>
23
//#include <gtk/gtk.h>
 
 
24
//#include <glade/glade.h>
 
 
25
#include "musicsource.h"
23
#include "musicsource.h"
26
 
24
 
27
G_BEGIN_DECLS
25
G_BEGIN_DECLS
28
 
26
 
29
void library_system_init(char *package, char *version, int argc, char **argv);
27
void library_system_init (char *package, char *version, int argc, char **argv);
30
 
28
 
31
#define LIBRARY_TYPE            (library_get_type())
29
#define LIBRARY_TYPE            (library_get_type())
32
#define LIBRARY(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), LIBRARY_TYPE, Library))
30
#define LIBRARY(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), LIBRARY_TYPE, Library))
33
#define LIBRARY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), LIBRARY_TYPE, LibraryClass))
31
#define LIBRARY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  LIBRARY_TYPE, LibraryClass))
34
#define IS_LIBRARY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), LIBRARY_TYPE))
32
#define IS_LIBRARY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), LIBRARY_TYPE))
35
#define IS_LIBRARY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), LIBRARY_TYPE))
33
#define IS_LIBRARY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),  LIBRARY_TYPE))
36
#define LIBRARY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), LIBRARY_TYPE, LibraryClass))
34
#define LIBRARY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),  LIBRARY_TYPE, LibraryClass))
37
 
35
 
38
typedef struct LibraryPrivate LibraryPrivate;
36
typedef struct LibraryPrivate LibraryPrivate;
39
typedef struct {
37
typedef struct {
46
} LibraryClass;
44
} LibraryClass;
47
 
45
 
48
GType library_get_type (void)G_GNUC_CONST;
46
GType library_get_type (void)G_GNUC_CONST;
49
MusicSource *library_new(const char *file_name);
47
 
50
 
48
MusicSource *library_new (const char *file_name);
51
// Cheaply exposed (like a pretend superclass!) just so libraryview can call this.
 
 
52
// Should really go in library-private.h ..
 
 
53
char **db_query_printf(Library *db, int *rows, int *columns, GError **perror, const char *format, ...);
 
 
54
 
 
 
55
// FIXME: only used for creating indices ..
 
 
56
void db_action_printf(Library *db, GError **perror, const char *format, ...);
 
 
57
 
 
 
58
gint64 db_expression_printf(Library *db, GError **perror, const char *format, ...);
 
 
59
 
 
 
60
void _db_append_join (EntryType local_type, int local_property_id, EntryType foreign_type,
 
 
61
                      GString *joins, gboolean *join_flags);
 
 
62
void _db_append_property_query (EntryType entry_type, int property_id, GString *projection,
 
 
63
                                            gboolean comma_flag, GString *joins, gboolean *join_flags);
 
 
64
 
49
 
65
G_END_DECLS
50
G_END_DECLS
66
 
51
 
50
#include "genericview.h"
50
#include "genericview.h"
51
#include "genericview-private.h"
51
#include "genericview-private.h"
52
#include "library.h"
52
#include "library.h"
 
 
53
#include "library-private.h"
53
#include "libraryview.h"
54
#include "libraryview.h"
54
 
55
 
55
#ifndef LIBRARY_DISABLE
56
#ifndef LIBRARY_DISABLE

Loggerhead is a web-based interface for Bazaar branches