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