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