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