/*************************************************************** -*- c++ -*-
 *       Copyright (c) 2003,2004 by Marcel Wiesweg                         *
 *       (autogenerated code (c) Klaus Schmidinger)                        *
 *       Copyright (c) 2021      by Peter Bieringer (extenions)            *
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <vdr/plugin.h>
#include <vdr/keys.h>
#include <vdr/config.h>

#include <getopt.h>
#include <iostream>

using namespace std;

#include "menu.h"
#include "txtrecv.h"
#include "setup.h"
#include "legacystorage.h"
#include "packedstorage.h"
#include "logging.h"

#if defined(APIVERSNUM) && APIVERSNUM < 10739
#error "VDR-1.7.39 API version or greater is required!"
#endif

#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))

static const char *VERSION        = "2.3.1";
static const char *DESCRIPTION    = trNOOP("Displays teletext on the OSD");
static const char *MAINMENUENTRY  = trNOOP("Teletext");

extern tColor clrBackground;

unsigned int m_debugmask = 0;
unsigned int m_debugpage = 0;
unsigned int m_debugpsub = 0;
int maxOsdPreset = 1;
int maxHotkeyLevel = 1;
int m_debugline = -1;

class cPluginTeletextosd : public cPlugin {
private:
  // Add any member variables or functions you may need here.
  cTxtStatus *txtStatus;
  bool startReceiver;
  bool storeTopText;
  Storage *storage;
  int maxStorage;
  void initTexts();
  Storage::StorageSystem storageSystem;

public:
  cPluginTeletextosd(void);
  virtual ~cPluginTeletextosd();
  virtual const char *Version(void) { return VERSION; }
  virtual const char *Description(void) { return tr(DESCRIPTION); }
  virtual const char *CommandLineHelp(void);
  virtual bool ProcessArgs(int argc, char *argv[]);
  virtual bool Start(void);
  virtual void Stop(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);
};

class cTeletextSetupPage;

// macro for creating setup string with given text and conditional index+1 suffix
#define CREATE_SETUP_STRING_COND_SUFFIX(index, text) \
      if (index == 0) \
         snprintf(str, sizeof(str), "%s", text); \
      else \
         snprintf(str, sizeof(str), "%s%d", text, index + 1);

class ActionEdit {
   public:
      void Init(cTeletextSetupPage*, int, cMenuEditIntItem  *, cMenuEditStraItem *);
      void Init(cTeletextSetupPage*, int, int, bool, cMenuEditIntItem  *, cMenuEditStraItem *);
      cMenuEditStraItem *action;
      cMenuEditIntItem  *number;
   };

struct ActionKeyName {
   const char *internalName;
   const char *userName;
};

class cTeletextSetupPage : public cMenuSetupPage {
friend class ActionEdit;
friend class cPluginTeletextosd;
private:
   TeletextSetup temp;
   int osdPreset;
   int hotkeyLevel;
   int tempPageNumber[LastActionKey];
   int tempPageNumberHotkey[LastActionHotkey][HOTKEY_LEVEL_MAX_LIMIT];
   cOsdItem *osdPresetMaxItem;
   cOsdItem *osdPresetItem;
   cString   osdPresetString;
   cOsdItem *osdPresetConfigItem[LastActionConfig][OSD_PRESET_MAX_LIMIT]; // array of supported configuration items
   cOsdItem *menuSectionKeysItem;
   cOsdItem *hotkeyLevelMaxItem;
   cOsdItem *hotkeyLevelItem;
   cString   hotkeyLevelString;
protected:
   virtual void Store(void);
   int osdConfig[LastActionConfig][OSD_PRESET_MAX_LIMIT]; // matrix of supported presets
   ActionEdit ActionEdits[LastActionKey];
   ActionEdit ActionEditsHotkey[LastActionHotkey][HOTKEY_LEVEL_MAX_LIMIT];
   void SetupRefreshKeys(void);
   void SetupRefreshHotkeys(void);
   void SetupRefreshOsdConfig(void);
   virtual eOSState ProcessKey(eKeys Key);
public:
   cTeletextSetupPage(void);
   static const ActionKeyName *actionKeyNames;
   static const ActionKeyName *actionHotkeyNames;
   static const char **modes;
   //~cTeletextSetupPage(void);
   //void SetItemVisible(cOsdItem *Item, bool visible, bool callDisplay=false);
};

const ActionKeyName *cTeletextSetupPage::actionKeyNames = 0;
const ActionKeyName *cTeletextSetupPage::actionHotkeyNames = 0;
const char **cTeletextSetupPage::modes = 0;

/*class MenuEditActionItem : public cMenuEditStraItem {
public:
   MenuEditActionItem(cTeletextSetupPage *parentMenu, cMenuEditIntItem *pageNumberMenuItem,
                           const char *Name, int *Value, int NumStrings, const char * const *Strings);
protected:
   virtual eOSState ProcessKey(eKeys Key);
   cTeletextSetupPage *parent;
   cMenuEditIntItem *pageNumberItem;
};*/


cPluginTeletextosd::cPluginTeletextosd(void)
  : txtStatus(0), startReceiver(true), storage(NULL), maxStorage(-1)
    , storageSystem(Storage::StorageSystemPacked)
{
  // 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!

   initTexts();

   // read available fonts into Vector
   cFont::GetAvailableFontNames(&ttSetup.txtFontNames, true);

   // run through available fonts backwards and delete blacklisted ones
   for (int i = ttSetup.txtFontNames.Size() -1;  i >= 0; i--) {
      if (    (strcasestr(ttSetup.txtFontNames[i], "Italic" ) != NULL)
           || (strcasestr(ttSetup.txtFontNames[i], "Oblique") != NULL)
      ) {
         DEBUG_OT_FONT("available font='%s' BLACKLISTED", ttSetup.txtFontNames[i]);
         ttSetup.txtFontNames.Remove(i);
      } else {
         DEBUG_OT_FONT("available font='%s' WHITELISTED", ttSetup.txtFontNames[i]);
      };
   };

   // display selectable fonts
   for (int i = 0; i < ttSetup.txtFontNames.Size(); i++) {
      if (ttSetup.txtFontNames[i] != NULL) {
         DEBUG_OT_FONT("selectable font[%d]='%s'", i, ttSetup.txtFontNames[i]);
      };
   };

   ttSetup.configuredClrBackground = -1; // flag for check whether it's still in setup.conf
};

cPluginTeletextosd::~cPluginTeletextosd()
{
   // Clean up after yourself!
}

