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