Compare commits

..

3 commits

Author SHA1 Message Date
Marcus Kida
d5ddc4748b Release 0.4.0
On-radio channel add/delete from the Channel quick menu (with translations and a
Managing-channels manual section), flash.sh always rebuilds language packs, and
releases before v1.0.0 are flagged as pre-releases.

Bump version to 0.4.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 12:49:45 +02:00
Marcus Kida
73d40374e7 README: document on-radio channel add/delete; fix stale dim/7-seg notes
Add a 'Managing channels on the radio' section and a feature bullet for channel
create/delete. Update the screen-dim levels (now includes 15%) and drop the
'faintly-lit inactive segments' note (inactive 7-seg segments are now off).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 12:48:55 +02:00
Marcus Kida
a8c2038552 Channels: translate the add/delete menu strings; flash.sh rebuilds packs
Add delete_channel and keep_one_channel to the language table (after mute) and
fill all 20 language files (13 translated, English fallback for the rest), with
correct single-byte ISO-8859 accents. Wire them into the Delete Channel label,
confirmation and last-channel guard. The new strings land on empty voice-table
slots, so they play as silence (no crash, no .vpr regeneration).

flash.sh now always rebuilds the .gla language packs so edits to the language
files are actually picked up (previously a stale pack was re-flashed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 12:47:27 +02:00
26 changed files with 84 additions and 13 deletions

View file

@ -7,6 +7,10 @@ single running list until the first versioned release.
## Unreleased ## Unreleased
_Nothing yet._
## 0.4.0
- **On-radio channel add/delete.** The Channel quick menu (top button) has two new - **On-radio channel add/delete.** The Channel quick menu (top button) has two new
entries: entries:
- **new channel** — creates a blank 145.500 MHz FM channel in the first free - **new channel** — creates a blank 145.500 MHz FM channel in the first free
@ -15,6 +19,11 @@ single running list until the first versioned release.
- **Delete Channel** — deletes the selected channel after a confirmation, and - **Delete Channel** — deletes the selected channel after a confirmation, and
scrubs it out of every zone that referenced it. Refuses to delete the last scrubs it out of every zone that referenced it. Refuses to delete the last
remaining channel, and respects the read-only/APRS channel lock. remaining channel, and respects the read-only/APRS channel lock.
- The new menu strings are **translated** (German, French, Italian, Spanish,
Portuguese, Dutch, Danish, Swedish, Catalan, Finnish, Czech; English fallback
for the rest).
- **`flash.sh`** now always rebuilds the language (`.gla`) packs, so edits to the
language files are picked up instead of a stale pack being re-flashed.
- **Release script** now flags every release before v1.0.0 as a **pre-release** - **Release script** now flags every release before v1.0.0 as a **pre-release**
(and corrects the flag on re-runs). (and corrects the flag on re-runs).

View file

@ -5,9 +5,9 @@
#define _FREETRX_VERSION_H_ #define _FREETRX_VERSION_H_
#define FREETRX_VERSION_MAJOR 0 #define FREETRX_VERSION_MAJOR 0
#define FREETRX_VERSION_MINOR 3 #define FREETRX_VERSION_MINOR 4
#define FREETRX_VERSION_PATCH 0 #define FREETRX_VERSION_PATCH 0
#define FREETRX_VERSION "0.3.0" #define FREETRX_VERSION "0.4.0"
#endif // _FREETRX_VERSION_H_ #endif // _FREETRX_VERSION_H_

View file

@ -327,6 +327,8 @@ const stringsTable_t catalanLanguage=
.text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Esborra canal", // MaxLen: 16
.keep_one_channel = "Deixa 1 canal", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t croatianLanguage =
.text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Delete Channel", // MaxLen: 16
.keep_one_channel = "Keep 1 channel", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t czechLanguage =
.text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Smazat kanál", // MaxLen: 16
.keep_one_channel = "Nech 1 kanál", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t danishLanguage =
.text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Slet kanal", // MaxLen: 16
.keep_one_channel = "Behold 1 kanal", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t dutchLanguage =
.text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Kanaal wissen", // MaxLen: 16
.keep_one_channel = "Hou 1 kanaal", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t englishLanguage =
.text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Delete Channel", // MaxLen: 16
.keep_one_channel = "Keep 1 channel", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t finnishLanguage =
.text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Poista kanava", // MaxLen: 16
.keep_one_channel = "Pidä 1 kanava", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t frenchLanguage =
.text_size = "Taille texte",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Taille texte",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Der. locut.", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Der. locut.", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Sourdine", // MaxLen: 16 (with ':' + .on or .off) .mute = "Sourdine", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Effacer canal", // MaxLen: 16
.keep_one_channel = "Garder 1 canal", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t germanLanguage =
.text_size = "Text Grösse",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text Grösse",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Kanal löschen", // MaxLen: 16
.keep_one_channel = "1 Kanal behalten", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t hungarianLanguage=
.text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Delete Channel", // MaxLen: 16
.keep_one_channel = "Keep 1 channel", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t italianLanguage =
.text_size = "Formato font",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Formato font",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Ultimo OM RX", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Ultimo OM RX", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Muto", // MaxLen: 16 (with ':' + .on or .off) .mute = "Muto", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Elimina canale", // MaxLen: 16
.keep_one_channel = "Tieni 1 canale", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t japaneseLanguage =
.text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Delete Channel", // MaxLen: 16
.keep_one_channel = "Keep 1 channel", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t polishLanguage =
.text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Delete Channel", // MaxLen: 16
.keep_one_channel = "Keep 1 channel", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t portuguesBrazilLanguage =
.text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Apagar canal", // MaxLen: 16
.keep_one_channel = "Manter 1 canal", // MaxLen: 16
}; };
/********************************************************************* /*********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t portuguesLanguage =
.text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Apagar canal", // MaxLen: 16
.keep_one_channel = "Manter 1 canal", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t romanianLanguage =
.text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Delete Channel", // MaxLen: 16
.keep_one_channel = "Keep 1 channel", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t slovenianLanguage =
.text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Delete Channel", // MaxLen: 16
.keep_one_channel = "Keep 1 channel", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t spanishLanguage =
.text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Borrar canal", // MaxLen: 16
.keep_one_channel = "Dejar 1 canal", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t swedishLanguage =
.text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Radera kanal", // MaxLen: 16
.keep_one_channel = "Behåll 1 kanal", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -327,6 +327,8 @@ const stringsTable_t turkishLanguage =
.text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit) .text_size = "Text size",//MaxLen: 16 (with ':' + 1 digit)
.last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's') .last_talker = "Last talker", // MaxLen: 16 (with ':' + .off or 2 digits value + 's')
.mute = "Mute", // MaxLen: 16 (with ':' + .on or .off) .mute = "Mute", // MaxLen: 16 (with ':' + .on or .off)
.delete_channel = "Delete Channel", // MaxLen: 16
.keep_one_channel = "Keep 1 channel", // MaxLen: 16
}; };
/******************************************************************** /********************************************************************
* *

View file

@ -335,6 +335,8 @@ typedef struct
const char text_size[LANGUAGE_TEXTS_LENGTH]; const char text_size[LANGUAGE_TEXTS_LENGTH];
const char last_talker[LANGUAGE_TEXTS_LENGTH]; const char last_talker[LANGUAGE_TEXTS_LENGTH];
const char mute[LANGUAGE_TEXTS_LENGTH]; const char mute[LANGUAGE_TEXTS_LENGTH];
const char delete_channel[LANGUAGE_TEXTS_LENGTH];
const char keep_one_channel[LANGUAGE_TEXTS_LENGTH];
} stringsTable_t; } stringsTable_t;
#endif // _OPENGD77_UILANGUAGE_H_ #endif // _OPENGD77_UILANGUAGE_H_

View file

@ -2416,7 +2416,7 @@ static void updateQuickMenuScreen(bool isFirstRun)
rightSideConst = currentLanguage->new_channel; rightSideConst = currentLanguage->new_channel;
break; break;
case CH_SCREEN_QUICK_MENU_DELETE_CHANNEL: case CH_SCREEN_QUICK_MENU_DELETE_CHANNEL:
rightSideConst = "Delete Channel"; rightSideConst = currentLanguage->delete_channel;
break; break;
default: default:
buf[0] = 0; buf[0] = 0;
@ -2683,7 +2683,7 @@ static void handleQuickMenuEvent(uiEvent_t *ev)
if (inUseCount < 2) if (inUseCount < 2)
{ {
snprintf(uiDataGlobal.MessageBox.message, MESSAGEBOX_MESSAGE_LEN_MAX, "Cannot delete\nlast channel"); snprintf(uiDataGlobal.MessageBox.message, MESSAGEBOX_MESSAGE_LEN_MAX, "%s", currentLanguage->keep_one_channel);
uiDataGlobal.MessageBox.buttons = uiDataGlobal.MessageBox.buttons =
#if defined(PLATFORM_MD9600) #if defined(PLATFORM_MD9600)
MESSAGEBOX_BUTTONS_ENT; MESSAGEBOX_BUTTONS_ENT;
@ -2695,12 +2695,13 @@ static void handleQuickMenuEvent(uiEvent_t *ev)
return; return;
} }
snprintf(uiDataGlobal.MessageBox.message, MESSAGEBOX_MESSAGE_LEN_MAX, "Delete channel?\n%s", currentLanguage->please_confirm); snprintf(uiDataGlobal.MessageBox.message, MESSAGEBOX_MESSAGE_LEN_MAX, "%s\n%s", currentLanguage->delete_channel, currentLanguage->please_confirm);
uiDataGlobal.MessageBox.buttons = MESSAGEBOX_BUTTONS_YESNO; uiDataGlobal.MessageBox.buttons = MESSAGEBOX_BUTTONS_YESNO;
uiDataGlobal.MessageBox.validatorCallback = validateDeleteChannel; uiDataGlobal.MessageBox.validatorCallback = validateDeleteChannel;
menuSystemPushNewMenu(UI_MESSAGE_BOX); menuSystemPushNewMenu(UI_MESSAGE_BOX);
voicePromptsInit(); voicePromptsInit();
voicePromptsAppendLanguageString(currentLanguage->delete_channel);
voicePromptsAppendLanguageString(currentLanguage->please_confirm); voicePromptsAppendLanguageString(currentLanguage->please_confirm);
voicePromptsPlay(); voicePromptsPlay();
return; return;

View file

@ -28,16 +28,35 @@ FreeTRX keeps OpenGD77's core (DMR/FM, hotspot, CPS compatibility) and adds:
TX talkgroup to the incoming one. TX talkgroup to the incoming one.
- **Segmented block S-meter** with one block per S-level (S1S9 green, S9+ red) - **Segmented block S-meter** with one block per S-level (S1S9 green, S9+ red)
and a scale label row (S1 S3 S5 S7 S9 / +20 +40 +60). and a scale label row (S1 S3 S5 S7 S9 / +20 +40 +60).
- **7-segment "LED" frequency font** with faintly-lit inactive segments, on the - **7-segment "LED" frequency font** on the VFO and the SK1-held frequency view.
VFO and the SK1-held frequency view. - **Screen dimming** — optionally dim the backlight to 100 / 50 / 30 / 15 % after
- **Screen dimming** — optionally dim the backlight to 100 / 50 / 30 % after
inactivity (Auto backlight mode); new setting in Display Options. inactivity (Auto backlight mode); new setting in Display Options.
- **On-radio channel management** — create and delete channels directly from the
Channel quick menu, no PC/CPS required (see [Managing channels](#managing-channels-on-the-radio)).
- **Main-menu icons** next to each top-level entry. - **Main-menu icons** next to each top-level entry.
- **DMR reliability fix** — resolves a hard-fault (freeze needing a battery pull) - **DMR reliability fix** — resolves a hard-fault (freeze needing a battery pull)
on the DMR path that affected recent self-built OpenGD77 revisions. on the DMR path that affected recent self-built OpenGD77 revisions.
See [`CHANGELOG.md`](CHANGELOG.md) for the complete list. See [`CHANGELOG.md`](CHANGELOG.md) for the complete list.
## Managing channels on the radio
FreeTRX can create and delete channels without a PC. Both actions live in the
**Channel quick menu** — from the Channel screen, press the **top (orange)
button** to open it, then scroll to the bottom:
- **New channel** — creates a blank **145.500 MHz FM** channel in the first free
slot, adds it to the current zone, and opens **Channel Details** so you can set
the name, frequency, tones, and so on. Press the **green** key to save. The new
channel is immediately selectable in the zone (no need to re-enter it).
- **Delete Channel** — deletes the currently-selected channel after a
confirmation (**green** confirms, **red** cancels). It is also scrubbed from
every zone that referenced it. The last remaining channel cannot be deleted,
and channels locked read-only (e.g. APRS) are protected.
> **Back up your codeplug** (via the CPS) before making bulk edits — these changes
> are written straight to the radio and there is no on-device undo.
## Supported radios ## Supported radios
This first iteration is developed for and **tested only on the TYT MD-UV380 and This first iteration is developed for and **tested only on the TYT MD-UV380 and

View file

@ -58,11 +58,11 @@ done
# ---- resolve / build the language file ------------------------------------ # ---- resolve / build the language file ------------------------------------
LANG_ARGS=() LANG_ARGS=()
if [[ -n "$LANGUAGE" && "$(lc "$LANGUAGE")" != "none" ]]; then if [[ -n "$LANGUAGE" && "$(lc "$LANGUAGE")" != "none" ]]; then
# Build the .gla files once if they're missing. # Always (re)build the .gla language packs so edits to the *.h language files
if ! ls "$LANG_SRC"/*.gla >/dev/null 2>&1; then # are picked up. (They're built from the .h sources by languages_builder; a
# stale pack would otherwise show blank/old strings on the radio.)
echo "==> Building language (.gla) files..." echo "==> Building language (.gla) files..."
( cd "$LANG_SRC" && gcc -w -I../ -o languages_builder languages_builder.c && ./languages_builder >/dev/null ) ( cd "$LANG_SRC" && gcc -w -I../ -o languages_builder languages_builder.c && ./languages_builder >/dev/null )
fi
# Case-insensitive match of the requested name to an actual <Name>.gla file. # Case-insensitive match of the requested name to an actual <Name>.gla file.
GLA="" GLA=""
for f in "$LANG_SRC"/*.gla; do for f in "$LANG_SRC"/*.gla; do