reapack

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

browser_entry.cpp (8016B)


      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 "browser_entry.hpp"
     19 
     20 #include "config.hpp"
     21 #include "index.hpp"
     22 #include "menu.hpp"
     23 #include "reapack.hpp"
     24 #include "string.hpp"
     25 
     26 #include <boost/range/adaptor/reversed.hpp>
     27 
     28 Browser::Entry::Entry(const Package *pkg, const Registry::Entry &re, const IndexPtr &i)
     29   : m_flags(0), regEntry(re), package(pkg), index(i), current(nullptr)
     30 {
     31   const auto &instOpts = g_reapack->config()->install;
     32   const bool pres = instOpts.bleedingEdge || re.test(Registry::Entry::BleedingEdgeFlag);
     33   latest = pkg->lastVersion(pres, regEntry.version);
     34 
     35   if(regEntry) {
     36     m_flags |= InstalledFlag;
     37 
     38     if(latest && regEntry.version < latest->name())
     39       m_flags |= OutOfDateFlag;
     40 
     41     current = pkg->findVersion(regEntry.version);
     42   }
     43   else
     44     m_flags |= UninstalledFlag;
     45 
     46   // Show latest pre-release if no stable version is available,
     47   // or the newest available version if older than current installed version.
     48   if(!latest)
     49     latest = pkg->lastVersion(true);
     50 
     51   if(g_reapack->remote(indexName()).isProtected())
     52     m_flags |= ProtectedFlag;
     53 }
     54 
     55 Browser::Entry::Entry(const Registry::Entry &re, const IndexPtr &i)
     56   : m_flags(InstalledFlag | ObsoleteFlag), regEntry(re), package(nullptr),
     57   index(i), current(nullptr), latest(nullptr)
     58 {}
     59 
     60 std::string Browser::Entry::displayState() const
     61 {
     62   std::string state;
     63 
     64   if(test(ObsoleteFlag))
     65     state += 'o';
     66   else if(test(OutOfDateFlag))
     67     state += 'u';
     68   else if(test(InstalledFlag))
     69     state += 'i';
     70   else
     71     state += '\x20';
     72 
     73   if(regEntry.test(Registry::Entry::PinnedFlag))
     74     state += 'p';
     75   if(regEntry.test(Registry::Entry::BleedingEdgeFlag))
     76     state += 'b';
     77 
     78   if(target)
     79     state += *target == nullptr ? 'R' : 'I';
     80 
     81   if(test(CanToggleFlags) && flags) {
     82     const int flagsDiff = regEntry.flags ^ *flags;
     83     if(flagsDiff & Registry::Entry::PinnedFlag)
     84       state += 'P';
     85     if(flagsDiff & Registry::Entry::BleedingEdgeFlag)
     86       state += 'B';
     87   }
     88 
     89   return state;
     90 }
     91 
     92 const std::string &Browser::Entry::indexName() const
     93 {
     94   return package ? package->category()->index()->name() : regEntry.remote;
     95 }
     96 
     97 const std::string &Browser::Entry::categoryName() const
     98 {
     99   return package ? package->category()->name() : regEntry.category;
    100 }
    101 
    102 const std::string &Browser::Entry::packageName() const
    103 {
    104   return package ? package->name() : regEntry.package;
    105 }
    106 
    107 std::string Browser::Entry::displayName() const
    108 {
    109   if(package)
    110     return package->displayName();
    111   else
    112     return Package::displayName(regEntry.package, regEntry.description);
    113 }
    114 
    115 Package::Type Browser::Entry::type() const
    116 {
    117   return latest ? package->type() : regEntry.type;
    118 }
    119 
    120 std::string Browser::Entry::displayType() const
    121 {
    122   return package ? package->displayType() : Package::displayType(regEntry.type);
    123 }
    124 
    125 std::string Browser::Entry::displayVersion() const
    126 {
    127   std::string display;
    128 
    129   if(test(InstalledFlag))
    130     display = regEntry.version.toString();
    131 
    132   if(latest && (!regEntry || latest->name() > regEntry.version)) {
    133     if(!display.empty())
    134       display += '\x20';
    135 
    136     display += '(' + latest->name().toString() + ')';
    137   }
    138 
    139   return display;
    140 }
    141 
    142 const VersionName *Browser::Entry::sortVersion() const
    143 {
    144   if(test(InstalledFlag))
    145     return &regEntry.version;
    146   else
    147     return &latest->name();
    148 }
    149 
    150 std::string Browser::Entry::displayAuthor() const
    151 {
    152   return latest ? latest->displayAuthor() : Version::displayAuthor(regEntry.author);
    153 }
    154 
    155 const Time *Browser::Entry::lastUpdate() const
    156 {
    157   return latest ? &latest->time() : nullptr;
    158 }
    159 
    160 void Browser::Entry::updateRow(ListView::Row *row) const
    161 {
    162   int c = 0;
    163   Time *time = const_cast<Time *>(lastUpdate());
    164 
    165   row->setCell(c++, displayState());
    166   row->setCell(c++, displayName());
    167   row->setCell(c++, categoryName());
    168   row->setCell(c++, displayVersion(), const_cast<VersionName *>(sortVersion()));
    169   row->setCell(c++, displayAuthor());
    170   row->setCell(c++, displayType());
    171   row->setCell(c++, indexName());
    172   row->setCell(c++, time ? time->toString() : std::string{}, time);
    173 }
    174 
    175 void Browser::Entry::fillMenu(Menu &menu) const
    176 {
    177   if(test(InstalledFlag)) {
    178     if(test(OutOfDateFlag)) {
    179       const UINT actionIndex = menu.addAction(String::format("U&pdate to v%s",
    180         latest->name().toString().c_str()), ACTION_LATEST);
    181 
    182       if(target && *target == latest)
    183         menu.check(actionIndex);
    184     }
    185 
    186     const UINT actionIndex = menu.addAction(String::format("&Reinstall v%s",
    187       regEntry.version.toString().c_str()), ACTION_REINSTALL);
    188 
    189     if(!current || test(ObsoleteFlag))
    190       menu.disable(actionIndex);
    191     else if(target && *target == current)
    192       menu.check(actionIndex);
    193   }
    194   else {
    195     const UINT actionIndex = menu.addAction(String::format("&Install v%s",
    196       latest->name().toString().c_str()), ACTION_LATEST);
    197     if(target && *target == latest)
    198       menu.check(actionIndex);
    199   }
    200 
    201   Menu versionMenu = menu.addMenu("Versions");
    202   const UINT versionMenuIndex = menu.size() - 1;
    203   if(test(ObsoleteFlag))
    204     menu.disable(versionMenuIndex);
    205   else {
    206     const auto &versions = package->versions();
    207     int verIndex = static_cast<int>(versions.size());
    208     for(const Version *ver : versions | boost::adaptors::reversed) {
    209       const UINT actionIndex = versionMenu.addAction(
    210         ver->name().toString().c_str(), --verIndex | (ACTION_VERSION << 8));
    211 
    212       if(target && *target == ver && ver != latest && ver != current)
    213         menu.check(versionMenuIndex);
    214 
    215       if(target ? *target == ver : ver == current)
    216         versionMenu.checkRadio(actionIndex);
    217     }
    218   }
    219 
    220   const UINT pinIndex = menu.addAction("&Pin current version", ACTION_PIN);
    221   if(!test(CanToggleFlags))
    222     menu.disable(pinIndex);
    223   if(flags.value_or(regEntry.flags) & Registry::Entry::PinnedFlag)
    224     menu.check(pinIndex);
    225 
    226   const UINT presIndex = menu.addAction("Enable pre-releases (&bleeding-edge)", ACTION_BLEEDINGEDGE);
    227   if(!test(CanToggleFlags))
    228     menu.disable(presIndex);
    229   if(flags.value_or(regEntry.flags) & Registry::Entry::BleedingEdgeFlag)
    230     menu.check(presIndex);
    231 
    232   const UINT uninstallIndex = menu.addAction("&Uninstall", ACTION_UNINSTALL);
    233   if(!test(InstalledFlag) || test(ProtectedFlag))
    234     menu.disable(uninstallIndex);
    235   else if(target && *target == nullptr)
    236     menu.check(uninstallIndex);
    237 
    238   menu.addSeparator();
    239 
    240   menu.setEnabled(!test(ObsoleteFlag),
    241     menu.addAction("About this &package", ACTION_ABOUT_PKG));
    242 
    243   menu.addAction(String::format("&About %s", indexName().c_str()), ACTION_ABOUT_REMOTE);
    244 }
    245 
    246 int Browser::Entry::possibleActions(bool allowToggle) const
    247 {
    248   int flags = 0;
    249 
    250   const auto canSetTarget = [=](const Version *ver) {
    251     return allowToggle || !target || *target != ver;
    252   };
    253 
    254   if((test(UninstalledFlag) || test(OutOfDateFlag)) && canSetTarget(latest))
    255     flags |= CanInstallLatest;
    256   if(test(InstalledFlag) && !test(ObsoleteFlag) && current && canSetTarget(current))
    257     flags |= CanReinstall;
    258   if(test(InstalledFlag) && !test(ProtectedFlag) && canSetTarget(nullptr))
    259     flags |= CanUninstall;
    260   if(target ? *target != nullptr : test(InstalledFlag))
    261     flags |= CanToggleFlags;
    262   if(target || flags)
    263     flags |= CanClearQueued;
    264 
    265   return flags;
    266 }
    267 
    268 bool Browser::Entry::operator==(const Entry &o) const
    269 {
    270   return indexName() == o.indexName() && categoryName() == o.categoryName() &&
    271     packageName() == o.packageName();
    272 }