summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSimon Quigley <tsimonq2@ubuntu.com>2017-08-24 08:12:18 -0500
committerSimon Quigley <tsimonq2@ubuntu.com>2017-08-24 08:12:18 -0500
commit4de0719ae55d72d5c45952a2b54136f96d462ba6 (patch)
treef0aea4a55fd3f0e36cc7e9a8860fb9fdcc55d1f2 /src
parent5d911895bec79b393b4b47f09fac3a29d8ebb1c1 (diff)
New upstream version 3.9
Diffstat (limited to 'src')
-rw-r--r--src/adplug/Makefile2
-rw-r--r--src/adplug/core/adlibemu.cc53
-rw-r--r--src/adplug/core/protrack.cc2
-rw-r--r--src/albumart/albumart.cc7
-rw-r--r--src/alsa/alsa.cc8
-rw-r--r--src/amidiplug/amidi-plug.cc2
-rw-r--r--src/amidiplug/i_fileinfo.cc12
-rw-r--r--src/amidiplug/i_fileinfo.h2
-rw-r--r--src/ampache/ampache.cc12
-rw-r--r--src/cdaudio/cdaudio-ng.cc19
-rw-r--r--src/console/Makefile2
-rw-r--r--src/delete-files/delete-files.cc10
-rw-r--r--src/ffaudio/ffaudio-core.cc3
-rw-r--r--src/gtkui/Makefile2
-rw-r--r--src/gtkui/columns.cc26
-rw-r--r--src/gtkui/gtkui.h5
-rw-r--r--src/gtkui/layout.cc8
-rw-r--r--src/gtkui/menu-ops.cc2
-rw-r--r--src/gtkui/menus.cc39
-rw-r--r--src/gtkui/playlist_util.cc66
-rw-r--r--src/gtkui/playlist_util.h28
-rw-r--r--src/gtkui/settings.cc4
-rw-r--r--src/gtkui/ui_gtk.cc89
-rw-r--r--src/gtkui/ui_infoarea.cc35
-rw-r--r--src/gtkui/ui_playlist_notebook.cc282
-rw-r--r--src/gtkui/ui_playlist_notebook.h24
-rw-r--r--src/gtkui/ui_playlist_widget.cc121
-rw-r--r--src/gtkui/ui_playlist_widget.h7
-rw-r--r--src/gtkui/ui_statusbar.cc6
-rw-r--r--src/hotkey/plugin.cc6
-rw-r--r--src/lirc/lirc.cc13
-rw-r--r--src/lyricwiki-qt/lyricwiki.cc31
-rw-r--r--src/lyricwiki/lyricwiki.cc21
-rw-r--r--src/mpris2/plugin.cc17
-rw-r--r--src/neon/neon.cc4
-rw-r--r--src/notify/event.cc8
-rw-r--r--src/notify/osd.cc5
-rw-r--r--src/playlist-manager-qt/playlist-manager-qt.cc101
-rw-r--r--src/playlist-manager/playlist-manager.cc73
-rw-r--r--src/qtui/Makefile6
-rw-r--r--src/qtui/dialogs-qt.cc1
-rw-r--r--src/qtui/filter_input.cc56
-rw-r--r--src/qtui/info_bar.cc219
-rw-r--r--src/qtui/info_bar.h37
-rw-r--r--src/qtui/main_window.cc205
-rw-r--r--src/qtui/main_window.h75
-rw-r--r--src/qtui/menu-ops.cc2
-rw-r--r--src/qtui/menus.cc39
-rw-r--r--src/qtui/playlist.cc267
-rw-r--r--src/qtui/playlist.h32
-rw-r--r--src/qtui/playlist_header.cc297
-rw-r--r--src/qtui/playlist_header.h53
-rw-r--r--src/qtui/playlist_model.cc300
-rw-r--r--src/qtui/playlist_model.h73
-rw-r--r--src/qtui/playlist_tabs.cc204
-rw-r--r--src/qtui/playlist_tabs.h25
-rw-r--r--src/qtui/qtui.cc3
-rw-r--r--src/qtui/search_bar.cc106
-rw-r--r--src/qtui/search_bar.h (renamed from src/qtui/filter_input.h)24
-rw-r--r--src/qtui/settings.cc45
-rw-r--r--src/qtui/settings.h4
-rw-r--r--src/qtui/status_bar.cc94
-rw-r--r--src/qtui/status_bar.h15
-rw-r--r--src/qtui/time_slider.cc70
-rw-r--r--src/qtui/time_slider.h20
-rw-r--r--src/search-tool-qt/search-tool-qt.cc533
-rw-r--r--src/search-tool/search-tool.cc418
-rw-r--r--src/sid/xmms-sid.cc7
-rw-r--r--src/skins-qt/Makefile2
-rw-r--r--src/skins-qt/actions.cc2
-rw-r--r--src/skins-qt/dialogs-qt.cc1
-rw-r--r--src/skins-qt/main.cc9
-rw-r--r--src/skins-qt/menu-ops.cc2
-rw-r--r--src/skins-qt/menus.cc42
-rw-r--r--src/skins-qt/menus.h2
-rw-r--r--src/skins-qt/playlist-slider.cc4
-rw-r--r--src/skins-qt/playlist-widget.cc85
-rw-r--r--src/skins-qt/playlist-widget.h5
-rw-r--r--src/skins-qt/playlist.cc26
-rw-r--r--src/skins-qt/plugin-window.cc8
-rw-r--r--src/skins-qt/plugin.cc5
-rw-r--r--src/skins/Makefile1
-rw-r--r--src/skins/actions.cc2
-rw-r--r--src/skins/main.cc12
-rw-r--r--src/skins/menu-ops.cc2
-rw-r--r--src/skins/menus.cc40
-rw-r--r--src/skins/playlist-slider.cc4
-rw-r--r--src/skins/playlist-widget.cc98
-rw-r--r--src/skins/playlist-widget.h6
-rw-r--r--src/skins/playlist.cc26
-rw-r--r--src/skins/plugin.cc3
-rw-r--r--src/skins/search-select.cc32
-rw-r--r--src/skins/skins_cfg.cc1
-rw-r--r--src/skins/skinselector.cc25
-rw-r--r--src/skins/surface.cc10
-rw-r--r--src/song-info-qt/song-info.cc16
-rw-r--r--src/speedpitch/speed-pitch.cc41
-rw-r--r--src/statusicon-qt/statusicon.cc17
-rw-r--r--src/statusicon/statusicon.cc8
-rw-r--r--src/ui-common/dialogs-qt.cc (renamed from src/qtui/dialog_windows.cc)4
-rw-r--r--src/ui-common/dialogs-qt.h (renamed from src/qtui/dialog_windows.h)2
-rw-r--r--src/ui-common/menu-ops-gtk.cc32
-rw-r--r--src/ui-common/menu-ops-qt.cc32
-rw-r--r--src/ui-common/menu-ops.cc127
-rw-r--r--src/ui-common/menu-ops.h3
-rw-r--r--src/vorbis/vcupdate.cc28
-rw-r--r--src/vorbis/vorbis.cc45
-rw-r--r--src/xsf/Makefile2
-rw-r--r--src/xsf/desmume/MMU.cc2
-rw-r--r--src/xspf/xspf.cc16
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);
}