Subversion Repositories Projects

Rev

Rev 2147 | Blame | Last modification | View Log | RSS feed

/*****************************************************************************
 *   Copyright (C) 2013 Oliver Gemesi                                        *
 *                                                                           *
 *   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.          *
 *                                                                           *
 *   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.               *
 *****************************************************************************/


//############################################################################
//# HISTORY  menuctrl.c
//#
//# 11.06.2014 OG
//# - add: MenuCtrl_GetMenuIndex()
//#
//# 31.05.2014 OG
//# - chg: MenuCtrl_Refresh() - umgestellt auf PKT_KeylineUpDown()
//#
//# 14.05.2014 OG
//# - chg: include "../mk/paramset.h" geaendert auf "../mksettings/paramset.h"
//# - chg: include "../mk/mkparameters.h" geaendert auf "../mksettings/mkparameters.h"
//#
//# 11.05.2014 OG
//# - add: MenuCtrl_SetTitleFromParentItem()
//# - add: MenuCtrl_GetItemText(), MenuCtrl_IsItemTextPGM()
//# - chg: _MenuCtrl_Error() - auf PKT_Popup_P() umgestellt, Layout geaendert und
//#        einen neuen Fehler (NOPARENTMENU) hinzugefuegt
//#
//# 09.05.2014 OG
//# - fix: MenuCtrl_Refresh() Anzeige von deaktivierten MK-Parametern (Favoriten)
//#        Wenn MK-Paramketer-Menueeintraege deaktiviert sind werden sie vom
//#        aktuellen MK-Parameterset nicht unterstuetzt - dennoch wurde der
//#        aktuelle Wert des MK-Parameters im Menuepunkt (ganz rechts) angezeigt.
//#        Das ist jetzt behoben - es wird kein Wert angezeigt (stattdessen "***")
//#        und auch keine entsprechende editParam-Funktionen aufgerufen.
//#
//# 07.05.2014 OG
//# - add: MenuCtrl_PushSeparatorID()
//#
//# 06.05.2014 OG
//# - chg: MenuCtrl_Refresh() Keyline leicht angepasst
//# - chg: 'not possible' Anzeige (bei deaktiviertem Item) umgestellt auf
//#        PKT_Popup_P()
//# - add: MenuCtrl_GetItemIdByIndex(), MenuCtrl_GetItemCount()
//#
//# 05.05.2014 OG
//# - chg: MenuCtrl_Control()
//#        -> Unterstuetzung fuer: MenuCtrl_SetMove() - Menueeintraege verschieben
//# - chg: MenuCtrl_Refresh() - Anpassung Slider fuer MenuCtrl_SetDelete()
//# - add: MenuCtrl_SetDelete(), MenuCtrl_SetMove()
//# - add: _MenuCtrl_SwapItem()
//# - add: die Datenstruktur vom Menue wurde erweitert um die Eigenschaften
//#        canMove und canDelete
//#
//# 17.04.2014 OG
//# - chg: MENUCTRL_MAXITEMS von 22 auf 28 erhoeht
//#
//# 30.03.2014 OG
//# - del: MenuCtrl_PushML_P(), MenuCtrl_PushML() (Umstellung Sprachen abgeschlossen)
//# - add: MenuCtrl_PushML2(), MenuCtrl_PushML2_P() fuer Umstellung von 3 auf 2 Sprachen
//#
//# 29.03.2014 OG
//# - chg: default MENUDEFAULT_SHOWBATT auf true umgestellt (=PKT-Batterie anzeigen)
//# - chg: MenuCtrl_Control() umgestellt auf clear_key_all()
//#
//# 24.03.2014 OG
//# - add: MenuCtrl_PushSeparator() - fuegt eine Trennlinie im Menue ein
//# - chg: MenuCtrl_Refresh() Codeoptimierung bzgl. topspace/Zeile loeschen/Slider
//# - fix: MenuCtrl_Control() es wurde zuoft ein Screen-Refresh durchgefuehrt
//#
//# 23.03.2014 OG
//# - add: Unterstuetzung fuer MK-Parameter-Edit (mkparameters.c)
//# - chg: MenuCtrl_Refresh() Unterstuetzung fuer MENU_PARAMEDIT
//# - add: MenuCtrl_PushParamEdit()
//# - add: paramset.h und mkparameters.h
//#
//# 04.03.2014 OG
//# - fix: MenuCtrl_Refresh() bei einem blauen Display (das ist wohl schneller
//#        in der Anzeige als das S/W-Display) flackerte der Menue-Slider
//#
//# 17.02.2014 OG
//# - add: MenuCtrl_SetTopSpace()
//# - chg: MenuCtrl_Refresh() angepasst auf MenuCtrl_SetTopSpace()
//#
//# 15.02.2014 OG
//# - add: MenuCtrl_ItemSelect()
//#
//# 01.02.2014 OG
//# - chg: MENUCTRL_MAXITEMS von 16 auf 22 fuer MK_parameters
//# - fix: _MenuCtrl_Error() Anzeigezeit von Menu-Errors auf 8 Sekunden verlaengert
//#        und Anzeige von "menuctrl.c"
//#
//# 07.07.2013 OG
//# - add: MenuCtrl_ItemMarkR() - Markiert einen Menueeintrag mit einem Haken am ENDE (Rechts)
//#
//# 24.05.2013 OG
//# - chg: MenuCtrl_Push... Funktionen vereinheitlicht mit internen Aenderungen
//#        betrifft externe Funktionsaufrufe die geändert wurden
//# - chg: MenuCtrl_Refresh() Anpassungen und Optimierung
//# - add: MenuCtrl_ItemMark()
//# - del: MenuCtrl_PushSimple_P(), MenuCtrl_PushSimple()
//#
//# 23.05.2013 OG
//# - add: MenuCtrl_PushSimple_P(), MenuCtrl_PushSimple()
//# - fix: MenuCtrl_Control() Eventcode Rueckgabe bei KEY_ENTER
//#
//# 22.05.2013 OG
//# - fix: nach Aufruf einer Menuefunktion in MenuCtrl_Control() jetzt
//#        get_key_press(KEY_ALL)
//#
//# 21.05.2013 OG
//# - add: MenuCtrl_ShowLevel()
//#
//# 20.05.2013 OG
//# - chg: MenuCtrl_Control() wieder mit get_key_press(KEY_ALL) ergaenzt
//#        wenn aufgerufen mit MENUCTRL_EVENT oder erster Aufruf mit RETURN
//#
//# 20.05.2013 OG
//# - chg: Space am Anfang der Titelanzeige
//#
//# 19.05.2013 OG
//# - add: define MENU_SHOWLEVEL mit dem der aktuelle Menuelevel in der
//#        Titelzeile angezeigt werden kann
//# - fix: Redraw-Logik bei MENUCTRL_RETURN
//# - chg: MenuCtrl_Control() erweitert um PKT_Update()
//#
//# 18.05.2013 OG - NEU
//############################################################################

