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