[gnome-music] Switch to js
- From: Seif Lotfy <seiflotfy src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music] Switch to js
- Date: Sun, 31 Mar 2013 20:58:40 +0000 (UTC)
commit b0e455b8ec73600c399cf74ed67a59eb7055af93
Author: Seif Lotfy <seif lotfy com>
Date: Sun Mar 31 22:43:19 2013 +0200
Switch to js
.gitignore | 97 -
AUTHORS | 1 -
COPYING | 339 -
INSTALL | 370 -
Makefile.am | 46 +-
NEWS | 3 +
README | 4 -
autogen.sh | 30 +-
configure.ac | 79 +-
data/Makefile.am | 25 -
data/album-art-default.svg | 673 --
data/gnome-music.desktop.in.in | 9 -
git.mk | 227 +
gnome-music.doap | 19 -
libgd | 1 -
po/LINGUAS | 13 -
po/POTFILES.in | 7 -
po/POTFILES.skip | 4 -
po/cs.po | 147 -
po/de.po | 150 -
po/es.po | 149 -
po/fi.po | 147 -
po/gl.po | 147 -
po/hu.po | 148 -
po/nb.po | 143 -
po/pa.po | 149 -
po/pl.po | 156 -
po/pt_BR.po | 146 -
po/ru.po | 153 -
po/sl.po | 150 -
po/sr.po | 150 -
po/sr latin po | 150 -
src/Makefile.am | 98 +-
src/album-art-cache.vala | 157 -
src/album_art_cache.js | 191 +
src/app-menu.ui | 21 -
src/application.js | 161 +
src/clickable_label.js | 34 +
src/gnome-music.in | 7 +
src/grilo.js | 115 +
src/main.js | 26 +
src/main.vala | 49 -
src/models.js | 96 +
src/music-album-info-box.vala | 97 -
src/music-app.vala | 336 -
src/music-browse-history.vala | 75 -
src/music-clickable-label.vala | 69 -
src/music-collection-view.vala | 225 -
src/music-list-store.vala | 341 -
src/music-player.vala | 303 -
src/music-playlist-songs.vala | 95 -
src/music-playlist-view.vala | 64 -
src/music-playlist.vala | 165 -
src/music-searchbar.vala | 84 -
src/music-topbar.vala | 346 -
src/music-utils.vala | 19 -
src/package.js | 282 +
src/player.js | 289 +
src/query.js | 34 +
src/resources/app-menu.ui | 27 +
{data => src/resources}/gtk-style.css | 1 +
src/resources/lp.jpg | Bin 0 -> 20663 bytes
src/{ => resources}/music.gresource.xml | 0
src/{ => resources}/org.gnome.Music.gschema.xml.in | 0
src/searchbar.js | 132 +
src/toolbar.js | 84 +
src/view.js | 383 +
src/widgets.js | 186 +
vapi/Makefile.am | 5 -
vapi/config.vapi | 18 -
vapi/custom.vapi | 12 -
vapi/upstream/Makefile.am | 19 -
vapi/upstream/cairo.vapi | 724 --
vapi/upstream/clutter-1.0.deps | 5 -
vapi/upstream/clutter-1.0.vapi | 7680 ------------------
vapi/upstream/clutter-gtk-1.0.deps | 8 -
vapi/upstream/clutter-gtk-1.0.vapi | 58 -
vapi/upstream/cogl-1.0.deps | 1 -
vapi/upstream/cogl-1.0.vapi | 802 --
vapi/upstream/gdk-3.0.deps | 5 -
vapi/upstream/gdk-3.0.vapi | 5957 --------------
vapi/upstream/gdk-pixbuf-2.0.deps | 1 -
vapi/upstream/gdk-pixbuf-2.0.vapi | 242 -
vapi/upstream/gtk+-3.0.deps | 7 -
vapi/upstream/gtk+-3.0.vapi | 8543 --------------------
85 files changed, 2362 insertions(+), 30349 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 6dbff12..25c551b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,41 +1,9 @@
-ACLOCAL_AMFLAGS= -I libgd ${ACLOCAL_FLAGS}
-NULL=
+ACLOCAL_AMFLAGS = -I m4 -I libgd
-SUBDIRS = data libgd src po vapi
-
-EXTRA_DIST = \
- autogen.sh \
- intltool-extract.in \
- intltool-merge.in \
- intltool-update.in \
- $(NULL)
-
-ChangeLog:
- $(AM_V_GEN) if test -d "$(srcdir)/.git"; then \
- (GIT_DIR=$(top_srcdir)/.git ./missing --run git log --stat) | fmt --split-only > $ tmp \
- && mv -f $ tmp $@ \
- || ($(RM) $ tmp; \
- echo Failed to generate ChangeLog, your ChangeLog may be outdated >&2; \
- (test -f $@ || echo git-log is required to generate this file >> $@)); \
- else \
- test -f $@ || \
- (echo A git checkout and git-log is required to generate ChangeLog >&2 && \
- echo A git checkout and git-log is required to generate this file >> $@); \
- fi
-
-
-distclean-local:
- if test "$(srcdir)" = "."; then :; else \
- rm -f ChangeLog; \
- fi
-
-
-DISTCLEANFILES = \
-intltool-extract \
-intltool-merge \
-intltool-update
+SUBDIRS = libgd src
MAINTAINERCLEANFILES = \
+ $(srcdir)/INSTALL \
$(srcdir)/aclocal.m4 \
$(srcdir)/autoscan.log \
$(srcdir)/compile \
@@ -45,6 +13,10 @@ MAINTAINERCLEANFILES = \
$(srcdir)/configure.scan \
$(srcdir)/depcomp \
$(srcdir)/install-sh \
+ $(srcdir)/ltmain.sh \
$(srcdir)/missing \
- $(srcdir)/ChangeLog \
- `find "$(srcdir)" -type f -name Makefile.in -print`
+ $(srcdir)/mkinstalldirs
+
+GITIGNOREFILES = m4/
+
+-include $(top_srcdir)/git.mk
diff --git a/NEWS b/NEWS
index e69de29..4507184 100644
--- a/NEWS
+++ b/NEWS
@@ -0,0 +1,3 @@
+0.2
+===
+* Initial release, first one to be published at GNOME archives
diff --git a/autogen.sh b/autogen.sh
index 3fe3f24..5f239c0 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -1,14 +1,26 @@
-#!/bin/sh
-
-set -e # exit on errors
+#!/bin/bash
+# Run this to generate all the initial makefiles, etc.
srcdir=`dirname $0`
test -z "$srcdir" && srcdir=.
-git submodule update --init --recursive
-autoreconf -v --force --install
-intltoolize -f
+ACLOCAL_FLAGS="-I libgd ${ACLOCAL_FLAGS}"
+PKG_NAME="gnome-music"
+
+test -f $srcdir/configure.ac || {
+ echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
+ echo " top-level gnome-music directory"
+ exit 1
+}
+
+which gnome-autogen.sh || {
+ echo "You need to install gnome-common from GNOME Git (or from"
+ echo "your OS vendor's package manager)."
+ exit 1
+}
-if [ -z "$NOCONFIGURE" ]; then
- "$srcdir"/configure --enable-maintainer-mode "$@"
-fi
+(cd "$srcdir" ;
+test -d m4 || mkdir m4/ ;
+git submodule update --init --recursive ;
+)
+. gnome-autogen.sh
diff --git a/configure.ac b/configure.ac
index ed87f1d..7f0fdfc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,68 +1,35 @@
-AC_PREREQ([2.67])
-AC_INIT([gnome-music],[0.0.0],[https://github.com/tapia/gnome-music])
-AC_CONFIG_SRCDIR([src/main.vala])
+AC_PREREQ(2.63)
+AC_INIT([gnome-music],[0.2],
+ [https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-music],
+ [gnome-music],
+ [https://live.gnome.org/Design/Apps/Music])
+
AC_CONFIG_HEADERS([config.h])
-AM_INIT_AUTOMAKE
-# Enable silent rules is available
-AM_SILENT_RULES([yes])
-AM_MAINTAINER_MODE([enable])
+AC_CONFIG_MACRO_DIR([m4])
-LT_INIT
-AC_PROG_CC
-AM_PROG_VALAC([0.17])
-AC_PROG_INSTALL
+AM_INIT_AUTOMAKE([1.11 dist-xz foreign tar-ustar -Wno-portability])
-GOBJECT_INTROSPECTION_REQUIRE([0.9.6])
+m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
-GLIB_GSETTINGS
-
-# i18n stuff
-IT_PROG_INTLTOOL([0.40])
+AC_PROG_CC
+AM_PROG_CC_C_O
+LT_INIT([disable-static])
-AM_GNU_GETTEXT([external])
-AM_GNU_GETTEXT_VERSION([0.17])
+LIBGD_INIT([header-bar main-toolbar main-view stack revealer gir])
-GETTEXT_PACKAGE=gnome-music
-AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"], [Gettext Package])
-AC_SUBST(GETTEXT_PACKAGE)
+PKG_PROG_PKG_CONFIG([0.22])
-SQLITE_MIN_VERSION=3.7.14
-GLIB_MIN_VERSION=2.31.0
-GTK_MIN_VERSION=3.5.6
-GRILO_MIN_VERSION=0.2
-GEE_MIN_VERSION=0.6.4
-GSTREAMER_MIN_VERSION=0.10
+GLIB_GSETTINGS
+GOBJECT_INTROSPECTION_REQUIRE([1.30.0])
-PKG_CHECK_MODULES(MUSIC, [
- sqlite3 >= $SQLITE_MIN_VERSION
- glib-2.0 >= $GLIB_MIN_VERSION
- gtk+-3.0 >= $GTK_MIN_VERSION
- grilo-0.2 >= $GRILO_MIN_VERSION
- gee-1.0 >= $GEE_MIN_VERSION
- gstreamer-0.10 >= $GSTREAMER_MIN_VERSION
-])
+AC_PATH_PROG([GJS],[gjs])
-MUSIC_PACKAGES="--pkg glib-2.0 --pkg gtk+-3.0 --pkg grilo-0.2 --pkg gee-1.0 --pkg gstreamer-0.10"
-AC_SUBST(MUSIC_PACKAGES)
+GLIB_COMPILE_RESOURCES=`$PKG_CONFIG --variable glib_compile_resources gio-2.0`
+AC_SUBST(GLIB_COMPILE_RESOURCES)
-LIBGD_INIT([
- gtk-hacks
- main-toolbar
- main-icon-view
- margin-container
- static
- tagged-entry
- vapi
+AC_CONFIG_FILES([
+ Makefile
+ libgd/Makefile
+ src/Makefile
])
-
-AC_CONFIG_FILES([Makefile
- vapi/Makefile
- vapi/upstream/Makefile
- src/Makefile
- libgd/Makefile
- data/Makefile
- po/Makefile.in
- data/gnome-music.desktop.in
- ])
-
AC_OUTPUT
diff --git a/git.mk b/git.mk
new file mode 100644
index 0000000..d5bf7b8
--- /dev/null
+++ b/git.mk
@@ -0,0 +1,227 @@
+# git.mk
+#
+# Copyright 2009, Red Hat, Inc.
+# Copyright 2010,2011 Behdad Esfahbod
+# Written by Behdad Esfahbod
+#
+# Copying and distribution of this file, with or without modification,
+# is permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+#
+# The latest version of this file can be downloaded from:
+# https://raw.github.com/behdad/git.mk/master/git.mk
+# Bugs, etc, should be reported upstream at:
+# https://github.com/behdad/git.mk
+#
+# To use in your project, import this file in your git repo's toplevel,
+# then do "make -f git.mk". This modifies all Makefile.am files in
+# your project to -include git.mk. Remember to add that line to new
+# Makefile.am files you create in your project, or just rerun the
+# "make -f git.mk".
+#
+# This enables automatic .gitignore generation. If you need to ignore
+# more files, add them to the GITIGNOREFILES variable in your Makefile.am.
+# But think twice before doing that. If a file has to be in .gitignore,
+# chances are very high that it's a generated file and should be in one
+# of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES.
+#
+# The only case that you need to manually add a file to GITIGNOREFILES is
+# when remove files in one of mostlyclean-local, clean-local, distclean-local,
+# or maintainer-clean-local make targets.
+#
+# Note that for files like editor backup, etc, there are better places to
+# ignore them. See "man gitignore".
+#
+# If "make maintainer-clean" removes the files but they are not recognized
+# by this script (that is, if "git status" shows untracked files still), send
+# me the output of "git status" as well as your Makefile.am and Makefile for
+# the directories involved and I'll diagnose.
+#
+# For a list of toplevel files that should be in MAINTAINERCLEANFILES, see
+# Makefile.am.sample in the git.mk git repo.
+#
+# Don't EXTRA_DIST this file. It is supposed to only live in git clones,
+# not tarballs. It serves no useful purpose in tarballs and clutters the
+# build dir.
+#
+# This file knows how to handle autoconf, automake, libtool, gtk-doc,
+# gnome-doc-utils, yelp.m4, mallard, intltool, gsettings, dejagnu.
+#
+# This makefile provides the following targets:
+#
+# - all: "make all" will build all gitignore files.
+# - gitignore: makes all gitignore files in the current dir and subdirs.
+# - .gitignore: make gitignore file for the current dir.
+# - gitignore-recurse: makes all gitignore files in the subdirs.
+#
+# KNOWN ISSUES:
+#
+# - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the
+# submodule doesn't find us. If you have configure.{in,ac} files in
+# subdirs, add a proxy git.mk file in those dirs that simply does:
+# "include $(top_srcdir)/../git.mk". Add more ..'s to your taste.
+# And add those files to git. See vte/gnome-pty-helper/git.mk for
+# example.
+#
+
+git-all: git-mk-install
+
+git-mk-install:
+ @echo Installing git makefile
+ @any_failed=; \
+ find "`test -z "$(top_srcdir)" && echo . || echo "$(top_srcdir)"`" -name Makefile.am | while
read x; do \
+ if grep 'include .*/git.mk' $$x >/dev/null; then \
+ echo $$x already includes git.mk; \
+ else \
+ failed=; \
+ echo "Updating $$x"; \
+ { cat $$x; \
+ echo ''; \
+ echo '-include $$(top_srcdir)/git.mk'; \
+ } > $$x.tmp || failed=1; \
+ if test x$$failed = x; then \
+ mv $$x.tmp $$x || failed=1; \
+ fi; \
+ if test x$$failed = x; then : else \
+ echo Failed updating $$x; >&2 \
+ any_failed=1; \
+ fi; \
+ fi; done; test -z "$$any_failed"
+
+.PHONY: git-all git-mk-install
+
+
+### .gitignore generation
+
+$(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk
+ $(AM_V_GEN) \
+ { \
+ if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \
+ for x in \
+ $(DOC_MODULE)-decl-list.txt \
+ $(DOC_MODULE)-decl.txt \
+ tmpl/$(DOC_MODULE)-unused.sgml \
+ "tmpl/*.bak" \
+ xml html \
+ ; do echo /$$x; done; \
+ fi; \
+ if test "x$(DOC_MODULE)$(DOC_ID)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \
+ for lc in $(DOC_LINGUAS); do \
+ for x in \
+ $(if $(DOC_MODULE),$(DOC_MODULE).xml) \
+ $(DOC_PAGES) \
+ $(DOC_INCLUDES) \
+ ; do echo /$$lc/$$x; done; \
+ done; \
+ for x in \
+ $(_DOC_OMF_ALL) \
+ $(_DOC_DSK_ALL) \
+ $(_DOC_HTML_ALL) \
+ $(_DOC_MOFILES) \
+ $(DOC_H_FILE) \
+ "*/.xml2po.mo" \
+ "*/*.omf.out" \
+ ; do echo /$$x; done; \
+ fi; \
+ if test "x$(HELP_ID)" = x -o "x$(HELP_LINGUAS)" = x; then :; else \
+ for lc in $(HELP_LINGUAS); do \
+ for x in \
+ $(HELP_FILES) \
+ "$$lc.stamp" \
+ "$$lc.mo" \
+ ; do echo /$$lc/$$x; done; \
+ done; \
+ fi; \
+ if test "x$(gsettings_SCHEMAS)" = x; then :; else \
+ for x in \
+ $(gsettings_SCHEMAS:.xml=.valid) \
+ $(gsettings__enum_file) \
+ ; do echo /$$x; done; \
+ fi; \
+ if test -f $(srcdir)/po/Makefile.in.in; then \
+ for x in \
+ po/Makefile.in.in \
+ po/Makefile.in \
+ po/Makefile \
+ po/POTFILES \
+ po/stamp-it \
+ po/.intltool-merge-cache \
+ "po/*.gmo" \
+ "po/*.mo" \
+ po/$(GETTEXT_PACKAGE).pot \
+ intltool-extract.in \
+ intltool-merge.in \
+ intltool-update.in \
+ ; do echo /$$x; done; \
+ fi; \
+ if test -f $(srcdir)/configure; then \
+ for x in \
+ autom4te.cache \
+ configure \
+ config.h \
+ stamp-h1 \
+ libtool \
+ config.lt \
+ ; do echo /$$x; done; \
+ fi; \
+ if test "x$(DEJATOOL)" = x; then :; else \
+ for x in \
+ $(DEJATOOL) \
+ ; do echo /$$x.sum; echo /$$x.log; done; \
+ echo /site.exp; \
+ fi; \
+ for x in \
+ .gitignore \
+ $(GITIGNOREFILES) \
+ $(CLEANFILES) \
+ $(PROGRAMS) $(check_PROGRAMS) $(EXTRA_PROGRAMS) \
+ $(LIBRARIES) $(check_LIBRARIES) $(EXTRA_LIBRARIES) \
+ $(LTLIBRARIES) $(check_LTLIBRARIES) $(EXTRA_LTLIBRARIES) \
+ so_locations \
+ .libs _libs \
+ $(MOSTLYCLEANFILES) \
+ "*.$(OBJEXT)" \
+ "*.lo" \
+ $(DISTCLEANFILES) \
+ $(am__CONFIG_DISTCLEAN_FILES) \
+ $(CONFIG_CLEAN_FILES) \
+ TAGS ID GTAGS GRTAGS GSYMS GPATH tags \
+ "*.tab.c" \
+ $(MAINTAINERCLEANFILES) \
+ $(BUILT_SOURCES) \
+ $(DEPDIR) \
+ Makefile \
+ Makefile.in \
+ "*.orig" \
+ "*.rej" \
+ "*.bak" \
+ "*~" \
+ ".*.sw[nop]" \
+ ".dirstamp" \
+ ; do echo /$$x; done; \
+ } | \
+ sed "s ^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \
+ sed 's@/[.]/@/@g' | \
+ LC_ALL=C sort | uniq > $ tmp && \
+ mv $ tmp $@;
+
+all: $(srcdir)/.gitignore gitignore-recurse-maybe
+gitignore: $(srcdir)/.gitignore gitignore-recurse
+
+gitignore-recurse-maybe:
+ @for subdir in $(DIST_SUBDIRS); do \
+ case " $(SUBDIRS) " in \
+ *" $$subdir "*) :;; \
+ *) test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) .gitignore
gitignore-recurse-maybe || echo "Skipping $$subdir");; \
+ esac; \
+ done
+gitignore-recurse:
+ @for subdir in $(DIST_SUBDIRS); do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) .gitignore gitignore-recurse ||
echo "Skipping $$subdir"); \
+ done
+
+maintainer-clean: gitignore-clean
+gitignore-clean:
+ -rm -f $(srcdir)/.gitignore
+
+.PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe
diff --git a/src/Makefile.am b/src/Makefile.am
index d285385..695ef5c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,71 +1,35 @@
NULL =
-AM_CPPFLAGS = \
- -include config.h \
- $(MUSIC_CFLAGS) \
- -DLOCALEDIR=\""$(localedir)"\" \
- -DPKGDATADIR=\""$(pkgdatadir)"\" \
- -DPKGLIBDIR=\""$(pkglibdir)"\" \
- -DGNOME_DESKTOP_USE_UNSTABLE_API \
- -I$(top_srcdir)/libgd \
+nodist_bin_SCRIPTS = gnome-music
+
+jsdir = $(pkgdatadir)
+dist_js_DATA = \
+ album_art_cache.js \
+ player.js \
+ toolbar.js \
+ application.js \
+ gnome-music.in \
+ grilo.js \
+ query.js \
+ widgets.js \
+ main.js \
+ package.js \
+ searchbar.js \
+ view.js \
$(NULL)
-AM_VALAFLAGS = \
- --vapidir=$(srcdir)/ \
- --vapidir=$(top_srcdir)/libgd \
- --vapidir=$(top_srcdir)/vapi \
- --pkg config \
- --pkg gd-1.0 \
- @MUSIC_PACKAGES@ \
- $(NULL)
-
-bin_PROGRAMS = gnome-music
-
-vala_sources = \
- music-app.vala \
- music-topbar.vala \
- music-searchbar.vala \
- music-player.vala \
- music-collection-view.vala \
- music-list-store.vala \
- music-playlist.vala \
- music-playlist-view.vala \
- music-playlist-songs.vala \
- music-album-info-box.vala \
- music-browse-history.vala \
- music-clickable-label.vala \
- music-utils.vala \
- album-art-cache.vala \
- main.vala \
- $(NULL)
-
-gsettingsschema_in_files = org.gnome.Music.gschema.xml.in
-gsettings_SCHEMAS = $(gsettingsschema_in_files:.xml.in=.xml)
-.PRECIOUS: $(gsettings_SCHEMAS)
-
- INTLTOOL_XML_NOMERGE_RULE@
-
- GSETTINGS_RULES@
-
-music-resources.c: music.gresource.xml app-menu.ui
- $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) $(srcdir)/music.gresource.xml \
- --target=$@ --sourcedir=$(srcdir) --c-name music --generate-source
-
-gnome_music_SOURCES = \
- $(vala_sources) \
- $(NULL)
-
-gnome_music_LDADD = $(MUSIC_LIBS) \
- $(top_builddir)/libgd/libgd.la \
- $(NULL)
-
-CLEANFILES = \
- $(vala_sources:.vala=.c) \
- $(gsettings_SCHEMAS) \
- *.vapi *.stamp
-
-EXTRA_DIST = \
- music.gresource.xml \
- app-menu.ui \
- $(gsettingsschema_in_files) \
- $(NULL)
+gnome-music: gnome-music.in
+ $(AM_V_GEN) sed \
+ -e "s|[ ]GJS@|$(GJS)|g" \
+ -e "s|[ ]PACKAGE_NAME@|$(PACKAGE_NAME)|g" \
+ -e "s|[ ]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g" \
+ -e "s|[ ]prefix@|$(prefix)|g" \
+ -e "s|[ ]libdir@|$(libdir)|g" \
+ -e "s|[ ]pkgdatadir@|$(pkgdatadir)|g" \
+ $< > $@
+ @chmod +x $@
+
+EXTRA_DIST = gnome-music.in
+DISTCLEANFILES = gnome-music
+
+-include $(top_srcdir)/git.mk
diff --git a/src/album_art_cache.js b/src/album_art_cache.js
new file mode 100644
index 0000000..c28a825
--- /dev/null
+++ b/src/album_art_cache.js
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2013 Seif Lotfy <seif lotfy com>.
+ *
+ * Gnome Music is free software; you can Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Music 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 Gnome Music; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+const Lang = imports.lang;
+const GdkPixbuf = imports.gi.GdkPixbuf;
+const GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+const Regex = GLib.Regex;
+const Path = GLib.Path;
+const Grl = imports.gi.Grl;
+
+const invalid_chars = /[()<>\[\]{}_! #$^&*+=|\\\/\"'?~]/g;
+const convert_chars = /[\t]/g;
+const blocks = ["()", "{}", "[]", "<>"];
+
+
+function escapeRegExp(str) {
+ return str.replace(/[\(\)\[\]\<\>\{\}\_\!\ \#\$\^\&\*\+\=\|\\\/\"\'\?\~]/g, "\\$&");
+}
+
+String.prototype.printf = function() {
+ var content = this;
+
+ for (let i = 0; i < arguments.length; i++) {
+ let replacement = '{' + i + '}';
+
+ content = content.replace(replacement, arguments[i]);
+ }
+
+ return content;
+};
+
+const AlbumArtCache = new Lang.Class({
+ Name: "AlbumArtCache",
+ Extends: GLib.Object,
+
+ _init: function() {
+ this.parent();
+ this.block_regexes = [];
+ this.space_compress_regex = new RegExp("\\s+");
+
+ for (let i in blocks) {
+ let block = blocks[i],
+ block_re = escapeRegExp(block[0]) + "[^" + escapeRegExp(block[1]) + "]*" +
escapeRegExp(block[1]);
+
+ this.block_regexes.push(new RegExp(block_re));
+ }
+
+ this.cache_dir = GLib.build_filenamev([GLib.get_user_cache_dir (), "media-art"]);
+ },
+
+ lookup: function(size, artist_, album_) {
+ var artist = artist_,
+ album = album_;
+
+ if (artist == null) {
+ artist = " " ;
+ }
+
+ if (album == null) {
+ album = " ";
+ }
+
+ try {
+ let key = "album-" + this.normalizeAndHash(artist) + "-" + this.normalizeAndHash(album);
+ let path = GLib.build_filenamev([this.cache_dir, key + ".jpeg"]);
+
+ return GdkPixbuf.Pixbuf.new_from_file_at_scale(path, size, -1, true);
+ }
+
+ catch (error) {
+ //print (error)
+ }
+
+ try {
+ let key = "album-" + this.normalizeAndHash(artist, false, true) + "-" +
this.normalizeAndHash(album, false, true);
+ let path = GLib.build_filenamev([this.cache_dir, key + ".jpeg"]);
+
+ return GdkPixbuf.Pixbuf.new_from_file_at_scale(path, size, -1, true);
+ }
+
+ catch (error) {
+ //print (error)
+ }
+
+ try {
+ let key = "album-" + this.normalizeAndHash(" ", false, true) + "-" +
this.normalizeAndHash(album, false, true);
+ let path = GLib.build_filenamev([this.cache_dir, key + ".jpeg"]);
+
+ return GdkPixbuf.Pixbuf.new_from_file_at_scale(path, size, -1, true);
+ }
+
+ catch (error) {
+ //print (error)
+ }
+
+ try {
+ let key = "album-" + this.normalizeAndHash(artist + "\t" + album, true, true);
+ let path = GLib.build_filenamev ([this.cache_dir, key + ".jpeg"]);
+
+ return GdkPixbuf.Pixbuf.new_from_file_at_scale(path, size, -1, true);
+ }
+
+ catch (error) {
+ //print (error)
+ }
+
+ return null;
+ },
+
+ normalizeAndHash: function(input, utf8_only, utf8) {
+ var normalized = " ";
+
+ if (input != null && input != "") {
+ if (utf8_only) {
+ normalized = input;
+ }
+
+ else {
+ normalized = this.stripInvalidEntities(input);
+ normalized = normalized.toLowerCase();
+ }
+
+ if (utf8) {
+ normalized = GLib.utf8_normalize(normalized, -1, 2)
+ }
+ }
+
+ return GLib.compute_checksum_for_string(GLib.ChecksumType.MD5, normalized, -1);
+ },
+
+ stripInvalidEntities: function(original) {
+ var result = original;
+
+ for (let i in this.block_regexes) {
+ let re = this.block_regexes[i];
+
+ result = result.replace(re, '');
+ }
+
+ result = result
+ .replace(invalid_chars, '')
+ .replace(convert_chars, ' ')
+ .replace(this.space_compress_regex, ' ');
+
+ return result;
+ },
+
+ getFromUri: function(uri, artist, album, width, height, callback) {
+ if (uri != null) {
+ print ("missing", album, artist)
+ let key = "album-" + this.normalizeAndHash(artist) + "-" + this.normalizeAndHash(album);
+ let path = GLib.build_filenamev([this.cache_dir, key + ".jpeg"]);
+ var file = Gio.File.new_for_uri(uri);
+ file.read_async(300, null, Lang.bind(this,
+ function(source, res, user_data) {
+ var stream = file.read_finish(res);
+ var icon = GdkPixbuf.Pixbuf.new_from_stream_at_scale(stream, height, width, true, null);
+ var new_file = Gio.File.new_for_path(path);
+ file.copy(new_file, Gio.FileCopyFlags.NONE, null, null)
+ callback(icon);
+ }));
+ }
+ }
+
+});
+
+AlbumArtCache.instance = null;
+
+AlbumArtCache.getDefault = function() {
+ if (AlbumArtCache.instance == null) {
+ AlbumArtCache.instance = new AlbumArtCache();
+ }
+
+ return AlbumArtCache.instance;
+};
diff --git a/src/application.js b/src/application.js
new file mode 100644
index 0000000..908aad7
--- /dev/null
+++ b/src/application.js
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2013 Eslam Mostafa.
+ *
+ * Gnome Music is free software; you can Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Music 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 Gnome Music; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author: Eslam Mostafa <cseslam gmail com>
+ *
+ */
+
+
+const Lang = imports.lang;
+const Gtk = imports.gi.Gtk;
+const Gdk = imports.gi.Gdk;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Gd = imports.gi.Gd;
+
+const Toolbar = imports.toolbar;
+const Views = imports.view;
+const Player = imports.player;
+
+const Gettext = imports.gettext;
+const _ = imports.gettext.gettext;
+
+var AppState = {
+ ARTISTS: 0,
+ ALBUMS: 1,
+ SONGS: 2,
+ PLAYLISTS: 3,
+ PLAYLIST: 4,
+ PLAYLIST_NEW: 5
+};
+
+const Application = new Lang.Class({
+ Name: 'Music',
+ Extends: Gtk.Application,
+
+ _init: function() {
+ this.parent({
+ application_id: 'org.gnome.Music',
+ flags: Gio.ApplicationFlags.FLAGS_NONE,
+ inactivity_timeout: 12000
+ });
+
+ GLib.set_prgname('gnome-music');
+ GLib.set_application_name(_("Music"));
+
+ this.connect('activate', Lang.bind(this, this._onActivate));
+ this.connect('startup', Lang.bind(this, this._onStartup));
+ },
+
+ _buildApp: function() {
+ this._window = new Gtk.ApplicationWindow({
+ application: this,
+ title: _("Music"),
+ window_position: Gtk.WindowPosition.CENTER,
+ hide_titlebar_when_maximized: true
+ });
+
+
+ this._window.set_size_request (800, 600)
+ this.vbox = new Gtk.Box({
+ orientation: Gtk.Orientation.VERTICAL,
+ spacing: 0
+ });
+
+ this.player = new Player.Player();
+
+ this.views = new Array();
+
+ this.toolbar = new Toolbar.Toolbar();
+ this._stack = new Gd.Stack({
+ visible: true
+ });
+
+ this._window.set_default_size(640, 400);
+ this.vbox.pack_start(this.toolbar, false, false, 0);
+ this.vbox.pack_start(this._stack, true, true, 0);
+ this.vbox.pack_start(this.player.eventbox, false, false, 0);
+ this._window.add(this.vbox);
+
+ this.views[0] = new Views.Albums(this.toolbar);
+ this.views[1] = new Views.Artists(this.toolbar);
+ this.views[2] = new Views.Songs(this.toolbar);
+ this.views[3] = new Views.Playlists(this.toolbar);
+
+ for (let i in this.views) {
+ this._stack.add_titled(
+ this.views[i],
+ this.views[i].title,
+ this.views[i].title
+ );
+ }
+
+ //this._stack.connect("notify::visible-child", this._onNotifyMode);
+
+ this.views[0].populate();
+ this.toolbar.set_stack(this._stack);
+ this.toolbar.show();
+ this.player.eventbox.show_all();
+ this.vbox.show();
+ },
+
+ _onNotifyMode: function(stack, param) {
+ stack.get_visible_child().populate();
+ },
+
+ _buildAppMenu: function() {
+ var builder,
+ menu;
+
+ builder = new Gtk.Builder();
+ builder.add_from_file('resources/app-menu.ui'); //fix this
+
+ menu = builder.get_object('app-menu');
+ this.set_app_menu(menu);
+ },
+
+ _toggleView: function(btn, i) {
+ this._stack.set_visible_child(this.views[i])
+ },
+
+ switchToView: function(view) {
+ if (view == 'artists') {
+ this.notebook.set_current_page(0);
+ }
+
+ else if (view == 'albums') {
+ this.notebook.set_current_page(1);
+ }
+
+ else if (view == 'songs') {
+ this.notebook.set_current_page(2);
+ }
+
+ else if (view == 'playlists') {
+ this.notebook.set_current_page(3);
+ }
+
+ this.notebook.show_all();
+ },
+
+ _onActivate: function() {
+ this._window.show();
+ },
+
+ _onStartup: function() {
+ this._buildApp();
+ }
+});
diff --git a/src/clickable_label.js b/src/clickable_label.js
new file mode 100644
index 0000000..f4ec997
--- /dev/null
+++ b/src/clickable_label.js
@@ -0,0 +1,34 @@
+const Gtk = imports.gi.Gtk;
+const Gdk = imports.gi.Gdk;
+const Lang = imports.lang;
+
+const ClickableLabel = new Lang.Class({
+ Name: "ClickableLabel",
+ Extends: Gtk.EventBox,
+
+ _init: function (text) {
+ let hbox = new Gtk.HBox ();
+ let label = new Gtk.Label ({label : text});
+ label.set_alignment(0, 0.5);
+
+ this.parent ();
+ hbox.add (label);
+ this.add(hbox);
+ /*
+ this.connect ("enter_notify_event", function () {
+ let cursor = new Gdk.Cursor ({Gdk.CursorType.HAND2});
+
+ this.get_window ().set_cursor (cursor);
+ return false;
+ });
+
+ this.connect("leave_notify_event", function () {
+ let cursor = new Gdk.Cursor (Gdk.CursorType.Arrow);
+
+ this.get_window ().set_cursor (cursor);
+ return false;
+ });
+ */
+ },
+
+});
diff --git a/src/gnome-music.in b/src/gnome-music.in
new file mode 100644
index 0000000..599e93d
--- /dev/null
+++ b/src/gnome-music.in
@@ -0,0 +1,7 @@
+#! GJS@
+imports.searchPath.push("@pkgdatadir@");
+imports.package.start({ name: "@PACKAGE_NAME@",
+ version: "@PACKAGE_VERSION@",
+ prefix: "@prefix@",
+ libdir: "@libdir@" });
+
diff --git a/src/grilo.js b/src/grilo.js
new file mode 100644
index 0000000..2372f1b
--- /dev/null
+++ b/src/grilo.js
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2013 Eslam Mostafa <cseslam gmail com>.
+ * Copyright (c) 2013 Seif Lotfy <seif lotfy com>.
+ *
+ * Gnome Music is free software; you can Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Music 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 Gnome Music; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *
+ */
+
+const Grl = imports.gi.Grl;
+const Lang = imports.lang;
+const Signals = imports.signals;
+const Tracker = imports.gi.Tracker;
+const Query = imports.query;
+const tracker = Tracker.SparqlConnection.get (null);
+
+Grl.init (null, 0);
+const Grilo = new Lang.Class({
+ Name: 'Grilo',
+
+ _init: function() {
+ this.registry = Grl.Registry.get_default ();
+ this.registry.load_all_plugins();
+
+ let sources = {};
+ this.sources = sources;
+ this.tracker = null;
+
+ this.registry.connect ("source_added",
+ Lang.bind(this, this._onSourceAdded));
+
+ this.registry.connect ("source_removed",
+ function (pluginRegistry, mediaSource) {
+ log ("source removed");
+ });
+
+ if (this.registry.load_all == false) {
+ log ("Failed to load plugins.");
+ }
+ },
+
+ _onSourceAdded: function(pluginRegistry, mediaSource) {
+ if (mediaSource.sourceId == "grl-tracker-source") {
+ let ops = mediaSource.supported_operations ();
+ if (ops & Grl.SupportedOps.SEARCH) {
+ print ("Detected new source availabe: '" +
+ mediaSource.get_name () +
+ "' and it supports search");
+ this.sources[mediaSource.sourceId] = mediaSource;
+ this.tracker = mediaSource;
+ if (this.tracker != null)
+ this.emit('ready');
+ }
+ }
+ },
+
+ populateAlbums: function (offset, callback) {
+ this.populateItems (Query.album, offset, callback)
+ },
+
+ populateSongs: function (offset, callback) {
+ this.populateItems (Query.songs, offset, callback)
+ },
+
+ populateItems: function (query, offset, callback) {
+ var options = Grl.OperationOptions.new(null);
+ options.set_flags (Grl.ResolutionFlags.FULL | Grl.ResolutionFlags.IDLE_RELAY);
+ options.set_count(50);
+ query = query + " OFFSET " + offset;
+ grilo.tracker.query(
+ query,
+ [Grl.METADATA_KEY_ID, Grl.METADATA_KEY_TITLE, Grl.METADATA_KEY_ARTIST],
+ options,
+ Lang.bind(this, callback, null));
+ },
+
+ getAlbumSongs: function (artist, album, callback) {
+ var query = Query.album_songs(artist, album);
+ var options = Grl.OperationOptions.new(null);
+ options.set_flags (Grl.ResolutionFlags.FULL | Grl.ResolutionFlags.IDLE_RELAY);
+ grilo.tracker.query(
+ query,
+ [Grl.METADATA_KEY_ID, Grl.METADATA_KEY_TITLE, Grl.METADATA_KEY_ARTIST],
+ options,
+ Lang.bind(this, callback, null));
+ },
+
+ _searchCallback: function search_cb () {
+ log ("yeah");
+ },
+
+ search: function (q) {
+ for each (let source in this.sources) {
+ log (source.get_name () + " - " + q);
+ source.search (q, [Grl.METADATA_KEY_ID], 0, 10,
+ Grl.MetadataResolutionFlags.FULL |
+ Grl.MetadataResolutionFlags.IDLE_RELAY,
+ this._searchCallback, source);
+ }
+ },
+});
+Signals.addSignalMethods(Grilo.prototype);
+
+let grilo = new Grilo();
diff --git a/src/main.js b/src/main.js
new file mode 100644
index 0000000..6196be2
--- /dev/null
+++ b/src/main.js
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2013 Eslam Mostafa <cseslam gmail com>.
+ * Copyright (c) 2013 Seif Lotfy <seif lotfy com>.
+ *
+ * Gnome Music is free software; you can Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Music 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 Gnome Music; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+pkg.initSubmodule('libgd')
+
+const GIRepository = imports.gi.GIRepository;
+const App = imports.application;
+
+let app = new App.Application();
+app.run(ARGV);
diff --git a/src/models.js b/src/models.js
new file mode 100644
index 0000000..3b7a570
--- /dev/null
+++ b/src/models.js
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2013 Eslam Mostafa.
+ *
+ * Gnome Music is free software; you can Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Music 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 Gnome Music; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author: Eslam Mostafa <cseslam gmail com>
+ *
+ */
+
+const Gtk = imports.gi.Gtk;
+const Lang = imports.lang;
+
+const Columns = {
+ ID: Gd.MainColumns.ID,
+ URI: Gd.MainColumns.URI,
+ PRIMARY_TEXT: Gd.MainColumns.PRIMARY_TEXT,
+ SECONDARY_TEXT: Gd.MainColumns.SECONDARY_TEXT,
+ ICON: Gd.MainColumns.ICON,
+ MTIME: Gd.MainColumns.MTIME,
+ SELECTED: Gd.MainColumns.SELECTED,
+ LOCATION: 7,
+ INFO: 8
+};
+
+const BaseModel = new Lang.Class({
+ Name: "BaseModel",
+ Extends: Gtk.ListStore,
+
+ _init: function() {
+ this.parent();
+ },
+
+ push_item: function(tracker_id, uri, title, artists, icon, duration, data) {
+ var iter = this.append();
+
+ this.set(iter, [Columns.PRIMARY_TEXT, Columns.ICON], title, icon);
+ }
+});
+
+const AlbumModel = new Lang.Class({
+ Name: "AlbumModel",
+ Extends: BaseModel,
+
+ _init: function() {
+ this.parent();
+
+ this.set_columns([
+ GObject.TYPE_STRING, // Album id
+ GObject.TYPE_STRING, // Album uri
+ GObject.TYPE_STRING, // Album title
+ GObject.TYPE_STRING, // Album artists
+ GObject.TYPE_STRING, // Album data
+ GdkPixbuf.Pixbuf, // Album icon
+ GObject.TYPE_INT, // Album duration
+ GObject.TYPE_BOOLEAN // Album ??
+ ]);
+ }
+});
+
+const ArtistsModel = new Lang.Class({
+ Name: "ArtistModel",
+ Extends: BaseModel,
+
+ _init: function() {
+ this.parent();
+ }
+});
+
+const SongModel = new Lang.Class({
+ Name: "SongModel",
+ Extends: BaseModel,
+
+ _init: function() {
+ this.parent();
+ }
+});
+
+const PlaylistModel = new Lang.Class({
+ Name: "PlaylistModel",
+ Extends: BaseModel,
+
+ _init: function() {
+ this.parent();
+ }
+});
\ No newline at end of file
diff --git a/src/package.js b/src/package.js
new file mode 100644
index 0000000..67d98d1
--- /dev/null
+++ b/src/package.js
@@ -0,0 +1,282 @@
+// Copyright 2012 Giovanni Campagna
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+/**
+ * This module provides a set of convenience APIs for building packaged
+ * applications.
+ */
+
+const GLib = imports.gi.GLib;
+const GIRepository = imports.gi.GIRepository;
+const System = imports.system;
+
+const Gettext = imports.gettext;
+
+/*< public >*/
+var name;
+var version;
+var prefix;
+var datadir;
+var libdir;
+var pkgdatadir;
+var pkglibdir;
+var moduledir;
+var localedir;
+
+/*< private >*/
+let _base;
+let _requires;
+
+/**
+ * init:
+ * @params: package parameters
+ *
+ * Initialize directories and global variables. Must be called
+ * before any of other API in Package is used.
+ * @params must be an object with at least the following keys:
+ * - name: the package name ($(PACKAGE_NAME) in autotools)
+ * - version: the package version
+ * - prefix: the installation prefix
+ *
+ * init() will take care to check if the program is running from
+ * the source directory or not, by looking for a 'src' directory.
+ *
+ * At the end, the global variable 'pkg' will contain the
+ * Package module (imports.package). Additionally, the following
+ * module variables will be available:
+ * - name, version: same as in @params
+ * - prefix: the installation prefix (as passed in @params)
+ * - datadir, libdir: the final datadir and libdir when installed;
+ * usually, these would be prefix + '/share' and
+ * and prefix + '/lib' (or '/lib64')
+ * - pkgdatadir: the directory to look for private data files, such as
+ * images, stylesheets and UI definitions;
+ * this will be datadir + name when installed and
+ * './data' when running from the source tree
+ * - pkglibdir: the directory to look for private typelibs and C
+ * libraries;
+ * this will be libdir + name when installed and
+ * './lib' when running from the source tree
+ * - moduledir: the directory to look for JS modules;
+ * this will be pkglibdir when installed and
+ * './src' when running from the source tree
+ * - localedir: the directory containing gettext translation files;
+ * this will be datadir + '/locale' when installed
+ * and './po' in the source tree
+ *
+ * All paths are absolute and will not end with '/'.
+ *
+ * As a side effect, init() calls GLib.set_prgname().
+ */
+function init(params) {
+ window.pkg = imports.package;
+ name = params.name;
+ version = params.version;
+
+ // Must call it first, because it can only be called
+ // once, and other library calls might have it as a
+ // side effect
+ GLib.set_prgname(name);
+
+ prefix = params.prefix;
+ libdir = params.libdir;
+ datadir = GLib.build_filenamev([prefix, 'share']);
+ let libpath, girpath;
+
+ if (GLib.file_test('./src',
+ GLib.FileTest.IS_DIR)) {
+ log('Running from source tree, using local files');
+ // Running from source directory
+ _base = GLib.get_current_dir();
+ pkglibdir = GLib.build_filenamev([_base, 'lib']);
+ libpath = GLib.build_filenamev([pkglibdir, '.libs']);
+ girpath = pkglibdir;
+ pkgdatadir = GLib.build_filenamev([_base, 'data']);
+ localedir = GLib.build_filenamev([_base, 'po']);
+ moduledir = GLib.build_filenamev([_base, 'src']);
+ } else {
+ _base = prefix;
+ pkglibdir = GLib.build_filenamev([libdir, name]);
+ libpath = pkglibdir;
+ girpath = GLib.build_filenamev([pkglibdir, 'girepository-1.0']);
+ pkgdatadir = GLib.build_filenamev([datadir, name]);
+ localedir = GLib.build_filenamev([datadir, 'locale']);
+ moduledir = pkgdatadir;
+ }
+
+ imports.searchPath.push(moduledir);
+ GIRepository.Repository.prepend_search_path(girpath);
+ GIRepository.Repository.prepend_library_path(libpath);
+}
+
+/**
+ * start:
+ * @params: see init()
+ *
+ * This is a convenience function if your package has a
+ * single entry point.
+ * You must define a main(ARGV) function inside a main.js
+ * module in moduledir.
+ */
+function start(params) {
+ init(params);
+
+ return imports.main.main(ARGV);
+}
+
+function _checkVersion(required, current) {
+ if (required == '') {
+ // No requirement
+ return true;
+ }
+
+ // Major version must match, it's used for API
+ // incompatible changes.
+ // The rest just needs to be less or equal to
+ // current. The code is generic, but gjs modules
+ // should use only [major, minor]
+ if (required[0] != current[0])
+ return false;
+
+ for (let i = 1; i < Math.min(current.length, required.length); i++) {
+ if (required[i] > current[i])
+ return false;
+ if (required[i] < current[i])
+ return true;
+
+ // else they're equal, go on
+ }
+
+ return true;
+}
+
+function _isGjsModule(name, version) {
+ // This is a subset of the JS modules we offer,
+ // it includes only those that makes sense to use
+ // standalone and in a general app.
+ //
+ // You will not find Gettext or Format here, use
+ // the package functions instead. And Package, obviously,
+ // because it's available as window.package.
+ //
+ // cairo is also a JS module, but the version checking
+ // differs, see _isForeignModule()
+ //
+ // FIXME: Mainloop might be better as a GLib override?
+ // FIXME: Signals should be an extension to Lang
+ const RECOGNIZED_MODULE_NAMES = ['Lang',
+ 'Mainloop',
+ 'Signals',
+ 'System',
+ 'Params'];
+ for (let i = 0; i < RECOGNIZED_MODULE_NAMES.length; i++) {
+ let module = RECOGNIZED_MODULE_NAMES[i];
+
+ if (module == name) {
+ let actualModule = imports[module.toLowerCase()];
+ let required = version.split('.');
+
+ if (!_checkVersion(required, actualModule.$API_VERSION)) {
+ printerr('Unsatisfied dependency: requested GJS module at version '
+ + version + ', but only ' + (actualModule.$API_VERSION.join('.'))
+ + ' is available');
+ System.exit(1);
+ } else {
+ window[module] = actualModule;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/**
+ * require:
+ * @libs: the external dependencies to import
+ *
+ * Mark a dependency on a specific version of one or more
+ * external GI typelibs.
+ * @libs must be an object whose keys are a typelib name,
+ * and values are the respective version. The empty string
+ * indicates any version.
+ *
+ * If dependencies are statisfied, require() will make
+ * the module objects available as global names.
+ */
+function require(libs) {
+ _requires = libs;
+
+ for (let l in libs) {
+ let version = libs[l];
+
+ if (_isGjsModule(l, version))
+ continue;
+
+ if (version != '')
+ imports.gi.versions[l] = version;
+
+ try {
+ if (name == 'cairo') {
+ // Import the GI package to check the version,
+ // but then load the JS one
+ imports.gi.cairo;
+ window.cairo = imports.cairo;
+ } else {
+ window[l] = imports.gi[l];
+ }
+ } catch(e) {
+ printerr('Unsatisfied dependency: ' + e.message);
+ System.exit(1);
+ }
+ }
+}
+
+function dumpRequires() {
+ print(JSON.stringify(_requires));
+}
+
+function initGettext() {
+ Gettext.bindtextdomain(name, localedir);
+ Gettext.textdomain(name);
+
+ let gettext = imports.gettext;
+ window._ = gettext.gettext;
+ window.C_ = gettext.pgettext;
+ window.N_ = function(x) { return x; }
+}
+
+function initFormat() {
+ let format = imports.format;
+ String.prototype.format = format.format;
+}
+
+function initSubmodule(name) {
+ if (moduledir != pkgdatadir) {
+ // Running from source tree, add './name' to search paths
+
+ let submoduledir = GLib.build_filenamev([_base, name]);
+ let libpath = GLib.build_filenamev([submoduledir, '.libs']);
+ GIRepository.Repository.prepend_search_path(submoduledir);
+ GIRepository.Repository.prepend_library_path(libpath);
+ } else {
+ // Running installed, submodule is in $(pkglibdir), nothing to do
+ }
+}
diff --git a/src/player.js b/src/player.js
new file mode 100644
index 0000000..e6372db
--- /dev/null
+++ b/src/player.js
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2013 Eslam Mostafa.
+ *
+ * Gnome Music is free software; you can Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Music 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 Gnome Music; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author: Eslam Mostafa <cseslam gmail com>
+ *
+ */
+
+const Lang = imports.lang;
+const Gtk = imports.gi.Gtk;
+const Gd = imports.gi.Gd;
+const Gst = imports.gi.Gst;
+const GLib = imports.gi.GLib;
+
+//pkg.initSubmodule('libgd');
+
+const Mainloop = imports.mainloop;
+const AlbumArtCache = imports.album_art_cache;
+
+const ART_SIZE = 240;
+
+const PlayPauseButton = new Lang.Class({
+ Name: "PlayPauseButton",
+ Extends: Gtk.ToggleButton,
+
+ _init: function() {
+ this.play_image = Gtk.Image.new_from_icon_name("media-playback-start-symbolic", Gtk.IconSize.BUTTON);
+ this.pause_image = Gtk.Image.new_from_icon_name("media-playback-pause-symbolic",
Gtk.IconSize.BUTTON);
+
+ this.parent();
+ this.set_image(this.play_image);
+ },
+});
+
+const Player = new Lang.Class({
+ Name: "Player",
+ //Extends: GLib.Object,
+
+ _init: function(playlist) {
+ this.playlist = playlist;
+ this.cache = AlbumArtCache.AlbumArtCache.getDefault();
+
+ //Gst.init(null, 0);
+ //this.source = new Gst.ElementFactory.make("audiotestrc", "source");
+ //this.sink = new Gst.ElementFactory.make("autoaudiosink", "output");
+ //this.playbin = new Gst.ElementFactory.make("playbin", "playbin");
+ //this.bus = this.playbin.get_bus();
+
+ this._setup_view();
+ },
+
+ _setup_view: function() {
+ var alignment,
+ artist_lbl,
+ box,
+ databox,
+ label,
+ next_btn,
+ play_btn,
+ prev_btn,
+ rate_btn,
+ toolbar_center,
+ toolbar_end,
+ toolbar_start,
+ toolbar_song_info;
+
+ this.eventbox = new Gtk.Box();
+ this.eventbox.set_spacing(9)
+ this.eventbox.set_border_width(9)
+ toolbar_start = new Gtk.Box({
+ orientation: Gtk.Orientation.HORIZONTAL,
+ spacing: 0
+ });
+ toolbar_start.get_style_context().add_class(Gtk.STYLE_CLASS_LINKED);
+
+ prev_btn = new Gtk.Button();
+ prev_btn.set_image(Gtk.Image.new_from_icon_name("media-skip-backward-symbolic",
Gtk.IconSize.BUTTON));
+ prev_btn.connect("clicked", Lang.bind(this, this._onPrevBtnClicked));
+ toolbar_start.pack_start(prev_btn, false, false, 0);
+
+ play_btn = new PlayPauseButton();
+ play_btn.connect("toggled", Lang.bind(this, this._onPlayBtnToggled));
+ toolbar_start.pack_start(play_btn, false, false, 0);
+
+ next_btn = new Gtk.Button();
+ next_btn.set_image(Gtk.Image.new_from_icon_name("media-skip-forward-symbolic", Gtk.IconSize.BUTTON));
+ next_btn.connect("clicked", Lang.bind(this, this._onNextBtnClicked));
+ toolbar_start.pack_start(next_btn, false, false, 0);
+
+ this.eventbox.pack_start(toolbar_start, false, false, 3)
+
+ toolbar_song_info = new Gtk.Box({
+ orientation: Gtk.Orientation.HORIZONTAL,
+ spacing: 0
+ });
+
+ this.cover_img = new Gtk.Image();
+ toolbar_song_info.pack_start(this.cover_img, false, false, 0);
+
+ databox = new Gtk.Box({
+ orientation: Gtk.Orientation.VERTICAL,
+ spacing: 0
+ });
+ toolbar_song_info.pack_start(databox, false, false, 0);
+ toolbar_start.pack_start(toolbar_song_info, false, false, 9)
+
+
+ this.title_lbl = new Gtk.Label({
+ label: ""
+ });
+ databox.pack_start(this.title_lbl, false, false, 0);
+
+ artist_lbl = new Gtk.Label({
+ label: ""
+ });
+ artist_lbl.get_style_context().add_class("dim-label");
+ databox.pack_start(artist_lbl, false, false, 0);
+
+ toolbar_center = new Gtk.Box({
+ orientation: Gtk.Orientation.HORIZONTAL,
+ spacing: 0
+ });
+
+ this.progress_scale = new Gtk.Scale({
+ orientation: Gtk.Orientation.HORIZONTAL,
+ sensitive: false
+ });
+ this.progress_scale.set_draw_value(false);
+ this._setDuration(1);
+ this.progress_scale.connect("change_value", Lang.bind(this, this.onProgressScaleChangeValue));
+ toolbar_center.pack_start(this.progress_scale, true, true, 0);
+
+ /*this.song_playback_time_lbl = new Gtk.Label({
+ label: "00:00"
+ });
+ toolbar_center.pack_start(this.song_playback_time_lbl, false, false, 0);
+ label = new Gtk.Label({
+ label: "/"
+ });
+ toolbar_center.pack_start(label, false, false, 0);
+ this.song_total_time_lbl = new Gtk.Label({
+ label: "00:00"
+ });
+ toolbar_center.pack_start(this.song_total_time_lbl, false, false, 0);
+ */
+ this.eventbox.pack_start(toolbar_center, true, true, 0)
+
+ toolbar_end = new Gtk.Box({
+ orientation: Gtk.Orientation.HORIZONTAL,
+ spacing: 5
+ });
+ alignment = new Gtk.Alignment({
+ xalign: 1,
+ yalign: 0.5,
+ xscale: 0,
+ yscale: 0
+ });
+ this.eventbox.pack_end(toolbar_end, false, false, 3);
+
+ rate_btn = new Gtk.Button ();
+ rate_btn.set_image(Gtk.Image.new_from_icon_name("bookmark-new-symbolic", Gtk.IconSize.BUTTON));
+ toolbar_end.pack_end(rate_btn, false, false, 0);
+
+ this.shuffle_btn = new Gtk.ToggleButton ();
+ this.shuffle_btn.set_image (Gtk.Image.new_from_icon_name("media-playlist-shuffle-symbolic",
Gtk.IconSize.BUTTON));
+ this.shuffle_btn.connect("clicked", Lang.bind(this, this._onShuffleBtnClicked));
+ toolbar_end.pack_end(this.shuffle_btn, false, false, 0);
+
+ this.eventbox.show_all();
+
+ },
+
+ load: function(media) {
+ var pixbuf,
+ uri;
+
+ this._setDuration(media.get_duration());
+ this.song_total_time_lbl.set_label(this.seconds_to_string (media.get_duration()));
+ this.progress_scale.sensitive = true;
+
+ // FIXME: site contains the album's name. It's obviously a hack to remove
+ pixbuf = this.cache.lookup (ART_SIZE, media.get_author (), media.get_site ());
+ this.cover_img.set_from_pixbuf (pixbuf);
+
+ if (media.get_title() != null) {
+ this.title_lbl.set_label(media.get_title());
+ }
+
+ else {
+ let url = media.get_url(),
+ file = GLib.File.new_for_path(url),
+ basename = file.get_basename(),
+ to_show = GLib.Uri.unescape_string(basename, null);
+
+ this.title_lbl.set_label(to_show);
+ }
+
+ artist_lbl.set_label(media.get_author());
+
+ uri = media.get_url();
+ },
+
+ uri: function() {
+ },
+
+ _onPlayBtnToggled: function(btn) {
+ if (btn.get_active()) {
+ //this.beginPlayback();
+ }
+
+ else {
+ //this.pausePlayback();
+ }
+ },
+
+ _onNextBtnClicked: function(btn) {
+ this._needNext();
+ },
+
+ _onPrevBtnClicked: function(btn) {
+ this._needPrevious();
+ },
+
+ _needNext: function() {
+ },
+
+ _needPrevious: function() {
+ },
+
+ _onShuffleBtnClicked: function(order) {
+ },
+
+ _onPlaylistShuffleModeChanged: function(mode) {
+ this.shuffle_btn.set_active(mode);
+ },
+
+ _setDuration: function(duration) {
+ this.progress_scale.set_range(0.0, duration*60);
+ this.progress_scale.set_value(0.0);
+ },
+
+ _updatePosition: function(update) {
+ if (update) {
+ if (this.position_update_timeout == 0) {
+ Timeout.add_seconds(1, Lang.bind(this, this.update_position_cb));
+ }
+ }
+
+ else {
+ if (this.position_update_timeout != 0) {
+ this.source.remove(position_update_timeout);
+ this.position_update_timeout = 0;
+ }
+ }
+ },
+
+ _updatePositionCallback: function() {
+ var format = Gst.Format.TIME,
+ duration = 0,
+ seconds;
+
+ this.playbin.query_position(format, duration);
+ this.progress_scale.set_value(duration);
+
+ seconds = duration / Gst.SECOND;
+
+ this.song_playback_time_lbl.set_label(this.seconds_to_string(seconds));
+
+ return true;
+ },
+
+ onProgressScaleChangeValue: function(scroll, newValue) {
+ this.playbin.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, newValue);
+
+ return false;
+ }
+});
diff --git a/src/query.js b/src/query.js
new file mode 100644
index 0000000..8a647dc
--- /dev/null
+++ b/src/query.js
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2013 Igalia S.L.
+ * Authored by: Juan A. Suarez Romero <jasuarez igalia com>
+ *
+ * Gnome Music is free software; you can Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Music 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 Gnome Music; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *
+ */
+
+const album = 'SELECT rdf:type(?album) tracker:id(?album) as id (SELECT nmm:artistName(?artist) WHERE {
?album nmm:albumArtist ?artist } LIMIT 1) AS artist nie:title(?album) as title nie:title(?album) as album
tracker:coalesce( (SELECT GROUP_CONCAT(nmm:artistName(?artist), ",") WHERE { ?album nmm:albumArtist ?artist
}), (SELECT GROUP_CONCAT((SELECT nmm:artistName(nmm:performer(?_12)) as perf WHERE { ?_12 nmm:musicAlbum
?album } GROUP BY ?perf), ",") as album_performer WHERE { }) ) as author
xsd:integer(tracker:coalesce(nmm:albumTrackCount(?album), (SELECT COUNT(?_1) WHERE { ?_1 nmm:musicAlbum
?album; tracker:available "true" }))) as childcount (SELECT fn:year-from-dateTime(?c) WHERE { ?_2
nmm:musicAlbum ?album; nie:contentCreated ?c; tracker:available "true" } LIMIT 1) as publishing-date { ?album
a nmm:MusicAlbum FILTER (EXISTS { ?_3 nmm:musicAlbum ?album; tracker:available "true" }) } ORDER BY
?album_artist ?albumyear nie:title(?album)';
+
+const album_count = 'SELECT COUNT(?album) AS childcount WHERE { ?album a nmm:MusicAlbum }';
+
+const songs = 'SELECT rdf:type(?song) tracker:id(?song) as id nie:url(?song) as url nie:title(?song) as
title nmm:artistName(nmm:performer(?song)) as artist nie:title(nmm:musicAlbum(?song)) as album
nfo:duration(?song) as duration { ?song a nmm:MusicPiece } ORDER BY tracker:added(?song)';
+
+const songs_count = 'SELECT COUNT(?song) AS childcount WHERE { ?song a nmm:MusicPiece }';
+
+function album_songs (artistName, albumTitle) {
+ var query = "SELECT rdf:type(?song) tracker:id(?song) as id nie:url(?song) as url nie:title(?song) as
title nmm:artistName(nmm:performer(?song)) as artist nie:title(nmm:musicAlbum(?song)) as album
nfo:duration(?song) as duration { ?song a nmm:MusicPiece ; nmm:performer [ nmm:artistName '%an' ] ;
nmm:musicAlbum [ nmm:albumTitle '%at' ] } ORDER BY tracker:added(?song)";
+ query = query.replace("%an", artistName);
+ query = query.replace("%at", albumTitle);
+ return query;
+}
diff --git a/src/resources/app-menu.ui b/src/resources/app-menu.ui
new file mode 100644
index 0000000..a45acc9
--- /dev/null
+++ b/src/resources/app-menu.ui
@@ -0,0 +1,27 @@
+<interface>
+ <menu id="app-menu">
+ <section>
+ <item>
+ <attribute name="action">app.newPlaylist</attribute>
+ <attribute name="label" translatable="yes">_New Playlist</attribute>
+ </item>
+ </section>
+ <section>
+ <item>
+ <attribute name="action">app.nowPlaying</attribute>
+ <attribute name="label" translatable="yes">_Now Playing</attribute>
+ </item>
+ </section>
+ <section>
+ <item>
+ <attribute name="action">app.about</attribute>
+ <attribute name="label" translatable="yes">_About Contacts</attribute>
+ </item>
+ <item>
+ <attribute name="action">app.quit</attribute>
+ <attribute name="label" translatable="yes">_Quit</attribute>
+ <attribute name="accel"><Primary>q</attribute>
+ </item>
+ </section>
+ </menu>
+</interface>
diff --git a/data/gtk-style.css b/src/resources/gtk-style.css
similarity index 99%
rename from data/gtk-style.css
rename to src/resources/gtk-style.css
index 06aef5d..fdf22fb 100644
--- a/data/gtk-style.css
+++ b/src/resources/gtk-style.css
@@ -92,3 +92,4 @@
font-weight: bold;
color: mix (@theme_fg_color, @theme_bg_color, 0.50);
}
+
diff --git a/src/resources/lp.jpg b/src/resources/lp.jpg
new file mode 100644
index 0000000..e1be0ba
Binary files /dev/null and b/src/resources/lp.jpg differ
diff --git a/src/music.gresource.xml b/src/resources/music.gresource.xml
similarity index 100%
rename from src/music.gresource.xml
rename to src/resources/music.gresource.xml
diff --git a/src/org.gnome.Music.gschema.xml.in b/src/resources/org.gnome.Music.gschema.xml.in
similarity index 100%
rename from src/org.gnome.Music.gschema.xml.in
rename to src/resources/org.gnome.Music.gschema.xml.in
diff --git a/src/searchbar.js b/src/searchbar.js
new file mode 100644
index 0000000..29344c4
--- /dev/null
+++ b/src/searchbar.js
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2013 Eslam Mostafa.
+ *
+ * Gnome Music is free software; you can Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Music 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 Gnome Music; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author: Eslam Mostafa <cseslam gmail com>
+ *
+ */
+
+const Lang = imports.lang;
+const Gtk = imports.gi.Gtk;
+const Gdk = imports.gi.Gdk;
+const Gd = imports.gi.Gd;
+
+const Searchbar = new Lang.Class({
+ Name: "Searchbar",
+
+ actor: function() { return this._eventbox; },
+
+ _eventbox: null,
+
+ _search_entry: null,
+
+ _init: function() {
+ this._setup_ui();
+ },
+
+ _setup_ui: function() {
+ this._eventbox = new Gtk.EventBox();
+ //this._eventbox.margin_top = 5;
+ //this._eventbox.margin_bottom = 5;
+
+ let container = new Gd.MarginContainer();
+ //container.min_margin = 64;
+ //container.max_margin = 128;
+ this._eventbox.add (container);
+
+ let box = new Gtk.Box({orientation: Gtk.Orientation.HORIZONTAL, spacing : 0});
+ container.add(box);
+
+ this._search_entry = new Gd.TaggedEntry();
+ this._search_entry.hexpand = true;
+ this._search_entry.key_press_event.connect (on_search_entry_key_pressed);
+ this._search_entry.changed.connect (on_search_entry_changed);
+ this._search_entry.tag_clicked.connect (on_search_entry_tag_clicked);
+ box.add (this._search_entry);
+
+ this._eventbox.show_all();
+ },
+
+ _on_search_entry_key_pressed: function(e) {
+ let keyval = e.keyval;
+
+ if(keyval == Gdk.Key.Escape) {
+ //App.app.search_mode = false;
+ return true;
+ }
+
+ return false;
+ },
+
+ _on_search_entry_changed: function() {
+ debug("2");
+ },
+
+ _on_search_entry_tag_clicked: function() {
+ debug("3");
+ },
+
+ show: function() {
+ this.actor.show();
+ },
+
+ hide: function() {
+ this.actor.hide();
+ },
+
+ grab_focus: function() {
+ this._search_entry.grab_focus();
+ },
+
+});
+
+const Dropdown = new Lang.Class({
+ Name: 'Dropdown',
+
+ _init: function() {
+ this._sourceView = new Manager.BaseView(Application.sourceManager);
+ this._typeView = new Manager.BaseView(Application.searchTypeManager);
+ this._matchView = new Manager.BaseView(Application.searchMatchManager);
+ // TODO: this is out for now, but should we move it somewhere
+ // else?
+ // this._categoryView = new Manager.BaseView(Application.searchCategoryManager);
+
+ this._sourceView.connect('item-activated',
+ Lang.bind(this, this._onItemActivated));
+ this._typeView.connect('item-activated',
+ Lang.bind(this, this._onItemActivated));
+ this._matchView.connect('item-activated',
+ Lang.bind(this, this._onItemActivated));
+
+ let frame = new Gtk.Frame({ shadow_type: Gtk.ShadowType.IN,
+ opacity: 0.9 });
+ frame.get_style_context().add_class('documents-dropdown');
+
+ this.widget = new Gd.Revealer({ halign: Gtk.Align.CENTER,
+ valign: Gtk.Align.START });
+ this.widget.add(frame);
+
+ this._grid = new Gtk.Grid({ orientation: Gtk.Orientation.HORIZONTAL });
+ frame.add(this._grid);
+
+ this._grid.add(this._sourceView.widget);
+ this._grid.add(this._typeView.widget);
+ this._grid.add(this._matchView.widget);
+ //this._grid.add(this._categoryView.widget);
+
+ this.hide();
+ this.widget.show_all();
+ },
+});
diff --git a/src/toolbar.js b/src/toolbar.js
new file mode 100644
index 0000000..8d3e57d
--- /dev/null
+++ b/src/toolbar.js
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2013 Next Tuesday GmbH.
+ * Authored by: Seif Lotfy <sfl nexttuesday de>
+ * Copyright (c) 2013 Seif Lotfy <seif lotfy com>.
+ *
+ * Gnome Music is free software; you can Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Music 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 Gnome Music; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+const Lang = imports.lang;
+
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Gtk = imports.gi.Gtk;
+const Gd = imports.gi.Gd;
+const Pango = imports.gi.Pango;
+const Signals = imports.signals;
+
+const Gettext = imports.gettext;
+const _ = imports.gettext.gettext;
+
+const Searchbar = imports.searchbar;
+
+const ToolbarState = {
+ SINGLE: 0,
+ ALBUMS: 1,
+ ARTISTS: 2,
+ PLAYLISTS: 3,
+ SONGS: 4};
+
+const Toolbar = new Lang.Class({
+ Name: 'MainToolbar',
+ Extends: Gd.HeaderBar,
+
+ _init: function() {
+ this.parent();
+ this._stack_switcher = new Gd.StackSwitcher ();
+ this.set_custom_title (null);
+ this._addBackButton();
+ },
+
+ set_stack: function(stack) {
+ this._stack_switcher.set_stack (stack);
+ },
+
+ get_stack: function() {
+ return this._stack_switcher.get_stack();
+ },
+
+ setState: function (state) {
+ if (state == ToolbarState.SINGLE) {
+ this.custom_title = null
+ this._backButton.show()
+ }
+ else {
+ this.title = ""
+ this.custom_title = this._stack_switcher;
+ this._backButton.hide()
+ }
+ this.emit ("state-changed")
+ },
+
+ _addBackButton: function() {
+ let iconName =
+ (this.get_direction() == Gtk.TextDirection.RTL) ?
+ 'go-next-symbolic' : 'go-previous-symbolic';
+ this._backButton = new Gd.HeaderSimpleButton({ symbolic_icon_name: iconName,
+ label: _("Back") });
+ this._backButton.connect('clicked', Lang.bind(this, this.setState))
+ this.pack_start(this._backButton);
+ }
+});
+Signals.addSignalMethods(Toolbar.prototype);
diff --git a/src/view.js b/src/view.js
new file mode 100644
index 0000000..0092b43
--- /dev/null
+++ b/src/view.js
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2013 Next Tuesday GmbH.
+ * Authored by: Seif Lotfy
+ * Copyright (c) 2013 Eslam Mostafa.
+ * Copyright (c) 2013 Seif Lotfy.
+ *
+ * Gnome Music is free software; you can Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Music 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 Gnome Music; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+const Lang = imports.lang;
+const Gtk = imports.gi.Gtk;
+const Gdk = imports.gi.Gdk;
+const GdkPixbuf = imports.gi.GdkPixbuf;
+const GObject = imports.gi.GObject;
+const Gd = imports.gi.Gd;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Grl = imports.gi.Grl;
+const Tracker = imports.gi.Tracker;
+const Signals = imports.signals;
+const Application = imports.application;
+const Query = imports.query;
+const Widgets = imports.widgets;
+const Toolbar = imports.toolbar;
+
+const tracker = Tracker.SparqlConnection.get (null);
+const AlbumArtCache = imports.album_art_cache;
+const Grilo = imports.grilo;
+const albumArtCache = AlbumArtCache.AlbumArtCache.getDefault();
+
+function extractFileName(uri) {
+ var exp = /^.*[\\\/]|[.][^.]*$/g;
+ return unescape(uri.replace(exp, ''));
+}
+
+const grilo = Grilo.grilo;
+
+
+const LoadMoreButton = new Lang.Class({
+ Name: 'LoadMoreButton',
+ _init: function(counter) {
+ this._block = false;
+ this._counter = counter;
+ let child = new Gtk.Grid({ column_spacing: 10,
+ hexpand: true,
+ halign: Gtk.Align.CENTER,
+ visible: true });
+
+ this._spinner = new Gtk.Spinner({ halign: Gtk.Align.CENTER,
+ no_show_all: true });
+ this._spinner.set_size_request(16, 16);
+ child.add(this._spinner);
+
+ this._label = new Gtk.Label({ label: "Load More",
+ visible: true });
+ child.add(this._label);
+
+ this.widget = new Gtk.Button({ no_show_all: true,
+ child: child });
+ this.widget.get_style_context().add_class('documents-load-more');
+ this.widget.connect('clicked', Lang.bind(this,
+ function() {
+ this._label.label = "Loading...";
+ this._spinner.show();
+ this._spinner.start();
+ }));
+
+ this._onItemCountChanged();
+ },
+
+ _onItemCountChanged: function() {
+ let remainingDocs = this._counter();
+ let visible = !(remainingDocs <= 0 || this._block);
+ this.widget.set_visible(visible);
+
+ if (!visible) {
+ this._label.label = "Load More";
+ this._spinner.stop();
+ this._spinner.hide();
+ }
+ },
+
+ setBlock: function(block) {
+ if (this._block == block)
+ return;
+
+ this._block = block;
+ this._onItemCountChanged();
+ }
+});
+
+const ViewContainer = new Lang.Class({
+ Name: "ViewContainer",
+ Extends: Gd.Stack,
+
+ _init: function(title, header_bar) {
+ this.parent({transition_type: Gd.StackTransitionType.CROSSFADE});
+ this._grid = new Gtk.Grid({orientation: Gtk.Orientation.VERTICAL})
+ this._iconWidth = -1
+ this._iconHeight = 128
+ this._offset = 0;
+ this._adjustmentValueId = 0;
+ this._adjustmentChangedId = 0;
+ this._scrollbarVisibleId = 0;
+ this._model = Gtk.ListStore.new([
+ GObject.TYPE_STRING,
+ GObject.TYPE_STRING,
+ GObject.TYPE_STRING,
+ GObject.TYPE_STRING,
+ GdkPixbuf.Pixbuf,
+ GObject.TYPE_OBJECT,
+ GObject.TYPE_BOOLEAN
+ ]);
+ this.view = new Gd.MainView({
+ shadow_type: Gtk.ShadowType.NONE
+ });
+ this.view.set_view_type(Gd.MainViewType.ICON);
+ this.view.set_model(this._model);
+ this._grid.add(this.view);
+
+ this._loadMore = new LoadMoreButton(this._getRemainingItemCount);
+ this._grid.add(this._loadMore.widget);
+ this._loadMore.widget.connect("clicked", Lang.bind(this, this.populate))
+ this.view.connect('item-activated',
+ Lang.bind(this, this._onItemActivated));
+ this._cursor = null;
+ this.header_bar = header_bar;
+ this.title = title;
+
+ this.add(this._grid)
+
+ this.show_all();
+ this._items = [];
+ this._loadMore.widget.hide();
+ this._connectView();
+ grilo.connect('ready', Lang.bind(this, this.populate));
+ this.header_bar.connect('state-changed', Lang.bind(this, this._onStateChanged))
+ },
+
+ _onStateChanged: function() {
+ },
+
+ _connectView: function() {
+ this._adjustmentValueId = this.view.vadjustment.connect(
+ 'value-changed',
+ Lang.bind(this, this._onScrolledWinChange)
+ );
+ this._adjustmentChangedId = this.view.vadjustment.connect(
+ 'changed',
+ Lang.bind(this, this._onScrolledWinChange)
+ );
+ this._scrollbarVisibleId = this.view.get_vscrollbar().connect(
+ 'notify::visible',
+ Lang.bind(this, this._onScrolledWinChange)
+ );
+ this._onScrolledWinChange();
+ },
+
+ _onScrolledWinChange: function() {
+ let vScrollbar = this.view.get_vscrollbar();
+ let adjustment = this.view.vadjustment;
+ let revealAreaHeight = 32;
+
+ // if there's no vscrollbar, or if it's not visible, hide the button
+ if (!vScrollbar ||
+ !vScrollbar.get_visible()) {
+ this._loadMore.setBlock(true);
+ return;
+ }
+
+ let value = adjustment.value;
+ let upper = adjustment.upper;
+ let page_size = adjustment.page_size;
+
+ let end = false;
+ // special case this values which happen at construction
+ if ((value == 0) && (upper == 1) && (page_size == 1))
+ end = false;
+ else
+ end = !(value < (upper - page_size - revealAreaHeight));
+ if (this._getRemainingItemCount() <= 0)
+ end = false;
+ this._loadMore.setBlock(!end);
+ },
+
+ populate: function() {
+ },
+
+ _addItem: function(source, param, item) {
+ if (item != null) {
+ this._offset += 1;
+ let path = "/usr/share/icons/gnome/scalable/places/folder-music-symbolic.svg";
+ let icon = GdkPixbuf.Pixbuf.new_from_file_at_scale(path, this._iconHeight, this._iconWidth,
true);
+ var iter = this._model.append();
+ var artist = "Unkown"
+ if (item.get_author() != null)
+ artist = item.get_author();
+ if (item.get_string(Grl.METADATA_KEY_ARTIST) != null)
+ artist = item.get_string(Grl.METADATA_KEY_ARTIST)
+ if (item.get_title() == null) {
+ item.set_title (extractFileName(item.get_url()));
+ }
+ this._model.set(
+ iter,
+ [0, 1, 2, 3, 4, 5],
+ [toString(item.get_id()), "", item.get_title(), artist, icon, item]
+ );
+ GLib.idle_add(300, Lang.bind(this, this._updateAlbumArt, item, iter));
+ }
+ },
+
+ _getRemainingItemCount: function () {
+ let count = -1;
+ if (this.countQuery != null) {
+ let cursor = tracker.query(this.countQuery, null)
+ if (cursor != null && cursor.next(null))
+ count = cursor.get_integer(0);
+ }
+ return ( count - this._offset);
+ },
+
+ _updateAlbumArt: function(item, iter) {
+ var artist = null;
+ if (item.get_author() != null)
+ artist = item.get_author();
+ if (item.get_string(Grl.METADATA_KEY_ARTIST) != null)
+ artist = item.get_string(Grl.METADATA_KEY_ARTIST)
+ var icon = albumArtCache.lookup(this._iconHeight, artist, item.get_string(Grl.METADATA_KEY_ALBUM));
+ if (icon != null) {
+ this._model.set_value(iter, 4, icon);
+ return false;
+ }
+ var options = Grl.OperationOptions.new(null);
+ options.set_flags (Grl.ResolutionFlags.FULL | Grl.ResolutionFlags.IDLE_RELAY);
+ grilo.tracker.resolve(
+ item,
+ [Grl.METADATA_KEY_THUMBNAIL],
+ options,
+ Lang.bind(this,
+ function(source, param, item) {
+ var uri = item.get_thumbnail();
+ albumArtCache.getFromUri(uri,
+ artist,
+ item.get_string(Grl.METADATA_KEY_ALBUM),
+ this._iconWidth,
+ this._iconHeight,
+ Lang.bind(this,
+ function (icon) {
+ this._model.set_value(iter, 4, icon);
+ }))
+ }));
+ return false;
+ },
+
+ _addListRenderers: function () {
+ },
+
+ _onItemActivated: function (widget, id, path) {
+ }
+
+});
+
+const Albums = new Lang.Class({
+ Name: "AlbumsView",
+ Extends: ViewContainer,
+
+ _init: function(header_bar){
+ this.parent("Albums", header_bar);
+ this.view.set_view_type(Gd.MainViewType.ICON);
+ this.countQuery = Query.album_count;
+ this._albumWidget = new Widgets.AlbumWidget ();
+ this.add(this._albumWidget)
+ this.header_bar.setState (1);
+ },
+
+ _onStateChanged: function (widget) {
+ if (this.header_bar.get_stack() != null &&
+ this == this.header_bar.get_stack().get_visible_child())
+ this.visible_child = this._grid;
+ },
+
+ _onItemActivated: function (widget, id, path) {
+ var iter = this._model.get_iter (path)[1]
+ var title = this._model.get_value (iter, 2)
+ var artist = this._model.get_value (iter, 3)
+ var item = this._model.get_value (iter, 5)
+ var window = new Gtk.Window ()
+ this._albumWidget.update (artist, title, item)
+ this.header_bar.setState (0);
+ this.header_bar.title = title
+ this.header_bar.sub_title = artist
+ this.visible_child = this._albumWidget
+ },
+
+ populate: function() {
+ if (grilo.tracker != null)
+ grilo.populateAlbums (this._offset, Lang.bind(this, this._addItem, null));
+ },
+
+});
+
+const Songs = new Lang.Class({
+ Name: "SongsView",
+ Extends: ViewContainer,
+
+ _init: function(header_bar) {
+ this.parent("Songs", header_bar);
+ this.countQuery = Query.songs_count;
+ this._items = {};
+ this.view.set_view_type(Gd.MainViewType.LIST);
+ this._iconHeight = 32
+ this._iconWidth = 32
+ this._addListRenderers();
+ },
+
+ _addListRenderers: function() {
+ let listWidget = this.view.get_generic_view();
+
+ let typeRenderer =
+ new Gd.StyledTextRenderer({ xpad: 0 });
+ typeRenderer.add_class('dim-label');
+ typeRenderer.set_property("ellipsize", 3);
+ listWidget.add_renderer(typeRenderer, Lang.bind(this,
+ function(col, cell, model, iter) {
+ let item = model.get_value(iter, 5);
+ typeRenderer.set_property("ellipsize", 3);
+ typeRenderer.text = item.get_string(Grl.METADATA_KEY_ALBUM);
+ }));
+
+ let durationRenderer =
+ new Gd.StyledTextRenderer({ xpad: 16 });
+ durationRenderer.add_class('dim-label');
+ listWidget.add_renderer(durationRenderer, Lang.bind(this,
+ function(col, cell, model, iter) {
+ let item = model.get_value(iter, 5);
+ let duration = item.get_duration ();
+ var minutes = parseInt(duration / 60);
+ var seconds = duration % 60;
+ var time = null
+ if (seconds < 10)
+ time = minutes + ":0" + seconds;
+ else
+ time = minutes + ":" + seconds;
+ durationRenderer.text = time;
+ }));
+ },
+
+ populate: function() {
+ if (grilo.tracker != null)
+ grilo.populateSongs (this._offset, Lang.bind(this, this._addItem, null));
+ },
+
+});
+
+const Playlists = new Lang.Class({
+ Name: "PlaylistsView",
+ Extends: ViewContainer,
+
+ _init: function(header_bar) {
+ this.parent("Playlists", header_bar);
+ },
+});
+
+const Artists = new Lang.Class({
+ Name: "ArtistsView",
+ Extends: ViewContainer,
+
+ _init: function(header_bar) {
+ this.parent("Artists", header_bar);
+ },
+});
diff --git a/src/widgets.js b/src/widgets.js
new file mode 100644
index 0000000..5a1498d
--- /dev/null
+++ b/src/widgets.js
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2013 Eslam Mostafa <cseslam gmail com>.
+ * Copyright (c) 2013 Seif Lotfy <seif lotfy com>.
+ *
+ * Gnome Music is free software; you can Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Music 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 Gnome Music; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+const Gtk = imports.gi.Gtk;
+const Gdk = imports.gi.Gdk;
+const GdkPixbuf = imports.gi.GdkPixbuf;
+const Gio = imports.gi.Gio;
+const Lang = imports.lang;
+const Grl = imports.gi.Grl;
+const Query = imports.query;
+const Grilo = imports.grilo;
+
+const grilo = Grilo.grilo;
+const AlbumArtCache = imports.album_art_cache;
+const albumArtCache = AlbumArtCache.AlbumArtCache.getDefault();
+
+const ClickableLabel = new Lang.Class({
+ Name: "ClickableLabel",
+ Extends: Gtk.Button,
+
+ _init: function (track) {
+ this.track = track
+ var text = track.get_title()
+ var duration = track.get_duration()
+ let box = new Gtk.HBox();
+ let label = new Gtk.Label({ label : text });
+ label.set_alignment(0.0, 0.5)
+
+ var minutes = parseInt(duration / 60);
+ var seconds = duration % 60;
+ var time = null
+ if (seconds < 10)
+ time = minutes + ":0" + seconds;
+ else
+ time = minutes + ":" + seconds;
+ let length_label = new Gtk.Label({ label : time });
+ length_label.set_alignment(0.0, 0.5)
+
+ this.parent();
+ this.set_relief(Gtk.ReliefStyle.NONE);
+ this.set_can_focus(false);
+ box.homogeneous = true;
+ box.pack_start(label, true, true, 0);
+ box.pack_end(length_label, true, true, 0);
+ this.add(box);
+ this.show_all();
+
+ this.connect("clicked", Lang.bind(this, this._on_btn_clicked));
+ },
+
+ _on_btn_clicked: function(btn) {
+ },
+
+});
+
+
+const AlbumWidget = new Lang.Class({
+ Name: "AlbumWidget",
+ Extends: Gtk.EventBox,
+
+ _init: function (album) {
+ this.hbox = new Gtk.HBox ();
+ this.box = new Gtk.VBox();
+ this.scrolledWindow = new Gtk.ScrolledWindow();
+ this.songsList = new Gtk.VBox();
+ this.cover = new Gtk.Image();
+ this.vbox = new Gtk.VBox();
+ this.title_label = new Gtk.Label({label : ""});
+ this.artist_label = new Gtk.Label({label : ""});
+ this.tracks_labels = {};
+ this.running_length = 0;
+ this.released_label = new Gtk.Label()
+ this.released_label.set_markup ("<span color='grey'>Released</span>");
+ this.running_length_label = new Gtk.Label({});
+ this.running_length_label.set_markup ("<span color='grey'>Running Length</span>");
+ this.released_label_info = new Gtk.Label({label: "----"});
+ this.running_length_label_info = new Gtk.Label({label: "--:--"});
+ this.released_label.set_alignment(1.0, 0.5)
+ this.running_length_label.set_alignment(1.0, 0.5)
+ this.released_label_info.set_alignment(0.0, 0.5)
+ this.running_length_label_info.set_alignment(0.0, 0.5)
+
+ this.parent();
+ this.hbox.set_homogeneous(true);
+ this.box.set_homogeneous (false);
+ this.songsList.pack_start(new Gtk.Label(), false, false, 24)
+ this.songsList.pack_end(new Gtk.Label(), true, true, 24)
+ this.vbox.set_homogeneous(false);
+ this.scrolledWindow.set_policy(
+ Gtk.PolicyType.NEVER,
+ Gtk.PolicyType.AUTOMATIC);
+ this.scrolledWindow.add(this.songsList);
+
+
+ this.infobox = new Gtk.Box()
+ this.infobox.homogeneous = true;
+ this.infobox.spacing = 36
+ var box = new Gtk.VBox();
+ box.pack_start (this.released_label, false, false, 0)
+ box.pack_start (this.running_length_label, false, false, 0)
+ this.infobox.pack_start(box, true, true, 0)
+ box = new Gtk.VBox();
+ box.pack_start (this.released_label_info, false, false, 0)
+ box.pack_start (this.running_length_label_info, false, false, 0)
+ this.infobox.pack_start(box, true, true, 0)
+
+ this.vbox.pack_start (new Gtk.Label({label:""}), false, false, 24);
+ this.vbox.pack_start (this.cover, false, false, 0);
+ this.vbox.pack_start (this.title_label, false, false, 9);
+ this.vbox.pack_start (this.artist_label, false, false, 0);
+ this.vbox.pack_start (new Gtk.Label({label:""}), false, false, 6);
+ this.vbox.pack_start(this.infobox, false, false, 0)
+ this.box.pack_end (this.scrolledWindow, true, true, 0);
+
+ let hbox = new Gtk.Box()
+ hbox.pack_start (new Gtk.Label({label: ""}), true, true, 0)
+ hbox.pack_start (this.vbox, false, false, 0)
+ this.hbox.pack_start (hbox, true, true, 64);
+ this.hbox.pack_start (this.box, true, true, 6);
+
+ this.get_style_context ().add_class ("view");
+ this.get_style_context ().add_class ("content-view");
+ this.add(this.hbox)
+ this.show_all ()
+ },
+
+ update: function (artist, album, item) {
+ var pixbuf = albumArtCache.lookup (256, artist, item.get_string(Grl.METADATA_KEY_ALBUM));
+ let duration = 0;
+ for (let t in this.tracks_labels) {
+ this.songsList.remove(this.tracks_labels[t]);
+ }
+ this.tracks_labels = {};
+ grilo.getAlbumSongs(artist, album, Lang.bind(this, function (source, prefs, track) {
+ if (track != null) {
+ duration = duration + track.get_duration()
+ this.tracks_labels[track.get_title()] = new ClickableLabel (track);
+ this.songsList.pack_start(this.tracks_labels[track.get_title()], false, false, 0);
+ this.running_length_label_info.set_text(parseInt(duration/60) + " min")
+ }
+ }));
+
+ //update labels view
+ var i = 0 ;
+ for (let t in this.tracks_labels) {
+ let length = new Gtk.Label ({label : tracks[t]}); //use this._toTimeLength()
+ this.songsList.pack_start(this.tracks_labels[t], false, false, 0);
+ this.running_length = this.running_length+ parseInt(tracks[t], 10);
+ i++;
+ }
+
+ if (pixbuf == null) {
+ let path = "/usr/share/icons/gnome/scalable/places/folder-music-symbolic.svg";
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(path, -1, 256, true);
+ }
+ this.cover.set_from_pixbuf (pixbuf);
+
+ this.setArtistLabel(artist);
+ this.setTitleLabel(album);
+ },
+
+ setArtistLabel: function(artist) {
+ this.artist_label.set_markup("<b><span size='large' color='grey'>" + artist + "</span></b>");
+ },
+
+ setTitleLabel: function(title) {
+ this.title_label.set_markup("<b><span size='large'>" + title + "</span></b>");
+ },
+
+});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]