const char *cPluginTeletextosd::CommandLineHelp(void)
{
  // Return a string that describes all known command line options.
  return "  -d        --directory=DIR    The directory where the temporary files will be stored.\n"
         "                                default: /var/cache/vdr/vtx\n"
         "                                Ensure that the directory exists and is writable.\n"
         "  -n        --max-cache=NUM    Maximum size in megabytes of cache used\n"
         "                                to store the pages on the harddisk.\n"
         "                                default: a calculated value below 50 MB\n"
         "  -s        --cache-system=SYS Set the cache system to be used.\n"
         "                                Choose \"legacy\" for the traditional\n"
         "                                 one-file-per-page system.\n"
         "                                Default is \"packed\" for the \n"
         "                                 one-file-for-a-few-pages system.\n"
         "  -t,       --toptext          Store top text pages at cache. (unviewable pages)\n"
         "  -k        --key-levels=NUM   Maximum amount of Hotkey levels selectable and stored\n"
         "                                default: 1 (which deactivate this feature)\n"
         "                                maximum: " HOTKEY_LEVEL_MAX_LIMIT_STRING " levels\n"
         "  -o        --osd-presets=NUM  Maximum amount of OSD presets selectable and stored\n"
         "                                default: 1 (which deactivate this feature)\n"
         "                                maximum: " OSD_PRESET_MAX_LIMIT_STRING " presets\n"
         "  -P|--debugpage <int|hexint>  Specify page to debug (int: autoconvert internally to hex)\n"
         "  -S|--debugpsub <int|hexint>  Specify sub-page to debug (int: autoconvert internally to hex)\n"
         "  -L|--debugline <int>         Specify line of page to debug\n"
         "  -D|--debugmask <int|hexint>  Enable debugmask (see logging.h for details)\n";
}

bool cPluginTeletextosd::ProcessArgs(int argc, char *argv[])
{
  // Implement command line argument processing here if applicable.
   static struct option long_options[] = {
       { "directory",    required_argument,       NULL, 'd' },
       { "max-cache",    required_argument,       NULL, 'n' },
       { "cache-system", required_argument,       NULL, 's' },
       { "toptext",      no_argument,             NULL, 't' },
       { "key-levels",   required_argument,       NULL, 'k' },
       { "osd-presets",  required_argument,       NULL, 'o' },
       { "debugmask",    required_argument,       NULL, 'D' },
       { "debugpage",    required_argument,       NULL, 'P' },
       { "debugpsub",    required_argument,       NULL, 'S' },
       { "debugline",    required_argument,       NULL, 'L' },
       { NULL }
       };

   int c;
   while ((c = getopt_long(argc, argv, "o:k:s:d:n:tD:", long_options, NULL)) != -1) {
        switch (c) {
          case 's':
                    if (!optarg)
                       break;
                    if (strcasecmp(optarg, "legacy")==0)
                       storageSystem = Storage::StorageSystemLegacy;
                    else if (strcasecmp(optarg, "packed")==0)
                       storageSystem = Storage::StorageSystemPacked;
                    break;
          case 'd': Storage::setRootDir(optarg);
                    break;
          case 'n': if (isnumber(optarg)) {
                       int n = atoi(optarg);
                       maxStorage=n;
                    }
                    break;
          case 't': storeTopText=true;
                    break;
          case 'k': if (isnumber(optarg)) {
                       int n = atoi(optarg);
                       if (n < 1) {
                          maxHotkeyLevel = 1;
                          esyslog("osdteletext: maximum key-level value (-k %s) below 1 (ignore and use minimum)", optarg);
                       } else if ((n > HOTKEY_LEVEL_MAX_LIMIT)) {
                          maxHotkeyLevel = HOTKEY_LEVEL_MAX_LIMIT;
                          esyslog("osdteletext: maximum key-level value (-k %s) above limit of %d (ignore and use maximum)", optarg, HOTKEY_LEVEL_MAX_LIMIT);
                       } else {
                          maxHotkeyLevel = n;
                       };
                    }
                    break;
          case 'o': if (isnumber(optarg)) {
                       int n = atoi(optarg);
                       if (n < 1) {
                          maxOsdPreset = 1;
                          esyslog("osdteletext: maximum OSD-preset value (-o %s) below 1 (ignore and use minimum)", optarg);
                       } else if ((n > OSD_PRESET_MAX_LIMIT)) {
                          maxOsdPreset = OSD_PRESET_MAX_LIMIT;
                          esyslog("osdteletext: maximum OSD-preset value (-o %s) above limit of %d (ignore and use maximum)", optarg, OSD_PRESET_MAX_LIMIT);
                       } else {
                          maxOsdPreset = n;
                       };
                    }
                    break;
          case 'D':
            if ((strlen(optarg) > 2) && (strncasecmp(optarg, "0x", 2) == 0)) {
               // hex conversion
               if (sscanf(optarg + 2, "%x", &m_debugmask) == 0) {
                  esyslog("osdteletext: can't parse hexadecimal debug mask (skip): %s", optarg);
               };
            } else {
				   m_debugmask = atoi(optarg);
            };
			   dsyslog("osdteletext: enable debug mask: %d (0x%08x)", m_debugmask, m_debugmask);
            break;

          case 'P':
            if ((strlen(optarg) > 2) && (strncasecmp(optarg, "0x", 2) == 0)) {
               // hex conversion
               if (sscanf(optarg + 2, "%x", &m_debugpage) == 0) {
                  esyslog("osdteletext: can't parse hexadecimal debug page (skip): %s", optarg);
               };
            } else {
               // hex conversion
               if (sscanf(optarg, "%x", &m_debugpage) == 0) {
                  esyslog("osdteletext: can't parse hexadecimal debug page (skip): %s", optarg);
               };
            };
			   dsyslog("osdteletext: enable debug page: %03x", m_debugpage);
            break;

          case 'S':
            if ((strlen(optarg) > 2) && (strncasecmp(optarg, "0x", 2) == 0)) {
               // hex conversion
               if (sscanf(optarg + 2, "%x", &m_debugpsub) == 0) {
                  esyslog("osdteletext: can't parse hexadecimal debug sub-page (skip): %s", optarg);
               };
            } else {
               // hex conversion
               if (sscanf(optarg, "%x", &m_debugpsub) == 0) {
                  esyslog("osdteletext: can't parse hexadecimal debug sub-page (skip): %s", optarg);
               };
            };
			   dsyslog("osdteletext: enable debug sub-page: %03x", m_debugpsub);
            break;

          case 'L':
            m_debugline = atoi(optarg);
			   dsyslog("osdteletext: enable debug page line: %d", m_debugline);
            break;
        }
   }
   return true;
}

