RSS

(root)/calliope : /src/base/musicsourceview.c (revision 453)

Line Revision Contents
1 185 /*      Calliope Music Player
2  *      Copyright 2005-09 Sam Thursfield <ssssam gmail.com>
3  *
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 253  *      along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 185  */
17 253
18 185 #include <string.h>
19 #include "gtk/gtktreemodel.h"
20 #include "gtk/gtktreednd.h"
21 #include "main.h"
22 #include "musicsource.h"
23 #include "musicsourceview.h"
24 #include "musicsourceview-private.h"
25
26 /* MusicSourceView:
27  *      this file defines a basic view for the MusicSource model. It is possible to extend
28  *  it partially, as in LibraryView, but it's not intended for there to be a complete
29 253  *  reimplementation in some subclass. I don't see there being any dramatic improvements
30 185  *  that couldn't be implemented directly in this base class.
31  *
32  *  Its architecture is such that only a tiny portion of the source needs to be in memory
33  *  in any situation. The view is divided up into pages, of MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE
34  *  rows. On creation, the first & last pages are queried from the source, and the total
35  *  row count is  *estimated*. This value can be used for the tree view scrollbar - which needs
36  *  to be visually approximate but is by no means an exact selector of position, except for at
37  *  the top and the bottom. This is less true with small numbers of rows, but in this case
38  *  of course we can just query the whole source. The scrollbar can then select a position
39  *  0 to 100% through the view - and we can estimate what page this will be on and give
40  *  hopefully sensible results! Due to estimates being based on averages (obviously) the bigger
41  *  the page size the more realistic the estimates will probably be. Even in the worst case,
42  *  the only issue is a goofy vertical scroll bar, **provided MIN_PAGE_SIZE is bigger than the
43  *  visible area**.
44  *
45 253  *  FIXME: there are a couple of major hacks in place at the moment to work around the
46 185  *    limitations of the current GtkTreeView. What we need is a GtkTreeView which doesn't
47  *        start its day by querying the entire model .. until then, the strategy is this.
48  *      1. iter.stamp stores the first row in the current node this iter represented, so we
49  *     can tell how far it has travelled easily. (Which I know is a misuse of stamp :)
50 253  *  2. if one iterator goes through more than PAGE_SIZE rows, we stop querying the source,
51 185  *     set user_data2 to NULL and instead let it go through the estimated rows. This is
52  *         called 'freefall'.
53  *  3. has_child always returns true except for leaves
54  *  4. calls to iter_nth_child are treated as using the estimated row number, unless it's
55  *         actually known - so we return what's near that row rather than its actual contents.
56  *  5. I presume this only works in fixed height mode at the moment.
57  *  6. if an estimated n is found to be wrong, row-added, row-changed and row-removed are
58  *     emitted until the treeview's internal data is correct.
59 253  *  7. to help test for overlaps, each page can have one row more than its -> size suggests,
60 185  *         except: the last page, and any page with chain_next=TRUE. This is used by libraryview.
61  *  8. therefore, in fixed height mode, tree_view_set_model should involve reading the first,
62  *     last and middle pages only. (Middle page because gtktreeview reads the root of its
63  *         internal tree). In non fixed height mode I don't really know what happens.
64  *  9. row-inserted etc. signals are unreliable and only for the benefit of the treeview. If you
65  *     want to find out about entries being added and removed, you should connect to the signals
66  *     of the underlying musicsourceview.
67 253  *
68 185  *      FIXME: I'm sure there are a lot of other possible improvements to this implementation.
69  *
70  *  Variables such as n_children have an e_ or _e counterpart. This is the estimated value.
71  *  _e is always set; _n is -1 if unknown. If _n is known, _e=_n.
72  */
73 253
74 185 /* IMPORTANT POINTS:
75         GtkTreePaths are no longer reliable, because I could return one with an estimated n,
76 253         and then some other query could make us find the real n and suddenly the path would
77 185         point to a different row with no indication of the change. How to solve this conundrum??
78         Well, sometimes it doesn't matter, if the path is only used briefly (ie. nothing else
79         given a chance to query the model, and no queries which would cause us to load more
80         pages.
81         Solutions:
82                 -> give GtkTreePaths an 'approximate' flag?
83         ow do you refer to a row consistently? Well gtktreerowreference would still work b/c
84         we emit row-inserted and row-deleted when we renumber rows. I guess that's the only
85         way.
86 253
87 185         The point of this rambling anyway is, don't rely on GtkTreePath once you call something
88         that could result in estimates being revised - ie. iter_next, iter_nth_child or get_iter
89         I think.
90  */
91
92 /*      GTKTREEITER STRUCTURE:
93  *      stamp: first row n within this *node* that this iter represented.
94  *                      FIXME: i know this is a misuse of stamp :(
95  *      user_data:  ViewNode *
96  *              user_data2: ViewPage *, or NULL if has been through more than a page already
97  *              user_data3: index under current page, or under node
98  *
99  *      If user_data3+(ViewPage *)user_data2->start_e > iter.stamp+MIN_PAGE_SIZE,
100  *      iter_next does no more page querying and instead returns TRUE up until it falls off the
101  *      bottom of the last page. I decided to call this freefall for some reason.
102  */
103
104 /*      ORPHANS:
105  *  All the stuff about orphans looks pretty confusing. The basic idea is that one view config
106  *      node, where the next node down is a many:1 reference instead of the usual 1:many, can have
107  *  some entries with more than one parent, and some entries with no parents at all. The entries
108  *  with more than one parent just appear more than once, but the entries with no parent are
109  *  treated specially with all this orphanage nonsense.
110  *
111 253  *  At the moment, this is only possible with the track->recording relation. Hence only one
112  *  orphan property in a config is possible at the moment. I don't see any point in the near future
113 215  *  when more than one is likely. Some work is needed to make more than one orphaning join work.
114 185  *
115 217  *  To accomodate orphans the config can branch at a certain point. If a branch isn't specified
116  *  explicitly, it's assumed to be at the first node.  Take this example config:
117 185  *
118 217  *      artist > (album > release > track:recording) / recording
119 185  *
120 253  *  The orphan join is track:recording, on config level 3. Any recordings which are not
121 185  *  referenced in any track.recording_id are instead found on level 1, after all of the albums
122 253  *  - the connecting property (in this case recording.artist_id) is found in the same way as
123 217  *  all the other connections. The branch point is often referred to as the orphanage.
124 185  *
125  *  Orphan info is stored as follows:
126 217  *    - the branching node has its 'orphan_branch' member set to the head node of the branch
127 185  */
128 253
129 185 /*      LEVELS:
130  *      Level numbering works as follows. Using the above example config we get a tree like this:
131  *
132  *      root
133  *      | - artist 1
134  *              | - album 1
135  *                      | - release 1
136  *                              | track 1 & recording 1
137  *              | - recording 2 (an orphan)
138  *              | - recording 3 (..)
139  *  | - artist 2
140  *              ....
141  *
142 253  *      In this tree, the root node has depth 0. The artists have depth 1. The album and the
143 185  *  orphan recording nodes are at depth 2. The release is at depth 3, and the track is at 4.
144  *  The *config depth* is the position in the view config. For both the orphan recordings and
145  *  the normal recordings, for example, this will be 3. For the artists, the config depth is 1.
146  *
147  *  The rec_level and rec_orphan_level members store the depth that recordings and orphan
148  *  recordings are at, respectively.
149  */
150
151 Column column[MUSIC_SOURCE_VIEW_COLUMN_COUNT]={
152 310         // Smart columns
153         {       "Artist", TRUE, -1, -1 },
154
155         // Dumb columns
156         {       "Original Title", TRUE, _COMPOSITION_NAME, -1, 14       },
157 185         {       "Length", TRUE, _RECORDING_LENGTH, -1, -1       },
158         {       "Title", TRUE, _RECORDING_NAME, -1, 1},
159 310         {       "Composer", FALSE, _ARTIST_NAME, _COMPOSITION_ARTIST, 15        },
160         {       "Recording Artist", FALSE, _ARTIST_NAME, _RECORDING_ARTIST, 2},
161 185         {       "Path", FALSE, _FILE_PATH, -1, -1},
162         {       "Path Encoding", FALSE, _FILE_PATH_ENCODING, -1, -1},
163         {       "Type", FALSE, _FILE_TYPE, -1, -1},
164         {       "Size", FALSE, _FILE_SIZE, -1, -1},
165 310         {       "Bitrate", TRUE, _FILE_BITRATE, -1, -1},
166 185         {       "Sample Rate", FALSE, _FILE_SAMPLERATE, -1, -1},
167         {       "Channels", FALSE, _FILE_CHANNELS, -1, -1},
168         {       "Date Added", TRUE, _FILE_DATE_ADDED, -1, -1},
169         {       "Album Title", TRUE, _ALBUM_NAME, -1, -1},
170 310         {       "Album Artist", FALSE, _ARTIST_NAME, _ALBUM_ARTIST, -1  },
171 185         {       "Release Date", TRUE, _RELEASE_DATE, -1, -1     },
172         {       "Track Number", TRUE, _TRACK_NUMBER, -1, -1     }
173 };
174
175 #define COLUMN_COUNT    MUSIC_SOURCE_VIEW_COLUMN_COUNT
176 268 static gboolean columns_initialised = FALSE;
177 185
178 // MusicSourceViewPrivate and some related stuff is defined in musicsourceview-private.h
179
180
181
182 static void     tree_model_init(GtkTreeModelIface *iface);
183 static GObject *constructor(GType type, guint n_construct_properties, GObjectConstructParam *construct_params);
184 static void finalize(GObject *self);
185 static void set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
186
187 // GtkTreeModel methods
188 static GtkTreeModelFlags get_flags(GtkTreeModel *tree_model);
189 static gint     get_n_columns(GtkTreeModel *tree_model);
190 static GType get_column_type(GtkTreeModel *tree_model, gint index);
191
192 static gboolean get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path);
193 static GtkTreePath *get_path(GtkTreeModel *tree_model, GtkTreeIter      *iter);
194 static void     get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column, GValue *value);
195 static gboolean iter_children(GtkTreeModel *tree_model, GtkTreeIter     *iter, GtkTreeIter *parent);
196
197 // The four above methods are implemented in terms of these five
198 static gboolean iter_next(GtkTreeModel *tree_model,     GtkTreeIter     *iter);
199 static gboolean iter_has_child(GtkTreeModel *tree_model, GtkTreeIter *iter);
200 216 static gint     iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter);
201 185 static gboolean iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, gint n);
202 static gboolean iter_parent(GtkTreeModel *tree_model,GtkTreeIter *iter, GtkTreeIter     *child);
203
204 228 static void _init_node(ViewNode *node, GList *parent_tail, GList *head, GList *tail);
205 185 static gboolean _find_iter_for_row(MusicSourceView *self, ViewNode *node, int n, GtkTreeIter *iter);
206 261 static gboolean defloat(MusicSourceView *self, GtkTreeIter *iter);
207 268 static int _get_id_at_iter (MusicSourceView *self, EntryType type, GtkTreeIter *iter_param,
208                             int entry_map[ENTRY_TYPE_COUNT]);
209 185
210 static void interface_track_printf(const char *text, ...);
211 void _music_source_view_dump_node(MusicSourceView *self, ViewNode *node);
212 //static void dump_iter(GtkTreeIter *iter);
213 //static void dump_page(ViewPage *page);
214
215 enum {
216         PROP_0, PROP_SOURCE, PROP_CONFIG
217 };
218
219 G_DEFINE_TYPE_WITH_CODE (MusicSourceView, music_source_view, G_TYPE_OBJECT,
220                          G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL, tree_model_init))
221
222
223 215 static void music_source_view_class_init (MusicSourceViewClass *cl) {
224         GObjectClass *object_class = (GObjectClass *) cl;
225         object_class->constructor = constructor;
226         object_class->finalize = finalize;
227         object_class->set_property = set_property;
228 253
229         g_object_class_install_property (object_class, PROP_SOURCE,
230                                          g_param_spec_object("source", "source", "Source",
231                                                              MUSIC_SOURCE_TYPE, STATICSTRS |
232 215                                                              G_PARAM_CONSTRUCT_ONLY|G_PARAM_WRITABLE));
233         g_object_class_install_property (object_class, PROP_CONFIG,
234 253                                          g_param_spec_pointer("config", "config", "View configuration",
235                                                                                               STATICSTRS | G_PARAM_CONSTRUCT_ONLY |
236 215                                                               G_PARAM_WRITABLE));
237 253
238 185         g_type_class_add_private(object_class, sizeof(MusicSourceViewPrivate));
239 }
240
241 static void tree_model_init(GtkTreeModelIface *iface) {
242         iface->get_flags=get_flags;                             iface->get_n_columns=get_n_columns;
243         iface->get_column_type=get_column_type; iface->get_iter=get_iter;
244         iface->get_path=get_path;                               iface->get_value=get_value;
245         iface->iter_next=iter_next;                             iface->iter_children=iter_children;
246 216         iface->iter_has_child=iter_has_child;   iface->iter_n_children = iter_n_children;
247 185         iface->iter_nth_child=iter_nth_child;   iface->iter_parent=iter_parent;
248 }
249
250 static GObject *constructor(GType type, guint n_construct_properties, GObjectConstructParam *construct_params) {
251         GObject *object=((GObjectClass *)music_source_view_parent_class)->constructor(type, n_construct_properties, construct_params);
252         MusicSourceView *self=MUSIC_SOURCE_VIEW(object);
253 253
254 185         // FIXME: should be done on program init really.
255         if (!columns_initialised) {
256 268                 for (int i=0; i<MUSIC_SOURCE_VIEW_COLUMN_COUNT; i++)
257                         entry_property_map (APID_GET_TYPE(column[i].apid), column[i].route_apid,
258                                             column[i].connection);
259                 columns_initialised = TRUE;
260 185         };
261
262         g_object_ref(SP->source);
263
264 216         if (SP->config==NULL || SP->config->head==NULL) {
265                 g_message("musicsourceview: empty configuration\n");
266 185                 return object;
267         };
268 253
269 185         SP->row_markers = NULL;
270         //char *dump = view_config_to_string(SP->config, TRUE);
271         //printf ("musicsourceview: config: %s\n", dump); fflush(stdout);
272         //g_free (dump);
273
274         // Initialise tree
275 253         //
276 228         _init_node(&SP->root, NULL, SP->config->head, NULL);
277 420         SP->root.has_child_toggled_emitted = TRUE;
278 253
279 185         // FIXME: is a hash table the best way to maintain an index of largely consecutive id's ?
280         // The pain with a GArray comes inserting and removing entries, but then again that shouldn't
281         // happen so often, whereas lookups are pretty frequent ... what about a Btree?
282         //SP->index=g_hash_table_new_full(g_int_hash, g_int_equal, g_free, (GDestroyNotify)gtk_tree_path_free);
283
284 253         return object;
285 185 };
286
287 static void music_source_view_init (MusicSourceView *self) {
288         SP = G_TYPE_INSTANCE_GET_PRIVATE(self, MUSIC_SOURCE_VIEW_TYPE, MusicSourceViewPrivate);
289         //SP->config_n_levels=0;
290 253         //SP->rec_level=0; SP->rec_orphan_level=0;
291 185         //SP->index=NULL;
292 }
293
294 static void finalize(GObject *object) {
295         MusicSourceView *self=MUSIC_SOURCE_VIEW(object);
296 253
297 453         //printf ("Freeing msv: %x.\n", object); fflush (stdout);
298 185         //if (SP->index!=NULL)
299         //      g_hash_table_destroy(SP->index);
300 253         for (GSList *ref_node=SP->row_markers; ref_node; ref_node=ref_node->next)
301 185                 g_slice_free (_MusicSourceViewRowMarker, ref_node->data);
302 253         g_slist_free (SP->row_markers);
303
304 185         _music_source_view_free_node(&self->priv->root);
305 253
306 216         if (SP->config!=NULL)
307                 view_config_free(SP->config);
308 185
309         g_object_unref(SP->source);
310         G_OBJECT_CLASS(music_source_view_parent_class)->finalize(object);
311 };
312
313 static void set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) {
314         MusicSourceView *self=MUSIC_SOURCE_VIEW(object);
315 253
316         switch(prop_id) {
317 216         case PROP_SOURCE:       SP->source = g_value_get_object(value); break;
318         case PROP_CONFIG:       SP->config = g_value_get_pointer(value); break;
319 185         default:
320                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
321         }
322 };
323
324 ////////////////////////////////
325 // Internal GtkTreeModel utilities
326 //
327
328 // Get estimated position within its parent node of an iter.
329 static gint iter_get_e(GtkTreeIter *iter) {
330         ViewPage *page=iter->user_data2;
331         if (page==NULL)
332                 return GPOINTER_TO_INT(iter->user_data3);
333         return page->start_n!=-1?
334 253                         page->start_n + GPOINTER_TO_INT(iter->user_data3):
335 185                         page->start_e + GPOINTER_TO_INT(iter->user_data3);
336 };
337
338 // Get the node this iter is pointing to.
339 static ViewNode *iter_get_node(MusicSourceView *self, GtkTreeIter *iter) {
340         if (iter==NULL)
341                 return &self->priv->root;
342         else {
343 216                 // iter never points directly to leaf nodes so this should be unneccessary
344 253                 //if (!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(self), iter))
345 216                 //      return NULL;
346 253
347 221                 ViewPage *page = iter->user_data2;
348                 int n = GPOINTER_TO_INT(iter->user_data3);
349 185                 if (page==NULL) {
350                         // FIXME: We will need to query the page, since this iter is in freefall. This
351                         //                is possible, however.
352                         g_warning("iter_get_node: node %x is in freefall, pointing at row %i, so I "
353                                           "should really have done some work here ..\n", (guint)iter, n);
354                         return NULL;
355                 };
356 253
357                 //printf ("iter_get_node: n %i, page->size %i ... n children %i.\n", n, page->size,
358 233                 //        page->parent_node->e_children); fflush (stdout);
359 185                 g_return_val_if_fail(n>=0 && n<page->size, NULL);
360 253
361 185                 // Take the iter out of freefall again.
362                 // If we don't change the stamp (origin), it will go straight back into freefall on
363                 // iter_next ..
364 221                 iter->stamp = n;
365                 iter->user_data2 = page;
366                 iter->user_data3 = GINT_TO_POINTER(n-page->start_e);
367 253
368 235                 //printf (">>Returning child %i at depth %i.\n", n, page->parent_node->depth); fflush (stdout);
369 185                 return g_array_index(page->nodes, ViewNode *, n);
370         };
371         return NULL;
372 };
373
374 ////////////////////////////////
375 // GtkTreeModel interface
376 //
377
378 235 /* FIXME: since it seems set that all descendents of musicsourceview will store internal data
379  * in the same way (and that seems fair, since any descendent will be doing the same job so
380  * it seems unlikely it should need radically different internal storage - any better methods
381  * should be implemented in this base class instead). Anyway since this is true we should maybe
382  * rewrite these not in terms of gtktreemodel methods but directly acting on the internal
383  * representation since that would be faster. Although make sure there are no radically better
384  * internal representations you are thinking of implementing first. */
385 185
386 static GtkTreeModelFlags get_flags(GtkTreeModel *tree_model) {
387         return /*GTK_TREE_MODEL_ITERS_PERSIST*/ 0;      // (I don't think they do)
388 }
389
390 static gint get_n_columns(GtkTreeModel *tree_model) {
391         interface_track_printf("music_source_view_get_n_columns\n");
392         return MUSIC_SOURCE_VIEW_COLUMN_COUNT;
393 }
394
395 static GType get_column_type(GtkTreeModel *tree_model, gint index) {
396         interface_track_printf("music_source_view_get_column_type [%i]\n", index);
397 253
398         if (index>=MUSIC_SOURCE_VIEW_COLUMN_COUNT) return G_TYPE_INVALID;
399 185         return G_TYPE_STRING;
400 }
401
402 // Return iter for a given path.
403 static gboolean get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path) {
404 241         MusicSourceView *self = MUSIC_SOURCE_VIEW(tree_model);
405 259         //interface_track_printf ("music_source_view_get_iter (path %X - %s) ..", path,
406         //                        gtk_tree_path_to_string(path));
407 185
408 241         g_return_val_if_fail (SP->config!=NULL, FALSE);
409 253
410 185         int depth = gtk_tree_path_get_depth(path);
411 216         if (depth<=0 || depth > SP->config->n_levels) {
412 253                 interface_track_printf ("false - depth is %i, config_n_levels in %i\n", depth,
413 216                                         SP->config->n_levels);
414 185                 return FALSE;
415         };
416 253
417 259         int *indices = gtk_tree_path_get_indices(path);
418 185         GtkTreeIter parent;
419         if (!gtk_tree_model_iter_nth_child (tree_model, iter, NULL, indices[0])) {
420 259                 interface_track_printf ("false - no %ith root child\n", indices[0]);
421 185         return FALSE;
422     };
423
424 259         for (int i=1; i<depth; i++) {
425         parent = *iter;
426 185         if (!gtk_tree_model_iter_nth_child (tree_model, iter, &parent, indices[i])) {
427 259                         interface_track_printf ("false - no child at depth %i\n", i);
428 185                         return FALSE;
429                 };
430     }
431
432 259         interface_track_printf ("got iter\n");
433 185     return TRUE;
434 }
435
436 // Return path for a given iter.
437 // This may be an estimated path!!!!!!!
438 static GtkTreePath *get_path(GtkTreeModel *tree_model, GtkTreeIter *iter) {
439         interface_track_printf ("music_source_view_get_path\n");
440         if (iter==NULL)
441                 return gtk_tree_path_new_first();
442
443         //g_message("music_source_view_get_path: might be returning an estimated path!\n");
444 253
445 185         int e = iter_get_e(iter);
446         //printf ("get_path: got e %i\n", e); fflush (stdout);
447 253         GtkTreePath *path = gtk_tree_path_new_from_indices(e, -1);
448 185         GtkTreeIter new_iter = *iter, parent;
449         while (gtk_tree_model_iter_parent(tree_model, &parent, &new_iter)) {
450                 gtk_tree_path_prepend_index (path, iter_get_e(&parent));
451                 new_iter = parent;
452 253         };
453 185         return path;
454 }
455
456 310 /* get_column_value: returns TRUE if a valid value could be found, and sets p_value. */
457 static gboolean get_column_value (MusicSourceView *self, GtkTreeIter *iter, gint column_index,
458                                   void **p_value) {
459         const EntryType type = APID_GET_TYPE(column[column_index].apid);
460         const int property_id = APID_GET_PROPERTY_ID(column[column_index].apid);
461
462         int id = _get_id_at_iter(self, type, iter, column[column_index].connection);
463         if (id==0)
464                 return FALSE;
465
466         Entry *entry = music_source_query_entry(SP->source, type, id, "music_source_view::get_value");
467         if (!entry) {
468                 g_message ("Unable to find %s %i\n", entry_type_name[type], id);
469                 return FALSE;
470         };
471
472         *(p_value) = entry_get_property(entry, property_id);
473         entry_unref (entry, "music_source_view::get_value");
474         return TRUE;
475 };
476
477 185 // Fill value with specified column of given iter.
478 // FIXME: make this really efficient! It's called a *lot* it seems !
479 // (in fact, is it right that gtktreeview should be calling this on every mouse movement?)
480 //
481 268 static void get_value (GtkTreeModel *tree_model, GtkTreeIter *iter, gint column_index,
482 310                        GValue *g_value) {
483 268         MusicSourceView *self = MUSIC_SOURCE_VIEW(tree_model);
484 185         //printf("music_source_view_get_value .. "); dump_iter(iter); printf("\n");  fflush(stdout);
485 268         // FIXME: it seems wrong that we should return a blank string for columns we have no value for -
486         // surely the type should be unset? That breaks everything though.
487 310         g_value_init (g_value, G_TYPE_STRING);
488
489         // Process smart columns
490         // FIXME: implement these more automatically
491         // FIXME: and more cleverly - instead of get_id_at_iter, we have a lookup table of what levels
492         // have what entry types available and use that. It's important because this value gets called a
493         // lot!
494         if (column_index == COLUMN_ARTIST) {
495                 void *recording_artist_value;
496                 gboolean has_recording_artist = get_column_value(self, iter, COLUMN_RECORDING_ARTIST_NAME,
497                                                                  &recording_artist_value);
498                 if (has_recording_artist && recording_artist_value!=NULL) {
499                         g_value_set_string (g_value, recording_artist_value);
500                         return;
501                 }
502
503                 void *album_artist_value;
504                 gboolean has_album_artist = get_column_value(self, iter, COLUMN_ALBUM_ARTIST_NAME,
505                                                              &album_artist_value);
506                 if (has_album_artist && album_artist_value!=NULL) {
507                         g_value_set_string (g_value, album_artist_value);
508                         return;
509                 }
510
511                 return;
512         };
513 268
514         if (column_index < COLUMN_COUNT) {
515 310                 void *value;
516                 if (!get_column_value(self, iter, column_index, &value)) return;
517
518 309                 const EntryProperty *property = &APID_GET_PROPERTY(column[column_index].apid);
519
520                 switch (property->type) {
521                         case PROPERTY_TYPE_INT: {
522                                 // -1 means unknown for length.
523 310                                 if (column[column_index].apid == _RECORDING_LENGTH && (int)value==-1)
524 309                                         break;
525
526 350                                 g_value_take_string (g_value, format_int64(property->unit, (guint)value));
527 310                                 return;
528 309                         };
529
530 310                         case PROPERTY_TYPE_STRING: case PROPERTY_TYPE_NAME:
531                                 g_value_set_string (g_value, value);
532                                 return;
533
534 309                         default:
535 310                                 g_warning ("Invalid property for column %s\n", column[column_index].name);
536                         case PROPERTY_TYPE_DATE: case PROPERTY_TYPE_DATE_TIME:
537                                 g_value_take_string (g_value, entry_value_to_string (property->type, value));
538                                 return;
539 309                 };
540 185         }
541 }
542
543 static gboolean iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent) {
544         //MusicSourceView *self=MUSIC_SOURCE_VIEW(tree_model);
545         interface_track_printf("music_source_view_iter_children\n");
546 253
547 185         if (gtk_tree_model_iter_nth_child(tree_model, iter, parent, 0)) {
548                 return TRUE;
549         };
550         return FALSE;
551 }
552
553
554 ///////////////////////////////////////////////////////////////////////////
555 // These five methods are the basic treemodel interface. The rest of it
556 // can be implemented in terms of these with pretty much no loss of
557 // efficiency as far as i can see .. has_child could be iter_nth_child(0), but GtkTreeView
558 253 // calls it so much that it needs to be really fast.
559 185 //
560
561 221 static gboolean iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter) {
562 185         //interface_track_printf("musicsourceview::iter-next\n");
563 221         g_return_val_if_fail (iter!=NULL, FALSE);
564 253
565 221         const int n = GPOINTER_TO_INT(iter->user_data3),
566                       origin = GPOINTER_TO_INT(iter->stamp);
567         ViewNode *node = iter->user_data;
568         ViewPage *page = iter->user_data2;
569         g_return_val_if_fail (node!=NULL, FALSE);
570 253
571 185         if (page==NULL) {
572                 // This iter is in freefall (gone through more than 1 page worth of rows, so we don't
573                 // want to do any more querying for it because it's probably going to iterate through
574                 // the rest of the tree model).
575 253                 //printf("Freefall: n %i/%i\n", n, node->e_children);
576 221                 if (n >= node->e_children-1)
577 185                         return FALSE;
578 221                 iter->user_data3 = GINT_TO_POINTER(n+1);
579 185                 return TRUE;
580         };
581
582         // Check if this iter needs to go into freefall. Or if this is the last page, don't bother.
583         // The -2 is so we avoid changing pages at all in the most common case (starting from 0).
584 368         // If origin==-1, we never go into freefall (for internal hacks :)
585         if (origin!=-1 && n+page->start_e >= origin+MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE
586               && page->next!=NULL) {
587 185                 // n is now node-relative not page-relative because the page may not be in memory.
588                 // Of course it also still needs incrementing!
589 221                 iter->user_data2 = NULL;
590                 iter->user_data3 = GINT_TO_POINTER(n+page->start_e)+1;
591 185                 return TRUE;
592         };
593 253
594 185         //printf("n+1 %i, page->size-1 %i\n", n+1, page->size-1);fflush(stdout);
595 221         if (n >= page->size-1) {
596 185                 if (page->next==NULL) {
597                         // This iter is at the bottom of the last page. We query the last page on
598                         // load, so there is no possibility of extra unqueried data.
599
600                         // (The hard way to find out the same thing)
601                         //printf("page start e %i, page size %i, c %i\n", page->start_e, page->size, page->parent_node->e_children);  fflush(stdout);
602 221                         g_warn_if_fail (page->start_e+page->size==page->parent_node->e_children);
603 185                         return FALSE;
604                 };
605 253
606 185                 if (!page->chain_next)
607                         // The next page is probably unqueried.
608 221                         _music_source_view_read_next_page (MUSIC_SOURCE_VIEW(tree_model), page);
609 253
610 185                 // We should definitely have the next page now.
611 221                 g_warn_if_fail (page->chain_next);
612 253
613 221                 iter->user_data2 = page->next;
614                 iter->user_data3 = GINT_TO_POINTER(0);
615 185                 return TRUE;
616         }
617 253
618 221         //printf ("setting n: %i.\n", GINT_TO_POINTER(n+1)); fflush (stdout);
619         iter->user_data3 = GINT_TO_POINTER(n+1);
620 185         return TRUE;
621 };
622
623 253 /* iter_has_child needs to be able to execute without taking the iter out of freefall. If the iter
624 221  * is in freefall (ie. it points to an as yet unqueried region of the node) has_child has to guess,
625  * which it does by returning TRUE unless depth==n_levels. For this reason, when reading bits of
626  * partially queried pages, leaf nodes of depth < n_levels need to have has_child_toggled emitted so
627  * they display correctly.
628  */
629 216 static gboolean iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter) {
630         MusicSourceView *self = MUSIC_SOURCE_VIEW(tree_model);
631         interface_track_printf ("musicsourceview::iter-has-child\n");
632         g_warn_if_fail (iter!=NULL);
633 253
634 221         ViewNode *node;
635         if (iter==NULL)
636                 node = &self->priv->root;
637         else {
638                 ViewPage *page = iter->user_data2;
639                 int n = GPOINTER_TO_INT(iter->user_data3);
640                 if (page==NULL) {
641                         /* Iter is in freefall - guess. */
642                         ViewNode *parent_node = iter->user_data;
643 441                         g_return_val_if_fail (parent_node != NULL, FALSE);
644
645 221                         if (parent_node->depth+1 == SP->config->n_levels)
646                                 return FALSE;
647                         return TRUE;
648                 };
649 253
650 221                 g_return_val_if_fail(n>=0 && n<page->size, FALSE);
651                 node = g_array_index(page->nodes, ViewNode *, n);
652         };
653 253
654 216         /* Leaf nodes can't have any children. */
655         if (node->head==NULL)
656 185                 return FALSE;
657         return TRUE;
658 };
659
660 // FIXME: this is iter_e_children
661 216 static int iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter) {
662         MusicSourceView *self = MUSIC_SOURCE_VIEW(tree_model);
663         interface_track_printf ("musicsourceview::iter-n-children\n");
664 253
665         ViewNode *node = iter_get_node(self, iter);
666 235         /*ViewNode *node;
667 253         if (iter==NULL)
668 235                 node = &SP->root;
669         else node = iter->user_data;*/
670
671 237         //printf ("Got node: depth %i, kids %i. Head %x - parent tail %s\n", node->depth, node->e_children,
672         //        node->head, CONFIG_TYPE_NAME(node->parent_tail)); fflush (stdout);
673 253         if (node==NULL)
674 185                 return 0;
675 216         if (node->head==NULL)   // Is a leaf node.
676                 return 0;
677 185
678 235         if (node->e_children==-1)
679 185                 _music_source_view_initial_read(self, node);
680 235
681 218         //_music_source_view_dump_node (self, node); fflush (stdout);
682 185         return node->e_children;
683 };
684
685 // FIXME: this is iter_eth_child
686 static gboolean iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, int n) {
687         MusicSourceView *self=MUSIC_SOURCE_VIEW(tree_model);
688         interface_track_printf("musicsourceview::iter-nth-child (%i)\n", n);
689         g_return_val_if_fail(iter!=NULL, FALSE);
690 253
691 185         ViewNode *node=iter_get_node(self, parent);
692         //printf("\tgot node %x\n", node);fflush(stdout);
693         if (node==NULL) return 0;
694
695         if (node->e_children<0) {
696                 // Start off the query for this node.
697                 _music_source_view_initial_read(self, node);
698 253
699 185                 // FIXME: Maybe the source knows something we don't? I'm not sure how to handle this
700                 // situation .. so at the moment we warn and return. And leak some memory.
701                 if (n>=node->e_children) {
702                         if (node->n_children<0) {
703                                 char *path_string="<root>";
704                                 if (parent!=NULL) {
705                                         GtkTreePath *path=gtk_tree_model_get_path(tree_model, parent);
706                                         path_string=gtk_tree_path_to_string(path);
707                                         gtk_tree_path_free(path);
708                                 }
709                                 //printf(str); fflush(stdout);
710                                 g_warning("iter_nth_child called at %s %i - but this node was only estimated "
711                                                   "to have %i children :(\n", path_string, n, node->e_children);
712                                 if (parent!=NULL) g_free(path_string);
713                         };
714                         return FALSE;
715                 };
716         };
717         //printf("\tafter read: %i , n%i kids\n", node->e_children, node->n_children);fflush(stdout);
718 253
719 185         if (node->e_children==0 || n>=node->e_children)
720                 return FALSE;
721
722         //printf("iter-nth-child: %i/%i[%i]\n", n, node->e_children, node->n_children);
723 253
724 185         ViewPage *page=node->children;
725         g_return_val_if_fail(page!=NULL, FALSE);
726 253
727 185         return _find_iter_for_row(self, node, n, iter);
728 };
729
730 218 static gboolean iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child) {
731 185         //MusicSourceView *self=MUSIC_SOURCE_VIEW(tree_model);
732 218         interface_track_printf ("musicsourceview::iter-parent\n");
733         g_return_val_if_fail (child!=NULL, FALSE);
734 253
735 218         ViewNode *child_node = child->user_data;
736         ViewPage *page = child->user_data2;
737 253         g_return_val_if_fail (child_node!=NULL, FALSE);
738
739         if (child_node->depth==0)
740 185                 return FALSE;
741 253
742 185         if (iter!=NULL) {
743 218                 iter->stamp = page->start_e + GPOINTER_TO_INT(child->user_data3);
744                 iter->user_data = child_node->parent_page->parent_node;
745                 iter->user_data2 = child_node->parent_page;
746                 iter->user_data3 = GINT_TO_POINTER(child_node->n);
747 185         };
748 218         g_return_val_if_fail (iter->user_data!=NULL, FALSE);
749 185         return TRUE;
750 };
751
752
753 /////////////////////////////////////////////
754 // Our own methods - virtual
755 //
756
757 GtkTreePath *music_source_view_get_path_for_id(MusicSourceView *self, EntryType type, int id) {
758         return MUSIC_SOURCE_VIEW_GET_CLASS(self)->get_path_for_id (self, type, id);
759 };
760
761 253 MusicSourceViewSearch *music_source_view_get_path_for_id_begin (MusicSourceView *self,
762 185                                                                 EntryType type, int id) {
763 253         return MUSIC_SOURCE_VIEW_GET_CLASS(self)->get_path_for_id_begin (self, type, id);
764 185 };
765
766 253 gboolean music_source_view_get_path_for_id_step (MusicSourceView *self,
767 185                                                  MusicSourceViewSearch *state) {
768         return MUSIC_SOURCE_VIEW_GET_CLASS(self)->get_path_for_id_step (self, state);
769 };
770 253
771 GtkTreePath *music_source_view_get_path_for_id_end (MusicSourceView *self,
772 185                                                     MusicSourceViewSearch *state) {
773         return MUSIC_SOURCE_VIEW_GET_CLASS(self)->get_path_for_id_end (self, state);
774 };
775
776
777 ///////////////////////////
778 // Own methods - non virtual
779 //
780
781 MusicSource *music_source_view_get_source(MusicSourceView *self) {
782         return SP->source;
783 };
784
785 241 const ViewConfig *music_source_view_get_config(MusicSourceView *self) {
786 185         //printf ("self -> priv: %s\n", self->priv); fflush (stdout);
787 241         return SP->config;
788 185 };
789
790 int music_source_view_get_n_levels(MusicSourceView *self) {
791 216         if (SP->config==NULL)
792 185                 return 0;
793 216         return SP->config->n_levels;
794 185 };
795
796 // At the moment this function has a shortcoming of assuming that there are no empty nodes, so
797 // if there is one, it will be very confused .. eventually, there should not be any empty
798 // join nodes in the model, so there is no point adding lots of code to deal with this problem.
799 // Nobody wants to see artists listed who they don't have any songs by etc.
800 GtkTreePath *music_source_view_get_first (MusicSourceView *self, EntryType type) {
801         ViewNode *node = &SP->root;
802 216         GList *clnode = SP->config->head;
803 185         GtkTreePath *path = gtk_tree_path_new();
804 216         for (int i=0; i<SP->config->n_levels; i++) {
805 185                 if (node->e_children==-1)
806 253                         _music_source_view_initial_read (self, node);
807                 if (node->e_children==0)
808 185                         break;
809
810                 // FIXME: is append or prepend faster? Our index will always be 0 here so I guess it
811                 // doesn't matter which we use ..
812                 gtk_tree_path_append_index (path, 0);
813
814                 ViewPage *page = node->children;
815                 g_return_val_if_fail (page!=NULL, NULL);
816                 g_return_val_if_fail (page->size>0, NULL);
817 253
818 185                 node = g_array_index (page->nodes, ViewNode *, 0);
819                 g_return_val_if_fail (node!=NULL, NULL);
820
821                 // Now, see if 'type' is on this level.
822                 while (CONFIG_NODE(clnode)->flat) {
823                         if (CONFIG_NODE(clnode)->type==type)
824                                 return path;
825                         clnode = clnode->next;
826                         g_return_val_if_fail (clnode!=NULL, NULL);
827                 };
828 253
829 185                 // If we're at the orphanage and there are only orphans, return here.
830 218                 // FIXME: reimplement this, unit test for it too since it's clearly not tested.
831 253                 /*if (SP->config->orphaning_join_level>0
832 216                       && node->tree_depth==SP->config->orphanage_level && node->orphan_break==0)
833 218                         return path;*/
834 185         };
835 253
836 185         gtk_tree_path_free (path);
837         return NULL;
838 };
839
840
841 261
842 // FIXME: optimise this bad boy! get_value depends on it at the moment and that
843 // is called all the time!
844 //
845 // FIXME: is it possible to go from node->id and node->intermediate_ids to just node->ids?
846 // It would be easier for this func, but what about everyone else !!
847 // This is when it's a good time to write your "what bits of code does changing this bit affect"
848 // tool.
849 //
850 // FIXME: how about, rewriting in terms of _walk_config ..
851 310 // FIXME: could we speed this up with some sort of lookup table??
852 268 /*      entry_map: Used to find which entry types can be connected to other types. There is a global
853  *                 'entry_map' that's normally used, but when finding a column value we want to use
854  *                 a more limited map - for example, the 'Album Artist' column's map doesn't connect
855  *                 recording or composition to artist, whereas the normal entry map does -
856  *                 otherwise, the album artist column might return the recording artist or something
857  *                 instead.
858  */
859 static int _get_id_at_iter (MusicSourceView *self, EntryType type, GtkTreeIter *iter_param,
860                             int entry_map[ENTRY_TYPE_COUNT]) {
861 261         if (iter_param->user_data2==NULL)
862                 if (!defloat(self, iter_param)) {
863                         g_warning ("Defloat of iter failed.\n"); return 0;
864                 };
865         //printf ("\n\nget_id_at_iter: %s\n", entry_type_name[type]); fflush (stdout);
866
867 268         /*printf ("Map: (to %s)\n", entry_type_name[type]);
868         for (int i=0; i<ENTRY_TYPE_COUNT; i++) {
869                 if (entry_map[i] < 0) printf ("\t<%s>\n", entry_type_name[i]);
870                 else printf ("\t->%s: %s.\n", entry_type_name[i], schema[i][entry_map[i]].name);
871         }; fflush (stdout);*/
872
873 261         GtkTreeIter iter = *iter_param;
874
875         ViewPage *page = iter.user_data2;
876         int n = GPOINTER_TO_INT(iter.user_data3);
877         g_return_val_if_fail (page!=NULL, 0);
878         g_return_val_if_fail (n < page->size, 0);
879
880         // First, see if the tail join at each level is what we want, because that's really easy
881         // to find.
882 268         ViewNode *node = g_array_index(page->nodes, ViewNode *, n);
883 261         do {
884 410                 //printf ("Tail type: %s. prev %x\n", CONFIG_TYPE_NAME(node->parent_tail), node->parent_tail->prev); fflush (stdout);
885 261                 //printf ("--head type: %s. next %x\n", entry_type_name[CONFIG_NODE(parent_node->head)->type], parent_node->head->next); fflush (stdout);
886 268                 if (CONFIG_NODE(node->parent_tail)->type == type) {
887                         //printf ("Got type: %s. id %i. At depth %i\n", entry_type_name[type], node->id,
888                         //        node->depth); fflush (stdout);
889 261                         return node->id;
890                 };
891
892 268                 g_return_val_if_fail (node->parent_page!=NULL, 0);
893                 node = node->parent_page->parent_node;
894                 g_return_val_if_fail (node!=NULL, 0);
895 410         } while (node->depth > 0);
896 261
897         // Loop through the levels, going up a level each time we don't find what we want, and
898         // looking at each entry type that we can reach along the way.
899         gboolean tree_result = TRUE;
900         while (tree_result!=0) {
901 411                 ViewNode *parent_node = iter.user_data;   g_return_val_if_fail (parent_node != NULL, 0);
902                 ViewPage *page = iter.user_data2;         g_return_val_if_fail (page != NULL, 0);
903 261                 int n = GPOINTER_TO_INT(iter.user_data3);
904 411
905 261                 ViewNode *node = g_array_index(page->nodes, ViewNode *, n);
906                 g_return_val_if_fail (node!=NULL, 0);
907
908                 if (parent_node->head!=NULL /* has_child */ && parent_node->e_children==-1) {
909 411                         // node should be read if we have an iter pointing to it, so this code is an error if
910                         // called.
911 261                         g_warn_if_reached();
912                         _music_source_view_initial_read (self, parent_node);
913                 };
914
915                 // Loop through the joins on this level. We don't skip the tail join node, because we didn't
916                 // look at the type map for it before.
917                 GList *clnode = node->parent_tail;
918 385                 g_return_val_if_fail (node->parent_tail!=NULL, 0);
919 261
920                 /* The *tail* level_break_count takes into account any orphan branching along the
921                  * way that would have reduced the number of intermediate ids. */
922                 int join_n = CONFIG_NODE(parent_node->head)->level_break_count - 1,
923                     intermediate_n = CONFIG_NODE(parent_node->tail)->level_break_count - 1;
924
925                 while(1) {
926                         ViewConfigNode *cnode = CONFIG_NODE(clnode);
927                         EntryType current_type = cnode->type;
928                         int current_id = 0;
929                         if (node->intermediate_ids==NULL || node->intermediate_ids[intermediate_n]==0)
930                                 current_id = node->id;
931                         else current_id = node->intermediate_ids[intermediate_n];
932
933                         /*printf ("get_id_at_iter[%s]: Got %s %i - depth %i join n %i/%i intermediate_n %i/%i "
934                                 "%x - map %i\n", entry_type_name[type], entry_type_name[current_type],
935                                     current_id, node->depth, join_n,
936                                         CONFIG_NODE(parent_node->head)->level_break_count, intermediate_n,
937                                         CONFIG_NODE(parent_node->tail)->level_break_count,
938 268                                     (guint)node->intermediate_ids, entry_map[current_type]); fflush(stdout);*/
939 261
940                         // Got the right entry straight away - return
941                         if (type==current_type) return current_id;
942
943 268                         if (entry_map[current_type]!=-1) {
944 261                                 // FIXME: I'm not sure if the entry map is the right/only way to do things.
945                                 // Why can't we use the view config to work out what properties link what?
946
947                                 // Let's try to make our way to the desired entry type.
948                                 Entry *base_entry = music_source_query_entry(SP->source, current_type,
949                                                                              current_id, "msv::get-id-at-path"),
950                                           *entry = base_entry;
951
952                                 // When this can happen.
953                                 if (base_entry==NULL) {
954                                         GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL(self), iter_param);
955                                         g_warning ("get_id_at_iter: %s %i was NULL, at path %s :(",
956                                                    entry_type_name[current_type], current_id,
957                                                    gtk_tree_path_to_string(path));
958                                         break;
959                                 };
960
961                                 while (current_type!=type) {
962                                         // FIXME: here recording.default-file works as a connection, even though that
963                                         // defeats the point of info listing all the files if it doesn't have a
964                                         // specific one .. not a big issue now since only one file per rec anyway. But
965                                         // my tentative solution is to flag default rels as a default-only somehow, so
966                                         // they can be ignored here.
967 268                                         int conn_id = entry_map[current_type];
968 261                                         if (conn_id==-1) {
969                                                 current_id = 0; break;
970                                         };
971
972                                         entry = entry_get_property(entry, conn_id);
973                                         if (entry==NULL)
974                                                 break;
975                                         current_id = entry->id; current_type = entry->type;
976                                 };
977                                 entry_unref (base_entry, "msv::get-id-at-path");
978
979                                 //if (current_id!=0) {
980                                 //      printf("Connected to %s %i\n\n", entry_type_name[current_type], current_id);
981                                 //} else printf("No connection found\n\n");
982                                 fflush (stdout);
983
984                                 if (current_id!=0) {
985                                         //interface_track_printf("\tgot id %s %i\n", entry_type_name[type], current_id);
986                                         return current_id;
987                                 }
988                         };
989
990 411                         // Walk backwards to next break - branch or many:1 join.
991 261                         //
992                         do {
993                                 if (cnode->flags & VIEW_CONFIG_NODE_MANY_TO_ONE_JOIN) {
994 411                                         clnode = clnode->prev; g_return_val_if_fail (clnode!=NULL, 0);
995                                         cnode  = clnode->data; g_return_val_if_fail (cnode->flat, 0);
996 261                                         break;
997                                 };
998 411
999                                 clnode = clnode->prev; if (clnode==NULL) break;
1000                                 cnode  = clnode->data;
1001
1002                                 if (!cnode->flat) break;
1003 261                         } while (clnode);
1004
1005 411                         // At end of config
1006                         if (clnode==NULL) break;
1007
1008                         // At end of level
1009 261                         if (join_n==0 || intermediate_n==0) {
1010 411                                 g_warn_if_fail (cnode->flat==0);
1011 261                                 break;
1012                         }
1013
1014 411                         join_n--; intermediate_n--;
1015
1016 261                         g_warn_if_fail (clnode->next != NULL);
1017                         g_warn_if_fail (CONFIG_NODE(clnode->next)->flags && VIEW_CONFIG_NODE_MANY_TO_ONE_JOIN);
1018                 };
1019
1020                 // Try again at the level above, if possible
1021 //next_level:;
1022                 GtkTreeIter old = iter;
1023                 tree_result = gtk_tree_model_iter_parent(GTK_TREE_MODEL(self), &iter, &old);
1024
1025                 if (!tree_result)
1026 269                         // Make sure we worked all the way through the config, and didn't give up early.
1027 261                         g_warn_if_fail (clnode==NULL);
1028                 else
1029                         g_warn_if_fail (clnode!=NULL);
1030         };
1031         //printf ("\tgot no id for %s\n", entry_type_name[type]);
1032         return 0;
1033 };
1034
1035 268
1036 int music_source_view_get_id_at_iter (MusicSourceView *self, EntryType type,
1037                                       GtkTreeIter *iter_param) {
1038 304         return _get_id_at_iter(self, type, iter_param, entry_type_info[type].map);
1039 268 };
1040
1041 185 // Finds entry id at a given entry, or 0 if uncertain.
1042 //
1043 261 // FIXME: make sure this is used only when necessary, and we aren't converting iters to paths and
1044 // back to iters .
1045 185 int music_source_view_get_id_at_path(MusicSourceView *self, EntryType type, const GtkTreePath *path) {
1046         ///*interface_track_*/printf("music-source-view::get-id-at-path %s at %s\n", entry_type_name[type], gtk_tree_path_to_string((GtkTreePath *)path));
1047 253
1048         // The method here is to try to map the entry type we have to the join node of this
1049 185         // level .. if that isn't possible, move up a level and try again.
1050         //
1051         GtkTreeIter iter;
1052         if (gtk_tree_model_get_iter(GTK_TREE_MODEL(self), &iter, (GtkTreePath *)path))
1053 261                 return music_source_view_get_id_at_iter(self, type, &iter);
1054 185         return 0;
1055 };
1056
1057 261
1058 185 // These functions are a bit slow, given how often _locate_entry and _locate_entry_range
1059 // call them, but much faster than the alternative of connecting to the signals!
1060 //
1061 // 'maximise' specifies what to do if the exact row pointed to by 'row' moves - if 'maximise'
1062 253 // is false, 'row' points to before any added rows, and if 'maximise' is true 'row' points
1063 185 // to after the new rows. When 'row' is removed, a minimised reference moves to the row
1064 // before while a maximised reference jumps to the row after.
1065 //
1066 253 void music_source_view_add_row_marker (MusicSourceView *self, int *row, ViewNode *parent_node,
1067 185                                        gboolean maximise) {
1068 237         // Allowed to be 1 node off the end, to allow exclusive row ranges like in locate_entry_range.
1069         g_return_if_fail (*row >=0 || *row <= parent_node->e_children);
1070
1071 185         _MusicSourceViewRowMarker *ref = g_slice_new(_MusicSourceViewRowMarker);
1072         ref->parent_node = parent_node; ref->row = row; ref->maximise = maximise;
1073         SP->row_markers = g_slist_prepend(SP->row_markers, ref);
1074 };
1075
1076 253 void music_source_view_remove_row_marker (MusicSourceView *self, int *row) {
1077 185         for (GSList *ref_node=SP->row_markers; ref_node; ref_node=ref_node->next) {
1078                 _MusicSourceViewRowMarker *ref = ref_node->data;
1079                 if (ref->row==row) {
1080                         g_slice_free (_MusicSourceViewRowMarker, ref);
1081                         SP->row_markers = g_slist_remove_link(SP->row_markers, ref_node);
1082                         return;
1083                 };
1084         };
1085 253
1086 185         g_warning ("music_source_view_remove_marker: Unable to find given marker!!\n");
1087 };
1088
1089 366
1090 185 ////////////////////////////////////////
1091 // Internal non-virtual methods
1092 //
1093
1094 228 static void _init_node(ViewNode *node, GList *parent_tail, GList *head, GList *tail) {
1095 253         g_warn_if_fail (parent_tail!=NULL || head!=NULL);
1096
1097 228         if (head==NULL && parent_tail!=NULL)
1098                 head = parent_tail->next;
1099 218         if (tail==NULL && head!=NULL) {
1100                 tail = head;
1101 253                 for (; CONFIG_NODE(tail)->flat; tail=tail->next)
1102 218                         g_warn_if_fail (tail!=NULL);
1103         };
1104 253
1105 235         //printf ("New node - head %s, tail %s.\n", head==NULL?"<none>":CONFIG_TYPE_NAME(head),
1106         //        tail==NULL?"<none>":CONFIG_TYPE_NAME(tail)); fflush (stdout);
1107
1108 253         node->parent_page = NULL;
1109
1110 215         node->depth = 0;
1111 253         node->head = head;
1112 218         node->tail = tail;
1113 228         node->parent_tail = parent_tail;
1114 253
1115 259         if (head==NULL && tail==NULL)
1116                 // Leaf node.
1117                 node->e_children = node->n_children = 0;
1118         else node->e_children = node->n_children = -1;
1119 261
1120 215         node->children = NULL;
1121         node->intermediate_ids = NULL;
1122
1123         /*node->orphan_break = 0;
1124 253
1125 215         node->root_orphan_break = -1;*/
1126 185         node->root_to_size_ratio=1.0;
1127 420         
1128         node->has_child_toggled_emitted = FALSE;
1129 185 };
1130
1131 228 ViewNode *_music_source_view_new_node (int depth, GList *parent_tail, GList *head, GList *tail) {
1132 185         ViewNode *node = g_slice_new(ViewNode);
1133 228         _init_node (node, parent_tail, head, tail);
1134 216         node->depth = depth;
1135 420
1136 185         return node;
1137 };
1138
1139 ViewPage *_music_source_view_new_page (ViewNode *node, ViewPage *prev, ViewPage *next) {
1140         ViewPage *page = g_slice_new0(ViewPage);
1141         if (prev!=NULL) {
1142                 g_warn_if_fail (prev->next==next);
1143                 prev->next = page; page->prev = prev;
1144         };
1145         if (next!=NULL) {
1146                 g_warn_if_fail (next->prev==prev);
1147                 page->next = next; next->prev = page;
1148         };
1149 253
1150 185         page->parent_node = node;
1151
1152         page->start_e = -1; page->start_n = -1;
1153         page->size = 0;
1154         page->root_overhang = 0;
1155
1156         page->nodes = g_array_new(FALSE, FALSE, sizeof(ViewNode *));
1157 253
1158 185         page->chain_next = FALSE;
1159         return page;
1160 };
1161
1162 // FIXME: this should really be virtual, because of the user_data in
1163 // Library ...
1164 // FIXME: yes this is really bad! The user_data is leaked !!!! and that's lots of memory !!!!
1165 253 // This doesn't free the actual node structure, which needs freeing with g_free -
1166 185 // this is because the root is statically allocated.
1167 void _music_source_view_free_node (ViewNode *node) {
1168         ViewPage *page = node->children;
1169         while (page!=NULL) {
1170                 for (int i=0; i<page->size; i++) {
1171 253                         if (page->nodes!=NULL) {
1172 185                                 ViewNode *child = g_array_index(page->nodes, ViewNode *, i);
1173 253                                 _music_source_view_free_node (child);
1174 185                                 g_slice_free (ViewNode, child);
1175                         };
1176 253                 }
1177
1178 185                 if (page->nodes!=NULL)
1179                         g_array_free (page->nodes, TRUE);
1180
1181                 if (node->intermediate_ids!=NULL)
1182                         g_free (node->intermediate_ids);
1183
1184                 ViewPage *old_page = page;
1185                 page = page->next;
1186                 g_slice_free (ViewPage, old_page);
1187 253         };
1188 185 };
1189
1190 366 /* _music_source_view_match_node: Used by the views in places. */
1191 gboolean _music_source_view_match_node (ViewNode *node, int entry_id, int *entry_intermediate_ids) {
1192         if (node->id != entry_id)
1193                 return FALSE;
1194
1195         // We could still be different rows - check each intermediate id! The int. id arrays are all
1196         // 0-terminated for safety reasons.
1197         //
1198         int *i = entry_intermediate_ids,
1199             *j = node->intermediate_ids,
1200                 count = 0;
1201         if (i != 0) {
1202                 g_return_val_if_fail (j!=0, TRUE);
1203                 while (*i!=0 || *j!=0) {
1204                         // ID arrays hould be the same length, because they represent the same level.
1205                         g_return_val_if_fail (*i!=0 && *j!=0, FALSE);
1206
1207                         if (*i != *j)
1208                                 return FALSE;
1209                         i++; j++;
1210                         count++;
1211                 };
1212         }
1213
1214         return TRUE;
1215 };
1216
1217 185 // Alters n based on row-deleted and row-inserted signals.
1218 ViewPage *_music_source_view_find_page_for_row (MusicSourceView *self, ViewNode *node, int *n,
1219                                                 ViewPage *start) {
1220         //printf ("find_page_for_row: %i (/%i)\n", *n, node->e_children); fflush (stdout);
1221         ViewPage *page = start!=NULL? start: node->children;
1222         g_return_val_if_fail (page!=NULL, NULL);
1223 438         g_return_val_if_fail (*n >= 0,    NULL);
1224 253
1225 185         // Step 1. search page tree (FIXME: still a list at the moment).
1226         // We traverse the list (which of course is ordered) and stop on the last page
1227         // before 'n'.
1228         while (page->next!=NULL) {
1229                 if (page->next->start_e > *n)
1230                         break;
1231                 g_warn_if_fail (page->start_n < *n);
1232 253                 page = page->next;
1233 185         };
1234 253
1235 185         // Page has not been added properly.
1236         g_warn_if_fail (page->start_e > -1);
1237 253
1238 185         //printf ("start_e: %i, size %i\n", page->start_e, page->size); fflush(stdout);
1239         // Step 2. If page->size+start_e<n, we are here!
1240 253         if (page->start_e<=*n && *n<page->start_e+page->size)
1241 185                 return page;
1242 253
1243 185         // If this is the last page and n is past the last row, return this page - it's the
1244 253         // caller's problem that the row it wants doesn't actually exist (and it may be planning
1245 185         // to add it).
1246         if (*n >= node->e_children) {
1247                 g_warn_if_fail (page->next==NULL);      // Should have come to last page.
1248                 return page;
1249         };
1250 253
1251         // Else, query the page at row n. This is where it gets complicated - rows may be
1252 185         // removed or inserted if estimations are changed. 'n' is updated appropriately in this
1253         // case.
1254         //
1255         //printf ("calling _read_page_at:\n"); fflush (stdout);
1256         page = _music_source_view_read_page_at (self, page, page->next, n);
1257         #ifdef MUSIC_SOURCE_VIEW_CHECK_TREE
1258         _music_source_view_check_tree (self, NULL);
1259         #endif
1260         return page;
1261 };
1262
1263 // Sets iter to point to row n in node.
1264 //
1265 static gboolean _find_iter_for_row(MusicSourceView *self, ViewNode *node, int n, GtkTreeIter *iter) {
1266         ViewPage *page = _music_source_view_find_page_for_row (self, node, &n, NULL);
1267         g_return_val_if_fail(page!=NULL, FALSE);
1268 253
1269 185         iter->stamp=n;
1270         iter->user_data=page->parent_node;
1271         iter->user_data2=page;
1272         iter->user_data3=GINT_TO_POINTER(n-page->start_e);
1273         return TRUE;
1274 };
1275
1276 // FIXME: warn when we have to do this - either GtkTreeView has changed, or someone else is
1277 // querying using the default methods .. when I guess really we should have some "i'm sure
1278 // i really want to iterate through every node" interface ...
1279 static gboolean defloat(MusicSourceView *self, GtkTreeIter *iter) {
1280         g_return_val_if_fail(iter->user_data2==NULL, FALSE);
1281 253
1282 185         void row_inserted(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, void *data) {
1283         };
1284
1285         void row_deleted(GtkTreeModel *model, GtkTreePath *path, void *data) {
1286         };
1287 253
1288 185         int h1=g_signal_connect(GTK_TREE_MODEL(self), "row-inserted", G_CALLBACK(row_inserted), NULL);
1289         int h2=g_signal_connect(GTK_TREE_MODEL(self), "row-deleted", G_CALLBACK(row_deleted), NULL);
1290 253
1291 185         ViewNode *node=iter->user_data;
1292         g_return_val_if_fail(node!=NULL, FALSE);
1293 253
1294 185         gboolean result=_find_iter_for_row(self, node, GPOINTER_TO_INT(iter->user_data3), iter);
1295 253
1296 185         g_signal_handler_disconnect(GTK_TREE_MODEL(self), h1);
1297         g_signal_handler_disconnect(GTK_TREE_MODEL(self), h2);
1298 253
1299 185         return result;
1300 };
1301
1302
1303 389 void _music_source_view_start_initial_read_watch (MusicSourceView *self) {
1304         if (SP->initial_read_watch)
1305                 g_warning ("music_source_view_start_initial_read_watch: watch already in progress.\n");
1306         if (SP->initial_read_watch_list != NULL)
1307                 g_warning ("music_source_view_start_initial_read_watch: watch list not empty.\n");
1308         SP->initial_read_watch = TRUE;
1309 };
1310
1311 GSList *_music_source_view_get_initial_read_watch_list (MusicSourceView *self) {
1312         if (!SP->initial_read_watch)
1313                 g_warning ("music_source_view_get_initial_read_watch_list: watch not in progress.\n");
1314
1315         return SP->initial_read_watch_list;
1316 };
1317
1318 void _music_source_view_end_initial_read_watch (MusicSourceView *self) {
1319         if (!SP->initial_read_watch)
1320                 g_warning ("music_source_view_end_initial_read_watch: watch not in progress.\n");
1321         g_slist_free (SP->initial_read_watch_list);
1322         SP->initial_read_watch_list = NULL;
1323         SP->initial_read_watch = FALSE;
1324 };
1325
1326 185 /////////////////////////////////////////////////////
1327 // Internal interface
1328 //
1329
1330 216 void _music_source_view_initial_read (MusicSourceView *self, ViewNode *node) {
1331 389         interface_track_printf ("music_source_view_initial_read: node %x\n", node);
1332         if (SP->initial_read_watch)
1333                 SP->initial_read_watch_list = g_slist_prepend (SP->initial_read_watch_list, node);
1334 216         MUSIC_SOURCE_VIEW_GET_CLASS(self)->initial_read (self, node);
1335 185 };
1336
1337 253 ViewPage *_music_source_view_read_page_at (MusicSourceView *self, ViewPage *head,
1338 185                                            ViewPage *tail, int *e) {
1339         return MUSIC_SOURCE_VIEW_GET_CLASS(self)->read_page_at(self, head, tail, e);
1340 }
1341
1342 void _music_source_view_read_next_page(MusicSourceView *self, ViewPage *page) {
1343         return MUSIC_SOURCE_VIEW_GET_CLASS(self)->read_next_page(self, page);
1344 }
1345
1346
1347
1348 ////////////////////////////
1349 // Debugging methods
1350 //
1351
1352
1353 static void interface_track_printf(const char *text, ...) {
1354         #ifdef MUSIC_SOURCE_VIEW_TRACE_INTERFACE
1355         va_list ap; va_start(ap, text); vprintf(text, ap); va_end(ap); fflush(stdout);
1356         #endif
1357 };
1358
1359 /*static void dump_iter(GtkTreeIter *iter) {
1360         ViewPage *page=iter->user_data2;
1361         if (page==NULL) {
1362                 printf("iter %x: no page :(", (guint)iter);
1363                 return;
1364         };
1365         printf("PAGE %x (e%in%i)/(e%in%i) - root_rows- %i size %i n %i\n", (guint)page, page->start_e, page->start_n, page->parent_node->e_children, page->parent_node->n_children,
1366           page->root_rows, page->size, GPOINTER_TO_INT(iter->user_data3));
1367 //      printf("\nIter: tree depth %i, config depth %i, id %i, n %i - index %i\n", node->tree_depth, node->config_depth, node->id, node->n, iter->user_data3); fflush(stdout);
1368 253 };
1369 185
1370 static void dump_page(ViewPage *page) {
1371         g_return_if_fail(page!=NULL);
1372         printf("%x: prev %x, next %x - parent node %x\n", (guint)page, (guint)page->prev, (guint)page->next, (guint)page->parent_node);
1373         //printf("At: Tree %i Config %i\n", page->tree_depth, page->config_depth);
1374         printf("Start: e%i/n%i. chain_next: %i", page->start_e, page->start_n, page->chain_next);
1375         printf("root rows %i, size %i.\n", page->root_rows, page->size);
1376         printf("nodes %x\n\n", (guint)page->nodes);
1377 };*/
1378
1379 208 void _music_source_view_dump_node (MusicSourceView *self, ViewNode *node) {
1380 368         if (node==NULL)
1381                 node = &SP->root;
1382         printf ("NODE: depth %i - type %s ", node->depth,
1383                 node->head==NULL? "<leaf>": entry_type_name[CONFIG_NODE(node->head)->type]);
1384
1385         // Dump intermediate ids, and then entry id
1386         printf ("["); int *ii = node->intermediate_ids;
1387         while (*ii != 0)
1388                 printf ("%i, ", *(ii++));
1389         printf ("%i] ", node->id); fflush (stdout);
1390
1391 208         int i = 1;
1392         for (ViewPage *page=node->children; page; page=page->next) {
1393 253                 printf ("\t%i:%x|  [%x  %x]\t%i-%i <%i> [%i-%i]\n", i++, (guint)page, (guint)page->prev,
1394                         (guint)page->next, page->start_e, page->start_e+page->size, page->start_n,
1395 208                                 page->root_start, page->root_start+page->root_rows-1);
1396 185         };
1397 216         if (node->children!=NULL)
1398 368                  printf ("\nNode thinks there are %i children \n", node->e_children);
1399         else printf ("..no children\n");
1400 185         fflush(stdout);
1401 };
1402
1403 259 /* A couple of utils for _check_tree. 'Running' differs from 'walking' in that we don't care about
1404  * the entries along the way.
1405  * FIXME: put in viewconfig.c?
1406  */
1407 static GList *_run_to_level_head (GList *start_node) {
1408         GList *level_head;
1409         for (level_head=start_node; level_head; level_head=level_head->prev) {
1410 261                 while (level_head->prev==NULL && CONFIG_NODE(level_head)->parent!=NULL)
1411 259                         level_head = CONFIG_NODE(level_head)->parent;
1412                 if (level_head->prev==NULL || !CONFIG_NODE(level_head->prev)->flat) break;
1413         };
1414         return level_head;
1415 };
1416
1417 431 /* _music_source_view_check_tree: assert 'node' and its children are in a consistent state.
1418  * 
1419  *   This uses g_assert rather than g_[warn|return]_if_fail because it is not intended to be used
1420  *   outside of debugging builds and unit tests.
1421 218  */
1422 261 void _music_source_view_check_tree (MusicSourceView *self, ViewNode *node) {
1423 185         #ifdef MUSIC_SOURCE_VIEW_CHECK_TREE
1424 218         if (SP->config==NULL) return;
1425 253
1426         if (node==NULL)
1427 218                 node = &SP->root;
1428 185         //_music_source_view_dump_node(self, node);
1429
1430 218         g_assert_cmpint (node->depth, <=, SP->config->n_levels);
1431
1432 431         // Test 'n' is correct. This should be the index to 'node' within its parent page. (So it should
1433         // really be called 'page_i', being a relative index).
1434         //
1435         if (node != &SP->root) {
1436                 ViewNode *n_node = g_array_index (node->parent_page->nodes, ViewNode *, node->n);
1437                 g_assert (node == n_node);
1438         }
1439
1440         // Test 'parent_tail'. Normally, parent_tail==parent->tail, but we could also be on an orphan
1441         // branch. In this case we walk to the parent_tail's head to check it matches with parent->head.
1442 259         //
1443         if (node==&SP->root) {
1444                 g_assert (node->parent_tail==NULL);
1445                 g_assert (node->parent_page==NULL);
1446         } else {
1447                 ViewNode *parent_node = node->parent_page->parent_node;
1448                 g_assert (parent_node!=NULL);
1449                 g_assert (node->parent_tail!=NULL);
1450 261
1451 259                 if (node->parent_tail!=parent_node->tail) {
1452 261                         g_assert (CONFIG_NODE(node->parent_tail)->flat == FALSE);
1453 259                         g_assert (_run_to_level_head(node->parent_tail) == parent_node->head);
1454                 };
1455         };
1456
1457 431         // Check 'head' and 'tail' are correct.
1458         //
1459 218         if (node->head==NULL && node->tail==NULL) {
1460 259                 // Must be a leaf node if there is no config level associated. -1 (not yet read) isn't
1461 261                 // acceptable either.
1462 259                 g_assert (node->e_children==0);
1463 218         } else {
1464 259                 //printf ("Checking: %i, %s.\n", node->depth, entry_type_name[CONFIG_NODE(node->head)->type]);
1465                 g_assert (node->head!=NULL); g_assert (node->tail!=NULL);
1466
1467                 // First, run from tail to head.
1468                 GList *head, *tail;
1469                 g_assert (node->head->prev==NULL || CONFIG_NODE(node->head->prev)->flat==FALSE);
1470 261
1471 259                 g_assert (CONFIG_NODE(node->tail)->flat == FALSE);
1472                 g_assert (_run_to_level_head(node->tail) == node->head);
1473                 //printf ("FROM %s %i TO %s %i == %s %i\n", CONFIG_TYPE_NAME(node->tail), CONFIG_TYPE_NAME(_run_to_level_head(node->tail)),
1474                 //        CONFIG_TYPE_NAME(node->head)); fflush (stdout);
1475
1476                 head = node->head;
1477 218                 int level = node->depth;
1478 259                 fflush (stdout);
1479 218                 // Given node's 'head' and 'depth', seek the tail node of the first level.
1480                 while (level > 0) {
1481                         g_assert (head!=NULL);
1482                         if (head->prev) head = head->prev;
1483                         else if (CONFIG_NODE(head)->parent) head = CONFIG_NODE(head)->parent;
1484                         else g_assert_not_reached ();
1485                         if (!CONFIG_NODE(head)->flat) level--;
1486                 };
1487
1488                 // Seek head node of first level.
1489                 do {
1490                         if (head->prev==NULL) {
1491                                 if (CONFIG_NODE(head)->parent!=NULL)
1492                                         head = CONFIG_NODE(head)->parent;
1493                                 else break;
1494                         } else head = head->prev;
1495                         g_assert (head!=NULL);
1496                 } while (CONFIG_NODE(head)->flat);
1497 253
1498 218                 // Check we got to config->head - if not, node's 'head' or 'depth' is wrong.
1499                 g_assert (level==0);
1500                 g_assert (head==SP->config->head);
1501 253
1502 218                 // Check tail is the end of this level.
1503                 for (tail=node->head; CONFIG_NODE(tail)->flat; tail=tail->next)
1504                         g_assert (tail);
1505                 g_assert (tail==node->tail);
1506         };
1507
1508 431         // Investigate the children.
1509         //
1510 218         ViewPage *page = node->children;
1511 185         g_assert (node->e_children<=0 || node->children!=NULL);
1512 253
1513 185         if (node->e_children==-1) {
1514                 g_assert (node->children==NULL);
1515                 return;
1516         };
1517 431
1518 185         if (node->n_children==0)
1519                 return;
1520
1521         ViewPage *previous_page=NULL;
1522 259         while (page!=NULL) {
1523 185                 g_assert (page->prev==previous_page);
1524 253
1525 185                 if (previous_page!=NULL)
1526                         g_assert_cmpint (page->start_e, >=, previous_page->start_e+previous_page->size);
1527
1528                 // Test all of the children now!
1529                 if (page->next==NULL || page->chain_next)
1530                         g_warn_if_fail(page->size==page->nodes->len);
1531                 else g_warn_if_fail(page->size==page->nodes->len || page->size==page->nodes->len-1);
1532 253
1533 185                 for (int i=0;i<page->size;i++) {
1534                         ViewNode *child = g_array_index(page->nodes, ViewNode *, i);
1535                         //printf("[page start %i] Testing node %i [id %i] - n %i i %i\n", page->start_e, i, child->id, child->n, i);fflush(stdout);
1536 253
1537 185                         g_assert_cmpint (child->n, ==, i);
1538                         g_assert_cmphex ((guint32)child->parent_page, ==, (guint32)page);
1539                         _music_source_view_check_tree(self, child);
1540                 };
1541 253
1542 185                 previous_page=page;
1543                 page=page->next;
1544         };
1545 253
1546 185         g_assert (previous_page->size+previous_page->start_e == node->e_children);
1547         #endif
1548 };

Loggerhead is a web-based interface for Bazaar branches