178 lines
7.9 KiB
C++
178 lines
7.9 KiB
C++
// Included inside ui.cpp's anonymous namespace, alongside the settings editors.
|
|
std::wstring trimApplicationText(const std::wstring &text)
|
|
{
|
|
auto first = text.find_first_not_of(L" \t\r\n");
|
|
return first == text.npos ? L"" : text.substr(first, text.find_last_not_of(L" \t\r\n") - first + 1);
|
|
}
|
|
struct AppLanguagesView
|
|
{
|
|
HWND parent{};
|
|
SettingsView *settings{};
|
|
std::map<std::wstring, std::vector<std::wstring>> profiles;
|
|
std::vector<std::wstring> applications;
|
|
int selection = -1;
|
|
bool dirty{};
|
|
};
|
|
void fillAppLanguages(HWND w, AppLanguagesView *v)
|
|
{
|
|
bool selected = v->selection >= 0 && v->selection < (int)v->applications.size();
|
|
EnableWindow(GetDlgItem(w, 722), selected);
|
|
SendDlgItemMessageW(w, 722, LB_SETSEL, FALSE, -1);
|
|
if (!selected)
|
|
{
|
|
EnableWindow(GetDlgItem(w, 725), FALSE);
|
|
value(w, 721, L"Select an application, or add its executable name below.");
|
|
return;
|
|
}
|
|
auto found = v->profiles.find(v->applications[v->selection]);
|
|
const auto &cycle = found == v->profiles.end() ? v->settings->draft.cycle : found->second;
|
|
for (size_t i = 0; i < v->settings->order.size(); ++i)
|
|
{
|
|
const auto &id = v->settings->app->layouts[v->settings->order[i]].id;
|
|
SendDlgItemMessageW(w, 722, LB_SETSEL,
|
|
std::find(cycle.begin(), cycle.end(), id) != cycle.end(), i);
|
|
}
|
|
value(w, 721, found == v->profiles.end() ? L"Using default languages. Click languages to customize this application." :
|
|
L"Custom languages for this application. Other applications are unchanged.");
|
|
EnableWindow(GetDlgItem(w, 725), found != v->profiles.end());
|
|
}
|
|
void collectAppLanguages(HWND w, AppLanguagesView *v)
|
|
{
|
|
if (v->selection < 0 || !v->dirty) return;
|
|
std::vector<std::wstring> cycle;
|
|
for (size_t i = 0; i < v->settings->order.size(); ++i)
|
|
if (SendDlgItemMessageW(w, 722, LB_GETSEL, i, 0) > 0)
|
|
cycle.push_back(v->settings->app->layouts[v->settings->order[i]].id);
|
|
if (cycle.empty()) throw std::runtime_error("Select at least one language.");
|
|
v->profiles[v->applications[v->selection]] = std::move(cycle);
|
|
v->dirty = false;
|
|
}
|
|
LRESULT CALLBACK appLanguagesProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
|
|
{
|
|
auto *v = (AppLanguagesView *)GetWindowLongPtrW(w, GWLP_USERDATA);
|
|
if (!v) return DefWindowProcW(w, msg, wp, lp);
|
|
try
|
|
{
|
|
if (msg == WM_COMMAND)
|
|
{
|
|
int id = LOWORD(wp);
|
|
if (id == 722 && HIWORD(wp) == LBN_SELCHANGE)
|
|
{
|
|
v->dirty = true;
|
|
EnableWindow(GetDlgItem(w, 725), TRUE);
|
|
value(w, 721, L"Custom languages for this application. Other applications are unchanged.");
|
|
return 0;
|
|
}
|
|
if (id == 720 && HIWORD(wp) == LBN_SELCHANGE)
|
|
{
|
|
int next = (int)SendDlgItemMessageW(w, 720, LB_GETCURSEL, 0, 0);
|
|
try { collectAppLanguages(w, v); }
|
|
catch (...) { SendDlgItemMessageW(w, 720, LB_SETCURSEL, v->selection, 0); throw; }
|
|
v->selection = next;
|
|
fillAppLanguages(w, v);
|
|
return 0;
|
|
}
|
|
if (id == 725 && v->selection >= 0)
|
|
{
|
|
v->profiles.erase(v->applications[v->selection]);
|
|
v->dirty = false;
|
|
fillAppLanguages(w, v);
|
|
return 0;
|
|
}
|
|
if (id == 727)
|
|
{
|
|
auto app = applicationName(trimApplicationText(value(w, 726)));
|
|
Settings check;
|
|
check.appLanguageSets[L"check"] = {{L"win:04090409"}, {app}};
|
|
validateSettings(check);
|
|
collectAppLanguages(w, v);
|
|
auto found = std::find(v->applications.begin(), v->applications.end(), app);
|
|
int index = (int)(found - v->applications.begin());
|
|
if (found == v->applications.end())
|
|
{
|
|
v->applications.push_back(app);
|
|
SendDlgItemMessageW(w, 720, LB_ADDSTRING, 0, (LPARAM)app.c_str());
|
|
}
|
|
v->selection = index;
|
|
SendDlgItemMessageW(w, 720, LB_SETCURSEL, index, 0);
|
|
fillAppLanguages(w, v);
|
|
value(w, 726, L"");
|
|
SetFocus(GetDlgItem(w, 722));
|
|
return 0;
|
|
}
|
|
if (id == IDOK)
|
|
{
|
|
collectAppLanguages(w, v);
|
|
auto next = v->settings->draft;
|
|
next.appLanguageSets.clear();
|
|
// Keep the existing on-disk format, but save independent application profiles.
|
|
for (const auto &[app, cycle] : v->profiles) next.appLanguageSets[app] = {cycle, {app}};
|
|
validateSettings(next);
|
|
v->settings->draft.appLanguageSets = std::move(next.appLanguageSets);
|
|
DestroyWindow(w);
|
|
return 0;
|
|
}
|
|
if (id == IDCANCEL) { DestroyWindow(w); return 0; }
|
|
}
|
|
if (msg == WM_CLOSE) { DestroyWindow(w); return 0; }
|
|
if (msg == WM_DPICHANGED) { resizeForDpi(w, wp, lp); return 0; }
|
|
if (msg == WM_NCDESTROY)
|
|
{
|
|
EnableWindow(v->parent, TRUE);
|
|
SetForegroundWindow(v->parent);
|
|
disposeFont(w);
|
|
delete v;
|
|
SetWindowLongPtrW(w, GWLP_USERDATA, 0);
|
|
}
|
|
}
|
|
catch (const std::exception &e) { error(w, e); }
|
|
return DefWindowProcW(w, msg, wp, lp);
|
|
}
|
|
void showAppLanguages(HWND parent, SettingsView *settings)
|
|
{
|
|
std::set<std::wstring> apps;
|
|
for (const auto &[name, set] : settings->draft.appLanguageSets)
|
|
for (const auto &app : set.applications) apps.insert(applicationName(app));
|
|
EnumWindows([](HWND candidate, LPARAM context) -> BOOL {
|
|
if (IsWindowVisible(candidate) && !chatTitle(candidate).empty())
|
|
{
|
|
auto name = applicationName(processName(candidate));
|
|
if (!name.empty()) ((std::set<std::wstring> *)context)->insert(name);
|
|
}
|
|
return TRUE;
|
|
}, (LPARAM)&apps);
|
|
HWND w = makeWindow(*settings->app, L"KSwitcher.AppLanguages", L"Languages for applications", appLanguagesProc, 720, 500);
|
|
auto *v = new AppLanguagesView{parent, settings};
|
|
for (const auto &[name, set] : settings->draft.appLanguageSets)
|
|
for (const auto &app : set.applications) v->profiles[applicationName(app)] = set.languages;
|
|
v->applications.assign(apps.begin(), apps.end());
|
|
SetWindowLongPtrW(w, GWLP_HWNDPARENT, (LONG_PTR)parent);
|
|
SetWindowLongPtrW(w, GWLP_USERDATA, (LONG_PTR)v);
|
|
label(w, L"Application", 18, 16, 278);
|
|
control(w, L"LISTBOX", L"", WS_TABSTOP | WS_VSCROLL | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | WS_BORDER,
|
|
720, 18, 44, 278, 280);
|
|
label(w, L"Allowed languages (click to include)", 316, 16, 386);
|
|
control(w, L"LISTBOX", L"", WS_TABSTOP | WS_VSCROLL | LBS_NOTIFY | LBS_MULTIPLESEL | LBS_NOINTEGRALHEIGHT | WS_BORDER,
|
|
722, 316, 44, 386, 280);
|
|
for (int i : settings->order)
|
|
SendDlgItemMessageW(w, 722, LB_ADDSTRING, 0, (LPARAM)settings->app->layouts[i].name.c_str());
|
|
for (const auto &app : v->applications) SendDlgItemMessageW(w, 720, LB_ADDSTRING, 0, (LPARAM)app.c_str());
|
|
control(w, L"STATIC", L"", 0, 721, 18, 338, 684, 22);
|
|
label(w, L"Add an executable name (e.g. Codex.exe)", 18, 372, 684);
|
|
edit(w, 726, 18, 400, 526);
|
|
button(w, L"&Add application", 727, 558, 398, 144);
|
|
label(w, L"Switch applications freely. Use languages, then Save settings. Chat profiles take priority.", 18, 436, 684);
|
|
button(w, L"Use &defaults", 725, 18, 466, 170);
|
|
button(w, L"&Use languages", IDOK, 414, 466, 144);
|
|
button(w, L"&Cancel", IDCANCEL, 572, 466, 130);
|
|
if (!v->applications.empty())
|
|
{
|
|
v->selection = 0;
|
|
SendDlgItemMessageW(w, 720, LB_SETCURSEL, 0, 0);
|
|
}
|
|
fillAppLanguages(w, v);
|
|
EnableWindow(parent, FALSE);
|
|
showRequestedWindow(w);
|
|
SetFocus(GetDlgItem(w, v->selection < 0 ? 726 : 720));
|
|
}
|