Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 738c81dce2 |
+1
-1
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
project(KSwitcher VERSION 0.6.26 LANGUAGES CXX RC)
|
||||
project(KSwitcher VERSION 0.6.27 LANGUAGES CXX RC)
|
||||
if(NOT WIN32)
|
||||
message(FATAL_ERROR "KSwitcher uses native Windows APIs")
|
||||
endif()
|
||||
|
||||
@@ -16,6 +16,11 @@ Run `build/KSwitcher.exe`, or extract `dist/KSwitcher-win-x64.zip` and run the e
|
||||
The build also refreshes the release copy in `build/release/`. Exit KSwitcher before rebuilding.
|
||||
The application lives in the notification area. Right-click its icon for Settings, Snippets and history.
|
||||
Double-click the tray icon to open Settings, the bold default menu item.
|
||||
Menus support access keys: press the underlined letter while the menu is open (for example,
|
||||
S for Settings or X for Exit in the tray menu). In forms, press Alt+the underlined letter
|
||||
to activate a button, toggle a checkbox or focus a labeled field. Press Alt to reveal
|
||||
the underlines when Windows hides them. Shortcut-capture fields still record Alt combinations;
|
||||
press Tab to leave a shortcut field before using form access keys.
|
||||
Settings, the snippet editor and log viewer run on demand in **KSwitcher.Config.exe**, beside the main executable.
|
||||
Repeated launches activate the existing configurator; closing its last window exits that process.
|
||||
Saving applies changes immediately through the resident KSwitcher process. The configurator does not
|
||||
|
||||
Binary file not shown.
+6
-8
@@ -1,17 +1,15 @@
|
||||
# Latest Windows release
|
||||
|
||||
## v0.6.26 — 2026-11-02
|
||||
## v0.6.27 — 2026-11-02
|
||||
|
||||
## Changes
|
||||
- Settings → Sync… is available to everyone. Connect your own OpenBao KV v2 with a username and password; synchronization is disabled by default and no server is built in.
|
||||
- Synchronize at startup, approximately two seconds after saving settings, and every 10 minutes in the background. Local operation continues offline.
|
||||
- Merge independent settings changes and resolve conflicts individually, with local/server values shown side by side. Unresolved values stay pending while independent changes synchronize. Every server write uses CAS to protect concurrent changes.
|
||||
- Snippets have stable IDs and retained deletion records. Independent snippets, renames and text edits can merge; deletion versus editing and duplicate shortcuts/triggers receive explicit conflict handling.
|
||||
- Personal dictionaries, exclusions, shortcuts and layout preferences synchronize. Startup, history, local paths, connection details and credentials stay on the PC. Passwords use Windows Credential Manager; cached sync state uses DPAPI.
|
||||
- Existing profiles migrate to sync envelope version 2. Update every participating PC before using the new synchronization behavior.
|
||||
- Tray and clipboard context menus support underlined access keys. Press the letter while a menu is open (S for Settings, X for Exit).
|
||||
- Alt+letter activates buttons and checkboxes in Settings, Snippets, Logs, synchronization/conflict, OCR and dictionary dialogs. Selected field labels also move focus to their input.
|
||||
- Clipboard history supports Alt+letter for its actions and search while retaining Enter, Shift+Enter and Ctrl+digit shortcuts.
|
||||
- Press Alt to reveal Windows' hidden underlines. Shortcut capture still records Alt combinations; press Tab to leave the capture field before using form access keys. Captions and access keys currently use English letters.
|
||||
|
||||
## Validation
|
||||
91 sync/protocol/conflict checks, 423 native regressions, 510 input/UI checks, screenshot editor and PNG save regressions, shared wheel handling, notification registration, 48 updater checks and updater progress checks. Initial upload and personal-dictionary synchronization were confirmed against the user's OpenBao. Two-physical-PC production testing is still pending.
|
||||
91 sync/protocol/conflict checks, 423 native regressions, 513 input/UI checks (including Windows Alt+K dispatch and checkbox toggling), shared wheel handling, screenshot editor and PNG save regressions, notification registration, 48 updater checks and updater progress checks.
|
||||
|
||||
## Limitations
|
||||
OpenBao synchronization is experimental and requires a reachable HTTPS server with a trusted certificate, KV v2 and a provisioned userpass account/policy. It does not provide account registration, MFA login, non-root namespaces or reverse-proxy URL prefixes. Ordered layout/language lists are resolved as whole settings. Deletion records are retained without expiry. Missing keyboard layouts block applying a profile. Download/Upload replace a whole profile and are intended for initial setup or deliberate recovery; use Resolve individual conflicts for normal conflicts. Existing browser-selection and clipboard/accessibility limitations remain unchanged.
|
||||
|
||||
@@ -1 +1 @@
|
||||
99a863ddb82d3ec868d874ba5986d6552ab8e5b2fd61d0e227577b35f2657383 KSwitcher-win-x64.zip
|
||||
e6597d11fc388979e972c8572e2471a00dec6c3782414a22d396072508fe7329 KSwitcher-win-x64.zip
|
||||
|
||||
+3
-3
@@ -9,8 +9,8 @@ IDC_SCREENSHOT_PENCIL CURSOR "icons/lucide/pencil.cur"
|
||||
1 RT_MANIFEST "app.manifest"
|
||||
201 RCDATA "update.ps1"
|
||||
1 VERSIONINFO
|
||||
FILEVERSION 0,6,26,0
|
||||
PRODUCTVERSION 0,6,26,0
|
||||
FILEVERSION 0,6,27,0
|
||||
PRODUCTVERSION 0,6,27,0
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
BEGIN
|
||||
@@ -20,7 +20,7 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "FileDescription", "KSwitcher — native keyboard and clipboard utility"
|
||||
VALUE "ProductName", "KSwitcher"
|
||||
VALUE "FileVersion", "0.6.26"
|
||||
VALUE "FileVersion", "0.6.27"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
+20
-17
@@ -1453,28 +1453,28 @@ void App::recognizeEntry(std::wstring id)
|
||||
void App::trayMenu()
|
||||
{
|
||||
HMENU menu = CreatePopupMenu();
|
||||
AppendMenuW(menu, MF_STRING, 10, L"Clipboard history");
|
||||
AppendMenuW(menu, MF_STRING, 24, L"Capture screen region…");
|
||||
AppendMenuW(menu, MF_STRING, 11, L"Settings…");
|
||||
AppendMenuW(menu, MF_STRING, 10, L"Clipboard &history");
|
||||
AppendMenuW(menu, MF_STRING, 24, L"&Capture screen region…");
|
||||
AppendMenuW(menu, MF_STRING, 11, L"&Settings…");
|
||||
SetMenuDefaultItem(menu, 11, FALSE);
|
||||
AppendMenuW(menu, MF_STRING, 12, L"Snippets…");
|
||||
AppendMenuW(menu, MF_STRING, 13, L"Keyboard shortcuts…");
|
||||
AppendMenuW(menu, MF_STRING, 12, L"S&nippets…");
|
||||
AppendMenuW(menu, MF_STRING, 13, L"&Keyboard shortcuts…");
|
||||
AppendMenuW(menu, MF_SEPARATOR, 0, nullptr);
|
||||
AppendMenuW(menu, MF_STRING | (settings.awake ? MF_CHECKED : 0), 14, L"Keep screen awake");
|
||||
AppendMenuW(menu, MF_STRING | (settings.awake ? MF_CHECKED : 0), 14, L"Keep screen a&wake");
|
||||
HMENU pause = CreatePopupMenu();
|
||||
AppendMenuW(pause, MF_STRING, 20, L"Resume");
|
||||
AppendMenuW(pause, MF_STRING, 21, L"10 minutes");
|
||||
AppendMenuW(pause, MF_STRING, 22, L"1 hour");
|
||||
AppendMenuW(pause, MF_STRING, 23, L"Until restart");
|
||||
AppendMenuW(menu, MF_POPUP, (UINT_PTR)pause, L"Pause clipboard history");
|
||||
AppendMenuW(menu, MF_STRING, 15, L"Open data folder");
|
||||
AppendMenuW(menu, MF_STRING, 18, L"View log…");
|
||||
AppendMenuW(pause, MF_STRING, 20, L"&Resume");
|
||||
AppendMenuW(pause, MF_STRING, 21, L"&10 minutes");
|
||||
AppendMenuW(pause, MF_STRING, 22, L"1 &hour");
|
||||
AppendMenuW(pause, MF_STRING, 23, L"&Until restart");
|
||||
AppendMenuW(menu, MF_POPUP, (UINT_PTR)pause, L"&Pause clipboard history");
|
||||
AppendMenuW(menu, MF_STRING, 15, L"&Open data folder");
|
||||
AppendMenuW(menu, MF_STRING, 18, L"View &log…");
|
||||
AppendMenuW(menu, MF_STRING | (updateRunning ? MF_GRAYED : 0), 19,
|
||||
updateRunning ? L"Update (checking / preparing…)" :
|
||||
availableUpdate.empty() ? L"Update" : L"Update (new version available)");
|
||||
updateRunning ? L"&Update (checking / preparing…)" :
|
||||
availableUpdate.empty() ? L"&Update" : L"&Update (new version available)");
|
||||
AppendMenuW(menu, MF_SEPARATOR, 0, nullptr);
|
||||
AppendMenuW(menu, MF_STRING, 16, L"About KSwitcher");
|
||||
AppendMenuW(menu, MF_STRING, 17, L"Exit");
|
||||
AppendMenuW(menu, MF_STRING, 16, L"&About KSwitcher");
|
||||
AppendMenuW(menu, MF_STRING, 17, L"E&xit");
|
||||
POINT pt;
|
||||
GetCursorPos(&pt);
|
||||
pasteTarget = GetForegroundWindow();
|
||||
@@ -1837,6 +1837,9 @@ int App::execute()
|
||||
if (popup != settingsWindow && IsDialogMessageW(popup, &msg))
|
||||
continue;
|
||||
}
|
||||
// Preserve history-specific Enter/Shift+Enter and Ctrl+digit handling.
|
||||
if (historyWindow && msg.message == WM_SYSCHAR && IsDialogMessageW(historyWindow, &msg))
|
||||
continue;
|
||||
if (settingsWindow && IsDialogMessageW(settingsWindow, &msg))
|
||||
continue;
|
||||
if (snippetsWindow && IsDialogMessageW(snippetsWindow, &msg))
|
||||
|
||||
+57
-57
@@ -493,7 +493,7 @@ bool selectedImage(HWND w, HistoryView *v)
|
||||
}
|
||||
void updateHistoryActions(HWND w, HistoryView *v)
|
||||
{
|
||||
SetDlgItemTextW(w,114,selectedImage(w,v)?L"Edit image":L"Open item");
|
||||
SetDlgItemTextW(w,114,selectedImage(w,v)?L"&Edit image":L"&Open item");
|
||||
}
|
||||
std::wstring selected(HWND w, HistoryView *v)
|
||||
{
|
||||
@@ -854,12 +854,12 @@ LRESULT CALLBACK historyProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
|
||||
p={row.left,row.top}; ClientToScreen(list,&p);
|
||||
}
|
||||
HMENU menu = CreatePopupMenu();
|
||||
AppendMenuW(menu, MF_STRING, 110, L"Paste");
|
||||
AppendMenuW(menu, MF_STRING, 111, L"Paste plain text");
|
||||
AppendMenuW(menu, MF_STRING, 112, L"Pin / unpin");
|
||||
AppendMenuW(menu, MF_STRING, 113, L"Delete");
|
||||
AppendMenuW(menu, MF_STRING, 114, selectedImage(w,v)?L"Edit image in screenshot editor":L"Open full item");
|
||||
AppendMenuW(menu, MF_STRING, 115, L"Copy text from image (OCR)");
|
||||
AppendMenuW(menu, MF_STRING, 110, L"&Paste");
|
||||
AppendMenuW(menu, MF_STRING, 111, L"Paste plain &text");
|
||||
AppendMenuW(menu, MF_STRING, 112, L"P&in / unpin");
|
||||
AppendMenuW(menu, MF_STRING, 113, L"&Delete");
|
||||
AppendMenuW(menu, MF_STRING, 114, selectedImage(w,v)?L"&Edit image in screenshot editor":L"&Open full item");
|
||||
AppendMenuW(menu, MF_STRING, 115, L"&Copy text from image (OCR)");
|
||||
int cmd = TrackPopupMenu(menu, TPM_RETURNCMD, p.x, p.y, 0, w, nullptr);
|
||||
DestroyMenu(menu);
|
||||
if (cmd)
|
||||
@@ -1159,9 +1159,9 @@ void showAutoOptions(HWND parent, SettingsView *v)
|
||||
SetWindowLongPtrW(w, GWLP_HWNDPARENT, (LONG_PTR)parent);
|
||||
SetWindowLongPtrW(w, GWLP_USERDATA, (LONG_PTR) new AutoOptionsView{parent, v});
|
||||
label(w, L"Offline dictionaries (only layouts in your cycle are used)", 18, 16, 525);
|
||||
checkbox(w, L"English", 401, 18, 48, 150);
|
||||
checkbox(w, L"Ukrainian", 402, 188, 48, 150);
|
||||
checkbox(w, L"Russian", 403, 358, 48, 150);
|
||||
checkbox(w, L"&English", 401, 18, 48, 150);
|
||||
checkbox(w, L"&Ukrainian", 402, 188, 48, 150);
|
||||
checkbox(w, L"&Russian", 403, 358, 48, 150);
|
||||
for (auto &lang : v->draft.autoLanguages)
|
||||
check(w, lang == L"en" ? 401 : lang == L"uk" ? 402 : 403, true);
|
||||
label(w, L"Excluded applications (process names, comma-separated)", 18, 94, 525);
|
||||
@@ -1174,7 +1174,7 @@ void showAutoOptions(HWND parent, SettingsView *v)
|
||||
for (auto [id, language] : {std::pair{411, L"en"}, {412, L"uk"}, {413, L"ru"}})
|
||||
{
|
||||
int x = 18 + (id - 411) * 178;
|
||||
label(w, id == 411 ? L"English" : id == 412 ? L"Ukrainian" : L"Russian", x, 302, 168);
|
||||
label(w, id == 411 ? L"En&glish" : id == 412 ? L"U&krainian" : L"Ru&ssian", x, 302, 168);
|
||||
edit(w, id, x, 328, 168, 56, ES_MULTILINE | WS_VSCROLL | ES_AUTOVSCROLL | ES_WANTRETURN);
|
||||
auto words = v->draft.autoWords.find(language);
|
||||
if (words != v->draft.autoWords.end()) value(w, id, commaList(words->second));
|
||||
@@ -1192,10 +1192,10 @@ void showAutoOptions(HWND parent, SettingsView *v)
|
||||
value(w, 408, v->draft.autoPrefixChars);
|
||||
value(w, 409, v->draft.autoSuffixChars);
|
||||
label(w, L"Enter punctuation without spaces or separators. These characters are kept.", 18, 530, 525);
|
||||
button(w, L"Open dictionary folder", 407, 18, 562, 220);
|
||||
button(w, L"Open dictionary &folder", 407, 18, 562, 220);
|
||||
label(w, L"After editing files, click OK here, then Save in Settings to reload.", 18, 600, 525);
|
||||
button(w, L"OK", IDOK, 326, 632, 100);
|
||||
button(w, L"Cancel", IDCANCEL, 440, 632, 100);
|
||||
button(w, L"&OK", IDOK, 326, 632, 100);
|
||||
button(w, L"&Cancel", IDCANCEL, 440, 632, 100);
|
||||
RECT initial{};
|
||||
GetWindowRect(w, &initial);
|
||||
initial.right += scale(w, 200);
|
||||
@@ -1278,23 +1278,23 @@ void showOcrOptions(HWND parent, SettingsView *v)
|
||||
SetWindowLongPtrW(w, GWLP_HWNDPARENT, (LONG_PTR)parent);
|
||||
SetWindowLongPtrW(w, GWLP_USERDATA, (LONG_PTR)new OcrOptionsView{parent, v});
|
||||
label(w, L"Windows OCR is used by default. Recognition stays on this computer.", 18, 16, 600);
|
||||
checkbox(w, L"Use optional Tesseract 5 instead of Windows OCR", 501, 18, 48, 600);
|
||||
checkbox(w, L"&Use optional Tesseract 5 instead of Windows OCR", 501, 18, 48, 600);
|
||||
check(w, 501, v->draft.useTesseract);
|
||||
label(w, L"Tesseract executable (blank = detect installed location)", 18, 90, 600);
|
||||
edit(w, 502, 18, 118, 484);
|
||||
value(w, 502, v->draft.tesseractPath);
|
||||
button(w, L"Browse…", 504, 514, 117, 106);
|
||||
button(w, L"&Browse…", 504, 514, 117, 106);
|
||||
label(w, L"Languages used together (for example ukr+eng+rus)", 18, 160, 600);
|
||||
edit(w, 503, 18, 188, 602);
|
||||
value(w, 503, v->draft.tesseractLanguages);
|
||||
button(w, L"Check installation", 505, 18, 228, 180);
|
||||
button(w, L"Check &installation", 505, 18, 228, 180);
|
||||
control(w, L"STATIC", L"", 0, 506, 212, 232, 408, 24);
|
||||
button(w, L"Download Tesseract…", 507, 18, 266, 180);
|
||||
button(w, L"&Download Tesseract…", 507, 18, 266, 180);
|
||||
label(w, L"Opens the Windows installer page in your browser.", 212, 270, 408);
|
||||
label(w, L"Changing the engine or languages reprocesses saved images in the background.", 18, 314, 602);
|
||||
label(w, L"Click OK here, then Save in Settings to apply. No downloads are automatic.", 18, 344, 602);
|
||||
button(w, L"OK", IDOK, 406, 390, 100);
|
||||
button(w, L"Cancel", IDCANCEL, 520, 390, 100);
|
||||
button(w, L"&OK", IDOK, 406, 390, 100);
|
||||
button(w, L"&Cancel", IDCANCEL, 520, 390, 100);
|
||||
EnableWindow(parent, FALSE);
|
||||
showRequestedWindow(w); SetForegroundWindow(w);
|
||||
}
|
||||
@@ -1606,18 +1606,18 @@ void showHistory(App &app, bool visible)
|
||||
app.historyWindow = w;
|
||||
windowLayoutState(w)->history = true;
|
||||
((HistoryView *)GetWindowLongPtrW(w, GWLP_USERDATA))->rich = createRichText(w, 105, false);
|
||||
label(w, L"Search clipboard history", 16, 12, 500);
|
||||
label(w, L"&Search clipboard history", 16, 12, 500);
|
||||
edit(w, 101, 16, 38, 728);
|
||||
auto list = control(w, L"LISTBOX", L"",
|
||||
WS_TABSTOP | WS_VSCROLL | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | WS_BORDER |
|
||||
LBS_OWNERDRAWFIXED | LBS_HASSTRINGS,
|
||||
102, 16, 76, 728, 360);
|
||||
SendMessageW(list, LB_SETITEMHEIGHT, 0, scale(w, 88));
|
||||
button(w, L"Paste", 110, 16, 452, 90);
|
||||
button(w, L"Plain text", 111, 114, 452, 100);
|
||||
button(w, L"Pin / unpin", 112, 222, 452, 110);
|
||||
button(w, L"Delete", 113, 340, 452, 90);
|
||||
button(w, L"Open item", 114, 438, 452, 100);
|
||||
button(w, L"&Paste", 110, 16, 452, 90);
|
||||
button(w, L"Plain &text", 111, 114, 452, 100);
|
||||
button(w, L"P&in / unpin", 112, 222, 452, 110);
|
||||
button(w, L"&Delete", 113, 340, 452, 90);
|
||||
button(w, L"&Open item", 114, 438, 452, 100);
|
||||
SetWindowSubclass(list, historyKey, 1, (DWORD_PTR)w);
|
||||
SetWindowSubclass(GetDlgItem(w, 101), historyKey, 1, (DWORD_PTR)w);
|
||||
}
|
||||
@@ -1656,13 +1656,13 @@ void showSettings(App &app, bool visible)
|
||||
HWND w = makeWindow(app, L"KSwitcher.Settings", L"KSwitcher settings", settingsProc, 780, 660, 0, false);
|
||||
app.settingsWindow = w;
|
||||
label(w, L"Text conversion", 18, 16);
|
||||
checkbox(w, L"Convert recently typed text when nothing is selected", 201, 18, 43, 390);
|
||||
checkbox(w, L"Switch Windows input layout after conversion", 202, 18, 70, 390);
|
||||
checkbox(w, L"Record clipboard history", 203, 18, 106);
|
||||
checkbox(w, L"Start with Windows", 204, 18, 133);
|
||||
checkbox(w, L"Start with Windows as administrator", 206, 18, 160, 395);
|
||||
checkbox(w, L"Keep screen awake", 205, 18, 184, 210);
|
||||
checkbox(w, L"Write log", 208, 240, 184, 160);
|
||||
checkbox(w, L"Con&vert recently typed text when nothing is selected", 201, 18, 43, 390);
|
||||
checkbox(w, L"Switch Windows input &layout after conversion", 202, 18, 70, 390);
|
||||
checkbox(w, L"Record clip&board history", 203, 18, 106);
|
||||
checkbox(w, L"Start with &Windows", 204, 18, 133);
|
||||
checkbox(w, L"Start with Windows as ad&ministrator", 206, 18, 160, 395);
|
||||
checkbox(w, L"&Keep screen awake", 205, 18, 184, 210);
|
||||
checkbox(w, L"Write lo&g", 208, 240, 184, 160);
|
||||
label(w, L"Click a shortcut and press the desired keys", 420, 16, 350);
|
||||
const wchar_t *names[] = {L"Next layout", L"Change case", L"Transliterate", L"Undo",
|
||||
L"Clipboard history", L"Screen capture"};
|
||||
@@ -1671,18 +1671,18 @@ void showSettings(App &app, bool visible)
|
||||
label(w, names[i], 420, 48 + i * 30, 115);
|
||||
hotkeyEdit(app, w, 210 + i, 540, 44 + i * 30, 220);
|
||||
}
|
||||
checkbox(w, L"Open editor after screen capture", 209, 420, 224, 340);
|
||||
checkbox(w, L"Automatically correct layout (experimental)", 207, 18, 210, 395);
|
||||
checkbox(w, L"Open editor after screen capt&ure", 209, 420, 224, 340);
|
||||
checkbox(w, L"Automatically correct layout (e&xperimental)", 207, 18, 210, 395);
|
||||
label(w, L"Layout cycle — top to bottom", 18, 240, 400);
|
||||
control(w, L"LISTBOX", L"", WS_TABSTOP | WS_VSCROLL | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | WS_BORDER, 220,
|
||||
18, 262, 470, 172);
|
||||
button(w, L"Move up", 232, 504, 262, 120);
|
||||
button(w, L"Move down", 233, 632, 262, 128);
|
||||
checkbox(w, L"Include in cycle", 230, 504, 300, 240);
|
||||
button(w, L"Move u&p", 232, 504, 262, 120);
|
||||
button(w, L"Move &down", 233, 632, 262, 128);
|
||||
checkbox(w, L"&Include in cycle", 230, 504, 300, 240);
|
||||
label(w, L"Direct layout shortcut", 504, 332, 240);
|
||||
hotkeyEdit(app, w, 231, 504, 356, 256);
|
||||
label(w, L"Backspace / Delete clears a shortcut", 504, 382, 256);
|
||||
button(w, L"Refresh layouts", 253, 504, 408, 150);
|
||||
button(w, L"Re&fresh layouts", 253, 504, 408, 150);
|
||||
label(w, L"History limits", 18, 444);
|
||||
label(w, L"Items", 18, 478, 50);
|
||||
edit(w, 240, 70, 474, 90);
|
||||
@@ -1690,18 +1690,18 @@ void showSettings(App &app, bool visible)
|
||||
edit(w, 241, 260, 474, 90);
|
||||
label(w, L"Per item MiB", 370, 478, 100);
|
||||
edit(w, 242, 475, 474, 80);
|
||||
button(w, L"Clear history…", 254, 580, 474, 180);
|
||||
button(w, L"Clear &history…", 254, 580, 474, 180);
|
||||
label(w, L"Exclude applications (process names, comma-separated)", 18, 516, 700);
|
||||
edit(w, 243, 18, 544, 742);
|
||||
button(w, L"Auto-correction options…", 255, 18, 575, 240);
|
||||
button(w, L"View log…", 256, 278, 575, 220);
|
||||
button(w, L"OCR options…", 257, 518, 575, 242);
|
||||
button(w, L"Import settings…", 250, 18, 606, 145);
|
||||
button(w, L"Export settings…", 251, 173, 606, 145);
|
||||
button(w, L"Snippets…", 252, 328, 606, 110);
|
||||
button(w, L"Sync...", 258, 448, 606, 90);
|
||||
button(w, L"Save", IDOK, 548, 606, 100);
|
||||
button(w, L"Cancel", IDCANCEL, 660, 606, 100);
|
||||
button(w, L"&Auto-correction options…", 255, 18, 575, 240);
|
||||
button(w, L"View l&og…", 256, 278, 575, 220);
|
||||
button(w, L"OCR op&tions…", 257, 518, 575, 242);
|
||||
button(w, L"Impo&rt settings…", 250, 18, 606, 145);
|
||||
button(w, L"&Export settings…", 251, 173, 606, 145);
|
||||
button(w, L"S&nippets…", 252, 328, 606, 110);
|
||||
button(w, L"S&ync...", 258, 448, 606, 90);
|
||||
button(w, L"&Save", IDOK, 548, 606, 100);
|
||||
button(w, L"&Cancel", IDCANCEL, 660, 606, 100);
|
||||
fillSettings(w, (SettingsView *)GetWindowLongPtrW(w, GWLP_USERDATA));
|
||||
if (visible)
|
||||
{
|
||||
@@ -1721,17 +1721,17 @@ void showSnippets(App &app)
|
||||
label(w, L"Triggers", 16, 16);
|
||||
control(w, L"LISTBOX", L"", WS_TABSTOP | WS_VSCROLL | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | WS_BORDER, 301,
|
||||
16, 44, 200, 380);
|
||||
button(w, L"Add", 310, 16, 438, 90);
|
||||
button(w, L"Delete", 311, 116, 438, 100);
|
||||
label(w, L"Trigger (for example ;addr)", 236, 16, 470);
|
||||
button(w, L"&Add", 310, 16, 438, 90);
|
||||
button(w, L"&Delete", 311, 116, 438, 100);
|
||||
label(w, L"&Trigger (for example ;addr)", 236, 16, 470);
|
||||
edit(w, 302, 236, 44, 488);
|
||||
label(w, L"Replacement text", 236, 82, 480);
|
||||
label(w, L"&Replacement text", 236, 82, 480);
|
||||
edit(w, 303, 236, 110, 488, 270, ES_MULTILINE | ES_WANTRETURN | ES_AUTOVSCROLL | WS_VSCROLL);
|
||||
control(w, L"STATIC", L"{cursor}: caret; {enter}: press Enter after insertion.\nUse {{cursor}} / {{enter}} for literal markers.", 0, 0, 236, 392, 488, 36);
|
||||
button(w, L"Insert cursor marker", 312, 236, 430, 190);
|
||||
button(w, L"Insert Enter marker", 313, 436, 430, 190);
|
||||
button(w, L"Save", IDOK, 510, 478, 100);
|
||||
button(w, L"Cancel", IDCANCEL, 624, 478, 100);
|
||||
button(w, L"Insert cursor &marker", 312, 236, 430, 190);
|
||||
button(w, L"Insert &Enter marker", 313, 436, 430, 190);
|
||||
button(w, L"&Save", IDOK, 510, 478, 100);
|
||||
button(w, L"&Cancel", IDCANCEL, 624, 478, 100);
|
||||
fillSnippets(w, (SnippetView *)GetWindowLongPtrW(w, GWLP_USERDATA));
|
||||
showRequestedWindow(w);
|
||||
SetForegroundWindow(w);
|
||||
|
||||
+9
-9
@@ -309,18 +309,18 @@ void showLogs(App &app)
|
||||
SendMessageW(choice, CB_ADDSTRING, 0, (LPARAM)L"Current log — kswitcher.log");
|
||||
SendMessageW(choice, CB_ADDSTRING, 0, (LPARAM)L"Previous log — kswitcher.log.1");
|
||||
SendMessageW(choice, CB_SETCURSEL, 0, 0);
|
||||
button(w, L"Refresh", LogRefresh, 310, 14, 100);
|
||||
button(w, L"Copy selected", LogCopy, 424, 14, 140);
|
||||
button(w, L"Clear log", LogClear, 578, 14, 100);
|
||||
button(w, L"&Refresh", LogRefresh, 310, 14, 100);
|
||||
button(w, L"&Copy selected", LogCopy, 424, 14, 140);
|
||||
button(w, L"Clear &log", LogClear, 578, 14, 100);
|
||||
control(w, L"STATIC", L"", 0, LogStatus, 16, 49, 1068, 22);
|
||||
label(w, L"Program", 16, 78, 170);
|
||||
label(w, L"&Program", 16, 78, 170);
|
||||
control(w, L"COMBOBOX", L"", WS_TABSTOP | CBS_DROPDOWNLIST | WS_VSCROLL, LogProgram, 16, 101, 174, 300);
|
||||
label(w, L"Command", 206, 78, 200);
|
||||
label(w, L"Co&mmand", 206, 78, 200);
|
||||
control(w, L"COMBOBOX", L"", WS_TABSTOP | CBS_DROPDOWNLIST | WS_VSCROLL, LogCommand, 206, 101, 204, 300);
|
||||
label(w, L"Result", 426, 78, 300);
|
||||
label(w, L"Res&ult", 426, 78, 300);
|
||||
control(w, L"COMBOBOX", L"", WS_TABSTOP | CBS_DROPDOWNLIST | WS_VSCROLL, LogResult, 426, 101, 310, 300);
|
||||
checkbox(w, L"Only problems", LogProblems, 752, 102, 166);
|
||||
button(w, L"Reset filters", LogReset, 938, 98, 146);
|
||||
checkbox(w, L"&Only problems", LogProblems, 752, 102, 166);
|
||||
button(w, L"Reset &filters", LogReset, 938, 98, 146);
|
||||
INITCOMMONCONTROLSEX common{sizeof(common), ICC_LISTVIEW_CLASSES};
|
||||
InitCommonControlsEx(&common);
|
||||
auto list = control(w, WC_LISTVIEWW, L"", WS_TABSTOP | WS_BORDER | LVS_REPORT | LVS_SHOWSELALWAYS,
|
||||
@@ -344,7 +344,7 @@ void showLogs(App &app)
|
||||
w, L"STATIC",
|
||||
L"Click a column to sort. Select an operation for details. Ctrl+C copies selected operations.", 0, 0,
|
||||
16, 712, 860, 22);
|
||||
button(w, L"Close", IDCANCEL, 974, 708, 110);
|
||||
button(w, L"Clos&e", IDCANCEL, 974, 708, 110);
|
||||
auto *layout = windowLayoutState(w);
|
||||
layout->stretch = list;
|
||||
layout->details = details;
|
||||
|
||||
+13
-13
@@ -83,25 +83,25 @@ void showSyncOptions(HWND parent, App &app)
|
||||
HWND w = makeWindow(app, L"KSwitcher.Sync", L"Settings synchronization - OpenBao", syncProc, 660, 640);
|
||||
SetWindowLongPtrW(w, GWLP_HWNDPARENT, (LONG_PTR)parent);
|
||||
SetWindowLongPtrW(w, GWLP_USERDATA, (LONG_PTR)new SyncView{parent, &app});
|
||||
checkbox(w, L"Enable synchronization with your OpenBao (KV v2)", 601, 18, 18, 620);
|
||||
checkbox(w, L"&Enable synchronization with your OpenBao (KV v2)", 601, 18, 18, 620);
|
||||
check(w, 601, c.enabled);
|
||||
label(w, L"Server (HTTPS)", 18, 60, 160); edit(w, 602, 190, 56, 450); value(w, 602, c.server);
|
||||
label(w, L"Username", 18, 100, 160); edit(w, 603, 190, 96, 450); value(w, 603, c.username);
|
||||
label(w, L"Password", 18, 140, 160); edit(w, 604, 190, 136, 450, 25, ES_AUTOHSCROLL | ES_PASSWORD);
|
||||
label(w, L"Se&rver (HTTPS)", 18, 60, 160); edit(w, 602, 190, 56, 450); value(w, 602, c.server);
|
||||
label(w, L"User&name", 18, 100, 160); edit(w, 603, 190, 96, 450); value(w, 603, c.username);
|
||||
label(w, L"&Password", 18, 140, 160); edit(w, 604, 190, 136, 450, 25, ES_AUTOHSCROLL | ES_PASSWORD);
|
||||
label(w, L"Leave password blank to use the saved Windows credential.", 190, 166, 450);
|
||||
label(w, L"KV v2 mount", 18, 210, 160); edit(w, 605, 190, 206, 450); value(w, 605, c.mount);
|
||||
label(w, L"Profile path", 18, 250, 160); edit(w, 606, 190, 246, 450); value(w, 606, c.path);
|
||||
label(w, L"Login mount", 18, 290, 160); edit(w, 607, 190, 286, 450); value(w, 607, c.authMount);
|
||||
label(w, L"KV v2 &mount", 18, 210, 160); edit(w, 605, 190, 206, 450); value(w, 605, c.mount);
|
||||
label(w, L"Profile pa&th", 18, 250, 160); edit(w, 606, 190, 246, 450); value(w, 606, c.path);
|
||||
label(w, L"&Login mount", 18, 290, 160); edit(w, 607, 190, 286, 450); value(w, 607, c.authMount);
|
||||
label(w, L"Sync uses saved settings. Close and reopen Settings after downloading changes.", 18, 330, 622);
|
||||
control(w, L"STATIC", L"", 0, 608, 18, 362, 622, 76);
|
||||
value(w, 608, app.syncCommand());
|
||||
button(w, L"Save / Sign in / Sync", IDOK, 18, 452, 200);
|
||||
button(w, L"Download remote", 610, 230, 452, 195);
|
||||
button(w, L"Upload local", 611, 437, 452, 203);
|
||||
button(w, L"Resolve individual conflicts...", 612, 18, 498, 300);
|
||||
button(w, L"&Save / Sign in / Sync", IDOK, 18, 452, 200);
|
||||
button(w, L"&Download remote", 610, 230, 452, 195);
|
||||
button(w, L"&Upload local", 611, 437, 452, 203);
|
||||
button(w, L"Resolve &individual conflicts...", 612, 18, 498, 300);
|
||||
label(w, L"Background check: every 10 minutes", 330, 502, 310);
|
||||
button(w, L"Disconnect / forget password", 609, 18, 550, 290);
|
||||
button(w, L"Close", IDCANCEL, 540, 550, 100);
|
||||
button(w, L"Disconnect / &forget password", 609, 18, 550, 290);
|
||||
button(w, L"&Close", IDCANCEL, 540, 550, 100);
|
||||
EnableWindow(parent, FALSE);
|
||||
SetTimer(w, 1, 1000, nullptr);
|
||||
showRequestedWindow(w); SetForegroundWindow(w);
|
||||
|
||||
@@ -70,11 +70,11 @@ void showSyncConflicts(HWND parent, App &app)
|
||||
label(w, L"This PC", 18, 190, 360); label(w, L"Server", 410, 190, 368);
|
||||
edit(w, 621, 18, 220, 368, 180, ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | WS_VSCROLL);
|
||||
edit(w, 622, 410, 220, 368, 180, ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | WS_VSCROLL);
|
||||
button(w, L"Use this PC for selected setting", 623, 18, 412, 368);
|
||||
button(w, L"Use server for selected setting", 624, 410, 412, 368);
|
||||
button(w, L"Use this &PC for selected setting", 623, 18, 412, 368);
|
||||
button(w, L"Use &server for selected setting", 624, 410, 412, 368);
|
||||
label(w, L"Unselected conflicts stay pending. Changed values will be checked again before saving.", 18, 454, 760);
|
||||
button(w, L"Apply choices", IDOK, 526, 488, 140);
|
||||
button(w, L"Cancel", IDCANCEL, 678, 488, 100);
|
||||
button(w, L"&Apply choices", IDOK, 526, 488, 140);
|
||||
button(w, L"&Cancel", IDCANCEL, 678, 488, 100);
|
||||
SendDlgItemMessageW(w, 620, LB_SETCURSEL, 0, 0); showConflictValues(w, v);
|
||||
EnableWindow(parent, FALSE); showRequestedWindow(w); SetForegroundWindow(w);
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
#include "platform.h"
|
||||
namespace ks
|
||||
{
|
||||
inline constexpr wchar_t AppVersion[] = L"0.6.26";
|
||||
inline constexpr wchar_t AppVersion[] = L"0.6.27";
|
||||
inline constexpr UINT UpdateMessage = WM_APP + 7;
|
||||
inline constexpr UINT UpdateCheckIntervalMs = 6 * 60 * 60 * 1000;
|
||||
inline constexpr UINT UpdateNotificationRetryMs = 5 * 60 * 1000;
|
||||
|
||||
@@ -627,6 +627,24 @@ int main(int argc, char **argv) {
|
||||
require(fitted.left==-600&&fitted.top==680&&fitted.right==0,"negative-coordinate monitor supported");
|
||||
ks::showSettings(app,false);
|
||||
require(GetDlgItem(app.settingsWindow,258)!=nullptr,"sync available without local opt-in");
|
||||
// Mnemonics only target visible controls; show without taking foreground focus.
|
||||
ShowWindow(app.settingsWindow,SW_SHOWNOACTIVATE);
|
||||
auto previousActive=SetActiveWindow(app.settingsWindow);
|
||||
auto awakeBefore=SendDlgItemMessageW(app.settingsWindow,205,BM_GETCHECK,0,0);
|
||||
MSG accessKey{}; accessKey.hwnd=GetDlgItem(app.settingsWindow,201);
|
||||
accessKey.message=WM_SYSCHAR; accessKey.wParam=L'k'; accessKey.lParam=1LL<<29;
|
||||
require(IsDialogMessageW(app.settingsWindow,&accessKey),"Alt+K is handled as a dialog access key");
|
||||
MSG dispatched{};
|
||||
while(PeekMessageW(&dispatched,nullptr,0,0,PM_REMOVE)) { TranslateMessage(&dispatched); DispatchMessageW(&dispatched); }
|
||||
require(SendDlgItemMessageW(app.settingsWindow,205,BM_GETCHECK,0,0)!=awakeBefore,
|
||||
"Alt+K toggles Keep screen awake");
|
||||
IsDialogMessageW(app.settingsWindow,&accessKey);
|
||||
while(PeekMessageW(&dispatched,nullptr,0,0,PM_REMOVE)) { TranslateMessage(&dispatched); DispatchMessageW(&dispatched); }
|
||||
require(SendDlgItemMessageW(app.settingsWindow,205,BM_GETCHECK,0,0)==awakeBefore,
|
||||
"repeated access key restores checkbox state");
|
||||
ShowWindow(app.settingsWindow,SW_HIDE);
|
||||
SetActiveWindow(previousActive);
|
||||
|
||||
DestroyWindow(app.settingsWindow);
|
||||
ks::saveSyncConfig(app.data,ks::SyncConfig{});
|
||||
ks::showSettings(app,false);
|
||||
|
||||
Reference in New Issue
Block a user