xref: /haiku/src/kits/interface/Dragger.cpp (revision 0e50eab75e25d0d82090e22dbff766dfaa6f5e86)
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 <Beep.h>
15 #include <Bitmap.h>
16 #include <Dragger.h>
17 #include <MenuItem.h>
18 #include <Message.h>
19 #include <PopUpMenu.h>
20 #include <Shelf.h>
21 #include <Window.h>
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 
26 
27 bool BDragger::sVisible;
28 bool BDragger::sInited;
29 BLocker BDragger::sLock("BDragger_sLock");
30 BList BDragger::sList;
31 
32 const static rgb_color kZombieColor = {220, 220, 220, 255};
33 
34 const unsigned char
35 kHandBitmap[] = {
36 	255, 255,   0,   0,   0, 255, 255, 255,
37 	255, 255,   0, 131, 131,   0, 255, 255,
38 	  0,   0,   0,   0, 131, 131,   0,   0,
39 	  0, 131,   0,   0, 131, 131,   0,   0,
40 	  0, 131, 131, 131, 131, 131,   0,   0,
41 	255,   0, 131, 131, 131, 131,   0,   0,
42 	255, 255,   0,   0,   0,   0,   0,   0,
43 	255, 255, 255, 255, 255, 255,   0,   0
44 };
45 
46 
47 BDragger::BDragger(BRect bounds, BView *target, uint32 rmask, uint32 flags)
48 	:	BView(bounds, "_dragger_", rmask, flags),
49 		fTarget(target),
50 		fRelation(TARGET_UNKNOWN),
51 		fShelf(NULL),
52 		fTransition(false),
53 		fIsZombie(false),
54 		fErrCount(0),
55 		fPopUp(NULL)
56 {
57 	fBitmap = new BBitmap(BRect(0.0f, 0.0f, 7.0f, 7.0f), B_CMAP8, false, false);
58 	fBitmap->SetBits(kHandBitmap, fBitmap->BitsLength(), 0, B_CMAP8);
59 }
60 
61 
62 BDragger::BDragger(BMessage *data)
63 	:	BView(data),
64 		fTarget(NULL),
65 		fRelation(TARGET_UNKNOWN),
66 		fShelf(NULL),
67 		fTransition(false),
68 		fIsZombie(false),
69 		fErrCount(0),
70 		fPopUp(NULL)
71 {
72 	data->FindInt32("_rel", (int32 *)&fRelation);
73 
74 	fBitmap = new BBitmap(BRect(0.0f, 0.0f, 7.0f, 7.0f), B_CMAP8, false, false);
75 	fBitmap->SetBits(kHandBitmap, fBitmap->BitsLength(), 0, B_CMAP8);
76 
77 	BMessage popupMsg;
78 
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 	else
102 		return NULL;
103 }
104 
105 
106 status_t
107 BDragger::Archive(BMessage *data, bool deep) const
108 {
109 	BMessage popupMsg;
110 	status_t ret = B_OK;
111 
112 	if (fPopUp) {
113 		ret = fPopUp->Archive(&popupMsg);
114 		if (ret == B_OK)
115 			ret = data->AddMessage("_popup", &popupMsg);
116 	}
117 
118 	if (ret == B_OK)
119 		ret = data->AddInt32("_rel", fRelation);
120 	if (ret != B_OK)
121 		return ret;
122 	return BView::Archive(data, deep);
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 	determine_relationship();
138 	ListManage(true);
139 }
140 
141 
142 void
143 BDragger::DetachedFromWindow()
144 {
145 	ListManage(false);
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 
192 	} else {
193 		bigtime_t time = system_time();
194 		bigtime_t clickSpeed = 0;
195 
196 		get_click_speed(&clickSpeed);
197 
198 		bool drag = false;
199 
200 		while (true) {
201 			BPoint mousePoint;
202 			GetMouse(&mousePoint, &buttons);
203 
204 			if (!buttons || system_time() > time + clickSpeed)
205 				break;
206 
207 			if (mousePoint != where) {
208 				drag = true;
209 				break;
210 			}
211 
212 			snooze(40000);
213 		}
214 
215 		if (drag) {
216 			BMessage archive(B_ARCHIVED_OBJECT);
217 
218 			if (fRelation == TARGET_IS_PARENT)
219 				fTarget->Archive(&archive);
220 			else if (fRelation == TARGET_IS_CHILD)
221 				Archive(&archive);
222 			else {
223 				if (fTarget->Archive(&archive)) {
224 					BMessage widget(B_ARCHIVED_OBJECT);
225 
226 					if (Archive(&widget))
227 						archive.AddMessage("__widget", &widget);
228 				}
229 			}
230 
231 			archive.AddInt32("be:actions", B_TRASH_TARGET);
232 
233 			BPoint offset;
234 			drawing_mode mode;
235 			BBitmap *bitmap = DragBitmap(&offset, &mode);
236 			if (bitmap)
237 				DragMessage(&archive, bitmap, mode, offset, this);
238 			else
239 				DragMessage(&archive,
240 					ConvertFromScreen(fTarget->ConvertToScreen(fTarget->Bounds())),
241 					this);
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)
267 			Window()->PostMessage(kDeleteDragger, 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 		if (fRelation == TARGET_IS_CHILD) {
275 			fTransition = true;
276 			Invalidate();
277 			Flush();
278 			fTransition = false;
279 
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 	// TODO: Implement. Should ask the registrar or the app server
326 	return true;
327 }
328 
329 
330 BHandler *
331 BDragger::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier,
332 					int32 form, const char *property)
333 {
334 	return BView::ResolveSpecifier(msg, index, specifier, form, property);
335 }
336 
337 
338 status_t
339 BDragger::GetSupportedSuites(BMessage *data)
340 {
341 	return BView::GetSupportedSuites(data);
342 }
343 
344 
345 status_t
346 BDragger::Perform(perform_code d, void *arg)
347 {
348 	return BView::Perform(d, arg);
349 }
350 
351 
352 void
353 BDragger::ResizeToPreferred()
354 {
355 	BView::ResizeToPreferred();
356 }
357 
358 
359 void
360 BDragger::GetPreferredSize(float *width, float *height)
361 {
362 	BView::GetPreferredSize(width, height);
363 }
364 
365 
366 void
367 BDragger::MakeFocus(bool state)
368 {
369 	BView::MakeFocus(state);
370 }
371 
372 
373 void
374 BDragger::AllAttached()
375 {
376 	BView::AllAttached();
377 }
378 
379 
380 void
381 BDragger::AllDetached()
382 {
383 	BView::AllDetached();
384 }
385 
386 
387 status_t
388 BDragger::SetPopUp(BPopUpMenu *context_menu)
389 {
390 	if (fPopUp && fPopUp != context_menu)
391 		delete fPopUp;
392 
393 	fPopUp = context_menu;
394 	return B_OK;
395 }
396 
397 
398 BPopUpMenu *
399 BDragger::PopUp() const
400 {
401 	if (!fPopUp && fTarget)
402 		const_cast<BDragger *>(this)->BuildDefaultPopUp();
403 
404 	return fPopUp;
405 }
406 
407 
408 bool
409 BDragger::InShelf() const
410 {
411 	return fShelf != NULL;
412 }
413 
414 
415 BView *
416 BDragger::Target() const
417 {
418 	return fTarget;
419 }
420 
421 
422 BBitmap *
423 BDragger::DragBitmap(BPoint *offset, drawing_mode *mode)
424 {
425 	return NULL;
426 }
427 
428 
429 bool
430 BDragger::IsVisibilityChanging() const
431 {
432 	return fTransition;
433 }
434 
435 
436 void BDragger::_ReservedDragger2() {}
437 void BDragger::_ReservedDragger3() {}
438 void BDragger::_ReservedDragger4() {}
439 
440 
441 BDragger &
442 BDragger::operator=(const BDragger &)
443 {
444 	return *this;
445 }
446 
447 
448 void
449 BDragger::ListManage(bool add)
450 {
451 	if (sLock.Lock()) {
452 		bool drawn = AreDraggersDrawn();
453 
454 		if (add) {
455 			bool dragging = true;
456 
457 			sList.AddItem(this);
458 
459 			if (fShelf)
460 				dragging = fShelf->AllowsDragging();
461 
462 			if (!drawn && !dragging) {
463 				if (fRelation != TARGET_IS_CHILD)
464 					Hide();
465 			}
466 		} else
467 			sList.RemoveItem(this);
468 
469 		sLock.Unlock();
470 	}
471 }
472 
473 
474 status_t
475 BDragger::determine_relationship()
476 {
477 	status_t err = B_OK;
478 
479 	if (fTarget) {
480 		if (fTarget == Parent())
481 			fRelation = TARGET_IS_PARENT;
482 		else if (fTarget == ChildAt(0))
483 			fRelation = TARGET_IS_CHILD;
484 		else
485 			fRelation = TARGET_IS_SIBLING;
486 	} else {
487 		if (fRelation == TARGET_IS_PARENT)
488 			fTarget = Parent();
489 		else if (fRelation == TARGET_IS_CHILD)
490 			fTarget = ChildAt(0);
491 		else
492 			err = B_ERROR;
493 	}
494 
495 	return err;
496 }
497 
498 
499 status_t
500 BDragger::SetViewToDrag(BView *target)
501 {
502 	if (target->Window() != Window())
503 		return B_ERROR;
504 
505 	fTarget = target;
506 
507 	if (Window())
508 		determine_relationship();
509 
510 	return B_OK;
511 }
512 
513 
514 void
515 BDragger::SetShelf(BShelf *shelf)
516 {
517 	fShelf = shelf;
518 }
519 
520 
521 void
522 BDragger::SetZombied(bool state)
523 {
524 	fIsZombie = state;
525 
526 	if (state) {
527 		SetLowColor(kZombieColor);
528 		SetViewColor(kZombieColor);
529 	}
530 }
531 
532 
533 void
534 BDragger::BuildDefaultPopUp()
535 {
536 	fPopUp = new BPopUpMenu("Shelf", false, false, B_ITEMS_IN_COLUMN);
537 
538 	// About
539 	BMessage *msg = new BMessage(B_ABOUT_REQUESTED);
540 
541 	const char *name = fTarget->Name();
542 
543 	if (name)
544 		msg->AddString("target", name);
545 
546 	char about[B_OS_NAME_LENGTH];
547 	snprintf(about, B_OS_NAME_LENGTH, "About %s", name);
548 
549 	fPopUp->AddItem(new BMenuItem(about, msg));
550 
551 	// Separator
552 	fPopUp->AddItem(new BSeparatorItem());
553 
554 	// Delete
555 	fPopUp->AddItem(new BMenuItem("Delete", new BMessage(kDeleteDragger)));
556 }
557 
558 
559 void
560 BDragger::ShowPopUp(BView *target, BPoint where)
561 {
562 	BPoint point = ConvertToScreen(where);
563 
564 	if (!fPopUp && fTarget)
565 		BuildDefaultPopUp();
566 
567 	fPopUp->SetTargetForItems(fTarget);
568 
569 	float menuWidth, menuHeight;
570 	fPopUp->GetPreferredSize(&menuWidth, &menuHeight);
571 	BRect rect(0, 0, menuWidth, menuHeight);
572 	rect.InsetBy(-0.5, -0.5);
573 	rect.OffsetTo(point);
574 
575 	fPopUp->Go(point, true, false, rect, true);
576 }
577