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