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