reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

manager.cpp (17876B)


      1 /* ReaPack: Package manager for REAPER
      2  * Copyright (C) 2015-2025  Christian Fillion
      3  *
      4  * This program is free software: you can redistribute it and/or modify
      5  * it under the terms of the GNU Lesser General Public License as published by
      6  * the Free Software Foundation, either version 3 of the License, or
      7  * (at your option) any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12  * GNU Lesser General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Lesser General Public License
     15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     16  */
     17 
     18 #include "manager.hpp"
     19 
     20 #include "about.hpp"
     21 #include "archive.hpp"
     22 #include "config.hpp"
     23 #include "errors.hpp"
     24 #include "filedialog.hpp"
     25 #include "import.hpp"
     26 #include "listview.hpp"
     27 #include "menu.hpp"
     28 #include "progress.hpp"
     29 #include "reapack.hpp"
     30 #include "remote.hpp"
     31 #include "resource.hpp"
     32 #include "transaction.hpp"
     33 #include "win32.hpp"
     34 
     35 #include <algorithm>
     36 
     37 static const Win32::char_type *ARCHIVE_FILTER =
     38   L("ReaPack Offline Archive (*.ReaPackArchive)\0*.ReaPackArchive\0");
     39 static const Win32::char_type *ARCHIVE_EXT = L("ReaPackArchive");
     40 
     41 enum {
     42   ACTION_UNINSTALL = 80, ACTION_ABOUT, ACTION_REFRESH, ACTION_COPYURL,
     43   ACTION_SELECT, ACTION_UNSELECT, ACTION_AUTOINSTALL_GLOBAL,
     44   ACTION_AUTOINSTALL_OFF, ACTION_AUTOINSTALL_ON, ACTION_AUTOINSTALL,
     45   ACTION_BLEEDINGEDGE, ACTION_PROMPTOBSOLETE, ACTION_SYNONYMS, ACTION_NETCONFIG,
     46   ACTION_RESETCONFIG, ACTION_IMPORT_REPO, ACTION_IMPORT_ARCHIVE,
     47   ACTION_EXPORT_ARCHIVE,
     48 };
     49 
     50 enum { TIMER_ABOUT = 1, };
     51 
     52 Manager::Manager()
     53   : Dialog(IDD_CONFIG_DIALOG),
     54     m_list(nullptr), m_changes(0), m_importing(false)
     55 {
     56 }
     57 
     58 void Manager::onInit()
     59 {
     60   Dialog::onInit();
     61 
     62   auto msize = minimumSize();
     63   msize.y = 210;
     64   setMinimumSize(msize);
     65 
     66   m_apply = getControl(IDAPPLY);
     67   disable(m_apply);
     68 
     69   m_list = createControl<ListView>(IDC_LIST, ListView::Columns{
     70     {"Name", 155},
     71     {"Index URL", 435},
     72   });
     73 
     74   m_list->enableIcons();
     75   m_list->onSelect >> std::bind(&Dialog::startTimer, this, 100, TIMER_ABOUT, true);
     76   m_list->onIconClick >> std::bind(&Manager::toggleEnabled, this);
     77   m_list->onActivate >> std::bind(&Manager::aboutRepo, this, true);
     78   m_list->onFillContextMenu >> std::bind(&Manager::fillContextMenu, this,
     79     std::placeholders::_1, std::placeholders::_2);
     80 
     81   setAnchor(m_list->handle(), AnchorRight | AnchorBottom);
     82   setAnchor(getControl(IDC_IMPORT), AnchorTop | AnchorBottom);
     83   setAnchor(getControl(IDC_BROWSE), AnchorTop | AnchorBottom);
     84   setAnchor(getControl(IDC_OPTIONS), AnchorTop | AnchorBottom);
     85   setAnchor(getControl(IDOK), AnchorAll);
     86   setAnchor(getControl(IDCANCEL), AnchorAll);
     87   setAnchor(m_apply, AnchorAll);
     88 
     89   auto data = m_serializer.read(g_reapack->config()->windowState.manager, 2);
     90   restoreState(data);
     91   m_list->restoreState(data);
     92 
     93   refresh();
     94 
     95   m_list->autoSizeHeader();
     96 }
     97 
     98 void Manager::onTimer(const int id)
     99 {
    100   stopTimer(id);
    101 
    102   if(About *about = g_reapack->about(false)) {
    103     if(about->testDelegate<AboutIndexDelegate>())
    104       aboutRepo(false);
    105   }
    106 }
    107 
    108 void Manager::onClose()
    109 {
    110   Serializer::Data data;
    111   saveState(data);
    112   m_list->saveState(data);
    113   g_reapack->config()->windowState.manager = m_serializer.write(data);
    114 }
    115 
    116 void Manager::onCommand(const int id, int)
    117 {
    118   switch(id) {
    119   case IDC_IMPORT:
    120     importExport();
    121     break;
    122   case IDC_BROWSE:
    123     launchBrowser();
    124     break;
    125   case IDC_OPTIONS:
    126     options();
    127     break;
    128   case ACTION_REFRESH:
    129     refreshIndex();
    130     break;
    131   case ACTION_AUTOINSTALL_GLOBAL:
    132     setRemoteAutoInstall(boost::logic::indeterminate);
    133     break;
    134   case ACTION_AUTOINSTALL_OFF:
    135     setRemoteAutoInstall(false);
    136     break;
    137   case ACTION_AUTOINSTALL_ON:
    138     setRemoteAutoInstall(true);
    139     break;
    140   case ACTION_COPYURL:
    141     copyUrl();
    142     break;
    143   case ACTION_UNINSTALL:
    144     uninstall();
    145     break;
    146   case ACTION_IMPORT_REPO:
    147     importRepo();
    148     break;
    149   case ACTION_IMPORT_ARCHIVE:
    150     importArchive();
    151     break;
    152   case ACTION_EXPORT_ARCHIVE:
    153     exportArchive();
    154     break;
    155   case ACTION_AUTOINSTALL:
    156     toggle(m_autoInstall, g_reapack->config()->install.autoInstall);
    157     break;
    158   case ACTION_BLEEDINGEDGE:
    159     toggle(m_bleedingEdge, g_reapack->config()->install.bleedingEdge);
    160     break;
    161   case ACTION_PROMPTOBSOLETE:
    162     toggle(m_promptObsolete, g_reapack->config()->install.promptObsolete);
    163     break;
    164   case ACTION_SYNONYMS:
    165     toggle(m_expandSynonyms, g_reapack->config()->filter.expandSynonyms);
    166     break;
    167   case ACTION_NETCONFIG:
    168     setupNetwork();
    169     break;
    170   case ACTION_RESETCONFIG:
    171     g_reapack->config()->resetOptions();
    172     g_reapack->config()->restoreDefaultRemotes();
    173     refresh();
    174     break;
    175   case ACTION_SELECT:
    176     m_list->selectAll();
    177     SetFocus(m_list->handle());
    178     break;
    179   case ACTION_UNSELECT:
    180     m_list->unselectAll();
    181     SetFocus(m_list->handle());
    182     break;
    183   case IDOK:
    184   case IDAPPLY:
    185     if(confirm()) {
    186       if(!apply() || id == IDAPPLY)
    187         break;
    188 
    189       // IDOK -> continue to next case (IDCANCEL)
    190     }
    191     else {
    192       setChange(-static_cast<int>(m_uninstall.size()));
    193       m_uninstall.clear();
    194       refresh();
    195       break;
    196     }
    197     [[fallthrough]];
    198   case IDCANCEL:
    199     close();
    200     break;
    201   default:
    202     const int action = id >> 8;
    203     if(action == ACTION_ABOUT)
    204       g_reapack->about(getRemote(id & 0xff));
    205     break;
    206   }
    207 }
    208 
    209 bool Manager::fillContextMenu(Menu &menu, const int index) const
    210 {
    211   const Remote &remote = getRemote(index);
    212 
    213   if(!remote) {
    214     menu.addAction("&Select all", ACTION_SELECT);
    215     menu.addAction("&Unselect all", ACTION_UNSELECT);
    216     return true;
    217   }
    218 
    219   menu.addAction("&Refresh", ACTION_REFRESH);
    220   menu.addAction("&Copy URL", ACTION_COPYURL);
    221 
    222   Menu autoInstallMenu = menu.addMenu("&Install new packages");
    223   const UINT autoInstallGlobal = autoInstallMenu.addAction(
    224     "Use &global setting", ACTION_AUTOINSTALL_GLOBAL);
    225   const UINT autoInstallOff = autoInstallMenu.addAction(
    226     "Manually", ACTION_AUTOINSTALL_OFF);
    227   const UINT autoInstallOn = autoInstallMenu.addAction(
    228     "When synchronizing", ACTION_AUTOINSTALL_ON);
    229 
    230   const UINT uninstallAction =
    231     menu.addAction("&Uninstall", ACTION_UNINSTALL);
    232 
    233   menu.addSeparator();
    234 
    235   menu.addAction(String::format("&About %s", remote.name().c_str()),
    236     index | (ACTION_ABOUT << 8));
    237 
    238   bool allProtected = true;
    239   bool allAutoInstallGlobal = true;
    240   bool allAutoInstallOff = true;
    241   bool allAutoInstallOn = true;
    242 
    243   for(const int i : m_list->selection()) {
    244     const Remote &r = getRemote(i);
    245     if(!r.isProtected())
    246       allProtected = false;
    247 
    248     const tribool &autoInstall = remoteAutoInstall(r);
    249     if(boost::logic::indeterminate(autoInstall)) {
    250       allAutoInstallOff = false;
    251       allAutoInstallOn = false;
    252     }
    253     else {
    254       allAutoInstallGlobal = false;
    255       if(autoInstall)
    256         allAutoInstallOff = false;
    257       else if(!autoInstall)
    258         allAutoInstallOn = false;
    259     }
    260   };
    261 
    262   if(allProtected)
    263     menu.disable(uninstallAction);
    264 
    265   if(allAutoInstallGlobal)
    266     autoInstallMenu.check(autoInstallGlobal);
    267   else if(allAutoInstallOff)
    268     autoInstallMenu.check(autoInstallOff);
    269   else if(allAutoInstallOn)
    270     autoInstallMenu.check(autoInstallOn);
    271 
    272   return true;
    273 }
    274 
    275 bool Manager::onKeyDown(const int key, const int mods)
    276 {
    277   if(GetFocus() != m_list->handle())
    278     return false;
    279 
    280   if(mods == CtrlModifier && key == 'A')
    281     m_list->selectAll();
    282   else if(mods == (CtrlModifier | ShiftModifier) && key == 'A')
    283     m_list->unselectAll();
    284   else if(mods == CtrlModifier && key == 'C')
    285     copyUrl();
    286   else if(!mods && key == VK_SPACE)
    287     toggleEnabled();
    288   else
    289     return false;
    290 
    291   return true;
    292 }
    293 
    294 void Manager::refresh()
    295 {
    296   ListView::BeginEdit edit(m_list);
    297 
    298   const std::vector<int> selection = m_list->selection();
    299   std::vector<std::string> selected(selection.size());
    300   for(size_t i = 0; i < selection.size(); i++)
    301     selected[i] = m_list->row(selection[i])->cell(0).value; // TODO: use data ptr to Remote
    302 
    303   const auto &remotes = g_reapack->config()->remotes;
    304 
    305   m_list->clear();
    306   m_list->reserveRows(remotes.size());
    307 
    308   for(const Remote &remote : remotes) {
    309     if(m_uninstall.count(remote))
    310       continue;
    311 
    312     int c = 0;
    313     auto row = m_list->createRow();
    314     row->setChecked(isRemoteEnabled(remote));
    315     row->setCell(c++, remote.name());
    316     row->setCell(c++, remote.url());
    317 
    318     if(find(selected.begin(), selected.end(), remote.name()) != selected.end())
    319       m_list->select(row->index());
    320   }
    321 }
    322 
    323 void Manager::setMods(const ModsCallback &cb)
    324 {
    325   ListView::BeginEdit edit(m_list);
    326 
    327   for(const int index : m_list->selection()) {
    328     const Remote &remote = getRemote(index);
    329 
    330     auto it = m_mods.find(remote);
    331 
    332     if(it == m_mods.end()) {
    333       RemoteMods mods;
    334       cb(remote, index, &mods);
    335 
    336       if(!mods)
    337         continue;
    338 
    339       m_mods.insert({remote, mods});
    340       setChange(1);
    341     }
    342     else {
    343       RemoteMods *mods = &it->second;
    344       cb(remote, index, mods);
    345 
    346       if(!*mods) {
    347         m_mods.erase(it);
    348         setChange(-1);
    349       }
    350     }
    351   }
    352 }
    353 
    354 void Manager::toggleEnabled()
    355 {
    356   setMods([=](const Remote &remote, const int index, RemoteMods *mods) {
    357     const bool enable = !mods->enable.value_or(remote.isEnabled());
    358 
    359     if(remote.isEnabled() == enable)
    360       mods->enable = std::nullopt;
    361     else
    362       mods->enable = enable;
    363 
    364     m_list->row(index)->setChecked(enable);
    365   });
    366 }
    367 
    368 bool Manager::isRemoteEnabled(const Remote &remote) const
    369 {
    370   const auto &it = m_mods.find(remote);
    371 
    372   if(it == m_mods.end())
    373     return remote.isEnabled();
    374   else
    375     return it->second.enable.value_or(remote.isEnabled());
    376 }
    377 
    378 void Manager::setRemoteAutoInstall(const tribool &enabled)
    379 {
    380   setMods([=](const Remote &remote, int, RemoteMods *mods) {
    381     if(remote.autoInstall() == enabled
    382         || (indeterminate(remote.autoInstall()) && indeterminate(enabled)))
    383       mods->autoInstall = std::nullopt;
    384     else
    385       mods->autoInstall = enabled;
    386   });
    387 }
    388 
    389 tribool Manager::remoteAutoInstall(const Remote &remote) const
    390 {
    391   const auto &it = m_mods.find(remote);
    392 
    393   if(it == m_mods.end())
    394     return remote.autoInstall();
    395   else
    396     return it->second.autoInstall.value_or(remote.autoInstall());
    397 }
    398 
    399 void Manager::refreshIndex()
    400 {
    401   if(!m_list->hasSelection())
    402     return;
    403 
    404   const std::vector<int> selection = m_list->selection();
    405   std::vector<Remote> remotes(selection.size());
    406   for(size_t i = 0; i < selection.size(); i++)
    407     remotes[i] = getRemote(selection[i]);
    408 
    409   if(Transaction *tx = g_reapack->setupTransaction()) {
    410     tx->fetchIndexes(remotes, true);
    411     tx->runTasks();
    412   }
    413 }
    414 
    415 void Manager::uninstall()
    416 {
    417   int keep = 0;
    418 
    419   while(m_list->selectionSize() - keep > 0) {
    420     const int index = m_list->currentIndex() + keep;
    421     const Remote &remote = getRemote(index);
    422 
    423     if(remote.isProtected()) {
    424       keep++;
    425       continue;
    426     }
    427 
    428     m_uninstall.insert(remote);
    429     setChange(1);
    430 
    431     m_list->removeRow(index);
    432   }
    433 }
    434 
    435 void Manager::toggle(std::optional<bool> &setting, const bool current)
    436 {
    437   setting = !setting.value_or(current);
    438   setChange(*setting == current ? -1 : 1);
    439 }
    440 
    441 void Manager::setChange(const int increment)
    442 {
    443   if(!m_changes && increment < 0)
    444     return;
    445 
    446   m_changes += increment;
    447 
    448   if(m_changes)
    449     enable(m_apply);
    450   else
    451     disable(m_apply);
    452 }
    453 
    454 void Manager::copyUrl()
    455 {
    456   std::vector<std::string> values;
    457 
    458   for(const int index : m_list->selection(false))
    459     values.push_back(getRemote(index).url());
    460 
    461   setClipboard(values);
    462 }
    463 
    464 void Manager::aboutRepo(const bool focus)
    465 {
    466   if(m_list->hasSelection())
    467     g_reapack->about(getRemote(m_list->currentIndex()), focus);
    468 }
    469 
    470 void Manager::importExport()
    471 {
    472   Menu menu;
    473   menu.addAction("Import &repositories...", ACTION_IMPORT_REPO);
    474   menu.addSeparator();
    475   menu.addAction("Import offline archive...", ACTION_IMPORT_ARCHIVE);
    476   menu.addAction("&Export offline archive...", ACTION_EXPORT_ARCHIVE);
    477 
    478   menu.show(getControl(IDC_IMPORT), handle());
    479 }
    480 
    481 bool Manager::importRepo()
    482 {
    483   if(m_importing) // avoid opening the import dialog twice on windows
    484     return true;
    485 
    486   m_importing = true;
    487   const auto ret = Dialog::Show<Import>(instance(), handle());
    488   m_importing = false;
    489 
    490   return ret != 0;
    491 }
    492 
    493 void Manager::importArchive()
    494 {
    495   const char *title = "Import offline archive";
    496 
    497   const std::string &path = FileDialog::getOpenFileName(handle(), instance(),
    498     title, Path::DATA.prependRoot(), ARCHIVE_FILTER, ARCHIVE_EXT);
    499 
    500   if(path.empty())
    501     return;
    502 
    503   try {
    504     Archive::import(path);
    505   }
    506   catch(const reapack_error &e) {
    507     Win32::messageBox(handle(), String::format(
    508       "An error occured while reading %s.\n\n%s.", path.c_str(), e.what()
    509     ).c_str(), title, MB_OK);
    510   }
    511 }
    512 
    513 void Manager::exportArchive()
    514 {
    515   const std::string &path = FileDialog::getSaveFileName(handle(), instance(),
    516     "Export offline archive", Path::DATA.prependRoot(), ARCHIVE_FILTER, ARCHIVE_EXT);
    517 
    518   if(!path.empty()) {
    519     if(Transaction *tx = g_reapack->setupTransaction()) {
    520       tx->exportArchive(path);
    521       tx->runTasks();
    522     }
    523   }
    524 }
    525 
    526 void Manager::launchBrowser()
    527 {
    528   const auto promptApply = [this] {
    529     return IDYES == Win32::messageBox(handle(), "Apply unsaved changes?", "ReaPack Query", MB_YESNO);
    530   };
    531 
    532   if(m_changes && promptApply())
    533     apply();
    534 
    535   g_reapack->browsePackages();
    536 }
    537 
    538 void Manager::options()
    539 {
    540   Menu menu;
    541 
    542   UINT index = menu.addAction(
    543     "&Install new packages when synchronizing", ACTION_AUTOINSTALL);
    544   if(m_autoInstall.value_or(g_reapack->config()->install.autoInstall))
    545     menu.check(index);
    546 
    547   index = menu.addAction(
    548     "Enable &pre-releases globally (bleeding edge)", ACTION_BLEEDINGEDGE);
    549   if(m_bleedingEdge.value_or(g_reapack->config()->install.bleedingEdge))
    550     menu.check(index);
    551 
    552   index = menu.addAction(
    553     "Prompt to uninstall obsolete packages", ACTION_PROMPTOBSOLETE);
    554   if(m_promptObsolete.value_or(g_reapack->config()->install.promptObsolete))
    555     menu.check(index);
    556 
    557   index = menu.addAction(
    558     "Search for synonyms of common words", ACTION_SYNONYMS);
    559   if(m_expandSynonyms.value_or(g_reapack->config()->filter.expandSynonyms))
    560     menu.check(index);
    561 
    562   menu.addAction("&Network settings...", ACTION_NETCONFIG);
    563 
    564   menu.addSeparator();
    565 
    566   menu.addAction("&Restore default settings", ACTION_RESETCONFIG);
    567 
    568   menu.show(getControl(IDC_OPTIONS), handle());
    569 }
    570 
    571 void Manager::setupNetwork()
    572 {
    573   if(IDOK == Dialog::Show<NetworkConfig>(instance(), handle(), &g_reapack->config()->network))
    574     g_reapack->config()->write();
    575 }
    576 
    577 bool Manager::confirm() const
    578 {
    579   const size_t uninstallCount = m_uninstall.size();
    580 
    581   if(!uninstallCount)
    582     return true;
    583 
    584   return IDYES == Win32::messageBox(handle(), String::format(
    585     "Uninstall %zu %s?\n"
    586     "Every file they contain will be removed from your computer.",
    587     uninstallCount, uninstallCount == 1 ? "repository" : "repositories"
    588   ).c_str(), "ReaPack Query", MB_YESNO);
    589 }
    590 
    591 bool Manager::apply()
    592 {
    593   if(!m_changes)
    594     return true;
    595 
    596   Transaction *tx = g_reapack->setupTransaction();
    597 
    598   if(!tx)
    599     return false;
    600 
    601   // syncList is the list of repos to synchronize for autoinstall
    602   // (global or local setting)
    603   std::set<Remote> syncList;
    604 
    605   if(m_autoInstall) {
    606     g_reapack->config()->install.autoInstall = *m_autoInstall;
    607 
    608     if(*m_autoInstall) {
    609       const auto &enabledNow = g_reapack->config()->remotes.getEnabled();
    610       copy(enabledNow.begin(), enabledNow.end(), inserter(syncList, syncList.end()));
    611     }
    612   }
    613 
    614   if(m_bleedingEdge)
    615     g_reapack->config()->install.bleedingEdge = *m_bleedingEdge;
    616 
    617   if(m_promptObsolete)
    618     g_reapack->config()->install.promptObsolete = *m_promptObsolete;
    619 
    620   if(m_expandSynonyms)
    621     g_reapack->config()->filter.expandSynonyms = *m_expandSynonyms;
    622 
    623   for(const auto &pair : m_mods) {
    624     Remote remote = pair.first;
    625     const RemoteMods &mods = pair.second;
    626 
    627     if(m_uninstall.find(remote) != m_uninstall.end())
    628       continue;
    629 
    630     if(mods.enable) {
    631       remote.setEnabled(*mods.enable);
    632       syncList.erase(remote);
    633     }
    634 
    635     if(mods.autoInstall) {
    636       remote.setAutoInstall(*mods.autoInstall);
    637 
    638       const bool isEnabled = mods.enable.value_or(remote.isEnabled());
    639 
    640       if(isEnabled && *mods.autoInstall)
    641         syncList.insert(remote);
    642     }
    643 
    644     g_reapack->addSetRemote(remote);
    645   }
    646 
    647   for(const Remote &remote : m_uninstall) {
    648     g_reapack->uninstall(remote);
    649     syncList.erase(remote);
    650   }
    651 
    652   for(const Remote &remote : syncList)
    653     tx->synchronize(remote);
    654 
    655   reset();
    656 
    657   g_reapack->commitConfig();
    658 
    659   return true;
    660 }
    661 
    662 void Manager::reset()
    663 {
    664   m_mods.clear();
    665   m_uninstall.clear();
    666   m_autoInstall = m_bleedingEdge = m_promptObsolete =
    667                   m_expandSynonyms = std::nullopt;
    668 
    669   m_changes = 0;
    670   disable(m_apply);
    671 }
    672 
    673 Remote Manager::getRemote(const int index) const
    674 {
    675   if(index < 0 || index > m_list->rowCount() - 1)
    676     return {};
    677 
    678   const std::string &remoteName = m_list->row(index)->cell(0).value;
    679   return g_reapack->config()->remotes.get(remoteName);
    680 }
    681 
    682 NetworkConfig::NetworkConfig(NetworkOpts *opts)
    683   : Dialog(IDD_NETCONF_DIALOG), m_opts(opts)
    684 {
    685 }
    686 
    687 void NetworkConfig::onInit()
    688 {
    689   Dialog::onInit();
    690 
    691   m_proxy = getControl(IDC_PROXY);
    692   Win32::setWindowText(m_proxy, m_opts->proxy.c_str());
    693 
    694   m_verifyPeer = getControl(IDC_VERIFYPEER);
    695   setChecked(m_opts->verifyPeer, m_verifyPeer);
    696 
    697   m_staleThreshold = getControl(IDC_STALETHRSH);
    698   setChecked(m_opts->staleThreshold > 0, m_staleThreshold);
    699 }
    700 
    701 void NetworkConfig::onCommand(const int id, int)
    702 {
    703   switch(id) {
    704   case IDOK:
    705     apply();
    706     [[fallthrough]];
    707   case IDCANCEL:
    708     close(id);
    709     break;
    710   }
    711 }
    712 
    713 void NetworkConfig::apply()
    714 {
    715   m_opts->proxy = Win32::getWindowText(m_proxy);
    716   m_opts->verifyPeer = isChecked(m_verifyPeer);
    717   m_opts->staleThreshold = isChecked(m_staleThreshold)
    718     ? NetworkOpts::OneWeekThreshold : NetworkOpts::NoThreshold;
    719 }