vfs: check userland buffers before reading them.
[haiku.git] / src / apps / launchbox / LaunchButton.cpp
blobd22be5eb68a7fbbd4ef4fa2ffea31b965a00b40b
1 /*
2 * Copyright 2006-2009, Stephan Aßmus <superstippi@gmx.de>
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
7 #include "LaunchButton.h"
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
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>
25 #include "PadView.h"
26 #include "MainWindow.h"
29 #undef B_TRANSLATION_CONTEXT
30 #define B_TRANSLATION_CONTEXT "LaunchBox"
33 static const float kDragStartDist = 10.0;
34 static const float kDragBitmapAlphaScale = 0.6;
35 static const char* kEmptyHelpString = B_TRANSLATE("You can drag an icon here.");
38 bigtime_t LaunchButton::sClickSpeed = 0;
39 bool LaunchButton::sIgnoreDoubleClick = true;
42 LaunchButton::LaunchButton(const char* name, const char* label,
43 BMessage* message, BHandler* target)
45 BIconButton(name, label, message, target),
46 fRef(NULL),
47 fAppSig(NULL),
48 fDescription(""),
49 fAnticipatingDrop(false),
50 fLastClickTime(0),
51 fIconSize(DEFAULT_ICON_SIZE)
53 if (sClickSpeed == 0 || get_click_speed(&sClickSpeed) != B_OK)
54 sClickSpeed = 500000;
58 LaunchButton::~LaunchButton()
60 delete fRef;
61 free(fAppSig);
65 void
66 LaunchButton::AttachedToWindow()
68 BIconButton::AttachedToWindow();
69 _UpdateToolTip();
73 void
74 LaunchButton::Draw(BRect updateRect)
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);
87 if (IsValid()) {
88 BIconButton::Draw(updateRect);
89 } else {
90 rgb_color background = LowColor();
91 rgb_color lightShadow = tint_color(background,
92 (B_NO_TINT + B_DARKEN_1_TINT) / 2.0);
93 rgb_color shadow = tint_color(background, B_DARKEN_1_TINT);
94 rgb_color light = tint_color(background, B_LIGHTEN_1_TINT);
95 BRect r(Bounds());
96 _DrawFrame(r, shadow, light, lightShadow, lightShadow);
97 r.InsetBy(2.0, 2.0);
98 SetHighColor(lightShadow);
99 FillRect(r);
104 void
105 LaunchButton::MessageReceived(BMessage* message)
107 switch (message->what) {
108 case B_SIMPLE_DATA:
109 case B_REFS_RECEIVED: {
110 entry_ref ref;
111 if (message->FindRef("refs", &ref) == B_OK) {
112 if (fRef) {
113 if (ref != *fRef) {
114 BEntry entry(fRef, true);
115 if (entry.IsDirectory()) {
116 message->PrintToStream();
117 // copy stuff into the directory
118 } else {
119 message->what = B_REFS_RECEIVED;
120 team_id team;
121 if (fAppSig)
122 team = be_roster->TeamFor(fAppSig);
123 else
124 team = be_roster->TeamFor(fRef);
125 if (team < 0) {
126 if (fAppSig)
127 be_roster->Launch(fAppSig, message, &team);
128 else
129 be_roster->Launch(fRef, message, &team);
130 } else {
131 app_info appInfo;
132 if (team >= 0
133 && be_roster->GetRunningAppInfo(team,
134 &appInfo) == B_OK) {
135 BMessenger messenger(appInfo.signature,
136 team);
137 if (messenger.IsValid())
138 messenger.SendMessage(message);
143 } else {
144 SetTo(&ref);
147 break;
149 case B_PASTE:
150 case B_MODIFIERS_CHANGED:
151 default:
152 BIconButton::MessageReceived(message);
153 break;
158 void
159 LaunchButton::MouseDown(BPoint where)
161 bigtime_t now = system_time();
162 bool callInherited = true;
163 if (sIgnoreDoubleClick && now - fLastClickTime < sClickSpeed)
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) != 0 && IsInside()) {
170 if (PadView* parent = dynamic_cast<PadView*>(Parent())) {
171 parent->DisplayMenu(ConvertToParent(where), this);
172 SetInside(false);
173 callInherited = false;
175 } else {
176 fDragStart = where;
179 if (callInherited)
180 BIconButton::MouseDown(where);
184 void
185 LaunchButton::MouseUp(BPoint where)
187 if (fAnticipatingDrop) {
188 fAnticipatingDrop = false;
189 Invalidate();
191 BIconButton::MouseUp(where);
195 void
196 LaunchButton::MouseMoved(BPoint where, uint32 transit,
197 const BMessage* dragMessage)
199 if ((dragMessage && (transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW))
200 && ((dragMessage->what == B_SIMPLE_DATA
201 || dragMessage->what == B_REFS_RECEIVED) || fRef)) {
202 if (!fAnticipatingDrop) {
203 fAnticipatingDrop = true;
204 Invalidate();
207 if (!dragMessage || (transit == B_EXITED_VIEW || transit == B_OUTSIDE_VIEW)) {
208 if (fAnticipatingDrop) {
209 fAnticipatingDrop = false;
210 Invalidate();
213 // see if we should create a drag message
214 if (IsTracking() && fRef != NULL) {
215 BPoint diff = where - fDragStart;
216 float dist = sqrtf(diff.x * diff.x + diff.y * diff.y);
217 if (dist >= kDragStartDist) {
218 // stop tracking
219 SetTracking(false);
220 SetPressed(false);
221 SetInside(false);
223 // create drag bitmap and message
224 if (BBitmap* bitmap = Bitmap()) {
225 if (bitmap->ColorSpace() == B_RGB32) {
226 // make semitransparent
227 uint8* bits = (uint8*)bitmap->Bits();
228 uint32 width = bitmap->Bounds().IntegerWidth() + 1;
229 uint32 height = bitmap->Bounds().IntegerHeight() + 1;
230 uint32 bpr = bitmap->BytesPerRow();
231 for (uint32 y = 0; y < height; y++) {
232 uint8* bitsHandle = bits;
233 for (uint32 x = 0; x < width; x++) {
234 bitsHandle[3] = uint8(bitsHandle[3]
235 * kDragBitmapAlphaScale);
236 bitsHandle += 4;
238 bits += bpr;
241 BMessage message(B_SIMPLE_DATA);
242 message.AddPointer("button", this);
243 message.AddRef("refs", fRef);
244 // DragMessage takes ownership of the bitmap.
245 DragMessage(&message, bitmap, B_OP_ALPHA, fDragStart);
249 BIconButton::MouseMoved(where, transit, dragMessage);
253 BSize
254 LaunchButton::MinSize()
256 return PreferredSize();
260 BSize
261 LaunchButton::PreferredSize()
263 float minWidth = fIconSize;
264 float minHeight = fIconSize;
266 float hPadding = max_c(6.0, ceilf(minHeight / 3.0));
267 float vPadding = max_c(6.0, ceilf(minWidth / 3.0));
269 if (Label() != NULL && Label()[0] != '\0') {
270 font_height fh;
271 GetFontHeight(&fh);
272 minHeight += ceilf(fh.ascent + fh.descent) + vPadding;
273 minWidth += StringWidth(Label()) + vPadding;
276 return BSize(minWidth + hPadding, minHeight + vPadding);
280 BSize
281 LaunchButton::MaxSize()
283 return PreferredSize();
287 // #pragma mark -
290 void
291 LaunchButton::SetTo(const entry_ref* ref)
293 free(fAppSig);
294 fAppSig = NULL;
296 delete fRef;
297 if (ref) {
298 fRef = new entry_ref(*ref);
299 // follow links
300 BEntry entry(fRef, true);
301 entry.GetRef(fRef);
303 _UpdateIcon(fRef);
304 // see if this is an application
305 BFile file(ref, B_READ_ONLY);
306 BAppFileInfo info;
307 if (info.SetTo(&file) == B_OK) {
308 char mimeSig[B_MIME_TYPE_LENGTH];
309 if (info.GetSignature(mimeSig) == B_OK) {
310 SetTo(mimeSig, false);
311 } else {
312 fprintf(stderr, "no MIME signature for '%s'\n", fRef->name);
314 } else {
315 fprintf(stderr, "no BAppFileInfo for '%s'\n", fRef->name);
317 } else {
318 fRef = NULL;
319 ClearIcon();
321 _UpdateToolTip();
322 _NotifySettingsChanged();
326 entry_ref*
327 LaunchButton::Ref() const
329 return fRef;
333 void
334 LaunchButton::SetTo(const char* appSig, bool updateIcon)
336 if (appSig) {
337 free(fAppSig);
338 fAppSig = strdup(appSig);
339 if (updateIcon) {
340 entry_ref ref;
341 if (be_roster->FindApp(fAppSig, &ref) == B_OK)
342 SetTo(&ref);
345 _UpdateToolTip();
346 _NotifySettingsChanged();
350 void
351 LaunchButton::SetDescription(const char* text)
353 fDescription.SetTo(text);
354 _UpdateToolTip();
355 _NotifySettingsChanged();
359 void
360 LaunchButton::SetIconSize(uint32 size)
362 if (fIconSize == size)
363 return;
365 fIconSize = size;
366 _UpdateIcon(fRef);
368 InvalidateLayout();
369 Invalidate();
373 void
374 LaunchButton::SetIgnoreDoubleClick(bool refuse)
376 sIgnoreDoubleClick = refuse;
380 // #pragma mark -
383 void
384 LaunchButton::_UpdateToolTip()
386 // TODO: This works around a bug in the tool tip management.
387 // Remove when fixed (although no harm done...)
388 HideToolTip();
389 SetToolTip(static_cast<BToolTip*>(NULL));
391 if (fRef) {
392 BString helper(fRef->name);
393 if (fDescription.CountChars() > 0) {
394 if (fDescription != helper)
395 helper << "\n\n" << fDescription.String();
396 } else {
397 BFile file(fRef, B_READ_ONLY);
398 BAppFileInfo appFileInfo;
399 version_info info;
400 if (appFileInfo.SetTo(&file) == B_OK
401 && appFileInfo.GetVersionInfo(&info,
402 B_APP_VERSION_KIND) == B_OK
403 && strlen(info.short_info) > 0
404 && helper.Compare(info.short_info) != 0) {
405 helper << "\n\n" << info.short_info;
408 SetToolTip(helper.String());
409 } else {
410 SetToolTip(kEmptyHelpString);
415 void
416 LaunchButton::_UpdateIcon(const entry_ref* ref)
418 BBitmap* icon = new BBitmap(BRect(0.0, 0.0, fIconSize - 1,
419 fIconSize - 1), B_RGBA32);
420 // NOTE: passing an invalid/unknown icon_size argument will cause
421 // the BNodeInfo to ignore it and just use the bitmap bounds.
422 if (BNodeInfo::GetTrackerIcon(ref, icon, (icon_size)fIconSize) == B_OK)
423 SetIcon(icon);
425 delete icon;
429 void
430 LaunchButton::_NotifySettingsChanged()
432 be_app->PostMessage(MSG_SETTINGS_CHANGED);
436 void
437 LaunchButton::_DrawFrame(BRect r, rgb_color col1, rgb_color col2,
438 rgb_color col3, rgb_color col4)
440 BeginLineArray(8);
441 AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), col1);
442 AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), col1);
443 AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), col2);
444 AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), col2);
445 r.InsetBy(1.0, 1.0);
446 AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), col3);
447 AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), col3);
448 AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), col4);
449 AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), col4);
450 EndLineArray();