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