#include "../cpu.h"
#include <avr/io.h>
#include <inttypes.h>
#include <stdlib.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include <util/delay.h>
#include <string.h>         // memset
#include <stdarg.h>

#include "../main.h"
#include "../lcd/lcd.h"
#include "../eeprom/eeprom.h"
#include "../messages.h"
#include "../lipo/lipo.h"
#include "../menu.h"
#include "../pkt/pkt.h"
#include "../mksettings/paramset.h"
#include "../mksettings/mkparameters.h"
#include "menuctrl.h"


//#############################################################################################
//# internal defines & structs
//#############################################################################################

#define MENU_SHOWLEVEL          false   // Anzeige der Menue-Verschachtelungstiefe in der Titelzeile
                                        // zeigt dem Benutzer wie weit er wieder zurueckspringen muss
                                        // um in das Hauptmenue zu kommen

#define MENUDEFAULT_CYCLE       false   // cycle: wenn Menue am Ende dann wieder zum Anfang
#define MENUDEFAULT_SHOWBATT    true    // showbatt: zeigt PKT-Batterie im Menuetitel ganz rechts
#define MENUDEFAULT_BEEP        true    // beep: wenn Cursor ueber Ende/Anfang


#define MENUCTRL_MAXMENUES  6           // Anzahl max. verschachlteter Menues
//#define MENUCTRL_MAXITEMS   22          // Anzahl max. Menu-Items pro Menue (verbraucht Speicher)
//#define MENUCTRL_MAXITEMS   28          // Anzahl max. Menu-Items pro Menue (verbraucht Speicher)
#define MENUCTRL_MAXITEMS   34          // Anzahl max. Menu-Items pro Menue (verbraucht Speicher)
#define ERROR_NOMENU        1           // Errorcode
#define ERROR_MAXMENU       2           // Errorcode
#define ERROR_MAXITEM       3           // Errorcode
#define ERROR_NOITEM        4           // Errorcode
#define ERROR_NOPARENTMENU  5           // Errorcode

//-----------------------------------------------------------
// typedef: scrollbox_key_t
//-----------------------------------------------------------
typedef struct
{
    uint8_t     active;             // Taste aktiv? (true/false)
    const char  *text;              // Tastentext
    void (*func)(void);             // ggf. Funktions-Pointer (0=keine Funktion)
}  menue_key_t;


//---------------------------
// typedef: einzelnes Menueitem
//---------------------------
typedef struct
{
    uint8_t     id;
    uint8_t     type;               // MENU_ITEM, MENU_SUB, MENU_PARAMEDIT
    uint8_t     disabled;           // true / false  (deaktiviert einen Menuepunkt)
    uint8_t     mark;               // true / false  (markiert einen Menuepunkt Links '*')
    uint8_t     markR;              // true / false  (markiert einen Menuepunkt Rechts)
    uint8_t     textPGM;            // true / false (true = text in PROGMEN; false = text im RAM)
    uint8_t     textcount;          // 1 .. NUM_LANG
    const char *text[NUM_LANG];     // de, en, nl  (NUM_LANG aus messages.h)
    void (*func)(void);
}  menue_item_t;


//---------------------------
// typedef: Menueitem Liste
//---------------------------
typedef struct
{
    uint8_t         active;         // aktives item
    uint8_t         itemcount;      // Anzahl Items
    uint8_t         scroll_pos;     // aktuelle obere Anzeigezeile
    uint8_t         display_pos;    // aktuelle obere Anzeigezeile
    uint8_t         topspace;       // obere Leerzeilen - default=0 (Vorsicht! ggf. nur bedingt einsetzbar bei Menues die ohne scrollen auskommen!)
    uint8_t         firstredraw;    // true / false  (flag fuer ein erstes redraw bei MENUCTRL_RETURN)
    uint8_t         cycle;          // true / false
    uint8_t         showbatt;       // true / false
    uint8_t         beep;           // true / false
    uint8_t         canMove;        // true / false
    uint8_t         canDelete;      // false/0 = kein entfernen; true/1 = entfernen mit Nachfrage; 2 = entfernen ohne Nachfrage
    uint8_t         titlePGM;       // true / false
    const char      *title;         // Menuetitel (oberste Zeile) (ggf. Multisprache machen?)
    uint8_t         lastkey;        // verwendet von GetKey()
    menue_key_t     key_enter;
    menue_key_t     key_enter_long;
    menue_key_t     key_esc;
    menue_key_t     key_esc_long;
    menue_item_t    item[MENUCTRL_MAXITEMS];
}  menue_t;

uint8_t showlevel = MENU_SHOWLEVEL; // Anzeige Menulevel in der Titelanzeige
int8_t  midx = -1;                  // aktives Menue; -1 = kein Menue
menue_t menu[MENUCTRL_MAXMENUES];   // Stack fuer n geschachltete Menues



//#############################################################################################
//# PRIVAT funcs
//#############################################################################################


//--------------------------------------------------------------
// INTERN
//--------------------------------------------------------------
void _MenuCtrl_Error( uint8_t error )
{
    const char *pStr = NULL;

    switch( error )
    {
        case ERROR_NOMENU:          pStr = PSTR("NOMENU");       break;
        case ERROR_MAXMENU:         pStr = PSTR("MAXMENU");      break;
        case ERROR_MAXITEM:         pStr = PSTR("MAXITEM");      break;
        case ERROR_NOITEM:          pStr = PSTR("NOITEM");       break;
        case ERROR_NOPARENTMENU:    pStr = PSTR("NOPARENTMENU"); break;
    }

    set_beep( 1000, 0x000f, BeepNormal);                                    // Beep Error
    PKT_Popup_P( 10000, PSTR("menuctrl.c"), 0, PSTR("* ERROR *"), pStr );   // 10000 = max. 100 Sekunden anzeigen
}


//--------------------------------------------------------------
// INTERN
//--------------------------------------------------------------
int8_t _MenuCtrl_FindItem( uint8_t itemid )
{
    int8_t i;

    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return -1; }

    for( i=0; i<menu[midx].itemcount; i++)
    {
        if( itemid == menu[midx].item[i].id )
        {
            return i;   // found: return index
        }
    }
    return -1;          // not found
}


