1 /* 2 * Copyright 2006-2009, Stephan Aßmus <superstippi@gmx.de> 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "LaunchButton.h" 8 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 13 #include <AppDefs.h> 14 #include <AppFileInfo.h> 15 #include <Application.h> 16 #include <Bitmap.h> 17 #include <Catalog.h> 18 #include <File.h> 19 #include <Node.h> 20 #include <NodeInfo.h> 21 #include <Region.h> 22 #include <Roster.h> 23 #include <Window.h> 24 25 #include "PadView.h" 26 #include "MainWindow.h" 27 28 29 #undef B_TRANSLATION_CONTEXT 30 #define B_TRANSLATION_CONTEXT "LaunchBox" 31 32 33 static const float kDragStartDist = 10.0; 34 static const float kDragBitmapAlphaScale = 0.6; 35 static const char* kEmptyHelpString = B_TRANSLATE("You can drag an icon here."); 36 37 38 bigtime_t LaunchButton::sClickSpeed = 0; 39 bool LaunchButton::sIgnoreDoubleClick = true; 40 41 42 LaunchButton::LaunchButton(const char* name, const char* label, 43 BMessage* message, BHandler* target) 44 : 45 BIconButton(name, label, message, target), 46 fRef(NULL), 47 fAppSig(NULL), 48 fDescription(""), 49 fAnticipatingDrop(false), 50 fLastClickTime(0), 51 fIconSize(DEFAULT_ICON_SIZE) 52 { 53 if (sClickSpeed == 0 || get_click_speed(&sClickSpeed) != B_OK) 54 sClickSpeed = 500000; 55 } 56 57 58 LaunchButton::~LaunchButton() 59 { 60 delete fRef; 61 free(fAppSig); 62 } 63 64 65 void 66 LaunchButton::AttachedToWindow() 67 { 68 BIconButton::AttachedToWindow(); 69 _UpdateToolTip(); 70 } 71 72 73 void 74 LaunchButton::Draw(BRect updateRect) 75 { 76 if (fAnticipatingDrop) { 77 rgb_color color = fRef ? ui_color(B_KEYBOARD_NAVIGATION_COLOR) 78 : (rgb_color){ 0, 130, 60, 255 }; 79 SetHighColor(color); 80 // limit clipping region to exclude the blue rect we just drew 81 BRect r(Bounds()); 82 StrokeRect(r); 83 r.InsetBy(1.0, 1.0); 84 BRegion region(r); 85 ConstrainClippingRegion(®ion); 86 } 87 if (IsValid()) { 88 BIconButton::Draw(updateRect); 89 } else { 90 rgb_color background = LowColor(); 91 rgb_color lightShadow = tint_color(background, 92 (B_NO_TINT + B_DARKEN_1_TINT) / 2.0); 93 rgb_color shadow = tint_color(background, B_DARKEN_1_TINT); 94 rgb_color light = tint_color(background, B_LIGHTEN_1_TINT); 95 BRect r(Bounds()); 96 _DrawFrame(r, shadow, light, lightShadow, lightShadow); 97 r.InsetBy(2.0, 2.0); 98 SetHighColor(lightShadow); 99 FillRect(r); 100 } 101 } 102 103 104 void 105 LaunchButton::MessageReceived(BMessage* message) 106 { 107 switch (message->what) { 108 case B_SIMPLE_DATA: 109 case B_REFS_RECEIVED: { 110 entry_ref ref; 111 if (message->FindRef("refs", &ref) == B_OK) { 112 if (fRef) { 113 if (ref != *fRef) { 114 BEntry entry(fRef, true); 115 if (entry.IsDirectory()) { 116 message->PrintToStream(); 117 // copy stuff into the directory 118 } else { 119 message->what = B_REFS_RECEIVED; 120 team_id team; 121 if (fAppSig) 122 team = be_roster->TeamFor(fAppSig); 123 else 124 team = be_roster->TeamFor(fRef); 125 if (team < 0) { 126 if (fAppSig) 127 be_roster->Launch(fAppSig, message, &team); 128 else 129 be_roster->Launch(fRef, message, &team); 130 } else { 131 app_info appInfo; 132 if (team >= 0 133 && be_roster->GetRunningAppInfo(team, 134 &appInfo) == B_OK) { 135 BMessenger messenger(appInfo.signature, 136 team); 137 if (messenger.IsValid()) 138 messenger.SendMessage(message); 139 } 140 } 141 } 142 } 143 } else { 144 SetTo(&ref); 145 } 146 } 147 break; 148 } 149 case B_PASTE: 150 case B_MODIFIERS_CHANGED: 151 default: 152 BIconButton::MessageReceived(message); 153 break; 154 } 155 } 156 157 158 void 159 LaunchButton::MouseDown(BPoint where) 160 { 161 bigtime_t now = system_time(); 162 bool callInherited = true; 163 if (sIgnoreDoubleClick && now - fLastClickTime < sClickSpeed) 164 callInherited = false; 165 fLastClickTime = now; 166 if (BMessage* message = Window()->CurrentMessage()) { 167 uint32 buttons; 168 message->FindInt32("buttons", (int32*)&buttons); 169 if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0 && IsInside()) { 170 if (PadView* parent = dynamic_cast<PadView*>(Parent())) { 171 parent->DisplayMenu(ConvertToParent(where), this); 172 SetInside(false); 173 callInherited = false; 174 } 175 } else { 176 fDragStart = where; 177 } 178 } 179 if (callInherited) 180 BIconButton::MouseDown(where); 181 } 182 183 184 void 185 LaunchButton::MouseUp(BPoint where) 186 { 187 if (fAnticipatingDrop) { 188 fAnticipatingDrop = false; 189 Invalidate(); 190 } 191 BIconButton::MouseUp(where); 192 } 193 194 195 void 196 LaunchButton::MouseMoved(BPoint where, uint32 transit, 197 const BMessage* dragMessage) 198 { 199 if ((dragMessage && (transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW)) 200 && ((dragMessage->what == B_SIMPLE_DATA 201 || dragMessage->what == B_REFS_RECEIVED) || fRef)) { 202 if (!fAnticipatingDrop) { 203 fAnticipatingDrop = true; 204 Invalidate(); 205 } 206 } 207 if (!dragMessage || (transit == B_EXITED_VIEW || transit == B_OUTSIDE_VIEW)) { 208 if (fAnticipatingDrop) { 209 fAnticipatingDrop = false; 210 Invalidate(); 211 } 212 } 213 // see if we should create a drag message 214 if (IsTracking() && fRef != NULL) { 215 BPoint diff = where - fDragStart; 216 float dist = sqrtf(diff.x * diff.x + diff.y * diff.y); 217 if (dist >= kDragStartDist) { 218 // stop tracking 219 SetTracking(false); 220 SetPressed(false); 221 SetInside(false); 222 223 // create drag bitmap and message 224 if (BBitmap* bitmap = Bitmap()) { 225 if (bitmap->ColorSpace() == B_RGB32) { 226 // make semitransparent 227 uint8* bits = (uint8*)bitmap->Bits(); 228 uint32 width = bitmap->Bounds().IntegerWidth() + 1; 229 uint32 height = bitmap->Bounds().IntegerHeight() + 1; 230 uint32 bpr = bitmap->BytesPerRow(); 231 for (uint32 y = 0; y < height; y++) { 232 uint8* bitsHandle = bits; 233 for (uint32 x = 0; x < width; x++) { 234 bitsHandle[3] = uint8(bitsHandle[3] 235 * kDragBitmapAlphaScale); 236 bitsHandle += 4; 237 } 238 bits += bpr; 239 } 240 } 241 BMessage message(B_SIMPLE_DATA); 242 message.AddPointer("button", this); 243 message.AddRef("refs", fRef); 244 // DragMessage takes ownership of the bitmap. 245 DragMessage(&message, bitmap, B_OP_ALPHA, fDragStart); 246 } 247 } 248 } 249 BIconButton::MouseMoved(where, transit, dragMessage); 250 } 251 252 253 BSize 254 LaunchButton::MinSize() 255 { 256 return PreferredSize(); 257 } 258 259 260 BSize 261 LaunchButton::PreferredSize() 262 { 263 float minWidth = fIconSize; 264 float minHeight = fIconSize; 265 266 float hPadding = max_c(6.0, ceilf(minHeight / 3.0)); 267 float vPadding = max_c(6.0, ceilf(minWidth / 3.0)); 268 269 if (Label() != NULL && Label()[0] != '\0') { 270 font_height fh; 271 GetFontHeight(&fh); 272 minHeight += ceilf(fh.ascent + fh.descent) + vPadding; 273 minWidth += StringWidth(Label()) + vPadding; 274 } 275 276 return BSize(minWidth + hPadding, minHeight + vPadding); 277 } 278 279 280 BSize 281 LaunchButton::MaxSize() 282 { 283 return PreferredSize(); 284 } 285 286 287 // #pragma mark - 288 289 290 void 291 LaunchButton::SetTo(const entry_ref* ref) 292 { 293 free(fAppSig); 294 fAppSig = NULL; 295 296 delete fRef; 297 if (ref) { 298 fRef = new entry_ref(*ref); 299 // follow links 300 BEntry entry(fRef, true); 301 entry.GetRef(fRef); 302 303 _UpdateIcon(fRef); 304 // see if this is an application 305 BFile file(ref, B_READ_ONLY); 306 BAppFileInfo info; 307 if (info.SetTo(&file) == B_OK) { 308 char mimeSig[B_MIME_TYPE_LENGTH]; 309 if (info.GetSignature(mimeSig) == B_OK) { 310 SetTo(mimeSig, false); 311 } else { 312 fprintf(stderr, "no MIME signature for '%s'\n", fRef->name); 313 } 314 } else { 315 fprintf(stderr, "no BAppFileInfo for '%s'\n", fRef->name); 316 } 317 } else { 318 fRef = NULL; 319 ClearIcon(); 320 } 321 _UpdateToolTip(); 322 _NotifySettingsChanged(); 323 } 324 325 326 entry_ref* 327 LaunchButton::Ref() const 328 { 329 return fRef; 330 } 331 332 333 void 334 LaunchButton::SetTo(const char* appSig, bool updateIcon) 335 { 336 if (appSig) { 337 free(fAppSig); 338 fAppSig = strdup(appSig); 339 if (updateIcon) { 340 entry_ref ref; 341 if (be_roster->FindApp(fAppSig, &ref) == B_OK) 342 SetTo(&ref); 343 } 344 } 345 _UpdateToolTip(); 346 _NotifySettingsChanged(); 347 } 348 349 350 void 351 LaunchButton::SetDescription(const char* text) 352 { 353 fDescription.SetTo(text); 354 _UpdateToolTip(); 355 _NotifySettingsChanged(); 356 } 357 358 359 void 360 LaunchButton::SetIconSize(uint32 size) 361 { 362 if (fIconSize == size) 363 return; 364 365 fIconSize = size; 366 _UpdateIcon(fRef); 367 368 InvalidateLayout(); 369 Invalidate(); 370 } 371 372 373 void 374 LaunchButton::SetIgnoreDoubleClick(bool refuse) 375 { 376 sIgnoreDoubleClick = refuse; 377 } 378 379 380 // #pragma mark - 381 382 383 void 384 LaunchButton::_UpdateToolTip() 385 { 386 // TODO: This works around a bug in the tool tip management. 387 // Remove when fixed (although no harm done...) 388 HideToolTip(); 389 SetToolTip(static_cast<BToolTip*>(NULL)); 390 391 if (fRef) { 392 BString helper(fRef->name); 393 if (fDescription.CountChars() > 0) { 394 if (fDescription != helper) 395 helper << "\n\n" << fDescription.String(); 396 } else { 397 BFile file(fRef, B_READ_ONLY); 398 BAppFileInfo appFileInfo; 399 version_info info; 400 if (appFileInfo.SetTo(&file) == B_OK 401 && appFileInfo.GetVersionInfo(&info, 402 B_APP_VERSION_KIND) == B_OK 403 && strlen(info.short_info) > 0 404 && helper.Compare(info.short_info) != 0) { 405 helper << "\n\n" << info.short_info; 406 } 407 } 408 SetToolTip(helper.String()); 409 } else { 410 SetToolTip(kEmptyHelpString); 411 } 412 } 413 414 415 void 416 LaunchButton::_UpdateIcon(const entry_ref* ref) 417 { 418 BBitmap* icon = new BBitmap(BRect(0.0, 0.0, fIconSize - 1, 419 fIconSize - 1), B_RGBA32); 420 // NOTE: passing an invalid/unknown icon_size argument will cause 421 // the BNodeInfo to ignore it and just use the bitmap bounds. 422 if (BNodeInfo::GetTrackerIcon(ref, icon, (icon_size)fIconSize) == B_OK) 423 SetIcon(icon); 424 425 delete icon; 426 } 427 428 429 void 430 LaunchButton::_NotifySettingsChanged() 431 { 432 be_app->PostMessage(MSG_SETTINGS_CHANGED); 433 } 434 435 436 void 437 LaunchButton::_DrawFrame(BRect r, rgb_color col1, rgb_color col2, 438 rgb_color col3, rgb_color col4) 439 { 440 BeginLineArray(8); 441 AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), col1); 442 AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), col1); 443 AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), col2); 444 AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), col2); 445 r.InsetBy(1.0, 1.0); 446 AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), col3); 447 AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), col3); 448 AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), col4); 449 AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), col4); 450 EndLineArray(); 451 }