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