//--------------------------------------------------------------
// INTERN
//--------------------------------------------------------------
void _MenuCtrl_CalcDisplayPos( void )
{
    int8_t i;

    i = menu[midx].active - menu[midx].display_pos;

    if( i < 0 )
        menu[midx].display_pos = menu[midx].active;
    else if( i > 5 )
        menu[midx].display_pos = (menu[midx].active - 5);
}


//--------------------------------------------------------------
// INTERN
//--------------------------------------------------------------
void _MenuCtrl_Beep( void )
{
    if( menu[midx].beep )
        set_beep( 25, 0xffff, BeepNormal );
}


//--------------------------------------------------------------
// INTERN
// Dreieckstausch von zwei Menuepunkten.
// Wird verwendet um Menueitems zu verschieben.
//--------------------------------------------------------------
void _MenuCtrl_SwapItem( uint8_t itemindex_1, uint8_t itemindex_2 )
{
    menue_item_t tmpItem;

    memcpy( &tmpItem                      , &menu[midx].item[itemindex_1], sizeof(menue_item_t) );
    memcpy( &menu[midx].item[itemindex_1] , &menu[midx].item[itemindex_2], sizeof(menue_item_t) );
    memcpy( &menu[midx].item[itemindex_2] , &tmpItem, sizeof(menue_item_t) );
}



//#############################################################################################
//# PUBLIC funcs
//#############################################################################################


//--------------------------------------------------------------
// MenuCtrl_Create()
//--------------------------------------------------------------
void MenuCtrl_Create( void )
{
    if( midx >= MENUCTRL_MAXMENUES) { _MenuCtrl_Error( ERROR_MAXMENU ); return; }

    midx++;
    memset( &menu[midx], 0, sizeof(menue_t) );
    menu[midx].cycle        = MENUDEFAULT_CYCLE;
    menu[midx].showbatt     = MENUDEFAULT_SHOWBATT;
    menu[midx].beep         = MENUDEFAULT_BEEP;
}


//--------------------------------------------------------------
// MenuCtrl_Destroy()
//--------------------------------------------------------------
void MenuCtrl_Destroy( void )
{
    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return; }

    midx--;
}


//--------------------------------------------------------------
// MenuCtrl_ShowLevel( value )
//
// Diese Einstellung ist Global und wirkt sich direkt auf alle
// Menues aus!
// Man kann das also nicht fuer einzelne Menues ein-/ausschalten.
//
// Parameter:
//   value: true / false
//--------------------------------------------------------------
void MenuCtrl_ShowLevel( uint8_t value )
{
    showlevel = value;
}


//--------------------------------------------------------------
// MenuCtrl_SetTitle_P( *title )
//
// Parameter:
//   title: Menuetitel; Zeiger auf String PROGMEM
//--------------------------------------------------------------
void MenuCtrl_SetTitle_P( const char *title )
{
    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return; }

    menu[midx].title    = title;
    menu[midx].titlePGM = true;
}


//--------------------------------------------------------------
// MenuCtrl_SetTitle( *title )
//
// Parameter:
//   title: Menuetitel; Zeiger auf String im RAM
//--------------------------------------------------------------
void MenuCtrl_SetTitle( const char *title )
{
    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return; }

    menu[midx].title    = title;
    menu[midx].titlePGM = false;
}



//--------------------------------------------------------------
// MenuCtrl_SetTitleFromParentItem()
//
// setzte anhand des uebergeordneten Menuepunktes (dem aufrufenden
// Menuepunkt) den Titel des neuen Menues
//
// wird u.a. verwendet in setup.c
//--------------------------------------------------------------
void MenuCtrl_SetTitleFromParentItem( void )
{
    uint8_t     lang;
    const char *pStr;
    uint8_t     mparent;

    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU );       return; }
    if( midx < 1)   { _MenuCtrl_Error( ERROR_NOPARENTMENU ); return; }

    mparent = midx-1;

    //--------------------------
    // Sprache: Multilanguage oder 0 wenn wenn nur ein Text
    //--------------------------
    lang = (menu[mparent].item[ menu[mparent].active ].textcount == 1) ? 0 : Config.DisplayLanguage;

    //--------------------------
    // Text vom uebergeordneten Menuepunkt
    //--------------------------
    pStr = menu[mparent].item[ menu[mparent].active ].text[lang];

    //--------------------------
    // Titel setzen
    //--------------------------
    if( menu[mparent].item[ menu[mparent].active ].textPGM )
    {
        MenuCtrl_SetTitle_P( pStr );
    }
    else
    {
        MenuCtrl_SetTitle( pStr );
    }
}



//--------------------------------------------------------------
// MenuCtrl_SetCycle( value )
//
// Parameter:
//   value: true / false
//--------------------------------------------------------------
void MenuCtrl_SetCycle( uint8_t value )
{
    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return; }

    menu[midx].cycle    = value;
}



//--------------------------------------------------------------
// MenuCtrl_SetBeep( value )
//
// Parameter:
//   value: true / false
//--------------------------------------------------------------
void MenuCtrl_SetBeep( uint8_t value )
{
    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return; }

    menu[midx].beep = value;
}



//--------------------------------------------------------------
// MenuCtrl_SetMove( value )
//
// Schaltet in einem Menue die Moeglichkeit ein Menueeintraege nach
// oben oder nach unten zu verschieben.
// Zum verschieden eines Menueeintrages muessen die PLUS/MINUS Tasten
// lange gedrueckt werden.
//
// Parameter:
//   value: true / false (Default: false)
//--------------------------------------------------------------
void MenuCtrl_SetMove( uint8_t value )
{
    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return; }

    menu[midx].canMove = value;
}


//--------------------------------------------------------------
// MenuCtrl_SetDelete( value )
//
// Schaltet in einem Menue die Moeglichkeit ein Menueeintraege zu loeschen.
// Zum loeschen eines Menueeintrages muss dann die ganz rechte 'OK' Taste
// lange gedrueckt werden.
//
// Parameter:
//   value: true / false (Default: false)
//--------------------------------------------------------------
void MenuCtrl_SetDelete( uint8_t value )
{
    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return; }

    menu[midx].canDelete = value;
}


//--------------------------------------------------------------
// MenuCtrl_SetShowBatt( value )
//
// Parameter:
//   value: true / false
//--------------------------------------------------------------
void MenuCtrl_SetShowBatt( uint8_t value )
{
    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return; }

    menu[midx].showbatt = value;
}



