diff options
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | mg_incremental_search.c | 78 | ||||
-rw-r--r-- | mg_incremental_search.h | 36 | ||||
-rw-r--r-- | vdr_actions.c | 49 | ||||
-rw-r--r-- | vdr_menu.c | 221 | ||||
-rw-r--r-- | vdr_menu.h | 60 |
6 files changed, 369 insertions, 82 deletions
@@ -31,7 +31,7 @@ CXXFLAGS ?= -fPIC -O0 -Wall -Woverloaded-virtual -Wno-deprecated -g DVBDIR ?= ../../../../DVB VDRDIR ?= ../../../ -LIBDIR ?= /../../lib +LIBDIR ?= ../../lib TMPDIR ?= /tmp ### Allow user defined options to overwrite defaults: @@ -56,9 +56,10 @@ DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DMYSQLCLIENTVERSION=' ### The object files (add further files here): -OBJS = $(PLUGIN).o i18n.o mg_valmap.o mg_mysql.o mg_sync.o mg_thread_sync.o mg_order.o mg_content.o mg_selection.o vdr_actions.o vdr_menu.o mg_tools.o \ +OBJS = $(PLUGIN).o i18n.o mg_valmap.o mg_mysql.o mg_sync.o mg_thread_sync.o mg_order.o \ + mg_content.o mg_selection.o vdr_actions.o vdr_menu.o mg_tools.o \ vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o \ - vdr_setup.o mg_setup.o + vdr_setup.o mg_setup.o mg_incremental_search.o LIBS = -lmad $(shell taglib-config --libs) MILIBS = $(shell taglib-config --libs) diff --git a/mg_incremental_search.c b/mg_incremental_search.c new file mode 100644 index 0000000..58b2147 --- /dev/null +++ b/mg_incremental_search.c @@ -0,0 +1,78 @@ +/*! \file mg_incremental_search.c + * \ingroup muggle + * \brief A class that encapsulates incremental search + * + * \version $Revision: $ + * \date $Date: $ + * \author Lars von Wedel + * \author file owner: $Author: $ + * + */ + +#include "mg_incremental_search.h" +#include <iostream> + +using namespace std; + +static char* keys[] = { " 0", + ".-_1", + "abc2", + "def3", + "ghi4", + "jkl5", + "mno6", + "pqrs7", + "tuv8", + "wxyz9" }; + +mgIncrementalSearch::mgIncrementalSearch() + : m_position(-1), m_repeats(0), m_last_key(100), m_last_keypress(0.0) +{ } + +string mgIncrementalSearch::KeyStroke( unsigned key ) +{ + struct timeval now ; + gettimeofday( &now, NULL ); + + double current_t = now.tv_sec + (double)now.tv_usec / 1000000.0; + double delta_t = current_t - m_last_keypress; + m_last_keypress = current_t; + + const double IS_TIMEOUT = 1.0; // 1 second + if( delta_t > IS_TIMEOUT || key != m_last_key ) + { + m_position ++; + + char tmp[2]; + tmp[0] = (keys[key])[0]; + tmp[1] = '\0'; + + m_buffer += string( tmp ); + + m_repeats = 0; + m_last_key = key; + } + else + { + // within timeout and have the same key + // position remains + m_repeats ++; + + if( (unsigned) m_repeats > strlen( keys[m_position] ) ) + { + // wrap around to first char + m_repeats = 0; + } + m_buffer[m_position] = (keys[key])[m_repeats]; + } + return m_buffer; +} + +string mgIncrementalSearch::Backspace() +{ + if( !m_buffer.empty() ) + { + m_buffer.erase( m_buffer.size()-1, 1 ); + } + return m_buffer; +} diff --git a/mg_incremental_search.h b/mg_incremental_search.h new file mode 100644 index 0000000..e125577 --- /dev/null +++ b/mg_incremental_search.h @@ -0,0 +1,36 @@ +/*! \file mg_incremental_search.h + * \ingroup muggle + * \brief A class that encapsulates incremental search + * + * \version $Revision: $ + * \date $Date: $ + * \author Lars von Wedel + * \author file owner: $Author: $ + * + */ + +/* makes sure we don't use the same declarations twice */ +#ifndef _MUGGLE_INCSEARCH_H +#define _MUGGLE_INCSEARCH_H + +#include <string> +#include <sys/time.h> + +class mgIncrementalSearch +{ + public: + mgIncrementalSearch(); + + std::string KeyStroke( unsigned key ); + + std::string Backspace(); + + private: + std::string m_buffer; + int m_position; + unsigned m_repeats, m_last_key; + + double m_last_keypress; +}; + +#endif diff --git a/vdr_actions.c b/vdr_actions.c index 1d7a0c5..2bb958d 100644 --- a/vdr_actions.c +++ b/vdr_actions.c @@ -2,7 +2,8 @@ * \file vdr_actions.c * \brief Implements all actions for browsing media libraries within VDR * - * \version $Revision: 1.27 $ * \date $Date: 2004-12-25 16:52:35 +0100 (Sat, 25 Dec 2004) $ + * \version $Revision: 1.27 $ + * \date $Date: 2004-12-25 16:52:35 +0100 (Sat, 25 Dec 2004) $ * \author Wolfgang Rohdewald * \author Responsible author: $Author: wr61 $ * @@ -367,18 +368,48 @@ mgEntry::Execute() eOSState mgEntry::Process(eKeys key) { - switch (key) { + eOSState result = osUnknown; + + mgTree *menu = dynamic_cast<mgTree*>(m); // und 0 abfangen + if( m ) + { + switch (key) + { case kOk: - Execute(); - return osContinue; - case k0: - osd()->RefreshTitle(); - return osContinue; + { + menu->TerminateIncrementalSearch( true ); + Execute(); + + result = osContinue; + } break; + case k0...k9: + { + menu->UpdateIncrementalSearch( key ); + result = osContinue; + } break; case kBack: - return Back(); + { + if( menu->UpdateIncrementalSearch( key ) ) + { // search is continued + result = osContinue; + } + else + { // search is not active at all + result = Back(); + } + } break; default: - return osUnknown; + { + if( key != kNone ) + { + menu->TerminateIncrementalSearch( true ); + } + result = osUnknown; + } } + } + + return result; } @@ -29,6 +29,7 @@ #include "vdr_setup.h" #include "vdr_menu.h" #include "vdr_player.h" +#include "mg_incremental_search.h" #include "i18n.h" #define DEBUG @@ -678,8 +679,10 @@ mgMenu::ExecuteButton(eKeys key) mgTree::mgTree() { - TreeBlueAction = actShowCommands; - CollBlueAction = actShowCommands; + TreeBlueAction = actShowCommands; + CollBlueAction = actShowCommands; + m_incsearch = NULL; + m_start_position = 0; } eOSState @@ -688,10 +691,130 @@ mgMenu::Process (eKeys key) return ExecuteButton(key); } +void +mgTree::UpdateSearchPosition() +{ + int position = -1; + if( !m_incsearch || m_filter.empty() ) + { + position = m_start_position; + } + else + { + // find the first item starting with m_filter + int counter = 0; + cOsdItem *item = osd()->First(); + while( item && position < 0 ) + { + if( !strncasecmp( item->Text(), m_filter.c_str(), m_filter.size() ) ) + { + position = counter; + } + else + { + counter ++; + item = (cOsdItem *) item->Next(); + } + } + } + + // Set the title accordingly + osd()->RefreshTitle(); + + // Jump to the current item and refresh + osd()->newposition = position; + osd()->DisplayGoto(); +} + +bool +mgTree::UpdateIncrementalSearch( eKeys key ) +{ + bool result; // false if no search active and keystroke was not used + + if( !m_incsearch ) + { + switch( key ) + { + case k0...k9: + { // create a new search object as this is the first keystroke + m_incsearch = new mgIncrementalSearch(); + + // remember the position where we started to search + m_start_position = osd()->Current(); + + // interprete this keystroke + m_filter = m_incsearch->KeyStroke( key - k0 ); + result = true; + UpdateSearchPosition(); + } break; + default: + { + result = false; + } + } + } + else + { // an incremental search is already active + switch( key ) + { + case kBack: + { + m_filter = m_incsearch->Backspace(); + + if( m_filter.empty() ) + { // search should be terminated, returning to the previous item + TerminateIncrementalSearch( false ); + } + else + { // just find the first item for the current search string + UpdateSearchPosition(); + } + result = true; + } break; + case k0...k9: + { + // evaluate the keystroke + m_filter = m_incsearch->KeyStroke( key - k0 ); + result = true; + UpdateSearchPosition(); + } break; + default: + { + result = false; + } + } + } + return result; +} + +void mgTree::TerminateIncrementalSearch( bool remain_on_current ) +{ + if( m_incsearch ) + { + m_filter = ""; + delete m_incsearch; + m_incsearch = NULL; + + if( remain_on_current ) + { + m_start_position = osd()->Current(); + } + + UpdateSearchPosition(); + } +} + string mgTree::Title () const { - return selection ()->getListname (); + string title = selection ()->getListname (); + + if( !m_filter.empty() ) + { + title += " (" + m_filter + ")"; + } + + return title; } void @@ -732,63 +855,65 @@ eOSState mgMainMenu::ProcessKey (eKeys key) } else { - switch (key) - { - case kPause: - c->Pause (); - break; - case kStop: - if (instant_playing && queue_playing) { - PlayQueue(); - } - else - { - queue_playing = false; - c->Stop (); - } - break; - case kChanUp: - c->Forward (); - break; - case kChanDn: - c->Backward (); - break; - default: - goto otherkeys; + switch (key) + { + case kPause: + c->Pause (); + break; + case kStop: + if (instant_playing && queue_playing) + { + PlayQueue(); + } + else + { + queue_playing = false; + c->Stop (); + } + break; + case kChanUp: + c->Forward (); + break; + case kChanDn: + c->Backward (); + break; + default: + goto otherkeys; } goto pr_exit; } } else - if (key==kPlay) { - PlayQueue(); - goto pr_exit; - } + if (key==kPlay) + { + PlayQueue(); + goto pr_exit; + } otherkeys: newmenu = Menus.back(); // Default: Stay in current menu newposition = -1; - + { - mgMenu * oldmenu = newmenu; - -// item specific key logic: - result = cOsdMenu::ProcessKey (key); - -// mgMenu specific key logic: - if (result == osUnknown) - result = oldmenu->Process (key); + mgMenu * oldmenu = newmenu; + + // item specific key logic: + result = cOsdMenu::ProcessKey (key); + + // mgMenu specific key logic: + if (result == osUnknown) + result = oldmenu->Process (key); } -// catch osBack for empty OSD lists . This should only happen for playlistitems -// (because if the list was empty, no mgActions::ProcessKey was ever called) + // catch osBack for empty OSD lists . This should only happen for playlistitems + // (because if the list was empty, no mgActions::ProcessKey was ever called) if (result == osBack) { - // do as if there was an entry - mgAction *a = Menus.back()->GenerateAction(actEntry,actEntry); - if (a) - { - result = a->Back(); - delete a; - } + // do as if there was an entry + mgAction *a = Menus.back()->GenerateAction(actEntry,actEntry); + if (a) + { + result = a->Back(); + delete a; + } } // do nothing for unknown keys: @@ -14,7 +14,7 @@ #define _VDR_MENU_H #include <string> -#include <list> +// #include <list> #include <vector> #include <osd.h> @@ -24,8 +24,6 @@ #include "vdr_player.h" -using namespace std; - //! \brief play a selection, aborting what is currently played //! \param select if true, play only what the current position selects void Play(mgSelection *sel,const bool select=false); @@ -38,6 +36,7 @@ class cCommands; class mgSelection; class mgMenu; class mgMainMenu; +class mgIncrementalSearch; //! \brief if a player is running, return it mgPlayerControl * PlayerControl (); @@ -145,7 +144,7 @@ class mgMainMenu:public cOsdMenu } //! \brief this is the collection things will be added to - string default_collection; + std::string default_collection; /*! \brief this is the "now playing" collection translated in * the current language. When changing the OSD language, this @@ -154,7 +153,7 @@ class mgMainMenu:public cOsdMenu * previous language will stay, so the user can copy from the * old one to the new one. */ - string play_collection; + std::string play_collection; /*! \brief selects a certain line on the OSD and displays the OSD */ @@ -177,13 +176,13 @@ class mgMainMenu:public cOsdMenu // because that might do forcerefresh which overwrites the message void Message (const char *msg) { m_message = strdup(msg); } void Message1 (const char *msg, const char *arg1); - void Message1 (const char *msg, string arg1) { Message1(msg,arg1.c_str()); } + void Message1 (const char *msg, std::string arg1) { Message1(msg,arg1.c_str()); } //! \brief Actions can request a new position. -1 means none wanted int newposition; //! \brief clears the screen, sets a title and the hotkey flag - void InitOsd (string title,const bool hashotkeys); + void InitOsd (std::string title,const bool hashotkeys); #if VDRVERSNUM >= 10307 //! \brief expose the protected DisplayMenu() from cOsdMenu @@ -227,11 +226,11 @@ class mgMainMenu:public cOsdMenu bool DefaultCollectionSelected(); //! \brief true if the cursor is placed in the default collection - bool CollectionEntered(string name); + bool CollectionEntered(std::string name); void AddItem(mgAction *a); - void CollectionChanged(string name); + void CollectionChanged(std::string name); void CloseMenu(); @@ -261,7 +260,7 @@ class mgMenu void AddSelectionItems (mgSelection *sel,mgActions act = actEntry); //! \brief the name of the blue button depends of where we are int m_parent_index; - string m_parent_name; + std::string m_parent_name; public: /*! sets the correct help keys. * \todo without data from mysql, no key is shown, @@ -279,8 +278,8 @@ class mgMenu void setParentIndex(int idx) { m_parent_index = idx; } int getParentIndex() { return m_parent_index; } - void setParentName(string name) { m_parent_name = name; } - string getParentName() { return m_parent_name; } + void setParentName(std::string name) { m_parent_name = name; } + std::string getParentName() { return m_parent_name; } //! \brief the pointer to the owning mgMainMenu mgMainMenu* osd () const @@ -306,7 +305,7 @@ class mgMenu } //! \brief computes the title - virtual string Title() const = 0; + virtual std::string Title() const = 0; //! \brief clears the screen, sets a title and the hotkey flag void InitOsd (const bool hashotkeys=true); @@ -348,11 +347,28 @@ class mgMenu class mgTree:public mgMenu { public: + mgTree(); -//! \brief computes the title - string Title() const; + + //! \brief computes the title + std::string Title() const; + + bool UpdateIncrementalSearch( eKeys key ); + + void TerminateIncrementalSearch( bool remain_on_current ); + protected: void BuildOsd (); + + private: + + void UpdateSearchPosition(); + + mgIncrementalSearch *m_incsearch; + + std::string m_filter; + + int m_start_position; }; //! \brief an mgMenu class for submenus @@ -361,7 +377,7 @@ class mgSubmenu:public mgMenu public: mgSubmenu::mgSubmenu(); //! \brief computes the title - string Title() const; + std::string Title() const; protected: void BuildOsd (); }; @@ -371,7 +387,7 @@ class mgMenuOrders:public mgMenu { public: //! \brief computes the title - string Title() const; + std::string Title() const; protected: void BuildOsd (); }; @@ -382,7 +398,7 @@ class mgMenuOrder : public mgMenu mgMenuOrder(); ~mgMenuOrder(); //! \brief computes the title - string Title() const; + std::string Title() const; bool ChangeOrder(eKeys key); void SaveOrder(); protected: @@ -402,18 +418,18 @@ class mgTreeCollSelector:public mgMenu mgTreeCollSelector(); ~mgTreeCollSelector(); //! \brief computes the title - string Title() const; + std::string Title() const; protected: void BuildOsd (); virtual mgActions coll_action() = 0; - string m_title; + std::string m_title; }; class mgTreeAddToCollSelector:public mgTreeCollSelector { public: //! \brief computes the title - mgTreeAddToCollSelector(string title); + mgTreeAddToCollSelector(std::string title); protected: virtual mgActions coll_action() { return actAddCollEntry; } }; @@ -423,7 +439,7 @@ class mgTreeRemoveFromCollSelector:public mgTreeCollSelector { public: //! \brief computes the title - mgTreeRemoveFromCollSelector(string title); + mgTreeRemoveFromCollSelector(std::string title); protected: virtual mgActions coll_action() { return actRemoveCollEntry; } }; |