summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlessio Treglia <alessio@debian.org>2014-06-12 12:30:32 +0100
committerAlessio Treglia <alessio@debian.org>2014-06-12 12:30:32 +0100
commit260690bc6b904d1ca0025e539b9663ddb9fd2424 (patch)
tree6638a0254217dc8443bdd4ff00a5d78c5a3da556 /src
parentb380f841f764543f477b658af25194c4d9f8a1eb (diff)
Imported Upstream version 3.5
Diffstat (limited to 'src')
-rw-r--r--src/Makefile13
-rw-r--r--src/audacious/Makefile62
-rw-r--r--src/audacious/adder.c146
-rw-r--r--src/audacious/api-alias-begin.h2
-rw-r--r--src/audacious/api-alias-end.h2
-rw-r--r--src/audacious/api-declare-begin.h2
-rw-r--r--src/audacious/api-declare-end.h2
-rw-r--r--src/audacious/api-define-begin.h2
-rw-r--r--src/audacious/api-define-end.h2
-rw-r--r--src/audacious/api-local-begin.h2
-rw-r--r--src/audacious/api-local-end.h2
-rw-r--r--src/audacious/api.h19
-rw-r--r--src/audacious/art.c66
-rw-r--r--src/audacious/chardet.c194
-rw-r--r--src/audacious/config.c411
-rw-r--r--src/audacious/dbus-server.c779
-rw-r--r--src/audacious/dbus-service.h237
-rw-r--r--src/audacious/dbus.c1049
-rw-r--r--src/audacious/dbus.h48
-rw-r--r--src/audacious/debug.h2
-rw-r--r--src/audacious/drct-api.h24
-rw-r--r--src/audacious/drct.c21
-rw-r--r--src/audacious/effect.c13
-rw-r--r--src/audacious/effect.h2
-rw-r--r--src/audacious/equalizer.c12
-rw-r--r--src/audacious/equalizer_preset.c272
-rw-r--r--src/audacious/fft.c6
-rw-r--r--src/audacious/history.c14
-rw-r--r--src/audacious/images/appearance.pngbin0 -> 2721 bytes
-rw-r--r--src/audacious/images/audacious.pngbin1788 -> 0 bytes
-rw-r--r--src/audacious/images/menu_playlist.pngbin688 -> 0 bytes
-rw-r--r--src/audacious/images/menu_plugin.pngbin588 -> 0 bytes
-rw-r--r--src/audacious/images/menu_queue_toggle.pngbin686 -> 0 bytes
-rw-r--r--src/audacious/input-api.h60
-rw-r--r--src/audacious/input.h69
-rw-r--r--src/audacious/interface.c72
-rw-r--r--src/audacious/main.c555
-rw-r--r--src/audacious/main.h21
-rw-r--r--src/audacious/misc-api.h65
-rw-r--r--src/audacious/misc.h12
-rw-r--r--src/audacious/mpris-signals.c64
-rw-r--r--src/audacious/mpris_player.xml77
-rw-r--r--src/audacious/mpris_root.xml31
-rw-r--r--src/audacious/mpris_tracklist.xml52
-rw-r--r--src/audacious/output.c75
-rw-r--r--src/audacious/output.h4
-rw-r--r--src/audacious/playback.c422
-rw-r--r--src/audacious/playback.h2
-rw-r--r--src/audacious/playlist-api.h7
-rw-r--r--src/audacious/playlist-files.c181
-rw-r--r--src/audacious/playlist-new.c101
-rw-r--r--src/audacious/playlist-utils.c136
-rw-r--r--src/audacious/plugin-init.c8
-rw-r--r--src/audacious/plugin-preferences.c27
-rw-r--r--src/audacious/plugin-registry.c151
-rw-r--r--src/audacious/plugin-view.c44
-rw-r--r--src/audacious/plugin.h181
-rw-r--r--src/audacious/pluginenum.c57
-rw-r--r--src/audacious/plugins-api.h9
-rw-r--r--src/audacious/plugins.h8
-rw-r--r--src/audacious/preferences.c641
-rw-r--r--src/audacious/preferences.h6
-rw-r--r--src/audacious/probe-buffer.c28
-rw-r--r--src/audacious/probe.c49
-rw-r--r--src/audacious/scanner.c12
-rw-r--r--src/audacious/scanner.h2
-rw-r--r--src/audacious/signals.c18
-rw-r--r--src/audacious/types.h8
-rw-r--r--src/audacious/ui_albumart.c239
-rw-r--r--src/audacious/ui_plugin_menu.c38
-rw-r--r--src/audacious/ui_preferences.c1405
-rw-r--r--src/audacious/ui_preferences.h4
-rw-r--r--src/audacious/util.c184
-rw-r--r--src/audacious/util.h14
-rw-r--r--src/audacious/vis_runner.c141
-rw-r--r--src/audtool/Makefile17
-rw-r--r--src/audtool/audtool.h215
-rw-r--r--src/audtool/handlers_equalizer.c142
-rw-r--r--src/audtool/handlers_general.c225
-rw-r--r--src/audtool/handlers_playback.c127
-rw-r--r--src/audtool/handlers_playlist.c451
-rw-r--r--src/audtool/handlers_playqueue.c147
-rw-r--r--src/audtool/handlers_vitals.c176
-rw-r--r--src/audtool/main.c314
-rw-r--r--src/audtool/report.c111
-rw-r--r--src/audtool/wrappers.c194
-rw-r--r--src/audtool/wrappers.h43
-rw-r--r--src/dbus/Makefile18
-rw-r--r--src/dbus/aud-dbus.xml (renamed from src/audacious/objects.xml)262
-rw-r--r--src/libaudclient/Makefile23
-rw-r--r--src/libaudclient/audctrl.c993
-rw-r--r--src/libaudclient/audctrl.h138
-rw-r--r--src/libaudcore/Makefile17
-rw-r--r--src/libaudcore/audio.c7
-rw-r--r--src/libaudcore/audio.h.in2
-rw-r--r--src/libaudcore/audstrings.c599
-rw-r--r--src/libaudcore/audstrings.h101
-rw-r--r--src/libaudcore/charset.c180
-rw-r--r--src/libaudcore/core.h27
-rw-r--r--src/libaudcore/eventqueue.c31
-rw-r--r--src/libaudcore/hook.c2
-rw-r--r--src/libaudcore/hook.h3
-rw-r--r--src/libaudcore/index.c142
-rw-r--r--src/libaudcore/index.h66
-rw-r--r--src/libaudcore/inifile.c134
-rw-r--r--src/libaudcore/inifile.h (renamed from src/libaudgui/ui_regex.h)72
-rw-r--r--src/libaudcore/multihash.c153
-rw-r--r--src/libaudcore/multihash.h95
-rw-r--r--src/libaudcore/strpool.c259
-rw-r--r--src/libaudcore/tinylock.c65
-rw-r--r--src/libaudcore/tinylock.h.in56
-rw-r--r--src/libaudcore/tuple.c286
-rw-r--r--src/libaudcore/tuple.h49
-rw-r--r--src/libaudcore/tuple_compiler.c182
-rw-r--r--src/libaudcore/tuple_compiler.h5
-rw-r--r--src/libaudcore/tuple_formatter.c74
-rw-r--r--src/libaudcore/vfs.c103
-rw-r--r--src/libaudcore/vfs.h34
-rw-r--r--src/libaudcore/vfs_async.c2
-rw-r--r--src/libaudcore/vfs_common.c280
-rw-r--r--src/libaudcore/vfs_local.c231
-rw-r--r--src/libaudcore/vfs_local.h (renamed from src/libaudcore/tuple_formatter.h)15
-rw-r--r--src/libaudgui/Makefile17
-rw-r--r--src/libaudgui/about.c37
-rw-r--r--src/libaudgui/confirm.c84
-rw-r--r--src/libaudgui/effects-menu.c94
-rw-r--r--src/libaudgui/equalizer.c134
-rw-r--r--src/libaudgui/icons-stock.c60
-rw-r--r--src/libaudgui/iface-menu.c55
-rw-r--r--src/libaudgui/infopopup.c62
-rw-r--r--src/libaudgui/infowin.c402
-rw-r--r--src/libaudgui/init.c111
-rw-r--r--src/libaudgui/init.h42
-rw-r--r--src/libaudgui/jump-to-time.c62
-rw-r--r--src/libaudgui/libaudgui-gtk.h36
-rw-r--r--src/libaudgui/libaudgui.h18
-rw-r--r--src/libaudgui/list.c2
-rw-r--r--src/libaudgui/menu.c134
-rw-r--r--src/libaudgui/menu.h62
-rw-r--r--src/libaudgui/pixbufs.c141
-rw-r--r--src/libaudgui/playlists.c182
-rw-r--r--src/libaudgui/queue-manager.c67
-rw-r--r--src/libaudgui/scaled-image.c114
-rw-r--r--src/libaudgui/ui_fileopener.c180
-rw-r--r--src/libaudgui/ui_jumptotrack.c96
-rw-r--r--src/libaudgui/ui_jumptotrack_cache.c105
-rw-r--r--src/libaudgui/ui_playlist_manager.c148
-rw-r--r--src/libaudgui/urilist.c9
-rw-r--r--src/libaudgui/url-opener.c77
-rw-r--r--src/libaudgui/util.c267
-rw-r--r--src/libaudtag/ape/ape.c137
-rw-r--r--src/libaudtag/audtag.c62
-rw-r--r--src/libaudtag/audtag.h3
-rw-r--r--src/libaudtag/id3/id3-common.c287
-rw-r--r--src/libaudtag/id3/id3-common.h33
-rw-r--r--src/libaudtag/id3/id3v1.c200
-rw-r--r--src/libaudtag/id3/id3v22.c315
-rw-r--r--src/libaudtag/id3/id3v24.c447
-rw-r--r--src/libaudtag/tag_module.c5
-rw-r--r--src/libaudtag/util.c255
-rw-r--r--src/libaudtag/util.h44
161 files changed, 9405 insertions, 10735 deletions
diff --git a/src/Makefile b/src/Makefile
index 3b0ed74..482f7c0 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,9 +1,16 @@
-SUBDIRS = libaudcore libaudgui libaudtag audacious
-
include ../extra.mk
+SUBDIRS := audacious libaudcore libaudgui libaudtag
+
ifeq ($(USE_DBUS),yes)
-SUBDIRS := libaudclient audtool ${SUBDIRS}
+SUBDIRS := dbus audtool ${SUBDIRS}
endif
include ../buildsys.mk
+
+audacious libaudgui libaudtag: libaudcore
+audacious: libaudgui libaudtag
+
+ifeq ($(USE_DBUS),yes)
+audacious audtool: dbus
+endif
diff --git a/src/audacious/Makefile b/src/audacious/Makefile
index c87881e..bccf836 100644
--- a/src/audacious/Makefile
+++ b/src/audacious/Makefile
@@ -14,7 +14,6 @@ SRCS = adder.c \
history.c \
interface.c \
main.c \
- mpris-signals.c \
output.c \
playback.c \
playlist-files.c \
@@ -25,6 +24,7 @@ SRCS = adder.c \
plugin-registry.c \
plugin-init.c \
plugin-view.c \
+ preferences.c \
probe.c \
probe-buffer.c \
scanner.c \
@@ -36,10 +36,6 @@ SRCS = adder.c \
visualization.c \
ui_albumart.c
-ifeq ($(USE_DBUS),yes)
-SRCS += dbus.c
-endif
-
ifeq ($(HAVE_MSWINDOWS),yes)
SRCS += audacious.rc
endif
@@ -49,12 +45,12 @@ INCLUDES = api.h \
api-alias-end.h \
api-define-begin.h \
api-define-end.h \
- dbus.h \
- dbus-service.h \
debug.h \
drct.h \
drct-api.h \
i18n.h \
+ input.h \
+ input-api.h \
misc.h \
misc-api.h \
playlist.h \
@@ -67,39 +63,43 @@ INCLUDES = api.h \
DATA = images/about-logo.png \
images/album.png \
- images/audacious.png \
+ images/appearance.png \
images/audio.png \
images/connectivity.png \
images/info.png \
- images/menu_playlist.png \
- images/menu_plugin.png \
- images/menu_queue_toggle.png \
images/playlist.png \
images/plugins.png
-CLEAN = build_stamp.c dbus-client-bindings.h dbus-server-bindings.h
+CLEAN = build_stamp.c
+
+ifeq ($(USE_DBUS),yes)
+SRCS += dbus-server.c
+EXT_DEPS += ../dbus/aud-dbus.a
+endif
include ../../buildsys.mk
ifeq ($(USE_DBUS),yes)
-DBUS_BINDINGS = dbus-server-bindings.h dbus-client-bindings.h
-LIBS := -L../libaudclient -laudclient ${LIBS}
+CPPFLAGS := -I../dbus ${CPPFLAGS} ${GIO_CFLAGS}
+LIBS := ../dbus/aud-dbus.a ${LIBS} ${GIO_LIBS}
endif
-pre-depend: ${DBUS_BINDINGS}
-
CPPFLAGS := -I.. -I../.. \
${CPPFLAGS} \
${GLIB_CFLAGS} \
${GMODULE_CFLAGS} \
- ${GTHREAD_CFLAGS} \
${GTK_CFLAGS} \
- ${AUDACIOUS_DEFINES} \
- ${DBUS_CFLAGS} \
- ${REGEX_CFLAGS} \
- -D_AUDACIOUS_CORE \
${LIBGUESS_CFLAGS}
+CPPFLAGS := ${CPPFLAGS} \
+ -D_AUDACIOUS_CORE \
+ -DHARDCODE_BINDIR=\"${bindir}\" \
+ -DHARDCODE_DATADIR=\"${datadir}/audacious\" \
+ -DHARDCODE_PLUGINDIR=\"${plugindir}\" \
+ -DHARDCODE_LOCALEDIR=\"${localedir}\" \
+ -DHARDCODE_DESKTOPFILE=\"${datarootdir}/applications/audacious.desktop\" \
+ -DHARDCODE_ICONFILE=\"${datarootdir}/pixmaps/audacious.png\"
+
LIBS := -L../libaudcore -laudcore \
-L../libaudgui -laudgui \
-L../libaudtag -laudtag \
@@ -107,25 +107,7 @@ LIBS := -L../libaudcore -laudcore \
${LIBINTL} \
${GLIB_LIBS} \
${GMODULE_LIBS} \
- ${GTHREAD_LIBS} \
- ${GTK_LIBS} \
- ${DBUS_LIBS} \
- ${REGEX_LIBS} \
- ${LIBGUESS_LIBS}
-
-DBUS_BINDINGS_SOURCES = objects.xml \
- mpris_root.xml \
- mpris_tracklist.xml \
- mpris_player.xml
+ ${GTK_LIBS}
desktop_DATA = audacious.desktop
desktopdir = ${datarootdir}/applications
-
-dbus-server-bindings.h: ${DBUS_BINDINGS_SOURCES}
- ${DBUS_BINDING_TOOL} --mode=glib-server --prefix=audacious_rc objects.xml > $@
- ${DBUS_BINDING_TOOL} --mode=glib-server --prefix=mpris_root mpris_root.xml >> $@
- ${DBUS_BINDING_TOOL} --mode=glib-server --prefix=mpris_tracklist mpris_tracklist.xml >> $@
- ${DBUS_BINDING_TOOL} --mode=glib-server --prefix=mpris_player mpris_player.xml >> $@
-
-dbus-client-bindings.h: ${DBUS_BINDINGS_SOURCES}
- ${DBUS_BINDING_TOOL} --mode=glib-client --prefix=audacious_rc objects.xml > $@
diff --git a/src/audacious/adder.c b/src/audacious/adder.c
index 5d3a138..509f363 100644
--- a/src/audacious/adder.c
+++ b/src/audacious/adder.c
@@ -1,6 +1,6 @@
/*
* adder.c
- * Copyright 2011 John Lindgren
+ * Copyright 2011-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,11 +17,11 @@
* the use of this software.
*/
-#include <dirent.h>
#include <pthread.h>
#include <string.h>
#include <sys/stat.h>
+#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include <libaudcore/audstrings.h>
@@ -33,6 +33,7 @@
#include "plugins.h"
#include "main.h"
#include "misc.h"
+#include "util.h"
typedef struct {
int playlist_id, at;
@@ -45,6 +46,7 @@ typedef struct {
typedef struct {
int playlist_id, at;
bool_t play;
+ char * title;
Index * filenames, * tuples, * decoders;
} AddResult;
@@ -66,7 +68,7 @@ static GtkWidget * status_window = NULL, * status_path_label,
static bool_t status_cb (void * unused)
{
- if (! headless && ! status_window)
+ if (! headless_mode () && ! status_window)
{
status_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_type_hint ((GtkWindow *) status_window,
@@ -102,7 +104,7 @@ static bool_t status_cb (void * unused)
snprintf (scratch, sizeof scratch, dngettext (PACKAGE, "%d file found",
"%d files found", status_count), status_count);
- if (headless)
+ if (headless_mode ())
{
printf ("Searching, %s ...\r", scratch);
fflush (stdout);
@@ -138,34 +140,12 @@ static void status_done_locked (void)
status_source = 0;
}
- if (headless)
+ if (headless_mode ())
printf ("\n");
else if (status_window)
gtk_widget_destroy (status_window);
}
-static void index_free_filenames (Index * filenames)
-{
- int count = index_count (filenames);
- for (int i = 0; i < count; i ++)
- str_unref (index_get (filenames, i));
-
- index_free (filenames);
-}
-
-static void index_free_tuples (Index * tuples)
-{
- int count = index_count (tuples);
- for (int i = 0; i < count; i ++)
- {
- Tuple * tuple = index_get (tuples, i);
- if (tuple)
- tuple_unref (tuple);
- }
-
- index_free (tuples);
-}
-
static AddTask * add_task_new (int playlist_id, int at, bool_t play,
Index * filenames, Index * tuples, PlaylistFilterFunc filter,
void * user)
@@ -184,9 +164,9 @@ static AddTask * add_task_new (int playlist_id, int at, bool_t play,
static void add_task_free (AddTask * task)
{
if (task->filenames)
- index_free_filenames (task->filenames);
+ index_free_full (task->filenames, (IndexFreeFunc) str_unref);
if (task->tuples)
- index_free_tuples (task->tuples);
+ index_free_full (task->tuples, (IndexFreeFunc) tuple_unref);
g_slice_free (AddTask, task);
}
@@ -197,6 +177,7 @@ static AddResult * add_result_new (int playlist_id, int at, bool_t play)
result->playlist_id = playlist_id;
result->at = at;
result->play = play;
+ result->title = NULL;
result->filenames = index_new ();
result->tuples = index_new ();
result->decoders = index_new ();
@@ -205,10 +186,12 @@ static AddResult * add_result_new (int playlist_id, int at, bool_t play)
static void add_result_free (AddResult * result)
{
+ str_unref (result->title);
+
if (result->filenames)
- index_free_filenames (result->filenames);
+ index_free_full (result->filenames, (IndexFreeFunc) str_unref);
if (result->tuples)
- index_free_tuples (result->tuples);
+ index_free_full (result->tuples, (IndexFreeFunc) tuple_unref);
if (result->decoders)
index_free (result->decoders);
@@ -257,86 +240,79 @@ static void add_file (char * filename, Tuple * tuple, PluginHandle * decoder,
return;
}
- index_append (result->filenames, filename);
- index_append (result->tuples, tuple);
- index_append (result->decoders, decoder);
+ index_insert (result->filenames, -1, filename);
+ index_insert (result->tuples, -1, tuple);
+ index_insert (result->decoders, -1, decoder);
}
static void add_folder (char * filename, PlaylistFilterFunc filter,
- void * user, AddResult * result)
+ void * user, AddResult * result, bool_t is_single)
{
+ char * path = NULL;
+
g_return_if_fail (filename);
+
if (filter && ! filter (filename, user))
- {
- str_unref (filename);
- return;
- }
+ goto DONE;
status_update (filename, index_count (result->filenames));
- char * unix_name = uri_to_filename (filename);
- if (! unix_name)
- {
- str_unref (filename);
- return;
- }
+ if (! (path = uri_to_filename (filename)))
+ goto DONE;
GList * files = NULL;
- DIR * folder = opendir (unix_name);
+ GDir * folder = g_dir_open (path, 0, NULL);
if (! folder)
- goto FREE;
+ goto DONE;
+
+ const char * name;
+ while ((name = g_dir_read_name (folder)))
+ {
+ char * filepath = filename_build (path, name);
+ files = g_list_prepend (files, filepath);
+ }
+
+ g_dir_close (folder);
- struct dirent * entry;
- while ((entry = readdir (folder)))
+ if (files && is_single)
{
- if (entry->d_name[0] != '.')
- files = g_list_prepend (files, g_build_filename (unix_name, entry->d_name, NULL));
+ char * last = last_path_element (path);
+ result->title = str_get (last ? last : path);
}
- closedir (folder);
- files = g_list_sort (files, (GCompareFunc) string_compare);
+ files = g_list_sort (files, (GCompareFunc) str_compare);
while (files)
{
- struct stat info;
-#ifdef S_ISLNK
- if (lstat (files->data, & info) < 0)
-#else
- if (stat (files->data, & info) < 0)
-#endif
+ GStatBuf info;
+ if (g_lstat (files->data, & info) < 0)
goto NEXT;
if (S_ISREG (info.st_mode))
{
char * item_name = filename_to_uri (files->data);
if (item_name)
- {
- add_file (str_get (item_name), NULL, NULL, filter, user, result, TRUE);
- g_free (item_name);
- }
+ add_file (item_name, NULL, NULL, filter, user, result, TRUE);
}
else if (S_ISDIR (info.st_mode))
{
char * item_name = filename_to_uri (files->data);
if (item_name)
- {
- add_folder (str_get (item_name), filter, user, result);
- g_free (item_name);
- }
+ add_folder (item_name, filter, user, result, FALSE);
}
NEXT:
- g_free (files->data);
+ str_unref (files->data);
files = g_list_delete_link (files, files);
}
-FREE:
+DONE:
str_unref (filename);
- g_free (unix_name);
+ str_unref (path);
}
static void add_playlist (char * filename, PlaylistFilterFunc filter,
- void * user, AddResult * result)
+ void * user, AddResult * result, bool_t is_single)
{
g_return_if_fail (filename);
if (filter && ! filter (filename, user))
@@ -355,29 +331,33 @@ static void add_playlist (char * filename, PlaylistFilterFunc filter,
return;
}
+ if (is_single)
+ result->title = title;
+ else
+ str_unref (title);
+
int count = index_count (filenames);
for (int i = 0; i < count; i ++)
add_file (index_get (filenames, i), tuples ? index_get (tuples, i) :
NULL, NULL, filter, user, result, FALSE);
str_unref (filename);
- str_unref (title);
index_free (filenames);
if (tuples)
index_free (tuples);
}
static void add_generic (char * filename, Tuple * tuple,
- PlaylistFilterFunc filter, void * user, AddResult * result)
+ PlaylistFilterFunc filter, void * user, AddResult * result, bool_t is_single)
{
g_return_if_fail (filename);
if (tuple)
add_file (filename, tuple, NULL, filter, user, result, FALSE);
else if (vfs_file_test (filename, G_FILE_TEST_IS_DIR))
- add_folder (filename, filter, user, result);
+ add_folder (filename, filter, user, result, is_single);
else if (filename_is_playlist (filename))
- add_playlist (filename, filter, user, result);
+ add_playlist (filename, filter, user, result, is_single);
else
add_file (filename, NULL, NULL, filter, user, result, FALSE);
}
@@ -399,6 +379,16 @@ static bool_t add_finish (void * unused)
if (result->at < 0 || result->at > count)
result->at = count;
+ if (result->title && ! count)
+ {
+ char * old_title = playlist_get_title (playlist);
+
+ if (! strcmp (old_title, N_("New Playlist")))
+ playlist_set_title (playlist, result->title);
+
+ str_unref (old_title);
+ }
+
playlist_entry_insert_batch_raw (playlist, result->at,
result->filenames, result->tuples, result->decoders);
result->filenames = NULL;
@@ -461,7 +451,7 @@ static void * add_worker (void * unused)
{
add_generic (index_get (task->filenames, i), task->tuples ?
index_get (task->tuples, i) : NULL, task->filter, task->user,
- result);
+ result, (count == 1));
index_set (task->filenames, i, NULL);
if (task->tuples)
@@ -518,8 +508,8 @@ void playlist_entry_insert (int playlist, int at, const char * filename,
{
Index * filenames = index_new ();
Index * tuples = index_new ();
- index_append (filenames, str_get (filename));
- index_append (tuples, tuple);
+ index_insert (filenames, -1, str_get (filename));
+ index_insert (tuples, -1, tuple);
playlist_entry_insert_batch (playlist, at, filenames, tuples, play);
}
diff --git a/src/audacious/api-alias-begin.h b/src/audacious/api-alias-begin.h
index 3a947fa..457d29d 100644
--- a/src/audacious/api-alias-begin.h
+++ b/src/audacious/api-alias-begin.h
@@ -1,6 +1,6 @@
/*
* api-alias-begin.h
- * Copyright 2010 John Lindgren
+ * Copyright 2010-2011 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/audacious/api-alias-end.h b/src/audacious/api-alias-end.h
index ac0a2bd..a003cde 100644
--- a/src/audacious/api-alias-end.h
+++ b/src/audacious/api-alias-end.h
@@ -1,6 +1,6 @@
/*
* api-alias-end.h
- * Copyright 2010 John Lindgren
+ * Copyright 2010-2011 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/audacious/api-declare-begin.h b/src/audacious/api-declare-begin.h
index d6c3d34..defc557 100644
--- a/src/audacious/api-declare-begin.h
+++ b/src/audacious/api-declare-begin.h
@@ -1,6 +1,6 @@
/*
* api-declare-begin.h
- * Copyright 2010 John Lindgren
+ * Copyright 2010-2011 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/audacious/api-declare-end.h b/src/audacious/api-declare-end.h
index 8ac6135..fa73092 100644
--- a/src/audacious/api-declare-end.h
+++ b/src/audacious/api-declare-end.h
@@ -1,6 +1,6 @@
/*
* api-declare-end.h
- * Copyright 2010 John Lindgren
+ * Copyright 2010-2011 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/audacious/api-define-begin.h b/src/audacious/api-define-begin.h
index 9904761..5fe32cd 100644
--- a/src/audacious/api-define-begin.h
+++ b/src/audacious/api-define-begin.h
@@ -1,6 +1,6 @@
/*
* api-define-begin.h
- * Copyright 2010 John Lindgren
+ * Copyright 2010-2011 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/audacious/api-define-end.h b/src/audacious/api-define-end.h
index 25d874f..91bd85f 100644
--- a/src/audacious/api-define-end.h
+++ b/src/audacious/api-define-end.h
@@ -1,6 +1,6 @@
/*
* api-define-end.h
- * Copyright 2010 John Lindgren
+ * Copyright 2010-2011 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/audacious/api-local-begin.h b/src/audacious/api-local-begin.h
index 88b780d..b32eeb1 100644
--- a/src/audacious/api-local-begin.h
+++ b/src/audacious/api-local-begin.h
@@ -1,6 +1,6 @@
/*
* api-local-begin.h
- * Copyright 2010 John Lindgren
+ * Copyright 2010-2011 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/audacious/api-local-end.h b/src/audacious/api-local-end.h
index 1aff447..81ec43d 100644
--- a/src/audacious/api-local-end.h
+++ b/src/audacious/api-local-end.h
@@ -1,6 +1,6 @@
/*
* api-local-end.h
- * Copyright 2010 John Lindgren
+ * Copyright 2010-2011 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/audacious/api.h b/src/audacious/api.h
index 523c2b9..f68987a 100644
--- a/src/audacious/api.h
+++ b/src/audacious/api.h
@@ -1,6 +1,6 @@
/*
* api.h
- * Copyright 2010 John Lindgren
+ * Copyright 2010-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -20,9 +20,26 @@
#ifndef AUDACIOUS_API_H
#define AUDACIOUS_API_H
+/* API version. Plugins are marked with this number at compile time.
+ *
+ * _AUD_PLUGIN_VERSION is the current version; _AUD_PLUGIN_VERSION_MIN is
+ * the oldest one we are backward compatible with. Plugins marked older than
+ * _AUD_PLUGIN_VERSION_MIN or newer than _AUD_PLUGIN_VERSION are not loaded.
+ *
+ * Before releases that add new pointers to the end of the API tables, increment
+ * _AUD_PLUGIN_VERSION but leave _AUD_PLUGIN_VERSION_MIN the same.
+ *
+ * Before releases that break backward compatibility (e.g. remove pointers from
+ * the API tables), increment _AUD_PLUGIN_VERSION *and* set
+ * _AUD_PLUGIN_VERSION_MIN to the same value. */
+
+#define _AUD_PLUGIN_VERSION_MIN 45 /* 3.5-devel */
+#define _AUD_PLUGIN_VERSION 45 /* 3.5-devel */
+
typedef const struct {
const struct ConfigDBAPI * configdb_api;
const struct DRCTAPI * drct_api;
+ const struct InputAPI * input_api;
const struct MiscAPI * misc_api;
const struct PlaylistAPI * playlist_api;
const struct PluginsAPI * plugins_api;
diff --git a/src/audacious/art.c b/src/audacious/art.c
index f09fcc5..24658a0 100644
--- a/src/audacious/art.c
+++ b/src/audacious/art.c
@@ -19,13 +19,14 @@
#include <assert.h>
#include <errno.h>
-#include <glib.h>
#include <pthread.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+
#include <libaudcore/audstrings.h>
#include <libaudcore/hook.h>
@@ -47,12 +48,11 @@ typedef struct {
int64_t len;
/* album art as (possibly a temporary) file */
- char * art_file;
+ char * art_file; /* pooled */
bool_t is_temp;
} ArtItem;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static GHashTable * art_items; /* of ArtItem */
static char * current_ref; /* pooled */
@@ -66,13 +66,13 @@ static void art_item_free (ArtItem * item)
char * unixname = uri_to_filename (item->art_file);
if (unixname)
{
- unlink (unixname);
- free (unixname);
+ g_unlink (unixname);
+ str_unref (unixname);
}
}
- free (item->data);
- free (item->art_file);
+ g_free (item->data);
+ str_unref (item->art_file);
g_slice_free (ArtItem, item);
}
@@ -140,17 +140,16 @@ static void request_callback (ScanRequest * request)
assert (item != NULL && ! item->flag);
scan_request_get_image_data (request, & item->data, & item->len);
- item->art_file = scan_request_get_image_file (request);
+ item->art_file = str_get (scan_request_get_image_file (request));
item->flag = FLAG_DONE;
if (! send_source)
send_source = g_idle_add (send_requests, NULL);
- pthread_cond_broadcast (& cond);
pthread_mutex_unlock (& mutex);
}
-static ArtItem * art_item_get (const char * file, bool_t blocking)
+static ArtItem * art_item_get (const char * file)
{
ArtItem * item = g_hash_table_lookup (art_items, file);
@@ -169,15 +168,7 @@ static ArtItem * art_item_get (const char * file, bool_t blocking)
scan_request (file, SCAN_IMAGE, NULL, request_callback);
}
- if (! blocking)
- return NULL;
-
- item->refcount ++;
-
- while (! item->flag)
- pthread_cond_wait (& cond, & mutex);
-
- return item;
+ return NULL;
}
static void art_item_unref (const char * file, ArtItem * item)
@@ -222,15 +213,14 @@ void art_cleanup (void)
art_items = NULL;
}
-void art_get_data_real (const char * file, const void * * data, int64_t * len,
- bool_t blocking)
+void art_request_data (const char * file, const void * * data, int64_t * len)
{
* data = NULL;
* len = 0;
pthread_mutex_lock (& mutex);
- ArtItem * item = art_item_get (file, blocking);
+ ArtItem * item = art_item_get (file);
if (! item)
goto UNLOCK;
@@ -250,12 +240,12 @@ UNLOCK:
pthread_mutex_unlock (& mutex);
}
-const char * art_get_file_real (const char * file, bool_t blocking)
+const char * art_request_file (const char * file)
{
const char * art_file = NULL;
pthread_mutex_lock (& mutex);
- ArtItem * item = art_item_get (file, blocking);
+ ArtItem * item = art_item_get (file);
if (! item)
goto UNLOCK;
@@ -267,7 +257,7 @@ const char * art_get_file_real (const char * file, bool_t blocking)
{
item->art_file = filename_to_uri (unixname);
item->is_temp = TRUE;
- free (unixname);
+ str_unref (unixname);
}
}
@@ -281,30 +271,6 @@ UNLOCK:
return art_file;
}
-void art_request_data (const char * file, const void * * data, int64_t * len)
-{
- return art_get_data_real (file, data, len, FALSE);
-}
-
-const char * art_request_file (const char * file)
-{
- return art_get_file_real (file, FALSE);
-}
-
-void art_get_data (const char * file, const void * * data, int64_t * len)
-{
- fprintf (stderr, "aud_art_get_data() is deprecated. Use "
- "aud_art_request_data() instead.\n");
- return art_get_data_real (file, data, len, TRUE);
-}
-
-const char * art_get_file (const char * file)
-{
- fprintf (stderr, "aud_art_get_file() is deprecated. Use "
- "aud_art_request_file() instead.\n");
- return art_get_file_real (file, TRUE);
-}
-
void art_unref (const char * file)
{
pthread_mutex_lock (& mutex);
diff --git a/src/audacious/chardet.c b/src/audacious/chardet.c
index 145ec3f..08fe97e 100644
--- a/src/audacious/chardet.c
+++ b/src/audacious/chardet.c
@@ -1,6 +1,6 @@
/*
* chardet.c
- * Copyright 2006-2010 Yoshiki Yazawa, Matti HÀmÀlÀinen, and John Lindgren
+ * Copyright 2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,196 +17,36 @@
* the use of this software.
*/
-#include <glib.h>
-#include <string.h>
#include <libaudcore/audstrings.h>
+#include <libaudcore/hook.h>
-#include "debug.h"
-#include "i18n.h"
#include "main.h"
#include "misc.h"
-#ifdef USE_CHARDET
-# include <libguess.h>
-#endif
-
-static char * cd_chardet_to_utf8 (const char * str, int len,
- int * arg_bytes_read, int * arg_bytes_written);
-
-static char * str_to_utf8_fallback (const char * str)
+static void chardet_update (void)
{
- char * out = g_strconcat (str, _(" (invalid UTF-8)"), NULL);
+ char * region = get_str (NULL, "chardet_detector");
+ char * fallbacks = get_str (NULL, "chardet_fallback");
- for (char * c = out; * c; c ++)
- {
- if (* c & 0x80)
- * c = '?';
- }
+ Index * list = str_list_to_index (fallbacks, ", ");
+ str_set_charsets (region[0] ? region : NULL, list);
- return out;
+ str_unref (region);
+ str_unref (fallbacks);
}
-static char * cd_str_to_utf8 (const char * str)
+void chardet_init (void)
{
- char *out_str;
-
- if (str == NULL)
- return NULL;
-
- /* Note: Currently, playlist calls this function repeatedly, even
- * if the string is already converted into utf-8.
- * chardet_to_utf8() would convert a valid utf-8 string into a
- * different utf-8 string, if fallback encodings were supplied and
- * the given string could be treated as a string in one of
- * fallback encodings. To avoid this, g_utf8_validate() had been
- * used at the top of evaluation.
- */
-
- /* Note 2: g_utf8_validate() has so called encapsulated utf-8
- * problem, thus chardet_to_utf8() took the place of that.
- */
-
- /* Note 3: As introducing madplug, the problem of conversion from
- * ISO-8859-1 to UTF-8 arose. This may be coped with g_convert()
- * located near the end of chardet_to_utf8(), but it requires utf8
- * validation guard where g_utf8_validate() was. New
- * dfa_validate_utf8() employs libguess' DFA engine to validate
- * utf-8 and can properly distinguish examples of encapsulated
- * utf-8. It is considered to be safe to use as a guard.
- */
-
- /* Already UTF-8? */
-#ifdef USE_CHARDET
- if (libguess_validate_utf8(str, strlen(str)))
- return g_strdup(str);
-#else
- if (g_utf8_validate(str, strlen(str), NULL))
- return g_strdup(str);
-#endif
+ chardet_update ();
- /* chardet encoding detector */
- if ((out_str = cd_chardet_to_utf8 (str, strlen (str), NULL, NULL)))
- return out_str;
-
- /* all else fails, we mask off character codes >= 128, replace with '?' */
- return str_to_utf8_fallback(str);
+ hook_associate ("set chardet_detector", (HookFunction) chardet_update, NULL);
+ hook_associate ("set chardet_fallback", (HookFunction) chardet_update, NULL);
}
-static char * cd_chardet_to_utf8 (const char * str, int len,
- int * arg_bytes_read, int * arg_bytes_write)
+void chardet_cleanup (void)
{
- char *ret = NULL;
- int * bytes_read, * bytes_write;
- int my_bytes_read, my_bytes_write;
-
- bytes_read = arg_bytes_read != NULL ? arg_bytes_read : &my_bytes_read;
- bytes_write = arg_bytes_write != NULL ? arg_bytes_write : &my_bytes_write;
-
- g_return_val_if_fail(str != NULL, NULL);
-
-#ifdef USE_CHARDET
- if (libguess_validate_utf8(str, len))
-#else
- if (g_utf8_validate(str, len, NULL))
-#endif
- {
- if (len < 0)
- len = strlen (str);
-
- ret = g_malloc (len + 1);
- memcpy (ret, str, len);
- ret[len] = 0;
-
- if (arg_bytes_read != NULL)
- * arg_bytes_read = len;
- if (arg_bytes_write != NULL)
- * arg_bytes_write = len;
-
- return ret;
- }
+ hook_dissociate ("set chardet_detector", (HookFunction) chardet_update);
+ hook_dissociate ("set chardet_fallback", (HookFunction) chardet_update);
-#ifdef USE_CHARDET
- char * det = get_string (NULL, "chardet_detector");
-
- if (det[0])
- {
- AUDDBG("guess encoding (%s) %s\n", det, str);
- const char * encoding = libguess_determine_encoding (str, len, det);
- AUDDBG("encoding = %s\n", encoding);
- if (encoding)
- {
- gsize read_gsize = 0, written_gsize = 0;
- ret = g_convert (str, len, "UTF-8", encoding, & read_gsize, & written_gsize, NULL);
- * bytes_read = read_gsize;
- * bytes_write = written_gsize;
- }
- }
-
- g_free (det);
-#endif
-
- /* If detection failed or was not enabled, try fallbacks (if there are any) */
- if (! ret)
- {
- char * fallbacks = get_string (NULL, "chardet_fallback");
- char * * split = g_strsplit_set (fallbacks, " ,:;|/", -1);
-
- for (char * * enc = split; * enc; enc ++)
- {
- gsize read_gsize = 0, written_gsize = 0;
- ret = g_convert (str, len, "UTF-8", * enc, & read_gsize, & written_gsize, NULL);
- * bytes_read = read_gsize;
- * bytes_write = written_gsize;
-
- if (len == *bytes_read)
- break;
- else {
- g_free(ret);
- ret = NULL;
- }
- }
-
- g_strfreev (split);
- g_free (fallbacks);
- }
-
- /* First fallback: locale (duh!) */
- if (ret == NULL)
- {
- gsize read_gsize = 0, written_gsize = 0;
- ret = g_locale_to_utf8 (str, len, & read_gsize, & written_gsize, NULL);
- * bytes_read = read_gsize;
- * bytes_write = written_gsize;
- }
-
- /* The final fallback is ISO-8859-1, if no other is specified or conversions fail */
- if (ret == NULL)
- {
- gsize read_gsize = 0, written_gsize = 0;
- ret = g_convert (str, len, "UTF-8", "ISO-8859-1", & read_gsize, & written_gsize, NULL);
- * bytes_read = read_gsize;
- * bytes_write = written_gsize;
- }
-
- if (ret != NULL)
- {
- if (g_utf8_validate(ret, -1, NULL))
- return ret;
- else
- {
- g_warning("g_utf8_validate() failed for converted string in cd_chardet_to_utf8: '%s'", ret);
- g_free(ret);
- return NULL;
- }
- }
-
- return NULL; /* If we have no idea, return NULL. */
-}
-
-void chardet_init (void)
-{
-#ifdef USE_CHARDET
- libguess_determine_encoding(NULL, -1, "");
-#endif
- str_set_utf8_impl (cd_str_to_utf8, cd_chardet_to_utf8);
+ str_set_charsets (NULL, NULL);
}
diff --git a/src/audacious/config.c b/src/audacious/config.c
index c40de94..0bc4966 100644
--- a/src/audacious/config.c
+++ b/src/audacious/config.c
@@ -1,6 +1,6 @@
/*
* config.c
- * Copyright 2011 John Lindgren
+ * Copyright 2011-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -18,12 +18,12 @@
*/
#include <glib.h>
-#include <pthread.h>
-#include <stdio.h>
#include <string.h>
#include <libaudcore/audstrings.h>
#include <libaudcore/hook.h>
+#include <libaudcore/inifile.h>
+#include <libaudcore/multihash.h>
#include "main.h"
#include "misc.h"
@@ -81,6 +81,7 @@ static const char * const core_defaults[] = {
"stop_after_current_song", "FALSE",
/* playlist */
+ "chardet_fallback", "ISO-8859-1",
#ifdef _WIN32
"convert_backslash", "TRUE",
#else
@@ -93,111 +94,270 @@ static const char * const core_defaults[] = {
NULL};
-static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-static GHashTable * defaults;
-static GKeyFile * keyfile;
-static bool_t modified;
+typedef enum {
+ OP_IS_DEFAULT,
+ OP_GET,
+ OP_SET,
+ OP_SET_NO_FLAG,
+ OP_CLEAR,
+ OP_CLEAR_NO_FLAG
+} OpType;
+
+typedef struct {
+ const char * section;
+ const char * key;
+ const char * value;
+} ConfigItem;
+
+typedef struct {
+ MultihashNode node;
+ ConfigItem item;
+} ConfigNode;
+
+typedef struct {
+ OpType type;
+ ConfigItem item;
+ unsigned hash;
+ bool_t result;
+} ConfigOp;
+
+typedef struct {
+ char * section;
+} LoadState;
+
+typedef struct {
+ GArray * list;
+} SaveState;
+
+static unsigned item_hash (const ConfigItem * item)
+{
+ return g_str_hash (item->section) + g_str_hash (item->key);
+}
-void config_load (void)
+/* assumes pooled strings */
+static int item_compare (const ConfigItem * a, const ConfigItem * b)
{
- g_return_if_fail (! defaults && ! keyfile);
- pthread_mutex_lock (& mutex);
+ if (str_equal (a->section, b->section))
+ return strcmp (a->key, b->key);
+ else
+ return strcmp (a->section, b->section);
+}
- defaults = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
- (GDestroyNotify) g_hash_table_destroy);
- keyfile = g_key_file_new ();
+/* assumes pooled strings */
+static void item_clear (ConfigItem * item)
+{
+ str_unref ((char *) item->section);
+ str_unref ((char *) item->key);
+ str_unref ((char *) item->value);
+}
- char * path = g_strdup_printf ("%s/config", get_path (AUD_PATH_USER_DIR));
- if (g_file_test (path, G_FILE_TEST_EXISTS))
- {
- GError * error = NULL;
- if (! g_key_file_load_from_file (keyfile, path, 0, & error))
- {
- fprintf (stderr, "Error loading config: %s\n", error->message);
- g_error_free (error);
- }
- }
- g_free (path);
+static unsigned config_node_hash (const MultihashNode * node0)
+{
+ const ConfigNode * node = (const ConfigNode *) node0;
- modified = FALSE;
- pthread_mutex_unlock (& mutex);
+ return item_hash (& node->item);
+}
- config_set_defaults (NULL, core_defaults);
+static bool_t config_node_match (const MultihashNode * node0, const void * data, unsigned hash)
+{
+ const ConfigNode * node = (const ConfigNode *) node0;
+ const ConfigItem * item = data;
+
+ return ! strcmp (node->item.section, item->section) && ! strcmp (node->item.key, item->key);
}
-void config_save (void)
+static MultihashTable defaults = {
+ .hash_func = config_node_hash,
+ .match_func = config_node_match
+};
+
+static MultihashTable config = {
+ .hash_func = config_node_hash,
+ .match_func = config_node_match
+};
+
+static volatile bool_t modified;
+
+static MultihashNode * add_cb (const void * data, unsigned hash, void * state)
{
- g_return_if_fail (defaults && keyfile);
- pthread_mutex_lock (& mutex);
+ ConfigOp * op = state;
- if (! modified)
+ switch (op->type)
{
- pthread_mutex_unlock (& mutex);
- return;
+ case OP_IS_DEFAULT:
+ op->result = ! op->item.value[0]; /* empty string is default */
+ return NULL;
+
+ case OP_SET:
+ op->result = TRUE;
+ modified = TRUE;
+
+ case OP_SET_NO_FLAG:;
+ ConfigNode * node = g_slice_new (ConfigNode);
+ node->item.section = str_get (op->item.section);
+ node->item.key = str_get (op->item.key);
+ node->item.value = str_get (op->item.value);
+ return (MultihashNode *) node;
+
+ default:
+ return NULL;
}
+}
- char * path = g_strdup_printf ("%s/config", get_path (AUD_PATH_USER_DIR));
- char * data = g_key_file_to_data (keyfile, NULL, NULL);
+static bool_t action_cb (MultihashNode * node0, void * state)
+{
+ ConfigNode * node = (ConfigNode *) node0;
+ ConfigOp * op = state;
- GError * error = NULL;
- if (! g_file_set_contents (path, data, -1, & error))
+ switch (op->type)
{
- fprintf (stderr, "Error saving config: %s\n", error->message);
- g_error_free (error);
- }
+ case OP_IS_DEFAULT:
+ op->result = ! strcmp (node->item.value, op->item.value);
+ return FALSE;
+
+ case OP_GET:
+ op->item.value = str_ref (node->item.value);
+ return FALSE;
+
+ case OP_SET:
+ op->result = !! strcmp (node->item.value, op->item.value);
+ if (op->result)
+ modified = TRUE;
+
+ case OP_SET_NO_FLAG:
+ str_unref ((char *) node->item.value);
+ node->item.value = str_get (op->item.value);
+ return FALSE;
+
+ case OP_CLEAR:
+ op->result = TRUE;
+ modified = TRUE;
- g_free (data);
- g_free (path);
+ case OP_CLEAR_NO_FLAG:
+ item_clear (& node->item);
+ g_slice_free (ConfigNode, node);
+ return TRUE;
- modified = FALSE;
- pthread_mutex_unlock (& mutex);
+ default:
+ return FALSE;
+ }
}
-void config_cleanup (void)
+static bool_t config_op_run (ConfigOp * op, OpType type, MultihashTable * table)
{
- g_return_if_fail (defaults && keyfile);
- pthread_mutex_lock (& mutex);
+ if (! op->hash)
+ op->hash = item_hash (& op->item);
+
+ op->type = type;
+ op->result = FALSE;
+ multihash_lookup (table, & op->item, op->hash, add_cb, action_cb, op);
+ return op->result;
+}
- g_key_file_free (keyfile);
- keyfile = NULL;
- g_hash_table_destroy (defaults);
- defaults = NULL;
+static void load_heading (const char * section, void * data)
+{
+ LoadState * state = data;
- pthread_mutex_unlock (& mutex);
+ str_unref (state->section);
+ state->section = str_get (section);
}
-void config_clear_section (const char * section)
+static void load_entry (const char * key, const char * value, void * data)
{
- g_return_if_fail (defaults && keyfile);
- pthread_mutex_lock (& mutex);
+ LoadState * state = data;
+ g_return_if_fail (state->section);
- if (! section)
- section = DEFAULT_SECTION;
+ ConfigOp op = {.item = {state->section, key, value}};
+ config_op_run (& op, OP_SET_NO_FLAG, & config);
+}
+
+void config_load (void)
+{
+ char * folder = filename_to_uri (get_path (AUD_PATH_USER_DIR));
+ SCONCAT2 (path, folder, "/config");
+ str_unref (folder);
- if (g_key_file_has_group (keyfile, section))
+ if (vfs_file_test (path, VFS_EXISTS))
{
- g_key_file_remove_group (keyfile, section, NULL);
- modified = TRUE;
+ VFSFile * file = vfs_fopen (path, "r");
+
+ if (file)
+ {
+ LoadState state = {0};
+
+ inifile_parse (file, load_heading, load_entry, & state);
+
+ str_unref (state.section);
+ vfs_fclose (file);
+ }
}
- pthread_mutex_unlock (& mutex);
+ config_set_defaults (NULL, core_defaults);
}
-void config_set_defaults (const char * section, const char * const * entries)
+static bool_t add_to_save_list (MultihashNode * node0, void * state0)
{
- g_return_if_fail (defaults && keyfile);
- pthread_mutex_lock (& mutex);
+ ConfigNode * node = (ConfigNode *) node0;
+ SaveState * state = state0;
- if (! section)
- section = DEFAULT_SECTION;
+ int pos = state->list->len;
+ g_array_set_size (state->list, pos + 1);
+
+ ConfigItem * copy = & g_array_index (state->list, ConfigItem, pos);
+ copy->section = str_ref (node->item.section);
+ copy->key = str_ref (node->item.key);
+ copy->value = str_ref (node->item.value);
+
+ modified = FALSE;
+
+ return FALSE;
+}
+
+void config_save (void)
+{
+ if (! modified)
+ return;
+
+ SaveState state = {.list = g_array_new (FALSE, FALSE, sizeof (ConfigItem))};
+
+ multihash_iterate (& config, add_to_save_list, & state);
+ g_array_sort (state.list, (GCompareFunc) item_compare);
- GHashTable * table = g_hash_table_lookup (defaults, section);
- if (! table)
+ char * folder = filename_to_uri (get_path (AUD_PATH_USER_DIR));
+ SCONCAT2 (path, folder, "/config");
+ str_unref (folder);
+
+ VFSFile * file = vfs_fopen (path, "w");
+
+ if (file)
{
- table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) str_unref);
- g_hash_table_replace (defaults, g_strdup (section), table);
+ const char * current_heading = NULL;
+
+ for (int i = 0; i < state.list->len; i ++)
+ {
+ ConfigItem * item = & g_array_index (state.list, ConfigItem, i);
+
+ if (! str_equal (item->section, current_heading))
+ {
+ inifile_write_heading (file, item->section);
+ current_heading = item->section;
+ }
+
+ inifile_write_entry (file, item->key, item->value);
+ }
+
+ vfs_fclose (file);
}
+ g_array_set_clear_func (state.list, (GDestroyNotify) item_clear);
+ g_array_free (state.list, TRUE);
+}
+
+void config_set_defaults (const char * section, const char * const * entries)
+{
+ if (! section)
+ section = DEFAULT_SECTION;
+
while (1)
{
const char * name = * entries ++;
@@ -205,130 +365,89 @@ void config_set_defaults (const char * section, const char * const * entries)
if (! name || ! value)
break;
- g_hash_table_replace (table, g_strdup (name), str_get (value));
+ ConfigOp op = {.item = {section, name, value}};
+ config_op_run (& op, OP_SET_NO_FLAG, & defaults);
}
-
- pthread_mutex_unlock (& mutex);
}
-static const char * get_default (const char * section, const char * name)
+void config_cleanup (void)
{
- GHashTable * table = g_hash_table_lookup (defaults, section);
- const char * def = table ? g_hash_table_lookup (table, name) : NULL;
- return def ? def : "";
+ ConfigOp op = {.type = OP_CLEAR_NO_FLAG};
+ multihash_iterate (& config, action_cb, & op);
+ multihash_iterate (& defaults, action_cb, & op);
}
-void set_string (const char * section, const char * name, const char * value)
+void set_str (const char * section, const char * name, const char * value)
{
- g_return_if_fail (defaults && keyfile);
g_return_if_fail (name && value);
- pthread_mutex_lock (& mutex);
-
- if (! section)
- section = DEFAULT_SECTION;
-
- const char * def = get_default (section, name);
- bool_t changed = FALSE;
-
- if (! strcmp (value, def))
- {
- if (g_key_file_has_key (keyfile, section, name, NULL))
- {
- g_key_file_remove_key (keyfile, section, name, NULL);
- changed = TRUE;
- }
- }
- else
- {
- char * old = g_key_file_has_key (keyfile, section, name, NULL) ?
- g_key_file_get_value (keyfile, section, name, NULL) : NULL;
- if (! old || strcmp (value, old))
- {
- g_key_file_set_value (keyfile, section, name, value);
- changed = TRUE;
- }
+ ConfigOp op = {.item = {section ? section : DEFAULT_SECTION, name, value}};
- g_free (old);
- }
+ bool_t is_default = config_op_run (& op, OP_IS_DEFAULT, & defaults);
+ bool_t changed = config_op_run (& op, is_default ? OP_CLEAR : OP_SET, & config);
- if (changed)
+ if (changed && ! section)
{
- modified = TRUE;
-
- if (! strcmp (section, DEFAULT_SECTION))
- {
- char * event = g_strdup_printf ("set %s", name);
- event_queue (event, NULL);
- g_free (event);
- }
+ SCONCAT2 (event, "set ", name);
+ event_queue (event, NULL);
}
-
- pthread_mutex_unlock (& mutex);
}
-char * get_string (const char * section, const char * name)
+char * get_str (const char * section, const char * name)
{
- g_return_val_if_fail (defaults && keyfile, g_strdup (""));
- g_return_val_if_fail (name, g_strdup (""));
- pthread_mutex_lock (& mutex);
+ g_return_val_if_fail (name, NULL);
- if (! section)
- section = DEFAULT_SECTION;
+ ConfigOp op = {.item = {section ? section : DEFAULT_SECTION, name, NULL}};
- char * value = g_key_file_has_key (keyfile, section, name, NULL) ?
- g_key_file_get_value (keyfile, section, name, NULL) : NULL;
+ config_op_run (& op, OP_GET, & config);
- if (! value)
- value = g_strdup (get_default (section, name));
+ if (! op.item.value)
+ config_op_run (& op, OP_GET, & defaults);
- pthread_mutex_unlock (& mutex);
- return value;
+ return op.item.value ? (char *) op.item.value : str_get ("");
}
void set_bool (const char * section, const char * name, bool_t value)
{
- set_string (section, name, value ? "TRUE" : "FALSE");
+ set_str (section, name, value ? "TRUE" : "FALSE");
}
bool_t get_bool (const char * section, const char * name)
{
- char * string = get_string (section, name);
+ char * string = get_str (section, name);
bool_t value = ! strcmp (string, "TRUE");
- g_free (string);
+ str_unref (string);
return value;
}
void set_int (const char * section, const char * name, int value)
{
- char * string = int_to_string (value);
+ char * string = int_to_str (value);
g_return_if_fail (string);
- set_string (section, name, string);
- g_free (string);
+ set_str (section, name, string);
+ str_unref (string);
}
int get_int (const char * section, const char * name)
{
- int value = 0;
- char * string = get_string (section, name);
- string_to_int (string, & value);
- g_free (string);
+ char * string = get_str (section, name);
+ int value = str_to_int (string);
+ str_unref (string);
return value;
}
void set_double (const char * section, const char * name, double value)
{
- char * string = double_to_string (value);
+ char * string = double_to_str (value);
g_return_if_fail (string);
- set_string (section, name, string);
- g_free (string);
+ set_str (section, name, string);
+ str_unref (string);
}
double get_double (const char * section, const char * name)
{
- double value = 0;
- char * string = get_string (section, name);
- string_to_double (string, & value);
- g_free (string);
+ char * string = get_str (section, name);
+ double value = str_to_double (string);
+ str_unref (string);
return value;
}
diff --git a/src/audacious/dbus-server.c b/src/audacious/dbus-server.c
new file mode 100644
index 0000000..af55f20
--- /dev/null
+++ b/src/audacious/dbus-server.c
@@ -0,0 +1,779 @@
+/*
+ * dbus-server.c
+ * Copyright 2013 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 <stdio.h>
+
+#include <libaudgui/libaudgui.h>
+
+#include "aud-dbus.h"
+#include "drct.h"
+#include "main.h"
+#include "misc.h"
+#include "playlist.h"
+#include "ui_preferences.h"
+
+typedef ObjAudacious Obj;
+typedef GDBusMethodInvocation Invoc;
+
+#define FINISH(name) \
+ obj_audacious_complete_##name (obj, invoc)
+
+#define FINISH2(name, ...) \
+ obj_audacious_complete_##name (obj, invoc, __VA_ARGS__)
+
+static Index * strv_to_index (const char * const * strv)
+{
+ Index * index = index_new ();
+ while (* strv)
+ index_insert (index, -1, str_get (* strv ++));
+
+ return index;
+}
+
+static bool_t do_add (Obj * obj, Invoc * invoc, const char * file)
+{
+ playlist_entry_insert (playlist_get_active (), -1, file, NULL, FALSE);
+ FINISH (add);
+ return TRUE;
+}
+
+static bool_t do_add_list (Obj * obj, Invoc * invoc, const char * const * filenames)
+{
+ playlist_entry_insert_batch (playlist_get_active (), -1,
+ strv_to_index (filenames), NULL, FALSE);
+ FINISH (add_list);
+ return TRUE;
+}
+
+static bool_t do_add_url (Obj * obj, Invoc * invoc, const char * url)
+{
+ playlist_entry_insert (playlist_get_active (), -1, url, NULL, FALSE);
+ FINISH (add_url);
+ return TRUE;
+}
+
+static bool_t do_advance (Obj * obj, Invoc * invoc)
+{
+ drct_pl_next ();
+ FINISH (advance);
+ return TRUE;
+}
+
+static bool_t do_auto_advance (Obj * obj, Invoc * invoc)
+{
+ FINISH2 (auto_advance, ! get_bool (NULL, "no_playlist_advance"));
+ return TRUE;
+}
+
+static bool_t do_balance (Obj * obj, Invoc * invoc)
+{
+ int balance;
+ drct_get_volume_balance (& balance);
+ FINISH2 (balance, balance);
+ return TRUE;
+}
+
+static bool_t do_clear (Obj * obj, Invoc * invoc)
+{
+ int playlist = playlist_get_active ();
+ playlist_entry_delete (playlist, 0, playlist_entry_count (playlist));
+ FINISH (clear);
+ return TRUE;
+}
+
+static bool_t do_delete (Obj * obj, Invoc * invoc, unsigned pos)
+{
+ playlist_entry_delete (playlist_get_active (), pos, 1);
+ FINISH (delete);
+ return TRUE;
+}
+
+static bool_t do_delete_active_playlist (Obj * obj, Invoc * invoc)
+{
+ playlist_delete (playlist_get_active ());
+ FINISH (delete_active_playlist);
+ return TRUE;
+}
+
+static bool_t do_eject (Obj * obj, Invoc * invoc)
+{
+ if (! headless_mode ())
+ audgui_run_filebrowser (TRUE);
+
+ FINISH (eject);
+ return TRUE;
+}
+
+static bool_t do_equalizer_activate (Obj * obj, Invoc * invoc, bool_t active)
+{
+ set_bool (NULL, "equalizer_active", active);
+ FINISH (equalizer_activate);
+ return TRUE;
+}
+
+static bool_t do_get_active_playlist (Obj * obj, Invoc * invoc)
+{
+ FINISH2 (get_active_playlist, playlist_get_active ());
+ return TRUE;
+}
+
+static bool_t do_get_active_playlist_name (Obj * obj, Invoc * invoc)
+{
+ char * title = playlist_get_title (playlist_get_active ());
+ FINISH2 (get_active_playlist_name, title ? title : "");
+ str_unref (title);
+ return TRUE;
+}
+
+static bool_t do_get_eq (Obj * obj, Invoc * invoc)
+{
+ double preamp = get_double (NULL, "equalizer_preamp");
+ double bands[AUD_EQUALIZER_NBANDS];
+ eq_get_bands (bands);
+
+ GVariant * var = g_variant_new_fixed_array (G_VARIANT_TYPE_DOUBLE, bands,
+ AUD_EQUALIZER_NBANDS, sizeof (double));
+ FINISH2 (get_eq, preamp, var);
+ return TRUE;
+}
+
+static bool_t do_get_eq_band (Obj * obj, Invoc * invoc, int band)
+{
+ FINISH2 (get_eq_band, eq_get_band (band));
+ return TRUE;
+}
+
+static bool_t do_get_eq_preamp (Obj * obj, Invoc * invoc)
+{
+ FINISH2 (get_eq_preamp, get_double (NULL, "equalizer_preamp"));
+ return TRUE;
+}
+
+static bool_t do_get_info (Obj * obj, Invoc * invoc)
+{
+ int bitrate, samplerate, channels;
+ drct_get_info (& bitrate, & samplerate, & channels);
+ FINISH2 (get_info, bitrate, samplerate, channels);
+ return TRUE;
+}
+
+static bool_t do_get_playqueue_length (Obj * obj, Invoc * invoc)
+{
+ FINISH2 (get_playqueue_length, playlist_queue_count (playlist_get_active ()));
+ return TRUE;
+}
+
+static bool_t do_get_tuple_fields (Obj * obj, Invoc * invoc)
+{
+ const char * fields[TUPLE_FIELDS + 1];
+
+ for (int i = 0; i < TUPLE_FIELDS; i ++)
+ fields[i] = tuple_field_get_name (i);
+
+ fields[TUPLE_FIELDS] = NULL;
+
+ FINISH2 (get_tuple_fields, fields);
+ return TRUE;
+}
+
+static bool_t do_info (Obj * obj, Invoc * invoc)
+{
+ int bitrate, samplerate, channels;
+ drct_get_info (& bitrate, & samplerate, & channels);
+ FINISH2 (info, bitrate, samplerate, channels);
+ return TRUE;
+}
+
+static bool_t do_jump (Obj * obj, Invoc * invoc, unsigned pos)
+{
+ playlist_set_position (playlist_get_active (), pos);
+ FINISH (jump);
+ return TRUE;
+}
+
+static bool_t do_length (Obj * obj, Invoc * invoc)
+{
+ FINISH2 (length, playlist_entry_count (playlist_get_active ()));
+ return TRUE;
+}
+
+static bool_t do_main_win_visible (Obj * obj, Invoc * invoc)
+{
+ FINISH2 (main_win_visible, ! headless_mode () && interface_is_shown ());
+ return TRUE;
+}
+
+static bool_t do_new_playlist (Obj * obj, Invoc * invoc)
+{
+ playlist_insert (-1);
+ playlist_set_active (playlist_count () - 1);
+ FINISH (new_playlist);
+ return TRUE;
+}
+
+static bool_t do_number_of_playlists (Obj * obj, Invoc * invoc)
+{
+ FINISH2 (number_of_playlists, playlist_count ());
+ return TRUE;
+}
+
+static bool_t do_open_list (Obj * obj, Invoc * invoc, const char * const * filenames)
+{
+ drct_pl_open_list (strv_to_index (filenames));
+ FINISH (open_list);
+ return TRUE;
+}
+
+static bool_t do_open_list_to_temp (Obj * obj, Invoc * invoc, const char * const * filenames)
+{
+ drct_pl_open_temp_list (strv_to_index (filenames));
+ FINISH (open_list_to_temp);
+ return TRUE;
+}
+
+static bool_t do_pause (Obj * obj, Invoc * invoc)
+{
+ drct_pause ();
+ FINISH (pause);
+ return TRUE;
+}
+
+static bool_t do_paused (Obj * obj, Invoc * invoc)
+{
+ FINISH2 (paused, drct_get_paused ());
+ return TRUE;
+}
+
+static bool_t do_play (Obj * obj, Invoc * invoc)
+{
+ drct_play ();
+ FINISH (play);
+ return TRUE;
+}
+
+static bool_t do_play_active_playlist (Obj * obj, Invoc * invoc)
+{
+ drct_play_playlist (playlist_get_active ());
+ FINISH (play_active_playlist);
+ return TRUE;
+}
+
+static bool_t do_play_pause (Obj * obj, Invoc * invoc)
+{
+ drct_play_pause ();
+ FINISH (play_pause);
+ return TRUE;
+}
+
+static bool_t do_playing (Obj * obj, Invoc * invoc)
+{
+ FINISH2 (playing, drct_get_playing ());
+ return TRUE;
+}
+
+static bool_t do_playlist_add (Obj * obj, Invoc * invoc, const char * list)
+{
+ playlist_entry_insert (playlist_get_active (), -1, list, NULL, FALSE);
+ FINISH (playlist_add);
+ return TRUE;
+}
+
+static bool_t do_playlist_enqueue_to_temp (Obj * obj, Invoc * invoc, const char * url)
+{
+ drct_pl_open_temp (url);
+ FINISH (playlist_enqueue_to_temp);
+ return TRUE;
+}
+
+static bool_t do_playlist_ins_url_string (Obj * obj, Invoc * invoc, const char * url, int pos)
+{
+ playlist_entry_insert (playlist_get_active (), pos, url, NULL, FALSE);
+ FINISH (playlist_ins_url_string);
+ return TRUE;
+}
+
+static bool_t do_playqueue_add (Obj * obj, Invoc * invoc, int pos)
+{
+ playlist_queue_insert (playlist_get_active (), -1, pos);
+ FINISH (playqueue_add);
+ return TRUE;
+}
+
+static bool_t do_playqueue_clear (Obj * obj, Invoc * invoc)
+{
+ int playlist = playlist_get_active ();
+ playlist_queue_delete (playlist, 0, playlist_queue_count (playlist));
+ FINISH (playqueue_clear);
+ return TRUE;
+}
+
+static bool_t do_playqueue_is_queued (Obj * obj, Invoc * invoc, int pos)
+{
+ bool_t queued = (playlist_queue_find_entry (playlist_get_active (), pos) >= 0);
+ FINISH2 (playqueue_is_queued, queued);
+ return TRUE;
+}
+
+static bool_t do_playqueue_remove (Obj * obj, Invoc * invoc, int pos)
+{
+ int playlist = playlist_get_active ();
+ int qpos = playlist_queue_find_entry (playlist, pos);
+
+ if (qpos >= 0)
+ playlist_queue_delete (playlist, qpos, 1);
+
+ FINISH (playqueue_remove);
+ return TRUE;
+}
+
+static bool_t do_position (Obj * obj, Invoc * invoc)
+{
+ FINISH2 (position, playlist_get_position (playlist_get_active ()));
+ return TRUE;
+}
+
+static bool_t do_queue_get_list_pos (Obj * obj, Invoc * invoc, unsigned qpos)
+{
+ FINISH2 (queue_get_list_pos, playlist_queue_get_entry (playlist_get_active (), qpos));
+ return TRUE;
+}
+
+static bool_t do_queue_get_queue_pos (Obj * obj, Invoc * invoc, unsigned pos)
+{
+ FINISH2 (queue_get_queue_pos, playlist_queue_find_entry (playlist_get_active (), pos));
+ return TRUE;
+}
+
+static bool_t do_quit (Obj * obj, Invoc * invoc)
+{
+ drct_quit ();
+ FINISH (quit);
+ return TRUE;
+}
+
+static bool_t do_repeat (Obj * obj, Invoc * invoc)
+{
+ FINISH2 (repeat, get_bool (NULL, "repeat"));
+ return TRUE;
+}
+
+static bool_t do_reverse (Obj * obj, Invoc * invoc)
+{
+ drct_pl_prev ();
+ FINISH (reverse);
+ return TRUE;
+}
+
+static bool_t do_seek (Obj * obj, Invoc * invoc, unsigned pos)
+{
+ drct_seek (pos);
+ FINISH (seek);
+ return TRUE;
+}
+
+static bool_t do_set_active_playlist (Obj * obj, Invoc * invoc, int playlist)
+{
+ playlist_set_active (playlist);
+ FINISH (set_active_playlist);
+ return TRUE;
+}
+
+static bool_t do_set_active_playlist_name (Obj * obj, Invoc * invoc, const char * title)
+{
+ playlist_set_title (playlist_get_active (), title);
+ FINISH (set_active_playlist_name);
+ return TRUE;
+}
+
+static bool_t do_set_eq (Obj * obj, Invoc * invoc, double preamp, GVariant * var)
+{
+ if (! g_variant_is_of_type (var, G_VARIANT_TYPE ("ad")))
+ return FALSE;
+
+ size_t nbands = 0;
+ const double * bands = g_variant_get_fixed_array (var, & nbands, sizeof (double));
+
+ if (nbands != AUD_EQUALIZER_NBANDS)
+ return FALSE;
+
+ set_double (NULL, "equalizer_preamp", preamp);
+ eq_set_bands (bands);
+ FINISH (set_eq);
+ return TRUE;
+}
+
+static bool_t do_set_eq_band (Obj * obj, Invoc * invoc, int band, double value)
+{
+ eq_set_band (band, value);
+ FINISH (set_eq_band);
+ return TRUE;
+}
+
+static bool_t do_set_eq_preamp (Obj * obj, Invoc * invoc, double preamp)
+{
+ set_double (NULL, "equalizer_preamp", preamp);
+ FINISH (set_eq_preamp);
+ return TRUE;
+}
+
+static bool_t do_set_volume (Obj * obj, Invoc * invoc, int vl, int vr)
+{
+ drct_set_volume (vl, vr);
+ FINISH (set_volume);
+ return TRUE;
+}
+
+static bool_t do_show_about_box (Obj * obj, Invoc * invoc, bool_t show)
+{
+ if (! headless_mode ())
+ {
+ if (show)
+ audgui_show_about_window ();
+ else
+ audgui_hide_about_window ();
+ }
+
+ FINISH (show_about_box);
+ return TRUE;
+}
+
+static bool_t do_show_filebrowser (Obj * obj, Invoc * invoc, bool_t show)
+{
+ if (! headless_mode ())
+ {
+ if (show)
+ audgui_run_filebrowser (FALSE);
+ else
+ audgui_hide_filebrowser ();
+ }
+
+ FINISH (show_filebrowser);
+ return TRUE;
+}
+
+static bool_t do_show_jtf_box (Obj * obj, Invoc * invoc, bool_t show)
+{
+ if (! headless_mode ())
+ {
+ if (show)
+ audgui_jump_to_track ();
+ else
+ audgui_jump_to_track_hide ();
+ }
+
+ FINISH (show_jtf_box);
+ return TRUE;
+}
+
+static bool_t do_show_main_win (Obj * obj, Invoc * invoc, bool_t show)
+{
+ if (! headless_mode ())
+ interface_show (show);
+
+ FINISH (show_main_win);
+ return TRUE;
+}
+
+static bool_t do_show_prefs_box (Obj * obj, Invoc * invoc, bool_t show)
+{
+ if (! headless_mode ())
+ {
+ if (show)
+ show_prefs_window ();
+ else
+ hide_prefs_window ();
+ }
+
+ FINISH (show_prefs_box);
+ return TRUE;
+}
+
+static bool_t do_shuffle (Obj * obj, Invoc * invoc)
+{
+ FINISH2 (shuffle, get_bool (NULL, "shuffle"));
+ return TRUE;
+}
+
+static bool_t do_song_filename (Obj * obj, Invoc * invoc, unsigned pos)
+{
+ char * filename = playlist_entry_get_filename (playlist_get_active (), pos);
+ FINISH2 (song_filename, filename ? filename : "");
+ str_unref (filename);
+ return TRUE;
+}
+
+static bool_t do_song_frames (Obj * obj, Invoc * invoc, unsigned pos)
+{
+ FINISH2 (song_frames, playlist_entry_get_length (playlist_get_active (), pos, FALSE));
+ return TRUE;
+}
+
+static bool_t do_song_length (Obj * obj, Invoc * invoc, unsigned pos)
+{
+ int length = playlist_entry_get_length (playlist_get_active (), pos, FALSE);
+ FINISH2 (song_length, length >= 0 ? length / 1000 : -1);
+ return TRUE;
+}
+
+static bool_t do_song_title (Obj * obj, Invoc * invoc, unsigned pos)
+{
+ char * title = playlist_entry_get_title (playlist_get_active (), pos, FALSE);
+ FINISH2 (song_title, title ? title : "");
+ str_unref (title);
+ return TRUE;
+}
+
+static bool_t do_song_tuple (Obj * obj, Invoc * invoc, unsigned pos, const char * key)
+{
+ int field = tuple_field_by_name (key);
+ Tuple * tuple = NULL;
+ GVariant * var = NULL;
+
+ if (field >= 0)
+ tuple = playlist_entry_get_tuple (playlist_get_active (), pos, FALSE);
+
+ if (tuple)
+ {
+ char * str;
+
+ switch (tuple_get_value_type (tuple, field))
+ {
+ case TUPLE_STRING:
+ str = tuple_get_str (tuple, field);
+ var = g_variant_new_string (str);
+ str_unref (str);
+ break;
+
+ case TUPLE_INT:
+ var = g_variant_new_int32 (tuple_get_int (tuple, field));
+ break;
+
+ default:
+ break;
+ }
+
+ tuple_unref (tuple);
+ }
+
+ if (! var)
+ var = g_variant_new_string ("");
+
+ FINISH2 (song_tuple, g_variant_new_variant (var));
+ return TRUE;
+}
+
+static bool_t do_status (Obj * obj, Invoc * invoc)
+{
+ const char * status = "stopped";
+ if (drct_get_playing ())
+ status = drct_get_paused () ? "paused" : "playing";
+
+ FINISH2 (status, status);
+ return TRUE;
+}
+
+static bool_t do_stop (Obj * obj, Invoc * invoc)
+{
+ drct_stop ();
+ FINISH (stop);
+ return TRUE;
+}
+
+static bool_t do_stop_after (Obj * obj, Invoc * invoc)
+{
+ FINISH2 (stop_after, get_bool (NULL, "stop_after_current_song"));
+ return TRUE;
+}
+
+static bool_t do_stopped (Obj * obj, Invoc * invoc)
+{
+ FINISH2 (stopped, ! drct_get_playing ());
+ return TRUE;
+}
+
+static bool_t do_time (Obj * obj, Invoc * invoc)
+{
+ FINISH2 (time, drct_get_time ());
+ return TRUE;
+}
+
+static bool_t do_toggle_auto_advance (Obj * obj, Invoc * invoc)
+{
+ set_bool (NULL, "no_playlist_advance", ! get_bool (NULL, "no_playlist_advance"));
+ FINISH (toggle_auto_advance);
+ return TRUE;
+}
+
+static bool_t do_toggle_repeat (Obj * obj, Invoc * invoc)
+{
+ set_bool (NULL, "repeat", ! get_bool (NULL, "repeat"));
+ FINISH (toggle_repeat);
+ return TRUE;
+}
+
+static bool_t do_toggle_shuffle (Obj * obj, Invoc * invoc)
+{
+ set_bool (NULL, "shuffle", ! get_bool (NULL, "shuffle"));
+ FINISH (toggle_shuffle);
+ return TRUE;
+}
+
+static bool_t do_toggle_stop_after (Obj * obj, Invoc * invoc)
+{
+ set_bool (NULL, "stop_after_current_song", ! get_bool (NULL, "stop_after_current_song"));
+ FINISH (toggle_stop_after);
+ return TRUE;
+}
+
+static bool_t do_version (Obj * obj, Invoc * invoc)
+{
+ FINISH2 (version, VERSION);
+ return TRUE;
+}
+
+static bool_t do_volume (Obj * obj, Invoc * invoc)
+{
+ int left, right;
+ drct_get_volume (& left, & right);
+ FINISH2 (volume, left, right);
+ return TRUE;
+}
+
+static const struct
+{
+ const char * signal;
+ GCallback callback;
+}
+handlers[] =
+{
+ {"handle-add", (GCallback) do_add},
+ {"handle-add-list", (GCallback) do_add_list},
+ {"handle-add-url", (GCallback) do_add_url},
+ {"handle-advance", (GCallback) do_advance},
+ {"handle-auto-advance", (GCallback) do_auto_advance},
+ {"handle-balance", (GCallback) do_balance},
+ {"handle-clear", (GCallback) do_clear},
+ {"handle-delete", (GCallback) do_delete},
+ {"handle-delete-active-playlist", (GCallback) do_delete_active_playlist},
+ {"handle-eject", (GCallback) do_eject},
+ {"handle-equalizer-activate", (GCallback) do_equalizer_activate},
+ {"handle-get-active-playlist", (GCallback) do_get_active_playlist},
+ {"handle-get-active-playlist-name", (GCallback) do_get_active_playlist_name},
+ {"handle-get-eq", (GCallback) do_get_eq},
+ {"handle-get-eq-band", (GCallback) do_get_eq_band},
+ {"handle-get-eq-preamp", (GCallback) do_get_eq_preamp},
+ {"handle-get-info", (GCallback) do_get_info},
+ {"handle-get-playqueue-length", (GCallback) do_get_playqueue_length},
+ {"handle-get-tuple-fields", (GCallback) do_get_tuple_fields},
+ {"handle-info", (GCallback) do_info},
+ {"handle-jump", (GCallback) do_jump},
+ {"handle-length", (GCallback) do_length},
+ {"handle-main-win-visible", (GCallback) do_main_win_visible},
+ {"handle-new-playlist", (GCallback) do_new_playlist},
+ {"handle-number-of-playlists", (GCallback) do_number_of_playlists},
+ {"handle-open-list", (GCallback) do_open_list},
+ {"handle-open-list-to-temp", (GCallback) do_open_list_to_temp},
+ {"handle-pause", (GCallback) do_pause},
+ {"handle-paused", (GCallback) do_paused},
+ {"handle-play", (GCallback) do_play},
+ {"handle-play-active-playlist", (GCallback) do_play_active_playlist},
+ {"handle-play-pause", (GCallback) do_play_pause},
+ {"handle-playing", (GCallback) do_playing},
+ {"handle-playlist-add", (GCallback) do_playlist_add},
+ {"handle-playlist-enqueue-to-temp", (GCallback) do_playlist_enqueue_to_temp},
+ {"handle-playlist-ins-url-string", (GCallback) do_playlist_ins_url_string},
+ {"handle-playqueue-add", (GCallback) do_playqueue_add},
+ {"handle-playqueue-clear", (GCallback) do_playqueue_clear},
+ {"handle-playqueue-is-queued", (GCallback) do_playqueue_is_queued},
+ {"handle-playqueue-remove", (GCallback) do_playqueue_remove},
+ {"handle-position", (GCallback) do_position},
+ {"handle-queue-get-list-pos", (GCallback) do_queue_get_list_pos},
+ {"handle-queue-get-queue-pos", (GCallback) do_queue_get_queue_pos},
+ {"handle-quit", (GCallback) do_quit},
+ {"handle-repeat", (GCallback) do_repeat},
+ {"handle-reverse", (GCallback) do_reverse},
+ {"handle-seek", (GCallback) do_seek},
+ {"handle-set-active-playlist", (GCallback) do_set_active_playlist},
+ {"handle-set-active-playlist-name", (GCallback) do_set_active_playlist_name},
+ {"handle-set-eq", (GCallback) do_set_eq},
+ {"handle-set-eq-band", (GCallback) do_set_eq_band},
+ {"handle-set-eq-preamp", (GCallback) do_set_eq_preamp},
+ {"handle-set-volume", (GCallback) do_set_volume},
+ {"handle-show-about-box", (GCallback) do_show_about_box},
+ {"handle-show-filebrowser", (GCallback) do_show_filebrowser},
+ {"handle-show-jtf-box", (GCallback) do_show_jtf_box},
+ {"handle-show-main-win", (GCallback) do_show_main_win},
+ {"handle-show-prefs-box", (GCallback) do_show_prefs_box},
+ {"handle-shuffle", (GCallback) do_shuffle},
+ {"handle-song-filename", (GCallback) do_song_filename},
+ {"handle-song-frames", (GCallback) do_song_frames},
+ {"handle-song-length", (GCallback) do_song_length},
+ {"handle-song-title", (GCallback) do_song_title},
+ {"handle-song-tuple", (GCallback) do_song_tuple},
+ {"handle-status", (GCallback) do_status},
+ {"handle-stop", (GCallback) do_stop},
+ {"handle-stop-after", (GCallback) do_stop_after},
+ {"handle-stopped", (GCallback) do_stopped},
+ {"handle-time", (GCallback) do_time},
+ {"handle-toggle-auto-advance", (GCallback) do_toggle_auto_advance},
+ {"handle-toggle-repeat", (GCallback) do_toggle_repeat},
+ {"handle-toggle-shuffle", (GCallback) do_toggle_shuffle},
+ {"handle-toggle-stop-after", (GCallback) do_toggle_stop_after},
+ {"handle-version", (GCallback) do_version},
+ {"handle-volume", (GCallback) do_volume}
+};
+
+static GDBusInterfaceSkeleton * skeleton = NULL;
+
+void dbus_server_init (void)
+{
+ GError * error = NULL;
+ GDBusConnection * bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, & error);
+
+ if (! bus)
+ goto ERROR;
+
+ g_bus_own_name_on_connection (bus, "org.atheme.audacious", 0, NULL, NULL, NULL, NULL);
+
+ skeleton = (GDBusInterfaceSkeleton *) obj_audacious_skeleton_new ();
+
+ for (int i = 0; i < ARRAY_LEN (handlers); i ++)
+ g_signal_connect (skeleton, handlers[i].signal, handlers[i].callback, NULL);
+
+ if (! g_dbus_interface_skeleton_export (skeleton, bus, "/org/atheme/audacious", & error))
+ goto ERROR;
+
+ return;
+
+ERROR:
+ if (error)
+ {
+ fprintf (stderr, "D-Bus error: %s\n", error->message);
+ g_error_free (error);
+ }
+}
+
+void dbus_server_cleanup (void)
+{
+ if (skeleton)
+ {
+ g_object_unref (skeleton);
+ skeleton = NULL;
+ }
+}
diff --git a/src/audacious/dbus-service.h b/src/audacious/dbus-service.h
deleted file mode 100644
index 8d773fa..0000000
--- a/src/audacious/dbus-service.h
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * dbus-service.h
- * Copyright 2007-2011 Ben Tucker, Yoshiki Yazawa, Matti HÀmÀlÀinen,
- * John Lindgren, and William Pitcock
- *
- * 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 AUDACIOUS_DBUS_SERVICE_H
-#define AUDACIOUS_DBUS_SERVICE_H
-
-#include <glib.h>
-#include <libaudcore/core.h>
-
-#define DBUS_API_SUBJECT_TO_CHANGE
-#include <dbus/dbus-glib.h>
-
-typedef struct {
- GObject parent;
- DBusGProxy *proxy;
-} RemoteObject, MprisRoot, MprisPlayer, MprisTrackList;
-
-typedef struct {
- GObjectClass parent_class;
-} RemoteObjectClass, MprisRootClass, MprisPlayerClass, MprisTrackListClass;
-
-/* MPRIS API */
-// Capabilities
-enum {
- MPRIS_CAPS_NONE = 0,
- MPRIS_CAPS_CAN_GO_NEXT = 1 << 0,
- MPRIS_CAPS_CAN_GO_PREV = 1 << 1,
- MPRIS_CAPS_CAN_PAUSE = 1 << 2,
- MPRIS_CAPS_CAN_PLAY = 1 << 3,
- MPRIS_CAPS_CAN_SEEK = 1 << 4,
- MPRIS_CAPS_CAN_PROVIDE_METADATA = 1 << 5,
- MPRIS_CAPS_PROVIDES_TIMING = 1 << 6,
-};
-
-// Status
-typedef enum {
- MPRIS_STATUS_INVALID = -1,
- MPRIS_STATUS_PLAY = 0,
- MPRIS_STATUS_PAUSE = 1,
- MPRIS_STATUS_STOP = 2,
-} PlaybackStatus;
-
-extern MprisPlayer * mpris;
-
-// MPRIS /
-bool_t mpris_root_identity(MprisRoot *obj, char **identity,
- GError **error);
-bool_t mpris_root_quit(MprisPlayer *obj, GError **error);
-
-// MPRIS /Player
-bool_t mpris_player_next(MprisPlayer *obj, GError **error);
-bool_t mpris_player_prev(MprisPlayer *obj, GError **error);
-bool_t mpris_player_pause(MprisPlayer *obj, GError **error);
-bool_t mpris_player_stop(MprisPlayer *obj, GError **error);
-bool_t mpris_player_play(MprisPlayer *obj, GError **error);
-bool_t mpris_player_repeat(MprisPlayer *obj, bool_t rpt, GError **error);
-bool_t mpris_player_get_status(MprisPlayer *obj, GValueArray **status,
- GError **error);
-bool_t mpris_player_get_metadata(MprisPlayer *obj, GHashTable **metadata,
- GError **error);
-bool_t mpris_player_get_caps(MprisPlayer *obj, int *capabilities,
- GError **error);
-bool_t mpris_player_volume_set(MprisPlayer *obj, int vol, GError **error);
-bool_t mpris_player_volume_get(MprisPlayer *obj, int *vol,
- GError **error);
-bool_t mpris_player_position_set(MprisPlayer *obj, int pos, GError **error);
-bool_t mpris_player_position_get(MprisPlayer *obj, int *pos,
- GError **error);
-enum {
- TRACK_CHANGE_SIG,
- STATUS_CHANGE_SIG,
- CAPS_CHANGE_SIG,
- LAST_SIG
-};
-
-enum {
- TRACKLIST_CHANGE_SIG,
- LAST_TRACKLIST_SIG
-};
-
-bool_t mpris_emit_track_change(MprisPlayer *obj);
-bool_t mpris_emit_status_change(MprisPlayer *obj, PlaybackStatus status);
-bool_t mpris_emit_caps_change(MprisPlayer *obj);
-bool_t mpris_emit_tracklist_change(MprisTrackList *obj, int playlist);
-
-// MPRIS /TrackList
-bool_t mpris_tracklist_get_metadata(MprisTrackList *obj, int pos,
- GHashTable **metadata, GError **error);
-bool_t mpris_tracklist_get_current_track(MprisTrackList *obj, int *pos,
- GError **error);
-bool_t mpris_tracklist_get_length(MprisTrackList *obj, int *length,
- GError **error);
-bool_t mpris_tracklist_add_track(MprisTrackList *obj, char *uri,
- bool_t play, GError **error);
-bool_t mpris_tracklist_del_track(MprisTrackList *obj, int pos,
- GError **error);
-bool_t mpris_tracklist_loop(MprisTrackList *obj, bool_t loop,
- GError **error);
-bool_t mpris_tracklist_random(MprisTrackList *obj, bool_t random,
- GError **error);
-
-/* Legacy API */
-// Audacious General Information
-bool_t audacious_rc_version(RemoteObject *obj, char **version, GError **error);
-bool_t audacious_rc_quit(RemoteObject *obj, GError **error);
-bool_t audacious_rc_eject(RemoteObject *obj, GError **error);
-bool_t audacious_rc_main_win_visible(RemoteObject *obj,
- bool_t *is_main_win, GError **error);
-bool_t audacious_rc_show_main_win(RemoteObject *obj, bool_t show,
- GError **error);
-bool_t audacious_rc_equalizer_visible(RemoteObject *obj, bool_t *is_eq_win,
- GError **error);
-bool_t audacious_rc_show_equalizer(RemoteObject *obj, bool_t show,
- GError **error);
-bool_t audacious_rc_playlist_visible(RemoteObject *obj,
- bool_t *is_pl_win,
- GError **error);
-bool_t audacious_rc_show_playlist(RemoteObject *obj, bool_t show,
- GError **error);
-bool_t audacious_rc_get_tuple_fields(RemoteObject *obj, char ***fields,
- GError **error);
-
-// Playback Information/Manipulation
-bool_t audacious_rc_play(RemoteObject *obj, GError **error);
-bool_t audacious_rc_pause(RemoteObject *obj, GError **error);
-bool_t audacious_rc_stop(RemoteObject *obj, GError **error);
-bool_t audacious_rc_playing(RemoteObject *obj, bool_t *is_playing,
- GError **error);
-bool_t audacious_rc_paused(RemoteObject *obj, bool_t *is_paused,
- GError **error);
-bool_t audacious_rc_stopped(RemoteObject *obj, bool_t *is_stopped,
- GError **error);
-bool_t audacious_rc_status(RemoteObject *obj, char **status,
- GError **error);
-bool_t audacious_rc_info(RemoteObject *obj, int *rate, int *freq,
- int *nch, GError **error);
-bool_t audacious_rc_time(RemoteObject *obj, int *time, GError **error);
-bool_t audacious_rc_seek(RemoteObject *obj, unsigned int pos, GError **error);
-bool_t audacious_rc_volume(RemoteObject *obj, int *vl, int *vr,
- GError **error);
-bool_t audacious_rc_set_volume(RemoteObject *obj, int vl, int vr,
- GError **error);
-bool_t audacious_rc_balance(RemoteObject *obj, int *balance,
- GError **error);
-
-// Playlist Information/Manipulation
-bool_t audacious_rc_position(RemoteObject *obj, int *pos, GError **error);
-bool_t audacious_rc_advance(RemoteObject *obj, GError **error);
-bool_t audacious_rc_reverse(RemoteObject *obj, GError **error);
-bool_t audacious_rc_length(RemoteObject *obj, int *length,
- GError **error);
-bool_t audacious_rc_song_title(RemoteObject *obj, unsigned int pos,
- char **title, GError **error);
-bool_t audacious_rc_song_filename(RemoteObject *obj, unsigned int pos,
- char **filename, GError **error);
-bool_t audacious_rc_song_length(RemoteObject *obj, unsigned int pos, int *length,
- GError **error);
-bool_t audacious_rc_song_frames(RemoteObject *obj, unsigned int pos, int *length,
- GError **error);
-bool_t audacious_rc_song_tuple(RemoteObject *obj, unsigned int pos, char *tuple,
- GValue *value, GError **error);
-bool_t audacious_rc_jump(RemoteObject *obj, unsigned int pos, GError **error);
-bool_t audacious_rc_add(RemoteObject *obj, char *file, GError **error);
-bool_t audacious_rc_add_url(RemoteObject *obj, char *url,
- GError **error);
-bool_t audacious_rc_add_list (RemoteObject * obj, char * * filenames,
- GError * * error);
-bool_t audacious_rc_open_list (RemoteObject * obj, char * * filenames,
- GError * * error);
-bool_t audacious_rc_open_list_to_temp (RemoteObject * obj, char * *
- filenames, GError * * error);
-bool_t audacious_rc_delete(RemoteObject *obj, unsigned int pos, GError **error);
-bool_t audacious_rc_clear(RemoteObject *obj, GError **error);
-bool_t audacious_rc_auto_advance(RemoteObject *obj, bool_t *is_advance,
- GError **error);
-bool_t audacious_rc_toggle_auto_advance(RemoteObject *obj, GError **error);
-bool_t audacious_rc_repeat(RemoteObject *obj, bool_t *is_repeat,
- GError **error);
-bool_t audacious_rc_toggle_repeat(RemoteObject *obj, GError **error);
-bool_t audacious_rc_shuffle(RemoteObject *obj, bool_t *is_shuffle,
- GError **error);
-bool_t audacious_rc_toggle_shuffle(RemoteObject *obj, GError **error);
-
-/* new */
-bool_t audacious_rc_show_prefs_box(RemoteObject *obj, bool_t show, GError **error);
-bool_t audacious_rc_show_about_box(RemoteObject *obj, bool_t show, GError **error);
-bool_t audacious_rc_show_jtf_box(RemoteObject *obj, bool_t show, GError **error);
-bool_t audacious_rc_show_filebrowser(RemoteObject *obj, bool_t show, GError **error); //new Nov 8
-bool_t audacious_rc_play_pause(RemoteObject *obj, GError **error);
-bool_t audacious_rc_activate(RemoteObject *obj, GError **error);
-bool_t audacious_rc_queue_get_list_pos(RemoteObject *obj, int qpos, int *pos, GError **error);
-bool_t audacious_rc_queue_get_queue_pos(RemoteObject *obj, int pos, int *qpos, GError **error);
-bool_t audacious_rc_get_info(RemoteObject *obj, int *rate, int *freq, int *nch, GError **error);
-bool_t audacious_rc_toggle_aot(RemoteObject *obj, bool_t ontop, GError **error);
-bool_t audacious_rc_get_playqueue_length(RemoteObject *obj, int *length, GError **error);
-bool_t audacious_rc_playqueue_add(RemoteObject *obj, int pos, GError **error);
-bool_t audacious_rc_playqueue_remove(RemoteObject *obj, int pos, GError **error);
-bool_t audacious_rc_playqueue_clear(RemoteObject *obj, GError **error);
-bool_t audacious_rc_playqueue_is_queued(RemoteObject *obj, int pos, bool_t *is_queued, GError **error);
-bool_t audacious_rc_playlist_ins_url_string(RemoteObject *obj, char *url, int pos, GError **error);
-bool_t audacious_rc_playlist_enqueue_to_temp(RemoteObject *obj, char *url, GError **error);
-bool_t audacious_rc_playlist_add(RemoteObject *obj, void * list, GError **error);
-
-/* new on nov 7 */
-bool_t audacious_rc_get_eq(RemoteObject *obj, double *preamp, GArray **bands, GError **error);
-bool_t audacious_rc_get_eq_preamp(RemoteObject *obj, double *preamp, GError **error);
-bool_t audacious_rc_get_eq_band(RemoteObject *obj, int band, double *value, GError **error);
-bool_t audacious_rc_set_eq(RemoteObject *obj, double preamp, GArray *bands, GError **error);
-bool_t audacious_rc_set_eq_preamp(RemoteObject *obj, double preamp, GError **error);
-bool_t audacious_rc_set_eq_band(RemoteObject *obj, int band, double value, GError **error);
-bool_t audacious_rc_equalizer_activate(RemoteObject *obj, bool_t active, GError **error);
-
-/* new in 2.4 */
-bool_t audacious_rc_get_active_playlist_name(RemoteObject *obj, char **title, GError **error);
-
-/* new in 3.1 */
-bool_t audacious_rc_stop_after (RemoteObject * obj, bool_t * is_stopping, GError * * error);
-bool_t audacious_rc_toggle_stop_after (RemoteObject * obj, GError * * error);
-
-#endif /* AUDACIOUS_DBUS_SERVICE_H */
diff --git a/src/audacious/dbus.c b/src/audacious/dbus.c
deleted file mode 100644
index 950c4cc..0000000
--- a/src/audacious/dbus.c
+++ /dev/null
@@ -1,1049 +0,0 @@
-/*
- * dbus.c
- * Copyright 2007-2011 Ben Tucker, Yoshiki Yazawa, William Pitcock,
- * Matti HÀmÀlÀinen, Andrew Shadoura, John Lindgren, and
- * Tomasz MoƄ
- *
- * 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 <glib.h>
-#include <string.h>
-
-#include <dbus/dbus.h>
-#include <dbus/dbus-glib.h>
-#include <dbus/dbus-glib-bindings.h>
-#include <dbus/dbus-glib-lowlevel.h>
-#include "dbus.h"
-#include "dbus-service.h"
-#include "dbus-server-bindings.h"
-
-#include <math.h>
-
-#include <libaudcore/hook.h>
-
-#include "debug.h"
-#include "drct.h"
-#include "playlist.h"
-#include "interface.h"
-#include "misc.h"
-
-static DBusGConnection *dbus_conn = NULL;
-static unsigned int signals[LAST_SIG] = { 0 };
-static unsigned int tracklist_signals[LAST_TRACKLIST_SIG] = { 0 };
-
-MprisPlayer * mpris = NULL;
-MprisTrackList * mpris_tracklist = NULL;
-
-G_DEFINE_TYPE (RemoteObject, audacious_rc, G_TYPE_OBJECT)
-G_DEFINE_TYPE (MprisRoot, mpris_root, G_TYPE_OBJECT)
-G_DEFINE_TYPE (MprisPlayer, mpris_player, G_TYPE_OBJECT)
-G_DEFINE_TYPE (MprisTrackList, mpris_tracklist, G_TYPE_OBJECT)
-
-#define DBUS_TYPE_G_STRING_VALUE_HASHTABLE (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE))
-
-static void mpris_playlist_update_hook(void * unused, MprisTrackList *obj);
-
-void audacious_rc_class_init(RemoteObjectClass * klass)
-{
-}
-
-void mpris_root_class_init(MprisRootClass * klass)
-{
-}
-
-void mpris_player_class_init(MprisPlayerClass * klass)
-{
- signals[CAPS_CHANGE_SIG] = g_signal_new("caps_change", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
- signals[TRACK_CHANGE_SIG] =
- g_signal_new("track_change",
- G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, DBUS_TYPE_G_STRING_VALUE_HASHTABLE);
-
- GType status_type = dbus_g_type_get_struct ("GValueArray", G_TYPE_INT,
- G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INVALID);
- signals[STATUS_CHANGE_SIG] =
- g_signal_new ("status_change", G_OBJECT_CLASS_TYPE (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL,
- g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, status_type);
-}
-
-void mpris_tracklist_class_init(MprisTrackListClass * klass)
-{
- tracklist_signals[TRACKLIST_CHANGE_SIG] = g_signal_new("track_list_change", G_OBJECT_CLASS_TYPE(klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
-}
-
-void audacious_rc_init(RemoteObject * object)
-{
- GError *error = NULL;
- DBusGProxy *driver_proxy;
- unsigned int request_ret;
-
- AUDDBG ("Registering remote D-Bus interfaces.\n");
-
- dbus_g_object_type_install_info(audacious_rc_get_type(), &dbus_glib_audacious_rc_object_info);
-
- // Register DBUS path
- dbus_g_connection_register_g_object(dbus_conn, AUDACIOUS_DBUS_PATH, G_OBJECT(object));
-
- // Register the service name, the constants here are defined in
- // dbus-glib-bindings.h
- driver_proxy = dbus_g_proxy_new_for_name(dbus_conn, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
-
- if (!org_freedesktop_DBus_request_name(driver_proxy, AUDACIOUS_DBUS_SERVICE, 0, &request_ret, &error))
- {
- g_warning("Unable to register service: %s", error->message);
- g_error_free(error);
- }
-
- if (!org_freedesktop_DBus_request_name(driver_proxy, AUDACIOUS_DBUS_SERVICE_MPRIS, 0, &request_ret, &error))
- {
- g_warning("Unable to register service: %s", error->message);
- g_error_free(error);
- }
-
- g_object_unref(driver_proxy);
-}
-
-void mpris_root_init(MprisRoot * object)
-{
- dbus_g_object_type_install_info(mpris_root_get_type(), &dbus_glib_mpris_root_object_info);
-
- // Register DBUS path
- dbus_g_connection_register_g_object(dbus_conn, AUDACIOUS_DBUS_PATH_MPRIS_ROOT, G_OBJECT(object));
-}
-
-void mpris_player_init(MprisPlayer * object)
-{
- dbus_g_object_type_install_info(mpris_player_get_type(), &dbus_glib_mpris_player_object_info);
-
- // Register DBUS path
- dbus_g_connection_register_g_object(dbus_conn, AUDACIOUS_DBUS_PATH_MPRIS_PLAYER, G_OBJECT(object));
-
- // Add signals
- DBusGProxy *proxy = object->proxy;
- if (proxy != NULL)
- {
- dbus_g_proxy_add_signal (proxy, "StatusChange", dbus_g_type_get_struct
- ("GValueArray", G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT,
- G_TYPE_INVALID), G_TYPE_INVALID);
- dbus_g_proxy_add_signal (proxy, "CapsChange", G_TYPE_INT, G_TYPE_INVALID);
- dbus_g_proxy_add_signal(proxy, "TrackChange", DBUS_TYPE_G_STRING_VALUE_HASHTABLE, G_TYPE_INVALID);
- }
- else
- {
- /* XXX / FIXME: Why does this happen? -- ccr */
- AUDDBG ("object->proxy == NULL; not adding some signals.\n");
- }
-}
-
-void mpris_tracklist_init(MprisTrackList * object)
-{
- dbus_g_object_type_install_info(mpris_tracklist_get_type(), &dbus_glib_mpris_tracklist_object_info);
-
- // Register DBUS path
- dbus_g_connection_register_g_object(dbus_conn, AUDACIOUS_DBUS_PATH_MPRIS_TRACKLIST, G_OBJECT(object));
-
- // Add signals
- DBusGProxy *proxy = object->proxy;
- if (proxy != NULL)
- {
- dbus_g_proxy_add_signal(proxy, "TrackListChange", G_TYPE_INT, G_TYPE_INVALID);
- }
- else
- {
- /* XXX / FIXME: Why does this happen? -- ccr */
- AUDDBG ("object->proxy == NULL, not adding some signals.\n");
- }
-}
-
-void init_dbus()
-{
- GError *error = NULL;
- DBusConnection *local_conn;
-
- AUDDBG ("Trying to initialize D-Bus.\n");
- dbus_conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
- if (dbus_conn == NULL)
- {
- g_warning("Unable to connect to dbus: %s", error->message);
- g_error_free(error);
- return;
- }
-
- g_object_new(audacious_rc_get_type(), NULL);
- g_object_new(mpris_root_get_type(), NULL);
- mpris = g_object_new(mpris_player_get_type(), NULL);
- mpris_tracklist = g_object_new(mpris_tracklist_get_type(), NULL);
-
- local_conn = dbus_g_connection_get_connection(dbus_conn);
- dbus_connection_set_exit_on_disconnect(local_conn, FALSE);
-
- hook_associate ("playlist update",
- (HookFunction) mpris_playlist_update_hook, mpris_tracklist);
-}
-
-void cleanup_dbus (void)
-{
- hook_dissociate ("playlist update", (HookFunction) mpris_playlist_update_hook);
-}
-
-static GValue *tuple_value_to_gvalue(const Tuple * tuple, const char * key)
-{
- GValue *val;
- TupleValueType type = tuple_get_value_type (tuple, -1, key);
-
- if (type == TUPLE_STRING)
- {
- val = g_new0(GValue, 1);
- g_value_init(val, G_TYPE_STRING);
- char * str = tuple_get_str (tuple, -1, key);
- g_value_set_string (val, str);
- str_unref (str);
- return val;
- }
- else if (type == TUPLE_INT)
- {
- val = g_new0(GValue, 1);
- g_value_init(val, G_TYPE_INT);
- int x = tuple_get_int (tuple, -1, key);
- g_value_set_int (val, x);
- return val;
- }
- return NULL;
-}
-
-/**
- * Retrieves value named tuple_key and inserts it inside hash table.
- *
- * @param[in,out] md GHashTable to insert into
- * @param[in] tuple Tuple to read data from
- * @param[in] tuple_key Tuple field key
- * @param[in] key key used for inserting into hash table.
- */
-static void tuple_insert_to_hash_full(GHashTable * md, const Tuple * tuple,
- const char * tuple_key, const char *key)
-{
- GValue *value = tuple_value_to_gvalue(tuple, tuple_key);
- if (value != NULL)
- g_hash_table_insert (md, (void *) key, value);
-}
-
-static void tuple_insert_to_hash(GHashTable * md, const Tuple * tuple,
- const char *key)
-{
- tuple_insert_to_hash_full(md, tuple, key, key);
-}
-
-static void remove_metadata_value(void * value)
-{
- g_value_unset((GValue *) value);
- g_free((GValue *) value);
-}
-
-static GHashTable *make_mpris_metadata(const char * filename, const Tuple * tuple)
-{
- GHashTable *md = NULL;
- void * value;
-
- md = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, remove_metadata_value);
-
- value = g_malloc(sizeof(GValue));
- memset(value, 0, sizeof(GValue));
- g_value_init(value, G_TYPE_STRING);
- g_value_take_string(value, g_strdup(filename));
- g_hash_table_insert(md, "location", value);
-
- if (tuple != NULL)
- {
- tuple_insert_to_hash_full(md, tuple, "length", "mtime");
- tuple_insert_to_hash(md, tuple, "title");
- tuple_insert_to_hash(md, tuple, "artist");
- tuple_insert_to_hash(md, tuple, "album");
- tuple_insert_to_hash(md, tuple, "comment");
- tuple_insert_to_hash(md, tuple, "genre");
- tuple_insert_to_hash(md, tuple, "year");
- tuple_insert_to_hash(md, tuple, "codec");
- tuple_insert_to_hash(md, tuple, "quality");
- tuple_insert_to_hash_full(md, tuple, "track-number", "tracknumber");
- tuple_insert_to_hash_full(md, tuple, "bitrate", "audio-bitrate");
- }
-
- return md;
-}
-
-static GValue * get_field (int playlist, int entry, const char * field)
-{
- Tuple * tuple = playlist_entry_get_tuple (playlist, entry, FALSE);
- GValue * value = tuple ? tuple_value_to_gvalue (tuple, field) : NULL;
-
- if (tuple)
- tuple_unref (tuple);
-
- return value;
-}
-
-static GHashTable * get_mpris_metadata (int playlist, int entry)
-{
- char * filename = playlist_entry_get_filename (playlist, entry);
- Tuple * tuple = playlist_entry_get_tuple (playlist, entry, FALSE);
-
- GHashTable * metadata = NULL;
- if (filename && tuple)
- metadata = make_mpris_metadata (filename, tuple);
-
- str_unref (filename);
- if (tuple)
- tuple_unref (tuple);
-
- return metadata;
-}
-
-/* MPRIS API */
-// MPRIS /
-bool_t mpris_root_identity(MprisRoot * obj, char ** identity, GError ** error)
-{
- *identity = g_strdup_printf("Audacious %s", VERSION);
- return TRUE;
-}
-
-bool_t mpris_root_quit(MprisPlayer * obj, GError ** error)
-{
- event_queue("quit", NULL);
- return TRUE;
-}
-
-// MPRIS /Player
-
-bool_t mpris_player_next (MprisPlayer * obj, GError * * error)
-{
- drct_pl_next ();
- return TRUE;
-}
-
-bool_t mpris_player_prev (MprisPlayer * obj, GError * * error)
-{
- drct_pl_prev ();
- return TRUE;
-}
-
-bool_t mpris_player_pause (MprisPlayer * obj, GError * * error)
-{
- drct_pause ();
- return TRUE;
-}
-
-bool_t mpris_player_stop (MprisPlayer * obj, GError * * error)
-{
- drct_stop ();
- return TRUE;
-}
-
-bool_t mpris_player_play (MprisPlayer * obj, GError * * error)
-{
- drct_play ();
- return TRUE;
-}
-
-bool_t mpris_player_repeat(MprisPlayer * obj, bool_t rpt, GError ** error)
-{
- set_bool (NULL, "repeat", rpt);
- return TRUE;
-}
-
-static void append_int_value(GValueArray * ar, int tmp)
-{
- GValue value;
- memset(&value, 0, sizeof(value));
- g_value_init(&value, G_TYPE_INT);
- g_value_set_int(&value, tmp);
- g_value_array_append(ar, &value);
-}
-
-static int get_playback_status (void)
-{
- if (! drct_get_playing ())
- return MPRIS_STATUS_STOP;
-
- return drct_get_paused () ? MPRIS_STATUS_PAUSE : MPRIS_STATUS_PLAY;
-}
-
-bool_t mpris_player_get_status(MprisPlayer * obj, GValueArray * *status, GError * *error)
-{
- *status = g_value_array_new(4);
-
- append_int_value(*status, (int) get_playback_status());
- append_int_value (* status, get_bool (NULL, "shuffle"));
- append_int_value (* status, get_bool (NULL, "no_playlist_advance"));
- append_int_value (* status, get_bool (NULL, "repeat"));
- return TRUE;
-}
-
-bool_t mpris_player_get_metadata (MprisPlayer * obj, GHashTable * * metadata,
- GError * * error)
-{
- int playlist = playlist_get_playing ();
- int entry = (playlist >= 0) ? playlist_get_position (playlist) : -1;
-
- * metadata = (entry >= 0) ? get_mpris_metadata (playlist, entry) : NULL;
- if (! * metadata)
- * metadata = g_hash_table_new (g_str_hash, g_str_equal);
-
- return TRUE;
-}
-
-bool_t mpris_player_get_caps(MprisPlayer * obj, int * capabilities, GError ** error)
-{
- *capabilities = MPRIS_CAPS_CAN_GO_NEXT | MPRIS_CAPS_CAN_GO_PREV | MPRIS_CAPS_CAN_PAUSE | MPRIS_CAPS_CAN_PLAY | MPRIS_CAPS_CAN_SEEK | MPRIS_CAPS_CAN_PROVIDE_METADATA | MPRIS_CAPS_PROVIDES_TIMING;
- return TRUE;
-}
-
-bool_t mpris_player_volume_set(MprisPlayer * obj, int vol, GError ** error)
-{
- drct_set_volume_main (vol);
- return TRUE;
-}
-
-bool_t mpris_player_volume_get(MprisPlayer * obj, int * vol, GError ** error)
-{
- drct_get_volume_main (vol);
- return TRUE;
-}
-
-bool_t mpris_player_position_set (MprisPlayer * obj, int pos, GError * * error)
-{
- drct_seek (pos);
- return TRUE;
-}
-
-bool_t mpris_player_position_get (MprisPlayer * obj, int * pos, GError * * error)
-{
- * pos = drct_get_time ();
- return TRUE;
-}
-
-// MPRIS /Player signals
-bool_t mpris_emit_caps_change(MprisPlayer * obj)
-{
- g_signal_emit(obj, signals[CAPS_CHANGE_SIG], 0, 0);
- return TRUE;
-}
-
-bool_t mpris_emit_track_change(MprisPlayer * obj)
-{
- int playlist, entry;
- GHashTable *metadata;
-
- playlist = playlist_get_playing();
- entry = playlist_get_position(playlist);
- char * filename = playlist_entry_get_filename (playlist, entry);
- Tuple * tuple = playlist_entry_get_tuple (playlist, entry, FALSE);
-
- if (filename && tuple)
- {
- metadata = make_mpris_metadata (filename, tuple);
- g_signal_emit (obj, signals[TRACK_CHANGE_SIG], 0, metadata);
- g_hash_table_destroy (metadata);
- }
-
- str_unref (filename);
- if (tuple)
- tuple_unref (tuple);
-
- return (filename && tuple);
-}
-
-bool_t mpris_emit_status_change(MprisPlayer * obj, PlaybackStatus status)
-{
- GValueArray *ar = g_value_array_new(4);
-
- if (status == MPRIS_STATUS_INVALID)
- status = get_playback_status ();
-
- append_int_value(ar, (int) status);
- append_int_value (ar, get_bool (NULL, "shuffle"));
- append_int_value (ar, get_bool (NULL, "no_playlist_advance"));
- append_int_value (ar, get_bool (NULL, "repeat"));
-
- g_signal_emit(obj, signals[STATUS_CHANGE_SIG], 0, ar);
- g_value_array_free(ar);
- return TRUE;
-}
-
-// MPRIS /TrackList
-bool_t mpris_emit_tracklist_change(MprisTrackList * obj, int playlist)
-{
- g_signal_emit(obj, tracklist_signals[TRACKLIST_CHANGE_SIG], 0, playlist_entry_count(playlist));
- return TRUE;
-}
-
-static void mpris_playlist_update_hook(void * unused, MprisTrackList * obj)
-{
- int playlist = playlist_get_active();
-
- mpris_emit_tracklist_change(obj, playlist);
-}
-
-bool_t mpris_tracklist_get_metadata (MprisTrackList * obj, int pos,
- GHashTable * * metadata, GError * * error)
-{
- * metadata = get_mpris_metadata (playlist_get_active (), pos);
- if (! * metadata)
- * metadata = g_hash_table_new (g_str_hash, g_str_equal);
-
- return TRUE;
-}
-
-bool_t mpris_tracklist_get_current_track (MprisTrackList * obj, int * pos,
- GError * * error)
-{
- * pos = playlist_get_position (playlist_get_active ());
- return TRUE;
-}
-
-bool_t mpris_tracklist_get_length (MprisTrackList * obj, int * length, GError * * error)
-{
- * length = playlist_entry_count (playlist_get_active ());
- return TRUE;
-}
-
-bool_t mpris_tracklist_add_track (MprisTrackList * obj, char * uri, bool_t play,
- GError * * error)
-{
- playlist_entry_insert (playlist_get_active (), -1, uri, NULL, play);
- return TRUE;
-}
-
-bool_t mpris_tracklist_del_track (MprisTrackList * obj, int pos, GError * * error)
-{
- playlist_entry_delete (playlist_get_active (), pos, 1);
- return TRUE;
-}
-
-bool_t mpris_tracklist_loop (MprisTrackList * obj, bool_t loop, GError * *
- error)
-{
- set_bool (NULL, "repeat", loop);
- return TRUE;
-}
-
-bool_t mpris_tracklist_random (MprisTrackList * obj, bool_t random,
- GError * * error)
-{
- set_bool (NULL, "shuffle", random);
- return TRUE;
-}
-
-// Audacious General Information
-bool_t audacious_rc_version(RemoteObject * obj, char ** version, GError ** error)
-{
- *version = g_strdup(VERSION);
- return TRUE;
-}
-
-bool_t audacious_rc_quit(RemoteObject * obj, GError * *error)
-{
- event_queue("quit", NULL);
- return TRUE;
-}
-
-bool_t audacious_rc_eject(RemoteObject * obj, GError ** error)
-{
- interface_show_filebrowser (TRUE);
- return TRUE;
-}
-
-bool_t audacious_rc_main_win_visible (RemoteObject * obj,
- bool_t * visible, GError ** error)
-{
- * visible = interface_is_shown ();
- return TRUE;
-}
-
-bool_t audacious_rc_show_main_win (RemoteObject * obj, bool_t show,
- GError * * error)
-{
- interface_show (show);
- return TRUE;
-}
-
-bool_t audacious_rc_get_tuple_fields(RemoteObject * obj, char *** fields, GError ** error)
-{
- * fields = g_new (char *, TUPLE_FIELDS);
-
- for (int i = 0; i < TUPLE_FIELDS; i ++)
- (* fields)[i] = g_strdup (tuple_field_get_name (i));
-
- (* fields)[TUPLE_FIELDS] = NULL;
- return TRUE;
-}
-
-
-// Playback Information/Manipulation
-
-bool_t audacious_rc_play (RemoteObject * obj, GError * * error)
-{
- drct_play ();
- return TRUE;
-}
-
-bool_t audacious_rc_pause (RemoteObject * obj, GError * * error)
-{
- drct_pause ();
- return TRUE;
-}
-
-bool_t audacious_rc_stop (RemoteObject * obj, GError * * error)
-{
- drct_stop ();
- return TRUE;
-}
-
-bool_t audacious_rc_playing (RemoteObject * obj, bool_t * is_playing, GError * * error)
-{
- * is_playing = drct_get_playing ();
- return TRUE;
-}
-
-bool_t audacious_rc_paused (RemoteObject * obj, bool_t * is_paused, GError * * error)
-{
- * is_paused = drct_get_paused ();
- return TRUE;
-}
-
-bool_t audacious_rc_stopped (RemoteObject * obj, bool_t * is_stopped, GError * * error)
-{
- * is_stopped = ! drct_get_playing ();
- return TRUE;
-}
-
-bool_t audacious_rc_status (RemoteObject * obj, char * * status, GError * * error)
-{
- if (drct_get_playing ())
- * status = strdup (drct_get_paused () ? "paused" : "playing");
- else
- * status = strdup ("stopped");
-
- return TRUE;
-}
-
-bool_t audacious_rc_info (RemoteObject * obj, int * rate, int * freq, int * nch,
- GError * * error)
-{
- drct_get_info (rate, freq, nch);
- return TRUE;
-}
-
-bool_t audacious_rc_time (RemoteObject * obj, int * time, GError * * error)
-{
- * time = drct_get_time ();
- return TRUE;
-}
-
-bool_t audacious_rc_seek (RemoteObject * obj, unsigned int pos, GError * * error)
-{
- drct_seek (pos);
- return TRUE;
-}
-
-bool_t audacious_rc_volume(RemoteObject * obj, int * vl, int * vr, GError ** error)
-{
- drct_get_volume (vl, vr);
- return TRUE;
-}
-
-bool_t audacious_rc_set_volume(RemoteObject * obj, int vl, int vr, GError ** error)
-{
- drct_set_volume (vl, vr);
- return TRUE;
-}
-
-bool_t audacious_rc_balance(RemoteObject * obj, int * balance, GError ** error)
-{
- drct_get_volume_balance (balance);
- return TRUE;
-}
-
-// Playlist Information/Manipulation
-
-bool_t audacious_rc_position (RemoteObject * obj, int * pos, GError * * error)
-{
- * pos = playlist_get_position (playlist_get_active ());
- return TRUE;
-}
-
-bool_t audacious_rc_advance(RemoteObject * obj, GError * *error)
-{
- drct_pl_next ();
- return TRUE;
-}
-
-bool_t audacious_rc_reverse (RemoteObject * obj, GError * * error)
-{
- drct_pl_prev ();
- return TRUE;
-}
-
-bool_t audacious_rc_length (RemoteObject * obj, int * length, GError * * error)
-{
- * length = playlist_entry_count (playlist_get_active ());
- return TRUE;
-}
-
-bool_t audacious_rc_song_title (RemoteObject * obj, unsigned int pos, char * *
- title, GError * * error)
-{
- char * title2 = playlist_entry_get_title (playlist_get_active (), pos, FALSE);
- if (! title2)
- return FALSE;
-
- * title = strdup (title2);
- str_unref (title2);
- return TRUE;
-}
-
-bool_t audacious_rc_song_filename (RemoteObject * obj, unsigned int pos,
- char * * filename, GError * * error)
-{
- char * filename2 = playlist_entry_get_filename (playlist_get_active (), pos);
- if (! filename2)
- return FALSE;
-
- * filename = strdup (filename2);
- str_unref (filename2);
- return TRUE;
-}
-
-bool_t audacious_rc_song_length(RemoteObject * obj, unsigned int pos, int * length, GError * *error)
-{
- audacious_rc_song_frames(obj, pos, length, error);
- *length /= 1000;
- return TRUE;
-}
-
-bool_t audacious_rc_song_frames (RemoteObject * obj, unsigned int pos, int *
- length, GError * * error)
-{
- * length = playlist_entry_get_length (playlist_get_active (), pos, FALSE);
- return TRUE;
-}
-
-bool_t audacious_rc_song_tuple (RemoteObject * obj, unsigned int pos, char *
- field, GValue * value, GError * * error)
-{
- GValue * value2 = get_field (playlist_get_active (), pos, field);
- if (! value2)
- return FALSE;
-
- memset (value, 0, sizeof (GValue));
- g_value_init (value, G_VALUE_TYPE (value2));
- g_value_copy (value2, value);
- g_value_unset (value2);
- g_free (value2);
- return TRUE;
-}
-
-bool_t audacious_rc_jump (RemoteObject * obj, unsigned int pos, GError * * error)
-{
- playlist_set_position (playlist_get_active (), pos);
- return TRUE;
-}
-
-bool_t audacious_rc_add(RemoteObject * obj, char * file, GError * *error)
-{
- return audacious_rc_playlist_ins_url_string(obj, file, -1, error);
-}
-
-bool_t audacious_rc_add_url(RemoteObject * obj, char * file, GError * *error)
-{
- return audacious_rc_playlist_ins_url_string(obj, file, -1, error);
-}
-
-static Index * strings_to_index (char * * strings)
-{
- Index * index = index_new ();
-
- while (* strings)
- index_append (index, str_get (* strings ++));
-
- return index;
-}
-
-bool_t audacious_rc_add_list (RemoteObject * obj, char * * filenames,
- GError * * error)
-{
- drct_pl_add_list (strings_to_index (filenames), -1);
- return TRUE;
-}
-
-bool_t audacious_rc_open_list (RemoteObject * obj, char * * filenames,
- GError * * error)
-{
- drct_pl_open_list (strings_to_index (filenames));
- return TRUE;
-}
-
-bool_t audacious_rc_open_list_to_temp (RemoteObject * obj, char * *
- filenames, GError * * error)
-{
- drct_pl_open_temp_list (strings_to_index (filenames));
- return TRUE;
-}
-
-bool_t audacious_rc_delete (RemoteObject * obj, unsigned int pos, GError * * error)
-{
- playlist_entry_delete (playlist_get_active (), pos, 1);
- return TRUE;
-}
-
-bool_t audacious_rc_clear (RemoteObject * obj, GError * * error)
-{
- int playlist = playlist_get_active ();
- playlist_entry_delete (playlist, 0, playlist_entry_count (playlist));
- return TRUE;
-}
-
-bool_t audacious_rc_auto_advance(RemoteObject * obj, bool_t * is_advance, GError ** error)
-{
- * is_advance = ! get_bool (NULL, "no_playlist_advance");
- return TRUE;
-}
-
-bool_t audacious_rc_toggle_auto_advance(RemoteObject * obj, GError ** error)
-{
- set_bool (NULL, "no_playlist_advance", ! get_bool (NULL, "no_playlist_advance"));
- return TRUE;
-}
-
-bool_t audacious_rc_repeat(RemoteObject * obj, bool_t * is_repeating, GError ** error)
-{
- *is_repeating = get_bool (NULL, "repeat");
- return TRUE;
-}
-
-bool_t audacious_rc_toggle_repeat (RemoteObject * obj, GError * * error)
-{
- set_bool (NULL, "repeat", ! get_bool (NULL, "repeat"));
- return TRUE;
-}
-
-bool_t audacious_rc_shuffle(RemoteObject * obj, bool_t * is_shuffling, GError ** error)
-{
- *is_shuffling = get_bool (NULL, "shuffle");
- return TRUE;
-}
-
-bool_t audacious_rc_toggle_shuffle (RemoteObject * obj, GError * * error)
-{
- set_bool (NULL, "shuffle", ! get_bool (NULL, "shuffle"));
- return TRUE;
-}
-
-bool_t audacious_rc_stop_after (RemoteObject * obj, bool_t * is_stopping, GError * * error)
-{
- * is_stopping = get_bool (NULL, "stop_after_current_song");
- return TRUE;
-}
-
-bool_t audacious_rc_toggle_stop_after (RemoteObject * obj, GError * * error)
-{
- set_bool (NULL, "stop_after_current_song", ! get_bool (NULL, "stop_after_current_song"));
- return TRUE;
-}
-
-/* New on Oct 5 */
-bool_t audacious_rc_show_prefs_box(RemoteObject * obj, bool_t show, GError ** error)
-{
- event_queue("prefswin show", GINT_TO_POINTER(show));
- return TRUE;
-}
-
-bool_t audacious_rc_show_about_box(RemoteObject * obj, bool_t show, GError ** error)
-{
- event_queue("aboutwin show", GINT_TO_POINTER(show));
- return TRUE;
-}
-
-bool_t audacious_rc_show_jtf_box(RemoteObject * obj, bool_t show, GError ** error)
-{
- if (show)
- interface_show_jump_to_track ();
-
- return TRUE;
-}
-
-bool_t audacious_rc_show_filebrowser(RemoteObject * obj, bool_t show, GError ** error)
-{
- if (show)
- interface_show_filebrowser (FALSE);
-
- return TRUE;
-}
-
-bool_t audacious_rc_play_pause (RemoteObject * obj, GError * * error)
-{
- drct_play_pause ();
- return TRUE;
-}
-
-bool_t audacious_rc_get_info (RemoteObject * obj, int * rate, int * freq,
- int * nch, GError * * error)
-{
- drct_get_info (rate, freq, nch);
- return TRUE;
-}
-
-bool_t audacious_rc_toggle_aot(RemoteObject * obj, bool_t ontop, GError ** error)
-{
- hook_call("mainwin set always on top", &ontop);
- return TRUE;
-}
-
-bool_t audacious_rc_playqueue_add (RemoteObject * obj, int pos, GError * * error)
-{
- playlist_queue_insert (playlist_get_active (), -1, pos);
- return TRUE;
-}
-
-bool_t audacious_rc_playqueue_remove (RemoteObject * obj, int pos, GError * * error)
-{
- int playlist = playlist_get_active ();
- int at = playlist_queue_find_entry (playlist, pos);
-
- if (at >= 0)
- playlist_queue_delete (playlist, at, 1);
-
- return TRUE;
-}
-
-bool_t audacious_rc_playqueue_clear (RemoteObject * obj, GError * * error)
-{
- int playlist = playlist_get_active ();
- playlist_queue_delete (playlist, 0, playlist_queue_count (playlist));
- return TRUE;
-}
-
-bool_t audacious_rc_get_playqueue_length (RemoteObject * obj, int * length,
- GError * * error)
-{
- * length = playlist_queue_count (playlist_get_active ());
- return TRUE;
-}
-
-bool_t audacious_rc_queue_get_list_pos (RemoteObject * obj, int qpos, int * pos,
- GError * * error)
-{
- * pos = playlist_queue_get_entry (playlist_get_active (), qpos);
- return TRUE;
-}
-
-bool_t audacious_rc_queue_get_queue_pos (RemoteObject * obj, int pos, int *
- qpos, GError * * error)
-{
- * qpos = playlist_queue_find_entry (playlist_get_active (), pos);
- return TRUE;
-}
-
-bool_t audacious_rc_playqueue_is_queued (RemoteObject * obj, int pos, bool_t *
- is_queued, GError * * error)
-{
- * is_queued = (playlist_queue_find_entry (playlist_get_active (), pos) >= 0);
- return TRUE;
-}
-
-bool_t audacious_rc_playlist_ins_url_string (RemoteObject * obj, char * url, int
- pos, GError * * error)
-{
- playlist_entry_insert (playlist_get_active (), pos, url, NULL, FALSE);
- return TRUE;
-}
-
-bool_t audacious_rc_playlist_add(RemoteObject * obj, void *list, GError * *error)
-{
- return audacious_rc_playlist_ins_url_string(obj, list, -1, error);
-}
-
-bool_t audacious_rc_playlist_enqueue_to_temp (RemoteObject * obj, char * url,
- GError * * error)
-{
- drct_pl_open_temp (url);
- return TRUE;
-}
-
-/* New on Nov 7: Equalizer */
-bool_t audacious_rc_get_eq(RemoteObject * obj, double * preamp, GArray ** bands, GError ** error)
-{
- * preamp = get_double (NULL, "equalizer_preamp");
- * bands = g_array_new (FALSE, FALSE, sizeof (double));
- g_array_set_size (* bands, AUD_EQUALIZER_NBANDS);
- eq_get_bands ((double *) (* bands)->data);
-
- return TRUE;
-}
-
-bool_t audacious_rc_get_eq_preamp(RemoteObject * obj, double * preamp, GError ** error)
-{
- * preamp = get_double (NULL, "equalizer_preamp");
- return TRUE;
-}
-
-bool_t audacious_rc_get_eq_band(RemoteObject * obj, int band, double * value, GError ** error)
-{
- * value = eq_get_band (band);
- return TRUE;
-}
-
-bool_t audacious_rc_set_eq(RemoteObject * obj, double preamp, GArray * bands, GError ** error)
-{
- set_double (NULL, "equalizer_preamp", preamp);
- eq_set_bands ((double *) bands->data);
- return TRUE;
-}
-
-bool_t audacious_rc_set_eq_preamp(RemoteObject * obj, double preamp, GError ** error)
-{
- set_double (NULL, "equalizer_preamp", preamp);
- return TRUE;
-}
-
-bool_t audacious_rc_set_eq_band(RemoteObject * obj, int band, double value, GError ** error)
-{
- eq_set_band (band, value);
- return TRUE;
-}
-
-bool_t audacious_rc_equalizer_activate(RemoteObject * obj, bool_t active, GError ** error)
-{
- set_bool (NULL, "equalizer_active", active);
- return TRUE;
-}
-
-bool_t audacious_rc_get_active_playlist_name (RemoteObject * obj, char * *
- title, GError * * error)
-{
- char * title2 = playlist_get_title (playlist_get_active ());
- * title = strdup (title2);
- str_unref (title2);
- return TRUE;
-}
-
-DBusGProxy *audacious_get_dbus_proxy(void)
-{
- DBusGConnection *connection = NULL;
- GError *error = NULL;
- connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
- g_clear_error(&error);
- return dbus_g_proxy_new_for_name(connection, AUDACIOUS_DBUS_SERVICE, AUDACIOUS_DBUS_PATH, AUDACIOUS_DBUS_INTERFACE);
-}
diff --git a/src/audacious/dbus.h b/src/audacious/dbus.h
deleted file mode 100644
index 3a3300d..0000000
--- a/src/audacious/dbus.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * dbus.h
- * Copyright 2007 Ben Tucker
- *
- * 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 AUDACIOUS_DBUS_H
-#define AUDACIOUS_DBUS_H
-
-#include <dbus/dbus-glib.h>
-
-#define AUDACIOUS_DBUS_SERVICE "org.atheme.audacious"
-#define AUDACIOUS_DBUS_PATH "/org/atheme/audacious"
-#define AUDACIOUS_DBUS_INTERFACE "org.atheme.audacious"
-#define AUDACIOUS_DBUS_SERVICE_MPRIS "org.mpris.audacious"
-#define AUDACIOUS_DBUS_INTERFACE_MPRIS "org.freedesktop.MediaPlayer"
-#define AUDACIOUS_DBUS_PATH_MPRIS_ROOT "/"
-#define AUDACIOUS_DBUS_PATH_MPRIS_PLAYER "/Player"
-#define AUDACIOUS_DBUS_PATH_MPRIS_TRACKLIST "/TrackList"
-
-#define NONE = 0
-#define CAN_GO_NEXT = 1 << 0
-#define CAN_GO_PREV = 1 << 1
-#define CAN_PAUSE = 1 << 2
-#define CAN_PLAY = 1 << 3
-#define CAN_SEEK = 1 << 4
-#define CAN_RESTORE_CONTEXT = 1 << 5
-#define CAN_PROVIDE_METADATA = 1 << 6
-#define PROVIDES_TIMING = 1 << 7
-
-void init_dbus (void);
-void cleanup_dbus (void);
-DBusGProxy * audacious_get_dbus_proxy (void);
-
-#endif /* AUDACIOUS_DBUS_H */
diff --git a/src/audacious/debug.h b/src/audacious/debug.h
index b10fd6c..2b372ad 100644
--- a/src/audacious/debug.h
+++ b/src/audacious/debug.h
@@ -1,6 +1,6 @@
/*
* debug.h
- * Copyright 2010 John Lindgren
+ * Copyright 2010-2011 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/audacious/drct-api.h b/src/audacious/drct-api.h
index 1a259f0..cffaf4b 100644
--- a/src/audacious/drct-api.h
+++ b/src/audacious/drct-api.h
@@ -1,6 +1,6 @@
/*
* drct-api.h
- * Copyright 2010-2011 John Lindgren
+ * Copyright 2010-2012 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -31,6 +31,7 @@ AUD_VFUNC0 (drct_quit)
* and must be freed with str_unref(). */
AUD_VFUNC0 (drct_play)
+AUD_VFUNC0 (drct_play_pause)
AUD_VFUNC1 (drct_play_playlist, int, playlist)
AUD_VFUNC0 (drct_pause)
AUD_VFUNC0 (drct_stop)
@@ -44,6 +45,13 @@ AUD_FUNC0 (int, drct_get_time)
AUD_FUNC0 (int, drct_get_length)
AUD_VFUNC1 (drct_seek, int, time)
+/* "A-B repeat": when playback reaches point B, it returns to point A (where A
+ * and B are in milliseconds). The value -1 is interpreted as the beginning of
+ * the song (for A) or the end of the song (for B). A-B repeat is disabled
+ * entirely by setting both A and B to -1. */
+AUD_VFUNC2 (drct_set_ab_repeat, int, a, int, b)
+AUD_VFUNC2 (drct_get_ab_repeat, int *, a, int *, b)
+
/* --- VOLUME CONTROL --- */
AUD_VFUNC2 (drct_get_volume, int *, left, int *, right)
@@ -68,17 +76,3 @@ AUD_VFUNC1 (drct_pl_open, const char *, filename)
AUD_VFUNC1 (drct_pl_open_list, Index *, filenames)
AUD_VFUNC1 (drct_pl_open_temp, const char *, filename)
AUD_VFUNC1 (drct_pl_open_temp_list, Index *, filenames)
-
-/* deprecated; use playlist_delete_selected() */
-AUD_VFUNC1 (drct_pl_delete_selected, int, playlist)
-
-/* added in Audacious 3.4 */
-
-/* "A-B repeat": when playback reaches point B, it returns to point A (where A
- * and B are in milliseconds). The value -1 is interpreted as the beginning of
- * the song (for A) or the end of the song (for B). A-B repeat is disabled
- * entirely by setting both A and B to -1. */
-AUD_VFUNC2 (drct_set_ab_repeat, int, a, int, b)
-AUD_VFUNC2 (drct_get_ab_repeat, int *, a, int *, b)
-
-AUD_VFUNC0 (drct_play_pause)
diff --git a/src/audacious/drct.c b/src/audacious/drct.c
index 40e3151..5fbef8f 100644
--- a/src/audacious/drct.c
+++ b/src/audacious/drct.c
@@ -1,6 +1,6 @@
/*
* drct.c
- * Copyright 2009-2011 John Lindgren
+ * Copyright 2009-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,7 +17,6 @@
* the use of this software.
*/
-#include <glib.h>
#include <libaudcore/hook.h>
#include <libaudcore/vfs.h>
@@ -26,13 +25,6 @@
#include "misc.h"
#include "playlist.h"
-/* --- PROGRAM CONTROL --- */
-
-void drct_quit (void)
-{
- hook_call ("quit", NULL);
-}
-
/* --- PLAYBACK CONTROL --- */
void drct_play (void)
@@ -169,7 +161,7 @@ static void add_list (Index * filenames, int at, bool_t to_temp, bool_t play)
void drct_pl_add (const char * filename, int at)
{
Index * filenames = index_new ();
- index_append (filenames, str_get (filename));
+ index_insert (filenames, -1, str_get (filename));
add_list (filenames, at, FALSE, FALSE);
}
@@ -181,7 +173,7 @@ void drct_pl_add_list (Index * filenames, int at)
void drct_pl_open (const char * filename)
{
Index * filenames = index_new ();
- index_append (filenames, str_get (filename));
+ index_insert (filenames, -1, str_get (filename));
add_list (filenames, -1, get_bool (NULL, "open_to_temporary"), TRUE);
}
@@ -193,7 +185,7 @@ void drct_pl_open_list (Index * filenames)
void drct_pl_open_temp (const char * filename)
{
Index * filenames = index_new ();
- index_append (filenames, str_get (filename));
+ index_insert (filenames, -1, str_get (filename));
add_list (filenames, -1, TRUE, TRUE);
}
@@ -201,8 +193,3 @@ void drct_pl_open_temp_list (Index * filenames)
{
add_list (filenames, -1, TRUE, TRUE);
}
-
-void drct_pl_delete_selected (int list)
-{
- playlist_delete_selected (list);
-}
diff --git a/src/audacious/effect.c b/src/audacious/effect.c
index 23f12ea..c344e06 100644
--- a/src/audacious/effect.c
+++ b/src/audacious/effect.c
@@ -1,6 +1,6 @@
/*
* effect.c
- * Copyright 2010 John Lindgren
+ * Copyright 2010-2012 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -50,7 +50,7 @@ static bool_t effect_start_cb (PluginHandle * plugin, EffectStartState * state)
g_return_val_if_fail (header != NULL, TRUE);
header->start (state->channels, state->rate);
- RunningEffect * effect = g_malloc (sizeof (RunningEffect));
+ RunningEffect * effect = g_slice_new (RunningEffect);
effect->plugin = plugin;
effect->header = header;
effect->channels_returned = * state->channels;
@@ -66,7 +66,10 @@ void effect_start (int * channels, int * rate)
pthread_mutex_lock (& mutex);
AUDDBG ("Starting effects.\n");
- g_list_foreach (running_effects, (GFunc) g_free, NULL);
+
+ for (GList * node = running_effects; node; node = node->next)
+ g_slice_free (RunningEffect, node->data);
+
g_list_free (running_effects);
running_effects = NULL;
@@ -96,7 +99,7 @@ static void effect_process_cb (RunningEffect * effect, EffectProcessState *
effect->header->finish (state->data, state->samples);
running_effects = g_list_remove (running_effects, effect);
- g_free (effect);
+ g_slice_free (RunningEffect, effect);
}
else
effect->header->process (state->data, state->samples);
@@ -166,7 +169,7 @@ static void effect_insert (PluginHandle * plugin, EffectPlugin * header)
return;
AUDDBG ("Adding %s without reset.\n", plugin_get_name (plugin));
- RunningEffect * effect = g_malloc (sizeof (RunningEffect));
+ RunningEffect * effect = g_slice_new (RunningEffect);
effect->plugin = plugin;
effect->header = header;
effect->remove_flag = FALSE;
diff --git a/src/audacious/effect.h b/src/audacious/effect.h
index c663eb7..2a0add9 100644
--- a/src/audacious/effect.h
+++ b/src/audacious/effect.h
@@ -1,6 +1,6 @@
/*
* effect.h
- * Copyright 2010 John Lindgren
+ * Copyright 2010-2012 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/audacious/equalizer.c b/src/audacious/equalizer.c
index ef67b89..1b74f31 100644
--- a/src/audacious/equalizer.c
+++ b/src/audacious/equalizer.c
@@ -182,18 +182,18 @@ void eq_cleanup (void)
void eq_set_bands (const double *values)
{
- char *string = double_array_to_string (values, EQ_BANDS);
+ char *string = double_array_to_str (values, EQ_BANDS);
g_return_if_fail (string);
- set_string (NULL, "equalizer_bands", string);
- g_free (string);
+ set_str (NULL, "equalizer_bands", string);
+ str_unref (string);
}
void eq_get_bands (double *values)
{
memset (values, 0, sizeof (double) * EQ_BANDS);
- char *string = get_string (NULL, "equalizer_bands");
- string_to_double_array (string, values, EQ_BANDS);
- g_free (string);
+ char *string = get_str (NULL, "equalizer_bands");
+ str_to_double_array (string, values, EQ_BANDS);
+ str_unref (string);
}
void eq_set_band (int band, double value)
diff --git a/src/audacious/equalizer_preset.c b/src/audacious/equalizer_preset.c
index dce62f9..6c70eb8 100644
--- a/src/audacious/equalizer_preset.c
+++ b/src/audacious/equalizer_preset.c
@@ -1,6 +1,7 @@
/*
* equalizer_preset.c
- * Copyright 2003-2011 Eugene Zagidullin, William Pitcock, and John Lindgren
+ * Copyright 2003-2013 Eugene Zagidullin, William Pitcock, John Lindgren, and
+ * Thomas Lange
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -18,235 +19,224 @@
*/
#include <glib.h>
+#include <math.h>
#include <string.h>
+#include <libaudcore/audstrings.h>
+
#include "debug.h"
#include "i18n.h"
-#include "interface.h"
#include "misc.h"
-static EqualizerPreset * equalizer_preset_new (const char * name)
+EqualizerPreset * equalizer_preset_new (const char * name)
{
- EqualizerPreset *preset = g_new0(EqualizerPreset, 1);
- preset->name = g_strdup(name);
+ EqualizerPreset * preset = g_slice_new0 (EqualizerPreset);
+ preset->name = str_get (name);
return preset;
}
+void equalizer_preset_free (EqualizerPreset * preset)
+{
+ str_unref (preset->name);
+ g_slice_free (EqualizerPreset, preset);
+}
+
Index * equalizer_read_presets (const char * basename)
{
- char *filename, *name;
- GKeyFile *rcfile;
- int i, p = 0;
- EqualizerPreset *preset;
+ GKeyFile * rcfile = g_key_file_new ();
- filename = g_build_filename (get_path (AUD_PATH_USER_DIR), basename, NULL);
+ char * filename = filename_build (get_path (AUD_PATH_USER_DIR), basename);
- rcfile = g_key_file_new();
- if (!g_key_file_load_from_file(rcfile, filename, G_KEY_FILE_NONE, NULL))
+ if (! g_key_file_load_from_file (rcfile, filename, G_KEY_FILE_NONE, NULL))
{
- g_free(filename);
- filename = g_build_filename (get_path (AUD_PATH_DATA_DIR), basename,
- NULL);
+ str_unref (filename);
+ filename = filename_build (get_path (AUD_PATH_DATA_DIR), basename);
- if (!g_key_file_load_from_file(rcfile, filename, G_KEY_FILE_NONE, NULL))
+ if (! g_key_file_load_from_file (rcfile, filename, G_KEY_FILE_NONE, NULL))
{
- g_free(filename);
- return NULL;
+ str_unref (filename);
+ g_key_file_free (rcfile);
+ return NULL;
}
}
- g_free(filename);
+ str_unref (filename);
Index * list = index_new ();
- for (;;)
+ for (int p = 0;; p ++)
{
- char section[32];
+ SPRINTF (section, "Preset%d", p);
- g_snprintf(section, sizeof(section), "Preset%d", p++);
+ char * name = g_key_file_get_string (rcfile, "Presets", section, NULL);
+ if (! name)
+ break;
- if ((name = g_key_file_get_string(rcfile, "Presets", section, NULL)) != NULL)
- {
- preset = g_new0(EqualizerPreset, 1);
- preset->name = name;
- preset->preamp = g_key_file_get_double(rcfile, name, "Preamp", NULL);
+ EqualizerPreset * preset = equalizer_preset_new (name);
+ preset->preamp = g_key_file_get_double (rcfile, name, "Preamp", NULL);
- for (i = 0; i < AUD_EQUALIZER_NBANDS; i++)
- {
- char band[16];
- g_snprintf(band, sizeof(band), "Band%d", i);
+ for (int i = 0; i < AUD_EQUALIZER_NBANDS; i++)
+ {
+ SPRINTF (band, "Band%d", i);
+ preset->bands[i] = g_key_file_get_double (rcfile, name, band, NULL);
+ }
- preset->bands[i] = g_key_file_get_double(rcfile, name, band, NULL);
- }
+ index_insert (list, -1, preset);
- index_append (list, preset);
- }
- else
- break;
+ g_free (name);
}
- g_key_file_free(rcfile);
+ g_key_file_free (rcfile);
return list;
}
-bool_t equalizer_write_preset_file (Index * list, const char * basename)
+bool_t equalizer_write_presets (Index * list, const char * basename)
{
- char *filename;
- int i;
- GKeyFile *rcfile;
- char *data;
- gsize len;
-
- rcfile = g_key_file_new();
+ GKeyFile * rcfile = g_key_file_new ();
for (int p = 0; p < index_count (list); p ++)
{
EqualizerPreset * preset = index_get (list, p);
- char * tmp = g_strdup_printf ("Preset%d", p);
- g_key_file_set_string(rcfile, "Presets", tmp, preset->name);
- g_free(tmp);
-
- g_key_file_set_double(rcfile, preset->name, "Preamp", preset->preamp);
+ SPRINTF (tmp, "Preset%d", p);
+ g_key_file_set_string (rcfile, "Presets", tmp, preset->name);
+ g_key_file_set_double (rcfile, preset->name, "Preamp", preset->preamp);
- for (i = 0; i < 10; i++)
+ for (int i = 0; i < AUD_EQUALIZER_NBANDS; i ++)
{
- tmp = g_strdup_printf("Band%d", i);
- g_key_file_set_double(rcfile, preset->name, tmp,
- preset->bands[i]);
- g_free(tmp);
+ SPRINTF (tmp, "Band%d", i);
+ g_key_file_set_double (rcfile, preset->name, tmp, preset->bands[i]);
}
}
- filename = g_build_filename (get_path (AUD_PATH_USER_DIR), basename, NULL);
+ size_t len;
+ char * data = g_key_file_to_data (rcfile, & len, NULL);
- data = g_key_file_to_data(rcfile, &len, NULL);
+ char * filename = filename_build (get_path (AUD_PATH_USER_DIR), basename);
bool_t success = g_file_set_contents (filename, data, len, NULL);
- g_free(data);
+ str_unref (filename);
+
+ g_key_file_free (rcfile);
+ g_free (data);
- g_key_file_free(rcfile);
- g_free(filename);
return success;
}
-Index * import_winamp_eqf (VFSFile * file)
+/* Note: Winamp 2.x had a +/- 20 dB range.
+ * Winamp 5.x had a +/- 12 dB range, which we use here. */
+#define FROM_WINAMP_VAL(x) ((31.5 - (x)) * (12.0 / 31.5))
+#define TO_WINAMP_VAL(x) (round (31.5 - (x) * (31.5 / 12.0)))
+
+Index * import_winamp_presets (VFSFile * file)
{
char header[31];
char bands[11];
- int i = 0;
- EqualizerPreset *preset = NULL;
- char *markup;
- char preset_name[0xb4];
+ char preset_name[181];
- if (vfs_fread (header, 1, sizeof header, file) != sizeof header || strncmp
- (header, "Winamp EQ library file v1.1", 27))
- goto error;
+ if (vfs_fread (header, 1, sizeof header, file) != sizeof header ||
+ strncmp (header, "Winamp EQ library file v1.1", 27))
+ return NULL;
- AUDDBG("The EQF header is OK\n");
+ Index * list = index_new ();
- if (vfs_fseek(file, 0x1f, SEEK_SET) == -1) goto error;
+ while (vfs_fread (preset_name, 1, 180, file) == 180)
+ {
+ preset_name[180] = 0; /* protect against buffer overflow */
- Index * list = index_new ();
+ if (vfs_fseek (file, 77, SEEK_CUR)) /* unknown crap --asphyx */
+ break;
- while (vfs_fread(preset_name, 1, 0xb4, file) == 0xb4) {
- AUDDBG("The preset name is '%s'\n", preset_name);
- if (vfs_fseek (file, 0x4d, SEEK_CUR)) /* unknown crap --asphyx */
+ if (vfs_fread (bands, 1, 11, file) != 11)
break;
- if (vfs_fread(bands, 1, 11, file) != 11) break;
- preset = equalizer_preset_new(preset_name);
- /*this was divided by 63, but shouldn't it be 64? --majeru*/
- preset->preamp = EQUALIZER_MAX_GAIN - ((bands[10] * EQUALIZER_MAX_GAIN * 2) / 64.0);
+ EqualizerPreset * preset = equalizer_preset_new (preset_name);
+ preset->preamp = FROM_WINAMP_VAL (bands[10]);
- for (i = 0; i < 10; i++)
- preset->bands[i] = EQUALIZER_MAX_GAIN - ((bands[i] * EQUALIZER_MAX_GAIN * 2) / 64.0);
+ for (int i = 0; i < AUD_EQUALIZER_NBANDS; i ++)
+ preset->bands[i] = FROM_WINAMP_VAL (bands[i]);
- index_append (list, preset);
+ index_insert (list, -1, preset);
}
return list;
+}
+
+bool_t export_winamp_preset (EqualizerPreset * preset, VFSFile * file)
+{
+ char name[257];
+ char bands[11];
-error:
- markup = g_strdup_printf (_("Error importing Winamp EQF file '%s'"),
- vfs_get_filename (file));
- interface_show_error(markup);
+ if (vfs_fwrite ("Winamp EQ library file v1.1\x1a!--", 1, 31, file) != 31)
+ return FALSE;
- g_free(markup);
- return NULL;
+ strncpy (name, preset->name, 257);
+
+ if (vfs_fwrite (name, 1, 257, file) != 257)
+ return FALSE;
+
+ for (int i = 0; i < AUD_EQUALIZER_NBANDS; i ++)
+ bands[i] = TO_WINAMP_VAL (preset->bands[i]);
+
+ bands[10] = TO_WINAMP_VAL (preset->preamp);
+
+ if (vfs_fwrite (bands, 1, 11, file) != 11)
+ return FALSE;
+
+ return TRUE;
}
bool_t save_preset_file (EqualizerPreset * preset, const char * filename)
{
- GKeyFile *rcfile;
- int i;
- char *data;
- gsize len;
-
- rcfile = g_key_file_new();
- g_key_file_set_double(rcfile, "Equalizer preset", "Preamp", preset->preamp);
-
- for (i = 0; i < 10; i++) {
- char tmp[7];
- g_snprintf(tmp, sizeof(tmp), "Band%d", i);
- g_key_file_set_double(rcfile, "Equalizer preset", tmp,
- preset->bands[i]);
+ GKeyFile * rcfile = g_key_file_new ();
+
+ g_key_file_set_double (rcfile, "Equalizer preset", "Preamp", preset->preamp);
+
+ for (int i = 0; i < AUD_EQUALIZER_NBANDS; i ++)
+ {
+ SPRINTF (tmp, "Band%d", i);
+ g_key_file_set_double (rcfile, "Equalizer preset", tmp, preset->bands[i]);
}
- data = g_key_file_to_data(rcfile, &len, NULL);
+ size_t len;
+ char * data = g_key_file_to_data (rcfile, & len, NULL);
+ VFSFile * file = vfs_fopen (filename, "w");
bool_t success = FALSE;
- VFSFile * file = vfs_fopen (filename, "w");
- if (file == NULL)
- goto DONE;
- if (vfs_fwrite (data, 1, strlen (data), file) == strlen (data))
- success = TRUE;
- vfs_fclose (file);
-
-DONE:
- g_free(data);
- g_key_file_free(rcfile);
+ if (file)
+ {
+ success = (vfs_fwrite (data, 1, len, file) == len);
+ vfs_fclose (file);
+ }
+
+ g_key_file_free (rcfile);
+ g_free (data);
+
return success;
}
-static EqualizerPreset * equalizer_read_aud_preset (const char * filename)
+EqualizerPreset * load_preset_file (const char * filename)
{
- int i;
- EqualizerPreset *preset;
- GKeyFile *rcfile;
-
- preset = g_new0(EqualizerPreset, 1);
- preset->name = g_strdup("");
+ GKeyFile * rcfile = g_key_file_new ();
- rcfile = g_key_file_new();
- if (!g_key_file_load_from_file(rcfile, filename, G_KEY_FILE_NONE, NULL))
+ if (! g_key_file_load_from_file (rcfile, filename, G_KEY_FILE_NONE, NULL))
{
- g_key_file_free(rcfile);
- g_free(preset->name);
- g_free(preset);
+ g_key_file_free (rcfile);
return NULL;
}
- preset->preamp = g_key_file_get_double(rcfile, "Equalizer preset", "Preamp", NULL);
- for (i = 0; i < 10; i++)
- {
- char tmp[7];
- g_snprintf(tmp, sizeof(tmp), "Band%d", i);
+ EqualizerPreset * preset = equalizer_preset_new ("");
+
+ preset->preamp = g_key_file_get_double (rcfile, "Equalizer preset", "Preamp", NULL);
- preset->bands[i] = g_key_file_get_double(rcfile, "Equalizer preset", tmp, NULL);
+ for (int i = 0; i < AUD_EQUALIZER_NBANDS; i ++)
+ {
+ SPRINTF (tmp, "Band%d", i);
+ preset->bands[i] = g_key_file_get_double (rcfile, "Equalizer preset", tmp, NULL);
}
- g_key_file_free(rcfile);
- return preset;
-}
+ g_key_file_free (rcfile);
-EqualizerPreset *
-load_preset_file(const char *filename)
-{
- if (filename) {
- EqualizerPreset *preset = equalizer_read_aud_preset(filename);
- return preset;
- }
- return NULL;
+ return preset;
}
diff --git a/src/audacious/fft.c b/src/audacious/fft.c
index d967396..09a6602 100644
--- a/src/audacious/fft.c
+++ b/src/audacious/fft.c
@@ -22,12 +22,6 @@
#include "fft.h"
-#ifndef HAVE_CEXPF
-/* e^(a+bi) = (e^a)(cos(b)+sin(b)i) */
-#define cexpf(x) (expf(crealf(x))*(cosf(cimagf(x))+sinf(cimagf(x))*I))
-#warning Your C library does not have cexpf(). Please update it.
-#endif
-
#define N 512 /* size of the DFT */
#define LOGN 9 /* log N (base 2) */
diff --git a/src/audacious/history.c b/src/audacious/history.c
index b09903f..4dde368 100644
--- a/src/audacious/history.c
+++ b/src/audacious/history.c
@@ -36,8 +36,6 @@ static void history_save (void)
if (! modified)
return;
- config_clear_section ("history");
-
GList * node = history.head;
for (int i = 0; i < MAX_ENTRIES; i ++)
{
@@ -46,7 +44,7 @@ static void history_save (void)
char name[32];
snprintf (name, sizeof name, "entry%d", i);
- set_string ("history", name, node->data);
+ set_str ("history", name, node->data);
node = node->next;
}
@@ -63,11 +61,11 @@ static void history_load (void)
{
char name[32];
snprintf (name, sizeof name, "entry%d", i);
- char * path = get_string ("history", name);
+ char * path = get_str ("history", name);
if (! path[0])
{
- g_free (path);
+ str_unref (path);
break;
}
@@ -85,7 +83,7 @@ void history_cleanup (void)
hook_dissociate ("config save", (HookFunction) history_save);
- g_queue_foreach (& history, (GFunc) g_free, NULL);
+ g_queue_foreach (& history, (GFunc) str_unref, NULL);
g_queue_clear (& history);
loaded = FALSE;
@@ -108,11 +106,11 @@ void history_add (const char * path)
next = node->next;
if (! strcmp (node->data, path))
{
- g_free (node->data);
+ str_unref (node->data);
g_queue_delete_link (& history, node);
}
}
- g_queue_push_head (& history, g_strdup (path));
+ g_queue_push_head (& history, str_get (path));
modified = TRUE;
}
diff --git a/src/audacious/images/appearance.png b/src/audacious/images/appearance.png
new file mode 100644
index 0000000..f73239a
--- /dev/null
+++ b/src/audacious/images/appearance.png
Binary files differ
diff --git a/src/audacious/images/audacious.png b/src/audacious/images/audacious.png
deleted file mode 100644
index 5de1b7f..0000000
--- a/src/audacious/images/audacious.png
+++ /dev/null
Binary files differ
diff --git a/src/audacious/images/menu_playlist.png b/src/audacious/images/menu_playlist.png
deleted file mode 100644
index 2bd5af9..0000000
--- a/src/audacious/images/menu_playlist.png
+++ /dev/null
Binary files differ
diff --git a/src/audacious/images/menu_plugin.png b/src/audacious/images/menu_plugin.png
deleted file mode 100644
index d90ab66..0000000
--- a/src/audacious/images/menu_plugin.png
+++ /dev/null
Binary files differ
diff --git a/src/audacious/images/menu_queue_toggle.png b/src/audacious/images/menu_queue_toggle.png
deleted file mode 100644
index 6cf6443..0000000
--- a/src/audacious/images/menu_queue_toggle.png
+++ /dev/null
Binary files differ
diff --git a/src/audacious/input-api.h b/src/audacious/input-api.h
new file mode 100644
index 0000000..7c49b0e
--- /dev/null
+++ b/src/audacious/input-api.h
@@ -0,0 +1,60 @@
+/*
+ * input-api.h
+ * Copyright 2013 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.
+ */
+
+/* Do not include this file directly; use input.h instead. */
+
+/* These functions are to be used only from the play() function of an input plugin. */
+
+/* Prepares the output system for playback in the specified format. Returns
+ * TRUE on success, FALSE if the selected format is not supported. */
+AUD_FUNC3 (bool_t, input_open_audio, int, format, int, rate, int, channels)
+
+/* Informs the output system of replay gain values for the current song so
+ * that volume levels can be adjusted accordingly, if the user so desires.
+ * This may be called at any time during playback should the values change. */
+AUD_VFUNC1 (input_set_gain, const ReplayGainInfo *, info)
+
+/* Passes audio data to the output system for playback. The data must be in
+ * the format passed to open_audio, and the length (in bytes) must be an
+ * integral number of frames. This function blocks until all the data has
+ * been written (though it may not yet be heard by the user). */
+AUD_VFUNC2 (input_write_audio, void *, data, int, length)
+
+/* Returns the time counter. Note that this represents the amount of audio
+ * data passed to the output system, not the amount actually heard by the
+ * user. */
+AUD_FUNC0 (int, input_written_time)
+
+/* Returns a reference to the current tuple for the stream. */
+AUD_FUNC0 (Tuple *, input_get_tuple)
+
+/* Updates the tuple for the stream. The caller gives up ownership of one
+ * reference to the tuple. */
+AUD_VFUNC1 (input_set_tuple, Tuple *, tuple)
+
+/* Updates the displayed bitrate, in bits per second. */
+AUD_VFUNC1 (input_set_bitrate, int, bitrate)
+
+/* Checks whether playback is to be stopped. The play() function should poll
+ * check_stop() periodically and return as soon as check_stop() returns TRUE. */
+AUD_FUNC0 (bool_t, input_check_stop)
+
+/* Checks whether a seek has been requested. If so, discards any buffered audio
+ * and returns the position to seek to, in milliseconds. Otherwise, returns -1. */
+AUD_FUNC0 (int, input_check_seek)
diff --git a/src/audacious/input.h b/src/audacious/input.h
new file mode 100644
index 0000000..4935483
--- /dev/null
+++ b/src/audacious/input.h
@@ -0,0 +1,69 @@
+/*
+ * input.h
+ * Copyright 2013 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 AUDACIOUS_INPUT_H
+#define AUDACIOUS_INPUT_H
+
+#include <audacious/api.h>
+#include <audacious/types.h>
+#include <libaudcore/tuple.h>
+
+#define AUD_API_NAME InputAPI
+#define AUD_API_SYMBOL input_api
+
+#ifdef _AUDACIOUS_CORE
+
+#include "api-local-begin.h"
+#include "input-api.h"
+#include "api-local-end.h"
+
+#else
+
+#include <audacious/api-define-begin.h>
+#include <audacious/input-api.h>
+#include <audacious/api-define-end.h>
+
+#include <audacious/api-alias-begin.h>
+#include <audacious/input-api.h>
+#include <audacious/api-alias-end.h>
+
+#endif
+
+#undef AUD_API_NAME
+#undef AUD_API_SYMBOL
+
+#endif
+
+#ifdef AUD_API_DECLARE
+
+#define AUD_API_NAME InputAPI
+#define AUD_API_SYMBOL input_api
+
+#include "api-define-begin.h"
+#include "input-api.h"
+#include "api-define-end.h"
+
+#include "api-declare-begin.h"
+#include "input-api.h"
+#include "api-declare-end.h"
+
+#undef AUD_API_NAME
+#undef AUD_API_SYMBOL
+
+#endif
diff --git a/src/audacious/interface.c b/src/audacious/interface.c
index 066b0ef..5c8045f 100644
--- a/src/audacious/interface.c
+++ b/src/audacious/interface.c
@@ -1,6 +1,6 @@
/*
* interface.c
- * Copyright 2010-2011 John Lindgren
+ * Copyright 2010-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -21,21 +21,23 @@
#include <pthread.h>
#include <libaudcore/hook.h>
+#include <libaudgui/libaudgui-gtk.h>
#include "debug.h"
#include "general.h"
+#include "i18n.h"
#include "interface.h"
-#include "main.h"
#include "misc.h"
#include "plugin.h"
#include "plugins.h"
#include "visualization.h"
-static IfacePlugin *current_interface = NULL;
+static IfacePlugin * current_interface = NULL;
static pthread_mutex_t error_mutex = PTHREAD_MUTEX_INITIALIZER;
static GQueue error_queue = G_QUEUE_INIT;
static int error_source;
+static GtkWidget * error_win;
bool_t interface_load (PluginHandle * plugin)
{
@@ -73,18 +75,7 @@ bool_t interface_is_shown (void)
{
g_return_val_if_fail (current_interface, FALSE);
- if (PLUGIN_HAS_FUNC (current_interface, is_shown))
- return current_interface->is_shown ();
- return TRUE;
-}
-
-bool_t interface_is_focused (void)
-{
- g_return_val_if_fail (current_interface, FALSE);
-
- if (PLUGIN_HAS_FUNC (current_interface, is_focused))
- return current_interface->is_focused ();
- return TRUE;
+ return get_bool (NULL, "show_interface");
}
static bool_t error_idle_func (void * unused)
@@ -96,12 +87,12 @@ static bool_t error_idle_func (void * unused)
{
pthread_mutex_unlock (& error_mutex);
- if (current_interface && PLUGIN_HAS_FUNC (current_interface, show_error))
- current_interface->show_error (message);
- else
+ if (headless_mode ())
fprintf (stderr, "ERROR: %s\n", message);
+ else
+ audgui_simple_message (& error_win, GTK_MESSAGE_ERROR, _("Error"), message);
- g_free (message);
+ str_unref (message);
pthread_mutex_lock (& error_mutex);
}
@@ -116,7 +107,7 @@ void interface_show_error (const char * message)
{
pthread_mutex_lock (& error_mutex);
- g_queue_push_tail (& error_queue, g_strdup (message));
+ g_queue_push_tail (& error_queue, str_get (message));
if (! error_source)
error_source = g_idle_add (error_idle_func, NULL);
@@ -124,27 +115,6 @@ void interface_show_error (const char * message)
pthread_mutex_unlock (& error_mutex);
}
-/*
- * bool_t play_button
- * TRUE - open files
- * FALSE - add files
- */
-void interface_show_filebrowser (bool_t play_button)
-{
- g_return_if_fail (current_interface);
-
- if (PLUGIN_HAS_FUNC (current_interface, show_filebrowser))
- current_interface->show_filebrowser (play_button);
-}
-
-void interface_show_jump_to_track (void)
-{
- g_return_if_fail (current_interface);
-
- if (PLUGIN_HAS_FUNC (current_interface, show_jump_to_track))
- current_interface->show_jump_to_track ();
-}
-
static bool_t delete_cb (GtkWidget * window, GdkEvent * event, PluginHandle *
plugin)
{
@@ -180,26 +150,6 @@ void interface_remove_plugin_widget (PluginHandle * plugin, GtkWidget * widget)
gtk_widget_destroy (gtk_widget_get_parent (widget));
}
-void interface_install_toolbar (void * widget)
-{
- g_return_if_fail (current_interface);
-
- if (PLUGIN_HAS_FUNC (current_interface, install_toolbar))
- current_interface->install_toolbar (widget);
- else
- g_object_ref (widget);
-}
-
-void interface_uninstall_toolbar (void * widget)
-{
- g_return_if_fail (current_interface);
-
- if (PLUGIN_HAS_FUNC (current_interface, uninstall_toolbar))
- current_interface->uninstall_toolbar (widget);
- else
- g_object_unref (widget);
-}
-
static bool_t probe_cb (PluginHandle * p, PluginHandle * * pp)
{
* pp = p;
diff --git a/src/audacious/main.c b/src/audacious/main.c
index 1e245fb..8440969 100644
--- a/src/audacious/main.c
+++ b/src/audacious/main.c
@@ -1,6 +1,6 @@
/*
* main.c
- * Copyright 2007-2011 William Pitcock and John Lindgren
+ * Copyright 2007-2013 William Pitcock and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -29,11 +29,11 @@
#include <libaudcore/audstrings.h>
#include <libaudcore/hook.h>
+#include <libaudgui/libaudgui.h>
#include <libaudtag/audtag.h>
#ifdef USE_DBUS
-#include "../libaudclient/audctrl.h"
-#include "dbus.h"
+#include "aud-dbus.h"
#endif
#include "debug.h"
@@ -50,20 +50,40 @@
#define AUTOSAVE_INTERVAL 300 /* seconds */
-bool_t headless;
-
static struct {
- char **filenames;
- int session;
- bool_t play, stop, pause, fwd, rew, play_pause, show_jump_box;
- bool_t enqueue, mainwin, remote;
- bool_t enqueue_to_temp;
- bool_t quit_after_play;
- bool_t version;
+ bool_t help, version;
+ bool_t play, pause, play_pause, stop, fwd, rew;
+ bool_t enqueue, enqueue_to_temp;
+ bool_t mainwin, show_jump_box;
+ bool_t headless, quit_after_play;
bool_t verbose;
- char *previous_session_id;
} options;
+static Index * filenames;
+
+static const struct {
+ const char * long_arg;
+ char short_arg;
+ bool_t * value;
+ const char * desc;
+} arg_map[] = {
+ {"help", 'h', & options.help, N_("Show command-line help")},
+ {"version", 'v', & options.version, N_("Show version")},
+ {"play", 'p', & options.play, N_("Start playback")},
+ {"pause", 'u', & options.pause, N_("Pause playback")},
+ {"play-pause", 't', & options.play_pause, N_("Pause if playing, play otherwise")},
+ {"stop", 's', & options.stop, N_("Stop playback")},
+ {"rew", 'r', & options.rew, N_("Skip to previous song")},
+ {"fwd", 'f', & options.fwd, N_("Skip to next song")},
+ {"enqueue", 'e', & options.enqueue, N_("Add files to the playlist")},
+ {"enqueue-to-temp", 'E', & options.enqueue_to_temp, N_("Add files to a temporary playlist")},
+ {"show-main-window", 'm', & options.mainwin, N_("Display the main window")},
+ {"show-jump-box", 'j', & options.show_jump_box, N_("Display the jump-to-song window")},
+ {"headless", 'H', & options.headless, N_("Start without a graphical interface")},
+ {"quit-after-play", 'q', & options.quit_after_play, N_("Quit on playback stop")},
+ {"verbose", 'V', & options.verbose, N_("Print debugging messages")},
+};
+
static char * aud_paths[AUD_PATH_COUNT];
static void make_dirs(void)
@@ -78,41 +98,8 @@ static void make_dirs(void)
make_directory(aud_paths[AUD_PATH_PLAYLISTS_DIR], mode755);
}
-static void normalize_path (char * path)
-{
-#ifdef _WIN32
- string_replace_char (path, '/', '\\');
-#endif
- int len = strlen (path);
-#ifdef _WIN32
- if (len > 3 && path[len - 1] == '\\') /* leave "C:\" */
-#else
- if (len > 1 && path[len - 1] == '/') /* leave leading "/" */
-#endif
- path[len - 1] = 0;
-}
-
-static char * last_path_element (char * path)
-{
- char * slash = strrchr (path, G_DIR_SEPARATOR);
- return (slash && slash[1]) ? slash + 1 : NULL;
-}
-
-static void strip_path_element (char * path, char * elem)
-{
-#ifdef _WIN32
- if (elem > path + 3)
-#else
- if (elem > path + 1)
-#endif
- elem[-1] = 0; /* overwrite slash */
- else
- elem[0] = 0; /* leave [drive letter and] leading slash */
-}
-
-static void relocate_path (char * * pathp, const char * old, const char * new)
+static char * relocate_path (const char * path, const char * old, const char * new)
{
- char * path = * pathp;
int oldlen = strlen (old);
int newlen = strlen (new);
@@ -122,79 +109,87 @@ static void relocate_path (char * * pathp, const char * old, const char * new)
newlen --;
#ifdef _WIN32
- if (strncasecmp (path, old, oldlen) || (path[oldlen] && path[oldlen] != G_DIR_SEPARATOR))
+ if (g_ascii_strncasecmp (path, old, oldlen) || (path[oldlen] && path[oldlen] != G_DIR_SEPARATOR))
#else
if (strncmp (path, old, oldlen) || (path[oldlen] && path[oldlen] != G_DIR_SEPARATOR))
#endif
{
fprintf (stderr, "Failed to relocate a data path. Falling back to "
"compile-time path: %s\n", path);
- return;
+ return str_get (path);
}
- * pathp = g_strdup_printf ("%.*s%s", newlen, new, path + oldlen);
- g_free (path);
+ return str_printf ("%.*s%s", newlen, new, path + oldlen);
}
static void relocate_paths (void)
{
- /* Start with the paths hard coded at compile time. */
- aud_paths[AUD_PATH_BIN_DIR] = g_strdup (HARDCODE_BINDIR);
- aud_paths[AUD_PATH_DATA_DIR] = g_strdup (HARDCODE_DATADIR);
- aud_paths[AUD_PATH_PLUGIN_DIR] = g_strdup (HARDCODE_PLUGINDIR);
- aud_paths[AUD_PATH_LOCALE_DIR] = g_strdup (HARDCODE_LOCALEDIR);
- aud_paths[AUD_PATH_DESKTOP_FILE] = g_strdup (HARDCODE_DESKTOPFILE);
- aud_paths[AUD_PATH_ICON_FILE] = g_strdup (HARDCODE_ICONFILE);
- normalize_path (aud_paths[AUD_PATH_BIN_DIR]);
- normalize_path (aud_paths[AUD_PATH_DATA_DIR]);
- normalize_path (aud_paths[AUD_PATH_PLUGIN_DIR]);
- normalize_path (aud_paths[AUD_PATH_LOCALE_DIR]);
- normalize_path (aud_paths[AUD_PATH_DESKTOP_FILE]);
- normalize_path (aud_paths[AUD_PATH_ICON_FILE]);
+ char bindir[] = HARDCODE_BINDIR;
+ char datadir[] = HARDCODE_DATADIR;
+ char plugindir[] = HARDCODE_PLUGINDIR;
+ char localedir[] = HARDCODE_LOCALEDIR;
+ char desktopfile[] = HARDCODE_DESKTOPFILE;
+ char iconfile[] = HARDCODE_ICONFILE;
+
+ filename_normalize (bindir);
+ filename_normalize (datadir);
+ filename_normalize (plugindir);
+ filename_normalize (localedir);
+ filename_normalize (desktopfile);
+ filename_normalize (iconfile);
/* Compare the compile-time path to the executable and the actual path to
* see if we have been moved. */
- char * old = g_strdup (aud_paths[AUD_PATH_BIN_DIR]);
- char * new = get_path_to_self ();
- if (! new)
+ char * self = get_path_to_self ();
+ if (! self)
{
-ERR:
- g_free (old);
- g_free (new);
+FALLBACK:
+ /* Fall back to compile-time paths. */
+ aud_paths[AUD_PATH_BIN_DIR] = str_get (bindir);
+ aud_paths[AUD_PATH_DATA_DIR] = str_get (datadir);
+ aud_paths[AUD_PATH_PLUGIN_DIR] = str_get (plugindir);
+ aud_paths[AUD_PATH_LOCALE_DIR] = str_get (localedir);
+ aud_paths[AUD_PATH_DESKTOP_FILE] = str_get (desktopfile);
+ aud_paths[AUD_PATH_ICON_FILE] = str_get (iconfile);
+
return;
}
- normalize_path (new);
+
+ SCOPY (old, bindir);
+ SCOPY (new, self);
+
+ str_unref (self);
+
+ filename_normalize (new);
/* Strip the name of the executable file, leaving the path. */
char * base = last_path_element (new);
if (! base)
- goto ERR;
- strip_path_element (new, base);
+ goto FALLBACK;
+
+ cut_path_element (new, base);
/* Strip innermost folder names from both paths as long as they match. This
* leaves a compile-time prefix and a run-time one to replace it with. */
char * a, * b;
while ((a = last_path_element (old)) && (b = last_path_element (new)) &&
#ifdef _WIN32
- ! strcasecmp (a, b))
+ ! g_ascii_strcasecmp (a, b))
#else
! strcmp (a, b))
#endif
{
- strip_path_element (old, a);
- strip_path_element (new, b);
+ cut_path_element (old, a);
+ cut_path_element (new, b);
}
/* Do the replacements. */
- relocate_path (& aud_paths[AUD_PATH_BIN_DIR], old, new);
- relocate_path (& aud_paths[AUD_PATH_DATA_DIR], old, new);
- relocate_path (& aud_paths[AUD_PATH_PLUGIN_DIR], old, new);
- relocate_path (& aud_paths[AUD_PATH_LOCALE_DIR], old, new);
- relocate_path (& aud_paths[AUD_PATH_DESKTOP_FILE], old, new);
- relocate_path (& aud_paths[AUD_PATH_ICON_FILE], old, new);
-
- g_free (old);
- g_free (new);
+ aud_paths[AUD_PATH_BIN_DIR] = relocate_path (bindir, old, new);
+ aud_paths[AUD_PATH_DATA_DIR] = relocate_path (datadir, old, new);
+ aud_paths[AUD_PATH_PLUGIN_DIR] = relocate_path (plugindir, old, new);
+ aud_paths[AUD_PATH_LOCALE_DIR] = relocate_path (localedir, old, new);
+ aud_paths[AUD_PATH_DESKTOP_FILE] = relocate_path (desktopfile, old, new);
+ aud_paths[AUD_PATH_ICON_FILE] = relocate_path (iconfile, old, new);
}
static void init_paths (void)
@@ -202,24 +197,18 @@ static void init_paths (void)
relocate_paths ();
const char * xdg_config_home = g_get_user_config_dir ();
- const char * xdg_data_home = g_get_user_data_dir ();
+
+ aud_paths[AUD_PATH_USER_DIR] = filename_build (xdg_config_home, "audacious");
+ aud_paths[AUD_PATH_PLAYLISTS_DIR] = filename_build (aud_paths[AUD_PATH_USER_DIR], "playlists");
#ifdef _WIN32
/* Some libraries (libmcs) and plugins (filewriter) use these variables,
* which are generally not set on Windows. */
g_setenv ("HOME", g_get_home_dir (), TRUE);
g_setenv ("XDG_CONFIG_HOME", xdg_config_home, TRUE);
- g_setenv ("XDG_DATA_HOME", xdg_data_home, TRUE);
+ g_setenv ("XDG_DATA_HOME", g_get_user_data_dir (), TRUE);
g_setenv ("XDG_CACHE_HOME", g_get_user_cache_dir (), TRUE);
#endif
-
- aud_paths[AUD_PATH_USER_DIR] = g_build_filename(xdg_config_home, "audacious", NULL);
- aud_paths[AUD_PATH_USER_PLUGIN_DIR] = g_build_filename(xdg_data_home, "audacious", "Plugins", NULL);
- aud_paths[AUD_PATH_PLAYLISTS_DIR] = g_build_filename(aud_paths[AUD_PATH_USER_DIR], "playlists", NULL);
- aud_paths[AUD_PATH_GTKRC_FILE] = g_build_filename(aud_paths[AUD_PATH_USER_DIR], "gtkrc", NULL);
-
- for (int i = 0; i < AUD_PATH_COUNT; i ++)
- AUDDBG ("Data path: %s\n", aud_paths[i]);
}
const char * get_path (int id)
@@ -228,181 +217,202 @@ const char * get_path (int id)
return aud_paths[id];
}
-static GOptionEntry cmd_entries[] = {
- {"rew", 'r', 0, G_OPTION_ARG_NONE, &options.rew, N_("Skip backwards in playlist"), NULL},
- {"play", 'p', 0, G_OPTION_ARG_NONE, &options.play, N_("Start playing current playlist"), NULL},
- {"pause", 'u', 0, G_OPTION_ARG_NONE, &options.pause, N_("Pause current song"), NULL},
- {"stop", 's', 0, G_OPTION_ARG_NONE, &options.stop, N_("Stop current song"), NULL},
- {"play-pause", 't', 0, G_OPTION_ARG_NONE, &options.play_pause, N_("Pause if playing, play otherwise"), NULL},
- {"fwd", 'f', 0, G_OPTION_ARG_NONE, &options.fwd, N_("Skip forward in playlist"), NULL},
- {"show-jump-box", 'j', 0, G_OPTION_ARG_NONE, &options.show_jump_box, N_("Display Jump to File dialog"), NULL},
- {"enqueue", 'e', 0, G_OPTION_ARG_NONE, &options.enqueue, N_("Add files to the playlist"), NULL},
- {"enqueue-to-temp", 'E', 0, G_OPTION_ARG_NONE, &options.enqueue_to_temp, N_("Add new files to a temporary playlist"), NULL},
- {"show-main-window", 'm', 0, G_OPTION_ARG_NONE, &options.mainwin, N_("Display the main window"), NULL},
- {"headless", 'h', 0, G_OPTION_ARG_NONE, & headless, N_("Headless mode"), NULL},
- {"quit-after-play", 'q', 0, G_OPTION_ARG_NONE, &options.quit_after_play, N_("Quit on playback stop"), NULL},
- {"version", 'v', 0, G_OPTION_ARG_NONE, &options.version, N_("Show version"), NULL},
- {"verbose", 'V', 0, G_OPTION_ARG_NONE, &options.verbose, N_("Print debugging messages"), NULL},
- {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &options.filenames, N_("FILE..."), NULL},
- {NULL},
-};
-
-static void parse_options (int * argc, char *** argv)
+static bool_t parse_options (int argc, char * * argv)
{
- GOptionContext *context;
- GError *error = NULL;
-
- memset (& options, 0, sizeof options);
- options.session = -1;
+ char * cur = g_get_current_dir ();
+ bool_t success = TRUE;
- context = g_option_context_new(_("- play multimedia files"));
- g_option_context_add_main_entries(context, cmd_entries, PACKAGE);
- g_option_context_add_group(context, gtk_get_option_group(FALSE));
+#ifdef _WIN32
+ get_argv_utf8 (& argc, & argv);
+#endif
- if (!g_option_context_parse(context, argc, argv, &error))
+ for (int n = 1; n < argc; n ++)
{
- fprintf (stderr,
- _("%s: %s\nTry `%s --help' for more information.\n"), (* argv)[0],
- error->message, (* argv)[0]);
- g_error_free (error);
- exit (EXIT_FAILURE);
- }
+ if (argv[n][0] != '-') /* filename */
+ {
+ char * uri = NULL;
- g_option_context_free (context);
+ if (strstr (argv[n], "://"))
+ uri = str_get (argv[n]);
+ else if (g_path_is_absolute (argv[n]))
+ uri = filename_to_uri (argv[n]);
+ else
+ {
+ char * tmp = filename_build (cur, argv[n]);
+ uri = filename_to_uri (tmp);
+ str_unref (tmp);
+ }
+
+ if (uri)
+ {
+ if (! filenames)
+ filenames = index_new ();
+
+ index_insert (filenames, -1, uri);
+ }
+ }
+ else if (argv[n][1] == '-') /* long option */
+ {
+ int i;
+
+ for (i = 0; i < ARRAY_LEN (arg_map); i ++)
+ {
+ if (! strcmp (argv[n] + 2, arg_map[i].long_arg))
+ {
+ * arg_map[i].value = TRUE;
+ break;
+ }
+ }
+
+ if (i == ARRAY_LEN (arg_map))
+ {
+ fprintf (stderr, _("Unknown option: %s\n"), argv[n]);
+ success = FALSE;
+ goto OUT;
+ }
+ }
+ else /* short form */
+ {
+ for (int c = 1; argv[n][c]; c ++)
+ {
+ int i;
+
+ for (i = 0; i < ARRAY_LEN (arg_map); i ++)
+ {
+ if (argv[n][c] == arg_map[i].short_arg)
+ {
+ * arg_map[i].value = TRUE;
+ break;
+ }
+ }
+
+ if (i == ARRAY_LEN (arg_map))
+ {
+ fprintf (stderr, _("Unknown option: -%c\n"), argv[n][c]);
+ success = FALSE;
+ goto OUT;
+ }
+ }
+ }
+ }
verbose = options.verbose;
+
+OUT:
+#ifdef _WIN32
+ free_argv_utf8 (& argc, & argv);
+#endif
+
+ g_free (cur);
+ return success;
}
-static bool_t get_lock (void)
+static void print_help (void)
{
- char * path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "lock", aud_paths[AUD_PATH_USER_DIR]);
- int handle = open (path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+ static const char pad[20] = " ";
- if (handle < 0)
- {
- if (errno != EEXIST)
- fprintf (stderr, "Cannot create %s: %s.\n", path, strerror (errno));
+ fprintf (stderr, _("Usage: audacious [OPTION] ... [FILE] ...\n\n"));
- g_free (path);
- return FALSE;
- }
+ for (int i = 0; i < ARRAY_LEN (arg_map); i ++)
+ fprintf (stderr, " -%c, --%s%.*s%s\n", arg_map[i].short_arg,
+ arg_map[i].long_arg, (int) (20 - strlen (arg_map[i].long_arg)), pad,
+ _(arg_map[i].desc));
- close (handle);
- g_free (path);
- return TRUE;
+ fprintf (stderr, "\n");
}
-static void release_lock (void)
+bool_t headless_mode (void)
{
- char * path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "lock", aud_paths[AUD_PATH_USER_DIR]);
- unlink (path);
- g_free (path);
+ return options.headless;
}
-static Index * convert_filenames (void)
+#ifdef USE_DBUS
+static void do_remote (void)
{
- if (! options.filenames)
- return NULL;
+ GDBusConnection * bus = NULL;
+ ObjAudacious * obj = NULL;
+ GError * error = NULL;
- Index * filenames = index_new ();
- char * * f = options.filenames;
- char * cur = g_get_current_dir ();
+ if (! (bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, & error)))
+ goto ERR;
- for (int i = 0; f[i]; i ++)
- {
- char * uri = NULL;
+ if (! (obj = obj_audacious_proxy_new_sync (bus, 0, "org.atheme.audacious",
+ "/org/atheme/audacious", NULL, & error)))
+ goto ERR;
- if (strstr (f[i], "://"))
- uri = str_get (f[i]);
- else if (g_path_is_absolute (f[i]))
- {
- char * tmp = filename_to_uri (f[i]);
- uri = str_get (tmp);
- free (tmp);
- }
- else
- {
- char * tmp = g_build_filename (cur, f[i], NULL);
- char * tmp2 = filename_to_uri (tmp);
- uri = str_get (tmp2);
- free (tmp);
- free (tmp2);
- }
+ /* check whether remote is running */
+ char * version = NULL;
+ obj_audacious_call_version_sync (obj, & version, NULL, NULL);
- if (uri)
- index_append (filenames, uri);
- }
+ if (! version)
+ goto DONE;
- g_free (cur);
- return filenames;
-}
+ AUDDBG ("Connected to remote version %s.\n", version);
-static void do_remote (void)
-{
-#ifdef USE_DBUS
- DBusGProxy * session = audacious_get_dbus_proxy ();
+ /* if no command line options, then present running instance */
+ if (! (filenames || options.play || options.pause || options.play_pause ||
+ options.stop || options.rew || options.fwd || options.show_jump_box ||
+ options.mainwin))
+ options.mainwin = TRUE;
- if (session && audacious_remote_is_running (session))
+ if (filenames)
{
- Index * filenames = convert_filenames ();
+ int n_filenames = index_count (filenames);
+ const char * * list = g_new (const char *, n_filenames + 1);
- /* if no command line options, then present running instance */
- if (! (filenames || options.play || options.pause || options.play_pause ||
- options.stop || options.rew || options.fwd || options.show_jump_box ||
- options.mainwin))
- options.mainwin = TRUE;
+ for (int i = 0; i < n_filenames; i ++)
+ list[i] = index_get (filenames, i);
- if (filenames)
- {
- GList * list = NULL;
+ list[n_filenames] = NULL;
+
+ if (options.enqueue_to_temp)
+ obj_audacious_call_open_list_to_temp_sync (obj, list, NULL, NULL);
+ else if (options.enqueue)
+ obj_audacious_call_add_list_sync (obj, list, NULL, NULL);
+ else
+ obj_audacious_call_open_list_sync (obj, list, NULL, NULL);
- for (int f = index_count (filenames); f --; )
- list = g_list_prepend (list, index_get (filenames, f));
+ g_free (list);
+ }
- if (options.enqueue_to_temp)
- audacious_remote_playlist_open_list_to_temp (session, list);
- else if (options.enqueue)
- audacious_remote_playlist_add (session, list);
- else
- audacious_remote_playlist_open_list (session, list);
+ if (options.play)
+ obj_audacious_call_play_sync (obj, NULL, NULL);
+ if (options.pause)
+ obj_audacious_call_pause_sync (obj, NULL, NULL);
+ if (options.play_pause)
+ obj_audacious_call_play_pause_sync (obj, NULL, NULL);
+ if (options.stop)
+ obj_audacious_call_stop_sync (obj, NULL, NULL);
+ if (options.rew)
+ obj_audacious_call_reverse_sync (obj, NULL, NULL);
+ if (options.fwd)
+ obj_audacious_call_advance_sync (obj, NULL, NULL);
+ if (options.show_jump_box)
+ obj_audacious_call_show_jtf_box_sync (obj, TRUE, NULL, NULL);
+ if (options.mainwin)
+ obj_audacious_call_show_main_win_sync (obj, TRUE, NULL, NULL);
- g_list_free (list);
+ g_free (version);
+ g_object_unref (obj);
- for (int f = 0; f < index_count (filenames); f ++)
- str_unref (index_get (filenames, f));
+ exit (EXIT_SUCCESS);
- index_free (filenames);
- }
+ERR:
+ fprintf (stderr, "D-Bus error: %s\n", error->message);
+ g_error_free (error);
- if (options.play)
- audacious_remote_play (session);
- if (options.pause)
- audacious_remote_pause (session);
- if (options.play_pause)
- audacious_remote_play_pause (session);
- if (options.stop)
- audacious_remote_stop (session);
- if (options.rew)
- audacious_remote_playlist_prev (session);
- if (options.fwd)
- audacious_remote_playlist_next (session);
- if (options.show_jump_box)
- audacious_remote_show_jtf_box (session);
- if (options.mainwin)
- audacious_remote_main_win_toggle (session, TRUE);
-
- exit (EXIT_SUCCESS);
- }
-#endif
+DONE:
+ if (obj)
+ g_object_unref (obj);
- fprintf (stderr, "WARNING: Audacious seems to be already running but is not responding.\n");
+ return;
}
+#endif
static void do_commands (void)
{
bool_t resume = get_bool (NULL, "resume_playback_on_startup");
- Index * filenames = convert_filenames ();
if (filenames)
{
if (options.enqueue_to_temp)
@@ -417,6 +427,8 @@ static void do_commands (void)
drct_pl_open_list (filenames);
resume = FALSE;
}
+
+ filenames = NULL;
}
if (resume)
@@ -430,14 +442,31 @@ static void do_commands (void)
drct_pause ();
}
- if (options.show_jump_box)
- interface_show_jump_to_track ();
- if (options.mainwin)
+ if (options.show_jump_box && ! options.headless)
+ audgui_jump_to_track ();
+ if (options.mainwin && ! options.headless)
interface_show (TRUE);
}
+static void main_cleanup (void)
+{
+ for (int i = 0; i < AUD_PATH_COUNT; i ++)
+ str_unref (aud_paths[i]);
+
+ if (filenames)
+ index_free_full (filenames, (IndexFreeFunc) str_unref);
+
+ strpool_shutdown ();
+}
+
static void init_one (void)
{
+ atexit (main_cleanup);
+
+#ifdef HAVE_SIGWAIT
+ signals_init_one ();
+#endif
+
init_paths ();
make_dirs ();
@@ -447,17 +476,20 @@ static void init_one (void)
bindtextdomain (PACKAGE "-plugins", aud_paths[AUD_PATH_LOCALE_DIR]);
bind_textdomain_codeset (PACKAGE "-plugins", "UTF-8");
textdomain (PACKAGE);
+
+#if ! GLIB_CHECK_VERSION (2, 36, 0)
+ g_type_init ();
+#endif
}
-static void init_two (int * p_argc, char * * * p_argv)
+static void init_two (void)
{
- if (! headless)
- {
-#if ! GLIB_CHECK_VERSION (2, 32, 0)
- g_thread_init (NULL);
+ if (! options.headless)
+ gtk_init (NULL, NULL);
+
+#ifdef HAVE_SIGWAIT
+ signals_init_two ();
#endif
- gtk_init (p_argc, p_argv);
- }
AUDDBG ("Loading configuration.\n");
config_load ();
@@ -468,10 +500,6 @@ static void init_two (int * p_argc, char * * * p_argv)
eq_init ();
playlist_init ();
-#ifdef HAVE_SIGWAIT
- signals_init ();
-#endif
-
tag_set_verbose (verbose);
vfs_set_verbose (verbose);
@@ -491,16 +519,12 @@ static void init_two (int * p_argc, char * * * p_argv)
start_plugins_two ();
#ifdef USE_DBUS
- init_dbus ();
+ dbus_server_init ();
#endif
-
- mpris_signals_init ();
}
static void shut_down (void)
{
- mpris_signals_cleanup ();
-
AUDDBG ("Saving playlist state.\n");
save_playlists (TRUE);
@@ -508,7 +532,7 @@ static void shut_down (void)
stop_plugins_two ();
#ifdef USE_DBUS
- cleanup_dbus ();
+ dbus_server_cleanup ();
#endif
AUDDBG ("Stopping playback.\n");
@@ -522,17 +546,18 @@ static void shut_down (void)
AUDDBG ("Unloading lowlevel plugins.\n");
stop_plugins_one ();
+ event_queue_cancel_all ();
+
AUDDBG ("Saving configuration.\n");
config_save ();
config_cleanup ();
AUDDBG ("Cleaning up.\n");
art_cleanup ();
+ chardet_cleanup ();
eq_cleanup ();
history_cleanup ();
playlist_end ();
-
- strpool_shutdown ();
}
bool_t do_autosave (void)
@@ -555,10 +580,21 @@ static void maybe_quit (void)
gtk_main_quit ();
}
-int main(int argc, char ** argv)
+int main (int argc, char * * argv)
{
init_one ();
- parse_options (& argc, & argv);
+
+ if (! parse_options (argc, argv))
+ {
+ print_help ();
+ return EXIT_FAILURE;
+ }
+
+ if (options.help)
+ {
+ print_help ();
+ return EXIT_SUCCESS;
+ }
if (options.version)
{
@@ -566,11 +602,12 @@ int main(int argc, char ** argv)
return EXIT_SUCCESS;
}
- if (! get_lock ())
- do_remote (); /* may exit */
+#if USE_DBUS
+ do_remote (); /* may exit */
+#endif
AUDDBG ("No remote session; starting up.\n");
- init_two (& argc, & argv);
+ init_two ();
AUDDBG ("Startup complete.\n");
g_timeout_add_seconds (AUTOSAVE_INTERVAL, (GSourceFunc) do_autosave, NULL);
@@ -580,16 +617,18 @@ int main(int argc, char ** argv)
hook_associate ("playback stop", (HookFunction) maybe_quit, NULL);
hook_associate ("playlist add complete", (HookFunction) maybe_quit, NULL);
- hook_associate ("quit", (HookFunction) gtk_main_quit, NULL);
gtk_main ();
hook_dissociate ("playback stop", (HookFunction) maybe_quit);
hook_dissociate ("playlist add complete", (HookFunction) maybe_quit);
- hook_dissociate ("quit", (HookFunction) gtk_main_quit);
QUIT:
shut_down ();
- release_lock ();
return EXIT_SUCCESS;
}
+
+void drct_quit (void)
+{
+ gtk_main_quit ();
+}
diff --git a/src/audacious/main.h b/src/audacious/main.h
index 81f9a14..0fa3432 100644
--- a/src/audacious/main.h
+++ b/src/audacious/main.h
@@ -1,6 +1,6 @@
/*
* main.h
- * Copyright 2011 John Lindgren
+ * Copyright 2011-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -32,8 +32,15 @@ void adder_cleanup (void);
void art_init (void);
void art_cleanup (void);
+/* dbus-server.c */
+#ifdef USE_DBUS
+void dbus_server_init (void);
+void dbus_server_cleanup (void);
+#endif
+
/* chardet.c */
void chardet_init (void);
+void chardet_cleanup (void);
/* config.c */
void config_load (void);
@@ -44,17 +51,15 @@ void config_cleanup (void);
void history_cleanup (void);
/* main.c */
-extern bool_t headless;
bool_t do_autosave (void);
-/* mpris-signals.c */
-void mpris_signals_init (void);
-void mpris_signals_cleanup (void);
-
/* signals.c */
-void signals_init (void);
+#ifdef HAVE_SIGWAIT
+void signals_init_one (void);
+void signals_init_two (void);
+#endif
/* ui_albumart.c */
-char * get_associated_image_file (const char * filename);
+char * get_associated_image_file (const char * filename); /* pooled */
#endif
diff --git a/src/audacious/misc-api.h b/src/audacious/misc-api.h
index 5ebff3d..b9ea2a5 100644
--- a/src/audacious/misc-api.h
+++ b/src/audacious/misc-api.h
@@ -1,6 +1,6 @@
/*
* misc-api.h
- * Copyright 2010-2011 John Lindgren
+ * Copyright 2010-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -19,22 +19,30 @@
/* Do not include this file directly; use misc.h instead. */
+/* all (char *) return values must be freed with str_unref() */
+
/* art.c (thread-safe) */
-/* deprecated; use the non-blocking art_request_* functions instead */
-AUD_VFUNC3 (art_get_data, const char *, file, const void * *, data, int64_t *, len)
-AUD_FUNC1 (const char *, art_get_file, const char *, file)
+/* Gets album art for <file> (the URI of a song file) as JPEG or PNG data. If
+ * the album art is not yet loaded, sets <data> to NULL and begins to load the
+ * album art in the background. On completion, the "art ready" hook is called,
+ * with <file> as a parameter. The "current art ready" hook is also called if
+ * <file> is the currently playing song. */
+AUD_VFUNC3 (art_request_data, const char *, file, const void * *, data, int64_t *, len)
+
+/* Similar to art_request_data() but returns the URI of an image file.
+ * (A temporary file will be created if necessary.) */
+AUD_FUNC1 (const char *, art_request_file, const char *, file)
/* Releases album art returned by art_request_data() or art_request_file(). */
AUD_VFUNC1 (art_unref, const char *, file)
-/* config.c */
+/* config.c (thread-safe) */
-AUD_VFUNC1 (config_clear_section, const char *, section)
AUD_VFUNC2 (config_set_defaults, const char *, section, const char * const *, entries)
-AUD_VFUNC3 (set_string, const char *, section, const char *, name, const char *, value)
-AUD_FUNC2 (char *, get_string, const char *, section, const char *, name)
+AUD_VFUNC3 (set_str, const char *, section, const char *, name, const char *, value)
+AUD_FUNC2 (char *, get_str, const char *, section, const char *, name)
AUD_VFUNC3 (set_bool, const char *, section, const char *, name, bool_t, value)
AUD_FUNC2 (bool_t, get_bool, const char *, section, const char *, name)
AUD_VFUNC3 (set_int, const char *, section, const char *, name, int, value)
@@ -49,11 +57,17 @@ AUD_VFUNC2 (eq_set_band, int, band, double, value)
AUD_FUNC1 (double, eq_get_band, int, band)
/* equalizer_preset.c */
+AUD_FUNC1 (EqualizerPreset *, equalizer_preset_new, const char *, name)
+AUD_VFUNC1 (equalizer_preset_free, EqualizerPreset *, preset)
AUD_FUNC1 (Index *, equalizer_read_presets, const char *, basename)
-AUD_FUNC2 (bool_t, equalizer_write_preset_file, Index *, list, const char *, basename)
+AUD_FUNC2 (bool_t, equalizer_write_presets, Index *, list, const char *, basename)
+
+/* note: legacy code! these are local filenames, not URIs */
AUD_FUNC1 (EqualizerPreset *, load_preset_file, const char *, filename)
AUD_FUNC2 (bool_t, save_preset_file, EqualizerPreset *, preset, const char *, filename)
-AUD_FUNC1 (Index *, import_winamp_eqf, VFSFile *, file)
+
+AUD_FUNC1 (Index *, import_winamp_presets, VFSFile *, file)
+AUD_FUNC2 (bool_t, export_winamp_preset, EqualizerPreset *, preset, VFSFile *, file)
/* history.c */
AUD_FUNC1 (const char *, history_get, int, entry)
@@ -62,19 +76,13 @@ AUD_VFUNC1 (history_add, const char *, path)
/* interface.c */
AUD_VFUNC1 (interface_show, bool_t, show)
AUD_FUNC0 (bool_t, interface_is_shown)
-AUD_FUNC0 (bool_t, interface_is_focused)
/* interface_show_error() is safe to call from any thread */
AUD_VFUNC1 (interface_show_error, const char *, message)
-AUD_VFUNC1 (interface_show_filebrowser, bool_t, play)
-AUD_VFUNC0 (interface_show_jump_to_track)
-
-AUD_VFUNC1 (interface_install_toolbar, void *, button)
-AUD_VFUNC1 (interface_uninstall_toolbar, void *, button)
-
/* main.c */
AUD_FUNC1 (const char *, get_path, int, path)
+AUD_FUNC0 (bool_t, headless_mode)
/* output.c */
AUD_VFUNC1 (output_reset, int, type)
@@ -103,23 +111,18 @@ AUD_VFUNC2 (plugin_menu_remove, int, id, MenuFunc, func)
AUD_VFUNC4 (create_widgets_with_domain, /* GtkWidget * */ void *, box,
const PreferencesWidget *, widgets, int, n_widgets, const char *, domain)
AUD_VFUNC0 (show_prefs_window)
+AUD_VFUNC1 (show_prefs_for_plugin_type, int, type)
/* util.c */
-AUD_FUNC2 (char *, construct_uri, const char *, base, const char *, reference)
+
+/* Constructs a full URI given:
+ * 1. path: one of the following:
+ * a. a full URI (returned unchanged)
+ * b. an absolute filename (in the system locale)
+ * c. a relative path (character set detected according to user settings)
+ * 2. reference: the full URI of the playlist containing <path> */
+AUD_FUNC2 (char *, construct_uri, const char *, path, const char *, reference)
/* visualization.c */
AUD_VFUNC2 (vis_func_add, int, type, VisFunc, func)
AUD_VFUNC1 (vis_func_remove, VisFunc, func)
-
-/* added in Audacious 3.4 */
-
-/* Gets album art for <file> (the URI of a song file) as JPEG or PNG data. If
- * the album art is not yet loaded, sets <data> to NULL and begins to load the
- * album art in the background. On completion, the "art ready" hook is called,
- * with <file> as a parameter. The "current art ready" hook is also called if
- * <file> is the currently playing song. */
-AUD_VFUNC3 (art_request_data, const char *, file, const void * *, data, int64_t *, len)
-
-/* Similar to art_request_data() but returns the URI of an image file.
- * (A temporary file will be created if necessary.) */
-AUD_FUNC1 (const char *, art_request_file, const char *, file)
diff --git a/src/audacious/misc.h b/src/audacious/misc.h
index c214eb6..3d25517 100644
--- a/src/audacious/misc.h
+++ b/src/audacious/misc.h
@@ -1,6 +1,6 @@
/*
* misc.h
- * Copyright 2010-2011 John Lindgren
+ * Copyright 2010-2012 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -34,27 +34,17 @@ enum {
AUD_PATH_DESKTOP_FILE,
AUD_PATH_ICON_FILE,
AUD_PATH_USER_DIR,
- AUD_PATH_USER_PLUGIN_DIR,
AUD_PATH_PLAYLISTS_DIR,
- AUD_PATH_GTKRC_FILE, /* deprecated */
AUD_PATH_COUNT
};
-typedef struct {
- char * name;
- float preamp, bands[10];
-} EqualizerPreset;
-
enum {OUTPUT_RESET_EFFECTS_ONLY, OUTPUT_RESET_SOFT, OUTPUT_RESET_HARD};
enum {
AUD_MENU_MAIN,
AUD_MENU_PLAYLIST,
- AUD_MENU_PLAYLIST_RCLICK,
AUD_MENU_PLAYLIST_ADD,
AUD_MENU_PLAYLIST_REMOVE,
- AUD_MENU_PLAYLIST_SELECT,
- AUD_MENU_PLAYLIST_MISC,
AUD_MENU_COUNT};
typedef void (* MenuFunc) (void);
diff --git a/src/audacious/mpris-signals.c b/src/audacious/mpris-signals.c
deleted file mode 100644
index a5d55fb..0000000
--- a/src/audacious/mpris-signals.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * mpris-signals.c
- * Copyright 2011 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.
- */
-
-#ifdef USE_DBUS
-
-#include <libaudcore/hook.h>
-
-#include "dbus-service.h"
-#include "main.h"
-
-static void mpris_status_cb (void * hook_data, void * user_data)
-{
- mpris_emit_status_change (mpris, GPOINTER_TO_INT (user_data));
-}
-#endif
-
-void mpris_signals_init (void)
-{
-#ifdef USE_DBUS
- hook_associate ("playback begin", mpris_status_cb, GINT_TO_POINTER
- (MPRIS_STATUS_PLAY));
- hook_associate ("playback pause", mpris_status_cb, GINT_TO_POINTER
- (MPRIS_STATUS_PAUSE));
- hook_associate ("playback unpause", mpris_status_cb, GINT_TO_POINTER
- (MPRIS_STATUS_PLAY));
- hook_associate ("playback stop", mpris_status_cb, GINT_TO_POINTER
- (MPRIS_STATUS_STOP));
-
- hook_associate ("set shuffle", mpris_status_cb, GINT_TO_POINTER (MPRIS_STATUS_INVALID));
- hook_associate ("set repeat", mpris_status_cb, GINT_TO_POINTER (MPRIS_STATUS_INVALID));
- hook_associate ("set no_playlist_advance", mpris_status_cb, GINT_TO_POINTER
- (MPRIS_STATUS_INVALID));
-#endif
-}
-
-void mpris_signals_cleanup (void)
-{
-#ifdef USE_DBUS
- hook_dissociate ("playback begin", mpris_status_cb);
- hook_dissociate ("playback pause", mpris_status_cb);
- hook_dissociate ("playback unpause", mpris_status_cb);
- hook_dissociate ("playback stop", mpris_status_cb);
-
- hook_dissociate ("set shuffle", mpris_status_cb);
- hook_dissociate ("set repeat", mpris_status_cb);
- hook_dissociate ("set no_playlist_advance", mpris_status_cb);
-#endif
-}
diff --git a/src/audacious/mpris_player.xml b/src/audacious/mpris_player.xml
deleted file mode 100644
index c954f8b..0000000
--- a/src/audacious/mpris_player.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-
-<!--
- * mpris_player.xml
- * Copyright 2007 William Pitcock and Ben Tucker
- *
- * 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.
- -->
-
-<node name="/Player">
- <interface name="org.freedesktop.MediaPlayer">
- <method name="Next">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
- <method name="Prev">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
- <method name="Pause">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
- <method name="Stop">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
- <method name="Play">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
- <method name="Repeat">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="b" direction="in" />
- </method>
- <method name="GetStatus">
- <arg type="(iiii)" direction="out" />
- </method>
- <method name="GetMetadata">
- <arg type="a{sv}" direction="out" />
- </method>
- <method name="GetCaps">
- <arg type="i" direction="out" />
- </method>
- <method name="VolumeSet">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="i" direction="in" />
- </method>
- <method name="VolumeGet">
- <arg type="i" direction="out" />
- </method>
- <method name="PositionSet">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="i" direction="in" />
- </method>
- <method name="PositionGet">
- <arg type="i" direction="out" />
- </method>
-
- <signal name="TrackChange">
- <arg type="a{sv}" />
- </signal>
- <signal name="StatusChange">
- <arg type="(iiii)" />
- </signal>
- <signal name="CapsChange">
- <arg type="i" />
- </signal>
- </interface>
-</node>
diff --git a/src/audacious/mpris_root.xml b/src/audacious/mpris_root.xml
deleted file mode 100644
index 7d84ded..0000000
--- a/src/audacious/mpris_root.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-
-<!--
- * mpris_root.xml
- * Copyright 2007 William Pitcock
- *
- * 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.
- -->
-
-<node name="/">
- <interface name="org.freedesktop.MediaPlayer">
- <method name="Identity">
- <arg type="s" direction="out" />
- </method>
- <method name="Quit">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
- </interface>
-</node>
diff --git a/src/audacious/mpris_tracklist.xml b/src/audacious/mpris_tracklist.xml
deleted file mode 100644
index 46859a2..0000000
--- a/src/audacious/mpris_tracklist.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-
-<!--
- * mpris_tracklist.xml
- * Copyright 2007 William Pitcock and Ben Tucker
- *
- * 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.
- -->
-
-<node name="/TrackList">
- <interface name="org.freedesktop.MediaPlayer">
- <method name="GetMetadata">
- <arg type="i" direction="in" />
- <arg type="a{sv}" direction="out" />
- </method>
- <method name="GetCurrentTrack">
- <arg type="i" direction="out" />
- </method>
- <method name="GetLength">
- <arg type="i" direction="out" />
- </method>
- <method name="AddTrack">
- <arg type="s" direction="in" />
- <arg type="b" direction="in" />
- </method>
- <method name="DelTrack">
- <arg type="i" direction="in" />
- </method>
- <method name="Loop">
- <arg type="b" direction="in" />
- </method>
- <method name="Random">
- <arg type="b" direction="in" />
- </method>
-
- <signal name="TrackListChange">
- <arg type="i" />
- </signal>
- </interface>
-</node>
diff --git a/src/audacious/output.c b/src/audacious/output.c
index dda7300..b6f862a 100644
--- a/src/audacious/output.c
+++ b/src/audacious/output.c
@@ -1,6 +1,6 @@
/*
* output.c
- * Copyright 2009-2012 John Lindgren
+ * Copyright 2009-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -24,8 +24,6 @@
#include <glib.h> /* for g_usleep */
-#include <libaudcore/hook.h>
-
#include "debug.h"
#include "effect.h"
#include "equalizer.h"
@@ -68,8 +66,13 @@ static ReplayGainInfo gain_info;
static bool_t change_op;
static OutputPlugin * new_op;
+static void * buffer1, * buffer2;
+static int buffer1_size, buffer2_size;
+
static inline int FR2MS (int64_t f, int r)
{ return (f > 0) ? (f * 1000 + r / 2) / r : (f * 1000 - r / 2) / r; }
+static inline int MS2FR (int64_t ms, int r)
+ { return (ms > 0) ? (ms * r + 500) / 1000 : (ms * r - 500) / 1000; }
static inline int get_format (void)
{
@@ -82,6 +85,16 @@ static inline int get_format (void)
}
}
+static void ensure_buffer (void * * buffer, int * size, int newsize)
+{
+ if (newsize > * size)
+ {
+ g_free (* buffer);
+ * buffer = g_malloc (newsize);
+ * size = newsize;
+ }
+}
+
/* assumes LOCK_ALL, s_output */
static void cleanup_output (void)
{
@@ -94,6 +107,13 @@ static void cleanup_output (void)
s_output = FALSE;
+ g_free (buffer1);
+ g_free (buffer2);
+ buffer1 = NULL;
+ buffer2 = NULL;
+ buffer1_size = 0;
+ buffer2_size = 0;
+
if (PLUGIN_HAS_FUNC (cop, close_audio))
cop->close_audio ();
@@ -217,8 +237,6 @@ static void apply_software_volume (float * data, int channels, int samples)
/* assumes LOCK_ALL, s_output */
static void write_output_raw (void * data, int samples)
{
- void * buffer = NULL;
-
vis_runner_pass_audio (FR2MS (out_frames, out_rate), data, samples,
out_channels, out_rate);
out_frames += samples / out_channels;
@@ -231,9 +249,9 @@ static void write_output_raw (void * data, int samples)
if (out_format != FMT_FLOAT)
{
- buffer = malloc (FMT_SIZEOF (out_format) * samples);
- audio_to_int (data, buffer, out_format, samples);
- data = buffer;
+ ensure_buffer (& buffer2, & buffer2_size, FMT_SIZEOF (out_format) * samples);
+ audio_to_int (data, buffer2, out_format, samples);
+ data = buffer2;
}
while (! (s_aborted || s_resetting))
@@ -270,26 +288,39 @@ static void write_output_raw (void * data, int samples)
LOCK_MINOR;
}
-
- free (buffer);
}
/* assumes LOCK_ALL, s_input, s_output */
-static void write_output (void * data, int size)
+static bool_t write_output (void * data, int size, int stop_time)
{
- void * buffer = NULL;
+ bool_t stopped = FALSE;
+ int64_t cur_frame = in_frames;
int samples = size / FMT_SIZEOF (in_format);
+
+ /* always update in_frames, whether we use all the decoded frames or not */
in_frames += samples / in_channels;
+ if (stop_time != -1)
+ {
+ int64_t frames_left = MS2FR (stop_time - seek_time, in_rate) - cur_frame;
+ int64_t samples_left = in_channels * MAX (0, frames_left);
+
+ if (samples >= samples_left)
+ {
+ samples = samples_left;
+ stopped = TRUE;
+ }
+ }
+
if (s_aborted)
- return;
+ return ! stopped;
if (in_format != FMT_FLOAT)
{
- buffer = malloc (sizeof (float) * samples);
- audio_from_int (data, in_format, buffer, samples);
- data = buffer;
+ ensure_buffer (& buffer1, & buffer1_size, sizeof (float) * samples);
+ audio_from_int (data, in_format, buffer1, samples);
+ data = buffer1;
}
float * fdata = data;
@@ -297,7 +328,7 @@ static void write_output (void * data, int size)
effect_process (& fdata, & samples);
write_output_raw (fdata, samples);
- free (buffer);
+ return ! stopped;
}
/* assumes LOCK_ALL, s_output */
@@ -359,9 +390,11 @@ void output_set_replaygain_info (const ReplayGainInfo * info)
UNLOCK_ALL;
}
-void output_write_audio (void * data, int size)
+/* returns FALSE if stop_time is reached */
+bool_t output_write_audio (void * data, int size, int stop_time)
{
LOCK_ALL;
+ bool_t good = FALSE;
if (s_input)
{
@@ -372,10 +405,11 @@ void output_write_audio (void * data, int size)
LOCK_ALL;
}
- write_output (data, size);
+ good = write_output (data, size, stop_time);
}
UNLOCK_ALL;
+ return good;
}
void output_abort_write (void)
@@ -432,9 +466,6 @@ void output_set_time (int time)
}
UNLOCK_ALL;
-
- /* See comment in playback_seek(). */
- event_queue ("playback seek", NULL);
}
bool_t output_is_open (void)
diff --git a/src/audacious/output.h b/src/audacious/output.h
index 8d8ffe1..ac9ee48 100644
--- a/src/audacious/output.h
+++ b/src/audacious/output.h
@@ -1,6 +1,6 @@
/*
* output.h
- * Copyright 2010-2011 John Lindgren
+ * Copyright 2010-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -25,7 +25,7 @@
bool_t output_open_audio (int format, int rate, int channels);
void output_set_replaygain_info (const ReplayGainInfo * info);
-void output_write_audio (void * data, int length);
+bool_t output_write_audio (void * data, int size, int stop_time);
void output_abort_write (void);
void output_pause (bool_t pause);
int output_written_time (void);
diff --git a/src/audacious/playback.c b/src/audacious/playback.c
index 2f1a1b3..7d9c56f 100644
--- a/src/audacious/playback.c
+++ b/src/audacious/playback.c
@@ -1,6 +1,6 @@
/*
* playback.c
- * Copyright 2009-2012 John Lindgren
+ * Copyright 2009-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -23,9 +23,11 @@
#include <libaudcore/audstrings.h>
#include <libaudcore/hook.h>
+#include <libaudgui/libaudgui.h>
#include "drct.h"
#include "i18n.h"
+#include "input.h"
#include "interface.h"
#include "misc.h"
#include "output.h"
@@ -33,38 +35,31 @@
#include "playlist.h"
#include "plugin.h"
-static void playback_restart (int seek_time, bool_t pause);
-
-static const struct OutputAPI output_api = {
- .open_audio = output_open_audio,
- .set_replaygain_info = output_set_replaygain_info,
- .write_audio = output_write_audio,
- .abort_write = output_abort_write,
- .pause = output_pause,
- .written_time = output_written_time,
- .flush = output_set_time};
-
-static InputPlayback playback_api;
-
static pthread_t playback_thread_handle;
static int end_source = 0;
static pthread_mutex_t ready_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t ready_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t control_mutex = PTHREAD_MUTEX_INITIALIZER;
+
/* level 1 data (persists to end of song) */
static bool_t playing = FALSE;
-static bool_t restart_flag = FALSE;
-static int time_offset = 0, initial_seek = 0;
+static int time_offset = 0;
+static int stop_time = -1;
static bool_t paused = FALSE;
static bool_t ready_flag = FALSE;
static bool_t playback_error = FALSE;
static bool_t song_finished = FALSE;
-static void * current_data = NULL;
+static int seek_request = -1; /* under control_mutex */
+static int repeat_a = -1; /* under control_mutex */
+
+static volatile int repeat_b = -1; /* atomic */
+static volatile int stop_flag = FALSE; /* atomic */
+
static int current_bitrate = -1, current_samplerate = -1, current_channels = -1;
-/* level 2 data (persists when restarting same song) */
static int current_entry = -1;
static char * current_filename = NULL; /* pooled */
static char * current_title = NULL; /* pooled */
@@ -72,39 +67,37 @@ static int current_length = -1;
static InputPlugin * current_decoder = NULL;
static VFSFile * current_file = NULL;
-static ReplayGainInfo gain_from_playlist;
-
-static int repeat_a = -1, repeat_b = -1;
+static ReplayGainInfo current_gain;
-/* level 3 data (persists to end of playlist) */
+/* level 2 data (persists to end of playlist) */
static bool_t stopped = TRUE;
static int failed_entries = 0;
/* clears gain info if tuple == NULL */
static void read_gain_from_tuple (const Tuple * tuple)
{
- memset (& gain_from_playlist, 0, sizeof gain_from_playlist);
+ memset (& current_gain, 0, sizeof current_gain);
if (tuple == NULL)
return;
- int album_gain = tuple_get_int (tuple, FIELD_GAIN_ALBUM_GAIN, NULL);
- int album_peak = tuple_get_int (tuple, FIELD_GAIN_ALBUM_PEAK, NULL);
- int track_gain = tuple_get_int (tuple, FIELD_GAIN_TRACK_GAIN, NULL);
- int track_peak = tuple_get_int (tuple, FIELD_GAIN_TRACK_PEAK, NULL);
- int gain_unit = tuple_get_int (tuple, FIELD_GAIN_GAIN_UNIT, NULL);
- int peak_unit = tuple_get_int (tuple, FIELD_GAIN_PEAK_UNIT, NULL);
+ int album_gain = tuple_get_int (tuple, FIELD_GAIN_ALBUM_GAIN);
+ int album_peak = tuple_get_int (tuple, FIELD_GAIN_ALBUM_PEAK);
+ int track_gain = tuple_get_int (tuple, FIELD_GAIN_TRACK_GAIN);
+ int track_peak = tuple_get_int (tuple, FIELD_GAIN_TRACK_PEAK);
+ int gain_unit = tuple_get_int (tuple, FIELD_GAIN_GAIN_UNIT);
+ int peak_unit = tuple_get_int (tuple, FIELD_GAIN_PEAK_UNIT);
if (gain_unit)
{
- gain_from_playlist.album_gain = album_gain / (float) gain_unit;
- gain_from_playlist.track_gain = track_gain / (float) gain_unit;
+ current_gain.album_gain = album_gain / (float) gain_unit;
+ current_gain.track_gain = track_gain / (float) gain_unit;
}
if (peak_unit)
{
- gain_from_playlist.album_peak = album_peak / (float) peak_unit;
- gain_from_playlist.track_peak = track_peak / (float) peak_unit;
+ current_gain.album_peak = album_peak / (float) peak_unit;
+ current_gain.track_peak = track_peak / (float) peak_unit;
}
}
@@ -114,8 +107,7 @@ static bool_t update_from_playlist (void)
char * title = playback_entry_get_title ();
int length = playback_entry_get_length ();
- /* pointer comparison works for pooled strings */
- if (entry == current_entry && title == current_title && length == current_length)
+ if (entry == current_entry && str_equal (title, current_title) && length == current_length)
{
str_unref (title);
return FALSE;
@@ -134,26 +126,19 @@ bool_t drct_get_ready (void)
return FALSE;
pthread_mutex_lock (& ready_mutex);
-
- /* on restart, always report ready */
- bool_t ready = ready_flag || restart_flag;
-
+ bool_t ready = ready_flag;
pthread_mutex_unlock (& ready_mutex);
return ready;
}
-static void set_pb_ready (InputPlayback * p)
+static void set_ready (void)
{
g_return_if_fail (playing);
- pthread_mutex_lock (& ready_mutex);
- /* on restart, don't update or send "playback ready" */
- if (! restart_flag)
- {
- update_from_playlist ();
- event_queue ("playback ready", NULL);
- }
+ pthread_mutex_lock (& ready_mutex);
+ update_from_playlist ();
+ event_queue ("playback ready", NULL);
ready_flag = TRUE;
pthread_cond_signal (& ready_cond);
@@ -190,15 +175,7 @@ int drct_get_time (void)
wait_until_ready ();
- int time = -1;
-
- if (current_decoder && current_decoder->get_time)
- time = current_decoder->get_time (& playback_api);
-
- if (time < 0)
- time = output_get_time ();
-
- return time - time_offset;
+ return output_get_time () - time_offset;
}
void drct_pause (void)
@@ -208,11 +185,9 @@ void drct_pause (void)
wait_until_ready ();
- if (! current_decoder || ! current_decoder->pause)
- return;
-
paused = ! paused;
- current_decoder->pause (& playback_api, paused);
+
+ output_pause (paused);
if (paused)
hook_call ("playback pause", NULL);
@@ -220,21 +195,27 @@ void drct_pause (void)
hook_call ("playback unpause", NULL);
}
-static void playback_finish (void)
+static void playback_cleanup (void)
{
g_return_if_fail (playing);
wait_until_ready ();
- /* calling stop() is unnecessary if the song finished on its own;
- * also, it might flush the output buffer, breaking gapless playback */
- if (current_decoder && ! song_finished)
- current_decoder->stop (& playback_api);
+ if (! song_finished)
+ {
+ g_atomic_int_set (& stop_flag, TRUE);
+ output_abort_write ();
+ }
pthread_join (playback_thread_handle, NULL);
output_close_audio ();
hook_dissociate ("playlist update", update_cb);
+ event_queue_cancel ("playback ready", NULL);
+ event_queue_cancel ("playback seek", NULL);
+ event_queue_cancel ("info change", NULL);
+ event_queue_cancel ("title change", NULL);
+
if (end_source)
{
g_source_remove (end_source);
@@ -243,30 +224,21 @@ static void playback_finish (void)
/* level 1 data cleanup */
playing = FALSE;
- restart_flag = FALSE;
- time_offset = initial_seek = 0;
+ time_offset = 0;
+ stop_time = -1;
paused = FALSE;
ready_flag = FALSE;
playback_error = FALSE;
song_finished = FALSE;
- current_data = NULL;
- current_bitrate = current_samplerate = current_channels = -1;
-}
-
-static void playback_cleanup (void)
-{
- g_return_if_fail (current_filename);
- playback_finish ();
+ seek_request = -1;
+ repeat_a = -1;
- event_queue_cancel ("playback ready", NULL);
- event_queue_cancel ("playback seek", NULL);
- event_queue_cancel ("info change", NULL);
- event_queue_cancel ("title change", NULL);
+ g_atomic_int_set (& repeat_b, -1);
+ g_atomic_int_set (& stop_flag, FALSE);
- set_bool (NULL, "stop_after_current_song", FALSE);
+ current_bitrate = current_samplerate = current_channels = -1;
- /* level 2 data cleanup */
current_entry = -1;
str_unref (current_filename);
current_filename = NULL;
@@ -284,7 +256,7 @@ static void playback_cleanup (void)
read_gain_from_tuple (NULL);
- repeat_a = repeat_b = -1;
+ set_bool (NULL, "stop_after_current_song", FALSE);
}
void playback_stop (void)
@@ -292,12 +264,12 @@ void playback_stop (void)
if (stopped)
return;
- if (current_filename)
+ if (playing)
playback_cleanup ();
output_drain ();
- /* level 3 data cleanup */
+ /* level 2 data cleanup */
stopped = TRUE;
failed_entries = 0;
@@ -342,17 +314,10 @@ static bool_t end_cb (void * unused)
if (! get_bool (NULL, "no_playlist_advance"))
do_next (playlist);
}
- else if (repeat_a >= 0 || repeat_b >= 0)
- {
- if (! failed_entries)
- playback_restart (MAX (repeat_a, 0), FALSE);
- else
- do_stop (playlist);
- }
else if (get_bool (NULL, "no_playlist_advance"))
{
if (get_bool (NULL, "repeat") && ! failed_entries)
- playback_restart (0, FALSE);
+ playback_play (0, FALSE);
else
do_stop (playlist);
}
@@ -367,6 +332,16 @@ static bool_t end_cb (void * unused)
return FALSE;
}
+static bool_t open_file (void)
+{
+ /* no need to open a handle for custom URI schemes */
+ if (current_decoder->schemes && current_decoder->schemes[0])
+ return TRUE;
+
+ current_file = vfs_fopen (current_filename, "r");
+ return (current_file != NULL);
+}
+
static void * playback_thread (void * unused)
{
if (! current_decoder)
@@ -384,87 +359,66 @@ static void * playback_thread (void * unused)
}
Tuple * tuple = playback_entry_get_tuple ();
- read_gain_from_tuple (tuple);
+ int length = playback_entry_get_length ();
- int start_time = time_offset = 0;
- int end_time = -1;
+ if (length < 1)
+ seek_request = -1;
- if (tuple && playback_entry_get_length () > 0)
+ if (tuple && length > 0)
{
- if (tuple_get_value_type (tuple, FIELD_SEGMENT_START, NULL) == TUPLE_INT)
- time_offset = tuple_get_int (tuple, FIELD_SEGMENT_START, NULL);
-
- start_time = time_offset + MAX (initial_seek, 0);
+ if (tuple_get_value_type (tuple, FIELD_SEGMENT_START) == TUPLE_INT)
+ {
+ time_offset = tuple_get_int (tuple, FIELD_SEGMENT_START);
+ if (time_offset)
+ seek_request = time_offset + MAX (seek_request, 0);
+ }
- if (repeat_b >= 0)
- end_time = time_offset + repeat_b;
- else if (tuple_get_value_type (tuple, FIELD_SEGMENT_END, NULL) == TUPLE_INT)
- end_time = tuple_get_int (tuple, FIELD_SEGMENT_END, NULL);
+ if (tuple_get_value_type (tuple, FIELD_SEGMENT_END) == TUPLE_INT)
+ stop_time = tuple_get_int (tuple, FIELD_SEGMENT_END);
}
+ read_gain_from_tuple (tuple);
+
if (tuple)
tuple_unref (tuple);
- if (! current_decoder->schemes || ! current_decoder->schemes[0])
+ if (! open_file ())
{
- if (current_file)
- vfs_rewind (current_file);
- else
- current_file = vfs_fopen (current_filename, "r");
-
- if (! current_file)
- {
- SPRINTF (error, _("%s could not be opened."), current_filename);
- interface_show_error (error);
- playback_error = TRUE;
- goto DONE;
- }
+ SPRINTF (error, _("%s could not be opened."), current_filename);
+ interface_show_error (error);
+ playback_error = TRUE;
+ goto DONE;
}
- playback_error = ! current_decoder->play (& playback_api, current_filename,
- current_file, start_time, end_time, paused);
+ playback_error = ! current_decoder->play (current_filename, current_file);
DONE:
if (! ready_flag)
- set_pb_ready (& playback_api);
+ set_ready ();
end_source = g_timeout_add (0, end_cb, NULL);
return NULL;
}
-static void playback_start (int seek_time, bool_t pause)
+void playback_play (int seek_time, bool_t pause)
{
- playing = TRUE;
- initial_seek = seek_time;
- paused = pause;
- stopped = FALSE;
+ char * new_filename = playback_entry_get_filename ();
+ g_return_if_fail (new_filename);
- hook_associate ("playlist update", update_cb, NULL);
- pthread_create (& playback_thread_handle, NULL, playback_thread, NULL);
-}
-
-static void playback_restart (int seek_time, bool_t pause)
-{
if (playing)
- playback_finish ();
-
- restart_flag = TRUE;
+ playback_cleanup ();
- playback_start (seek_time, pause);
+ current_filename = new_filename;
- /* on restart, send "playback seek" instead of "playback begin" */
- hook_call ("playback seek", NULL);
-}
+ playing = TRUE;
+ paused = pause;
-void playback_play (int seek_time, bool_t pause)
-{
- if (current_filename)
- playback_cleanup ();
+ seek_request = (seek_time > 0) ? seek_time : -1;
- current_filename = playback_entry_get_filename ();
- g_return_if_fail (current_filename);
+ stopped = FALSE;
- playback_start (seek_time, pause);
+ hook_associate ("playlist update", update_cb, NULL);
+ pthread_create (& playback_thread_handle, NULL, playback_thread, NULL);
hook_call ("playback begin", NULL);
}
@@ -486,67 +440,121 @@ void drct_seek (int time)
wait_until_ready ();
- if (! current_decoder || ! current_decoder->mseek || current_length < 1)
+ if (current_length < 1)
return;
- current_decoder->mseek (& playback_api, time_offset + CLAMP (time, 0,
- current_length));
+ pthread_mutex_lock (& control_mutex);
+
+ seek_request = time_offset + CLAMP (time, 0, current_length);
+ output_abort_write ();
- /* If the plugin is using our output system, don't call "playback seek"
- * immediately but wait for output_set_time() to be called. This ensures
- * that a "playback seek" handler can call playback_get_time() and get the
- * new time. */
- if (! output_is_open ())
- hook_call ("playback seek", NULL);
+ pthread_mutex_unlock (& control_mutex);
}
-static void set_data (InputPlayback * p, void * data)
+bool_t input_open_audio (int format, int rate, int channels)
{
- g_return_if_fail (playing);
- current_data = data;
+ g_return_val_if_fail (playing, FALSE);
+
+ if (! output_open_audio (format, rate, channels))
+ return FALSE;
+
+ output_set_replaygain_info (& current_gain);
+
+ if (paused)
+ output_pause (TRUE);
+
+ current_samplerate = rate;
+ current_channels = channels;
+
+ if (ready_flag)
+ event_queue ("info change", NULL);
+
+ return TRUE;
}
-static void * get_data (InputPlayback * p)
+void input_set_gain (const ReplayGainInfo * info)
{
- g_return_val_if_fail (playing, NULL);
- return current_data;
+ g_return_if_fail (playing);
+ memcpy (& current_gain, info, sizeof current_gain);
+ output_set_replaygain_info (& current_gain);
}
-static void set_params (InputPlayback * p, int bitrate, int samplerate,
- int channels)
+void input_write_audio (void * data, int length)
{
g_return_if_fail (playing);
- current_bitrate = bitrate;
- current_samplerate = samplerate;
- current_channels = channels;
+ if (! ready_flag)
+ set_ready ();
- if (drct_get_ready ())
- event_queue ("info change", NULL);
+ int b = g_atomic_int_get (& repeat_b);
+
+ if (b >= 0)
+ {
+ if (! output_write_audio (data, length, b))
+ {
+ pthread_mutex_lock (& control_mutex);
+ seek_request = MAX (repeat_a, time_offset);
+ pthread_mutex_unlock (& control_mutex);
+ }
+ }
+ else
+ {
+ if (! output_write_audio (data, length, stop_time))
+ g_atomic_int_set (& stop_flag, TRUE);
+ }
+}
+
+int input_written_time (void)
+{
+ g_return_val_if_fail (playing, -1);
+ return output_written_time ();
}
-static void set_tuple (InputPlayback * p, Tuple * tuple)
+Tuple * input_get_tuple (void)
+{
+ g_return_val_if_fail (playing, NULL);
+ return playback_entry_get_tuple ();
+}
+
+void input_set_tuple (Tuple * tuple)
{
g_return_if_fail (playing);
- read_gain_from_tuple (tuple);
playback_entry_set_tuple (tuple);
}
-static void set_gain_from_playlist (InputPlayback * p)
+void input_set_bitrate (int bitrate)
{
g_return_if_fail (playing);
- p->output->set_replaygain_info (& gain_from_playlist);
+ current_bitrate = bitrate;
+
+ if (ready_flag)
+ event_queue ("info change", NULL);
}
-static InputPlayback playback_api = {
- .output = & output_api,
- .set_data = set_data,
- .get_data = get_data,
- .set_pb_ready = set_pb_ready,
- .set_params = set_params,
- .set_tuple = set_tuple,
- .set_gain_from_playlist = set_gain_from_playlist,
-};
+bool_t input_check_stop (void)
+{
+ g_return_val_if_fail (playing, TRUE);
+ return g_atomic_int_get (& stop_flag);
+}
+
+int input_check_seek (void)
+{
+ g_return_val_if_fail (playing, -1);
+
+ pthread_mutex_lock (& control_mutex);
+ int seek = seek_request;
+
+ if (seek != -1)
+ {
+ output_set_time (seek);
+ seek_request = -1;
+
+ event_queue ("playback seek", NULL);
+ }
+
+ pthread_mutex_unlock (& control_mutex);
+ return seek;
+}
char * drct_get_filename (void)
{
@@ -567,14 +575,9 @@ char * drct_get_title (void)
if (current_length > 0)
{
- int len = current_length / 1000;
-
- if (len < 3600)
- snprintf (s, sizeof s, get_bool (NULL, "leading_zero") ?
- " (%02d:%02d)" : " (%d:%02d)", len / 60, len % 60);
- else
- snprintf (s, sizeof s, " (%d:%02d:%02d)", len / 3600, (len / 60) %
- 60, len % 60);
+ char t[16];
+ audgui_format_time (t, sizeof t, current_length);
+ snprintf (s, sizeof s, " (%s)", t);
}
else
s[0] = 0;
@@ -605,23 +608,12 @@ void drct_get_info (int * bitrate, int * samplerate, int * channels)
void drct_get_volume (int * l, int * r)
{
- if (playing && drct_get_ready () && current_decoder &&
- current_decoder->get_volume && current_decoder->get_volume (l, r))
- return;
-
output_get_volume (l, r);
}
void drct_set_volume (int l, int r)
{
- l = CLAMP (l, 0, 100);
- r = CLAMP (r, 0, 100);
-
- if (playing && drct_get_ready () && current_decoder &&
- current_decoder->set_volume && current_decoder->set_volume (l, r))
- return;
-
- output_set_volume (l, r);
+ output_set_volume (CLAMP (l, 0, 100), CLAMP (r, 0, 100));
}
void drct_set_ab_repeat (int a, int b)
@@ -634,27 +626,27 @@ void drct_set_ab_repeat (int a, int b)
if (current_length < 1)
return;
- repeat_a = a;
+ if (a >= 0)
+ a += time_offset;
+ if (b >= 0)
+ b += time_offset;
- if (repeat_b != b)
- {
- repeat_b = b;
+ pthread_mutex_lock (& control_mutex);
- /* Restart playback so the new setting takes effect. We could add
- * something like InputPlugin::set_stop_time(), but this is the only
- * place it would be used. */
- int seek_time = drct_get_time ();
- bool_t was_paused = paused;
-
- if (repeat_b >= 0 && seek_time >= repeat_b)
- seek_time = MAX (repeat_a, 0);
+ repeat_a = a;
+ g_atomic_int_set (& repeat_b, b);
- playback_restart (seek_time, was_paused);
+ if (b != -1 && output_get_time () >= b)
+ {
+ seek_request = MAX (a, time_offset);
+ output_abort_write ();
}
+
+ pthread_mutex_unlock (& control_mutex);
}
void drct_get_ab_repeat (int * a, int * b)
{
- * a = playing ? repeat_a : -1;
- * b = playing ? repeat_b : -1;
+ * a = (playing && repeat_a != -1) ? repeat_a - time_offset : -1;
+ * b = (playing && repeat_b != -1) ? repeat_b - time_offset : -1;
}
diff --git a/src/audacious/playback.h b/src/audacious/playback.h
index fb84b01..2937791 100644
--- a/src/audacious/playback.h
+++ b/src/audacious/playback.h
@@ -1,6 +1,6 @@
/*
* playback.h
- * Copyright 2006-2011 William Pitcock and John Lindgren
+ * Copyright 2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/audacious/playlist-api.h b/src/audacious/playlist-api.h
index 08d71a8..634fd05 100644
--- a/src/audacious/playlist-api.h
+++ b/src/audacious/playlist-api.h
@@ -1,6 +1,6 @@
/*
* playlist-api.h
- * Copyright 2010-2012 John Lindgren
+ * Copyright 2010-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -124,8 +124,9 @@ AUD_VFUNC5 (playlist_entry_insert_batch, int, playlist, int, at,
/* Similar to playlist_entry_insert_batch, but allows the caller to prevent some
* items from being added by returning false from the <filter> callback. Useful
- * for searching a folder and adding only new files to the playlist. <user> is
- * an untyped pointer passed to the <filter> callback. */
+ * for searching a folder and adding only new files to the playlist. Filenames
+ * passed to the callback can be used with str_ref(), str_equal(), etc. <user>
+ * is an additional, untyped pointer passed to the callback. */
AUD_VFUNC7 (playlist_entry_insert_filtered, int, playlist, int, at,
Index *, filenames, Index *, tuples, PlaylistFilterFunc, filter,
void *, user, bool_t, play)
diff --git a/src/audacious/playlist-files.c b/src/audacious/playlist-files.c
index 795042e..8ebe521 100644
--- a/src/audacious/playlist-files.c
+++ b/src/audacious/playlist-files.c
@@ -1,6 +1,6 @@
/*
* playlist-files.c
- * Copyright 2010-2011 John Lindgren
+ * Copyright 2010-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -27,80 +27,98 @@
#include "plugin.h"
#include "plugins.h"
-static PluginHandle * get_plugin_silent (const char * filename)
+typedef struct
{
- char buf[32];
- if (! uri_get_extension (filename, buf, sizeof buf))
- return NULL;
-
- return playlist_plugin_for_extension (buf);
+ const char * filename;
+ char * title;
+ Index * filenames;
+ Index * tuples;
+ bool_t plugin_found;
+ bool_t success;
}
+PlaylistData;
-bool_t filename_is_playlist (const char * filename)
+static void plugin_for_filename (const char * filename, PluginForEachFunc func, void * data)
{
- return (get_plugin_silent (filename) != NULL);
+ char ext[32];
+ if (uri_get_extension (filename, ext, sizeof ext))
+ playlist_plugin_for_ext (ext, func, data);
}
-static PluginHandle * get_plugin (const char * filename, bool_t saving)
+static bool_t plugin_found_cb (PluginHandle * plugin, void * data)
{
- PluginHandle * plugin = get_plugin_silent (filename);
-
- if (! plugin)
- {
- SPRINTF (error, _("Cannot %s %s: unsupported file extension."),
- saving ? _("save") : _("load"), filename);
- interface_show_error (error);
- return NULL;
- }
-
- return plugin;
+ * (PluginHandle * *) data = plugin;
+ return FALSE; /* stop when first plugin is found */
}
-bool_t playlist_load (const char * filename, char * * title,
- Index * * filenames_p, Index * * tuples_p)
+bool_t filename_is_playlist (const char * filename)
{
- AUDDBG ("Loading playlist %s.\n", filename);
+ PluginHandle * plugin = NULL;
+ plugin_for_filename (filename, plugin_found_cb, & plugin);
+ return (plugin != NULL);
+}
- PluginHandle * plugin = get_plugin (filename, FALSE);
- if (! plugin)
- return FALSE;
+static bool_t playlist_load_cb (PluginHandle * plugin, void * data_)
+{
+ PlaylistData * data = (PlaylistData *) data_;
PlaylistPlugin * pp = plugin_get_header (plugin);
- g_return_val_if_fail (pp && PLUGIN_HAS_FUNC (pp, load), FALSE);
+ if (! pp || ! PLUGIN_HAS_FUNC (pp, load))
+ return TRUE; /* try another plugin */
+
+ data->plugin_found = TRUE;
- VFSFile * file = vfs_fopen (filename, "r");
+ VFSFile * file = vfs_fopen (data->filename, "r");
if (! file)
- return FALSE;
+ return FALSE; /* stop if we can't open file */
- Index * filenames = index_new ();
- Index * tuples = index_new ();
- bool_t success = pp->load (filename, file, title, filenames, tuples);
+ data->success = pp->load (data->filename, file, & data->title, data->filenames, data->tuples);
vfs_fclose (file);
+ return ! data->success; /* stop when playlist is loaded */
+}
+
+bool_t playlist_load (const char * filename, char * * title, Index * * filenames, Index * * tuples)
+{
+ PlaylistData data =
+ {
+ .filename = filename,
+ .filenames = index_new (),
+ .tuples = index_new ()
+ };
+
+ AUDDBG ("Loading playlist %s.\n", filename);
+ plugin_for_filename (filename, playlist_load_cb, & data);
+
+ if (! data.plugin_found)
+ {
+ SPRINTF (error, _("Cannot load %s: unsupported file extension."), filename);
+ interface_show_error (error);
+ }
- if (! success)
+ if (! data.success)
{
- index_free (filenames);
- index_free (tuples);
+ str_unref (data.title);
+ index_free_full (data.filenames, (IndexFreeFunc) str_unref);
+ index_free_full (data.tuples, (IndexFreeFunc) tuple_unref);
return FALSE;
}
- if (index_count (tuples))
- g_return_val_if_fail (index_count (tuples) == index_count (filenames),
- FALSE);
+ if (index_count (data.tuples))
+ g_return_val_if_fail (index_count (data.tuples) == index_count (data.filenames), FALSE);
else
{
- index_free (tuples);
- tuples = NULL;
+ index_free (data.tuples);
+ data.tuples = NULL;
}
- * filenames_p = filenames;
- * tuples_p = tuples;
+ * title = data.title;
+ * filenames = data.filenames;
+ * tuples = data.tuples;
return TRUE;
}
-bool_t playlist_insert_playlist_raw (int list, int at,
- const char * filename)
+bool_t playlist_insert_playlist_raw (int list, int at, const char * filename)
{
char * title = NULL;
Index * filenames, * tuples;
@@ -117,59 +135,60 @@ bool_t playlist_insert_playlist_raw (int list, int at,
return TRUE;
}
-bool_t playlist_save (int list, const char * filename)
+static bool_t playlist_save_cb (PluginHandle * plugin, void * data_)
{
- AUDDBG ("Saving playlist %s.\n", filename);
-
- PluginHandle * plugin = get_plugin (filename, TRUE);
- if (! plugin)
- return FALSE;
+ PlaylistData * data = data_;
PlaylistPlugin * pp = plugin_get_header (plugin);
- g_return_val_if_fail (pp, FALSE);
+ if (! pp || ! PLUGIN_HAS_FUNC (pp, save))
+ return TRUE; /* try another plugin */
- if (! PLUGIN_HAS_FUNC (pp, save))
- {
- SPRINTF (error, _("Cannot save %s: plugin does not support saving."), filename);
- interface_show_error (error);
- return FALSE;
- }
+ data->plugin_found = TRUE;
- bool_t fast = get_bool (NULL, "metadata_on_play");
-
- VFSFile * file = vfs_fopen (filename, "w");
+ VFSFile * file = vfs_fopen (data->filename, "w");
if (! file)
- return FALSE;
+ return FALSE; /* stop if we can't open file */
- char * title = playlist_get_title (list);
+ data->success = pp->save (data->filename, file, data->title, data->filenames, data->tuples);
+
+ vfs_fclose (file);
+ return FALSE; /* stop after first attempt (successful or not) */
+}
+
+bool_t playlist_save (int list, const char * filename)
+{
+ PlaylistData data =
+ {
+ .filename = filename,
+ .title = playlist_get_title (list),
+ .filenames = index_new (),
+ .tuples = index_new ()
+ };
int entries = playlist_entry_count (list);
- Index * filenames = index_new ();
- index_allocate (filenames, entries);
- Index * tuples = index_new ();
- index_allocate (tuples, entries);
+ bool_t fast = get_bool (NULL, "metadata_on_play");
+
+ index_allocate (data.filenames, entries);
+ index_allocate (data.tuples, entries);
for (int i = 0; i < entries; i ++)
{
- index_append (filenames, playlist_entry_get_filename (list, i));
- index_append (tuples, playlist_entry_get_tuple (list, i, fast));
+ index_insert (data.filenames, -1, playlist_entry_get_filename (list, i));
+ index_insert (data.tuples, -1, playlist_entry_get_tuple (list, i, fast));
}
- bool_t success = pp->save (filename, file, title, filenames, tuples);
-
- vfs_fclose (file);
- str_unref (title);
+ AUDDBG ("Saving playlist %s.\n", filename);
+ plugin_for_filename (filename, playlist_save_cb, & data);
- for (int i = 0; i < entries; i ++)
+ if (! data.plugin_found)
{
- str_unref (index_get (filenames, i));
- Tuple * tuple = index_get (tuples, i);
- if (tuple)
- tuple_unref (tuple);
+ SPRINTF (error, _("Cannot save %s: unsupported file extension."), filename);
+ interface_show_error (error);
}
- index_free (filenames);
- index_free (tuples);
+ str_unref (data.title);
+ index_free_full (data.filenames, (IndexFreeFunc) str_unref);
+ index_free_full (data.tuples, (IndexFreeFunc) tuple_unref);
- return success;
+ return data.success;
}
diff --git a/src/audacious/playlist-new.c b/src/audacious/playlist-new.c
index 29aab8f..23696b0 100644
--- a/src/audacious/playlist-new.c
+++ b/src/audacious/playlist-new.c
@@ -1,6 +1,6 @@
/*
* playlist-new.c
- * Copyright 2009-2012 John Lindgren
+ * Copyright 2009-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -24,6 +24,7 @@
#include <time.h>
#include <glib.h>
+#include <glib/gstdio.h>
#include <libaudcore/audstrings.h>
#include <libaudcore/hook.h>
@@ -126,22 +127,13 @@ static void scan_restart (void);
static bool_t next_song_locked (Playlist * playlist, bool_t repeat, int hint);
-static char * title_format;
-
-static char * title_from_tuple (Tuple * tuple)
-{
- if (! title_format)
- title_format = get_string (NULL, "generic_title_format");
-
- return tuple_format_title (tuple, title_format);
-}
+static TupleFormatter * title_formatter;
static void entry_set_tuple_real (Entry * entry, Tuple * tuple)
{
/* Hack: We cannot refresh segmented entries (since their info is read from
* the cue sheet when it is first loaded), so leave them alone. -jlindgren */
- if (entry->tuple && tuple_get_value_type (entry->tuple, FIELD_SEGMENT_START,
- NULL) == TUPLE_INT)
+ if (entry->tuple && tuple_get_value_type (entry->tuple, FIELD_SEGMENT_START) == TUPLE_INT)
{
if (tuple)
tuple_unref (tuple);
@@ -168,8 +160,8 @@ static void entry_set_tuple_real (Entry * entry, Tuple * tuple)
}
else
{
- entry->formatted = title_from_tuple (tuple);
- entry->length = tuple_get_int (tuple, FIELD_LENGTH, NULL);
+ entry->formatted = tuple_format_title (title_formatter, tuple);
+ entry->length = tuple_get_int (tuple, FIELD_LENGTH);
if (entry->length < 0)
entry->length = 0;
}
@@ -286,11 +278,7 @@ static void playlist_free (Playlist * playlist)
str_unref (playlist->filename);
str_unref (playlist->title);
-
- for (int count = 0; count < index_count (playlist->entries); count ++)
- entry_free (index_get (playlist->entries, count));
-
- index_free (playlist->entries);
+ index_free_full (playlist->entries, (IndexFreeFunc) entry_free);
g_list_free (playlist->queued);
g_slice_free (Playlist, playlist);
}
@@ -655,6 +643,9 @@ void playlist_init (void)
scan_playlist = scan_row = 0;
LEAVE;
+
+ /* initialize title formatter */
+ playlist_reformat_titles ();
}
void playlist_end (void)
@@ -670,17 +661,14 @@ void playlist_end (void)
active_playlist = playing_playlist = NULL;
resume_playlist = -1;
- for (int i = 0; i < index_count (playlists); i ++)
- playlist_free (index_get (playlists, i));
-
- index_free (playlists);
+ index_free_full (playlists, (IndexFreeFunc) playlist_free);
playlists = NULL;
g_hash_table_destroy (unique_id_table);
unique_id_table = NULL;
- g_free (title_format);
- title_format = NULL;
+ tuple_formatter_free (title_formatter);
+ title_formatter = NULL;
LEAVE;
}
@@ -722,11 +710,11 @@ void playlist_reorder (int from, int to, int count)
Index * displaced = index_new ();
if (to < from)
- index_copy_append (playlists, to, displaced, from - to);
+ index_copy_insert (playlists, to, displaced, -1, from - to);
else
- index_copy_append (playlists, from + count, displaced, to - from);
+ index_copy_insert (playlists, from + count, displaced, -1, to - from);
- index_move (playlists, from, to, count);
+ index_copy_set (playlists, from, playlists, to, count);
if (to < from)
{
@@ -751,8 +739,7 @@ void playlist_delete (int playlist_num)
bool_t was_playing = (playlist == playing_playlist);
- index_delete (playlists, playlist_num, 1);
- playlist_free (playlist);
+ index_delete_full (playlists, playlist_num, 1, (IndexFreeFunc) playlist_free);
if (! index_count (playlists))
index_insert (playlists, 0, playlist_new (-1));
@@ -1010,7 +997,7 @@ void playlist_entry_insert_batch_raw (int playlist_num, int at,
char * filename = index_get (filenames, i);
Tuple * tuple = tuples ? index_get (tuples, i) : NULL;
PluginHandle * decoder = decoders ? index_get (decoders, i) : NULL;
- index_append (add, entry_new (filename, tuple, decoder));
+ index_insert (add, -1, entry_new (filename, tuple, decoder));
}
index_free (filenames);
@@ -1020,7 +1007,7 @@ void playlist_entry_insert_batch_raw (int playlist_num, int at,
index_free (tuples);
number = index_count (add);
- index_merge_insert (playlist->entries, at, add);
+ index_copy_insert (add, 0, playlist->entries, at, -1);
index_free (add);
number_entries (playlist, at, entries + number - at);
@@ -1083,10 +1070,9 @@ void playlist_entry_delete (int playlist_num, int at, int number)
}
playlist->total_length -= entry->length;
- entry_free (entry);
}
- index_delete (playlist->entries, at, number);
+ index_delete_full (playlist->entries, at, number, (IndexFreeFunc) entry_free);
number_entries (playlist, at, entries - at - number);
if (position_changed && get_bool (NULL, "advance_on_delete"))
@@ -1352,21 +1338,21 @@ int playlist_shift (int playlist_num, int entry_num, int distance)
{
entry = index_get (playlist->entries, i);
if (! entry->selected)
- index_append (temp, entry);
+ index_insert (temp, -1, entry);
}
for (int i = top; i < bottom; i ++)
{
entry = index_get (playlist->entries, i);
if (entry->selected)
- index_append (temp, entry);
+ index_insert (temp, -1, entry);
}
for (int i = center; i < bottom; i ++)
{
entry = index_get (playlist->entries, i);
if (! entry->selected)
- index_append (temp, entry);
+ index_insert (temp, -1, entry);
}
index_copy_set (temp, 0, playlist->entries, top, bottom - top);
@@ -1446,7 +1432,7 @@ void playlist_delete_selected (int playlist_num)
}
else
{
- index_append (others, entry);
+ index_insert (others, -1, entry);
if (found)
after ++;
@@ -1486,7 +1472,7 @@ void playlist_reverse (int playlist_num)
index_allocate (reversed, entries);
for (int count = entries; count --; )
- index_append (reversed, index_get (playlist->entries, count));
+ index_insert (reversed, -1, index_get (playlist->entries, count));
index_free (playlist->entries);
playlist->entries = reversed;
@@ -1509,7 +1495,7 @@ void playlist_reverse_selected (int playlist_num)
{
Entry * entry = index_get (playlist->entries, count);
if (entry->selected)
- index_append (reversed, index_get (playlist->entries, count));
+ index_insert (reversed, -1, index_get (playlist->entries, count));
}
int count2 = 0;
@@ -1560,7 +1546,7 @@ void playlist_randomize_selected (int playlist_num)
{
Entry * entry = index_get (playlist->entries, count);
if (entry->selected)
- index_append (selected, entry);
+ index_insert (selected, -1, entry);
}
for (int i = 0; i < playlist->selected_count; i ++)
@@ -1637,7 +1623,7 @@ static void sort_selected (Playlist * playlist, CompareData * data)
{
Entry * entry = index_get (playlist->entries, count);
if (entry->selected)
- index_append (selected, entry);
+ index_insert (selected, -1, entry);
}
index_sort_with_data (selected, compare_cb, data);
@@ -1750,11 +1736,14 @@ void playlist_reformat_titles (void)
{
ENTER;
- g_free (title_format);
- title_format = NULL;
+ if (title_formatter)
+ tuple_formatter_free (title_formatter);
- for (int playlist_num = 0; playlist_num < index_count (playlists);
- playlist_num ++)
+ char * format = get_str (NULL, "generic_title_format");
+ title_formatter = tuple_formatter_new (format);
+ str_unref (format);
+
+ for (int playlist_num = 0; playlist_num < index_count (playlists); playlist_num ++)
{
Playlist * playlist = index_get (playlists, playlist_num);
int entries = index_count (playlist->entries);
@@ -1763,7 +1752,11 @@ void playlist_reformat_titles (void)
{
Entry * entry = index_get (playlist->entries, count);
str_unref (entry->formatted);
- entry->formatted = entry->tuple ? title_from_tuple (entry->tuple) : NULL;
+
+ if (entry->tuple)
+ entry->formatted = tuple_format_title (title_formatter, entry->tuple);
+ else
+ entry->formatted = NULL;
}
queue_update (PLAYLIST_UPDATE_METADATA, playlist_num, 0, entries);
@@ -2251,9 +2244,10 @@ void playlist_save_state (void)
ENTER;
- char * path = g_strdup_printf ("%s/" STATE_FILE, get_path (AUD_PATH_USER_DIR));
- FILE * handle = fopen (path, "w");
- g_free (path);
+ const char * user_dir = get_path (AUD_PATH_USER_DIR);
+ SCONCAT2 (path, user_dir, "/" STATE_FILE);
+
+ FILE * handle = g_fopen (path, "w");
if (! handle)
RETURN ();
@@ -2323,9 +2317,10 @@ void playlist_load_state (void)
ENTER;
int playlist_num;
- char * path = g_strdup_printf ("%s/" STATE_FILE, get_path (AUD_PATH_USER_DIR));
- FILE * handle = fopen (path, "r");
- g_free (path);
+ const char * user_dir = get_path (AUD_PATH_USER_DIR);
+ SCONCAT2 (path, user_dir, "/" STATE_FILE);
+
+ FILE * handle = g_fopen (path, "r");
if (! handle)
RETURN ();
diff --git a/src/audacious/playlist-utils.c b/src/audacious/playlist-utils.c
index e690c9f..c51dfa2 100644
--- a/src/audacious/playlist-utils.c
+++ b/src/audacious/playlist-utils.c
@@ -17,13 +17,13 @@
* the use of this software.
*/
-#include <dirent.h>
-#include <glib.h>
-#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+
#include <libaudcore/audstrings.h>
#include <libaudcore/hook.h>
@@ -39,13 +39,13 @@ static const char * get_basename (const char * filename)
static int filename_compare_basename (const char * a, const char * b)
{
- return string_compare_encoded (get_basename (a), get_basename (b));
+ return str_compare_encoded (get_basename (a), get_basename (b));
}
static int tuple_compare_string (const Tuple * a, const Tuple * b, int field)
{
- char * string_a = tuple_get_str (a, field, NULL);
- char * string_b = tuple_get_str (b, field, NULL);
+ char * string_a = tuple_get_str (a, field);
+ char * string_b = tuple_get_str (b, field);
int ret;
if (string_a == NULL)
@@ -53,7 +53,7 @@ static int tuple_compare_string (const Tuple * a, const Tuple * b, int field)
else if (string_b == NULL)
ret = 1;
else
- ret = string_compare (string_a, string_b);
+ ret = str_compare (string_a, string_b);
str_unref (string_a);
str_unref (string_b);
@@ -62,13 +62,13 @@ static int tuple_compare_string (const Tuple * a, const Tuple * b, int field)
static int tuple_compare_int (const Tuple * a, const Tuple * b, int field)
{
- if (tuple_get_value_type (a, field, NULL) != TUPLE_INT)
- return (tuple_get_value_type (b, field, NULL) != TUPLE_INT) ? 0 : -1;
- if (tuple_get_value_type (b, field, NULL) != TUPLE_INT)
+ if (tuple_get_value_type (a, field) != TUPLE_INT)
+ return (tuple_get_value_type (b, field) != TUPLE_INT) ? 0 : -1;
+ if (tuple_get_value_type (b, field) != TUPLE_INT)
return 1;
- int int_a = tuple_get_int (a, field, NULL);
- int int_b = tuple_get_int (b, field, NULL);
+ int int_a = tuple_get_int (a, field);
+ int int_b = tuple_get_int (b, field);
return (int_a < int_b) ? -1 : (int_a > int_b);
}
@@ -104,7 +104,7 @@ static int tuple_compare_length (const Tuple * a, const Tuple * b)
}
static const PlaylistStringCompareFunc filename_comparisons[] = {
- [PLAYLIST_SORT_PATH] = string_compare_encoded,
+ [PLAYLIST_SORT_PATH] = str_compare_encoded,
[PLAYLIST_SORT_FILENAME] = filename_compare_basename,
[PLAYLIST_SORT_TITLE] = NULL,
[PLAYLIST_SORT_ALBUM] = NULL,
@@ -133,7 +133,7 @@ static const PlaylistStringCompareFunc title_comparisons[] = {
[PLAYLIST_SORT_ARTIST] = NULL,
[PLAYLIST_SORT_DATE] = NULL,
[PLAYLIST_SORT_TRACK] = NULL,
- [PLAYLIST_SORT_FORMATTED_TITLE] = string_compare,
+ [PLAYLIST_SORT_FORMATTED_TITLE] = str_compare,
[PLAYLIST_SORT_LENGTH] = NULL};
void playlist_sort_by_scheme (int playlist, int scheme)
@@ -248,12 +248,13 @@ void playlist_select_by_patterns (int playlist, const Tuple * patterns)
playlist_select_all (playlist, TRUE);
- for (field = 0; field < G_N_ELEMENTS (fields); field ++)
+ for (field = 0; field < ARRAY_LEN (fields); field ++)
{
- char * pattern = tuple_get_str (patterns, fields[field], NULL);
- regex_t regex;
+ char * pattern = tuple_get_str (patterns, fields[field]);
+ GRegex * regex;
- if (! pattern || ! pattern[0] || regcomp (& regex, pattern, REG_ICASE))
+ if (! pattern || ! pattern[0] || ! (regex = g_regex_new (pattern,
+ G_REGEX_CASELESS, 0, NULL)))
{
str_unref (pattern);
continue;
@@ -265,9 +266,9 @@ void playlist_select_by_patterns (int playlist, const Tuple * patterns)
continue;
Tuple * tuple = playlist_entry_get_tuple (playlist, entry, FALSE);
- char * string = tuple ? tuple_get_str (tuple, fields[field], NULL) : NULL;
+ char * string = tuple ? tuple_get_str (tuple, fields[field]) : NULL;
- if (! string || regexec (& regex, string, 0, NULL, 0))
+ if (! string || ! g_regex_match (regex, string, 0, NULL))
playlist_entry_set_selected (playlist, entry, FALSE);
str_unref (string);
@@ -275,7 +276,7 @@ void playlist_select_by_patterns (int playlist, const Tuple * patterns)
tuple_unref (tuple);
}
- regfree (& regex);
+ g_regex_unref (regex);
str_unref (pattern);
}
}
@@ -283,14 +284,16 @@ void playlist_select_by_patterns (int playlist, const Tuple * patterns)
static char * make_playlist_path (int playlist)
{
if (! playlist)
- return g_strdup_printf ("%s/playlist.xspf", get_path (AUD_PATH_USER_DIR));
+ return filename_build (get_path (AUD_PATH_USER_DIR), "playlist.xspf");
- return g_strdup_printf ("%s/playlist_%02d.xspf",
- get_path (AUD_PATH_PLAYLISTS_DIR), 1 + playlist);
+ SPRINTF (name, "playlist_%02d.xspf", 1 + playlist);
+ return filename_build (get_path (AUD_PATH_PLAYLISTS_DIR), name);
}
static void load_playlists_real (void)
{
+ const char * folder = get_path (AUD_PATH_PLAYLISTS_DIR);
+
/* old (v3.1 and earlier) naming scheme */
int count;
@@ -300,7 +303,7 @@ static void load_playlists_real (void)
if (! g_file_test (path, G_FILE_TEST_EXISTS))
{
- g_free (path);
+ str_unref (path);
break;
}
@@ -310,47 +313,52 @@ static void load_playlists_real (void)
playlist_insert_playlist_raw (count, 0, uri);
playlist_set_modified (count, TRUE);
- g_free (path);
- g_free (uri);
+ str_unref (path);
+ str_unref (uri);
}
/* unique ID-based naming scheme */
- char * order_path = g_strdup_printf ("%s/order", get_path (AUD_PATH_PLAYLISTS_DIR));
+ char * order_path = filename_build (folder, "order");
char * order_string;
g_file_get_contents (order_path, & order_string, NULL, NULL);
- g_free (order_path);
+ str_unref (order_path);
if (! order_string)
goto DONE;
- char * * order = g_strsplit (order_string, " ", -1);
+ Index * order = str_list_to_index (order_string, " ");
g_free (order_string);
- for (int i = 0; order[i]; i ++)
+ for (int i = 0; i < index_count (order); i ++)
{
- char * path = g_strdup_printf ("%s/%s.audpl", get_path (AUD_PATH_PLAYLISTS_DIR), order[i]);
+ char * number = index_get (order, i);
+
+ SCONCAT2 (name, number, ".audpl");
+ char * path = filename_build (folder, name);
if (! g_file_test (path, G_FILE_TEST_EXISTS))
{
- g_free (path);
- path = g_strdup_printf ("%s/%s.xspf", get_path (AUD_PATH_PLAYLISTS_DIR), order[i]);
+ str_unref (path);
+
+ SCONCAT2 (name2, number, ".xspf");
+ path = filename_build (folder, name2);
}
char * uri = filename_to_uri (path);
- playlist_insert_with_id (count + i, atoi (order[i]));
+ playlist_insert_with_id (count + i, atoi (number));
playlist_insert_playlist_raw (count + i, 0, uri);
playlist_set_modified (count + i, FALSE);
if (g_str_has_suffix (path, ".xspf"))
playlist_set_modified (count + i, TRUE);
- g_free (path);
- g_free (uri);
+ str_unref (path);
+ str_unref (uri);
}
- g_strfreev (order);
+ index_free_full (order, (IndexFreeFunc) str_unref);
DONE:
if (! playlist_count ())
@@ -366,35 +374,38 @@ static void save_playlists_real (void)
/* save playlists */
- char * * order = g_malloc (sizeof (char *) * (lists + 1));
- GHashTable * saved = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ Index * order = index_new ();
+ GHashTable * saved = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) str_unref, NULL);
for (int i = 0; i < lists; i ++)
{
int id = playlist_get_unique_id (i);
- order[i] = g_strdup_printf ("%d", id);
+ char * number = int_to_str (id);
+
+ SCONCAT2 (name, number, ".audpl");
if (playlist_get_modified (i))
{
- char * path = g_strdup_printf ("%s/%d.audpl", folder, id);
+ char * path = filename_build (folder, name);
char * uri = filename_to_uri (path);
playlist_save (i, uri);
playlist_set_modified (i, FALSE);
- g_free (path);
- g_free (uri);
+ str_unref (path);
+ str_unref (uri);
}
- g_hash_table_insert (saved, g_strdup_printf ("%d.audpl", id), NULL);
+ index_insert (order, -1, number);
+ g_hash_table_insert (saved, str_get (name), NULL);
}
- order[lists] = NULL;
- char * order_string = g_strjoinv (" ", order);
- g_strfreev (order);
+ char * order_string = index_to_str_list (order, " ");
+ index_free_full (order, (IndexFreeFunc) str_unref);
GError * error = NULL;
- char * order_path = g_strdup_printf ("%s/order", get_path (AUD_PATH_PLAYLISTS_DIR));
+ char * order_path = filename_build (folder, "order");
char * old_order_string;
g_file_get_contents (order_path, & old_order_string, NULL, NULL);
@@ -408,36 +419,35 @@ static void save_playlists_real (void)
}
}
- g_free (order_string);
- g_free (order_path);
+ str_unref (order_string);
+ str_unref (order_path);
g_free (old_order_string);
/* clean up deleted playlists and files from old naming scheme */
char * path = make_playlist_path (0);
- remove (path);
- g_free (path);
+ g_unlink (path);
+ str_unref (path);
- DIR * dir = opendir (folder);
+ GDir * dir = g_dir_open (folder, 0, NULL);
if (! dir)
goto DONE;
- struct dirent * entry;
- while ((entry = readdir (dir)))
+ const char * name;
+ while ((name = g_dir_read_name (dir)))
{
- if (! g_str_has_suffix (entry->d_name, ".audpl")
- && ! g_str_has_suffix (entry->d_name, ".xspf"))
+ if (! g_str_has_suffix (name, ".audpl") && ! g_str_has_suffix (name, ".xspf"))
continue;
- if (! g_hash_table_lookup_extended (saved, entry->d_name, NULL, NULL))
+ if (! g_hash_table_contains (saved, name))
{
- char * path = g_strdup_printf ("%s/%s", folder, entry->d_name);
- remove (path);
- g_free (path);
+ char * path = filename_build (folder, name);
+ g_unlink (path);
+ str_unref (path);
}
}
- closedir (dir);
+ g_dir_close (dir);
DONE:
g_hash_table_destroy (saved);
diff --git a/src/audacious/plugin-init.c b/src/audacious/plugin-init.c
index 0992bf5..10edb28 100644
--- a/src/audacious/plugin-init.c
+++ b/src/audacious/plugin-init.c
@@ -1,6 +1,6 @@
/*
* plugin-init.c
- * Copyright 2010-2011 John Lindgren
+ * Copyright 2010-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -27,7 +27,7 @@
#include "effect.h"
#include "general.h"
#include "interface.h"
-#include "main.h"
+#include "misc.h"
#include "output.h"
#include "plugin.h"
#include "plugins.h"
@@ -125,7 +125,7 @@ static bool_t start_multi_cb (PluginHandle * p, void * type)
static void start_plugins (int type)
{
- if (headless && type == PLUGIN_TYPE_IFACE)
+ if (type == PLUGIN_TYPE_IFACE && headless_mode ())
return;
if (table[type].is_single)
@@ -177,7 +177,7 @@ static bool_t stop_multi_cb (PluginHandle * p, void * type)
static void stop_plugins (int type)
{
- if (headless && type == PLUGIN_TYPE_IFACE)
+ if (type == PLUGIN_TYPE_IFACE && headless_mode ())
return;
plugin_for_enabled (type, misc_cleanup_cb, GINT_TO_POINTER (type));
diff --git a/src/audacious/plugin-preferences.c b/src/audacious/plugin-preferences.c
index d064c52..8a0bf81 100644
--- a/src/audacious/plugin-preferences.c
+++ b/src/audacious/plugin-preferences.c
@@ -1,6 +1,6 @@
/*
* plugin-preferences.c
- * Copyright 2012 John Lindgren
+ * Copyright 2012-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,6 +17,7 @@
* the use of this software.
*/
+#include <libaudcore/audstrings.h>
#include <libaudgui/libaudgui-gtk.h>
#include "i18n.h"
@@ -51,9 +52,8 @@ void plugin_make_about_window (PluginHandle * plugin)
text = dgettext (header->domain, text);
}
- char * title = g_strdup_printf (_("About %s"), name);
+ SCONCAT3 (title, _("About"), " ", name);
audgui_simple_message (& misc->about_window, GTK_MESSAGE_INFO, title, text);
- g_free (title);
}
static void response_cb (GtkWidget * window, int response, const PluginPreferences * p)
@@ -89,14 +89,23 @@ void plugin_make_config_window (PluginHandle * plugin)
if (PLUGIN_HAS_FUNC (header, domain))
name = dgettext (header->domain, header->name);
- char * title = g_strdup_printf (_("%s Settings"), name);
+ GtkWidget * window = gtk_dialog_new ();
- GtkWidget * window = p->apply ? gtk_dialog_new_with_buttons (title, NULL, 0,
- GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL)
- : gtk_dialog_new_with_buttons (title, NULL, 0, GTK_STOCK_CLOSE,
- GTK_RESPONSE_CLOSE, NULL);
+ SCONCAT3 (title, name, " ", _("Settings"));
+ gtk_window_set_title ((GtkWindow *) window, title);
- g_free (title);
+ if (p->apply)
+ {
+ GtkWidget * button1 = audgui_button_new (_("_Set"), "system-run", NULL, NULL);
+ GtkWidget * button2 = audgui_button_new (_("_Cancel"), "process-stop", NULL, NULL);
+ gtk_dialog_add_action_widget ((GtkDialog *) window, button2, GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_action_widget ((GtkDialog *) window, button1, GTK_RESPONSE_OK);
+ }
+ else
+ {
+ GtkWidget * button = audgui_button_new (_("_Close"), "window-close", NULL, NULL);
+ gtk_dialog_add_action_widget ((GtkDialog *) window, button, GTK_RESPONSE_CLOSE);
+ }
GtkWidget * content = gtk_dialog_get_content_area ((GtkDialog *) window);
GtkWidget * box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
diff --git a/src/audacious/plugin-registry.c b/src/audacious/plugin-registry.c
index 2b56928..bc0d441 100644
--- a/src/audacious/plugin-registry.c
+++ b/src/audacious/plugin-registry.c
@@ -1,6 +1,6 @@
/*
* plugin-registry.c
- * Copyright 2009-2011 John Lindgren
+ * Copyright 2009-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -24,11 +24,13 @@
* loaded; hence the mutex must be locked before checking that a plugin is
* loaded and while loading it. */
-#include <glib.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+
#include <libaudcore/audstrings.h>
#include "debug.h"
@@ -99,7 +101,7 @@ static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static PluginHandle * plugin_new (char * path, bool_t confirmed, bool_t
loaded, int timestamp, int type, Plugin * header)
{
- PluginHandle * plugin = g_malloc (sizeof (PluginHandle));
+ PluginHandle * plugin = g_slice_new (PluginHandle);
plugin->path = path;
plugin->confirmed = confirmed;
@@ -144,31 +146,31 @@ static void plugin_free (PluginHandle * plugin)
{
plugin_list = g_list_remove (plugin_list, plugin);
- g_list_free_full (plugin->watches, g_free);
+ for (GList * node = plugin->watches; node; node = node->next)
+ g_slice_free (PluginWatch, node->data);
if (plugin->type == PLUGIN_TYPE_TRANSPORT)
- g_list_free_full (plugin->u.t.schemes, g_free);
+ g_list_free_full (plugin->u.t.schemes, (GDestroyNotify) str_unref);
else if (plugin->type == PLUGIN_TYPE_PLAYLIST)
- g_list_free_full (plugin->u.p.exts, g_free);
+ g_list_free_full (plugin->u.p.exts, (GDestroyNotify) str_unref);
else if (plugin->type == PLUGIN_TYPE_INPUT)
{
for (int key = 0; key < INPUT_KEYS; key ++)
- g_list_free_full (plugin->u.i.keys[key], g_free);
+ g_list_free_full (plugin->u.i.keys[key], (GDestroyNotify) str_unref);
}
- g_free (plugin->path);
- g_free (plugin->name);
- g_free (plugin->domain);
+ str_unref (plugin->path);
+ str_unref (plugin->name);
+ str_unref (plugin->domain);
g_free (plugin->misc);
- g_free (plugin);
+ g_slice_free (PluginHandle, plugin);
}
static FILE * open_registry_file (const char * mode)
{
- char * path = g_strdup_printf ("%s/" FILENAME, get_path (AUD_PATH_USER_DIR));
- FILE * file = fopen (path, mode);
- g_free (path);
- return file;
+ const char * user_dir = get_path (AUD_PATH_USER_DIR);
+ SCONCAT2 (path, user_dir, "/" FILENAME);
+ return g_fopen (path, mode);
}
static void transport_plugin_save (PluginHandle * plugin, FILE * handle)
@@ -264,8 +266,7 @@ static bool_t parse_integer (const char * key, int * value)
static char * parse_string (const char * key)
{
- return (parse_value && ! strcmp (parse_key, key)) ? g_strdup (parse_value) :
- NULL;
+ return (parse_value && ! strcmp (parse_key, key)) ? str_get (parse_value) : NULL;
}
static void transport_plugin_parse (PluginHandle * plugin, FILE * handle)
@@ -330,7 +331,7 @@ FOUND:
int timestamp;
if (! parse_integer ("stamp", & timestamp))
{
- g_free (path);
+ str_unref (path);
return FALSE;
}
@@ -405,10 +406,10 @@ int plugin_compare (PluginHandle * a, PluginHandle * b)
return 1;
int diff;
- if ((diff = string_compare (dgettext (a->domain, a->name), dgettext (b->domain, b->name))))
+ if ((diff = str_compare (dgettext (a->domain, a->name), dgettext (b->domain, b->name))))
return diff;
- return string_compare (a->path, b->path);
+ return str_compare (a->path, b->path);
}
void plugin_registry_prune (void)
@@ -432,16 +433,15 @@ PluginHandle * plugin_lookup (const char * path)
static int plugin_lookup_basename_cb (PluginHandle * plugin, const char * basename)
{
- char * test = g_path_get_basename (plugin->path);
-
- char * dot = strrchr (test, '.');
- if (dot)
- * dot = 0;
+ const char * slash = strrchr (plugin->path, G_DIR_SEPARATOR);
+ if (! slash)
+ return TRUE;
- int ret = strcmp (test, basename);
+ const char * dot = strrchr (slash + 1, '.');
+ if (! dot)
+ return TRUE;
- g_free (test);
- return ret;
+ return strncmp (slash + 1, basename, dot - (slash + 1));
}
/* Note: If there are multiple plugins with the same basename, this returns only
@@ -457,10 +457,10 @@ static void plugin_get_info (PluginHandle * plugin, bool_t new)
{
Plugin * header = plugin->header;
- g_free (plugin->name);
- g_free (plugin->domain);
- plugin->name = g_strdup (header->name);
- plugin->domain = PLUGIN_HAS_FUNC (header, domain) ? g_strdup (header->domain) : NULL;
+ str_unref (plugin->name);
+ str_unref (plugin->domain);
+ plugin->name = str_get (header->name);
+ plugin->domain = PLUGIN_HAS_FUNC (header, domain) ? str_get (header->domain) : NULL;
plugin->has_about = PLUGIN_HAS_FUNC (header, about) || PLUGIN_HAS_FUNC (header, about_text);
plugin->has_configure = PLUGIN_HAS_FUNC (header, configure) || PLUGIN_HAS_FUNC (header, prefs);
@@ -468,23 +468,21 @@ static void plugin_get_info (PluginHandle * plugin, bool_t new)
{
TransportPlugin * tp = (TransportPlugin *) header;
- g_list_free_full (plugin->u.t.schemes, g_free);
+ g_list_free_full (plugin->u.t.schemes, (GDestroyNotify) str_unref);
plugin->u.t.schemes = NULL;
for (int i = 0; tp->schemes[i]; i ++)
- plugin->u.t.schemes = g_list_prepend (plugin->u.t.schemes, g_strdup
- (tp->schemes[i]));
+ plugin->u.t.schemes = g_list_prepend (plugin->u.t.schemes, str_get (tp->schemes[i]));
}
else if (header->type == PLUGIN_TYPE_PLAYLIST)
{
PlaylistPlugin * pp = (PlaylistPlugin *) header;
- g_list_free_full (plugin->u.p.exts, g_free);
+ g_list_free_full (plugin->u.p.exts, (GDestroyNotify) str_unref);
plugin->u.p.exts = NULL;
for (int i = 0; pp->extensions[i]; i ++)
- plugin->u.p.exts = g_list_prepend (plugin->u.p.exts, g_strdup
- (pp->extensions[i]));
+ plugin->u.p.exts = g_list_prepend (plugin->u.p.exts, str_get (pp->extensions[i]));
}
else if (header->type == PLUGIN_TYPE_INPUT)
{
@@ -493,7 +491,7 @@ static void plugin_get_info (PluginHandle * plugin, bool_t new)
for (int key = 0; key < INPUT_KEYS; key ++)
{
- g_list_free_full (plugin->u.i.keys[key], g_free);
+ g_list_free_full (plugin->u.i.keys[key], (GDestroyNotify) str_unref);
plugin->u.i.keys[key] = NULL;
}
@@ -501,22 +499,22 @@ static void plugin_get_info (PluginHandle * plugin, bool_t new)
{
for (int i = 0; ip->extensions[i]; i ++)
plugin->u.i.keys[INPUT_KEY_EXTENSION] = g_list_prepend
- (plugin->u.i.keys[INPUT_KEY_EXTENSION], g_strdup
- (ip->extensions[i]));
+ (plugin->u.i.keys[INPUT_KEY_EXTENSION],
+ str_get (ip->extensions[i]));
}
if (PLUGIN_HAS_FUNC (ip, mimes))
{
for (int i = 0; ip->mimes[i]; i ++)
plugin->u.i.keys[INPUT_KEY_MIME] = g_list_prepend
- (plugin->u.i.keys[INPUT_KEY_MIME], g_strdup (ip->mimes[i]));
+ (plugin->u.i.keys[INPUT_KEY_MIME], str_get (ip->mimes[i]));
}
if (PLUGIN_HAS_FUNC (ip, schemes))
{
for (int i = 0; ip->schemes[i]; i ++)
plugin->u.i.keys[INPUT_KEY_SCHEME] = g_list_prepend
- (plugin->u.i.keys[INPUT_KEY_SCHEME], g_strdup (ip->schemes[i]));
+ (plugin->u.i.keys[INPUT_KEY_SCHEME], str_get (ip->schemes[i]));
}
plugin->u.i.has_images = PLUGIN_HAS_FUNC (ip, get_song_image);
@@ -572,7 +570,7 @@ void plugin_register (const char * path, int timestamp)
if (! header)
return;
- plugin = plugin_new (g_strdup (path), TRUE, TRUE, timestamp,
+ plugin = plugin_new (str_get (path), TRUE, TRUE, timestamp,
header->type, header);
plugin_get_info (plugin, TRUE);
@@ -620,6 +618,54 @@ PluginHandle * plugin_by_header (const void * header)
return node ? node->data : NULL;
}
+int plugin_count (int type)
+{
+ int count = 0;
+
+ for (GList * node = plugin_list; node; node = node->next)
+ {
+ PluginHandle * plugin = node->data;
+ if (plugin->type == type)
+ count ++;
+ }
+
+ return count;
+}
+
+int plugin_get_index (PluginHandle * plugin)
+{
+ int index = 0;
+
+ for (GList * node = plugin_list; node; node = node->next)
+ {
+ PluginHandle * plugin2 = node->data;
+ if (plugin2->type == plugin->type)
+ {
+ if (plugin2 == plugin)
+ return index;
+ index ++;
+ }
+ }
+
+ return -1;
+}
+
+PluginHandle * plugin_by_index (int type, int index)
+{
+ for (GList * node = plugin_list; node; node = node->next)
+ {
+ PluginHandle * plugin = node->data;
+ if (plugin->type == type)
+ {
+ if (! index)
+ return plugin;
+ index --;
+ }
+ }
+
+ return NULL;
+}
+
void plugin_for_each (int type, PluginForEachFunc func, void * data)
{
for (GList * node = plugin_list; node; node = node->next)
@@ -660,7 +706,7 @@ static void plugin_call_watches (PluginHandle * plugin)
if (! watch->func (plugin, watch->data))
{
- g_free (watch);
+ g_slice_free (PluginWatch, watch);
plugin->watches = g_list_delete_link (plugin->watches, node);
}
@@ -696,7 +742,7 @@ void plugin_for_enabled (int type, PluginForEachFunc func, void * data)
void plugin_add_watch (PluginHandle * plugin, PluginForEachFunc func, void *
data)
{
- PluginWatch * watch = g_malloc (sizeof (PluginWatch));
+ PluginWatch * watch = g_slice_new (PluginWatch);
watch->func = func;
watch->data = data;
plugin->watches = g_list_prepend (plugin->watches, watch);
@@ -712,7 +758,7 @@ void plugin_remove_watch (PluginHandle * plugin, PluginForEachFunc func, void *
if (watch->func == func && watch->data == data)
{
- g_free (watch);
+ g_slice_free (PluginWatch, watch);
plugin->watches = g_list_delete_link (plugin->watches, node);
}
@@ -754,7 +800,8 @@ PluginHandle * transport_plugin_for_scheme (const char * scheme)
typedef struct {
const char * ext;
- PluginHandle * plugin;
+ PluginForEachFunc func;
+ void * data;
} PlaylistPluginForExtState;
static bool_t playlist_plugin_for_ext_cb (PluginHandle * plugin,
@@ -764,16 +811,14 @@ static bool_t playlist_plugin_for_ext_cb (PluginHandle * plugin,
(GCompareFunc) g_ascii_strcasecmp))
return TRUE;
- state->plugin = plugin;
- return FALSE;
+ return state->func (plugin, state->data);
}
-PluginHandle * playlist_plugin_for_extension (const char * extension)
+void playlist_plugin_for_ext (const char * ext, PluginForEachFunc func, void * data)
{
- PlaylistPluginForExtState state = {extension, NULL};
+ PlaylistPluginForExtState state = {ext, func, data};
plugin_for_enabled (PLUGIN_TYPE_PLAYLIST, (PluginForEachFunc)
playlist_plugin_for_ext_cb, & state);
- return state.plugin;
}
typedef struct {
diff --git a/src/audacious/plugin-view.c b/src/audacious/plugin-view.c
index 7d3a8af..36959af 100644
--- a/src/audacious/plugin-view.c
+++ b/src/audacious/plugin-view.c
@@ -1,6 +1,6 @@
/*
* plugin-view.c
- * Copyright 2010 John Lindgren
+ * Copyright 2010-2012 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -19,6 +19,9 @@
#include <gtk/gtk.h>
+#include <libaudgui/libaudgui-gtk.h>
+
+#include "i18n.h"
#include "plugin.h"
#include "plugins.h"
#include "ui_preferences.h"
@@ -27,7 +30,6 @@ enum {
PVIEW_COL_NODE,
PVIEW_COL_ENABLED,
PVIEW_COL_NAME,
- PVIEW_COL_PATH,
PVIEW_COLS
};
@@ -88,8 +90,8 @@ static bool_t fill_cb (PluginHandle * p, GtkTreeModel * model)
GtkTreeIter iter;
gtk_list_store_append ((GtkListStore *) model, & iter);
gtk_list_store_set ((GtkListStore *) model, & iter, PVIEW_COL_NODE, n,
- PVIEW_COL_ENABLED, plugin_get_enabled (p), PVIEW_COL_NAME, plugin_get_name
- (p), PVIEW_COL_PATH, plugin_get_filename (p), -1);
+ PVIEW_COL_ENABLED, plugin_get_enabled (p), PVIEW_COL_NAME,
+ plugin_get_name (p), -1);
n->p = p;
n->model = model;
@@ -103,7 +105,7 @@ static bool_t fill_cb (PluginHandle * p, GtkTreeModel * model)
static void list_fill (GtkTreeView * tree, void * type)
{
GtkTreeModel * model = (GtkTreeModel *) gtk_list_store_new (PVIEW_COLS,
- G_TYPE_POINTER, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING);
+ G_TYPE_POINTER, G_TYPE_BOOLEAN, G_TYPE_STRING);
gtk_tree_view_set_model (tree, model);
GtkTreeViewColumn * col = gtk_tree_view_column_new ();
@@ -117,17 +119,15 @@ static void list_fill (GtkTreeView * tree, void * type)
gtk_tree_view_column_set_attributes (col, rend, "active", PVIEW_COL_ENABLED,
NULL);
- for (int i = PVIEW_COL_NAME; i <= PVIEW_COL_PATH; i ++)
- {
- col = gtk_tree_view_column_new ();
- gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
- gtk_tree_view_column_set_resizable (col, FALSE);
- gtk_tree_view_append_column (tree, col);
-
- rend = gtk_cell_renderer_text_new ();
- gtk_tree_view_column_pack_start (col, rend, FALSE);
- gtk_tree_view_column_set_attributes (col, rend, "text", i, NULL);
- }
+ col = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_expand (col, TRUE);
+ gtk_tree_view_column_set_resizable (col, FALSE);
+ gtk_tree_view_append_column (tree, col);
+
+ rend = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (col, rend, FALSE);
+ gtk_tree_view_column_set_attributes (col, rend, "text", PVIEW_COL_NAME, NULL);
plugin_for_each (GPOINTER_TO_INT (type), (PluginForEachFunc) fill_cb, model);
}
@@ -192,14 +192,14 @@ static void button_update (GtkTreeView * tree, GtkWidget * b)
gtk_widget_set_sensitive (b, FALSE);
}
-static void do_config (GtkTreeView * tree)
+static void do_config (void * tree)
{
PluginHandle * plugin = get_selected_plugin (tree);
g_return_if_fail (plugin != NULL);
plugin_do_configure (plugin);
}
-static void do_about (GtkTreeView * tree)
+static void do_about (void * tree)
{
PluginHandle * plugin = get_selected_plugin (tree);
g_return_if_fail (plugin != NULL);
@@ -239,21 +239,19 @@ GtkWidget * plugin_view_new (int type)
GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start ((GtkBox *) vbox, hbox, FALSE, FALSE, 0);
- GtkWidget * config = gtk_button_new_from_stock (GTK_STOCK_PREFERENCES);
+ GtkWidget * config = audgui_button_new (_("_Settings"),
+ "preferences-system", do_config, tree);
gtk_box_pack_start ((GtkBox *) hbox, config, FALSE, FALSE, 0);
gtk_widget_set_sensitive (config, FALSE);
g_object_set_data ((GObject *) config, "watcher", (void *) config_watcher);
g_signal_connect (tree, "cursor-changed", (GCallback) button_update, config);
- g_signal_connect_swapped (config, "clicked", (GCallback)
- do_config, tree);
g_signal_connect (config, "destroy", (GCallback) button_destroy, NULL);
- GtkWidget * about = gtk_button_new_from_stock (GTK_STOCK_ABOUT);
+ GtkWidget * about = audgui_button_new (_("_About"), "help-about", do_about, tree);
gtk_box_pack_start ((GtkBox *) hbox, about, FALSE, FALSE, 0);
gtk_widget_set_sensitive (about, FALSE);
g_object_set_data ((GObject *) about, "watcher", (void *) about_watcher);
g_signal_connect (tree, "cursor-changed", (GCallback) button_update, about);
- g_signal_connect_swapped (about, "clicked", (GCallback) do_about, tree);
g_signal_connect (about, "destroy", (GCallback) button_destroy, NULL);
return vbox;
diff --git a/src/audacious/plugin.h b/src/audacious/plugin.h
index 4591894..49f5f27 100644
--- a/src/audacious/plugin.h
+++ b/src/audacious/plugin.h
@@ -1,6 +1,6 @@
/*
* plugin.h
- * Copyright 2005-2012 William Pitcock, Yoshiki Yazawa, Eugene Zagidullin, and
+ * Copyright 2005-2013 William Pitcock, Yoshiki Yazawa, Eugene Zagidullin, and
* John Lindgren
*
* Redistribution and use in source and binary forms, with or without
@@ -31,22 +31,6 @@
/* "Magic" bytes identifying an Audacious plugin header. */
#define _AUD_PLUGIN_MAGIC 0x8EAC8DE2
-/* API version. Plugins are marked with this number at compile time.
- *
- * _AUD_PLUGIN_VERSION is the current version; _AUD_PLUGIN_VERSION_MIN is
- * the oldest one we are backward compatible with. Plugins marked older than
- * _AUD_PLUGIN_VERSION_MIN or newer than _AUD_PLUGIN_VERSION are not loaded.
- *
- * Before releases that add new pointers to the end of the API tables, increment
- * _AUD_PLUGIN_VERSION but leave _AUD_PLUGIN_VERSION_MIN the same.
- *
- * Before releases that break backward compatibility (e.g. remove pointers from
- * the API tables), increment _AUD_PLUGIN_VERSION *and* set
- * _AUD_PLUGIN_VERSION_MIN to the same value. */
-
-#define _AUD_PLUGIN_VERSION_MIN 40 /* 3.3-devel */
-#define _AUD_PLUGIN_VERSION 43 /* 3.4-devel */
-
/* A NOTE ON THREADS
*
* How thread-safe a plugin must be depends on the type of plugin. Note that
@@ -254,78 +238,6 @@ struct _EffectPlugin
bool_t preserves_format;
};
-struct OutputAPI
-{
- /* Prepares the output system for playback in the specified format. Returns
- * TRUE on success. If the call fails, no other output functions may be
- * called. */
- bool_t (* open_audio) (int format, int rate, int channels);
-
- /* Informs the output system of replay gain values for the current song so
- * that volume levels can be adjusted accordingly, if the user so desires.
- * This may be called at any time during playback should the values change. */
- void (* set_replaygain_info) (const ReplayGainInfo * info);
-
- /* Passes audio data to the output system for playback. The data must be in
- * the format passed to open_audio, and the length (in bytes) must be an
- * integral number of frames. This function blocks until all the data has
- * been written (though it may not yet be heard by the user). */
- void (* write_audio) (void * data, int length);
-
- /* Interrupts a call to write_audio() so that it returns immediately.
- * Buffered audio data is discarded. Until set_written_time() or
- * open_audio() is called, further calls to write_audio() will have no
- * effect and will return immediately. */
- void (* abort_write) (void);
-
- /* Pauses or unpauses playback. If playback is paused during a call to
- * write_audio(), the call will block until playback is unpaused again or
- * abort_write() is called. */
- void (* pause) (bool_t pause);
-
- /* Returns the time counter. Note that this represents the amount of audio
- * data passed to the output system, not the amount actually heard by the
- * user. */
- int (* written_time) (void);
-
- /* Sets the time counter to a new value. Does not perform a flush; the name
- * is kept only for compatibility. */
- void (* flush) (int time);
-};
-
-typedef const struct _InputPlayback InputPlayback;
-
-struct _InputPlayback
-{
- /* Pointer to the output API functions. */
- const struct OutputAPI * output;
-
- /* Allows the plugin to associate data with a playback instance. */
- void (* set_data) (InputPlayback * p, void * data);
-
- /* Returns the pointer passed to set_data. */
- void * (* get_data) (InputPlayback * p);
-
- /* Signifies that the plugin has started playback is ready to accept mseek,
- * pause, and stop calls. */
- void (* set_pb_ready) (InputPlayback * p);
-
- /* Updates attributes of the stream. "bitrate" is in bits per second.
- * "samplerate" is in hertz. */
- void (* set_params) (InputPlayback * p, int bitrate, int samplerate,
- int channels);
-
- /* Updates metadata for the stream. Caller gives up ownership of one
- * reference to the tuple. */
- void (* set_tuple) (InputPlayback * playback, Tuple * tuple);
-
- /* If replay gain settings are stored in the tuple associated with the
- * current song, this function can be called (after opening audio) to apply
- * those settings. If the settings are changed in a call to set_tuple, this
- * function must be called again to apply the updated settings. */
- void (* set_gain_from_playlist) (InputPlayback * playback);
-};
-
struct _InputPlugin
{
PLUGIN_COMMON_FIELDS
@@ -355,8 +267,11 @@ struct _InputPlugin
/* Pointer to an array (terminated with NULL) of MIME types the plugin can
* handle. */
const char * const * mimes;
+
/* Pointer to an array (terminated with NULL) of custom URI schemes the
- * plugin can handle. */
+ * plugin supports. Plugins using custom URI schemes are expected to
+ * handle their own I/O. Hence, any VFSFile pointers passed to play(),
+ * probe_for_tuple(), etc. will be NULL. */
const char * const * schemes;
/* How quickly the plugin should be tried in searching for a plugin to
@@ -364,73 +279,28 @@ struct _InputPlugin
* with priority 0 are tried first, 10 last. */
int priority;
- /* Must return nonzero if the plugin can handle this file. If the file
- * could not be opened, "file" will be NULL. (This is normal in the case of
- * special URI schemes like cdda:// that do not represent actual files.) */
+ /* Returns TRUE if the plugin can handle the file. */
bool_t (* is_our_file_from_vfs) (const char * filename, VFSFile * file);
- /* Must return a tuple containing metadata for this file, or NULL if no
- * metadata could be read. If the file could not be opened, "file" will be
- * NULL. Audacious takes over one reference to the tuple returned. */
+ /* Reads metadata from the file, returning a reference to the tuple produced. */
Tuple * (* probe_for_tuple) (const char * filename, VFSFile * file);
- /* Optional. Must write metadata from a tuple to this file. Must return
- * nonzero on success or zero on failure. "file" will never be NULL. */
- /* Bug: This function does not support special URI schemes like cdda://,
- * since no file name is passed. */
- bool_t (* update_song_tuple) (const Tuple * tuple, VFSFile * file);
+ /* Plays the file. Returns FALSE on error. Also see input-api.h. */
+ bool_t (* play) (const char * filename, VFSFile * file);
- /* Optional, and not recommended. Must show a window with information about
- * this file. If this function is provided, update_song_tuple should not be. */
- /* Bug: Implementing this function duplicates user interface code and code
- * to open the file in each and every plugin. */
- void (* file_info_box) (const char * filename);
+ /* Optional. Writes metadata to the file, returning FALSE on error. */
+ bool_t (* update_song_tuple) (const char * filename, VFSFile * file, const Tuple * tuple);
- /* Optional. Must try to read an "album art" image embedded in this file.
- * Must return nonzero on success or zero on failure. If the file could not
- * be opened, "file" will be NULL. On success, must fill "data" with a
- * pointer to a block of data allocated with g_malloc and "size" with the
- * size in bytes of that block. The data may be in any format supported by
- * GTK. Audacious will free the data when it is no longer needed. */
+ /* Optional. Reads an album art image (JPEG or PNG data) from the file.
+ * Returns a pointer to the data along with its size in bytes. The returned
+ * data will be freed when no longer needed. Returns FALSE on error. */
bool_t (* get_song_image) (const char * filename, VFSFile * file,
void * * data, int64_t * size);
- /* Must try to play this file. "playback" is a structure containing output-
- * related functions which the plugin may make use of. It also contains a
- * "data" pointer which the plugin may use to refer private data associated
- * with the playback state. This pointer can then be used from pause,
- * mseek, and stop. If the file could not be opened, "file" will be NULL.
- * "start_time" is the position in milliseconds at which to start from, or
- * -1 to start from the beginning of the file. "stop_time" is the position
- * in milliseconds at which to end playback, or -1 to play to the end of the
- * file. "paused" specifies whether playback should immediately be paused.
- * Must return nonzero if some of the file was successfully played or zero
- * on failure. */
- bool_t (* play) (InputPlayback * playback, const char * filename,
- VFSFile * file, int start_time, int stop_time, bool_t pause);
-
- /* Must pause or unpause a file currently being played. This function will
- * be called from a different thread than play, but it will not be called
- * before the plugin calls set_pb_ready or after stop is called. */
- void (* pause) (InputPlayback * playback, bool_t paused);
-
- /* Optional. Must seek to the given position in milliseconds within a file
- * currently being played. This function will be called from a different
- * thread than play, but it will not be called before the plugin calls
- * set_pb_ready or after stop is called. */
- void (* mseek) (InputPlayback * playback, int time);
-
- /* Must signal a currently playing song to stop and cause play to return.
- * This function will be called from a different thread than play. It will
- * only be called once. It should not join the thread from which play is
- * called. */
- void (* stop) (InputPlayback * playback);
-
- /* Advanced, for plugins that do not use Audacious's output system. Use at
- * your own risk. */
- int (* get_time) (InputPlayback * playback);
- int (* get_volume) (int * l, int * r);
- int (* set_volume) (int l, int r);
+ /* Optional. Displays a window showing info about the file. In general,
+ * this function should be avoided since Audacious already provides a file
+ * info window. */
+ void (* file_info_box) (const char * filename);
};
struct _GeneralPlugin
@@ -467,25 +337,10 @@ struct _IfacePlugin
{
PLUGIN_COMMON_FIELDS
- /* is_shown() may return nonzero even if the interface is not actually
- * visible; for example, if it is obscured by other windows or minimized.
- * is_focused() only returns nonzero if the interface is actually visible;
- * in X11, this should be determined by whether the interface has the
- * toplevel focus. show() should show and raise the interface, so that both
- * is_shown() and is_focused() will return nonzero. */
void (* show) (bool_t show);
- bool_t (* is_shown) (void);
- bool_t (* is_focused) (void);
-
- void (* show_error) (const char * markup);
- void (* show_filebrowser) (bool_t play_button);
- void (* show_jump_to_track) (void);
void (* run_gtk_plugin) (void /* GtkWidget */ * widget, const char * name);
void (* stop_gtk_plugin) (void /* GtkWidget */ * widget);
-
- void (* install_toolbar) (void /* GtkWidget */ * button);
- void (* uninstall_toolbar) (void /* GtkWidget */ * button);
};
#undef PLUGIN_COMMON_FIELDS
diff --git a/src/audacious/pluginenum.c b/src/audacious/pluginenum.c
index ed8df47..58216a5 100644
--- a/src/audacious/pluginenum.c
+++ b/src/audacious/pluginenum.c
@@ -1,6 +1,6 @@
/*
* pluginenum.c
- * Copyright 2007-2011 William Pitcock and John Lindgren
+ * Copyright 2007-2013 William Pitcock and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -19,12 +19,14 @@
#include <assert.h>
#include <errno.h>
-#include <glib.h>
-#include <gmodule.h>
#include <pthread.h>
#include <string.h>
#include <sys/stat.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gmodule.h>
+
#include <libaudcore/audstrings.h>
#include <libaudgui/init.h>
@@ -34,17 +36,27 @@
#define AUD_API_DECLARE
#include "drct.h"
+#include "input.h"
#include "misc.h"
#include "playlist.h"
#include "plugins.h"
#undef AUD_API_DECLARE
-static const char * plugin_dir_list[] = {PLUGINSUBS, NULL};
+static const char * plugin_dir_list[] = {
+ "Transport",
+ "Container",
+ "Input",
+ "Output",
+ "Effect",
+ "General",
+ "Visualization"
+};
char verbose = 0;
AudAPITable api_table = {
.drct_api = & drct_api,
+ .input_api = & input_api,
.misc_api = & misc_api,
.playlist_api = & playlist_api,
.plugins_api = & plugins_api,
@@ -132,7 +144,9 @@ static void plugin2_unload (LoadedModule * loaded)
}
pthread_mutex_lock (& mutex);
+#ifndef VALGRIND_FRIENDLY
g_module_close (loaded->module);
+#endif
g_slice_free (LoadedModule, loaded);
pthread_mutex_unlock (& mutex);
}
@@ -144,8 +158,8 @@ static bool_t scan_plugin_func(const char * path, const char * basename, void *
if (!str_has_suffix_nocase(basename, PLUGIN_SUFFIX))
return FALSE;
- struct stat st;
- if (stat (path, & st))
+ GStatBuf st;
+ if (g_stat (path, & st) < 0)
{
fprintf (stderr, "Unable to stat %s: %s\n", path, strerror (errno));
return FALSE;
@@ -166,36 +180,17 @@ void plugin_system_init(void)
{
assert (g_module_supported ());
- char *dir;
- int dirsel = 0;
-
- audgui_init (& api_table);
+ audgui_init (& api_table, _AUD_PLUGIN_VERSION);
plugin_registry_load ();
-#ifndef DISABLE_USER_PLUGIN_DIR
- scan_plugins (get_path (AUD_PATH_USER_PLUGIN_DIR));
- /*
- * This is in a separate loop so if the user puts them in the
- * wrong dir we'll still get them in the right order (home dir
- * first) - Zinx
- */
- while (plugin_dir_list[dirsel])
- {
- dir = g_build_filename (get_path (AUD_PATH_USER_PLUGIN_DIR),
- plugin_dir_list[dirsel ++], NULL);
- scan_plugins(dir);
- g_free(dir);
- }
- dirsel = 0;
-#endif
+ const char * path = get_path (AUD_PATH_PLUGIN_DIR);
- while (plugin_dir_list[dirsel])
+ for (int i = 0; i < ARRAY_LEN (plugin_dir_list); i ++)
{
- dir = g_build_filename (get_path (AUD_PATH_PLUGIN_DIR),
- plugin_dir_list[dirsel ++], NULL);
- scan_plugins(dir);
- g_free(dir);
+ char * dir = filename_build (path, plugin_dir_list[i]);
+ scan_plugins (dir);
+ str_unref (dir);
}
plugin_registry_prune ();
diff --git a/src/audacious/plugins-api.h b/src/audacious/plugins-api.h
index 6a1d4de..af4c1b2 100644
--- a/src/audacious/plugins-api.h
+++ b/src/audacious/plugins-api.h
@@ -1,6 +1,6 @@
/*
* plugins-api.h
- * Copyright 2010 John Lindgren
+ * Copyright 2010-2012 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -37,9 +37,12 @@ AUD_FUNC1 (PluginHandle *, plugin_lookup_basename, const char *, basename)
AUD_FUNC1 (const void *, plugin_get_header, PluginHandle *, plugin)
AUD_FUNC1 (PluginHandle *, plugin_by_header, const void *, header)
+AUD_FUNC1 (int, plugin_count, int, type)
+AUD_FUNC1 (int, plugin_get_index, PluginHandle *, plugin)
+AUD_FUNC2 (PluginHandle *, plugin_by_index, int, type, int, index)
+
AUD_FUNC2 (int, plugin_compare, PluginHandle *, a, PluginHandle *, b)
-AUD_VFUNC3 (plugin_for_each, int, type, PluginForEachFunc, func, void *,
- data)
+AUD_VFUNC3 (plugin_for_each, int, type, PluginForEachFunc, func, void *, data)
AUD_FUNC1 (bool_t, plugin_get_enabled, PluginHandle *, plugin)
AUD_VFUNC3 (plugin_for_enabled, int, type, PluginForEachFunc, func,
diff --git a/src/audacious/plugins.h b/src/audacious/plugins.h
index ccda6b1..ea956ee 100644
--- a/src/audacious/plugins.h
+++ b/src/audacious/plugins.h
@@ -1,6 +1,6 @@
/*
* plugins.h
- * Copyright 2010 John Lindgren
+ * Copyright 2010-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -24,6 +24,7 @@
#include <audacious/types.h>
#include <libaudcore/core.h>
+/* returns TRUE to call again for the next plugin, FALSE to stop */
typedef bool_t (* PluginForEachFunc) (PluginHandle * plugin, void * data);
#define AUD_API_NAME PluginsAPI
@@ -58,9 +59,8 @@ void plugin_set_enabled (PluginHandle * plugin, bool_t enabled);
void * plugin_get_misc_data (PluginHandle * plugin, int size);
PluginHandle * transport_plugin_for_scheme (const char * scheme);
-PluginHandle * playlist_plugin_for_extension (const char * extension);
-void input_plugin_for_key (int key, const char * value, PluginForEachFunc
- func, void * data);
+void playlist_plugin_for_ext (const char * ext, PluginForEachFunc func, void * data);
+void input_plugin_for_key (int key, const char * value, PluginForEachFunc func, void * data);
bool_t input_plugin_has_images (PluginHandle * plugin);
bool_t input_plugin_has_subtunes (PluginHandle * plugin);
bool_t input_plugin_can_write_tuple (PluginHandle * plugin);
diff --git a/src/audacious/preferences.c b/src/audacious/preferences.c
new file mode 100644
index 0000000..eb1b651
--- /dev/null
+++ b/src/audacious/preferences.c
@@ -0,0 +1,641 @@
+/*
+ * preferences.c
+ * Copyright 2007-2012 Tomasz MoƄ, William Pitcock, 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 "preferences.h"
+
+#include <string.h>
+#include <gtk/gtk.h>
+
+#include "i18n.h"
+#include "misc.h"
+
+/* HELPERS */
+
+static bool_t widget_get_bool (const PreferencesWidget * widget)
+{
+ g_return_val_if_fail (widget->cfg_type == VALUE_BOOLEAN, FALSE);
+
+ if (widget->cfg)
+ return * (bool_t *) widget->cfg;
+ else if (widget->cname)
+ return get_bool (widget->csect, widget->cname);
+ else
+ return FALSE;
+}
+
+static void widget_set_bool (const PreferencesWidget * widget, bool_t value)
+{
+ g_return_if_fail (widget->cfg_type == VALUE_BOOLEAN);
+
+ if (widget->cfg)
+ * (bool_t *) widget->cfg = value;
+ else if (widget->cname)
+ set_bool (widget->csect, widget->cname, value);
+
+ if (widget->callback)
+ widget->callback ();
+}
+
+static int widget_get_int (const PreferencesWidget * widget)
+{
+ g_return_val_if_fail (widget->cfg_type == VALUE_INT, 0);
+
+ if (widget->cfg)
+ return * (int *) widget->cfg;
+ else if (widget->cname)
+ return get_int (widget->csect, widget->cname);
+ else
+ return 0;
+}
+
+static void widget_set_int (const PreferencesWidget * widget, int value)
+{
+ g_return_if_fail (widget->cfg_type == VALUE_INT);
+
+ if (widget->cfg)
+ * (int *) widget->cfg = value;
+ else if (widget->cname)
+ set_int (widget->csect, widget->cname, value);
+
+ if (widget->callback)
+ widget->callback ();
+}
+
+static double widget_get_double (const PreferencesWidget * widget)
+{
+ g_return_val_if_fail (widget->cfg_type == VALUE_FLOAT, 0);
+
+ if (widget->cfg)
+ return * (float *) widget->cfg;
+ else if (widget->cname)
+ return get_double (widget->csect, widget->cname);
+ else
+ return 0;
+}
+
+static void widget_set_double (const PreferencesWidget * widget, double value)
+{
+ g_return_if_fail (widget->cfg_type == VALUE_FLOAT);
+
+ if (widget->cfg)
+ * (float *) widget->cfg = value;
+ else if (widget->cname)
+ set_double (widget->csect, widget->cname, value);
+
+ if (widget->callback)
+ widget->callback ();
+}
+
+static char * widget_get_string (const PreferencesWidget * widget)
+{
+ g_return_val_if_fail (widget->cfg_type == VALUE_STRING, NULL);
+
+ if (widget->cfg)
+ return str_get (* (char * *) widget->cfg);
+ else if (widget->cname)
+ return get_str (widget->csect, widget->cname);
+ else
+ return NULL;
+}
+
+static void widget_set_string (const PreferencesWidget * widget, const char * value)
+{
+ g_return_if_fail (widget->cfg_type == VALUE_STRING);
+
+ if (widget->cfg)
+ {
+ g_free (* (char * *) widget->cfg);
+ * (char * *) widget->cfg = g_strdup (value);
+ }
+ else if (widget->cname)
+ set_str (widget->csect, widget->cname, value);
+
+ if (widget->callback)
+ widget->callback ();
+}
+
+/* WIDGET_CHK_BTN */
+
+static void on_toggle_button_toggled (GtkToggleButton * button, const PreferencesWidget * widget)
+{
+ bool_t active = gtk_toggle_button_get_active (button);
+ widget_set_bool (widget, active);
+
+ GtkWidget * child = g_object_get_data ((GObject *) button, "child");
+ if (child)
+ gtk_widget_set_sensitive (child, active);
+}
+
+static void init_toggle_button (GtkWidget * button, const PreferencesWidget * widget)
+{
+ if (widget->cfg_type != VALUE_BOOLEAN)
+ return;
+
+ gtk_toggle_button_set_active ((GtkToggleButton *) button, widget_get_bool (widget));
+ g_signal_connect (button, "toggled", (GCallback) on_toggle_button_toggled, (void *) widget);
+}
+
+/* WIDGET_LABEL */
+
+static void create_label (const PreferencesWidget * widget, GtkWidget * * label,
+ GtkWidget * * icon, const char * domain)
+{
+ if (widget->data.label.stock_id)
+ * icon = gtk_image_new_from_icon_name (widget->data.label.stock_id, GTK_ICON_SIZE_BUTTON);
+
+ * label = gtk_label_new_with_mnemonic (dgettext (domain, widget->label));
+ gtk_label_set_use_markup ((GtkLabel *) * label, TRUE);
+
+ if (widget->data.label.single_line == FALSE)
+ gtk_label_set_line_wrap ((GtkLabel *) * label, TRUE);
+
+ gtk_misc_set_alignment ((GtkMisc *) * label, 0, 0.5);
+}
+
+/* WIDGET_RADIO_BTN */
+
+static void on_radio_button_toggled (GtkWidget * button, const PreferencesWidget * widget)
+{
+ if (gtk_toggle_button_get_active ((GtkToggleButton *) button))
+ widget_set_int (widget, widget->data.radio_btn.value);
+}
+
+static void init_radio_button (GtkWidget * button, const PreferencesWidget * widget)
+{
+ if (widget->cfg_type != VALUE_INT)
+ return;
+
+ if (widget_get_int (widget) == widget->data.radio_btn.value)
+ gtk_toggle_button_set_active ((GtkToggleButton *) button, TRUE);
+
+ g_signal_connect (button, "toggled", (GCallback) on_radio_button_toggled, (void *) widget);
+}
+
+/* WIDGET_SPIN_BTN */
+
+static void on_spin_btn_changed_int (GtkSpinButton * button, const PreferencesWidget * widget)
+{
+ widget_set_int (widget, gtk_spin_button_get_value_as_int (button));
+}
+
+static void on_spin_btn_changed_float (GtkSpinButton * button, const PreferencesWidget * widget)
+{
+ widget_set_double (widget, gtk_spin_button_get_value (button));
+}
+
+static void create_spin_button (const PreferencesWidget * widget,
+ GtkWidget * * label_pre, GtkWidget * * spin_btn, GtkWidget * * label_past,
+ const char * domain)
+{
+ * label_pre = gtk_label_new (dgettext (domain, widget->label));
+ * spin_btn = gtk_spin_button_new_with_range (widget->data.spin_btn.min,
+ widget->data.spin_btn.max, widget->data.spin_btn.step);
+
+ if (widget->tooltip)
+ gtk_widget_set_tooltip_text (* spin_btn, dgettext (domain, widget->tooltip));
+
+ if (widget->data.spin_btn.right_label)
+ * label_past = gtk_label_new (dgettext (domain, widget->data.spin_btn.right_label));
+
+ switch (widget->cfg_type)
+ {
+ case VALUE_INT:
+ gtk_spin_button_set_value ((GtkSpinButton *) * spin_btn, widget_get_int (widget));
+ g_signal_connect (* spin_btn, "value_changed", (GCallback)
+ on_spin_btn_changed_int, (void *) widget);
+ break;
+
+ case VALUE_FLOAT:
+ gtk_spin_button_set_value ((GtkSpinButton *) * spin_btn, widget_get_double (widget));
+ g_signal_connect (* spin_btn, "value_changed", (GCallback)
+ on_spin_btn_changed_float, (void *) widget);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* WIDGET_FONT_BTN */
+
+static void on_font_btn_font_set (GtkFontButton * button, const PreferencesWidget * widget)
+{
+ widget_set_string (widget, gtk_font_button_get_font_name (button));
+}
+
+void create_font_btn (const PreferencesWidget * widget, GtkWidget * * label,
+ GtkWidget * * font_btn, const char * domain)
+{
+ * font_btn = gtk_font_button_new ();
+ gtk_font_button_set_use_font ((GtkFontButton *) * font_btn, TRUE);
+ gtk_font_button_set_use_size ((GtkFontButton *) * font_btn, TRUE);
+ gtk_widget_set_hexpand (* font_btn, TRUE);
+
+ if (widget->label)
+ {
+ * label = gtk_label_new_with_mnemonic (dgettext (domain, widget->label));
+ gtk_label_set_use_markup ((GtkLabel *) * label, TRUE);
+ gtk_misc_set_alignment ((GtkMisc *) * label, 1, 0.5);
+ gtk_label_set_justify ((GtkLabel *) * label, GTK_JUSTIFY_RIGHT);
+ gtk_label_set_mnemonic_widget ((GtkLabel *) * label, * font_btn);
+ }
+
+ if (widget->data.font_btn.title)
+ gtk_font_button_set_title ((GtkFontButton *) * font_btn,
+ dgettext (domain, widget->data.font_btn.title));
+
+ char * name = widget_get_string (widget);
+ if (name)
+ {
+ gtk_font_button_set_font_name ((GtkFontButton *) * font_btn, name);
+ str_unref (name);
+ }
+
+ g_signal_connect (* font_btn, "font_set", (GCallback) on_font_btn_font_set, (void *) widget);
+}
+
+/* WIDGET_ENTRY */
+
+static void on_entry_changed (GtkEntry * entry, const PreferencesWidget * widget)
+{
+ widget_set_string (widget, gtk_entry_get_text (entry));
+}
+
+static void create_entry (const PreferencesWidget * widget, GtkWidget * * label,
+ GtkWidget * * entry, const char * domain)
+{
+ * entry = gtk_entry_new ();
+ gtk_entry_set_visibility ((GtkEntry *) * entry, ! widget->data.entry.password);
+ gtk_widget_set_hexpand (* entry, TRUE);
+
+ if (widget->label)
+ * label = gtk_label_new (dgettext (domain, widget->label));
+
+ if (widget->tooltip)
+ gtk_widget_set_tooltip_text (* entry, dgettext (domain, widget->tooltip));
+
+ if (widget->cfg_type == VALUE_STRING)
+ {
+ char * value = widget_get_string (widget);
+ if (value)
+ {
+ gtk_entry_set_text ((GtkEntry *) * entry, value);
+ str_unref (value);
+ }
+
+ g_signal_connect (* entry, "changed", (GCallback) on_entry_changed, (void *) widget);
+ }
+}
+
+/* WIDGET_COMBO_BOX */
+
+static void on_cbox_changed_int (GtkComboBox * combobox, const PreferencesWidget * widget)
+{
+ int position = gtk_combo_box_get_active (combobox);
+ const ComboBoxElements * elements = g_object_get_data ((GObject *) combobox, "comboboxelements");
+ widget_set_int (widget, GPOINTER_TO_INT (elements[position].value));
+}
+
+static void on_cbox_changed_string (GtkComboBox * combobox, const PreferencesWidget * widget)
+{
+ int position = gtk_combo_box_get_active (combobox);
+ const ComboBoxElements * elements = g_object_get_data ((GObject *) combobox, "comboboxelements");
+ widget_set_string (widget, elements[position].value);
+}
+
+static void fill_cbox (GtkWidget * combobox, const PreferencesWidget * widget, const char * domain)
+{
+ const ComboBoxElements * elements = widget->data.combo.elements;
+ int n_elements = widget->data.combo.n_elements;
+
+ if (widget->data.combo.fill)
+ elements = widget->data.combo.fill (& n_elements);
+
+ g_object_set_data ((GObject *) combobox, "comboboxelements", (void *) elements);
+
+ for (int i = 0; i < n_elements; i ++)
+ gtk_combo_box_text_append_text ((GtkComboBoxText *) combobox,
+ dgettext (domain, elements[i].label));
+
+ switch (widget->cfg_type)
+ {
+ case VALUE_INT:;
+ int ivalue = widget_get_int (widget);
+
+ for (int i = 0; i < n_elements; i++)
+ {
+ if (GPOINTER_TO_INT (elements[i].value) == ivalue)
+ {
+ gtk_combo_box_set_active ((GtkComboBox *) combobox, i);
+ break;
+ }
+ }
+
+ g_signal_connect (combobox, "changed", (GCallback) on_cbox_changed_int, (void *) widget);
+ break;
+
+ case VALUE_STRING:;
+ char * value = widget_get_string (widget);
+
+ for(int i = 0; i < n_elements; i++)
+ {
+ if (value && ! strcmp (elements[i].value, value))
+ {
+ gtk_combo_box_set_active ((GtkComboBox *) combobox, i);
+ break;
+ }
+ }
+
+ str_unref (value);
+
+ g_signal_connect (combobox, "changed", (GCallback) on_cbox_changed_string, (void *) widget);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void create_cbox (const PreferencesWidget * widget, GtkWidget * * label,
+ GtkWidget * * combobox, const char * domain)
+{
+ * combobox = gtk_combo_box_text_new ();
+
+ if (widget->label)
+ * label = gtk_label_new (dgettext (domain, widget->label));
+
+ fill_cbox (* combobox, widget, domain);
+}
+
+/* WIDGET_TABLE */
+
+static void fill_grid (GtkWidget * grid, const PreferencesWidget * elements,
+ int n_elements, const char * domain)
+{
+ for (int i = 0; i < n_elements; i ++)
+ {
+ GtkWidget * widget_left = NULL, * widget_middle = NULL, * widget_right = NULL;
+
+ switch (elements[i].type)
+ {
+ case WIDGET_SPIN_BTN:
+ create_spin_button (& elements[i], & widget_left,
+ & widget_middle, & widget_right, domain);
+ break;
+
+ case WIDGET_LABEL:
+ create_label (& elements[i], & widget_middle, & widget_left, domain);
+ break;
+
+ case WIDGET_FONT_BTN:
+ create_font_btn (& elements[i], & widget_left, & widget_middle, domain);
+ break;
+
+ case WIDGET_ENTRY:
+ create_entry (& elements[i], & widget_left, & widget_middle, domain);
+ break;
+
+ case WIDGET_COMBO_BOX:
+ create_cbox (& elements[i], & widget_left, & widget_middle, domain);
+ break;
+
+ default:
+ break;
+ }
+
+ if (widget_left)
+ gtk_grid_attach ((GtkGrid *) grid, widget_left, 0, i, 1, 1);
+
+ if (widget_middle)
+ gtk_grid_attach ((GtkGrid *) grid, widget_middle, 1, i, 1, 1);
+
+ if (widget_right)
+ gtk_grid_attach ((GtkGrid *) grid, widget_right, 2, i, 1, 1);
+ }
+}
+
+/* ALL WIDGETS */
+
+/* box: a GtkBox */
+void create_widgets_with_domain (void * box, const PreferencesWidget * widgets,
+ int n_widgets, const char * domain)
+{
+ GtkWidget * widget = NULL, * child_box = NULL;
+ GSList * radio_btn_group = NULL;
+
+ for (int i = 0; i < n_widgets; i ++)
+ {
+ GtkWidget * label = NULL;
+
+ if (widget && widgets[i].child)
+ {
+ if (! child_box)
+ {
+ child_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ g_object_set_data ((GObject *) widget, "child", child_box);
+
+ GtkWidget * alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
+ gtk_box_pack_start (box, alignment, FALSE, FALSE, 0);
+ gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 0, 12, 0);
+ gtk_container_add ((GtkContainer *) alignment, child_box);
+
+ if (GTK_IS_TOGGLE_BUTTON (widget))
+ gtk_widget_set_sensitive (child_box,
+ gtk_toggle_button_get_active ((GtkToggleButton *) widget));
+ }
+ }
+ else
+ child_box = NULL;
+
+ GtkWidget * alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
+ gtk_alignment_set_padding ((GtkAlignment *) alignment, 6, 0, 12, 0);
+ gtk_box_pack_start (child_box ? (GtkBox *) child_box : box, alignment, FALSE, FALSE, 0);
+
+ widget = NULL;
+
+ if (radio_btn_group && widgets[i].type != WIDGET_RADIO_BTN)
+ radio_btn_group = NULL;
+
+ switch (widgets[i].type)
+ {
+ case WIDGET_CHK_BTN:
+ widget = gtk_check_button_new_with_mnemonic (dgettext (domain, widgets[i].label));
+ init_toggle_button (widget, & widgets[i]);
+ break;
+
+ case WIDGET_LABEL:
+ if (strstr (widgets[i].label, "<b>"))
+ gtk_alignment_set_padding ((GtkAlignment *) alignment,
+ (i == 0) ? 0 : 12, 0, 0, 0);
+
+ GtkWidget * icon = NULL;
+ create_label (& widgets[i], & label, & icon, domain);
+
+ if (icon)
+ {
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start ((GtkBox *) widget, icon, FALSE, FALSE, 0);
+ gtk_box_pack_start ((GtkBox *) widget, label, FALSE, FALSE, 0);
+ }
+ else
+ widget = label;
+
+ break;
+
+ case WIDGET_RADIO_BTN:
+ widget = gtk_radio_button_new_with_mnemonic (radio_btn_group,
+ dgettext (domain, widgets[i].label));
+ radio_btn_group = gtk_radio_button_get_group ((GtkRadioButton *) widget);
+ init_radio_button (widget, & widgets[i]);
+ break;
+
+ case WIDGET_SPIN_BTN:
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
+ GtkWidget * label_pre = NULL, * spin_btn = NULL, * label_past = NULL;
+ create_spin_button (& widgets[i], & label_pre, & spin_btn, & label_past, domain);
+
+ if (label_pre)
+ gtk_box_pack_start ((GtkBox *) widget, label_pre, FALSE, FALSE, 0);
+ if (spin_btn)
+ gtk_box_pack_start ((GtkBox *) widget, spin_btn, FALSE, FALSE, 0);
+ if (label_past)
+ gtk_box_pack_start ((GtkBox *) widget, label_past, FALSE, FALSE, 0);
+
+ break;
+
+ case WIDGET_CUSTOM:
+ if (widgets[i].data.populate)
+ widget = widgets[i].data.populate ();
+
+ break;
+
+ case WIDGET_FONT_BTN:
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
+ GtkWidget * font_btn = NULL;
+ create_font_btn (& widgets[i], & label, & font_btn, domain);
+
+ if (label)
+ gtk_box_pack_start ((GtkBox *) widget, label, FALSE, FALSE, 0);
+ if (font_btn)
+ gtk_box_pack_start ((GtkBox *) widget, font_btn, FALSE, FALSE, 0);
+
+ break;
+
+ case WIDGET_TABLE:
+ widget = gtk_grid_new ();
+ gtk_grid_set_column_spacing ((GtkGrid *) widget, 6);
+ gtk_grid_set_row_spacing ((GtkGrid *) widget, 6);
+
+ fill_grid (widget, widgets[i].data.table.elem, widgets[i].data.table.rows, domain);
+
+ break;
+
+ case WIDGET_ENTRY:
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
+ GtkWidget * entry = NULL;
+ create_entry (& widgets[i], & label, & entry, domain);
+
+ if (label)
+ gtk_box_pack_start ((GtkBox *) widget, label, FALSE, FALSE, 0);
+ if (entry)
+ gtk_box_pack_start ((GtkBox *) widget, entry, TRUE, TRUE, 0);
+
+ break;
+
+ case WIDGET_COMBO_BOX:
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
+ GtkWidget * combo = NULL;
+ create_cbox (& widgets[i], & label, & combo, domain);
+
+ if (label)
+ gtk_box_pack_start ((GtkBox *) widget, label, FALSE, FALSE, 0);
+ if (combo)
+ gtk_box_pack_start ((GtkBox *) widget, combo, FALSE, FALSE, 0);
+
+ break;
+
+ case WIDGET_BOX:
+ if (widgets[i].data.box.horizontal)
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ else
+ widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+
+ create_widgets_with_domain ((GtkBox *) widget,
+ widgets[i].data.box.elem, widgets[i].data.box.n_elem, domain);
+
+ if (widgets[i].data.box.frame)
+ {
+ GtkWidget * frame = gtk_frame_new (dgettext (domain, widgets[i].label));
+ gtk_container_add ((GtkContainer *) frame, widget);
+ widget = frame;
+ }
+
+ break;
+
+ case WIDGET_NOTEBOOK:
+ gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 0, 0, 0);
+
+ widget = gtk_notebook_new ();
+
+ for (int j = 0; j < widgets[i].data.notebook.n_tabs; j ++)
+ {
+ GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_set_border_width ((GtkContainer *) vbox, 6);
+
+ create_widgets_with_domain ((GtkBox *) vbox,
+ widgets[i].data.notebook.tabs[j].widgets,
+ widgets[i].data.notebook.tabs[j].n_widgets, domain);
+
+ gtk_notebook_append_page ((GtkNotebook *) widget, vbox,
+ gtk_label_new (dgettext (domain,
+ widgets[i].data.notebook.tabs[j].name)));
+ }
+
+ break;
+
+ case WIDGET_SEPARATOR:
+ gtk_alignment_set_padding ((GtkAlignment *) alignment, 6, 6, 0, 0);
+
+ widget = gtk_separator_new (widgets[i].data.separator.horizontal
+ ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
+ break;
+
+ default:
+ break;
+ }
+
+ if (widget)
+ {
+ /* use uniform spacing for horizontal boxes */
+ if (gtk_orientable_get_orientation ((GtkOrientable *) box) ==
+ GTK_ORIENTATION_HORIZONTAL)
+ gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 0, 0, 0);
+
+ gtk_container_add ((GtkContainer *) alignment, widget);
+
+ if (widgets[i].tooltip && widgets[i].type != WIDGET_SPIN_BTN)
+ gtk_widget_set_tooltip_text (widget, dgettext (domain,
+ widgets[i].tooltip));
+ }
+ }
+}
diff --git a/src/audacious/preferences.h b/src/audacious/preferences.h
index bff4539..af58567 100644
--- a/src/audacious/preferences.h
+++ b/src/audacious/preferences.h
@@ -1,6 +1,6 @@
/*
* preferences.h
- * Copyright 2007-2011 Tomasz MoƄ, William Pitcock, and John Lindgren
+ * Copyright 2007-2012 Tomasz MoƄ, William Pitcock, and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -93,8 +93,12 @@ struct _PreferencesWidget {
} entry;
struct {
+ /* static init */
const ComboBoxElements * elements;
int n_elements;
+
+ /* runtime init */
+ const ComboBoxElements * (* fill) (int * n_elements);
} combo;
struct {
diff --git a/src/audacious/probe-buffer.c b/src/audacious/probe-buffer.c
index 98f65c8..c27995b 100644
--- a/src/audacious/probe-buffer.c
+++ b/src/audacious/probe-buffer.c
@@ -1,6 +1,6 @@
/*
* probe-buffer.c
- * Copyright 2010-2011 John Lindgren
+ * Copyright 2010-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,9 +17,10 @@
* the use of this software.
*/
-#include <stdlib.h>
#include <string.h>
+#include <glib.h>
+
#include "debug.h"
#include "probe-buffer.h"
@@ -38,7 +39,7 @@ static int probe_buffer_fclose (VFSFile * file)
ProbeBuffer * p = vfs_get_handle (file);
int ret = vfs_fclose (p->file);
- free (p);
+ g_free (p);
return ret;
}
@@ -73,12 +74,6 @@ static int64_t probe_buffer_fwrite (const void * data, int64_t size, int64_t cou
return 0; /* not allowed */
}
-static int probe_buffer_getc (VFSFile * file)
-{
- unsigned char c;
- return (probe_buffer_fread (& c, 1, 1, file) == 1) ? c : EOF;
-}
-
static int probe_buffer_fseek (VFSFile * file, int64_t offset, int whence)
{
ProbeBuffer * p = vfs_get_handle (file);
@@ -101,16 +96,6 @@ static int probe_buffer_fseek (VFSFile * file, int64_t offset, int whence)
return 0;
}
-static int probe_buffer_ungetc (int c, VFSFile * file)
-{
- return (! probe_buffer_fseek (file, -1, SEEK_CUR)) ? c : EOF;
-}
-
-static void probe_buffer_rewind (VFSFile * file)
-{
- probe_buffer_fseek (file, 0, SEEK_SET);
-}
-
static int64_t probe_buffer_ftell (VFSFile * file)
{
return ((ProbeBuffer *) vfs_get_handle (file))->at;
@@ -152,10 +137,7 @@ static VFSConstructor probe_buffer_table =
.vfs_fclose_impl = probe_buffer_fclose,
.vfs_fread_impl = probe_buffer_fread,
.vfs_fwrite_impl = probe_buffer_fwrite,
- .vfs_getc_impl = probe_buffer_getc,
- .vfs_ungetc_impl = probe_buffer_ungetc,
.vfs_fseek_impl = probe_buffer_fseek,
- .vfs_rewind_impl = probe_buffer_rewind,
.vfs_ftell_impl = probe_buffer_ftell,
.vfs_feof_impl = probe_buffer_feof,
.vfs_ftruncate_impl = probe_buffer_ftruncate,
@@ -170,7 +152,7 @@ VFSFile * probe_buffer_new (const char * filename)
if (! file)
return NULL;
- ProbeBuffer * p = malloc (sizeof (ProbeBuffer));
+ ProbeBuffer * p = g_new (ProbeBuffer, 1);
p->file = file;
p->filled = 0;
p->at = 0;
diff --git a/src/audacious/probe.c b/src/audacious/probe.c
index 8c7fe77..c627abe 100644
--- a/src/audacious/probe.c
+++ b/src/audacious/probe.c
@@ -1,6 +1,6 @@
/*
* probe.c
- * Copyright 2009-2010 John Lindgren
+ * Copyright 2009-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -101,7 +101,7 @@ static bool_t probe_func_fast (PluginHandle * plugin, ProbeState * state)
PluginHandle * prev = state->plugin;
state->plugin = NULL;
- if (prev != NULL && ! probe_func (prev, state))
+ if (! probe_func (prev, state))
return FALSE;
}
@@ -113,15 +113,11 @@ static bool_t probe_func_fast (PluginHandle * plugin, ProbeState * state)
static void probe_by_scheme (ProbeState * state)
{
const char * s = strstr (state->filename, "://");
-
if (s == NULL)
return;
AUDDBG ("Probing by scheme.\n");
- char buf[s - state->filename + 1];
- memcpy (buf, state->filename, s - state->filename);
- buf[s - state->filename] = 0;
-
+ SNCOPY (buf, state->filename, s - state->filename);
input_plugin_for_key (INPUT_KEY_SCHEME, buf, (PluginForEachFunc) probe_func_fast, state);
}
@@ -148,7 +144,7 @@ static void probe_by_mime (ProbeState * state)
AUDDBG ("Probing by MIME type.\n");
input_plugin_for_key (INPUT_KEY_MIME, mime, (PluginForEachFunc)
probe_func_fast, state);
- g_free (mime);
+ str_unref (mime);
}
static void probe_by_content (ProbeState * state)
@@ -196,13 +192,27 @@ DONE:
return state.plugin;
}
+static bool_t open_file (const char * filename, InputPlugin * ip,
+ const char * mode, VFSFile * * handle)
+{
+ /* no need to open a handle for custom URI schemes */
+ if (ip->schemes && ip->schemes[0])
+ return TRUE;
+
+ * handle = vfs_fopen (filename, mode);
+ return (* handle != NULL);
+}
+
Tuple * file_read_tuple (const char * filename, PluginHandle * decoder)
{
InputPlugin * ip = plugin_get_header (decoder);
g_return_val_if_fail (ip, NULL);
g_return_val_if_fail (ip->probe_for_tuple, NULL);
- VFSFile * handle = vfs_fopen (filename, "r");
+ VFSFile * handle = NULL;
+ if (! open_file (filename, ip, "r", & handle))
+ return FALSE;
+
Tuple * tuple = ip->probe_for_tuple (filename, handle);
if (handle)
@@ -214,6 +224,9 @@ Tuple * file_read_tuple (const char * filename, PluginHandle * decoder)
bool_t file_read_image (const char * filename, PluginHandle * decoder,
void * * data, int64_t * size)
{
+ * data = NULL;
+ * size = 0;
+
if (! input_plugin_has_images (decoder))
return FALSE;
@@ -221,18 +234,15 @@ bool_t file_read_image (const char * filename, PluginHandle * decoder,
g_return_val_if_fail (ip, FALSE);
g_return_val_if_fail (ip->get_song_image, FALSE);
- VFSFile * handle = vfs_fopen (filename, "r");
+ VFSFile * handle = NULL;
+ if (! open_file (filename, ip, "r", & handle))
+ return FALSE;
+
bool_t success = ip->get_song_image (filename, handle, data, size);
if (handle)
vfs_fclose (handle);
- if (! success)
- {
- * data = NULL;
- * size = 0;
- }
-
return success;
}
@@ -248,12 +258,11 @@ bool_t file_write_tuple (const char * filename, PluginHandle * decoder,
g_return_val_if_fail (ip, FALSE);
g_return_val_if_fail (ip->update_song_tuple, FALSE);
- VFSFile * handle = vfs_fopen (filename, "r+");
-
- if (! handle)
+ VFSFile * handle = NULL;
+ if (! open_file (filename, ip, "r+", & handle))
return FALSE;
- bool_t success = ip->update_song_tuple (tuple, handle);
+ bool_t success = ip->update_song_tuple (filename, handle, tuple);
if (handle)
vfs_fclose (handle);
diff --git a/src/audacious/scanner.c b/src/audacious/scanner.c
index 2ba3718..e3257da 100644
--- a/src/audacious/scanner.c
+++ b/src/audacious/scanner.c
@@ -36,7 +36,7 @@ struct _ScanRequest {
Tuple * tuple;
void * image_data;
int64_t image_len;
- char * image_file;
+ char * image_file; /* pooled */
};
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -73,8 +73,8 @@ static void scan_request_free (ScanRequest * request)
tuple_unref (request->tuple);
str_unref (request->filename);
- free (request->image_data);
- free (request->image_file);
+ g_free (request->image_data);
+ str_unref (request->image_file);
g_slice_free (ScanRequest, request);
}
@@ -144,11 +144,9 @@ void scan_request_get_image_data (ScanRequest * request, void * * data, int64_t
request->image_len = 0;
}
-char * scan_request_get_image_file (ScanRequest * request)
+const char * scan_request_get_image_file (ScanRequest * request)
{
- char * image_file = request->image_file;
- request->image_file = NULL;
- return image_file;
+ return request->image_file;
}
void scanner_init (void)
diff --git a/src/audacious/scanner.h b/src/audacious/scanner.h
index 488c8d9..3eee728 100644
--- a/src/audacious/scanner.h
+++ b/src/audacious/scanner.h
@@ -40,7 +40,7 @@ const char * scan_request_get_filename (ScanRequest * request);
PluginHandle * scan_request_get_decoder (ScanRequest * request);
Tuple * scan_request_get_tuple (ScanRequest * request);
void scan_request_get_image_data (ScanRequest * request, void * * data, int64_t * len);
-char * scan_request_get_image_file (ScanRequest * request);
+const char * scan_request_get_image_file (ScanRequest * request);
void scanner_init (void);
void scanner_cleanup (void);
diff --git a/src/audacious/signals.c b/src/audacious/signals.c
index 89fc8af..2b5515e 100644
--- a/src/audacious/signals.c
+++ b/src/audacious/signals.c
@@ -17,14 +17,14 @@
* the use of this software.
*/
+#ifdef HAVE_SIGWAIT
+
#include <pthread.h>
#include <signal.h>
-#include <libaudcore/hook.h>
-
+#include "drct.h"
#include "main.h"
-#ifdef HAVE_SIGWAIT
static sigset_t signal_set;
static void * signal_thread (void * data)
@@ -32,16 +32,14 @@ static void * signal_thread (void * data)
int signal;
while (! sigwait (& signal_set, & signal))
- event_queue ("quit", NULL);
+ drct_quit ();
return NULL;
}
-#endif
/* Must be called before any threads are created. */
-void signals_init (void)
+void signals_init_one (void)
{
-#ifdef HAVE_SIGWAIT
sigemptyset (& signal_set);
sigaddset (& signal_set, SIGHUP);
sigaddset (& signal_set, SIGINT);
@@ -49,8 +47,12 @@ void signals_init (void)
sigaddset (& signal_set, SIGTERM);
sigprocmask (SIG_BLOCK, & signal_set, NULL);
+}
+void signals_init_two (void)
+{
pthread_t thread;
pthread_create (& thread, NULL, signal_thread, NULL);
-#endif
}
+
+#endif /* HAVE_SIGWAIT */
diff --git a/src/audacious/types.h b/src/audacious/types.h
index 03cad9a..d160bab 100644
--- a/src/audacious/types.h
+++ b/src/audacious/types.h
@@ -1,6 +1,6 @@
/*
* types.h
- * Copyright 2010 John Lindgren
+ * Copyright 2010-2011 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -52,6 +52,12 @@ typedef struct _PluginPreferences PluginPreferences;
typedef struct _PreferencesWidget PreferencesWidget;
typedef struct {
+ char * name;
+ float preamp;
+ float bands[AUD_EQUALIZER_NBANDS];
+} EqualizerPreset;
+
+typedef struct {
float track_gain; /* dB */
float track_peak; /* 0-1 */
float album_gain; /* dB */
diff --git a/src/audacious/ui_albumart.c b/src/audacious/ui_albumart.c
index d8e4a36..a3f14a4 100644
--- a/src/audacious/ui_albumart.c
+++ b/src/audacious/ui_albumart.c
@@ -1,6 +1,6 @@
/*
* ui_albumart.c
- * Copyright 2006 Michael Hanselmann and Yoshiki Yazawa
+ * Copyright 2006-2013 Michael Hanselmann, Yoshiki Yazawa, and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -23,187 +23,166 @@
#include <libaudcore/audstrings.h>
#include "i18n.h"
+#include "main.h"
#include "misc.h"
+#include "util.h"
-static bool_t
-has_front_cover_extension(const char *name)
-{
- char *ext;
+typedef struct {
+ const char * basename;
+ Index * include, * exclude;
+} SearchParams;
- ext = strrchr(name, '.');
- if (!ext) {
- /* No file extension */
+static bool_t has_front_cover_extension (const char * name)
+{
+ char * ext = strrchr (name, '.');
+ if (! ext)
return FALSE;
- }
- return g_ascii_strcasecmp(ext, ".jpg") == 0 ||
- g_ascii_strcasecmp(ext, ".jpeg") == 0 ||
- g_ascii_strcasecmp(ext, ".png") == 0;
+ return ! g_ascii_strcasecmp (ext, ".jpg") ||
+ ! g_ascii_strcasecmp (ext, ".jpeg") || ! g_ascii_strcasecmp (ext, ".png");
}
-static bool_t
-cover_name_filter(const char *name, const char *filter, const bool_t ret_on_empty)
+static bool_t cover_name_filter (const char * name, Index * keywords, bool_t ret_on_empty)
{
- bool_t result = FALSE;
- char **splitted;
- char *current;
- char *lname;
- int i;
-
- if (!filter || strlen(filter) == 0) {
+ int count = index_count (keywords);
+ if (! count)
return ret_on_empty;
- }
-
- splitted = g_strsplit(filter, ",", 0);
- lname = g_ascii_strdown (name, -1);
- for (i = 0; ! result && (current = splitted[i]); i ++)
+ for (int i = 0; i < count; i ++)
{
- char * stripped = g_strstrip (g_ascii_strdown (current, -1));
- result = result || strstr(lname, stripped);
- g_free(stripped);
+ if (strstr_nocase (name, index_get (keywords, i)))
+ return TRUE;
}
- g_free(lname);
- g_strfreev(splitted);
-
- return result;
-}
-
-/* Check wether it's an image we want */
-static bool_t is_front_cover_image (const char * file)
-{
- char * include = get_string (NULL, "cover_name_include");
- char * exclude = get_string (NULL, "cover_name_exclude");
- bool_t accept = cover_name_filter (file, include, TRUE) &&
- ! cover_name_filter (file, exclude, FALSE);
- g_free (include);
- g_free (exclude);
- return accept;
+ return FALSE;
}
-static bool_t
-is_file_image(const char *imgfile, const char *file_name)
+static bool_t is_file_image (const char * imgfile, const char * file_name)
{
- char *imgfile_ext, *file_name_ext;
- size_t imgfile_len, file_name_len;
-
- imgfile_ext = strrchr(imgfile, '.');
- if (!imgfile_ext) {
- /* No file extension */
+ char * imgfile_ext = strrchr (imgfile, '.');
+ if (! imgfile_ext)
return FALSE;
- }
- file_name_ext = strrchr(file_name, '.');
- if (!file_name_ext) {
- /* No file extension */
+ char * file_name_ext = strrchr (file_name, '.');
+ if (! file_name_ext)
return FALSE;
- }
- imgfile_len = (imgfile_ext - imgfile);
- file_name_len = (file_name_ext - file_name);
+ size_t imgfile_len = imgfile_ext - imgfile;
+ size_t file_name_len = file_name_ext - file_name;
- if (imgfile_len == file_name_len) {
- return (g_ascii_strncasecmp(imgfile, file_name, imgfile_len) == 0);
- } else {
- return FALSE;
- }
+ return imgfile_len == file_name_len && ! g_ascii_strncasecmp (imgfile, file_name, imgfile_len);
}
-static char * fileinfo_recursive_get_image (const char * path, const char *
- file_name, int depth)
+static char * fileinfo_recursive_get_image (const char * path,
+ const SearchParams * params, int depth)
{
- GDir *d;
-
- if (get_bool (NULL, "recurse_for_cover") && depth > get_int (NULL, "recurse_for_cover_depth"))
+ GDir * d = g_dir_open (path, 0, NULL);
+ if (! d)
return NULL;
- d = g_dir_open(path, 0, NULL);
-
- if (d) {
- const char *f;
+ const char * name;
- if (get_bool (NULL, "use_file_cover") && file_name)
+ if (get_bool (NULL, "use_file_cover") && ! depth)
+ {
+ /* Look for images matching file name */
+ while ((name = g_dir_read_name (d)))
{
- /* Look for images matching file name */
- while((f = g_dir_read_name(d))) {
- char *newpath = g_strconcat(path, "/", f, NULL);
-
- if (!g_file_test(newpath, G_FILE_TEST_IS_DIR) &&
- has_front_cover_extension(f) &&
- is_file_image(f, file_name)) {
- g_dir_close(d);
- return newpath;
- }
-
- g_free(newpath);
- }
- g_dir_rewind(d);
- }
-
- /* Search for files using filter */
- while ((f = g_dir_read_name(d))) {
- char *newpath = g_strconcat(path, "/", f, NULL);
+ char * newpath = filename_build (path, name);
- if (!g_file_test(newpath, G_FILE_TEST_IS_DIR) &&
- has_front_cover_extension(f) &&
- is_front_cover_image(f)) {
- g_dir_close(d);
+ if (! g_file_test (newpath, G_FILE_TEST_IS_DIR) &&
+ has_front_cover_extension (name) &&
+ is_file_image (name, params->basename))
+ {
+ g_dir_close (d);
return newpath;
}
- g_free(newpath);
+ str_unref (newpath);
}
- g_dir_rewind(d);
- /* checks whether recursive or not. */
- if (! get_bool (NULL, "recurse_for_cover"))
+ g_dir_rewind (d);
+ }
+
+ /* Search for files using filter */
+ while ((name = g_dir_read_name (d)))
+ {
+ char * newpath = filename_build (path, name);
+
+ if (! g_file_test (newpath, G_FILE_TEST_IS_DIR) &&
+ has_front_cover_extension (name) &&
+ cover_name_filter (name, params->include, TRUE) &&
+ ! cover_name_filter (name, params->exclude, FALSE))
{
- g_dir_close(d);
- return NULL;
+ g_dir_close (d);
+ return newpath;
}
+ str_unref (newpath);
+ }
+
+ g_dir_rewind (d);
+
+ if (get_bool (NULL, "recurse_for_cover") && depth < get_int (NULL, "recurse_for_cover_depth"))
+ {
/* Descend into directories recursively. */
- while ((f = g_dir_read_name(d))) {
- char *newpath = g_strconcat(path, "/", f, NULL);
-
- if(g_file_test(newpath, G_FILE_TEST_IS_DIR)) {
- char *tmp = fileinfo_recursive_get_image(newpath,
- NULL, depth + 1);
- if(tmp) {
- g_free(newpath);
- g_dir_close(d);
+ while ((name = g_dir_read_name (d)))
+ {
+ char * newpath = filename_build (path, name);
+
+ if (g_file_test (newpath, G_FILE_TEST_IS_DIR))
+ {
+ char * tmp = fileinfo_recursive_get_image (newpath, params, depth + 1);
+
+ if (tmp)
+ {
+ str_unref (newpath);
+ g_dir_close (d);
return tmp;
}
}
- g_free(newpath);
+ str_unref (newpath);
}
-
- g_dir_close(d);
}
+ g_dir_close (d);
return NULL;
}
char * get_associated_image_file (const char * filename)
{
- if (strncmp (filename, "file://", 7))
- return NULL;
+ char * image_uri = NULL;
- char * unesc = uri_to_filename (filename);
- if (! unesc)
- return NULL;
+ char * local = uri_to_filename (filename);
+ char * base = local ? last_path_element (local) : NULL;
+
+ if (local && base)
+ {
+ char * include = get_str (NULL, "cover_name_include");
+ char * exclude = get_str (NULL, "cover_name_exclude");
+
+ SearchParams params = {
+ .basename = base,
+ .include = str_list_to_index (include, ", "),
+ .exclude = str_list_to_index (exclude, ", ")
+ };
- char * path = g_path_get_dirname (unesc);
- char * base = g_path_get_basename (unesc);
- char * image_unesc = fileinfo_recursive_get_image (path, base, 0);
- char * image_file = image_unesc ? filename_to_uri (image_unesc) : NULL;
+ str_unref (include);
+ str_unref (exclude);
+
+ SNCOPY (path, local, base - 1 - local);
+
+ char * image_local = fileinfo_recursive_get_image (path, & params, 0);
+ if (image_local)
+ image_uri = filename_to_uri (image_local);
+
+ str_unref (image_local);
+
+ index_free_full (params.include, (IndexFreeFunc) str_unref);
+ index_free_full (params.exclude, (IndexFreeFunc) str_unref);
+ }
- g_free (unesc);
- g_free (path);
- g_free (base);
- g_free (image_unesc);
+ str_unref (local);
- return image_file;
+ return image_uri;
}
diff --git a/src/audacious/ui_plugin_menu.c b/src/audacious/ui_plugin_menu.c
index 66475ab..9d81b65 100644
--- a/src/audacious/ui_plugin_menu.c
+++ b/src/audacious/ui_plugin_menu.c
@@ -20,27 +20,28 @@
#include <glib.h>
#include <gtk/gtk.h>
-#include "misc.h"
+#include <libaudgui/menu.h>
-struct Item {
- MenuFunc func;
- const char * name;
- const char * icon;
-};
+#include "i18n.h"
+#include "misc.h"
-static GList * items[AUD_MENU_COUNT];
+static GList * items[AUD_MENU_COUNT]; /* of AudguiMenuItem */
static GtkWidget * menus[AUD_MENU_COUNT];
-static void add_to_menu (GtkWidget * menu, struct Item * item)
+static void configure_plugins (void)
{
- GtkWidget * widget = gtk_image_menu_item_new_with_mnemonic (item->name);
- g_object_set_data ((GObject *) widget, "func", (void *) item->func);
- g_signal_connect (widget, "activate", item->func, NULL);
+ show_prefs_for_plugin_type (PLUGIN_TYPE_GENERAL);
+}
- if (item->icon)
- gtk_image_menu_item_set_image ((GtkImageMenuItem *) widget,
- gtk_image_new_from_stock (item->icon, GTK_ICON_SIZE_MENU));
+static const AudguiMenuItem main_items[] = {
+ {N_("_Plugins ..."), .func = configure_plugins},
+ {.sep = TRUE}
+};
+static void add_to_menu (GtkWidget * menu, const AudguiMenuItem * item)
+{
+ GtkWidget * widget = audgui_menu_item_new_with_domain (item, NULL, NULL);
+ g_object_set_data ((GObject *) widget, "func", (void *) item->func);
gtk_widget_show (widget);
gtk_menu_shell_append ((GtkMenuShell *) menu, widget);
}
@@ -54,6 +55,9 @@ void * get_plugin_menu (int id)
g_signal_connect (menus[id], "destroy", (GCallback)
gtk_widget_destroyed, & menus[id]);
+ if (id == AUD_MENU_MAIN)
+ audgui_menu_init (menus[id], main_items, ARRAY_LEN (main_items), NULL);
+
for (GList * node = items[id]; node; node = node->next)
add_to_menu (menus[id], node->data);
}
@@ -64,7 +68,7 @@ void * get_plugin_menu (int id)
void plugin_menu_add (int id, MenuFunc func, const char * name,
const char * icon)
{
- struct Item * item = g_slice_new (struct Item);
+ AudguiMenuItem * item = g_slice_new0 (AudguiMenuItem);
item->name = name;
item->icon = icon;
item->func = func;
@@ -92,9 +96,9 @@ void plugin_menu_remove (int id, MenuFunc func)
{
next = node->next;
- if (((struct Item *) node->data)->func == func)
+ if (((AudguiMenuItem *) node->data)->func == func)
{
- g_slice_free (struct Item, node->data);
+ g_slice_free (AudguiMenuItem, node->data);
items[id] = g_list_delete_link (items[id], node);
}
}
diff --git a/src/audacious/ui_preferences.c b/src/audacious/ui_preferences.c
index 96dab5b..2949fdc 100644
--- a/src/audacious/ui_preferences.c
+++ b/src/audacious/ui_preferences.c
@@ -1,6 +1,6 @@
/*
* ui_preferences.c
- * Copyright 2006-2011 William Pitcock, Tomasz MoƄ, Michael FĂ€rber, and
+ * Copyright 2006-2012 William Pitcock, Tomasz MoƄ, Michael FĂ€rber, and
* John Lindgren
*
* Redistribution and use in source and binary forms, with or without
@@ -23,7 +23,9 @@
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
+#include <libaudcore/audstrings.h>
#include <libaudcore/hook.h>
+#include <libaudgui/libaudgui-gtk.h>
#include "debug.h"
#include "i18n.h"
@@ -42,36 +44,57 @@
enum CategoryViewCols {
CATEGORY_VIEW_COL_ICON,
CATEGORY_VIEW_COL_NAME,
- CATEGORY_VIEW_COL_ID,
CATEGORY_VIEW_N_COLS
};
typedef struct {
- const char *icon_path;
- const char *name;
+ const char * icon_path;
+ const char * name;
} Category;
typedef struct {
- const char *name;
- const char *tag;
+ int type;
+ const char * name;
+} PluginCategory;
+
+typedef struct {
+ const char * name;
+ const char * tag;
} TitleFieldTag;
-static /* GtkWidget * */ void * prefswin = NULL;
-static GtkWidget *category_treeview = NULL;
-static GtkWidget *category_notebook = NULL;
+static const char aud_version_string[] =
+ "<span size='small'>Audacious " VERSION " (" BUILDSTAMP ")</span>";
-/* prefswin widgets */
-GtkWidget *titlestring_entry;
+static GtkWidget * prefswin;
+static GtkWidget * category_treeview, * category_notebook, * plugin_notebook;
+static GtkWidget * titlestring_entry;
+
+enum {
+ CATEGORY_APPEARANCE = 0,
+ CATEGORY_AUDIO,
+ CATEGORY_NETWORK,
+ CATEGORY_PLAYLIST,
+ CATEGORY_SONG_INFO,
+ CATEGORY_PLUGINS
+};
-static Category categories[] = {
- {"audio.png", N_("Audio")},
- {"connectivity.png", N_("Network")},
- {"playlist.png", N_("Playlist")},
- {"info.png", N_("Song Info")},
- {"plugins.png", N_("Plugins")},
+static const Category categories[] = {
+ { "appearance.png", N_("Appearance") },
+ { "audio.png", N_("Audio") },
+ { "connectivity.png", N_("Network") },
+ { "playlist.png", N_("Playlist")} ,
+ { "info.png", N_("Song Info") },
+ { "plugins.png", N_("Plugins") }
};
-static int n_categories = G_N_ELEMENTS(categories);
+static const PluginCategory plugin_categories[] = {
+ { PLUGIN_TYPE_GENERAL, N_("General") },
+ { PLUGIN_TYPE_EFFECT, N_("Effect") },
+ { PLUGIN_TYPE_VIS, N_("Visualization") },
+ { PLUGIN_TYPE_INPUT, N_("Input") },
+ { PLUGIN_TYPE_PLAYLIST, N_("Playlist") },
+ { PLUGIN_TYPE_TRANSPORT, N_("Transport") }
+};
static TitleFieldTag title_field_tags[] = {
{ N_("Artist") , "${artist}" },
@@ -85,48 +108,76 @@ static TitleFieldTag title_field_tags[] = {
{ N_("Year") , "${year}" },
{ N_("Comment") , "${comment}" },
{ N_("Codec") , "${codec}" },
- { N_("Quality") , "${quality}" },
+ { N_("Quality") , "${quality}" }
};
-static const unsigned int n_title_field_tags = G_N_ELEMENTS(title_field_tags);
#ifdef USE_CHARDET
static ComboBoxElements chardet_detector_presets[] = {
- {"", N_("None")},
- {GUESS_REGION_AR, N_("Arabic")},
- {GUESS_REGION_BL, N_("Baltic")},
- {GUESS_REGION_CN, N_("Chinese")},
- {GUESS_REGION_GR, N_("Greek")},
- {GUESS_REGION_HW, N_("Hebrew")},
- {GUESS_REGION_JP, N_("Japanese")},
- {GUESS_REGION_KR, N_("Korean")},
- {GUESS_REGION_PL, N_("Polish")},
- {GUESS_REGION_RU, N_("Russian")},
- {GUESS_REGION_TW, N_("Taiwanese")},
- {GUESS_REGION_TR, N_("Turkish")}};
+ { "", N_("None")},
+ { GUESS_REGION_AR, N_("Arabic") },
+ { GUESS_REGION_BL, N_("Baltic") },
+ { GUESS_REGION_CN, N_("Chinese") },
+ { GUESS_REGION_GR, N_("Greek") },
+ { GUESS_REGION_HW, N_("Hebrew") },
+ { GUESS_REGION_JP, N_("Japanese") },
+ { GUESS_REGION_KR, N_("Korean") },
+ { GUESS_REGION_PL, N_("Polish") },
+ { GUESS_REGION_RU, N_("Russian") },
+ { GUESS_REGION_TW, N_("Taiwanese") },
+ { GUESS_REGION_TR, N_("Turkish") }
+};
#endif
static ComboBoxElements bitdepth_elements[] = {
- { GINT_TO_POINTER(16), "16" },
- { GINT_TO_POINTER(24), "24" },
- { GINT_TO_POINTER(32), "32" },
- {GINT_TO_POINTER (0), N_("Floating point")},
+ { GINT_TO_POINTER (16), "16" },
+ { GINT_TO_POINTER (24), "24" },
+ { GINT_TO_POINTER (32), "32" },
+ { GINT_TO_POINTER (0), N_("Floating point") }
};
-static void * create_output_plugin_box (void);
+static GArray * iface_combo_elements;
+static int iface_combo_selected;
+static GtkWidget * iface_prefs_box;
+
+static const ComboBoxElements * iface_combo_fill (int * n_elements);
+static void iface_combo_changed (void);
+static void * iface_create_prefs_box (void);
+
+static PreferencesWidget appearance_page_widgets[] = {
+ {WIDGET_LABEL, N_("<b>Interface Settings</b>")},
+ {WIDGET_COMBO_BOX, N_("Interface plugin:"),
+ .cfg_type = VALUE_INT, .cfg = & iface_combo_selected,
+ .data.combo.fill = iface_combo_fill, .callback = iface_combo_changed},
+ {WIDGET_CUSTOM, .data.populate = iface_create_prefs_box}};
+
+static GArray * output_combo_elements;
+static int output_combo_selected;
+static GtkWidget * output_config_button;
+static GtkWidget * output_about_button;
+
+static const ComboBoxElements * output_combo_fill (int * n_elements);
+static void output_combo_changed (void);
+static void * output_create_config_button (void);
+static void * output_create_about_button (void);
static void output_bit_depth_changed (void);
-static PreferencesWidget rg_mode_widgets[] = {
- {WIDGET_CHK_BTN, N_("Album mode"), .cfg_type = VALUE_BOOLEAN, .cname = "replay_gain_album"}};
+static PreferencesWidget output_combo_widgets[] = {
+ {WIDGET_COMBO_BOX, N_("Output plugin:"),
+ .cfg_type = VALUE_INT, .cfg = & output_combo_selected,
+ .data.combo.fill = output_combo_fill, .callback = output_combo_changed},
+ {WIDGET_CUSTOM, .data.populate = output_create_config_button},
+ {WIDGET_CUSTOM, .data.populate = output_create_about_button}};
static PreferencesWidget audio_page_widgets[] = {
{WIDGET_LABEL, N_("<b>Output Settings</b>")},
- {WIDGET_CUSTOM, .data = {.populate = create_output_plugin_box}},
+ {WIDGET_BOX, .data.box = {.elem = output_combo_widgets,
+ .n_elem = ARRAY_LEN (output_combo_widgets), .horizontal = TRUE}},
{WIDGET_COMBO_BOX, N_("Bit depth:"),
.cfg_type = VALUE_INT, .cname = "output_bit_depth", .callback = output_bit_depth_changed,
- .data = {.combo = {bitdepth_elements, G_N_ELEMENTS (bitdepth_elements)}}},
+ .data.combo = {bitdepth_elements, ARRAY_LEN (bitdepth_elements)}},
{WIDGET_SPIN_BTN, N_("Buffer size:"),
.cfg_type = VALUE_INT, .cname = "output_buffer_size",
- .data = {.spin_btn = {100, 10000, 1000, N_("ms")}}},
+ .data.spin_btn = {100, 10000, 1000, N_("ms")}},
{WIDGET_CHK_BTN, N_("Soft clipping"),
.cfg_type = VALUE_BOOLEAN, .cname = "soft_clipping"},
{WIDGET_CHK_BTN, N_("Use software volume control (not recommended)"),
@@ -134,16 +185,17 @@ static PreferencesWidget audio_page_widgets[] = {
{WIDGET_LABEL, N_("<b>Replay Gain</b>")},
{WIDGET_CHK_BTN, N_("Enable Replay Gain"),
.cfg_type = VALUE_BOOLEAN, .cname = "enable_replay_gain"},
- {WIDGET_BOX, .child = TRUE, .data = {.box = {rg_mode_widgets, G_N_ELEMENTS (rg_mode_widgets), TRUE}}},
+ {WIDGET_CHK_BTN, N_("Album mode"), .child = TRUE,
+ .cfg_type = VALUE_BOOLEAN, .cname = "replay_gain_album"},
{WIDGET_CHK_BTN, N_("Prevent clipping (recommended)"), .child = TRUE,
.cfg_type = VALUE_BOOLEAN, .cname = "enable_clipping_prevention"},
{WIDGET_LABEL, N_("<b>Adjust Levels</b>"), .child = TRUE},
{WIDGET_SPIN_BTN, N_("Amplify all files:"), .child = TRUE,
.cfg_type = VALUE_FLOAT, .cname = "replay_gain_preamp",
- .data = {.spin_btn = {-15, 15, 0.1, N_("dB")}}},
+ .data.spin_btn = {-15, 15, 0.1, N_("dB")}},
{WIDGET_SPIN_BTN, N_("Amplify untagged files:"), .child = TRUE,
.cfg_type = VALUE_FLOAT, .cname = "default_gain",
- .data = {.spin_btn = {-15, 15, 0.1, N_("dB")}}}};
+ .data.spin_btn = {-15, 15, 0.1, N_("dB")}}};
static PreferencesWidget proxy_host_port_elements[] = {
{WIDGET_ENTRY, N_("Proxy hostname:"), .cfg_type = VALUE_STRING, .cname = "proxy_host"},
@@ -152,47 +204,44 @@ static PreferencesWidget proxy_host_port_elements[] = {
static PreferencesWidget proxy_auth_elements[] = {
{WIDGET_ENTRY, N_("Proxy username:"), .cfg_type = VALUE_STRING, .cname = "proxy_user"},
{WIDGET_ENTRY, N_("Proxy password:"), .cfg_type = VALUE_STRING, .cname = "proxy_pass",
- .data = {.entry = {.password = TRUE}}}};
+ .data.entry.password = TRUE}};
static PreferencesWidget connectivity_page_widgets[] = {
- {WIDGET_LABEL, N_("<b>Proxy Configuration</b>"), NULL, NULL, NULL, FALSE},
- {WIDGET_CHK_BTN, N_("Enable proxy usage"), .cfg_type = VALUE_BOOLEAN, .cname = "use_proxy"},
- {WIDGET_TABLE, .child = TRUE, .data = {.table = {proxy_host_port_elements,
- G_N_ELEMENTS (proxy_host_port_elements)}}},
- {WIDGET_CHK_BTN, N_("Use authentication with proxy"),
- .cfg_type = VALUE_BOOLEAN, .cname = "use_proxy_auth"},
- {WIDGET_TABLE, .child = TRUE, .data = {.table = {proxy_auth_elements,
- G_N_ELEMENTS (proxy_auth_elements)}}}
-};
+ {WIDGET_LABEL, N_("<b>Proxy Configuration</b>"), NULL, NULL, NULL, FALSE},
+ {WIDGET_CHK_BTN, N_("Enable proxy usage"), .cfg_type = VALUE_BOOLEAN, .cname = "use_proxy"},
+ {WIDGET_TABLE, .child = TRUE, .data.table = {proxy_host_port_elements,
+ ARRAY_LEN (proxy_host_port_elements)}},
+ {WIDGET_CHK_BTN, N_("Use authentication with proxy"),
+ .cfg_type = VALUE_BOOLEAN, .cname = "use_proxy_auth"},
+ {WIDGET_TABLE, .child = TRUE, .data.table = {proxy_auth_elements,
+ ARRAY_LEN (proxy_auth_elements)}}};
static PreferencesWidget chardet_elements[] = {
#ifdef USE_CHARDET
{WIDGET_COMBO_BOX, N_("Auto character encoding detector for:"),
.cfg_type = VALUE_STRING, .cname = "chardet_detector", .child = TRUE,
- .data = {.combo = {chardet_detector_presets, G_N_ELEMENTS (chardet_detector_presets)}}},
+ .data.combo = {chardet_detector_presets, ARRAY_LEN (chardet_detector_presets)}},
#endif
{WIDGET_ENTRY, N_("Fallback character encodings:"), .cfg_type = VALUE_STRING,
.cname = "chardet_fallback", .child = TRUE}};
static PreferencesWidget playlist_page_widgets[] = {
- {WIDGET_LABEL, N_("<b>Behavior</b>"), NULL, NULL, NULL, FALSE},
- {WIDGET_CHK_BTN, N_("Continue playback on startup"),
- .cfg_type = VALUE_BOOLEAN, .cname = "resume_playback_on_startup"},
- {WIDGET_CHK_BTN, N_("Advance when the current song is deleted"),
- .cfg_type = VALUE_BOOLEAN, .cname = "advance_on_delete"},
- {WIDGET_CHK_BTN, N_("Clear the playlist when opening files"),
- .cfg_type = VALUE_BOOLEAN, .cname = "clear_playlist"},
- {WIDGET_CHK_BTN, N_("Open files in a temporary playlist"),
- .cfg_type = VALUE_BOOLEAN, .cname = "open_to_temporary"},
- {WIDGET_CHK_BTN, N_("Do not load metadata for songs until played"),
- .cfg_type = VALUE_BOOLEAN, .cname = "metadata_on_play",
- .callback = playlist_trigger_scan},
- {WIDGET_LABEL, N_("<b>Compatibility</b>"), NULL, NULL, NULL, FALSE},
- {WIDGET_CHK_BTN, N_("Interpret \\ (backward slash) as a folder delimiter"),
- .cfg_type = VALUE_BOOLEAN, .cname = "convert_backslash"},
- {WIDGET_TABLE, .data = {.table = {chardet_elements,
- G_N_ELEMENTS (chardet_elements)}}}
-};
+ {WIDGET_LABEL, N_("<b>Behavior</b>"), NULL, NULL, NULL, FALSE},
+ {WIDGET_CHK_BTN, N_("Continue playback on startup"),
+ .cfg_type = VALUE_BOOLEAN, .cname = "resume_playback_on_startup"},
+ {WIDGET_CHK_BTN, N_("Advance when the current song is deleted"),
+ .cfg_type = VALUE_BOOLEAN, .cname = "advance_on_delete"},
+ {WIDGET_CHK_BTN, N_("Clear the playlist when opening files"),
+ .cfg_type = VALUE_BOOLEAN, .cname = "clear_playlist"},
+ {WIDGET_CHK_BTN, N_("Open files in a temporary playlist"),
+ .cfg_type = VALUE_BOOLEAN, .cname = "open_to_temporary"},
+ {WIDGET_CHK_BTN, N_("Do not load metadata for songs until played"),
+ .cfg_type = VALUE_BOOLEAN, .cname = "metadata_on_play",
+ .callback = playlist_trigger_scan},
+ {WIDGET_LABEL, N_("<b>Compatibility</b>"), NULL, NULL, NULL, FALSE},
+ {WIDGET_CHK_BTN, N_("Interpret \\ (backward slash) as a folder delimiter"),
+ .cfg_type = VALUE_BOOLEAN, .cname = "convert_backslash"},
+ {WIDGET_TABLE, .data.table = {chardet_elements, ARRAY_LEN (chardet_elements)}}};
static PreferencesWidget song_info_page_widgets[] = {
{WIDGET_LABEL, N_("<b>Album Art</b>")},
@@ -206,72 +255,89 @@ static PreferencesWidget song_info_page_widgets[] = {
.cfg_type = VALUE_BOOLEAN, .cname = "recurse_for_cover"},
{WIDGET_SPIN_BTN, N_("Search depth:"), .child = TRUE,
.cfg_type = VALUE_INT, .cname = "recurse_for_cover_depth",
- .data = {.spin_btn = {0, 100, 1}}},
+ .data.spin_btn = {0, 100, 1}},
{WIDGET_LABEL, N_("<b>Popup Information</b>")},
{WIDGET_CHK_BTN, N_("Show popup information"),
.cfg_type = VALUE_BOOLEAN, .cname = "show_filepopup_for_tuple"},
{WIDGET_SPIN_BTN, N_("Popup delay (tenths of a second):"), .child = TRUE,
.cfg_type = VALUE_INT, .cname = "filepopup_delay",
- .data = {.spin_btn = {0, 100, 1}}},
+ .data.spin_btn = {0, 100, 1}},
{WIDGET_CHK_BTN, N_("Show time scale for current song"), .child = TRUE,
.cfg_type = VALUE_BOOLEAN, .cname = "filepopup_showprogressbar"}};
#define TITLESTRING_NPRESETS 6
static const char * const titlestring_presets[TITLESTRING_NPRESETS] = {
- "${title}",
- "${?artist:${artist} - }${title}",
- "${?artist:${artist} - }${?album:${album} - }${title}",
- "${?artist:${artist} - }${?album:${album} - }${?track-number:${track-number}. }${title}",
- "${?artist:${artist} }${?album:[ ${album} ] }${?artist:- }${?track-number:${track-number}. }${title}",
- "${?album:${album} - }${title}"};
+ "${title}",
+ "${?artist:${artist} - }${title}",
+ "${?artist:${artist} - }${?album:${album} - }${title}",
+ "${?artist:${artist} - }${?album:${album} - }${?track-number:${track-number}. }${title}",
+ "${?artist:${artist} }${?album:[ ${album} ] }${?artist:- }${?track-number:${track-number}. }${title}",
+ "${?album:${album} - }${title}"
+};
static const char * const titlestring_preset_names[TITLESTRING_NPRESETS] = {
- N_("TITLE"),
- N_("ARTIST - TITLE"),
- N_("ARTIST - ALBUM - TITLE"),
- N_("ARTIST - ALBUM - TRACK. TITLE"),
- N_("ARTIST [ ALBUM ] - TRACK. TITLE"),
- N_("ALBUM - TITLE")};
-
-static void
-change_category(GtkNotebook * notebook,
- GtkTreeSelection * selection)
+ N_("TITLE"),
+ N_("ARTIST - TITLE"),
+ N_("ARTIST - ALBUM - TITLE"),
+ N_("ARTIST - ALBUM - TRACK. TITLE"),
+ N_("ARTIST [ ALBUM ] - TRACK. TITLE"),
+ N_("ALBUM - TITLE")
+};
+
+static GArray * fill_plugin_combo (int type)
{
- GtkTreeModel *model;
- GtkTreeIter iter;
- int index;
+ GArray * array = g_array_new (FALSE, FALSE, sizeof (ComboBoxElements));
+ g_array_set_size (array, plugin_count (type));
- if (!gtk_tree_selection_get_selected(selection, &model, &iter))
- return;
+ for (int i = 0; i < array->len; i ++)
+ {
+ ComboBoxElements * elem = & g_array_index (array, ComboBoxElements, i);
+ elem->label = plugin_get_name (plugin_by_index (type, i));
+ elem->value = GINT_TO_POINTER (i);
+ }
- gtk_tree_model_get(model, &iter, CATEGORY_VIEW_COL_ID, &index, -1);
- gtk_notebook_set_current_page(notebook, index);
+ return array;
}
-static void
-titlestring_tag_menu_callback(GtkMenuItem * menuitem,
- void * data)
+static void change_category (int category)
{
- const char *separator = " - ";
- int item = GPOINTER_TO_INT(data);
- int pos;
+ GtkTreeSelection * selection = gtk_tree_view_get_selection ((GtkTreeView *) category_treeview);
+ GtkTreePath * path = gtk_tree_path_new_from_indices (category, -1);
+ gtk_tree_selection_select_path (selection, path);
+ gtk_tree_path_free (path);
+}
- pos = gtk_editable_get_position(GTK_EDITABLE(titlestring_entry));
+static void category_changed (GtkTreeSelection * selection)
+{
+ GtkTreeModel * model;
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected (selection, & model, & iter))
+ {
+ GtkTreePath * path = gtk_tree_model_get_path (model, & iter);
+ int category = gtk_tree_path_get_indices (path)[0];
+ gtk_notebook_set_current_page ((GtkNotebook *) category_notebook, category);
+ gtk_tree_path_free (path);
+ }
+}
+
+static void titlestring_tag_menu_cb (GtkMenuItem * menuitem, void * data)
+{
+ const char * separator = " - ";
+ int item = GPOINTER_TO_INT (data);
+ int pos = gtk_editable_get_position ((GtkEditable *) titlestring_entry);
/* insert separator as needed */
- if (gtk_entry_get_text(GTK_ENTRY(titlestring_entry))[0])
- gtk_editable_insert_text(GTK_EDITABLE(titlestring_entry), separator, -1, &pos);
+ if (gtk_entry_get_text ((GtkEntry *) titlestring_entry)[0])
+ gtk_editable_insert_text ((GtkEditable *) titlestring_entry, separator, -1, & pos);
- gtk_editable_insert_text(GTK_EDITABLE(titlestring_entry), _(title_field_tags[item].tag), -1, &pos);
- gtk_editable_set_position(GTK_EDITABLE(titlestring_entry), pos);
+ gtk_editable_insert_text ((GtkEditable *) titlestring_entry, _(title_field_tags[item].tag), -1, & pos);
+ gtk_editable_set_position ((GtkEditable *) titlestring_entry, pos);
}
-static void
-on_titlestring_help_button_clicked(GtkButton * button,
- void * data)
+static void on_titlestring_help_button_clicked (GtkButton * button, void * menu)
{
- GtkMenu * menu = data;
gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0, GDK_CURRENT_TIME);
}
@@ -291,7 +357,7 @@ static void update_titlestring_cbox (GtkComboBox * cbox, const char * format)
static void on_titlestring_entry_changed (GtkEntry * entry, GtkComboBox * cbox)
{
const char * format = gtk_entry_get_text (entry);
- set_string (NULL, "generic_title_format", format);
+ set_str (NULL, "generic_title_format", format);
update_titlestring_cbox (cbox, format);
playlist_reformat_titles ();
}
@@ -303,638 +369,68 @@ static void on_titlestring_cbox_changed (GtkComboBox * cbox, GtkEntry * entry)
gtk_entry_set_text (entry, titlestring_presets[preset]);
}
-static void widget_set_bool (const PreferencesWidget * widget, bool_t value)
-{
- g_return_if_fail (widget->cfg_type == VALUE_BOOLEAN);
-
- if (widget->cfg)
- * (bool_t *) widget->cfg = value;
- else if (widget->cname)
- set_bool (widget->csect, widget->cname, value);
-
- if (widget->callback)
- widget->callback ();
-}
-
-static bool_t widget_get_bool (const PreferencesWidget * widget)
-{
- g_return_val_if_fail (widget->cfg_type == VALUE_BOOLEAN, FALSE);
-
- if (widget->cfg)
- return * (bool_t *) widget->cfg;
- else if (widget->cname)
- return get_bool (widget->csect, widget->cname);
- else
- return FALSE;
-}
-
-static void widget_set_int (const PreferencesWidget * widget, int value)
-{
- g_return_if_fail (widget->cfg_type == VALUE_INT);
-
- if (widget->cfg)
- * (int *) widget->cfg = value;
- else if (widget->cname)
- set_int (widget->csect, widget->cname, value);
-
- if (widget->callback)
- widget->callback ();
-}
-
-static int widget_get_int (const PreferencesWidget * widget)
-{
- g_return_val_if_fail (widget->cfg_type == VALUE_INT, 0);
-
- if (widget->cfg)
- return * (int *) widget->cfg;
- else if (widget->cname)
- return get_int (widget->csect, widget->cname);
- else
- return 0;
-}
-
-static void widget_set_double (const PreferencesWidget * widget, double value)
-{
- g_return_if_fail (widget->cfg_type == VALUE_FLOAT);
-
- if (widget->cfg)
- * (float *) widget->cfg = value;
- else if (widget->cname)
- set_double (widget->csect, widget->cname, value);
-
- if (widget->callback)
- widget->callback ();
-}
-
-static double widget_get_double (const PreferencesWidget * widget)
-{
- g_return_val_if_fail (widget->cfg_type == VALUE_FLOAT, 0);
-
- if (widget->cfg)
- return * (float *) widget->cfg;
- else if (widget->cname)
- return get_double (widget->csect, widget->cname);
- else
- return 0;
-}
-
-static void widget_set_string (const PreferencesWidget * widget, const char * value)
-{
- g_return_if_fail (widget->cfg_type == VALUE_STRING);
-
- if (widget->cfg)
- {
- g_free (* (char * *) widget->cfg);
- * (char * *) widget->cfg = g_strdup (value);
- }
- else if (widget->cname)
- set_string (widget->csect, widget->cname, value);
-
- if (widget->callback)
- widget->callback ();
-}
-
-static char * widget_get_string (const PreferencesWidget * widget)
-{
- g_return_val_if_fail (widget->cfg_type == VALUE_STRING, NULL);
-
- if (widget->cfg)
- return g_strdup (* (char * *) widget->cfg);
- else if (widget->cname)
- return get_string (widget->csect, widget->cname);
- else
- return NULL;
-}
-
-static void on_font_btn_font_set (GtkFontButton * button, const PreferencesWidget * widget)
-{
- widget_set_string (widget, gtk_font_button_get_font_name (button));
-}
-
-static void on_spin_btn_changed_int (GtkSpinButton * button, const PreferencesWidget * widget)
-{
- widget_set_int (widget, gtk_spin_button_get_value_as_int (button));
-}
-
-static void on_spin_btn_changed_float (GtkSpinButton * button, const PreferencesWidget * widget)
-{
- widget_set_double (widget, gtk_spin_button_get_value (button));
-}
-
static void fill_category_list (GtkTreeView * treeview, GtkNotebook * notebook)
{
- GtkListStore *store;
- GtkCellRenderer *renderer;
- GtkTreeViewColumn *column;
- GtkTreeSelection *selection;
- GtkTreeIter iter;
- GdkPixbuf *img;
- int i;
+ GtkTreeViewColumn * column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_title (column, _("Category"));
+ gtk_tree_view_append_column (treeview, column);
+ gtk_tree_view_column_set_spacing (column, 2);
- column = gtk_tree_view_column_new();
- gtk_tree_view_column_set_title(column, _("Category"));
- gtk_tree_view_append_column(treeview, column);
- gtk_tree_view_column_set_spacing(column, 2);
+ GtkCellRenderer * renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes (column, renderer, "pixbuf", 0, NULL);
- renderer = gtk_cell_renderer_pixbuf_new();
- gtk_tree_view_column_pack_start(column, renderer, FALSE);
- gtk_tree_view_column_set_attributes(column, renderer, "pixbuf", 0, NULL);
-
- renderer = gtk_cell_renderer_text_new();
- gtk_tree_view_column_pack_start(column, renderer, FALSE);
- gtk_tree_view_column_set_attributes(column, renderer, "text", 1, NULL);
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes (column, renderer, "text", 1, NULL);
g_object_set ((GObject *) renderer, "wrap-width", 96, "wrap-mode",
PANGO_WRAP_WORD_CHAR, NULL);
- store = gtk_list_store_new(CATEGORY_VIEW_N_COLS,
- GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT);
- gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(store));
+ GtkListStore * store = gtk_list_store_new (CATEGORY_VIEW_N_COLS,
+ GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT);
+ gtk_tree_view_set_model (treeview, (GtkTreeModel *) store);
- for (i = 0; i < n_categories; i ++)
- {
- char * path = g_strdup_printf ("%s/images/%s",
- get_path (AUD_PATH_DATA_DIR), categories[i].icon_path);
- img = gdk_pixbuf_new_from_file (path, NULL);
- g_free (path);
-
- gtk_list_store_append(store, &iter);
- gtk_list_store_set(store, &iter,
- CATEGORY_VIEW_COL_ICON, img,
- CATEGORY_VIEW_COL_NAME,
- gettext(categories[i].name), CATEGORY_VIEW_COL_ID,
- i, -1);
- g_object_unref(img);
- }
-
- selection = gtk_tree_view_get_selection(treeview);
-
- g_signal_connect_swapped(selection, "changed",
- G_CALLBACK(change_category), notebook);
-}
+ const char * data_dir = get_path (AUD_PATH_DATA_DIR);
-static void on_radio_button_toggled (GtkWidget * button, const PreferencesWidget * widget)
-{
- if (gtk_toggle_button_get_active ((GtkToggleButton *) button))
- widget_set_int (widget, widget->data.radio_btn.value);
-}
-
-static void init_radio_button (GtkWidget * button, const PreferencesWidget * widget)
-{
- if (widget->cfg_type != VALUE_INT)
- return;
-
- if (widget_get_int (widget) == widget->data.radio_btn.value)
- gtk_toggle_button_set_active ((GtkToggleButton *) button, TRUE);
-
- g_signal_connect (button, "toggled", (GCallback) on_radio_button_toggled, (void *) widget);
-}
-
-static void on_toggle_button_toggled (GtkToggleButton * button, const PreferencesWidget * widget)
-{
- bool_t active = gtk_toggle_button_get_active (button);
- widget_set_bool (widget, active);
-
- GtkWidget * child = g_object_get_data ((GObject *) button, "child");
- if (child)
- gtk_widget_set_sensitive (child, active);
-}
-
-static void init_toggle_button (GtkWidget * button, const PreferencesWidget * widget)
-{
- if (widget->cfg_type != VALUE_BOOLEAN)
- return;
-
- gtk_toggle_button_set_active ((GtkToggleButton *) button, widget_get_bool (widget));
- g_signal_connect (button, "toggled", (GCallback) on_toggle_button_toggled, (void *) widget);
-}
-
-static void on_entry_changed (GtkEntry * entry, const PreferencesWidget * widget)
-{
- widget_set_string (widget, gtk_entry_get_text (entry));
-}
-
-static void on_cbox_changed_int (GtkComboBox * combobox, const PreferencesWidget * widget)
-{
- int position = gtk_combo_box_get_active (combobox);
- widget_set_int (widget, GPOINTER_TO_INT (widget->data.combo.elements[position].value));
-}
-
-static void on_cbox_changed_string (GtkComboBox * combobox, const PreferencesWidget * widget)
-{
- int position = gtk_combo_box_get_active (combobox);
- widget_set_string (widget, widget->data.combo.elements[position].value);
-}
-
-static void fill_cbox (GtkWidget * combobox, const PreferencesWidget * widget, const char * domain)
-{
- for (int i = 0; i < widget->data.combo.n_elements; i ++)
- gtk_combo_box_text_append_text ((GtkComboBoxText *) combobox,
- dgettext (domain, widget->data.combo.elements[i].label));
-
- switch (widget->cfg_type)
+ for (int i = 0; i < ARRAY_LEN (categories); i ++)
{
- case VALUE_INT:;
- int ivalue = widget_get_int (widget);
+ SCONCAT3 (path, data_dir, "/images/", categories[i].icon_path);
- for (int i = 0; i < widget->data.combo.n_elements; i++)
- {
- if (GPOINTER_TO_INT (widget->data.combo.elements[i].value) == ivalue)
- {
- gtk_combo_box_set_active ((GtkComboBox *) combobox, i);
- break;
- }
- }
+ GtkTreeIter iter;
- g_signal_connect (combobox, "changed", (GCallback) on_cbox_changed_int, (void *) widget);
- break;
+ gtk_list_store_append (store, & iter);
+ gtk_list_store_set (store, & iter, CATEGORY_VIEW_COL_NAME,
+ gettext (categories[i].name), -1);
- case VALUE_STRING:;
- char * value = widget_get_string (widget);
+ GdkPixbuf * img = gdk_pixbuf_new_from_file (path, NULL);
- for(int i = 0; i < widget->data.combo.n_elements; i++)
+ if (img)
{
- if (value && ! strcmp (widget->data.combo.elements[i].value, value))
- {
- gtk_combo_box_set_active ((GtkComboBox *) combobox, i);
- break;
- }
+ gtk_list_store_set (store, & iter, CATEGORY_VIEW_COL_ICON, img, -1);
+ g_object_unref (img);
}
-
- g_free (value);
-
- g_signal_connect (combobox, "changed", (GCallback) on_cbox_changed_string, (void *) widget);
- break;
-
- default:
- break;
}
-}
-static void create_spin_button (const PreferencesWidget * widget, GtkWidget * *
- label_pre, GtkWidget * * spin_btn, GtkWidget * * label_past, const char *
- domain)
-{
- g_return_if_fail(widget->type == WIDGET_SPIN_BTN);
+ g_object_unref (store);
- * label_pre = gtk_label_new (dgettext (domain, widget->label));
-
- *spin_btn = gtk_spin_button_new_with_range(widget->data.spin_btn.min,
- widget->data.spin_btn.max,
- widget->data.spin_btn.step);
-
-
- if (widget->tooltip)
- gtk_widget_set_tooltip_text (* spin_btn, dgettext (domain,
- widget->tooltip));
-
- if (widget->data.spin_btn.right_label) {
- * label_past = gtk_label_new (dgettext (domain,
- widget->data.spin_btn.right_label));
- }
-
- switch (widget->cfg_type)
- {
- case VALUE_INT:
- gtk_spin_button_set_value ((GtkSpinButton *) * spin_btn, widget_get_int (widget));
- g_signal_connect (* spin_btn, "value_changed", (GCallback)
- on_spin_btn_changed_int, (void *) widget);
- break;
- case VALUE_FLOAT:
- gtk_spin_button_set_value ((GtkSpinButton *) * spin_btn, widget_get_double (widget));
- g_signal_connect (* spin_btn, "value_changed", (GCallback)
- on_spin_btn_changed_float, (void *) widget);
- break;
- default:
- break;
- }
+ GtkTreeSelection * selection = gtk_tree_view_get_selection (treeview);
+ g_signal_connect (selection, "changed", (GCallback) category_changed, NULL);
}
-void create_font_btn (const PreferencesWidget * widget, GtkWidget * * label,
- GtkWidget * * font_btn, const char * domain)
+static GtkWidget * create_titlestring_tag_menu (void)
{
- *font_btn = gtk_font_button_new();
- gtk_font_button_set_use_font(GTK_FONT_BUTTON(*font_btn), TRUE);
- gtk_font_button_set_use_size(GTK_FONT_BUTTON(*font_btn), TRUE);
- gtk_widget_set_hexpand(*font_btn, TRUE);
- if (widget->label) {
- * label = gtk_label_new_with_mnemonic (dgettext (domain, widget->label));
- gtk_label_set_use_markup(GTK_LABEL(*label), TRUE);
- gtk_misc_set_alignment(GTK_MISC(*label), 1, 0.5);
- gtk_label_set_justify(GTK_LABEL(*label), GTK_JUSTIFY_RIGHT);
- gtk_label_set_mnemonic_widget(GTK_LABEL(*label), *font_btn);
- }
-
- if (widget->data.font_btn.title)
- gtk_font_button_set_title (GTK_FONT_BUTTON (* font_btn),
- dgettext (domain, widget->data.font_btn.title));
-
- char * name = widget_get_string (widget);
- if (name)
- {
- gtk_font_button_set_font_name ((GtkFontButton *) * font_btn, name);
- g_free (name);
- }
-
- g_signal_connect (* font_btn, "font_set", (GCallback) on_font_btn_font_set, (void *) widget);
-}
-
-static void create_entry (const PreferencesWidget * widget, GtkWidget * * label,
- GtkWidget * * entry, const char * domain)
-{
- *entry = gtk_entry_new();
- gtk_entry_set_visibility(GTK_ENTRY(*entry), !widget->data.entry.password);
- gtk_widget_set_hexpand(*entry, TRUE);
-
- if (widget->label)
- * label = gtk_label_new (dgettext (domain, widget->label));
+ GtkWidget * titlestring_tag_menu = gtk_menu_new ();
- if (widget->tooltip)
- gtk_widget_set_tooltip_text (* entry, dgettext (domain, widget->tooltip));
-
- if (widget->cfg_type == VALUE_STRING)
+ for (int i = 0; i < ARRAY_LEN (title_field_tags); i ++)
{
- char * value = widget_get_string (widget);
- if (value)
- {
- gtk_entry_set_text ((GtkEntry *) * entry, value);
- g_free (value);
- }
-
- g_signal_connect (* entry, "changed", (GCallback) on_entry_changed, (void *) widget);
- }
-}
-
-static void create_label (const PreferencesWidget * widget, GtkWidget * * label,
- GtkWidget * * icon, const char * domain)
-{
- if (widget->data.label.stock_id)
- *icon = gtk_image_new_from_stock(widget->data.label.stock_id, GTK_ICON_SIZE_BUTTON);
-
- * label = gtk_label_new_with_mnemonic (dgettext (domain, widget->label));
- gtk_label_set_use_markup(GTK_LABEL(*label), TRUE);
-
- if (widget->data.label.single_line == FALSE)
- gtk_label_set_line_wrap(GTK_LABEL(*label), TRUE);
-
- gtk_misc_set_alignment(GTK_MISC(*label), 0, 0.5);
-}
-
-static void create_cbox (const PreferencesWidget * widget, GtkWidget * * label,
- GtkWidget * * combobox, const char * domain)
-{
- * combobox = gtk_combo_box_text_new ();
-
- if (widget->label) {
- * label = gtk_label_new (dgettext (domain, widget->label));
- }
-
- fill_cbox (* combobox, widget, domain);
-}
-
-static void fill_grid (GtkWidget * grid, const PreferencesWidget * elements, int
- amt, const char * domain)
-{
- int x;
- GtkWidget *widget_left, *widget_middle, *widget_right;
-
- for (x = 0; x < amt; ++x) {
- widget_left = widget_middle = widget_right = NULL;
- switch (elements[x].type) {
- case WIDGET_SPIN_BTN:
- create_spin_button (& elements[x], & widget_left,
- & widget_middle, & widget_right, domain);
- break;
- case WIDGET_LABEL:
- create_label (& elements[x], & widget_middle, & widget_left,
- domain);
- break;
- case WIDGET_FONT_BTN:
- create_font_btn (& elements[x], & widget_left, & widget_middle,
- domain);
- break;
- case WIDGET_ENTRY:
- create_entry (& elements[x], & widget_left, & widget_middle,
- domain);
- break;
- case WIDGET_COMBO_BOX:
- create_cbox (& elements[x], & widget_left, & widget_middle,
- domain);
- break;
- default:
- g_warning("Unsupported widget type %d in table", elements[x].type);
- }
-
- if (widget_left)
- gtk_grid_attach(GTK_GRID(grid), widget_left, 0, x, 1, 1);
-
- if (widget_middle)
- gtk_grid_attach(GTK_GRID(grid), widget_middle, 1, x, 1, 1);
-
- if (widget_right)
- gtk_grid_attach(GTK_GRID(grid), widget_right, 2, x, 1, 1);
- }
-}
-
-/* box: a GtkBox */
-void create_widgets_with_domain (void * box, const PreferencesWidget * widgets,
- int amt, const char * domain)
-{
- GtkWidget *alignment = NULL, *widget = NULL;
- GtkWidget *child_box = NULL;
- GSList *radio_btn_group = NULL;
-
- for (int x = 0; x < amt; x ++)
- {
- GtkWidget * label = NULL;
-
- if (widget && widgets[x].child)
- {
- if (!child_box) {
- child_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
- g_object_set_data(G_OBJECT(widget), "child", child_box);
- alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
- gtk_box_pack_start(box, alignment, FALSE, FALSE, 0);
- gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 12, 0);
- gtk_container_add (GTK_CONTAINER (alignment), child_box);
-
- if (GTK_IS_TOGGLE_BUTTON (widget))
- gtk_widget_set_sensitive (child_box, gtk_toggle_button_get_active ((GtkToggleButton *) widget));
- }
- } else
- child_box = NULL;
-
- alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
- gtk_alignment_set_padding ((GtkAlignment *) alignment, 6, 0, 12, 0);
- gtk_box_pack_start(child_box ? GTK_BOX(child_box) : box, alignment, FALSE, FALSE, 0);
-
- if (radio_btn_group && widgets[x].type != WIDGET_RADIO_BTN)
- radio_btn_group = NULL;
-
- switch(widgets[x].type) {
- case WIDGET_CHK_BTN:
- widget = gtk_check_button_new_with_mnemonic (dgettext (domain, widgets[x].label));
- init_toggle_button (widget, & widgets[x]);
- break;
- case WIDGET_LABEL:
- if (strstr (widgets[x].label, "<b>"))
- gtk_alignment_set_padding ((GtkAlignment *) alignment,
- (x == 0) ? 0 : 12, 0, 0, 0);
-
- GtkWidget * icon = NULL;
- create_label (& widgets[x], & label, & icon, domain);
-
- if (icon == NULL)
- widget = label;
- else {
- widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
- gtk_box_pack_start(GTK_BOX(widget), icon, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
- }
- break;
- case WIDGET_RADIO_BTN:
- widget = gtk_radio_button_new_with_mnemonic (radio_btn_group,
- dgettext (domain, widgets[x].label));
- radio_btn_group = gtk_radio_button_get_group ((GtkRadioButton *) widget);
- init_radio_button (widget, & widgets[x]);
- break;
- case WIDGET_SPIN_BTN:
- widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
-
- GtkWidget *label_pre = NULL, *spin_btn = NULL, *label_past = NULL;
- create_spin_button (& widgets[x], & label_pre, & spin_btn,
- & label_past, domain);
-
- if (label_pre)
- gtk_box_pack_start(GTK_BOX(widget), label_pre, FALSE, FALSE, 0);
- if (spin_btn)
- gtk_box_pack_start(GTK_BOX(widget), spin_btn, FALSE, FALSE, 0);
- if (label_past)
- gtk_box_pack_start(GTK_BOX(widget), label_past, FALSE, FALSE, 0);
-
- break;
- case WIDGET_CUSTOM: /* custom widget. --nenolod */
- if (widgets[x].data.populate)
- widget = widgets[x].data.populate();
- else
- widget = NULL;
-
- break;
- case WIDGET_FONT_BTN:
- widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
-
- GtkWidget *font_btn = NULL;
- create_font_btn (& widgets[x], & label, & font_btn, domain);
-
- if (label)
- gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
- if (font_btn)
- gtk_box_pack_start(GTK_BOX(widget), font_btn, FALSE, FALSE, 0);
- break;
- case WIDGET_TABLE:
- widget = gtk_grid_new();
- fill_grid(widget, widgets[x].data.table.elem,
- widgets[x].data.table.rows, domain);
- gtk_grid_set_column_spacing(GTK_GRID(widget), 6);
- gtk_grid_set_row_spacing(GTK_GRID(widget), 6);
- break;
- case WIDGET_ENTRY:
- widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
-
- GtkWidget *entry = NULL;
- create_entry (& widgets[x], & label, & entry, domain);
-
- if (label)
- gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
- if (entry)
- gtk_box_pack_start(GTK_BOX(widget), entry, TRUE, TRUE, 0);
- break;
- case WIDGET_COMBO_BOX:
- widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
-
- GtkWidget *combo = NULL;
- create_cbox (& widgets[x], & label, & combo, domain);
-
- if (label)
- gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
- if (combo)
- gtk_box_pack_start(GTK_BOX(widget), combo, FALSE, FALSE, 0);
- break;
- case WIDGET_BOX:
- gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 0, 0);
-
- if (widgets[x].data.box.horizontal) {
- widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
- } else {
- widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
- }
-
- create_widgets_with_domain ((GtkBox *) widget,
- widgets[x].data.box.elem, widgets[x].data.box.n_elem, domain);
-
- if (widgets[x].data.box.frame) {
- GtkWidget *tmp;
- tmp = widget;
-
- widget = gtk_frame_new (dgettext (domain, widgets[x].label));
- gtk_container_add(GTK_CONTAINER(widget), tmp);
- }
- break;
- case WIDGET_NOTEBOOK:
- gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
-
- widget = gtk_notebook_new();
-
- int i;
- for (i = 0; i<widgets[x].data.notebook.n_tabs; i++) {
- GtkWidget *vbox;
- vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
- create_widgets_with_domain ((GtkBox *) vbox,
- widgets[x].data.notebook.tabs[i].widgets,
- widgets[x].data.notebook.tabs[i].n_widgets, domain);
-
- gtk_notebook_append_page (GTK_NOTEBOOK (widget), vbox,
- gtk_label_new (dgettext (domain,
- widgets[x].data.notebook.tabs[i].name)));
- }
- break;
- case WIDGET_SEPARATOR:
- gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 6, 0, 0);
-
- if (widgets[x].data.separator.horizontal == TRUE) {
- widget = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
- } else {
- widget = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
- }
- break;
- default:
- break;
- }
-
- if (widget && !gtk_widget_get_parent(widget))
- gtk_container_add(GTK_CONTAINER(alignment), widget);
- if (widget && widgets[x].tooltip && widgets[x].type != WIDGET_SPIN_BTN)
- gtk_widget_set_tooltip_text (widget, dgettext (domain,
- widgets[x].tooltip));
- }
-
-}
-
-static GtkWidget *
-create_titlestring_tag_menu(void)
-{
- GtkWidget *titlestring_tag_menu, *menu_item;
- unsigned int i;
-
- titlestring_tag_menu = gtk_menu_new();
- for(i = 0; i < n_title_field_tags; i++) {
- menu_item = gtk_menu_item_new_with_label(_(title_field_tags[i].name));
- gtk_menu_shell_append(GTK_MENU_SHELL(titlestring_tag_menu), menu_item);
+ GtkWidget * menu_item = gtk_menu_item_new_with_label (_(title_field_tags[i].name));
+ gtk_menu_shell_append ((GtkMenuShell *) titlestring_tag_menu, menu_item);
g_signal_connect(menu_item, "activate",
- G_CALLBACK(titlestring_tag_menu_callback),
- GINT_TO_POINTER(i));
+ (GCallback) titlestring_tag_menu_cb, GINT_TO_POINTER (i));
};
- gtk_widget_show_all(titlestring_tag_menu);
+
+ gtk_widget_show_all (titlestring_tag_menu);
return titlestring_tag_menu;
}
@@ -962,106 +458,90 @@ static void create_titlestring_widgets (GtkWidget * * cbox, GtkWidget * * entry)
* entry = gtk_entry_new ();
- char * format = get_string (NULL, "generic_title_format");
+ char * format = get_str (NULL, "generic_title_format");
update_titlestring_cbox ((GtkComboBox *) * cbox, format);
gtk_entry_set_text ((GtkEntry *) * entry, format);
- g_free (format);
+ str_unref (format);
g_signal_connect (* cbox, "changed", (GCallback) on_titlestring_cbox_changed, * entry);
g_signal_connect (* entry, "changed", (GCallback) on_titlestring_entry_changed, * cbox);
}
-static void
-create_playlist_category(void)
+static void create_playlist_category (void)
{
- GtkWidget *vbox5;
- GtkWidget *alignment55;
- GtkWidget *label60;
- GtkWidget *alignment56;
- GtkWidget *grid6;
- GtkWidget *titlestring_help_button;
- GtkWidget *image1;
- GtkWidget *label62;
- GtkWidget *label61;
- GtkWidget *titlestring_tag_menu = create_titlestring_tag_menu();
- GtkWidget * numbers_alignment, * numbers;
-
- vbox5 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
- gtk_container_add ((GtkContainer *) category_notebook, vbox5);
-
- create_widgets(GTK_BOX(vbox5), playlist_page_widgets, G_N_ELEMENTS(playlist_page_widgets));
-
- alignment55 = gtk_alignment_new (0.5, 0.5, 1, 1);
- gtk_box_pack_start (GTK_BOX (vbox5), alignment55, FALSE, FALSE, 0);
- gtk_alignment_set_padding ((GtkAlignment *) alignment55, 12, 3, 0, 0);
-
- label60 = gtk_label_new (_("<b>Song Display</b>"));
- gtk_container_add (GTK_CONTAINER (alignment55), label60);
- gtk_label_set_use_markup (GTK_LABEL (label60), TRUE);
- gtk_misc_set_alignment (GTK_MISC (label60), 0, 0.5);
+ GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add ((GtkContainer *) category_notebook, vbox);
- numbers_alignment = gtk_alignment_new (0, 0, 0, 0);
+ create_widgets ((GtkBox *) vbox, playlist_page_widgets, ARRAY_LEN (playlist_page_widgets));
+
+ GtkWidget * alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
+ gtk_box_pack_start ((GtkBox *) vbox, alignment, FALSE, FALSE, 0);
+ gtk_alignment_set_padding ((GtkAlignment *) alignment, 12, 3, 0, 0);
+
+ GtkWidget * label = gtk_label_new (_("<b>Song Display</b>"));
+ gtk_container_add ((GtkContainer *) alignment, label);
+ gtk_label_set_use_markup ((GtkLabel *) label, TRUE);
+ gtk_misc_set_alignment ((GtkMisc *) label, 0, 0.5);
+
+ GtkWidget * numbers_alignment = gtk_alignment_new (0, 0, 0, 0);
gtk_alignment_set_padding ((GtkAlignment *) numbers_alignment, 0, 0, 12, 0);
- gtk_box_pack_start ((GtkBox *) vbox5, numbers_alignment, 0, 0, 3);
+ gtk_box_pack_start ((GtkBox *) vbox, numbers_alignment, 0, 0, 3);
- numbers = gtk_check_button_new_with_label (_("Show song numbers"));
+ GtkWidget * numbers = gtk_check_button_new_with_label (_("Show song numbers"));
gtk_toggle_button_set_active ((GtkToggleButton *) numbers,
get_bool (NULL, "show_numbers_in_pl"));
- g_signal_connect ((GObject *) numbers, "toggled", (GCallback)
- show_numbers_cb, 0);
+ g_signal_connect (numbers, "toggled", (GCallback) show_numbers_cb, 0);
gtk_container_add ((GtkContainer *) numbers_alignment, numbers);
numbers_alignment = gtk_alignment_new (0, 0, 0, 0);
gtk_alignment_set_padding ((GtkAlignment *) numbers_alignment, 0, 0, 12, 0);
- gtk_box_pack_start ((GtkBox *) vbox5, numbers_alignment, 0, 0, 3);
+ gtk_box_pack_start ((GtkBox *) vbox, numbers_alignment, 0, 0, 3);
- numbers = gtk_check_button_new_with_label (_("Show leading zeroes (02:00 "
- "instead of 2:00)"));
+ numbers = gtk_check_button_new_with_label (
+ _("Show leading zeroes (02:00 instead of 2:00)"));
gtk_toggle_button_set_active ((GtkToggleButton *) numbers, get_bool (NULL, "leading_zero"));
- g_signal_connect ((GObject *) numbers, "toggled", (GCallback)
- leading_zero_cb, 0);
+ g_signal_connect (numbers, "toggled", (GCallback) leading_zero_cb, 0);
gtk_container_add ((GtkContainer *) numbers_alignment, numbers);
- alignment56 = gtk_alignment_new (0.5, 0.5, 1, 1);
- gtk_box_pack_start (GTK_BOX (vbox5), alignment56, FALSE, FALSE, 0);
- gtk_alignment_set_padding (GTK_ALIGNMENT (alignment56), 0, 0, 12, 0);
+ alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
+ gtk_box_pack_start ((GtkBox *) vbox, alignment, FALSE, FALSE, 0);
+ gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 0, 12, 0);
- grid6 = gtk_grid_new ();
- gtk_container_add (GTK_CONTAINER (alignment56), grid6);
- gtk_grid_set_row_spacing (GTK_GRID (grid6), 4);
- gtk_grid_set_column_spacing (GTK_GRID (grid6), 12);
+ GtkWidget * grid = gtk_grid_new ();
+ gtk_container_add ((GtkContainer *) alignment, grid);
+ gtk_grid_set_row_spacing ((GtkGrid *) grid, 4);
+ gtk_grid_set_column_spacing ((GtkGrid *) grid, 12);
- titlestring_help_button = gtk_button_new ();
- gtk_grid_attach (GTK_GRID (grid6), titlestring_help_button, 2, 1, 1, 1);
+ label = gtk_label_new (_("Title format:"));
+ gtk_grid_attach ((GtkGrid *) grid, label, 0, 0, 1, 1);
+ gtk_label_set_justify ((GtkLabel *) label, GTK_JUSTIFY_RIGHT);
+ gtk_misc_set_alignment ((GtkMisc *) label, 1, 0.5);
- gtk_widget_set_can_focus (titlestring_help_button, FALSE);
- gtk_widget_set_tooltip_text (titlestring_help_button, _("Show information about titlestring format"));
- gtk_button_set_relief (GTK_BUTTON (titlestring_help_button), GTK_RELIEF_HALF);
- gtk_button_set_focus_on_click (GTK_BUTTON (titlestring_help_button), FALSE);
-
- image1 = gtk_image_new_from_stock ("gtk-index", GTK_ICON_SIZE_BUTTON);
- gtk_container_add (GTK_CONTAINER (titlestring_help_button), image1);
+ label = gtk_label_new (_("Custom string:"));
+ gtk_grid_attach ((GtkGrid *) grid, label, 0, 1, 1, 1);
+ gtk_label_set_justify ((GtkLabel *) label, GTK_JUSTIFY_RIGHT);
+ gtk_misc_set_alignment ((GtkMisc *) label, 1, 0.5);
GtkWidget * titlestring_cbox;
create_titlestring_widgets (& titlestring_cbox, & titlestring_entry);
gtk_widget_set_hexpand (titlestring_cbox, TRUE);
gtk_widget_set_hexpand (titlestring_entry, TRUE);
- gtk_grid_attach (GTK_GRID (grid6), titlestring_cbox, 1, 0, 1, 1);
- gtk_grid_attach (GTK_GRID (grid6), titlestring_entry, 1, 1, 1, 1);
-
- label62 = gtk_label_new (_("Custom string:"));
- gtk_grid_attach (GTK_GRID (grid6), label62, 0, 1, 1, 1);
- gtk_label_set_justify (GTK_LABEL (label62), GTK_JUSTIFY_RIGHT);
- gtk_misc_set_alignment (GTK_MISC (label62), 1, 0.5);
-
- label61 = gtk_label_new (_("Title format:"));
- gtk_grid_attach (GTK_GRID (grid6), label61, 0, 0, 1, 1);
- gtk_label_set_justify (GTK_LABEL (label61), GTK_JUSTIFY_RIGHT);
- gtk_misc_set_alignment (GTK_MISC (label61), 1, 0.5);
-
- g_signal_connect(titlestring_help_button, "clicked",
- G_CALLBACK(on_titlestring_help_button_clicked),
- titlestring_tag_menu);
+ gtk_grid_attach ((GtkGrid *) grid, titlestring_cbox, 1, 0, 1, 1);
+ gtk_grid_attach ((GtkGrid *) grid, titlestring_entry, 1, 1, 1, 1);
+
+ GtkWidget * titlestring_help_button = gtk_button_new ();
+ gtk_widget_set_can_focus (titlestring_help_button, FALSE);
+ gtk_button_set_focus_on_click ((GtkButton *) titlestring_help_button, FALSE);
+ gtk_button_set_relief ((GtkButton *) titlestring_help_button, GTK_RELIEF_HALF);
+ gtk_grid_attach ((GtkGrid *) grid, titlestring_help_button, 2, 1, 1, 1);
+
+ GtkWidget * titlestring_tag_menu = create_titlestring_tag_menu ();
+
+ g_signal_connect (titlestring_help_button, "clicked",
+ (GCallback) on_titlestring_help_button_clicked, titlestring_tag_menu);
+
+ GtkWidget * image = gtk_image_new_from_icon_name ("list-add", GTK_ICON_SIZE_BUTTON);
+ gtk_container_add ((GtkContainer *) titlestring_help_button, image);
}
static void create_song_info_category (void)
@@ -1069,54 +549,74 @@ static void create_song_info_category (void)
GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add ((GtkContainer *) category_notebook, vbox);
create_widgets ((GtkBox *) vbox, song_info_page_widgets,
- G_N_ELEMENTS (song_info_page_widgets));
+ ARRAY_LEN (song_info_page_widgets));
}
-static GtkWidget * output_config_button, * output_about_button;
-
-static bool_t output_enum_cb (PluginHandle * plugin, GList * * list)
+static void iface_fill_prefs_box (void)
{
- * list = g_list_prepend (* list, plugin);
- return TRUE;
+ Plugin * header = plugin_get_header (plugin_get_current (PLUGIN_TYPE_IFACE));
+ if (header && header->prefs)
+ create_widgets_with_domain (iface_prefs_box, header->prefs->widgets,
+ header->prefs->n_widgets, header->domain);
}
-static GList * output_get_list (void)
+static void iface_combo_changed (void)
{
- static GList * list = NULL;
+ gtk_container_foreach ((GtkContainer *) iface_prefs_box, (GtkCallback) gtk_widget_destroy, NULL);
- if (list == NULL)
+ plugin_enable (plugin_by_index (PLUGIN_TYPE_IFACE, iface_combo_selected), TRUE);
+
+ iface_fill_prefs_box ();
+ gtk_widget_show_all (iface_prefs_box);
+}
+
+static const ComboBoxElements * iface_combo_fill (int * n_elements)
+{
+ if (! iface_combo_elements)
{
- plugin_for_each (PLUGIN_TYPE_OUTPUT, (PluginForEachFunc) output_enum_cb,
- & list);
- list = g_list_reverse (list);
+ iface_combo_elements = fill_plugin_combo (PLUGIN_TYPE_IFACE);
+ iface_combo_selected = plugin_get_index (plugin_get_current (PLUGIN_TYPE_IFACE));
}
- return list;
+ * n_elements = iface_combo_elements->len;
+ return (const ComboBoxElements *) iface_combo_elements->data;
}
-static void output_combo_update (GtkComboBox * combo)
+static void * iface_create_prefs_box (void)
{
- PluginHandle * plugin = plugin_get_current (PLUGIN_TYPE_OUTPUT);
- gtk_combo_box_set_active (combo, g_list_index (output_get_list (), plugin));
- gtk_widget_set_sensitive (output_config_button, plugin_has_configure (plugin));
- gtk_widget_set_sensitive (output_about_button, plugin_has_about (plugin));
+ iface_prefs_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ iface_fill_prefs_box ();
+ return iface_prefs_box;
}
-static void output_combo_changed (GtkComboBox * combo)
+static void create_appearance_category (void)
{
- PluginHandle * plugin = g_list_nth_data (output_get_list (),
- gtk_combo_box_get_active (combo));
- g_return_if_fail (plugin != NULL);
+ GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add ((GtkContainer *) category_notebook, vbox);
+ create_widgets ((GtkBox *) vbox, appearance_page_widgets, ARRAY_LEN (appearance_page_widgets));
+}
- plugin_enable (plugin, TRUE);
- output_combo_update (combo);
+static void output_combo_changed (void)
+{
+ PluginHandle * plugin = plugin_by_index (PLUGIN_TYPE_OUTPUT, output_combo_selected);
+
+ if (plugin_enable (plugin, TRUE))
+ {
+ gtk_widget_set_sensitive (output_config_button, plugin_has_configure (plugin));
+ gtk_widget_set_sensitive (output_about_button, plugin_has_about (plugin));
+ }
}
-static void output_combo_fill (GtkComboBox * combo)
+static const ComboBoxElements * output_combo_fill (int * n_elements)
{
- for (GList * node = output_get_list (); node != NULL; node = node->next)
- gtk_combo_box_text_append_text ((GtkComboBoxText *) combo,
- plugin_get_name (node->data));
+ if (! output_combo_elements)
+ {
+ output_combo_elements = fill_plugin_combo (PLUGIN_TYPE_OUTPUT);
+ output_combo_selected = plugin_get_index (output_plugin_get_current ());
+ }
+
+ * n_elements = output_combo_elements->len;
+ return (const ComboBoxElements *) output_combo_elements->data;
}
static void output_bit_depth_changed (void)
@@ -1124,214 +624,191 @@ static void output_bit_depth_changed (void)
output_reset (OUTPUT_RESET_SOFT);
}
-static void output_do_config (void)
+static void output_do_config (void * unused)
{
- PluginHandle * plugin = output_plugin_get_current ();
- g_return_if_fail (plugin != NULL);
- plugin_do_configure (plugin);
+ plugin_do_configure (output_plugin_get_current ());
}
-static void output_do_about (void)
+static void output_do_about (void * unused)
{
- PluginHandle * plugin = output_plugin_get_current ();
- g_return_if_fail (plugin != NULL);
- plugin_do_about (plugin);
+ plugin_do_about (output_plugin_get_current ());
}
-static void * create_output_plugin_box (void)
+static void * output_create_config_button (void)
{
- GtkWidget * hbox1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
- gtk_box_pack_start ((GtkBox *) hbox1, gtk_label_new (_("Output plugin:")), FALSE, FALSE, 0);
-
- GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
- gtk_box_pack_start ((GtkBox *) hbox1, vbox, FALSE, FALSE, 0);
+ bool_t enabled = plugin_has_configure (output_plugin_get_current ());
- GtkWidget * hbox2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
- gtk_box_pack_start ((GtkBox *) vbox, hbox2, FALSE, FALSE, 0);
+ output_config_button = audgui_button_new (_("_Settings"),
+ "preferences-system", output_do_config, NULL);
+ gtk_widget_set_sensitive (output_config_button, enabled);
- GtkWidget * output_plugin_cbox = gtk_combo_box_text_new ();
- gtk_box_pack_start ((GtkBox *) hbox2, output_plugin_cbox, FALSE, FALSE, 0);
-
- GtkWidget * hbox3 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
- gtk_box_pack_start ((GtkBox *) vbox, hbox3, FALSE, FALSE, 0);
-
- output_config_button = gtk_button_new_from_stock (GTK_STOCK_PREFERENCES);
- gtk_box_pack_start ((GtkBox *) hbox3, output_config_button, FALSE, FALSE, 0);
-
- output_about_button = gtk_button_new_from_stock (GTK_STOCK_ABOUT);
- gtk_box_pack_start ((GtkBox *) hbox3, output_about_button, FALSE, FALSE, 0);
+ return output_config_button;
+}
- output_combo_fill ((GtkComboBox *) output_plugin_cbox);
- output_combo_update ((GtkComboBox *) output_plugin_cbox);
+static void * output_create_about_button (void)
+{
+ bool_t enabled = plugin_has_about (output_plugin_get_current ());
- g_signal_connect (output_plugin_cbox, "changed", (GCallback) output_combo_changed, NULL);
- g_signal_connect (output_config_button, "clicked", (GCallback) output_do_config, NULL);
- g_signal_connect (output_about_button, "clicked", (GCallback) output_do_about, NULL);
+ output_about_button = audgui_button_new (_("_About"), "help-about", output_do_about, NULL);
+ gtk_widget_set_sensitive (output_about_button, enabled);
- return hbox1;
+ return output_about_button;
}
static void create_audio_category (void)
{
GtkWidget * audio_page_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
- create_widgets ((GtkBox *) audio_page_vbox, audio_page_widgets, G_N_ELEMENTS (audio_page_widgets));
+ create_widgets ((GtkBox *) audio_page_vbox, audio_page_widgets, ARRAY_LEN (audio_page_widgets));
gtk_container_add ((GtkContainer *) category_notebook, audio_page_vbox);
}
-static void
-create_connectivity_category(void)
+static void create_connectivity_category (void)
{
- GtkWidget *connectivity_page_vbox;
- GtkWidget *vbox29;
-
- connectivity_page_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
- gtk_container_add (GTK_CONTAINER (category_notebook), connectivity_page_vbox);
+ GtkWidget * connectivity_page_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add ((GtkContainer *) category_notebook, connectivity_page_vbox);
- vbox29 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
- gtk_box_pack_start (GTK_BOX (connectivity_page_vbox), vbox29, TRUE, TRUE, 0);
+ GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_pack_start ((GtkBox *) connectivity_page_vbox, vbox, TRUE, TRUE, 0);
- create_widgets(GTK_BOX(vbox29), connectivity_page_widgets, G_N_ELEMENTS(connectivity_page_widgets));
+ create_widgets ((GtkBox *) vbox, connectivity_page_widgets, ARRAY_LEN (connectivity_page_widgets));
}
static void create_plugin_category (void)
{
- GtkWidget * notebook = gtk_notebook_new ();
- gtk_container_add ((GtkContainer *) category_notebook, notebook);
-
- int types[] = {PLUGIN_TYPE_TRANSPORT, PLUGIN_TYPE_PLAYLIST,
- PLUGIN_TYPE_INPUT, PLUGIN_TYPE_EFFECT, PLUGIN_TYPE_VIS, PLUGIN_TYPE_GENERAL};
- const char * names[] = {N_("Transport"), N_("Playlist"), N_("Input"),
- N_("Effect"), N_("Visualization"), N_("General")};
+ plugin_notebook = gtk_notebook_new ();
+ gtk_container_add ((GtkContainer *) category_notebook, plugin_notebook);
- for (int i = 0; i < G_N_ELEMENTS (types); i ++)
- gtk_notebook_append_page ((GtkNotebook *) notebook, plugin_view_new
- (types[i]), gtk_label_new (_(names[i])));
+ for (int i = 0; i < ARRAY_LEN (plugin_categories); i ++)
+ {
+ const PluginCategory * cat = & plugin_categories[i];
+ gtk_notebook_append_page ((GtkNotebook *) plugin_notebook,
+ plugin_view_new (cat->type), gtk_label_new (_(cat->name)));
+ }
}
-static bool_t
-prefswin_destroy(GtkWidget *window, GdkEvent *event, void * data)
+static void destroy_cb (void)
{
prefswin = NULL;
+ category_treeview = NULL;
category_notebook = NULL;
- gtk_widget_destroy(window);
- return TRUE;
+ titlestring_entry = NULL;
+
+ if (iface_combo_elements)
+ {
+ g_array_free (iface_combo_elements, TRUE);
+ iface_combo_elements = NULL;
+ }
+
+ if (output_combo_elements)
+ {
+ g_array_free (output_combo_elements, TRUE);
+ output_combo_elements = NULL;
+ }
}
static void create_prefs_window (void)
{
- char *aud_version_string;
-
- GtkWidget *vbox;
- GtkWidget *hbox1;
- GtkWidget *scrolledwindow6;
- GtkWidget *hseparator1;
- GtkWidget *hbox4;
- GtkWidget *audversionlabel;
- GtkWidget *prefswin_button_box;
- GtkWidget *close;
- GtkAccelGroup *accel_group;
-
- accel_group = gtk_accel_group_new ();
-
prefswin = gtk_window_new (GTK_WINDOW_TOPLEVEL);
- gtk_window_set_type_hint (GTK_WINDOW (prefswin), GDK_WINDOW_TYPE_HINT_DIALOG);
- gtk_container_set_border_width (GTK_CONTAINER (prefswin), 12);
- gtk_window_set_title (GTK_WINDOW (prefswin), _("Audacious Preferences"));
- gtk_window_set_position (GTK_WINDOW (prefswin), GTK_WIN_POS_CENTER);
- gtk_window_set_default_size (GTK_WINDOW (prefswin), 680, 400);
+ gtk_window_set_type_hint ((GtkWindow *) prefswin, GDK_WINDOW_TYPE_HINT_DIALOG);
+ gtk_container_set_border_width ((GtkContainer *) prefswin, 12);
+ gtk_window_set_title ((GtkWindow *) prefswin, _("Audacious Settings"));
+ gtk_window_set_default_size ((GtkWindow *) prefswin, 680, 400);
- vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
- gtk_container_add (GTK_CONTAINER (prefswin), vbox);
+ GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add ((GtkContainer *) prefswin, vbox);
- hbox1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
- gtk_box_pack_start (GTK_BOX (vbox), hbox1, TRUE, TRUE, 0);
+ GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
+ gtk_box_pack_start ((GtkBox *) vbox, hbox, TRUE, TRUE, 0);
- scrolledwindow6 = gtk_scrolled_window_new (NULL, NULL);
- gtk_box_pack_start (GTK_BOX (hbox1), scrolledwindow6, FALSE, FALSE, 0);
- gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow6), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
- gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow6), GTK_SHADOW_IN);
+ GtkWidget * scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
+ gtk_box_pack_start ((GtkBox *) hbox, scrolledwindow, FALSE, FALSE, 0);
+ gtk_scrolled_window_set_policy ((GtkScrolledWindow *) scrolledwindow,
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type ((GtkScrolledWindow *) scrolledwindow, GTK_SHADOW_IN);
category_treeview = gtk_tree_view_new ();
- gtk_container_add (GTK_CONTAINER (scrolledwindow6), category_treeview);
- gtk_widget_set_size_request (scrolledwindow6, 168, -1);
- gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (category_treeview), FALSE);
+ gtk_container_add ((GtkContainer *) scrolledwindow, category_treeview);
+ gtk_widget_set_size_request (scrolledwindow, 168, -1);
+ gtk_tree_view_set_headers_visible ((GtkTreeView *) category_treeview, FALSE);
category_notebook = gtk_notebook_new ();
- gtk_box_pack_start (GTK_BOX (hbox1), category_notebook, TRUE, TRUE, 0);
+ gtk_box_pack_start ((GtkBox *) hbox, category_notebook, TRUE, TRUE, 0);
gtk_widget_set_can_focus (category_notebook, FALSE);
- gtk_notebook_set_show_tabs (GTK_NOTEBOOK (category_notebook), FALSE);
- gtk_notebook_set_show_border (GTK_NOTEBOOK (category_notebook), FALSE);
- gtk_notebook_set_scrollable (GTK_NOTEBOOK (category_notebook), TRUE);
-
- create_audio_category();
- create_connectivity_category();
- create_playlist_category();
- create_song_info_category();
- create_plugin_category();
-
- hseparator1 = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
- gtk_box_pack_start (GTK_BOX (vbox), hseparator1, FALSE, FALSE, 6);
-
- hbox4 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
- gtk_box_pack_start (GTK_BOX (vbox), hbox4, FALSE, FALSE, 0);
-
- audversionlabel = gtk_label_new ("");
- gtk_box_pack_start (GTK_BOX (hbox4), audversionlabel, FALSE, FALSE, 0);
- gtk_label_set_use_markup (GTK_LABEL (audversionlabel), TRUE);
-
- prefswin_button_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
- gtk_box_pack_start (GTK_BOX (hbox4), prefswin_button_box, TRUE, TRUE, 0);
- gtk_button_box_set_layout (GTK_BUTTON_BOX (prefswin_button_box), GTK_BUTTONBOX_END);
- gtk_box_set_spacing (GTK_BOX (prefswin_button_box), 6);
-
- close = gtk_button_new_from_stock ("gtk-close");
- gtk_container_add (GTK_CONTAINER (prefswin_button_box), close);
- gtk_widget_set_can_default(close, TRUE);
- gtk_widget_add_accelerator (close, "clicked", accel_group,
- GDK_KEY_Escape, (GdkModifierType) 0,
- GTK_ACCEL_VISIBLE);
-
-
- gtk_window_add_accel_group (GTK_WINDOW (prefswin), accel_group);
-
- /* connect signals */
- g_signal_connect(G_OBJECT(prefswin), "delete_event",
- G_CALLBACK(prefswin_destroy),
- NULL);
- g_signal_connect_swapped(G_OBJECT(close), "clicked",
- G_CALLBACK(prefswin_destroy),
- prefswin);
-
- /* create category view */
+ gtk_notebook_set_show_tabs ((GtkNotebook *) category_notebook, FALSE);
+ gtk_notebook_set_show_border ((GtkNotebook *) category_notebook, FALSE);
+
+ create_appearance_category ();
+ create_audio_category ();
+ create_connectivity_category ();
+ create_playlist_category ();
+ create_song_info_category ();
+ create_plugin_category ();
+
+ GtkWidget * hseparator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_box_pack_start ((GtkBox *) vbox, hseparator, FALSE, FALSE, 6);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start ((GtkBox *) vbox, hbox, FALSE, FALSE, 0);
+
+ GtkWidget * audversionlabel = gtk_label_new (aud_version_string);
+ gtk_box_pack_start ((GtkBox *) hbox, audversionlabel, FALSE, FALSE, 0);
+ gtk_label_set_use_markup ((GtkLabel *) audversionlabel, TRUE);
+
+ GtkWidget * prefswin_button_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_box_pack_start ((GtkBox *) hbox, prefswin_button_box, TRUE, TRUE, 0);
+ gtk_button_box_set_layout ((GtkButtonBox *) prefswin_button_box, GTK_BUTTONBOX_END);
+ gtk_box_set_spacing ((GtkBox *) prefswin_button_box, 6);
+
+ GtkWidget * close = audgui_button_new (_("_Close"), "window-close",
+ (AudguiCallback) gtk_widget_destroy, prefswin);
+ gtk_container_add ((GtkContainer *) prefswin_button_box, close);
+ gtk_widget_set_can_default (close, TRUE);
+
fill_category_list ((GtkTreeView *) category_treeview, (GtkNotebook *) category_notebook);
- /* audacious version label */
+ gtk_widget_show_all (vbox);
- aud_version_string = g_strdup_printf
- ("<span size='small'>%s (%s)</span>", "Audacious " VERSION, BUILDSTAMP);
+ g_signal_connect (prefswin, "destroy", (GCallback) destroy_cb, NULL);
- gtk_label_set_markup( GTK_LABEL(audversionlabel) , aud_version_string );
- g_free(aud_version_string);
- gtk_widget_show_all(vbox);
+ audgui_destroy_on_escape (prefswin);
}
-void
-destroy_prefs_window(void)
+void show_prefs_window (void)
{
- prefswin_destroy(prefswin, NULL, NULL);
+ if (! prefswin)
+ create_prefs_window ();
+
+ change_category (CATEGORY_APPEARANCE);
+
+ gtk_window_present ((GtkWindow *) prefswin);
}
-void show_prefs_window (void)
+void show_prefs_for_plugin_type (int type)
{
if (! prefswin)
create_prefs_window ();
+ if (type == PLUGIN_TYPE_IFACE)
+ change_category (CATEGORY_APPEARANCE);
+ else if (type == PLUGIN_TYPE_OUTPUT)
+ change_category (CATEGORY_AUDIO);
+ else
+ {
+ change_category (CATEGORY_PLUGINS);
+
+ for (int i = 0; i < ARRAY_LEN (plugin_categories); i ++)
+ {
+ if (plugin_categories[i].type == type)
+ gtk_notebook_set_current_page ((GtkNotebook *) plugin_notebook, i);
+ }
+ }
+
gtk_window_present ((GtkWindow *) prefswin);
}
void hide_prefs_window (void)
{
- g_return_if_fail(prefswin);
- gtk_widget_hide(GTK_WIDGET(prefswin));
+ if (prefswin)
+ gtk_widget_destroy (prefswin);
}
diff --git a/src/audacious/ui_preferences.h b/src/audacious/ui_preferences.h
index ab91f1b..b9b2707 100644
--- a/src/audacious/ui_preferences.h
+++ b/src/audacious/ui_preferences.h
@@ -1,6 +1,6 @@
/*
* ui_preferences.h
- * Copyright 2006-2010 William Pitcock, Tomasz MoƄ, and John Lindgren
+ * Copyright 2006-2012 William Pitcock, Tomasz MoƄ, 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,7 +22,7 @@
#include <gtk/gtk.h>
-#include "preferences.h"
+#include "types.h"
void show_prefs_window(void);
void hide_prefs_window(void);
diff --git a/src/audacious/util.c b/src/audacious/util.c
index 37455b5..3556e68 100644
--- a/src/audacious/util.c
+++ b/src/audacious/util.c
@@ -1,6 +1,6 @@
/*
* util.c
- * Copyright 2009-2012 John Lindgren and MichaƂ Lipski
+ * Copyright 2009-2013 John Lindgren and MichaƂ Lipski
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,7 +17,10 @@
* the use of this software.
*/
-#include <dirent.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
#ifdef _WIN32
@@ -29,11 +32,6 @@
#endif
#include <glib.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
-#include <errno.h>
#include <libaudcore/audstrings.h>
@@ -45,64 +43,64 @@
bool_t dir_foreach (const char * path, DirForeachFunc func, void * user)
{
- DIR * dir = opendir (path);
+ GDir * dir = g_dir_open (path, 0, NULL);
if (! dir)
return FALSE;
- struct dirent * entry;
- while ((entry = readdir (dir)))
+ const char * name;
+ while ((name = g_dir_read_name (dir)))
{
- if (entry->d_name[0] == '.')
- continue;
-
- char * full = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s", path, entry->d_name);
- bool_t stop = func (full, entry->d_name, user);
- g_free (full);
+ char * full = filename_build (path, name);
+ bool_t stop = func (full, name, user);
+ str_unref (full);
if (stop)
break;
}
- closedir (dir);
+ g_dir_close (dir);
return TRUE;
}
-char * construct_uri (const char * string, const char * playlist_name)
+char * construct_uri (const char * path, const char * reference)
{
/* URI */
- if (strstr (string, "://"))
- return strdup (string);
+ if (strstr (path, "://"))
+ return str_get (path);
- /* absolute filename (assumed UTF-8) */
+ /* absolute filename */
#ifdef _WIN32
- if (string[0] && string[1] == ':' && string[2] == '\\')
+ if (path[0] && path[1] == ':' && path[2] == '\\')
#else
- if (string[0] == '/')
+ if (path[0] == '/')
#endif
- return filename_to_uri (string);
+ return filename_to_uri (path);
- /* relative filename (assumed UTF-8) */
- const char * slash = strrchr (playlist_name, '/');
+ /* relative path */
+ const char * slash = strrchr (reference, '/');
if (! slash)
return NULL;
- int pathlen = slash + 1 - playlist_name;
- int rellen = strlen (string);
+ char * utf8 = str_to_utf8 (path, -1);
+ if (! utf8)
+ return NULL;
+
+ int pathlen = slash + 1 - reference;
- char buf[pathlen + 3 * rellen + 1];
- memcpy (buf, playlist_name, pathlen);
+ char buf[pathlen + 3 * strlen (utf8) + 1];
+ memcpy (buf, reference, pathlen);
if (get_bool (NULL, "convert_backslash"))
{
- char tmp[rellen + 1];
- strcpy (tmp, string);
- string_replace_char (tmp, '\\', '/');
+ SCOPY (tmp, utf8);
+ str_replace_char (tmp, '\\', '/');
str_encode_percent (tmp, -1, buf + pathlen);
}
else
- str_encode_percent (string, -1, buf + pathlen);
+ str_encode_percent (utf8, -1, buf + pathlen);
- return strdup (buf);
+ str_unref (utf8);
+ return str_get (buf);
}
void
@@ -117,13 +115,14 @@ make_directory(const char * path, mode_t mode)
char * write_temp_file (void * data, int64_t len)
{
- char * name = g_strdup_printf ("%s/audacious-temp-XXXXXX", g_get_tmp_dir ());
+ char * temp = filename_build (g_get_tmp_dir (), "audacious-temp-XXXXXX");
+ SCOPY (name, temp);
+ str_unref (temp);
int handle = g_mkstemp (name);
if (handle < 0)
{
fprintf (stderr, "Error creating temporary file: %s\n", strerror (errno));
- g_free (name);
return NULL;
}
@@ -134,7 +133,6 @@ char * write_temp_file (void * data, int64_t len)
{
fprintf (stderr, "Error writing %s: %s\n", name, strerror (errno));
close (handle);
- g_free (name);
return NULL;
}
@@ -145,72 +143,103 @@ char * write_temp_file (void * data, int64_t len)
if (close (handle) < 0)
{
fprintf (stderr, "Error closing %s: %s\n", name, strerror (errno));
- g_free (name);
return NULL;
}
- return name;
+ return str_get (name);
}
char * get_path_to_self (void)
{
-#if defined _WIN32 || defined HAVE_PROC_SELF_EXE
+#ifdef HAVE_PROC_SELF_EXE
int size = 256;
- char * buf = g_malloc (size);
while (1)
{
+ char buf[size];
int len;
-#ifdef _WIN32
- if (! (len = GetModuleFileName (NULL, buf, size)))
- {
- fprintf (stderr, "GetModuleFileName failed.\n");
- g_free (buf);
- return NULL;
- }
-#else
if ((len = readlink ("/proc/self/exe", buf, size)) < 0)
{
fprintf (stderr, "Cannot access /proc/self/exe: %s.\n", strerror (errno));
- g_free (buf);
return NULL;
}
-#endif
if (len < size)
{
buf[len] = 0;
- return buf;
+ return str_get (buf);
+ }
+
+ size += size;
+ }
+#elif defined _WIN32
+ int size = 256;
+
+ while (1)
+ {
+ wchar_t buf[size];
+ int len;
+
+ if (! (len = GetModuleFileNameW (NULL, buf, size)))
+ {
+ fprintf (stderr, "GetModuleFileName failed.\n");
+ return NULL;
+ }
+
+ if (len < size)
+ {
+ char * temp = g_utf16_to_utf8 (buf, len, NULL, NULL, NULL);
+ char * path = str_get (temp);
+ g_free (temp);
+ return path;
}
size += size;
- buf = g_realloc (buf, size);
}
#elif defined __APPLE__
unsigned int size = 256;
- char * buf = g_malloc (size);
while (1)
{
+ char buf[size];
int res;
if (! (res = _NSGetExecutablePath (buf, &size)))
- return buf;
+ return str_get (buf);
- if (res == -1)
- buf = g_realloc (buf, size);
- else
- {
- g_free (buf);
+ if (res != -1)
return NULL;
- }
}
#else
return NULL;
#endif
}
+#ifdef _WIN32
+void get_argv_utf8 (int * argc, char * * * argv)
+{
+ wchar_t * combined = GetCommandLineW ();
+ wchar_t * * split = CommandLineToArgvW (combined, argc);
+
+ * argv = g_new (char *, argc + 1);
+
+ for (int i = 0; i < * argc; i ++)
+ (* argv)[i] = g_utf16_to_utf8 (split[i], -1, NULL, NULL, NULL);
+
+ (* argv)[* argc] = 0;
+
+ LocalFree (split);
+}
+
+void free_argv_utf8 (int * argc, char * * * argv)
+{
+ g_strfreev (* argv);
+ * argc = 0;
+ * argv = NULL;
+}
+#endif
+
/* Strips various common top-level folders from a filename. The string passed
* will not be modified, but the string returned will share the same memory.
* Examples:
@@ -327,7 +356,7 @@ static char * stream_name (char * name)
static char * get_nonblank_field (const Tuple * tuple, int field)
{
- char * str = tuple ? tuple_get_str (tuple, field, NULL) : NULL;
+ char * str = tuple ? tuple_get_str (tuple, field) : NULL;
if (str && ! str[0])
{
@@ -375,13 +404,15 @@ DONE:
if (! filename)
goto DONE;
+ SCOPY (buf, filename);
+
char * base, * first, * second;
- split_filename (skip_top_folders (filename), & base, & first, & second);
+ split_filename (skip_top_folders (buf), & base, & first, & second);
if (! title)
title = str_get (base);
- for (int i = 0; i < G_N_ELEMENTS (skip); i ++)
+ for (int i = 0; i < ARRAY_LEN (skip); i ++)
{
if (first && ! g_ascii_strcasecmp (first, skip[i]))
first = NULL;
@@ -402,12 +433,11 @@ DONE:
album = str_get (first);
}
- free (filename);
+ str_unref (filename);
}
else
{
- char buf[strlen (name) + 1];
- strcpy (buf, name);
+ SCOPY (buf, name);
if (! title)
{
@@ -424,3 +454,21 @@ DONE:
goto DONE;
}
+
+char * last_path_element (char * path)
+{
+ char * slash = strrchr (path, G_DIR_SEPARATOR);
+ return (slash && slash[1]) ? slash + 1 : NULL;
+}
+
+void cut_path_element (char * path, char * elem)
+{
+#ifdef _WIN32
+ if (elem > path + 3)
+#else
+ if (elem > path + 1)
+#endif
+ elem[-1] = 0; /* overwrite slash */
+ else
+ elem[0] = 0; /* leave [drive letter and] leading slash */
+}
diff --git a/src/audacious/util.h b/src/audacious/util.h
index 1c244f5..7f92cb3 100644
--- a/src/audacious/util.h
+++ b/src/audacious/util.h
@@ -1,6 +1,6 @@
/*
* util.h
- * Copyright 2009-2011 John Lindgren
+ * Copyright 2009-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -30,11 +30,19 @@ typedef bool_t(*DirForeachFunc) (const char * path,
bool_t dir_foreach (const char * path, DirForeachFunc func, void * user_data);
void make_directory(const char * path, mode_t mode);
-char * write_temp_file (void * data, int64_t len);
+char * write_temp_file (void * data, int64_t len); /* pooled */
-char * get_path_to_self (void);
+char * get_path_to_self (void); /* pooled */
+
+#ifdef _WIN32
+void get_argv_utf8 (int * argc, char * * * argv);
+void free_argv_utf8 (int * argc, char * * * argv);
+#endif
void describe_song (const char * filename, const Tuple * tuple,
char * * title, char * * artist, char * * album);
+char * last_path_element (char * path);
+void cut_path_element (char * path, char * elem);
+
#endif /* AUDACIOUS_UTIL_H */
diff --git a/src/audacious/vis_runner.c b/src/audacious/vis_runner.c
index 0a6bead..ba8390c 100644
--- a/src/audacious/vis_runner.c
+++ b/src/audacious/vis_runner.c
@@ -1,6 +1,6 @@
/*
* vis_runner.c
- * Copyright 2009-2011 John Lindgren
+ * Copyright 2009-2012 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,35 +17,85 @@
* the use of this software.
*/
-#include <glib.h>
+#include <assert.h>
#include <pthread.h>
#include <stdint.h>
#include <string.h>
+#include <glib.h>
+
#include "output.h"
#include "vis_runner.h"
#include "visualization.h"
#define INTERVAL 30 /* milliseconds */
+#define FRAMES_PER_NODE 512
-typedef struct {
- int time;
- float * data;
+struct _VisNode {
+ struct _VisNode * next;
int channels;
-} VisNode;
+ int time;
+ float data[];
+};
+
+typedef struct _VisNode VisNode;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static bool_t enabled = FALSE;
static bool_t playing = FALSE, paused = FALSE, active = FALSE;
static VisNode * current_node = NULL;
static int current_frames;
-static GQueue vis_list = G_QUEUE_INIT;
+static VisNode * vis_list = NULL;
+static VisNode * vis_list_tail = NULL;
+static VisNode * vis_pool = NULL;
static int send_source = 0, clear_source = 0;
-static void vis_node_free (VisNode * node)
+static VisNode * alloc_node_locked (int channels)
+{
+ VisNode * node;
+
+ if (vis_pool)
+ {
+ node = vis_pool;
+ assert (node->channels == channels);
+ vis_pool = node->next;
+ }
+ else
+ {
+ node = g_malloc (offsetof (VisNode, data) + sizeof (float) * channels * FRAMES_PER_NODE);
+ node->channels = channels;
+ }
+
+ node->next = NULL;
+ return node;
+}
+
+static void free_node_locked (VisNode * node)
+{
+ node->next = vis_pool;
+ vis_pool = node;
+}
+
+static void push_node_locked (VisNode * node)
{
- g_free (node->data);
- g_free (node);
+ if (vis_list)
+ vis_list_tail->next = node;
+ else
+ vis_list = node;
+
+ vis_list_tail = node;
+}
+
+static VisNode * pop_node_locked (void)
+{
+ VisNode * node = vis_list;
+ vis_list = node->next;
+ node->next = NULL;
+
+ if (vis_list_tail == node)
+ vis_list_tail = NULL;
+
+ return node;
}
static bool_t send_audio (void * unused)
@@ -62,21 +112,20 @@ static bool_t send_audio (void * unused)
}
VisNode * vis_node = NULL;
- VisNode * next;
- while ((next = g_queue_peek_head (& vis_list)))
+ while (vis_list)
{
/* If we are considering a node, stop searching and use it if it is the
* most recent (that is, the next one is in the future). Otherwise,
* consider the next node if it is not in the future by more than the
* length of an interval. */
- if (next->time > outputted + (vis_node ? 0 : INTERVAL))
+ if (vis_list->time > outputted + (vis_node ? 0 : INTERVAL))
break;
if (vis_node)
- vis_node_free (vis_node);
+ free_node_locked (vis_node);
- vis_node = g_queue_pop_head (& vis_list);
+ vis_node = pop_node_locked ();
}
pthread_mutex_unlock (& mutex);
@@ -86,7 +135,10 @@ static bool_t send_audio (void * unused)
vis_send_audio (vis_node->data, vis_node->channels);
- vis_node_free (vis_node);
+ pthread_mutex_lock (& mutex);
+ free_node_locked (vis_node);
+ pthread_mutex_unlock (& mutex);
+
return TRUE;
}
@@ -101,16 +153,26 @@ static bool_t send_clear (void * unused)
return FALSE;
}
-static void flush (void)
+static void flush_locked (void)
{
- if (current_node)
+ g_free (current_node);
+ current_node = NULL;
+
+ while (vis_list)
{
- vis_node_free (current_node);
- current_node = NULL;
+ VisNode * node = vis_list;
+ vis_list = node->next;
+ g_free (node);
}
- g_queue_foreach (& vis_list, (GFunc) vis_node_free, NULL);
- g_queue_clear (& vis_list);
+ vis_list_tail = NULL;
+
+ while (vis_pool)
+ {
+ VisNode * node = vis_pool;
+ vis_pool = node->next;
+ g_free (node);
+ }
if (! clear_source)
clear_source = g_timeout_add (0, send_clear, NULL);
@@ -119,11 +181,11 @@ static void flush (void)
void vis_runner_flush (void)
{
pthread_mutex_lock (& mutex);
- flush ();
+ flush_locked ();
pthread_mutex_unlock (& mutex);
}
-static void start_stop (bool_t new_playing, bool_t new_paused)
+static void start_stop_locked (bool_t new_playing, bool_t new_paused)
{
playing = new_playing;
paused = new_paused;
@@ -142,7 +204,7 @@ static void start_stop (bool_t new_playing, bool_t new_paused)
}
if (! active)
- flush ();
+ flush_locked ();
else if (! paused)
send_source = g_timeout_add (INTERVAL, send_audio, NULL);
}
@@ -150,7 +212,7 @@ static void start_stop (bool_t new_playing, bool_t new_paused)
void vis_runner_start_stop (bool_t new_playing, bool_t new_paused)
{
pthread_mutex_lock (& mutex);
- start_stop (new_playing, new_paused);
+ start_stop_locked (new_playing, new_paused);
pthread_mutex_unlock (& mutex);
}
@@ -166,20 +228,15 @@ void vis_runner_pass_audio (int time, float * data, int samples, int
* multiple nodes from the same call. If current_node is present, it was
* partly built in the last call and needs to be finished. */
- if (current_node && current_node->channels != channels)
- {
- vis_node_free (current_node);
- current_node = NULL;
- }
-
int at = 0;
while (1)
{
- if (! current_node)
+ if (current_node)
+ assert (current_node->channels == channels);
+ else
{
int node_time = time;
- VisNode * last;
/* There is no partly-built node, so start a new one. Normally
* there will be nodes in the queue already; if so, we want to copy
@@ -188,8 +245,8 @@ void vis_runner_pass_audio (int time, float * data, int samples, int
* queue, we are at the beginning of the song or had an underrun,
* and we want to copy the earliest audio data we have. */
- if ((last = g_queue_peek_tail (& vis_list)))
- node_time = last->time + INTERVAL;
+ if (vis_list_tail)
+ node_time = vis_list_tail->time + INTERVAL;
at = channels * (int) ((int64_t) (node_time - time) * rate / 1000);
@@ -198,10 +255,8 @@ void vis_runner_pass_audio (int time, float * data, int samples, int
if (at >= samples)
break;
- current_node = g_malloc (sizeof (VisNode));
+ current_node = alloc_node_locked (channels);
current_node->time = node_time;
- current_node->data = g_malloc (sizeof (float) * channels * 512);
- current_node->channels = channels;
current_frames = 0;
}
@@ -210,14 +265,14 @@ void vis_runner_pass_audio (int time, float * data, int samples, int
* wait for more data to be passed in the next call. If we do fill the
* node, we loop and start building a new one. */
- int copy = MIN (samples - at, channels * (512 - current_frames));
+ int copy = MIN (samples - at, channels * (FRAMES_PER_NODE - current_frames));
memcpy (current_node->data + channels * current_frames, data + at, sizeof (float) * copy);
current_frames += copy / channels;
- if (current_frames < 512)
+ if (current_frames < FRAMES_PER_NODE)
break;
- g_queue_push_tail (& vis_list, current_node);
+ push_node_locked (current_node);
current_node = NULL;
}
@@ -229,6 +284,6 @@ void vis_runner_enable (bool_t enable)
{
pthread_mutex_lock (& mutex);
enabled = enable;
- start_stop (playing, paused);
+ start_stop_locked (playing, paused);
pthread_mutex_unlock (& mutex);
}
diff --git a/src/audtool/Makefile b/src/audtool/Makefile
index fa78492..90c822c 100644
--- a/src/audtool/Makefile
+++ b/src/audtool/Makefile
@@ -6,18 +6,13 @@ SRCS = main.c \
handlers_playqueue.c \
handlers_vitals.c \
handlers_equalizer.c \
- report.c
+ report.c \
+ wrappers.c
+
+EXT_DEPS += ../dbus/aud-dbus.a
include ../../buildsys.mk
include ../../extra.mk
-CPPFLAGS := -I.. -I../.. \
- ${CPPFLAGS} \
- ${AUDACIOUS_DEFINES} \
- ${DBUS_CFLAGS} \
- ${GTK_CFLAGS}
-
-LIBS := -L../libaudclient -laudclient \
- ${LIBS} \
- ${DBUS_LIBS} \
- ${GTK_LIBS}
+CPPFLAGS := -I../.. -I../dbus ${CPPFLAGS} ${GLIB_CFLAGS} ${GIO_CFLAGS}
+LIBS := ../dbus/aud-dbus.a ${LIBS} ${GLIB_LIBS} ${GIO_LIBS}
diff --git a/src/audtool/audtool.h b/src/audtool/audtool.h
index 6db161e..cb62488 100644
--- a/src/audtool/audtool.h
+++ b/src/audtool/audtool.h
@@ -21,112 +21,117 @@
#ifndef AUDTOOL_H
#define AUDTOOL_H
-#include <audacious/dbus.h>
-
-extern DBusGProxy *dbus_proxy;
-
-struct commandhandler {
- gchar *name;
- void (*handler)(gint argc, gchar **argv);
- gchar *desc;
- gint args;
+#include "aud-dbus.h"
+
+struct commandhandler
+{
+ char * name;
+ void (* handler) (int argc, char * * argv);
+ char * desc;
+ int args;
};
-extern struct commandhandler handlers[];
-
-extern void audtool_report(const gchar *str, ...);
-extern void audtool_whine(const gchar *str, ...);
-extern void audtool_whine_args(const gchar *name, const gchar *str, ...);
-extern void audtool_whine_tuple_fields(void);
-
-extern void get_handlers_list(gint, gchar **);
-extern void get_current_song(gint, gchar **);
-extern void get_current_song_filename(gint, gchar **);
-extern void get_current_song_length(gint, gchar **);
-extern void get_current_song_length_seconds(gint, gchar **);
-extern void get_current_song_length_frames(gint, gchar **);
-extern void get_current_song_output_length(gint, gchar **);
-extern void get_current_song_output_length_seconds(gint, gchar **);
-extern void get_current_song_output_length_frames(gint, gchar **);
-extern void get_current_song_bitrate(gint, gchar **);
-extern void get_current_song_bitrate_kbps(gint, gchar **);
-extern void get_current_song_frequency(gint, gchar **);
-extern void get_current_song_frequency_khz(gint, gchar **);
-extern void get_current_song_channels(gint, gchar **);
-extern void get_current_song_tuple_field_data(gint, gchar **argv);
-extern void get_current_song_info(gint argc, gchar **argv);
-
-extern void get_volume(gint, gchar **);
-extern void set_volume(gint, gchar **);
-
-extern void playlist_position(gint, gchar **);
-extern void playlist_advance(gint, gchar **);
-extern void playlist_auto_advance_status(gint, gchar **);
-extern void playlist_auto_advance_toggle(gint, gchar **);
-extern void playlist_reverse(gint, gchar **);
-extern void playlist_length(gint, gchar **);
-extern void playlist_song(gint, gchar **);
-extern void playlist_song_filename(gint, gchar **);
-extern void playlist_song_length(gint, gchar **);
-extern void playlist_song_length_seconds(gint, gchar **);
-extern void playlist_song_length_frames(gint, gchar **);
-extern void playlist_display(gint, gchar **);
-extern void playlist_position(gint, gchar **);
-extern void playlist_jump(gint, gchar **);
-extern void playlist_add_url_string(gint, gchar **);
-extern void playlist_delete(gint, gchar **);
-extern void playlist_clear(gint, gchar **);
-extern void playlist_repeat_status(gint, gchar **);
-extern void playlist_repeat_toggle(gint, gchar **);
-extern void playlist_shuffle_status(gint, gchar **);
-extern void playlist_shuffle_toggle(gint, gchar **);
-void playlist_stop_after_status (gint argc, gchar * * argv);
-void playlist_stop_after_toggle (gint argc, gchar * * argv);
-extern void playlist_tuple_field_data(gint, gchar **argv);
-extern void playlist_enqueue_to_temp(gint argc, gchar **argv);
-extern void playlist_ins_url_string(gint argc, gchar **argv);
-extern void playlist_title(gint, gchar **);
-
-extern void playqueue_add(gint, gchar **);
-extern void playqueue_remove(gint, gchar **);
-extern void playqueue_is_queued(gint, gchar **);
-extern void playqueue_get_queue_position(gint, gchar **);
-extern void playqueue_get_list_position(gint, gchar **);
-extern void playqueue_display(gint, gchar **);
-extern void playqueue_length(gint, gchar **);
-extern void playqueue_clear(gint, gchar **);
-
-extern void playback_play(gint, gchar **);
-extern void playback_pause(gint, gchar **);
-extern void playback_playpause(gint, gchar **);
-extern void playback_stop(gint, gchar **);
-extern void playback_playing(gint, gchar **);
-extern void playback_paused(gint, gchar **);
-extern void playback_stopped(gint, gchar **);
-extern void playback_status(gint, gchar **);
-extern void playback_seek(gint, gchar **);
-extern void playback_seek_relative(gint, gchar **);
-
-extern void mainwin_show(gint, gchar **);
-extern void show_preferences_window(gint, gchar **);
-extern void show_jtf_window(gint, gchar **);
-extern void show_filebrowser(gint, gchar **);
-extern void shutdown_audacious_server(gint, gchar **);
-extern void show_about_window(gint, gchar **);
-
-extern void toggle_aot(gint argc, gchar **argv);
-extern void get_skin(gint argc, gchar **argv);
-extern void set_skin(gint argc, gchar **argv);
-extern void get_version(gint argc, gchar **argv);
-
-extern void equalizer_get_eq(gint argc, gchar **argv);
-extern void equalizer_get_eq_preamp(gint argc, gchar **argv);
-extern void equalizer_get_eq_band(gint argc, gchar **argv);
-extern void equalizer_set_eq(gint argc, gchar **argv);
-extern void equalizer_set_eq_preamp(gint argc, gchar **argv);
-extern void equalizer_set_eq_band(gint argc, gchar **argv);
-extern void equalizer_active(gint argc, gchar **argv);
-
-extern gint check_args_playlist_pos(gint argc, gchar **argv);
+extern const struct commandhandler handlers[];
+extern ObjAudacious * dbus_proxy;
+
+void audtool_report (const char * str, ...);
+void audtool_whine (const char * str, ...);
+void audtool_whine_args (const char * name, const char * str, ...);
+void audtool_whine_tuple_fields (void);
+
+void get_handlers_list (int, char * *);
+void get_current_song (int, char * *);
+void get_current_song_filename (int, char * *);
+void get_current_song_length (int, char * *);
+void get_current_song_length_seconds (int, char * *);
+void get_current_song_length_frames (int, char * *);
+void get_current_song_output_length (int, char * *);
+void get_current_song_output_length_seconds (int, char * *);
+void get_current_song_output_length_frames (int, char * *);
+void get_current_song_bitrate (int, char * *);
+void get_current_song_bitrate_kbps (int, char * *);
+void get_current_song_frequency (int, char * *);
+void get_current_song_frequency_khz (int, char * *);
+void get_current_song_channels (int, char * *);
+void get_current_song_tuple_field_data (int, char * * argv);
+void get_current_song_info (int argc, char * * argv);
+
+void get_volume (int, char * *);
+void set_volume (int, char * *);
+
+void playlist_position (int, char * *);
+void playlist_advance (int, char * *);
+void playlist_auto_advance_status (int, char * *);
+void playlist_auto_advance_toggle (int, char * *);
+void playlist_reverse (int, char * *);
+void playlist_length (int, char * *);
+void playlist_song (int, char * *);
+void playlist_song_filename (int, char * *);
+void playlist_song_length (int, char * *);
+void playlist_song_length_seconds (int, char * *);
+void playlist_song_length_frames (int, char * *);
+void playlist_display (int, char * *);
+void playlist_position (int, char * *);
+void playlist_jump (int, char * *);
+void playlist_add_url_string (int, char * *);
+void playlist_delete (int, char * *);
+void playlist_clear (int, char * *);
+void playlist_repeat_status (int, char * *);
+void playlist_repeat_toggle (int, char * *);
+void playlist_shuffle_status (int, char * *);
+void playlist_shuffle_toggle (int, char * *);
+void playlist_stop_after_status (int argc, char * * argv);
+void playlist_stop_after_toggle (int argc, char * * argv);
+void playlist_tuple_field_data (int, char * * argv);
+void playlist_enqueue_to_temp (int argc, char * * argv);
+void playlist_ins_url_string (int argc, char * * argv);
+
+void number_of_playlists (int argc, char * * argv);
+void current_playlist (int argc, char * * argv);
+void set_current_playlist (int argc, char * * argv);
+void playlist_title (int argc, char * * argv);
+void set_playlist_title (int argc, char * * argv);
+void new_playlist (int argc, char * * argv);
+void delete_current_playlist (int argc, char * * argv);
+void play_current_playlist (int argc, char * * argv);
+
+void playqueue_add (int, char * *);
+void playqueue_remove (int, char * *);
+void playqueue_is_queued (int, char * *);
+void playqueue_get_queue_position (int, char * *);
+void playqueue_get_list_position (int, char * *);
+void playqueue_display (int, char * *);
+void playqueue_length (int, char * *);
+void playqueue_clear (int, char * *);
+
+void playback_play (int, char * *);
+void playback_pause (int, char * *);
+void playback_playpause (int, char * *);
+void playback_stop (int, char * *);
+void playback_playing (int, char * *);
+void playback_paused (int, char * *);
+void playback_stopped (int, char * *);
+void playback_status (int, char * *);
+void playback_seek (int, char * *);
+void playback_seek_relative (int, char * *);
+
+void mainwin_show (int, char * *);
+void show_preferences_window (int, char * *);
+void show_jtf_window (int, char * *);
+void show_filebrowser (int, char * *);
+void shutdown_audacious_server (int, char * *);
+void show_about_window (int, char * *);
+
+void get_version (int argc, char * * argv);
+
+void equalizer_get_eq (int argc, char * * argv);
+void equalizer_get_eq_preamp (int argc, char * * argv);
+void equalizer_get_eq_band (int argc, char * * argv);
+void equalizer_set_eq (int argc, char * * argv);
+void equalizer_set_eq_preamp (int argc, char * * argv);
+void equalizer_set_eq_band (int argc, char * * argv);
+void equalizer_active (int argc, char * * argv);
+
+int check_args_playlist_pos (int argc, char * * argv);
#endif
diff --git a/src/audtool/handlers_equalizer.c b/src/audtool/handlers_equalizer.c
index 2a35ff0..4c70126 100644
--- a/src/audtool/handlers_equalizer.c
+++ b/src/audtool/handlers_equalizer.c
@@ -1,6 +1,6 @@
/*
* handlers_equalizer.c
- * Copyright 2007-2008 Yoshiki Yazawa and Matti HÀmÀlÀinen
+ * Copyright 2007-2013 Yoshiki Yazawa, Matti HÀmÀlÀinen, and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -19,125 +19,105 @@
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include <locale.h>
-#include "libaudclient/audctrl.h"
+
#include "audtool.h"
+#include "wrappers.h"
+
+#define NUM_BANDS 10
-void equalizer_get_eq(gint argc, gchar **argv)
+void equalizer_get_eq (int argc, char * * argv)
{
- double preamp;
- GArray *bands;
- int i;
+ double preamp = 0.0;
+ GVariant * var = NULL;
- audacious_remote_get_eq(dbus_proxy, &preamp, &bands);
+ obj_audacious_call_get_eq_sync (dbus_proxy, & preamp, & var, NULL, NULL);
- audtool_report("preamp = %.2f", preamp);
- for(i=0; i<10; i++){
- printf("%.2f ", g_array_index(bands, gdouble, i));
- }
- printf("\n");
- g_array_free(bands, TRUE);
+ if (! var || ! g_variant_is_of_type (var, G_VARIANT_TYPE ("ad")))
+ exit (1);
+
+ audtool_report ("preamp = %.2f", preamp);
+
+ size_t nbands = 0;
+ const double * bands = g_variant_get_fixed_array (var, & nbands, sizeof (double));
+
+ if (nbands != NUM_BANDS)
+ exit (1);
+
+ for (int i = 0; i < NUM_BANDS; i ++)
+ printf ("%.2f ", bands[i]);
+
+ printf ("\n");
+ g_variant_unref (var);
}
-void equalizer_get_eq_preamp(gint argc, gchar **argv)
+void equalizer_get_eq_preamp (int argc, char * * argv)
{
- audtool_report("preamp = %.2f", audacious_remote_get_eq_preamp(dbus_proxy));
+ double preamp = 0;
+ obj_audacious_call_get_eq_preamp_sync (dbus_proxy, & preamp, NULL, NULL);
+ audtool_report ("preamp = %.2f", preamp);
}
-void equalizer_get_eq_band(gint argc, gchar **argv)
+void equalizer_get_eq_band (int argc, char * * argv)
{
- int band;
-
if (argc < 2)
{
- audtool_whine_args(argv[0], "<band>");
- exit(1);
- }
-
- band = atoi(argv[1]);
-
- /* FIXME, XXX, TODO: we should have a function for requesting
- * the actual number of bands, if we support dynamic amount some day ...
- * -- ccr
- */
- if (band < 0 || band > 9)
- {
- audtool_whine("band number out of range\n");
- exit(1);
+ audtool_whine_args (argv[0], "<band>");
+ exit (1);
}
- audtool_report("band %d = %.2f", band, audacious_remote_get_eq_band(dbus_proxy, band));
+ int band = atoi (argv[1]);
+ double level = 0;
+ obj_audacious_call_get_eq_band_sync (dbus_proxy, band, & level, NULL, NULL);
+ audtool_report ("band %d = %.2f", band, level);
}
-void equalizer_set_eq(gint argc, gchar **argv)
+void equalizer_set_eq (int argc, char * * argv)
{
- gdouble preamp;
- GArray *bands = g_array_sized_new(FALSE, FALSE, sizeof(gdouble), 10);
- int i;
-
- if (argc < 12)
+ if (argc < 2 + NUM_BANDS)
{
- audtool_whine_args(argv[0], "<preamp> <band0> <band1> <band2> <band3> <band4> <band5> <band6> <band7> <band8> <band9>");
- exit(1);
+ audtool_whine_args (argv[0], "<preamp> <band0> <band1> <band2> <band3> "
+ "<band4> <band5> <band6> <band7> <band8> <band9>");
+ exit (1);
}
- preamp = atof(argv[1]);
+ double preamp = atof (argv[1]);
+ double bands[NUM_BANDS];
- for(i=0; i<10; i++){
- gdouble val = atof(argv[i+2]);
- g_array_append_val(bands, val);
- }
+ for (int i = 0; i < NUM_BANDS; i ++)
+ bands[i] = atof (argv[i + 2]);
- audacious_remote_set_eq(dbus_proxy, preamp, bands);
+ GVariant * var = g_variant_new_fixed_array (G_VARIANT_TYPE_DOUBLE, bands,
+ NUM_BANDS, sizeof (double));
+ obj_audacious_call_set_eq_sync (dbus_proxy, preamp, var, NULL, NULL);
}
-void equalizer_set_eq_preamp(gint argc, gchar **argv)
+void equalizer_set_eq_preamp (int argc, char * * argv)
{
- gdouble preamp;
-
if (argc < 2)
{
- audtool_whine_args(argv[0], "<preamp>");
- exit(1);
+ audtool_whine_args (argv[0], "<preamp>");
+ exit (1);
}
- preamp = atof(argv[1]);
-
- audacious_remote_set_eq_preamp(dbus_proxy, preamp);
+ double preamp = atof (argv[1]);
+ obj_audacious_call_set_eq_preamp_sync (dbus_proxy, preamp, NULL, NULL);
}
-void equalizer_set_eq_band(gint argc, gchar **argv)
+void equalizer_set_eq_band (int argc, char * * argv)
{
- int band;
- gdouble preamp;
-
if (argc < 3)
{
- audtool_whine_args(argv[0], "<band> <value>");
- exit(1);
+ audtool_whine_args (argv[0], "<band> <value>");
+ exit (1);
}
- band = atoi(argv[1]);
- preamp = atof(argv[2]);
-
- audacious_remote_set_eq_band(dbus_proxy, band, preamp);
+ int band = atoi (argv[1]);
+ double level = atof (argv[2]);
+ obj_audacious_call_set_eq_band_sync (dbus_proxy, band, level, NULL, NULL);
}
-void equalizer_active(gint argc, gchar **argv)
+void equalizer_active (int argc, char * * argv)
{
- if (argc < 2)
- {
- audtool_whine_args(argv[0], "<on/off>");
- exit(1);
- }
-
- if (!g_ascii_strcasecmp(argv[1], "on")) {
- audacious_remote_eq_activate(dbus_proxy, TRUE);
- }
- else if (!g_ascii_strcasecmp(argv[1], "off")) {
- audacious_remote_eq_activate(dbus_proxy, FALSE);
- }
+ generic_on_off (argc, argv, obj_audacious_call_equalizer_activate_sync);
}
diff --git a/src/audtool/handlers_general.c b/src/audtool/handlers_general.c
index 5933e54..e1ff2c2 100644
--- a/src/audtool/handlers_general.c
+++ b/src/audtool/handlers_general.c
@@ -1,7 +1,7 @@
/*
* handlers_general.c
- * Copyright 2005-2008 George Averill, William Pitcock, Giacomo Lozito, and
- * Matti HÀmÀlÀinen
+ * Copyright 2005-2013 George Averill, William Pitcock, Giacomo Lozito,
+ * Matti HÀmÀlÀinen, and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -19,211 +19,96 @@
*/
#include <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include <locale.h>
-#include "libaudclient/audctrl.h"
+
#include "audtool.h"
+#include "wrappers.h"
-void get_volume(gint argc, gchar **argv)
+static int get_main_volume (void)
{
- gint i;
-
- i = audacious_remote_get_main_volume(dbus_proxy);
-
- audtool_report("%d", i);
+ int left = 0, right = 0;
+ obj_audacious_call_volume_sync (dbus_proxy, & left, & right, NULL, NULL);
+ return MAX (left, right);
}
-void set_volume(gint argc, gchar **argv)
+void get_volume (int argc, char * * argv)
{
- gint i, current_volume;
-
- if (argc < 2)
- {
- audtool_whine_args(argv[0], "<level>", argv[0]);
- exit(1);
- }
-
- current_volume = audacious_remote_get_main_volume(dbus_proxy);
- switch (argv[1][0])
- {
- case '+':
- case '-':
- i = current_volume + atoi(argv[1]);
- break;
- default:
- i = atoi(argv[1]);
- break;
- }
-
- audacious_remote_set_main_volume(dbus_proxy, i);
+ audtool_report ("%d", get_main_volume ());
}
-void mainwin_show(gint argc, gchar **argv)
+void set_volume (int argc, char * * argv)
{
- if (argc < 2) {
- audtool_whine_args(argv[0], "<on/off>");
- exit(1);
- }
-
- if (!g_ascii_strcasecmp(argv[1], "on")) {
- audacious_remote_main_win_toggle(dbus_proxy, TRUE);
- return;
- }
- else if (!g_ascii_strcasecmp(argv[1], "off")) {
- audacious_remote_main_win_toggle(dbus_proxy, FALSE);
- return;
+ if (argc < 2)
+ {
+ audtool_whine_args (argv[0], "<level>");
+ exit (1);
}
-}
-void show_preferences_window(gint argc, gchar **argv)
-{
- gboolean show = TRUE;
-
- if (argc < 2) {
-#if 0
- audtool_whine_args(argv[0], "<on/off>");
- exit(1);
-#else
- audacious_remote_toggle_prefs_box(dbus_proxy, show);
- return;
-#endif
- }
+ int vol = atoi (argv[1]);
- if (!g_ascii_strcasecmp(argv[1], "on"))
- show = TRUE;
- else if (!g_ascii_strcasecmp(argv[1], "off"))
- show = FALSE;
- else {
- audtool_whine_args(argv[0], "<on/off>");
- exit (1);
+ switch (argv[1][0])
+ {
+ case '+':
+ case '-':
+ vol += get_main_volume ();
+ break;
}
- audacious_remote_toggle_prefs_box(dbus_proxy, show);
+ obj_audacious_call_set_volume_sync (dbus_proxy, vol, vol, NULL, NULL);
}
-void show_about_window(gint argc, gchar **argv)
+void mainwin_show (int argc, char * * argv)
{
- gboolean show = TRUE;
-
- if (argc < 2) {
-#if 0
- audtool_whine_args(argv[0], "<on/off>");
- exit(1);
-#else
- audacious_remote_toggle_about_box(dbus_proxy, show);
- return;
-#endif
- }
-
- if (!g_ascii_strcasecmp(argv[1], "on"))
- show = TRUE;
- else if (!g_ascii_strcasecmp(argv[1], "off"))
- show = FALSE;
- else {
- audtool_whine_args(argv[0], "<on/off>");
- exit (1);
- }
-
- audacious_remote_toggle_about_box(dbus_proxy, show);
+ generic_on_off (argc, argv, obj_audacious_call_show_main_win_sync);
}
-void show_jtf_window(gint argc, gchar **argv)
+void show_preferences_window (int argc, char * * argv)
{
- gboolean show = TRUE;
-
- if (argc < 2) {
-#if 0
- audtool_whine_args(argv[0], "<on/off>");
- exit(1);
-#else
- audacious_remote_toggle_jtf_box(dbus_proxy, show);
- return;
-#endif
- }
- if (!g_ascii_strcasecmp(argv[1], "on"))
- show = TRUE;
- else if (!g_ascii_strcasecmp(argv[1], "off"))
- show = FALSE;
- else {
- audtool_whine_args(argv[0], "<on/off>");
- exit (1);
- }
-
- audacious_remote_toggle_jtf_box(dbus_proxy, show);
+ generic_on_off (argc, argv, obj_audacious_call_show_prefs_box_sync);
}
-void show_filebrowser(gint argc, gchar **argv)
+void show_about_window (int argc, char * * argv)
{
- gboolean show = TRUE;
-
- if (argc < 2) {
-#if 0
- audtool_whine_args(argv[0], "<on/off>");
- exit(1);
-#else
- audacious_remote_toggle_filebrowser(dbus_proxy, show);
- return;
-#endif
- }
-
- if (!g_ascii_strcasecmp(argv[1], "on"))
- show = TRUE;
- else if (!g_ascii_strcasecmp(argv[1], "off"))
- show = FALSE;
- else {
- audtool_whine_args(argv[0], "<on/off>");
- exit (1);
- }
+ generic_on_off (argc, argv, obj_audacious_call_show_about_box_sync);
+}
- audacious_remote_toggle_filebrowser(dbus_proxy, show);
+void show_jtf_window (int argc, char * * argv)
+{
+ generic_on_off (argc, argv, obj_audacious_call_show_jtf_box_sync);
}
-void shutdown_audacious_server(gint argc, gchar **argv)
+void show_filebrowser (int argc, char * * argv)
{
- audacious_remote_quit(dbus_proxy);
+ generic_on_off (argc, argv, obj_audacious_call_show_filebrowser_sync);
}
-void get_handlers_list(gint argc, gchar **argv)
+void shutdown_audacious_server (int argc, char * * argv)
{
- gint i;
-
- for (i = 0; handlers[i].name != NULL; i++)
- {
- if (!g_ascii_strcasecmp("<sep>", handlers[i].name))
- audtool_report("%s%s:", i == 0 ? "" : "\n", handlers[i].desc);
- else
- audtool_report(" %-34s - %s", handlers[i].name, handlers[i].desc);
- }
-
- audtool_report("");
- audtool_report("Handlers may be prefixed with `--' (GNU-style long-options) or not, your choice.");
- audtool_report("Report bugs to http://redmine.audacious-media-player.org/");
+ obj_audacious_call_quit_sync (dbus_proxy, NULL, NULL);
}
-void toggle_aot(gint argc, gchar **argv)
+void get_handlers_list (int argc, char * * argv)
{
- if (argc < 2)
+ for (int i = 0; handlers[i].name; i ++)
{
- audtool_whine_args(argv[0], "<on/off>");
- exit(1);
+ if (! g_ascii_strcasecmp ("<sep>", handlers[i].name))
+ audtool_report ("%s%s:", i == 0 ? "" : "\n", handlers[i].desc);
+ else
+ audtool_report (" %-34s - %s", handlers[i].name, handlers[i].desc);
}
- if (!g_ascii_strcasecmp(argv[1], "on")) {
- audacious_remote_toggle_aot(dbus_proxy, TRUE);
- return;
- }
- else if (!g_ascii_strcasecmp(argv[1], "off")) {
- audacious_remote_toggle_aot(dbus_proxy, FALSE);
- return;
- }
+ audtool_report ("");
+ audtool_report ("Handlers may be prefixed with `--' (GNU-style long-options) or not, your choice.");
+ audtool_report ("Report bugs to http://redmine.audacious-media-player.org/");
}
-void get_version(gint argc, gchar **argv)
+void get_version (int argc, char * * argv)
{
- gchar *version = NULL;
- version = audacious_remote_get_version(dbus_proxy);
- if(version)
- audtool_report("Audacious %s", version);
- g_free(version);
+ char * version = NULL;
+ obj_audacious_call_version_sync (dbus_proxy, & version, NULL, NULL);
+
+ if (! version)
+ exit (1);
+
+ audtool_report ("Audacious %s", version);
+ g_free (version);
}
diff --git a/src/audtool/handlers_playback.c b/src/audtool/handlers_playback.c
index 754169e..4e7e17f 100644
--- a/src/audtool/handlers_playback.c
+++ b/src/audtool/handlers_playback.c
@@ -1,6 +1,7 @@
/*
* handlers_playback.c
- * Copyright 2005-2008 George Averill, William Pitcock, and Matti HÀmÀlÀinen
+ * Copyright 2005-2013 George Averill, William Pitcock, Matti HÀmÀlÀinen, and
+ * John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -18,111 +19,85 @@
*/
#include <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include <locale.h>
-#include "libaudclient/audctrl.h"
+
#include "audtool.h"
-void playback_play(gint argc, gchar **argv)
+void playback_play (int argc, char * * argv)
{
- audacious_remote_play(dbus_proxy);
+ obj_audacious_call_play_sync (dbus_proxy, NULL, NULL);
}
-void playback_pause(gint argc, gchar **argv)
+void playback_pause (int argc, char * * argv)
{
- audacious_remote_pause(dbus_proxy);
+ obj_audacious_call_pause_sync (dbus_proxy, NULL, NULL);
}
-void playback_playpause(gint argc, gchar **argv)
+void playback_playpause (int argc, char * * argv)
{
- if (audacious_remote_is_playing(dbus_proxy))
- {
- audacious_remote_pause(dbus_proxy);
- }
- else
- {
- audacious_remote_play(dbus_proxy);
- }
+ obj_audacious_call_play_pause_sync (dbus_proxy, NULL, NULL);
}
-void playback_stop(gint argc, gchar **argv)
+void playback_stop (int argc, char * * argv)
{
- audacious_remote_stop(dbus_proxy);
+ obj_audacious_call_stop_sync (dbus_proxy, NULL, NULL);
}
-void playback_playing(gint argc, gchar **argv)
+void playback_playing (int argc, char * * argv)
{
- if (!audacious_remote_is_paused(dbus_proxy))
- {
- exit(!audacious_remote_is_playing(dbus_proxy));
- }
- else
- {
- exit(1);
- }
-}
+ gboolean playing = FALSE;
+ obj_audacious_call_playing_sync (dbus_proxy, & playing, NULL, NULL);
-void playback_paused(gint argc, gchar **argv)
-{
- exit(!audacious_remote_is_paused(dbus_proxy));
+ exit (! playing);
}
-void playback_stopped(gint argc, gchar **argv)
+void playback_paused (int argc, char * * argv)
{
- if (!audacious_remote_is_playing(dbus_proxy) && !audacious_remote_is_paused(dbus_proxy))
- {
- exit(0);
- }
- else
- {
- exit(1);
- }
+ gboolean paused = FALSE;
+ obj_audacious_call_paused_sync (dbus_proxy, & paused, NULL, NULL);
+
+ exit (! paused);
}
-void playback_status(gint argc, gchar **argv)
+void playback_stopped (int argc, char * * argv)
{
- if (audacious_remote_is_paused(dbus_proxy))
- {
- audtool_report("paused");
- return;
- }
- else if (audacious_remote_is_playing(dbus_proxy))
- {
- audtool_report("playing");
- return;
- }
- else
- {
- audtool_report("stopped");
- return;
- }
+ gboolean stopped = FALSE;
+ obj_audacious_call_stopped_sync (dbus_proxy, & stopped, NULL, NULL);
+
+ exit (! stopped);
}
-void playback_seek(gint argc, gchar **argv)
+void playback_status (int argc, char * * argv)
{
- if (argc < 2)
- {
- audtool_whine_args(argv[0], "<position>");
- exit(1);
- }
+ char * status = NULL;
+ obj_audacious_call_status_sync (dbus_proxy, & status, NULL, NULL);
+
+ if (! status)
+ exit (1);
- audacious_remote_jump_to_time(dbus_proxy, atof(argv[1]) * 1000);
+ audtool_report ("%s", status);
+ g_free (status);
}
-void playback_seek_relative(gint argc, gchar **argv)
+void playback_seek (int argc, char * * argv)
{
- gint oldtime, newtime, diff;
+ if (argc < 2)
+ {
+ audtool_whine_args (argv[0], "<position>");
+ exit (1);
+ }
- if (argc < 2)
- {
- audtool_whine_args(argv[0], "<position>");
- exit(1);
- }
+ obj_audacious_call_seek_sync (dbus_proxy, atof (argv[1]) * 1000, NULL, NULL);
+}
- oldtime = audacious_remote_get_output_time(dbus_proxy);
- diff = atof(argv[1]) * 1000;
- newtime = oldtime + diff;
+void playback_seek_relative (int argc, char * * argv)
+{
+ if (argc < 2)
+ {
+ audtool_whine_args (argv[0], "<position>");
+ exit (1);
+ }
- audacious_remote_jump_to_time(dbus_proxy, newtime);
+ unsigned oldtime = 0;
+ obj_audacious_call_time_sync (dbus_proxy, & oldtime, NULL, NULL);
+ obj_audacious_call_seek_sync (dbus_proxy, oldtime + atof (argv[1]) * 1000, NULL, NULL);
}
diff --git a/src/audtool/handlers_playlist.c b/src/audtool/handlers_playlist.c
index b834bd7..21e8f64 100644
--- a/src/audtool/handlers_playlist.c
+++ b/src/audtool/handlers_playlist.c
@@ -1,6 +1,6 @@
/*
* handlers_playlist.c
- * Copyright 2005-2011 George Averill, William Pitcock, Yoshiki Yazawa,
+ * Copyright 2005-2013 George Averill, William Pitcock, Yoshiki Yazawa,
* Matti HÀmÀlÀinen, and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
@@ -20,362 +20,347 @@
#include <stdlib.h>
#include <string.h>
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <locale.h>
-#include "libaudclient/audctrl.h"
+
#include "audtool.h"
+#include "wrappers.h"
-void playlist_reverse(gint argc, gchar **argv)
+void playlist_reverse (int argc, char * * argv)
{
- audacious_remote_playlist_prev(dbus_proxy);
+ obj_audacious_call_reverse_sync (dbus_proxy, NULL, NULL);
}
-void playlist_advance(gint argc, gchar **argv)
+void playlist_advance (int argc, char * * argv)
{
- audacious_remote_playlist_next(dbus_proxy);
+ obj_audacious_call_advance_sync (dbus_proxy, NULL, NULL);
}
-void playlist_auto_advance_status(gint argc, gchar **argv)
+void playlist_auto_advance_status (int argc, char * * argv)
{
- if (audacious_remote_is_advance(dbus_proxy))
- {
- audtool_report("on");
- }
- else
- {
- audtool_report("off");
- }
+ gboolean advance = FALSE;
+ obj_audacious_call_auto_advance_sync (dbus_proxy, & advance, NULL, NULL);
+ audtool_report (advance ? "on" : "off");
}
-void playlist_auto_advance_toggle(gint argc, gchar **argv)
+void playlist_auto_advance_toggle (int argc, char * * argv)
{
- audacious_remote_toggle_advance(dbus_proxy);
+ obj_audacious_call_toggle_auto_advance_sync (dbus_proxy, NULL, NULL);
}
-void playlist_stop_after_status (gint argc, gchar * * argv)
+void playlist_stop_after_status (int argc, char * * argv)
{
- audtool_report (audacious_remote_is_stop_after (dbus_proxy) ? "on" : "off");
+ gboolean stop_after = FALSE;
+ obj_audacious_call_stop_after_sync (dbus_proxy, & stop_after, NULL, NULL);
+ audtool_report (stop_after ? "on" : "off");
}
-void playlist_stop_after_toggle (gint argc, gchar * * argv)
+void playlist_stop_after_toggle (int argc, char * * argv)
{
- audacious_remote_toggle_stop_after (dbus_proxy);
+ obj_audacious_call_toggle_stop_after_sync (dbus_proxy, NULL, NULL);
}
-gint check_args_playlist_pos(gint argc, gchar **argv)
+int check_args_playlist_pos (int argc, char * * argv)
{
- gint playpos;
-
- if (argc < 2)
- {
- audtool_whine_args(argv[0], "<position>");
- exit(1);
- }
-
- playpos = atoi(argv[1]);
+ int pos;
- if (playpos < 1 || playpos > audacious_remote_get_playlist_length(dbus_proxy))
- {
- audtool_whine("invalid playlist position %d ('%s')\n", playpos, argv[1]);
- exit(2);
- }
+ if (argc < 2 || (pos = atoi (argv[1])) < 1)
+ {
+ audtool_whine_args (argv[0], "<position>");
+ exit (1);
+ }
- return playpos;
+ return pos;
}
-
-static gchar * construct_uri(gchar *string)
+static char * construct_uri (char * string)
{
- gchar *filename = g_strdup(string);
- gchar *tmp, *path;
- gchar *uri = NULL;
+ char * filename = g_strdup (string);
+ char * tmp, * path;
+ char * uri = NULL;
// case 1: filename is raw full path or uri
- if (filename[0] == '/' || strstr(filename, "://")) {
- uri = g_filename_to_uri(filename, NULL, NULL);
- if(!uri) {
- uri = g_strdup(filename);
- }
- g_free(filename);
+ if (filename[0] == '/' || strstr (filename, "://"))
+ {
+ uri = g_filename_to_uri (filename, NULL, NULL);
+
+ if (! uri)
+ uri = g_strdup (filename);
+
+ g_free (filename);
}
// case 2: filename is not raw full path nor uri.
// make full path with pwd. (using g_build_filename)
- else {
+ else
+ {
path = g_get_current_dir();
- tmp = g_build_filename(path, filename, NULL);
- g_free(path); g_free(filename);
- uri = g_filename_to_uri(tmp, NULL, NULL);
- g_free(tmp);
+ tmp = g_build_filename (path, filename, NULL);
+ g_free (path);
+ g_free (filename);
+ uri = g_filename_to_uri (tmp, NULL, NULL);
+ g_free (tmp);
}
return uri;
}
-void playlist_add_url_string(gint argc, gchar **argv)
+void playlist_add_url_string (int argc, char * * argv)
{
- gchar *uri;
-
- if (argc < 2)
- {
- audtool_whine_args(argv[0], "<url>");
- exit(1);
- }
+ char * uri;
- uri = construct_uri(argv[1]);
- if (uri) {
- audacious_remote_playlist_add_url_string(dbus_proxy, uri);
+ if (argc < 2)
+ {
+ audtool_whine_args (argv[0], "<url>");
+ exit (1);
}
- g_free(uri);
+
+ uri = construct_uri (argv[1]);
+
+ if (! uri)
+ exit (1);
+
+ obj_audacious_call_add_sync (dbus_proxy, uri, NULL, NULL);
+ g_free (uri);
}
-void playlist_delete(gint argc, gchar **argv)
+void playlist_delete (int argc, char * * argv)
{
- gint playpos = check_args_playlist_pos(argc, argv);
- audacious_remote_playlist_delete(dbus_proxy, playpos - 1);
+ int pos = check_args_playlist_pos (argc, argv);
+ obj_audacious_call_delete_sync (dbus_proxy, pos - 1, NULL, NULL);
}
-void playlist_length(gint argc, gchar **argv)
+void playlist_length (int argc, char * * argv)
{
- gint i;
-
- i = audacious_remote_get_playlist_length(dbus_proxy);
+ audtool_report ("%d", get_playlist_length ());
+}
- audtool_report("%d", i);
+void playlist_song (int argc, char * * argv)
+{
+ int pos = check_args_playlist_pos (argc, argv);
+ char * title = get_entry_title (pos - 1);
+ audtool_report ("%s", title);
+ g_free (title);
}
-void playlist_song(gint argc, gchar **argv)
+void number_of_playlists (int argc, char * * argv)
{
- gint playpos = check_args_playlist_pos(argc, argv);
- gchar *song;
+ int playlists = 0;
+ obj_audacious_call_number_of_playlists_sync (dbus_proxy, & playlists, NULL, NULL);
+ audtool_report ("%d", playlists);
+}
- song = audacious_remote_get_playlist_title(dbus_proxy, playpos - 1);
- audtool_report("%s", song);
+void current_playlist (int argc, char * * argv)
+{
+ int playlist = -1;
+ obj_audacious_call_get_active_playlist_sync (dbus_proxy, & playlist, NULL, NULL);
+ audtool_report ("%d", playlist + 1);
}
-void playlist_title(gint argc, gchar **argv)
+void set_current_playlist (int argc, char * * argv)
{
- gchar *title;
+ if (argc < 2)
+ {
+ audtool_whine_args (argv[0], "<number>");
+ exit (1);
+ }
- title = audacious_remote_playlist_get_active_name(dbus_proxy);
- audtool_report("%s", title);
+ obj_audacious_call_set_active_playlist_sync (dbus_proxy, atoi (argv[1]) - 1, NULL, NULL);
}
-void playlist_song_length(gint argc, gchar **argv)
+void playlist_title (int argc, char * * argv)
{
- gint playpos = check_args_playlist_pos(argc, argv);
- gint frames, length;
+ char * title = NULL;
+ obj_audacious_call_get_active_playlist_name_sync (dbus_proxy, & title, NULL, NULL);
- frames = audacious_remote_get_playlist_time(dbus_proxy, playpos - 1);
- length = frames / 1000;
+ if (! title)
+ exit (1);
- audtool_report("%d:%.2d", length / 60, length % 60);
+ audtool_report ("%s", title);
+ g_free (title);
}
-void playlist_song_length_seconds(gint argc, gchar **argv)
+void set_playlist_title (int argc, char * * argv)
{
- gint playpos = check_args_playlist_pos(argc, argv);
- gint frames, length;
+ if (argc < 2)
+ {
+ audtool_whine_args (argv[0], "<title>");
+ exit (1);
+ }
- frames = audacious_remote_get_playlist_time(dbus_proxy, playpos - 1);
- length = frames / 1000;
+ obj_audacious_call_set_active_playlist_name_sync (dbus_proxy, argv[1], NULL, NULL);
+}
- audtool_report("%d", length);
+void new_playlist (int argc, char * * argv)
+{
+ obj_audacious_call_new_playlist_sync (dbus_proxy, NULL, NULL);
}
-void playlist_song_length_frames(gint argc, gchar **argv)
+void delete_current_playlist (int argc, char * * argv)
{
- gint playpos = check_args_playlist_pos(argc, argv);
- gint frames;
+ obj_audacious_call_delete_active_playlist_sync (dbus_proxy, NULL, NULL);
+}
- frames = audacious_remote_get_playlist_time(dbus_proxy, playpos - 1);
+void play_current_playlist (int argc, char * * argv)
+{
+ obj_audacious_call_play_active_playlist_sync (dbus_proxy, NULL, NULL);
+}
- audtool_report("%d", frames);
+void playlist_song_length (int argc, char * * argv)
+{
+ int pos = check_args_playlist_pos (argc, argv);
+ int length = get_entry_length (pos - 1) / 1000;
+ audtool_report ("%d:%.2d", length / 60, length % 60);
}
-void playlist_display(gint argc, gchar **argv)
+void playlist_song_length_seconds (int argc, char * * argv)
{
- gint i, ii, frames, length, total;
- gchar *songname;
- gchar *fmt = NULL, *p;
- gint column;
+ int pos = check_args_playlist_pos (argc, argv);
+ int length = get_entry_length (pos - 1) / 1000;
+ audtool_report ("%d", length);
+}
- i = audacious_remote_get_playlist_length(dbus_proxy);
+void playlist_song_length_frames (int argc, char * * argv)
+{
+ int pos = check_args_playlist_pos (argc, argv);
+ int length = get_entry_length (pos - 1);
+ audtool_report ("%d", length);
+}
- audtool_report("%d track%s.", i, i != 1 ? "s" : "");
+void playlist_display (int argc, char * * argv)
+{
+ int entries = get_playlist_length ();
- total = 0;
+ audtool_report ("%d track%s.", entries, entries != 1 ? "s" : "");
- for (ii = 0; ii < i; ii++)
- {
- songname = audacious_remote_get_playlist_title(dbus_proxy, ii);
- frames = audacious_remote_get_playlist_time(dbus_proxy, ii);
- length = frames / 1000;
- total += length;
+ int total = 0;
- /* adjust width for multi byte characters */
- column = 60;
- if(songname){
- p = songname;
- while(*p){
- gint stride;
- stride = g_utf8_next_char(p) - p;
- if(g_unichar_iswide(g_utf8_get_char(p))
- || g_unichar_iswide_cjk(g_utf8_get_char(p))
- ){
- column += (stride - 2);
- }
- else {
- column += (stride - 1);
- }
- p = g_utf8_next_char(p);
- }
+ for (int entry = 0; entry < entries; entry ++)
+ {
+ char * title = get_entry_title (entry);
+ int length = get_entry_length (entry) / 1000;
- }
+ total += length;
- fmt = g_strdup_printf("%%4d | %%-%ds | %%d:%%.2d", column);
- audtool_report(fmt, ii + 1, songname, length / 60, length % 60);
- g_free(fmt);
- }
+ /* adjust width for multi byte characters */
+ int column = 60;
- audtool_report("Total length: %d:%.2d", total / 60, total % 60);
-}
+ for (const char * p = title; * p; p = g_utf8_next_char (p))
+ {
+ int stride = g_utf8_next_char (p) - p;
-void playlist_position(gint argc, gchar **argv)
-{
- gint i;
+ if (g_unichar_iswide (g_utf8_get_char (p)) ||
+ g_unichar_iswide_cjk (g_utf8_get_char (p)))
+ column += (stride - 2);
+ else
+ column += (stride - 1);
+ }
- i = audacious_remote_get_playlist_pos(dbus_proxy);
+ char * fmt = g_strdup_printf ("%%4d | %%-%ds | %%d:%%.2d", column);
+ audtool_report (fmt, entry + 1, title, length / 60, length % 60);
- audtool_report("%d", i + 1);
+ g_free (fmt);
+ g_free (title);
+ }
+
+ audtool_report ("Total length: %d:%.2d", total / 60, total % 60);
}
-void playlist_song_filename (gint argc, gchar * * argv)
+void playlist_position (int argc, char * * argv)
{
- gint playpos = check_args_playlist_pos (argc, argv);
- gchar * uri, * filename;
-
- uri = audacious_remote_get_playlist_file (dbus_proxy, playpos - 1);
- filename = (uri != NULL) ? g_filename_from_uri (uri, NULL, NULL) : NULL;
-
- audtool_report ("%s", (filename != NULL) ? filename : (uri != NULL) ? uri :
- _("Position not found."));
+ audtool_report ("%d", get_current_entry () + 1);
+}
- g_free (uri);
+void playlist_song_filename (int argc, char * * argv)
+{
+ int pos = check_args_playlist_pos (argc, argv);
+ char * filename = get_entry_filename (pos - 1);
+ audtool_report ("%s", filename);
g_free (filename);
}
-void playlist_jump(gint argc, gchar **argv)
+void playlist_jump (int argc, char * * argv)
{
- gint playpos = check_args_playlist_pos(argc, argv);
-
- audacious_remote_set_playlist_pos(dbus_proxy, playpos - 1);
+ int pos = check_args_playlist_pos (argc, argv);
+ obj_audacious_call_jump_sync (dbus_proxy, pos - 1, NULL, NULL);
}
-void playlist_clear(gint argc, gchar **argv)
+void playlist_clear (int argc, char * * argv)
{
- audacious_remote_stop(dbus_proxy);
- audacious_remote_playlist_clear(dbus_proxy);
+ obj_audacious_call_clear_sync (dbus_proxy, NULL, NULL);
}
-void playlist_repeat_status(gint argc, gchar **argv)
+void playlist_repeat_status (int argc, char * * argv)
{
- if (audacious_remote_is_repeat(dbus_proxy))
- {
- audtool_report("on");
- }
- else
- {
- audtool_report("off");
- }
+ gboolean repeat = FALSE;
+ obj_audacious_call_repeat_sync (dbus_proxy, & repeat, NULL, NULL);
+ audtool_report (repeat ? "on" : "off");
}
-void playlist_repeat_toggle(gint argc, gchar **argv)
+void playlist_repeat_toggle (int argc, char * * argv)
{
- audacious_remote_toggle_repeat(dbus_proxy);
+ obj_audacious_call_toggle_repeat_sync (dbus_proxy, NULL, NULL);
}
-void playlist_shuffle_status(gint argc, gchar **argv)
+void playlist_shuffle_status (int argc, char * * argv)
{
- if (audacious_remote_is_shuffle(dbus_proxy))
- {
- audtool_report("on");
- }
- else
- {
- audtool_report("off");
- }
+ gboolean shuffle = FALSE;
+ obj_audacious_call_shuffle_sync (dbus_proxy, & shuffle, NULL, NULL);
+ audtool_report (shuffle ? "on" : "off");
}
-void playlist_shuffle_toggle(gint argc, gchar **argv)
+void playlist_shuffle_toggle (int argc, char * * argv)
{
- audacious_remote_toggle_shuffle(dbus_proxy);
+ obj_audacious_call_toggle_shuffle_sync (dbus_proxy, NULL, NULL);
}
-void playlist_tuple_field_data(gint argc, gchar **argv)
+void playlist_tuple_field_data (int argc, char * * argv)
{
- gint i;
- gchar *data;
-
- if (argc < 3)
- {
- audtool_whine_args(argv[0], "<fieldname> <position>");
- audtool_whine_tuple_fields();
- exit(1);
- }
+ int pos;
- i = atoi(argv[2]);
-
- if (i < 1 || i > audacious_remote_get_playlist_length(dbus_proxy))
- {
- audtool_whine("invalid playlist position %d\n", i);
- exit(1);
- }
-
- if (!(data = audacious_get_tuple_field_data(dbus_proxy, argv[1], i - 1)))
- {
- return;
- }
-
- audtool_report("%s", data);
+ if (argc < 3 || (pos = atoi (argv[2])) < 1)
+ {
+ audtool_whine_args (argv[0], "<fieldname> <position>");
+ audtool_whine_tuple_fields ();
+ exit (1);
+ }
- g_free(data);
+ char * str = get_entry_field (pos - 1, argv[1]);
+ audtool_report ("%s", str);
+ g_free (str);
}
-void playlist_ins_url_string(gint argc, gchar **argv)
+void playlist_ins_url_string (int argc, char * * argv)
{
- gint pos = -1;
- gchar *uri;
+ int pos;
- if (argc < 3)
+ if (argc < 3 || (pos = atoi (argv[2])) < 1)
{
- audtool_whine_args(argv[0], "<url> <position>");
- exit(1);
+ audtool_whine_args (argv[0], "<url> <position>");
+ exit (1);
}
- pos = atoi(argv[2]) - 1;
- if(pos >= 0) {
- uri = construct_uri(argv[1]);
- if (uri) {
- audacious_remote_playlist_ins_url_string(dbus_proxy, uri, pos);
- }
- g_free(uri);
- }
+ char * uri = construct_uri (argv[1]);
+
+ if (! uri)
+ exit (1);
+
+ obj_audacious_call_playlist_ins_url_string_sync (dbus_proxy, uri, pos - 1, NULL, NULL);
+
+ g_free (uri);
}
-void playlist_enqueue_to_temp(gint argc, gchar **argv)
+void playlist_enqueue_to_temp (int argc, char * * argv)
{
- gchar *uri;
-
if (argc < 2)
{
- audtool_whine_args(argv[0], "<url>");
- exit(1);
+ audtool_whine_args (argv[0], "<url>");
+ exit (1);
}
- uri = construct_uri(argv[1]);
- if (uri) {
- audacious_remote_playlist_enqueue_to_temp(dbus_proxy, uri);
- }
- g_free(uri);
+ char * uri = construct_uri (argv[1]);
+
+ if (! uri)
+ exit (1);
+
+ obj_audacious_call_playlist_enqueue_to_temp_sync (dbus_proxy, uri, NULL, NULL);
+
+ g_free (uri);
}
diff --git a/src/audtool/handlers_playqueue.c b/src/audtool/handlers_playqueue.c
index 275404e..dd5e509 100644
--- a/src/audtool/handlers_playqueue.c
+++ b/src/audtool/handlers_playqueue.c
@@ -1,7 +1,7 @@
/*
* handlers_playqueue.c
- * Copyright 2005-2008 George Averill, William Pitcock, Yoshiki Yazawa, and
- * Matti HÀmÀlÀinen
+ * Copyright 2005-2013 George Averill, William Pitcock, Yoshiki Yazawa,
+ * Matti HÀmÀlÀinen, and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -19,122 +19,85 @@
*/
#include <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include <locale.h>
-#include "libaudclient/audctrl.h"
+
#include "audtool.h"
+#include "wrappers.h"
-void playqueue_add(gint argc, gchar **argv)
+void playqueue_add (int argc, char * * argv)
{
- gint i = check_args_playlist_pos(argc, argv);
-
- if (!(audacious_remote_playqueue_is_queued(dbus_proxy, i - 1)))
- audacious_remote_playqueue_add(dbus_proxy, i - 1);
+ int pos = check_args_playlist_pos (argc, argv);
+ obj_audacious_call_playqueue_add_sync (dbus_proxy, pos - 1, NULL, NULL);
}
-void playqueue_remove(gint argc, gchar **argv)
+void playqueue_remove (int argc, char * * argv)
{
- gint i = check_args_playlist_pos(argc, argv);
+ int pos = check_args_playlist_pos (argc, argv);
+ obj_audacious_call_playqueue_remove_sync (dbus_proxy, pos - 1, NULL, NULL);
+}
- if (audacious_remote_playqueue_is_queued(dbus_proxy, i - 1))
- audacious_remote_playqueue_remove(dbus_proxy, i - 1);
+void playqueue_is_queued (int argc, char * * argv)
+{
+ int pos = check_args_playlist_pos (argc, argv);
+ find_in_queue (pos - 1); /* exits if not found */
+ audtool_report ("OK");
}
-void playqueue_is_queued(gint argc, gchar **argv)
+void playqueue_get_queue_position (int argc, char * * argv)
{
- gint i = check_args_playlist_pos(argc, argv);
+ int pos = check_args_playlist_pos (argc, argv);
+ audtool_report ("%d", find_in_queue (pos - 1) + 1);
+}
- if (audacious_remote_playqueue_is_queued(dbus_proxy, i - 1)) {
- audtool_report("OK");
- exit(0);
- }
- else
- exit(1);
+void playqueue_get_list_position (int argc, char * * argv)
+{
+ int qpos = check_args_playlist_pos (argc, argv);
+ audtool_report ("%d", get_queue_entry (qpos - 1) + 1);
}
-void playqueue_get_queue_position(gint argc, gchar **argv)
+void playqueue_display (int argc, char * * argv)
{
- gint pos, i = check_args_playlist_pos(argc, argv);
+ int qlength = get_queue_length ();
- pos = audacious_remote_get_playqueue_queue_position(dbus_proxy, i - 1) + 1;
+ audtool_report ("%d queued tracks.", qlength);
- if (pos < 1)
- return;
+ int total = 0;
- audtool_report("%d", pos);
-}
+ for (int qpos = 0; qpos < qlength; qpos ++)
+ {
+ int pos = get_queue_entry (qpos);
+ char * title = get_entry_title (pos);
+ int length = get_entry_length (pos) / 1000;
-void playqueue_get_list_position(gint argc, gchar **argv)
-{
- gint pos, i = check_args_playlist_pos(argc, argv);
+ total += length;
- pos = audacious_remote_get_playqueue_list_position(dbus_proxy, i - 1) + 1;
+ /* adjust width for multi byte characters */
+ int column = 60;
- if (pos < 1)
- return;
+ for (const char * p = title; * p; p = g_utf8_next_char (p))
+ {
+ int stride = g_utf8_next_char (p) - p;
- audtool_report("%d", pos);
-}
+ if (g_unichar_iswide (g_utf8_get_char (p)) ||
+ g_unichar_iswide_cjk (g_utf8_get_char (p)))
+ column += (stride - 2);
+ else
+ column += (stride - 1);
+ }
-void playqueue_display(gint argc, gchar **argv)
-{
- gint i, ii, position, frames, length, total;
- gchar *songname;
- gchar *fmt = NULL, *p;
- gint column;
-
- i = audacious_remote_get_playqueue_length(dbus_proxy);
-
- audtool_report("%d queued tracks.", i);
-
- total = 0;
-
- for (ii = 0; ii < i; ii++)
- {
- position = audacious_remote_get_playqueue_list_position(dbus_proxy, ii);
- songname = audacious_remote_get_playlist_title(dbus_proxy, position);
- frames = audacious_remote_get_playlist_time(dbus_proxy, position);
- length = frames / 1000;
- total += length;
-
- /* adjust width for multi byte characters */
- column = 60;
- if(songname) {
- p = songname;
- while(*p){
- gint stride;
- stride = g_utf8_next_char(p) - p;
- if(g_unichar_iswide(g_utf8_get_char(p))
- || g_unichar_iswide_cjk(g_utf8_get_char(p))
- ){
- column += (stride - 2);
- }
- else {
- column += (stride - 1);
- }
- p = g_utf8_next_char(p);
- }
- }
-
- fmt = g_strdup_printf("%%4d | %%4d | %%-%ds | %%d:%%.2d", column);
- audtool_report(fmt, ii + 1, position + 1, songname, length / 60, length % 60);
- g_free(fmt);
- }
-
- audtool_report("Total length: %d:%.2d", total / 60, total % 60);
+ char * fmt = g_strdup_printf ("%%4d | %%4d | %%-%ds | %%d:%%.2d", column);
+ audtool_report (fmt, qpos + 1, pos + 1, title, length / 60, length % 60);
+ g_free (fmt);
+ }
+
+ audtool_report ("Total length: %d:%.2d", total / 60, total % 60);
}
-void playqueue_length(gint argc, gchar **argv)
+void playqueue_length (int argc, char * * argv)
{
- gint i;
-
- i = audacious_remote_get_playqueue_length(dbus_proxy);
-
- audtool_report("%d", i);
+ audtool_report ("%d", get_queue_length ());
}
-void playqueue_clear(gint argc, gchar **argv)
+void playqueue_clear (int argc, char * * argv)
{
- audacious_remote_playqueue_clear(dbus_proxy);
+ obj_audacious_call_playqueue_clear_sync (dbus_proxy, NULL, NULL);
}
diff --git a/src/audtool/handlers_vitals.c b/src/audtool/handlers_vitals.c
index f2325b4..68918dc 100644
--- a/src/audtool/handlers_vitals.c
+++ b/src/audtool/handlers_vitals.c
@@ -1,6 +1,6 @@
/*
* handlers_vitals.c
- * Copyright 2005-2007 George Averill and William Pitcock
+ * Copyright 2005-2013 George Averill, William Pitcock, and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -18,162 +18,112 @@
*/
#include <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <locale.h>
-#include "libaudclient/audctrl.h"
+
#include "audtool.h"
+#include "wrappers.h"
-void get_current_song(gint argc, gchar **argv)
+void get_current_song (int argc, char * * argv)
{
- gint playpos = audacious_remote_get_playlist_pos(dbus_proxy);
- gchar *song = audacious_remote_get_playlist_title(dbus_proxy, playpos);
-
- if (!song)
- {
- audtool_report("No song playing.");
- return;
- }
-
- audtool_report("%s", song);
+ char * title = get_entry_title (get_current_entry ());
+ audtool_report ("%s", title);
+ g_free (title);
}
-void get_current_song_filename (gint argc, gchar * * argv)
+void get_current_song_filename (int argc, char * * argv)
{
- gint playpos = audacious_remote_get_playlist_pos (dbus_proxy);
- gchar * uri, * filename;
-
- uri = audacious_remote_get_playlist_file (dbus_proxy, playpos);
- filename = (uri != NULL) ? g_filename_from_uri (uri, NULL, NULL) : NULL;
-
- audtool_report ("%s", (filename != NULL) ? filename : (uri != NULL) ? uri :
- _("No song playing."));
-
- g_free (uri);
+ char * filename = get_entry_filename (get_current_entry ());
+ audtool_report ("%s", filename);
g_free (filename);
}
-void get_current_song_output_length(gint argc, gchar **argv)
+void get_current_song_output_length (int argc, char * * argv)
{
- gint frames = audacious_remote_get_output_time(dbus_proxy);
- gint length = frames / 1000;
-
- audtool_report("%d:%.2d", length / 60, length % 60);
+ int time = get_current_time () / 1000;
+ audtool_report ("%d:%.2d", time / 60, time % 60);
}
-void get_current_song_output_length_seconds(gint argc, gchar **argv)
+void get_current_song_output_length_seconds (int argc, char * * argv)
{
- gint frames = audacious_remote_get_output_time(dbus_proxy);
- gint length = frames / 1000;
-
- audtool_report("%d", length);
+ int time = get_current_time () / 1000;
+ audtool_report ("%d", time);
}
-void get_current_song_output_length_frames(gint argc, gchar **argv)
+void get_current_song_output_length_frames (int argc, char * * argv)
{
- gint frames = audacious_remote_get_output_time(dbus_proxy);
-
- audtool_report("%d", frames);
+ int time = get_current_time ();
+ audtool_report ("%d", time);
}
-void get_current_song_length(gint argc, gchar **argv)
+void get_current_song_length (int argc, char * * argv)
{
- gint playpos = audacious_remote_get_playlist_pos(dbus_proxy);
- gint frames = audacious_remote_get_playlist_time(dbus_proxy, playpos);
- gint length = frames / 1000;
-
- audtool_report("%d:%.2d", length / 60, length % 60);
+ int length = get_entry_length (get_current_entry ()) / 1000;
+ audtool_report ("%d:%.2d", length / 60, length % 60);
}
-void get_current_song_length_seconds(gint argc, gchar **argv)
+void get_current_song_length_seconds (int argc, char * * argv)
{
- gint playpos = audacious_remote_get_playlist_pos(dbus_proxy);
- gint frames = audacious_remote_get_playlist_time(dbus_proxy, playpos);
- gint length = frames / 1000;
-
- audtool_report("%d", length);
+ int length = get_entry_length (get_current_entry ()) / 1000;
+ audtool_report ("%d", length);
}
-void get_current_song_length_frames(gint argc, gchar **argv)
+void get_current_song_length_frames (int argc, char * * argv)
{
- gint playpos = audacious_remote_get_playlist_pos(dbus_proxy);
- gint frames = audacious_remote_get_playlist_time(dbus_proxy, playpos);
-
- audtool_report("%d", frames);
+ int length = get_entry_length (get_current_entry ());
+ audtool_report ("%d", length);
}
-void get_current_song_bitrate(gint argc, gchar **argv)
+void get_current_song_bitrate (int argc, char * * argv)
{
- gint rate, freq, nch;
-
- audacious_remote_get_info(dbus_proxy, &rate, &freq, &nch);
-
- audtool_report("%d", rate);
+ int bitrate;
+ get_current_info (& bitrate, NULL, NULL);
+ audtool_report ("%d", bitrate);
}
-void get_current_song_bitrate_kbps(gint argc, gchar **argv)
+void get_current_song_bitrate_kbps (int argc, char * * argv)
{
- gint rate, freq, nch;
-
- audacious_remote_get_info(dbus_proxy, &rate, &freq, &nch);
-
- audtool_report("%d", rate / 1000);
+ int bitrate;
+ get_current_info (& bitrate, NULL, NULL);
+ audtool_report ("%d", bitrate / 1000);
}
-void get_current_song_frequency(gint argc, gchar **argv)
+void get_current_song_frequency (int argc, char * * argv)
{
- gint rate, freq, nch;
-
- audacious_remote_get_info(dbus_proxy, &rate, &freq, &nch);
-
- audtool_report("%d", freq);
+ int samplerate;
+ get_current_info (NULL, & samplerate, NULL);
+ audtool_report ("%d", samplerate);
}
-void get_current_song_frequency_khz(gint argc, gchar **argv)
+void get_current_song_frequency_khz (int argc, char * * argv)
{
- gint rate, freq, nch;
-
- audacious_remote_get_info(dbus_proxy, &rate, &freq, &nch);
-
- audtool_report("%0.1f", (gfloat) freq / 1000);
+ int samplerate;
+ get_current_info (NULL, & samplerate, NULL);
+ audtool_report ("%d", samplerate / 1000);
}
-void get_current_song_channels(gint argc, gchar **argv)
+void get_current_song_channels (int argc, char * * argv)
{
- gint rate, freq, nch;
-
- audacious_remote_get_info(dbus_proxy, &rate, &freq, &nch);
-
- audtool_report("%d", nch);
+ int channels;
+ get_current_info (NULL, NULL, & channels);
+ audtool_report ("%d", channels);
}
-void get_current_song_tuple_field_data(gint argc, gchar **argv)
+void get_current_song_tuple_field_data (int argc, char * * argv)
{
- gchar *data;
-
- if (argc < 2)
- {
- audtool_whine_args(argv[0], "<fieldname>");
- audtool_whine_tuple_fields();
- exit(1);
- }
-
- if (!(data = audacious_get_tuple_field_data(dbus_proxy, argv[1], audacious_remote_get_playlist_pos(dbus_proxy))))
- {
- return;
- }
-
- audtool_report("%s", data);
-
- g_free(data);
+ if (argc < 2)
+ {
+ audtool_whine_args (argv[0], "<fieldname>");
+ audtool_whine_tuple_fields();
+ exit (1);
+ }
+
+ char * str = get_entry_field (get_current_entry (), argv[1]);
+ audtool_report ("%s", str);
+ g_free (str);
}
-void get_current_song_info(gint argc, gchar **argv)
+void get_current_song_info (int argc, char * * argv)
{
- gint rate, freq, nch;
-
- audacious_remote_get_info(dbus_proxy, &rate, &freq, &nch);
- audtool_report("rate = %d freq = %d nch = %d", rate, freq, nch);
+ int bitrate, samplerate, channels;
+ get_current_info (& bitrate, & samplerate, & channels);
+ audtool_report ("rate = %d freq = %d nch = %d", bitrate, samplerate, channels);
}
-
diff --git a/src/audtool/main.c b/src/audtool/main.c
index 0fc45f7..83c959f 100644
--- a/src/audtool/main.c
+++ b/src/audtool/main.c
@@ -1,6 +1,6 @@
/*
* main.c
- * Copyright 2005-2011 George Averill, William Pitcock, Yoshiki Yazawa, and
+ * Copyright 2005-2013 George Averill, William Pitcock, Yoshiki Yazawa, and
* John Lindgren
*
* Redistribution and use in source and binary forms, with or without
@@ -20,89 +20,93 @@
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#include <glib.h>
#include <locale.h>
-#include "libaudclient/audctrl.h"
+
#include "audtool.h"
-struct commandhandler handlers[] = {
- {"<sep>", NULL, "Vital information", 0},
- {"current-song", get_current_song, "returns current song title", 0},
- {"current-song-filename", get_current_song_filename, "returns current song filename", 0},
- {"current-song-length", get_current_song_length, "returns current song length", 0},
- {"current-song-length-seconds", get_current_song_length_seconds, "returns current song length in seconds", 0},
- {"current-song-length-frames", get_current_song_length_frames, "returns current song length in frames", 0},
- {"current-song-output-length", get_current_song_output_length, "returns current song output length", 0},
- {"current-song-output-length-seconds", get_current_song_output_length_seconds, "returns current song output length in seconds", 0},
- {"current-song-output-length-frames", get_current_song_output_length_frames, "returns current song output length in frames", 0},
- {"current-song-bitrate", get_current_song_bitrate, "returns current song bitrate in bits per second", 0},
- {"current-song-bitrate-kbps", get_current_song_bitrate_kbps, "returns current song bitrate in kilobits per second", 0},
- {"current-song-frequency", get_current_song_frequency, "returns current song frequency in hertz", 0},
- {"current-song-frequency-khz", get_current_song_frequency_khz, "returns current song frequency in kilohertz", 0},
- {"current-song-channels", get_current_song_channels, "returns current song channels", 0},
- {"current-song-tuple-data", get_current_song_tuple_field_data, "returns the value of a tuple field for the current song", 1},
+const struct commandhandler handlers[] =
+{
+ {"<sep>", NULL, "Vital information", 0},
+ {"current-song", get_current_song, "returns current song title", 0},
+ {"current-song-filename", get_current_song_filename, "returns current song filename", 0},
+ {"current-song-length", get_current_song_length, "returns current song length", 0},
+ {"current-song-length-seconds", get_current_song_length_seconds, "returns current song length in seconds", 0},
+ {"current-song-length-frames", get_current_song_length_frames, "returns current song length in frames", 0},
+ {"current-song-output-length", get_current_song_output_length, "returns current song output length", 0},
+ {"current-song-output-length-seconds", get_current_song_output_length_seconds, "returns current song output length in seconds", 0},
+ {"current-song-output-length-frames", get_current_song_output_length_frames, "returns current song output length in frames", 0},
+ {"current-song-bitrate", get_current_song_bitrate, "returns current song bitrate in bits per second", 0},
+ {"current-song-bitrate-kbps", get_current_song_bitrate_kbps, "returns current song bitrate in kilobits per second", 0},
+ {"current-song-frequency", get_current_song_frequency, "returns current song frequency in hertz", 0},
+ {"current-song-frequency-khz", get_current_song_frequency_khz, "returns current song frequency in kilohertz", 0},
+ {"current-song-channels", get_current_song_channels, "returns current song channels", 0},
+ {"current-song-tuple-data", get_current_song_tuple_field_data, "returns the value of a tuple field for the current song", 1},
{"current-song-info", get_current_song_info, "returns current song bitrate, frequency and channels", 0},
-
- {"<sep>", NULL, "Playlist manipulation", 0},
- {"playlist-advance", playlist_advance, "go to the next song in the playlist", 0},
- {"playlist-auto-advance-status", playlist_auto_advance_status, "returns the status of playlist auto-advance", 0},
- {"playlist-auto-advance-toggle", playlist_auto_advance_toggle, "toggles playlist auto-advance", 0},
- {"playlist-reverse", playlist_reverse, "go to the previous song in the playlist", 0},
- {"playlist-addurl", playlist_add_url_string, "adds a URL to the playlist", 1},
+ {"<sep>", NULL, "Playlist manipulation", 0},
+ {"playlist-advance", playlist_advance, "go to the next song in the playlist", 0},
+ {"playlist-auto-advance-status", playlist_auto_advance_status, "returns the status of playlist auto-advance", 0},
+ {"playlist-auto-advance-toggle", playlist_auto_advance_toggle, "toggles playlist auto-advance", 0},
+ {"playlist-reverse", playlist_reverse, "go to the previous song in the playlist", 0},
+ {"playlist-addurl", playlist_add_url_string, "adds a URL to the playlist", 1},
{"playlist-insurl", playlist_ins_url_string, "inserts a URL at specified position in the playlist", 2},
- {"playlist-addurl-to-new-playlist", playlist_enqueue_to_temp, "adds a URL to the newly created playlist", 1},
- {"playlist-delete", playlist_delete, "deletes a song from the playlist", 1},
- {"playlist-length", playlist_length, "returns the total length of the playlist", 0},
- {"playlist-song", playlist_song, "returns the title of a song in the playlist", 1},
- {"playlist-song-filename", playlist_song_filename, "returns the filename of a song in the playlist", 1},
- {"playlist-song-length", playlist_song_length, "returns the length of a song in the playlist", 1},
- {"playlist-song-length-seconds", playlist_song_length_seconds, "returns the length of a song in the playlist in seconds", 1},
- {"playlist-song-length-frames", playlist_song_length_frames, "returns the length of a song in the playlist in frames", 1},
- {"playlist-display", playlist_display, "returns the entire playlist", 0},
- {"playlist-position", playlist_position, "returns the position in the playlist", 0},
- {"playlist-jump", playlist_jump, "jumps to a position in the playlist", 1},
- {"playlist-clear", playlist_clear, "clears the playlist", 0},
- {"playlist-repeat-status", playlist_repeat_status, "returns the status of playlist repeat", 0},
- {"playlist-repeat-toggle", playlist_repeat_toggle, "toggles playlist repeat", 0},
- {"playlist-shuffle-status", playlist_shuffle_status, "returns the status of playlist shuffle", 0},
- {"playlist-shuffle-toggle", playlist_shuffle_toggle, "toggles playlist shuffle", 0},
- {"playlist-stop-after-status", playlist_stop_after_status, "queries if stopping after current song", 0},
- {"playlist-stop-after-toggle", playlist_stop_after_toggle, "toggles if stopping after current song", 0},
- {"playlist-tuple-data", playlist_tuple_field_data, "returns the value of a tuple field for a song in the playlist", 2},
- {"current-playlist-name", playlist_title, "returns the playlist title of the active playlist", 0},
-
- {"<sep>", NULL, "Playqueue manipulation", 0},
- {"playqueue-add", playqueue_add, "adds a song to the playqueue", 1},
- {"playqueue-remove", playqueue_remove, "removes a song from the playqueue", 1},
- {"playqueue-is-queued", playqueue_is_queued, "returns OK if a song is queued", 1},
- {"playqueue-get-queue-position", playqueue_get_queue_position, "returns the playqueue position of a song in the given position in the playlist", 1},
- {"playqueue-get-list-position", playqueue_get_list_position, "returns the playlist position of a song in the given position in the playqueue", 1},
- {"playqueue-length", playqueue_length, "returns the length of the playqueue", 0},
- {"playqueue-display", playqueue_display, "returns a list of currently-queued songs", 0},
- {"playqueue-clear", playqueue_clear, "clears the playqueue", 0},
-
-
- {"<sep>", NULL, "Playback manipulation", 0},
- {"playback-play", playback_play, "starts/unpauses song playback", 0},
- {"playback-pause", playback_pause, "(un)pauses song playback", 0},
- {"playback-playpause", playback_playpause, "plays/(un)pauses song playback", 0},
- {"playback-stop", playback_stop, "stops song playback", 0},
- {"playback-playing", playback_playing, "returns OK if Audacious is playing", 0},
- {"playback-paused", playback_paused, "returns OK if Audacious is paused", 0},
- {"playback-stopped", playback_stopped, "returns OK if Audacious is stopped", 0},
- {"playback-status", playback_status, "returns the playback status", 0},
- {"playback-seek", playback_seek, "performs an absolute seek", 1},
- {"playback-seek-relative", playback_seek_relative, "performs a seek relative to the current position", 1},
-
-
- {"<sep>", NULL, "Volume control", 0},
- {"get-volume", get_volume, "returns the current volume level in percent", 0},
- {"set-volume", set_volume, "sets the current volume level in percent", 1},
-
-
- {"<sep>", NULL, "Equalizer manipulation", 0},
+ {"playlist-addurl-to-new-playlist", playlist_enqueue_to_temp, "adds a URL to the newly created playlist", 1},
+ {"playlist-delete", playlist_delete, "deletes a song from the playlist", 1},
+ {"playlist-length", playlist_length, "returns the total length of the playlist", 0},
+ {"playlist-song", playlist_song, "returns the title of a song in the playlist", 1},
+ {"playlist-song-filename", playlist_song_filename, "returns the filename of a song in the playlist", 1},
+ {"playlist-song-length", playlist_song_length, "returns the length of a song in the playlist", 1},
+ {"playlist-song-length-seconds", playlist_song_length_seconds, "returns the length of a song in the playlist in seconds", 1},
+ {"playlist-song-length-frames", playlist_song_length_frames, "returns the length of a song in the playlist in frames", 1},
+ {"playlist-display", playlist_display, "returns the entire playlist", 0},
+ {"playlist-position", playlist_position, "returns the position in the playlist", 0},
+ {"playlist-jump", playlist_jump, "jumps to a position in the playlist", 1},
+ {"playlist-clear", playlist_clear, "clears the playlist", 0},
+ {"playlist-repeat-status", playlist_repeat_status, "returns the status of playlist repeat", 0},
+ {"playlist-repeat-toggle", playlist_repeat_toggle, "toggles playlist repeat", 0},
+ {"playlist-shuffle-status", playlist_shuffle_status, "returns the status of playlist shuffle", 0},
+ {"playlist-shuffle-toggle", playlist_shuffle_toggle, "toggles playlist shuffle", 0},
+ {"playlist-stop-after-status", playlist_stop_after_status, "queries if stopping after current song", 0},
+ {"playlist-stop-after-toggle", playlist_stop_after_toggle, "toggles if stopping after current song", 0},
+ {"playlist-tuple-data", playlist_tuple_field_data, "returns the value of a tuple field for a song in the playlist", 2},
+
+ {"<sep>", NULL, "More playlist manipulation", 0},
+ {"number-of-playlists", number_of_playlists, "returns the number of open playlists", 0},
+ {"current-playlist", current_playlist, "returns the number of the active playlist", 0},
+ {"set-current-playlist", set_current_playlist, "activates the specified playlist", 1},
+ {"current-playlist-name", playlist_title, "returns the title of the active playlist", 0},
+ {"set-current-playlist-name", set_playlist_title, "sets the title of the active playlist", 1},
+ {"new-playlist", new_playlist, "creates a new playlist", 0},
+ {"delete-current-playlist", delete_current_playlist, "removes the active playlist", 0},
+ {"play-current-playlist", play_current_playlist, "starts or resumes playing the active playlist", 0},
+
+ {"<sep>", NULL, "Playqueue manipulation", 0},
+ {"playqueue-add", playqueue_add, "adds a song to the playqueue", 1},
+ {"playqueue-remove", playqueue_remove, "removes a song from the playqueue", 1},
+ {"playqueue-is-queued", playqueue_is_queued, "returns OK if a song is queued", 1},
+ {"playqueue-get-queue-position", playqueue_get_queue_position, "returns the playqueue position of a song in the given position in the playlist", 1},
+ {"playqueue-get-list-position", playqueue_get_list_position, "returns the playlist position of a song in the given position in the playqueue", 1},
+ {"playqueue-length", playqueue_length, "returns the length of the playqueue", 0},
+ {"playqueue-display", playqueue_display, "returns a list of currently-queued songs", 0},
+ {"playqueue-clear", playqueue_clear, "clears the playqueue", 0},
+
+ {"<sep>", NULL, "Playback manipulation", 0},
+ {"playback-play", playback_play, "starts/unpauses song playback", 0},
+ {"playback-pause", playback_pause, "(un)pauses song playback", 0},
+ {"playback-playpause", playback_playpause, "plays/(un)pauses song playback", 0},
+ {"playback-stop", playback_stop, "stops song playback", 0},
+ {"playback-playing", playback_playing, "returns OK if Audacious is playing", 0},
+ {"playback-paused", playback_paused, "returns OK if Audacious is paused", 0},
+ {"playback-stopped", playback_stopped, "returns OK if Audacious is stopped", 0},
+ {"playback-status", playback_status, "returns the playback status", 0},
+ {"playback-seek", playback_seek, "performs an absolute seek", 1},
+ {"playback-seek-relative", playback_seek_relative, "performs a seek relative to the current position", 1},
+
+ {"<sep>", NULL, "Volume control", 0},
+ {"get-volume", get_volume, "returns the current volume level in percent", 0},
+ {"set-volume", set_volume, "sets the current volume level in percent", 1},
+
+ {"<sep>", NULL, "Equalizer manipulation", 0},
{"equalizer-activate", equalizer_active, "activates/deactivates the equalizer", 1},
{"equalizer-get", equalizer_get_eq, "gets the equalizer settings", 0},
{"equalizer-set", equalizer_set_eq, "sets the equalizer settings", 11},
@@ -111,100 +115,104 @@ struct commandhandler handlers[] = {
{"equalizer-get-band", equalizer_get_eq_band, "gets the equalizer value in specified band", 1},
{"equalizer-set-band", equalizer_set_eq_band, "sets the equalizer value in the specified band", 2},
+ {"<sep>", NULL, "Miscellaneous", 0},
+ {"mainwin-show", mainwin_show, "shows/hides the main window", 1},
+ {"filebrowser-show", show_filebrowser, "shows/hides the filebrowser", 1},
+ {"jumptofile-show", show_jtf_window, "shows/hides the jump to file window", 1},
+ {"preferences-show", show_preferences_window, "shows/hides the preferences window", 1},
+ {"about-show", show_about_window, "shows/hides the about window", 1},
- {"<sep>", NULL, "Miscellaneous", 0},
- {"mainwin-show", mainwin_show, "shows/hides the main window", 1},
- {"filebrowser-show", show_filebrowser, "shows/hides the filebrowser", 1},
- {"jumptofile-show", show_jtf_window, "shows/hides the jump to file window", 1},
- {"preferences-show", show_preferences_window, "shows/hides the preferences window", 1},
- {"about-show", show_about_window, "shows/hides the about window", 1},
-
- {"always-on-top", toggle_aot, "on/off always on top", 1},
{"version", get_version, "shows Audacious version", 0},
- {"shutdown", shutdown_audacious_server, "shuts down Audacious", 0},
+ {"shutdown", shutdown_audacious_server, "shuts down Audacious", 0},
+ {"<sep>", NULL, "Help system", 0},
+ {"list-handlers", get_handlers_list, "shows handlers list", 0},
+ {"help", get_handlers_list, "shows handlers list", 0},
- {"<sep>", NULL, "Help system", 0},
- {"list-handlers", get_handlers_list, "shows handlers list", 0},
- {"help", get_handlers_list, "shows handlers list", 0},
+ {NULL, NULL, NULL, 0}
+};
+ObjAudacious * dbus_proxy = NULL;
+static GDBusConnection * connection = NULL;
- {NULL, NULL, NULL, 0}
-};
+static void audtool_disconnect (void)
+{
+ g_object_unref (dbus_proxy);
+ dbus_proxy = NULL;
-DBusGProxy *dbus_proxy = NULL;
-static DBusGConnection *connection = NULL;
+ g_dbus_connection_close_sync (connection, NULL, NULL);
+ connection = NULL;
+}
-static void
-audtool_connect(void)
+static void audtool_connect (void)
{
- GError *error = NULL;
+ GError * error = NULL;
- connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, & error);
- if (connection == NULL)
- {
- fprintf (stderr, "D-Bus Error: %s\n", error->message);
- g_error_free (error);
- exit (EXIT_FAILURE);
- }
+ if (! connection)
+ {
+ fprintf (stderr, "D-Bus error: %s\n", error->message);
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
- dbus_proxy = dbus_g_proxy_new_for_name(connection, AUDACIOUS_DBUS_SERVICE,
- AUDACIOUS_DBUS_PATH,
- AUDACIOUS_DBUS_INTERFACE);
-}
+ dbus_proxy = obj_audacious_proxy_new_sync (connection, 0,
+ "org.atheme.audacious", "/org/atheme/audacious", NULL, & error);
-static void
-audtool_disconnect(void)
-{
- g_object_unref(dbus_proxy);
- dbus_proxy = NULL;
+ if (! dbus_proxy)
+ {
+ fprintf (stderr, "D-Bus error: %s\n", error->message);
+ g_error_free (error);
+ g_dbus_connection_close_sync (connection, NULL, NULL);
+ exit (EXIT_FAILURE);
+ }
+
+ atexit (audtool_disconnect);
}
-gint
-main(gint argc, gchar **argv)
+int main (int argc, char * * argv)
{
- gint i, j = 0, k = 0;
+ int i, j = 0, k = 0;
- setlocale(LC_CTYPE, "");
+ setlocale (LC_CTYPE, "");
#if ! GLIB_CHECK_VERSION (2, 36, 0)
- g_type_init();
+ g_type_init();
#endif
- audtool_connect();
-
- if (argc < 2)
- {
- fprintf (stderr, "Not enough parameters. Try \"audtool help\".\n");
- exit (EXIT_FAILURE);
- }
-
- for (j = 1; j < argc; j++)
- {
- for (i = 0; handlers[i].name != NULL; i++)
- {
- if ((!g_ascii_strcasecmp(handlers[i].name, argv[j]) ||
- !g_ascii_strcasecmp(g_strconcat("--", handlers[i].name, NULL), argv[j]))
- && g_ascii_strcasecmp("<sep>", handlers[i].name))
- {
- int numargs = handlers[i].args + 1 < argc - j ? handlers[i].args + 1 : argc - j;
- handlers[i].handler(numargs, &argv[j]);
- j += handlers[i].args;
- k++;
- if(j >= argc)
- break;
- }
- }
- }
-
- if (k == 0)
- {
- fprintf (stderr, "Unknown command \"%s\". Try \"audtool help\".\n", argv[1]);
- exit (EXIT_FAILURE);
- }
-
- audtool_disconnect();
-
- return 0;
+ audtool_connect ();
+
+ if (argc < 2)
+ {
+ fprintf (stderr, "Not enough parameters. Try \"audtool help\".\n");
+ exit (EXIT_FAILURE);
+ }
+
+ for (j = 1; j < argc; j ++)
+ {
+ for (i = 0; handlers[i].name != NULL; i++)
+ {
+ if ((! g_ascii_strcasecmp (handlers[i].name, argv[j]) ||
+ ! g_ascii_strcasecmp (g_strconcat ("--", handlers[i].name, NULL),
+ argv[j])) && g_ascii_strcasecmp ("<sep>", handlers[i].name))
+ {
+ int numargs = handlers[i].args + 1 < argc - j ? handlers[i].args + 1 : argc - j;
+ handlers[i].handler (numargs, & argv[j]);
+ j += handlers[i].args;
+ k ++;
+
+ if (j >= argc)
+ break;
+ }
+ }
+ }
+
+ if (k == 0)
+ {
+ fprintf (stderr, "Unknown command \"%s\". Try \"audtool help\".\n", argv[1]);
+ exit (EXIT_FAILURE);
+ }
+
+ return 0;
}
diff --git a/src/audtool/report.c b/src/audtool/report.c
index ecb4791..01a2116 100644
--- a/src/audtool/report.c
+++ b/src/audtool/report.c
@@ -18,81 +18,84 @@
*/
#include <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include <locale.h>
-#include "libaudclient/audctrl.h"
+
#include "audtool.h"
-void audtool_report(const gchar *str, ...)
+void audtool_report (const char * str, ...)
{
- gchar *buf;
- va_list va;
+ char * buf;
+ va_list va;
- va_start(va, str);
- buf = g_strdup_vprintf(str, va);
- va_end(va);
+ va_start (va, str);
+ buf = g_strdup_vprintf (str, va);
+ va_end (va);
- g_print("%s\n", buf);
- g_free(buf);
+ g_print ("%s\n", buf);
+ g_free (buf);
}
-void audtool_whine(const gchar *str, ...)
+void audtool_whine (const char * str, ...)
{
- gchar *buf;
- va_list va;
+ char * buf;
+ va_list va;
- va_start(va, str);
- buf = g_strdup_vprintf(str, va);
- va_end(va);
+ va_start (va, str);
+ buf = g_strdup_vprintf (str, va);
+ va_end (va);
- g_printerr("audtool: %s", buf);
- g_free(buf);
+ g_printerr ("audtool: %s", buf);
+ g_free (buf);
}
-void audtool_whine_args(const gchar *name, const gchar *fmt, ...)
+void audtool_whine_args (const char * name, const char * fmt, ...)
{
- gchar *buf;
- va_list va;
+ char * buf;
+ va_list va;
- va_start(va, fmt);
- buf = g_strdup_vprintf(fmt, va);
- va_end(va);
+ va_start (va, fmt);
+ buf = g_strdup_vprintf (fmt, va);
+ va_end (va);
- g_printerr("audtool: Invalid parameters for %s\n", name);
- g_printerr(" syntax: %s %s\n", name, buf);
- g_free(buf);
+ g_printerr ("audtool: Invalid parameters for %s\n", name);
+ g_printerr (" syntax: %s %s\n", name, buf);
+ g_free (buf);
}
-void audtool_whine_tuple_fields(void)
+void audtool_whine_tuple_fields (void)
{
- gint nfields, i;
- gchar **fields = audacious_remote_get_tuple_fields(dbus_proxy),
- **tmp = fields;
-
- audtool_whine("Field names include, but are not limited to:\n");
-
- for (nfields = 0; tmp && *tmp; nfields++, tmp++);
-
- tmp = fields;
- i = 0;
- g_printerr(" ");
- while (tmp && *tmp) {
- i += g_utf8_strlen(*tmp, -1);
- if (i > 45) {
- g_printerr("\n ");
- i = 0;
+ char * * fields = NULL;
+ obj_audacious_call_get_tuple_fields_sync (dbus_proxy, & fields, NULL, NULL);
+
+ if (! fields)
+ exit (1);
+
+ audtool_whine ("Field names include, but are not limited to:\n");
+
+ char * * tmp = fields;
+ int col = 0;
+
+ g_printerr (" ");
+
+ while (tmp && * tmp)
+ {
+ col += g_utf8_strlen (* tmp, -1);
+
+ if (col > 45)
+ {
+ g_printerr ("\n ");
+ col = 0;
}
- g_printerr("%s", *tmp);
- if (--nfields > 0)
- g_printerr(", ");
- g_free(*tmp);
- tmp++;
+ g_printerr ("%s", * tmp);
+
+ g_free (* tmp);
+ tmp ++;
+
+ if (* tmp)
+ g_printerr (", ");
}
- g_printerr("\n");
+ g_printerr ("\n");
- g_free(fields);
+ g_free (fields);
}
-
diff --git a/src/audtool/wrappers.c b/src/audtool/wrappers.c
new file mode 100644
index 0000000..c255954
--- /dev/null
+++ b/src/audtool/wrappers.c
@@ -0,0 +1,194 @@
+/*
+ * wrappers.c
+ * Copyright 2013 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 <stdlib.h>
+
+#include "audtool.h"
+#include "wrappers.h"
+
+void generic_on_off (int argc, char * * argv, OnOffFunc func)
+{
+ gboolean show = TRUE;
+
+ if (argc >= 2)
+ {
+ if (! g_ascii_strcasecmp (argv[1], "on"))
+ show = TRUE;
+ else if (! g_ascii_strcasecmp (argv[1], "off"))
+ show = FALSE;
+ else
+ {
+ audtool_whine_args (argv[0], "<on/off>");
+ exit (1);
+ }
+ }
+
+ func (dbus_proxy, show, NULL, NULL);
+}
+
+int get_playlist_length (void)
+{
+ int length = -1;
+ obj_audacious_call_length_sync (dbus_proxy, & length, NULL, NULL);
+
+ if (length < 0)
+ exit (1);
+
+ return length;
+}
+
+int get_queue_length (void)
+{
+ int length = -1;
+ obj_audacious_call_get_playqueue_length_sync (dbus_proxy, & length, NULL, NULL);
+
+ if (length < 0)
+ exit (1);
+
+ return length;
+}
+
+int get_queue_entry (int qpos)
+{
+ unsigned entry = -1;
+ obj_audacious_call_queue_get_list_pos_sync (dbus_proxy, qpos, & entry, NULL, NULL);
+
+ if (entry == (unsigned) -1)
+ exit (1);
+
+ return entry;
+}
+
+int find_in_queue (int entry)
+{
+ unsigned qpos = -1;
+ obj_audacious_call_queue_get_queue_pos_sync (dbus_proxy, entry, & qpos, NULL, NULL);
+
+ if (qpos == (unsigned) -1)
+ exit (1);
+
+ return qpos;
+}
+
+int get_current_entry (void)
+{
+ unsigned entry = -1;
+ obj_audacious_call_position_sync (dbus_proxy, & entry, NULL, NULL);
+
+ if (entry == (unsigned) -1)
+ exit (1);
+
+ return entry;
+}
+
+char * get_entry_filename (int entry)
+{
+ char * uri = NULL;
+ obj_audacious_call_song_filename_sync (dbus_proxy, entry, & uri, NULL, NULL);
+
+ if (! uri)
+ exit (1);
+
+ char * filename = g_filename_from_uri (uri, NULL, NULL);
+
+ if (filename)
+ {
+ g_free (uri);
+ return filename;
+ }
+
+ return uri;
+}
+
+char * get_entry_title (int entry)
+{
+ char * title = NULL;
+ obj_audacious_call_song_title_sync (dbus_proxy, entry, & title, NULL, NULL);
+
+ if (! title)
+ exit (1);
+
+ return title;
+}
+
+int get_entry_length (int entry)
+{
+ int length = -1;
+ obj_audacious_call_song_frames_sync (dbus_proxy, entry, & length, NULL, NULL);
+
+ if (length < 0)
+ exit (1);
+
+ return length;
+}
+
+char * get_entry_field (int entry, const char * field)
+{
+ GVariant * var = NULL;
+ obj_audacious_call_song_tuple_sync (dbus_proxy, entry, field, & var, NULL, NULL);
+
+ if (! var || ! g_variant_is_of_type (var, G_VARIANT_TYPE_VARIANT))
+ exit (1);
+
+ GVariant * var2 = g_variant_get_variant (var);
+
+ if (! var2)
+ exit (1);
+
+ char * str;
+
+ if (g_variant_is_of_type (var2, G_VARIANT_TYPE_STRING))
+ str = g_strdup (g_variant_get_string (var2, NULL));
+ else if (g_variant_is_of_type (var2, G_VARIANT_TYPE_INT32))
+ str = g_strdup_printf ("%d", (int) g_variant_get_int32 (var2));
+ else
+ exit (1);
+
+ g_variant_unref (var);
+ g_variant_unref (var2);
+
+ return str;
+}
+
+int get_current_time (void)
+{
+ unsigned time = -1;
+ obj_audacious_call_time_sync (dbus_proxy, & time, NULL, NULL);
+
+ if (time == (unsigned) -1)
+ exit (1);
+
+ return time;
+}
+
+void get_current_info (int * bitrate_p, int * samplerate_p, int * channels_p)
+{
+ int bitrate = -1, samplerate = -1, channels = -1;
+ obj_audacious_call_get_info_sync (dbus_proxy, & bitrate, & samplerate, & channels, NULL, NULL);
+
+ if (bitrate < 0 || samplerate < 0 || channels < 0)
+ exit (1);
+
+ if (bitrate_p)
+ * bitrate_p = bitrate;
+ if (samplerate_p)
+ * samplerate_p = samplerate;
+ if (channels_p)
+ * channels_p = channels;
+}
diff --git a/src/audtool/wrappers.h b/src/audtool/wrappers.h
new file mode 100644
index 0000000..e9a9659
--- /dev/null
+++ b/src/audtool/wrappers.h
@@ -0,0 +1,43 @@
+/*
+ * wrappers.h
+ * Copyright 2013 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 AUDTOOL_WRAPPERS_H
+#define AUDTOOL_WRAPPERS_H
+
+typedef gboolean (* OnOffFunc) (ObjAudacious * proxy, gboolean show,
+ GCancellable * cancellable, GError * * error);
+
+void generic_on_off (int argc, char * * argv, OnOffFunc func);
+
+int get_playlist_length (void);
+
+int get_queue_length (void);
+int get_queue_entry (int qpos);
+int find_in_queue (int entry);
+
+int get_current_entry (void);
+char * get_entry_filename (int entry);
+char * get_entry_title (int entry);
+int get_entry_length (int entry);
+char * get_entry_field (int entry, const char * field);
+
+int get_current_time (void);
+void get_current_info (int * bitrate, int * samplerate, int * channels);
+
+#endif /* AUDTOOL_WRAPPERS_H */
diff --git a/src/dbus/Makefile b/src/dbus/Makefile
new file mode 100644
index 0000000..45a7e46
--- /dev/null
+++ b/src/dbus/Makefile
@@ -0,0 +1,18 @@
+STATIC_LIB_NOINST = aud-dbus.a
+
+SRCS = aud-dbus.c
+CLEAN = aud-dbus.c aud-dbus.h
+
+include ../../buildsys.mk
+include ../../extra.mk
+
+CPPFLAGS := -I../.. ${CPPFLAGS} ${GLIB_CFLAGS} ${GIO_CFLAGS}
+LIBS := ${LIBS} ${GLIB_LIBS} ${GIO_LIBS}
+
+pre-depend: aud-dbus.c aud-dbus.h
+
+aud-dbus.h: aud-dbus.xml
+ gdbus-codegen --interface-prefix org.atheme. --c-namespace Obj --generate-c-code aud-dbus aud-dbus.xml
+
+aud-dbus.c: aud-dbus.h
+ # nothing to do here
diff --git a/src/audacious/objects.xml b/src/dbus/aud-dbus.xml
index 4b3317f..36e2184 100644
--- a/src/audacious/objects.xml
+++ b/src/dbus/aud-dbus.xml
@@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
- * objects.xml
- * Copyright 2007 Ben Tucker
+ * aud-dbus.xml
+ * Copyright 2007-2014 Ben Tucker, Yoshiki Yazawa, Matti HÀmÀlÀinen, and
+ * John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -19,80 +20,6 @@
* the use of this software.
-->
-<!--
- Defined:
- CMD_GET_VERSION //CHANGED: now it returns audacious version instead of protocol version.
- CMD_PLAY
- CMD_PAUSE
- CMD_STOP
- CMD_IS_PLAYING
- CMD_GET_PLAYLIST_POS
- CMD_SET_PLAYLIST_POS
- CMD_GET_PLAYLIST_LENGTH
- CMD_PLAYLIST_CLEAR
- CMD_GET_OUTPUT_TIME
- CMD_JUMP_TO_TIME
- CMD_GET_PLAYLIST_FILE
- CMD_GET_PLAYLIST_TITLE
- CMD_GET_PLAYLIST_TIME
- CMD_PLAYLIST_PREV
- CMD_PLAYLIST_NEXT
- CMD_TOGGLE_REPEAT
- CMD_TOGGLE_SHUFFLE
- CMD_PLAYLIST_ADD_URL_STRING
- CMD_PLAYLIST_DELETE
- CMD_IS_REPEAT
- CMD_IS_SHUFFLE
- CMD_GET_VOLUME
- CMD_SET_VOLUME
- CMD_GET_BALANCE
- CMD_IS_EQ_WIN
- CMD_IS_PL_WIN
- CMD_IS_MAIN_WIN
- CMD_PL_WIN_TOGGLE
- CMD_EQ_WIN_TOGGLE
- CMD_MAIN_WIN_TOGGLE
- CMD_QUIT
- CMD_EJECT
- CMD_GET_INFO
- CMD_PLAYLIST_GET_TUPLE_DATA
- CMD_IS_ADVANCE
- CMD_TOGGLE_ADVANCE
- CMD_SHOW_PREFS_BOX
- CMD_SHOW_ABOUT_BOX
- CMD_SHOW_JTF_BOX
-
- Newly defined:
- CMD_PLAY_PAUSE
- CMD_ACTIVATE
- CMD_GET_SKIN
- CMD_SET_SKIN
- CMD_GET_INFO
- CMD_TOGGLE_AOT
- CMD_GET_PLAYQUEUE_LENGTH
- CMD_PLAYQUEUE_ADD
- CMD_PLAYQUEUE_REMOVE
- CMD_PLAYQUEUE_CLEAR
- CMD_PLAYQUEUE_GET_LIST_POS //CHANGED: get list position by queue position
- CMD_PLAYQUEUE_GET_QUEUE_POS //CHANGED: get queue position by list postion
- CMD_PLAYQUEUE_IS_QUEUED
- CMD_PLAYLIST_INS_URL_STRING
- CMD_PLAYLIST_ENQUEUE_TO_TEMP
- CMD_PLAYLIST_ADD
- CMD_GET_EQ //CHANGED: now these functions use double due to dbus-glib restriction
- CMD_GET_EQ_PREAMP
- CMD_GET_EQ_BAND //NOTE: GArray is used for bands
- CMD_SET_EQ
- CMD_SET_EQ_PREAMP
- CMD_SET_EQ_BAND
-
- Obsolete:
- CMD_PLAYLIST_INS //unnecessary?
- CMD_GET_EQ_DATA //obsolete
- CMD_SET_EQ_DATA //obsolete
-
--->
-
<node name="/">
<!-- Audacious General Information -->
<interface name="org.atheme.audacious">
@@ -102,14 +29,10 @@
</method>
<!-- Quit Audacious -->
- <method name="Quit">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
+ <method name="Quit" />
<!-- Open files (Eject) -->
- <method name="Eject">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
+ <method name="Eject" />
<!-- Main window visibility -->
<method name="MainWinVisible">
@@ -118,8 +41,7 @@
<!-- Toggle main window visibility -->
<method name="ShowMainWin">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="b" name="show"/>
+ <arg type="b" direction="in" name="show"/>
</method>
<!-- Get names of available 'standard' tuple fields -->
@@ -130,19 +52,13 @@
<!-- Playback Information/Manipulation -->
<!-- Begin or resume playback -->
- <method name="Play">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
+ <method name="Play" />
<!-- Pause playback -->
- <method name="Pause">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
+ <method name="Pause" />
<!-- Stop playback -->
- <method name="Stop">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
+ <method name="Stop" />
<!-- Is playback playing? -->
<method name="Playing">
@@ -185,9 +101,8 @@
<!-- Seek to some absolute position in the current song -->
<method name="Seek">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
<!-- Position of song, in ms, to seek to -->
- <arg type="u" name="pos"/>
+ <arg type="u" direction="in" name="pos"/>
</method>
<!-- What is the playback volume? -->
@@ -200,11 +115,10 @@
<!-- Set the playback volume -->
<method name="SetVolume">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
<!-- Volume of the left channel -->
- <arg type="i" name="vl"/>
+ <arg type="i" direction="in" name="vl"/>
<!-- Volume of the right channel -->
- <arg type="i" name="vr"/>
+ <arg type="i" direction="in" name="vr"/>
</method>
<!-- Get the playback balance -->
@@ -221,14 +135,10 @@
</method>
<!-- Skip ahead one song in the current playlist -->
- <method name="Advance">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
+ <method name="Advance" />
<!-- Skip backwards one song in the current playlist -->
- <method name="Reverse">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
+ <method name="Reverse" />
<!-- Playlist length -->
<method name="Length">
@@ -239,7 +149,7 @@
<!-- Get a song's title -->
<method name="SongTitle">
<!-- Song position in the playlist -->
- <arg type="u" name="pos"/>
+ <arg type="u" direction="in" name="pos"/>
<!-- Return title of desired song -->
<arg type="s" direction="out" name="title"/>
@@ -248,7 +158,7 @@
<!-- Get a song's filename -->
<method name="SongFilename">
<!-- Song position in the playlist -->
- <arg type="u" name="pos"/>
+ <arg type="u" direction="in" name="pos"/>
<!-- Return filename of desired song -->
<arg type="s" direction="out" name="filename"/>
@@ -257,7 +167,7 @@
<!-- Get the length of some song, in seconds -->
<method name="SongLength">
<!-- Song position in the playlist -->
- <arg type="u" name="pos"/>
+ <arg type="u" direction="in" name="pos"/>
<!-- Return length, in seconds, of desired song -->
<arg type="i" direction="out" name="length"/>
@@ -266,7 +176,7 @@
<!-- Get the length of some song, in frames -->
<method name="SongFrames">
<!-- Song position in the playlist -->
- <arg type="u" name="pos"/>
+ <arg type="u" direction="in" name="pos"/>
<!-- Return length, in frames, of desired song -->
<arg type="i" direction="out" name="length"/>
@@ -275,10 +185,10 @@
<!-- Get the value of a tuple field of some song -->
<method name="SongTuple">
<!-- Song position in the playlist -->
- <arg type="u" name="pos"/>
+ <arg type="u" direction="in" name="pos"/>
<!-- Tuple name -->
- <arg type="s" name="tuple"/>
+ <arg type="s" direction="in" name="tuple"/>
<!-- Return tuple value -->
<arg type="v" direction="out" name="value"/>
@@ -286,57 +196,48 @@
<!-- Jump to some position in the playlist -->
<method name="Jump">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
<!-- Song position to jump to -->
- <arg type="u" name="pos"/>
+ <arg type="u" direction="in" name="pos"/>
</method>
<!-- Add some file to the current playlist -->
<method name="Add">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
<!-- File to add -->
- <arg type="s" name="file"/>
+ <arg type="s" direction="in" name="file"/>
</method>
<!-- Add some URL to the current playlist -->
<method name="AddUrl">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
<!-- URL to add -->
- <arg type="s" name="url"/>
+ <arg type="s" direction="in" name="url"/>
</method>
<!-- Add a list of files -->
<method name="AddList">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
<!-- Array of filenames to add -->
- <arg type="as" name="filenames"/>
+ <arg type="as" direction="in" name="filenames"/>
</method>
<!-- Open a list of files -->
<method name="OpenList">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
<!-- Array of filenames to open -->
- <arg type="as" name="filenames"/>
+ <arg type="as" direction="in" name="filenames"/>
</method>
<!-- Open a list of files in a temporary playlist -->
<method name="OpenListToTemp">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
<!-- Array of filenames to open -->
- <arg type="as" name="filenames"/>
+ <arg type="as" direction="in" name="filenames"/>
</method>
<!-- Delete some song from the playlist -->
<method name="Delete">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
<!-- Position of song to delete -->
- <arg type="u" name="pos"/>
+ <arg type="u" direction="in" name="pos"/>
</method>
<!-- Clear the playlist -->
- <method name="Clear">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
+ <method name="Clear" />
<!-- Query auto-advance status -->
<method name="AutoAdvance">
@@ -344,9 +245,7 @@
</method>
<!-- Toggle auto-advance -->
- <method name="ToggleAutoAdvance">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
+ <method name="ToggleAutoAdvance" />
<!-- Query repeat status -->
<method name="Repeat">
@@ -354,9 +253,7 @@
</method>
<!-- Toggle repeat -->
- <method name="ToggleRepeat">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
+ <method name="ToggleRepeat" />
<!-- Query shuffle status -->
<method name="Shuffle">
@@ -364,9 +261,7 @@
</method>
<!-- Toggle shuffle -->
- <method name="ToggleShuffle">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
+ <method name="ToggleShuffle" />
<!-- Query stop-after-song status -->
<method name="StopAfter">
@@ -374,48 +269,40 @@
</method>
<!-- Toggle stop-after-song -->
- <method name="ToggleStopAfter">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
+ <method name="ToggleStopAfter" />
<!-- Show preferences window -->
<method name="ShowPrefsBox">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="b" name="show"/>
+ <arg type="b" direction="in" name="show"/>
</method>
<!-- Show about window -->
<method name="ShowAboutBox">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="b" name="show"/>
+ <arg type="b" direction="in" name="show"/>
</method>
<!-- Show jump to file window -->
<method name="ShowJtfBox">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="b" name="show"/>
+ <arg type="b" direction="in" name="show"/>
</method>
<!-- Show filebrowser -->
<method name="ShowFilebrowser">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="b" name="show"/>
+ <arg type="b" direction="in" name="show"/>
</method>
<!-- Either play or pause -->
- <method name="PlayPause">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
+ <method name="PlayPause" />
<!-- Playqueue get playlist pos -->
<method name="QueueGetListPos">
- <arg type="u" name="qpos"/>
+ <arg type="u" direction="in" name="qpos"/>
<arg type="u" direction="out" name="pos"/>
</method>
<!-- Playqueue get playqueue pos -->
<method name="QueueGetQueuePos">
- <arg type="u" name="pos"/>
+ <arg type="u" direction="in" name="pos"/>
<arg type="u" direction="out" name="qpos"/>
</method>
@@ -426,48 +313,36 @@
<arg type="i" direction="out" name="nch"/>
</method>
- <method name="ToggleAot">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="b" name="ontop"/>
- </method>
-
<method name="GetPlayqueueLength">
<arg type="i" direction="out" name="length"/>
</method>
<method name="PlaylistInsUrlString">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="s" name="url"/>
- <arg type="i" name="pos"/>
+ <arg type="s" direction="in" name="url"/>
+ <arg type="i" direction="in" name="pos"/>
</method>
<method name="PlaylistAdd">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="s" name="list"/>
+ <arg type="s" direction="in" name="list"/>
</method>
<method name="PlayqueueAdd">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="i" name="pos"/>
+ <arg type="i" direction="in" name="pos"/>
</method>
<method name="PlayqueueRemove">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="i" name="pos"/>
+ <arg type="i" direction="in" name="pos"/>
</method>
- <method name="PlayqueueClear">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- </method>
+ <method name="PlayqueueClear" />
<method name="PlayqueueIsQueued">
- <arg type="i" name="pos"/>
+ <arg type="i" direction="in" name="pos"/>
<arg type="b" direction="out" name="is_queued"/>
</method>
<method name="PlaylistEnqueueToTemp">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="s" name="url"/>
+ <arg type="s" direction="in" name="url"/>
</method>
<!-- equalizer -->
@@ -481,35 +356,54 @@
</method>
<method name="GetEqBand">
- <arg type="i" name="band"/>
+ <arg type="i" direction="in" name="band"/>
<arg type="d" direction="out" name="value"/>
</method>
<method name="SetEq">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="d" name="preamp"/>
- <arg type="ad" name="bands"/>
+ <arg type="d" direction="in" name="preamp"/>
+ <arg type="ad" direction="in" name="bands"/>
</method>
<method name="SetEqPreamp">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="d" name="preamp"/>
+ <arg type="d" direction="in" name="preamp"/>
</method>
<method name="SetEqBand">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="i" name="band"/>
- <arg type="d" name="value"/>
+ <arg type="i" direction="in" name="band"/>
+ <arg type="d" direction="in" name="value"/>
</method>
- <!-- Activate/Deactivate Equalizer -->
+ <!-- Activate/Deactivate Equalizer -->
<method name="EqualizerActivate">
- <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/>
- <arg type="b" name="active"/>
+ <arg type="b" direction="in" name="active"/>
+ </method>
+
+ <method name="NumberOfPlaylists">
+ <arg type="i" direction="out" name="count" />
+ </method>
+
+ <method name="GetActivePlaylist">
+ <arg type="i" direction="out" name="plnum" />
+ </method>
+
+ <method name="SetActivePlaylist">
+ <arg type="i" direction="in" name="plnum" />
</method>
<method name="GetActivePlaylistName">
<arg type="s" direction="out" name="plname" />
</method>
+
+ <method name="SetActivePlaylistName">
+ <arg type="s" direction="in" name="plname" />
+ </method>
+
+ <method name="NewPlaylist" />
+
+ <method name="DeleteActivePlaylist" />
+
+ <method name="PlayActivePlaylist" />
+
</interface>
</node>
diff --git a/src/libaudclient/Makefile b/src/libaudclient/Makefile
deleted file mode 100644
index 52f720d..0000000
--- a/src/libaudclient/Makefile
+++ /dev/null
@@ -1,23 +0,0 @@
-SHARED_LIB = ${LIB_PREFIX}audclient${LIB_SUFFIX}
-LIB_MAJOR = 2
-LIB_MINOR = 0
-
-SRCS = audctrl.c
-
-INCLUDES = audctrl.h
-
-include ../../buildsys.mk
-include ../../extra.mk
-
-pre-depend:
- cd ../audacious; ${MAKE} ${MFLAGS} dbus-client-bindings.h
-
-CPPFLAGS := -I.. -I../.. \
- ${CPPFLAGS} \
- ${DBUS_CFLAGS} \
- ${GLIB_CFLAGS}
-
-CFLAGS += ${LIB_CFLAGS}
-
-LIBS += ${GLIB_LIBS} \
- ${DBUS_LIBS}
diff --git a/src/libaudclient/audctrl.c b/src/libaudclient/audctrl.c
deleted file mode 100644
index 87b36a6..0000000
--- a/src/libaudclient/audctrl.c
+++ /dev/null
@@ -1,993 +0,0 @@
-/*
- * audctrl.c
- * Copyright 2007-2011 Ben Tucker, William Pitcock, Yoshiki Yazawa,
- * Matti HÀmÀlÀinen, 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 <stdlib.h>
-#include <glib.h>
-#include <string.h>
-#include <dbus/dbus-glib.h>
-
-#include "audacious/dbus.h"
-#include "audacious/dbus-client-bindings.h"
-#include "audctrl.h"
-
-static GError *error = NULL; //it must be hidden from outside, otherwise symbol conflict likely to happen.
-
-/**
- * Sends a list of URIs to Audacious, either replacing current
- * playlist or enqueuing them.
- *
- * @param[in] proxy DBus proxy for Audacious
- * @param[in] list An array of URI strings to add.
- * @param[in] num Number of URIs to add.
- * @param[in] enqueue Whether or not the new playlist should be added on, or replace the current playlist.
- **/
-EXPORT void audacious_remote_playlist(DBusGProxy *proxy, gchar **list, gint num, gboolean enqueue) {
- GList *glist = NULL;
- gchar **data = list;
-
- g_return_if_fail(list != NULL);
- g_return_if_fail(num > 0);
-
- if (!enqueue)
- audacious_remote_playlist_clear(proxy);
-
- // construct a GList
- while(data) {
- glist = g_list_append(glist, (gpointer)data);
- data++;
- }
-
- org_atheme_audacious_playlist_add(proxy, (gpointer)glist, &error);
-
- g_list_free(glist);
- glist = NULL;
-
- if (!enqueue)
- audacious_remote_play(proxy);
-}
-
-/**
- * Queries Audacious for its version identifier.
- *
- * @param[in] proxy DBus proxy for audacious
- * @return String describing the version of Audacious.
- **/
-EXPORT gchar *audacious_remote_get_version(DBusGProxy *proxy) {
- char *string = NULL;
- org_atheme_audacious_version(proxy, &string, &error);
- g_clear_error(&error);
-
- return (string ? string : NULL);
-}
-
-/**
- * Sends a list of URIs to Audacious to add to the playlist.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] list A GList of URIs to add to the playlist.
- **/
-EXPORT void audacious_remote_playlist_add (DBusGProxy * proxy, GList * list)
-{
- const gchar * filenames[g_list_length (list) + 1];
- int count;
-
- for (count = 0; list != NULL; count ++, list = list->next)
- filenames[count] = list->data;
-
- filenames[count] = NULL;
-
- org_atheme_audacious_add_list (proxy, filenames, & error);
- g_clear_error (& error);
-}
-
-/**
- * Sends a list of URIs for Audacious to open. New in Audacious 2.3.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] list A GList of URIs to open
- **/
-EXPORT void audacious_remote_playlist_open_list (DBusGProxy * proxy, GList * list)
-{
- const gchar * filenames[g_list_length (list) + 1];
- int count;
-
- for (count = 0; list != NULL; count ++, list = list->next)
- filenames[count] = list->data;
-
- filenames[count] = NULL;
-
- org_atheme_audacious_open_list (proxy, filenames, & error);
- g_clear_error (& error);
-}
-
-/**
- * Sends a list of URIs for Audacious to open in a temporary playlist. New in
- * Audacious 2.3.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] list A GList of URIs to open
- **/
-EXPORT void audacious_remote_playlist_open_list_to_temp (DBusGProxy * proxy, GList *
- list)
-{
- const gchar * filenames[g_list_length (list) + 1];
- int count;
-
- for (count = 0; list != NULL; count ++, list = list->next)
- filenames[count] = list->data;
-
- filenames[count] = NULL;
-
- org_atheme_audacious_open_list_to_temp (proxy, filenames, & error);
- g_clear_error (& error);
-}
-
-/**
- * Deletes a playlist entry from current playlist in given position.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] pos The playlist position to delete.
- **/
-EXPORT void audacious_remote_playlist_delete(DBusGProxy *proxy, guint pos) {
- org_atheme_audacious_delete(proxy, pos, &error);
- g_clear_error(&error);
-}
-
-/**
- * Requests audacious to begin playback.
- *
- * @param[in] proxy DBus proxy for audacious
- **/
-EXPORT void audacious_remote_play(DBusGProxy *proxy) {
- org_atheme_audacious_play(proxy, &error);
- g_clear_error(&error);
-}
-
-/**
- * Tells audacious to pause.
- *
- * @param[in] proxy DBus proxy for audacious
- **/
-EXPORT void audacious_remote_pause(DBusGProxy *proxy) {
- org_atheme_audacious_pause(proxy, &error);
- g_clear_error(&error);
-}
-
-/**
- * Tells audacious to stop.
- *
- * @param[in] proxy DBus proxy for audacious
- **/
-EXPORT void audacious_remote_stop(DBusGProxy *proxy) {
- org_atheme_audacious_stop(proxy, &error);
- g_clear_error(&error);
-}
-
-/**
- * Queries audacious about whether it is playing or not.
- *
- * @param[in] proxy DBus proxy for audacious
- * @return TRUE if playing, FALSE otherwise.
- **/
-EXPORT gboolean audacious_remote_is_playing(DBusGProxy *proxy) {
- gboolean is_playing = FALSE;
- org_atheme_audacious_playing(proxy, &is_playing, &error);
- g_clear_error(&error);
- return is_playing;
-}
-
-/**
- * audacious_remote_is_paused:
- * @param[in] proxy DBus proxy for audacious
- *
- * Queries audacious about whether it is paused or not.
- *
- * @return TRUE if playing, FALSE otherwise.
- **/
-EXPORT gboolean audacious_remote_is_paused(DBusGProxy *proxy) {
- gboolean is_paused = FALSE;
- org_atheme_audacious_paused(proxy, &is_paused, &error);
- g_clear_error(&error);
- return is_paused;
-}
-
-/**
- * audacious_remote_get_playlist_pos:
- * @param[in] proxy DBus proxy for audacious
- *
- * Queries audacious about the current playlist position.
- *
- * @return The current playlist position.
- **/
-EXPORT gint audacious_remote_get_playlist_pos(DBusGProxy *proxy) {
- guint pos = 0;
- org_atheme_audacious_position(proxy, &pos, &error);
- g_clear_error(&error);
- return pos;
-}
-
-/**
- * audacious_remote_set_playlist_pos:
- * @param[in] proxy DBus proxy for audacious
- * @param[in] pos Playlist position to jump to.
- *
- * Tells audacious to jump to a different playlist position.
- **/
-EXPORT void audacious_remote_set_playlist_pos(DBusGProxy *proxy, guint pos) {
- org_atheme_audacious_jump (proxy, pos, &error);
- g_clear_error(&error);
-}
-
-/**
- * audacious_remote_get_playlist_length:
- * @param[in] proxy DBus proxy for audacious
- *
- * Queries audacious about the current playlist length.
- *
- * @return The amount of entries in the playlist.
- **/
-EXPORT gint audacious_remote_get_playlist_length(DBusGProxy *proxy) {
- gint len = 0;
- org_atheme_audacious_length(proxy, &len, &error);
- g_clear_error(&error);
- return len;
-}
-
-/**
- * audacious_remote_playlist_clear:
- * @param[in] proxy DBus proxy for audacious
- *
- * Clears the playlist.
- **/
-EXPORT void audacious_remote_playlist_clear(DBusGProxy *proxy) {
- org_atheme_audacious_clear(proxy, &error);
- g_clear_error(&error);
-}
-
-/**
- * audacious_remote_get_output_time:
- * @param[in] proxy DBus proxy for audacious
- *
- * Queries audacious about the current output position.
- *
- * @return The current output position.
- **/
-EXPORT gint audacious_remote_get_output_time(DBusGProxy *proxy) {
- guint time = 0;
- org_atheme_audacious_time(proxy, &time, &error);
- g_clear_error(&error);
- return time;
-}
-
-/**
- * audacious_remote_jump_to_time:
- * @param[in] proxy DBus proxy for audacious
- * @param[in] pos The time (in milliseconds) to jump to.
- *
- * Tells audacious to seek to a new time position.
- **/
-EXPORT void audacious_remote_jump_to_time(DBusGProxy *proxy, guint pos) {
- org_atheme_audacious_seek (proxy, pos, &error);
- g_clear_error(&error);
-}
-
-/**
- * Queries audacious for the current volume settings.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[out] vl Pointer to integer containing the left channel's volume (0-100).
- * @param[out] vr Pointer to integer containing the right channel's volume (0-100).
- **/
-EXPORT void audacious_remote_get_volume(DBusGProxy *proxy, gint * vl, gint * vr) {
- org_atheme_audacious_volume(proxy, vl, vr, &error);
- g_clear_error(&error);
-}
-
-/**
- * Queries audacious about the current volume.
- *
- * @param[in] proxy DBus proxy for audacious
- * @return The current volume (0-100).
- **/
-EXPORT gint audacious_remote_get_main_volume(DBusGProxy *proxy) {
- gint vl = 0, vr = 0;
-
- audacious_remote_get_volume(proxy, &vl, &vr);
-
- return (vl > vr) ? vl : vr;
-}
-
-/**
- * Queries audacious about the current balance.
- *
- * @param[in] proxy DBus proxy for audacious
- * @return The current balance.
- **/
-EXPORT gint audacious_remote_get_balance(DBusGProxy *proxy) {
- gint balance = 50;
- org_atheme_audacious_balance(proxy, &balance, &error);
- g_clear_error(&error);
- return balance;
-}
-
-/**
- * Sets the volume for the left and right channels in Audacious.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] vl The volume for the left channel (0-100).
- * @param[in] vr The volume for the right channel (0-100).
- **/
-EXPORT void audacious_remote_set_volume(DBusGProxy *proxy, gint vl, gint vr) {
- org_atheme_audacious_set_volume(proxy, vl, vr, &error);
- g_clear_error(&error);
-}
-
-
-/**
- * Sets the volume in Audacious.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] v The volume to set (0-100).
- **/
-EXPORT void audacious_remote_set_main_volume(DBusGProxy *proxy, gint v) {
- gint b = 50, vl = 0, vr = 0;
-
- b = audacious_remote_get_balance(proxy);
-
- if (b < 0) {
- vl = v;
- vr = (v * (100 - abs(b))) / 100;
- } else if (b > 0) {
- vl = (v * (100 - b)) / 100;
- vr = v;
- } else
- vl = vr = v;
- audacious_remote_set_volume(proxy, vl, vr);
-}
-
-/**
- * Sets the balance in Audacious.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] b The balance to set.
- **/
-EXPORT void audacious_remote_set_balance(DBusGProxy *proxy, gint b) {
- gint v = 0, vl = 0, vr = 0;
-
- if (b < -100)
- b = -100;
- if (b > 100)
- b = 100;
-
- v = audacious_remote_get_main_volume(proxy);
-
- if (b < 0) {
- vl = v;
- vr = (v * (100 - abs(b))) / 100;
- } else if (b > 0) {
- vl = (v * (100 - b)) / 100;
- vr = v;
- } else
- vl = vr = v;
- audacious_remote_set_volume(proxy, vl, vr);
-}
-
-/**
- * Queries Audacious about a playlist entry's file.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] pos The playlist position to query for.
- * @return A path to the file in the playlist at %pos position.
- **/
-EXPORT gchar *audacious_remote_get_playlist_file(DBusGProxy *proxy, guint pos) {
- gchar *out = NULL;
- org_atheme_audacious_song_filename(proxy, pos, &out, &error);
- g_clear_error(&error);
- return out;
-}
-
-/**
- * Queries Audacious about a playlist entry's title.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] pos The playlist position to query for.
- * @return The title for the entry in the playlist at %pos position.
- **/
-EXPORT gchar *audacious_remote_get_playlist_title(DBusGProxy *proxy, guint pos) {
- gchar *out = NULL;
- org_atheme_audacious_song_title(proxy, pos, &out, &error);
- g_clear_error(&error);
- return out;
-}
-
-/**
- * Queries Audacious about a playlist entry's length.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] pos The playlist position to query for.
- * @return The length of the entry in the playlist at %pos position.
- **/
-EXPORT gint audacious_remote_get_playlist_time(DBusGProxy *proxy, guint pos) {
- gint out = 0;
- org_atheme_audacious_song_frames(proxy, pos, &out, &error);
- g_clear_error(&error);
- return out;
-}
-
-/**
- * Queries Audacious about the current audio format.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[out] rate Pointer to an integer containing the bitrate.
- * @param[out] freq Pointer to an integer containing the frequency.
- * @param[out] nch Pointer to an integer containing the number of channels.
- **/
-EXPORT void audacious_remote_get_info(DBusGProxy *proxy, gint *rate, gint *freq,
- gint *nch) {
- org_atheme_audacious_info(proxy, rate, freq, nch, &error);
- g_clear_error(&error);
-}
-
-/**
- * Toggles the main window's visibility.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] show Whether or not to show the main window.
- **/
-EXPORT void audacious_remote_main_win_toggle(DBusGProxy *proxy, gboolean show) {
- org_atheme_audacious_show_main_win(proxy, show, &error);
- g_clear_error(&error);
-}
-
-/**
- * Queries Audacious about the main window's visibility.
- *
- * @param[in] proxy DBus proxy for audacious
- * @return TRUE if visible, FALSE otherwise.
- **/
-EXPORT gboolean audacious_remote_is_main_win(DBusGProxy *proxy) {
- gboolean visible = TRUE;
- org_atheme_audacious_main_win_visible(proxy, &visible, &error);
- g_clear_error(&error);
- return visible;
-}
-
-/**
- * Tells audacious to show the preferences pane.
- *
- * @param[in] proxy DBus proxy for audacious
- **/
-EXPORT void audacious_remote_show_prefs_box(DBusGProxy *proxy) {
- audacious_remote_toggle_prefs_box(proxy, TRUE);
-}
-
-/**
- * Tells audacious to show/hide the preferences pane.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] show shows/hides
- **/
-EXPORT void audacious_remote_toggle_prefs_box(DBusGProxy *proxy, gboolean show) {
- org_atheme_audacious_show_prefs_box(proxy, show, &error);
- g_clear_error(&error);
-}
-
-/**
- * Tells audacious to show the about box.
- *
- * @param[in] proxy DBus proxy for audacious
- **/
-EXPORT void audacious_remote_show_about_box(DBusGProxy *proxy) {
- audacious_remote_toggle_about_box(proxy, TRUE);
-}
-
-/**
- * Tells audacious to show/hide the about box.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] show shows/hides
- **/
-EXPORT void audacious_remote_toggle_about_box(DBusGProxy *proxy, gboolean show) {
- org_atheme_audacious_show_about_box(proxy, show, &error);
- g_clear_error(&error);
-}
-
-/**
- * Tells audacious to set the always-on-top feature.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] ontop Boolean value whether or not Audacious should be always-on-top.
- **/
-EXPORT void audacious_remote_toggle_aot(DBusGProxy *proxy, gboolean ontop) {
- org_atheme_audacious_toggle_aot(proxy, ontop, &error);
- g_clear_error(&error);
-}
-
-/**
- * Tells audacious to display the open files pane.
- *
- * @param[in] proxy DBus proxy for audacious
- **/
-EXPORT void audacious_remote_eject(DBusGProxy *proxy) {
- org_atheme_audacious_eject(proxy, &error);
- g_clear_error(&error);
-}
-
-/**
- * audacious_remote_playlist_prev:
- * @param[in] proxy DBus proxy for audacious
- *
- * Tells audacious to move backwards in the playlist.
- **/
-EXPORT void audacious_remote_playlist_prev(DBusGProxy *proxy) {
- org_atheme_audacious_reverse(proxy, &error);
- g_clear_error(&error);
-}
-
-/**
- * Tells audacious to move forward in the playlist.
- *
- * @param[in] proxy DBus proxy for audacious
- **/
-EXPORT void audacious_remote_playlist_next(DBusGProxy *proxy) {
- org_atheme_audacious_advance(proxy, &error);
- g_clear_error(&error);
-}
-
-/**
- * Tells audacious to add an URI to the playlist.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] string The URI to add.
- **/
-EXPORT void audacious_remote_playlist_add_url_string(DBusGProxy *proxy,
- gchar *string) {
- org_atheme_audacious_add_url(proxy, string, &error);
- g_clear_error(&error);
-}
-
-/**
- * Check if an Audacious instance is running.
- *
- * @param[in] proxy DBus proxy for audacious
- * @return TRUE if yes, otherwise FALSE.
- **/
-EXPORT gboolean audacious_remote_is_running(DBusGProxy *proxy) {
- char *string = NULL;
- org_atheme_audacious_version(proxy, &string, &error);
- g_clear_error(&error);
- if(string) {
- g_free(string);
- return TRUE;
- }
- else
- return FALSE;
-}
-
-/**
- * Tells audacious to toggle the repeat feature.
- *
- * @param[in] proxy DBus proxy for audacious
- **/
-EXPORT void audacious_remote_toggle_repeat(DBusGProxy *proxy) {
- org_atheme_audacious_toggle_repeat(proxy, &error);
- g_clear_error(&error);
-}
-
-/**
- * Tells audacious to toggle the shuffle feature.
- *
- * @param[in] proxy DBus proxy for audacious
- **/
-EXPORT void audacious_remote_toggle_shuffle(DBusGProxy *proxy) {
- org_atheme_audacious_toggle_shuffle (proxy, &error);
- g_clear_error(&error);
-}
-
-EXPORT void audacious_remote_toggle_stop_after (DBusGProxy * proxy)
-{
- org_atheme_audacious_toggle_stop_after (proxy, & error);
- g_clear_error (& error);
-}
-
-/**
- * Queries audacious about whether or not the repeat feature is active.
- *
- * @param[in] proxy DBus proxy for audacious
- * @return TRUE if yes, otherwise FALSE.
- **/
-EXPORT gboolean audacious_remote_is_repeat(DBusGProxy *proxy) {
- gboolean is_repeat;
- org_atheme_audacious_repeat(proxy, &is_repeat, &error);
- g_clear_error(&error);
- return is_repeat;
-}
-
-/**
- * Queries audacious about whether or not the shuffle feature is active.
- *
- * @param[in] proxy DBus proxy for audacious
- * @return TRUE if yes, otherwise FALSE.
- **/
-EXPORT gboolean audacious_remote_is_shuffle(DBusGProxy *proxy) {
- gboolean is_shuffle;
- org_atheme_audacious_shuffle(proxy, &is_shuffle, &error);
- g_clear_error(&error);
- return is_shuffle;
-}
-
-EXPORT gboolean audacious_remote_is_stop_after (DBusGProxy * proxy)
-{
- gboolean is_stop_after;
- org_atheme_audacious_stop_after (proxy, & is_stop_after, & error);
- g_clear_error (& error);
- return is_stop_after;
-}
-
-/**
- * Queries audacious about the equalizer settings.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[out] preamp Pointer to value for preamp setting.
- * @param[out] bands Pointer to array of band settings.
- **/
-EXPORT void audacious_remote_get_eq(DBusGProxy *proxy, gdouble *preamp, GArray **bands) {
- org_atheme_audacious_get_eq(proxy, preamp, bands, &error);
- g_clear_error(&error);
-}
-
-/**
- * Queries audacious about the equalizer preamp's setting.
- *
- * @param[in] proxy DBus proxy for audacious
- * @return The equalizer preamp's setting.
- **/
-EXPORT gdouble audacious_remote_get_eq_preamp(DBusGProxy *proxy) {
- gdouble preamp = 0.0;
-
- org_atheme_audacious_get_eq_preamp(proxy, &preamp, &error);
- g_clear_error(&error);
-
- return preamp;
-}
-
-/**
- * Queries audacious about an equalizer band's value.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] band Which band to lookup the value for.
- * @return The equalizer band's value.
- **/
-EXPORT gdouble audacious_remote_get_eq_band(DBusGProxy *proxy, gint band) {
- gdouble value = 0.0;
-
- org_atheme_audacious_get_eq_band(proxy, band, &value, &error);
- g_clear_error(&error);
-
- return value;
-}
-
-/**
- * Tells audacious to set the equalizer up using the provided values.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] preamp Value for preamp setting.
- * @param[in] bands Array of band settings.
- **/
-EXPORT void audacious_remote_set_eq(DBusGProxy *proxy, gdouble preamp, GArray *bands) {
- org_atheme_audacious_set_eq(proxy, preamp, bands, &error);
- g_clear_error(&error);
-}
-
-/**
- * Tells audacious to set the equalizer's preamp setting.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] preamp Value for preamp setting.
- **/
-EXPORT void audacious_remote_set_eq_preamp(DBusGProxy *proxy, gdouble preamp) {
- org_atheme_audacious_set_eq_preamp(proxy, preamp, &error);
- g_clear_error(&error);
-}
-
-/**
- * Tells audacious to set an equalizer band's setting.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] band The band to set the value for.
- * @param[in] value The value to set that band to.
- **/
-EXPORT void audacious_remote_set_eq_band(DBusGProxy *proxy, gint band, gdouble value) {
- org_atheme_audacious_set_eq_band(proxy, band, value, &error);
- g_clear_error(&error);
-}
-
-/**
- * Requests audacious to quit.
- *
- * @param[in] proxy DBus proxy for audacious
- **/
-EXPORT void audacious_remote_quit(DBusGProxy *proxy) {
- org_atheme_audacious_quit(proxy, &error);
- g_clear_error(&error);
-}
-
-/**
- * Tells audacious to toggle between play and pause.
- *
- * @param[in] proxy DBus proxy for audacious
- **/
-EXPORT void audacious_remote_play_pause(DBusGProxy *proxy) {
- org_atheme_audacious_play_pause(proxy, &error);
-}
-
-/**
- * Tells audacious to add an URI to the playlist at a specific position.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] string The URI to add.
- * @param[in] pos The position to add the URI at.
- **/
-EXPORT void audacious_remote_playlist_ins_url_string(DBusGProxy *proxy,
- gchar *string, guint pos) {
- org_atheme_audacious_playlist_ins_url_string (proxy, string, pos, &error);
- g_clear_error(&error);
-}
-
-/**
- * Tells audacious to add a playlist entry to the playqueue.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] pos The playlist position to add to the queue.
- **/
-EXPORT void audacious_remote_playqueue_add(DBusGProxy *proxy, guint pos) {
- org_atheme_audacious_playqueue_add (proxy, pos, &error);
- g_clear_error(&error);
-}
-
-/**
- * Tells audacious to remove a playlist entry from the playqueue.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] pos The playlist position to remove from the queue.
- **/
-EXPORT void audacious_remote_playqueue_remove(DBusGProxy *proxy, guint pos) {
- org_atheme_audacious_playqueue_remove (proxy, pos, &error);
- g_clear_error(&error);
-}
-
-/**
- * Queries audacious about the playqueue's length.
- *
- * @bug This returns the length of the playlist, NOT the length of the playqueue.
- *
- * @param[in] proxy DBus proxy for audacious
- * @return The number of entries in the playqueue.
- **/
-EXPORT gint audacious_remote_get_playqueue_length(DBusGProxy *proxy) {
- gint len = 0;
- org_atheme_audacious_length(proxy, &len, &error);
- g_clear_error(&error);
- return len;
-}
-
-/**
- * Tells audacious to toggle the no-playlist-advance feature.
- *
- * @param[in] proxy DBus proxy for audacious
- **/
-EXPORT void audacious_remote_toggle_advance(DBusGProxy *proxy) {
- org_atheme_audacious_toggle_auto_advance(proxy, &error);
- g_clear_error(&error);
-}
-
-/**
- * audacious_remote_is_advance:
- * @param[in] proxy DBus proxy for audacious
- *
- * Queries audacious about whether or not the no-playlist-advance feature is active.
- *
- * @return TRUE if yes, otherwise FALSE.
- **/
-EXPORT gboolean audacious_remote_is_advance(DBusGProxy *proxy) {
- gboolean is_advance = FALSE;
- org_atheme_audacious_auto_advance(proxy, &is_advance, &error);
- g_clear_error(&error);
- return is_advance;
-}
-
-/**
- * Tells audacious to show the Jump-to-File pane.
- *
- * @param[in] proxy DBus proxy for audacious
- **/
-EXPORT void audacious_remote_show_jtf_box(DBusGProxy *proxy) {
- audacious_remote_toggle_jtf_box(proxy, TRUE);
-}
-
-/**
- * Tells audacious to show/hide the Jump-to-File pane.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] show shows/hides jtf pane
- **/
-EXPORT void audacious_remote_toggle_jtf_box(DBusGProxy *proxy, gboolean show) {
- org_atheme_audacious_show_jtf_box(proxy, show, &error);
- g_clear_error(&error);
-}
-
-/**
- * Tells audacious to show the filebrowser dialog.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] show shows/hides filebrowser
- **/
-EXPORT void audacious_remote_toggle_filebrowser(DBusGProxy *proxy, gboolean show) {
- org_atheme_audacious_show_filebrowser(proxy, show, &error);
- g_clear_error(&error);
-}
-
-/**
- * audacious_remote_playqueue_clear:
- * @param[in] proxy DBus proxy for audacious
- *
- * Tells audacious to clear the playqueue.
- **/
-EXPORT void audacious_remote_playqueue_clear(DBusGProxy *proxy) {
- org_atheme_audacious_playqueue_clear(proxy, &error);
- g_clear_error(&error);
-}
-
-/**
- * Queries audacious about whether or not a playlist entry is in the playqueue.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] pos Position to check queue for.
- * @return TRUE if yes, FALSE otherwise.
- **/
-EXPORT gboolean audacious_remote_playqueue_is_queued(DBusGProxy *proxy, guint pos) {
- gboolean is_queued;
- org_atheme_audacious_playqueue_is_queued (proxy, pos, &is_queued, &error);
- g_clear_error(&error);
- return is_queued;
-}
-
-/**
- * Queries audacious about what the playqueue position is for a playlist entry.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] pos Position to check queue for.
- * @return the playqueue position for a playlist entry
- **/
-EXPORT gint audacious_remote_get_playqueue_queue_position(DBusGProxy *proxy, guint pos) {
- guint qpos = 0;
- org_atheme_audacious_queue_get_queue_pos (proxy, pos, &qpos, &error);
- g_clear_error(&error);
- return qpos;
-}
-
-/**
- * Queries audacious what is the playlist position for given a playqueue entry index.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] qpos Position to check queue for.
- *
- * @return the playlist position for a playqueue entry
- **/
-EXPORT gint audacious_remote_get_playqueue_list_position(DBusGProxy *proxy, guint qpos) {
- guint pos = 0;
- org_atheme_audacious_queue_get_list_pos (proxy, qpos, &pos, &error);
- g_clear_error(&error);
- return pos;
-}
-
-/**
- * Tells audacious to add an URI to a temporary playlist.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] string The URI to enqueue to a temporary playlist.
- **/
-EXPORT void audacious_remote_playlist_enqueue_to_temp(DBusGProxy *proxy,
- gchar *string) {
- org_atheme_audacious_playlist_enqueue_to_temp(proxy, string, &error);
- g_clear_error(&error);
-}
-
-/**
- * Queries Audacious about a playlist entry's tuple information.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] field The name of the tuple field to retrieve.
- * @param[in] pos The playlist position to query for.
- * @return The requested field's data for the entry in the playlist at %pos position.
- **/
-EXPORT gchar *audacious_get_tuple_field_data(DBusGProxy *proxy, gchar *field,
- guint pos) {
- GValue value = {0};
- gchar *s = NULL;
-
- org_atheme_audacious_song_tuple(proxy, pos, field, &value, &error);
-
- g_clear_error(&error);
-
- if (G_IS_VALUE(&value) == FALSE)
- return NULL;
-
- /* I think the original "purpose" of using g_strescape() here
- * has probably been to escape only \n, \t, \r, etc. but the function
- * actually escapes all non-ASCII characters. Which is bad, since we
- * are using UTF-8. -- ccr
- */
- if (G_VALUE_HOLDS_STRING(&value))
- //s = g_strescape(g_value_get_string(&value), NULL);
- s = g_strdup(g_value_get_string(&value));
- else if (g_value_type_transformable(G_VALUE_TYPE(&value), G_TYPE_STRING))
- {
- GValue tmp_value = { 0, };
-
- g_value_init(&tmp_value, G_TYPE_STRING);
- g_value_transform(&value, &tmp_value);
-
- //s = g_strescape(g_value_get_string(&tmp_value), NULL);
- s = g_strdup(g_value_get_string(&tmp_value));
-
- g_value_unset(&tmp_value);
- }
- else
- s = g_strdup("<unknown type>");
-
- g_value_unset(&value);
- return s;
-}
-
-/**
- * Toggles the equalizer.
- *
- * @param[in] proxy DBus proxy for audacious
- * @param[in] active Whether or not to activate the equalizer.
- **/
-EXPORT void audacious_remote_eq_activate(DBusGProxy *proxy, gboolean active) {
- org_atheme_audacious_equalizer_activate (proxy, active, &error);
- g_clear_error(&error);
-}
-
-/**
- * Returns a array of strings with available tuple field names.
- *
- * @param[in] proxy DBus proxy for audacious
- * @return Array of strings.
- **/
-EXPORT gchar **audacious_remote_get_tuple_fields(DBusGProxy *proxy) {
- gchar **res = NULL;
- org_atheme_audacious_get_tuple_fields (proxy, &res, &error);
- g_clear_error(&error);
- return res;
-}
-
-/**
- * Returns the active playlist name.
- */
-EXPORT gchar *audacious_remote_playlist_get_active_name(DBusGProxy *proxy) {
- char *string = NULL;
- org_atheme_audacious_get_active_playlist_name (proxy, &string, &error);
- g_clear_error(&error);
-
- return (string ? string : NULL);
-}
diff --git a/src/libaudclient/audctrl.h b/src/libaudclient/audctrl.h
deleted file mode 100644
index 6e316f8..0000000
--- a/src/libaudclient/audctrl.h
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * audctrl.h
- * Copyright 2007-2011 Ben Tucker, William Pitcock, Yoshiki Yazawa,
- * Matti HÀmÀlÀinen, 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 AUDACIOUS_AUDCTRL_H
-#define AUDACIOUS_AUDCTRL_H
-
-#include <glib.h>
-#include <dbus/dbus-glib.h>
-
-G_BEGIN_DECLS
-
-void audacious_remote_playlist(DBusGProxy *proxy, gchar **list, gint num,
- gboolean enqueue);
-gchar *audacious_remote_get_version(DBusGProxy *proxy);
-void audacious_remote_playlist_add(DBusGProxy *proxy, GList *list);
-void audacious_remote_playlist_delete(DBusGProxy *proxy, guint pos);
-void audacious_remote_play(DBusGProxy *proxy);
-void audacious_remote_pause(DBusGProxy *proxy);
-void audacious_remote_stop(DBusGProxy *proxy);
-gboolean audacious_remote_is_playing(DBusGProxy *proxy);
-gboolean audacious_remote_is_paused(DBusGProxy *proxy);
-gint audacious_remote_get_playlist_pos(DBusGProxy *proxy);
-void audacious_remote_set_playlist_pos(DBusGProxy *proxy, guint pos);
-gint audacious_remote_get_playlist_length(DBusGProxy *proxy);
-void audacious_remote_playlist_clear(DBusGProxy *proxy);
-gint audacious_remote_get_output_time(DBusGProxy *proxy);
-void audacious_remote_jump_to_time(DBusGProxy *proxy, guint pos);
-void audacious_remote_get_volume(DBusGProxy *proxy, gint *vl, gint *vr);
-gint audacious_remote_get_main_volume(DBusGProxy *proxy);
-gint audacious_remote_get_balance(DBusGProxy *proxy);
-void audacious_remote_set_volume(DBusGProxy *proxy, gint vl, gint vr);
-void audacious_remote_set_main_volume(DBusGProxy *proxy, gint v);
-void audacious_remote_set_balance(DBusGProxy *proxy, gint b);
-gchar *audacious_remote_get_skin(DBusGProxy *proxy);
-void audacious_remote_set_skin(DBusGProxy *proxy, gchar *skinfile);
-gchar *audacious_remote_get_playlist_file(DBusGProxy *proxy, guint pos);
-gchar *audacious_remote_get_playlist_title(DBusGProxy *proxy, guint pos);
-gint audacious_remote_get_playlist_time(DBusGProxy *proxy, guint pos);
-void audacious_remote_get_info(DBusGProxy *proxy, gint *rate, gint *freq,
- gint *nch);
-void audacious_remote_main_win_toggle(DBusGProxy *proxy, gboolean show);
-gboolean audacious_remote_is_main_win(DBusGProxy *proxy);
-void audacious_remote_show_prefs_box(DBusGProxy *proxy);
-void audacious_remote_toggle_aot(DBusGProxy *proxy, gboolean ontop);
-void audacious_remote_eject(DBusGProxy *proxy);
-void audacious_remote_playlist_prev(DBusGProxy *proxy);
-void audacious_remote_playlist_next(DBusGProxy *proxy);
-void audacious_remote_playlist_add_url_string(DBusGProxy *proxy,
- gchar *string);
-gboolean audacious_remote_is_running(DBusGProxy *proxy);
-void audacious_remote_toggle_repeat(DBusGProxy *proxy);
-void audacious_remote_toggle_shuffle(DBusGProxy *proxy);
-void audacious_remote_toggle_stop_after (DBusGProxy * proxy);
-gboolean audacious_remote_is_repeat(DBusGProxy *proxy);
-gboolean audacious_remote_is_shuffle(DBusGProxy *proxy);
-gboolean audacious_remote_is_stop_after (DBusGProxy * proxy);
-
-void audacious_remote_get_eq(DBusGProxy *proxy, gdouble *preamp,
- GArray **bands);
-gdouble audacious_remote_get_eq_preamp(DBusGProxy *proxy);
-gdouble audacious_remote_get_eq_band(DBusGProxy *proxy, gint band);
-void audacious_remote_set_eq(DBusGProxy *proxy, gdouble preamp,
- GArray *bands);
-void audacious_remote_set_eq_preamp(DBusGProxy *proxy, gdouble preamp);
-void audacious_remote_set_eq_band(DBusGProxy *proxy, gint band,
- gdouble value);
-
-/* Added in XMMS 1.2.1 */
-void audacious_remote_quit(DBusGProxy *proxy);
-
-/* Added in XMMS 1.2.6 */
-void audacious_remote_play_pause(DBusGProxy *proxy);
-void audacious_remote_playlist_ins_url_string(DBusGProxy *proxy,
- gchar *string, guint pos);
-
-/* Added in XMMS 1.2.11 */
-void audacious_remote_playqueue_add(DBusGProxy *proxy, guint pos);
-void audacious_remote_playqueue_remove(DBusGProxy *proxy, guint pos);
-gint audacious_remote_get_playqueue_length(DBusGProxy *proxy);
-void audacious_remote_toggle_advance(DBusGProxy *proxy);
-gboolean audacious_remote_is_advance(DBusGProxy *proxy);
-
-/* Added in Audacious 1.1 */
-void audacious_remote_show_jtf_box(DBusGProxy *proxy);
-void audacious_remote_playqueue_clear(DBusGProxy *proxy);
-gboolean audacious_remote_playqueue_is_queued(DBusGProxy *proxy, guint pos);
-gint audacious_remote_get_playqueue_list_position(DBusGProxy *proxy, guint qpos);
-gint audacious_remote_get_playqueue_queue_position(DBusGProxy *proxy, guint pos);
-
-/* Added in Audacious 1.2 */
-void audacious_set_session_uri(DBusGProxy *proxy, gchar *uri);
-gchar *audacious_get_session_uri(DBusGProxy *proxy);
-void audacious_set_session_type(DBusGProxy *proxy, gint type);
-
-/* Added in Audacious 1.3 */
-void audacious_remote_playlist_enqueue_to_temp(DBusGProxy *proxy,
- gchar *string);
-gchar *audacious_get_tuple_field_data(DBusGProxy *proxy, gchar *field,
- guint pos);
-/* Added in Audacious 1.4 */
-void audacious_remote_show_about_box(DBusGProxy *proxy);
-void audacious_remote_toggle_about_box(DBusGProxy *proxy, gboolean show);
-void audacious_remote_toggle_jtf_box(DBusGProxy *proxy, gboolean show);
-void audacious_remote_toggle_prefs_box(DBusGProxy *proxy, gboolean show);
-void audacious_remote_toggle_filebrowser(DBusGProxy *proxy, gboolean show);
-void audacious_remote_eq_activate(DBusGProxy *proxy, gboolean active);
-
-/* Added in Audacious 1.9 */
-gchar **audacious_remote_get_tuple_fields(DBusGProxy *proxy);
-
-/* Added in Audacious 2.3 */
-void audacious_remote_playlist_open_list (DBusGProxy * proxy, GList * list);
-void audacious_remote_playlist_open_list_to_temp (DBusGProxy * proxy, GList *
- list);
-
-/* Added in Audacious 2.4 */
-gchar *audacious_remote_playlist_get_active_name(DBusGProxy *proxy);
-
-G_END_DECLS
-
-#endif /* AUDACIOUS_AUDCTRL_H */
diff --git a/src/libaudcore/Makefile b/src/libaudcore/Makefile
index c823ae7..8f380f7 100644
--- a/src/libaudcore/Makefile
+++ b/src/libaudcore/Makefile
@@ -1,25 +1,33 @@
SHARED_LIB = ${LIB_PREFIX}audcore${LIB_SUFFIX}
-LIB_MAJOR = 1
+LIB_MAJOR = 2
LIB_MINOR = 0
SRCS = audio.c \
audstrings.c \
+ charset.c \
eventqueue.c \
hook.c \
index.c \
+ inifile.c \
+ multihash.c \
strpool.c \
+ tinylock.c \
tuple.c \
tuple_compiler.c \
tuple_formatter.c \
vfs.c \
vfs_async.c \
- vfs_common.c
+ vfs_common.c \
+ vfs_local.c
INCLUDES = audio.h \
audstrings.h \
core.h \
hook.h \
index.h \
+ inifile.h \
+ multihash.h \
+ tinylock.h \
tuple.h \
vfs.h \
vfs_async.h
@@ -32,7 +40,10 @@ includesubdir = libaudcore
CPPFLAGS := -I.. -I../.. \
${CPPFLAGS} \
${GLIB_CFLAGS} \
+ ${LIBGUESS_CFLAGS}
CFLAGS += ${LIB_CFLAGS}
-LIBS += ${GLIB_LIBS} -lm
+LIBS += -lm \
+ ${GLIB_LIBS} \
+ ${LIBGUESS_LIBS}
diff --git a/src/libaudcore/audio.c b/src/libaudcore/audio.c
index f866509..25246be 100644
--- a/src/libaudcore/audio.c
+++ b/src/libaudcore/audio.c
@@ -1,6 +1,6 @@
/*
* audio.c
- * Copyright 2009-2012 John Lindgren, MichaƂ Lipski, and Anders Johansson
+ * Copyright 2009-2013 John Lindgren, MichaƂ Lipski, and Anders Johansson
*
* 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 <math.h>
#include "audio.h"
+#include "core.h"
#define INTERLACE_LOOP(TYPE) \
for (int c = 0; c < channels; c ++) \
@@ -173,7 +174,7 @@ EXPORT void audio_from_int (const void * in, int format, float * out, int sample
{
int entry;
- for (entry = 0; entry < G_N_ELEMENTS (convert_table); entry ++)
+ for (entry = 0; entry < ARRAY_LEN (convert_table); entry ++)
{
if (convert_table[entry].format == format)
{
@@ -187,7 +188,7 @@ EXPORT void audio_to_int (const float * in, void * out, int format, int samples)
{
int entry;
- for (entry = 0; entry < G_N_ELEMENTS (convert_table); entry ++)
+ for (entry = 0; entry < ARRAY_LEN (convert_table); entry ++)
{
if (convert_table[entry].format == format)
{
diff --git a/src/libaudcore/audio.h.in b/src/libaudcore/audio.h.in
index 19cfef2..2e9dd5a 100644
--- a/src/libaudcore/audio.h.in
+++ b/src/libaudcore/audio.h.in
@@ -1,6 +1,6 @@
/*
* audio.h
- * Copyright 2009-2011 John Lindgren
+ * Copyright 2009-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/libaudcore/audstrings.c b/src/libaudcore/audstrings.c
index eb4b8f1..c1d878b 100644
--- a/src/libaudcore/audstrings.c
+++ b/src/libaudcore/audstrings.c
@@ -1,7 +1,6 @@
/*
* audstrings.c
- * Copyright 2009-2011 John Lindgren
- * Copyright 2010 William Pitcock
+ * Copyright 2009-2012 John Lindgren and William Pitcock
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -22,19 +21,67 @@
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
-#include <glib.h>
#include <string.h>
-#include <ctype.h>
-#include <locale.h>
+
+#include <glib.h>
#include <audacious/i18n.h>
#include "audstrings.h"
+#include "index.h"
+
+static const char ascii_to_hex[256] = {
+ ['0'] = 0x0, ['1'] = 0x1, ['2'] = 0x2, ['3'] = 0x3, ['4'] = 0x4,
+ ['5'] = 0x5, ['6'] = 0x6, ['7'] = 0x7, ['8'] = 0x8, ['9'] = 0x9,
+ ['a'] = 0xa, ['b'] = 0xb, ['c'] = 0xc, ['d'] = 0xd, ['e'] = 0xe, ['f'] = 0xf,
+ ['A'] = 0xa, ['B'] = 0xb, ['C'] = 0xc, ['D'] = 0xd, ['E'] = 0xe, ['F'] = 0xf
+};
+
+static const char hex_to_ascii[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+};
+
+static const char uri_legal_table[256] = {
+ ['0'] = 1, ['1'] = 1, ['2'] = 1, ['3'] = 1, ['4'] = 1,
+ ['5'] = 1, ['6'] = 1, ['7'] = 1, ['8'] = 1, ['9'] = 1,
+ ['a'] = 1, ['b'] = 1, ['c'] = 1, ['d'] = 1, ['e'] = 1, ['f'] = 1, ['g'] = 1,
+ ['h'] = 1, ['i'] = 1, ['j'] = 1, ['k'] = 1, ['l'] = 1, ['m'] = 1, ['n'] = 1,
+ ['o'] = 1, ['p'] = 1, ['q'] = 1, ['r'] = 1, ['s'] = 1, ['t'] = 1, ['u'] = 1,
+ ['v'] = 1, ['w'] = 1, ['x'] = 1, ['y'] = 1, ['z'] = 1,
+ ['A'] = 1, ['B'] = 1, ['C'] = 1, ['D'] = 1, ['E'] = 1, ['F'] = 1, ['G'] = 1,
+ ['H'] = 1, ['I'] = 1, ['J'] = 1, ['K'] = 1, ['L'] = 1, ['M'] = 1, ['N'] = 1,
+ ['O'] = 1, ['P'] = 1, ['Q'] = 1, ['R'] = 1, ['S'] = 1, ['T'] = 1, ['U'] = 1,
+ ['V'] = 1, ['W'] = 1, ['X'] = 1, ['Y'] = 1, ['Z'] = 1,
+ ['-'] = 1, ['_'] = 1, ['.'] = 1, ['~'] = 1, ['/'] = 1
+};
+
+static const char swap_case[256] =
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
+ "\0ABCDEFGHIJKLMNOPQRSTUVWXYZ\0\0\0\0\0";
+
+#define FROM_HEX(c) (ascii_to_hex[(unsigned char) (c)])
+#define TO_HEX(i) (hex_to_ascii[(i) & 15])
+#define IS_LEGAL(c) (uri_legal_table[(unsigned char) (c)])
+#define SWAP_CASE(c) (swap_case[(unsigned char) (c)])
+
+EXPORT char * str_printf (const char * format, ...)
+{
+ va_list args;
+ va_start (args, format);
+
+ char * str = str_vprintf (format, args);
-#define FROM_HEX(c) ((c) < 'A' ? (c) - '0' : (c) < 'a' ? 10 + (c) - 'A' : 10 + (c) - 'a')
-#define TO_HEX(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10)
-#define IS_LEGAL(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z') \
- || ((c) >= '0' && (c) <= '9') || (strchr ("-_.~/", (c))))
+ va_end (args);
+ return str;
+}
+
+EXPORT char * str_vprintf (const char * format, va_list args)
+{
+ VSPRINTF (buf, format, args);
+ return str_get (buf);
+}
EXPORT bool_t str_has_prefix_nocase (const char * str, const char * prefix)
{
@@ -52,60 +99,154 @@ EXPORT bool_t str_has_suffix_nocase (const char * str, const char * suffix)
return ! g_ascii_strcasecmp (str + len1 - len2, suffix);
}
-static char * (* str_to_utf8_impl) (const char *) = NULL;
-static char * (* str_to_utf8_full_impl) (const char *, int, int *, int *) = NULL;
-
-EXPORT void str_set_utf8_impl (char * (* stu_impl) (const char *),
- char * (* stuf_impl) (const char *, int, int *, int *))
+EXPORT char * strstr_nocase (const char * haystack, const char * needle)
{
- str_to_utf8_impl = stu_impl;
- str_to_utf8_full_impl = stuf_impl;
+ while (1)
+ {
+ const char * ap = haystack;
+ const char * bp = needle;
+
+ while (1)
+ {
+ char a = * ap ++;
+ char b = * bp ++;
+
+ if (! b) /* all of needle matched */
+ return (char *) haystack;
+ if (! a) /* end of haystack reached */
+ return NULL;
+
+ if (a != b && a != SWAP_CASE (b))
+ break;
+ }
+
+ haystack ++;
+ }
}
-EXPORT char * str_to_utf8 (const char * str)
+EXPORT char * strstr_nocase_utf8 (const char * haystack, const char * needle)
{
- g_return_val_if_fail (str_to_utf8_impl, NULL);
- return str_to_utf8_impl (str);
+ while (1)
+ {
+ const char * ap = haystack;
+ const char * bp = needle;
+
+ while (1)
+ {
+ gunichar a = g_utf8_get_char (ap);
+ gunichar b = g_utf8_get_char (bp);
+
+ if (! b) /* all of needle matched */
+ return (char *) haystack;
+ if (! a) /* end of haystack reached */
+ return NULL;
+
+ if (a != b && (a < 128 ? SWAP_CASE (a) != b :
+ g_unichar_tolower (a) != g_unichar_tolower (b)))
+ break;
+
+ ap = g_utf8_next_char (ap);
+ bp = g_utf8_next_char (bp);
+ }
+
+ haystack = g_utf8_next_char (haystack);
+ }
}
-EXPORT char * str_to_utf8_full (const char * str, int len, int * bytes_read, int * bytes_written)
+EXPORT char * str_tolower_utf8 (const char * str)
{
- g_return_val_if_fail (str_to_utf8_full_impl, NULL);
- return str_to_utf8_full_impl (str, len, bytes_read, bytes_written);
+ char buf[6 * strlen (str) + 1];
+ const char * get = str;
+ char * set = buf;
+ gunichar c;
+
+ while ((c = g_utf8_get_char (get)))
+ {
+ if (c < 128)
+ * set ++ = g_ascii_tolower (c);
+ else
+ set += g_unichar_to_utf8 (g_unichar_tolower (c), set);
+
+ get = g_utf8_next_char (get);
+ }
+
+ * set = 0;
+
+ return str_get (buf);
}
-EXPORT void string_replace_char (char * string, char old_c, char new_c)
+EXPORT void str_replace_char (char * string, char old_c, char new_c)
{
while ((string = strchr (string, old_c)))
* string ++ = new_c;
}
+EXPORT void str_itoa (int x, char * buf, int bufsize)
+{
+ if (! bufsize)
+ return;
+
+ if (x < 0)
+ {
+ if (bufsize > 1)
+ {
+ * buf ++ = '-';
+ bufsize --;
+ }
+
+ x = -x;
+ }
+
+ char * rev = buf + bufsize - 1;
+ * rev = 0;
+
+ while (rev > buf)
+ {
+ * (-- rev) = '0' + x % 10;
+ if (! (x /= 10))
+ break;
+ }
+
+ while ((* buf ++ = * rev ++));
+}
+
/* Percent-decodes up to <len> bytes of <str> to <out>, which must be large
* enough to hold the decoded string (i.e., (len + 1) bytes). If <len> is
* negative, decodes all of <str>. */
EXPORT void str_decode_percent (const char * str, int len, char * out)
{
+ const char * nul;
+
if (len < 0)
- len = INT_MAX;
+ len = strlen (str);
+ else if ((nul = memchr (str, 0, len)))
+ len = nul - str;
- while (len --)
+ while (1)
{
- char c = * str ++;
- if (! c)
+ const char * p = memchr (str, '%', len);
+ if (! p)
break;
- if (c == '%' && len >= 2 && str[0] && str[1])
- {
- c = (FROM_HEX (str[0]) << 4) | FROM_HEX (str[1]);
- str += 2;
- len -= 2;
- }
+ int block = p - str;
+ memmove (out, str, block);
+
+ str += block;
+ out += block;
+ len -= block;
+
+ if (len < 3)
+ break;
+
+ * out ++ = (FROM_HEX (str[1]) << 4) | FROM_HEX (str[2]);
- * out ++ = c;
+ str += 3;
+ len -= 3;
}
- * out = 0;
+ memmove (out, str, len);
+ out[len] = 0;
}
/* Percent-encodes up to <len> bytes of <str> to <out>, which must be large
@@ -114,14 +255,16 @@ EXPORT void str_decode_percent (const char * str, int len, char * out)
EXPORT void str_encode_percent (const char * str, int len, char * out)
{
+ const char * nul;
+
if (len < 0)
- len = INT_MAX;
+ len = strlen (str);
+ else if ((nul = memchr (str, 0, len)))
+ len = nul - str;
while (len --)
{
char c = * str ++;
- if (! c)
- break;
if (IS_LEGAL (c))
* out ++ = c;
@@ -136,62 +279,101 @@ EXPORT void str_encode_percent (const char * str, int len, char * out)
* out = 0;
}
-/* Like g_filename_to_uri, but converts the filename from the system locale to
- * UTF-8 before percent-encoding. On Windows, replaces '\' with '/' and adds a
- * leading '/'. */
+EXPORT void filename_normalize (char * filename)
+{
+#ifdef _WIN32
+ /* convert slash to backslash on Windows */
+ str_replace_char (filename, '/', '\\');
+#endif
-EXPORT char * filename_to_uri (const char * name)
+ /* remove trailing slash */
+ int len = strlen (filename);
+#ifdef _WIN32
+ if (len > 3 && filename[len - 1] == '\\') /* leave "C:\" */
+#else
+ if (len > 1 && filename[len - 1] == '/') /* leave leading "/" */
+#endif
+ filename[len - 1] = 0;
+}
+
+EXPORT char * filename_build (const char * path, const char * name)
{
- char * utf8 = g_locale_to_utf8 (name, -1, NULL, NULL, NULL);
- if (! utf8)
+ int len = strlen (path);
+
+#ifdef _WIN32
+ if (! len || path[len - 1] == '/' || path[len - 1] == '\\')
{
- const char * locale = setlocale (LC_ALL, NULL);
- fprintf (stderr, "Cannot convert filename from system locale (%s): %s\n", locale, name);
- return NULL;
+ SCONCAT2 (filename, path, name);
+ return str_get (filename);
+ }
+
+ SCONCAT3 (filename, path, "\\", name);
+ return str_get (filename);
+#else
+ if (! len || path[len - 1] == '/')
+ {
+ SCONCAT2 (filename, path, name);
+ return str_get (filename);
}
+ SCONCAT3 (filename, path, "/", name);
+ return str_get (filename);
+#endif
+}
+
#ifdef _WIN32
- string_replace_char (utf8, '\\', '/');
+#define URI_PREFIX "file:///"
+#define URI_PREFIX_LEN 8
+#else
+#define URI_PREFIX "file://"
+#define URI_PREFIX_LEN 7
#endif
- char enc[3 * strlen (utf8) + 1];
- str_encode_percent (utf8, -1, enc);
- g_free (utf8);
+/* Like g_filename_to_uri, but converts the filename from the system locale to
+ * UTF-8 before percent-encoding (except on Windows, where filenames are assumed
+ * to be UTF-8). On Windows, replaces '\' with '/' and adds a leading '/'. */
+EXPORT char * filename_to_uri (const char * name)
+{
#ifdef _WIN32
- return g_strdup_printf ("file:///%s", enc);
+ SCOPY (utf8, name);
+ str_replace_char (utf8, '\\', '/');
#else
- return g_strdup_printf ("file://%s", enc);
+ char * utf8 = str_from_locale (name, -1);
+ if (! utf8)
+ return NULL;
#endif
+
+ char enc[URI_PREFIX_LEN + 3 * strlen (utf8) + 1];
+ strcpy (enc, URI_PREFIX);
+ str_encode_percent (utf8, -1, enc + URI_PREFIX_LEN);
+
+#ifndef _WIN32
+ str_unref (utf8);
+#endif
+
+ return str_get (enc);
}
/* Like g_filename_from_uri, but converts the filename from UTF-8 to the system
- * locale after percent-decoding. On Windows, strips the leading '/' and
- * replaces '/' with '\'. */
+ * locale after percent-decoding (except on Windows, where filenames are assumed
+ * to be UTF-8). On Windows, strips the leading '/' and replaces '/' with '\'. */
EXPORT char * uri_to_filename (const char * uri)
{
+ if (strncmp (uri, URI_PREFIX, URI_PREFIX_LEN))
+ return NULL;
+
+ char buf[strlen (uri + URI_PREFIX_LEN) + 1];
+ str_decode_percent (uri + URI_PREFIX_LEN, -1, buf);
+
+ filename_normalize (buf);
+
#ifdef _WIN32
- g_return_val_if_fail (! strncmp (uri, "file:///", 8), NULL);
- char buf[strlen (uri + 8) + 1];
- str_decode_percent (uri + 8, -1, buf);
+ return str_get (buf);
#else
- g_return_val_if_fail (! strncmp (uri, "file://", 7), NULL);
- char buf[strlen (uri + 7) + 1];
- str_decode_percent (uri + 7, -1, buf);
-#endif
-#ifdef _WIN32
- string_replace_char (buf, '/', '\\');
+ return str_to_locale (buf, -1);
#endif
-
- char * name = g_locale_from_utf8 (buf, -1, NULL, NULL, NULL);
- if (! name)
- {
- const char * locale = setlocale (LC_ALL, NULL);
- fprintf (stderr, "Cannot convert filename to system locale (%s): %s\n", locale, buf);
- }
-
- return name;
}
/* Formats a URI for human-readable display. Percent-decodes and, for file://
@@ -200,26 +382,26 @@ EXPORT char * uri_to_filename (const char * uri)
EXPORT char * uri_to_display (const char * uri)
{
if (! strncmp (uri, "cdda://?", 8))
- return g_strdup_printf (_("Audio CD, track %s"), uri + 8);
+ return str_printf (_("Audio CD, track %s"), uri + 8);
char buf[strlen (uri) + 1];
-#ifdef _WIN32
- if (! strncmp (uri, "file:///", 8))
+ if (! strncmp (uri, URI_PREFIX, URI_PREFIX_LEN))
{
- str_decode_percent (uri + 8, -1, buf);
- string_replace_char (buf, '/', '\\');
- }
-#else
- if (! strncmp (uri, "file://", 7))
- str_decode_percent (uri + 7, -1, buf);
+ str_decode_percent (uri + URI_PREFIX_LEN, -1, buf);
+#ifdef _WIN32
+ str_replace_char (buf, '/', '\\');
#endif
+ }
else
str_decode_percent (uri, -1, buf);
- return g_strdup (buf);
+ return str_get (buf);
}
+#undef URI_PREFIX
+#undef URI_PREFIX_LEN
+
EXPORT void uri_parse (const char * uri, const char * * base_p, const char * * ext_p,
const char * * sub_p, int * isub_p)
{
@@ -238,9 +420,7 @@ EXPORT void uri_parse (const char * uri, const char * * base_p, const char * * e
else
sub = end;
- char buf[sub - base + 1];
- memcpy (buf, base, sub - base);
- buf[sub - base] = 0;
+ SNCOPY (buf, base, sub - base);
if ((c = strrchr (buf, '.')))
ext = base + (c - buf);
@@ -265,7 +445,8 @@ EXPORT bool_t uri_get_extension (const char * uri, char * buf, int buflen)
if (ext[0] != '.')
return FALSE;
- g_strlcpy (buf, ext + 1, buflen);
+ strncpy (buf, ext + 1, buflen - 1);
+ buf[buflen - 1] = 0;
/* remove subtunes and HTTP query strings */
char * qmark;
@@ -279,7 +460,7 @@ EXPORT bool_t uri_get_extension (const char * uri, char * buf, int buflen)
/* Non-ASCII characters are treated exactly as is. */
/* Handles NULL gracefully. */
-EXPORT int string_compare (const char * ap, const char * bp)
+EXPORT int str_compare (const char * ap, const char * bp)
{
if (ap == NULL)
return (bp == NULL) ? 0 : -1;
@@ -321,9 +502,9 @@ EXPORT int string_compare (const char * ap, const char * bp)
return 0;
}
-/* Decodes percent-encoded strings, then compares then with string_compare. */
+/* Decodes percent-encoded strings, then compares then with str_compare. */
-EXPORT int string_compare_encoded (const char * ap, const char * bp)
+EXPORT int str_compare_encoded (const char * ap, const char * bp)
{
if (ap == NULL)
return (bp == NULL) ? 0 : -1;
@@ -376,37 +557,72 @@ EXPORT int string_compare_encoded (const char * ap, const char * bp)
return 0;
}
-EXPORT char *
-str_replace_fragment(char *s, int size, const char *old, const char *new)
+EXPORT Index * str_list_to_index (const char * list, const char * delims)
{
- char *ptr = s;
- int left = strlen(s);
- int avail = size - (left + 1);
- int oldlen = strlen(old);
- int newlen = strlen(new);
- int diff = newlen - oldlen;
-
- while (left >= oldlen)
+ char dmap[256] = {0};
+
+ for (; * delims; delims ++)
+ dmap[(unsigned char) (* delims)] = 1;
+
+ Index * index = index_new ();
+ const char * word = NULL;
+
+ for (; * list; list ++)
{
- if (strncmp(ptr, old, oldlen))
+ if (dmap[(unsigned char) (* list)])
{
- left--;
- ptr++;
- continue;
+ if (word)
+ {
+ index_insert (index, -1, str_nget (word, list - word));
+ word = NULL;
+ }
}
+ else
+ {
+ if (! word)
+ {
+ word = list;
+ }
+ }
+ }
- if (diff > avail)
- break;
+ if (word)
+ index_insert (index, -1, str_get (word));
- if (diff != 0)
- memmove(ptr + oldlen + diff, ptr + oldlen, left + 1 - oldlen);
+ return index;
+}
+
+EXPORT char * index_to_str_list (Index * index, const char * sep)
+{
+ int count = index_count (index);
+ int seplen = strlen (sep);
+ int total = count ? seplen * (count - 1) : 0;
+ int lengths[count];
- memcpy(ptr, new, newlen);
- ptr += newlen;
- left -= oldlen;
+ for (int i = 0; i < count; i ++)
+ {
+ lengths[i] = strlen (index_get (index, i));
+ total += lengths[i];
}
- return s;
+ char buf[total + 1];
+ int pos = 0;
+
+ for (int i = 0; i < count; i ++)
+ {
+ if (i)
+ {
+ strcpy (buf + pos, sep);
+ pos += seplen;
+ }
+
+ strcpy (buf + pos, index_get (index, i));
+ pos += lengths[i];
+ }
+
+ buf[pos] = 0;
+
+ return str_get (buf);
}
/*
@@ -420,11 +636,11 @@ str_replace_fragment(char *s, int size, const char *old, const char *new)
* architecture or locale we have to deal with.
* - Readability, meaning that the number one is rendered "1", not "1.000".
*
- * Values are limited between -1,000,000,000 and 1,000,000,000 (inclusive) and
+ * Values between -1,000,000,000 and 1,000,000,000 (inclusive) are guaranteed to
* have an accuracy of 6 decimal places.
*/
-EXPORT bool_t string_to_int (const char * string, int * addr)
+EXPORT int str_to_int (const char * string)
{
bool_t neg = (string[0] == '-');
if (neg)
@@ -433,88 +649,41 @@ EXPORT bool_t string_to_int (const char * string, int * addr)
int val = 0;
char c;
- while ((c = * string ++))
- {
- if (c < '0' || c > '9' || val > 100000000)
- goto ERR;
-
+ while ((c = * string ++) && c >= '0' && c <= '9')
val = val * 10 + (c - '0');
- }
-
- if (val > 1000000000)
- goto ERR;
- * addr = neg ? -val : val;
- return TRUE;
-
-ERR:
- return FALSE;
+ return neg ? -val : val;
}
-EXPORT bool_t string_to_double (const char * string, double * addr)
+EXPORT double str_to_double (const char * string)
{
bool_t neg = (string[0] == '-');
if (neg)
string ++;
+ double val = str_to_int (string);
const char * p = strchr (string, '.');
- int i, f;
if (p)
{
- char buf[11];
- int len;
-
- len = p - string;
- if (len > 10)
- goto ERR;
-
- memcpy (buf, string, len);
- buf[len] = 0;
-
- if (! string_to_int (buf, & i))
- goto ERR;
-
- len = strlen (p + 1);
- if (len > 6)
- goto ERR;
-
- memcpy (buf, p + 1, len);
- memset (buf + len, '0', 6 - len);
- buf[6] = 0;
-
- if (! string_to_int (buf, & f))
- goto ERR;
+ char buf[7] = "000000";
+ const char * nul = memchr (p + 1, 0, 6);
+ memcpy (buf, p + 1, nul ? nul - (p + 1) : 6);
+ val += (double) str_to_int (buf) / 1000000;
}
- else
- {
- if (! string_to_int (string, & i))
- goto ERR;
-
- f = 0;
- }
-
- double val = i + (double) f / 1000000;
- if (val > 1000000000)
- goto ERR;
-
- * addr = neg ? -val : val;
- return TRUE;
-ERR:
- return FALSE;
+ return neg ? -val : val;
}
-EXPORT char * int_to_string (int val)
+EXPORT char * int_to_str (int val)
{
- g_return_val_if_fail (val >= -1000000000 && val <= 1000000000, NULL);
- return g_strdup_printf ("%d", val);
+ char buf[16];
+ str_itoa (val, buf, sizeof buf);
+ return str_get (buf);
}
-EXPORT char * double_to_string (double val)
+EXPORT char * double_to_str (double val)
{
- g_return_val_if_fail (val >= -1000000000 && val <= 1000000000, NULL);
-
bool_t neg = (val < 0);
if (neg)
val = -val;
@@ -528,94 +697,88 @@ EXPORT char * double_to_string (double val)
f = 0;
}
- char * s = neg ? g_strdup_printf ("-%d.%06d", i, f) : g_strdup_printf ("%d.%06d", i, f);
+ SPRINTF (buf, "%s%d.%06d", neg ? "-" : "", i, f);
- char * c = s + strlen (s);
+ char * c = buf + strlen (buf);
while (* (c - 1) == '0')
c --;
if (* (c - 1) == '.')
c --;
* c = 0;
- return s;
+ return str_get (buf);
}
-EXPORT bool_t string_to_int_array (const char * string, int * array, int count)
+EXPORT bool_t str_to_int_array (const char * string, int * array, int count)
{
- char * * split = g_strsplit (string, ",", -1);
- if (g_strv_length (split) != count)
- goto ERR;
+ Index * index = str_list_to_index (string, ", ");
+ bool_t okay = (index_count (index) == count);
- for (int i = 0; i < count; i ++)
+ if (okay)
{
- if (! string_to_int (split[i], & array[i]))
- goto ERR;
+ for (int i = 0; i < count; i ++)
+ array[i] = str_to_int (index_get (index, i));
}
- g_strfreev (split);
- return TRUE;
-
-ERR:
- g_strfreev (split);
- return FALSE;
+ index_free_full (index, (IndexFreeFunc) str_unref);
+ return okay;
}
-EXPORT char * int_array_to_string (const int * array, int count)
+EXPORT char * int_array_to_str (const int * array, int count)
{
- char * * split = g_malloc0 (sizeof (char *) * (count + 1));
+ Index * index = index_new ();
for (int i = 0; i < count; i ++)
{
- split[i] = int_to_string (array[i]);
- if (! split[i])
+ char * value = int_to_str (array[i]);
+ if (! value)
goto ERR;
+
+ index_insert (index, -1, value);
}
- char * string = g_strjoinv (",", split);
- g_strfreev (split);
+ char * string = index_to_str_list (index, ",");
+ index_free_full (index, (IndexFreeFunc) str_unref);
return string;
ERR:
- g_strfreev (split);
+ index_free_full (index, (IndexFreeFunc) str_unref);
return NULL;
}
-EXPORT bool_t string_to_double_array (const char * string, double * array, int count)
+EXPORT bool_t str_to_double_array (const char * string, double * array, int count)
{
- char * * split = g_strsplit (string, ",", -1);
- if (g_strv_length (split) != count)
- goto ERR;
+ Index * index = str_list_to_index (string, ", ");
+ bool_t okay = (index_count (index) == count);
- for (int i = 0; i < count; i ++)
+ if (okay)
{
- if (! string_to_double (split[i], & array[i]))
- goto ERR;
+ for (int i = 0; i < count; i ++)
+ array[i] = str_to_double (index_get (index, i));
}
- g_strfreev (split);
- return TRUE;
-
-ERR:
- g_strfreev (split);
- return FALSE;
+ index_free_full (index, (IndexFreeFunc) str_unref);
+ return okay;
}
-EXPORT char * double_array_to_string (const double * array, int count)
+EXPORT char * double_array_to_str (const double * array, int count)
{
- char * * split = g_malloc0 (sizeof (char *) * (count + 1));
+ Index * index = index_new ();
for (int i = 0; i < count; i ++)
{
- split[i] = double_to_string (array[i]);
- if (! split[i])
+ char * value = double_to_str (array[i]);
+ if (! value)
goto ERR;
+
+ index_insert (index, -1, value);
}
- char * string = g_strjoinv (",", split);
- g_strfreev (split);
+ char * string = index_to_str_list (index, ",");
+ index_free_full (index, (IndexFreeFunc) str_unref);
return string;
ERR:
- g_strfreev (split);
+ index_free_full (index, (IndexFreeFunc) str_unref);
return NULL;
}
diff --git a/src/libaudcore/audstrings.h b/src/libaudcore/audstrings.h
index c8e1035..9f6cae7 100644
--- a/src/libaudcore/audstrings.h
+++ b/src/libaudcore/audstrings.h
@@ -1,7 +1,6 @@
/*
* audstrings.h
- * Copyright 2009-2011 John Lindgren
- * Copyright 2010 William Pitcock
+ * Copyright 2009-2012 John Lindgren and William Pitcock
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -21,21 +20,86 @@
#ifndef LIBAUDCORE_STRINGS_H
#define LIBAUDCORE_STRINGS_H
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
#include <libaudcore/core.h>
+#define SPRINTF(s, ...) \
+ char s[snprintf (NULL, 0, __VA_ARGS__) + 1]; \
+ snprintf (s, sizeof s, __VA_ARGS__)
+
+#define VSPRINTF(s, f, v) \
+ va_list v##2; \
+ va_copy (v##2, v); \
+ char s[vsnprintf (NULL, 0, f, v##2) + 1]; \
+ va_end (v##2); \
+ vsnprintf (s, sizeof s, f, v)
+
+#define SCOPY(s, a) \
+ char s[strlen (a) + 1]; \
+ strcpy (s, a)
+
+#define SNCOPY(s, a, x) \
+ char s[(x) + 1]; \
+ strncpy (s, a, sizeof s - 1); \
+ s[sizeof s - 1] = 0
+
+#define SCONCAT2(s, a, b) \
+ int s##_1 = strlen (a), s##_2 = strlen (b); \
+ char s[s##_1 + s##_2 + 1]; \
+ memcpy (s, (a), s##_1); \
+ strcpy (s + s##_1, (b))
+
+#define SCONCAT3(s, a, b, c) \
+ int s##_1 = strlen (a), s##_2 = strlen (b), s##_3 = strlen (c); \
+ char s[s##_1 + s##_2 + s##_3 + 1]; \
+ memcpy (s, (a), s##_1); \
+ memcpy (s + s##_1, (b), s##_2); \
+ strcpy (s + s##_1 + s##_2, (c))
+
+#define SCONCAT4(s, a, b, c, d) \
+ int s##_1 = strlen (a), s##_2 = strlen (b), s##_3 = strlen (c), s##_4 = strlen (d); \
+ char s[s##_1 + s##_2 + s##_3 + s##_4 + 1]; \
+ memcpy (s, (a), s##_1); \
+ memcpy (s + s##_1, (b), s##_2); \
+ memcpy (s + s##_1 + s##_2, (c), s##_3); \
+ strcpy (s + s##_1 + s##_2 + s##_3, (d))
+
+struct _Index;
+
+/* all (char *) return values must be freed with str_unref() */
+
+char * str_printf (const char * format, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+char * str_vprintf (const char * format, va_list args);
+
bool_t str_has_prefix_nocase(const char * str, const char * prefix);
bool_t str_has_suffix_nocase(const char * str, const char * suffix);
-void str_set_utf8_impl (char * (* stu_impl) (const char *),
- char * (* stuf_impl) (const char *, int, int *, int *));
-char * str_to_utf8 (const char * str);
-char * str_to_utf8_full (const char * str, int len, int * bytes_read, int * bytes_written);
+char * strstr_nocase (const char * haystack, const char * needle);
+char * strstr_nocase_utf8 (const char * haystack, const char * needle);
+
+char * str_tolower_utf8 (const char * str);
-void string_replace_char (char * string, char old_c, char new_c);
+void str_replace_char (char * string, char old_c, char new_c);
+
+void str_itoa (int x, char * buf, int bufsize);
void str_decode_percent (const char * str, int len, char * out);
void str_encode_percent (const char * str, int len, char * out);
+char * str_convert (const char * str, int len, const char * from_charset, const char * to_charset);
+char * str_from_locale (const char * str, int len);
+char * str_to_locale (const char * str, int len);
+char * str_to_utf8 (const char * str, int len);
+
+/* takes ownership of <fallbacks> and the pooled strings in it */
+void str_set_charsets (const char * region, struct _Index * fallbacks);
+
+void filename_normalize (char * filename);
+
+char * filename_build (const char * path, const char * name);
char * filename_to_uri (const char * filename);
char * uri_to_filename (const char * uri);
char * uri_to_display (const char * uri);
@@ -44,19 +108,20 @@ void uri_parse (const char * uri, const char * * base_p, const char * * ext_p,
const char * * sub_p, int * isub_p);
bool_t uri_get_extension (const char * uri, char * buf, int buflen);
-int string_compare (const char * a, const char * b);
-int string_compare_encoded (const char * a, const char * b);
+int str_compare (const char * a, const char * b);
+int str_compare_encoded (const char * a, const char * b);
-char *str_replace_fragment(char *s, int size, const char *old_str, const char *new_str);
+struct _Index * str_list_to_index (const char * list, const char * delims);
+char * index_to_str_list (struct _Index * index, const char * sep);
-bool_t string_to_int (const char * string, int * addr);
-bool_t string_to_double (const char * string, double * addr);
-char * int_to_string (int val);
-char * double_to_string (double val);
+int str_to_int (const char * string);
+double str_to_double (const char * string);
+char * int_to_str (int val);
+char * double_to_str (double val);
-bool_t string_to_int_array (const char * string, int * array, int count);
-char * int_array_to_string (const int * array, int count);
-bool_t string_to_double_array (const char * string, double * array, int count);
-char * double_array_to_string (const double * array, int count);
+bool_t str_to_int_array (const char * string, int * array, int count);
+char * int_array_to_str (const int * array, int count);
+bool_t str_to_double_array (const char * string, double * array, int count);
+char * double_array_to_str (const double * array, int count);
#endif /* LIBAUDCORE_STRINGS_H */
diff --git a/src/libaudcore/charset.c b/src/libaudcore/charset.c
new file mode 100644
index 0000000..d3c6d5f
--- /dev/null
+++ b/src/libaudcore/charset.c
@@ -0,0 +1,180 @@
+/*
+ * charset.c
+ * Copyright 2013 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 <iconv.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+
+#ifdef USE_CHARDET
+#include <libguess/libguess.h>
+#endif
+
+#include "audstrings.h"
+#include "index.h"
+#include "tinylock.h"
+
+EXPORT char * str_convert (const char * str, int len, const char * from_charset,
+ const char * to_charset)
+{
+ iconv_t conv = iconv_open (to_charset, from_charset);
+ if (conv == (iconv_t) -1)
+ return NULL;
+
+ if (len < 0)
+ len = strlen (str);
+
+ // liberal estimate of how much space we will need
+ // are there obscure cases that require even more?
+ int maxlen = 4 * len;
+
+ char buf[maxlen + 1];
+ char * result = NULL;
+
+ size_t inbytes = len;
+ size_t outbytes = maxlen;
+ char * in = (char *) str;
+ char * out = buf;
+
+ if (iconv (conv, & in, & inbytes, & out, & outbytes) != (size_t) -1 && ! inbytes)
+ {
+ buf[maxlen - outbytes] = 0;
+ result = str_get (buf);
+ }
+
+ iconv_close (conv);
+ return result;
+}
+
+static void whine_locale (const char * str, int len, const char * dir, const char * charset)
+{
+ if (len < 0)
+ fprintf (stderr, "Cannot convert %s locale (%s): %s\n", dir, charset, str);
+ else
+ fprintf (stderr, "Cannot convert %s locale (%s): %.*s\n", dir, charset, len, str);
+}
+
+EXPORT char * str_from_locale (const char * str, int len)
+{
+ const char * charset;
+
+ if (g_get_charset (& charset))
+ {
+ /* locale is UTF-8 */
+ if (! g_utf8_validate (str, len, NULL))
+ {
+ whine_locale (str, len, "from", "UTF-8");
+ return NULL;
+ }
+
+ return (len < 0) ? str_get (str) : str_nget (str, len);
+ }
+ else
+ {
+ char * utf8 = str_convert (str, len, charset, "UTF-8");
+ if (! utf8)
+ whine_locale (str, len, "from", charset);
+
+ return utf8;
+ }
+}
+
+EXPORT char * str_to_locale (const char * str, int len)
+{
+ const char * charset;
+
+ if (g_get_charset (& charset))
+ {
+ /* locale is UTF-8 */
+ return (len < 0) ? str_get (str) : str_nget (str, len);
+ }
+ else
+ {
+ char * local = str_convert (str, len, "UTF-8", charset);
+ if (! local)
+ whine_locale (str, len, "to", charset);
+
+ return local;
+ }
+}
+
+static TinyRWLock settings_lock;
+static char * detect_region;
+static Index * fallback_charsets;
+
+EXPORT void str_set_charsets (const char * region, Index * fallbacks)
+{
+ tiny_lock_write (& settings_lock);
+
+ str_unref (detect_region);
+ detect_region = str_get (region);
+
+#ifdef USE_CHARDET
+ if (detect_region)
+ libguess_init ();
+#endif
+
+ if (fallback_charsets)
+ index_free_full (fallback_charsets, (IndexFreeFunc) str_unref);
+
+ fallback_charsets = fallbacks;
+
+ tiny_unlock_write (& settings_lock);
+}
+
+EXPORT char * str_to_utf8 (const char * str, int len)
+{
+ /* check whether already UTF-8 */
+ if (g_utf8_validate (str, len, NULL))
+ return (len < 0) ? str_get (str) : str_nget (str, len);
+
+ if (len < 0)
+ len = strlen (str);
+
+ char * utf8 = NULL;
+ tiny_lock_read (& settings_lock);
+
+#ifdef USE_CHARDET
+ if (detect_region)
+ {
+ /* prefer libguess-detected charset */
+ const char * detected = libguess_determine_encoding (str, len, detect_region);
+ if (detected && (utf8 = str_convert (str, len, detected, "UTF-8")))
+ goto DONE;
+ }
+#endif
+
+ if (fallback_charsets)
+ {
+ /* try user-configured fallbacks */
+ for (int i = 0; i < index_count (fallback_charsets); i ++)
+ {
+ if ((utf8 = str_convert (str, len, index_get (fallback_charsets, i), "UTF-8")))
+ goto DONE;
+ }
+ }
+
+ /* try system locale last (this one will print a warning if it fails) */
+ utf8 = str_from_locale (str, len);
+
+DONE:
+ tiny_unlock_read (& settings_lock);
+ return utf8;
+}
diff --git a/src/libaudcore/core.h b/src/libaudcore/core.h
index b92c500..f3c2615 100644
--- a/src/libaudcore/core.h
+++ b/src/libaudcore/core.h
@@ -1,6 +1,6 @@
/*
* core.h
- * Copyright 2011 John Lindgren
+ * Copyright 2011-2012 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -43,14 +43,7 @@
#undef CLAMP
#define CLAMP(a,min,max) ((a) < (min) ? (min) : (a) > (max) ? (max) : (a))
-#define SPRINTF(s,...) \
- char s[snprintf (NULL, 0, __VA_ARGS__) + 1]; \
- snprintf (s, sizeof s, __VA_ARGS__);
-
-/* Simple sanity check to catch (1) strings that are still in use after their
- * reference count has dropped to zero and (2) strings that should have been
- * pooled but never were. If the check fails, the program is aborted. */
-#define STR_CHECK(str) do {if ((str) && (str)[-1] != '@') strpool_abort (str);} while (0)
+#define ARRAY_LEN(a) (sizeof (a) / sizeof (a)[0])
/* If the pool contains a copy of <str>, increments its reference count.
* Otherwise, adds a copy of <str> to the pool with a reference count of one.
@@ -63,7 +56,7 @@ char * str_get (const char * str);
* string already in the pool. Faster than calling str_get() a second time.
* Returns <str> for convenience. If <str> is NULL, simply returns NULL with no
* side effects. */
-char * str_ref (char * str);
+char * str_ref (const char * str);
/* Decrements the reference count of <str>, where <str> is the address of a
* string in the pool. If the reference count drops to zero, releases the
@@ -71,16 +64,18 @@ char * str_ref (char * str);
* effects. */
void str_unref (char * str);
+/* Returns the cached hash value of a pooled string (or 0 for NULL). */
+unsigned str_hash (const char * str);
+
+/* Checks whether two pooled strings are equal. Since the pool never contains
+ * duplicate strings, this is a simple pointer comparison and thus much faster
+ * than strcmp(). NULL is considered equal to NULL but not equal to any string. */
+bool_t str_equal (const char * str1, const char * str2);
+
/* Calls str_get() on the first <len> characters of <str>. If <str> has less
* than or equal to <len> characters, equivalent to str_get(). */
char * str_nget (const char * str, int len);
-/* Calls sprintf() internally, then pools the produced string with str_get(). */
-char * str_printf (const char * format, ...);
-
-/* Used by STR_CHECK; should not be called directly. */
-void strpool_abort (char * str);
-
/* Releases all memory used by the string pool. If strings remain in the pool,
* a warning may be printed to stderr in order to reveal memory leaks. */
void strpool_shutdown (void);
diff --git a/src/libaudcore/eventqueue.c b/src/libaudcore/eventqueue.c
index c2648b9..ee92c03 100644
--- a/src/libaudcore/eventqueue.c
+++ b/src/libaudcore/eventqueue.c
@@ -45,7 +45,7 @@ static bool_t event_execute (Event * event)
hook_call (event->name, event->data);
- g_free (event->name);
+ str_unref (event->name);
if (event->destroy)
event->destroy (event->data);
@@ -56,7 +56,7 @@ static bool_t event_execute (Event * event)
EXPORT void event_queue_full (int time, const char * name, void * data, void (* destroy) (void *))
{
Event * event = g_slice_new (Event);
- event->name = g_strdup (name);
+ event->name = str_get (name);
event->data = data;
event->destroy = destroy;
@@ -83,7 +83,7 @@ EXPORT void event_queue_cancel (const char * name, void * data)
g_source_remove (event->source);
events = g_list_delete_link (events, node);
- g_free (event->name);
+ str_unref (event->name);
if (event->destroy)
event->destroy (event->data);
@@ -95,3 +95,28 @@ EXPORT void event_queue_cancel (const char * name, void * data)
pthread_mutex_unlock (& mutex);
}
+
+EXPORT void event_queue_cancel_all (void)
+{
+ pthread_mutex_lock (& mutex);
+
+ GList * node = events;
+ while (node)
+ {
+ Event * event = node->data;
+ GList * next = node->next;
+
+ g_source_remove (event->source);
+ events = g_list_delete_link (events, node);
+
+ str_unref (event->name);
+ if (event->destroy)
+ event->destroy (event->data);
+
+ g_slice_free (Event, event);
+
+ node = next;
+ }
+
+ pthread_mutex_unlock (& mutex);
+}
diff --git a/src/libaudcore/hook.c b/src/libaudcore/hook.c
index 00d69c8..e9f4e39 100644
--- a/src/libaudcore/hook.c
+++ b/src/libaudcore/hook.c
@@ -1,6 +1,6 @@
/*
* hook.c
- * Copyright 2011 John Lindgren
+ * Copyright 2011-2012 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/libaudcore/hook.h b/src/libaudcore/hook.h
index 79fe8ac..646359b 100644
--- a/src/libaudcore/hook.h
+++ b/src/libaudcore/hook.h
@@ -47,4 +47,7 @@ void event_queue_full (int time, const char * name, void * data, void (* destroy
* all hook calls matching <name> are canceled. */
void event_queue_cancel (const char * name, void * data);
+/* Cancels all pending hook calls. */
+void event_queue_cancel_all (void);
+
#endif /* LIBAUDCORE_HOOK_H */
diff --git a/src/libaudcore/index.c b/src/libaudcore/index.c
index 160a97d..c2f7f39 100644
--- a/src/libaudcore/index.c
+++ b/src/libaudcore/index.c
@@ -1,6 +1,6 @@
/*
* index.c
- * Copyright 2009-2011 John Lindgren
+ * Copyright 2009-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,16 +17,19 @@
* the use of this software.
*/
+#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
+#include "core.h"
#include "index.h"
struct _Index {
void * * data;
int count, size;
+ void * sdata[16];
};
typedef struct {
@@ -40,19 +43,26 @@ typedef struct {
EXPORT Index * index_new (void)
{
- Index * index = g_slice_new (Index);
-
- index->data = NULL;
- index->count = 0;
- index->size = 0;
-
+ Index * index = g_new0 (Index, 1);
+ index->data = index->sdata;
+ index->size = ARRAY_LEN (index->sdata);
return index;
}
EXPORT void index_free (Index * index)
{
- g_free (index->data);
- g_slice_free (Index, index);
+ if (index->data != index->sdata)
+ g_free (index->data);
+
+ g_free (index);
+}
+
+EXPORT void index_free_full (Index * index, IndexFreeFunc func)
+{
+ for (int i = 0; i < index->count; i ++)
+ func (index->data[i]);
+
+ index_free (index);
}
EXPORT int index_count (Index * index)
@@ -62,26 +72,38 @@ EXPORT int index_count (Index * index)
EXPORT void index_allocate (Index * index, int size)
{
- if (size <= index->size)
+ assert (size >= 0);
+
+ if (index->size >= size)
return;
- if (! index->size)
+ if (index->size < 64)
index->size = 64;
- while (size > index->size)
+ while (index->size < size)
index->size <<= 1;
- index->data = g_realloc (index->data, sizeof (void *) * index->size);
+ if (index->data == index->sdata)
+ {
+ index->data = g_new (void *, index->size);
+ memcpy (index->data, index->sdata, sizeof index->sdata);
+ }
+ else
+ index->data = g_renew (void *, index->data, index->size);
}
-EXPORT void index_set (Index * index, int at, void * value)
+EXPORT void * index_get (Index * index, int at)
{
- index->data[at] = value;
+ assert (at >= 0 && at < index->count);
+
+ return index->data[at];
}
-EXPORT void * index_get (Index * index, int at)
+EXPORT void index_set (Index * index, int at, void * value)
{
- return index->data[at];
+ assert (at >= 0 && at < index->count);
+
+ index->data[at] = value;
}
static void make_room (Index * index, int at, int count)
@@ -89,62 +111,80 @@ static void make_room (Index * index, int at, int count)
index_allocate (index, index->count + count);
if (at < index->count)
- memmove (index->data + at + count, index->data + at, sizeof (void *) *
- (index->count - at));
+ memmove (index->data + at + count, index->data + at, sizeof (void *) * (index->count - at));
index->count += count;
}
EXPORT void index_insert (Index * index, int at, void * value)
{
+ if (at == -1)
+ at = index->count;
+
+ assert (at >= 0 && at <= index->count);
+
make_room (index, at, 1);
index->data[at] = value;
}
-EXPORT void index_append (Index * index, void * value)
+EXPORT void index_copy_set (Index * source, int from, Index * target, int to, int count)
{
- index_insert (index, index->count, value);
-}
+ assert (count >= 0);
+ assert (from >= 0 && from + count <= source->count);
+ assert (to >= 0 && to + count <= target->count);
-EXPORT void index_copy_set (Index * source, int from, Index * target,
- int to, int count)
-{
- memcpy (target->data + to, source->data + from, sizeof (void *) * count);
+ memmove (target->data + to, source->data + from, sizeof (void *) * count);
}
-EXPORT void index_copy_insert (Index * source, int from, Index * target,
- int to, int count)
+EXPORT void index_copy_insert (Index * source, int from, Index * target, int to, int count)
{
+ if (to == -1)
+ to = target->count;
+ if (count == -1)
+ count = source->count - from;
+
+ assert (count >= 0);
+ assert (from >= 0 && from + count <= source->count);
+ assert (to >= 0 && to <= target->count);
+
make_room (target, to, count);
- memcpy (target->data + to, source->data + from, sizeof (void *) * count);
-}
-EXPORT void index_copy_append (Index * source, int from, Index * target,
- int count)
-{
- index_copy_insert (source, from, target, target->count, count);
+ if (source == target && to <= from)
+ index_copy_set (source, from + count, target, to, count);
+ else if (source == target && to <= from + count)
+ {
+ index_copy_set (source, from, target, to, to - from);
+ index_copy_set (source, to + count, target, to + (to - from), count - (to - from));
+ }
+ else
+ index_copy_set (source, from, target, to, count);
}
-EXPORT void index_merge_insert (Index * first, int at, Index * second)
+EXPORT void index_delete (Index * index, int at, int count)
{
- index_copy_insert (second, 0, first, at, second->count);
-}
+ if (count == -1)
+ count = index->count - at;
-EXPORT void index_merge_append (Index * first, Index * second)
-{
- index_copy_insert (second, 0, first, first->count, second->count);
-}
+ assert (count >= 0);
+ assert (at >= 0 && at + count <= index->count);
-EXPORT void index_move (Index * index, int from, int to, int count)
-{
- memmove (index->data + to, index->data + from, sizeof (void *) * count);
+ index_copy_set (index, at + count, index, at, index->count - (at + count));
+
+ index->count -= count;
}
-EXPORT void index_delete (Index * index, int at, int count)
+EXPORT void index_delete_full (Index * index, int at, int count, IndexFreeFunc func)
{
- index->count -= count;
- memmove (index->data + at, index->data + at + count, sizeof (void *) *
- (index->count - at));
+ if (count == -1)
+ count = index->count - at;
+
+ assert (count >= 0);
+ assert (at >= 0 && at + count <= index->count);
+
+ for (int i = at; i < at + count; i ++)
+ func (index->data[i]);
+
+ index_delete (index, at, count);
}
static int index_compare (const void * ap, const void * bp, void * _wrapper)
@@ -156,8 +196,7 @@ static int index_compare (const void * ap, const void * bp, void * _wrapper)
EXPORT void index_sort (Index * index, int (* compare) (const void *, const void *))
{
CompareWrapper wrapper = {compare};
- g_qsort_with_data (index->data, index->count, sizeof (void *),
- index_compare, & wrapper);
+ g_qsort_with_data (index->data, index->count, sizeof (void *), index_compare, & wrapper);
}
static int index_compare2 (const void * ap, const void * bp, void * _wrapper)
@@ -170,6 +209,5 @@ EXPORT void index_sort_with_data (Index * index, int (* compare)
(const void * a, const void * b, void * data), void * data)
{
CompareWrapper2 wrapper = {compare, data};
- g_qsort_with_data (index->data, index->count, sizeof (void *),
- index_compare2, & wrapper);
+ g_qsort_with_data (index->data, index->count, sizeof (void *), index_compare2, & wrapper);
}
diff --git a/src/libaudcore/index.h b/src/libaudcore/index.h
index 68d21a6..3990ff8 100644
--- a/src/libaudcore/index.h
+++ b/src/libaudcore/index.h
@@ -1,6 +1,6 @@
/*
* index.h
- * Copyright 2009-2011 John Lindgren
+ * Copyright 2009-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -20,25 +20,77 @@
#ifndef LIBAUDCORE_INDEX_H
#define LIBAUDCORE_INDEX_H
+/* An "index" is an opaque structure representing a list of pointers. It is
+ * used primarily to store Audacious playlists, but can be useful for other
+ * purposes as well. */
+
struct _Index;
typedef struct _Index Index;
+typedef void (* IndexFreeFunc) (void * value);
+
+/* Returns a new, empty index. */
Index * index_new (void);
+
+/* Destroys <index>. */
void index_free (Index * index);
+
+/* Destroys <index>, first calling <func> on each pointer stored in it. */
+void index_free_full (Index * index, IndexFreeFunc func);
+
+/* Returns the number of pointers stored in <index>. */
int index_count (Index * index);
+
+/* Preallocates space to store <size> pointers in <index>. This can be used to
+ * avoid repeated memory allocations when adding pointers to <index> one by one.
+ * The value returned by index_count() does not changed until the pointers are
+ * actually added. */
void index_allocate (Index * index, int size);
-void index_set (Index * index, int at, void * value);
+
+/* Returns the value stored in <index> at position <at>. */
void * index_get (Index * index, int at);
+
+/* Stores <value> in <index> at position <at>, overwriting the previous value. */
+void index_set (Index * index, int at, void * value);
+
+/* Stores <value> in <index> at position <at>, shifting the previous value (if
+ * any) and those following it forward to make room. If <at> is -1, <value> is
+ * added to the end of <index>. */
void index_insert (Index * index, int at, void * value);
-void index_append (Index * index, void * value);
+
+/* Copies <count> pointers from <source>, starting at position <from>, to
+ * <target>, starting at position <to>. Existing pointers in <target> are
+ * overwritten. Overlapping regions are handled correctly if <source> and
+ * <target> are the same index. */
void index_copy_set (Index * source, int from, Index * target, int to, int count);
+
+/* Copies <count> pointers from <source>, starting at position <from>, to
+ * <target>, starting at position <to>. Existing pointers in <target> are
+ * shifted forward to make room. All cases are handled correctly if <source>
+ * and <target> are the same index, including the case where <to> is between
+ * <from> and <from + count>. If <to> is -1, the pointers are added to the end
+ * of <target>. If <count> is -1, copying continues until the end of <source>
+ * is reached. */
void index_copy_insert (Index * source, int from, Index * target, int to, int count);
-void index_copy_append (Index * source, int from, Index * target, int count);
-void index_merge_insert (Index * first, int at, Index * second);
-void index_merge_append (Index * first, Index * second);
-void index_move (Index * index, int from, int to, int count);
+
+/* Removes <count> pointers from <index>, start at position <at>. Any following
+ * pointers are shifted backward to close the gap. If <count> is -1, all
+ * pointers from <at> to the end of <index> are removed. */
void index_delete (Index * index, int at, int count);
+
+/* Like index_delete(), but first calls <func> on any pointer that is being
+ * removed. */
+void index_delete_full (Index * index, int at, int count, IndexFreeFunc func);
+
+/* Sort the entries in <index> using the quick-sort algorithm with the given
+ * comparison function. The return value of <compare> should follow the
+ * convention used by strcmp(): negative if (a < b), zero if (a = b), positive
+ * if (a > b). */
void index_sort (Index * index, int (* compare) (const void * a, const void * b));
+
+/* Exactly like index_sort() except that <data> is forwarded to the comparison
+ * function. This allows comparison functions whose behavior changes depending
+ * on context. */
void index_sort_with_data (Index * index, int (* compare) (const void * a,
const void * b, void * data), void * data);
diff --git a/src/libaudcore/inifile.c b/src/libaudcore/inifile.c
new file mode 100644
index 0000000..6e13a48
--- /dev/null
+++ b/src/libaudcore/inifile.c
@@ -0,0 +1,134 @@
+/*
+ * inifile.c
+ * Copyright 2013 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 "audstrings.h"
+#include "inifile.h"
+
+#include <glib.h>
+
+#include <string.h>
+
+static char * strskip (char * str)
+{
+ while (g_ascii_isspace (* str))
+ str ++;
+
+ return str;
+}
+
+static char * strtrim (char * str)
+{
+ int len = strlen (str);
+
+ while (len && g_ascii_isspace(str[len - 1]))
+ str[-- len] = 0;
+
+ return str;
+}
+
+EXPORT void inifile_parse (VFSFile * file,
+ void (* handle_heading) (const char * heading, void * data),
+ void (* handle_entry) (const char * key, const char * value, void * data),
+ void * data)
+{
+ int size = 512;
+ char * buf = g_new (char, size);
+
+ char * pos = buf;
+ int len = 0;
+ bool_t eof = FALSE;
+
+ while (1)
+ {
+ char * newline = memchr (pos, '\n', len);
+
+ while (! newline && ! eof)
+ {
+ memmove (buf, pos, len);
+ pos = buf;
+
+ if (len >= size - 1)
+ {
+ size <<= 1;
+ buf = g_renew (char, buf, size);
+ pos = buf;
+ }
+
+ len += vfs_fread (buf + len, 1, size - 1 - len, file);
+
+ if (len < size - 1)
+ eof = TRUE;
+
+ newline = memchr (pos, '\n', len);
+ }
+
+ if (newline)
+ * newline = 0;
+ else
+ pos[len] = 0;
+
+ char * start = strskip (pos);
+
+ switch (* start)
+ {
+ case 0:
+ case '#':
+ case ';':
+ break;
+
+ case '[':;
+ char * end = strchr (start + 1, ']');
+ if (! end)
+ break;
+
+ * end = 0;
+ handle_heading (strtrim (strskip (start + 1)), data);
+ break;
+
+ default:;
+ char * sep = strchr (start, '=');
+ if (! sep)
+ break;
+
+ * sep = 0;
+ handle_entry (strtrim (start), strtrim (strskip (sep + 1)), data);
+ break;
+ }
+
+ if (! newline)
+ break;
+
+ len -= newline + 1 - pos;
+ pos = newline + 1;
+ }
+
+ g_free (buf);
+}
+
+EXPORT bool_t inifile_write_heading (VFSFile * file, const char * heading)
+{
+ SCONCAT3 (buf, "\n[", heading, "]\n");
+ return (vfs_fwrite (buf, 1, sizeof buf - 1, file) == sizeof buf - 1);
+}
+
+EXPORT bool_t inifile_write_entry (VFSFile * file, const char * key, const char * value)
+{
+ SCONCAT4 (buf, key, "=", value, "\n");
+ return (vfs_fwrite (buf, 1, sizeof buf - 1, file) == sizeof buf - 1);
+}
diff --git a/src/libaudgui/ui_regex.h b/src/libaudcore/inifile.h
index 801a2ff..87189c2 100644
--- a/src/libaudgui/ui_regex.h
+++ b/src/libaudcore/inifile.h
@@ -1,39 +1,33 @@
-/*
- * ui_regex.h
- * Copyright 2010 Carlo Bramini
- *
- * 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 __UI_REGEX_H__
-#define __UI_REGEX_H__
-
-#if defined USE_REGEX_ONIGURUMA
- #include <onigposix.h>
-
-#elif defined USE_REGEX_PCRE
- #include <pcreposix.h>
-
-#elif defined HAVE_REGEX_H
- #include <regex.h>
-
-#elif defined HAVE_RXPOSIX_H
- #include <rxposix.h>
-
-#elif defined HAVE_RX_RXPOSIX_H
- #include <rx/rxposix.h>
-#endif
-
-#endif
+/*
+ * inifile.h
+ * Copyright 2013 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 LIBAUDCORE_INIFILE_H
+#define LIBAUDCORE_INIFILE_H
+
+#include "vfs.h"
+
+void inifile_parse (VFSFile * file,
+ void (* handle_heading) (const char * heading, void * data),
+ void (* handle_entry) (const char * key, const char * value, void * data),
+ void * data);
+
+bool_t inifile_write_heading (VFSFile * file, const char * heading);
+bool_t inifile_write_entry (VFSFile * file, const char * key, const char * value);
+
+#endif /* LIBAUDCORE_INIFILE_H */
diff --git a/src/libaudcore/multihash.c b/src/libaudcore/multihash.c
new file mode 100644
index 0000000..2be8839
--- /dev/null
+++ b/src/libaudcore/multihash.c
@@ -0,0 +1,153 @@
+/*
+ * multihash.c
+ * Copyright 2013 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 "multihash.h"
+
+#include <glib.h>
+
+#define INITIAL_SIZE 256 /* must be a power of two */
+
+static void resize_channel (MultihashTable * table, MultihashChannel * channel, unsigned size)
+{
+ MultihashNode * * buckets = g_new0 (MultihashNode *, size);
+
+ for (int b1 = 0; b1 < channel->size; b1 ++)
+ {
+ MultihashNode * node = channel->buckets[b1];
+
+ while (node)
+ {
+ MultihashNode * next = node->next;
+
+ unsigned hash = table->hash_func (node);
+ unsigned b2 = (hash >> MULTIHASH_SHIFT) & (size - 1);
+ MultihashNode * * node_ptr = & buckets[b2];
+
+ node->next = * node_ptr;
+ * node_ptr = node;
+
+ node = next;
+ }
+ }
+
+ g_free (channel->buckets);
+ channel->buckets = buckets;
+ channel->size = size;
+}
+
+EXPORT int multihash_lookup (MultihashTable * table, const void * data,
+ unsigned hash, MultihashAddFunc add, MultihashActionFunc action, void * state)
+{
+ unsigned c = hash & (MULTIHASH_CHANNELS - 1);
+ MultihashChannel * channel = & table->channels[c];
+
+ int status = 0;
+ tiny_lock (& channel->lock);
+
+ if (! channel->buckets)
+ {
+ if (! add)
+ goto DONE;
+
+ channel->buckets = g_new0 (MultihashNode *, INITIAL_SIZE);
+ channel->size = INITIAL_SIZE;
+ channel->used = 0;
+ }
+
+ unsigned b = (hash >> MULTIHASH_SHIFT) & (channel->size - 1);
+ MultihashNode * * node_ptr = & channel->buckets[b];
+ MultihashNode * node = * node_ptr;
+
+ while (node && ! table->match_func (node, data, hash))
+ {
+ node_ptr = & node->next;
+ node = * node_ptr;
+ }
+
+ if (node)
+ {
+ status |= MULTIHASH_FOUND;
+
+ MultihashNode * next = node->next;
+
+ if (action && action (node, state))
+ {
+ status |= MULTIHASH_REMOVED;
+
+ * node_ptr = next;
+
+ channel->used --;
+ if (channel->used < channel->size >> 2 && channel->size > INITIAL_SIZE)
+ resize_channel (table, channel, channel->size >> 1);
+ }
+ }
+ else if (add && (node = add (data, hash, state)))
+ {
+ status |= MULTIHASH_ADDED;
+
+ * node_ptr = node;
+ node->next = NULL;
+
+ channel->used ++;
+ if (channel->used > channel->size)
+ resize_channel (table, channel, channel->size << 1);
+ }
+
+DONE:
+ tiny_unlock (& channel->lock);
+ return status;
+}
+
+EXPORT void multihash_iterate (MultihashTable * table, MultihashActionFunc action, void * state)
+{
+ for (int c = 0; c < MULTIHASH_CHANNELS; c ++)
+ tiny_lock (& table->channels[c].lock);
+
+ for (int c = 0; c < MULTIHASH_CHANNELS; c ++)
+ {
+ MultihashChannel * channel = & table->channels[c];
+
+ for (int b = 0; b < channel->size; b ++)
+ {
+ MultihashNode * * node_ptr = & channel->buckets[b];
+ MultihashNode * node = * node_ptr;
+
+ while (node)
+ {
+ MultihashNode * next = node->next;
+
+ if (action (node, state))
+ {
+ * node_ptr = next;
+ channel->used --;
+ }
+ else
+ node_ptr = & node->next;
+
+ node = next;
+ }
+ }
+
+ if (channel->used < channel->size >> 2 && channel->size > INITIAL_SIZE)
+ resize_channel (table, channel, channel->size >> 1);
+ }
+
+ for (int c = 0; c < MULTIHASH_CHANNELS; c ++)
+ tiny_unlock (& table->channels[c].lock);
+}
diff --git a/src/libaudcore/multihash.h b/src/libaudcore/multihash.h
new file mode 100644
index 0000000..1e66b21
--- /dev/null
+++ b/src/libaudcore/multihash.h
@@ -0,0 +1,95 @@
+/*
+ * multihash.h
+ * Copyright 2013 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 LIBAUDCORE_MULTIHASH_H
+#define LIBAUDCORE_MULTIHASH_H
+
+#include <libaudcore/core.h>
+#include <libaudcore/tinylock.h>
+
+/* Multihash is a generic, thread-safe hash table. It scales well to multiple
+ * processors by the use of multiple channels, each with a separate lock. The
+ * hash value of a given node decides what channel it is stored in. Hence,
+ * different processors will tend to hit different channels, keeping lock
+ * contention to a minimum. The data structures are public in order to allow
+ * static initialization (setting all bytes to zero gives the initial state).
+ * The all-purpose lookup function enables a variety of atomic operations, such
+ * as allocating and adding a node only if not already present. */
+
+#define MULTIHASH_CHANNELS 16 /* must be a power of two */
+#define MULTIHASH_SHIFT 4 /* log (base 2) of MULTIHASH_CHANNELS */
+
+#define MULTIHASH_FOUND (1 << 0)
+#define MULTIHASH_ADDED (1 << 1)
+#define MULTIHASH_REMOVED (1 << 2)
+
+/* Skeleton structure containing internal member(s) of a multihash node. Actual
+ * node structures should be defined with MultihashNode as the first member. */
+typedef struct _MultihashNode {
+ struct _MultihashNode * next;
+} MultihashNode;
+
+/* Single channel of a multihash table. For internal use only. */
+typedef struct {
+ TinyLock lock;
+ MultihashNode * * buckets;
+ unsigned size, used;
+} MultihashChannel;
+
+/* Callback. Calculates (or retrieves) the hash value of <node>. */
+typedef unsigned (* MultihashFunc) (const MultihashNode * node);
+
+/* Callback. Returns TRUE if <node> matches <data>, otherwise FALSE. */
+typedef bool_t (* MultihashMatchFunc) (const MultihashNode * node,
+ const void * data, unsigned hash);
+
+/* Multihash table. <hash_func> and <match_func> should be initialized to
+ * functions appropriate for the type of data to be stored in the table. */
+typedef struct {
+ MultihashFunc hash_func;
+ MultihashMatchFunc match_func;
+ MultihashChannel channels[MULTIHASH_CHANNELS];
+} MultihashTable;
+
+/* Callback. May create a new node representing <data> to be added to the
+ * table. Returns the new node or NULL. */
+typedef MultihashNode * (* MultihashAddFunc) (const void * data, unsigned hash, void * state);
+
+/* Callback. Performs a user-defined action when a matching node is found.
+ * Doubles as a node removal function. Returns TRUE if <node> was freed and is
+ * to be removed, otherwise FALSE. */
+typedef bool_t (* MultihashActionFunc) (MultihashNode * node, void * state);
+
+/* All-purpose lookup function. The caller passes in the data to be looked up
+ * along with its hash value. The two callbacks are optional. <add> (if not
+ * NULL) is called if no matching node is found, and may return a new node to
+ * add to the table. <action> (if not NULL) is called if a matching node is
+ * found, and may return TRUE to remove the node from the table. <state> is
+ * forwarded to either callback. Returns the status of the lookup as a bitmask
+ * of MULTIHASH_FOUND, MULTIHASH_ADDED, and MULTIHASH_REMOVED. */
+int multihash_lookup (MultihashTable * table, const void * data, unsigned hash,
+ MultihashAddFunc add, MultihashActionFunc action, void * state);
+
+/* All-purpose iteration function. All channels of the table are locked
+ * simultaneously during the iteration to freeze the table in a consistent
+ * state. <action> is called on each node in order, and may return TRUE to
+ * remove the node from the table. <state> is forwarded to <action>. */
+void multihash_iterate (MultihashTable * table, MultihashActionFunc action, void * state);
+
+#endif /* LIBAUDCORE_MULTIHASH_H */
diff --git a/src/libaudcore/strpool.c b/src/libaudcore/strpool.c
index 634a84d..71e99b3 100644
--- a/src/libaudcore/strpool.c
+++ b/src/libaudcore/strpool.c
@@ -1,6 +1,6 @@
/*
* strpool.c
- * Copyright 2011 John Lindgren
+ * Copyright 2011-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,136 +17,237 @@
* the use of this software.
*/
-#include <glib.h>
-#include <pthread.h>
-#include <stdarg.h>
+#include <assert.h>
#include <stdio.h>
-#include <stdint.h>
#include <stdlib.h>
#include <string.h>
-#include "core.h"
+#include <glib.h>
-/* Each string in the pool is allocated with five leading bytes: a 32-bit
- * reference count and a one-byte signature, the '@' character. */
+#include "audstrings.h"
+#include "multihash.h"
-static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-static GHashTable * table;
+#ifdef VALGRIND_FRIENDLY
-static void str_destroy (void * str)
-{
- * ((char *) str - 1) = 0;
- free ((char *) str - 5);
-}
+typedef struct {
+ unsigned hash;
+ char magic;
+ char str[];
+} StrNode;
+
+#define NODE_SIZE_FOR(s) (offsetof (StrNode, str) + strlen (s) + 1)
+#define NODE_OF(s) ((StrNode *) ((s) - offsetof (StrNode, str)))
EXPORT char * str_get (const char * str)
{
if (! str)
return NULL;
- char * copy;
- pthread_mutex_lock (& mutex);
+ StrNode * node = g_malloc (NODE_SIZE_FOR (str));
+ node->magic = '@';
+ node->hash = g_str_hash (str);
- if (! table)
- table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, str_destroy);
+ strcpy (node->str, str);
+ return node->str;
+}
- if ((copy = g_hash_table_lookup (table, str)))
- {
- void * mem = copy - 5;
- (* (int32_t *) mem) ++;
- }
- else
- {
- void * mem = malloc (6 + strlen (str));
- (* (int32_t *) mem) = 1;
+EXPORT char * str_ref (const char * str)
+{
+ StrNode * node = NODE_OF (str);
+ assert (node->magic == '@');
+ assert (g_str_hash (str) == node->hash);
- copy = (char *) mem + 5;
- copy[-1] = '@';
- strcpy (copy, str);
+ return str_get (str);
+}
- g_hash_table_insert (table, copy, copy);
- }
+EXPORT void str_unref (char * str)
+{
+ if (! str)
+ return;
+
+ StrNode * node = NODE_OF (str);
+ assert (node->magic == '@');
+ assert (g_str_hash (str) == node->hash);
- pthread_mutex_unlock (& mutex);
- return copy;
+ node->magic = 0;
+ g_free (node);
}
-EXPORT char * str_ref (char * str)
+EXPORT unsigned str_hash (const char * str)
{
if (! str)
- return NULL;
+ return 0;
- pthread_mutex_lock (& mutex);
- STR_CHECK (str);
+ StrNode * node = NODE_OF (str);
+ assert (node->magic == '@');
+
+ return g_str_hash (str);
+}
- void * mem = str - 5;
- (* (int32_t *) mem) ++;
+EXPORT bool_t str_equal (const char * str1, const char * str2)
+{
+ assert (! str1 || NODE_OF (str1)->magic == '@');
+ assert (! str2 || NODE_OF (str2)->magic == '@');
- pthread_mutex_unlock (& mutex);
- return str;
+ return ! g_strcmp0 (str1, str2);
}
-EXPORT void str_unref (char * str)
+EXPORT void strpool_shutdown (void)
{
- if (! str)
- return;
+}
+
+#else /* ! VALGRIND_FRIENDLY */
- pthread_mutex_lock (& mutex);
- STR_CHECK (str);
+typedef struct {
+ MultihashNode node;
+ unsigned hash, refs;
+ char magic;
+ char str[];
+} StrNode;
- void * mem = str - 5;
- if (! -- (* (int32_t *) mem))
- g_hash_table_remove (table, str);
+#define NODE_SIZE_FOR(s) (offsetof (StrNode, str) + strlen (s) + 1)
+#define NODE_OF(s) ((StrNode *) ((s) - offsetof (StrNode, str)))
- pthread_mutex_unlock (& mutex);
+static unsigned hash_cb (const MultihashNode * node)
+{
+ return ((const StrNode *) node)->hash;
}
-EXPORT char * str_nget (const char * str, int len)
+static bool_t match_cb (const MultihashNode * node_, const void * data, unsigned hash)
{
- if (memchr (str, 0, len))
- return str_get (str);
+ const StrNode * node = (const StrNode *) node_;
+ return data == node->str || (hash == node->hash && ! strcmp (data, node->str));
+}
- char buf[len + 1];
- memcpy (buf, str, len);
- buf[len] = 0;
+static MultihashTable strpool_table = {
+ .hash_func = hash_cb,
+ .match_func = match_cb
+};
- return str_get (buf);
+static MultihashNode * add_cb (const void * data, unsigned hash, void * state)
+{
+ StrNode * node = g_malloc (NODE_SIZE_FOR (data));
+ node->hash = hash;
+ node->refs = 1;
+ node->magic = '@';
+ strcpy (node->str, data);
+
+ * ((char * *) state) = node->str;
+ return (MultihashNode *) node;
}
-EXPORT char * str_printf (const char * format, ...)
+static bool_t ref_cb (MultihashNode * node_, void * state)
{
- va_list args;
+ StrNode * node = (StrNode *) node_;
- va_start (args, format);
- int len = vsnprintf (NULL, 0, format, args);
- va_end (args);
+ __sync_fetch_and_add (& node->refs, 1);
- char buf[len + 1];
+ * ((char * *) state) = node->str;
+ return FALSE;
+}
- va_start (args, format);
- vsnprintf (buf, sizeof buf, format, args);
- va_end (args);
+EXPORT char * str_get (const char * str)
+{
+ if (! str)
+ return NULL;
- return str_get (buf);
+ char * ret = NULL;
+ multihash_lookup (& strpool_table, str, g_str_hash (str), add_cb, ref_cb, & ret);
+ return ret;
}
-EXPORT void strpool_abort (char * str)
+EXPORT char * str_ref (const char * str)
{
- fprintf (stderr, "String not in pool: %s\n", str);
- abort ();
+ if (! str)
+ return NULL;
+
+ StrNode * node = NODE_OF (str);
+ assert (node->magic == '@');
+
+ __sync_fetch_and_add (& node->refs, 1);
+
+ return (char *) str;
}
-static void str_leaked (void * key, void * str, void * unused)
+static bool_t remove_cb (MultihashNode * node_, void * state)
{
- fprintf (stderr, "String not freed: %s\n", (char *) str);
+ StrNode * node = (StrNode *) node_;
+
+ if (! __sync_bool_compare_and_swap (& node->refs, 1, 0))
+ return FALSE;
+
+ node->magic = 0;
+ g_free (node);
+ return TRUE;
}
-EXPORT void strpool_shutdown (void)
+EXPORT void str_unref (char * str)
{
- if (! table)
+ if (! str)
return;
- g_hash_table_foreach (table, str_leaked, NULL);
- g_hash_table_destroy (table);
- table = NULL;
+ StrNode * node = NODE_OF (str);
+ assert (node->magic == '@');
+
+ while (1)
+ {
+ int refs = __sync_fetch_and_add (& node->refs, 0);
+
+ if (refs > 1)
+ {
+ if (__sync_bool_compare_and_swap (& node->refs, refs, refs - 1))
+ break;
+ }
+ else
+ {
+ int status = multihash_lookup (& strpool_table, node->str,
+ node->hash, NULL, remove_cb, NULL);
+
+ assert (status & MULTIHASH_FOUND);
+ if (status & MULTIHASH_REMOVED)
+ break;
+ }
+ }
+}
+
+static bool_t leak_cb (MultihashNode * node, void * state)
+{
+ fprintf (stderr, "String leaked: %s\n", ((StrNode *) node)->str);
+ return FALSE;
+}
+
+EXPORT void strpool_shutdown (void)
+{
+ multihash_iterate (& strpool_table, leak_cb, NULL);
+}
+
+EXPORT unsigned str_hash (const char * str)
+{
+ if (! str)
+ return 0;
+
+ StrNode * node = NODE_OF (str);
+ assert (node->magic == '@');
+
+ return node->hash;
+}
+
+
+EXPORT bool_t str_equal (const char * str1, const char * str2)
+{
+ assert (! str1 || NODE_OF (str1)->magic == '@');
+ assert (! str2 || NODE_OF (str2)->magic == '@');
+
+ return str1 == str2;
+}
+
+#endif /* ! VALGRIND_FRIENDLY */
+
+EXPORT char * str_nget (const char * str, int len)
+{
+ if (memchr (str, 0, len))
+ return str_get (str);
+
+ SNCOPY (buf, str, len);
+ return str_get (buf);
}
diff --git a/src/libaudcore/tinylock.c b/src/libaudcore/tinylock.c
new file mode 100644
index 0000000..873aff1
--- /dev/null
+++ b/src/libaudcore/tinylock.c
@@ -0,0 +1,65 @@
+/*
+ * tinylock.c
+ * Copyright 2013 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 "tinylock.h"
+
+#ifndef VALGRIND_FRIENDLY
+
+#include <limits.h>
+#include <sched.h>
+
+#define WRITE_BIT (SHRT_MAX + 1)
+
+EXPORT void tiny_lock (TinyLock * lock)
+{
+ while (__builtin_expect (__sync_lock_test_and_set (lock, 1), 0))
+ sched_yield ();
+}
+
+EXPORT void tiny_unlock (TinyLock * lock)
+{
+ __sync_lock_release (lock);
+}
+
+EXPORT void tiny_lock_read (TinyRWLock * lock)
+{
+ while (__builtin_expect (__sync_fetch_and_add (lock, 1) & WRITE_BIT, 0))
+ {
+ __sync_fetch_and_sub (lock, 1);
+ sched_yield ();
+ }
+}
+
+EXPORT void tiny_unlock_read (TinyRWLock * lock)
+{
+ __sync_fetch_and_sub (lock, 1);
+}
+
+EXPORT void tiny_lock_write (TinyRWLock * lock)
+{
+ while (! __builtin_expect (__sync_bool_compare_and_swap (lock, 0, WRITE_BIT), 1))
+ sched_yield ();
+}
+
+EXPORT void tiny_unlock_write (TinyRWLock * lock)
+{
+ __sync_fetch_and_sub (lock, WRITE_BIT);
+}
+
+#endif /* ! VALGRIND_FRIENDLY */
diff --git a/src/libaudcore/tinylock.h.in b/src/libaudcore/tinylock.h.in
new file mode 100644
index 0000000..873af1b
--- /dev/null
+++ b/src/libaudcore/tinylock.h.in
@@ -0,0 +1,56 @@
+/*
+ * tinylock.h
+ * Copyright 2013 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 LIBAUDCORE_TINYLOCK_H
+#define LIBAUDCORE_TINYLOCK_H
+
+#if @VALGRIND_FRIENDLY@ /* VALGRIND_FRIENDLY */
+
+#include <pthread.h>
+
+typedef pthread_mutex_t TinyLock;
+typedef pthread_rwlock_t TinyRWLock;
+
+#define tiny_lock pthread_mutex_lock
+#define tiny_unlock pthread_mutex_unlock
+#define tiny_lock_read pthread_rwlock_rdlock
+#define tiny_unlock_read pthread_rwlock_unlock
+#define tiny_lock_write pthread_rwlock_wrlock
+#define tiny_unlock_write pthread_rwlock_unlock
+
+#else /* ! VALGRIND_FRIENDLY */
+
+/* TinyLock is an extremely low-overhead lock object (in terms of speed and
+ * memory usage). It makes no guarantees of fair scheduling, however. */
+
+typedef char TinyLock;
+
+void tiny_lock (TinyLock * lock);
+void tiny_unlock (TinyLock * lock);
+
+typedef unsigned short TinyRWLock;
+
+void tiny_lock_read (TinyRWLock * lock);
+void tiny_unlock_read (TinyRWLock * lock);
+void tiny_lock_write (TinyRWLock * lock);
+void tiny_unlock_write (TinyRWLock * lock);
+
+#endif /* ! VALGRIND_FRIENDLY */
+
+#endif /* LIBAUDCORE_TINYLOCK_H */
diff --git a/src/libaudcore/tuple.c b/src/libaudcore/tuple.c
index f9a65dc..7974c8b 100644
--- a/src/libaudcore/tuple.c
+++ b/src/libaudcore/tuple.c
@@ -1,6 +1,6 @@
/*
* tuple.c
- * Copyright 2007-2011 William Pitcock, Christian Birchinger, Matti HÀmÀlÀinen,
+ * Copyright 2007-2013 William Pitcock, Christian Birchinger, Matti HÀmÀlÀinen,
* Giacomo Lozito, Eugene Zagidullin, and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
@@ -18,13 +18,7 @@
* the use of this software.
*/
-/**
- * @file tuple.c
- * @brief Basic Tuple handling API.
- */
-
#include <glib.h>
-#include <pthread.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
@@ -33,8 +27,12 @@
#include <audacious/i18n.h>
#include "audstrings.h"
+#include "tinylock.h"
#include "tuple.h"
-#include "tuple_formatter.h"
+
+#if TUPLE_FIELDS > 64
+#error The current tuple implementation is limited to 64 fields
+#endif
#define BLOCK_VALS 4
@@ -61,20 +59,25 @@ struct _TupleBlock {
* metadata. This is not the same as a playlist entry, though.
*/
struct _Tuple {
- int refcount;
int64_t setmask;
TupleBlock * blocks;
- int nsubtunes; /**< Number of subtunes, if any. Values greater than 0
- mean that there are subtunes and #subtunes array
- may be set. */
int *subtunes; /**< Array of int containing subtune index numbers.
Can be NULL if indexing is linear or if
there are no subtunes. */
+ int nsubtunes; /**< Number of subtunes, if any. Values greater than 0
+ mean that there are subtunes and #subtunes array
+ may be set. */
+
+ int refcount;
+ TinyLock lock;
};
#define BIT(i) ((int64_t) 1 << (i))
+#define LOCK(t) tiny_lock ((TinyLock *) & t->lock)
+#define UNLOCK(t) tiny_unlock ((TinyLock *) & t->lock)
+
/** Ordered table of basic #Tuple field names and their #TupleValueType.
*/
static const TupleBasicType tuple_fields[TUPLE_FIELDS] = {
@@ -154,8 +157,8 @@ static const FieldDictEntry field_dict[TUPLE_FIELDS] = {
{"track-number", FIELD_TRACK_NUMBER},
{"year", FIELD_YEAR}};
-static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-
+#define VALID_FIELD(f) ((f) >= 0 && (f) < TUPLE_FIELDS)
+#define FIELD_TYPE(f) (tuple_fields[f].type)
static int field_dict_compare (const void * a, const void * b)
{
@@ -168,26 +171,18 @@ EXPORT int tuple_field_by_name (const char * name)
FieldDictEntry * found = bsearch (& find, field_dict, TUPLE_FIELDS,
sizeof (FieldDictEntry), field_dict_compare);
- if (found)
- return found->field;
-
- fprintf (stderr, "Unknown tuple field name \"%s\".\n", name);
- return -1;
+ return found ? found->field : -1;
}
EXPORT const char * tuple_field_get_name (int field)
{
- if (field < 0 || field >= TUPLE_FIELDS)
- return NULL;
-
+ g_return_val_if_fail (VALID_FIELD (field), NULL);
return tuple_fields[field].name;
}
EXPORT TupleValueType tuple_field_get_type (int field)
{
- if (field < 0 || field >= TUPLE_FIELDS)
- return TUPLE_UNKNOWN;
-
+ g_return_val_if_fail (VALID_FIELD (field), TUPLE_UNKNOWN);
return tuple_fields[field].type;
}
@@ -240,7 +235,7 @@ static TupleVal * lookup_val (Tuple * tuple, int field, bool_t add, bool_t remov
return & block->vals[0];
}
-static void tuple_destroy_unlocked (Tuple * tuple)
+static void tuple_destroy (Tuple * tuple)
{
TupleBlock * next;
for (TupleBlock * block = tuple->blocks; block; block = next)
@@ -254,13 +249,10 @@ static void tuple_destroy_unlocked (Tuple * tuple)
str_unref (block->vals[i].str);
}
- memset (block, 0, sizeof (TupleBlock));
g_slice_free (TupleBlock, block);
}
- g_free(tuple->subtunes);
-
- memset (tuple, 0, sizeof (Tuple));
+ g_free (tuple->subtunes);
g_slice_free (Tuple, tuple);
}
@@ -273,11 +265,8 @@ EXPORT Tuple * tuple_new (void)
EXPORT Tuple * tuple_ref (Tuple * tuple)
{
- pthread_mutex_lock (& mutex);
+ __sync_fetch_and_add (& tuple->refcount, 1);
- tuple->refcount ++;
-
- pthread_mutex_unlock (& mutex);
return tuple;
}
@@ -286,22 +275,10 @@ EXPORT void tuple_unref (Tuple * tuple)
if (! tuple)
return;
- pthread_mutex_lock (& mutex);
-
- if (! -- tuple->refcount)
- tuple_destroy_unlocked (tuple);
-
- pthread_mutex_unlock (& mutex);
+ if (! __sync_sub_and_fetch (& tuple->refcount, 1))
+ tuple_destroy (tuple);
}
-/**
- * Sets filename/URI related fields of a #Tuple structure, based
- * on the given filename argument. The fields set are:
- * #FIELD_FILE_PATH, #FIELD_FILE_NAME and #FIELD_FILE_EXT.
- *
- * @param[in] filename Filename URI.
- * @param[in,out] tuple Tuple structure to manipulate.
- */
EXPORT void tuple_set_filename (Tuple * tuple, const char * filename)
{
const char * base, * ext, * sub;
@@ -311,34 +288,27 @@ EXPORT void tuple_set_filename (Tuple * tuple, const char * filename)
char path[base - filename + 1];
str_decode_percent (filename, base - filename, path);
- tuple_set_str (tuple, FIELD_FILE_PATH, NULL, path);
+ tuple_set_str (tuple, FIELD_FILE_PATH, path);
char name[ext - base + 1];
str_decode_percent (base, ext - base, name);
- tuple_set_str (tuple, FIELD_FILE_NAME, NULL, name);
+ tuple_set_str (tuple, FIELD_FILE_NAME, name);
if (ext < sub)
{
char extbuf[sub - ext];
str_decode_percent (ext + 1, sub - ext - 1, extbuf);
- tuple_set_str (tuple, FIELD_FILE_EXT, NULL, extbuf);
+ tuple_set_str (tuple, FIELD_FILE_EXT, extbuf);
}
if (sub[0])
- tuple_set_int (tuple, FIELD_SUBSONG_ID, NULL, isub);
+ tuple_set_int (tuple, FIELD_SUBSONG_ID, isub);
}
-/**
- * Creates a copy of given Tuple structure, with copied data.
- *
- * @param[in] src Tuple structure to be made a copy of.
- * @return Pointer to newly allocated Tuple.
- */
EXPORT Tuple * tuple_copy (const Tuple * old)
{
- pthread_mutex_lock (& mutex);
-
Tuple * new = tuple_new ();
+ LOCK (old);
for (int f = 0; f < TUPLE_FIELDS; f ++)
{
@@ -358,83 +328,56 @@ EXPORT Tuple * tuple_copy (const Tuple * old)
if (old->subtunes)
new->subtunes = g_memdup (old->subtunes, sizeof (int) * old->nsubtunes);
- pthread_mutex_unlock (& mutex);
+ UNLOCK (old);
return new;
}
-/**
- * Allocates a new #Tuple structure, setting filename/URI related
- * fields based on the given filename argument by calling #tuple_set_filename.
- *
- * @param[in] filename Filename URI.
- * @return Pointer to newly allocated Tuple.
- */
-EXPORT Tuple *
-tuple_new_from_filename(const char *filename)
+EXPORT Tuple * tuple_new_from_filename (const char * filename)
{
- Tuple *tuple = tuple_new();
-
- tuple_set_filename(tuple, filename);
+ Tuple * tuple = tuple_new ();
+ tuple_set_filename (tuple, filename);
return tuple;
}
-EXPORT void tuple_set_int (Tuple * tuple, int nfield, const char * field, int x)
+EXPORT void tuple_set_int (Tuple * tuple, int field, int x)
{
- if (nfield < 0)
- nfield = tuple_field_by_name (field);
- if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_INT)
- return;
+ g_return_if_fail (VALID_FIELD (field) && FIELD_TYPE (field) == TUPLE_INT);
+ LOCK (tuple);
- pthread_mutex_lock (& mutex);
-
- TupleVal * val = lookup_val (tuple, nfield, TRUE, FALSE);
+ TupleVal * val = lookup_val (tuple, field, TRUE, FALSE);
val->x = x;
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
}
-EXPORT void tuple_set_str (Tuple * tuple, int nfield, const char * field, const char * str)
+EXPORT void tuple_set_str (Tuple * tuple, int field, const char * str)
{
- if (! str)
- {
- tuple_unset (tuple, nfield, field);
- return;
- }
+ g_return_if_fail (VALID_FIELD (field) && FIELD_TYPE (field) == TUPLE_STRING);
- if (! g_utf8_validate (str, -1, NULL))
+ if (! str)
{
- fprintf (stderr, "Invalid UTF-8: %s\n", str);
+ tuple_unset (tuple, field);
return;
}
- if (nfield < 0)
- nfield = tuple_field_by_name (field);
- if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_STRING)
- return;
-
- pthread_mutex_lock (& mutex);
+ LOCK (tuple);
- TupleVal * val = lookup_val (tuple, nfield, TRUE, FALSE);
- if (val->str)
- str_unref (val->str);
- val->str = str_get (str);
+ TupleVal * val = lookup_val (tuple, field, TRUE, FALSE);
+ str_unref (val->str);
+ val->str = str_to_utf8 (str, -1);
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
}
-EXPORT void tuple_unset (Tuple * tuple, int nfield, const char * field)
+EXPORT void tuple_unset (Tuple * tuple, int field)
{
- if (nfield < 0)
- nfield = tuple_field_by_name (field);
- if (nfield < 0 || nfield >= TUPLE_FIELDS)
- return;
+ g_return_if_fail (VALID_FIELD (field));
+ LOCK (tuple);
- pthread_mutex_lock (& mutex);
-
- TupleVal * val = lookup_val (tuple, nfield, FALSE, TRUE);
+ TupleVal * val = lookup_val (tuple, field, FALSE, TRUE);
if (val)
{
- if (tuple_fields[nfield].type == TUPLE_STRING)
+ if (tuple_fields[field].type == TUPLE_STRING)
{
str_unref (val->str);
val->str = NULL;
@@ -443,96 +386,52 @@ EXPORT void tuple_unset (Tuple * tuple, int nfield, const char * field)
val->x = 0;
}
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
}
-/**
- * Returns #TupleValueType of given #Tuple field.
- * Desired field can be specified either by key name or if it is
- * one of basic fields, by #TupleBasicType index.
- *
- * @param[in] tuple #Tuple structure pointer.
- * @param[in] cnfield #TupleBasicType index or -1 if key name is to be used instead.
- * @param[in] field String acting as key name or NULL if nfield is used.
- * @return #TupleValueType of the field or TUPLE_UNKNOWN if there was an error.
- */
-EXPORT TupleValueType tuple_get_value_type (const Tuple * tuple, int nfield, const char * field)
+EXPORT TupleValueType tuple_get_value_type (const Tuple * tuple, int field)
{
- if (nfield < 0)
- nfield = tuple_field_by_name (field);
- if (nfield < 0 || nfield >= TUPLE_FIELDS)
- return TUPLE_UNKNOWN;
-
- pthread_mutex_lock (& mutex);
+ g_return_val_if_fail (VALID_FIELD (field), TUPLE_UNKNOWN);
+ LOCK (tuple);
- TupleValueType type = TUPLE_UNKNOWN;
+ TupleVal * val = lookup_val ((Tuple *) tuple, field, FALSE, FALSE);
+ TupleValueType type = val ? FIELD_TYPE (field) : TUPLE_UNKNOWN;
- TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE);
- if (val)
- type = tuple_fields[nfield].type;
-
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
return type;
}
-EXPORT char * tuple_get_str (const Tuple * tuple, int nfield, const char * field)
+EXPORT char * tuple_get_str (const Tuple * tuple, int field)
{
- if (nfield < 0)
- nfield = tuple_field_by_name (field);
- if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_STRING)
- return NULL;
-
- pthread_mutex_lock (& mutex);
+ g_return_val_if_fail (VALID_FIELD (field) && FIELD_TYPE (field) == TUPLE_STRING, NULL);
+ LOCK (tuple);
- char * str = NULL;
+ TupleVal * val = lookup_val ((Tuple *) tuple, field, FALSE, FALSE);
+ char * str = val ? str_ref (val->str) : NULL;
- TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE);
- if (val)
- str = str_ref (val->str);
-
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
return str;
}
-/**
- * Returns integer associated to #Tuple field.
- * Desired field can be specified either by key name or if it is
- * one of basic fields, by #TupleBasicType index.
- *
- * @param[in] tuple #Tuple structure pointer.
- * @param[in] cnfield #TupleBasicType index or -1 if key name is to be used instead.
- * @param[in] field String acting as key name or NULL if nfield is used.
- * @return Integer value or 0 if the field/key did not exist.
- *
- * @bug There is no way to distinguish error situations if the associated value is zero.
- */
-EXPORT int tuple_get_int (const Tuple * tuple, int nfield, const char * field)
+EXPORT int tuple_get_int (const Tuple * tuple, int field)
{
- if (nfield < 0)
- nfield = tuple_field_by_name (field);
- if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_INT)
- return 0;
-
- pthread_mutex_lock (& mutex);
+ g_return_val_if_fail (VALID_FIELD (field) && FIELD_TYPE (field) == TUPLE_INT, -1);
+ LOCK (tuple);
- int x = 0;
-
- TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE);
- if (val)
- x = val->x;
+ TupleVal * val = lookup_val ((Tuple *) tuple, field, FALSE, FALSE);
+ int x = val ? val->x : -1;
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
return x;
}
-#define APPEND(b, ...) snprintf (b + strlen (b), sizeof b - strlen (b), \
- __VA_ARGS__)
+#define APPEND(b, ...) snprintf (b + strlen (b), sizeof b - strlen (b), __VA_ARGS__)
EXPORT void tuple_set_format (Tuple * t, const char * format, int chans, int rate,
int brate)
{
if (format)
- tuple_set_str (t, FIELD_CODEC, NULL, format);
+ tuple_set_str (t, FIELD_CODEC, format);
char buf[32];
buf[0] = 0;
@@ -544,8 +443,7 @@ EXPORT void tuple_set_format (Tuple * t, const char * format, int chans, int rat
else if (chans == 2)
APPEND (buf, _("Stereo"));
else
- APPEND (buf, dngettext (PACKAGE, "%d channel", "%d channels",
- chans), chans);
+ APPEND (buf, dngettext (PACKAGE, "%d channel", "%d channels", chans), chans);
if (rate > 0)
APPEND (buf, ", ");
@@ -555,15 +453,15 @@ EXPORT void tuple_set_format (Tuple * t, const char * format, int chans, int rat
APPEND (buf, "%d kHz", rate / 1000);
if (buf[0])
- tuple_set_str (t, FIELD_QUALITY, NULL, buf);
+ tuple_set_str (t, FIELD_QUALITY, buf);
if (brate > 0)
- tuple_set_int (t, FIELD_BITRATE, NULL, brate);
+ tuple_set_int (t, FIELD_BITRATE, brate);
}
EXPORT void tuple_set_subtunes (Tuple * tuple, int n_subtunes, const int * subtunes)
{
- pthread_mutex_lock (& mutex);
+ LOCK (tuple);
g_free (tuple->subtunes);
tuple->subtunes = NULL;
@@ -572,45 +470,27 @@ EXPORT void tuple_set_subtunes (Tuple * tuple, int n_subtunes, const int * subtu
if (subtunes)
tuple->subtunes = g_memdup (subtunes, sizeof (int) * n_subtunes);
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
}
EXPORT int tuple_get_n_subtunes (Tuple * tuple)
{
- pthread_mutex_lock (& mutex);
+ LOCK (tuple);
int n_subtunes = tuple->nsubtunes;
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
return n_subtunes;
}
EXPORT int tuple_get_nth_subtune (Tuple * tuple, int n)
{
- pthread_mutex_lock (& mutex);
+ LOCK (tuple);
int subtune = -1;
if (n >= 0 && n < tuple->nsubtunes)
subtune = tuple->subtunes ? tuple->subtunes[n] : 1 + n;
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
return subtune;
}
-
-EXPORT char * tuple_format_title (Tuple * tuple, const char * format)
-{
- static const gint fallbacks[] = {FIELD_TITLE, FIELD_FILE_NAME, FIELD_FILE_PATH};
-
- char * title = tuple_formatter_process_string (tuple, format);
-
- for (int i = 0; i < G_N_ELEMENTS (fallbacks); i ++)
- {
- if (title && title[0])
- break;
-
- str_unref (title);
- title = tuple_get_str (tuple, fallbacks[i], NULL);
- }
-
- return title ? title : str_get ("");
-}
diff --git a/src/libaudcore/tuple.h b/src/libaudcore/tuple.h
index 55f3e2e..873f699 100644
--- a/src/libaudcore/tuple.h
+++ b/src/libaudcore/tuple.h
@@ -1,6 +1,6 @@
/*
* tuple.h
- * Copyright 2007-2011 William Pitcock, Christian Birchinger, Matti HÀmÀlÀinen,
+ * Copyright 2007-2013 William Pitcock, Christian Birchinger, Matti HÀmÀlÀinen,
* Giacomo Lozito, Eugene Zagidullin, and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
@@ -90,6 +90,7 @@ const char * tuple_field_get_name (int field);
TupleValueType tuple_field_get_type (int field);
typedef struct _Tuple Tuple;
+typedef struct _TupleFormatter TupleFormatter;
/* Creates a new, blank tuple with a reference count of one. */
Tuple * tuple_new (void);
@@ -105,45 +106,41 @@ void tuple_unref (Tuple * tuple);
/* Makes a copy of <tuple>. Only use tuple_copy() if you need to modify one
* copy of the tuple while not modifying the other. In most cases, tuple_ref()
* is more appropriate. */
-Tuple *tuple_copy(const Tuple *);
+Tuple * tuple_copy (const Tuple * tuple);
/* Parses the URI <filename> and sets FIELD_FILE_NAME, FIELD_FILE_PATH,
* FIELD_FILE_EXT, and FIELD_SUBSONG_ID accordingly. */
-void tuple_set_filename(Tuple *tuple, const char *filename);
+void tuple_set_filename (Tuple * tuple, const char * filename);
/* Convenience function, equivalent to calling tuple_new() and then
* tuple_set_filename(). */
-Tuple *tuple_new_from_filename(const char *filename);
+Tuple * tuple_new_from_filename (const char * filename);
/* Sets a field to the integer value <x>. */
-void tuple_set_int (Tuple * tuple, int nfield, const char * field, int x);
+void tuple_set_int (Tuple * tuple, int field, int x);
-/* Sets the field specified by <nfield> (one of the FIELD_* constants) or
- * <field> (one of the names returned by tuple_field_get_name() to the string
- * value <str>. Only one of <nfield> or <field> may be set. If <nfield> is
- * set, <field> must be NULL; if <field> is set, <nfield> must be -1. As a
+/* Sets a field to the string value <str>. If <str> is not valid UTF-8, it will
+ * be converted according to the user's character set detection rules. As a
* special case, if <str> is NULL, the result is equivalent to calling
* tuple_unset(). */
-void tuple_set_str (Tuple * tuple, int nfield, const char * field, const char * str);
+void tuple_set_str (Tuple * tuple, int field, const char * str);
/* Clears any value that a field is currently set to. */
-void tuple_unset (Tuple * tuple, int nfield, const char * field);
+void tuple_unset (Tuple * tuple, int field);
/* Returns the value type of a field, or TUPLE_UNKNOWN if the field has not been
* set to any value. */
-TupleValueType tuple_get_value_type (const Tuple * tuple, int nfield,
- const char * field);
+TupleValueType tuple_get_value_type (const Tuple * tuple, int field);
/* Returns the string value of a field. The returned string is pooled and must
* be released with str_unref() when no longer needed. If the field has not
* been set to any value, returns NULL. */
-char * tuple_get_str (const Tuple * tuple, int nfield, const char * field);
+char * tuple_get_str (const Tuple * tuple, int field);
/* Returns the integer value of a field. If the field has not been set to any
- * value, returns 0. (In hindsight, it would have been better to return -1 in
- * this case. If you need to distinguish between a value of 0 and a field not
- * set to any value, use tuple_get_value_type().) */
-int tuple_get_int (const Tuple * tuple, int nfield, const char * field);
+ * value, returns -1. If you need to distinguish between a value of -1 and a
+ * field not set to any value, use tuple_get_value_type(). */
+int tuple_get_int (const Tuple * tuple, int field);
/* Fills in format-related fields (specifically FIELD_CODEC, FIELD_QUALITY, and
* FIELD_BITRATE). Plugins should use this function instead of setting these
@@ -168,10 +165,16 @@ int tuple_get_n_subtunes (Tuple * tuple);
/* Returns the <n>th member of the subtune array. */
int tuple_get_nth_subtune (Tuple * tuple, int n);
-/* Generates a formatted title string for <tuple> according to <format>. The
- * syntax of <format> is documented in tuple_formatter.c. The returned string
- * is pooled and must be released with str_unref() when no longer need. The
- * returned string is never NULL, though it may be the empty string. */
-char * tuple_format_title (Tuple * tuple, const char * format);
+/* Creates a tuple formatter object for the given format. The syntax of
+ * <format> is documented in tuple_formatter.c. */
+TupleFormatter * tuple_formatter_new (const char * format);
+
+/* Destroys a tuple formatter object. */
+void tuple_formatter_free (TupleFormatter * formatter);
+
+/* Generates a title string for <tuple> using the given formatter object. The
+ * returned string is pooled and must be released with str_unref() when no
+ * longer needed. Never returns NULL, but may return an empty string. */
+char * tuple_format_title (TupleFormatter * formatter, const Tuple * tuple);
#endif /* LIBAUDCORE_TUPLE_H */
diff --git a/src/libaudcore/tuple_compiler.c b/src/libaudcore/tuple_compiler.c
index 4834e17..74584c1 100644
--- a/src/libaudcore/tuple_compiler.c
+++ b/src/libaudcore/tuple_compiler.c
@@ -1,7 +1,7 @@
/*
* tuple_compiler.c
* Copyright (c) 2007 Matti 'ccr' HÀmÀlÀinen
- * Copyright (c) 2011 John Lindgren
+ * Copyright (c) 2011-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -31,7 +31,6 @@
* currently there is just a single context, is a "global" context needed?
*/
-#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
@@ -39,18 +38,19 @@
#include <glib.h>
+#include "audstrings.h"
#include "tuple_compiler.h"
-#define MAX_STR (256)
-#define MIN_ALLOC_NODES (8)
-#define MIN_ALLOC_BUF (64)
+#define MAX_STR (256)
#define TUPLEZ_MAX_VARS (4)
+#define GET_VAR(c, i) (& g_array_index ((c), TupleEvalVar, (i)))
+
#define tuple_error(ctx, ...) fprintf (stderr, "Tuple compiler: " __VA_ARGS__)
enum {
- OP_RAW = 0, /* plain text */
- OP_FIELD, /* a field/variable */
+ OP_RAW = 0, /* plain text */
+ OP_FIELD, /* a field/variable */
OP_EXISTS,
OP_EQUALS,
OP_NOT_EQUALS,
@@ -67,42 +67,28 @@ enum {
};
struct _TupleEvalNode {
- int opcode; /* operator, see OP_ enums */
- int var[TUPLEZ_MAX_VARS]; /* tuple variable references */
- char *text; /* raw text, if any (OP_RAW) */
+ int opcode; /* operator, see OP_ enums */
+ int var[TUPLEZ_MAX_VARS]; /* tuple variable references */
+ char *text; /* raw text, if any (OP_RAW) */
struct _TupleEvalNode *children, *next, *prev; /* children of this struct, and pointer to next node. */
};
typedef struct {
char *name;
- int type; /* Type of variable, see VAR_* */
+ int type; /* Type of variable, see VAR_* */
int defvali;
- TupleValueType ctype; /* Type of constant/def value */
+ TupleValueType ctype; /* Type of constant/def value */
- int fieldidx; /* if >= 0: Index # of "pre-defined" Tuple fields */
+ int fieldidx; /* if >= 0: Index # of "pre-defined" Tuple fields */
bool_t fieldread, fieldvalid;
char * fieldstr;
} TupleEvalVar;
-struct _TupleEvalContext {
- int nvariables;
- TupleEvalVar **variables;
-};
-
-
-static void tuple_evalctx_free_var(TupleEvalVar *var)
-{
- g_free(var->name);
- str_unref (var->fieldstr);
- g_free(var);
-}
-
-
/* Initialize an evaluation context
*/
TupleEvalContext * tuple_evalctx_new(void)
{
- return g_new0(TupleEvalContext, 1);
+ return g_array_new (FALSE, TRUE, sizeof (TupleEvalVar));
}
@@ -110,15 +96,15 @@ TupleEvalContext * tuple_evalctx_new(void)
*/
void tuple_evalctx_reset(TupleEvalContext *ctx)
{
- int i;
-
- for (i = 0; i < ctx->nvariables; i++)
- if (ctx->variables[i]) {
- ctx->variables[i]->fieldread = FALSE;
- ctx->variables[i]->fieldvalid = FALSE;
- str_unref (ctx->variables[i]->fieldstr);
- ctx->variables[i]->fieldstr = NULL;
- }
+ for (int i = 0; i < ctx->len; i ++)
+ {
+ TupleEvalVar * var = GET_VAR (ctx, i);
+
+ var->fieldread = FALSE;
+ var->fieldvalid = FALSE;
+ str_unref (var->fieldstr);
+ var->fieldstr = NULL;
+ }
}
@@ -126,57 +112,52 @@ void tuple_evalctx_reset(TupleEvalContext *ctx)
*/
void tuple_evalctx_free(TupleEvalContext *ctx)
{
- int i;
-
- if (!ctx) return;
+ for (int i = 0; i < ctx->len; i ++)
+ {
+ TupleEvalVar * var = GET_VAR (ctx, i);
- /* Deallocate variables */
- for (i = 0; i < ctx->nvariables; i++)
- if (ctx->variables[i])
- tuple_evalctx_free_var(ctx->variables[i]);
+ str_unref (var->name);
+ str_unref (var->fieldstr);
+ }
- g_free(ctx->variables);
- g_free(ctx);
+ g_array_free (ctx, TRUE);
}
+/* note: may invalidate TupleEvalVar pointers due to reallocation */
static int tuple_evalctx_add_var (TupleEvalContext * ctx, const char * name,
const int type, const TupleValueType ctype)
{
- int i;
- TupleEvalVar *tmp = g_new0(TupleEvalVar, 1);
+ int field = -1;
+
+ if (type == TUPLE_VAR_FIELD)
+ {
+ field = tuple_field_by_name (name);
+ if (field < 0)
+ return -1;
+ }
+
+ int i = ctx->len;
+ g_array_set_size (ctx, i + 1);
- tmp->name = g_strdup(name);
- tmp->type = type;
- tmp->fieldidx = -1;
- tmp->ctype = ctype;
+ TupleEvalVar * var = GET_VAR (ctx, i);
+
+ var->name = str_get (name);
+ var->type = type;
+ var->fieldidx = field;
+ var->ctype = ctype;
- /* Find fieldidx, if any */
switch (type) {
case TUPLE_VAR_FIELD:
- tmp->fieldidx = tuple_field_by_name (name);
- tmp->ctype = tuple_field_get_type (tmp->fieldidx);
+ var->ctype = tuple_field_get_type (field);
break;
case TUPLE_VAR_CONST:
if (ctype == TUPLE_INT)
- tmp->defvali = atoi(name);
+ var->defvali = atoi (name);
break;
}
- /* Find a free slot */
- for (i = 0; i < ctx->nvariables; i++)
- if (!ctx->variables[i]) {
- ctx->variables[i] = tmp;
- return i;
- }
-
- i = ctx->nvariables;
- ctx->variables = g_renew(TupleEvalVar *, ctx->variables, ctx->nvariables + MIN_ALLOC_NODES);
- memset(&(ctx->variables[ctx->nvariables]), 0, MIN_ALLOC_NODES * sizeof(TupleEvalVar *));
- ctx->nvariables += MIN_ALLOC_NODES;
- ctx->variables[i] = tmp;
-
return i;
}
@@ -196,12 +177,6 @@ static void tuple_evalnode_insert(TupleEvalNode **nodes, TupleEvalNode *node)
}
-static TupleEvalNode *tuple_evalnode_new(void)
-{
- return g_new0(TupleEvalNode, 1);
-}
-
-
void tuple_evalnode_free(TupleEvalNode *expr)
{
TupleEvalNode *curr = expr, *next;
@@ -209,12 +184,12 @@ void tuple_evalnode_free(TupleEvalNode *expr)
while (curr) {
next = curr->next;
- g_free(curr->text);
+ str_unref (curr->text);
if (curr->children)
tuple_evalnode_free(curr->children);
- g_free(curr);
+ g_slice_free (TupleEvalNode, curr);
curr = next;
}
@@ -246,11 +221,11 @@ static bool_t tc_get_item(TupleEvalContext *ctx,
}
if (*literal == FALSE) {
- while (*s != '\0' && *s != tmpendch && (isalnum(*s) || *s == '-') && i < (max - 1)) {
+ while (*s != '\0' && *s != tmpendch && (g_ascii_isalnum(*s) || *s == '-') && i < (max - 1)) {
buf[i++] = *(s++);
}
- if (*s != tmpendch && *s != '}' && !isalnum(*s) && *s != '-') {
+ if (*s != tmpendch && *s != '}' && !g_ascii_isalnum(*s) && *s != '-') {
tuple_error(ctx, "Invalid field '%s' in '%s'.\n", *str, item);
return FALSE;
} else if (*s != tmpendch) {
@@ -286,20 +261,17 @@ static bool_t tc_get_item(TupleEvalContext *ctx,
static int tc_get_variable(TupleEvalContext *ctx, char *name, int type)
{
- int i;
TupleValueType ctype = TUPLE_UNKNOWN;
- if (name == '\0') return -1;
-
- if (isdigit(name[0])) {
+ if (g_ascii_isdigit(name[0])) {
ctype = TUPLE_INT;
type = TUPLE_VAR_CONST;
} else
ctype = TUPLE_STRING;
if (type != TUPLE_VAR_CONST) {
- for (i = 0; i < ctx->nvariables; i++)
- if (ctx->variables[i] && !strcmp(ctx->variables[i]->name, name))
+ for (int i = 0; i < ctx->len; i ++)
+ if (! strcmp (GET_VAR (ctx, i)->name, name))
return i;
}
@@ -317,7 +289,7 @@ static bool_t tc_parse_construct(TupleEvalContext *ctx, TupleEvalNode **res,
if (tc_get_item(ctx, c, tmps1, MAX_STR, ',', &literal1, "tag1", item)) {
(*c)++;
if (tc_get_item(ctx, c, tmps2, MAX_STR, ':', &literal2, "tag2", item)) {
- TupleEvalNode *tmp = tuple_evalnode_new();
+ TupleEvalNode *tmp = g_slice_new0 (TupleEvalNode);
(*c)++;
tmp->opcode = opcode;
@@ -374,7 +346,7 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co
literal = FALSE;
if (tc_get_item(ctx, &c, tmps1, MAX_STR, ':', &literal, "tag", item)) {
c++;
- tmp = tuple_evalnode_new();
+ tmp = g_slice_new0 (TupleEvalNode);
tmp->opcode = OP_EXISTS;
if ((tmp->var[0] = tc_get_variable(ctx, tmps1, TUPLE_VAR_FIELD)) < 0) {
tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, expr);
@@ -395,7 +367,7 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co
if (*c == '"') {
/* String */
c++;
- } else if (isdigit(*c)) {
+ } else if (g_ascii_isdigit(*c)) {
/* Integer */
}
@@ -444,7 +416,7 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co
literal = FALSE;
if (tc_get_item(ctx, &c, tmps1, MAX_STR, ':', &literal, "tag", item)) {
c++;
- tmp = tuple_evalnode_new();
+ tmp = g_slice_new0 (TupleEvalNode);
tmp->opcode = OP_IS_EMPTY;
if ((tmp->var[0] = tc_get_variable(ctx, tmps1, TUPLE_VAR_FIELD)) < 0) {
tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, expr);
@@ -467,7 +439,7 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co
/* FIXME!! FIX ME! Check for external expressions */
/* I HAS A FIELD - A field. You has it. */
- tmp = tuple_evalnode_new();
+ tmp = g_slice_new0 (TupleEvalNode);
tmp->opcode = OP_FIELD;
if ((tmp->var[0] = tc_get_variable(ctx, tmps1, TUPLE_VAR_FIELD)) < 0) {
tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, expr);
@@ -491,7 +463,7 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co
gssize i = 0;
c++;
- while (*c != '\0' && (isalnum(*c) || *c == '-') && *c != '}' && *c != ':' && i < (MAX_STR - 1))
+ while (*c != '\0' && (g_ascii_isalnum(*c) || *c == '-') && *c != '}' && *c != ':' && i < (MAX_STR - 1))
tmps1[i++] = *(c++);
tmps1[i] = '\0';
@@ -516,9 +488,9 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co
}
tmps1[i] = '\0';
- tmp = tuple_evalnode_new();
+ tmp = g_slice_new0 (TupleEvalNode);
tmp->opcode = OP_RAW;
- tmp->text = g_strdup(tmps1);
+ tmp->text = str_get (tmps1);
tuple_evalnode_insert(&res, tmp);
}
}
@@ -565,16 +537,16 @@ static bool_t tf_get_fieldval (TupleEvalVar * var, const Tuple * tuple)
if (var->fieldread)
return var->fieldvalid;
- if (tuple_get_value_type (tuple, var->fieldidx, NULL) != var->ctype) {
+ if (tuple_get_value_type (tuple, var->fieldidx) != var->ctype) {
var->fieldread = TRUE;
var->fieldvalid = FALSE;
return FALSE;
}
if (var->ctype == TUPLE_INT)
- var->defvali = tuple_get_int (tuple, var->fieldidx, NULL);
+ var->defvali = tuple_get_int (tuple, var->fieldidx);
else if (var->ctype == TUPLE_STRING)
- var->fieldstr = tuple_get_str (tuple, var->fieldidx, NULL);
+ var->fieldstr = tuple_get_str (tuple, var->fieldidx);
var->fieldread = TRUE;
var->fieldvalid = TRUE;
@@ -642,7 +614,7 @@ static bool_t tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode *
break;
case OP_FIELD:
- var0 = ctx->variables[curr->var[0]];
+ var0 = GET_VAR (ctx, curr->var[0]);
switch (var0->type) {
case TUPLE_VAR_FIELD:
@@ -653,7 +625,7 @@ static bool_t tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode *
break;
case TUPLE_INT:
- g_snprintf (tmps, sizeof (tmps), "%d", var0->defvali);
+ str_itoa (var0->defvali, tmps, sizeof (tmps));
str = tmps;
break;
@@ -669,8 +641,8 @@ static bool_t tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode *
case OP_NOT_EQUALS:
case OP_LT: case OP_LTEQ:
case OP_GT: case OP_GTEQ:
- var0 = ctx->variables[curr->var[0]];
- var1 = ctx->variables[curr->var[1]];
+ var0 = GET_VAR (ctx, curr->var[0]);
+ var1 = GET_VAR (ctx, curr->var[1]);
type0 = tf_get_var(&tmps0, &tmpi0, var0, tuple);
type1 = tf_get_var(&tmps1, &tmpi1, var1, tuple);
@@ -705,14 +677,15 @@ static bool_t tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode *
break;
case OP_EXISTS:
- if (tf_get_fieldval (ctx->variables[curr->var[0]], tuple)) {
+ if (tf_get_fieldval (GET_VAR (ctx, curr->var[0]), tuple))
+ {
if (! tuple_formatter_eval_do (ctx, curr->children, tuple, out))
return FALSE;
}
break;
case OP_IS_EMPTY:
- var0 = ctx->variables[curr->var[0]];
+ var0 = GET_VAR (ctx, curr->var[0]);
if (tf_get_fieldval (var0, tuple)) {
switch (var0->ctype) {
@@ -725,9 +698,8 @@ static bool_t tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode *
tmps2 = var0->fieldstr;
while (result && tmps2 && *tmps2 != '\0') {
- gunichar uc = g_utf8_get_char(tmps2);
- if (g_unichar_isspace(uc))
- tmps2 = g_utf8_next_char(tmps2);
+ if (g_ascii_isspace (* tmps2))
+ tmps2 ++;
else
result = FALSE;
}
diff --git a/src/libaudcore/tuple_compiler.h b/src/libaudcore/tuple_compiler.h
index 0e2ba54..1a951c1 100644
--- a/src/libaudcore/tuple_compiler.h
+++ b/src/libaudcore/tuple_compiler.h
@@ -23,12 +23,9 @@
#include <glib.h>
#include <libaudcore/tuple.h>
-struct _TupleEvalNode;
+typedef GArray TupleEvalContext;
typedef struct _TupleEvalNode TupleEvalNode;
-struct _TupleEvalContext;
-typedef struct _TupleEvalContext TupleEvalContext;
-
TupleEvalContext * tuple_evalctx_new(void);
void tuple_evalctx_reset(TupleEvalContext *ctx);
void tuple_evalctx_free(TupleEvalContext *ctx);
diff --git a/src/libaudcore/tuple_formatter.c b/src/libaudcore/tuple_formatter.c
index 04ea555..44c6e44 100644
--- a/src/libaudcore/tuple_formatter.c
+++ b/src/libaudcore/tuple_formatter.c
@@ -1,6 +1,6 @@
/*
* tuple_formatter.c
- * Copyright (c) 2007 William Pitcock
+ * Copyright (c) 2007-2013 William Pitcock and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -18,11 +18,9 @@
*/
#include <glib.h>
-#include <pthread.h>
-#include <string.h>
+#include "tuple.h"
#include "tuple_compiler.h"
-#include "tuple_formatter.h"
/*
* the tuple formatter:
@@ -47,48 +45,52 @@
* - %{function:args,arg2,...}: runs function and inserts the result.
*
* everything else is treated as raw text.
- * additionally, plugins can add additional instructions and functions!
*/
-/*
- * Compile a tuplez string and cache the result.
- * This caches the result for the last string, so that
- * successive calls are sped up.
- */
+struct _TupleFormatter
+{
+ TupleEvalContext * context;
+ TupleEvalNode * node;
+ GString * buf;
+};
-char * tuple_formatter_process_string (const Tuple * tuple, const char * string)
+EXPORT TupleFormatter * tuple_formatter_new (const char * format)
{
- static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- pthread_mutex_lock (& mutex);
+ TupleFormatter * formatter = g_slice_new (TupleFormatter);
- static char *last_string = NULL;
- static TupleEvalContext *last_ctx = NULL;
- static TupleEvalNode *last_ev = NULL;
+ formatter->context = tuple_evalctx_new ();
+ formatter->node = tuple_formatter_compile (formatter->context, format);
+ formatter->buf = g_string_sized_new (255);
- if (! last_string || strcmp (string, last_string))
- {
- g_free(last_string);
+ return formatter;
+}
+
+EXPORT void tuple_formatter_free (TupleFormatter * formatter)
+{
+ tuple_evalctx_free (formatter->context);
+ tuple_evalnode_free (formatter->node);
+ g_string_free (formatter->buf, TRUE);
- if (last_ctx != NULL)
- {
- tuple_evalctx_free(last_ctx);
- tuple_evalnode_free(last_ev);
- }
+ g_slice_free (TupleFormatter, formatter);
+}
- last_ctx = tuple_evalctx_new();
- last_string = g_strdup(string);
- last_ev = tuple_formatter_compile(last_ctx, last_string);
- }
+EXPORT char * tuple_format_title (TupleFormatter * formatter, const Tuple * tuple)
+{
+ tuple_formatter_eval (formatter->context, formatter->node, tuple, formatter->buf);
+ tuple_evalctx_reset (formatter->context);
- static GString * buf;
- if (! buf)
- buf = g_string_sized_new (255);
+ if (formatter->buf->len)
+ return str_get (formatter->buf->str);
- tuple_formatter_eval (last_ctx, last_ev, tuple, buf);
- tuple_evalctx_reset (last_ctx);
+ /* formatting failed, try fallbacks */
+ static const int fallbacks[] = {FIELD_TITLE, FIELD_FILE_NAME};
- char * result = str_get (buf->str);
+ for (int i = 0; i < ARRAY_LEN (fallbacks); i ++)
+ {
+ char * title = tuple_get_str (tuple, fallbacks[i]);
+ if (title)
+ return title;
+ }
- pthread_mutex_unlock (& mutex);
- return result;
+ return str_get ("");
}
diff --git a/src/libaudcore/vfs.c b/src/libaudcore/vfs.c
index b1c3b03..000205f 100644
--- a/src/libaudcore/vfs.c
+++ b/src/libaudcore/vfs.c
@@ -1,6 +1,6 @@
/*
* vfs.c
- * Copyright 2006-2011 William Pitcock, Daniel Barkalow, Ralf Ertzinger,
+ * Copyright 2006-2013 William Pitcock, Daniel Barkalow, Ralf Ertzinger,
* Yoshiki Yazawa, Matti HÀmÀlÀinen, and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
@@ -18,16 +18,20 @@
* the use of this software.
*/
-#include <glib.h>
-#include <inttypes.h>
-
#include "vfs.h"
-#include "audstrings.h"
+
+#include <inttypes.h>
#include <stdio.h>
-#include <unistd.h>
+#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <string.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "audstrings.h"
+#include "vfs_local.h"
#define VFS_SIG ('V' | ('F' << 8) | ('S' << 16))
@@ -123,24 +127,31 @@ vfs_fopen(const char * path,
const char * mode)
{
g_return_val_if_fail (path && mode, NULL);
- g_return_val_if_fail (lookup_func, NULL);
- const char * s = strstr (path, "://");
- g_return_val_if_fail (s, NULL);
- char scheme[s - path + 1];
- strncpy (scheme, path, s - path);
- scheme[s - path] = 0;
+ VFSConstructor * vtable = NULL;
- VFSConstructor * vtable = lookup_func (scheme);
- if (! vtable)
- return NULL;
+ if (! strncmp (path, "file://", 7))
+ vtable = & vfs_local_vtable;
+ else
+ {
+ const char * s = strstr (path, "://");
+
+ if (! s)
+ {
+ fprintf (stderr, "Invalid URI: %s\n", path);
+ return NULL;
+ }
+
+ SNCOPY (scheme, path, s - path);
+
+ if (! (vtable = lookup_func (scheme)))
+ return NULL;
+ }
const gchar * sub;
uri_parse (path, NULL, NULL, & sub, NULL);
- gchar buf[sub - path + 1];
- memcpy (buf, path, sub - path);
- buf[sub - path] = 0;
+ SNCOPY (buf, path, sub - path);
void * handle = vtable->vfs_fopen_impl (buf, mode);
if (! handle)
@@ -174,8 +185,6 @@ vfs_fclose(VFSFile * file)
ret = -1;
str_unref (file->uri);
-
- memset (file, 0, sizeof (VFSFile));
g_slice_free (VFSFile, file);
return ret;
@@ -234,12 +243,12 @@ EXPORT int64_t vfs_fwrite (const void * ptr, int64_t size, int64_t nmemb, VFSFil
EXPORT int
vfs_getc(VFSFile *file)
{
- g_return_val_if_fail (file && file->sig == VFS_SIG, EOF);
+ unsigned char c;
- if (verbose)
- logger ("VFS: <%p> getc\n", file);
+ if (vfs_fread (& c, 1, 1, file) != 1)
+ return EOF;
- return file->base->vfs_getc_impl(file);
+ return c;
}
/**
@@ -252,12 +261,10 @@ vfs_getc(VFSFile *file)
EXPORT int
vfs_ungetc(int c, VFSFile *file)
{
- g_return_val_if_fail (file && file->sig == VFS_SIG, EOF);
-
- if (verbose)
- logger ("VFS: <%p> ungetc\n", file);
+ if (vfs_fseek (file, -1, SEEK_CUR) < 0)
+ return EOF;
- return file->base->vfs_ungetc_impl(c, file);
+ return c;
}
/**
@@ -285,23 +292,13 @@ vfs_fseek(VFSFile * file,
SEEK_CUR ? "current" : whence == SEEK_SET ? "beginning" : whence ==
SEEK_END ? "end" : "invalid");
- return file->base->vfs_fseek_impl(file, offset, whence);
-}
-
-/**
- * Rewinds a VFS stream.
- *
- * @param file #VFSFile object that represents the VFS stream.
- */
-EXPORT void
-vfs_rewind(VFSFile * file)
-{
- g_return_if_fail (file && file->sig == VFS_SIG);
+ if (! file->base->vfs_fseek_impl (file, offset, whence))
+ return 0;
if (verbose)
- logger ("VFS: <%p> rewind\n", file);
+ logger ("VFS: <%p> seek failed!\n", file);
- file->base->vfs_rewind_impl(file);
+ return -1;
}
/**
@@ -415,8 +412,8 @@ vfs_file_test(const char * path, int test)
#ifdef S_ISLNK
if (test & VFS_IS_SYMLINK)
{
- struct stat st;
- if (lstat (path2, & st) < 0)
+ GStatBuf st;
+ if (g_lstat (path2, & st) < 0)
goto DONE;
if (S_ISLNK (st.st_mode))
@@ -426,8 +423,8 @@ vfs_file_test(const char * path, int test)
if (test & (VFS_IS_REGULAR | VFS_IS_DIR | VFS_IS_EXECUTABLE | VFS_EXISTS))
{
- struct stat st;
- if (stat (path2, & st) < 0)
+ GStatBuf st;
+ if (g_stat (path2, & st) < 0)
goto DONE;
if (S_ISREG (st.st_mode))
@@ -441,8 +438,7 @@ vfs_file_test(const char * path, int test)
}
DONE:
- g_free (path2);
-
+ str_unref (path2);
return ! test;
}
@@ -455,14 +451,13 @@ DONE:
EXPORT bool_t
vfs_is_writeable(const char * path)
{
- struct stat info;
+ GStatBuf info;
char * realfn = uri_to_filename (path);
- if (stat(realfn, &info) == -1)
+ if (! realfn || g_stat (realfn, & info) < 0)
return FALSE;
- g_free(realfn);
-
+ str_unref (realfn);
return (info.st_mode & S_IWUSR);
}
diff --git a/src/libaudcore/vfs.h b/src/libaudcore/vfs.h
index 006c3b7..8dcbc32 100644
--- a/src/libaudcore/vfs.h
+++ b/src/libaudcore/vfs.h
@@ -1,6 +1,6 @@
/*
* vfs.h
- * Copyright 2006-2011 William Pitcock, Daniel Barkalow, Ralf Ertzinger,
+ * Copyright 2006-2013 William Pitcock, Daniel Barkalow, Ralf Ertzinger,
* Yoshiki Yazawa, Matti HÀmÀlÀinen, and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
@@ -62,15 +62,14 @@ struct _VFSConstructor {
int64_t (* vfs_fwrite_impl) (const void * ptr, int64_t size, int64_t nmemb,
VFSFile * file);
- /** A function pointer which points to a getc implementation. */
- int (* vfs_getc_impl) (VFSFile * stream);
- /** A function pointer which points to an ungetc implementation. */
- int (* vfs_ungetc_impl) (int c, VFSFile * stream);
+ void (* obs_getc) (void); // obsolete
+ void (* obs_ungetc) (void); // obsolete
/** A function pointer which points to a fseek implementation. */
int (* vfs_fseek_impl) (VFSFile * file, int64_t offset, int whence);
- /** function pointer which points to a rewind implementation. */
- void (* vfs_rewind_impl) (VFSFile * file);
+
+ void (* obs_rewind) (void); // obsolete
+
/** A function pointer which points to a ftell implementation. */
int64_t (* vfs_ftell_impl) (VFSFile * file);
/** A function pointer which points to a feof implementation. */
@@ -111,34 +110,21 @@ int vfs_fprintf (VFSFile * stream, char const * format, ...) __attribute__
((__format__ (__printf__, 2, 3)));
int vfs_fseek (VFSFile * file, int64_t offset, int whence) WARN_RETURN;
-void vfs_rewind (VFSFile * file);
int64_t vfs_ftell (VFSFile * file) WARN_RETURN;
int64_t vfs_fsize (VFSFile * file) WARN_RETURN;
int vfs_ftruncate (VFSFile * file, int64_t length) WARN_RETURN;
-bool_t vfs_fget_le16 (uint16_t * value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fget_le32 (uint32_t * value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fget_le64 (uint64_t * value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fget_be16 (uint16_t * value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fget_be32 (uint32_t * value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fget_be64 (uint64_t * value, VFSFile * stream) WARN_RETURN;
-
-bool_t vfs_fput_le16 (uint16_t value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fput_le32 (uint32_t value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fput_le64 (uint64_t value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fput_be16 (uint16_t value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fput_be32 (uint32_t value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fput_be64 (uint64_t value, VFSFile * stream) WARN_RETURN;
-
bool_t vfs_is_streaming (VFSFile * file) WARN_RETURN;
+
+/* free returned string with str_unref() */
char * vfs_get_metadata (VFSFile * file, const char * field) WARN_RETURN;
bool_t vfs_file_test (const char * path, int test) WARN_RETURN;
bool_t vfs_is_writeable (const char * path) WARN_RETURN;
bool_t vfs_is_remote (const char * path) WARN_RETURN;
-void vfs_file_get_contents (const char * filename, void * * buf, int64_t *
- size);
+void vfs_file_read_all (VFSFile * file, void * * buf, int64_t * size);
+void vfs_file_get_contents (const char * filename, void * * buf, int64_t * size);
void vfs_set_lookup_func (VFSConstructor * (* func) (const char * scheme));
void vfs_set_verbose (bool_t verbose);
diff --git a/src/libaudcore/vfs_async.c b/src/libaudcore/vfs_async.c
index 88a47ea..d8864c6 100644
--- a/src/libaudcore/vfs_async.c
+++ b/src/libaudcore/vfs_async.c
@@ -1,6 +1,6 @@
/*
* vfs_async.c
- * Copyright 2010 William Pitcock
+ * Copyright 2010-2012 William Pitcock and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/libaudcore/vfs_common.c b/src/libaudcore/vfs_common.c
index 3e64b5c..18b05c7 100644
--- a/src/libaudcore/vfs_common.c
+++ b/src/libaudcore/vfs_common.c
@@ -1,7 +1,7 @@
/*
* vfs_common.c
- * Copyright 2006-2010 Tony Vroon, William Pitcock, Maciej Grela,
- * Matti HÀmÀlÀinen, and John Lindgren
+ * Copyright 2006-2013 Tony Vroon, William Pitcock, Matti HÀmÀlÀinen, and
+ * John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -18,12 +18,12 @@
* the use of this software.
*/
-#include <glib.h>
-#include <glib/gprintf.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
+#include <glib.h>
+
+#include "audstrings.h"
#include "vfs.h"
/**
@@ -94,7 +94,7 @@ EXPORT char *vfs_fgets(char *s, int n, VFSFile *stream)
*/
EXPORT int vfs_fputs(const char *s, VFSFile *stream)
{
- gsize n = strlen(s);
+ int64_t n = strlen(s);
return ((vfs_fwrite(s, 1, n, stream) == n) ? n : EOF);
}
@@ -109,12 +109,8 @@ EXPORT int vfs_fputs(const char *s, VFSFile *stream)
*/
EXPORT int vfs_vfprintf(VFSFile *stream, char const *format, va_list args)
{
- char *string;
- int rv = g_vasprintf(&string, format, args);
- if (rv < 0) return rv;
- rv = vfs_fputs(string, stream);
- g_free(string);
- return rv;
+ VSPRINTF (buf, format, args);
+ return vfs_fputs (buf, stream);
}
/**
@@ -137,242 +133,66 @@ EXPORT int vfs_fprintf(VFSFile *stream, char const *format, ...)
return rv;
}
-/**
- * Gets contents of the file into a buffer. Buffer of filesize bytes
- * is allocated by this function as necessary.
- *
- * @param filename URI of the file to read in.
- * @param buf Pointer to a pointer variable of buffer.
- * @param size Pointer to gsize variable that will hold the amount of
- * read data e.g. filesize.
- */
-EXPORT void vfs_file_get_contents (const char * filename, void * * buf, int64_t * size)
+EXPORT void vfs_file_read_all (VFSFile * file, void * * bufp, int64_t * sizep)
{
- * buf = NULL;
- * size = 0;
-
- VFSFile *fd;
- gsize filled_size = 0, buf_size = 4096;
- unsigned char * ptr;
-
- if ((fd = vfs_fopen(filename, "rb")) == NULL)
- return;
+ char * buf = NULL;
+ int64_t size = vfs_fsize (file);
- if ((* size = vfs_fsize (fd)) >= 0)
+ if (size >= 0)
{
- * buf = g_malloc (* size);
- * size = vfs_fread (* buf, 1, * size, fd);
- goto close_handle;
+ size = MIN (size, SIZE_MAX - 1);
+ buf = g_malloc (size + 1);
+ size = vfs_fread (buf, 1, size, file);
}
+ else
+ {
+ size = 0;
- if ((*buf = g_malloc(buf_size)) == NULL)
- goto close_handle;
-
- ptr = *buf;
- while (TRUE) {
- gsize read_size = vfs_fread(ptr, 1, buf_size - filled_size, fd);
- if (read_size == 0) break;
-
- filled_size += read_size;
- ptr += read_size;
-
- if (filled_size == buf_size) {
- buf_size += 4096;
+ size_t bufsize = 4096;
+ buf = g_malloc (bufsize);
- *buf = g_realloc(*buf, buf_size);
+ size_t readsize;
+ while ((readsize = vfs_fread (buf + size, 1, bufsize - 1 - size, file)))
+ {
+ size += readsize;
- if (*buf == NULL)
- goto close_handle;
+ if (size == bufsize - 1)
+ {
+ if (bufsize > SIZE_MAX - 4096)
+ break;
- ptr = (unsigned char *) (* buf) + filled_size;
+ bufsize += 4096;
+ buf = g_realloc (buf, bufsize);
+ }
}
}
- *size = filled_size;
+ buf[size] = 0; // nul-terminate
-close_handle:
- vfs_fclose(fd);
+ * bufp = buf;
+ if (sizep)
+ * sizep = size;
}
-
/**
- * Reads an unsigned 16-bit Little Endian value from the stream
- * into native endian format.
- *
- * @param value Pointer to the variable to read the value into.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fget_le16(uint16_t *value, VFSFile *stream)
-{
- uint16_t tmp;
- if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1)
- return FALSE;
- *value = GUINT16_FROM_LE(tmp);
- return TRUE;
-}
-
-/**
- * Reads an unsigned 32-bit Little Endian value from the stream into native endian format.
- *
- * @param value Pointer to the variable to read the value into.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fget_le32(uint32_t *value, VFSFile *stream)
-{
- uint32_t tmp;
- if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1)
- return FALSE;
- *value = GUINT32_FROM_LE(tmp);
- return TRUE;
-}
-
-/**
- * Reads an unsigned 64-bit Little Endian value from the stream into native endian format.
- *
- * @param value Pointer to the variable to read the value into.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fget_le64(uint64_t *value, VFSFile *stream)
-{
- uint64_t tmp;
- if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1)
- return FALSE;
- *value = GUINT64_FROM_LE(tmp);
- return TRUE;
-}
-
-
-/**
- * Reads an unsigned 16-bit Big Endian value from the stream into native endian format.
- *
- * @param value Pointer to the variable to read the value into.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fget_be16(uint16_t *value, VFSFile *stream)
-{
- uint16_t tmp;
- if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1)
- return FALSE;
- *value = GUINT16_FROM_BE(tmp);
- return TRUE;
-}
-
-/**
- * Reads an unsigned 32-bit Big Endian value from the stream into native endian format.
- *
- * @param value Pointer to the variable to read the value into.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fget_be32(uint32_t *value, VFSFile *stream)
-{
- uint32_t tmp;
- if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1)
- return FALSE;
- *value = GUINT32_FROM_BE(tmp);
- return TRUE;
-}
-
-/**
- * Reads an unsigned 64-bit Big Endian value from the stream into native endian format.
- *
- * @param value Pointer to the variable to read the value into.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fget_be64(uint64_t *value, VFSFile *stream)
-{
- uint64_t tmp;
- if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1)
- return FALSE;
- *value = GUINT64_FROM_BE(tmp);
- return TRUE;
-}
-
-/**
- * Writes an unsigned 16-bit native endian value into the stream as a
- * Little Endian value.
- *
- * @param value Value to write into the stream.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fput_le16(uint16_t value, VFSFile *stream)
-{
- uint16_t tmp = GUINT16_TO_LE(value);
- return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1;
-}
-
-/**
- * Writes an unsigned 32-bit native endian value into the stream as a
- * Big Endian value.
- *
- * @param value Value to write into the stream.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fput_le32(uint32_t value, VFSFile *stream)
-{
- uint32_t tmp = GUINT32_TO_LE(value);
- return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1;
-}
-
-/**
- * Writes an unsigned 64-bit native endian value into the stream as a
- * Big Endian value.
- *
- * @param value Value to write into the stream.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fput_le64(uint64_t value, VFSFile *stream)
-{
- uint64_t tmp = GUINT64_TO_LE(value);
- return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1;
-}
-
-/**
- * Writes an unsigned 16-bit native endian value into the stream as a
- * Big Endian value.
+ * Gets contents of the file into a buffer. Buffer of filesize bytes
+ * is allocated by this function as necessary.
*
- * @param value Value to write into the stream.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
+ * @param filename URI of the file to read in.
+ * @param buf Pointer to a pointer variable of buffer.
+ * @param size Pointer to gsize variable that will hold the amount of
+ * read data e.g. filesize.
*/
-EXPORT bool_t vfs_fput_be16(uint16_t value, VFSFile *stream)
+EXPORT void vfs_file_get_contents (const char * filename, void * * buf, int64_t * size)
{
- uint16_t tmp = GUINT16_TO_BE(value);
- return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1;
-}
+ * buf = NULL;
+ if (size)
+ * size = 0;
-/**
- * Writes an unsigned 32-bit native endian value into the stream as a
- * Big Endian value.
- *
- * @param value Value to write into the stream.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fput_be32(uint32_t value, VFSFile *stream)
-{
- uint32_t tmp = GUINT32_TO_BE(value);
- return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1;
-}
+ VFSFile * file = vfs_fopen (filename, "r");
+ if (! file)
+ return;
-/**
- * Writes an unsigned 64-bit native endian value into the stream as a
- * Big Endian value.
- *
- * @param value Value to write into the stream.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fput_be64(uint64_t value, VFSFile *stream)
-{
- uint64_t tmp = GUINT64_TO_BE(value);
- return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1;
+ vfs_file_read_all (file, buf, size);
+ vfs_fclose (file);
}
diff --git a/src/libaudcore/vfs_local.c b/src/libaudcore/vfs_local.c
new file mode 100644
index 0000000..d91317a
--- /dev/null
+++ b/src/libaudcore/vfs_local.c
@@ -0,0 +1,231 @@
+/*
+ * vfs_local.c
+ * Copyright 2009-2014 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 "vfs_local.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "audstrings.h"
+
+#ifdef _WIN32
+#define fseeko fseeko64
+#define ftello ftello64
+#endif
+
+typedef enum {
+ OP_NONE,
+ OP_READ,
+ OP_WRITE
+} LocalOp;
+
+typedef struct {
+ char * path;
+ FILE * stream;
+ int64_t cached_size;
+ LocalOp last_op;
+} LocalFile;
+
+static void * local_fopen (const char * uri, const char * mode)
+{
+ char * path = uri_to_filename (uri);
+ g_return_val_if_fail (path, NULL);
+
+ const char * suffix = "";
+
+#ifdef _WIN32
+ if (! strchr (mode, 'b')) /* binary mode (Windows) */
+ suffix = "b";
+#else
+ if (! strchr (mode, 'e')) /* close on exec (POSIX) */
+ suffix = "e";
+#endif
+
+ SCONCAT2 (mode2, mode, suffix);
+
+ FILE * stream = g_fopen (path, mode2);
+
+ if (! stream)
+ {
+ perror (path);
+ str_unref (path);
+ return NULL;
+ }
+
+ LocalFile * local = g_slice_new (LocalFile);
+
+ local->path = path;
+ local->stream = stream;
+ local->cached_size = -1;
+ local->last_op = OP_NONE;
+
+ return local;
+}
+
+static int local_fclose (VFSFile * file)
+{
+ LocalFile * local = vfs_get_handle (file);
+
+ int result = fclose (local->stream);
+ if (result < 0)
+ perror (local->path);
+
+ str_unref (local->path);
+ g_slice_free (LocalFile, local);
+
+ return result;
+}
+
+static int64_t local_fread (void * ptr, int64_t size, int64_t nitems, VFSFile * file)
+{
+ LocalFile * local = vfs_get_handle (file);
+
+ if (local->last_op == OP_WRITE)
+ {
+ if (fseeko (local->stream, 0, SEEK_CUR) < 0) /* flush buffers */
+ {
+ perror (local->path);
+ return 0;
+ }
+ }
+
+ local->last_op = OP_READ;
+
+ clearerr (local->stream);
+
+ int64_t result = fread (ptr, size, nitems, local->stream);
+ if (result < nitems && ferror (local->stream))
+ perror (local->path);
+
+ return result;
+}
+
+static int64_t local_fwrite (const void * ptr, int64_t size, int64_t nitems, VFSFile * file)
+{
+ LocalFile * local = vfs_get_handle (file);
+
+ if (local->last_op == OP_READ)
+ {
+ if (fseeko (local->stream, 0, SEEK_CUR) < 0) /* flush buffers */
+ {
+ perror (local->path);
+ return 0;
+ }
+ }
+
+ local->last_op = OP_WRITE;
+ local->cached_size = -1;
+
+ clearerr (local->stream);
+
+ int64_t result = fwrite (ptr, size, nitems, local->stream);
+ if (result < nitems && ferror (local->stream))
+ perror (local->path);
+
+ return result;
+}
+
+static int local_fseek (VFSFile * file, int64_t offset, int whence)
+{
+ LocalFile * local = vfs_get_handle (file);
+
+ int result = fseeko (local->stream, offset, whence);
+ if (result < 0)
+ perror (local->path);
+
+ if (result == 0)
+ local->last_op = OP_NONE;
+
+ return result;
+}
+
+static int64_t local_ftell (VFSFile * file)
+{
+ LocalFile * local = vfs_get_handle (file);
+ return ftello (local->stream);
+}
+
+static bool_t local_feof (VFSFile * file)
+{
+ LocalFile * local = vfs_get_handle (file);
+ return feof (local->stream);
+}
+
+static int local_ftruncate (VFSFile * file, int64_t length)
+{
+ LocalFile * local = vfs_get_handle (file);
+
+ if (local->last_op != OP_NONE)
+ {
+ if (fseeko (local->stream, 0, SEEK_CUR) < 0) /* flush buffers */
+ {
+ perror (local->path);
+ return 0;
+ }
+ }
+
+ int result = ftruncate (fileno (local->stream), length);
+ if (result < 0)
+ perror (local->path);
+
+ if (result == 0)
+ {
+ local->last_op = OP_NONE;
+ local->cached_size = length;
+ }
+
+ return result;
+}
+
+static int64_t local_fsize (VFSFile * file)
+{
+ LocalFile * local = vfs_get_handle (file);
+
+ if (local->cached_size >= 0)
+ return local->cached_size;
+
+ int64_t saved_pos = ftello (local->stream);
+
+ if (local_fseek (file, 0, SEEK_END) < 0)
+ return -1;
+
+ int64_t length = ftello (local->stream);
+
+ if (local_fseek (file, saved_pos, SEEK_SET) < 0)
+ return -1;
+
+ return length;
+}
+
+VFSConstructor vfs_local_vtable = {
+ .vfs_fopen_impl = local_fopen,
+ .vfs_fclose_impl = local_fclose,
+ .vfs_fread_impl = local_fread,
+ .vfs_fwrite_impl = local_fwrite,
+ .vfs_fseek_impl = local_fseek,
+ .vfs_ftell_impl = local_ftell,
+ .vfs_feof_impl = local_feof,
+ .vfs_ftruncate_impl = local_ftruncate,
+ .vfs_fsize_impl = local_fsize
+};
diff --git a/src/libaudcore/tuple_formatter.h b/src/libaudcore/vfs_local.h
index bb3052b..6fa3686 100644
--- a/src/libaudcore/tuple_formatter.h
+++ b/src/libaudcore/vfs_local.h
@@ -1,6 +1,6 @@
/*
- * tuple_formatter.h
- * Copyright (c) 2007 William Pitcock
+ * vfs_local.h
+ * Copyright 2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,12 +17,11 @@
* the use of this software.
*/
-#ifndef LIBAUDCORE_TUPLE_FORMATTER_H
-#define LIBAUDCORE_TUPLE_FORMATTER_H
+#ifndef LIBAUDCORE_VFS_LOCAL_H
+#define LIBAUDCORE_VFS_LOCAL_H
-#include <libaudcore/tuple.h>
+#include "vfs.h"
-/* returned string be released with str_unref() */
-char * tuple_formatter_process_string (const Tuple * tuple, const char * string);
+extern VFSConstructor vfs_local_vtable;
-#endif /* LIBAUDCORE_TUPLE_FORMATTER_H */
+#endif /* LIBAUDCORE_VFS_LOCAL_H */
diff --git a/src/libaudgui/Makefile b/src/libaudgui/Makefile
index 8246dd3..9da027f 100644
--- a/src/libaudgui/Makefile
+++ b/src/libaudgui/Makefile
@@ -1,23 +1,23 @@
SHARED_LIB = ${LIB_PREFIX}audgui${LIB_SUFFIX}
-LIB_MAJOR = 1
+LIB_MAJOR = 2
LIB_MINOR = 0
SRCS = about.c \
confirm.c \
- effects-menu.c \
equalizer.c \
- iface-menu.c \
infopopup.c \
infowin.c \
init.c \
jump-to-time.c \
list.c \
+ menu.c \
+ pixbufs.c \
playlists.c \
queue-manager.c \
+ scaled-image.c \
ui_fileopener.c \
ui_jumptotrack.c \
ui_jumptotrack_cache.c \
- icons-stock.c \
ui_playlist_manager.c \
urilist.c \
url-opener.c \
@@ -25,7 +25,8 @@ SRCS = about.c \
INCLUDES = libaudgui.h \
libaudgui-gtk.h \
- list.h
+ list.h \
+ menu.h
include ../../buildsys.mk
include ../../extra.mk
@@ -35,13 +36,11 @@ includesubdir = libaudgui
CPPFLAGS := -I.. -I../.. \
${CPPFLAGS} \
${GLIB_CFLASG} \
- ${GTK_CFLAGS} \
- ${REGEX_CFLAGS}
+ ${GTK_CFLAGS}
CFLAGS += ${LIB_CFLAGS}
LIBS := -L../libaudcore -laudcore \
${LIBS} -lm \
${GLIB_LIBS} \
- ${GTK_LIBS} \
- ${REGEX_LIBS}
+ ${GTK_LIBS}
diff --git a/src/libaudgui/about.c b/src/libaudgui/about.c
index 613bf4e..56cdbef 100644
--- a/src/libaudgui/about.c
+++ b/src/libaudgui/about.c
@@ -1,6 +1,6 @@
/*
* about.c
- * Copyright 2011-2012 John Lindgren
+ * Copyright 2011-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -21,7 +21,9 @@
#include <audacious/i18n.h>
#include <audacious/misc.h>
+#include <libaudcore/audstrings.h>
+#include "init.h"
#include "libaudgui-gtk.h"
static const char about_text[] =
@@ -30,8 +32,6 @@ static const char about_text[] =
static const char website[] = "http://audacious-media-player.org";
-static GtkWidget * about_window;
-
static GtkWidget * create_credits_notebook (const char * credits, const char * license)
{
const char * titles[2] = {_("Credits"), _("License")};
@@ -61,27 +61,21 @@ static GtkWidget * create_credits_notebook (const char * credits, const char * l
return notebook;
}
-EXPORT void audgui_show_about_window (void)
+static GtkWidget * create_about_window (void)
{
- if (about_window)
- {
- gtk_window_present ((GtkWindow *) about_window);
- return;
- }
+ const char * data_dir = aud_get_path (AUD_PATH_DATA_DIR);
- about_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ GtkWidget * about_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title ((GtkWindow *) about_window, _("About Audacious"));
gtk_window_set_resizable ((GtkWindow *) about_window, FALSE);
gtk_container_set_border_width ((GtkContainer *) about_window, 3);
audgui_destroy_on_escape (about_window);
- g_signal_connect (about_window, "destroy", (GCallback) gtk_widget_destroyed,
- & about_window);
GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_container_add ((GtkContainer *) about_window, vbox);
- SPRINTF (logo_path, "%s/images/about-logo.png", aud_get_path (AUD_PATH_DATA_DIR));
+ SCONCAT2 (logo_path, data_dir, "/images/about-logo.png");
GtkWidget * image = gtk_image_new_from_file (logo_path);
gtk_box_pack_start ((GtkBox *) vbox, image, FALSE, FALSE, 0);
@@ -96,11 +90,11 @@ EXPORT void audgui_show_about_window (void)
char * credits, * license;
- SPRINTF (credits_path, "%s/AUTHORS", aud_get_path (AUD_PATH_DATA_DIR));
+ SCONCAT2 (credits_path, data_dir, "/AUTHORS");
if (! g_file_get_contents (credits_path, & credits, NULL, NULL))
credits = g_strdup_printf ("Unable to load %s; check your installation.", credits_path);
- SPRINTF (license_path, "%s/COPYING", aud_get_path (AUD_PATH_DATA_DIR));
+ SCONCAT2 (license_path, data_dir, "/COPYING");
if (! g_file_get_contents (license_path, & license, NULL, NULL))
license = g_strdup_printf ("Unable to load %s; check your installation.", license_path);
@@ -114,5 +108,16 @@ EXPORT void audgui_show_about_window (void)
g_free (credits);
g_free (license);
- gtk_widget_show_all (about_window);
+ return about_window;
+}
+
+EXPORT void audgui_show_about_window (void)
+{
+ if (! audgui_reshow_unique_window (AUDGUI_ABOUT_WINDOW))
+ audgui_show_unique_window (AUDGUI_ABOUT_WINDOW, create_about_window ());
+}
+
+EXPORT void audgui_hide_about_window (void)
+{
+ audgui_hide_unique_window (AUDGUI_ABOUT_WINDOW);
}
diff --git a/src/libaudgui/confirm.c b/src/libaudgui/confirm.c
index 695e7b6..fb92da4 100644
--- a/src/libaudgui/confirm.c
+++ b/src/libaudgui/confirm.c
@@ -1,6 +1,6 @@
/*
* confirm.c
- * Copyright 2010-2011 John Lindgren
+ * Copyright 2010-2013 John Lindgren and Thomas Lange
*
* 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 <audacious/i18n.h>
#include <audacious/misc.h>
#include <audacious/playlist.h>
+#include <libaudcore/audstrings.h>
#include "libaudgui-gtk.h"
@@ -33,85 +34,68 @@ static void no_confirm_cb (GtkToggleButton * toggle)
static void confirm_delete_cb (void * data)
{
int list = aud_playlist_by_unique_id (GPOINTER_TO_INT (data));
- if (list < 0)
- return;
-
- aud_playlist_delete (list);
- if (list > 0)
- aud_playlist_set_active (list - 1);
-}
-
-static void confirm_playlist_delete_response (GtkWidget * dialog, gint response, gpointer data)
-{
- if (response == GTK_RESPONSE_YES)
- confirm_delete_cb (data);
- gtk_widget_destroy (dialog);
+ if (list >= 0)
+ aud_playlist_delete (list);
}
EXPORT void audgui_confirm_playlist_delete (int playlist)
{
- GtkWidget * dialog, * vbox, * button;
- char * message;
-
if (aud_get_bool ("audgui", "no_confirm_playlist_delete"))
{
aud_playlist_delete (playlist);
- if (playlist > 0)
- aud_playlist_set_active (playlist - 1);
return;
}
char * title = aud_playlist_get_title (playlist);
- message = g_strdup_printf (_("Are you sure you want to close %s? If you "
- "do, any changes made since the playlist was exported will be lost."), title);
+ SPRINTF (message, _("Do you want to permanently remove “%s”?"), title);
str_unref (title);
- dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "%s", message);
- g_free (message);
- gtk_window_set_title ((GtkWindow *) dialog, _("Close Playlist"));
- gtk_dialog_set_default_response ((GtkDialog *) dialog, GTK_RESPONSE_YES);
-
- vbox = gtk_message_dialog_get_message_area ((GtkMessageDialog *) dialog);
- button = gtk_check_button_new_with_mnemonic (_("_Don't show this message again"));
+ int id = aud_playlist_get_unique_id (playlist);
+ GtkWidget * button1 = audgui_button_new (_("_Remove"), "edit-delete",
+ confirm_delete_cb, GINT_TO_POINTER (id));
+ GtkWidget * button2 = audgui_button_new (_("_Cancel"), "process-stop", NULL, NULL);
- gtk_container_add ((GtkContainer *) vbox, button);
+ GtkWidget * dialog = audgui_dialog_new (GTK_MESSAGE_QUESTION,
+ _("Remove Playlist"), message, button1, button2);
- g_signal_connect (button, "toggled", (GCallback) no_confirm_cb, NULL);
- g_signal_connect (dialog, "response", (GCallback) confirm_playlist_delete_response,
- GINT_TO_POINTER (aud_playlist_get_unique_id (playlist)));
+ GtkWidget * check = gtk_check_button_new_with_mnemonic (_("_Don’t ask again"));
+ g_signal_connect (check, "toggled", (GCallback) no_confirm_cb, NULL);
+ audgui_dialog_add_widget (dialog, check);
gtk_widget_show_all (dialog);
}
-static void rename_cb (GtkDialog * dialog, int resp, void * list)
+static void rename_cb (void * entry)
{
- if (resp == GTK_RESPONSE_ACCEPT && GPOINTER_TO_INT (list) <
- aud_playlist_count ())
- aud_playlist_set_title (GPOINTER_TO_INT (list), gtk_entry_get_text
- ((GtkEntry *) g_object_get_data ((GObject *) dialog, "entry")));
+ void * data = g_object_get_data ((GObject *) entry, "playlist-id");
+ int list = aud_playlist_by_unique_id (GPOINTER_TO_INT (data));
- gtk_widget_destroy ((GtkWidget *) dialog);
+ if (list >= 0)
+ aud_playlist_set_title (list, gtk_entry_get_text ((GtkEntry *) entry));
}
EXPORT void audgui_show_playlist_rename (int playlist)
{
- GtkWidget * dialog = gtk_dialog_new_with_buttons (_("Rename Playlist"),
- NULL, 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL,
- GTK_RESPONSE_REJECT, NULL);
- gtk_dialog_set_default_response ((GtkDialog *) dialog, GTK_RESPONSE_ACCEPT);
+ char * title = aud_playlist_get_title (playlist);
GtkWidget * entry = gtk_entry_new ();
- char * title = aud_playlist_get_title (playlist);
gtk_entry_set_text ((GtkEntry *) entry, title);
- str_unref (title);
gtk_entry_set_activates_default ((GtkEntry *) entry, TRUE);
- gtk_box_pack_start ((GtkBox *) gtk_dialog_get_content_area ((GtkDialog *)
- dialog), entry, FALSE, FALSE, 0);
- g_object_set_data ((GObject *) dialog, "entry", entry);
- g_signal_connect (dialog, "response", (GCallback) rename_cb, GINT_TO_POINTER
- (playlist));
+ int id = aud_playlist_get_unique_id (playlist);
+ g_object_set_data ((GObject *) entry, "playlist-id", GINT_TO_POINTER (id));
+
+ GtkWidget * button1 = audgui_button_new (_("_Rename"), "insert-text", rename_cb, entry);
+ GtkWidget * button2 = audgui_button_new (_("_Cancel"), "process-stop", NULL, NULL);
+
+ GtkWidget * dialog = audgui_dialog_new (GTK_MESSAGE_QUESTION,
+ _("Rename Playlist"), _("What would you like to call this playlist?"),
+ button1, button2);
+
+ audgui_dialog_add_widget (dialog, entry);
+
gtk_widget_show_all (dialog);
+
+ str_unref (title);
}
diff --git a/src/libaudgui/effects-menu.c b/src/libaudgui/effects-menu.c
deleted file mode 100644
index 3e10588..0000000
--- a/src/libaudgui/effects-menu.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * effects-menu.c
- * Copyright 2010-2011 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 <audacious/i18n.h>
-#include <audacious/plugin.h>
-#include <audacious/plugins.h>
-
-#include "libaudgui-gtk.h"
-
-static bool_t watch_cb (PluginHandle * plugin, GtkCheckMenuItem * item)
-{
- bool_t enabled = aud_plugin_get_enabled (plugin);
- gtk_check_menu_item_set_active (item, enabled);
-
- GtkWidget * settings = g_object_get_data ((GObject *) item, "settings");
- if (settings != NULL)
- gtk_widget_set_sensitive (settings, enabled);
-
- return TRUE;
-}
-
-static void enable_cb (GtkCheckMenuItem * item, PluginHandle * plugin)
-{
- aud_plugin_enable (plugin, gtk_check_menu_item_get_active (item));
-}
-
-static void destroy_cb (GtkCheckMenuItem * item, PluginHandle * plugin)
-{
- aud_plugin_remove_watch (plugin, (PluginForEachFunc) watch_cb, item);
-}
-
-static void settings_cb (GtkMenuItem * settings, PluginHandle * plugin)
-{
- if (! aud_plugin_get_enabled (plugin))
- return;
-
- aud_plugin_do_configure (plugin);
-}
-
-static bool_t add_item_cb (PluginHandle * plugin, GtkWidget * menu)
-{
- GtkWidget * item = gtk_check_menu_item_new_with_label (aud_plugin_get_name
- (plugin));
- gtk_check_menu_item_set_active ((GtkCheckMenuItem *) item,
- aud_plugin_get_enabled (plugin));
- gtk_menu_shell_append ((GtkMenuShell *) menu, item);
- aud_plugin_add_watch (plugin, (PluginForEachFunc) watch_cb, item);
- g_signal_connect (item, "toggled", (GCallback) enable_cb, plugin);
- g_signal_connect (item, "destroy", (GCallback) destroy_cb, plugin);
- gtk_widget_show (item);
-
- if (aud_plugin_has_configure (plugin))
- {
- GtkWidget * settings = gtk_menu_item_new_with_label (_("settings ..."));
- gtk_widget_set_sensitive (settings, aud_plugin_get_enabled (plugin));
- g_object_set_data ((GObject *) item, "settings", settings);
- gtk_menu_shell_append ((GtkMenuShell *) menu, settings);
- g_signal_connect (settings, "activate", (GCallback) settings_cb, plugin);
- gtk_widget_show (settings);
- }
-
- return TRUE;
-}
-
-EXPORT GtkWidget * audgui_create_effects_menu (void)
-{
- GtkWidget * menu = gtk_menu_new ();
- aud_plugin_for_each (PLUGIN_TYPE_EFFECT, (PluginForEachFunc) add_item_cb,
- menu);
- return menu;
-}
-
-EXPORT GtkWidget * audgui_create_vis_menu (void)
-{
- GtkWidget * menu = gtk_menu_new ();
- aud_plugin_for_each (PLUGIN_TYPE_VIS, (PluginForEachFunc) add_item_cb, menu);
- return menu;
-}
diff --git a/src/libaudgui/equalizer.c b/src/libaudgui/equalizer.c
index b5bcd7c..1c3a082 100644
--- a/src/libaudgui/equalizer.c
+++ b/src/libaudgui/equalizer.c
@@ -17,19 +17,15 @@
* the use of this software.
*/
-/*
- * Note: Because some GTK developer had the bright idea to put the minimum at
- * the top of a GtkVScale and the maximum at the bottom, we have to reverse the
- * sign of the values we get.
- */
-
#include <math.h>
#include <audacious/i18n.h>
#include <audacious/misc.h>
#include <audacious/types.h>
+#include <libaudcore/audstrings.h>
#include <libaudcore/hook.h>
+#include "init.h"
#include "libaudgui-gtk.h"
static void on_off_cb (GtkToggleButton * on_off, void * unused)
@@ -45,9 +41,7 @@ static void on_off_update (void * unused, GtkWidget * on_off)
static GtkWidget * create_on_off (void)
{
- GtkWidget * on_off;
-
- on_off = gtk_check_button_new_with_mnemonic (_("_Enable"));
+ GtkWidget * on_off = gtk_check_button_new_with_mnemonic (_("_Enable"));
g_signal_connect ((GObject *) on_off, "toggled", (GCallback) on_off_cb, NULL);
hook_associate ("set equalizer_active", (HookFunction) on_off_update, on_off);
@@ -58,7 +52,7 @@ static GtkWidget * create_on_off (void)
static void slider_moved (GtkRange * slider, void * unused)
{
int band = GPOINTER_TO_INT (g_object_get_data ((GObject *) slider, "band"));
- double value = round (-gtk_range_get_value (slider));
+ double value = round (gtk_range_get_value (slider));
if (band == -1)
aud_set_double (NULL, "equalizer_preamp", value);
@@ -66,56 +60,58 @@ static void slider_moved (GtkRange * slider, void * unused)
aud_eq_set_band (band, value);
}
-static void slider_update (void * unused, GtkRange * slider)
+static GtkWidget * create_slider (const char * name, int band, GtkWidget * hbox)
{
- int band = GPOINTER_TO_INT (g_object_get_data ((GObject *) slider, "band"));
- double value;
+ GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
- if (band == -1)
- value = round (aud_get_double (NULL, "equalizer_preamp"));
- else
- value = round (aud_eq_get_band (band));
-
- g_signal_handlers_block_by_func (slider, (void *) slider_moved, NULL);
- gtk_range_set_value (slider, -value);
- g_signal_handlers_unblock_by_func (slider, (void *) slider_moved, NULL);
-}
-
-static char * format_value (GtkScale * slider, double value, void * unused)
-{
- return g_strdup_printf ("%d", (int) -value);
-}
-
-static GtkWidget * create_slider (const char * name, int band)
-{
- GtkWidget * vbox, * slider, * label;
-
- vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
-
- label = gtk_label_new (name);
+ GtkWidget * label = gtk_label_new (name);
gtk_label_set_angle ((GtkLabel *) label, 90);
gtk_box_pack_start ((GtkBox *) vbox, label, TRUE, FALSE, 0);
- slider = gtk_scale_new_with_range (GTK_ORIENTATION_VERTICAL,
+ GtkWidget * slider = gtk_scale_new_with_range (GTK_ORIENTATION_VERTICAL,
-EQUALIZER_MAX_GAIN, EQUALIZER_MAX_GAIN, 1);
gtk_scale_set_draw_value ((GtkScale *) slider, TRUE);
gtk_scale_set_value_pos ((GtkScale *) slider, GTK_POS_BOTTOM);
+ gtk_range_set_inverted ((GtkRange *) slider, TRUE);
gtk_widget_set_size_request (slider, -1, 120);
g_object_set_data ((GObject *) slider, "band", GINT_TO_POINTER (band));
- g_signal_connect ((GObject *) slider, "format-value", (GCallback) format_value, NULL);
g_signal_connect ((GObject *) slider, "value-changed", (GCallback) slider_moved, NULL);
- slider_update (NULL, (GtkRange *) slider);
+ gtk_box_pack_start ((GtkBox *) vbox, slider, FALSE, FALSE, 0);
+ gtk_box_pack_start ((GtkBox *) hbox, vbox, FALSE, FALSE, 0);
- if (band == -1)
- hook_associate ("set equalizer_preamp", (HookFunction) slider_update, slider);
- else
- hook_associate ("set equalizer_bands", (HookFunction) slider_update, slider);
+ return slider;
+}
- gtk_box_pack_start ((GtkBox *) vbox, slider, FALSE, FALSE, 0);
+static void set_slider (GtkWidget * slider, double value)
+{
+ g_signal_handlers_block_by_func (slider, (void *) slider_moved, NULL);
+ gtk_range_set_value ((GtkRange *) slider, round (value));
+ g_signal_handlers_unblock_by_func (slider, (void *) slider_moved, NULL);
+}
- return vbox;
+static void update_sliders (void * unused, GtkWidget * window)
+{
+ GtkWidget * preamp = g_object_get_data ((GObject *) window, "preamp");
+ set_slider (preamp, aud_get_double (NULL, "equalizer_preamp"));
+
+ double values[AUD_EQUALIZER_NBANDS];
+ aud_eq_get_bands (values);
+
+ for (int i = 0; i < AUD_EQUALIZER_NBANDS; i ++)
+ {
+ SPRINTF (slider_id, "slider%d", i);
+ GtkWidget * slider = g_object_get_data ((GObject *) window, slider_id);
+ set_slider (slider, values[i]);
+ }
+}
+
+static void destroy_cb (void)
+{
+ hook_dissociate ("set equalizer_active", (HookFunction) on_off_update);
+ hook_dissociate ("set equalizer_bands", (HookFunction) update_sliders);
+ hook_dissociate ("set equalizer_preamp", (HookFunction) update_sliders);
}
static GtkWidget * create_window (void)
@@ -123,56 +119,52 @@ static GtkWidget * create_window (void)
const char * const names[AUD_EQUALIZER_NBANDS] = {N_("31 Hz"), N_("63 Hz"),
N_("125 Hz"), N_("250 Hz"), N_("500 Hz"), N_("1 kHz"), N_("2 kHz"),
N_("4 kHz"), N_("8 kHz"), N_("16 kHz")};
- GtkWidget * window, * vbox, * hbox;
- int i;
- window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ GtkWidget * window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title ((GtkWindow *) window, _("Equalizer"));
gtk_window_set_type_hint ((GtkWindow *) window, GDK_WINDOW_TYPE_HINT_DIALOG);
gtk_window_set_resizable ((GtkWindow *) window, FALSE);
gtk_container_set_border_width ((GtkContainer *) window, 6);
- g_signal_connect ((GObject *) window, "delete-event", (GCallback)
- gtk_widget_hide_on_delete, NULL);
- audgui_hide_on_escape (window);
+ audgui_destroy_on_escape (window);
- vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_container_add ((GtkContainer *) window, vbox);
gtk_box_pack_start ((GtkBox *) vbox, create_on_off (), FALSE, FALSE, 0);
- hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start ((GtkBox *) vbox, hbox, FALSE, FALSE, 0);
- gtk_box_pack_start ((GtkBox *) hbox, create_slider (_("Preamp"), -1), FALSE, FALSE, 0);
+ GtkWidget * preamp = create_slider (_("Preamp"), -1, hbox);
+ g_object_set_data ((GObject *) window, "preamp", preamp);
+
gtk_box_pack_start ((GtkBox *) hbox,
gtk_separator_new (GTK_ORIENTATION_VERTICAL), FALSE, FALSE, 0);
- for (i = 0; i < AUD_EQUALIZER_NBANDS; i ++)
- gtk_box_pack_start ((GtkBox *) hbox, create_slider (_(names[i]), i), FALSE, FALSE, 0);
+ for (int i = 0; i < AUD_EQUALIZER_NBANDS; i ++)
+ {
+ GtkWidget * slider = create_slider (_(names[i]), i, hbox);
+ SPRINTF (slider_id, "slider%d", i);
+ g_object_set_data ((GObject *) window, slider_id, slider);
+ }
+
+ update_sliders (NULL, window);
+
+ hook_associate ("set equalizer_preamp", (HookFunction) update_sliders, window);
+ hook_associate ("set equalizer_bands", (HookFunction) update_sliders, window);
+
+ g_signal_connect (window, "destroy", (GCallback) destroy_cb, NULL);
- gtk_widget_show_all (vbox);
return window;
}
-static GtkWidget * equalizer_window = NULL;
-
EXPORT void audgui_show_equalizer_window (void)
{
- if (equalizer_window == NULL)
- equalizer_window = create_window ();
-
- gtk_window_present ((GtkWindow *) equalizer_window);
+ if (! audgui_reshow_unique_window (AUDGUI_EQUALIZER_WINDOW))
+ audgui_show_unique_window (AUDGUI_EQUALIZER_WINDOW, create_window ());
}
EXPORT void audgui_hide_equalizer_window (void)
{
- if (! equalizer_window)
- return;
-
- hook_dissociate ("set equalizer_active", (HookFunction) on_off_update);
- hook_dissociate ("set equalizer_bands", (HookFunction) slider_update);
- hook_dissociate ("set equalizer_preamp", (HookFunction) slider_update);
-
- gtk_widget_destroy (equalizer_window);
- equalizer_window = NULL;
+ audgui_hide_unique_window (AUDGUI_EQUALIZER_WINDOW);
}
diff --git a/src/libaudgui/icons-stock.c b/src/libaudgui/icons-stock.c
deleted file mode 100644
index fb8200f..0000000
--- a/src/libaudgui/icons-stock.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * icons-stock.c
- * Copyright 2007-2010 Michael FĂ€rber 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 <gtk/gtk.h>
-#include <audacious/misc.h>
-
-#include "libaudgui.h"
-
-static void load_stock_icon (char * id, char * filename,
- GtkIconFactory * iconfactory)
-{
- char * path = g_strdup_printf ("%s/images/%s",
- aud_get_path (AUD_PATH_DATA_DIR), filename);
-
- GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file (path, NULL);
- if (pixbuf == NULL)
- goto ERR;
-
- GtkIconSet * iconset = gtk_icon_set_new_from_pixbuf(pixbuf);
- g_object_unref(pixbuf);
-
- gtk_icon_factory_add(iconfactory, id, iconset);
-
-ERR:
- g_free (path);
-}
-
-EXPORT void
-audgui_register_stock_icons(void)
-{
- GtkIconFactory *iconfactory = gtk_icon_factory_new();
-
- load_stock_icon(AUD_STOCK_AUDACIOUS,
- "audacious.png", iconfactory);
- load_stock_icon(AUD_STOCK_PLAYLIST,
- "menu_playlist.png", iconfactory);
- load_stock_icon(AUD_STOCK_PLUGIN,
- "menu_plugin.png", iconfactory);
- load_stock_icon(AUD_STOCK_QUEUETOGGLE,
- "menu_queue_toggle.png", iconfactory);
-
- gtk_icon_factory_add_default( iconfactory );
- g_object_unref( iconfactory );
-}
diff --git a/src/libaudgui/iface-menu.c b/src/libaudgui/iface-menu.c
deleted file mode 100644
index 448a634..0000000
--- a/src/libaudgui/iface-menu.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * iface-menu.c
- * Copyright 2010 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 <gtk/gtk.h>
-#include <audacious/plugins.h>
-
-#include "libaudgui-gtk.h"
-
-static void switch_cb (GtkMenuItem * item, PluginHandle * plugin)
-{
- if (gtk_check_menu_item_get_active ((GtkCheckMenuItem *) item))
- aud_plugin_enable (plugin, TRUE);
-}
-
-typedef struct {
- GtkWidget * menu;
- GSList * group;
-} IfaceMenuAddState;
-
-static bool_t add_item_cb (PluginHandle * plugin, IfaceMenuAddState * state)
-{
- GtkWidget * item = gtk_radio_menu_item_new_with_label (state->group,
- aud_plugin_get_name (plugin));
- state->group = gtk_radio_menu_item_get_group ((GtkRadioMenuItem *) item);
- if (aud_plugin_get_enabled (plugin))
- gtk_check_menu_item_set_active ((GtkCheckMenuItem *) item, TRUE);
- gtk_menu_shell_append ((GtkMenuShell *) state->menu, item);
- g_signal_connect (item, "activate", (GCallback) switch_cb, plugin);
- gtk_widget_show (item);
- return TRUE;
-}
-
-EXPORT GtkWidget * audgui_create_iface_menu (void)
-{
- IfaceMenuAddState state = {gtk_menu_new (), NULL};
- aud_plugin_for_each (PLUGIN_TYPE_IFACE, (PluginForEachFunc) add_item_cb,
- & state);
- return state.menu;
-}
diff --git a/src/libaudgui/infopopup.c b/src/libaudgui/infopopup.c
index 6cc341d..d0f6fb0 100644
--- a/src/libaudgui/infopopup.c
+++ b/src/libaudgui/infopopup.c
@@ -1,6 +1,7 @@
/*
* infopopup.c
- * Copyright 2006-2012 William Pitcock, Giacomo Lozito, and John Lindgren
+ * Copyright 2006-2012 William Pitcock, Giacomo Lozito, John Lindgren, and
+ * Thomas Lange
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -24,15 +25,15 @@
#include <audacious/i18n.h>
#include <audacious/misc.h>
#include <audacious/playlist.h>
+#include <libaudcore/audstrings.h>
#include <libaudcore/hook.h>
+#include "init.h"
#include "libaudgui.h"
#include "libaudgui-gtk.h"
#define IMAGE_SIZE 96
-static GtkWidget * infopopup = NULL;
-
static struct {
GtkWidget * title_header, * title_label;
GtkWidget * artist_header, * artist_label;
@@ -80,7 +81,8 @@ static bool_t infopopup_progress_cb (void * unused)
gtk_progress_bar_set_fraction ((GtkProgressBar *) widgets.progress,
time / (float) length);
- SPRINTF (time_str, "%d:%02d", time / 60000, (time / 1000) % 60);
+ char time_str[16];
+ audgui_format_time (time_str, sizeof time_str, time);
gtk_progress_bar_set_text ((GtkProgressBar *) widgets.progress, time_str);
gtk_widget_show (widgets.progress);
@@ -121,16 +123,15 @@ static void infopopup_destroyed (void)
progress_source = 0;
}
- infopopup = NULL;
memset (& widgets, 0, sizeof widgets);
str_unref (current_file);
current_file = NULL;
}
-static void infopopup_create (void)
+static GtkWidget * infopopup_create (void)
{
- infopopup = gtk_window_new (GTK_WINDOW_POPUP);
+ GtkWidget * infopopup = gtk_window_new (GTK_WINDOW_POPUP);
gtk_window_set_type_hint ((GtkWindow *) infopopup, GDK_WINDOW_TYPE_HINT_TOOLTIP);
gtk_window_set_decorated ((GtkWindow *) infopopup, FALSE);
gtk_container_set_border_width ((GtkContainer *) infopopup, 4);
@@ -163,7 +164,8 @@ static void infopopup_create (void)
/* do not show the track progress */
gtk_widget_set_no_show_all (widgets.progress, TRUE);
- gtk_widget_show_all (hbox);
+
+ return infopopup;
}
/* calls str_unref() on <text> */
@@ -187,13 +189,13 @@ static void infopopup_set_field (GtkWidget * header, GtkWidget * label, char * t
static void infopopup_set_fields (const Tuple * tuple, const char * title)
{
/* use title from tuple if possible */
- char * title2 = tuple_get_str (tuple, FIELD_TITLE, NULL);
+ char * title2 = tuple_get_str (tuple, FIELD_TITLE);
if (! title2)
title2 = str_get (title);
- char * artist = tuple_get_str (tuple, FIELD_ARTIST, NULL);
- char * album = tuple_get_str (tuple, FIELD_ALBUM, NULL);
- char * genre = tuple_get_str (tuple, FIELD_GENRE, NULL);
+ char * artist = tuple_get_str (tuple, FIELD_ARTIST);
+ char * album = tuple_get_str (tuple, FIELD_ALBUM);
+ char * genre = tuple_get_str (tuple, FIELD_GENRE);
infopopup_set_field (widgets.title_header, widgets.title_label, title2);
infopopup_set_field (widgets.artist_header, widgets.artist_label, artist);
@@ -203,20 +205,29 @@ static void infopopup_set_fields (const Tuple * tuple, const char * title)
int value;
char * tmp;
- value = tuple_get_int (tuple, FIELD_LENGTH, NULL);
- tmp = (value > 0) ? str_printf ("%d:%02d", value / 60000, value / 1000 % 60) : NULL;
+ value = tuple_get_int (tuple, FIELD_LENGTH);
+
+ if (value > 0)
+ {
+ char buf[16];
+ audgui_format_time (buf, sizeof buf, value);
+ tmp = str_get (buf);
+ }
+ else
+ tmp = NULL;
+
infopopup_set_field (widgets.length_header, widgets.length_label, tmp);
- value = tuple_get_int (tuple, FIELD_YEAR, NULL);
- tmp = (value > 0) ? str_printf ("%d", value) : NULL;
+ value = tuple_get_int (tuple, FIELD_YEAR);
+ tmp = (value > 0) ? int_to_str (value) : NULL;
infopopup_set_field (widgets.year_header, widgets.year_label, tmp);
- value = tuple_get_int (tuple, FIELD_TRACK_NUMBER, NULL);
- tmp = (value > 0) ? str_printf ("%d", value) : NULL;
+ value = tuple_get_int (tuple, FIELD_TRACK_NUMBER);
+ tmp = (value > 0) ? int_to_str (value) : NULL;
infopopup_set_field (widgets.track_header, widgets.track_label, tmp);
}
-static void infopopup_move_to_mouse (void)
+static void infopopup_move_to_mouse (GtkWidget * infopopup)
{
int x, y, h, w;
@@ -242,13 +253,12 @@ static void infopopup_move_to_mouse (void)
static void infopopup_show (const char * filename, const Tuple * tuple,
const char * title)
{
- if (infopopup)
- gtk_widget_destroy (infopopup);
+ audgui_hide_unique_window (AUDGUI_INFOPOPUP_WINDOW);
str_unref (current_file);
current_file = str_get (filename);
- infopopup_create ();
+ GtkWidget * infopopup = infopopup_create ();
infopopup_set_fields (tuple, title);
infopopup_display_image (filename);
@@ -264,8 +274,9 @@ static void infopopup_show (const char * filename, const Tuple * tuple,
/* immediately run the callback once to update progressbar status */
infopopup_progress_cb (NULL);
- infopopup_move_to_mouse ();
- gtk_widget_show (infopopup);
+ infopopup_move_to_mouse (infopopup);
+
+ audgui_show_unique_window (AUDGUI_INFOPOPUP_WINDOW, infopopup);
}
EXPORT void audgui_infopopup_show (int playlist, int entry)
@@ -298,6 +309,5 @@ EXPORT void audgui_infopopup_show_current (void)
EXPORT void audgui_infopopup_hide (void)
{
- if (infopopup)
- gtk_widget_destroy (infopopup);
+ audgui_hide_unique_window (AUDGUI_INFOPOPUP_WINDOW);
}
diff --git a/src/libaudgui/infowin.c b/src/libaudgui/infowin.c
index 9156569..530a65e 100644
--- a/src/libaudgui/infowin.c
+++ b/src/libaudgui/infowin.c
@@ -1,7 +1,7 @@
/*
* libaudgui/infowin.c
- * Copyright 2006-2012 William Pitcock, Tomasz MoƄ, Eugene Zagidullin, and
- * John Lindgren
+ * Copyright 2006-2013 William Pitcock, Tomasz MoƄ, Eugene Zagidullin,
+ * John Lindgren, and Thomas Lange
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -29,20 +29,24 @@
#include <libaudcore/audstrings.h>
#include <libaudcore/hook.h>
+#include "init.h"
#include "libaudgui.h"
#include "libaudgui-gtk.h"
#define AUDGUI_STATUS_TIMEOUT 3000
-#define IMAGE_SIZE 150
-enum
-{
- RAWDATA_KEY,
- RAWDATA_VALUE,
- RAWDATA_N_COLS
+enum {
+ CODEC_FORMAT,
+ CODEC_QUALITY,
+ CODEC_BITRATE,
+ CODEC_ITEMS
};
-static GtkWidget * infowin = NULL;
+static const char * codec_labels[CODEC_ITEMS] = {
+ N_("Format:"),
+ N_("Quality:"),
+ N_("Bitrate:")
+};
static struct {
GtkWidget * location;
@@ -54,9 +58,7 @@ static struct {
GtkWidget * track;
GtkWidget * genre;
GtkWidget * image;
- GtkWidget * format;
- GtkWidget * quality;
- GtkWidget * bitrate;
+ GtkWidget * codec[3];
GtkWidget * apply;
GtkWidget * ministatus;
} widgets;
@@ -64,6 +66,7 @@ static struct {
static char * current_file = NULL;
static PluginHandle * current_decoder = NULL;
static bool_t can_write = FALSE;
+static int timeout_source = 0;
/* This is by no means intended to be a complete list. If it is not short, it
* is useless: scrolling through ten pages of dropdown list is more work than
@@ -110,10 +113,27 @@ static const char * genre_table[] = {
N_("Techno"),
N_("Trip-hop")};
+static GtkWidget * small_label_new (const char * text)
+{
+ static PangoAttrList * attrs = NULL;
+
+ if (! attrs)
+ {
+ attrs = pango_attr_list_new ();
+ pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
+ }
+
+ GtkWidget * label = gtk_label_new (text);
+ gtk_label_set_attributes ((GtkLabel *) label, attrs);
+ gtk_misc_set_alignment ((GtkMisc *) label, 0, 0.5);
+
+ return label;
+}
+
static void set_entry_str_from_field (GtkWidget * widget, const Tuple * tuple,
int fieldn, bool_t editable)
{
- char * text = tuple_get_str (tuple, fieldn, NULL);
+ char * text = tuple_get_str (tuple, fieldn);
gtk_entry_set_text ((GtkEntry *) widget, text != NULL ? text : "");
gtk_editable_set_editable ((GtkEditable *) widget, editable);
@@ -126,9 +146,8 @@ static void set_entry_int_from_field (GtkWidget * widget, const Tuple * tuple,
{
char scratch[32];
- if (tuple_get_value_type (tuple, fieldn, NULL) == TUPLE_INT)
- snprintf (scratch, sizeof scratch, "%d", tuple_get_int (tuple, fieldn,
- NULL));
+ if (tuple_get_value_type (tuple, fieldn) == TUPLE_INT)
+ str_itoa (tuple_get_int (tuple, fieldn), scratch, sizeof scratch);
else
scratch[0] = 0;
@@ -142,9 +161,9 @@ static void set_field_str_from_entry (Tuple * tuple, int fieldn, GtkWidget *
const char * text = gtk_entry_get_text ((GtkEntry *) widget);
if (text[0])
- tuple_set_str (tuple, fieldn, NULL, text);
+ tuple_set_str (tuple, fieldn, text);
else
- tuple_unset (tuple, fieldn, NULL);
+ tuple_unset (tuple, fieldn);
}
static void set_field_int_from_entry (Tuple * tuple, int fieldn, GtkWidget *
@@ -153,28 +172,9 @@ static void set_field_int_from_entry (Tuple * tuple, int fieldn, GtkWidget *
const char * text = gtk_entry_get_text ((GtkEntry *) widget);
if (text[0])
- tuple_set_int (tuple, fieldn, NULL, atoi (text));
- else
- tuple_unset (tuple, fieldn, NULL);
-}
-
-/* call str_unref() on <text> */
-static void infowin_label_set_text (GtkWidget * widget, char * text)
-{
- char * tmp;
-
- if (text)
- {
- tmp = g_strdup_printf("<span size=\"small\">%s</span>", text);
- gtk_label_set_text ((GtkLabel *) widget, tmp);
- g_free (tmp);
- str_unref (text);
- }
+ tuple_set_int (tuple, fieldn, atoi (text));
else
- gtk_label_set_text ((GtkLabel *) widget,
- _("<span size=\"small\">n/a</span>"));
-
- gtk_label_set_use_markup ((GtkLabel *) widget, TRUE);
+ tuple_unset (tuple, fieldn);
}
static void entry_changed (GtkEditable * editable, void * unused)
@@ -183,26 +183,23 @@ static void entry_changed (GtkEditable * editable, void * unused)
gtk_widget_set_sensitive (widgets.apply, TRUE);
}
-static bool_t ministatus_timeout_proc (void * data)
+static bool_t ministatus_timeout_proc (void)
{
- GtkLabel * status = data;
-
- gtk_label_set_text (status, "<span size=\"small\"></span>");
- gtk_label_set_use_markup (status, TRUE);
+ gtk_label_set_text ((GtkLabel *) widgets.ministatus, NULL);
+ timeout_source = 0;
return FALSE;
}
static void ministatus_display_message (const char * text)
{
- char * tmp = g_strdup_printf ("<span size=\"small\">%s</span>", text);
+ gtk_label_set_text ((GtkLabel *) widgets.ministatus, text);
- gtk_label_set_text ((GtkLabel *) widgets.ministatus, tmp);
- gtk_label_set_use_markup ((GtkLabel *) widgets.ministatus, TRUE);
- g_free (tmp);
+ if (timeout_source)
+ g_source_remove (timeout_source);
- g_timeout_add (AUDGUI_STATUS_TIMEOUT, (GSourceFunc) ministatus_timeout_proc,
- widgets.ministatus);
+ timeout_source = g_timeout_add (AUDGUI_STATUS_TIMEOUT, (GSourceFunc)
+ ministatus_timeout_proc, NULL);
}
static void infowin_update_tuple (void * unused)
@@ -220,11 +217,11 @@ static void infowin_update_tuple (void * unused)
if (aud_file_write_tuple (current_file, current_decoder, tuple))
{
- ministatus_display_message (_("Metadata updated successfully"));
+ ministatus_display_message (_("Save successful"));
gtk_widget_set_sensitive (widgets.apply, FALSE);
}
else
- ministatus_display_message (_("Metadata updating failed"));
+ ministatus_display_message (_("Save error"));
tuple_unref (tuple);
}
@@ -235,7 +232,7 @@ static bool_t genre_fill (GtkWidget * combo)
GList * node;
int i;
- for (i = 0; i < G_N_ELEMENTS (genre_table); i ++)
+ for (i = 0; i < ARRAY_LEN (genre_table); i ++)
list = g_list_prepend (list, _(genre_table[i]));
list = g_list_sort (list, (GCompareFunc) strcmp);
@@ -256,16 +253,23 @@ static void infowin_display_image (const char * filename)
if (! pb)
pb = audgui_pixbuf_fallback ();
- audgui_pixbuf_scale_within (& pb, IMAGE_SIZE);
- gtk_image_set_from_pixbuf ((GtkImage *) widgets.image, pb);
- g_object_unref (pb);
+ if (pb)
+ {
+ audgui_scaled_image_set (widgets.image, pb);
+ g_object_unref (pb);
+ }
}
static void infowin_destroyed (void)
{
hook_dissociate ("art ready", (HookFunction) infowin_display_image);
- infowin = NULL;
+ if (timeout_source)
+ {
+ g_source_remove (timeout_source);
+ timeout_source = 0;
+ }
+
memset (& widgets, 0, sizeof widgets);
str_unref (current_file);
@@ -274,226 +278,115 @@ static void infowin_destroyed (void)
can_write = FALSE;
}
-static void create_infowin (void)
+static void add_entry (GtkWidget * grid, const char * title, GtkWidget * entry,
+ int x, int y, int span)
{
- GtkWidget * hbox;
- GtkWidget * hbox_status_and_bbox;
- GtkWidget * vbox0;
- GtkWidget * vbox2;
- GtkWidget * label_title;
- GtkWidget * label_artist;
- GtkWidget * label_album;
- GtkWidget * label_comment;
- GtkWidget * label_genre;
- GtkWidget * label_year;
- GtkWidget * label_track;
- GtkWidget * label_format;
- GtkWidget * label_quality_label;
- GtkWidget * label_bitrate_label;
- GtkWidget * codec_hbox;
- GtkWidget * codec_grid;
- GtkWidget * grid1;
- GtkWidget * bbox_close;
- GtkWidget * btn_close;
- GtkWidget * alignment;
-
- infowin = gtk_window_new (GTK_WINDOW_TOPLEVEL);
- gtk_container_set_border_width ((GtkContainer *) infowin, 6);
- gtk_window_set_title ((GtkWindow *) infowin, _("Track Information"));
- gtk_window_set_type_hint ((GtkWindow *) infowin,
- GDK_WINDOW_TYPE_HINT_DIALOG);
+ GtkWidget * label = small_label_new (title);
- vbox0 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
- gtk_container_add ((GtkContainer *) infowin, vbox0);
+ if (y > 0)
+ gtk_widget_set_margin_top (label, 6);
- hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
- gtk_box_pack_start ((GtkBox *) vbox0, hbox, TRUE, TRUE, 0);
+ gtk_grid_attach ((GtkGrid *) grid, label, x, y, span, 1);
+ gtk_grid_attach ((GtkGrid *) grid, entry, x, y + 1, span, 1);
- vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
- gtk_box_pack_start ((GtkBox *) hbox, vbox2, TRUE, TRUE, 0);
+ g_signal_connect (entry, "changed", (GCallback) entry_changed, NULL);
+}
- GtkWidget * vbox3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
- gtk_box_pack_start ((GtkBox *) vbox2, vbox3, TRUE, FALSE, 0);
+static GtkWidget * create_infowin (void)
+{
+ GtkWidget * infowin = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_container_set_border_width ((GtkContainer *) infowin, 6);
+ gtk_window_set_title ((GtkWindow *) infowin, _("Song Info"));
+ gtk_window_set_type_hint ((GtkWindow *) infowin,
+ GDK_WINDOW_TYPE_HINT_DIALOG);
- widgets.image = gtk_image_new ();
- gtk_widget_set_size_request (widgets.image, IMAGE_SIZE, IMAGE_SIZE);
- gtk_box_pack_start ((GtkBox *) vbox3, widgets.image, FALSE, FALSE, 0);
+ GtkWidget * main_grid = gtk_grid_new ();
+ gtk_grid_set_column_spacing ((GtkGrid *) main_grid, 6);
+ gtk_grid_set_row_spacing ((GtkGrid *) main_grid, 6);
+ gtk_container_add ((GtkContainer *) infowin, main_grid);
+
+ widgets.image = audgui_scaled_image_new (NULL);
+ gtk_widget_set_hexpand (widgets.image, TRUE);
+ gtk_widget_set_vexpand (widgets.image, TRUE);
+ gtk_grid_attach ((GtkGrid *) main_grid, widgets.image, 0, 0, 1, 1);
widgets.location = gtk_label_new ("");
- gtk_widget_set_size_request (widgets.location, 200, -1);
+ gtk_label_set_max_width_chars ((GtkLabel *) widgets.location, 40);
gtk_label_set_line_wrap ((GtkLabel *) widgets.location, TRUE);
gtk_label_set_line_wrap_mode ((GtkLabel *) widgets.location, PANGO_WRAP_WORD_CHAR);
gtk_label_set_selectable ((GtkLabel *) widgets.location, TRUE);
- gtk_box_pack_start ((GtkBox *) vbox3, widgets.location, FALSE, FALSE, 0);
-
- codec_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
- gtk_box_pack_start ((GtkBox *) vbox2, codec_hbox, FALSE, FALSE, 0);
+ gtk_grid_attach ((GtkGrid *) main_grid, widgets.location, 0, 1, 1, 1);
- codec_grid = gtk_grid_new ();
+ GtkWidget * codec_grid = gtk_grid_new ();
gtk_grid_set_row_spacing ((GtkGrid *) codec_grid, 3);
gtk_grid_set_column_spacing ((GtkGrid *) codec_grid, 12);
- gtk_box_pack_start ((GtkBox *) codec_hbox, codec_grid, FALSE, FALSE, 0);
-
- label_format = gtk_label_new (_("<span size=\"small\">Format:</span>"));
- gtk_label_set_use_markup ((GtkLabel *) label_format, TRUE);
- gtk_misc_set_alignment ((GtkMisc *) label_format, 0, 0.5);
- label_quality_label = gtk_label_new
- (_("<span size=\"small\">Quality:</span>"));
- gtk_label_set_use_markup ((GtkLabel *) label_quality_label, TRUE);
- gtk_misc_set_alignment ((GtkMisc *) label_quality_label, 0, 0.5);
- label_bitrate_label = gtk_label_new (_("<span size=\"small\">Bitrate:</span>"));
- gtk_label_set_use_markup ((GtkLabel *) label_bitrate_label, TRUE);
- gtk_misc_set_alignment ((GtkMisc *) label_bitrate_label, 0, 0.5);
-
- widgets.format = gtk_label_new (_("<span size=\"small\">n/a</span>"));
- gtk_label_set_use_markup ((GtkLabel *) widgets.format, TRUE);
- gtk_misc_set_alignment ((GtkMisc *) widgets.format, 0, 0.5);
- widgets.quality = gtk_label_new (_("<span size=\"small\">n/a</span>"));
- gtk_label_set_use_markup ((GtkLabel *) widgets.quality, TRUE);
- gtk_misc_set_alignment ((GtkMisc *) widgets.quality, 0, 0.5);
- widgets.bitrate = gtk_label_new (_("<span size=\"small\">n/a</span>"));
- gtk_label_set_use_markup ((GtkLabel *) widgets.bitrate, TRUE);
- gtk_misc_set_alignment ((GtkMisc *) widgets.bitrate, 0, 0.5);
-
- gtk_grid_attach ((GtkGrid *) codec_grid, label_format, 0, 0, 1, 1);
- gtk_grid_attach ((GtkGrid *) codec_grid, widgets.format, 1, 0, 1, 1);
- gtk_grid_attach ((GtkGrid *) codec_grid, label_quality_label, 0, 1, 1, 1);
- gtk_grid_attach ((GtkGrid *) codec_grid, widgets.quality, 1, 1, 1, 1);
- gtk_grid_attach ((GtkGrid *) codec_grid, label_bitrate_label, 0, 2, 1, 1);
- gtk_grid_attach ((GtkGrid *) codec_grid, widgets.bitrate, 1, 2, 1, 1);
-
- vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
- gtk_box_pack_start ((GtkBox *) hbox, vbox2, TRUE, TRUE, 0);
-
- label_title = gtk_label_new (_("<span size=\"small\">Title</span>"));
- gtk_box_pack_start ((GtkBox *) vbox2, label_title, FALSE, FALSE, 0);
- gtk_label_set_use_markup ((GtkLabel *) label_title, TRUE);
- gtk_misc_set_alignment ((GtkMisc *) label_title, 0, 0);
-
- alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
- gtk_box_pack_start ((GtkBox *) vbox2, alignment, FALSE, FALSE, 0);
- gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 6, 0, 0);
- widgets.title = gtk_entry_new ();
- gtk_container_add ((GtkContainer *) alignment, widgets.title);
- g_signal_connect (widgets.title, "changed", (GCallback) entry_changed, NULL);
-
- label_artist = gtk_label_new (_("<span size=\"small\">Artist</span>"));
- gtk_box_pack_start ((GtkBox *) vbox2, label_artist, FALSE, FALSE, 0);
- gtk_label_set_use_markup ((GtkLabel *) label_artist, TRUE);
- gtk_misc_set_alignment ((GtkMisc *) label_artist, 0, 0.5);
+ gtk_grid_attach ((GtkGrid *) main_grid, codec_grid, 0, 2, 1, 1);
- alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
- gtk_box_pack_start ((GtkBox *) vbox2, alignment, FALSE, FALSE, 0);
- gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 6, 0, 0);
- widgets.artist = gtk_entry_new ();
- gtk_container_add ((GtkContainer *) alignment, widgets.artist);
- g_signal_connect (widgets.artist, "changed", (GCallback) entry_changed, NULL);
+ for (int row = 0; row < CODEC_ITEMS; row ++)
+ {
+ GtkWidget * label = small_label_new (_(codec_labels[row]));
+ gtk_grid_attach ((GtkGrid *) codec_grid, label, 0, row, 1, 1);
- label_album = gtk_label_new (_("<span size=\"small\">Album</span>"));
- gtk_box_pack_start ((GtkBox *) vbox2, label_album, FALSE, FALSE, 0);
- gtk_label_set_use_markup ((GtkLabel *) label_album, TRUE);
- gtk_misc_set_alignment ((GtkMisc *) label_album, 0, 0.5);
+ widgets.codec[row] = small_label_new (NULL);
+ gtk_grid_attach ((GtkGrid *) codec_grid, widgets.codec[row], 1, row, 1, 1);
+ }
- alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
- gtk_box_pack_start ((GtkBox *) vbox2, alignment, FALSE, FALSE, 0);
- gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 6, 0, 0);
- widgets.album = gtk_entry_new ();
- gtk_container_add ((GtkContainer *) alignment, widgets.album);
- g_signal_connect (widgets.album, "changed", (GCallback) entry_changed, NULL);
+ GtkWidget * grid = gtk_grid_new ();
+ gtk_grid_set_column_homogeneous ((GtkGrid *) grid, TRUE);
+ gtk_grid_set_column_spacing ((GtkGrid *) grid, 6);
+ gtk_grid_attach ((GtkGrid *) main_grid, grid, 1, 0, 1, 3);
- label_comment = gtk_label_new (_("<span size=\"small\">Comment</span>"));
- gtk_box_pack_start ((GtkBox *) vbox2, label_comment, FALSE, FALSE, 0);
- gtk_label_set_use_markup ((GtkLabel *) label_comment, TRUE);
- gtk_misc_set_alignment ((GtkMisc *) label_comment, 0, 0.5);
+ widgets.title = gtk_entry_new ();
+ add_entry (grid, _("Title"), widgets.title, 0, 0, 2);
- alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
- gtk_box_pack_start ((GtkBox *) vbox2, alignment, FALSE, FALSE, 0);
- gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 6, 0, 0);
- widgets.comment = gtk_entry_new ();
- gtk_container_add ((GtkContainer *) alignment, widgets.comment);
- g_signal_connect (widgets.comment, "changed", (GCallback) entry_changed, NULL);
+ widgets.artist = gtk_entry_new ();
+ add_entry (grid, _("Artist"), widgets.artist, 0, 2, 2);
- label_genre = gtk_label_new (_("<span size=\"small\">Genre</span>"));
- gtk_box_pack_start ((GtkBox *) vbox2, label_genre, FALSE, FALSE, 0);
- gtk_label_set_use_markup ((GtkLabel *) label_genre, TRUE);
- gtk_misc_set_alignment ((GtkMisc *) label_genre, 0, 0.5);
+ widgets.album = gtk_entry_new ();
+ add_entry (grid, _("Album"), widgets.album, 0, 4, 2);
- alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
- gtk_box_pack_start ((GtkBox *) vbox2, alignment, FALSE, FALSE, 0);
- gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 6, 0, 0);
+ widgets.comment = gtk_entry_new ();
+ add_entry (grid, _("Comment"), widgets.comment, 0, 6, 2);
widgets.genre = gtk_combo_box_text_new_with_entry ();
- gtk_container_add ((GtkContainer *) alignment, widgets.genre);
- g_signal_connect (widgets.genre, "changed", (GCallback) entry_changed, NULL);
+ add_entry (grid, _("Genre"), widgets.genre, 0, 8, 2);
g_idle_add ((GSourceFunc) genre_fill, widgets.genre);
- alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
- gtk_box_pack_start ((GtkBox *) vbox2, alignment, FALSE, FALSE, 0);
- gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 6, 0, 0);
- grid1 = gtk_grid_new ();
- gtk_grid_set_column_homogeneous ((GtkGrid *) grid1, TRUE);
- gtk_container_add ((GtkContainer *) alignment, grid1);
- gtk_grid_set_column_spacing ((GtkGrid *) grid1, 6);
+ widgets.year = gtk_entry_new ();
+ add_entry (grid, _("Year"), widgets.year, 0, 10, 1);
- label_year = gtk_label_new (_("<span size=\"small\">Year</span>"));
- gtk_grid_attach ((GtkGrid *) grid1, label_year, 0, 0, 1, 1);
- gtk_label_set_use_markup ((GtkLabel *) label_year, TRUE);
- gtk_misc_set_alignment ((GtkMisc *) label_year, 0, 0.5);
+ widgets.track = gtk_entry_new ();
+ add_entry (grid, _("Track Number"), widgets.track, 1, 10, 1);
- widgets.year = gtk_entry_new ();
- gtk_grid_attach ((GtkGrid *) grid1, widgets.year, 0, 1, 1, 1);
- g_signal_connect (widgets.year, "changed", (GCallback) entry_changed, NULL);
+ GtkWidget * bottom_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_grid_attach ((GtkGrid *) main_grid, bottom_hbox, 0, 3, 2, 1);
- label_track = gtk_label_new (_("<span size=\"small\">Track Number</span>"));
- gtk_grid_attach ((GtkGrid *) grid1, label_track, 1, 0, 1, 1);
- gtk_label_set_use_markup ((GtkLabel *) label_track, TRUE);
- gtk_misc_set_alignment ((GtkMisc *) label_track, 0, 0.5);
+ widgets.ministatus = small_label_new (NULL);
+ gtk_box_pack_start ((GtkBox *) bottom_hbox, widgets.ministatus, TRUE, TRUE, 0);
- widgets.track = gtk_entry_new ();
- gtk_grid_attach ((GtkGrid *) grid1, widgets.track, 1, 1, 1, 1);
- g_signal_connect (widgets.track, "changed", (GCallback) entry_changed, NULL);
-
- hbox_status_and_bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
- gtk_box_pack_start ((GtkBox *) vbox0, hbox_status_and_bbox, FALSE, FALSE, 0);
-
- widgets.ministatus = gtk_label_new ("<span size=\"small\"></span>");
- gtk_label_set_use_markup ((GtkLabel *) widgets.ministatus, TRUE);
- gtk_misc_set_alignment ((GtkMisc *) widgets.ministatus, 0, 0.5);
- gtk_box_pack_start ((GtkBox *) hbox_status_and_bbox, widgets.ministatus,
- TRUE, TRUE, 0);
-
- bbox_close = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
- gtk_box_set_spacing ((GtkBox *) bbox_close, 6);
- gtk_box_pack_start ((GtkBox *) hbox_status_and_bbox, bbox_close, FALSE,
- FALSE, 0);
- gtk_button_box_set_layout ((GtkButtonBox *) bbox_close, GTK_BUTTONBOX_END);
-
- widgets.apply = gtk_button_new_from_stock (GTK_STOCK_SAVE);
- gtk_container_add ((GtkContainer *) bbox_close, widgets.apply);
- g_signal_connect (widgets.apply, "clicked", (GCallback) infowin_update_tuple, NULL);
- gtk_widget_set_sensitive (widgets.apply, FALSE);
+ widgets.apply = audgui_button_new (_("_Save"), "document-save",
+ (AudguiCallback) infowin_update_tuple, NULL);
- btn_close = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
- gtk_container_add ((GtkContainer *) bbox_close, btn_close);
- g_signal_connect_swapped (btn_close, "clicked", (GCallback) gtk_widget_hide,
- infowin);
+ GtkWidget * close_button = audgui_button_new (_("_Close"), "window-close",
+ (AudguiCallback) audgui_infowin_hide, NULL);
- gtk_widget_show_all (vbox0);
- gtk_widget_grab_focus (widgets.title);
+ gtk_box_pack_end ((GtkBox *) bottom_hbox, close_button, FALSE, FALSE, 0);
+ gtk_box_pack_end ((GtkBox *) bottom_hbox, widgets.apply, FALSE, FALSE, 0);
audgui_destroy_on_escape (infowin);
g_signal_connect (infowin, "destroy", (GCallback) infowin_destroyed, NULL);
hook_associate ("art ready", (HookFunction) infowin_display_image, NULL);
+
+ return infowin;
}
static void infowin_show (int list, int entry, const char * filename,
const Tuple * tuple, PluginHandle * decoder, bool_t updating_enabled)
{
- char * tmp;
+ audgui_hide_unique_window (AUDGUI_INFO_WINDOW);
- if (! infowin)
- create_infowin ();
+ GtkWidget * infowin = create_infowin ();
str_unref (current_file);
current_file = str_get (filename);
@@ -507,25 +400,37 @@ static void infowin_show (int list, int entry, const char * filename,
set_entry_str_from_field (gtk_bin_get_child ((GtkBin *) widgets.genre),
tuple, FIELD_GENRE, updating_enabled);
- tmp = uri_to_display (filename);
+ char * tmp = uri_to_display (filename);
gtk_label_set_text ((GtkLabel *) widgets.location, tmp);
- g_free (tmp);
+ str_unref (tmp);
set_entry_int_from_field (widgets.year, tuple, FIELD_YEAR, updating_enabled);
set_entry_int_from_field (widgets.track, tuple, FIELD_TRACK_NUMBER, updating_enabled);
- infowin_label_set_text (widgets.format, tuple_get_str (tuple, FIELD_CODEC, NULL));
- infowin_label_set_text (widgets.quality, tuple_get_str (tuple, FIELD_QUALITY, NULL));
+ char * codec_values[CODEC_ITEMS] = {
+ [CODEC_FORMAT] = tuple_get_str (tuple, FIELD_CODEC),
+ [CODEC_QUALITY] = tuple_get_str (tuple, FIELD_QUALITY)
+ };
- if (tuple_get_value_type (tuple, FIELD_BITRATE, NULL) == TUPLE_INT)
- infowin_label_set_text (widgets.bitrate, str_printf (_("%d kb/s"),
- tuple_get_int (tuple, FIELD_BITRATE, NULL)));
- else
- infowin_label_set_text (widgets.bitrate, NULL);
+ if (tuple_get_value_type (tuple, FIELD_BITRATE) == TUPLE_INT)
+ {
+ int bitrate = tuple_get_int (tuple, FIELD_BITRATE);
+ codec_values[CODEC_BITRATE] = str_printf (_("%d kb/s"), bitrate);
+ }
+
+ for (int row = 0; row < CODEC_ITEMS; row ++)
+ {
+ const char * text = codec_values[row] ? codec_values[row] : _("N/A");
+ gtk_label_set_text ((GtkLabel *) widgets.codec[row], text);
+ str_unref (codec_values[row]);
+ }
infowin_display_image (filename);
- gtk_window_present ((GtkWindow *) infowin);
+ /* nothing has been changed yet */
+ gtk_widget_set_sensitive (widgets.apply, FALSE);
+
+ audgui_show_unique_window (AUDGUI_INFO_WINDOW, infowin);
}
EXPORT void audgui_infowin_show (int playlist, int entry)
@@ -545,10 +450,8 @@ EXPORT void audgui_infowin_show (int playlist, int entry)
if (tuple == NULL)
{
- char * message = g_strdup_printf (_("No info available for %s.\n"),
- filename);
+ SPRINTF (message, _("No info available for %s.\n"), filename);
aud_interface_show_error (message);
- g_free (message);
goto FREE;
}
@@ -578,6 +481,5 @@ EXPORT void audgui_infowin_show_current (void)
EXPORT void audgui_infowin_hide (void)
{
- if (infowin)
- gtk_widget_destroy (infowin);
+ audgui_hide_unique_window (AUDGUI_INFO_WINDOW);
}
diff --git a/src/libaudgui/init.c b/src/libaudgui/init.c
index 1a5517f..3128a55 100644
--- a/src/libaudgui/init.c
+++ b/src/libaudgui/init.c
@@ -1,6 +1,6 @@
/*
* init.c
- * Copyright 2010-2011 John Lindgren
+ * Copyright 2010-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,8 +17,12 @@
* the use of this software.
*/
+#include <stdio.h>
+#include <stdlib.h>
+
#include <audacious/misc.h>
#include <audacious/playlist.h>
+#include <libaudcore/audstrings.h>
#include <libaudcore/hook.h>
#include "init.h"
@@ -33,8 +37,90 @@ static const char * const audgui_defaults[] = {
"remember_jtf_entry", "TRUE",
NULL};
+static const char * const window_names[AUDGUI_NUM_UNIQUE_WINDOWS] = {
+ "about_win",
+ "equalizer_win",
+ "filebrowser_win",
+ NULL, /* infopopup position is not saved */
+ "info_win",
+ "jump_to_time_win",
+ "jump_to_track_win",
+ "playlist_export_win",
+ "playlist_import_win",
+ "playlist_manager_win",
+ "queue_manager_win",
+ "url_opener_win"
+};
+
AudAPITable * _aud_api_table = NULL;
+static GtkWidget * windows[AUDGUI_NUM_UNIQUE_WINDOWS];
+
+static bool_t configure_cb (GtkWidget * window, GdkEventConfigure * event, const char * name)
+{
+ if (gtk_widget_get_visible (window))
+ {
+ int pos[4];
+ gtk_window_get_position ((GtkWindow *) window, & pos[0], & pos[1]);
+ gtk_window_get_size ((GtkWindow *) window, & pos[2], & pos[3]);
+
+ char * str = int_array_to_str (pos, 4);
+ aud_set_str ("audgui", name, str);
+ str_unref (str);
+ }
+
+ return FALSE;
+}
+
+void audgui_show_unique_window (int id, GtkWidget * widget)
+{
+ g_return_if_fail (id >= 0 && id < AUDGUI_NUM_UNIQUE_WINDOWS);
+
+ if (windows[id])
+ gtk_widget_destroy (windows[id]);
+
+ windows[id] = widget;
+ g_signal_connect (widget, "destroy", (GCallback) gtk_widget_destroyed, & windows[id]);
+
+ if (window_names[id])
+ {
+ char * str = aud_get_str ("audgui", window_names[id]);
+ int pos[4];
+
+ if (str_to_int_array (str, pos, 4))
+ {
+ gtk_window_move ((GtkWindow *) widget, pos[0], pos[1]);
+ gtk_window_set_default_size ((GtkWindow *) widget, pos[2], pos[3]);
+ }
+
+ g_signal_connect (widget, "configure-event", (GCallback) configure_cb,
+ (void *) window_names[id]);
+
+ str_unref (str);
+ }
+
+ gtk_widget_show_all (widget);
+}
+
+bool_t audgui_reshow_unique_window (int id)
+{
+ g_return_val_if_fail (id >= 0 && id < AUDGUI_NUM_UNIQUE_WINDOWS, FALSE);
+
+ if (! windows[id])
+ return FALSE;
+
+ gtk_window_present ((GtkWindow *) windows[id]);
+ return TRUE;
+}
+
+void audgui_hide_unique_window (int id)
+{
+ g_return_if_fail (id >= 0 && id < AUDGUI_NUM_UNIQUE_WINDOWS);
+
+ if (windows[id])
+ gtk_widget_destroy (windows[id]);
+}
+
static void playlist_set_playing_cb (void * unused, void * unused2)
{
audgui_pixbuf_uncache ();
@@ -46,13 +132,23 @@ static void playlist_position_cb (void * list, void * unused)
audgui_pixbuf_uncache ();
}
-EXPORT void audgui_init (AudAPITable * table)
+EXPORT void audgui_init (AudAPITable * table, int version)
{
+ if (version != _AUD_PLUGIN_VERSION)
+ {
+ fprintf (stderr, "libaudgui version mismatch\n");
+ abort ();
+ }
+
_aud_api_table = table;
aud_config_set_defaults ("audgui", audgui_defaults);
hook_associate ("playlist set playing", playlist_set_playing_cb, NULL);
hook_associate ("playlist position", playlist_position_cb, NULL);
+
+#ifndef _WIN32
+ gtk_window_set_default_icon_name ("audacious");
+#endif
}
EXPORT void audgui_cleanup (void)
@@ -60,15 +156,8 @@ EXPORT void audgui_cleanup (void)
hook_dissociate ("playlist set playing", playlist_set_playing_cb);
hook_dissociate ("playlist position", playlist_position_cb);
- audgui_hide_equalizer_window ();
- audgui_infopopup_hide ();
- audgui_infowin_hide ();
- audgui_jump_to_time_cleanup ();
- audgui_jump_to_track_hide ();
- audgui_pixbuf_uncache ();
- audgui_playlist_manager_cleanup ();
- audgui_queue_manager_cleanup ();
- audgui_url_opener_cleanup ();
+ for (int id = 0; id < AUDGUI_NUM_UNIQUE_WINDOWS; id ++)
+ audgui_hide_unique_window (id);
_aud_api_table = NULL;
}
diff --git a/src/libaudgui/init.h b/src/libaudgui/init.h
index 1018d0a..32632cf 100644
--- a/src/libaudgui/init.h
+++ b/src/libaudgui/init.h
@@ -1,6 +1,6 @@
/*
* init.h
- * Copyright 2010-2011 John Lindgren
+ * Copyright 2010-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,23 +17,37 @@
* the use of this software.
*/
-#include <audacious/plugin.h>
+#ifndef AUDGUI_INIT_H
+#define AUDGUI_INIT_H
-/* init.c */
-void audgui_init (AudAPITable * table);
-void audgui_cleanup (void);
+#include <gtk/gtk.h>
-/* jump-to-time.c */
-void audgui_jump_to_time_cleanup (void);
+#include <audacious/api.h>
-/* queue-manager.c */
-void audgui_queue_manager_cleanup (void);
+enum {
+ AUDGUI_ABOUT_WINDOW,
+ AUDGUI_EQUALIZER_WINDOW,
+ AUDGUI_FILEBROWSER_WINDOW,
+ AUDGUI_INFOPOPUP_WINDOW,
+ AUDGUI_INFO_WINDOW,
+ AUDGUI_JUMP_TO_TIME_WINDOW,
+ AUDGUI_JUMP_TO_TRACK_WINDOW,
+ AUDGUI_PLAYLIST_EXPORT_WINDOW,
+ AUDGUI_PLAYLIST_IMPORT_WINDOW,
+ AUDGUI_PLAYLIST_MANAGER_WINDOW,
+ AUDGUI_QUEUE_MANAGER_WINDOW,
+ AUDGUI_URL_OPENER_WINDOW,
+ AUDGUI_NUM_UNIQUE_WINDOWS
+};
-/* ui_playlist_manager.c */
-void audgui_playlist_manager_cleanup (void);
+void audgui_show_unique_window (int id, GtkWidget * widget);
+bool_t audgui_reshow_unique_window (int id);
+void audgui_hide_unique_window (int id);
-/* url-opener.c */
-void audgui_url_opener_cleanup (void);
+void audgui_init (AudAPITable * table, int version);
+void audgui_cleanup (void);
-/* util.c */
+/* pixbufs.c */
void audgui_pixbuf_uncache (void);
+
+#endif /* AUDGUI_INIT_H */
diff --git a/src/libaudgui/jump-to-time.c b/src/libaudgui/jump-to-time.c
index f551a07..357b422 100644
--- a/src/libaudgui/jump-to-time.c
+++ b/src/libaudgui/jump-to-time.c
@@ -1,6 +1,6 @@
/*
* jump-to-time.c
- * Copyright 2012 John Lindgren
+ * Copyright 2012-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -22,67 +22,43 @@
#include <audacious/drct.h>
#include <audacious/i18n.h>
+#include <libaudcore/audstrings.h>
#include "init.h"
#include "libaudgui.h"
+#include "libaudgui-gtk.h"
-static GtkWidget * window;
-
-static void response_cb (GtkWidget * dialog, int response)
+static void jump_cb (void * entry)
{
- if (response == GTK_RESPONSE_ACCEPT)
- {
- GtkWidget * entry = g_object_get_data ((GObject *) dialog, "entry");
- const char * text = gtk_entry_get_text ((GtkEntry *) entry);
- unsigned minutes, seconds;
-
- if (sscanf (text, "%u:%u", & minutes, & seconds) == 2 && aud_drct_get_playing ())
- aud_drct_seek ((minutes * 60 + seconds) * 1000);
- }
+ const char * text = gtk_entry_get_text ((GtkEntry *) entry);
+ unsigned minutes, seconds;
- gtk_widget_destroy (dialog);
+ if (sscanf (text, "%u:%u", & minutes, & seconds) == 2 && aud_drct_get_playing ())
+ aud_drct_seek ((minutes * 60 + seconds) * 1000);
}
EXPORT void audgui_jump_to_time (void)
{
- if (window)
- gtk_widget_destroy (window);
+ if (audgui_reshow_unique_window (AUDGUI_JUMP_TO_TIME_WINDOW))
+ return;
- window = gtk_dialog_new_with_buttons (_("Jump to Time"), NULL, 0,
- GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, GTK_STOCK_JUMP_TO,
- GTK_RESPONSE_ACCEPT, NULL);
- gtk_widget_set_size_request (window, 200, -1);
- gtk_window_set_resizable ((GtkWindow *) window, FALSE);
- gtk_dialog_set_default_response ((GtkDialog *) window, GTK_RESPONSE_ACCEPT);
+ GtkWidget * entry = gtk_entry_new ();
+ gtk_entry_set_activates_default ((GtkEntry *) entry, TRUE);
- GtkWidget * box = gtk_dialog_get_content_area ((GtkDialog *) window);
+ GtkWidget * button1 = audgui_button_new (_("_Jump"), "go-jump", jump_cb, entry);
+ GtkWidget * button2 = audgui_button_new (_("_Cancel"), "process-stop", NULL, NULL);
- GtkWidget * label = gtk_label_new (_("Enter time (minutes:seconds):"));
- gtk_misc_set_alignment ((GtkMisc *) label, 0, 0);
- gtk_box_pack_start ((GtkBox *) box, label, FALSE, FALSE, 0);
+ GtkWidget * dialog = audgui_dialog_new (GTK_MESSAGE_OTHER,
+ _("Jump to Time"), _("Enter time (minutes:seconds):"), button1, button2);
- GtkWidget * entry = gtk_entry_new ();
- gtk_entry_set_activates_default ((GtkEntry *) entry, TRUE);
- gtk_box_pack_start ((GtkBox *) box, entry, FALSE, FALSE, 0);
+ audgui_dialog_add_widget (dialog, entry);
if (aud_drct_get_playing ())
{
- char buf[16];
int time = aud_drct_get_time () / 1000;
- snprintf (buf, sizeof buf, "%u:%02u", time / 60, time % 60);
+ SPRINTF (buf, "%u:%02u", time / 60, time % 60);
gtk_entry_set_text ((GtkEntry *) entry, buf);
}
- g_object_set_data ((GObject *) window, "entry", entry);
-
- g_signal_connect (window, "response", (GCallback) response_cb, NULL);
- g_signal_connect (window, "destroy", (GCallback) gtk_widget_destroyed, & window);
-
- gtk_widget_show_all (window);
-}
-
-void audgui_jump_to_time_cleanup (void)
-{
- if (window)
- gtk_widget_destroy (window);
+ audgui_show_unique_window (AUDGUI_JUMP_TO_TIME_WINDOW, dialog);
}
diff --git a/src/libaudgui/libaudgui-gtk.h b/src/libaudgui/libaudgui-gtk.h
index aee1c2a..7e65a03 100644
--- a/src/libaudgui/libaudgui-gtk.h
+++ b/src/libaudgui/libaudgui-gtk.h
@@ -1,6 +1,6 @@
/*
* libaudgui-gtk.h
- * Copyright 2010-2011 John Lindgren
+ * Copyright 2010-2012 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -24,33 +24,31 @@
#include <gtk/gtk.h>
#include <libaudcore/core.h>
-/* effects-menu.c */
-GtkWidget * audgui_create_effects_menu (void);
-GtkWidget * audgui_create_vis_menu (void);
+typedef void (* AudguiCallback) (void * data);
-/* iface-menu.c */
-GtkWidget * audgui_create_iface_menu (void);
+/* pixbufs.c */
+GdkPixbuf * audgui_pixbuf_from_data (const void * data, int64_t size);
+GdkPixbuf * audgui_pixbuf_fallback (void);
+void audgui_pixbuf_scale_within (GdkPixbuf * * pixbuf, int size);
+GdkPixbuf * audgui_pixbuf_request (const char * filename);
+GdkPixbuf * audgui_pixbuf_request_current (void);
+
+/* scaled-image.c */
+GtkWidget * audgui_scaled_image_new (GdkPixbuf * pixbuf);
+void audgui_scaled_image_set (GtkWidget * widget, GdkPixbuf * pixbuf);
/* util.c */
int audgui_get_digit_width (GtkWidget * widget);
void audgui_get_mouse_coords (GtkWidget * widget, int * x, int * y);
-void audgui_hide_on_delete (GtkWidget * widget);
-void audgui_hide_on_escape (GtkWidget * widget);
void audgui_destroy_on_escape (GtkWidget * widget);
void audgui_simple_message (GtkWidget * * widget, GtkMessageType type,
const char * title, const char * text);
-void audgui_connect_check_box (GtkWidget * box, bool_t * setting);
-GdkPixbuf * audgui_pixbuf_from_data (const void * data, int64_t size);
-GdkPixbuf * audgui_pixbuf_fallback (void);
-void audgui_pixbuf_scale_within (GdkPixbuf * * pixbuf, int size);
-
-/* non-blocking (like the aud_art_request_* functions) */
-GdkPixbuf * audgui_pixbuf_request (const char * filename);
-GdkPixbuf * audgui_pixbuf_request_current (void);
+GtkWidget * audgui_button_new (const char * text, const char * icon,
+ AudguiCallback callback, void * data);
-/* deprecated */
-GdkPixbuf * audgui_pixbuf_for_current (void) __attribute ((deprecated));
-GdkPixbuf * audgui_pixbuf_for_entry (int playlist, int entry) __attribute ((deprecated));
+GtkWidget * audgui_dialog_new (GtkMessageType type, const char * title,
+ const char * text, GtkWidget * button1, GtkWidget * button2);
+void audgui_dialog_add_widget (GtkWidget * dialog, GtkWidget * widget);
#endif
diff --git a/src/libaudgui/libaudgui.h b/src/libaudgui/libaudgui.h
index 686fe24..db88ed1 100644
--- a/src/libaudgui/libaudgui.h
+++ b/src/libaudgui/libaudgui.h
@@ -1,7 +1,7 @@
/*
* libaudgui.h
- * Copyright 2007-2009 Giacomo Lozito and Tomasz MoƄ
-
+ * Copyright 2007-2013 Giacomo Lozito, Tomasz MoƄ, and John Lindgren
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
@@ -20,26 +20,20 @@
#ifndef LIBAUDGUI_H
#define LIBAUDGUI_H
+#include <stdint.h>
#include <libaudcore/core.h>
-/* macro defines for Audacious stock icons */
-#define AUD_STOCK_AUDACIOUS "aud-audacious"
-#define AUD_STOCK_PLAYLIST "aud-playlist"
-#define AUD_STOCK_PLUGIN "aud-plugin"
-#define AUD_STOCK_QUEUETOGGLE "aud-queuetoggle"
-
-void audgui_register_stock_icons(void);
-
void audgui_show_add_url_window(bool_t open);
void audgui_jump_to_track(void);
void audgui_jump_to_track_hide(void);
-void audgui_run_filebrowser(bool_t clear_pl_on_ok);
+void audgui_run_filebrowser(bool_t open);
void audgui_hide_filebrowser(void);
/* about.c */
void audgui_show_about_window (void);
+void audgui_hide_about_window (void);
/* confirm.c */
void audgui_confirm_playlist_delete (int playlist);
@@ -78,6 +72,6 @@ void audgui_urilist_insert (int playlist, int position, const char * list);
char * audgui_urilist_create_from_selected (int playlist);
/* util.c */
-void audgui_set_default_icon (void);
+void audgui_format_time (char * buf, int bufsize, int64_t milliseconds);
#endif /* LIBAUDGUI_H */
diff --git a/src/libaudgui/list.c b/src/libaudgui/list.c
index 751a7d5..0fcc269 100644
--- a/src/libaudgui/list.c
+++ b/src/libaudgui/list.c
@@ -1,6 +1,6 @@
/*
* list.c
- * Copyright 2011-2012 John Lindgren and MichaƂ Lipski
+ * Copyright 2011-2013 John Lindgren and MichaƂ Lipski
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/libaudgui/menu.c b/src/libaudgui/menu.c
new file mode 100644
index 0000000..6fe882f
--- /dev/null
+++ b/src/libaudgui/menu.c
@@ -0,0 +1,134 @@
+/*
+ * menu.c
+ * Copyright 2011-2014 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 "menu.h"
+
+#include <audacious/i18n.h>
+#include <audacious/misc.h>
+#include <libaudcore/hook.h>
+
+/* we still use GtkImageMenuItem until there is a good alternative */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+static GtkWidget * image_menu_item_new (const char * text, const char * icon)
+{
+ GtkWidget * widget = gtk_image_menu_item_new_with_mnemonic (text);
+
+ if (icon)
+ {
+ GtkWidget * image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image ((GtkImageMenuItem *) widget, image);
+ }
+
+ return widget;
+}
+
+#pragma GCC diagnostic pop
+
+static void toggled_cb (GtkCheckMenuItem * check, const AudguiMenuItem * item)
+{
+ bool_t on = gtk_check_menu_item_get_active (check);
+
+ if (aud_get_bool (item->csect, item->cname) == on)
+ return;
+
+ aud_set_bool (item->csect, item->cname, on);
+
+ if (item->func)
+ item->func ();
+}
+
+static void hook_cb (void * data, GtkWidget * check)
+{
+ const AudguiMenuItem * item = g_object_get_data ((GObject *) check, "item");
+ gtk_check_menu_item_set_active ((GtkCheckMenuItem *) check, aud_get_bool
+ (item->csect, item->cname));
+}
+
+static void unhook_cb (GtkCheckMenuItem * check, const AudguiMenuItem * item)
+{
+ hook_dissociate_full (item->hook, (HookFunction) hook_cb, check);
+}
+
+EXPORT GtkWidget * audgui_menu_item_new_with_domain
+ (const AudguiMenuItem * item, GtkAccelGroup * accel, const char * domain)
+{
+ const char * name = domain ? dgettext (domain, item->name) : item->name;
+ GtkWidget * widget = NULL;
+
+ if (name && item->func && ! item->cname) /* normal widget */
+ {
+ widget = image_menu_item_new (name, item->icon);
+ g_signal_connect (widget, "activate", item->func, NULL);
+ }
+ else if (name && item->cname) /* toggle widget */
+ {
+ widget = gtk_check_menu_item_new_with_mnemonic (name);
+ gtk_check_menu_item_set_active ((GtkCheckMenuItem *) widget,
+ aud_get_bool (item->csect, item->cname));
+ g_signal_connect (widget, "toggled", (GCallback) toggled_cb, (void *) item);
+
+ if (item->hook)
+ {
+ g_object_set_data ((GObject *) widget, "item", (void *) item);
+ hook_associate (item->hook, (HookFunction) hook_cb, widget);
+ g_signal_connect (widget, "destroy", (GCallback) unhook_cb, (void *) item);
+ }
+ }
+ else if (name && (item->items || item->get_sub)) /* submenu */
+ {
+ widget = image_menu_item_new (name, item->icon);
+
+ GtkWidget * sub;
+
+ if (item->get_sub)
+ sub = item->get_sub ();
+ else
+ {
+ sub = gtk_menu_new ();
+ audgui_menu_init_with_domain (sub, item->items, item->n_items, accel, domain);
+ }
+
+ gtk_menu_item_set_submenu ((GtkMenuItem *) widget, sub);
+ }
+ else if (item->sep) /* separator */
+ widget = gtk_separator_menu_item_new ();
+
+ if (widget && accel && item->key)
+ gtk_widget_add_accelerator (widget, "activate", accel, item->key,
+ item->mod, GTK_ACCEL_VISIBLE);
+
+ return widget;
+}
+
+EXPORT void audgui_menu_init_with_domain (GtkWidget * shell,
+ const AudguiMenuItem * items, int n_items, GtkAccelGroup * accel,
+ const char * domain)
+{
+ for (int i = 0; i < n_items; i ++)
+ {
+ GtkWidget * widget = audgui_menu_item_new_with_domain (& items[i], accel, domain);
+ if (! widget)
+ continue;
+
+ gtk_widget_show (widget);
+ gtk_menu_shell_append ((GtkMenuShell *) shell, widget);
+ }
+}
diff --git a/src/libaudgui/menu.h b/src/libaudgui/menu.h
new file mode 100644
index 0000000..fc27ae5
--- /dev/null
+++ b/src/libaudgui/menu.h
@@ -0,0 +1,62 @@
+/*
+ * menu.h
+ * Copyright 2011-2014 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 AUDGUI_MENU_H
+#define AUDGUI_MENU_H
+
+#include <gtk/gtk.h>
+#include <libaudcore/core.h>
+
+typedef struct _AudguiMenuItem {
+ const char * name;
+ const char * icon;
+ unsigned key;
+ GdkModifierType mod;
+
+ /* for normal items */
+ void (* func) (void);
+
+ /* for toggle items */
+ const char * csect;
+ const char * cname;
+ const char * hook;
+
+ /* for submenus */
+ const struct _AudguiMenuItem * items;
+ int n_items;
+
+ /* for custom submenus */
+ GtkWidget * (* get_sub) (void);
+
+ /* for separators */
+ bool_t sep;
+} AudguiMenuItem;
+
+/* use NULL for domain to skip translation */
+GtkWidget * audgui_menu_item_new_with_domain (const AudguiMenuItem * item,
+ GtkAccelGroup * accel, const char * domain);
+
+void audgui_menu_init_with_domain (GtkWidget * shell,
+ const AudguiMenuItem * items, int n_items, GtkAccelGroup * accel,
+ const char * domain);
+
+#define audgui_menu_item_new(i, a) audgui_menu_item_new_with_domain (i, a, PACKAGE)
+#define audgui_menu_init(s, i, n, a) audgui_menu_init_with_domain (s, i, n, a, PACKAGE)
+
+#endif /* AUDGUI_MENU_H */
diff --git a/src/libaudgui/pixbufs.c b/src/libaudgui/pixbufs.c
new file mode 100644
index 0000000..550dce0
--- /dev/null
+++ b/src/libaudgui/pixbufs.c
@@ -0,0 +1,141 @@
+/*
+ * pixbufs.c
+ * Copyright 2010-2012 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 <gdk-pixbuf/gdk-pixbuf.h>
+
+#include <audacious/debug.h>
+#include <audacious/misc.h>
+#include <audacious/playlist.h>
+#include <libaudcore/audstrings.h>
+
+#include "libaudgui-gtk.h"
+
+static GdkPixbuf * current_pixbuf;
+
+EXPORT GdkPixbuf * audgui_pixbuf_fallback (void)
+{
+ static GdkPixbuf * fallback = NULL;
+
+ if (! fallback)
+ {
+ const char * data_dir = aud_get_path (AUD_PATH_DATA_DIR);
+ SCONCAT2 (path, data_dir, "/images/album.png");
+ fallback = gdk_pixbuf_new_from_file (path, NULL);
+ }
+
+ if (fallback)
+ g_object_ref ((GObject *) fallback);
+
+ return fallback;
+}
+
+void audgui_pixbuf_uncache (void)
+{
+ if (current_pixbuf)
+ {
+ g_object_unref ((GObject *) current_pixbuf);
+ current_pixbuf = NULL;
+ }
+}
+
+EXPORT void audgui_pixbuf_scale_within (GdkPixbuf * * pixbuf, int size)
+{
+ int width = gdk_pixbuf_get_width (* pixbuf);
+ int height = gdk_pixbuf_get_height (* pixbuf);
+
+ if (width <= size && height <= size)
+ return;
+
+ if (width > height)
+ {
+ height = size * height / width;
+ width = size;
+ }
+ else
+ {
+ width = size * width / height;
+ height = size;
+ }
+
+ if (width < 1)
+ width = 1;
+ if (height < 1)
+ height = 1;
+
+ GdkPixbuf * pixbuf2 = gdk_pixbuf_scale_simple (* pixbuf, width, height, GDK_INTERP_BILINEAR);
+ g_object_unref (* pixbuf);
+ * pixbuf = pixbuf2;
+}
+
+EXPORT GdkPixbuf * audgui_pixbuf_request (const char * filename)
+{
+ const void * data;
+ int64_t size;
+
+ aud_art_request_data (filename, & data, & size);
+ if (! data)
+ return NULL;
+
+ GdkPixbuf * p = audgui_pixbuf_from_data (data, size);
+
+ aud_art_unref (filename);
+ return p;
+}
+
+EXPORT GdkPixbuf * audgui_pixbuf_request_current (void)
+{
+ if (! current_pixbuf)
+ {
+ int list = aud_playlist_get_playing ();
+ int entry = aud_playlist_get_position (list);
+ if (entry < 0)
+ return NULL;
+
+ char * filename = aud_playlist_entry_get_filename (list, entry);
+ current_pixbuf = audgui_pixbuf_request (filename);
+ str_unref (filename);
+ }
+
+ if (current_pixbuf)
+ g_object_ref ((GObject *) current_pixbuf);
+
+ return current_pixbuf;
+}
+
+EXPORT GdkPixbuf * audgui_pixbuf_from_data (const void * data, int64_t size)
+{
+ GdkPixbuf * pixbuf = NULL;
+ GdkPixbufLoader * loader = gdk_pixbuf_loader_new ();
+ GError * error = NULL;
+
+ if (gdk_pixbuf_loader_write (loader, data, size, & error) &&
+ gdk_pixbuf_loader_close (loader, & error))
+ {
+ if ((pixbuf = gdk_pixbuf_loader_get_pixbuf (loader)))
+ g_object_ref (pixbuf);
+ }
+ else
+ {
+ AUDDBG ("error while loading pixbuf: %s\n", error->message);
+ g_error_free (error);
+ }
+
+ g_object_unref (loader);
+ return pixbuf;
+}
diff --git a/src/libaudgui/playlists.c b/src/libaudgui/playlists.c
index 5a7ef1d..fa3fa05 100644
--- a/src/libaudgui/playlists.c
+++ b/src/libaudgui/playlists.c
@@ -1,6 +1,6 @@
/*
* playlists.c
- * Copyright 2011 John Lindgren
+ * Copyright 2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -22,87 +22,163 @@
#include <audacious/i18n.h>
#include <audacious/misc.h>
#include <audacious/playlist.h>
+#include <libaudcore/audstrings.h>
#include <libaudcore/vfs.h>
+#include "init.h"
#include "libaudgui.h"
+#include "libaudgui-gtk.h"
-static char * select_file (bool_t save, const char * default_filename)
+typedef struct
{
- GtkWidget * dialog = gtk_file_chooser_dialog_new (save ?
- _("Export Playlist") : _("Import Playlist"), NULL, save ?
- GTK_FILE_CHOOSER_ACTION_SAVE : GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL);
+ bool_t save;
+ int list_id;
+ char * filename;
+ GtkWidget * selector, * confirm;
+}
+ImportExportJob;
- if (default_filename)
- gtk_file_chooser_set_uri ((GtkFileChooser *) dialog, default_filename);
+/* "destroy" callback; do not call directly */
+static void cleanup_job (void * data)
+{
+ ImportExportJob * job = data;
- gtk_dialog_add_button ((GtkDialog *) dialog, GTK_STOCK_CANCEL,
- GTK_RESPONSE_REJECT);
- gtk_dialog_add_button ((GtkDialog *) dialog, save ? GTK_STOCK_SAVE :
- GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT);
+ char * folder = gtk_file_chooser_get_current_folder_uri ((GtkFileChooser *) job->selector);
- gtk_dialog_set_default_response ((GtkDialog *) dialog, GTK_RESPONSE_ACCEPT);
+ if (folder)
+ aud_set_str ("audgui", "playlist_path", folder);
- char * path = aud_get_string ("audgui", "playlist_path");
- if (path[0])
- gtk_file_chooser_set_current_folder_uri ((GtkFileChooser *) dialog, path);
- g_free (path);
+ g_free (folder);
- char * filename = NULL;
- if (gtk_dialog_run ((GtkDialog *) dialog) == GTK_RESPONSE_ACCEPT)
- filename = gtk_file_chooser_get_uri ((GtkFileChooser *) dialog);
+ if (job->confirm)
+ gtk_widget_destroy (job->confirm);
- path = gtk_file_chooser_get_current_folder_uri ((GtkFileChooser *) dialog);
- aud_set_string ("audgui", "playlist_path", path);
- g_free (path);
+ g_free (job->filename);
+ g_slice_free (ImportExportJob, job);
+}
- gtk_widget_destroy (dialog);
- return filename;
+static void finish_job (void * data)
+{
+ ImportExportJob * job = data;
+ int list = aud_playlist_by_unique_id (job->list_id);
+
+ if (list >= 0)
+ {
+ aud_playlist_set_filename (list, job->filename);
+
+ if (job->save)
+ aud_playlist_save (list, job->filename);
+ else
+ {
+ aud_playlist_entry_delete (list, 0, aud_playlist_entry_count (list));
+ aud_playlist_entry_insert (list, 0, job->filename, NULL, FALSE);
+ }
+ }
+
+ gtk_widget_destroy (job->selector);
}
-static bool_t confirm_overwrite (const char * filename)
+static void confirm_overwrite (ImportExportJob * job)
{
- GtkWidget * dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_QUESTION,
- GTK_BUTTONS_YES_NO, _("Overwrite %s?"), filename);
+ if (job->confirm)
+ gtk_widget_destroy (job->confirm);
+
+ SPRINTF (message, _("Overwrite %s?"), job->filename);
+
+ GtkWidget * button1 = audgui_button_new (_("_Overwrite"), "document-save", finish_job, job);
+ GtkWidget * button2 = audgui_button_new (_("_Cancel"), "process-stop", NULL, NULL);
+
+ job->confirm = audgui_dialog_new (GTK_MESSAGE_QUESTION,
+ _("Confirm Overwrite"), message, button1, button2);
- int result = gtk_dialog_run ((GtkDialog *) dialog);
- gtk_widget_destroy (dialog);
- return (result == GTK_RESPONSE_YES);
+ g_signal_connect (job->confirm, "destroy", (GCallback) gtk_widget_destroyed, & job->confirm);
+
+ gtk_widget_show_all (job->confirm);
}
-EXPORT void audgui_import_playlist (void)
+static void check_overwrite (void * data)
{
- int list = aud_playlist_get_active ();
- int id = aud_playlist_get_unique_id (list);
+ ImportExportJob * job = data;
- char * filename = select_file (FALSE, NULL);
- if (! filename)
- return;
+ job->filename = gtk_file_chooser_get_uri ((GtkFileChooser *) job->selector);
- if ((list = aud_playlist_by_unique_id (id)) < 0)
+ if (! job->filename)
return;
- aud_playlist_entry_delete (list, 0, aud_playlist_entry_count (list));
- aud_playlist_entry_insert (list, 0, filename, NULL, FALSE);
- aud_playlist_set_filename (list, filename);
- g_free (filename);
+ if (job->save && vfs_file_test (job->filename, G_FILE_TEST_EXISTS))
+ confirm_overwrite (job);
+ else
+ finish_job (data);
}
-EXPORT void audgui_export_playlist (void)
+static void create_selector (ImportExportJob * job, const char * filename, const char * folder)
+{
+ const char * title, * verb, * icon;
+ GtkFileChooserAction action;
+
+ if (job->save)
+ {
+ title = _("Export Playlist");
+ verb = _("_Export");
+ icon = "document-save";
+ action = GTK_FILE_CHOOSER_ACTION_SAVE;
+ }
+ else
+ {
+ title = _("Import Playlist");
+ verb = _("_Import");
+ icon = "document-open";
+ action = GTK_FILE_CHOOSER_ACTION_OPEN;
+ }
+
+ job->selector = gtk_file_chooser_dialog_new (title, NULL, action, NULL, NULL);
+
+ if (filename)
+ gtk_file_chooser_set_uri ((GtkFileChooser *) job->selector, filename);
+ else if (folder)
+ gtk_file_chooser_set_current_folder_uri ((GtkFileChooser *) job->selector, folder);
+
+ GtkWidget * button1 = audgui_button_new (verb, icon, check_overwrite, job);
+ GtkWidget * button2 = audgui_button_new (_("_Cancel"), "process-stop",
+ (AudguiCallback) gtk_widget_destroy, job->selector);
+
+ gtk_dialog_add_action_widget ((GtkDialog *) job->selector, button2, GTK_RESPONSE_NONE);
+ gtk_dialog_add_action_widget ((GtkDialog *) job->selector, button1, GTK_RESPONSE_NONE);
+
+ gtk_widget_set_can_default (button1, TRUE);
+ gtk_widget_grab_default (button1);
+
+ g_signal_connect_swapped (job->selector, "destroy", (GCallback) cleanup_job, job);
+
+ gtk_widget_show_all (job->selector);
+}
+
+static GtkWidget * start_job (bool_t save)
{
int list = aud_playlist_get_active ();
- int id = aud_playlist_get_unique_id (list);
- char * def = aud_playlist_get_filename (list);
- char * filename = select_file (TRUE, def);
- str_unref (def);
+ char * filename = aud_playlist_get_filename (list);
+ char * folder = aud_get_str ("audgui", "playlist_path");
- if (! filename || (vfs_file_test (filename, G_FILE_TEST_EXISTS) &&
- ! confirm_overwrite (filename)))
- return;
+ ImportExportJob * job = g_slice_new0 (ImportExportJob);
- if ((list = aud_playlist_by_unique_id (id)) < 0)
- return;
+ job->save = save;
+ job->list_id = aud_playlist_get_unique_id (list);
+
+ create_selector (job, filename, folder[0] ? folder : NULL);
+
+ str_unref (filename);
+ str_unref (folder);
+
+ return job->selector;
+}
- aud_playlist_save (list, filename);
- g_free (filename);
+EXPORT void audgui_import_playlist (void)
+{
+ audgui_show_unique_window (AUDGUI_PLAYLIST_IMPORT_WINDOW, start_job (FALSE));
+}
+
+EXPORT void audgui_export_playlist (void)
+{
+ audgui_show_unique_window (AUDGUI_PLAYLIST_EXPORT_WINDOW, start_job (TRUE));
}
diff --git a/src/libaudgui/queue-manager.c b/src/libaudgui/queue-manager.c
index 60e66c6..8718365 100644
--- a/src/libaudgui/queue-manager.c
+++ b/src/libaudgui/queue-manager.c
@@ -1,6 +1,6 @@
/*
* queue-manager.c
- * Copyright 2011 John Lindgren
+ * Copyright 2011-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -26,17 +26,13 @@
#include "init.h"
#include "libaudgui.h"
+#include "libaudgui-gtk.h"
#include "list.h"
enum {
COLUMN_ENTRY,
COLUMN_TITLE};
-#define RESPONSE_REMOVE 1
-
-static GtkWidget * qm_win;
-static GtkWidget * qm_list;
-
static void get_value (void * user, int row, int column, GValue * value)
{
int list = aud_playlist_get_active ();
@@ -110,7 +106,7 @@ static const AudguiListCallbacks callbacks = {
.select_all = select_all,
.shift_rows = shift_rows};
-static void remove_selected (void)
+static void remove_selected (void * unused)
{
int list = aud_playlist_get_active ();
int count = aud_playlist_queue_count (list);
@@ -132,6 +128,8 @@ static void remove_selected (void)
static void update_hook (void * data, void * user)
{
+ GtkWidget * qm_list = user;
+
int oldrows = audgui_list_row_count (qm_list);
int newrows = aud_playlist_queue_count (aud_playlist_get_active ());
int focus = audgui_list_get_focus (qm_list);
@@ -152,9 +150,6 @@ static void destroy_cb (void)
{
hook_dissociate ("playlist activate", update_hook);
hook_dissociate ("playlist update", update_hook);
-
- qm_win = NULL;
- qm_list = NULL;
}
static bool_t keypress_cb (GtkWidget * widget, GdkEventKey * event)
@@ -162,39 +157,19 @@ static bool_t keypress_cb (GtkWidget * widget, GdkEventKey * event)
if (event->keyval == GDK_KEY_A && (event->state & GDK_CONTROL_MASK))
select_all (NULL, TRUE);
else if (event->keyval == GDK_KEY_Delete)
- remove_selected ();
+ remove_selected (NULL);
else if (event->keyval == GDK_KEY_Escape)
- gtk_widget_destroy (qm_win);
+ gtk_widget_destroy (widget);
else
return FALSE;
return TRUE;
}
-static void response_cb (GtkDialog * dialog, int response)
-{
- switch (response)
- {
- case RESPONSE_REMOVE:;
- remove_selected ();
- break;
- case GTK_RESPONSE_CLOSE:
- gtk_widget_destroy (qm_win);
- break;
- }
-}
-
-EXPORT void audgui_queue_manager_show (void)
+static GtkWidget * create_queue_manager (void)
{
- if (qm_win)
- {
- gtk_window_present ((GtkWindow *) qm_win);
- return;
- }
-
- qm_win = gtk_dialog_new_with_buttons (_("Queue Manager"), NULL, 0,
- GTK_STOCK_REMOVE, RESPONSE_REMOVE, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
- NULL);
+ GtkWidget * qm_win = gtk_dialog_new ();
+ gtk_window_set_title ((GtkWindow *) qm_win, _("Queue Manager"));
gtk_window_set_default_size ((GtkWindow *) qm_win, 400, 250);
GtkWidget * vbox = gtk_dialog_get_content_area ((GtkDialog *) qm_win);
@@ -204,24 +179,30 @@ EXPORT void audgui_queue_manager_show (void)
gtk_box_pack_start ((GtkBox *) vbox, scrolled, TRUE, TRUE, 0);
int count = aud_playlist_queue_count (aud_playlist_get_active ());
- qm_list = audgui_list_new (& callbacks, NULL, count);
+ GtkWidget * qm_list = audgui_list_new (& callbacks, NULL, count);
gtk_tree_view_set_headers_visible ((GtkTreeView *) qm_list, FALSE);
audgui_list_add_column (qm_list, NULL, 0, G_TYPE_INT, 7);
audgui_list_add_column (qm_list, NULL, 1, G_TYPE_STRING, -1);
gtk_container_add ((GtkContainer *) scrolled, qm_list);
- hook_associate ("playlist activate", update_hook, NULL);
- hook_associate ("playlist update", update_hook, NULL);
+ GtkWidget * button1 = audgui_button_new (_("_Unqueue"), "list-remove", remove_selected, NULL);
+ GtkWidget * button2 = audgui_button_new (_("_Close"), "window-close",
+ (AudguiCallback) gtk_widget_destroy, qm_win);
+
+ gtk_dialog_add_action_widget ((GtkDialog *) qm_win, button1, GTK_RESPONSE_NONE);
+ gtk_dialog_add_action_widget ((GtkDialog *) qm_win, button2, GTK_RESPONSE_NONE);
+
+ hook_associate ("playlist activate", update_hook, qm_list);
+ hook_associate ("playlist update", update_hook, qm_list);
g_signal_connect (qm_win, "destroy", (GCallback) destroy_cb, NULL);
g_signal_connect (qm_win, "key-press-event", (GCallback) keypress_cb, NULL);
- g_signal_connect (qm_win, "response", (GCallback) response_cb, NULL);
- gtk_widget_show_all (qm_win);
+ return qm_win;
}
-void audgui_queue_manager_cleanup (void)
+EXPORT void audgui_queue_manager_show (void)
{
- if (qm_win)
- gtk_widget_destroy (qm_win);
+ if (! audgui_reshow_unique_window (AUDGUI_QUEUE_MANAGER_WINDOW))
+ audgui_show_unique_window (AUDGUI_QUEUE_MANAGER_WINDOW, create_queue_manager ());
}
diff --git a/src/libaudgui/scaled-image.c b/src/libaudgui/scaled-image.c
new file mode 100644
index 0000000..70adcce
--- /dev/null
+++ b/src/libaudgui/scaled-image.c
@@ -0,0 +1,114 @@
+/*
+ * scaled-image.c
+ * Copyright 2012-2013 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 <libaudgui/libaudgui-gtk.h>
+
+static GdkPixbuf * get_scaled (GtkWidget * widget, int maxwidth, int maxheight)
+{
+ GdkPixbuf * unscaled = g_object_get_data ((GObject *) widget, "pixbuf-unscaled");
+
+ if (! unscaled)
+ return NULL;
+
+ int width = gdk_pixbuf_get_width (unscaled);
+ int height = gdk_pixbuf_get_height (unscaled);
+
+ if (width > maxwidth || height > maxheight)
+ {
+ if (width * maxheight > height * maxwidth)
+ {
+ height = height * maxwidth / width;
+ width = maxwidth;
+ }
+ else
+ {
+ width = width * maxheight / height;
+ height = maxheight;
+ }
+ }
+
+ GdkPixbuf * scaled = g_object_get_data ((GObject *) widget, "pixbuf-scaled");
+
+ if (scaled)
+ {
+ if (gdk_pixbuf_get_width (scaled) == width && gdk_pixbuf_get_height (scaled) == height)
+ return scaled;
+
+ g_object_unref (scaled);
+ }
+
+ scaled = gdk_pixbuf_scale_simple (unscaled, width, height, GDK_INTERP_BILINEAR);
+ g_object_set_data ((GObject *) widget, "pixbuf-scaled", scaled);
+
+ return scaled;
+}
+
+static bool_t draw_cb (GtkWidget * widget, cairo_t * cr)
+{
+ GdkRectangle rect;
+ gtk_widget_get_allocation (widget, & rect);
+
+ GdkPixbuf * scaled = get_scaled (widget, rect.width, rect.height);
+
+ if (scaled)
+ {
+ int x = (rect.width - gdk_pixbuf_get_width (scaled)) / 2;
+ int y = (rect.height - gdk_pixbuf_get_height (scaled)) / 2;
+ gdk_cairo_set_source_pixbuf (cr, scaled, x, y);
+ cairo_paint (cr);
+ }
+
+ return TRUE;
+}
+
+EXPORT void audgui_scaled_image_set (GtkWidget * widget, GdkPixbuf * pixbuf)
+{
+ GdkPixbuf * old;
+
+ if ((old = g_object_get_data ((GObject *) widget, "pixbuf-unscaled")))
+ g_object_unref (old);
+ if ((old = g_object_get_data ((GObject *) widget, "pixbuf-scaled")))
+ g_object_unref (old);
+
+ if (pixbuf)
+ g_object_ref (pixbuf);
+
+ g_object_set_data ((GObject *) widget, "pixbuf-unscaled", pixbuf);
+ g_object_set_data ((GObject *) widget, "pixbuf-scaled", NULL);
+
+ if (! gtk_widget_in_destruction (widget))
+ gtk_widget_queue_draw (widget);
+}
+
+static void destroy_cb (GtkWidget * widget)
+{
+ audgui_scaled_image_set (widget, NULL);
+}
+
+EXPORT GtkWidget * audgui_scaled_image_new (GdkPixbuf * pixbuf)
+{
+ GtkWidget * widget = gtk_drawing_area_new ();
+
+ g_signal_connect (widget, "draw", (GCallback) draw_cb, NULL);
+ g_signal_connect (widget, "destroy", (GCallback) destroy_cb, NULL);
+
+ audgui_scaled_image_set (widget, pixbuf);
+
+ return widget;
+}
diff --git a/src/libaudgui/ui_fileopener.c b/src/libaudgui/ui_fileopener.c
index eb1eea0..ae0ad1f 100644
--- a/src/libaudgui/ui_fileopener.c
+++ b/src/libaudgui/ui_fileopener.c
@@ -1,6 +1,6 @@
/*
* ui_fileopener.c
- * Copyright 2007-2011 Michael FĂ€rber and John Lindgren
+ * Copyright 2007-2013 Michael FĂ€rber and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -23,175 +23,133 @@
#include <audacious/drct.h>
#include <audacious/misc.h>
+#include "init.h"
#include "libaudgui.h"
#include "libaudgui-gtk.h"
-static void filebrowser_add_files (GtkFileChooser * browser, GSList * files,
- bool_t play)
+static Index * get_files (GtkWidget * chooser)
{
- Index * list = index_new ();
+ Index * index = index_new ();
+ GSList * list = gtk_file_chooser_get_uris ((GtkFileChooser *) chooser);
- for (GSList * node = files; node; node = node->next)
- index_append (list, str_get (node->data));
+ for (GSList * node = list; node; node = node->next)
+ index_insert (index, -1, str_get (node->data));
- if (play)
- aud_drct_pl_open_list (list);
- else
- aud_drct_pl_add_list (list, -1);
-
- char * path = gtk_file_chooser_get_current_folder (browser);
- if (path)
- {
- aud_set_string ("audgui", "filesel_path", path);
- g_free (path);
- }
+ g_slist_free_full (list, g_free);
+ return index;
}
-static void
-action_button_cb(GtkWidget *widget, gpointer data)
+static void open_cb (void * data)
{
- GtkWidget *window = g_object_get_data(data, "window");
- GtkWidget *chooser = g_object_get_data(data, "chooser");
- GtkWidget *toggle = g_object_get_data(data, "toggle-button");
-
- GSList * files = gtk_file_chooser_get_uris ((GtkFileChooser *) chooser);
-
- bool_t play = GPOINTER_TO_INT (g_object_get_data (data, "play-button"));
- filebrowser_add_files ((GtkFileChooser *) chooser, files, play);
+ GtkWidget * chooser = data;
+ Index * files = get_files (chooser);
+ bool_t open = GPOINTER_TO_INT (g_object_get_data ((GObject *) chooser, "do-open"));
- g_slist_foreach(files, (GFunc) g_free, NULL);
- g_slist_free(files);
+ if (open)
+ aud_drct_pl_open_list (files);
+ else
+ aud_drct_pl_add_list (files, -1);
+ GtkWidget * toggle = g_object_get_data ((GObject *) chooser, "toggle-button");
if (gtk_toggle_button_get_active ((GtkToggleButton *) toggle))
- gtk_widget_destroy (window);
+ audgui_hide_filebrowser ();
}
-static void toggled_cb (GtkToggleButton * toggle, void * option)
+static void destroy_cb (GtkWidget * chooser)
{
- aud_set_bool ("audgui", (const char *) option, gtk_toggle_button_get_active (toggle));
+ char * path = gtk_file_chooser_get_current_folder ((GtkFileChooser *) chooser);
+ if (path)
+ {
+ aud_set_str ("audgui", "filesel_path", path);
+ g_free (path);
+ }
}
-static void
-close_button_cb(GtkWidget *widget, gpointer data)
+static void toggled_cb (GtkToggleButton * toggle, void * option)
{
- gtk_widget_destroy(GTK_WIDGET(data));
+ aud_set_bool ("audgui", (const char *) option, gtk_toggle_button_get_active (toggle));
}
-static void
-run_filebrowser_gtk2style(bool_t play_button, bool_t show)
+static GtkWidget * create_filebrowser (bool_t open)
{
- static GtkWidget *window = NULL;
- GtkWidget *vbox, *hbox, *bbox;
- GtkWidget *chooser;
- GtkWidget *action_button, *close_button;
- GtkWidget *toggle;
- char *window_title, *toggle_text;
- gpointer action_stock, storage;
-
- if (!show) {
- if (window){
- gtk_widget_hide(window);
- return;
- }
- else
- return;
+ const char * window_title, * verb, * icon, * toggle_text, * option;
+
+ if (open)
+ {
+ window_title = _("Open Files");
+ verb = _("_Open");
+ icon = "document-open";
+ toggle_text = _("Close _dialog on open");
+ option = "close_dialog_open";
}
- else {
- if (window) {
- gtk_window_present(GTK_WINDOW(window)); /* raise filebrowser */
- return;
- }
+ else
+ {
+ window_title = _("Add Files");
+ verb = _("_Add");
+ icon = "list-add";
+ toggle_text = _("Close _dialog on add");
+ option = "close_dialog_add";
}
- window_title = play_button ? _("Open Files") : _("Add Files");
- toggle_text = play_button ?
- _("Close dialog on Open") : _("Close dialog on Add");
- action_stock = play_button ? GTK_STOCK_OPEN : GTK_STOCK_ADD;
-
- window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ GtkWidget * window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DIALOG);
gtk_window_set_title(GTK_WINDOW(window), window_title);
gtk_window_set_default_size(GTK_WINDOW(window), 700, 450);
- gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
- vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
- chooser = gtk_file_chooser_widget_new(GTK_FILE_CHOOSER_ACTION_OPEN);
+ GtkWidget * chooser = gtk_file_chooser_widget_new(GTK_FILE_CHOOSER_ACTION_OPEN);
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chooser), TRUE);
- char * path = aud_get_string ("audgui", "filesel_path");
+ char * path = aud_get_str ("audgui", "filesel_path");
if (path[0])
gtk_file_chooser_set_current_folder ((GtkFileChooser *) chooser, path);
- g_free (path);
+ str_unref (path);
gtk_box_pack_start(GTK_BOX(vbox), chooser, TRUE, TRUE, 3);
- hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
- const char * option = play_button ? "close_dialog_open" : "close_dialog_add";
- toggle = gtk_check_button_new_with_label(toggle_text);
- gtk_toggle_button_set_active ((GtkToggleButton *) toggle, aud_get_bool
- ("audgui", option));
+ GtkWidget * toggle = gtk_check_button_new_with_mnemonic (toggle_text);
+ gtk_toggle_button_set_active ((GtkToggleButton *) toggle, aud_get_bool ("audgui", option));
g_signal_connect (toggle, "toggled", (GCallback) toggled_cb, (void *) option);
gtk_box_pack_start(GTK_BOX(hbox), toggle, TRUE, TRUE, 3);
- bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+ GtkWidget * bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
gtk_box_set_spacing(GTK_BOX(bbox), 6);
gtk_box_pack_end(GTK_BOX(hbox), bbox, TRUE, TRUE, 3);
- close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
- action_button = gtk_button_new_from_stock(action_stock);
+ GtkWidget * action_button = audgui_button_new (verb, icon, open_cb, chooser);
+ GtkWidget * close_button = audgui_button_new (_("_Close"), "window-close",
+ (AudguiCallback) audgui_hide_filebrowser, NULL);
+
gtk_container_add(GTK_CONTAINER(bbox), close_button);
gtk_container_add(GTK_CONTAINER(bbox), action_button);
gtk_widget_set_can_default (action_button, TRUE);
gtk_widget_grab_default (action_button);
- /* this storage object holds several other objects which are used in the
- * callback functions
- */
- storage = g_object_new(G_TYPE_OBJECT, NULL);
- g_object_set_data(storage, "window", window);
- g_object_set_data(storage, "chooser", chooser);
- g_object_set_data(storage, "toggle-button", toggle);
- g_object_set_data(storage, "play-button", GINT_TO_POINTER(play_button));
-
- g_signal_connect(chooser, "file-activated",
- G_CALLBACK(action_button_cb), storage);
- g_signal_connect(action_button, "clicked",
- G_CALLBACK(action_button_cb), storage);
- g_signal_connect(close_button, "clicked",
- G_CALLBACK(close_button_cb), window);
- g_signal_connect(window, "destroy",
- G_CALLBACK(gtk_widget_destroyed), &window);
+ g_object_set_data ((GObject *) chooser, "toggle-button", toggle);
+ g_object_set_data ((GObject *) chooser, "do-open", GINT_TO_POINTER (open));
+
+ g_signal_connect (chooser, "file-activated", (GCallback) open_cb, NULL);
+ g_signal_connect (chooser, "destroy", (GCallback) destroy_cb, NULL);
audgui_destroy_on_escape (window);
- gtk_widget_show_all (window);
+
+ return window;
}
-/*
- * run_filebrowser(bool_t play_button)
- *
- * Inputs:
- * - bool_t play_button
- * TRUE - open files
- * FALSE - add files
- *
- * Outputs:
- * - none
- */
-EXPORT void
-audgui_run_filebrowser(bool_t play_button)
+EXPORT void audgui_run_filebrowser (bool_t open)
{
- run_filebrowser_gtk2style(play_button, TRUE);
+ audgui_show_unique_window (AUDGUI_FILEBROWSER_WINDOW, create_filebrowser (open));
}
-EXPORT void
-audgui_hide_filebrowser(void)
+EXPORT void audgui_hide_filebrowser (void)
{
- run_filebrowser_gtk2style(FALSE, FALSE);
+ audgui_hide_unique_window (AUDGUI_FILEBROWSER_WINDOW);
}
diff --git a/src/libaudgui/ui_jumptotrack.c b/src/libaudgui/ui_jumptotrack.c
index 8898843..83addd4 100644
--- a/src/libaudgui/ui_jumptotrack.c
+++ b/src/libaudgui/ui_jumptotrack.c
@@ -1,6 +1,6 @@
/*
* ui_jumptotrack.c
- * Copyright 2007-2011 Yoshiki Yazawa and John Lindgren
+ * Copyright 2007-2012 Yoshiki Yazawa and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -26,21 +26,21 @@
#include <audacious/playlist.h>
#include <libaudcore/hook.h>
+#include "init.h"
#include "libaudgui.h"
+#include "libaudgui-gtk.h"
#include "list.h"
#include "ui_jumptotrack_cache.h"
static void update_cb (void * data, void * user);
static void activate_cb (void * data, void * user);
-static GtkWidget *jump_to_track_win = NULL;
static JumpToTrackCache* cache = NULL;
static const GArray * search_matches;
static GtkWidget * treeview, * filter_entry, * queue_button;
static bool_t watching = FALSE;
-EXPORT void
-audgui_jump_to_track_hide(void)
+static void destroy_cb (void)
{
if (watching)
{
@@ -49,9 +49,6 @@ audgui_jump_to_track_hide(void)
watching = FALSE;
}
- if (jump_to_track_win != NULL)
- gtk_widget_hide (jump_to_track_win);
-
if (cache != NULL)
{
ui_jump_to_track_cache_free (cache);
@@ -80,7 +77,7 @@ static int get_selected_entry (void)
return g_array_index (search_matches, int, row);
}
-static void do_jump (void)
+static void do_jump (void * unused)
{
int entry = get_selected_entry ();
if (entry < 0)
@@ -115,7 +112,7 @@ static void update_queue_button (int entry)
}
}
-static void do_queue (void)
+static void do_queue (void * unused)
{
int playlist = aud_playlist_get_active ();
int entry = get_selected_entry ();
@@ -169,13 +166,6 @@ static void fill_list (void)
}
}
-static void clear_cb (GtkWidget * widget)
-{
- g_return_if_fail (filter_entry);
- gtk_entry_set_text ((GtkEntry *) filter_entry, "");
- gtk_widget_grab_focus (filter_entry);
-}
-
static void update_cb (void * data, void * user)
{
g_return_if_fail (treeview);
@@ -220,12 +210,6 @@ static void toggle_button_cb (GtkToggleButton * toggle, const char * setting)
aud_set_bool ("audgui", setting, gtk_toggle_button_get_active (toggle));
}
-static bool_t delete_cb (void)
-{
- audgui_jump_to_track_hide ();
- return TRUE;
-}
-
static void list_get_value (void * user, int row, int column, GValue * value)
{
g_return_if_fail (search_matches);
@@ -252,16 +236,16 @@ static void list_get_value (void * user, int row, int column, GValue * value)
static const AudguiListCallbacks callbacks = {
.get_value = list_get_value};
-static void create_window (void)
+static GtkWidget * create_window (void)
{
- jump_to_track_win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ GtkWidget * jump_to_track_win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_type_hint(GTK_WINDOW(jump_to_track_win),
GDK_WINDOW_TYPE_HINT_DIALOG);
gtk_window_set_title(GTK_WINDOW(jump_to_track_win), _("Jump to Song"));
g_signal_connect (jump_to_track_win, "key_press_event", (GCallback) keypress_cb, NULL);
- g_signal_connect (jump_to_track_win, "delete-event", (GCallback) delete_cb, NULL);
+ g_signal_connect (jump_to_track_win, "destroy", (GCallback) destroy_cb, NULL);
gtk_container_set_border_width(GTK_CONTAINER(jump_to_track_win), 10);
gtk_window_set_default_size(GTK_WINDOW(jump_to_track_win), 600, 500);
@@ -279,7 +263,7 @@ static void create_window (void)
"changed", (GCallback) selection_changed, NULL);
g_signal_connect (treeview, "row-activated", (GCallback) do_jump, NULL);
- GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
+ GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
/* filter box */
@@ -291,21 +275,7 @@ static void create_window (void)
gtk_label_set_mnemonic_widget ((GtkLabel *) search_label, filter_entry);
g_signal_connect (filter_entry, "changed", (GCallback) fill_list, NULL);
gtk_entry_set_activates_default ((GtkEntry *) filter_entry, TRUE);
- gtk_box_pack_start ((GtkBox *) hbox, filter_entry, TRUE, TRUE, 3);
-
- /* remember text entry */
- GtkWidget * toggle2 = gtk_check_button_new_with_label (_("Remember"));
- gtk_toggle_button_set_active ((GtkToggleButton *) toggle2, aud_get_bool
- ("audgui", "remember_jtf_entry"));
- gtk_box_pack_start(GTK_BOX(hbox), toggle2, FALSE, FALSE, 0);
- g_signal_connect (toggle2, "clicked", (GCallback) toggle_button_cb, "remember_jtf_entry");
-
- /* clear button */
- GtkWidget * rescan = gtk_button_new_with_mnemonic (_("Clea_r"));
- gtk_button_set_image ((GtkButton *) rescan, gtk_image_new_from_stock
- (GTK_STOCK_CLEAR, GTK_ICON_SIZE_BUTTON));
- gtk_box_pack_start(GTK_BOX(hbox), rescan, FALSE, FALSE, 0);
- g_signal_connect (rescan, "clicked", (GCallback) clear_cb, NULL);
+ gtk_box_pack_start ((GtkBox *) hbox, filter_entry, TRUE, TRUE, 0);
GtkWidget * scrollwin = gtk_scrolled_window_new (NULL, NULL);
gtk_container_add(GTK_CONTAINER(scrollwin), treeview);
@@ -321,49 +291,37 @@ static void create_window (void)
gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
/* close dialog toggle */
- GtkWidget * toggle = gtk_check_button_new_with_label(_("Close on Jump"));
+ GtkWidget * toggle = gtk_check_button_new_with_mnemonic (_("C_lose on jump"));
gtk_toggle_button_set_active ((GtkToggleButton *) toggle, aud_get_bool
("audgui", "close_jtf_dialog"));
gtk_box_pack_start(GTK_BOX(bbox), toggle, FALSE, FALSE, 0);
g_signal_connect (toggle, "clicked", (GCallback) toggle_button_cb, "close_jtf_dialog");
/* queue button */
- queue_button = gtk_button_new_with_mnemonic(_("_Queue"));
- gtk_button_set_image ((GtkButton *) queue_button, gtk_image_new_from_stock
- (AUD_STOCK_QUEUETOGGLE, GTK_ICON_SIZE_BUTTON));
+ queue_button = audgui_button_new (_("_Queue"), NULL, do_queue, NULL);
gtk_box_pack_start ((GtkBox *) bbox, queue_button, FALSE, FALSE, 0);
- g_signal_connect (queue_button, "clicked", (GCallback) do_queue, NULL);
-
/* jump button */
- GtkWidget * jump = gtk_button_new_from_stock (GTK_STOCK_JUMP_TO);
+ GtkWidget * jump = audgui_button_new (_("_Jump"), "go-jump", do_jump, NULL);
gtk_box_pack_start(GTK_BOX(bbox), jump, FALSE, FALSE, 0);
- g_signal_connect (jump, "clicked", (GCallback) do_jump, NULL);
-
gtk_widget_set_can_default(jump, TRUE);
gtk_widget_grab_default(jump);
/* close button */
- GtkWidget * close = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
+ GtkWidget * close = audgui_button_new (_("_Close"), "window-close",
+ (AudguiCallback) audgui_jump_to_track_hide, NULL);
gtk_box_pack_start(GTK_BOX(bbox), close, FALSE, FALSE, 0);
- g_signal_connect (close, "clicked", (GCallback) audgui_jump_to_track_hide,
- NULL);
- gtk_widget_set_can_default(close, TRUE);
+
+ return jump_to_track_win;
}
EXPORT void audgui_jump_to_track (void)
{
- bool_t create = (! jump_to_track_win);
- if (create)
- create_window ();
+ if (audgui_reshow_unique_window (AUDGUI_JUMP_TO_TRACK_WINDOW))
+ return;
- g_return_if_fail (filter_entry);
-
- if (aud_get_bool ("audgui", "remember_jtf_entry"))
- gtk_editable_select_region ((GtkEditable *) filter_entry, 0, -1);
- else
- gtk_entry_set_text ((GtkEntry *) filter_entry, "");
+ GtkWidget * jump_to_track_win = create_window ();
if (! watching)
{
@@ -373,10 +331,12 @@ EXPORT void audgui_jump_to_track (void)
watching = TRUE;
}
- if (create)
- gtk_widget_show_all (jump_to_track_win);
- else
- gtk_window_present ((GtkWindow *) jump_to_track_win);
-
gtk_widget_grab_focus (filter_entry);
+
+ audgui_show_unique_window (AUDGUI_JUMP_TO_TRACK_WINDOW, jump_to_track_win);
+}
+
+EXPORT void audgui_jump_to_track_hide (void)
+{
+ audgui_hide_unique_window (AUDGUI_JUMP_TO_TRACK_WINDOW);
}
diff --git a/src/libaudgui/ui_jumptotrack_cache.c b/src/libaudgui/ui_jumptotrack_cache.c
index f9aa160..21d4370 100644
--- a/src/libaudgui/ui_jumptotrack_cache.c
+++ b/src/libaudgui/ui_jumptotrack_cache.c
@@ -1,6 +1,6 @@
/*
* ui_jumptotrack_cache.c
- * Copyright 2008-2011 Jussi Judin and John Lindgren
+ * Copyright 2008-2012 Jussi Judin 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,29 +22,25 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
-#ifdef HAVE_SYS_TYPES_H
-# include "sys/types.h"
-#endif
#include <audacious/debug.h>
#include <audacious/playlist.h>
#include <libaudcore/audstrings.h>
#include "ui_jumptotrack_cache.h"
-#include "ui_regex.h"
// Struct to keep information about matches from searches.
typedef struct
{
GArray * entries; // int
- GArray * titles, * artists, * albums, * paths; // char *
+ GArray * titles, * artists, * albums, * paths; // char * (pooled)
} KeywordMatches;
static void ui_jump_to_track_cache_init (JumpToTrackCache * cache);
static KeywordMatches * keyword_matches_new (void)
{
- KeywordMatches * k = g_malloc (sizeof (KeywordMatches));
+ KeywordMatches * k = g_slice_new (KeywordMatches);
k->entries = g_array_new (FALSE, FALSE, sizeof (int));
k->titles = g_array_new (FALSE, FALSE, sizeof (char *));
k->artists = g_array_new (FALSE, FALSE, sizeof (char *));
@@ -60,7 +56,7 @@ static void keyword_matches_free (KeywordMatches * k)
g_array_free (k->artists, TRUE);
g_array_free (k->albums, TRUE);
g_array_free (k->paths, TRUE);
- g_free (k);
+ g_slice_free (KeywordMatches, k);
}
/**
@@ -89,15 +85,10 @@ ui_jump_to_track_cache_regex_list_create(const GString* keyword)
if (words[i][0] == 0) {
continue;
}
- regex_t *regex = g_malloc(sizeof(regex_t));
- #if defined(USE_REGEX_PCRE)
- if ( regcomp( regex , words[i] , REG_NOSUB | REG_UTF8 ) == 0 )
- #else
- if ( regcomp( regex , words[i] , REG_NOSUB ) == 0 )
- #endif
- regex_list = g_slist_append( regex_list , regex );
- else
- g_free( regex );
+
+ GRegex * regex = g_regex_new (words[i], G_REGEX_CASELESS, 0, NULL);
+ if (regex)
+ regex_list = g_slist_append (regex_list, regex);
}
g_strfreev(words);
@@ -106,26 +97,6 @@ ui_jump_to_track_cache_regex_list_create(const GString* keyword)
}
/**
- * Frees the regular expression list used in searches.
- */
-static void
-ui_jump_to_track_cache_regex_list_free(GSList* regex_list)
-{
- if ( regex_list != NULL )
- {
- GSList* regex_list_tmp = regex_list;
- while ( regex_list != NULL )
- {
- regex_t *regex = regex_list->data;
- regfree( regex );
- g_free( regex );
- regex_list = g_slist_next(regex_list);
- }
- g_slist_free( regex_list_tmp );
- }
-}
-
-/**
* Checks if 'song' matches all regular expressions in 'regex_list'.
*/
static bool_t
@@ -136,8 +107,8 @@ ui_jump_to_track_match(const char * song, GSList *regex_list)
for ( ; regex_list ; regex_list = g_slist_next(regex_list) )
{
- regex_t *regex = regex_list->data;
- if ( regexec( regex , song , 0 , NULL , 0 ) != 0 )
+ GRegex * regex = regex_list->data;
+ if (! g_regex_match (regex, song, 0, NULL))
return FALSE;
}
@@ -190,30 +161,9 @@ ui_jump_to_track_cache_match_keyword(JumpToTrackCache* cache,
g_hash_table_insert (cache->keywords, GINT_TO_POINTER (g_string_hash (keyword)), k);
- ui_jump_to_track_cache_regex_list_free(regex_list);
- return k->entries;
-}
-
-/* calls str_unref() on <string> */
-/* returned string must be freed */
-static char * process_string (char * string, bool_t decode)
-{
- if (! string)
- return NULL;
-
- char * normal;
+ g_slist_free_full (regex_list, (GDestroyNotify) g_regex_unref);
- if (decode)
- {
- char temp[strlen (string) + 1];
- str_decode_percent (string, -1, temp);
- normal = g_utf8_casefold (temp, -1);
- }
- else
- normal = g_utf8_casefold (string, -1);
-
- str_unref (string);
- return normal;
+ return k->entries;
}
/**
@@ -224,10 +174,10 @@ ui_jump_to_track_cache_free_keywordmatch_data(KeywordMatches* match_entry)
{
for (int i = 0; i < match_entry->entries->len; i ++)
{
- g_free (g_array_index (match_entry->titles, char *, i));
- g_free (g_array_index (match_entry->artists, char *, i));
- g_free (g_array_index (match_entry->albums, char *, i));
- g_free (g_array_index (match_entry->paths, char *, i));
+ str_unref (g_array_index (match_entry->titles, char *, i));
+ str_unref (g_array_index (match_entry->artists, char *, i));
+ str_unref (g_array_index (match_entry->albums, char *, i));
+ str_unref (g_array_index (match_entry->paths, char *, i));
}
}
@@ -239,7 +189,7 @@ ui_jump_to_track_cache_free_keywordmatch_data(KeywordMatches* match_entry)
JumpToTrackCache*
ui_jump_to_track_cache_new()
{
- JumpToTrackCache* cache = g_new(JumpToTrackCache, 1);
+ JumpToTrackCache * cache = g_slice_new (JumpToTrackCache);
cache->keywords = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) keyword_matches_free);
ui_jump_to_track_cache_init (cache);
@@ -287,20 +237,18 @@ static void ui_jump_to_track_cache_init (JumpToTrackCache * cache)
for (int entry = 0; entry < entries; entry ++)
{
- char * title, * artist, * album, * path;
+ char * title, * artist, * album;
aud_playlist_entry_describe (playlist, entry, & title, & artist, & album, TRUE);
- path = aud_playlist_entry_get_filename (playlist, entry);
- title = process_string (title, FALSE);
- artist = process_string (artist, FALSE);
- album = process_string (album, FALSE);
- path = process_string (path, TRUE);
+ char * uri = aud_playlist_entry_get_filename (playlist, entry);
+ char * decoded = uri_to_display (uri);
+ str_unref (uri);
g_array_append_val (k->entries, entry);
g_array_append_val (k->titles, title);
g_array_append_val (k->artists, artist);
g_array_append_val (k->albums, album);
- g_array_append_val (k->paths, path);
+ g_array_append_val (k->paths, decoded);
}
// Finally insert all titles into cache into an empty key "" so that
@@ -363,9 +311,8 @@ static void ui_jump_to_track_cache_init (JumpToTrackCache * cache)
const GArray * ui_jump_to_track_cache_search (JumpToTrackCache * cache, const
char * keyword)
{
- char * normalized_keyword = g_utf8_casefold (keyword, -1);
- GString* keyword_string = g_string_new(normalized_keyword);
- GString* match_string = g_string_new(normalized_keyword);
+ GString* keyword_string = g_string_new(keyword);
+ GString* match_string = g_string_new(keyword);
int match_string_length = keyword_string->len;
while (match_string_length >= 0)
@@ -381,7 +328,6 @@ const GArray * ui_jump_to_track_cache_search (JumpToTrackCache * cache, const
if (match_string_length == keyword_string->len) {
g_string_free(keyword_string, TRUE);
g_string_free(match_string, TRUE);
- g_free(normalized_keyword);
return matched_entries->entries;
}
@@ -392,7 +338,6 @@ const GArray * ui_jump_to_track_cache_search (JumpToTrackCache * cache, const
keyword_string);
g_string_free(keyword_string, TRUE);
g_string_free(match_string, TRUE);
- g_free(normalized_keyword);
return result;
}
match_string_length--;
@@ -409,5 +354,5 @@ void ui_jump_to_track_cache_free (JumpToTrackCache * cache)
{
ui_jump_to_track_cache_clear (cache);
g_hash_table_unref (cache->keywords);
- g_free (cache);
+ g_slice_free (JumpToTrackCache, cache);
}
diff --git a/src/libaudgui/ui_playlist_manager.c b/src/libaudgui/ui_playlist_manager.c
index e5de3a2..7e42947 100644
--- a/src/libaudgui/ui_playlist_manager.c
+++ b/src/libaudgui/ui_playlist_manager.c
@@ -1,6 +1,6 @@
/*
* ui_playlist_manager.c
- * Copyright 2006-2011 Giacomo Lozito and John Lindgren
+ * Copyright 2006-2012 Giacomo Lozito, John Lindgren, and Thomas Lange
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -24,6 +24,7 @@
#include <audacious/misc.h>
#include <audacious/drct.h>
#include <audacious/playlist.h>
+#include <libaudcore/audstrings.h>
#include <libaudcore/hook.h>
#include "init.h"
@@ -31,55 +32,29 @@
#include "libaudgui-gtk.h"
#include "list.h"
-static GtkWidget * playman_win = NULL;
static void activate_row (void * user, int row);
-static void save_position (GtkWidget * window)
-{
- int x, y, w, h;
- gtk_window_get_position ((GtkWindow *) window, & x, & y);
- gtk_window_get_size ((GtkWindow *) window, & w, & h);
-
- aud_set_int ("audgui", "playlist_manager_x", x);
- aud_set_int ("audgui", "playlist_manager_y", y);
- aud_set_int ("audgui", "playlist_manager_w", w);
- aud_set_int ("audgui", "playlist_manager_h", h);
-}
-
-static bool_t hide_cb (GtkWidget * window)
-{
- save_position (window);
- gtk_widget_hide (window);
- return TRUE;
-}
-
-static void play_cb (void)
+static void play_cb (void * unused)
{
activate_row (NULL, aud_playlist_get_active ());
}
-static void rename_cb (void)
+static void rename_cb (void * unused)
{
audgui_show_playlist_rename (aud_playlist_get_active ());
}
-static void new_cb (void)
+static void new_cb (void * unused)
{
aud_playlist_insert (aud_playlist_get_active () + 1);
aud_playlist_set_active (aud_playlist_get_active () + 1);
}
-static void delete_cb (void)
+static void delete_cb (void * unused)
{
audgui_confirm_playlist_delete (aud_playlist_get_active ());
}
-static void save_config_cb (void * hook_data, void * user_data)
-{
- if (gtk_widget_get_visible ((GtkWidget *) user_data))
- save_position ((GtkWidget *) user_data);
-}
-
static void get_value (void * user, int row, int column, GValue * value)
{
switch (column)
@@ -116,7 +91,7 @@ static void activate_row (void * user, int row)
aud_drct_play_playlist (row);
if (aud_get_bool ("audgui", "playlist_manager_close_on_activate"))
- hide_cb (playman_win);
+ audgui_hide_unique_window (AUDGUI_PLAYLIST_MANAGER_WINDOW);
}
static void shift_rows (void * user, int row, int before)
@@ -147,23 +122,17 @@ static bool_t search_cb (GtkTreeModel * model, int column, const char * key,
int row = gtk_tree_path_get_indices (path)[0];
gtk_tree_path_free (path);
- char * temp = aud_playlist_get_title (row);
- g_return_val_if_fail (temp, TRUE);
- char * title = g_utf8_strdown (temp, -1);
- str_unref (temp);
+ char * title = aud_playlist_get_title (row);
+ g_return_val_if_fail (title, TRUE);
- temp = g_utf8_strdown (key, -1);
- char * * keys = g_strsplit (temp, " ", 0);
- g_free (temp);
+ Index * keys = str_list_to_index (key, " ");
+ int count = index_count (keys);
bool_t match = FALSE;
- for (int i = 0; keys[i]; i ++)
+ for (int i = 0; i < count; i ++)
{
- if (! keys[i][0])
- continue;
-
- if (strstr (title, keys[i]))
+ if (strstr_nocase_utf8 (title, index_get (keys, i)))
match = TRUE;
else
{
@@ -172,8 +141,8 @@ static bool_t search_cb (GtkTreeModel * model, int column, const char * key,
}
}
- g_free (title);
- g_strfreev (keys);
+ index_free_full (keys, (IndexFreeFunc) str_unref);
+ str_unref (title);
return ! match; /* TRUE == not matched, FALSE == matched */
}
@@ -240,49 +209,29 @@ static void close_on_activate_cb (GtkToggleButton * toggle)
gtk_toggle_button_get_active (toggle));
}
-EXPORT void audgui_playlist_manager (void)
+static void destroy_cb (GtkWidget * window)
{
- GtkWidget * playman_vbox;
- GtkWidget * playman_pl_lv, * playman_pl_lv_sw;
- GtkWidget * playman_button_hbox;
- GtkWidget * new_button, * delete_button, * rename_button, * play_button;
- GtkWidget * hbox, * check_button;
- GdkGeometry playman_win_hints;
-
- if (playman_win)
- {
- gtk_window_present ((GtkWindow *) playman_win);
- return;
- }
+ hook_dissociate ("playlist update", update_hook);
+ hook_dissociate ("playlist activate", activate_hook);
+ hook_dissociate ("playlist set playing", position_hook);
+}
- playman_win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+static GtkWidget * create_playlist_manager (void)
+{
+ GtkWidget * playman_win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_type_hint ((GtkWindow *) playman_win, GDK_WINDOW_TYPE_HINT_DIALOG);
gtk_window_set_title ((GtkWindow *) playman_win, _("Playlist Manager"));
gtk_container_set_border_width ((GtkContainer *) playman_win, 6);
- playman_win_hints.min_width = 400;
- playman_win_hints.min_height = 250;
- gtk_window_set_geometry_hints ((GtkWindow *) playman_win, playman_win,
- &playman_win_hints , GDK_HINT_MIN_SIZE);
-
- int x = aud_get_int ("audgui", "playlist_manager_x");
- int y = aud_get_int ("audgui", "playlist_manager_y");
- int w = aud_get_int ("audgui", "playlist_manager_w");
- int h = aud_get_int ("audgui", "playlist_manager_h");
+ gtk_widget_set_size_request (playman_win, 400, 250);
- if (w && h)
- {
- gtk_window_move ((GtkWindow *) playman_win, x, y);
- gtk_window_set_default_size ((GtkWindow *) playman_win, w, h);
- }
+ g_signal_connect (playman_win, "destroy", (GCallback) destroy_cb, NULL);
+ audgui_destroy_on_escape (playman_win);
- g_signal_connect (playman_win, "delete-event", (GCallback) hide_cb, NULL);
- audgui_hide_on_escape (playman_win);
-
- playman_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ GtkWidget * playman_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_container_add ((GtkContainer *) playman_win, playman_vbox);
/* ListView */
- playman_pl_lv = audgui_list_new (& callbacks, NULL, aud_playlist_count ());
+ GtkWidget * playman_pl_lv = audgui_list_new (& callbacks, NULL, aud_playlist_count ());
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 ());
@@ -292,7 +241,7 @@ EXPORT void audgui_playlist_manager (void)
hook_associate ("playlist activate", activate_hook, playman_pl_lv);
hook_associate ("playlist set playing", position_hook, playman_pl_lv);
- playman_pl_lv_sw = gtk_scrolled_window_new (NULL, NULL);
+ GtkWidget * playman_pl_lv_sw = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_shadow_type ((GtkScrolledWindow *) playman_pl_lv_sw,
GTK_SHADOW_IN);
gtk_scrolled_window_set_policy ((GtkScrolledWindow *) playman_pl_lv_sw,
@@ -301,13 +250,11 @@ EXPORT void audgui_playlist_manager (void)
gtk_box_pack_start ((GtkBox *) playman_vbox, playman_pl_lv_sw, TRUE, TRUE, 0);
/* ButtonBox */
- playman_button_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
- new_button = gtk_button_new_from_stock (GTK_STOCK_NEW);
- delete_button = gtk_button_new_from_stock (GTK_STOCK_DELETE);
- rename_button = gtk_button_new_with_mnemonic (_("_Rename"));
- gtk_button_set_image ((GtkButton *) rename_button, gtk_image_new_from_stock
- (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON));
- play_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PLAY);
+ GtkWidget * playman_button_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ GtkWidget * new_button = audgui_button_new (_("_New"), "document-new", new_cb, NULL);
+ GtkWidget * delete_button = audgui_button_new (_("_Remove"), "edit-delete", delete_cb, NULL);
+ GtkWidget * rename_button = audgui_button_new (_("Ren_ame"), "insert-text", rename_cb, NULL);
+ GtkWidget * play_button = audgui_button_new (_("_Play"), "media-playback-start", play_cb, NULL);
gtk_container_add ((GtkContainer *) playman_button_hbox, new_button);
gtk_container_add ((GtkContainer *) playman_button_hbox, delete_button);
@@ -315,36 +262,21 @@ EXPORT void audgui_playlist_manager (void)
gtk_box_pack_end ((GtkBox *) playman_button_hbox, rename_button, FALSE, FALSE, 0);
gtk_container_add ((GtkContainer *) playman_vbox, playman_button_hbox);
- g_signal_connect (new_button, "clicked", (GCallback) new_cb, NULL);
- g_signal_connect (delete_button, "clicked", (GCallback) delete_cb, NULL);
- g_signal_connect (rename_button, "clicked", (GCallback) rename_cb, NULL);
- g_signal_connect (play_button, "clicked", (GCallback) play_cb, NULL);
-
/* CheckButton */
- hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start ((GtkBox *) playman_vbox, hbox, FALSE, FALSE, 0);
- check_button = gtk_check_button_new_with_mnemonic
+ GtkWidget * check_button = gtk_check_button_new_with_mnemonic
(_("_Close dialog on activating playlist"));
gtk_box_pack_start ((GtkBox *) hbox, check_button, FALSE, FALSE, 0);
gtk_toggle_button_set_active ((GtkToggleButton *) check_button, aud_get_bool
("audgui", "playlist_manager_close_on_activate"));
g_signal_connect (check_button, "toggled", (GCallback) close_on_activate_cb, NULL);
- gtk_widget_show_all (playman_win);
-
- hook_associate ("config save", save_config_cb, playman_win);
+ return playman_win;
}
-void audgui_playlist_manager_cleanup (void)
+EXPORT void audgui_playlist_manager (void)
{
- if (! playman_win)
- return;
-
- hook_dissociate ("playlist update", update_hook);
- hook_dissociate ("playlist activate", activate_hook);
- hook_dissociate ("playlist set playing", position_hook);
- hook_dissociate ("config save", save_config_cb);
-
- gtk_widget_destroy (playman_win);
- playman_win = NULL;
+ if (! audgui_reshow_unique_window (AUDGUI_PLAYLIST_MANAGER_WINDOW))
+ audgui_show_unique_window (AUDGUI_PLAYLIST_MANAGER_WINDOW, create_playlist_manager ());
}
diff --git a/src/libaudgui/urilist.c b/src/libaudgui/urilist.c
index 2ffdfea..4f75764 100644
--- a/src/libaudgui/urilist.c
+++ b/src/libaudgui/urilist.c
@@ -1,6 +1,6 @@
/*
* urilist.c
- * Copyright 2010 John Lindgren
+ * Copyright 2010-2011 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -36,7 +36,7 @@ static char * check_uri (char * name)
if (strstr (name, "://") || ! (new = filename_to_uri (name)))
return name;
- g_free (name);
+ str_unref (name);
return new;
}
@@ -53,15 +53,14 @@ static void urilist_for_each (const char * list, ForEachFunc func, void * user)
else
next = end = strchr (list, 0);
- func (check_uri (g_strndup (list, end - list)), user);
+ func (check_uri (str_nget (list, end - list)), user);
list = next;
}
}
static void add_to_index (char * name, Index * index)
{
- index_append (index, str_get (name));
- g_free (name);
+ index_insert (index, -1, name);
}
EXPORT void audgui_urilist_open (const char * list)
diff --git a/src/libaudgui/url-opener.c b/src/libaudgui/url-opener.c
index 0e69946..3baf7e8 100644
--- a/src/libaudgui/url-opener.c
+++ b/src/libaudgui/url-opener.c
@@ -1,6 +1,6 @@
/*
* url-opener.c
- * Copyright 2012 John Lindgren
+ * Copyright 2012-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -25,49 +25,39 @@
#include "init.h"
#include "libaudgui.h"
+#include "libaudgui-gtk.h"
-static GtkWidget * window;
-
-static void response_cb (GtkWidget * dialog, int response)
+static void open_cb (void * entry)
{
- if (response == GTK_RESPONSE_ACCEPT)
- {
- GtkWidget * entry = g_object_get_data ((GObject *) dialog, "entry");
- const char * text = gtk_entry_get_text ((GtkEntry *) entry);
- bool_t open = GPOINTER_TO_INT (g_object_get_data ((GObject *) dialog, "open"));
+ const char * text = gtk_entry_get_text ((GtkEntry *) entry);
+ bool_t open = GPOINTER_TO_INT (g_object_get_data ((GObject *) entry, "open"));
- if (open)
- aud_drct_pl_open (text);
- else
- aud_drct_pl_add (text, -1);
-
- aud_history_add (text);
- }
+ if (open)
+ aud_drct_pl_open (text);
+ else
+ aud_drct_pl_add (text, -1);
- gtk_widget_destroy (dialog);
+ aud_history_add (text);
}
-EXPORT void audgui_show_add_url_window (bool_t open)
+static GtkWidget * create_url_opener (bool_t open)
{
- if (window)
- gtk_widget_destroy (window);
-
- window = gtk_dialog_new_with_buttons (open ? _("Open URL") : _("Add URL"),
- NULL, 0, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, open ? GTK_STOCK_OPEN :
- GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT, NULL);
- gtk_widget_set_size_request (window, 300, -1);
- gtk_window_set_resizable ((GtkWindow *) window, FALSE);
- gtk_dialog_set_default_response ((GtkDialog *) window, GTK_RESPONSE_ACCEPT);
-
- GtkWidget * box = gtk_dialog_get_content_area ((GtkDialog *) window);
+ const char * title, * verb, * icon;
- GtkWidget * label = gtk_label_new (_("Enter URL:"));
- gtk_misc_set_alignment ((GtkMisc *) label, 0, 0);
- gtk_box_pack_start ((GtkBox *) box, label, FALSE, FALSE, 0);
+ if (open)
+ {
+ title = _("Open URL");
+ verb = _("_Open");
+ icon = "document-open";
+ }
+ else
+ {
+ title = _("Add URL");
+ verb = _("_Add");
+ icon = "list-add";
+ }
GtkWidget * combo = gtk_combo_box_text_new_with_entry ();
- gtk_box_pack_start ((GtkBox *) box, combo, FALSE, FALSE, 0);
-
GtkWidget * entry = gtk_bin_get_child ((GtkBin *) combo);
gtk_entry_set_activates_default ((GtkEntry *) entry, TRUE);
@@ -75,17 +65,20 @@ EXPORT void audgui_show_add_url_window (bool_t open)
for (int i = 0; (item = aud_history_get (i)); i++)
gtk_combo_box_text_append_text ((GtkComboBoxText *) combo, item);
- g_object_set_data ((GObject *) window, "entry", entry);
- g_object_set_data ((GObject *) window, "open", GINT_TO_POINTER (open));
+ g_object_set_data ((GObject *) entry, "open", GINT_TO_POINTER (open));
+
+ GtkWidget * button1 = audgui_button_new (verb, icon, open_cb, entry);
+ GtkWidget * button2 = audgui_button_new (_("_Cancel"), "process-stop", NULL, NULL);
- g_signal_connect (window, "response", (GCallback) response_cb, NULL);
- g_signal_connect (window, "destroy", (GCallback) gtk_widget_destroyed, & window);
+ GtkWidget * dialog = audgui_dialog_new (GTK_MESSAGE_OTHER, title,
+ _("Enter URL:"), button1, button2);
+ gtk_widget_set_size_request (dialog, 400, -1);
+ audgui_dialog_add_widget (dialog, combo);
- gtk_widget_show_all (window);
+ return dialog;
}
-void audgui_url_opener_cleanup (void)
+EXPORT void audgui_show_add_url_window (bool_t open)
{
- if (window)
- gtk_widget_destroy (window);
+ audgui_show_unique_window (AUDGUI_URL_OPENER_WINDOW, create_url_opener (open));
}
diff --git a/src/libaudgui/util.c b/src/libaudgui/util.c
index 5a3061e..32d3cb8 100644
--- a/src/libaudgui/util.c
+++ b/src/libaudgui/util.c
@@ -1,6 +1,6 @@
/*
* util.c
- * Copyright 2010-2012 John Lindgren
+ * Copyright 2010-2012 John Lindgren and MichaƂ Lipski
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -20,12 +20,10 @@
#include <string.h>
#include <gdk/gdkkeysyms.h>
-#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gtk/gtk.h>
#include <audacious/debug.h>
#include <audacious/i18n.h>
-#include <audacious/playlist.h>
#include <audacious/misc.h>
#include <libaudcore/audstrings.h>
#include <libaudcore/hook.h>
@@ -34,8 +32,6 @@
#include "libaudgui.h"
#include "libaudgui-gtk.h"
-static GdkPixbuf * current_pixbuf;
-
EXPORT int audgui_get_digit_width (GtkWidget * widget)
{
int width;
@@ -45,6 +41,7 @@ EXPORT int audgui_get_digit_width (GtkWidget * widget)
pango_layout_set_font_description (layout, desc);
pango_layout_get_pixel_size (layout, & width, NULL);
pango_font_description_free (desc);
+ g_object_unref (layout);
return (width + 9) / 10;
}
@@ -75,28 +72,6 @@ EXPORT void audgui_get_mouse_coords (GtkWidget * widget, int * x, int * y)
}
}
-EXPORT void audgui_hide_on_delete (GtkWidget * widget)
-{
- g_signal_connect (widget, "delete-event", (GCallback)
- gtk_widget_hide_on_delete, NULL);
-}
-
-static bool_t escape_hide_cb (GtkWidget * widget, GdkEventKey * event)
-{
- if (event->keyval == GDK_KEY_Escape)
- {
- gtk_widget_hide (widget);
- return TRUE;
- }
-
- return FALSE;
-}
-
-EXPORT void audgui_hide_on_escape (GtkWidget * widget)
-{
- g_signal_connect (widget, "key-press-event", (GCallback) escape_hide_cb, NULL);
-}
-
static bool_t escape_destroy_cb (GtkWidget * widget, GdkEventKey * event)
{
if (event->keyval == GDK_KEY_Escape)
@@ -113,223 +88,97 @@ EXPORT void audgui_destroy_on_escape (GtkWidget * widget)
g_signal_connect (widget, "key-press-event", (GCallback) escape_destroy_cb, NULL);
}
-static void toggle_cb (GtkToggleButton * toggle, bool_t * setting)
-{
- * setting = gtk_toggle_button_get_active (toggle);
-}
-
-EXPORT void audgui_connect_check_box (GtkWidget * box, bool_t * setting)
-{
- gtk_toggle_button_set_active ((GtkToggleButton *) box, * setting);
- g_signal_connect ((GObject *) box, "toggled", (GCallback) toggle_cb, setting);
-}
-
-EXPORT void audgui_simple_message (GtkWidget * * widget, GtkMessageType type,
- const char * title, const char * text)
+EXPORT GtkWidget * audgui_button_new (const char * text, const char * icon,
+ AudguiCallback callback, void * data)
{
- AUDDBG ("%s\n", text);
+ GtkWidget * button = gtk_button_new_with_mnemonic (text);
- if (* widget != NULL)
+ if (icon)
{
- const char * old = NULL;
- g_object_get ((GObject *) * widget, "text", & old, NULL);
- g_return_if_fail (old);
-
- int messages = GPOINTER_TO_INT (g_object_get_data ((GObject *)
- * widget, "messages"));
-
- if (messages > 10)
- text = _("\n(Further messages have been hidden.)");
-
- if (strstr (old, text))
- goto CREATED;
-
- char both[strlen (old) + strlen (text) + 2];
- snprintf (both, sizeof both, "%s\n%s", old, text);
- g_object_set ((GObject *) * widget, "text", both, NULL);
-
- g_object_set_data ((GObject *) * widget, "messages", GINT_TO_POINTER
- (messages + 1));
-
- goto CREATED;
+ GtkWidget * image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU);
+ gtk_button_set_image ((GtkButton *) button, image);
}
- * widget = gtk_message_dialog_new (NULL, 0, type, GTK_BUTTONS_OK, "%s", text);
- gtk_window_set_title ((GtkWindow *) * widget, title);
-
- g_object_set_data ((GObject *) * widget, "messages", GINT_TO_POINTER (1));
-
- g_signal_connect (* widget, "response", (GCallback) gtk_widget_destroy, NULL);
- audgui_destroy_on_escape (* widget);
- g_signal_connect (* widget, "destroy", (GCallback) gtk_widget_destroyed,
- widget);
+ if (callback)
+ g_signal_connect_swapped (button, "clicked", (GCallback) callback, data);
-CREATED:
- gtk_window_present ((GtkWindow *) * widget);
+ return button;
}
-EXPORT GdkPixbuf * audgui_pixbuf_from_data (const void * data, int64_t size)
+EXPORT GtkWidget * audgui_dialog_new (GtkMessageType type, const char * title,
+ const char * text, GtkWidget * button1, GtkWidget * button2)
{
- GdkPixbuf * pixbuf = NULL;
- GdkPixbufLoader * loader = gdk_pixbuf_loader_new ();
- GError * error = NULL;
+ GtkWidget * dialog = gtk_message_dialog_new (NULL, 0, type, GTK_BUTTONS_NONE, "%s", text);
+ gtk_window_set_title ((GtkWindow *) dialog, title);
- if (gdk_pixbuf_loader_write (loader, data, size, & error) &&
- gdk_pixbuf_loader_close (loader, & error))
- {
- if ((pixbuf = gdk_pixbuf_loader_get_pixbuf (loader)))
- g_object_ref (pixbuf);
- }
- else
+ if (button2)
{
- AUDDBG ("error while loading pixbuf: %s\n", error->message);
- g_error_free (error);
+ gtk_dialog_add_action_widget ((GtkDialog *) dialog, button2, GTK_RESPONSE_NONE);
+ g_signal_connect_swapped (button2, "clicked", (GCallback) gtk_widget_destroy, dialog);
}
- g_object_unref (loader);
- return pixbuf;
-}
-
-/* deprecated */
-EXPORT GdkPixbuf * audgui_pixbuf_for_entry (int list, int entry)
-{
- char * name = aud_playlist_entry_get_filename (list, entry);
- g_return_val_if_fail (name, NULL);
-
- const void * data;
- int64_t size;
-
- aud_art_get_data (name, & data, & size);
+ gtk_dialog_add_action_widget ((GtkDialog *) dialog, button1, GTK_RESPONSE_NONE);
+ g_signal_connect_swapped (button1, "clicked", (GCallback) gtk_widget_destroy, dialog);
- if (data)
- {
- GdkPixbuf * p = audgui_pixbuf_from_data (data, size);
- aud_art_unref (name);
-
- if (p)
- {
- str_unref (name);
- return p;
- }
- }
+ gtk_widget_set_can_default (button1, TRUE);
+ gtk_widget_grab_default (button1);
- str_unref (name);
- return audgui_pixbuf_fallback ();
+ return dialog;
}
-EXPORT GdkPixbuf * audgui_pixbuf_fallback (void)
+EXPORT void audgui_dialog_add_widget (GtkWidget * dialog, GtkWidget * widget)
{
- static GdkPixbuf * fallback = NULL;
-
- if (! fallback)
- {
- SPRINTF (path, "%s/images/album.png", aud_get_path (AUD_PATH_DATA_DIR));
- fallback = gdk_pixbuf_new_from_file (path, NULL);
- }
-
- if (fallback)
- g_object_ref ((GObject *) fallback);
-
- return fallback;
+ GtkWidget * box = gtk_message_dialog_get_message_area ((GtkMessageDialog *) dialog);
+ gtk_box_pack_start ((GtkBox *) box, widget, FALSE, FALSE, 0);
}
-void audgui_pixbuf_uncache (void)
+EXPORT void audgui_simple_message (GtkWidget * * widget, GtkMessageType type,
+ const char * title, const char * text)
{
- if (current_pixbuf)
- {
- g_object_unref ((GObject *) current_pixbuf);
- current_pixbuf = NULL;
- }
-}
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ AUDDBG ("%s\n", text);
-/* deprecated */
-EXPORT GdkPixbuf * audgui_pixbuf_for_current (void)
-{
- if (! current_pixbuf)
+ if (* widget)
{
- int list = aud_playlist_get_playing ();
- current_pixbuf = audgui_pixbuf_for_entry (list, aud_playlist_get_position (list));
- }
-
- if (current_pixbuf)
- g_object_ref ((GObject *) current_pixbuf);
-
- return current_pixbuf;
-}
-
-#pragma GCC diagnostic pop
+ const char * old = NULL;
+ g_object_get ((GObject *) * widget, "text", & old, NULL);
+ g_return_if_fail (old);
-EXPORT void audgui_pixbuf_scale_within (GdkPixbuf * * pixbuf, int size)
-{
- int width = gdk_pixbuf_get_width (* pixbuf);
- int height = gdk_pixbuf_get_height (* pixbuf);
+ int messages = GPOINTER_TO_INT (g_object_get_data ((GObject *) * widget, "messages"));
+ if (messages > 10)
+ text = _("\n(Further messages have been hidden.)");
- if (width <= size && height <= size)
- return;
+ if (! strstr (old, text))
+ {
+ SCONCAT3 (both, old, "\n", text);
+ g_object_set ((GObject *) * widget, "text", both, NULL);
+ g_object_set_data ((GObject *) * widget, "messages", GINT_TO_POINTER (messages + 1));
+ }
- if (width > height)
- {
- height = size * height / width;
- width = size;
+ gtk_window_present ((GtkWindow *) * widget);
}
else
{
- width = size * width / height;
- height = size;
- }
+ GtkWidget * button = audgui_button_new (_("_Close"), "window-close", NULL, NULL);
+ * widget = audgui_dialog_new (type, title, text, button, NULL);
- if (width < 1)
- width = 1;
- if (height < 1)
- height = 1;
+ g_object_set_data ((GObject *) * widget, "messages", GINT_TO_POINTER (1));
+ g_signal_connect (* widget, "destroy", (GCallback) gtk_widget_destroyed, widget);
- GdkPixbuf * pixbuf2 = gdk_pixbuf_scale_simple (* pixbuf, width, height,
- GDK_INTERP_BILINEAR);
- g_object_unref (* pixbuf);
- * pixbuf = pixbuf2;
+ gtk_widget_show_all (* widget);
+ }
}
-EXPORT GdkPixbuf * audgui_pixbuf_request (const char * filename)
+EXPORT void audgui_format_time (char * buf, int bufsize, int64_t milliseconds)
{
- const void * data;
- int64_t size;
-
- aud_art_request_data (filename, & data, & size);
- if (! data)
- return NULL;
+ int hours = milliseconds / 3600000;
+ int minutes = (milliseconds / 60000) % 60;
+ int seconds = (milliseconds / 1000) % 60;
- GdkPixbuf * p = audgui_pixbuf_from_data (data, size);
-
- aud_art_unref (filename);
- return p;
-}
-
-EXPORT GdkPixbuf * audgui_pixbuf_request_current (void)
-{
- if (! current_pixbuf)
+ if (hours)
+ snprintf (buf, bufsize, "%d:%02d:%02d", hours, minutes, seconds);
+ else
{
- int list = aud_playlist_get_playing ();
- int entry = aud_playlist_get_position (list);
- if (entry < 0)
- return NULL;
-
- char * filename = aud_playlist_entry_get_filename (list, entry);
- current_pixbuf = audgui_pixbuf_request (filename);
- str_unref (filename);
+ bool_t zero = aud_get_bool (NULL, "leading_zero");
+ snprintf (buf, bufsize, zero ? "%02d:%02d" : "%d:%02d", minutes, seconds);
}
-
- if (current_pixbuf)
- g_object_ref ((GObject *) current_pixbuf);
-
- return current_pixbuf;
-}
-
-EXPORT void audgui_set_default_icon (void)
-{
-#ifndef _WIN32
- gtk_window_set_default_icon_name ("audacious");
-#endif
}
diff --git a/src/libaudtag/ape/ape.c b/src/libaudtag/ape/ape.c
index d494403..b755a04 100644
--- a/src/libaudtag/ape/ape.c
+++ b/src/libaudtag/ape/ape.c
@@ -26,6 +26,7 @@
#include <stdlib.h>
#include <string.h>
+#include <libaudcore/audstrings.h>
#include <libaudcore/vfs.h>
#include "ape.h"
@@ -73,8 +74,8 @@ static bool_t ape_read_header (VFSFile * handle, APEHeader * header)
return TRUE;
}
-static bool_t ape_find_header (VFSFile * handle, APEHeader * header, int *
- start, int * length, int * data_start, int * data_length)
+static bool_t ape_find_header (VFSFile * handle, APEHeader * header,
+ int * start, int * length, int * data_start, int * data_length)
{
APEHeader secondary;
@@ -83,15 +84,15 @@ static bool_t ape_find_header (VFSFile * handle, APEHeader * header, int *
if (ape_read_header (handle, header))
{
- TAGDBG ("Found header at 0, length = %d, version = %d.\n", (int)
- header->length, (int) header->version);
+ TAGDBG ("Found header at 0, length = %d, version = %d.\n",
+ (int) header->length, (int) header->version);
+
* start = 0;
* length = header->length;
* data_start = sizeof (APEHeader);
* data_length = header->length - sizeof (APEHeader);
- if (! (header->flags & APE_FLAG_HAS_HEADER) || ! (header->flags &
- APE_FLAG_IS_HEADER))
+ if (! (header->flags & APE_FLAG_HAS_HEADER) || ! (header->flags & APE_FLAG_IS_HEADER))
{
TAGDBG ("Invalid header flags (%u).\n", (unsigned int) header->flags);
return FALSE;
@@ -117,44 +118,50 @@ static bool_t ape_find_header (VFSFile * handle, APEHeader * header, int *
if (vfs_fseek (handle, -(int) sizeof (APEHeader), SEEK_END))
return FALSE;
- if (ape_read_header (handle, header))
+ if (! ape_read_header (handle, header))
{
- TAGDBG ("Found footer at %d, length = %d, version = %d.\n", (int)
- vfs_ftell (handle) - (int) sizeof (APEHeader), (int) header->length,
- (int) header->version);
- * start = vfs_ftell (handle) - header->length;
- * length = header->length;
- * data_start = vfs_ftell (handle) - header->length;
- * data_length = header->length - sizeof (APEHeader);
+ /* APE tag may be followed by an ID3v1 tag */
+ if (vfs_fseek (handle, -128 - (int) sizeof (APEHeader), SEEK_END))
+ return FALSE;
- if ((header->flags & APE_FLAG_HAS_NO_FOOTER) || (header->flags &
- APE_FLAG_IS_HEADER))
+ if (! ape_read_header (handle, header))
{
- TAGDBG ("Invalid footer flags (%u).\n", (unsigned int) header->flags);
+ TAGDBG ("No header found.\n");
return FALSE;
}
+ }
- if (header->flags & APE_FLAG_HAS_HEADER)
- {
- if (vfs_fseek (handle, -(int) header->length - sizeof (APEHeader),
- SEEK_CUR))
- return FALSE;
+ TAGDBG ("Found footer at %d, length = %d, version = %d.\n",
+ (int) vfs_ftell (handle) - (int) sizeof (APEHeader), (int) header->length,
+ (int) header->version);
- if (! ape_read_header (handle, & secondary))
- {
- TAGDBG ("Expected header, but found none.\n");
- return FALSE;
- }
+ * start = vfs_ftell (handle) - header->length;
+ * length = header->length;
+ * data_start = vfs_ftell (handle) - header->length;
+ * data_length = header->length - sizeof (APEHeader);
- * start -= sizeof (APEHeader);
- * length += sizeof (APEHeader);
+ if ((header->flags & APE_FLAG_HAS_NO_FOOTER) || (header->flags & APE_FLAG_IS_HEADER))
+ {
+ TAGDBG ("Invalid footer flags (%u).\n", (unsigned) header->flags);
+ return FALSE;
+ }
+
+ if (header->flags & APE_FLAG_HAS_HEADER)
+ {
+ if (vfs_fseek (handle, -(int) header->length - sizeof (APEHeader), SEEK_CUR))
+ return FALSE;
+
+ if (! ape_read_header (handle, & secondary))
+ {
+ TAGDBG ("Expected header, but found none.\n");
+ return FALSE;
}
- return TRUE;
+ * start -= sizeof (APEHeader);
+ * length += sizeof (APEHeader);
}
- TAGDBG ("No header found.\n");
- return FALSE;
+ return TRUE;
}
static bool_t ape_is_our_file (VFSFile * handle)
@@ -195,9 +202,9 @@ static ValuePair * ape_read_item (void * * data, int length)
return NULL;
}
- pair = g_malloc (sizeof (ValuePair));
- pair->key = g_strdup ((char *) (* data) + 8);
- pair->value = g_strndup (value, header[0]);
+ pair = g_slice_new (ValuePair);
+ pair->key = str_get ((char *) (* data) + 8);
+ pair->value = str_nget (value, header[0]);
* data = value + header[0];
@@ -245,15 +252,11 @@ static GList * ape_read_items (VFSFile * handle)
return g_list_reverse (list);
}
-static void free_tag_list (GList * list)
+static void free_value_pair (ValuePair * pair)
{
- while (list != NULL)
- {
- g_free (((ValuePair *) list->data)->key);
- g_free (((ValuePair *) list->data)->value);
- g_free (list->data);
- list = g_list_delete_link (list, list);
- }
+ str_unref (pair->key);
+ str_unref (pair->value);
+ g_slice_free (ValuePair, pair);
}
static void parse_gain_text (const char * text, int * value, int * unit)
@@ -297,12 +300,12 @@ static void set_gain_info (Tuple * tuple, int field, int unit_field,
parse_gain_text (text, & value, & unit);
- if (tuple_get_value_type (tuple, unit_field, NULL) == TUPLE_INT)
- value = value * (int64_t) tuple_get_int (tuple, unit_field, NULL) / unit;
+ if (tuple_get_value_type (tuple, unit_field) == TUPLE_INT)
+ value = value * (int64_t) tuple_get_int (tuple, unit_field) / unit;
else
- tuple_set_int (tuple, unit_field, NULL, unit);
+ tuple_set_int (tuple, unit_field, unit);
- tuple_set_int (tuple, field, NULL, value);
+ tuple_set_int (tuple, field, value);
}
static bool_t ape_read_tag (Tuple * tuple, VFSFile * handle)
@@ -315,34 +318,30 @@ static bool_t ape_read_tag (Tuple * tuple, VFSFile * handle)
char * value = ((ValuePair *) node->data)->value;
if (! strcmp (key, "Artist"))
- tuple_set_str (tuple, FIELD_ARTIST, NULL, value);
+ tuple_set_str (tuple, FIELD_ARTIST, value);
else if (! strcmp (key, "Title"))
- tuple_set_str (tuple, FIELD_TITLE, NULL, value);
+ tuple_set_str (tuple, FIELD_TITLE, value);
else if (! strcmp (key, "Album"))
- tuple_set_str (tuple, FIELD_ALBUM, NULL, value);
+ tuple_set_str (tuple, FIELD_ALBUM, value);
else if (! strcmp (key, "Comment"))
- tuple_set_str (tuple, FIELD_COMMENT, NULL, value);
+ tuple_set_str (tuple, FIELD_COMMENT, value);
else if (! strcmp (key, "Genre"))
- tuple_set_str (tuple, FIELD_GENRE, NULL, value);
+ tuple_set_str (tuple, FIELD_GENRE, value);
else if (! strcmp (key, "Track"))
- tuple_set_int (tuple, FIELD_TRACK_NUMBER, NULL, atoi (value));
+ tuple_set_int (tuple, FIELD_TRACK_NUMBER, atoi (value));
else if (! strcmp (key, "Year"))
- tuple_set_int (tuple, FIELD_YEAR, NULL, atoi (value));
+ tuple_set_int (tuple, FIELD_YEAR, atoi (value));
else if (! g_ascii_strcasecmp (key, "REPLAYGAIN_TRACK_GAIN"))
- set_gain_info (tuple, FIELD_GAIN_TRACK_GAIN, FIELD_GAIN_GAIN_UNIT,
- value);
+ set_gain_info (tuple, FIELD_GAIN_TRACK_GAIN, FIELD_GAIN_GAIN_UNIT, value);
else if (! g_ascii_strcasecmp (key, "REPLAYGAIN_TRACK_PEAK"))
- set_gain_info (tuple, FIELD_GAIN_TRACK_PEAK, FIELD_GAIN_PEAK_UNIT,
- value);
+ set_gain_info (tuple, FIELD_GAIN_TRACK_PEAK, FIELD_GAIN_PEAK_UNIT, value);
else if (! g_ascii_strcasecmp (key, "REPLAYGAIN_ALBUM_GAIN"))
- set_gain_info (tuple, FIELD_GAIN_ALBUM_GAIN, FIELD_GAIN_GAIN_UNIT,
- value);
+ set_gain_info (tuple, FIELD_GAIN_ALBUM_GAIN, FIELD_GAIN_GAIN_UNIT, value);
else if (! g_ascii_strcasecmp (key, "REPLAYGAIN_ALBUM_PEAK"))
- set_gain_info (tuple, FIELD_GAIN_ALBUM_PEAK, FIELD_GAIN_PEAK_UNIT,
- value);
+ set_gain_info (tuple, FIELD_GAIN_ALBUM_PEAK, FIELD_GAIN_PEAK_UNIT, value);
}
- free_tag_list (list);
+ g_list_free_full (list, (GDestroyNotify) free_value_pair);
return TRUE;
}
@@ -374,7 +373,7 @@ static bool_t ape_write_item (VFSFile * handle, const char * key,
static bool_t write_string_item (const Tuple * tuple, int field, VFSFile *
handle, const char * key, int * written_length, int * written_items)
{
- char * value = tuple_get_str (tuple, field, NULL);
+ char * value = tuple_get_str (tuple, field);
if (value == NULL)
return TRUE;
@@ -391,13 +390,13 @@ static bool_t write_string_item (const Tuple * tuple, int field, VFSFile *
static bool_t write_integer_item (const Tuple * tuple, int field, VFSFile *
handle, const char * key, int * written_length, int * written_items)
{
- int value = tuple_get_int (tuple, field, NULL);
+ int value = tuple_get_int (tuple, field);
char scratch[32];
- if (! value)
+ if (value <= 0)
return TRUE;
- snprintf (scratch, sizeof scratch, "%d", value);
+ str_itoa (value, scratch, sizeof scratch);
if (! ape_write_item (handle, key, scratch, written_length))
return FALSE;
@@ -488,11 +487,11 @@ static bool_t ape_write_tag (const Tuple * tuple, VFSFile * handle)
start, SEEK_SET) || ! write_header (length, items, TRUE, handle))
goto ERR;
- free_tag_list (list);
+ g_list_free_full (list, (GDestroyNotify) free_value_pair);
return TRUE;
ERR:
- free_tag_list (list);
+ g_list_free_full (list, (GDestroyNotify) free_value_pair);
return FALSE;
}
diff --git a/src/libaudtag/audtag.c b/src/libaudtag/audtag.c
index 2e0d6b0..0ef0f3e 100644
--- a/src/libaudtag/audtag.c
+++ b/src/libaudtag/audtag.c
@@ -17,6 +17,9 @@
* the use of this software.
*/
+#include <stdlib.h>
+#include <string.h>
+
#include <glib.h>
#include <libaudcore/tuple.h>
@@ -38,8 +41,11 @@ EXPORT bool_t tag_tuple_read (Tuple * tuple, VFSFile * handle)
{
tag_module_t * module = find_tag_module (handle, TAG_TYPE_NONE);
- if (module == NULL)
+ if (! module || ! module->read_tag)
+ {
+ TAGDBG ("read_tag() not supported for %s\n", vfs_get_filename (handle));
return FALSE;
+ }
return module->read_tag (tuple, handle);
}
@@ -48,8 +54,11 @@ EXPORT bool_t tag_image_read (VFSFile * handle, void * * data, int64_t * size)
{
tag_module_t * module = find_tag_module (handle, TAG_TYPE_NONE);
- if (module == NULL || module->read_image == NULL)
+ if (! module || ! module->read_image)
+ {
+ TAGDBG ("read_image() not supported for %s\n", vfs_get_filename (handle));
return FALSE;
+ }
return module->read_image (handle, data, size);
}
@@ -58,12 +67,59 @@ EXPORT bool_t tag_tuple_write (const Tuple * tuple, VFSFile * handle, int new_ty
{
tag_module_t * module = find_tag_module (handle, new_type);
- if (module == NULL)
+ if (! module || ! module->write_tag)
+ {
+ TAGDBG ("write_tag() not supported for %s\n", vfs_get_filename (handle));
return FALSE;
+ }
return module->write_tag (tuple, handle);
}
+EXPORT bool_t tag_update_stream_metadata (Tuple * tuple, VFSFile * handle)
+{
+ bool_t updated = FALSE;
+ char * old, * new;
+ int value;
+
+ old = tuple_get_str (tuple, FIELD_TITLE);
+ new = vfs_get_metadata (handle, "track-name");
+
+ if (new && (! old || strcmp (old, new)))
+ {
+ tuple_set_str (tuple, FIELD_TITLE, new);
+ updated = TRUE;
+ }
+
+ str_unref (old);
+ str_unref (new);
+
+ old = tuple_get_str (tuple, FIELD_ARTIST);
+ new = vfs_get_metadata (handle, "stream-name");
+
+ if (new && (! old || strcmp (old, new)))
+ {
+ tuple_set_str (tuple, FIELD_ARTIST, new);
+ updated = TRUE;
+ }
+
+ str_unref (old);
+ str_unref (new);
+
+ new = vfs_get_metadata (handle, "content-bitrate");
+ value = new ? atoi (new) / 1000 : 0;
+
+ if (value && value != tuple_get_int (tuple, FIELD_BITRATE))
+ {
+ tuple_set_int (tuple, FIELD_BITRATE, value);
+ updated = TRUE;
+ }
+
+ str_unref (new);
+
+ return updated;
+}
+
/* deprecated */
EXPORT bool_t tag_tuple_write_to_file (Tuple * tuple, VFSFile * handle)
{
diff --git a/src/libaudtag/audtag.h b/src/libaudtag/audtag.h
index 626e89d..b746feb 100644
--- a/src/libaudtag/audtag.h
+++ b/src/libaudtag/audtag.h
@@ -27,6 +27,7 @@ enum
{
TAG_TYPE_NONE = 0,
TAG_TYPE_APE,
+ TAG_TYPE_ID3V2
};
void tag_set_verbose (bool_t verbose);
@@ -38,6 +39,8 @@ bool_t tag_image_read (VFSFile * handle, void * * data, int64_t * size);
* written if the file does not have any existing tag. */
bool_t tag_tuple_write (const Tuple * tuple, VFSFile * handle, int new_type);
+bool_t tag_update_stream_metadata (Tuple * tuple, VFSFile * handle);
+
/* deprecated, use tag_tuple_write */
bool_t tag_tuple_write_to_file (Tuple * tuple, VFSFile * handle);
diff --git a/src/libaudtag/id3/id3-common.c b/src/libaudtag/id3/id3-common.c
index aec6eb2..2a8a87e 100644
--- a/src/libaudtag/id3/id3-common.c
+++ b/src/libaudtag/id3/id3-common.c
@@ -1,6 +1,6 @@
/*
* id3-common.c
- * Copyright 2010-2011 John Lindgren
+ * Copyright 2010-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,7 +17,10 @@
* the use of this software.
*/
+#include "id3-common.h"
+
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <glib.h>
@@ -25,7 +28,11 @@
#include <libaudcore/audstrings.h>
#include "../util.h"
-#include "id3-common.h"
+
+#define ID3_ENCODING_LATIN1 0
+#define ID3_ENCODING_UTF16 1
+#define ID3_ENCODING_UTF16_BE 2
+#define ID3_ENCODING_UTF8 3
static void * memchr16 (const void * mem, int16_t chr, int len)
{
@@ -41,76 +48,246 @@ static void * memchr16 (const void * mem, int16_t chr, int len)
return NULL;
}
-char * convert_text (const char * text, int length, int encoding, bool_t
- nulled, int * _converted, const char * * after)
+void id3_strnlen (const char * data, int size, int encoding,
+ int * bytes_without_nul, int * bytes_with_nul)
{
- char * buffer = NULL;
- gsize converted = 0;
+ bool_t is16 = (encoding == ID3_ENCODING_UTF16 || encoding == ID3_ENCODING_UTF16_BE);
+ char * nul = is16 ? memchr16 (data, 0, size) : memchr (data, 0, size);
- TAGDBG ("length = %d, encoding = %d, nulled = %d\n", length, encoding,
- nulled);
+ if (nul)
+ {
+ if (bytes_without_nul)
+ * bytes_without_nul = nul - data;
+ if (bytes_with_nul)
+ * bytes_with_nul = is16 ? nul + 2 - data : nul + 1 - data;
+ }
+ else
+ {
+ if (bytes_without_nul)
+ * bytes_without_nul = size;
+ if (bytes_with_nul)
+ * bytes_with_nul = size;
+ }
+}
- if (nulled)
+char * id3_convert (const char * data, int size, int encoding)
+{
+ if (encoding == ID3_ENCODING_UTF16)
+ return str_convert (data, size, "UTF-16", "UTF-8");
+ else if (encoding == ID3_ENCODING_UTF16_BE)
+ return str_convert (data, size, "UTF-16BE", "UTF-8");
+ else
+ return str_to_utf8 (data, size);
+}
+
+char * id3_decode_text (const char * data, int size)
+{
+ if (size < 1)
+ return NULL;
+
+ return id3_convert ((const char *) data + 1, size - 1, data[0]);
+}
+
+void id3_associate_string (Tuple * tuple, int field, const char * data, int size)
+{
+ char * text = id3_decode_text (data, size);
+
+ if (text && text[0])
{
- const char * null;
+ TAGDBG ("Field %i = %s.\n", field, text);
+ tuple_set_str (tuple, field, text);
+ }
- switch (encoding)
- {
- case 0:
- case 3:
- if ((null = memchr (text, 0, length)) == NULL)
- return NULL;
+ str_unref (text);
+}
- length = null - text;
- TAGDBG ("length before null = %d\n", length);
+void id3_associate_int (Tuple * tuple, int field, const char * data, int size)
+{
+ char * text = id3_decode_text (data, size);
- if (after != NULL)
- * after = null + 1;
+ if (text && atoi (text) >= 0)
+ {
+ TAGDBG ("Field %i = %s.\n", field, text);
+ tuple_set_int (tuple, field, atoi (text));
+ }
- break;
- case 1:
- case 2:
- if ((null = memchr16 (text, 0, length)) == NULL)
- return NULL;
+ str_unref (text);
+}
- length = null - text;
- TAGDBG ("length before null = %d\n", length);
+void id3_decode_genre (Tuple * tuple, const char * data, int size)
+{
+ char * text = id3_decode_text (data, size);
+ int numericgenre;
- if (after != NULL)
- * after = null + 2;
+ if (! text)
+ return;
- break;
- }
+ if (text[0] == '(')
+ numericgenre = atoi (text + 1);
+ else
+ numericgenre = atoi (text);
+
+ if (numericgenre > 0)
+ tuple_set_str (tuple, FIELD_GENRE, convert_numericgenre_to_text (numericgenre));
+ else
+ tuple_set_str (tuple, FIELD_GENRE, text);
+
+ str_unref (text);
+}
+
+void id3_decode_comment (Tuple * tuple, const char * data, int size)
+{
+ if (size < 4)
+ return;
+
+ int before_nul, after_nul;
+ id3_strnlen (data + 4, size - 4, data[0], & before_nul, & after_nul);
+
+ const char * lang = data + 1;
+ char * type = id3_convert (data + 4, before_nul, data[0]);
+ char * value = id3_convert (data + 4 + after_nul, size - 4 - after_nul, data[0]);
+
+ TAGDBG ("Comment: lang = %.3s, type = %s, value = %s.\n", lang, type, value);
+
+ if (type && ! type[0] && value) /* blank type = actual comment */
+ tuple_set_str (tuple, FIELD_COMMENT, value);
+
+ str_unref (type);
+ str_unref (value);
+}
+
+static bool_t decode_rva_block (const char * * _data, int * _size,
+ int * channel, int * adjustment, int * adjustment_unit, int * peak,
+ int * peak_unit)
+{
+ const char * data = * _data;
+ int size = * _size;
+ int peak_bits;
+
+ if (size < 4)
+ return FALSE;
+
+ * channel = data[0];
+ * adjustment = (char) data[1]; /* first byte is signed */
+ * adjustment = (* adjustment << 8) | data[2];
+ * adjustment_unit = 512;
+ peak_bits = data[3];
+
+ data += 4;
+ size -= 4;
+
+ TAGDBG ("RVA block: channel = %d, adjustment = %d/%d, peak bits = %d\n",
+ * channel, * adjustment, * adjustment_unit, peak_bits);
+
+ if (peak_bits > 0 && peak_bits < sizeof (int) * 8)
+ {
+ int bytes = (peak_bits + 7) / 8;
+ int count;
+
+ if (bytes > size)
+ return FALSE;
+
+ * peak = 0;
+ * peak_unit = 1 << peak_bits;
+
+ for (count = 0; count < bytes; count ++)
+ * peak = (* peak << 8) | data[count];
+
+ data += bytes;
+ size -= count;
+
+ TAGDBG ("RVA block: peak = %d/%d\n", * peak, * peak_unit);
+ }
+ else
+ {
+ * peak = 0;
+ * peak_unit = 0;
}
- switch (encoding)
+ * _data = data;
+ * _size = size;
+ return TRUE;
+}
+
+void id3_decode_rva (Tuple * tuple, const char * data, int size)
+{
+ const char * domain;
+ int channel, adjustment, adjustment_unit, peak, peak_unit;
+
+ if (memchr (data, 0, size) == NULL)
+ return;
+
+ domain = data;
+
+ TAGDBG ("RVA domain: %s\n", domain);
+
+ size -= strlen (domain) + 1;
+ data += strlen (domain) + 1;
+
+ while (size > 0)
{
- case 0:
- case 3:;
- int converted_int = 0;
- buffer = str_to_utf8_full (text, length, NULL, & converted_int);
- converted = converted_int;
- break;
- case 1:
- if (text[0] == (char) 0xff)
- buffer = g_convert (text + 2, length - 2, "UTF-8", "UTF-16LE", NULL,
- & converted, NULL);
+ if (! decode_rva_block (& data, & size, & channel, & adjustment,
+ & adjustment_unit, & peak, & peak_unit))
+ break;
+
+ if (channel != 1) /* specific channel? */
+ continue;
+
+ if (tuple_get_value_type (tuple, FIELD_GAIN_GAIN_UNIT) == TUPLE_INT)
+ adjustment = adjustment * (int64_t) tuple_get_int (tuple,
+ FIELD_GAIN_GAIN_UNIT) / adjustment_unit;
else
- buffer = g_convert (text + 2, length - 2, "UTF-8", "UTF-16BE", NULL,
- & converted, NULL);
-
- break;
- case 2:
- buffer = g_convert (text, length, "UTF-8", "UTF-16BE", NULL,
- & converted, NULL);
- break;
+ tuple_set_int (tuple, FIELD_GAIN_GAIN_UNIT, adjustment_unit);
+
+ if (peak_unit)
+ {
+ if (tuple_get_value_type (tuple, FIELD_GAIN_PEAK_UNIT) == TUPLE_INT)
+ peak = peak * (int64_t) tuple_get_int (tuple,
+ FIELD_GAIN_PEAK_UNIT) / peak_unit;
+ else
+ tuple_set_int (tuple, FIELD_GAIN_PEAK_UNIT, peak_unit);
+ }
+
+ if (! g_ascii_strcasecmp (domain, "album"))
+ {
+ tuple_set_int (tuple, FIELD_GAIN_ALBUM_GAIN, adjustment);
+
+ if (peak_unit)
+ tuple_set_int (tuple, FIELD_GAIN_ALBUM_PEAK, peak);
+ }
+ else if (! g_ascii_strcasecmp (domain, "track"))
+ {
+ tuple_set_int (tuple, FIELD_GAIN_TRACK_GAIN, adjustment);
+
+ if (peak_unit)
+ tuple_set_int (tuple, FIELD_GAIN_TRACK_PEAK, peak);
+ }
}
+}
+
+bool_t id3_decode_picture (const char * data, int size, int * type,
+ void * * image_data, int64_t * image_size)
+{
+ const char * nul;
+ if (size < 2 || ! (nul = memchr (data + 1, 0, size - 2)))
+ return FALSE;
+
+ const char * body = nul + 2;
+ int body_size = data + size - body;
+
+ int before_nul2, after_nul2;
+ id3_strnlen (body, body_size, data[0], & before_nul2, & after_nul2);
+
+ const char * mime = data + 1;
+ char * desc = id3_convert (body, before_nul2, data[0]);
- TAGDBG ("length converted: %d\n", (int) converted);
- TAGDBG ("string: %s\n", buffer);
+ * type = nul[1];
+ * image_size = body_size - after_nul2;
+ * image_data = g_memdup (body + after_nul2, * image_size);
- if (_converted != NULL)
- * _converted = converted;
+ TAGDBG ("Picture: mime = %s, type = %d, desc = %s, size = %d.\n", mime,
+ * type, desc, (int) * image_size);
- return buffer;
+ str_unref (desc);
+ return TRUE;
}
diff --git a/src/libaudtag/id3/id3-common.h b/src/libaudtag/id3/id3-common.h
index 6a0c61e..1b100ab 100644
--- a/src/libaudtag/id3/id3-common.h
+++ b/src/libaudtag/id3/id3-common.h
@@ -1,6 +1,6 @@
/*
* id3-common.h
- * Copyright 2010 John Lindgren
+ * Copyright 2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -20,25 +20,16 @@
#ifndef AUDTAG_ID3_COMMON_H
#define AUDTAG_ID3_COMMON_H
-/*
- * text: A pointer to the text data to be converted.
- * length: The size in bytes of the text data.
- * encoding: 0 = ISO-8859-1 or a similar 8-bit character set, 1 = UTF-16 with
- * BOM, 2 = UTF-16BE without BOM, 3 = UTF-8.
- * nulled: Whether the text data is null-terminated. If this flag is set and no
- * null character is found, an error occurs. If this flag is not set, any null
- * characters found are included in the converted text.
- * converted: A location in which to store the number of bytes converted, or
- * NULL. This count does not include the null character appended to the
- * converted text, but may include null characters present in the text data.
- * This count is undefined if an error occurs.
- * after: A location in which to store a pointer to the next character after a
- * terminating null character, or NULL. This pointer is undefined if the
- * "nulled" flag is not used or if an error occurs.
- * returns: A pointer to the converted text with a null character appended, or
- * NULL if an error occurs. The text must be freed when no longer needed.
- */
-char * convert_text (const char * text, int length, int encoding, bool_t
- nulled, int * _converted, const char * * after);
+#include <stdint.h>
+#include <libaudcore/tuple.h>
+
+void id3_associate_string (Tuple * tuple, int field, const char * data, int size);
+void id3_associate_int (Tuple * tuple, int field, const char * data, int size);
+void id3_decode_genre (Tuple * tuple, const char * data, int size);
+void id3_decode_comment (Tuple * tuple, const char * data, int size);
+void id3_decode_rva (Tuple * tuple, const char * data, int size);
+
+bool_t id3_decode_picture (const char * data, int size, int * type,
+ void * * image_data, int64_t * image_size);
#endif
diff --git a/src/libaudtag/id3/id3v1.c b/src/libaudtag/id3/id3v1.c
index 7517d19..16da2d2 100644
--- a/src/libaudtag/id3/id3v1.c
+++ b/src/libaudtag/id3/id3v1.c
@@ -1,6 +1,6 @@
/*
* id3v1.c
- * Copyright 2010-2011 Tony Vroon, MichaƂ Lipski, and John Lindgren
+ * Copyright 2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,150 +17,126 @@
* the use of this software.
*/
-#include <glib.h>
-#include <glib/gstdio.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <glib.h>
+
#include <libaudcore/audstrings.h>
-#include "id3v1.h"
-#include "../util.h"
-#include <inttypes.h>
#include "../tag_module.h"
+#include "../util.h"
+
+#include "id3v1.h"
-bool_t id3v1_can_handle_file(VFSFile *f)
+#pragma pack(push)
+#pragma pack(1)
+
+typedef struct {
+ char header[3];
+ char title[30];
+ char artist[30];
+ char album[30];
+ char year[4];
+ char comment[30];
+ unsigned char genre;
+} ID3v1Tag;
+
+typedef struct {
+ char header[4];
+ char title[60];
+ char artist[60];
+ char album[60];
+ unsigned char speed;
+ char genre[30];
+ char start[6];
+ char end[6];
+} ID3v1Ext;
+
+#pragma pack(pop)
+
+static bool_t read_id3v1_tag (VFSFile * file, ID3v1Tag * tag)
{
- if (vfs_fseek(f, -128, SEEK_END))
+ if (vfs_fseek (file, -sizeof (ID3v1Tag), SEEK_END) < 0)
+ return FALSE;
+ if (vfs_fread (tag, 1, sizeof (ID3v1Tag), file) != sizeof (ID3v1Tag))
return FALSE;
- char *tag = read_char_data(f, 3);
-
- if (tag && !strncmp(tag, "TAG", 3))
- {
- g_free(tag);
- return TRUE;
- }
-
- g_free(tag);
- return FALSE;
+ return ! strncmp (tag->header, "TAG", 3);
}
-static char *convert_to_utf8(char *str)
+static bool_t read_id3v1_ext (VFSFile * file, ID3v1Ext * ext)
{
- if (!str)
- return NULL;
+ if (vfs_fseek (file, -(sizeof (ID3v1Ext) + sizeof (ID3v1Tag)), SEEK_END) < 0)
+ return FALSE;
+ if (vfs_fread (ext, 1, sizeof (ID3v1Ext), file) != sizeof (ID3v1Ext))
+ return FALSE;
- char *tmp = str;
- str = str_to_utf8(str);
- g_free(tmp);
+ return ! strncmp (ext->header, "TAG+", 4);
+}
- return g_strchomp(str);
+static bool_t id3v1_can_handle_file (VFSFile * file)
+{
+ ID3v1Tag tag;
+ return read_id3v1_tag (file, & tag);
}
-bool_t id3v1_read_tag (Tuple * tuple, VFSFile * f)
+static bool_t combine_string (Tuple * tuple, int field, const char * str1,
+ int size1, const char * str2, int size2)
{
- bool_t genre_set = FALSE;
+ char str[size1 + size2 + 1];
+
+ memset (str, 0, sizeof str);
+ strncpy (str, str1, size1);
+ strncpy (str + strlen (str), str2, size2);
+
+ g_strchomp (str);
- if (vfs_fseek(f, -125, SEEK_END))
+ if (! str[0])
return FALSE;
- char *title = read_char_data(f, 30);
- char *artist = read_char_data(f, 30);
- char *album = read_char_data(f, 30);
- char *year = read_char_data(f, 4);
- char *comment = read_char_data(f, 30);
- char *genre = read_char_data(f, 1);
- char track = 0;
+ char * utf8 = str_to_utf8 (str, -1);
+ if (! utf8)
+ return FALSE;
- if (comment && comment[28] == 0 && comment[29] != 0)
- track = comment[29];
+ tuple_set_str (tuple, field, utf8);
- title = convert_to_utf8(title);
- artist = convert_to_utf8(artist);
- album = convert_to_utf8(album);
- comment = convert_to_utf8(comment);
+ str_unref (utf8);
+ return TRUE;
+}
+
+static bool_t id3v1_read_tag (Tuple * tuple, VFSFile * file)
+{
+ ID3v1Tag tag;
+ ID3v1Ext ext;
- if (vfs_fseek(f, -355, SEEK_END))
+ if (! read_id3v1_tag (file, & tag))
return FALSE;
- char *tag = read_char_data(f, 4);
-
- if (tag && ! strncmp (tag, "TAG+", 4))
- {
- char *ext_title = convert_to_utf8(read_char_data(f, 60));
- char *ext_artist = convert_to_utf8(read_char_data(f, 60));
- char *ext_album = convert_to_utf8(read_char_data(f, 60));
- char *tmp_title = title ? g_strconcat(title, ext_title, NULL) : NULL;
- char *tmp_artist = artist ? g_strconcat(artist, ext_artist, NULL) : NULL;
- char *tmp_album = album ? g_strconcat(album, ext_album, NULL) : NULL;
- g_free(title);
- g_free(artist);
- g_free(album);
- g_free(ext_title);
- g_free(ext_artist);
- g_free(ext_album);
- title = tmp_title;
- artist = tmp_artist;
- album = tmp_album;
-
- if (vfs_fseek (f, -170, SEEK_END))
- goto ERR;
-
- char *ext_genre = convert_to_utf8(read_char_data(f, 30));
-
- if (ext_genre)
- {
- tuple_set_str(tuple, FIELD_GENRE, NULL, ext_genre);
- genre_set = TRUE;
- g_free(ext_genre);
- }
- }
-
- if (title)
- tuple_set_str(tuple, FIELD_TITLE, NULL, title);
- if (artist)
- tuple_set_str(tuple, FIELD_ARTIST, NULL, artist);
- if (album)
- tuple_set_str(tuple, FIELD_ALBUM, NULL, album);
- if (year)
- tuple_set_int(tuple, FIELD_YEAR, NULL, atoi(year));
- if (comment)
- tuple_set_str(tuple, FIELD_COMMENT, NULL, comment);
- if (track)
- tuple_set_int(tuple, FIELD_TRACK_NUMBER, NULL, track);
-
- if (genre && !genre_set)
- tuple_set_str(tuple, FIELD_GENRE, NULL, convert_numericgenre_to_text(*genre));
-
- g_free(title);
- g_free(artist);
- g_free(album);
- g_free(year);
- g_free(comment);
- g_free(genre);
+ if (! read_id3v1_ext (file, & ext))
+ memset (& ext, 0, sizeof (ID3v1Ext));
- return TRUE;
+ combine_string (tuple, FIELD_TITLE, tag.title, sizeof tag.title, ext.title, sizeof ext.title);
+ combine_string (tuple, FIELD_ARTIST, tag.artist, sizeof tag.artist, ext.artist, sizeof ext.artist);
+ combine_string (tuple, FIELD_ALBUM, tag.album, sizeof tag.album, ext.album, sizeof ext.album);
+ combine_string (tuple, FIELD_COMMENT, tag.comment, sizeof tag.comment, NULL, 0);
-ERR:
- g_free (title);
- g_free (artist);
- g_free (album);
- g_free (year);
- g_free (comment);
- g_free (genre);
- return FALSE;
-}
+ SNCOPY (year, tag.year, 4);
+ if (atoi (year))
+ tuple_set_int (tuple, FIELD_YEAR, atoi (year));
-static bool_t id3v1_write_tag (const Tuple * tuple, VFSFile * handle)
-{
- fprintf (stderr, "Writing ID3v1 tags is not implemented yet, sorry.\n");
- return FALSE;
+ if (! tag.comment[28] && tag.comment[29])
+ tuple_set_int (tuple, FIELD_TRACK_NUMBER, (unsigned char) tag.comment[29]);
+
+ if (! combine_string (tuple, FIELD_GENRE, ext.genre, sizeof ext.genre, NULL, 0))
+ tuple_set_str (tuple, FIELD_GENRE, convert_numericgenre_to_text (tag.genre));
+
+ return TRUE;
}
tag_module_t id3v1 = {
.name = "ID3v1",
.can_handle_file = id3v1_can_handle_file,
.read_tag = id3v1_read_tag,
- .write_tag = id3v1_write_tag,
};
diff --git a/src/libaudtag/id3/id3v22.c b/src/libaudtag/id3/id3v22.c
index 40cc345..7e886fd 100644
--- a/src/libaudtag/id3/id3v22.c
+++ b/src/libaudtag/id3/id3v22.c
@@ -1,5 +1,5 @@
/*
- * id3v24.c
+ * id3v22.c
* Copyright 2009-2011 Paula Stanciu, Tony Vroon, John Lindgren,
* and William Pitcock
*
@@ -135,7 +135,7 @@ static bool_t read_header (VFSFile * handle, int * version, bool_t *
}
static bool_t read_frame (VFSFile * handle, int max_size, int version,
- bool_t syncsafe, int * frame_size, char * key, unsigned char * * data, int * size)
+ bool_t syncsafe, int * frame_size, char * key, char * * data, int * size)
{
ID3v2FrameHeader header;
int i;
@@ -178,31 +178,6 @@ static bool_t read_frame (VFSFile * handle, int max_size, int version,
return TRUE;
}
-static char * decode_text_frame (const unsigned char * data, int size)
-{
- return convert_text ((const char *) data + 1, size - 1, data[0], FALSE,
- NULL, NULL);
-}
-
-static bool_t decode_comment_frame (const unsigned char * _data, int size, char * *
- lang, char * * type, char * * value)
-{
- const char * data = (const char *) _data;
- char * pair, * sep;
- int converted;
-
- pair = convert_text (data + 4, size - 4, data[0], FALSE, & converted, NULL);
-
- if (pair == NULL || (sep = memchr (pair, 0, converted)) == NULL)
- return FALSE;
-
- * lang = g_strndup (data + 1, 3);
- * type = g_strdup (pair);
- * value = g_strdup (sep + 1);
-
- g_free (pair);
- return TRUE;
-}
static int get_frame_id (const char * key)
{
@@ -217,218 +192,6 @@ static int get_frame_id (const char * key)
return -1;
}
-static void associate_string (Tuple * tuple, int field, const char *
- customfield, const unsigned char * data, int size)
-{
- char * text = decode_text_frame (data, size);
-
- if (text == NULL || ! text[0])
- {
- g_free (text);
- return;
- }
-
- if (customfield != NULL)
- TAGDBG ("Custom field %s = %s.\n", customfield, text);
- else
- TAGDBG ("Field %i = %s.\n", field, text);
-
- tuple_set_str (tuple, field, customfield, text);
- g_free (text);
-}
-
-static void associate_int (Tuple * tuple, int field, const char *
- customfield, const unsigned char * data, int size)
-{
- char * text = decode_text_frame (data, size);
-
- if (text == NULL || atoi (text) < 1)
- {
- g_free (text);
- return;
- }
-
- if (customfield != NULL)
- TAGDBG ("Custom field %s = %s.\n", customfield, text);
- else
- TAGDBG ("Field %i = %s.\n", field, text);
-
- tuple_set_int (tuple, field, customfield, atoi (text));
- g_free (text);
-}
-
-static void decode_comment (Tuple * tuple, const unsigned char * data, int size)
-{
- char * lang, * type, * value;
-
- if (! decode_comment_frame (data, size, & lang, & type, & value))
- return;
-
- TAGDBG ("Comment: lang = %s, type = %s, value = %s.\n", lang, type, value);
-
- if (! type[0]) /* blank type == actual comment */
- tuple_set_str (tuple, FIELD_COMMENT, NULL, value);
-
- g_free (lang);
- g_free (type);
- g_free (value);
-}
-
-static void decode_txx (Tuple * tuple, const unsigned char * data, int size)
-{
- char * text = decode_text_frame (data, size);
-
- if (text == NULL)
- return;
-
- char *separator = strchr(text, 0);
-
- if (separator == NULL)
- return;
-
- char * value = separator + 1;
- TAGDBG ("TXX: %s = %s.\n", text, value);
- tuple_set_str (tuple, -1, text, value);
-
- g_free (text);
-}
-
-static bool_t decode_rva_block (const unsigned char * * _data, int * _size, int *
- channel, int * adjustment, int * adjustment_unit, int * peak, int *
- peak_unit)
-{
- const unsigned char * data = * _data;
- int size = * _size;
- int peak_bits;
-
- if (size < 4)
- return FALSE;
-
- * channel = data[0];
- * adjustment = (char) data[1]; /* first byte is signed */
- * adjustment = (* adjustment << 8) | data[2];
- * adjustment_unit = 512;
- peak_bits = data[3];
-
- data += 4;
- size -= 4;
-
- TAGDBG ("RVA block: channel = %d, adjustment = %d/%d, peak bits = %d\n",
- * channel, * adjustment, * adjustment_unit, peak_bits);
-
- if (peak_bits > 0 && peak_bits < sizeof (int) * 8)
- {
- int bytes = (peak_bits + 7) / 8;
- int count;
-
- if (bytes > size)
- return FALSE;
-
- * peak = 0;
- * peak_unit = 1 << peak_bits;
-
- for (count = 0; count < bytes; count ++)
- * peak = (* peak << 8) | data[count];
-
- data += bytes;
- size -= count;
-
- TAGDBG ("RVA block: peak = %d/%d\n", * peak, * peak_unit);
- }
- else
- {
- * peak = 0;
- * peak_unit = 0;
- }
-
- * _data = data;
- * _size = size;
- return TRUE;
-}
-
-static void decode_rva (Tuple * tuple, const unsigned char * data, int size)
-{
- const char * domain;
- int channel, adjustment, adjustment_unit, peak, peak_unit;
-
- if (memchr (data, 0, size) == NULL)
- return;
-
- domain = (const char *) data;
-
- TAGDBG ("RVA domain: %s\n", domain);
-
- size -= strlen (domain) + 1;
- data += strlen (domain) + 1;
-
- while (size > 0)
- {
- if (! decode_rva_block (& data, & size, & channel, & adjustment,
- & adjustment_unit, & peak, & peak_unit))
- break;
-
- if (channel != 1) /* specific channel? */
- continue;
-
- if (tuple_get_value_type (tuple, FIELD_GAIN_GAIN_UNIT, NULL) ==
- TUPLE_INT)
- adjustment = adjustment * (int64_t) tuple_get_int (tuple,
- FIELD_GAIN_GAIN_UNIT, NULL) / adjustment_unit;
- else
- tuple_set_int (tuple, FIELD_GAIN_GAIN_UNIT, NULL,
- adjustment_unit);
-
- if (peak_unit)
- {
- if (tuple_get_value_type (tuple, FIELD_GAIN_PEAK_UNIT, NULL) ==
- TUPLE_INT)
- peak = peak * (int64_t) tuple_get_int (tuple,
- FIELD_GAIN_PEAK_UNIT, NULL) / peak_unit;
- else
- tuple_set_int (tuple, FIELD_GAIN_PEAK_UNIT, NULL,
- peak_unit);
- }
-
- if (! g_ascii_strcasecmp (domain, "album"))
- {
- tuple_set_int (tuple, FIELD_GAIN_ALBUM_GAIN, NULL, adjustment);
-
- if (peak_unit)
- tuple_set_int (tuple, FIELD_GAIN_ALBUM_PEAK, NULL, peak);
- }
- else if (! g_ascii_strcasecmp (domain, "track"))
- {
- tuple_set_int (tuple, FIELD_GAIN_TRACK_GAIN, NULL, adjustment);
-
- if (peak_unit)
- tuple_set_int (tuple, FIELD_GAIN_TRACK_PEAK, NULL, peak);
- }
- }
-}
-
-static void decode_genre (Tuple * tuple, const unsigned char * data, int size)
-{
- int numericgenre;
- char * text = decode_text_frame (data, size);
-
- if (text == NULL)
- return;
-
- if (text[0] == '(')
- numericgenre = atoi (text + 1);
- else
- numericgenre = atoi (text);
-
- if (numericgenre > 0)
- {
- tuple_set_str(tuple, FIELD_GENRE, NULL, convert_numericgenre_to_text(numericgenre));
- return;
- }
- tuple_set_str(tuple, FIELD_GENRE, NULL, text);
- g_free (text);
- return;
-}
-
static bool_t id3v22_can_handle_file (VFSFile * handle)
{
int version, header_size, data_size;
@@ -457,58 +220,55 @@ bool_t id3v22_read_tag (Tuple * tuple, VFSFile * handle)
{
int frame_size, size, id;
char key[5];
- unsigned char * data;
+ char * data;
if (! read_frame (handle, data_size - pos, version, syncsafe,
& frame_size, key, & data, & size))
- {
- TAGDBG("read_frame failed at pos %i\n", pos);
- break;
- }
+ {
+ TAGDBG("read_frame failed at pos %i\n", pos);
+ break;
+ }
id = get_frame_id (key);
switch (id)
{
case ID3_ALBUM:
- associate_string (tuple, FIELD_ALBUM, NULL, data, size);
+ id3_associate_string (tuple, FIELD_ALBUM, data, size);
break;
case ID3_TITLE:
- associate_string (tuple, FIELD_TITLE, NULL, data, size);
+ id3_associate_string (tuple, FIELD_TITLE, data, size);
break;
case ID3_COMPOSER:
- associate_string (tuple, FIELD_COMPOSER, NULL, data, size);
+ id3_associate_string (tuple, FIELD_COMPOSER, data, size);
break;
case ID3_COPYRIGHT:
- associate_string (tuple, FIELD_COPYRIGHT, NULL, data, size);
+ id3_associate_string (tuple, FIELD_COPYRIGHT, data, size);
break;
case ID3_DATE:
- associate_string (tuple, FIELD_DATE, NULL, data, size);
+ id3_associate_string (tuple, FIELD_DATE, data, size);
break;
case ID3_LENGTH:
- associate_int (tuple, FIELD_LENGTH, NULL, data, size);
+ id3_associate_int (tuple, FIELD_LENGTH, data, size);
break;
case ID3_FUCKO_ARTIST:
case ID3_ARTIST:
- associate_string (tuple, FIELD_ARTIST, NULL, data, size);
+ id3_associate_string (tuple, FIELD_ARTIST, data, size);
break;
case ID3_TRACKNR:
- associate_int (tuple, FIELD_TRACK_NUMBER, NULL, data, size);
+ id3_associate_int (tuple, FIELD_TRACK_NUMBER, data, size);
break;
case ID3_YEAR:
- associate_int (tuple, FIELD_YEAR, NULL, data, size);
+ id3_associate_int (tuple, FIELD_YEAR, data, size);
break;
case ID3_GENRE:
- decode_genre (tuple, data, size);
+ id3_decode_genre (tuple, data, size);
break;
case ID3_COMMENT:
- decode_comment (tuple, data, size);
- break;
- case ID3_TXX:
- decode_txx (tuple, data, size);
+ id3_decode_comment (tuple, data, size);
break;
case ID3_RVA:
- decode_rva (tuple, data, size);
+ id3_decode_rva (tuple, data, size);
break;
default:
TAGDBG ("Ignoring unsupported ID3 frame %s.\n", key);
@@ -522,30 +282,9 @@ bool_t id3v22_read_tag (Tuple * tuple, VFSFile * handle)
return TRUE;
}
-static bool_t parse_pic (const unsigned char * data, int size, char * * mime,
- int * type, void * * image_data, int * image_size)
+static bool_t id3v22_read_image (VFSFile * handle, void * * image_data, int64_t * image_size)
{
- const unsigned char * sep;
- const unsigned char * after;
-
- if (size < 2 || (sep = memchr (data + 1, 0, size - 2)) == NULL)
- return FALSE;
-
- after = sep + 2;
- * mime = g_strdup ((const char *) data + 1);
- * type = sep[1];
- * image_data = g_memdup (after, data + size - after);
- * image_size = data + size - after;
-
- TAGDBG ("PIC: mime = %s, type = %d, size = %d.\n", * mime,
- * type, * image_size);
- return TRUE;
-}
-
-static bool_t id3v22_read_image (VFSFile * handle, void * * image_data,
- int64_t * image_size64)
-{
- int version, header_size, data_size, parsed, image_size = 0;
+ int version, header_size, data_size, parsed;
bool_t syncsafe;
gsize offset;
bool_t found = FALSE;
@@ -558,8 +297,7 @@ static bool_t id3v22_read_image (VFSFile * handle, void * * image_data,
{
int frame_size, size, type;
char key[5];
- unsigned char * data;
- char * mime;
+ char * data;
int frame_length;
if (! read_frame (handle, data_size - parsed, version, syncsafe,
@@ -568,11 +306,9 @@ static bool_t id3v22_read_image (VFSFile * handle, void * * image_data,
frame_length = size;
- if (! strcmp (key, "PIC") && parse_pic (data, frame_length, & mime, & type,
- image_data, & image_size))
+ if (! strcmp (key, "PIC") && id3_decode_picture (data, frame_length,
+ & type, image_data, image_size))
{
- g_free (mime);
-
if (type == 3) /* album cover */
found = TRUE;
else if (type == 0) /* iTunes */
@@ -588,9 +324,6 @@ static bool_t id3v22_read_image (VFSFile * handle, void * * image_data,
parsed += frame_size;
}
- if (found)
- * image_size64 = image_size;
-
return found;
}
diff --git a/src/libaudtag/id3/id3v24.c b/src/libaudtag/id3/id3v24.c
index 15b78bd..fac1a44 100644
--- a/src/libaudtag/id3/id3v24.c
+++ b/src/libaudtag/id3/id3v24.c
@@ -1,6 +1,6 @@
/*
* id3v24.c
- * Copyright 2009-2011 Paula Stanciu, Tony Vroon, John Lindgren,
+ * Copyright 2009-2014 Paula Stanciu, Tony Vroon, John Lindgren,
* Mikael Magnusson, and MichaƂ Lipski
*
* Redistribution and use in source and binary forms, with or without
@@ -22,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include <libaudcore/audstrings.h>
@@ -95,7 +96,7 @@ ID3v2FrameHeader;
typedef struct
{
char key[5];
- unsigned char * data;
+ char * data;
int size;
}
GenericFrame;
@@ -197,6 +198,9 @@ static bool_t read_header (VFSFile * handle, int * version, bool_t *
if (! validate_header (& footer, TRUE))
return FALSE;
+ if (vfs_fseek (handle, sizeof (ID3v2Header), SEEK_SET))
+ return FALSE;
+
* footer_size = sizeof (ID3v2Header);
}
else
@@ -263,15 +267,15 @@ static bool_t read_header (VFSFile * handle, int * version, bool_t *
return TRUE;
}
-static int unsyncsafe (unsigned char * data, int size)
+static int unsyncsafe (char * data, int size)
{
- unsigned char * get = data, * set = data;
+ char * get = data, * set = data;
while (size --)
{
- unsigned char c = * set ++ = * get ++;
+ char c = * set ++ = * get ++;
- if (c == 0xff && size && ! get[0])
+ if (c == (char) 0xff && size && ! get[0])
{
size --;
get ++;
@@ -282,7 +286,7 @@ static int unsyncsafe (unsigned char * data, int size)
}
static bool_t read_frame (VFSFile * handle, int max_size, int version,
- bool_t syncsafe, int * frame_size, char * key, unsigned char * * data, int * size)
+ bool_t syncsafe, int * frame_size, char * key, char * * data, int * size)
{
ID3v2FrameHeader header;
int skip = 0;
@@ -339,44 +343,15 @@ static bool_t read_frame (VFSFile * handle, int max_size, int version,
return TRUE;
}
-static char * decode_text_frame (const unsigned char * data, int size)
-{
- return convert_text ((const char *) data + 1, size - 1, data[0], FALSE,
- NULL, NULL);
-}
-
-static bool_t decode_comment_frame (const unsigned char * _data, int size, char * *
- lang, char * * type, char * * value)
-{
- const char * data = (const char *) _data;
- char * pair, * sep;
- int converted;
-
- pair = convert_text (data + 4, size - 4, data[0], FALSE, & converted, NULL);
-
- if (pair == NULL || (sep = memchr (pair, 0, converted)) == NULL)
- return FALSE;
-
- * lang = g_strndup (data + 1, 3);
- * type = g_strdup (pair);
- * value = g_strdup (sep + 1);
-
- g_free (pair);
- return TRUE;
-}
-
static void free_frame (GenericFrame * frame)
{
g_free (frame->data);
- g_free (frame);
+ g_slice_free (GenericFrame, frame);
}
static void free_frame_list (GList * list)
{
- for (GList * node = list; node; node = node->next)
- free_frame (node->data);
-
- g_list_free (list);
+ g_list_free_full (list, (GDestroyNotify) free_frame);
}
static void read_all_frames (VFSFile * handle, int version, bool_t syncsafe,
@@ -388,7 +363,7 @@ static void read_all_frames (VFSFile * handle, int version, bool_t syncsafe,
{
int frame_size, size;
char key[5];
- unsigned char * data;
+ char * data;
GenericFrame * frame;
if (! read_frame (handle, data_size - pos, version, syncsafe,
@@ -397,7 +372,7 @@ static void read_all_frames (VFSFile * handle, int version, bool_t syncsafe,
pos += frame_size;
- frame = g_malloc (sizeof (GenericFrame));
+ frame = g_slice_new (GenericFrame);
strcpy (frame->key, key);
frame->data = data;
frame->size = size;
@@ -414,23 +389,23 @@ static void read_all_frames (VFSFile * handle, int version, bool_t syncsafe,
}
}
-static bool_t write_frame (VFSFile * handle, GenericFrame * frame, int *
- frame_size)
+static bool_t write_frame (int fd, GenericFrame * frame, int version, int * frame_size)
{
TAGDBG ("Writing frame %s, size %d\n", frame->key, frame->size);
ID3v2FrameHeader header;
memcpy (header.key, frame->key, 4);
- header.size = syncsafe32 (frame->size);
- header.size = GUINT32_TO_BE (header.size);
+
+ uint32_t size = (version == 3) ? frame->size : syncsafe32 (frame->size);
+ header.size = GUINT32_TO_BE (size);
+
header.flags = 0;
- if (vfs_fwrite (& header, 1, sizeof (ID3v2FrameHeader), handle) != sizeof
- (ID3v2FrameHeader))
+ if (write (fd, & header, sizeof (ID3v2FrameHeader)) != sizeof (ID3v2FrameHeader))
return FALSE;
- if (vfs_fwrite (frame->data, 1, frame->size, handle) != frame->size)
+ if (write (fd, frame->data, frame->size) != frame->size)
return FALSE;
* frame_size = sizeof (ID3v2FrameHeader) + frame->size;
@@ -438,7 +413,8 @@ static bool_t write_frame (VFSFile * handle, GenericFrame * frame, int *
}
typedef struct {
- VFSFile * file;
+ int fd;
+ int version;
int written_size;
} WriteState;
@@ -449,33 +425,32 @@ static void write_frame_list (void * key, void * list, void * user)
for (GList * node = list; node; node = node->next)
{
int size;
- if (write_frame (state->file, node->data, & size))
+ if (write_frame (state->fd, node->data, state->version, & size))
state->written_size += size;
}
}
-static int write_all_frames (VFSFile * handle, GHashTable * dict)
+static int write_all_frames (int fd, GHashTable * dict, int version)
{
- WriteState state = {handle, 0};
+ WriteState state = {fd, version, 0};
g_hash_table_foreach (dict, write_frame_list, & state);
TAGDBG ("Total frame bytes written = %d.\n", state.written_size);
return state.written_size;
}
-static bool_t write_header (VFSFile * handle, int size, bool_t is_footer)
+static bool_t write_header (int fd, int version, int size)
{
ID3v2Header header;
- memcpy (header.magic, is_footer ? "3DI" : "ID3", 3);
- header.version = 4;
+ memcpy (header.magic, "ID3", 3);
+ header.version = version;
header.revision = 0;
- header.flags = ID3_HEADER_HAS_FOOTER;
+ header.flags = 0;
header.size = syncsafe32 (size);
header.size = GUINT32_TO_BE (header.size);
- return vfs_fwrite (& header, 1, sizeof (ID3v2Header), handle) == sizeof
- (ID3v2Header);
+ return write (fd, & header, sizeof (ID3v2Header)) == sizeof (ID3v2Header);
}
static int get_frame_id (const char * key)
@@ -491,46 +466,7 @@ static int get_frame_id (const char * key)
return -1;
}
-static void associate_string (Tuple * tuple, int field, const char *
- customfield, const unsigned char * data, int size)
-{
- char * text = decode_text_frame (data, size);
-
- if (text == NULL || ! text[0])
- {
- g_free (text);
- return;
- }
-
- if (customfield != NULL)
- TAGDBG ("Custom field %s = %s.\n", customfield, text);
- else
- TAGDBG ("Field %i = %s.\n", field, text);
-
- tuple_set_str (tuple, field, customfield, text);
- g_free (text);
-}
-
-static void associate_int (Tuple * tuple, int field, const char *
- customfield, const unsigned char * data, int size)
-{
- char * text = decode_text_frame (data, size);
-
- if (text == NULL || atoi (text) < 1)
- {
- g_free (text);
- return;
- }
-
- if (customfield != NULL)
- TAGDBG ("Custom field %s = %s.\n", customfield, text);
- else
- TAGDBG ("Field %i = %s.\n", field, text);
-
- tuple_set_int (tuple, field, customfield, atoi (text));
- g_free (text);
-}
-
+#if 0
static void decode_private_info (Tuple * tuple, const unsigned char * data, int size)
{
char * text = g_strndup ((const char *) data, size);
@@ -572,164 +508,12 @@ static void decode_private_info (Tuple * tuple, const unsigned char * data, int
DONE:
g_free (text);
}
-
-static void decode_comment (Tuple * tuple, const unsigned char * data, int size)
-{
- char * lang, * type, * value;
-
- if (! decode_comment_frame (data, size, & lang, & type, & value))
- return;
-
- TAGDBG ("Comment: lang = %s, type = %s, value = %s.\n", lang, type, value);
-
- if (! type[0]) /* blank type == actual comment */
- tuple_set_str (tuple, FIELD_COMMENT, NULL, value);
-
- g_free (lang);
- g_free (type);
- g_free (value);
-}
-
-static bool_t decode_rva2_block (const unsigned char * * _data, int * _size, int *
- channel, int * adjustment, int * adjustment_unit, int * peak, int *
- peak_unit)
-{
- const unsigned char * data = * _data;
- int size = * _size;
- int peak_bits;
-
- if (size < 4)
- return FALSE;
-
- * channel = data[0];
- * adjustment = (char) data[1]; /* first byte is signed */
- * adjustment = (* adjustment << 8) | data[2];
- * adjustment_unit = 512;
- peak_bits = data[3];
-
- data += 4;
- size -= 4;
-
- TAGDBG ("RVA2 block: channel = %d, adjustment = %d/%d, peak bits = %d\n",
- * channel, * adjustment, * adjustment_unit, peak_bits);
-
- if (peak_bits > 0 && peak_bits < sizeof (int) * 8)
- {
- int bytes = (peak_bits + 7) / 8;
- int count;
-
- if (bytes > size)
- return FALSE;
-
- * peak = 0;
- * peak_unit = 1 << peak_bits;
-
- for (count = 0; count < bytes; count ++)
- * peak = (* peak << 8) | data[count];
-
- data += bytes;
- size -= count;
-
- TAGDBG ("RVA2 block: peak = %d/%d\n", * peak, * peak_unit);
- }
- else
- {
- * peak = 0;
- * peak_unit = 0;
- }
-
- * _data = data;
- * _size = size;
- return TRUE;
-}
-
-static void decode_rva2 (Tuple * tuple, const unsigned char * data, int size)
-{
- const char * domain;
- int channel, adjustment, adjustment_unit, peak, peak_unit;
-
- if (memchr (data, 0, size) == NULL)
- return;
-
- domain = (const char *) data;
-
- TAGDBG ("RVA2 domain: %s\n", domain);
-
- size -= strlen (domain) + 1;
- data += strlen (domain) + 1;
-
- while (size > 0)
- {
- if (! decode_rva2_block (& data, & size, & channel, & adjustment,
- & adjustment_unit, & peak, & peak_unit))
- break;
-
- if (channel != 1) /* specific channel? */
- continue;
-
- if (tuple_get_value_type (tuple, FIELD_GAIN_GAIN_UNIT, NULL) ==
- TUPLE_INT)
- adjustment = adjustment * (int64_t) tuple_get_int (tuple,
- FIELD_GAIN_GAIN_UNIT, NULL) / adjustment_unit;
- else
- tuple_set_int (tuple, FIELD_GAIN_GAIN_UNIT, NULL,
- adjustment_unit);
-
- if (peak_unit)
- {
- if (tuple_get_value_type (tuple, FIELD_GAIN_PEAK_UNIT, NULL) ==
- TUPLE_INT)
- peak = peak * (int64_t) tuple_get_int (tuple,
- FIELD_GAIN_PEAK_UNIT, NULL) / peak_unit;
- else
- tuple_set_int (tuple, FIELD_GAIN_PEAK_UNIT, NULL,
- peak_unit);
- }
-
- if (! g_ascii_strcasecmp (domain, "album"))
- {
- tuple_set_int (tuple, FIELD_GAIN_ALBUM_GAIN, NULL, adjustment);
-
- if (peak_unit)
- tuple_set_int (tuple, FIELD_GAIN_ALBUM_PEAK, NULL, peak);
- }
- else if (! g_ascii_strcasecmp (domain, "track"))
- {
- tuple_set_int (tuple, FIELD_GAIN_TRACK_GAIN, NULL, adjustment);
-
- if (peak_unit)
- tuple_set_int (tuple, FIELD_GAIN_TRACK_PEAK, NULL, peak);
- }
- }
-}
-
-static void decode_genre (Tuple * tuple, const unsigned char * data, int size)
-{
- int numericgenre;
- char * text = decode_text_frame (data, size);
-
- if (text == NULL)
- return;
-
- if (text[0] == '(')
- numericgenre = atoi (text + 1);
- else
- numericgenre = atoi (text);
-
- if (numericgenre > 0)
- tuple_set_str (tuple, FIELD_GENRE, NULL,
- convert_numericgenre_to_text (numericgenre));
- else
- tuple_set_str (tuple, FIELD_GENRE, NULL, text);
-
- g_free (text);
- return;
-}
+#endif
static GenericFrame * add_generic_frame (int id, int size,
GHashTable * dict)
{
- GenericFrame * frame = g_malloc (sizeof (GenericFrame));
+ GenericFrame * frame = g_slice_new (GenericFrame);
strcpy (frame->key, id3_frames[id]);
frame->data = g_malloc (size);
@@ -756,11 +540,18 @@ static void add_text_frame (int id, const char * text, GHashTable * dict)
}
TAGDBG ("Adding text frame %s = %s.\n", id3_frames[id], text);
- int length = strlen (text);
- GenericFrame * frame = add_generic_frame (id, length + 1, dict);
- frame->data[0] = 3; /* UTF-8 encoding */
- memcpy (frame->data + 1, text, length);
+ long words;
+ uint16_t * utf16 = g_utf8_to_utf16 (text, -1, NULL, & words, NULL);
+ g_return_if_fail (utf16);
+
+ GenericFrame * frame = add_generic_frame (id, 3 + 2 * words, dict);
+
+ frame->data[0] = 1; /* UTF-16 encoding */
+ * (uint16_t *) (frame->data + 1) = 0xfeff; /* byte order mark */
+ memcpy (frame->data + 3, utf16, 2 * words);
+
+ g_free (utf16);
}
static void add_comment_frame (const char * text, GHashTable * dict)
@@ -772,18 +563,27 @@ static void add_comment_frame (const char * text, GHashTable * dict)
}
TAGDBG ("Adding comment frame = %s.\n", text);
- int length = strlen (text);
- GenericFrame * frame = add_generic_frame (ID3_COMMENT, length + 5, dict);
- frame->data[0] = 3; /* UTF-8 encoding */
- strcpy ((char *) frame->data + 1, "eng"); /* well, it *might* be English */
- memcpy (frame->data + 5, text, length);
+ long words;
+ uint16_t * utf16 = g_utf8_to_utf16 (text, -1, NULL, & words, NULL);
+ g_return_if_fail (utf16);
+
+ GenericFrame * frame = add_generic_frame (ID3_COMMENT, 10 + 2 * words, dict);
+
+ frame->data[0] = 1; /* UTF-16 encoding */
+ memcpy (frame->data + 1, "eng", 3); /* language */
+ * (uint16_t *) (frame->data + 4) = 0xfeff; /* byte order mark */
+ * (uint16_t *) (frame->data + 6) = 0; /* end of content description */
+ * (uint16_t *) (frame->data + 8) = 0xfeff; /* byte order mark */
+ memcpy (frame->data + 10, utf16, 2 * words);
+
+ g_free (utf16);
}
static void add_frameFromTupleStr (const Tuple * tuple, int field, int
id3_field, GHashTable * dict)
{
- char * str = tuple_get_str (tuple, field, NULL);
+ char * str = tuple_get_str (tuple, field);
add_text_frame (id3_field, str, dict);
str_unref (str);
}
@@ -791,14 +591,14 @@ static void add_frameFromTupleStr (const Tuple * tuple, int field, int
static void add_frameFromTupleInt (const Tuple * tuple, int field, int
id3_field, GHashTable * dict)
{
- if (tuple_get_value_type (tuple, field, NULL) != TUPLE_INT)
+ if (tuple_get_value_type (tuple, field) != TUPLE_INT)
{
remove_frame (id3_field, dict);
return;
}
char scratch[16];
- snprintf (scratch, sizeof scratch, "%d", tuple_get_int (tuple, field, NULL));
+ str_itoa (tuple_get_int (tuple, field), scratch, sizeof scratch);
add_text_frame (id3_field, scratch, dict);
}
@@ -827,7 +627,7 @@ static bool_t id3v24_read_tag (Tuple * tuple, VFSFile * handle)
{
int frame_size, size, id;
char key[5];
- unsigned char * data;
+ char * data;
if (! read_frame (handle, data_size - pos, version, syncsafe,
& frame_size, key, & data, & size))
@@ -838,44 +638,46 @@ static bool_t id3v24_read_tag (Tuple * tuple, VFSFile * handle)
switch (id)
{
case ID3_ALBUM:
- associate_string (tuple, FIELD_ALBUM, NULL, data, size);
+ id3_associate_string (tuple, FIELD_ALBUM, data, size);
break;
case ID3_TITLE:
- associate_string (tuple, FIELD_TITLE, NULL, data, size);
+ id3_associate_string (tuple, FIELD_TITLE, data, size);
break;
case ID3_COMPOSER:
- associate_string (tuple, FIELD_COMPOSER, NULL, data, size);
+ id3_associate_string (tuple, FIELD_COMPOSER, data, size);
break;
case ID3_COPYRIGHT:
- associate_string (tuple, FIELD_COPYRIGHT, NULL, data, size);
+ id3_associate_string (tuple, FIELD_COPYRIGHT, data, size);
break;
case ID3_DATE:
- associate_string (tuple, FIELD_DATE, NULL, data, size);
+ id3_associate_string (tuple, FIELD_DATE, data, size);
break;
case ID3_LENGTH:
- associate_int (tuple, FIELD_LENGTH, NULL, data, size);
+ id3_associate_int (tuple, FIELD_LENGTH, data, size);
break;
case ID3_ARTIST:
- associate_string (tuple, FIELD_ARTIST, NULL, data, size);
+ id3_associate_string (tuple, FIELD_ARTIST, data, size);
break;
case ID3_TRACKNR:
- associate_int (tuple, FIELD_TRACK_NUMBER, NULL, data, size);
+ id3_associate_int (tuple, FIELD_TRACK_NUMBER, data, size);
break;
case ID3_YEAR:
case ID3_RECORDING_TIME:
- associate_int (tuple, FIELD_YEAR, NULL, data, size);
+ id3_associate_int (tuple, FIELD_YEAR, data, size);
break;
case ID3_GENRE:
- decode_genre (tuple, data, size);
+ id3_decode_genre (tuple, data, size);
break;
case ID3_COMMENT:
- decode_comment (tuple, data, size);
+ id3_decode_comment (tuple, data, size);
break;
+#if 0
case ID3_PRIVATE:
decode_private_info (tuple, data, size);
break;
+#endif
case ID3_RVA2:
- decode_rva2 (tuple, data, size);
+ id3_decode_rva (tuple, data, size);
break;
default:
TAGDBG ("Ignoring unsupported ID3 frame %s.\n", key);
@@ -889,33 +691,10 @@ static bool_t id3v24_read_tag (Tuple * tuple, VFSFile * handle)
return TRUE;
}
-static bool_t parse_apic (const unsigned char * _data, int size, char * * mime,
- int * type, char * * desc, void * * image_data, int * image_size)
-{
- const char * data = (const char *) _data;
- const char * sep, * after;
-
- if (size < 2 || (sep = memchr (data + 1, 0, size - 2)) == NULL)
- return FALSE;
-
- if ((* desc = convert_text (sep + 2, data + size - sep - 2, data[0], TRUE,
- NULL, & after)) == NULL)
- return FALSE;
-
- * mime = g_strdup (data + 1);
- * type = sep[1];
- * image_data = g_memdup (after, data + size - after);
- * image_size = data + size - after;
-
- TAGDBG ("APIC: mime = %s, type = %d, desc = %s, size = %d.\n", * mime,
- * type, * desc, * image_size);
- return TRUE;
-}
-
static bool_t id3v24_read_image (VFSFile * handle, void * * image_data,
- int64_t * image_size64)
+ int64_t * image_size)
{
- int version, header_size, data_size, footer_size, parsed, image_size = 0;
+ int version, header_size, data_size, footer_size, parsed;
bool_t syncsafe;
int64_t offset;
bool_t found = FALSE;
@@ -928,19 +707,15 @@ static bool_t id3v24_read_image (VFSFile * handle, void * * image_data,
{
int frame_size, size, type;
char key[5];
- unsigned char * data;
- char * mime, * desc;
+ char * data;
if (! read_frame (handle, data_size - parsed, version, syncsafe,
& frame_size, key, & data, & size))
break;
- if (! strcmp (key, "APIC") && parse_apic (data, size, & mime, & type,
- & desc, image_data, & image_size))
+ if (! strcmp (key, "APIC") && id3_decode_picture (data, size, & type,
+ image_data, image_size))
{
- g_free (mime);
- g_free (desc);
-
if (type == 3) /* album cover */
found = TRUE;
else if (type == 0) /* iTunes */
@@ -956,26 +731,22 @@ static bool_t id3v24_read_image (VFSFile * handle, void * * image_data,
parsed += frame_size;
}
- if (found)
- * image_size64 = image_size;
-
return found;
}
static bool_t id3v24_write_tag (const Tuple * tuple, VFSFile * f)
{
- int version, header_size, data_size, footer_size;
+ int version = 3;
+ int header_size, data_size, footer_size;
bool_t syncsafe;
int64_t offset;
- if (! read_header (f, & version, & syncsafe, & offset, & header_size,
- & data_size, & footer_size))
- return FALSE;
-
//read all frames into generic frames;
GHashTable * dict = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify) str_unref, (GDestroyNotify) free_frame_list);
- read_all_frames (f, version, syncsafe, data_size, dict);
+
+ if (read_header (f, & version, & syncsafe, & offset, & header_size, & data_size, & footer_size))
+ read_all_frames (f, version, syncsafe, data_size, dict);
//make the new frames from tuple and replace in the dictionary the old frames with the new ones
add_frameFromTupleStr (tuple, FIELD_TITLE, ID3_TITLE, dict);
@@ -985,46 +756,50 @@ static bool_t id3v24_write_tag (const Tuple * tuple, VFSFile * f)
add_frameFromTupleInt (tuple, FIELD_TRACK_NUMBER, ID3_TRACKNR, dict);
add_frameFromTupleStr (tuple, FIELD_GENRE, ID3_GENRE, dict);
- char * comment = tuple_get_str (tuple, FIELD_COMMENT, NULL);
+ char * comment = tuple_get_str (tuple, FIELD_COMMENT);
add_comment_frame (comment, dict);
str_unref (comment);
- if (! offset)
- {
- if (! cut_beginning_tag (f, header_size + data_size + footer_size))
- goto ERR;
- }
- else
- {
- if (offset + header_size + data_size + footer_size != vfs_fsize (f))
- goto ERR;
- if (vfs_ftruncate (f, offset))
- goto ERR;
- }
+ /* location and size of non-tag data */
+ int64_t mp3_offset = offset ? 0 : header_size + data_size + footer_size;
+ int64_t mp3_size = offset ? offset : -1;
- offset = vfs_fsize (f);
+ TempFile temp = {0};
+ if (! open_temp_file_for (& temp, f))
+ goto ERR;
- if (offset < 0 || vfs_fseek (f, offset, SEEK_SET) || ! write_header (f, 0,
- FALSE))
+ /* write empty header (will be overwritten later) */
+ if (! write_header (temp.fd, version, 0))
goto ERR;
- data_size = write_all_frames (f, dict);
+ /* write tag data */
+ data_size = write_all_frames (temp.fd, dict, version);
+
+ /* copy non-tag data */
+ if (! copy_region_to_temp_file (& temp, f, mp3_offset, mp3_size))
+ goto ERR;
+
+ /* go back to beginning and write real header */
+ if (lseek (temp.fd, 0, SEEK_SET) < 0 || ! write_header (temp.fd, version, data_size))
+ goto ERR;
- if (! write_header (f, data_size, TRUE) || vfs_fseek (f, offset, SEEK_SET)
- || ! write_header (f, data_size, FALSE))
+ if (! replace_with_temp_file (& temp, f))
goto ERR;
g_hash_table_destroy (dict);
+ str_unref (temp.name);
return TRUE;
ERR:
g_hash_table_destroy (dict);
+ str_unref (temp.name);
return FALSE;
}
tag_module_t id3v24 =
{
.name = "ID3v2.3/4",
+ .type = TAG_TYPE_ID3V2,
.can_handle_file = id3v24_can_handle_file,
.read_tag = id3v24_read_tag,
.read_image = id3v24_read_image,
diff --git a/src/libaudtag/tag_module.c b/src/libaudtag/tag_module.c
index 90f6806..560c5b8 100644
--- a/src/libaudtag/tag_module.c
+++ b/src/libaudtag/tag_module.c
@@ -32,13 +32,12 @@
#include "ape/ape.h"
static tag_module_t * const modules[] = {& id3v24, & id3v22, & ape, & id3v1};
-#define N_MODULES (sizeof modules / sizeof modules[0])
tag_module_t * find_tag_module (VFSFile * fd, int new_type)
{
int i;
- for (i = 0; i < N_MODULES; i ++)
+ for (i = 0; i < ARRAY_LEN (modules); i ++)
{
if (vfs_fseek(fd, 0, SEEK_SET))
{
@@ -56,7 +55,7 @@ tag_module_t * find_tag_module (VFSFile * fd, int new_type)
/* No existing tag; see if we can create a new one. */
if (new_type != TAG_TYPE_NONE)
{
- for (i = 0; i < N_MODULES; i ++)
+ for (i = 0; i < ARRAY_LEN (modules); i ++)
{
if (modules[i]->type == new_type)
return modules[i];
diff --git a/src/libaudtag/util.c b/src/libaudtag/util.c
index 15ffe55..5abd24b 100644
--- a/src/libaudtag/util.c
+++ b/src/libaudtag/util.c
@@ -1,6 +1,6 @@
/*
* util.c
- * Copyright 2009-2011 Paula Stanciu, Tony Vroon, and John Lindgren
+ * Copyright 2009-2014 Paula Stanciu, Tony Vroon, and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,165 +17,15 @@
* the use of this software.
*/
-#include <glib.h>
#include <stdio.h>
+#include <unistd.h>
-#include "util.h"
-#include <inttypes.h>
-
-/* convert windows time to unix time */
-time_t unix_time(uint64_t win_time)
-{
- uint64_t t = (uint64_t) ((win_time / 10000000LL) - 11644473600LL);
- return (time_t) t;
-}
-
-uint16_t get_year(uint64_t win_time)
-{
- GDate *d = g_date_new();
- g_date_set_time_t(d, unix_time(win_time));
- uint16_t year = g_date_get_year(d);
- g_date_free(d);
- return year;
-}
-
-char * read_char_data (VFSFile * file, int size)
-{
- char * value = g_malloc (size + 1);
- if (vfs_fread (value, 1, size, file) < size)
- {
- g_free (value);
- return NULL;
- }
- value[size] = 0;
- return value;
-}
-
-bool_t write_char_data(VFSFile * f, char * data, size_t i)
-{
- return (vfs_fwrite(data, i, 1, f) == i);
-}
-
-uint8_t read_uint8(VFSFile * fd)
-{
- uint16_t i;
- if (vfs_fread(&i, 1, 1, fd) == 1)
- {
- return i;
- }
- return -1;
-}
-
-uint16_t read_LEuint16(VFSFile * fd)
-{
- uint16_t a;
- if (vfs_fget_le16(&a, fd))
- return a;
- else
- return -1;
-}
-
-uint16_t read_BEuint16(VFSFile * fd)
-{
- uint16_t a;
- if (vfs_fget_be16(&a, fd))
- return a;
- else
- return -1;
-}
-
-uint32_t read_LEuint32(VFSFile * fd)
-{
- uint32_t a;
- if (vfs_fget_le32(&a, fd))
- return a;
- else
- return -1;
-}
-
-uint32_t read_BEuint32(VFSFile * fd)
-{
- uint32_t a;
- if (vfs_fget_be32(&a, fd))
- return a;
- else
- return -1;
-}
-
-uint64_t read_LEuint64(VFSFile * fd)
-{
- uint64_t a;
- if (vfs_fget_le64(&a, fd))
- return a;
- else
- return -1;
-}
-
-uint64_t read_BEuint64(VFSFile * fd)
-{
- uint64_t a;
- if (vfs_fget_be64(&a, fd))
- return a;
- else
- return 1;
-}
-
-bool_t write_uint8(VFSFile * fd, uint8_t val)
-{
- return (vfs_fwrite(&val, 1, 1, fd) == 1);
-}
-
-bool_t write_LEuint16(VFSFile * fd, uint16_t val)
-{
- uint16_t le_val = GUINT32_TO_LE(val);
- return (vfs_fwrite(&le_val, 2, 1, fd) == 2);
-}
-
-bool_t write_BEuint32(VFSFile * fd, uint32_t val)
-{
- uint32_t be_val = GUINT32_TO_BE(val);
- return (vfs_fwrite(&be_val, 4, 1, fd) == 4);
-}
-
-bool_t write_LEuint32(VFSFile * fd, uint32_t val)
-{
- uint32_t le_val = GUINT32_TO_LE(val);
- return (vfs_fwrite(&le_val, 4, 1, fd) == 4);
-}
-
-bool_t write_LEuint64(VFSFile * fd, uint64_t val)
-{
- uint64_t le_val = GUINT64_TO_LE(val);
- return (vfs_fwrite(&le_val, 8, 1, fd) == 8);
-}
-
-bool_t cut_beginning_tag (VFSFile * handle, int64_t tag_size)
-{
- unsigned char buffer[16384];
- gsize offset = 0, readed;
-
- if (! tag_size)
- return TRUE;
-
- do
- {
- if (vfs_fseek (handle, offset + tag_size, SEEK_SET))
- return FALSE;
-
- readed = vfs_fread (buffer, 1, sizeof buffer, handle);
-
- if (vfs_fseek (handle, offset, SEEK_SET))
- return FALSE;
+#include <glib.h>
+#include <glib/gstdio.h>
- if (vfs_fwrite (buffer, 1, readed, handle) != readed)
- return FALSE;
+#include <libaudcore/audstrings.h>
- offset += readed;
- }
- while (readed);
-
- return vfs_ftruncate (handle, offset) == 0;
-}
+#include "util.h"
const char *convert_numericgenre_to_text(int numericgenre)
{
@@ -315,12 +165,10 @@ const char *convert_numericgenre_to_text(int numericgenre)
int count;
- for (count = 0; count < G_N_ELEMENTS(table); count++)
+ for (count = 0; count < ARRAY_LEN (table); count++)
{
if (table[count].numericgenre == numericgenre)
- {
- return table[count].genre;
- }
+ return table[count].genre;
}
return "Unknown";
@@ -338,3 +186,90 @@ uint32_t syncsafe32 (uint32_t x)
0xfe00000) << 3);
}
+bool_t open_temp_file_for (TempFile * temp, VFSFile * file)
+{
+ char * template = filename_build (g_get_tmp_dir (), "audacious-temp-XXXXXX");
+ SCOPY (tempname, template);
+ str_unref (template);
+
+ temp->fd = g_mkstemp (tempname);
+ if (temp->fd < 0)
+ return FALSE;
+
+ temp->name = str_get (tempname);
+
+ return TRUE;
+}
+
+bool_t copy_region_to_temp_file (TempFile * temp, VFSFile * file, int64_t offset, int64_t size)
+{
+ if (vfs_fseek (file, offset, SEEK_SET) < 0)
+ return FALSE;
+
+ char buf[16384];
+
+ while (size < 0 || size > 0)
+ {
+ int64_t readsize;
+
+ if (size > 0)
+ {
+ readsize = MIN (size, sizeof buf);
+ if (vfs_fread (buf, 1, readsize, file) != readsize)
+ return FALSE;
+
+ size -= readsize;
+ }
+ else
+ {
+ /* negative size means copy to EOF */
+ readsize = vfs_fread (buf, 1, sizeof buf, file);
+ if (! readsize)
+ break;
+ }
+
+ int64_t written = 0;
+ while (written < readsize)
+ {
+ int64_t writesize = write (temp->fd, buf + written, readsize - written);
+ if (writesize <= 0)
+ return FALSE;
+
+ written += writesize;
+ }
+ }
+
+ return TRUE;
+}
+
+bool_t replace_with_temp_file (TempFile * temp, VFSFile * file)
+{
+ if (lseek (temp->fd, 0, SEEK_SET) < 0)
+ return FALSE;
+
+ if (vfs_fseek (file, 0, SEEK_SET) < 0)
+ return FALSE;
+
+ if (vfs_ftruncate (file, 0) < 0)
+ return FALSE;
+
+ char buf[16384];
+
+ while (1)
+ {
+ int64_t readsize = read (temp->fd, buf, sizeof buf);
+ if (readsize < 0)
+ return FALSE;
+
+ if (readsize == 0)
+ break;
+
+ if (vfs_fwrite (buf, 1, readsize, file) != readsize)
+ return FALSE;
+ }
+
+ close (temp->fd);
+ g_unlink (temp->name);
+
+ return TRUE;
+}
diff --git a/src/libaudtag/util.h b/src/libaudtag/util.h
index 27cfd80..3b80ad7 100644
--- a/src/libaudtag/util.h
+++ b/src/libaudtag/util.h
@@ -18,17 +18,13 @@
*/
#ifndef TAGUTIL_H
-
#define TAGUTIL_H
#include <stdint.h>
-#include <time.h>
+#include <stdio.h>
-#include "libaudcore/tuple.h"
#include "libaudcore/vfs.h"
-#define BROKEN 1
-
enum {
GENRE_BLUES = 0,
GENRE_CLASSIC_ROCK,
@@ -161,36 +157,18 @@ extern bool_t tag_verbose;
#define TAGDBG(...) do {if (tag_verbose) {printf ("%s:%d [%s]: ", __FILE__, __LINE__, __FUNCTION__); printf (__VA_ARGS__);}} while (0)
-time_t unix_time(uint64_t win_time);
-
-uint16_t get_year(uint64_t win_time);
-
-char *read_char_data(VFSFile *fd, int size);
-bool_t write_char_data(VFSFile *f, char *data, size_t i);
-
-uint8_t read_uint8(VFSFile *fd);
-uint16_t read_LEuint16(VFSFile *fd);
-uint16_t read_BEuint16(VFSFile *fd);
-uint32_t read_LEuint32(VFSFile *fd);
-uint32_t read_BEuint32(VFSFile *fd);
-uint64_t read_LEuint64(VFSFile *fd);
-uint64_t read_BEuint64(VFSFile *fd);
-
-
-bool_t write_uint8(VFSFile *fd, uint8_t val);
-bool_t write_BEuint16(VFSFile *fd, uint16_t val);
-bool_t write_LEuint16(VFSFile *fd, uint16_t val);
-bool_t write_BEuint32(VFSFile *fd, uint32_t val);
-bool_t write_LEuint32(VFSFile *fd, uint32_t val);
-bool_t write_BEuint64(VFSFile *fd, uint64_t val);
-bool_t write_LEuint64(VFSFile *fd, uint64_t val);
-
-uint64_t read_LEint64(VFSFile *fd);
-bool_t cut_beginning_tag (VFSFile * handle, int64_t tag_size);
-
const char *convert_numericgenre_to_text(int numericgenre);
uint32_t unsyncsafe32 (uint32_t x);
uint32_t syncsafe32 (uint32_t x);
-#endif
+typedef struct {
+ char * name;
+ int fd;
+} TempFile;
+
+bool_t open_temp_file_for (TempFile * temp, VFSFile * file);
+bool_t copy_region_to_temp_file (TempFile * temp, VFSFile * file, int64_t offset, int64_t size);
+bool_t replace_with_temp_file (TempFile * temp, VFSFile * file);
+
+#endif /* TAGUTIL_H */