summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.c28
-rw-r--r--src/lang.c2
-rw-r--r--src/prefs.c2
-rw-r--r--src/prefs.h1
-rw-r--r--src/ui/certimportwidget.c36
-rw-r--r--src/ui/command.c2
-rw-r--r--src/ui/inputwidget.c14
-rw-r--r--src/ui/labelwidget.c9
-rw-r--r--src/ui/labelwidget.h1
-rw-r--r--src/ui/util.c61
-rw-r--r--src/ui/window.c4
11 files changed, 124 insertions, 36 deletions
diff --git a/src/app.c b/src/app.c
index 95345bda..d477abb5 100644
--- a/src/app.c
+++ b/src/app.c
@@ -208,6 +208,7 @@ static iString *serializePrefs_App_(const iApp *d) {
208 } 208 }
209 appendFormat_String(str, "sidebar2.mode arg:%d\n", mode_SidebarWidget(sidebar2)); 209 appendFormat_String(str, "sidebar2.mode arg:%d\n", mode_SidebarWidget(sidebar2));
210 } 210 }
211 appendFormat_String(str, "uilang id:%s\n", cstr_String(&d->prefs.uiLanguage));
211 appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(d->window)); 212 appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(d->window));
212 appendFormat_String(str, "prefs.dialogtab arg:%d\n", d->prefs.dialogTab); 213 appendFormat_String(str, "prefs.dialogtab arg:%d\n", d->prefs.dialogTab);
213 appendFormat_String(str, "font.set arg:%d\n", d->prefs.font); 214 appendFormat_String(str, "font.set arg:%d\n", d->prefs.font);
@@ -308,6 +309,11 @@ static void loadPrefs_App_(iApp *d) {
308 if (equal_Command(cmd, "uiscale")) { 309 if (equal_Command(cmd, "uiscale")) {
309 setUiScale_Window(get_Window(), argf_Command(cmd)); 310 setUiScale_Window(get_Window(), argf_Command(cmd));
310 } 311 }
312 else if (equal_Command(cmd, "uilang")) {
313 const char *id = cstr_Rangecc(range_Command(cmd, "id"));
314 setCStr_String(&d->prefs.uiLanguage, id);
315 setCurrent_Lang(id);
316 }
311 else if (equal_Command(cmd, "ca.file") || equal_Command(cmd, "ca.path")) { 317 else if (equal_Command(cmd, "ca.file") || equal_Command(cmd, "ca.path")) {
312 /* Background requests may be started before these commands would get 318 /* Background requests may be started before these commands would get
313 handled via the event loop. */ 319 handled via the event loop. */
@@ -1286,13 +1292,14 @@ static void updatePrefsThemeButtons_(iWidget *d) {
1286} 1292}
1287 1293
1288static void updateDropdownSelection_(iLabelWidget *dropButton, const char *selectedCommand) { 1294static void updateDropdownSelection_(iLabelWidget *dropButton, const char *selectedCommand) {
1289 iForEach(ObjectList, i, children_Widget(findChild_Widget(as_Widget(dropButton), "menu"))) { 1295 iWidget *menu = findChild_Widget(as_Widget(dropButton), "menu");
1296 iForEach(ObjectList, i, children_Widget(menu)) {
1290 if (isInstance_Object(i.object, &Class_LabelWidget)) { 1297 if (isInstance_Object(i.object, &Class_LabelWidget)) {
1291 iLabelWidget *item = i.object; 1298 iLabelWidget *item = i.object;
1292 const iBool isSelected = endsWith_String(command_LabelWidget(item), selectedCommand); 1299 const iBool isSelected = endsWith_String(command_LabelWidget(item), selectedCommand);
1293 setFlags_Widget(as_Widget(item), selected_WidgetFlag, isSelected); 1300 setFlags_Widget(as_Widget(item), selected_WidgetFlag, isSelected);
1294 if (isSelected) { 1301 if (isSelected) {
1295 updateText_LabelWidget(dropButton, text_LabelWidget(item)); 1302 updateText_LabelWidget(dropButton, sourceText_LabelWidget(item));
1296 } 1303 }
1297 } 1304 }
1298 } 1305 }
@@ -1300,8 +1307,6 @@ static void updateDropdownSelection_(iLabelWidget *dropButton, const char *selec
1300 1307
1301static void updateColorThemeButton_(iLabelWidget *button, int theme) { 1308static void updateColorThemeButton_(iLabelWidget *button, int theme) {
1302 if (!button) return; 1309 if (!button) return;
1303// const char *mode = strstr(cstr_String(id_Widget(as_Widget(button))), ".dark")
1304// ? "dark" : "light";
1305 updateDropdownSelection_(button, format_CStr(".set arg:%d", theme)); 1310 updateDropdownSelection_(button, format_CStr(".set arg:%d", theme));
1306} 1311}
1307 1312
@@ -1355,6 +1360,11 @@ static iBool handlePrefsCommands_(iWidget *d, const char *cmd) {
1355 postCommand_App("prefs.changed"); 1360 postCommand_App("prefs.changed");
1356 return iTrue; 1361 return iTrue;
1357 } 1362 }
1363 else if (equal_Command(cmd, "uilang")) {
1364 updateDropdownSelection_(findChild_Widget(d, "prefs.uilang"),
1365 cstr_String(string_Command(cmd, "id")));
1366 return iFalse;
1367 }
1358 else if (equal_Command(cmd, "quoteicon.set")) { 1368 else if (equal_Command(cmd, "quoteicon.set")) {
1359 const int arg = arg_Command(cmd); 1369 const int arg = arg_Command(cmd);
1360 setFlags_Widget(findChild_Widget(d, "prefs.quoteicon.0"), selected_WidgetFlag, arg == 0); 1370 setFlags_Widget(findChild_Widget(d, "prefs.quoteicon.0"), selected_WidgetFlag, arg == 0);
@@ -1532,6 +1542,15 @@ iBool handleCommand_App(const char *cmd) {
1532 d->prefs.dialogTab = arg_Command(cmd); 1542 d->prefs.dialogTab = arg_Command(cmd);
1533 return iTrue; 1543 return iTrue;
1534 } 1544 }
1545 else if (equal_Command(cmd, "uilang")) {
1546 const iString *lang = string_Command(cmd, "id");
1547 if (!equal_String(lang, &d->prefs.uiLanguage)) {
1548 set_String(&d->prefs.uiLanguage, lang);
1549 setCurrent_Lang(cstr_String(&d->prefs.uiLanguage));
1550 postCommand_App("lang.changed");
1551 }
1552 return iTrue;
1553 }
1535 else if (equal_Command(cmd, "translation.languages")) { 1554 else if (equal_Command(cmd, "translation.languages")) {
1536 d->prefs.langFrom = argLabel_Command(cmd, "from"); 1555 d->prefs.langFrom = argLabel_Command(cmd, "from");
1537 d->prefs.langTo = argLabel_Command(cmd, "to"); 1556 d->prefs.langTo = argLabel_Command(cmd, "to");
@@ -1891,6 +1910,7 @@ iBool handleCommand_App(const char *cmd) {
1891 setToggle_Widget(findChild_Widget(dlg, "prefs.hidetoolbarscroll"), d->prefs.hideToolbarOnScroll); 1910 setToggle_Widget(findChild_Widget(dlg, "prefs.hidetoolbarscroll"), d->prefs.hideToolbarOnScroll);
1892 setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme); 1911 setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme);
1893 setToggle_Widget(findChild_Widget(dlg, "prefs.customframe"), d->prefs.customFrame); 1912 setToggle_Widget(findChild_Widget(dlg, "prefs.customframe"), d->prefs.customFrame);
1913 updateDropdownSelection_(findChild_Widget(dlg, "prefs.uilang"), cstr_String(&d->prefs.uiLanguage));
1894 setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize); 1914 setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize);
1895 setText_InputWidget(findChild_Widget(dlg, "prefs.uiscale"), 1915 setText_InputWidget(findChild_Widget(dlg, "prefs.uiscale"),
1896 collectNewFormat_String("%g", uiScale_Window(d->window))); 1916 collectNewFormat_String("%g", uiScale_Window(d->window)));
diff --git a/src/lang.c b/src/lang.c
index 57efcaa2..db28e318 100644
--- a/src/lang.c
+++ b/src/lang.c
@@ -72,7 +72,7 @@ iRangecc range_Lang(iRangecc msgId) {
72 return ((const iMsgStr *) at_SortedArray(d->messages, pos))->str; 72 return ((const iMsgStr *) at_SortedArray(d->messages, pos))->str;
73 } 73 }
74 fprintf(stderr, "[Lang] missing: %s\n", cstr_Rangecc(msgId)); fflush(stderr); 74 fprintf(stderr, "[Lang] missing: %s\n", cstr_Rangecc(msgId)); fflush(stderr);
75 iAssert(iFalse); 75// iAssert(iFalse);
76 return msgId; 76 return msgId;
77} 77}
78 78
diff --git a/src/prefs.c b/src/prefs.c
index a4f12e20..119dfd56 100644
--- a/src/prefs.c
+++ b/src/prefs.c
@@ -54,6 +54,7 @@ void init_Prefs(iPrefs *d) {
54 d->docThemeDark = colorfulDark_GmDocumentTheme; 54 d->docThemeDark = colorfulDark_GmDocumentTheme;
55 d->docThemeLight = white_GmDocumentTheme; 55 d->docThemeLight = white_GmDocumentTheme;
56 d->saturation = 1.0f; 56 d->saturation = 1.0f;
57 initCStr_String(&d->uiLanguage, "en");
57 init_String(&d->caFile); 58 init_String(&d->caFile);
58 init_String(&d->caPath); 59 init_String(&d->caPath);
59 init_String(&d->geminiProxy); 60 init_String(&d->geminiProxy);
@@ -81,4 +82,5 @@ void deinit_Prefs(iPrefs *d) {
81 deinit_String(&d->downloadDir); 82 deinit_String(&d->downloadDir);
82 deinit_String(&d->caPath); 83 deinit_String(&d->caPath);
83 deinit_String(&d->caFile); 84 deinit_String(&d->caFile);
85 deinit_String(&d->uiLanguage);
84} 86}
diff --git a/src/prefs.h b/src/prefs.h
index 130f11e2..1bd434d0 100644
--- a/src/prefs.h
+++ b/src/prefs.h
@@ -38,6 +38,7 @@ struct Impl_Prefs {
38 int langFrom; 38 int langFrom;
39 int langTo; 39 int langTo;
40 /* Window */ 40 /* Window */
41 iString uiLanguage;
41 iBool useSystemTheme; 42 iBool useSystemTheme;
42 enum iColorTheme theme; 43 enum iColorTheme theme;
43 enum iColorAccent accent; 44 enum iColorAccent accent;
diff --git a/src/ui/certimportwidget.c b/src/ui/certimportwidget.c
index d47851f5..43e8ff97 100644
--- a/src/ui/certimportwidget.c
+++ b/src/ui/certimportwidget.c
@@ -47,8 +47,7 @@ struct Impl_CertImportWidget {
47 iTlsCertificate *cert; 47 iTlsCertificate *cert;
48}; 48};
49 49
50static const char *infoText_ = "Paste a PEM-encoded certificate and/or private key,\n" 50static const char *infoText_ = "${dlg.certimport.help}";
51 "or drop a .crt/.key file on the window.";
52 51
53static iBool tryImport_CertImportWidget_(iCertImportWidget *d, const iBlock *data) { 52static iBool tryImport_CertImportWidget_(iCertImportWidget *d, const iBlock *data) {
54 iBool ok = iFalse; 53 iBool ok = iFalse;
@@ -84,7 +83,7 @@ static iBool tryImport_CertImportWidget_(iCertImportWidget *d, const iBlock *dat
84 setFrameColor_Widget(as_Widget(d->crtLabel), uiTextAction_ColorId); 83 setFrameColor_Widget(as_Widget(d->crtLabel), uiTextAction_ColorId);
85 } 84 }
86 else { 85 else {
87 setTextCStr_LabelWidget(d->crtLabel, uiTextCaution_ColorEscape "No Certificate"); 86 setTextCStr_LabelWidget(d->crtLabel, uiTextCaution_ColorEscape "${dlg.certimport.nocert}");
88 setFrameColor_Widget(as_Widget(d->crtLabel), uiTextCaution_ColorId); 87 setFrameColor_Widget(as_Widget(d->crtLabel), uiTextCaution_ColorId);
89 } 88 }
90 if (d->cert && hasPrivateKey_TlsCertificate(d->cert)) { 89 if (d->cert && hasPrivateKey_TlsCertificate(d->cert)) {
@@ -96,7 +95,7 @@ static iBool tryImport_CertImportWidget_(iCertImportWidget *d, const iBlock *dat
96 setFrameColor_Widget(as_Widget(d->keyLabel), uiTextAction_ColorId); 95 setFrameColor_Widget(as_Widget(d->keyLabel), uiTextAction_ColorId);
97 } 96 }
98 else { 97 else {
99 setTextCStr_LabelWidget(d->keyLabel, uiTextCaution_ColorEscape "No Private Key"); 98 setTextCStr_LabelWidget(d->keyLabel, uiTextCaution_ColorEscape "${dlg.certimport.nokey}");
100 setFrameColor_Widget(as_Widget(d->keyLabel), uiTextCaution_ColorId); 99 setFrameColor_Widget(as_Widget(d->keyLabel), uiTextCaution_ColorId);
101 } 100 }
102 } 101 }
@@ -118,9 +117,10 @@ void init_CertImportWidget(iCertImportWidget *d) {
118 overflowScrollable_WidgetFlag, 117 overflowScrollable_WidgetFlag,
119 iTrue); 118 iTrue);
120 } 119 }
121 addChildFlags_Widget(w, 120 addChildFlags_Widget(
122 iClob(new_LabelWidget(uiHeading_ColorEscape "IMPORT IDENTITY", NULL)), 121 w,
123 frameless_WidgetFlag); 122 iClob(new_LabelWidget(uiHeading_ColorEscape "${heading.certimport}", NULL)),
123 frameless_WidgetFlag);
124 d->info = addChildFlags_Widget(w, iClob(new_LabelWidget(infoText_, NULL)), frameless_WidgetFlag); 124 d->info = addChildFlags_Widget(w, iClob(new_LabelWidget(infoText_, NULL)), frameless_WidgetFlag);
125 addChild_Widget(w, iClob(makePadding_Widget(gap_UI))); 125 addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
126 d->crtLabel = new_LabelWidget("", NULL); { 126 d->crtLabel = new_LabelWidget("", NULL); {
@@ -141,9 +141,9 @@ void init_CertImportWidget(iCertImportWidget *d) {
141 page, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag); 141 page, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag);
142 iWidget *values = addChildFlags_Widget( 142 iWidget *values = addChildFlags_Widget(
143 page, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag); 143 page, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag);
144 addChild_Widget(headings, iClob(makeHeading_Widget("Notes:"))); 144 addChild_Widget(headings, iClob(makeHeading_Widget("${dlg.certimport.notes}")));
145 addChild_Widget(values, iClob(d->notes = new_InputWidget(0))); 145 addChild_Widget(values, iClob(d->notes = new_InputWidget(0)));
146 setHint_InputWidget(d->notes, "description"); 146 setHint_InputWidget(d->notes, "${hint.certimport.description}");
147 as_Widget(d->notes)->rect.size.x = gap_UI * 70; 147 as_Widget(d->notes)->rect.size.x = gap_UI * 70;
148 } 148 }
149 addChild_Widget(w, iClob(page)); 149 addChild_Widget(w, iClob(page));
@@ -153,9 +153,11 @@ void init_CertImportWidget(iCertImportWidget *d) {
153 /* Buttons. */ 153 /* Buttons. */
154 addChild_Widget(w, iClob(makePadding_Widget(gap_UI))); 154 addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
155 iWidget *buttons = makeDialogButtons_Widget( 155 iWidget *buttons = makeDialogButtons_Widget(
156 (iMenuItem[]){ 156 (iMenuItem[]){ { "${cancel}", 0, 0, NULL },
157 { "${cancel}", 0, 0, NULL }, 157 { uiTextAction_ColorEscape "${dlg.certimport.import}",
158 { uiTextAction_ColorEscape "Import", SDLK_RETURN, KMOD_PRIMARY, "certimport.accept" } }, 158 SDLK_RETURN,
159 KMOD_PRIMARY,
160 "certimport.accept" } },
159 2); 161 2);
160 addChild_Widget(w, iClob(buttons)); 162 addChild_Widget(w, iClob(buttons));
161 arrange_Widget(w); 163 arrange_Widget(w);
@@ -182,7 +184,7 @@ void setPageContent_CertImportWidget(iCertImportWidget *d, const iBlock *content
182 } 184 }
183 else { 185 else {
184 setTextCStr_LabelWidget( 186 setTextCStr_LabelWidget(
185 d->info, format_CStr("No certificate/key found on the current page.\n%s", infoText_)); 187 d->info, format_CStr("${dlg.certimport.notfound.page}\n%s", infoText_));
186 } 188 }
187 arrange_Widget(as_Widget(d)); 189 arrange_Widget(as_Widget(d));
188} 190}
@@ -198,8 +200,8 @@ static iBool processEvent_CertImportWidget_(iCertImportWidget *d, const SDL_Even
198 const int mods = keyMods_Sym(ev->key.keysym.mod); 200 const int mods = keyMods_Sym(ev->key.keysym.mod);
199 if (key == SDLK_v && mods == KMOD_PRIMARY) { 201 if (key == SDLK_v && mods == KMOD_PRIMARY) {
200 if (!tryImportFromClipboard_CertImportWidget_(d)) { 202 if (!tryImportFromClipboard_CertImportWidget_(d)) {
201 makeMessage_Widget(uiTextCaution_ColorEscape "PASTED FROM CLIPBOARD", 203 makeMessage_Widget(uiTextCaution_ColorEscape "${heading.certimport.pasted}",
202 "No certificate or private key was found."); 204 "${dlg.certimport.notfound}");
203 } 205 }
204 postRefresh_App(); 206 postRefresh_App();
205 return iTrue; 207 return iTrue;
@@ -232,8 +234,8 @@ static iBool processEvent_CertImportWidget_(iCertImportWidget *d, const SDL_Even
232 } 234 }
233 } 235 }
234 else { 236 else {
235 makeMessage_Widget(uiTextCaution_ColorEscape "DROPPED FILE", 237 makeMessage_Widget(uiTextCaution_ColorEscape "${heading.certimport.dropped}",
236 "No certificate or private key was found."); 238 "${dlg.certimport.notfound}");
237 } 239 }
238 } 240 }
239 iRelease(f); 241 iRelease(f);
diff --git a/src/ui/command.c b/src/ui/command.c
index 44e66121..c5ca164e 100644
--- a/src/ui/command.c
+++ b/src/ui/command.c
@@ -96,7 +96,7 @@ iString *suffix_Command(const char *cmd, const char *label) {
96} 96}
97 97
98const iString *string_Command(const char *cmd, const char *label) { 98const iString *string_Command(const char *cmd, const char *label) {
99 return collect_String(newRange_String(range_Command(cmd, label))); 99 return collectNewRange_String(range_Command(cmd, label));
100} 100}
101 101
102iRangecc range_Command(const char *cmd, const char *label) { 102iRangecc range_Command(const char *cmd, const char *label) {
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index 05b83b3d..52359732 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -83,6 +83,7 @@ struct Impl_InputWidget {
83 iArray text; /* iChar[] */ 83 iArray text; /* iChar[] */
84 iArray oldText; /* iChar[] */ 84 iArray oldText; /* iChar[] */
85 iString hint; 85 iString hint;
86 iString srcHint;
86 int leftPadding; 87 int leftPadding;
87 int rightPadding; 88 int rightPadding;
88 size_t cursor; 89 size_t cursor;
@@ -136,6 +137,7 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) {
136 init_Array(&d->text, sizeof(iChar)); 137 init_Array(&d->text, sizeof(iChar));
137 init_Array(&d->oldText, sizeof(iChar)); 138 init_Array(&d->oldText, sizeof(iChar));
138 init_String(&d->hint); 139 init_String(&d->hint);
140 init_String(&d->srcHint);
139 init_Array(&d->undoStack, sizeof(iInputUndo)); 141 init_Array(&d->undoStack, sizeof(iInputUndo));
140 d->font = uiInput_FontId | alwaysVariableFlag_FontId; 142 d->font = uiInput_FontId | alwaysVariableFlag_FontId;
141 d->leftPadding = 0; 143 d->leftPadding = 0;
@@ -164,6 +166,7 @@ void deinit_InputWidget(iInputWidget *d) {
164 if (d->timer) { 166 if (d->timer) {
165 SDL_RemoveTimer(d->timer); 167 SDL_RemoveTimer(d->timer);
166 } 168 }
169 deinit_String(&d->srcHint);
167 deinit_String(&d->hint); 170 deinit_String(&d->hint);
168 deinit_Array(&d->oldText); 171 deinit_Array(&d->oldText);
169 deinit_Array(&d->text); 172 deinit_Array(&d->text);
@@ -251,8 +254,10 @@ void setMaxLen_InputWidget(iInputWidget *d, size_t maxLen) {
251} 254}
252 255
253void setHint_InputWidget(iInputWidget *d, const char *hintText) { 256void setHint_InputWidget(iInputWidget *d, const char *hintText) {
254 setCStr_String(&d->hint, hintText); 257 /* Keep original for retranslations. */
255 translate_Lang(&d->hint); /* TODO: Keep original for retranslations. */ 258 setCStr_String(&d->srcHint, hintText);
259 set_String(&d->hint, &d->srcHint);
260 translate_Lang(&d->hint);
256} 261}
257 262
258void setContentPadding_InputWidget(iInputWidget *d, int left, int right) { 263void setContentPadding_InputWidget(iInputWidget *d, int left, int right) {
@@ -665,6 +670,11 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
665 begin_InputWidget(d); 670 begin_InputWidget(d);
666 return iFalse; 671 return iFalse;
667 } 672 }
673 else if (isCommand_UserEvent(ev, "lang.changed")) {
674 set_String(&d->hint, &d->srcHint);
675 translate_Lang(&d->hint);
676 return iFalse;
677 }
668 else if (isCommand_Widget(w, ev, "focus.lost")) { 678 else if (isCommand_Widget(w, ev, "focus.lost")) {
669 end_InputWidget(d, iTrue); 679 end_InputWidget(d, iTrue);
670 return iFalse; 680 return iFalse;
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c
index 2a1eb06a..8089445b 100644
--- a/src/ui/labelwidget.c
+++ b/src/ui/labelwidget.c
@@ -85,6 +85,10 @@ static iBool processEvent_LabelWidget_(iLabelWidget *d, const SDL_Event *ev) {
85 if (isMetricsChange_UserEvent(ev)) { 85 if (isMetricsChange_UserEvent(ev)) {
86 updateSize_LabelWidget(d); 86 updateSize_LabelWidget(d);
87 } 87 }
88 else if (isCommand_UserEvent(ev, "lang.changed")) {
89 setText_LabelWidget(d, &d->srcLabel);
90 return iFalse;
91 }
88 else if (isCommand_UserEvent(ev, "bindings.changed")) { 92 else if (isCommand_UserEvent(ev, "bindings.changed")) {
89 /* Update the key used to trigger this label. */ 93 /* Update the key used to trigger this label. */
90 updateKey_LabelWidget_(d); 94 updateKey_LabelWidget_(d);
@@ -474,6 +478,11 @@ const iString *text_LabelWidget(const iLabelWidget *d) {
474 return &d->label; 478 return &d->label;
475} 479}
476 480
481const iString *sourceText_LabelWidget(const iLabelWidget *d) {
482 if (!d) return collectNew_String();
483 return &d->srcLabel;
484}
485
477const iString *command_LabelWidget(const iLabelWidget *d) { 486const iString *command_LabelWidget(const iLabelWidget *d) {
478 return &d->command; 487 return &d->command;
479} 488}
diff --git a/src/ui/labelwidget.h b/src/ui/labelwidget.h
index 3e3c76fb..f4c4658c 100644
--- a/src/ui/labelwidget.h
+++ b/src/ui/labelwidget.h
@@ -45,6 +45,7 @@ void updateTextCStr_LabelWidget (iLabelWidget *, const char *text); /* not r
45iInt2 defaultSize_LabelWidget (const iLabelWidget *); 45iInt2 defaultSize_LabelWidget (const iLabelWidget *);
46int font_LabelWidget (const iLabelWidget *); 46int font_LabelWidget (const iLabelWidget *);
47const iString * text_LabelWidget (const iLabelWidget *); 47const iString * text_LabelWidget (const iLabelWidget *);
48const iString * sourceText_LabelWidget (const iLabelWidget *); /* untranslated */
48const iString * command_LabelWidget (const iLabelWidget *); 49const iString * command_LabelWidget (const iLabelWidget *);
49iChar icon_LabelWidget (const iLabelWidget *); 50iChar icon_LabelWidget (const iLabelWidget *);
50 51
diff --git a/src/ui/util.c b/src/ui/util.c
index 94690bc6..8c0b0138 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -1679,14 +1679,13 @@ iWidget *makeQuestion_Widget(const char *title, const char *msg,
1679 1679
1680void setToggle_Widget(iWidget *d, iBool active) { 1680void setToggle_Widget(iWidget *d, iBool active) {
1681 if (d) { 1681 if (d) {
1682 const char *YES = cstr_Lang("toggle.yes");
1683 const char *NO = cstr_Lang("toggle.no");
1684 setFlags_Widget(d, selected_WidgetFlag, active); 1682 setFlags_Widget(d, selected_WidgetFlag, active);
1685 iLabelWidget *label = (iLabelWidget *) d; 1683 iLabelWidget *label = (iLabelWidget *) d;
1686 if (!cmp_String(text_LabelWidget(label), YES) || 1684 if (!cmp_String(text_LabelWidget(label), cstr_Lang("toggle.yes")) ||
1687 !cmp_String(text_LabelWidget(label), NO)) { 1685 !cmp_String(text_LabelWidget(label), cstr_Lang("toggle.no"))) {
1688 updateText_LabelWidget((iLabelWidget *) d, 1686 updateText_LabelWidget(
1689 collectNewCStr_String(isSelected_Widget(d) ? YES : NO)); 1687 (iLabelWidget *) d,
1688 collectNewCStr_String(isSelected_Widget(d) ? "${toggle.yes}" : "${toggle.no}"));
1690 } 1689 }
1691 else { 1690 else {
1692 refresh_Widget(d); 1691 refresh_Widget(d);
@@ -1707,9 +1706,10 @@ static iBool toggleHandler_(iWidget *d, const char *cmd) {
1707} 1706}
1708 1707
1709iWidget *makeToggle_Widget(const char *id) { 1708iWidget *makeToggle_Widget(const char *id) {
1710 iWidget *toggle = as_Widget(new_LabelWidget("YES", "toggle")); /* "YES" for sizing */ 1709 iWidget *toggle = as_Widget(new_LabelWidget("${toggle.yes}", "toggle")); /* "YES" for sizing */
1711 setId_Widget(toggle, id); 1710 setId_Widget(toggle, id);
1712 updateTextCStr_LabelWidget((iLabelWidget *) toggle, "NO"); /* actual initial value */ 1711 updateTextCStr_LabelWidget((iLabelWidget *) toggle, "${toggle.no}"); /* actual initial value */
1712 setFlags_Widget(toggle, fixedWidth_WidgetFlag, iTrue);
1713 setCommandHandler_Widget(toggle, toggleHandler_); 1713 setCommandHandler_Widget(toggle, toggleHandler_);
1714 return toggle; 1714 return toggle;
1715} 1715}
@@ -1787,6 +1787,11 @@ static void addFontButtons_(iWidget *parent, const char *id) {
1787 delete_Array(items); 1787 delete_Array(items);
1788} 1788}
1789 1789
1790static int cmp_MenuItem_(const void *e1, const void *e2) {
1791 const iMenuItem *a = e1, *b = e2;
1792 return iCmpStr(a->label, b->label);
1793}
1794
1790iWidget *makePreferences_Widget(void) { 1795iWidget *makePreferences_Widget(void) {
1791 iWidget *dlg = makeSheet_Widget("prefs"); 1796 iWidget *dlg = makeSheet_Widget("prefs");
1792 addChildFlags_Widget(dlg, 1797 addChildFlags_Widget(dlg,
@@ -1820,6 +1825,36 @@ iWidget *makePreferences_Widget(void) {
1820 } 1825 }
1821 /* Window. */ { 1826 /* Window. */ {
1822 appendTwoColumnPage_(tabs, "${heading.prefs.interface}", '2', &headings, &values); 1827 appendTwoColumnPage_(tabs, "${heading.prefs.interface}", '2', &headings, &values);
1828 /* UI languages. */ {
1829 iArray *uiLangs = collectNew_Array(sizeof(iMenuItem));
1830 const iMenuItem langItems[] = {
1831 { "${lang.en}", 0, 0, "uilang id:en" },
1832 { "${lang.fi}", 0, 0, "uilang id:fi" },
1833 };
1834 pushBackN_Array(uiLangs, langItems, iElemCount(langItems));
1835 sort_Array(uiLangs, cmp_MenuItem_);
1836 /* TODO: Add an arrange flag for resizing parent to widest child. */
1837 int widest = 0;
1838 size_t widestPos = iInvalidPos;
1839 iConstForEach(Array, i, uiLangs) {
1840 const int width =
1841 advance_Text(uiLabel_FontId,
1842 translateCStr_Lang(((const iMenuItem *) i.value)->label))
1843 .x;
1844 if (widestPos == iInvalidPos || width > widest) {
1845 widest = width;
1846 widestPos = index_ArrayConstIterator(&i);
1847 }
1848 }
1849 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.uilang}")));
1850 setId_Widget(addChildFlags_Widget(values,
1851 iClob(makeMenuButton_LabelWidget(
1852 value_Array(uiLangs, widestPos, iMenuItem).label,
1853 data_Array(uiLangs),
1854 size_Array(uiLangs))),
1855 0),
1856 "prefs.uilang");
1857 }
1823#if defined (iPlatformApple) || defined (iPlatformMSys) 1858#if defined (iPlatformApple) || defined (iPlatformMSys)
1824 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.ostheme}"))); 1859 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.ostheme}")));
1825 addChild_Widget(values, iClob(makeToggle_Widget("prefs.ostheme"))); 1860 addChild_Widget(values, iClob(makeToggle_Widget("prefs.ostheme")));
@@ -1898,11 +1933,15 @@ iWidget *makePreferences_Widget(void) {
1898 addFontButtons_(values, "font"); 1933 addFontButtons_(values, "font");
1899 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.mono}"))); 1934 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.mono}")));
1900 iWidget *mono = new_Widget(); 1935 iWidget *mono = new_Widget();
1901 /* TODO: Needs labels! */ 1936 iWidget *tog;
1902 setTextCStr_LabelWidget( 1937 setTextCStr_LabelWidget(
1903 addChild_Widget(mono, iClob(makeToggle_Widget("prefs.mono.gemini"))), "${prefs.mono.gemini}"); 1938 addChild_Widget(mono, tog = iClob(makeToggle_Widget("prefs.mono.gemini"))), "${prefs.mono.gemini}");
1939 setFlags_Widget(tog, fixedWidth_WidgetFlag, iFalse);
1940 updateSize_LabelWidget((iLabelWidget *) tog);
1904 setTextCStr_LabelWidget( 1941 setTextCStr_LabelWidget(
1905 addChild_Widget(mono, iClob(makeToggle_Widget("prefs.mono.gopher"))), "${prefs.mono.gopher}"); 1942 addChild_Widget(mono, tog = iClob(makeToggle_Widget("prefs.mono.gopher"))), "${prefs.mono.gopher}");
1943 setFlags_Widget(tog, fixedWidth_WidgetFlag, iFalse);
1944 updateSize_LabelWidget((iLabelWidget *) tog);
1906 addChildFlags_Widget(values, iClob(mono), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); 1945 addChildFlags_Widget(values, iClob(mono), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag);
1907 } 1946 }
1908 makeTwoColumnHeading_("${heading.prefs.paragraph}", headings, values); 1947 makeTwoColumnHeading_("${heading.prefs.paragraph}", headings, values);
diff --git a/src/ui/window.c b/src/ui/window.c
index 6949e245..97500b22 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -1820,6 +1820,10 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {
1820 if (isMetricsChange_UserEvent(&event)) { 1820 if (isMetricsChange_UserEvent(&event)) {
1821 updateMetrics_Window_(d); 1821 updateMetrics_Window_(d);
1822 } 1822 }
1823 if (isCommand_UserEvent(&event, "lang.changed")) {
1824 invalidate_Window_(d);
1825 arrange_Widget(d->root);
1826 }
1823 if (oldHover != hover_Widget()) { 1827 if (oldHover != hover_Widget()) {
1824 postRefresh_App(); 1828 postRefresh_App();
1825 } 1829 }