xref: /haiku/src/apps/launchbox/LaunchButton.cpp (revision 56eb8e78cc702792e3b032e3f5f45da9e5dbea9e)
1 /*
2  * Copyright 2006, Haiku.
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 {
45 	if (fClickSpeed == 0 || get_click_speed(&fClickSpeed) < B_OK)
46 		fClickSpeed = 500000;
47 
48 	BSize size(32.0 + 8.0, 32.0 + 8.0);
49 	SetExplicitMinSize(size);
50 	SetExplicitMaxSize(size);
51 }
52 
53 // destructor
54 LaunchButton::~LaunchButton()
55 {
56 	delete fRef;
57 	if (fAppSig)
58 		free(fAppSig);
59 }
60 
61 // AttachedToWindow
62 void
63 LaunchButton::AttachedToWindow()
64 {
65 	IconButton::AttachedToWindow();
66 	_UpdateToolTip();
67 }
68 
69 // DetachedFromWindow
70 void
71 LaunchButton::DetachedFromWindow()
72 {
73 //	BubbleHelper::Default()->SetHelp(this, NULL);
74 }
75 
76 // Draw
77 void
78 LaunchButton::Draw(BRect updateRect)
79 {
80 	if (fAnticipatingDrop) {
81 		rgb_color color = fRef ? ui_color(B_KEYBOARD_NAVIGATION_COLOR)
82 							   : (rgb_color){ 0, 130, 60, 255 };
83 		SetHighColor(color);
84 		// limit clipping region to exclude the blue rect we just drew
85 		BRect r(Bounds());
86 		StrokeRect(r);
87 		r.InsetBy(1.0, 1.0);
88 		BRegion region(r);
89 		ConstrainClippingRegion(&region);
90 	}
91 	if (IsValid()) {
92 		IconButton::Draw(updateRect);
93 	} else {
94 		rgb_color background = LowColor();
95 		rgb_color lightShadow = tint_color(background, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0);
96 		rgb_color shadow = tint_color(background, B_DARKEN_1_TINT);
97 		rgb_color light = tint_color(background, B_LIGHTEN_1_TINT);
98 		BRect r(Bounds());
99 		_DrawFrame(r, shadow, light, lightShadow, lightShadow);
100 		r.InsetBy(2.0, 2.0);
101 		SetHighColor(lightShadow);
102 		FillRect(r);
103 	}
104 }
105 
106 // MessageReceived
107 void
108 LaunchButton::MessageReceived(BMessage* message)
109 {
110 	switch (message->what) {
111 		case B_SIMPLE_DATA:
112 		case B_REFS_RECEIVED: {
113 			entry_ref ref;
114 			if (message->FindRef("refs", &ref) >= B_OK) {
115 				if (fRef) {
116 					if (ref != *fRef) {
117 						BEntry entry(fRef, true);
118 						if (entry.IsDirectory()) {
119 							message->PrintToStream();
120 							// copy stuff into the directory
121 						} else {
122 							message->what = B_REFS_RECEIVED;
123 							team_id team;
124 							if (fAppSig)
125 								team = be_roster->TeamFor(fAppSig);
126 							else
127 								team = be_roster->TeamFor(fRef);
128 							if (team < B_OK) {
129 								if (fAppSig)
130 									be_roster->Launch(fAppSig, message, &team);
131 								else
132 									be_roster->Launch(fRef, message, &team);
133 							} else {
134 								app_info appInfo;
135 								if (team >= B_OK && be_roster->GetRunningAppInfo(team, &appInfo) >= B_OK) {
136 									BMessenger messenger(appInfo.signature, 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 			IconButton::MessageReceived(message);
153 			break;
154 	}
155 }
156 
157 // MouseDown
158 void
159 LaunchButton::MouseDown(BPoint where)
160 {
161 	bigtime_t now = system_time();
162 	bool callInherited = true;
163 	if (now - fLastClickTime < fClickSpeed)
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 		IconButton::MouseDown(where);
181 }
182 
183 // MouseUp
184 void
185 LaunchButton::MouseUp(BPoint where)
186 {
187 	if (fAnticipatingDrop) {
188 		fAnticipatingDrop = false;
189 		Invalidate();
190 	}
191 	IconButton::MouseUp(where);
192 }
193 
194 // MouseMoved
195 void
196 LaunchButton::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
197 {
198 	if ((dragMessage && (transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW))
199 		&& ((dragMessage->what == B_SIMPLE_DATA
200 			|| dragMessage->what == B_REFS_RECEIVED) || fRef)) {
201 		if (!fAnticipatingDrop) {
202 			fAnticipatingDrop = true;
203 			Invalidate();
204 		}
205 	}
206 	if (!dragMessage || (transit == B_EXITED_VIEW || transit == B_OUTSIDE_VIEW)) {
207 		if (fAnticipatingDrop) {
208 			fAnticipatingDrop = false;
209 			Invalidate();
210 		}
211 	}
212 	// see if we should create a drag message
213 	if (_HasFlags(STATE_TRACKING) && fRef) {
214 		BPoint diff = where - fDragStart;
215 		float dist = sqrtf(diff.x * diff.x + diff.y * diff.y);
216 		if (dist >= kDragStartDist) {
217 			// stop tracking
218 			_ClearFlags(STATE_PRESSED | STATE_TRACKING | STATE_INSIDE);
219 			// create drag bitmap and message
220 			if (BBitmap* bitmap = Bitmap()) {
221 				if (bitmap->ColorSpace() == B_RGB32) {
222 					// make semitransparent
223 					uint8* bits = (uint8*)bitmap->Bits();
224 					uint32 width = bitmap->Bounds().IntegerWidth() + 1;
225 					uint32 height = bitmap->Bounds().IntegerHeight() + 1;
226 					uint32 bpr = bitmap->BytesPerRow();
227 					for (uint32 y = 0; y < height; y++) {
228 						uint8* bitsHandle = bits;
229 						for (uint32 x = 0; x < width; x++) {
230 							bitsHandle[3] = uint8(bitsHandle[3] * kDragBitmapAlphaScale);
231 							bitsHandle += 4;
232 						}
233 						bits += bpr;
234 					}
235 				}
236 				BMessage message(B_SIMPLE_DATA);
237 				message.AddPointer("button", this);
238 				message.AddRef("refs", fRef);
239 				DragMessage(&message, bitmap, B_OP_ALPHA, fDragStart);
240 			}
241 		}
242 	}
243 	IconButton::MouseMoved(where, transit, dragMessage);
244 }
245 
246 // SetTo
247 void
248 LaunchButton::SetTo(const entry_ref* ref)
249 {
250 	if (fAppSig) {
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 				printf("no MIME sig\n");
271 			}
272 		} else {
273 			printf("no app\n");
274 		}
275 	} else {
276 		fRef = NULL;
277 		ClearIcon();
278 	}
279 	_UpdateToolTip();
280 }
281 
282 // Ref
283 entry_ref*
284 LaunchButton::Ref() const
285 {
286 	return fRef;
287 }
288 
289 // SetTo
290 void
291 LaunchButton::SetTo(const char* appSig, bool updateIcon)
292 {
293 	if (appSig) {
294 		if (fAppSig)
295 			free(fAppSig);
296 		fAppSig = strdup(appSig);
297 		if (updateIcon) {
298 			entry_ref ref;
299 			if (be_roster->FindApp(fAppSig, &ref) >= B_OK)
300 				SetTo(&ref);
301 		}
302 	}
303 	_UpdateToolTip();
304 }
305 
306 // SetDesciption
307 void
308 LaunchButton::SetDescription(const char* text)
309 {
310 	fDescription.SetTo(text);
311 	_UpdateToolTip();
312 }
313 
314 // _UpdateToolTip
315 void
316 LaunchButton::_UpdateToolTip()
317 {
318 	if (fRef) {
319 		BString helper(fRef->name);
320 		if (fDescription.CountChars() > 0) {
321 			helper << "\n\n" << fDescription.String();
322 		} else {
323 			BFile file(fRef, B_READ_ONLY);
324 			BAppFileInfo appFileInfo;
325 			version_info info;
326 			if (appFileInfo.SetTo(&file) >= B_OK
327 				&& appFileInfo.GetVersionInfo(&info,
328 											  B_APP_VERSION_KIND) >= B_OK
329 				&& strlen(info.short_info) > 0) {
330 				helper << "\n\n" << info.short_info;
331 			}
332 		}
333 //		BubbleHelper::Default()->SetHelp(this, helper.String());
334 	} else {
335 //		BubbleHelper::Default()->SetHelp(this, kEmptyHelpString);
336 	}
337 }
338 
339 // _UpdateIcon
340 void
341 LaunchButton::_UpdateIcon(const entry_ref* ref)
342 {
343 	BBitmap* icon = new BBitmap(BRect(0.0, 0.0, 31.0, 31.0), B_RGBA32);
344 	if (BNodeInfo::GetTrackerIcon(ref, icon, B_LARGE_ICON) >= B_OK)
345 		SetIcon(icon);
346 
347 	delete icon;
348 }
349