diff --git a/src/apps/Jamfile b/src/apps/Jamfile index c27c475bf8..096eff4084 100644 --- a/src/apps/Jamfile +++ b/src/apps/Jamfile @@ -38,6 +38,7 @@ HaikuSubInclude midiplayer ; HaikuSubInclude networkstatus ; HaikuSubInclude overlayimage ; HaikuSubInclude packageinstaller ; +HaikuSubInclude packagemanager ; HaikuSubInclude pairs ; HaikuSubInclude people ; HaikuSubInclude poorman ; diff --git a/src/apps/packagemanager/ApplicationView.cpp b/src/apps/packagemanager/ApplicationView.cpp new file mode 100644 index 0000000000..814e40879f --- /dev/null +++ b/src/apps/packagemanager/ApplicationView.cpp @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * Copyright (C) 2010 Adrien Destugues + * + * Distributed under the terms of the MIT licence. + */ + +#include "ApplicationView.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class IconView : public BView { +public: + IconView(const BEntry& entry) + : + BView("Download icon", B_WILL_DRAW), + fIconBitmap(BRect(0, 0, 31, 31), 0, B_RGBA32), + fDimmedIcon(false) + { + SetDrawingMode(B_OP_OVER); + SetTo(entry); + } + + IconView() + : + BView("Download icon", B_WILL_DRAW), + fIconBitmap(BRect(0, 0, 31, 31), 0, B_RGBA32), + fDimmedIcon(false) + { + SetDrawingMode(B_OP_OVER); + memset(fIconBitmap.Bits(), 0, fIconBitmap.BitsLength()); + } + + IconView(const BMessage* archive) + : + BView("Download icon", B_WILL_DRAW), + fIconBitmap((BMessage*)archive), + fDimmedIcon(true) + { + SetDrawingMode(B_OP_OVER); + } + + void SetTo(const BEntry& entry) + { + BNode node(&entry); + BNodeInfo info(&node); + info.GetTrackerIcon(&fIconBitmap, B_LARGE_ICON); + Invalidate(); + } + + void SetIconDimmed(bool iconDimmed) + { + if (fDimmedIcon != iconDimmed) { + fDimmedIcon = iconDimmed; + Invalidate(); + } + } + + bool IsIconDimmed() const + { + return fDimmedIcon; + } + + status_t SaveSettings(BMessage* archive) + { + return fIconBitmap.Archive(archive); + } + + virtual void AttachedToWindow() + { + SetViewColor(Parent()->ViewColor()); + } + + virtual void Draw(BRect updateRect) + { + if (fDimmedIcon) { + SetDrawingMode(B_OP_ALPHA); + SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); + SetHighColor(0, 0, 0, 100); + } + DrawBitmapAsync(&fIconBitmap); + } + + virtual BSize MinSize() + { + return BSize(fIconBitmap.Bounds().Width(), fIconBitmap.Bounds().Height()); + } + + virtual BSize PreferredSize() + { + return MinSize(); + } + + virtual BSize MaxSize() + { + return MinSize(); + } + +private: + BBitmap fIconBitmap; + bool fDimmedIcon; +}; + + +class SmallButton : public BButton { +public: + SmallButton(const char* label, BMessage* message = NULL) + : + BButton(label, message) + { + BFont font; + GetFont(&font); + float size = ceilf(font.Size() * 0.8); + font.SetSize(max_c(8, size)); + SetFont(&font, B_FONT_SIZE); + } +}; + + +// #pragma mark - ApplicationView + + +ApplicationView::ApplicationView(const char* name, const char* icon, + const char* description) + : + BGroupView(B_HORIZONTAL, 8) +{ + Init(NULL); + + fNameView->SetText(name); + fInfoView->SetText(description); +} + + +ApplicationView::ApplicationView(const BMessage* archive) + : + BGroupView(B_HORIZONTAL, 8) +{ + Init(archive); + BString temp; + archive->FindString("appname",&temp); + fNameView->SetText(temp); + archive->FindString("appdesc",&temp); + float ver = 0.0; + archive->FindFloat("appver", ver); + int size = 0; + archive->FindInt32("appsize", size); + temp << " Version: " << ver << " Size: " << size; + fInfoView->SetText(temp); +} + + +bool +ApplicationView::Init(const BMessage* archive) +{ + SetViewColor(245, 245, 245); + SetFlags(Flags() | B_FULL_UPDATE_ON_RESIZE | B_WILL_DRAW); + + if (archive) { + fIconView = new IconView(archive); + } else + fIconView = new IconView(); + + if (archive) { + fTopButton = new SmallButton("Info", new BMessage('NADA')); + fBottomButton = new SmallButton("Install", new BMessage('NADA')); + } + + fInfoView = new BStringView("info view", ""); + fNameView = new BStringView("name view", ""); + + BGroupLayout* layout = GroupLayout(); + layout->SetInsets(8, 5, 5, 6); + layout->AddView(fIconView); + BView* verticalGroup = BGroupLayoutBuilder(B_VERTICAL, 3) + .Add(fNameView) + .Add(fInfoView) + .TopView() + ; + verticalGroup->SetViewColor(ViewColor()); + layout->AddView(verticalGroup); + if (fTopButton && fBottomButton) { + verticalGroup = BGroupLayoutBuilder(B_VERTICAL, 3) + .Add(fTopButton) + .Add(fBottomButton) + .TopView() + ; + } + verticalGroup->SetViewColor(ViewColor()); + layout->AddView(verticalGroup); + + BFont font; + fInfoView->GetFont(&font); + float fontSize = font.Size() * 0.8f; + font.SetSize(max_c(8.0f, fontSize)); + fInfoView->SetFont(&font, B_FONT_SIZE); + fInfoView->SetHighColor(tint_color(fInfoView->LowColor(), + B_DARKEN_4_TINT)); + fInfoView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); + + fNameView->GetFont(&font); + font.SetSize(font.Size() * 2.0f); + fNameView->SetFont(&font, B_FONT_SIZE); + fNameView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); + + return true; +} + + +void +ApplicationView::AttachedToWindow() +{ + if (fTopButton) + fTopButton->SetTarget(this); + if (fBottomButton) + fBottomButton->SetTarget(this); +} + + +void +ApplicationView::AllAttached() +{ + SetViewColor(B_TRANSPARENT_COLOR); + SetLowColor(245, 245, 245); + SetHighColor(tint_color(LowColor(), B_DARKEN_1_TINT)); +} + + +void +ApplicationView::Draw(BRect updateRect) +{ + BRect bounds(Bounds()); + bounds.bottom--; + FillRect(bounds, B_SOLID_LOW); + bounds.bottom++; + StrokeLine(bounds.LeftBottom(), bounds.RightBottom()); +} + + +void +ApplicationView::MessageReceived(BMessage* message) +{ + switch (message->what) { + case B_NODE_MONITOR: + { + int32 opCode; + if (message->FindInt32("opcode", &opCode) != B_OK) + break; + switch (opCode) { + case B_ENTRY_MOVED: + { + // Follow the entry to the new location + dev_t device; + ino_t directory; + const char* name; + if (message->FindInt32("device", + reinterpret_cast(&device)) != B_OK + || message->FindInt64("to directory", + reinterpret_cast(&directory)) != B_OK + || message->FindString("name", &name) != B_OK + || strlen(name) == 0) { + break; + } + + Window()->PostMessage(SAVE_SETTINGS); + break; + } + case B_ATTR_CHANGED: + { + fIconView->SetIconDimmed(false); + break; + } + } + break; + } + + default: + BGroupView::MessageReceived(message); + } +} + + +void +ApplicationView::ShowContextMenu(BPoint screenWhere) +{ + screenWhere += BPoint(2, 2); + + BPopUpMenu* contextMenu = new BPopUpMenu("download context"); + BMenuItem* copyURL = new BMenuItem("Copy URL to clipboard", + new BMessage('NADA')); + contextMenu->AddItem(copyURL); + BMenuItem* openFolder = new BMenuItem("Open containing folder", + new BMessage('NADA')); + contextMenu->AddItem(openFolder); + + contextMenu->SetTargetForItems(this); + contextMenu->Go(screenWhere, true, true, true); +} + diff --git a/src/apps/packagemanager/ApplicationView.h b/src/apps/packagemanager/ApplicationView.h new file mode 100644 index 0000000000..079c791d14 --- /dev/null +++ b/src/apps/packagemanager/ApplicationView.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * Copyright (C) 2010 Adrien Destugues + * + * Distributed under the terms of the MIT licence. + */ +#ifndef DOWNLOAD_PROGRESS_VIEW_H +#define DOWNLOAD_PROGRESS_VIEW_H + + +#include +#include +#include + +class BEntry; +class BStatusBar; +class BStringView; +class BWebDownload; +class IconView; +class SmallButton; + + +enum { + SAVE_SETTINGS = 'svst' +}; + + +class ApplicationView : public BGroupView { +public: + ApplicationView(const char* name, + const char* icon, + const char* description); + ApplicationView(const BMessage* archive); + + bool Init(const BMessage* archive = NULL); + + virtual void AttachedToWindow(); + virtual void AllAttached(); + + virtual void Draw(BRect updateRect); + + virtual void MessageReceived(BMessage* message); + + void ShowContextMenu(BPoint screenWhere); + +private: + IconView* fIconView; + BStringView* fNameView; + BStringView* fInfoView; + SmallButton* fTopButton; + SmallButton* fBottomButton; +}; + +#endif // DOWNLOAD_PROGRESS_VIEW_H diff --git a/src/apps/packagemanager/ApplicationWindow.cpp b/src/apps/packagemanager/ApplicationWindow.cpp new file mode 100644 index 0000000000..d9a1f155a5 --- /dev/null +++ b/src/apps/packagemanager/ApplicationWindow.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * Copyright (C) 2010 Adrien Destugues + * + * Distributed under the terms of the MIT licence. + */ + +#include "ApplicationWindow.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ApplicationView.h" + + +enum { + INIT = 'init', +}; + + +class ApplicationsContainerView : public BGroupView { +public: + ApplicationsContainerView() + : + BGroupView(B_VERTICAL, 0.0) + { + SetFlags(Flags() | B_PULSE_NEEDED); + SetViewColor(245, 245, 245); + AddChild(BSpaceLayoutItem::CreateGlue()); + } + + virtual BSize MinSize() + { + BSize minSize = BGroupView::MinSize(); + return BSize(minSize.width, 80); + } + +protected: + virtual void DoLayout() + { + BGroupView::DoLayout(); + if (BScrollBar* scrollBar = ScrollBar(B_VERTICAL)) { + BSize minSize = BGroupView::MinSize(); + float height = Bounds().Height(); + float max = minSize.height - height; + scrollBar->SetRange(0, max); + if (minSize.height > 0) + scrollBar->SetProportion(height / minSize.height); + else + scrollBar->SetProportion(1); + } + } +}; + + +class ApplicationContainerScrollView : public BScrollView { +public: + ApplicationContainerScrollView(BView* target) + : + BScrollView("Applications scroll view", target, 0, false, true, + B_NO_BORDER) + { + } + +protected: + virtual void DoLayout() + { + BScrollView::DoLayout(); + // Tweak scroll bar layout to hide part of the frame for better looks. + BScrollBar* scrollBar = ScrollBar(B_VERTICAL); + scrollBar->MoveBy(1, -1); + scrollBar->ResizeBy(0, 2); + Target()->ResizeBy(1, 0); + // Set the scroll steps + if (BView* item = Target()->ChildAt(0)) { + scrollBar->SetSteps(item->MinSize().height + 1, + item->MinSize().height + 1); + } + } +}; + + +// #pragma mark - + + +ApplicationWindow::ApplicationWindow(BRect frame, bool visible) + : BWindow(frame, "Package Manager", + B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, + B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE), + fMinimizeOnClose(false) +{ + SetPulseRate(1000000); + + SetLayout(new BGroupLayout(B_VERTICAL, 0.0)); + + ApplicationsContainerView* downloadsGroupView = new ApplicationsContainerView(); + fApplicationViewsLayout = downloadsGroupView->GroupLayout(); + + BMenuBar* menuBar = new BMenuBar("Menu bar"); + BMenu* menu = new BMenu("Actions"); + menu->AddItem(new BMenuItem("Apply changes", + new BMessage('NADA'))); + menu->AddItem(new BMenuItem("Exit", new BMessage(B_QUIT_REQUESTED))); + menuBar->AddItem(menu); + + fApplicationsScrollView = new ApplicationContainerScrollView(downloadsGroupView); + + fDiscardButton = new BButton("Revert", + new BMessage('NADA')); + fDiscardButton->SetEnabled(false); + + fApplyChangesButton = new BButton("Apply", + new BMessage('NADA')); + fApplyChangesButton->SetEnabled(false); + + const float spacing = be_control_look->DefaultItemSpacing(); + + AddChild(BGroupLayoutBuilder(B_VERTICAL, 0.0) + .Add(menuBar) + .Add(fApplicationsScrollView) + .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) + .Add(BGroupLayoutBuilder(B_HORIZONTAL, spacing) + .AddGlue() + .Add(fApplyChangesButton) + .Add(fDiscardButton) + .SetInsets(12, 5, 12, 5) + ) + ); + + PostMessage(INIT); + + if (!visible) + Hide(); + Show(); +} + + +ApplicationWindow::~ApplicationWindow() +{ +} + + +void +ApplicationWindow::DispatchMessage(BMessage* message, BHandler* target) +{ + // We need to intercept mouse down events inside the area of download + // progress views (regardless of whether they have children at the click), + // so that they may display a context menu. + BPoint where; + int32 buttons; + if (message->what == B_MOUSE_DOWN + && message->FindPoint("screen_where", &where) == B_OK + && message->FindInt32("buttons", &buttons) == B_OK + && (buttons & B_SECONDARY_MOUSE_BUTTON) != 0) { + for (int32 i = fApplicationViewsLayout->CountItems() - 1; + BLayoutItem* item = fApplicationViewsLayout->ItemAt(i); i--) { + ApplicationView* view = dynamic_cast( + item->View()); + if (!view) + continue; + BPoint viewWhere(where); + view->ConvertFromScreen(&viewWhere); + if (view->Bounds().Contains(viewWhere)) { + view->ShowContextMenu(where); + return; + } + } + } + BWindow::DispatchMessage(message, target); +} + + +void +ApplicationWindow::MessageReceived(BMessage* message) +{ + switch (message->what) { + case INIT: + { + break; + } + + default: + BWindow::MessageReceived(message); + break; + } +} + + +bool +ApplicationWindow::QuitRequested() +{ + if (fMinimizeOnClose) { + if (!IsMinimized()) + Minimize(true); + } else { + if (!IsHidden()) + Hide(); + } + return false; +} + + +void +ApplicationWindow::SetMinimizeOnClose(bool minimize) +{ + if (Lock()) { + fMinimizeOnClose = minimize; + Unlock(); + } +} + + +// #pragma mark - private + + +void +ApplicationWindow::AddCategory(const char* name, const char* icon, + const char* description) +{ + ApplicationView* category = new ApplicationView(name, icon, description); + fApplicationViewsLayout->AddView(0, category); +} + + +void +ApplicationWindow::AddApplication(const BMessage* info) +{ + ApplicationView* app = new ApplicationView(info); + fApplicationViewsLayout->AddView(0, app); +} + + +void +ApplicationWindow::_ValidateButtonStatus() +{ + int32 finishedCount = 0; + int32 missingCount = 0; + for (int32 i = fApplicationViewsLayout->CountItems() - 1; + BLayoutItem* item = fApplicationViewsLayout->ItemAt(i); i--) { + ApplicationView* view = dynamic_cast( + item->View()); + if (!view) + continue; + } + fDiscardButton->SetEnabled(finishedCount > 0); + fApplyChangesButton->SetEnabled(missingCount > 0); +} + diff --git a/src/apps/packagemanager/ApplicationWindow.h b/src/apps/packagemanager/ApplicationWindow.h new file mode 100644 index 0000000000..df1e7fd4ee --- /dev/null +++ b/src/apps/packagemanager/ApplicationWindow.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 Stephan Aßmus + * Copyright (C) 2010 Adrien Destugues + * + * Distributed under the terms of the MIT Licence. + */ +#ifndef APPLICATION_WINDOW_H +#define APPLICATION_WINDOW_H + + +#include +#include + +class BButton; +class BFile; +class BGroupLayout; +class BScrollView; +class BWebApplication; +class SettingsMessage; + + +class ApplicationWindow : public BWindow { +public: + ApplicationWindow(BRect frame, bool visible); + virtual ~ApplicationWindow(); + + virtual void DispatchMessage(BMessage* message, + BHandler* target); + virtual void MessageReceived(BMessage* message); + virtual bool QuitRequested(); + + void SetMinimizeOnClose(bool minimize); + + void AddCategory(const char* name, + const char* icon, + const char* description); + void AddApplication(const BMessage* info); + +private: + void _ValidateButtonStatus(); + +private: + BScrollView* fApplicationsScrollView; + BGroupLayout* fApplicationViewsLayout; + BButton* fDiscardButton; + BButton* fApplyChangesButton; + BString fApplicationPath; + bool fMinimizeOnClose; +}; + +#endif // APPLICATION_WINDOW_H diff --git a/src/apps/packagemanager/Jamfile b/src/apps/packagemanager/Jamfile new file mode 100644 index 0000000000..a099d6aace --- /dev/null +++ b/src/apps/packagemanager/Jamfile @@ -0,0 +1,15 @@ +SubDir HAIKU_TOP src apps packagemanager ; + +# UsePrivateHeaders shared interface ; +# SubDirHdrs $(HAIKU_TOP) headers libs zlib ; + +#SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits interface ] ; + +Application PackageManager : + ApplicationView.cpp + ApplicationWindow.cpp + PackageManager.cpp + : be $(HAIKU_LOCALE_LIBS) translation $(TARGET_LIBSUPC++) + : PackageManager.rdef +; + diff --git a/src/apps/packagemanager/PackageManager.cpp b/src/apps/packagemanager/PackageManager.cpp new file mode 100644 index 0000000000..242f10babc --- /dev/null +++ b/src/apps/packagemanager/PackageManager.cpp @@ -0,0 +1,144 @@ +/* + * Copyright 2010, Haiku, Inc. + * Distributed under the terms of the MIT license. + * + * Author: + * Adrien Destugues + */ + + +#include "ApplicationWindow.h" + +#include +#include +#include +#include +#include +#include + + +#undef B_TRANSLATE_CONTEXT +#define B_TRANSLATE_CONTEXT "main" + + +class PackageManager : public BApplication { + public: + PackageManager(); + ~PackageManager(); + + void RefsReceived(BMessage *msg); + void ArgvReceived(int32 argc, char **argv); + void ReadyToRun(); + + void MessageReceived(BMessage *msg); + + void AboutRequested(); + + private: + ApplicationWindow* fMainWindow; +}; + + +PackageManager::PackageManager() + : BApplication("application/x-vnd.Haiku-PackageManager") +{ +} + + +PackageManager::~PackageManager() +{ +} + + +void +PackageManager::ReadyToRun() +{ + // Open the main window + BRect frame(20, 40, 600, 400); + fMainWindow = new ApplicationWindow(frame, true); + + // Add some test content to the window + fMainWindow->AddCategory("Test category", "development", + "Thisis a short description of the category"); + + BMessage entryInfo; + // This message is a convenient way of storing various infos about an app. + // What's inside : + // icon as an archived BBitmap + entryInfo.AddString("appname", "Test Application"); + entryInfo.AddString("appdesc", "Some text telling what it does"); + entryInfo.AddFloat("appver", 1.302); + // as a float so it can be compared to decide if there is an update + entryInfo.AddInt32("appsize", 123456); // this is in bytes + + fMainWindow->AddApplication(&entryInfo); + + fMainWindow->Show(); +} + + +void +PackageManager::RefsReceived(BMessage *msg) +{ + uint32 type; + int32 i, count; + status_t ret = msg->GetInfo("refs", &type, &count); + if (ret != B_OK || type != B_REF_TYPE) + return; + + entry_ref ref; + for (i = 0; i < count; i++) { + if (msg->FindRef("refs", i, &ref) == B_OK) { + // TODO: handle bundle-files for installation + } + } +} + + +void +PackageManager::ArgvReceived(int32 argc, char **argv) +{ + // TODO: handle command-line driven actions +} + + +void +PackageManager::MessageReceived(BMessage *msg) +{ + switch (msg->what) { + default: + BApplication::MessageReceived(msg); + } +} + + +void +PackageManager::AboutRequested() +{ + BAlert *about = new BAlert("about", + B_TRANSLATE("PackageManager\n" + "Written by Adrien Destugues\n" + "Copyright 2010 Haiku, Inc. \n"), + B_TRANSLATE("OK")); + + BTextView *view = about->TextView(); + BFont font; + view->SetStylable(true); + view->GetFont(&font); + font.SetFace(B_BOLD_FACE); + font.SetSize(font.Size() * 1.5); + view->SetFontAndColor(0, 17, &font); + + about->Go(); +} + + +int +main(void) +{ + PackageManager app; + app.Run(); + + return 0; +} + diff --git a/src/apps/packagemanager/PackageManager.rdef b/src/apps/packagemanager/PackageManager.rdef new file mode 100644 index 0000000000..6ca691d056 --- /dev/null +++ b/src/apps/packagemanager/PackageManager.rdef @@ -0,0 +1,26 @@ +resource app_signature "application/x-vnd.Haiku-PackageManager"; +resource app_flags B_SINGLE_LAUNCH; + +resource vector_icon { + $"6E6369660C04006605000200060237D82B3BACE0BC056F381D3E4AB7654A7200" + $"00D6DEEEFFA2ACBF0200060237D82B3BACE0BC056F381D3E4AB7654A720000A1" + $"A7B3FF7A849903AAB0BA02000602BC42C0BAFFBEBB0A953C4AEB4A9136468CBF" + $"0080858EFF5C6066020006023B966D3B95A9BAFE123AFEB54997F949D8D800FF" + $"ED00FFFFB80002000602B885D93BCB6FBAC787B7B21C4B7A9C4A838200FFB800" + $"FFDC7D0B020006023C2D5037C5C6B380243805214A178B4603F200FEFFEAFFFF" + $"FF7905D905FF020002023AE951B9F63F3E400B3F4E9C462C4147F2CA0000A2FF" + $"00FF2BCD05FF130A043F5AC154CBA22C52274E0A07C6CCBF4A5049C334C6414C" + $"52495C57575E450A03254A284D383D0A083C25262D2245383D274E3F5A504954" + $"310A0A3D4640413C3F33453248284D274E3F5AC334C6413E4B0A05324833453C" + $"3F383D284D0A0540413D463E4BC334C64150490A0340413C253C3F0A033C2538" + $"3D3C3F0A04504954313C2540410A043C25262D2245383D0A043F5A3B4A233E27" + $"4E0A064B3E474244534756504954310A063C252D292E2D4036453554310A0450" + $"4947564B3E54310A044756445347424B3E0A043C252D29453554310A04453540" + $"362E2D2D2906069F0E24382438263434302C30343038402C3420342834282428" + $"150A0001001001158402040A0001011001158102040A0001021001158402040A" + $"0101031001178302040A020104000A030105000A030106000A040107000A0501" + $"08000A050109000A05010A000A01010B1001178302040A06010B000A01010C10" + $"01178302040A01010D1001178302040A07010E000A09010F000A080110000A0A" + $"0111000A0B011202BE1E0DBF64C43F64C4BE1E0D46D63B4A99F90A01011212BE" + $"1E0DBF64C43F64C4BE1E0D46D63B4A99F90117820004" +};