xref: /haiku/src/kits/interface/Dragger.cpp (revision 2600324b57fa31cdea1627d584d314f2a579c4a8)
1 /*
2  * Copyright 2001-2008, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marc Flerackers (mflerackers@androme.be)
7  *		Rene Gollent (rene@gollent.com)
8  */
9 
10 //!	BDragger represents a replicant "handle".
11 
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 
16 #include <Alert.h>
17 #include <Autolock.h>
18 #include <Beep.h>
19 #include <Bitmap.h>
20 #include <Dragger.h>
21 #include <MenuItem.h>
22 #include <Message.h>
23 #include <PopUpMenu.h>
24 #include <Shelf.h>
25 #include <Window.h>
26 
27 #include <AppServerLink.h>
28 #include <binary_compatibility/Interface.h>
29 #include <ServerProtocol.h>
30 #include <ViewPrivate.h>
31 
32 #include "ZombieReplicantView.h"
33 
34 
35 bool BDragger::sVisible;
36 bool BDragger::sVisibleInitialized;
37 BLocker BDragger::sLock("BDragger static");
38 BList BDragger::sList;
39 
40 
41 const unsigned char
42 kHandBitmap[] = {
43 	255, 255,   0,   0,   0, 255, 255, 255,
44 	255, 255,   0, 131, 131,   0, 255, 255,
45 	  0,   0,   0,   0, 131, 131,   0,   0,
46 	  0, 131,   0,   0, 131, 131,   0,   0,
47 	  0, 131, 131, 131, 131, 131,   0,   0,
48 	255,   0, 131, 131, 131, 131,   0,   0,
49 	255, 255,   0,   0,   0,   0,   0,   0,
50 	255, 255, 255, 255, 255, 255,   0,   0
51 };
52 
53 
54 BDragger::BDragger(BRect bounds, BView *target, uint32 rmask, uint32 flags)
55 	: BView(bounds, "_dragger_", rmask, flags),
56 	fTarget(target),
57 	fRelation(TARGET_UNKNOWN),
58 	fShelf(NULL),
59 	fTransition(false),
60 	fIsZombie(false),
61 	fErrCount(0),
62 	fPopUp(NULL)
63 {
64 	fBitmap = new BBitmap(BRect(0.0f, 0.0f, 7.0f, 7.0f), B_CMAP8, false, false);
65 	fBitmap->SetBits(kHandBitmap, fBitmap->BitsLength(), 0, B_CMAP8);
66 }
67 
68 
69 BDragger::BDragger(BMessage *data)
70 	: BView(data),
71 	fTarget(NULL),
72 	fRelation(TARGET_UNKNOWN),
73 	fShelf(NULL),
74 	fTransition(false),
75 	fIsZombie(false),
76 	fErrCount(0),
77 	fPopUp(NULL)
78 {
79 	data->FindInt32("_rel", (int32 *)&fRelation);
80 
81 	fBitmap = new BBitmap(BRect(0.0f, 0.0f, 7.0f, 7.0f), B_CMAP8, false, false);
82 	fBitmap->SetBits(kHandBitmap, fBitmap->BitsLength(), 0, B_CMAP8);
83 
84 	BMessage popupMsg;
85 	if (data->FindMessage("_popup", &popupMsg) == B_OK) {
86 		BArchivable *archivable = instantiate_object(&popupMsg);
87 
88 		if (archivable)
89 			fPopUp = dynamic_cast<BPopUpMenu *>(archivable);
90 	}
91 }
92 
93 
94 BDragger::~BDragger()
95 {
96 	SetPopUp(NULL);
97 
98 	delete fBitmap;
99 }
100 
101 
102 BArchivable	*
103 BDragger::Instantiate(BMessage *data)
104 {
105 	if (validate_instantiation(data, "BDragger"))
106 		return new BDragger(data);
107 	return NULL;
108 }
109 
110 
111 status_t
112 BDragger::Archive(BMessage *data, bool deep) const
113 {
114 	status_t ret = BView::Archive(data, deep);
115 	if (ret != B_OK)
116 		return ret;
117 
118 	BMessage popupMsg;
119 
120 	if (fPopUp) {
121 		ret = fPopUp->Archive(&popupMsg, deep);
122 		if (ret == B_OK)
123 			ret = data->AddMessage("_popup", &popupMsg);
124 	}
125 
126 	if (ret == B_OK)
127 		ret = data->AddInt32("_rel", fRelation);
128 	return ret;
129 }
130 
131 
132 void
133 BDragger::AttachedToWindow()
134 {
135 	if (fIsZombie) {
136 		SetLowColor(kZombieColor);
137 		SetViewColor(kZombieColor);
138 	} else {
139 		SetLowColor(B_TRANSPARENT_COLOR);
140 		SetViewColor(B_TRANSPARENT_COLOR);
141 	}
142 
143 	_DetermineRelationship();
144 	_AddToList();
145 }
146 
147 
148 void
149 BDragger::DetachedFromWindow()
150 {
151 	_RemoveFromList();
152 }
153 
154 
155 void
156 BDragger::Draw(BRect update)
157 {
158 	BRect bounds(Bounds());
159 
160 	if (AreDraggersDrawn() && (!fShelf || fShelf->AllowsDragging())) {
161 		BPoint where = bounds.RightBottom() - BPoint(fBitmap->Bounds().Width(), fBitmap->Bounds().Height());
162 		SetDrawingMode(B_OP_OVER);
163 		DrawBitmap(fBitmap, where);
164 		SetDrawingMode(B_OP_COPY);
165 
166 		if (fIsZombie) {
167 			// TODO: should draw it differently ?
168 		}
169 	} else if (IsVisibilityChanging()) {
170 		if (Parent())
171 			Parent()->Invalidate(Frame());
172 
173 		else {
174 			SetHighColor(255, 255, 255);
175 			FillRect(bounds);
176 		}
177 	}
178 }
179 
180 
181 void
182 BDragger::MouseDown(BPoint where)
183 {
184 	if (!fTarget || !AreDraggersDrawn())
185 		return;
186 
187 	uint32 buttons;
188 	Window()->CurrentMessage()->FindInt32("buttons", (int32 *)&buttons);
189 
190 	if (buttons & B_SECONDARY_MOUSE_BUTTON) {
191 		if (!fShelf || !fTarget) {
192 			beep();
193 			return;
194 		}
195 
196 		_ShowPopUp(fTarget, where);
197 	} else {
198 		bigtime_t time = system_time();
199 		bigtime_t clickSpeed = 0;
200 
201 		get_click_speed(&clickSpeed);
202 
203 		bool drag = false;
204 
205 		while (true) {
206 			BPoint mousePoint;
207 			GetMouse(&mousePoint, &buttons);
208 
209 			if (!buttons || system_time() > time + clickSpeed)
210 				break;
211 
212 			if (mousePoint != where) {
213 				drag = true;
214 				break;
215 			}
216 
217 			snooze(40000);
218 		}
219 
220 		if (drag) {
221 			BMessage archive(B_ARCHIVED_OBJECT);
222 
223 			if (fRelation == TARGET_IS_PARENT)
224 				fTarget->Archive(&archive);
225 			else if (fRelation == TARGET_IS_CHILD)
226 				Archive(&archive);
227 			else {
228 				if (fTarget->Archive(&archive)) {
229 					BMessage widget(B_ARCHIVED_OBJECT);
230 
231 					if (Archive(&widget))
232 						archive.AddMessage("__widget", &widget);
233 				}
234 			}
235 
236 			archive.AddInt32("be:actions", B_TRASH_TARGET);
237 
238 			BPoint offset;
239 			drawing_mode mode;
240 			BBitmap *bitmap = DragBitmap(&offset, &mode);
241 			if (bitmap != NULL)
242 				DragMessage(&archive, bitmap, mode, offset, this);
243 			else {
244 				DragMessage(&archive,
245 					ConvertFromScreen(fTarget->ConvertToScreen(fTarget->Bounds())),
246 					this);
247 			}
248 		} else
249 			_ShowPopUp(fTarget, where);
250 	}
251 }
252 
253 
254 void
255 BDragger::MouseUp(BPoint point)
256 {
257 	BView::MouseUp(point);
258 }
259 
260 
261 void
262 BDragger::MouseMoved(BPoint point, uint32 code, const BMessage *msg)
263 {
264 	BView::MouseMoved(point, code, msg);
265 }
266 
267 
268 void
269 BDragger::MessageReceived(BMessage *msg)
270 {
271 	if (msg->what == B_TRASH_TARGET) {
272 		if (fShelf != NULL)
273 			Window()->PostMessage(kDeleteReplicant, fTarget, NULL);
274 		else {
275 			(new BAlert("??",
276 				"Can't delete this replicant from its original application. Life goes on.",
277 				"OK", NULL, NULL, B_WIDTH_FROM_WIDEST, B_WARNING_ALERT))->Go(NULL);
278 		}
279 	} else if (msg->what == _SHOW_DRAG_HANDLES_) {
280 		// this code is used whenever the "are draggers drawn" option is changed
281 		if (fRelation == TARGET_IS_CHILD) {
282 			fTransition = true;
283 			Invalidate();
284 			Flush();
285 			fTransition = false;
286 		} else {
287 			if ((fShelf && (fShelf->AllowsDragging() && AreDraggersDrawn()))
288 				|| AreDraggersDrawn())
289 				Show();
290 			else
291 				Hide();
292 		}
293 	} else
294 		BView::MessageReceived(msg);
295 }
296 
297 
298 void
299 BDragger::FrameMoved(BPoint newPosition)
300 {
301 	BView::FrameMoved(newPosition);
302 }
303 
304 
305 void
306 BDragger::FrameResized(float newWidth, float newHeight)
307 {
308 	BView::FrameResized(newWidth, newHeight);
309 }
310 
311 
312 status_t
313 BDragger::ShowAllDraggers()
314 {
315 	BPrivate::AppServerLink link;
316 	link.StartMessage(AS_SET_SHOW_ALL_DRAGGERS);
317 	link.Attach<bool>(true);
318 
319 	status_t status = link.Flush();
320 	if (status == B_OK) {
321 		sVisible = true;
322 		sVisibleInitialized = true;
323 	}
324 
325 	return status;
326 }
327 
328 
329 status_t
330 BDragger::HideAllDraggers()
331 {
332 	BPrivate::AppServerLink link;
333 	link.StartMessage(AS_SET_SHOW_ALL_DRAGGERS);
334 	link.Attach<bool>(false);
335 
336 	status_t status = link.Flush();
337 	if (status == B_OK) {
338 		sVisible = false;
339 		sVisibleInitialized = true;
340 	}
341 
342 	return status;
343 }
344 
345 
346 bool
347 BDragger::AreDraggersDrawn()
348 {
349 	BAutolock _(sLock);
350 
351 	if (!sVisibleInitialized) {
352 		BPrivate::AppServerLink link;
353 		link.StartMessage(AS_GET_SHOW_ALL_DRAGGERS);
354 
355 		status_t status;
356 		if (link.FlushWithReply(status) == B_OK && status == B_OK) {
357 			link.Read<bool>(&sVisible);
358 			sVisibleInitialized = true;
359 		} else
360 			return false;
361 	}
362 
363 	return sVisible;
364 }
365 
366 
367 BHandler*
368 BDragger::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
369 	int32 form, const char* property)
370 {
371 	return BView::ResolveSpecifier(message, index, specifier, form, property);
372 }
373 
374 
375 status_t
376 BDragger::GetSupportedSuites(BMessage* data)
377 {
378 	return BView::GetSupportedSuites(data);
379 }
380 
381 
382 status_t
383 BDragger::Perform(perform_code code, void* _data)
384 {
385 	switch (code) {
386 		case PERFORM_CODE_MIN_SIZE:
387 			((perform_data_min_size*)_data)->return_value
388 				= BDragger::MinSize();
389 			return B_OK;
390 		case PERFORM_CODE_MAX_SIZE:
391 			((perform_data_max_size*)_data)->return_value
392 				= BDragger::MaxSize();
393 			return B_OK;
394 		case PERFORM_CODE_PREFERRED_SIZE:
395 			((perform_data_preferred_size*)_data)->return_value
396 				= BDragger::PreferredSize();
397 			return B_OK;
398 		case PERFORM_CODE_LAYOUT_ALIGNMENT:
399 			((perform_data_layout_alignment*)_data)->return_value
400 				= BDragger::LayoutAlignment();
401 			return B_OK;
402 		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
403 			((perform_data_has_height_for_width*)_data)->return_value
404 				= BDragger::HasHeightForWidth();
405 			return B_OK;
406 		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
407 		{
408 			perform_data_get_height_for_width* data
409 				= (perform_data_get_height_for_width*)_data;
410 			BDragger::GetHeightForWidth(data->width, &data->min, &data->max,
411 				&data->preferred);
412 			return B_OK;
413 }
414 		case PERFORM_CODE_SET_LAYOUT:
415 		{
416 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
417 			BDragger::SetLayout(data->layout);
418 			return B_OK;
419 		}
420 		case PERFORM_CODE_INVALIDATE_LAYOUT:
421 		{
422 			perform_data_invalidate_layout* data
423 				= (perform_data_invalidate_layout*)_data;
424 			BDragger::InvalidateLayout(data->descendants);
425 			return B_OK;
426 		}
427 		case PERFORM_CODE_DO_LAYOUT:
428 		{
429 			BDragger::DoLayout();
430 			return B_OK;
431 		}
432 	}
433 
434 	return BView::Perform(code, _data);
435 }
436 
437 
438 void
439 BDragger::ResizeToPreferred()
440 {
441 	BView::ResizeToPreferred();
442 }
443 
444 
445 void
446 BDragger::GetPreferredSize(float* _width, float* _height)
447 {
448 	BView::GetPreferredSize(_width, _height);
449 }
450 
451 
452 void
453 BDragger::MakeFocus(bool state)
454 {
455 	BView::MakeFocus(state);
456 }
457 
458 
459 void
460 BDragger::AllAttached()
461 {
462 	BView::AllAttached();
463 }
464 
465 
466 void
467 BDragger::AllDetached()
468 {
469 	BView::AllDetached();
470 }
471 
472 
473 status_t
474 BDragger::SetPopUp(BPopUpMenu *menu)
475 {
476 	if (fPopUp != menu)
477 		delete fPopUp;
478 
479 	fPopUp = menu;
480 	return B_OK;
481 }
482 
483 
484 BPopUpMenu *
485 BDragger::PopUp() const
486 {
487 	if (fPopUp == NULL && fTarget)
488 		const_cast<BDragger *>(this)->_BuildDefaultPopUp();
489 
490 	return fPopUp;
491 }
492 
493 
494 bool
495 BDragger::InShelf() const
496 {
497 	return fShelf != NULL;
498 }
499 
500 
501 BView *
502 BDragger::Target() const
503 {
504 	return fTarget;
505 }
506 
507 
508 BBitmap *
509 BDragger::DragBitmap(BPoint *offset, drawing_mode *mode)
510 {
511 	return NULL;
512 }
513 
514 
515 bool
516 BDragger::IsVisibilityChanging() const
517 {
518 	return fTransition;
519 }
520 
521 
522 void BDragger::_ReservedDragger2() {}
523 void BDragger::_ReservedDragger3() {}
524 void BDragger::_ReservedDragger4() {}
525 
526 
527 BDragger &
528 BDragger::operator=(const BDragger &)
529 {
530 	return *this;
531 }
532 
533 
534 /*static*/ void
535 BDragger::_UpdateShowAllDraggers(bool visible)
536 {
537 	BAutolock _(sLock);
538 
539 	sVisibleInitialized = true;
540 	sVisible = visible;
541 
542 	for (int32 i = sList.CountItems(); i-- > 0;) {
543 		BDragger* dragger = (BDragger*)sList.ItemAt(i);
544 		BMessenger target(dragger);
545 		target.SendMessage(_SHOW_DRAG_HANDLES_);
546 	}
547 }
548 
549 
550 void
551 BDragger::_AddToList()
552 {
553 	BAutolock _(sLock);
554 	sList.AddItem(this);
555 
556 	bool allowsDragging = true;
557 	if (fShelf)
558 		allowsDragging = fShelf->AllowsDragging();
559 
560 	if (!AreDraggersDrawn() || !allowsDragging) {
561 		// The dragger is not shown - but we can't hide us in case we're the
562 		// parent of the actual target view (because then you couldn't see
563 		// it anymore).
564 		if (fRelation != TARGET_IS_CHILD)
565 			Hide();
566 	}
567 }
568 
569 
570 void
571 BDragger::_RemoveFromList()
572 {
573 	BAutolock _(sLock);
574 	sList.RemoveItem(this);
575 }
576 
577 
578 status_t
579 BDragger::_DetermineRelationship()
580 {
581 	if (fTarget) {
582 		if (fTarget == Parent())
583 			fRelation = TARGET_IS_PARENT;
584 		else if (fTarget == ChildAt(0))
585 			fRelation = TARGET_IS_CHILD;
586 		else
587 			fRelation = TARGET_IS_SIBLING;
588 	} else {
589 		if (fRelation == TARGET_IS_PARENT)
590 			fTarget = Parent();
591 		else if (fRelation == TARGET_IS_CHILD)
592 			fTarget = ChildAt(0);
593 		else
594 			return B_ERROR;
595 	}
596 
597 	if (fRelation == TARGET_IS_PARENT) {
598 		BRect bounds (Frame());
599 		BRect parentBounds (Parent()->Bounds());
600 		if (!parentBounds.Contains(bounds))
601 			MoveTo(parentBounds.right - bounds.Width(),
602 				parentBounds.bottom - bounds.Height());
603 	}
604 
605 	return B_OK;
606 }
607 
608 
609 status_t
610 BDragger::_SetViewToDrag(BView *target)
611 {
612 	if (target->Window() != Window())
613 		return B_ERROR;
614 
615 	fTarget = target;
616 
617 	if (Window())
618 		_DetermineRelationship();
619 
620 	return B_OK;
621 }
622 
623 
624 void
625 BDragger::_SetShelf(BShelf *shelf)
626 {
627 	fShelf = shelf;
628 }
629 
630 
631 void
632 BDragger::_SetZombied(bool state)
633 {
634 	fIsZombie = state;
635 
636 	if (state) {
637 		SetLowColor(kZombieColor);
638 		SetViewColor(kZombieColor);
639 	}
640 }
641 
642 
643 void
644 BDragger::_BuildDefaultPopUp()
645 {
646 	fPopUp = new BPopUpMenu("Shelf", false, false, B_ITEMS_IN_COLUMN);
647 
648 	// About
649 	BMessage *msg = new BMessage(B_ABOUT_REQUESTED);
650 
651 	const char *name = fTarget->Name();
652 	if (name)
653 		msg->AddString("target", name);
654 
655 	char about[B_OS_NAME_LENGTH];
656 	snprintf(about, B_OS_NAME_LENGTH, "About %s" B_UTF8_ELLIPSIS, name);
657 
658 	fPopUp->AddItem(new BMenuItem(about, msg));
659 	fPopUp->AddSeparatorItem();
660 	fPopUp->AddItem(new BMenuItem("Remove Replicant",
661 		new BMessage(kDeleteReplicant)));
662 }
663 
664 
665 void
666 BDragger::_ShowPopUp(BView *target, BPoint where)
667 {
668 	BPoint point = ConvertToScreen(where);
669 
670 	if (!fPopUp && fTarget)
671 		_BuildDefaultPopUp();
672 
673 	fPopUp->SetTargetForItems(fTarget);
674 
675 	float menuWidth, menuHeight;
676 	fPopUp->GetPreferredSize(&menuWidth, &menuHeight);
677 	BRect rect(0, 0, menuWidth, menuHeight);
678 	rect.InsetBy(-0.5, -0.5);
679 	rect.OffsetTo(point);
680 
681 	fPopUp->Go(point, true, false, rect, true);
682 }
683 
684 
685 #if __GNUC__ < 3
686 
687 extern "C" BBitmap*
688 _ReservedDragger1__8BDragger(BDragger* dragger, BPoint* offset,
689 	drawing_mode* mode)
690 {
691 	return dragger->BDragger::DragBitmap(offset, mode);
692 }
693 
694 #endif
695