#!/usr/bin/env python3 """Fix the Messages menus: wrong language string index, and hand-rolled navigation. Two defects: - the main menu entry pointed at string 281 while .messages is index 280, so the entry rendered as "GD77LANG" (it picked up a neighbouring field); - the screens tracked the selection in their own variables and drew entries at a fixed offset, instead of using the menu system's list handling. That makes the cursor behave wrongly (no scrolling, wrong highlight). Both screens are rewritten to the pattern used by the stock list menus: menuDataGlobal.currentItemIndex + menuGetMenuOffset() + menuSystemMenuIncrement(). """ W = "/home/viktor/ogd77rel/uv_src/OpenGD77_MDUV380_DM1701_20260130/MDUV380_firmware/application" # ---- 1. correct the main menu string index ------------------------------ p = W + "/source/user_interface/menuSystem.c" s = open(p).read() if "{ 281, MENU_MESSAGES" in s: s = s.replace("{ 281, MENU_MESSAGES },", "{ 280, MENU_MESSAGES },", 1) open(p, "w").write(s) print("menuSystem.c: string index corrected 281 -> 280") else: print("menuSystem.c: index already correct") # ---- 2. rewrite the screens using the standard list handling ------------ p = W + "/source/user_interface/menuMessages.c" src = open(p).read() header_end = src.index("// Entries of the top level Messages menu.") header = src[:header_end] body = '''// Entries of the top level Messages menu. enum { MESSAGES_ITEM_NEW = 0, MESSAGES_ITEM_INBOX, MESSAGES_ITEM_OUTBOX, NUM_MESSAGES_ITEMS }; // Entries of the Inbox / Outbox menus. enum { FOLDER_ITEM_LIST = 0, FOLDER_ITEM_DELETE_ALL, NUM_FOLDER_ITEMS }; static bool showingOutbox = false; // which folder the list screens act on static bool viewingMessage = false;// a single message is open static menuStatus_t exitCode = MENU_STATUS_SUCCESS; static void messagesUpdateScreen(void); static void messagesHandleEvent(uiEvent_t *ev); static void folderUpdateScreen(void); static void folderHandleEvent(uiEvent_t *ev); static void listUpdateScreen(void); static void listHandleEvent(uiEvent_t *ev); // ---------------------------------------------------------------- Messages menuStatus_t menuMessages(uiEvent_t *ev, bool isFirstRun) { if (isFirstRun) { menuDataGlobal.numItems = NUM_MESSAGES_ITEMS; menuDataGlobal.currentItemIndex = 0; messagesUpdateScreen(); return (MENU_STATUS_LIST_TYPE | MENU_STATUS_SUCCESS); } exitCode = MENU_STATUS_SUCCESS; if (ev->hasEvent) { messagesHandleEvent(ev); } return exitCode; } static void messagesUpdateScreen(void) { char buf[SCREEN_LINE_BUFFER_SIZE]; int mNum; displayClearBuf(); menuDisplayTitle(currentLanguage->messages); for (int i = MENU_START_ITERATION_VALUE; i <= MENU_END_ITERATION_VALUE; i++) { mNum = menuGetMenuOffset(NUM_MESSAGES_ITEMS, i); if (mNum == MENU_OFFSET_BEFORE_FIRST_ENTRY) { continue; } else if (mNum == MENU_OFFSET_AFTER_LAST_ENTRY) { break; } switch (mNum) { case MESSAGES_ITEM_NEW: snprintf(buf, SCREEN_LINE_BUFFER_SIZE, "New msg"); break; case MESSAGES_ITEM_INBOX: snprintf(buf, SCREEN_LINE_BUFFER_SIZE, "Inbox (%u)", (unsigned int)smsInboxCount()); break; case MESSAGES_ITEM_OUTBOX: snprintf(buf, SCREEN_LINE_BUFFER_SIZE, "Outbox (%u)", (unsigned int)smsOutboxCount()); break; default: buf[0] = 0; break; } menuDisplayEntry(i, mNum, buf, 0, THEME_ITEM_FG_MENU_ITEM, THEME_ITEM_COLOUR_NONE, THEME_ITEM_BG); } displayRender(); } static void messagesHandleEvent(uiEvent_t *ev) { if (KEYCHECK_PRESS(ev->keys, KEY_DOWN)) { menuSystemMenuIncrement(&menuDataGlobal.currentItemIndex, NUM_MESSAGES_ITEMS); messagesUpdateScreen(); exitCode |= MENU_STATUS_LIST_TYPE; return; } if (KEYCHECK_PRESS(ev->keys, KEY_UP)) { menuSystemMenuDecrement(&menuDataGlobal.currentItemIndex, NUM_MESSAGES_ITEMS); messagesUpdateScreen(); exitCode |= MENU_STATUS_LIST_TYPE; return; } if (KEYCHECK_SHORTUP(ev->keys, KEY_GREEN)) { switch (menuDataGlobal.currentItemIndex) { case MESSAGES_ITEM_NEW: // Sending is not implemented yet; say so rather than opening a dead screen. uiNotificationShow(NOTIFICATION_TYPE_MESSAGE, NOTIFICATION_ID_MESSAGE, 2000, "Not yet", true); break; case MESSAGES_ITEM_INBOX: showingOutbox = false; menuSystemPushNewMenu(MENU_MESSAGES_FOLDER); break; case MESSAGES_ITEM_OUTBOX: showingOutbox = true; menuSystemPushNewMenu(MENU_MESSAGES_FOLDER); break; } return; } if (KEYCHECK_SHORTUP(ev->keys, KEY_RED)) { menuSystemPopPreviousMenu(); } } // ------------------------------------------------------- Inbox / Outbox menu menuStatus_t menuMessagesFolder(uiEvent_t *ev, bool isFirstRun) { if (isFirstRun) { menuDataGlobal.numItems = NUM_FOLDER_ITEMS; menuDataGlobal.currentItemIndex = 0; folderUpdateScreen(); return (MENU_STATUS_LIST_TYPE | MENU_STATUS_SUCCESS); } exitCode = MENU_STATUS_SUCCESS; if (ev->hasEvent) { folderHandleEvent(ev); } return exitCode; } static void folderUpdateScreen(void) { char buf[SCREEN_LINE_BUFFER_SIZE]; uint8_t count = (showingOutbox ? smsOutboxCount() : smsInboxCount()); int mNum; displayClearBuf(); menuDisplayTitle(showingOutbox ? "Outbox" : "Inbox"); for (int i = MENU_START_ITERATION_VALUE; i <= MENU_END_ITERATION_VALUE; i++) { mNum = menuGetMenuOffset(NUM_FOLDER_ITEMS, i); if (mNum == MENU_OFFSET_BEFORE_FIRST_ENTRY) { continue; } else if (mNum == MENU_OFFSET_AFTER_LAST_ENTRY) { break; } if (mNum == FOLDER_ITEM_LIST) { snprintf(buf, SCREEN_LINE_BUFFER_SIZE, "%s (%u)", (showingOutbox ? "Outbox List" : "Inbox List"), (unsigned int)count); } else { snprintf(buf, SCREEN_LINE_BUFFER_SIZE, "Delete All"); } menuDisplayEntry(i, mNum, buf, 0, THEME_ITEM_FG_MENU_ITEM, THEME_ITEM_COLOUR_NONE, THEME_ITEM_BG); } displayRender(); } static void folderHandleEvent(uiEvent_t *ev) { if (KEYCHECK_PRESS(ev->keys, KEY_DOWN)) { menuSystemMenuIncrement(&menuDataGlobal.currentItemIndex, NUM_FOLDER_ITEMS); folderUpdateScreen(); exitCode |= MENU_STATUS_LIST_TYPE; return; } if (KEYCHECK_PRESS(ev->keys, KEY_UP)) { menuSystemMenuDecrement(&menuDataGlobal.currentItemIndex, NUM_FOLDER_ITEMS); folderUpdateScreen(); exitCode |= MENU_STATUS_LIST_TYPE; return; } if (KEYCHECK_SHORTUP(ev->keys, KEY_GREEN)) { if (menuDataGlobal.currentItemIndex == FOLDER_ITEM_LIST) { viewingMessage = false; menuSystemPushNewMenu(MENU_MESSAGES_LIST); } else { if (showingOutbox) { smsOutboxClear(); } else { smsInboxClear(); } soundSetMelody(MELODY_ACK_BEEP); folderUpdateScreen(); } return; } if (KEYCHECK_SHORTUP(ev->keys, KEY_RED)) { menuSystemPopPreviousMenu(); } } // ------------------------------------------------------------- message list menuStatus_t menuMessagesList(uiEvent_t *ev, bool isFirstRun) { if (isFirstRun) { menuDataGlobal.numItems = (showingOutbox ? smsOutboxCount() : smsInboxCount()); menuDataGlobal.currentItemIndex = 0; viewingMessage = false; listUpdateScreen(); return (MENU_STATUS_LIST_TYPE | MENU_STATUS_SUCCESS); } exitCode = MENU_STATUS_SUCCESS; if (ev->hasEvent) { listHandleEvent(ev); } return exitCode; } static void listUpdateScreen(void) { char buf[SCREEN_LINE_BUFFER_SIZE]; uint8_t count = (showingOutbox ? smsOutboxCount() : smsInboxCount()); int mNum; displayClearBuf(); if (count == 0) { menuDisplayTitle(showingOutbox ? "Outbox" : "Inbox"); displayPrintCentered(32, "Empty", FONT_SIZE_3); displayRender(); return; } if (viewingMessage) { const smsMessage_t *msg = (showingOutbox ? smsOutboxGet((uint8_t)menuDataGlobal.currentItemIndex) : smsInboxGet((uint8_t)menuDataGlobal.currentItemIndex)); if (msg != NULL) { const int charsPerLine = 20; int len = (int)strlen(msg->text); snprintf(buf, SCREEN_LINE_BUFFER_SIZE, "%u", (unsigned int)(showingOutbox ? msg->dstId : msg->srcId)); menuDisplayTitle(buf); // Wrap the text so long messages are readable instead of being cut off. for (int line = 0; (line < 4) && ((line * charsPerLine) < len); line++) { int n = len - (line * charsPerLine); if (n > charsPerLine) { n = charsPerLine; } memcpy(buf, &msg->text[line * charsPerLine], n); buf[n] = 0; displayPrintCore(0, (16 + (line * 12)), buf, FONT_SIZE_2, TEXT_ALIGN_LEFT, false); } } displayRender(); return; } menuDisplayTitle(showingOutbox ? "Outbox" : "Inbox"); for (int i = MENU_START_ITERATION_VALUE; i <= MENU_END_ITERATION_VALUE; i++) { mNum = menuGetMenuOffset(count, i); if (mNum == MENU_OFFSET_BEFORE_FIRST_ENTRY) { continue; } else if (mNum == MENU_OFFSET_AFTER_LAST_ENTRY) { break; } const smsMessage_t *m = (showingOutbox ? smsOutboxGet((uint8_t)mNum) : smsInboxGet((uint8_t)mNum)); if (m == NULL) { continue; } snprintf(buf, SCREEN_LINE_BUFFER_SIZE, "%s%u %.8s", (m->unread ? "*" : ""), (unsigned int)(showingOutbox ? m->dstId : m->srcId), m->text); menuDisplayEntry(i, mNum, buf, 0, THEME_ITEM_FG_MENU_ITEM, THEME_ITEM_COLOUR_NONE, THEME_ITEM_BG); } displayRender(); } static void listHandleEvent(uiEvent_t *ev) { uint8_t count = (showingOutbox ? smsOutboxCount() : smsInboxCount()); if ((viewingMessage == false) && (count > 0)) { if (KEYCHECK_PRESS(ev->keys, KEY_DOWN)) { menuSystemMenuIncrement(&menuDataGlobal.currentItemIndex, count); listUpdateScreen(); exitCode |= MENU_STATUS_LIST_TYPE; return; } if (KEYCHECK_PRESS(ev->keys, KEY_UP)) { menuSystemMenuDecrement(&menuDataGlobal.currentItemIndex, count); listUpdateScreen(); exitCode |= MENU_STATUS_LIST_TYPE; return; } } if (KEYCHECK_SHORTUP(ev->keys, KEY_GREEN) && (count > 0)) { if (viewingMessage == false) { viewingMessage = true; if (showingOutbox == false) { smsMarkRead(); } listUpdateScreen(); } return; } if (KEYCHECK_SHORTUP(ev->keys, KEY_RED)) { if (viewingMessage) { viewingMessage = false; listUpdateScreen(); return; } menuSystemPopPreviousMenu(); } } ''' open(p, "w").write(header + body) print("menuMessages.c: rewritten with standard list navigation")