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