diff options
author | LarsAC <LarsAC@e10066b5-e1e2-0310-b819-94efdf66514b> | 2004-02-01 18:22:52 +0000 |
---|---|---|
committer | LarsAC <LarsAC@e10066b5-e1e2-0310-b819-94efdf66514b> | 2004-02-01 18:22:52 +0000 |
commit | 9ffff4626dbb713e2fa6e025868928393ebf2ae7 (patch) | |
tree | ebe95d53808375beaed9f2c4ea2d17e7e8cb01de | |
parent | 717ec9a2d6e43a61ccff152a8d887fdb8e8f303e (diff) | |
download | vdr-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
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 |