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