r2752 - in trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2: . images src
abraxa at sita.openmoko.org
abraxa at sita.openmoko.org
Tue Aug 21 09:41:22 CEST 2007
Author: abraxa
Date: 2007-08-21 09:41:07 +0200 (Tue, 21 Aug 2007)
New Revision: 2752
Added:
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/images/ico-tracktype-general.png
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/editor_page.c
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/editor_page.h
Modified:
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/TODO
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/images/Makefile.am
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/Makefile.am
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/files_page.c
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/files_page.h
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/guitools.c
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main.c
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main.h
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main_page.c
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main_page.h
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/persistent.c
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/persistent.h
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playback.c
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playback.h
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist.c
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist.h
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist_page.c
trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist_page.h
Log:
Added metadata processing and caching
Added playlist chooser
Added volume slider
Enhanced playlist handling
Prepared GUI and codebase for playlist editing
Various bugfixes
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/TODO
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/TODO 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/TODO 2007-08-21 07:41:07 UTC (rev 2752)
@@ -3,18 +3,19 @@
Add repeat mode indicator (different button pixmaps)
Make FFWD/REW buttons trigger repeatedly
Make Prev/Next buttons sensitive depending on playlist state
+ m3u import
Backend:
Use GConf
Use GST_MESSAGE_DURATION and use saved value
- Use GST_MESSAGE_TAG
Use GST_MESSAGE_BUFFERING
+ Check for unicode compliance
Issues:
How/where to store playlists? -> Single common path, omitting path chooser
- How to adjust volume/panning?
Use EQ presets only or allow individual band adjustment?
How to adjust equalizer?
+ Reduce call frequency of omp_playback_get_track_length()
Build process:
Bitbake recipe and its integration into OE
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/images/Makefile.am
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/images/Makefile.am 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/images/Makefile.am 2007-08-21 07:41:07 UTC (rev 2752)
@@ -9,6 +9,7 @@
ico-repeat.png \
ico-shuffle.png \
ico-list.png \
+ ico-tracktype-general.png \
ind-music-eq-01.png \
ind-music-eq-02.png \
ind-music-eq-03.png \
@@ -38,6 +39,6 @@
ind-music-volume-07.png \
ind-music-volume-08.png \
ind-music-volume-09.png \
- ind-music-volume-10.png
+ ind-music-volume-10.png
EXTRA_DIST = $(images_DATA)
Added: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/images/ico-tracktype-general.png
===================================================================
(Binary files differ)
Property changes on: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/images/ico-tracktype-general.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/Makefile.am
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/Makefile.am 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/Makefile.am 2007-08-21 07:41:07 UTC (rev 2752)
@@ -30,4 +30,5 @@
persistent.c persistent.h \
main_page.c main_page.h \
playlist_page.c playlist_page.h \
+ editor_page.c editor_page.h \
files_page.c files_page.h
Added: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/editor_page.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/editor_page.c 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/editor_page.c 2007-08-21 07:41:07 UTC (rev 2752)
@@ -0,0 +1,276 @@
+/*
+ * OpenMoko Media Player
+ * http://openmoko.org/
+ *
+ * Copyright (C) 2007 by the OpenMoko 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file editor_page.c
+ * Playlist editor, part 1: main editor
+ */
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <libmokoui2/moko-finger-scroll.h>
+
+#include "editor_page.h"
+#include "playlist.h"
+#include "guitools.h"
+
+/// Enumeration for the track list columns
+enum
+{
+ TYPE_COLUMN,
+ NUMBER_COLUMN,
+ TITLE_COLUMN,
+ DURATION_COLUMN,
+ COLUMN_COUNT
+};
+
+/// List store for the playlist selector
+GtkListStore *omp_editor_page_list_store = NULL;
+
+/// The label showing the editor's title caption
+GtkWidget *omp_editor_title_label = NULL;
+
+// Forward declarations for internal use
+void omp_editor_page_list_populate();
+
+
+
+/**
+ * Gets called when a new playlist was loaded - we then update the editor window accordingly
+ */
+void
+omp_editor_page_playlist_loaded(gpointer instance, gchar *title, gpointer user_data)
+{
+ gchar *text;
+
+ text = g_strdup_printf(_(OMP_WIDGET_CAPTION_EDITOR), title);
+ gtk_label_set_text(GTK_LABEL(omp_editor_title_label), text);
+ g_free(text);
+
+ omp_editor_page_list_populate();
+}
+
+/**
+ * Updates a track's title and duration upon arrival of metadata
+ */
+void
+omp_editor_page_update_track_info(gpointer instance, guint track_id, gpointer user_data)
+{
+ guint duration;
+ gchar *track_title, *track_duration, *path;
+ GtkTreeIter tree_iter;
+
+ omp_playlist_get_track_info(track_id, &track_title, &duration);
+
+ if (duration > 0)
+ {
+ track_duration = g_strdup_printf(OMP_WIDGET_CAPTION_EDITOR_TRACK_TIME,
+ duration / 60000, (duration/1000) % 60);
+ } else {
+ track_duration = NULL;
+ }
+
+ path = g_strdup_printf("%d", track_id);
+ gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(omp_editor_page_list_store),
+ &tree_iter, path);
+
+ gtk_list_store_set(omp_editor_page_list_store, &tree_iter,
+ TITLE_COLUMN, track_title,
+ DURATION_COLUMN, track_duration, -1);
+
+ g_free(path);
+ g_free(track_duration);
+}
+
+/**
+ * Callback for the "add track" button
+ */
+void
+omp_editor_page_add_clicked(gpointer instance, gpointer user_data)
+{
+
+}
+
+/**
+ * Monitors general click events on the list view and acts appropriately
+ */
+gboolean
+omp_editor_page_list_clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
+{
+
+}
+
+/**
+ * Fills the data model with track titles from the playlist
+ */
+void
+omp_editor_page_list_populate()
+{
+ GtkTreeIter tree_iter;
+ guint track_num, duration;
+ gchar *track_title, *track_duration;
+ struct playlist_iter *pl_iter;
+
+ gtk_list_store_clear(omp_editor_page_list_store);
+
+ pl_iter = omp_playlist_init_iterator();
+
+ // Iterate over the playlist and gather track infos to fill the list with
+ while (!omp_playlist_iter_finished(pl_iter))
+ {
+ omp_playlist_get_track_from_iter(pl_iter, &track_num, &track_title, &duration);
+
+ if (duration > 0)
+ {
+ track_duration = g_strdup_printf(OMP_WIDGET_CAPTION_EDITOR_TRACK_TIME,
+ duration / 60000, (duration/1000) % 60);
+ } else {
+ track_duration = g_strdup("");
+ }
+
+ gtk_list_store_append(omp_editor_page_list_store, &tree_iter);
+ gtk_list_store_set(omp_editor_page_list_store, &tree_iter,
+ NUMBER_COLUMN, track_num+1,
+ TITLE_COLUMN, track_title,
+ DURATION_COLUMN, track_duration, -1);
+
+ g_free(track_duration);
+ g_free(track_title);
+
+ omp_playlist_advance_iter(pl_iter);
+ }
+}
+
+/**
+ * Creates the track view
+ * @param container Destination container of the view
+ */
+void
+omp_editor_page_list_create(GtkContainer *container)
+{
+ GtkWidget *tree_view;
+ GtkTreeSelection *select;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GdkPixbuf *track_icon;
+
+ track_icon = pixbuf_new_from_file("ico-tracktype-general.png");
+
+ // Create data model
+ omp_editor_page_list_store = gtk_list_store_new(COLUMN_COUNT,
+ G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING);
+
+ // Create data view
+ tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(omp_editor_page_list_store));
+ g_object_unref(G_OBJECT(omp_editor_page_list_store));
+
+ g_signal_connect(G_OBJECT(tree_view), "button-press-event",
+ G_CALLBACK(omp_editor_page_list_clicked), NULL);
+
+ // Configure selection handler
+ select = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
+ gtk_tree_selection_set_mode(GTK_TREE_SELECTION(select), GTK_SELECTION_SINGLE);
+
+ // Set up columns
+ renderer = gtk_cell_renderer_pixbuf_new();
+ g_object_set(G_OBJECT(renderer), "pixbuf", track_icon, NULL);
+ column = gtk_tree_view_column_new_with_attributes("", renderer, NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_fixed_width(column, BUTTON_PIXMAP_SIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_("#"), renderer,
+ "text", NUMBER_COLUMN, NULL);
+ gtk_tree_view_column_set_fixed_width(column, BUTTON_PIXMAP_SIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_("Track Title"), renderer,
+ "text", TITLE_COLUMN, NULL);
+ gtk_tree_view_column_set_expand(column, TRUE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_("Duration"), renderer,
+ "text", DURATION_COLUMN, NULL);
+ gtk_tree_view_column_set_fixed_width(column, 2*BUTTON_PIXMAP_SIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
+
+ // Add playlist view to container
+ gtk_container_add(container, GTK_WIDGET(tree_view));
+}
+
+/**
+ * Creates the playlist editor UI page and all its elements
+ */
+GtkWidget *
+omp_editor_page_create()
+{
+ GtkWidget *main_vbox, *alignment, *label, *scroll_box, *hbox, *button, *image;
+
+ // Create main container
+ main_vbox = gtk_vbox_new(FALSE, 0);
+
+ // Caption
+ alignment = create_label(&label, "Sans 14", "black", 0, 0, 0, 0, 0);
+ gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 5, 5, 5, 5);
+ gtk_box_pack_start(GTK_BOX(main_vbox), GTK_WIDGET(alignment), FALSE, FALSE, 0);
+ omp_editor_title_label = label;
+
+ // Track list viewport
+ scroll_box = moko_finger_scroll_new();
+ gtk_box_pack_start(GTK_BOX(main_vbox), GTK_WIDGET(scroll_box), TRUE, TRUE, 0);
+
+ // Create track view
+ omp_editor_page_list_create(GTK_CONTAINER(scroll_box));
+
+ // Add "add tracks" button
+ alignment = gtk_alignment_new(0, 0, 1, 0);
+ gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 5, 5, 5, 5);
+ gtk_box_pack_start(GTK_BOX(main_vbox), GTK_WIDGET(alignment), FALSE, TRUE, 0);
+
+ button = gtk_button_new();
+ gtk_container_add(GTK_CONTAINER(alignment), GTK_WIDGET(button));
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(button), GTK_WIDGET(hbox));
+
+ image = gtk_image_new_from_icon_name("gtk-file", BUTTON_PIXMAP_SIZE);
+ gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(image), TRUE, TRUE, 0);
+
+ alignment = create_label(&label, "Sans 14", "black", 0, 0, 0, 0, 0);
+ gtk_label_set_text(GTK_LABEL(label), _("Add Tracks"));
+ gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(alignment), TRUE, TRUE, 0);
+
+ g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(omp_editor_page_add_clicked), NULL);
+
+ // Make all widgets visible
+ gtk_widget_show_all(main_vbox);
+
+ // Set up signal handlers
+ g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYLIST_LOADED,
+ G_CALLBACK(omp_editor_page_playlist_loaded), NULL);
+
+ g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYLIST_TRACK_INFO_CHANGED,
+ G_CALLBACK(omp_editor_page_update_track_info), NULL);
+
+ return main_vbox;
+}
Added: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/editor_page.h
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/editor_page.h 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/editor_page.h 2007-08-21 07:41:07 UTC (rev 2752)
@@ -0,0 +1,39 @@
+/*
+ * OpenMoko Media Player
+ * http://openmoko.org/
+ *
+ * Copyright (C) 2007 by the OpenMoko 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file editor_page.h
+ * Playlist editor, part 1: main editor
+ */
+
+#ifndef EDITOR_PAGE_H
+#define EDITOR_PAGE_H
+
+#include <gtk/gtk.h>
+
+#define OMP_WIDGET_CAPTION_EDITOR "Tracks in playlist '%s':"
+#define OMP_WIDGET_CAPTION_EDITOR_TRACK_TIME "%d:%.2d"
+
+GtkWidget *omp_editor_page_create();
+
+#endif
+
+
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/files_page.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/files_page.c 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/files_page.c 2007-08-21 07:41:07 UTC (rev 2752)
@@ -21,7 +21,7 @@
/**
* @file files_page.c
- * Playlist editor
+ * Playlist editor, part 2: file adding window
*/
#include "files_page.h"
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/files_page.h
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/files_page.h 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/files_page.h 2007-08-21 07:41:07 UTC (rev 2752)
@@ -21,14 +21,13 @@
/**
* @file files_page.h
- * Playlist editor
+ * Playlist editor, part 2: file adding window
*/
#ifndef FILES_PAGE_H
#define FILES_PAGE_H
-
#endif
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/guitools.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/guitools.c 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/guitools.c 2007-08-21 07:41:07 UTC (rev 2752)
@@ -27,20 +27,19 @@
#include "guitools.h"
/// Absolute path to the UI pixmaps
-extern gchar *ui_image_path = NULL;
+gchar *ui_image_path = NULL;
-
/**
* Loads an image from a file into a pixel buffer
*/
GdkPixbuf*
-pixbuf_new_from_file(const gchar* file_name)
+pixbuf_new_from_file(const gchar *file_name)
{
- gchar* image_file_name;
- GdkPixbuf* pixbuf = NULL;
- GError* error = NULL;
+ gchar *image_file_name;
+ GdkPixbuf *pixbuf = NULL;
+ GError *error = NULL;
image_file_name = g_strdup_printf("%s/%s", ui_image_path, file_name);
@@ -49,11 +48,11 @@
pixbuf = gdk_pixbuf_new_from_file(image_file_name, &error);
if(!pixbuf)
{
- g_print("File found but failed to load: %s\n", image_file_name);
+ g_printerr("File found but failed to load: %s\n", image_file_name);
g_error_free(error);
}
} else {
- g_debug("Can't find %s\n", image_file_name);
+ g_printerr("Can't find %s\n", image_file_name);
}
g_free(image_file_name);
@@ -84,10 +83,14 @@
font_desc = pango_font_description_from_string(font_info);
gtk_widget_modify_font(*label, font_desc);
pango_font_description_free(font_desc);
- gtk_label_set_width_chars(GTK_LABEL(*label), max_char_count);
gtk_misc_set_alignment(GTK_MISC(*label), 0, 0.5);
- gtk_label_set_ellipsize(GTK_LABEL(*label), PANGO_ELLIPSIZE_END);
+ if (max_char_count)
+ {
+ gtk_label_set_width_chars(GTK_LABEL(*label), max_char_count);
+ gtk_label_set_ellipsize(GTK_LABEL(*label), PANGO_ELLIPSIZE_END);
+ }
+
gdk_color_parse(color_desc, &color);
gtk_widget_modify_fg(GTK_WIDGET(*label), GTK_STATE_NORMAL, &color);
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main.c 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main.c 2007-08-21 07:41:07 UTC (rev 2752)
@@ -48,6 +48,8 @@
#include "main.h"
#include "main_page.h"
+#include "playlist_page.h"
+#include "editor_page.h"
#include "guitools.h"
#include "playlist.h"
#include "playback.h"
@@ -59,39 +61,19 @@
// Enables GLib memory profiling when defined
//define DEBUG_MEM_PROFILE
+// Forces the window to the native size of the Neo1973's screen area if enabled
+//define EMULATE_SIZE
+
// The padding applied to the page handle's contents
#define NOTEBOOK_PAGE_PADDING 6
-GtkWidget *omp_window = NULL;
-GtkWidget *omp_notebook = NULL;
-struct _omp_notebook_tabs *omp_notebook_tabs = NULL;
+GtkWidget *omp_window = NULL; ///< Application's main window
+GtkWidget *omp_notebook = NULL; ///< GtkNotebook containing the pages making up the UI
+struct _omp_notebook_tab_ids *omp_notebook_tab_ids = NULL; ///< Holds numerical IDs of the notebook tabs, used for gtk_notebook_set_current_page()
+struct _omp_notebook_tabs *omp_notebook_tabs = NULL; ///< Holds the GtkWidget handles of the notebook tabs so they can be hidden/shown
-/*
-void
-init_dbus()
-{
- //added by lijiang
- DBusConnection *bus;
- DBusError error;
-
- dbus_error_init(&error);
- bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
- if(!bus)
- {
- g_print("Failed to connect to the D-Bus daemon: %s", error.message);
- dbus_error_free(&error);
- return;
- }
- dbus_connection_setup_with_g_main(bus, NULL);
- dbus_bus_add_match(bus, "type='signal',interface='com.burtonini.dbus.Signal'", &error);
- dbus_connection_add_filter(bus, signal_filter, mainwindow, NULL);
- //added end
-
-}
-*/
-
/**
* Terminate the entire program normally
*/
@@ -238,32 +220,52 @@
omp_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(omp_window), _("Media Player"));
g_signal_connect(G_OBJECT(omp_window), "destroy", G_CALLBACK(omp_close), NULL);
+
+ #ifdef EMULATE_SIZE
+ gtk_widget_set_size_request(GTK_WIDGET(omp_window), 480, 620);
+ #endif
}
/**
- * Create the individual pages that make up the UI
+ * Creates the individual pages that make up the UI
* @note Must be called after the backends have been initialized so the signals exist that the UIs hook to
*/
void
omp_window_create_pages()
{
+ GtkWidget *page;
+ guint page_id = 0;
+
// Create and set up the notebook that contains the individual UI pages
omp_notebook = gtk_notebook_new();
g_object_set(G_OBJECT(omp_notebook), "can-focus", FALSE, "homogeneous", TRUE, NULL);
gtk_notebook_set_tab_pos(GTK_NOTEBOOK(omp_notebook), GTK_POS_BOTTOM);
gtk_container_add(GTK_CONTAINER(omp_window), GTK_WIDGET(omp_notebook));
+ gtk_widget_show(omp_notebook);
+ omp_notebook_tab_ids = g_new0(struct _omp_notebook_tab_ids, 1);
omp_notebook_tabs = g_new0(struct _omp_notebook_tabs, 1);
// Add main page
- omp_notebook_tabs->main = omp_main_page_create(GTK_WINDOW(omp_window));
- omp_notebook_add_page_with_icon(omp_notebook, omp_notebook_tabs->main,
+ page = omp_main_page_create();
+ omp_notebook_add_page_with_icon(omp_notebook, page,
MOKO_STOCK_SPEAKER, NOTEBOOK_PAGE_PADDING);
+ omp_notebook_tab_ids->main = page_id++;
+ omp_notebook_tabs->main = page;
// Add playlist page
- omp_notebook_tabs->playlists = omp_playlist_page_create(GTK_WINDOW(omp_window));
- omp_notebook_add_page_with_icon(omp_notebook, omp_notebook_tabs->playlists,
+ page = omp_playlist_page_create();
+ omp_notebook_add_page_with_icon(omp_notebook, page,
MOKO_STOCK_VIEW, NOTEBOOK_PAGE_PADDING);
+ omp_notebook_tab_ids->playlists = page_id++;
+ omp_notebook_tabs->playlists = page;
+
+ // Add playlist editor page
+/* page = omp_editor_page_create();
+ omp_notebook_add_page_with_icon(omp_notebook, page,
+ "gtk-index", NOTEBOOK_PAGE_PADDING);
+ omp_notebook_tab_ids->editor = page_id++;
+ omp_notebook_tabs->editor = page; */
}
/**
@@ -272,7 +274,7 @@
void
omp_window_free()
{
- g_free(omp_notebook_tabs);
+ g_free(omp_notebook_tab_ids);
}
/**
@@ -281,7 +283,7 @@
void
omp_window_show()
{
- gtk_widget_show_all(omp_window);
+ gtk_widget_show(omp_window);
}
/**
@@ -361,6 +363,10 @@
omp_playback_init();
omp_playlist_init();
omp_window_create_pages();
+
+ // Let the UI catch up
+ while (gtk_events_pending()) gtk_main_iteration();
+
omp_session_restore_state();
omp_window_show();
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main.h
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main.h 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main.h 2007-08-21 07:41:07 UTC (rev 2752)
@@ -42,12 +42,19 @@
#define SESSION_FILE_NAME "/.openmoko-mediaplayer"
+struct _omp_notebook_tab_ids
+{
+ guint main, playlists, editor, files;
+};
+
struct _omp_notebook_tabs
{
- GtkWidget *main, *playlists, *files;
+ GtkWidget *main, *playlists, *editor, *files;
};
+extern struct _omp_notebook_tab_ids *omp_notebook_tab_ids;
extern struct _omp_notebook_tabs *omp_notebook_tabs;
+extern GtkWidget *omp_notebook;
extern GtkWidget *omp_window;
void omp_application_terminate();
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main_page.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main_page.c 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main_page.c 2007-08-21 07:41:07 UTC (rev 2752)
@@ -55,6 +55,7 @@
GtkWidget *shuffle_button;
GtkWidget *repeat_button;
GtkWidget *playlist_button;
+ GtkWidget *volume_hscale;
} main_widgets;
GtkWidget *omp_main_window = NULL;
@@ -62,8 +63,16 @@
gboolean omp_main_time_slider_can_update = TRUE; ///< Determines whether the time slider can be updated or not
gboolean omp_main_time_slider_was_dragged = FALSE; ///< Is toggled after the user finished dragging the time slider's button
+// Forward declarations for internal use
+void omp_main_update_track_change(gpointer instance, gpointer user_data);
+void omp_main_update_status_change(gpointer instance, gpointer user_data);
+void omp_main_update_track_position(gpointer instance, gpointer user_data);
+void omp_main_update_volume(gpointer instance, gpointer user_data);
+void omp_main_update_tag_artist(gpointer instance, const gchar *artist, gpointer user_data);
+void omp_main_update_tag_title(gpointer instance, const gchar *title, gpointer user_data);
+
/**
* Updates the UI volume display
* @param vol Volume to show, in percent
@@ -237,19 +246,10 @@
}
/**
- * Event handler for the Playlist button
- */
-void
-omp_playlist_button_callback(GtkWidget *widget, gpointer data)
-{
- // ...
-}
-
-/**
* Event handler for the Fast Forward button
*/
void
-omp_main_button_fast_forward_callback()
+omp_main_button_fast_forward_callback(GtkWidget *widget, gpointer data)
{
// Set new position and resume playback that was paused when dragging started
omp_playback_set_track_position(omp_playback_get_track_position()+BUTTON_SEEK_DISTANCE);
@@ -262,7 +262,7 @@
* Event handler for the Rewind button
*/
void
-omp_main_button_rewind_callback()
+omp_main_button_rewind_callback(GtkWidget *widget, gpointer data)
{
// Set new position and resume playback that was paused when dragging started
omp_playback_set_track_position(omp_playback_get_track_position()-BUTTON_SEEK_DISTANCE);
@@ -275,7 +275,7 @@
* Event handler for the Play/Pause button
*/
void
-omp_main_button_play_pause_callback()
+omp_main_button_play_pause_callback(GtkWidget *widget, gpointer data)
{
if (omp_playback_get_state() != OMP_PLAYBACK_STATE_PLAYING)
{
@@ -288,11 +288,43 @@
}
/**
+ * Gets called when the volume slider's value got changed
+ */
+void
+omp_main_volume_slider_changed(GtkRange *range, gpointer data)
+{
+ omp_playback_set_volume(gtk_range_get_value(GTK_RANGE(range)));
+}
+
+/**
+ * Resets the UI to a "no track loaded" state
+ */
+void
+omp_main_reset_ui(gpointer instance, gpointer user_data)
+{
+ gchar *caption;
+
+ gtk_label_set_text(GTK_LABEL(main_widgets.title_label), "No track info available");
+ gtk_label_set_text(GTK_LABEL(main_widgets.artist_label), "");
+
+ caption = g_strdup_printf(OMP_WIDGET_CAPTION_TRACK_NUM, 0, 0);
+ gtk_label_set_text(GTK_LABEL(main_widgets.track_number_label), caption);
+ g_free(caption);
+
+ caption = g_strdup_printf(OMP_WIDGET_CAPTION_TRACK_TIME, 0, 0, 0, 0);
+ gtk_label_set_text(GTK_LABEL(main_widgets.time_label), caption);
+ g_free(caption);
+
+ gtk_range_set_range(GTK_RANGE(main_widgets.time_hscale), 0, 1);
+ gtk_range_set_value(GTK_RANGE(main_widgets.time_hscale), 0.0);
+}
+
+/**
* Creates a button framed by a GtkAlignment
* @param image_name Path and file name of the image to use as pixmap
* @return A GtkAlignment containing the button
*/
-GtkWidget*
+GtkWidget *
omp_button_create(gchar *image_name, gint pad_left, GCallback callback, GtkWidget **button)
{
GtkWidget *image;
@@ -322,7 +354,7 @@
* @param image_name The name of the image resource to use, not a file name
* @return The button
*/
-GtkWidget*
+GtkWidget *
omp_stock_button_create(gchar *image_name, GtkWidget **image, GCallback callback)
{
GtkWidget *button;
@@ -348,8 +380,7 @@
{
GtkWidget *alignment;
GtkWidget *mainvbox;
- GtkWidget *background_vbox;
- GtkWidget *upper_hbox, *middle_hbox, *lower_hbox, *controls_hbox;
+ GtkWidget *upper_hbox, *middle_hbox, *lower_hbox;
GtkWidget *middle_right_vbox;
GtkWidget *image, *button;
@@ -362,14 +393,12 @@
// Title label
alignment = create_label(&main_widgets.title_label, "Bitstream Vera Sans 24", "black", 0, 0, 1, 0, 18);
- gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 18, 0, 50, 30);
- gtk_label_set_text(GTK_LABEL(main_widgets.title_label), "No track available");
+ gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 18, 0, 35, 30);
gtk_box_pack_start(GTK_BOX(mainvbox), GTK_WIDGET(alignment), TRUE, TRUE, 0);
// Artist label
alignment = create_label(&main_widgets.artist_label, "Bitstream Vera Sans 14", "black", 0, 0, 1, 0, 30);
- gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 5, 0, 50, 30);
- gtk_label_set_text(GTK_LABEL(main_widgets.artist_label), "");
+ gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 5, 0, 35, 30);
gtk_box_pack_start(GTK_BOX(mainvbox), GTK_WIDGET(alignment), TRUE, TRUE, 0);
// --- --- --- --- --- Upper hbox --- --- --- --- ---
@@ -383,28 +412,22 @@
// Track number icon
alignment = gtk_alignment_new(0, 0, 0, 0);
- gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 2, 0, 0, 15);
+ gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 2, 0, 14, 15);
gtk_box_pack_start(GTK_BOX(upper_hbox), GTK_WIDGET(alignment), TRUE, TRUE, 0);
container_add_image(GTK_CONTAINER(alignment), "ico-track.png");
// Track number
- alignment = create_label(&main_widgets.track_number_label, "Bitstream Vera Sans 14", "black", 0, 0, 0, 0, 12);
- caption = g_strdup_printf(WIDGET_CAPTION_TRACK_NUM, 0, 0);
- gtk_label_set_text(GTK_LABEL(main_widgets.track_number_label), caption);
- g_free(caption);
+ alignment = create_label(&main_widgets.track_number_label, "Bitstream Vera Sans 14", "black", 0, 0, 0, 0, 0);
gtk_box_pack_start(GTK_BOX(upper_hbox), GTK_WIDGET(alignment), TRUE, TRUE, 0);
// Time icon
alignment = gtk_alignment_new(0, 0, 0, 0);
- gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 2, 0, 48, 15);
+ gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 2, 0, 86, 15);
gtk_box_pack_start(GTK_BOX(upper_hbox), GTK_WIDGET(alignment), TRUE, TRUE, 0);
container_add_image(GTK_CONTAINER(alignment), "ico-time.png");
// Time
- alignment = create_label(&main_widgets.time_label, "Bitstream Vera Sans 14", "black", 0, 0, 0, 0, 12);
- caption = g_strdup_printf(WIDGET_CAPTION_TRACK_TIME, 0, 0, 0, 0);
- gtk_label_set_text(GTK_LABEL(main_widgets.time_label), caption);
- g_free(caption);
+ alignment = create_label(&main_widgets.time_label, "Bitstream Vera Sans 14", "black", 0, 0, 0, 0, 0);
gtk_box_pack_start(GTK_BOX(upper_hbox), GTK_WIDGET(alignment), TRUE, TRUE, 0);
// --- --- --- --- --- Slider --- --- --- --- ---
@@ -419,7 +442,6 @@
GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(main_widgets.time_hscale), GTK_CAN_FOCUS);
gtk_widget_set_size_request(GTK_WIDGET(main_widgets.time_hscale), 338, 35);
gtk_range_set_update_policy(GTK_RANGE(main_widgets.time_hscale), GTK_UPDATE_DISCONTINUOUS);
- gtk_range_set_value(GTK_RANGE(main_widgets.time_hscale), 0.0);
g_signal_connect(G_OBJECT(main_widgets.time_hscale), "value_changed", G_CALLBACK(omp_main_time_slider_changed), NULL);
g_signal_connect(G_OBJECT(main_widgets.time_hscale), "button-press-event", G_CALLBACK(omp_main_time_slider_drag_start), NULL);
g_signal_connect(G_OBJECT(main_widgets.time_hscale), "button-release-event", G_CALLBACK(omp_main_time_slider_drag_stop), NULL);
@@ -464,10 +486,10 @@
container_add_image_with_ref(GTK_CONTAINER(alignment), "ind-music-volume-00.png", &main_widgets.volume_image);
// Volume label
- alignment = create_label(&main_widgets.volume_label, "Sans 14", "darkorange", 0, 0, 1, 0, 4);
+ alignment = create_label(&main_widgets.volume_label, "Sans 14", "darkorange", 0, 0, 1, 0, 0);
gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 6, 0, 10, 0);
gtk_box_pack_start(GTK_BOX(volume_box), GTK_WIDGET(alignment), TRUE, TRUE, 0);
- caption = g_strdup_printf(WIDGET_CAPTION_VOLUME, 0);
+ caption = g_strdup_printf(OMP_WIDGET_CAPTION_VOLUME, 0);
gtk_label_set_text(GTK_LABEL(main_widgets.volume_label), caption);
g_free(caption);
@@ -483,7 +505,7 @@
alignment = gtk_alignment_new(0, 0, 0, 0);
gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 25, 0, 0, 0);
gtk_box_pack_start(GTK_BOX(mainvbox), alignment, TRUE, TRUE, 0);
- lower_hbox = gtk_hbox_new(FALSE, 0);
+ lower_hbox = gtk_hbutton_box_new();
gtk_container_add(GTK_CONTAINER(alignment), lower_hbox);
// Shuffle toggle button
@@ -493,53 +515,80 @@
// Repeat toggle button
alignment = omp_button_create("ico-repeat.png", 10, G_CALLBACK(omp_repeat_button_callback), &main_widgets.repeat_button);
gtk_box_pack_start(GTK_BOX(lower_hbox), alignment, TRUE, TRUE, 0);
+}
- // Playlist button
- alignment = omp_button_create("ico-list.png", 10, G_CALLBACK(omp_playlist_button_callback), &main_widgets.playlist_button);
- gtk_box_pack_start(GTK_BOX(lower_hbox), alignment, TRUE, TRUE, 0);
+/**
+ * Creates the widgets that didn't originally belong to the main UI
+ */
+void
+omp_main_secondary_widgets_create(GtkContainer *destination)
+{
+ GtkWidget *alignment;
+ GtkWidget *mainvbox;
+ GtkWidget *vbox, *hbox, *vol_vbox, *bal_hbox;
+ GtkWidget *image, *button, *vol_scale;
+ // Add mainvbox to destination container
+ mainvbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(destination), GTK_WIDGET(mainvbox));
+ gtk_widget_set_size_request(GTK_WIDGET(mainvbox), 480, -1);
+
// --- --- --- --- --- Player controls --- --- --- --- --- ---
alignment = gtk_alignment_new(0, 0, 1, 0);
- gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 40, 0, 0, 0);
gtk_box_pack_start(GTK_BOX(mainvbox), alignment, TRUE, TRUE, 0);
- controls_hbox = gtk_hbox_new(FALSE, 0);
- gtk_container_add(GTK_CONTAINER(alignment), controls_hbox);
+ hbox = gtk_hbutton_box_new();
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_SPREAD);
+ gtk_container_add(GTK_CONTAINER(alignment), hbox);
- gtk_box_set_homogeneous(GTK_BOX(controls_hbox), TRUE);
-
// Previous Track button
- button = omp_stock_button_create("gtk-media-previous-ltr", &image, G_CALLBACK(omp_playlist_set_prev_track));
- gtk_box_pack_start(GTK_BOX(controls_hbox), button, TRUE, TRUE, 0);
- gtk_box_set_child_packing(GTK_BOX(controls_hbox), GTK_WIDGET(button), FALSE, FALSE, 0, GTK_PACK_START);
+ button = omp_stock_button_create("gtk-media-previous-ltr", &image,
+ G_CALLBACK(omp_playlist_set_prev_track));
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
// Rewind button
- button = omp_stock_button_create("gtk-media-rewind-ltr", &image, G_CALLBACK(omp_main_button_rewind_callback));
- gtk_box_pack_start(GTK_BOX(controls_hbox), button, TRUE, TRUE, 0);
- gtk_box_set_child_packing(GTK_BOX(controls_hbox), GTK_WIDGET(button), FALSE, FALSE, 0, GTK_PACK_START);
+ button = omp_stock_button_create("gtk-media-rewind-ltr", &image,
+ G_CALLBACK(omp_main_button_rewind_callback));
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
// Play/Pause button
- button = omp_stock_button_create("gtk-media-play-ltr", &main_widgets.play_pause_button_image, G_CALLBACK(omp_main_button_play_pause_callback));
- gtk_box_pack_start(GTK_BOX(controls_hbox), button, TRUE, TRUE, 0);
- gtk_box_set_child_packing(GTK_BOX(controls_hbox), GTK_WIDGET(button), FALSE, FALSE, 0, GTK_PACK_START);
+ button = omp_stock_button_create("gtk-media-play-ltr", &main_widgets.play_pause_button_image,
+ G_CALLBACK(omp_main_button_play_pause_callback));
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
// Fast Forward button
- button = omp_stock_button_create("gtk-media-forward-ltr", &image, G_CALLBACK(omp_main_button_fast_forward_callback));
- gtk_box_pack_start(GTK_BOX(controls_hbox), button, TRUE, TRUE, 0);
- gtk_box_set_child_packing(GTK_BOX(controls_hbox), GTK_WIDGET(button), FALSE, FALSE, 0, GTK_PACK_START);
+ button = omp_stock_button_create("gtk-media-forward-ltr", &image,
+ G_CALLBACK(omp_main_button_fast_forward_callback));
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
// Next Track button
- button = omp_stock_button_create("gtk-media-next-ltr", &image, G_CALLBACK(omp_playlist_set_next_track));
- gtk_box_pack_start(GTK_BOX(controls_hbox), button, TRUE, TRUE, 0);
- gtk_box_set_child_packing(GTK_BOX(controls_hbox), GTK_WIDGET(button), FALSE, FALSE, 0, GTK_PACK_START);
+ button = omp_stock_button_create("gtk-media-next-ltr", &image,
+ G_CALLBACK(omp_playlist_set_next_track));
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+ // --- --- --- --- --- Volume control --- --- --- --- --- ---
+
+ alignment = gtk_alignment_new(0, 0, 0, 0);
+ gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 15, 0, 71, 0);
+ gtk_box_pack_start(GTK_BOX(mainvbox), alignment, TRUE, TRUE, 0);
+
+ // Volume hscale
+ vol_scale = gtk_hscale_new_with_range(0.0, 100.0, 1.0);
+ gtk_scale_set_draw_value(GTK_SCALE(vol_scale), FALSE);
+ GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(vol_scale), GTK_CAN_FOCUS);
+ gtk_widget_set_size_request(GTK_WIDGET(vol_scale), 338, 35);
+ gtk_range_set_update_policy(GTK_RANGE(vol_scale), GTK_UPDATE_DISCONTINUOUS);
+ g_signal_connect(G_OBJECT(vol_scale), "value_changed", G_CALLBACK(omp_main_volume_slider_changed), NULL);
+ gtk_container_add(GTK_CONTAINER(alignment), GTK_WIDGET(vol_scale));
+ main_widgets.volume_hscale = vol_scale;
}
/**
- * Create the main UI page and all its elements
+ * Creates the main UI page and all its elements
*/
GtkWidget *
-omp_main_page_create(GtkWindow *window)
+omp_main_page_create()
{
GtkWidget *alignment, *bg_muxer;
@@ -548,18 +597,47 @@
// Background image
alignment = gtk_alignment_new(0, 0, 0, 0);
container_add_image(GTK_CONTAINER(alignment), "background.png");
- gtk_fixed_put(GTK_FIXED(bg_muxer), GTK_WIDGET(alignment), 15, 30);
+ gtk_fixed_put(GTK_FIXED(bg_muxer), GTK_WIDGET(alignment), 15, 10);
// Create all widgets
alignment = gtk_alignment_new(0, 0, 0, 0);
omp_main_widgets_create(GTK_CONTAINER(alignment));
- gtk_fixed_put(GTK_FIXED(bg_muxer), GTK_WIDGET(alignment), 20, 47);
+ gtk_fixed_put(GTK_FIXED(bg_muxer), GTK_WIDGET(alignment), 20, 26);
+ alignment = gtk_alignment_new(0, 0, 0, 0);
+ omp_main_secondary_widgets_create(GTK_CONTAINER(alignment));
+ gtk_fixed_put(GTK_FIXED(bg_muxer), GTK_WIDGET(alignment), 0, 420);
+
+ omp_main_reset_ui(NULL, NULL);
+
// Set up signal handlers
- g_signal_connect(G_OBJECT(window), OMP_EVENT_PLAYLIST_TRACK_CHANGED, G_CALLBACK(omp_main_update_track_change), NULL);
- g_signal_connect(G_OBJECT(window), OMP_EVENT_PLAYBACK_STATUS_CHANGED, G_CALLBACK(omp_main_update_status_change), NULL);
- g_signal_connect(G_OBJECT(window), OMP_EVENT_PLAYBACK_POSITION_CHANGED, G_CALLBACK(omp_main_update_track_position), NULL);
+ g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYLIST_TRACK_CHANGED,
+ G_CALLBACK(omp_main_update_track_change), NULL);
+ g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYLIST_TRACK_COUNT_CHANGED,
+ G_CALLBACK(omp_main_update_track_change), NULL);
+
+ g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_RESET,
+ G_CALLBACK(omp_main_reset_ui), NULL);
+
+ g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_STATUS_CHANGED,
+ G_CALLBACK(omp_main_update_status_change), NULL);
+
+ g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_POSITION_CHANGED,
+ G_CALLBACK(omp_main_update_track_position), NULL);
+
+ g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_VOLUME_CHANGED,
+ G_CALLBACK(omp_main_update_volume), NULL);
+
+ g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_META_ARTIST_CHANGED,
+ G_CALLBACK(omp_main_update_tag_artist), NULL);
+
+ g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_META_TITLE_CHANGED,
+ G_CALLBACK(omp_main_update_tag_title), NULL);
+
+ // Make all widgets visible
+ gtk_widget_show_all(bg_muxer);
+
return bg_muxer;
}
@@ -568,7 +646,7 @@
* @note This function only checks elements that don't change too often - for the rest we have specialized functions below
*/
void
-omp_main_update_track_change()
+omp_main_update_track_change(gpointer instance, gpointer user_data)
{
static gint old_track_count = 0;
static gint old_track_id = 0;
@@ -576,6 +654,7 @@
gulong track_length, track_position;
gchar *text;
+ gint track_id;
// Track id/track count changed?
if ( (omp_playlist_track_count != old_track_count) || (omp_playlist_current_track_id != old_track_id) )
@@ -583,11 +662,9 @@
old_track_count = omp_playlist_track_count;
old_track_id = omp_playlist_current_track_id;
- // Update session
- omp_session_set_track_id(omp_playlist_current_track_id);
-
// Update label
- text = g_strdup_printf(WIDGET_CAPTION_TRACK_NUM, omp_playlist_current_track_id+1, omp_playlist_track_count);
+ track_id = (omp_playlist_track_count) ? omp_playlist_current_track_id+1 : 0;
+ text = g_strdup_printf(OMP_WIDGET_CAPTION_TRACK_NUM, track_id, omp_playlist_track_count);
gtk_label_set_text(GTK_LABEL(main_widgets.track_number_label), text);
g_free(text);
}
@@ -604,9 +681,9 @@
gtk_range_set_increments(GTK_RANGE(main_widgets.time_hscale), track_length/10, track_length/10);
// Update label and slider
- text = g_strdup_printf(WIDGET_CAPTION_TRACK_TIME,
- track_position / 60, track_position % 60,
- track_length / 60, track_length % 60);
+ text = g_strdup_printf(OMP_WIDGET_CAPTION_TRACK_TIME,
+ track_position / 60000, (track_position/1000) % 60,
+ track_length / 60000, (track_length/1000) % 60);
gtk_label_set_text(GTK_LABEL(main_widgets.time_label), text);
g_free(text);
@@ -623,14 +700,18 @@
* Updates the UI if playback engine switched between paused and playing modes
*/
void
-omp_main_update_status_change()
+omp_main_update_status_change(gpointer instance, gpointer user_data)
{
// Update Play/Pause button pixmap
if (omp_playback_get_state() == OMP_PLAYBACK_STATE_PAUSED)
{
- gtk_image_set_from_icon_name(GTK_IMAGE(main_widgets.play_pause_button_image), "gtk-media-play-ltr", BUTTON_PIXMAP_SIZE);
+ gtk_image_set_from_icon_name(GTK_IMAGE(main_widgets.play_pause_button_image),
+ "gtk-media-play-ltr", BUTTON_PIXMAP_SIZE);
+
} else {
- gtk_image_set_from_icon_name(GTK_IMAGE(main_widgets.play_pause_button_image), "gtk-media-pause", BUTTON_PIXMAP_SIZE);
+
+ gtk_image_set_from_icon_name(GTK_IMAGE(main_widgets.play_pause_button_image),
+ "gtk-media-pause", BUTTON_PIXMAP_SIZE);
}
}
@@ -638,7 +719,7 @@
* Updates the UI if the playback position changed
*/
void
-omp_main_update_track_position()
+omp_main_update_track_position(gpointer instance, gpointer user_data)
{
static gulong old_track_position = 0;
@@ -652,9 +733,9 @@
track_length = omp_playback_get_track_length();
// Update UI
- text = g_strdup_printf(WIDGET_CAPTION_TRACK_TIME,
- track_position / 60, track_position % 60,
- track_length / 60, track_length % 60);
+ text = g_strdup_printf(OMP_WIDGET_CAPTION_TRACK_TIME,
+ track_position / 60000, (track_position/1000) % 60,
+ track_length / 60000, (track_length/1000) % 60);
gtk_label_set_text(GTK_LABEL(main_widgets.time_label), text);
g_free(text);
@@ -668,3 +749,39 @@
}
+/**
+ * Updates the UI if the playback volume changed
+ */
+void
+omp_main_update_volume(gpointer instance, gpointer user_data)
+{
+ gchar *text;
+ guint volume;
+
+ volume = omp_playback_get_volume();
+
+ text = g_strdup_printf(OMP_WIDGET_CAPTION_VOLUME, volume);
+ gtk_label_set_text(GTK_LABEL(main_widgets.volume_label), text);
+ g_free(text);
+
+ gtk_range_set_value(GTK_RANGE(main_widgets.volume_hscale), volume);
+}
+
+/**
+ * Updates the UI if the track's artist changed
+ */
+void
+omp_main_update_tag_artist(gpointer instance, const gchar *artist, gpointer user_data)
+{
+ gtk_label_set_text(GTK_LABEL(main_widgets.artist_label), artist);
+}
+
+/**
+ * Updates the UI if the track's title changed
+ */
+void
+omp_main_update_tag_title(gpointer instance, const gchar *title, gpointer user_data)
+{
+ gtk_label_set_text(GTK_LABEL(main_widgets.title_label), title);
+}
+
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main_page.h
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main_page.h 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main_page.h 2007-08-21 07:41:07 UTC (rev 2752)
@@ -29,18 +29,13 @@
#include <gtk/gtk.h>
-#define WIDGET_CAPTION_TRACK_TIME "%d:%.2d / %d:%.2d"
-#define WIDGET_CAPTION_TRACK_NUM "%.3d / %.3d"
-#define WIDGET_CAPTION_VOLUME "%d%%"
+#define OMP_WIDGET_CAPTION_TRACK_TIME "%d:%.2d / %d:%.2d"
+#define OMP_WIDGET_CAPTION_TRACK_NUM "%.3d / %.3d"
+#define OMP_WIDGET_CAPTION_VOLUME "%d%%"
-// Determines how many seconds the engine will seek if the FFWD/REW buttons are clicked
-#define BUTTON_SEEK_DISTANCE 10
+// Determines how many milliseconds the engine will seek if the FFWD/REW buttons are clicked
+#define BUTTON_SEEK_DISTANCE 10000
+GtkWidget *omp_main_page_create();
-GtkWidget *omp_main_page_create(GtkWindow *window);
-
-void omp_main_update_track_change();
-void omp_main_update_status_change();
-void omp_main_update_track_position();
-
#endif
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/persistent.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/persistent.c 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/persistent.c 2007-08-21 07:41:07 UTC (rev 2752)
@@ -90,6 +90,18 @@
}
/**
+ * Fills the session data with sane default values
+ */
+void
+omp_session_reset()
+{
+ memset(omp_session, 0, sizeof(struct _omp_session));
+
+ omp_session->volume = 100;
+ omp_session->fade_speed = 5000;
+}
+
+/**
* Restores program state from last session
*/
void
@@ -104,12 +116,16 @@
omp_session = g_new0(struct _omp_session, 1);
- // Load config and last used playlist if set
+ // Load config
omp_session_load();
+ omp_playback_set_volume(omp_session->volume);
+
if (omp_session->playlist_file[0])
{
- omp_playlist_load(omp_session->playlist_file);
+ // Don't reset playlist state on load or else we'll alter the session
+ // data in unwanted ways since the new session state would be saved
+ omp_playlist_load(omp_session->playlist_file, FALSE);
}
// Check whether playlist_position is valid
@@ -126,6 +142,7 @@
{
if (omp_session->was_playing)
{
+ omp_playback_fade_volume();
omp_playback_play();
}
@@ -204,12 +221,13 @@
io_error:
#ifdef DEBUG
g_printerr("Failed trying to load session data from %s: %s\n", file_name, strerror(errno));
+ g_printerr("Resetting session data\n");
#endif
g_free(file_name);
- // Clear session data on error - just to be safe
- memset(omp_session, 0, sizeof(struct _omp_session));
+ // Reset session data on error - just to be safe
+ omp_session_reset();
}
/**
@@ -244,3 +262,12 @@
omp_session_save();
}
+/**
+ * Set volume to be set next session
+ */
+void
+omp_session_set_volume(guint volume)
+{
+ omp_session->volume = volume;
+ omp_session_save();
+}
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/persistent.h
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/persistent.h 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/persistent.h 2007-08-21 07:41:07 UTC (rev 2752)
@@ -45,11 +45,13 @@
};
/// Session-persistent data
-/// @note Default values should be 0/FALSE as session data will be zeroed on error
+/// @note Default values are set in omp_session_reset()
struct _omp_session
{
+ guint volume; ///< Playback volume in percent (0..100)
+ guint fade_speed; ///< Volume fading speed in milliseconds
guint playlist_position; ///< Position within the playlist
- glong track_position; ///< Position to resume playback from within the last played track
+ gulong track_position; ///< Position to resume playback from within the last played track
gboolean was_playing; ///< Set to TRUE of track was being played as the player was closed
gchar filesel_path[256]; ///< Last path used in the file selection dialog
gchar playlist_file[256]; ///< Path and file name of current (=last used) playlist
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playback.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playback.c 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playback.c 2007-08-21 07:41:07 UTC (rev 2752)
@@ -34,8 +34,15 @@
gboolean omp_playback_ui_timeout_halted; ///< Flag that tells the UI-updating timeout to exit if set
gulong omp_playback_pending_position = 0; ///< Since we can't set a new position if element is not paused or playing we store the position here and set it when it reached either state
+// Some private forward declarations
+static gboolean omp_gst_message_eos(GstBus *bus, GstMessage *message, gpointer data);
+static gboolean omp_gst_message_state_changed(GstBus *bus, GstMessage *message, gpointer data);
+static gboolean omp_gst_message_error(GstBus *bus, GstMessage *message, gpointer data);
+static gboolean omp_gst_message_warning(GstBus *bus, GstMessage *message, gpointer data);
+static gboolean omp_gst_message_tag(GstBus *bus, GstMessage *message, gpointer data);
+
/**
* Initializes gstreamer by setting up pipe, message hooks and bins
*/
@@ -51,10 +58,34 @@
}
// Create the signals we'll emit
- g_signal_new(OMP_EVENT_PLAYBACK_EOS, G_TYPE_OBJECT, 0, 0, 0, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
- g_signal_new(OMP_EVENT_PLAYBACK_STATUS_CHANGED, G_TYPE_OBJECT, 0, 0, 0, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
- g_signal_new(OMP_EVENT_PLAYBACK_POSITION_CHANGED, G_TYPE_OBJECT, 0, 0, 0, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
+ g_signal_new(OMP_EVENT_PLAYBACK_RESET, G_TYPE_OBJECT,
+ G_SIGNAL_RUN_FIRST, 0, 0, NULL, g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ g_signal_new(OMP_EVENT_PLAYBACK_EOS, G_TYPE_OBJECT,
+ G_SIGNAL_RUN_FIRST, 0, 0, NULL, g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_signal_new(OMP_EVENT_PLAYBACK_STATUS_CHANGED, G_TYPE_OBJECT,
+ G_SIGNAL_RUN_FIRST, 0, 0, NULL, g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_signal_new(OMP_EVENT_PLAYBACK_POSITION_CHANGED, G_TYPE_OBJECT,
+ G_SIGNAL_RUN_FIRST, 0, 0, NULL, g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_signal_new(OMP_EVENT_PLAYBACK_VOLUME_CHANGED, G_TYPE_OBJECT,
+ G_SIGNAL_RUN_FIRST, 0, 0, NULL, g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_signal_new(OMP_EVENT_PLAYBACK_META_ARTIST_CHANGED, G_TYPE_OBJECT,
+ G_SIGNAL_RUN_FIRST, 0, 0, NULL, g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ g_signal_new(OMP_EVENT_PLAYBACK_META_TITLE_CHANGED, G_TYPE_OBJECT,
+ G_SIGNAL_RUN_FIRST, 0, 0, NULL, g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+
// Set up gstreamer pipe and bins
omp_gst_playbin = gst_element_factory_make("playbin", "play");
@@ -66,6 +97,7 @@
g_signal_connect(bus, "message::error", G_CALLBACK(omp_gst_message_error), NULL);
g_signal_connect(bus, "message::warning", G_CALLBACK(omp_gst_message_warning), NULL);
g_signal_connect(bus, "message::state-changed", G_CALLBACK(omp_gst_message_state_changed), NULL);
+ g_signal_connect(bus, "message::tag", G_CALLBACK(omp_gst_message_tag), NULL);
gst_object_unref(bus);
}
@@ -103,6 +135,17 @@
}
/**
+ * Stops playback and unloads the current track
+ */
+void
+omp_playback_reset()
+{
+ gst_element_set_state(omp_gst_playbin, GST_STATE_READY);
+
+ g_signal_emit_by_name(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_RESET);
+}
+
+/**
* Attempts to load a track from an URI
* @return TRUE if successful, FALSE if failed
*/
@@ -216,7 +259,7 @@
}
/**
- * Returns the number of seconds that the track has been playing so far
+ * Returns the number of milliseconds that the track has been playing so far
*/
gulong
omp_playback_get_track_position()
@@ -230,16 +273,18 @@
}
// Return 0 if function returns FALSE, position otherwise
- return (gst_element_query_position(omp_gst_playbin, &format, &position)) ? (position/GST_SECOND) : 0;
+ return (gst_element_query_position(omp_gst_playbin, &format, &position)) ? (position/1000000) : 0;
}
/**
* Sets the playback position of the currently loaded track
+ * @param position Track position in milliseconds
*/
void
-omp_playback_set_track_position(glong position)
+omp_playback_set_track_position(gulong position)
{
GstState pipe_state;
+ gint64 pos;
if (!omp_gst_playbin)
{
@@ -258,21 +303,28 @@
omp_playback_pending_position = position;
#ifdef DEBUG
- g_printf("Pended track position change to %d:%.2ds\n", position / 60, position % 60);
+ g_printf("Pended track position change to %d:%.2ds\n", position / 60000, (position/1000) % 60);
#endif
return;
}
omp_playback_pending_position = 0;
#ifdef DEBUG
- g_printf("Setting track position to %d:%.2ds\n", position / 60, position % 60);
+ g_printf("Setting track position to %d:%.2ds\n", position / 60000, (position/1000) % 60);
#endif
+ // Overflow workaround
+ pos = position;
+ pos = pos*1000000;
+
gst_element_seek(GST_ELEMENT(omp_gst_playbin), 1.0,
GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
- GST_SEEK_TYPE_SET, position*GST_SECOND,
+ GST_SEEK_TYPE_SET, pos,
GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
+ // Save session data
+ omp_playback_save_state();
+
g_signal_emit_by_name(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_POSITION_CHANGED);
}
@@ -290,11 +342,54 @@
return 0;
}
- // Return 0 if function returns FALSE, track length otherwise
- return (gst_element_query_duration(omp_gst_playbin, &format, &length)) ? (length/GST_SECOND) : 0;
+ gst_element_query_duration(omp_gst_playbin, &format, &length);
+ return (length > 0) ? (length/1000000) : 0;
}
/**
+ * Sets the playback volume
+ * @param volume Volume in percent (0..100)
+ */
+void
+omp_playback_set_volume(guint volume)
+{
+ // Sanity check and failure recovery
+ if (volume > 100)
+ {
+ g_warning("Attempted to set invalid volume!");
+ volume = 100;
+ }
+
+ // Set playbin volume which ranges from 0.0 to 1.0
+ g_object_set(G_OBJECT(omp_gst_playbin), "volume", volume/100.0, NULL);
+
+ omp_session_set_volume(volume);
+
+ g_signal_emit_by_name(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_VOLUME_CHANGED);
+}
+
+/**
+ * Returns the playback volume
+ * @return Volume in percent (0..100)
+ */
+guint
+omp_playback_get_volume()
+{
+ gdouble volume;
+ g_object_get(G_OBJECT(omp_gst_playbin), "volume", &volume, NULL);
+
+ return volume*100;
+}
+
+/**
+ * Sets up the fade-in timer
+ */
+void
+omp_playback_fade_volume()
+{
+}
+
+/**
* Handles gstreamer's end-of-stream notification
*/
static gboolean
@@ -336,6 +431,8 @@
previous_state = new_state;
g_signal_emit_by_name(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_STATUS_CHANGED);
}
+
+ return TRUE;
}
/**
@@ -371,3 +468,36 @@
return TRUE;
}
+
+/**
+ * Handles gstreamer's tag data notification
+ * @note We can not assume that all meta data will be sent in one go so we use one signal per entry
+ */
+static gboolean
+omp_gst_message_tag(GstBus *bus, GstMessage *message, gpointer data)
+{
+ GstTagList *tag_list;
+ gchar *s;
+
+ #ifdef DEBUG
+ g_printf("gstreamer discovered tag info\n");
+ #endif
+
+ gst_message_parse_tag(message, &tag_list);
+
+ // Read artist
+ if (gst_tag_list_get_string(tag_list, GST_TAG_ARTIST, &s))
+ {
+ g_signal_emit_by_name(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_META_ARTIST_CHANGED, s);
+ g_free(s);
+ }
+
+ // Read title
+ if (gst_tag_list_get_string(tag_list, GST_TAG_TITLE, &s))
+ {
+ g_signal_emit_by_name(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_META_TITLE_CHANGED, s);
+ g_free(s);
+ }
+
+ return TRUE;
+}
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playback.h
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playback.h 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playback.h 2007-08-21 07:41:07 UTC (rev 2752)
@@ -29,9 +29,13 @@
#include <gst/gst.h>
+#define OMP_EVENT_PLAYBACK_RESET "playback_reset"
#define OMP_EVENT_PLAYBACK_EOS "playback_end_of_stream"
#define OMP_EVENT_PLAYBACK_STATUS_CHANGED "playback_status_change"
#define OMP_EVENT_PLAYBACK_POSITION_CHANGED "playback_position_change"
+#define OMP_EVENT_PLAYBACK_VOLUME_CHANGED "playback_volume_change"
+#define OMP_EVENT_PLAYBACK_META_ARTIST_CHANGED "playback_tag_artist_change"
+#define OMP_EVENT_PLAYBACK_META_TITLE_CHANGED "playback_tag_title_change"
// Player states masking the gstreamer states so we can be more abstract
#define OMP_PLAYBACK_STATE_PAUSED GST_STATE_PAUSED
@@ -43,6 +47,7 @@
void omp_playback_init();
void omp_playback_free();
void omp_playback_save_state();
+void omp_playback_reset();
gboolean omp_playback_load_track_from_uri(gchar *uri);
@@ -50,12 +55,11 @@
void omp_playback_pause();
gint omp_playback_get_state();
gulong omp_playback_get_track_position();
-void omp_playback_set_track_position(glong position);
+void omp_playback_set_track_position(gulong position);
gulong omp_playback_get_track_length();
-static gboolean omp_gst_message_eos(GstBus *bus, GstMessage *message, gpointer data);
-static gboolean omp_gst_message_state_changed(GstBus *bus, GstMessage *message, gpointer data);
-static gboolean omp_gst_message_error(GstBus *bus, GstMessage *message, gpointer data);
-static gboolean omp_gst_message_warning(GstBus *bus, GstMessage *message, gpointer data);
+void omp_playback_set_volume(guint volume);
+guint omp_playback_get_volume();
+void omp_playback_fade_volume();
#endif
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist.c 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist.c 2007-08-21 07:41:07 UTC (rev 2752)
@@ -27,19 +27,31 @@
#include <glib.h>
#include <glib/gprintf.h>
#include <glib-object.h>
+#include <glib/gi18n.h>
#include <spiff/spiff_c.h>
+#include <string.h>
+
#include "playlist.h"
#include "main.h"
#include "playback.h"
#include "persistent.h"
-struct spiff_list *omp_playlist = NULL; ///< Loaded playlist
-guint omp_playlist_track_count = 0; ///< Number of tracks stored within the current playlist
+/// Loaded playlist
+struct spiff_list *omp_playlist = NULL;
-struct spiff_track *omp_playlist_current_track = NULL; ///< Current track's data
-guint omp_playlist_current_track_id = -1; ///< Numerical id of the current track within the playlist
+/// File name of currently loaded playlist
+gchar *omp_playlist_file = NULL;
+/// Number of tracks stored within the current playlist
+guint omp_playlist_track_count = 0;
+
+/// Current track's data
+struct spiff_track *omp_playlist_current_track = NULL;
+
+/// Numerical id of the current track within the playlist
+guint omp_playlist_current_track_id = -1;
+
/// This linked list holds all tracks that were played in this session, most recently played entry first
GSList *omp_track_history = NULL;
@@ -50,8 +62,14 @@
guint track_id;
};
+// Forward declarations for internal use
+void omp_playlist_process_eos_event(gpointer instance, gpointer user_data);
+void omp_playlist_process_tag_artist_change(gpointer instance, gchar *artist, gpointer user_data);
+void omp_playlist_process_tag_title_change(gpointer instance, gchar *title, gpointer user_data);
+void omp_playlist_check_track_duration();
+
/**
* Initialize all things playlist
*/
@@ -59,10 +77,31 @@
omp_playlist_init()
{
// Hook up event handlers to the playback routines
- g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_EOS, G_CALLBACK(omp_playlist_process_eos_event), NULL);
+ g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_EOS,
+ G_CALLBACK(omp_playlist_process_eos_event), NULL);
- // Create the signals we emit: no params, no return value
- g_signal_new(OMP_EVENT_PLAYLIST_TRACK_CHANGED, G_TYPE_OBJECT, 0, 0, 0, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
+ g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_META_ARTIST_CHANGED,
+ G_CALLBACK(omp_playlist_process_tag_artist_change), NULL);
+
+ g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_META_TITLE_CHANGED,
+ G_CALLBACK(omp_playlist_process_tag_title_change), NULL);
+
+ // Create the signals we emit
+ g_signal_new(OMP_EVENT_PLAYLIST_LOADED,
+ G_TYPE_OBJECT, G_SIGNAL_RUN_FIRST, 0, 0, NULL,
+ g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ g_signal_new(OMP_EVENT_PLAYLIST_TRACK_CHANGED,
+ G_TYPE_OBJECT, G_SIGNAL_RUN_FIRST, 0, 0, NULL,
+ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+ g_signal_new(OMP_EVENT_PLAYLIST_TRACK_INFO_CHANGED,
+ G_TYPE_OBJECT, G_SIGNAL_RUN_FIRST, 0, 0, NULL,
+ g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
+
+ g_signal_new(OMP_EVENT_PLAYLIST_TRACK_COUNT_CHANGED,
+ G_TYPE_OBJECT, G_SIGNAL_RUN_FIRST, 0, 0, NULL,
+ g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
}
/**
@@ -82,16 +121,24 @@
if (omp_playlist)
{
spiff_free(omp_playlist);
+ g_free(omp_playlist_file);
}
}
/**
* Load playlist
- * @todo Count playlist entries on load, trigger "playlist loaded" event to update UI
+ * @param playlist_file Absolute file name of playlist to load
+ * @param do_state_reset Determines whether to reset playlist state on successful load
+ * @return TRUE on success, FALSE on failure
*/
-void
-omp_playlist_load(gchar *playlist_file)
+gboolean
+omp_playlist_load(gchar *playlist_file, gboolean do_state_reset)
{
+ struct spiff_track *track;
+ guint track_num;
+ GtkWidget *dialog;
+ gchar *title;
+
// Free the track history's memory by deleting the first element until the list is empty
while (omp_track_history)
{
@@ -103,9 +150,10 @@
if (omp_playlist)
{
spiff_free(omp_playlist);
+ g_free(omp_playlist_file);
}
- // Update session unless target and source are the same
+ // Update session unless no change happened
if (omp_session->playlist_file != playlist_file)
{
omp_session_set_playlist(playlist_file);
@@ -114,13 +162,100 @@
// Load playlist
omp_playlist = spiff_parse(playlist_file);
- if (!omp_playlist)
+ if (omp_playlist)
{
- g_printerr("Could not load playlist: %s\n", playlist_file);
+ omp_playlist_file = g_strdup(playlist_file);
+
+ if (do_state_reset)
+ {
+ // Reset playlist state and prepare playback
+ omp_playback_reset();
+ omp_playlist_current_track_id = 0;
+ omp_playlist_current_track = omp_playlist->tracks;
+
+ if (omp_playlist_current_track)
+ {
+ omp_playlist_set_current_track(0);
+ omp_playlist_load_current_track();
+ }
+ }
+
+ omp_playlist_update_track_count();
+
+ title = get_playlist_title(omp_playlist_file);
+ g_signal_emit_by_name(G_OBJECT(omp_window), OMP_EVENT_PLAYLIST_LOADED, title);
+ g_free(title);
+
+ } else {
+
+ omp_playlist_current_track_id = -1;
+ omp_playlist_current_track = NULL;
+
+ #ifdef DEBUG
+ g_printerr("Could not load playlist: %s\n", playlist_file);
+ #endif
+
+ // Notify user
+ dialog = gtk_message_dialog_new(0,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ _("\nCould not load playlist '%s'"), playlist_file);
+
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
}
+
+ return omp_playlist ? TRUE : FALSE;
}
/**
+ * Creates a new playlist and loads it so it can be used/edited
+ */
+void
+omp_playlist_create(gchar *playlist_file)
+{
+ if (omp_playlist)
+ {
+ spiff_free(omp_playlist);
+ g_free(omp_playlist_file);
+ }
+
+ // Create new playlist, save and load it
+ omp_playlist = spiff_new();
+ omp_playlist_file = g_strdup(playlist_file);
+ omp_playlist_save();
+ omp_playlist_load(playlist_file, TRUE);
+}
+
+/**
+ * Saves the currently loaded playlist to disk
+ */
+void
+omp_playlist_save()
+{
+ if (omp_playlist && omp_playlist_file)
+ {
+ spiff_write(omp_playlist, omp_playlist_file);
+ }
+}
+
+/**
+ * Deletes a playlist file, making sure things stay sane if it's currently loaded
+ * @todo Make unicode safe (-> g_filename_to_utf8())
+ */
+void
+omp_playlist_delete(gchar *playlist_file)
+{
+ if (strcmp(omp_playlist_file, playlist_file) == 0)
+ {
+ spiff_free(omp_playlist);
+ g_free(omp_playlist_file);
+ omp_playback_reset();
+ }
+
+ g_unlink(playlist_file);
+}
+
+/**
* Tries to set the position within the playlist and indicates success/failure
* @param playlist_pos New position, counting starts at 0
*/
@@ -131,17 +266,13 @@
struct spiff_track *track;
gint track_num = 0;
- #ifdef DEBUG
- g_printf("Setting current track to #%d\n", playlist_pos);
- #endif
-
if (!omp_playlist)
{
return FALSE;
}
// Walk through the playlist and see if the new position is valid
- for (track=omp_playlist->tracks; track!=NULL; track=track->next, track_num++)
+ for (track=omp_playlist->tracks; (track!=NULL) && (!position_valid); track=track->next, track_num++)
{
if (track_num == playlist_pos)
{
@@ -153,7 +284,8 @@
if (position_valid)
{
- omp_playlist_track_count = track_num;
+ // Update session
+ omp_session_set_track_id(omp_playlist_current_track_id);
// Emit signal to update UI and the like
g_signal_emit_by_name(G_OBJECT(omp_window), OMP_EVENT_PLAYLIST_TRACK_CHANGED);
@@ -246,6 +378,9 @@
if (is_new_track)
{
+ // Update session
+ omp_session_set_track_id(omp_playlist_current_track_id);
+
// Emit signal to update UI and the like
g_signal_emit_by_name(G_OBJECT(omp_window), OMP_EVENT_PLAYLIST_TRACK_CHANGED);
@@ -308,6 +443,9 @@
// Add track to track history
omp_track_history = g_slist_prepend(omp_track_history, (gpointer)history_entry);
+ // Update session
+ omp_session_set_track_id(omp_playlist_current_track_id);
+
// Emit signal to update UI and the like
g_signal_emit_by_name(G_OBJECT(omp_window), OMP_EVENT_PLAYLIST_TRACK_CHANGED);
@@ -335,7 +473,8 @@
/**
* Signal handler that gets called whenever the current stream ends
*/
-void omp_playlist_process_eos_event()
+void
+omp_playlist_process_eos_event(gpointer instance, gpointer user_data)
{
if (omp_playlist_set_next_track())
{
@@ -345,12 +484,129 @@
}
/**
+ * Updates the track's artist information in the playlist on incoming tag data
+ * @note This is our way of caching metadata information so we can display it in the playlist editor
+ * @param instance Ignored
+ * @param title Artist of currently played track
+ * @param user_data Ignored
+ */
+void
+omp_playlist_process_tag_artist_change(gpointer instance, gchar *artist, gpointer user_data)
+{
+ gchar **tokens;
+
+ // Now that we have received metadata information we might also have the track duration ready
+ omp_playlist_check_track_duration();
+
+ if (!omp_playlist_current_track) return;
+
+ // Set preliminary title if nothing was set at all
+ if (!omp_playlist_current_track->title)
+ {
+ omp_playlist_current_track->title = g_strdup_printf(_("%s - [unknown title]"), artist);
+ return;
+ }
+
+ // Split title into artist/title to see if we need to replace the artist part
+ /// @todo Make unicode safe
+ tokens = g_strsplit(omp_playlist_current_track->title, " - ", 2);
+ if (!tokens) return;
+
+ if (strcmp(tokens[0], _("[unknown artist]")) == 0)
+ {
+ // Yep, track artist was our placeholder, so we replace it
+ g_free(omp_playlist_current_track->title);
+ omp_playlist_current_track->title =
+ g_strdup_printf("%s - %s", artist, tokens[1]);
+ }
+
+ g_strfreev(tokens);
+
+ // Save changes to disk
+ omp_playlist_save();
+
+ // Notify UI of the change
+ g_signal_emit_by_name(G_OBJECT(omp_window), OMP_EVENT_PLAYLIST_TRACK_INFO_CHANGED,
+ omp_playlist_current_track_id);
+}
+
+/**
+ * Updates the track's artist information in the playlist on incoming tag data
+ * @note This is our way of caching metadata information so we can display it in the playlist editor
+ * @param instance Ignored
+ * @param title Song title of currently played track
+ * @param user_data Ignored
+ */
+void
+omp_playlist_process_tag_title_change(gpointer instance, gchar *title, gpointer user_data)
+{
+ gchar **tokens;
+
+ // Now that we have received metadata information we might also have the track duration ready
+ omp_playlist_check_track_duration();
+
+ if (!omp_playlist_current_track) return;
+
+ // Set preliminary title if nothing was set at all
+ if (!omp_playlist_current_track->title)
+ {
+ omp_playlist_current_track->title = g_strdup_printf("[unknown artist] - %s", title);
+ return;
+ }
+
+ // Split title into artist/title to see if we need to replace the title part
+ /// @todo Make unicode safe
+ tokens = g_strsplit(omp_playlist_current_track->title, " - ", 2);
+ if (!tokens) return;
+
+ if (strcmp(tokens[1], _("[unknown title]")) == 0)
+ {
+ // Yep, track title was our placeholder, so we replace it
+ g_free(omp_playlist_current_track->title);
+ omp_playlist_current_track->title =
+ g_strdup_printf("%s - %s", tokens[0], title);
+ }
+
+ g_strfreev(tokens);
+
+ // Save changes to disk
+ omp_playlist_save();
+
+ // Notify UI of the change
+ g_signal_emit_by_name(G_OBJECT(omp_window), OMP_EVENT_PLAYLIST_TRACK_INFO_CHANGED,
+ omp_playlist_current_track_id);
+}
+
+/**
+ * Checks to see if we can get track duration information from the playback interface
+ */
+void
+omp_playlist_check_track_duration()
+{
+ gulong duration;
+
+ if (!omp_playlist_current_track) return;
+
+ // Check if we can update duration information (spiff saves it in milliseconds as well)
+ duration = omp_playback_get_track_length();
+ if ( (duration > 0) && (duration != omp_playlist_current_track->duration) )
+ {
+ omp_playlist_current_track->duration = duration;
+ omp_playlist_save();
+
+ // Notify UI of the change
+ g_signal_emit_by_name(G_OBJECT(omp_window), OMP_EVENT_PLAYLIST_TRACK_INFO_CHANGED,
+ omp_playlist_current_track_id);
+ }
+}
+
+/**
* Uses the URI(s) and metadata information of a track to locate the resource to play, then returns a playable URI
* @return URI of a playable resource
* @note If the URI of the found resource is not in the track's locations list already it is added and the playlist saved
* @todo Actually make this function do what it's supposed to do :)
*/
-gchar*
+gchar *
omp_playlist_resolve_track(struct spiff_track *track)
{
if (!track)
@@ -359,10 +615,18 @@
return NULL;
}
+ if (!track->locations)
+ {
+ g_printerr("Resolve request for a track without any locations, returning null as URI.\n");
+ return NULL;
+ }
+
if (track->locations->value)
{
- return(g_strdup(track->locations->value));
+ return g_strdup(track->locations->value);
+
} else {
+
g_printerr("Resolve request for a track without a valid location, ignoring. Will be implemented later.\n");
return NULL;
}
@@ -392,9 +656,174 @@
{
track_loaded = omp_playback_load_track_from_uri(track_uri);
g_free(track_uri);
+
+ omp_playlist_check_track_duration();
+
return track_loaded;
}
return FALSE;
}
+/**
+ * Retrieves a track's meta data if possible
+ * @param track_id Track ID to get meta data of, starting at 0
+ * @param title Destination for the title string, can be NULL; must be freed after use
+ * @param duration Destination for the track duration (in milliseconds), can be NULL
+ * @todo List walking
+ */
+void
+omp_playlist_get_track_info(guint track_id, gchar **title, guint *duration)
+{
+ if (track_id == omp_playlist_current_track_id)
+ {
+ if (title) *title = g_strdup(omp_playlist_current_track->title);
+
+ // Again, spiff's internal duration is given in milliseconds
+ if (duration) *duration = omp_playlist_current_track->duration;
+
+ } else {
+
+ // It's not the current track we want to get the infos of so we need to walk the list
+ #ifdef DEBUG
+ g_print("List walking in omp_playlist_get_track_info() not yet implemented.\n");
+ #endif
+ }
+}
+
+/**
+ * Counts the number of tracks in the playlist and updates the UI
+ */
+void
+omp_playlist_update_track_count()
+{
+ struct spiff_track *track;
+ gint tracks = 0;
+
+ if (!omp_playlist) return;
+
+ for (track=omp_playlist->tracks; track!=NULL; track=track->next, tracks++);
+
+ omp_playlist_track_count = tracks;
+
+ g_signal_emit_by_name(G_OBJECT(omp_window), OMP_EVENT_PLAYLIST_TRACK_COUNT_CHANGED);
+}
+
+/**
+ * Creates an iterator for iterating over the playlist
+ * @return Returns a new iterator which is deallocated by omp_playlist_advance_iter() - or yourself
+ */
+playlist_iter *
+omp_playlist_init_iterator()
+{
+ playlist_iter *iter;
+
+ if (!omp_playlist) return NULL;
+ if (!omp_playlist->tracks) return NULL;
+
+ iter = g_new(playlist_iter, 1);
+
+ iter->track = omp_playlist->tracks;
+ iter->track_num = 0;
+
+ return iter;
+}
+
+/**
+ * Fetches information about the track the iter points at
+ * @param iter The iterator
+ * @param track_num Destination for the track number (can be NULL)
+ * @param track_title Destination for the track title (can be NULL), must be freed after use
+ */
+void
+omp_playlist_get_track_from_iter(playlist_iter *iter, guint *track_num, gchar **track_title,
+ guint *duration)
+{
+ // Sanity checks - one silent, one not
+ if (!iter) return;
+ g_return_if_fail(iter->track);
+
+ // Assign values
+ if (track_num)
+ {
+ *track_num = iter->track_num;
+ }
+
+ if (track_title)
+ {
+ if (iter->track)
+ {
+ *track_title = g_strdup(iter->track->title);
+ } else {
+ *track_title = NULL;
+ }
+ }
+
+ if (duration)
+ {
+ // Spiff saves the duration in milliseconds, too
+ *duration = (iter->track->duration > 0) ? iter->track->duration : 0;
+ }
+}
+
+/**
+ * Advances a playlist iterator by one track
+ */
+void
+omp_playlist_advance_iter(playlist_iter *iter)
+{
+ if (iter)
+ {
+ if (iter->track)
+ {
+ iter->track = iter->track->next;
+ iter->track_num++;
+ }
+ }
+}
+
+/**
+ * Determines whether an iterator has reached the end of the playlist
+ */
+gboolean
+omp_playlist_iter_finished(struct playlist_iter *iter)
+{
+ if (!iter) return TRUE;
+
+ return (iter->track) ? FALSE : TRUE;
+}
+
+/**
+ * Utility function that extracts a playlist's name from its file name
+ * @param playlist_file File name to extract title from, can contain a path
+ * @return String holding the title, must be freed after use
+ * @todo Make unicode safe
+ * @note Yes, this is quick'n'dirty. It will be replaced.
+ */
+gchar *
+get_playlist_title(gchar *playlist_file)
+{
+ gchar title[256];
+ guint i, j, last_delim, extension_pos;
+
+ // Find last directory delimiter
+ last_delim = 0;
+ for (i=0; playlist_file[i]; i++)
+ {
+ if (playlist_file[i] == '/') last_delim = i+1;
+ }
+
+ // Find file extension
+ for(extension_pos = strlen(playlist_file);
+ (extension_pos) && (playlist_file[extension_pos] != '.');
+ extension_pos--);
+
+ // Extract title
+ for (j=0, i=last_delim; i<extension_pos; i++)
+ {
+ title[j++] = playlist_file[i];
+ }
+ title[j] = 0;
+
+ return g_strdup((gchar *)&title);
+}
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist.h
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist.h 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist.h 2007-08-21 07:41:07 UTC (rev 2752)
@@ -27,9 +27,15 @@
#ifndef PLAYLIST_H
#define PLAYLIST_H
+#include <glib.h>
#include <spiff/spiff_c.h>
+#define OMP_PLAYLIST_FILE_EXTENSION "xspf"
+
+#define OMP_EVENT_PLAYLIST_LOADED "playlist_loaded"
#define OMP_EVENT_PLAYLIST_TRACK_CHANGED "playlist_track_changed"
+#define OMP_EVENT_PLAYLIST_TRACK_INFO_CHANGED "playlist_track_info_changed"
+#define OMP_EVENT_PLAYLIST_TRACK_COUNT_CHANGED "playlist_track_count_changed"
/// Modes available for repetitive track playback
enum omp_repeat_modes
@@ -42,20 +48,40 @@
extern struct spiff_list *omp_playlist;
extern guint omp_playlist_track_count;
+extern gchar *omp_playlist_title;
extern struct spiff_track *omp_playlist_current_track;
extern guint omp_playlist_current_track_id;
+/// Playlist iterator
+typedef struct playlist_iter
+{
+ struct spiff_track *track;
+ guint track_num;
+} playlist_iter;
+
void omp_playlist_init();
void omp_playlist_free();
-void omp_playlist_load(gchar *playlist_file);
+gboolean omp_playlist_load(gchar *playlist_file, gboolean do_state_reset);
+void omp_playlist_create(gchar *playlist_file);
+void omp_playlist_save();
+void omp_playlist_delete(gchar *playlist_file);
gboolean omp_playlist_set_current_track(gint playlist_pos);
gboolean omp_playlist_set_prev_track();
gboolean omp_playlist_set_next_track();
-void omp_playlist_process_eos_event();
gchar *omp_playlist_resolve_track(struct spiff_track *track);
gboolean omp_playlist_load_current_track();
+void omp_playlist_get_track_info(guint track_id, gchar **title, guint *duration);
+void omp_playlist_update_track_count();
+playlist_iter *omp_playlist_init_iterator();
+void omp_playlist_get_track_info_from_iter(playlist_iter *iter, guint *track_num,
+ gchar **track_title, guint *duration);
+void omp_playlist_advance_iter(playlist_iter *iter);
+gboolean omp_playlist_iter_finished(playlist_iter *iter);
+
+gchar *get_playlist_title(gchar *playlist_file);
+
#endif
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist_page.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist_page.c 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist_page.c 2007-08-21 07:41:07 UTC (rev 2752)
@@ -25,79 +25,318 @@
*/
#include <gtk/gtk.h>
+#include <glib/gi18n.h>
#include <libmokoui2/moko-finger-scroll.h>
+#include <libmokoui2/moko-stock.h>
#include "playlist_page.h"
#include "main.h"
+#include "guitools.h"
+#include "playlist.h"
-/// Enumeration for the list columns
+/// Enumeration for the playlist list columns
enum
{
- ICON_COLUMN,
+ TYPE_COLUMN,
NAME_COLUMN,
- ACTION_COLUMN,
+ ACT_DELETE_COLUMN,
+ FILE_NAME_COLUMN, // This one isn't shown, it's for internal storage only
COLUMN_COUNT
};
-/// The input field where a new playlist name is entered
-GtkWidget *omp_playlist_page_entry;
+/// Input field where a new playlist name is entered
+GtkWidget *omp_playlist_page_entry = NULL;
+/// List store for the playlist selector
+GtkListStore *omp_playlist_page_list_store = NULL;
+
+// Just a forward declaration we don't want in the header file
+void omp_playlist_page_list_populate();
+
+
+
/**
+ * Called when a row was selected and queries user whether he wants to load selected playlist
+ * @param playlist_name Name of the playlist
+ * @param playlist_file_abs File name of playlist with absolute path
+ */
+void
+omp_playlist_page_list_entry_select(gchar *playlist_name, gchar *playlist_file_abs)
+{
+ GtkWidget *dialog;
+ gint dialog_result;
+
+ // Get user confirmation
+ dialog = gtk_message_dialog_new(0,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
+ _("\nLoad playlist '%s'?"), playlist_name);
+
+ dialog_result = gtk_dialog_run(GTK_DIALOG(dialog));
+
+ if (dialog_result == GTK_RESPONSE_YES)
+ {
+ // Load playlist with state reset to have sane playlist values
+ if (omp_playlist_load(playlist_file_abs, TRUE))
+ {
+ // Switch back to main UI page
+/* gtk_notebook_set_current_page(GTK_NOTEBOOK(omp_notebook),
+ omp_notebook_tab_ids->main); */
+ }
+ }
+
+ // Clean up
+ gtk_widget_destroy(dialog);
+}
+
+/**
+ * Called after the user clicked the "delete" icon in a row of the list view
+ * @param playlist_name Name of the playlist
+ * @param playlist_file_abs File name of playlist with absolute path
+ */
+void
+omp_playlist_page_list_entry_delete(gchar *playlist_name, gchar *playlist_file)
+{
+ GtkWidget *dialog;
+ gint dialog_result;
+
+ // Get user confirmation
+ dialog = gtk_message_dialog_new(0,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
+ _("\nDelete playlist '%s'?"), playlist_name);
+
+ dialog_result = gtk_dialog_run(GTK_DIALOG(dialog));
+
+ if (dialog_result == GTK_RESPONSE_YES)
+ {
+ // Delete playlist
+ omp_playlist_delete(playlist_file);
+
+ // Rebuild the list
+ omp_playlist_page_list_populate();
+ }
+
+ // Clean up
+ gtk_widget_destroy(dialog);
+}
+
+/**
+ * Monitors general click events on the list view and acts appropriately
+ */
+gboolean
+omp_playlist_page_list_clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
+{
+ GtkTreePath *tree_path;
+ GtkTreeViewColumn *tree_column;
+ GtkTreeIter iterator;
+ GtkTreeModel *model;
+ GList *columns;
+ gint column_id;
+ gchar *playlist_name, *playlist_file, *playlist_file_abs;
+
+ g_return_if_fail(GTK_IS_TREE_VIEW(widget));
+
+ // Find colum that was hit
+ gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), event->x, event->y,
+ &tree_path, &tree_column, NULL, NULL);
+ if (!tree_path) return;
+
+ columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(widget));
+ column_id = g_list_index(columns, (gpointer)tree_column);
+ g_list_free(columns);
+
+ // Find row that was hit
+ model = GTK_TREE_MODEL(gtk_tree_view_get_model(GTK_TREE_VIEW(widget)));
+ gtk_tree_model_get_iter(model, &iterator, tree_path);
+
+ // Select row so the selection gets updated right now
+ gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tree_path, NULL, FALSE);
+
+ // Get playlist names
+ gtk_tree_model_get(model, &iterator,
+ NAME_COLUMN, &playlist_name,
+ FILE_NAME_COLUMN, &playlist_file, -1);
+
+ playlist_file_abs =
+ g_build_filename(g_get_home_dir(), RELATIVE_PLAYLIST_PATH, playlist_file, NULL);
+
+ // Determine what to do
+ switch (column_id)
+ {
+ case NAME_COLUMN:
+ {
+ omp_playlist_page_list_entry_select(playlist_name, playlist_file_abs);
+ break;
+ }
+
+ case ACT_DELETE_COLUMN:
+ {
+ omp_playlist_page_list_entry_delete(playlist_name, playlist_file_abs);
+ break;
+ }
+ }
+
+ // Clean up
+ g_free(playlist_file_abs);
+ gtk_tree_path_free(tree_path);
+
+ return TRUE;
+}
+
+/**
+ * Click handler for the "add new list" button
+ */
+void
+omp_playlist_page_add_list(GtkButton *button, gpointer user_data)
+{
+ gchar *path, *file_name;
+ const gchar *name = gtk_entry_get_text(GTK_ENTRY(omp_playlist_page_entry));
+
+ g_return_if_fail(name);
+
+ // Playlist path is relative to user's home dir
+ path = g_build_path("/", g_get_home_dir(), RELATIVE_PLAYLIST_PATH, NULL);
+ file_name = g_strdup_printf("%s/%s.%s", path, name, OMP_PLAYLIST_FILE_EXTENSION);
+
+ omp_playlist_create(file_name);
+
+ // Rebuild the list
+ omp_playlist_page_list_populate();
+
+ gtk_entry_set_text(GTK_ENTRY(omp_playlist_page_entry), "");
+}
+
+/**
+ * Fills the playlist data model with names from the file system
+ */
+void
+omp_playlist_page_list_populate()
+{
+ gchar *path, *dir_entry, *title;
+ GDir *playlist_dir;
+ GError *error;
+ GtkTreeIter iterator;
+
+ gtk_list_store_clear(omp_playlist_page_list_store);
+
+ // Playlist path is relative to user's home dir
+ path = g_build_path("/", g_get_home_dir(), RELATIVE_PLAYLIST_PATH, NULL);
+
+ playlist_dir = g_dir_open(path, 0, &error);
+
+ if (!playlist_dir)
+ {
+ g_printerr("Could not read playlist directory: %s\n", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ do
+ {
+ dir_entry = (gchar*)g_dir_read_name(playlist_dir);
+
+ // Add entry to list if it's valid
+ if (dir_entry)
+ {
+ title = get_playlist_title(dir_entry);
+
+ gtk_list_store_append(omp_playlist_page_list_store, &iterator);
+ gtk_list_store_set(omp_playlist_page_list_store, &iterator,
+ NAME_COLUMN, title,
+ FILE_NAME_COLUMN, dir_entry, -1);
+
+ g_free(title);
+ }
+ } while (dir_entry);
+
+ g_dir_close(playlist_dir);
+ g_free(path);
+}
+
+/**
* Creates the playlist view
+ * @param container Destination container of the view
*/
void
omp_playlist_page_list_create(GtkContainer *container)
{
- GtkListStore *store;
GtkWidget *tree_view;
+ GtkTreeSelection *select;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
// Create and populate data model
- store = gtk_list_store_new(1, G_TYPE_STRING);
+ omp_playlist_page_list_store = gtk_list_store_new(COLUMN_COUNT,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+ omp_playlist_page_list_populate();
+
// Create data view
- tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+ tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(omp_playlist_page_list_store));
+ g_object_unref(G_OBJECT(omp_playlist_page_list_store));
+ g_signal_connect(G_OBJECT(tree_view), "button-press-event",
+ G_CALLBACK(omp_playlist_page_list_clicked), NULL);
+
+ // Configure selection handler
+ select = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
+ gtk_tree_selection_set_mode(GTK_TREE_SELECTION(select), GTK_SELECTION_SINGLE);
+
+ // Set up columns
renderer = gtk_cell_renderer_pixbuf_new();
+ g_object_set(G_OBJECT(renderer), "stock-id", MOKO_STOCK_VIEW, NULL);
column = gtk_tree_view_column_new_with_attributes("", renderer, NULL);
gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
gtk_tree_view_column_set_fixed_width(column, BUTTON_PIXMAP_SIZE);
gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
renderer = gtk_cell_renderer_text_new();
- column = gtk_tree_view_column_new_with_attributes("Playlist Name", renderer,
- "name", NAME_COLUMN, NULL);
+ column = gtk_tree_view_column_new_with_attributes(_("Playlist Name"), renderer,
+ "text", NAME_COLUMN, NULL);
gtk_tree_view_column_set_expand(column, TRUE);
gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
+ // Column with "delete" icon
renderer = gtk_cell_renderer_pixbuf_new();
- column = gtk_tree_view_column_new_with_attributes("Actions", renderer, NULL);
+ g_object_set(G_OBJECT(renderer), "stock-id", "gtk-delete", NULL);
+ column = gtk_tree_view_column_new_with_attributes(_("Del?"), renderer, NULL);
gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
- gtk_tree_view_column_set_fixed_width(column, 2*BUTTON_PIXMAP_SIZE+10);
+ gtk_tree_view_column_set_fixed_width(column, 2*BUTTON_PIXMAP_SIZE);
gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
- // Add view to container
+ // Add playlist view to container
gtk_container_add(container, GTK_WIDGET(tree_view));
}
/**
- * Create the playlist UI page and all its elements
+ * Creates the playlist UI page and all its elements
*/
GtkWidget *
-omp_playlist_page_create(GtkWindow *window)
+omp_playlist_page_create()
{
- GtkWidget *main_vbox, *scroll_box, *input_box, *button, *image;
+ GtkWidget *main_vbox, *scroll_box, *input_box, *button, *image, *alignment, *label;
// Create main container
main_vbox = gtk_vbox_new(FALSE, 0);
+ // Caption #1
+ alignment = create_label(&label, "Sans 14", "black", 0, 0, 0, 0, 0);
+ gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 5, 5, 5, 5);
+ gtk_box_pack_start(GTK_BOX(main_vbox), GTK_WIDGET(alignment), FALSE, FALSE, 0);
+ gtk_label_set_text(GTK_LABEL(label), _("Select Playlist to load:"));
+
+ // Playlist list viewport
scroll_box = moko_finger_scroll_new();
gtk_box_pack_start(GTK_BOX(main_vbox), GTK_WIDGET(scroll_box), TRUE, TRUE, 0);
// Create playlist view
omp_playlist_page_list_create(GTK_CONTAINER(scroll_box));
+ // Caption #2
+ alignment = create_label(&label, "Sans 14", "black", 0, 0, 0, 0, 0);
+ gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 5, 5, 5, 5);
+ gtk_box_pack_start(GTK_BOX(main_vbox), GTK_WIDGET(alignment), FALSE, FALSE, 0);
+ gtk_label_set_text(GTK_LABEL(label), _("Enter name to create a new playlist:"));
+
// Add entry field for creation of a new playlist
input_box = gtk_hbox_new(FALSE, 0);
omp_playlist_page_entry = gtk_entry_new();
@@ -105,11 +344,15 @@
image = gtk_image_new_from_icon_name("gtk-add", BUTTON_PIXMAP_SIZE);
gtk_container_add(GTK_CONTAINER(button), GTK_WIDGET(image));
+ g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(omp_playlist_page_add_list), NULL);
+
gtk_box_pack_start(GTK_BOX(input_box), GTK_WIDGET(omp_playlist_page_entry), TRUE, TRUE, 5);
gtk_box_pack_start(GTK_BOX(input_box), GTK_WIDGET(button), FALSE, TRUE, 5);
gtk_box_pack_start(GTK_BOX(main_vbox), GTK_WIDGET(input_box), FALSE, TRUE, 10);
+ // Make all widgets visible
+ gtk_widget_show_all(main_vbox);
+
return main_vbox;
}
-
Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist_page.h
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist_page.h 2007-08-20 22:31:27 UTC (rev 2751)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist_page.h 2007-08-21 07:41:07 UTC (rev 2752)
@@ -30,6 +30,5 @@
#include <gtk/gtk.h>
GtkWidget *omp_playlist_page_create();
-void omp_playlist_connect_signals();
#endif
More information about the commitlog
mailing list