1 /* 2 * Copyright 2006-2008, Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 */ 8 9 #include "LaunchButton.h" 10 11 #include <malloc.h> // string.h is not enough on Haiku?!? 12 #include <stdio.h> 13 #include <string.h> 14 15 #include <AppDefs.h> 16 #include <AppFileInfo.h> 17 #include <Bitmap.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 "BubbleHelper.h" 26 #include "PadView.h" 27 28 static const float kDragStartDist = 10.0; 29 static const float kDragBitmapAlphaScale = 0.6; 30 //static const char* kEmptyHelpString = "You can drag an icon here."; 31 32 bigtime_t 33 LaunchButton::fClickSpeed = 0; 34 35 // constructor 36 LaunchButton::LaunchButton(const char* name, uint32 id, const char* label, 37 BMessage* message, BHandler* target) 38 : IconButton(name, id, label, message, target), 39 fRef(NULL), 40 fAppSig(NULL), 41 fDescription(""), 42 fAnticipatingDrop(false), 43 fLastClickTime(0), 44 fIconSize(DEFAULT_ICON_SIZE) 45 { 46 if (fClickSpeed == 0 || get_click_speed(&fClickSpeed) < B_OK) 47 fClickSpeed = 500000; 48 } 49 50 // destructor 51 LaunchButton::~LaunchButton() 52 { 53 delete fRef; 54 free(fAppSig); 55 } 56 57 // AttachedToWindow 58 void 59 LaunchButton::AttachedToWindow() 60 { 61 IconButton::AttachedToWindow(); 62 _UpdateToolTip(); 63 } 64 65 // DetachedFromWindow 66 void 67 LaunchButton::DetachedFromWindow() 68 { 69 // BubbleHelper::Default()->SetHelp(this, NULL); 70 } 71 72 // Draw 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 IconButton::Draw(updateRect); 89 } else { 90 rgb_color background = LowColor(); 91 rgb_color lightShadow = tint_color(background, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0); 92 rgb_color shadow = tint_color(background, B_DARKEN_1_TINT); 93 rgb_color light = tint_color(background, B_LIGHTEN_1_TINT); 94 BRect r(Bounds()); 95 _DrawFrame(r, shadow, light, lightShadow, lightShadow); 96 r.InsetBy(2.0, 2.0); 97 SetHighColor(lightShadow); 98 FillRect(r); 99 } 100 } 101 102 // MessageReceived 103 void 104 LaunchButton::MessageReceived(BMessage* message) 105 { 106 switch (message->what) { 107 case B_SIMPLE_DATA: 108 case B_REFS_RECEIVED: { 109 entry_ref ref; 110 if (message->FindRef("refs", &ref) >= B_OK) { 111 if (fRef) { 112 if (ref != *fRef) { 113 BEntry entry(fRef, true); 114 if (entry.IsDirectory()) { 115 message->PrintToStream(); 116 // copy stuff into the directory 117 } else { 118 message->what = B_REFS_RECEIVED; 119 team_id team; 120 if (fAppSig) 121 team = be_roster->TeamFor(fAppSig); 122 else 123 team = be_roster->TeamFor(fRef); 124 if (team < B_OK) { 125 if (fAppSig) 126 be_roster->Launch(fAppSig, message, &team); 127 else 128 be_roster->Launch(fRef, message, &team); 129 } else { 130 app_info appInfo; 131 if (team >= B_OK && be_roster->GetRunningAppInfo(team, &appInfo) >= B_OK) { 132 BMessenger messenger(appInfo.signature, team); 133 if (messenger.IsValid()) 134 messenger.SendMessage(message); 135 } 136 } 137 } 138 } 139 } else { 140 SetTo(&ref); 141 } 142 } 143 break; 144 } 145 case B_PASTE: 146 case B_MODIFIERS_CHANGED: 147 default: 148 IconButton::MessageReceived(message); 149 break; 150 } 151 } 152 153 // MouseDown 154 void 155 LaunchButton::MouseDown(BPoint where) 156 { 157 bigtime_t now = system_time(); 158 bool callInherited = true; 159 if (now - fLastClickTime < fClickSpeed) 160 callInherited = false; 161 fLastClickTime = now; 162 if (BMessage* message = Window()->CurrentMessage()) { 163 uint32 buttons; 164 message->FindInt32("buttons", (int32*)&buttons); 165 if (buttons & B_SECONDARY_MOUSE_BUTTON) { 166 if (PadView* parent = dynamic_cast<PadView*>(Parent())) { 167 parent->DisplayMenu(ConvertToParent(where), this); 168 _ClearFlags(STATE_INSIDE); 169 callInherited = false; 170 } 171 } else { 172 fDragStart = where; 173 } 174 } 175 if (callInherited) 176 IconButton::MouseDown(where); 177 } 178 179 // MouseUp 180 void 181 LaunchButton::MouseUp(BPoint where) 182 { 183 if (fAnticipatingDrop) { 184 fAnticipatingDrop = false; 185 Invalidate(); 186 } 187 IconButton::MouseUp(where); 188 } 189 190 // MouseMoved 191 void 192 LaunchButton::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) 193 { 194 if ((dragMessage && (transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW)) 195 && ((dragMessage->what == B_SIMPLE_DATA 196 || dragMessage->what == B_REFS_RECEIVED) || fRef)) { 197 if (!fAnticipatingDrop) { 198 fAnticipatingDrop = true; 199 Invalidate(); 200 } 201 } 202 if (!dragMessage || (transit == B_EXITED_VIEW || transit == B_OUTSIDE_VIEW)) { 203 if (fAnticipatingDrop) { 204 fAnticipatingDrop = false; 205 Invalidate(); 206 } 207 } 208 // see if we should create a drag message 209 if (_HasFlags(STATE_TRACKING) && fRef) { 210 BPoint diff = where - fDragStart; 211 float dist = sqrtf(diff.x * diff.x + diff.y * diff.y); 212 if (dist >= kDragStartDist) { 213 // stop tracking 214 _ClearFlags(STATE_PRESSED | STATE_TRACKING | STATE_INSIDE); 215 // create drag bitmap and message 216 if (BBitmap* bitmap = Bitmap()) { 217 if (bitmap->ColorSpace() == B_RGB32) { 218 // make semitransparent 219 uint8* bits = (uint8*)bitmap->Bits(); 220 uint32 width = bitmap->Bounds().IntegerWidth() + 1; 221 uint32 height = bitmap->Bounds().IntegerHeight() + 1; 222 uint32 bpr = bitmap->BytesPerRow(); 223 for (uint32 y = 0; y < height; y++) { 224 uint8* bitsHandle = bits; 225 for (uint32 x = 0; x < width; x++) { 226 bitsHandle[3] = uint8(bitsHandle[3] * kDragBitmapAlphaScale); 227 bitsHandle += 4; 228 } 229 bits += bpr; 230 } 231 } 232 BMessage message(B_SIMPLE_DATA); 233 message.AddPointer("button", this); 234 message.AddRef("refs", fRef); 235 DragMessage(&message, bitmap, B_OP_ALPHA, fDragStart); 236 } 237 } 238 } 239 IconButton::MouseMoved(where, transit, dragMessage); 240 } 241 242 243 BSize 244 LaunchButton::MinSize() 245 { 246 return PreferredSize(); 247 } 248 249 250 BSize 251 LaunchButton::PreferredSize() 252 { 253 float minWidth = fIconSize; 254 float minHeight = fIconSize; 255 256 float hPadding = max_c(6.0, ceilf(minHeight / 3.0)); 257 float vPadding = max_c(6.0, ceilf(minWidth / 3.0)); 258 259 if (fLabel.CountChars() > 0) { 260 font_height fh; 261 GetFontHeight(&fh); 262 minHeight += ceilf(fh.ascent + fh.descent) + vPadding; 263 minWidth += StringWidth(fLabel.String()) + vPadding; 264 } 265 266 return BSize(minWidth + hPadding, minHeight + vPadding); 267 } 268 269 270 BSize 271 LaunchButton::MaxSize() 272 { 273 return PreferredSize(); 274 } 275 276 277 // #pragma mark - 278 279 280 // SetTo 281 void 282 LaunchButton::SetTo(const entry_ref* ref) 283 { 284 free(fAppSig); 285 fAppSig = NULL; 286 287 delete fRef; 288 if (ref) { 289 fRef = new entry_ref(*ref); 290 // follow links 291 BEntry entry(fRef, true); 292 entry.GetRef(fRef); 293 294 _UpdateIcon(fRef); 295 // see if this is an application 296 BFile file(ref, B_READ_ONLY); 297 BAppFileInfo info; 298 if (info.SetTo(&file) >= B_OK) { 299 char mimeSig[B_MIME_TYPE_LENGTH]; 300 if (info.GetSignature(mimeSig) >= B_OK) { 301 SetTo(mimeSig, false); 302 } else { 303 fprintf(stderr, "no MIME signature for '%s'\n", fRef->name); 304 } 305 } else { 306 fprintf(stderr, "no BAppFileInfo for '%s'\n", fRef->name); 307 } 308 } else { 309 fRef = NULL; 310 ClearIcon(); 311 } 312 _UpdateToolTip(); 313 } 314 315 // Ref 316 entry_ref* 317 LaunchButton::Ref() const 318 { 319 return fRef; 320 } 321 322 // SetTo 323 void 324 LaunchButton::SetTo(const char* appSig, bool updateIcon) 325 { 326 if (appSig) { 327 free(fAppSig); 328 fAppSig = strdup(appSig); 329 if (updateIcon) { 330 entry_ref ref; 331 if (be_roster->FindApp(fAppSig, &ref) >= B_OK) 332 SetTo(&ref); 333 } 334 } 335 _UpdateToolTip(); 336 } 337 338 // SetDesciption 339 void 340 LaunchButton::SetDescription(const char* text) 341 { 342 fDescription.SetTo(text); 343 _UpdateToolTip(); 344 } 345 346 // SetIconSize 347 void 348 LaunchButton::SetIconSize(uint32 size) 349 { 350 if (fIconSize == size) 351 return; 352 353 fIconSize = size; 354 _UpdateIcon(fRef); 355 356 InvalidateLayout(); 357 Invalidate(); 358 } 359 360 // #pragma mark - 361 362 // _UpdateToolTip 363 void 364 LaunchButton::_UpdateToolTip() 365 { 366 if (fRef) { 367 BString helper(fRef->name); 368 if (fDescription.CountChars() > 0) { 369 helper << "\n\n" << fDescription.String(); 370 } else { 371 BFile file(fRef, B_READ_ONLY); 372 BAppFileInfo appFileInfo; 373 version_info info; 374 if (appFileInfo.SetTo(&file) >= B_OK 375 && appFileInfo.GetVersionInfo(&info, 376 B_APP_VERSION_KIND) >= B_OK 377 && strlen(info.short_info) > 0) { 378 helper << "\n\n" << info.short_info; 379 } 380 } 381 // BubbleHelper::Default()->SetHelp(this, helper.String()); 382 } else { 383 // BubbleHelper::Default()->SetHelp(this, kEmptyHelpString); 384 } 385 } 386 387 // _UpdateIcon 388 void 389 LaunchButton::_UpdateIcon(const entry_ref* ref) 390 { 391 BBitmap* icon = new BBitmap(BRect(0.0, 0.0, fIconSize - 1, 392 fIconSize - 1), B_RGBA32); 393 // NOTE: passing an invalid/unknown icon_size argument will cause 394 // the BNodeInfo to ignore it and just use the bitmap bounds. 395 if (BNodeInfo::GetTrackerIcon(ref, icon, (icon_size)fIconSize) >= B_OK) 396 SetIcon(icon); 397 398 delete icon; 399 } 400