//--------------------------------------------------------------
// MenuCtrl_SetTopSpace( n )
//
// fuegt oben im Menue n Leerzeichen ein um Abstand vom Menuetitel
// zu erhalten
//
// ACHTUNG! Das funktioniert nur mit Menues die nicht Scrollen!
// -> also weniger als 6 Eintraege haben (je nach Anzahl von TopSpace)
//
// ACHTUNG! Keine Absicherung bzgl. obigem Warnpunkt! Das kann man
// zwar machen - ist aber aktuell nicht so!
//
// Parameter:
//   value: 0..n  Anzahl der Leerzeilen ueber dem Menue
//--------------------------------------------------------------
void MenuCtrl_SetTopSpace( uint8_t n )
{
    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return; }

    menu[midx].topspace = n;
}



//--------------------------------------------------------------
// MenuCtrl_SetKey( key, *keytext, *keyfunc )
//
// Parameter:
//   key    : KEY_ENTER, KEY_ENTER_LONG, KEY_ESC, KEY_ESC_LONG
//   keytext: Pointer auf Text PROGMEM oder NOTEXT (=0)
//   keyfunc: Pointer auf Funktion oder NOFUNC (=0)
//--------------------------------------------------------------
void MenuCtrl_SetKey( uint8_t key, const char *keytext, void (*keyfunc)(void) )
{
    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return; }        // kein aktives Menue

    if( key == KEY_ENTER )
    {
        menu[midx].key_enter.active     = true;
        menu[midx].key_enter.text       = keytext;
        menu[midx].key_enter.func       = keyfunc;
    }

    if( key == KEY_ENTER_LONG )
    {
        menu[midx].key_enter_long.active = true;
        menu[midx].key_enter_long.text  = keytext;
        menu[midx].key_enter_long.func  = keyfunc;
    }

    if( key == KEY_ESC )
    {
        menu[midx].key_esc.active       = true;
        menu[midx].key_esc.text         = keytext;
        menu[midx].key_esc.func         = keyfunc;
    }

    if( key == KEY_ESC_LONG )
    {
        menu[midx].key_esc_long.active  = true;
        menu[midx].key_esc_long.text    = keytext;
        menu[midx].key_esc_long.func    = keyfunc;
    }
}


//--------------------------------------------------------------
// key = MenuCtrl_GetKey()
//
// Rueckgabe:
//   key: z.B. KEY_ENTER oder KEY_ENTER_LONG
//--------------------------------------------------------------
uint8_t MenuCtrl_GetKey( void )
{
    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return 0; }      // kein aktives Menue

    return menu[midx].lastkey;
}



//--------------------------------------------------------------
// menuindex = MenuCtrl_GetMenuIndex()
//
// gibt den aktuellen Menuindex zurueck
//
// Rueckgabe:
//   menuindex: <0 = keine Menue (-1)
//              =0 = Hauptmenue (Toplevel)
//              >0 = Untermenue Level
//--------------------------------------------------------------
int8_t MenuCtrl_GetMenuIndex( void )
{
    return midx;
}



//--------------------------------------------------------------
// itemid = MenuCtrl_GetItemId()
//
// gibt die itemID des aktuell angewaehlten Menuepunktes zurueck
//
// Rueckgabe:
//   itemid:
//--------------------------------------------------------------
uint8_t MenuCtrl_GetItemId( void )
{
    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return 0; }      // kein aktives Menue

    return menu[midx].item[ menu[midx].active ].id;
}



//--------------------------------------------------------------
// itemid = MenuCtrl_GetItemIdByIndex( index )
//
// damit koennen die aktuell vorhanden itemID's der Menuepunkte
// ermittelt wenn im Menue via SetMove oder SetDelete Menuepunkte
// durch den Benutzer verschoben oder geloescht wurden.
//
// Parameter:
//   index: von 0..itemcount-1
//--------------------------------------------------------------
uint8_t MenuCtrl_GetItemIdByIndex( uint8_t index )
{
    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return 0; }      // kein aktives Menue

    if( (menu[midx].itemcount == 0) || (index > menu[midx].itemcount-1) )   return 0;   // index ausserhalb aktiver Menuepunkte

    return menu[midx].item[ index ].id;
}



//--------------------------------------------------------------
// num = MenuCtrl_GetItemCount()
//
// gibt die Anzahl der Menuepunkte zurueck
//
// Rueckgabe:
//   num: Anzahl der Menuepunkte
//--------------------------------------------------------------
uint8_t MenuCtrl_GetItemCount( void )
{
    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return 0; }      // kein aktives Menue

    return menu[midx].itemcount;
}


//--------------------------------------------------------------
// textPtr = MenuCtrl_GetItemText()
//
// gibt einen Pointer auf den Text des aktuellen Menuepunktes zurueck
//
// ACHTUNG! Der Pointer kann auf RAM oder auch auf PGM zeigen!
//
// Um herauszufinden auf welchen Bereich der Pointer zeigt kann
// MenuCtrl_IsItemTextPGM() verwendet werden.
//
// Rueckgabe:
//   textPtr:  in RAM oder PGM
//--------------------------------------------------------------
const char * MenuCtrl_GetItemText( void )
{
    uint8_t lang;

    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return 0; }      // kein aktives Menue

    //--------------------------
    // Sprache: Multilanguage oder 0 wenn wenn nur ein Text
    //--------------------------
    lang = (menu[midx].item[ menu[midx].active ].textcount == 1) ? 0 : Config.DisplayLanguage;

    return menu[midx].item[ menu[midx].active ].text[lang];
}



//--------------------------------------------------------------
// isPGM = MenuCtrl_IsItemTextPGM()
//
// gibt true/false zurueck je nachdem der Text von MenuCtrl_GetItemText()
// im Progmem (=true) oder im RAM (=false) ist
//
// Rueckgabe:
//   isPGM: true / false (true = text in PROGMEN; false = text im RAM)
//--------------------------------------------------------------
uint8_t MenuCtrl_IsItemTextPGM( void )
{
    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return 0; }      // kein aktives Menue

    return menu[midx].item[ menu[midx].active ].textPGM;
}



//--------------------------------------------------------------
// MenuCtrl_ItemSelect( itemid )
//
// aktiviert einen Menuepunkt / setzt den Menue-Cursor darauf
//
// die Funktion ist nicht durchgetestet, funktioniert aber
// in MKSettings_Menu() / mkparameters.c
//
// es kann sein das es zu Problemen kommt wenn das Menue
// scrollt (mehr als 6 Menueeintraege)
//
// Parameter:
//   itemid: ID des Menuitems
//--------------------------------------------------------------
void MenuCtrl_ItemSelect( uint8_t itemid )
{
    int8_t i;

    i = _MenuCtrl_FindItem( itemid );

    if( i >= 0 )
    {
        menu[midx].active = i;
    }
}


