1 /* 2 * Copyright (c) 2007-2010, Haiku, Inc. 3 * Distributed under the terms of the MIT license. 4 * 5 * Author: 6 * Łukasz 'Sil2100' Zemczak <sil2100@vexillium.org> 7 */ 8 9 10 #include "UninstallView.h" 11 12 #include <stdio.h> 13 #include <string.h> 14 15 #include <Alert.h> 16 #include <Box.h> 17 #include <Button.h> 18 #include <Catalog.h> 19 #include <ControlLook.h> 20 #include <Directory.h> 21 #include <Entry.h> 22 #include <File.h> 23 #include <FilePanel.h> 24 #include <FindDirectory.h> 25 #include <LayoutBuilder.h> 26 #include <ListView.h> 27 #include <Locale.h> 28 #include <NodeMonitor.h> 29 #include <ScrollView.h> 30 #include <SeparatorView.h> 31 #include <String.h> 32 #include <StringView.h> 33 #include <SpaceLayoutItem.h> 34 #include <TextView.h> 35 36 #include "main.h" 37 38 39 #undef B_TRANSLATION_CONTEXT 40 #define B_TRANSLATION_CONTEXT "UninstallView" 41 42 43 enum { 44 P_MSG_INSTALL = 'umin', 45 P_MSG_REMOVE = 'umrm', 46 P_MSG_SELECT 47 }; 48 49 50 // TODO list: 51 // - B_ENTRY_MOVED 52 // - Right now the installed package info naming convention is the same 53 // as at SoftwareValet. Maybe there would be a better one? 54 // - Add a status window (reuse the one from PackageInstall) 55 56 57 class UninstallView::InfoItem : public BStringItem { 58 public: 59 InfoItem(const BString& name, const BString& version, 60 const char* filename, const node_ref& ref) 61 : 62 BStringItem(name.String()), 63 fName(name), 64 fVersion(version), 65 fNodeRef(ref) 66 { 67 if (fName.Length() == 0) 68 SetText(filename); 69 } 70 71 const char* GetName() { return fName.String(); } 72 const char* GetVersion() { return fVersion.String(); }; 73 node_ref GetNodeRef() { return fNodeRef; }; 74 75 private: 76 BString fName; 77 BString fVersion; 78 node_ref fNodeRef; 79 }; 80 81 82 83 84 UninstallView::UninstallView() 85 : 86 BGroupView(B_VERTICAL), 87 fOpenPanel(new BFilePanel(B_OPEN_PANEL)) 88 { 89 fNoPackageSelectedString = B_TRANSLATE("No package selected."); 90 _InitView(); 91 } 92 93 94 UninstallView::~UninstallView() 95 { 96 // Stop all node watching 97 stop_watching(this); 98 } 99 100 101 void 102 UninstallView::AttachedToWindow() 103 { 104 fAppList->SetTarget(this); 105 fInstallButton->SetTarget(this); 106 fRemoveButton->SetTarget(this); 107 108 _ReloadAppList(); 109 110 // We loaded the list, but now let's set up a node watcher for the packages 111 // directory, so that we can update the list of installed packages in real 112 // time 113 _CachePathToPackages(); 114 node_ref ref; 115 fWatcherRunning = false; 116 BDirectory dir(fToPackages.Path()); 117 if (dir.InitCheck() != B_OK) { 118 // The packages/ directory obviously does not exist. 119 // Since this is the case, we need to watch for it to appear first 120 121 BPath path; 122 fToPackages.GetParent(&path); 123 if (dir.SetTo(path.Path()) != B_OK) 124 return; 125 } else 126 fWatcherRunning = true; 127 128 dir.GetNodeRef(&ref); 129 130 if (watch_node(&ref, B_WATCH_DIRECTORY, this) != B_OK) { 131 fWatcherRunning = false; 132 return; 133 } 134 } 135 136 137 void 138 UninstallView::MessageReceived(BMessage* msg) 139 { 140 switch (msg->what) { 141 case B_NODE_MONITOR: 142 { 143 int32 opcode; 144 if (msg->FindInt32("opcode", &opcode) != B_OK) 145 break; 146 147 fprintf(stderr, "Got an opcoded node monitor message\n"); 148 if (opcode == B_ENTRY_CREATED) { 149 fprintf(stderr, "Created?...\n"); 150 BString filename, name, version; 151 node_ref ref; 152 if (msg->FindString("name", &filename) != B_OK 153 || msg->FindInt32("device", &ref.device) != B_OK 154 || msg->FindInt64("node", &ref.node) != B_OK) 155 break; 156 157 // TODO: This obviously is a hack 158 // The node watcher informs the view a bit to early, and 159 // because of this the data of the node is not ready at this 160 // moment. For this reason, we must give the filesystem some 161 // time before continuing. 162 usleep(10000); 163 164 if (fWatcherRunning) { 165 _AddFile(filename.String(), ref); 166 } else { 167 // This most likely means we were waiting for 168 // the packages/ dir to appear 169 if (filename == "packages") { 170 if (watch_node(&ref, B_WATCH_DIRECTORY, this) == B_OK) 171 fWatcherRunning = true; 172 } 173 } 174 } else if (opcode == B_ENTRY_REMOVED) { 175 node_ref ref; 176 if (msg->FindInt32("device", &ref.device) != B_OK 177 || msg->FindInt64("node", &ref.node) != B_OK) 178 break; 179 180 int32 i, count = fAppList->CountItems(); 181 InfoItem* iter; 182 for (i = 0; i < count; i++) { 183 iter = static_cast<InfoItem *>(fAppList->ItemAt(i)); 184 if (iter->GetNodeRef() == ref) { 185 if (i == fAppList->CurrentSelection()) 186 fDescription->SetText(fNoPackageSelectedString); 187 fAppList->RemoveItem(i); 188 delete iter; 189 } 190 } 191 } else if (opcode == B_ENTRY_MOVED) { 192 ino_t from, to; 193 if (msg->FindInt64("from directory", &from) != B_OK 194 || msg->FindInt64("to directory", &to) != B_OK) 195 break; 196 197 BDirectory packagesDir(fToPackages.Path()); 198 node_ref ref; 199 packagesDir.GetNodeRef(&ref); 200 201 if (ref.node == to) { 202 // Package added 203 // TODO 204 } else if (ref.node == from) { 205 // Package removed 206 // TODO 207 } 208 } 209 break; 210 } 211 case P_MSG_SELECT: 212 { 213 fRemoveButton->SetEnabled(false); 214 fDescription->SetText(fNoPackageSelectedString); 215 216 int32 index = fAppList->CurrentSelection(); 217 if (index < 0) 218 break; 219 220 fprintf(stderr, "Another debug message...\n"); 221 222 InfoItem* item = dynamic_cast<InfoItem*>(fAppList->ItemAt(index)); 223 if (!item) 224 break; 225 226 fprintf(stderr, "Uh: %s and %s\n", item->GetName(), 227 item->GetVersion()); 228 229 if (fCurrentSelection.SetTo(item->GetName(), 230 item->GetVersion()) != B_OK) 231 break; 232 233 fRemoveButton->SetEnabled(true); 234 fDescription->SetText(fCurrentSelection.Description()); 235 break; 236 } 237 case P_MSG_INSTALL: 238 { 239 fOpenPanel->Show(); 240 break; 241 } 242 case P_MSG_REMOVE: 243 { 244 if (fCurrentSelection.InitCheck() != B_OK) 245 break; 246 247 int32 index = fAppList->CurrentSelection(); 248 if (index < 0) 249 break; 250 251 BAlert* notify; 252 if (fCurrentSelection.Uninstall() == B_OK) { 253 BListItem* item = fAppList->RemoveItem(index); 254 delete item; 255 256 fDescription->SetText(fNoPackageSelectedString); 257 258 notify = new BAlert("removal_success", 259 B_TRANSLATE("The package you selected has been " 260 "successfully removed from your system."), 261 B_TRANSLATE("OK")); 262 } else { 263 notify = new BAlert("removal_failed", 264 B_TRANSLATE( 265 "The selected package was not removed from your system. " 266 "The given installed package information file might have " 267 "been corrupted."), B_TRANSLATE("OK"), NULL, 268 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 269 } 270 notify->SetFlags(notify->Flags() | B_CLOSE_ON_ESCAPE); 271 notify->Go(); 272 break; 273 } 274 default: 275 BView::MessageReceived(msg); 276 break; 277 } 278 } 279 280 281 void 282 UninstallView::RefsReceived(BMessage* message) 283 { 284 static_cast<PackageInstaller*>(be_app)->RefsReceived(message); 285 } 286 287 288 void 289 UninstallView::_InitView() 290 { 291 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 292 293 fAppList = new BListView("pkg_list", B_SINGLE_SELECTION_LIST); 294 fAppList->SetSelectionMessage(new BMessage(P_MSG_SELECT)); 295 BScrollView* scrollView = new BScrollView("list_scroll", fAppList, 296 0, false, true, B_NO_BORDER); 297 298 BStringView* descriptionLabel = new BStringView("desc_label", 299 B_TRANSLATE("Package description")); 300 descriptionLabel->SetFont(be_bold_font); 301 302 fDescription = new BTextView("description", B_WILL_DRAW); 303 fDescription->MakeSelectable(false); 304 fDescription->MakeEditable(false); 305 fDescription->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 306 fDescription->SetText(fNoPackageSelectedString); 307 308 fInstallButton = new BButton("install", B_TRANSLATE("Install" B_UTF8_ELLIPSIS), 309 new BMessage(P_MSG_INSTALL)); 310 fRemoveButton = new BButton("removal", B_TRANSLATE("Remove"), 311 new BMessage(P_MSG_REMOVE)); 312 fRemoveButton->SetEnabled(false); 313 314 const float spacing = be_control_look->DefaultItemSpacing(); 315 316 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 317 .Add(scrollView, 10) 318 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) 319 .AddGroup(B_VERTICAL) 320 .SetInsets(spacing) 321 .AddGroup(B_HORIZONTAL, 0) 322 .Add(descriptionLabel) 323 .AddGlue() 324 .End() 325 .AddGroup(B_HORIZONTAL, 0) 326 .Add(BSpaceLayoutItem::CreateHorizontalStrut(10)) 327 .Add(fDescription) 328 .End() 329 .End() 330 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) 331 .AddGroup(B_HORIZONTAL) 332 .SetInsets(spacing) 333 .AddGlue() 334 .Add(fInstallButton) 335 .Add(fRemoveButton) 336 .End() 337 .End(); 338 } 339 340 341 status_t 342 UninstallView::_ReloadAppList() 343 { 344 _ClearAppList(); 345 346 if (fToPackages.InitCheck() != B_OK) 347 _CachePathToPackages(); 348 349 BDirectory dir(fToPackages.Path()); 350 status_t ret = dir.InitCheck(); 351 if (ret != B_OK) 352 return ret; 353 354 BEntry iter; 355 while (dir.GetNextEntry(&iter) == B_OK) { 356 char filename[B_FILE_NAME_LENGTH]; 357 if (iter.GetName(filename) != B_OK) 358 continue; 359 360 node_ref ref; 361 if (iter.GetNodeRef(&ref) != B_OK) 362 continue; 363 364 BString filenameString(filename); 365 if (!filenameString.IEndsWith(".pdb")) { 366 printf("Ignoring non-package '%s'\n", filename); 367 continue; 368 } 369 370 printf("Found package '%s'\n", filename); 371 _AddFile(filename, ref); 372 } 373 374 if (ret != B_ENTRY_NOT_FOUND) 375 return ret; 376 377 return B_OK; 378 } 379 380 381 void 382 UninstallView::_ClearAppList() 383 { 384 while (BListItem* item = fAppList->RemoveItem((int32)0)) 385 delete item; 386 } 387 388 389 void 390 UninstallView::_AddFile(const char* filename, const node_ref& ref) 391 { 392 BString name; 393 status_t ret = info_get_package_name(filename, name); 394 if (ret != B_OK || name.Length() == 0) 395 fprintf(stderr, "Error extracting package name: %s\n", strerror(ret)); 396 BString version; 397 ret = info_get_package_version(filename, version); 398 if (ret != B_OK || version.Length() == 0) { 399 fprintf(stderr, "Error extracting package version: %s\n", 400 strerror(ret)); 401 } 402 fAppList->AddItem(new InfoItem(name, version, filename, ref)); 403 } 404 405 406 void 407 UninstallView::_CachePathToPackages() 408 { 409 if (find_directory(B_USER_CONFIG_DIRECTORY, &fToPackages) != B_OK) 410 return; 411 if (fToPackages.Append(kPackagesDir) != B_OK) 412 return; 413 } 414 415