* Copyright 2006-2010, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include "ApplicationTypesWindow.h"
#include "FileTypes.h"
#include "FileTypesWindow.h"
#include "MimeTypeListView.h"
#include "StringView.h"
#include <AppFileInfo.h>
#include <Application.h>
#include <Bitmap.h>
#include <Box.h>
#include <Button.h>
#include <Catalog.h>
#include <ControlLook.h>
#include <LayoutBuilder.h>
#include <Locale.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <Mime.h>
#include <NodeInfo.h>
#include <Path.h>
#include <PopUpMenu.h>
#include <Query.h>
#include <Roster.h>
#include <Screen.h>
#include <ScrollView.h>
#include <StatusBar.h>
#include <StringFormat.h>
#include <StringView.h>
#include <TextView.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include <stdio.h>
#include <strings.h>
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Application Types Window"
class ProgressWindow : public BWindow {
public:
ProgressWindow(const char* message, int32 max,
volatile bool* signalQuit);
virtual ~ProgressWindow();
virtual void MessageReceived(BMessage* message);
private:
BStatusBar* fStatusBar;
BButton* fAbortButton;
volatile bool* fQuitListener;
};
const uint32 kMsgTypeSelected = 'typs';
const uint32 kMsgTypeInvoked = 'typi';
const uint32 kMsgRemoveUninstalled = 'runs';
const uint32 kMsgEdit = 'edit';
const char*
variety_to_text(uint32 variety)
{
switch (variety) {
case B_DEVELOPMENT_VERSION:
return B_TRANSLATE("Development");
case B_ALPHA_VERSION:
return B_TRANSLATE("Alpha");
case B_BETA_VERSION:
return B_TRANSLATE("Beta");
case B_GAMMA_VERSION:
return B_TRANSLATE("Gamma");
case B_GOLDEN_MASTER_VERSION:
return B_TRANSLATE("Golden master");
case B_FINAL_VERSION:
return B_TRANSLATE("Final");
}
return "-";
}
ProgressWindow::ProgressWindow(const char* message,
int32 max, volatile bool* signalQuit)
:
BWindow(BRect(0, 0, 300, 200), B_TRANSLATE("Progress"), B_MODAL_WINDOW_LOOK,
B_MODAL_SUBSET_WINDOW_FEEL, B_ASYNCHRONOUS_CONTROLS |
B_NOT_V_RESIZABLE | B_AUTO_UPDATE_SIZE_LIMITS),
fQuitListener(signalQuit)
{
char count[100];
snprintf(count, sizeof(count), "/%" B_PRId32, max);
fStatusBar = new BStatusBar("status", message, count);
fStatusBar->SetMaxValue(max);
fAbortButton = new BButton("abort", B_TRANSLATE("Abort"),
new BMessage(B_CANCEL));
float padding = be_control_look->DefaultItemSpacing();
BLayoutBuilder::Group<>(this, B_VERTICAL, padding)
.SetInsets(padding, padding, padding, padding)
.Add(fStatusBar)
.Add(fAbortButton);
BScreen screen(this);
MoveTo(screen.Frame().left + (screen.Frame().Width()
- Bounds().Width()) / 2.0f,
screen.Frame().top + (screen.Frame().Height()
- Bounds().Height()) / 2.0f);
}
ProgressWindow::~ProgressWindow()
{
}
void
ProgressWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_UPDATE_STATUS_BAR:
char count[100];
snprintf(count, sizeof(count), "%" B_PRId32,
(int32)fStatusBar->CurrentValue() + 1);
fStatusBar->Update(1, NULL, count);
break;
case B_CANCEL:
fAbortButton->SetEnabled(false);
if (fQuitListener != NULL)
*fQuitListener = true;
break;
default:
BWindow::MessageReceived(message);
break;
}
}
ApplicationTypesWindow::ApplicationTypesWindow(const BMessage& settings)
: BWindow(_Frame(settings), B_TRANSLATE("Application types"),
B_TITLED_WINDOW,
B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS)
{
float padding = be_control_look->DefaultItemSpacing();
BAlignment labelAlignment = be_control_look->DefaultLabelAlignment();
BAlignment fullWidthTopAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_TOP);
fTypeListView = new MimeTypeListView("listview", "application", true, true);
fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected));
fTypeListView->SetInvocationMessage(new BMessage(kMsgTypeInvoked));
BScrollView* scrollView = new BScrollView("scrollview", fTypeListView,
B_FRAME_EVENTS | B_WILL_DRAW, false, true);
BButton* button = new BButton("remove", B_TRANSLATE("Remove uninstalled"),
new BMessage(kMsgRemoveUninstalled));
BBox* infoBox = new BBox((char*)NULL);
infoBox->SetLabel(B_TRANSLATE("Information"));
infoBox->SetExplicitAlignment(fullWidthTopAlignment);
fNameView = new StringView(B_TRANSLATE("Name:"), NULL);
fNameView->TextView()->SetExplicitAlignment(labelAlignment);
fNameView->LabelView()->SetExplicitAlignment(labelAlignment);
fSignatureView = new StringView(B_TRANSLATE("Signature:"), NULL);
fSignatureView->TextView()->SetExplicitAlignment(labelAlignment);
fSignatureView->LabelView()->SetExplicitAlignment(labelAlignment);
fSignatureView->TextView()->SetExplicitMinSize(BSize(
fSignatureView->TextView()->StringWidth("M") * 42, B_SIZE_UNSET));
fPathView = new StringView(B_TRANSLATE("Path:"), NULL);
fPathView->TextView()->SetExplicitAlignment(labelAlignment);
fPathView->LabelView()->SetExplicitAlignment(labelAlignment);
BLayoutBuilder::Grid<>(infoBox, padding, padding)
.SetInsets(padding, padding * 2, padding, padding)
.Add(fNameView->LabelView(), 0, 0)
.Add(fNameView->TextView(), 1, 0, 2)
.Add(fSignatureView->LabelView(), 0, 1)
.Add(fSignatureView->TextView(), 1, 1, 2)
.Add(fPathView->LabelView(), 0, 2)
.Add(fPathView->TextView(), 1, 2, 2);
BBox* versionBox = new BBox("");
versionBox->SetLabel(B_TRANSLATE("Version"));
versionBox->SetExplicitAlignment(fullWidthTopAlignment);
fVersionView = new StringView(B_TRANSLATE("Version:"), NULL);
fVersionView->TextView()->SetExplicitAlignment(labelAlignment);
fVersionView->LabelView()->SetExplicitAlignment(labelAlignment);
fDescriptionLabel = new StringView(B_TRANSLATE("Description:"), NULL);
fDescriptionLabel->LabelView()->SetExplicitAlignment(labelAlignment);
fDescriptionView = new BTextView("description");
fDescriptionView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
fDescriptionView->SetLowColor(fDescriptionView->ViewColor());
fDescriptionView->MakeEditable(false);
BLayoutBuilder::Grid<>(versionBox, padding, padding)
.SetInsets(padding, padding * 2, padding, padding)
.Add(fVersionView->LabelView(), 0, 0)
.Add(fVersionView->TextView(), 1, 0)
.Add(fDescriptionLabel->LabelView(), 0, 1)
.Add(fDescriptionView, 1, 1, 2, 2);
fEditButton = new BButton(B_TRANSLATE("Edit" B_UTF8_ELLIPSIS),
new BMessage(kMsgEdit));
fLaunchButton = new BButton(B_TRANSLATE("Launch"));
fTrackerButton = new BButton(
B_TRANSLATE("Show in Tracker" B_UTF8_ELLIPSIS));
BLayoutBuilder::Group<>(this, B_HORIZONTAL, padding)
.AddGroup(B_VERTICAL, padding, 3)
.Add(scrollView)
.AddGroup(B_HORIZONTAL)
.Add(button)
.AddGlue()
.End()
.End()
.AddGroup(B_VERTICAL, padding)
.Add(infoBox)
.Add(versionBox)
.AddGroup(B_HORIZONTAL, padding)
.Add(fEditButton)
.Add(fLaunchButton)
.Add(fTrackerButton)
.AddGlue()
.End()
.AddGlue(10.0)
.End()
.SetInsets(B_USE_WINDOW_SPACING);
BMimeType::StartWatching(this);
_SetType(NULL);
}
ApplicationTypesWindow::~ApplicationTypesWindow()
{
BMimeType::StopWatching(this);
}
BRect
ApplicationTypesWindow::_Frame(const BMessage& settings) const
{
BRect rect;
if (settings.FindRect("app_types_frame", &rect) == B_OK)
return rect;
return BRect(100.0f, 100.0f, 540.0f, 480.0f);
}
void
ApplicationTypesWindow::_RemoveUninstalled()
{
int32 removed = 0;
volatile bool quit = false;
BWindow* progressWindow =
new ProgressWindow(
B_TRANSLATE("Removing uninstalled application types"),
fTypeListView->FullListCountItems(), &quit);
progressWindow->AddToSubset(this);
progressWindow->Show();
for (int32 i = fTypeListView->FullListCountItems(); i-- > 0 && !quit;) {
MimeTypeItem* item = dynamic_cast<MimeTypeItem*>
(fTypeListView->FullListItemAt(i));
progressWindow->PostMessage(B_UPDATE_STATUS_BAR);
if (item == NULL)
continue;
bool found = false;
BVolumeRoster volumeRoster;
BVolume volume;
while (volumeRoster.GetNextVolume(&volume) == B_OK) {
if (!volume.KnowsQuery())
continue;
BQuery query;
query.PushAttr("BEOS:APP_SIG");
query.PushString(item->Type());
query.PushOp(B_EQ);
query.SetVolume(&volume);
query.Fetch();
entry_ref ref;
if (query.GetNextRef(&ref) == B_OK) {
found = true;
break;
}
}
if (!found) {
BMimeType mimeType(item->Type());
mimeType.Delete();
removed++;
if (removed % 10 == 0)
UpdateIfNeeded();
}
}
progressWindow->PostMessage(B_QUIT_REQUESTED);
static BStringFormat format(B_TRANSLATE("{0, plural, "
"one{# Application type could be removed} "
"other{# Application types could be removed}}"));
BString message;
format.Format(message, removed);
error_alert(message, B_OK, B_INFO_ALERT);
}
void
ApplicationTypesWindow::_SetType(BMimeType* type, int32 forceUpdate)
{
if (type == NULL) {
fCurrentType.Unset();
fNameView->SetText(NULL);
fNameView->SetEnabled(false);
fSignatureView->SetText(NULL);
fSignatureView->SetEnabled(false);
fPathView->SetText(NULL);
fPathView->SetEnabled(false);
fVersionView->SetText(NULL);
fVersionView->SetEnabled(false);
fDescriptionView->SetText(NULL);
fDescriptionLabel->SetEnabled(true);
fEditButton->SetEnabled(false);
fLaunchButton->SetMessage(NULL);
fLaunchButton->SetEnabled(false);
fTrackerButton->SetMessage(NULL);
fTrackerButton->SetEnabled(false);
return;
}
if (fCurrentType == *type) {
if (!forceUpdate)
return;
} else
forceUpdate = B_EVERYTHING_CHANGED;
if (&fCurrentType != type)
fCurrentType.SetTo(type->Type());
fSignatureView->SetText(type->Type());
fSignatureView->SetEnabled(true);
if ((forceUpdate & B_SHORT_DESCRIPTION_CHANGED) != 0) {
char description[B_MIME_TYPE_LENGTH];
if (type->GetShortDescription(description) != B_OK) {
fNameView->SetText("");
fNameView->SetEnabled(false);
} else {
fNameView->SetText(description);
fNameView->SetEnabled(true);
}
}
if ((forceUpdate & B_APP_HINT_CHANGED) != 0) {
bool appInfoFound = false;
entry_ref ref;
if (be_roster->FindApp(fCurrentType.Type(), &ref) == B_OK) {
BMessenger tracker("application/x-vnd.Be-TRAK");
BMessage* message = new BMessage(B_REFS_RECEIVED);
message->AddRef("refs", &ref);
fLaunchButton->SetMessage(message);
fLaunchButton->SetTarget(tracker);
fLaunchButton->SetEnabled(true);
BFile file(&ref, B_READ_ONLY);
if (file.InitCheck() == B_OK) {
fEditButton->SetEnabled(true);
BAppFileInfo appInfo(&file);
version_info versionInfo;
if (appInfo.InitCheck() == B_OK
&& appInfo.GetVersionInfo(&versionInfo, B_APP_VERSION_KIND)
== B_OK) {
char version[256];
snprintf(version, sizeof(version),
"%" B_PRIu32 ".%" B_PRIu32 ".%" B_PRIu32 ", %s/%" B_PRIu32,
versionInfo.major, versionInfo.middle,
versionInfo.minor,
variety_to_text(versionInfo.variety),
versionInfo.internal);
fVersionView->SetText(version);
fVersionView->SetEnabled(true);
fDescriptionView->SetText(versionInfo.long_info);
fDescriptionLabel->SetEnabled(true);
appInfoFound = true;
}
} else {
fEditButton->SetEnabled(false);
}
} else {
fEditButton->SetEnabled(false);
fLaunchButton->SetMessage(NULL);
fLaunchButton->SetEnabled(false);
}
if (!appInfoFound) {
fVersionView->SetText(NULL);
fVersionView->SetEnabled(false);
fDescriptionView->SetText(NULL);
fDescriptionLabel->SetEnabled(false);
}
BPath path(&ref);
if (path.InitCheck() == B_OK) {
path.GetParent(&path);
fPathView->SetText(path.Path());
fPathView->SetEnabled(true);
BEntry entry(path.Path());
entry_ref directoryRef;
if (entry.GetRef(&directoryRef) == B_OK) {
BMessenger tracker("application/x-vnd.Be-TRAK");
BMessage* message = new BMessage(B_REFS_RECEIVED);
message->AddRef("refs", &directoryRef);
fTrackerButton->SetMessage(message);
fTrackerButton->SetTarget(tracker);
fTrackerButton->SetEnabled(true);
} else {
fTrackerButton->SetMessage(NULL);
fTrackerButton->SetEnabled(false);
}
} else {
fPathView->SetText(NULL);
fPathView->SetEnabled(false);
fTrackerButton->SetMessage(NULL);
fTrackerButton->SetEnabled(false);
}
}
}
void
ApplicationTypesWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case kMsgTypeSelected:
{
int32 index;
if (message->FindInt32("index", &index) == B_OK) {
MimeTypeItem* item = (MimeTypeItem*)fTypeListView->ItemAt(index);
if (item != NULL) {
BMimeType type(item->Type());
_SetType(&type);
} else
_SetType(NULL);
}
break;
}
case kMsgTypeInvoked:
{
int32 index;
if (message->FindInt32("index", &index) == B_OK) {
MimeTypeItem* item = (MimeTypeItem*)fTypeListView->ItemAt(index);
if (item != NULL) {
BMimeType type(item->Type());
entry_ref ref;
if (type.GetAppHint(&ref) == B_OK) {
BMessage refs(B_REFS_RECEIVED);
refs.AddRef("refs", &ref);
be_app->PostMessage(&refs);
}
}
}
break;
}
case kMsgEdit:
fTypeListView->Invoke();
break;
case kMsgRemoveUninstalled:
_RemoveUninstalled();
break;
case B_META_MIME_CHANGED:
{
const char* type;
int32 which;
if (message->FindString("be:type", &type) != B_OK
|| message->FindInt32("be:which", &which) != B_OK) {
break;
}
if (fCurrentType.Type() == NULL)
break;
if (!strcasecmp(fCurrentType.Type(), type)) {
if (which != B_MIME_TYPE_DELETED)
_SetType(&fCurrentType, which);
else
_SetType(NULL);
}
break;
}
default:
BWindow::MessageReceived(message);
}
}
bool
ApplicationTypesWindow::QuitRequested()
{
BMessage update(kMsgSettingsChanged);
update.AddRect("app_types_frame", Frame());
be_app_messenger.SendMessage(&update);
be_app->PostMessage(kMsgApplicationTypesWindowClosed);
return true;
}