Beispiel #1
0
bool check(check_event_t event, uint8_t curr, const MenuFuncP *menuTab, uint8_t menuTabSize, const pm_uint8_t *horTab, uint8_t horTabMax, vertpos_t maxrow)
{
    vertpos_t l_posVert = m_posVert;
    horzpos_t l_posHorz = m_posHorz;

    uint8_t maxcol = MAXCOL(l_posVert);

#if defined(NAVIGATION_POT1)
    // check pot 1 - if changed -> scroll values
    static int16_t p1val;
    static int16_t p1valprev;
    p1valdiff = (p1val-calibratedStick[6]) / SCROLL_POT1_TH;
    if (p1valdiff) {
        p1valdiff = (p1valprev-calibratedStick[6]) / 2;
        p1val = calibratedStick[6];
    }
    p1valprev = calibratedStick[6];
#endif

#if defined(NAVIGATION_POT2)
    // check pot 2 - if changed -> scroll menu
    static int16_t p2valprev;
    p2valdiff = (p2valprev-calibratedStick[4]) / SCROLL_TH;
    if (p2valdiff) p2valprev = calibratedStick[4];
#endif

#if defined(NAVIGATION_POT3)
    // check pot 3 if changed -> cursor down/up
    static int16_t p3valprev;
    int8_t scrollUD = (p3valprev-calibratedStick[5]) / SCROLL_TH;
    if (scrollUD) p3valprev = calibratedStick[5];
#else
#define scrollUD 0
#endif

    if (p2valdiff || scrollUD || p1valdiff) backlightOn(); // on keypress turn the light on

    if (menuTab) {
        uint8_t attr = 0;


        if (l_posVert==0 && !calibrationState) {
            attr = INVERS;

            int8_t cc = curr;

            if (p2valdiff) {
                cc = limit((int8_t)0, (int8_t)(cc - p2valdiff), (int8_t)(menuTabSize-1));
            }

            switch(event) {
#if defined(ROTARY_ENCODER_NAVIGATION)
            case EVT_ROTARY_BREAK:
                if (s_editMode < 0 && maxrow > 0) {
                    s_editMode = 0;
                    // TODO ? l_posVert = (horTab && horTab[1]==0xff) ? 2 : 1;
                    l_posHorz = 0;
                }
                else {
                    s_editMode = -1;
                }
                event = 0;
                break;
#endif

#if defined(ROTARY_ENCODER_NAVIGATION)
            case EVT_ROTARY_LEFT:
                if (s_editMode >= 0)
                    break;
#endif
            case EVT_KEY_FIRST(KEY_LEFT):
                if (curr > 0)
                    cc = curr - 1;
                else
                    cc = menuTabSize-1;
                break;

#if defined(ROTARY_ENCODER_NAVIGATION)
            case EVT_ROTARY_RIGHT:
                if (s_editMode >= 0)
                    break;
#endif
            case EVT_KEY_FIRST(KEY_RIGHT):
                if (curr < (menuTabSize-1))
                    cc = curr + 1;
                else
                    cc = 0;
                break;
            }

            if (cc != curr) {
                chainMenu((MenuFuncP)pgm_read_adr(&menuTab[cc]));
                return false;
            }

#if defined(ROTARY_ENCODER_NAVIGATION)
            if (IS_RE_NAVIGATION_ENABLE() && s_editMode < 0)
                attr = INVERS|BLINK;
#endif
        }

        calibrationState = 0;
        displayScreenIndex(curr, menuTabSize, attr);

    }

    DISPLAY_PROGRESS_BAR(menuTab ? lcdLastPos-2*FW-((curr+1)/10*FWNUM)-2 : 20*FW+1);

    if (s_editMode<=0) {
        if (scrollUD) {
            l_posVert = limit((int8_t)0, (int8_t)(l_posVert - scrollUD), (int8_t)maxrow);
            l_posHorz = min((uint8_t)l_posHorz, MAXCOL(l_posVert));
        }

        if (p2valdiff && l_posVert>0) {
            l_posHorz = limit((int8_t)0, (int8_t)((uint8_t)l_posHorz - p2valdiff), (int8_t)maxcol);
        }
    }

    switch(event)
    {
    case EVT_ENTRY:
#if defined(CPUARM)
        menuEntryTime = get_tmr10ms();
#endif
        l_posVert = POS_VERT_INIT;
        l_posHorz = POS_HORZ_INIT(l_posVert);
        SET_SCROLLBAR_X(LCD_W-1);
#if defined(ROTARY_ENCODER_NAVIGATION)
        if (menuTab) {
            s_editMode = EDIT_MODE_INIT;
            break;
        }
        // no break
#else
        s_editMode = EDIT_MODE_INIT;
        break;
#endif

#if defined(ROTARY_ENCODER_NAVIGATION)
    case EVT_ENTRY_UP:
        s_editMode = 0;
        SET_SCROLLBAR_X(LCD_W-1);
        break;

    case EVT_ROTARY_BREAK:
        if (s_editMode > 1) break;
#endif

    case EVT_KEY_FIRST(KEY_ENTER):
        if (!menuTab || l_posVert>0) {
            if (READ_ONLY_UNLOCKED()) {
                s_editMode = (s_editMode<=0);
            }
        }
        break;

#if defined(ROTARY_ENCODER_NAVIGATION)
    case EVT_ROTARY_LONG:
        if (s_editMode > 1) break;
        killEvents(event);
        if (l_posVert != POS_VERT_INIT) {
            l_posVert = POS_VERT_INIT;
            s_editMode = EDIT_MODE_INIT;
            break;
        }
        // no break
#endif
    case EVT_KEY_LONG(KEY_EXIT):
        s_editMode = 0; // TODO needed? we call ENTRY_UP after which does the same
        popMenu();
        return false;

    case EVT_KEY_BREAK(KEY_EXIT):
#if defined(ROTARY_ENCODER_NAVIGATION)
        if (s_editMode == 0)
            s_editMode = EDIT_MODE_INIT;
        else
#endif
            if (s_editMode>0) {
                s_editMode = 0;
                break;
            }

        if (l_posVert==0 || !menuTab) {
            popMenu();  // beeps itself
            return false;
        }
        else {
            AUDIO_MENUS();
            l_posVert = 0;
            l_posHorz = 0;
        }
        break;

    case EVT_KEY_REPT(KEY_RIGHT):  //inc
        if (l_posHorz==maxcol) break;
    // no break

    case EVT_KEY_FIRST(KEY_RIGHT)://inc
        if (!horTab || s_editMode>0) break;

#if defined(ROTARY_ENCODER_NAVIGATION)
        CASE_EVT_ROTARY_MOVE_RIGHT
        if (s_editMode != 0) break;
        if (l_posHorz < maxcol) {
            l_posHorz++;
            break;
        }
        else {
            l_posHorz = 0;
            if (!IS_ROTARY_MOVE_RIGHT(event))
                break;
        }
#else
        INC(l_posHorz, 0, maxcol);
        break;
#endif

    case EVT_KEY_REPT(KEY_DOWN):  //inc
        if (!IS_ROTARY_RIGHT(event) && l_posVert==maxrow) break;
    // no break

    case EVT_KEY_FIRST(KEY_DOWN): //inc
        if (s_editMode>0) break;
        do {
            INC(l_posVert, POS_VERT_INIT, maxrow);
        } while (CURSOR_NOT_ALLOWED_IN_ROW(l_posVert));

#if defined(ROTARY_ENCODER_NAVIGATION)
        s_editMode = 0; // if we go down, we must be in this mode
#endif

        l_posHorz = min(l_posHorz, MAXCOL(l_posVert));
        break;

    case EVT_KEY_REPT(KEY_LEFT):  //dec
        if (l_posHorz==0) break;
    // no break

    case EVT_KEY_FIRST(KEY_LEFT)://dec
        if (!horTab || s_editMode>0) break;

#if defined(ROTARY_ENCODER_NAVIGATION)
        CASE_EVT_ROTARY_MOVE_LEFT
        if (s_editMode != 0) break;
        if (l_posHorz > 0) {
            l_posHorz--;
            break;
        }
        else if (IS_ROTARY_MOVE_LEFT(event) && s_editMode == 0) {
            l_posHorz = 0xff;
        }
        else {
            l_posHorz = maxcol;
            break;
        }
#else
        DEC(l_posHorz, 0, maxcol);
        break;
#endif

    case EVT_KEY_REPT(KEY_UP):  //dec
        if (!IS_ROTARY_LEFT(event) && l_posVert==0) break;
    // no break
    case EVT_KEY_FIRST(KEY_UP): //dec
        if (s_editMode>0) break;

        do {
            DEC(l_posVert, POS_VERT_INIT, maxrow);
        } while (CURSOR_NOT_ALLOWED_IN_ROW(l_posVert));

#if defined(ROTARY_ENCODER_NAVIGATION) || defined(PCBTARANIS)
        s_editMode = 0; // if we go up, we must be in this mode
#endif

        l_posHorz = min((uint8_t)l_posHorz, MAXCOL(l_posVert));
        break;
    }

#if defined(CPUARM)
    if (l_posVert<1) s_pgOfs=0;
    else if (menuTab && horTab) {
        vertpos_t realPosVert = l_posVert;
        vertpos_t realPgOfs = s_pgOfs;
        vertpos_t realMaxrow = maxrow;
        for (vertpos_t i=1; i<=maxrow; i++) {
            if (MAXCOL(i) == HIDDEN_ROW) {
                realMaxrow--;
                if (i < l_posVert)
                    realPosVert--;
                if (i < s_pgOfs)
                    realPgOfs--;
            }
        }
        if (realPosVert>(LCD_LINES-1)+realPgOfs) realPgOfs = realPosVert-(LCD_LINES-1);
        else if (realPosVert<1+realPgOfs) realPgOfs = realPosVert-1;
        s_pgOfs = realPgOfs;
        for (vertpos_t i=1; i<=realPgOfs; i++) {
            if (MAXCOL(i) == HIDDEN_ROW) {
                s_pgOfs++;
            }
        }
        maxrow = realMaxrow;
    }
    else {
        uint8_t max = menuTab ? LCD_LINES-1 : LCD_LINES-2;
        if (l_posVert>max+s_pgOfs) s_pgOfs = l_posVert-max;
        else if (l_posVert<1+s_pgOfs) s_pgOfs = l_posVert-1;
    }

#if LCD_W >= 212
    if (maxrow > LCD_LINES-1 && scrollbar_X)
        displayScrollbar(scrollbar_X, FH, LCD_H-FH, s_pgOfs, menuTab ? maxrow : maxrow+1, LCD_LINES-1);
#endif

#else
    uint8_t max = menuTab ? LCD_LINES-1 : LCD_LINES-2;
    if (l_posVert<1) s_pgOfs=0;
    else if (l_posVert>max+s_pgOfs) s_pgOfs = l_posVert-max;
    else if (l_posVert<1+s_pgOfs) s_pgOfs = l_posVert-1;
#endif
    m_posVert = l_posVert;
    m_posHorz = l_posHorz;
#if !defined(CPUM64)
    // cosmetics on 9x
    if (s_pgOfs > 0) {
        l_posVert--;
        if (l_posVert == s_pgOfs && CURSOR_NOT_ALLOWED_IN_ROW(l_posVert))
            s_pgOfs = l_posVert-1;
    }
#endif
    return true;
}
Beispiel #2
0
const char * displayMenu(uint8_t event)
{
    const char * result = NULL;

    uint8_t display_count = min(s_menu_count, (uint16_t)MENU_MAX_LINES);
    uint8_t y = (display_count >= 5 ? MENU_Y - FH - 1 : MENU_Y);
    lcd_filled_rect(MENU_X, y, MENU_W, display_count * (FH+1) + 2, SOLID, ERASE);
    lcd_rect(MENU_X, y, MENU_W, display_count * (FH+1) + 2);

    for (uint8_t i=0; i<display_count; i++) {
        lcd_putsAtt(MENU_X+6, i*(FH+1) + y + 2, s_menu[i], s_menu_flags);
        if (i == s_menu_item) lcd_filled_rect(MENU_X+1, i*(FH+1) + y + 1, MENU_W-2, 9);
    }

    if (s_menu_count > display_count) {
        displayScrollbar(MENU_X+MENU_W-1, y+1, MENU_MAX_LINES * (FH+1), s_menu_offset, s_menu_count, MENU_MAX_LINES);
    }

    switch(event) {
#if defined(ROTARY_ENCODER_NAVIGATION)
        CASE_EVT_ROTARY_LEFT
#endif
    case EVT_KEY_FIRST(KEY_MOVE_UP):
    case EVT_KEY_REPT(KEY_MOVE_UP):
        if (s_menu_item > 0) {
            s_menu_item--;
        }
#if defined(SDCARD)
        else if (s_menu_offset > 0) {
            s_menu_offset--;
            result = STR_UPDATE_LIST;
        }
#endif
        else {
            s_menu_item = display_count - 1;
#if defined(SDCARD)
            if (s_menu_count > MENU_MAX_LINES) {
                s_menu_offset = s_menu_count - display_count;
                result = STR_UPDATE_LIST;
            }
#endif
        }
        break;

#if defined(ROTARY_ENCODER_NAVIGATION)
        CASE_EVT_ROTARY_RIGHT
#endif
    case EVT_KEY_FIRST(KEY_MOVE_DOWN):
    case EVT_KEY_REPT(KEY_MOVE_DOWN):
        if (s_menu_item < display_count - 1 && s_menu_offset + s_menu_item + 1 < s_menu_count) {
            s_menu_item++;
        }
#if defined(SDCARD)
        else if (s_menu_count > s_menu_offset + display_count) {
            s_menu_offset++;
            result = STR_UPDATE_LIST;
        }
#endif
        else {
            s_menu_item = 0;
#if defined(SDCARD)
            if (s_menu_offset) {
                s_menu_offset = 0;
                result = STR_UPDATE_LIST;
            }
#endif
        }
        break;
        CASE_EVT_ROTARY_BREAK
    case EVT_KEY_BREAK(KEY_ENTER):
        result = s_menu[s_menu_item];
        // no break
#if defined(ROTARY_ENCODER_NAVIGATION)
        CASE_EVT_ROTARY_LONG
        killEvents(event);
#endif
    case EVT_KEY_BREAK(KEY_EXIT):
        s_menu_count = 0;
        s_menu_item = 0;
        s_menu_flags = 0;
        s_menu_offset = 0;
        break;
    }

    return result;
}
Beispiel #3
0
bool check(check_event_t event, uint8_t curr, const MenuFuncP *menuTab, uint8_t menuTabSize, const pm_uint8_t *horTab, uint8_t horTabMax, vertpos_t maxrow, uint8_t flags)
{
    vertpos_t l_posVert = m_posVert;
    horzpos_t l_posHorz = m_posHorz;

    uint8_t maxcol = MAXCOL(l_posVert);

#define scrollUD 0

    if (p2valdiff || scrollUD || p1valdiff) backlightOn(); // on keypress turn the light on

    if (menuTab) {
        uint8_t attr = 0;

        int8_t cc = curr;
        switch (event) {
        case EVT_KEY_LONG(KEY_MENU):
            if (menuTab == menuTabModel) {
                killEvents(event);
                if (modelHasNotes()) {
                    MENU_ADD_SD_ITEM(STR_VIEW_CHANNELS);
                    MENU_ADD_ITEM(STR_VIEW_NOTES);
                    menuHandler = onLongMenuPress;
                }
                else {
                    pushMenu(menuChannelsView);
                    return false;
                }
            }
            break;

        case EVT_KEY_LONG(KEY_PAGE):
            if (curr > 0)
                cc = curr - 1;
            else
                cc = menuTabSize-1;
            killEvents(event);
            break;

        case EVT_KEY_BREAK(KEY_PAGE):
            if (curr < (menuTabSize-1))
                cc = curr + 1;
            else
                cc = 0;
            break;
        }

        if (!calibrationState && cc != curr) {
            chainMenu((MenuFuncP)pgm_read_adr(&menuTab[cc]));
            return false;
        }

        if (!(flags&CHECK_FLAG_NO_SCREEN_INDEX)) {
            displayScreenIndex(curr, menuTabSize, attr);
        }

        lcd_filled_rect(0, 0, LCD_W, FH, SOLID, FILL_WHITE|GREY_DEFAULT);
    }

    DISPLAY_PROGRESS_BAR(menuTab ? lcdLastPos-2*FW-((curr+1)/10*FWNUM)-2 : 20*FW+1);

    if (s_editMode<=0) {
        if (scrollUD) {
            l_posVert = limit((int8_t)0, (int8_t)(l_posVert - scrollUD), (int8_t)maxrow);
            l_posHorz = min((uint8_t)l_posHorz, MAXCOL(l_posVert));
        }

        if (p2valdiff && l_posVert>0) {
            l_posHorz = limit((int8_t)0, (int8_t)((uint8_t)l_posHorz - p2valdiff), (int8_t)maxcol);
        }
    }

    switch(event)
    {
    case EVT_ENTRY:
        menuEntryTime = get_tmr10ms();
        l_posVert = POS_VERT_INIT;
        l_posHorz = POS_HORZ_INIT(l_posVert);
        SET_SCROLLBAR_X(LCD_W-1);
#if defined(ROTARY_ENCODER_NAVIGATION)
        if (menuTab) {
            s_editMode = EDIT_MODE_INIT;
            break;
        }
        // no break
#else
        s_editMode = EDIT_MODE_INIT;
        break;
#endif

    case EVT_ENTRY_UP:
        menuEntryTime = get_tmr10ms();
        s_editMode = 0;
        l_posHorz = POS_HORZ_INIT(l_posVert);
        SET_SCROLLBAR_X(LCD_W-1);
        break;

    case EVT_ROTARY_BREAK:
        if (s_editMode > 1) break;
        if (m_posHorz < 0 && maxcol > 0 && READ_ONLY_UNLOCKED()) {
            l_posHorz = 0;
            break;
        }
        if (!menuTab || l_posVert>0) {
            if (READ_ONLY_UNLOCKED()) {
                s_editMode = (s_editMode<=0);
            }
        }
        break;

#if defined(ROTARY_ENCODER_NAVIGATION)
    case EVT_ROTARY_LONG:
        if (s_editMode > 1) break;
        killEvents(event);
        if (l_posVert != POS_VERT_INIT) {
            l_posVert = POS_VERT_INIT;
            s_editMode = EDIT_MODE_INIT;
            break;
        }
        // no break
#endif
    case EVT_KEY_LONG(KEY_EXIT):
        s_editMode = 0; // TODO needed? we call ENTRY_UP after which does the same
        popMenu();
        return false;

    case EVT_KEY_BREAK(KEY_EXIT):
#if defined(ROTARY_ENCODER_NAVIGATION)
        if (s_editMode == 0)
            s_editMode = EDIT_MODE_INIT;
        else
#endif
            if (s_editMode>0) {
                s_editMode = 0;
                break;
            }

        if (l_posHorz >= 0 && (COLATTR(l_posVert) & NAVIGATION_LINE_BY_LINE)) {
            l_posHorz = -1;
        }
        else
        {
            uint8_t posVertInit = POS_VERT_INIT;
            if (s_pgOfs != 0 || l_posVert != posVertInit) {
                s_pgOfs = 0;
                l_posVert = posVertInit;
                l_posHorz = POS_HORZ_INIT(l_posVert);
            }
            else {
                popMenu();
                return false;
            }
        }
        break;

        CASE_EVT_ROTARY_MOVE_RIGHT
        if (s_editMode != 0) break;
        if ((COLATTR(l_posVert) & NAVIGATION_LINE_BY_LINE)) {
            if (l_posHorz >= 0) {
                INC(l_posHorz, 0, maxcol);
                break;
            }
        }
        else {
            if (l_posHorz < maxcol) {
                l_posHorz++;
                break;
            }
            else {
                l_posHorz = 0;
                if (!IS_ROTARY_MOVE_RIGHT(event))
                    break;
            }
        }

        do {
            INC(l_posVert, POS_VERT_INIT, maxrow);
        } while (CURSOR_NOT_ALLOWED_IN_ROW(l_posVert));

        s_editMode = 0; // if we go down, we must be in this mode

        l_posHorz = POS_HORZ_INIT(l_posVert);
        break;

        CASE_EVT_ROTARY_MOVE_LEFT
        if (s_editMode != 0) break;
        if ((COLATTR(l_posVert) & NAVIGATION_LINE_BY_LINE)) {
            if (l_posHorz >= 0) {
                DEC(l_posHorz, 0, maxcol);
                break;
            }
        }
        else {
            if (l_posHorz > 0) {
                l_posHorz--;
                break;
            }
            else if (IS_ROTARY_MOVE_LEFT(event) && s_editMode == 0) {
                l_posHorz = 0xff;
            }
            else {
                l_posHorz = maxcol;
                break;
            }
        }

        do {
            DEC(l_posVert, POS_VERT_INIT, maxrow);
        } while (CURSOR_NOT_ALLOWED_IN_ROW(l_posVert));

        s_editMode = 0; // if we go up, we must be in this mode

        if ((COLATTR(l_posVert) & NAVIGATION_LINE_BY_LINE))
            l_posHorz = -1;
        else
            l_posHorz = min((uint8_t)l_posHorz, MAXCOL(l_posVert));

        break;
    }

#if defined(CPUARM)
    if (l_posVert<1) {
        s_pgOfs=0;
    }
    else if (menuTab && horTab) {
        if (maxrow > LCD_LINES-1) {
            while (1) {
                vertpos_t line = s_pgOfs+1;
                for (int numLines=0; line<=maxrow && numLines<LCD_LINES-1; line++) {
                    if (MAXCOL(line) != HIDDEN_ROW) {
                        numLines++;
                    }
                }
                int max = line - s_pgOfs - 1;
                if (l_posVert > max+s_pgOfs) {
                    s_pgOfs++;
                }
                else if (l_posVert < 1+s_pgOfs) {
                    s_pgOfs--;
                }
                else {
                    break;
                }
            }
        }
    }
    else {
        uint8_t max = menuTab ? LCD_LINES-1 : LCD_LINES-2;
        if (l_posVert>max+s_pgOfs) {
            s_pgOfs = l_posVert-max;
        }
        else if (l_posVert<1+s_pgOfs) {
            s_pgOfs = l_posVert-1;
        }
    }

#if LCD_W >= 212
    if (maxrow > LCD_LINES-1 && scrollbar_X)
        displayScrollbar(scrollbar_X, FH, LCD_H-FH, s_pgOfs, menuTab ? maxrow : maxrow+1, LCD_LINES-1);
#endif

#else
    uint8_t max = menuTab ? LCD_LINES-1 : LCD_LINES-2;
    if (l_posVert<1) s_pgOfs=0;
    else if (l_posVert>max+s_pgOfs) s_pgOfs = l_posVert-max;
    else if (l_posVert<1+s_pgOfs) s_pgOfs = l_posVert-1;
#endif
    m_posVert = l_posVert;
    m_posHorz = l_posHorz;
    if (s_pgOfs > 0) {
        l_posVert--;
        if (l_posVert == s_pgOfs && CURSOR_NOT_ALLOWED_IN_ROW(l_posVert)) {
            s_pgOfs = l_posVert-1;
        }
    }
    return true;
}
Beispiel #4
0
void check(const char *name, check_event_t event, uint8_t curr, const menuHandlerFunc *menuTab, uint8_t menuTabSize, const pm_uint8_t *horTab, uint8_t horTabMax, vertpos_t rowcount, uint8_t flags)
{
  vertpos_t l_posVert = menuVerticalPosition;
  horzpos_t l_posHorz = menuHorizontalPosition;

  uint8_t maxcol = MAXCOL(l_posVert);

  if (menuTab) {
    int cc = curr;
    switch (event) {
      case EVT_KEY_LONG(KEY_MENU):
        if (menuTab == menuTabModel) {
          killEvents(event);
          if (modelHasNotes()) {
            POPUP_MENU_ADD_SD_ITEM(STR_VIEW_CHANNELS);
            POPUP_MENU_ADD_ITEM(STR_VIEW_NOTES);
            popupMenuHandler = onLongMenuPress;
          }
          else {
            pushMenu(menuChannelsView);
          }
        }
        break;

      case EVT_KEY_LONG(KEY_PAGE):
        if (curr > 0)
          cc = curr - 1;
        else
          cc = menuTabSize-1;
        killEvents(event);
        break;

      case EVT_KEY_BREAK(KEY_PAGE):
        if (curr < (menuTabSize-1))
          cc = curr + 1;
        else
          cc = 0;
        break;
    }

    if (!calibrationState && cc != curr) {
      chainMenu((menuHandlerFunc)pgm_read_adr(&menuTab[cc]));
    }

    if (!(flags&CHECK_FLAG_NO_SCREEN_INDEX)) {
      displayScreenIndex(curr, menuTabSize, 0);
    }

    drawFilledRect(0, 0, LCD_W, MENU_HEADER_HEIGHT, SOLID, FILL_WHITE|GREY_DEFAULT);
  }

  DISPLAY_PROGRESS_BAR(menuTab ? lcdLastPos-2*FW-((curr+1)/10*FWNUM)-2 : 20*FW+1);

  switch(event)
  {
    case EVT_ENTRY:
      menuEntryTime = get_tmr10ms();
      l_posVert = POS_VERT_INIT;
      l_posHorz = POS_HORZ_INIT(l_posVert);
      SET_SCROLLBAR_X(LCD_W-1);
      s_editMode = EDIT_MODE_INIT;
      break;

    case EVT_ENTRY_UP:
      menuEntryTime = get_tmr10ms();
      s_editMode = 0;
      l_posHorz = POS_HORZ_INIT(l_posVert);
      SET_SCROLLBAR_X(LCD_W-1);
      break;

    case EVT_ROTARY_BREAK:
      if (s_editMode > 1) break;
      if (menuHorizontalPosition < 0 && maxcol > 0 && READ_ONLY_UNLOCKED()) {
        l_posHorz = 0;
        break;
      }
      if (READ_ONLY_UNLOCKED()) {
        s_editMode = (s_editMode<=0);
      }
      break;

    case EVT_KEY_LONG(KEY_EXIT):
      s_editMode = 0; // TODO needed? we call ENTRY_UP after which does the same
      popMenu();
      break;

    case EVT_KEY_BREAK(KEY_EXIT):
      if (s_editMode>0) {
        s_editMode = 0;
        break;
      }

      if (l_posHorz >= 0 && (COLATTR(l_posVert) & NAVIGATION_LINE_BY_LINE)) {
        l_posHorz = -1;
      }
      else
      {
        uint8_t posVertInit = POS_VERT_INIT;
        if (menuVerticalOffset != 0 || l_posVert != posVertInit) {
          menuVerticalOffset = 0;
          l_posVert = posVertInit;
          l_posHorz = POS_HORZ_INIT(l_posVert);
        }
        else {
          popMenu();
        }
      }
      break;

    CASE_EVT_ROTARY_MOVE_RIGHT
      if (s_editMode != 0) break;
      if ((COLATTR(l_posVert) & NAVIGATION_LINE_BY_LINE)) {
        if (l_posHorz >= 0) {
          INC(l_posHorz, 0, maxcol);
          break;
        }
      }
      else {
        if (l_posHorz < maxcol) {
          l_posHorz++;
          break;
        }
        else {
          l_posHorz = 0;
          if (!IS_ROTARY_MOVE_RIGHT(event))
            break;
        }
      }

      do {
        INC(l_posVert, POS_VERT_INIT, rowcount-1);
      } while (CURSOR_NOT_ALLOWED_IN_ROW(l_posVert));

      s_editMode = 0; // if we go down, we must be in this mode

      l_posHorz = POS_HORZ_INIT(l_posVert);
      break;

    CASE_EVT_ROTARY_MOVE_LEFT
      if (s_editMode != 0) break;
      if ((COLATTR(l_posVert) & NAVIGATION_LINE_BY_LINE)) {
        if (l_posHorz >= 0) {
          DEC(l_posHorz, 0, maxcol);
          break;
        }
      }
      else {
        if (l_posHorz > 0) {
          l_posHorz--;
          break;
        }
        else if (IS_ROTARY_MOVE_LEFT(event) && s_editMode == 0) {
          l_posHorz = 0xff;
        }
        else {
          l_posHorz = maxcol;
          break;
        }
      }

      do {
        DEC(l_posVert, POS_VERT_INIT, rowcount-1);
      } while (CURSOR_NOT_ALLOWED_IN_ROW(l_posVert));

      s_editMode = 0; // if we go up, we must be in this mode

      if ((COLATTR(l_posVert) & NAVIGATION_LINE_BY_LINE))
        l_posHorz = -1;
      else
        l_posHorz = min((uint8_t)l_posHorz, MAXCOL(l_posVert));

      break;
  }

  int linesCount = rowcount;

  if (l_posVert == 0 || (l_posVert==1 && MAXCOL(vertpos_t(0)) >= HIDDEN_ROW) || (l_posVert==2 && MAXCOL(vertpos_t(0)) >= HIDDEN_ROW && MAXCOL(vertpos_t(1)) >= HIDDEN_ROW)) {
    menuVerticalOffset = 0;
    if (horTab) {
      linesCount = 0;
      for (int i=0; i<rowcount; i++) {
        if (i>horTabMax || horTab[i] != HIDDEN_ROW) {
          linesCount++;
        }
      }
    }
  }
  else if (horTab) {
    if (rowcount > NUM_BODY_LINES) {
      while (1) {
        vertpos_t firstLine = 0;
        for (int numLines=0; firstLine<rowcount && numLines<menuVerticalOffset; firstLine++) {
          if (firstLine>=horTabMax || horTab[firstLine] != HIDDEN_ROW) {
            numLines++;
          }
        }
        if (l_posVert < firstLine) {
          menuVerticalOffset--;
        }
        else {
          vertpos_t lastLine = firstLine;
          for (int numLines=0; lastLine<rowcount && numLines<NUM_BODY_LINES; lastLine++) {
            if (lastLine>=horTabMax || horTab[lastLine] != HIDDEN_ROW) {
              numLines++;
            }
          }
          if (l_posVert >= lastLine) {
            menuVerticalOffset++;
          }
          else {
            linesCount = menuVerticalOffset + NUM_BODY_LINES;
            for (int i=lastLine; i<rowcount; i++) {
              if (i>horTabMax || horTab[i] != HIDDEN_ROW) {
                linesCount++;
              }
            }
            break;
          }
        }
      }
    }
  }
  else {
    if (l_posVert>=NUM_BODY_LINES+menuVerticalOffset) {
      menuVerticalOffset = l_posVert-NUM_BODY_LINES+1;
    }
    else if (l_posVert<menuVerticalOffset) {
      menuVerticalOffset = l_posVert;
    }
  }

  if (scrollbar_X && linesCount > NUM_BODY_LINES) {
    displayScrollbar(scrollbar_X, MENU_HEADER_HEIGHT, LCD_H-MENU_HEADER_HEIGHT, menuVerticalOffset, linesCount, NUM_BODY_LINES);
  }

  if (name) {
    title(name);
  }

  menuVerticalPosition = l_posVert;
  menuHorizontalPosition = l_posHorz;
}