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