bool cPluginTeletextosd::Start(void)
{
   // Start any background activities the plugin shall perform.
   //Clean any files which might be remaining from the last session,
   //perhaps due to a crash they have not been deleted.
   switch (storageSystem) {
      case Storage::StorageSystemLegacy:
         isyslog("osdteletext: selected storage system: legacy");
         storage = new LegacyStorage(maxStorage);
         break;
      case Storage::StorageSystemPacked:
      default:
         isyslog("osdteletext: selected storage system: packed");
         storage = new PackedStorage(maxStorage);
         break;
   }

   if (startReceiver)
      txtStatus=new cTxtStatus(storeTopText, storage);

   if (maxOsdPreset > 1)
      isyslog("osdteletext: OSD multiple preset feature enabled with maximum of presets: %d", maxOsdPreset);
   else
      isyslog("osdteletext: OSD multiple preset feature not activated");

   if (maxHotkeyLevel > 1)
      isyslog("osdteletext: OSD menu Hotkey multi-level feature enabled with maximum of levels: %d", maxHotkeyLevel);
   else
      isyslog("osdteletext: OSD menu Hotkey multi-level feature not activated");

   ttSetup.osdPreset = 0; // default

   // legacy migration handling
   if ((ttSetup.migrationFlag_2_2 == false) && (ttSetup.configuredClrBackground >= 0)) {
      // BackTrans(1) not found, but configuredClrBackground found in setup.conf
      // overtake value from legacy TODO remove >= 2.3.0
      ttSetup.osdConfig[BackTrans][0] = ttSetup.configuredClrBackground;
      dsyslog("osdteletext: overtake into 'OSDbackTrans' (preset 1) from setup.conf: configuredClrBackground: %d -> %d" , 255 - ttSetup.configuredClrBackground, ttSetup.osdConfig[BackTrans][0]);
   };

   return true;
}

void cPluginTeletextosd::Stop(void)
{
   char str[40];

   // TODO: deduplicate code, see also cTeletextSetupPage::Store
   // Question: how to share that code beteen cTeletextSetupPage and cPluginTeletextosd
   // store OSD presets
   // preset "1" (internally 0) without digit suffix for backwards compatibility
   for (int p = 0; p < maxOsdPreset; p++) {
      CREATE_SETUP_STRING_COND_SUFFIX(p, "OSDleftPct")
      SetupStore(str, ttSetup.osdConfig[Left][p]);

      CREATE_SETUP_STRING_COND_SUFFIX(p, "OSDtopPct")
      SetupStore(str, ttSetup.osdConfig[Top][p]);

      CREATE_SETUP_STRING_COND_SUFFIX(p, "OSDwidthPct")
      SetupStore(str, ttSetup.osdConfig[Width][p]);

      CREATE_SETUP_STRING_COND_SUFFIX(p, "OSDheightPct")
      SetupStore(str, ttSetup.osdConfig[Height][p]);

      CREATE_SETUP_STRING_COND_SUFFIX(p, "OSDframePix")
      SetupStore(str, ttSetup.osdConfig[Frame][p]);

      CREATE_SETUP_STRING_COND_SUFFIX(p, "txtVoffset")
      SetupStore(str, ttSetup.osdConfig[Voffset][p]);

      CREATE_SETUP_STRING_COND_SUFFIX(p, "txtFontName")
      SetupStore(str, ttSetup.txtFontNames[ttSetup.osdConfig[Font][p]]); // convert into name

      CREATE_SETUP_STRING_COND_SUFFIX(p, "OSDbackTrans")
      SetupStore(str, ttSetup.osdConfig[BackTrans][p]);
   };

   // legacy TODO remove >= 2.3.0
   if (ttSetup.configuredClrBackground >= 0) {
      // found in setup.conf during read, so store it back in legacy format
      SetupStore("configuredClrBackground", 255 - ttSetup.osdConfig[BackTrans][0]);
   };

   DELETENULL(txtStatus);
   if (storage) {
      storage->cleanUp();
      DELETENULL(storage);
   }
}

void cPluginTeletextosd::initTexts() {
   if (cTeletextSetupPage::actionKeyNames)
      return;

   static const ActionKeyName st_actionKeyNames[] =
   {
      { "Action_kFastRew",  trVDR("Key$FastRew") },
      { "Action_kFastFwd",  trVDR("Key$FastFwd") },
      { "Action_kStop",     trVDR("Key$Stop") },
      { "Action_kOk",       trVDR("Key$Ok") },
      { "Action_kPlay",     trVDR("Key$Play") },
   };

   cTeletextSetupPage::actionKeyNames = st_actionKeyNames;


   static const ActionKeyName st_actionHotkeyNames[] =
   {
      { "Action_kRed",      trVDR("Key$Red") },
      { "Action_kGreen",    trVDR("Key$Green") },
      { "Action_kYellow",   trVDR("Key$Yellow") },
      { "Action_kBlue",     trVDR("Key$Blue") },
   };

   cTeletextSetupPage::actionHotkeyNames = st_actionHotkeyNames;


   static const char *st_modes[] =
   {
      // 1:1 relation between st_modes[] in osdteletext.c + eTeletextAction in setup.h + st_modesHotkey in setup.c
      tr("Zoom"),
      tr("Half page"),
      tr("Change channel"),
      tr("Switch background"),
      //tr("Suspend receiving"),
      tr("Config"),
      tr("Footer Line"),
      tr("Answer"),
      tr("Pause"),
      tr("Hotkey Level+"),
      tr("Hotkey Level-"),
      tr("OSD Preset+"),
      tr("OSD Preset-"),
      tr("Jump to...") // has to stay always as the last one
   };

   cTeletextSetupPage::modes = st_modes;
}

void cPluginTeletextosd::Housekeeping(void)
{
  // Perform any cleanup or other regular tasks.
}

const char *cPluginTeletextosd::MainMenuEntry(void)
{
   return ttSetup.HideMainMenu ? 0 : tr(MAINMENUENTRY);
}

cOsdObject *cPluginTeletextosd::MainMenuAction(void)
{
   // Perform the action when selected from the main VDR menu.
   return new TeletextBrowser(txtStatus,storage);
}

cMenuSetupPage *cPluginTeletextosd::SetupMenu(void)
{
  // Return a setup menu in case the plugin supports one.
  return new cTeletextSetupPage;
}

