diff options
40 files changed, 2117 insertions, 424 deletions
diff --git a/configure.ac b/configure.ac index 99b78310..493926ec 100644 --- a/configure.ac +++ b/configure.ac @@ -108,6 +108,7 @@ AC_ARG_ENABLE(m3u, [AS_HELP_STRING([--enable-m3u ], [build m3u plugin AC_ARG_ENABLE(vfs-zip, [AS_HELP_STRING([--enable-vfs-zip ], [build vfs_zip plugin (default: auto)])], [enable_vfs_zip=$enableval], [enable_vfs_zip=yes]) AC_ARG_ENABLE(converter, [AS_HELP_STRING([--enable-converter ], [build converter plugin (default: auto)])], [enable_converter=$enableval], [enable_converter=yes]) AC_ARG_ENABLE(artwork-imlib2, [AS_HELP_STRING([--enable-artwork-imlib2 ], [use imlib2 in artwork plugin (default: auto)])], [enable_artwork_imlib2=$enableval], [enable_artwork_imlib2=yes]) +AC_ARG_ENABLE(medialib, [AS_HELP_STRING([--enable-medialib ], [build medialibrary plugin (default: auto)])], [enable_medialib=$enableval], [enable_medialib=yes]) if test "x$enable_staticlink" != "xno" ; then AC_DEFINE_UNQUOTED([STATICLINK], [1], [Define if building static version]) @@ -549,7 +550,11 @@ if test "x$enable_converter" != "xno" ; then fi fi -PLUGINS_DIRS="plugins/lastfm plugins/mpgmad plugins/vorbis plugins/flac plugins/wavpack plugins/sndfile plugins/vfs_curl plugins/cdda plugins/gtkui plugins/alsa plugins/ffmpeg plugins/hotkeys plugins/oss plugins/artwork plugins/adplug plugins/ffap plugins/sid plugins/nullout plugins/supereq plugins/vtx plugins/gme plugins/pulse plugins/notify plugins/musepack plugins/wildmidi plugins/tta plugins/dca plugins/aac plugins/mms plugins/shellexec plugins/dsp_libsrc plugins/m3u plugins/vfs_zip plugins/converter" +if test "x$enable_medialib" != "xno" ; then + HAVE_MEDIALIB=yes +fi + +PLUGINS_DIRS="plugins/lastfm plugins/mpgmad plugins/vorbis plugins/flac plugins/wavpack plugins/sndfile plugins/vfs_curl plugins/cdda plugins/gtkui plugins/alsa plugins/ffmpeg plugins/hotkeys plugins/oss plugins/artwork plugins/adplug plugins/ffap plugins/sid plugins/nullout plugins/supereq plugins/vtx plugins/gme plugins/pulse plugins/notify plugins/musepack plugins/wildmidi plugins/tta plugins/dca plugins/aac plugins/mms plugins/shellexec plugins/dsp_libsrc plugins/m3u plugins/vfs_zip plugins/converter plugins/medialib" AM_CONDITIONAL(HAVE_VORBIS, test "x$HAVE_VORBISPLUGIN" = "xyes") AM_CONDITIONAL(HAVE_FLAC, test "x$HAVE_FLACPLUGIN" = "xyes") @@ -592,6 +597,7 @@ AM_CONDITIONAL(HAVE_IMLIB2, test "x$HAVE_IMLIB2" = "xyes") AM_CONDITIONAL(HAVE_JPEG, test "x$HAVE_JPEG" = "xyes") AM_CONDITIONAL(HAVE_PNG, test "x$HAVE_PNG" = "xyes") AM_CONDITIONAL(HAVE_YASM, test "x$HAVE_YASM" = "xyes") +AM_CONDITIONAL(HAVE_MEDIALIB, test "x$HAVE_MEDIALIB" = "xyes") AC_SUBST(PLUGINS_DIRS) @@ -653,6 +659,7 @@ PRINT_PLUGIN_INFO([dsp_src],[High quality samplerate conversion using libsampler PRINT_PLUGIN_INFO([m3u],[M3U and PLS playlist support],[test "x$HAVE_M3U" = "xyes"]) PRINT_PLUGIN_INFO([vfs_zip],[zip archive support],[test "x$HAVE_VFS_ZIP" = "xyes"]) PRINT_PLUGIN_INFO([converter],[plugin for converting files to any formats],[test "x$HAVE_CONVERTER" = "xyes"]) +PRINT_PLUGIN_INFO([medialib],[media library support plugin],[test "x$HAVE_MEDIALIB" = "xyes"]) echo @@ -694,6 +701,7 @@ plugins/dsp_libsrc/Makefile plugins/m3u/Makefile plugins/vfs_zip/Makefile plugins/converter/Makefile +plugins/medialib/Makefile intl/Makefile po/Makefile.in deadbeef.desktop @@ -76,7 +76,7 @@ extern "C" { // 0.1 -- deadbeef-0.2.0 #define DB_API_VERSION_MAJOR 1 -#define DB_API_VERSION_MINOR 1 +#define DB_API_VERSION_MINOR 2 #define DDB_PLUGIN_SET_API_VERSION\ .plugin.api_vmajor = DB_API_VERSION_MAJOR,\ @@ -267,6 +267,9 @@ enum { DB_EV_PLAYLISTSWITCHED = 18, // playlist switch occured DB_EV_SEEK = 19, // seek current track to position p1 (ms) + // new in 1.2 + DB_EV_SELCHANGED = 20, // selection changed in playlist p1 iter p2 + DB_EV_FIRST = 1000, DB_EV_SONGCHANGED = 1000, // current song changed from one to another, ctx=ddb_event_trackchange_t DB_EV_SONGSTARTED = 1001, // song started playing, ctx=ddb_event_track_t @@ -725,6 +728,17 @@ typedef struct { int (*dsp_preset_load) (const char *fname, struct ddb_dsp_context_s **head); int (*dsp_preset_save) (const char *fname, struct ddb_dsp_context_s *head); void (*dsp_preset_free) (struct ddb_dsp_context_s *head); + + // new 1.2 APIs + ddb_playlist_t *(*plt_alloc) (const char *title); + void (*plt_free) (ddb_playlist_t *plt); + //int (*plt_insert) (ddb_playlist_t *plt, int before); + void (*plt_set_fast_mode) (ddb_playlist_t *plt, int fast); + int (*plt_is_fast_mode) (ddb_playlist_t *plt); + const char * (*metacache_add_string) (const char *str); + void (*metacache_remove_string) (const char *str); + void (*metacache_ref) (const char *str); + void (*metacache_unref) (const char *str); } DB_functions_t; enum { diff --git a/metacache.c b/metacache.c index 84acaa91..13616516 100644 --- a/metacache.c +++ b/metacache.c @@ -114,3 +114,15 @@ metacache_remove_string (const char *str) { chain = chain->next; } } + +void +metacache_ref (const char *str) { + uint32_t *refc = (uint32_t)(str-5); + *refc++; +} + +void +metacache_unref (const char *str) { + uint32_t *refc = (uint32_t *)(str-5); + *refc--; +} diff --git a/metacache.h b/metacache.h index b5187c0f..50c5cb7f 100644 --- a/metacache.h +++ b/metacache.h @@ -25,4 +25,10 @@ metacache_add_string (const char *str); void metacache_remove_string (const char *str); +void +metacache_ref (const char *str); + +void +metacache_unref (const char *str); + #endif @@ -327,6 +327,15 @@ plt_get_sel_count (int plt) { return 0; } +playlist_t * +plt_alloc (const char *title) { + playlist_t *plt = malloc (sizeof (playlist_t)); + memset (plt, 0, sizeof (playlist_t)); + plt->refc = 1; + plt->title = strdup (title); + return plt; +} + int plt_add (int before, const char *title) { assert (before >= 0); @@ -335,10 +344,7 @@ plt_add (int before, const char *title) { fprintf (stderr, "can't create more than 100 playlists. sorry.\n"); return -1; } - playlist_t *plt = malloc (sizeof (playlist_t)); - memset (plt, 0, sizeof (playlist_t)); - plt->refc = 1; - plt->title = strdup (title); + playlist_t *plt = plt_alloc (title); plt_modified (plt); LOCK; @@ -398,7 +404,7 @@ plt_add (int before, const char *title) { conf_save (); messagepump_push (DB_EV_PLAYLISTSWITCHED, 0, 0, 0); } - return playlists_count-1; + return before; } // NOTE: caller must ensure that configuration is saved after that call @@ -980,7 +986,7 @@ plt_process_cue_track (playlist_t *playlist, playItem_t *after, const char *fnam pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKPEAK, atof (replaygain_track_peak)); } it->_flags |= DDB_IS_SUBTRACK | DDB_TAG_CUESHEET; - after = pl_insert_item (after, it); + after = plt_insert_item (playlist, after, it); pl_item_unref (it); *prev = it; return it; @@ -2876,7 +2882,7 @@ pl_format_time (float t, char *dur, int size) { } } else { - strcpy (dur, "-:--"); + strcpy (dur, "∞"); } } @@ -3897,3 +3903,13 @@ plt_init_shuffle_albums (playlist_t *plt, int r) { } pl_unlock (); } + +void +plt_set_fast_mode (playlist_t *plt, int fast) { + plt->fast_mode = (unsigned)fast; +} + +int +plt_is_fast_mode (playlist_t *plt) { + return plt->fast_mode; +} @@ -56,6 +56,7 @@ typedef struct playlist_s { int current_row[PL_MAX_ITERATORS]; // current row (cursor) struct DB_metaInfo_s *meta; // linked list storing metainfo int refc; + unsigned fast_mode : 1; } playlist_t; // global playlist control functions @@ -92,6 +93,9 @@ plt_ref (playlist_t *plt); void plt_unref (playlist_t *plt); +playlist_t * +plt_alloc (const char *title); + void plt_free (playlist_t *plt); @@ -437,4 +441,10 @@ pl_get_playlist (playItem_t *it); void plt_init_shuffle_albums (playlist_t *plt, int r); +void +plt_set_fast_mode (playlist_t *plt, int fast); + +int +plt_is_fast_mode (playlist_t *plt); + #endif // __PLAYLIST_H @@ -45,6 +45,7 @@ #include "premix.h" #include "dsppreset.h" #include "pltmeta.h" +#include "metacache.h" #define trace(...) { fprintf(stderr, __VA_ARGS__); } //#define trace(fmt,...) @@ -316,7 +317,17 @@ static DB_functions_t deadbeef_api = { // dsp preset management .dsp_preset_load = dsp_preset_load, .dsp_preset_save = dsp_preset_save, - .dsp_preset_free = dsp_preset_free + .dsp_preset_free = dsp_preset_free, + // new 1.2 APIs + .plt_alloc = (ddb_playlist_t *(*)(const char *title))plt_alloc, + .plt_free = (void (*)(ddb_playlist_t *plt))plt_free, + //.plt_insert = plt_insert, + .plt_set_fast_mode = (void (*)(ddb_playlist_t *plt, int fast))plt_set_fast_mode, + .plt_is_fast_mode = (int (*)(ddb_playlist_t *plt))plt_is_fast_mode, + .metacache_add_string = metacache_add_string, + .metacache_remove_string = metacache_remove_string, + .metacache_ref = metacache_ref, + .metacache_unref = metacache_unref, }; DB_functions_t *deadbeef = &deadbeef_api; diff --git a/plugins/artwork/artwork.c b/plugins/artwork/artwork.c index d603c06d..e395756d 100644 --- a/plugins/artwork/artwork.c +++ b/plugins/artwork/artwork.c @@ -117,6 +117,9 @@ queue_add (const char *fname, const char *artist, const char *album, int img_siz for (cover_query_t *q = queue; q; q = q->next) { if (!strcasecmp (artist, q->artist) || !strcasecmp (album, q->album)) { deadbeef->mutex_unlock (mutex); + if (callback) { + callback (NULL, NULL, NULL, user_data); + } return; // already in queue } } @@ -154,6 +157,9 @@ queue_pop (void) { if (queue->album) { free (queue->album); } + if (queue->callback) { + queue->callback (NULL, NULL, NULL, queue->user_data); + } free (queue); } queue = next; @@ -1044,6 +1050,7 @@ fetcher_thread (void *none) } if (param->callback) { param->callback (param->fname, param->artist, param->album, param->user_data); + param->callback = NULL; } } queue_pop (); @@ -1098,16 +1105,25 @@ get_album_art (const char *fname, const char *artist, const char *album, int siz if (!*artist || !*album) { //give up + if (callback) { + callback (NULL, NULL, NULL, user_data); + } return size == -1 ? strdup (get_default_cover ()) : NULL; } if (!deadbeef->is_local_file (fname)) { + if (callback) { + callback (NULL, NULL, NULL, user_data); + } return size == -1 ? strdup (get_default_cover ()) : NULL; } make_cache_path (path, sizeof (path), album, artist, size); char *p = find_image (path); if (p) { + if (callback) { + callback (NULL, NULL, NULL, user_data); + } return p; } @@ -1126,6 +1142,9 @@ get_album_art (const char *fname, const char *artist, const char *album, int siz else { int res = copy_file (unscaled_path, path, size); if (!res) { + if (callback) { + callback (NULL, NULL, NULL, user_data); + } return strdup (path); } } diff --git a/plugins/gtkui/Makefile.am b/plugins/gtkui/Makefile.am index 329f14b2..7c82d4e5 100644 --- a/plugins/gtkui/Makefile.am +++ b/plugins/gtkui/Makefile.am @@ -36,7 +36,8 @@ GTKUI_SOURCES = gtkui.c gtkui.h\ dspconfig.c dspconfig.h\ tagwritersettings.c tagwritersettings.h\ wingeom.c wingeom.h\ - pluginconf.h + pluginconf.h\ + widgets.c widgets.h sdkdir = $(pkgincludedir) sdk_HEADERS = gtkui_api.h diff --git a/plugins/gtkui/callbacks.c b/plugins/gtkui/callbacks.c index 4fdc73c3..ffff9a5a 100644 --- a/plugins/gtkui/callbacks.c +++ b/plugins/gtkui/callbacks.c @@ -44,6 +44,7 @@ #include "drawing.h" #include "eq.h" #include "wingeom.h" +#include "widgets.h" //#define trace(...) { fprintf (stderr, __VA_ARGS__); } #define trace(fmt,...) @@ -282,9 +283,8 @@ on_select_all1_activate (GtkMenuItem *menuitem, gpointer user_data) { deadbeef->pl_select_all (); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (pl, DDB_REFRESH_LIST); - pl = DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); + DdbListview *pl = DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")); if (pl) { ddb_listview_refresh (pl, DDB_REFRESH_LIST); } @@ -358,9 +358,6 @@ on_mainwin_key_press_event (GtkWidget *widget, deadbeef->conf_set_int ("playlist.current", pl); } } - else { - ddb_listview_handle_keypress (DDB_LISTVIEW (lookup_widget (mainwin, "playlist")), event->keyval, event->state); - } return FALSE; } @@ -837,6 +834,8 @@ void on_toggle_column_headers_activate (GtkMenuItem *menuitem, gpointer user_data) { + // FIXME! + return; GtkWidget *playlist = lookup_widget (mainwin, "playlist"); if (playlist) { if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem))) { @@ -902,6 +901,8 @@ void on_toggle_tabs (GtkMenuItem *menuitem, gpointer user_data) { + // FIXME! + return; GtkWidget *ts = lookup_widget (mainwin, "tabstrip"); if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem))) { deadbeef->conf_set_int ("gtkui.tabs.visible", 0); @@ -945,9 +946,8 @@ on_deselect_all1_activate (GtkMenuItem *menuitem, it = next; } deadbeef->pl_unlock (); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (pl, DDB_REFRESH_LIST); - pl = DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); + DdbListview *pl = DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")); if (pl) { ddb_listview_refresh (pl, DDB_REFRESH_LIST); } @@ -972,8 +972,7 @@ on_invert_selection1_activate (GtkMenuItem *menuitem, it = next; } deadbeef->pl_unlock (); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (pl, DDB_REFRESH_LIST); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } @@ -1132,9 +1131,7 @@ on_sort_by_title_activate (GtkMenuItem *menuitem, deadbeef->plt_sort (plt, PL_MAIN, -1, "%t", 1); deadbeef->plt_unref (plt); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_clear_sort (pl); - ddb_listview_refresh (pl, DDB_REFRESH_LIST | DDB_LIST_CHANGED); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } @@ -1146,9 +1143,7 @@ on_sort_by_track_nr_activate (GtkMenuItem *menuitem, deadbeef->plt_sort (plt, PL_MAIN, -1, "%n", 1); deadbeef->plt_unref (plt); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_clear_sort (pl); - ddb_listview_refresh (pl, DDB_REFRESH_LIST | DDB_LIST_CHANGED); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } @@ -1160,9 +1155,7 @@ on_sort_by_album_activate (GtkMenuItem *menuitem, deadbeef->plt_sort (plt, PL_MAIN, -1, "%b", 1); deadbeef->plt_unref (plt); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_clear_sort (pl); - ddb_listview_refresh (pl, DDB_REFRESH_LIST | DDB_LIST_CHANGED); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } @@ -1174,9 +1167,7 @@ on_sort_by_artist_activate (GtkMenuItem *menuitem, deadbeef->plt_sort (plt, PL_MAIN, -1, "%a", 1); deadbeef->plt_unref (plt); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_clear_sort (pl); - ddb_listview_refresh (pl, DDB_REFRESH_LIST | DDB_LIST_CHANGED); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } @@ -1188,9 +1179,7 @@ on_sort_by_date_activate (GtkMenuItem *menuitem, deadbeef->plt_sort (plt, PL_MAIN, -1, "%y", 1); deadbeef->plt_unref (plt); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_clear_sort (pl); - ddb_listview_refresh (pl, DDB_REFRESH_LIST | DDB_LIST_CHANGED); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } @@ -1224,11 +1213,18 @@ on_sort_by_custom_activate (GtkMenuItem *menuitem, deadbeef->plt_sort (plt, PL_MAIN, -1, fmt, order == 0 ? 1 : 0); deadbeef->plt_unref (plt); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_clear_sort (pl); - ddb_listview_refresh (pl, DDB_REFRESH_LIST | DDB_LIST_CHANGED); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } gtk_widget_destroy (dlg); dlg = NULL; } + +void +on_design_mode1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + gboolean act = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem)); + w_set_design_mode (act ? 1 : 0); +} + diff --git a/plugins/gtkui/callbacks.h b/plugins/gtkui/callbacks.h index fd92b376..1c407678 100644 --- a/plugins/gtkui/callbacks.h +++ b/plugins/gtkui/callbacks.h @@ -1155,3 +1155,7 @@ on_sort_by_custom_activate (GtkMenuItem *menuitem, void on_convert8to16_toggled (GtkToggleButton *togglebutton, gpointer user_data); + +void +on_design_mode1_activate (GtkMenuItem *menuitem, + gpointer user_data); diff --git a/plugins/gtkui/coverart.c b/plugins/gtkui/coverart.c index 4118aa66..e1dec261 100644 --- a/plugins/gtkui/coverart.c +++ b/plugins/gtkui/coverart.c @@ -45,6 +45,8 @@ typedef struct { typedef struct load_query_s { char *fname; int width; + void (*callback) (void *user_data); + void *user_data; struct load_query_s *next; } load_query_t; @@ -57,7 +59,7 @@ load_query_t *queue; load_query_t *tail; static void -queue_add (const char *fname, int width) { +queue_add (const char *fname, int width, void (*callback) (void *user_data), void *user_data) { deadbeef->mutex_lock (mutex); load_query_t *q; for (q = queue; q; q = q->next) { @@ -70,6 +72,8 @@ queue_add (const char *fname, int width) { memset (q, 0, sizeof (load_query_t)); q->fname = strdup (fname); q->width = width; + q->callback = callback; + q->user_data = user_data; if (tail) { tail->next = q; tail = q; @@ -108,7 +112,7 @@ loading_thread (void *none) { for (;;) { trace ("covercache: waiting for signal\n"); deadbeef->cond_wait (cond, mutex); - trace ("covercache: signal received\n"); + trace ("covercache: signal received (terminate=%d, queue=%p)\n", terminate, queue); deadbeef->mutex_unlock (mutex); while (!terminate && queue) { int cache_min = 0; @@ -144,6 +148,7 @@ loading_thread (void *none) { if (stat (queue->fname, &stat_buf) < 0) { trace ("failed to stat file %s\n", queue->fname); } + trace ("covercache: caching pixbuf for %s\n", queue->fname); GdkPixbuf *pixbuf = NULL; GError *error = NULL; pixbuf = gdk_pixbuf_new_from_file_at_scale (queue->fname, queue->width, queue->width, TRUE, &error); @@ -182,8 +187,12 @@ loading_thread (void *none) { struct stat stat_buf; deadbeef->mutex_unlock (mutex); } + + if (queue->callback) { + queue->callback (queue->user_data); + } queue_pop (); - g_idle_add (redraw_playlist_cb, NULL); + //g_idle_add (redraw_playlist_cb, NULL); } if (terminate) { break; @@ -191,20 +200,30 @@ loading_thread (void *none) { } } -void +typedef struct { + int width; + void (*callback)(void *user_data); + void *user_data; +} cover_avail_info_t; + +static void cover_avail_callback (const char *fname, const char *artist, const char *album, void *user_data) { + if (!fname) { + free (user_data); + return; + } + cover_avail_info_t *dt = user_data; // means requested image is now in disk cache // load it into main memory - GdkPixbuf *pb = get_cover_art (fname, artist, album, (intptr_t)user_data); + GdkPixbuf *pb = get_cover_art_callb (fname, artist, album, dt->width, dt->callback, dt->user_data); if (pb) { g_object_unref (pb); - // already in cache, redraw - g_idle_add (redraw_playlist_cb, NULL); } + free (dt); } static GdkPixbuf * -get_pixbuf (const char *fname, int width) { +get_pixbuf (const char *fname, int width, void (*callback)(void *user_data), void *user_data) { int requested_width = width; // find in cache deadbeef->mutex_lock (mutex); @@ -232,18 +251,45 @@ get_pixbuf (const char *fname, int width) { } #endif deadbeef->mutex_unlock (mutex); - queue_add (fname, width); + queue_add (fname, width, callback, user_data); return NULL; } +static void +redraw_playlist (void *user_data) { + g_idle_add (redraw_playlist_cb, NULL); +} + GdkPixbuf * get_cover_art (const char *fname, const char *artist, const char *album, int width) { if (!coverart_plugin) { return NULL; } - char *image_fname = coverart_plugin->get_album_art (fname, artist, album, -1, cover_avail_callback, (void *)(intptr_t)width); + cover_avail_info_t *dt = malloc (sizeof (cover_avail_info_t)); + dt->width = width; + dt->callback = redraw_playlist; + dt->user_data = NULL; + char *image_fname = coverart_plugin->get_album_art (fname, artist, album, -1, cover_avail_callback, (void*)dt); + if (image_fname) { + GdkPixbuf *pb = get_pixbuf (image_fname, width, redraw_playlist, NULL); + free (image_fname); + return pb; + } + return NULL; +} + +GdkPixbuf * +get_cover_art_callb (const char *fname, const char *artist, const char *album, int width, void (*callback) (void *user_data), void *user_data) { + if (!coverart_plugin) { + return NULL; + } + cover_avail_info_t *dt = malloc (sizeof (cover_avail_info_t)); + dt->width = width; + dt->callback = callback; + dt->user_data = user_data; + char *image_fname = coverart_plugin->get_album_art (fname, artist, album, -1, cover_avail_callback, dt); if (image_fname) { - GdkPixbuf *pb = get_pixbuf (image_fname, width); + GdkPixbuf *pb = get_pixbuf (image_fname, width, callback, user_data); free (image_fname); return pb; } diff --git a/plugins/gtkui/coverart.h b/plugins/gtkui/coverart.h index 689e0f80..aafec0d6 100644 --- a/plugins/gtkui/coverart.h +++ b/plugins/gtkui/coverart.h @@ -32,6 +32,10 @@ GdkPixbuf * get_cover_art (const char *fname, const char *artist, const char *album, int width); +GdkPixbuf * +get_cover_art_callb (const char *fname, const char *artist, const char *album, int width, void +(*cover_avail_callback) (void *user_data), void *user_data); + void coverart_reset_queue (void); diff --git a/plugins/gtkui/ddbcellrenderertextmultiline.c b/plugins/gtkui/ddbcellrenderertextmultiline.c index a6e95853..aff449b5 100644 --- a/plugins/gtkui/ddbcellrenderertextmultiline.c +++ b/plugins/gtkui/ddbcellrenderertextmultiline.c @@ -1,4 +1,4 @@ -/* ddbcellrenderertextmultiline.c generated by valac 0.10.2, the Vala compiler +/* ddbcellrenderertextmultiline.c generated by valac, the Vala compiler * generated from ddbcellrenderertextmultiline.vala, do not modify */ /* @@ -25,8 +25,8 @@ #include <gtk/gtk.h> #include <stdlib.h> #include <string.h> -#include <gdk/gdk.h> #include <gdk/gdkkeysyms.h> +#include <gdk/gdk.h> #define DDB_TYPE_CELL_EDITABLE_TEXT_VIEW (ddb_cell_editable_text_view_get_type ()) @@ -84,16 +84,16 @@ static gpointer ddb_cell_editable_text_view_parent_class = NULL; static GtkCellEditableIface* ddb_cell_editable_text_view_gtk_cell_editable_parent_iface = NULL; static gpointer ddb_cell_renderer_text_multiline_parent_class = NULL; -GType ddb_cell_editable_text_view_get_type (void) G_GNUC_CONST; +GType ddb_cell_editable_text_view_get_type (void); enum { DDB_CELL_EDITABLE_TEXT_VIEW_DUMMY_PROPERTY }; static gboolean ddb_cell_editable_text_view_real_key_press_event (GtkWidget* base, GdkEventKey* event); -static void ddb_cell_editable_text_view_real_start_editing (GtkCellEditable* base, GdkEvent* event); +void ddb_cell_editable_text_view_start_editing (DdbCellEditableTextView* self, GdkEvent* event); DdbCellEditableTextView* ddb_cell_editable_text_view_new (void); DdbCellEditableTextView* ddb_cell_editable_text_view_construct (GType object_type); static void ddb_cell_editable_text_view_finalize (GObject* obj); -GType ddb_cell_renderer_text_multiline_get_type (void) G_GNUC_CONST; +GType ddb_cell_renderer_text_multiline_get_type (void); #define DDB_CELL_RENDERER_TEXT_MULTILINE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DDB_TYPE_CELL_RENDERER_TEXT_MULTILINE, DdbCellRendererTextMultilinePrivate)) enum { DDB_CELL_RENDERER_TEXT_MULTILINE_DUMMY_PROPERTY @@ -138,9 +138,8 @@ static gboolean ddb_cell_editable_text_view_real_key_press_event (GtkWidget* bas } -static void ddb_cell_editable_text_view_real_start_editing (GtkCellEditable* base, GdkEvent* event) { - DdbCellEditableTextView * self; - self = (DdbCellEditableTextView*) base; +void ddb_cell_editable_text_view_start_editing (DdbCellEditableTextView* self, GdkEvent* event) { + g_return_if_fail (self != NULL); g_return_if_fail (event != NULL); } @@ -166,7 +165,6 @@ static void ddb_cell_editable_text_view_class_init (DdbCellEditableTextViewClass static void ddb_cell_editable_text_view_gtk_cell_editable_interface_init (GtkCellEditableIface * iface) { ddb_cell_editable_text_view_gtk_cell_editable_parent_iface = g_type_interface_peek_parent (iface); - iface->start_editing = ddb_cell_editable_text_view_real_start_editing; } @@ -214,10 +212,10 @@ static void ddb_cell_renderer_text_multiline_gtk_cell_renderer_text_editing_done buf = _g_object_ref0 (gtk_text_view_get_buffer ((GtkTextView*) entry)); gtk_text_buffer_get_iter_at_offset (buf, &begin, 0); gtk_text_buffer_get_iter_at_offset (buf, &end, -1); - new_text = gtk_text_buffer_get_text (buf, &begin, &end, TRUE); + new_text = g_strdup (gtk_text_buffer_get_text (buf, &begin, &end, TRUE)); g_signal_emit_by_name ((GtkCellRendererText*) _self_, "edited", entry->tree_path, new_text); - _g_free0 (new_text); _g_object_unref0 (buf); + _g_free0 (new_text); } @@ -242,14 +240,14 @@ static GtkCellEditable* ddb_cell_renderer_text_multiline_real_start_editing (Gtk GtkListStore* store; GtkTreeIter iter = {0}; GValue v = {0}; - GValue _tmp1_ = {0}; GValue _tmp2_; + GValue _tmp1_ = {0}; gint mult; DdbCellEditableTextView* _tmp3_; char* _tmp4_; GtkTextBuffer* buf; - char* _tmp5_ = NULL; char* _tmp6_; + char* _tmp5_ = NULL; gboolean _tmp7_; self = (DdbCellRendererTextMultiline*) base; g_return_val_if_fail (event != NULL, NULL); @@ -273,8 +271,8 @@ static GtkCellEditable* ddb_cell_renderer_text_multiline_real_start_editing (Gtk self->priv->entry->tree_path = (_tmp4_ = g_strdup (path), _g_free0 (self->priv->entry->tree_path), _tmp4_); buf = gtk_text_buffer_new (NULL); if ((_tmp7_ = (_tmp6_ = (g_object_get ((GtkCellRendererText*) self, "text", &_tmp5_, NULL), _tmp5_)) != NULL, _g_free0 (_tmp6_), _tmp7_)) { - char* _tmp8_ = NULL; char* _tmp9_; + char* _tmp8_ = NULL; gtk_text_buffer_set_text (buf, _tmp9_ = (g_object_get ((GtkCellRendererText*) self, "text", &_tmp8_, NULL), _tmp8_), -1); _g_free0 (_tmp9_); } @@ -284,11 +282,11 @@ static GtkCellEditable* ddb_cell_renderer_text_multiline_real_start_editing (Gtk gtk_widget_set_size_request ((GtkWidget*) self->priv->entry, (*cell_area).width, (*cell_area).height); gtk_widget_show ((GtkWidget*) self->priv->entry); result = GTK_CELL_EDITABLE (self->priv->entry); - _g_object_unref0 (buf); - G_IS_VALUE (&v) ? (g_value_unset (&v), NULL) : NULL; - _g_object_unref0 (store); - _g_object_unref0 (tv); _gtk_tree_path_free0 (p); + _g_object_unref0 (tv); + _g_object_unref0 (store); + G_IS_VALUE (&v) ? (g_value_unset (&v), NULL) : NULL; + _g_object_unref0 (buf); return result; } diff --git a/plugins/gtkui/ddbcellrenderertextmultiline.h b/plugins/gtkui/ddbcellrenderertextmultiline.h index 2fec6b26..55d4a82c 100644 --- a/plugins/gtkui/ddbcellrenderertextmultiline.h +++ b/plugins/gtkui/ddbcellrenderertextmultiline.h @@ -1,4 +1,4 @@ -/* ddbcellrenderertextmultiline.h generated by valac 0.10.2, the Vala compiler, do not modify */ +/* ddbcellrenderertextmultiline.h generated by valac, the Vala compiler, do not modify */ #ifndef __DDBCELLRENDERERTEXTMULTILINE_H__ @@ -8,6 +8,7 @@ #include <gtk/gtk.h> #include <stdlib.h> #include <string.h> +#include <gdk/gdk.h> G_BEGIN_DECLS @@ -55,10 +56,11 @@ struct _DdbCellRendererTextMultilineClass { }; -GType ddb_cell_editable_text_view_get_type (void) G_GNUC_CONST; +GType ddb_cell_editable_text_view_get_type (void); +void ddb_cell_editable_text_view_start_editing (DdbCellEditableTextView* self, GdkEvent* event); DdbCellEditableTextView* ddb_cell_editable_text_view_new (void); DdbCellEditableTextView* ddb_cell_editable_text_view_construct (GType object_type); -GType ddb_cell_renderer_text_multiline_get_type (void) G_GNUC_CONST; +GType ddb_cell_renderer_text_multiline_get_type (void); DdbCellRendererTextMultiline* ddb_cell_renderer_text_multiline_new (void); DdbCellRendererTextMultiline* ddb_cell_renderer_text_multiline_construct (GType object_type); diff --git a/plugins/gtkui/ddbequalizer.c b/plugins/gtkui/ddbequalizer.c index 7cd9ee4f..cf1c10eb 100644 --- a/plugins/gtkui/ddbequalizer.c +++ b/plugins/gtkui/ddbequalizer.c @@ -1,4 +1,4 @@ -/* ddbequalizer.c generated by valac 0.10.2, the Vala compiler +/* ddbequalizer.c generated by valac, the Vala compiler * generated from ddbequalizer.vala, do not modify */ /* @@ -45,8 +45,8 @@ typedef struct _DdbEqualizerClass DdbEqualizerClass; typedef struct _DdbEqualizerPrivate DdbEqualizerPrivate; #define _gdk_cursor_unref0(var) ((var == NULL) ? NULL : (var = (gdk_cursor_unref (var), NULL))) #define _g_free0(var) (var = (g_free (var), NULL)) -#define _pango_font_description_free0(var) ((var == NULL) ? NULL : (var = (pango_font_description_free (var), NULL))) #define _g_object_unref0(var) ((var == NULL) ? NULL : (var = (g_object_unref (var), NULL))) +#define _pango_font_description_free0(var) ((var == NULL) ? NULL : (var = (pango_font_description_free (var), NULL))) struct _DdbEqualizer { GtkDrawingArea parent_instance; @@ -73,7 +73,7 @@ struct _DdbEqualizerPrivate { static gpointer ddb_equalizer_parent_class = NULL; -GType ddb_equalizer_get_type (void) G_GNUC_CONST; +GType ddb_equalizer_get_type (void); #define DDB_EQUALIZER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DDB_TYPE_EQUALIZER, DdbEqualizerPrivate)) enum { DDB_EQUALIZER_DUMMY_PROPERTY @@ -82,8 +82,8 @@ enum { #define DDB_EQUALIZER_spot_size 3 static gboolean ddb_equalizer_real_configure_event (GtkWidget* base, GdkEventConfigure* event); static void ddb_equalizer_real_realize (GtkWidget* base); -static gboolean ddb_equalizer_real_expose_event (GtkWidget* base, GdkEventExpose* event); static inline double ddb_equalizer_scale (DdbEqualizer* self, double val); +static gboolean ddb_equalizer_real_expose_event (GtkWidget* base, GdkEventExpose* event); static gboolean ddb_equalizer_in_curve_area (DdbEqualizer* self, double x, double y); static void ddb_equalizer_update_eq_drag (DdbEqualizer* self, double x, double y); static gboolean ddb_equalizer_real_button_press_event (GtkWidget* base, GdkEventButton* event); @@ -137,8 +137,8 @@ static gboolean ddb_equalizer_real_expose_event (GtkWidget* base, GdkEventExpose gint width; gint height; GdkDrawable* d; - GdkGCValues _tmp2_ = {0}; GdkGCValues _tmp3_; + GdkGCValues _tmp2_ = {0}; GdkGC* gc; double step; gint i = 0; @@ -154,14 +154,14 @@ static gboolean ddb_equalizer_real_expose_event (GtkWidget* base, GdkEventExpose char* _tmp10_; const char* _tmp11_; char* _tmp12_; - GdkRectangle _tmp13_ = {0}; GdkRectangle _tmp14_; + GdkRectangle _tmp13_ = {0}; gint count; - GdkRectangle _tmp16_ = {0}; GdkRectangle _tmp17_; + GdkRectangle _tmp16_ = {0}; gint bar_w; - GdkRectangle _tmp22_ = {0}; GdkRectangle _tmp23_; + GdkRectangle _tmp22_ = {0}; self = (DdbEqualizer*) base; fore_bright_color = (gtkui_get_bar_foreground_color (&_tmp0_), _tmp0_); c1 = fore_bright_color; @@ -328,8 +328,8 @@ static gboolean ddb_equalizer_real_expose_event (GtkWidget* base, GdkEventExpose i = 0; _tmp18_ = TRUE; while (TRUE) { - GdkRectangle _tmp19_ = {0}; GdkRectangle _tmp20_; + GdkRectangle _tmp19_ = {0}; if (!_tmp18_) { i++; } @@ -363,12 +363,12 @@ static gboolean ddb_equalizer_real_expose_event (GtkWidget* base, GdkEventExpose gdk_gc_set_line_attributes (gc, 1, GDK_LINE_ON_OFF_DASH, GDK_CAP_NOT_LAST, GDK_JOIN_MITER); gdk_draw_line (d, gc, self->priv->margin_left + 1, self->priv->mouse_y, width, self->priv->mouse_y); result = FALSE; - _g_free0 (tmp); - _pango_font_description_free0 (fd); - _g_object_unref0 (ctx); - _g_object_unref0 (l); - _g_object_unref0 (gc); _g_object_unref0 (d); + _g_object_unref0 (gc); + _g_object_unref0 (l); + _g_object_unref0 (ctx); + _pango_font_description_free0 (fd); + _g_free0 (tmp); return result; } diff --git a/plugins/gtkui/ddbequalizer.h b/plugins/gtkui/ddbequalizer.h index 2ce2ba5c..b0493d52 100644 --- a/plugins/gtkui/ddbequalizer.h +++ b/plugins/gtkui/ddbequalizer.h @@ -1,4 +1,4 @@ -/* ddbequalizer.h generated by valac 0.10.2, the Vala compiler, do not modify */ +/* ddbequalizer.h generated by valac, the Vala compiler, do not modify */ #ifndef __DDBEQUALIZER_H__ @@ -33,7 +33,7 @@ struct _DdbEqualizerClass { }; -GType ddb_equalizer_get_type (void) G_GNUC_CONST; +GType ddb_equalizer_get_type (void); void ddb_equalizer_set_band (DdbEqualizer* self, gint band, double v); double ddb_equalizer_get_band (DdbEqualizer* self, gint band); void ddb_equalizer_set_preamp (DdbEqualizer* self, double v); diff --git a/plugins/gtkui/ddblistview.c b/plugins/gtkui/ddblistview.c index b6140456..bc2ded0a 100644 --- a/plugins/gtkui/ddblistview.c +++ b/plugins/gtkui/ddblistview.c @@ -265,6 +265,14 @@ ddb_listview_motion_notify_event (GtkWidget *widget, GdkEventMotion *event, gpointer user_data); +gboolean +ddb_listview_list_key_press_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data); + +gboolean +ddb_listview_list_focus_in_event (GtkWidget *widget, GdkEvent *event, gpointer user_data); + +gboolean +ddb_listview_list_focus_out_event (GtkWidget *widget, GdkEvent *event, gpointer user_data); static void ddb_listview_class_init(DdbListviewClass *class) @@ -351,7 +359,9 @@ ddb_listview_init(DdbListview *listview) listview->list = gtk_drawing_area_new (); gtk_widget_show (listview->list); gtk_box_pack_start (GTK_BOX (vbox), listview->list, TRUE, TRUE, 0); - gtk_widget_set_events (listview->list, GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); + GTK_WIDGET_SET_FLAGS (listview->list, GTK_CAN_FOCUS); + GTK_WIDGET_SET_FLAGS (listview->list, GTK_CAN_DEFAULT); + gtk_widget_set_events (listview->list, GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_FOCUS_CHANGE_MASK); listview->hscrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, 0, 0, 0, 0))); gtk_widget_show (listview->hscrollbar); @@ -384,7 +394,7 @@ ddb_listview_init(DdbListview *listview) g_signal_connect ((gpointer) listview->header, "motion_notify_event", G_CALLBACK (ddb_listview_header_motion_notify_event), NULL); - g_signal_connect ((gpointer) listview->header, "button_press_event", + g_signal_connect_after ((gpointer) listview->header, "button_press_event", G_CALLBACK (ddb_listview_header_button_press_event), NULL); g_signal_connect ((gpointer) listview->header, "button_release_event", @@ -396,7 +406,7 @@ ddb_listview_init(DdbListview *listview) g_signal_connect ((gpointer) listview->list, "realize", G_CALLBACK (ddb_listview_list_realize), NULL); - g_signal_connect ((gpointer) listview->list, "button_press_event", + g_signal_connect_after ((gpointer) listview->list, "button_press_event", G_CALLBACK (ddb_listview_list_button_press_event), NULL); g_signal_connect ((gpointer) listview->list, "scroll_event", @@ -435,6 +445,10 @@ ddb_listview_init(DdbListview *listview) g_signal_connect ((gpointer) listview->hscrollbar, "value_changed", G_CALLBACK (ddb_listview_hscroll_value_changed), NULL); + + g_signal_connect ((gpointer)listview->list, "key_press_event", G_CALLBACK (ddb_listview_list_key_press_event), NULL); + g_signal_connect ((gpointer)listview->list, "focus_in_event", G_CALLBACK (ddb_listview_list_focus_in_event), NULL); + g_signal_connect ((gpointer)listview->list, "focus_out_event", G_CALLBACK (ddb_listview_list_focus_out_event), NULL); } GtkWidget * ddb_listview_new() @@ -1413,6 +1427,7 @@ ddb_listview_click_selection (DdbListview *ps, int ex, int ey, DdbListviewGroup UNREF (it); } deadbeef->pl_unlock (); + deadbeef->sendmessage (DB_EV_SELCHANGED, 0, deadbeef->plt_get_curr_idx (), PL_MAIN); } // {{{ expected behaviour for mouse1 without modifiers: @@ -2415,7 +2430,7 @@ ddb_listview_header_button_press_event (GtkWidget *widget, } ps->prev_header_x = -1; ps->last_header_motion_ev = -1; - return FALSE; + return TRUE; } gboolean @@ -2563,6 +2578,7 @@ ddb_listview_list_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data) { + gtk_widget_grab_focus (widget); DdbListview *ps = DDB_LISTVIEW (g_object_get_data (G_OBJECT (widget), "owner")); if (event->button == 1) { ddb_listview_list_mouse1_pressed (ps, event->state, event->x, event->y, event->type); @@ -2604,7 +2620,7 @@ ddb_listview_list_button_press_event (GtkWidget *widget, UNREF (it); } } - return FALSE; + return TRUE; } gboolean @@ -2969,3 +2985,23 @@ ddb_listview_clear_sort (DdbListview *listview) { } gtk_widget_queue_draw (listview->header); } + +gboolean +ddb_listview_list_key_press_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data) { + DdbListview *listview = DDB_LISTVIEW (g_object_get_data (G_OBJECT (widget), "owner")); + ddb_listview_handle_keypress (listview, event->keyval, event->state); + return FALSE; + +} + +gboolean +ddb_listview_list_focus_in_event (GtkWidget *widget, GdkEvent *event, gpointer user_data) { + GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS); + return FALSE; +} + +gboolean +ddb_listview_list_focus_out_event (GtkWidget *widget, GdkEvent *event, gpointer user_data) { + GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS); + return FALSE; +} diff --git a/plugins/gtkui/ddbseekbar.c b/plugins/gtkui/ddbseekbar.c index 1ad285a7..d70ebc05 100644 --- a/plugins/gtkui/ddbseekbar.c +++ b/plugins/gtkui/ddbseekbar.c @@ -1,4 +1,4 @@ -/* ddbseekbar.c generated by valac 0.10.2, the Vala compiler +/* ddbseekbar.c generated by valac, the Vala compiler * generated from ddbseekbar.vala, do not modify */ /* @@ -51,7 +51,7 @@ struct _DdbSeekbarClass { static gpointer ddb_seekbar_parent_class = NULL; -GType ddb_seekbar_get_type (void) G_GNUC_CONST; +GType ddb_seekbar_get_type (void); enum { DDB_SEEKBAR_DUMMY_PROPERTY }; @@ -138,8 +138,8 @@ static gboolean ddb_seekbar_real_configure_event (GtkWidget* base, GdkEventConfi DdbSeekbar* ddb_seekbar_construct (GType object_type) { - DdbSeekbar * self = NULL; - self = (DdbSeekbar*) gtk_widget_new (object_type, NULL); + DdbSeekbar * self; + self = g_object_newv (object_type, 0, NULL); return self; } diff --git a/plugins/gtkui/ddbseekbar.h b/plugins/gtkui/ddbseekbar.h index c975654e..28c69cd3 100644 --- a/plugins/gtkui/ddbseekbar.h +++ b/plugins/gtkui/ddbseekbar.h @@ -1,4 +1,4 @@ -/* ddbseekbar.h generated by valac 0.10.2, the Vala compiler, do not modify */ +/* ddbseekbar.h generated by valac, the Vala compiler, do not modify */ #ifndef __DDBSEEKBAR_H__ @@ -31,7 +31,7 @@ struct _DdbSeekbarClass { }; -GType ddb_seekbar_get_type (void) G_GNUC_CONST; +GType ddb_seekbar_get_type (void); DdbSeekbar* ddb_seekbar_new (void); DdbSeekbar* ddb_seekbar_construct (GType object_type); diff --git a/plugins/gtkui/ddbtabstrip.c b/plugins/gtkui/ddbtabstrip.c index f8f3e6c4..e1882d1e 100644 --- a/plugins/gtkui/ddbtabstrip.c +++ b/plugins/gtkui/ddbtabstrip.c @@ -25,7 +25,7 @@ #include "gtkui.h" #include "interface.h" #include "support.h" -#include "ddblistview.h" +#include "mainplaylist.h" #define GLADE_HOOKUP_OBJECT(component,widget,name) \ g_object_set_data_full (G_OBJECT (component), name, \ @@ -220,7 +220,6 @@ ddb_tabstrip_class_init(DdbTabStripClass *class) widget_class->realize = ddb_tabstrip_realize; widget_class->size_allocate = ddb_tabstrip_size_allocate; widget_class->expose_event = on_tabstrip_expose_event; - widget_class->button_press_event = on_tabstrip_button_press_event; widget_class->button_release_event = on_tabstrip_button_release_event; widget_class->configure_event = on_tabstrip_configure_event; widget_class->motion_notify_event = on_tabstrip_motion_notify_event; @@ -253,8 +252,6 @@ on_tabstrip_drag_data_received (GtkWidget *widget, guint target_type, guint time) { - DdbListview *ps = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - gchar *ptr=(char*)data->data; if (target_type == 0) { // uris // this happens when dropped from file manager @@ -262,7 +259,7 @@ on_tabstrip_drag_data_received (GtkWidget *widget, memcpy (mem, ptr, data->length); mem[data->length] = 0; // we don't pass control structure, but there's only one drag-drop view currently - ps->binding->external_drag_n_drop (NULL, mem, data->length); + gtkui_receive_fm_drop (NULL, mem, data->length); } else if (target_type == 1) { uint32_t *d= (uint32_t *)ptr; @@ -271,7 +268,8 @@ on_tabstrip_drag_data_received (GtkWidget *widget, int length = (data->length/4)-1; ddb_playlist_t *p = deadbeef->plt_get_for_idx (plt); if (p) { - ps->binding->drag_n_drop (NULL, p, d, length, drag_context->action == GDK_ACTION_COPY ? 1 : 0); + //ps->binding->drag_n_drop (NULL, p, d, length, drag_context->action == GDK_ACTION_COPY ? 1 : 0); + main_drag_n_drop (NULL, p, d, length, drag_context->action == GDK_ACTION_COPY ? 1 : 0); deadbeef->plt_unref (p); } } @@ -283,16 +281,16 @@ on_tabstrip_drag_leave (GtkWidget *widget, GdkDragContext *drag_context, guint time) { - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_list_drag_leave (pl->list, drag_context, time, NULL); +// DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); +// ddb_listview_list_drag_leave (pl->list, drag_context, time, NULL); } void on_tabstrip_drag_end (GtkWidget *widget, GdkDragContext *drag_context) { - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_list_drag_end (pl->list, drag_context, NULL); +// DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); +// ddb_listview_list_drag_end (pl->list, drag_context, NULL); } GtkWidget * ddb_tabstrip_new() { @@ -309,6 +307,9 @@ ddb_tabstrip_init(DdbTabStrip *tabstrip) tabstrip->dragpt[1] = 0; tabstrip->prev_x = 0; tabstrip->movepos = 0; + g_signal_connect_after ((gpointer) tabstrip, "button_press_event", + G_CALLBACK (on_tabstrip_button_press_event), + NULL); } static int tab_clicked = -1; @@ -713,8 +714,6 @@ on_remove_playlist1_activate (GtkMenuItem *menuitem, { if (tab_clicked != -1) { deadbeef->plt_remove (tab_clicked); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (pl, DDB_LIST_CHANGED | DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); search_refresh (); int playlist = deadbeef->plt_get_curr_idx (); deadbeef->conf_set_int ("playlist.current", playlist); @@ -845,7 +844,7 @@ on_tabstrip_button_press_event(GtkWidget *widget, ts->scroll_direction = -1; ts->scroll_timer = g_timeout_add (300, tabstrip_scroll_cb, ts); } - return FALSE; + return TRUE; } else if (event->x >= widget->allocation.width - arrow_widget_width) { if (event->type == GDK_BUTTON_PRESS) { @@ -853,7 +852,7 @@ on_tabstrip_button_press_event(GtkWidget *widget, ts->scroll_direction = 1; ts->scroll_timer = g_timeout_add (300, tabstrip_scroll_cb, ts); } - return FALSE; + return TRUE; } } if (tab_clicked != -1) { @@ -866,9 +865,9 @@ on_tabstrip_button_press_event(GtkWidget *widget, if (playlist != -1) { gtkui_playlist_set_curr (playlist); } - return FALSE; + return TRUE; } - return FALSE; + return TRUE; } // adjust scroll if clicked tab spans border @@ -904,21 +903,19 @@ on_tabstrip_button_press_event(GtkWidget *widget, if (playlist != -1) { gtkui_playlist_set_curr (playlist); } - return FALSE; + return TRUE; } else if (deadbeef->conf_get_int ("gtkui.mmb_delete_playlist", 1)) { if (tab_clicked != -1) { deadbeef->plt_remove (tab_clicked); // force invalidation of playlist cache - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (pl, DDB_LIST_CHANGED | DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); search_refresh (); int playlist = deadbeef->plt_get_curr_idx (); deadbeef->conf_set_int ("playlist.current", playlist); } } } - return FALSE; + return TRUE; } diff --git a/plugins/gtkui/deadbeef.glade b/plugins/gtkui/deadbeef.glade index 73759b65..926fa6ab 100644 --- a/plugins/gtkui/deadbeef.glade +++ b/plugins/gtkui/deadbeef.glade @@ -62,7 +62,7 @@ <accelerator key="O" modifiers="GDK_CONTROL_MASK" signal="activate"/> <child internal-child="image"> - <widget class="GtkImage" id="image555"> + <widget class="GtkImage" id="image563"> <property name="visible">True</property> <property name="stock">gtk-open</property> <property name="icon_size">1</property> @@ -89,7 +89,7 @@ <signal name="activate" handler="on_add_files_activate" last_modification_time="Sat, 04 Jul 2009 13:04:01 GMT"/> <child internal-child="image"> - <widget class="GtkImage" id="image556"> + <widget class="GtkImage" id="image564"> <property name="visible">True</property> <property name="stock">gtk-add</property> <property name="icon_size">1</property> @@ -110,7 +110,7 @@ <signal name="activate" handler="on_add_folders_activate" last_modification_time="Sun, 06 Sep 2009 17:51:40 GMT"/> <child internal-child="image"> - <widget class="GtkImage" id="image557"> + <widget class="GtkImage" id="image565"> <property name="visible">True</property> <property name="stock">gtk-add</property> <property name="icon_size">1</property> @@ -181,7 +181,7 @@ <accelerator key="Q" modifiers="GDK_CONTROL_MASK" signal="activate"/> <child internal-child="image"> - <widget class="GtkImage" id="image558"> + <widget class="GtkImage" id="image566"> <property name="visible">True</property> <property name="stock">gtk-quit</property> <property name="icon_size">1</property> @@ -215,7 +215,7 @@ <signal name="activate" handler="on_clear1_activate" last_modification_time="Sun, 06 Sep 2009 18:30:03 GMT"/> <child internal-child="image"> - <widget class="GtkImage" id="image559"> + <widget class="GtkImage" id="image567"> <property name="visible">True</property> <property name="stock">gtk-clear</property> <property name="icon_size">1</property> @@ -274,7 +274,7 @@ <signal name="activate" handler="on_remove1_activate" last_modification_time="Sun, 06 Sep 2009 18:30:03 GMT"/> <child internal-child="image"> - <widget class="GtkImage" id="image560"> + <widget class="GtkImage" id="image568"> <property name="visible">True</property> <property name="stock">gtk-remove</property> <property name="icon_size">1</property> @@ -444,6 +444,16 @@ <signal name="activate" handler="on_toggle_eq" last_modification_time="Sat, 20 Mar 2010 12:28:50 GMT"/> </widget> </child> + + <child> + <widget class="GtkCheckMenuItem" id="design_mode1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Design mode</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <signal name="activate" handler="on_design_mode1_activate" last_modification_time="Fri, 17 Jun 2011 19:10:50 GMT"/> + </widget> + </child> </widget> </child> </widget> @@ -472,7 +482,7 @@ <property name="visible">True</property> <property name="label" translatable="yes">Linear</property> <property name="use_underline">True</property> - <property name="active">False</property> + <property name="active">True</property> <signal name="activate" handler="on_order_linear_activate" last_modification_time="Sat, 08 Aug 2009 12:26:33 GMT"/> </widget> </child> @@ -482,7 +492,7 @@ <property name="visible">True</property> <property name="label" translatable="yes">Shuffle tracks</property> <property name="use_underline">True</property> - <property name="active">False</property> + <property name="active">True</property> <property name="group">order_linear</property> <signal name="activate" handler="on_order_shuffle_activate" last_modification_time="Fri, 21 Aug 2009 16:46:02 GMT"/> </widget> @@ -493,7 +503,7 @@ <property name="visible">True</property> <property name="label" translatable="yes">Shuffle albums</property> <property name="use_underline">True</property> - <property name="active">False</property> + <property name="active">True</property> <property name="group">order_linear</property> <signal name="activate" handler="on_order_shuffle_albums_activate" last_modification_time="Sun, 12 Dec 2010 18:14:47 GMT"/> </widget> @@ -528,7 +538,7 @@ <property name="visible">True</property> <property name="label" translatable="yes">Loop All</property> <property name="use_underline">True</property> - <property name="active">False</property> + <property name="active">True</property> <signal name="activate" handler="on_loop_all_activate" last_modification_time="Sat, 08 Aug 2009 12:26:33 GMT"/> </widget> </child> @@ -538,7 +548,7 @@ <property name="visible">True</property> <property name="label" translatable="yes">Loop Single Song</property> <property name="use_underline">True</property> - <property name="active">False</property> + <property name="active">True</property> <property name="group">loop_all</property> <signal name="activate" handler="on_loop_single_activate" last_modification_time="Sat, 08 Aug 2009 12:26:33 GMT"/> </widget> @@ -627,7 +637,7 @@ <signal name="activate" handler="on_help1_activate" last_modification_time="Tue, 08 Sep 2009 17:32:06 GMT"/> <child internal-child="image"> - <widget class="GtkImage" id="image561"> + <widget class="GtkImage" id="image569"> <property name="visible">True</property> <property name="stock">gtk-help</property> <property name="icon_size">1</property> @@ -687,7 +697,7 @@ <signal name="activate" handler="on_about1_activate" last_modification_time="Sat, 04 Jul 2009 12:57:58 GMT"/> <child internal-child="image"> - <widget class="GtkImage" id="image562"> + <widget class="GtkImage" id="image570"> <property name="visible">True</property> <property name="stock">gtk-about</property> <property name="icon_size">1</property> @@ -708,7 +718,7 @@ <signal name="activate" handler="on_translators1_activate" last_modification_time="Sun, 19 Sep 2010 13:38:07 GMT"/> <child internal-child="image"> - <widget class="GtkImage" id="image563"> + <widget class="GtkImage" id="image571"> <property name="visible">True</property> <property name="stock">gtk-about</property> <property name="icon_size">1</property> @@ -928,47 +938,6 @@ </child> <child> - <widget class="Custom" id="tabstrip"> - <property name="height_request">24</property> - <property name="visible">True</property> - <property name="creation_function">create_tabstrip_widget</property> - <property name="int1">0</property> - <property name="int2">0</property> - <property name="last_modification_time">Thu, 18 Feb 2010 18:05:36 GMT</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">True</property> - </packing> - </child> - - <child> - <widget class="GtkFrame" id="frame1"> - <property name="border_width">1</property> - <property name="visible">True</property> - <property name="label_xalign">0</property> - <property name="label_yalign">0.5</property> - <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property> - - <child> - <widget class="Custom" id="playlist"> - <property name="visible">True</property> - <property name="creation_function">create_ddb_listview_widget</property> - <property name="int1">0</property> - <property name="int2">0</property> - <property name="last_modification_time">Sat, 13 Feb 2010 20:26:03 GMT</property> - </widget> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> - - <child> <widget class="GtkVBox" id="plugins_bottom_vbox"> <property name="visible">True</property> <property name="homogeneous">False</property> @@ -980,8 +949,8 @@ </widget> <packing> <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> + <property name="expand">True</property> + <property name="fill">True</property> </packing> </child> diff --git a/plugins/gtkui/fileman.c b/plugins/gtkui/fileman.c index 8947c821..4b1b6949 100644 --- a/plugins/gtkui/fileman.c +++ b/plugins/gtkui/fileman.c @@ -104,17 +104,15 @@ static void open_files_worker (void *data) { GSList *lst = (GSList *)data; gtkpl_add_files (lst); - gtkui_playlist_changed (); - extern GtkWidget *mainwin; - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_set_cursor (pl, 0); + deadbeef->pl_set_cursor (PL_MAIN, 0); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); deadbeef->sendmessage (DB_EV_PLAY_CURRENT, 0, 1, 0); } void gtkui_open_files (struct _GSList *lst) { deadbeef->pl_clear (); - playlist_refresh (); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); intptr_t tid = deadbeef->thread_start (open_files_worker, lst); deadbeef->thread_detach (tid); @@ -164,13 +162,14 @@ strcopy_special (char *dest, const char *src, int len) { static gboolean set_dnd_cursor_idle (gpointer data) { - DdbListview *listview = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); if (!data) { - ddb_listview_set_cursor (listview, -1); + deadbeef->pl_set_cursor (PL_MAIN, -1); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); return FALSE; } int cursor = deadbeef->pl_get_idx_of (DB_PLAYITEM (data)); - ddb_listview_set_cursor (listview, cursor); + deadbeef->pl_set_cursor (PL_MAIN, cursor); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); return FALSE; } @@ -182,7 +181,6 @@ gtkpl_add_fm_dropped_files (DB_playItem_t *drop_before, char *ptr, int length) { deadbeef->plt_unref (plt); return; } - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); DdbListviewIter first = NULL; DdbListviewIter after = NULL; diff --git a/plugins/gtkui/gtkui.c b/plugins/gtkui/gtkui.c index 4dd222b1..a7378953 100644 --- a/plugins/gtkui/gtkui.c +++ b/plugins/gtkui/gtkui.c @@ -29,7 +29,6 @@ #include "../../gettext.h" #include "gtkui.h" #include "ddblistview.h" -#include "mainplaylist.h" #include "search.h" #include "progress.h" #include "interface.h" @@ -47,6 +46,7 @@ #include "pluginconf.h" #include "gtkui_api.h" #include "wingeom.h" +#include "widgets.h" #define trace(...) { fprintf(stderr, __VA_ARGS__); } //#define trace(fmt,...) @@ -337,7 +337,7 @@ activate_cb (gpointer nothing) { } void -redraw_queued_tracks (DdbListview *pl, int list) { +redraw_queued_tracks (DdbListview *pl) { DB_playItem_t *it; int idx = 0; deadbeef->pl_lock (); @@ -352,14 +352,10 @@ redraw_queued_tracks (DdbListview *pl, int list) { deadbeef->pl_unlock (); } -static gboolean -redraw_queued_tracks_cb (gpointer nothing) { - int iconified = gdk_window_get_state(mainwin->window) & GDK_WINDOW_STATE_ICONIFIED; - if (!gtk_widget_get_visible (mainwin) || iconified) { - return FALSE; - } - redraw_queued_tracks (DDB_LISTVIEW (lookup_widget (mainwin, "playlist")), PL_MAIN); - redraw_queued_tracks (DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")), PL_SEARCH); +gboolean +redraw_queued_tracks_cb (gpointer plt) { + DdbListview *list = plt; + redraw_queued_tracks (list); return FALSE; } @@ -376,7 +372,12 @@ gtkpl_songchanged_wrapper (DB_playItem_t *from, DB_playItem_t *to) { } g_idle_add (update_win_title_idle, ft); g_idle_add (redraw_seekbar_cb, NULL); - g_idle_add (redraw_queued_tracks_cb, NULL); + if (searchwin && searchwin->window) { + int iconified = gdk_window_get_state(searchwin->window) & GDK_WINDOW_STATE_ICONIFIED; + if (gtk_widget_get_visible (searchwin) && !iconified) { + g_idle_add (redraw_queued_tracks_cb, DDB_LISTVIEW (lookup_widget (searchwin, "searchlist"))); + } + } } void @@ -415,8 +416,8 @@ trackinfochanged_wrapper (DdbListview *playlist, DB_playItem_t *track, int iter) void gtkui_trackinfochanged (DB_playItem_t *track) { - GtkWidget *playlist = lookup_widget (mainwin, "playlist"); - trackinfochanged_wrapper (DDB_LISTVIEW (playlist), track, PL_MAIN); +// GtkWidget *playlist = lookup_widget (mainwin, "playlist"); +// trackinfochanged_wrapper (DDB_LISTVIEW (playlist), track, PL_MAIN); if (searchwin && gtk_widget_get_visible (searchwin)) { GtkWidget *search = lookup_widget (searchwin, "searchlist"); @@ -441,22 +442,10 @@ trackinfochanged_cb (gpointer data) { return FALSE; } -static gboolean -paused_cb (gpointer nothing) { - DB_playItem_t *curr = deadbeef->streamer_get_playing_track (); - if (curr) { - int idx = deadbeef->pl_get_idx_of (curr); - GtkWidget *playlist = lookup_widget (mainwin, "playlist"); - ddb_listview_draw_row (DDB_LISTVIEW (playlist), idx, (DdbListviewIter)curr); - deadbeef->pl_item_unref (curr); - } - return FALSE; -} - void playlist_refresh (void) { - DdbListview *ps = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (ps, DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); +// DdbListview *ps = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); +// ddb_listview_refresh (ps, DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); search_refresh (); } @@ -473,26 +462,6 @@ gtkui_playlist_changed (void) { static gboolean playlistswitch_cb (gpointer none) { - GtkWidget *tabstrip = lookup_widget (mainwin, "tabstrip"); - int curr = deadbeef->plt_get_curr_idx (); - char conf[100]; - snprintf (conf, sizeof (conf), "playlist.scroll.%d", curr); - int scroll = deadbeef->conf_get_int (conf, 0); - snprintf (conf, sizeof (conf), "playlist.cursor.%d", curr); - int cursor = deadbeef->conf_get_int (conf, -1); - ddb_tabstrip_refresh (DDB_TABSTRIP (tabstrip)); - DdbListview *listview = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - deadbeef->pl_set_cursor (PL_MAIN, cursor); - if (cursor != -1) { - DB_playItem_t *it = deadbeef->pl_get_for_idx_and_iter (cursor, PL_MAIN); - if (it) { - deadbeef->pl_set_selected (it, 1); - deadbeef->pl_item_unref (it); - } - } - - ddb_listview_refresh (listview, DDB_LIST_CHANGED | DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); - ddb_listview_set_vscroll (listview, scroll); search_refresh (); return FALSE; } @@ -825,40 +794,6 @@ on_add_location_activate (GtkMenuItem *menuitem, gtk_widget_destroy (dlg); } -static void -songchanged (DdbListview *ps, DB_playItem_t *from, DB_playItem_t *to) { - int to_idx = -1; - if (!ddb_listview_is_scrolling (ps) && to) { - int cursor_follows_playback = deadbeef->conf_get_int ("playlist.scroll.cursorfollowplayback", 0); - int scroll_follows_playback = deadbeef->conf_get_int ("playlist.scroll.followplayback", 0); - int plt = deadbeef->streamer_get_current_playlist (); - if (plt != -1) { - if (cursor_follows_playback && plt != deadbeef->plt_get_curr_idx ()) { - deadbeef->plt_set_curr_idx (plt); - } - to_idx = deadbeef->pl_get_idx_of (to); - if (to_idx != -1) { - if (cursor_follows_playback) { - ddb_listview_set_cursor_noscroll (ps, to_idx); - } - if (scroll_follows_playback && plt == deadbeef->plt_get_curr_idx ()) { - ddb_listview_scroll_to (ps, to_idx); - } - } - } - } - - if (from) { - int idx = deadbeef->pl_get_idx_of (from); - if (idx != -1) { - ddb_listview_draw_row (ps, idx, from); - } - } - if (to && to_idx != -1) { - ddb_listview_draw_row (ps, to_idx, to); - } -} - static gboolean update_win_title_idle (gpointer data) { struct fromto_t *ft = (struct fromto_t *)data; @@ -882,8 +817,6 @@ update_win_title_idle (gpointer data) { gtkui_set_titlebar (NULL); } } - // update playlist view - songchanged (DDB_LISTVIEW (lookup_widget (mainwin, "playlist")), from, to); if (from) { deadbeef->pl_item_unref (from); } @@ -941,11 +874,11 @@ volumebar_redraw (void) { gdk_window_invalidate_rect (volumebar->window, NULL, FALSE); } -void -tabstrip_redraw (void) { - GtkWidget *ts = lookup_widget (mainwin, "tabstrip"); - ddb_tabstrip_refresh (DDB_TABSTRIP (ts)); -} +//void +//tabstrip_redraw (void) { +// GtkWidget *ts = lookup_widget (mainwin, "tabstrip"); +// ddb_tabstrip_refresh (DDB_TABSTRIP (ts)); +//} static int gtk_initialized = 0; static gint refresh_timeout = 0; @@ -970,8 +903,22 @@ gtkui_setup_gui_refresh (void) { refresh_timeout = g_timeout_add (tm, gtkui_on_frameupdate, NULL); } +static void +send_messages_to_widgets (ddb_gtkui_widget_t *w, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { + for (ddb_gtkui_widget_t *c = w->children; c; c = c->next) { + send_messages_to_widgets (c, id, ctx, p1, p2); + } + if (w->message) { + w->message (w, id, ctx, p1, p2); + } +} + int gtkui_message (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { + ddb_gtkui_widget_t *rootwidget = w_get_rootwidget (); + if (rootwidget) { + send_messages_to_widgets (rootwidget, id, ctx, p1, p2); + } switch (id) { case DB_EV_ACTIVATED: g_idle_add (activate_cb, NULL); @@ -991,9 +938,9 @@ gtkui_message (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { g_idle_add (trackinfochanged_cb, ev->track); } break; - case DB_EV_PAUSED: - g_idle_add (paused_cb, NULL); - break; +// case DB_EV_PAUSED: +// g_idle_add (paused_cb, NULL); +// break; case DB_EV_PLAYLISTCHANGED: gtkui_playlist_changed (); break; @@ -1033,7 +980,44 @@ gtkui_thread (void *ctx) { gtk_disable_setlocale (); gtk_init (&argc, (char ***)&argv); + // register widget types + w_reg_widget ("tabbed_playlist", _("Playlist with tabs"), w_tabbed_playlist_create); + w_reg_widget ("box", NULL, w_box_create); + w_reg_widget ("vsplitter", _("Splitter (top and bottom)"), w_vsplitter_create); + w_reg_widget ("hsplitter", _("Splitter (left and right)"), w_hsplitter_create); + w_reg_widget ("placeholder", NULL, w_placeholder_create); + w_reg_widget ("tabs", _("Tabs"), w_tabs_create); + w_reg_widget ("tabstrip", _("Playlist tabs"), w_tabstrip_create); + w_reg_widget ("playlist", _("Playlist"), w_playlist_create); + w_reg_widget ("selproperties", _("Selection properties"), w_selproperties_create); + w_reg_widget ("coverart", _("Album art display"), w_coverart_create); + mainwin = create_mainwin (); + + // construct mainwindow widgets + { + + w_init (); + ddb_gtkui_widget_t *rootwidget = w_get_rootwidget (); + gtk_widget_show (rootwidget->widget); + gtk_box_pack_start (GTK_BOX(lookup_widget(mainwin, "plugins_bottom_vbox")), rootwidget->widget, TRUE, TRUE, 0); + + // load layout + char layout[1024]; + deadbeef->conf_get_str ("gtkui.layout", "tabbed_playlist { }", layout, sizeof (layout)); + + ddb_gtkui_widget_t *w = NULL; + w_create_from_string (layout, &w); + if (!w) { + ddb_gtkui_widget_t *plt = w_create ("tabbed_playlist"); + w_append (rootwidget, plt); + gtk_widget_show (plt->widget); + } + else { + w_append (rootwidget, w); + } + } + gtkpl_init (); GtkIconTheme *theme = gtk_icon_theme_get_default(); @@ -1074,16 +1058,13 @@ gtkui_thread (void *ctx) { searchwin = create_searchwin (); gtk_window_set_transient_for (GTK_WINDOW (searchwin), GTK_WINDOW (mainwin)); - DdbListview *main_playlist = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - main_playlist_init (GTK_WIDGET (main_playlist)); - +// DdbListview *main_playlist = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); +// main_playlist_init (GTK_WIDGET (main_playlist)); if (deadbeef->conf_get_int ("gtkui.headers.visible", 1)) { gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (header_mi), TRUE); - ddb_listview_show_header (main_playlist, 1); } else { gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (header_mi), FALSE); - ddb_listview_show_header (main_playlist, 0); } DdbListview *search_playlist = DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")); @@ -1106,6 +1087,8 @@ gtkui_thread (void *ctx) { gtk_main (); + w_free (); + if (refresh_timeout) { g_source_remove (refresh_timeout); refresh_timeout = 0; @@ -1311,7 +1294,7 @@ gtkui_stop (void) { deadbeef->thread_join (gtk_tid); trace ("gtk thread finished\n"); gtk_tid = 0; - main_playlist_free (); + //main_playlist_free (); trace ("gtkui_stop completed\n"); return 0; } @@ -1371,4 +1354,5 @@ static ddb_gtkui_t plugin = { .gui.plugin.message = gtkui_message, .gui.run_dialog = gtkui_run_dialog_root, .get_mainwin = gtkui_get_mainwin, + .api_version = GTKUI_API_VERSION, }; diff --git a/plugins/gtkui/gtkui.h b/plugins/gtkui/gtkui.h index e2ed8dbf..f3b075d3 100644 --- a/plugins/gtkui/gtkui.h +++ b/plugins/gtkui/gtkui.h @@ -133,8 +133,8 @@ on_seekbar_motion_notify_event (GtkWidget *widget, void volumebar_redraw (void); -void -tabstrip_redraw (void); +//void +//tabstrip_redraw (void); void gtkui_playlist_changed (void); @@ -172,4 +172,7 @@ gtkui_get_curr_playlist_mod (void); void gtkui_trackinfochanged (DB_playItem_t *it); +gboolean +redraw_queued_tracks_cb (gpointer plt); + #endif diff --git a/plugins/gtkui/gtkui_api.h b/plugins/gtkui/gtkui_api.h index f20c411e..042c4fa5 100644 --- a/plugins/gtkui/gtkui_api.h +++ b/plugins/gtkui/gtkui_api.h @@ -25,9 +25,36 @@ #ifndef __GTKUI_API_H #define __GTKUI_API_H +#define GTKUI_API_VERSION 1 // for compile-time checking + +typedef struct ddb_gtkui_widget_s { + const char *type; + + struct ddb_gtkui_widget_s *parent; + + GtkWidget *widget; + + uint32_t flags; + + void (*destroy) (struct ddb_gtkui_widget_s *w); + + void (*append) (struct ddb_gtkui_widget_s *container, struct ddb_gtkui_widget_s *child); + void (*remove) (struct ddb_gtkui_widget_s *container, struct ddb_gtkui_widget_s *child); + void (*replace) (struct ddb_gtkui_widget_s *container, struct ddb_gtkui_widget_s *child, struct ddb_gtkui_widget_s *newchild); + + int (*message) (struct ddb_gtkui_widget_s *w, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2); + + struct ddb_gtkui_widget_s *children; + struct ddb_gtkui_widget_s *next; // points to next widget in the same container +} ddb_gtkui_widget_t; + typedef struct { DB_gui_t gui; + int api_version; GtkWidget * (*get_mainwin) (void); + void (*reg_widget) (const char *type, ddb_gtkui_widget_t *(*create_func) (void)); + void (*unreg_widget) (const char *type); + ddb_gtkui_widget_t * (*get_root_widget) (void); } ddb_gtkui_t; #endif diff --git a/plugins/gtkui/interface.c b/plugins/gtkui/interface.c index 67d70dba..d09e9dc7 100644 --- a/plugins/gtkui/interface.c +++ b/plugins/gtkui/interface.c @@ -35,12 +35,12 @@ create_mainwin (void) GtkWidget *File; GtkWidget *File_menu; GtkWidget *open; - GtkWidget *image555; + GtkWidget *image563; GtkWidget *separator2; GtkWidget *add_files; - GtkWidget *image556; + GtkWidget *image564; GtkWidget *add_folders; - GtkWidget *image557; + GtkWidget *image565; GtkWidget *add_location1; GtkWidget *separatormenuitem1; GtkWidget *new_playlist1; @@ -48,18 +48,18 @@ create_mainwin (void) GtkWidget *playlist_save_as; GtkWidget *separator8; GtkWidget *quit; - GtkWidget *image558; + GtkWidget *image566; GtkWidget *Edit; GtkWidget *Edit_menu; GtkWidget *clear1; - GtkWidget *image559; + GtkWidget *image567; GtkWidget *select_all1; GtkWidget *deselect_all1; GtkWidget *invert_selection1; GtkWidget *Selection; GtkWidget *Selection_menu; GtkWidget *remove1; - GtkWidget *image560; + GtkWidget *image568; GtkWidget *crop1; GtkWidget *find1; GtkWidget *sort_by1; @@ -78,6 +78,7 @@ create_mainwin (void) GtkWidget *view_headers; GtkWidget *view_tabs; GtkWidget *view_eq; + GtkWidget *design_mode1; GtkWidget *Playback; GtkWidget *Playback_menu; GtkWidget *Order; @@ -101,16 +102,16 @@ create_mainwin (void) GtkWidget *Help; GtkWidget *Help_menu; GtkWidget *help1; - GtkWidget *image561; + GtkWidget *image569; GtkWidget *changelog1; GtkWidget *separator10; GtkWidget *gpl1; GtkWidget *lgpl1; GtkWidget *separator9; GtkWidget *about1; - GtkWidget *image562; + GtkWidget *image570; GtkWidget *translators1; - GtkWidget *image563; + GtkWidget *image571; GtkWidget *hbox2; GtkWidget *hbox3; GtkWidget *stopbtn; @@ -125,9 +126,6 @@ create_mainwin (void) GtkWidget *image5; GtkWidget *seekbar; GtkWidget *volumebar; - GtkWidget *tabstrip; - GtkWidget *frame1; - GtkWidget *playlist; GtkWidget *plugins_bottom_vbox; GtkWidget *statusbar; GtkAccelGroup *accel_group; @@ -161,9 +159,9 @@ create_mainwin (void) GDK_O, (GdkModifierType) GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); - image555 = gtk_image_new_from_stock ("gtk-open", GTK_ICON_SIZE_MENU); - gtk_widget_show (image555); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (open), image555); + image563 = gtk_image_new_from_stock ("gtk-open", GTK_ICON_SIZE_MENU); + gtk_widget_show (image563); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (open), image563); separator2 = gtk_separator_menu_item_new (); gtk_widget_show (separator2); @@ -174,17 +172,17 @@ create_mainwin (void) gtk_widget_show (add_files); gtk_container_add (GTK_CONTAINER (File_menu), add_files); - image556 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); - gtk_widget_show (image556); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_files), image556); + image564 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); + gtk_widget_show (image564); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_files), image564); add_folders = gtk_image_menu_item_new_with_mnemonic (_("Add folder(s)")); gtk_widget_show (add_folders); gtk_container_add (GTK_CONTAINER (File_menu), add_folders); - image557 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); - gtk_widget_show (image557); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_folders), image557); + image565 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); + gtk_widget_show (image565); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_folders), image565); add_location1 = gtk_menu_item_new_with_mnemonic (_("Add location")); gtk_widget_show (add_location1); @@ -222,9 +220,9 @@ create_mainwin (void) GDK_Q, (GdkModifierType) GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); - image558 = gtk_image_new_from_stock ("gtk-quit", GTK_ICON_SIZE_MENU); - gtk_widget_show (image558); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (quit), image558); + image566 = gtk_image_new_from_stock ("gtk-quit", GTK_ICON_SIZE_MENU); + gtk_widget_show (image566); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (quit), image566); Edit = gtk_menu_item_new_with_mnemonic (_("_Edit")); gtk_widget_show (Edit); @@ -237,9 +235,9 @@ create_mainwin (void) gtk_widget_show (clear1); gtk_container_add (GTK_CONTAINER (Edit_menu), clear1); - image559 = gtk_image_new_from_stock ("gtk-clear", GTK_ICON_SIZE_MENU); - gtk_widget_show (image559); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (clear1), image559); + image567 = gtk_image_new_from_stock ("gtk-clear", GTK_ICON_SIZE_MENU); + gtk_widget_show (image567); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (clear1), image567); select_all1 = gtk_menu_item_new_with_mnemonic (_("Select all")); gtk_widget_show (select_all1); @@ -270,9 +268,9 @@ create_mainwin (void) gtk_widget_show (remove1); gtk_container_add (GTK_CONTAINER (Selection_menu), remove1); - image560 = gtk_image_new_from_stock ("gtk-remove", GTK_ICON_SIZE_MENU); - gtk_widget_show (image560); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (remove1), image560); + image568 = gtk_image_new_from_stock ("gtk-remove", GTK_ICON_SIZE_MENU); + gtk_widget_show (image568); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (remove1), image568); crop1 = gtk_menu_item_new_with_mnemonic (_("Crop")); gtk_widget_show (crop1); @@ -348,6 +346,10 @@ create_mainwin (void) gtk_widget_show (view_eq); gtk_container_add (GTK_CONTAINER (View_menu), view_eq); + design_mode1 = gtk_check_menu_item_new_with_mnemonic (_("Design mode")); + gtk_widget_show (design_mode1); + gtk_container_add (GTK_CONTAINER (View_menu), design_mode1); + Playback = gtk_menu_item_new_with_mnemonic (_("_Playback")); gtk_widget_show (Playback); gtk_container_add (GTK_CONTAINER (menubar1), Playback); @@ -366,16 +368,19 @@ create_mainwin (void) order_linear_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (order_linear)); gtk_widget_show (order_linear); gtk_container_add (GTK_CONTAINER (Order_menu), order_linear); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (order_linear), TRUE); order_shuffle = gtk_radio_menu_item_new_with_mnemonic (order_linear_group, _("Shuffle tracks")); order_linear_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (order_shuffle)); gtk_widget_show (order_shuffle); gtk_container_add (GTK_CONTAINER (Order_menu), order_shuffle); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (order_shuffle), TRUE); order_shuffle_albums = gtk_radio_menu_item_new_with_mnemonic (order_linear_group, _("Shuffle albums")); order_linear_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (order_shuffle_albums)); gtk_widget_show (order_shuffle_albums); gtk_container_add (GTK_CONTAINER (Order_menu), order_shuffle_albums); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (order_shuffle_albums), TRUE); order_random = gtk_radio_menu_item_new_with_mnemonic (order_linear_group, _("Random")); order_linear_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (order_random)); @@ -394,11 +399,13 @@ create_mainwin (void) loop_all_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (loop_all)); gtk_widget_show (loop_all); gtk_container_add (GTK_CONTAINER (Looping_menu), loop_all); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (loop_all), TRUE); loop_single = gtk_radio_menu_item_new_with_mnemonic (loop_all_group, _("Loop Single Song")); loop_all_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (loop_single)); gtk_widget_show (loop_single); gtk_container_add (GTK_CONTAINER (Looping_menu), loop_single); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (loop_single), TRUE); loop_disable = gtk_radio_menu_item_new_with_mnemonic (loop_all_group, _("Don't Loop")); loop_all_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (loop_disable)); @@ -445,9 +452,9 @@ create_mainwin (void) gtk_widget_show (help1); gtk_container_add (GTK_CONTAINER (Help_menu), help1); - image561 = gtk_image_new_from_stock ("gtk-help", GTK_ICON_SIZE_MENU); - gtk_widget_show (image561); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (help1), image561); + image569 = gtk_image_new_from_stock ("gtk-help", GTK_ICON_SIZE_MENU); + gtk_widget_show (image569); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (help1), image569); changelog1 = gtk_menu_item_new_with_mnemonic (_("_ChangeLog")); gtk_widget_show (changelog1); @@ -475,17 +482,17 @@ create_mainwin (void) gtk_widget_show (about1); gtk_container_add (GTK_CONTAINER (Help_menu), about1); - image562 = gtk_image_new_from_stock ("gtk-about", GTK_ICON_SIZE_MENU); - gtk_widget_show (image562); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (about1), image562); + image570 = gtk_image_new_from_stock ("gtk-about", GTK_ICON_SIZE_MENU); + gtk_widget_show (image570); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (about1), image570); translators1 = gtk_image_menu_item_new_with_mnemonic (_("_Translators")); gtk_widget_show (translators1); gtk_container_add (GTK_CONTAINER (Help_menu), translators1); - image563 = gtk_image_new_from_stock ("gtk-about", GTK_ICON_SIZE_MENU); - gtk_widget_show (image563); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (translators1), image563); + image571 = gtk_image_new_from_stock ("gtk-about", GTK_ICON_SIZE_MENU); + gtk_widget_show (image571); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (translators1), image571); hbox2 = gtk_hbox_new (FALSE, 0); gtk_widget_show (hbox2); @@ -580,27 +587,9 @@ create_mainwin (void) GTK_WIDGET_UNSET_FLAGS (volumebar, GTK_CAN_FOCUS); GTK_WIDGET_UNSET_FLAGS (volumebar, GTK_CAN_DEFAULT); - tabstrip = create_tabstrip_widget ("tabstrip", "", "", 0, 0); - gtk_widget_show (tabstrip); - gtk_box_pack_start (GTK_BOX (vbox1), tabstrip, FALSE, TRUE, 0); - gtk_widget_set_size_request (tabstrip, -1, 24); - GTK_WIDGET_UNSET_FLAGS (tabstrip, GTK_CAN_FOCUS); - GTK_WIDGET_UNSET_FLAGS (tabstrip, GTK_CAN_DEFAULT); - - frame1 = gtk_frame_new (NULL); - gtk_widget_show (frame1); - gtk_box_pack_start (GTK_BOX (vbox1), frame1, TRUE, TRUE, 0); - gtk_container_set_border_width (GTK_CONTAINER (frame1), 1); - - playlist = create_ddb_listview_widget ("playlist", "", "", 0, 0); - gtk_widget_show (playlist); - gtk_container_add (GTK_CONTAINER (frame1), playlist); - GTK_WIDGET_UNSET_FLAGS (playlist, GTK_CAN_FOCUS); - GTK_WIDGET_UNSET_FLAGS (playlist, GTK_CAN_DEFAULT); - plugins_bottom_vbox = gtk_vbox_new (FALSE, 0); gtk_widget_show (plugins_bottom_vbox); - gtk_box_pack_start (GTK_BOX (vbox1), plugins_bottom_vbox, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox1), plugins_bottom_vbox, TRUE, TRUE, 0); statusbar = gtk_statusbar_new (); gtk_widget_show (statusbar); @@ -711,6 +700,9 @@ create_mainwin (void) g_signal_connect ((gpointer) view_eq, "activate", G_CALLBACK (on_toggle_eq), NULL); + g_signal_connect ((gpointer) design_mode1, "activate", + G_CALLBACK (on_design_mode1_activate), + NULL); g_signal_connect ((gpointer) order_linear, "activate", G_CALLBACK (on_order_linear_activate), NULL); @@ -785,12 +777,12 @@ create_mainwin (void) GLADE_HOOKUP_OBJECT (mainwin, File, "File"); GLADE_HOOKUP_OBJECT (mainwin, File_menu, "File_menu"); GLADE_HOOKUP_OBJECT (mainwin, open, "open"); - GLADE_HOOKUP_OBJECT (mainwin, image555, "image555"); + GLADE_HOOKUP_OBJECT (mainwin, image563, "image563"); GLADE_HOOKUP_OBJECT (mainwin, separator2, "separator2"); GLADE_HOOKUP_OBJECT (mainwin, add_files, "add_files"); - GLADE_HOOKUP_OBJECT (mainwin, image556, "image556"); + GLADE_HOOKUP_OBJECT (mainwin, image564, "image564"); GLADE_HOOKUP_OBJECT (mainwin, add_folders, "add_folders"); - GLADE_HOOKUP_OBJECT (mainwin, image557, "image557"); + GLADE_HOOKUP_OBJECT (mainwin, image565, "image565"); GLADE_HOOKUP_OBJECT (mainwin, add_location1, "add_location1"); GLADE_HOOKUP_OBJECT (mainwin, separatormenuitem1, "separatormenuitem1"); GLADE_HOOKUP_OBJECT (mainwin, new_playlist1, "new_playlist1"); @@ -798,18 +790,18 @@ create_mainwin (void) GLADE_HOOKUP_OBJECT (mainwin, playlist_save_as, "playlist_save_as"); GLADE_HOOKUP_OBJECT (mainwin, separator8, "separator8"); GLADE_HOOKUP_OBJECT (mainwin, quit, "quit"); - GLADE_HOOKUP_OBJECT (mainwin, image558, "image558"); + GLADE_HOOKUP_OBJECT (mainwin, image566, "image566"); GLADE_HOOKUP_OBJECT (mainwin, Edit, "Edit"); GLADE_HOOKUP_OBJECT (mainwin, Edit_menu, "Edit_menu"); GLADE_HOOKUP_OBJECT (mainwin, clear1, "clear1"); - GLADE_HOOKUP_OBJECT (mainwin, image559, "image559"); + GLADE_HOOKUP_OBJECT (mainwin, image567, "image567"); GLADE_HOOKUP_OBJECT (mainwin, select_all1, "select_all1"); GLADE_HOOKUP_OBJECT (mainwin, deselect_all1, "deselect_all1"); GLADE_HOOKUP_OBJECT (mainwin, invert_selection1, "invert_selection1"); GLADE_HOOKUP_OBJECT (mainwin, Selection, "Selection"); GLADE_HOOKUP_OBJECT (mainwin, Selection_menu, "Selection_menu"); GLADE_HOOKUP_OBJECT (mainwin, remove1, "remove1"); - GLADE_HOOKUP_OBJECT (mainwin, image560, "image560"); + GLADE_HOOKUP_OBJECT (mainwin, image568, "image568"); GLADE_HOOKUP_OBJECT (mainwin, crop1, "crop1"); GLADE_HOOKUP_OBJECT (mainwin, find1, "find1"); GLADE_HOOKUP_OBJECT (mainwin, sort_by1, "sort_by1"); @@ -828,6 +820,7 @@ create_mainwin (void) GLADE_HOOKUP_OBJECT (mainwin, view_headers, "view_headers"); GLADE_HOOKUP_OBJECT (mainwin, view_tabs, "view_tabs"); GLADE_HOOKUP_OBJECT (mainwin, view_eq, "view_eq"); + GLADE_HOOKUP_OBJECT (mainwin, design_mode1, "design_mode1"); GLADE_HOOKUP_OBJECT (mainwin, Playback, "Playback"); GLADE_HOOKUP_OBJECT (mainwin, Playback_menu, "Playback_menu"); GLADE_HOOKUP_OBJECT (mainwin, Order, "Order"); @@ -849,16 +842,16 @@ create_mainwin (void) GLADE_HOOKUP_OBJECT (mainwin, Help, "Help"); GLADE_HOOKUP_OBJECT (mainwin, Help_menu, "Help_menu"); GLADE_HOOKUP_OBJECT (mainwin, help1, "help1"); - GLADE_HOOKUP_OBJECT (mainwin, image561, "image561"); + GLADE_HOOKUP_OBJECT (mainwin, image569, "image569"); GLADE_HOOKUP_OBJECT (mainwin, changelog1, "changelog1"); GLADE_HOOKUP_OBJECT (mainwin, separator10, "separator10"); GLADE_HOOKUP_OBJECT (mainwin, gpl1, "gpl1"); GLADE_HOOKUP_OBJECT (mainwin, lgpl1, "lgpl1"); GLADE_HOOKUP_OBJECT (mainwin, separator9, "separator9"); GLADE_HOOKUP_OBJECT (mainwin, about1, "about1"); - GLADE_HOOKUP_OBJECT (mainwin, image562, "image562"); + GLADE_HOOKUP_OBJECT (mainwin, image570, "image570"); GLADE_HOOKUP_OBJECT (mainwin, translators1, "translators1"); - GLADE_HOOKUP_OBJECT (mainwin, image563, "image563"); + GLADE_HOOKUP_OBJECT (mainwin, image571, "image571"); GLADE_HOOKUP_OBJECT (mainwin, hbox2, "hbox2"); GLADE_HOOKUP_OBJECT (mainwin, hbox3, "hbox3"); GLADE_HOOKUP_OBJECT (mainwin, stopbtn, "stopbtn"); @@ -873,9 +866,6 @@ create_mainwin (void) GLADE_HOOKUP_OBJECT (mainwin, image5, "image5"); GLADE_HOOKUP_OBJECT (mainwin, seekbar, "seekbar"); GLADE_HOOKUP_OBJECT (mainwin, volumebar, "volumebar"); - GLADE_HOOKUP_OBJECT (mainwin, tabstrip, "tabstrip"); - GLADE_HOOKUP_OBJECT (mainwin, frame1, "frame1"); - GLADE_HOOKUP_OBJECT (mainwin, playlist, "playlist"); GLADE_HOOKUP_OBJECT (mainwin, plugins_bottom_vbox, "plugins_bottom_vbox"); GLADE_HOOKUP_OBJECT (mainwin, statusbar, "statusbar"); diff --git a/plugins/gtkui/mainplaylist.c b/plugins/gtkui/mainplaylist.c index f04f9cb1..17083b7c 100644 --- a/plugins/gtkui/mainplaylist.c +++ b/plugins/gtkui/mainplaylist.c @@ -127,10 +127,11 @@ void main_external_drag_n_drop (DdbListviewIter before, char *mem, int length) { gboolean playlist_tooltip_handler (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer unused) { - GtkWidget *pl = lookup_widget (mainwin, "playlist"); - DB_playItem_t *it = (DB_playItem_t *)ddb_listview_get_iter_from_coord (DDB_LISTVIEW (pl), 0, y); + DdbListview *pl = DDB_LISTVIEW (g_object_get_data (G_OBJECT (widget), "owner")); + DB_playItem_t *it = (DB_playItem_t *)ddb_listview_get_iter_from_coord (pl, 0, y); if (it) { gtk_tooltip_set_text (tooltip, deadbeef->pl_find_meta (it, ":URI")); + deadbeef->pl_item_unref (it); return TRUE; } return FALSE; @@ -157,6 +158,7 @@ void main_selection_changed (DdbListviewIter it, int idx) { else { ddb_listview_draw_row (search, search_get_idx ((DB_playItem_t *)it), it); } + deadbeef->sendmessage (DB_EV_SELCHANGED, 0, deadbeef->plt_get_curr_idx (), PL_MAIN); } void main_draw_group_title (DdbListview *listview, GdkDrawable *drawable, DdbListviewIter it, int x, int y, int width, int height) { @@ -251,6 +253,14 @@ main_vscroll_changed (int pos) { deadbeef->conf_set_int (conf, pos); } +void +main_header_context_menu (DdbListview *ps, int column) { + GtkWidget *menu = create_headermenu (1); + set_last_playlist_cm (ps); // playlist ptr for context menu + set_active_column_cm (column); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, ps, 3, gtk_get_current_event_time()); +} + DdbListviewBinding main_binding = { // rows .count = main_get_count, @@ -287,7 +297,7 @@ DdbListviewBinding main_binding = { // callbacks .handle_doubleclick = main_handle_doubleclick, .selection_changed = main_selection_changed, - .header_context_menu = header_context_menu, + .header_context_menu = main_header_context_menu, .list_context_menu = list_context_menu, .delete_selected = main_delete_selected, .vscroll_changed = main_vscroll_changed, @@ -329,8 +339,9 @@ main_playlist_init (GtkWidget *widget) { GValue value = {0, }; g_value_init (&value, G_TYPE_BOOLEAN); g_value_set_boolean (&value, TRUE); - g_object_set_property (G_OBJECT (widget), "has-tooltip", &value); - g_signal_connect (G_OBJECT (widget), "query-tooltip", G_CALLBACK (playlist_tooltip_handler), NULL); + DdbListview *pl = DDB_LISTVIEW (widget); + g_object_set_property (G_OBJECT (pl->list), "has-tooltip", &value); + g_signal_connect (G_OBJECT (pl->list), "query-tooltip", G_CALLBACK (playlist_tooltip_handler), NULL); } deadbeef->conf_lock (); strncpy (group_by_str, deadbeef->conf_get_str_fast ("playlist.group_by", ""), sizeof (group_by_str)); @@ -347,9 +358,6 @@ main_playlist_free (void) { void main_refresh (void) { - if (mainwin && gtk_widget_get_visible (mainwin)) { - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (pl, DDB_REFRESH_VSCROLL | DDB_REFRESH_LIST); - } + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } diff --git a/plugins/gtkui/mainplaylist.h b/plugins/gtkui/mainplaylist.h index b17ef63d..56a076c1 100644 --- a/plugins/gtkui/mainplaylist.h +++ b/plugins/gtkui/mainplaylist.h @@ -19,6 +19,8 @@ #ifndef __MAINPLAYLIST_H #define __MAINPLAYLIST_H +#include "ddblistview.h" + void main_playlist_init (GtkWidget *widget); @@ -31,4 +33,7 @@ main_refresh (void); int main_get_idx (DdbListviewIter it); +void +main_drag_n_drop (DdbListviewIter before, DdbPlaylistHandle from_playlist, uint32_t *indices, int length, int copy); + #endif diff --git a/plugins/gtkui/plcommon.c b/plugins/gtkui/plcommon.c index 41e3fdd8..5cc749d8 100644 --- a/plugins/gtkui/plcommon.c +++ b/plugins/gtkui/plcommon.c @@ -281,8 +281,7 @@ on_clear1_activate (GtkMenuItem *menuitem, gpointer user_data) { deadbeef->pl_clear (); - main_refresh (); - search_refresh (); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } void @@ -290,8 +289,7 @@ on_remove1_activate (GtkMenuItem *menuitem, gpointer user_data) { int cursor = deadbeef->pl_delete_selected (); - main_refresh (); - search_refresh (); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } @@ -299,20 +297,16 @@ void on_crop1_activate (GtkMenuItem *menuitem, gpointer user_data) { - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); deadbeef->pl_crop_selected (); - main_refresh (); - search_refresh (); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } void on_remove2_activate (GtkMenuItem *menuitem, gpointer user_data) { - GtkWidget *widget = GTK_WIDGET (menuitem); int cursor = deadbeef->pl_delete_selected (); - main_refresh (); - search_refresh (); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } void @@ -348,8 +342,7 @@ on_remove_from_disk_activate (GtkMenuItem *menuitem, int cursor = deadbeef->pl_delete_selected (); deadbeef->pl_unlock (); - main_refresh (); - search_refresh (); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } void @@ -702,6 +695,16 @@ static DdbListview *last_playlist; static int active_column; void +set_last_playlist_cm (DdbListview *pl) { + last_playlist = pl; +} + +void +set_active_column_cm (int col) { + active_column = col; +} + +void append_column_from_textdef (DdbListview *listview, const uint8_t *def) { // syntax: "title" "format" id width alignright char token[MAX_TOKEN]; @@ -1034,14 +1037,6 @@ create_headermenu (int groupby) } void -header_context_menu (DdbListview *ps, int column) { - GtkWidget *menu = create_headermenu (GTK_WIDGET (ps) == lookup_widget (mainwin, "playlist") ? 1 : 0); - last_playlist = ps; - active_column = column; - gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, ps, 3, gtk_get_current_event_time()); -} - -void add_column_helper (DdbListview *listview, const char *title, int width, int id, const char *format, int align_right) { if (!format) { format = ""; diff --git a/plugins/gtkui/plcommon.h b/plugins/gtkui/plcommon.h index 6a3114c1..edd7d3f2 100644 --- a/plugins/gtkui/plcommon.h +++ b/plugins/gtkui/plcommon.h @@ -41,12 +41,18 @@ void list_context_menu (DdbListview *listview, DdbListviewIter it, int idx); void -header_context_menu (DdbListview *ps, int column); - -void append_column_from_textdef (DdbListview *listview, const uint8_t *def); void add_column_helper (DdbListview *listview, const char *title, int width, int id, const char *format, int align_right); +GtkWidget* +create_headermenu (int groupby); + +void +set_last_playlist_cm (DdbListview *pl); + +void +set_active_column_cm (int col); + #endif // __PLCOLUMNS_H diff --git a/plugins/gtkui/prefwin.c b/plugins/gtkui/prefwin.c index 4f6207f4..d2d09300 100644 --- a/plugins/gtkui/prefwin.c +++ b/plugins/gtkui/prefwin.c @@ -860,8 +860,9 @@ on_tabstrip_light_color_set (GtkColorButton *colorbutton, deadbeef->conf_set_str ("gtkui.color.tabstrip_light", str); deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); gtkui_init_theme_colors (); - redraw_headers (); - tabstrip_redraw (); +// redraw_headers (); +// tabstrip_redraw (); + gtk_widget_queue_draw (mainwin); } @@ -876,8 +877,9 @@ on_tabstrip_mid_color_set (GtkColorButton *colorbutton, deadbeef->conf_set_str ("gtkui.color.tabstrip_mid", str); deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); gtkui_init_theme_colors (); - redraw_headers (); - tabstrip_redraw (); +// redraw_headers (); +// tabstrip_redraw (); + gtk_widget_queue_draw (mainwin); } @@ -892,8 +894,9 @@ on_tabstrip_dark_color_set (GtkColorButton *colorbutton, deadbeef->conf_set_str ("gtkui.color.tabstrip_dark", str); deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); gtkui_init_theme_colors (); - redraw_headers (); - tabstrip_redraw (); +// redraw_headers (); +// tabstrip_redraw (); + gtk_widget_queue_draw (mainwin); } void @@ -907,8 +910,9 @@ on_tabstrip_base_color_set (GtkColorButton *colorbutton, deadbeef->conf_set_str ("gtkui.color.tabstrip_base", str); deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); gtkui_init_theme_colors (); - redraw_headers (); - tabstrip_redraw (); +// redraw_headers (); +// tabstrip_redraw (); + gtk_widget_queue_draw (mainwin); } void @@ -922,8 +926,9 @@ on_tabstrip_text_color_set (GtkColorButton *colorbutton, deadbeef->conf_set_str ("gtkui.color.tabstrip_text", str); deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); gtkui_init_theme_colors (); - redraw_headers (); - tabstrip_redraw (); +// redraw_headers (); +// tabstrip_redraw (); + gtk_widget_queue_draw (mainwin); } void @@ -1084,8 +1089,9 @@ on_override_tabstrip_colors_toggled (GtkToggleButton *togglebutton, deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); gtkui_init_theme_colors (); prefwin_init_theme_colors (); - redraw_headers (); - tabstrip_redraw (); +// redraw_headers (); +// tabstrip_redraw (); + gtk_widget_queue_draw (mainwin); } void diff --git a/plugins/gtkui/search.c b/plugins/gtkui/search.c index e3d46819..d3665bf3 100644 --- a/plugins/gtkui/search.c +++ b/plugins/gtkui/search.c @@ -319,13 +319,7 @@ void search_handle_doubleclick (DdbListview *listview, DdbListviewIter iter, int } void search_selection_changed (DdbListviewIter it, int idx) { - DdbListview *main = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - if (idx == -1) { - ddb_listview_refresh (main, DDB_REFRESH_LIST); - } - else { - ddb_listview_draw_row (main, main_get_idx ((DB_playItem_t *)it), it); - } + deadbeef->sendmessage (DB_EV_SELCHANGED, 0, 0, 0); } void @@ -335,6 +329,14 @@ search_delete_selected (void) { search_refresh (); } +void +search_header_context_menu (DdbListview *ps, int column) { + GtkWidget *menu = create_headermenu (0); + set_last_playlist_cm (ps); // playlist ptr for context menu + set_active_column_cm (column); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, ps, 3, gtk_get_current_event_time()); +} + DdbListviewBinding search_binding = { // rows .count = search_get_count, @@ -371,7 +373,7 @@ DdbListviewBinding search_binding = { // callbacks .handle_doubleclick = search_handle_doubleclick, .selection_changed = search_selection_changed, - .header_context_menu = header_context_menu, + .header_context_menu = search_header_context_menu, .list_context_menu = list_context_menu, .delete_selected = search_delete_selected, .modification_idx = gtkui_get_curr_playlist_mod, diff --git a/plugins/gtkui/trkproperties.c b/plugins/gtkui/trkproperties.c index 8dc5302d..5ff479ce 100644 --- a/plugins/gtkui/trkproperties.c +++ b/plugins/gtkui/trkproperties.c @@ -52,8 +52,8 @@ static int numtracks; static GtkWidget *progressdlg; static int progress_aborted; -static int -build_key_list (const char ***pkeys, int props) { +int +build_key_list (const char ***pkeys, int props, DB_playItem_t **tracks, int numtracks) { int sz = 20; const char **keys = malloc (sizeof (const char *) * sz); if (!keys) { @@ -100,7 +100,7 @@ equals_ptr (const char *a, const char *b) { } static int -get_field_value (char *out, int size, const char *key, const char *(*getter)(DB_playItem_t *it, const char *key), int (*equals)(const char *a, const char *b)) { +get_field_value (char *out, int size, const char *key, const char *(*getter)(DB_playItem_t *it, const char *key), int (*equals)(const char *a, const char *b), DB_playItem_t **tracks, int numtracks) { int multiple = 0; *out = 0; if (numtracks == 0) { @@ -249,13 +249,13 @@ static const char *hc_props[] = { }; void -add_field (GtkListStore *store, const char *key, const char *title, int is_prop) { +add_field (GtkListStore *store, const char *key, const char *title, int is_prop, DB_playItem_t **tracks, int numtracks) { // get value to edit const char *mult = is_prop ? "" : _("[Multiple values] "); char val[1000]; size_t ml = strlen (mult); memcpy (val, mult, ml+1); - int n = get_field_value (val + ml, sizeof (val) - ml, key, deadbeef->pl_find_meta, equals_ptr); + int n = get_field_value (val + ml, sizeof (val) - ml, key, deadbeef->pl_find_meta, equals_ptr, tracks, numtracks); GtkTreeIter iter; gtk_list_store_append (store, &iter); @@ -279,23 +279,20 @@ add_field (GtkListStore *store, const char *key, const char *title, int is_prop) } void -trkproperties_fill_metadata (void) { - if (!trackproperties) { +trkproperties_fill_meta (GtkListStore *store, DB_playItem_t **tracks, int numtracks) { + gtk_list_store_clear (store); + if (!tracks) { return; } - trkproperties_modified = 0; - gtk_list_store_clear (store); - gtk_list_store_clear (propstore); - deadbeef->pl_lock (); const char **keys = NULL; - int nkeys = build_key_list (&keys, 0); + int nkeys = build_key_list (&keys, 0, tracks, numtracks); int k; // add "standard" fields for (int i = 0; types[i]; i += 2) { - add_field (store, types[i], _(types[i+1]), 0); + add_field (store, types[i], _(types[i+1]), 0, tracks, numtracks); } // add all other fields @@ -314,19 +311,31 @@ trkproperties_fill_metadata (void) { if (!types[i]) { snprintf (title, sizeof (title), "<%s>", keys[k]); } - add_field (store, keys[k], title, 0); + add_field (store, keys[k], title, 0, tracks, numtracks); } if (keys) { free (keys); } +} + +void +trkproperties_fill_metadata (void) { + if (!trackproperties) { + return; + } + trkproperties_modified = 0; + deadbeef->pl_lock (); + + trkproperties_fill_meta (store, tracks, numtracks); + gtk_list_store_clear (propstore); // hardcoded properties for (int i = 0; hc_props[i]; i += 2) { - add_field (propstore, hc_props[i], _(hc_props[i+1]), 1); + add_field (propstore, hc_props[i], _(hc_props[i+1]), 1, tracks, numtracks); } // properties - keys = NULL; - nkeys = build_key_list (&keys, 1); + const char **keys = NULL; + int nkeys = build_key_list (&keys, 1, tracks, numtracks); for (int k = 0; k < nkeys; k++) { int i; for (i = 0; hc_props[i]; i += 2) { @@ -339,7 +348,7 @@ trkproperties_fill_metadata (void) { } char title[1000]; snprintf (title, sizeof (title), "<%s>", keys[k]+1); - add_field (propstore, keys[k], title, 1); + add_field (propstore, keys[k], title, 1, tracks, numtracks); } if (keys) { free (keys); diff --git a/plugins/gtkui/trkproperties.h b/plugins/gtkui/trkproperties.h index ccdaa69f..3604cccc 100644 --- a/plugins/gtkui/trkproperties.h +++ b/plugins/gtkui/trkproperties.h @@ -19,6 +19,8 @@ #ifndef __TRKPROPERTIES_H #define __TRKPROPERTIES_H +#include "../../deadbeef.h" + struct DB_playItem_s; void @@ -30,4 +32,10 @@ trkproperties_destroy (void); void trkproperties_fill_metadata (void); +int +build_key_list (const char ***pkeys, int props, DB_playItem_t **tracks, int numtracks); + +void +trkproperties_fill_meta (GtkListStore *store, DB_playItem_t **tracks, int numtracks); + #endif diff --git a/plugins/gtkui/widgets.c b/plugins/gtkui/widgets.c new file mode 100644 index 00000000..b3e1c32f --- /dev/null +++ b/plugins/gtkui/widgets.c @@ -0,0 +1,1151 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include "gtkui.h" +#include "widgets.h" +#include "ddbtabstrip.h" +#include "ddblistview.h" +#include "mainplaylist.h" +#include "../../gettext.h" +#include "parser.h" +#include "trkproperties.h" +#include "coverart.h" + +#define min(x,y) ((x)<(y)?(x):(y)) +#define max(x,y) ((x)>(y)?(x):(y)) + +typedef struct w_creator_s { + const char *type; + const char *title; // set to NULL to avoid exposing this widget type to user + ddb_gtkui_widget_t *(*create_func) (void); + struct w_creator_s *next; +} w_creator_t; + +static w_creator_t *w_creators; + +typedef struct { + ddb_gtkui_widget_t base; +} w_splitter_t; + +typedef struct { + ddb_gtkui_widget_t base; +} w_box_t; + +typedef struct { + ddb_gtkui_widget_t base; +} w_tabstrip_t; + +typedef struct { + ddb_gtkui_widget_t base; + DdbTabStrip *tabstrip; + DdbListview *list; +} w_tabbed_playlist_t; + +typedef struct { + ddb_gtkui_widget_t base; +} w_playlist_t; + +typedef struct { + ddb_gtkui_widget_t base; +} w_placeholder_t; + +typedef struct { + ddb_gtkui_widget_t base; +} w_tabs_t; + +typedef struct { + ddb_gtkui_widget_t base; + GtkWidget *tree; +} w_selproperties_t; + +typedef struct { + ddb_gtkui_widget_t base; + GtkWidget *drawarea; +} w_coverart_t; + +static int design_mode; +static ddb_gtkui_widget_t *rootwidget; + +//// common functions + +void +w_init (void) { + rootwidget = w_create ("box"); +} + +void +w_free (void) { + w_creator_t *next = NULL; + for (w_creator_t *cr = w_creators; cr; cr = next) { + next = cr->next; + free (cr); + } + w_creators = NULL; +} + +ddb_gtkui_widget_t * +w_get_rootwidget (void) { + return rootwidget; +} + +static void +set_design_mode (ddb_gtkui_widget_t *w) { + for (ddb_gtkui_widget_t *c = w->children; c; c = c->next) { + set_design_mode (c); + } +} + +void +w_set_design_mode (int active) { + design_mode = active; + set_design_mode (rootwidget); +} + +void +w_append (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child) { + child->parent = cont; + if (!cont->children) { + cont->children = child; + } + else { + for (ddb_gtkui_widget_t *c = cont->children; c; c = c->next) { + if (!c->next) { + c->next = child; + break; + } + } + } + + if (cont->append) { + cont->append (cont, child); + } +} + +void +w_remove (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child) { + if (cont->remove) { + cont->remove (cont, child); + } + child->widget = NULL; + ddb_gtkui_widget_t *prev = NULL; + for (ddb_gtkui_widget_t *c = cont->children; c; c = c->next) { + if (c == child) { + if (prev) { + prev->next = c->next; + } + else { + cont->children = c->next; + } + break; + } + prev = c; + } + child->parent = NULL; +} + +void +w_replace (ddb_gtkui_widget_t *w, ddb_gtkui_widget_t *from, ddb_gtkui_widget_t *to) { + if (w->replace) { + w->replace (w, from, to); + } + else { + w_remove (w, from); + w_destroy (from); + w_append (w, to); + } +} + +const char * +w_create_from_string (const char *s, ddb_gtkui_widget_t **parent) { + char t[MAX_TOKEN]; + s = gettoken (s, t); + if (!s) { + return NULL; + } + ddb_gtkui_widget_t *w = w_create (t); + // nuke all default children + while (w->children) { + w_remove (w, w->children); + } + + s = gettoken (s, t); + if (!s) { + w_destroy (w); + return NULL; + } + if (strcmp (t, "{")) { + w_destroy (w); + return NULL; + } + + const char *back = s; + s = gettoken (s, t); + if (!s) { + w_destroy (w); + return NULL; + } + for (;;) { + if (!strcmp (t, "}")) { + break; + } + + s = w_create_from_string (back, &w); + if (!s) { + w_destroy (w); + return NULL; + } + + back = s; + s = gettoken (s, t); + if (!s) { + w_destroy (w); + return NULL; + } + } + + if (*parent) { + w_append (*parent, w); + } + else { + *parent = w; + } + return s; +} + +static ddb_gtkui_widget_t *current_widget; +static int hidden = 0; + +gboolean +w_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { + if (hidden && user_data == current_widget) { + GdkColor clr = { + .red = 0x2d00, + .green = 0x0000, + .blue = 0xd600 + }; + GdkGC *gc = gdk_gc_new (widget->window); + gdk_gc_set_rgb_fg_color (gc, &clr); + if (GTK_WIDGET_NO_WINDOW (widget)) { + gdk_draw_rectangle (widget->window, gc, TRUE, widget->allocation.x, widget->allocation.y, widget->allocation.width, widget->allocation.height); + } + else { + gdk_draw_rectangle (widget->window, gc, TRUE, 0, 0, widget->allocation.width, widget->allocation.height); + } + g_object_unref (gc); + return TRUE; + } + + return FALSE; +} + +static char paste_buffer[1000]; + +static void +save_widget_to_string (char *str, ddb_gtkui_widget_t *w) { + strcat (str, w->type); + strcat (str, "{"); + for (ddb_gtkui_widget_t *c = w->children; c; c = c->next) { + save_widget_to_string (str, c); + } + strcat (str, "} "); +} + +void +w_save (void) { + char buf[1000] = ""; + save_widget_to_string (buf, rootwidget->children); + deadbeef->conf_set_str ("gtkui.layout", buf); + deadbeef->conf_save (); +} + +static void +on_replace_activate (GtkMenuItem *menuitem, gpointer user_data) { + for (w_creator_t *cr = w_creators; cr; cr = cr->next) { + if (cr->type == user_data) { + w_replace (current_widget->parent, current_widget, w_create (user_data)); + } + } + w_save (); +} + +static void +on_delete_activate (GtkMenuItem *menuitem, gpointer user_data) { + ddb_gtkui_widget_t *parent = current_widget->parent; + if (!strcmp (current_widget->type, "placeholder")) { + return; + } + if (parent->replace) { + parent->replace (parent, current_widget, w_create ("placeholder")); + } + else { + w_remove (parent, current_widget); + w_destroy (current_widget); + current_widget = w_create ("placeholder"); + w_append (parent, current_widget); + } + w_save (); +} + +static void +on_cut_activate (GtkMenuItem *menuitem, gpointer user_data) { + ddb_gtkui_widget_t *parent = current_widget->parent; + if (!strcmp (current_widget->type, "placeholder")) { + return; + } + // save hierarchy to string + // FIXME: use real clipboard + paste_buffer[0] = 0; + save_widget_to_string (paste_buffer, current_widget); + + if (parent->replace) { + parent->replace (parent, current_widget, w_create ("placeholder")); + } + else { + w_remove (parent, current_widget); + w_destroy (current_widget); + current_widget = w_create ("placeholder"); + w_append (parent, current_widget); + } + w_save (); +} + +static void +on_copy_activate (GtkMenuItem *menuitem, gpointer user_data) { + ddb_gtkui_widget_t *parent = current_widget->parent; + if (!strcmp (current_widget->type, "placeholder")) { + return; + } + // save hierarchy to string + // FIXME: use real clipboard + paste_buffer[0] = 0; + save_widget_to_string (paste_buffer, current_widget); +} + +static void +on_paste_activate (GtkMenuItem *menuitem, gpointer user_data) { + ddb_gtkui_widget_t *parent = current_widget->parent; + if (!paste_buffer[0]) { + return; + } + ddb_gtkui_widget_t *w = NULL; + w_create_from_string (paste_buffer, &w); + if (parent->replace) { + parent->replace (parent, current_widget, w); + } + else { + w_remove (parent, current_widget); + w_destroy (current_widget); + current_widget = w; + w_append (parent, current_widget); + } + w_save (); +} + +void +hide_widget (GtkWidget *widget, gpointer data) { + if (GTK_IS_CONTAINER (widget)) { + gtk_container_foreach (GTK_CONTAINER (widget), hide_widget, NULL); + } + gtk_widget_hide (widget); +} + +void +show_widget (GtkWidget *widget, gpointer data) { + if (GTK_IS_CONTAINER (widget)) { + gtk_container_foreach (GTK_CONTAINER (widget), show_widget, NULL); + } + gtk_widget_show (widget); +} + +void +w_menu_deactivate (GtkMenuShell *menushell, gpointer user_data) { + hidden = 0; + ddb_gtkui_widget_t *w = user_data; + if (GTK_IS_CONTAINER (w->widget)) { + gtk_container_foreach (GTK_CONTAINER (w->widget), show_widget, NULL); + } + gtk_widget_queue_draw (w->widget); +} + +gboolean +w_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data) { + if (!design_mode || event->button != 3) { + return FALSE; + } + + current_widget = user_data; + hidden = 1; + if (GTK_IS_CONTAINER (widget)) { + gtk_container_foreach (GTK_CONTAINER (widget), hide_widget, NULL); + } + gtk_widget_queue_draw (((ddb_gtkui_widget_t *)user_data)->widget); + GtkWidget *menu; + GtkWidget *submenu; + GtkWidget *item; + menu = gtk_menu_new (); + if (strcmp (current_widget->type, "placeholder")) { + item = gtk_menu_item_new_with_mnemonic (_("Replace with...")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + } + else { + item = gtk_menu_item_new_with_mnemonic (_("Insert...")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + } + + submenu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu); + + for (w_creator_t *cr = w_creators; cr; cr = cr->next) { + if (cr->title) { + item = gtk_menu_item_new_with_mnemonic (cr->title); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (submenu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_replace_activate), + (void *)cr->type); + } + } + + if (strcmp (current_widget->type, "placeholder")) { + item = gtk_menu_item_new_with_mnemonic (_("Delete")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_delete_activate), + NULL); + + item = gtk_menu_item_new_with_mnemonic (_("Cut")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_cut_activate), + NULL); + + item = gtk_menu_item_new_with_mnemonic (_("Copy")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_copy_activate), + NULL); + } + item = gtk_menu_item_new_with_mnemonic ("Paste"); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_paste_activate), + NULL); + + g_signal_connect ((gpointer) menu, "deactivate", G_CALLBACK (w_menu_deactivate), user_data); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, widget, 0, gtk_get_current_event_time()); + return TRUE; +} + +static void +w_override_signals (GtkWidget *widget, gpointer user_data) { + g_signal_connect ((gpointer) widget, "button_press_event", G_CALLBACK (w_button_press_event), user_data); + g_signal_connect ((gpointer) widget, "expose_event", G_CALLBACK (w_expose_event), user_data); + if (GTK_IS_CONTAINER (widget)) { + gtk_container_forall (GTK_CONTAINER (widget), w_override_signals, user_data); + } +} + +void +w_reg_widget (const char *type, const char *title, ddb_gtkui_widget_t *(*create_func) (void)) { + w_creator_t *c; + for (c = w_creators; c; c = c->next) { + if (!strcmp (c->type, type)) { + fprintf (stderr, "gtkui w_reg_widget: widget type %s already registered\n"); + return; + } + } + c = malloc (sizeof (w_creator_t)); + memset (c, 0, sizeof (w_creator_t)); + c->type = type; + c->title = title; + c->create_func = create_func; + c->next = w_creators; + w_creators = c; +} + +void +w_unreg_widget (const char *type) { + w_creator_t *c, *prev = NULL; + for (c = w_creators; c; c = c->next) { + if (!strcmp (c->type, type)) { + if (prev) { + prev->next = c->next; + } + else { + w_creators = c->next; + } + free (c); + return; + } + prev = c; + } + fprintf (stderr, "gtkui w_unreg_widget: widget type %s is not registered\n"); +} + +ddb_gtkui_widget_t * +w_create (const char *type) { + for (w_creator_t *c = w_creators; c; c = c->next) { + if (!strcmp (c->type, type)) { + ddb_gtkui_widget_t *w = c->create_func (); + w->type = c->type; + + return w; + } + } + return NULL; +} + +void +w_destroy (ddb_gtkui_widget_t *w) { + if (w->destroy) { + w->destroy (w); + } + if (w->widget) { + gtk_widget_destroy (w->widget); + } + free (w); +} + +///// gtk_container convenience functions +void +w_container_add (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child) { + GtkWidget *container = NULL; + container = cont->widget; + gtk_container_add (GTK_CONTAINER (container), child->widget); + gtk_widget_show (child->widget); +} + +void +w_container_remove (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child) { + GtkWidget *container = NULL; + container = cont->widget; + gtk_container_remove (GTK_CONTAINER (container), child->widget); + +} + +////// placeholder widget + +gboolean +w_placeholder_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { + cairo_t *cr = gdk_cairo_create (widget->window); + cairo_set_source_rgb (cr, 255, 0, 0); + cairo_surface_t *checker; + cairo_t *cr2; + + checker = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 12, 12); + cr2 = cairo_create (checker); + + cairo_set_source_rgb (cr2, 0.5, 0.5 ,0.5); + cairo_paint (cr2); + cairo_set_source_rgb (cr2, 0, 0, 0); + cairo_move_to (cr2, 0, 0); + cairo_line_to (cr2, 12, 12); + cairo_move_to (cr2, 1, 12); + cairo_line_to (cr2, 12, 1); + cairo_set_line_width (cr2, 1); + cairo_set_antialias (cr2, CAIRO_ANTIALIAS_NONE); + cairo_stroke (cr2); + cairo_fill (cr2); + cairo_destroy (cr2); + + cairo_set_source_surface (cr, checker, 0, 0); + cairo_pattern_t *pt = cairo_get_source(cr); + cairo_pattern_set_extend (pt, CAIRO_EXTEND_REPEAT); + cairo_rectangle (cr, 0, 0, widget->allocation.width, widget->allocation.height); + cairo_paint (cr); + cairo_surface_destroy (checker); + cairo_destroy (cr); + return FALSE; +} + +ddb_gtkui_widget_t * +w_placeholder_create (void) { + w_placeholder_t *w = malloc (sizeof (w_placeholder_t)); + memset (w, 0, sizeof (w_placeholder_t)); + w->base.widget = gtk_drawing_area_new (); + gtk_widget_set_events (w->base.widget, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); + g_signal_connect ((gpointer) w->base.widget, "expose_event", G_CALLBACK (w_expose_event), w); + g_signal_connect_after ((gpointer) w->base.widget, "expose_event", G_CALLBACK (w_placeholder_expose_event), w); + g_signal_connect ((gpointer) w->base.widget, "button_press_event", G_CALLBACK (w_button_press_event), w); + return (ddb_gtkui_widget_t*)w; +} + +////// vsplitter widget +void +w_splitter_replace (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child, ddb_gtkui_widget_t *newchild) { + int ntab = 0; + ddb_gtkui_widget_t *prev = NULL; + for (ddb_gtkui_widget_t *c = cont->children; c; c = c->next, ntab++) { + if (c == child) { + newchild->next = c->next; + if (prev) { + prev->next = newchild; + } + else { + cont->children = newchild; + } + newchild->parent = cont; + gtk_container_remove (GTK_CONTAINER(cont->widget), c->widget); + c->widget = NULL; + w_destroy (c); + gtk_widget_show (newchild->widget); + if (ntab == 0) { + gtk_paned_add1 (GTK_PANED (cont->widget), newchild->widget); + } + else { + gtk_paned_add2 (GTK_PANED (cont->widget), newchild->widget); + } + break; + } + prev = c; + } +} + +ddb_gtkui_widget_t * +w_vsplitter_create (void) { + w_splitter_t *w = malloc (sizeof (w_splitter_t)); + memset (w, 0, sizeof (w_splitter_t)); + w->base.widget = gtk_vpaned_new (); + w->base.append = w_container_add; + w->base.remove = w_container_remove; + w->base.replace = w_splitter_replace; + + ddb_gtkui_widget_t *ph1, *ph2; + ph1 = w_create ("placeholder"); + ph2 = w_create ("placeholder"); + g_signal_connect ((gpointer) w->base.widget, "expose_event", G_CALLBACK (w_expose_event), w); + g_signal_connect ((gpointer) w->base.widget, "button_press_event", G_CALLBACK (w_button_press_event), w); + + w_append ((ddb_gtkui_widget_t*)w, ph1); + w_append ((ddb_gtkui_widget_t*)w, ph2); + + return (ddb_gtkui_widget_t*)w; +} + +////// hsplitter widget + +ddb_gtkui_widget_t * +w_hsplitter_create (void) { + w_splitter_t *w = malloc (sizeof (w_splitter_t)); + memset (w, 0, sizeof (w_splitter_t)); + w->base.widget = gtk_hpaned_new (); + w->base.append = w_container_add; + w->base.remove = w_container_remove; + w->base.replace = w_splitter_replace; + + ddb_gtkui_widget_t *ph1, *ph2; + ph1 = w_create ("placeholder"); + ph2 = w_create ("placeholder"); + g_signal_connect ((gpointer) w->base.widget, "expose_event", G_CALLBACK (w_expose_event), w); + g_signal_connect ((gpointer) w->base.widget, "button_press_event", G_CALLBACK (w_button_press_event), w); + + w_append ((ddb_gtkui_widget_t*)w, ph1); + w_append ((ddb_gtkui_widget_t*)w, ph2); + + return (ddb_gtkui_widget_t*)w; +} + +///// tabs widget + +void +w_tabs_add (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child) { + GtkWidget *label = gtk_label_new (child->type); + gtk_widget_show (label); + gtk_widget_show (child->widget); + gtk_notebook_append_page (GTK_NOTEBOOK (cont->widget), child->widget, label); +} + +void +w_tabs_replace (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child, ddb_gtkui_widget_t *newchild) { + int ntab = 0; + ddb_gtkui_widget_t *prev = NULL; + for (ddb_gtkui_widget_t *c = cont->children; c; c = c->next, ntab++) { + if (c == child) { + newchild->next = c->next; + if (prev) { + prev->next = newchild; + } + else { + cont->children = newchild; + } + newchild->parent = cont; + w_destroy (c); + GtkWidget *label = gtk_label_new (newchild->type); + gtk_widget_show (label); + gtk_widget_show (newchild->widget); + int pos = gtk_notebook_insert_page (GTK_NOTEBOOK (cont->widget), newchild->widget, label, ntab); + gtk_notebook_set_page (GTK_NOTEBOOK (cont->widget), pos); + break; + } + } +} + +ddb_gtkui_widget_t * +w_tabs_create (void) { + w_tabs_t *w = malloc (sizeof (w_tabs_t)); + memset (w, 0, sizeof (w_tabs_t)); + w->base.widget = gtk_notebook_new (); + w->base.append = w_tabs_add; + w->base.remove = w_container_remove; + w->base.replace = w_tabs_replace; + + ddb_gtkui_widget_t *ph1, *ph2, *ph3; + ph1 = w_create ("placeholder"); + ph2 = w_create ("placeholder"); + ph3 = w_create ("placeholder"); + + g_signal_connect ((gpointer) w->base.widget, "expose_event", G_CALLBACK (w_expose_event), w); + g_signal_connect ((gpointer) w->base.widget, "button_press_event", G_CALLBACK (w_button_press_event), w); + + w_append ((ddb_gtkui_widget_t*)w, ph1); + w_append ((ddb_gtkui_widget_t*)w, ph2); + w_append ((ddb_gtkui_widget_t*)w, ph3); + + return (ddb_gtkui_widget_t*)w; +} + +//// box widget +//// this widget should not be exposed to user, it is used as a top level +//// container (rootwidget) + +ddb_gtkui_widget_t * +w_box_create (void) { + w_box_t *w = malloc (sizeof (w_box_t)); + memset (w, 0, sizeof (w_box_t)); + w->base.widget = gtk_vbox_new (FALSE, 0); + w->base.append = w_container_add; + w->base.remove = w_container_remove; + + return (ddb_gtkui_widget_t*)w; +} + +//// tabstrip widget + +ddb_gtkui_widget_t * +w_tabstrip_create (void) { + w_tabstrip_t *w = malloc (sizeof (w_tabstrip_t)); + memset (w, 0, sizeof (w_tabstrip_t)); + w->base.widget = ddb_tabstrip_new (); + w_override_signals (w->base.widget, w); + return (ddb_gtkui_widget_t*)w; +} + +//// tabbed playlist widget + +typedef struct { + ddb_gtkui_widget_t *w; + DB_playItem_t *trk; +} w_trackdata_t; + +static gboolean +trackinfochanged_cb (gpointer p) { + w_trackdata_t *d = p; + w_tabbed_playlist_t *tp = (w_tabbed_playlist_t *)d->w; + ddb_playlist_t *plt = deadbeef->plt_get_curr (); + if (plt) { + int idx = deadbeef->plt_get_item_idx (plt, (DB_playItem_t *)d->trk, PL_MAIN); + if (idx != -1) { + ddb_listview_draw_row (tp->list, idx, (DdbListviewIter)d->trk); + } + deadbeef->plt_unref (plt); + } + if (d->trk) { + deadbeef->pl_item_unref (d->trk); + } + free (d); + return FALSE; +} + +static gboolean +paused_cb (gpointer p) { + w_tabbed_playlist_t *tp = (w_tabbed_playlist_t *)p; + DB_playItem_t *curr = deadbeef->streamer_get_playing_track (); + if (curr) { + int idx = deadbeef->pl_get_idx_of (curr); + ddb_listview_draw_row (tp->list, idx, (DdbListviewIter)curr); + deadbeef->pl_item_unref (curr); + } + return FALSE; +} + +static gboolean +refresh_cb (gpointer p) { + w_tabbed_playlist_t *tp = (w_tabbed_playlist_t *)p; + ddb_listview_clear_sort (tp->list); + ddb_listview_refresh (tp->list, DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); + return FALSE; +} + + +static gboolean +playlistswitch_cb (gpointer p) { + w_tabbed_playlist_t *tp = (w_tabbed_playlist_t *)p; + int curr = deadbeef->plt_get_curr_idx (); + char conf[100]; + snprintf (conf, sizeof (conf), "playlist.scroll.%d", curr); + int scroll = deadbeef->conf_get_int (conf, 0); + snprintf (conf, sizeof (conf), "playlist.cursor.%d", curr); + int cursor = deadbeef->conf_get_int (conf, -1); + ddb_tabstrip_refresh (tp->tabstrip); + deadbeef->pl_set_cursor (PL_MAIN, cursor); + if (cursor != -1) { + DB_playItem_t *it = deadbeef->pl_get_for_idx_and_iter (cursor, PL_MAIN); + if (it) { + deadbeef->pl_set_selected (it, 1); + deadbeef->pl_item_unref (it); + } + } + + ddb_listview_refresh (tp->list, DDB_LIST_CHANGED | DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); + ddb_listview_set_vscroll (tp->list, scroll); + return FALSE; +} + +struct fromto_t { + ddb_gtkui_widget_t *w; + DB_playItem_t *from; + DB_playItem_t *to; +}; + +static gboolean +songchanged_cb (gpointer p) { + struct fromto_t *ft = p; + DB_playItem_t *from = ft->from; + DB_playItem_t *to = ft->to; + w_tabbed_playlist_t *tp = (w_tabbed_playlist_t *)ft->w; + int to_idx = -1; + if (!ddb_listview_is_scrolling (tp->list) && to) { + int cursor_follows_playback = deadbeef->conf_get_int ("playlist.scroll.cursorfollowplayback", 0); + int scroll_follows_playback = deadbeef->conf_get_int ("playlist.scroll.followplayback", 0); + int plt = deadbeef->streamer_get_current_playlist (); + if (plt != -1) { + if (cursor_follows_playback && plt != deadbeef->plt_get_curr_idx ()) { + deadbeef->plt_set_curr_idx (plt); + } + to_idx = deadbeef->pl_get_idx_of (to); + if (to_idx != -1) { + if (cursor_follows_playback) { + ddb_listview_set_cursor_noscroll (tp->list, to_idx); + } + if (scroll_follows_playback && plt == deadbeef->plt_get_curr_idx ()) { + ddb_listview_scroll_to (tp->list, to_idx); + } + } + } + } + + if (from) { + int idx = deadbeef->pl_get_idx_of (from); + if (idx != -1) { + ddb_listview_draw_row (tp->list, idx, from); + } + } + if (to && to_idx != -1) { + ddb_listview_draw_row (tp->list, to_idx, to); + } + if (ft->from) { + deadbeef->pl_item_unref (ft->from); + } + if (ft->to) { + deadbeef->pl_item_unref (ft->to); + } + free (ft); + return FALSE; +} + +static int +w_tabbed_playlist_message (ddb_gtkui_widget_t *w, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { + w_tabbed_playlist_t *tp = (w_tabbed_playlist_t *)w; + switch (id) { + case DB_EV_SONGCHANGED: + g_idle_add (redraw_queued_tracks_cb, tp->list); + ddb_event_trackchange_t *ev = (ddb_event_trackchange_t *)ctx; + struct fromto_t *ft = malloc (sizeof (struct fromto_t)); + ft->from = ev->from; + ft->to = ev->to; + if (ft->from) { + deadbeef->pl_item_ref (ft->from); + } + if (ft->to) { + deadbeef->pl_item_ref (ft->to); + } + ft->w = w; + g_idle_add (songchanged_cb, ft); + break; + case DB_EV_TRACKINFOCHANGED: + { + ddb_event_track_t *ev = (ddb_event_track_t *)ctx; + if (ev->track) { + deadbeef->pl_item_ref (ev->track); + } + w_trackdata_t *d = malloc (sizeof (w_trackdata_t)); + memset (d, 0, sizeof (w_trackdata_t)); + d->w = w; + d->trk = ev->track; + g_idle_add (trackinfochanged_cb, d); + } + break; + case DB_EV_PAUSED: + g_idle_add (paused_cb, w); + break; + case DB_EV_PLAYLISTCHANGED: + g_idle_add (refresh_cb, w); + break; + case DB_EV_PLAYLISTSWITCHED: + g_idle_add (playlistswitch_cb, w); + break; + } + return 0; +} + +ddb_gtkui_widget_t * +w_tabbed_playlist_create (void) { + w_tabbed_playlist_t *w = malloc (sizeof (w_tabbed_playlist_t)); + memset (w, 0, sizeof (w_tabbed_playlist_t)); + + GtkWidget *vbox = gtk_vbox_new (FALSE, 0); + w->base.widget = vbox; + gtk_widget_show (vbox); + + GtkWidget *tabstrip = ddb_tabstrip_new (); + w->tabstrip = (DdbTabStrip *)tabstrip; + gtk_widget_show (tabstrip); + GtkWidget *list = ddb_listview_new (); + w->list = (DdbListview *)list; + gtk_widget_show (list); + GtkWidget *frame = gtk_frame_new (NULL); + gtk_widget_show (frame); + + gtk_box_pack_start (GTK_BOX (vbox), tabstrip, FALSE, TRUE, 0); + gtk_widget_set_size_request (tabstrip, -1, 24); + GTK_WIDGET_UNSET_FLAGS (tabstrip, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS (tabstrip, GTK_CAN_DEFAULT); + + gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (frame), 1); + + gtk_container_add (GTK_CONTAINER (frame), list); + main_playlist_init (list); + if (deadbeef->conf_get_int ("gtkui.headers.visible", 1)) { + ddb_listview_show_header (w->list, 1); + } + else { + ddb_listview_show_header (w->list, 0); + } + + gtk_container_forall (GTK_CONTAINER (w->base.widget), w_override_signals, w); + + w->base.message = w_tabbed_playlist_message; + return (ddb_gtkui_widget_t*)w; +} + +///// playlist widget + +ddb_gtkui_widget_t * +w_playlist_create (void) { + w_playlist_t *w = malloc (sizeof (w_playlist_t)); + memset (w, 0, sizeof (w_playlist_t)); + w->base.widget = ddb_listview_new (); + main_playlist_init (w->base.widget); + if (deadbeef->conf_get_int ("gtkui.headers.visible", 1)) { + ddb_listview_show_header (DDB_LISTVIEW (w->base.widget), 1); + } + else { + ddb_listview_show_header (DDB_LISTVIEW (w->base.widget), 0); + } + + w_override_signals (w->base.widget, w); + + w->base.message = w_tabbed_playlist_message; + return (ddb_gtkui_widget_t*)w; +} + +////// selection properties widget + +gboolean +fill_selproperties_cb (gpointer data) { + w_selproperties_t *w = data; + DB_playItem_t **tracks = NULL; + int numtracks = 0; + deadbeef->pl_lock (); + int nsel = deadbeef->pl_getselcount (); + if (0 < nsel) { + tracks = malloc (sizeof (DB_playItem_t *) * nsel); + if (tracks) { + int n = 0; + DB_playItem_t *it = deadbeef->pl_get_first (PL_MAIN); + while (it) { + if (deadbeef->pl_is_selected (it)) { + assert (n < nsel); + deadbeef->pl_item_ref (it); + tracks[n++] = it; + } + DB_playItem_t *next = deadbeef->pl_get_next (it, PL_MAIN); + deadbeef->pl_item_unref (it); + it = next; + } + numtracks = nsel; + } + else { + deadbeef->pl_unlock (); + return FALSE; + } + } + GtkListStore *store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (w->tree))); + trkproperties_fill_meta (store, tracks, numtracks); + if (tracks) { + for (int i = 0; i < numtracks; i++) { + deadbeef->pl_item_unref (tracks[i]); + } + free (tracks); + tracks = NULL; + numtracks = 0; + } + deadbeef->pl_unlock (); + return FALSE; +} + +static int +selproperties_message (ddb_gtkui_widget_t *w, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { + w_tabbed_playlist_t *tp = (w_tabbed_playlist_t *)w; + switch (id) { + case DB_EV_PLAYLISTCHANGED: + case DB_EV_SELCHANGED: + { + g_idle_add (fill_selproperties_cb, w); + } + break; + } + return 0; +} + +ddb_gtkui_widget_t * +w_selproperties_create (void) { + w_selproperties_t *w = malloc (sizeof (w_selproperties_t)); + memset (w, 0, sizeof (w_selproperties_t)); + + w->base.widget = gtk_scrolled_window_new (NULL, NULL); + w->tree = gtk_tree_view_new (); + gtk_widget_show (w->tree); + gtk_container_add (GTK_CONTAINER (w->base.widget), w->tree); + w->base.message = selproperties_message; + + GtkListStore *store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT); + gtk_tree_view_set_model (GTK_TREE_VIEW (w->tree), GTK_TREE_MODEL (store)); + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (w->tree), TRUE); + + GtkCellRenderer *rend1 = gtk_cell_renderer_text_new (); + GtkCellRenderer *rend2 = gtk_cell_renderer_text_new (); + GtkTreeViewColumn *col1 = gtk_tree_view_column_new_with_attributes (_("Key"), rend1, "text", 0, NULL); + gtk_tree_view_column_set_resizable (col1, TRUE); + GtkTreeViewColumn *col2 = gtk_tree_view_column_new_with_attributes (_("Value"), rend2, "text", 1, NULL); + gtk_tree_view_column_set_resizable (col2, TRUE); + gtk_tree_view_append_column (GTK_TREE_VIEW (w->tree), col1); + gtk_tree_view_append_column (GTK_TREE_VIEW (w->tree), col2); + GtkCellRenderer *rend_propkey = gtk_cell_renderer_text_new (); + GtkCellRenderer *rend_propvalue = gtk_cell_renderer_text_new (); + gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW (w->tree), TRUE); + w_override_signals (w->base.widget, w); + + return (ddb_gtkui_widget_t *)w; +} + +///// cover art display +void +coverart_avail_callback (void *user_data) { + w_coverart_t *w = user_data; + gtk_widget_queue_draw (w->drawarea); +} + +static gboolean +coverart_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { + DB_playItem_t *it = deadbeef->streamer_get_playing_track (); + if (!it) { + return FALSE; + } + int width = widget->allocation.width; + int height = widget->allocation.height; + const char *album = deadbeef->pl_find_meta (it, "album"); + const char *artist = deadbeef->pl_find_meta (it, "artist"); + if (!album || !*album) { + album = deadbeef->pl_find_meta (it, "title"); + } + GdkPixbuf *pixbuf = get_cover_art_callb (deadbeef->pl_find_meta ((it), ":URI"), artist, album, min(width,height), coverart_avail_callback, user_data); + if (pixbuf) { + int pw = gdk_pixbuf_get_width (pixbuf); + int ph = gdk_pixbuf_get_height (pixbuf); + gdk_draw_pixbuf (widget->window, widget->style->white_gc, pixbuf, 0, 0, widget->allocation.width/2-pw/2, widget->allocation.height/2-ph/2, pw, ph, GDK_RGB_DITHER_NONE, 0, 0); + g_object_unref (pixbuf); + } + deadbeef->pl_item_unref (it); + return TRUE; +} + +static gboolean +coverart_redraw_cb (void *user_data) { + w_coverart_t *w = user_data; + gtk_widget_queue_draw (w->drawarea); + return FALSE; +} + +static int +coverart_message (ddb_gtkui_widget_t *w, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { + w_coverart_t *ca = (w_coverart_t *)w; + switch (id) { + case DB_EV_TRACKINFOCHANGED: + { + ddb_event_track_t *ev = (ddb_event_track_t *)ctx; + DB_playItem_t *it = deadbeef->streamer_get_playing_track (); + if (it == ev->track) { + g_idle_add (coverart_redraw_cb, w); + } + if (it) { + deadbeef->pl_item_unref (it); + } + } + break; + } + return 0; +} + +ddb_gtkui_widget_t * +w_coverart_create (void) { + w_coverart_t *w = malloc (sizeof (w_coverart_t)); + memset (w, 0, sizeof (w_coverart_t)); + + w->base.widget = gtk_event_box_new (); + w->base.message = coverart_message; + w->drawarea = gtk_drawing_area_new (); + gtk_widget_show (w->drawarea); + gtk_container_add (GTK_CONTAINER (w->base.widget), w->drawarea); + g_signal_connect_after ((gpointer) w->drawarea, "expose_event", G_CALLBACK (coverart_expose_event), w); + w_override_signals (w->base.widget, w); + return (ddb_gtkui_widget_t *)w; +} + + diff --git a/plugins/gtkui/widgets.h b/plugins/gtkui/widgets.h new file mode 100644 index 00000000..9263eb92 --- /dev/null +++ b/plugins/gtkui/widgets.h @@ -0,0 +1,87 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifndef __WIDGETS_H +#define __WIDGETS_H + +#include "gtkui_api.h" + +void +w_init (void); + +void +w_free (void); + +ddb_gtkui_widget_t * +w_get_rootwidget (void); + +void +w_set_design_mode (int active); + +void +w_reg_widget (const char *type, const char *title, ddb_gtkui_widget_t *(*create_func) (void)); + +void +w_unreg_widget (const char *type); + +ddb_gtkui_widget_t * +w_create (const char *type); + +const char * +w_create_from_string (const char *s, ddb_gtkui_widget_t **parent); + +void +w_destroy (ddb_gtkui_widget_t *w); + +void +w_append (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child); + +void +w_remove (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child); + +ddb_gtkui_widget_t * +w_hsplitter_create (void); + +ddb_gtkui_widget_t * +w_vsplitter_create (void); + +ddb_gtkui_widget_t * +w_box_create (void); + +ddb_gtkui_widget_t * +w_tabstrip_create (void); + +ddb_gtkui_widget_t * +w_tabbed_playlist_create (void); + +ddb_gtkui_widget_t * +w_playlist_create (void); + +ddb_gtkui_widget_t * +w_placeholder_create (void); + +ddb_gtkui_widget_t * +w_tabs_create (void); + +ddb_gtkui_widget_t * +w_selproperties_create (void); + +ddb_gtkui_widget_t * +w_coverart_create (void); + +#endif diff --git a/plugins/medialib/Makefile.am b/plugins/medialib/Makefile.am new file mode 100644 index 00000000..0406cac9 --- /dev/null +++ b/plugins/medialib/Makefile.am @@ -0,0 +1,8 @@ +if HAVE_MEDIALIB +pkglib_LTLIBRARIES = medialib.la +medialib_la_SOURCES = medialib.c +medialib_la_LDFLAGS = -module + +medialib_la_LIBADD = $(LDADD) +AM_CFLAGS = $(CFLAGS) -std=c99 -fPIC +endif diff --git a/plugins/medialib/medialib.c b/plugins/medialib/medialib.c new file mode 100644 index 00000000..8a957156 --- /dev/null +++ b/plugins/medialib/medialib.c @@ -0,0 +1,256 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include <sys/time.h> +#include <string.h> +#include "../../deadbeef.h" + +DB_functions_t *deadbeef; + +typedef struct ml_string_s { + const char *text; + struct ml_string_s *next; +} ml_string_t; + +typedef struct ml_entry_s { + const char *file; + const char *title; + int subtrack; + ml_string_t *artist; + ml_string_t *album; + ml_string_t *genre; + ml_string_t *folder; + struct ml_entry_s *next; +} ml_entry_t; + +typedef struct { + ml_entry_t *tracks; + + // index tables + ml_string_t *album; + ml_string_t *artist; + ml_string_t *genre; + ml_string_t *folder; +} ml_db_t; + +#define REG_COL(col)\ +ml_string_t *\ +ml_reg_##col (ml_db_t *db, const char *col) {\ + ml_string_t *s;\ + int release = 0;\ + if (!col) {\ + col = deadbeef->metacache_add_string ("Unknown");\ + release = 1;\ + }\ + for (s = db->col; s; s = s->next) {\ + if (s->text == col) {\ + if (release) {\ + deadbeef->metacache_unref (col);\ + }\ + return s;\ + }\ + }\ + if (!release) {\ + deadbeef->metacache_ref (col);\ + }\ + s = malloc (sizeof (ml_string_t));\ + memset (s, 0, sizeof (ml_string_t));\ + s->text = col;\ + s->next = db->col;\ + db->col = s;\ + return s;\ +} + +REG_COL(album); +REG_COL(artist); +REG_COL(genre); +REG_COL(folder); + +DB_playItem_t *(*plt_insert_dir) (ddb_playlist_t *plt, DB_playItem_t *after, const char *dirname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data); + +uintptr_t tid; +int scanner_terminate; + +static int +add_file_info_cb (DB_playItem_t *it, void *data) { +// fprintf (stderr, "added %s \r", deadbeef->pl_find_meta (it, ":URI")); + return 0; +} + +static void +scanner_thread (void *none) { + return; + // create invisible playlist + ddb_playlist_t *plt = deadbeef->plt_alloc ("scanner"); + + struct timeval tm1; + gettimeofday (&tm1, NULL); + + plt_insert_dir (plt, NULL, "/backup/mus/en", &scanner_terminate, add_file_info_cb, NULL); + + struct timeval tm2; + gettimeofday (&tm2, NULL); + int ms = (tm2.tv_sec*1000+tm2.tv_usec/1000) - (tm1.tv_sec*1000+tm1.tv_usec/1000); + fprintf (stderr, "initial scan time: %f seconds (%d tracks)\n", ms / 1000.f, deadbeef->plt_get_item_count (plt, PL_MAIN)); + + fprintf (stderr, "building index...\n"); + gettimeofday (&tm1, NULL); + ml_db_t db; + memset (&db, 0, sizeof (db)); + + ml_entry_t *tail = NULL; + + DB_playItem_t *it = deadbeef->plt_get_first (plt, PL_MAIN); + while (it) { + + ml_entry_t *en = malloc (sizeof (ml_entry_t)); + memset (en, 0, sizeof (ml_entry_t)); + + const char *uri = deadbeef->pl_find_meta (it, ":URI"); + const char *title = deadbeef->pl_find_meta (it, "title"); + const char *artist = deadbeef->pl_find_meta (it, "artist"); + const char *album = deadbeef->pl_find_meta (it, "album"); + const char *genre = deadbeef->pl_find_meta (it, "genre"); + + ml_string_t *alb = ml_reg_album (&db, album); + ml_string_t *art = ml_reg_artist (&db, artist); + ml_string_t *gnr = ml_reg_genre (&db, genre); + + char *fn = strrchr (uri, '/'); + ml_string_t *fld = NULL; + if (fn) { + char folder[fn-uri+1]; + memcpy (folder, uri, fn-uri); + folder[fn-uri] = 0; + const char *s = deadbeef->metacache_add_string (folder); + fld = ml_reg_folder (&db, s); + deadbeef->metacache_unref (s); // there should be at least 1 ref left, so it's safe + } + + deadbeef->metacache_ref (uri); + en->file = uri; + deadbeef->metacache_ref (title); + if (deadbeef->pl_get_item_flags (it) & DDB_IS_SUBTRACK) { + en->subtrack = deadbeef->pl_find_meta_int (it, ":TRACKNUM", -1); + } + else { + en->subtrack = -1; + } + en->title = title; + en->artist = art; + en->album = alb; + en->genre = gnr; + en->folder = fld; + + if (tail) { + tail->next = en; + tail = en; + } + else { + tail = db.tracks = en; + } + + DB_playItem_t *next = deadbeef->pl_get_next (it, PL_MAIN); + deadbeef->pl_item_unref (it); + it = next; + } + ms = (tm2.tv_sec*1000+tm2.tv_usec/1000) - (tm1.tv_sec*1000+tm1.tv_usec/1000); + + int nalb = 0; + int nart = 0; + int ngnr = 0; + int nfld = 0; + ml_string_t *s; + for (s = db.album; s; s = s->next, nalb++); + for (s = db.artist; s; s = s->next, nart++); + for (s = db.genre; s; s = s->next, ngnr++); + for (s = db.folder; s; s = s->next, nfld++); + + fprintf (stderr, "index build time: %f seconds (%d albums, %d artists, %d genres, %d folders)\n", ms / 1000.f, nalb, nart, ngnr, nfld); + + deadbeef->plt_free (plt); +} + +static int +ml_connect (void) { + tid = deadbeef->thread_start_low_priority (scanner_thread, NULL); + return 0; +} + +static int +ml_stop (void) { + if (tid) { + scanner_terminate = 1; + deadbeef->thread_join (tid); + tid = 0; + } + + return 0; +} + +static int +ml_message (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { + return 0; +} + +typedef struct { + DB_misc_t plugin; +} ddb_medialib_plugin_t; + +// define plugin interface +static ddb_medialib_plugin_t plugin = { + .plugin.plugin.api_vmajor = 1, + .plugin.plugin.api_vminor = 0, + .plugin.plugin.version_major = 0, + .plugin.plugin.version_minor = 1, + .plugin.plugin.type = DB_PLUGIN_MISC, + .plugin.plugin.id = "medialib", + .plugin.plugin.name = "Media Library", + .plugin.plugin.descr = "Scans disk for music files and manages them as database", + .plugin.plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , + .plugin.plugin.website = "http://deadbeef.sf.net", + .plugin.plugin.connect = ml_connect, + .plugin.plugin.stop = ml_stop, +// .plugin.plugin.configdialog = settings_dlg, + .plugin.plugin.message = ml_message, +}; + +DB_plugin_t * +medialib_load (DB_functions_t *api) { + deadbeef = api; + + // hack: we need original function without overrides + plt_insert_dir = deadbeef->plt_insert_dir; + return DB_PLUGIN (&plugin); +} diff --git a/scripts/quickinstall.sh b/scripts/quickinstall.sh index 20b89482..b7cda98b 100755 --- a/scripts/quickinstall.sh +++ b/scripts/quickinstall.sh @@ -42,3 +42,4 @@ cp ./plugins/converter/.libs/converter_gtkui.so /usr/local/lib/deadbeef/ cp ./plugins/soundtouch/ddb_soundtouch.so /usr/local/lib/deadbeef/ cp ./plugins/vfs_zip/.libs/vfs_zip.so /usr/local/lib/deadbeef/ cp ./plugins/mono2stereo/mono2stereo.so /usr/local/lib/deadbeef/ +cp ./plugins/medialib/.libs/medialib.so /usr/local/lib/deadbeef/ |