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