diff options
author | Simon Quigley <tsimonq2@ubuntu.com> | 2017-08-24 08:12:18 -0500 |
---|---|---|
committer | Simon Quigley <tsimonq2@ubuntu.com> | 2017-08-24 08:12:18 -0500 |
commit | 4de0719ae55d72d5c45952a2b54136f96d462ba6 (patch) | |
tree | f0aea4a55fd3f0e36cc7e9a8860fb9fdcc55d1f2 /src | |
parent | 5d911895bec79b393b4b47f09fac3a29d8ebb1c1 (diff) |
New upstream version 3.9
Diffstat (limited to 'src')
110 files changed, 3024 insertions, 2193 deletions
diff --git a/src/adplug/Makefile b/src/adplug/Makefile index 742c9e4..0cc004e 100644 --- a/src/adplug/Makefile +++ b/src/adplug/Makefile @@ -64,5 +64,5 @@ plugindir := ${plugindir}/${INPUT_PLUGIN_DIR} LD = ${CXX} CFLAGS += ${PLUGIN_CFLAGS} # FIXME: Turning off warnings for now; this code is awful -CXXFLAGS += ${PLUGIN_CFLAGS} -Wno-sign-compare +CXXFLAGS += ${PLUGIN_CFLAGS} -Wno-sign-compare -Wno-shift-negative-value CPPFLAGS += ${PLUGIN_CPPFLAGS} -I../.. -I./core -I./binio diff --git a/src/adplug/core/adlibemu.cc b/src/adplug/core/adlibemu.cc index a5ecf36..6554d97 100644 --- a/src/adplug/core/adlibemu.cc +++ b/src/adplug/core/adlibemu.cc @@ -105,25 +105,9 @@ static float *rptr[9], *nrptr[9]; static float rbuf[9][FIFOSIZ*2]; static float snd[FIFOSIZ*2]; -#ifndef USING_ASM -#define _inline -#endif - -#ifdef USING_ASM -static _inline void ftol (float f, long *a) -{ - _asm - { - mov eax, a - fld f - fistp dword ptr [eax] - } -} -#else static void ftol(float f, long *a) { *a=f; } -#endif #define ctc ((celltype *)c) //A rare attempt to make code easier to read! void docell4 (void *c, float modulator) { } @@ -382,42 +366,6 @@ void adlib0 (long i, long v) //outdata(i,v); } -#ifdef USING_ASM -static long fpuasm; -static float fakeadd = 8388608.0+128.0; -static _inline void clipit8 (float f, long a) -{ - _asm - { - mov edi, a - fld dword ptr f - fadd dword ptr fakeadd - fstp dword ptr fpuasm - mov eax, fpuasm - test eax, 0x007fff00 - jz short skipit - shr eax, 16 - xor eax, -1 - skipit: mov byte ptr [edi], al - } -} - -static _inline void clipit16 (float f, long a) -{ - _asm - { - mov eax, a - fld dword ptr f - fist word ptr [eax] - cmp word ptr [eax], 0x8000 - jne short skipit2 - fst dword ptr [fpuasm] - cmp fpuasm, 0x80000000 - sbb word ptr [eax], 0 - skipit2: fstp st - } -} -#else static void clipit8(float f,unsigned char *a) { f/=256.0; f+=128.0; @@ -431,7 +379,6 @@ static void clipit16(float f,short *a) { else if (f<-32767.5) *a=-32768; else *a=f; } -#endif void adlibsetvolume(int i) { AMPSCALE=i; diff --git a/src/adplug/core/protrack.cc b/src/adplug/core/protrack.cc index b652e1f..cb6da57 100644 --- a/src/adplug/core/protrack.cc +++ b/src/adplug/core/protrack.cc @@ -205,7 +205,7 @@ bool CmodPlayer::update() for(chan = 0; chan < nchans; chan++) { oplchan = set_opl_chip(chan); - if(!(activechan >> (31 - chan)) & 1) { // channel active? + if(!((activechan >> (31 - chan)) & 1)) { // channel active? AdPlug_LogWrite("N/A|"); continue; } diff --git a/src/albumart/albumart.cc b/src/albumart/albumart.cc index 45fc0bb..676ab89 100644 --- a/src/albumart/albumart.cc +++ b/src/albumart/albumart.cc @@ -44,15 +44,12 @@ EXPORT AlbumArtPlugin aud_plugin_instance; static void album_update (void *, GtkWidget * widget) { - GdkPixbuf * pixbuf = audgui_pixbuf_request_current (); + AudguiPixbuf pixbuf = audgui_pixbuf_request_current (); if (! pixbuf) pixbuf = audgui_pixbuf_fallback (); - audgui_scaled_image_set (widget, pixbuf); - - if (pixbuf) - g_object_unref (pixbuf); + audgui_scaled_image_set (widget, pixbuf.get ()); } static void album_clear (void *, GtkWidget * widget) diff --git a/src/alsa/alsa.cc b/src/alsa/alsa.cc index 410f1ae..14884ab 100644 --- a/src/alsa/alsa.cc +++ b/src/alsa/alsa.cc @@ -296,6 +296,10 @@ static snd_pcm_format_t convert_aud_format (int aud_format) {FMT_S32_BE, SND_PCM_FORMAT_S32_BE}, {FMT_U32_LE, SND_PCM_FORMAT_U32_LE}, {FMT_U32_BE, SND_PCM_FORMAT_U32_BE}, + {FMT_S24_3LE, SND_PCM_FORMAT_S24_3LE}, + {FMT_S24_3BE, SND_PCM_FORMAT_S24_3BE}, + {FMT_U24_3LE, SND_PCM_FORMAT_U24_3LE}, + {FMT_U24_3BE, SND_PCM_FORMAT_U24_3BE}, }; for (auto & conv : table) @@ -326,7 +330,7 @@ bool ALSAPlugin::open_audio (int aud_format, int rate, int channels, String & er goto FAILED; } - AUDDBG ("Opening PCM device %s for %s, %d channels, %d Hz.\n", + AUDINFO ("Opening PCM device %s for %s, %d channels, %d Hz.\n", (const char *) pcm, snd_pcm_format_name (format), channels, rate); CHECK_STR (error, snd_pcm_open, & alsa_handle, pcm, SND_PCM_STREAM_PLAYBACK, 0); @@ -360,7 +364,7 @@ bool ALSAPlugin::open_audio (int aud_format, int rate, int channels, String & er CHECK_STR (error, snd_pcm_hw_params, alsa_handle, params); soft_buffer = aud::max (total_buffer / 2, total_buffer - hard_buffer); - AUDDBG ("Buffer: hardware %d ms, software %d ms, period %d ms.\n", + AUDINFO ("Buffer: hardware %d ms, software %d ms, period %d ms.\n", hard_buffer, soft_buffer, alsa_period); buffer_frames = aud::rescale<int64_t> (soft_buffer, 1000, rate); diff --git a/src/amidiplug/amidi-plug.cc b/src/amidiplug/amidi-plug.cc index c6de04a..9cdc3db 100644 --- a/src/amidiplug/amidi-plug.cc +++ b/src/amidiplug/amidi-plug.cc @@ -56,7 +56,7 @@ public: #ifdef USE_GTK bool file_info_box (const char * filename, VFSFile & file) - { i_fileinfo_gui (filename, file); return true; } + { return i_fileinfo_gui (filename, file); } #endif protected: diff --git a/src/amidiplug/i_fileinfo.cc b/src/amidiplug/i_fileinfo.cc index 1a2ee7e..507243f 100644 --- a/src/amidiplug/i_fileinfo.cc +++ b/src/amidiplug/i_fileinfo.cc @@ -103,8 +103,12 @@ void i_fileinfo_text_fill (midifile_t * mf, GtkTextBuffer * text_tb, GtkTextBuff } -void i_fileinfo_gui (const char * filename_uri, VFSFile & file) +bool i_fileinfo_gui (const char * filename_uri, VFSFile & file) { + // TODO: Qt support + if (aud_get_mainloop_type () != MainloopType::GLib) + return false; + static GtkWidget * fileinfowin = nullptr; GtkWidget * fileinfowin_vbox, *fileinfowin_columns_hbox; GtkWidget * midiinfoboxes_vbox, *miditextboxes_vbox, *miditextboxes_paned; @@ -122,13 +126,13 @@ void i_fileinfo_gui (const char * filename_uri, VFSFile & file) int bpm = 0, wavg_bpm = 0; if (fileinfowin) - return; + return true; midifile_t mf; /****************** midifile parser ******************/ if (! mf.parse_from_file (filename_uri, file)) - return; + return false; /* midifile is filled with information at this point, bpm information is needed too */ @@ -339,6 +343,8 @@ void i_fileinfo_gui (const char * filename_uri, VFSFile & file) gtk_widget_grab_focus (GTK_WIDGET (footer_bclose)); gtk_widget_show_all (fileinfowin); + + return true; } #endif // USE_GTK diff --git a/src/amidiplug/i_fileinfo.h b/src/amidiplug/i_fileinfo.h index 15749e5..b2bfb5a 100644 --- a/src/amidiplug/i_fileinfo.h +++ b/src/amidiplug/i_fileinfo.h @@ -23,6 +23,6 @@ class VFSFile; -void i_fileinfo_gui (const char * filename_uri, VFSFile & file); +bool i_fileinfo_gui (const char * filename_uri, VFSFile & file); #endif /* !_I_FILEINFO_H */ diff --git a/src/ampache/ampache.cc b/src/ampache/ampache.cc index a09f6b9..0e4c10b 100644 --- a/src/ampache/ampache.cc +++ b/src/ampache/ampache.cc @@ -43,10 +43,10 @@ public: }; const char AmpacheBrowserPlugin::about[] = - N_("Ampache Browser\n\n" - "Ampache client for Audacious.\n\n" - "License: GNU GPLv3\n" - "Copyright (C) Róbert Čerňanský and John Lindgren\n"); + N_("Ampache Browser for Audacious\n" + "http://ampache-browser.org/\n\n" + "Copyright (C) Róbert Čerňanský and John Lindgren\n" + "License: GNU GPLv3"); static SmartPtr<ampache_browser::ApplicationQt> s_app; @@ -108,11 +108,11 @@ bool AmpacheBrowserPlugin::init() }); browser.connectCreatePlaylist([](const UrlList& urls) { - aud_playlist_entry_insert_batch(aud_playlist_new(), -1, toAddItems(urls), true); + Playlist::new_playlist().insert_items(-1, toAddItems(urls), false); }); browser.connectAddToPlaylist([](const UrlList& urls) { - aud_playlist_entry_insert_batch(aud_playlist_get_active(), -1, toAddItems(urls), false); + Playlist::active_playlist().insert_items(-1, toAddItems(urls), false); }); initSettings(s_app->getSettings()); diff --git a/src/cdaudio/cdaudio-ng.cc b/src/cdaudio/cdaudio-ng.cc index 188b72a..58c4dbf 100644 --- a/src/cdaudio/cdaudio-ng.cc +++ b/src/cdaudio/cdaudio-ng.cc @@ -172,18 +172,18 @@ static void cdaudio_error (const char * message_format, ...) } /* main thread only */ -static void purge_playlist (int playlist) +static void purge_playlist (Playlist playlist) { - int length = aud_playlist_entry_count (playlist); + int length = playlist.n_entries (); - for (int count = 0; count < length; count ++) + for (int i = 0; i < length; i ++) { - String filename = aud_playlist_entry_get_filename (playlist, count); + String filename = playlist.entry_filename (i); if (! strncmp (filename, "cdda://", 7)) { - aud_playlist_entry_delete (playlist, count, 1); - count--; + playlist.remove_entry (i); + i--; length--; } } @@ -192,11 +192,10 @@ static void purge_playlist (int playlist) /* main thread only */ static void purge_all_playlists (void * = nullptr) { - int playlists = aud_playlist_count (); - int count; + int playlists = Playlist::n_playlists (); - for (count = 0; count < playlists; count++) - purge_playlist (count); + for (int i = 0; i < playlists; i++) + purge_playlist (Playlist::by_index (i)); } /* main thread only */ diff --git a/src/console/Makefile b/src/console/Makefile index c3714b8..fb75a52 100644 --- a/src/console/Makefile +++ b/src/console/Makefile @@ -61,6 +61,6 @@ plugindir := ${plugindir}/${INPUT_PLUGIN_DIR} LD = ${CXX} CFLAGS += ${PLUGIN_CFLAGS} -CXXFLAGS += ${PLUGIN_CFLAGS} +CXXFLAGS += ${PLUGIN_CFLAGS} -Wno-shift-negative-value CPPFLAGS += ${PLUGIN_CPPFLAGS} -I../.. LIBS += -lz diff --git a/src/delete-files/delete-files.cc b/src/delete-files/delete-files.cc index d3d408c..5472cbd 100644 --- a/src/delete-files/delete-files.cc +++ b/src/delete-files/delete-files.cc @@ -101,16 +101,16 @@ static void confirm_delete () { Index<String> files; - int playlist = aud_playlist_get_active (); - int entry_count = aud_playlist_entry_count (playlist); + auto playlist = Playlist::active_playlist (); + int entry_count = playlist.n_entries (); for (int i = 0; i < entry_count; i ++) { - if (aud_playlist_entry_get_selected (playlist, i)) - files.append (aud_playlist_entry_get_filename (playlist, i)); + if (playlist.entry_selected (i)) + files.append (playlist.entry_filename (i)); } - aud_playlist_delete_selected (playlist); + playlist.remove_selected (); for (const String & uri : files) { diff --git a/src/ffaudio/ffaudio-core.cc b/src/ffaudio/ffaudio-core.cc index 390160c..8eef1e5 100644 --- a/src/ffaudio/ffaudio-core.cc +++ b/src/ffaudio/ffaudio-core.cc @@ -662,6 +662,9 @@ const char * const FFaudio::exts[] = { /* True Audio */ "tta", + /* WebM */ + "webm", + /* end of table */ nullptr }; diff --git a/src/gtkui/Makefile b/src/gtkui/Makefile index cdfe244..238c788 100644 --- a/src/gtkui/Makefile +++ b/src/gtkui/Makefile @@ -2,13 +2,13 @@ PLUGIN = gtkui${PLUGIN_SUFFIX} SRCS = columns.cc \ layout.cc \ + menu-ops.cc \ menus.cc \ ui_infoarea.cc \ ui_gtk.cc \ ui_playlist_widget.cc \ ui_playlist_notebook.cc \ ui_statusbar.cc \ - playlist_util.cc \ settings.cc include ../../buildsys.mk diff --git a/src/gtkui/columns.cc b/src/gtkui/columns.cc index daf5eda..082582b 100644 --- a/src/gtkui/columns.cc +++ b/src/gtkui/columns.cc @@ -45,7 +45,8 @@ const char * const pw_col_names[PW_COLS] = { N_("File path"), N_("File name"), N_("Custom title"), - N_("Bitrate") + N_("Bitrate"), + N_("Comment") }; int pw_num_cols; @@ -66,7 +67,8 @@ static const char * const pw_col_keys[PW_COLS] = { "path", "filename", "custom", - "bitrate" + "bitrate", + "comment" }; static const int pw_default_widths[PW_COLS] = { @@ -83,7 +85,8 @@ static const int pw_default_widths[PW_COLS] = { 275, // path 275, // filename 275, // custom title - 10 // bitrate + 10, // bitrate + 275 // comment }; void pw_col_init () @@ -111,14 +114,13 @@ void pw_col_init () pw_cols[pw_num_cols ++] = i; } - String widths = aud_get_str ("gtkui", "column_widths"); + auto widths = str_list_to_index (aud_get_str ("gtkui", "column_widths"), ", "); + int nwidths = aud::min (widths.len (), (int) PW_COLS); - int iwidths[PW_COLS]; - bool valid = str_to_int_array (widths, iwidths, PW_COLS); - auto source = valid ? iwidths : pw_default_widths; - - for (int i = 0; i < PW_COLS; i ++) - pw_col_widths[i] = audgui_to_native_dpi (source[i]); + for (int i = 0; i < nwidths; i ++) + pw_col_widths[i] = audgui_to_native_dpi (str_to_int (widths[i])); + for (int i = nwidths; i < PW_COLS; i ++) + pw_col_widths[i] = audgui_to_native_dpi (pw_default_widths[i]); } struct Column { @@ -134,12 +136,12 @@ static void apply_changes () int cols = chosen.len (); g_return_if_fail (cols <= PW_COLS); - ui_playlist_notebook_empty (); + pl_notebook_purge (); for (pw_num_cols = 0; pw_num_cols < cols; pw_num_cols ++) pw_cols[pw_num_cols] = chosen[pw_num_cols].column; - ui_playlist_notebook_populate (); + pl_notebook_populate (); } static void get_value (void * user, int row, int column, GValue * value) diff --git a/src/gtkui/gtkui.h b/src/gtkui/gtkui.h index 08d40ed..39cde7b 100644 --- a/src/gtkui/gtkui.h +++ b/src/gtkui/gtkui.h @@ -23,6 +23,7 @@ #include <stdint.h> #include <gtk/gtk.h> +class Playlist; struct PluginPreferences; /* menus.c */ @@ -30,7 +31,7 @@ GtkWidget * make_menu_bar (GtkAccelGroup * accel); GtkWidget * make_menu_main (GtkAccelGroup * accel); GtkWidget * make_menu_rclick (GtkAccelGroup * accel); GtkWidget * make_menu_tab (GtkAccelGroup * accel); -extern int menu_tab_playlist_id; +extern Playlist menu_tab_playlist; /* settings.c */ extern const PluginPreferences gtkui_prefs; @@ -44,7 +45,7 @@ void show_hide_infoarea (); void show_hide_infoarea_vis (); void show_hide_statusbar (); void popup_menu_rclick (unsigned button, uint32_t time); -void popup_menu_tab (unsigned button, uint32_t time, int playlist); +void popup_menu_tab (unsigned button, uint32_t time, Playlist playlist); void activate_search_tool (); void activate_playlist_manager (); void update_step_size (); diff --git a/src/gtkui/layout.cc b/src/gtkui/layout.cc index 40b60b9..93a57f7 100644 --- a/src/gtkui/layout.cc +++ b/src/gtkui/layout.cc @@ -22,6 +22,7 @@ #include <gdk/gdkkeysyms.h> #include <gtk/gtk.h> +#define AUD_GLIB_INTEGRATION #include <libaudcore/i18n.h> #include <libaudcore/runtime.h> #include <libaudcore/plugins.h> @@ -195,9 +196,8 @@ static GtkWidget * vbox_new (GtkWidget * widget, const char * name) widget); GtkWidget * label = gtk_label_new (nullptr); - char * markup = g_markup_printf_escaped ("<small><b>%s</b></small>", name); + CharPtr markup (g_markup_printf_escaped ("<small><b>%s</b></small>", name)); gtk_label_set_markup ((GtkLabel *) label, markup); - g_free (markup); gtk_misc_set_alignment ((GtkMisc *) label, 0, 0); gtk_container_add ((GtkContainer *) ebox, label); @@ -563,7 +563,7 @@ void layout_save () Item * item = (Item *) node->data; g_return_if_fail (item && item->name); - char key[16], value[64]; + char key[32], value[64]; snprintf (key, sizeof key, "item%d_name", i); aud_set_str ("gtkui-layout", key, item->name); @@ -590,7 +590,7 @@ void layout_load () for (int i = 0; i < count; i ++) { - char key[16]; + char key[32]; snprintf (key, sizeof key, "item%d_name", i); String name = aud_get_str ("gtkui-layout", key); diff --git a/src/gtkui/menu-ops.cc b/src/gtkui/menu-ops.cc new file mode 100644 index 0000000..358ec47 --- /dev/null +++ b/src/gtkui/menu-ops.cc @@ -0,0 +1,2 @@ +#include "../ui-common/menu-ops.cc" +#include "../ui-common/menu-ops-gtk.cc" diff --git a/src/gtkui/menus.cc b/src/gtkui/menus.cc index 58e7414..4bc6430 100644 --- a/src/gtkui/menus.cc +++ b/src/gtkui/menus.cc @@ -32,7 +32,6 @@ #include <libaudgui/menu.h> #include "gtkui.h" -#include "playlist_util.h" #include "ui_playlist_notebook.h" #include "ui_playlist_widget.h" @@ -45,7 +44,7 @@ #define SHIFT_CTRL (GdkModifierType) (SHIFT | CTRL) #define NONE 0, (GdkModifierType) 0 -int menu_tab_playlist_id = -1; /* should really be stored in the menu somehow */ +Playlist menu_tab_playlist; /* should really be stored in the menu somehow */ static void open_files () { audgui_run_filebrowser (true); } static void open_url () { audgui_show_add_url_window (true); } @@ -56,42 +55,26 @@ static void configure_effects () { audgui_show_prefs_for_plugin_type (PluginType static void configure_output () { audgui_show_prefs_for_plugin_type (PluginType::Output); } static void configure_visualizations () { audgui_show_prefs_for_plugin_type (PluginType::Vis); } -static void pl_rename () { start_rename_playlist (aud_playlist_get_active ()); } -static void pl_close () { audgui_confirm_playlist_delete (aud_playlist_get_active ()); } +static void pl_rename () { start_rename_playlist (Playlist::active_playlist ()); } +static void pl_close () { audgui_confirm_playlist_delete (Playlist::active_playlist ()); } -static void pl_tab_play () -{ - int playlist = aud_playlist_by_unique_id (menu_tab_playlist_id); - if (playlist >= 0) - aud_playlist_play (playlist); -} +static void pl_tab_play () { menu_tab_playlist.start_playback (); } static void pl_tab_rename () { - int playlist = aud_playlist_by_unique_id (menu_tab_playlist_id); - if (playlist >= 0) - start_rename_playlist (playlist); + if (menu_tab_playlist.exists ()) + start_rename_playlist (menu_tab_playlist); } static void pl_tab_close () { - int playlist = aud_playlist_by_unique_id (menu_tab_playlist_id); - if (playlist >= 0) - audgui_confirm_playlist_delete (playlist); + if (menu_tab_playlist.exists ()) + audgui_confirm_playlist_delete (menu_tab_playlist); } static GtkWidget * get_services_main () { return audgui_get_plugin_menu (AudMenuID::Main); } static GtkWidget * get_services_pl () { return audgui_get_plugin_menu (AudMenuID::Playlist); } -static void toggle_record () -{ - if (! aud_drct_enable_record (aud_get_bool ("gtkui", "record"))) - { - aud_set_bool ("gtkui", "record", aud_drct_get_record_enabled ()); - hook_call ("gtkui set record", nullptr); - } -} - static const AudguiMenuItem file_items[] = { MenuCommand (N_("_Open Files ..."), "document-open", 'o', CTRL, open_files), MenuCommand (N_("Open _URL ..."), "folder-remote", 'l', CTRL, open_url), @@ -144,6 +127,7 @@ static const AudguiMenuItem sort_items[] = { MenuCommand (N_("By _Length"), nullptr, NONE, sort_length), MenuCommand (N_("By _File Path"), nullptr, NONE, sort_path), MenuCommand (N_("By _Custom Title"), nullptr, NONE, sort_custom_title), + MenuCommand (N_("By C_omment"), nullptr, NONE, sort_comment), MenuSep (), MenuCommand (N_("R_everse Order"), "view-sort-descending", NONE, sort_reverse), MenuCommand (N_("_Random Order"), nullptr, NONE, sort_random) @@ -160,6 +144,7 @@ static const AudguiMenuItem sort_sel_items[] = { MenuCommand (N_("By _Length"), nullptr, NONE, sort_sel_length), MenuCommand (N_("By _File Path"), nullptr, NONE, sort_sel_path), MenuCommand (N_("By _Custom Title"), nullptr, NONE, sort_sel_custom_title), + MenuCommand (N_("By C_omment"), nullptr, NONE, sort_sel_comment), MenuSep (), MenuCommand (N_("R_everse Order"), "view-sort-descending", NONE, sort_sel_reverse), MenuCommand (N_("_Random Order"), nullptr, NONE, sort_sel_random) @@ -174,7 +159,7 @@ static const AudguiMenuItem playlist_items[] = { MenuSub (N_("Remove _Duplicates"), "edit-copy", {dupe_items}), MenuCommand (N_("Remove _Unavailable Files"), "dialog-warning", NONE, pl_remove_failed), MenuSep (), - MenuCommand (N_("_New"), "document-new", 't', CTRL, (GCallback) aud_playlist_new), + MenuCommand (N_("_New"), "document-new", 't', CTRL, pl_new), MenuCommand (N_("Ren_ame ..."), "insert-text", GDK_KEY_F2, (GdkModifierType) 0, pl_rename), MenuCommand (N_("Remo_ve"), "edit-delete", 'w', CTRL, pl_close), MenuSep (), @@ -192,7 +177,7 @@ static const AudguiMenuItem output_items[] = { MenuCommand (N_("_Equalizer ..."), "multimedia-volume-control", 'e', CTRL, audgui_show_equalizer_window), MenuCommand (N_("E_ffects ..."), nullptr, NONE, configure_effects), MenuSep (), - MenuToggle (N_("_Record Stream"), nullptr, 'd', CTRL, "gtkui", "record", toggle_record, "gtkui set record"), + MenuToggle (N_("_Record Stream"), nullptr, 'd', CTRL, nullptr, "record", nullptr, "set record"), MenuCommand (N_("Audio _Settings ..."), "audio-card", NONE, configure_output) }; diff --git a/src/gtkui/playlist_util.cc b/src/gtkui/playlist_util.cc deleted file mode 100644 index 05f5587..0000000 --- a/src/gtkui/playlist_util.cc +++ /dev/null @@ -1,66 +0,0 @@ -/* - * playlist_util.c - * Copyright 2010-2011 Michał Lipski and John Lindgren - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#include <string.h> -#include <gtk/gtk.h> - -#include <libaudcore/audstrings.h> -#include <libaudcore/interface.h> -#include <libaudcore/playlist.h> -#include <libaudgui/libaudgui.h> -#include <libaudgui/list.h> - -#include "playlist_util.h" -#include "ui_playlist_notebook.h" - -#include "../ui-common/menu-ops.h" - -GtkWidget * playlist_get_treeview (int playlist) -{ - GtkWidget * page = gtk_notebook_get_nth_page (UI_PLAYLIST_NOTEBOOK, playlist); - - if (!page) - return nullptr; - - return (GtkWidget *) g_object_get_data ((GObject *) page, "treeview"); -} - -int playlist_count_selected_in_range (int list, int top, int length) -{ - int selected = 0; - - for (int count = 0; count < length; count ++) - { - if (aud_playlist_entry_get_selected (list, top + count)) - selected ++; - } - - return selected; -} - -void playlist_shift (int offset) -{ - int list = aud_playlist_get_active (); - int focus = aud_playlist_get_focus (list); - - if (focus < 0 || ! aud_playlist_entry_get_selected (list, focus)) - return; - - aud_playlist_shift (list, focus, offset); -} diff --git a/src/gtkui/playlist_util.h b/src/gtkui/playlist_util.h deleted file mode 100644 index 66ce5cb..0000000 --- a/src/gtkui/playlist_util.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * playlist_util.h - * Copyright 2010-2011 Michał Lipski and John Lindgren - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#ifndef __PLAYLISTUTIL_H__ -#define __PLAYLISTUTIL_H__ - -GtkWidget * playlist_get_treeview (int playlist); - -int playlist_count_selected_in_range (int list, int top, int length); -void playlist_shift (int offset); - -#endif diff --git a/src/gtkui/settings.cc b/src/gtkui/settings.cc index e459ecc..a21c85d 100644 --- a/src/gtkui/settings.cc +++ b/src/gtkui/settings.cc @@ -27,8 +27,8 @@ static void redisplay_playlists () { - ui_playlist_notebook_empty (); - ui_playlist_notebook_populate (); + pl_notebook_purge (); + pl_notebook_populate (); } static const PreferencesWidget gtkui_widgets[] = { diff --git a/src/gtkui/ui_gtk.cc b/src/gtkui/ui_gtk.cc index dacd9df..daee866 100644 --- a/src/gtkui/ui_gtk.cc +++ b/src/gtkui/ui_gtk.cc @@ -40,10 +40,8 @@ #include "ui_playlist_widget.h" #include "ui_infoarea.h" #include "ui_statusbar.h" -#include "playlist_util.h" -#include "../ui-common/menu-ops.cc" -#include "../ui-common/menu-ops-gtk.cc" +#include "../ui-common/menu-ops.h" static const char * const gtkui_defaults[] = { "infoarea_show_vis", "TRUE", @@ -57,7 +55,6 @@ static const char * const gtkui_defaults[] = { "autoscroll", "TRUE", "playlist_columns", "title artist album queued length", "playlist_headers", "TRUE", - "record", "FALSE", "show_remaining_time", "FALSE", "step_size", "5", @@ -491,11 +488,6 @@ static GtkWidget * markup_label_new (const char * str) return label; } -static void window_mapped_cb (GtkWidget * widget) -{ - gtk_widget_grab_focus (playlist_get_treeview (aud_playlist_get_active ())); -} - static gboolean window_keypress_cb (GtkWidget * widget, GdkEventKey * event) { switch (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK)) @@ -507,8 +499,8 @@ static gboolean window_keypress_cb (GtkWidget * widget, GdkEventKey * event) /* escape key returns focus to playlist */ if (event->keyval == GDK_KEY_Escape) { - if (! focused || ! gtk_widget_is_ancestor (focused, (GtkWidget *) UI_PLAYLIST_NOTEBOOK)) - gtk_widget_grab_focus (playlist_get_treeview (aud_playlist_get_active ())); + if (! focused || ! gtk_widget_is_ancestor (focused, pl_notebook)) + pl_notebook_grab_focus (); return false; } @@ -600,7 +592,7 @@ static gboolean playlist_keypress_cb (GtkWidget * widget, GdkEventKey * event) switch (event->keyval) { case GDK_KEY_Escape: - ui_playlist_notebook_position (aud::to_ptr (aud_playlist_get_active ()), nullptr); + pl_notebook_set_position (aud::to_ptr (Playlist::active_playlist ()), nullptr); return true; case GDK_KEY_Delete: pl_remove_selected (); @@ -636,6 +628,10 @@ static gboolean playlist_keypress_cb (GtkWidget * widget, GdkEventKey * event) static void update_toggles (void * = nullptr, void * = nullptr) { + gtk_widget_set_visible ((GtkWidget *) button_record, aud_drct_get_record_enabled ()); + + gtk_toggle_tool_button_set_active ((GtkToggleToolButton *) button_record, + aud_get_bool (nullptr, "record")); gtk_toggle_tool_button_set_active ((GtkToggleToolButton *) button_repeat, aud_get_bool (nullptr, "repeat")); gtk_toggle_tool_button_set_active ((GtkToggleToolButton *) button_shuffle, @@ -654,24 +650,7 @@ static void toggle_shuffle (GtkToggleToolButton * button) static void toggle_record (GtkToggleToolButton * button) { - if (! aud_drct_enable_record (gtk_toggle_tool_button_get_active (button))) - gtk_toggle_tool_button_set_active (button, aud_drct_get_record_enabled ()); -} - -static void record_toggled (void * = nullptr, void * = nullptr) -{ - bool supported = (bool) aud_drct_get_record_plugin (); - bool enabled = aud_drct_get_record_enabled (); - - gtk_widget_set_sensitive ((GtkWidget *) button_record, supported); - gtk_toggle_tool_button_set_active ((GtkToggleToolButton *) button_record, enabled); - - /* update menu item */ - if (enabled != aud_get_bool ("gtkui", "record")) - { - aud_set_bool ("gtkui", "record", enabled); - hook_call ("gtkui set record", nullptr); - } + aud_set_bool (nullptr, "record", gtk_toggle_tool_button_get_active (button)); } static void toggle_search_tool (GtkToggleToolButton * button) @@ -703,13 +682,14 @@ static void ui_hooks_associate () hook_associate ("playback pause", (HookFunction) pause_cb, nullptr); hook_associate ("playback unpause", (HookFunction) pause_cb, nullptr); hook_associate ("playback stop", (HookFunction) ui_playback_stop, nullptr); - hook_associate ("playlist update", ui_playlist_notebook_update, nullptr); - hook_associate ("playlist activate", ui_playlist_notebook_activate, nullptr); - hook_associate ("playlist set playing", ui_playlist_notebook_set_playing, nullptr); - hook_associate ("playlist position", ui_playlist_notebook_position, nullptr); + hook_associate ("playlist update", pl_notebook_update, nullptr); + hook_associate ("playlist activate", pl_notebook_activate, nullptr); + hook_associate ("playlist set playing", pl_notebook_set_playing, nullptr); + hook_associate ("playlist position", pl_notebook_set_position, nullptr); + hook_associate ("enable record", update_toggles, nullptr); + hook_associate ("set record", update_toggles, nullptr); hook_associate ("set shuffle", update_toggles, nullptr); hook_associate ("set repeat", update_toggles, nullptr); - hook_associate ("enable record", record_toggled, nullptr); hook_associate ("config save", (HookFunction) config_save, nullptr); } @@ -721,13 +701,14 @@ static void ui_hooks_disassociate () hook_dissociate ("playback pause", (HookFunction) pause_cb); hook_dissociate ("playback unpause", (HookFunction) pause_cb); hook_dissociate ("playback stop", (HookFunction) ui_playback_stop); - hook_dissociate ("playlist update", ui_playlist_notebook_update); - hook_dissociate ("playlist activate", ui_playlist_notebook_activate); - hook_dissociate ("playlist set playing", ui_playlist_notebook_set_playing); - hook_dissociate ("playlist position", ui_playlist_notebook_position); + hook_dissociate ("playlist update", pl_notebook_update); + hook_dissociate ("playlist activate", pl_notebook_activate); + hook_dissociate ("playlist set playing", pl_notebook_set_playing); + hook_dissociate ("playlist position", pl_notebook_set_position); + hook_dissociate ("enable record", update_toggles); + hook_dissociate ("set record", update_toggles); hook_dissociate ("set shuffle", update_toggles); hook_dissociate ("set repeat", update_toggles); - hook_dissociate ("enable record", record_toggled); hook_dissociate ("config save", (HookFunction) config_save); } @@ -815,16 +796,24 @@ bool GtkUI::init () aud_plugin_add_watch (search_tool, search_tool_toggled, nullptr); } - /* playback buttons */ + /* open/add buttons */ toolbar_button_add (toolbar, button_open_pressed, "document-open"); toolbar_button_add (toolbar, button_add_pressed, "list-add"); + + gtk_toolbar_insert ((GtkToolbar *) toolbar, gtk_separator_tool_item_new (), -1); + + /* playback buttons */ toolbar_button_add (toolbar, aud_drct_pl_prev, "media-skip-backward"); - toolbar_button_add (toolbar, aud_drct_pl_next, "media-skip-forward"); button_play = toolbar_button_add (toolbar, aud_drct_play_pause, "media-playback-start"); button_stop = toolbar_button_add (toolbar, aud_drct_stop, "media-playback-stop"); + toolbar_button_add (toolbar, aud_drct_pl_next, "media-skip-forward"); + button_record = toggle_button_new ("media-record", toggle_record); + gtk_widget_set_no_show_all ((GtkWidget *) button_record, true); gtk_toolbar_insert ((GtkToolbar *) toolbar, button_record, -1); + gtk_toolbar_insert ((GtkToolbar *) toolbar, gtk_separator_tool_item_new (), -1); + /* time slider and label */ GtkToolItem * boxitem1 = gtk_tool_item_new (); gtk_tool_item_set_expand (boxitem1, true); @@ -847,6 +836,8 @@ bool GtkUI::init () gtk_widget_set_no_show_all (slider, true); gtk_widget_set_no_show_all (label_time, true); + gtk_toolbar_insert ((GtkToolbar *) toolbar, gtk_separator_tool_item_new (), -1); + /* repeat and shuffle buttons */ button_repeat = toggle_button_new ("media-playlist-repeat", toggle_repeat); gtk_toolbar_insert ((GtkToolbar *) toolbar, button_repeat, -1); @@ -880,8 +871,7 @@ bool GtkUI::init () vbox = gtk_vbox_new (false, 6); layout_add_center (vbox); - ui_playlist_notebook_new (); - gtk_box_pack_start ((GtkBox *) vbox, (GtkWidget *) UI_PLAYLIST_NOTEBOOK, true, true, 0); + gtk_box_pack_start ((GtkBox *) vbox, pl_notebook_new (), true, true, 0); /* optional UI elements */ show_hide_menu (); @@ -892,7 +882,7 @@ bool GtkUI::init () ui_hooks_associate (); AUDDBG ("playlist associate\n"); - ui_playlist_notebook_populate (); + pl_notebook_populate (); g_signal_connect (slider, "change-value", (GCallback) ui_slider_change_value_cb , nullptr); g_signal_connect (slider, "button-press-event", (GCallback) ui_slider_button_press_cb, nullptr); @@ -904,10 +894,10 @@ bool GtkUI::init () timer_add (TimerRate::Hz4, ui_volume_slider_update, volume); - g_signal_connect (window, "map-event", (GCallback) window_mapped_cb, nullptr); + g_signal_connect (window, "map-event", (GCallback) pl_notebook_grab_focus, nullptr); g_signal_connect (window, "delete-event", (GCallback) window_delete, nullptr); g_signal_connect (window, "key-press-event", (GCallback) window_keypress_cb, nullptr); - g_signal_connect (UI_PLAYLIST_NOTEBOOK, "key-press-event", (GCallback) playlist_keypress_cb, nullptr); + g_signal_connect (pl_notebook, "key-press-event", (GCallback) playlist_keypress_cb, nullptr); if (aud_drct_get_playing ()) { @@ -921,7 +911,6 @@ bool GtkUI::init () title_change (); update_toggles (); - record_toggled (); gtk_widget_show_all (vbox_outer); @@ -1085,9 +1074,9 @@ void popup_menu_rclick (unsigned button, uint32_t time) time); } -void popup_menu_tab (unsigned button, uint32_t time, int playlist) +void popup_menu_tab (unsigned button, uint32_t time, Playlist playlist) { - menu_tab_playlist_id = aud_playlist_get_unique_id (playlist); + menu_tab_playlist = playlist; gtk_menu_popup ((GtkMenu *) menu_tab, nullptr, nullptr, nullptr, nullptr, button, time); } diff --git a/src/gtkui/ui_infoarea.cc b/src/gtkui/ui_infoarea.cc index 3a19331..62df5e1 100644 --- a/src/gtkui/ui_infoarea.cc +++ b/src/gtkui/ui_infoarea.cc @@ -44,7 +44,7 @@ static void compute_sizes () HEIGHT = ICON_SIZE + 2 * SPACING; BAND_WIDTH = aud::rescale (dpi, 16, 1); BAND_SPACING = aud::rescale (dpi, 48, 1); - VIS_WIDTH = VIS_BANDS * (BAND_WIDTH + BAND_SPACING) - BAND_SPACING; + VIS_WIDTH = VIS_BANDS * (BAND_WIDTH + BAND_SPACING) - BAND_SPACING + 2 * SPACING; VIS_SCALE = aud::rescale (ICON_SIZE, 8, 5); VIS_CENTER = VIS_SCALE + SPACING; } @@ -54,11 +54,10 @@ typedef struct { String title, artist, album; String last_title, last_artist, last_album; + AudguiPixbuf pb, last_pb; float alpha, last_alpha; bool stopped; - - GdkPixbuf * pb, * last_pb; } UIInfoArea; class InfoAreaVis : public Visualizer @@ -290,17 +289,17 @@ static void draw_album_art (cairo_t * cr) if (area->pb) { - int left = SPACING + (ICON_SIZE - gdk_pixbuf_get_width (area->pb)) / 2; - int top = SPACING + (ICON_SIZE - gdk_pixbuf_get_height (area->pb)) / 2; - gdk_cairo_set_source_pixbuf (cr, area->pb, left, top); + int left = SPACING + (ICON_SIZE - area->pb.width ()) / 2; + int top = SPACING + (ICON_SIZE - area->pb.height ()) / 2; + gdk_cairo_set_source_pixbuf (cr, area->pb.get (), left, top); cairo_paint_with_alpha (cr, area->alpha); } if (area->last_pb) { - int left = SPACING + (ICON_SIZE - gdk_pixbuf_get_width (area->last_pb)) / 2; - int top = SPACING + (ICON_SIZE - gdk_pixbuf_get_height (area->last_pb)) / 2; - gdk_cairo_set_source_pixbuf (cr, area->last_pb, left, top); + int left = SPACING + (ICON_SIZE - area->last_pb.width ()) / 2; + int top = SPACING + (ICON_SIZE - area->last_pb.height ()) / 2; + gdk_cairo_set_source_pixbuf (cr, area->last_pb.get (), left, top); cairo_paint_with_alpha (cr, area->last_alpha); } } @@ -395,28 +394,21 @@ static void set_album_art () { g_return_if_fail (area); - if (area->pb) - g_object_unref (area->pb); - area->pb = audgui_pixbuf_request_current (); if (! area->pb) area->pb = audgui_pixbuf_fallback (); if (area->pb) - audgui_pixbuf_scale_within (& area->pb, ICON_SIZE); + audgui_pixbuf_scale_within (area->pb, ICON_SIZE); } static void infoarea_next () { g_return_if_fail (area); - if (area->last_pb) - g_object_unref (area->last_pb); - area->last_pb = area->pb; - area->pb = nullptr; - area->last_title = std::move (area->title); area->last_artist = std::move (area->artist); area->last_album = std::move (area->album); + area->last_pb = std::move (area->pb); area->last_alpha = area->alpha; area->alpha = 0; @@ -469,7 +461,7 @@ void ui_infoarea_show_vis (bool show) /* note: "realize" signal must be connected before adding to box */ g_signal_connect (vis.widget, "realize", (GCallback) realize_cb, nullptr); - gtk_widget_set_size_request (vis.widget, VIS_WIDTH + 2 * SPACING, HEIGHT); + gtk_widget_set_size_request (vis.widget, VIS_WIDTH, HEIGHT); gtk_box_pack_start ((GtkBox *) area->box, vis.widget, false, false, 0); g_signal_connect (vis.widget, "expose-event", (GCallback) expose_vis_cb, nullptr); @@ -503,11 +495,6 @@ static void destroy_cb (GtkWidget * widget) timer_remove (TimerRate::Hz30, ui_infoarea_do_fade); - if (area->pb) - g_object_unref (area->pb); - if (area->last_pb) - g_object_unref (area->last_pb); - delete area; area = nullptr; } diff --git a/src/gtkui/ui_playlist_notebook.cc b/src/gtkui/ui_playlist_notebook.cc index 8c42e05..376d7c2 100644 --- a/src/gtkui/ui_playlist_notebook.cc +++ b/src/gtkui/ui_playlist_notebook.cc @@ -1,6 +1,6 @@ /* * ui_playlist_notebook.c - * Copyright 2010-2012 Michał Lipski and John Lindgren + * Copyright 2010-2017 Michał Lipski and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -22,6 +22,7 @@ #include <gdk/gdkkeysyms.h> #include <gtk/gtk.h> +#define AUD_GLIB_INTEGRATION #include <libaudcore/runtime.h> #include <libaudcore/playlist.h> #include <libaudcore/audstrings.h> @@ -29,17 +30,28 @@ #include <libaudgui/list.h> #include <libaudgui/libaudgui.h> +#include "../ui-common/menu-ops.h" + #include "gtkui.h" #include "ui_playlist_notebook.h" #include "ui_playlist_widget.h" -#include "playlist_util.h" -static GtkWidget * notebook = nullptr; -static int highlighted = -1; +GtkWidget * pl_notebook = nullptr; + +static Playlist highlighted; static int switch_handler = 0; static int reorder_handler = 0; +static GtkWidget * treeview_of (GtkWidget * page) + { return (GtkWidget *) g_object_get_data ((GObject *) page, "treeview"); } + +static GtkWidget * treeview_at_idx (int idx) + { return treeview_of (gtk_notebook_get_nth_page ((GtkNotebook *) pl_notebook, idx)); } + +static Playlist list_of (GtkWidget * widget) + { return aud::from_ptr<Playlist> (g_object_get_data ((GObject *) widget, "playlist")); } + void apply_column_widths (GtkWidget * treeview) { /* skip righthand column since it expands with the window */ @@ -52,9 +64,9 @@ void apply_column_widths (GtkWidget * treeview) static void size_allocate_cb (GtkWidget * treeview) { - int current = gtk_notebook_get_current_page ((GtkNotebook *) notebook); + int current = gtk_notebook_get_current_page ((GtkNotebook *) pl_notebook); - if (current < 0 || treeview != playlist_get_treeview (current)) + if (current < 0 || treeview != treeview_at_idx (current)) return; bool changed = false; @@ -74,12 +86,12 @@ static void size_allocate_cb (GtkWidget * treeview) if (changed) { - int count = gtk_notebook_get_n_pages ((GtkNotebook *) notebook); + int count = gtk_notebook_get_n_pages ((GtkNotebook *) pl_notebook); for (int i = 0; i < count; i ++) { if (i != current) - apply_column_widths (playlist_get_treeview (i)); + apply_column_widths (treeview_at_idx (i)); } } } @@ -92,15 +104,15 @@ static void make_add_button (GtkWidget * notebook) ("list-add", GTK_ICON_SIZE_MENU)); gtk_widget_set_can_focus (button, false); - g_signal_connect (button, "clicked", (GCallback) aud_playlist_new, nullptr); + g_signal_connect (button, "clicked", pl_new, nullptr); gtk_widget_show_all (button); gtk_notebook_set_action_widget ((GtkNotebook *) notebook, button, GTK_PACK_END); } -static void close_button_cb (GtkWidget * button, void * id) +static void close_button_cb (GtkWidget * button, void * data) { - audgui_confirm_playlist_delete (aud_playlist_by_unique_id (GPOINTER_TO_INT (id))); + audgui_confirm_playlist_delete (aud::from_ptr<Playlist> (data)); } static void close_button_style_set (GtkWidget * button) @@ -111,7 +123,7 @@ static void close_button_style_set (GtkWidget * button) gtk_widget_set_size_request (button, w + 2, h + 2); } -static GtkWidget * make_close_button (GtkWidget * ebox, int list) +static GtkWidget * make_close_button (GtkWidget * ebox, Playlist list) { GtkWidget * button = gtk_button_new (); GtkWidget * image = gtk_image_new_from_icon_name ("window-close", GTK_ICON_SIZE_MENU); @@ -120,8 +132,7 @@ static GtkWidget * make_close_button (GtkWidget * ebox, int list) gtk_button_set_focus_on_click ((GtkButton *) button, false); gtk_widget_set_name (button, "gtkui-tab-close-button"); - g_signal_connect (button, "clicked", (GCallback) close_button_cb, - GINT_TO_POINTER (aud_playlist_get_unique_id (list))); + g_signal_connect (button, "clicked", (GCallback) close_button_cb, aud::to_ptr (list)); gtk_rc_parse_string ( "style \"gtkui-tab-close-button-style\" {" @@ -142,9 +153,10 @@ static GtkWidget * make_close_button (GtkWidget * ebox, int list) return button; } -GtkNotebook * ui_playlist_get_notebook () +void pl_notebook_grab_focus () { - return (GtkNotebook *) notebook; + int idx = gtk_notebook_get_current_page ((GtkNotebook *) pl_notebook); + gtk_widget_grab_focus (treeview_at_idx (idx)); } static void tab_title_reset (GtkWidget * ebox) @@ -155,12 +167,10 @@ static void tab_title_reset (GtkWidget * ebox) gtk_widget_show (label); } -static void tab_title_save (GtkEntry * entry, void * ebox) +static void tab_title_save (GtkEntry * entry, GtkWidget * ebox) { - int id = GPOINTER_TO_INT (g_object_get_data ((GObject *) ebox, "playlist-id")); GtkWidget * label = (GtkWidget *) g_object_get_data ((GObject *) ebox, "label"); - - aud_playlist_set_title (aud_playlist_by_unique_id (id), gtk_entry_get_text (entry)); + list_of (ebox).set_title (gtk_entry_get_text (entry)); gtk_widget_hide ((GtkWidget *) entry); gtk_widget_show (label); } @@ -175,17 +185,16 @@ static gboolean tab_key_press_cb (GtkWidget * widget, GdkEventKey * event) static gboolean tab_button_press_cb (GtkWidget * ebox, GdkEventButton * event) { - int id = GPOINTER_TO_INT (g_object_get_data ((GObject *) ebox, "playlist-id")); - int playlist = aud_playlist_by_unique_id (id); + auto list = list_of (ebox); if (event->type == GDK_2BUTTON_PRESS && event->button == 1) - aud_playlist_play (playlist); + list.start_playback (); if (event->type == GDK_BUTTON_PRESS && event->button == 2) - audgui_confirm_playlist_delete (playlist); + audgui_confirm_playlist_delete (list); if (event->type == GDK_BUTTON_PRESS && event->button == 3) - popup_menu_tab (event->button, event->time, playlist); + popup_menu_tab (event->button, event->time, list); return false; } @@ -196,12 +205,12 @@ static gboolean scroll_cb (GtkWidget * widget, GdkEventScroll * event) { case GDK_SCROLL_UP: case GDK_SCROLL_LEFT: - aud_playlist_set_active (aud_playlist_get_active () - 1); + pl_prev (); return true; case GDK_SCROLL_DOWN: case GDK_SCROLL_RIGHT: - aud_playlist_set_active (aud_playlist_get_active () + 1); + pl_next (); return true; default: @@ -216,63 +225,61 @@ static gboolean scroll_ignore_cb () static void tab_changed (GtkNotebook * notebook, GtkWidget * page, unsigned page_num) { - aud_playlist_set_active (page_num); + Playlist::by_index (page_num).activate (); } -static void tab_reordered (GtkNotebook * notebook, GtkWidget * child, unsigned page_num) +static void tab_reordered (GtkNotebook * notebook, GtkWidget * page, unsigned page_num) { - GtkWidget * widget = (GtkWidget *) g_object_get_data ((GObject *) child, "treeview"); - g_return_if_fail (widget); - aud_playlist_reorder (ui_playlist_widget_get_playlist (widget), page_num, 1); + auto list = list_of (treeview_of (page)); + Playlist::reorder_playlists (list.index (), page_num, 1); } -static GtkLabel * get_tab_label (int playlist) +static GtkLabel * get_tab_label (int list_idx) { - GtkWidget * page = gtk_notebook_get_nth_page (UI_PLAYLIST_NOTEBOOK, playlist); - GtkWidget * ebox = gtk_notebook_get_tab_label (UI_PLAYLIST_NOTEBOOK, page); + GtkWidget * page = gtk_notebook_get_nth_page ((GtkNotebook *) pl_notebook, list_idx); + GtkWidget * ebox = gtk_notebook_get_tab_label ((GtkNotebook *) pl_notebook, page); return (GtkLabel *) g_object_get_data ((GObject *) ebox, "label"); } -static void set_tab_label (int list, GtkLabel * label) +static void update_tab_label (GtkLabel * label, Playlist list) { - String title0 = aud_playlist_get_title (list); + String title0 = list.get_title (); StringBuf title = aud_get_bool ("gtkui", "entry_count_visible") ? - str_printf ("%s (%d)", (const char *) title0, aud_playlist_entry_count (list)) : + str_printf ("%s (%d)", (const char *) title0, list.n_entries ()) : str_copy (title0); - if (list == aud_playlist_get_playing ()) + if (list == Playlist::playing_playlist ()) { - char * markup = g_markup_printf_escaped ("<b>%s</b>", (const char *) title); + CharPtr markup (g_markup_printf_escaped ("<b>%s</b>", (const char *) title)); gtk_label_set_markup (label, markup); - g_free (markup); } else gtk_label_set_text (label, title); } -void start_rename_playlist (int playlist) +void start_rename_playlist (Playlist playlist) { - if (! gtk_notebook_get_show_tabs ((GtkNotebook *) notebook)) + if (! gtk_notebook_get_show_tabs ((GtkNotebook *) pl_notebook)) { audgui_show_playlist_rename (playlist); return; } - GtkWidget * page = gtk_notebook_get_nth_page (UI_PLAYLIST_NOTEBOOK, playlist); - GtkWidget * ebox = gtk_notebook_get_tab_label (UI_PLAYLIST_NOTEBOOK, page); + GtkWidget * page = gtk_notebook_get_nth_page ((GtkNotebook *) pl_notebook, playlist.index ()); + GtkWidget * ebox = gtk_notebook_get_tab_label ((GtkNotebook *) pl_notebook, page); GtkWidget * label = (GtkWidget *) g_object_get_data ((GObject *) ebox, "label"); GtkWidget * entry = (GtkWidget *) g_object_get_data ((GObject *) ebox, "entry"); gtk_widget_hide (label); - gtk_entry_set_text ((GtkEntry *) entry, aud_playlist_get_title (playlist)); + gtk_entry_set_text ((GtkEntry *) entry, playlist.get_title ()); gtk_widget_grab_focus (entry); gtk_editable_select_region ((GtkEditable *) entry, 0, -1); gtk_widget_show (entry); } -void ui_playlist_notebook_create_tab (int playlist) +static void create_tab (int list_idx, Playlist list) { GtkWidget * scrollwin = gtk_scrolled_window_new (nullptr, nullptr); GtkAdjustment * vscroll = gtk_scrolled_window_get_vadjustment ((GtkScrolledWindow *) scrollwin); @@ -280,7 +287,7 @@ void ui_playlist_notebook_create_tab (int playlist) /* do not allow scroll events to propagate up to the notebook */ g_signal_connect_after (scrollwin, "scroll-event", (GCallback) scroll_ignore_cb, nullptr); - GtkWidget * treeview = ui_playlist_widget_new (playlist); + GtkWidget * treeview = ui_playlist_widget_new (list); apply_column_widths (treeview); g_signal_connect (treeview, "size-allocate", (GCallback) size_allocate_cb, nullptr); @@ -298,7 +305,7 @@ void ui_playlist_notebook_create_tab (int playlist) GtkWidget * hbox = gtk_hbox_new (false, 2); GtkWidget * label = gtk_label_new (""); - set_tab_label (playlist, (GtkLabel *) label); + update_tab_label ((GtkLabel *) label, list); gtk_box_pack_start ((GtkBox *) hbox, label, false, false, 0); GtkWidget * entry = gtk_entry_new (); @@ -311,7 +318,7 @@ void ui_playlist_notebook_create_tab (int playlist) if (aud_get_bool ("gtkui", "close_button_visible")) { - button = make_close_button (ebox, playlist); + button = make_close_button (ebox, list); gtk_box_pack_end ((GtkBox *) hbox, button, false, false, 0); } @@ -319,18 +326,17 @@ void ui_playlist_notebook_create_tab (int playlist) g_object_set_data ((GObject *) ebox, "entry", entry); g_object_set_data ((GObject *) ebox, "page", scrollwin); - gtk_notebook_insert_page (UI_PLAYLIST_NOTEBOOK, scrollwin, ebox, playlist); - gtk_notebook_set_tab_reorderable (UI_PLAYLIST_NOTEBOOK, scrollwin, true); + gtk_notebook_insert_page ((GtkNotebook *) pl_notebook, scrollwin, ebox, list_idx); + gtk_notebook_set_tab_reorderable ((GtkNotebook *) pl_notebook, scrollwin, true); - int id = aud_playlist_get_unique_id (playlist); - g_object_set_data ((GObject *) ebox, "playlist-id", GINT_TO_POINTER (id)); - g_object_set_data ((GObject *) treeview, "playlist-id", GINT_TO_POINTER (id)); + g_object_set_data ((GObject *) ebox, "playlist", aud::to_ptr (list)); + g_object_set_data ((GObject *) treeview, "playlist", aud::to_ptr (list)); - int position = aud_playlist_get_position (playlist); + int position = list.get_position (); if (position >= 0) audgui_list_set_highlight (treeview, position); - int focus = aud_playlist_get_focus (playlist); + int focus = list.get_focus (); if (focus >= 0) audgui_list_set_focus (treeview, position); @@ -352,70 +358,71 @@ void ui_playlist_notebook_create_tab (int playlist) } } -void ui_playlist_notebook_populate () +static void switch_to_active () { - int playlist = aud_playlist_get_active (); - int playlist_count = aud_playlist_count (); + int active_idx = Playlist::active_playlist ().index (); + gtk_notebook_set_current_page ((GtkNotebook *) pl_notebook, active_idx); +} - for (int count = 0; count < playlist_count; count ++) - ui_playlist_notebook_create_tab (count); +void pl_notebook_populate () +{ + int n_playlists = Playlist::n_playlists (); + for (int idx = 0; idx < n_playlists; idx ++) + create_tab (idx, Playlist::by_index (idx)); - gtk_notebook_set_current_page (UI_PLAYLIST_NOTEBOOK, playlist); - highlighted = aud_playlist_get_unique_id (aud_playlist_get_playing ()); + switch_to_active (); + highlighted = Playlist::playing_playlist (); if (! switch_handler) - switch_handler = g_signal_connect (notebook, "switch-page", (GCallback) - tab_changed, nullptr); + switch_handler = g_signal_connect (pl_notebook, "switch-page", + (GCallback) tab_changed, nullptr); if (! reorder_handler) - reorder_handler = g_signal_connect (notebook, "page-reordered", + reorder_handler = g_signal_connect (pl_notebook, "page-reordered", (GCallback) tab_reordered, nullptr); - gtk_widget_grab_focus (playlist_get_treeview (playlist)); + pl_notebook_grab_focus (); } -void ui_playlist_notebook_empty () +void pl_notebook_purge () { if (switch_handler) - g_signal_handler_disconnect (notebook, switch_handler); + g_signal_handler_disconnect (pl_notebook, switch_handler); switch_handler = 0; if (reorder_handler) - g_signal_handler_disconnect (notebook, reorder_handler); + g_signal_handler_disconnect (pl_notebook, reorder_handler); reorder_handler = 0; - int n_pages = gtk_notebook_get_n_pages ((GtkNotebook *) notebook); + int n_pages = gtk_notebook_get_n_pages ((GtkNotebook *) pl_notebook); while (n_pages) - gtk_notebook_remove_page ((GtkNotebook *) notebook, -- n_pages); + gtk_notebook_remove_page ((GtkNotebook *) pl_notebook, -- n_pages); } static void add_remove_pages () { - g_signal_handlers_block_by_func (notebook, (void *) tab_changed, nullptr); - g_signal_handlers_block_by_func (notebook, (void *) tab_reordered, nullptr); + g_signal_handlers_block_by_func (pl_notebook, (void *) tab_changed, nullptr); + g_signal_handlers_block_by_func (pl_notebook, (void *) tab_reordered, nullptr); - int lists = aud_playlist_count (); - int pages = gtk_notebook_get_n_pages ((GtkNotebook *) notebook); + int lists = Playlist::n_playlists (); + int pages = gtk_notebook_get_n_pages ((GtkNotebook *) pl_notebook); /* scan through existing treeviews */ for (int i = 0; i < pages; ) { - GtkWidget * page = gtk_notebook_get_nth_page ((GtkNotebook *) notebook, i); - GtkWidget * tree = (GtkWidget *) g_object_get_data ((GObject *) page, "treeview"); - int tree_id = GPOINTER_TO_INT (g_object_get_data ((GObject *) tree, "playlist-id")); + auto list0 = list_of (treeview_at_idx (i)); /* do we have an orphaned treeview? */ - if (aud_playlist_by_unique_id (tree_id) < 0) + if (! list0.exists ()) { - gtk_notebook_remove_page ((GtkNotebook *) notebook, i); + gtk_notebook_remove_page ((GtkNotebook *) pl_notebook, i); pages --; continue; } /* do we have the right treeview? */ - int list_id = aud_playlist_get_unique_id (i); + auto list = Playlist::by_index (i); - if (tree_id == list_id) + if (list0 == list) { - ui_playlist_widget_set_playlist (tree, i); i ++; continue; } @@ -425,14 +432,13 @@ static void add_remove_pages () for (int j = i + 1; j < pages; j ++) { - page = gtk_notebook_get_nth_page ((GtkNotebook *) notebook, j); - tree = (GtkWidget *) g_object_get_data ((GObject *) page, "treeview"); - tree_id = GPOINTER_TO_INT (g_object_get_data ((GObject *) tree, "playlist-id")); + GtkWidget * page = gtk_notebook_get_nth_page ((GtkNotebook *) pl_notebook, j); + auto list2 = list_of (treeview_of (page)); /* found it? move it to the right place */ - if (tree_id == list_id) + if (list2 == list) { - gtk_notebook_reorder_child ((GtkNotebook *) notebook, page, i); + gtk_notebook_reorder_child ((GtkNotebook *) pl_notebook, page, i); found = true; break; } @@ -441,7 +447,7 @@ static void add_remove_pages () /* didn't find it? create it */ if (! found) { - ui_playlist_notebook_create_tab (i); + create_tab (i, list); pages ++; continue; } @@ -450,115 +456,105 @@ static void add_remove_pages () /* create new treeviews */ while (pages < lists) { - ui_playlist_notebook_create_tab (pages); + create_tab (pages, Playlist::by_index (pages)); pages ++; } - gtk_notebook_set_current_page ((GtkNotebook *) notebook, aud_playlist_get_active ()); - + switch_to_active (); show_hide_playlist_tabs (); - g_signal_handlers_unblock_by_func (notebook, (void *) tab_changed, nullptr); - g_signal_handlers_unblock_by_func (notebook, (void *) tab_reordered, nullptr); + g_signal_handlers_unblock_by_func (pl_notebook, (void *) tab_changed, nullptr); + g_signal_handlers_unblock_by_func (pl_notebook, (void *) tab_reordered, nullptr); } -void ui_playlist_notebook_update (void * data, void * user) +void pl_notebook_update (void * data, void * user) { auto global_level = aud::from_ptr<Playlist::UpdateLevel> (data); if (global_level == Playlist::Structure) add_remove_pages (); - int lists = aud_playlist_count (); + int n_pages = gtk_notebook_get_n_pages ((GtkNotebook *) pl_notebook); - for (int list = 0; list < lists; list ++) + for (int i = 0; i < n_pages; i ++) { - if (global_level >= Playlist::Metadata) - set_tab_label (list, get_tab_label (list)); + GtkWidget * treeview = treeview_at_idx (i); - GtkWidget * treeview = playlist_get_treeview (list); - - auto update = aud_playlist_update_detail (list); - if (update.level) - ui_playlist_widget_update (treeview, update); + if (global_level >= Playlist::Metadata) + update_tab_label (get_tab_label (i), list_of (treeview)); - audgui_list_set_highlight (treeview, aud_playlist_get_position (list)); + ui_playlist_widget_update (treeview); } - gtk_notebook_set_current_page ((GtkNotebook *) notebook, aud_playlist_get_active ()); + switch_to_active (); } -void ui_playlist_notebook_position (void * data, void * user) +void pl_notebook_set_position (void * data, void * user) { - int list = aud::from_ptr<int> (data); - int row = aud_playlist_get_position (list); + auto list = aud::from_ptr<Playlist> (data); + int row = list.get_position (); if (aud_get_bool ("gtkui", "autoscroll")) { - aud_playlist_select_all (list, false); - aud_playlist_entry_set_selected (list, row, true); - aud_playlist_set_focus (list, row); + list.select_all (false); + list.select_entry (row, true); + list.set_focus (row); } - if (! aud_playlist_update_pending ()) - audgui_list_set_highlight (playlist_get_treeview (list), row); + audgui_list_set_highlight (treeview_at_idx (list.index ()), row); } -void ui_playlist_notebook_activate (void * data, void * user) +void pl_notebook_activate (void * data, void * user) { - if (! aud_playlist_update_pending ()) - gtk_notebook_set_current_page ((GtkNotebook *) notebook, aud_playlist_get_active ()); + switch_to_active (); } -void ui_playlist_notebook_set_playing (void * data, void * user) +void pl_notebook_set_playing (void * data, void * user) { - int id = aud_playlist_get_unique_id (aud_playlist_get_playing ()); + auto playing = Playlist::playing_playlist (); // if the previous playing playlist was deleted, ignore it - if (aud_playlist_by_unique_id (highlighted) < 0) - highlighted = -1; + if (! highlighted.exists ()) + highlighted = Playlist (); - if (highlighted == id) + if (highlighted == playing) return; - int pages = gtk_notebook_get_n_pages ((GtkNotebook *) notebook); + int pages = gtk_notebook_get_n_pages ((GtkNotebook *) pl_notebook); for (int i = 0; i < pages; i ++) { - GtkWidget * page = gtk_notebook_get_nth_page ((GtkNotebook *) notebook, i); - GtkWidget * tree = (GtkWidget *) g_object_get_data ((GObject *) page, "treeview"); - int tree_id = GPOINTER_TO_INT (g_object_get_data ((GObject *) tree, "playlist-id")); - - if (tree_id == highlighted || tree_id == id) - set_tab_label (i, get_tab_label (i)); + auto list = list_of (treeview_at_idx (i)); + if (list == highlighted || list == playing) + update_tab_label (get_tab_label (i), list); } - highlighted = id; + highlighted = playing; } static void destroy_cb () { - notebook = nullptr; + pl_notebook = nullptr; switch_handler = 0; reorder_handler = 0; } -GtkWidget * ui_playlist_notebook_new () +GtkWidget * pl_notebook_new () { - notebook = gtk_notebook_new (); - gtk_notebook_set_scrollable ((GtkNotebook *) notebook, true); - make_add_button (notebook); + pl_notebook = gtk_notebook_new (); + gtk_notebook_set_scrollable ((GtkNotebook *) pl_notebook, true); + make_add_button (pl_notebook); show_hide_playlist_tabs (); - gtk_widget_add_events (notebook, GDK_SCROLL_MASK); - g_signal_connect (notebook, "scroll-event", (GCallback) scroll_cb, nullptr); - g_signal_connect (notebook, "destroy", (GCallback) destroy_cb, nullptr); + gtk_widget_add_events (pl_notebook, GDK_SCROLL_MASK); + g_signal_connect (pl_notebook, "scroll-event", (GCallback) scroll_cb, nullptr); + g_signal_connect (pl_notebook, "destroy", (GCallback) destroy_cb, nullptr); - return notebook; + return pl_notebook; } void show_hide_playlist_tabs () { - gtk_notebook_set_show_tabs ((GtkNotebook *) notebook, aud_get_bool ("gtkui", - "playlist_tabs_visible") || aud_playlist_count () > 1); + gtk_notebook_set_show_tabs ((GtkNotebook *) pl_notebook, aud_get_bool ("gtkui", + "playlist_tabs_visible") || Playlist::n_playlists () > 1); } diff --git a/src/gtkui/ui_playlist_notebook.h b/src/gtkui/ui_playlist_notebook.h index 1deb651..317997c 100644 --- a/src/gtkui/ui_playlist_notebook.h +++ b/src/gtkui/ui_playlist_notebook.h @@ -22,20 +22,22 @@ #include <gtk/gtk.h> -#define UI_PLAYLIST_NOTEBOOK ui_playlist_get_notebook () +class Playlist; -GtkNotebook * ui_playlist_get_notebook (); -GtkWidget * ui_playlist_notebook_new (); +extern GtkWidget * pl_notebook; -void ui_playlist_notebook_create_tab (int playlist); -void ui_playlist_notebook_populate (); -void ui_playlist_notebook_empty (); -void ui_playlist_notebook_update (void * data, void * user); -void ui_playlist_notebook_activate (void * data, void * user); -void ui_playlist_notebook_set_playing (void * data, void * user); -void ui_playlist_notebook_position (void * data, void * user); +GtkWidget * pl_notebook_new (); -void start_rename_playlist (int playlist); +void pl_notebook_grab_focus (); +void pl_notebook_populate (); +void pl_notebook_purge (); + +void pl_notebook_update (void * data, void * user); +void pl_notebook_activate (void * data, void * user); +void pl_notebook_set_playing (void * data, void * user); +void pl_notebook_set_position (void * data, void * user); + +void start_rename_playlist (Playlist playlist); void show_hide_playlist_tabs (); #endif diff --git a/src/gtkui/ui_playlist_widget.cc b/src/gtkui/ui_playlist_widget.cc index 9307c6f..b2cf40b 100644 --- a/src/gtkui/ui_playlist_widget.cc +++ b/src/gtkui/ui_playlist_widget.cc @@ -32,7 +32,6 @@ #include <libaudgui/list.h> #include "gtkui.h" -#include "playlist_util.h" #include "ui_playlist_widget.h" static const GType pw_col_types[PW_COLS] = @@ -50,7 +49,8 @@ static const GType pw_col_types[PW_COLS] = G_TYPE_STRING, // path G_TYPE_STRING, // file name G_TYPE_STRING, // custom title - G_TYPE_STRING // bitrate + G_TYPE_STRING, // bitrate + G_TYPE_STRING // comment }; static const int pw_col_min_widths[PW_COLS] = { @@ -67,7 +67,8 @@ static const int pw_col_min_widths[PW_COLS] = { 10, // path 10, // file name 10, // custom title - 3 // bitrate + 3, // bitrate + 10 // comment }; static const bool pw_col_label[PW_COLS] = { @@ -84,13 +85,18 @@ static const bool pw_col_label[PW_COLS] = { true, // path true, // file name true, // custom title - false // bitrate + false, // bitrate + true // comment }; -struct PlaylistWidgetData { - int list; +struct PlaylistWidgetData +{ + Playlist list; int popup_pos = -1; QueuedFunc popup_timer; + + void show_popup () + { audgui_infopopup_show (list, popup_pos); } }; static void set_int_from_tuple (GValue * value, const Tuple & tuple, Tuple::Field field) @@ -107,9 +113,9 @@ static void set_string_from_tuple (GValue * value, const Tuple & tuple, Tuple::F g_value_set_string (value, tuple.get_str (field)); } -static void set_queued (GValue * value, int list, int row) +static void set_queued (GValue * value, Playlist list, int row) { - int q = aud_playlist_queue_find_entry (list, row); + int q = list.queue_find_entry (row); if (q < 0) g_value_set_string (value, ""); else @@ -129,29 +135,14 @@ static void get_value (void * user, int row, int column, GValue * value) { PlaylistWidgetData * data = (PlaylistWidgetData *) user; g_return_if_fail (column >= 0 && column < pw_num_cols); - g_return_if_fail (row >= 0 && row < aud_playlist_entry_count (data->list)); + g_return_if_fail (row >= 0 && row < data->list.n_entries ()); column = pw_cols[column]; Tuple tuple; - switch (column) - { - case PW_COL_TITLE: - case PW_COL_ARTIST: - case PW_COL_ALBUM: - case PW_COL_YEAR: - case PW_COL_ALBUM_ARTIST: - case PW_COL_TRACK: - case PW_COL_GENRE: - case PW_COL_LENGTH: - case PW_COL_FILENAME: - case PW_COL_PATH: - case PW_COL_CUSTOM: - case PW_COL_BITRATE: - tuple = aud_playlist_entry_get_tuple (data->list, row, Playlist::NoWait); - break; - } + if (column != PW_COL_NUMBER && column != PW_COL_QUEUED) + tuple = data->list.entry_tuple (row, Playlist::NoWait); switch (column) { @@ -197,34 +188,37 @@ static void get_value (void * user, int row, int column, GValue * value) case PW_COL_BITRATE: set_int_from_tuple (value, tuple, Tuple::Bitrate); break; + case PW_COL_COMMENT: + set_string_from_tuple (value, tuple, Tuple::Comment); + break; } } static bool get_selected (void * user, int row) { - return aud_playlist_entry_get_selected (((PlaylistWidgetData *) user)->list, row); + return ((PlaylistWidgetData *) user)->list.entry_selected (row); } static void set_selected (void * user, int row, bool selected) { - aud_playlist_entry_set_selected (((PlaylistWidgetData *) user)->list, row, selected); + ((PlaylistWidgetData *) user)->list.select_entry (row, selected); } static void select_all (void * user, bool selected) { - aud_playlist_select_all (((PlaylistWidgetData *) user)->list, selected); + ((PlaylistWidgetData *) user)->list.select_all (selected); } static void focus_change (void * user, int row) { - aud_playlist_set_focus (((PlaylistWidgetData *) user)->list, row); + ((PlaylistWidgetData *) user)->list.set_focus (row); } static void activate_row (void * user, int row) { - int list = ((PlaylistWidgetData *) user)->list; - aud_playlist_set_position (list, row); - aud_playlist_play (list); + auto list = ((PlaylistWidgetData *) user)->list; + list.set_position (row); + list.start_playback (); } static void right_click (void * user, GdkEventButton * event) @@ -234,16 +228,16 @@ static void right_click (void * user, GdkEventButton * event) static void shift_rows (void * user, int row, int before) { - int list = ((PlaylistWidgetData *) user)->list; + auto list = ((PlaylistWidgetData *) user)->list; /* Adjust the shift amount so that the selected entry closest to the * destination ends up at the destination. */ if (before > row) - before -= playlist_count_selected_in_range (list, row, before - row); + before -= list.n_selected (row, before - row); else - before += playlist_count_selected_in_range (list, before, row - before); + before += list.n_selected (before, row - before); - aud_playlist_shift (list, row, before - row); + list.shift_entries (row, before - row); } static void popup_hide (PlaylistWidgetData * data) @@ -258,13 +252,9 @@ static void popup_trigger (PlaylistWidgetData * data, int pos) { audgui_infopopup_hide (); - auto show_cb = [] (void * data_) { - auto data = (PlaylistWidgetData *) data_; - audgui_infopopup_show (data->list, data->popup_pos); - }; - data->popup_pos = pos; - data->popup_timer.queue (aud_get_int (nullptr, "filepopup_delay") * 100, show_cb, data); + data->popup_timer.queue (aud_get_int (nullptr, "filepopup_delay") * 100, + aud::obj_member<PlaylistWidgetData, & PlaylistWidgetData::show_popup>, data); } static void mouse_motion (void * user, GdkEventMotion * event, int row) @@ -288,14 +278,14 @@ static void mouse_leave (void * user, GdkEventMotion * event, int row) static Index<char> get_data (void * user) { - int playlist = ((PlaylistWidgetData *) user)->list; + auto playlist = ((PlaylistWidgetData *) user)->list; return audgui_urilist_create_from_selected (playlist); } // length is ignored; GtkSelectionData null-terminates the data for us static void receive_data (void * user, int row, const char * data, int /*length*/) { - int playlist = ((PlaylistWidgetData *) user)->list; + auto playlist = ((PlaylistWidgetData *) user)->list; audgui_urilist_insert (playlist, row, data); } @@ -330,8 +320,8 @@ static gboolean search_cb (GtkTreeModel * model, int column, const char * search if (keys.len ()) { - int list = ((PlaylistWidgetData *) user)->list; - Tuple tuple = aud_playlist_entry_get_tuple (list, row); + auto list = ((PlaylistWidgetData *) user)->list; + Tuple tuple = list.entry_tuple (row); String strings[3] = { tuple.get_str (Tuple::Title), @@ -361,13 +351,13 @@ static void destroy_cb (PlaylistWidgetData * data) delete data; } -GtkWidget * ui_playlist_widget_new (int playlist) +GtkWidget * ui_playlist_widget_new (Playlist playlist) { PlaylistWidgetData * data = new PlaylistWidgetData; data->list = playlist; GtkWidget * list = audgui_list_new (& callbacks, data, - aud_playlist_entry_count (playlist)); + playlist.n_entries ()); gtk_tree_view_set_headers_visible ((GtkTreeView *) list, aud_get_bool ("gtkui", "playlist_headers")); @@ -390,26 +380,17 @@ GtkWidget * ui_playlist_widget_new (int playlist) return list; } -int ui_playlist_widget_get_playlist (GtkWidget * widget) -{ - PlaylistWidgetData * data = (PlaylistWidgetData *) audgui_list_get_user (widget); - g_return_val_if_fail (data, -1); - return data->list; -} - -void ui_playlist_widget_set_playlist (GtkWidget * widget, int list) +void ui_playlist_widget_update (GtkWidget * widget) { PlaylistWidgetData * data = (PlaylistWidgetData *) audgui_list_get_user (widget); g_return_if_fail (data); - data->list = list; -} -void ui_playlist_widget_update (GtkWidget * widget, const Playlist::Update & update) -{ - PlaylistWidgetData * data = (PlaylistWidgetData *) audgui_list_get_user (widget); - g_return_if_fail (data); + auto update = data->list.update_detail (); + + if (update.level == Playlist::NoUpdate) + return; - int entries = aud_playlist_entry_count (data->list); + int entries = data->list.n_entries (); int changed = entries - update.before - update.after; if (update.level == Playlist::Structure) @@ -422,9 +403,8 @@ void ui_playlist_widget_update (GtkWidget * widget, const Playlist::Update & upd /* scroll to end of playlist if entries were added there (but not if a newly added entry is playing) */ - if (entries > old_entries && ! update.after && - aud_playlist_get_focus (data->list) < old_entries) - aud_playlist_set_focus (data->list, entries - 1); + if (entries > old_entries && ! update.after && data->list.get_focus () < old_entries) + data->list.set_focus (entries - 1); ui_playlist_widget_scroll (widget); } @@ -433,16 +413,17 @@ void ui_playlist_widget_update (GtkWidget * widget, const Playlist::Update & upd if (update.queue_changed) { - for (int i = aud_playlist_queue_count (data->list); i --; ) + for (int i = data->list.n_queued (); i --; ) { - int entry = aud_playlist_queue_get_entry (data->list, i); + int entry = data->list.queue_get_entry (i); if (entry < update.before || entry >= entries - update.after) audgui_list_update_rows (widget, entry, 1); } } audgui_list_update_selection (widget, update.before, changed); - audgui_list_set_focus (widget, aud_playlist_get_focus (data->list)); + audgui_list_set_highlight (widget, data->list.get_position ()); + audgui_list_set_focus (widget, data->list.get_focus ()); } void ui_playlist_widget_scroll (GtkWidget * widget) diff --git a/src/gtkui/ui_playlist_widget.h b/src/gtkui/ui_playlist_widget.h index 9503d84..e634557 100644 --- a/src/gtkui/ui_playlist_widget.h +++ b/src/gtkui/ui_playlist_widget.h @@ -23,10 +23,8 @@ #include <gtk/gtk.h> #include <libaudcore/playlist.h> -GtkWidget * ui_playlist_widget_new (int playlist); -int ui_playlist_widget_get_playlist (GtkWidget * widget); -void ui_playlist_widget_set_playlist (GtkWidget * widget, int playlist); -void ui_playlist_widget_update (GtkWidget * widget, const Playlist::Update & update); +GtkWidget * ui_playlist_widget_new (Playlist playlist); +void ui_playlist_widget_update (GtkWidget * widget); void ui_playlist_widget_scroll (GtkWidget * widget); enum { @@ -44,6 +42,7 @@ enum { PW_COL_FILENAME, PW_COL_CUSTOM, PW_COL_BITRATE, + PW_COL_COMMENT, PW_COLS }; diff --git a/src/gtkui/ui_statusbar.cc b/src/gtkui/ui_statusbar.cc index f496c8e..f4df54a 100644 --- a/src/gtkui/ui_statusbar.cc +++ b/src/gtkui/ui_statusbar.cc @@ -31,9 +31,9 @@ static QueuedFunc clear_timeout; static void ui_statusbar_update_playlist_length (void *, void * label) { - int playlist = aud_playlist_get_active (); - StringBuf s1 = str_format_time (aud_playlist_get_selected_length (playlist)); - StringBuf s2 = str_format_time (aud_playlist_get_total_length (playlist)); + auto playlist = Playlist::active_playlist (); + StringBuf s1 = str_format_time (playlist.selected_length_ms ()); + StringBuf s2 = str_format_time (playlist.total_length_ms ()); gtk_label_set_text ((GtkLabel *) label, str_concat ({s1, " / ", s2})); } diff --git a/src/hotkey/plugin.cc b/src/hotkey/plugin.cc index b08975f..f108e33 100644 --- a/src/hotkey/plugin.cc +++ b/src/hotkey/plugin.cc @@ -267,19 +267,19 @@ gboolean handle_keyevent (EVENT event) if (event == EVENT_TOGGLE_REPEAT) { - aud_set_bool (nullptr, "repeat", ! aud_get_bool (nullptr, "repeat")); + aud_toggle_bool (nullptr, "repeat"); return true; } if (event == EVENT_TOGGLE_SHUFFLE) { - aud_set_bool (nullptr, "shuffle", ! aud_get_bool (nullptr, "shuffle")); + aud_toggle_bool (nullptr, "shuffle"); return true; } if (event == EVENT_TOGGLE_STOP) { - aud_set_bool (nullptr, "stop_after_current_song", ! aud_get_bool (nullptr, "stop_after_current_song")); + aud_toggle_bool (nullptr, "stop_after_current_song"); return true; } diff --git a/src/lirc/lirc.cc b/src/lirc/lirc.cc index f37d411..e954a48 100644 --- a/src/lirc/lirc.cc +++ b/src/lirc/lirc.cc @@ -147,8 +147,8 @@ void LIRCPlugin::cleanup () gboolean jump_to (void * data) { - int playlist = aud_playlist_get_active (); - aud_playlist_set_position (playlist, atoi (track_no) - 1); + auto playlist = Playlist::active_playlist (); + playlist.set_position (atoi (track_no) - 1); track_no_pos = 0; tid = 0; return false; @@ -209,9 +209,9 @@ static gboolean lirc_input_callback (GIOChannel * source, GIOCondition condition } } else if (g_ascii_strcasecmp ("SHUFFLE", c) == 0) - aud_set_bool (nullptr, "shuffle", ! aud_get_bool (nullptr, "shuffle")); + aud_toggle_bool (nullptr, "shuffle"); else if (g_ascii_strcasecmp ("REPEAT", c) == 0) - aud_set_bool (nullptr, "repeat", ! aud_get_bool (nullptr, "repeat")); + aud_toggle_bool (nullptr, "repeat"); else if (g_ascii_strncasecmp ("FWD", c, 3) == 0) { ptr = c + 3; @@ -330,10 +330,7 @@ static gboolean lirc_input_callback (GIOChannel * source, GIOCondition condition else if (g_ascii_strcasecmp ("PLAYLIST_CLEAR", c) == 0) { aud_drct_stop (); - int playlist = aud_playlist_get_active (); - aud_playlist_entry_delete (playlist, 0, - aud_playlist_entry_count - (playlist)); + Playlist::active_playlist ().remove_all_entries (); } else if (g_ascii_strncasecmp ("PLAYLIST_ADD ", c, 13) == 0) { diff --git a/src/lyricwiki-qt/lyricwiki.cc b/src/lyricwiki-qt/lyricwiki.cc index cf140d2..d325347 100644 --- a/src/lyricwiki-qt/lyricwiki.cc +++ b/src/lyricwiki-qt/lyricwiki.cc @@ -33,6 +33,7 @@ #include <libxml/HTMLparser.h> #include <libxml/xpath.h> +#define AUD_GLIB_INTEGRATION #include <libaudcore/drct.h> #include <libaudcore/i18n.h> #include <libaudcore/plugin.h> @@ -86,11 +87,10 @@ static void libxml_error_handler (void * ctx, const char * msg, ...) { } -/* g_free() returned text */ -static char * scrape_lyrics_from_lyricwiki_edit_page (const char * buf, int64_t len) +static CharPtr scrape_lyrics_from_lyricwiki_edit_page (const char * buf, int64_t len) { xmlDocPtr doc; - char * ret = nullptr; + CharPtr ret; /* * temporarily set our error-handling functor to our suppression function, @@ -145,12 +145,9 @@ give_up: (GRegexMatchFlags) 0, nullptr); g_regex_match (reg, (char *) lyric, G_REGEX_MATCH_NEWLINE_ANY, & match_info); - ret = g_match_info_fetch (match_info, 2); + ret.capture (g_match_info_fetch (match_info, 2)); if (! strcmp_nocase (ret, "<!-- PUT LYRICS HERE (and delete this entire line) -->")) - { - g_free (ret); - ret = g_strdup (_("No lyrics available")); - } + ret.capture (g_strdup (_("No lyrics available"))); g_regex_unref (reg); } @@ -178,7 +175,7 @@ static String scrape_uri_from_lyricwiki_search_result (const char * buf, int64_t reg = g_regex_new ("<(lyrics?)>.*</\\1>", (GRegexCompileFlags) (G_REGEX_MULTILINE | G_REGEX_DOTALL | G_REGEX_UNGREEDY), (GRegexMatchFlags) 0, nullptr); - char * newbuf = g_regex_replace_literal (reg, buf, len, 0, "", G_REGEX_MATCH_NEWLINE_ANY, nullptr); + CharPtr newbuf (g_regex_replace_literal (reg, buf, len, 0, "", G_REGEX_MATCH_NEWLINE_ANY, nullptr)); g_regex_unref (reg); /* @@ -244,9 +241,11 @@ static String scrape_uri_from_lyricwiki_search_result (const char * buf, int64_t { // Convert normal lyrics link to edit page link char * slash = strrchr (lyric, '/'); - if (slash) + if (slash && ! strstr (slash, "lyrics.wikia.com")) uri = String (str_printf ("http://lyrics.wikia.com/index.php?" "action=edit&title=%s", slash + 1)); + else + uri = String ("N/A"); } xmlFree ((xmlChar *) lyric); @@ -256,8 +255,6 @@ static String scrape_uri_from_lyricwiki_search_result (const char * buf, int64_t xmlFreeDoc (doc); } - g_free (newbuf); - return uri; } @@ -275,7 +272,7 @@ static void get_lyrics_step_3 (const char * uri, const Index<char> & buf, void * return; } - char * lyrics = scrape_lyrics_from_lyricwiki_edit_page (buf.begin (), buf.len ()); + CharPtr lyrics = scrape_lyrics_from_lyricwiki_edit_page (buf.begin (), buf.len ()); if (! lyrics) { @@ -285,8 +282,6 @@ static void get_lyrics_step_3 (const char * uri, const Index<char> & buf, void * } update_lyrics_window (state.title, state.artist, lyrics); - - g_free (lyrics); } static void get_lyrics_step_2 (const char * uri1, const Index<char> & buf, void *) @@ -309,6 +304,12 @@ static void get_lyrics_step_2 (const char * uri1, const Index<char> & buf, void str_printf (_("Unable to parse %s"), uri1)); return; } + else if (uri == String ("N/A")) + { + update_lyrics_window (state.title, state.artist, + _("No lyrics available")); + return; + } state.uri = uri; diff --git a/src/lyricwiki/lyricwiki.cc b/src/lyricwiki/lyricwiki.cc index 8fa8361..093d116 100644 --- a/src/lyricwiki/lyricwiki.cc +++ b/src/lyricwiki/lyricwiki.cc @@ -26,6 +26,7 @@ #include <libxml/HTMLparser.h> #include <libxml/xpath.h> +#define AUD_GLIB_INTEGRATION #include <libaudcore/drct.h> #include <libaudcore/i18n.h> #include <libaudcore/plugin.h> @@ -68,11 +69,10 @@ static void libxml_error_handler (void * ctx, const char * msg, ...) { } -/* g_free() returned text */ -static char * scrape_lyrics_from_lyricwiki_edit_page (const char * buf, int64_t len) +static CharPtr scrape_lyrics_from_lyricwiki_edit_page (const char * buf, int64_t len) { xmlDocPtr doc; - char * ret = nullptr; + CharPtr ret; /* * temporarily set our error-handling functor to our suppression function, @@ -127,12 +127,9 @@ give_up: (GRegexMatchFlags) 0, nullptr); g_regex_match (reg, (char *) lyric, G_REGEX_MATCH_NEWLINE_ANY, & match_info); - ret = g_match_info_fetch (match_info, 2); + ret.capture (g_match_info_fetch (match_info, 2)); if (! strcmp_nocase (ret, "<!-- PUT LYRICS HERE (and delete this entire line) -->")) - { - g_free (ret); - ret = g_strdup (_("No lyrics available")); - } + ret.capture (g_strdup (_("No lyrics available"))); g_regex_unref (reg); } @@ -160,7 +157,7 @@ static String scrape_uri_from_lyricwiki_search_result (const char * buf, int64_t reg = g_regex_new ("<(lyrics?)>.*</\\1>", (GRegexCompileFlags) (G_REGEX_MULTILINE | G_REGEX_DOTALL | G_REGEX_UNGREEDY), (GRegexMatchFlags) 0, nullptr); - char * newbuf = g_regex_replace_literal (reg, buf, len, 0, "", G_REGEX_MATCH_NEWLINE_ANY, nullptr); + CharPtr newbuf (g_regex_replace_literal (reg, buf, len, 0, "", G_REGEX_MATCH_NEWLINE_ANY, nullptr)); g_regex_unref (reg); /* @@ -238,8 +235,6 @@ static String scrape_uri_from_lyricwiki_search_result (const char * buf, int64_t xmlFreeDoc (doc); } - g_free (newbuf); - return uri; } @@ -258,7 +253,7 @@ static void get_lyrics_step_3 (const char * uri, const Index<char> & buf, void * return; } - char * lyrics = scrape_lyrics_from_lyricwiki_edit_page (buf.begin (), buf.len ()); + CharPtr lyrics = scrape_lyrics_from_lyricwiki_edit_page (buf.begin (), buf.len ()); if (! lyrics) { @@ -268,8 +263,6 @@ static void get_lyrics_step_3 (const char * uri, const Index<char> & buf, void * } update_lyrics_window (state.title, state.artist, lyrics, true); - - g_free (lyrics); } static void get_lyrics_step_2 (const char * uri1, const Index<char> & buf, void *) diff --git a/src/mpris2/plugin.cc b/src/mpris2/plugin.cc index 833cc62..3f439ee 100644 --- a/src/mpris2/plugin.cc +++ b/src/mpris2/plugin.cc @@ -50,7 +50,7 @@ EXPORT MPRIS2Plugin aud_plugin_instance; static GObject * object_core, * object_player; static String last_title, last_artist, last_album, last_file; static int last_length; -static const char * image_file; +static AudArtPtr image; static gboolean quit_cb (MprisMediaPlayer2 * object, GDBusMethodInvocation * call, void * unused) @@ -90,11 +90,7 @@ static void update_metadata (void * data, GObject * object) return; if (file != last_file) - { - if (image_file) - aud_art_unref (last_file); - image_file = file ? aud_art_request_file (file) : nullptr; - } + image = file ? aud_art_request (file, AUD_ART_FILE) : AudArtPtr (); last_title = title; last_artist = artist; @@ -146,6 +142,7 @@ static void update_metadata (void * data, GObject * object) elems[nelems ++] = g_variant_new_dict_entry (key, var); } + auto image_file = image.file (); if (image_file) { GVariant * key = g_variant_new_string ("mpris:artUrl"); @@ -292,17 +289,13 @@ void MPRIS2Plugin::cleanup () g_object_unref (object_core); g_object_unref (object_player); - if (image_file) - { - aud_art_unref (last_file); - image_file = nullptr; - } - last_title = String (); last_artist = String (); last_album = String (); last_file = String (); last_length = 0; + + image.clear (); } bool MPRIS2Plugin::init () diff --git a/src/neon/neon.cc b/src/neon/neon.cc index 6cddac0..0bfd7d8 100644 --- a/src/neon/neon.cc +++ b/src/neon/neon.cc @@ -458,9 +458,9 @@ int NeonFile::open_request (int64_t startbyte, String * error) m_request = ne_request_create (m_session, "GET", m_purl.path); if (startbyte > 0) - ne_print_request_header (m_request, "Range", "bytes=%" PRIu64 "-", startbyte); + ne_add_request_header (m_request, "Range", str_printf ("bytes=%" PRIu64 "-", startbyte)); - ne_print_request_header (m_request, "Icy-MetaData", "1"); + ne_add_request_header (m_request, "Icy-MetaData", "1"); /* Try to connect to the server. */ AUDDBG ("<%p> Connecting...\n", this); diff --git a/src/notify/event.cc b/src/notify/event.cc index 8f3a323..640c906 100644 --- a/src/notify/event.cc +++ b/src/notify/event.cc @@ -68,9 +68,11 @@ static void get_album_art () #ifdef USE_GTK if (aud_get_mainloop_type () == MainloopType::GLib) { - last_pixbuf = audgui_pixbuf_request_current (); - if (last_pixbuf) - audgui_pixbuf_scale_within (& last_pixbuf, audgui_get_dpi ()); + AudguiPixbuf pb = audgui_pixbuf_request_current (); + if (pb) + audgui_pixbuf_scale_within (pb, audgui_get_dpi ()); + + last_pixbuf = pb.release (); } #endif #ifdef USE_QT diff --git a/src/notify/osd.cc b/src/notify/osd.cc index 7af7255..cd7c73c 100644 --- a/src/notify/osd.cc +++ b/src/notify/osd.cc @@ -22,6 +22,7 @@ #include <libnotify/notify.h> +#define AUD_GLIB_INTEGRATION #include <libaudcore/drct.h> #include <libaudcore/i18n.h> #include <libaudcore/interface.h> @@ -78,7 +79,7 @@ static NotifyNotification * notification = nullptr; void osd_show (const char * title, const char * _message, const char * icon, GdkPixbuf * pixbuf) { - char * message = g_markup_escape_text (_message, -1); + CharPtr message (g_markup_escape_text (_message, -1)); if (pixbuf) icon = nullptr; @@ -96,8 +97,6 @@ void osd_show (const char * title, const char * _message, const char * icon, osd_setup_buttons (notification); notify_notification_show (notification, nullptr); - - g_free (message); } void osd_hide () diff --git a/src/playlist-manager-qt/playlist-manager-qt.cc b/src/playlist-manager-qt/playlist-manager-qt.cc index cf779ca..66ae68f 100644 --- a/src/playlist-manager-qt/playlist-manager-qt.cc +++ b/src/playlist-manager-qt/playlist-manager-qt.cc @@ -18,6 +18,7 @@ */ #include <QAbstractListModel> +#include <QBoxLayout> #include <QFont> #include <QGuiApplication> #include <QHeaderView> @@ -25,7 +26,6 @@ #include <QMouseEvent> #include <QToolButton> #include <QTreeView> -#include <QVBoxLayout> #include <libaudcore/hook.h> #include <libaudcore/i18n.h> @@ -47,6 +47,7 @@ public: constexpr PlaylistManagerQt () : GeneralPlugin (info, false) {} void * get_qt_widget (); + int take_message (const char * code, const void *, int); }; EXPORT PlaylistManagerQt aud_plugin_instance; @@ -61,8 +62,8 @@ public: }; PlaylistsModel () : - m_rows (aud_playlist_count ()), - m_playing (aud_playlist_get_playing ()), + m_rows (Playlist::n_playlists ()), + m_playing (Playlist::playing_playlist ().index ()), m_bold (QGuiApplication::font ()) { m_bold.setBold (true); @@ -105,6 +106,7 @@ public: protected: void currentChanged (const QModelIndex & current, const QModelIndex & previous); + void keyPressEvent (QKeyEvent * event); void mouseDoubleClickEvent (QMouseEvent * event); void dropEvent (QDropEvent * event); @@ -114,6 +116,9 @@ private: void update (Playlist::UpdateLevel level); void update_sel (); + void play_current () + { Playlist::by_index (currentIndex ().row ()).start_playback (); } + const HookReceiver<PlaylistsView, Playlist::UpdateLevel> update_hook {"playlist update", this, & PlaylistsView::update}; const HookReceiver<PlaylistsView> @@ -127,13 +132,16 @@ QVariant PlaylistsModel::data (const QModelIndex & index, int role) const switch (role) { case Qt::DisplayRole: + { + auto list = Playlist::by_index (index.row ()); switch (index.column ()) { case ColumnTitle: - return QString (aud_playlist_get_title (index.row ())); + return QString (list.get_title ()); case ColumnEntries: - return aud_playlist_entry_count (index.row ()); + return list.n_entries (); } + } break; case Qt::FontRole: @@ -178,10 +186,7 @@ void PlaylistsModel::update_rows (int row, int count) void PlaylistsModel::update_playing () { - if (aud_playlist_update_pending ()) - return; - - int playing = aud_playlist_get_playing (); + int playing = Playlist::playing_playlist ().index (); if (playing != m_playing) { @@ -196,7 +201,7 @@ void PlaylistsModel::update_playing () void PlaylistsModel::update (const Playlist::UpdateLevel level) { - int rows = aud_playlist_count (); + int rows = Playlist::n_playlists (); if (level == Playlist::Structure) { @@ -217,7 +222,7 @@ void PlaylistsModel::update (const Playlist::UpdateLevel level) if (level >= Playlist::Metadata) { update_rows (0, m_rows); - m_playing = aud_playlist_get_playing (); + m_playing = Playlist::playing_playlist ().index (); } else update_playing (); @@ -234,9 +239,10 @@ PlaylistsView::PlaylistsView () hdr->setStretchLastSection (false); hdr->setSectionResizeMode (PlaylistsModel::ColumnTitle, QHeaderView::Stretch); hdr->setSectionResizeMode (PlaylistsModel::ColumnEntries, QHeaderView::Interactive); - hdr->resizeSection (PlaylistsModel::ColumnEntries, 64); + hdr->resizeSection (PlaylistsModel::ColumnEntries, audqt::to_native_dpi (64)); setDragDropMode (InternalMove); + setFrameShape (QFrame::NoFrame); setIndentation (0); } @@ -244,13 +250,31 @@ void PlaylistsView::currentChanged (const QModelIndex & current, const QModelInd { QTreeView::currentChanged (current, previous); if (! m_in_update) - aud_playlist_set_active (current.row ()); + Playlist::by_index (current.row ()).activate (); +} + +void PlaylistsView::keyPressEvent (QKeyEvent * event) +{ + if (event->modifiers () == Qt::NoModifier) + { + switch (event->key ()) + { + case Qt::Key_Enter: + case Qt::Key_Return: + play_current (); + return; + default: + break; + } + } + + QTreeView::keyPressEvent (event); } void PlaylistsView::mouseDoubleClickEvent (QMouseEvent * event) { if (event->button () == Qt::LeftButton) - aud_playlist_play (currentIndex ().row ()); + play_current (); } void PlaylistsView::dropEvent (QDropEvent * event) @@ -267,11 +291,11 @@ void PlaylistsView::dropEvent (QDropEvent * event) { case AboveItem: to = indexAt (event->pos ()).row (); break; case BelowItem: to = indexAt (event->pos ()).row () + 1; break; - case OnViewport: to = aud_playlist_count (); break; + case OnViewport: to = Playlist::n_playlists (); break; default: return; } - aud_playlist_reorder (from, (to > from) ? to - 1 : to, 1); + Playlist::reorder_playlists (from, (to > from) ? to - 1 : to, 1); event->acceptProposedAction (); } @@ -285,16 +309,15 @@ void PlaylistsView::update (Playlist::UpdateLevel level) void PlaylistsView::update_sel () { - if (aud_playlist_update_pending ()) - return; - m_in_update ++; auto sel = selectionModel (); - auto current = m_model.index (aud_playlist_get_active (), 0); + auto current = m_model.index (Playlist::active_playlist ().index (), 0); sel->setCurrentIndex (current, sel->ClearAndSelect | sel->Rows); m_in_update --; } +static PlaylistsView * s_playlists_view = nullptr; + static QToolButton * new_tool_button (const char * text, const char * icon) { auto button = new QToolButton; @@ -306,33 +329,49 @@ static QToolButton * new_tool_button (const char * text, const char * icon) void * PlaylistManagerQt::get_qt_widget () { - auto widget = new QWidget; - auto vbox = new QVBoxLayout (widget); - vbox->setContentsMargins (0, 0, 0, 0); - - auto view = new PlaylistsView; - vbox->addWidget (view, 1); + s_playlists_view = new PlaylistsView; - auto hbox = new QHBoxLayout; - vbox->addLayout (hbox); + QObject::connect (s_playlists_view, & QObject::destroyed, [] () { + s_playlists_view = nullptr; + }); auto new_button = new_tool_button (N_("_New"), "document-new"); - QObject::connect (new_button, & QToolButton::clicked, aud_playlist_new); + QObject::connect (new_button, & QToolButton::clicked, Playlist::new_playlist); auto rename_button = new_tool_button (N_("Ren_ame"), "insert-text"); QObject::connect (rename_button, & QToolButton::clicked, [] () { - audqt::playlist_show_rename (aud_playlist_get_active ()); + audqt::playlist_show_rename (Playlist::active_playlist ()); }); auto remove_button = new_tool_button (N_("_Remove"), "edit-delete"); QObject::connect (remove_button, & QToolButton::clicked, [] () { - audqt::playlist_confirm_delete (aud_playlist_get_active ()); + audqt::playlist_confirm_delete (Playlist::active_playlist ()); }); + auto hbox = audqt::make_hbox (nullptr); + hbox->setContentsMargins (audqt::margins.TwoPt); + hbox->addWidget (new_button); hbox->addWidget (rename_button); hbox->addStretch (1); hbox->addWidget (remove_button); + auto widget = new QWidget; + auto vbox = audqt::make_vbox (widget, 0); + + vbox->addWidget (s_playlists_view, 1); + vbox->addLayout (hbox); + return widget; } + +int PlaylistManagerQt::take_message (const char * code, const void *, int) +{ + if (! strcmp (code, "grab focus") && s_playlists_view) + { + s_playlists_view->setFocus (Qt::OtherFocusReason); + return 0; + } + + return -1; +} diff --git a/src/playlist-manager/playlist-manager.cc b/src/playlist-manager/playlist-manager.cc index bb5f0d0..d05c03a 100644 --- a/src/playlist-manager/playlist-manager.cc +++ b/src/playlist-manager/playlist-manager.cc @@ -55,38 +55,39 @@ static void activate_row (void * user, int row); static void rename_cb (void * unused) { - audgui_show_playlist_rename (aud_playlist_get_active ()); + audgui_show_playlist_rename (Playlist::active_playlist ()); } static void delete_cb (void * unused) { - audgui_confirm_playlist_delete (aud_playlist_get_active ()); + audgui_confirm_playlist_delete (Playlist::active_playlist ()); } static void get_value (void * user, int row, int column, GValue * value) { + auto list = Playlist::by_index (row); + switch (column) { case 0: - { - g_value_set_string (value, aud_playlist_get_title (row)); + g_value_set_string (value, list.get_title ()); break; - } + case 1: - g_value_set_int (value, aud_playlist_entry_count (row)); + g_value_set_int (value, list.n_entries ()); break; } } static bool get_selected (void * user, int row) { - return (row == aud_playlist_get_active ()); + return (row == Playlist::active_playlist ().index ()); } static void set_selected (void * user, int row, bool selected) { if (selected) - aud_playlist_set_active (row); + Playlist::by_index (row).activate (); } static void select_all (void * user, bool selected) @@ -95,16 +96,17 @@ static void select_all (void * user, bool selected) static void activate_row (void * user, int row) { - aud_playlist_set_active (row); - aud_playlist_play (row); + auto playlist = Playlist::by_index (row); + playlist.activate (); + playlist.start_playback (); } static void shift_rows (void * user, int row, int before) { if (before < row) - aud_playlist_reorder (row, before, 1); + Playlist::reorder_playlists (row, before, 1); else if (before - 1 > row) - aud_playlist_reorder (row, before - 1, 1); + Playlist::reorder_playlists (row, before - 1, 1); } static const AudguiListCallbacks callbacks = { @@ -125,7 +127,7 @@ static gboolean search_cb (GtkTreeModel * model, int column, const char * key, int row = gtk_tree_path_get_indices (path)[0]; gtk_tree_path_free (path); - String title = aud_playlist_get_title (row); + String title = Playlist::by_index (row).get_title (); g_return_val_if_fail (title, true); Index<String> keys = str_list_to_index (key, " "); @@ -142,14 +144,11 @@ static gboolean search_cb (GtkTreeModel * model, int column, const char * key, return false; /* matched */ } -static gboolean position_changed = false; -static gboolean playlist_activated = false; - static void update_hook (void * data, void * list_) { auto level = aud::from_ptr<Playlist::UpdateLevel> (data); GtkWidget * list = (GtkWidget *) list_; - int rows = aud_playlist_count (); + int rows = Playlist::n_playlists (); if (level == Playlist::Structure) { @@ -160,48 +159,26 @@ static void update_hook (void * data, void * list_) else if (rows > old_rows) audgui_list_insert_rows (list, old_rows, rows - old_rows); - position_changed = true; - playlist_activated = true; + audgui_list_set_focus (list, Playlist::active_playlist ().index ()); + audgui_list_set_highlight (list, Playlist::playing_playlist ().index ()); + audgui_list_update_selection (list, 0, rows); } if (level >= Playlist::Metadata) audgui_list_update_rows (list, 0, rows); - - if (playlist_activated) - { - audgui_list_set_focus (list, aud_playlist_get_active ()); - audgui_list_update_selection (list, 0, rows); - playlist_activated = false; - } - - if (position_changed) - { - audgui_list_set_highlight (list, aud_playlist_get_playing ()); - position_changed = false; - } } static void activate_hook (void * data, void * list_) { GtkWidget * list = (GtkWidget *) list_; - - if (aud_playlist_update_pending ()) - playlist_activated = true; - else - { - audgui_list_set_focus (list, aud_playlist_get_active ()); - audgui_list_update_selection (list, 0, aud_playlist_count ()); - } + audgui_list_set_focus (list, Playlist::active_playlist ().index ()); + audgui_list_update_selection (list, 0, Playlist::n_playlists ()); } static void position_hook (void * data, void * list_) { GtkWidget * list = (GtkWidget *) list_; - - if (aud_playlist_update_pending ()) - position_changed = true; - else - audgui_list_set_highlight (list, aud_playlist_get_playing ()); + audgui_list_set_highlight (list, Playlist::playing_playlist ().index ()); } static void destroy_cb (GtkWidget * window) @@ -218,10 +195,10 @@ void * PlaylistManager::get_gtk_widget () GtkWidget * playman_vbox = gtk_vbox_new (false, 6); /* ListView */ - GtkWidget * playman_pl_lv = audgui_list_new (& callbacks, nullptr, aud_playlist_count ()); + GtkWidget * playman_pl_lv = audgui_list_new (& callbacks, nullptr, Playlist::n_playlists ()); audgui_list_add_column (playman_pl_lv, _("Title"), 0, G_TYPE_STRING, -1); audgui_list_add_column (playman_pl_lv, _("Entries"), 1, G_TYPE_INT, 7); - audgui_list_set_highlight (playman_pl_lv, aud_playlist_get_playing ()); + audgui_list_set_highlight (playman_pl_lv, Playlist::playing_playlist ().index ()); gtk_tree_view_set_search_equal_func ((GtkTreeView *) playman_pl_lv, search_cb, nullptr, nullptr); hook_associate ("playlist update", update_hook, playman_pl_lv); @@ -239,7 +216,7 @@ void * PlaylistManager::get_gtk_widget () /* ButtonBox */ GtkWidget * playman_button_hbox = gtk_hbox_new (false, 6); GtkWidget * new_button = audgui_button_new (_("_New"), "document-new", - (AudguiCallback) aud_playlist_new, nullptr); + [] (void *) { Playlist::new_playlist (); }, nullptr); GtkWidget * delete_button = audgui_button_new (_("_Remove"), "edit-delete", delete_cb, nullptr); GtkWidget * rename_button = audgui_button_new (_("Ren_ame"), "insert-text", rename_cb, nullptr); diff --git a/src/qtui/Makefile b/src/qtui/Makefile index 0a1823f..6576ef7 100644 --- a/src/qtui/Makefile +++ b/src/qtui/Makefile @@ -1,14 +1,16 @@ PLUGIN = qtui${PLUGIN_SUFFIX} SRCS = qtui.cc \ - dialog_windows.cc \ + dialogs-qt.cc \ main_window.cc \ + menu-ops.cc \ menus.cc \ playlist.cc \ + playlist_header.cc \ playlist_model.cc \ playlist_tabs.cc \ - filter_input.cc \ info_bar.cc \ + search_bar.cc \ status_bar.cc \ tool_bar.cc \ time_slider.cc \ diff --git a/src/qtui/dialogs-qt.cc b/src/qtui/dialogs-qt.cc new file mode 100644 index 0000000..70524e6 --- /dev/null +++ b/src/qtui/dialogs-qt.cc @@ -0,0 +1 @@ +#include "../ui-common/dialogs-qt.cc" diff --git a/src/qtui/filter_input.cc b/src/qtui/filter_input.cc deleted file mode 100644 index 98c6423..0000000 --- a/src/qtui/filter_input.cc +++ /dev/null @@ -1,56 +0,0 @@ -/* - * filter_input.cc - * Copyright 2014 Daniel (dmilith) Dettlaff - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#include <libaudcore/i18n.h> - -#include "filter_input.h" - -#include <QKeyEvent> - -FilterInput::FilterInput (QWidget * parent) : QLineEdit (parent) -{ -#ifdef Q_OS_MAC - setStyleSheet ( - "QLineEdit {" - " padding: 2px 4px;" - " border: 1px solid silver;" - " border-radius: 10px;" - " margin-right: 5px;" - "}" - "QLineEdit:focus {" - " border: 1px solid gray;" - "}" - ); -#endif - - setAttribute (Qt::WA_MacShowFocusRect, false); - setClearButtonEnabled (true); - setPlaceholderText (_("Search")); -} - -void FilterInput::keyPressEvent (QKeyEvent * e) -{ - if (e->key () == Qt::Key_Enter || e->key () == Qt::Key_Return) - { - e->ignore (); - focusNextChild (); - } - else - QLineEdit::keyPressEvent (e); -} diff --git a/src/qtui/info_bar.cc b/src/qtui/info_bar.cc index f80e5d7..c764c2a 100644 --- a/src/qtui/info_bar.cc +++ b/src/qtui/info_bar.cc @@ -20,22 +20,35 @@ #include <cmath> #include "info_bar.h" +#include "settings.h" #include <libaudcore/drct.h> #include <libaudcore/interface.h> +#include <libaudcore/runtime.h> #include <libaudqt/libaudqt.h> #include <QPainter> -static constexpr int Spacing = 8; -static constexpr int IconSize = 64; -static constexpr int Height = IconSize + 2 * Spacing; +static constexpr int FadeSteps = 10; static constexpr int VisBands = 12; -static constexpr int VisWidth = 8 * VisBands + Spacing - 2; -static constexpr int VisCenter = IconSize * 5 / 8 + Spacing; -static constexpr int VisDelay = 2; -static constexpr int VisFalloff = 2; +static constexpr int VisDelay = 2; /* delay before falloff in frames */ +static constexpr int VisFalloff = 2; /* falloff in decibels per frame */ + +struct PixelSizes +{ + int Spacing, IconSize, Height, BandWidth, BandSpacing, VisWidth, VisScale, VisCenter; + + PixelSizes (int dpi) : + Spacing (aud::rescale (dpi, 12, 1)), + IconSize (2 * aud::rescale (dpi, 3, 1)), // should be divisible by 2 + Height (IconSize + 2 * Spacing), + BandWidth (aud::rescale (dpi, 16, 1)), + BandSpacing (aud::rescale (dpi, 48, 1)), + VisWidth (VisBands * (BandWidth + BandSpacing) - BandSpacing + 2 * Spacing), + VisScale (aud::rescale (IconSize, 8, 5)), + VisCenter (VisScale + Spacing) {} +}; class InfoVis : public QWidget, Visualizer { @@ -48,14 +61,19 @@ public: void paintEvent (QPaintEvent *); + void enable (bool enabled); + const QGradient & gradient () const { return m_gradient; } + const PixelSizes & pixelSizes () const + { return ps; } private: + const PixelSizes ps; QLinearGradient m_gradient; QColor m_colors[VisBands], m_shadow[VisBands]; - char m_bars[VisBands] {}; + float m_bars[VisBands] {}; char m_delay[VisBands] {}; }; @@ -79,7 +97,8 @@ static void get_color (int i, QColor & color, QColor & shadow) InfoVis::InfoVis (QWidget * parent) : QWidget (parent), Visualizer (Freq), - m_gradient (0, 0, 0, Height) + ps (audqt::sizes.OneInch), + m_gradient (0, 0, 0, ps.Height) { m_gradient.setStops ({ {0, QColor (64, 64, 64)}, @@ -92,14 +111,12 @@ InfoVis::InfoVis (QWidget * parent) : get_color (i, m_colors[i], m_shadow[i]); setAttribute (Qt::WA_OpaquePaintEvent); - resize (VisWidth, Height); - - aud_visualizer_add (this); + resize (ps.VisWidth + 2 * ps.Spacing, ps.Height); } InfoVis::~InfoVis () { - aud_visualizer_remove (this); + enable (false); } void InfoVis::render_freq (const float * freq) @@ -127,8 +144,7 @@ void InfoVis::render_freq (const float * freq) } /* 40 dB range */ - int x = 40 + 20 * log10f (n); - x = aud::clamp (x, 0, 40); + float x = 40 + 20 * log10f (n); m_bars[i] -= aud::max (0, VisFalloff - m_delay[i]); @@ -156,89 +172,174 @@ void InfoVis::clear () void InfoVis::paintEvent (QPaintEvent *) { QPainter p (this); - p.fillRect (0, 0, VisWidth, Height, m_gradient); + p.fillRect (0, 0, ps.VisWidth, ps.Height, m_gradient); for (int i = 0; i < VisBands; i ++) { - int x = 8 * i; - int v = m_bars[i]; - int m = aud::min (VisCenter + v, Height); + int x = ps.Spacing + i * (ps.BandWidth + ps.BandSpacing); + int v = aud::clamp ((int) (m_bars[i] * ps.VisScale / 40), 0, ps.VisScale); + int m = aud::min (ps.VisCenter + v, ps.Height); - p.fillRect (x, VisCenter - v, 6, v, m_colors[i]); - p.fillRect (x, VisCenter, 6, m - VisCenter, m_shadow[i]); + p.fillRect (x, ps.VisCenter - v, ps.BandWidth, v, m_colors[i]); + p.fillRect (x, ps.VisCenter, ps.BandWidth, m - ps.VisCenter, m_shadow[i]); + } +} + +void InfoVis::enable (bool enabled) +{ + if (enabled) + aud_visualizer_add (this); + else + { + aud_visualizer_remove (this); + clear (); } } InfoBar::InfoBar (QWidget * parent) : QWidget (parent), - m_vis (new InfoVis (this)) + m_vis (new InfoVis (this)), + ps (m_vis->pixelSizes ()) { - setFixedHeight (Height); + update_vis (); + setFixedHeight (ps.Height); + + for (SongData & d : sd) + { + d.title.setTextFormat (Qt::PlainText); + d.artist.setTextFormat (Qt::PlainText); + d.album.setTextFormat (Qt::PlainText); + d.alpha = 0; + } - m_title.setTextFormat (Qt::PlainText); - m_artist.setTextFormat (Qt::PlainText); - m_album.setTextFormat (Qt::PlainText); + if (aud_drct_get_ready ()) + { + update_title (); + update_album_art (); - update_cb (); + /* skip fade-in */ + sd[Cur].alpha = FadeSteps; + } } void InfoBar::resizeEvent (QResizeEvent *) { - m_title.setText (QString ()); - m_vis->move (width () - VisWidth, 0); + for (SongData & d : sd) + d.title.setText (QString ()); + + m_vis->move (width () - ps.VisWidth, 0); } void InfoBar::paintEvent (QPaintEvent *) { QPainter p (this); - p.fillRect (0, 0, width () - VisWidth, Height, m_vis->gradient ()); + p.fillRect (0, 0, width () - ps.VisWidth, ps.Height, m_vis->gradient ()); - if (! m_art.isNull ()) + for (SongData & d : sd) { - int r = m_art.devicePixelRatio (); - int left = Spacing + (IconSize - m_art.width () / r) / 2; - int top = Spacing + (IconSize - m_art.height () / r) / 2; - p.drawPixmap (left, top, m_art); + p.setOpacity ((float) d.alpha / FadeSteps); + + if (! d.art.isNull ()) + { + int r = d.art.devicePixelRatio (); + int left = ps.Spacing + (ps.IconSize - d.art.width () / r) / 2; + int top = ps.Spacing + (ps.IconSize - d.art.height () / r) / 2; + p.drawPixmap (left, top, d.art); + } + + QFont font = p.font (); + font.setPointSize (18); + p.setFont (font); + + if (d.title.text ().isNull () && ! d.orig_title.isNull ()) + { + QFontMetrics metrics = p.fontMetrics (); + d.title = metrics.elidedText (d.orig_title, Qt::ElideRight, + width () - ps.VisWidth - ps.Height - ps.Spacing); + } + + p.setPen (QColor (255, 255, 255)); + p.drawStaticText (ps.Height, ps.Spacing, d.title); + + font.setPointSize (9); + p.setFont (font); + + p.drawStaticText (ps.Height, ps.Spacing + ps.IconSize / 2, d.artist); + + p.setPen (QColor (179, 179, 179)); + p.drawStaticText (ps.Height, ps.Spacing + ps.IconSize * 3 / 4, d.album); } +} + +void InfoBar::update_title () +{ + Tuple tuple = aud_drct_get_tuple (); + + sd[Cur].title.setText (QString ()); + sd[Cur].orig_title = tuple.get_str (Tuple::Title); + sd[Cur].artist.setText ((const char *) tuple.get_str (Tuple::Artist)); + sd[Cur].album.setText ((const char *) tuple.get_str (Tuple::Album)); + + update (); +} + +void InfoBar::update_album_art () +{ + sd[Cur].art = audqt::art_request_current (ps.IconSize, ps.IconSize); +} + +void InfoBar::next_song () +{ + sd[Prev] = std::move (sd[Cur]); + sd[Cur].alpha = 0; +} - QFont font = p.font (); - font.setPointSize (18); - p.setFont (font); +void InfoBar::do_fade () +{ + bool done = true; - if (m_title.text ().isNull () && ! m_original_title.isNull ()) + if (aud_drct_get_playing () && sd[Cur].alpha < FadeSteps) { - QFontMetrics metrics = p.fontMetrics (); - m_title = metrics.elidedText (m_original_title, Qt::ElideRight, - width () - VisWidth - Height - Spacing); + sd[Cur].alpha ++; + done = false; } - p.setPen (QColor (255, 255, 255)); - p.drawStaticText (Height, Spacing, m_title); - - font.setPointSize (9); - p.setFont (font); + if (sd[Prev].alpha > 0) + { + sd[Prev].alpha --; + done = false; + } - p.drawStaticText (Height, Spacing + IconSize / 2, m_artist); + update (); - p.setPen (QColor (179, 179, 179)); - p.drawStaticText (Height, Spacing + IconSize * 3 / 4, m_album); + if (done) + fade_timer.stop (); } -void InfoBar::update_metadata_cb () +void InfoBar::playback_ready_cb () { - Tuple tuple = aud_drct_get_tuple (); + if (! m_stopped) + next_song (); + + m_stopped = false; + update_title (); + update_album_art (); + + update (); + fade_timer.start (); +} - m_title.setText (QString ()); - m_original_title = tuple.get_str (Tuple::Title); - m_artist.setText ((const char *) tuple.get_str (Tuple::Artist)); - m_album.setText ((const char *) tuple.get_str (Tuple::Album)); +void InfoBar::playback_stop_cb () +{ + next_song (); + m_stopped = true; update (); + fade_timer.start (); } -void InfoBar::update_cb () +void InfoBar::update_vis () { - m_art = audqt::art_request_current (IconSize, IconSize); - update_metadata_cb (); + m_vis->enable (aud_get_bool ("qtui", "infoarea_show_vis")); } diff --git a/src/qtui/info_bar.h b/src/qtui/info_bar.h index 2404967..14542e8 100644 --- a/src/qtui/info_bar.h +++ b/src/qtui/info_bar.h @@ -26,6 +26,7 @@ #include <libaudcore/hook.h> class InfoVis; +struct PixelSizes; class InfoBar : public QWidget { @@ -36,18 +37,38 @@ public: void paintEvent (QPaintEvent *); private: - void update_metadata_cb (); - void update_cb (); + void update_title (); + void update_album_art (); + void next_song (); + void do_fade (); + + void playback_ready_cb (); + void playback_stop_cb (); + void update_vis (); const HookReceiver<InfoBar> - hook1 {"tuple change", this, & InfoBar::update_metadata_cb}, - hook2 {"playback ready", this, & InfoBar::update_cb}, - hook3 {"playback stop", this, & InfoBar::update_cb}; + hook1 {"tuple change", this, & InfoBar::update_title}, + hook2 {"playback ready", this, & InfoBar::playback_ready_cb}, + hook3 {"playback stop", this, & InfoBar::playback_stop_cb}, + hook4 {"qtui toggle infoarea_vis", this, & InfoBar::update_vis}; + + const Timer<InfoBar> + fade_timer {TimerRate::Hz30, this, & InfoBar::do_fade}; - QPixmap m_art; - QString m_original_title; - QStaticText m_title, m_artist, m_album; InfoVis * m_vis; + const PixelSizes & ps; + + struct SongData { + QPixmap art; + QString orig_title; + QStaticText title, artist, album; + int alpha; + }; + + enum {Prev = 0, Cur = 1}; /* index into SongData array */ + + SongData sd[2]; + bool m_stopped; }; #endif diff --git a/src/qtui/main_window.cc b/src/qtui/main_window.cc index 57e4791..4a16a65 100644 --- a/src/qtui/main_window.cc +++ b/src/qtui/main_window.cc @@ -26,11 +26,11 @@ #include <libaudqt/libaudqt.h> -#include "filter_input.h" #include "info_bar.h" #include "menus.h" #include "playlist.h" #include "playlist_tabs.h" +#include "settings.h" #include "status_bar.h" #include "time_slider.h" #include "tool_bar.h" @@ -40,6 +40,7 @@ #include <QCloseEvent> #include <QDockWidget> #include <QLabel> +#include <QMenuBar> #include <QSettings> #include <QToolButton> @@ -67,13 +68,32 @@ private: PluginHandle * m_plugin; }; +static QString get_config_name () +{ + int instance = aud_get_instance (); + return (instance == 1) ? + QString ("audacious") : + QString ("audacious-%1").arg (instance); +} + +static void toggle_search_tool (bool enable) +{ + auto search_tool = aud_plugin_lookup_basename ("search-tool-qt"); + if (search_tool) + aud_plugin_enable (search_tool, enable); +} + MainWindow::MainWindow () : + m_config_name (get_config_name ()), m_dialogs (this), - filterInput (new FilterInput (this)), - playlistTabs (new PlaylistTabs (this)), - infoBar (new InfoBar (this)), - centralWidget (new QWidget (this)), - centralLayout (new QVBoxLayout (centralWidget)) + m_menubar (qtui_build_menubar (this)), + m_playlist_tabs (new PlaylistTabs (this)), + m_center_widget (new QWidget (this)), + m_center_layout (audqt::make_vbox (m_center_widget, 0)), + m_infobar (new InfoBar (this)), + m_statusbar (new StatusBar (this)), + m_search_tool (aud_plugin_lookup_basename ("search-tool-qt")), + m_playlist_manager (aud_plugin_lookup_basename ("playlist-manager-qt")) { #if defined(Q_OS_WIN32) || defined(Q_OS_MAC) QIcon::setThemeName ("QtUi"); @@ -83,54 +103,52 @@ MainWindow::MainWindow () : QIcon::setThemeSearchPaths (paths); #endif - int instance = aud_get_instance (); - if (instance == 1) - m_config_name = "audacious"; - else - m_config_name = QString ("audacious-%1").arg (instance); - auto slider = new TimeSlider (this); const ToolBarItem items[] = { + ToolBarAction ("edit-find", N_("Search Library"), N_("Search Library"), toggle_search_tool, & m_search_action), ToolBarAction ("document-open", N_("Open Files"), N_("Open Files"), [] () { audqt::fileopener_show (audqt::FileMode::Open); }), ToolBarAction ("list-add", N_("Add Files"), N_("Add Files"), [] () { audqt::fileopener_show (audqt::FileMode::Add); }), ToolBarSeparator (), - ToolBarAction ("media-playback-play", N_("Play"), N_("Play"), aud_drct_play_pause, & toolButtonPlayPause), - ToolBarAction ("media-playback-stop", N_("Stop"), N_("Stop"), aud_drct_stop), ToolBarAction ("media-skip-backward", N_("Previous"), N_("Previous"), aud_drct_pl_prev), + ToolBarAction ("media-playback-play", N_("Play"), N_("Play"), aud_drct_play_pause, & m_play_pause_action), + ToolBarAction ("media-playback-stop", N_("Stop"), N_("Stop"), aud_drct_stop, & m_stop_action), + ToolBarAction ("media-playback-stop", N_("Stop After This Song"), N_("Stop After This Song"), + [] (bool on) { aud_set_bool (nullptr, "stop_after_current_song", on); }, & m_stop_after_action), ToolBarAction ("media-skip-forward", N_("Next"), N_("Next"), aud_drct_pl_next), + ToolBarAction ("media-record", N_("Record Stream"), N_("Record Stream"), + [] (bool on) { aud_set_bool (nullptr, "record", on); }, & m_record_action), ToolBarSeparator (), ToolBarCustom (slider), ToolBarCustom (slider->label ()), ToolBarSeparator (), ToolBarAction ("media-playlist-repeat", N_("Repeat"), N_("Repeat"), - [] (bool on) { aud_set_bool (nullptr, "repeat", on); }, & toolButtonRepeat), + [] (bool on) { aud_set_bool (nullptr, "repeat", on); }, & m_repeat_action), ToolBarAction ("media-playlist-shuffle", N_("Shuffle"), N_("Shuffle"), - [] (bool on) { aud_set_bool (nullptr, "shuffle", on); }, & toolButtonShuffle), - ToolBarCustom (audqt::volume_button_new (this)), - ToolBarCustom (filterInput), + [] (bool on) { aud_set_bool (nullptr, "shuffle", on); }, & m_shuffle_action), + ToolBarCustom (audqt::volume_button_new (this)) }; addToolBar (Qt::TopToolBarArea, new ToolBar (this, items)); - setUnifiedTitleAndToolBarOnMac (true); - updateToggles (); - - setStatusBar (new StatusBar (this)); - setCentralWidget (centralWidget); + if (m_search_tool) + aud_plugin_add_watch (m_search_tool, plugin_watcher, this); + else + m_search_action->setVisible (false); - centralLayout->addWidget (playlistTabs); - centralLayout->addWidget (infoBar); + update_toggles (); - centralLayout->setContentsMargins (0, 0, 0, 0); - centralLayout->setSpacing (4); + setStatusBar (m_statusbar); + setCentralWidget (m_center_widget); - connect (filterInput, & QLineEdit::textChanged, playlistTabs, & PlaylistTabs::filterTrigger); + m_center_layout->addWidget (m_playlist_tabs); + m_center_layout->addWidget (m_infobar); - setMenuBar (qtui_build_menubar (this)); + setMenuBar (m_menubar); + setDockNestingEnabled (true); add_dock_plugins (); if (aud_drct_get_playing ()) @@ -142,7 +160,8 @@ MainWindow::MainWindow () : else playback_stop_cb (); - readSettings (); + read_settings (); + update_visibility (); } MainWindow::~MainWindow () @@ -152,6 +171,9 @@ MainWindow::~MainWindow () settings.setValue ("windowState", saveState ()); remove_dock_plugins (); + + if (m_search_tool) + aud_plugin_remove_watch (m_search_tool, plugin_watcher, this); } void MainWindow::closeEvent (QCloseEvent * e) @@ -166,34 +188,34 @@ void MainWindow::closeEvent (QCloseEvent * e) e->ignore (); } -void MainWindow::readSettings () +void MainWindow::keyPressEvent (QKeyEvent * event) { - QSettings settings (m_config_name, "QtUi"); + if (event->modifiers () == Qt::NoModifier && event->key () == Qt::Key_Escape) + { + auto widget = m_playlist_tabs->currentPlaylistWidget (); - if (! restoreGeometry (settings.value ("geometry").toByteArray ())) - resize (768, 480); + if (widget->hasFocus ()) + widget->scrollToCurrent (true); + else + widget->setFocus (Qt::OtherFocusReason); - restoreState (settings.value ("windowState").toByteArray ()); + return; + } + + QMainWindow::keyPressEvent (event); } -void MainWindow::keyPressEvent (QKeyEvent * e) +void MainWindow::read_settings () { - switch (e->modifiers ()) - { - case Qt::ControlModifier: - switch (e->key ()) - { - case Qt::Key_F: - filterInput->setFocus (); - break; - } - break; - } + QSettings settings (m_config_name, "QtUi"); - QMainWindow::keyPressEvent (e); + if (! restoreGeometry (settings.value ("geometry").toByteArray ())) + resize (audqt::to_native_dpi (768), audqt::to_native_dpi (480)); + + restoreState (settings.value ("windowState").toByteArray ()); } -void MainWindow::setWindowTitle (const QString & title) +void MainWindow::set_title (const QString & title) { int instance = aud_get_instance (); if (instance == 1) @@ -202,25 +224,43 @@ void MainWindow::setWindowTitle (const QString & title) QMainWindow::setWindowTitle (QString ("%1 (%2)").arg (title).arg (instance)); } -void MainWindow::updateToggles () +void MainWindow::update_toggles () { - toolButtonRepeat->setChecked (aud_get_bool (nullptr, "repeat")); - toolButtonShuffle->setChecked (aud_get_bool (nullptr, "shuffle")); + if (m_search_tool) + m_search_action->setChecked (aud_plugin_get_enabled (m_search_tool)); + + bool stop_after = aud_get_bool (nullptr, "stop_after_current_song"); + m_stop_action->setVisible (! stop_after); + m_stop_after_action->setVisible (stop_after); + m_stop_after_action->setChecked (stop_after); + + m_record_action->setVisible (aud_drct_get_record_enabled ()); + m_record_action->setChecked (aud_get_bool (nullptr, "record")); + + m_repeat_action->setChecked (aud_get_bool (nullptr, "repeat")); + m_shuffle_action->setChecked (aud_get_bool (nullptr, "shuffle")); +} + +void MainWindow::update_visibility () +{ + m_menubar->setVisible (aud_get_bool ("qtui", "menu_visible")); + m_infobar->setVisible (aud_get_bool ("qtui", "infoarea_visible")); + m_statusbar->setVisible (aud_get_bool ("qtui", "statusbar_visible")); } void MainWindow::update_play_pause () { if (! aud_drct_get_playing () || aud_drct_get_paused ()) { - toolButtonPlayPause->setIcon (QIcon::fromTheme ("media-playback-start")); - toolButtonPlayPause->setText (_("Play")); - toolButtonPlayPause->setToolTip (_("Play")); + m_play_pause_action->setIcon (QIcon::fromTheme ("media-playback-start")); + m_play_pause_action->setText (_("Play")); + m_play_pause_action->setToolTip (_("Play")); } else { - toolButtonPlayPause->setIcon (QIcon::fromTheme ("media-playback-pause")); - toolButtonPlayPause->setText (_("Pause")); - toolButtonPlayPause->setToolTip (_("Pause")); + m_play_pause_action->setIcon (QIcon::fromTheme ("media-playback-pause")); + m_play_pause_action->setText (_("Pause")); + m_play_pause_action->setToolTip (_("Pause")); } } @@ -229,8 +269,8 @@ void MainWindow::title_change_cb () auto title = aud_drct_get_title (); if (title) { - setWindowTitle (QString (title) + QString (" - Audacious")); - buffering_timer.stop (); + set_title (QString (title) + QString (" - Audacious")); + m_buffering_timer.stop (); } } @@ -238,58 +278,53 @@ void MainWindow::playback_begin_cb () { update_play_pause (); - int last_list = aud_playlist_by_unique_id (playing_id); - auto last_widget = playlistTabs->playlistWidget (last_list); + auto last_widget = m_playlist_tabs->playlistWidget (m_last_playing.index ()); if (last_widget) last_widget->updatePlaybackIndicator (); - int list = aud_playlist_get_playing (); - auto widget = playlistTabs->playlistWidget (list); + auto playing = Playlist::playing_playlist (); + auto widget = m_playlist_tabs->playlistWidget (playing.index ()); if (widget) widget->scrollToCurrent (); if (widget && widget != last_widget) widget->updatePlaybackIndicator (); - playing_id = aud_playlist_get_unique_id (list); + m_last_playing = playing; - buffering_timer.queue (250, [] (void * me) { - ((MainWindow *) me)->setWindowTitle (_("Buffering ...")); - }, this); + m_buffering_timer.queue (250, aud::obj_member<MainWindow, & MainWindow::buffering_cb>, this); +} + +void MainWindow::buffering_cb () +{ + set_title (_("Buffering ...")); } void MainWindow::pause_cb () { update_play_pause (); - int list = aud_playlist_by_unique_id (playing_id); - auto widget = playlistTabs->playlistWidget (list); + auto widget = m_playlist_tabs->playlistWidget (m_last_playing.index ()); if (widget) widget->updatePlaybackIndicator (); } void MainWindow::playback_stop_cb () { - setWindowTitle ("Audacious"); - buffering_timer.stop (); + set_title ("Audacious"); + m_buffering_timer.stop (); update_play_pause (); - int last_list = aud_playlist_by_unique_id (playing_id); - auto last_widget = playlistTabs->playlistWidget (last_list); + auto last_widget = m_playlist_tabs->playlistWidget (m_last_playing.index ()); if (last_widget) last_widget->updatePlaybackIndicator (); - playing_id = -1; -} - -void MainWindow::update_toggles_cb () -{ - updateToggles (); + m_last_playing = Playlist (); } PluginWidget * MainWindow::find_dock_plugin (PluginHandle * plugin) { - for (PluginWidget * w : dock_widgets) + for (PluginWidget * w : m_dock_widgets) { if (w->plugin () == plugin) return w; @@ -298,6 +333,12 @@ PluginWidget * MainWindow::find_dock_plugin (PluginHandle * plugin) return nullptr; } +void MainWindow::show_dock_plugin (PluginHandle * plugin) +{ + aud_plugin_enable (plugin, true); + aud_plugin_send_message (plugin, "grab focus", nullptr, 0); +} + void MainWindow::add_dock_plugin_cb (PluginHandle * plugin) { QWidget * widget = (QWidget *) aud_plugin_get_qt_widget (plugin); @@ -308,7 +349,7 @@ void MainWindow::add_dock_plugin_cb (PluginHandle * plugin) if (! w) { w = new PluginWidget (plugin); - dock_widgets.append (w); + m_dock_widgets.append (w); } w->setWidget (widget); diff --git a/src/qtui/main_window.h b/src/qtui/main_window.h index 654abdd..c56bb87 100644 --- a/src/qtui/main_window.h +++ b/src/qtui/main_window.h @@ -21,19 +21,19 @@ #define MAIN_WINDOW_H #include <libaudcore/hook.h> -#include <libaudcore/index.h> #include <libaudcore/mainloop.h> +#include <libaudcore/playlist.h> -#include "dialog_windows.h" +#include "../ui-common/dialogs-qt.h" #include <QMainWindow> -class FilterInput; class InfoBar; class PlaylistTabs; class PluginHandle; class PluginWidget; class QVBoxLayout; +class StatusBar; class MainWindow : public QMainWindow { @@ -44,40 +44,54 @@ public: private: QString m_config_name; DialogWindows m_dialogs; - FilterInput * filterInput; - PlaylistTabs * playlistTabs; - InfoBar * infoBar; - QWidget * centralWidget; - QVBoxLayout * centralLayout; + QMenuBar * m_menubar; + PlaylistTabs * m_playlist_tabs; + QWidget * m_center_widget; + QVBoxLayout * m_center_layout; + InfoBar * m_infobar; + StatusBar * m_statusbar; - QAction * toolButtonPlayPause; - QAction * toolButtonRepeat; - QAction * toolButtonShuffle; + PluginHandle * m_search_tool, * m_playlist_manager; + Index<PluginWidget *> m_dock_widgets; - QueuedFunc buffering_timer; + QAction * m_search_action; + QAction * m_play_pause_action, * m_stop_action, * m_stop_after_action; + QAction * m_record_action; + QAction * m_repeat_action, * m_shuffle_action; - void closeEvent (QCloseEvent * e); - void keyPressEvent (QKeyEvent * e); - - void setWindowTitle (const QString & title); - - void updateToggles (); - void readSettings (); + QueuedFunc m_buffering_timer; + Playlist m_last_playing; - void add_dock_plugins (); - void remove_dock_plugins (); + void closeEvent (QCloseEvent * e); + void keyPressEvent (QKeyEvent * event); + void read_settings (); + void set_title (const QString & title); + void update_toggles (); + void update_visibility (); void update_play_pause (); + void title_change_cb (); void playback_begin_cb (); + void buffering_cb (); void playback_ready_cb (); void pause_cb (); void playback_stop_cb (); - void update_toggles_cb (); PluginWidget * find_dock_plugin (PluginHandle * plugin); + void show_dock_plugin (PluginHandle * plugin); void add_dock_plugin_cb (PluginHandle * plugin); void remove_dock_plugin_cb (PluginHandle * plugin); + void add_dock_plugins (); + void remove_dock_plugins (); + + void show_search_tool () + { if (m_search_tool) show_dock_plugin (m_search_tool); } + void show_playlist_manager () + { if (m_playlist_manager) show_dock_plugin (m_playlist_manager); } + + static bool plugin_watcher (PluginHandle *, void * me) + { ((MainWindow *) me)->update_toggles (); return true; } const HookReceiver<MainWindow> hook1 {"title change", this, & MainWindow::title_change_cb}, @@ -86,17 +100,20 @@ private: hook4 {"playback pause", this, & MainWindow::pause_cb}, hook5 {"playback unpause", this, & MainWindow::pause_cb}, hook6 {"playback stop", this, & MainWindow::playback_stop_cb}, - hook7 {"set repeat", this, & MainWindow::update_toggles_cb}, - hook8 {"set shuffle", this, & MainWindow::update_toggles_cb}, - hook9 {"set no_playlist_advance", this, & MainWindow::update_toggles_cb}, - hook10 {"set stop_after_current_song", this, & MainWindow::update_toggles_cb}; + hook7 {"set stop_after_current_song", this, & MainWindow::update_toggles}, + hook8 {"enable record", this, & MainWindow::update_toggles}, + hook9 {"set record", this, & MainWindow::update_toggles}, + hook10 {"set repeat", this, & MainWindow::update_toggles}, + hook11 {"set shuffle", this, & MainWindow::update_toggles}, + hook12 {"qtui toggle menubar", this, & MainWindow::update_visibility}, + hook13 {"qtui toggle infoarea", this, & MainWindow::update_visibility}, + hook14 {"qtui toggle statusbar", this, & MainWindow::update_visibility}, + hook15 {"qtui show search tool", this, & MainWindow::show_search_tool}, + hook16 {"qtui show playlist manager", this, & MainWindow::show_playlist_manager}; const HookReceiver<MainWindow, PluginHandle *> plugin_hook1 {"dock plugin enabled", this, & MainWindow::add_dock_plugin_cb}, plugin_hook2 {"dock plugin disabled", this, & MainWindow::remove_dock_plugin_cb}; - - Index<PluginWidget *> dock_widgets; - int playing_id = -1; }; #endif diff --git a/src/qtui/menu-ops.cc b/src/qtui/menu-ops.cc new file mode 100644 index 0000000..21b5eda --- /dev/null +++ b/src/qtui/menu-ops.cc @@ -0,0 +1,2 @@ +#include "../ui-common/menu-ops.cc" +#include "../ui-common/menu-ops-qt.cc" diff --git a/src/qtui/menus.cc b/src/qtui/menus.cc index d3ca365..0a8c729 100644 --- a/src/qtui/menus.cc +++ b/src/qtui/menus.cc @@ -44,10 +44,21 @@ static void add_folder () { audqt::fileopener_show (audqt::FileMode::AddFolder); static void open_url () { audqt::urlopener_show (true); } static void add_url () { audqt::urlopener_show (false); } +static void pl_find () { hook_call ("qtui find", nullptr); } static void pl_rename () { hook_call ("qtui rename playlist", nullptr); } -static void pl_close () { audqt::playlist_confirm_delete (aud_playlist_get_active ()); } +static void pl_close () { audqt::playlist_confirm_delete (Playlist::active_playlist ()); } static void configure_effects () { audqt::prefswin_show_plugin_page (PluginType::Effect); } +static void configure_output () { audqt::prefswin_show_plugin_page (PluginType::Output); } +static void configure_visualizations () { audqt::prefswin_show_plugin_page (PluginType::Vis); } + +static void show_search_tool () { hook_call ("qtui show search tool", nullptr); } +static void show_playlist_manager () { hook_call ("qtui show playlist manager", nullptr); } +static void toggle_menubar () { hook_call ("qtui toggle menubar", nullptr); } +static void toggle_infoarea () { hook_call ("qtui toggle infoarea", nullptr); } +static void toggle_infoarea_vis () { hook_call ("qtui toggle infoarea_vis", nullptr); } +static void toggle_statusbar () { hook_call ("qtui toggle statusbar", nullptr); } +static void toggle_remaining_time () { hook_call ("qtui toggle remaining time", nullptr); } QMenuBar * qtui_build_menubar (QWidget * parent) { @@ -59,6 +70,8 @@ QMenuBar * qtui_build_menubar (QWidget * parent) audqt::MenuCommand ({N_("_Add Folder ..."), "list-add"}, add_folder), audqt::MenuCommand ({N_("Add U_RL ..."), "folder-remote", "Ctrl+Shift+L"}, add_url), audqt::MenuSep (), + audqt::MenuCommand ({N_("Search _Library"), "edit-find", "Ctrl+Y"}, show_search_tool), + audqt::MenuSep (), audqt::MenuCommand ({N_("A_bout ..."), "help-about"}, aud_ui_show_about_window), audqt::MenuCommand ({N_("_Settings ..."), "preferences-system"}, aud_ui_show_prefs_window), audqt::MenuSep (), @@ -100,6 +113,7 @@ QMenuBar * qtui_build_menubar (QWidget * parent) audqt::MenuCommand ({N_("By _Length")}, sort_length), audqt::MenuCommand ({N_("By _File Path")}, sort_path), audqt::MenuCommand ({N_("By _Custom Title")}, sort_custom_title), + audqt::MenuCommand ({N_("By C_omment")}, sort_comment), audqt::MenuSep (), audqt::MenuCommand ({N_("R_everse Order"), "view-sort-descending"}, sort_reverse), audqt::MenuCommand ({N_("_Random Order")}, sort_random) @@ -116,6 +130,7 @@ QMenuBar * qtui_build_menubar (QWidget * parent) audqt::MenuCommand ({N_("By _Length")}, sort_sel_length), audqt::MenuCommand ({N_("By _File Path")}, sort_sel_path), audqt::MenuCommand ({N_("By _Custom Title")}, sort_sel_custom_title), + audqt::MenuCommand ({N_("By C_omment")}, sort_sel_comment), audqt::MenuSep (), audqt::MenuCommand ({N_("R_everse Order"), "view-sort-descending"}, sort_sel_reverse), audqt::MenuCommand ({N_("_Random Order")}, sort_sel_random) @@ -125,19 +140,21 @@ QMenuBar * qtui_build_menubar (QWidget * parent) audqt::MenuCommand ({N_("_Play/Resume"), "media-playback-start", "Shift+Return"}, pl_play), audqt::MenuCommand ({N_("_Refresh"), "view-refresh", "F5"}, pl_refresh), audqt::MenuSep (), + audqt::MenuCommand ({N_("_Find ..."), "edit-find", "Ctrl+F"}, pl_find), + audqt::MenuSep (), audqt::MenuSub ({N_("_Sort"), "view-sort-ascending"}, sort_items), audqt::MenuSub ({N_("Sort Se_lected"), "view-sort-ascending"}, sort_selected_items), audqt::MenuSub ({N_("Remove _Duplicates"), "edit-copy"}, dupe_items), audqt::MenuCommand ({N_("Remove _Unavailable Files"), "dialog-warning"}, pl_remove_failed), audqt::MenuSep (), - audqt::MenuCommand ({N_("_New"), "document-new", "Ctrl+T"}, (audqt::MenuFunc) aud_playlist_new), + audqt::MenuCommand ({N_("_New"), "document-new", "Ctrl+T"}, pl_new), audqt::MenuCommand ({N_("Ren_ame ..."), "insert-text", "F2"}, pl_rename), audqt::MenuCommand ({N_("Remo_ve"), "edit-delete", "Ctrl+W"}, pl_close), audqt::MenuSep (), //audqt::MenuCommand ({N_("_Import ..."), "document-open"}, TODO), //audqt::MenuCommand ({N_("_Export ..."), "document-save"}, TODO), audqt::MenuSep (), - //audqt::MenuCommand ({N_("Playlist _Manager ..."), "audio-x-generic", "Ctrl+P"}, TODO), + audqt::MenuCommand ({N_("Playlist _Manager ..."), "audio-x-generic", "Ctrl+P"}, show_playlist_manager), audqt::MenuCommand ({N_("_Queue Manager ..."), nullptr, "Ctrl+U"}, audqt::queue_manager_show) }; @@ -146,8 +163,21 @@ QMenuBar * qtui_build_menubar (QWidget * parent) audqt::MenuCommand ({N_("Volume _Down"), "audio-volume-low", "Ctrl+-"}, volume_down), audqt::MenuSep (), audqt::MenuCommand ({N_("_Equalizer ..."), "multimedia-volume-control", "Ctrl+E"}, audqt::equalizer_show), + audqt::MenuCommand ({N_("E_ffects ..."), "preferences-system"}, configure_effects), + audqt::MenuSep (), + audqt::MenuToggle ({N_("Record Stream"), "media-record", "Ctrl+D"}, {nullptr, "record", "set record"}), + audqt::MenuCommand ({N_("Audio Settings ..."), "audio-card"}, configure_output) + }; + + static const audqt::MenuItem view_items[] = { + audqt::MenuToggle ({N_("Show _Menu Bar"), nullptr, "Shift+Ctrl+M"}, {"qtui", "menu_visible"}, toggle_menubar), + audqt::MenuToggle ({N_("Show I_nfo Bar"), nullptr, "Shift+Ctrl+I"}, {"qtui", "infoarea_visible"}, toggle_infoarea), + audqt::MenuToggle ({N_("Show Info Bar Vis_ualization")}, {"qtui", "infoarea_show_vis"}, toggle_infoarea_vis), + audqt::MenuToggle ({N_("Show _Status Bar"), nullptr, "Shift+Ctrl+S"}, {"qtui", "statusbar_visible"}, toggle_statusbar), + audqt::MenuSep (), + audqt::MenuToggle ({N_("Show _Remaining Time"), nullptr, "Shift+Ctrl+R"}, {"qtui", "show_remaining_time", "qtui toggle remaining time"}, toggle_remaining_time), audqt::MenuSep (), - audqt::MenuCommand ({N_("E_ffects ...")}, configure_effects) + audqt::MenuCommand ({N_("_Visualizations ..."), "preferences-system"}, configure_visualizations) }; static const audqt::MenuItem main_items[] = { @@ -156,6 +186,7 @@ QMenuBar * qtui_build_menubar (QWidget * parent) audqt::MenuSub ({N_("P_laylist")}, playlist_items), audqt::MenuSub ({N_("_Services")}, services_menu), audqt::MenuSub ({N_("_Output")}, output_items), + audqt::MenuSub ({N_("_View")}, view_items) }; return audqt::menubar_build (main_items, parent); diff --git a/src/qtui/playlist.cc b/src/qtui/playlist.cc index 001193f..7c4c02e 100644 --- a/src/qtui/playlist.cc +++ b/src/qtui/playlist.cc @@ -17,6 +17,7 @@ * the use of this software. */ +#include <QHeaderView> #include <QKeyEvent> #include <QMenu> #include <QSortFilterProxyModel> @@ -28,50 +29,42 @@ #include <libaudcore/runtime.h> #include "playlist.h" +#include "playlist_header.h" #include "playlist_model.h" #include "../ui-common/menu-ops.h" -PlaylistWidget::PlaylistWidget (QWidget * parent, int uniqueId) : QTreeView (parent) +PlaylistWidget::PlaylistWidget (QWidget * parent, Playlist playlist) : + QTreeView (parent), + m_playlist (playlist), + model (new PlaylistModel (this, playlist)), + proxyModel (new PlaylistProxyModel (this, playlist)) { - model = new PlaylistModel (nullptr, uniqueId); - /* setting up filtering model */ - proxyModel = new QSortFilterProxyModel (this); proxyModel->setSourceModel (model); - proxyModel->setFilterKeyColumn (-1); /* filter by all columns */ inUpdate = true; /* prevents changing focused row */ setModel (proxyModel); inUpdate = false; + auto header = new PlaylistHeader (this); + setHeader (header); + setAllColumnsShowFocus (true); setAlternatingRowColors (true); setAttribute (Qt::WA_MacShowFocusRect, false); - setIndentation (0); setUniformRowHeights (true); setFrameShape (QFrame::NoFrame); setSelectionMode (ExtendedSelection); + setDragDropMode (DragDrop); updateSettings (); - - /* TODO: set column width based on font size */ - setColumnWidth (PL_COL_NOW_PLAYING, 25); - setColumnWidth (PL_COL_TITLE, 275); - setColumnWidth (PL_COL_ARTIST, 175); - setColumnWidth (PL_COL_ALBUM, 175); - setColumnWidth (PL_COL_QUEUED, 25); - setColumnWidth (PL_COL_LENGTH, 50); + header->updateColumns (); /* get initial selection and focus from core */ - Playlist::Update upd {}; - upd.level = Playlist::Selection; - update (upd); -} - -void PlaylistWidget::setFilter (const QString & text) -{ - proxyModel->setFilterRegExp (QRegExp (text, Qt::CaseInsensitive, QRegExp::FixedString)); + inUpdate = true; + updateSelection (0, 0); + inUpdate = false; } PlaylistWidget::~PlaylistWidget () @@ -85,7 +78,7 @@ QModelIndex PlaylistWidget::rowToIndex (int row) if (row < 0) return QModelIndex (); - return proxyModel->mapFromSource (model->index (row)); + return proxyModel->mapFromSource (model->index (row, firstVisibleColumn)); } int PlaylistWidget::indexToRow (const QModelIndex & index) @@ -109,25 +102,22 @@ void PlaylistWidget::keyPressEvent (QKeyEvent * event) case Qt::NoModifier: switch (event->key ()) { - case Qt::Key_Escape: - scrollToCurrent (); - break; case Qt::Key_Enter: case Qt::Key_Return: playCurrentIndex (); - break; + return; case Qt::Key_Right: - aud_drct_seek (aud_drct_get_time () + 5000); - break; + aud_drct_seek (aud_drct_get_time () + aud_get_double ("qtui", "step_size") * 1000); + return; case Qt::Key_Left: - aud_drct_seek (aud_drct_get_time () - 5000); - break; + aud_drct_seek (aud_drct_get_time () - aud_get_double ("qtui", "step_size") * 1000); + return; case Qt::Key_Space: aud_drct_play_pause (); - break; + return; case Qt::Key_Delete: pl_remove_selected (); - break; + return; case Qt::Key_Z: aud_drct_pl_prev (); return; @@ -160,12 +150,56 @@ void PlaylistWidget::mouseDoubleClickEvent (QMouseEvent * event) playCurrentIndex (); } +/* Since Qt doesn't support both DragDrop and InternalMove at once, + * this hack is needed to set the drag icon to "move" for internal drags. */ +void PlaylistWidget::dragMoveEvent (QDragMoveEvent * event) +{ + if (event->source () == this) + event->setDropAction (Qt::MoveAction); + + QTreeView::dragMoveEvent (event); + + if (event->source () == this) + event->setDropAction (Qt::MoveAction); +} + +void PlaylistWidget::dropEvent (QDropEvent * event) +{ + /* let Qt forward external drops to the PlaylistModel */ + if (event->source () != this) + return QTreeView::dropEvent (event); + + int from = indexToRow (currentIndex ()); + if (from < 0) + return; + + int to; + switch (dropIndicatorPosition ()) + { + case AboveItem: to = indexToRow (indexAt (event->pos ())); break; + case BelowItem: to = indexToRow (indexAt (event->pos ())) + 1; break; + case OnViewport: to = m_playlist.n_entries (); break; + default: return; + } + + /* Adjust the shift amount so that the selected entry closest to the + * destination ends up at the destination. */ + if (to > from) + to -= m_playlist.n_selected (from, to - from); + else + to += m_playlist.n_selected (to, from - to); + + m_playlist.shift_entries (from, to - from); + + event->acceptProposedAction (); +} + void PlaylistWidget::currentChanged (const QModelIndex & current, const QModelIndex & previous) { QTreeView::currentChanged (current, previous); if (! inUpdate) - aud_playlist_set_focus (playlist (), indexToRow (current)); + m_playlist.set_focus (indexToRow (current)); } void PlaylistWidget::selectionChanged (const QItemSelection & selected, @@ -175,66 +209,49 @@ void PlaylistWidget::selectionChanged (const QItemSelection & selected, if (! inUpdate) { - int list = playlist (); - for (const QModelIndex & idx : selected.indexes ()) - aud_playlist_entry_set_selected (list, indexToRow (idx), true); + m_playlist.select_entry (indexToRow (idx), true); for (const QModelIndex & idx : deselected.indexes ()) - aud_playlist_entry_set_selected (list, indexToRow (idx), false); + m_playlist.select_entry (indexToRow (idx), false); } } -int PlaylistWidget::playlist () const -{ - return model->playlist (); -} - -int PlaylistWidget::uniqueId () const -{ - return model->uniqueId (); -} - -void PlaylistWidget::scrollToCurrent () +void PlaylistWidget::scrollToCurrent (bool force) { - int list = playlist (); - int entry = aud_playlist_get_position (list); + int entry = m_playlist.get_position (); - aud_playlist_select_all (list, false); - aud_playlist_entry_set_selected (list, entry, true); - aud_playlist_set_focus (list, entry); + if (aud_get_bool ("qtui", "autoscroll") || force) + { + m_playlist.select_all (false); + m_playlist.select_entry (entry, true); + m_playlist.set_focus (entry); - // a playlist update should have been queued, unless the playlist is empty - if (aud_playlist_update_pending (list)) - scrollQueued = true; + scrollTo (rowToIndex (entry)); + } } void PlaylistWidget::updatePlaybackIndicator () { - int list = playlist (); - - if (aud_playlist_update_pending (list)) - needIndicatorUpdate = true; - else if (currentPos >= 0) - model->updateRows (currentPos, 1); + if (currentPos >= 0) + model->entriesChanged (currentPos, 1); } -void PlaylistWidget::getSelectedRanges (const Playlist::Update & update, +void PlaylistWidget::getSelectedRanges (int rowsBefore, int rowsAfter, QItemSelection & selected, QItemSelection & deselected) { - int list = playlist (); - int entries = aud_playlist_entry_count (list); + int entries = m_playlist.n_entries (); QItemSelection ranges[2]; QModelIndex first, last; bool prev = false; - for (int row = update.before; row < entries - update.after; row ++) + for (int row = rowsBefore; row < entries - rowsAfter; row ++) { auto idx = rowToIndex (row); if (! idx.isValid ()) continue; - bool sel = aud_playlist_entry_get_selected (list, row); + bool sel = m_playlist.entry_selected (row); if (sel != prev && first.isValid ()) ranges[prev].merge (QItemSelection (first, last), QItemSelectionModel::Select); @@ -253,12 +270,31 @@ void PlaylistWidget::getSelectedRanges (const Playlist::Update & update, deselected = std::move (ranges[false]); } -void PlaylistWidget::update (const Playlist::Update & update) +void PlaylistWidget::updateSelection (int rowsBefore, int rowsAfter) +{ + QItemSelection selected, deselected; + getSelectedRanges (rowsBefore, rowsAfter, selected, deselected); + + auto sel = selectionModel (); + + if (! selected.isEmpty ()) + sel->select (selected, sel->Select | sel->Rows); + if (! deselected.isEmpty ()) + sel->select (deselected, sel->Deselect | sel->Rows); + + sel->setCurrentIndex (rowToIndex (m_playlist.get_focus ()), sel->NoUpdate); +} + +void PlaylistWidget::playlistUpdate () { + auto update = m_playlist.update_detail (); + + if (update.level == Playlist::NoUpdate) + return; + inUpdate = true; - int list = playlist (); - int entries = aud_playlist_entry_count (list); + int entries = m_playlist.n_entries (); int changed = entries - update.before - update.after; if (update.level == Playlist::Structure) @@ -271,61 +307,96 @@ void PlaylistWidget::update (const Playlist::Update & update) else if (currentPos >= update.before) currentPos = -1; - model->removeRows (update.before, removed); - model->insertRows (update.before, changed); + model->entriesRemoved (update.before, removed); + model->entriesAdded (update.before, changed); } else if (update.level == Playlist::Metadata || update.queue_changed) - model->updateRows (update.before, changed); + model->entriesChanged (update.before, changed); if (update.queue_changed) { - for (int i = aud_playlist_queue_count (list); i --; ) + for (int i = m_playlist.n_queued (); i --; ) { - int entry = aud_playlist_queue_get_entry (list, i); + int entry = m_playlist.queue_get_entry (i); if (entry < update.before || entry >= entries - update.after) - model->updateRows (entry, 1); + model->entriesChanged (entry, 1); } } - int pos = aud_playlist_get_position (list); + int pos = m_playlist.get_position (); - if (needIndicatorUpdate || pos != currentPos) + if (pos != currentPos) { if (currentPos >= 0) - model->updateRows (currentPos, 1); - if (pos >= 0 && pos != currentPos) - model->updateRows (pos, 1); + model->entriesChanged (currentPos, 1); + if (pos >= 0) + model->entriesChanged (pos, 1); currentPos = pos; - needIndicatorUpdate = false; } - QItemSelection selected, deselected; - getSelectedRanges (update, selected, deselected); + updateSelection (update.before, update.after); - auto sel = selectionModel (); + inUpdate = false; +} - if (! selected.isEmpty ()) - sel->select (selected, sel->Select | sel->Rows); - if (! deselected.isEmpty ()) - sel->select (deselected, sel->Deselect | sel->Rows); +void PlaylistWidget::playCurrentIndex () +{ + m_playlist.set_position (indexToRow (currentIndex ())); + m_playlist.start_playback (); +} + +void PlaylistWidget::setFilter (const char * text) +{ + proxyModel->setFilter (text); + + int focus = m_playlist.get_focus (); + QModelIndex index; + + // If there was a valid focus before filtering, Qt updates it for us via + // currentChanged(). If not, we will set focus on the first visible row. + + if (focus >= 0) + index = rowToIndex (focus); + else + { + if (! proxyModel->rowCount ()) + return; - auto current = rowToIndex (aud_playlist_get_focus (list)); - sel->setCurrentIndex (current, sel->NoUpdate); + index = proxyModel->index (0, 0); + focus = indexToRow (index); + m_playlist.set_focus (focus); + } - if (scrollQueued) + if (! m_playlist.entry_selected (focus)) { - scrollTo (current); - scrollQueued = false; + m_playlist.select_all (false); + m_playlist.select_entry (focus, true); } + scrollTo (index); +} + +void PlaylistWidget::setFirstVisibleColumn (int col) +{ + inUpdate = true; + firstVisibleColumn = col; + + // make sure current and selected indexes point to a visible column + updateSelection (0, 0); + inUpdate = false; } -void PlaylistWidget::playCurrentIndex () +void PlaylistWidget::moveFocus (int distance) { - aud_playlist_set_position (playlist (), indexToRow (currentIndex ())); - aud_playlist_play (playlist ()); + int visibleRows = proxyModel->rowCount (); + if (! visibleRows) + return; + + int row = currentIndex ().row (); + row = aud::clamp (row + distance, 0, visibleRows - 1); + setCurrentIndex (proxyModel->index (row, 0)); } void PlaylistWidget::updateSettings () diff --git a/src/qtui/playlist.h b/src/qtui/playlist.h index be396d9..8df5639 100644 --- a/src/qtui/playlist.h +++ b/src/qtui/playlist.h @@ -26,53 +26,59 @@ #include <libaudcore/playlist.h> class PlaylistModel; +class PlaylistProxyModel; class QContextMenuEvent; class QMenu; -class QSortFilterProxyModel; class PlaylistWidget : public QTreeView { public: - PlaylistWidget (QWidget * parent = nullptr, int uniqueId = -1); + PlaylistWidget (QWidget * parent, Playlist playlist); ~PlaylistWidget (); - void scrollToCurrent (); + Playlist playlist () const + { return m_playlist; } + + void scrollToCurrent (bool force = false); void updatePlaybackIndicator (); - void update (const Playlist::Update & update); + void playlistUpdate (); void playCurrentIndex (); - void setFilter (const QString & text); - - int playlist () const; - int uniqueId () const; + void setFilter (const char * text); + void setFirstVisibleColumn (int col); + void moveFocus (int distance); void setContextMenu (QMenu * menu) { contextMenu = menu; } private: + Playlist m_playlist; PlaylistModel * model; - QSortFilterProxyModel * proxyModel; + PlaylistProxyModel * proxyModel; QMenu * contextMenu = nullptr; int currentPos = -1; bool inUpdate = false; - bool needIndicatorUpdate = false; - bool scrollQueued = false; + int firstVisibleColumn = 0; QModelIndex rowToIndex (int row); int indexToRow (const QModelIndex & index); - void getSelectedRanges (const Playlist::Update & update, + void getSelectedRanges (int rowsBefore, int rowsAfter, QItemSelection & selected, QItemSelection & deselected); + void updateSelection (int rowsBefore, int rowsAfter); void contextMenuEvent (QContextMenuEvent * event); void keyPressEvent (QKeyEvent * event); void mouseDoubleClickEvent (QMouseEvent * event); + void dragMoveEvent (QDragMoveEvent * event); + void dropEvent (QDropEvent * event); void currentChanged (const QModelIndex & current, const QModelIndex & previous); void selectionChanged (const QItemSelection & selected, const QItemSelection & deselected); void updateSettings (); + const HookReceiver<PlaylistWidget> - settings_hook {"qtui update playlist settings", this, & PlaylistWidget::updateSettings}; + hook1 {"qtui update playlist settings", this, & PlaylistWidget::updateSettings}; }; #endif diff --git a/src/qtui/playlist_header.cc b/src/qtui/playlist_header.cc new file mode 100644 index 0000000..8af90f1 --- /dev/null +++ b/src/qtui/playlist_header.cc @@ -0,0 +1,297 @@ +/* + * playlist_header.cc + * Copyright 2017 John Lindgren and Eugene Paskevich + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#include "playlist_header.h" +#include "playlist_model.h" +#include "playlist.h" +#include "settings.h" + +#include <string.h> + +#include <QAction> +#include <QContextMenuEvent> +#include <QMenu> + +#include <libaudcore/audstrings.h> +#include <libaudcore/hook.h> +#include <libaudcore/i18n.h> +#include <libaudcore/runtime.h> +#include <libaudqt/libaudqt.h> + +static const char * const s_col_keys[] = { + "playing", + "number", + "title", + "artist", + "year", + "album", + "album-artist", + "track", + "genre", + "queued", + "length", + "path", + "filename", + "custom", + "bitrate", + "comment" +}; + +static const int s_default_widths[] = { + 25, // now playing + 25, // entry number + 275, // title + 175, // artist + 50, // year + 175, // album + 175, // album artist + 75, // track + 100, // genre + 25, // queue position + 75, // length + 275, // path + 275, // filename + 275, // custom title + 75, // bitrate + 275 // comment +}; + +static_assert (aud::n_elems (s_col_keys) == PlaylistModel::n_cols, "update s_col_keys"); +static_assert (aud::n_elems (s_default_widths) == PlaylistModel::n_cols, "update s_default_widths"); + +static Index<int> s_cols; +static int s_col_widths[PlaylistModel::n_cols]; + +static void loadConfig (bool force = false) +{ + static bool loaded = false; + + if (loaded && ! force) + return; + + auto columns = str_list_to_index (aud_get_str ("qtui", "playlist_columns"), " "); + int n_columns = aud::min (columns.len (), (int) PlaylistModel::n_cols); + + s_cols.clear (); + + for (int c = 0; c < n_columns; c ++) + { + int i = 0; + while (i < PlaylistModel::n_cols && strcmp (columns[c], s_col_keys[i])) + i ++; + + if (i < PlaylistModel::n_cols) + s_cols.append (i); + } + + auto widths = str_list_to_index (aud_get_str ("qtui", "column_widths"), ", "); + int n_widths = aud::min (widths.len (), (int) PlaylistModel::n_cols); + + for (int i = 0; i < n_widths; i ++) + s_col_widths[i] = audqt::to_native_dpi (str_to_int (widths[i])); + for (int i = n_widths; i < PlaylistModel::n_cols; i ++) + s_col_widths[i] = audqt::to_native_dpi (s_default_widths[i]); + + loaded = true; +} + +static void saveConfig () +{ + Index<String> index; + for (int col : s_cols) + index.append (String (s_col_keys[col])); + + int widths[PlaylistModel::n_cols]; + for (int i = 0; i < PlaylistModel::n_cols; i ++) + widths[i] = audqt::to_portable_dpi (s_col_widths[i]); + + aud_set_str ("qtui", "playlist_columns", index_to_str_list (index, " ")); + aud_set_str ("qtui", "column_widths", int_array_to_str (widths, PlaylistModel::n_cols)); +} + +PlaylistHeader::PlaylistHeader (PlaylistWidget * playlist) : + QHeaderView (Qt::Horizontal, playlist), + m_playlist (playlist) +{ + loadConfig (); + + setSectionsMovable (true); + setStretchLastSection (true); + + connect (this, & QHeaderView::sectionResized, this, & PlaylistHeader::sectionResized); + connect (this, & QHeaderView::sectionMoved, this, & PlaylistHeader::sectionMoved); +} + +static void toggleColumn (int col, bool on) +{ + int pos = s_cols.find (col); + + if (on) + { + if (pos >= 0) + return; + + s_cols.append (col); + } + else + { + if (pos < 0) + return; + + s_cols.remove (pos, 1); + } + + saveConfig (); + + // update all playlists + hook_call ("qtui update playlist columns", nullptr); +} + +static void resetToDefaults () +{ + aud_set_str ("qtui", "playlist_columns", DEFAULT_COLUMNS); + aud_set_str ("qtui", "column_widths", ""); + + loadConfig (true); + + // update all playlists + hook_call ("qtui update playlist columns", nullptr); +} + +void PlaylistHeader::contextMenuEvent (QContextMenuEvent * event) +{ + auto menu = new QMenu (this); + QAction * actions[PlaylistModel::n_cols]; + + for (int col = 0; col < PlaylistModel::n_cols; col ++) + { + actions[col] = new QAction (_(PlaylistModel::labels[col]), menu); + actions[col]->setCheckable (true); + + connect (actions[col], & QAction::toggled, [col] (bool on) { + toggleColumn (col, on); + }); + + menu->addAction (actions[col]); + } + + for (int col : s_cols) + actions[col]->setChecked (true); + + auto sep = new QAction (menu); + sep->setSeparator (true); + menu->addAction (sep); + + auto reset = new QAction (_("Reset to Defaults"), menu); + connect (reset, & QAction::triggered, resetToDefaults); + menu->addAction (reset); + + menu->popup (event->globalPos ()); +} + +void PlaylistHeader::updateColumns () +{ + m_inUpdate = true; + + int n_shown = s_cols.len (); + + // Due to QTBUG-33974, column #0 cannot be moved by the user. + // As a workaround, hide column #0 and start the real columns at #1. + // However, Qt will hide the header completely if no columns are visible. + // This is bad since the user can't right-click to add any columns again. + // To prevent this, show column #0 if no real columns are visible. + m_playlist->setColumnHidden (0, (n_shown > 0)); + + bool shown[PlaylistModel::n_cols] {}; + + for (int i = 0; i < n_shown; i++) + { + int col = s_cols[i]; + moveSection (visualIndex (1 + col), 1 + i); + shown[col] = true; + } + + // last column expands to fit, so size is not restored + int last = (n_shown > 0) ? s_cols[n_shown - 1] : -1; + + for (int col = 0; col < PlaylistModel::n_cols; col++) + { + if (col != last) + m_playlist->setColumnWidth (1 + col, s_col_widths[col]); + + m_playlist->setColumnHidden (1 + col, ! shown[col]); + } + + // width of last column should be set to 0 initially, + // but doing so repeatedly causes flicker + if (last >= 0 && last != m_lastCol) + m_playlist->setColumnWidth (1 + last, 0); + + // this should come after all setColumnHidden() calls + m_playlist->setFirstVisibleColumn ((n_shown > 0) ? 1 + s_cols[0] : 0); + + m_inUpdate = false; + m_lastCol = last; +} + +void PlaylistHeader::sectionMoved (int logicalIndex, int oldVisualIndex, int newVisualIndex) +{ + if (m_inUpdate) + return; + + int old_pos = oldVisualIndex - 1; + int new_pos = newVisualIndex - 1; + + if (old_pos < 0 || old_pos > s_cols.len () || new_pos < 0 || new_pos > s_cols.len ()) + return; + + int col = logicalIndex - 1; + if (col != s_cols[old_pos]) + return; + + s_cols.remove (old_pos, 1); + s_cols.insert (& col, new_pos, 1); + + saveConfig (); + + // update all the other playlists + hook_call ("qtui update playlist columns", nullptr); +} + +void PlaylistHeader::sectionResized (int logicalIndex, int /*oldSize*/, int newSize) +{ + if (m_inUpdate) + return; + + int col = logicalIndex - 1; + if (col < 0 || col > PlaylistModel::n_cols) + return; + + // last column expands to fit, so size is not saved + int pos = s_cols.find (col); + if (pos < 0 || pos == s_cols.len () - 1) + return; + + s_col_widths[col] = newSize; + + saveConfig (); + + // update all the other playlists + hook_call ("qtui update playlist columns", nullptr); +} diff --git a/src/qtui/playlist_header.h b/src/qtui/playlist_header.h new file mode 100644 index 0000000..c324efb --- /dev/null +++ b/src/qtui/playlist_header.h @@ -0,0 +1,53 @@ +/* + * playlist_header.h + * Copyright 2017 John Lindgren and Eugene Paskevich + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#ifndef PLAYLIST_HEADER_H +#define PLAYLIST_HEADER_H + +#include <QHeaderView> +#include <libaudcore/hook.h> + +class PlaylistWidget; +class QAction; +class QContextMenuEvent; +class QMenu; + +class PlaylistHeader : public QHeaderView +{ +public: + PlaylistHeader (PlaylistWidget * parent); + + /* this should be called by the playlist after adding the header */ + void updateColumns (); + +private: + PlaylistWidget * m_playlist; + bool m_inUpdate = false; + int m_lastCol = -1; + + void sectionMoved (int logicalIndex, int oldVisualIndex, int newVisualIndex); + void sectionResized (int logicalIndex, int /*oldSize*/, int newSize); + + void contextMenuEvent (QContextMenuEvent * event); + + const HookReceiver<PlaylistHeader> + hook1 {"qtui update playlist columns", this, & PlaylistHeader::updateColumns}; +}; + +#endif // PLAYLIST_HEADER_H diff --git a/src/qtui/playlist_model.cc b/src/qtui/playlist_model.cc index 7c15a98..821e947 100644 --- a/src/qtui/playlist_model.cc +++ b/src/qtui/playlist_model.cc @@ -19,15 +19,56 @@ #include <QApplication> #include <QIcon> +#include <QMimeData> +#include <QUrl> #include <libaudcore/i18n.h> #include <libaudcore/audstrings.h> #include <libaudcore/drct.h> -#include <libaudcore/playlist.h> -#include <libaudcore/tuple.h> #include "playlist_model.h" +const char * const PlaylistModel::labels[] = { + N_("Now Playing"), + N_("Entry Number"), + N_("Title"), + N_("Artist"), + N_("Year"), + N_("Album"), + N_("Album Artist"), + N_("Track"), + N_("Genre"), + N_("Queue Position"), + N_("Length"), + N_("File Path"), + N_("File Name"), + N_("Custom Title"), + N_("Bitrate"), + N_("Comment") +}; + +static const Tuple::Field s_fields[] = { + Tuple::Invalid, + Tuple::Invalid, + Tuple::Title, + Tuple::Artist, + Tuple::Year, + Tuple::Album, + Tuple::AlbumArtist, + Tuple::Track, + Tuple::Genre, + Tuple::Invalid, + Tuple::Length, + Tuple::Path, + Tuple::Basename, + Tuple::FormattedTitle, + Tuple::Bitrate, + Tuple::Comment +}; + +static_assert (aud::n_elems (PlaylistModel::labels) == PlaylistModel::n_cols, "update PlaylistModel::labels"); +static_assert (aud::n_elems (s_fields) == PlaylistModel::n_cols, "update s_fields"); + static inline QPixmap get_icon (const char * name) { qreal r = qApp->devicePixelRatio (); @@ -36,14 +77,10 @@ static inline QPixmap get_icon (const char * name) return pm; } -PlaylistModel::PlaylistModel (QObject * parent, int id) : +PlaylistModel::PlaylistModel (QObject * parent, Playlist playlist) : QAbstractListModel (parent), - m_uniqueId (id) -{ - m_rows = aud_playlist_entry_count (playlist ()); -} - -PlaylistModel::~PlaylistModel () {} + m_playlist (playlist), + m_rows (playlist.n_entries ()) {} int PlaylistModel::rowCount (const QModelIndex & parent) const { @@ -52,60 +89,79 @@ int PlaylistModel::rowCount (const QModelIndex & parent) const int PlaylistModel::columnCount (const QModelIndex & parent) const { - return PL_COLS; + return 1 + n_cols; +} + +QVariant PlaylistModel::alignment (int col) const +{ + switch (col) + { + case NowPlaying: + return Qt::AlignCenter; + case Length: + return Qt::AlignRight + Qt::AlignVCenter; + default: + return Qt::AlignLeft + Qt::AlignVCenter; + } } QVariant PlaylistModel::data (const QModelIndex &index, int role) const { - String title, artist, album; + int col = index.column () - 1; + if (col < 0 || col > n_cols) + return QVariant (); + Tuple tuple; + int val = -1; switch (role) { case Qt::DisplayRole: - switch (index.column ()) + if (s_fields[col] != Tuple::Invalid) { - case PL_COL_TITLE: - case PL_COL_ARTIST: - case PL_COL_ALBUM: - case PL_COL_LENGTH: - tuple = aud_playlist_entry_get_tuple (playlist (), index.row (), Playlist::NoWait); - break; + tuple = m_playlist.entry_tuple (index.row (), Playlist::NoWait); + + switch (tuple.get_value_type (s_fields[col])) + { + case Tuple::Empty: + return QVariant (); + case Tuple::String: + return QString (tuple.get_str (s_fields[col])); + case Tuple::Int: + val = tuple.get_int (s_fields[col]); + break; + } } - switch (index.column ()) + switch (col) { - case PL_COL_TITLE: - return QString (tuple.get_str (Tuple::Title)); - case PL_COL_ARTIST: - return QString (tuple.get_str (Tuple::Artist)); - case PL_COL_ALBUM: - return QString (tuple.get_str (Tuple::Album)); - case PL_COL_QUEUED: - return getQueued (index.row ()); - case PL_COL_LENGTH: - return QString (str_format_time (tuple.get_int (Tuple::Length))); + case NowPlaying: + return QVariant (); + case EntryNumber: + return QString ("%1").arg (index.row () + 1); + case QueuePos: + return queuePos (index.row ()); + case Length: + return QString (str_format_time (val)); + case Bitrate: + return QString ("%1 kbps").arg (val); + default: + return QString ("%1").arg (val); } - break; case Qt::TextAlignmentRole: - switch (index.column ()) - { - case PL_COL_LENGTH: - return Qt::AlignRight; - } - break; + return alignment (col); case Qt::DecorationRole: - if (index.column () == 0 && index.row () == aud_playlist_get_position (playlist ())) + if (col == NowPlaying && index.row () == m_playlist.get_position ()) { - if (aud_playlist_get_playing () == playlist ()) - if (aud_drct_get_paused ()) - return get_icon ("media-playback-pause"); - else - return get_icon ("media-playback-start"); - else - return get_icon ("media-playback-stop"); + const char * icon_name = "media-playback-stop"; + + if (m_playlist == Playlist::playing_playlist ()) + icon_name = aud_drct_get_paused () ? "media-playback-pause" : + "media-playback-start"; + + return get_icon (icon_name); } break; } @@ -114,63 +170,112 @@ QVariant PlaylistModel::data (const QModelIndex &index, int role) const QVariant PlaylistModel::headerData (int section, Qt::Orientation orientation, int role) const { - if (role == Qt::DisplayRole) + if (orientation != Qt::Horizontal) + return QVariant (); + + int col = section - 1; + if (col < 0 || col > n_cols) + return QVariant (); + + switch (role) { - if (orientation == Qt::Horizontal) + case Qt::DisplayRole: + switch (col) { - switch (section) - { - case PL_COL_TITLE: - return QString (_("Title")); - case PL_COL_ARTIST: - return QString (_("Artist")); - case PL_COL_ALBUM: - return QString (_("Album")); - case PL_COL_QUEUED: - return QString (); - case PL_COL_LENGTH: - return QString (); - } + case NowPlaying: + case EntryNumber: + case QueuePos: + return QVariant (); } + + return QString (_(labels[col])); + + case Qt::TextAlignmentRole: + return alignment (col); + + default: + return QVariant (); } - return QVariant (); } -int PlaylistModel::playlist () const +Qt::DropActions PlaylistModel::supportedDropActions () const +{ + return Qt::CopyAction | Qt::MoveAction; +} + +Qt::ItemFlags PlaylistModel::flags (const QModelIndex & index) const { - return aud_playlist_by_unique_id (m_uniqueId); + if (index.isValid ()) + return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; + else + return Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled; } -int PlaylistModel::uniqueId () const +QStringList PlaylistModel::mimeTypes () const { - return m_uniqueId; + return QStringList ("text/uri-list"); } -bool PlaylistModel::insertRows (int row, int count, const QModelIndex & parent) +QMimeData * PlaylistModel::mimeData (const QModelIndexList & indexes) const +{ + /* we assume that <indexes> contains the selected entries */ + m_playlist.cache_selected (); + + QList<QUrl> urls; + int prev = -1; + + for (auto & index : indexes) + { + int row = index.row (); + if (row != prev) /* skip multiple cells in same row */ + { + urls.append (QString (m_playlist.entry_filename (row))); + prev = row; + } + } + + auto data = new QMimeData; + data->setUrls (urls); + return data; +} + +bool PlaylistModel::dropMimeData (const QMimeData * data, Qt::DropAction action, + int row, int column, const QModelIndex & parent) +{ + if (action != Qt::CopyAction || ! data->hasUrls ()) + return false; + + Index<PlaylistAddItem> items; + for (auto & url : data->urls ()) + items.append (String (url.toEncoded ())); + + m_playlist.insert_items (row, std::move (items), false); + return true; +} + +void PlaylistModel::entriesAdded (int row, int count) { if (count < 1) - return true; + return; int last = row + count - 1; - beginInsertRows (parent, row, last); - m_rows = aud_playlist_entry_count (playlist ()); + beginInsertRows (QModelIndex (), row, last); + m_rows += count; endInsertRows (); - return true; } -bool PlaylistModel::removeRows (int row, int count, const QModelIndex & parent) +void PlaylistModel::entriesRemoved (int row, int count) { if (count < 1) - return true; + return; int last = row + count - 1; - beginRemoveRows (parent, row, last); - m_rows = aud_playlist_entry_count (playlist ()); + beginRemoveRows (QModelIndex (), row, last); + m_rows -= count; endRemoveRows (); - return true; } -void PlaylistModel::updateRows (int row, int count) +void PlaylistModel::entriesChanged (int row, int count) { if (count < 1) return; @@ -181,11 +286,52 @@ void PlaylistModel::updateRows (int row, int count) emit dataChanged (topLeft, bottomRight); } -QString PlaylistModel::getQueued (int row) const +QString PlaylistModel::queuePos (int row) const { - int at = aud_playlist_queue_find_entry (playlist (), row); + int at = m_playlist.queue_find_entry (row); if (at < 0) return QString (); else return QString ("#%1").arg (at + 1); } + +/* ---------------------------------- */ + +void PlaylistProxyModel::setFilter (const char * filter) +{ + m_searchTerms = str_list_to_index (filter, " "); + invalidateFilter (); +} + +bool PlaylistProxyModel::filterAcceptsRow (int source_row, const QModelIndex &) const +{ + if (! m_searchTerms.len ()) + return true; + + Tuple tuple = m_playlist.entry_tuple (source_row); + + String strings[] = { + tuple.get_str (Tuple::Title), + tuple.get_str (Tuple::Artist), + tuple.get_str (Tuple::Album) + }; + + for (auto & term : m_searchTerms) + { + bool found = false; + + for (auto & s : strings) + { + if (s && strstr_nocase_utf8 (s, term)) + { + found = true; + break; + } + } + + if (! found) + return false; + } + + return true; +} diff --git a/src/qtui/playlist_model.h b/src/qtui/playlist_model.h index d2dad2e..a156c80 100644 --- a/src/qtui/playlist_model.h +++ b/src/qtui/playlist_model.h @@ -21,39 +21,76 @@ #define PLAYLIST_MODEL_H #include <QAbstractListModel> +#include <QSortFilterProxyModel> -enum { - PL_COL_NOW_PLAYING, - PL_COL_TITLE, - PL_COL_ARTIST, - PL_COL_ALBUM, - PL_COL_QUEUED, - PL_COL_LENGTH, - PL_COLS -}; +#include <libaudcore/playlist.h> class PlaylistModel : public QAbstractListModel { public: - PlaylistModel (QObject * parent = nullptr, int id = -1); - ~PlaylistModel (); + enum { + NowPlaying, + EntryNumber, + Title, + Artist, + Year, + Album, + AlbumArtist, + Track, + Genre, + QueuePos, + Length, + Path, + Filename, + CustomTitle, + Bitrate, + Comment, + n_cols + }; + + static const char * const labels[]; + + PlaylistModel (QObject * parent, Playlist playlist); int rowCount (const QModelIndex & parent = QModelIndex ()) const; int columnCount (const QModelIndex & parent = QModelIndex ()) const; QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - bool insertRows (int row, int count, const QModelIndex & parent = QModelIndex ()); - bool removeRows (int row, int count, const QModelIndex & parent = QModelIndex ()); - void updateRows (int row, int count); + Qt::DropActions supportedDropActions () const; + Qt::ItemFlags flags (const QModelIndex & index) const; + + QStringList mimeTypes () const; + QMimeData * mimeData (const QModelIndexList & indexes) const; + bool dropMimeData (const QMimeData * data, Qt::DropAction action, int row, + int column, const QModelIndex & parent); - QString getQueued (int row) const; - int playlist () const; - int uniqueId () const; + void entriesAdded (int row, int count); + void entriesRemoved (int row, int count); + void entriesChanged (int row, int count); private: - int m_uniqueId; + Playlist m_playlist; int m_rows; + + QVariant alignment (int col) const; + QString queuePos (int row) const; +}; + +class PlaylistProxyModel : public QSortFilterProxyModel +{ +public: + PlaylistProxyModel (QObject * parent, Playlist playlist) : + QSortFilterProxyModel (parent), + m_playlist (playlist) {} + + void setFilter (const char * filter); + +private: + bool filterAcceptsRow (int source_row, const QModelIndex &) const; + + Playlist m_playlist; + Index<String> m_searchTerms; }; #endif diff --git a/src/qtui/playlist_tabs.cc b/src/qtui/playlist_tabs.cc index cc8a875..4bf2dd1 100644 --- a/src/qtui/playlist_tabs.cc +++ b/src/qtui/playlist_tabs.cc @@ -20,15 +20,52 @@ #include "playlist.h" #include "playlist_tabs.h" #include "menus.h" +#include "search_bar.h" +#include <QBoxLayout> #include <QKeyEvent> #include <QLineEdit> +#include <libaudcore/i18n.h> #include <libaudcore/playlist.h> #include <libaudcore/runtime.h> #include <libaudqt/libaudqt.h> +class LayoutWidget : public QWidget +{ +public: + LayoutWidget (QWidget * parent, Playlist playlist, QMenu * contextMenu); + + PlaylistWidget * playlistWidget () const + { return m_playlistWidget; } + + void activateSearch () + { + m_searchBar->show (); + m_searchBar->setFocus (); + } + +private: + PlaylistWidget * m_playlistWidget; + SearchBar * m_searchBar; +}; + +LayoutWidget::LayoutWidget (QWidget * parent, Playlist playlist, QMenu * contextMenu) : + QWidget (parent), + m_playlistWidget (new PlaylistWidget (this, playlist)), + m_searchBar (new SearchBar (this, m_playlistWidget)) +{ + auto layout = audqt::make_vbox (this, 0); + layout->addWidget (m_playlistWidget); + layout->addWidget (m_searchBar); + + m_playlistWidget->setContextMenu (contextMenu); + m_searchBar->hide (); +} + +/* --------------------------------- */ + PlaylistTabs::PlaylistTabs (QWidget * parent) : QTabWidget (parent), m_pl_menu (qtui_build_pl_menu (this)), @@ -43,49 +80,57 @@ PlaylistTabs::PlaylistTabs (QWidget * parent) : addRemovePlaylists (); updateTitles (); - setCurrentIndex (aud_playlist_get_active ()); + setCurrentIndex (Playlist::active_playlist ().index ()); connect (this, & QTabWidget::currentChanged, this, & PlaylistTabs::currentChangedTrigger); } -PlaylistWidget * PlaylistTabs::createWidget (int list) +PlaylistWidget * PlaylistTabs::currentPlaylistWidget () const +{ + return ((LayoutWidget *) currentWidget ())->playlistWidget (); +} + +PlaylistWidget * PlaylistTabs::playlistWidget (int idx) const +{ + auto w = (LayoutWidget *) widget (idx); + return w ? w->playlistWidget () : nullptr; +} + +void PlaylistTabs::activateSearch () { - int id = aud_playlist_get_unique_id (list); - auto widget = new PlaylistWidget (this, id); - widget->setContextMenu (m_pl_menu); - return widget; + ((LayoutWidget *) currentWidget ())->activateSearch (); } void PlaylistTabs::addRemovePlaylists () { int tabs = count (); - int playlists = aud_playlist_count (); + int playlists = Playlist::n_playlists (); for (int i = 0; i < tabs; i ++) { - auto widget = playlistWidget (i); - int playlist = widget->playlist (); + auto w = (LayoutWidget *) widget (i); + int list_idx = w->playlistWidget ()->playlist ().index (); - if (playlist < 0) + if (list_idx < 0) { removeTab (i); - delete widget; + delete w; tabs --; i --; } - else if (playlist != i) + else if (list_idx != i) { bool found = false; for (int j = i + 1; j < tabs; j ++) { - widget = playlistWidget (j); - playlist = widget->playlist (); + w = (LayoutWidget *) widget (j); + list_idx = w->playlistWidget ()->playlist ().index (); - if (playlist == i) + if (list_idx == i) { removeTab (j); - insertTab (i, widget, QString ()); + insertTab (i, w, QString ()); found = true; break; } @@ -93,7 +138,7 @@ void PlaylistTabs::addRemovePlaylists () if (! found) { - insertTab (i, createWidget (i), QString ()); + insertTab (i, new LayoutWidget (this, Playlist::by_index (i), m_pl_menu), QString ()); tabs ++; } } @@ -101,7 +146,7 @@ void PlaylistTabs::addRemovePlaylists () while (tabs < playlists) { - addTab (createWidget (tabs), QString ()); + addTab (new LayoutWidget (this, Playlist::by_index (tabs), m_pl_menu), QString ()); tabs ++; } } @@ -110,18 +155,12 @@ void PlaylistTabs::updateTitles () { int tabs = count (); for (int i = 0; i < tabs; i ++) - setTabTitle (i, aud_playlist_get_title (i)); -} - -void PlaylistTabs::filterTrigger (const QString & text) -{ - ((PlaylistWidget *) currentWidget ())->setFilter (text); + updateTabText (i); } void PlaylistTabs::currentChangedTrigger (int idx) { - cancelRename (); - aud_playlist_set_active (idx); + Playlist::by_index (idx).activate (); } QLineEdit * PlaylistTabs::getTabEdit (int idx) @@ -129,22 +168,28 @@ QLineEdit * PlaylistTabs::getTabEdit (int idx) return dynamic_cast<QLineEdit *> (m_tabbar->tabButton (idx, QTabBar::LeftSide)); } -void PlaylistTabs::setTabTitle (int idx, const char * text) +void PlaylistTabs::updateTabText (int idx) { - // escape ampersands for setTabText () - auto title = QString (text).replace ("&", "&&"); + QString title; + + if (! getTabEdit (idx)) + { + auto playlist = Playlist::by_index (idx); - if (aud_get_bool ("qtui", "entry_count_visible")) - title += QString (" (%1)").arg (aud_playlist_entry_count (idx)); + // escape ampersands for setTabText () + title = QString (playlist.get_title ()).replace ("&", "&&"); + + if (aud_get_bool ("qtui", "entry_count_visible")) + title += QString (" (%1)").arg (playlist.n_entries ()); + } setTabText (idx, title); } -void PlaylistTabs::setupTab (int idx, QWidget * button, const char * text, QWidget * * oldp) +void PlaylistTabs::setupTab (int idx, QWidget * button, QWidget * * oldp) { QWidget * old = m_tabbar->tabButton (idx, QTabBar::LeftSide); m_tabbar->setTabButton (idx, QTabBar::LeftSide, button); - setTabTitle (idx, text); if (oldp) * oldp = old; @@ -153,37 +198,25 @@ void PlaylistTabs::setupTab (int idx, QWidget * button, const char * text, QWidg old->setParent (nullptr); old->deleteLater (); } -} - -void PlaylistTabs::tabEditedTrigger () -{ - int idx = currentIndex (); - if (idx < 0) - return; - - QLineEdit * edit = getTabEdit (idx); - if (! edit) - return; - QByteArray title = edit->text ().toUtf8 (); - aud_playlist_set_title (idx, title); - - setupTab (idx, m_leftbtn, title, nullptr); - m_leftbtn = nullptr; + updateTabText (idx); } -void PlaylistTabs::editTab (int idx) +void PlaylistTabs::editTab (int idx, Playlist playlist) { QLineEdit * edit = getTabEdit (idx); if (! edit) { - edit = new QLineEdit ((const char *) aud_playlist_get_title (idx)); + edit = new QLineEdit ((const char *) playlist.get_title ()); - connect (edit, & QLineEdit::returnPressed, this, & PlaylistTabs::tabEditedTrigger); + connect (edit, & QLineEdit::returnPressed, [this, playlist, edit] () + { + playlist.set_title (edit->text ().toUtf8 ()); + cancelRename (); + }); - setupTab (idx, edit, nullptr, & m_leftbtn); - setTabText (idx, nullptr); + setupTab (idx, edit, & m_leftbtn); } edit->selectAll (); @@ -197,10 +230,7 @@ bool PlaylistTabs::eventFilter (QObject * obj, QEvent * e) QKeyEvent * ke = (QKeyEvent *) e; if (ke->key () == Qt::Key_Escape) - { - cancelRename (); - return true; - } + return cancelRename (); } return QTabWidget::eventFilter(obj, e); @@ -209,30 +239,36 @@ bool PlaylistTabs::eventFilter (QObject * obj, QEvent * e) void PlaylistTabs::renameCurrent () { int idx = currentIndex (); + auto playlist = currentPlaylistWidget ()->playlist (); if (! m_tabbar->isVisible ()) - audqt::playlist_show_rename (idx); + audqt::playlist_show_rename (playlist); else - editTab (idx); + editTab (idx, playlist); } -void PlaylistTabs::cancelRename () +bool PlaylistTabs::cancelRename () { + bool cancelled = false; + for (int i = 0; i < count (); i ++) { QLineEdit * edit = getTabEdit (i); if (! edit) continue; - setupTab (i, m_leftbtn, (const char *) aud_playlist_get_title (i), nullptr); + setupTab (i, m_leftbtn, nullptr); m_leftbtn = nullptr; + cancelled = true; } + + return cancelled; } void PlaylistTabs::playlist_activate_cb () { - if (! aud_playlist_update_pending ()) - setCurrentIndex (aud_playlist_get_active ()); + setCurrentIndex (Playlist::active_playlist ().index ()); + cancelRename (); } void PlaylistTabs::playlist_update_cb (Playlist::UpdateLevel global_level) @@ -242,15 +278,10 @@ void PlaylistTabs::playlist_update_cb (Playlist::UpdateLevel global_level) if (global_level >= Playlist::Metadata) updateTitles (); - int lists = aud_playlist_count (); - for (int list = 0; list < lists; list ++) - { - auto update = aud_playlist_update_detail (list); - if (update.level) - playlistWidget (list)->update (update); - } + for (int i = 0; i < count (); i ++) + playlistWidget (i)->playlistUpdate (); - setCurrentIndex (aud_playlist_get_active ()); + setCurrentIndex (Playlist::active_playlist ().index ()); } void PlaylistTabs::playlist_position_cb (int list) @@ -267,12 +298,14 @@ PlaylistTabBar::PlaylistTabBar (QWidget * parent) : QTabBar (parent) updateSettings (); connect (this, & QTabBar::tabMoved, this, & PlaylistTabBar::tabMoved); - connect (this, & QTabBar::tabCloseRequested, this, & PlaylistTabBar::handleCloseRequest); + connect (this, & QTabBar::tabCloseRequested, [] (int idx) { + audqt::playlist_confirm_delete (Playlist::by_index (idx)); + }); } void PlaylistTabBar::tabMoved (int from, int to) { - aud_playlist_reorder (from, to, 1); + Playlist::reorder_playlists (from, to, 1); } void PlaylistTabBar::mousePressEvent (QMouseEvent * e) @@ -280,8 +313,11 @@ void PlaylistTabBar::mousePressEvent (QMouseEvent * e) if (e->button () == Qt::MidButton) { int index = tabAt (e->pos ()); - handleCloseRequest (index); - e->accept (); + if (index >= 0) + { + audqt::playlist_confirm_delete (Playlist::by_index (index)); + e->accept (); + } } QTabBar::mousePressEvent (e); @@ -293,21 +329,7 @@ void PlaylistTabBar::mouseDoubleClickEvent (QMouseEvent * e) if (idx < 0 || e->button () != Qt::LeftButton) return; - PlaylistTabs * p = (PlaylistTabs *) parent (); - PlaylistWidget * pl = p->playlistWidget (idx); - - aud_playlist_play (pl->playlist ()); -} - -void PlaylistTabBar::handleCloseRequest (int idx) -{ - PlaylistTabs * p = (PlaylistTabs *) parent (); - PlaylistWidget * pl = p->playlistWidget (idx); - - if (! pl) - return; - - audqt::playlist_confirm_delete (pl->playlist ()); + Playlist::by_index (idx).start_playback (); } void PlaylistTabBar::updateSettings () diff --git a/src/qtui/playlist_tabs.h b/src/qtui/playlist_tabs.h index d825068..7168aa8 100644 --- a/src/qtui/playlist_tabs.h +++ b/src/qtui/playlist_tabs.h @@ -36,11 +36,10 @@ class PlaylistTabs : public QTabWidget public: PlaylistTabs (QWidget * parent = nullptr); - PlaylistWidget * playlistWidget (int num) - { return (PlaylistWidget *) widget (num); } + PlaylistWidget * currentPlaylistWidget () const; + PlaylistWidget * playlistWidget (int idx) const; - void editTab (int idx); - void filterTrigger (const QString & text); + void editTab (int idx, Playlist playlist); void currentChangedTrigger (int idx); void tabEditedTrigger (); @@ -53,36 +52,36 @@ private: PlaylistTabBar * m_tabbar; QLineEdit * getTabEdit (int idx); - void setTabTitle (int idx, const char * text); - void setupTab (int idx, QWidget * button, const char * text, QWidget * * oldp); - PlaylistWidget * createWidget (int list); + void updateTabText (int idx); + void setupTab (int idx, QWidget * button, QWidget * * oldp); + void activateSearch (); void addRemovePlaylists (); void updateTitles (); void renameCurrent (); - void cancelRename (); + bool cancelRename (); void playlist_activate_cb (); void playlist_update_cb (Playlist::UpdateLevel global_level); void playlist_position_cb (int list); const HookReceiver<PlaylistTabs> + hook1 {"qtui find", this, & PlaylistTabs::activateSearch}, + hook2 {"qtui rename playlist", this, & PlaylistTabs::renameCurrent}, + hook3 {"qtui update playlist settings", this, & PlaylistTabs::updateTitles}; + + const HookReceiver<PlaylistTabs> activate_hook {"playlist activate", this, & PlaylistTabs::playlist_activate_cb}; const HookReceiver<PlaylistTabs, Playlist::UpdateLevel> update_hook {"playlist update", this, & PlaylistTabs::playlist_update_cb}; const HookReceiver<PlaylistTabs, int> position_hook {"playlist position", this, & PlaylistTabs::playlist_position_cb}; - const HookReceiver<PlaylistTabs> - rename_hook {"qtui rename playlist", this, & PlaylistTabs::renameCurrent}; - const HookReceiver<PlaylistTabs> - settings_hook {"qtui update playlist settings", this, & PlaylistTabs::updateTitles}; }; class PlaylistTabBar : public QTabBar { public: PlaylistTabBar (QWidget * parent = nullptr); - void handleCloseRequest (int idx); protected: void tabMoved (int from, int to); diff --git a/src/qtui/qtui.cc b/src/qtui/qtui.cc index d786b30..11a256c 100644 --- a/src/qtui/qtui.cc +++ b/src/qtui/qtui.cc @@ -29,9 +29,6 @@ #include "main_window.h" #include "settings.h" -#include "../ui-common/menu-ops.cc" -#include "../ui-common/menu-ops-qt.cc" - class QtUI : public audqt::QtIfacePlugin { private: diff --git a/src/qtui/search_bar.cc b/src/qtui/search_bar.cc new file mode 100644 index 0000000..8ba749f --- /dev/null +++ b/src/qtui/search_bar.cc @@ -0,0 +1,106 @@ +/* + * search_bar.cc + * Copyright 2016 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#include "search_bar.h" +#include "playlist.h" + +#include <QApplication> +#include <QHBoxLayout> +#include <QKeyEvent> +#include <QLabel> +#include <QLineEdit> +#include <QPushButton> + +#include <libaudcore/i18n.h> +#include <libaudqt/libaudqt.h> + +static QPushButton * makeButton (const char * icon, QWidget * parent) +{ + auto button = new QPushButton (QIcon::fromTheme (icon), QString (), parent); + button->setFlat (true); + button->setFocusPolicy (Qt::NoFocus); + return button; +} + +SearchBar::SearchBar (QWidget * parent, PlaylistWidget * playlistWidget) : + QWidget (parent), + m_playlistWidget (playlistWidget), + m_entry (new QLineEdit (this)) +{ + m_entry->setClearButtonEnabled (true); + m_entry->setPlaceholderText (_("Search playlist")); + + auto upButton = makeButton ("go-up", this); + auto downButton = makeButton ("go-down", this); + auto closeButton = makeButton ("window-close", this); + + auto layout = audqt::make_hbox (this); + layout->setContentsMargins (audqt::margins.TwoPt); + + layout->addWidget (m_entry); + layout->addWidget (upButton); + layout->addWidget (downButton); + layout->addWidget (closeButton); + + setFocusProxy (m_entry); + + connect (m_entry, & QLineEdit::textChanged, [this] (const QString & text) { + m_playlistWidget->setFilter (text.toUtf8 ()); + }); + + connect (upButton, & QPushButton::clicked, [this] (bool) { + m_playlistWidget->moveFocus (-1); + }); + + connect (downButton, & QPushButton::clicked, [this] (bool) { + m_playlistWidget->moveFocus (1); + }); + + connect (closeButton, & QPushButton::clicked, [this] (bool) { + m_entry->clear (); + m_playlistWidget->setFocus (); + hide (); + }); +} + +void SearchBar::keyPressEvent (QKeyEvent * event) +{ + if (event->modifiers () == Qt::NoModifier) + { + switch (event->key ()) + { + case Qt::Key_Enter: + case Qt::Key_Return: + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_PageUp: + case Qt::Key_PageDown: + QApplication::sendEvent (m_playlistWidget, event); + return; + + case Qt::Key_Escape: + m_entry->clear (); + m_playlistWidget->setFocus (); + hide (); + return; + } + } + + QWidget::keyPressEvent (event); +} diff --git a/src/qtui/filter_input.h b/src/qtui/search_bar.h index 52d8689..ac452f7 100644 --- a/src/qtui/filter_input.h +++ b/src/qtui/search_bar.h @@ -1,6 +1,6 @@ /* - * filter_input.h - * Copyright 2014 Daniel (dmilith) Dettlaff + * search_bar.h + * Copyright 2016 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -17,18 +17,24 @@ * the use of this software. */ -#ifndef FILTER_INPUT_H -#define FILTER_INPUT_H +#ifndef SEARCH_BAR_H +#define SEARCH_BAR_H -#include <QLineEdit> +#include <QWidget> -class FilterInput : public QLineEdit +class PlaylistWidget; +class QLineEdit; + +class SearchBar : public QWidget { public: - FilterInput (QWidget * parent = nullptr); + SearchBar (QWidget * parent, PlaylistWidget * playlistWidget); + +private: + void keyPressEvent (QKeyEvent * event); -protected: - virtual void keyPressEvent (QKeyEvent * e); /* override default handler */ + PlaylistWidget * m_playlistWidget; + QLineEdit * m_entry; }; #endif diff --git a/src/qtui/settings.cc b/src/qtui/settings.cc index 7f43508..1bd5dd8 100644 --- a/src/qtui/settings.cc +++ b/src/qtui/settings.cc @@ -23,23 +23,24 @@ #include <libaudcore/i18n.h> #include <libaudcore/preferences.h> -#include <QtGlobal> +#include <QApplication> +#include <QDesktopWidget> const char * const qtui_defaults[] = { -// "infoarea_show_vis", "TRUE", -// "infoarea_visible", "TRUE", -// "menu_visible", "TRUE", + "infoarea_show_vis", "TRUE", + "infoarea_visible", "TRUE", + "menu_visible", "TRUE", "playlist_tabs_visible", "TRUE", -// "statusbar_visible", "TRUE", + "statusbar_visible", "TRUE", "entry_count_visible", "FALSE", "close_button_visible", "TRUE", -// "autoscroll", "TRUE", -// "playlist_columns", "title artist album queued length", + "autoscroll", "TRUE", + "playlist_columns", DEFAULT_COLUMNS, "playlist_headers", "TRUE", // "record", "FALSE", -// "show_remaining_time", "FALSE", -// "step_size", "5", + "show_remaining_time", "FALSE", + "step_size", "5", nullptr }; @@ -60,15 +61,27 @@ static const PreferencesWidget qtui_widgets[] = { WidgetCheck (N_("Show close buttons"), WidgetBool ("qtui", "close_button_visible", qtui_update_playlist_settings)), WidgetLabel (N_("<b>Playlist Columns</b>")), -// WidgetCustomQt (pw_col_create_chooser), WidgetCheck (N_("Show column headers"), WidgetBool ("qtui", "playlist_headers", qtui_update_playlist_settings)), -// WidgetLabel (N_("<b>Miscellaneous</b>")), -// WidgetSpin (N_("Arrow keys seek by:"), -// WidgetFloat ("qtui", "step_size", update_step_size), -// {0.1, 60, 0.1, N_("seconds")}), -// WidgetCheck (N_("Scroll on song change"), -// WidgetBool ("qtui", "autoscroll")) + WidgetLabel (N_("<b>Miscellaneous</b>")), + WidgetSpin (N_("Arrow keys seek by:"), + WidgetFloat ("qtui", "step_size"), + {0.1, 60, 0.1, N_("seconds")}), + WidgetCheck (N_("Scroll on song change"), + WidgetBool ("qtui", "autoscroll")) }; const PluginPreferences qtui_prefs = {{qtui_widgets}}; + +int getDPI () +{ + static int dpi = 0; + + if (! dpi) + { + auto desktop = qApp->desktop (); + dpi = aud::max (96, (desktop->logicalDpiX () + desktop->logicalDpiY ()) / 2); + } + + return dpi; +} diff --git a/src/qtui/settings.h b/src/qtui/settings.h index c0bca19..07d2b1d 100644 --- a/src/qtui/settings.h +++ b/src/qtui/settings.h @@ -20,6 +20,10 @@ #ifndef SETTINGS_H #define SETTINGS_H +#include <libaudcore/templates.h> + +#define DEFAULT_COLUMNS "playing title artist album queued length" + struct PluginPreferences; extern const PluginPreferences qtui_prefs; diff --git a/src/qtui/status_bar.cc b/src/qtui/status_bar.cc index 4753fc3..df51809 100644 --- a/src/qtui/status_bar.cc +++ b/src/qtui/status_bar.cc @@ -22,26 +22,110 @@ #include <libaudcore/audstrings.h> #include <libaudcore/drct.h> #include <libaudcore/i18n.h> +#include <libaudcore/mainloop.h> #include <libaudcore/playlist.h> +#include <libaudcore/tinylock.h> #include <QLabel> +#define TIMEOUT_MS 5000 + +static const char * normal_css = + "QStatusBar { background: transparent; }\n" + "QStatusBar::item { border: none; }"; +static const char * warning_css = + "QStatusBar { background: rgba(255,255,0,64); }\n" + "QStatusBar::item { border: none; }"; +static const char * error_css = + "QStatusBar { background: rgba(255,0,0,64); }\n" + "QStatusBar::item { border: none; }"; + StatusBar::StatusBar (QWidget * parent) : QStatusBar (parent), codec_label (new QLabel (this)), length_label (new QLabel (this)) { - setStyleSheet ("QStatusBar { background: transparent; } QStatusBar::item { border: none; }"); - addWidget (codec_label); addPermanentWidget (length_label); update_codec (); update_length (); + + setStyleSheet (normal_css); + + audlog::subscribe (log_handler, audlog::Warning); + + /* redisplay codec info when message is cleared */ + connect (this, & QStatusBar::messageChanged, [this] (const QString & text) { + if (text.isEmpty ()) { + setStyleSheet (normal_css); + update_codec (); + } + }); +} + +StatusBar::~StatusBar () +{ + audlog::unsubscribe (log_handler); + event_queue_cancel ("qtui log message"); +} + +/* rate-limiting data */ +static QueuedFunc message_func; +static TinyLock message_lock; +static int current_message_level = -1; +static unsigned current_message_serial = 0; + +static void one_second_cb (void * serial) +{ + tiny_lock (& message_lock); + + /* allow new messages after one second */ + if (aud::from_ptr<unsigned> (serial) == current_message_serial) + current_message_level = -1; + + tiny_unlock (& message_lock); +} + +void StatusBar::log_handler (audlog::Level level, const char * file, int line, + const char * func, const char * text) +{ + tiny_lock (& message_lock); + + /* do not replace a message of same or higher priority */ + if (level <= current_message_level) + { + tiny_unlock (& message_lock); + return; + } + + current_message_level = level; + current_message_serial ++; + + message_func.queue (1000, one_second_cb, aud::to_ptr (current_message_serial)); + + tiny_unlock (& message_lock); + + QString s = text; + if (s.contains ('\n')) + s = s.split ('\n', QString::SkipEmptyParts).last (); + + event_queue ("qtui log message", new Message {level, s}, aud::delete_obj<Message>); +} + +void StatusBar::log_message (const Message * message) +{ + codec_label->hide (); + setStyleSheet ((message->level == audlog::Error) ? error_css : warning_css); + showMessage (message->text, TIMEOUT_MS); } void StatusBar::update_codec () { + /* codec info is hidden when a message is displayed */ + if (! currentMessage ().isEmpty ()) + return; + if (! aud_drct_get_ready ()) { codec_label->hide (); @@ -92,10 +176,10 @@ void StatusBar::update_codec () void StatusBar::update_length () { - int playlist = aud_playlist_get_active (); + auto playlist = Playlist::active_playlist (); - StringBuf s1 = str_format_time (aud_playlist_get_selected_length (playlist)); - StringBuf s2 = str_format_time (aud_playlist_get_total_length (playlist)); + StringBuf s1 = str_format_time (playlist.selected_length_ms ()); + StringBuf s2 = str_format_time (playlist.total_length_ms ()); length_label->setText ((const char *) str_concat ({s1, " / ", s2})); } diff --git a/src/qtui/status_bar.h b/src/qtui/status_bar.h index ebd1a63..778bf43 100644 --- a/src/qtui/status_bar.h +++ b/src/qtui/status_bar.h @@ -23,6 +23,7 @@ #include <QStatusBar> #include <libaudcore/hook.h> +#include <libaudcore/runtime.h> class QLabel; @@ -30,14 +31,28 @@ class StatusBar : public QStatusBar { public: StatusBar (QWidget * parent); + ~StatusBar (); private: + struct Message { + audlog::Level level; + QString text; + }; + QLabel * codec_label; QLabel * length_label; + static void log_handler (audlog::Level level, const char * file, int line, + const char * func, const char * text); + + void log_message (const Message * message); + void update_codec (); void update_length (); + const HookReceiver<StatusBar, const Message *> + log_hook {"qtui log message", this, & StatusBar::log_message}; + const HookReceiver<StatusBar> hook1 {"playlist activate", this, & StatusBar::update_length}, hook2 {"playlist update", this, & StatusBar::update_length}, diff --git a/src/qtui/time_slider.cc b/src/qtui/time_slider.cc index 68b850c..4c3bfa2 100644 --- a/src/qtui/time_slider.cc +++ b/src/qtui/time_slider.cc @@ -21,14 +21,27 @@ #include <libaudcore/audstrings.h> #include <libaudcore/drct.h> +#include <libaudcore/runtime.h> -#include <QLabel> #include <QMouseEvent> #include <QStyle> +MyLabel::MyLabel (QWidget * parent) : QLabel (parent) {} +MyLabel::~MyLabel () {} + +void MyLabel::mouseDoubleClickEvent (QMouseEvent * event) +{ + aud_toggle_bool ("qtui", "show_remaining_time"); + hook_call ("qtui toggle remaining time", nullptr); + + event->accept (); + + QLabel::mouseDoubleClickEvent (event); +} + TimeSlider::TimeSlider (QWidget * parent) : QSlider (Qt::Horizontal, parent), - m_label (new QLabel (parent)) + m_label (new MyLabel (parent)) { setFocusPolicy (Qt::NoFocus); setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Fixed); @@ -36,8 +49,7 @@ TimeSlider::TimeSlider (QWidget * parent) : m_label->setContentsMargins (4, 0, 4, 0); m_label->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::MinimumExpanding); - connect (this, & QSlider::valueChanged, this, & TimeSlider::moved); - connect (this, & QSlider::sliderPressed, this, & TimeSlider::pressed); + connect (this, & QSlider::sliderMoved, this, & TimeSlider::moved); connect (this, & QSlider::sliderReleased, this, & TimeSlider::released); start_stop (); @@ -49,8 +61,11 @@ void TimeSlider::set_label (int time, int length) { QString text; - if (length > 0) - text = str_concat ({str_format_time (time), " / ", str_format_time (length)}); + if (length >= 0) + if (aud_get_bool ("qtui", "show_remaining_time")) + text = str_concat ({str_format_time (time - length), " / ", str_format_time (length)}); + else + text = str_concat ({str_format_time (time), " / ", str_format_time (length)}); else text = str_format_time (time); @@ -65,18 +80,9 @@ void TimeSlider::start_stop () setEnabled (ready); m_label->setEnabled (ready); - if (ready) - { - if (! isSliderDown ()) - update (); - } - else - { - setRange (0, 0); - m_label->setText ("0:00 / 0:00"); - } + update (); - if (ready && ! paused && ! isSliderDown ()) + if (ready && ! paused) m_timer.start (); else m_timer.stop (); @@ -84,13 +90,24 @@ void TimeSlider::start_stop () void TimeSlider::update () { - int time = aud_drct_get_time (); - int length = aud_drct_get_length (); + if (aud_drct_get_ready ()) + { + if (! isSliderDown ()) + { + int time = aud_drct_get_time (); + int length = aud_drct_get_length (); - setRange (0, length); - setValue (time); + setRange (0, length); + setValue (time); - set_label (time, length); + set_label (time, length); + } + } + else + { + setRange (0, 0); + set_label (0, 0); + } } void TimeSlider::moved (int value) @@ -98,18 +115,9 @@ void TimeSlider::moved (int value) set_label (value, aud_drct_get_length ()); } -void TimeSlider::pressed () -{ - m_timer.stop (); -} - void TimeSlider::released () { aud_drct_seek (value ()); - set_label (value (), aud_drct_get_length ()); - - if (! aud_drct_get_paused ()) - m_timer.start (); } void TimeSlider::mousePressEvent (QMouseEvent * event) diff --git a/src/qtui/time_slider.h b/src/qtui/time_slider.h index d216026..94f26ac 100644 --- a/src/qtui/time_slider.h +++ b/src/qtui/time_slider.h @@ -20,20 +20,30 @@ #ifndef TIME_SLIDER_H #define TIME_SLIDER_H +#include <QLabel> #include <QSlider> #include <libaudcore/hook.h> -class QLabel; class QMouseEvent; +class MyLabel : public QLabel +{ +public: + MyLabel (QWidget * parent); + ~MyLabel (); + +protected: + void mouseDoubleClickEvent (QMouseEvent * event); +}; + class TimeSlider : public QSlider { public: TimeSlider (QWidget * parent); ~TimeSlider (); - QLabel * label () + MyLabel * label () { return m_label; } private: @@ -47,7 +57,7 @@ private: void mousePressEvent (QMouseEvent * event); - QLabel * m_label; + MyLabel * m_label; const Timer<TimeSlider> m_timer {TimerRate::Hz4, this, & TimeSlider::update}; @@ -56,7 +66,9 @@ private: hook1 {"playback ready", this, & TimeSlider::start_stop}, hook2 {"playback pause", this, & TimeSlider::start_stop}, hook3 {"playback unpause", this, & TimeSlider::start_stop}, - hook4 {"playback stop", this, & TimeSlider::start_stop}; + hook4 {"playback seek", this, & TimeSlider::update}, + hook5 {"playback stop", this, & TimeSlider::start_stop}, + hook6 {"qtui toggle remaining time", this, & TimeSlider::start_stop}; }; #endif diff --git a/src/search-tool-qt/search-tool-qt.cc b/src/search-tool-qt/search-tool-qt.cc index fba5335..f7b478b 100644 --- a/src/search-tool-qt/search-tool-qt.cc +++ b/src/search-tool-qt/search-tool-qt.cc @@ -27,8 +27,10 @@ #include <QLabel> #include <QLineEdit> #include <QMenu> +#include <QMimeData> #include <QPushButton> #include <QTreeView> +#include <QUrl> #include <libaudcore/audstrings.h> #include <libaudcore/hook.h> @@ -58,6 +60,7 @@ public: constexpr SearchToolQt () : GeneralPlugin (info, false) {} void * get_qt_widget (); + int take_message (const char * code, const void *, int); }; EXPORT SearchToolQt aud_plugin_instance; @@ -99,11 +102,6 @@ struct Item Item & operator= (Item &&) = default; }; -struct SearchState { - Index<const Item *> items; - int mask; -}; - class ResultsModel : public QAbstractListModel { public: @@ -112,9 +110,23 @@ public: protected: int rowCount (const QModelIndex & parent) const { return m_rows; } int columnCount (const QModelIndex & parent) const { return 1; } - QVariant data (const QModelIndex & index, int role) const; + Qt::ItemFlags flags (const QModelIndex & index) const + { + if (index.isValid ()) + return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; + else + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; + } + + QStringList mimeTypes () const + { + return QStringList ("text/uri-list"); + } + + QMimeData * mimeData (const QModelIndexList & indexes) const; + private: int m_rows = 0; }; @@ -127,33 +139,34 @@ protected: static StringBuf create_item_label (int row); -static int playlist_id; -static Index<String> search_terms; +static Playlist s_playlist; +static Index<String> s_search_terms; /* Note: added_table is accessed by multiple threads. * When adding = true, it may only be accessed by the playlist add thread. * When adding = false, it may only be accessed by the UI thread. * adding may only be set by the UI thread while holding adding_lock. */ -static TinyLock adding_lock; -static bool adding = false; -static SimpleHash<String, bool> added_table; +static TinyLock s_adding_lock; +static bool s_adding = false; +static SimpleHash<String, bool> s_added_table; -static SimpleHash<Key, Item> database; -static bool database_valid; -static Index<const Item *> items; -static int hidden_items; +static SimpleHash<Key, Item> s_database; +static bool s_database_valid; +static Index<const Item *> s_items; +static int s_hidden_items; -static QueuedFunc search_timer; -static bool search_pending; +static QueuedFunc s_search_timer; +static bool s_search_pending; -static ResultsModel model; -static QLabel * help_label, * wait_label, * stats_label; -static QTreeView * results_list; -static QMenu * menu; +static ResultsModel s_model; +static QLabel * s_help_label, * s_wait_label, * s_stats_label; +static QLineEdit * s_search_entry; +static QTreeView * s_results_list; +static QMenu * s_menu; void ResultsModel::update () { - int rows = items.len (); + int rows = s_items.len (); int keep = aud::min (rows, m_rows); if (rows < m_rows) @@ -187,44 +200,40 @@ QVariant ResultsModel::data (const QModelIndex & index, int role) const static void find_playlist () { - playlist_id = -1; + s_playlist = Playlist (); - for (int p = 0; playlist_id < 0 && p < aud_playlist_count (); p ++) + for (int p = 0; p < Playlist::n_playlists (); p ++) { - String title = aud_playlist_get_title (p); - if (! strcmp (title, _("Library"))) - playlist_id = aud_playlist_get_unique_id (p); + auto playlist = Playlist::by_index (p); + if (! strcmp (playlist.get_title (), _("Library"))) + { + s_playlist = playlist; + break; + } } } -static int create_playlist () +static void create_playlist () { - int list = aud_playlist_get_blank (); - aud_playlist_set_title (list, _("Library")); - aud_playlist_set_active (list); - playlist_id = aud_playlist_get_unique_id (list); - return list; + s_playlist = Playlist::blank_playlist (); + s_playlist.set_title (_("Library")); + s_playlist.active_playlist (); } -static int get_playlist (bool require_added, bool require_scanned) +static bool check_playlist (bool require_added, bool require_scanned) { - if (playlist_id < 0) - return -1; - - int list = aud_playlist_by_unique_id (playlist_id); - - if (list < 0) + if (! s_playlist.exists ()) { - playlist_id = -1; - return -1; + s_playlist = Playlist (); + return false; } - if (require_added && aud_playlist_add_in_progress (list)) - return -1; - if (require_scanned && aud_playlist_scan_in_progress (list)) - return -1; + if (require_added && s_playlist.add_in_progress ()) + return false; + if (require_scanned && s_playlist.scan_in_progress ()) + return false; - return list; + return true; } static String get_uri () @@ -245,21 +254,21 @@ static String get_uri () static void destroy_database () { - items.clear (); - hidden_items = 0; - database.clear (); - database_valid = false; + s_items.clear (); + s_hidden_items = 0; + s_database.clear (); + s_database_valid = false; } -static void create_database (int list) +static void create_database () { destroy_database (); - int entries = aud_playlist_entry_count (list); + int entries = s_playlist.n_entries (); for (int e = 0; e < entries; e ++) { - Tuple tuple = aud_playlist_entry_get_tuple (list, e, Playlist::NoWait); + Tuple tuple = s_playlist.entry_tuple (e, Playlist::NoWait); aud::array<SearchField, String> fields; fields[SearchField::Genre] = tuple.get_str (Tuple::Genre); @@ -268,7 +277,7 @@ static void create_database (int list) fields[SearchField::Title] = tuple.get_str (Tuple::Title); Item * parent = nullptr; - SimpleHash<Key, Item> * hash = & database; + SimpleHash<Key, Item> * hash = & s_database; for (auto f : aud::range<SearchField> ()) { @@ -292,34 +301,33 @@ static void create_database (int list) } } - database_valid = true; + s_database_valid = true; } -static void search_cb (const Key & key, Item & item, void * _state) +static void search_recurse (SimpleHash<Key, Item> & domain, int mask, Index<const Item *> & results) { - SearchState * state = (SearchState *) _state; - - int oldmask = state->mask; - int count = search_terms.len (); - - for (int t = 0, bit = 1; t < count; t ++, bit <<= 1) + domain.iterate ([mask, & results] (const Key & key, Item & item) { - if (! (state->mask & bit)) - continue; /* skip term if it is already found */ + int count = s_search_terms.len (); + int new_mask = mask; - if (strstr (item.folded, search_terms[t])) - state->mask &= ~bit; /* we found it */ - else if (! item.children.n_items ()) - break; /* quit early if there are no children to search */ - } + for (int t = 0, bit = 1; t < count; t ++, bit <<= 1) + { + if (! (new_mask & bit)) + continue; /* skip term if it is already found */ - /* adding an item with exactly one child is redundant, so avoid it */ - if (! state->mask && item.children.n_items () != 1) - state->items.append (& item); + if (strstr (item.folded, s_search_terms[t])) + new_mask &= ~bit; /* we found it */ + else if (! item.children.n_items ()) + break; /* quit early if there are no children to search */ + } - item.children.iterate (search_cb, state); + /* adding an item with exactly one child is redundant, so avoid it */ + if (! new_mask && item.children.n_items () != 1) + results.append (& item); - state->mask = oldmask; + search_recurse (item.children, new_mask, results); + }); } static int item_compare (const Item * const & a, const Item * const & b) @@ -351,120 +359,112 @@ static int item_compare_pass1 (const Item * const & a, const Item * const & b) static void do_search () { - items.clear (); - hidden_items = 0; + s_items.clear (); + s_hidden_items = 0; - if (! database_valid) + if (! s_database_valid) return; - SearchState state; - /* effectively limits number of search terms to 32 */ - state.mask = (1 << search_terms.len ()) - 1; - - database.iterate (search_cb, & state); - - items = std::move (state.items); + search_recurse (s_database, (1 << s_search_terms.len ()) - 1, s_items); /* first sort by number of songs per item */ - items.sort (item_compare_pass1); + s_items.sort (item_compare_pass1); /* limit to items with most songs */ - if (items.len () > MAX_RESULTS) + if (s_items.len () > MAX_RESULTS) { - hidden_items = items.len () - MAX_RESULTS; - items.remove (MAX_RESULTS, -1); + s_hidden_items = s_items.len () - MAX_RESULTS; + s_items.remove (MAX_RESULTS, -1); } /* sort by item type, then item name */ - items.sort (item_compare); + s_items.sort (item_compare); } static bool filter_cb (const char * filename, void * unused) { bool add = false; - tiny_lock (& adding_lock); + tiny_lock (& s_adding_lock); - if (adding) + if (s_adding) { - bool * added = added_table.lookup (String (filename)); + bool * added = s_added_table.lookup (String (filename)); if ((add = ! added)) - added_table.add (String (filename), true); + s_added_table.add (String (filename), true); else (* added) = true; } - tiny_unlock (& adding_lock); + tiny_unlock (& s_adding_lock); return add; } static void begin_add (const char * uri) { - if (adding) + if (s_adding) return; - int list = get_playlist (false, false); - - if (list < 0) - list = create_playlist (); + if (! check_playlist (false, false)) + create_playlist (); /* if possible, store local path for compatibility with older versions */ StringBuf path = uri_to_filename (uri); aud_set_str ("search-tool", "path", path ? path : uri); - added_table.clear (); + s_added_table.clear (); - int entries = aud_playlist_entry_count (list); + int entries = s_playlist.n_entries (); for (int entry = 0; entry < entries; entry ++) { - String filename = aud_playlist_entry_get_filename (list, entry); + String filename = s_playlist.entry_filename (entry); - if (! added_table.lookup (filename)) + if (! s_added_table.lookup (filename)) { - aud_playlist_entry_set_selected (list, entry, false); - added_table.add (filename, false); + s_playlist.select_entry (entry, false); + s_added_table.add (filename, false); } else - aud_playlist_entry_set_selected (list, entry, true); + s_playlist.select_entry (entry, true); } - aud_playlist_delete_selected (list); + s_playlist.remove_selected (); - tiny_lock (& adding_lock); - adding = true; - tiny_unlock (& adding_lock); + tiny_lock (& s_adding_lock); + s_adding = true; + tiny_unlock (& s_adding_lock); Index<PlaylistAddItem> add; add.append (String (uri)); - aud_playlist_entry_insert_filtered (list, -1, std::move (add), filter_cb, nullptr, false); + s_playlist.insert_filtered (-1, std::move (add), filter_cb, nullptr, false); } static void show_hide_widgets () { - if (playlist_id < 0) + if (s_playlist == Playlist ()) { - wait_label->hide (); - results_list->hide (); - stats_label->hide (); - help_label->show (); + s_wait_label->hide (); + s_results_list->hide (); + s_stats_label->hide (); + s_help_label->show (); } else { - help_label->hide (); + s_help_label->hide (); - if (database_valid) + if (s_database_valid) { - wait_label->hide (); - results_list->show (); - stats_label->show (); + s_wait_label->hide (); + s_results_list->show (); + s_stats_label->show (); } else { - results_list->hide (); - stats_label->hide (); - wait_label->show (); + s_results_list->hide (); + s_stats_label->hide (); + s_wait_label->show (); } } } @@ -473,106 +473,96 @@ static void search_timeout (void * = nullptr) { do_search (); - model.update (); + s_model.update (); - if (items.len ()) + if (s_items.len ()) { - auto sel = results_list->selectionModel (); - sel->select (model.index (0, 0), sel->Clear | sel->SelectCurrent); + auto sel = s_results_list->selectionModel (); + sel->select (s_model.index (0, 0), sel->Clear | sel->SelectCurrent); } - int total = items.len () + hidden_items; - StringBuf stats = str_printf (dngettext (PACKAGE, "%d result", - "%d results", total), total); - - if (hidden_items) - { - stats.insert (-1, " "); - stats.combine (str_printf (dngettext (PACKAGE, "(%d hidden)", - "(%d hidden)", hidden_items), hidden_items)); - } + int total = s_items.len () + s_hidden_items; - stats_label->setText ((const char *) stats); + if (s_hidden_items) + s_stats_label->setText ((const char *) + str_printf (dngettext (PACKAGE, "%d of %d result shown", + "%d of %d results shown", total), s_items.len (), total)); + else + s_stats_label->setText ((const char *) + str_printf (dngettext (PACKAGE, "%d result", "%d results", total), total)); - search_timer.stop (); - search_pending = false; + s_search_timer.stop (); + s_search_pending = false; } static void update_database () { - int list = get_playlist (true, true); - - if (list >= 0) + if (check_playlist (true, true)) { - create_database (list); + create_database (); search_timeout (); } else { destroy_database (); - model.update (); - stats_label->clear (); + s_model.update (); + s_stats_label->clear (); } show_hide_widgets (); } -static void add_complete_cb (void * unused, void * unused2) +static void add_complete_cb (void *, void *) { - int list = get_playlist (true, false); - if (list < 0) + if (! check_playlist (true, false)) return; - if (adding) + if (s_adding) { - tiny_lock (& adding_lock); - adding = false; - tiny_unlock (& adding_lock); + tiny_lock (& s_adding_lock); + s_adding = false; + tiny_unlock (& s_adding_lock); - int entries = aud_playlist_entry_count (list); + int entries = s_playlist.n_entries (); for (int entry = 0; entry < entries; entry ++) { - String filename = aud_playlist_entry_get_filename (list, entry); - bool * added = added_table.lookup (filename); + String filename = s_playlist.entry_filename (entry); + bool * added = s_added_table.lookup (filename); - aud_playlist_entry_set_selected (list, entry, ! added || ! (* added)); + s_playlist.select_entry (entry, ! added || ! (* added)); } - added_table.clear (); + s_added_table.clear (); /* don't clear the playlist if nothing was added */ - if (aud_playlist_selected_count (list) < aud_playlist_entry_count (list)) - aud_playlist_delete_selected (list); + if (s_playlist.n_selected () < entries) + s_playlist.remove_selected (); else - aud_playlist_select_all (list, false); + s_playlist.select_all (false); - aud_playlist_sort_by_scheme (list, Playlist::Path); + s_playlist.sort_entries (Playlist::Path); } - if (! database_valid && ! aud_playlist_update_pending (list)) + if (! s_database_valid && ! s_playlist.update_pending ()) update_database (); } -static void scan_complete_cb (void * unused, void * unused2) +static void scan_complete_cb (void *, void *) { - int list = get_playlist (true, true); - if (list < 0) + if (! check_playlist (true, true)) return; - if (! database_valid && ! aud_playlist_update_pending (list)) + if (! s_database_valid && ! s_playlist.update_pending ()) update_database (); } -static void playlist_update_cb (void * data, void * unused) +static void playlist_update_cb (void *, void *) { - if (! database_valid) - update_database (); - else + if (! s_database_valid || ! check_playlist (true, true) || + s_playlist.update_detail ().level >= Playlist::Metadata) { - int list = get_playlist (true, true); - if (list < 0 || aud_playlist_update_detail (list).level >= Playlist::Metadata) - update_database (); + update_database (); } } @@ -593,49 +583,52 @@ static void search_cleanup () hook_dissociate ("playlist scan complete", scan_complete_cb); hook_dissociate ("playlist update", playlist_update_cb); - search_timer.stop (); - search_pending = false; + s_search_timer.stop (); + s_search_pending = false; - search_terms.clear (); - items.clear (); + s_search_terms.clear (); + s_items.clear (); - tiny_lock (& adding_lock); - adding = false; - tiny_unlock (& adding_lock); + tiny_lock (& s_adding_lock); + s_adding = false; + tiny_unlock (& s_adding_lock); - added_table.clear (); + s_added_table.clear (); destroy_database (); - delete menu; - menu = nullptr; + s_help_label = s_wait_label = s_stats_label = nullptr; + s_search_entry = nullptr; + s_results_list = nullptr; + + delete s_menu; + s_menu = nullptr; } static void do_add (bool play, bool set_title) { - if (search_pending) + if (s_search_pending) search_timeout (); - int list = aud_playlist_by_unique_id (playlist_id); - int n_items = items.len (); + int n_items = s_items.len (); int n_selected = 0; Index<PlaylistAddItem> add; String title; - for (auto & idx : results_list->selectionModel ()->selectedRows ()) + for (auto & idx : s_results_list->selectionModel ()->selectedRows ()) { int i = idx.row (); if (i < 0 || i >= n_items) continue; - const Item * item = items[i]; + const Item * item = s_items[i]; for (int entry : item->matches) { add.append ( - aud_playlist_entry_get_filename (list, entry), - aud_playlist_entry_get_tuple (list, entry, Playlist::NoWait), - aud_playlist_entry_get_decoder (list, entry, Playlist::NoWait) + s_playlist.entry_filename (entry), + s_playlist.entry_tuple (entry, Playlist::NoWait), + s_playlist.entry_decoder (entry, Playlist::NoWait) ); } @@ -644,37 +637,37 @@ static void do_add (bool play, bool set_title) title = item->name; } - int list2 = aud_playlist_get_active (); - aud_playlist_entry_insert_batch (list2, -1, std::move (add), play); + auto list2 = Playlist::active_playlist (); + list2.insert_items (-1, std::move (add), play); if (set_title && n_selected == 1) - aud_playlist_set_title (list2, title); + list2.set_title (title); } static void action_play () { - aud_playlist_set_active (aud_playlist_get_temporary ()); + Playlist::temporary_playlist ().activate (); do_add (true, false); } static void action_create_playlist () { - aud_playlist_new (); + Playlist::new_playlist (); do_add (false, true); } static void action_add_to_playlist () { - if (aud_playlist_by_unique_id (playlist_id) != aud_playlist_get_active ()) + if (s_playlist != Playlist::active_playlist ()) do_add (false, false); } static StringBuf create_item_label (int row) { - if (row < 0 || row >= items.len ()) + if (row < 0 || row >= s_items.len ()) return StringBuf (); - const Item * item = items[row]; + const Item * item = s_items[row]; StringBuf string = str_concat ({item->name, "\n"}); if (item->field != SearchField::Title) @@ -690,12 +683,14 @@ static StringBuf create_item_label (int row) string.insert (-1, _("of this genre")); } - while ((item = item->parent)) + if (item->parent) { + auto parent = (item->parent->parent ? item->parent->parent : item->parent); + string.insert (-1, " "); - string.insert (-1, (item->field == SearchField::Album) ? _("on") : _("by")); + string.insert (-1, (parent->field == SearchField::Album) ? _("on") : _("by")); string.insert (-1, " "); - string.insert (-1, item->name); + string.insert (-1, parent->name); } return string; @@ -709,69 +704,106 @@ void ResultsView::contextMenuEvent (QContextMenuEvent * event) audqt::MenuCommand ({N_("_Add to Playlist"), "list-add"}, action_add_to_playlist) }; - if (! menu) - menu = audqt::menu_build ({items}); + if (! s_menu) + s_menu = audqt::menu_build ({items}); - menu->popup (event->globalPos ()); + s_menu->popup (event->globalPos ()); } -void * SearchToolQt::get_qt_widget () +QMimeData * ResultsModel::mimeData (const QModelIndexList & indexes) const { - auto widget = new QWidget; - auto vbox = new QVBoxLayout (widget); - vbox->setContentsMargins (0, 0, 0, 0); + if (s_search_pending) + search_timeout (); + + s_playlist.select_all (false); + + QList<QUrl> urls; + for (auto & index : indexes) + { + int row = index.row (); + if (row < 0 || row >= s_items.len ()) + continue; - auto entry = new QLineEdit; - entry->setPlaceholderText (_("Search library")); - vbox->addWidget (entry); + for (int entry : s_items[row]->matches) + { + urls.append (QString (s_playlist.entry_filename (entry))); + s_playlist.select_entry (entry, true); + } + } - help_label = new QLabel (_("To import your music library into Audacious, " + s_playlist.cache_selected (); + + auto data = new QMimeData; + data->setUrls (urls); + return data; +} + +void * SearchToolQt::get_qt_widget () +{ + s_search_entry = new QLineEdit; + s_search_entry->setContentsMargins (audqt::margins.TwoPt); + s_search_entry->setClearButtonEnabled (true); + s_search_entry->setPlaceholderText (_("Search library")); + + s_help_label = new QLabel (_("To import your music library into Audacious, " "choose a folder and then click the \"refresh\" icon.")); - help_label->setAlignment (Qt::AlignCenter); - help_label->setWordWrap (true); - vbox->addWidget (help_label); - - wait_label = new QLabel (_("Please wait ...")); - wait_label->setAlignment (Qt::AlignCenter); - vbox->addWidget (wait_label); - - results_list = new ResultsView; - results_list->setHeaderHidden (true); - results_list->setIndentation (0); - results_list->setModel (& model); - results_list->setSelectionMode (QTreeView::ExtendedSelection); - vbox->addWidget (results_list); - - stats_label = new QLabel; - stats_label->setAlignment (Qt::AlignCenter); - vbox->addWidget (stats_label); - - auto hbox = new QHBoxLayout; - vbox->addLayout (hbox); + s_help_label->setAlignment (Qt::AlignCenter); + s_help_label->setContentsMargins (audqt::margins.EightPt); + s_help_label->setWordWrap (true); + + s_wait_label = new QLabel (_("Please wait ...")); + s_wait_label->setAlignment (Qt::AlignCenter); + s_wait_label->setContentsMargins (audqt::margins.EightPt); + + s_results_list = new ResultsView; + s_results_list->setFrameStyle (QFrame::NoFrame); + s_results_list->setHeaderHidden (true); + s_results_list->setIndentation (0); + s_results_list->setModel (& s_model); + s_results_list->setSelectionMode (QTreeView::ExtendedSelection); + s_results_list->setDragDropMode (QTreeView::DragOnly); + + s_stats_label = new QLabel; + s_stats_label->setAlignment (Qt::AlignCenter); + s_stats_label->setContentsMargins (audqt::margins.TwoPt); auto chooser = new QLineEdit; - hbox->addWidget (chooser); auto button = new QPushButton (QIcon::fromTheme ("view-refresh"), QString ()); button->setFlat (true); button->setFocusPolicy (Qt::NoFocus); + + auto hbox = audqt::make_hbox (nullptr); + hbox->setContentsMargins (audqt::margins.TwoPt); + + hbox->addWidget (chooser); hbox->addWidget (button); + auto widget = new QWidget; + auto vbox = audqt::make_vbox (widget, 0); + + vbox->addWidget (s_search_entry); + vbox->addWidget (s_help_label); + vbox->addWidget (s_wait_label); + vbox->addWidget (s_results_list); + vbox->addWidget (s_stats_label); + vbox->addLayout (hbox); + String uri = get_uri (); StringBuf path = uri_to_filename (uri, false); chooser->setText (path ? path : uri); search_init (); - QObject::connect (vbox, & QObject::destroyed, search_cleanup); - QObject::connect (entry, & QLineEdit::returnPressed, action_play); - QObject::connect (results_list, & QTreeView::doubleClicked, action_play); + QObject::connect (widget, & QObject::destroyed, search_cleanup); + QObject::connect (s_search_entry, & QLineEdit::returnPressed, action_play); + QObject::connect (s_results_list, & QTreeView::doubleClicked, action_play); - QObject::connect (entry, & QLineEdit::textEdited, [] (const QString & text) + QObject::connect (s_search_entry, & QLineEdit::textEdited, [] (const QString & text) { - search_terms = str_list_to_index (str_tolower_utf8 (text.toUtf8 ()), " "); - search_timer.queue (SEARCH_DELAY, search_timeout, nullptr); - search_pending = true; + s_search_terms = str_list_to_index (str_tolower_utf8 (text.toUtf8 ()), " "); + s_search_timer.queue (SEARCH_DELAY, search_timeout, nullptr); + s_search_pending = true; }); QObject::connect (chooser, & QLineEdit::textEdited, [button] (const QString & text) @@ -791,3 +823,14 @@ void * SearchToolQt::get_qt_widget () return widget; } + +int SearchToolQt::take_message (const char * code, const void *, int) +{ + if (! strcmp (code, "grab focus") && s_search_entry) + { + s_search_entry->setFocus (Qt::OtherFocusReason); + return 0; + } + + return -1; +} diff --git a/src/search-tool/search-tool.cc b/src/search-tool/search-tool.cc index 05d1780..5022781 100644 --- a/src/search-tool/search-tool.cc +++ b/src/search-tool/search-tool.cc @@ -20,6 +20,7 @@ #include <string.h> #include <gtk/gtk.h> +#define AUD_GLIB_INTEGRATION #include <libaudcore/audstrings.h> #include <libaudcore/hook.h> #include <libaudcore/i18n.h> @@ -91,73 +92,64 @@ struct Item Item & operator= (Item &&) = default; }; -struct SearchState { - Index<const Item *> items; - int mask; -}; - -static int playlist_id; -static Index<String> search_terms; +static Playlist s_playlist; +static Index<String> s_search_terms; /* Note: added_table is accessed by multiple threads. * When adding = true, it may only be accessed by the playlist add thread. * When adding = false, it may only be accessed by the UI thread. * adding may only be set by the UI thread while holding adding_lock. */ -static TinyLock adding_lock; -static bool adding = false; -static SimpleHash<String, bool> added_table; +static TinyLock s_adding_lock; +static bool s_adding = false; +static SimpleHash<String, bool> s_added_table; -static SimpleHash<Key, Item> database; -static bool database_valid; -static Index<const Item *> items; -static int hidden_items; -static Index<bool> selection; +static SimpleHash<Key, Item> s_database; +static bool s_database_valid; +static Index<const Item *> s_items; +static int s_hidden_items; +static Index<bool> s_selection; -static QueuedFunc search_timer; -static bool search_pending; +static QueuedFunc s_search_timer; +static bool s_search_pending; static GtkWidget * entry, * help_label, * wait_label, * scrolled, * results_list, * stats_label; static void find_playlist () { - playlist_id = -1; + s_playlist = Playlist (); - for (int p = 0; playlist_id < 0 && p < aud_playlist_count (); p ++) + for (int p = 0; p < Playlist::n_playlists (); p ++) { - String title = aud_playlist_get_title (p); - if (! strcmp (title, _("Library"))) - playlist_id = aud_playlist_get_unique_id (p); + auto playlist = Playlist::by_index (p); + if (! strcmp (playlist.get_title (), _("Library"))) + { + s_playlist = playlist; + break; + } } } -static int create_playlist () +static void create_playlist () { - int list = aud_playlist_get_blank (); - aud_playlist_set_title (list, _("Library")); - aud_playlist_set_active (list); - playlist_id = aud_playlist_get_unique_id (list); - return list; + s_playlist = Playlist::blank_playlist (); + s_playlist.set_title (_("Library")); + s_playlist.active_playlist (); } -static int get_playlist (bool require_added, bool require_scanned) +static bool check_playlist (bool require_added, bool require_scanned) { - if (playlist_id < 0) - return -1; - - int list = aud_playlist_by_unique_id (playlist_id); - - if (list < 0) + if (! s_playlist.exists ()) { - playlist_id = -1; - return -1; + s_playlist = Playlist (); + return false; } - if (require_added && aud_playlist_add_in_progress (list)) - return -1; - if (require_scanned && aud_playlist_scan_in_progress (list)) - return -1; + if (require_added && s_playlist.add_in_progress ()) + return false; + if (require_scanned && s_playlist.scan_in_progress ()) + return false; - return list; + return true; } static String get_uri () @@ -178,21 +170,21 @@ static String get_uri () static void destroy_database () { - items.clear (); - hidden_items = 0; - database.clear (); - database_valid = false; + s_items.clear (); + s_hidden_items = 0; + s_database.clear (); + s_database_valid = false; } -static void create_database (int list) +static void create_database () { destroy_database (); - int entries = aud_playlist_entry_count (list); + int entries = s_playlist.n_entries (); for (int e = 0; e < entries; e ++) { - Tuple tuple = aud_playlist_entry_get_tuple (list, e, Playlist::NoWait); + Tuple tuple = s_playlist.entry_tuple (e, Playlist::NoWait); aud::array<SearchField, String> fields; fields[SearchField::Genre] = tuple.get_str (Tuple::Genre); @@ -201,7 +193,7 @@ static void create_database (int list) fields[SearchField::Title] = tuple.get_str (Tuple::Title); Item * parent = nullptr; - SimpleHash<Key, Item> * hash = & database; + SimpleHash<Key, Item> * hash = & s_database; for (auto f : aud::range<SearchField> ()) { @@ -225,34 +217,33 @@ static void create_database (int list) } } - database_valid = true; + s_database_valid = true; } -static void search_cb (const Key & key, Item & item, void * _state) +static void search_recurse (SimpleHash<Key, Item> & domain, int mask, Index<const Item *> & results) { - SearchState * state = (SearchState *) _state; - - int oldmask = state->mask; - int count = search_terms.len (); - - for (int t = 0, bit = 1; t < count; t ++, bit <<= 1) + domain.iterate ([mask, & results] (const Key & key, Item & item) { - if (! (state->mask & bit)) - continue; /* skip term if it is already found */ + int count = s_search_terms.len (); + int new_mask = mask; - if (strstr (item.folded, search_terms[t])) - state->mask &= ~bit; /* we found it */ - else if (! item.children.n_items ()) - break; /* quit early if there are no children to search */ - } + for (int t = 0, bit = 1; t < count; t ++, bit <<= 1) + { + if (! (new_mask & bit)) + continue; /* skip term if it is already found */ - /* adding an item with exactly one child is redundant, so avoid it */ - if (! state->mask && item.children.n_items () != 1) - state->items.append (& item); + if (strstr (item.folded, s_search_terms[t])) + new_mask &= ~bit; /* we found it */ + else if (! item.children.n_items ()) + break; /* quit early if there are no children to search */ + } - item.children.iterate (search_cb, state); + /* adding an item with exactly one child is redundant, so avoid it */ + if (! new_mask && item.children.n_items () != 1) + results.append (& item); - state->mask = oldmask; + search_recurse (item.children, new_mask, results); + }); } static int item_compare (const Item * const & a, const Item * const & b) @@ -284,104 +275,96 @@ static int item_compare_pass1 (const Item * const & a, const Item * const & b) static void do_search () { - items.clear (); - hidden_items = 0; + s_items.clear (); + s_hidden_items = 0; - if (! database_valid) + if (! s_database_valid) return; - SearchState state; - /* effectively limits number of search terms to 32 */ - state.mask = (1 << search_terms.len ()) - 1; - - database.iterate (search_cb, & state); - - items = std::move (state.items); + search_recurse (s_database, (1 << s_search_terms.len ()) - 1, s_items); /* first sort by number of songs per item */ - items.sort (item_compare_pass1); + s_items.sort (item_compare_pass1); /* limit to items with most songs */ - if (items.len () > MAX_RESULTS) + if (s_items.len () > MAX_RESULTS) { - hidden_items = items.len () - MAX_RESULTS; - items.remove (MAX_RESULTS, -1); + s_hidden_items = s_items.len () - MAX_RESULTS; + s_items.remove (MAX_RESULTS, -1); } /* sort by item type, then item name */ - items.sort (item_compare); + s_items.sort (item_compare); - selection.remove (0, -1); - selection.insert (0, items.len ()); - if (items.len ()) - selection[0] = true; + s_selection.remove (0, -1); + s_selection.insert (0, s_items.len ()); + if (s_items.len ()) + s_selection[0] = true; } static bool filter_cb (const char * filename, void * unused) { bool add = false; - tiny_lock (& adding_lock); + tiny_lock (& s_adding_lock); - if (adding) + if (s_adding) { - bool * added = added_table.lookup (String (filename)); + bool * added = s_added_table.lookup (String (filename)); if ((add = ! added)) - added_table.add (String (filename), true); + s_added_table.add (String (filename), true); else (* added) = true; } - tiny_unlock (& adding_lock); + tiny_unlock (& s_adding_lock); return add; } static void begin_add (const char * uri) { - if (adding) + if (s_adding) return; - int list = get_playlist (false, false); - - if (list < 0) - list = create_playlist (); + if (! check_playlist (false, false)) + create_playlist (); /* if possible, store local path for compatibility with older versions */ StringBuf path = uri_to_filename (uri); aud_set_str ("search-tool", "path", path ? path : uri); - added_table.clear (); + s_added_table.clear (); - int entries = aud_playlist_entry_count (list); + int entries = s_playlist.n_entries (); for (int entry = 0; entry < entries; entry ++) { - String filename = aud_playlist_entry_get_filename (list, entry); + String filename = s_playlist.entry_filename (entry); - if (! added_table.lookup (filename)) + if (! s_added_table.lookup (filename)) { - aud_playlist_entry_set_selected (list, entry, false); - added_table.add (filename, false); + s_playlist.select_entry (entry, false); + s_added_table.add (filename, false); } else - aud_playlist_entry_set_selected (list, entry, true); + s_playlist.select_entry (entry, true); } - aud_playlist_delete_selected (list); + s_playlist.remove_selected (); - tiny_lock (& adding_lock); - adding = true; - tiny_unlock (& adding_lock); + tiny_lock (& s_adding_lock); + s_adding = true; + tiny_unlock (& s_adding_lock); Index<PlaylistAddItem> add; add.append (String (uri)); - aud_playlist_entry_insert_filtered (list, -1, std::move (add), filter_cb, nullptr, false); + s_playlist.insert_filtered (-1, std::move (add), filter_cb, nullptr, false); } static void show_hide_widgets () { - if (playlist_id < 0) + if (s_playlist == Playlist ()) { gtk_widget_hide (wait_label); gtk_widget_hide (scrolled); @@ -392,7 +375,7 @@ static void show_hide_widgets () { gtk_widget_hide (help_label); - if (database_valid) + if (s_database_valid) { gtk_widget_hide (wait_label); gtk_widget_show (scrolled); @@ -412,32 +395,27 @@ static void search_timeout (void * = nullptr) do_search (); audgui_list_delete_rows (results_list, 0, audgui_list_row_count (results_list)); - audgui_list_insert_rows (results_list, 0, items.len ()); - - int total = items.len () + hidden_items; - StringBuf stats = str_printf (dngettext (PACKAGE, "%d result", - "%d results", total), total); + audgui_list_insert_rows (results_list, 0, s_items.len ()); - if (hidden_items) - { - stats.insert (-1, " "); - stats.combine (str_printf (dngettext (PACKAGE, "(%d hidden)", - "(%d hidden)", hidden_items), hidden_items)); - } + int total = s_items.len () + s_hidden_items; - gtk_label_set_text ((GtkLabel *) stats_label, stats); + if (s_hidden_items) + gtk_label_set_text ((GtkLabel *) stats_label, + str_printf (dngettext (PACKAGE, "%d of %d result shown", + "%d of %d results shown", total), s_items.len (), total)); + else + gtk_label_set_text ((GtkLabel *) stats_label, + str_printf (dngettext (PACKAGE, "%d result", "%d results", total), total)); - search_timer.stop (); - search_pending = false; + s_search_timer.stop (); + s_search_pending = false; } static void update_database () { - int list = get_playlist (true, true); - - if (list >= 0) + if (check_playlist (true, true)) { - create_database (list); + create_database (); search_timeout (); } else @@ -450,62 +428,57 @@ static void update_database () show_hide_widgets (); } -static void add_complete_cb (void * unused, void * unused2) +static void add_complete_cb (void *, void *) { - int list = get_playlist (true, false); - if (list < 0) + if (! check_playlist (true, false)) return; - if (adding) + if (s_adding) { - tiny_lock (& adding_lock); - adding = false; - tiny_unlock (& adding_lock); + tiny_lock (& s_adding_lock); + s_adding = false; + tiny_unlock (& s_adding_lock); - int entries = aud_playlist_entry_count (list); + int entries = s_playlist.n_entries (); for (int entry = 0; entry < entries; entry ++) { - String filename = aud_playlist_entry_get_filename (list, entry); - bool * added = added_table.lookup (filename); + String filename = s_playlist.entry_filename (entry); + bool * added = s_added_table.lookup (filename); - aud_playlist_entry_set_selected (list, entry, ! added || ! (* added)); + s_playlist.select_entry (entry, ! added || ! (* added)); } - added_table.clear (); + s_added_table.clear (); /* don't clear the playlist if nothing was added */ - if (aud_playlist_selected_count (list) < aud_playlist_entry_count (list)) - aud_playlist_delete_selected (list); + if (s_playlist.n_selected () < entries) + s_playlist.remove_selected (); else - aud_playlist_select_all (list, false); + s_playlist.select_all (false); - aud_playlist_sort_by_scheme (list, Playlist::Path); + s_playlist.sort_entries (Playlist::Path); } - if (! database_valid && ! aud_playlist_update_pending (list)) + if (! s_database_valid && ! s_playlist.update_pending ()) update_database (); } -static void scan_complete_cb (void * unused, void * unused2) +static void scan_complete_cb (void *, void *) { - int list = get_playlist (true, true); - if (list < 0) + if (! check_playlist (true, true)) return; - if (! database_valid && ! aud_playlist_update_pending (list)) + if (! s_database_valid && ! s_playlist.update_pending ()) update_database (); } -static void playlist_update_cb (void * data, void * unused) +static void playlist_update_cb (void *, void *) { - if (! database_valid) - update_database (); - else + if (! s_database_valid || ! check_playlist (true, true) || + s_playlist.update_detail ().level >= Playlist::Metadata) { - int list = get_playlist (true, true); - if (list < 0 || aud_playlist_update_detail (list).level >= Playlist::Metadata) - update_database (); + update_database (); } } @@ -526,28 +499,27 @@ static void search_cleanup () hook_dissociate ("playlist scan complete", scan_complete_cb); hook_dissociate ("playlist update", playlist_update_cb); - search_timer.stop (); - search_pending = false; + s_search_timer.stop (); + s_search_pending = false; - search_terms.clear (); - items.clear (); - selection.clear (); + s_search_terms.clear (); + s_items.clear (); + s_selection.clear (); - tiny_lock (& adding_lock); - adding = false; - tiny_unlock (& adding_lock); + tiny_lock (& s_adding_lock); + s_adding = false; + tiny_unlock (& s_adding_lock); - added_table.clear (); + s_added_table.clear (); destroy_database (); } static void do_add (bool play, bool set_title) { - if (search_pending) + if (s_search_pending) search_timeout (); - int list = aud_playlist_by_unique_id (playlist_id); - int n_items = items.len (); + int n_items = s_items.len (); int n_selected = 0; Index<PlaylistAddItem> add; @@ -555,17 +527,17 @@ static void do_add (bool play, bool set_title) for (int i = 0; i < n_items; i ++) { - if (! selection[i]) + if (! s_selection[i]) continue; - const Item * item = items[i]; + const Item * item = s_items[i]; for (int entry : item->matches) { add.append ( - aud_playlist_entry_get_filename (list, entry), - aud_playlist_entry_get_tuple (list, entry, Playlist::NoWait), - aud_playlist_entry_get_decoder (list, entry, Playlist::NoWait) + s_playlist.entry_filename (entry), + s_playlist.entry_tuple (entry, Playlist::NoWait), + s_playlist.entry_decoder (entry, Playlist::NoWait) ); } @@ -574,77 +546,95 @@ static void do_add (bool play, bool set_title) title = item->name; } - int list2 = aud_playlist_get_active (); - aud_playlist_entry_insert_batch (list2, -1, std::move (add), play); + auto list2 = Playlist::active_playlist (); + list2.insert_items (-1, std::move (add), play); if (set_title && n_selected == 1) - aud_playlist_set_title (list2, title); + list2.set_title (title); } static void action_play () { - aud_playlist_set_active (aud_playlist_get_temporary ()); + Playlist::temporary_playlist ().activate (); do_add (true, false); } static void action_create_playlist () { - aud_playlist_new (); + Playlist::new_playlist (); do_add (false, true); } static void action_add_to_playlist () { - if (aud_playlist_by_unique_id (playlist_id) != aud_playlist_get_active ()) + if (s_playlist != Playlist::active_playlist ()) do_add (false, false); } static void list_get_value (void * user, int row, int column, GValue * value) { - g_return_if_fail (row >= 0 && row < items.len ()); + static constexpr aud::array<SearchField, const char *> start_tags = + {"", "<b>", "<i>", ""}; + static constexpr aud::array<SearchField, const char *> end_tags = + {"", "</b>", "</i>", ""}; + + auto escape = [] (const char * s) + { return CharPtr (g_markup_escape_text (s, -1)); }; - const Item * item = items[row]; - StringBuf string = str_concat ({item->name, "\n"}); + g_return_if_fail (row >= 0 && row < s_items.len ()); + + const Item * item = s_items[row]; + + CharPtr name = escape ((item->field == SearchField::Genre) ? + str_toupper_utf8 (item->name) : item->name); + + StringBuf desc (0); if (item->field != SearchField::Title) { - string.insert (-1, " "); - string.combine (str_printf (dngettext (PACKAGE, "%d song", "%d songs", + desc.insert (-1, " "); + desc.combine (str_printf (dngettext (PACKAGE, "%d song", "%d songs", item->matches.len ()), item->matches.len ())); } if (item->field == SearchField::Genre) { - string.insert (-1, " "); - string.insert (-1, _("of this genre")); + desc.insert (-1, " "); + desc.insert (-1, _("of this genre")); } - while ((item = item->parent)) + if (item->parent) { - string.insert (-1, " "); - string.insert (-1, (item->field == SearchField::Album) ? _("on") : _("by")); - string.insert (-1, " "); - string.insert (-1, item->name); + auto parent = (item->parent->parent ? item->parent->parent : item->parent); + + desc.insert (-1, " "); + desc.insert (-1, (parent->field == SearchField::Album) ? _("on") : _("by")); + desc.insert (-1, " "); + desc.insert (-1, start_tags[parent->field]); + desc.insert (-1, escape (parent->name)); + desc.insert (-1, end_tags[parent->field]); } - g_value_set_string (value, string); + g_value_take_string (value, g_strdup_printf + ("%s%s%s\n<small>%s</small>", start_tags[item->field], (const char *) name, + end_tags[item->field], (const char *) desc)); } static bool list_get_selected (void * user, int row) { - g_return_val_if_fail (row >= 0 && row < selection.len (), false); - return selection[row]; + g_return_val_if_fail (row >= 0 && row < s_selection.len (), false); + return s_selection[row]; } static void list_set_selected (void * user, int row, bool selected) { - g_return_if_fail (row >= 0 && row < selection.len ()); - selection[row] = selected; + g_return_if_fail (row >= 0 && row < s_selection.len ()); + s_selection[row] = selected; } static void list_select_all (void * user, bool selected) { - for (bool & s : selection) + for (bool & s : s_selection) s = selected; } @@ -671,36 +661,34 @@ static void list_right_click (void * user, GdkEventButton * event) static Index<char> list_get_data (void * user) { - if (search_pending) + if (s_search_pending) search_timeout (); - int list = aud_playlist_by_unique_id (playlist_id); - int n_items = items.len (); - + int n_items = s_items.len (); Index<char> buf; - aud_playlist_select_all (list, false); + s_playlist.select_all (false); for (int i = 0; i < n_items; i ++) { - if (! selection[i]) + if (! s_selection[i]) continue; - const Item * item = items[i]; + const Item * item = s_items[i]; for (int entry : item->matches) { if (buf.len ()) buf.append ('\n'); - String filename = aud_playlist_entry_get_filename (list, entry); + String filename = s_playlist.entry_filename (entry); buf.insert (filename, -1, strlen (filename)); - aud_playlist_entry_set_selected (list, entry, true); + s_playlist.select_entry (entry, true); } } - aud_playlist_cache_selected (list); + s_playlist.cache_selected (); return buf; } @@ -721,9 +709,9 @@ static const AudguiListCallbacks list_callbacks = { static void entry_cb (GtkEntry * entry, void * unused) { const char * text = gtk_entry_get_text ((GtkEntry *) entry); - search_terms = str_list_to_index (str_tolower_utf8 (text), " "); - search_timer.queue (SEARCH_DELAY, search_timeout, nullptr); - search_pending = true; + s_search_terms = str_list_to_index (str_tolower_utf8 (text), " "); + s_search_timer.queue (SEARCH_DELAY, search_timeout, nullptr); + s_search_pending = true; } static void file_entry_cb (GtkEntry * entry, GtkWidget * button) @@ -775,10 +763,10 @@ void * SearchTool::get_gtk_widget () gtk_widget_set_no_show_all (scrolled, true); gtk_box_pack_start ((GtkBox *) vbox, scrolled, true, true, 0); - results_list = audgui_list_new (& list_callbacks, nullptr, items.len ()); + results_list = audgui_list_new (& list_callbacks, nullptr, s_items.len ()); g_signal_connect (results_list, "destroy", (GCallback) gtk_widget_destroyed, & results_list); gtk_tree_view_set_headers_visible ((GtkTreeView *) results_list, false); - audgui_list_add_column (results_list, nullptr, 0, G_TYPE_STRING, -1); + audgui_list_add_column (results_list, nullptr, 0, G_TYPE_STRING, -1, true); gtk_container_add ((GtkContainer *) scrolled, results_list); stats_label = gtk_label_new (""); diff --git a/src/sid/xmms-sid.cc b/src/sid/xmms-sid.cc index e38f040..5495068 100644 --- a/src/sid/xmms-sid.cc +++ b/src/sid/xmms-sid.cc @@ -65,7 +65,6 @@ public: private: bool delayed_init(); - pthread_mutex_t m_init_mutex = PTHREAD_MUTEX_INITIALIZER; bool m_initialized = false; bool m_init_failed = false; }; @@ -74,12 +73,14 @@ EXPORT SIDPlugin aud_plugin_instance; static void xs_get_song_tuple_info(Tuple &pResult, const xs_tuneinfo_t &info, int subTune); +static pthread_mutex_t s_init_mutex = PTHREAD_MUTEX_INITIALIZER; + /* * Initialization functions */ bool SIDPlugin::delayed_init() { - pthread_mutex_lock(&m_init_mutex); + pthread_mutex_lock(&s_init_mutex); if (!m_initialized && !m_init_failed) { @@ -89,7 +90,7 @@ bool SIDPlugin::delayed_init() m_init_failed = true; } - pthread_mutex_unlock(&m_init_mutex); + pthread_mutex_unlock(&s_init_mutex); return m_initialized; } diff --git a/src/skins-qt/Makefile b/src/skins-qt/Makefile index 491ff76..cdbc578 100644 --- a/src/skins-qt/Makefile +++ b/src/skins-qt/Makefile @@ -2,6 +2,7 @@ PLUGIN = skins-qt${PLUGIN_SUFFIX} SRCS = actions.cc \ button.cc \ + dialogs-qt.cc \ dock.cc \ drag-handle.cc \ eq-graph.cc \ @@ -10,6 +11,7 @@ SRCS = actions.cc \ hslider.cc \ main.cc \ menurow.cc \ + menu-ops.cc \ menus.cc \ monostereo.cc \ number.cc \ diff --git a/src/skins-qt/actions.cc b/src/skins-qt/actions.cc index a01db44..48cde8b 100644 --- a/src/skins-qt/actions.cc +++ b/src/skins-qt/actions.cc @@ -36,7 +36,7 @@ #include <libaudcore/plugins.h> #include <libaudqt/libaudqt.h> -#define ACTIVE (aud_playlist_get_active ()) +#define ACTIVE (Playlist::active_playlist ()) void action_ab_clear () { diff --git a/src/skins-qt/dialogs-qt.cc b/src/skins-qt/dialogs-qt.cc new file mode 100644 index 0000000..70524e6 --- /dev/null +++ b/src/skins-qt/dialogs-qt.cc @@ -0,0 +1 @@ +#include "../ui-common/dialogs-qt.cc" diff --git a/src/skins-qt/main.cc b/src/skins-qt/main.cc index f1db1a2..abe28b6 100644 --- a/src/skins-qt/main.cc +++ b/src/skins-qt/main.cc @@ -37,6 +37,8 @@ #include <libaudcore/runtime.h> #include <libaudqt/libaudqt.h> +#include "../ui-common/dialogs-qt.h" + #include "actions-mainwin.h" #include "actions-playlist.h" #include "dnd.h" @@ -69,9 +71,12 @@ public: MainWindow (bool shaded) : Window (WINDOW_MAIN, & config.player_x, & config.player_y, shaded ? MAINWIN_SHADED_WIDTH : skin.hints.mainwin_width, - shaded ? MAINWIN_SHADED_HEIGHT : skin.hints.mainwin_height, shaded) {} + shaded ? MAINWIN_SHADED_HEIGHT : skin.hints.mainwin_height, shaded), + m_dialogs (this) {} private: + DialogWindows m_dialogs; + void draw (QPainter & cr); bool button_press (QMouseEvent * event); bool scroll (QWheelEvent * event); @@ -129,6 +134,7 @@ static void format_time (char buf[7], int time, int length) if (remaining && length > 0) { time = (length - time) / 1000; + time = aud::clamp(0, time, 359999); // 99:59:59 if (time < 60) snprintf (buf, 7, zero ? "-00:%02d" : " -0:%02d", time); @@ -140,6 +146,7 @@ static void format_time (char buf[7], int time, int length) else { time /= 1000; + time = aud::clamp(0, time, 3599999); // 999:59:59 if (time < 6000) snprintf (buf, 7, zero ? " %02d:%02d" : " %2d:%02d", time / 60, time % 60); diff --git a/src/skins-qt/menu-ops.cc b/src/skins-qt/menu-ops.cc new file mode 100644 index 0000000..21b5eda --- /dev/null +++ b/src/skins-qt/menu-ops.cc @@ -0,0 +1,2 @@ +#include "../ui-common/menu-ops.cc" +#include "../ui-common/menu-ops-qt.cc" diff --git a/src/skins-qt/menus.cc b/src/skins-qt/menus.cc index b302fd8..2df225f 100644 --- a/src/skins-qt/menus.cc +++ b/src/skins-qt/menus.cc @@ -58,37 +58,13 @@ static void configure_visualizations () { audqt::prefswin_show_plugin_page (Plug static void skins_volume_up () { mainwin_set_volume_diff (5); } static void skins_volume_down () { mainwin_set_volume_diff (-5); } -/* emulate a config item for the recording toggle */ -static void toggle_record () -{ - bool enable = aud_get_bool ("skins", "record"); - - if (aud_drct_enable_record (enable)) - mainwin_show_status_message (enable ? _("Recording on") : _("Recording off")); - else - { - aud_set_bool ("skins", "record", aud_drct_get_record_enabled ()); - hook_call ("skins set record", nullptr); - } -} - -static void record_toggled (void * = nullptr, void * = nullptr) -{ - bool enabled = aud_drct_get_record_enabled (); - if (enabled != aud_get_bool ("skins", "record")) - { - aud_set_bool ("skins", "record", enabled); - hook_call ("skins set record", nullptr); - } -} - static const audqt::MenuItem output_items[] = { audqt::MenuCommand ({N_("Volume Up"), "audio-volume-high", "+"}, skins_volume_up), audqt::MenuCommand ({N_("Volume Down"), "audio-volume-low", "-"}, skins_volume_down), audqt::MenuSep (), audqt::MenuCommand ({N_("Effects ...")}, configure_effects), audqt::MenuSep (), - audqt::MenuToggle ({N_("Record Stream"), "media-record", "D"}, {"skins", "record", "skins set record"}, toggle_record), + audqt::MenuToggle ({N_("Record Stream"), "media-record", "D"}, {nullptr, "record", "set record"}), audqt::MenuCommand ({N_("Audio Settings ..."), "audio-card"}, configure_output) }; @@ -137,7 +113,7 @@ static const audqt::MenuItem playback_items[] = { static const audqt::MenuItem playlist_items[] = { audqt::MenuCommand ({N_("Play/Resume"), "media-playback-start", "Shift+Return"}, pl_play), audqt::MenuSep (), - audqt::MenuCommand ({N_("New Playlist"), "document-new", "Shift+N"}, (audqt::MenuFunc) aud_playlist_new), + audqt::MenuCommand ({N_("New Playlist"), "document-new", "Shift+N"}, pl_new), audqt::MenuCommand ({N_("Rename Playlist ..."), "insert-text", "F2"}, action_playlist_rename), audqt::MenuCommand ({N_("Remove Playlist"), "edit-delete", "Shift+D"}, action_playlist_delete), audqt::MenuSep (), @@ -218,7 +194,8 @@ static const audqt::MenuItem sort_items[] = { audqt::MenuCommand ({N_("By Length")}, sort_length), audqt::MenuCommand ({N_("By File Name")}, sort_filename), audqt::MenuCommand ({N_("By File Path")}, sort_path), - audqt::MenuCommand ({N_("By Custom Title")}, sort_custom_title) + audqt::MenuCommand ({N_("By Custom Title")}, sort_custom_title), + audqt::MenuCommand ({N_("By Comment")}, sort_comment) }; static const audqt::MenuItem sort_selected_items[] = { @@ -232,7 +209,8 @@ static const audqt::MenuItem sort_selected_items[] = { audqt::MenuCommand ({N_("By Length")}, sort_sel_length), audqt::MenuCommand ({N_("By File Name")}, sort_sel_filename), audqt::MenuCommand ({N_("By File Path")}, sort_sel_path), - audqt::MenuCommand ({N_("By Custom Title")}, sort_sel_custom_title) + audqt::MenuCommand ({N_("By Custom Title")}, sort_sel_custom_title), + audqt::MenuCommand ({N_("By Comment")}, sort_sel_comment) }; static const audqt::MenuItem playlist_sort_items[] = { @@ -271,18 +249,10 @@ void menu_init (QWidget * parent) {playlist_context_items} }; - record_toggled (); - hook_associate ("enable record", record_toggled, nullptr); - for (int i = UI_MENUS; i --; ) menus[i] = audqt::menu_build (table[i], parent); } -void menu_cleanup () -{ - hook_dissociate ("enable record", record_toggled); -} - void menu_popup (int id, int x, int y, bool leftward, bool upward) { if (leftward || upward) diff --git a/src/skins-qt/menus.h b/src/skins-qt/menus.h index 463e60d..52d1296 100644 --- a/src/skins-qt/menus.h +++ b/src/skins-qt/menus.h @@ -38,8 +38,6 @@ enum { }; void menu_init (QWidget * parent); -void menu_cleanup (); - void menu_popup (int id, int x, int y, bool leftward, bool upward); #endif /* SKINS_MENUS_H */ diff --git a/src/skins-qt/playlist-slider.cc b/src/skins-qt/playlist-slider.cc index d2bf53c..8e6f16f 100644 --- a/src/skins-qt/playlist-slider.cc +++ b/src/skins-qt/playlist-slider.cc @@ -103,7 +103,7 @@ bool PlaylistSlider::motion (QMouseEvent * event) PlaylistSlider::PlaylistSlider (PlaylistWidget * list, int height) : m_list (list), m_height (height), - m_length (aud_playlist_entry_count (aud_playlist_get_active ())) + m_length (Playlist::active_playlist ().n_entries ()) { set_scale (config.scale); add_input (8, height, true, true); @@ -118,6 +118,6 @@ void PlaylistSlider::resize (int height) void PlaylistSlider::refresh () { - m_length = aud_playlist_entry_count (aud_playlist_get_active ()); + m_length = Playlist::active_playlist ().n_entries (); queue_draw (); } diff --git a/src/skins-qt/playlist-widget.cc b/src/skins-qt/playlist-widget.cc index af7606e..b59e1b9 100644 --- a/src/skins-qt/playlist-widget.cc +++ b/src/skins-qt/playlist-widget.cc @@ -46,11 +46,11 @@ enum { void PlaylistWidget::update_title () { - if (aud_playlist_count () > 1) + if (Playlist::n_playlists () > 1) { - String title = aud_playlist_get_title (m_playlist); + String title = m_playlist.get_title (); m_title_text = String (str_printf (_("%s (%d of %d)"), - (const char *) title, 1 + m_playlist, aud_playlist_count ())); + (const char *) title, 1 + m_playlist.index (), Playlist::n_playlists ())); } else m_title_text = String (); @@ -93,7 +93,7 @@ int PlaylistWidget::adjust_position (bool relative, int position) const if (relative) { - int focus = aud_playlist_get_focus (m_playlist); + int focus = m_playlist.get_focus (); if (focus == -1) return 0; @@ -129,7 +129,7 @@ void PlaylistWidget::cancel_all () void PlaylistWidget::draw (QPainter & cr) { - int active_entry = aud_playlist_get_position (m_playlist); + int active_entry = m_playlist.get_position (); int left = 3, right = 3; int width; QRect rect; @@ -153,7 +153,7 @@ void PlaylistWidget::draw (QPainter & cr) for (int i = m_first; i < m_first + m_rows && i < m_length; i ++) { - if (aud_playlist_entry_get_selected (m_playlist, i)) + if (m_playlist.entry_selected (i)) cr.fillRect (0, m_offset + m_row_height * (i - m_first), m_width, m_row_height, QColor (skin.colors[SKIN_PLEDIT_SELECTEDBG])); } @@ -187,7 +187,7 @@ void PlaylistWidget::draw (QPainter & cr) for (int i = m_first; i < m_first + m_rows && i < m_length; i ++) { - Tuple tuple = aud_playlist_entry_get_tuple (m_playlist, i, Playlist::NoWait); + Tuple tuple = m_playlist.entry_tuple (i, Playlist::NoWait); int len = tuple.get_int (Tuple::Length); if (len < 0) continue; @@ -206,13 +206,13 @@ void PlaylistWidget::draw (QPainter & cr) /* queue positions */ - if (aud_playlist_queue_count (m_playlist)) + if (m_playlist.n_queued ()) { width = 0; for (int i = m_first; i < m_first + m_rows && i < m_length; i ++) { - int pos = aud_playlist_queue_find_entry (m_playlist, i); + int pos = m_playlist.queue_find_entry (i); if (pos < 0) continue; @@ -235,7 +235,7 @@ void PlaylistWidget::draw (QPainter & cr) for (int i = m_first; i < m_first + m_rows && i < m_length; i ++) { - Tuple tuple = aud_playlist_entry_get_tuple (m_playlist, i, Playlist::NoWait); + Tuple tuple = m_playlist.entry_tuple (i, Playlist::NoWait); String title = tuple.get_str (Tuple::FormattedTitle); cr.setPen (QColor (skin.colors[(i == active_entry) ? @@ -247,12 +247,11 @@ void PlaylistWidget::draw (QPainter & cr) /* focus rectangle */ - int focus = aud_playlist_get_focus (m_playlist); + int focus = m_playlist.get_focus (); /* don't show rectangle if this is the only selected entry */ if (focus >= m_first && focus <= m_first + m_rows - 1 && - (! aud_playlist_entry_get_selected (m_playlist, focus) || - aud_playlist_selected_count (m_playlist) > 1)) + (! m_playlist.entry_selected (focus) || m_playlist.n_selected () > 1)) { cr.setPen (QColor (skin.colors[SKIN_PLEDIT_NORMAL])); cr.drawRect (0, m_offset + m_row_height * (focus - m_first), m_width - 1, m_row_height - 1); @@ -300,18 +299,18 @@ void PlaylistWidget::set_font (const char * font) void PlaylistWidget::refresh () { - m_playlist = aud_playlist_get_active (); - m_length = aud_playlist_entry_count (m_playlist); + auto prev_playlist = m_playlist; + m_playlist = Playlist::active_playlist (); + m_length = m_playlist.n_entries (); + update_title (); calc_layout (); - int id = aud_playlist_get_unique_id (m_playlist); - if (m_playlist_id != id) + if (m_playlist != prev_playlist) { cancel_all (); - m_playlist_id = id; m_first = 0; - ensure_visible (aud_playlist_get_focus (m_playlist)); + ensure_visible (m_playlist.get_focus ()); } queue_draw (); @@ -335,9 +334,9 @@ void PlaylistWidget::select_single (bool relative, int position) if (position == -1) return; - aud_playlist_select_all (m_playlist, false); - aud_playlist_entry_set_selected (m_playlist, position, true); - aud_playlist_set_focus (m_playlist, position); + m_playlist.select_all (false); + m_playlist.select_entry (position, true); + m_playlist.set_focus (position); ensure_visible (position); } @@ -352,11 +351,10 @@ void PlaylistWidget::select_extend (bool relative, int position) int sign = (position > count) ? 1 : -1; for (; count != position; count += sign) - aud_playlist_entry_set_selected (m_playlist, count, - ! aud_playlist_entry_get_selected (m_playlist, count + sign)); + m_playlist.select_entry (count, ! m_playlist.entry_selected (count + sign)); - aud_playlist_entry_set_selected (m_playlist, position, true); - aud_playlist_set_focus (m_playlist, position); + m_playlist.select_entry (position, true); + m_playlist.set_focus (position); ensure_visible (position); } @@ -367,7 +365,7 @@ void PlaylistWidget::select_slide (bool relative, int position) if (position == -1) return; - aud_playlist_set_focus (m_playlist, position); + m_playlist.set_focus (position); ensure_visible (position); } @@ -378,34 +376,33 @@ void PlaylistWidget::select_toggle (bool relative, int position) if (position == -1) return; - aud_playlist_entry_set_selected (m_playlist, position, - ! aud_playlist_entry_get_selected (m_playlist, position)); - aud_playlist_set_focus (m_playlist, position); + m_playlist.select_entry (position, ! m_playlist.entry_selected (position)); + m_playlist.set_focus (position); ensure_visible (position); } void PlaylistWidget::select_move (bool relative, int position) { - int focus = aud_playlist_get_focus (m_playlist); + int focus = m_playlist.get_focus (); position = adjust_position (relative, position); if (focus == -1 || position == -1 || position == focus) return; - focus += aud_playlist_shift (m_playlist, focus, position - focus); + focus += m_playlist.shift_entries (focus, position - focus); ensure_visible (focus); } void PlaylistWidget::delete_selected () { - aud_playlist_delete_selected (m_playlist); + m_playlist.remove_selected (); - m_length = aud_playlist_entry_count (m_playlist); - int focus = aud_playlist_get_focus (m_playlist); + m_length = m_playlist.n_entries (); + int focus = m_playlist.get_focus (); if (focus != -1) { - aud_playlist_entry_set_selected (m_playlist, focus, true); + m_playlist.select_entry (focus, true); ensure_visible (focus); } } @@ -439,11 +436,11 @@ bool PlaylistWidget::handle_keypress (QKeyEvent * event) break; case Qt::Key_Return: select_single (true, 0); - aud_playlist_set_position (m_playlist, aud_playlist_get_focus (m_playlist)); - aud_playlist_play (m_playlist); + m_playlist.set_position (m_playlist.get_focus ()); + m_playlist.start_playback (); break; case Qt::Key_Escape: - select_single (false, aud_playlist_get_position (m_playlist)); + select_single (false, m_playlist.get_position ()); break; case Qt::Key_Delete: delete_selected (); @@ -554,7 +551,7 @@ void PlaylistWidget::scroll_to (int row) void PlaylistWidget::set_focused (int row) { cancel_all (); - aud_playlist_set_focus (m_playlist, row); + m_playlist.set_focus (row); ensure_visible (row); refresh (); } @@ -608,7 +605,7 @@ bool PlaylistWidget::button_press (QMouseEvent * event) switch (state) { case 0: - if (aud_playlist_entry_get_selected (m_playlist, position)) + if (m_playlist.entry_selected (position)) select_slide (false, position); else select_single (false, position); @@ -634,7 +631,7 @@ bool PlaylistWidget::button_press (QMouseEvent * event) if (position != -1 && position != m_length) { - if (aud_playlist_entry_get_selected (m_playlist, position)) + if (m_playlist.entry_selected (position)) select_slide (false, position); else select_single (false, position); @@ -654,9 +651,9 @@ bool PlaylistWidget::button_press (QMouseEvent * event) return true; if (position != -1) - aud_playlist_set_position (m_playlist, position); + m_playlist.set_position (position); - aud_playlist_play (m_playlist); + m_playlist.start_playback (); break; default: return true; diff --git a/src/skins-qt/playlist-widget.h b/src/skins-qt/playlist-widget.h index 4167be5..58e3123 100644 --- a/src/skins-qt/playlist-widget.h +++ b/src/skins-qt/playlist-widget.h @@ -30,7 +30,7 @@ #include <libaudcore/hook.h> #include <libaudcore/mainloop.h> -#include <libaudcore/objects.h> +#include <libaudcore/playlist.h> #include "widget.h" @@ -89,7 +89,8 @@ private: SmartPtr<QFontMetrics> m_metrics; String m_title_text; - int m_playlist = -1, m_playlist_id = -1, m_length = 0; + Playlist m_playlist; + int m_length = 0; int m_width = 0, m_height = 0, m_row_height = 1, m_offset = 0, m_rows = 0, m_first = 0; int m_scroll = 0, m_hover = -1, m_drag = 0, m_popup_pos = -1; QueuedFunc m_popup_timer; diff --git a/src/skins-qt/playlist.cc b/src/skins-qt/playlist.cc index 3b63b34..e8e8efe 100644 --- a/src/skins-qt/playlist.cc +++ b/src/skins-qt/playlist.cc @@ -94,17 +94,17 @@ static bool song_changed; static void update_info () { - int playlist = aud_playlist_get_active (); - StringBuf s1 = str_format_time (aud_playlist_get_selected_length (playlist)); - StringBuf s2 = str_format_time (aud_playlist_get_total_length (playlist)); + auto playlist = Playlist::active_playlist (); + StringBuf s1 = str_format_time (playlist.selected_length_ms ()); + StringBuf s2 = str_format_time (playlist.total_length_ms ()); playlistwin_info->set_text (str_concat ({s1, "/", s2})); } static void update_rollup_text () { - int playlist = aud_playlist_get_active (); - int entry = aud_playlist_get_position (playlist); - Tuple tuple = aud_playlist_entry_get_tuple (playlist, entry, Playlist::NoWait); + auto playlist = Playlist::active_playlist (); + int entry = playlist.get_position (); + Tuple tuple = playlist.entry_tuple (entry, Playlist::NoWait); char scratch[512]; scratch[0] = 0; @@ -281,7 +281,7 @@ static void drag_drop (GtkWidget * widget, GdkDragContext * context, int x, static void drag_data_received (GtkWidget * widget, GdkDragContext * context, int x, int y, GtkSelectionData * data, unsigned info, unsigned time, void * unused) { - audgui_urilist_insert (aud_playlist_get_active (), drop_position, + audgui_urilist_insert (Playlist::active_playlist (), drop_position, (const char *) gtk_selection_data_get_data (data)); drop_position = -1; } @@ -494,7 +494,7 @@ static void update_cb (void *, void *) if (song_changed) { - playlistwin_list->set_focused (aud_playlist_get_position (aud_playlist_get_active ())); + playlistwin_list->set_focused (Playlist::active_playlist ().get_position ()); song_changed = false; } @@ -504,14 +504,14 @@ static void update_cb (void *, void *) static void follow_cb (void * data, void *) { - int list = aud::from_ptr<int> (data); - aud_playlist_select_all (list, false); + auto list = aud::from_ptr<Playlist> (data); + list.select_all (false); - int row = aud_playlist_get_position (list); + int row = list.get_position (); if (row >= 0) - aud_playlist_entry_set_selected (list, row, true); + list.select_entry (row, true); - if (list == aud_playlist_get_active ()) + if (list == Playlist::active_playlist ()) song_changed = true; } diff --git a/src/skins-qt/plugin-window.cc b/src/skins-qt/plugin-window.cc index 8b19b65..97d58a9 100644 --- a/src/skins-qt/plugin-window.cc +++ b/src/skins-qt/plugin-window.cc @@ -23,7 +23,7 @@ #include "plugin-window.h" -#include <QVBoxLayout> +#include <QBoxLayout> #include <QWidget> #include <QWindow> @@ -33,6 +33,7 @@ #include <libaudcore/plugins.h> #include <libaudcore/hook.h> #include <libaudcore/runtime.h> +#include <libaudqt/libaudqt.h> #include "main.h" #include "window.h" @@ -57,10 +58,9 @@ public: resize (pos[2], pos[3]); } else - resize (320, 240); + resize (3 * audqt::sizes.OneInch, 2 * audqt::sizes.OneInch); - auto vbox = new QVBoxLayout (this); - vbox->setContentsMargins (2, 2, 2, 2); + auto vbox = audqt::make_vbox (this); vbox->addWidget (widget); } diff --git a/src/skins-qt/plugin.cc b/src/skins-qt/plugin.cc index d943164..db42e70 100644 --- a/src/skins-qt/plugin.cc +++ b/src/skins-qt/plugin.cc @@ -44,9 +44,6 @@ #include "window.h" #include "view.h" -#include "../ui-common/menu-ops.cc" -#include "../ui-common/menu-ops-qt.cc" - class QtSkins : public audqt::QtIfacePlugin { public: @@ -160,8 +157,6 @@ static void skins_cleanup_main () equalizerwin_unhook (); playlistwin_unhook (); - menu_cleanup (); - timer_remove (TimerRate::Hz4, (TimerFunc) mainwin_update_song_info); delete mainwin; mainwin = nullptr; diff --git a/src/skins/Makefile b/src/skins/Makefile index 1ef397f..2514c81 100644 --- a/src/skins/Makefile +++ b/src/skins/Makefile @@ -9,6 +9,7 @@ SRCS = actions.cc \ equalizer.cc \ hslider.cc \ main.cc \ + menu-ops.cc \ menurow.cc \ menus.cc \ monostereo.cc \ diff --git a/src/skins/actions.cc b/src/skins/actions.cc index 6b2a60f..b679201 100644 --- a/src/skins/actions.cc +++ b/src/skins/actions.cc @@ -38,7 +38,7 @@ #include <libaudcore/plugins.h> #include <libaudgui/libaudgui.h> -#define ACTIVE (aud_playlist_get_active ()) +#define ACTIVE (Playlist::active_playlist ()) void action_ab_clear () { diff --git a/src/skins/main.cc b/src/skins/main.cc index 704992a..8a93e16 100644 --- a/src/skins/main.cc +++ b/src/skins/main.cc @@ -134,6 +134,7 @@ static void format_time (char buf[7], int time, int length) if (remaining && length > 0) { time = (length - time) / 1000; + time = aud::clamp(0, time, 359999); // 99:59:59 if (time < 60) snprintf (buf, 7, zero ? "-00:%02d" : " -0:%02d", time); @@ -145,6 +146,7 @@ static void format_time (char buf[7], int time, int length) else { time /= 1000; + time = aud::clamp(0, time, 3599999); // 999:59:59 if (time < 6000) snprintf (buf, 7, zero ? " %02d:%02d" : " %2d:%02d", time / 60, time % 60); @@ -436,6 +438,14 @@ static void mainwin_playback_stop () playlistwin_hide_timer(); } +static void record_toggled () +{ + if (aud_drct_get_record_enabled ()) + mainwin_show_status_message (_("Recording on")); + else + mainwin_show_status_message (_("Recording off")); +} + static void repeat_toggled () { mainwin_repeat->set_active (aud_get_bool (nullptr, "repeat")); @@ -1124,6 +1134,7 @@ static void mainwin_create_window () hook_associate ("playback unpause", (HookFunction) playback_unpause, nullptr); hook_associate ("title change", (HookFunction) title_change, nullptr); hook_associate ("info change", (HookFunction) info_change, nullptr); + hook_associate ("enable record", (HookFunction) record_toggled, nullptr); hook_associate ("set repeat", (HookFunction) repeat_toggled, nullptr); hook_associate ("set shuffle", (HookFunction) shuffle_toggled, nullptr); hook_associate ("set no_playlist_advance", (HookFunction) no_advance_toggled, nullptr); @@ -1146,6 +1157,7 @@ void mainwin_unhook () hook_dissociate ("playback unpause", (HookFunction) playback_unpause); hook_dissociate ("title change", (HookFunction) title_change); hook_dissociate ("info change", (HookFunction) info_change); + hook_dissociate ("enable record", (HookFunction) record_toggled); hook_dissociate ("set repeat", (HookFunction) repeat_toggled); hook_dissociate ("set shuffle", (HookFunction) shuffle_toggled); hook_dissociate ("set no_playlist_advance", (HookFunction) no_advance_toggled); diff --git a/src/skins/menu-ops.cc b/src/skins/menu-ops.cc new file mode 100644 index 0000000..358ec47 --- /dev/null +++ b/src/skins/menu-ops.cc @@ -0,0 +1,2 @@ +#include "../ui-common/menu-ops.cc" +#include "../ui-common/menu-ops-gtk.cc" diff --git a/src/skins/menus.cc b/src/skins/menus.cc index e50e0d3..20e5bd5 100644 --- a/src/skins/menus.cc +++ b/src/skins/menus.cc @@ -24,7 +24,6 @@ #include <gdk/gdkkeysyms.h> #include <libaudcore/drct.h> -#include <libaudcore/hook.h> #include <libaudcore/i18n.h> #include <libaudcore/interface.h> #include <libaudcore/playlist.h> @@ -71,37 +70,13 @@ static void configure_visualizations () { audgui_show_prefs_for_plugin_type (Plu static void skins_volume_up () { mainwin_set_volume_diff (5); } static void skins_volume_down () { mainwin_set_volume_diff (-5); } -/* emulate a config item for the recording toggle */ -static void toggle_record () -{ - bool enable = aud_get_bool ("skins", "record"); - - if (aud_drct_enable_record (enable)) - mainwin_show_status_message (enable ? _("Recording on") : _("Recording off")); - else - { - aud_set_bool ("skins", "record", aud_drct_get_record_enabled ()); - hook_call ("skins set record", nullptr); - } -} - -static void record_toggled (void * = nullptr, void * = nullptr) -{ - bool enabled = aud_drct_get_record_enabled (); - if (enabled != aud_get_bool ("skins", "record")) - { - aud_set_bool ("skins", "record", enabled); - hook_call ("skins set record", nullptr); - } -} - static const AudguiMenuItem output_items[] = { MenuCommand (N_("Volume Up"), "audio-volume-high", '+', NO_MOD, skins_volume_up), MenuCommand (N_("Volume Down"), "audio-volume-low", '-', NO_MOD, skins_volume_down), MenuSep (), MenuCommand (N_("Effects ..."), nullptr, NO_KEY, configure_effects), MenuSep (), - MenuToggle (N_("Record Stream"), nullptr, 'd', NO_MOD, "skins", "record", toggle_record, "skins set record"), + MenuToggle (N_("Record Stream"), nullptr, 'd', NO_MOD, nullptr, "record", nullptr, "set record"), MenuCommand (N_("Audio Settings ..."), "audio-card", NO_KEY, configure_output) }; @@ -147,7 +122,7 @@ static const AudguiMenuItem playback_items[] = { static const AudguiMenuItem playlist_items[] = { MenuCommand (N_("Play/Resume"), "media-playback-start", GDK_KEY_Return, SHIFT, pl_play), MenuSep (), - MenuCommand (N_("New Playlist"), "document-new", 'n', SHIFT, (GCallback) aud_playlist_new), + MenuCommand (N_("New Playlist"), "document-new", 'n', SHIFT, pl_new), MenuCommand (N_("Rename Playlist ..."), "insert-text", GDK_KEY_F2, NO_MOD, action_playlist_rename), MenuCommand (N_("Remove Playlist"), "edit-delete", 'd', SHIFT, action_playlist_delete), MenuSep (), @@ -225,7 +200,8 @@ static const AudguiMenuItem sort_items[] = { MenuCommand (N_("By Length"), nullptr, NO_KEY, sort_length), MenuCommand (N_("By File Name"), nullptr, NO_KEY, sort_filename), MenuCommand (N_("By File Path"), nullptr, NO_KEY, sort_path), - MenuCommand (N_("By Custom Title"), nullptr, NO_KEY, sort_custom_title) + MenuCommand (N_("By Custom Title"), nullptr, NO_KEY, sort_custom_title), + MenuCommand (N_("By Comment"), nullptr, NO_KEY, sort_comment) }; static const AudguiMenuItem sort_selected_items[] = { @@ -239,7 +215,8 @@ static const AudguiMenuItem sort_selected_items[] = { MenuCommand (N_("By Length"), nullptr, NO_KEY, sort_sel_length), MenuCommand (N_("By File Name"), nullptr, NO_KEY, sort_sel_filename), MenuCommand (N_("By File Path"), nullptr, NO_KEY, sort_sel_path), - MenuCommand (N_("By Custom Title"), nullptr, NO_KEY, sort_sel_custom_title) + MenuCommand (N_("By Custom Title"), nullptr, NO_KEY, sort_sel_custom_title), + MenuCommand (N_("By Comment"), nullptr, NO_KEY, sort_comment) }; static const AudguiMenuItem playlist_sort_items[] = { @@ -278,9 +255,6 @@ void menu_init () {playlist_context_items} }; - record_toggled (); - hook_associate ("enable record", record_toggled, nullptr); - accel = gtk_accel_group_new (); for (int i = UI_MENUS; i --; ) @@ -301,8 +275,6 @@ void menu_cleanup () g_object_unref (accel); accel = nullptr; - - hook_dissociate ("enable record", record_toggled); } GtkAccelGroup * menu_get_accel_group () diff --git a/src/skins/playlist-slider.cc b/src/skins/playlist-slider.cc index a63fd40..83b14ee 100644 --- a/src/skins/playlist-slider.cc +++ b/src/skins/playlist-slider.cc @@ -103,7 +103,7 @@ bool PlaylistSlider::motion (GdkEventMotion * event) PlaylistSlider::PlaylistSlider (PlaylistWidget * list, int height) : m_list (list), m_height (height), - m_length (aud_playlist_entry_count (aud_playlist_get_active ())) + m_length (Playlist::active_playlist ().n_entries ()) { set_scale (config.scale); add_input (8, height, true, true); @@ -118,6 +118,6 @@ void PlaylistSlider::resize (int height) void PlaylistSlider::refresh () { - m_length = aud_playlist_entry_count (aud_playlist_get_active ()); + m_length = Playlist::active_playlist ().n_entries (); queue_draw (); } diff --git a/src/skins/playlist-widget.cc b/src/skins/playlist-widget.cc index 96edb82..85063b6 100644 --- a/src/skins/playlist-widget.cc +++ b/src/skins/playlist-widget.cc @@ -49,11 +49,11 @@ enum { void PlaylistWidget::update_title () { - if (aud_playlist_count () > 1) + if (Playlist::n_playlists () > 1) { - String title = aud_playlist_get_title (m_playlist); + String title = m_playlist.get_title (); m_title_text = String (str_printf (_("%s (%d of %d)"), - (const char *) title, 1 + m_playlist, aud_playlist_count ())); + (const char *) title, 1 + m_playlist.index (), Playlist::n_playlists ())); } else m_title_text = String (); @@ -96,7 +96,7 @@ int PlaylistWidget::adjust_position (bool relative, int position) const if (relative) { - int focus = aud_playlist_get_focus (m_playlist); + int focus = m_playlist.get_focus (); if (focus == -1) return 0; @@ -132,7 +132,7 @@ void PlaylistWidget::cancel_all () void PlaylistWidget::draw (cairo_t * cr) { - int active_entry = aud_playlist_get_position (m_playlist); + int active_entry = m_playlist.get_position (); int left = 3, right = 3; PangoLayout * layout; int width; @@ -162,7 +162,7 @@ void PlaylistWidget::draw (cairo_t * cr) for (int i = m_first; i < m_first + m_rows && i < m_length; i ++) { - if (! aud_playlist_entry_get_selected (m_playlist, i)) + if (! m_playlist.entry_selected (i)) continue; cairo_rectangle (cr, 0, m_offset + m_row_height * (i - m_first), m_width, m_row_height); @@ -204,7 +204,7 @@ void PlaylistWidget::draw (cairo_t * cr) for (int i = m_first; i < m_first + m_rows && i < m_length; i ++) { - Tuple tuple = aud_playlist_entry_get_tuple (m_playlist, i, Playlist::NoWait); + Tuple tuple = m_playlist.entry_tuple (i, Playlist::NoWait); int len = tuple.get_int (Tuple::Length); if (len < 0) continue; @@ -227,13 +227,13 @@ void PlaylistWidget::draw (cairo_t * cr) /* queue positions */ - if (aud_playlist_queue_count (m_playlist)) + if (m_playlist.n_queued ()) { width = 0; for (int i = m_first; i < m_first + m_rows && i < m_length; i ++) { - int pos = aud_playlist_queue_find_entry (m_playlist, i); + int pos = m_playlist.queue_find_entry (i); if (pos < 0) continue; @@ -262,7 +262,7 @@ void PlaylistWidget::draw (cairo_t * cr) for (int i = m_first; i < m_first + m_rows && i < m_length; i ++) { - Tuple tuple = aud_playlist_entry_get_tuple (m_playlist, i, Playlist::NoWait); + Tuple tuple = m_playlist.entry_tuple (i, Playlist::NoWait); String title = tuple.get_str (Tuple::FormattedTitle); layout = gtk_widget_create_pango_layout (gtk_dr (), title); @@ -279,12 +279,11 @@ void PlaylistWidget::draw (cairo_t * cr) /* focus rectangle */ - int focus = aud_playlist_get_focus (m_playlist); + int focus = m_playlist.get_focus (); /* don't show rectangle if this is the only selected entry */ if (focus >= m_first && focus <= m_first + m_rows - 1 && - (! aud_playlist_entry_get_selected (m_playlist, focus) || - aud_playlist_selected_count (m_playlist) > 1)) + (! m_playlist.entry_selected (focus) || m_playlist.n_selected () > 1)) { cairo_new_path (cr); cairo_set_line_width (cr, 1); @@ -343,18 +342,18 @@ void PlaylistWidget::set_font (const char * font) void PlaylistWidget::refresh () { - m_playlist = aud_playlist_get_active (); - m_length = aud_playlist_entry_count (m_playlist); + auto prev_playlist = m_playlist; + m_playlist = Playlist::active_playlist (); + m_length = m_playlist.n_entries (); + update_title (); calc_layout (); - int id = aud_playlist_get_unique_id (m_playlist); - if (m_playlist_id != id) + if (m_playlist != prev_playlist) { cancel_all (); - m_playlist_id = id; m_first = 0; - ensure_visible (aud_playlist_get_focus (m_playlist)); + ensure_visible (m_playlist.get_focus ()); } queue_draw (); @@ -378,9 +377,9 @@ void PlaylistWidget::select_single (bool relative, int position) if (position == -1) return; - aud_playlist_select_all (m_playlist, false); - aud_playlist_entry_set_selected (m_playlist, position, true); - aud_playlist_set_focus (m_playlist, position); + m_playlist.select_all (false); + m_playlist.select_entry (position, true); + m_playlist.set_focus (position); ensure_visible (position); } @@ -395,11 +394,10 @@ void PlaylistWidget::select_extend (bool relative, int position) int sign = (position > count) ? 1 : -1; for (; count != position; count += sign) - aud_playlist_entry_set_selected (m_playlist, count, - ! aud_playlist_entry_get_selected (m_playlist, count + sign)); + m_playlist.select_entry (count, ! m_playlist.entry_selected (count + sign)); - aud_playlist_entry_set_selected (m_playlist, position, true); - aud_playlist_set_focus (m_playlist, position); + m_playlist.select_entry (position, true); + m_playlist.set_focus (position); ensure_visible (position); } @@ -410,7 +408,7 @@ void PlaylistWidget::select_slide (bool relative, int position) if (position == -1) return; - aud_playlist_set_focus (m_playlist, position); + m_playlist.set_focus (position); ensure_visible (position); } @@ -421,34 +419,33 @@ void PlaylistWidget::select_toggle (bool relative, int position) if (position == -1) return; - aud_playlist_entry_set_selected (m_playlist, position, - ! aud_playlist_entry_get_selected (m_playlist, position)); - aud_playlist_set_focus (m_playlist, position); + m_playlist.select_entry (position, ! m_playlist.entry_selected (position)); + m_playlist.set_focus (position); ensure_visible (position); } void PlaylistWidget::select_move (bool relative, int position) { - int focus = aud_playlist_get_focus (m_playlist); + int focus = m_playlist.get_focus (); position = adjust_position (relative, position); if (focus == -1 || position == -1 || position == focus) return; - focus += aud_playlist_shift (m_playlist, focus, position - focus); + focus += m_playlist.shift_entries (focus, position - focus); ensure_visible (focus); } void PlaylistWidget::delete_selected () { - aud_playlist_delete_selected (m_playlist); + m_playlist.remove_selected (); - m_length = aud_playlist_entry_count (m_playlist); - int focus = aud_playlist_get_focus (m_playlist); + m_length = m_playlist.n_entries (); + int focus = m_playlist.get_focus (); if (focus != -1) { - aud_playlist_entry_set_selected (m_playlist, focus, true); + m_playlist.select_entry (focus, true); ensure_visible (focus); } } @@ -482,11 +479,11 @@ bool PlaylistWidget::handle_keypress (GdkEventKey * event) break; case GDK_KEY_Return: select_single (true, 0); - aud_playlist_set_position (m_playlist, aud_playlist_get_focus (m_playlist)); - aud_playlist_play (m_playlist); + m_playlist.set_position (m_playlist.get_focus ()); + m_playlist.start_playback (); break; case GDK_KEY_Escape: - select_single (false, aud_playlist_get_position (m_playlist)); + select_single (false, m_playlist.get_position ()); break; case GDK_KEY_Delete: delete_selected (); @@ -597,7 +594,7 @@ void PlaylistWidget::scroll_to (int row) void PlaylistWidget::set_focused (int row) { cancel_all (); - aud_playlist_set_focus (m_playlist, row); + m_playlist.set_focus (row); ensure_visible (row); refresh (); } @@ -652,7 +649,7 @@ bool PlaylistWidget::button_press (GdkEventButton * event) switch (state) { case 0: - if (aud_playlist_entry_get_selected (m_playlist, position)) + if (m_playlist.entry_selected (position)) select_slide (false, position); else select_single (false, position); @@ -678,7 +675,7 @@ bool PlaylistWidget::button_press (GdkEventButton * event) if (position != -1 && position != m_length) { - if (aud_playlist_entry_get_selected (m_playlist, position)) + if (m_playlist.entry_selected (position)) select_slide (false, position); else select_single (false, position); @@ -698,9 +695,9 @@ bool PlaylistWidget::button_press (GdkEventButton * event) return true; if (position != -1) - aud_playlist_set_position (m_playlist, position); + m_playlist.set_position (position); - aud_playlist_play (m_playlist); + m_playlist.start_playback (); break; default: return true; @@ -795,13 +792,14 @@ void PlaylistWidget::popup_trigger (int pos) { audgui_infopopup_hide (); - auto show_cb = [] (void * me_) { - auto me = (PlaylistWidget *) me_; - audgui_infopopup_show (me->m_playlist, me->m_popup_pos); - }; - m_popup_pos = pos; - m_popup_timer.queue (aud_get_int (nullptr, "filepopup_delay") * 100, show_cb, this); + m_popup_timer.queue (aud_get_int (nullptr, "filepopup_delay") * 100, + aud::obj_member<PlaylistWidget, & PlaylistWidget::popup_show>, this); +} + +void PlaylistWidget::popup_show () +{ + audgui_infopopup_show (m_playlist, m_popup_pos); } void PlaylistWidget::popup_hide () diff --git a/src/skins/playlist-widget.h b/src/skins/playlist-widget.h index deb5f3a..cf7c33f 100644 --- a/src/skins/playlist-widget.h +++ b/src/skins/playlist-widget.h @@ -30,7 +30,7 @@ #include <libaudcore/hook.h> #include <libaudcore/mainloop.h> -#include <libaudcore/objects.h> +#include <libaudcore/playlist.h> #include "widget.h" @@ -79,6 +79,7 @@ private: void cancel_all (); void scroll_timeout (); void popup_trigger (int pos); + void popup_show (); void popup_hide (); const Timer<PlaylistWidget> @@ -88,7 +89,8 @@ private: PangoFontDescPtr m_font; String m_title_text; - int m_playlist = -1, m_playlist_id = -1, m_length = 0; + Playlist m_playlist; + int m_length = 0; int m_width = 0, m_height = 0, m_row_height = 1, m_offset = 0, m_rows = 0, m_first = 0; int m_scroll = 0, m_hover = -1, m_drag = 0, m_popup_pos = -1; QueuedFunc m_popup_timer; diff --git a/src/skins/playlist.cc b/src/skins/playlist.cc index af95724..2e10e43 100644 --- a/src/skins/playlist.cc +++ b/src/skins/playlist.cc @@ -98,17 +98,17 @@ static bool song_changed; static void update_info () { - int playlist = aud_playlist_get_active (); - StringBuf s1 = str_format_time (aud_playlist_get_selected_length (playlist)); - StringBuf s2 = str_format_time (aud_playlist_get_total_length (playlist)); + auto playlist = Playlist::active_playlist (); + StringBuf s1 = str_format_time (playlist.selected_length_ms ()); + StringBuf s2 = str_format_time (playlist.total_length_ms ()); playlistwin_info->set_text (str_concat ({s1, "/", s2})); } static void update_rollup_text () { - int playlist = aud_playlist_get_active (); - int entry = aud_playlist_get_position (playlist); - Tuple tuple = aud_playlist_entry_get_tuple (playlist, entry, Playlist::NoWait); + auto playlist = Playlist::active_playlist (); + int entry = playlist.get_position (); + Tuple tuple = playlist.entry_tuple (entry, Playlist::NoWait); char scratch[512]; scratch[0] = 0; @@ -292,7 +292,7 @@ static void drag_drop (GtkWidget * widget, GdkDragContext * context, int x, static void drag_data_received (GtkWidget * widget, GdkDragContext * context, int x, int y, GtkSelectionData * data, unsigned info, unsigned time, void * unused) { - audgui_urilist_insert (aud_playlist_get_active (), drop_position, + audgui_urilist_insert (Playlist::active_playlist (), drop_position, (const char *) gtk_selection_data_get_data (data)); drop_position = -1; } @@ -507,7 +507,7 @@ static void update_cb (void *, void *) if (song_changed) { - playlistwin_list->set_focused (aud_playlist_get_position (aud_playlist_get_active ())); + playlistwin_list->set_focused (Playlist::active_playlist ().get_position ()); song_changed = false; } @@ -517,14 +517,14 @@ static void update_cb (void *, void *) static void follow_cb (void * data, void *) { - int list = aud::from_ptr<int> (data); - aud_playlist_select_all (list, false); + auto list = aud::from_ptr<Playlist> (data); + list.select_all (false); - int row = aud_playlist_get_position (list); + int row = list.get_position (); if (row >= 0) - aud_playlist_entry_set_selected (list, row, true); + list.select_entry (row, true); - if (list == aud_playlist_get_active ()) + if (list == Playlist::active_playlist ()) song_changed = true; } diff --git a/src/skins/plugin.cc b/src/skins/plugin.cc index bab7ed6..253e79a 100644 --- a/src/skins/plugin.cc +++ b/src/skins/plugin.cc @@ -42,9 +42,6 @@ #include "window.h" #include "view.h" -#include "../ui-common/menu-ops.cc" -#include "../ui-common/menu-ops-gtk.cc" - class SkinnedUI : public IfacePlugin { public: diff --git a/src/skins/search-select.cc b/src/skins/search-select.cc index 2267473..1e0888d 100644 --- a/src/skins/search-select.cc +++ b/src/skins/search-select.cc @@ -49,23 +49,23 @@ static gboolean search_kp_cb (GtkWidget * entry, GdkEventKey * event, GtkWidget return true; } -static void copy_selected_to_new (int playlist) +static void copy_selected_to_new (Playlist playlist) { - int entries = aud_playlist_entry_count (playlist); + int entries = playlist.n_entries (); Index<PlaylistAddItem> items; for (int entry = 0; entry < entries; entry ++) { - if (aud_playlist_entry_get_selected (playlist, entry)) + if (playlist.entry_selected (entry)) { items.append - (aud_playlist_entry_get_filename (playlist, entry), - aud_playlist_entry_get_tuple (playlist, entry, Playlist::NoWait)); + (playlist.entry_filename (entry), + playlist.entry_tuple (entry, Playlist::NoWait)); } } - int new_list = aud_playlist_new (); - aud_playlist_entry_insert_batch (new_list, 0, std::move (items), false); + auto new_list = Playlist::new_playlist (); + new_list.insert_items (0, std::move (items), false); } void action_playlist_search_and_select () @@ -153,7 +153,7 @@ void action_playlist_search_and_select () /* create a TitleInput tuple with user search data */ Tuple tuple; const char * searchdata = nullptr; - int active_playlist = aud_playlist_get_active (); + auto playlist = Playlist::active_playlist (); searchdata = gtk_entry_get_text ((GtkEntry *) entry_title); AUDDBG ("title=\"%s\"\n", searchdata); @@ -173,29 +173,29 @@ void action_playlist_search_and_select () /* check if previous selection should be cleared before searching */ if (gtk_toggle_button_get_active ((GtkToggleButton *) checkbt_clearprevsel)) - aud_playlist_select_all (active_playlist, false); + playlist.select_all (false); - aud_playlist_select_by_patterns (active_playlist, tuple); + playlist.select_by_patterns (tuple); /* check if a new playlist should be created after searching */ if (gtk_toggle_button_get_active ((GtkToggleButton *) checkbt_newplaylist)) - copy_selected_to_new (active_playlist); + copy_selected_to_new (playlist); else { /* set focus on the first entry found */ - int entries = aud_playlist_entry_count (active_playlist); - for (int count = 0; count < entries; count ++) + int entries = playlist.n_entries (); + for (int i = 0; i < entries; i ++) { - if (aud_playlist_entry_get_selected (active_playlist, count)) + if (playlist.entry_selected (i)) { - playlistwin_list->set_focused (count); + playlistwin_list->set_focused (i); break; } } /* check if matched entries should be queued */ if (gtk_toggle_button_get_active ((GtkToggleButton *) checkbt_autoenqueue)) - aud_playlist_queue_insert_selected (active_playlist, -1); + playlist.queue_insert_selected (-1); } } diff --git a/src/skins/skins_cfg.cc b/src/skins/skins_cfg.cc index 9c4fb97..006b303 100644 --- a/src/skins/skins_cfg.cc +++ b/src/skins/skins_cfg.cc @@ -47,7 +47,6 @@ static const char * const skins_defaults[] = { "mainwin_font", "Sans Bold 9", "mainwin_use_bitmapfont", "TRUE", "playlist_font", "Sans Bold 8", - "record", "FALSE", "show_remaining_time", "FALSE", "twoway_scroll", "FALSE", diff --git a/src/skins/skinselector.cc b/src/skins/skinselector.cc index 7d090c3..4ecffbb 100644 --- a/src/skins/skinselector.cc +++ b/src/skins/skinselector.cc @@ -50,23 +50,23 @@ static Index<SkinNode> skinlist; static void skin_view_on_cursor_changed (GtkTreeView * treeview); -static GdkPixbuf * skin_get_preview (const char * path) +static AudguiPixbuf skin_get_preview (const char * path) { - GdkPixbuf * preview = nullptr; + AudguiPixbuf preview; StringBuf archive_path; if (file_is_archive (path)) { archive_path.steal (archive_decompress (path)); if (! archive_path) - return nullptr; + return preview; path = archive_path; } StringBuf preview_path = skin_pixmap_locate (path, "main"); if (preview_path) - preview = gdk_pixbuf_new_from_file (preview_path, nullptr); + preview.capture (gdk_pixbuf_new_from_file (preview_path, nullptr)); if (archive_path) del_directory (archive_path); @@ -74,16 +74,16 @@ static GdkPixbuf * skin_get_preview (const char * path) return preview; } -static GdkPixbuf * skin_get_thumbnail (const char * path) +static AudguiPixbuf skin_get_thumbnail (const char * path) { StringBuf base = filename_get_base (path); base.insert (-1, ".png"); StringBuf thumbname = filename_build ({skins_get_skin_thumb_dir (), base}); - GdkPixbuf * thumb = nullptr; + AudguiPixbuf thumb; if (g_file_test (thumbname, G_FILE_TEST_EXISTS)) - thumb = gdk_pixbuf_new_from_file (thumbname, nullptr); + thumb.capture (gdk_pixbuf_new_from_file (thumbname, nullptr)); if (! thumb) { @@ -92,12 +92,12 @@ static GdkPixbuf * skin_get_thumbnail (const char * path) if (thumb) { make_directory (skins_get_skin_thumb_dir ()); - gdk_pixbuf_save (thumb, thumbname, "png", nullptr, nullptr); + gdk_pixbuf_save (thumb.get (), thumbname, "png", nullptr, nullptr); } } if (thumb) - audgui_pixbuf_scale_within (& thumb, audgui_get_dpi () * 3 / 2); + audgui_pixbuf_scale_within (thumb, audgui_get_dpi () * 3 / 2); return thumb; } @@ -151,20 +151,17 @@ void skin_view_update (GtkTreeView * treeview) for (const SkinNode & node : skinlist) { - GdkPixbuf * thumbnail = skin_get_thumbnail (node.path); + AudguiPixbuf thumbnail = skin_get_thumbnail (node.path); StringBuf formattedname = str_concat ({"<big><b>", node.name, "</b></big>\n<i>", node.desc, "</i>"}); GtkTreeIter iter; gtk_list_store_append (store, & iter); gtk_list_store_set (store, & iter, - SKIN_VIEW_COL_PREVIEW, thumbnail, + SKIN_VIEW_COL_PREVIEW, thumbnail.get (), SKIN_VIEW_COL_FORMATTEDNAME, (const char *) formattedname, SKIN_VIEW_COL_NAME, (const char *) node.name, -1); - if (thumbnail) - g_object_unref (thumbnail); - if (! current_skin && strstr (current_path, node.name)) current_skin = gtk_tree_model_get_path ((GtkTreeModel *) store, & iter); } diff --git a/src/skins/surface.cc b/src/skins/surface.cc index 5847c61..0ca9e44 100644 --- a/src/skins/surface.cc +++ b/src/skins/surface.cc @@ -24,6 +24,7 @@ #include "surface.h" #include <libaudcore/runtime.h> +#include <libaudgui/libaudgui-gtk.h> cairo_surface_t * surface_new (int w, int h) { @@ -33,7 +34,7 @@ cairo_surface_t * surface_new (int w, int h) cairo_surface_t * surface_new_from_file (const char * name) { GError * error = nullptr; - GdkPixbuf * p = gdk_pixbuf_new_from_file (name, & error); + AudguiPixbuf p (gdk_pixbuf_new_from_file (name, & error)); if (error) { @@ -44,16 +45,13 @@ cairo_surface_t * surface_new_from_file (const char * name) if (! p) return nullptr; - cairo_surface_t * surface = surface_new (gdk_pixbuf_get_width (p), - gdk_pixbuf_get_height (p)); + cairo_surface_t * surface = surface_new (p.width (), p.height ()); cairo_t * cr = cairo_create (surface); - gdk_cairo_set_source_pixbuf (cr, p, 0, 0); + gdk_cairo_set_source_pixbuf (cr, p.get (), 0, 0); cairo_paint (cr); cairo_destroy (cr); - g_object_unref (p); - return surface; } diff --git a/src/song-info-qt/song-info.cc b/src/song-info-qt/song-info.cc index 8d012d6..20262f0 100644 --- a/src/song-info-qt/song-info.cc +++ b/src/song-info-qt/song-info.cc @@ -54,26 +54,26 @@ void SongInfo::update (void * unused, audqt::InfoWidget * widget) if (! widget) return; - int playlist = aud_playlist_get_playing (); + auto playlist = Playlist::playing_playlist (); - if (playlist == -1) - playlist = aud_playlist_get_active (); + if (playlist == Playlist ()) + playlist = Playlist::active_playlist (); - int position = aud_playlist_get_position (playlist); + int position = playlist.get_position (); if (position == -1) return; - String filename = aud_playlist_entry_get_filename (playlist, position); + String filename = playlist.entry_filename (position); if (! filename) return; - PluginHandle * decoder = aud_playlist_entry_get_decoder (playlist, position); + PluginHandle * decoder = playlist.entry_decoder (position); if (! decoder) return; - Tuple tuple = aud_playlist_entry_get_tuple (playlist, position); + Tuple tuple = playlist.entry_tuple (position); if (tuple.valid ()) - widget->fillInfo (playlist, position, filename, tuple, decoder, + widget->fillInfo (filename, tuple, decoder, aud_file_can_write_tuple (filename, decoder)); } diff --git a/src/speedpitch/speed-pitch.cc b/src/speedpitch/speed-pitch.cc index 81a42fc..e5bc6c0 100644 --- a/src/speedpitch/speed-pitch.cc +++ b/src/speedpitch/speed-pitch.cc @@ -156,6 +156,12 @@ Index<float> & SpeedPitch::process (Index<float> & data, bool ending) /* Copy the passed audio to the input buffer, scaled to adjust pitch. */ add_data (in, data, 1.0 / pitch); + if (! aud_get_bool (CFGSECT, "decouple")) + { + data = std::move (in); + return data; + } + /* Calculate the spacing interval for input. */ int instep = (int) round ((outstep / curchans) * speed / pitch) * curchans; @@ -197,6 +203,9 @@ Index<float> & SpeedPitch::process (Index<float> & data, bool ending) int SpeedPitch::adjust_delay (int delay) { + if (! aud_get_bool (CFGSECT, "decouple")) + return delay; + float samples_to_ms = 1000.0 / (curchans * currate); float speed = aud_get_double (CFGSECT, "speed"); int in_samples = in.len () - src; @@ -205,34 +214,50 @@ int SpeedPitch::adjust_delay (int delay) return (delay + in_samples * samples_to_ms) * speed + out_samples * samples_to_ms; } +static void sync_speed () +{ + if (! aud_get_bool (CFGSECT, "decouple")) + { + aud_set_double (CFGSECT, "speed", aud_get_double (CFGSECT, "pitch")); + hook_call ("speed-pitch set speed", nullptr); + } +} + static void pitch_changed () { semitones = 12 * log (aud_get_double (CFGSECT, "pitch")) / log (2); hook_call ("speed-pitch set semitones", nullptr); + sync_speed (); } static void semitones_changed () { aud_set_double (CFGSECT, "pitch", pow (2, semitones / 12)); hook_call ("speed-pitch set pitch", nullptr); + sync_speed (); } const char * const SpeedPitch::defaults[] = { + "decouple", "TRUE", "speed", "1", "pitch", "1", nullptr}; const PreferencesWidget SpeedPitch::widgets[] = { - WidgetLabel (N_("<b>Speed and Pitch</b>")), - WidgetSpin (N_("Speed:"), - WidgetFloat (CFGSECT, "speed"), - {MINSPEED, MAXSPEED, 0.05}), - WidgetSpin (N_("Pitch:"), - WidgetFloat (CFGSECT, "pitch", pitch_changed, "speed-pitch set pitch"), - {MINPITCH, MAXPITCH, 0.005}), + WidgetLabel (N_("<b>Speed</b>")), + WidgetCheck (N_("Decouple from pitch"), + WidgetBool (CFGSECT, "decouple", sync_speed)), + WidgetSpin (N_("Multiplier:"), + WidgetFloat (CFGSECT, "speed", nullptr, "speed-pitch set speed"), + {MINSPEED, MAXSPEED, 0.05}, + WIDGET_CHILD), + WidgetLabel (N_("<b>Pitch</b>")), WidgetSpin (nullptr, WidgetFloat (semitones, semitones_changed, "speed-pitch set semitones"), - {MINSEMITONES, MAXSEMITONES, 0.05, N_("semitones")}, + {MINSEMITONES, MAXSEMITONES, 0.05, N_("semitones")}), + WidgetSpin (N_("Multiplier:"), + WidgetFloat (CFGSECT, "pitch", pitch_changed, "speed-pitch set pitch"), + {MINPITCH, MAXPITCH, 0.005}, WIDGET_CHILD) }; diff --git a/src/statusicon-qt/statusicon.cc b/src/statusicon-qt/statusicon.cc index b0c9244..2643c4e 100644 --- a/src/statusicon-qt/statusicon.cc +++ b/src/statusicon-qt/statusicon.cc @@ -56,7 +56,6 @@ public: static void activate (QSystemTrayIcon::ActivationReason); static void open_files (); static void toggle_aud_ui (); - static void update_menu (); }; EXPORT StatusIcon aud_plugin_instance; @@ -82,16 +81,13 @@ const PluginPreferences StatusIcon::prefs = {{widgets}}; const audqt::MenuItem StatusIcon::items[] = { - audqt::MenuCommand ({N_("_Hide"), "window-close"}, StatusIcon::toggle_aud_ui), - audqt::MenuCommand ({N_("_Restore"), "window-new"}, StatusIcon::toggle_aud_ui), - audqt::MenuSep (), - audqt::MenuCommand ({N_("_Open Files ..."), "document-open"}, StatusIcon::open_files), - audqt::MenuCommand ({N_("Pre_vious"), "media-skip-backward"}, aud_drct_pl_prev), audqt::MenuCommand ({N_("_Play"), "media-playback-start"}, aud_drct_play), audqt::MenuCommand ({N_("Paus_e"), "media-playback-pause"}, aud_drct_pause), audqt::MenuCommand ({N_("_Stop"), "media-playback-stop"}, aud_drct_stop), + audqt::MenuCommand ({N_("Pre_vious"), "media-skip-backward"}, aud_drct_pl_prev), audqt::MenuCommand ({N_("_Next"), "media-skip-forward"}, aud_drct_pl_next), audqt::MenuSep (), + audqt::MenuCommand ({N_("_Open Files ..."), "document-open"}, StatusIcon::open_files), audqt::MenuCommand ({N_("Se_ttings ..."), "preferences-system"}, audqt::prefswin_show), audqt::MenuCommand ({N_("_Quit"), "application-exit"}, aud_quit), }; @@ -109,7 +105,6 @@ bool StatusIcon::init () QObject::connect (tray, & QSystemTrayIcon::activated, activate); menu = audqt::menu_build (items); tray->setContextMenu (menu); - QObject::connect (menu, & QMenu::aboutToShow, update_menu); tray->show (); hook_associate ("window close", window_closed, nullptr); @@ -172,11 +167,3 @@ void StatusIcon::toggle_aud_ui () { aud_ui_show (! aud_ui_is_shown ()); } - -void StatusIcon::update_menu () -{ - QList< QAction *> acts = menu->actions (); - - acts.at (0)->setVisible (aud_ui_is_shown ()); - acts.at (1)->setVisible (! aud_ui_is_shown ()); -} diff --git a/src/statusicon/statusicon.cc b/src/statusicon/statusicon.cc index 07b6661..1f474b4 100644 --- a/src/statusicon/statusicon.cc +++ b/src/statusicon/statusicon.cc @@ -274,13 +274,13 @@ static void open_files () static GtkWidget * si_menu_create () { static const AudguiMenuItem items[] = { - MenuCommand (N_("_Open Files ..."), "document-open", 0, (GdkModifierType) 0, open_files), - MenuCommand (N_("Pre_vious"), "media-skip-backward", 0, (GdkModifierType) 0, aud_drct_pl_prev), MenuCommand (N_("_Play"), "media-playback-start", 0, (GdkModifierType) 0, aud_drct_play), MenuCommand (N_("Paus_e"), "media-playback-pause", 0, (GdkModifierType) 0, aud_drct_pause), MenuCommand (N_("_Stop"), "media-playback-stop", 0, (GdkModifierType) 0, aud_drct_stop), + MenuCommand (N_("Pre_vious"), "media-skip-backward", 0, (GdkModifierType) 0, aud_drct_pl_prev), MenuCommand (N_("_Next"), "media-skip-forward", 0, (GdkModifierType) 0, aud_drct_pl_next), MenuSep (), + MenuCommand (N_("_Open Files ..."), "document-open", 0, (GdkModifierType) 0, open_files), MenuCommand (N_("Se_ttings ..."), "preferences-system", 0, (GdkModifierType) 0, audgui_show_prefs_window), MenuCommand (N_("_Quit"), "application-exit", 0, (GdkModifierType) 0, aud_quit) }; @@ -357,6 +357,10 @@ bool StatusIcon::init () aud_config_set_defaults ("statusicon", defaults); audgui_init (); si_enable (true); + + if (! aud_ui_is_shown ()) + gdk_notify_startup_complete (); + return true; } diff --git a/src/qtui/dialog_windows.cc b/src/ui-common/dialogs-qt.cc index 05c47d6..8f565ee 100644 --- a/src/qtui/dialog_windows.cc +++ b/src/ui-common/dialogs-qt.cc @@ -1,5 +1,5 @@ /* - * dialog_windows.cc + * dialogs-qt.cc * Copyright 2014 John Lindgren and Michał Lipski * * Redistribution and use in source and binary forms, with or without @@ -17,7 +17,7 @@ * the use of this software. */ -#include "dialog_windows.h" +#include "dialogs-qt.h" #include <libaudcore/i18n.h> #include <libaudqt/libaudqt.h> diff --git a/src/qtui/dialog_windows.h b/src/ui-common/dialogs-qt.h index f6a6778..d7fe4a9 100644 --- a/src/qtui/dialog_windows.h +++ b/src/ui-common/dialogs-qt.h @@ -1,5 +1,5 @@ /* - * dialog_windows.h + * dialogs-qt.h * Copyright 2014 John Lindgren and Michał Lipski * * Redistribution and use in source and binary forms, with or without diff --git a/src/ui-common/menu-ops-gtk.cc b/src/ui-common/menu-ops-gtk.cc index 2d85982..4f5d5c4 100644 --- a/src/ui-common/menu-ops-gtk.cc +++ b/src/ui-common/menu-ops-gtk.cc @@ -34,22 +34,22 @@ static void uri_clear_func (GtkClipboard *, void * data) void pl_copy () { - int list = aud_playlist_get_active (); - int entries = aud_playlist_entry_count (list); - int selected = aud_playlist_selected_count (list); + auto list = Playlist::active_playlist (); + int entries = list.n_entries (); + int selected = list.n_selected (); int fetched = 0; if (! selected) return; - aud_playlist_cache_selected (list); + list.cache_selected (); char * * uris = g_new (char *, selected + 1); for (int i = 0; i < entries && fetched < selected; i ++) { - if (aud_playlist_entry_get_selected (list, i)) - uris[fetched ++] = g_strdup (aud_playlist_entry_get_filename (list, i)); + if (list.entry_selected (i)) + uris[fetched ++] = g_strdup (list.entry_filename (i)); } uris[fetched] = nullptr; @@ -73,7 +73,7 @@ void pl_cut () pl_remove_selected (); } -static void paste_to (int list, int pos) +static void paste_to (Playlist list, int pos) { char * * uris = gtk_clipboard_wait_for_uris (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD)); if (! uris) @@ -83,35 +83,35 @@ static void paste_to (int list, int pos) for (int i = 0; uris[i]; i ++) items.append (String (uris[i])); - aud_playlist_entry_insert_batch (list, pos, std::move (items), false); + list.insert_items (pos, std::move (items), false); g_strfreev (uris); } void pl_paste () { - int list = aud_playlist_get_active (); - paste_to (list, aud_playlist_get_focus (list)); + auto list = Playlist::active_playlist (); + paste_to (list, list.get_focus ()); } void pl_paste_end () { - paste_to (aud_playlist_get_active (), -1); + paste_to (Playlist::active_playlist (), -1); } void pl_song_info () { - int list = aud_playlist_get_active (); - int focus = aud_playlist_get_focus (list); + auto list = Playlist::active_playlist (); + int focus = list.get_focus (); if (focus >= 0) audgui_infowin_show (list, focus); } void pl_open_folder () { - int list = aud_playlist_get_active (); - int focus = aud_playlist_get_focus (list); + auto list = Playlist::active_playlist (); + int focus = list.get_focus (); - String filename = aud_playlist_entry_get_filename (list, focus); + String filename = list.entry_filename (focus); if (! filename) return; diff --git a/src/ui-common/menu-ops-qt.cc b/src/ui-common/menu-ops-qt.cc index f603e5a..03e1042 100644 --- a/src/ui-common/menu-ops-qt.cc +++ b/src/ui-common/menu-ops-qt.cc @@ -31,19 +31,19 @@ void pl_copy () { - int list = aud_playlist_get_active (); - int entries = aud_playlist_entry_count (list); + auto list = Playlist::active_playlist (); + int entries = list.n_entries (); - if (! aud_playlist_selected_count (list)) + if (! list.n_selected ()) return; - aud_playlist_cache_selected (list); + list.cache_selected (); QList<QUrl> urls; for (int i = 0; i < entries; i ++) { - if (aud_playlist_entry_get_selected (list, i)) - urls.append (QString (aud_playlist_entry_get_filename (list, i))); + if (list.entry_selected (i)) + urls.append (QString (list.entry_filename (i))); } auto data = new QMimeData; @@ -57,7 +57,7 @@ void pl_cut () pl_remove_selected (); } -static void paste_to (int list, int pos) +static void paste_to (Playlist list, int pos) { auto data = QApplication::clipboard ()->mimeData (); if (! data->hasUrls ()) @@ -67,34 +67,34 @@ static void paste_to (int list, int pos) for (auto & url : data->urls ()) items.append (String (url.toEncoded ())); - aud_playlist_entry_insert_batch (list, pos, std::move (items), false); + list.insert_items (pos, std::move (items), false); } void pl_paste () { - int list = aud_playlist_get_active (); - paste_to (list, aud_playlist_get_focus (list)); + auto list = Playlist::active_playlist (); + paste_to (list, list.get_focus ()); } void pl_paste_end () { - paste_to (aud_playlist_get_active (), -1); + paste_to (Playlist::active_playlist (), -1); } void pl_song_info () { - int list = aud_playlist_get_active (); - int focus = aud_playlist_get_focus (list); + auto list = Playlist::active_playlist (); + int focus = list.get_focus (); if (focus >= 0) audqt::infowin_show (list, focus); } void pl_open_folder () { - int list = aud_playlist_get_active (); - int focus = aud_playlist_get_focus (list); + auto list = Playlist::active_playlist (); + int focus = list.get_focus (); - String filename = aud_playlist_entry_get_filename (list, focus); + String filename = list.entry_filename (focus); if (! filename) return; diff --git a/src/ui-common/menu-ops.cc b/src/ui-common/menu-ops.cc index cd2dc71..6469076 100644 --- a/src/ui-common/menu-ops.cc +++ b/src/ui-common/menu-ops.cc @@ -20,101 +20,94 @@ #include <libaudcore/drct.h> #include <libaudcore/playlist.h> -#define ACTIVE aud_playlist_get_active () - -void rm_dupes_title () { aud_playlist_remove_duplicates_by_scheme (ACTIVE, Playlist::Title); } -void rm_dupes_filename () { aud_playlist_remove_duplicates_by_scheme (ACTIVE, Playlist::Filename); } -void rm_dupes_path () { aud_playlist_remove_duplicates_by_scheme (ACTIVE, Playlist::Path); } - -void sort_track () { aud_playlist_sort_by_scheme (ACTIVE, Playlist::Track); } -void sort_title () { aud_playlist_sort_by_scheme (ACTIVE, Playlist::Title); } -void sort_artist () { aud_playlist_sort_by_scheme (ACTIVE, Playlist::Artist); } -void sort_album () { aud_playlist_sort_by_scheme (ACTIVE, Playlist::Album); } -void sort_album_artist () { aud_playlist_sort_by_scheme (ACTIVE, Playlist::AlbumArtist); } -void sort_date () { aud_playlist_sort_by_scheme (ACTIVE, Playlist::Date); } -void sort_genre () { aud_playlist_sort_by_scheme (ACTIVE, Playlist::Genre); } -void sort_length () { aud_playlist_sort_by_scheme (ACTIVE, Playlist::Length); } -void sort_path () { aud_playlist_sort_by_scheme (ACTIVE, Playlist::Path); } -void sort_filename () { aud_playlist_sort_by_scheme (ACTIVE, Playlist::Filename); } -void sort_custom_title () { aud_playlist_sort_by_scheme (ACTIVE, Playlist::FormattedTitle); } -void sort_reverse () { aud_playlist_reverse (ACTIVE); } -void sort_random () { aud_playlist_randomize (ACTIVE); } - -void sort_sel_track () { aud_playlist_sort_selected_by_scheme (ACTIVE, Playlist::Track); } -void sort_sel_title () { aud_playlist_sort_selected_by_scheme (ACTIVE, Playlist::Title); } -void sort_sel_artist () { aud_playlist_sort_selected_by_scheme (ACTIVE, Playlist::Artist); } -void sort_sel_album () { aud_playlist_sort_selected_by_scheme (ACTIVE, Playlist::Album); } -void sort_sel_album_artist () { aud_playlist_sort_selected_by_scheme (ACTIVE, Playlist::AlbumArtist); } -void sort_sel_date () { aud_playlist_sort_selected_by_scheme (ACTIVE, Playlist::Date); } -void sort_sel_genre () { aud_playlist_sort_selected_by_scheme (ACTIVE, Playlist::Genre); } -void sort_sel_length () { aud_playlist_sort_selected_by_scheme (ACTIVE, Playlist::Length); } -void sort_sel_path () { aud_playlist_sort_selected_by_scheme (ACTIVE, Playlist::Path); } -void sort_sel_filename () { aud_playlist_sort_selected_by_scheme (ACTIVE, Playlist::Filename); } -void sort_sel_custom_title () { aud_playlist_sort_selected_by_scheme (ACTIVE, Playlist::FormattedTitle); } -void sort_sel_reverse () { aud_playlist_reverse_selected (ACTIVE); } -void sort_sel_random () { aud_playlist_randomize_selected (ACTIVE); } +#define ACTIVE Playlist::active_playlist () + +void rm_dupes_title () { ACTIVE.remove_duplicates (Playlist::Title); } +void rm_dupes_filename () { ACTIVE.remove_duplicates (Playlist::Filename); } +void rm_dupes_path () { ACTIVE.remove_duplicates (Playlist::Path); } + +void sort_track () { ACTIVE.sort_entries (Playlist::Track); } +void sort_title () { ACTIVE.sort_entries (Playlist::Title); } +void sort_artist () { ACTIVE.sort_entries (Playlist::Artist); } +void sort_album () { ACTIVE.sort_entries (Playlist::Album); } +void sort_album_artist () { ACTIVE.sort_entries (Playlist::AlbumArtist); } +void sort_date () { ACTIVE.sort_entries (Playlist::Date); } +void sort_genre () { ACTIVE.sort_entries (Playlist::Genre); } +void sort_length () { ACTIVE.sort_entries (Playlist::Length); } +void sort_path () { ACTIVE.sort_entries (Playlist::Path); } +void sort_filename () { ACTIVE.sort_entries (Playlist::Filename); } +void sort_custom_title () { ACTIVE.sort_entries (Playlist::FormattedTitle); } +void sort_comment () { ACTIVE.sort_entries (Playlist::Comment); } +void sort_reverse () { ACTIVE.reverse_order (); } +void sort_random () { ACTIVE.randomize_order (); } + +void sort_sel_track () { ACTIVE.sort_selected (Playlist::Track); } +void sort_sel_title () { ACTIVE.sort_selected (Playlist::Title); } +void sort_sel_artist () { ACTIVE.sort_selected (Playlist::Artist); } +void sort_sel_album () { ACTIVE.sort_selected (Playlist::Album); } +void sort_sel_album_artist () { ACTIVE.sort_selected (Playlist::AlbumArtist); } +void sort_sel_date () { ACTIVE.sort_selected (Playlist::Date); } +void sort_sel_genre () { ACTIVE.sort_selected (Playlist::Genre); } +void sort_sel_length () { ACTIVE.sort_selected (Playlist::Length); } +void sort_sel_path () { ACTIVE.sort_selected (Playlist::Path); } +void sort_sel_filename () { ACTIVE.sort_selected (Playlist::Filename); } +void sort_sel_custom_title () { ACTIVE.sort_selected (Playlist::FormattedTitle); } +void sort_sel_comment () { ACTIVE.sort_selected (Playlist::Comment); } +void sort_sel_reverse () { ACTIVE.reverse_selected (); } +void sort_sel_random () { ACTIVE.randomize_selected (); } void pl_prev () { - int list = aud_playlist_get_active (); - aud_playlist_set_active (list > 0 ? list - 1 : aud_playlist_count () - 1); + int idx = ACTIVE.index (); + Playlist::by_index (idx > 0 ? idx - 1 : Playlist::n_playlists () - 1).activate (); } void pl_next () { - aud_playlist_set_active ((aud_playlist_get_active () + 1) % aud_playlist_count ()); + int idx = ACTIVE.index (); + Playlist::by_index ((idx + 1) % Playlist::n_playlists ()).activate (); } -void pl_play () { aud_playlist_play (ACTIVE); } +void pl_new () { Playlist::new_playlist (); } +void pl_play () { ACTIVE.start_playback (); } -void pl_select_all () { aud_playlist_select_all (ACTIVE, true); } -void pl_select_none () { aud_playlist_select_all (ACTIVE, false); } -void pl_refresh () { aud_playlist_rescan (ACTIVE); } -void pl_refresh_sel () { aud_playlist_rescan_selected (ACTIVE); } -void pl_remove_failed () { aud_playlist_remove_failed (ACTIVE); } -void pl_remove_selected () { aud_playlist_delete_selected (ACTIVE); } - -void pl_queue_clear () -{ - int list = aud_playlist_get_active (); - aud_playlist_queue_delete (list, 0, aud_playlist_queue_count (list)); -} +void pl_queue_clear () { ACTIVE.queue_remove_all (); } +void pl_select_all () { ACTIVE.select_all (true); } +void pl_select_none () { ACTIVE.select_all (false); } +void pl_refresh () { ACTIVE.rescan_all (); } +void pl_refresh_sel () { ACTIVE.rescan_selected (); } +void pl_remove_all () { ACTIVE.remove_all_entries (); } +void pl_remove_failed () { ACTIVE.remove_unavailable (); } +void pl_remove_selected () { ACTIVE.remove_selected (); } void pl_queue_toggle () { - int list = aud_playlist_get_active (); - int focus = aud_playlist_get_focus (list); + auto list = ACTIVE; + int focus = list.get_focus (); if (focus < 0) return; /* make sure focused row is selected */ - if (! aud_playlist_entry_get_selected (list, focus)) + if (! list.entry_selected (focus)) { - aud_playlist_select_all (list, false); - aud_playlist_entry_set_selected (list, focus, true); + list.select_all (false); + list.select_entry (focus, true); } - int at = aud_playlist_queue_find_entry (list, focus); + int at = list.queue_find_entry (focus); if (at < 0) - aud_playlist_queue_insert_selected (list, -1); + list.queue_insert_selected (-1); else - aud_playlist_queue_delete_selected (list); + list.queue_remove_selected (); } void pl_select_invert () { - int list = aud_playlist_get_active (); - int entries = aud_playlist_entry_count (list); + auto list = ACTIVE; + int entries = list.n_entries (); for (int entry = 0; entry < entries; entry ++) - aud_playlist_entry_set_selected (list, entry, - ! aud_playlist_entry_get_selected (list, entry)); -} - -void pl_remove_all () -{ - int list = aud_playlist_get_active (); - aud_playlist_entry_delete (list, 0, aud_playlist_entry_count (list)); + list.select_entry (entry, ! list.entry_selected (entry)); } void pl_remove_unselected () diff --git a/src/ui-common/menu-ops.h b/src/ui-common/menu-ops.h index b7bcfc7..39e2909 100644 --- a/src/ui-common/menu-ops.h +++ b/src/ui-common/menu-ops.h @@ -37,6 +37,7 @@ void sort_length (); void sort_path (); void sort_filename (); void sort_custom_title (); +void sort_comment (); void sort_reverse (); void sort_random (); @@ -52,12 +53,14 @@ void sort_sel_length (); void sort_sel_path (); void sort_sel_filename (); void sort_sel_custom_title (); +void sort_sel_comment (); void sort_sel_reverse (); void sort_sel_random (); // playlist switching void pl_prev (); void pl_next (); +void pl_new (); void pl_play (); // playlist editing diff --git a/src/vorbis/vcupdate.cc b/src/vorbis/vcupdate.cc index 2be5b7e..24ece96 100644 --- a/src/vorbis/vcupdate.cc +++ b/src/vorbis/vcupdate.cc @@ -48,21 +48,19 @@ static Dictionary dictionary_from_vorbis_comment (vorbis_comment * vc) const char * eq = strchr (s, '='); if (eq && eq > s && eq[1]) - dict.add (String (str_tolower (str_copy (s, eq - s))), String (eq + 1)); + dict.add (String (str_toupper (str_copy (s, eq - s))), String (eq + 1)); } return dict; } -static void add_tag_cb (const String & key, String & field, void * vc) -{ - vorbis_comment_add_tag ((vorbis_comment *) vc, key, field); -} - static void dictionary_to_vorbis_comment (vorbis_comment * vc, Dictionary & dict) { vorbis_comment_clear (vc); - dict.iterate (add_tag_cb, vc); + + dict.iterate ([vc] (const String & key, String & field) { + vorbis_comment_add_tag (vc, key, field); + }); } static void insert_str_tuple_field_to_dictionary (const Tuple & tuple, @@ -95,15 +93,15 @@ bool VorbisPlugin::write_tuple (const char * filename, VFSFile & file, const Tup Dictionary dict = dictionary_from_vorbis_comment (& edit.vc); - insert_str_tuple_field_to_dictionary (tuple, Tuple::Title, dict, "title"); - insert_str_tuple_field_to_dictionary (tuple, Tuple::Artist, dict, "artist"); - insert_str_tuple_field_to_dictionary (tuple, Tuple::Album, dict, "album"); - insert_str_tuple_field_to_dictionary (tuple, Tuple::AlbumArtist, dict, "albumartist"); - insert_str_tuple_field_to_dictionary (tuple, Tuple::Comment, dict, "comment"); - insert_str_tuple_field_to_dictionary (tuple, Tuple::Genre, dict, "genre"); + insert_str_tuple_field_to_dictionary (tuple, Tuple::Title, dict, "TITLE"); + insert_str_tuple_field_to_dictionary (tuple, Tuple::Artist, dict, "ARTIST"); + insert_str_tuple_field_to_dictionary (tuple, Tuple::Album, dict, "ALBUM"); + insert_str_tuple_field_to_dictionary (tuple, Tuple::AlbumArtist, dict, "ALBUMARTIST"); + insert_str_tuple_field_to_dictionary (tuple, Tuple::Comment, dict, "COMMENT"); + insert_str_tuple_field_to_dictionary (tuple, Tuple::Genre, dict, "GENRE"); - insert_int_tuple_field_to_dictionary (tuple, Tuple::Year, dict, "date"); - insert_int_tuple_field_to_dictionary (tuple, Tuple::Track, dict, "tracknumber"); + insert_int_tuple_field_to_dictionary (tuple, Tuple::Year, dict, "DATE"); + insert_int_tuple_field_to_dictionary (tuple, Tuple::Track, dict, "TRACKNUMBER"); dictionary_to_vorbis_comment (& edit.vc, dict); diff --git a/src/vorbis/vorbis.cc b/src/vorbis/vorbis.cc index a03b303..22111ec 100644 --- a/src/vorbis/vorbis.cc +++ b/src/vorbis/vorbis.cc @@ -34,6 +34,7 @@ #include <vorbis/codec.h> #include <vorbis/vorbisfile.h> +#define AUD_GLIB_INTEGRATION #define WANT_AUD_BSWAP #define WANT_VFS_STDIO_COMPAT #include <libaudcore/audstrings.h> @@ -135,16 +136,16 @@ static void read_comment (vorbis_comment * comment, Tuple & tuple) { const char * tmps; - set_tuple_str (tuple, Tuple::Title, comment, "title"); - set_tuple_str (tuple, Tuple::Artist, comment, "artist"); - set_tuple_str (tuple, Tuple::Album, comment, "album"); - set_tuple_str (tuple, Tuple::AlbumArtist, comment, "albumartist"); - set_tuple_str (tuple, Tuple::Genre, comment, "genre"); - set_tuple_str (tuple, Tuple::Comment, comment, "comment"); + set_tuple_str (tuple, Tuple::Title, comment, "TITLE"); + set_tuple_str (tuple, Tuple::Artist, comment, "ARTIST"); + set_tuple_str (tuple, Tuple::Album, comment, "ALBUM"); + set_tuple_str (tuple, Tuple::AlbumArtist, comment, "ALBUMARTIST"); + set_tuple_str (tuple, Tuple::Genre, comment, "GENRE"); + set_tuple_str (tuple, Tuple::Comment, comment, "COMMENT"); - if ((tmps = vorbis_comment_query (comment, "tracknumber", 0))) + if ((tmps = vorbis_comment_query (comment, "TRACKNUMBER", 0))) tuple.set_int (Tuple::Track, atoi (tmps)); - if ((tmps = vorbis_comment_query (comment, "date", 0))) + if ((tmps = vorbis_comment_query (comment, "DATE", 0))) tuple.set_int (Tuple::Year, atoi (tmps)); } @@ -156,7 +157,7 @@ static bool update_tuple (OggVorbis_File * vf, Tuple & tuple) return false; String old_title = tuple.get_str (Tuple::Title); - const char * new_title = vorbis_comment_query (comment, "title", 0); + const char * new_title = vorbis_comment_query (comment, "TITLE", 0); if (! new_title || (old_title && ! strcmp (old_title, new_title))) return false; @@ -173,22 +174,22 @@ static bool update_replay_gain (OggVorbis_File * vf, ReplayGainInfo * rg_info) if (! comment) return false; - rg_gain = vorbis_comment_query(comment, "replaygain_album_gain", 0); - if (!rg_gain) rg_gain = vorbis_comment_query(comment, "rg_audiophile", 0); /* Old */ + rg_gain = vorbis_comment_query(comment, "REPLAYGAIN_ALBUM_GAIN", 0); + if (!rg_gain) rg_gain = vorbis_comment_query(comment, "RG_AUDIOPHILE", 0); /* Old */ rg_info->album_gain = (rg_gain != nullptr) ? str_to_double (rg_gain) : 0.0; AUDDBG ("Album gain: %s (%f)\n", rg_gain, rg_info->album_gain); - rg_gain = vorbis_comment_query(comment, "replaygain_track_gain", 0); - if (!rg_gain) rg_gain = vorbis_comment_query(comment, "rg_radio", 0); /* Old */ + rg_gain = vorbis_comment_query(comment, "REPLAYGAIN_TRACK_GAIN", 0); + if (!rg_gain) rg_gain = vorbis_comment_query(comment, "RG_RADIO", 0); /* Old */ rg_info->track_gain = (rg_gain != nullptr) ? str_to_double (rg_gain) : 0.0; AUDDBG ("Track gain: %s (%f)\n", rg_gain, rg_info->track_gain); - rg_peak = vorbis_comment_query(comment, "replaygain_album_peak", 0); + rg_peak = vorbis_comment_query(comment, "REPLAYGAIN_ALBUM_PEAK", 0); rg_info->album_peak = rg_peak != nullptr ? str_to_double (rg_peak) : 0.0; AUDDBG ("Album peak: %s (%f)\n", rg_peak, rg_info->album_peak); - rg_peak = vorbis_comment_query(comment, "replaygain_track_peak", 0); - if (!rg_peak) rg_peak = vorbis_comment_query(comment, "rg_peak", 0); /* Old */ + rg_peak = vorbis_comment_query(comment, "REPLAYGAIN_TRACK_PEAK", 0); + if (!rg_peak) rg_peak = vorbis_comment_query(comment, "RG_PEAK", 0); /* Old */ rg_info->track_peak = rg_peak != nullptr ? str_to_double (rg_peak) : 0.0; AUDDBG ("Track peak: %s (%f)\n", rg_peak, rg_info->track_peak); @@ -359,7 +360,7 @@ static Index<char> read_image_from_comment (const char * filename, vorbis_commen unsigned mime_length, desc_length, length; size_t length2; - unsigned char * data2 = g_base64_decode (s, & length2); + CharPtr data2 ((char *) g_base64_decode (s, & length2)); if (! data2 || length2 < 8) goto PARSE_ERR; @@ -375,27 +376,23 @@ static Index<char> read_image_from_comment (const char * filename, vorbis_commen if (length2 < 8 + mime_length + 4 + desc_length + 20 + length) goto PARSE_ERR; - data.insert ((char *) data2 + 8 + mime_length + 4 + desc_length + 20, 0, length); - - g_free (data2); + data.insert (data2 + 8 + mime_length + 4 + desc_length + 20, 0, length); return data; PARSE_ERR: AUDERR ("Error parsing METADATA_BLOCK_PICTURE in %s.\n", filename); - g_free (data2); } if ((s = vorbis_comment_query (comment, "COVERART", 0))) { size_t length2; - void * data2 = g_base64_decode (s, & length2); + CharPtr data2 ((char *) g_base64_decode (s, & length2)); if (data2 && length2) - data.insert ((const char *) data2, 0, length2); + data.insert (data2, 0, length2); else AUDERR ("Error parsing COVERART in %s.\n", filename); - g_free (data2); return data; } diff --git a/src/xsf/Makefile b/src/xsf/Makefile index 60b5523..31944c5 100644 --- a/src/xsf/Makefile +++ b/src/xsf/Makefile @@ -13,6 +13,6 @@ plugindir := ${plugindir}/${INPUT_PLUGIN_DIR} LD = ${CXX} -CXXFLAGS += ${PLUGIN_CFLAGS} -Wno-sign-compare +CXXFLAGS += ${PLUGIN_CFLAGS} -Wno-sign-compare -Wno-shift-negative-value -Wno-bool-operation CPPFLAGS += ${PLUGIN_CPPFLAGS} -I../.. -Ispu/ LIBS += -lm -lz diff --git a/src/xsf/desmume/MMU.cc b/src/xsf/desmume/MMU.cc index 79e47ff..ebf1bc7 100644 --- a/src/xsf/desmume/MMU.cc +++ b/src/xsf/desmume/MMU.cc @@ -307,7 +307,7 @@ void MMU_clearMem() memset(ARM9Mem.ARM9_WRAM, 0, 0x01000000); memset(ARM9Mem.MAIN_MEM, 0, 0x400000); - memset(ARM9Mem.blank_memory, 0, 0x020000); + memset(ARM9Mem.blank_memory, 0, sizeof ARM9Mem.blank_memory); memset(MMU.ARM7_ERAM, 0, 0x010000); memset(MMU.ARM7_REG, 0, 0x010000); diff --git a/src/xspf/xspf.cc b/src/xspf/xspf.cc index dc89c9a..51d05ff 100644 --- a/src/xspf/xspf.cc +++ b/src/xspf/xspf.cc @@ -31,6 +31,7 @@ #include <libxml/xpathInternals.h> #include <libxml/uri.h> +#define AUD_GLIB_INTEGRATION #include <libaudcore/i18n.h> #include <libaudcore/plugin.h> #include <libaudcore/audstrings.h> @@ -259,7 +260,7 @@ bool XSPFLoader::load (const char * filename, VFSFile & file, String & title, ((c) >= 0x10000 && (c) <= 0x10ffff)) /* check for characters that are invalid in XML */ -static bool is_valid_string (const char * s, char * * subst) +static bool is_valid_string (const char * s, CharPtr & subst) { bool valid = g_utf8_validate (s, -1, nullptr); @@ -294,8 +295,8 @@ static bool is_valid_string (const char * s, char * * subst) p ++; } - * subst = g_new (char, len + 1); - char * w = * subst; + subst.capture (g_new (char, len + 1)); + char * w = subst.get (); p = s; while (* p) @@ -319,7 +320,7 @@ static bool is_valid_string (const char * s, char * * subst) static void xspf_add_node (xmlNodePtr node, bool isMeta, const char * xspfName, const char * strVal) { xmlNodePtr tmp; - char * subst; + CharPtr subst; if (isMeta) { tmp = xmlNewNode(nullptr, (xmlChar *) "meta"); @@ -327,13 +328,10 @@ static void xspf_add_node (xmlNodePtr node, bool isMeta, const char * xspfName, } else tmp = xmlNewNode(nullptr, (xmlChar *) xspfName); - if (is_valid_string (strVal, & subst)) + if (is_valid_string (strVal, subst)) xmlAddChild (tmp, xmlNewText ((xmlChar *) strVal)); else - { - xmlAddChild (tmp, xmlNewText ((xmlChar *) subst)); - g_free (subst); - } + xmlAddChild (tmp, xmlNewText ((xmlChar *) subst.get ())); xmlAddChild(node, tmp); } |