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