/* * Copyright (C)2019 Roger Clark. VK3KYY / G4KYF * * 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "functions/codeplug.h" #include "main.h" #include "functions/settings.h" #include "user_interface/menuSystem.h" #include "user_interface/uiUtilities.h" #include "user_interface/uiLocalisation.h" typedef enum { MENU_CONTACT_LIST_DISPLAY = 0, MENU_CONTACT_LIST_CONFIRM, MENU_CONTACT_LIST_DELETED, MENU_CONTACT_LIST_TG_IN_RXGROUP } contactListState_t; typedef enum { MENU_CONTACT_LIST_CONTACT_DIGITAL = 0, MENU_CONTACT_LIST_CONTACT_DTMF } contactListContactType_t; static void updateScreen(bool isFirstRun); static void handleEvent(uiEvent_t *ev); static struct_codeplugContact_t contact; static struct_codeplugDTMFContact_t dtmfContact; static int contactListType; static int contactCallType; static contactListState_t contactListDisplayState; static contactListState_t contactListOverrideState = MENU_CONTACT_LIST_DISPLAY; static int contactListTimeout; static menuStatus_t menuContactListExitCode = MENU_STATUS_SUCCESS; static menuStatus_t menuContactListSubMenuExitCode = MENU_STATUS_SUCCESS; // Apply contact + its TS on selection for TX (contact list of quick list). static void overrideWithSelectedContact(void) { menuPrivateCallClear(); setOverrideTGorPC(contactListContactData.tgNumber, (contactListContactData.callType == CONTACT_CALLTYPE_PC)); // Contact has a TS override set if ((contactListContactData.reserve1 & 0x01) == 0x00) { int ts = ((contactListContactData.reserve1 & 0x02) >> 1); trxSetDMRTimeSlot(ts); tsSetManualOverride(((menuSystemGetRootMenuNumber() == UI_CHANNEL_MODE) ? CHANNEL_CHANNEL : (CHANNEL_VFO_A + nonVolatileSettings.currentVFONumber)), (ts + 1)); } } static void reloadContactList(contactListContactType_t type) { menuDataGlobal.endIndex = (type == MENU_CONTACT_LIST_CONTACT_DIGITAL) ? codeplugContactsGetCount(contactCallType) : codeplugDTMFContactsGetCount(); if (menuDataGlobal.endIndex > 0) { if (menuDataGlobal.currentItemIndex >= menuDataGlobal.endIndex) { menuDataGlobal.currentItemIndex = 0; } uiDataGlobal.currentSelectedContactIndex = (type == MENU_CONTACT_LIST_CONTACT_DIGITAL) ? codeplugContactGetDataForNumberInType(menuDataGlobal.currentItemIndex + 1, contactCallType, &contactListContactData) : codeplugDTMFContactGetDataForNumber(menuDataGlobal.currentItemIndex + 1, &contactListDTMFContactData); } else { uiDataGlobal.currentSelectedContactIndex = 0; } } menuStatus_t menuContactList(uiEvent_t *ev, bool isFirstRun) { if (isFirstRun) { if (contactListOverrideState == MENU_CONTACT_LIST_DISPLAY) { if (uiDataGlobal.currentSelectedContactIndex == 0) { int currentMenu = menuSystemGetCurrentMenuNumber(); // Shows digital contact list if called from "contact list" menu entry, or from +# in digital. // Otherwise displays DTMF contact list contactListType = ((currentMenu == MENU_CONTACT_LIST) || ((currentMenu == MENU_CONTACT_QUICKLIST) && (trxGetMode() != RADIO_MODE_ANALOG))) ? MENU_CONTACT_LIST_CONTACT_DIGITAL : MENU_CONTACT_LIST_CONTACT_DTMF; contactCallType = CONTACT_CALLTYPE_TG; dtmfSequenceReset(); } else { if (contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) { codeplugContactGetDataForIndex(uiDataGlobal.currentSelectedContactIndex, &contactListContactData); contactCallType = contactListContactData.callType; } else { codeplugDTMFContactGetDataForIndex(uiDataGlobal.currentSelectedContactIndex, &contactListDTMFContactData); } } reloadContactList(contactListType); contactListDisplayState = MENU_CONTACT_LIST_DISPLAY; voicePromptsInit(); voicePromptsAppendPrompt(PROMPT_SILENCE); voicePromptsAppendPrompt(PROMPT_SILENCE); if (contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) { voicePromptsAppendLanguageString(¤tLanguage->contacts); voicePromptsAppendLanguageString(¤tLanguage->menu); voicePromptsAppendPrompt(PROMPT_SILENCE); voicePromptsAppendPrompt(PROMPT_SILENCE); } else { if (menuSystemGetCurrentMenuNumber() == MENU_CONTACT_QUICKLIST) { voicePromptsAppendLanguageString(¤tLanguage->dtmf_contact_list); voicePromptsAppendPrompt(PROMPT_SILENCE); voicePromptsAppendPrompt(PROMPT_SILENCE); } } } else { if (contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) { codeplugContactGetDataForIndex(uiDataGlobal.currentSelectedContactIndex, &contactListContactData); } else { codeplugDTMFContactGetDataForIndex(uiDataGlobal.currentSelectedContactIndex, &contactListDTMFContactData); } contactListDisplayState = contactListOverrideState; contactListOverrideState = MENU_CONTACT_LIST_DISPLAY; } updateScreen(true); menuContactListExitCode = (MENU_STATUS_LIST_TYPE | MENU_STATUS_SUCCESS); } else { menuContactListExitCode = MENU_STATUS_SUCCESS; if (ev->hasEvent || (contactListTimeout > 0)) { handleEvent(ev); } } // Play the DTMF contact sequence from Quicklist dtmfSequenceTick(false); return menuContactListExitCode; } static void updateScreen(bool isFirstRun) { char nameBuf[33]; int mNum; int idx; const char *calltypeName[] = { currentLanguage->group_call, currentLanguage->private_call, currentLanguage->all_call, "DTMF" }; ucClearBuf(); switch (contactListDisplayState) { case MENU_CONTACT_LIST_DISPLAY: menuDisplayTitle((char *) calltypeName[((contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) ? contactCallType : 3)]); if (!isFirstRun) { voicePromptsInit(); } if (menuDataGlobal.endIndex == 0) { ucPrintCentered((DISPLAY_SIZE_Y / 2), currentLanguage->empty_list, FONT_SIZE_3); voicePromptsAppendLanguageString(¤tLanguage->empty_list); promptsPlayNotAfterTx(); } else { for (int i = -1; i <= 1; i++) { mNum = menuGetMenuOffset(menuDataGlobal.endIndex, i); idx = (contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) ? codeplugContactGetDataForNumberInType(mNum + 1, contactCallType, &contact) : codeplugDTMFContactGetDataForNumber(mNum + 1, &dtmfContact); if (idx > 0) { codeplugUtilConvertBufToString(((contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) ? contact.name : dtmfContact.name), nameBuf, 16); // need to convert to zero terminated string menuDisplayEntry(i, mNum, (char*) nameBuf); } if (i == 0) { voicePromptsAppendString(nameBuf); promptsPlayNotAfterTx(); } } } break; case MENU_CONTACT_LIST_CONFIRM: codeplugUtilConvertBufToString(contactListContactData.name, nameBuf, 16); menuDisplayTitle(nameBuf); ucPrintCentered(16, currentLanguage->delete_contact_qm, FONT_SIZE_3); ucDrawChoice(CHOICE_YESNO, false); break; case MENU_CONTACT_LIST_DELETED: codeplugUtilConvertBufToString(contactListContactData.name, nameBuf, 16); ucPrintCentered(16, currentLanguage->contact_deleted, FONT_SIZE_3); ucDrawChoice(CHOICE_DISMISS, false); break; case MENU_CONTACT_LIST_TG_IN_RXGROUP: codeplugUtilConvertBufToString(contactListContactData.name, nameBuf, 16); menuDisplayTitle(nameBuf); ucPrintCentered(16, currentLanguage->contact_used, FONT_SIZE_3); ucPrintCentered((DISPLAY_SIZE_Y/2), currentLanguage->in_rx_group, FONT_SIZE_3); ucDrawChoice(CHOICE_DISMISS, false); break; } ucRender(); } static void handleEvent(uiEvent_t *ev) { if (ev->events & BUTTON_EVENT) { if (repeatVoicePromptOnSK1(ev)) { return; } } // DTMF sequence is playing, stop it. if (uiDataGlobal.DTMFContactList.isKeying && ((ev->keys.key != 0) || BUTTONCHECK_DOWN(ev, BUTTON_PTT) #if ! defined(PLATFORM_RD5R) || BUTTONCHECK_DOWN(ev, BUTTON_ORANGE) #endif )) { uiDataGlobal.DTMFContactList.poLen = 0U; return; } switch (contactListDisplayState) { case MENU_CONTACT_LIST_DISPLAY: if (KEYCHECK_PRESS(ev->keys, KEY_DOWN)) { menuSystemMenuIncrement(&menuDataGlobal.currentItemIndex, menuDataGlobal.endIndex); uiDataGlobal.currentSelectedContactIndex = (contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) ? codeplugContactGetDataForNumberInType(menuDataGlobal.currentItemIndex + 1, contactCallType, &contactListContactData) : codeplugDTMFContactGetDataForNumber(menuDataGlobal.currentItemIndex + 1, &contactListDTMFContactData); updateScreen(false); menuContactListExitCode |= MENU_STATUS_LIST_TYPE; } else if (KEYCHECK_PRESS(ev->keys, KEY_UP)) { menuSystemMenuDecrement(&menuDataGlobal.currentItemIndex, menuDataGlobal.endIndex); uiDataGlobal.currentSelectedContactIndex = (contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) ? codeplugContactGetDataForNumberInType(menuDataGlobal.currentItemIndex + 1, contactCallType, &contactListContactData) : codeplugDTMFContactGetDataForNumber(menuDataGlobal.currentItemIndex + 1, &contactListDTMFContactData); updateScreen(false); menuContactListExitCode |= MENU_STATUS_LIST_TYPE; } else if (KEYCHECK_SHORTUP(ev->keys, KEY_HASH)) { if (contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) { contactCallType = (contactCallType + 1) % 3; reloadContactList(contactListType); updateScreen(false); } } else if (KEYCHECK_SHORTUP(ev->keys, KEY_GREEN)) { int currentMenu = menuSystemGetCurrentMenuNumber(); int currentMode = trxGetMode(); if (currentMenu == MENU_CONTACT_QUICKLIST) { if (contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) { if (menuDataGlobal.endIndex > 0) { if (currentMode == RADIO_MODE_DIGITAL) { overrideWithSelectedContact(); uiDataGlobal.currentSelectedContactIndex = 0; announceItem(PROMPT_SEQUENCE_CONTACT_TG_OR_PC, PROMPT_THRESHOLD_3); menuSystemPopAllAndDisplayRootMenu(); } else { menuContactListExitCode |= MENU_STATUS_ERROR; } } return; } else // MENU_CONTACT_LIST_CONTACT_DTMF { if (currentMode == RADIO_MODE_ANALOG) { dtmfSequencePrepare(contactListDTMFContactData.code, true); } else { menuContactListExitCode |= MENU_STATUS_ERROR; } return; } } // Display submenu for DTMF contact list if (menuDataGlobal.endIndex > 0) // display action list only if contact list is non empty { if ((currentMenu == MENU_CONTACT_LIST) || ((currentMenu == MENU_DTMF_CONTACT_LIST) && (currentMode == RADIO_MODE_ANALOG))) { menuSystemPushNewMenu(MENU_CONTACT_LIST_SUBMENU); } else { menuContactListExitCode |= MENU_STATUS_ERROR; } } return; } else if (KEYCHECK_SHORTUP(ev->keys, KEY_RED)) { uiDataGlobal.currentSelectedContactIndex = 0; menuSystemPopPreviousMenu(); return; } if ((contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) && (KEYCHECK_SHORTUP_NUMBER(ev->keys) && (BUTTONCHECK_DOWN(ev, BUTTON_SK2)))) { saveQuickkeyContactIndex(ev->keys.key, (uint16_t)contactListContactData.NOT_IN_CODEPLUGDATA_indexNumber); return; } break; case MENU_CONTACT_LIST_CONFIRM: if (KEYCHECK_SHORTUP(ev->keys, KEY_GREEN)) { memset(contact.name, 0xff, 16); contact.tgNumber = 0; contact.callType = 0xFF; codeplugContactSaveDataForIndex(uiDataGlobal.currentSelectedContactIndex, &contact); uiDataGlobal.currentSelectedContactIndex = 0; contactListTimeout = 2000; contactListDisplayState = MENU_CONTACT_LIST_DELETED; reloadContactList(contactListType); updateScreen(false); voicePromptsAppendLanguageString(¤tLanguage->contact_deleted); voicePromptsPlay(); } else if (KEYCHECK_SHORTUP(ev->keys, KEY_RED)) { contactListDisplayState = MENU_CONTACT_LIST_DISPLAY; reloadContactList(contactListType); updateScreen(false); } break; case MENU_CONTACT_LIST_DELETED: case MENU_CONTACT_LIST_TG_IN_RXGROUP: contactListTimeout--; if ((contactListTimeout == 0) || KEYCHECK_SHORTUP(ev->keys, KEY_GREEN) || KEYCHECK_SHORTUP(ev->keys, KEY_RED)) { contactListDisplayState = MENU_CONTACT_LIST_DISPLAY; reloadContactList(contactListType); } updateScreen(false); break; } } enum CONTACT_LIST_QUICK_MENU_ITEMS { CONTACT_LIST_QUICK_MENU_SELECT = 0, CONTACT_LIST_QUICK_MENU_EDIT, CONTACT_LIST_QUICK_MENU_DELETE, NUM_CONTACT_LIST_QUICK_MENU_ITEMS // The last item in the list is used so that we automatically get a total number of items in the list }; static void updateSubMenuScreen(void) { int mNum = 0; static const int bufferLen = 17; char buf[bufferLen]; char * const *langTextConst = NULL;// initialise to please the compiler voicePromptsInit(); ucClearBuf(); codeplugUtilConvertBufToString((contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) ? contactListContactData.name : contactListDTMFContactData.name, buf, 16); menuDisplayTitle(buf); for(int i = -1; i <= 1; i++) { mNum = menuGetMenuOffset(((contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) ? NUM_CONTACT_LIST_QUICK_MENU_ITEMS : 1), i); buf[0] = 0; switch(mNum) { case CONTACT_LIST_QUICK_MENU_SELECT: langTextConst = (char * const *)¤tLanguage->select_tx; break; case CONTACT_LIST_QUICK_MENU_EDIT: langTextConst = (contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) ? (char * const *)¤tLanguage->edit_contact : NULL; break; case CONTACT_LIST_QUICK_MENU_DELETE: langTextConst = (contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) ? (char * const *)¤tLanguage->delete_contact : NULL; break; } if (langTextConst != NULL) { strncpy(buf, *langTextConst, 17); } else { strncpy(buf, " ", 17); } if ((i == 0) && (langTextConst != NULL)) { voicePromptsAppendLanguageString((const char * const *)langTextConst); promptsPlayNotAfterTx(); } menuDisplayEntry(i, mNum, buf); } ucRender(); } static void handleSubMenuEvent(uiEvent_t *ev) { if (ev->events & BUTTON_EVENT) { if (repeatVoicePromptOnSK1(ev)) { return; } } // DTMF sequence is playing, stop it. if (uiDataGlobal.DTMFContactList.isKeying && ((ev->keys.key != 0) || BUTTONCHECK_DOWN(ev, BUTTON_PTT) #if ! defined(PLATFORM_RD5R) || BUTTONCHECK_DOWN(ev, BUTTON_ORANGE) #endif )) { uiDataGlobal.DTMFContactList.poLen = 0U; return; } if (KEYCHECK_SHORTUP(ev->keys, KEY_RED)) { menuSystemPopPreviousMenu(); } else if (KEYCHECK_SHORTUP(ev->keys, KEY_GREEN)) { contactListOverrideState = MENU_CONTACT_LIST_DISPLAY; switch (menuDataGlobal.currentItemIndex) { case CONTACT_LIST_QUICK_MENU_SELECT: if (contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) { if (trxGetMode() == RADIO_MODE_DIGITAL) { overrideWithSelectedContact(); uiDataGlobal.currentSelectedContactIndex = 0; announceItem(PROMPT_SEQUENCE_CONTACT_TG_OR_PC, PROMPT_THRESHOLD_3); uiDataGlobal.VoicePrompts.inhibitInitial = true; menuSystemPopAllAndDisplayRootMenu(); } else { menuContactListSubMenuExitCode |= MENU_STATUS_ERROR; } return; } else { dtmfSequencePrepare(contactListDTMFContactData.code, true); } break; case CONTACT_LIST_QUICK_MENU_EDIT: if (contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) { menuSystemSetCurrentMenu(MENU_CONTACT_DETAILS); } break; case CONTACT_LIST_QUICK_MENU_DELETE: if (uiDataGlobal.currentSelectedContactIndex > 0) { if (contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) { if ((contactListContactData.callType == CONTACT_CALLTYPE_TG) && codeplugContactGetRXGroup(contactListContactData.NOT_IN_CODEPLUGDATA_indexNumber)) { contactListTimeout = 2000; contactListOverrideState = MENU_CONTACT_LIST_TG_IN_RXGROUP; voicePromptsAppendLanguageString(¤tLanguage->contact_used); voicePromptsAppendLanguageString(¤tLanguage->in_rx_group); voicePromptsPlay(); } else { contactListOverrideState = MENU_CONTACT_LIST_CONFIRM; voicePromptsAppendLanguageString(¤tLanguage->delete_contact_qm); voicePromptsPlay(); } } menuSystemPopPreviousMenu(); } break; } } else if (KEYCHECK_PRESS(ev->keys, KEY_DOWN)) { menuSystemMenuIncrement(&menuDataGlobal.currentItemIndex, ((contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) ? NUM_CONTACT_LIST_QUICK_MENU_ITEMS : 1)); updateSubMenuScreen(); } else if (KEYCHECK_PRESS(ev->keys, KEY_UP)) { menuSystemMenuDecrement(&menuDataGlobal.currentItemIndex, ((contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) ? NUM_CONTACT_LIST_QUICK_MENU_ITEMS : 1)); updateSubMenuScreen(); } if ((menuDataGlobal.currentItemIndex == CONTACT_LIST_QUICK_MENU_SELECT) && (contactListType == MENU_CONTACT_LIST_CONTACT_DIGITAL) && (KEYCHECK_SHORTUP_NUMBER(ev->keys) && (BUTTONCHECK_DOWN(ev, BUTTON_SK2)))) { saveQuickkeyContactIndex(ev->keys.key, (uint16_t)contactListContactData.NOT_IN_CODEPLUGDATA_indexNumber); return; } } menuStatus_t menuContactListSubMenu(uiEvent_t *ev, bool isFirstRun) { if (isFirstRun) { updateSubMenuScreen(); keyboardInit(); menuContactListSubMenuExitCode = (MENU_STATUS_LIST_TYPE | MENU_STATUS_SUCCESS); } else { menuContactListSubMenuExitCode = MENU_STATUS_SUCCESS; if (ev->hasEvent) { handleSubMenuEvent(ev); } } dtmfSequenceTick(true); return menuContactListSubMenuExitCode; }