/* index extraction macro */
#define CHECK_SETUP_STRING_COND_SUFFIX(name, text, index, limit) \
   index = -1; \
   if (! strcasecmp(name, text)) { \
      index = 0; \
   } else { \
      if (! strncasecmp(name, text, strlen(text))) { \
         if ((strlen(name) - 1) != strlen(text)) { \
           esyslog("osdteletext: ignore entry with too long suffix in setup.conf: osdteletext.%s", name); \
           return false; /* invalid option, only 1 digit is supported */ \
         } \
         /* extract digit suffix */ \
         index = atoi(name + strlen(text)) - 1; /* last char digit */ \
         if ((index < 1) || (index >= limit)) { \
           /* ignore out-of-range suffix */ \
           esyslog("osdteletext: ignore entry with out-of-range digit in setup.conf: osdteletext.%s (detected index=%d)", name, index); \
           return false; \
         }; \
      }; \
   }; \
   if (index >= 0) { DEBUG_OT_SETUP("found setup config: Name=%s Text=%s Index=%d Value='%s'\n", name, text, index, Value); };

/* value check/store macro */
#define CHECK_STORE_INT_VALUE(store, value, min, max) \
   store = atoi(value); \
   if (store < min) store = min; \
   else if (store > max) store = max;

/* ignore obsolete options */
#define DSYSLOG_IGNORE_OPTION(info) dsyslog("osdteletext: ignore obsolete option in setup.conf: osdteletext.%s (%s)", Name, info);

bool cPluginTeletextosd::SetupParse(const char *Name, const char *Value)
{
  initTexts();

  // Parse your own setup parameters and store their values.
  if      (!strcasecmp(Name, "HideMainMenu")) ttSetup.HideMainMenu=atoi(Value);
  else if (!strcasecmp(Name, "showClock")) ttSetup.showClock=atoi(Value);
  else if (!strcasecmp(Name, "autoUpdatePage")) ttSetup.autoUpdatePage=atoi(Value);
  else if (!strcasecmp(Name, "txtG0Block")) ttSetup.txtG0Block=atoi(Value);
  else if (!strcasecmp(Name, "txtG2Block")) ttSetup.txtG2Block=atoi(Value);
  else if (!strcasecmp(Name, "colorMode4bpp")) ttSetup.colorMode4bpp=atoi(Value);
  else if (!strcasecmp(Name, "lineMode24")) ttSetup.lineMode24=atoi(Value);
  else if (!strcasecmp(Name, "OSDHAlign"  )) { DSYSLOG_IGNORE_OPTION("<1.0.0") }
  else if (!strcasecmp(Name, "OSDVAlign"  )) { DSYSLOG_IGNORE_OPTION("<1.0.0") }
  else if (!strcasecmp(Name, "OSDheight"  )) { DSYSLOG_IGNORE_OPTION("<1.0.0") }
  else if (!strcasecmp(Name, "OSDwidth"   )) { DSYSLOG_IGNORE_OPTION("<1.0.0") }
  else if (!strcasecmp(Name, "OSDhcentPct")) { DSYSLOG_IGNORE_OPTION("1.0.0-1.0.4") }
  else if (!strcasecmp(Name, "OSDvcentPct")) { DSYSLOG_IGNORE_OPTION("1.0.0-1.0.4") }
  else if (!strcasecmp(Name, "OSDframePct")) { DSYSLOG_IGNORE_OPTION(">1.0.6 && <1.0.7") }
  else if (!strcasecmp(Name, "suspendReceiving")) { DSYSLOG_IGNORE_OPTION("<0.8.0") }
  else if (!strcasecmp(Name, "inactivityTimeout")) { DSYSLOG_IGNORE_OPTION("<0.8.0") }
  else if (!strcasecmp(Name, "configuredClrBackground")) {
      // DSYSLOG_IGNORE_OPTION("<2.2.0") TODO >= 2.3.0
      ttSetup.configuredClrBackground = 255 - atoi(Value); // legacy setting, map already to new internal value handling
      if (ttSetup.configuredClrBackground < 0)
         ttSetup.configuredClrBackground = 0;
      else if (ttSetup.configuredClrBackground > 255)
         ttSetup.configuredClrBackground = 255;
  }
  else if (!strcasecmp(Name, "osdPresetMax")) {
     ttSetup.osdPresetMax = atoi(Value);
     if (ttSetup.osdPresetMax > maxOsdPreset) {
        // limit by command line option maximum
        ttSetup.osdPresetMax = maxOsdPreset;
     } else if (ttSetup.osdPresetMax < 1) {
        // minimum is 1
        ttSetup.osdPresetMax = 1;
     };
  }
  else if (!strcasecmp(Name, "hotkeyLevelMax")) {
     ttSetup.hotkeyLevelMax = atoi(Value);
     if (ttSetup.hotkeyLevelMax > maxHotkeyLevel) {
        // limit by command line option maximum
        ttSetup.hotkeyLevelMax = maxHotkeyLevel;
     } else if (ttSetup.hotkeyLevelMax < 1) {
        // minimum is 1
        ttSetup.hotkeyLevelMax = 1;
     };
  }
  else {
      // parse setup related to OSD with preset
      int p;

      CHECK_SETUP_STRING_COND_SUFFIX(Name, "OSDleftPct", p, OSD_PRESET_MAX_LIMIT);
      if (p >= 0) {
         CHECK_STORE_INT_VALUE(ttSetup.osdConfig[Left][p], Value, OSDleftPctMin, OSDleftPctMax)
         return true;
      };

      CHECK_SETUP_STRING_COND_SUFFIX(Name, "OSDtopPct", p, OSD_PRESET_MAX_LIMIT);
      if (p >= 0) {
         CHECK_STORE_INT_VALUE(ttSetup.osdConfig[Top][p], Value, OSDtopPctMin, OSDtopPctMax);
         return true;
      };

      CHECK_SETUP_STRING_COND_SUFFIX(Name, "OSDwidthPct", p, OSD_PRESET_MAX_LIMIT);
      if (p >= 0) {
         CHECK_STORE_INT_VALUE(ttSetup.osdConfig[Width][p], Value, OSDwidthPctMin, OSDwidthPctMax);
         return true;
      };

      CHECK_SETUP_STRING_COND_SUFFIX(Name, "OSDheightPct", p, OSD_PRESET_MAX_LIMIT);
      if (p >= 0) {
         CHECK_STORE_INT_VALUE(ttSetup.osdConfig[Height][p], Value, OSDheightPctMin, OSDheightPctMax);
         return true;
      };

      CHECK_SETUP_STRING_COND_SUFFIX(Name, "OSDframePix", p, OSD_PRESET_MAX_LIMIT);
      if (p >= 0) {
         CHECK_STORE_INT_VALUE(ttSetup.osdConfig[Frame][p], Value, OSDframePixMin, OSDframePixMax);
         return true;
      };

      CHECK_SETUP_STRING_COND_SUFFIX(Name, "txtVoffset", p, OSD_PRESET_MAX_LIMIT);
      if (p >= 0) {
         CHECK_STORE_INT_VALUE(ttSetup.osdConfig[Voffset][p], Value, txtVoffsetMin, txtVoffsetMax);
         return true;
      };

      CHECK_SETUP_STRING_COND_SUFFIX(Name, "OSDbackTrans", p, OSD_PRESET_MAX_LIMIT);
      if (p >= 0) {
         CHECK_STORE_INT_VALUE(ttSetup.osdConfig[BackTrans][p], Value, BackTransMin, BackTransMax);
         if (p == 0) ttSetup.migrationFlag_2_2 = true; // set migration flag for 2.2.0 (configuredClrBackground)
         return true;
      };

      CHECK_SETUP_STRING_COND_SUFFIX(Name, "txtFontName", p, OSD_PRESET_MAX_LIMIT);
      if (p >= 0) {
         // font name to index conversion
         int i = ttSetup.txtFontNames.Find(Value);
         if (i < 0) i = 0; // not found -> default is 1st one
         ttSetup.osdConfig[Font][p] = i;
         DEBUG_OT_SETUP("map setup config: Name=%s Index=%d Value='%s' to FontIndex=%d\n", Name, p, Value, i);
         return true;
      };

     // parse setup related to keys
     for (int i=0;i<LastActionKey;i++) {
        // DEBUG_OT_SETUP("compare i=%d internalName=%s\n", i, cTeletextSetupPage::actionKeyNames[i].internalName);
        if (!strcasecmp(Name, cTeletextSetupPage::actionKeyNames[i].internalName)) {
           ttSetup.mapKeyToAction[i]=(eTeletextAction)atoi(Value);
           return true;
        }
     }

     // parse setup related to Hotkey with levels
     for (int i = 0; i < LastActionHotkey; i++) {
        int l;
        int v;
        CHECK_SETUP_STRING_COND_SUFFIX(Name, cTeletextSetupPage::actionHotkeyNames[i].internalName, l, HOTKEY_LEVEL_MAX_LIMIT);
        if (l >= 0) {
           CHECK_STORE_INT_VALUE(v, Value, 0, 899);
           if ((v < 100) && (v >= (int) LastAction)) {
              esyslog("osdteletext: ignore entry with out-of-range value in setup.conf: osdteletext.%s (%d)", Name, v);
              return true;
           };
           ttSetup.mapHotkeyToAction[i][l] = (eTeletextAction) v;
           return true;
        };
     };

     return false;
  }
  return true;
}


