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