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 ®Entry.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 }