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