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