//--------------------------------------------------------------
// MenuCtrl_ItemActive( itemid, value )
//
// Parameter:
//   itemid: ID des Menuitems
//   value : true / false
//--------------------------------------------------------------
void MenuCtrl_ItemActive( uint8_t itemid, uint8_t value )
{
    int8_t i;

    i = _MenuCtrl_FindItem( itemid );

    if( i >= 0 )
    {
        menu[midx].item[i].disabled = !value;
    }
}


//--------------------------------------------------------------
// MenuCtrl_ItemMark( itemid, value )
//
// Markiert einen Menueeintrag mit einem '*' am Anfang (Links).
// Wird u.a. von BT_SelectDevice() (setup.c) verwendet
//
// Parameter:
//   itemid: ID des Menuitems
//   value : true / false
//--------------------------------------------------------------
void MenuCtrl_ItemMark( uint8_t itemid, uint8_t value )
{
    int8_t i;

    i = _MenuCtrl_FindItem( itemid );

    if( i >= 0 )
    {
        menu[midx].item[i].mark = value;
    }
}


//--------------------------------------------------------------
// MenuCtrl_ItemMarkR( itemid, value )
//
// Markiert einen Menueeintrag mit einem Haken am ENDE (Rechts).
// Wird u.a. von ... (setup.c) verwendet
//
// Parameter:
//   itemid: ID des Menuitems
//   value : true / false
//--------------------------------------------------------------
void MenuCtrl_ItemMarkR( uint8_t itemid, uint8_t value )
{
    int8_t i;

    i = _MenuCtrl_FindItem( itemid );

    if( i >= 0 )
    {
        menu[midx].item[i].markR = value;
    }
}


//--------------------------------------------------------------
// INTERN
//
// fuer: MenuCtrl_PushML_P(), MenuCtrl_Push()
//--------------------------------------------------------------
void _MenuCtrl_Push( uint8_t useprogmem, uint8_t numlang, uint8_t itemid, uint8_t itemtype, void (*menufunc)(void), ... )
{
    va_list ap;
    uint8_t idx;
    uint8_t i;

    if( midx < 0  )                                 { _MenuCtrl_Error( ERROR_NOMENU );  return; }
    if( menu[midx].itemcount >= MENUCTRL_MAXITEMS ) { _MenuCtrl_Error( ERROR_MAXITEM ); return; }
    //if( numlang > NUM_LANG )                      { _MenuCtrl_Error( ERROR_MAXLANG ); return; }

    idx = menu[midx].itemcount;

    menu[midx].item[idx].id       = itemid;
    menu[midx].item[idx].type     = itemtype;   // MENU_ITEM, MENU_SUB
    menu[midx].item[idx].mark     = false;
    menu[midx].item[idx].func     = menufunc;
    menu[midx].item[idx].textPGM  = useprogmem;
    menu[midx].item[idx].textcount= numlang;

    va_start(ap, menufunc);

    for( i=0; i<numlang; i++)
    {
        menu[midx].item[idx].text[i]  = va_arg( ap, const char *);
    }

    va_end(ap);

    menu[midx].itemcount++;
}


//--------------------------------------------------------------
// MenuCtrl_PushML2_P( itemid, itemtype, *menufunc, *text_de, *text_en, *text_nl )
//
// MultiLanguage Texte fuer 2 Sprachen (DE, EN)
// PROGMEN Version
//
// Parameter:
//   itemid  : ID des Menueitems
//   itemtype: MENU_ITEM oder MENU_SUB (bei MENU_SUB wird noch ein ">" angezeigt)
//   menufunc: Pointer auf Funktion oder NOFUNC (=0) wenn keine Funktion
//   texte   : alles Zeiger auf PROGMEM
//--------------------------------------------------------------
void MenuCtrl_PushML2_P( uint8_t itemid, uint8_t itemtype, void (*menufunc)(void), const char *text_de, const char *text_en )
{
  //_MenuCtrl_Push( useprogmem, numlang, itemid, itemtype, void (*menufunc)(void), ... )
    _MenuCtrl_Push( true, NUM_LANG, itemid, itemtype, menufunc, text_de, text_en );
}


//--------------------------------------------------------------
// MenuCtrl_PushML2( itemid, itemtype, *menufunc, *text_de, *text_en, *text_nl )
//
// MultiLanguage Texte fuer 2 Sprachen (DE, EN)
// RAM Version
//
// Parameter:
//   itemid  : ID des Menueitems
//   itemtype: MENU_ITEM oder MENU_SUB (bei MENU_SUB wird noch ein ">" angezeigt)
//   menufunc: Pointer auf Funktion oder NOFUNC (=0) wenn keine Funktion
//   texte   : alles Zeiger auf RAM
//--------------------------------------------------------------
void MenuCtrl_PushML2( uint8_t itemid, uint8_t itemtype, void (*menufunc)(void), const char *text_de, const char *text_en )
{
  //_MenuCtrl_Push( useprogmem, numlang, itemid, itemtype, void (*menufunc)(void), ... )
    _MenuCtrl_Push( false, NUM_LANG, itemid, itemtype, menufunc, text_de, text_en );
}


//--------------------------------------------------------------
//--------------------------------------------------------------
void MenuCtrl_Push_P( uint8_t itemid, uint8_t itemtype, void (*menufunc)(void), const char *text )
{
  //_MenuCtrl_Push( useprogmem, numlang, itemid, itemtype, void (*menufunc)(void), ... )
    _MenuCtrl_Push( true, 1, itemid, itemtype, menufunc, text );
}


//--------------------------------------------------------------
//--------------------------------------------------------------
void MenuCtrl_Push( uint8_t itemid, uint8_t itemtype, void (*menufunc)(void), const char *text )
{
  //_MenuCtrl_Push( useprogmem, numlang, itemid, itemtype, void (*menufunc)(void), ... )
    _MenuCtrl_Push( false, 1, itemid, itemtype, menufunc, text );
}


//--------------------------------------------------------------
// Spezialfunktion fuer mkparameters.c !
//
// -> siehe: mkparameters.c / Menu_EditCategory()
//
// ACHTUNG!
// Die externe Variable paramEditItem muss in mkparameters.c
// vor Aufruf dieser Funktion richtig initialisiert sein!
// Ich habe darauf verzichtet hier nochmal ein find_paramEditItem()
// aufzurufen um Speicherplatz zu sparen.
//--------------------------------------------------------------
void MenuCtrl_PushParamEdit( uint8_t paramID )
{
  //_MenuCtrl_Push( useprogmem, numlang, itemid, itemtype, void (*menufunc)(void), ... )
    _MenuCtrl_Push( true, 2, paramID, MENU_PARAMEDIT, NOFUNC, paramEditItem.title_de, paramEditItem.title_en );
}



