summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLarsAC <LarsAC@e10066b5-e1e2-0310-b819-94efdf66514b>2004-02-01 18:22:52 +0000
committerLarsAC <LarsAC@e10066b5-e1e2-0310-b819-94efdf66514b>2004-02-01 18:22:52 +0000
commit9ffff4626dbb713e2fa6e025868928393ebf2ae7 (patch)
treeebe95d53808375beaed9f2c4ea2d17e7e8cb01de
parent717ec9a2d6e43a61ccff152a8d887fdb8e8f303e (diff)
downloadvdr-plugin-muggle-9ffff4626dbb713e2fa6e025868928393ebf2ae7.tar.gz
vdr-plugin-muggle-9ffff4626dbb713e2fa6e025868928393ebf2ae7.tar.bz2
Initial revision
git-svn-id: https://vdr-muggle.svn.sourceforge.net/svnroot/vdr-muggle/trunk@3 e10066b5-e1e2-0310-b819-94efdf66514b
-rw-r--r--muggle-plugin/COPYING340
-rw-r--r--muggle-plugin/HISTORY6
-rw-r--r--muggle-plugin/Makefile91
-rw-r--r--muggle-plugin/README11
-rw-r--r--muggle-plugin/gd_content_interface.c943
-rw-r--r--muggle-plugin/gd_content_interface.h214
-rwxr-xr-xmuggle-plugin/mg_content_interface.c397
-rwxr-xr-xmuggle-plugin/mg_content_interface.h261
-rw-r--r--muggle-plugin/mg_database.c35
-rw-r--r--muggle-plugin/mg_database.h34
-rw-r--r--muggle-plugin/mg_media.c272
-rw-r--r--muggle-plugin/mg_media.h137
-rw-r--r--muggle-plugin/mg_tools.c140
-rw-r--r--muggle-plugin/mg_tools.h31
-rw-r--r--muggle-plugin/muggle.c98
-rw-r--r--muggle-plugin/muggle.h51
-rw-r--r--muggle-plugin/sh_console_osd.c212
-rw-r--r--muggle-plugin/sh_console_osd.h191
-rw-r--r--muggle-plugin/sh_console_osd_menuitems.c530
-rw-r--r--muggle-plugin/sh_console_osd_menuitems.h130
-rw-r--r--muggle-plugin/sh_dummy_content.c567
-rw-r--r--muggle-plugin/sh_dummy_content.h178
-rw-r--r--muggle-plugin/sh_muggle.c365
-rw-r--r--muggle-plugin/sh_muggle2.c66
-rw-r--r--muggle-plugin/sh_plugin.c77
-rw-r--r--muggle-plugin/sh_plugin.h46
-rw-r--r--muggle-plugin/vdr_menu.c523
-rw-r--r--muggle-plugin/vdr_menu.h87
28 files changed, 6033 insertions, 0 deletions
diff --git a/muggle-plugin/COPYING b/muggle-plugin/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/muggle-plugin/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/muggle-plugin/HISTORY b/muggle-plugin/HISTORY
new file mode 100644
index 0000000..0f92024
--- /dev/null
+++ b/muggle-plugin/HISTORY
@@ -0,0 +1,6 @@
+VDR Plugin 'muggle' Revision History
+------------------------------------
+
+2004-01-15: Version 0.0.1
+
+- Initial revision.
diff --git a/muggle-plugin/Makefile b/muggle-plugin/Makefile
new file mode 100644
index 0000000..8728cc3
--- /dev/null
+++ b/muggle-plugin/Makefile
@@ -0,0 +1,91 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id: Makefile,v 1.1 2004/02/01 18:22:53 LarsAC Exp $
+
+# The official name of this plugin.
+# This name will be used in the '-P...' option of VDR to load the plugin.
+# By default the main source file also carries this name.
+#
+PLUGIN = muggle
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g')
+
+### The C++ compiler and options:
+
+CXX ?= g++-3.3
+CXXFLAGS ?= -O2 -Wall -Woverloaded-virtual -Wno-deprecated -g
+
+### The directory environment:
+
+DVBDIR = ../../../../DVB
+VDRDIR = ../../..
+LIBDIR = ../../lib
+TMPDIR = /tmp
+
+### Allow user defined options to overwrite defaults:
+
+-include $(VDRDIR)/Make.config
+
+### The version number of VDR (taken from VDR's "config.h"):
+
+VDRVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### Includes and Defines (add further entries here):
+
+INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include -I/usr/include/mysql/
+
+DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).o vdr_menu.o mg_database.o mg_content_interface.o sh_dummy_content.o gd_content_interface.o mg_tools.o mg_media.o
+
+BINOBJS = sh_shell_osd_plugin.o sh_shell_osd.o sh_shell_osd_menuitems.o muggle.o vdr_menu.o content_interface.o dummy_content.o gd_contentinterface.o muggle_tools.o mgmedia.o
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+# Dependencies:
+
+MAKEDEP = g++ -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Targets:
+
+all: libvdr-$(PLUGIN).so
+
+libvdr-$(PLUGIN).so: $(OBJS)
+ $(CXX) $(CXXFLAGS) -shared $(OBJS) -lmysqlclient -o $@
+ @cp $@ $(LIBDIR)/$@.$(VDRVERSION)
+
+sh_muggle : sh_muggle.c $(BINOBJS)
+ $(CXX) $(CXXFLAGS) $(BINOBJS) sh_muggle.c -lmysqlclient -o $@
+
+sh_muggle2 : sh_muggle2.c $(BINOBJS)
+ $(CXX) $(CXXFLAGS) sh_muggle2.c $(BINOBJS) -lmysqlclient -o $@
+
+dist: clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+ @-rm -f $(OBJS) $(BINOBJS) $(DEPFILE) *.so *.tgz core* *~
+ @-rm -f sh_muggle sh_muggle2
diff --git a/muggle-plugin/README b/muggle-plugin/README
new file mode 100644
index 0000000..4e04b83
--- /dev/null
+++ b/muggle-plugin/README
@@ -0,0 +1,11 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Your Name <email@host.dom>
+
+Project's homepage: URL
+
+Latest version available at: URL
+
+See the file COPYING for license information.
+
+Description:
diff --git a/muggle-plugin/gd_content_interface.c b/muggle-plugin/gd_content_interface.c
new file mode 100644
index 0000000..5707f78
--- /dev/null
+++ b/muggle-plugin/gd_content_interface.c
@@ -0,0 +1,943 @@
+/*******************************************************************/
+/*! \file content_interface.cpp
+ * \brief Data Objects for content (e.g. mp3 files, movies)
+ * for the vdr muggle plugindatabase
+ ********************************************************************
+ * \version $Revision: 1.1 $
+ * \date $Date: 2004/02/01 18:22:53 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author file owner: $Author: LarsAC $
+ *
+ * DUMMY
+ * Implements main classes of for content items and interfaces to SQL databases
+ *
+ * This file implements the following classes
+ * - GdPlaylist a playlist
+ * - mgGdTrack a single track (content item). e.g. an mp3 file
+ * - mgSelection a set of tracks (e.g. a database subset matching certain criteria)
+ *
+ */
+/*******************************************************************/
+#define DEBUG
+
+#include "gd_content_interface.h"
+#include "mg_tools.h"
+
+using namespace std;
+
+#define GD_PLAYLIST_TYPE 0 // listtype for giant disc db
+
+// some dummies to keep the compiler happy
+#define DUMMY_CONDITION true // we use that as dummy condition to satisfy C++ syntax
+#define DUMMY
+
+// non-member function
+int GdInitDatabase(MYSQL *db)
+{
+ if(mysql_init(db) == NULL)
+ {
+ return -1;
+ }
+
+ if(mysql_real_connect(db,"localhost","root","",
+ "GiantDisc2",0,NULL,0) == NULL)
+ {
+ return -2;
+ }
+ return 0;
+}
+
+vector<string> GdGetStoredPlaylists(MYSQL db)
+{
+ vector<string> list;
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+
+ result=mgSqlReadQuery(&db, "SELECT title FROM playlist");
+
+ while((row = mysql_fetch_row(result)) != NULL)
+ {
+ list.push_back(row[0]);
+ }
+ return list;
+}
+
+/*******************************************************************/
+/* class mgTack */
+/********************************************************************/
+mgGdTrack mgGdTrack::UNDEFINED = mgGdTrack();
+
+/*!
+ *****************************************************************************
+ * \brief Constructor: creates a mgGdTrack obect
+ *
+ * \param sqlIdentifier identifies a unique row in the track database
+ * \param dbase database which stores the track table
+ *
+ * On creation, the object contains only the idea. The actual data fields
+ * are filled when readData() is called for the first time.
+ ****************************************************************************/
+mgGdTrack::mgGdTrack(int sqlIdentifier, MYSQL dbase)
+{
+ m_uniqID = sqlIdentifier;
+ m_db = dbase;
+ m_retrieved = false;
+
+}
+
+/*!
+ *****************************************************************************
+ * \brief copy constructor
+ *
+ ****************************************************************************/
+mgGdTrack::mgGdTrack(const mgGdTrack& org)
+{
+ m_uniqID = org.m_uniqID;
+ m_db = org.m_db;
+ m_retrieved = org.m_retrieved;
+ if(m_retrieved)
+ {
+ m_artist = org.m_artist;
+ m_title = org.m_title;
+ m_mp3file = org.m_mp3file;
+ m_album = org.m_album;
+ m_genre = org.m_genre;
+ m_year = org.m_year;
+ m_rating = org.m_rating;
+ }
+
+}
+
+
+/*!
+ *****************************************************************************
+ * \brief destructor
+ *
+ ****************************************************************************/
+mgGdTrack::~mgGdTrack()
+{
+ // nothing to be done
+}
+
+/*!
+ *****************************************************************************
+ * \brief accesses the database to fill the actual data fields
+ *
+ * In order to avoid innecessary queries to the database, the content fields
+ * of the mgGdTrack object may not be filled upon creation.
+ * As soon as the first content field is needed, this private function
+ * is called to fill all content fields at once
+ ****************************************************************************/
+bool mgGdTrack::readData()
+{
+ MYSQL_RES *result;
+ int nrows;
+ int nfields;
+
+ // note: this does not work with empty album or genre fields
+ result = mgSqlReadQuery(&m_db,
+ "SELECT tracks.artist, album.title, tracks.title, "
+ " tracks.mp3file, genre.genre, tracks.year, "
+ " tracks.rating, tracks.length "
+ "FROM tracks, album, genre "
+ "WHERE tracks.id=%d "
+ "AND album.cddbid=tracks.sourceid AND "
+ " genre.id=tracks.genre1",
+ m_uniqID);
+
+ nrows = mysql_num_rows(result);
+ nfields = mysql_num_fields(result);
+ if(nrows == 0)
+ {
+ mgWarning("No entries found \n");
+ return false;
+ }
+ else
+ {
+ if (nrows >1 )
+ {
+ mgWarning("More than one entry found");
+ }
+ MYSQL_ROW row;
+
+ row = mysql_fetch_row(result);
+ m_artist = row[0];
+ m_album = row[1];
+ m_title = row [2];
+ m_mp3file = row [3];
+ m_genre = row [4];
+ if(sscanf(row [5], "%d", &m_year) !=1)
+ {
+ mgError("Invalid year '%s' in database", row [5]);
+ }
+ if(sscanf(row [6], "%d", &m_rating) !=1)
+ {
+ mgError("Invalid rating '%s' in database", row [6]);
+ }
+ if(sscanf(row [7], "%d", &m_length) !=1)
+ {
+ mgError("Invalid duration '%s' in database", row [7]);
+ }
+ }
+ m_retrieved = true;
+ return true;
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns value for _mp3file
+ *
+ * If value has not been retrieved from the database, radData() is called first
+ ****************************************************************************/
+string mgGdTrack::getSourceFile()
+{
+ if(!m_retrieved)
+ {
+ readData();
+ }
+ return m_mp3file;
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns value for m_title
+ *
+ * If value has not been retrieved from the database, radData() is called first
+ ****************************************************************************/
+string mgGdTrack::getTitle()
+{
+ if(!m_retrieved)
+ {
+ readData();
+ }
+ return m_title;
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns value for m_artist
+ *
+ * If value has not been retrieved from the database, radData() is called first
+ ****************************************************************************/
+string mgGdTrack::getArtist()
+{
+ if(!m_retrieved)
+ {
+ readData();
+ }
+ return m_artist;
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns a string for one field of the item
+ *
+ * This is a generic function that shozld work for all content items
+ ****************************************************************************/
+string mgGdTrack::getLabel(int col)
+{
+ if(!m_retrieved)
+ {
+ readData();
+ }
+ switch(col)
+ {
+ case 0:
+ return m_title;
+ case 1:
+ return m_artist;
+ case 2:
+ return m_album;
+ case 3:
+ return m_genre;
+ default:
+ return "";
+ }
+}
+
+string mgGdTrack::getDescription()
+{
+ if(!m_retrieved)
+ {
+ readData();
+ }
+ return m_artist + " - " + m_title;
+
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns value for m_album
+ *
+ * If value has not been retrieved from the database, radData() is called first
+ ****************************************************************************/
+string mgGdTrack::getAlbum()
+{
+ if(!m_retrieved)
+ {
+ readData();
+ }
+ return m_album;
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns value for m_genre
+ *
+ * If value has not been retrieved from the database, radData() is called first
+ ****************************************************************************/
+string mgGdTrack::getGenre()
+{
+ if(!m_retrieved)
+ {
+ readData();
+ }
+ return m_genre;
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns value for m_year
+ *
+ * If value has not been retrieved from the database, radData() is called first
+ ****************************************************************************/
+int mgGdTrack::getYear()
+{
+ if(!m_retrieved)
+ {
+ readData();
+ }
+ return m_year;
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns value for m_rating
+ *
+ * If value has not been retrieved from the database, radData() is called first
+ ****************************************************************************/
+int mgGdTrack::getRating()
+{
+ if(!m_retrieved)
+ {
+ readData();
+ }
+ return m_rating;
+}
+
+int mgGdTrack::getDuration()
+{
+ if(!m_retrieved)
+ {
+ readData();
+ }
+ return m_rating;
+}
+string mgGdTrack::getImageFile()
+{
+ return "dummyImg.jpg";
+}
+
+/*!
+ *****************************************************************************
+ * \brief sets the field for m_title to the specified value
+ *
+ * Note: The new value is not stored in the database.
+ * This is only done, when writeData() is called
+ ****************************************************************************/
+void mgGdTrack::setTitle(string new_title)
+{
+ m_title = new_title;
+}
+
+/*!
+ *****************************************************************************
+ * \brief sets the field for m_artist to the specified value
+ *
+ * Note: The new value is not stored in the database.
+ * This is only done, when writeData() is called
+ ****************************************************************************/
+void mgGdTrack::setArtist(string new_artist)
+{
+ m_artist = new_artist;
+}
+
+
+/*!
+ *****************************************************************************
+ * \brief sets the field for m_album to the specified value
+ *
+ * Note: The new value is not stored in the database.
+ * This is only done, when writeData() is called
+ ****************************************************************************/
+void mgGdTrack::setAlbum(string new_album)
+{
+ m_album = new_album;
+}
+
+
+/*!
+ *****************************************************************************
+ * \brief sets the field for m_genre to the specified value
+ *
+ * Note: The new value is not stored in the database.
+ * This is only done, when writeData() is called
+ ****************************************************************************/
+void mgGdTrack::setGenre(string new_genre)
+{
+ m_genre = new_genre;
+}
+
+
+/*!
+ *****************************************************************************
+ * \brief sets the field for m_year to the specified value
+ *
+ * Note: The new value is not stored in the database.
+ * This is only done, when writeData() is called
+ ****************************************************************************/
+void mgGdTrack::setYear(int new_year)
+{
+ m_year = new_year;
+}
+
+
+/*!
+ *****************************************************************************
+ * \brief sets the field for m_rating to the specified value
+ *
+ * Note: The new value is not stored in the database.
+ * This is only done, when writeData() is called
+ ****************************************************************************/
+void mgGdTrack::setRating(int new_rating)
+{
+ m_rating = new_rating;
+}
+
+
+/*!
+ *****************************************************************************
+ * \brief stores current values in the sql database
+ *
+ * Note: At the moment, only the values stored directly in the 'tracks'
+ * database are updated
+ ****************************************************************************/
+bool mgGdTrack::writeData()
+{
+ mgSqlWriteQuery(&m_db, "UPDATE tracks "
+ "SET artist=\"%s\", title=\"%s\", year=%d, rating=%d "
+ "WHERE id=%d",
+ m_artist.c_str(), m_title.c_str(),
+ m_year, m_rating, m_uniqID);
+ return true;
+}
+
+GdTracklist::GdTracklist(MYSQL db_handle, string restrictions)
+{
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+ int trackid;
+
+ result = mgSqlReadQuery(&db_handle,
+ "SELECT tracks.id "
+ " FROM tracks, album, genre WHERE %s"
+ " AND album.cddbid=tracks.sourceid "
+ " AND genre.id=tracks.genre1",
+ restrictions.c_str());
+ while((row = mysql_fetch_row(result)) != NULL)
+ {
+ // row[0] is the trackid
+ if(sscanf(row[0], "%d", &trackid) != 1)
+ {
+ mgError("Can not extract integer track id from '%s'",
+ row[0]);
+ }
+ m_list.push_back(new mgGdTrack(trackid, db_handle));
+ }
+
+}
+
+
+/*******************************************************************/
+/* class GdPlaylist */
+/********************************************************************/
+
+/*!
+ *****************************************************************************
+ * \brief Constructor: opens playlist by name
+ *
+ * \param listname user-readable identifier of the paylist
+ * \param db_handl database which stores the playlist
+ *
+ * If the playlist does not yet exist, an empty playlist is created
+ ****************************************************************************/
+GdPlaylist::GdPlaylist(string listname, MYSQL db_handle)
+{
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+ int nrows;
+
+ m_db = db_handle;
+
+ //
+ // check, if the database really exists
+ //
+ result=mgSqlReadQuery(&m_db,
+ "SELECT id,author FROM playlist where title=\"%s\"",
+ listname.c_str());
+ nrows = mysql_num_rows(result);
+ if(nrows == 0)
+ {
+ mgDebug(3, "No playlist with name %s found. Creating new playlist\n",
+ listname.c_str());
+
+ // create new database entry
+ mgSqlWriteQuery(&m_db, "INSERT into playlist "
+ "SET title=\"%s\", author=\"%s\"",
+ listname.c_str(),
+ "VDR", // default author
+ ""); // creates current time as timestamp
+ m_author = "VDR";
+ m_listname = listname;
+ }
+ else // playlist exists, read data
+ {
+ row = mysql_fetch_row(result);
+
+ if(sscanf(row [0], "%d", & m_sqlId) !=1)
+ {
+ mgError("Invalid id '%s' in database", row [5]);
+ }
+ m_author = row[1];
+ m_listname = listname;
+ // now read allentries of the playlist and
+ // write them into the tracklist
+ insertDataFromSQL();
+ }// end 'else (playlist exists)
+
+ m_listtype = GD_PLAYLIST_TYPE; // GiantDB list type for playlists
+}
+
+/*!
+ *****************************************************************************
+ * \brief Constructor: construct playlist object from existing sql playlist
+ *
+ * \param sql_identifier: sql internal identifier for the playlist
+ * \param db_handl database which stores the playlist
+ *
+ * This constructor is typically used when a playlist is selected from an
+ * internal list of playlists
+ ****************************************************************************/
+GdPlaylist::GdPlaylist(unsigned int sql_identifier, MYSQL db_handle)
+{
+ MYSQL_RES *result;
+ int nrows;
+
+ m_db = db_handle;
+
+ // check, if the database really exists
+ result = mgSqlReadQuery(&m_db,
+ "SELECT title,author FROM playlist where id=%d",
+ sql_identifier);
+ nrows = mysql_num_rows(result);
+ if(nrows == 0)
+ {
+ mgDebug(3, "No playlist with id %d found. Creating new playlist\n",
+ sql_identifier);
+
+ // create new database entry
+ // DUMMY
+ }
+ else // playlist exists, read data
+ {
+ MYSQL_ROW row;
+ row = mysql_fetch_row(result);
+
+ m_listname = row[0];
+ m_author = row[1];
+ m_sqlId = sql_identifier;
+ // now read allentries of the playlist and
+ // write them into the tracklist
+ insertDataFromSQL();
+ }
+ m_listtype = GD_PLAYLIST_TYPE; // GiantDB list type for playlists
+}
+
+/*!
+ *****************************************************************************
+ * \brief empty destructor
+ *
+ * Nothing to be done. Constructor of parent class takes care
+ ****************************************************************************/
+GdPlaylist::~GdPlaylist()
+{
+
+}
+
+/*!
+ *****************************************************************************
+ * \brief reads the track list from the sql database into a locallist
+ ****************************************************************************/
+int GdPlaylist::insertDataFromSQL()
+{
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+ mgGdTrack* trackptr;
+ int id;
+ int nrows;
+
+ result = mgSqlReadQuery(&m_db,
+ "SELECT tracknumber, trackid FROM playlistitem "
+ "WHERE playlist = %d ORDER BY tracknumber",
+ m_sqlId);
+ nrows = mysql_num_rows(result);
+ while((row = mysql_fetch_row(result)) != NULL)
+ {
+ // add antry to tracklist
+ if(sscanf(row[1], "%d", &id) !=1)
+ {
+ mgWarning("Track id '%s' is not an integer. Ignoring \n", row[1]);
+ }
+ else
+ {
+ trackptr = new mgGdTrack(id, m_db);
+ m_list.push_back(trackptr);
+ }
+ }
+ return nrows;
+}
+bool GdPlaylist::storePlaylist()
+{
+ vector<mgContentItem*>::iterator iter;
+ int num;
+
+
+ if(m_listname =="")
+ {
+ mgWarning("Can not store Tracklist without name");
+ return false;
+ }
+ // remove old playlist items from db
+ mgSqlWriteQuery(&m_db,
+ "DELETE FROM playlistitem WHERE playlist = %d",
+ m_sqlId);
+
+ // add new playlist items to db
+
+ for(iter= m_list.begin(), num=0; iter != m_list.end(); iter++, num++)
+ {
+
+ mgSqlWriteQuery(&m_db,
+ "INSERT into playlistitem "
+ "SET tracknumber=\"%s\", trackid=\"%s\", playlist=%d",
+ num, (*iter)->getId(), m_sqlId);
+ }
+ return true;
+}
+/*!
+ *****************************************************************************
+ * \brief returns the total duration of all songs in the list in seconds
+ *
+ ****************************************************************************/
+int GdPlaylist::getPlayTime()
+{
+ //DUMMY
+ // go over all entries in the playlist and accumulate their playtime
+
+ return 0;
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns the duration of all remaining songs in the list in seconds
+ *
+ ****************************************************************************/
+int GdPlaylist::getPlayTimeRemaining()
+{
+ //DUMMY
+ // go over all remaining entries in the playlist and accumulate their
+ // playtime
+ // The remaining playtime of the current song is only known by the mplayer
+ return 0; // dummy
+}
+
+/*******************************************************************/
+/* class GdTreeNode */
+/*******************************************************************/
+
+/*!
+ *****************************************************************************
+ * \brief constructor
+ *
+ ****************************************************************************/
+GdTreeNode::GdTreeNode(MYSQL db, int view, string filters)
+ : mgSelectionTreeNode(db, view)
+{
+ // create a root node
+ // everything is done in the parent class
+ m_restriction = filters;
+ m_view = view;
+}
+GdTreeNode::GdTreeNode(mgSelectionTreeNode* parent,
+ string id, string label, string restriction)
+ : mgSelectionTreeNode(parent, id, label)
+{
+ m_restriction = restriction;
+ // everything else is done in the parent class
+}
+
+/*!
+ *****************************************************************************
+ * \brief destructor
+ *
+ ****************************************************************************/
+GdTreeNode::~GdTreeNode()
+{
+ // _children.clear();
+}
+
+
+
+/*!
+ *****************************************************************************
+ * \brief compute children on the fly
+ *
+ * \return: true, if the node could be expanded (or was already), false,of
+ * node can not be expanded any further
+ *
+ * retrieves all entries for the next level that satisfy the restriction of
+ * the current level and create a child-arc for each distinct entry
+ *
+ ****************************************************************************/
+bool GdTreeNode::expand()
+{
+
+ MYSQL_ROW row;
+ MYSQL_RES *result;
+ int nrows;
+ int nfields;
+ char sqlbuff[1024]; /* hope it's big enough ! */
+ char idbuf[255];
+ int numchild;
+
+ string labelfield; // human readable db field for the column to be expanded
+ string idfield; // unique id field for the column to be expanded
+ string tables; // stores the db tables used
+ string new_restriction_field; // field to be restricted by the new level
+ string new_restriction; // complete restriction str for the current child
+ string new_label;
+ GdTreeNode* new_child;
+
+ if (m_expanded)
+ {
+ mgWarning("Node already expanded\n");
+ return true;
+ }
+ if (m_level == 1)
+ {
+ m_view = atoi(m_id.c_str());
+ }
+ mgDebug(5, "Expanding level %d view %d\n", m_level,m_view);
+ if (m_level > 0)
+ {
+ switch(m_view)
+ {
+ case 1: // artist -> album -> title
+ if(m_level == 1) {
+ sprintf(sqlbuff,
+ "SELECT DISTINCT album.artist,album.artist"
+ " FROM album"
+ " WHERE %s"
+ " ORDER BY album.artist"
+ , m_restriction.c_str() );
+ idfield = "album.artist";
+ } else if(m_level == 2) { // artist -> album
+ sprintf(sqlbuff,
+ "SELECT DISTINCT album.title,album.cddbid"
+ " FROM album"
+ " WHERE %s"
+ " ORDER BY album.title"
+ , m_restriction.c_str() );
+ idfield = "album.cddbid";
+ } else if(m_level == 3) { // album -> title
+ sprintf(sqlbuff,
+ "SELECT tracks.title,tracks.id"
+ " FROM tracks,album"
+ " WHERE %s AND tracks.sourceid=album.cddbid"
+ " ORDER BY tracks.tracknb"
+ , m_restriction.c_str() );
+ idfield = "tracks.id";
+ } else {
+ mgWarning("View #%d level %d' not yet implemented", m_view, m_level);
+ m_expanded = false;
+ return false;
+ }
+ break;
+ case 2: // genre -> artist -> album -> track
+ if(m_level == 1) { // genre
+ sprintf(sqlbuff,
+ "SELECT DISTINCT genre.genre,tracks.genre1"
+ " FROM tracks,genre"
+ " WHERE (genre.id=tracks.genre1) AND"
+ " %s"
+ " ORDER BY genre.id"
+ , m_restriction.c_str());
+ idfield = "tracks.genre1";
+ } else if(m_level == 2) { // genre -> artist
+ sprintf(sqlbuff,
+ "SELECT DISTINCT album.artist,album.artist"
+ " FROM tracks,genre,album"
+ " WHERE (genre.id=tracks.genre1) AND"
+ " (album.cddbid=tracks.sourceid) AND"
+ " %s"
+ " ORDER BY album.artist",
+ m_restriction.c_str());
+ idfield = "album.artist";
+ } else if(m_level == 3) { // genre -> artist -> album
+ sprintf(sqlbuff,
+ "SELECT DISTINCT album.title,tracks.sourceid"
+ " FROM tracks,genre,album"
+ " WHERE (genre.id=tracks.genre1) AND"
+ " (album.cddbid=tracks.sourceid) AND"
+ " %s"
+ " ORDER BY album.title"
+ , m_restriction.c_str());
+ idfield = "tracks.sourceid";
+ } else if(m_level == 4) { // genre -> artist -> album -> track
+ sprintf(sqlbuff,
+ "SELECT DISTINCT tracks.title, tracks.id"
+ " FROM tracks,genre,album"
+ " WHERE (genre.id=tracks.genre1) AND"
+ " (album.cddbid=tracks.sourceid) AND"
+ " %s"
+ " ORDER BY tracks.tracknb"
+ , m_restriction.c_str());
+ idfield = "tracks.id";
+ } else {
+ mgWarning("View #%d level %d' not yet implemented", m_view, m_level);
+ m_expanded = false;
+ return false;
+ }
+ break;
+ case 3: // Artist -> Track
+ if(m_level ==1)
+ {
+ sprintf(sqlbuff,
+ "SELECT DISTINCT tracks.artist,tracks.artist"
+ " FROM tracks"
+ " WHERE "
+ " %s"
+ " ORDER BY tracks.artist"
+ , m_restriction.c_str());
+ idfield = "tracks.artist";
+ } else if (m_level == 2) { // Track
+ sprintf(sqlbuff,
+ "SELECT DISTINCT tracks.title,tracks.id"
+ " FROM tracks, album"
+ " WHERE"
+ " %s AND tracks.sourceid=album.cddbid"
+ " ORDER BY tracks.title"
+ , m_restriction.c_str());
+ idfield = "tracks.id";
+ } else {
+ mgWarning("View #%d level %d' not yet implemented", m_view, m_level);
+ m_expanded = false;
+ return false;
+ }
+ break;
+ default:
+ mgError("View '%d' not yet implemented", m_view);
+ }
+
+ // now get all childrean ofthe current node fromthe database
+ result = mgSqlReadQuery(&m_db, sqlbuff);
+ nrows = mysql_num_rows(result);
+ nfields = mysql_num_fields(result);
+
+ numchild=1;
+ while((row = mysql_fetch_row(result)) != NULL)
+ {
+ // row[0] is the printable label for the new child
+ // row[1] is the unique id for the new child
+ sprintf(idbuf, "%s_%03d", m_id.c_str(), numchild);
+
+ new_restriction = m_restriction + " AND "
+ + idfield + "= '" + row[1] + "'";
+
+ new_child = new GdTreeNode(this, // parent
+ (string) idbuf, // id
+ row[0], // label,
+ new_restriction);
+ m_children.push_back(new_child);
+ numchild++;
+ }
+ } else {
+ new_child = new GdTreeNode(this, // parent
+ "1" , // id
+ "Artist -> Album -> Title", // label,
+ "1");
+ m_children.push_back(new_child);
+ new_child = new GdTreeNode(this, // parent
+ "2" , // id
+ "Genre -> Artist -> Album -> Track" , // label,
+ "1");
+ m_children.push_back(new_child);
+ new_child = new GdTreeNode(this, // parent
+ "3" , // id
+ "Artist -> Track" , // label,
+ "1");
+ m_children.push_back(new_child);
+ }
+
+ m_expanded = true;
+ mgDebug(5, "%d children expanded\n", m_children.size());
+ return true;
+}
+
+/*!
+ *****************************************************************************
+ * \brief go over all children recursively to find the tracks
+ *
+ ****************************************************************************/
+vector<mgContentItem*>* GdTreeNode::getTracks()
+{
+ MYSQL_ROW row;
+ MYSQL_RES *result;
+ int nrows;
+ int nfields;
+ vector<mgContentItem*>* retlist;
+ int trackid;
+
+ retlist = new vector<mgContentItem*>();
+
+ // get all tracks satisying the restrictions of this node
+ mgDebug(5, "getTracks(): query '%s'", m_restriction.c_str());
+
+ result = mgSqlReadQuery(&m_db,
+ "SELECT tracks.id FROM tracks, album, genre WHERE %s"
+ " AND album.cddbid=tracks.sourceid AND genre.id=tracks.genre1",
+ m_restriction.c_str());
+ nrows = mysql_num_rows(result);
+ nfields = mysql_num_fields(result);
+
+ while((row = mysql_fetch_row(result)) != NULL)
+ {
+ // row[0] is the trackid
+ if(sscanf(row[0], "%d", &trackid) != 1)
+ {
+ mgError("Can not extract integer track id from '%s'",
+ row[0]);
+ }
+ retlist->push_back(new mgGdTrack(trackid, m_db));
+ }
+ return retlist;
+}
+
+
+
+
+
+
+
+
+
diff --git a/muggle-plugin/gd_content_interface.h b/muggle-plugin/gd_content_interface.h
new file mode 100644
index 0000000..8e63956
--- /dev/null
+++ b/muggle-plugin/gd_content_interface.h
@@ -0,0 +1,214 @@
+
+/*******************************************************************/
+/*! \file gd_content_interface.h
+ * \brief Data Objects for content (e.g. mp3 files, movies)
+ * for the vdr muggle plugindatabase
+ ********************************************************************
+ * \version $Revision: 1.1 $
+ * \date $Date: 2004/02/01 18:22:53 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author file owner: $Author: LarsAC $
+ *
+ * Declares main classes of for content items and interfaces to SQL databases
+ *
+ * This file defines the following classes
+ * - mgPlaylist a playlist
+ * - mgGdTrack a single track (content item). e.g. an mp3 file
+ * - mgSelection a set of tracks (e.g. a database subset matching certain criteria)
+ *
+ */
+/*******************************************************************/
+
+/* makes sure we dont use parse the same declarations twice */
+#ifndef _GD_CONTENT_INTERFACE_H
+#define _GD_CONTENT_INTERFACE_H
+
+#include <string>
+#include <vector>
+
+#include <mysql/mysql.h>
+
+#include "mg_content_interface.h"
+
+// non-member function
+int GdInitDatabase(MYSQL *db);
+std::vector<std::string> GdGetStoredPlaylists(MYSQL db);
+
+/*!
+ *******************************************************************
+ * \class mgGdTrack
+ *
+ * \brief represents a a single track
+ * This may be any content item. e.g. a mp3 fileselection
+ *
+ * The object is initially created with a database identifier.
+ * The actual data is only read when a content field is accessed for
+ * The first time
+ ********************************************************************/
+class mgGdTrack : public mgContentItem
+{
+
+private:
+ MYSQL m_db;
+ bool m_retrieved; // false if content field values have not yet been
+ // retrieved from database. This is done on demand
+
+ // content fields
+ std::string m_artist;
+ std::string m_title;
+ std::string m_mp3file;
+ std::string m_album;
+ std::string m_genre;
+ int m_year;
+ int m_rating;
+ int m_length;
+
+ bool readData();
+
+ public:
+
+
+ /* constructor */
+ mgGdTrack(){ m_uniqID = -1;} // creates invalid item
+ mgGdTrack(int sqlIdentifier, MYSQL dbase);
+ mgGdTrack(const mgGdTrack&);
+
+ /* destructor */
+ virtual ~mgGdTrack();
+
+ virtual mgContentItem::contentType getContentType(){return mgContentItem::GD_AUDIO;}
+ virtual mgMediaPlayer getPlayer()
+ {
+ return mgMediaPlayer();
+ }
+
+ /* data acess */
+ //virtual functions of the base class
+ virtual std::string getSourceFile();
+ virtual std::string getTitle();
+ virtual std::string getLabel(int col);
+ virtual std::string getDescription();
+ virtual std::string getGenre();
+ virtual int getRating();
+
+ // additional class-specific functions
+ std::string getArtist();
+ std::string getAlbum();
+ int getYear();
+ int getDuration();
+ std::string getImageFile();
+
+ /* data manipulation */
+ void setTitle(std::string new_title);
+ void setArtist(std::string new_artist);
+ void setAlbum(std::string new_album);
+ void setGenre(std::string new_genre);
+ void setYear(int new_rating);
+ void setRating(int new_rating);
+
+ bool writeData();
+ static mgGdTrack UNDEFINED;
+
+};
+
+class GdTracklist : public mgTracklist
+{
+ public:
+ GdTracklist(MYSQL db_handle, std::string restrictions);
+};
+
+/*!
+ *******************************************************************
+ * \class GdPlaylist
+ *
+ * \brief represents a playlist, i.e. an ordered collection of tracks
+ ********************************************************************/
+class GdPlaylist : public mgPlaylist
+{
+ private:
+ int m_sqlId; /* -1 means: not valid */
+ int m_listtype; // used in GiantDisc db queries
+ std::string m_author;
+ MYSQL m_db;
+
+ private:
+ int insertDataFromSQL();
+
+ public:
+
+
+ /*==== constructors ====*/
+ GdPlaylist(std::string listname, MYSQL db_handle);
+ /* opens existing or creates empty playlist */
+
+ GdPlaylist(unsigned int sql_identifier, MYSQL db_handle);
+ /* construct from the db by internal id*/
+
+
+ /*==== destructor ====*/
+ virtual ~GdPlaylist();
+
+
+ int getPlayTime();
+ /* returns the total duration of all songs in the list in seconds */
+
+ int getPlayTimeRemaining();
+ /* returns the duration of all remaining songs in the list in seconds */
+
+
+ bool storePlaylist();
+};
+/*!
+ *******************************************************************
+ * \class mgSelectionTreeNode
+ *
+ * \brief hierarchical representation of a set of tracks
+ * The selection can be based on the whole database or a subset of it.
+ * Within this selection, the data is organized in a tree hierarchy
+ * The levels hof the hierarchy can be expanded dynamically by specifying
+ * the database field for the next expansion step
+ * In this way, the expnasion scheme (order of the fields) is not static.
+ * When a node is expanded, a list of children is created.
+ * Each child inherits the restrictions of its father and an additional
+ * restriction on the recently expanded db field
+ ********************************************************************/
+class GdTreeNode : public mgSelectionTreeNode{
+
+private:
+ // everything is in the base class
+public:
+
+ /*==== constructors ====*/
+ GdTreeNode(MYSQL db, int view, std::string filters); // top level
+ GdTreeNode(mgSelectionTreeNode* parent,
+ std::string id, std::string label, std::string restriction);
+
+ /*==== destructor ====*/
+ virtual ~GdTreeNode();
+
+ // compute children on the fly
+ virtual bool expand();
+
+ // access data in current node
+ virtual std::vector<mgContentItem*>* getTracks();
+};
+
+#endif /* END _GD_CONTENT_INTERFACE_H */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/muggle-plugin/mg_content_interface.c b/muggle-plugin/mg_content_interface.c
new file mode 100755
index 0000000..5015cf5
--- /dev/null
+++ b/muggle-plugin/mg_content_interface.c
@@ -0,0 +1,397 @@
+/*******************************************************************/
+/*! \file content_interface.c
+ * \brief Data Objects for content (e.g. mp3 files, movies)
+ * for the vdr muggle plugindatabase
+ ********************************************************************
+ * \version $Revision: 1.1 $
+ * \date $Date: 2004/02/01 18:22:53 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author file owner: $Author: LarsAC $
+ *
+ * DUMMY
+ * Implements main classes of for content items and interfaces to SQL databases
+ *
+ * This file implements the following classes
+ * - GdTracklist a playlist
+ * - mgGdTrack a single track (content item). e.g. an mp3 file
+ * - mgSelection a set of tracks (e.g. a database subset matching certain criteria)
+ *
+ */
+/*******************************************************************/
+#define DEBUG
+
+#include "mg_content_interface.h"
+#include "mg_tools.h"
+
+#define DUMMY
+
+/* constructor */
+mgContentItem mgContentItem::UNDEFINED = mgContentItem();
+
+using namespace std;
+
+/*!
+ *****************************************************************************
+ * \brief construcor
+ *
+ * creates empty tracklist
+ ****************************************************************************/
+mgTracklist::mgTracklist()
+{
+
+}
+
+/*!
+ *****************************************************************************
+ * \brief destrucor
+ *
+ * Deletes all items in the tracklist and removes the list itself
+ ****************************************************************************/
+mgTracklist::~mgTracklist()
+{
+ mgContentItem* ptr;
+ vector<mgContentItem*>::iterator iter;
+
+ for(iter = m_list.begin(); iter != m_list.end();iter++)
+ {
+ ptr = *iter;
+ delete ptr;
+ }
+ m_list.clear();
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns a pointer to the list of elements
+ *
+ ****************************************************************************/
+vector<mgContentItem*> *mgTracklist::getAll()
+{
+ return &m_list;
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns the number of elements in the list
+ *
+ ****************************************************************************/
+unsigned int mgTracklist::getNumItems()
+{
+ return m_list.size();
+}
+
+/*!
+ *****************************************************************************
+ * \brief randomizes the order of the elements in the list
+ *
+ ****************************************************************************/
+void mgTracklist::shuffle()
+{
+ random_shuffle(m_list.begin(),m_list.end ());
+}
+
+/*!
+ *****************************************************************************
+ * \brief sorts the elements in the list by the nth column
+ *
+ ****************************************************************************/
+void mgTracklist::sortBy(int col, bool direction)
+{
+}
+
+/*!
+ *****************************************************************************
+ * \brief stores the ids of columns to be used in label creation
+ *
+ * The list can create a label with different fields (columns) using the
+ * function getLabel()
+ * This function defines the fields of the contentItems to be used
+ * in the label and their order
+ ****************************************************************************/
+void mgTracklist::setDisplayColumns(vector<int> cols)
+{
+
+ m_columns = cols;
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns the number of dsplay coulmns
+ *
+ ****************************************************************************/
+unsigned int mgTracklist::getNumColumns()
+{
+ return m_columns.size();
+}
+
+
+/*!
+ *****************************************************************************
+ * \brief creates the label string for an item
+ *
+ * The list can create a label with different fields (columns).
+ * The fields used in the list and their order is set by the function
+ using the function setDisplayColumns
+ * function getLabel().
+ * This function creates a string from these columns, separated by the string
+ * 'separator'
+ * in the label and their order
+ ****************************************************************************/
+string mgTracklist::getLabel(unsigned int position, const string separator)
+{
+ string label ="";
+ mgContentItem* item;
+
+ if(position >= m_list.size())
+ return "";
+
+ else
+ {
+ item = *(m_list.begin()+position);
+ }
+
+
+ for( vector<int>::iterator iter = m_columns.begin();
+ iter != m_columns.end(); iter++ )
+ {
+ if( iter != m_columns.begin() )
+ {
+ label += separator;
+ }
+ label += item->getLabel(*iter);
+ }
+ return label;
+}
+
+
+/*!
+ *****************************************************************************
+ * \brief returns an item from the list at the specified position
+ *
+ ****************************************************************************/
+mgContentItem* mgTracklist::getItem(unsigned int position)
+{
+ if(position >= m_list.size())
+ return &(mgContentItem::UNDEFINED); //invalid
+ return *(m_list.begin()+position);
+}
+
+/*!
+ *****************************************************************************
+ * \brief remove item at position
+ *
+ ****************************************************************************/
+bool mgTracklist::remove(unsigned int position)
+{
+ if(position >= m_list.size()) return false;
+ vector<mgContentItem*>::iterator iter;
+
+ iter = m_list.begin()+ position;
+ m_list.erase(iter);
+ return true;
+}
+
+/*!
+ *****************************************************************************
+ * \brief remove all occurences of item
+ *
+ ****************************************************************************/
+int mgTracklist::remove(mgContentItem* item)
+{
+ int retval = 0;
+ vector<mgContentItem*>::iterator iter;
+ for(iter=m_list.begin(); iter != m_list.end (); iter++)
+ {
+ if(*iter == item)
+ {
+ m_list.erase(iter);
+ retval++;
+ break;
+ }
+ }
+ return retval;
+}
+
+
+/*=================================================================*/
+/* */
+/* class mgPlaylist */
+/* */
+/*=================================================================*/
+mgPlaylist::mgPlaylist()
+{
+}
+mgPlaylist::mgPlaylist(string listname)
+{
+ m_listname = listname;
+}
+
+ /*==== destructor ====*/
+mgPlaylist::~mgPlaylist()
+{
+
+}
+/*==== add/ remove tracks ====*/
+
+/* adds a song at the end of the playlist */
+void mgPlaylist::append(mgContentItem* item)
+{
+ m_list.push_back(item);
+}
+
+void mgPlaylist::appendList(vector<mgContentItem*> *tracks)
+{
+ vector<mgContentItem*>::iterator iter;
+ mgDebug(3, "Adding %d tracks to the playlist",tracks->size());
+ for(iter = tracks->begin(); iter != tracks->end(); iter++)
+ {
+ m_list.push_back(*iter);
+ }
+ tracks->clear();
+}
+
+
+/* adds a song after 'position' */
+void mgPlaylist::insert(mgContentItem* item, unsigned int position)
+{
+ if(position >= m_list.size())
+ m_list.push_back(item);
+ else
+ m_list.insert(m_list.begin()+position, item);
+}
+
+
+
+/*==== access tracks ====*/
+string mgPlaylist::getListname() { return m_listname; }
+void mgPlaylist::setListname(string name){ m_listname = name;}
+
+
+
+// returns the first item of the list
+mgContentItem* mgPlaylist::getFirst()
+{
+ m_current = m_list.begin();
+ return *m_current;
+}
+
+// returns the nth track from the playlist
+mgContentItem* mgPlaylist::getPosition(unsigned int position)
+{
+ if(position >= m_list.size())
+ return &(mgContentItem::UNDEFINED); //invalid
+ m_current = m_list.begin()+position;
+ return *m_current;
+}
+
+// proceeds to the next item
+mgContentItem* mgPlaylist::skipFwd()
+{
+ if(m_current+1 == m_list.end())
+ return &(mgContentItem::UNDEFINED); //invalid
+ else
+ return * (++m_current);
+}
+
+// goes back to the previous item
+mgContentItem* mgPlaylist::skipBack()
+{
+ if(m_current == m_list.begin())
+ return &(mgContentItem::UNDEFINED); //invalid
+ else
+ return * (--m_current);
+}
+
+// get next track, do not update data structures
+mgContentItem* mgPlaylist::sneakNext()
+{
+ if(m_current+1 == m_list.end())
+ return &(mgContentItem::UNDEFINED); //invalid
+ else
+ return * (m_current+1);
+}
+
+
+/*=================================================================*/
+/* */
+/* class mgSelectionTreeNode */
+/* */
+/*=================================================================*/
+mgSelectionTreeNode::mgSelectionTreeNode(MYSQL db, int view)
+{
+ m_db = db;
+ m_parent=NULL;
+ m_level = 0;
+ m_view = view;
+ m_id = "";
+ m_label = "ROOT";
+ m_expanded = false;
+}
+mgSelectionTreeNode::mgSelectionTreeNode(mgSelectionTreeNode* parent, string id, string label)
+{
+ m_parent = parent;
+ m_level = m_parent->m_level+1;
+ m_view = m_parent->m_view;
+ m_db = m_parent->m_db;
+ m_id = id;
+ m_label = label;
+ m_expanded = false;
+}
+
+ /*==== destructor ====*/
+mgSelectionTreeNode::~mgSelectionTreeNode()
+{
+ collapse();
+// _children.clear();
+}
+
+mgSelectionTreeNode* mgSelectionTreeNode::getParent()
+{
+ return m_parent;
+}
+
+void mgSelectionTreeNode::collapse() // removes all children (recursively)
+{
+ vector <mgSelectionTreeNode*>::iterator iter;
+ mgSelectionTreeNode* ptr;
+
+ for(iter = m_children.begin(); iter != m_children.end();iter++)
+ {
+ ptr = *iter;
+ delete ptr;
+ }
+ m_expanded = false;
+ m_children.clear();
+}
+// access children
+vector<mgSelectionTreeNode*> &mgSelectionTreeNode::getChildren()
+{
+ mgDebug(5," returning %d children", m_children.size());
+ return m_children;
+}
+
+// access data in current node
+string mgSelectionTreeNode::getID()
+{
+ return m_id;
+}
+string mgSelectionTreeNode::getLabel()
+{
+ return m_label;
+}
+string mgSelectionTreeNode::getLabel(int n)
+{
+ mgSelectionTreeNode* node = this;
+ int d = m_level;
+ while(n < d)
+ {
+ node = node->m_parent;
+ d--;
+ }
+ return node->m_label;
+}
+
+string mgSelectionTreeNode::getRestrictions()
+{
+ return m_restriction;
+}
diff --git a/muggle-plugin/mg_content_interface.h b/muggle-plugin/mg_content_interface.h
new file mode 100755
index 0000000..d39e5d0
--- /dev/null
+++ b/muggle-plugin/mg_content_interface.h
@@ -0,0 +1,261 @@
+/*******************************************************************/
+/*! \file content_interface.h
+ * \brief Data Objects for content (e.g. mp3 files, movies)
+ * for the vdr muggle plugindatabase
+ ********************************************************************
+ * \version $Revision: 1.1 $
+ * \date $Date: 2004/02/01 18:22:53 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author file owner: $Author: LarsAC $
+ *
+ * Declares main classes of for content items and interfaces to SQL databases
+ *
+ * This file defines the following classes
+ * - mgPlaylist a playlist
+ * - mgTrack a single track (content item). e.g. an mp3 file
+ * - mgSelection a set of tracks (e.g. a database subset matching certain criteria)
+ *
+ */
+/*******************************************************************/
+
+/* makes sure we dont parse the same declarations twice */
+#ifndef _CONTENT_INTERFACE_H
+#define _CONTENT_INTERFACE_H
+
+#include <string>
+#include <vector>
+
+#include <mysql/mysql.h>
+
+#define ILLEGAL_ID -1
+
+/*!
+ *******************************************************************
+ * \class mgMediaPlayer
+ *
+ * \brief dummy player class
+ ********************************************************************/
+class mgMediaPlayer
+{
+ public:
+ mgMediaPlayer(){;}
+ ~mgMediaPlayer(){;}
+
+};
+
+/*!
+ *******************************************************************
+ * \class mgContentItem
+ *
+ * \brief Generic class that represents a single content item.
+ * This is the parent class from which classes like mgGdTrack are derived
+ ********************************************************************/
+class mgContentItem
+{
+
+ public:
+ typedef enum contentType{
+ ABSTRACT =0,
+ GD_AUDIO,
+ EPG
+ }contentType;
+
+ protected:
+ int m_uniqID; // internal identifier to uniquely identify a content item;
+
+ public:
+ /* constructor */
+ mgContentItem(){ m_uniqID = -1;}
+ mgContentItem(int id){m_uniqID = id;}
+ mgContentItem(const mgContentItem& org){m_uniqID = org.m_uniqID;}
+ /* destructor */
+ virtual ~mgContentItem(){};
+
+ /* data acess */
+ int getId(){ return m_uniqID;}
+
+ // what type of content are we looking at (e.g. audio, video, epg)
+ virtual contentType getContentType(){return ABSTRACT;}
+
+ // return (global?) object that is used to play the content items
+ virtual mgMediaPlayer getPlayer(){return mgMediaPlayer();}
+
+
+ // get the "file" (or URL) that is passed to the player
+ virtual std::string getSourceFile(){return "";}
+
+ // ============ data access =================
+ virtual std::string getTitle(){return "";} // return the title
+ virtual std::string getLabel(int col){return "";} // return the title
+
+ virtual std::string getDescription()// return a short textual description
+ {return "";}
+ virtual std::string getGenre(){return "";}
+ virtual int getRating()
+ {
+ return 0;
+ }
+
+ virtual bool operator == (mgContentItem o){return m_uniqID == o.m_uniqID;}
+
+ // check, if usable
+ virtual bool isValid() {return (m_uniqID >=0);}
+ static mgContentItem UNDEFINED;
+};
+
+
+class mgTracklist
+{
+ protected:
+ std::vector<mgContentItem*> m_list;
+ std::vector<int> m_columns;
+ int sorting;
+
+ public:
+ mgTracklist(); // creates empty tracklist;
+
+ virtual ~mgTracklist();
+
+ std::vector<mgContentItem*> *getAll();
+ unsigned int getNumItems();
+
+ virtual void shuffle();
+ virtual void sortBy(int col, bool direction);
+
+ void setDisplayColumns(std::vector<int> cols);
+ unsigned int getNumColumns();
+ virtual std::string getLabel(unsigned int position, const std::string separator);
+
+ virtual mgContentItem* mgTracklist::getItem(unsigned int position);
+
+ virtual int remove(mgContentItem* item); // remove all occurences of item
+ virtual bool remove(unsigned int position); // remove item at position
+};
+
+/*!
+ *******************************************************************
+ * \class mgPlaylist
+ *
+ * Represents a generic playlist, i.e. an ordered collection of tracks
+ * The derived classes take care of specifics of certain media types
+ ********************************************************************/
+class mgPlaylist : public mgTracklist
+{
+ protected:
+ std::string m_listname;
+ std::vector<mgContentItem*>::iterator m_current;
+ public:
+
+ /*==== constructors ====*/
+ mgPlaylist();
+ mgPlaylist(std::string listname);
+
+ /*==== destructor ====*/
+ virtual ~mgPlaylist();
+
+ /*==== add/ remove tracks ====*/
+
+ /* adds a song at the end of the playlist */
+ virtual void append(mgContentItem* item);
+ virtual void appendList(std::vector<mgContentItem*> *tracks);
+ /* adds a song after 'position' */
+ virtual void insert(mgContentItem* item, unsigned int position);
+
+ /*==== access tracks ====*/
+ std::string getListname() ;
+ void setListname(std::string name);
+
+ // returns the first item of the list
+ virtual mgContentItem* getFirst();
+
+ // returns the nth track from the playlist
+ virtual mgContentItem* getPosition(unsigned int position);
+
+ // proceeds to the next item
+ virtual mgContentItem* skipFwd();
+
+ // goes back to the previous item
+ virtual mgContentItem* skipBack();
+
+ virtual mgContentItem* sneakNext();
+
+};
+
+class mgSelectionTreeNode
+{
+
+protected:
+ MYSQL m_db;
+ bool m_expanded; // already expanded ?
+ std::string m_restriction; // list of active restrictions at this level
+ std::string m_id; // ID of the node, used for further expand
+ int m_level; // depth of tree (0 = root)
+ int m_view;
+ std::string m_label;
+
+// std::vector<std::string> _labels; // Labels used for interaction with the user
+ // about this node
+
+// vector<mgSelectionTreeNode> _children; // if expanded the links to the
+ // children are stopred here
+ mgSelectionTreeNode* m_parent;
+ std::vector <mgSelectionTreeNode*> m_children;
+
+public:
+
+ /*==== constructors ====*/
+ mgSelectionTreeNode(MYSQL db, int view);
+
+ mgSelectionTreeNode(mgSelectionTreeNode* parent, std::string id, std::string label);
+
+ /*==== destructor ====*/
+ virtual ~mgSelectionTreeNode();
+
+ // compute children on the fly
+ virtual bool expand()=0;
+ virtual void collapse(); // removes all children (recursively)
+
+ mgSelectionTreeNode* getParent();
+
+ // access children
+ virtual std::vector<mgSelectionTreeNode*> &getChildren();
+
+ // access data in current node
+ bool isExpanded(){return m_expanded;}
+ int getLevel(){return m_level;} // for debugging
+ std::string getID();
+ virtual std::string getRestrictions();
+
+ std::string getLabel();
+ virtual std::string getLabel(int n);
+ #if 0
+ virtual std::string viewTitle(int level)=0;
+ virtual std::vector<std::string> viewChoices(int level, int choice);
+#endif
+
+ // returns all tracks below this node
+ // Note: This function allocates memory for the vector and for all elements of the vector
+ // The calling function is in charge of releasing this memory
+ virtual std::vector<mgContentItem*>* getTracks()=0;
+
+};
+
+#endif /* END _CONTENT_INTERFACE_H */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/muggle-plugin/mg_database.c b/muggle-plugin/mg_database.c
new file mode 100644
index 0000000..9a85cb8
--- /dev/null
+++ b/muggle-plugin/mg_database.c
@@ -0,0 +1,35 @@
+/*******************************************************************/
+/*! \file mg_database.c
+ * \brief A capsule around MySql database access
+ ********************************************************************
+ * \version $Revision: 1.1 $
+ * \date $Date: 2004/02/01 18:22:53 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author file owner: $Author: LarsAC $
+ */
+/*******************************************************************/
+
+#include "mg_database.h"
+
+using namespace std;
+
+mgDB::mgDB()
+{
+}
+
+mgDB::mgDB(string user, string pass)
+{
+}
+
+mgDB::~mgDB()
+{
+}
+
+MYSQL mgDB::getDBHandle()
+{
+ MYSQL m;
+
+ return m;
+}
+
+
diff --git a/muggle-plugin/mg_database.h b/muggle-plugin/mg_database.h
new file mode 100644
index 0000000..b979df4
--- /dev/null
+++ b/muggle-plugin/mg_database.h
@@ -0,0 +1,34 @@
+/*******************************************************************/
+/*! \file mg_database.h
+ * \brief A capsule around MySql database access
+ ********************************************************************
+ * \version $Revision: 1.1 $
+ * \date $Date: 2004/02/01 18:22:53 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author file owner: $Author: LarsAC $
+ */
+/*******************************************************************/
+
+
+#ifndef __MG_DATABASE_H
+#define __MG_DATABASE_H
+
+#include <string>
+
+#include <mysql/mysql.h>
+
+class mgDB
+{
+ public:
+
+ mgDB( );
+ mgDB( std::string user, std::string pass );
+ ~mgDB();
+
+ MYSQL getDBHandle();
+
+ private:
+ // MYSQL m_dbase;
+};
+
+#endif
diff --git a/muggle-plugin/mg_media.c b/muggle-plugin/mg_media.c
new file mode 100644
index 0000000..26d8b8f
--- /dev/null
+++ b/muggle-plugin/mg_media.c
@@ -0,0 +1,272 @@
+/*******************************************************************/
+/*! \file mg_media.c
+ * \brief Top level access to media in vdr plugin muggle
+ * for the vdr muggle plugindatabase
+ ********************************************************************
+ * \version $Revision: 1.1 $
+ * \date $Date: 2004/02/01 18:22:53 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author file owner: $Author: LarsAC $
+ *
+ *
+ */
+/*******************************************************************/
+
+/* makes sure we dont parse the same declarations twice */
+#include "mg_media.h"
+#include "mg_tools.h"
+#include "mg_content_interface.h"
+#include "gd_content_interface.h"
+
+#include "sh_dummy_content.h"
+
+using namespace std;
+
+mgFilters::mgFilters()
+{
+}
+
+mgFilters::~mgFilters()
+{
+}
+
+int mgFilters::getNumFilters()
+{
+ return 0;
+}
+
+string mgFilters::getName(int filter)
+{
+ return 0;
+}
+
+int mgFilters::getValue(int filter)
+{
+ return 0;
+}
+
+mgFilters::filterType mgFilters::getType(int filter)
+{
+ return NUMBER;
+}
+
+// for NUMBER filters
+
+int mgFilters::getMin(int filter)
+{
+ return 0;
+}
+
+int mgFilters::getMax(int filter)
+{
+ return 0;
+}
+
+// for CHOICE
+
+vector<string> mgFilters::getChoices()
+{
+ return vector<string>();
+}
+
+int mgFilters::getCurrent(int filter)
+{
+ return 0;
+}
+
+ // check, if a value is correct
+bool mgFilters::checkValue(int filter, string value)
+{
+ return false;
+}
+
+bool mgFilters::checkValue(int filter, int value)
+{
+ return false;
+}
+
+// finally set the values
+bool mgFilters::setValue(int filter, string value)
+{
+ return false;
+}
+
+bool mgFilters::setValue(int filter, int value)
+{
+ return false;
+}
+
+/*!
+ *******************************************************************
+ * \class mgMedia
+ *
+ * \brief mein class to access content in the vdr plugin muggle
+ ********************************************************************/
+mgMedia::mgMedia(contentType mediatype)
+{
+ int errval = 0;
+
+ m_mediatype = mediatype;
+ m_filter = "1";
+ m_defaultView = 1;
+
+ // now initialize the database
+ mgDebug(1, "Media Type %sselected", getMediaTypeName().c_str());
+ switch(m_mediatype)
+ {
+ case DUMMY:
+ {
+ errval = DummyInitDatabase(&m_db);
+ break;
+ }
+ case GD_MP3:
+ {
+ errval = GdInitDatabase(&m_db);
+ mgDebug(3, "Successfully conntected to sql database 'GiantDisc'");
+ }
+ }
+ if(errval < 0)
+ {
+ mgError("Error connecting to database\n");
+ }
+
+}
+
+mgMedia::~mgMedia()
+{
+}
+
+string mgMedia::getMediaTypeName()
+{
+ switch(m_mediatype)
+ {
+ case DUMMY:
+ return "DUMMY";
+ case GD_MP3:
+ return "GiantDisc-mp3";
+ }
+ mgError("implementation Error"); // we should never get here
+ return "";
+}
+
+
+mgSelectionTreeNode* mgMedia::getSelectionRoot()
+{
+ switch(m_mediatype)
+ {
+ case DUMMY:
+ return new DummyTreeNode(m_db, m_defaultView);
+ case GD_MP3:
+ return new GdTreeNode(m_db, m_defaultView, m_filter);
+ }
+ mgError("implementation Error"); // we should never get here
+ return NULL;
+}
+
+mgFilters mgMedia::getActiveFilters()
+{
+ switch(m_mediatype)
+ {
+ case DUMMY:
+ return mgFilters();
+ case GD_MP3:
+ return mgFilters();
+ }
+ mgError("implementation Error"); // we should never get here
+ return mgFilters();
+}
+
+void mgMedia::setFilters(mgFilters filters)
+{
+}
+
+mgPlaylist* mgMedia::createTemporaryPlaylist()
+{
+ string tmpname = "current";
+ return loadPlaylist(tmpname);
+}
+
+mgPlaylist* mgMedia::loadPlaylist(string name)
+{
+ mgPlaylist *list;
+ switch(m_mediatype)
+ {
+ case DUMMY:
+ list = new DummyPlaylist(name, m_db);
+ list->setDisplayColumns(getDefaultCols());
+ return list;
+ case GD_MP3:
+ list = new GdPlaylist(name, m_db);
+ list->setDisplayColumns(getDefaultCols());
+ return list;
+ }
+ mgError("implementation Error"); // we should never get here
+ return NULL;
+}
+
+vector<string> mgMedia::getStoredPlaylists()
+{
+ switch(m_mediatype)
+ {
+ case DUMMY:
+ return DummyGetStoredPlaylists(m_db);
+ case GD_MP3:
+ return GdGetStoredPlaylists(m_db);
+ }
+ mgError("implementation Error"); // we should never get here
+ return vector<string>();
+}
+
+vector<int> mgMedia::getDefaultCols()
+{
+ vector<int> cols;
+ switch(m_mediatype)
+ {
+ case DUMMY:
+ cols.push_back(1); // artist
+ cols.push_back(0); // track
+ return cols;
+ case GD_MP3:
+ cols.push_back(1); // artist
+ cols.push_back(0); // track
+ return cols;
+ }
+ mgError("implementation Error"); // we should never get here
+
+ return cols;
+}
+
+mgTracklist* mgMedia::getTracks()
+{
+ mgTracklist *tracks;
+ switch(m_mediatype)
+ {
+ case DUMMY:
+
+ tracks = new DummyTracklist(m_db, m_filter);
+ tracks->setDisplayColumns(getDefaultCols());
+ return tracks;
+ case GD_MP3:
+ tracks = new GdTracklist(m_db, m_filter);
+ tracks->setDisplayColumns(getDefaultCols());
+ return tracks;
+ }
+ mgError("implementation Error"); // we should never get here
+
+ return NULL;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/muggle-plugin/mg_media.h b/muggle-plugin/mg_media.h
new file mode 100644
index 0000000..f4cd64f
--- /dev/null
+++ b/muggle-plugin/mg_media.h
@@ -0,0 +1,137 @@
+/*******************************************************************/
+/*! \file mgmedia.h
+ * \brief Top level access to media in vdr plugin muggle
+ * for the vdr muggle plugindatabase
+ ********************************************************************
+ * \version $Revision: 1.1 $
+ * \date $Date: 2004/02/01 18:22:53 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author file owner: $Author: LarsAC $
+ *
+ *
+ */
+/*******************************************************************/
+/* makes sur we dont use parse the same declarations twice */
+#ifndef _MG_MEDIA_H
+#define _MG_MEDIA_H
+
+#include <string>
+#include <vector>
+
+#include <mysql/mysql.h>
+
+class mgPlaylist;
+class mgTracklist;
+class mgSelectionTreeNode;
+
+/*!
+ *******************************************************************
+ * \class mgFilters
+ *
+ * \brief stores a set of search constraints to the media database
+ ********************************************************************/
+class mgFilters
+{
+ typedef enum filterType
+ {
+ NUMBER, // integer number (with upper and lower limits)
+ STRING, // any string
+ REGEXP_STRING, // string containing wildcard symbol '*'
+ CHOICE // value fro ma list of choices
+ }filterType;
+
+ public:
+ std::vector<std::string> fields;
+ std::vector<std::string> values;
+
+ mgFilters();
+ ~mgFilters();
+
+ int getNumFilters();
+ std::string getName(int filter);
+ int getValue(int filter);
+ filterType getType(int filter);
+
+ // for NUMBER filters
+ int getMin(int filter);
+ int getMax(int filter);
+
+ // for CHOICE
+ std::vector<std::string> getChoices();
+ int getCurrent(int filter);
+
+ // check, if a value is correct
+ bool checkValue(int filter, std::string value);
+ bool checkValue(int filter, int value);
+
+ // finally set the values
+
+ bool setValue(int filter, std::string value);
+ bool setValue(int filter, int value);
+};
+
+
+/*!
+ *******************************************************************
+ * \class mgMedia
+ *
+ * \brief main class to access content in the vdr plugin muggle
+ ********************************************************************/
+class mgMedia
+{
+
+ public:
+ typedef enum contentType{
+ DUMMY =0,
+ GD_MP3
+ } contentType;
+
+ private:
+ MYSQL m_db;
+ contentType m_mediatype;
+ std::string m_filter;
+ int m_defaultView;
+
+ public:
+ mgMedia(contentType mediatype);
+ ~mgMedia();
+
+ std::string getMediaTypeName();
+
+ mgSelectionTreeNode* getSelectionRoot();
+
+ mgFilters getActiveFilters();
+
+ void setFilters(mgFilters filters);
+ void setFilters(std::string sql)
+ {
+ m_filter=sql;
+ }
+
+ // playlist management
+ mgPlaylist* createTemporaryPlaylist();
+ mgPlaylist* loadPlaylist( std::string name );
+ std::vector<std::string> getStoredPlaylists();
+
+ std::vector<int> getDefaultCols();
+ mgTracklist* getTracks();
+};
+#endif /* END _CONTENT_INTERFACE_H */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/muggle-plugin/mg_tools.c b/muggle-plugin/mg_tools.c
new file mode 100644
index 0000000..b540a4e
--- /dev/null
+++ b/muggle-plugin/mg_tools.c
@@ -0,0 +1,140 @@
+/*******************************************************************/
+/*! \file muggle_tools.cpp
+ * \brief A few util functions for standalone and plugin messaging
+ * for the vdr muggle plugindatabase
+ ********************************************************************
+ * \version $Revision: 1.1 $
+ * \date $Date: 2004/02/01 18:22:53 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author file owner: $Author: LarsAC $
+ *
+ */
+/*******************************************************************/
+
+#include "mg_tools.h"
+
+extern "C"
+{
+ #include <stdarg.h>
+ #include <stdio.h>
+}
+#include <stdlib.h>
+
+
+#define MAX_BUFLEN 1024
+#define MAX_QUERY_BUFLEN 1024
+static char buffer[MAX_BUFLEN];
+static char querybuf[MAX_QUERY_BUFLEN];
+
+static int DEBUG_LEVEL=3;
+
+void mgSetDebugLevel(int new_level)
+{
+ DEBUG_LEVEL = new_level;
+}
+
+void mgDebug(int level, const char *fmt, ...)
+{
+
+ va_list ap;
+ if(level <= DEBUG_LEVEL)
+ {
+ va_start(ap, fmt);
+
+ vsnprintf(buffer, MAX_BUFLEN-1, fmt, ap);
+ if(STANDALONE)
+ {
+ fprintf(stderr, "dbg %d: %s\n", level, buffer);
+ }
+ else
+ {
+#if !STANDALONE
+ isyslog( "%s\n", buffer);
+#endif
+ }
+ }
+ va_end(ap);
+}
+
+void mgDebug( const char *fmt, ... )
+{
+ va_list ap;
+ va_start( ap, fmt );
+ mgDebug( 1, fmt, ap );
+}
+
+
+void mgWarning(const char *fmt, ...)
+{
+
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ vsnprintf(buffer, MAX_BUFLEN-1, fmt, ap);
+ if(STANDALONE)
+ {
+ fprintf(stderr, "warning: %s\n", buffer);
+ }
+ else
+ {
+#if !STANDALONE
+ isyslog( "Warning: %s\n", buffer);
+#endif
+
+ }
+
+ va_end(ap);
+}
+
+void mgError(const char *fmt, ...)
+{
+
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ vsnprintf(buffer, MAX_BUFLEN-1, fmt, ap);
+ if(STANDALONE)
+ {
+ fprintf(stderr, "Error: %s\n", buffer);
+ exit(1);
+ }
+ else
+ {
+#if !STANDALONE
+ isyslog( "Error in Muggle: %s\n", buffer);
+#endif
+ }
+
+ va_end(ap);
+}
+
+MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ vsnprintf(querybuf, MAX_QUERY_BUFLEN-1, fmt, ap);
+ mgDebug(9, "SQL-Query: '%s'",querybuf);
+ if(mysql_query(db,querybuf))
+ {
+ mgError("SQL error in MUGGLE\n%s\n", querybuf);
+ }
+ return mysql_store_result(db);
+}
+
+void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ vsnprintf(querybuf, MAX_QUERY_BUFLEN-1, fmt, ap);
+ if(mysql_query(db,querybuf))
+ {
+ mgError("SQL error in MUGGLE\n%s\n", querybuf);
+ }
+}
+
diff --git a/muggle-plugin/mg_tools.h b/muggle-plugin/mg_tools.h
new file mode 100644
index 0000000..68c8104
--- /dev/null
+++ b/muggle-plugin/mg_tools.h
@@ -0,0 +1,31 @@
+/*******************************************************************/
+/*! \file muggle_tools.h
+ * \brief A few util functions for standalone and plugin messaging
+ * for the vdr muggle plugindatabase
+ ********************************************************************
+ * \version $Revision: 1.1 $
+ * \date $Date: 2004/02/01 18:22:53 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author file owner: $Author: LarsAC $
+ *
+ */
+/*******************************************************************/
+/* makes sur we dont use parse the same declarations twice */
+#ifndef _MUGGLE_TOOLS_H
+#define _MUGGLE_TOOLS_H
+
+#include "mysql/mysql.h"
+
+#define STANDALONE 1
+
+void mgSetDebugLevel(int new_level);
+void mgDebug(int level, const char *fmt, ...);
+void mgDebug( const char *fmt, ... );
+void mgWarning(const char *fmt, ...);
+void mgError(const char *fmt, ...);
+
+
+MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...);
+void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...);
+
+#endif
diff --git a/muggle-plugin/muggle.c b/muggle-plugin/muggle.c
new file mode 100644
index 0000000..481c699
--- /dev/null
+++ b/muggle-plugin/muggle.c
@@ -0,0 +1,98 @@
+/*******************************************************************/
+/*! \file muggle.c
+ * \brief Implements a plugin for browsing media libraries within VDR
+ ********************************************************************
+ * \version $Revision: 1.1 $
+ * \date $Date: 2004/02/01 18:22:53 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author file owner: $Author: LarsAC $
+ */
+/*******************************************************************/
+
+static const char *VERSION = "0.0.1";
+static const char *DESCRIPTION = "Access GiantDisc database contents";
+static const char *MAINMENUENTRY = "Muggle";
+
+#include "muggle.h"
+#include "vdr_menu.h"
+#include "mg_tools.h"
+
+const char* mgMuggle::Version(void)
+{
+ return VERSION;
+}
+
+const char* mgMuggle::Description(void)
+{
+ return DESCRIPTION;
+}
+
+const char* mgMuggle::MainMenuEntry(void)
+{
+ return MAINMENUENTRY;
+}
+
+mgMuggle::mgMuggle(void)
+{
+ // Initialize any member variables here.
+ // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
+ // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
+}
+
+mgMuggle::~mgMuggle()
+{
+ // Clean up after yourself!
+}
+
+const char *mgMuggle::CommandLineHelp(void)
+{
+ // Return a string that describes all known command line options.
+ return NULL;
+}
+
+bool mgMuggle::ProcessArgs(int argc, char *argv[])
+{
+ // Implement command line argument processing here if applicable.
+ return true;
+}
+
+bool mgMuggle::Initialize(void)
+{
+ // Initialize any background activities the plugin shall perform.
+ return true;
+}
+
+bool mgMuggle::Start(void)
+{
+ // Start any background activities the plugin shall perform.
+ mgSetDebugLevel( 99 );
+
+ return true;
+}
+
+void mgMuggle::Housekeeping(void)
+{
+ // Perform any cleanup or other regular tasks.
+}
+
+cOsdObject *mgMuggle::MainMenuAction(void)
+{
+ // Perform the action when selected from the main VDR menu.
+ cOsdObject* osd = new mgMainMenu();
+
+ return osd;
+}
+
+cMenuSetupPage *mgMuggle::SetupMenu(void)
+{
+ // Return a setup menu in case the plugin supports one.
+ return NULL;
+}
+
+bool mgMuggle::SetupParse(const char *Name, const char *Value)
+{
+ // Parse your own setup parameters and store their values.
+ return false;
+}
+
+VDRPLUGINCREATOR(mgMuggle); // Don't touch this!
diff --git a/muggle-plugin/muggle.h b/muggle-plugin/muggle.h
new file mode 100644
index 0000000..e6ed1c2
--- /dev/null
+++ b/muggle-plugin/muggle.h
@@ -0,0 +1,51 @@
+/*******************************************************************/
+/*! \file muggle.h
+ * \brief Implements a plugin for browsing media libraries within VDR
+ ********************************************************************
+ * \version $Revision: 1.1 $
+ * \date $Date: 2004/02/01 18:22:53 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author file owner: $Author: LarsAC $
+ */
+/*******************************************************************/
+
+#ifndef _MUGGLE_H
+#define _MUGGLE_H
+
+#include <vdr/plugin.h>
+
+class mgMuggle : public cPlugin
+{
+public:
+
+ mgMuggle(void);
+
+ virtual ~mgMuggle();
+
+ virtual const char *Version(void);
+
+ virtual const char *Description(void);
+
+ virtual const char *CommandLineHelp(void);
+
+ virtual bool ProcessArgs(int argc, char *argv[]);
+
+ virtual bool Initialize(void);
+
+ virtual bool Start(void);
+
+ virtual void Housekeeping(void);
+
+ virtual const char *MainMenuEntry(void);
+
+ virtual cOsdObject *MainMenuAction(void);
+
+ virtual cMenuSetupPage *SetupMenu(void);
+
+ virtual bool SetupParse(const char *Name, const char *Value);
+
+private:
+
+};
+
+#endif
diff --git a/muggle-plugin/sh_console_osd.c b/muggle-plugin/sh_console_osd.c
new file mode 100644
index 0000000..ebb4702
--- /dev/null
+++ b/muggle-plugin/sh_console_osd.c
@@ -0,0 +1,212 @@
+/*
+ * osd.c: Abstract On Screen Display layer
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: sh_console_osd.c,v 1.1 2004/02/01 18:22:53 LarsAC Exp $
+ */
+
+#include "myosd.h"
+#include <string.h>
+//#include "device.h"
+// #include "i18n.h"
+//#include "status.h"
+
+// --- cOsdItem --------------------------------------------------------------
+
+cOsdItem::cOsdItem(eOSState State)
+{
+ text = NULL;
+}
+
+cOsdItem::cOsdItem(const char *Text, eOSState State)
+{
+ text = NULL;
+ SetText(Text,true);
+}
+
+cOsdItem::~cOsdItem()
+{
+ free(text);
+}
+
+void cOsdItem::SetText(const char *Text, bool Copy)
+{
+ free(text);
+ text = Copy ? strdup(Text) : (char *)Text; // text assumes ownership!
+}
+
+const char* cOsdItem::Get()
+{
+ return text;
+}
+void cOsdItem::Display()
+{
+ printf("%s\n", text);
+}
+
+// --- cOsdMenu --------------------------------------------------------------
+
+cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4)
+{
+ cols[0] = c0;
+ cols[1] = c1;
+ cols[2] = c2;
+ cols[3] = c3;
+ cols[4] = c4;
+ m_first = 0;
+ m_current = m_marked = -1;
+ subMenu = NULL;
+ helpRed = helpGreen = helpYellow = helpBlue = NULL;
+ status = NULL;
+}
+
+cOsdMenu::~cOsdMenu()
+{
+ free(title);
+ delete subMenu;
+ free(status);
+}
+
+
+void cOsdMenu::SetTitle(const char *Title, bool ShowDate)
+{
+ title = strdup(Title);
+}
+
+void cOsdMenu::Add(cOsdItem *Item)
+{
+ m_display.push_back(Item);
+}
+
+void cOsdMenu::Display(void)
+{
+ //Interface->Clear();
+ printf("\n\n\n\n");
+ printf("----------(start %d, current %d)--------------------\n",
+ m_first, m_current);
+ //Interface->SetCols(cols);
+ //Interface->Title(title);
+ printf(" Title: %s\n", title);
+ //Interface->Help(helpRed, helpGreen, helpYellow, helpBlue);
+ for(int i= m_first; i < (int) m_display.size() && i < m_first+DISPLAY_SIZE; i++)
+ {
+ if(i == m_current)
+ {
+ printf("==>");
+
+ }
+ else
+ {
+ printf(" ");
+
+ }
+ printf("%s\n", m_display[i]->Get());
+ }
+ printf("----------------------------------------------\n");
+ if(hasHotkeys)
+ {
+ printf("%15s (r) %15s (g) %15s (y) %15s (b)\n",
+ helpRed? helpRed :"" ,
+ helpGreen? helpGreen :"" ,
+ helpYellow? helpYellow :"" ,
+ helpBlue? helpBlue :"" );
+
+ }
+}
+
+int cOsdMenu::Index(cOsdItem *Item)
+{
+ int pos=0;
+ for(vector<cOsdItem*>::iterator iter = m_display.begin();
+ iter != m_display.end(); iter++)
+ {
+ if((*iter) == Item)
+ {
+ break;
+ }
+ pos++;
+ }
+ return pos;
+}
+void cOsdMenu::SetCurrent(cOsdItem *Item)
+{
+ m_current = Item ? Index(Item) : -1;
+}
+void cOsdMenu::CursorUp(void)
+{
+ if(m_current >0) m_current--;
+ if(m_current < m_first) m_first = m_first - PAGE_JUMP;
+ Display();
+}
+void cOsdMenu::CursorDown(void)
+{
+ if(m_current < (int)m_display.size()-1)
+ m_current++;
+ if(m_current >= m_first+DISPLAY_SIZE) m_first =m_first + PAGE_JUMP;
+ Display();
+}
+
+void cOsdMenu::PageUp(void)
+{
+ if(m_first >= PAGE_JUMP)
+ {
+ m_first -= PAGE_JUMP;
+ m_current -= PAGE_JUMP;
+ }
+ Display();
+
+}
+void cOsdMenu::PageDown(void)
+{
+ if(m_first+ PAGE_JUMP< (int)m_display.size())
+ {
+ m_first += PAGE_JUMP;
+ m_current += PAGE_JUMP;
+ if(m_current < m_first) m_current = m_first;
+ if(m_current >= (int) m_display.size())
+ m_current = (int) m_display.size();
+ }
+ Display();
+}
+
+void cOsdMenu::Clear(void)
+{
+ m_first = 0;
+ m_current = m_marked = -1;
+ hasHotkeys = false;
+ for(vector<cOsdItem*>::iterator iter = m_display.begin();
+ iter != m_display.end(); iter++)
+ {
+ delete *iter;
+ }
+ m_display.clear();
+}
+
+eOSState cOsdMenu::ProcessKey(eKeys Key)
+{
+
+ switch (Key) {
+ case kUp|k_Repeat:
+ case kUp: CursorUp(); break;
+ case kDown|k_Repeat:
+ case kDown: CursorDown(); break;
+ case kLeft|k_Repeat:
+ case kLeft: PageUp(); break;
+ case kRight|k_Repeat:
+ case kRight: PageDown(); break;
+ case kBack: return osBack;
+ default: if (m_marked < 0)
+ return osUnknown;
+ }
+ return osContinue;
+}
+
+void cOsdMenu::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue)
+{
+ helpRed = Red;
+ helpGreen = Green;
+ helpYellow = Yellow;
+ helpBlue = Blue;
+}
diff --git a/muggle-plugin/sh_console_osd.h b/muggle-plugin/sh_console_osd.h
new file mode 100644
index 0000000..4a4e413
--- /dev/null
+++ b/muggle-plugin/sh_console_osd.h
@@ -0,0 +1,191 @@
+/*
+ * osd.h: Abstract On Screen Display layer
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: sh_console_osd.h,v 1.1 2004/02/01 18:22:53 LarsAC Exp $
+ */
+#ifndef __OSD_H
+#define __OSD_H
+#define DEBUG_OSD
+#if defined(DEBUG_OSD)
+#include <ncurses.h>
+#include <stdlib.h>
+#include <vector.h>
+#endif
+//#include "config.h"
+// #include "interface.h"
+//#include "osdbase.h"
+//#include "tools.h"
+#define MaxCols 5
+#define Setup_OSDheight 12
+#define Setup_OSDwidth 40
+#define Setup_Symbol false
+#define Setup_MenuScrollPage true
+#define MAXOSDITEMS (Setup.OSDheight - 4)
+enum eDvbColor {
+ clrBackground,
+ clrTransparent = clrBackground,
+ clrBlack = clrBackground,
+ clrRed,
+ clrGreen,
+ clrYellow,
+ clrBlue,
+ clrMagenta,
+ clrCyan,
+ clrWhite,
+ };
+
+extern int bgbackground, bgchannelname, bgepgtime, bgtitleline, bgscrolline,
+ bghelpred, bghelpgreen, bghelpyellow, bghelpblue;
+
+#define clrBackground ((eDvbColor)bgbackground)
+#define clrChannelName ((eDvbColor)bgchannelname)
+#define clrEpgTime ((eDvbColor)bgepgtime)
+#define clrTitleLine ((eDvbColor)bgtitleline)
+#define clrScrolLine ((eDvbColor)bgscrolline)
+#define clrHelpRed ((eDvbColor)bghelpred)
+#define clrHelpGreen ((eDvbColor)bghelpgreen)
+#define clrHelpYellow ((eDvbColor)bghelpyellow)
+#define clrHelpBlue ((eDvbColor)bghelpblue)
+
+#define DISPLAY_SIZE 12
+#define PAGE_JUMP 11
+
+extern int fgchannelname, fgdatetime, fgepgtime, fgtitle, fgsubtitle, fgtitleline, fgscrolline,
+ fgmenufont, volumebar, timebar1, timebar2, fgsymbolon, fgsymboloff;
+
+enum eOSState { osUnknown,
+ osContinue,
+ osSchedule,
+ osChannels,
+ osDirector,
+ osTimers,
+ osRecordings,
+ osPlugin,
+ osSetup,
+ osCommands,
+ osPause,
+ osRecord,
+ osReplay,
+ osStopRecord,
+ osStopReplay,
+ osCancelEdit,
+ osSwitchDvb,
+ osBack,
+ osEnd,
+ os_User, // the following values can be used locally
+ osUser1,
+ osUser2,
+ osUser3,
+ osUser4,
+ osUser5,
+ osUser6,
+ osUser7,
+ osUser8,
+ osUser9,
+ osUser10,
+ osUser11,
+ };
+enum eDvbFont {
+ fontOsd,
+ fontOsd2,
+ fontFix,
+ fontSym,
+/* TODO as soon as we have the font files...
+ fontTtxSmall,
+ fontTtxLarge,
+*/
+ };
+enum eKeys { // "Up" and "Down" must be the first two keys!
+ kUp,
+ kDown,
+ kMenu,
+ kOk,
+ kBack,
+ kLeft,
+ kRight,
+ kRed,
+ kGreen,
+ kYellow,
+ kBlue,
+ k0, k1, k2, k3, k4, k5, k6, k7, k8, k9,
+ kPlay,
+ kPause,
+ kStop,
+ kRecord,
+ kFastFwd,
+ kFastRew,
+ kPower,
+ kChanUp,
+ kChanDn,
+ kVolUp,
+ kVolDn,
+ kMute,
+ kSchedule,
+ kChannels,
+ kTimers,
+ kRecordings,
+ kDirector,
+ kSetup,
+ kCommands,
+ kUser1, kUser2, kUser3, kUser4, kUser5, kUser6, kUser7, kUser8, kUser9,
+ kNone,
+ kKbd,
+ // The following codes are used internally:
+ k_Plugin,
+ k_Setup,
+ // The following flags are OR'd with the above codes:
+ k_Repeat = 0x8000,
+ k_Release = 0x4000,
+ k_Flags = k_Repeat | k_Release,
+ };
+
+class cOsdItem {
+private:
+ char *text;
+ public:
+ cOsdItem(eOSState State = osUnknown);
+ cOsdItem(const char *Text, eOSState State = osUnknown);
+ void SetText(const char *Text, bool Copy);
+ virtual ~cOsdItem();
+ int Index();
+ const char* Get();
+ void Display();
+};
+
+class cOsdMenu {
+private:
+ char *title;
+ int cols[MaxCols];
+ int m_first, m_current, m_marked;
+ cOsdMenu *subMenu;
+ const char *helpRed, *helpGreen, *helpYellow, *helpBlue;
+ char *status;
+ int digit;
+ bool hasHotkeys;
+ vector<cOsdItem*> m_display;
+protected:
+ bool visible;
+ void CursorUp(void);
+ void CursorDown(void);
+ void PageUp(void);
+ void PageDown(void);
+ int Index(cOsdItem *Item);
+public:
+ cOsdMenu(const char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0);
+ virtual ~cOsdMenu();
+ void SetTitle(const char *Title, bool ShowDate=false);
+ int Current(void) { return m_current; }
+ void Add(cOsdItem *Item);
+// void Ins(cOsdItem *Item, bool Current = false, cOsdItem *Before = NULL);
+ virtual void Display(void);
+ virtual void SetCurrent(cOsdItem *Item);
+ virtual eOSState ProcessKey(eKeys Key);
+ virtual void Clear(void);
+ virtual void SetHasHotkeys(){ hasHotkeys = true; }
+ virtual void SetHelp(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL);
+};
+
+#endif //__OSD_H
diff --git a/muggle-plugin/sh_console_osd_menuitems.c b/muggle-plugin/sh_console_osd_menuitems.c
new file mode 100644
index 0000000..48b5d3a
--- /dev/null
+++ b/muggle-plugin/sh_console_osd_menuitems.c
@@ -0,0 +1,530 @@
+/*
+ * menuitems.c: General purpose menu items
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: sh_console_osd_menuitems.c,v 1.1 2004/02/01 18:22:53 LarsAC Exp $
+ */
+
+#include "mymenuitems.h"
+
+const char *FileNameChars = " abcdefghijklmnopqrstuvwxyz0123456789-.#~";
+
+// --- cMenuEditItem ---------------------------------------------------------
+
+cMenuEditItem::cMenuEditItem(const char *Name)
+{
+ name = strdup(Name);
+ value = NULL;
+}
+
+cMenuEditItem::~cMenuEditItem()
+{
+ free(name);
+ free(value);
+}
+
+void cMenuEditItem::SetValue(const char *Value)
+{
+ free(value);
+ value = strdup(Value);
+ char *buffer = NULL;
+ asprintf(&buffer, "%s:\t%s", name, value);
+ SetText(buffer, false);
+ Display();
+}
+
+// --- cMenuEditIntItem ------------------------------------------------------
+
+cMenuEditIntItem::cMenuEditIntItem(const char *Name, int *Value, int Min, int Max)
+:cMenuEditItem(Name)
+{
+ value = Value;
+ min = Min;
+ max = Max;
+ Set();
+}
+
+void cMenuEditIntItem::Set(void)
+{
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%d", *value);
+ SetValue(buf);
+}
+
+eOSState cMenuEditIntItem::ProcessKey(eKeys Key)
+{
+ return osContinue;
+}
+
+// --- cMenuEditBoolItem -----------------------------------------------------
+
+cMenuEditBoolItem::cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString, const char *TrueString)
+:cMenuEditIntItem(Name, Value, 0, 1)
+{
+ falseString = FalseString ? FalseString : "no";
+ trueString = TrueString ? TrueString : "yes";
+ Set();
+}
+
+void cMenuEditBoolItem::Set(void)
+{
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%s", *value ? trueString : falseString);
+ SetValue(buf);
+}
+
+// --- cMenuEditNumItem ------------------------------------------------------
+
+cMenuEditNumItem::cMenuEditNumItem(const char *Name, char *Value, int Length, bool Blind)
+:cMenuEditItem(Name)
+{
+ value = Value;
+ length = Length;
+ blind = Blind;
+ Set();
+}
+
+void cMenuEditNumItem::Set(void)
+{
+ if (blind) {
+ char buf[length + 1];
+ int i;
+ for (i = 0; i < length && value[i]; i++)
+ buf[i] = '*';
+ buf[i] = 0;
+ SetValue(buf);
+ }
+ else
+ SetValue(value);
+}
+
+eOSState cMenuEditNumItem::ProcessKey(eKeys Key)
+{
+ return osContinue;
+}
+
+// --- cMenuEditChrItem ------------------------------------------------------
+
+cMenuEditChrItem::cMenuEditChrItem(const char *Name, char *Value, const char *Allowed)
+:cMenuEditItem(Name)
+{
+ value = Value;
+ allowed = strdup(Allowed);
+ current = strchr(allowed, *Value);
+ if (!current)
+ current = allowed;
+ Set();
+}
+
+cMenuEditChrItem::~cMenuEditChrItem()
+{
+ free(allowed);
+}
+
+void cMenuEditChrItem::Set(void)
+{
+ char buf[2];
+ snprintf(buf, sizeof(buf), "%c", *value);
+ SetValue(buf);
+}
+
+eOSState cMenuEditChrItem::ProcessKey(eKeys Key)
+{
+ return osContinue;
+}
+
+// --- cMenuEditStrItem ------------------------------------------------------
+
+cMenuEditStrItem::cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed)
+:cMenuEditItem(Name)
+{
+ value = Value;
+ length = Length;
+ allowed = strdup(Allowed);
+ pos = -1;
+ insert = uppercase = false;
+ newchar = true;
+ ieCurChr = 0;
+ lastKey = kNone;
+ Set();
+}
+
+cMenuEditStrItem::~cMenuEditStrItem()
+{
+ free(allowed);
+}
+
+void cMenuEditStrItem::SetHelpKeys(void)
+{
+}
+
+void cMenuEditStrItem::Set(void)
+{
+}
+
+eOSState cMenuEditStrItem::ProcessKey(eKeys Key)
+{
+#if 0
+ const char c1[] = "-.#~,/_@1";
+ const char c2[] = "abcäåá2";
+ const char c3[] = "defé3";
+ const char c4[] = "ghi4";
+ const char c5[] = "jkl5";
+ const char c6[] = "mnoöñó6";
+ const char c7[] = "pqrs7";
+ const char c8[] = "tuvüú8";
+ const char c9[] = "wxyz9";
+ const char c0[] = " 0";
+
+ switch (Key) {
+ case kRed: // Switch between upper- and lowercase characters
+ if (pos >= 0 && (!insert || !newchar)) {
+ uppercase = !uppercase;
+ value[pos] = uppercase ? toupper(value[pos]) : tolower(value[pos]);
+ }
+ break;
+ case kGreen: // Toggle insert/overwrite modes
+ if (pos >= 0) {
+ insert = !insert;
+ newchar = true;
+ }
+ SetHelpKeys();
+ lastKey = Key;
+ break;
+ case kYellow|k_Repeat:
+ case kYellow: // Remove the character at current position; in insert mode it is the character to the right of cursor
+ if (pos >= 0) {
+ if (strlen(value) > 1) {
+ if (!insert || pos < int(strlen(value)) - 1)
+ memmove(value + pos, value + pos + 1, strlen(value) - pos);
+ // reduce position, if we removed the last character
+ if (pos == int(strlen(value)))
+ pos--;
+ }
+ else if (strlen(value) == 1)
+ value[0] = ' '; // This is the last character in the string, replace it with a blank
+ if (isalpha(value[pos]))
+ uppercase = isupper(value[pos]);
+ newchar = true;
+ }
+ lastKey = Key;
+ break;
+ case kLeft|k_Repeat:
+ case kLeft: if (pos > 0) {
+ if (!insert || newchar)
+ pos--;
+ newchar = true;
+ }
+ if (!insert && isalpha(value[pos]))
+ uppercase = isupper(value[pos]);
+ lastKey = Key;
+ break;
+ case kRight|k_Repeat:
+ case kRight: if (pos < length - 2 && pos < int(strlen(value)) ) {
+ if (++pos >= int(strlen(value))) {
+ if (pos >= 2 && value[pos - 1] == ' ' && value[pos - 2] == ' ')
+ pos--; // allow only two blanks at the end
+ else {
+ value[pos] = ' ';
+ value[pos + 1] = 0;
+ }
+ }
+ }
+ newchar = true;
+ if (!insert && isalpha(value[pos]))
+ uppercase = isupper(value[pos]);
+ if (pos == 0)
+ SetHelpKeys();
+ lastKey = Key;
+ break;
+ case kUp|k_Repeat:
+ case kUp:
+ case kDown|k_Repeat:
+ case kDown: if (pos >= 0) {
+ if (insert && newchar) {
+ // create a new character in insert mode
+ if (int(strlen(value)) < length - 1) {
+ memmove(value + pos + 1, value + pos, strlen(value) - pos + 1);
+ value[pos] = ' ';
+ }
+ }
+ if (uppercase)
+ value[pos] = toupper(Inc(tolower(value[pos]), NORMALKEY(Key) == kUp));
+ else
+ value[pos] = Inc( value[pos], NORMALKEY(Key) == kUp);
+ newchar = false;
+ }
+ else
+ return cMenuEditItem::ProcessKey(Key);
+ lastKey = Key;
+ break;
+ case k0|k_Repeat ... k9|k_Repeat:
+ case k0 ... k9: if (Key != lastKey) {
+ ieCurChr = 0;
+ if (!newchar) {
+ // kRight
+ if (pos < length - 2 && pos < int(strlen(value)) ) {
+ if (++pos >= int(strlen(value))) {
+ if (pos >= 2 && value[pos - 1] == ' ' && value[pos - 2] == ' ')
+ pos--; // allow only two blanks at the end
+ else {
+ value[pos] = ' ';
+ value[pos + 1] = 0;
+ }
+ }
+ }
+ newchar = true;
+ if (!insert && isalpha(value[pos]))
+ uppercase = isupper(value[pos]);
+ }
+ }
+ // kUp/kDown
+ if (pos >= 0) {
+ if (insert && newchar) {
+ // create a new character in insert mode
+ if (int(strlen(value)) < length - 1) {
+ memmove(value + pos + 1, value + pos, strlen(value) - pos + 1);
+ value[pos] = ' ';
+ }
+ }
+ }
+ else
+ return cMenuEditItem::ProcessKey(Key);
+ switch (Key) {
+ case k1:
+ if (uppercase)
+ value[pos] = toupper(c1[ieCurChr]);
+ else
+ value[pos] = c1[ieCurChr];
+ if (c1[ieCurChr+1] == 0)
+ ieCurChr = 0;
+ else
+ ieCurChr++;
+ break;
+ case k2:
+ if (uppercase)
+ value[pos] = toupper(c2[ieCurChr]);
+ else
+ value[pos] = c2[ieCurChr];
+ if (c2[ieCurChr+1] == 0)
+ ieCurChr = 0;
+ else
+ ieCurChr++;
+ break;
+ case k3:
+ if (uppercase)
+ value[pos] = toupper(c3[ieCurChr]);
+ else
+ value[pos] = c3[ieCurChr];
+ if (c3[ieCurChr+1] == 0)
+ ieCurChr = 0;
+ else
+ ieCurChr++;
+ break;
+ case k4:
+ if (uppercase)
+ value[pos] = toupper(c4[ieCurChr]);
+ else
+ value[pos] = c4[ieCurChr];
+ if (c4[ieCurChr+1] == 0)
+ ieCurChr = 0;
+ else
+ ieCurChr++;
+ break;
+ case k5:
+ if (uppercase)
+ value[pos] = toupper(c5[ieCurChr]);
+ else
+ value[pos] = c5[ieCurChr];
+ if (c5[ieCurChr+1] == 0)
+ ieCurChr = 0;
+ else
+ ieCurChr++;
+ break;
+ case k6:
+ if (uppercase)
+ value[pos] = toupper(c6[ieCurChr]);
+ else
+ value[pos] = c6[ieCurChr];
+ if (c6[ieCurChr+1] == 0)
+ ieCurChr = 0;
+ else
+ ieCurChr++;
+ break;
+ case k7:
+ if (uppercase)
+ value[pos] = toupper(c7[ieCurChr]);
+ else
+ value[pos] = c7[ieCurChr];
+ if (c7[ieCurChr+1] == 0)
+ ieCurChr = 0;
+ else
+ ieCurChr++;
+ break;
+ case k8:
+ if (uppercase)
+ value[pos] = toupper(c8[ieCurChr]);
+ else
+ value[pos] = c8[ieCurChr];
+ if (c8[ieCurChr+1] == 0)
+ ieCurChr = 0;
+ else
+ ieCurChr++;
+ break;
+ case k9:
+ if (uppercase)
+ value[pos] = toupper(c9[ieCurChr]);
+ else
+ value[pos] = c9[ieCurChr];
+ if (c9[ieCurChr+1] == 0)
+ ieCurChr = 0;
+ else
+ ieCurChr++;
+ break;
+ case k0:
+ if (uppercase)
+ value[pos] = toupper(c0[ieCurChr]);
+ else
+ value[pos] = c0[ieCurChr];
+ if (c0[ieCurChr+1] == 0)
+ ieCurChr = 0;
+ else
+ ieCurChr++;
+ break;
+ default:
+ break;
+ }
+ lastKey = Key;
+ newchar = false;
+ break;
+ case kOk: if (pos >= 0) {
+ pos = -1;
+ lastKey = Key;
+ newchar = true;
+ stripspace(value);
+ SetHelpKeys();
+ break;
+ }
+ // run into default
+ default: if (pos >= 0 && BASICKEY(Key) == kKbd) {
+ int c = KEYKBD(Key);
+ if (c <= 0xFF) {
+ const char *p = strchr(allowed, tolower(c));
+ if (p) {
+ int l = strlen(value);
+ if (insert && l < length - 1)
+ memmove(value + pos + 1, value + pos, l - pos + 1);
+ value[pos] = c;
+ if (pos < length - 2)
+ pos++;
+ if (pos >= l) {
+ value[pos] = ' ';
+ value[pos + 1] = 0;
+ }
+ }
+ else {
+ switch (c) {
+ case 0x7F: // backspace
+ if (pos > 0) {
+ pos--;
+ return ProcessKey(kYellow);
+ }
+ break;
+ }
+ }
+ }
+ else {
+ switch (c) {
+ case kfHome: pos = 0; break;
+ case kfEnd: pos = strlen(value) - 1; break;
+ case kfIns: return ProcessKey(kGreen);
+ case kfDel: return ProcessKey(kYellow);
+ }
+ }
+ }
+ else
+ return cMenuEditItem::ProcessKey(Key);
+ }
+ Set();
+#endif
+ return osContinue;
+}
+
+// --- cMenuEditStraItem -----------------------------------------------------
+
+cMenuEditStraItem::cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings)
+:cMenuEditIntItem(Name, Value, 0, NumStrings - 1)
+{
+ strings = Strings;
+ Set();
+}
+
+void cMenuEditStraItem::Set(void)
+{
+ SetValue(strings[*value]);
+}
+
+// --- cMenuTextItem ---------------------------------------------------------
+
+cMenuTextItem::cMenuTextItem(const char *Text, int X, int Y, int W, int H, eDvbColor FgColor, eDvbColor BgColor, eDvbFont Font)
+{
+
+ text = strdup(Text);
+}
+
+cMenuTextItem::~cMenuTextItem()
+{
+ free(text);
+}
+
+void cMenuTextItem::Clear(void)
+{
+}
+
+void cMenuTextItem::Display(int Offset, eDvbColor FgColor, eDvbColor BgColor)
+{
+}
+
+void cMenuTextItem::ScrollUp(bool Page)
+{
+}
+
+void cMenuTextItem::ScrollDown(bool Page)
+{
+}
+
+eOSState cMenuTextItem::ProcessKey(eKeys Key)
+{
+ return osContinue;
+}
+
+// --- cMenuSetupPage --------------------------------------------------------
+
+cMenuSetupPage::cMenuSetupPage(void)
+:cOsdMenu("", 33)
+{
+ plugin = NULL;
+}
+
+void cMenuSetupPage::SetSection(const char *Section)
+{
+}
+
+eOSState cMenuSetupPage::ProcessKey(eKeys Key)
+{
+ return osContinue;
+}
+
+void cMenuSetupPage::SetPlugin(cPlugin *Plugin)
+{
+}
+
+void cMenuSetupPage::SetupStore(const char *Name, const char *Value)
+{
+}
+
+void cMenuSetupPage::SetupStore(const char *Name, int Value)
+{
+}
diff --git a/muggle-plugin/sh_console_osd_menuitems.h b/muggle-plugin/sh_console_osd_menuitems.h
new file mode 100644
index 0000000..8812862
--- /dev/null
+++ b/muggle-plugin/sh_console_osd_menuitems.h
@@ -0,0 +1,130 @@
+/*
+ * menuitems.h: General purpose menu items
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: sh_console_osd_menuitems.h,v 1.1 2004/02/01 18:22:53 LarsAC Exp $
+ */
+
+#ifndef __MENUITEMS_H
+#define __MENUITEMS_H
+
+#include "myosd.h"
+
+extern const char *FileNameChars;
+
+class cMenuEditItem : public cOsdItem {
+private:
+ char *name;
+ char *value;
+public:
+ cMenuEditItem(const char *Name);
+ ~cMenuEditItem();
+ void SetValue(const char *Value);
+ };
+
+class cMenuEditIntItem : public cMenuEditItem {
+protected:
+ int *value;
+ int min, max;
+ virtual void Set(void);
+public:
+ cMenuEditIntItem(const char *Name, int *Value, int Min = 0, int Max = INT_MAX);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+class cMenuEditBoolItem : public cMenuEditIntItem {
+protected:
+ const char *falseString, *trueString;
+ virtual void Set(void);
+public:
+ cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString = NULL, const char *TrueString = NULL);
+ };
+
+class cMenuEditNumItem : public cMenuEditItem {
+protected:
+ char *value;
+ int length;
+ bool blind;
+ virtual void Set(void);
+public:
+ cMenuEditNumItem(const char *Name, char *Value, int Length, bool Blind = false);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+class cMenuEditChrItem : public cMenuEditItem {
+private:
+ char *value;
+ char *allowed;
+ const char *current;
+ virtual void Set(void);
+public:
+ cMenuEditChrItem(const char *Name, char *Value, const char *Allowed);
+ ~cMenuEditChrItem();
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+class cMenuEditStrItem : public cMenuEditItem {
+private:
+ char *value;
+ int length;
+ char *allowed;
+ int pos;
+ bool insert, newchar, uppercase;
+ void SetHelpKeys(void);
+ virtual void Set(void);
+ char Inc(char c, bool Up);
+ int ieCurChr;
+ eKeys lastKey;
+public:
+ cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed);
+ ~cMenuEditStrItem();
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+class cMenuEditStraItem : public cMenuEditIntItem {
+private:
+ const char * const *strings;
+protected:
+ virtual void Set(void);
+public:
+ cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings);
+ };
+
+class cMenuTextItem : public cOsdItem {
+private:
+ char *text;
+ int x, y, w, h, lines, offset;
+ eDvbColor fgColor, bgColor;
+ eDvbFont font;
+public:
+ cMenuTextItem(const char *Text, int X, int Y, int W, int H = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground, eDvbFont Font = fontOsd);
+ ~cMenuTextItem();
+ int Height(void) { return h; }
+ void Clear(void);
+ virtual void Display(int Offset = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground);
+ bool CanScrollUp(void) { return offset > 0; }
+ bool CanScrollDown(void) { return h + offset < lines; }
+ void ScrollUp(bool Page);
+ void ScrollDown(bool Page);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+class cPlugin;
+
+class cMenuSetupPage : public cOsdMenu {
+private:
+ cPlugin *plugin;
+protected:
+ void SetSection(const char *Section);
+ virtual void Store(void) = 0;
+ void SetupStore(const char *Name, const char *Value = NULL);
+ void SetupStore(const char *Name, int Value);
+public:
+ cMenuSetupPage(void);
+ virtual eOSState ProcessKey(eKeys Key);
+ void SetPlugin(cPlugin *Plugin);
+ };
+
+#endif //__MENUITEMS_H
diff --git a/muggle-plugin/sh_dummy_content.c b/muggle-plugin/sh_dummy_content.c
new file mode 100644
index 0000000..a6afcd9
--- /dev/null
+++ b/muggle-plugin/sh_dummy_content.c
@@ -0,0 +1,567 @@
+/*******************************************************************/
+/*! \file content_interface.cpp
+ * \brief Data Objects for content (e.g. mp3 files, movies)
+ * for the vdr muggle plugindatabase
+ ********************************************************************
+ * \version $Revision: 1.1 $
+ * \date $Date: 2004/02/01 18:22:53 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author file owner: $Author: LarsAC $
+ *
+ * DUMMY
+ *
+ *
+ * Dummy artists: Artist_1, Artist_2, Artist_3
+ * Dummy albums by ArtistX: Album_X1, .... AlbumX3
+ * Dummy Tracks On Album_XY: Track_XY1,... Track_XY5
+ *
+ */
+/*******************************************************************/
+#define DEBUG
+
+#include "sh_dummy_content.h"
+#include "mg_tools.h"
+
+#include <strstream>
+#include <string>
+#define GD_PLAYLIST_TYPE 0 // listtype for giant disc db
+
+// some dummies to keep the compiler happy
+#define DUMMY_CONDITION true // we use that as dummy condition to satisfy C++ syntax
+#define DUMMY
+#define NUM_ARTISTS 5
+#define NUM_ALBUMS_PER_ARTIST 3
+#define NUM_TRACKS_PER_ALBUM 9
+
+int DummyInitDatabase(MYSQL *db){return 0;}
+vector<string> DummyGetStoredPlaylists(MYSQL db){ return vector<string>();}
+
+/*******************************************************************/
+/* class mgTack */
+/********************************************************************/
+DummyTrack DummyTrack::UNDEFINED = DummyTrack();
+
+/*!
+ *****************************************************************************
+ * \brief Constructor: creates a DummyTrack obect
+ *
+ * \param sqlIdentifier identifies a unique row in the track database
+ * \param dbase database which stores the track table
+ *
+ * On creation, the object contains only the idea. The actual data fields
+ * are filled when readData() is called for the first time.
+ ****************************************************************************/
+DummyTrack::DummyTrack(int sqlIdentifier, MYSQL dbase)
+{
+ m_uniqID = sqlIdentifier;
+ char buf[255];
+
+ mgDebug(9, "Creating dumy track %d", sqlIdentifier);
+ // create dummy value based on the id
+ sprintf(buf, "ArtistName_%d", (int) m_uniqID / 100);
+ m_artist = buf;
+ sprintf(buf,"Album_%d", (int) m_uniqID / 10);
+ m_album = buf;
+ sprintf(buf,"Track_%d",m_uniqID);
+ m_title = buf;
+ sprintf(buf,"sourcefile_%d", m_uniqID);
+ m_mp3file = buf;
+ m_genre = "unknown_genre";
+ m_year = 1970 + (m_uniqID%35);
+ m_rating = 2;
+ m_length = 180;
+}
+
+/*!
+ *****************************************************************************
+ * \brief copy constructor
+ *
+ ****************************************************************************/
+DummyTrack::DummyTrack(const DummyTrack& org)
+{
+ m_uniqID = org.m_uniqID;
+ m_db = org.m_db;
+
+ mgDebug(9,
+ "Creating a TrackCopy for track '%s' (is this really necessary?",
+ org.m_title.c_str());
+
+ m_artist = org.m_artist;
+ m_title = org.m_title;
+ m_mp3file = org.m_mp3file;
+ m_album = org.m_album;
+ m_genre = org.m_genre;
+ m_year = org.m_year;
+ m_rating = org.m_rating;
+
+}
+
+
+/*!
+ *****************************************************************************
+ * \brief destructor
+ *
+ ****************************************************************************/
+DummyTrack::~DummyTrack()
+{
+ // nothing to be done
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns value for _mp3file
+ ****************************************************************************/
+string DummyTrack::getSourceFile()
+{
+ return m_mp3file;
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns value for m_title
+ ****************************************************************************/
+string DummyTrack::getTitle()
+{
+ return m_title;
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns value for m_artist
+ ****************************************************************************/
+string DummyTrack::getArtist()
+{
+ return m_artist;
+}
+string DummyTrack::getLabel(int col)
+{
+ switch(col)
+ {
+ case 0:
+ return m_artist;
+ break;
+ case 1:
+ return m_title;
+ break;
+ default:
+ return "";
+ }
+}
+string DummyTrack::getDescription()
+{
+ return m_artist + " - " + m_title;
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns value for m_album
+ ****************************************************************************/
+string DummyTrack::getAlbum()
+{
+ return m_album;
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns value for m_genre
+ ****************************************************************************/
+string DummyTrack::getGenre()
+{
+ return m_genre;
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns value for m_year
+ ****************************************************************************/
+int DummyTrack::getYear()
+{
+ return m_year;
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns value for m_rating
+ *
+ * If value has not been retrieved from the database, radData() is called first
+ ****************************************************************************/
+int DummyTrack::getRating()
+{
+ return m_rating;
+}
+
+int DummyTrack::getDuration()
+{
+ return m_rating;
+}
+
+/*!
+ *****************************************************************************
+ * \brief sets the field for m_title to the specified value
+ *
+ * Note: The new value is not stored in the database.
+ * This is only done, when writeData() is called
+ ****************************************************************************/
+void DummyTrack::setTitle(string new_title)
+{
+ m_title = new_title;
+}
+
+/*!
+ *****************************************************************************
+ * \brief sets the field for m_artist to the specified value
+ *
+ * Note: The new value is not stored in the database.
+ * This is only done, when writeData() is called
+ ****************************************************************************/
+void DummyTrack::setArtist(string new_artist)
+{
+ m_artist = new_artist;
+}
+
+
+/*!
+ *****************************************************************************
+ * \brief sets the field for m_album to the specified value
+ *
+ * Note: The new value is not stored in the database.
+ * This is only done, when writeData() is called
+ ****************************************************************************/
+void DummyTrack::setAlbum(string new_album)
+{
+ m_album = new_album;
+}
+
+
+/*!
+ *****************************************************************************
+ * \brief sets the field for m_genre to the specified value
+ *
+ * Note: The new value is not stored in the database.
+ * This is only done, when writeData() is called
+ ****************************************************************************/
+void DummyTrack::setGenre(string new_genre)
+{
+ m_genre = new_genre;
+}
+
+
+/*!
+ *****************************************************************************
+ * \brief sets the field for m_year to the specified value
+ *
+ * Note: The new value is not stored in the database.
+ * This is only done, when writeData() is called
+ ****************************************************************************/
+void DummyTrack::setYear(int new_year)
+{
+ m_year = new_year;
+}
+
+
+/*!
+ *****************************************************************************
+ * \brief sets the field for m_rating to the specified value
+ *
+ * Note: The new value is not stored in the database.
+ * This is only done, when writeData() is called
+ ****************************************************************************/
+void DummyTrack::setRating(int new_rating)
+{
+ m_rating = new_rating;
+}
+
+
+/*!
+ *****************************************************************************
+ * \brief stores current values in the sql database
+ *
+ * Note: At the moment, only the values stored directly in the 'tracks'
+ * database are updated
+ ****************************************************************************/
+bool DummyTrack::writeData()
+{
+ return true;
+}
+
+DummyTracklist::DummyTracklist(MYSQL db_handle, string restrictions)
+{
+}
+
+/*******************************************************************/
+/* class DummyPlaylist */
+/********************************************************************/
+
+/*!
+ *****************************************************************************
+ * \brief Constructor: opens playlist by name
+ *
+ * \param listname user-readable identifier of the paylist
+ * \param db_handl database which stores the playlist
+ *
+ * If the playlist does not yet exist, an empty playlist is created
+ ****************************************************************************/
+DummyPlaylist::DummyPlaylist(string listname, MYSQL db_handle)
+{
+ m_db = db_handle;
+
+ //
+ // check, if the database really exists
+ //
+ if(listname =="EXISTS")
+ {
+
+ m_author = "DUMMY_author";
+ m_listname = listname;
+ createDummyPlaylist(1);
+ }
+ else
+ {
+ // new Playlist
+ m_author = "VDR";
+ m_listname = listname;
+
+ }
+
+}
+
+/*!
+ *****************************************************************************
+ * \brief Constructor: construct playlist object from existing sql playlist
+ *
+ * \param sql_identifier: sql internal identifier for the playlist
+ * \param db_handl database which stores the playlist
+ *
+ * This constructor is typically used when a playlist is selected from an
+ * internal list of playlists
+ ****************************************************************************/
+DummyPlaylist::DummyPlaylist(unsigned int sql_identifier, MYSQL db_handle)
+{
+ m_db = db_handle;
+ char buf[256];
+
+ m_author = "DUMMY_author";
+ sprintf(buf, "Dummylist_%d", sql_identifier);
+ m_listname = buf;
+ createDummyPlaylist(sql_identifier);
+}
+
+void DummyPlaylist::createDummyPlaylist(int start)
+{
+ DummyTrack* trackptr;
+ for(int id=start; id < start+20; id++)
+ {
+ trackptr = new DummyTrack(id, m_db);
+ m_list.push_back(trackptr);
+ }
+}
+
+/*!
+ *****************************************************************************
+ * \brief empty destructor
+ *
+ * Nothing to be done. The actual data is stored in the sql db
+ ****************************************************************************/
+DummyPlaylist::~DummyPlaylist()
+{
+ // nothing to be done
+}
+
+bool DummyPlaylist::storePlaylist()
+{
+ mgDebug(1, "Storing Playlist #%s'", m_listname.c_str());
+ return true;
+}
+/*!
+ *****************************************************************************
+ * \brief returns the total duration of all songs in the list in seconds
+ *
+ ****************************************************************************/
+int DummyPlaylist::getPlayTime()
+{
+ return m_list.size()* 180;
+}
+
+/*!
+ *****************************************************************************
+ * \brief returns the duration of all remaining songs in the list in seconds
+ *
+ ****************************************************************************/
+int DummyPlaylist::getPlayTimeRemaining()
+{
+ return 0; // dummy
+}
+
+/*******************************************************************/
+/* class DummyTreeNode */
+/*******************************************************************/
+
+DummyTreeNode::DummyTreeNode(MYSQL db, int view)
+ : mgSelectionTreeNode(db, view)
+{
+ // create a root node
+ // everything is done in the parent class
+ m_restriction ="1";
+}
+
+DummyTreeNode::DummyTreeNode(mgSelectionTreeNode* parent,
+ string id, string label, string restriction)
+ : mgSelectionTreeNode(parent, id, label)
+{
+ m_restriction = restriction;
+ // everything else is done in the parent class
+}
+
+
+/*!
+ *****************************************************************************
+ * \brief destructor
+ *
+ ****************************************************************************/
+DummyTreeNode::~DummyTreeNode()
+{
+ m_children.clear();
+}
+
+
+
+/*!
+ *****************************************************************************
+ * \brief compute children on the fly
+ *
+ ****************************************************************************/
+bool DummyTreeNode::expand()
+{
+ char buf[20];
+ if (m_expanded)
+ {
+ mgWarning("Node already expanded\n");
+ return true;
+ }
+ m_expanded = true;
+
+ mgDebug(5, "Expanding level %d\n", m_level);
+ switch(m_view)
+ {
+ case 1: // artist -> album -> title
+ if(m_level == 0) // root, expand artist
+ {
+ for(int artnum=1; artnum <= NUM_ARTISTS; artnum++)
+ {
+ sprintf(buf, "%d", artnum);
+ m_children.push_back(new DummyTreeNode(this, m_id+ (string) buf,
+ "Artist "+ (string)buf,
+ m_restriction + " AND album.artist='Artist "+(string) buf+"'"));
+ }
+
+ }
+ else if(m_level == 1) // artist, expand album
+ {
+ // create album names in the form Album_x1, ... Album_x3
+ // where x is the artist ID
+ // for normal usage: _restrictions should now hold:
+ // album.artist = XYZ
+ for(int albnum=1; albnum <= NUM_ALBUMS_PER_ARTIST; albnum++)
+ {
+ sprintf(buf, "%d", albnum);
+
+ m_children.push_back(new DummyTreeNode(this, m_id+ (string) buf,
+ "Album_"+ m_id + (string) buf,
+ m_restriction + " AND track.sourceid='0x00"+ m_id + (string) buf +"'"));
+ }
+ break;
+ }
+ else if(m_level == 2) // artist -> album, expand title
+ {
+ // create track names in the form Track_xy1, ... Track_xy5
+ // where x is the artist ID and y is the album id
+ // for normal usage: _restrictions should now hold:
+ // album.artist = ... AND track.sourceid='0x00XY
+ for(int tracknum=1; tracknum <= NUM_TRACKS_PER_ALBUM; tracknum++)
+ {
+ sprintf(buf, "%d", tracknum);
+ m_children.push_back(new DummyTreeNode(this, m_id+ (string) buf,
+ "Track_"+m_id+ (string) buf,
+ m_restriction + " AND track.id=tr"+ m_id + (string) buf + "'" ));
+ }
+ break;
+ }
+ else
+ {
+ mgWarning("View #%d level %d' not yet implemented", m_view, m_level);
+ m_expanded = false;
+ return false;
+ }
+ break;
+ default:
+ mgError("View '%d' not yet implemented", m_view);
+ }
+ mgDebug(5, "%d children expanded\n", m_children.size());
+ return true;
+}
+
+/*!
+ *****************************************************************************
+ * \brief go over all children recursively to find the tracks
+ *
+ ****************************************************************************/
+vector<mgContentItem*>* DummyTreeNode::getTracks()
+{
+ vector<mgContentItem*>* dummy;
+ string sql;
+
+ int artistnum;
+ int albumnum;
+ int tracknum;
+ dummy = new vector<mgContentItem*>();
+
+ sql = m_restriction;
+ mgDebug(5, "QUERY:\n%s\n", sql.c_str());
+
+ artistnum = 0;
+ do // artist_loop
+ {
+ if(m_level >=1)
+ artistnum = m_id[0]-'0'; // we have a unique artist
+ else
+ artistnum++; // we are in a loop
+
+ albumnum = 0;
+ do // album_loop
+ {
+ if(m_level >=2)
+ albumnum = m_id[1]-'0'; // we have a unique album
+ else
+ albumnum++; // we are in a loop
+
+ tracknum =0;
+ do // track_loop
+ {
+ if(m_level ==3)
+ tracknum = m_id[2]-'0'; // we have a unique track
+ else
+ tracknum++; // we are in a loop
+ dummy->push_back(new DummyTrack(artistnum*100+albumnum*10+tracknum, m_db));
+ }while ((m_level < 3) && (tracknum< NUM_TRACKS_PER_ALBUM));
+ }while ((m_level < 2) && (albumnum < NUM_ALBUMS_PER_ARTIST));
+ }while ((m_level < 1) && (artistnum < NUM_ARTISTS ));
+
+ return dummy;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/muggle-plugin/sh_dummy_content.h b/muggle-plugin/sh_dummy_content.h
new file mode 100644
index 0000000..71d3ce7
--- /dev/null
+++ b/muggle-plugin/sh_dummy_content.h
@@ -0,0 +1,178 @@
+/*******************************************************************/
+/*! \file dummy_content.h
+ * \brief Dummy Data Objects for testing Muggle
+ ********************************************************************
+ * \version $Revision: 1.1 $
+ * \date $Date: 2004/02/01 18:22:53 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author file owner: $Author: LarsAC $
+ *
+ * Declares main classes of for content items and interfaces to SQL databases
+ *
+ *******************************************************************/
+/* makes sur we dont use parse the same declarations twice */
+#ifndef _DUMMY_CONTENT_H
+#define _DUMMY_CONTENT_H
+using namespace std;
+#include <string>
+#include <list>
+#include <vector>
+
+#include "mg_content_interface.h"
+
+// non-member function
+int DummyInitDatabase(MYSQL *db);
+vector<string> DummyGetStoredPlaylists(MYSQL db);
+
+/*!
+ *******************************************************************
+ * \class DummyTrack
+ *
+ * \brief represents a a single track
+ * DUMMY
+ ********************************************************************/
+class DummyTrack : public mgContentItem
+{
+
+private:
+ MYSQL m_db;
+
+ // content fields
+ string m_artist;
+ string m_title;
+ string m_mp3file;
+ string m_album;
+ string m_genre;
+ int m_year;
+ int m_rating;
+ int m_length;
+
+
+ public:
+
+ /* constructor */
+ DummyTrack(){ m_uniqID = -1;} // creates invalid item
+ DummyTrack(int sqlIdentifier, MYSQL dbase);
+
+ DummyTrack(const DummyTrack&);
+
+ /* destructor */
+ ~DummyTrack();
+
+ virtual mgContentItem::contentType getContentType()
+ {return mgContentItem::GD_AUDIO;}
+ virtual mgMediaPlayer getPlayer(){return mgMediaPlayer();}
+
+ /* data acess */
+ //virtual functions of the base class
+ virtual string getSourceFile();
+ virtual string getTitle();
+ virtual string getLabel(int col);
+ virtual string getDescription();
+ virtual string getGenre();
+ virtual int getRating();
+
+ // additional class-specific functions
+ string getArtist();
+ string getAlbum();
+ int getYear();
+ int getDuration();
+
+ /* data manipulation */
+ void setTitle(string new_title);
+ void setArtist(string new_artist);
+ void setAlbum(string new_album);
+ void setGenre(string new_genre);
+ void setYear(int new_rating);
+ void setRating(int new_rating);
+
+ bool writeData();
+ static DummyTrack UNDEFINED;
+
+};
+
+class DummyTracklist : public mgTracklist
+{
+ public:
+ DummyTracklist(MYSQL db_handle, string restrictions);
+};
+
+/*!
+ *******************************************************************
+ * \class GdTracklist
+ *
+ * \brief represents a playlist, i.e. an ordered collection of tracks
+ ********************************************************************/
+class DummyPlaylist : public mgPlaylist
+{
+ private:
+ int m_sqlId; /* -1 means: not valid */
+ string m_author;
+ MYSQL m_db;
+ private:
+ void createDummyPlaylist(int strt);
+ public:
+
+
+ /*==== constructors ====*/
+ DummyPlaylist(string listname, MYSQL db_handle);
+ /* opens existing or creates empty playlist */
+
+ DummyPlaylist(unsigned int sql_identifier, MYSQL db_handle);
+ /* construct from the db by internal id*/
+
+ /*==== destructor ====*/
+ ~DummyPlaylist();
+
+ int getPlayTime();
+ /* returns the total duration of all songs in the list in seconds */
+
+ int getPlayTimeRemaining();
+ /* returns the duration of all remaining songs in the list in seconds */
+
+ bool storePlaylist();
+};
+/*!
+ *******************************************************************
+ * \class mgSelectionTreeNode
+ ********************************************************************/
+class DummyTreeNode : public mgSelectionTreeNode {
+
+private:
+ MYSQL m_db; // underlying db
+
+public:
+
+ /*==== constructors ====*/
+ DummyTreeNode(MYSQL db, int view);
+ DummyTreeNode(mgSelectionTreeNode* parent,
+ string id, string label, string restriction);
+
+ /*==== destructor ====*/
+ ~DummyTreeNode();
+
+ // compute children o^xn the fly
+ virtual bool expand();
+
+ virtual vector<mgContentItem*>* getTracks();
+};
+
+#endif /* END _CONTENT_INTERFACE_H */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/muggle-plugin/sh_muggle.c b/muggle-plugin/sh_muggle.c
new file mode 100644
index 0000000..ef26f57
--- /dev/null
+++ b/muggle-plugin/sh_muggle.c
@@ -0,0 +1,365 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "mysql/mysql.h"
+
+#include "content_interface.h"
+#include "mgmedia.h"
+#include "muggle_tools.h"
+#include <unistd.h>
+
+#define DISPLAY_SIZE 20
+#define PAGE_JUMP 18
+
+
+#define EXIT 0
+#define TREE_VIEW 1
+#define LIST_VIEW 2
+#define PLAYLIST_VIEW 3
+#define FILTER_VIEW 4
+
+
+const char playlist_command_str[] =
+" s : shuffle\n"
+" 8 : up\n"
+" 2 : down\n"
+" 9 : page-up\n"
+" 3 : page-down\n"
+" p : go to top\n"
+" t : switch to tree view\n"
+" q : quit";
+
+const char tree_command_str[] =
+" 6 : expand\n"
+" 4 : collapse\n"
+" 8 : up\n"
+" 2 : down\n"
+" 9 : page-up\n"
+" 3 : page-down\n"
+" 5 : add selection to playlist\n"
+" p : switch to playlist view\n"
+" t : go to root node\n"
+" q : quit";
+
+mgSelectionTreeNode* GV_currentNode;
+vector<mgSelectionTreeNode*> GV_treeDisplay;
+
+mgPlaylist *GV_currentPlaylist;
+
+void print_tree(mgSelectionTreeNode* node, string tab)
+{
+ vector<mgSelectionTreeNode*> children;
+ vector<mgSelectionTreeNode*>::iterator iter;
+
+ tab = tab + " ";
+ printf("%2d %c %s-- Id: '%s', level %d '%s' \n",
+ GV_treeDisplay.size(), (node->isExpanded()?'x':'-'), tab.c_str(),
+ node->getID().c_str(), node->getLevel(),
+ node->getLabel().c_str());
+// printf(" %sRestrictions: '%s'\n", tab.c_str(), node->getRestrictions().c_str());
+ GV_treeDisplay.push_back(node);
+ if(node->isExpanded())
+ {
+ children = node->getChildren();
+ for(iter = children.begin(); iter != children.end(); iter++)
+ {
+ print_tree(*iter, tab);
+ }
+ }
+}
+void print_node(mgSelectionTreeNode* node, unsigned int selected, unsigned int start)
+{
+ vector<mgSelectionTreeNode*> children;
+ vector<mgSelectionTreeNode*>::iterator iter;
+ mgSelectionTreeNode* child;
+ unsigned int i;
+
+ GV_treeDisplay.clear();
+ printf("--%s---\n", node->getLabel().c_str());
+ children = node->getChildren();
+ // first, add al nodes to the storage
+ for(iter = children.begin(); iter != children.end(); iter++)
+ {
+ GV_treeDisplay.push_back(*iter);
+ }
+
+ // now display the ones in question
+ printf("Displaying %d-%d of %d (%s)\n", start+1, start+DISPLAY_SIZE+1,
+ GV_treeDisplay.size(), node->getRestrictions().c_str());
+ if(start >0)
+ printf(".......\n");
+ for (i = start; i < start+DISPLAY_SIZE && i< GV_treeDisplay.size(); i++)
+ {
+ child = GV_treeDisplay[i];
+ if(i == selected)
+ printf ("==>");
+ else
+ printf(" ");
+ printf ("%s\n",child->getLabel().c_str());
+ }
+ if(i==start+DISPLAY_SIZE)
+ printf(".......\n");
+}
+
+void print_tracks(vector<mgContentItem*>* tracks, string restriction)
+{
+ vector<mgContentItem*>::iterator iter;
+
+ printf("======BEGIN_TRACKS=====(restriction:'%s'======\n", restriction.c_str());
+ for(iter = tracks->begin(); iter != tracks->end(); iter++)
+ {
+ printf(" Track: (%d): '%s' \n",(*iter)->getId(), (*iter)->getTitle().c_str());
+ }
+ printf("======END_TRACKS===========\n");
+ printf("press key t continue\n");
+ getchar();
+}
+
+void PrintPlaylist(mgPlaylist *pl, unsigned int selected, unsigned int start)
+{
+ vector<mgContentItem*>::iterator iter;
+ unsigned int i;
+
+ // now display the ones in question
+ printf("Playlist '%s' (%d entries)\n", pl->getListname().c_str(),
+ pl->getNumItems());
+ if(start >0)
+ printf(".......\n");
+ for (i = start; i<start+DISPLAY_SIZE && i< pl->getNumItems(); i++)
+ {
+ if(i == selected)
+ printf ("==>");
+ else
+ printf(" ");
+ printf ("%s\n",pl->getLabel(i, "\t").c_str());
+ }
+ if(i==start+DISPLAY_SIZE)
+ printf(".......\n");
+}
+int PlaylistView()
+{
+ char cmd;
+ bool loop = true;
+ unsigned int selected = 0;
+ unsigned int start=0;
+
+ while(loop)
+ {
+ printf("\n\n\n\n\n\n");
+ PrintPlaylist(GV_currentPlaylist, selected, start);
+ printf("------- enter command ('h' for help) -----------\n");
+ cmd = getchar();
+ switch (cmd)
+ {
+ case 'h': // shuffle
+ printf("%s\n", playlist_command_str);
+ break;
+ case 's': // shuffle
+ GV_currentPlaylist->shuffle();
+ break;
+ case '8': // up
+ if(selected >0) selected--;
+ if(selected < start) start = start - PAGE_JUMP;
+ break;
+ case '2': // down
+ if(selected < (GV_currentPlaylist->getNumItems()-1))
+ selected++;
+ if(selected >= start+DISPLAY_SIZE) start = start + PAGE_JUMP;
+ break;
+ case '9': // pgup
+ if(start >= PAGE_JUMP)
+ {
+ start -= PAGE_JUMP;
+ selected -= PAGE_JUMP;
+ }
+ else
+ mgDebug(6,"Can not go up %d %d", start, selected);
+ break;
+ case '3': // pgdown
+ mgDebug(6,"pgdown %d %d", start, selected);
+ if(start+ PAGE_JUMP< (GV_currentNode->getChildren()).size())
+ {
+ start += PAGE_JUMP;
+ selected += PAGE_JUMP;
+ if(selected >= ((GV_currentNode->getChildren()).size()))
+ selected = (GV_currentNode->getChildren()).size();
+ }
+
+ break;
+ case 'p': // go to top of playlist view
+ selected=0;
+ start=0;
+ break;
+ case 't': // tree view (on root node)
+ cmd = getchar();
+ return TREE_VIEW;
+ break;
+ case 'q': // tree view (on root node)
+ cmd = getchar();
+ return EXIT;
+ break;
+ default:
+ mgWarning("Invalid Command '%c'",cmd);
+
+ }
+ cmd = getchar();
+
+ }// end nodeview loop
+ return EXIT;
+}
+// displays one node (and all its children)
+int NodeView()
+{
+ vector<mgContentItem*>* tracks;
+ char cmd;
+ bool loop = true;
+ unsigned int selected = 0;
+ unsigned int start=0;
+ while(loop)
+ {
+ printf("\n\n\n\n\n\n");
+ if(!GV_currentNode->isExpanded())
+ {
+ GV_currentNode->expand();
+ }
+ print_node(GV_currentNode,selected, start);
+ printf("------- enter command ('h' for help) -----------\n");
+
+ cmd = getchar();
+ switch (cmd)
+ {
+ case 'h': // shuffle
+ printf("%s\n", tree_command_str);
+ break;
+ case 'e':
+ case '6':// expand
+ GV_currentNode = GV_treeDisplay[selected];
+ if(GV_currentNode->expand())
+ {
+ selected = 0;
+ start=0;
+ }
+ else
+ {
+ GV_currentNode = GV_currentNode ->getParent();
+ }
+ break;
+ case 'c' :
+ case '4':
+ if(GV_currentNode->getParent() != NULL)
+ {
+ GV_currentNode->collapse();
+ GV_currentNode = GV_currentNode->getParent();
+ selected = 0;
+ start=0;
+ }
+ else
+ {
+ mgWarning("Already at top level");
+ }
+ break;
+ case '8': // up
+ if(selected >0) selected--;
+ if(selected < start) start = start - PAGE_JUMP;
+ break;
+ case '2': // down
+ if(selected < ((GV_currentNode->getChildren()).size()-1))
+ selected++;
+ if(selected >= start+DISPLAY_SIZE) start = start + PAGE_JUMP;
+ break;
+ case '9': // pgup
+ if(start >= PAGE_JUMP)
+ {
+ start -= PAGE_JUMP;
+ selected -= PAGE_JUMP;
+ }
+ else
+ mgDebug(6,"Can not go up %d %d", start, selected);
+ break;
+ case '3': // pgdown
+ mgDebug(6,"pgdown %d %d", start, selected);
+ if(start+ PAGE_JUMP< (GV_currentNode->getChildren()).size())
+ {
+ start += PAGE_JUMP;
+ selected += PAGE_JUMP;
+ if(selected >= ((GV_currentNode->getChildren()).size()))
+ selected = (GV_currentNode->getChildren()).size();
+ }
+
+ break;
+ case '5': // OK ,add to playlist
+ tracks = GV_treeDisplay[selected]->getTracks();
+ GV_currentPlaylist->appendList(tracks);
+ tracks = NULL;
+ break;
+ case 'p': // go to playlist view
+ cmd = getchar();
+ return PLAYLIST_VIEW;
+ break;
+ case 't': // tree view (on root node)
+ while(GV_currentNode->getParent() != NULL)
+ {
+ GV_currentNode->collapse();
+ GV_currentNode = GV_currentNode->getParent();
+ }
+ selected = 0;
+ start=0;
+ break;
+ case 'q': // go to playlist view
+ cmd = getchar();
+ return EXIT;
+ break;
+ default:
+ mgWarning("Invalid Command '%c'",cmd);
+
+ }
+ cmd = getchar();
+
+ }// end nodeview loop
+ return EXIT;
+}
+int main (int argc, char **argv)
+{
+ mgMedia* media;
+ string tab;
+ bool loop = true;
+ /* now to connect to the database */
+ mgSelectionTreeNode *root;
+ int activeScreen;
+
+ vector<int> listcols;
+ mgSetDebugLevel(8);
+ media = new mgMedia(mgMedia::GD_MP3);
+
+ mgDebug(3," Staring sh_muggle");
+ // create initial tree node in view '1'
+ activeScreen = TREE_VIEW;
+ root = media->getSelectionRoot();
+ root->expand();
+ GV_currentNode = root->getChildren()[0];
+ GV_currentPlaylist = media->createTemporaryPlaylist();
+ listcols.push_back(1);
+ listcols.push_back(0);
+ GV_currentPlaylist->setDisplayColumns(listcols);
+ mgDebug(3," Entering sh_muggle main loop");
+
+ // now switch to the initial view;
+ while(loop)
+ {
+ switch(activeScreen)
+ {
+ case TREE_VIEW:
+ activeScreen = NodeView();
+ break;
+ case PLAYLIST_VIEW:
+ activeScreen = PlaylistView();
+ break;
+ case EXIT:
+ exit(0);
+ break;
+ default:
+ mgError("Invalid screen %d");
+ }
+ }
+ mgError("leavingNodeView");
+
+}
diff --git a/muggle-plugin/sh_muggle2.c b/muggle-plugin/sh_muggle2.c
new file mode 100644
index 0000000..e3748a4
--- /dev/null
+++ b/muggle-plugin/sh_muggle2.c
@@ -0,0 +1,66 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "mysql/mysql.h"
+
+#include "myosd.h"
+#include "muggle.h"
+#include "mgmedia.h"
+#include "muggle_tools.h"
+#include <unistd.h>
+
+eKeys waitForKey()
+{
+ char buf[2];
+ read(0, buf, 2);
+ mgDebug(9, "Key '%c' read\n",buf[0]);
+ switch(buf[0])
+ {
+ case '8':
+ return kUp;
+ case '2':
+ return kDown;
+ case '1':
+ return kMenu;
+ case '5':
+ return kOk;
+ case '3':
+ return kBack;
+ case '4':
+ return kLeft;
+ case '6':
+ return kRight;
+ case 'r':
+ return kRed;
+ case 'g':
+ return kGreen;
+ case 'y':
+ return kYellow;
+ case 'b':
+ return kBlue;
+ default:
+ return kNone;
+
+ }
+}
+int main (int argc, char **argv)
+{
+
+ cOsdObject *mainMenu;
+ eKeys key;
+ eOSState state;
+ bool loop=true;
+ mgMuggle muggle;
+ muggle.Initialize();
+ muggle.Start();
+ mgSetDebugLevel(8);
+
+ mainMenu = muggle.MainMenuAction();
+
+ while(loop)
+ {
+ key = waitForKey();
+ state = mainMenu->ProcessKey(key);
+ if(state == osEnd)
+ loop = false;
+ }
+}
diff --git a/muggle-plugin/sh_plugin.c b/muggle-plugin/sh_plugin.c
new file mode 100644
index 0000000..eff5cff
--- /dev/null
+++ b/muggle-plugin/sh_plugin.c
@@ -0,0 +1,77 @@
+/*
+ * plugin.c: The VDR plugin interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: sh_plugin.c,v 1.1 2004/02/01 18:22:53 LarsAC Exp $
+ */
+
+#include "shell_plugin.h"
+
+#define LIBVDR_PREFIX "libvdr-"
+#define SO_INDICATOR ".so."
+
+#define MAXPLUGINARGS 1024
+#define HOUSEKEEPINGDELTA 10 // seconds
+
+// --- cPlugin ---------------------------------------------------------------
+
+char *cPlugin::configDirectory = NULL;
+
+cPlugin::cPlugin(void)
+{
+ name = NULL;
+}
+
+cPlugin::~cPlugin()
+{
+ // I18nRegister(NULL, Name());
+}
+
+void cPlugin::SetName(const char *s)
+{
+ name = s;
+}
+
+const char *cPlugin::CommandLineHelp(void)
+{
+ return NULL;
+}
+
+bool cPlugin::ProcessArgs(int argc, char *argv[])
+{
+ return true;
+}
+
+bool cPlugin::Initialize(void)
+{
+ return true;
+}
+
+bool cPlugin::Start(void)
+{
+ return true;
+}
+
+void cPlugin::Housekeeping(void)
+{
+}
+
+const char *cPlugin::MainMenuEntry(void)
+{
+ return NULL;
+}
+
+cOsdObject *cPlugin::MainMenuAction(void)
+{
+ return NULL;
+}
+bool cPlugin::SetupParse(const char *Name, const char *Value)
+{
+ return false;
+}
+cMenuSetupPage *cPlugin::SetupMenu(void)
+{
+ return NULL;
+}
diff --git a/muggle-plugin/sh_plugin.h b/muggle-plugin/sh_plugin.h
new file mode 100644
index 0000000..2bf7bcb
--- /dev/null
+++ b/muggle-plugin/sh_plugin.h
@@ -0,0 +1,46 @@
+/*
+ * plugin.h: The VDR plugin interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: sh_plugin.h,v 1.1 2004/02/01 18:22:53 LarsAC Exp $
+ */
+
+#ifndef __PLUGIN_H
+#define __PLUGIN_H
+
+#include "myosd.h"
+#include "mymenuitems.h"
+typedef cOsdMenu cOsdObject;
+#define VDRPLUGINCREATOR(PluginClass) extern "C" void *VDRPluginCreator(void) { return new PluginClass; }
+
+class cPlugin {
+private:
+ static char *configDirectory;
+ const char *name;
+ void SetName(const char *s);
+public:
+ cPlugin(void);
+ virtual ~cPlugin();
+
+ const char *Name(void) { return name; }
+ virtual const char *Version(void) = 0;
+ virtual const char *Description(void) = 0;
+ virtual const char *CommandLineHelp(void);
+
+ virtual bool ProcessArgs(int argc, char *argv[]);
+ virtual bool Initialize(void);
+ virtual bool Start(void);
+ virtual void Housekeeping(void);
+
+ virtual const char *MainMenuEntry(void);
+ virtual cOsdObject *MainMenuAction(void);
+
+ virtual cMenuSetupPage *SetupMenu(void);
+
+ virtual bool SetupParse(const char *Name, const char *Value);
+
+ };
+
+#endif //__PLUGIN_H
diff --git a/muggle-plugin/vdr_menu.c b/muggle-plugin/vdr_menu.c
new file mode 100644
index 0000000..d706e94
--- /dev/null
+++ b/muggle-plugin/vdr_menu.c
@@ -0,0 +1,523 @@
+/*******************************************************************/
+/*! \file vdr_menu.c
+ * \brief Implements menu handling for broswing media libraries within VDR
+ ********************************************************************
+ * \version $Revision: 1.1 $
+ * \date $Date: 2004/02/01 18:22:53 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author file owner: $Author: LarsAC $
+ */
+/*******************************************************************/
+
+#include <vdr/menuitems.h>
+#include <vdr/tools.h>
+#include <mysql/mysql.h>
+
+#include "vdr_menu.h"
+
+#include "mg_content_interface.h"
+#include "mg_tools.h"
+#include "mg_media.h"
+
+#include "gd_content_interface.h"
+
+#include <string>
+#include <vector>
+
+using namespace std;
+
+static const char* alpha_num_keys = "abcdefghijklmnopqrstuvwxyz0123456789-";
+
+// ----------------------- mgMenuTreeItem ------------------
+
+mgMenuTreeItem::mgMenuTreeItem( mgSelectionTreeNode *node )
+ : m_node( node )
+{
+ Set();
+}
+
+mgSelectionTreeNode* mgMenuTreeItem::Node()
+{
+ return m_node;
+}
+
+void mgMenuTreeItem::Set()
+{
+ char *buffer = 0;
+ asprintf( &buffer, m_node->getLabel().c_str() );
+ SetText( buffer, false );
+}
+
+// ----------------------- mgMenuTrackItem ------------------
+
+
+// ----------------------- mgMainMenu ----------------------
+
+mgMainMenu::mgMainMenu()
+ : cOsdMenu( "" )
+{
+ mgDebug( 1, "Creating Muggle Main Menu" );
+
+ SetTitle( "Muggle Media Database" );
+ SetButtons();
+
+ m_media = new mgMedia( mgMedia::GD_MP3 );
+ m_root = m_media->getSelectionRoot();
+
+ m_filtername = new char[32];
+ strcpy( m_filtername, "none" );
+
+ m_title = new char[32];
+ strcpy( m_title, "none" );
+
+ m_interpret = new char[32];
+ strcpy( m_interpret, "none" );
+
+ m_album = new char[32];
+ strcpy( m_album, "none" );
+
+ m_playlist = new char[32];
+ strcpy( m_playlist, "none" );
+
+ m_year_min = 1900;
+ m_year_max = 2100;
+
+ m_filter = 0;
+
+ m_current_playlist = m_media->createTemporaryPlaylist();
+ m_tracklist = NULL;
+
+ DisplayTree( m_root );
+}
+
+mgSelectionTreeNode *mgMainMenu::CurrentNode()
+{
+ mgMenuTreeItem *item = (mgMenuTreeItem *)Get( Current() );
+ return item? item->Node(): 0;
+}
+
+void mgMainMenu::SetButtons( )
+{
+ SetHasHotkeys();
+
+ if( m_state == TREE )
+ {
+ SetHelp( "Add", "Filter", "Playlist", "Change View" );
+ }
+ else if( m_state == PLAYLIST )
+ {
+ SetHelp( "Tracks", "Filter", "Edit PL", "Tree" );
+ }
+ else if( m_state == PLAYLIST_TRACKINFO )
+ {
+ SetHelp( "Play/Pause", "Album Info", "Filter", "Tree" );
+ }
+ else if( m_state == PLAYLIST_ALBUMINFO )
+ {
+ SetHelp( "Play/Pause", "Filter", "Playlist", "Tree" );
+ }
+ else if( m_state == FILTER )
+ {
+ SetHelp( "Add", "Playlist", "Filter", "Tree" );
+ }
+ else if( m_state == TRACKS )
+ {
+ SetHelp( "Add", "Filter", "Playlist", "Tree" );
+ }
+}
+
+eOSState mgMainMenu::ProcessKey(eKeys key)
+{
+ mgDebug( 1, "mgMainMenu::ProcessKey" );
+ eOSState state = cOsdMenu::ProcessKey(key);
+
+ if( m_state == PLAYLIST )
+ {
+ mgDebug( 1, "mgMainMenu: in state PLAYLIST" );
+ if( state == osUnknown )
+ {
+ switch( key )
+ {
+ case kOk:
+ {
+ mgDebug( 1, "mgMainMenu: Mark" );
+ Mark(); // Mark (to move), moving done by VDR, calls Move
+ state = osContinue;
+ } break;
+ case kRed:
+ {
+ mgDebug( 1, "mgMainMenu: " );
+ // Shuffle?
+ }
+ case kYellow:
+ {
+ if( m_state == PLAYLIST )
+ {
+ mgDebug( 1, "mgMainMenu: switch to TrackInfo" );
+ DisplayTrackInfo();
+ }
+ else if( m_state == PLAYLIST_TRACKINFO )
+ {
+ mgDebug( 1, "mgMainMenu: switch to AlbumInfo" );
+ DisplayAlbumInfo();
+ }
+ else if( m_state == PLAYLIST_ALBUMINFO )
+ {
+ mgDebug( 1, "mgMainMenu: switch to Playlist" );
+ DisplayPlaylist();
+ }
+ state = osContinue;
+ } break;
+ case kBlue:
+ {
+ mgDebug( 1, "mgMainMenu: switch to TreeView" );
+ DisplayTree( m_root );
+ state = osContinue;
+ } break;
+ case kGreen:
+ {
+ mgDebug( 1, "mgMainMenu: switch to Filter" );
+ DisplayFilter();
+ state = osContinue;
+ } break;
+ default:
+ {
+ mgDebug( 1, "mgMainMenu: default" );
+ state = osContinue;
+ };
+ }
+ }
+ }
+ else if( m_state == FILTER )
+ {
+ mgDebug( 1, "mgMainMenu: in state FILTER" );
+
+ if( state == osUnknown )
+ {
+ switch( key )
+ {
+ case kOk:
+ {
+ // OK: Create filter and selection tree and display
+ mgDebug( 1, "mgMainMenu: create and apply filter" );
+ } break;
+ case kRed: // ???
+case kYellow:
+ {
+ // Yellow always goes to playlist view
+ mgDebug( 1, "mgMainMenu: switch to playlist" );
+ DisplayPlaylist();
+ state = osContinue;
+ } break;
+ case kGreen:
+ {
+ mgDebug( 1, "mgMainMenu: switch to filter" );
+ // Green: select other filters
+ DisplayFilterSelector();
+ } break;
+ case kBlue:
+ {
+ // Blue: treeview
+ mgDebug( 1, "mgMainMenu: switch to treeview" );
+ DisplayTree( m_root );
+ state = osContinue;
+ }
+ default:
+ {
+ state = osContinue;
+ }
+ }
+ }
+ }
+ else if( m_state == TREE )
+ {
+ mgDebug( 1, "mgMainMenu: in state TREE" );
+ // Navigate with up/dn, left/right (pgup, pgdn)
+ // Expand with OK, Collapse with Back
+ if( state == osUnknown )
+ {
+ switch( key )
+ {
+ case kOk:
+ {
+ mgDebug( 1, "mgMainMenu: switch to filter" );
+ mgSelectionTreeNode *child = CurrentNode(); // m_node->getChildren()[ Current() ];
+ DisplayTree( child );
+
+ state = osContinue;
+ } break;
+ case kRed:
+ {
+ mgSelectionTreeNode *current = CurrentNode();
+ if( current )
+ {
+ mgDebug( 1, "mgMainMenu: add selection %s to playlist", current->getLabel().c_str() );
+ // Add selection to Play
+ vector<mgContentItem*> *tracks = current->getTracks();
+
+ if( tracks )
+ {
+ m_current_playlist->appendList(tracks);
+
+ char buffer[256];
+ sprintf( buffer, "%d tracks sent to current playlist", (int) tracks->size() );
+ Interface->Status( buffer );
+ Interface->Flush();
+ }
+ else
+ {
+ mgDebug(1, "No tracks for current selection" );
+ }
+ }
+ else
+ {
+ mgDebug(1, "Cannot find currently selected node!" );
+ }
+ state = osContinue;
+ } break;
+ case kYellow:
+ {
+ mgDebug( 1, "mgMainMenu: display playlist" );
+ // Yellow always goes to playlist view
+ DisplayPlaylist();
+ state = osContinue;
+ } break;
+ case kGreen:
+ {
+ mgDebug( 1, "mgMainMenu: display filter" );
+ DisplayFilter();
+ state = osContinue;
+ } break;
+ case kBlue:
+ {
+ mgDebug( 1, "mgMainMenu: select other tree view" );
+ // Select other views -> Select other Media
+ DisplayTreeViewSelector();
+ state = osContinue;
+ } break;
+ default:
+ {
+ state = osContinue;
+ } break;
+ }
+ }
+ else if( state == osBack )
+ {
+ mgSelectionTreeNode *parent = m_node->getParent();
+
+ if( parent )
+ {
+ mgDebug( 1, "mgMainMenu: collapse current node" );
+ m_node->collapse();
+ DisplayTree( parent );
+ }
+ state = osContinue;
+ }
+ }
+ else if( m_state == TRACKS )
+ {
+ // Navigate with up/dn, left/right (pgup, pgdn)
+ if( state == osUnknown )
+ {
+ switch( key )
+ {
+ case kOk:
+ {
+ // Show Song Info
+ state = osContinue;
+ } break;
+ case kRed:
+ {
+ // Add item to Playlist
+ if(Current() >= 0 && Current() < (int) m_tracklist->getNumItems())
+ {
+ mgContentItem* item = m_tracklist->getItem( Current() );
+ mgDebug(3, "Ading item %s to playlist",
+ item->getTitle().c_str() );
+
+ m_current_playlist->append( item );
+ }
+ state = osContinue;
+ } break;
+ case kGreen:
+ {
+ // Green always goes to the filter view
+ DisplayFilter();
+ state = osContinue;
+ } break;
+ case kYellow:
+ {
+ // Yellow always goes to playlist view
+ DisplayPlaylist();
+ state = osContinue;
+ } break;
+ case kBlue:
+ {
+ DisplayTree( m_root );
+ state = osContinue;
+ } break;
+ default:
+ {
+ state = osContinue;
+ } break;
+ }
+ }
+ else if( state == osBack )
+ {
+ // where to go on back?
+ state = osContinue;
+ }
+ }
+ else
+ {
+ mgDebug(1, "Process key: else");
+ }
+
+ return state;
+}
+
+void mgMainMenu::Move( int from, int to )
+{
+ // check current view, perform move in the content view
+ if( m_state == PLAYLIST )
+ {
+ // resort
+ }
+}
+
+void mgMainMenu::DisplayTracklist()
+{
+ m_state = TRACKS;
+ mgDebug( 1, "mgBrowseMenu::DisplayTracklist");
+
+ Clear();
+ SetButtons();
+
+ mgDebug( 1, "mgBrowseMenu::DisplayTracklist: %d elements received",
+ m_tracklist->getNumItems() );
+
+ static char titlestr[80];
+ sprintf( titlestr, "Muggle Tracklist (%d items)", m_tracklist->getNumItems() );
+ SetTitle( titlestr );
+
+ // create tracklist with the current filters
+ if( m_tracklist )
+ {
+ delete m_tracklist;
+ }
+
+ m_tracklist = m_media->getTracks();
+
+ for( unsigned int i = 0; i < m_tracklist->getNumItems(); i++)
+ {
+ string label = m_tracklist->getLabel( i, " " );
+ Add( new cOsdItem( label.c_str() ) );
+ }
+
+ Display();
+}
+
+void mgMainMenu::DisplayPlaylist()
+{
+ m_state = PLAYLIST;
+ mgDebug( 1, "mgBrowseMenu::DisplayPlaylist");
+
+ // make sure we have a current playlist
+ Clear();
+
+ SetButtons();
+
+ vector<mgContentItem*>* list = m_current_playlist-> getAll();
+ static char titlestr[80];
+ sprintf( titlestr, "Muggle Playlist (%d items)", list->size() );
+ SetTitle( titlestr );
+
+ mgDebug( 1, "mgBrowseMenu::DisplayPlaylist: %d elements received",
+ list->size() );
+
+ for( unsigned int i = 0; i < m_current_playlist->getNumItems(); i++)
+ {
+ string label = m_current_playlist->getLabel( i, " " );
+ Add( new cOsdItem( label.c_str() ) );
+ }
+
+ Display();
+}
+
+void mgMainMenu::DisplayTrackInfo()
+{
+ m_state = PLAYLIST_TRACKINFO;
+ // show info of the currently playing track
+}
+
+void mgMainMenu::DisplayAlbumInfo()
+{
+ m_state = PLAYLIST_ALBUMINFO;
+ // show info of the currently playing track
+}
+
+void mgMainMenu::DisplayTree( mgSelectionTreeNode* node )
+{
+ m_state = TREE;
+
+ if( node->expand( ) )
+ {
+ Clear();
+
+ char buffer[256];
+ sprintf( buffer, "Muggle - %s", node->getLabel().c_str() );
+
+ SetTitle( buffer );
+ SetButtons();
+
+ m_node = node;
+ mgDebug( 1, "mgBrowseMenu::DisplaySelection: node %s received", node->getLabel().c_str() );
+ vector<mgSelectionTreeNode*> children = node->getChildren();
+
+ mgDebug( 1, "mgBrowseMenu::DisplaySelection: %d elements received", children.size() );
+
+ for( vector<mgSelectionTreeNode*>::iterator iter = children.begin();
+ iter != children.end();
+ iter ++ )
+ {
+ Add( new mgMenuTreeItem( *iter ) );
+ }
+
+ mgDebug( 1, "mgBrowseMenu::DisplayNode: Children added to OSD" );
+ }
+ Display();
+}
+
+void mgMainMenu::DisplayTreeViewSelector()
+{
+}
+
+void mgMainMenu::DisplayFilter()
+{
+ m_state = FILTER;
+ Clear();
+
+ mgDebug( 1, "Creating Muggle filter view" );
+ SetButtons();
+
+ SetTitle( "Muggle Filter View" );
+
+ Add( new cMenuEditStrItem( "Name ", m_filtername, 32, alpha_num_keys ) );
+ Add( new cMenuEditBoolItem( "Filter ", &m_filter, "Off", "On" ) );
+
+ // Separator???
+
+ Add( new cMenuEditStrItem( "Title ", m_title, 32, alpha_num_keys ) );
+ Add( new cMenuEditStrItem( "Interpret", m_interpret, 32, alpha_num_keys ) );
+ Add( new cMenuEditStrItem( "Album ", m_album, 32, alpha_num_keys ) );
+ Add( new cMenuEditStrItem( "Playlist ", m_playlist, 32, alpha_num_keys ) );
+ Add( new cMenuEditIntItem( "Year from", &m_year_min, 1600, 9999 ) );
+ Add( new cMenuEditIntItem( "Year to ", &m_year_max, 1600, 9999 ) );
+
+ Display();
+}
+
+void mgMainMenu::DisplayFilterSelector()
+{
+
+}
diff --git a/muggle-plugin/vdr_menu.h b/muggle-plugin/vdr_menu.h
new file mode 100644
index 0000000..480c687
--- /dev/null
+++ b/muggle-plugin/vdr_menu.h
@@ -0,0 +1,87 @@
+/*******************************************************************/
+/*! \file vdr_menu.h
+ * \brief Implements menu handling for broswing media libraries within VDR
+ ********************************************************************
+ * \version $Revision: 1.1 $
+ * \date $Date: 2004/02/01 18:22:53 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author file owner: $Author: LarsAC $
+ */
+/*******************************************************************/
+
+#ifndef _VDR_MENU_H
+#define _VDR_MENU_H
+
+#include <vdr/osd.h>
+
+class mgMedia;
+class mgSelectionTreeNode;
+class mgPlaylist;
+class mgTracklist;
+
+class mgMenuTreeItem : public cOsdItem
+{
+ public:
+
+ mgMenuTreeItem( mgSelectionTreeNode *node );
+
+ mgSelectionTreeNode *Node();
+
+ void Set();
+
+ private:
+
+ mgSelectionTreeNode *m_node;
+
+};
+
+class mgMainMenu : public cOsdMenu
+{
+ public:
+
+ mgMainMenu();
+
+ mgSelectionTreeNode *CurrentNode();
+
+ eOSState ProcessKey(eKeys Key);
+ void Move( int from, int to);
+
+ protected:
+
+ enum MuggleStatus
+ {
+ TREE, FILTER, TRACKS,
+ PLAYLIST, PLAYLIST_TRACKINFO, PLAYLIST_ALBUMINFO,
+ };
+
+ // To be rewritten mode dependent
+ void SetButtons();
+
+ void DisplayTree( mgSelectionTreeNode *node );
+ void DisplayTreeViewSelector();
+
+ void DisplayFilter();
+ void DisplayFilterSelector();
+
+ void DisplayPlaylist();
+ void DisplayTracklist();
+ void DisplayTrackInfo();
+ void DisplayAlbumInfo();
+
+ private:
+
+ // content stuff
+ mgMedia *m_media;
+ mgSelectionTreeNode *m_root;
+ mgSelectionTreeNode *m_node;
+ mgPlaylist *m_current_playlist;
+ mgTracklist *m_tracklist;
+
+ // filter items
+ char *m_title, *m_interpret, *m_album, *m_playlist, *m_filtername;
+ int m_year_min, m_year_max, m_filter;
+
+ MuggleStatus m_state;
+};
+
+#endif