xref: /haiku/src/apps/launchbox/LaunchButton.cpp (revision adb0d19d561947362090081e81d90dde59142026)
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(&region);
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