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