/* * Copyright (c) 2007-2010, Haiku, Inc. * Distributed under the terms of the MIT license. * * Author: * Ɓukasz 'Sil2100' Zemczak */ #include "UninstallView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "UninstallView" enum { P_MSG_INSTALL = 'umin', P_MSG_REMOVE = 'umrm', P_MSG_SELECT }; // TODO list: // - B_ENTRY_MOVED // - Right now the installed package info naming convention is the same // as at SoftwareValet. Maybe there would be a better one? // - Add a status window (reuse the one from PackageInstall) class UninstallView::InfoItem : public BStringItem { public: InfoItem(const BString& name, const BString& version, const char* filename, const node_ref& ref) : BStringItem(name.String()), fName(name), fVersion(version), fNodeRef(ref) { if (fName.Length() == 0) SetText(filename); } const char* GetName() { return fName.String(); } const char* GetVersion() { return fVersion.String(); }; node_ref GetNodeRef() { return fNodeRef; }; private: BString fName; BString fVersion; node_ref fNodeRef; }; UninstallView::UninstallView() : BGroupView(B_VERTICAL), fOpenPanel(new BFilePanel(B_OPEN_PANEL)) { fNoPackageSelectedString = B_TRANSLATE("No package selected."); _InitView(); } UninstallView::~UninstallView() { // Stop all node watching stop_watching(this); } void UninstallView::AttachedToWindow() { fAppList->SetTarget(this); fInstallButton->SetTarget(this); fRemoveButton->SetTarget(this); _ReloadAppList(); // We loaded the list, but now let's set up a node watcher for the packages // directory, so that we can update the list of installed packages in real // time _CachePathToPackages(); node_ref ref; fWatcherRunning = false; BDirectory dir(fToPackages.Path()); if (dir.InitCheck() != B_OK) { // The packages/ directory obviously does not exist. // Since this is the case, we need to watch for it to appear first BPath path; fToPackages.GetParent(&path); if (dir.SetTo(path.Path()) != B_OK) return; } else fWatcherRunning = true; dir.GetNodeRef(&ref); if (watch_node(&ref, B_WATCH_DIRECTORY, this) != B_OK) { fWatcherRunning = false; return; } } void UninstallView::MessageReceived(BMessage* msg) { switch (msg->what) { case B_NODE_MONITOR: { int32 opcode; if (msg->FindInt32("opcode", &opcode) != B_OK) break; fprintf(stderr, "Got an opcoded node monitor message\n"); if (opcode == B_ENTRY_CREATED) { fprintf(stderr, "Created?...\n"); BString filename, name, version; node_ref ref; if (msg->FindString("name", &filename) != B_OK || msg->FindInt32("device", &ref.device) != B_OK || msg->FindInt64("node", &ref.node) != B_OK) break; // TODO: This obviously is a hack // The node watcher informs the view a bit to early, and // because of this the data of the node is not ready at this // moment. For this reason, we must give the filesystem some // time before continuing. usleep(10000); if (fWatcherRunning) { _AddFile(filename.String(), ref); } else { // This most likely means we were waiting for // the packages/ dir to appear if (filename == "packages") { if (watch_node(&ref, B_WATCH_DIRECTORY, this) == B_OK) fWatcherRunning = true; } } } else if (opcode == B_ENTRY_REMOVED) { node_ref ref; if (msg->FindInt32("device", &ref.device) != B_OK || msg->FindInt64("node", &ref.node) != B_OK) break; int32 i, count = fAppList->CountItems(); InfoItem* iter; for (i = 0; i < count; i++) { iter = static_cast(fAppList->ItemAt(i)); if (iter->GetNodeRef() == ref) { if (i == fAppList->CurrentSelection()) fDescription->SetText(fNoPackageSelectedString); fAppList->RemoveItem(i); delete iter; } } } else if (opcode == B_ENTRY_MOVED) { ino_t from, to; if (msg->FindInt64("from directory", &from) != B_OK || msg->FindInt64("to directory", &to) != B_OK) break; BDirectory packagesDir(fToPackages.Path()); node_ref ref; packagesDir.GetNodeRef(&ref); if (ref.node == to) { // Package added // TODO } else if (ref.node == from) { // Package removed // TODO } } break; } case P_MSG_SELECT: { fRemoveButton->SetEnabled(false); fDescription->SetText(fNoPackageSelectedString); int32 index = fAppList->CurrentSelection(); if (index < 0) break; fprintf(stderr, "Another debug message...\n"); InfoItem* item = dynamic_cast(fAppList->ItemAt(index)); if (!item) break; fprintf(stderr, "Uh: %s and %s\n", item->GetName(), item->GetVersion()); if (fCurrentSelection.SetTo(item->GetName(), item->GetVersion()) != B_OK) break; fRemoveButton->SetEnabled(true); fDescription->SetText(fCurrentSelection.Description()); break; } case P_MSG_INSTALL: { fOpenPanel->Show(); break; } case P_MSG_REMOVE: { if (fCurrentSelection.InitCheck() != B_OK) break; int32 index = fAppList->CurrentSelection(); if (index < 0) break; BAlert* notify; if (fCurrentSelection.Uninstall() == B_OK) { BListItem* item = fAppList->RemoveItem(index); delete item; fDescription->SetText(fNoPackageSelectedString); notify = new BAlert("removal_success", B_TRANSLATE("The package you selected has been " "successfully removed from your system."), B_TRANSLATE("OK")); } else { notify = new BAlert("removal_failed", B_TRANSLATE( "The selected package was not removed from your system. " "The given installed package information file might have " "been corrupted."), B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); } notify->SetFlags(notify->Flags() | B_CLOSE_ON_ESCAPE); notify->Go(); break; } default: BView::MessageReceived(msg); break; } } void UninstallView::RefsReceived(BMessage* message) { static_cast(be_app)->RefsReceived(message); } void UninstallView::_InitView() { SetViewUIColor(B_PANEL_BACKGROUND_COLOR); fAppList = new BListView("pkg_list", B_SINGLE_SELECTION_LIST); fAppList->SetSelectionMessage(new BMessage(P_MSG_SELECT)); BScrollView* scrollView = new BScrollView("list_scroll", fAppList, 0, false, true, B_NO_BORDER); BStringView* descriptionLabel = new BStringView("desc_label", B_TRANSLATE("Package description")); descriptionLabel->SetFont(be_bold_font); fDescription = new BTextView("description", B_WILL_DRAW); fDescription->MakeSelectable(false); fDescription->MakeEditable(false); fDescription->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); fDescription->SetText(fNoPackageSelectedString); fInstallButton = new BButton("install", B_TRANSLATE("Install" B_UTF8_ELLIPSIS), new BMessage(P_MSG_INSTALL)); fRemoveButton = new BButton("removal", B_TRANSLATE("Remove"), new BMessage(P_MSG_REMOVE)); fRemoveButton->SetEnabled(false); const float spacing = be_control_look->DefaultItemSpacing(); BLayoutBuilder::Group<>(this, B_VERTICAL, 0) .Add(scrollView, 10) .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) .AddGroup(B_VERTICAL) .SetInsets(spacing) .AddGroup(B_HORIZONTAL, 0) .Add(descriptionLabel) .AddGlue() .End() .AddGroup(B_HORIZONTAL, 0) .Add(BSpaceLayoutItem::CreateHorizontalStrut(10)) .Add(fDescription) .End() .End() .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) .AddGroup(B_HORIZONTAL) .SetInsets(spacing) .AddGlue() .Add(fInstallButton) .Add(fRemoveButton) .End() .End(); } status_t UninstallView::_ReloadAppList() { _ClearAppList(); if (fToPackages.InitCheck() != B_OK) _CachePathToPackages(); BDirectory dir(fToPackages.Path()); status_t ret = dir.InitCheck(); if (ret != B_OK) return ret; BEntry iter; while (dir.GetNextEntry(&iter) == B_OK) { char filename[B_FILE_NAME_LENGTH]; if (iter.GetName(filename) != B_OK) continue; node_ref ref; if (iter.GetNodeRef(&ref) != B_OK) continue; BString filenameString(filename); if (!filenameString.IEndsWith(".pdb")) { printf("Ignoring non-package '%s'\n", filename); continue; } printf("Found package '%s'\n", filename); _AddFile(filename, ref); } if (ret != B_ENTRY_NOT_FOUND) return ret; return B_OK; } void UninstallView::_ClearAppList() { while (BListItem* item = fAppList->RemoveItem((int32)0)) delete item; } void UninstallView::_AddFile(const char* filename, const node_ref& ref) { BString name; status_t ret = info_get_package_name(filename, name); if (ret != B_OK || name.Length() == 0) fprintf(stderr, "Error extracting package name: %s\n", strerror(ret)); BString version; ret = info_get_package_version(filename, version); if (ret != B_OK || version.Length() == 0) { fprintf(stderr, "Error extracting package version: %s\n", strerror(ret)); } fAppList->AddItem(new InfoItem(name, version, filename, ref)); } void UninstallView::_CachePathToPackages() { if (find_directory(B_USER_CONFIG_DIRECTORY, &fToPackages) != B_OK) return; if (fToPackages.Append(kPackagesDir) != B_OK) return; }