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