xref: /haiku/src/apps/deskbar/StatusView.cpp (revision c2751c41b7404c4e7b0b6ed723b92505a6cf332b)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered
30 trademarks of Be Incorporated in the United States and other countries. Other
31 brand product names are registered trademarks or trademarks of their respective
32 holders.
33 All rights reserved.
34 */
35 
36 
37 #include "StatusView.h"
38 
39 #include <errno.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44 #include <algorithm>
45 
46 #include <fs_index.h>
47 #include <fs_info.h>
48 
49 #include <Application.h>
50 #include <Beep.h>
51 #include <Bitmap.h>
52 #include <Catalog.h>
53 #include <ControlLook.h>
54 #include <Debug.h>
55 #include <Directory.h>
56 #include <FindDirectory.h>
57 #include <Locale.h>
58 #include <MenuItem.h>
59 #include <NodeInfo.h>
60 #include <NodeMonitor.h>
61 #include <Path.h>
62 #include <PopUpMenu.h>
63 #include <Roster.h>
64 #include <Screen.h>
65 #include <Volume.h>
66 #include <VolumeRoster.h>
67 #include <Window.h>
68 
69 #include "icons.h"
70 
71 #include "BarApp.h"
72 #include "DeskbarUtils.h"
73 #include "ResourceSet.h"
74 #include "StatusViewShelf.h"
75 #include "TimeView.h"
76 
77 
78 using std::max;
79 
80 #ifdef DB_ADDONS
81 // Add-on support
82 //
83 // Item - internal item list (node, eref, etc)
84 // Icon - physical replicant handed to the DeskbarClass class
85 // AddOn - attribute based add-on
86 
87 const char* const kInstantiateItemCFunctionName = "instantiate_deskbar_item";
88 const char* const kInstantiateEntryCFunctionName = "instantiate_deskbar_entry";
89 const char* const kReplicantSettingsFile = "replicants";
90 const char* const kReplicantPathField = "replicant_path";
91 
92 float gMinimumWindowWidth = kGutter + kMinimumTrayWidth + kDragRegionWidth;
93 
94 
95 static void
96 DumpItem(DeskbarItemInfo* item)
97 {
98 	printf("is addon: %i, id: %" B_PRId32 "\n", item->isAddOn, item->id);
99 	printf("entry_ref:  %" B_PRIdDEV ", %" B_PRIdINO ", %s\n",
100 		item->entryRef.device, item->entryRef.directory, item->entryRef.name);
101 	printf("node_ref:  %" B_PRIdDEV ", %" B_PRIdINO "\n", item->nodeRef.device,
102 		item->nodeRef.node);
103 }
104 
105 
106 static void
107 DumpList(BList* itemlist)
108 {
109 	int32 count = itemlist->CountItems() - 1;
110 	if (count < 0) {
111 		printf("no items in list\n");
112 		return;
113 	}
114 	for (int32 i = count; i >= 0; i--) {
115 		DeskbarItemInfo* item = (DeskbarItemInfo*)itemlist->ItemAt(i);
116 		if (!item)
117 			continue;
118 
119 		DumpItem(item);
120 	}
121 }
122 #endif	/* DB_ADDONS */
123 
124 
125 #undef B_TRANSLATION_CONTEXT
126 #define B_TRANSLATION_CONTEXT "Tray"
127 
128 // don't change the name of this view to anything other than "Status"!
129 
130 TReplicantTray::TReplicantTray(TBarView* parent, bool vertical)
131 	:
132 	BView(BRect(0, 0, 1, 1), "Status", B_FOLLOW_LEFT | B_FOLLOW_TOP,
133 		B_WILL_DRAW | B_FRAME_EVENTS),
134 	fTime(NULL),
135 	fBarView(parent),
136 	fShelf(new TReplicantShelf(this)),
137 	fMultiRowMode(vertical),
138 	fMinimumTrayWidth(kMinimumTrayWidth),
139 	fAlignmentSupport(false)
140 {
141 	// init the minimum window width according to the logo.
142 	const BBitmap* logoBitmap = AppResSet()->FindBitmap(B_MESSAGE_TYPE,
143 		R_LeafLogoBitmap);
144 	if (logoBitmap != NULL) {
145 		gMinimumWindowWidth = std::max(gMinimumWindowWidth,
146 			2 * (logoBitmap->Bounds().Width() + 8));
147 		fMinimumTrayWidth = gMinimumWindowWidth - kGutter - kDragRegionWidth;
148 	}
149 
150 	// Create the time view
151 	fTime = new TTimeView(fMinimumTrayWidth, kMaxReplicantHeight - 1.0);
152 }
153 
154 
155 TReplicantTray::~TReplicantTray()
156 {
157 	delete fShelf;
158 	delete fTime;
159 }
160 
161 
162 void
163 TReplicantTray::AttachedToWindow()
164 {
165 	BView::AttachedToWindow();
166 
167 	if (be_control_look != NULL) {
168 		SetViewColor(Parent()->ViewColor());
169 	} else {
170 		SetViewColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
171 			B_DARKEN_1_TINT));
172 	}
173 	SetDrawingMode(B_OP_COPY);
174 
175 	Window()->SetPulseRate(1000000);
176 
177 	clock_settings* clock = ((TBarApp*)be_app)->ClockSettings();
178 	fTime->SetShowSeconds(clock->showSeconds);
179 	fTime->SetShowDayOfWeek(clock->showDayOfWeek);
180 	fTime->SetShowTimeZone(clock->showTimeZone);
181 
182 	AddChild(fTime);
183 	fTime->MoveTo(Bounds().right - fTime->Bounds().Width() - 1, 2);
184 
185 	if (!((TBarApp*)be_app)->Settings()->showClock)
186 		fTime->Hide();
187 
188 #ifdef DB_ADDONS
189 	// load addons and rehydrate archives
190 #if !defined(HAIKU_TARGET_PLATFORM_LIBBE_TEST)
191 	InitAddOnSupport();
192 #endif
193 #endif
194 	ResizeToPreferred();
195 }
196 
197 
198 void
199 TReplicantTray::DetachedFromWindow()
200 {
201 #ifdef DB_ADDONS
202 	// clean up add-on support
203 #if !defined(HAIKU_TARGET_PLATFORM_LIBBE_TEST)
204 	DeleteAddOnSupport();
205 #endif
206 #endif
207 	BView::DetachedFromWindow();
208 }
209 
210 
211 /*! Width is set to a minimum of kMinimumReplicantCount by kMaxReplicantWidth
212 	if not in multirowmode and greater than kMinimumReplicantCount
213 	the width should be calculated based on the actual replicant widths
214 */
215 void
216 TReplicantTray::GetPreferredSize(float* preferredWidth, float* preferredHeight)
217 {
218 	float width = 0, height = kMinimumTrayHeight;
219 
220 	if (fMultiRowMode) {
221 		if (fShelf->CountReplicants() > 0)
222 			height = fRightBottomReplicant.bottom;
223 
224 		// the height will be uniform for the number of rows necessary to show
225 		// all the reps + any gutters necessary for spacing
226 		int32 rowCount = (int32)(height / kMaxReplicantHeight);
227 		height = kGutter + (rowCount * kMaxReplicantHeight)
228 			+ ((rowCount - 1) * kIconGap) + kGutter;
229 		height = max(kMinimumTrayHeight, height);
230 		width = fMinimumTrayWidth;
231 	} else {
232 		// if last replicant overruns clock then resize to accomodate
233 		if (fShelf->CountReplicants() > 0) {
234 			if (!fTime->IsHidden() && fTime->Frame().left
235 				< fRightBottomReplicant.right + 6) {
236 				width = fRightBottomReplicant.right + 6
237 					+ fTime->Frame().Width();
238 			} else
239 				width = fRightBottomReplicant.right + 3;
240 		}
241 
242 		// this view has a fixed minimum width
243 		width = max(fMinimumTrayWidth, width);
244 		height = kGutter + static_cast<TBarApp*>(be_app)->IconSize() + kGutter;
245 	}
246 
247 	*preferredWidth = width;
248 	// add 1 for the border
249 	*preferredHeight = height + 1;
250 }
251 
252 
253 void
254 TReplicantTray::AdjustPlacement()
255 {
256 	// called when an add-on has been added or removed
257 	// need to resize the parent of this accordingly
258 
259 	BRect bounds = Bounds();
260 	float width, height;
261 	GetPreferredSize(&width, &height);
262 
263 	if (width == bounds.Width() && height == bounds.Height()) {
264 		// no need to change anything
265 		return;
266 	}
267 
268 	Parent()->ResizeToPreferred();
269 	fBarView->UpdatePlacement();
270 	Parent()->Invalidate();
271 	Invalidate();
272 }
273 
274 
275 void
276 TReplicantTray::MessageReceived(BMessage* message)
277 {
278 	switch (message->what) {
279 		case B_LOCALE_CHANGED:
280 		{
281 			if (fTime == NULL)
282 				return;
283 
284 			fTime->Update();
285 
286 			// time string reformat -> realign
287 			RealignReplicants();
288 			AdjustPlacement();
289 			break;
290 		}
291 
292 		case kShowHideTime:
293 			// from context menu in clock and in this view
294 			ShowHideTime();
295 			break;
296 
297 		case kShowSeconds:
298 			if (fTime == NULL)
299 				return;
300 
301 			fTime->SetShowSeconds(!fTime->ShowSeconds());
302 
303 			// time string reformat -> realign
304 			RealignReplicants();
305 			AdjustPlacement();
306 			break;
307 
308 		case kShowDayOfWeek:
309 			if (fTime == NULL)
310 				return;
311 
312 			fTime->SetShowDayOfWeek(!fTime->ShowDayOfWeek());
313 
314 			// time string reformat -> realign
315 			RealignReplicants();
316 			AdjustPlacement();
317 			break;
318 
319 		case kShowTimeZone:
320 			if (fTime == NULL)
321 				return;
322 
323 			fTime->SetShowTimeZone(!fTime->ShowTimeZone());
324 
325 			// time string reformat -> realign
326 			RealignReplicants();
327 			AdjustPlacement();
328 			break;
329 
330 		case kGetClockSettings:
331 		{
332 			if (fTime == NULL)
333 				return;
334 
335 			bool showClock = !fTime->IsHidden();
336 			bool showSeconds = fTime->ShowSeconds();
337 			bool showDayOfWeek = fTime->ShowDayOfWeek();
338 			bool showTimeZone = fTime->ShowTimeZone();
339 
340 			BMessage reply(kGetClockSettings);
341 			reply.AddBool("showClock", showClock);
342 			reply.AddBool("showSeconds", showSeconds);
343 			reply.AddBool("showDayOfWeek", showDayOfWeek);
344 			reply.AddBool("showTimeZone", showTimeZone);
345 			message->SendReply(&reply);
346 			break;
347 		}
348 
349 #ifdef DB_ADDONS
350 		case B_NODE_MONITOR:
351 			HandleEntryUpdate(message);
352 			break;
353 #endif
354 
355 		default:
356 			BView::MessageReceived(message);
357 			break;
358 	}
359 }
360 
361 
362 void
363 TReplicantTray::MouseDown(BPoint where)
364 {
365 #ifdef DB_ADDONS
366 	if (modifiers() & B_CONTROL_KEY)
367 		DumpList(fItemList);
368 #endif
369 
370 	uint32 buttons;
371 
372 	Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
373 	if (buttons == B_SECONDARY_MOUSE_BUTTON) {
374 		ShowReplicantMenu(where);
375 	} else {
376 		BPoint save = where;
377 		bigtime_t doubleClickSpeed;
378 		bigtime_t start = system_time();
379 		uint32 buttons;
380 
381 		get_click_speed(&doubleClickSpeed);
382 
383 		do {
384 			if (fabs(where.x - save.x) > 4 || fabs(where.y - save.y) > 4)
385 				// user moved out of bounds of click area
386 				break;
387 
388 			if ((system_time() - start) > (2 * doubleClickSpeed)) {
389 				ShowReplicantMenu(where);
390 				break;
391 			}
392 
393 			snooze(50000);
394 			GetMouse(&where, &buttons);
395 		} while (buttons);
396 	}
397 	BView::MouseDown(where);
398 }
399 
400 
401 void
402 TReplicantTray::ShowReplicantMenu(BPoint point)
403 {
404 	BPopUpMenu* menu = new BPopUpMenu("", false, false);
405 	menu->SetFont(be_plain_font);
406 
407 	// If clock is visible show the extended menu, otherwise show "Show clock"
408 
409 	if (!fTime->IsHidden())
410 		fTime->ShowTimeOptions(ConvertToScreen(point));
411 	else {
412 		BMenuItem* item = new BMenuItem(B_TRANSLATE("Show clock"),
413 			new BMessage(kShowHideTime));
414 		menu->AddItem(item);
415 		menu->SetTargetForItems(this);
416 		BPoint where = ConvertToScreen(point);
417 		menu->Go(where, true, true, BRect(where - BPoint(4, 4),
418 			where + BPoint(4, 4)), true);
419 	}
420 }
421 
422 
423 void
424 TReplicantTray::SetMultiRow(bool state)
425 {
426 	fMultiRowMode = state;
427 }
428 
429 
430 void
431 TReplicantTray::ShowHideTime()
432 {
433 	if (fTime == NULL)
434 		return;
435 
436 	// Check from the point of view of fTime because we need to ignore
437 	// whether or not the parent window is hidden.
438 	if (fTime->IsHidden(fTime))
439 		fTime->Show();
440 	else
441 		fTime->Hide();
442 
443 	RealignReplicants();
444 	AdjustPlacement();
445 
446 	// Check from the point of view of fTime ignoring parent's state.
447 	bool showClock = !fTime->IsHidden(fTime);
448 
449 	// Update showClock setting that gets saved to disk on quit
450 	((TBarApp*)be_app)->Settings()->showClock = showClock;
451 
452 	// Send a message to Time preferences telling it to update
453 	BMessenger messenger("application/x-vnd.Haiku-Time");
454 	BMessage message(kShowHideTime);
455 	message.AddBool("showClock", showClock);
456 	messenger.SendMessage(&message);
457 }
458 
459 
460 #ifdef DB_ADDONS
461 
462 
463 void
464 TReplicantTray::InitAddOnSupport()
465 {
466 	// list to maintain refs to each rep added/deleted
467 	fItemList = new BList();
468 	BPath path;
469 
470 	if (GetDeskbarSettingsDirectory(path, true) == B_OK) {
471 		path.Append(kReplicantSettingsFile);
472 
473 		BFile file(path.Path(), B_READ_ONLY);
474 		if (file.InitCheck() == B_OK) {
475 			status_t result;
476 			BEntry entry;
477 			int32 id;
478 			BString path;
479 			if (fAddOnSettings.Unflatten(&file) == B_OK) {
480 				for (int32 i = 0; fAddOnSettings.FindString(kReplicantPathField,
481 					i, &path) == B_OK; i++) {
482 					if (entry.SetTo(path.String()) == B_OK && entry.Exists()) {
483 						result = LoadAddOn(&entry, &id, false);
484 					} else
485 						result = B_ENTRY_NOT_FOUND;
486 
487 					if (result != B_OK) {
488 						fAddOnSettings.RemoveData(kReplicantPathField, i);
489 						--i;
490 					}
491 				}
492 			}
493 		}
494 	}
495 }
496 
497 
498 void
499 TReplicantTray::DeleteAddOnSupport()
500 {
501 	_SaveSettings();
502 
503 	for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) {
504 		DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->RemoveItem(i);
505 		if (item) {
506 			if (item->isAddOn)
507 				watch_node(&(item->nodeRef), B_STOP_WATCHING, this, Window());
508 
509 			delete item;
510 		}
511 	}
512 	delete fItemList;
513 
514 	// stop the volume mount/unmount watch
515 	stop_watching(this, Window());
516 }
517 
518 
519 DeskbarItemInfo*
520 TReplicantTray::DeskbarItemFor(node_ref& nodeRef)
521 {
522 	for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) {
523 		DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i);
524 		if (item == NULL)
525 			continue;
526 
527 		if (item->nodeRef == nodeRef)
528 			return item;
529 	}
530 
531 	return NULL;
532 }
533 
534 
535 DeskbarItemInfo*
536 TReplicantTray::DeskbarItemFor(int32 id)
537 {
538 	for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) {
539 		DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i);
540 		if (item == NULL)
541 			continue;
542 
543 		if (item->id == id)
544 			return item;
545 	}
546 
547 	return NULL;
548 }
549 
550 
551 bool
552 TReplicantTray::NodeExists(node_ref& nodeRef)
553 {
554 	return DeskbarItemFor(nodeRef) != NULL;
555 }
556 
557 
558 /*! This handles B_NODE_MONITOR & B_QUERY_UPDATE messages received
559 	for the registered add-ons.
560 */
561 void
562 TReplicantTray::HandleEntryUpdate(BMessage* message)
563 {
564 	int32 opcode;
565 	if (message->FindInt32("opcode", &opcode) != B_OK)
566 		return;
567 
568 	BPath path;
569 	switch (opcode) {
570 		case B_ENTRY_MOVED:
571 		{
572 			entry_ref ref;
573 			ino_t todirectory;
574 			ino_t node;
575 			const char* name;
576 			if (message->FindString("name", &name) == B_OK
577 				&& message->FindInt64("from directory", &(ref.directory))
578 				== B_OK
579 				&& message->FindInt64("to directory", &todirectory) == B_OK
580 				&& message->FindInt32("device", &(ref.device)) == B_OK
581 				&& message->FindInt64("node", &node) == B_OK ) {
582 
583 				if (!name)
584 					break;
585 
586 				ref.set_name(name);
587 				// change the directory reference to
588 				// the new directory
589 				MoveItem(&ref, todirectory);
590 			}
591 			break;
592 		}
593 
594 		case B_ENTRY_REMOVED:
595 		{
596 			// entry was rm'd from the device
597 			node_ref nodeRef;
598 			if (message->FindInt32("device", &(nodeRef.device)) == B_OK
599 				&& message->FindInt64("node", &(nodeRef.node)) == B_OK) {
600 				DeskbarItemInfo* item = DeskbarItemFor(nodeRef);
601 				if (item == NULL)
602 					break;
603 
604 				// If there is a team running where the add-on comes from,
605 				// we don't want to remove the icon yet.
606 				if (be_roster->IsRunning(&item->entryRef))
607 					break;
608 
609 				UnloadAddOn(&nodeRef, NULL, true, false);
610 			}
611 			break;
612 		}
613 	}
614 }
615 
616 
617 /*! The add-ons must support the exported C function API
618 	if they do, they will be loaded and added to deskbar
619 	primary function is the Instantiate function
620 */
621 status_t
622 TReplicantTray::LoadAddOn(BEntry* entry, int32* id, bool addToSettings)
623 {
624 	if (!entry)
625 		return B_ERROR;
626 
627 	node_ref nodeRef;
628 	entry->GetNodeRef(&nodeRef);
629 	// no duplicates
630 	if (NodeExists(nodeRef))
631 		return B_ERROR;
632 
633 	BNode node(entry);
634 	BPath path;
635 	status_t status = entry->GetPath(&path);
636 	if (status < B_OK)
637 		return status;
638 
639 	// load the add-on
640 	image_id image = load_add_on(path.Path());
641 	if (image < B_OK)
642 		return image;
643 
644 	// get the view loading function symbol
645 	//    we first look for a symbol that takes an image_id
646 	//    and entry_ref pointer, if not found, go with normal
647 	//    instantiate function
648 	BView* (*entryFunction)(image_id, const entry_ref*);
649 	BView* (*itemFunction)(void);
650 	BView* view = NULL;
651 
652 	entry_ref ref;
653 	entry->GetRef(&ref);
654 
655 	if (get_image_symbol(image, kInstantiateEntryCFunctionName,
656 			B_SYMBOL_TYPE_TEXT, (void**)&entryFunction) >= B_OK) {
657 		view = (*entryFunction)(image, &ref);
658 	} else if (get_image_symbol(image, kInstantiateItemCFunctionName,
659 			B_SYMBOL_TYPE_TEXT, (void**)&itemFunction) >= B_OK) {
660 		view = (*itemFunction)();
661 	} else {
662 		unload_add_on(image);
663 		return B_ERROR;
664 	}
665 
666 	if (view == NULL || IconExists(view->Name())) {
667 		delete view;
668 		unload_add_on(image);
669 		return B_ERROR;
670 	}
671 
672 	BMessage* data = new BMessage;
673 	view->Archive(data);
674 	delete view;
675 
676 	// add the rep; adds info to list
677 	if (AddIcon(data, id, &ref) != B_OK)
678 		delete data;
679 
680 	if (addToSettings) {
681 		fAddOnSettings.AddString(kReplicantPathField, path.Path());
682 		_SaveSettings();
683 	}
684 
685 	return B_OK;
686 }
687 
688 
689 status_t
690 TReplicantTray::AddItem(int32 id, node_ref nodeRef, BEntry& entry, bool isAddOn)
691 {
692 	DeskbarItemInfo* item = new DeskbarItemInfo;
693 	if (item == NULL)
694 		return B_NO_MEMORY;
695 
696 	item->id = id;
697 	item->isAddOn = isAddOn;
698 
699 	if (entry.GetRef(&item->entryRef) < B_OK) {
700 		item->entryRef.device = -1;
701 		item->entryRef.directory = -1;
702 		item->entryRef.name = NULL;
703 	}
704 	item->nodeRef = nodeRef;
705 
706 	fItemList->AddItem(item);
707 
708 	if (isAddOn)
709 		watch_node(&nodeRef, B_WATCH_NAME | B_WATCH_ATTR, this, Window());
710 
711 	return B_OK;
712 }
713 
714 
715 /**	from entry_removed message, when attribute removed
716  *	or when a device is unmounted (use removeall, by device)
717  */
718 
719 void
720 TReplicantTray::UnloadAddOn(node_ref* nodeRef, dev_t* device,
721 	bool which, bool removeAll)
722 {
723 	for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) {
724 		DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i);
725 		if (!item)
726 			continue;
727 
728 		if ((which && nodeRef && item->nodeRef == *nodeRef)
729 			|| (device && item->nodeRef.device == *device)) {
730 
731 			if (device && be_roster->IsRunning(&item->entryRef))
732 				continue;
733 
734 			RemoveIcon(item->id);
735 
736 			if (!removeAll)
737 				break;
738 		}
739 	}
740 }
741 
742 
743 void
744 TReplicantTray::RemoveItem(int32 id)
745 {
746 	DeskbarItemInfo* item = DeskbarItemFor(id);
747 	if (item == NULL)
748 		return;
749 
750 	// attribute was added via Deskbar API (AddItem(entry_ref*, int32*)
751 	if (item->isAddOn) {
752 		BPath path(&item->entryRef);
753 		BString storedPath;
754 		for (int32 i = 0;
755 			fAddOnSettings.FindString(kReplicantPathField, i, &storedPath)
756 				== B_OK; i++) {
757 			if (storedPath == path.Path()) {
758 				fAddOnSettings.RemoveData(kReplicantPathField, i);
759 				break;
760 			}
761 		}
762 		_SaveSettings();
763 
764 		BNode node(&item->entryRef);
765 		watch_node(&item->nodeRef, B_STOP_WATCHING, this, Window());
766 	}
767 
768 	fItemList->RemoveItem(item);
769 	delete item;
770 }
771 
772 
773 /**	ENTRY_MOVED message, moving only occurs on a device
774  *	copying will occur (ENTRY_CREATED) between devices
775  */
776 
777 void
778 TReplicantTray::MoveItem(entry_ref* ref, ino_t toDirectory)
779 {
780 	if (!ref)
781 		return;
782 
783 	// scan for a matching entry_ref and update it
784 	//
785 	// don't need to change node info as it does not change
786 
787 	for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) {
788 		DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i);
789 		if (!item)
790 			continue;
791 
792 		if (!strcmp(item->entryRef.name, ref->name)
793 			&& item->entryRef.device == ref->device
794 			&& item->entryRef.directory == ref->directory) {
795 			item->entryRef.directory = toDirectory;
796 			break;
797 		}
798 	}
799 }
800 
801 #endif // add-on support
802 
803 //	external add-on API routines
804 //	called using the new BDeskbar class
805 
806 //	existence of icon/replicant by name or ID
807 //	returns opposite
808 //	note: name and id are semi-private limiting
809 //		the ability of non-host apps to remove
810 //		icons without a little bit of work
811 
812 /**	for a specific id
813  *	return the name of the replicant (name of view)
814  */
815 
816 status_t
817 TReplicantTray::ItemInfo(int32 id, const char** name)
818 {
819 	if (id < 0)
820 		return B_ERROR;
821 
822 	int32 index, temp;
823 	BView* view = ViewAt(&index, &temp, id, false);
824 	if (view) {
825 		*name = view->Name();
826 		return B_OK;
827 	}
828 
829 	return B_ERROR;
830 }
831 
832 
833 /**	for a specific name
834  *	return the id (internal to Deskbar)
835  */
836 
837 status_t
838 TReplicantTray::ItemInfo(const char* name, int32* id)
839 {
840 	if (!name || strlen(name) <= 0)
841 		return B_ERROR;
842 
843 	int32 index;
844 	BView* view = ViewAt(&index, id, name);
845 	if (view)
846 		return B_OK;
847 
848 	return B_ERROR;
849 }
850 
851 
852 /**	at a specific index
853  *	return both the name and the id of the replicant
854  */
855 
856 status_t
857 TReplicantTray::ItemInfo(int32 index, const char** name, int32* id)
858 {
859 	if (index < 0)
860 		return B_ERROR;
861 
862 	BView* view;
863 	fShelf->ReplicantAt(index, &view, (uint32*)id, NULL);
864 	if (view) {
865 		*name = view->Name();
866 		return B_OK;
867 	}
868 
869 	return B_ERROR;
870 }
871 
872 
873 /**	replicant exists, by id/index */
874 
875 bool
876 TReplicantTray::IconExists(int32 target, bool byIndex)
877 {
878 	int32 index, id;
879 	BView* view = ViewAt(&index, &id, target, byIndex);
880 
881 	return view && index >= 0;
882 }
883 
884 
885 /**	replicant exists, by name */
886 
887 bool
888 TReplicantTray::IconExists(const char* name)
889 {
890 	if (!name || strlen(name) == 0)
891 		return false;
892 
893 	int32 index, id;
894 	BView* view = ViewAt(&index, &id, name);
895 
896 	return view && index >= 0;
897 }
898 
899 
900 int32
901 TReplicantTray::IconCount() const
902 {
903 	return fShelf->CountReplicants();
904 }
905 
906 
907 /*! Message must contain an archivable view for later rehydration.
908 	This function takes over ownership of the provided message on success
909 	only.
910 	Returns the current replicant ID.
911 */
912 status_t
913 TReplicantTray::AddIcon(BMessage* archive, int32* id, const entry_ref* addOn)
914 {
915 	if (archive == NULL || id == NULL)
916 		return B_ERROR;
917 
918 	// find entry_ref
919 
920 	entry_ref ref;
921 	if (addOn) {
922 		// Use it if we got it
923 		ref = *addOn;
924 	} else {
925 		const char* signature;
926 
927 		status_t status = archive->FindString("add_on", &signature);
928 		if (status == B_OK) {
929 			BRoster roster;
930 			status = roster.FindApp(signature, &ref);
931 		}
932 		if (status < B_OK)
933 			return status;
934 	}
935 
936 	BFile file;
937 	status_t status = file.SetTo(&ref, B_READ_ONLY);
938 	if (status < B_OK)
939 		return status;
940 
941 	node_ref nodeRef;
942 	status = file.GetNodeRef(&nodeRef);
943 	if (status < B_OK)
944 		return status;
945 
946 	BEntry entry(&ref, true);
947 		// TODO: this resolves an eventual link for the item being added - this
948 		// is okay for now, but in multi-user environments, one might want to
949 		// have links that carry the be:deskbar_item_status attribute
950 	status = entry.InitCheck();
951 	if (status != B_OK)
952 		return status;
953 
954 	*id = 999;
955 	if (archive->what == B_ARCHIVED_OBJECT)
956 		archive->what = 0;
957 
958 	BRect originalBounds = archive->FindRect("_frame");
959 		// this is a work-around for buggy replicants that change their size in
960 		// AttachedToWindow() (such as "SVM")
961 
962 	// TODO: check for name collisions?
963 	status = fShelf->AddReplicant(archive, BPoint(1, 1));
964 	if (status != B_OK)
965 		return status;
966 
967 	int32 count = fShelf->CountReplicants();
968 	BView* view;
969 	fShelf->ReplicantAt(count - 1, &view, (uint32*)id, NULL);
970 
971 	if (originalBounds != view->Bounds()) {
972 		// The replicant changed its size when added to the window, so we need
973 		// to recompute all over again (it's already done once via
974 		// BShelf::AddReplicant() and TReplicantShelf::CanAcceptReplicantView())
975 		RealignReplicants();
976 	}
977 
978 	float oldWidth = Bounds().Width();
979 	float oldHeight = Bounds().Height();
980 	float width, height;
981 	GetPreferredSize(&width, &height);
982 	if (oldWidth != width || oldHeight != height)
983 		AdjustPlacement();
984 
985 	// add the item to the add-on list
986 
987 	AddItem(*id, nodeRef, entry, addOn != NULL);
988 	return B_OK;
989 }
990 
991 
992 void
993 TReplicantTray::RemoveIcon(int32 target, bool byIndex)
994 {
995 	if (target < 0)
996 		return;
997 
998 	int32 index, id;
999 	BView* view = ViewAt(&index, &id, target, byIndex);
1000 	if (view && index >= 0) {
1001 		// remove the reference from the item list & the shelf
1002 		RemoveItem(id);
1003 		fShelf->DeleteReplicant(index);
1004 
1005 		// force a placement update,  !! need to fix BShelf
1006 		RealReplicantAdjustment(index);
1007 	}
1008 }
1009 
1010 
1011 void
1012 TReplicantTray::RemoveIcon(const char* name)
1013 {
1014 	if (!name || strlen(name) <= 0)
1015 		return;
1016 
1017 	int32 id, index;
1018 	BView* view = ViewAt(&index, &id, name);
1019 	if (view && index >= 0) {
1020 		// remove the reference from the item list & shelf
1021 		RemoveItem(id);
1022 		fShelf->DeleteReplicant(index);
1023 
1024 		// force a placement update,  !! need to fix BShelf
1025 		RealReplicantAdjustment(index);
1026 	}
1027 }
1028 
1029 
1030 void
1031 TReplicantTray::RealReplicantAdjustment(int32 startIndex)
1032 {
1033 	if (startIndex < 0)
1034 		return;
1035 
1036 	if (startIndex == fLastReplicant)
1037 		startIndex = 0;
1038 
1039 	// reset the locations of all replicants after the one deleted
1040 	RealignReplicants(startIndex);
1041 
1042 	float oldWidth = Bounds().Width();
1043 	float oldHeight = Bounds().Height();
1044 	float width, height;
1045 	GetPreferredSize(&width, &height);
1046 	if (oldWidth != width || oldHeight != height) {
1047 		// resize view to accomodate the replicants, redraw as necessary
1048 		AdjustPlacement();
1049 	}
1050 }
1051 
1052 
1053 /**	looking for a replicant by id/index
1054  *	return the view and index
1055  */
1056 
1057 BView*
1058 TReplicantTray::ViewAt(int32* index, int32* id, int32 target, bool byIndex)
1059 {
1060 	*index = -1;
1061 
1062 	BView* view;
1063 	if (byIndex) {
1064 		if (fShelf->ReplicantAt(target, &view, (uint32*)id)) {
1065 			if (view) {
1066 				*index = target;
1067 				return view;
1068 			}
1069 		}
1070 	} else {
1071 		int32 count = fShelf->CountReplicants() - 1;
1072 		int32 localid;
1073 		for (int32 repIndex = count ; repIndex >= 0 ; repIndex--) {
1074 			fShelf->ReplicantAt(repIndex, &view, (uint32*)&localid);
1075 			if (localid == target && view) {
1076 				*index = repIndex;
1077 				*id = localid;
1078 				return view;
1079 			}
1080 		}
1081 	}
1082 	return NULL;
1083 }
1084 
1085 
1086 /**	looking for a replicant with a view by name
1087  *	return the view, index and the id of the replicant
1088  */
1089 
1090 BView*
1091 TReplicantTray::ViewAt(int32* index, int32* id, const char* name)
1092 {
1093 	*index = -1;
1094 	*id = -1;
1095 
1096 	BView* view;
1097 	int32 count = fShelf->CountReplicants()-1;
1098 	for (int32 repIndex = count ; repIndex >= 0 ; repIndex--) {
1099 		fShelf->ReplicantAt(repIndex, &view, (uint32*)id);
1100 		if (view && view->Name() && strcmp(name, view->Name()) == 0) {
1101 			*index = repIndex;
1102 			return view;
1103 		}
1104 	}
1105 	return NULL;
1106 }
1107 
1108 
1109 /**	Shelf will call to determine where and if
1110  *	the replicant is to be added
1111  */
1112 
1113 bool
1114 TReplicantTray::AcceptAddon(BRect replicantFrame, BMessage* message)
1115 {
1116 	if (!message)
1117 		return false;
1118 
1119 	if (replicantFrame.Height() > kMaxReplicantHeight)
1120 		return false;
1121 
1122 	alignment align = B_ALIGN_LEFT;
1123 	if (fAlignmentSupport && message->HasBool("deskbar:dynamic_align")) {
1124 		if (!fBarView->Vertical())
1125 			align = B_ALIGN_RIGHT;
1126 		else
1127 			align = fBarView->Left() ? B_ALIGN_LEFT : B_ALIGN_RIGHT;
1128 	} else if (message->HasInt32("deskbar:align"))
1129 		message->FindInt32("deskbar:align", (int32*)&align);
1130 
1131 	if (message->HasInt32("deskbar:private_align"))
1132 		message->FindInt32("deskbar:private_align", (int32*)&align);
1133 	else
1134 		align = B_ALIGN_LEFT;
1135 
1136 	BPoint loc = LocationForReplicant(fShelf->CountReplicants(),
1137 		replicantFrame.Width());
1138 
1139 	message->AddPoint("_pjp_loc", loc);
1140 	return true;
1141 }
1142 
1143 
1144 /**	based on the previous (index - 1) replicant in the list
1145  *	calculate where the left point should be for this
1146  *	replicant.  replicant will flow to the right on its own
1147  */
1148 
1149 BPoint
1150 TReplicantTray::LocationForReplicant(int32 index, float width)
1151 {
1152 	BPoint loc(kIconGap + 1, kGutter + 1);
1153 
1154 	if (fMultiRowMode) {
1155 		// try to find free space in every row
1156 		for (int32 row = 0; ; loc.y += kMaxReplicantHeight + kIconGap, row++) {
1157 			// determine free space in this row
1158 			BRect rect(loc.x, loc.y, loc.x + fMinimumTrayWidth - kIconGap
1159 				- 2.0, loc.y + kMaxReplicantHeight);
1160 			if (row == 0 && !fTime->IsHidden())
1161 				rect.right -= fTime->Frame().Width() + kIconGap;
1162 
1163 			for (int32 i = 0; i < index; i++) {
1164 				BView* view = NULL;
1165 				fShelf->ReplicantAt(i, &view);
1166 				if (view == NULL || view->Frame().top != rect.top)
1167 					continue;
1168 
1169 				rect.left = view->Frame().right + kIconGap + 1;
1170 			}
1171 
1172 			if (rect.Width() >= width) {
1173 				// the icon fits in this row
1174 				loc = rect.LeftTop();
1175 				break;
1176 			}
1177 		}
1178 	} else {
1179 		if (index > 0) {
1180 			// get the last replicant added for placement reference
1181 			BView* view = NULL;
1182 			fShelf->ReplicantAt(index - 1, &view);
1183 			if (view) {
1184 				// push this rep placement past the last one
1185 				loc.x = view->Frame().right + kIconGap + 1;
1186 				loc.y = view->Frame().top;
1187 			}
1188 		}
1189 	}
1190 
1191 	if ((loc.y == fRightBottomReplicant.top && loc.x
1192 		> fRightBottomReplicant.left) || loc.y > fRightBottomReplicant.top) {
1193 		fRightBottomReplicant.Set(loc.x, loc.y, loc.x + width, loc.y
1194 		+ kMaxReplicantHeight);
1195 		fLastReplicant = index;
1196 	}
1197 
1198 	return loc;
1199 }
1200 
1201 
1202 BRect
1203 TReplicantTray::IconFrame(int32 target, bool byIndex)
1204 {
1205 	int32 index, id;
1206 	BView* view = ViewAt(&index, &id, target, byIndex);
1207 	if (view)
1208 		return view->Frame();
1209 
1210 	return BRect(0, 0, 0, 0);
1211 }
1212 
1213 
1214 BRect
1215 TReplicantTray::IconFrame(const char* name)
1216 {
1217 	if (!name)
1218 		return BRect(0, 0, 0, 0);
1219 
1220 	int32 id, index;
1221 	BView* view = ViewAt(&index, &id, name);
1222 	if (view)
1223 		return view->Frame();
1224 
1225 	return BRect(0, 0, 0, 0);
1226 }
1227 
1228 
1229 /**	Scan from the startIndex and reset the location
1230  *	as defined in LocationForReplicant()
1231  */
1232 
1233 void
1234 TReplicantTray::RealignReplicants(int32 startIndex)
1235 {
1236 	if (startIndex < 0)
1237 		startIndex = 0;
1238 
1239 	int32 count = fShelf->CountReplicants();
1240 	if (count <= 0)
1241 		return;
1242 
1243 	if (startIndex == 0)
1244 		fRightBottomReplicant.Set(0, 0, 0, 0);
1245 
1246 	BView* view = NULL;
1247 	for (int32 i = startIndex ; i < count ; i++) {
1248 		fShelf->ReplicantAt(i, &view);
1249 		if (view != NULL) {
1250 			BPoint loc = LocationForReplicant(i, view->Frame().Width());
1251 			if (view->Frame().LeftTop() != loc)
1252 				view->MoveTo(loc);
1253 		}
1254 	}
1255 }
1256 
1257 
1258 status_t
1259 TReplicantTray::_SaveSettings()
1260 {
1261 	status_t result;
1262 	BPath path;
1263 	if ((result = GetDeskbarSettingsDirectory(path, true)) == B_OK) {
1264 		path.Append(kReplicantSettingsFile);
1265 
1266 		BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
1267 		if ((result = file.InitCheck()) == B_OK)
1268 			result = fAddOnSettings.Flatten(&file);
1269 	}
1270 
1271 	return result;
1272 }
1273 
1274 
1275 void
1276 TReplicantTray::SaveTimeSettings()
1277 {
1278 	if (fTime == NULL)
1279 		return;
1280 
1281 	clock_settings* settings = ((TBarApp*)be_app)->ClockSettings();
1282 	settings->showSeconds = fTime->ShowSeconds();
1283 	settings->showDayOfWeek = fTime->ShowDayOfWeek();
1284 	settings->showTimeZone = fTime->ShowTimeZone();
1285 }
1286 
1287 
1288 //	#pragma mark -
1289 
1290 
1291 /*! Draggable region that is asynchronous so that dragging does not block
1292 	other activities.
1293 */
1294 TDragRegion::TDragRegion(TBarView* parent, BView* child)
1295 	:
1296 	BControl(BRect(0, 0, 0, 0), "", "", NULL, B_FOLLOW_NONE,
1297 		B_WILL_DRAW | B_FRAME_EVENTS),
1298 	fBarView(parent),
1299 	fChild(child),
1300 	fDragLocation(kAutoPlaceDragRegion)
1301 {
1302 }
1303 
1304 
1305 void
1306 TDragRegion::AttachedToWindow()
1307 {
1308 	BView::AttachedToWindow();
1309 	if (be_control_look != NULL)
1310 		SetViewColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 1.1));
1311 	else
1312 		SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR));
1313 	ResizeToPreferred();
1314 }
1315 
1316 
1317 void
1318 TDragRegion::GetPreferredSize(float* width, float* height)
1319 {
1320 	fChild->ResizeToPreferred();
1321 	*width = fChild->Bounds().Width();
1322 	*height = fChild->Bounds().Height();
1323 
1324 	if (fDragLocation != kNoDragRegion)
1325 		*width += 7;
1326 	else
1327 		*width += 6;
1328 
1329 	*height += 3;
1330 }
1331 
1332 
1333 void
1334 TDragRegion::FrameMoved(BPoint)
1335 {
1336 	if (fBarView->Left() && fBarView->Vertical()
1337 		&& fDragLocation != kNoDragRegion)
1338 		fChild->MoveTo(5, 2);
1339 	else
1340 		fChild->MoveTo(2, 2);
1341 }
1342 
1343 
1344 void
1345 TDragRegion::Draw(BRect)
1346 {
1347 	rgb_color menuColor = ViewColor();
1348 	rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT);
1349 	rgb_color ldark = tint_color(menuColor, 1.02);
1350 	rgb_color dark = tint_color(menuColor, B_DARKEN_2_TINT);
1351 	rgb_color vvdark = tint_color(menuColor, B_DARKEN_4_TINT);
1352 	rgb_color light = tint_color(menuColor, B_LIGHTEN_2_TINT);
1353 
1354 	BRect frame(Bounds());
1355 	BeginLineArray(4);
1356 
1357 	if (be_control_look != NULL) {
1358 		if (fBarView->Vertical()) {
1359 			AddLine(frame.LeftTop(), frame.RightTop(), dark);
1360 			AddLine(BPoint(frame.left, frame.top + 1),
1361 				BPoint(frame.right, frame.top + 1), ldark);
1362 			AddLine(frame.LeftBottom(), frame.RightBottom(), hilite);
1363 		} else if (fBarView->AcrossTop() || fBarView->AcrossBottom()) {
1364 			AddLine(frame.LeftTop(),
1365 				BPoint(frame.left, frame.bottom), dark);
1366 			AddLine(BPoint(frame.left + 1, frame.top + 1),
1367 				BPoint(frame.right - 1, frame.top + 1), light);
1368 			AddLine(BPoint(frame.right, frame.top + 2),
1369 				BPoint(frame.right, frame.bottom), hilite);
1370 			AddLine(BPoint(frame.left + 1, frame.bottom),
1371 				BPoint(frame.right - 1, frame.bottom), hilite);
1372 		}
1373 	} else {
1374 		if (fBarView->Vertical()) {
1375 			AddLine(frame.LeftTop(), frame.RightTop(), light);
1376 			AddLine(frame.LeftTop(), frame.LeftBottom(), light);
1377 			AddLine(frame.RightBottom(), frame.RightTop(), hilite);
1378 		} else if (fBarView->AcrossTop()) {
1379 			AddLine(BPoint(frame.left, frame.top + 1),
1380 				BPoint(frame.right - 1, frame.top + 1), light);
1381 			AddLine(frame.RightTop(), frame.RightBottom(), vvdark);
1382 			AddLine(BPoint(frame.right - 1, frame.top + 2),
1383 				BPoint(frame.right - 1, frame.bottom - 1), hilite);
1384 			AddLine(frame.LeftBottom(),
1385 				BPoint(frame.right - 1, frame.bottom), hilite);
1386 		} else if (fBarView->AcrossBottom()) {
1387 			AddLine(BPoint(frame.left, frame.top + 1),
1388 				BPoint(frame.right - 1, frame.top + 1), light);
1389 			AddLine(frame.LeftBottom(), frame.RightBottom(), hilite);
1390 			AddLine(frame.RightTop(), frame.RightBottom(), vvdark);
1391 			AddLine(BPoint(frame.right - 1, frame.top + 1),
1392 				BPoint(frame.right - 1, frame.bottom - 1), hilite);
1393 		}
1394 	}
1395 
1396 	EndLineArray();
1397 
1398 	if (fDragLocation != kDontDrawDragRegion || fDragLocation != kNoDragRegion)
1399 		DrawDragRegion();
1400 }
1401 
1402 
1403 void
1404 TDragRegion::DrawDragRegion()
1405 {
1406 	BRect dragRegion(DragRegion());
1407 
1408 	rgb_color menuColor = ViewColor();
1409 	rgb_color menuHilite = menuColor;
1410 	if (IsTracking()) {
1411 		// Draw drag region highlighted if tracking mouse
1412 		menuHilite = tint_color(menuColor, B_HIGHLIGHT_BACKGROUND_TINT);
1413 		SetHighColor(menuHilite);
1414 		FillRect(dragRegion);
1415 	}
1416 	rgb_color vdark = tint_color(menuHilite, B_DARKEN_3_TINT);
1417 	rgb_color light = tint_color(menuHilite, B_LIGHTEN_2_TINT);
1418 
1419 	BeginLineArray(dragRegion.IntegerHeight());
1420 	BPoint pt;
1421 	pt.x = floorf((dragRegion.left + dragRegion.right) / 2 + 0.5) - 1;
1422 	pt.y = dragRegion.top + 2;
1423 
1424 	while (pt.y + 1 <= dragRegion.bottom) {
1425 		AddLine(pt, pt, vdark);
1426 		AddLine(pt + BPoint(1, 1), pt + BPoint(1, 1), light);
1427 
1428 		pt.y += 3;
1429 	}
1430 	EndLineArray();
1431 }
1432 
1433 
1434 BRect
1435 TDragRegion::DragRegion() const
1436 {
1437 	float kTopBottomInset = 2;
1438 	float kLeftRightInset = 1;
1439 	float kDragWidth = 3;
1440 	if (be_control_look != NULL) {
1441 		kTopBottomInset = 1;
1442 		kLeftRightInset = 0;
1443 		kDragWidth = 4;
1444 	}
1445 
1446 	BRect dragRegion(Bounds());
1447 	dragRegion.top += kTopBottomInset;
1448 	dragRegion.bottom -= kTopBottomInset;
1449 
1450 	bool placeOnLeft = false;
1451 	if (fDragLocation == kAutoPlaceDragRegion) {
1452 		if (fBarView->Vertical() && fBarView->Left())
1453 			placeOnLeft = true;
1454 		else
1455 			placeOnLeft = false;
1456 	} else if (fDragLocation == kDragRegionLeft)
1457 		placeOnLeft = true;
1458 	else if (fDragLocation == kDragRegionRight)
1459 		placeOnLeft = false;
1460 
1461 	if (placeOnLeft) {
1462 		dragRegion.left += kLeftRightInset;
1463 		dragRegion.right = dragRegion.left + kDragWidth;
1464 	} else {
1465 		dragRegion.right -= kLeftRightInset;
1466 		dragRegion.left = dragRegion.right - kDragWidth;
1467 	}
1468 
1469 	return dragRegion;
1470 }
1471 
1472 
1473 void
1474 TDragRegion::MouseDown(BPoint thePoint)
1475 {
1476 	uint32 buttons;
1477 	BPoint where;
1478 	BRect dragRegion(DragRegion());
1479 
1480 	dragRegion.InsetBy(-2.0f, -2.0f);
1481 		// DragRegion() is designed for drawing, not clicking
1482 
1483 	if (!dragRegion.Contains(thePoint))
1484 		return;
1485 
1486 	while (true) {
1487 		GetMouse(&where, &buttons);
1488 		if (!buttons)
1489 			break;
1490 
1491 		if ((Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) != 0) {
1492 			fPreviousPosition = thePoint;
1493 			SetTracking(true);
1494 			SetMouseEventMask(B_POINTER_EVENTS,
1495 				B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS);
1496 			Invalidate(DragRegion());
1497 			break;
1498 		}
1499 
1500 		snooze(25000);
1501 	}
1502 }
1503 
1504 
1505 void
1506 TDragRegion::MouseUp(BPoint pt)
1507 {
1508 	if (IsTracking()) {
1509 		SetTracking(false);
1510 		Invalidate(DragRegion());
1511 	} else
1512 		BControl::MouseUp(pt);
1513 }
1514 
1515 
1516 bool
1517 TDragRegion::SwitchModeForRect(BPoint mouse, BRect rect,
1518 	bool newVertical, bool newLeft, bool newTop, int32 newState)
1519 {
1520 	if (!rect.Contains(mouse)) {
1521 		// not our rect
1522 		return false;
1523 	}
1524 
1525 	if (newVertical == fBarView->Vertical() && newLeft == fBarView->Left()
1526 		&& newTop == fBarView->Top() && newState == fBarView->State()) {
1527 		// already in the correct mode
1528 		return true;
1529 	}
1530 
1531 	fBarView->ChangeState(newState, newVertical, newLeft, newTop, true);
1532 	return true;
1533 }
1534 
1535 
1536 void
1537 TDragRegion::MouseMoved(BPoint where, uint32 code, const BMessage* message)
1538 {
1539 	if (IsTracking()) {
1540 		BScreen screen;
1541 		BRect frame = screen.Frame();
1542 
1543 		float hDivider = frame.Width() / 6;
1544 		hDivider = (hDivider < gMinimumWindowWidth + 10.0f)
1545 			? gMinimumWindowWidth + 10.0f : hDivider;
1546 		float miniDivider = frame.top + kMiniHeight + 10.0f;
1547 		float vDivider = frame.Height() / 2;
1548 #ifdef FULL_MODE
1549 		float thirdScreen = frame.Height() / 3;
1550 #endif
1551 		BRect topLeft(frame.left, frame.top, frame.left + hDivider,
1552 			miniDivider);
1553 		BRect topMiddle(frame.left + hDivider, frame.top, frame.right
1554 			- hDivider, vDivider);
1555 		BRect topRight(frame.right - hDivider, frame.top, frame.right,
1556 			miniDivider);
1557 
1558 #ifdef FULL_MODE
1559 		vDivider = miniDivider + thirdScreen;
1560 #endif
1561 		BRect middleLeft(frame.left, miniDivider, frame.left + hDivider,
1562 			vDivider);
1563 		BRect middleRight(frame.right - hDivider, miniDivider, frame.right,
1564 			vDivider);
1565 
1566 #ifdef FULL_MODE
1567 		BRect leftSide(frame.left, vDivider, frame.left + hDivider,
1568 			frame.bottom - thirdScreen);
1569 		BRect rightSide(frame.right - hDivider, vDivider, frame.right,
1570 			frame.bottom - thirdScreen);
1571 
1572 		vDivider = frame.bottom - thirdScreen;
1573 #endif
1574 		BRect bottomLeft(frame.left, vDivider, frame.left + hDivider,
1575 			frame.bottom);
1576 		BRect bottomMiddle(frame.left + hDivider, vDivider, frame.right
1577 			- hDivider, frame.bottom);
1578 		BRect bottomRight(frame.right - hDivider, vDivider, frame.right,
1579 			frame.bottom);
1580 
1581 		if (where != fPreviousPosition) {
1582 			fPreviousPosition = where;
1583 			ConvertToScreen(&where);
1584 
1585 			// use short circuit evaluation for convenience
1586 			if (SwitchModeForRect(where, topLeft, true, true, true, kMiniState)
1587 				|| SwitchModeForRect(where, topMiddle, false, true, true,
1588 					kExpandoState)
1589 				|| SwitchModeForRect(where, topRight, true, false, true,
1590 					kMiniState)
1591 				|| SwitchModeForRect(where, middleLeft, true, true, true,
1592 					kExpandoState)
1593 				|| SwitchModeForRect(where, middleRight, true, false, true,
1594 					kExpandoState)
1595 
1596 #ifdef FULL_MODE
1597 				|| SwitchModeForRect(where, leftSide, true, true, true,
1598 					kFullState)
1599 				|| SwitchModeForRect(where, rightSide, true, false, true,
1600 					kFullState)
1601 #endif
1602 				|| SwitchModeForRect(where, bottomLeft, true, true, false,
1603 					kMiniState)
1604 				|| SwitchModeForRect(where, bottomMiddle, false, true, false,
1605 					kExpandoState)
1606 				|| SwitchModeForRect(where, bottomRight, true, false, false,
1607 					kMiniState))
1608 				;
1609 		}
1610 	} else
1611 		BControl::MouseMoved(where, code, message);
1612 }
1613 
1614 
1615 int32
1616 TDragRegion::DragRegionLocation() const
1617 {
1618 	return fDragLocation;
1619 }
1620 
1621 
1622 void
1623 TDragRegion::SetDragRegionLocation(int32 location)
1624 {
1625 	if (location == fDragLocation)
1626 		return;
1627 
1628 	fDragLocation = location;
1629 	Invalidate();
1630 }
1631