/*
* Copyright (c) 2007-2010, Haiku, Inc.
* Distributed under the terms of the MIT license.
*
* Author:
* Ćukasz 'Sil2100' Zemczak <sil2100@vexillium.org>
*/
#include "UninstallView.h"
#include <stdio.h>
#include <string.h>
#include <Alert.h>
#include <Box.h>
#include <Button.h>
#include <Catalog.h>
#include <ControlLook.h>
#include <Directory.h>
#include <Entry.h>
#include <File.h>
#include <FilePanel.h>
#include <FindDirectory.h>
#include <LayoutBuilder.h>
#include <ListView.h>
#include <Locale.h>
#include <NodeMonitor.h>
#include <ScrollView.h>
#include <SeparatorView.h>
#include <String.h>
#include <StringView.h>
#include <SpaceLayoutItem.h>
#include <TextView.h>
#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<InfoItem *>(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<InfoItem*>(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<PackageInstaller*>(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;
}
↑ V547 Expression is always true.