void cTeletextSetupPage::Store(void) {
   char str[40];

   // copy temporary preset table
   for (int p = 0; p < OSD_PRESET_MAX_LIMIT; p++) {
      for (int t = 0; t < LastActionConfig; t++) {
         ttSetup.osdConfig[t][p] = temp.osdConfig[t][p];
      };
   };

   ttSetup.osdPresetMax=temp.osdPresetMax;

   // copy key table
   for (int i=0;i<LastActionKey;i++) {
      if (temp.mapKeyToAction[i] >= LastAction) //jump to page selected
         ttSetup.mapKeyToAction[i]=(eTeletextAction)tempPageNumber[i];
      else //one of the other modes selected
         ttSetup.mapKeyToAction[i]=temp.mapKeyToAction[i];
   }

   // copy Hotkey table
   for (int l = 0; l < HOTKEY_LEVEL_MAX_LIMIT; l++) {
      for (int i = 0;i < LastActionHotkey; i++) {
         if (temp.mapHotkeyToAction[i][l] >= LastAction) //jump to page selected
            ttSetup.mapHotkeyToAction[i][l] = (eTeletextAction)tempPageNumberHotkey[i][l];
         else //one of the other modes selected
            ttSetup.mapHotkeyToAction[i][l] = temp.mapHotkeyToAction[i][l];
      }
   }

   ttSetup.hotkeyLevelMax=temp.hotkeyLevelMax;

   ttSetup.showClock=temp.showClock;
   ttSetup.autoUpdatePage=temp.autoUpdatePage;
   ttSetup.HideMainMenu=temp.HideMainMenu;
   ttSetup.txtG0Block=temp.txtG0Block;
   ttSetup.txtG2Block=temp.txtG2Block;
   ttSetup.colorMode4bpp=temp.colorMode4bpp;
   ttSetup.lineMode24=temp.lineMode24;

   // store key table
   for (int i=0;i<LastActionKey;i++) {
      SetupStore(actionKeyNames[i].internalName, ttSetup.mapKeyToAction[i]);
   }

   // store Hotkey table (maximum given by command line: maxHotkeyLevel)
   // hotkeyLevel "1" (interally 0) without digit for backwards compatibility
   for (int l = 0; l < maxHotkeyLevel; l++) {
      for (int i = 0; i < LastActionHotkey;i++) {
         CREATE_SETUP_STRING_COND_SUFFIX(l, actionHotkeyNames[i].internalName)
         SetupStore(str, ttSetup.mapHotkeyToAction[i][l]);
      };
   };

   SetupStore("hotkeyLevelMax", ttSetup.hotkeyLevelMax); // store currently configured maximum

   // store OSD presets
   // preset "1" (internally 0) without digit suffix for backwards compatibility
   for (int p = 0; p < maxOsdPreset; p++) {
      CREATE_SETUP_STRING_COND_SUFFIX(p, "OSDleftPct")
      SetupStore(str, ttSetup.osdConfig[Left][p]);

      CREATE_SETUP_STRING_COND_SUFFIX(p, "OSDtopPct")
      SetupStore(str, ttSetup.osdConfig[Top][p]);

      CREATE_SETUP_STRING_COND_SUFFIX(p, "OSDwidthPct")
      SetupStore(str, ttSetup.osdConfig[Width][p]);

      CREATE_SETUP_STRING_COND_SUFFIX(p, "OSDheightPct")
      SetupStore(str, ttSetup.osdConfig[Height][p]);

      CREATE_SETUP_STRING_COND_SUFFIX(p, "OSDframePix")
      SetupStore(str, ttSetup.osdConfig[Frame][p]);

      CREATE_SETUP_STRING_COND_SUFFIX(p, "txtVoffset")
      SetupStore(str, ttSetup.osdConfig[Voffset][p]);

      CREATE_SETUP_STRING_COND_SUFFIX(p, "txtFontName")
      SetupStore(str, ttSetup.txtFontNames[ttSetup.osdConfig[Font][p]]); // convert into name

      CREATE_SETUP_STRING_COND_SUFFIX(p, "OSDbackTrans")
      SetupStore(str, ttSetup.osdConfig[BackTrans][p]);
   };

   SetupStore("osdPresetMax", ttSetup.osdPresetMax); // store currently configured maximum


   // legacy TODO remove >= 2.3.0
   if (ttSetup.configuredClrBackground >= 0) {
      // found in setup.conf during read, so store it back in legacy format
      SetupStore("configuredClrBackground", 255 - ttSetup.osdConfig[BackTrans][0]);
   };

   // Global
   SetupStore("showClock", ttSetup.showClock);
   SetupStore("autoUpdatePage", ttSetup.autoUpdatePage);
   SetupStore("HideMainMenu", ttSetup.HideMainMenu);
   SetupStore("txtG0Block", ttSetup.txtG0Block);
   SetupStore("txtG2Block", ttSetup.txtG2Block);
   SetupStore("colorMode4bpp", ttSetup.colorMode4bpp);
   SetupStore("lineMode24", ttSetup.lineMode24);

   if (clrBackground != TTSETUPPRESET_TCOLOR(BackTrans)) {
      clrBackground = TTSETUPPRESET_TCOLOR(BackTrans);
   };
}

