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 #include "LaunchButton.h" 7 8 #include <malloc.h> // string.h is not enough on Haiku?!? 9 #include <stdio.h> 10 #include <string.h> 11 12 #include <Application.h> 13 #include <AppDefs.h> 14 #include <AppFileInfo.h> 15 #include <Bitmap.h> 16 #include <Catalog.h> 17 #include <File.h> 18 #include <Node.h> 19 #include <NodeInfo.h> 20 #include <Region.h> 21 #include <Roster.h> 22 #include <Window.h> 23 24 #include "PadView.h" 25 #include "MainWindow.h" 26 27 #undef B_TRANSLATE_CONTEXT 28 #define B_TRANSLATE_CONTEXT "LaunchBox" 29 30 static const float kDragStartDist = 10.0; 31 static const float kDragBitmapAlphaScale = 0.6; 32 static const char* kEmptyHelpString = B_TRANSLATE("You can drag an icon here."); 33 34 35 bigtime_t 36 LaunchButton::sClickSpeed = 0; 37 38 bool 39 LaunchButton::sIgnoreDoubleClick = true; 40 41 42 LaunchButton::LaunchButton(const char* name, uint32 id, const char* label, 43 BMessage* message, BHandler* target) 44 : 45 BIconButton(name, id, 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) { 170 if (PadView* parent = dynamic_cast<PadView*>(Parent())) { 171 parent->DisplayMenu(ConvertToParent(where), this); 172 _ClearFlags(STATE_INSIDE); 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 (_HasFlags(STATE_TRACKING) && fRef) { 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 _ClearFlags(STATE_PRESSED | STATE_TRACKING | STATE_INSIDE); 220 // create drag bitmap and message 221 if (BBitmap* bitmap = Bitmap()) { 222 if (bitmap->ColorSpace() == B_RGB32) { 223 // make semitransparent 224 uint8* bits = (uint8*)bitmap->Bits(); 225 uint32 width = bitmap->Bounds().IntegerWidth() + 1; 226 uint32 height = bitmap->Bounds().IntegerHeight() + 1; 227 uint32 bpr = bitmap->BytesPerRow(); 228 for (uint32 y = 0; y < height; y++) { 229 uint8* bitsHandle = bits; 230 for (uint32 x = 0; x < width; x++) { 231 bitsHandle[3] = uint8(bitsHandle[3] 232 * kDragBitmapAlphaScale); 233 bitsHandle += 4; 234 } 235 bits += bpr; 236 } 237 } 238 BMessage message(B_SIMPLE_DATA); 239 message.AddPointer("button", this); 240 message.AddRef("refs", fRef); 241 // DragMessage takes ownership of the bitmap. 242 DragMessage(&message, bitmap, B_OP_ALPHA, fDragStart); 243 } 244 } 245 } 246 BIconButton::MouseMoved(where, transit, dragMessage); 247 } 248 249 250 BSize 251 LaunchButton::MinSize() 252 { 253 return PreferredSize(); 254 } 255 256 257 BSize 258 LaunchButton::PreferredSize() 259 { 260 float minWidth = fIconSize; 261 float minHeight = fIconSize; 262 263 float hPadding = max_c(6.0, ceilf(minHeight / 3.0)); 264 float vPadding = max_c(6.0, ceilf(minWidth / 3.0)); 265 266 if (Label().CountChars() > 0) { 267 font_height fh; 268 GetFontHeight(&fh); 269 minHeight += ceilf(fh.ascent + fh.descent) + vPadding; 270 minWidth += StringWidth(Label().String()) + vPadding; 271 } 272 273 return BSize(minWidth + hPadding, minHeight + vPadding); 274 } 275 276 277 BSize 278 LaunchButton::MaxSize() 279 { 280 return PreferredSize(); 281 } 282 283 284 // #pragma mark - 285 286 287 void 288 LaunchButton::SetTo(const entry_ref* ref) 289 { 290 free(fAppSig); 291 fAppSig = NULL; 292 293 delete fRef; 294 if (ref) { 295 fRef = new entry_ref(*ref); 296 // follow links 297 BEntry entry(fRef, true); 298 entry.GetRef(fRef); 299 300 _UpdateIcon(fRef); 301 // see if this is an application 302 BFile file(ref, B_READ_ONLY); 303 BAppFileInfo info; 304 if (info.SetTo(&file) == B_OK) { 305 char mimeSig[B_MIME_TYPE_LENGTH]; 306 if (info.GetSignature(mimeSig) == B_OK) { 307 SetTo(mimeSig, false); 308 } else { 309 fprintf(stderr, "no MIME signature for '%s'\n", fRef->name); 310 } 311 } else { 312 fprintf(stderr, "no BAppFileInfo for '%s'\n", fRef->name); 313 } 314 } else { 315 fRef = NULL; 316 ClearIcon(); 317 } 318 _UpdateToolTip(); 319 _NotifySettingsChanged(); 320 } 321 322 323 entry_ref* 324 LaunchButton::Ref() const 325 { 326 return fRef; 327 } 328 329 330 void 331 LaunchButton::SetTo(const char* appSig, bool updateIcon) 332 { 333 if (appSig) { 334 free(fAppSig); 335 fAppSig = strdup(appSig); 336 if (updateIcon) { 337 entry_ref ref; 338 if (be_roster->FindApp(fAppSig, &ref) == B_OK) 339 SetTo(&ref); 340 } 341 } 342 _UpdateToolTip(); 343 _NotifySettingsChanged(); 344 } 345 346 347 void 348 LaunchButton::SetDescription(const char* text) 349 { 350 fDescription.SetTo(text); 351 _UpdateToolTip(); 352 _NotifySettingsChanged(); 353 } 354 355 356 void 357 LaunchButton::SetIconSize(uint32 size) 358 { 359 if (fIconSize == size) 360 return; 361 362 fIconSize = size; 363 _UpdateIcon(fRef); 364 365 InvalidateLayout(); 366 Invalidate(); 367 } 368 369 370 void 371 LaunchButton::SetIgnoreDoubleClick(bool refuse) 372 { 373 sIgnoreDoubleClick = refuse; 374 } 375 376 377 // #pragma mark - 378 379 380 void 381 LaunchButton::_UpdateToolTip() 382 { 383 // TODO: This works around a bug in the tool tip management. 384 // Remove when fixed (although no harm done...) 385 HideToolTip(); 386 SetToolTip(static_cast<BToolTip*>(NULL)); 387 388 if (fRef) { 389 BString helper(fRef->name); 390 if (fDescription.CountChars() > 0) { 391 if (fDescription != helper) 392 helper << "\n\n" << fDescription.String(); 393 } else { 394 BFile file(fRef, B_READ_ONLY); 395 BAppFileInfo appFileInfo; 396 version_info info; 397 if (appFileInfo.SetTo(&file) == B_OK 398 && appFileInfo.GetVersionInfo(&info, 399 B_APP_VERSION_KIND) == B_OK 400 && strlen(info.short_info) > 0 401 && helper.Compare(info.short_info) != 0) { 402 helper << "\n\n" << info.short_info; 403 } 404 } 405 SetToolTip(helper.String()); 406 } else { 407 SetToolTip(kEmptyHelpString); 408 } 409 } 410 411 412 void 413 LaunchButton::_UpdateIcon(const entry_ref* ref) 414 { 415 BBitmap* icon = new BBitmap(BRect(0.0, 0.0, fIconSize - 1, 416 fIconSize - 1), B_RGBA32); 417 // NOTE: passing an invalid/unknown icon_size argument will cause 418 // the BNodeInfo to ignore it and just use the bitmap bounds. 419 if (BNodeInfo::GetTrackerIcon(ref, icon, (icon_size)fIconSize) == B_OK) 420 SetIcon(icon); 421 422 delete icon; 423 } 424 425 426 void 427 LaunchButton::_NotifySettingsChanged() 428 { 429 be_app->PostMessage(MSG_SETTINGS_CHANGED); 430 } 431 432 433 void 434 LaunchButton::_DrawFrame(BRect r, rgb_color col1, rgb_color col2, 435 rgb_color col3, rgb_color col4) 436 { 437 BeginLineArray(8); 438 AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), col1); 439 AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), col1); 440 AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), col2); 441 AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), col2); 442 r.InsetBy(1.0, 1.0); 443 AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), col3); 444 AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), col3); 445 AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), col4); 446 AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), col4); 447 EndLineArray(); 448 } 449