xref: /haiku/src/kits/tracker/ContainerWindow.cpp (revision 344ded80d400028c8f561b4b876257b94c12db4a)
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 trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34 
35 
36 #include "ContainerWindow.h"
37 
38 #include <Alert.h>
39 #include <AppFileInfo.h>
40 #include <Application.h>
41 #include <Catalog.h>
42 #include <ControlLook.h>
43 #include <Debug.h>
44 #include <Directory.h>
45 #include <Entry.h>
46 #include <FindDirectory.h>
47 #include <GridView.h>
48 #include <GroupLayout.h>
49 #include <Keymap.h>
50 #include <Locale.h>
51 #include <MenuBar.h>
52 #include <MenuItem.h>
53 #include <NodeMonitor.h>
54 #include <Path.h>
55 #include <PopUpMenu.h>
56 #include <Roster.h>
57 #include <Screen.h>
58 #include <UnicodeChar.h>
59 #include <Volume.h>
60 #include <VolumeRoster.h>
61 #include <WindowPrivate.h>
62 
63 #include <fs_attr.h>
64 #include <image.h>
65 #include <strings.h>
66 #include <stdlib.h>
67 
68 #include "Attributes.h"
69 #include "AutoDeleter.h"
70 #include "AutoLock.h"
71 #include "BackgroundImage.h"
72 #include "Commands.h"
73 #include "CountView.h"
74 #include "DeskWindow.h"
75 #include "DraggableContainerIcon.h"
76 #include "FSClipboard.h"
77 #include "FSUndoRedo.h"
78 #include "FSUtils.h"
79 #include "FavoritesMenu.h"
80 #include "FindPanel.h"
81 #include "IconMenuItem.h"
82 #include "MimeTypes.h"
83 #include "Model.h"
84 #include "MountMenu.h"
85 #include "NavMenu.h"
86 #include "Navigator.h"
87 #include "OpenWithWindow.h"
88 #include "PoseView.h"
89 #include "QueryContainerWindow.h"
90 #include "SelectionWindow.h"
91 #include "TemplatesMenu.h"
92 #include "Thread.h"
93 #include "TitleView.h"
94 #include "Tracker.h"
95 #include "TrackerSettings.h"
96 
97 
98 #undef B_TRANSLATION_CONTEXT
99 #define B_TRANSLATION_CONTEXT "ContainerWindow"
100 
101 
102 #ifdef _IMPEXP_BE
103 _IMPEXP_BE
104 #endif
105 void do_minimize_team(BRect zoomRect, team_id team, bool zoom);
106 
107 
108 struct AddOneAddOnParams {
109 	BObjectList<BMenuItem>* primaryList;
110 	BObjectList<BMenuItem>* secondaryList;
111 };
112 
113 struct StaggerOneParams {
114 	bool rectFromParent;
115 };
116 
117 
118 BRect BContainerWindow::sNewWindRect;
119 static int32 sWindowStaggerBy;
120 
121 LockingList<AddOnShortcut>* BContainerWindow::fAddOnsList
122 	= new LockingList<struct AddOnShortcut>(10, true);
123 
124 
125 namespace BPrivate {
126 
127 
128 int
129 CompareContainerWindowNodeRef(const BContainerWindow* item1, const BContainerWindow* item2)
130 {
131 	const node_ref* ref1 = item1->TargetModel()->NodeRef();
132 	const node_ref* ref2 = item2->TargetModel()->NodeRef();
133 	if (*ref1 < *ref2)
134 		return -1;
135 	else if (*ref1 == *ref2)
136 		return 0;
137 	else
138 		return 1;
139 }
140 
141 
142 filter_result
143 ActivateWindowFilter(BMessage*, BHandler** target, BMessageFilter*)
144 {
145 	BView* view = dynamic_cast<BView*>(*target);
146 
147 	// activate the window if no PoseView or DraggableContainerIcon had been
148 	// pressed (those will activate the window themselves, if necessary)
149 	if (view != NULL
150 		&& dynamic_cast<BPoseView*>(view) == NULL
151 		&& dynamic_cast<DraggableContainerIcon*>(view) == NULL
152 		&& view->Window() != NULL) {
153 		view->Window()->Activate(true);
154 	}
155 
156 	return B_DISPATCH_MESSAGE;
157 }
158 
159 
160 }	// namespace BPrivate
161 
162 
163 static int32
164 AddOnMenuGenerate(const entry_ref* addOnRef, BMenu* menu, BContainerWindow* window)
165 {
166 	BEntry entry(addOnRef);
167 	BPath path;
168 	status_t result = entry.InitCheck();
169 	if (result != B_OK)
170 		return result;
171 
172 	result = entry.GetPath(&path);
173 	if (result != B_OK)
174 		return result;
175 
176 	image_id addOnImage = load_add_on(path.Path());
177 	if (addOnImage < 0)
178 		return addOnImage;
179 
180 	void (*populateMenu)(BMessage*, BMenu*, BHandler*);
181 	result = get_image_symbol(addOnImage, "populate_menu", 2,
182 		(void**)&populateMenu);
183 	if (result < 0) {
184 		PRINT(("Couldn't find populate_menu\n"));
185 		unload_add_on(addOnImage);
186 		return result;
187 	}
188 
189 	BMessage* message = window->AddOnMessage(B_TRACKER_ADDON_MESSAGE);
190 	message->AddRef("addon_ref", addOnRef);
191 
192 	// call add-on code
193 	(*populateMenu)(message, menu, window->PoseView());
194 
195 	unload_add_on(addOnImage);
196 	return B_OK;
197 }
198 
199 
200 static status_t
201 RunAddOnMessageThread(BMessage *message, void *)
202 {
203 	entry_ref addOnRef;
204 	BEntry entry;
205 	BPath path;
206 	status_t result = message->FindRef("addon_ref", &addOnRef);
207 	image_id addOnImage;
208 
209 	if (result != B_OK)
210 		goto end;
211 
212 	entry = BEntry(&addOnRef);
213 	result = entry.InitCheck();
214 	if (result != B_OK)
215 		goto end;
216 
217 	result = entry.GetPath(&path);
218 	if (result != B_OK)
219 		goto end;
220 
221 	addOnImage = load_add_on(path.Path());
222 	if (addOnImage < 0) {
223 		result = addOnImage;
224 		goto end;
225 	}
226 	void (*messageReceived)(BMessage*);
227 	result = get_image_symbol(addOnImage, "message_received", 2,
228 		(void**)&messageReceived);
229 
230 	if (result < 0) {
231 		PRINT(("Couldn't find message_received\n"));
232 		unload_add_on(addOnImage);
233 		goto end;
234 	}
235 	// call add-on code
236 	(*messageReceived)(message);
237 	unload_add_on(addOnImage);
238 	return B_OK;
239 
240 end:
241 	BString buffer(B_TRANSLATE("Error %error loading add-On %name."));
242 	buffer.ReplaceFirst("%error", strerror(result));
243 	buffer.ReplaceFirst("%name", addOnRef.name);
244 
245 	BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"),
246 		0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
247 	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
248 	alert->Go();
249 
250 	return result;
251 }
252 
253 
254 static void
255 AddOneAddOn(const Model* model, const char* name, uint32 shortcut,
256 	uint32 modifiers, bool primary, void* context,
257 	BContainerWindow* window, BMenu* menu)
258 {
259 	AddOneAddOnParams* params = (AddOneAddOnParams*)context;
260 
261 	BMessage* message = new BMessage(kLoadAddOn);
262 	message->AddRef("refs", model->EntryRef());
263 
264 	ModelMenuItem* item;
265 	try {
266 		item = new ModelMenuItem(model, name, message,
267 			(char)shortcut, modifiers);
268 	} catch (...) {
269 		delete message;
270 		return;
271 	}
272 
273 	const entry_ref* addOnRef = model->EntryRef();
274 	AddOnMenuGenerate(addOnRef, menu, window);
275 
276 	if (primary)
277 		params->primaryList->AddItem(item);
278 	else
279 		params->secondaryList->AddItem(item);
280 }
281 
282 
283 static int32
284 AddOnThread(BMessage* refsMessage, entry_ref addOnRef, entry_ref directoryRef)
285 {
286 	ObjectDeleter<BMessage> _(refsMessage);
287 
288 	BEntry entry(&addOnRef);
289 	BPath path;
290 	status_t result = entry.InitCheck();
291 	if (result == B_OK)
292 		result = entry.GetPath(&path);
293 
294 	if (result == B_OK) {
295 		image_id addOnImage = load_add_on(path.Path());
296 		if (addOnImage >= 0) {
297 			void (*processRefs)(entry_ref, BMessage*, void*);
298 			result = get_image_symbol(addOnImage, "process_refs", 2,
299 				(void**)&processRefs);
300 
301 			if (result >= 0) {
302 				// call add-on code
303 				(*processRefs)(directoryRef, refsMessage, NULL);
304 
305 				unload_add_on(addOnImage);
306 				return B_OK;
307 			} else
308 				PRINT(("couldn't find process_refs\n"));
309 
310 			unload_add_on(addOnImage);
311 		} else
312 			result = addOnImage;
313 	}
314 
315 	BString buffer(B_TRANSLATE("Error %error loading Add-On %name."));
316 	buffer.ReplaceFirst("%error", strerror(result));
317 	buffer.ReplaceFirst("%name", addOnRef.name);
318 
319 	BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"),
320 		0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
321 	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
322 	alert->Go();
323 
324 	return result;
325 }
326 
327 
328 static bool
329 NodeHasSavedState(const BNode* node)
330 {
331 	attr_info info;
332 	return node->GetAttrInfo(kAttrWindowFrame, &info) == B_OK;
333 }
334 
335 
336 static bool
337 OffsetFrameOne(const char* DEBUG_ONLY(name), uint32, off_t, void* castToRect,
338 	void* castToParams)
339 {
340 	ASSERT(strcmp(name, kAttrWindowFrame) == 0);
341 	StaggerOneParams* params = (StaggerOneParams*)castToParams;
342 
343 	if (!params->rectFromParent)
344 		return false;
345 
346 	if (!castToRect)
347 		return false;
348 
349 	((BRect*)castToRect)->OffsetBy(sWindowStaggerBy, sWindowStaggerBy);
350 
351 	return true;
352 }
353 
354 
355 static void
356 AddMimeTypeString(BStringList& list, Model* model)
357 {
358 	if (model == NULL)
359 		return;
360 
361 	const char* modelMimeType = model->MimeType();
362 	if (modelMimeType == NULL || *modelMimeType == '\0')
363 		return;
364 
365 	BString type = BString(modelMimeType);
366 	if (list.HasString(type, true))
367 		return;
368 
369 	list.Add(type);
370 }
371 
372 
373 //	#pragma mark - BContainerWindow
374 
375 
376 BContainerWindow::BContainerWindow(LockingList<BWindow>* list, uint32 openFlags, window_look look,
377 	window_feel feel, uint32 windowFlags, uint32 workspace, bool useLayout)
378 	:
379 	BWindow(InitialWindowRect(feel), "TrackerWindow", look, feel, windowFlags, workspace),
380 	fWindowList(list),
381 	fOpenFlags(openFlags),
382 	fUsesLayout(useLayout),
383 	fMenuContainer(NULL),
384 	fPoseContainer(NULL),
385 	fBorderedView(NULL),
386 	fVScrollBarContainer(NULL),
387 	fCountContainer(NULL),
388 	fContextMenu(NULL),
389 	fFileContextMenu(NULL),
390 	fWindowContextMenu(NULL),
391 	fDropContextMenu(NULL),
392 	fVolumeContextMenu(NULL),
393 	fTrashContextMenu(NULL),
394 	fDragContextMenu(NULL),
395 	fMoveToItem(NULL),
396 	fCopyToItem(NULL),
397 	fCreateLinkItem(NULL),
398 	fOpenWithItem(NULL),
399 	fNavigationItem(NULL),
400 	fMenuBar(NULL),
401 	fDraggableIcon(NULL),
402 	fNavigator(NULL),
403 	fPoseView(NULL),
404 	fAttrMenu(NULL),
405 	fWindowMenu(NULL),
406 	fFileMenu(NULL),
407 	fArrangeByMenu(NULL),
408 	fSelectionWindow(NULL),
409 	fTaskLoop(NULL),
410 	fStateNeedsSaving(false),
411 	fBackgroundImage(NULL),
412 	fSavedZoomRect(0, 0, -1, -1),
413 	fDragMessage(NULL),
414 	fCachedTypesList(NULL),
415 	fSaveStateIsEnabled(true),
416 	fIsWatchingPath(false)
417 {
418 	InitIconPreloader();
419 
420 	if (list != NULL) {
421 		ASSERT(list->IsLocked());
422 		list->AddItem(this);
423 	}
424 
425 	if (fUsesLayout) {
426 		SetFlags(Flags() | B_AUTO_UPDATE_SIZE_LIMITS);
427 
428 		fRootLayout = new BGroupLayout(B_VERTICAL, 0);
429 		fRootLayout->SetInsets(0);
430 		SetLayout(fRootLayout);
431 		fRootLayout->Owner()->AdoptSystemColors();
432 
433 		fMenuContainer = new BGroupView(B_HORIZONTAL, 0);
434 		fRootLayout->AddView(fMenuContainer);
435 
436 		fPoseContainer = new BGridView(0.0, 0.0);
437 		fRootLayout->AddView(fPoseContainer);
438 
439 		fBorderedView = new BorderedView;
440 		fPoseContainer->GridLayout()->AddView(fBorderedView, 0, 1);
441 
442 		fCountContainer = new BGroupView(B_HORIZONTAL, 0);
443 		fPoseContainer->GridLayout()->AddView(fCountContainer, 0, 2);
444 	}
445 
446 	AddCommonFilter(new BMessageFilter(B_MOUSE_DOWN, ActivateWindowFilter));
447 
448 	Run();
449 
450 	// watch out for settings changes
451 	TTracker* tracker = dynamic_cast<TTracker*>(be_app);
452 	if (tracker != NULL && tracker->Lock()) {
453 		tracker->StartWatching(this, kWindowsShowFullPathChanged);
454 		tracker->StartWatching(this, kSingleWindowBrowseChanged);
455 		tracker->StartWatching(this, kShowNavigatorChanged);
456 		tracker->Unlock();
457 	}
458 
459 	// ToDo: remove me once we have undo/redo menu items
460 	// (that is, move them to AddShortcuts())
461 	AddShortcut('Z', B_COMMAND_KEY, new BMessage(B_UNDO), this);
462 	AddShortcut('Z', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(B_REDO), this);
463 }
464 
465 
466 BContainerWindow::~BContainerWindow()
467 {
468 	ASSERT(IsLocked());
469 
470 	// stop the watchers
471 	TTracker* tracker = dynamic_cast<TTracker*>(be_app);
472 	if (tracker != NULL && tracker->Lock()) {
473 		tracker->StopWatching(this, kWindowsShowFullPathChanged);
474 		tracker->StopWatching(this, kSingleWindowBrowseChanged);
475 		tracker->StopWatching(this, kShowNavigatorChanged);
476 		tracker->Unlock();
477 	}
478 
479 	delete fTaskLoop;
480 	delete fBackgroundImage;
481 	delete fDragMessage;
482 	delete fCachedTypesList;
483 
484 	if (fSelectionWindow != NULL && fSelectionWindow->Lock())
485 		fSelectionWindow->Quit();
486 }
487 
488 
489 BRect
490 BContainerWindow::InitialWindowRect(window_feel feel)
491 {
492 	if (!sNewWindRect.IsValid()) {
493 		const float labelSpacing = be_control_look->DefaultLabelSpacing();
494 		// approximately (85, 50, 548, 280) with default spacing
495 		sNewWindRect = BRect(labelSpacing * 14, labelSpacing * 8,
496 			labelSpacing * 91, labelSpacing * 46);
497 		sWindowStaggerBy = (int32)(labelSpacing * 3.0f);
498 	}
499 
500 	if (feel != kDesktopWindowFeel)
501 		return sNewWindRect;
502 
503 	// do not offset desktop window
504 	BRect result = sNewWindRect;
505 	result.OffsetTo(0, 0);
506 	return result;
507 }
508 
509 
510 void
511 BContainerWindow::Minimize(bool minimize)
512 {
513 	if (minimize && (modifiers() & B_OPTION_KEY) != 0)
514 		do_minimize_team(BRect(0, 0, 0, 0), be_app->Team(), true);
515 	else
516 		_inherited::Minimize(minimize);
517 }
518 
519 
520 bool
521 BContainerWindow::QuitRequested()
522 {
523 	// this is a response to the DeskBar sending us a B_QUIT, when it really
524 	// means to say close all your windows. It might be better to have it
525 	// send a kCloseAllWindows message and have windowless apps stay running,
526 	// which is what we will do for the Tracker
527 	if (CurrentMessage() != NULL
528 		&& ((CurrentMessage()->FindInt32("modifiers") & B_CONTROL_KEY)) != 0) {
529 		be_app->PostMessage(kCloseAllWindows);
530 	}
531 
532 	Hide();
533 		// this will close the window instantly, even if
534 		// the file system is very busy right now
535 	return true;
536 }
537 
538 
539 void
540 BContainerWindow::Quit()
541 {
542 	// get rid of context menus
543 	if (fNavigationItem) {
544 		BMenu* menu = fNavigationItem->Menu();
545 		if (menu != NULL)
546 			menu->RemoveItem(fNavigationItem);
547 
548 		delete fNavigationItem;
549 		fNavigationItem = NULL;
550 	}
551 
552 	if (fOpenWithItem != NULL && fOpenWithItem->Menu() == NULL) {
553 		delete fOpenWithItem;
554 		fOpenWithItem = NULL;
555 	}
556 
557 	if (fMoveToItem != NULL && fMoveToItem->Menu() == NULL) {
558 		delete fMoveToItem;
559 		fMoveToItem = NULL;
560 	}
561 
562 	if (fCopyToItem != NULL && fCopyToItem->Menu() == NULL) {
563 		delete fCopyToItem;
564 		fCopyToItem = NULL;
565 	}
566 
567 	if (fCreateLinkItem != NULL && fCreateLinkItem->Menu() == NULL) {
568 		delete fCreateLinkItem;
569 		fCreateLinkItem = NULL;
570 	}
571 
572 	if (fAttrMenu != NULL && fAttrMenu->Supermenu() == NULL) {
573 		delete fAttrMenu;
574 		fAttrMenu = NULL;
575 	}
576 
577 	delete fFileContextMenu;
578 	fFileContextMenu = NULL;
579 
580 	delete fWindowContextMenu;
581 	fWindowContextMenu = NULL;
582 
583 	delete fDropContextMenu;
584 	fDropContextMenu = NULL;
585 
586 	delete fVolumeContextMenu;
587 	fVolumeContextMenu = NULL;
588 
589 	delete fDragContextMenu;
590 	fDragContextMenu = NULL;
591 
592 	delete fTrashContextMenu;
593 	fTrashContextMenu = NULL;
594 
595 	int32 windowCount = 0;
596 
597 	// This is a deadlock code sequence - need to change this
598 	// to acquire the window list while this container window is unlocked
599 	if (fWindowList != NULL) {
600 		AutoLock<LockingList<BWindow> > lock(fWindowList);
601 		if (lock.IsLocked()) {
602 			fWindowList->RemoveItem(this, false);
603 			windowCount = fWindowList->CountItems();
604 		}
605 	}
606 
607 	if (StateNeedsSaving())
608 		SaveState();
609 
610 	if (fWindowList != NULL && windowCount == 0)
611 		be_app->PostMessage(B_QUIT_REQUESTED);
612 
613 	_inherited::Quit();
614 }
615 
616 
617 BPoseView*
618 BContainerWindow::NewPoseView(Model* model, uint32 viewMode)
619 {
620 	return new BPoseView(model, viewMode);
621 }
622 
623 
624 void
625 BContainerWindow::CreatePoseView(Model* model)
626 {
627 	fPoseView = NewPoseView(model, kListMode);
628 	fBorderedView->GroupLayout()->AddView(fPoseView);
629 	fBorderedView->GroupLayout()->SetInsets(1, 0, 1, 1);
630 	fBorderedView->EnableBorderHighlight(false);
631 
632 	TrackerSettings settings;
633 	if (settings.SingleWindowBrowse() && model->IsDirectory() && !PoseView()->IsFilePanel()) {
634 		fNavigator = new BNavigator(model);
635 		fPoseContainer->GridLayout()->AddView(fNavigator, 0, 0, 2);
636 		if (!settings.ShowNavigator())
637 			fNavigator->Hide();
638 	}
639 
640 	SetPathWatchingEnabled(settings.ShowNavigator()
641 		|| settings.ShowFullPathInTitleBar());
642 }
643 
644 
645 void
646 BContainerWindow::AddContextMenus()
647 {
648 	// create context sensitive menus
649 	fFileContextMenu = new BPopUpMenu("FileContext", false, false);
650 	AddFileContextMenus(fFileContextMenu);
651 
652 	fVolumeContextMenu = new BPopUpMenu("VolumeContext", false, false);
653 	AddVolumeContextMenus(fVolumeContextMenu);
654 
655 	fWindowContextMenu = new BPopUpMenu("WindowContext", false, false);
656 	AddWindowContextMenus(fWindowContextMenu);
657 
658 	fDropContextMenu = new BPopUpMenu("DropContext", false, false);
659 	AddDropContextMenus(fDropContextMenu);
660 
661 	fDragContextMenu = new BPopUpNavMenu("DragContext");
662 		// will get added and built dynamically in ShowContextMenu
663 
664 	fTrashContextMenu = new BPopUpMenu("TrashContext", false, false);
665 	AddTrashContextMenus(fTrashContextMenu);
666 }
667 
668 
669 void
670 BContainerWindow::RepopulateMenus()
671 {
672 	// Avoid these menus to be destroyed:
673 	if (fMoveToItem != NULL && fMoveToItem->Menu() != NULL)
674 		fMoveToItem->Menu()->RemoveItem(fMoveToItem);
675 
676 	if (fCopyToItem != NULL && fCopyToItem->Menu() != NULL)
677 		fCopyToItem->Menu()->RemoveItem(fCopyToItem);
678 
679 	if (fCreateLinkItem != NULL && fCreateLinkItem->Menu() != NULL)
680 		fCreateLinkItem->Menu()->RemoveItem(fCreateLinkItem);
681 
682 	if (fOpenWithItem != NULL && fOpenWithItem->Menu() != NULL) {
683 		fOpenWithItem->Menu()->RemoveItem(fOpenWithItem);
684 		delete fOpenWithItem;
685 		fOpenWithItem = NULL;
686 	}
687 
688 	if (fNavigationItem != NULL) {
689 		BMenu* menu = fNavigationItem->Menu();
690 		if (menu != NULL) {
691 			menu->RemoveItem(fNavigationItem);
692 			BMenuItem* item = menu->RemoveItem((int32)0);
693 			ASSERT(item != fNavigationItem);
694 			delete item;
695 		}
696 	}
697 
698 	delete fFileContextMenu;
699 	fFileContextMenu = new BPopUpMenu("FileContext", false, false);
700 	AddFileContextMenus(fFileContextMenu);
701 
702 	delete fWindowContextMenu;
703 	fWindowContextMenu = new BPopUpMenu("WindowContext", false, false);
704 	AddWindowContextMenus(fWindowContextMenu);
705 
706 	if (fMenuBar != NULL) {
707 		fMenuBar->RemoveItem(fFileMenu);
708 		delete fFileMenu;
709 		fFileMenu = new BMenu(B_TRANSLATE("File"));
710 		AddFileMenu(fFileMenu);
711 		fMenuBar->AddItem(fFileMenu);
712 
713 		fMenuBar->RemoveItem(fWindowMenu);
714 		delete fWindowMenu;
715 		fWindowMenu = new BMenu(B_TRANSLATE("Window"));
716 		fMenuBar->AddItem(fWindowMenu);
717 		AddWindowMenu(fWindowMenu);
718 
719 		// just create the attribute, decide to add it later
720 		fMenuBar->RemoveItem(fAttrMenu);
721 		delete fAttrMenu;
722 		fAttrMenu = new BMenu(B_TRANSLATE("Attributes"));
723 		NewAttributesMenu(fAttrMenu);
724 		if (PoseView()->ViewMode() == kListMode)
725 			ShowAttributesMenu();
726 
727 		PopulateArrangeByMenu(fArrangeByMenu);
728 
729 		int32 selectCount = PoseView()->CountSelected();
730 
731 		SetupOpenWithMenu(fFileMenu);
732 		SetupMoveCopyMenus(selectCount ? PoseView()->SelectionList()
733 				->FirstItem()->TargetModel()->EntryRef() : NULL,
734 			fFileMenu);
735 	}
736 }
737 
738 
739 void
740 BContainerWindow::Init(const BMessage* message)
741 {
742 	// pose view is expected to be setup at this point
743 	if (PoseView() == NULL)
744 		return;
745 
746 	// deal with new unconfigured folders
747 	if (NeedsDefaultStateSetup())
748 		SetupDefaultState();
749 
750 	if (ShouldAddScrollBars())
751 		PoseView()->AddScrollBars();
752 
753 	fMoveToItem = new BMenuItem(new BNavMenu(B_TRANSLATE("Move to"),
754 		kMoveSelectionTo, this));
755 	fCopyToItem = new BMenuItem(new BNavMenu(B_TRANSLATE("Copy to"),
756 		kCopySelectionTo, this));
757 	fCreateLinkItem = new BMenuItem(new BNavMenu(B_TRANSLATE("Create link"),
758 		kCreateLink, this), new BMessage(kCreateLink));
759 
760 	TrackerSettings settings;
761 
762 	if (ShouldAddMenus()) {
763 		fMenuBar = new BMenuBar("MenuBar");
764 		fMenuContainer->GroupLayout()->AddView(fMenuBar);
765 		AddMenus();
766 
767 		if (!TargetModel()->IsRoot() && !TargetModel()->IsTrash())
768 			_AddFolderIcon();
769 	} else {
770 		// add equivalents of the menu shortcuts to the menuless
771 		// desktop window
772 		AddShortcuts();
773 	}
774 
775 	AddContextMenus();
776 	AddShortcut('T', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(kDeleteSelection), PoseView());
777 	AddShortcut('K', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(kCleanupAll), PoseView());
778 	AddShortcut('Q', B_COMMAND_KEY | B_OPTION_KEY | B_SHIFT_KEY | B_CONTROL_KEY,
779 		new BMessage(kQuitTracker));
780 
781 	AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY, new BMessage(kOpenSelection), PoseView());
782 
783 	SetSingleWindowBrowseShortcuts(settings.SingleWindowBrowse());
784 
785 #if DEBUG
786 	// add some debugging shortcuts
787 	AddShortcut('D', B_COMMAND_KEY | B_CONTROL_KEY, new BMessage('dbug'),
788 		PoseView());
789 	AddShortcut('C', B_COMMAND_KEY | B_CONTROL_KEY, new BMessage('dpcc'),
790 		PoseView());
791 	AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY, new BMessage('dpfl'),
792 		PoseView());
793 	AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY,
794 		new BMessage('dpfL'), PoseView());
795 #endif
796 
797 	BKeymap keymap;
798 	if (keymap.SetToCurrent() == B_OK) {
799 		BObjectList<const char> unmodified(3, true);
800 		if (keymap.GetModifiedCharacters("+", B_SHIFT_KEY, 0, &unmodified)
801 				== B_OK) {
802 			int32 count = unmodified.CountItems();
803 			for (int32 i = 0; i < count; i++) {
804 				uint32 key = BUnicodeChar::FromUTF8(unmodified.ItemAt(i));
805 				if (!HasShortcut(key, 0)) {
806 					// Add semantic zoom in shortcut, bug #6692
807 					BMessage* increaseSize = new BMessage(kIconMode);
808 					increaseSize->AddInt32("scale", 1);
809 					AddShortcut(key, B_COMMAND_KEY, increaseSize, PoseView());
810 				}
811 			}
812 		}
813 		unmodified.MakeEmpty();
814 	}
815 
816 	if (message != NULL)
817 		RestoreState(*message);
818 	else
819 		RestoreState();
820 
821 	if (ShouldAddMenus() && PoseView()->ViewMode() == kListMode) {
822 		// for now only show attributes in list view
823 		// eventually enable attribute menu to allow users to select
824 		// using different attributes as titles in icon view modes
825 		ShowAttributesMenu();
826 	}
827 	CheckScreenIntersect();
828 
829 	bool isListMode = PoseView()->ViewMode() == kListMode;
830 	if (fBackgroundImage != NULL && !PoseView()->IsDesktop() && !isListMode)
831 		fBackgroundImage->Show(PoseView(), current_workspace());
832 
833 	Show();
834 
835 	// done showing, turn the B_NO_WORKSPACE_ACTIVATION flag off;
836 	// it was on to prevent workspace jerking during boot
837 	SetFlags(Flags() & ~B_NO_WORKSPACE_ACTIVATION);
838 }
839 
840 
841 void
842 BContainerWindow::InitLayout()
843 {
844 	fBorderedView->GroupLayout()->AddView(0, PoseView()->TitleView());
845 
846 	fCountContainer->GroupLayout()->AddView(PoseView()->CountView(), 0.25f);
847 
848 	bool forFilePanel = PoseView()->IsFilePanel();
849 	if (!forFilePanel) {
850 		// Eliminate the extra borders
851 		fPoseContainer->GridLayout()->SetInsets(-1, 0, -1, -1);
852 		fCountContainer->GroupLayout()->SetInsets(0, -1, 0, 0);
853 	}
854 
855 	if (PoseView()->VScrollBar() != NULL) {
856 		fVScrollBarContainer = new BGroupView(B_VERTICAL, 0);
857 		fVScrollBarContainer->GroupLayout()->AddView(PoseView()->VScrollBar());
858 		fVScrollBarContainer->GroupLayout()->SetInsets(-1, forFilePanel ? 0 : -1,
859 			0, 0);
860 		fPoseContainer->GridLayout()->AddView(fVScrollBarContainer, 1, 1);
861 	}
862 	if (PoseView()->HScrollBar() != NULL) {
863 		BGroupView* hScrollBarContainer = new BGroupView(B_VERTICAL, 0);
864 		hScrollBarContainer->GroupLayout()->AddView(PoseView()->HScrollBar());
865 		hScrollBarContainer->GroupLayout()->SetInsets(0, -1, 0,
866 			forFilePanel ? 0 : -1);
867 		fCountContainer->GroupLayout()->AddView(hScrollBarContainer);
868 
869 		BSize size = PoseView()->HScrollBar()->MinSize();
870 		if (forFilePanel) {
871 			// Count view height is 1px smaller than scroll bar because it has
872 			// no upper border.
873 			size.height -= 1;
874 		}
875 		PoseView()->CountView()->SetExplicitMinSize(size);
876 	}
877 }
878 
879 
880 void
881 BContainerWindow::RestoreState()
882 {
883 	UpdateTitle();
884 
885 	WindowStateNodeOpener opener(this, false);
886 	RestoreWindowState(opener.StreamNode());
887 	PoseView()->Init(opener.StreamNode());
888 
889 	RestoreStateCommon();
890 }
891 
892 
893 void
894 BContainerWindow::RestoreState(const BMessage &message)
895 {
896 	UpdateTitle();
897 
898 	RestoreWindowState(message);
899 	PoseView()->Init(message);
900 
901 	RestoreStateCommon();
902 }
903 
904 
905 void
906 BContainerWindow::RestoreStateCommon()
907 {
908 	if (fUsesLayout)
909 		InitLayout();
910 
911 	if (BootedInSafeMode())
912 		// don't pick up backgrounds in safe mode
913 		return;
914 
915 	bool isDesktop = PoseView()->IsDesktop();
916 
917 	WindowStateNodeOpener opener(this, false);
918 	if (!TargetModel()->IsRoot() && opener.Node() != NULL) {
919 		// don't pick up background image for root disks
920 		// to do this, would have to have a unique attribute for the
921 		// disks window that doesn't collide with the desktop
922 		// for R4 this was not done to make things simpler
923 		// the default image will still work though
924 		fBackgroundImage = BackgroundImage::GetBackgroundImage(opener.Node(), isDesktop);
925 			// look for background image info in the window's node
926 	}
927 
928 	BNode defaultingNode;
929 	if (fBackgroundImage == NULL && !isDesktop
930 		&& DefaultStateSourceNode(kDefaultFolderTemplate, &defaultingNode)) {
931 		// look for background image info in the source for defaults
932 		fBackgroundImage = BackgroundImage::GetBackgroundImage(&defaultingNode, isDesktop);
933 	}
934 }
935 
936 
937 void
938 BContainerWindow::UpdateTitle()
939 {
940 	// set title to full path, if necessary
941 	if (TrackerSettings().ShowFullPathInTitleBar()) {
942 		// use the Entry's full path
943 		BPath path;
944 		TargetModel()->GetPath(&path);
945 		SetTitle(path.Path());
946 	} else {
947 		// use the default look
948 		SetTitle(TargetModel()->Name());
949 	}
950 
951 	if (Navigator() != NULL)
952 		Navigator()->UpdateLocation(TargetModel(), kActionUpdatePath);
953 }
954 
955 
956 void
957 BContainerWindow::UpdateBackgroundImage()
958 {
959 	if (BootedInSafeMode())
960 		return;
961 
962 	WindowStateNodeOpener opener(this, false);
963 
964 	if (!TargetModel()->IsRoot() && opener.Node() != NULL) {
965 		fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage, opener.Node(),
966 			PoseView()->IsDesktop(), PoseView());
967 	}
968 
969 		// look for background image info in the window's node
970 	BNode defaultingNode;
971 	if (!fBackgroundImage && !PoseView()->IsDesktop()
972 		&& DefaultStateSourceNode(kDefaultFolderTemplate, &defaultingNode)) {
973 		// look for background image info in the source for defaults
974 		fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage, &defaultingNode,
975 			PoseView()->IsDesktop(), PoseView());
976 	}
977 }
978 
979 
980 void
981 BContainerWindow::FrameResized(float, float)
982 {
983 	if (PoseView() != NULL && !PoseView()->IsDesktop()) {
984 		BRect extent = PoseView()->Extent();
985 		float offsetX = extent.left - PoseView()->Bounds().left;
986 		float offsetY = extent.top - PoseView()->Bounds().top;
987 
988 		// scroll when the size augmented, there is a negative offset
989 		// and we have resized over the bottom right corner of the extent
990 		BPoint scroll(B_ORIGIN);
991 		if (offsetX < 0 && PoseView()->Bounds().right > extent.right
992 			&& Bounds().Width() > fPreviousBounds.Width()) {
993 			scroll.x = std::max(fPreviousBounds.Width() - Bounds().Width(),
994 				offsetX);
995 		}
996 
997 		if (offsetY < 0 && PoseView()->Bounds().bottom > extent.bottom
998 			&& Bounds().Height() > fPreviousBounds.Height()) {
999 			scroll.y = std::max(fPreviousBounds.Height() - Bounds().Height(),
1000 				offsetY);
1001 		}
1002 
1003 		if (scroll != B_ORIGIN)
1004 			PoseView()->ScrollBy(scroll.x, scroll.y);
1005 
1006 		PoseView()->UpdateScrollRange();
1007 		PoseView()->ResetPosePlacementHint();
1008 	}
1009 
1010 	fPreviousBounds = Bounds();
1011 	if (IsActive())
1012 		fStateNeedsSaving = true;
1013 }
1014 
1015 
1016 void
1017 BContainerWindow::FrameMoved(BPoint)
1018 {
1019 	if (IsActive())
1020 		fStateNeedsSaving = true;
1021 }
1022 
1023 
1024 void
1025 BContainerWindow::WorkspacesChanged(uint32, uint32)
1026 {
1027 	if (IsActive())
1028 		fStateNeedsSaving = true;
1029 }
1030 
1031 
1032 void
1033 BContainerWindow::ViewModeChanged(uint32 oldMode, uint32 newMode)
1034 {
1035 	if (fBackgroundImage == NULL)
1036 		return;
1037 
1038 	if (newMode == kListMode)
1039 		fBackgroundImage->Remove();
1040 	else if (oldMode == kListMode)
1041 		fBackgroundImage->Show(PoseView(), current_workspace());
1042 }
1043 
1044 
1045 void
1046 BContainerWindow::CheckScreenIntersect()
1047 {
1048 	BScreen screen(this);
1049 	BRect screenFrame(screen.Frame());
1050 	BRect frame(Frame());
1051 
1052 	if (sNewWindRect.bottom > screenFrame.bottom)
1053 		sNewWindRect.OffsetTo(85, 50);
1054 
1055 	if (sNewWindRect.right > screenFrame.right)
1056 		sNewWindRect.OffsetTo(85, 50);
1057 
1058 	if (!frame.Intersects(screenFrame))
1059 		MoveTo(sNewWindRect.LeftTop());
1060 }
1061 
1062 
1063 void
1064 BContainerWindow::SaveState(bool hide)
1065 {
1066 	if (SaveStateIsEnabled()) {
1067 		WindowStateNodeOpener opener(this, true);
1068 		if (opener.StreamNode() != NULL)
1069 			SaveWindowState(opener.StreamNode());
1070 
1071 		if (hide)
1072 			Hide();
1073 
1074 		if (opener.StreamNode())
1075 			PoseView()->SaveState(opener.StreamNode());
1076 
1077 		fStateNeedsSaving = false;
1078 	}
1079 }
1080 
1081 
1082 void
1083 BContainerWindow::SaveState(BMessage& message) const
1084 {
1085 	if (SaveStateIsEnabled()) {
1086 		SaveWindowState(message);
1087 		PoseView()->SaveState(message);
1088 	}
1089 }
1090 
1091 
1092 bool
1093 BContainerWindow::StateNeedsSaving() const
1094 {
1095 	return PoseView() != NULL && (fStateNeedsSaving || PoseView()->StateNeedsSaving());
1096 }
1097 
1098 
1099 status_t
1100 BContainerWindow::GetLayoutState(BNode* node, BMessage* message)
1101 {
1102 	if (node == NULL || message == NULL)
1103 		return B_BAD_VALUE;
1104 
1105 	status_t result = node->InitCheck();
1106 	if (result != B_OK)
1107 		return result;
1108 
1109 	// ToDo: get rid of this, use AttrStream instead
1110 	node->RewindAttrs();
1111 	char attrName[256];
1112 	while (node->GetNextAttrName(attrName) == B_OK) {
1113 		attr_info info;
1114 		if (node->GetAttrInfo(attrName, &info) != B_OK)
1115 			continue;
1116 
1117 		// filter out attributes that are not related to window position
1118 		// and column resizing
1119 		// more can be added as needed
1120 		if (strcmp(attrName, kAttrWindowFrame) != 0
1121 			&& strcmp(attrName, kAttrColumns) != 0
1122 			&& strcmp(attrName, kAttrViewState) != 0
1123 			&& strcmp(attrName, kAttrColumnsForeign) != 0
1124 			&& strcmp(attrName, kAttrViewStateForeign) != 0) {
1125 			continue;
1126 		}
1127 
1128 		char* buffer = new char[info.size];
1129 		if (node->ReadAttr(attrName, info.type, 0, buffer,
1130 				(size_t)info.size) == info.size) {
1131 			message->AddData(attrName, info.type, buffer, (ssize_t)info.size);
1132 		}
1133 		delete[] buffer;
1134 	}
1135 
1136 	return B_OK;
1137 }
1138 
1139 
1140 status_t
1141 BContainerWindow::SetLayoutState(BNode* node, const BMessage* message)
1142 {
1143 	status_t result = node->InitCheck();
1144 	if (result != B_OK)
1145 		return result;
1146 
1147 	for (int32 globalIndex = 0; ;) {
1148 #if B_BEOS_VERSION_DANO
1149 		const char* name;
1150 #else
1151 		char* name;
1152 #endif
1153 		type_code type;
1154 		int32 count;
1155 		status_t result = message->GetInfo(B_ANY_TYPE, globalIndex, &name,
1156 			&type, &count);
1157 		if (result != B_OK)
1158 			break;
1159 
1160 		for (int32 index = 0; index < count; index++) {
1161 			const void* buffer;
1162 			ssize_t size;
1163 			result = message->FindData(name, type, index, &buffer, &size);
1164 			if (result != B_OK) {
1165 				PRINT(("error reading %s \n", name));
1166 				return result;
1167 			}
1168 
1169 			if (node->WriteAttr(name, type, 0, buffer,
1170 					(size_t)size) != size) {
1171 				PRINT(("error writing %s \n", name));
1172 				return result;
1173 			}
1174 			globalIndex++;
1175 		}
1176 	}
1177 
1178 	return B_OK;
1179 }
1180 
1181 
1182 bool
1183 BContainerWindow::ShouldAddMenus() const
1184 {
1185 	return true;
1186 }
1187 
1188 
1189 bool
1190 BContainerWindow::ShouldAddScrollBars() const
1191 {
1192 	return true;
1193 }
1194 
1195 
1196 Model*
1197 BContainerWindow::TargetModel() const
1198 {
1199 	return PoseView()->TargetModel();
1200 }
1201 
1202 
1203 void
1204 BContainerWindow::SelectionChanged()
1205 {
1206 }
1207 
1208 
1209 void
1210 BContainerWindow::Zoom(BPoint, float, float)
1211 {
1212 	BRect oldZoomRect(fSavedZoomRect);
1213 	fSavedZoomRect = Frame();
1214 	ResizeToFit();
1215 
1216 	if (fSavedZoomRect == Frame() && oldZoomRect.IsValid())
1217 		ResizeTo(oldZoomRect.Width(), oldZoomRect.Height());
1218 }
1219 
1220 
1221 void
1222 BContainerWindow::ResizeToFit()
1223 {
1224 	BScreen screen(this);
1225 	BRect screenFrame(screen.Frame());
1226 
1227 	screenFrame.InsetBy(5, 5);
1228 	BMessage decoratorSettings;
1229 	GetDecoratorSettings(&decoratorSettings);
1230 
1231 	float tabHeight = 15;
1232 	BRect tabRect;
1233 	if (decoratorSettings.FindRect("tab frame", &tabRect) == B_OK)
1234 		tabHeight = tabRect.Height();
1235 	screenFrame.top += tabHeight;
1236 
1237 	BRect frame(Frame());
1238 
1239 	float widthDiff = frame.Width() - PoseView()->Frame().Width();
1240 	float heightDiff = frame.Height() - PoseView()->Frame().Height();
1241 
1242 	// move frame left top on screen
1243 	BPoint leftTop(frame.LeftTop());
1244 	leftTop.ConstrainTo(screenFrame);
1245 	frame.OffsetTo(leftTop);
1246 
1247 	// resize to extent size
1248 	BRect extent(PoseView()->Extent());
1249 	frame.right = frame.left + extent.Width() + widthDiff;
1250 	frame.bottom = frame.top + extent.Height() + heightDiff;
1251 
1252 	// make sure entire window fits on screen
1253 	frame = frame & screenFrame;
1254 
1255 	ResizeTo(frame.Width(), frame.Height());
1256 	MoveTo(frame.LeftTop());
1257 	PoseView()->DisableScrollBars();
1258 
1259 	// scroll if there is an offset
1260 	PoseView()->ScrollBy(
1261 		extent.left - PoseView()->Bounds().left,
1262 		extent.top - PoseView()->Bounds().top);
1263 
1264 	PoseView()->UpdateScrollRange();
1265 	PoseView()->EnableScrollBars();
1266 }
1267 
1268 
1269 void
1270 BContainerWindow::MessageReceived(BMessage* message)
1271 {
1272 	switch (message->what) {
1273 		case B_CUT:
1274 		case B_COPY:
1275 		case B_PASTE:
1276 		case B_SELECT_ALL:
1277 		{
1278 			BView* view = CurrentFocus();
1279 			if (dynamic_cast<BTextView*>(view) == NULL) {
1280 				// The selected item is not a BTextView, so forward the
1281 				// message to the PoseView.
1282 				if (PoseView() != NULL)
1283 					PostMessage(message, PoseView());
1284 			} else {
1285 				// Since we catch the generic clipboard shortcuts in a way that
1286 				// means the BTextView will never get them, we must
1287 				// manually forward them ourselves.
1288 				//
1289 				// However, we have to take care to not forward the custom
1290 				// clipboard messages, else we would wind up in infinite
1291 				// recursion.
1292 				PostMessage(message, view);
1293 			}
1294 			break;
1295 		}
1296 
1297 		case kCutMoreSelectionToClipboard:
1298 		case kCopyMoreSelectionToClipboard:
1299 		case kPasteLinksFromClipboard:
1300 			if (PoseView() != NULL)
1301 				PostMessage(message, PoseView());
1302 			break;
1303 
1304 		case B_UNDO: {
1305 			BView* view = CurrentFocus();
1306 			if (dynamic_cast<BTextView*>(view) == NULL) {
1307 				FSUndo();
1308 			} else {
1309 				view->MessageReceived(message);
1310 			}
1311 			break;
1312 		}
1313 
1314 		case B_REDO: {
1315 			BView* view = CurrentFocus();
1316 			if (dynamic_cast<BTextView*>(view) == NULL) {
1317 				FSRedo();
1318 			} else {
1319 				view->MessageReceived(message);
1320 			}
1321 			break;
1322 		}
1323 
1324 		case kOpenParentDir:
1325 			PostMessage(message, PoseView());
1326 			break;
1327 
1328 		case kNewFolder:
1329 			PostMessage(message, PoseView());
1330 			break;
1331 
1332 		case kNewTemplateSubmenu:
1333 		{
1334 			entry_ref ref;
1335 			if (message->FindRef("refs", &ref) == B_OK)
1336 				_NewTemplateSubmenu(ref);
1337 			break;
1338 		}
1339 
1340 		case kRestoreState:
1341 			if (message->HasMessage("state")) {
1342 				BMessage state;
1343 				message->FindMessage("state", &state);
1344 				Init(&state);
1345 			} else
1346 				Init();
1347 			break;
1348 
1349 		case kResizeToFit:
1350 			ResizeToFit();
1351 			break;
1352 
1353 		case kLoadAddOn:
1354 			LoadAddOn(message);
1355 			break;
1356 
1357 		case kCopySelectionTo:
1358 		{
1359 			entry_ref ref;
1360 			if (message->FindRef("refs", &ref) != B_OK)
1361 				break;
1362 
1363 			BRoster().AddToRecentFolders(&ref);
1364 
1365 			Model model(&ref);
1366 			if (model.InitCheck() != B_OK)
1367 				break;
1368 
1369 			if (*model.NodeRef() == *TargetModel()->NodeRef())
1370 				PoseView()->DuplicateSelection();
1371 			else
1372 				PoseView()->MoveSelectionInto(&model, this, true);
1373 			break;
1374 		}
1375 
1376 		case kMoveSelectionTo:
1377 		{
1378 			entry_ref ref;
1379 			if (message->FindRef("refs", &ref) != B_OK)
1380 				break;
1381 
1382 			BRoster().AddToRecentFolders(&ref);
1383 
1384 			Model model(&ref);
1385 			if (model.InitCheck() != B_OK)
1386 				break;
1387 
1388 			PoseView()->MoveSelectionInto(&model, this, false, true);
1389 			break;
1390 		}
1391 
1392 		case kCreateLink:
1393 		case kCreateRelativeLink:
1394 		{
1395 			entry_ref ref;
1396 			if (message->FindRef("refs", &ref) == B_OK) {
1397 				BRoster().AddToRecentFolders(&ref);
1398 
1399 				Model model(&ref);
1400 				if (model.InitCheck() != B_OK)
1401 					break;
1402 
1403 				PoseView()->MoveSelectionInto(&model, this, false, false,
1404 					message->what == kCreateLink,
1405 					message->what == kCreateRelativeLink);
1406 			} else if (!TargetModel()->IsQuery() && !TargetModel()->IsVirtualDirectory()) {
1407 				// no destination specified, create link in same dir as item
1408 				PoseView()->MoveSelectionInto(TargetModel(), this, false, false,
1409 					message->what == kCreateLink,
1410 					message->what == kCreateRelativeLink);
1411 			}
1412 			break;
1413 		}
1414 
1415 		case kShowSelectionWindow:
1416 			ShowSelectionWindow();
1417 			break;
1418 
1419 		case kSelectMatchingEntries:
1420 			PoseView()->SelectMatchingEntries(message);
1421 			break;
1422 
1423 		case kFindButton:
1424 			(new FindWindow())->Show();
1425 			break;
1426 
1427 		case kQuitTracker:
1428 			be_app->PostMessage(B_QUIT_REQUESTED);
1429 			break;
1430 
1431 		case kRestoreBackgroundImage:
1432 			UpdateBackgroundImage();
1433 			break;
1434 
1435 		case kSwitchDirectory:
1436 		{
1437 			entry_ref ref;
1438 			if (message->FindRef("refs", &ref) != B_OK)
1439 				break;
1440 
1441 			BEntry entry;
1442 			if (entry.SetTo(&ref) != B_OK)
1443 				break;
1444 
1445 			if (StateNeedsSaving())
1446 				SaveState(false);
1447 
1448 			bool wasInTrash = TargetModel()->IsTrash() || TargetModel()->InTrash();
1449 			bool wasRoot = TargetModel()->IsRoot();
1450 			bool wasVolume = TargetModel()->IsVolume();
1451 
1452 			// Switch dir and apply new state
1453 			WindowStateNodeOpener opener(this, false);
1454 			opener.SetTo(&entry, false);
1455 
1456 			// Update pose view and set directory type
1457 			PoseView()->SwitchDir(&ref, opener.StreamNode());
1458 
1459 			if (wasInTrash ^ (TargetModel()->IsTrash() || TargetModel()->InTrash())
1460 				|| wasRoot != TargetModel()->IsRoot() || wasVolume != TargetModel()->IsVolume()) {
1461 				RepopulateMenus();
1462 			}
1463 
1464 			// skip the rest for file panel
1465 			if (PoseView()->IsFilePanel())
1466 				break;
1467 
1468 			if (Navigator() != NULL) {
1469 				// update Navigation bar
1470 				int32 action = message->GetInt32("action", kActionSet);
1471 				Navigator()->UpdateLocation(TargetModel(), action);
1472 			}
1473 
1474 			TrackerSettings settings;
1475 			if (settings.ShowNavigator() || settings.ShowFullPathInTitleBar())
1476 				SetPathWatchingEnabled(true);
1477 
1478 			SetSingleWindowBrowseShortcuts(settings.SingleWindowBrowse());
1479 
1480 			// Update draggable folder icon
1481 			if (fMenuBar != NULL) {
1482 				if (!TargetModel()->IsRoot() && !TargetModel()->IsTrash()) {
1483 					// Folder icon should be visible, but in single
1484 					// window navigation, it might not be.
1485 					if (fDraggableIcon != NULL) {
1486 						IconCache::sIconCache->IconChanged(TargetModel());
1487 						if (fDraggableIcon->IsHidden())
1488 							fDraggableIcon->Show();
1489 						fDraggableIcon->Invalidate();
1490 					} else {
1491 						// draggable icon visible
1492 						_AddFolderIcon();
1493 					}
1494 				} else if (fDraggableIcon != NULL) {
1495 					// hide for Root or Trash
1496 					fDraggableIcon->Hide();
1497 				}
1498 			}
1499 
1500 			// Update window title
1501 			UpdateTitle();
1502 			break;
1503 		}
1504 
1505 		case B_REFS_RECEIVED:
1506 			if (Dragging()) {
1507 				// ref in this message is the target,
1508 				// the end point of the drag
1509 
1510 				entry_ref ref;
1511 				if (message->FindRef("refs", &ref) == B_OK) {
1512 					fWaitingForRefs = false;
1513 					BEntry entry(&ref, true);
1514 					// don't copy to printers dir
1515 					if (!FSIsPrintersDir(&entry)) {
1516 						if (entry.InitCheck() == B_OK
1517 							&& entry.IsDirectory()) {
1518 							Model targetModel(&entry, true, false);
1519 							BPoint dropPoint;
1520 							uint32 buttons;
1521 							PoseView()->GetMouse(&dropPoint, &buttons, true);
1522 							PoseView()->HandleDropCommon(fDragMessage,
1523 								&targetModel, NULL, PoseView(), dropPoint);
1524 						}
1525 					}
1526 				}
1527 				DragStop();
1528 			}
1529 			break;
1530 
1531 		case B_TRACKER_ADDON_MESSAGE:
1532 		{
1533 			_PassMessageToAddOn(message);
1534 			break;
1535 		}
1536 
1537 		case B_OBSERVER_NOTICE_CHANGE:
1538 		{
1539 			int32 observerWhat;
1540 			if (message->FindInt32("be:observe_change_what", &observerWhat)
1541 					== B_OK) {
1542 				TrackerSettings settings;
1543 				switch (observerWhat) {
1544 					case kWindowsShowFullPathChanged:
1545 						UpdateTitle();
1546 						if (!IsPathWatchingEnabled()
1547 							&& settings.ShowFullPathInTitleBar()) {
1548 							SetPathWatchingEnabled(true);
1549 						}
1550 						if (IsPathWatchingEnabled()
1551 							&& !(settings.ShowNavigator() || settings.ShowFullPathInTitleBar())) {
1552 							SetPathWatchingEnabled(false);
1553 						}
1554 						break;
1555 
1556 					case kSingleWindowBrowseChanged:
1557 						if (PoseView()->IsFilePanel() || PoseView()->IsDesktop()) {
1558 							SetSingleWindowBrowseShortcuts(settings.SingleWindowBrowse());
1559 							break;
1560 						}
1561 
1562 						if (settings.SingleWindowBrowse() && Navigator() == NULL
1563 							&& TargetModel()->IsDirectory()) {
1564 							fNavigator = new BNavigator(TargetModel());
1565 							fPoseContainer->GridLayout()->AddView(fNavigator, 0, 0, 2);
1566 							fNavigator->Hide();
1567 							SetPathWatchingEnabled(settings.ShowNavigator()
1568 								|| settings.ShowFullPathInTitleBar());
1569 						}
1570 
1571 						if (!settings.SingleWindowBrowse() && fWindowList != NULL) {
1572 							// close duplicate windows
1573 							int32 windowCount = fWindowList->CountItems();
1574 							BObjectList<BContainerWindow> containerList(windowCount);
1575 							for (int32 index = 0; index < windowCount; index++) {
1576 								BContainerWindow* window
1577 									= dynamic_cast<BContainerWindow*>(fWindowList->ItemAt(index));
1578 								if (window != NULL && window->TargetModel() != NULL
1579 									&& window->TargetModel()->NodeRef() != NULL) {
1580 									containerList.AddItem(window);
1581 								}
1582 							}
1583 
1584 							windowCount = containerList.CountItems();
1585 								// get the window count again as it may have changed
1586 							if (windowCount > 1) {
1587 								// sort containerList by node ref
1588 								containerList.SortItems(CompareContainerWindowNodeRef);
1589 
1590 								// go backwards from second to last item to first
1591 								for (int32 index = windowCount - 2; index >= 0; --index) {
1592 									BContainerWindow* window = containerList.ItemAt(index);
1593 									BContainerWindow* second = containerList.ItemAt(index + 1);
1594 									if (window == NULL || second == NULL)
1595 										continue;
1596 
1597 									const node_ref* windowRef = window->TargetModel()->NodeRef();
1598 									const node_ref* secondRef = second->TargetModel()->NodeRef();
1599 									if (*windowRef == *secondRef) {
1600 										// duplicate windows found, close second window
1601 										// use BMessenger::SendMessage(), safer than PostMessage()
1602 										BMessenger(second).SendMessage(B_QUIT_REQUESTED);
1603 									}
1604 								}
1605 							}
1606 						}
1607 
1608 						if (!settings.SingleWindowBrowse() && TargetModel()->IsDesktop()) {
1609 							// close the "Desktop" window, but not the Desktop
1610 							this->Quit();
1611 						}
1612 
1613 						SetSingleWindowBrowseShortcuts(settings.SingleWindowBrowse());
1614 						break;
1615 
1616 					case kShowNavigatorChanged:
1617 						ShowNavigator(settings.ShowNavigator());
1618 						if (!IsPathWatchingEnabled() && settings.ShowNavigator())
1619 							SetPathWatchingEnabled(true);
1620 						if (IsPathWatchingEnabled()
1621 							&& !(settings.ShowNavigator() || settings.ShowFullPathInTitleBar())) {
1622 							SetPathWatchingEnabled(false);
1623 						}
1624 						SetSingleWindowBrowseShortcuts(settings.SingleWindowBrowse());
1625 						break;
1626 
1627 					default:
1628 						_inherited::MessageReceived(message);
1629 						break;
1630 				}
1631 			}
1632 			break;
1633 		}
1634 
1635 		case B_NODE_MONITOR:
1636 			UpdateTitle();
1637 			break;
1638 
1639 		default:
1640 			_inherited::MessageReceived(message);
1641 			break;
1642 	}
1643 }
1644 
1645 
1646 void
1647 BContainerWindow::SetCutItem(BMenu* menu)
1648 {
1649 	BMenuItem* item;
1650 	if ((item = menu->FindItem(B_CUT)) == NULL
1651 		&& (item = menu->FindItem(kCutMoreSelectionToClipboard)) == NULL) {
1652 		return;
1653 	}
1654 
1655 	if (PoseView() != CurrentFocus())
1656 		item->SetEnabled(dynamic_cast<BTextView*>(CurrentFocus()) != NULL);
1657 	else {
1658 		if (TargetModel()->IsRoot() || TargetModel()->IsTrash()
1659 			|| TargetModel()->IsVirtualDirectory()) {
1660 			// cannot cut files in root, trash or in a virtual directory
1661 			item->SetEnabled(false);
1662 		} else {
1663 			item->SetEnabled(PoseView()->CountSelected() > 0
1664 				&& !PoseView()->SelectedVolumeIsReadOnly());
1665 		}
1666 	}
1667 
1668 	if ((modifiers() & B_SHIFT_KEY) != 0) {
1669 		item->SetLabel(B_TRANSLATE("Cut more"));
1670 		item->SetShortcut('X', B_COMMAND_KEY | B_SHIFT_KEY);
1671 		item->SetMessage(new BMessage(kCutMoreSelectionToClipboard));
1672 	} else {
1673 		item->SetLabel(B_TRANSLATE("Cut"));
1674 		item->SetShortcut('X', B_COMMAND_KEY);
1675 		item->SetMessage(new BMessage(B_CUT));
1676 	}
1677 }
1678 
1679 
1680 void
1681 BContainerWindow::SetCopyItem(BMenu* menu)
1682 {
1683 	BMenuItem* item;
1684 	if ((item = menu->FindItem(B_COPY)) == NULL
1685 		&& (item = menu->FindItem(kCopyMoreSelectionToClipboard)) == NULL) {
1686 		return;
1687 	}
1688 
1689 	if (PoseView() != CurrentFocus())
1690 		item->SetEnabled(dynamic_cast<BTextView*>(CurrentFocus()) != NULL);
1691 	else
1692 		item->SetEnabled(PoseView()->CountSelected() > 0);
1693 
1694 	if ((modifiers() & B_SHIFT_KEY) != 0) {
1695 		item->SetLabel(B_TRANSLATE("Copy more"));
1696 		item->SetShortcut('C', B_COMMAND_KEY | B_SHIFT_KEY);
1697 		item->SetMessage(new BMessage(kCopyMoreSelectionToClipboard));
1698 	} else {
1699 		item->SetLabel(B_TRANSLATE("Copy"));
1700 		item->SetShortcut('C', B_COMMAND_KEY);
1701 		item->SetMessage(new BMessage(B_COPY));
1702 	}
1703 }
1704 
1705 
1706 void
1707 BContainerWindow::SetPasteItem(BMenu* menu)
1708 {
1709 	BMenuItem* item;
1710 	if ((item = menu->FindItem(B_PASTE)) == NULL
1711 		&& (item = menu->FindItem(kPasteLinksFromClipboard)) == NULL) {
1712 		return;
1713 	}
1714 
1715 	if (PoseView() != CurrentFocus())
1716 		item->SetEnabled(dynamic_cast<BTextView*>(CurrentFocus()) != NULL);
1717 	else {
1718 		item->SetEnabled(FSClipboardHasRefs()
1719 			&& !PoseView()->TargetVolumeIsReadOnly());
1720 	}
1721 
1722 	if ((modifiers() & B_SHIFT_KEY) != 0) {
1723 		item->SetLabel(B_TRANSLATE("Paste links"));
1724 		item->SetShortcut('V', B_COMMAND_KEY | B_SHIFT_KEY);
1725 		item->SetMessage(new BMessage(kPasteLinksFromClipboard));
1726 	} else {
1727 		item->SetLabel(B_TRANSLATE("Paste"));
1728 		item->SetShortcut('V', B_COMMAND_KEY);
1729 		item->SetMessage(new BMessage(B_PASTE));
1730 	}
1731 }
1732 
1733 
1734 void
1735 BContainerWindow::SetArrangeMenu(BMenu* menu)
1736 {
1737 	BMenuItem* item;
1738 	if ((item = menu->FindItem(kCleanup)) == NULL
1739 		&& (item = menu->FindItem(kCleanupAll)) == NULL) {
1740 		return;
1741 	}
1742 
1743 	item->Menu()->SetEnabled(PoseView()->CountItems() > 0
1744 		&& (PoseView()->ViewMode() != kListMode));
1745 
1746 	BMenu* arrangeMenu;
1747 
1748 	if ((modifiers() & B_SHIFT_KEY) != 0) {
1749 		item->SetLabel(B_TRANSLATE("Clean up all"));
1750 		item->SetShortcut('K', B_COMMAND_KEY | B_SHIFT_KEY);
1751 		item->SetMessage(new BMessage(kCleanupAll));
1752 		arrangeMenu = item->Menu();
1753 	} else {
1754 		item->SetLabel(B_TRANSLATE("Clean up"));
1755 		item->SetShortcut('K', B_COMMAND_KEY);
1756 		item->SetMessage(new BMessage(kCleanup));
1757 		arrangeMenu = item->Menu();
1758 	}
1759 
1760 	MarkArrangeByMenu(arrangeMenu);
1761 }
1762 
1763 
1764 void
1765 BContainerWindow::SetCloseItem(BMenu* menu)
1766 {
1767 	BMenuItem* item;
1768 	if ((item = menu->FindItem(B_QUIT_REQUESTED)) == NULL
1769 		&& (item = menu->FindItem(kCloseAllWindows)) == NULL) {
1770 		return;
1771 	}
1772 
1773 	if ((modifiers() & B_SHIFT_KEY) != 0) {
1774 		item->SetLabel(B_TRANSLATE("Close all"));
1775 		item->SetShortcut('W', B_COMMAND_KEY | B_SHIFT_KEY);
1776 		item->SetTarget(be_app);
1777 		item->SetMessage(new BMessage(kCloseAllWindows));
1778 	} else {
1779 		item->SetLabel(B_TRANSLATE("Close"));
1780 		item->SetShortcut('W', B_COMMAND_KEY);
1781 		item->SetTarget(this);
1782 		item->SetMessage(new BMessage(B_QUIT_REQUESTED));
1783 	}
1784 }
1785 
1786 
1787 bool
1788 BContainerWindow::IsShowing(const node_ref* node) const
1789 {
1790 	return PoseView()->Represents(node);
1791 }
1792 
1793 
1794 bool
1795 BContainerWindow::IsShowing(const entry_ref* entry) const
1796 {
1797 	return PoseView()->Represents(entry);
1798 }
1799 
1800 
1801 void
1802 BContainerWindow::AddMenus()
1803 {
1804 	fFileMenu = new BMenu(B_TRANSLATE("File"));
1805 	AddFileMenu(fFileMenu);
1806 	fMenuBar->AddItem(fFileMenu);
1807 	fWindowMenu = new BMenu(B_TRANSLATE("Window"));
1808 	fMenuBar->AddItem(fWindowMenu);
1809 	AddWindowMenu(fWindowMenu);
1810 	// just create the attribute, decide to add it later
1811 	fAttrMenu = new BMenu(B_TRANSLATE("Attributes"));
1812 	NewAttributesMenu(fAttrMenu);
1813 	PopulateArrangeByMenu(fArrangeByMenu);
1814 }
1815 
1816 
1817 void
1818 BContainerWindow::AddFileMenu(BMenu* menu)
1819 {
1820 	BMenuItem* item;
1821 
1822 	if (!PoseView()->IsFilePanel()) {
1823 		menu->AddItem(new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS),
1824 			new BMessage(kFindButton), 'F'));
1825 	}
1826 
1827 	if (!TargetModel()->IsQuery() && !TargetModel()->IsVirtualDirectory()
1828 		&& !TargetModel()->IsTrash() && !TargetModel()->IsPrintersDir()
1829 		&& !TargetModel()->IsRoot()) {
1830 		if (!PoseView()->IsFilePanel()) {
1831 			TemplatesMenu* templatesMenu = new TemplatesMenu(PoseView(),
1832 				B_TRANSLATE("New"));
1833 			menu->AddItem(templatesMenu);
1834 			templatesMenu->SetTargetForItems(PoseView());
1835 		} else {
1836 			item = new BMenuItem(B_TRANSLATE("New folder"),
1837 				new BMessage(kNewFolder), 'N');
1838 			menu->AddItem(item);
1839 		}
1840 	}
1841 	menu->AddSeparatorItem();
1842 
1843 	menu->AddItem(new BMenuItem(B_TRANSLATE("Open"),
1844 		new BMessage(kOpenSelection), 'O'));
1845 	menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
1846 		new BMessage(kGetInfo), 'I'));
1847 	menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"),
1848 		new BMessage(kEditItem), 'E'));
1849 
1850 	if (TargetModel()->IsTrash() || TargetModel()->InTrash()) {
1851 		menu->AddItem(new BMenuItem(B_TRANSLATE("Restore"),
1852 			new BMessage(kRestoreFromTrash)));
1853 		if (TargetModel()->IsTrash()) {
1854 			// add as first item in menu
1855 			menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"),
1856 				new BMessage(kEmptyTrash)), 0);
1857 			menu->AddItem(new BSeparatorItem(), 1);
1858 		}
1859 	} else if (TargetModel()->IsPrintersDir()) {
1860 		menu->AddItem(new BMenuItem(B_TRANSLATE("Add printer" B_UTF8_ELLIPSIS),
1861 			new BMessage(kAddPrinter), 'N'), 0);
1862 		menu->AddItem(new BSeparatorItem(), 1);
1863 		menu->AddItem(new BMenuItem(B_TRANSLATE("Make active printer"),
1864 			new BMessage(kMakeActivePrinter)));
1865 	} else if (TargetModel()->IsRoot()) {
1866 		item = new BMenuItem(B_TRANSLATE("Unmount"),
1867 			new BMessage(kUnmountVolume), 'U');
1868 		item->SetEnabled(false);
1869 		menu->AddItem(item);
1870 		menu->AddItem(new BMenuItem(
1871 			B_TRANSLATE("Mount settings" B_UTF8_ELLIPSIS),
1872 			new BMessage(kRunAutomounterSettings)));
1873 	} else {
1874 		item = new BMenuItem(B_TRANSLATE("Duplicate"),
1875 			new BMessage(kDuplicateSelection), 'D');
1876 		item->SetEnabled(PoseView()->CanMoveToTrashOrDuplicate());
1877 		menu->AddItem(item);
1878 
1879 		item = new BMenuItem(B_TRANSLATE("Move to Trash"),
1880 			new BMessage(kMoveSelectionToTrash), 'T');
1881 		item->SetEnabled(PoseView()->CanMoveToTrashOrDuplicate());
1882 		menu->AddItem(item);
1883 
1884 		menu->AddSeparatorItem();
1885 
1886 		// The "Move To", "Copy To", "Create Link" menus are inserted
1887 		// at this place, have a look at:
1888 		// BContainerWindow::SetupMoveCopyMenus()
1889 	}
1890 
1891 	BMenuItem* cutItem = NULL;
1892 	BMenuItem* copyItem = NULL;
1893 	BMenuItem* pasteItem = NULL;
1894 	if (!TargetModel()->IsPrintersDir()) {
1895 		menu->AddSeparatorItem();
1896 
1897 		if (!TargetModel()->IsRoot()) {
1898 			cutItem = new(std::nothrow) BMenuItem(B_TRANSLATE("Cut"),
1899 				new BMessage(B_CUT), 'X');
1900 			menu->AddItem(cutItem);
1901 			copyItem = new(std::nothrow) BMenuItem(B_TRANSLATE("Copy"),
1902 				new BMessage(B_COPY), 'C');
1903 			menu->AddItem(copyItem);
1904 			pasteItem = new(std::nothrow) BMenuItem(B_TRANSLATE("Paste"),
1905 				new BMessage(B_PASTE), 'V');
1906 			menu->AddItem(pasteItem);
1907 			menu->AddSeparatorItem();
1908 
1909 			menu->AddItem(new BMenuItem(B_TRANSLATE("Identify"),
1910 				new BMessage(kIdentifyEntry)));
1911 		}
1912 		BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons"));
1913 		menu->AddItem(addOnMenuItem);
1914 	}
1915 
1916 	menu->SetTargetForItems(PoseView());
1917 	if (cutItem != NULL)
1918 		cutItem->SetTarget(this);
1919 
1920 	if (copyItem != NULL)
1921 		copyItem->SetTarget(this);
1922 
1923 	if (pasteItem != NULL)
1924 		pasteItem->SetTarget(this);
1925 }
1926 
1927 
1928 void
1929 BContainerWindow::AddWindowMenu(BMenu* menu)
1930 {
1931 	BMenuItem* item;
1932 
1933 	BMenu* iconSizeMenu = new BMenu(B_TRANSLATE("Icon view"));
1934 
1935 	static const uint32 kIconSizes[] = { 32, 40, 48, 64, 96, 128 };
1936 	BMessage* message;
1937 
1938 	for (uint32 i = 0; i < sizeof(kIconSizes) / sizeof(uint32); ++i) {
1939 		uint32 iconSize = kIconSizes[i];
1940 		message = new BMessage(kIconMode);
1941 		message->AddInt32("size", iconSize);
1942 		BString label;
1943 		label.SetToFormat(B_TRANSLATE_COMMENT("%" B_PRId32" × %" B_PRId32,
1944 			"The '×' is the Unicode multiplication sign U+00D7"),
1945 			iconSize, iconSize);
1946 		item = new BMenuItem(label, message);
1947 		item->SetTarget(PoseView());
1948 		iconSizeMenu->AddItem(item);
1949 	}
1950 
1951 	iconSizeMenu->AddSeparatorItem();
1952 
1953 	message = new BMessage(kIconMode);
1954 	message->AddInt32("scale", 0);
1955 	item = new BMenuItem(B_TRANSLATE("Decrease size"), message, '-');
1956 	item->SetTarget(PoseView());
1957 	iconSizeMenu->AddItem(item);
1958 
1959 	message = new BMessage(kIconMode);
1960 	message->AddInt32("scale", 1);
1961 	item = new BMenuItem(B_TRANSLATE("Increase size"), message, '+');
1962 	item->SetTarget(PoseView());
1963 	iconSizeMenu->AddItem(item);
1964 
1965 	// A sub menu where the super item can be invoked.
1966 	menu->AddItem(iconSizeMenu);
1967 	iconSizeMenu->Superitem()->SetShortcut('1', B_COMMAND_KEY);
1968 	iconSizeMenu->Superitem()->SetMessage(new BMessage(kIconMode));
1969 	iconSizeMenu->Superitem()->SetTarget(PoseView());
1970 
1971 	item = new BMenuItem(B_TRANSLATE("Mini icon view"),
1972 		new BMessage(kMiniIconMode), '2');
1973 	item->SetTarget(PoseView());
1974 	menu->AddItem(item);
1975 
1976 	item = new BMenuItem(B_TRANSLATE("List view"),
1977 		new BMessage(kListMode), '3');
1978 	item->SetTarget(PoseView());
1979 	menu->AddItem(item);
1980 
1981 	menu->AddSeparatorItem();
1982 
1983 	item = new BMenuItem(B_TRANSLATE("Resize to fit"),
1984 		new BMessage(kResizeToFit), 'Y');
1985 	item->SetTarget(this);
1986 	menu->AddItem(item);
1987 
1988 	fArrangeByMenu = new BMenu(B_TRANSLATE("Arrange by"));
1989 	menu->AddItem(fArrangeByMenu);
1990 
1991 	item = new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS),
1992 		new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY);
1993 	item->SetTarget(PoseView());
1994 	menu->AddItem(item);
1995 
1996 	item = new BMenuItem(B_TRANSLATE("Select all"),
1997 		new BMessage(B_SELECT_ALL), 'A');
1998 	item->SetTarget(this);
1999 	menu->AddItem(item);
2000 
2001 	item = new BMenuItem(B_TRANSLATE("Invert selection"),
2002 		new BMessage(kInvertSelection), 'S');
2003 	item->SetTarget(PoseView());
2004 	menu->AddItem(item);
2005 
2006 	if (!TargetModel()->IsTrash()) {
2007 		item = new BMenuItem(B_TRANSLATE("Open parent"),
2008 			new BMessage(kOpenParentDir), B_UP_ARROW);
2009 		item->SetTarget(PoseView());
2010 		menu->AddItem(item);
2011 	}
2012 
2013 	item = new BMenuItem(B_TRANSLATE("Close"),
2014 		new BMessage(B_QUIT_REQUESTED), 'W');
2015 	item->SetTarget(this);
2016 	menu->AddItem(item);
2017 
2018 	item = new BMenuItem(B_TRANSLATE("Close all in workspace"),
2019 		new BMessage(kCloseAllInWorkspace), 'Q');
2020 	item->SetTarget(be_app);
2021 	menu->AddItem(item);
2022 
2023 	menu->AddSeparatorItem();
2024 
2025 	item = new BMenuItem(B_TRANSLATE("Preferences" B_UTF8_ELLIPSIS),
2026 		new BMessage(kShowSettingsWindow), ',');
2027 	item->SetTarget(be_app);
2028 	menu->AddItem(item);
2029 }
2030 
2031 
2032 void
2033 BContainerWindow::AddShortcuts()
2034 {
2035 	// add equivalents of the menu shortcuts to the menuless desktop window
2036 	ASSERT(!TargetModel()->IsTrash());
2037 	ASSERT(!PoseView()->IsFilePanel());
2038 	ASSERT(!TargetModel()->IsQuery());
2039 	ASSERT(!TargetModel()->IsVirtualDirectory());
2040 
2041 	AddShortcut('X', B_COMMAND_KEY | B_SHIFT_KEY,
2042 		new BMessage(kCutMoreSelectionToClipboard), this);
2043 	AddShortcut('C', B_COMMAND_KEY | B_SHIFT_KEY,
2044 		new BMessage(kCopyMoreSelectionToClipboard), this);
2045 	AddShortcut('F', B_COMMAND_KEY,
2046 		new BMessage(kFindButton), PoseView());
2047 	AddShortcut('N', B_COMMAND_KEY,
2048 		new BMessage(kNewFolder), PoseView());
2049 	AddShortcut('O', B_COMMAND_KEY,
2050 		new BMessage(kOpenSelection), PoseView());
2051 	AddShortcut('I', B_COMMAND_KEY,
2052 		new BMessage(kGetInfo), PoseView());
2053 	AddShortcut('E', B_COMMAND_KEY,
2054 		new BMessage(kEditItem), PoseView());
2055 	AddShortcut('D', B_COMMAND_KEY,
2056 		new BMessage(kDuplicateSelection), PoseView());
2057 	AddShortcut('T', B_COMMAND_KEY,
2058 		new BMessage(kMoveSelectionToTrash), PoseView());
2059 	AddShortcut('K', B_COMMAND_KEY,
2060 		new BMessage(kCleanup), PoseView());
2061 	AddShortcut('A', B_COMMAND_KEY,
2062 		new BMessage(B_SELECT_ALL), PoseView());
2063 	AddShortcut('S', B_COMMAND_KEY,
2064 		new BMessage(kInvertSelection), PoseView());
2065 	AddShortcut('A', B_COMMAND_KEY | B_SHIFT_KEY,
2066 		new BMessage(kShowSelectionWindow), PoseView());
2067 	AddShortcut('G', B_COMMAND_KEY,
2068 		new BMessage(kEditQuery), PoseView());
2069 		// it is ok to add a global Edit query shortcut here, PoseView will
2070 		// filter out cases where selected pose is not a query
2071 	AddShortcut('U', B_COMMAND_KEY,
2072 		new BMessage(kUnmountVolume), PoseView());
2073 	AddShortcut(B_UP_ARROW, B_COMMAND_KEY,
2074 		new BMessage(kOpenParentDir), PoseView());
2075 	AddShortcut('O', B_COMMAND_KEY | B_CONTROL_KEY,
2076 		new BMessage(kOpenSelectionWith), PoseView());
2077 
2078 	BMessage* decreaseSize = new BMessage(kIconMode);
2079 	decreaseSize->AddInt32("scale", 0);
2080 	AddShortcut('-', B_COMMAND_KEY, decreaseSize, PoseView());
2081 
2082 	BMessage* increaseSize = new BMessage(kIconMode);
2083 	increaseSize->AddInt32("scale", 1);
2084 	AddShortcut('+', B_COMMAND_KEY, increaseSize, PoseView());
2085 }
2086 
2087 
2088 void
2089 BContainerWindow::MenusBeginning()
2090 {
2091 	if (fMenuBar == NULL)
2092 		return;
2093 
2094 	if (CurrentMessage() != NULL && CurrentMessage()->what == B_MOUSE_DOWN) {
2095 		// don't commit active pose if only a keyboard shortcut is
2096 		// invoked - this would prevent Cut/Copy/Paste from working
2097 		PoseView()->CommitActivePose();
2098 	}
2099 
2100 	// File menu
2101 	int32 selectCount = PoseView()->SelectionList()->CountItems();
2102 
2103 	SetupOpenWithMenu(fFileMenu);
2104 	SetupMoveCopyMenus(selectCount
2105 		? PoseView()->SelectionList()->FirstItem()->TargetModel()->EntryRef()
2106 		: NULL, fFileMenu);
2107 
2108 	if (TargetModel()->IsRoot()) {
2109 		BVolume boot;
2110 		BVolumeRoster().GetBootVolume(&boot);
2111 
2112 		bool ejectableVolumeSelected = false;
2113 		for (int32 index = 0; index < selectCount; index++) {
2114 			Model* model
2115 				= PoseView()->SelectionList()->ItemAt(index)->TargetModel();
2116 			if (model->IsVolume()) {
2117 				BVolume volume;
2118 				volume.SetTo(model->NodeRef()->device);
2119 				if (volume != boot) {
2120 					ejectableVolumeSelected = true;
2121 					break;
2122 				}
2123 			}
2124 		}
2125 		BMenuItem* item = fMenuBar->FindItem(kUnmountVolume);
2126 		if (item != NULL)
2127 			item->SetEnabled(ejectableVolumeSelected);
2128 	}
2129 
2130 	UpdateMenu(fMenuBar, kMenuBarContext);
2131 
2132 	AddMimeTypesToMenu(fAttrMenu);
2133 
2134 	if (TargetModel()->IsPrintersDir())
2135 		EnableNamedMenuItem(fFileMenu, kMakeActivePrinter, selectCount == 1);
2136 }
2137 
2138 
2139 void
2140 BContainerWindow::MenusEnded()
2141 {
2142 	// when we're done we want to clear nav menus for next time
2143 	DeleteSubmenu(fNavigationItem);
2144 	DeleteSubmenu(fMoveToItem);
2145 	DeleteSubmenu(fCopyToItem);
2146 	DeleteSubmenu(fCreateLinkItem);
2147 	DeleteSubmenu(fOpenWithItem);
2148 }
2149 
2150 
2151 void
2152 BContainerWindow::SetupNavigationMenu(const entry_ref* ref, BMenu* parent)
2153 {
2154 	// start by removing nav item (and separator) from old menu
2155 	if (fNavigationItem != NULL) {
2156 		BMenu* menu = fNavigationItem->Menu();
2157 		if (menu != NULL) {
2158 			menu->RemoveItem(fNavigationItem);
2159 			BMenuItem* item = menu->RemoveItem((int32)0);
2160 			ASSERT(item != fNavigationItem);
2161 			delete item;
2162 		}
2163 	}
2164 
2165 	// if we weren't passed a ref then we're navigating this window
2166 	if (ref == NULL)
2167 		ref = TargetModel()->EntryRef();
2168 
2169 	BEntry entry;
2170 	if (entry.SetTo(ref) != B_OK)
2171 		return;
2172 
2173 	// only navigate directories and queries (check for symlink here)
2174 	Model model(&entry);
2175 	entry_ref resolvedRef;
2176 
2177 	if (model.InitCheck() != B_OK)
2178 		return;
2179 	else if (!model.IsContainer() && !model.IsSymLink())
2180 		return;
2181 
2182 	if (model.IsSymLink()) {
2183 		if (entry.SetTo(model.EntryRef(), true) != B_OK)
2184 			return;
2185 
2186 		Model resolvedModel(&entry);
2187 		if (resolvedModel.InitCheck() != B_OK || !resolvedModel.IsContainer())
2188 			return;
2189 
2190 		entry.GetRef(&resolvedRef);
2191 		ref = &resolvedRef;
2192 	}
2193 
2194 	if (fNavigationItem == NULL) {
2195 		fNavigationItem = new ModelMenuItem(&model,
2196 			new BNavMenu(model.Name(), B_REFS_RECEIVED, be_app, this));
2197 	}
2198 
2199 	// setup a navigation menu item which will dynamically load items
2200 	// as menu items are traversed
2201 	BNavMenu* navMenu = dynamic_cast<BNavMenu*>(fNavigationItem->Submenu());
2202 	navMenu->SetNavDir(ref);
2203 	fNavigationItem->SetLabel(model.Name());
2204 	fNavigationItem->SetEntry(&entry);
2205 
2206 	parent->AddItem(fNavigationItem, 0);
2207 	parent->AddItem(new BSeparatorItem(), 1);
2208 
2209 	BMessage* message = new BMessage(B_REFS_RECEIVED);
2210 	message->AddRef("refs", ref);
2211 	fNavigationItem->SetMessage(message);
2212 	fNavigationItem->SetTarget(be_app);
2213 
2214 	if (!Dragging())
2215 		parent->SetTrackingHook(NULL, NULL);
2216 }
2217 
2218 
2219 void
2220 BContainerWindow::SetupEditQueryItem(BMenu* menu)
2221 {
2222 	ASSERT(menu);
2223 	// File menu
2224 	int32 selectCount = PoseView()->CountSelected();
2225 
2226 	// add Edit query if appropriate
2227 	bool queryInSelection = false;
2228 	if (selectCount && selectCount < 100) {
2229 		// only do this for a limited number of selected poses
2230 
2231 		// if any queries selected, add an edit query menu item
2232 		for (int32 index = 0; index < selectCount; index++) {
2233 			BPose* pose = PoseView()->SelectionList()->ItemAt(index);
2234 			Model model(pose->TargetModel()->EntryRef(), true);
2235 			if (model.InitCheck() != B_OK)
2236 				continue;
2237 
2238 			if (model.IsQuery() || model.IsQueryTemplate()) {
2239 				queryInSelection = true;
2240 				break;
2241 			}
2242 		}
2243 	}
2244 
2245 	bool poseViewIsQuery = TargetModel()->IsQuery();
2246 		// if the view is a query pose view, add edit query menu item
2247 
2248 	BMenuItem* item = menu->FindItem(kEditQuery);
2249 	if (!poseViewIsQuery && !queryInSelection && item != NULL)
2250 		item->Menu()->RemoveItem(item);
2251 	else if ((poseViewIsQuery || queryInSelection) && item == NULL) {
2252 		// add edit query item after Open
2253 		item = menu->FindItem(kOpenSelection);
2254 		if (item) {
2255 			int32 itemIndex = item->Menu()->IndexOf(item);
2256 			BMenuItem* query = new BMenuItem(B_TRANSLATE("Edit query"),
2257 				new BMessage(kEditQuery), 'G');
2258 			item->Menu()->AddItem(query, itemIndex + 1);
2259 			query->SetTarget(PoseView());
2260 		}
2261 	}
2262 }
2263 
2264 
2265 void
2266 BContainerWindow::SetupOpenWithMenu(BMenu* parent)
2267 {
2268 	// start by removing nav item (and separator) from old menu
2269 	if (fOpenWithItem) {
2270 		BMenu* menu = fOpenWithItem->Menu();
2271 		if (menu != NULL)
2272 			menu->RemoveItem(fOpenWithItem);
2273 
2274 		delete fOpenWithItem;
2275 		fOpenWithItem = 0;
2276 	}
2277 
2278 	int32 selectCount = PoseView()->CountSelected();
2279 	if (selectCount <= 0) {
2280 		// no selection, nothing to open
2281 		return;
2282 	}
2283 
2284 	if (TargetModel()->IsRoot()) {
2285 		// don't add ourselves if we are root
2286 		return;
2287 	}
2288 
2289 	// ToDo:
2290 	// check if only item in selection list is the root
2291 	// and do not add if true
2292 
2293 	// add after "Open"
2294 	BMenuItem* item = parent->FindItem(kOpenSelection);
2295 
2296 	// build a list of all refs to open
2297 	BMessage message(B_REFS_RECEIVED);
2298 	for (int32 index = 0; index < selectCount; index++) {
2299 		BPose* pose = PoseView()->SelectionList()->ItemAt(index);
2300 		message.AddRef("refs", pose->TargetModel()->EntryRef());
2301 	}
2302 
2303 	// add Tracker token so that refs received recipients can script us
2304 	message.AddMessenger("TrackerViewToken", BMessenger(PoseView()));
2305 
2306 	int32 index = item->Menu()->IndexOf(item);
2307 	fOpenWithItem = new BMenuItem(
2308 		new OpenWithMenu(B_TRANSLATE("Open with" B_UTF8_ELLIPSIS),
2309 			&message, this, be_app), new BMessage(kOpenSelectionWith));
2310 	fOpenWithItem->SetTarget(PoseView());
2311 	fOpenWithItem->SetShortcut('O', B_COMMAND_KEY | B_CONTROL_KEY);
2312 
2313 	item->Menu()->AddItem(fOpenWithItem, index + 1);
2314 }
2315 
2316 
2317 void
2318 BContainerWindow::PopulateMoveCopyNavMenu(BNavMenu* navMenu, uint32 what,
2319 	const entry_ref* ref, bool addLocalOnly)
2320 {
2321 	BVolume volume;
2322 	BVolumeRoster volumeRoster;
2323 	BDirectory directory;
2324 	BEntry entry;
2325 	BPath path;
2326 	Model model;
2327 	dev_t device = ref->device;
2328 
2329 	int32 volumeCount = 0;
2330 
2331 	navMenu->RemoveItems(0, navMenu->CountItems(), true);
2332 
2333 	// count persistent writable volumes
2334 	volumeRoster.Rewind();
2335 	while (volumeRoster.GetNextVolume(&volume) == B_OK) {
2336 		if (!volume.IsReadOnly() && volume.IsPersistent())
2337 			volumeCount++;
2338 	}
2339 
2340 	// add the current folder
2341 	if (entry.SetTo(ref) == B_OK
2342 		&& entry.GetParent(&entry) == B_OK
2343 		&& model.SetTo(&entry) == B_OK) {
2344 		BNavMenu* menu = new BNavMenu(B_TRANSLATE("Current folder"), what,
2345 			this);
2346 		menu->SetNavDir(model.EntryRef());
2347 		menu->SetShowParent(true);
2348 
2349 		BMenuItem* item = new SpecialModelMenuItem(&model, menu);
2350 		item->SetMessage(new BMessage((uint32)what));
2351 
2352 		navMenu->AddItem(item);
2353 	}
2354 
2355 	// add the recent folder menu
2356 	// the "Tracker" settings directory is only used to get its icon
2357 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
2358 		path.Append("Tracker");
2359 		if (entry.SetTo(path.Path()) == B_OK
2360 			&& model.SetTo(&entry) == B_OK) {
2361 			BMenu* menu = new RecentsMenu(B_TRANSLATE("Recent folders"),
2362 				kRecentFolders, what, this);
2363 
2364 			BMenuItem* item = new SpecialModelMenuItem(&model, menu);
2365 			item->SetMessage(new BMessage((uint32)what));
2366 
2367 			navMenu->AddItem(item);
2368 		}
2369 	}
2370 
2371 	// add Desktop
2372 	FSGetBootDeskDir(&directory);
2373 	if (directory.InitCheck() == B_OK && directory.GetEntry(&entry) == B_OK
2374 		&& model.SetTo(&entry) == B_OK) {
2375 		navMenu->AddNavDir(&model, what, this, true);
2376 			// ask NavMenu to populate submenu for us
2377 	}
2378 
2379 	// add the home dir
2380 	if (find_directory(B_USER_DIRECTORY, &path) == B_OK
2381 		&& entry.SetTo(path.Path()) == B_OK && model.SetTo(&entry) == B_OK) {
2382 		navMenu->AddNavDir(&model, what, this, true);
2383 	}
2384 
2385 	navMenu->AddSeparatorItem();
2386 
2387 	// either add all mounted volumes (for copy), or all the top-level
2388 	// directories from the same device (for move)
2389 	// ToDo: can be changed if cross-device moves are implemented
2390 
2391 	if (addLocalOnly || volumeCount < 2) {
2392 		// add volume this item lives on
2393 		if (volume.SetTo(device) == B_OK
2394 			&& volume.GetRootDirectory(&directory) == B_OK
2395 			&& directory.GetEntry(&entry) == B_OK
2396 			&& model.SetTo(&entry) == B_OK) {
2397 			navMenu->AddNavDir(&model, what, this, false);
2398 				// do not have submenu populated
2399 
2400 			navMenu->SetNavDir(model.EntryRef());
2401 		}
2402 	} else {
2403 		// add all persistent writable volumes
2404 		volumeRoster.Rewind();
2405 		while (volumeRoster.GetNextVolume(&volume) == B_OK) {
2406 			if (volume.IsReadOnly() || !volume.IsPersistent())
2407 				continue;
2408 
2409 			// add root dir
2410 			if (volume.GetRootDirectory(&directory) == B_OK
2411 				&& directory.GetEntry(&entry) == B_OK
2412 				&& model.SetTo(&entry) == B_OK) {
2413 				navMenu->AddNavDir(&model, what, this, true);
2414 					// ask NavMenu to populate submenu for us
2415 			}
2416 		}
2417 	}
2418 }
2419 
2420 
2421 void
2422 BContainerWindow::SetupMoveCopyMenus(const entry_ref* item_ref, BMenu* parent)
2423 {
2424 	if (TargetModel()->IsTrash() || TargetModel()->InTrash() || TargetModel()->IsPrintersDir()
2425 		|| fMoveToItem == NULL || fCopyToItem == NULL || fCreateLinkItem == NULL
2426 		|| TargetModel()->IsRoot()) {
2427 		return;
2428 	}
2429 
2430 	// re-parent items to this menu since they're shared
2431 	BMenuItem* trash = parent->FindItem(kMoveSelectionToTrash);
2432 	int32 index = trash != NULL ? parent->IndexOf(trash) + 2 : 0;
2433 
2434 	if (fMoveToItem->Menu() != parent) {
2435 		if (fMoveToItem->Menu() != NULL)
2436 			fMoveToItem->Menu()->RemoveItem(fMoveToItem);
2437 
2438 		parent->AddItem(fMoveToItem, index++);
2439 	}
2440 
2441 	if (fCopyToItem->Menu() != parent) {
2442 		if (fCopyToItem->Menu() != NULL)
2443 			fCopyToItem->Menu()->RemoveItem(fCopyToItem);
2444 
2445 		parent->AddItem(fCopyToItem, index++);
2446 	}
2447 
2448 	if (fCreateLinkItem->Menu() != parent) {
2449 		if (fCreateLinkItem->Menu() != NULL)
2450 			fCreateLinkItem->Menu()->RemoveItem(fCreateLinkItem);
2451 
2452 		parent->AddItem(fCreateLinkItem, index);
2453 	}
2454 
2455 	// Set the "Create Link" item label here so it
2456 	// appears correctly when menus are disabled, too.
2457 	if ((modifiers() & B_SHIFT_KEY) != 0)
2458 		fCreateLinkItem->SetLabel(B_TRANSLATE("Create relative link"));
2459 	else
2460 		fCreateLinkItem->SetLabel(B_TRANSLATE("Create link"));
2461 
2462 	// only enable once the menus are built
2463 	fMoveToItem->SetEnabled(false);
2464 	fCopyToItem->SetEnabled(false);
2465 	fCreateLinkItem->SetEnabled(false);
2466 
2467 	// get ref for item which is selected
2468 	BEntry entry;
2469 	if (entry.SetTo(item_ref) != B_OK)
2470 		return;
2471 
2472 	Model tempModel(&entry);
2473 	if (tempModel.InitCheck() != B_OK)
2474 		return;
2475 
2476 	if (tempModel.IsRoot() || tempModel.IsVolume())
2477 		return;
2478 
2479 	// configure "Move to" menu item
2480 	PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>(fMoveToItem->Submenu()),
2481 		kMoveSelectionTo, item_ref, true);
2482 
2483 	// configure "Copy to" menu item
2484 	// add all mounted volumes (except the one this item lives on)
2485 	PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>(fCopyToItem->Submenu()),
2486 		kCopySelectionTo, item_ref, false);
2487 
2488 	// Set "Create Link" menu item message and
2489 	// add all mounted volumes (except the one this item lives on)
2490 	if ((modifiers() & B_SHIFT_KEY) != 0) {
2491 		fCreateLinkItem->SetMessage(new BMessage(kCreateRelativeLink));
2492 		PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>
2493 				(fCreateLinkItem->Submenu()),
2494 			kCreateRelativeLink, item_ref, false);
2495 	} else {
2496 		fCreateLinkItem->SetMessage(new BMessage(kCreateLink));
2497 		PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>
2498 				(fCreateLinkItem->Submenu()),
2499 			kCreateLink, item_ref, false);
2500 	}
2501 
2502 	fMoveToItem->SetEnabled(PoseView()->CountSelected() > 0
2503 		&& !PoseView()->SelectedVolumeIsReadOnly());
2504 	fCopyToItem->SetEnabled(PoseView()->CountSelected() > 0);
2505 	fCreateLinkItem->SetEnabled(PoseView()->CountSelected() > 0);
2506 
2507 	// Set the "Identify" item label
2508 	BMenuItem* identifyItem = parent->FindItem(kIdentifyEntry);
2509 	if (identifyItem != NULL) {
2510 		if ((modifiers() & B_SHIFT_KEY) != 0) {
2511 			identifyItem->SetLabel(B_TRANSLATE("Force identify"));
2512 			identifyItem->Message()->ReplaceBool("force", true);
2513 		} else {
2514 			identifyItem->SetLabel(B_TRANSLATE("Identify"));
2515 			identifyItem->Message()->ReplaceBool("force", false);
2516 		}
2517 	}
2518 }
2519 
2520 
2521 uint32
2522 BContainerWindow::ShowDropContextMenu(BPoint where, BPoseView* source)
2523 {
2524 	BPoint global(where);
2525 
2526 	PoseView()->ConvertToScreen(&global);
2527 	PoseView()->CommitActivePose();
2528 
2529 	// Change the "Create Link" item - allow user to
2530 	// create relative links with the Shift key down.
2531 	BMenuItem* item = fDropContextMenu->FindItem(kCreateLink);
2532 	if (item == NULL)
2533 		item = fDropContextMenu->FindItem(kCreateRelativeLink);
2534 
2535 	if (item != NULL && (modifiers() & B_SHIFT_KEY) != 0) {
2536 		item->SetLabel(B_TRANSLATE("Create relative link here"));
2537 		item->SetMessage(new BMessage(kCreateRelativeLink));
2538 	} else if (item != NULL) {
2539 		item->SetLabel(B_TRANSLATE("Create link here"));
2540 		item->SetMessage(new BMessage(kCreateLink));
2541 	}
2542 
2543 	int32 itemCount = fDropContextMenu->CountItems();
2544 	for(int32 i = 0; i < itemCount - 2; i++) {
2545 		// separator item and Cancel item are skipped
2546 		item = fDropContextMenu->ItemAt(i);
2547 		if (item == NULL)
2548 			break;
2549 
2550 		if (item->Command() == kMoveSelectionTo && source != NULL) {
2551 			item->SetEnabled(!source->SelectedVolumeIsReadOnly()
2552 				&& !PoseView()->TargetVolumeIsReadOnly());
2553 		} else
2554 			item->SetEnabled(!PoseView()->TargetVolumeIsReadOnly());
2555 	}
2556 
2557 	item = fDropContextMenu->Go(global, true, true);
2558 	if (item != NULL)
2559 		return item->Command();
2560 
2561 	return 0;
2562 }
2563 
2564 
2565 void
2566 BContainerWindow::ShowContextMenu(BPoint where, const entry_ref* ref)
2567 {
2568 	ASSERT(IsLocked());
2569 	BPoint global(where);
2570 	PoseView()->ConvertToScreen(&global);
2571 	PoseView()->CommitActivePose();
2572 
2573 	if (ref != NULL) {
2574 		// clicked on a pose, show file or volume context menu
2575 		Model model(ref);
2576 
2577 		if (model.IsTrash()) {
2578 			if (fTrashContextMenu->Window() || Dragging())
2579 				return;
2580 
2581 			DeleteSubmenu(fNavigationItem);
2582 
2583 			// selected item was trash, show the trash context menu instead
2584 
2585 			EnableNamedMenuItem(fTrashContextMenu, kEmptyTrash,
2586 				static_cast<TTracker*>(be_app)->TrashFull());
2587 
2588 			SetupNavigationMenu(ref, fTrashContextMenu);
2589 
2590 			fContextMenu = fTrashContextMenu;
2591 		} else {
2592 			bool showAsVolume = false;
2593 			bool isFilePanel = PoseView()->IsFilePanel();
2594 
2595 			if (Dragging()) {
2596 				fContextMenu = NULL;
2597 
2598 				BEntry entry;
2599 				model.GetEntry(&entry);
2600 
2601 				// only show for directories (directory, volume, root)
2602 				//
2603 				// don't show a popup for the trash or printers
2604 				// trash is handled in DeskWindow
2605 				//
2606 				// since this menu is opened asynchronously
2607 				// we need to make sure we don't open it more
2608 				// than once, the IsShowing flag is set in
2609 				// SlowContextPopup::AttachedToWindow and
2610 				// reset in DetachedFromWindow
2611 				// see the notes in SlowContextPopup::AttachedToWindow
2612 
2613 				if (!FSIsPrintersDir(&entry)
2614 					&& !fDragContextMenu->IsShowing()) {
2615 					//printf("ShowContextMenu - target is %s %i\n",
2616 					//	ref->name, IsShowing(ref));
2617 					fDragContextMenu->ClearMenu();
2618 
2619 					// in case the ref is a symlink, resolve it
2620 					// only pop open for directories
2621 					BEntry resolvedEntry(ref, true);
2622 					if (!resolvedEntry.IsDirectory())
2623 						return;
2624 
2625 					entry_ref resolvedRef;
2626 					resolvedEntry.GetRef(&resolvedRef);
2627 
2628 					// use the resolved ref for the menu
2629 					fDragContextMenu->SetNavDir(&resolvedRef);
2630 					fDragContextMenu->SetTypesList(fCachedTypesList);
2631 					fDragContextMenu->SetTarget(BMessenger(this));
2632 					BPoseView* poseView = PoseView();
2633 					if (poseView != NULL) {
2634 						BMessenger target(poseView);
2635 						fDragContextMenu->InitTrackingHook(
2636 							&BPoseView::MenuTrackingHook, &target,
2637 							fDragMessage);
2638 					}
2639 
2640 					// this is now asynchronous so that we don't
2641 					// deadlock in Window::Quit,
2642 					fDragContextMenu->Go(global);
2643 				}
2644 
2645 				return;
2646 			} else if (model.IsRoot() || model.IsVolume()) {
2647 				fContextMenu = fVolumeContextMenu;
2648 				showAsVolume = true;
2649 			} else {
2650 				fContextMenu = fFileContextMenu;
2651 			}
2652 
2653 			if (fContextMenu == NULL)
2654 				return;
2655 
2656 			// bail out before cleanup if popup window is already open
2657 			if (fContextMenu->Window() != NULL)
2658 				return;
2659 
2660 			// clean up items from last context menu
2661 			MenusEnded();
2662 
2663 			if (fContextMenu == fFileContextMenu) {
2664 				// Add all mounted volumes (except the one this item lives on.)
2665 				BNavMenu* navMenu = dynamic_cast<BNavMenu*>(
2666 					fCreateLinkItem->Submenu());
2667 				PopulateMoveCopyNavMenu(navMenu,
2668 				fCreateLinkItem->Message()->what, ref, false);
2669 			} else if (showAsVolume) {
2670 				// non-volume enable/disable copy, move, identify
2671 				EnableNamedMenuItem(fContextMenu, kDuplicateSelection, false);
2672 				EnableNamedMenuItem(fContextMenu, kMoveSelectionToTrash, false);
2673 				EnableNamedMenuItem(fContextMenu, kIdentifyEntry, false);
2674 
2675 				// volume model, enable/disable the Unmount item
2676 				bool ejectableVolumeSelected = false;
2677 
2678 				BVolume boot;
2679 				BVolumeRoster().GetBootVolume(&boot);
2680 				BVolume volume;
2681 				volume.SetTo(model.NodeRef()->device);
2682 				if (volume != boot)
2683 					ejectableVolumeSelected = true;
2684 
2685 				EnableNamedMenuItem(fContextMenu,
2686 					B_TRANSLATE("Unmount"), ejectableVolumeSelected);
2687 			}
2688 
2689 			SetupNavigationMenu(ref, fContextMenu);
2690 			if (!showAsVolume && !isFilePanel) {
2691 				SetupMoveCopyMenus(ref, fContextMenu);
2692 				SetupOpenWithMenu(fContextMenu);
2693 			}
2694 
2695 			UpdateMenu(fContextMenu, kPosePopUpContext);
2696 		}
2697 	} else if (fWindowContextMenu != NULL) {
2698 		// clicked on a window, show window context menu
2699 		fContextMenu = fWindowContextMenu;
2700 
2701 		// bail out before cleanup if popup window is already open
2702 		if (fContextMenu->Window() != NULL)
2703 			return;
2704 
2705 		// clean up items from last context menu
2706 		MenusEnded();
2707 
2708 		// setup nav menu
2709 		SetupNavigationMenu(TargetModel()->EntryRef(), fContextMenu);
2710 
2711 		// update the rest
2712 		UpdateMenu(fContextMenu, kWindowPopUpContext);
2713 	}
2714 
2715 	// context menu invalid or popup window is already open
2716 	if (fContextMenu == NULL || fContextMenu->Window() != NULL)
2717 		return;
2718 
2719 	fContextMenu->Go(global, true, true, true);
2720 	fContextMenu = NULL;
2721 }
2722 
2723 
2724 void
2725 BContainerWindow::AddFileContextMenus(BMenu* menu)
2726 {
2727 	menu->AddItem(new BMenuItem(B_TRANSLATE("Open"),
2728 		new BMessage(kOpenSelection), 'O'));
2729 	menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
2730 		new BMessage(kGetInfo), 'I'));
2731 	menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"),
2732 		new BMessage(kEditItem), 'E'));
2733 
2734 	BMessage* message = new BMessage(kDuplicateSelection);
2735 	if (!(TargetModel()->IsTrash() || TargetModel()->InTrash() || TargetModel()->IsPrintersDir()))
2736 		menu->AddItem(new BMenuItem(B_TRANSLATE("Duplicate"), message, 'D'));
2737 
2738 	if (!(TargetModel()->IsTrash() || TargetModel()->InTrash())) {
2739 		message = new BMessage(kMoveSelectionToTrash);
2740 		menu->AddItem( new BMenuItem(B_TRANSLATE("Move to Trash"), message, 'T'));
2741 		if (!TargetModel()->IsPrintersDir()) {
2742 			// add separator for copy to/move to items (navigation items)
2743 			menu->AddSeparatorItem();
2744 		}
2745 	} else {
2746 		message = new BMessage(kDeleteSelection);
2747 		menu->AddItem(new BMenuItem(B_TRANSLATE("Delete"), message, 0));
2748 		message = new BMessage(kRestoreSelectionFromTrash);
2749 		menu->AddItem(new BMenuItem(B_TRANSLATE("Restore"), message, 0));
2750 	}
2751 
2752 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
2753 	menu->AddSeparatorItem();
2754 	BMenuItem* cutItem = new BMenuItem(B_TRANSLATE("Cut"),
2755 		new BMessage(B_CUT), 'X');
2756 	menu->AddItem(cutItem);
2757 	BMenuItem* copyItem = new BMenuItem(B_TRANSLATE("Copy"),
2758 		new BMessage(B_COPY), 'C');
2759 	menu->AddItem(copyItem);
2760 	BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"),
2761 		new BMessage(B_PASTE), 'V');
2762 	menu->AddItem(pasteItem);
2763 #endif
2764 	menu->AddSeparatorItem();
2765 
2766 	message = new BMessage(kIdentifyEntry);
2767 	message->AddBool("force", false);
2768 	menu->AddItem(new BMenuItem(B_TRANSLATE("Identify"), message));
2769 
2770 	BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons"));
2771 	menu->AddItem(addOnMenuItem);
2772 
2773 	// set targets as needed
2774 	menu->SetTargetForItems(PoseView());
2775 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
2776 	cutItem->SetTarget(this);
2777 	copyItem->SetTarget(this);
2778 	pasteItem->SetTarget(this);
2779 #endif
2780 }
2781 
2782 
2783 void
2784 BContainerWindow::AddVolumeContextMenus(BMenu* menu)
2785 {
2786 	menu->AddItem(new BMenuItem(B_TRANSLATE("Open"),
2787 		new BMessage(kOpenSelection), 'O'));
2788 	menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
2789 		new BMessage(kGetInfo), 'I'));
2790 	menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"),
2791 		new BMessage(kEditItem), 'E'));
2792 
2793 	menu->AddSeparatorItem();
2794 	menu->AddItem(new MountMenu(B_TRANSLATE("Mount")));
2795 
2796 	BMenuItem* item = new BMenuItem(B_TRANSLATE("Unmount"),
2797 		new BMessage(kUnmountVolume), 'U');
2798 	item->SetEnabled(false);
2799 	menu->AddItem(item);
2800 	menu->AddSeparatorItem();
2801 
2802 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
2803 	menu->AddItem(new BMenuItem(B_TRANSLATE("Paste"),
2804 		new BMessage(B_PASTE), 'V'));
2805 	menu->AddSeparatorItem();
2806 #endif
2807 
2808 	menu->AddItem(new BMenu(B_TRANSLATE("Add-ons")));
2809 
2810 	menu->SetTargetForItems(PoseView());
2811 }
2812 
2813 
2814 void
2815 BContainerWindow::AddWindowContextMenus(BMenu* menu)
2816 {
2817 	// create context sensitive menu for empty area of window
2818 	// since we check view mode before display, this should be a radio
2819 	// mode menu
2820 
2821 	Model* targetModel = TargetModel();
2822 	ASSERT(targetModel != NULL);
2823 
2824 	bool needSeparator = true;
2825 	if (TargetModel()->IsTrash()) {
2826 		menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"),
2827 			new BMessage(kEmptyTrash)));
2828 	} else if (TargetModel()->IsPrintersDir()) {
2829 		menu->AddItem(new BMenuItem(B_TRANSLATE("Add printer" B_UTF8_ELLIPSIS),
2830 			new BMessage(kAddPrinter), 'N'));
2831 	} else if (targetModel->InTrash() || targetModel->IsRoot()) {
2832 		needSeparator = false;
2833 	} else if (!PoseView()->IsFilePanel()) {
2834 		TemplatesMenu* templatesMenu = new TemplatesMenu(PoseView(), B_TRANSLATE("New"));
2835 		menu->AddItem(templatesMenu);
2836 		templatesMenu->SetTargetForItems(PoseView());
2837 	} else {
2838 		menu->AddItem(new BMenuItem(B_TRANSLATE("New folder"), new BMessage(kNewFolder), 'N'));
2839 	}
2840 
2841 	if (needSeparator)
2842 		menu->AddSeparatorItem();
2843 
2844 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
2845 	BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"),
2846 		new BMessage(B_PASTE), 'V');
2847 	pasteItem->SetEnabled(FSClipboardHasRefs()
2848 		&& !PoseView()->TargetVolumeIsReadOnly());
2849 	menu->AddItem(pasteItem);
2850 	menu->AddSeparatorItem();
2851 #endif
2852 
2853 	BMenu* arrangeBy = new BMenu(B_TRANSLATE("Arrange by"));
2854 	PopulateArrangeByMenu(arrangeBy);
2855 	menu->AddItem(arrangeBy);
2856 
2857 	menu->AddItem(new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS),
2858 		new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY));
2859 	menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"),
2860 		new BMessage(B_SELECT_ALL), 'A'));
2861 	if (!TargetModel()->IsTrash()) {
2862 		menu->AddItem(new BMenuItem(B_TRANSLATE("Open parent"),
2863 			new BMessage(kOpenParentDir), B_UP_ARROW));
2864 	}
2865 
2866 	if (targetModel->IsRoot()) {
2867 		menu->AddSeparatorItem();
2868 		menu->AddItem(new MountMenu(B_TRANSLATE("Mount")));
2869 	}
2870 
2871 	menu->AddSeparatorItem();
2872 	BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons"));
2873 	menu->AddItem(addOnMenuItem);
2874 
2875 #if DEBUG
2876 	menu->AddSeparatorItem();
2877 	BMenuItem* testing = new BMenuItem("Test icon cache",
2878 		new BMessage(kTestIconCache));
2879 	menu->AddItem(testing);
2880 #endif
2881 
2882 	// target items as needed
2883 	menu->SetTargetForItems(PoseView());
2884 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
2885 	pasteItem->SetTarget(this);
2886 #endif
2887 }
2888 
2889 
2890 void
2891 BContainerWindow::AddDropContextMenus(BMenu* menu)
2892 {
2893 	menu->AddItem(new BMenuItem(B_TRANSLATE("Move here"),
2894 		new BMessage(kMoveSelectionTo)));
2895 	menu->AddItem(new BMenuItem(B_TRANSLATE("Copy here"),
2896 		new BMessage(kCopySelectionTo)));
2897 	menu->AddItem(new BMenuItem(B_TRANSLATE("Create link here"),
2898 		new BMessage(kCreateLink)));
2899 	menu->AddSeparatorItem();
2900 	menu->AddItem(new BMenuItem(B_TRANSLATE("Cancel"),
2901 		new BMessage(kCancelButton)));
2902 }
2903 
2904 
2905 void
2906 BContainerWindow::AddTrashContextMenus(BMenu* menu)
2907 {
2908 	// setup special trash context menu
2909 	menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"),
2910 		new BMessage(kEmptyTrash)));
2911 	menu->AddItem(new BMenuItem(B_TRANSLATE("Open"),
2912 		new BMessage(kOpenSelection), 'O'));
2913 	menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
2914 		new BMessage(kGetInfo), 'I'));
2915 	menu->SetTargetForItems(PoseView());
2916 }
2917 
2918 
2919 void
2920 BContainerWindow::EachAddOn(void (*eachAddOn)(const Model*, const char*,
2921 		uint32 shortcut, uint32 modifiers, bool primary, void* context,
2922 		BContainerWindow* window, BMenu* menu),
2923 	void* passThru, BStringList& mimeTypes, BMenu* menu)
2924 {
2925 	AutoLock<LockingList<AddOnShortcut> > lock(fAddOnsList);
2926 	if (!lock.IsLocked())
2927 		return;
2928 
2929 	for (int i = fAddOnsList->CountItems() - 1; i >= 0; i--) {
2930 		struct AddOnShortcut* item = fAddOnsList->ItemAt(i);
2931 		bool primary = false;
2932 
2933 		if (mimeTypes.CountStrings() > 0) {
2934 			BFile file(item->model->EntryRef(), B_READ_ONLY);
2935 			if (file.InitCheck() == B_OK) {
2936 				BAppFileInfo info(&file);
2937 				if (info.InitCheck() == B_OK) {
2938 					bool secondary = true;
2939 
2940 					// does this add-on has types set at all?
2941 					BMessage message;
2942 					if (info.GetSupportedTypes(&message) == B_OK) {
2943 						type_code typeCode;
2944 						int32 count;
2945 						if (message.GetInfo("types", &typeCode,
2946 								&count) == B_OK) {
2947 							secondary = false;
2948 						}
2949 					}
2950 
2951 					// check all supported types if it has some set
2952 					if (!secondary) {
2953 						for (int32 i = mimeTypes.CountStrings();
2954 								!primary && i-- > 0;) {
2955 							BString type = mimeTypes.StringAt(i);
2956 							if (info.IsSupportedType(type.String())) {
2957 								BMimeType mimeType(type.String());
2958 								if (info.Supports(&mimeType))
2959 									primary = true;
2960 								else
2961 									secondary = true;
2962 							}
2963 						}
2964 					}
2965 
2966 					if (!secondary && !primary)
2967 						continue;
2968 				}
2969 			}
2970 		}
2971 		((eachAddOn)(item->model, item->model->Name(), item->key,
2972 			item->modifiers, primary, passThru, this, menu));
2973 	}
2974 }
2975 
2976 
2977 void
2978 BContainerWindow::BuildMimeTypeList(BStringList& mimeTypes)
2979 {
2980 	int32 selectCount = PoseView()->CountSelected();
2981 	if (selectCount <= 0) {
2982 		// just add the type of the current directory
2983 		AddMimeTypeString(mimeTypes, TargetModel());
2984 	} else {
2985 		_UpdateSelectionMIMEInfo();
2986 		for (int32 index = 0; index < selectCount; index++) {
2987 			BPose* pose = PoseView()->SelectionList()->ItemAt(index);
2988 			AddMimeTypeString(mimeTypes, pose->TargetModel());
2989 			// If it's a symlink, resolves it and add the Target's MimeType
2990 			if (pose->TargetModel()->IsSymLink()) {
2991 				Model* resolved = new Model(
2992 					pose->TargetModel()->EntryRef(), true, true);
2993 				if (resolved->InitCheck() == B_OK)
2994 					AddMimeTypeString(mimeTypes, resolved);
2995 
2996 				delete resolved;
2997 			}
2998 		}
2999 	}
3000 }
3001 
3002 
3003 void
3004 BContainerWindow::BuildAddOnsMenu(BMenu* parentMenu)
3005 {
3006 	BMenuItem* item = parentMenu->FindItem(B_TRANSLATE("Add-ons"));
3007 	if (parentMenu->IndexOf(item) == 0) {
3008 		// the folder of the context menu seems to be named "Add-Ons"
3009 		// so we just take the last menu item, which is correct if not
3010 		// build with debug option
3011 		item = parentMenu->ItemAt(parentMenu->CountItems() - 1);
3012 	}
3013 	if (item == NULL)
3014 		return;
3015 
3016 	BFont font; {
3017 		AutoLock<BLooper> _(parentMenu->Looper());
3018 		parentMenu->GetFont(&font);
3019 	}
3020 
3021 	BMenu* menu = item->Submenu();
3022 	if (menu == NULL)
3023 		return;
3024 
3025 	menu->SetFont(&font);
3026 
3027 	// found add-ons menu, empty it first
3028 	for (;;) {
3029 		item = menu->RemoveItem((int32)0);
3030 		if (!item)
3031 			break;
3032 		delete item;
3033 	}
3034 
3035 	BObjectList<BMenuItem> primaryList;
3036 	BObjectList<BMenuItem> secondaryList;
3037 	BStringList mimeTypes(10);
3038 	BuildMimeTypeList(mimeTypes);
3039 
3040 	AddOneAddOnParams params;
3041 	params.primaryList = &primaryList;
3042 	params.secondaryList = &secondaryList;
3043 
3044 	// build a list of the MIME types of the selected items
3045 
3046 	EachAddOn(AddOneAddOn, &params, mimeTypes, parentMenu);
3047 
3048 	primaryList.SortItems(CompareLabels);
3049 	secondaryList.SortItems(CompareLabels);
3050 
3051 	int32 count = primaryList.CountItems();
3052 	for (int32 index = 0; index < count; index++)
3053 		menu->AddItem(primaryList.ItemAt(index));
3054 
3055 	if (count > 0)
3056 		menu->AddSeparatorItem();
3057 
3058 	count = secondaryList.CountItems();
3059 	for (int32 index = 0; index < count; index++)
3060 		menu->AddItem(secondaryList.ItemAt(index));
3061 
3062 	menu->SetTargetForItems(this);
3063 }
3064 
3065 
3066 void
3067 BContainerWindow::UpdateMenu(BMenu* menu, UpdateMenuContext context)
3068 {
3069 	const int32 selectCount = PoseView()->CountSelected();
3070 	const int32 poseCount = PoseView()->CountItems();
3071 
3072 	if (context == kMenuBarContext) {
3073 		EnableNamedMenuItem(menu, kOpenSelection, selectCount > 0);
3074 		EnableNamedMenuItem(menu, kIdentifyEntry, selectCount > 0);
3075 		EnableNamedMenuItem(menu, kRestoreFromTrash, selectCount > 0);
3076 		EnableNamedMenuItem(menu, kDeleteSelection, PoseView()->CanMoveToTrashOrDuplicate());
3077 	}
3078 
3079 	if (context == kMenuBarContext || context == kPosePopUpContext) {
3080 		SetupEditQueryItem(menu);
3081 
3082 		EnableNamedMenuItem(menu, kEditItem, PoseView()->CanEditName());
3083 		EnableNamedMenuItem(menu, kMoveSelectionToTrash, PoseView()->CanMoveToTrashOrDuplicate());
3084 		EnableNamedMenuItem(menu, kDuplicateSelection, PoseView()->CanMoveToTrashOrDuplicate());
3085 
3086 		SetCutItem(menu);
3087 		SetCopyItem(menu);
3088 		SetPasteItem(menu);
3089 	}
3090 
3091 	if (context == kMenuBarContext || context == kWindowPopUpContext) {
3092 		if (!PoseView()->IsFilePanel())
3093 			EnableNamedMenuItem(menu, B_TRANSLATE("New"), !PoseView()->TargetVolumeIsReadOnly());
3094 		else
3095 			EnableNamedMenuItem(menu, kNewFolder, !PoseView()->TargetVolumeIsReadOnly());
3096 
3097 		uint32 viewMode = PoseView()->ViewMode();
3098 
3099 		BMenu* iconSizeMenu = NULL;
3100 		if (BMenuItem* item = menu->FindItem(kIconMode))
3101 			iconSizeMenu = item->Submenu();
3102 
3103 		if (iconSizeMenu != NULL) {
3104 			if (viewMode == kIconMode) {
3105 				int32 iconSize = PoseView()->UnscaledIconSizeInt();
3106 				BMenuItem* item = iconSizeMenu->ItemAt(0);
3107 				for (int32 i = 0; (item = iconSizeMenu->ItemAt(i)) != NULL;
3108 						i++) {
3109 					BMessage* message = item->Message();
3110 					if (message == NULL) {
3111 						item->SetMarked(false);
3112 						continue;
3113 					}
3114 					int32 size;
3115 					if (message->FindInt32("size", &size) != B_OK)
3116 						size = -1;
3117 					item->SetMarked(iconSize == size);
3118 				}
3119 			} else {
3120 				BMenuItem* item;
3121 				for (int32 i = 0; (item = iconSizeMenu->ItemAt(i)) != NULL; i++)
3122 					item->SetMarked(false);
3123 			}
3124 		}
3125 
3126 		MarkNamedMenuItem(menu, kIconMode, viewMode == kIconMode);
3127 		MarkNamedMenuItem(menu, kListMode, viewMode == kListMode);
3128 		MarkNamedMenuItem(menu, kMiniIconMode, viewMode == kMiniIconMode);
3129 
3130 		SetCloseItem(menu);
3131 		SetArrangeMenu(menu);
3132 		SetPasteItem(menu);
3133 
3134 		EnableNamedMenuItem(menu, kOpenParentDir, !PoseView()->ParentIsRoot());
3135 		EnableNamedMenuItem(menu, kEmptyTrash, poseCount > 0);
3136 		EnableNamedMenuItem(menu, B_SELECT_ALL, poseCount > 0);
3137 
3138 		BMenuItem* item = menu->FindItem(B_TRANSLATE("New"));
3139 		if (item != NULL) {
3140 			TemplatesMenu* templatesMenu = dynamic_cast<TemplatesMenu*>(
3141 				item->Submenu());
3142 			if (templatesMenu != NULL)
3143 				templatesMenu->UpdateMenuState();
3144 		}
3145 	}
3146 
3147 	BuildAddOnsMenu(menu);
3148 }
3149 
3150 
3151 BMessage*
3152 BContainerWindow::AddOnMessage(int32 what)
3153 {
3154 	BMessage* message = new BMessage(what);
3155 
3156 	// add selected refs to message
3157 	BObjectList<BPose>* selectionList = PoseView()->SelectionList();
3158 
3159 	int32 index = 0;
3160 	BPose* pose;
3161 	while ((pose = selectionList->ItemAt(index++)) != NULL)
3162 		message->AddRef("refs", pose->TargetModel()->EntryRef());
3163 
3164 	message->AddRef("dir_ref", TargetModel()->EntryRef());
3165 	message->AddMessenger("TrackerViewToken", BMessenger(PoseView()));
3166 
3167 	return message;
3168 }
3169 
3170 
3171 void
3172 BContainerWindow::LoadAddOn(BMessage* message)
3173 {
3174 	UpdateIfNeeded();
3175 
3176 	entry_ref addOnRef;
3177 	status_t result = message->FindRef("refs", &addOnRef);
3178 	if (result != B_OK) {
3179 		BString buffer(B_TRANSLATE("Error %error loading add-On %name."));
3180 		buffer.ReplaceFirst("%error", strerror(result));
3181 		buffer.ReplaceFirst("%name", addOnRef.name);
3182 
3183 		BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"),
3184 			0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
3185 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
3186 		alert->Go();
3187 		return;
3188 	}
3189 
3190 	// add selected refs to message
3191 	BMessage* refs = AddOnMessage(B_REFS_RECEIVED);
3192 
3193 	LaunchInNewThread("Add-on", B_NORMAL_PRIORITY, &AddOnThread, refs,
3194 		addOnRef, *TargetModel()->EntryRef());
3195 }
3196 
3197 
3198 //	#pragma mark - BContainerWindow private methods
3199 
3200 
3201 void
3202 BContainerWindow::_UpdateSelectionMIMEInfo()
3203 {
3204 	BPose* pose;
3205 	int32 index = 0;
3206 	while ((pose = PoseView()->SelectionList()->ItemAt(index++)) != NULL) {
3207 		BString mimeType(pose->TargetModel()->MimeType());
3208 		if (!mimeType.Length() || mimeType.ICompare(B_FILE_MIMETYPE) == 0) {
3209 			pose->TargetModel()->Mimeset(true);
3210 			if (pose->TargetModel()->IsSymLink()) {
3211 				Model* resolved = new Model(pose->TargetModel()->EntryRef(),
3212 					true, true);
3213 				if (resolved->InitCheck() == B_OK) {
3214 					mimeType.SetTo(resolved->MimeType());
3215 					if (!mimeType.Length()
3216 						|| mimeType.ICompare(B_FILE_MIMETYPE) == 0) {
3217 						resolved->Mimeset(true);
3218 					}
3219 				}
3220 				delete resolved;
3221 			}
3222 		}
3223 	}
3224 }
3225 
3226 
3227 void
3228 BContainerWindow::_AddFolderIcon()
3229 {
3230 	if (fMenuBar == NULL) {
3231 		// We don't want to add the icon if there's no menubar
3232 		return;
3233 	}
3234 
3235 	float baseIconSize = be_control_look->ComposeIconSize(16).Height() + 1,
3236 		iconSize = fMenuBar->Bounds().Height() - 2;
3237 	if (iconSize < baseIconSize)
3238 		iconSize = baseIconSize;
3239 
3240 	fDraggableIcon = new(std::nothrow)
3241 		DraggableContainerIcon(BSize(iconSize - 1, iconSize - 1));
3242 	if (fDraggableIcon != NULL) {
3243 		fMenuContainer->GroupLayout()->AddView(fDraggableIcon);
3244 		fMenuBar->SetBorders(
3245 			BControlLook::B_ALL_BORDERS & ~BControlLook::B_RIGHT_BORDER);
3246 	}
3247 }
3248 
3249 
3250 void
3251 BContainerWindow::_PassMessageToAddOn(BMessage* message)
3252 {
3253 	LaunchInNewThread("Add-on-Pass-Message", B_NORMAL_PRIORITY,
3254 		&RunAddOnMessageThread, new BMessage(*message), (void*)NULL);
3255 }
3256 
3257 
3258 void
3259 BContainerWindow::_NewTemplateSubmenu(entry_ref dirRef)
3260 {
3261 	entry_ref submenuRef;
3262 	BPath path(&dirRef);
3263 	path.Append(B_TRANSLATE_COMMENT("New submenu", "Folder name of New-template submenu"));
3264 	get_ref_for_path(path.Path(), &submenuRef);
3265 
3266 	if (FSCreateNewFolder(&submenuRef) != B_OK)
3267 		return;
3268 
3269 	// kAttrTemplateSubMenu shows the folder to be a submenu
3270 	BNode node(&submenuRef);
3271 	if (node.InitCheck() != B_OK)
3272 		return;
3273 	bool flag = true;
3274 	node.WriteAttr(kAttrTemplateSubMenu, B_BOOL_TYPE, 0, &flag, sizeof(bool));
3275 
3276 	// show and select new submenu in Tracker
3277 	BEntry entry(&submenuRef);
3278 	node_ref nref;
3279 	if (entry.GetNodeRef(&nref) != B_OK)
3280 		return;
3281 
3282 	BMessage message(B_REFS_RECEIVED);
3283 	message.AddRef("refs", &dirRef);
3284 	message.AddData("nodeRefToSelect", B_RAW_TYPE, (void*)&nref, sizeof(node_ref));
3285 	be_app->PostMessage(&message);
3286 }
3287 
3288 
3289 BMenuItem*
3290 BContainerWindow::NewAttributeMenuItem(const char* label, const char* name,
3291 	int32 type, float width, int32 align, bool editable, bool statField)
3292 {
3293 	return NewAttributeMenuItem(label, name, type, NULL, width, align,
3294 		editable, statField);
3295 }
3296 
3297 
3298 BMenuItem*
3299 BContainerWindow::NewAttributeMenuItem(const char* label, const char* name,
3300 	int32 type, const char* displayAs, float width, int32 align,
3301 	bool editable, bool statField)
3302 {
3303 	BMessage* message = new BMessage(kAttributeItem);
3304 	message->AddString("attr_name", name);
3305 	message->AddInt32("attr_type", type);
3306 	message->AddInt32("attr_hash", (int32)AttrHashString(name, (uint32)type));
3307 	message->AddFloat("attr_width", width);
3308 	message->AddInt32("attr_align", align);
3309 	if (displayAs != NULL)
3310 		message->AddString("attr_display_as", displayAs);
3311 	message->AddBool("attr_editable", editable);
3312 	message->AddBool("attr_statfield", statField);
3313 
3314 	BMenuItem* menuItem = new BMenuItem(label, message);
3315 	menuItem->SetTarget(PoseView());
3316 
3317 	return menuItem;
3318 }
3319 
3320 
3321 void
3322 BContainerWindow::NewAttributesMenu(BMenu* menu)
3323 {
3324 	ASSERT(PoseView());
3325 
3326 	BMenuItem* item;
3327 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Copy layout"),
3328 		new BMessage(kCopyAttributes)));
3329 	item->SetTarget(PoseView());
3330 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Paste layout"),
3331 		new BMessage(kPasteAttributes)));
3332 	item->SetTarget(PoseView());
3333 	menu->AddSeparatorItem();
3334 
3335 	menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Name"),
3336 		kAttrStatName, B_STRING_TYPE, 145, B_ALIGN_LEFT, true, true));
3337 
3338 	if (gLocalizedNamePreferred) {
3339 		menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Real name"),
3340 			kAttrRealName, B_STRING_TYPE, 145, B_ALIGN_LEFT, true, true));
3341 	}
3342 
3343 	menu->AddItem(NewAttributeMenuItem (B_TRANSLATE("Size"), kAttrStatSize,
3344 		B_OFF_T_TYPE, 80, B_ALIGN_RIGHT, false, true));
3345 
3346 	menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Modified"),
3347 		kAttrStatModified, B_TIME_TYPE, 150, B_ALIGN_LEFT, false, true));
3348 
3349 	menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Created"),
3350 		kAttrStatCreated, B_TIME_TYPE, 150, B_ALIGN_LEFT, false, true));
3351 
3352 	menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Kind"),
3353 		kAttrMIMEType, B_MIME_STRING_TYPE, 145, B_ALIGN_LEFT, false, false));
3354 
3355 	if (TargetModel()->IsTrash() || TargetModel()->InTrash()) {
3356 		menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Original name"),
3357 			kAttrOriginalPath, B_STRING_TYPE, 225, B_ALIGN_LEFT, false,
3358 			false));
3359 	} else {
3360 		menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Location"), kAttrPath,
3361 			B_STRING_TYPE, 225, B_ALIGN_LEFT, false, false));
3362 	}
3363 
3364 #ifdef OWNER_GROUP_ATTRIBUTES
3365 	menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Owner"), kAttrStatOwner,
3366 		B_STRING_TYPE, 60, B_ALIGN_LEFT, false, true));
3367 
3368 	menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Group"), kAttrStatGroup,
3369 		B_STRING_TYPE, 60, B_ALIGN_LEFT, false, true));
3370 #endif
3371 
3372 	menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Permissions"),
3373 		kAttrStatMode, B_STRING_TYPE, 80, B_ALIGN_LEFT, false, true));
3374 }
3375 
3376 
3377 void
3378 BContainerWindow::ShowAttributesMenu()
3379 {
3380 	ASSERT(fAttrMenu);
3381 	fMenuBar->AddItem(fAttrMenu);
3382 }
3383 
3384 
3385 void
3386 BContainerWindow::HideAttributesMenu()
3387 {
3388 	ASSERT(fAttrMenu);
3389 	fMenuBar->RemoveItem(fAttrMenu);
3390 }
3391 
3392 
3393 void
3394 BContainerWindow::MarkAttributesMenu()
3395 {
3396 	MarkAttributesMenu(fAttrMenu);
3397 }
3398 
3399 
3400 void
3401 BContainerWindow::MarkAttributesMenu(BMenu* menu)
3402 {
3403 	if (menu == NULL)
3404 		return;
3405 
3406 	int32 count = menu->CountItems();
3407 	for (int32 index = 0; index < count; index++) {
3408 		BMenuItem* item = menu->ItemAt(index);
3409 		int32 attrHash;
3410 		if (item->Message() != NULL) {
3411 			if (item->Message()->FindInt32("attr_hash", &attrHash) == B_OK)
3412 				item->SetMarked(PoseView()->ColumnFor((uint32)attrHash) != 0);
3413 			else
3414 				item->SetMarked(false);
3415 		}
3416 
3417 		BMenu* submenu = item->Submenu();
3418 		if (submenu != NULL) {
3419 			int32 count2 = submenu->CountItems();
3420 			for (int32 subindex = 0; subindex < count2; subindex++) {
3421 				item = submenu->ItemAt(subindex);
3422 				if (item->Message() != NULL) {
3423 					if (item->Message()->FindInt32("attr_hash", &attrHash)
3424 						== B_OK) {
3425 						item->SetMarked(PoseView()->ColumnFor((uint32)attrHash)
3426 							!= 0);
3427 					} else
3428 						item->SetMarked(false);
3429 				}
3430 			}
3431 		}
3432 	}
3433 }
3434 
3435 
3436 void
3437 BContainerWindow::MarkArrangeByMenu(BMenu* menu)
3438 {
3439 	if (menu == NULL)
3440 		return;
3441 
3442 	int32 count = menu->CountItems();
3443 	for (int32 index = 0; index < count; index++) {
3444 		BMenuItem* item = menu->ItemAt(index);
3445 		if (item->Message() != NULL) {
3446 			uint32 attrHash;
3447 			if (item->Message()->FindInt32("attr_hash",
3448 					(int32*)&attrHash) == B_OK) {
3449 				item->SetMarked(PoseView()->PrimarySort() == attrHash);
3450 			} else if (item->Command() == kArrangeReverseOrder)
3451 				item->SetMarked(PoseView()->ReverseSort());
3452 		}
3453 	}
3454 }
3455 
3456 
3457 void
3458 BContainerWindow::AddMimeTypesToMenu()
3459 {
3460 	AddMimeTypesToMenu(fAttrMenu);
3461 }
3462 
3463 
3464 // Adds a menu for a specific MIME type if it doesn't exist already.
3465 // Returns the menu, if it existed or not.
3466 BMenu*
3467 BContainerWindow::AddMimeMenu(const BMimeType& mimeType, bool isSuperType,
3468 	BMenu* menu, int32 start)
3469 {
3470 	AutoLock<BLooper> _(menu->Looper());
3471 
3472 	if (!mimeType.IsValid())
3473 		return NULL;
3474 
3475 	// Check if we already have an entry for this MIME type in the menu.
3476 	for (int32 i = start; BMenuItem* item = menu->ItemAt(i); i++) {
3477 		BMessage* message = item->Message();
3478 		if (message == NULL)
3479 			continue;
3480 
3481 		const char* type;
3482 		if (message->FindString("mimetype", &type) == B_OK
3483 			&& !strcmp(mimeType.Type(), type)) {
3484 			return item->Submenu();
3485 		}
3486 	}
3487 
3488 	BMessage attrInfo;
3489 	char description[B_MIME_TYPE_LENGTH];
3490 	const char* label = mimeType.Type();
3491 
3492 	if (!mimeType.IsInstalled())
3493 		return NULL;
3494 
3495 	// only add things to menu which have "user-visible" data
3496 	if (mimeType.GetAttrInfo(&attrInfo) != B_OK)
3497 		return NULL;
3498 
3499 	if (mimeType.GetShortDescription(description) == B_OK && description[0])
3500 		label = description;
3501 
3502 	// go through each field in meta mime and add it to a menu
3503 	BMenu* mimeMenu = NULL;
3504 	if (isSuperType) {
3505 		// If it is a supertype, we create the menu anyway as it may have
3506 		// submenus later on.
3507 		mimeMenu = new BMenu(label);
3508 		BFont font;
3509 		menu->GetFont(&font);
3510 		mimeMenu->SetFont(&font);
3511 	}
3512 
3513 	int32 index = -1;
3514 	const char* publicName;
3515 	while (attrInfo.FindString("attr:public_name", ++index, &publicName)
3516 			== B_OK) {
3517 		if (!attrInfo.FindBool("attr:viewable", index)) {
3518 			// don't add if attribute not viewable
3519 			continue;
3520 		}
3521 
3522 		int32 type;
3523 		int32 align;
3524 		int32 width;
3525 		bool editable;
3526 		const char* attrName;
3527 		if (attrInfo.FindString("attr:name", index, &attrName) != B_OK
3528 			|| attrInfo.FindInt32("attr:type", index, &type) != B_OK
3529 			|| attrInfo.FindBool("attr:editable", index, &editable) != B_OK
3530 			|| attrInfo.FindInt32("attr:width", index, &width) != B_OK
3531 			|| attrInfo.FindInt32("attr:alignment", index, &align) != B_OK)
3532 			continue;
3533 
3534 		BString displayAs;
3535 		attrInfo.FindString("attr:display_as", index, &displayAs);
3536 
3537 		if (mimeMenu == NULL) {
3538 			// do a lazy allocation of the menu
3539 			mimeMenu = new BMenu(label);
3540 			BFont font;
3541 			menu->GetFont(&font);
3542 			mimeMenu->SetFont(&font);
3543 		}
3544 		mimeMenu->AddItem(NewAttributeMenuItem(publicName, attrName, type,
3545 			displayAs.String(), width, align, editable, false));
3546 	}
3547 
3548 	if (mimeMenu == NULL)
3549 		return NULL;
3550 
3551 	BMessage* message = new BMessage(kMIMETypeItem);
3552 	message->AddString("mimetype", mimeType.Type());
3553 	menu->AddItem(new IconMenuItem(mimeMenu, message, mimeType.Type()));
3554 
3555 	return mimeMenu;
3556 }
3557 
3558 
3559 void
3560 BContainerWindow::AddMimeTypesToMenu(BMenu* menu)
3561 {
3562 	if (menu == NULL)
3563 		return;
3564 
3565 	// Remove old mime type menus
3566 	int32 start = menu->CountItems();
3567 	while (start > 0 && menu->ItemAt(start - 1)->Submenu() != NULL) {
3568 		delete menu->RemoveItem(start - 1);
3569 		start--;
3570 	}
3571 
3572 	// Add a separator item if there is none yet
3573 	if (start > 0
3574 		&& dynamic_cast<BSeparatorItem*>(menu->ItemAt(start - 1)) == NULL)
3575 		menu->AddSeparatorItem();
3576 
3577 	// Add MIME type in case we're a default query type window
3578 	BPath path;
3579 	if (TargetModel() != NULL) {
3580 		TargetModel()->GetPath(&path);
3581 		if (path.InitCheck() == B_OK
3582 			&& strstr(path.Path(), "/" kQueryTemplates "/") != NULL) {
3583 			// demangle MIME type name
3584 			BString name(TargetModel()->Name());
3585 			name.ReplaceFirst('_', '/');
3586 
3587 			PoseView()->AddMimeType(name.String());
3588 		}
3589 	}
3590 
3591 	// Add MIME type menus
3592 
3593 	int32 typeCount = PoseView()->CountMimeTypes();
3594 
3595 	for (int32 index = 0; index < typeCount; index++) {
3596 		BMimeType mimeType(PoseView()->MimeTypeAt(index));
3597 		if (mimeType.InitCheck() == B_OK) {
3598 			BMimeType superType;
3599 			mimeType.GetSupertype(&superType);
3600 			if (superType.InitCheck() == B_OK) {
3601 				BMenu* superMenu = AddMimeMenu(superType, true, menu, start);
3602 				if (superMenu != NULL) {
3603 					// We have a supertype menu.
3604 					AddMimeMenu(mimeType, false, superMenu, 0);
3605 				}
3606 			}
3607 		}
3608 	}
3609 
3610 	// remove empty super menus, promote sub-types if needed
3611 
3612 	for (int32 index = 0; index < typeCount; index++) {
3613 		BMimeType mimeType(PoseView()->MimeTypeAt(index));
3614 		BMimeType superType;
3615 		mimeType.GetSupertype(&superType);
3616 
3617 		BMenu* superMenu = AddMimeMenu(superType, true, menu, start);
3618 		if (superMenu == NULL)
3619 			continue;
3620 
3621 		int32 itemsFound = 0;
3622 		int32 menusFound = 0;
3623 		for (int32 i = 0; BMenuItem* item = superMenu->ItemAt(i); i++) {
3624 			if (item->Submenu() != NULL)
3625 				menusFound++;
3626 			else
3627 				itemsFound++;
3628 		}
3629 
3630 		if (itemsFound == 0) {
3631 			if (menusFound != 0) {
3632 				// promote types to the top level
3633 				while (BMenuItem* item = superMenu->RemoveItem((int32)0)) {
3634 					menu->AddItem(item);
3635 				}
3636 			}
3637 
3638 			menu->RemoveItem(superMenu->Superitem());
3639 			delete superMenu->Superitem();
3640 		}
3641 	}
3642 
3643 	// remove separator if it's the only item in menu
3644 	BMenuItem* separator = menu->ItemAt(menu->CountItems() - 1);
3645 	if (dynamic_cast<BSeparatorItem*>(separator) != NULL) {
3646 		menu->RemoveItem(separator);
3647 		delete separator;
3648 	}
3649 
3650 	MarkAttributesMenu(menu);
3651 }
3652 
3653 
3654 BHandler*
3655 BContainerWindow::ResolveSpecifier(BMessage* message, int32 index,
3656 	BMessage* specifier, int32 form, const char* property)
3657 {
3658 	if (strcmp(property, "Poses") == 0) {
3659 //		PRINT(("BContainerWindow::ResolveSpecifier %s\n", property));
3660 		message->PopSpecifier();
3661 		return PoseView();
3662 	}
3663 
3664 	return _inherited::ResolveSpecifier(message, index, specifier,
3665 		form, property);
3666 }
3667 
3668 
3669 PiggybackTaskLoop*
3670 BContainerWindow::DelayedTaskLoop()
3671 {
3672 	if (!fTaskLoop)
3673 		fTaskLoop = new PiggybackTaskLoop;
3674 
3675 	return fTaskLoop;
3676 }
3677 
3678 
3679 bool
3680 BContainerWindow::NeedsDefaultStateSetup()
3681 {
3682 	if (TargetModel() == NULL)
3683 		return false;
3684 
3685 	if (TargetModel()->IsRoot()) {
3686 		// don't try to set up anything if we are root
3687 		return false;
3688 	}
3689 
3690 	WindowStateNodeOpener opener(this, false);
3691 	if (opener.StreamNode() == NULL) {
3692 		// can't read state, give up
3693 		return false;
3694 	}
3695 
3696 	return !NodeHasSavedState(opener.Node());
3697 }
3698 
3699 
3700 bool
3701 BContainerWindow::DefaultStateSourceNode(const char* name, BNode* result,
3702 	bool createNew, bool createFolder)
3703 {
3704 	//PRINT(("looking for default state in tracker settings dir\n"));
3705 	BPath settingsPath;
3706 	if (FSFindTrackerSettingsDir(&settingsPath) != B_OK)
3707 		return false;
3708 
3709 	BDirectory dir(settingsPath.Path());
3710 
3711 	BPath path(settingsPath);
3712 	path.Append(name);
3713 	if (!BEntry(path.Path()).Exists()) {
3714 		if (!createNew)
3715 			return false;
3716 
3717 		BPath tmpPath(settingsPath);
3718 		for (;;) {
3719 			// deal with several levels of folders
3720 			const char* nextSlash = strchr(name, '/');
3721 			if (!nextSlash)
3722 				break;
3723 
3724 			BString tmp;
3725 			tmp.SetTo(name, nextSlash - name);
3726 			tmpPath.Append(tmp.String());
3727 
3728 			mkdir(tmpPath.Path(), 0777);
3729 
3730 			name = nextSlash + 1;
3731 			if (!name[0]) {
3732 				// can't deal with a slash at end
3733 				return false;
3734 			}
3735 		}
3736 
3737 		if (createFolder) {
3738 			if (mkdir(path.Path(), 0777) < 0)
3739 				return false;
3740 		} else {
3741 			BFile file;
3742 			if (dir.CreateFile(name, &file) != B_OK)
3743 				return false;
3744 		}
3745 	}
3746 
3747 	//PRINT(("using default state from %s\n", path.Path()));
3748 	result->SetTo(path.Path());
3749 	return result->InitCheck() == B_OK;
3750 }
3751 
3752 
3753 void
3754 BContainerWindow::SetupDefaultState()
3755 {
3756 	BNode defaultingNode;
3757 		// this is where we'll ulitimately get the state from
3758 	bool gotDefaultingNode = 0;
3759 	bool shouldStagger = false;
3760 
3761 	ASSERT(TargetModel() != NULL);
3762 
3763 	PRINT(("folder %s does not have any saved state\n", TargetModel()->Name()));
3764 
3765 	WindowStateNodeOpener opener(this, true);
3766 		// this is our destination node, whatever it is for this window
3767 	if (opener.StreamNode() == NULL)
3768 		return;
3769 
3770 	if (!TargetModel()->IsRoot()) {
3771 		BDirectory deskDir;
3772 		FSGetDeskDir(&deskDir);
3773 
3774 		// try copying state from our parent directory, unless it is the
3775 		// desktop folder
3776 		BEntry entry(TargetModel()->EntryRef());
3777 		BNode parent;
3778 		if (FSGetParentVirtualDirectoryAware(entry, parent) == B_OK
3779 			&& parent != deskDir) {
3780 			PRINT(("looking at parent for state\n"));
3781 			if (NodeHasSavedState(&parent)) {
3782 				PRINT(("got state from parent\n"));
3783 				defaultingNode = parent;
3784 				gotDefaultingNode = true;
3785 				// when getting state from parent, stagger the window
3786 				shouldStagger = true;
3787 			}
3788 		}
3789 	}
3790 
3791 	if (!gotDefaultingNode
3792 		// parent didn't have any state, use the template directory from
3793 		// tracker settings folder for what our state should be
3794 		// For simplicity we are not picking up the most recent
3795 		// changes that didn't get committed if home is still open in
3796 		// a window, that's probably not a problem; would be OK if state
3797 		// got committed after every change
3798 		&& !DefaultStateSourceNode(kDefaultFolderTemplate, &defaultingNode,
3799 			true)) {
3800 		return;
3801 	}
3802 
3803 	if (PoseView()->IsDesktop()) {
3804 		// don't copy over the attributes if we are the Desktop
3805 		return;
3806 	}
3807 
3808 	// copy over the attributes
3809 
3810 	// set up a filter of the attributes we want copied
3811 	const char* allowAttrs[] = {
3812 		kAttrWindowFrame,
3813 		kAttrWindowWorkspace,
3814 		kAttrViewState,
3815 		kAttrViewStateForeign,
3816 		kAttrColumns,
3817 		kAttrColumnsForeign,
3818 		0
3819 	};
3820 
3821 	// copy over attributes that apply; transform them properly, stripping
3822 	// parts that do not apply, adding a window stagger, etc.
3823 
3824 	StaggerOneParams params;
3825 	params.rectFromParent = shouldStagger;
3826 	SelectiveAttributeTransformer frameOffsetter(kAttrWindowFrame,
3827 		OffsetFrameOne, &params);
3828 	SelectiveAttributeTransformer scrollOriginCleaner(kAttrViewState,
3829 		ClearViewOriginOne, &params);
3830 
3831 	// do it
3832 	AttributeStreamMemoryNode memoryNode;
3833 	NamesToAcceptAttrFilter filter(allowAttrs);
3834 	AttributeStreamFileNode fileNode(&defaultingNode);
3835 
3836 	*opener.StreamNode() << scrollOriginCleaner << frameOffsetter
3837 		<< memoryNode << filter << fileNode;
3838 }
3839 
3840 
3841 void
3842 BContainerWindow::RestoreWindowState(AttributeStreamNode* node)
3843 {
3844 	if (node == NULL || PoseView()->IsDesktop()) {
3845 		// don't restore any window state if we are the Desktop
3846 		return;
3847 	}
3848 
3849 	const char* rectAttributeName;
3850 	const char* workspaceAttributeName;
3851 	if (TargetModel()->IsRoot()) {
3852 		rectAttributeName = kAttrDisksFrame;
3853 		workspaceAttributeName = kAttrDisksWorkspace;
3854 	} else {
3855 		rectAttributeName = kAttrWindowFrame;
3856 		workspaceAttributeName = kAttrWindowWorkspace;
3857 	}
3858 
3859 	BRect frame(Frame());
3860 	if (node->Read(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame)
3861 			== sizeof(BRect)) {
3862 		const float scalingFactor = be_plain_font->Size() / 12.0f;
3863 		frame.left *= scalingFactor;
3864 		frame.top *= scalingFactor;
3865 		frame.right *= scalingFactor;
3866 		frame.bottom *= scalingFactor;
3867 
3868 		MoveTo(frame.LeftTop());
3869 		ResizeTo(frame.Width(), frame.Height());
3870 	} else
3871 		sNewWindRect.OffsetBy(sWindowStaggerBy, sWindowStaggerBy);
3872 
3873 	fPreviousBounds = Bounds();
3874 
3875 	uint32 workspace;
3876 	if (((fOpenFlags & kRestoreWorkspace) != 0)
3877 		&& node->Read(workspaceAttributeName, 0, B_INT32_TYPE, sizeof(uint32),
3878 			&workspace) == sizeof(uint32))
3879 		SetWorkspaces(workspace);
3880 
3881 	if ((fOpenFlags & kIsHidden) != 0)
3882 		Minimize(true);
3883 
3884 	// restore window decor settings
3885 	int32 size = node->Contains(kAttrWindowDecor, B_RAW_TYPE);
3886 	if (size > 0) {
3887 		char buffer[size];
3888 		if (((fOpenFlags & kRestoreDecor) != 0)
3889 			&& node->Read(kAttrWindowDecor, 0, B_RAW_TYPE, size, buffer)
3890 				== size) {
3891 			BMessage decorSettings;
3892 			if (decorSettings.Unflatten(buffer) == B_OK)
3893 				SetDecoratorSettings(decorSettings);
3894 		}
3895 	}
3896 }
3897 
3898 
3899 void
3900 BContainerWindow::RestoreWindowState(const BMessage& message)
3901 {
3902 	if (PoseView()->IsDesktop()) {
3903 		// don't restore any window state if we are the Desktop
3904 		return;
3905 	}
3906 
3907 	const char* rectAttributeName;
3908 	const char* workspaceAttributeName;
3909 	if (TargetModel()->IsRoot()) {
3910 		rectAttributeName = kAttrDisksFrame;
3911 		workspaceAttributeName = kAttrDisksWorkspace;
3912 	} else {
3913 		rectAttributeName = kAttrWindowFrame;
3914 		workspaceAttributeName = kAttrWindowWorkspace;
3915 	}
3916 
3917 	BRect frame(Frame());
3918 	if (message.FindRect(rectAttributeName, &frame) == B_OK) {
3919 		const float scalingFactor = be_plain_font->Size() / 12.0f;
3920 		frame.left *= scalingFactor;
3921 		frame.top *= scalingFactor;
3922 		frame.right *= scalingFactor;
3923 		frame.bottom *= scalingFactor;
3924 
3925 		MoveTo(frame.LeftTop());
3926 		ResizeTo(frame.Width(), frame.Height());
3927 	} else
3928 		sNewWindRect.OffsetBy(sWindowStaggerBy, sWindowStaggerBy);
3929 
3930 	uint32 workspace;
3931 	if (((fOpenFlags & kRestoreWorkspace) != 0)
3932 		&& message.FindInt32(workspaceAttributeName,
3933 			(int32*)&workspace) == B_OK) {
3934 		SetWorkspaces(workspace);
3935 	}
3936 
3937 	if ((fOpenFlags & kIsHidden) != 0)
3938 		Minimize(true);
3939 
3940 	// restore window decor settings
3941 	BMessage decorSettings;
3942 	if (((fOpenFlags & kRestoreDecor) != 0)
3943 		&& message.FindMessage(kAttrWindowDecor, &decorSettings) == B_OK) {
3944 		SetDecoratorSettings(decorSettings);
3945 	}
3946 
3947 	fStateNeedsSaving = false;
3948 		// Undo the effect of the above MoveTo and ResizeTo calls
3949 }
3950 
3951 
3952 void
3953 BContainerWindow::SaveWindowState(AttributeStreamNode* node)
3954 {
3955 	if (TargetModel() != NULL && PoseView()->IsDesktop()) {
3956 		// don't save window state if we are the Desktop
3957 		return;
3958 	}
3959 
3960 	ASSERT(node != NULL);
3961 
3962 	const char* rectAttributeName;
3963 	const char* workspaceAttributeName;
3964 	if (TargetModel()->IsRoot()) {
3965 		rectAttributeName = kAttrDisksFrame;
3966 		workspaceAttributeName = kAttrDisksWorkspace;
3967 	} else {
3968 		rectAttributeName = kAttrWindowFrame;
3969 		workspaceAttributeName = kAttrWindowWorkspace;
3970 	}
3971 
3972 	// node is null if it already got deleted
3973 	BRect frame(Frame());
3974 	const float scalingFactor = be_plain_font->Size() / 12.0f;
3975 	frame.left /= scalingFactor;
3976 	frame.top /= scalingFactor;
3977 	frame.right /= scalingFactor;
3978 	frame.bottom /= scalingFactor;
3979 	node->Write(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame);
3980 
3981 	uint32 workspaces = Workspaces();
3982 	node->Write(workspaceAttributeName, 0, B_INT32_TYPE, sizeof(uint32),
3983 		&workspaces);
3984 
3985 	BMessage decorSettings;
3986 	if (GetDecoratorSettings(&decorSettings) == B_OK) {
3987 		int32 size = decorSettings.FlattenedSize();
3988 		char buffer[size];
3989 		if (decorSettings.Flatten(buffer, size) == B_OK) {
3990 			node->Write(kAttrWindowDecor, 0, B_RAW_TYPE, size, buffer);
3991 		}
3992 	}
3993 }
3994 
3995 
3996 void
3997 BContainerWindow::SaveWindowState(BMessage& message) const
3998 {
3999 	const char* rectAttributeName;
4000 	const char* workspaceAttributeName;
4001 
4002 	if (TargetModel() != NULL && TargetModel()->IsRoot()) {
4003 		rectAttributeName = kAttrDisksFrame;
4004 		workspaceAttributeName = kAttrDisksWorkspace;
4005 	} else {
4006 		rectAttributeName = kAttrWindowFrame;
4007 		workspaceAttributeName = kAttrWindowWorkspace;
4008 	}
4009 
4010 	// node is null if it already got deleted
4011 	BRect frame(Frame());
4012 	const float scalingFactor = be_plain_font->Size() / 12.0f;
4013 	frame.left /= scalingFactor;
4014 	frame.top /= scalingFactor;
4015 	frame.right /= scalingFactor;
4016 	frame.bottom /= scalingFactor;
4017 	message.AddRect(rectAttributeName, frame);
4018 	message.AddInt32(workspaceAttributeName, (int32)Workspaces());
4019 
4020 	BMessage decorSettings;
4021 	if (GetDecoratorSettings(&decorSettings) == B_OK) {
4022 		message.AddMessage(kAttrWindowDecor, &decorSettings);
4023 	}
4024 }
4025 
4026 
4027 status_t
4028 BContainerWindow::DragStart(const BMessage* dragMessage)
4029 {
4030 	if (dragMessage == NULL)
4031 		return B_ERROR;
4032 
4033 	// if already dragging, or
4034 	// if all the refs match
4035 	if (Dragging()
4036 		&& SpringLoadedFolderCompareMessages(dragMessage, fDragMessage)) {
4037 		return B_OK;
4038 	}
4039 
4040 	// cache the current drag message
4041 	// build a list of the mimetypes in the message
4042 	SpringLoadedFolderCacheDragData(dragMessage, &fDragMessage,
4043 		&fCachedTypesList);
4044 
4045 	fWaitingForRefs = true;
4046 
4047 	return B_OK;
4048 }
4049 
4050 
4051 void
4052 BContainerWindow::DragStop()
4053 {
4054 	delete fDragMessage;
4055 	fDragMessage = NULL;
4056 
4057 	delete fCachedTypesList;
4058 	fCachedTypesList = NULL;
4059 
4060 	fWaitingForRefs = false;
4061 }
4062 
4063 
4064 void
4065 BContainerWindow::ShowSelectionWindow()
4066 {
4067 	if (fSelectionWindow == NULL) {
4068 		fSelectionWindow = new SelectionWindow(this);
4069 		fSelectionWindow->Show();
4070 	} else if (fSelectionWindow->Lock()) {
4071 		// The window is already there, just bring it close
4072 		fSelectionWindow->MoveCloseToMouse();
4073 		if (fSelectionWindow->IsHidden())
4074 			fSelectionWindow->Show();
4075 
4076 		fSelectionWindow->Unlock();
4077 	}
4078 }
4079 
4080 
4081 void
4082 BContainerWindow::ShowNavigator(bool show)
4083 {
4084 	if (PoseView()->IsDesktop() || !TargetModel()->IsDirectory() || PoseView()->IsFilePanel())
4085 		return;
4086 
4087 	if (show) {
4088 		if (Navigator() != NULL && !Navigator()->IsHidden())
4089 			return;
4090 
4091 		if (Navigator() == NULL) {
4092 			fNavigator = new BNavigator(TargetModel());
4093 			fPoseContainer->GridLayout()->AddView(fNavigator, 0, 0, 2);
4094 		}
4095 
4096 		if (Navigator()->IsHidden())
4097 			Navigator()->Show();
4098 
4099 		if (PoseView()->VScrollBar())
4100 			PoseView()->UpdateScrollRange();
4101 	} else {
4102 		if (Navigator() == NULL || Navigator()->IsHidden())
4103 			return;
4104 
4105 		if (PoseView()->VScrollBar())
4106 			PoseView()->UpdateScrollRange();
4107 
4108 		fNavigator->Hide();
4109 	}
4110 }
4111 
4112 
4113 void
4114 BContainerWindow::SetSingleWindowBrowseShortcuts(bool enabled)
4115 {
4116 	if (PoseView()->IsDesktop())
4117 		return;
4118 
4119 	if (enabled) {
4120 		if (Navigator() == NULL)
4121 			return;
4122 
4123 		RemoveShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY);
4124 		RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY);
4125 		RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY | B_OPTION_KEY);
4126 
4127 		AddShortcut(B_LEFT_ARROW, B_COMMAND_KEY,
4128 			new BMessage(kNavigatorCommandBackward), Navigator());
4129 		AddShortcut(B_RIGHT_ARROW, B_COMMAND_KEY,
4130 			new BMessage(kNavigatorCommandForward), Navigator());
4131 		AddShortcut(B_UP_ARROW, B_COMMAND_KEY,
4132 			new BMessage(kNavigatorCommandUp), Navigator());
4133 
4134 		AddShortcut(B_LEFT_ARROW, B_OPTION_KEY | B_COMMAND_KEY,
4135 			new BMessage(kNavigatorCommandBackward), Navigator());
4136 		AddShortcut(B_RIGHT_ARROW, B_OPTION_KEY | B_COMMAND_KEY,
4137 			new BMessage(kNavigatorCommandForward), Navigator());
4138 		AddShortcut(B_UP_ARROW, B_OPTION_KEY | B_COMMAND_KEY,
4139 			new BMessage(kNavigatorCommandUp), Navigator());
4140 		AddShortcut(B_DOWN_ARROW, B_OPTION_KEY | B_COMMAND_KEY,
4141 			new BMessage(kOpenSelection), PoseView());
4142 		AddShortcut('L', B_COMMAND_KEY,
4143 			new BMessage(kNavigatorCommandSetFocus), Navigator());
4144 	} else {
4145 		RemoveShortcut(B_LEFT_ARROW, B_COMMAND_KEY);
4146 		RemoveShortcut(B_RIGHT_ARROW, B_COMMAND_KEY);
4147 		RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY);
4148 			// This is added again, below, with a new meaning.
4149 
4150 		RemoveShortcut(B_LEFT_ARROW, B_OPTION_KEY | B_COMMAND_KEY);
4151 		RemoveShortcut(B_RIGHT_ARROW, B_OPTION_KEY | B_COMMAND_KEY);
4152 		RemoveShortcut(B_UP_ARROW, B_OPTION_KEY | B_COMMAND_KEY);
4153 		RemoveShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY);
4154 			// This also changes meaning, added again below.
4155 
4156 		AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY,
4157 			new BMessage(kOpenSelection), PoseView());
4158 		AddShortcut(B_UP_ARROW, B_COMMAND_KEY,
4159 			new BMessage(kOpenParentDir), PoseView());
4160 			// We change the meaning from kNavigatorCommandUp
4161 			// to kOpenParentDir.
4162 		AddShortcut(B_UP_ARROW, B_COMMAND_KEY | B_OPTION_KEY,
4163 			new BMessage(kOpenParentDir), PoseView());
4164 			// command + option results in closing the parent window
4165 		RemoveShortcut('L', B_COMMAND_KEY);
4166 	}
4167 }
4168 
4169 
4170 void
4171 BContainerWindow::SetPathWatchingEnabled(bool enable)
4172 {
4173 	if (IsPathWatchingEnabled()) {
4174 		stop_watching(this);
4175 		fIsWatchingPath = false;
4176 	}
4177 
4178 	if (enable) {
4179 		if (TargetModel() != NULL) {
4180 			BEntry entry;
4181 
4182 			TargetModel()->GetEntry(&entry);
4183 			status_t err;
4184 			do {
4185 				err = entry.GetParent(&entry);
4186 				if (err != B_OK)
4187 					break;
4188 
4189 				char name[B_FILE_NAME_LENGTH];
4190 				entry.GetName(name);
4191 				if (strcmp(name, "/") == 0)
4192 					break;
4193 
4194 				node_ref ref;
4195 				entry.GetNodeRef(&ref);
4196 				watch_node(&ref, B_WATCH_NAME, this);
4197 			} while (err == B_OK);
4198 
4199 			fIsWatchingPath = err == B_OK;
4200 		} else
4201 			fIsWatchingPath = false;
4202 	}
4203 }
4204 
4205 
4206 void
4207 BContainerWindow::PulseTaskLoop()
4208 {
4209 	if (fTaskLoop)
4210 		fTaskLoop->PulseMe();
4211 }
4212 
4213 
4214 void
4215 BContainerWindow::PopulateArrangeByMenu(BMenu* menu)
4216 {
4217 	if (!fAttrMenu || !menu)
4218 		return;
4219 	// empty fArrangeByMenu...
4220 	BMenuItem* item;
4221 	while ((item = menu->RemoveItem((int32)0)) != NULL)
4222 		delete item;
4223 
4224 	int32 itemCount = fAttrMenu->CountItems();
4225 	for (int32 i = 0; i < itemCount; i++) {
4226 		item = fAttrMenu->ItemAt(i);
4227 		if (item->Command() == kAttributeItem) {
4228 			BMessage* message = new BMessage(*(item->Message()));
4229 			message->what = kArrangeBy;
4230 			BMenuItem* newItem = new BMenuItem(item->Label(), message);
4231 			newItem->SetTarget(PoseView());
4232 			menu->AddItem(newItem);
4233 		}
4234 	}
4235 
4236 	menu->AddSeparatorItem();
4237 
4238 	item = new BMenuItem(B_TRANSLATE("Reverse order"),
4239 		new BMessage(kArrangeReverseOrder));
4240 
4241 	item->SetTarget(PoseView());
4242 	menu->AddItem(item);
4243 
4244 	menu->AddSeparatorItem();
4245 
4246 	item = new BMenuItem(B_TRANSLATE("Clean up"), new BMessage(kCleanup), 'K');
4247 	item->SetTarget(PoseView());
4248 	menu->AddItem(item);
4249 }
4250 
4251 
4252 //	#pragma mark - WindowStateNodeOpener
4253 
4254 
4255 WindowStateNodeOpener::WindowStateNodeOpener(BContainerWindow* window,
4256 	bool forWriting)
4257 	:
4258 	fModelOpener(NULL),
4259 	fNode(NULL),
4260 	fStreamNode(NULL)
4261 {
4262 	if (window->TargetModel() != NULL && window->TargetModel()->IsRoot()) {
4263 		BDirectory dir;
4264 		if (FSGetDeskDir(&dir) == B_OK) {
4265 			fNode = new BDirectory(dir);
4266 			fStreamNode = new AttributeStreamFileNode(fNode);
4267 		}
4268 	} else if (window->TargetModel() != NULL) {
4269 		fModelOpener = new ModelNodeLazyOpener(window->TargetModel(),
4270 			forWriting, false);
4271 		if (fModelOpener->IsOpen(forWriting)) {
4272 			fStreamNode = new AttributeStreamFileNode(
4273 				fModelOpener->TargetModel()->Node());
4274 		}
4275 	}
4276 }
4277 
4278 WindowStateNodeOpener::~WindowStateNodeOpener()
4279 {
4280 	delete fModelOpener;
4281 	delete fNode;
4282 	delete fStreamNode;
4283 }
4284 
4285 
4286 void
4287 WindowStateNodeOpener::SetTo(const BDirectory* node)
4288 {
4289 	delete fModelOpener;
4290 	delete fNode;
4291 	delete fStreamNode;
4292 
4293 	fModelOpener = NULL;
4294 	fNode = new BDirectory(*node);
4295 	fStreamNode = new AttributeStreamFileNode(fNode);
4296 }
4297 
4298 
4299 void
4300 WindowStateNodeOpener::SetTo(const BEntry* entry, bool forWriting)
4301 {
4302 	delete fModelOpener;
4303 	delete fNode;
4304 	delete fStreamNode;
4305 
4306 	fModelOpener = NULL;
4307 	fNode = new BFile(entry, (uint32)(forWriting ? O_RDWR : O_RDONLY));
4308 	fStreamNode = new AttributeStreamFileNode(fNode);
4309 }
4310 
4311 
4312 void
4313 WindowStateNodeOpener::SetTo(Model* model, bool forWriting)
4314 {
4315 	delete fModelOpener;
4316 	delete fNode;
4317 	delete fStreamNode;
4318 
4319 	fNode = NULL;
4320 	fStreamNode = NULL;
4321 	fModelOpener = new ModelNodeLazyOpener(model, forWriting, false);
4322 	if (fModelOpener->IsOpen(forWriting)) {
4323 		fStreamNode = new AttributeStreamFileNode(
4324 			fModelOpener->TargetModel()->Node());
4325 	}
4326 }
4327 
4328 
4329 AttributeStreamNode*
4330 WindowStateNodeOpener::StreamNode() const
4331 {
4332 	return fStreamNode;
4333 }
4334 
4335 
4336 BNode*
4337 WindowStateNodeOpener::Node() const
4338 {
4339 	if (!fStreamNode)
4340 		return NULL;
4341 
4342 	if (fNode)
4343 		return fNode;
4344 
4345 	return fModelOpener->TargetModel()->Node();
4346 }
4347 
4348 
4349 //	#pragma mark - BorderedView
4350 
4351 
4352 BorderedView::BorderedView()
4353 	:
4354 	BGroupView(B_VERTICAL, 0),
4355 	fEnableBorderHighlight(true)
4356 {
4357 	GroupLayout()->SetInsets(1);
4358 }
4359 
4360 
4361 void
4362 BorderedView::WindowActivated(bool active)
4363 {
4364 	BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
4365 	if (window == NULL)
4366 		return;
4367 
4368 	if (window->PoseView()->IsFocus())
4369 		PoseViewFocused(active); // Update border color
4370 }
4371 
4372 
4373 void BorderedView::EnableBorderHighlight(bool enable)
4374 {
4375 	fEnableBorderHighlight = enable;
4376 	PoseViewFocused(false);
4377 }
4378 
4379 
4380 void
4381 BorderedView::PoseViewFocused(bool focused)
4382 {
4383 	BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
4384 	if (window == NULL)
4385 		return;
4386 
4387 	color_which base = B_DOCUMENT_BACKGROUND_COLOR;
4388 	float tint = B_DARKEN_2_TINT;
4389 	if (focused && window->IsActive() && fEnableBorderHighlight) {
4390 		base = B_KEYBOARD_NAVIGATION_COLOR;
4391 		tint = B_NO_TINT;
4392 	}
4393 
4394 	BScrollBar* hScrollBar = window->PoseView()->HScrollBar();
4395 	if (hScrollBar != NULL)
4396 		hScrollBar->SetBorderHighlighted(focused);
4397 
4398 	BScrollBar* vScrollBar = window->PoseView()->VScrollBar();
4399 	if (vScrollBar != NULL)
4400 		vScrollBar->SetBorderHighlighted(focused);
4401 
4402 	SetViewUIColor(base, tint);
4403 	Invalidate();
4404 }
4405 
4406 
4407 void
4408 BorderedView::Pulse()
4409 {
4410 	BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
4411 	if (window != NULL)
4412 		window->PulseTaskLoop();
4413 }
4414