339 lines
9.3 KiB
C
339 lines
9.3 KiB
C
/*
|
||
* Copyright (C) 2019-2025 Roger Clark, VK3KYY / G4KYF
|
||
* Daniel Caujolle-Bert, F1RMB
|
||
*
|
||
*
|
||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions
|
||
* are met:
|
||
*
|
||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||
*
|
||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
|
||
* in the documentation and/or other materials provided with the distribution.
|
||
*
|
||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived
|
||
* from this software without specific prior written permission.
|
||
*
|
||
* 4. Use of this source code or binary releases for commercial purposes is strictly forbidden. This includes, without limitation,
|
||
* incorporation in a commercial product or incorporation into a product or project which allows commercial use.
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
*
|
||
*/
|
||
#include "user_interface/uiGlobals.h"
|
||
#include "user_interface/menuSystem.h"
|
||
#include "user_interface/uiLocalisation.h"
|
||
#include "user_interface/uiUtilities.h"
|
||
|
||
#define CHAR_CONSTANTS_ONLY // Needed to get FONT_CHAR_* glyph offsets
|
||
#if defined(PLATFORM_MD9600)
|
||
#if defined(LANGUAGE_BUILD_JAPANESE)
|
||
#include "hardware/ST7567_charset_JA.h"
|
||
#else
|
||
#include "hardware/ST7567_charset.h"
|
||
#endif
|
||
#elif (defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017))
|
||
#if defined(LANGUAGE_BUILD_JAPANESE)
|
||
#include "hardware/HX8353E_charset_JA.h"
|
||
#else
|
||
#include "hardware/HX8353E_charset.h"
|
||
#endif
|
||
#else
|
||
#if defined(LANGUAGE_BUILD_JAPANESE)
|
||
#include "hardware/UC1701_charset_JA.h"
|
||
#else
|
||
#include "hardware/UC1701_charset.h"
|
||
#endif
|
||
#endif
|
||
|
||
static void updateScreen(bool isFirstRun);
|
||
static void handleEvent(uiEvent_t *ev);
|
||
static menuStatus_t menuLanguageExitCode = MENU_STATUS_SUCCESS;
|
||
|
||
|
||
static void clearNonLatinChar(uint8_t *str)
|
||
{
|
||
#if ! defined(LANGUAGE_BUILD_JAPANESE)
|
||
uint8_t *p = str;
|
||
|
||
while (*p != '\0')
|
||
{
|
||
switch (*p)
|
||
{
|
||
case FONT_CHAR_CAPITAL_A_WITH_OGONEK: // Ą
|
||
case FONT_CHAR_SMALL_A_WITH_OGONEK: // ą
|
||
case 192: // À
|
||
case 193: // Á
|
||
case 194: // Â
|
||
case 195: // Ã
|
||
case 196: // Ä
|
||
case 197: // Å
|
||
case 224: // à
|
||
case 225: // á
|
||
case 226: // â
|
||
case 227: // ã
|
||
case 228: // ä
|
||
case 229: // å
|
||
*p = 'a';
|
||
break;
|
||
|
||
case FONT_CHAR_CAPITAL_C_WITH_CARON: // Č
|
||
case FONT_CHAR_SMALL_C_WITH_CARON: // č
|
||
case FONT_CHAR_CAPITAL_C_WITH_ACUTE: // Ć
|
||
case FONT_CHAR_SMALL_C_WITH_ACUTE: // ć
|
||
case 199: // Ç
|
||
case 231: // ç
|
||
*p = 'c';
|
||
break;
|
||
|
||
case FONT_CHAR_CAPITAL_E_WITH_OGONEK: // Ę
|
||
case FONT_CHAR_SMALL_E_WITH_OGONEK: // ę
|
||
case FONT_CHAR_CAPITAL_E_WITH_CARON: // Ě
|
||
case FONT_CHAR_SMALL_E_WITH_CARON: // ě
|
||
case 200: // È
|
||
case 201: // É
|
||
case 202: // Ê
|
||
case 203: // Ë
|
||
case 232: // è
|
||
case 233: // é
|
||
case 234: // ê
|
||
case 235: // ë
|
||
*p = 'e';
|
||
break;
|
||
|
||
case FONT_CHAR_G_CAPITAL_WITH_BREVE: // Ğ
|
||
case FONT_CHAR_G_SMALL_WITH_BREVE: // ğ
|
||
*p = 'g';
|
||
break;
|
||
|
||
case FONT_CHAR_SMALL_I_DOTLESS: // ı
|
||
case FONT_CHAR_CAPITAL_I_WITH_DOT: // İ
|
||
case 204: // Ì
|
||
case 205: // Í
|
||
case 206: // Î
|
||
case 207: // Ï
|
||
case 236: // ì
|
||
case 237: // í
|
||
case 238: // î
|
||
case 239: // ï
|
||
*p ='i';
|
||
break;
|
||
|
||
case FONT_CHAR_CAPITAL_L_WITH_STROKE: // Ł
|
||
case FONT_CHAR_SMALL_L_WITH_STROKE: // ł
|
||
*p = 'l';
|
||
break;
|
||
|
||
case FONT_CHAR_CAPITAL_N_WITH_ACUTE: // Ń
|
||
case FONT_CHAR_SMALL_N_WITH_ACUTE: // ń
|
||
case 209: // Ñ
|
||
case 241: // ñ
|
||
*p = 'n';
|
||
break;
|
||
|
||
case 210: // Ò
|
||
case 211: // Ó
|
||
case 212: // Ô
|
||
case 213: // Õ
|
||
case 214: // Ö
|
||
case 216: // Ø
|
||
case 242: // ò
|
||
case 243: // ó
|
||
case 244: // ô
|
||
case 245: // õ
|
||
case 246: // ö
|
||
case 248: // ø
|
||
case 240: // ð
|
||
*p = 'o';
|
||
break;
|
||
|
||
case FONT_CHAR_CAPITAL_R_WITH_CARON: // Ř
|
||
case FONT_CHAR_SMALL_R_WITH_CARON: // ř
|
||
case 208: // Ð
|
||
*p = 'r';
|
||
break;
|
||
|
||
case FONT_CHAR_CAPITAL_S_WITH_ACUTE: // Ś
|
||
case FONT_CHAR_SMALL_S_WITH_ACUTE: // ś
|
||
case FONT_CHAR_CAPITAL_S_WITH_CARON: // Š
|
||
case FONT_CHAR_SMALL_S_WITH_CARON: // š
|
||
case FONT_CHAR_S_CAPITAL_WITH_CEDILLA: // Ş
|
||
case FONT_CHAR_S_SMALL_WITH_CEDILLA: // ş
|
||
case 223: // ß
|
||
*p = 's';
|
||
break;
|
||
|
||
case FONT_CHAR_CAPITAL_U_WITH_RING_ABOVE: // Ů
|
||
case FONT_CHAR_SMALL_U_WITH_RING_ABOVE: // ů
|
||
case 217: // Ù
|
||
case 218: // Ú
|
||
case 219: // Û
|
||
case 220: // Ü
|
||
case 249: // ù
|
||
case 250: // ú
|
||
case 251: // û
|
||
case 252: // ü
|
||
*p = 'u';
|
||
break;
|
||
|
||
case FONT_CHAR_CAPITAL_Y_WITH_DIAERESIS: // Ÿ
|
||
case 221: // Ý
|
||
case 253: // ý
|
||
case 255: // ÿ
|
||
*p = 'y';
|
||
break;
|
||
|
||
case FONT_CHAR_SMALL_Z_WITH_CARON: // ž
|
||
case FONT_CHAR_CAPITAL_Z_WITH_CARON: // Ž
|
||
case FONT_CHAR_CAPITAL_Z_WITH_ACUTE: // Ź
|
||
case FONT_CHAR_SMALL_Z_WITH_ACUTE: // ź
|
||
case FONT_CHAR_CAPITAL_Z_WITH_DOT_ABOVE: // Ż
|
||
case FONT_CHAR_SMALL_Z_WITH_DOT_ABOVE: // ż
|
||
*p = 'z';
|
||
break;
|
||
}
|
||
p++;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
menuStatus_t menuLanguage(uiEvent_t *ev, bool isFirstRun)
|
||
{
|
||
if (isFirstRun)
|
||
{
|
||
menuDataGlobal.numItems = languagesGetCount();
|
||
|
||
voicePromptsInit();
|
||
voicePromptsAppendPrompt(PROMPT_SILENCE);
|
||
voicePromptsAppendLanguageString(currentLanguage->language);
|
||
voicePromptsAppendLanguageString(currentLanguage->menu);
|
||
voicePromptsAppendPrompt(PROMPT_SILENCE);
|
||
|
||
updateScreen(true);
|
||
return (MENU_STATUS_LIST_TYPE | MENU_STATUS_SUCCESS);
|
||
}
|
||
else
|
||
{
|
||
menuLanguageExitCode = MENU_STATUS_SUCCESS;
|
||
|
||
if (ev->hasEvent)
|
||
{
|
||
handleEvent(ev);
|
||
}
|
||
}
|
||
return menuLanguageExitCode;
|
||
}
|
||
|
||
static void updateScreen(bool isFirstRun)
|
||
{
|
||
int mNum = 0;
|
||
|
||
displayClearBuf();
|
||
menuDisplayTitle(currentLanguage->language);
|
||
|
||
for (int i = MENU_START_ITERATION_VALUE; i <= MENU_END_ITERATION_VALUE; i++)
|
||
{
|
||
mNum = menuGetMenuOffset(languagesGetCount(), i);
|
||
if (mNum == MENU_OFFSET_BEFORE_FIRST_ENTRY)
|
||
{
|
||
continue;
|
||
}
|
||
else if (mNum == MENU_OFFSET_AFTER_LAST_ENTRY)
|
||
{
|
||
break;
|
||
}
|
||
|
||
if (mNum < languagesGetCount())
|
||
{
|
||
menuDisplayEntry(i, mNum, (char *)languages[mNum].LANGUAGE_NAME, -1, THEME_ITEM_FG_MENU_ITEM, THEME_ITEM_FG_OPTIONS_VALUE, THEME_ITEM_BG);
|
||
|
||
if (i == 0)
|
||
{
|
||
if (nonVolatileSettings.audioPromptMode >= AUDIO_PROMPT_MODE_VOICE_THRESHOLD)
|
||
{
|
||
char buffer[SCREEN_LINE_BUFFER_SIZE];
|
||
|
||
snprintf(buffer, SCREEN_LINE_BUFFER_SIZE, "%s", (char *)languages[mNum].LANGUAGE_NAME);
|
||
|
||
clearNonLatinChar((uint8_t *)&buffer[0]);
|
||
|
||
if (isFirstRun == false)
|
||
{
|
||
voicePromptsInit();
|
||
}
|
||
|
||
voicePromptsAppendString(buffer);
|
||
promptsPlayNotAfterTx();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
displayRender();
|
||
}
|
||
|
||
static void handleEvent(uiEvent_t *ev)
|
||
{
|
||
if (ev->events & BUTTON_EVENT)
|
||
{
|
||
if (repeatVoicePromptOnSK1(ev))
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (ev->events & FUNCTION_EVENT)
|
||
{
|
||
if (ev->function == FUNC_REDRAW)
|
||
{
|
||
updateScreen(false);
|
||
}
|
||
else if ((QUICKKEY_TYPE(ev->function) == QUICKKEY_MENU) && (QUICKKEY_ENTRYID(ev->function) < languagesGetCount()))
|
||
{
|
||
menuDataGlobal.currentItemIndex = QUICKKEY_ENTRYID(ev->function);
|
||
settingsSetOptionBit(BIT_SECONDARY_LANGUAGE, ((menuDataGlobal.currentItemIndex > 0) ? true : false));
|
||
currentLanguage = &languages[(settingsIsOptionBitSet(BIT_SECONDARY_LANGUAGE) ? 1 : 0)];
|
||
settingsSaveIfNeeded(true);
|
||
menuSystemLanguageHasChanged();
|
||
menuSystemPopAllAndDisplayRootMenu();
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (KEYCHECK_PRESS(ev->keys, KEY_DOWN) && (menuDataGlobal.numItems != 0))
|
||
{
|
||
menuSystemMenuIncrement(&menuDataGlobal.currentItemIndex, languagesGetCount());
|
||
updateScreen(false);
|
||
menuLanguageExitCode |= MENU_STATUS_LIST_TYPE;
|
||
}
|
||
else if (KEYCHECK_PRESS(ev->keys, KEY_UP))
|
||
{
|
||
menuSystemMenuDecrement(&menuDataGlobal.currentItemIndex, languagesGetCount());
|
||
updateScreen(false);
|
||
menuLanguageExitCode |= MENU_STATUS_LIST_TYPE;
|
||
}
|
||
else if (KEYCHECK_SHORTUP(ev->keys, KEY_GREEN))
|
||
{
|
||
settingsSetOptionBit(BIT_SECONDARY_LANGUAGE, ((menuDataGlobal.currentItemIndex > 0) ? true : false));
|
||
currentLanguage = &languages[(settingsIsOptionBitSet(BIT_SECONDARY_LANGUAGE) ? 1 : 0)];
|
||
settingsSaveIfNeeded(true);
|
||
menuSystemLanguageHasChanged();
|
||
menuSystemPopAllAndDisplayRootMenu();
|
||
return;
|
||
}
|
||
else if (KEYCHECK_SHORTUP(ev->keys, KEY_RED))
|
||
{
|
||
menuSystemPopPreviousMenu();
|
||
return;
|
||
}
|
||
else if (KEYCHECK_SHORTUP_NUMBER(ev->keys) && BUTTONCHECK_DOWN(ev, BUTTON_SK2))
|
||
{
|
||
saveQuickkeyMenuIndex(ev->keys.key, menuSystemGetCurrentMenuNumber(), menuDataGlobal.currentItemIndex, 0);
|
||
return;
|
||
}
|
||
}
|