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