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