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