From 25648ff02029112c6152fbbc43949cc81b195adf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Sat, 9 Jun 2007 12:01:39 +0000 Subject: [PATCH] * added LaunchBox, an application launcher with drag&drop support * it has been rewritten from using liblayout to use the new Haiku layout framework * TODO: it should come with default settings * TODO: the minimum window size is not yet set by the layout framework (?) git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21373 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/Jamfile | 1 + src/apps/launchbox/App.cpp | 110 ++++ src/apps/launchbox/App.h | 29 ++ src/apps/launchbox/IconButton.cpp | 760 ++++++++++++++++++++++++++++ src/apps/launchbox/IconButton.h | 129 +++++ src/apps/launchbox/Jamfile | 22 + src/apps/launchbox/LaunchBox.rdef | 55 ++ src/apps/launchbox/LaunchButton.cpp | 348 +++++++++++++ src/apps/launchbox/LaunchButton.h | 70 +++ src/apps/launchbox/MainWindow.cpp | 475 +++++++++++++++++ src/apps/launchbox/MainWindow.h | 79 +++ src/apps/launchbox/NamePanel.cpp | 149 ++++++ src/apps/launchbox/NamePanel.h | 39 ++ src/apps/launchbox/PadView.cpp | 350 +++++++++++++ src/apps/launchbox/PadView.h | 44 ++ src/apps/launchbox/Panel.cpp | 86 ++++ src/apps/launchbox/Panel.h | 37 ++ src/apps/launchbox/main.cpp | 21 + src/apps/launchbox/run | 18 + src/apps/launchbox/support.cpp | 115 +++++ src/apps/launchbox/support.h | 32 ++ 21 files changed, 2969 insertions(+) create mode 100644 src/apps/launchbox/App.cpp create mode 100644 src/apps/launchbox/App.h create mode 100644 src/apps/launchbox/IconButton.cpp create mode 100644 src/apps/launchbox/IconButton.h create mode 100644 src/apps/launchbox/Jamfile create mode 100644 src/apps/launchbox/LaunchBox.rdef create mode 100644 src/apps/launchbox/LaunchButton.cpp create mode 100644 src/apps/launchbox/LaunchButton.h create mode 100644 src/apps/launchbox/MainWindow.cpp create mode 100644 src/apps/launchbox/MainWindow.h create mode 100644 src/apps/launchbox/NamePanel.cpp create mode 100644 src/apps/launchbox/NamePanel.h create mode 100644 src/apps/launchbox/PadView.cpp create mode 100644 src/apps/launchbox/PadView.h create mode 100644 src/apps/launchbox/Panel.cpp create mode 100644 src/apps/launchbox/Panel.h create mode 100644 src/apps/launchbox/main.cpp create mode 100755 src/apps/launchbox/run create mode 100644 src/apps/launchbox/support.cpp create mode 100644 src/apps/launchbox/support.h diff --git a/src/apps/Jamfile b/src/apps/Jamfile index 18d1dba155..b23e641514 100644 --- a/src/apps/Jamfile +++ b/src/apps/Jamfile @@ -13,6 +13,7 @@ SubInclude HAIKU_TOP src apps fontdemo ; SubInclude HAIKU_TOP src apps glteapot ; SubInclude HAIKU_TOP src apps icon-o-matic ; SubInclude HAIKU_TOP src apps installer ; +SubInclude HAIKU_TOP src apps launchbox ; SubInclude HAIKU_TOP src apps magnify ; SubInclude HAIKU_TOP src apps mail ; SubInclude HAIKU_TOP src apps mandelbrot ; diff --git a/src/apps/launchbox/App.cpp b/src/apps/launchbox/App.cpp new file mode 100644 index 0000000000..9eef31b166 --- /dev/null +++ b/src/apps/launchbox/App.cpp @@ -0,0 +1,110 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include + +#include +#include +#include +#include + +#include "support.h" + +#include "MainWindow.h" + +#include "App.h" + +// constructor +App::App() + : BApplication("application/x.vnd-YellowBites.LaunchBox") +{ +} + +// destructor +App::~App() +{ +} + +// QuitRequested +bool +App::QuitRequested() +{ + BMessage settings('sett'); + for (int32 i = 0; BWindow* window = WindowAt(i); i++) { + if (MainWindow* padWindow = dynamic_cast(window)) { + BMessage* windowSettings = padWindow->Settings(); + if (windowSettings && padWindow->Lock()) { + padWindow->SaveSettings(windowSettings); + padWindow->Unlock(); + settings.AddMessage("window", windowSettings); + } + } + } + save_settings(&settings, "main_settings", "LaunchBox"); + return true; +} + +// ReadyToRun +void +App::ReadyToRun() +{ + bool windowAdded = false; + BRect frame(50.0, 50.0, 65.0, 100.0); + + BMessage settings('sett'); + status_t status = load_settings(&settings, "main_settings", "LaunchBox"); + if (status >= B_OK) { + BMessage windowMessage; + for (int32 i = 0; settings.FindMessage("window", i, &windowMessage) >= B_OK; i++) { + BString name("Pad "); + name << i + 1; + BMessage* windowSettings = new BMessage(windowMessage); + MainWindow* window = new MainWindow(name.String(), frame, windowSettings); + window->Show(); + windowAdded = true; + frame.OffsetBy(10.0, 10.0); + windowMessage.MakeEmpty(); + } + } + + if (!windowAdded) { + MainWindow* window = new MainWindow("Pad 1", frame); + window->Show(); + } +} + +// MessageReceived +void +App::MessageReceived(BMessage* message) +{ + switch (message->what) { + case MSG_ADD_WINDOW: { + BMessage* settings = new BMessage('sett'); + message->FindMessage("window", settings); + BString name("Pad "); + name << CountWindows() + 1; + MainWindow* window = new MainWindow(name.String(), + BRect(50.0, 50.0, 65.0, 100.0), settings); + window->Show(); + break; + } + default: + BApplication::MessageReceived(message); + break; + } +} + +// AboutRequested +void +App::AboutRequested() +{ + (new BAlert("about", "LaunchBox by stippi\n\n" + "for bonefish\n\n\n" + "v1.1.0", + "Neat", NULL, NULL))->Go(NULL); +} diff --git a/src/apps/launchbox/App.h b/src/apps/launchbox/App.h new file mode 100644 index 0000000000..7b59f0dfcf --- /dev/null +++ b/src/apps/launchbox/App.h @@ -0,0 +1,29 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ +#ifndef APP_H +#define APP_H + +#include +#include + +class MainWindow; + +class App : public BApplication { + public: + App(); + virtual ~App(); + + virtual bool QuitRequested(); + virtual void ReadyToRun(); + virtual void MessageReceived(BMessage* message); + virtual void AboutRequested(); + + private: +}; + +#endif // APP_H diff --git a/src/apps/launchbox/IconButton.cpp b/src/apps/launchbox/IconButton.cpp new file mode 100644 index 0000000000..d09c6b16e9 --- /dev/null +++ b/src/apps/launchbox/IconButton.cpp @@ -0,0 +1,760 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +// NOTE: this file is a duplicate of the version in Icon-O-Matic/generic +// it should be placed into a common folder for generic useful stuff + +#include "IconButton.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using std::nothrow; + +// constructor +IconButton::IconButton(const char* name, uint32 id, const char* label, + BMessage* message, BHandler* target) + : BView(BRect(0.0, 0.0, 10.0, 10.0), name, B_FOLLOW_NONE, B_WILL_DRAW), + BInvoker(message, target), + fButtonState(STATE_ENABLED), + fID(id), + fNormalBitmap(NULL), + fDisabledBitmap(NULL), + fClickedBitmap(NULL), + fDisabledClickedBitmap(NULL), + fLabel(label), + fTargetCache(target) +{ + SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + SetViewColor(B_TRANSPARENT_32_BIT); +} + +// destructor +IconButton::~IconButton() +{ + _DeleteBitmaps(); +} + +// MessageReceived +void +IconButton::MessageReceived(BMessage* message) +{ + switch (message->what) { + default: + BView::MessageReceived(message); + break; + } +} + +// AttachedToWindow +void +IconButton::AttachedToWindow() +{ + SetTarget(fTargetCache); + if (!Target()) { + SetTarget(Window()); + } +} + +// Draw +void +IconButton::Draw(BRect area) +{ + rgb_color background = LowColor(); + if (BView* parent = Parent()) + background = parent->ViewColor(); + rgb_color lightShadow, shadow, darkShadow, light; + BRect r(Bounds()); + BBitmap* bitmap = fNormalBitmap; + // adjust colors and bitmap according to flags + if (IsEnabled()) { + lightShadow = tint_color(background, B_DARKEN_1_TINT); + shadow = tint_color(background, B_DARKEN_2_TINT); + darkShadow = tint_color(background, B_DARKEN_4_TINT); + light = tint_color(background, B_LIGHTEN_MAX_TINT); + SetHighColor(0, 0, 0, 255); + } else { + lightShadow = tint_color(background, 1.11); + shadow = tint_color(background, B_DARKEN_1_TINT); + darkShadow = tint_color(background, B_DARKEN_2_TINT); + light = tint_color(background, B_LIGHTEN_2_TINT); + bitmap = fDisabledBitmap; + SetHighColor(tint_color(background, B_DISABLED_LABEL_TINT)); + } + if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED)) { + if (IsEnabled()) { +// background = tint_color(background, B_DARKEN_2_TINT); +// background = tint_color(background, B_LIGHTEN_1_TINT); + background = tint_color(background, B_DARKEN_1_TINT); + bitmap = fClickedBitmap; + } else { +// background = tint_color(background, B_DARKEN_1_TINT); +// background = tint_color(background, (B_NO_TINT + B_LIGHTEN_1_TINT) / 2.0); + background = tint_color(background, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0); + bitmap = fDisabledClickedBitmap; + } + // background + SetLowColor(background); + r.InsetBy(2.0, 2.0); + StrokeLine(r.LeftBottom(), r.LeftTop(), B_SOLID_LOW); + StrokeLine(r.LeftTop(), r.RightTop(), B_SOLID_LOW); + r.InsetBy(-2.0, -2.0); + } + // draw frame only if tracking + if (DrawBorder()) { + if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED)) + DrawPressedBorder(r, background, shadow, darkShadow, lightShadow, light); + else + DrawNormalBorder(r, background, shadow, darkShadow, lightShadow, light); + r.InsetBy(2.0, 2.0); + } else + _DrawFrame(r, background, background, background, background); + float width = Bounds().Width(); + float height = Bounds().Height(); + // bitmap + BRegion originalClippingRegion; + if (bitmap && bitmap->IsValid()) { + float x = floorf((width - bitmap->Bounds().Width()) / 2.0 + 0.5); + float y = floorf((height - bitmap->Bounds().Height()) / 2.0 + 0.5); + BPoint point(x, y); + if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED)) + point += BPoint(1.0, 1.0); + if (bitmap->ColorSpace() == B_RGBA32 || bitmap->ColorSpace() == B_RGBA32_BIG) { + FillRect(r, B_SOLID_LOW); + SetDrawingMode(B_OP_ALPHA); + SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); + } + DrawBitmap(bitmap, point); + // constrain clipping region + BRegion region= originalClippingRegion; + GetClippingRegion(®ion); + region.Exclude(bitmap->Bounds().OffsetByCopy(point)); + ConstrainClippingRegion(®ion); + } + // background + SetDrawingMode(B_OP_COPY); + FillRect(r, B_SOLID_LOW); + ConstrainClippingRegion(&originalClippingRegion); + // label + if (fLabel.CountChars() > 0) { + SetDrawingMode(B_OP_COPY); + font_height fh; + GetFontHeight(&fh); + float y = Bounds().bottom - 4.0; + y -= fh.descent; + float x = (width - StringWidth(fLabel.String())) / 2.0; + DrawString(fLabel.String(), BPoint(x, y)); + } +} + +// MouseDown +void +IconButton::MouseDown(BPoint where) +{ + if (IsValid()) { + if (_HasFlags(STATE_ENABLED)/* && !_HasFlags(STATE_FORCE_PRESSED)*/) { + if (Bounds().Contains(where)) { + SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); + _AddFlags(STATE_PRESSED | STATE_TRACKING); + } else { + _ClearFlags(STATE_PRESSED | STATE_TRACKING); + } + } + } +} + +// MouseUp +void +IconButton::MouseUp(BPoint where) +{ + if (IsValid()) { +// if (!_HasFlags(STATE_FORCE_PRESSED)) { + if (_HasFlags(STATE_ENABLED) && _HasFlags(STATE_PRESSED) && Bounds().Contains(where)) + Invoke(); + else if (Bounds().Contains(where)) + _AddFlags(STATE_INSIDE); + _ClearFlags(STATE_PRESSED | STATE_TRACKING); +// } + } +} + +// MouseMoved +void +IconButton::MouseMoved(BPoint where, uint32 transit, const BMessage* message) +{ + if (IsValid()) { + uint32 buttons = 0; + Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); + // catch a mouse up event that we might have missed + if (!buttons && _HasFlags(STATE_PRESSED)) { + MouseUp(where); + return; + } + if (buttons && !_HasFlags(STATE_TRACKING)) + return; + if ((transit == B_INSIDE_VIEW || transit == B_ENTERED_VIEW) + && _HasFlags(STATE_ENABLED)) + _AddFlags(STATE_INSIDE); + else + _ClearFlags(STATE_INSIDE); + if (_HasFlags(STATE_TRACKING)) { + if (Bounds().Contains(where)) + _AddFlags(STATE_PRESSED); + else + _ClearFlags(STATE_PRESSED); + } + } +} + +#define MIN_SPACE 15.0 + +// GetPreferredSize +void +IconButton::GetPreferredSize(float* width, float* height) +{ + float minWidth = 0.0; + float minHeight = 0.0; + if (IsValid()) { + minWidth += fNormalBitmap->Bounds().IntegerWidth() + 1.0; + minHeight += fNormalBitmap->Bounds().IntegerHeight() + 1.0; + } else { + minWidth += MIN_SPACE; + minHeight += MIN_SPACE; + } + if (minWidth < MIN_SPACE) + minWidth = MIN_SPACE; + if (minHeight < MIN_SPACE) + minHeight = MIN_SPACE; + if (fLabel.CountChars() > 0) { + font_height fh; + GetFontHeight(&fh); + minHeight += ceilf(fh.ascent + fh.descent) + 4.0; + minWidth += StringWidth(fLabel.String()) + 4.0; + } + + if (width) + *width = minWidth + 4.0; + if (height) + *height = minHeight + 4.0; +} + +// Invoke +status_t +IconButton::Invoke(BMessage* message) +{ + if (!message) + message = Message(); + if (message) { + BMessage clone(*message); + clone.AddInt64("be:when", system_time()); + clone.AddPointer("be:source", (BView*)this); + clone.AddInt32("be:value", Value()); + clone.AddInt32("id", ID()); + return BInvoker::Invoke(&clone); + } + return BInvoker::Invoke(message); +} + +// SetPressed +void +IconButton::SetPressed(bool pressed) +{ + if (pressed) + _AddFlags(STATE_FORCE_PRESSED); + else + _ClearFlags(STATE_FORCE_PRESSED); +} + +// IsPressed +bool +IconButton::IsPressed() const +{ + return _HasFlags(STATE_FORCE_PRESSED); +} + +// SetIcon +status_t +IconButton::SetIcon(const char* pathToBitmap) +{ + status_t status = B_BAD_VALUE; + if (pathToBitmap) { + BBitmap* fileBitmap = NULL; + // try to load bitmap from either relative or absolute path + BEntry entry(pathToBitmap, true); + if (!entry.Exists()) { + app_info info; + status = be_app->GetAppInfo(&info); + if (status == B_OK) { + BEntry app_entry(&info.ref, true); + BPath path; + app_entry.GetPath(&path); + status = path.InitCheck(); + if (status == B_OK) { + status = path.GetParent(&path); + if (status == B_OK) { + status = path.Append(pathToBitmap, true); + if (status == B_OK) + fileBitmap = BTranslationUtils::GetBitmap(path.Path()); + else + printf("IconButton::SetIcon() - path.Append() failed: %s\n", strerror(status)); + } else + printf("IconButton::SetIcon() - path.GetParent() failed: %s\n", strerror(status)); + } else + printf("IconButton::SetIcon() - path.InitCheck() failed: %s\n", strerror(status)); + } else + printf("IconButton::SetIcon() - be_app->GetAppInfo() failed: %s\n", strerror(status)); + } else + fileBitmap = BTranslationUtils::GetBitmap(pathToBitmap); + if (fileBitmap) { + status = _MakeBitmaps(fileBitmap); + delete fileBitmap; + } else + status = B_ERROR; + } + return status; +} + +// SetIcon +status_t +IconButton::SetIcon(const BBitmap* bitmap) +{ + if (bitmap && bitmap->ColorSpace() == B_CMAP8) { + status_t status = bitmap->InitCheck(); + if (status >= B_OK) { + if (BBitmap* rgb32Bitmap = _ConvertToRGB32(bitmap)) { + status = _MakeBitmaps(rgb32Bitmap); + delete rgb32Bitmap; + } else + status = B_NO_MEMORY; + } + return status; + } else + return _MakeBitmaps(bitmap); +} + +// SetIcon +status_t +IconButton::SetIcon(const BMimeType* fileType, bool small) +{ + status_t status = fileType ? fileType->InitCheck() : B_BAD_VALUE; + if (status >= B_OK) { + BBitmap* mimeBitmap = new(nothrow) BBitmap(BRect(0.0, 0.0, 15.0, 15.0), B_CMAP8); + if (mimeBitmap && mimeBitmap->IsValid()) { + status = fileType->GetIcon(mimeBitmap, small ? B_MINI_ICON : B_LARGE_ICON); + if (status >= B_OK) { + if (BBitmap* bitmap = _ConvertToRGB32(mimeBitmap)) { + status = _MakeBitmaps(bitmap); + delete bitmap; + } else + printf("IconButton::SetIcon() - B_RGB32 bitmap is not valid\n"); + } else + printf("IconButton::SetIcon() - fileType->GetIcon() failed: %s\n", strerror(status)); + } else + printf("IconButton::SetIcon() - B_CMAP8 bitmap is not valid\n"); + delete mimeBitmap; + } else + printf("IconButton::SetIcon() - fileType is not valid: %s\n", strerror(status)); + return status; +} + +// SetIcon +status_t +IconButton::SetIcon(const unsigned char* bitsFromQuickRes, + uint32 width, uint32 height, color_space format, bool convertToBW) +{ + status_t status = B_BAD_VALUE; + if (bitsFromQuickRes && width > 0 && height > 0) { + BBitmap* quickResBitmap = new(nothrow) BBitmap(BRect(0.0, 0.0, width - 1.0, height - 1.0), format); + status = quickResBitmap ? quickResBitmap->InitCheck() : B_ERROR; + if (status >= B_OK) { + // It doesn't look right to copy BitsLength() bytes, but bitmaps + // exported from QuickRes still contain their padding, so it is alright. + memcpy(quickResBitmap->Bits(), bitsFromQuickRes, quickResBitmap->BitsLength()); + if (format != B_RGB32 && format != B_RGBA32 && format != B_RGB32_BIG && format != B_RGBA32_BIG) { + // colorspace needs conversion + BBitmap* bitmap = new(nothrow) BBitmap(quickResBitmap->Bounds(), B_RGB32, true); + if (bitmap && bitmap->IsValid()) { + BView* helper = new BView(bitmap->Bounds(), "helper", + B_FOLLOW_NONE, B_WILL_DRAW); + if (bitmap->Lock()) { + bitmap->AddChild(helper); + helper->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + helper->FillRect(helper->Bounds()); + helper->SetDrawingMode(B_OP_OVER); + helper->DrawBitmap(quickResBitmap, BPoint(0.0, 0.0)); + helper->Sync(); + bitmap->Unlock(); + } + status = _MakeBitmaps(bitmap); + } else + printf("IconButton::SetIcon() - B_RGB32 bitmap is not valid\n"); + delete bitmap; + } else { + // native colorspace (32 bits) + if (convertToBW) { + // convert to gray scale icon + uint8* bits = (uint8*)quickResBitmap->Bits(); + uint32 bpr = quickResBitmap->BytesPerRow(); + for (uint32 y = 0; y < height; y++) { + uint8* handle = bits; + uint8 gray; + for (uint32 x = 0; x < width; x++) { + gray = uint8((116 * handle[0] + 600 * handle[1] + 308 * handle[2]) / 1024); + handle[0] = gray; + handle[1] = gray; + handle[2] = gray; + handle += 4; + } + bits += bpr; + } + } + status = _MakeBitmaps(quickResBitmap); + } + } else + printf("IconButton::SetIcon() - error allocating bitmap: %s\n", strerror(status)); + delete quickResBitmap; + } + return status; +} + +// ClearIcon +void +IconButton::ClearIcon() +{ + _DeleteBitmaps(); + _Update(); +} + +// Bitmap +BBitmap* +IconButton::Bitmap() const +{ + BBitmap* bitmap = NULL; + if (fNormalBitmap && fNormalBitmap->IsValid()) { + bitmap = new(nothrow) BBitmap(fNormalBitmap); + if (bitmap->IsValid()) { + // TODO: remove this functionality when we use real transparent bitmaps + uint8* bits = (uint8*)bitmap->Bits(); + uint32 bpr = bitmap->BytesPerRow(); + uint32 width = bitmap->Bounds().IntegerWidth() + 1; + uint32 height = bitmap->Bounds().IntegerHeight() + 1; + color_space format = bitmap->ColorSpace(); + if (format == B_CMAP8) { + // replace gray with magic transparent index + } else if (format == B_RGB32) { + for (uint32 y = 0; y < height; y++) { + uint8* bitsHandle = bits; + for (uint32 x = 0; x < width; x++) { + if (bitsHandle[0] == 216 + && bitsHandle[1] == 216 + && bitsHandle[2] == 216) { + bitsHandle[3] = 0; // make this pixel completely transparent + } + bitsHandle += 4; + } + bits += bpr; + } + } + } else { + delete bitmap; + bitmap = NULL; + } + } + return bitmap; +} + +// DrawBorder +bool +IconButton::DrawBorder() const +{ + return (IsEnabled() && (_HasFlags(STATE_INSIDE) || _HasFlags(STATE_TRACKING)) + || _HasFlags(STATE_FORCE_PRESSED)); +} + +// DrawNormalBorder +void +IconButton::DrawNormalBorder(BRect r, rgb_color background, + rgb_color shadow, rgb_color darkShadow, + rgb_color lightShadow, rgb_color light) +{ + _DrawFrame(r, shadow, darkShadow, light, lightShadow); +} + +// DrawPressedBorder +void +IconButton::DrawPressedBorder(BRect r, rgb_color background, + rgb_color shadow, rgb_color darkShadow, + rgb_color lightShadow, rgb_color light) +{ + _DrawFrame(r, shadow, light, darkShadow, background); +} + +// IsValid +bool +IconButton::IsValid() const +{ + return (fNormalBitmap && fDisabledBitmap && fClickedBitmap && fDisabledClickedBitmap + && fNormalBitmap->IsValid() + && fDisabledBitmap->IsValid() + && fClickedBitmap->IsValid() + && fDisabledClickedBitmap->IsValid()); +} + +// Value +int32 +IconButton::Value() const +{ + return _HasFlags(STATE_PRESSED) ? B_CONTROL_ON : B_CONTROL_OFF; +} + +// SetValue +void +IconButton::SetValue(int32 value) +{ + if (value) + _AddFlags(STATE_PRESSED); + else + _ClearFlags(STATE_PRESSED); +} + +// IsEnabled +bool +IconButton::IsEnabled() const +{ + return _HasFlags(STATE_ENABLED) ? B_CONTROL_ON : B_CONTROL_OFF; +} + +// SetEnabled +void +IconButton::SetEnabled(bool enabled) +{ + if (enabled) + _AddFlags(STATE_ENABLED); + else + _ClearFlags(STATE_ENABLED | STATE_TRACKING | STATE_INSIDE); +} + +// _ConvertToRGB32 +BBitmap* +IconButton::_ConvertToRGB32(const BBitmap* bitmap) const +{ + BBitmap* convertedBitmap = new(nothrow) BBitmap(bitmap->Bounds(), B_BITMAP_ACCEPTS_VIEWS, B_RGBA32); + if (convertedBitmap && convertedBitmap->IsValid()) { + memset(convertedBitmap->Bits(), 0, convertedBitmap->BitsLength()); + BView* helper = new BView(bitmap->Bounds(), "helper", + B_FOLLOW_NONE, B_WILL_DRAW); + if (convertedBitmap->Lock()) { + convertedBitmap->AddChild(helper); + helper->SetDrawingMode(B_OP_OVER); + helper->DrawBitmap(bitmap, BPoint(0.0, 0.0)); + helper->Sync(); + convertedBitmap->Unlock(); + } + } else { + delete convertedBitmap; + convertedBitmap = NULL; + } + return convertedBitmap; +} + +// _MakeBitmaps +status_t +IconButton::_MakeBitmaps(const BBitmap* bitmap) +{ + status_t status = bitmap ? bitmap->InitCheck() : B_BAD_VALUE; + if (status >= B_OK) { + // make our own versions of the bitmap + BRect b(bitmap->Bounds()); + _DeleteBitmaps(); + color_space format = bitmap->ColorSpace(); + fNormalBitmap = new(nothrow) BBitmap(b, format); + fDisabledBitmap = new(nothrow) BBitmap(b, format); + fClickedBitmap = new(nothrow) BBitmap(b, format); + fDisabledClickedBitmap = new(nothrow) BBitmap(b, format); + if (IsValid()) { + // copy bitmaps from file bitmap + uint8* nBits = (uint8*)fNormalBitmap->Bits(); + uint8* dBits = (uint8*)fDisabledBitmap->Bits(); + uint8* cBits = (uint8*)fClickedBitmap->Bits(); + uint8* dcBits = (uint8*)fDisabledClickedBitmap->Bits(); + uint8* fBits = (uint8*)bitmap->Bits(); + int32 nbpr = fNormalBitmap->BytesPerRow(); + int32 fbpr = bitmap->BytesPerRow(); + int32 pixels = b.IntegerWidth() + 1; + int32 lines = b.IntegerHeight() + 1; + // nontransparent version: + if (format == B_RGB32 || format == B_RGB32_BIG) { + // iterate over color components + for (int32 y = 0; y < lines; y++) { + for (int32 x = 0; x < pixels; x++) { + int32 nOffset = 4 * x; + int32 fOffset = 4 * x; + nBits[nOffset + 0] = fBits[fOffset + 0]; + nBits[nOffset + 1] = fBits[fOffset + 1]; + nBits[nOffset + 2] = fBits[fOffset + 2]; + nBits[nOffset + 3] = 255; + // clicked bits are darker (lame method...) + cBits[nOffset + 0] = (uint8)((float)nBits[nOffset + 0] * 0.8); + cBits[nOffset + 1] = (uint8)((float)nBits[nOffset + 1] * 0.8); + cBits[nOffset + 2] = (uint8)((float)nBits[nOffset + 2] * 0.8); + cBits[nOffset + 3] = 255; + // disabled bits have less contrast (lame method...) + uint8 grey = 216; + float dist = (nBits[nOffset + 0] - grey) * 0.4; + dBits[nOffset + 0] = (uint8)(grey + dist); + dist = (nBits[nOffset + 1] - grey) * 0.4; + dBits[nOffset + 1] = (uint8)(grey + dist); + dist = (nBits[nOffset + 2] - grey) * 0.4; + dBits[nOffset + 2] = (uint8)(grey + dist); + dBits[nOffset + 3] = 255; + // disabled bits have less contrast (lame method...) + grey = 188; + dist = (nBits[nOffset + 0] - grey) * 0.4; + dcBits[nOffset + 0] = (uint8)(grey + dist); + dist = (nBits[nOffset + 1] - grey) * 0.4; + dcBits[nOffset + 1] = (uint8)(grey + dist); + dist = (nBits[nOffset + 2] - grey) * 0.4; + dcBits[nOffset + 2] = (uint8)(grey + dist); + dcBits[nOffset + 3] = 255; + } + nBits += nbpr; + dBits += nbpr; + cBits += nbpr; + dcBits += nbpr; + fBits += fbpr; + } + // transparent version: + } else if (format == B_RGBA32 || format == B_RGBA32_BIG) { + // iterate over color components + for (int32 y = 0; y < lines; y++) { + for (int32 x = 0; x < pixels; x++) { + int32 nOffset = 4 * x; + int32 fOffset = 4 * x; + nBits[nOffset + 0] = fBits[fOffset + 0]; + nBits[nOffset + 1] = fBits[fOffset + 1]; + nBits[nOffset + 2] = fBits[fOffset + 2]; + nBits[nOffset + 3] = fBits[fOffset + 3]; + // clicked bits are darker (lame method...) + cBits[nOffset + 0] = (uint8)(nBits[nOffset + 0] * 0.8); + cBits[nOffset + 1] = (uint8)(nBits[nOffset + 1] * 0.8); + cBits[nOffset + 2] = (uint8)(nBits[nOffset + 2] * 0.8); + cBits[nOffset + 3] = fBits[fOffset + 3]; + // disabled bits have less opacity + dBits[nOffset + 0] = fBits[fOffset + 0]; + dBits[nOffset + 1] = fBits[fOffset + 1]; + dBits[nOffset + 2] = fBits[fOffset + 2]; + dBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.5); + // disabled bits have less contrast (lame method...) + dcBits[nOffset + 0] = (uint8)(nBits[nOffset + 0] * 0.8); + dcBits[nOffset + 1] = (uint8)(nBits[nOffset + 1] * 0.8); + dcBits[nOffset + 2] = (uint8)(nBits[nOffset + 2] * 0.8); + dcBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.5); + } + nBits += nbpr; + dBits += nbpr; + cBits += nbpr; + dcBits += nbpr; + fBits += fbpr; + } + // unsupported format + } else { + printf("IconButton::_MakeBitmaps() - bitmap has unsupported colorspace\n"); + status = B_MISMATCHED_VALUES; + _DeleteBitmaps(); + } + } else { + printf("IconButton::_MakeBitmaps() - error allocating local bitmaps\n"); + status = B_NO_MEMORY; + _DeleteBitmaps(); + } + } else + printf("IconButton::_MakeBitmaps() - bitmap is not valid\n"); + return status; +} + +// _DeleteBitmaps +void +IconButton::_DeleteBitmaps() +{ + delete fNormalBitmap; + fNormalBitmap = NULL; + delete fDisabledBitmap; + fDisabledBitmap = NULL; + delete fClickedBitmap; + fClickedBitmap = NULL; + delete fDisabledClickedBitmap; + fDisabledClickedBitmap = NULL; +} + +// _Update +void +IconButton::_Update() +{ + if (LockLooper()) { + Invalidate(); + UnlockLooper(); + } +} + +// _AddFlags +void +IconButton::_AddFlags(uint32 flags) +{ + if (!(fButtonState & flags)) { + fButtonState |= flags; + _Update(); + } +} + +// _ClearFlags +void +IconButton::_ClearFlags(uint32 flags) +{ + if (fButtonState & flags) { + fButtonState &= ~flags; + _Update(); + } +} + +// _HasFlags +bool +IconButton::_HasFlags(uint32 flags) const +{ + return (fButtonState & flags); +} + +// _DrawFrame +void +IconButton::_DrawFrame(BRect r, rgb_color col1, rgb_color col2, + rgb_color col3, rgb_color col4) +{ + BeginLineArray(8); + AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), col1); + AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), col1); + AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), col2); + AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), col2); + r.InsetBy(1.0, 1.0); + AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), col3); + AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), col3); + AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), col4); + AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), col4); + EndLineArray(); +} diff --git a/src/apps/launchbox/IconButton.h b/src/apps/launchbox/IconButton.h new file mode 100644 index 0000000000..a7e07fa44c --- /dev/null +++ b/src/apps/launchbox/IconButton.h @@ -0,0 +1,129 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +/** gui class that loads an image from disk and shows it + as clickable button */ + +// TODO: inherit from BControl? + +// NOTE: this file is a duplicate of the version in Icon-O-Matic/generic +// it should be placed into a common folder for generic useful stuff + +#ifndef ICON_BUTTON_H +#define ICON_BUTTON_H + +#include +#include +#include + +class BBitmap; +class BMimeType; + +class IconButton : public BView, public BInvoker { + public: + IconButton(const char* name, + uint32 id, + const char* label = NULL, + BMessage* message = NULL, + BHandler* target = NULL); + virtual ~IconButton(); + + // BView interface + virtual void MessageReceived(BMessage* message); + virtual void AttachedToWindow(); + virtual void Draw(BRect updateRect); + virtual void MouseDown(BPoint where); + virtual void MouseUp(BPoint where); + virtual void MouseMoved(BPoint where, uint32 transit, + const BMessage* message); + virtual void GetPreferredSize(float* width, + float* height); + + // BInvoker interface + virtual status_t Invoke(BMessage* message = NULL); + + // IconButton + bool IsValid() const; + + virtual int32 Value() const; + virtual void SetValue(int32 value); + + bool IsEnabled() const; + void SetEnabled(bool enable); + + void SetPressed(bool pressed); + bool IsPressed() const; + uint32 ID() const + { return fID; } + + status_t SetIcon(const char* pathToBitmap); + status_t SetIcon(const BBitmap* bitmap); + status_t SetIcon(const BMimeType* fileType, + bool small = true); + status_t SetIcon(const unsigned char* bitsFromQuickRes, + uint32 width, uint32 height, + color_space format, + bool convertToBW = false); + void ClearIcon(); + + BBitmap* Bitmap() const; + // caller has to delete the returned bitmap + + virtual bool DrawBorder() const; + virtual void DrawNormalBorder(BRect r, + rgb_color background, + rgb_color shadow, + rgb_color darkShadow, + rgb_color lightShadow, + rgb_color light); + virtual void DrawPressedBorder(BRect r, + rgb_color background, + rgb_color shadow, + rgb_color darkShadow, + rgb_color lightShadow, + rgb_color light); + + protected: + enum { + STATE_NONE = 0x0000, + STATE_TRACKING = 0x0001, + STATE_PRESSED = 0x0002, + STATE_ENABLED = 0x0004, + STATE_INSIDE = 0x0008, + STATE_FORCE_PRESSED = 0x0010, + }; + + void _AddFlags(uint32 flags); + void _ClearFlags(uint32 flags); + bool _HasFlags(uint32 flags) const; + + void _DrawFrame(BRect frame, + rgb_color col1, + rgb_color col2, + rgb_color col3, + rgb_color col4); + +// private: + BBitmap* _ConvertToRGB32(const BBitmap* bitmap) const; + status_t _MakeBitmaps(const BBitmap* bitmap); + void _DeleteBitmaps(); + void _SendMessage() const; + void _Update(); + + uint32 fButtonState; + int32 fID; + BBitmap* fNormalBitmap; + BBitmap* fDisabledBitmap; + BBitmap* fClickedBitmap; + BBitmap* fDisabledClickedBitmap; + BString fLabel; + + BHandler* fTargetCache; +}; + +#endif // ICON_BUTTON_H diff --git a/src/apps/launchbox/Jamfile b/src/apps/launchbox/Jamfile new file mode 100644 index 0000000000..ceec9c4234 --- /dev/null +++ b/src/apps/launchbox/Jamfile @@ -0,0 +1,22 @@ +SubDir HAIKU_TOP src apps launchbox ; + +AddSubDirSupportedPlatforms libbe_test ; + +Application LaunchBox : + App.cpp + IconButton.cpp + LaunchButton.cpp + main.cpp + MainWindow.cpp + NamePanel.cpp + PadView.cpp + Panel.cpp + support.cpp + : be translation + : LaunchBox.rdef +; + +if $(TARGET_PLATFORM) = libbe_test { + HaikuInstall install-test-apps : $(HAIKU_APP_TEST_DIR) : LaunchBox + : tests!apps ; +} diff --git a/src/apps/launchbox/LaunchBox.rdef b/src/apps/launchbox/LaunchBox.rdef new file mode 100644 index 0000000000..28f8aba5e5 --- /dev/null +++ b/src/apps/launchbox/LaunchBox.rdef @@ -0,0 +1,55 @@ + +resource app_signature "application/x-vnd.Haiku-LaunchBox"; + +resource app_flags B_SINGLE_LAUNCH; + +resource app_version { + major = 1, + middle = 1, + minor = 0, + + variety = B_APPV_ALPHA, + internal = 1, + + short_info = "LaunchBox", + long_info = "LaunchBox ©2006 Haiku" +}; + +#ifdef HAIKU_TARGET_PLATFORM_HAIKU + +resource vector_icon { + $"6E6369660D0500020112023DEB47BD4D713B47993BE4594843FB4BC62F000032" + $"FF005A03FF990003FFE40003FDFF7502000602000000B6FA0BB6FA0B00000046" + $"86C94A0BE800FF0000FFFF6666020006023727A3AEA7053080AD38FA08496B7B" + $"4AC41300F70606FF9804040200060234A005341E7FB7602E3817404893414A70" + $"1400FF6666FFC200000200060239496E39C491BC42BD3BF1B749D83049AAFF00" + $"B20404FF8B03030200160236FA0B36FA0B36FA0BB6FA0B48D2FD4A56710096FF" + $"580200060338EC7E3992F0BA0B4939582E4ABDF947F9C300FF000066FF0000FF" + $"610202020006033C13B4BC6C533F097A3E9A69478C893F11A700FFACACA7FFF1" + $"F1FFB4B4B4020112033B000000000000000039000046C0004BB00000FF1FA7FF" + $"0BFFFF000C060A8ABF0ABC08CAA134603C40584C554C55595260455F485E4444" + $"4A4F42444A3C50334EBA4BC7E5060DE6FF8802364E5C405240464046C0FBC13D" + $"C4F9BEC1C2C2C005CAD6BB675C205C235920BF27B8EFC280B311BDE2BB25363C" + $"BCDDBD1A363C2A20462E2C4834500A0425442C44353E2B3E0A032F44353E2C44" + $"0A043E513E47385038570A03384D38503E470207BFD6B95343B760BDA6BD2633" + $"43BB79BFE1334339493949BE05C26DC494BE11C0C1C041C688BCF456315435C8" + $"67B8DCC70EB6DBC7F5B7C3C624B5F34B26C50BB57F47280A063343B89FC30BB8" + $"A1C30DBADBC546BADDC54739490A042E4F3A4339422D4E0604BF4233C44DB5A0" + $"BEEFBCB13840BD95BE8FBDC5BEBD4334BF3ABCFDC4F0B64359220605EE023A4B" + $"3852384E3857BEC15F43524357434E414B0A042054206034603454100A010100" + $"000A0C010B000A000101000A00010A123ECDF73EDA22BEDA223ECDF7494B02C5" + $"A04A01178400040A02010A023ECDF73EDA22BEDA223ECDF7494B02C5A04A0A03" + $"010A023E292D3E320CBE328D3E28AE48F3C2447A6A0A04010A023CACE53CB6B4" + $"BCB8813CAB20486D1B48A8BB0A050102000A080103000A060104000A08010500" + $"0A0A0106000A090107000A0001081001178400040A070108000A0B010900" +}; + +#else // HAIKU_TARGET_PLATFORM_HAIKU + +//resource large_icon { +//}; +// +//resource mini_icon { +//}; + +#endif // HAIKU_TARGET_PLATFORM_HAIKU diff --git a/src/apps/launchbox/LaunchButton.cpp b/src/apps/launchbox/LaunchButton.cpp new file mode 100644 index 0000000000..fc80217996 --- /dev/null +++ b/src/apps/launchbox/LaunchButton.cpp @@ -0,0 +1,348 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "LaunchButton.h" + +#include // string.h is not enough on Haiku?!? +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include "BubbleHelper.h" +#include "PadView.h" + +static const float kDragStartDist = 10.0; +static const float kDragBitmapAlphaScale = 0.6; +//static const char* kEmptyHelpString = "You can drag an icon here."; + +bigtime_t +LaunchButton::fClickSpeed = 0; + +// constructor +LaunchButton::LaunchButton(const char* name, uint32 id, const char* label, + BMessage* message, BHandler* target) + : IconButton(name, id, label, message, target), + fRef(NULL), + fAppSig(NULL), + fDescription(""), + fAnticipatingDrop(false), + fLastClickTime(0) +{ + if (fClickSpeed == 0 || get_click_speed(&fClickSpeed) < B_OK) + fClickSpeed = 500000; + + BSize size(32.0 + 8.0, 32.0 + 8.0); + SetExplicitMinSize(size); + SetExplicitMaxSize(size); +} + +// destructor +LaunchButton::~LaunchButton() +{ + delete fRef; + if (fAppSig) + free(fAppSig); +} + +// AttachedToWindow +void +LaunchButton::AttachedToWindow() +{ + IconButton::AttachedToWindow(); + _UpdateToolTip(); +} + +// DetachedFromWindow +void +LaunchButton::DetachedFromWindow() +{ +// BubbleHelper::Default()->SetHelp(this, NULL); +} + +// Draw +void +LaunchButton::Draw(BRect updateRect) +{ + if (fAnticipatingDrop) { + rgb_color color = fRef ? ui_color(B_KEYBOARD_NAVIGATION_COLOR) + : (rgb_color){ 0, 130, 60, 255 }; + SetHighColor(color); + // limit clipping region to exclude the blue rect we just drew + BRect r(Bounds()); + StrokeRect(r); + r.InsetBy(1.0, 1.0); + BRegion region(r); + ConstrainClippingRegion(®ion); + } + if (IsValid()) { + IconButton::Draw(updateRect); + } else { + rgb_color background = LowColor(); + rgb_color lightShadow = tint_color(background, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0); + rgb_color shadow = tint_color(background, B_DARKEN_1_TINT); + rgb_color light = tint_color(background, B_LIGHTEN_1_TINT); + BRect r(Bounds()); + _DrawFrame(r, shadow, light, lightShadow, lightShadow); + r.InsetBy(2.0, 2.0); + SetHighColor(lightShadow); + FillRect(r); + } +} + +// MessageReceived +void +LaunchButton::MessageReceived(BMessage* message) +{ + switch (message->what) { + case B_SIMPLE_DATA: + case B_REFS_RECEIVED: { + entry_ref ref; + if (message->FindRef("refs", &ref) >= B_OK) { + if (fRef) { + if (ref != *fRef) { + BEntry entry(fRef, true); + if (entry.IsDirectory()) { + message->PrintToStream(); + // copy stuff into the directory + } else { + message->what = B_REFS_RECEIVED; + team_id team; + if (fAppSig) + team = be_roster->TeamFor(fAppSig); + else + team = be_roster->TeamFor(fRef); + if (team < B_OK) { + if (fAppSig) + be_roster->Launch(fAppSig, message, &team); + else + be_roster->Launch(fRef, message, &team); + } else { + app_info appInfo; + if (team >= B_OK && be_roster->GetRunningAppInfo(team, &appInfo) >= B_OK) { + BMessenger messenger(appInfo.signature, team); + if (messenger.IsValid()) + messenger.SendMessage(message); + } + } + } + } + } else { + SetTo(&ref); + } + } + break; + } + case B_PASTE: + case B_MODIFIERS_CHANGED: + default: + IconButton::MessageReceived(message); + break; + } +} + +// MouseDown +void +LaunchButton::MouseDown(BPoint where) +{ + bigtime_t now = system_time(); + bool callInherited = true; + if (now - fLastClickTime < fClickSpeed) + callInherited = false; + fLastClickTime = now; + if (BMessage* message = Window()->CurrentMessage()) { + uint32 buttons; + message->FindInt32("buttons", (int32*)&buttons); + if (buttons & B_SECONDARY_MOUSE_BUTTON) { + if (PadView* parent = dynamic_cast(Parent())) { + parent->DisplayMenu(ConvertToParent(where), this); + _ClearFlags(STATE_INSIDE); + callInherited = false; + } + } else { + fDragStart = where; + } + } + if (callInherited) + IconButton::MouseDown(where); +} + +// MouseUp +void +LaunchButton::MouseUp(BPoint where) +{ + if (fAnticipatingDrop) { + fAnticipatingDrop = false; + Invalidate(); + } + IconButton::MouseUp(where); +} + +// MouseMoved +void +LaunchButton::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) +{ + if ((dragMessage && (transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW)) + && ((dragMessage->what == B_SIMPLE_DATA + || dragMessage->what == B_REFS_RECEIVED) || fRef)) { + if (!fAnticipatingDrop) { + fAnticipatingDrop = true; + Invalidate(); + } + } + if (!dragMessage || (transit == B_EXITED_VIEW || transit == B_OUTSIDE_VIEW)) { + if (fAnticipatingDrop) { + fAnticipatingDrop = false; + Invalidate(); + } + } + // see if we should create a drag message + if (_HasFlags(STATE_TRACKING) && fRef) { + BPoint diff = where - fDragStart; + float dist = sqrtf(diff.x * diff.x + diff.y * diff.y); + if (dist >= kDragStartDist) { + // stop tracking + _ClearFlags(STATE_PRESSED | STATE_TRACKING | STATE_INSIDE); + // create drag bitmap and message + if (BBitmap* bitmap = Bitmap()) { + if (bitmap->ColorSpace() == B_RGB32) { + // make semitransparent + uint8* bits = (uint8*)bitmap->Bits(); + uint32 width = bitmap->Bounds().IntegerWidth() + 1; + uint32 height = bitmap->Bounds().IntegerHeight() + 1; + uint32 bpr = bitmap->BytesPerRow(); + for (uint32 y = 0; y < height; y++) { + uint8* bitsHandle = bits; + for (uint32 x = 0; x < width; x++) { + bitsHandle[3] = uint8(bitsHandle[3] * kDragBitmapAlphaScale); + bitsHandle += 4; + } + bits += bpr; + } + } + BMessage message(B_SIMPLE_DATA); + message.AddPointer("button", this); + message.AddRef("refs", fRef); + DragMessage(&message, bitmap, B_OP_ALPHA, fDragStart); + } + } + } + IconButton::MouseMoved(where, transit, dragMessage); +} + +// SetTo +void +LaunchButton::SetTo(const entry_ref* ref) +{ + if (fAppSig) { + free(fAppSig); + fAppSig = NULL; + } + delete fRef; + if (ref) { + fRef = new entry_ref(*ref); + // follow links + BEntry entry(fRef, true); + entry.GetRef(fRef); + + _UpdateIcon(fRef); + // see if this is an application + BFile file(ref, B_READ_ONLY); + BAppFileInfo info; + if (info.SetTo(&file) >= B_OK) { + char mimeSig[B_MIME_TYPE_LENGTH]; + if (info.GetSignature(mimeSig) >= B_OK) { + SetTo(mimeSig, false); + } else { + printf("no MIME sig\n"); + } + } else { + printf("no app\n"); + } + } else { + fRef = NULL; + ClearIcon(); + } + _UpdateToolTip(); +} + +// Ref +entry_ref* +LaunchButton::Ref() const +{ + return fRef; +} + +// SetTo +void +LaunchButton::SetTo(const char* appSig, bool updateIcon) +{ + if (appSig) { + if (fAppSig) + free(fAppSig); + fAppSig = strdup(appSig); + if (updateIcon) { + entry_ref ref; + if (be_roster->FindApp(fAppSig, &ref) >= B_OK) + SetTo(&ref); + } + } + _UpdateToolTip(); +} + +// SetDesciption +void +LaunchButton::SetDescription(const char* text) +{ + fDescription.SetTo(text); + _UpdateToolTip(); +} + +// _UpdateToolTip +void +LaunchButton::_UpdateToolTip() +{ + if (fRef) { + BString helper(fRef->name); + if (fDescription.CountChars() > 0) { + helper << "\n\n" << fDescription.String(); + } else { + BFile file(fRef, B_READ_ONLY); + BAppFileInfo appFileInfo; + version_info info; + if (appFileInfo.SetTo(&file) >= B_OK + && appFileInfo.GetVersionInfo(&info, + B_APP_VERSION_KIND) >= B_OK + && strlen(info.short_info) > 0) { + helper << "\n\n" << info.short_info; + } + } +// BubbleHelper::Default()->SetHelp(this, helper.String()); + } else { +// BubbleHelper::Default()->SetHelp(this, kEmptyHelpString); + } +} + +// _UpdateIcon +void +LaunchButton::_UpdateIcon(const entry_ref* ref) +{ + BBitmap* icon = new BBitmap(BRect(0.0, 0.0, 31.0, 31.0), B_RGBA32); + if (BNodeInfo::GetTrackerIcon(ref, icon, B_LARGE_ICON) >= B_OK) + SetIcon(icon); + + delete icon; +} diff --git a/src/apps/launchbox/LaunchButton.h b/src/apps/launchbox/LaunchButton.h new file mode 100644 index 0000000000..b5c91d3398 --- /dev/null +++ b/src/apps/launchbox/LaunchButton.h @@ -0,0 +1,70 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef LAUNCH_BUTTON_H +#define LAUNCH_BUTTON_H + +#include +#include + +#include "IconButton.h" + +enum { + MSG_ADD_SLOT = 'adsl', + MSG_CLEAR_SLOT = 'clsl', + MSG_REMOVE_SLOT = 'rmsl', + MSG_LAUNCH = 'lnch', +}; + +class LaunchButton : public IconButton { + public: + LaunchButton(const char* name, + uint32 id, + const char* label = NULL, + BMessage* message = NULL, + BHandler* target = NULL); + virtual ~LaunchButton(); + + // IconButton interface + virtual void AttachedToWindow(); + virtual void DetachedFromWindow(); + virtual void Draw(BRect updateRect); + virtual void MessageReceived(BMessage* message); + virtual void MouseDown(BPoint where); + virtual void MouseUp(BPoint where); + virtual void MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage); + + // LaunchButton + void SetTo(const entry_ref* ref); + entry_ref* Ref() const; + + void SetTo(const char* appSig, bool updateIcon); + const char* AppSignature() const + { return fAppSig; } + + void SetDescription(const char* text); + const char* Description() const + { return fDescription.String(); } + + private: + void _UpdateToolTip(); + void _UpdateIcon(const entry_ref* ref); + + entry_ref* fRef; + char* fAppSig; + BString fDescription; + + bool fAnticipatingDrop; + bigtime_t fLastClickTime; + BPoint fDragStart; + + static bigtime_t fClickSpeed; +}; + +#endif // LAUNCH_BUTTON_H diff --git a/src/apps/launchbox/MainWindow.cpp b/src/apps/launchbox/MainWindow.cpp new file mode 100644 index 0000000000..8ae1df7fa7 --- /dev/null +++ b/src/apps/launchbox/MainWindow.cpp @@ -0,0 +1,475 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "MainWindow.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "support.h" + +#include "LaunchButton.h" +#include "NamePanel.h" +#include "PadView.h" + +// constructor +MainWindow::MainWindow(const char* name, BRect frame) + : BWindow(frame, name, + B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, + B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE + | B_WILL_ACCEPT_FIRST_CLICK | B_NO_WORKSPACE_ACTIVATION), + fSettings(new BMessage('sett')), + fPadView(new PadView("pad view")), + fLastID(0), + fNamePanelFrame(-1000.0, -1000.0, -900.0, -900.0), + fAutoRaise(false), + fShowOnAllWorkspaces(true) +{ + bool buttonsAdded = false; + if (load_settings(fSettings, "main_settings", "LaunchBox") >= B_OK) + buttonsAdded = LoadSettings(fSettings); + if (!buttonsAdded) { + fPadView->AddButton(new LaunchButton("launch button", fLastID++, NULL, + new BMessage(MSG_LAUNCH))); + fPadView->AddButton(new LaunchButton("launch button", fLastID++, NULL, + new BMessage(MSG_LAUNCH))); + fPadView->AddButton(new LaunchButton("launch button", fLastID++, NULL, + new BMessage(MSG_LAUNCH))); + } + + SetLayout(new BGroupLayout(B_HORIZONTAL)); + AddChild(fPadView); +} + +// constructor +MainWindow::MainWindow(const char* name, BRect frame, BMessage* settings) + : BWindow(frame, name, + B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, + B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE + | B_WILL_ACCEPT_FIRST_CLICK | B_NO_WORKSPACE_ACTIVATION), + fSettings(settings), + fPadView(new PadView("pad view")), + fLastID(0), + fNamePanelFrame(-1000.0, -1000.0, -900.0, -900.0), + fAutoRaise(false), + fShowOnAllWorkspaces(true) +{ + if (!LoadSettings(settings)) { + fPadView->AddButton(new LaunchButton("launch button", fLastID++, NULL, + new BMessage(MSG_LAUNCH))); + fPadView->AddButton(new LaunchButton("launch button", fLastID++, NULL, + new BMessage(MSG_LAUNCH))); + fPadView->AddButton(new LaunchButton("launch button", fLastID++, NULL, + new BMessage(MSG_LAUNCH))); + } + + SetLayout(new BGroupLayout(B_HORIZONTAL)); + AddChild(fPadView); +} + + +// destructor +MainWindow::~MainWindow() +{ + delete fSettings; +} + +// QuitRequested +bool +MainWindow::QuitRequested() +{ + int32 padWindowCount = 0; + for (int32 i = 0; BWindow* window = be_app->WindowAt(i); i++) { + if (dynamic_cast(window)) + padWindowCount++; + } + if (padWindowCount == 1) { + be_app->PostMessage(B_QUIT_REQUESTED); + return false; + } else { + BAlert* alert = new BAlert("last chance", "Really close this pad?\n" + "(The pad will not be remembered.)", + "Close", "Cancel", NULL); + if (alert->Go() == 1) + return false; + } + return true; +} + +// MessageReceived +void +MainWindow::MessageReceived(BMessage* message) +{ + switch (message->what) { + case MSG_LAUNCH: { + BView* pointer; + if (message->FindPointer("be:source", (void**)&pointer) >= B_OK) { + if (LaunchButton* button = dynamic_cast(pointer)) { + if (button->AppSignature()) { + be_roster->Launch(button->AppSignature()); + } else { + BEntry entry(button->Ref(), true); + if (entry.IsDirectory()) { + // open in Tracker + BMessenger messenger("application/x-vnd.Be-TRAK"); + if (messenger.IsValid()) { + BMessage trackerMessage(B_REFS_RECEIVED); + trackerMessage.AddRef("refs", button->Ref()); + messenger.SendMessage(&trackerMessage); + } + } else { + status_t ret = be_roster->Launch(button->Ref()); + if (ret < B_OK) + fprintf(stderr, "launching %s failed: %s\n", + button->Ref()->name, strerror(ret)); + } + } + } + } + break; + } + case MSG_ADD_SLOT: { + LaunchButton* button; + if (message->FindPointer("be:source", (void**)&button) >= B_OK) { + fPadView->AddButton(new LaunchButton("launch button", fLastID++, NULL, + new BMessage(MSG_LAUNCH)), button); + } + break; + } + case MSG_CLEAR_SLOT: { + LaunchButton* button; + if (message->FindPointer("be:source", (void**)&button) >= B_OK) + button->SetTo((entry_ref*)NULL); + break; + } + case MSG_REMOVE_SLOT: { + LaunchButton* button; + if (message->FindPointer("be:source", (void**)&button) >= B_OK) { + if (fPadView->RemoveButton(button)) + delete button; + } + break; + } + case MSG_SET_DESCRIPTION: { + LaunchButton* button; + if (message->FindPointer("be:source", (void**)&button) >= B_OK) { + const char* name; + if (message->FindString("name", &name) >= B_OK) { + // message comes from a previous name panel + button->SetDescription(name); + message->FindRect("frame", &fNamePanelFrame); + } else { + // message comes from pad view + entry_ref* ref = button->Ref(); + if (ref) { + BString helper("Description for '"); + helper << ref->name << "'"; +// BRect* frame = fNamePanelFrame.IsValid() ? &fNamePanelFrame : NULL; + new NamePanel(helper.String(), + button->Description(), + this, this, + new BMessage(*message), + fNamePanelFrame); + } + } + } + break; + } + case MSG_ADD_WINDOW: { + BMessage settings('sett'); + SaveSettings(&settings); + message->AddMessage("window", &settings); + be_app->PostMessage(message); + break; + } + case MSG_SHOW_BORDER: + SetLook(B_TITLED_WINDOW_LOOK); + break; + case MSG_HIDE_BORDER: + SetLook(B_BORDERED_WINDOW_LOOK); + break; + case MSG_TOGGLE_AUTORAISE: + ToggleAutoRaise(); + break; + case MSG_SHOW_ON_ALL_WORKSPACES: + fShowOnAllWorkspaces = !fShowOnAllWorkspaces; + break; + case B_SIMPLE_DATA: + case B_REFS_RECEIVED: + case B_PASTE: + case B_MODIFIERS_CHANGED: + break; + case B_ABOUT_REQUESTED: + be_app->PostMessage(message); + break; + default: + BWindow::MessageReceived(message); + break; + } +} + +// Show +void +MainWindow::Show() +{ + BWindow::Show(); + _GetLocation(); +} + +// ScreenChanged +void +MainWindow::ScreenChanged(BRect frame, color_space format) +{ + _AdjustLocation(Frame()); +} + +// WorkspaceActivated +void +MainWindow::WorkspaceActivated(int32 workspace, bool active) +{ + if (fShowOnAllWorkspaces) { + if (!active) { + SetWorkspaces(1 << current_workspace()); + _AdjustLocation(Frame()); + } else + _GetLocation(); + } +} + +// FrameMoved +void +MainWindow::FrameMoved(BPoint origin) +{ + if (IsActive()) + _GetLocation(); +} + +// FrameResized +void +MainWindow::FrameResized(float width, float height) +{ + if (IsActive()) + _GetLocation(); + BWindow::FrameResized(width, height); +} + +// ToggleAutoRaise +void +MainWindow::ToggleAutoRaise() +{ + fAutoRaise = !fAutoRaise; + if (fAutoRaise) + fPadView->SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); + else + fPadView->SetEventMask(0); +} + +// LoadSettings +bool +MainWindow::LoadSettings(const BMessage* message) +{ + // restore window positioning + BPoint point; + bool useAdjust = false; + if (message->FindPoint("window position", &point) == B_OK) { + fScreenPosition = point; + useAdjust = true; + } + float borderDist; + if (message->FindFloat("border distance", &borderDist) == B_OK) { + fBorderDist = borderDist; + } + // restore window frame + BRect frame; + if (message->FindRect("window frame", &frame) == B_OK) { + if (useAdjust) { + _AdjustLocation(frame); + } else { + make_sure_frame_is_on_screen(frame, this); + MoveTo(frame.LeftTop()); + ResizeTo(frame.Width(), frame.Height()); + } + } + + // restore name panel frame + if (message->FindRect("name panel frame", &frame) == B_OK) { + if (frame.IsValid()) { + make_sure_frame_is_on_screen(frame, this); + fNamePanelFrame = frame; + } + } + + // restore window look + window_look look; + if (message->FindInt32("window look", (int32*)&look) == B_OK) + SetLook(look); + // restore buttons + const char* path; + bool buttonAdded = false; + for (int32 i = 0; message->FindString("path", i, &path) >= B_OK; i++) { + LaunchButton* button = new LaunchButton("launch button", fLastID++, NULL, + new BMessage(MSG_LAUNCH)); + fPadView->AddButton(button); + BString signature; + if (message->FindString("signature", i, &signature) >= B_OK + && signature.CountChars() > 0) { + button->SetTo(signature.String(), true); + } else { + entry_ref ref; + if (get_ref_for_path(path, &ref) >= B_OK) + button->SetTo(&ref); + } + const char* text; + if (message->FindString("description", i, &text) >= B_OK) + button->SetDescription(text); + buttonAdded = true; + } + + // restore auto raise setting + bool autoRaise; + if (message->FindBool("auto raise", &autoRaise) == B_OK && autoRaise) + ToggleAutoRaise(); + + // store workspace setting + bool showOnAllWorkspaces; + if (message->FindBool("all workspaces", &showOnAllWorkspaces) == B_OK) + fShowOnAllWorkspaces = showOnAllWorkspaces; + if (!fShowOnAllWorkspaces) { + uint32 workspaces; + if (message->FindInt32("workspaces", (int32*)&workspaces) == B_OK) + SetWorkspaces(workspaces); + } + + return buttonAdded; +} + +// SaveSettings +void +MainWindow::SaveSettings(BMessage* message) +{ + // make sure the positioning info is correct + _GetLocation(); + // store window position + if (message->ReplacePoint("window position", fScreenPosition) != B_OK) + message->AddPoint("window position", fScreenPosition); + + if (message->ReplaceFloat("border distance", fBorderDist) != B_OK) + message->AddFloat("border distance", fBorderDist); + + // store window frame + if (message->ReplaceRect("window frame", Frame()) != B_OK) + message->AddRect("window frame", Frame()); + + // store name panel frame + if (message->ReplaceRect("name panel frame", fNamePanelFrame) != B_OK) + message->AddRect("name panel frame", fNamePanelFrame); + + if (message->ReplaceInt32("window look", Look()) != B_OK) + message->AddInt32("window look", Look()); + + // store buttons + message->RemoveName("path"); + message->RemoveName("description"); + message->RemoveName("signature"); + for (int32 i = 0; LaunchButton* button = fPadView->ButtonAt(i); i++) { + BPath path(button->Ref()); + if (path.InitCheck() >= B_OK) + message->AddString("path", path.Path()); + else + message->AddString("path", ""); + message->AddString("description", button->Description()); + + if (button->AppSignature()) + message->AddString("signature", button->AppSignature()); + else + message->AddString("signature", ""); + } + + // store auto raise setting + if (message->ReplaceBool("auto raise", fAutoRaise) != B_OK) + message->AddBool("auto raise", fAutoRaise); + + // store workspace setting + if (message->ReplaceBool("all workspaces", fShowOnAllWorkspaces) != B_OK) + message->AddBool("all workspaces", fShowOnAllWorkspaces); + if (message->ReplaceInt32("workspaces", Workspaces()) != B_OK) + message->AddInt32("workspaces", Workspaces()); +} + +// _GetLocation +void +MainWindow::_GetLocation() +{ + BRect frame = Frame(); + BPoint origin = frame.LeftTop(); + BPoint center(origin.x + frame.Width() / 2.0, origin.y + frame.Height() / 2.0); + BScreen screen(this); + BRect screenFrame = screen.Frame(); + fScreenPosition.x = center.x / screenFrame.Width(); + fScreenPosition.y = center.y / screenFrame.Height(); + if (fabs(0.5 - fScreenPosition.x) > fabs(0.5 - fScreenPosition.y)) { + // nearest to left or right border + if (fScreenPosition.x < 0.5) + fBorderDist = frame.left - screenFrame.left; + else + fBorderDist = screenFrame.right - frame.right; + } else { + // nearest to top or bottom border + if (fScreenPosition.y < 0.5) + fBorderDist = frame.top - screenFrame.top; + else + fBorderDist = screenFrame.bottom - frame.bottom; + } +} + +// _AdjustLocation +void +MainWindow::_AdjustLocation(BRect frame) +{ + BScreen screen(this); + BRect screenFrame = screen.Frame(); + BPoint center(fScreenPosition.x * screenFrame.Width(), + fScreenPosition.y * screenFrame.Height()); + BPoint frameCenter(frame.left + frame.Width() / 2.0, + frame.top + frame.Height() / 2.0); + frame.OffsetBy(center - frameCenter); + // ignore border dist when distance too large + if (fBorderDist < 10.0) { + // see which border we mean depending on screen position + BPoint offset(0.0, 0.0); + if (fabs(0.5 - fScreenPosition.x) > fabs(0.5 - fScreenPosition.y)) { + // left or right border + if (fScreenPosition.x < 0.5) + offset.x = (screenFrame.left + fBorderDist) - frame.left; + else + offset.x = (screenFrame.right - fBorderDist) - frame.right; + } else { + // top or bottom border + if (fScreenPosition.y < 0.5) + offset.y = (screenFrame.top + fBorderDist) - frame.top; + else + offset.y = (screenFrame.bottom - fBorderDist) - frame.bottom; + } + frame.OffsetBy(offset); + } + + make_sure_frame_is_on_screen(frame, this); + + MoveTo(frame.LeftTop()); + ResizeTo(frame.Width(), frame.Height()); +} + diff --git a/src/apps/launchbox/MainWindow.h b/src/apps/launchbox/MainWindow.h new file mode 100644 index 0000000000..9dd0420312 --- /dev/null +++ b/src/apps/launchbox/MainWindow.h @@ -0,0 +1,79 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef MAIN_WINDOW_H +#define MAIN_WINDOW_H + +#include + +class PadView; + +enum { + MSG_SHOW_BORDER = 'shbr', + MSG_HIDE_BORDER = 'hdbr', + + MSG_TOGGLE_AUTORAISE = 'tgar', + MSG_SHOW_ON_ALL_WORKSPACES = 'awrk', + + MSG_SET_DESCRIPTION = 'dscr', + + MSG_ADD_WINDOW = 'addw', +}; + +class MainWindow : public BWindow { + public: + MainWindow(const char* name, + BRect frame); + MainWindow(const char* name, + BRect frame, BMessage* settings); + virtual ~MainWindow(); + + // BWindow interface + virtual bool QuitRequested(); + virtual void MessageReceived(BMessage* message); + + virtual void Show(); + virtual void ScreenChanged(BRect frame, color_space format); + virtual void WorkspaceActivated(int32 workspace, bool active); + virtual void FrameMoved(BPoint origin); + virtual void FrameResized(float width, + float height); + + // MainWindow + void ToggleAutoRaise(); + bool AutoRaise() const + { return fAutoRaise; } + bool ShowOnAllWorkspaces() const + { return fShowOnAllWorkspaces; } + + BPoint ScreenPosition() const + { return fScreenPosition; } + + bool LoadSettings(const BMessage* message); + void SaveSettings(BMessage* message); + BMessage* Settings() const + { return fSettings; } + + private: + void _GetLocation(); + void _AdjustLocation(BRect frame); + + BMessage* fSettings; + PadView* fPadView; + int32 fLastID; + + float fBorderDist; + BPoint fScreenPosition; // not really the position, 0...1 = left...right + + BRect fNamePanelFrame; + + bool fAutoRaise; + bool fShowOnAllWorkspaces; +}; + +#endif // MAIN_WINDOW_H diff --git a/src/apps/launchbox/NamePanel.cpp b/src/apps/launchbox/NamePanel.cpp new file mode 100644 index 0000000000..46248b57b2 --- /dev/null +++ b/src/apps/launchbox/NamePanel.cpp @@ -0,0 +1,149 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include + +#include +#include +#include +#include +#include + +#include "NamePanel.h" + +enum { + MSG_PANEL_OK, + MSG_PANEL_CANCEL, +}; + +// constructor +NamePanel::NamePanel(const char* label, + const char* text, + BWindow *window, + BHandler* target, + BMessage* message, + BRect frame) + : Panel(frame, "Name Panel", + B_MODAL_WINDOW_LOOK, B_MODAL_SUBSET_WINDOW_FEEL, + B_ASYNCHRONOUS_CONTROLS | B_NOT_V_RESIZABLE), + fWindow(window), + fTarget(target), + fMessage(message) +{ + BButton* defaultButton = new BButton("Ok", new BMessage(MSG_PANEL_OK)); + BButton* cancelButton = new BButton("Cancel", new BMessage(MSG_PANEL_CANCEL)); + fNameTC = new BTextControl(label, text, NULL); + + BView* topView = BGroupLayoutBuilder(B_VERTICAL, 10) + .AddGlue() + + // controls + .Add(BGroupLayoutBuilder(B_HORIZONTAL, 5) + .Add(BSpaceLayoutItem::CreateHorizontalStrut(5)) + + // text control + .Add(fNameTC->CreateLabelLayoutItem()) + .Add(fNameTC->CreateTextViewLayoutItem()) + + .Add(BSpaceLayoutItem::CreateHorizontalStrut(5)) + ) + + .AddGlue() + + // buttons + .Add(BGroupLayoutBuilder(B_HORIZONTAL, 5) + .Add(BSpaceLayoutItem::CreateGlue()) + .Add(cancelButton) + .Add(defaultButton) + .Add(BSpaceLayoutItem::CreateHorizontalStrut(5)) + ) + + .AddGlue() + ; + + SetLayout(new BGroupLayout(B_HORIZONTAL)); + AddChild(topView); + + SetDefaultButton(defaultButton); + fNameTC->MakeFocus(true); + + if (fWindow && fWindow->Lock()) { + fSavedTargetWindowFeel = fWindow->Feel(); + if (fSavedTargetWindowFeel != B_NORMAL_WINDOW_FEEL) + fWindow->SetFeel(B_NORMAL_WINDOW_FEEL); + fWindow->Unlock(); + } + + AddToSubset(fWindow); + Hide(); + Show(); + if (Lock()) { + frame = _CalculateFrame(Frame()); + MoveTo(frame.LeftTop()); +// ResizeTo(frame.Width(), frame.Height()); + Show(); + Unlock(); + } +} + +// destructor +NamePanel::~NamePanel() +{ + if (fWindow && fWindow->Lock()) { + fWindow->SetFeel(fSavedTargetWindowFeel); + fWindow->Unlock(); + } + delete fMessage; +} + +// MessageReceived +void NamePanel::MessageReceived(BMessage* message) +{ + switch (message->what) { + case MSG_PANEL_CANCEL: + Quit(); + break; + case MSG_PANEL_OK: { + if (!fTarget) + fTarget = fWindow; + BLooper* looper = fTarget ? fTarget->Looper() : NULL; + if (fMessage && looper) { + BMessage cloneMessage(*fMessage); + cloneMessage.AddString("name", fNameTC->Text()); + cloneMessage.AddRect("frame", Frame()); + looper->PostMessage(&cloneMessage, fTarget); + } + Quit(); + break; + } + default: + Panel::MessageReceived(message); + } +} + +// _CalculateFrame +BRect +NamePanel::_CalculateFrame(BRect frame) +{ + BScreen screen(this); + BRect screenFrame = screen.Frame(); + if (!frame.IsValid()) + frame.Set(-1000.0, -1000.0, -900.0, -900.0); + if (!screenFrame.Contains(frame)) { + float width = frame.Width(); + float height = frame.Height(); + BPoint center; + center.x = screenFrame.left + screenFrame.Width() / 2.0; + center.y = screenFrame.top + screenFrame.Height() / 4.0; + frame.left = center.x - width / 2.0; + frame.right = frame.left + width; + frame.top = center.y - height / 2.0; + frame.bottom = frame.top + height; + } + return frame; +} diff --git a/src/apps/launchbox/NamePanel.h b/src/apps/launchbox/NamePanel.h new file mode 100644 index 0000000000..7ab2f416c1 --- /dev/null +++ b/src/apps/launchbox/NamePanel.h @@ -0,0 +1,39 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef NAME_PANEL_H +#define NAME_PANEL_H + +#include "Panel.h" + +class BTextControl; + +class NamePanel : public Panel { + public: + NamePanel(const char* label, + const char* text, + BWindow* window, + BHandler* target, + BMessage* message, + BRect frame = BRect(-1000.0, -1000.0, -900.0, -900.0)); + virtual ~NamePanel(); + + virtual void MessageReceived(BMessage *message); + + private: + BRect _CalculateFrame(BRect frame); + + BTextControl* fNameTC; + BWindow* fWindow; + BHandler* fTarget; + BMessage* fMessage; + + window_feel fSavedTargetWindowFeel; +}; + +#endif // NAME_PANEL_H diff --git a/src/apps/launchbox/PadView.cpp b/src/apps/launchbox/PadView.cpp new file mode 100644 index 0000000000..904d1b3018 --- /dev/null +++ b/src/apps/launchbox/PadView.cpp @@ -0,0 +1,350 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "PadView.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "LaunchButton.h" +#include "MainWindow.h" + +bigtime_t kActivationDelay = 40000; + +// constructor +PadView::PadView(const char* name) + : BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE, + new BGroupLayout(B_VERTICAL, 4)), + fDragging(false), + fClickTime(0) +{ + SetViewColor(B_TRANSPARENT_32_BIT); + SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + get_click_speed(&kActivationDelay); + + GetLayout()->AddItem(BSpaceLayoutItem::CreateVerticalStrut(5)); +} + +// destructor +PadView::~PadView() +{ +} + +// Draw +void +PadView::Draw(BRect updateRect) +{ + rgb_color background = LowColor(); + rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT); + rgb_color shadow = tint_color(background, B_DARKEN_2_TINT); + BRect r(Bounds()); + BeginLineArray(4); + AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), light); + AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), light); + AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), shadow); + AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), shadow); + EndLineArray(); + r.InsetBy(1.0, 1.0); + StrokeRect(r, B_SOLID_LOW); + r.InsetBy(1.0, 1.0); + // dots along top + BPoint dot = r.LeftTop(); + BPoint stop = r.RightTop(); + int32 num = 1; + while (dot.x <= stop.x) { + rgb_color col1; + rgb_color col2; + switch (num) { + case 1: + col1 = shadow; + col2 = background; + break; + case 2: + col1 = background; + col2 = light; + break; + case 3: + col1 = background; + col2 = background; + num = 0; + break; + } + SetHighColor(col1); + StrokeLine(dot, dot, B_SOLID_HIGH); + SetHighColor(col2); + dot.y++; + StrokeLine(dot, dot, B_SOLID_HIGH); + dot.y++; + StrokeLine(dot, dot, B_SOLID_LOW); + dot.y++; + SetHighColor(col1); + StrokeLine(dot, dot, B_SOLID_HIGH); + dot.y++; + SetHighColor(col2); + StrokeLine(dot, dot, B_SOLID_HIGH); + dot.y -= 4.0; + // next pixel + num++; + dot.x++; + } + r.top += 5.0; + FillRect(r, B_SOLID_LOW); +} + +// MessageReceived +void +PadView::MessageReceived(BMessage* message) +{ + switch (message->what) { + default: + BView::MessageReceived(message); + break; + } +} + +// MouseDown +void +PadView::MouseDown(BPoint where) +{ + if (BWindow* window = Window()) { + BRegion region; + GetClippingRegion(®ion); + if (region.Contains(where)) { + bool handle = true; + for (int32 i = 0; BView* child = ChildAt(i); i++) { + if (child->Frame().Contains(where)) { + handle = false; + break; + } + } + if (handle) { + if (BMessage* message = window->CurrentMessage()) { + uint32 buttons; + message->FindInt32("buttons", (int32*)&buttons); + if (buttons & B_SECONDARY_MOUSE_BUTTON) { + BRect r = Bounds(); + r.InsetBy(2.0, 2.0); + r.top += 6.0; + if (r.Contains(where)) { + DisplayMenu(where); + } else { + // sends the window to the back + window->Activate(false); + } + } else { + if (system_time() - fClickTime < kActivationDelay) { + window->Minimize(true); + fClickTime = 0; + } else { + window->Activate(); + fDragOffset = ConvertToScreen(where) - window->Frame().LeftTop(); + fDragging = true; + SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); + fClickTime = system_time(); + } + } + } + } + } + } +} + +// MouseUp +void +PadView::MouseUp(BPoint where) +{ + if (BWindow* window = Window()) { + uint32 buttons; + window->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); + if (buttons & B_PRIMARY_MOUSE_BUTTON + && system_time() - fClickTime < kActivationDelay + && window->IsActive()) + window->Activate(); + } + fDragging = false; +} + +// MouseMoved +void +PadView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) +{ + if (MainWindow* window = dynamic_cast(Window())) { + if (fDragging) { + window->MoveTo(ConvertToScreen(where) - fDragOffset); + } else if (window->AutoRaise()) { + where = ConvertToScreen(where); + BScreen screen(window); + BRect frame = screen.Frame(); + BRect windowFrame = window->Frame(); + if (where.x == frame.left || where.x == frame.right + || where.y == frame.top || where.y == frame.bottom) { + BPoint position = window->ScreenPosition(); + bool raise = false; + if (fabs(0.5 - position.x) > fabs(0.5 - position.y)) { + // left or right border + if (where.y >= windowFrame.top && where.y <= windowFrame.bottom) { + if (position.x < 0.5 && where.x == frame.left) + raise = true; + else if (position.x > 0.5 && where.x == frame.right) + raise = true; + } + } else { + // top or bottom border + if (where.x >= windowFrame.left && where.x <= windowFrame.right) { + if (position.y < 0.5 && where.y == frame.top) + raise = true; + else if (position.y > 0.5 && where.y == frame.top) + raise = true; + } + } + if (raise) + window->Activate(); + } + } + } +} + +// AddButton +void +PadView::AddButton(LaunchButton* button, LaunchButton* beforeButton) +{ + BLayout* layout = GetLayout(); + if (beforeButton) + layout->AddView(layout->IndexOfView(beforeButton), button); + else + layout->AddView(button); +} + +// RemoveButton +bool +PadView::RemoveButton(LaunchButton* button) +{ + return GetLayout()->RemoveView(button); +} + +// ButtonAt +LaunchButton* +PadView::ButtonAt(int32 index) const +{ + return dynamic_cast(ChildAt(index)); +} + +// DisplayMenu +void +PadView::DisplayMenu(BPoint where, LaunchButton* button) const +{ + if (MainWindow* window = dynamic_cast(Window())) { + LaunchButton* nearestButton = button; + if (!nearestButton) { + // find the nearest button + for (int32 i = 0; (nearestButton = ButtonAt(i)); i++) { + if (nearestButton->Frame().top > where.y) + break; + } + } + BPopUpMenu* menu = new BPopUpMenu("launch popup", false, false); + // add button + BMessage* message = new BMessage(MSG_ADD_SLOT); + message->AddPointer("be:source", (void*)nearestButton); + BMenuItem* item = new BMenuItem("Add Button Here", message); + item->SetTarget(window); + menu->AddItem(item); + // button options + if (button) { + // remove button + message = new BMessage(MSG_CLEAR_SLOT); + message->AddPointer("be:source", (void*)button); + item = new BMenuItem("Clear Button", message); + item->SetTarget(window); + menu->AddItem(item); + // remove button + message = new BMessage(MSG_REMOVE_SLOT); + message->AddPointer("be:source", (void*)button); + item = new BMenuItem("Remove Button", message); + item->SetTarget(window); + menu->AddItem(item); + if (button->Ref()) { + message = new BMessage(MSG_SET_DESCRIPTION); + message->AddPointer("be:source", (void*)button); + item = new BMenuItem("Set Description"B_UTF8_ELLIPSIS, message); + item->SetTarget(window); + menu->AddItem(item); + } + } + menu->AddSeparatorItem(); + // window settings + BMenu* settingsM = new BMenu("Settings"); + settingsM->SetFont(be_plain_font); + + uint32 what = window->Look() == B_BORDERED_WINDOW_LOOK ? MSG_SHOW_BORDER : MSG_HIDE_BORDER; + item = new BMenuItem("Show Window Border", new BMessage(what)); + item->SetTarget(window); + item->SetMarked(what == MSG_HIDE_BORDER); + settingsM->AddItem(item); + + item = new BMenuItem("Auto Raise", new BMessage(MSG_TOGGLE_AUTORAISE)); + item->SetTarget(window); + item->SetMarked(window->AutoRaise()); + settingsM->AddItem(item); + + item = new BMenuItem("Show On All Workspaces", new BMessage(MSG_SHOW_ON_ALL_WORKSPACES)); + item->SetTarget(window); + item->SetMarked(window->ShowOnAllWorkspaces()); + settingsM->AddItem(item); + + menu->AddItem(settingsM); + + menu->AddSeparatorItem(); + + // pad commands + BMenu* padM = new BMenu("Pad"); + padM->SetFont(be_plain_font); + // new pad + item = new BMenuItem("New", new BMessage(MSG_ADD_WINDOW)); + item->SetTarget(be_app); + padM->AddItem(item); + // new pad + item = new BMenuItem("Clone", new BMessage(MSG_ADD_WINDOW)); + item->SetTarget(window); + padM->AddItem(item); + padM->AddSeparatorItem(); + // close + item = new BMenuItem("Close", new BMessage(B_QUIT_REQUESTED)); + item->SetTarget(window); + padM->AddItem(item); + menu->AddItem(padM); + // app commands + BMenu* appM = new BMenu("LaunchBox"); + appM->SetFont(be_plain_font); + // about + item = new BMenuItem("About", new BMessage(B_ABOUT_REQUESTED)); + item->SetTarget(be_app); + appM->AddItem(item); + // quit + item = new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED)); + item->SetTarget(be_app); + appM->AddItem(item); + menu->AddItem(appM); + // finish popup + menu->SetAsyncAutoDestruct(true); + menu->SetFont(be_plain_font); + where = ConvertToScreen(where); + BRect mouseRect(where, where); + mouseRect.InsetBy(-4.0, -4.0); + menu->Go(where, true, false, mouseRect, true); + } +} + diff --git a/src/apps/launchbox/PadView.h b/src/apps/launchbox/PadView.h new file mode 100644 index 0000000000..30a372ae89 --- /dev/null +++ b/src/apps/launchbox/PadView.h @@ -0,0 +1,44 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef PAD_VIEW_H +#define PAD_VIEW_H + +#include + +class LaunchButton; + +class PadView : public BView { + public: + PadView(const char* name); + virtual ~PadView(); + + // BView interface + virtual void Draw(BRect updateRect); + virtual void MessageReceived(BMessage* message); + virtual void MouseDown(BPoint where); + virtual void MouseUp(BPoint where); + virtual void MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage); + + // PadView + void AddButton(LaunchButton* button, + LaunchButton* beforeButton = NULL); + bool RemoveButton(LaunchButton* button); + LaunchButton* ButtonAt(int32 index) const; + + void DisplayMenu(BPoint where, + LaunchButton* button = NULL) const; + + private: + BPoint fDragOffset; + bool fDragging; + bigtime_t fClickTime; +}; + +#endif // PAD_VIEW_H diff --git a/src/apps/launchbox/Panel.cpp b/src/apps/launchbox/Panel.cpp new file mode 100644 index 0000000000..e179bf49eb --- /dev/null +++ b/src/apps/launchbox/Panel.cpp @@ -0,0 +1,86 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "Panel.h" + +#include + +#include +#include +#include + +class EscapeFilter : public BMessageFilter { + public: + EscapeFilter(Panel* target) + : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE), + fPanel(target) + { + } + virtual ~EscapeFilter() + { + } + virtual filter_result Filter(BMessage* message, BHandler** target) + { + filter_result result = B_DISPATCH_MESSAGE; + switch (message->what) { + case B_KEY_DOWN: + case B_UNMAPPED_KEY_DOWN: { + uint32 key; + if (message->FindInt32("raw_char", (int32*)&key) >= B_OK) { + if (key == B_ESCAPE) { + result = B_SKIP_MESSAGE; + fPanel->Cancel(); + } + } + break; + } + default: + break; + } + return result; + } + private: + Panel* fPanel; +}; + +// constructor +Panel::Panel(BRect frame, const char* title, + window_type type, uint32 flags, + uint32 workspace) + : BWindow(frame, title, type, flags, workspace) +{ + _InstallFilter(); +} + +// constructor +Panel::Panel(BRect frame, const char* title, + window_look look, window_feel feel, + uint32 flags, uint32 workspace) + : BWindow(frame, title, look, feel, flags, workspace) +{ + _InstallFilter(); +} + +// destructor +Panel::~Panel() +{ +} + +// MessageReceived +void +Panel::Cancel() +{ + PostMessage(B_QUIT_REQUESTED); +} + +// _InstallFilter +void +Panel::_InstallFilter() +{ + AddCommonFilter(new EscapeFilter(this)); +} diff --git a/src/apps/launchbox/Panel.h b/src/apps/launchbox/Panel.h new file mode 100644 index 0000000000..f0cc3b1552 --- /dev/null +++ b/src/apps/launchbox/Panel.h @@ -0,0 +1,37 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef PANEL_H +#define PANEL_H + +#include + +class Panel : public BWindow { + public: + Panel(BRect frame, + const char* title, + window_type type, + uint32 flags, + uint32 workspace = B_CURRENT_WORKSPACE); + Panel(BRect frame, + const char* title, + window_look look, + window_feel feel, + uint32 flags, + uint32 workspace = B_CURRENT_WORKSPACE); + virtual ~Panel(); + + // Panel + virtual void Cancel(); + + private: + void _InstallFilter(); + +}; + +#endif // PANEL_H diff --git a/src/apps/launchbox/main.cpp b/src/apps/launchbox/main.cpp new file mode 100644 index 0000000000..fad82aa576 --- /dev/null +++ b/src/apps/launchbox/main.cpp @@ -0,0 +1,21 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + + +#include + +#include "App.h" + +int +main(int argc, char** argv) +{ + new App(); + be_app->Run(); + delete be_app; + return 0; +} diff --git a/src/apps/launchbox/run b/src/apps/launchbox/run new file mode 100755 index 0000000000..a773cda253 --- /dev/null +++ b/src/apps/launchbox/run @@ -0,0 +1,18 @@ +#!/bin/sh + +../../../generated/tests/libbe_test/x86/apps/run_haiku_registrar || exit + +if test -f ../../../generated/tests/libbe_test/x86/apps/haiku_app_server; then + ../../../generated/tests/libbe_test/x86/apps//haiku_app_server & +else + echo "You need to \"TARGET_PLATFORM=libbe_test jam install-test-apps\" first." +fi + +sleep 1s + +if test -f ../../../generated/tests/libbe_test/x86/apps/LaunchBox; then + ../../../generated/tests/libbe_test/x86/apps/LaunchBox +else + echo "You need to \"TARGET_PLATFORM=libbe_test jam install-test-apps\" first." +fi + diff --git a/src/apps/launchbox/support.cpp b/src/apps/launchbox/support.cpp new file mode 100644 index 0000000000..eb988ea14c --- /dev/null +++ b/src/apps/launchbox/support.cpp @@ -0,0 +1,115 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "support.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +// load_settings +status_t +load_settings(BMessage* message, const char* fileName, const char* folder) +{ + status_t ret = B_BAD_VALUE; + if (message) { + BPath path; + if ((ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path)) == B_OK) { + // passing folder is optional + if (folder) + ret = path.Append( folder ); + if (ret == B_OK && (ret = path.Append(fileName)) == B_OK ) { + BFile file(path.Path(), B_READ_ONLY); + if ((ret = file.InitCheck()) == B_OK) { + ret = message->Unflatten(&file); + file.Unset(); + } + } + } + } + return ret; +} + +// save_settings +status_t +save_settings(BMessage* message, const char* fileName, const char* folder) +{ + status_t ret = B_BAD_VALUE; + if (message) { + BPath path; + if ((ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path)) == B_OK) { + // passing folder is optional + if (folder && (ret = path.Append(folder)) == B_OK) + ret = create_directory(path.Path(), 0777); + if (ret == B_OK && (ret = path.Append(fileName)) == B_OK) { + BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); + if ((ret = file.InitCheck()) == B_OK) { + ret = message->Flatten(&file); + file.Unset(); + } + } + } + } + return ret; +} + +// stroke_frame +void +stroke_frame(BView* v, BRect r, rgb_color left, rgb_color top, + rgb_color right, rgb_color bottom) +{ + if (v && r.IsValid()) { + v->BeginLineArray(4); + v->AddLine(BPoint(r.left, r.bottom), + BPoint(r.left, r.top), left); + v->AddLine(BPoint(r.left + 1.0, r.top), + BPoint(r.right, r.top), top); + v->AddLine(BPoint(r.right, r.top + 1.0), + BPoint(r.right, r.bottom), right); + v->AddLine(BPoint(r.right - 1.0, r.bottom), + BPoint(r.left + 1.0, r.bottom), bottom); + v->EndLineArray(); + } +} + +// make_sure_frame_is_on_screen +bool +make_sure_frame_is_on_screen(BRect& frame, BWindow* window = NULL) +{ + BScreen* screen = window ? new BScreen(window) : new BScreen(B_MAIN_SCREEN_ID); + bool success = false; + if (frame.IsValid() && screen->IsValid()) { + BRect screenFrame = screen->Frame(); + if (!screenFrame.Contains(frame)) { + // make sure frame fits in the screen + if (frame.Width() > screenFrame.Width()) + frame.right -= frame.Width() - screenFrame.Width() + 10.0; + if (frame.Height() > screenFrame.Height()) + frame.bottom -= frame.Height() - screenFrame.Height() + 30.0; + // frame is now at the most the size of the screen + if (frame.right > screenFrame.right) + frame.OffsetBy(-(frame.right - screenFrame.right), 0.0); + if (frame.bottom > screenFrame.bottom) + frame.OffsetBy(0.0, -(frame.bottom - screenFrame.bottom)); + if (frame.left < screenFrame.left) + frame.OffsetBy((screenFrame.left - frame.left), 0.0); + if (frame.top < screenFrame.top) + frame.OffsetBy(0.0, (screenFrame.top - frame.top)); + } + success = true; + } + delete screen; + return success; +} + diff --git a/src/apps/launchbox/support.h b/src/apps/launchbox/support.h new file mode 100644 index 0000000000..d3e179e7bb --- /dev/null +++ b/src/apps/launchbox/support.h @@ -0,0 +1,32 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef SUPPORT_H +#define SUPPORT_H + +#include +#include + +class BMessage; +class BView; +class BWindow; + +status_t load_settings(BMessage* message, const char* fileName, + const char* folder = NULL); + +status_t save_settings(BMessage* message, const char* fileName, + const char* folder = NULL); + +// looper of view must be locked! +void stroke_frame(BView* view, BRect frame, + rgb_color left, rgb_color top, + rgb_color right, rgb_color bottom); + +bool make_sure_frame_is_on_screen(BRect& frame, BWindow* window = NULL); + +#endif // SUPPORT_H