cTeletextSetupPage::cTeletextSetupPage(void) {
   cString buf;
   cOsdItem *item;

   temp.txtBlock[0]  = tr("Latin 1");
   temp.txtBlock[1]  = tr("Latin 2");
   temp.txtBlock[2]  = tr("Latin 3");
   temp.txtBlock[3]  = tr("Latin 4");
   temp.txtBlock[4]  = tr("Cyrillic");
   temp.txtBlock[5]  = tr("Reserved");
   temp.txtBlock[6]  = tr("Greek");
   temp.txtBlock[7]  = tr("Reserved");
   temp.txtBlock[8]  = tr("Arabic");
   temp.txtBlock[9]  = tr("Reserved");
   temp.txtBlock[10] = tr("Hebrew");

   temp.lineMode[0]  = tr("Hotkeys");
   temp.lineMode[1]  = tr("None");
   temp.lineMode[2]  = tr("Hotkeys+Stdkeys");

   osdPreset = 1;
   temp.osdPresetMax = ttSetup.osdPresetMax;

   // init temporary preset table
   for (int p = 0; p < OSD_PRESET_MAX_LIMIT; p++) {
      for (int t = 0; t < LastActionConfig; t++) {
         temp.osdConfig[t][p] = ttSetup.osdConfig[t][p];
      };
   };

   hotkeyLevel = 1;
   temp.hotkeyLevelMax = ttSetup.hotkeyLevelMax;

   // init key tables
   for (int i=0;i<LastActionKey;i++) {
      if (ttSetup.mapKeyToAction[i] >= LastAction) {//jump to page selected
         temp.mapKeyToAction[i]=LastAction; //to display the last string
         tempPageNumber[i]=ttSetup.mapKeyToAction[i];
      } else { //one of the other modes selected
         temp.mapKeyToAction[i]=ttSetup.mapKeyToAction[i];
         tempPageNumber[i]=100;
      }
   }

   // init Hotkey tables
   for (int l = 0; l < HOTKEY_LEVEL_MAX_LIMIT; l++) {
      for (int i = 0; i < LastActionHotkey; i++) {
         if (ttSetup.mapHotkeyToAction[i][l] >= LastAction) {//jump to page selected
            temp.mapHotkeyToAction[i][l] = LastAction; //to display the last string
            tempPageNumberHotkey[i][l] = ttSetup.mapHotkeyToAction[i][l];
         } else { //one of the other modes selected
            temp.mapHotkeyToAction[i][l] = ttSetup.mapHotkeyToAction[i][l];
            tempPageNumberHotkey[i][l] = 100;
         }
      }
   }

   temp.showClock=ttSetup.showClock;
   temp.autoUpdatePage=ttSetup.autoUpdatePage;
   temp.osdPresetMax=ttSetup.osdPresetMax;
   temp.hotkeyLevelMax=ttSetup.hotkeyLevelMax;
   temp.HideMainMenu=ttSetup.HideMainMenu;
   temp.txtG0Block=ttSetup.txtG0Block;
   temp.txtG2Block=ttSetup.txtG2Block;
   temp.colorMode4bpp=ttSetup.colorMode4bpp;
   temp.lineMode24=ttSetup.lineMode24;

   Add(new cMenuEditBoolItem(tr("Hide mainmenu entry"), &temp.HideMainMenu));

   Add(new cMenuEditBoolItem(tr("Show clock"), &temp.showClock ));

   Add(new cMenuEditBoolItem(tr("Auto-update pages"), &temp.autoUpdatePage ));
   Add(new cMenuEditStraItem(tr("G0 code block"), &temp.txtG0Block, NUMELEMENTS(temp.txtBlock), temp.txtBlock));
   Add(new cMenuEditStraItem(tr("G2 code block"), &temp.txtG2Block, NUMELEMENTS(temp.txtBlock), temp.txtBlock));
   Add(new cMenuEditBoolItem(tr("16-Color Mode"), &temp.colorMode4bpp));
   Add(new cMenuEditStraItem(tr("Footer Line"), &temp.lineMode24, NUMELEMENTS(temp.lineMode), temp.lineMode));

   // OSD presets
   if (maxOsdPreset > 1)
      buf = cString::sprintf("OSD %s (%s %s %d/%d):", tr("Config"), tr("max"), tr("Presets"), maxOsdPreset, OSD_PRESET_MAX_LIMIT);
   else
      buf = cString::sprintf("OSD %s", tr("Config"));
   item = new cOsdItem(*buf);
   item->SetSelectable(false);
   Add(item);

   if (maxOsdPreset > 1) {
      // maximum given by command line option: maxOsdPreset
      cString buf2 = cString::sprintf("OSD %s %s", tr("Presets"), tr("visible"));
      osdPresetMaxItem = new cMenuEditIntItem(buf2, &temp.osdPresetMax, 1, maxOsdPreset);
      Add(osdPresetMaxItem);

      osdPresetString = cString::sprintf("OSD %s %s", tr("Presets"), tr("Config")); // remember string for refresh
      osdPresetItem = new cMenuEditIntItem(osdPresetString, &osdPreset, 1, temp.osdPresetMax);
      Add(osdPresetItem);
   } else {
      // hide option but remember for hook later the section entry from above
      osdPresetItem = item;
   };

   for (int p = 0; p < OSD_PRESET_MAX_LIMIT; p++) {
      // precreate all menu entries
      osdPresetConfigItem[Left]     [p] = new cMenuEditIntItem(tr("OSD left (%)"           ), &temp.osdConfig[Left]     [p], OSDleftPctMin  , OSDleftPctMax  );
      osdPresetConfigItem[Top]      [p] = new cMenuEditIntItem(tr("OSD top (%)"            ), &temp.osdConfig[Top]      [p], OSDtopPctMin   , OSDtopPctMax   );
      osdPresetConfigItem[Width]    [p] = new cMenuEditIntItem(tr("OSD width (%)"          ), &temp.osdConfig[Width]    [p], OSDwidthPctMin , OSDwidthPctMax );
      osdPresetConfigItem[Height]   [p] = new cMenuEditIntItem(tr("OSD height (%)"         ), &temp.osdConfig[Height]   [p], OSDheightPctMin, OSDheightPctMax);
      osdPresetConfigItem[Frame]    [p] = new cMenuEditIntItem(tr("OSD frame pixel"        ), &temp.osdConfig[Frame]    [p], OSDframePixMin , OSDframePixMax );
      osdPresetConfigItem[Font]     [p] = new cMenuEditStraItem(tr("Text Font"             ), &temp.osdConfig[Font]     [p], ttSetup.txtFontNames.Size(), &ttSetup.txtFontNames[0]);
      osdPresetConfigItem[Voffset]  [p] = new cMenuEditIntItem(tr("Text Vertical Offset"   ), &temp.osdConfig[Voffset]  [p], txtVoffsetMin  , txtVoffsetMax  );
      osdPresetConfigItem[BackTrans][p] = new cMenuEditIntItem(tr("Background transparency"), &temp.osdConfig[BackTrans][p], BackTransMin   , BackTransMax   );
   };

   SetupRefreshOsdConfig();

   // Hotkey bindings
   if (maxHotkeyLevel > 1)
      buf = cString::sprintf("%s Hotkey (%s %s %d/%d):", tr("Key bindings"), tr("max"), tr("Levels"), maxHotkeyLevel, HOTKEY_LEVEL_MAX_LIMIT);
   else
      buf = cString::sprintf("%s Hotkey:", tr("Key bindings"));
   item = new cOsdItem(*buf);
   item->SetSelectable(false);
   Add(item);

   if (maxHotkeyLevel > 1) {
      // maximum given by command line option: maxHotkeyLevel
      cString buf2 = cString::sprintf("OSD Hotkey %s %s", tr("Levels"), tr("visible"));
      hotkeyLevelMaxItem = new cMenuEditIntItem(buf2, &temp.hotkeyLevelMax, 1, maxHotkeyLevel);
      Add(hotkeyLevelMaxItem);

      hotkeyLevelString = cString::sprintf("OSD Hotkey %s %s", tr("Level"), tr("Config")); // remember string for refresh
      hotkeyLevelItem = new cMenuEditIntItem(hotkeyLevelString, &hotkeyLevel, 1, temp.hotkeyLevelMax);
      Add(hotkeyLevelItem);
   } else {
      // hide option but remember for hook later the section entry from above
      hotkeyLevelItem = item;
   };

   for (int l = 0; l < maxHotkeyLevel; l++) {
      for (int i = 0; i < LastActionHotkey; i++) {
         ActionEditsHotkey[i][l].Init(this, i, l
            , (l == (hotkeyLevel - 1))
            , new cMenuEditIntItem(tr("  Page number"), &tempPageNumberHotkey[i][l], 100, 899)
            , new cMenuEditStraItem(actionHotkeyNames[i].userName, (int*)&temp.mapHotkeyToAction[i][l], LastAction+1, modes)
         );
      };
   }

   // Standard Key bindings
   buf = cString::sprintf("%s:", tr("Key bindings"));
   menuSectionKeysItem = new cOsdItem(*buf);
   menuSectionKeysItem->SetSelectable(false);
   Add(menuSectionKeysItem);

   for (int i=0;i<LastActionKey;i++) {
      ActionEdits[i].Init(this, i, new cMenuEditIntItem(tr("  Page number"), &tempPageNumber[i], 100, 899),
         new cMenuEditStraItem(actionKeyNames[i].userName, (int*)&temp.mapKeyToAction[i], LastAction+1, modes) );
   }
}

