diff options
Diffstat (limited to 'plugins')
34 files changed, 2852 insertions, 415 deletions
diff --git a/plugins/artwork/artwork.c b/plugins/artwork/artwork.c index 47aaf748..5dffd714 100644 --- a/plugins/artwork/artwork.c +++ b/plugins/artwork/artwork.c @@ -126,6 +126,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 } } @@ -163,6 +166,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; @@ -1131,6 +1137,7 @@ fetcher_thread (void *none) } if (param->callback) { param->callback (param->fname, param->artist, param->album, param->user_data); + param->callback = NULL; } } queue_pop (); @@ -1185,16 +1192,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; } @@ -1213,6 +1229,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 74889110..69c36580 100644 --- a/plugins/gtkui/Makefile.am +++ b/plugins/gtkui/Makefile.am @@ -23,8 +23,10 @@ GTKUI_SOURCES = gtkui.c gtkui.h\ tagwritersettings.c tagwritersettings.h\ wingeom.c wingeom.h\ pluginconf.h\ + widgets.c widgets.h\ ddbseekbar.h ddbequalizer.h ddbcellrenderertextmultiline.h\ - ddbseekbar.c ddbequalizer.c ddbcellrenderertextmultiline.c + ddbseekbar.c ddbequalizer.c ddbcellrenderertextmultiline.c\ + gtkuigl.c gtkuigl.h sdkdir = $(pkgincludedir) sdk_HEADERS = gtkui_api.h @@ -51,7 +53,7 @@ endif if HAVE_GTK2 ddb_gui_GTK2_la_SOURCES = $(GTKUI_SOURCES) ddb_gui_GTK2_la_LDFLAGS = -module -ddb_gui_GTK2_la_LIBADD = $(LDADD) $(GTK2_DEPS_LIBS) +ddb_gui_GTK2_la_LIBADD = $(LDADD) $(GTK2_DEPS_LIBS) -lGLU ddb_gui_GTK2_la_CFLAGS = -std=c99 $(GTK2_DEPS_CFLAGS) endif if HAVE_GTK3 diff --git a/plugins/gtkui/callbacks.c b/plugins/gtkui/callbacks.c index be18328c..b70b6a17 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,...) @@ -284,9 +285,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); } @@ -387,9 +387,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; } @@ -852,6 +849,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))) { @@ -918,6 +917,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); @@ -963,9 +964,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); } @@ -990,8 +990,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); } @@ -1103,7 +1102,7 @@ void on_jump_to_current_track1_activate (GtkMenuItem *menuitem, gpointer user_data) { - gtkui_focus_on_playing_track (); + deadbeef->sendmessage (DB_EV_TRACKFOCUSCURRENT, 0, 0, 0); } static GtkWidget *translatorswindow; @@ -1157,9 +1156,7 @@ on_sort_by_title_activate (GtkMenuItem *menuitem, deadbeef->plt_sort (plt, PL_MAIN, -1, "%t", DDB_SORT_ASCENDING); 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); } @@ -1171,9 +1168,7 @@ on_sort_by_track_nr_activate (GtkMenuItem *menuitem, deadbeef->plt_sort (plt, PL_MAIN, -1, "%n", DDB_SORT_ASCENDING); 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); } @@ -1185,9 +1180,7 @@ on_sort_by_album_activate (GtkMenuItem *menuitem, deadbeef->plt_sort (plt, PL_MAIN, -1, "%b", DDB_SORT_ASCENDING); 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); } @@ -1199,9 +1192,7 @@ on_sort_by_artist_activate (GtkMenuItem *menuitem, deadbeef->plt_sort (plt, PL_MAIN, -1, "%a", DDB_SORT_ASCENDING); 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); } @@ -1228,9 +1219,7 @@ on_sort_by_random_activate (GtkMenuItem *menuitem, 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); } @@ -1264,12 +1253,18 @@ on_sort_by_custom_activate (GtkMenuItem *menuitem, deadbeef->plt_sort (plt, PL_MAIN, -1, fmt, order == 0 ? DDB_SORT_ASCENDING : DDB_SORT_DESCENDING); 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 a354baca..1e4e66e3 100644 --- a/plugins/gtkui/callbacks.h +++ b/plugins/gtkui/callbacks.h @@ -1161,5 +1161,9 @@ on_convert8to16_toggled (GtkToggleButton *togglebutton, gpointer user_data); void +on_design_mode1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void on_reset_autostop_toggled (GtkToggleButton *togglebutton, gpointer user_data); diff --git a/plugins/gtkui/coverart.c b/plugins/gtkui/coverart.c index e0451f5a..a2f1119a 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 490f62e1..a4914b6f 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 3b1ac61b..5bd97a9a 100644 --- a/plugins/gtkui/ddbcellrenderertextmultiline.c +++ b/plugins/gtkui/ddbcellrenderertextmultiline.c @@ -1,6 +1,3 @@ -/* ddbcellrenderertextmultiline.c generated by valac 0.14.0, the Vala compiler - * generated from ddbcellrenderertextmultiline.vala, do not modify */ - /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> @@ -84,7 +81,7 @@ 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 }; @@ -96,7 +93,7 @@ static void ddb_cell_editable_text_view_real_editing_canceled (GtkCellRenderer*) 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 @@ -160,13 +157,13 @@ static gboolean ddb_cell_editable_text_view_real_key_press_event (GtkWidget* bas return result; } - static void ddb_cell_editable_text_view_real_start_editing (GtkCellEditable* base, GdkEvent* event) { - DdbCellEditableTextView * self; - self = (DdbCellEditableTextView*) base; - g_return_if_fail (event != NULL); + DdbCellEditableTextView * self; + self = (DdbCellEditableTextView*) base; + g_return_if_fail (event != NULL); } + #if GTK_CHECK_VERSION(2,20,0) static void ddb_cell_editable_text_view_real_editing_canceled (GtkCellRenderer* base) { } @@ -190,6 +187,10 @@ static void ddb_cell_editable_text_view_class_init (DdbCellEditableTextViewClass G_OBJECT_CLASS (klass)->finalize = ddb_cell_editable_text_view_finalize; } +void ddb_cell_editable_text_view_start_editing (DdbCellEditableTextView* self, GdkEvent* event) { + g_return_if_fail (self != NULL); + g_return_if_fail (event != NULL); +} 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); @@ -279,6 +280,7 @@ static void ddb_cell_renderer_text_multiline_gtk_cell_renderer_text_editing_done g_signal_emit_by_name ((GtkCellRendererText*) _tmp14_, "edited", _tmp16_, new_text); _g_free0 (new_text); _g_object_unref0 (buf); + _g_free0 (new_text); } @@ -435,6 +437,10 @@ static GtkCellEditable* ddb_cell_renderer_text_multiline_real_start_editing (Gtk _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 b464472a..ca461f34 100644 --- a/plugins/gtkui/ddbcellrenderertextmultiline.h +++ b/plugins/gtkui/ddbcellrenderertextmultiline.h @@ -1,6 +1,3 @@ -/* ddbcellrenderertextmultiline.h generated by valac 0.14.0, the Vala compiler, do not modify */ - - #ifndef __DDBCELLRENDERERTEXTMULTILINE_H__ #define __DDBCELLRENDERERTEXTMULTILINE_H__ @@ -8,6 +5,7 @@ #include <gtk/gtk.h> #include <stdlib.h> #include <string.h> +#include <gdk/gdk.h> G_BEGIN_DECLS @@ -55,10 +53,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 05f42547..02946aea 100644 --- a/plugins/gtkui/ddbequalizer.c +++ b/plugins/gtkui/ddbequalizer.c @@ -44,7 +44,6 @@ 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 _cairo_destroy0(var) ((var == NULL) ? NULL : (var = (cairo_destroy (var), NULL))) @@ -73,7 +72,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 diff --git a/plugins/gtkui/ddbequalizer.h b/plugins/gtkui/ddbequalizer.h index c9e49988..02df70af 100644 --- a/plugins/gtkui/ddbequalizer.h +++ b/plugins/gtkui/ddbequalizer.h @@ -1,6 +1,3 @@ -/* ddbequalizer.h generated by valac 0.14.0, the Vala compiler, do not modify */ - - #ifndef __DDBEQUALIZER_H__ #define __DDBEQUALIZER_H__ diff --git a/plugins/gtkui/ddblistview.c b/plugins/gtkui/ddblistview.c index 6312a103..4bf53fc8 100644 --- a/plugins/gtkui/ddblistview.c +++ b/plugins/gtkui/ddblistview.c @@ -275,6 +275,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) @@ -361,7 +369,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_can_focus (listview->list, TRUE); + gtk_widget_set_can_default (listview->list, TRUE); + 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); @@ -400,7 +410,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", @@ -418,7 +428,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", @@ -457,6 +467,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() @@ -1250,7 +1264,7 @@ ddb_listview_list_render_row_background (DdbListview *ps, cairo_t *cr, DdbListvi if (gtk_widget_get_style (treeview)->depth == -1) { return; // drawing was called too early } - GTK_WIDGET_SET_FLAGS (GTK_WIDGET (treeview), GTK_HAS_FOCUS); + gtk_widget_set_can_focus (GTK_WIDGET (treeview), TRUE); #endif } int sel = it && ps->binding->is_selected (it); @@ -1437,6 +1451,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: @@ -2519,7 +2534,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 @@ -2669,6 +2684,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); @@ -2710,7 +2726,7 @@ ddb_listview_list_button_press_event (GtkWidget *widget, UNREF (it); } } - return FALSE; + return TRUE; } gboolean @@ -3077,3 +3093,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_can_focus (widget, TRUE); + return FALSE; +} + +gboolean +ddb_listview_list_focus_out_event (GtkWidget *widget, GdkEvent *event, gpointer user_data) { + gtk_widget_set_can_focus (widget, FALSE); + return FALSE; +} diff --git a/plugins/gtkui/ddbseekbar.c b/plugins/gtkui/ddbseekbar.c index bcb8b7fe..544083b9 100644 --- a/plugins/gtkui/ddbseekbar.c +++ b/plugins/gtkui/ddbseekbar.c @@ -48,7 +48,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 }; @@ -164,8 +164,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 71b753c0..ee455087 100644 --- a/plugins/gtkui/ddbseekbar.h +++ b/plugins/gtkui/ddbseekbar.h @@ -1,6 +1,3 @@ -/* ddbseekbar.h generated by valac 0.14.0, the Vala compiler, do not modify */ - - #ifndef __DDBSEEKBAR_H__ #define __DDBSEEKBAR_H__ @@ -31,7 +28,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 ab8fad1e..18151ff4 100644 --- a/plugins/gtkui/ddbtabstrip.c +++ b/plugins/gtkui/ddbtabstrip.c @@ -26,7 +26,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, \ @@ -279,7 +279,7 @@ on_tabstrip_drag_data_received (GtkWidget *widget, memcpy (mem, ptr, len); mem[len] = 0; // we don't pass control structure, but there's only one drag-drop view currently - ps->binding->external_drag_n_drop (NULL, mem, len); + gtkui_receive_fm_drop (NULL, mem, len); } else if (target_type == 1) { uint32_t *d= (uint32_t *)ptr; @@ -288,7 +288,7 @@ on_tabstrip_drag_data_received (GtkWidget *widget, int length = (len/4)-1; ddb_playlist_t *p = deadbeef->plt_get_for_idx (plt); if (p) { - ps->binding->drag_n_drop (NULL, p, d, length, gdk_drag_context_get_selected_action (drag_context) == GDK_ACTION_COPY ? 1 : 0); + main_drag_n_drop (NULL, p, d, length, gdk_drag_context_get_selected_action (drag_context) == GDK_ACTION_COPY ? 1 : 0); deadbeef->plt_unref (p); } } @@ -300,16 +300,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() { @@ -780,8 +780,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); @@ -1055,7 +1053,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 >= a.width - arrow_widget_width) { if (event->type == GDK_BUTTON_PRESS) { @@ -1063,7 +1061,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) { @@ -1076,9 +1074,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 @@ -1114,21 +1112,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 184b66fd..820c30a1 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> @@ -453,6 +453,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> @@ -481,7 +491,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> @@ -491,7 +501,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> @@ -502,7 +512,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> @@ -537,7 +547,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> @@ -547,7 +557,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> @@ -636,7 +646,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> @@ -696,7 +706,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> @@ -717,7 +727,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> @@ -937,47 +947,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> @@ -989,8 +958,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 36735cbe..69f016af 100644 --- a/plugins/gtkui/fileman.c +++ b/plugins/gtkui/fileman.c @@ -110,17 +110,15 @@ open_files_worker (void *data) { gtkpl_add_files (lst); deadbeef->pl_save_all (); deadbeef->conf_save (); - 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); @@ -170,13 +168,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; } @@ -188,7 +187,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 a4593ea0..5a692486 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,...) @@ -349,7 +349,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 (); @@ -364,14 +364,14 @@ redraw_queued_tracks (DdbListview *pl, int list) { deadbeef->pl_unlock (); } -static gboolean -redraw_queued_tracks_cb (gpointer nothing) { +gboolean +redraw_queued_tracks_cb (gpointer plt) { + DdbListview *list = plt; int iconified = gdk_window_get_state(gtk_widget_get_window(mainwin)) & 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); + redraw_queued_tracks (list); return FALSE; } @@ -388,7 +388,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 && gtk_widget_get_window (searchwin)) { + int iconified = gdk_window_get_state(gtk_widget_get_window (searchwin)) & 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 @@ -427,8 +432,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"); @@ -453,22 +458,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 (); } @@ -485,26 +478,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; } @@ -837,40 +810,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; @@ -894,8 +833,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); } @@ -953,11 +890,11 @@ volumebar_redraw (void) { gdk_window_invalidate_rect (gtk_widget_get_window (volumebar), 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; @@ -982,8 +919,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); @@ -1003,9 +954,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; @@ -1027,6 +978,7 @@ gtkui_message (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { void gtkui_thread (void *ctx) { + XInitThreads (); // let's start some gtk g_thread_init (NULL); // add_pixmap_directory (PREFIX "/share/deadbeef/pixmaps"); @@ -1045,7 +997,45 @@ 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); + w_reg_widget ("scope", _("Scope"), w_scope_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[4000]; + 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(); @@ -1087,16 +1077,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")); @@ -1119,6 +1106,8 @@ gtkui_thread (void *ctx) { gtk_main (); + w_free (); + if (refresh_timeout) { g_source_remove (refresh_timeout); refresh_timeout = 0; @@ -1209,24 +1198,6 @@ gtkui_plt_load (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname, in } void -gtkui_focus_on_playing_track (void) { - DB_playItem_t *it = deadbeef->streamer_get_playing_track (); - if (it) { - int plt = deadbeef->streamer_get_current_playlist (); - if (plt != deadbeef->plt_get_curr_idx ()) { - deadbeef->plt_set_curr_idx (plt); - } - int idx = deadbeef->pl_get_idx_of (it); - if (idx != -1) { - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_scroll_to (pl, idx); - ddb_listview_set_cursor (pl, idx); - } - deadbeef->pl_item_unref (it); - } -} - -void gtkui_playlist_set_curr (int playlist) { deadbeef->plt_set_curr_idx (playlist); deadbeef->conf_set_int ("playlist.current", playlist); @@ -1332,7 +1303,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; } @@ -1408,4 +1379,15 @@ static ddb_gtkui_t plugin = { .gui.plugin.message = gtkui_message, .gui.run_dialog = gtkui_run_dialog_root, .get_mainwin = gtkui_get_mainwin, + .w_reg_widget = w_reg_widget, + .w_unreg_widget = w_unreg_widget, + .w_is_registered = w_is_registered, + .w_get_rootwidget = w_get_rootwidget, + .w_create = w_create, + .w_set_name = w_set_name, + .w_destroy = w_destroy, + .w_append = w_append, + .w_replace = w_replace, + .w_remove = w_remove, + .api_version = GTKUI_API_VERSION, }; diff --git a/plugins/gtkui/gtkui.h b/plugins/gtkui/gtkui.h index 13e3e9cb..2d4ddf11 100644 --- a/plugins/gtkui/gtkui.h +++ b/plugins/gtkui/gtkui.h @@ -115,8 +115,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); @@ -140,9 +140,6 @@ extern int (*gtkui_original_plt_add_dir) (ddb_playlist_t *plt, const char *dirna extern int (*gtkui_original_plt_add_file) (ddb_playlist_t *plt, const char *fname, int (*cb)(DB_playItem_t *it, void *data), void *user_data); void -gtkui_focus_on_playing_track (void); - -void gtkui_playlist_set_curr (int playlist); void @@ -154,6 +151,9 @@ gtkui_get_curr_playlist_mod (void); void gtkui_trackinfochanged (DB_playItem_t *it); +gboolean +redraw_queued_tracks_cb (gpointer plt); + extern DB_playItem_t * (*gtkui_original_plt_load) (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data); #endif diff --git a/plugins/gtkui/gtkui_api.h b/plugins/gtkui/gtkui_api.h index 7fa7944f..30bf7090 100644 --- a/plugins/gtkui/gtkui_api.h +++ b/plugins/gtkui/gtkui_api.h @@ -25,9 +25,105 @@ #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; + char *name; + + struct ddb_gtkui_widget_s *parent; + + GtkWidget *widget; + + uint32_t flags; + + // all the functions here are overloads, so they are not mandatory + // they can be implemented to add custom code to normal widget code + // they can be NULL if you don't need them, or you can set them to + // standard functions (more below) + + // this function will be called after the widget is visible and needs to + // [re]initialize itself + // e.g. splitter widget sets the grip position in the init + void (*init) (struct ddb_gtkui_widget_s *container); + + // save your custom parameters in the string using strncat + // for example, if you need to write width and height: + // strncat (s, "100 200", sz); + void (*save) (struct ddb_gtkui_widget_s *w, char *s, int sz); + + // this is to read custom widget parameters, e.g. width and height + // you will be passed a string looking like "100 200 {" + // you will need to read params, and return the new pointer, normally it + // should be pointing to the "{" + const char *(*load) (struct ddb_gtkui_widget_s *w, const char *s); + + // custom destructor code + void (*destroy) (struct ddb_gtkui_widget_s *w); + + // custom append code + // if left NULL, appending will not be supported + // you should use standard w_container_add if your widget is derived from + // GTK_CONTAINER + void (*append) (struct ddb_gtkui_widget_s *container, struct ddb_gtkui_widget_s *child); + + // custom remove code + // you should use w_container_remove if your widget is derived from + // GTK_CONTAINER + void (*remove) (struct ddb_gtkui_widget_s *container, struct ddb_gtkui_widget_s *child); + + // custom replace code + // default replace will call remove;destroy;append + // but you can override if you need smarter behaviour + // look at the splitter and tabs implementation for more details + void (*replace) (struct ddb_gtkui_widget_s *container, struct ddb_gtkui_widget_s *child, struct ddb_gtkui_widget_s *newchild); + + // implement this if you want to handle deadbeef broadcast messages/events + int (*message) (struct ddb_gtkui_widget_s *w, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2); + + // this will be called to setup the menu widget in design mode + void (*initmenu) (struct ddb_gtkui_widget_s *w, GtkWidget *menu); + + // you shouldn't touch this list normally, the system takes care of it + 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; + + // returns main window ptr GtkWidget * (*get_mainwin) (void); -} ddb_gtkui_t; + // register the new widget + void (*w_reg_widget) (const char *type, const char *title, ddb_gtkui_widget_t *(*create_func) (void)); + + // unregister the widget + void (*w_unreg_widget) (const char *type); + + // returns 1 if a widget of the specified is registered + int (*w_is_registered) (const char *type); + + // returns toplevel widget + ddb_gtkui_widget_t * (*w_get_rootwidget) (void); + + // create a widget oof specified type + ddb_gtkui_widget_t * (*w_create) (const char *type); + + // set widget name + void (*w_set_name) (ddb_gtkui_widget_t *w, const char *name); + + // destroy the widget + void (*w_destroy) (ddb_gtkui_widget_t *w); + + // append the widget to container + void (*w_append) (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child); + + // replace existing child widget with another widget + void (*w_replace) (ddb_gtkui_widget_t *w, ddb_gtkui_widget_t *from, ddb_gtkui_widget_t *to); + + // remove the widget from its container + void (*w_remove) (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child); +} ddb_gtkui_t; #endif diff --git a/plugins/gtkui/gtkuigl.c b/plugins/gtkui/gtkuigl.c new file mode 100644 index 00000000..9d8df7da --- /dev/null +++ b/plugins/gtkui/gtkuigl.c @@ -0,0 +1,44 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2012 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 "../../deadbeef.h" +#include "gtkui.h" +#include "support.h" +#include "gtkuigl.h" + +static int gl_initialized; + +int +gtkui_gl_init (void) { + if (gl_initialized) { + return 0; + } + int argc = 1; + const char **argv = alloca (sizeof (char *) * argc); + argv[0] = "deadbeef"; + gboolean success = gdk_gl_init_check (&argc, (char ***)&argv); + if (!success) { + return -1; + } + return 0; +} + +void +gtkui_gl_free (void) { + // ??? +} diff --git a/plugins/gtkui/gtkuigl.h b/plugins/gtkui/gtkuigl.h new file mode 100644 index 00000000..17fda004 --- /dev/null +++ b/plugins/gtkui/gtkuigl.h @@ -0,0 +1,24 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2012 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 __GTKUIGL_H +#define __GTKUIGL_H + +#include <gtk/gtkgl.h> + +#endif diff --git a/plugins/gtkui/interface.c b/plugins/gtkui/interface.c index 84649092..7ccc50de 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; @@ -79,6 +79,7 @@ create_mainwin (void) GtkWidget *view_headers; GtkWidget *view_tabs; GtkWidget *view_eq; + GtkWidget *design_mode1; GtkWidget *Playback; GtkWidget *Playback_menu; GtkWidget *Order; @@ -102,16 +103,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; @@ -126,9 +127,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; @@ -162,9 +160,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); @@ -175,17 +173,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); @@ -223,9 +221,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); @@ -238,9 +236,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); @@ -271,9 +269,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); @@ -353,6 +351,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); @@ -371,16 +373,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)); @@ -399,11 +404,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)); @@ -450,9 +457,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); @@ -480,17 +487,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); @@ -585,27 +592,9 @@ create_mainwin (void) gtk_widget_set_can_focus(volumebar, FALSE); gtk_widget_set_can_default(volumebar, FALSE); - 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_set_can_focus(tabstrip, FALSE); - gtk_widget_set_can_default(tabstrip, FALSE); - - 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_set_can_focus(playlist, FALSE); - gtk_widget_set_can_default(playlist, FALSE); - 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); @@ -719,6 +708,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); @@ -793,12 +785,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"); @@ -806,18 +798,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"); @@ -837,6 +829,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"); @@ -858,16 +851,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"); @@ -882,9 +875,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 21661f6d..b781feaa 100644 --- a/plugins/gtkui/mainplaylist.c +++ b/plugins/gtkui/mainplaylist.c @@ -128,8 +128,8 @@ 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); @@ -159,6 +159,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, cairo_t *drawable, DdbListviewIter it, int x, int y, int width, int height) { @@ -254,6 +255,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, @@ -290,7 +299,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, @@ -332,8 +341,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)); @@ -350,9 +360,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 34cc67bc..1b25ca6b 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 09b65353..748bd90b 100644 --- a/plugins/gtkui/plcommon.c +++ b/plugins/gtkui/plcommon.c @@ -291,8 +291,7 @@ on_clear1_activate (GtkMenuItem *menuitem, { deadbeef->pl_clear (); deadbeef->pl_save_all (); - main_refresh (); - search_refresh (); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } void @@ -301,8 +300,7 @@ on_remove1_activate (GtkMenuItem *menuitem, { int cursor = deadbeef->pl_delete_selected (); deadbeef->pl_save_all (); - main_refresh (); - search_refresh (); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } @@ -310,22 +308,18 @@ void on_crop1_activate (GtkMenuItem *menuitem, gpointer user_data) { - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); deadbeef->pl_crop_selected (); deadbeef->pl_save_all (); - 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 (); deadbeef->pl_save_all (); - main_refresh (); - search_refresh (); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } void @@ -362,8 +356,7 @@ on_remove_from_disk_activate (GtkMenuItem *menuitem, deadbeef->pl_save_all (); deadbeef->pl_unlock (); - main_refresh (); - search_refresh (); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } void @@ -717,6 +710,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]; @@ -1049,14 +1052,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 3c24f00f..f5c04e35 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 6f447792..81bb49e1 100644 --- a/plugins/gtkui/prefwin.c +++ b/plugins/gtkui/prefwin.c @@ -875,8 +875,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); } @@ -891,8 +892,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); } @@ -907,8 +909,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 @@ -922,8 +925,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 @@ -937,8 +941,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 @@ -1099,8 +1104,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 662839bf..87344e39 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 590bf1fa..ff061cd9 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_raw, equals_ptr); + int n = get_field_value (val + ml, sizeof (val) - ml, key, deadbeef->pl_find_meta_raw, 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 8b2b6311..ef1e530c 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..50b6eabf --- /dev/null +++ b/plugins/gtkui/widgets.c @@ -0,0 +1,1831 @@ +/* + 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 <GL/gl.h> +#include <GL/glu.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" +#include "gtkuigl.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; + int position; +} 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; + DdbListview *list; +} w_playlist_t; + +typedef struct { + ddb_gtkui_widget_t base; + GtkWidget *drawarea; +} w_placeholder_t; + +typedef struct { + ddb_gtkui_widget_t base; + int clicked_page; +} 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; + +typedef struct { + ddb_gtkui_widget_t base; + GtkWidget *drawarea; + guint drawtimer; + GdkGLContext *glcontext; +} w_scope_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_save (); + 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); + } + if (child->init) { + child->init (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; +} + +gboolean +w_init_cb (void *data) { + ddb_gtkui_widget_t *w = data; + if (w->init) { + w->init (w); + } + return FALSE; +} + +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); + if (to->init) { + g_idle_add (w_init_cb, 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); + } + + // name + s = gettoken (s, t); + if (!s) { + w_destroy (w); + return NULL; + } + if (t[0]) { + w_set_name (w, t); + } + + // load widget params + if (w->load) { + s = w->load (w, s); + if (!s) { + w_destroy (w); + return NULL; + } + } + + // { + 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; + +static gboolean +w_draw_event (GtkWidget *widget, cairo_t *cr, gpointer user_data) { + if (hidden && user_data == current_widget) { + cairo_set_source_rgb (cr, 0.17f, 0, 0.83f); + GtkAllocation allocation; + gtk_widget_get_allocation(widget, &allocation); + + if (!gtk_widget_get_has_window (widget)) { + cairo_reset_clip (cr); + cairo_rectangle (cr, allocation.x, allocation.y, allocation.width, allocation.height); + } + else { + cairo_reset_clip (cr); + cairo_rectangle (cr, 0, 0, allocation.width, allocation.height); + } + cairo_fill (cr); + } + return FALSE; +} + +gboolean +w_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { + cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (widget)); + gboolean res = w_draw_event (widget, cr, user_data); + cairo_destroy (cr); + return res; +} + +static char paste_buffer[1000]; + +void +save_widget_to_string (char *str, int sz, ddb_gtkui_widget_t *w) { + strcat (str, w->type); + strcat (str, " \""); + strcat (str, w->name ? w->name : ""); + strcat (str, "\""); + if (w->save) { + w->save (w, str, sz); + } + strcat (str, " {"); + for (ddb_gtkui_widget_t *c = w->children; c; c = c->next) { + save_widget_to_string (str, sz, c); + } + strcat (str, "} "); +} + +void +w_save (void) { + char buf[4000] = ""; + save_widget_to_string (buf, sizeof (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, sizeof (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, sizeof (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) { + gtk_widget_hide (widget); +} + +void +show_widget (GtkWidget *widget, gpointer data) { + 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_set_app_paintable (w->widget, FALSE); + 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; + widget = current_widget->widget; + hidden = 1; + if (GTK_IS_CONTAINER (widget)) { + gtk_container_foreach (GTK_CONTAINER (widget), hide_widget, NULL); + } + gtk_widget_set_app_paintable (widget, TRUE); + 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); + + if (current_widget->initmenu) { + current_widget->initmenu (current_widget, menu); + } + + 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); +#if !GTK_CHECK_VERSION(3,0,0) + g_signal_connect ((gpointer) widget, "expose_event", G_CALLBACK (w_expose_event), user_data); +#else + g_signal_connect ((gpointer) widget, "draw", G_CALLBACK (w_draw_event), user_data); +#endif + 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", type); + 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", type); +} + +int +w_is_registered (const char *type) { + // FIXME + return 0; +} + +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_set_name (ddb_gtkui_widget_t *w, const char *name) { + if (w->name) { + free (w->name); + w->name = NULL; + } + if (name) { + w->name = strdup (name); + } +} + +void +w_destroy (ddb_gtkui_widget_t *w) { + if (w->destroy) { + w->destroy (w); + } + if (w->widget) { + gtk_widget_destroy (w->widget); + } + if (w->name) { + free (w->name); + } + 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_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data) { + 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); + GtkAllocation a; + gtk_widget_get_allocation (widget, &a); + cairo_rectangle (cr, 0, 0, a.width, a.height); + cairo_paint (cr); + cairo_surface_destroy (checker); + return FALSE; +} + +gboolean +w_placeholder_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { + cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (widget)); + gboolean res = w_placeholder_draw (widget, cr, user_data); + cairo_destroy (cr); + return res; +} + +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_event_box_new (); + w->drawarea = gtk_drawing_area_new (); + gtk_widget_show (w->drawarea); + gtk_container_add (GTK_CONTAINER (w->base.widget), w->drawarea); + +#if !GTK_CHECK_VERSION(3,0,0) + g_signal_connect_after ((gpointer) w->drawarea, "expose_event", G_CALLBACK (w_placeholder_expose_event), w); +#else + g_signal_connect_after ((gpointer) w->drawarea, "draw", G_CALLBACK (w_placeholder_draw), w); +#endif + w_override_signals (w->base.widget, w); + return (ddb_gtkui_widget_t*)w; +} + +// common splitter funcs +const char * +w_splitter_load (struct ddb_gtkui_widget_s *w, const char *s) { + char t[MAX_TOKEN]; + s = gettoken (s, t); + if (!s) { + return NULL; + } + ((w_splitter_t *)w)->position = atoi (t); + return s; +} + +void +w_splitter_save (struct ddb_gtkui_widget_s *w, char *s, int sz) { + int pos = gtk_paned_get_position (GTK_PANED(w->widget)); + char spos[10]; + snprintf (spos, sizeof (spos), " %d", pos); + strncat (s, spos, sz); +} + +void +w_splitter_add (ddb_gtkui_widget_t *w, ddb_gtkui_widget_t *child) { + w_container_add (w, child); + gtk_paned_set_position (GTK_PANED(w->widget), ((w_splitter_t *)w)->position); +} + +////// 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; + } +} + +void +w_vsplitter_init (ddb_gtkui_widget_t *w) { + int pos = ((w_splitter_t *)w)->position; + if (pos == -1) { + GtkAllocation a; + gtk_widget_get_allocation (w->widget, &a); + pos = a.height/2; + } + gtk_paned_set_position (GTK_PANED(w->widget), pos); +} + +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->position = -1; + w->base.widget = gtk_vpaned_new (); + w->base.append = w_splitter_add; + w->base.remove = w_container_remove; + w->base.replace = w_splitter_replace; + w->base.load = w_splitter_load; + w->base.save = w_splitter_save; + w->base.init = w_vsplitter_init; + + ddb_gtkui_widget_t *ph1, *ph2; + ph1 = w_create ("placeholder"); + ph2 = w_create ("placeholder"); +#if !GTK_CHECK_VERSION(3,0,0) + g_signal_connect ((gpointer) w->base.widget, "expose_event", G_CALLBACK (w_expose_event), w); +#else + g_signal_connect ((gpointer) w->base.widget, "draw", G_CALLBACK (w_draw_event), w); +#endif + 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 +void +w_hsplitter_init (ddb_gtkui_widget_t *w) { + int pos = ((w_splitter_t *)w)->position; + if (pos == -1) { + GtkAllocation a; + gtk_widget_get_allocation (w->widget, &a); + pos = a.width/2; + } + gtk_paned_set_position (GTK_PANED(w->widget), pos); +} + +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->position = -1; + w->base.widget = gtk_hpaned_new (); + w->base.append = w_splitter_add; + w->base.remove = w_container_remove; + w->base.replace = w_splitter_replace; + w->base.load = w_splitter_load; + w->base.save = w_splitter_save; + w->base.init = w_hsplitter_init; + + ddb_gtkui_widget_t *ph1, *ph2; + ph1 = w_create ("placeholder"); + ph2 = w_create ("placeholder"); +#if !GTK_CHECK_VERSION(3,0,0) + g_signal_connect ((gpointer) w->base.widget, "expose_event", G_CALLBACK (w_expose_event), w); +#else + g_signal_connect ((gpointer) w->base.widget, "draw", G_CALLBACK (w_draw_event), w); +#endif + 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 +static gboolean +tab_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data); + +static void +on_remove_tab_activate (GtkMenuItem *menuitem, gpointer user_data) { + w_tabs_t *w = user_data; + + int i = 0; + for (ddb_gtkui_widget_t *c = w->base.children; c; c = c->next, i++) { + if (i == w->clicked_page) { + w_remove ((ddb_gtkui_widget_t *)w, c); + return; + } + } +} + +static void +on_add_tab_activate (GtkMenuItem *menuitem, gpointer user_data) { + w_tabs_t *w = user_data; + + ddb_gtkui_widget_t *ph; + ph = w_create ("placeholder"); + w_append ((ddb_gtkui_widget_t*)w, ph); + + int i = 0; + for (ddb_gtkui_widget_t *c = w->base.children; c; c = c->next, i++); + w->clicked_page = i-1; + gtk_notebook_set_current_page (GTK_NOTEBOOK (w->base.widget), w->clicked_page); + +} + +static void +on_move_tab_left_activate (GtkMenuItem *menuitem, gpointer user_data) { + w_tabs_t *w = user_data; + if (w->clicked_page <= 0) { + return; + } + + // remove and save widget + int i = 0; + ddb_gtkui_widget_t *newchild = NULL; + ddb_gtkui_widget_t *prev = NULL; + for (ddb_gtkui_widget_t *c = w->base.children; c; c = c->next, i++) { + if (i == w->clicked_page) { + char buf[4000] = ""; + save_widget_to_string (buf, sizeof (buf), c); + w_create_from_string (buf, &newchild); + + w_remove ((ddb_gtkui_widget_t *)w, c); + break; + } + } + if (!newchild) { + return; + } + + // add new child at new position + i = 0; + prev = NULL; + for (ddb_gtkui_widget_t *c = w->base.children; c; c = c->next, i++) { + if (i == w->clicked_page-1) { + if (prev) { + newchild->next = prev->next; + prev->next = newchild; + } + else { + newchild->next = w->base.children; + w->base.children = newchild; + } + GtkWidget *eventbox = gtk_event_box_new (); + GtkWidget *label = gtk_label_new (newchild->type); + gtk_widget_show (eventbox); + g_object_set_data (G_OBJECT (eventbox), "owner", w); + g_signal_connect ((gpointer) eventbox, "button_press_event", G_CALLBACK (tab_button_press_event), newchild->widget); + gtk_widget_show (label); + gtk_container_add (GTK_CONTAINER (eventbox), label); + gtk_widget_show (newchild->widget); + + gtk_notebook_insert_page (GTK_NOTEBOOK (w->base.widget), newchild->widget, eventbox, w->clicked_page-1); + gtk_notebook_set_current_page (GTK_NOTEBOOK (w->base.widget), w->clicked_page-1); + w->clicked_page--; + break; + } + prev = c; + } +} + +static void +on_move_tab_right_activate (GtkMenuItem *menuitem, gpointer user_data) { + w_tabs_t *w = user_data; + + int i = 0; + for (ddb_gtkui_widget_t *c = w->base.children; c; c = c->next, i++); + if (w->clicked_page >= i) + return; + + gtk_notebook_set_current_page (GTK_NOTEBOOK (w->base.widget), ++w->clicked_page); + on_move_tab_left_activate (menuitem, user_data); + gtk_notebook_set_current_page (GTK_NOTEBOOK (w->base.widget), ++w->clicked_page); +} + +static gboolean +tab_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data) { + if (event->button != 3) { + return FALSE; + } + // user_data is child widget + if (design_mode) { + w_tabs_t *w = (w_tabs_t *)g_object_get_data (G_OBJECT (widget), "owner"); + GtkWidget *menu; + GtkWidget *item; + menu = gtk_menu_new (); + + item = gtk_menu_item_new_with_mnemonic (_("Move tab left")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_move_tab_left_activate), + w); + + item = gtk_menu_item_new_with_mnemonic (_("Move tab right")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_move_tab_right_activate), + w); + + item = gtk_menu_item_new_with_mnemonic (_("Remove tab")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_remove_tab_activate), + w); + + item = gtk_menu_item_new_with_mnemonic (_("Rename tab")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + + w->clicked_page = gtk_notebook_page_num (GTK_NOTEBOOK (w->base.widget), user_data); + gtk_notebook_set_current_page (GTK_NOTEBOOK (w->base.widget), w->clicked_page); + + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, widget, 0, gtk_get_current_event_time()); + return TRUE; + } + return FALSE; +} + +void +w_tabs_add (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child) { + GtkWidget *eventbox = gtk_event_box_new (); + GtkWidget *label = gtk_label_new (child->type); + gtk_widget_show (eventbox); + g_object_set_data (G_OBJECT (eventbox), "owner", cont); + g_signal_connect ((gpointer) eventbox, "button_press_event", G_CALLBACK (tab_button_press_event), child->widget); + gtk_widget_show (label); + gtk_container_add (GTK_CONTAINER (eventbox), label); + gtk_widget_show (child->widget); + gtk_notebook_append_page (GTK_NOTEBOOK (cont->widget), child->widget, eventbox); +} + +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; prev = 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_notebook_remove_page (GTK_NOTEBOOK(cont->widget), ntab); + c->widget = NULL; + w_destroy (c); + GtkWidget *eventbox = gtk_event_box_new (); + GtkWidget *label = gtk_label_new (newchild->type); + gtk_widget_show (eventbox); + g_object_set_data (G_OBJECT (eventbox), "owner", cont); + g_signal_connect ((gpointer) eventbox, "button_press_event", G_CALLBACK (tab_button_press_event), newchild->widget); + gtk_widget_show (label); + gtk_container_add (GTK_CONTAINER (eventbox), label); + gtk_widget_show (newchild->widget); + int pos = gtk_notebook_insert_page (GTK_NOTEBOOK (cont->widget), newchild->widget, eventbox, ntab); + gtk_notebook_set_current_page (GTK_NOTEBOOK (cont->widget), pos); + break; + } + } +} + +void +w_tabs_initmenu (struct ddb_gtkui_widget_s *w, GtkWidget *menu) { + GtkWidget *item; + item = gtk_menu_item_new_with_mnemonic (_("Add new tab")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_add_tab_activate), + w); +} + +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; + w->base.initmenu = w_tabs_initmenu; + + ddb_gtkui_widget_t *ph1, *ph2, *ph3; + ph1 = w_create ("placeholder"); + ph2 = w_create ("placeholder"); + ph3 = w_create ("placeholder"); + +#if !GTK_CHECK_VERSION(3,0,0) + g_signal_connect ((gpointer) w->base.widget, "expose_event", G_CALLBACK (w_expose_event), w); +#else + g_signal_connect ((gpointer) w->base.widget, "draw", G_CALLBACK (w_draw_event), w); +#endif + 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 +tabbed_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 +trackinfochanged_cb (gpointer data) { + w_trackdata_t *d = data; + w_playlist_t *p = (w_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 (DDB_LISTVIEW (p->list), idx, (DdbListviewIter)d->trk); + } + deadbeef->plt_unref (plt); + } + if (d->trk) { + deadbeef->pl_item_unref (d->trk); + } + free (d); + return FALSE; +} + +static gboolean +tabbed_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 +paused_cb (gpointer data) { + w_playlist_t *p = (w_playlist_t *)data; + DB_playItem_t *curr = deadbeef->streamer_get_playing_track (); + if (curr) { + int idx = deadbeef->pl_get_idx_of (curr); + ddb_listview_draw_row (p->list, idx, (DdbListviewIter)curr); + deadbeef->pl_item_unref (curr); + } + return FALSE; +} + +static gboolean +tabbed_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 +refresh_cb (gpointer data) { + w_playlist_t *p = (w_playlist_t *)data; + ddb_listview_clear_sort (DDB_LISTVIEW (p->list)); + ddb_listview_refresh (DDB_LISTVIEW (p->list), DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); + return FALSE; +} + +static gboolean +tabbed_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; +} + +static gboolean +playlistswitch_cb (gpointer data) { + w_playlist_t *p = (w_playlist_t *)data; + 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); + 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 (DDB_LISTVIEW (p->list), DDB_LIST_CHANGED | DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); + ddb_listview_set_vscroll (DDB_LISTVIEW (p->list), scroll); + return FALSE; +} + +struct fromto_t { + ddb_gtkui_widget_t *w; + DB_playItem_t *from; + DB_playItem_t *to; +}; + +static gboolean +tabbed_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 gboolean +songchanged_cb (gpointer data) { + struct fromto_t *ft = data; + DB_playItem_t *from = ft->from; + DB_playItem_t *to = ft->to; + w_playlist_t *p = (w_playlist_t *)ft->w; + int to_idx = -1; + if (!ddb_listview_is_scrolling (DDB_LISTVIEW (p->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 (DDB_LISTVIEW (p->list), to_idx); + } + if (scroll_follows_playback && plt == deadbeef->plt_get_curr_idx ()) { + ddb_listview_scroll_to (DDB_LISTVIEW (p->list), to_idx); + } + } + } + } + + if (from) { + int idx = deadbeef->pl_get_idx_of (from); + if (idx != -1) { + ddb_listview_draw_row (DDB_LISTVIEW (p->list), idx, from); + } + } + if (to && to_idx != -1) { + ddb_listview_draw_row (DDB_LISTVIEW (p->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 gboolean +tabbed_trackfocus_cb (gpointer p) { + w_tabbed_playlist_t *tp = p; + DB_playItem_t *it = deadbeef->streamer_get_playing_track (); + if (it) { + int idx = deadbeef->pl_get_idx_of (it); + if (idx != -1) { + ddb_listview_scroll_to (tp->list, idx); + ddb_listview_set_cursor (tp->list, idx); + } + deadbeef->pl_item_unref (it); + } + + return FALSE; +} + +static gboolean +trackfocus_cb (gpointer p) { + w_playlist_t *tp = p; + DB_playItem_t *it = deadbeef->streamer_get_playing_track (); + if (it) { + int idx = deadbeef->pl_get_idx_of (it); + if (idx != -1) { + ddb_listview_scroll_to (tp->list, idx); + ddb_listview_set_cursor (tp->list, idx); + } + deadbeef->pl_item_unref (it); + } + + 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 (tabbed_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 (tabbed_trackinfochanged_cb, d); + } + break; + case DB_EV_PAUSED: + g_idle_add (tabbed_paused_cb, w); + break; + case DB_EV_PLAYLISTCHANGED: + g_idle_add (tabbed_refresh_cb, w); + break; + case DB_EV_PLAYLISTSWITCHED: + g_idle_add (tabbed_playlistswitch_cb, w); + break; + case DB_EV_TRACKFOCUSCURRENT: + g_idle_add (tabbed_trackfocus_cb, w); + break; + } + return 0; +} + +static int +w_playlist_message (ddb_gtkui_widget_t *w, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { + w_playlist_t *p = (w_playlist_t *)w; + switch (id) { + case DB_EV_SONGCHANGED: + g_idle_add (redraw_queued_tracks_cb, p->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; + case DB_EV_TRACKFOCUSCURRENT: + g_idle_add (trackfocus_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_set_can_focus (tabstrip, FALSE); + gtk_widget_set_can_default (tabstrip, FALSE); + + 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_override_signals (w->base.widget, 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 = gtk_event_box_new (); + w->list = DDB_LISTVIEW (ddb_listview_new ()); + gtk_widget_show (GTK_WIDGET (w->list)); + main_playlist_init (GTK_WIDGET (w->list)); + if (deadbeef->conf_get_int ("gtkui.headers.visible", 1)) { + ddb_listview_show_header (DDB_LISTVIEW (w->list), 1); + } + else { + ddb_listview_show_header (DDB_LISTVIEW (w->list), 0); + } + + gtk_container_add (GTK_CONTAINER (w->base.widget), GTK_WIDGET (w->list)); + w_override_signals (w->base.widget, w); + w->base.message = w_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); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w->base.widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + 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_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data) { + DB_playItem_t *it = deadbeef->streamer_get_playing_track (); + if (!it) { + return FALSE; + } + GtkAllocation a; + gtk_widget_get_allocation (widget, &a); + int width = a.width; + int height = a.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_cairo_set_source_pixbuf (cr, pixbuf, 0, 0); + cairo_rectangle (cr, 0, 0, pw, ph); + cairo_fill (cr); +// gdk_draw_pixbuf (gtk_widget_get_window (widget), widget->style->white_gc, pixbuf, 0, 0, a.width/2-pw/2, a.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_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { + cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (widget)); + gboolean res = coverart_draw (widget, cr, user_data); + cairo_destroy (cr); + return res; +} + +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); +#if !GTK_CHECK_VERSION(3,0,0) + g_signal_connect_after ((gpointer) w->drawarea, "expose_event", G_CALLBACK (coverart_expose_event), w); +#else + g_signal_connect_after ((gpointer) w->drawarea, "draw", G_CALLBACK (coverart_draw), w); +#endif + w_override_signals (w->base.widget, w); + return (ddb_gtkui_widget_t *)w; +} + +///// scope vis +void +w_scope_destroy (ddb_gtkui_widget_t *w) { + w_scope_t *s = (w_scope_t *)w; + if (s->drawtimer) { + g_source_remove (s->drawtimer); + s->drawtimer = 0; + } + if (s->glcontext) { + gdk_gl_context_destroy (s->glcontext); + s->glcontext = NULL; + } +} + +gboolean +w_scope_draw_cb (void *data) { + w_scope_t *s = data; + gtk_widget_queue_draw (s->drawarea); + return TRUE; +} + +gboolean +scope_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data) { + ddb_waveformat_t fmt; + char data[DDB_AUDIO_MEMORY_BUFFER_SIZE]; + int size = deadbeef->audio_get_waveform_data (&fmt, data); + if (fmt.channels && size > 0) { + cairo_set_source_rgb (cr, 0, 0, 0); + cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); + cairo_set_line_width (cr, 1); + GtkAllocation a; + gtk_widget_get_allocation (widget, &a); + short *samples = (short *)data; + + int nframes = size / (fmt.bps/8*fmt.channels); + float incr = nframes / (float)a.width; + float pos = 0; + for (int x = 0; x < a.width; x++, pos += incr) { + short s = max (samples[(int)pos*2], samples[(int)pos*2+1]); + cairo_line_to (cr, x, s * a.height/2 / 0x7fff + a.height/2); + } + cairo_stroke (cr); + } + + return FALSE; +} + +gboolean +scope_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { + w_scope_t *w = user_data; + ddb_waveformat_t fmt; + char data[DDB_AUDIO_MEMORY_BUFFER_SIZE]; + int size = deadbeef->audio_get_waveform_data (&fmt, data); + if (fmt.channels && size > 0) { + GtkAllocation a; + gtk_widget_get_allocation (widget, &a); + + GdkGLDrawable *d = gtk_widget_get_gl_drawable (widget); + gdk_gl_drawable_gl_begin (d, w->glcontext); + + glClear (GL_COLOR_BUFFER_BIT); + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + gluOrtho2D(0,a.width,a.height,0); + glMatrixMode (GL_MODELVIEW); + glViewport (0, 0, a.width, a.height); + glBegin (GL_LINE_STRIP); + + short *samples = (short *)data; + + int nframes = size / (fmt.bps/8*fmt.channels); + float incr = nframes / (float)a.width; + float pos = 0; + for (int x = 0; x < a.width; x++, pos += incr) { + short s = max (samples[(int)pos*2], samples[(int)pos*2+1]); + glVertex2f (x, s * a.height/2 / 0x7fff + a.height/2); + } + + glEnd(); + gdk_gl_drawable_swap_buffers (d); + + gdk_gl_drawable_gl_end (d); + } + + return FALSE; + + cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (widget)); + gboolean res = scope_draw (widget, cr, user_data); + cairo_destroy (cr); + return res; +} + +void +w_scope_init (ddb_gtkui_widget_t *w) { + w_scope_t *s = (w_scope_t *)w; + if (s->drawtimer) { + g_source_remove (s->drawtimer); + } + s->drawtimer = g_timeout_add (33, w_scope_draw_cb, w); +} + +void +scope_realize (GtkWidget *widget, gpointer data) { + w_scope_t *w = data; + w->glcontext = gtk_widget_create_gl_context (w->drawarea, NULL, TRUE, GDK_GL_RGBA_TYPE); + printf ("ctx: %p\n", w->glcontext); +} + +ddb_gtkui_widget_t * +w_scope_create (void) { + w_scope_t *w = malloc (sizeof (w_scope_t)); + memset (w, 0, sizeof (w_scope_t)); + + w->base.widget = gtk_event_box_new (); + w->base.init = w_scope_init; + w->base.destroy = w_scope_destroy; + w->drawarea = gtk_drawing_area_new (); + int attrlist[] = {GDK_GL_ATTRIB_LIST_NONE}; + GdkGLConfig *conf = gdk_gl_config_new_by_mode ((GdkGLConfigMode)(GDK_GL_MODE_RGB | + GDK_GL_MODE_DOUBLE)); + printf ("conf: %p\n", conf); + gboolean cap = gtk_widget_set_gl_capability (w->drawarea, conf, NULL, TRUE, GDK_GL_RGBA_TYPE); + printf ("cap: %d\n", cap); + gtk_widget_show (w->drawarea); + gtk_container_add (GTK_CONTAINER (w->base.widget), w->drawarea); +#if !GTK_CHECK_VERSION(3,0,0) + g_signal_connect_after ((gpointer) w->drawarea, "expose_event", G_CALLBACK (scope_expose_event), w); +#else + g_signal_connect_after ((gpointer) w->drawarea, "draw", G_CALLBACK (scope_draw), w); +#endif + g_signal_connect_after (G_OBJECT (w->drawarea), "realize", G_CALLBACK (scope_realize), 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..27e8c013 --- /dev/null +++ b/plugins/gtkui/widgets.h @@ -0,0 +1,101 @@ +/* + 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); + +void +w_save (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); + +int +w_is_registered (const char *type); + +ddb_gtkui_widget_t * +w_create (const char *type); + +void +w_set_name (ddb_gtkui_widget_t *w, const char *name); + +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_replace (ddb_gtkui_widget_t *w, ddb_gtkui_widget_t *from, ddb_gtkui_widget_t *to); + +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); + +ddb_gtkui_widget_t * +w_scope_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); +} |