/* * Copyright 2006-2009, Stephan Aßmus * All rights reserved. Distributed under the terms of the MIT License. */ #include "LaunchButton.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "PadView.h" #include "MainWindow.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "LaunchBox" static const float kDragStartDist = 10.0; static const float kDragBitmapAlphaScale = 0.6; static const char* kEmptyHelpString = B_TRANSLATE("You can drag an icon here."); bigtime_t LaunchButton::sClickSpeed = 0; bool LaunchButton::sIgnoreDoubleClick = true; LaunchButton::LaunchButton(const char* name, const char* label, BMessage* message, BHandler* target) : BIconButton(name, label, message, target), fRef(NULL), fAppSig(NULL), fDescription(""), fAnticipatingDrop(false), fLastClickTime(0), fIconSize(DEFAULT_ICON_SIZE) { if (sClickSpeed == 0 || get_click_speed(&sClickSpeed) != B_OK) sClickSpeed = 500000; } LaunchButton::~LaunchButton() { delete fRef; free(fAppSig); } void LaunchButton::AttachedToWindow() { BIconButton::AttachedToWindow(); _UpdateToolTip(); } 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()) { BIconButton::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); } } 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 < 0) { if (fAppSig) be_roster->Launch(fAppSig, message, &team); else be_roster->Launch(fRef, message, &team); } else { app_info appInfo; if (team >= 0 && 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: BIconButton::MessageReceived(message); break; } } void LaunchButton::MouseDown(BPoint where) { bigtime_t now = system_time(); bool callInherited = true; if (sIgnoreDoubleClick && now - fLastClickTime < sClickSpeed) callInherited = false; fLastClickTime = now; if (BMessage* message = Window()->CurrentMessage()) { uint32 buttons; message->FindInt32("buttons", (int32*)&buttons); if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0 && IsInside()) { if (PadView* parent = dynamic_cast(Parent())) { parent->DisplayMenu(ConvertToParent(where), this); SetInside(false); callInherited = false; } } else { fDragStart = where; } } if (callInherited) BIconButton::MouseDown(where); } void LaunchButton::MouseUp(BPoint where) { if (fAnticipatingDrop) { fAnticipatingDrop = false; Invalidate(); } BIconButton::MouseUp(where); } 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 (IsTracking() && fRef != NULL) { BPoint diff = where - fDragStart; float dist = sqrtf(diff.x * diff.x + diff.y * diff.y); if (dist >= kDragStartDist) { // stop tracking SetTracking(false); SetPressed(false); SetInside(false); // 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 takes ownership of the bitmap. DragMessage(&message, bitmap, B_OP_ALPHA, fDragStart); } } } BIconButton::MouseMoved(where, transit, dragMessage); } BSize LaunchButton::MinSize() { return PreferredSize(); } BSize LaunchButton::PreferredSize() { float minWidth = fIconSize; float minHeight = fIconSize; float hPadding = max_c(6.0, ceilf(minHeight / 3.0)); float vPadding = max_c(6.0, ceilf(minWidth / 3.0)); if (Label() != NULL && Label()[0] != '\0') { font_height fh; GetFontHeight(&fh); minHeight += ceilf(fh.ascent + fh.descent) + vPadding; minWidth += StringWidth(Label()) + vPadding; } return BSize(minWidth + hPadding, minHeight + vPadding); } BSize LaunchButton::MaxSize() { return PreferredSize(); } // #pragma mark - void LaunchButton::SetTo(const entry_ref* ref) { 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 { fprintf(stderr, "no MIME signature for '%s'\n", fRef->name); } } else { fprintf(stderr, "no BAppFileInfo for '%s'\n", fRef->name); } } else { fRef = NULL; ClearIcon(); } _UpdateToolTip(); _NotifySettingsChanged(); } entry_ref* LaunchButton::Ref() const { return fRef; } void LaunchButton::SetTo(const char* appSig, bool updateIcon) { if (appSig) { free(fAppSig); fAppSig = strdup(appSig); if (updateIcon) { entry_ref ref; if (be_roster->FindApp(fAppSig, &ref) == B_OK) SetTo(&ref); } } _UpdateToolTip(); _NotifySettingsChanged(); } void LaunchButton::SetDescription(const char* text) { fDescription.SetTo(text); _UpdateToolTip(); _NotifySettingsChanged(); } void LaunchButton::SetIconSize(uint32 size) { if (fIconSize == size) return; fIconSize = size; _UpdateIcon(fRef); InvalidateLayout(); Invalidate(); } void LaunchButton::SetIgnoreDoubleClick(bool refuse) { sIgnoreDoubleClick = refuse; } // #pragma mark - void LaunchButton::_UpdateToolTip() { // TODO: This works around a bug in the tool tip management. // Remove when fixed (although no harm done...) HideToolTip(); SetToolTip(static_cast(NULL)); if (fRef) { BString helper(fRef->name); if (fDescription.CountChars() > 0) { if (fDescription != helper) 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.Compare(info.short_info) != 0) { helper << "\n\n" << info.short_info; } } SetToolTip(helper.String()); } else { SetToolTip(kEmptyHelpString); } } void LaunchButton::_UpdateIcon(const entry_ref* ref) { BBitmap* icon = new BBitmap(BRect(0.0, 0.0, fIconSize - 1, fIconSize - 1), B_RGBA32); // NOTE: passing an invalid/unknown icon_size argument will cause // the BNodeInfo to ignore it and just use the bitmap bounds. if (BNodeInfo::GetTrackerIcon(ref, icon, (icon_size)fIconSize) == B_OK) SetIcon(icon); delete icon; } void LaunchButton::_NotifySettingsChanged() { be_app->PostMessage(MSG_SETTINGS_CHANGED); } void LaunchButton::_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(); }