void cTeletextSetupPage::SetupRefreshOsdConfig(void) {
   // delete all entry if existing
   #define DELETE_IF_EXISTING(item) if (cList<cOsdItem>::Contains(item)) cList<cOsdItem>::Del(item, false);

   // remove all entries
   for (int t = 0; t < LastActionConfig; t++)
      for (int p = 0; p < OSD_PRESET_MAX_LIMIT; p++)
         DELETE_IF_EXISTING(osdPresetConfigItem[t][p]);

   int p = osdPreset - 1;

   DEBUG_OT_SETUP("display setup menu for OSD preset: %d\n", osdPreset);

   for (int t = 0; t < LastActionConfig; t++) {
      if (t == Left) {
         // using hook from section or preset switch
         Add(osdPresetConfigItem[t][p], false, osdPresetItem);
      } else {
         // use previous hook
         Add(osdPresetConfigItem[t][p] , false, osdPresetConfigItem[t - 1][p]);
      }
   };
};

void cTeletextSetupPage::SetupRefreshKeys(void) {
   // recreate key setup without sophisticated and issue causing dynamic Insert+Add (e.g. last line 'Jump to' is not working)

   // delete all key entry if existing
   for (int i = 0; i < LastActionKey; i++) {
      if (cList<cOsdItem>::Contains(ActionEdits[i].action))
         cList<cOsdItem>::Del(ActionEdits[i].action, false);
      if (cList<cOsdItem>::Contains(ActionEdits[i].number))
         cList<cOsdItem>::Del(ActionEdits[i].number, false);
   };

   // add selected ones
   cOsdItem *hook = menuSectionKeysItem;
   for (int i = 0; i < LastActionKey; i++) {
      DEBUG_OT_KEYS("key assigment menu i=%d action=%d (main)", i, temp.mapKeyToAction[i]);
      Add(ActionEdits[i].action, false, hook);
      hook = ActionEdits[i].action;

      if (temp.mapKeyToAction[i] == LastAction) {
         // insert number if selected
         DEBUG_OT_KEYS("key assigment menu i=%d action=%d pagenumber=%d", i, temp.mapKeyToAction[i], tempPageNumber[i]);
         Add(ActionEdits[i].number, false, hook);
         hook = ActionEdits[i].number;
      };
   };
}

