Mercurial > audlegacy
changeset 3385:d1b43bf0e67d
merge
author | Cristi Magherusan <majeru@atheme-project.org> |
---|---|
date | Thu, 09 Aug 2007 16:28:42 +0300 |
parents | 7ac9c5c6b44e (current diff) a97fb19a0148 (diff) |
children | 3e9bc5fd5c36 |
files | src/audacious/Makefile |
diffstat | 21 files changed, 1165 insertions(+), 131 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Wed Aug 08 22:04:41 2007 +0300 +++ b/.hgtags Thu Aug 09 16:28:42 2007 +0300 @@ -1,2 +1,3 @@ 55a4a6da92a5e5bc68e352e47e0c9259818a1f92 audacious-1.4.0-dr1 40b4b64dfb42c81a6677532097c41037026b0b86 audacious-1.4.0-DR1 +91a5f34b07803a8bae1e162446e40c08ddf33273 audacious-1.4.0-dr2
--- a/src/audacious/Makefile Wed Aug 08 22:04:41 2007 +0300 +++ b/src/audacious/Makefile Thu Aug 09 16:28:42 2007 +0300 @@ -53,6 +53,7 @@ plugin.h \ strings.h \ titlestring.h \ + tuple.h \ ui_fileinfopopup.h \ ui_lastfm.h\ ui_preferences.h \ @@ -97,6 +98,8 @@ signals.c \ strings.c \ titlestring.c \ + tuple.c \ + tuple_formatter.c \ skin.c \ ui_about.c \ ui_albumart.c \ @@ -145,7 +148,7 @@ CFLAGS += -I../libaudclient DBUS_BINDINGS = dbus-server-bindings.h dbus-client-bindings.h OBJECTIVE_LIBS_NOINST += $(DBUS_BINDINGS) -LIBDEP += ../libaudclient/libaudclient.so +LIBDEP += ../libaudclient/libaudclient$(SHARED_SUFFIX) LDADD += -L../libaudclient -laudclient endif
--- a/src/audacious/dbus-service.h Wed Aug 08 22:04:41 2007 +0300 +++ b/src/audacious/dbus-service.h Thu Aug 09 16:28:42 2007 +0300 @@ -28,6 +28,7 @@ typedef struct { GObject parent; + DBusGProxy *proxy; } RemoteObject, MprisRoot, MprisPlayer, MprisTrackList; typedef struct { @@ -50,10 +51,13 @@ gboolean mpris_player_pause(MprisPlayer *obj, GError **error); gboolean mpris_player_stop(MprisPlayer *obj, GError **error); gboolean mpris_player_play(MprisPlayer *obj, GError **error); +gboolean mpris_player_repeat(MprisPlayer *obj, gboolean rpt, GError **error); gboolean mpris_player_quit(MprisPlayer *obj, GError **error); -gboolean mpris_player_repeat(MprisPlayer *obj, gboolean rpt, GError **error); +gboolean mpris_player_disconnect(MprisPlayer *obj, GError **error); gboolean mpris_player_get_status(MprisPlayer *obj, gint *status, GError **error); +gboolean mpris_player_get_metadata(MprisTrackList *obj, gint pos, + GHashTable *metadata, GError **error); gboolean mpris_player_get_caps(MprisPlayer *obj, gint *capabilities, GError **error); gboolean mpris_player_volume_set(MprisPlayer *obj, gint vol, GError **error); @@ -63,21 +67,23 @@ gboolean mpris_player_position_get(MprisPlayer *obj, gint *pos, GError **error); enum { - CAPS_CHANGE_SIG, TRACK_CHANGE_SIG, STATUS_CHANGE_SIG, + CAPS_CHANGE_SIG, + DISCONNECTED, LAST_SIG }; -gboolean mpris_player_emit_caps_change(MprisPlayer *obj, GError **error); gboolean mpris_player_emit_track_change(MprisPlayer *obj, GError **error); gboolean mpris_player_emit_status_change(MprisPlayer *obj, GError **error); +gboolean mpris_player_emit_caps_change(MprisPlayer *obj, GError **error); +gboolean mpris_player_emit_disconnected(MprisPlayer *obj, GError **error); // MPRIS /TrackList gboolean mpris_tracklist_get_metadata(MprisTrackList *obj, gint pos, GHashTable *metadata, GError **error); gboolean mpris_tracklist_get_current_track(MprisTrackList *obj, gint *pos, GError **error); -gboolean mpris_tracklist_get_length(MprisTrackList *obj, gint *pos, +gboolean mpris_tracklist_get_length(MprisTrackList *obj, gint *length, GError **error); gboolean mpris_tracklist_add_track(MprisTrackList *obj, gchar *uri, gboolean play, GError **error);
--- a/src/audacious/dbus.c Wed Aug 08 22:04:41 2007 +0300 +++ b/src/audacious/dbus.c Thu Aug 09 16:28:42 2007 +0300 @@ -175,32 +175,54 @@ // MPRIS /Player gboolean mpris_player_next(MprisPlayer *obj, GError **error) { - return audacious_rc_advance(obj, error); + playlist_next(playlist_get_active()); + return TRUE; } gboolean mpris_player_prev(MprisPlayer *obj, GError **error) { - return audacious_rc_reverse(obj, error); + playlist_prev(playlist_get_active()); + return TRUE; } gboolean mpris_player_pause(MprisPlayer *obj, GError **error) { - return audacious_rc_pause(obj, error); + playback_pause(); + return TRUE; } gboolean mpris_player_stop(MprisPlayer *obj, GError **error) { - return audacious_rc_stop(obj, error); + ip_data.stop = TRUE; + playback_stop(); + ip_data.stop = FALSE; + mainwin_clear_song_info(); + return TRUE; } gboolean mpris_player_play(MprisPlayer *obj, GError **error) { - return audacious_rc_play(obj, error); -} -gboolean mpris_player_quit(MprisPlayer *obj, GError **error) { - return audacious_rc_quit(obj, error); + if (playback_get_paused()) + playback_pause(); + else if (playlist_get_length(playlist_get_active())) + playback_initiate(); + else + mainwin_eject_pushed(); + return TRUE; } gboolean mpris_player_repeat(MprisPlayer *obj, gboolean rpt, GError **error) { mainwin_repeat_pushed(rpt); mainwin_set_noplaylistadvance(rpt); return TRUE; } +gboolean mpris_player_quit(MprisPlayer *obj, GError **error) { + // TODO: emit disconnected signal + mainwin_quit_cb(); + return TRUE; +} +gboolean mpris_player_disconnect(MprisPlayer *obj, GError **error) { + return FALSE; +} gboolean mpris_player_get_status(MprisPlayer *obj, gint *status, GError **error) { return FALSE; } +gboolean mpris_player_get_metadata(MprisTrackList *obj, gint pos, + GHashTable *metadata, GError **error) { + return FALSE; +} gboolean mpris_player_get_caps(MprisPlayer *obj, gint *capabilities, GError **error) { return FALSE; @@ -236,6 +258,11 @@ return TRUE; } +gboolean mpris_player_emit_disconnected(MprisPlayer *obj, GError **error) { + g_signal_emit(obj, signals[DISCONNECTED], 0, NULL); + return TRUE; +} + // MPRIS /TrackList gboolean mpris_tracklist_get_metadata(MprisTrackList *obj, gint pos, GHashTable *metadata, GError **error) { @@ -243,11 +270,13 @@ } gboolean mpris_tracklist_get_current_track(MprisTrackList *obj, gint *pos, GError **error) { - return audacious_rc_position(obj, pos, error); + *pos = playlist_get_position(playlist_get_active()); + return TRUE; } -gboolean mpris_tracklist_get_length(MprisTrackList *obj, gint *pos, +gboolean mpris_tracklist_get_length(MprisTrackList *obj, gint *length, GError **error) { - return FALSE; + *length = playlist_get_length(playlist_get_active()); + return TRUE; } gboolean mpris_tracklist_add_track(MprisTrackList *obj, gchar *uri, gboolean play, GError **error) { @@ -261,7 +290,8 @@ } gboolean mpris_tracklist_del_track(MprisTrackList *obj, gint pos, GError **error) { - return FALSE; + playlist_delete_index(playlist_get_active(), pos); + return TRUE; } gboolean mpris_tracklist_loop(MprisTrackList *obj, gboolean loop, GError **error) { @@ -269,7 +299,8 @@ } gboolean mpris_tracklist_random(MprisTrackList *obj, gboolean random, GError **error) { - return FALSE; + mainwin_shuffle_pushed(!cfg.shuffle); + return TRUE; } // Audacious General Information
--- a/src/audacious/dbus.h Wed Aug 08 22:04:41 2007 +0300 +++ b/src/audacious/dbus.h Thu Aug 09 16:28:42 2007 +0300 @@ -25,8 +25,20 @@ #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 + + #endif // !_AUDDBUS_H
--- a/src/audacious/mpris_player.xml Wed Aug 08 22:04:41 2007 +0300 +++ b/src/audacious/mpris_player.xml Thu Aug 09 16:28:42 2007 +0300 @@ -3,6 +3,7 @@ <!-- - Audacious: A cross-platform multimedia player - Copyright (c) 2007 William Pitcock + - Copyright (c) 2007 Ben Tucker - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by @@ -34,51 +35,49 @@ <method name="Play"> <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/> </method> - <method name="Quit"> - <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="Quit"> + <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/> + </method> + <method name="Disconnect"> + <annotation name="org.freedesktop.DBus.GLib.NoReply" value=""/> + </method> <method name="GetStatus"> <arg type="i" 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="CapsChange"> - <arg type="i" /> - </signal> - <signal name="TrackChange"> <arg type="a{sv}" /> </signal> - <signal name="StatusChange"> <arg type="i" /> </signal> + <signal name="CapsChange"> + <arg type="i" /> + </signal> + <signal name="Disconnected" /> </interface> </node>
--- a/src/audacious/mpris_tracklist.xml Wed Aug 08 22:04:41 2007 +0300 +++ b/src/audacious/mpris_tracklist.xml Thu Aug 09 16:28:42 2007 +0300 @@ -3,6 +3,7 @@ <!-- - Audacious: A cross-platform multimedia player - Copyright (c) 2007 William Pitcock + - Copyright (c) 2007 Ben Tucker - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/audacious/tuple.c Thu Aug 09 16:28:42 2007 +0300 @@ -0,0 +1,191 @@ +/* + * Audacious + * Copyright (c) 2006-2007 Audacious team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + * + * The Audacious team does not consider modular code linking to + * Audacious or using our public API to be a derived work. + */ + +#include <glib.h> +#include <mowgli.h> + +#include "tuple.h" + +struct _Tuple { + mowgli_object_t parent; + mowgli_dictionary_t *dict; +}; + +typedef struct { + TupleValueType type; + union { + gchar *string; + gint integer; + } value; +} TupleValue; + +static mowgli_heap_t *tuple_heap = NULL; +static mowgli_heap_t *tuple_value_heap = NULL; +static mowgli_object_class_t tuple_klass; + +/* iterative destructor of tuple values. */ +static void +tuple_value_destroy(mowgli_dictionary_elem_t *delem, gpointer privdata) +{ + TupleValue *value = (TupleValue *) delem->data; + + if (value->type == TUPLE_STRING) + g_free(value->value.string); + + mowgli_heap_free(tuple_value_heap, value); +} + +static void +tuple_destroy(gpointer data) +{ + Tuple *tuple = (Tuple *) data; + + mowgli_dictionary_destroy(tuple->dict, tuple_value_destroy, NULL); + mowgli_heap_free(tuple_heap, tuple); +} + +Tuple * +tuple_new(void) +{ + Tuple *tuple; + + if (tuple_heap == NULL) + { + tuple_heap = mowgli_heap_create(sizeof(Tuple), 256, BH_NOW); + tuple_value_heap = mowgli_heap_create(sizeof(TupleValue), 512, BH_NOW); + mowgli_object_class_init(&tuple_klass, "audacious.tuple", tuple_destroy, FALSE); + } + + /* FIXME: use mowgli_object_bless_from_class() in mowgli 0.4 + when it is released --nenolod */ + tuple = mowgli_heap_alloc(tuple_heap); + mowgli_object_init(mowgli_object(tuple), NULL, &tuple_klass, NULL); + + tuple->dict = mowgli_dictionary_create(g_ascii_strcasecmp); + + return tuple; +} + +gboolean +tuple_associate_string(Tuple *tuple, const gchar *field, const gchar *string) +{ + TupleValue *value; + + g_return_val_if_fail(tuple != NULL, FALSE); + g_return_val_if_fail(field != NULL, FALSE); + g_return_val_if_fail(string != NULL, FALSE); + + if (mowgli_dictionary_find(tuple->dict, field)) + tuple_disassociate(tuple, field); + + value = mowgli_heap_alloc(tuple_value_heap); + value->type = TUPLE_STRING; + value->value.string = g_strdup(string); + + mowgli_dictionary_add(tuple->dict, field, value); + + return TRUE; +} + +gboolean +tuple_associate_int(Tuple *tuple, const gchar *field, gint integer) +{ + TupleValue *value; + + g_return_val_if_fail(tuple != NULL, FALSE); + g_return_val_if_fail(field != NULL, FALSE); + + if (mowgli_dictionary_find(tuple->dict, field)) + tuple_disassociate(tuple, field); + + value = mowgli_heap_alloc(tuple_value_heap); + value->type = TUPLE_INT; + value->value.integer = integer; + + mowgli_dictionary_add(tuple->dict, field, value); + + return TRUE; +} + +void +tuple_disassociate(Tuple *tuple, const gchar *field) +{ + TupleValue *value; + + g_return_if_fail(tuple != NULL); + g_return_if_fail(field != NULL); + + /* why _delete()? because _delete() returns the dictnode's data on success */ + if ((value = mowgli_dictionary_delete(tuple->dict, field)) == NULL) + return; + + if (value->type == TUPLE_STRING) + g_free(value->value.string); + + mowgli_heap_free(tuple_value_heap, value); +} + +TupleValueType +tuple_get_value_type(Tuple *tuple, const gchar *field) +{ + TupleValue *value; + + g_return_val_if_fail(tuple != NULL, TUPLE_UNKNOWN); + g_return_val_if_fail(field != NULL, TUPLE_UNKNOWN); + + if ((value = mowgli_dictionary_retrieve(tuple->dict, field)) == NULL) + return TUPLE_UNKNOWN; + + return value->type; +} + +const gchar * +tuple_get_string(Tuple *tuple, const gchar *field) +{ + TupleValue *value; + + g_return_val_if_fail(tuple != NULL, NULL); + g_return_val_if_fail(field != NULL, NULL); + + if ((value = mowgli_dictionary_retrieve(tuple->dict, field)) == NULL) + return NULL; + + if (value->type != TUPLE_STRING) + mowgli_throw_exception_val(audacious.tuple.invalid_type_request, NULL); + + return value->value.string; +} + +int +tuple_get_int(Tuple *tuple, const gchar *field) +{ + TupleValue *value; + + g_return_val_if_fail(tuple != NULL, 0); + g_return_val_if_fail(field != NULL, 0); + + if ((value = mowgli_dictionary_retrieve(tuple->dict, field)) == NULL) + return 0; + + if (value->type != TUPLE_INT) + mowgli_throw_exception_val(audacious.tuple.invalid_type_request, 0); + + return value->value.integer; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/audacious/tuple.h Thu Aug 09 16:28:42 2007 +0300 @@ -0,0 +1,44 @@ +/* + * Audacious + * Copyright (c) 2006-2007 Audacious team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + * + * The Audacious team does not consider modular code linking to + * Audacious or using our public API to be a derived work. + */ + +#ifndef __AUDACIOUS_TUPLE_H__ +#define __AUDACIOUS_TUPLE_H__ + +#include <glib.h> +#include <mowgli.h> + +struct _Tuple; +typedef struct _Tuple Tuple; + +typedef enum { + TUPLE_STRING, + TUPLE_INT, + TUPLE_UNKNOWN +} TupleValueType; + +Tuple *tuple_new(void); +gboolean tuple_associate_string(Tuple *tuple, const gchar *field, const gchar *string); +gboolean tuple_associate_int(Tuple *tuple, const gchar *field, gint integer); +void tuple_disassociate(Tuple *tuple, const gchar *field); +TupleValueType tuple_get_value_type(Tuple *tuple, const gchar *field); +const gchar *tuple_get_string(Tuple *tuple, const gchar *field); +int tuple_get_int(Tuple *tuple, const gchar *field); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/audacious/tuple_formatter.c Thu Aug 09 16:28:42 2007 +0300 @@ -0,0 +1,446 @@ +/* + * Audacious + * Copyright (c) 2007 William Pitcock + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + * + * The Audacious team does not consider modular code linking to + * Audacious or using our public API to be a derived work. + */ + +#include <glib.h> +#include <mowgli.h> + +#include "config.h" +#include "tuple.h" +#include "tuple_formatter.h" + +/* + * the tuple formatter: + * + * this is a data-driven meta-language which eventually hopes to be + * turing complete. + * + * language constructs follow the following basic rules: + * - begin with ${ + * - end with } + * + * language constructs: + * - ${field}: prints a field + * - ${?field:expr}: evaluates expr if field exists + * - ${=field,"value"}: defines field in the currently iterated + * tuple as string value of "value" + * - ${=field,value}: defines field in the currently iterated + * tuple as integer value of "value" + * - ${==field,field:expr}: evaluates expr if both fields are the same + * - ${!=field,field:expr}: evaluates expr if both fields are not the same + * - ${(empty)?field:expr}: evaluates expr if field is empty or does not exist + * - %{function:args,arg2,...}: runs function and inserts the result. + * + * everything else is treated as raw text. + * additionally, plugins can add additional instructions and functions! + */ + +typedef struct { + Tuple *tuple; + GString *str; +} TupleFormatterContext; + +/* processes a construct, e.g. "${?artist:artist is defined}" would + return "artist is defined" if artist is defined. */ +gchar * +tuple_formatter_process_construct(Tuple *tuple, const gchar *string) +{ + TupleFormatterContext *ctx; + const gchar *iter; + gchar *out; + + g_return_val_if_fail(tuple != NULL, NULL); + g_return_val_if_fail(string != NULL, NULL); + + ctx = g_new0(TupleFormatterContext, 1); + ctx->str = g_string_new(""); + + /* parsers are ugly */ + for (iter = string; *iter != '\0'; iter++) + { + /* if it's raw text, just copy the byte */ + if (*iter != '$' && *iter != '%') + g_string_append_c(ctx->str, *iter); + else if (*iter == '$' && *(iter + 1) == '{') + { + GString *expression = g_string_new(""); + GString *argument = g_string_new(""); + GString *sel = expression; + gchar *result; + gint level = 0; + + for (iter += 2; *iter != '\0'; iter++) + { + if (*iter == ':') + { + sel = argument; + continue; + } + + if (g_str_has_prefix(iter, "${") == TRUE || g_str_has_prefix(iter, "%{") == TRUE) + { + if (sel == argument) + { + g_string_append_c(sel, *iter); + level++; + } + } + else if (*iter == '}' && (sel == argument && --level > 0)) + g_string_append_c(sel, *iter); + else if (*iter == '}' && ((sel != argument) || (sel == argument && level <= 0))) + { + if (sel == argument) + iter++; + break; + } + else + g_string_append_c(sel, *iter); + } + + if (expression->len == 0) + { + g_string_free(expression, TRUE); + g_string_free(argument, TRUE); + continue; + } + + result = tuple_formatter_process_expr(tuple, expression->str, argument->len ? argument->str : NULL); + if (result != NULL) + { + g_string_append(ctx->str, result); + g_free(result); + } + + g_string_free(expression, TRUE); + g_string_free(argument, TRUE); + + if (*iter == '\0') + break; + } + else if (*iter == '%' && *(iter + 1) == '{') + { + GString *expression = g_string_new(""); + GString *argument = g_string_new(""); + GString *sel = expression; + gchar *result; + gint level = 0; + + for (iter += 2; *iter != '\0'; iter++) + { + if (*iter == ':') + { + sel = argument; + continue; + } + + if (g_str_has_prefix(iter, "${") == TRUE || g_str_has_prefix(iter, "%{") == TRUE) + { + if (sel == argument) + { + g_string_append_c(sel, *iter); + level++; + } + } + else if (*iter == '}' && (sel == argument && --level > 0)) + g_string_append_c(sel, *iter); + else if (*iter == '}' && ((sel != argument) || (sel == argument && level <= 0))) + { + if (sel == argument) + iter++; + break; + } + else + g_string_append_c(sel, *iter); + } + + if (expression->len == 0) + { + g_string_free(expression, TRUE); + g_string_free(argument, TRUE); + continue; + } + + result = tuple_formatter_process_function(tuple, expression->str, argument->len ? argument->str : NULL); + if (result != NULL) + { + g_string_append(ctx->str, result); + g_free(result); + } + + g_string_free(expression, TRUE); + g_string_free(argument, TRUE); + + if (*iter == '\0') + break; + } + } + + out = g_strdup(ctx->str->str); + g_string_free(ctx->str, TRUE); + g_free(ctx); + + return out; +} + +static GList *tuple_formatter_expr_list = NULL; + +typedef struct { + const gchar *name; + gboolean (*func)(Tuple *tuple, const gchar *expression); +} TupleFormatterExpression; + +/* processes an expression and optional argument pair. */ +gchar * +tuple_formatter_process_expr(Tuple *tuple, const gchar *expression, + const gchar *argument) +{ + TupleFormatterExpression *expr = NULL; + GList *iter; + + g_return_val_if_fail(tuple != NULL, NULL); + g_return_val_if_fail(expression != NULL, NULL); + + for (iter = tuple_formatter_expr_list; iter != NULL; iter = iter->next) + { + TupleFormatterExpression *tmp = (TupleFormatterExpression *) iter->data; + + if (g_str_has_prefix(expression, tmp->name) == TRUE) + { + expr = tmp; + expression += strlen(tmp->name); + } + } + + /* ${artist} */ + if (expr == NULL && argument == NULL) + { + TupleValueType type = tuple_get_value_type(tuple, expression); + + switch(type) + { + case TUPLE_STRING: + return g_strdup(tuple_get_string(tuple, expression)); + break; + case TUPLE_INT: + return g_strdup_printf("%d", tuple_get_int(tuple, expression)); + break; + case TUPLE_UNKNOWN: + default: + return NULL; + } + } + else if (expr != NULL) + { + if (expr->func(tuple, expression) == TRUE && argument != NULL) + return tuple_formatter_process_construct(tuple, argument); + } + + return NULL; +} + +static GList *tuple_formatter_func_list = NULL; + +typedef struct { + const gchar *name; + gchar *(*func)(Tuple *tuple, gchar **args); +} TupleFormatterFunction; + +/* processes a function */ +gchar * +tuple_formatter_process_function(Tuple *tuple, const gchar *expression, + const gchar *argument) +{ + TupleFormatterFunction *expr = NULL; + GList *iter; + + g_return_val_if_fail(tuple != NULL, NULL); + g_return_val_if_fail(expression != NULL, NULL); + + for (iter = tuple_formatter_func_list; iter != NULL; iter = iter->next) + { + TupleFormatterFunction *tmp = (TupleFormatterFunction *) iter->data; + + if (g_str_has_prefix(expression, tmp->name) == TRUE) + { + expr = tmp; + expression += strlen(tmp->name); + } + } + + if (expr != NULL) + { + gchar **args; + gchar *ret; + + if (argument) + args = g_strsplit(argument, ",", 10); + else + args = NULL; + + ret = expr->func(tuple, args); + + if (args) + g_strfreev(args); + + return ret; + } + + return NULL; +} + +/* registers a formatter */ +void +tuple_formatter_register_expression(const gchar *keyword, + gboolean (*func)(Tuple *tuple, const gchar *argument)) +{ + TupleFormatterExpression *expr; + + g_return_if_fail(keyword != NULL); + g_return_if_fail(func != NULL); + + expr = g_new0(TupleFormatterExpression, 1); + expr->name = keyword; + expr->func = func; + + tuple_formatter_expr_list = g_list_append(tuple_formatter_expr_list, expr); +} + +/* registers a function */ +void +tuple_formatter_register_function(const gchar *keyword, + gchar *(*func)(Tuple *tuple, gchar **argument)) +{ + TupleFormatterFunction *expr; + + g_return_if_fail(keyword != NULL); + g_return_if_fail(func != NULL); + + expr = g_new0(TupleFormatterFunction, 1); + expr->name = keyword; + expr->func = func; + + tuple_formatter_func_list = g_list_append(tuple_formatter_func_list, expr); +} + +/* builtin-keyword: ${?arg}, returns TRUE if <arg> exists. */ +static gboolean +tuple_formatter_expression_exists(Tuple *tuple, const gchar *expression) +{ + return (tuple_get_value_type(tuple, expression) != TUPLE_UNKNOWN) ? TRUE : FALSE; +} + +/* builtin-keyword: ${==arg1,arg2}, returns TRUE if <arg1> and <arg2> match. */ +static gboolean +tuple_formatter_expression_match(Tuple *tuple, const gchar *expression) +{ + gchar **args = g_strsplit(expression, ",", 2); + gchar *arg1, *arg2; + gint ret; + + if (tuple_get_value_type(tuple, args[0]) == TUPLE_UNKNOWN) + { + g_strfreev(args); + return FALSE; + } + + if (tuple_get_value_type(tuple, args[1]) == TUPLE_UNKNOWN) + { + g_strfreev(args); + return FALSE; + } + + if (tuple_get_value_type(tuple, args[0]) == TUPLE_STRING) + arg1 = g_strdup(tuple_get_string(tuple, args[0])); + else + arg1 = g_strdup_printf("%d", tuple_get_int(tuple, args[0])); + + if (tuple_get_value_type(tuple, args[1]) == TUPLE_STRING) + arg2 = g_strdup(tuple_get_string(tuple, args[1])); + else + arg2 = g_strdup_printf("%d", tuple_get_int(tuple, args[1])); + + ret = g_ascii_strcasecmp(arg1, arg2); + g_free(arg1); + g_free(arg2); + g_strfreev(args); + + return ret ? FALSE : TRUE; +} + +/* builtin-keyword: ${!=arg1,arg2}. returns TRUE if <arg1> and <arg2> don't match. */ +static gboolean +tuple_formatter_expression_nonmatch(Tuple *tuple, const gchar *expression) +{ + return tuple_formatter_expression_match(tuple, expression) ^ 1; +} + +/* builtin-keyword: ${empty?}. returns TRUE if <arg> is empty. */ +static gboolean +tuple_formatter_expression_empty(Tuple *tuple, const gchar *expression) +{ + gboolean ret = TRUE; + const gchar *iter; + TupleValueType type = tuple_get_value_type(tuple, expression); + + if (type == TUPLE_UNKNOWN) + return TRUE; + + if (type == TUPLE_INT && tuple_get_int(tuple, expression) != 0) + return FALSE; + + iter = tuple_get_string(tuple, expression); + + while (ret && *iter != '\0') + { + if (*iter == ' ') + iter++; + else + ret = FALSE; + } + + return ret; +} + +/* builtin function: %{audacious-version} */ +static gchar * +tuple_formatter_function_version(Tuple *tuple, gchar **args) +{ + return g_strdup(PACKAGE_NAME " " PACKAGE_VERSION); +} + +/* processes a string containing instructions. does initialization phases + if not already done */ +gchar * +tuple_formatter_process_string(Tuple *tuple, const gchar *string) +{ + static gboolean initialized = FALSE; + + if (initialized == FALSE) + { + tuple_formatter_register_expression("?", tuple_formatter_expression_exists); + tuple_formatter_register_expression("==", tuple_formatter_expression_match); + tuple_formatter_register_expression("!=", tuple_formatter_expression_nonmatch); + tuple_formatter_register_expression("(empty)?", tuple_formatter_expression_empty); + + tuple_formatter_register_function("audacious-version", tuple_formatter_function_version); + initialized = TRUE; + } + + return tuple_formatter_process_construct(tuple, string); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/audacious/tuple_formatter.h Thu Aug 09 16:28:42 2007 +0300 @@ -0,0 +1,40 @@ +/* + * Audacious + * Copyright (c) 2007 William Pitcock + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + * + * The Audacious team does not consider modular code linking to + * Audacious or using our public API to be a derived work. + */ + +#ifndef __AUDACIOUS_TUPLE_FORMATTER_H__ +#define __AUDACIOUS_TUPLE_FORMATTER_H__ + +#include <glib.h> +#include <mowgli.h> + +#include "tuple.h" + +gchar *tuple_formatter_process_string(Tuple *tuple, const gchar *string); +void tuple_formatter_register_expression(const gchar *keyword, + gboolean (*func)(Tuple *tuple, const gchar *argument)); +void tuple_formatter_register_function(const gchar *keyword, + gchar *(*func)(Tuple *tuple, gchar **argument)); +gchar *tuple_formatter_process_expr(Tuple *tuple, const gchar *expression, + const gchar *argument); +gchar *tuple_formatter_process_function(Tuple *tuple, const gchar *expression, + const gchar *argument); +gchar *tuple_formatter_process_construct(Tuple *tuple, const gchar *string); + +#endif
--- a/src/audacious/ui_fileinfopopup.c Wed Aug 08 22:04:41 2007 +0300 +++ b/src/audacious/ui_fileinfopopup.c Thu Aug 09 16:28:42 2007 +0300 @@ -470,9 +470,9 @@ filepopup_entry_set_text(GTK_WIDGET(filepopup_win), "label_artist", ""); filepopup_entry_set_text(GTK_WIDGET(filepopup_win), "label_album", ""); filepopup_entry_set_text(GTK_WIDGET(filepopup_win), "label_genre", ""); - filepopup_entry_set_text(GTK_WIDGET(filepopup_win), "label_track", ""); + filepopup_entry_set_text(GTK_WIDGET(filepopup_win), "label_tracknum", ""); filepopup_entry_set_text(GTK_WIDGET(filepopup_win), "label_year", ""); - filepopup_entry_set_text(GTK_WIDGET(filepopup_win), "label_length", ""); + filepopup_entry_set_text(GTK_WIDGET(filepopup_win), "label_tracklen", ""); gtk_window_resize(GTK_WINDOW(filepopup_win), 1, 1); }
--- a/src/audacious/ui_main.c Wed Aug 08 22:04:41 2007 +0300 +++ b/src/audacious/ui_main.c Thu Aug 09 16:28:42 2007 +0300 @@ -523,7 +523,7 @@ static void mainwin_refresh_visible(void) { - if (!bmp_active_skin) + if (!bmp_active_skin || !cfg.player_visible) return; gtk_widget_show_all(mainwin);
--- a/src/audacious/ui_playlist.c Wed Aug 08 22:04:41 2007 +0300 +++ b/src/audacious/ui_playlist.c Thu Aug 09 16:28:42 2007 +0300 @@ -50,7 +50,6 @@ #include "strings.h" #include "ui_equalizer.h" #include "ui_fileopener.h" -#include "ui_fileinfopopup.h" #include "ui_main.h" #include "ui_manager.h" #include "util.h" @@ -75,9 +74,6 @@ static gboolean playlistwin_hint_flag = FALSE; -static GtkWidget *playlistwin_infopopup = NULL; -static guint playlistwin_infopopup_sid = 0; - static GtkWidget *playlistwin_slider = NULL; static GtkWidget *playlistwin_time_min, *playlistwin_time_sec; static GtkWidget *playlistwin_info, *playlistwin_sinfo; @@ -91,8 +87,6 @@ static gboolean playlistwin_select_search_kp_cb( GtkWidget *entry , GdkEventKey *event , gpointer searchdlg_win ); -static gboolean playlistwin_fileinfopopup_probe(gpointer * filepopup_win); - static gboolean playlistwin_resizing = FALSE; static gint playlistwin_resize_x, playlistwin_resize_y; @@ -1597,8 +1591,6 @@ playlistwin_create_widgets(); playlistwin_update_info(playlist_get_active()); - playlistwin_infopopup = audacious_fileinfopopup_create(); - gtk_window_add_accel_group(GTK_WINDOW(playlistwin), ui_manager_get_accel_group()); } @@ -1617,10 +1609,6 @@ playlistwin_set_toprow(0); playlist_check_pos_current(playlist_get_active()); - if ( playlistwin_infopopup_sid == 0 ) - playlistwin_infopopup_sid = g_timeout_add( - 50 , (GSourceFunc)playlistwin_fileinfopopup_probe , playlistwin_infopopup ); - gtk_widget_show_all(playlistwin); if (!cfg.playlist_shaded) gtk_widget_hide(playlistwin_sinfo); @@ -1640,13 +1628,6 @@ UI_SKINNED_BUTTON(mainwin_pl)->inside = FALSE; gtk_widget_queue_draw(mainwin_pl); - /* no point in probing for playlistwin_infopopup trigger when the playlistwin is hidden */ - if ( playlistwin_infopopup_sid != 0 ) - { - g_source_remove( playlistwin_infopopup_sid ); - playlistwin_infopopup_sid = 0; - } - if ( cfg.player_visible ) { gtk_window_present(GTK_WINDOW(mainwin)); @@ -1975,74 +1956,3 @@ return FALSE; } } - - -/* fileinfopopup callback for playlistwin */ -static gboolean -playlistwin_fileinfopopup_probe(gpointer * filepopup_win) -{ - gint x, y, pos; - TitleInput *tuple; - static gint prev_x = 0, prev_y = 0, ctr = 0, prev_pos = -1; - static gint shaded_pos = -1, shaded_prev_pos = -1; - gboolean skip = FALSE; - GdkWindow *win; - Playlist *playlist = playlist_get_active(); - - win = gdk_window_at_pointer(NULL, NULL); - gdk_window_get_pointer(GDK_WINDOW(playlistwin->window), &x, &y, NULL); - pos = ui_skinned_playlist_get_position(playlistwin_list, x - 12, y - 20); - - if (win == NULL - || cfg.show_filepopup_for_tuple == FALSE - || UI_SKINNED_PLAYLIST(playlistwin_list)->tooltips == FALSE - || pos != prev_pos - || win != playlistwin_list->window) - { - prev_pos = pos; - ctr = 0; - audacious_fileinfopopup_hide(GTK_WIDGET(filepopup_win), NULL); - return TRUE; - } - - if (prev_x == x && prev_y == y) - ctr++; - else - { - ctr = 0; - prev_x = x; - prev_y = y; - audacious_fileinfopopup_hide(GTK_WIDGET(filepopup_win), NULL); - return TRUE; - } - - if (playlistwin_is_shaded()) - { - shaded_pos = playlist_get_position(playlist); - if (shaded_prev_pos != shaded_pos) - skip = TRUE; - } - - if (ctr >= cfg.filepopup_delay && (skip == TRUE || GTK_WIDGET_VISIBLE(GTK_WIDGET(filepopup_win)) != TRUE)) { - if (pos == -1 && !playlistwin_is_shaded()) - { - audacious_fileinfopopup_hide(GTK_WIDGET(filepopup_win), NULL); - return TRUE; - } - /* shaded mode */ - else - { - tuple = playlist_get_tuple(playlist, shaded_pos); - audacious_fileinfopopup_hide(GTK_WIDGET(filepopup_win), NULL); - audacious_fileinfopopup_show_from_tuple(GTK_WIDGET(filepopup_win), tuple); - shaded_prev_pos = shaded_pos; - } - - prev_pos = pos; - - tuple = playlist_get_tuple(playlist, pos); - audacious_fileinfopopup_show_from_tuple(GTK_WIDGET(filepopup_win), tuple); - } - - return TRUE; -}
--- a/src/audacious/ui_skinned_playlist.c Wed Aug 08 22:04:41 2007 +0300 +++ b/src/audacious/ui_skinned_playlist.c Thu Aug 09 16:28:42 2007 +0300 @@ -50,6 +50,7 @@ #include "playback.h" #include "playlist.h" #include "ui_manager.h" +#include "ui_fileinfopopup.h" #include "debug.h" static PangoFontDescription *playlist_list_font = NULL; @@ -90,7 +91,12 @@ static gboolean ui_skinned_playlist_button_press (GtkWidget *widget, GdkEventButton *event); static gboolean ui_skinned_playlist_button_release (GtkWidget *widget, GdkEventButton *event); static gboolean ui_skinned_playlist_motion_notify (GtkWidget *widget, GdkEventMotion *event); +static gboolean ui_skinned_playlist_leave_notify (GtkWidget *widget, GdkEventCrossing *event); static void ui_skinned_playlist_redraw (UiSkinnedPlaylist *playlist); +static gboolean ui_skinned_playlist_popup_show (gpointer data); +static void ui_skinned_playlist_popup_hide (GtkWidget *widget); +static void ui_skinned_playlist_popup_timer_start (GtkWidget *widget); +static void ui_skinned_playlist_popup_timer_stop (GtkWidget *widget); static GtkWidgetClass *parent_class = NULL; static guint playlist_signals[LAST_SIGNAL] = { 0 }; @@ -134,6 +140,7 @@ widget_class->button_press_event = ui_skinned_playlist_button_press; widget_class->button_release_event = ui_skinned_playlist_button_release; widget_class->motion_notify_event = ui_skinned_playlist_motion_notify; + widget_class->leave_notify_event = ui_skinned_playlist_leave_notify; klass->redraw = ui_skinned_playlist_redraw; @@ -153,7 +160,14 @@ playlist->prev_selected = -1; playlist->prev_min = -1; playlist->prev_max = -1; - playlist->tooltips = TRUE; + + g_object_set_data(G_OBJECT(playlist), "timer_id", GINT_TO_POINTER(0)); + g_object_set_data(G_OBJECT(playlist), "timer_active", GINT_TO_POINTER(0)); + + GtkWidget *popup = audacious_fileinfopopup_create(); + g_object_set_data(G_OBJECT(playlist), "popup", popup); + g_object_set_data(G_OBJECT(playlist), "popup_active", GINT_TO_POINTER(0)); + g_object_set_data(G_OBJECT(playlist), "popup_position", GINT_TO_POINTER(-1)); } GtkWidget* ui_skinned_playlist_new(GtkWidget *fixed, gint x, gint y, gint w, gint h) { @@ -204,7 +218,7 @@ attributes.window_type = GDK_WINDOW_CHILD; attributes.event_mask = gtk_widget_get_events(widget); attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK; + GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK; attributes.visual = gtk_widget_get_visual(widget); attributes.colormap = gtk_widget_get_colormap(widget); @@ -797,7 +811,6 @@ if (nr == -1) return FALSE; - pl->tooltips = FALSE; if (event->button == 3) { GList* selection = playlist_get_selected(playlist); if (g_list_find(selection, GINT_TO_POINTER(nr)) == NULL) { @@ -847,6 +860,8 @@ priv->dragging = TRUE; } playlistwin_update_list(playlist); + ui_skinned_playlist_popup_hide(widget); + ui_skinned_playlist_popup_timer_stop(widget); return TRUE; } @@ -858,9 +873,11 @@ priv->dragging = FALSE; priv->auto_drag_down = FALSE; priv->auto_drag_up = FALSE; - UI_SKINNED_PLAYLIST(widget)->tooltips = TRUE; gtk_widget_queue_draw(widget); } + + ui_skinned_playlist_popup_hide(widget); + ui_skinned_playlist_popup_timer_stop(widget); return TRUE; } @@ -906,10 +923,28 @@ playlistwin_update_list(playlist_get_active()); } priv->drag_pos = nr; + } else { + gint pos = ui_skinned_playlist_get_position(widget, event->x, event->y); + gint cur_pos = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "popup_position")); + if (pos != cur_pos) { + g_object_set_data(G_OBJECT(widget), "popup_position", GINT_TO_POINTER(pos)); + ui_skinned_playlist_popup_hide(widget); + ui_skinned_playlist_popup_timer_stop(widget); + if (pos != -1) + ui_skinned_playlist_popup_timer_start(widget); + } } + return TRUE; } +static gboolean ui_skinned_playlist_leave_notify(GtkWidget *widget, GdkEventCrossing *event) { + ui_skinned_playlist_popup_hide(widget); + ui_skinned_playlist_popup_timer_stop(widget); + + return FALSE; +} + static void ui_skinned_playlist_redraw(UiSkinnedPlaylist *playlist) { UiSkinnedPlaylistPrivate *priv = UI_SKINNED_PLAYLIST_GET_PRIVATE(playlist); @@ -972,3 +1007,49 @@ priv->resize_width += w; priv->resize_height += h; } + +static gboolean ui_skinned_playlist_popup_show(gpointer data) { + GtkWidget *widget = data; + gint pos = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "popup_position")); + + if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "timer_active")) == 1 && pos != -1) { + TitleInput *tuple; + Playlist *pl_active = playlist_get_active(); + GtkWidget *popup = g_object_get_data(G_OBJECT(widget), "popup"); + + tuple = playlist_get_tuple(pl_active, pos); + if ((tuple == NULL) || (tuple->length < 1)) { + gchar *title = playlist_get_songtitle(pl_active, pos); + audacious_fileinfopopup_show_from_title(popup, title); + g_free(title); + } else { + audacious_fileinfopopup_show_from_tuple(popup , tuple); + } + g_object_set_data(G_OBJECT(widget), "popup_active" , GINT_TO_POINTER(1)); + } + + ui_skinned_playlist_popup_timer_stop(widget); + return FALSE; +} + +static void ui_skinned_playlist_popup_hide(GtkWidget *widget) { + if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "popup_active")) == 1) { + GtkWidget *popup = g_object_get_data(G_OBJECT(widget), "popup"); + g_object_set_data(G_OBJECT(widget), "popup_active", GINT_TO_POINTER(0)); + audacious_fileinfopopup_hide(popup, NULL); + } +} + +static void ui_skinned_playlist_popup_timer_start(GtkWidget *widget) { + gint timer_id = g_timeout_add(cfg.filepopup_delay*100, ui_skinned_playlist_popup_show, widget); + g_object_set_data(G_OBJECT(widget), "timer_id", GINT_TO_POINTER(timer_id)); + g_object_set_data(G_OBJECT(widget), "timer_active", GINT_TO_POINTER(1)); +} + +static void ui_skinned_playlist_popup_timer_stop(GtkWidget *widget) { + if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "timer_active")) == 1) + g_source_remove(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "timer_id"))); + + g_object_set_data(G_OBJECT(widget), "timer_id", GINT_TO_POINTER(0)); + g_object_set_data(G_OBJECT(widget), "timer_active", GINT_TO_POINTER(0)); +}
--- a/src/audacious/ui_skinned_playlist.h Wed Aug 08 22:04:41 2007 +0300 +++ b/src/audacious/ui_skinned_playlist.h Thu Aug 09 16:28:42 2007 +0300 @@ -48,7 +48,6 @@ gint first; gint num_visible; gint prev_selected, prev_min, prev_max; - gboolean tooltips; gboolean drag_motion; gint drag_motion_x, drag_motion_y; gint fheight;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tests/Makefile Thu Aug 09 16:28:42 2007 +0300 @@ -0,0 +1,38 @@ +include ../../mk/rules.mk +include ../../mk/init.mk +include ../../mk/objective.mk + +OBJECTIVE_LIBS_NOINST = tuple_formatter_test \ + tuple_formatter_functor_test + +LDFLAGS += $(AUDLDFLAGS) +LDADD = \ + $(DBUS_LIBS) \ + $(GTK_LIBS) \ + $(MOWGLI_LIBS) \ + $(LIBGLADE_LIBS) + +CFLAGS += \ + $(GTK_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(LIBGLADE_CFLAGS) \ + $(BEEP_DEFINES) \ + $(ARCH_DEFINES) \ + $(MOWGLI_CFLAGS) \ + -I.. -I../.. \ + -I../intl -I../audacious + +COMMON_OBJS = test_harness.o +TFT_OBJS = $(COMMON_OBJS) tuple_formatter_test.o ../audacious/tuple.o ../audacious/tuple_formatter.o +tuple_formatter_test: $(TFT_OBJS) + $(CC) $(LDFLAGS) $(TFT_OBJS) $(LDADD) -o $@ + @printf "%10s %-20s\n" LINK $@ + ./$@ + @printf "%10s %-20s\n" TEST-PASS $@ + +TFFT_OBJS = $(COMMON_OBJS) tuple_formatter_functor_test.o ../audacious/tuple.o ../audacious/tuple_formatter.o +tuple_formatter_functor_test: $(TFFT_OBJS) + $(CC) $(LDFLAGS) $(TFFT_OBJS) $(LDADD) -o $@ + @printf "%10s %-20s\n" LINK $@ + ./$@ + @printf "%10s %-20s\n" TEST-PASS $@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tests/README Thu Aug 09 16:28:42 2007 +0300 @@ -0,0 +1,11 @@ +This directory contains some tests. To run them, type "make". + +Any needed uncompiled files in the audacious core will be compiled +with the proper CFLAGS. Don't worry about that. + +You can help make this directory more useful by writing tests as +you fix bugs. Unless they're in the UI, autotesting that may be +problematic. + +To write a test, simply edit an existing test and the Makefile +as required.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tests/test_harness.c Thu Aug 09 16:28:42 2007 +0300 @@ -0,0 +1,38 @@ +/* + * Audacious + * Copyright (c) 2007 William Pitcock + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + * + * The Audacious team does not consider modular code linking to + * Audacious or using our public API to be a derived work. + */ + +#include <glib.h> +#include <mowgli.h> + +extern int test_run(gint argc, const gchar *argv[]); + +int +main(gint argc, const gchar *argv[]) +{ + g_thread_init(NULL); + + mowgli_init(); + + if (!g_thread_supported()) + mowgli_log("Warning: GThread not supported. Some tests may fail."); + + return test_run(argc, argv); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tests/tuple_formatter_functor_test.c Thu Aug 09 16:28:42 2007 +0300 @@ -0,0 +1,71 @@ +/* + * Audacious + * Copyright (c) 2007 William Pitcock + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + * + * The Audacious team does not consider modular code linking to + * Audacious or using our public API to be a derived work. + */ + +#include <glib.h> +#include <mowgli.h> + +#include "tuple.h" +#include "tuple_formatter.h" + +static gboolean +test_functor(Tuple *tuple, const char *expr) +{ + return TRUE; +} + +int +test_run(int argc, const char *argv[]) +{ + Tuple *tuple; + gchar *tstr; + + tuple_formatter_register_expression("(true)", test_functor); + + tuple = tuple_new(); + tuple_associate_string(tuple, "splork", "moo"); + + tstr = tuple_formatter_process_string(tuple, "${(true):${splork}}"); + if (g_ascii_strcasecmp(tstr, "moo")) + { + g_print("fail 1: '%s'\n", tstr); + return EXIT_FAILURE; + } + g_free(tstr); + + tstr = tuple_formatter_process_string(tuple, "%{audacious-version}"); + if (g_str_has_prefix(tstr, "audacious") == FALSE) + { + g_print("fail 2: '%s'\n", tstr); + return EXIT_FAILURE; + } + g_free(tstr); + + tstr = tuple_formatter_process_string(tuple, "${(true):%{audacious-version}}"); + if (g_str_has_prefix(tstr, "audacious") == FALSE) + { + g_print("fail 3: '%s'\n", tstr); + return EXIT_FAILURE; + } + g_free(tstr); + + mowgli_object_unref(tuple); + + return EXIT_SUCCESS; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tests/tuple_formatter_test.c Thu Aug 09 16:28:42 2007 +0300 @@ -0,0 +1,112 @@ +/* + * Audacious + * Copyright (c) 2007 William Pitcock + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + * + * The Audacious team does not consider modular code linking to + * Audacious or using our public API to be a derived work. + */ + +#include <glib.h> +#include <mowgli.h> + +#include "tuple.h" +#include "tuple_formatter.h" + +int +test_run(int argc, const char *argv[]) +{ + Tuple *tuple; + gchar *tstr; + + tuple = tuple_new(); + tuple_associate_string(tuple, "splork", "moo"); + tuple_associate_int(tuple, "splorkerz", 42); + + tstr = tuple_formatter_process_string(tuple, "${splork} ${splorkerz}"); + if (g_ascii_strcasecmp(tstr, "moo 42")) + { + g_print("fail 1: '%s'\n", tstr); + return EXIT_FAILURE; + } + g_free(tstr); + + tstr = tuple_formatter_process_string(tuple, "${?fizz:${splork}} ${splorkerz}"); + if (g_ascii_strcasecmp(tstr, " 42")) + { + g_print("fail 2: '%s'\n", tstr); + return EXIT_FAILURE; + } + g_free(tstr); + + tstr = tuple_formatter_process_string(tuple, "${?splork:${splork}} ${splorkerz}"); + if (g_ascii_strcasecmp(tstr, "moo 42")) + { + g_print("fail 3: '%s'\n", tstr); + return EXIT_FAILURE; + } + g_free(tstr); + + tstr = tuple_formatter_process_string(tuple, "${==splork,splork:fields given matched}"); + if (g_ascii_strcasecmp(tstr, "fields given matched")) + { + g_print("fail 4: '%s'\n", tstr); + return EXIT_FAILURE; + } + g_free(tstr); + + tstr = tuple_formatter_process_string(tuple, "${==splork,splork:${splork}}"); + if (g_ascii_strcasecmp(tstr, "moo")) + { + g_print("fail 5: '%s'\n", tstr); + return EXIT_FAILURE; + } + g_free(tstr); + + tstr = tuple_formatter_process_string(tuple, "${!=splork,splorkerz:fields did not match}"); + if (g_ascii_strcasecmp(tstr, "fields did not match")) + { + g_print("fail 6: '%s'\n", tstr); + return EXIT_FAILURE; + } + g_free(tstr); + + tstr = tuple_formatter_process_string(tuple, "${!=splork,splorkerz:${splorkerz}}"); + if (g_ascii_strcasecmp(tstr, "42")) + { + g_print("fail 7: '%s'\n", tstr); + return EXIT_FAILURE; + } + g_free(tstr); + + tstr = tuple_formatter_process_string(tuple, "${!=splork,splork:${splorkerz}}"); + if (g_ascii_strcasecmp(tstr, "")) + { + g_print("fail 8: '%s'\n", tstr); + return EXIT_FAILURE; + } + g_free(tstr); + + tstr = tuple_formatter_process_string(tuple, "${(empty)?splorky:${splorkerz}}"); + if (g_ascii_strcasecmp(tstr, "42")) + { + g_print("fail 9: '%s'\n", tstr); + return EXIT_FAILURE; + } + g_free(tstr); + + mowgli_object_unref(tuple); + + return EXIT_SUCCESS; +}