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