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