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