void cTeletextSetupPage::SetupRefreshHotkeys(void) {
   // recreate hot key setup without sophisticated and issue causing dynamic Insert+Add (e.g. last line 'Jump to' is not working)

   // Hotkey assignment
   int l = hotkeyLevel - 1;

   // delete all hot key entry if existing
   for (int l = 0; l < HOTKEY_LEVEL_MAX_LIMIT; l++) {
      for (int i = 0; i < LastActionHotkey; i++) {
         if (cList<cOsdItem>::Contains(ActionEditsHotkey[i][l].action))
            cList<cOsdItem>::Del(ActionEditsHotkey[i][l].action, false);
         if (cList<cOsdItem>::Contains(ActionEditsHotkey[i][l].number))
            cList<cOsdItem>::Del(ActionEditsHotkey[i][l].number, false);
      };
   };

   // add selected ones
   cOsdItem *hook = hotkeyLevelItem;
   for (int i = 0; i < LastActionHotkey; i++) {
      DEBUG_OT_KEYS("hot key assigment menu i=%d hotkeyLevel=%d action=%d (main)", i, hotkeyLevel, temp.mapHotkeyToAction[i][l]);
      Add(ActionEditsHotkey[i][l].action, false, hook);
      hook = ActionEditsHotkey[i][l].action;

      if (temp.mapHotkeyToAction[i][l] == LastAction) {
         // insert number if selected
         DEBUG_OT_KEYS("hot key assigment menu i=%d hotkeyLevel=%d action=%d pagenumber=%d", i, hotkeyLevel, temp.mapHotkeyToAction[i][l], tempPageNumberHotkey[i][l]);
         Add(ActionEditsHotkey[i][l].number, false, hook);
         hook = ActionEditsHotkey[i][l].number;
      };
   };

}

eOSState cTeletextSetupPage::ProcessKey(eKeys Key) {
   if (Key != kNone) DEBUG_OT_KEYS("called with Key=%d", Key);
   eOSState state = cMenuSetupPage::ProcessKey(Key);
   if (Key != kRight && Key!=kLeft)
      return state;
   cOsdItem *item = Get(Current());

   // OSD config preset
   if (item == osdPresetMaxItem) {
      // change of OSD preset max
      DEBUG_OT_KEYS("osdPresetMaxItem changed osdPresetMax=%d (osdPreset=%d)", temp.osdPresetMax, osdPreset);

      if (osdPreset > temp.osdPresetMax) {
         osdPreset = temp.osdPresetMax;
         // recreate OSD config with reduced osdPreset (e.g. switch from >max to max)
         SetupRefreshOsdConfig();
      };

      // replace entry with new maximum
      cList<cOsdItem>::Del(osdPresetItem);
      osdPresetItem = new cMenuEditIntItem(osdPresetString, &osdPreset, 1, temp.osdPresetMax);
      Add(osdPresetItem, false, osdPresetMaxItem);

      Display();
      return state;
   };

   if (item == osdPresetItem) {
      // change between configs
      DEBUG_OT_KEYS("osdPresetItem changed osdPreset=%d", osdPreset);
      SetupRefreshOsdConfig();
      Display();
      return state;
   };

   // OSD hotkey
   if (item == hotkeyLevelMaxItem) {
      // change of OSD menu level max
      DEBUG_OT_KEYS("hotkeyLevelMaxItem changed hotkeyLevelMax=%d (hotkeyLevel=%d)", temp.hotkeyLevelMax, hotkeyLevel);

      if (hotkeyLevel > temp.hotkeyLevelMax) {
         hotkeyLevel = temp.hotkeyLevelMax;
         // recreate Hotkey with reduced hotkeyLevel (e.g. switch from >max to max)
         SetupRefreshHotkeys();
      };

      // replace entry with new maximum
      cList<cOsdItem>::Del(hotkeyLevelItem);
      hotkeyLevelItem = new cMenuEditIntItem(hotkeyLevelString, &hotkeyLevel, 1, temp.hotkeyLevelMax);
      Add(hotkeyLevelItem, false, hotkeyLevelMaxItem);

      Display();
      return state;
   };

   if (item == hotkeyLevelItem) {
      // change between menu levels
      DEBUG_OT_KEYS("hotkeyLevelItem changed hotkeyLevel=%d", hotkeyLevel);
      SetupRefreshHotkeys();
      Display();
      return state;
   };

   // Standard Key change handling for 'Jump to'
   for (int i=0;i<LastActionKey;i++) {
      if (ActionEdits[i].action==item) { //we have a key left/right and one of our items as current
         SetupRefreshKeys();
         Display();
      }
   }

   // Hotkey change handling for 'Jump to'
   int l = hotkeyLevel - 1;
   for (int i=0; i < LastActionHotkey; i++) {
      DEBUG_OT_KEYS("hot key assigment menu i=%d hotkeyLevel=%d action=%d (pagenumber)", i, hotkeyLevel, temp.mapHotkeyToAction[i][l]);
      if (ActionEditsHotkey[i][l].action == item) { //we have a key left/right and one of our items as current
         SetupRefreshHotkeys();
         Display();
      };
   };

   return state;
   //return cMenuSetupPage::ProcessKey(Key);
}


void ActionEdit::Init(cTeletextSetupPage* s, int num, cMenuEditIntItem  *p, cMenuEditStraItem * a) {
   action=a;
   number=p;
   s->Add(action);
   if (s->temp.mapKeyToAction[num] == LastAction) {
      s->Add(number);
   };
}

void ActionEdit::Init(cTeletextSetupPage* s, int num, int level, bool active, cMenuEditIntItem  *p, cMenuEditStraItem * a) {
   action=a;
   number=p;
   if (!active) return;
   s->Add(action);
   if (s->temp.mapHotkeyToAction[num][level] == LastAction) {
      s->Add(number);
   };
}


VDRPLUGINCREATOR(cPluginTeletextosd); // Don't touch this!

// vim: ts=3 sw=3 et