//--------------------------------------------------------------
// MenuCtrl_PushSeparator()
//
// fuegt eine Trennlinie im Menue ein
// (die itemID ist dabei 0)
//--------------------------------------------------------------
void MenuCtrl_PushSeparator( void )
{
  //_MenuCtrl_Push( useprogmem, numlang, itemid, itemtype, void (*menufunc)(void), ... )
    _MenuCtrl_Push( true, 0, 0, MENU_SEPARATOR, NOFUNC );
}



//--------------------------------------------------------------
// MenuCtrl_PushSeparatorID( itemId )
//
// fuegt eine Trennlinie im Menue ein mit einer eigenen itemID
//--------------------------------------------------------------
void MenuCtrl_PushSeparatorID( uint8_t itemid )
{
  //_MenuCtrl_Push( useprogmem, numlang, itemid, itemtype, void (*menufunc)(void), ... )
    _MenuCtrl_Push( true, 0, itemid, MENU_SEPARATOR, NOFUNC );
}



//--------------------------------------------------------------
// MenuCtrl_Refresh( mode )
//
// Parameter:
//   mode: MENU_REDRAW || MENU_REFRESH
//--------------------------------------------------------------
void MenuCtrl_Refresh( uint8_t mode )
{
    uint8_t y;
    uint8_t item;
    uint8_t sh;
    uint8_t sy;
    uint8_t actchar;
    uint8_t markchar;
    uint8_t markcharR;
    uint8_t lang;

    if( midx < 0)   { _MenuCtrl_Error( ERROR_NOMENU ); return; }            // kein Menue vorhanden

    if( mode == MENU_REDRAW )
    {
        //--------------------------
        // Clear
        //--------------------------
        lcd_frect( 0,  8, 6, 47, 0);                                        // Clear: ganz linke Spalte des Sliders
        lcd_frect( 127-2, 0, 2, 63, 0);                                     // Clear: ganz rechts 2 Pixel

        //--------------------------
        // Titel
        //--------------------------
        if( menu[midx].title != 0 )
        {
            if( showlevel )
            {
                if( menu[midx].titlePGM )
                    lcdx_printf_at_P( 0, 0, MINVERS, 0,0 , PSTR("  %19S"), menu[midx].title );
                else
                    lcdx_printf_at_P( 0, 0, MINVERS, 0,0 , PSTR("  %19s"), menu[midx].title );

                writex_ndigit_number_u( 0, 0, midx, 1, 0, MINVERS, 1,0);    // Menuelevel zeigen
            }
            else
            {
                if( menu[midx].titlePGM )
                    lcdx_printf_at_P( 0, 0, MINVERS, 0,0 , PSTR(" %20S"), menu[midx].title );
                else
                    lcdx_printf_at_P( 0, 0, MINVERS, 0,0 , PSTR(" %20s"), menu[midx].title );
            }
        }
        else
            lcd_frect( 0, 0, 128, 7, 1);                                       // Titel: Leerzeile


        //--------------------------
        // Keyline Beschriftung
        //--------------------------
        lcdx_cls_row( 7, MNORMAL, 0);
        PKT_KeylineUpDown( 1, 7,  0,0);
        lcd_printp_at( 12, 7, strGet(KEYLINE4), MNORMAL);


        // Taste: KEY_ENTER
        if( menu[midx].key_enter.active && menu[midx].key_enter.text != 0 )
        {
            lcdx_printf_at_P( 17, 7, MNORMAL, 0,0 , PSTR("%4S"), menu[midx].key_enter.text );
        }

        // Taste: KEY_ESC
        if( menu[midx].key_esc.active && menu[midx].key_esc.text != 0 )
        {
            lcdx_printf_at_P( 12, 7, MNORMAL, 0,0 , PSTR("%5S"), menu[midx].key_esc.text );
        }
    }


    //--------------------------
    // PKT Batterie Anzeige
    //--------------------------
    if( menu[midx].showbatt )
    {
        show_Lipo();
    }


    //--------------------------
    // Zeilen
    //--------------------------
    for( y=0; y<6; y++)
    {
        item = y + menu[midx].display_pos;

        if( item < menu[midx].itemcount )
        {
            //--------------------------
            // char: aktuelle Auswahl
            //--------------------------
            actchar = ' ';
            if( item == menu[midx].active )
            {
                if( menu[midx].item[item].disabled )    actchar = 0x15;         // aktuelle Auswahl: Pfeil disabled
                else                                    actchar = 0x1d;         // aktuelle Auswahl: Pfeil
            }

            //--------------------------
            // char: markiert
            //--------------------------
            markchar = ' ';
            if( menu[midx].item[item].mark ) markchar = '*';

            //--------------------------
            // char: markiert RECHTS
            //--------------------------
            markcharR = ' ';
            if( menu[midx].item[item].type == MENU_SUB ) markcharR = 0x1d;
            if( menu[midx].item[item].markR )            markcharR = SYMBOL_CHECK;

            //--------------------------
            // Sprache: Multilanguage oder 0 wenn wenn nur ein Text
            //--------------------------
            lang = (menu[midx].item[item].textcount == 1) ? 0 : Config.DisplayLanguage;

            //--------------------------
            // Ausgabe: RAM oder PROGMEM
            //--------------------------
            if( MENU_SEPARATOR == menu[midx].item[item].type )
            {
                lcdx_printf_at_P( 1, y+1+menu[midx].topspace, MNORMAL, 0,0 , PSTR("%c%c%18S")  , actchar, markchar, PSTR("") );
                lcd_frect( 6*3, ((y+1)*8)+3, 124-(6*3), 0, 1);                                  // Trennlinie
            }
            else if( MENU_PARAMEDIT == menu[midx].item[item].type )
            {
                // SPEZIELL fuer mkparameters.c - zeigt die paramID-Bezeichnung inkl. dem aktuellen WERT an!
                strcpy_P( mkparam_strValueBuffer, PSTR("***"));                                     // Vorbelegung von mkparam_strValueBuffer
                if( !menu[midx].item[item].disabled )                                               // nur wenn Menuepunkt nicht deaktiviert (disabled = paramID nicht vorhanden)
                {                                                                                   //   -> ermittle den aktuellen Wert der paramID fuer Anzeige
                    find_paramEditItem( menu[midx].item[item].id );                                 // paramID suchen (Ergebnis in Variable 'paramEditItem')
                    paramEditItem.editfunc( paramEditItem.paramID, MKPARAM_SHORTVALUE );            // Wert von paramID in mkparam_strValueBuffer darstellen
                }
                lcdx_printf_at_P( 1, y+1+menu[midx].topspace, MNORMAL, 0,0 , PSTR("%c%c%14S %3s")  , actchar, markchar, menu[midx].item[item].text[lang], mkparam_strValueBuffer );
            }
            else
            {
                // Anmerkung zu menu[midx].topspace: NUR fuer Menues OHNE scrollen!!
                if( menu[midx].item[item].textPGM )
                {
                    // String im PGM
                    if( menu[midx].item[item].type == MENU_SUB || menu[midx].item[item].markR )
                        lcdx_printf_at_P( 1, y+1+menu[midx].topspace, MNORMAL, 0,0 , PSTR("%c%c%16S%c "), actchar, markchar, menu[midx].item[item].text[lang], markcharR );
                    else
                        lcdx_printf_at_P( 1, y+1+menu[midx].topspace, MNORMAL, 0,0 , PSTR("%c%c%17S ")  , actchar, markchar, menu[midx].item[item].text[lang] );
                }
                else
                {
                    // String im RAM
                    if( menu[midx].item[item].type == MENU_SUB || menu[midx].item[item].markR )
                        lcdx_printf_at_P( 1, y+1+menu[midx].topspace, MNORMAL, 0,0 , PSTR("%c%c%16s%c "), actchar, markchar, menu[midx].item[item].text[lang], markcharR );
                    else
                        lcdx_printf_at_P( 1, y+1+menu[midx].topspace, MNORMAL, 0,0 , PSTR("%c%c%17s ")  , actchar, markchar, menu[midx].item[item].text[lang] );
                }
            }

        } // end: if( item < menu[midx].itemcount )


        //--------------------------
        // evtl. Leerzeile loeschen
        //--------------------------
        if( (y < menu[midx].topspace) || (item >= menu[midx].itemcount+menu[midx].topspace) )
        {
            lcd_frect( 6, (y+1)*8, 128-6, 7, 0);                                    // Zeile loeschen
        }

    }


    //--------------------------
    // Slider
    //--------------------------
    #define SLIDERH   45                                                            // Finetuning der Slider-Hoehe

    lcd_frect( 0,  8+1, 1, SLIDERH, 0);                                             // Slider: full Clear

    if( menu[midx].itemcount > 6 )                                                  // Slider nur bei mehr als 6 Menueitems
    {
        // Slider: 7 zeilen * 8 pixel = 56 pixel
        sh = (SLIDERH * 6) / menu[midx].itemcount;                                  // Slider: Hoehe
        sh = (sh > SLIDERH) ? SLIDERH : sh;

        sy = 8+(menu[midx].display_pos * (SLIDERH-sh)) / (menu[midx].itemcount-6);  // Slider: Position

        //lcd_frect( 0,  8+1, 1, SLIDERH, 0);                                       // Slider: full Clear    (flackert auf blauen Display!)
        //lcd_frect( 0, 8+1, 1, sy-(8), 0);                                         // Slider: Clear / oben  (nicht mehr notwendig da Aenderung am Refresh)
        //lcd_frect( 0, sy+1+sh+1, 1, SLIDERH-(sh-sy+1), 0);                        // Slider: Clear / unten (nicht mehr notwendig da Aenderung am Refresh)
        lcd_frect( 0, sy+1, 1, sh, 1);                                              // Slider: Draw
    }
}



