#include "ui_sync_conflicts.inl" struct SyncView { HWND parent; App *app; }; void updateSyncControls(HWND w, App &app) { bool enabled = checked(w, 601); bool savedEnabled = loadSyncConfig(app.data).enabled; for (int id : {602, 603, 604, 605, 606, 607, 610, 611, 613}) EnableWindow(GetDlgItem(w, id), enabled); EnableWindow(GetDlgItem(w, 612), enabled && savedEnabled); EnableWindow(GetDlgItem(w, IDOK), enabled || savedEnabled); value(w, IDOK, enabled ? L"&Save / Sign in / Sync" : L"&Save"); std::wstring status; if (!enabled) status = savedEnabled ? L"Click Save to disable synchronization." : L"Synchronization is disabled. Enable it above to configure and sync."; else if (!savedEnabled) status = L"Configure the connection, then click Save / Sign in / Sync to enable synchronization."; else status = app.syncCommand(); if (value(w, 608) != status) value(w, 608, status); } SyncConfig syncFields(HWND w) { SyncConfig c; c.enabled = checked(w, 601); c.keepLocalLayouts = checked(w, 613); c.server = value(w, 602); while (!c.server.empty() && c.server.back() == L'/') c.server.pop_back(); c.username = lower(value(w, 603)); c.mount = value(w, 605); c.path = value(w, 606); c.authMount = value(w, 607); return c; } LRESULT CALLBACK syncProc(HWND w, UINT msg, WPARAM wp, LPARAM lp) { if (msg == WM_DPICHANGED) { resizeForDpi(w, wp, lp); return 0; } auto *v = reinterpret_cast(GetWindowLongPtrW(w, GWLP_USERDATA)); if (!v) return DefWindowProcW(w, msg, wp, lp); try { if (msg == WM_TIMER) { updateSyncControls(w, *v->app); return 0; } if (msg == WM_COMMAND) { int id = LOWORD(wp); if (id == 601 && HIWORD(wp) == BN_CLICKED) { updateSyncControls(w, *v->app); return 0; } if ((id == IDOK || id == 610 || id == 611 || id == 612) && !IsWindowEnabled(GetDlgItem(w, id))) return 0; if (id == 612) { showSyncConflicts(w, *v->app); return 0; } if (id == IDCANCEL) { DestroyWindow(w); return 0; } if (id == 609) { auto c = loadSyncConfig(v->app->data); c.enabled = false; saveSyncConfig(v->app->data, c); forgetSyncPassword(c); check(w, 601, false); value(w, 604, L""); updateSyncControls(w, *v->app); value(w, 608, L"Disabled. Saved password removed. An in-flight server request may still finish."); KillTimer(w, 1); return 0; } if (id == IDOK || id == 610 || id == 611) { if (!checked(w, 601)) { if (id != IDOK) return 0; auto c = loadSyncConfig(v->app->data); c.enabled = false; saveSyncConfig(v->app->data, c); value(w, 604, L""); updateSyncControls(w, *v->app); return 0; } if (id != IDOK && MessageBoxW(w, id == 610 ? L"Replace this PC's shared settings with the remote profile? A local backup will be saved. Unsaved edits in Settings are not included." : L"Replace the remote profile with this PC's saved settings? OpenBao retains older versions according to its retention policy. Unsaved edits are not included.", L"Resolve synchronization", MB_YESNO | MB_ICONWARNING) != IDYES) return 0; auto c = syncFields(w); if (c.enabled) validateSyncConfig(c); auto password = value(w, 604); try { if (!password.empty()) saveSyncPassword(c, password); } catch (...) { SecureZeroMemory(password.data(), password.size() * sizeof(wchar_t)); throw; } SecureZeroMemory(password.data(), password.size() * sizeof(wchar_t)); value(w, 604, L""); saveSyncConfig(v->app->data, c); value(w, 608, v->app->syncCommand(id == 610 ? 2 : id == 611 ? 3 : 1)); updateSyncControls(w, *v->app); SetTimer(w, 1, 1000, nullptr); return 0; } } } catch (const std::exception &e) { // Never display parser exceptions containing connection file contents. value(w, 608, L"Unable to complete sync action. Check connection fields and saved configuration."); if (dynamic_cast(&e)) value(w, 608, wide(e.what())); return 0; } if (msg == WM_CLOSE) { DestroyWindow(w); return 0; } if (msg == WM_NCDESTROY) { KillTimer(w, 1); EnableWindow(v->parent, TRUE); SetForegroundWindow(v->parent); disposeFont(w); delete v; SetWindowLongPtrW(w, GWLP_USERDATA, 0); } return DefWindowProcW(w, msg, wp, lp); } void showSyncOptions(HWND parent, App &app) { auto c = loadSyncConfig(app.data); HWND w = makeWindow(app, L"KSwitcher.Sync", L"Settings synchronization - OpenBao", syncProc, 660, 672); 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); check(w, 601, c.enabled); 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 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); edit(w, 608, 18, 362, 622, 76, ES_MULTILINE | ES_READONLY | WS_VSCROLL | ES_AUTOVSCROLL); value(w, 608, app.syncCommand()); checkbox(w, L"&Keep this PC's layouts and layout shortcuts (sync other settings)", 613, 18, 446, 622); check(w, 613, c.keepLocalLayouts); button(w, L"&Save / Sign in / Sync", IDOK, 18, 484, 200); button(w, L"&Download remote", 610, 230, 484, 195); button(w, L"&Upload local", 611, 437, 484, 203); button(w, L"Resolve &individual conflicts...", 612, 18, 530, 300); label(w, L"Background check: every 10 minutes", 330, 534, 310); button(w, L"Disconnect / &forget password", 609, 18, 582, 290); button(w, L"&Close", IDCANCEL, 540, 582, 100); updateSyncControls(w, app); EnableWindow(parent, FALSE); SetTimer(w, 1, 1000, nullptr); showRequestedWindow(w); SetForegroundWindow(w); }