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