//--------------------------------------------------------------
// event = MenuCtrl_Control( ctrlmode )
//
// Parameter:
//   ctrlmode: MENUCTRL_EVENT || MENUCTRL_RETURN
//
// Rueckgabe:
//   event: MENUEVENT_NONE || MENUEVENT_ITEM || MENUEVENT_KEY
//--------------------------------------------------------------
uint8_t MenuCtrl_Control( uint8_t ctrlmode )
{
    uint8_t menu_event;
    uint8_t refreshmode;
    uint8_t item;
    uint8_t wahl;
    uint8_t i;
    uint8_t goUp    = false;
    uint8_t goDown  = false;

    if( midx < 0)                   { _MenuCtrl_Error( ERROR_NOMENU ); return 0; }              // kein Menue vorhanden
    if( menu[midx].itemcount == 0)  { _MenuCtrl_Error( ERROR_NOITEM ); return 0; }              // kein Menueitem vorhanden


    // bei MENUCTRL_RETURN muss man ggf. selber fuer ein Redraw sorgen
    // (beim ersten Aufruf wird es jedoch dennoch erzwungen)
    if( ctrlmode == MENUCTRL_EVENT || !menu[midx].firstredraw )
    {
        MenuCtrl_Refresh( MENU_REDRAW );
        menu[midx].firstredraw = true;
    }

    clear_key_all();

    do
    {
        menu_event         = MENUEVENT_NONE;
        menu[midx].lastkey = KEY_NONE;
        refreshmode        = false;

        //--------------------------
        // Pruefe PKT Update oder
        // andere PKT-Aktion
        //--------------------------
        if( PKT_CtrlHook() )                                                                    // Update vom Updatetool angefordert?
        {
            refreshmode = MENU_REDRAW;
        }

        //--------------------------
        // PKT Batterie Anzeige
        //--------------------------
        if( menu[midx].showbatt )
        {
            show_Lipo();
        }


        //--------------------------
        // Cursor: nach oben (UP)
        //--------------------------
        //+++++
        // UP - Verschiebemodus AKTIV
        //+++++
        if( menu[midx].canMove )
        {
            if( get_key_long(1 << KEY_MINUS) )
            {
                if( (menu[midx].active > 0) && (menu[midx].itemcount > 0) )
                {
                    _MenuCtrl_SwapItem( menu[midx].active, menu[midx].active-1 );
                    goUp = true;
                }
                else _MenuCtrl_Beep();                                                           // am oberen Ende angelangt
            }

            if( get_key_short(1 << KEY_MINUS) )     // up
            {
                goUp = true;
            }
        }   // end: UP - Verschiebemodus AKTIV


        //+++++
        // UP - Verschiebemodus NICHT aktiv
        //+++++
        if( !menu[midx].canMove )     // up
        {
            if( get_key_press(1 << KEY_MINUS) || get_key_long_rpt_sp((1 << KEY_MINUS),2) )      // up
            {
                goUp = true;
            }
        }   // end: UP - Verschiebemodus NICHT aktiv


        //+++++
        // UP - goUp
        //+++++
        if( goUp )     // up
        {
            refreshmode = MENU_REFRESH;
            if( (menu[midx].active > 0)  && (menu[midx].itemcount > 0) )
            {
                menu[midx].active--;
            }
            else if( menu[midx].cycle )
            {
                menu[midx].active = menu[midx].itemcount-1;
            }
            else
            {
                _MenuCtrl_Beep();                                                               // am oberen Ende angelangt
            }
            _MenuCtrl_CalcDisplayPos();
        }


        //--------------------------
        // Cursor: nach unten (DOWN)
        //--------------------------
        //+++++
        // DOWN - Verschiebemodus AKTIV
        //+++++
        if( menu[midx].canMove )
        {
            if( get_key_long(1 << KEY_PLUS) )
            {
                if( menu[midx].active < menu[midx].itemcount-1 )
                {
                    _MenuCtrl_SwapItem( menu[midx].active, menu[midx].active+1 );
                    goDown = true;
                }
                else _MenuCtrl_Beep();                                                           // am unteren Ende angelangt
            }

            if( get_key_short(1 << KEY_PLUS) )     // up
            {
                goDown = true;
            }
        }   // end: DOWN - Verschiebemodus AKTIV


        //+++++
        // DOWN - Verschiebemodus NICHT aktiv
        //+++++
        if( !menu[midx].canMove )     // up
        {
            if( get_key_press(1 << KEY_PLUS) || get_key_long_rpt_sp((1 << KEY_PLUS),2) )     // up
            {
                goDown = true;
            }
        }   // end: DOWN - Verschiebemodus NICHT aktiv


        //+++++
        // DOWN - goDown
        //+++++
        if( goDown )     // down
        {
            refreshmode = MENU_REFRESH;
            if( menu[midx].active < menu[midx].itemcount-1 )
            {
                menu[midx].active++;
            }
            else if( menu[midx].cycle )
            {
                menu[midx].active = 0;
            }
            else
            {
                _MenuCtrl_Beep();                                                           // am unteren Ende angelangt
            }
            _MenuCtrl_CalcDisplayPos();
        }


        //--------------------------
        //--------------------------
        item    = menu[midx].active;
        goUp    = false;
        goDown  = false;


        //--------------------------
        // KEY_ENTER
        //--------------------------
        if( get_key_short(1 << KEY_ENTER) )
        {
            refreshmode = MENU_REFRESH;
            // todo: keyfunc
            if( menu[midx].item[item].type == MENU_SEPARATOR )
            {
                // do nothing
            }
            else if( menu[midx].item[item].disabled )
            {
                set_beep( 200, 0x00ff, BeepNormal);                             // Beep
                PKT_Popup_P( 200, strGet(STR_MENUCTRL_NOTPOSSIBLE),0,0,0);      // "nicht möglich!"
                refreshmode = MENU_REDRAW;
            }
            else if( menu[midx].item[item].func != NOFUNC )
            {
                menu[midx].item[item].func();
                refreshmode = MENU_REDRAW;
            }
            else if( menu[midx].key_enter.func != NOFUNC )
            {
                menu[midx].key_enter.func();
                refreshmode = MENU_REDRAW;
            }
            else
            {
                menu[midx].lastkey = KEY_ENTER;
                menu_event = MENUEVENT_ITEM;
            }
        }


        //--------------------------
        // KEY_ENTER_LONG (Deletemodus AKTIV)
        //
        // loescht einen Menueeintrag
        //--------------------------
        if( menu[midx].canDelete && get_key_long(1 << KEY_ENTER) && (menu[midx].itemcount > 0) )
        {
            wahl = 1;
            if( menu[midx].canDelete == 1 )                                     // ==1 -> mit Nachfrage
            {
                set_beep( 200, 0xffff, BeepNormal);                             // Beep
                PKT_Popup_P( 0, strGet(STR_MENUCTRL_DELASK),PSTR(""),0,0);      // "Eintrag entfernen?"
                lcd_printp_at( 12, 7, strGet(YESNO), MINVERS);

                wahl = 0;
                while( !wahl )
                {
                    if( get_key_short(1<<KEY_ESC) )   wahl = 1;                 // "Ja"
                    if( get_key_short(1<<KEY_ENTER) ) wahl = 2;                 // "Nein"
                }
                refreshmode = MENU_REDRAW;
            }

            if( wahl==1 )
            {
                for( i=menu[midx].active; i<menu[midx].itemcount-1; i++)
                {
                    _MenuCtrl_SwapItem( i, i+1 );
                }
                menu[midx].itemcount--;

                if( (menu[midx].display_pos > 0)  &&   (menu[midx].display_pos+6 > menu[midx].itemcount)  )
                {
                    menu[midx].display_pos--;
                    menu[midx].active--;
                }

                if( menu[midx].active >= menu[midx].itemcount )
                    menu[midx].active = menu[midx].itemcount-1;

                _MenuCtrl_CalcDisplayPos();
                refreshmode = MENU_REDRAW;

                if( menu[midx].canDelete == 2 )                                 // ==2 -> direkt loeschen, ohne Nachfrage
                {
                    set_beep( 200, 0xffff, BeepNormal);                         // Beep
                    PKT_Popup_P( 200, strGet(STR_MENUCTRL_DELITEM),0,0,0);      // "Eintrag entfernt!"
                }
            }
        }


        //--------------------------
        // KEY_ENTER_LONG (NICHT Deletemodus)
        //--------------------------
        if( !menu[midx].canDelete && menu[midx].key_enter_long.active && get_key_long(1 << KEY_ENTER) )
        {
            refreshmode = MENU_REFRESH;
            if( menu[midx].key_enter_long.func != NOFUNC )
            {
                menu[midx].key_enter_long.func();
                refreshmode = MENU_REDRAW;
            }
            else
            {
                menu_event = MENUEVENT_KEY;
                menu[midx].lastkey = KEY_ENTER_LONG;
            }
        }


        //--------------------------
        // KEY_ESC_LONG
        //--------------------------
        if( menu[midx].key_esc_long.active && get_key_long(1 << KEY_ESC) )
        {
            refreshmode = MENU_REFRESH;
            if( menu[midx].key_esc_long.func != NOFUNC )
            {
                menu[midx].key_esc_long.func();
                refreshmode = MENU_REDRAW;
            }
            else
            {
                menu_event = MENUEVENT_KEY;
                menu[midx].lastkey = KEY_ESC_LONG;
            }
        }


        //--------------------------
        // KEY_ESC
        //--------------------------
        if( get_key_short(1 << KEY_ESC) )
        {
            refreshmode = MENU_REFRESH;
            if( menu[midx].key_esc.func != NOFUNC )
            {
                menu[midx].key_esc.func();
                refreshmode = MENU_REDRAW;
            }
            else
            {
                menu_event = MENUEVENT_KEY;
                menu[midx].lastkey = KEY_ESC;
            }
        }

        if( refreshmode )
        {
            MenuCtrl_Refresh( refreshmode );
            clear_key_all();
        }
    }
    while ( menu_event == MENUEVENT_NONE && ctrlmode == MENUCTRL_EVENT );

    return menu_event;
}