vfs: check userland buffers before reading them.
[haiku.git] / src / kits / interface / Dragger.cpp
blob1022db13b4fd9c9f7c723e3ecab96a744f5bc65a
1 /*
2 * Copyright 2001-2012, Haiku.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Marc Flerackers (mflerackers@androme.be)
7 * Rene Gollent (rene@gollent.com)
8 * Alexandre Deckner (alex@zappotek.com)
9 */
12 //! BDragger represents a replicant "handle".
15 #include <pthread.h>
16 #include <stdio.h>
17 #include <stdlib.h>
19 #include <Alert.h>
20 #include <Beep.h>
21 #include <Bitmap.h>
22 #include <Dragger.h>
23 #include <MenuItem.h>
24 #include <Message.h>
25 #include <PopUpMenu.h>
26 #include <Shelf.h>
27 #include <SystemCatalog.h>
28 #include <Window.h>
30 #include <AutoLocker.h>
32 #include <AppServerLink.h>
33 #include <DragTrackingFilter.h>
34 #include <binary_compatibility/Interface.h>
35 #include <ServerProtocol.h>
36 #include <ViewPrivate.h>
38 #include "ZombieReplicantView.h"
40 using BPrivate::gSystemCatalog;
42 #undef B_TRANSLATION_CONTEXT
43 #define B_TRANSLATION_CONTEXT "Dragger"
45 #undef B_TRANSLATE
46 #define B_TRANSLATE(str) \
47 gSystemCatalog.GetString(B_TRANSLATE_MARK(str), "Dragger")
50 static const uint32 kMsgDragStarted = 'Drgs';
52 static const unsigned char kHandBitmap[] = {
53 255, 255, 0, 0, 0, 255, 255, 255,
54 255, 255, 0, 131, 131, 0, 255, 255,
55 0, 0, 0, 0, 131, 131, 0, 0,
56 0, 131, 0, 0, 131, 131, 0, 0,
57 0, 131, 131, 131, 131, 131, 0, 0,
58 255, 0, 131, 131, 131, 131, 0, 0,
59 255, 255, 0, 0, 0, 0, 0, 0,
60 255, 255, 255, 255, 255, 255, 0, 0
64 namespace {
66 struct DraggerManager {
67 bool visible;
68 bool visibleInitialized;
69 BList list;
71 DraggerManager()
73 visible(false),
74 visibleInitialized(false),
75 fLock("BDragger static")
79 bool Lock()
81 return fLock.Lock();
84 void Unlock()
86 fLock.Unlock();
89 static DraggerManager* Default()
91 if (sDefaultInstance == NULL)
92 pthread_once(&sDefaultInitOnce, &_InitSingleton);
94 return sDefaultInstance;
97 private:
98 static void _InitSingleton()
100 sDefaultInstance = new DraggerManager;
103 private:
104 BLocker fLock;
106 static pthread_once_t sDefaultInitOnce;
107 static DraggerManager* sDefaultInstance;
110 pthread_once_t DraggerManager::sDefaultInitOnce = PTHREAD_ONCE_INIT;
111 DraggerManager* DraggerManager::sDefaultInstance = NULL;
113 } // unnamed namespace
116 BDragger::BDragger(BRect frame, BView* target, uint32 resizingMode,
117 uint32 flags)
119 BView(frame, "_dragger_", resizingMode, flags),
120 fTarget(target),
121 fRelation(TARGET_UNKNOWN),
122 fShelf(NULL),
123 fTransition(false),
124 fIsZombie(false),
125 fErrCount(0),
126 fPopUpIsCustom(false),
127 fPopUp(NULL)
129 _InitData();
133 BDragger::BDragger(BView* target, uint32 flags)
135 BView("_dragger_", flags),
136 fTarget(target),
137 fRelation(TARGET_UNKNOWN),
138 fShelf(NULL),
139 fTransition(false),
140 fIsZombie(false),
141 fErrCount(0),
142 fPopUpIsCustom(false),
143 fPopUp(NULL)
145 _InitData();
149 BDragger::BDragger(BMessage* data)
151 BView(data),
152 fTarget(NULL),
153 fRelation(TARGET_UNKNOWN),
154 fShelf(NULL),
155 fTransition(false),
156 fIsZombie(false),
157 fErrCount(0),
158 fPopUpIsCustom(false),
159 fPopUp(NULL)
161 data->FindInt32("_rel", (int32*)&fRelation);
163 _InitData();
165 BMessage popupMsg;
166 if (data->FindMessage("_popup", &popupMsg) == B_OK) {
167 BArchivable* archivable = instantiate_object(&popupMsg);
169 if (archivable) {
170 fPopUp = dynamic_cast<BPopUpMenu*>(archivable);
171 fPopUpIsCustom = true;
177 BDragger::~BDragger()
179 delete fPopUp;
180 delete fBitmap;
184 BArchivable *
185 BDragger::Instantiate(BMessage* data)
187 if (validate_instantiation(data, "BDragger"))
188 return new BDragger(data);
189 return NULL;
193 status_t
194 BDragger::Archive(BMessage* data, bool deep) const
196 status_t ret = BView::Archive(data, deep);
197 if (ret != B_OK)
198 return ret;
200 BMessage popupMsg;
202 if (fPopUp != NULL && fPopUpIsCustom) {
203 bool windowLocked = fPopUp->Window()->Lock();
205 ret = fPopUp->Archive(&popupMsg, deep);
207 if (windowLocked) {
208 fPopUp->Window()->Unlock();
209 // TODO: Investigate, in some (rare) occasions the menu window
210 // has already been unlocked
213 if (ret == B_OK)
214 ret = data->AddMessage("_popup", &popupMsg);
217 if (ret == B_OK)
218 ret = data->AddInt32("_rel", fRelation);
219 return ret;
223 void
224 BDragger::AttachedToWindow()
226 if (fIsZombie) {
227 SetLowColor(kZombieColor);
228 SetViewColor(kZombieColor);
229 } else {
230 SetLowColor(B_TRANSPARENT_COLOR);
231 SetViewColor(B_TRANSPARENT_COLOR);
234 _DetermineRelationship();
235 _AddToList();
237 AddFilter(new DragTrackingFilter(this, kMsgDragStarted));
241 void
242 BDragger::DetachedFromWindow()
244 _RemoveFromList();
248 void
249 BDragger::Draw(BRect update)
251 BRect bounds(Bounds());
253 if (AreDraggersDrawn() && (fShelf == NULL || fShelf->AllowsDragging())) {
254 if (Parent() != NULL && (Parent()->Flags() & B_DRAW_ON_CHILDREN) == 0) {
255 uint32 flags = Parent()->Flags();
256 Parent()->SetFlags(flags | B_DRAW_ON_CHILDREN);
257 SetHighColor(Parent()->ViewColor());
258 FillRect(Bounds());
259 Parent()->Draw(Frame() & ConvertToParent(update));
260 Parent()->Flush();
261 Parent()->SetFlags(flags);
264 BPoint where = bounds.RightBottom() - BPoint(fBitmap->Bounds().Width(),
265 fBitmap->Bounds().Height());
266 SetDrawingMode(B_OP_OVER);
267 DrawBitmap(fBitmap, where);
268 SetDrawingMode(B_OP_COPY);
270 if (fIsZombie) {
271 // TODO: should draw it differently ?
273 } else if (IsVisibilityChanging()) {
274 if (Parent() != NULL) {
275 if ((Parent()->Flags() & B_DRAW_ON_CHILDREN) == 0) {
276 uint32 flags = Parent()->Flags();
277 Parent()->SetFlags(flags | B_DRAW_ON_CHILDREN);
278 Parent()->Invalidate(Frame() & ConvertToParent(update));
279 Parent()->SetFlags(flags);
281 } else {
282 SetHighColor(255, 255, 255);
283 FillRect(bounds);
289 void
290 BDragger::MouseDown(BPoint where)
292 if (fTarget == NULL || !AreDraggersDrawn())
293 return;
295 uint32 buttons;
296 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
298 if (fShelf != NULL && (buttons & B_SECONDARY_MOUSE_BUTTON) != 0)
299 _ShowPopUp(fTarget, where);
303 void
304 BDragger::MouseUp(BPoint point)
306 BView::MouseUp(point);
310 void
311 BDragger::MouseMoved(BPoint point, uint32 code, const BMessage* msg)
313 BView::MouseMoved(point, code, msg);
317 void
318 BDragger::MessageReceived(BMessage* msg)
320 switch (msg->what) {
321 case B_TRASH_TARGET:
322 if (fShelf != NULL)
323 Window()->PostMessage(kDeleteReplicant, fTarget, NULL);
324 else {
325 BAlert* alert = new BAlert(B_TRANSLATE("Warning"),
326 B_TRANSLATE("Can't delete this replicant from its original "
327 "application. Life goes on."),
328 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_FROM_WIDEST,
329 B_WARNING_ALERT);
330 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
331 alert->Go(NULL);
333 break;
335 case _SHOW_DRAG_HANDLES_:
336 // This code is used whenever the "are draggers drawn" option is
337 // changed.
338 if (fRelation == TARGET_IS_CHILD) {
339 fTransition = true;
340 Draw(Bounds());
341 Flush();
342 fTransition = false;
343 } else {
344 if ((fShelf != NULL && fShelf->AllowsDragging()
345 && AreDraggersDrawn())
346 || AreDraggersDrawn()) {
347 Show();
348 } else
349 Hide();
351 break;
353 case kMsgDragStarted:
354 if (fTarget != NULL) {
355 BMessage archive(B_ARCHIVED_OBJECT);
357 if (fRelation == TARGET_IS_PARENT)
358 fTarget->Archive(&archive);
359 else if (fRelation == TARGET_IS_CHILD)
360 Archive(&archive);
361 else if (fTarget->Archive(&archive)) {
362 BMessage archivedSelf(B_ARCHIVED_OBJECT);
364 if (Archive(&archivedSelf))
365 archive.AddMessage("__widget", &archivedSelf);
368 archive.AddInt32("be:actions", B_TRASH_TARGET);
369 BPoint offset;
370 drawing_mode mode;
371 BBitmap* bitmap = DragBitmap(&offset, &mode);
372 if (bitmap != NULL)
373 DragMessage(&archive, bitmap, mode, offset, this);
374 else {
375 DragMessage(&archive, ConvertFromScreen(
376 fTarget->ConvertToScreen(fTarget->Bounds())), this);
379 break;
381 default:
382 BView::MessageReceived(msg);
383 break;
388 void
389 BDragger::FrameMoved(BPoint newPosition)
391 BView::FrameMoved(newPosition);
395 void
396 BDragger::FrameResized(float newWidth, float newHeight)
398 BView::FrameResized(newWidth, newHeight);
402 status_t
403 BDragger::ShowAllDraggers()
405 BPrivate::AppServerLink link;
406 link.StartMessage(AS_SET_SHOW_ALL_DRAGGERS);
407 link.Attach<bool>(true);
409 status_t status = link.Flush();
410 if (status == B_OK) {
411 DraggerManager* manager = DraggerManager::Default();
412 AutoLocker<DraggerManager> locker(manager);
413 manager->visible = true;
414 manager->visibleInitialized = true;
417 return status;
421 status_t
422 BDragger::HideAllDraggers()
424 BPrivate::AppServerLink link;
425 link.StartMessage(AS_SET_SHOW_ALL_DRAGGERS);
426 link.Attach<bool>(false);
428 status_t status = link.Flush();
429 if (status == B_OK) {
430 DraggerManager* manager = DraggerManager::Default();
431 AutoLocker<DraggerManager> locker(manager);
432 manager->visible = false;
433 manager->visibleInitialized = true;
436 return status;
440 bool
441 BDragger::AreDraggersDrawn()
443 DraggerManager* manager = DraggerManager::Default();
444 AutoLocker<DraggerManager> locker(manager);
446 if (!manager->visibleInitialized) {
447 BPrivate::AppServerLink link;
448 link.StartMessage(AS_GET_SHOW_ALL_DRAGGERS);
450 status_t status;
451 if (link.FlushWithReply(status) == B_OK && status == B_OK) {
452 link.Read<bool>(&manager->visible);
453 manager->visibleInitialized = true;
454 } else
455 return false;
458 return manager->visible;
462 BHandler*
463 BDragger::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
464 int32 form, const char* property)
466 return BView::ResolveSpecifier(message, index, specifier, form, property);
470 status_t
471 BDragger::GetSupportedSuites(BMessage* data)
473 return BView::GetSupportedSuites(data);
477 status_t
478 BDragger::Perform(perform_code code, void* _data)
480 switch (code) {
481 case PERFORM_CODE_MIN_SIZE:
482 ((perform_data_min_size*)_data)->return_value
483 = BDragger::MinSize();
484 return B_OK;
485 case PERFORM_CODE_MAX_SIZE:
486 ((perform_data_max_size*)_data)->return_value
487 = BDragger::MaxSize();
488 return B_OK;
489 case PERFORM_CODE_PREFERRED_SIZE:
490 ((perform_data_preferred_size*)_data)->return_value
491 = BDragger::PreferredSize();
492 return B_OK;
493 case PERFORM_CODE_LAYOUT_ALIGNMENT:
494 ((perform_data_layout_alignment*)_data)->return_value
495 = BDragger::LayoutAlignment();
496 return B_OK;
497 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
498 ((perform_data_has_height_for_width*)_data)->return_value
499 = BDragger::HasHeightForWidth();
500 return B_OK;
501 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
503 perform_data_get_height_for_width* data
504 = (perform_data_get_height_for_width*)_data;
505 BDragger::GetHeightForWidth(data->width, &data->min, &data->max,
506 &data->preferred);
507 return B_OK;
509 case PERFORM_CODE_SET_LAYOUT:
511 perform_data_set_layout* data = (perform_data_set_layout*)_data;
512 BDragger::SetLayout(data->layout);
513 return B_OK;
515 case PERFORM_CODE_LAYOUT_INVALIDATED:
517 perform_data_layout_invalidated* data
518 = (perform_data_layout_invalidated*)_data;
519 BDragger::LayoutInvalidated(data->descendants);
520 return B_OK;
522 case PERFORM_CODE_DO_LAYOUT:
524 BDragger::DoLayout();
525 return B_OK;
529 return BView::Perform(code, _data);
533 void
534 BDragger::ResizeToPreferred()
536 BView::ResizeToPreferred();
540 void
541 BDragger::GetPreferredSize(float* _width, float* _height)
543 BView::GetPreferredSize(_width, _height);
547 void
548 BDragger::MakeFocus(bool state)
550 BView::MakeFocus(state);
554 void
555 BDragger::AllAttached()
557 BView::AllAttached();
561 void
562 BDragger::AllDetached()
564 BView::AllDetached();
568 status_t
569 BDragger::SetPopUp(BPopUpMenu* menu)
571 if (menu != NULL && menu != fPopUp) {
572 delete fPopUp;
573 fPopUp = menu;
574 fPopUpIsCustom = true;
575 return B_OK;
577 return B_ERROR;
581 BPopUpMenu*
582 BDragger::PopUp() const
584 if (fPopUp == NULL && fTarget)
585 const_cast<BDragger*>(this)->_BuildDefaultPopUp();
587 return fPopUp;
591 bool
592 BDragger::InShelf() const
594 return fShelf != NULL;
598 BView*
599 BDragger::Target() const
601 return fTarget;
605 BBitmap*
606 BDragger::DragBitmap(BPoint* offset, drawing_mode* mode)
608 return NULL;
612 bool
613 BDragger::IsVisibilityChanging() const
615 return fTransition;
619 void BDragger::_ReservedDragger2() {}
620 void BDragger::_ReservedDragger3() {}
621 void BDragger::_ReservedDragger4() {}
624 BDragger&
625 BDragger::operator=(const BDragger&)
627 return *this;
631 /*static*/ void
632 BDragger::_UpdateShowAllDraggers(bool visible)
634 DraggerManager* manager = DraggerManager::Default();
635 AutoLocker<DraggerManager> locker(manager);
637 manager->visibleInitialized = true;
638 manager->visible = visible;
640 for (int32 i = manager->list.CountItems(); i-- > 0;) {
641 BDragger* dragger = (BDragger*)manager->list.ItemAt(i);
642 BMessenger target(dragger);
643 target.SendMessage(_SHOW_DRAG_HANDLES_);
648 void
649 BDragger::_InitData()
651 fBitmap = new BBitmap(BRect(0.0f, 0.0f, 7.0f, 7.0f), B_CMAP8, false, false);
652 fBitmap->SetBits(kHandBitmap, fBitmap->BitsLength(), 0, B_CMAP8);
656 void
657 BDragger::_AddToList()
659 DraggerManager* manager = DraggerManager::Default();
660 AutoLocker<DraggerManager> locker(manager);
661 manager->list.AddItem(this);
663 bool allowsDragging = true;
664 if (fShelf)
665 allowsDragging = fShelf->AllowsDragging();
667 if (!AreDraggersDrawn() || !allowsDragging) {
668 // The dragger is not shown - but we can't hide us in case we're the
669 // parent of the actual target view (because then you couldn't see
670 // it anymore).
671 if (fRelation != TARGET_IS_CHILD && !IsHidden())
672 Hide();
677 void
678 BDragger::_RemoveFromList()
680 DraggerManager* manager = DraggerManager::Default();
681 AutoLocker<DraggerManager> locker(manager);
682 manager->list.RemoveItem(this);
686 status_t
687 BDragger::_DetermineRelationship()
689 if (fTarget != NULL) {
690 if (fTarget == Parent())
691 fRelation = TARGET_IS_PARENT;
692 else if (fTarget == ChildAt(0))
693 fRelation = TARGET_IS_CHILD;
694 else
695 fRelation = TARGET_IS_SIBLING;
696 } else {
697 if (fRelation == TARGET_IS_PARENT)
698 fTarget = Parent();
699 else if (fRelation == TARGET_IS_CHILD)
700 fTarget = ChildAt(0);
701 else
702 return B_ERROR;
705 if (fRelation == TARGET_IS_PARENT) {
706 BRect bounds(Frame());
707 BRect parentBounds(Parent()->Bounds());
708 if (!parentBounds.Contains(bounds)) {
709 MoveTo(parentBounds.right - bounds.Width(),
710 parentBounds.bottom - bounds.Height());
714 return B_OK;
718 status_t
719 BDragger::_SetViewToDrag(BView* target)
721 if (target->Window() != Window())
722 return B_ERROR;
724 fTarget = target;
726 if (Window() != NULL)
727 _DetermineRelationship();
729 return B_OK;
733 void
734 BDragger::_SetShelf(BShelf* shelf)
736 fShelf = shelf;
740 void
741 BDragger::_SetZombied(bool state)
743 fIsZombie = state;
745 if (state) {
746 SetLowColor(kZombieColor);
747 SetViewColor(kZombieColor);
752 void
753 BDragger::_BuildDefaultPopUp()
755 fPopUp = new BPopUpMenu("Shelf", false, false, B_ITEMS_IN_COLUMN);
757 // About
758 BMessage* msg = new BMessage(B_ABOUT_REQUESTED);
760 const char* name = fTarget->Name();
761 if (name != NULL)
762 msg->AddString("target", name);
764 BString about(B_TRANSLATE("About %app" B_UTF8_ELLIPSIS));
765 about.ReplaceFirst("%app", name);
767 fPopUp->AddItem(new BMenuItem(about.String(), msg));
768 fPopUp->AddSeparatorItem();
769 fPopUp->AddItem(new BMenuItem(B_TRANSLATE("Remove replicant"),
770 new BMessage(kDeleteReplicant)));
774 void
775 BDragger::_ShowPopUp(BView* target, BPoint where)
777 BPoint point = ConvertToScreen(where);
779 if (fPopUp == NULL && fTarget != NULL)
780 _BuildDefaultPopUp();
782 fPopUp->SetTargetForItems(fTarget);
784 float menuWidth, menuHeight;
785 fPopUp->GetPreferredSize(&menuWidth, &menuHeight);
786 BRect rect(0, 0, menuWidth, menuHeight);
787 rect.InsetBy(-0.5, -0.5);
788 rect.OffsetTo(point);
790 fPopUp->Go(point, true, false, rect, true);
794 #if __GNUC__ < 3
796 extern "C" BBitmap*
797 _ReservedDragger1__8BDragger(BDragger* dragger, BPoint* offset,
798 drawing_mode* mode)
800 return dragger->BDragger::DragBitmap(offset, mode);
803 #endif