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