xref: /haiku/src/kits/tracker/ContainerWindow.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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 point, 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()->SelectionList()->CountItems();
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 	item->SetEnabled(PoseView()->SelectionList()->CountItems() > 0
1852 		|| PoseView() != CurrentFocus());
1853 
1854 	if ((modifiers() & B_SHIFT_KEY) != 0) {
1855 		item->SetLabel(B_TRANSLATE("Cut more"));
1856 		item->SetShortcut('X', B_COMMAND_KEY | B_SHIFT_KEY);
1857 		item->SetMessage(new BMessage(kCutMoreSelectionToClipboard));
1858 	} else {
1859 		item->SetLabel(B_TRANSLATE("Cut"));
1860 		item->SetShortcut('X', B_COMMAND_KEY);
1861 		item->SetMessage(new BMessage(B_CUT));
1862 	}
1863 }
1864 
1865 
1866 void
1867 BContainerWindow::SetCopyItem(BMenu* menu)
1868 {
1869 	BMenuItem* item;
1870 	if ((item = menu->FindItem(B_COPY)) == NULL
1871 		&& (item = menu->FindItem(kCopyMoreSelectionToClipboard)) == NULL) {
1872 		return;
1873 	}
1874 
1875 	item->SetEnabled(PoseView()->SelectionList()->CountItems() > 0
1876 		|| PoseView() != CurrentFocus());
1877 
1878 	if ((modifiers() & B_SHIFT_KEY) != 0) {
1879 		item->SetLabel(B_TRANSLATE("Copy more"));
1880 		item->SetShortcut('C', B_COMMAND_KEY | B_SHIFT_KEY);
1881 		item->SetMessage(new BMessage(kCopyMoreSelectionToClipboard));
1882 	} else {
1883 		item->SetLabel(B_TRANSLATE("Copy"));
1884 		item->SetShortcut('C', B_COMMAND_KEY);
1885 		item->SetMessage(new BMessage(B_COPY));
1886 	}
1887 }
1888 
1889 
1890 void
1891 BContainerWindow::SetPasteItem(BMenu* menu)
1892 {
1893 	BMenuItem* item;
1894 	if ((item = menu->FindItem(B_PASTE)) == NULL
1895 		&& (item = menu->FindItem(kPasteLinksFromClipboard)) == NULL) {
1896 		return;
1897 	}
1898 
1899 	item->SetEnabled(FSClipboardHasRefs() || PoseView() != CurrentFocus());
1900 
1901 	if ((modifiers() & B_SHIFT_KEY) != 0) {
1902 		item->SetLabel(B_TRANSLATE("Paste links"));
1903 		item->SetShortcut('V', B_COMMAND_KEY | B_SHIFT_KEY);
1904 		item->SetMessage(new BMessage(kPasteLinksFromClipboard));
1905 	} else {
1906 		item->SetLabel(B_TRANSLATE("Paste"));
1907 		item->SetShortcut('V', B_COMMAND_KEY);
1908 		item->SetMessage(new BMessage(B_PASTE));
1909 	}
1910 }
1911 
1912 
1913 void
1914 BContainerWindow::SetArrangeMenu(BMenu* menu)
1915 {
1916 	BMenuItem* item;
1917 	if ((item = menu->FindItem(kCleanup)) == NULL
1918 		&& (item = menu->FindItem(kCleanupAll)) == NULL) {
1919 		return;
1920 	}
1921 
1922 	item->Menu()->SetEnabled(PoseView()->CountItems() > 0
1923 		&& (PoseView()->ViewMode() != kListMode));
1924 
1925 	BMenu* arrangeMenu;
1926 
1927 	if ((modifiers() & B_SHIFT_KEY) != 0) {
1928 		item->SetLabel(B_TRANSLATE("Clean up all"));
1929 		item->SetShortcut('K', B_COMMAND_KEY | B_SHIFT_KEY);
1930 		item->SetMessage(new BMessage(kCleanupAll));
1931 		arrangeMenu = item->Menu();
1932 	} else {
1933 		item->SetLabel(B_TRANSLATE("Clean up"));
1934 		item->SetShortcut('K', B_COMMAND_KEY);
1935 		item->SetMessage(new BMessage(kCleanup));
1936 		arrangeMenu = item->Menu();
1937 	}
1938 
1939 	MarkArrangeByMenu(arrangeMenu);
1940 }
1941 
1942 
1943 void
1944 BContainerWindow::SetCloseItem(BMenu* menu)
1945 {
1946 	BMenuItem* item;
1947 	if ((item = menu->FindItem(B_QUIT_REQUESTED)) == NULL
1948 		&& (item = menu->FindItem(kCloseAllWindows)) == NULL) {
1949 		return;
1950 	}
1951 
1952 	if ((modifiers() & B_SHIFT_KEY) != 0) {
1953 		item->SetLabel(B_TRANSLATE("Close all"));
1954 		item->SetShortcut('W', B_COMMAND_KEY | B_SHIFT_KEY);
1955 		item->SetTarget(be_app);
1956 		item->SetMessage(new BMessage(kCloseAllWindows));
1957 	} else {
1958 		item->SetLabel(B_TRANSLATE("Close"));
1959 		item->SetShortcut('W', B_COMMAND_KEY);
1960 		item->SetTarget(this);
1961 		item->SetMessage(new BMessage(B_QUIT_REQUESTED));
1962 	}
1963 }
1964 
1965 
1966 bool
1967 BContainerWindow::IsShowing(const node_ref* node) const
1968 {
1969 	return PoseView()->Represents(node);
1970 }
1971 
1972 
1973 bool
1974 BContainerWindow::IsShowing(const entry_ref* entry) const
1975 {
1976 	return PoseView()->Represents(entry);
1977 }
1978 
1979 
1980 void
1981 BContainerWindow::AddMenus()
1982 {
1983 	fFileMenu = new BMenu(B_TRANSLATE("File"));
1984 	AddFileMenu(fFileMenu);
1985 	fMenuBar->AddItem(fFileMenu);
1986 	fWindowMenu = new BMenu(B_TRANSLATE("Window"));
1987 	fMenuBar->AddItem(fWindowMenu);
1988 	AddWindowMenu(fWindowMenu);
1989 	// just create the attribute, decide to add it later
1990 	fAttrMenu = new BMenu(B_TRANSLATE("Attributes"));
1991 	NewAttributeMenu(fAttrMenu);
1992 	PopulateArrangeByMenu(fArrangeByMenu);
1993 }
1994 
1995 
1996 void
1997 BContainerWindow::AddFileMenu(BMenu* menu)
1998 {
1999 	if (!PoseView()->IsFilePanel()) {
2000 		menu->AddItem(new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS),
2001 			new BMessage(kFindButton), 'F'));
2002 	}
2003 
2004 	if (!TargetModel()->IsQuery() && !TargetModel()->IsVirtualDirectory()
2005 		&& !IsTrash() && !IsPrintersDir() && !TargetModel()->IsRoot()) {
2006 		if (!PoseView()->IsFilePanel()) {
2007 			TemplatesMenu* templatesMenu = new TemplatesMenu(PoseView(),
2008 				B_TRANSLATE("New"));
2009 			menu->AddItem(templatesMenu);
2010 			templatesMenu->SetTargetForItems(PoseView());
2011 		} else {
2012 			menu->AddItem(new BMenuItem(B_TRANSLATE("New folder"),
2013 				new BMessage(kNewFolder), 'N'));
2014 		}
2015 	}
2016 	menu->AddSeparatorItem();
2017 
2018 	menu->AddItem(new BMenuItem(B_TRANSLATE("Open"),
2019 		new BMessage(kOpenSelection), 'O'));
2020 	menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
2021 		new BMessage(kGetInfo), 'I'));
2022 	menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"),
2023 		new BMessage(kEditItem), 'E'));
2024 
2025 	if (IsTrash() || InTrash()) {
2026 		menu->AddItem(new BMenuItem(B_TRANSLATE("Restore"),
2027 			new BMessage(kRestoreFromTrash)));
2028 		if (IsTrash()) {
2029 			// add as first item in menu
2030 			menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"),
2031 				new BMessage(kEmptyTrash)), 0);
2032 			menu->AddItem(new BSeparatorItem(), 1);
2033 		}
2034 	} else if (IsPrintersDir()) {
2035 		menu->AddItem(new BMenuItem(B_TRANSLATE("Add printer" B_UTF8_ELLIPSIS),
2036 			new BMessage(kAddPrinter), 'N'), 0);
2037 		menu->AddItem(new BSeparatorItem(), 1);
2038 		menu->AddItem(new BMenuItem(B_TRANSLATE("Make active printer"),
2039 			new BMessage(kMakeActivePrinter)));
2040 	} else if (TargetModel()->IsRoot()) {
2041 		BMenuItem* item = new BMenuItem(B_TRANSLATE("Unmount"),
2042 			new BMessage(kUnmountVolume), 'U');
2043 		item->SetEnabled(false);
2044 		menu->AddItem(item);
2045 		menu->AddItem(new BMenuItem(
2046 			B_TRANSLATE("Mount settings" B_UTF8_ELLIPSIS),
2047 			new BMessage(kRunAutomounterSettings)));
2048 	} else {
2049 		menu->AddItem(new BMenuItem(B_TRANSLATE("Duplicate"),
2050 			new BMessage(kDuplicateSelection), 'D'));
2051 		menu->AddItem(new BMenuItem(TrackerSettings().DontMoveFilesToTrash()
2052 			? B_TRANSLATE("Delete") : B_TRANSLATE("Move to Trash"),
2053 			new BMessage(kMoveToTrash), 'T'));
2054 		menu->AddSeparatorItem();
2055 
2056 		// The "Move To", "Copy To", "Create Link" menus are inserted
2057 		// at this place, have a look at:
2058 		// BContainerWindow::SetupMoveCopyMenus()
2059 	}
2060 
2061 	BMenuItem* cutItem = NULL;
2062 	BMenuItem* copyItem = NULL;
2063 	BMenuItem* pasteItem = NULL;
2064 	if (!IsPrintersDir()) {
2065 		menu->AddSeparatorItem();
2066 
2067 		if (!TargetModel()->IsRoot()) {
2068 			cutItem = new(std::nothrow) BMenuItem(B_TRANSLATE("Cut"),
2069 				new BMessage(B_CUT), 'X');
2070 			menu->AddItem(cutItem);
2071 			copyItem = new(std::nothrow) BMenuItem(B_TRANSLATE("Copy"),
2072 				new BMessage(B_COPY), 'C');
2073 			menu->AddItem(copyItem);
2074 			pasteItem = new(std::nothrow) BMenuItem(B_TRANSLATE("Paste"),
2075 				new BMessage(B_PASTE), 'V');
2076 			menu->AddItem(pasteItem);
2077 			menu->AddSeparatorItem();
2078 			menu->AddItem(new BMenuItem(B_TRANSLATE("Identify"),
2079 				new BMessage(kIdentifyEntry)));
2080 		}
2081 		BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons"));
2082 		addOnMenuItem->SetFont(be_plain_font);
2083 		menu->AddItem(addOnMenuItem);
2084 	}
2085 
2086 	menu->SetTargetForItems(PoseView());
2087 	if (cutItem != NULL)
2088 		cutItem->SetTarget(this);
2089 
2090 	if (copyItem != NULL)
2091 		copyItem->SetTarget(this);
2092 
2093 	if (pasteItem != NULL)
2094 		pasteItem->SetTarget(this);
2095 }
2096 
2097 
2098 void
2099 BContainerWindow::AddWindowMenu(BMenu* menu)
2100 {
2101 	BMenuItem* item;
2102 
2103 	BMenu* iconSizeMenu = new BMenu(B_TRANSLATE("Icon view"));
2104 
2105 	BMessage* message = new BMessage(kIconMode);
2106 	message->AddInt32("size", 32);
2107 	item = new BMenuItem(B_TRANSLATE("32 x 32"), message);
2108 	item->SetTarget(PoseView());
2109 	iconSizeMenu->AddItem(item);
2110 
2111 	message = new BMessage(kIconMode);
2112 	message->AddInt32("size", 40);
2113 	item = new BMenuItem(B_TRANSLATE("40 x 40"), message);
2114 	item->SetTarget(PoseView());
2115 	iconSizeMenu->AddItem(item);
2116 
2117 	message = new BMessage(kIconMode);
2118 	message->AddInt32("size", 48);
2119 	item = new BMenuItem(B_TRANSLATE("48 x 48"), message);
2120 	item->SetTarget(PoseView());
2121 	iconSizeMenu->AddItem(item);
2122 
2123 	message = new BMessage(kIconMode);
2124 	message->AddInt32("size", 64);
2125 	item = new BMenuItem(B_TRANSLATE("64 x 64"), message);
2126 	item->SetTarget(PoseView());
2127 	iconSizeMenu->AddItem(item);
2128 
2129 	message = new BMessage(kIconMode);
2130 	message->AddInt32("size", 96);
2131 	item = new BMenuItem(B_TRANSLATE("96 x 96"), message);
2132 	item->SetTarget(PoseView());
2133 	iconSizeMenu->AddItem(item);
2134 
2135 	message = new BMessage(kIconMode);
2136 	message->AddInt32("size", 128);
2137 	item = new BMenuItem(B_TRANSLATE("128 x 128"), message);
2138 	item->SetTarget(PoseView());
2139 	iconSizeMenu->AddItem(item);
2140 
2141 	iconSizeMenu->AddSeparatorItem();
2142 
2143 	message = new BMessage(kIconMode);
2144 	message->AddInt32("scale", 0);
2145 	item = new BMenuItem(B_TRANSLATE("Decrease size"), message, '-');
2146 	item->SetTarget(PoseView());
2147 	iconSizeMenu->AddItem(item);
2148 
2149 	message = new BMessage(kIconMode);
2150 	message->AddInt32("scale", 1);
2151 	item = new BMenuItem(B_TRANSLATE("Increase size"), message, '+');
2152 	item->SetTarget(PoseView());
2153 	iconSizeMenu->AddItem(item);
2154 
2155 	// A sub menu where the super item can be invoked.
2156 	menu->AddItem(iconSizeMenu);
2157 	iconSizeMenu->Superitem()->SetShortcut('1', B_COMMAND_KEY);
2158 	iconSizeMenu->Superitem()->SetMessage(new BMessage(kIconMode));
2159 	iconSizeMenu->Superitem()->SetTarget(PoseView());
2160 
2161 	item = new BMenuItem(B_TRANSLATE("Mini icon view"),
2162 		new BMessage(kMiniIconMode), '2');
2163 	item->SetTarget(PoseView());
2164 	menu->AddItem(item);
2165 
2166 	item = new BMenuItem(B_TRANSLATE("List view"),
2167 		new BMessage(kListMode), '3');
2168 	item->SetTarget(PoseView());
2169 	menu->AddItem(item);
2170 
2171 	menu->AddSeparatorItem();
2172 
2173 	item = new BMenuItem(B_TRANSLATE("Resize to fit"),
2174 		new BMessage(kResizeToFit), 'Y');
2175 	item->SetTarget(this);
2176 	menu->AddItem(item);
2177 
2178 	fArrangeByMenu = new BMenu(B_TRANSLATE("Arrange by"));
2179 	menu->AddItem(fArrangeByMenu);
2180 
2181 	item = new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS),
2182 		new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY);
2183 	item->SetTarget(PoseView());
2184 	menu->AddItem(item);
2185 
2186 	item = new BMenuItem(B_TRANSLATE("Select all"),
2187 		new BMessage(B_SELECT_ALL), 'A');
2188 	item->SetTarget(PoseView());
2189 	menu->AddItem(item);
2190 
2191 	item = new BMenuItem(B_TRANSLATE("Invert selection"),
2192 		new BMessage(kInvertSelection), 'S');
2193 	item->SetTarget(PoseView());
2194 	menu->AddItem(item);
2195 
2196 	if (!IsTrash()) {
2197 		item = new BMenuItem(B_TRANSLATE("Open parent"),
2198 			new BMessage(kOpenParentDir), B_UP_ARROW);
2199 		item->SetTarget(PoseView());
2200 		menu->AddItem(item);
2201 	}
2202 
2203 	item = new BMenuItem(B_TRANSLATE("Close"),
2204 		new BMessage(B_QUIT_REQUESTED), 'W');
2205 	item->SetTarget(this);
2206 	menu->AddItem(item);
2207 
2208 	item = new BMenuItem(B_TRANSLATE("Close all in workspace"),
2209 		new BMessage(kCloseAllInWorkspace), 'Q');
2210 	item->SetTarget(be_app);
2211 	menu->AddItem(item);
2212 
2213 	menu->AddSeparatorItem();
2214 
2215 	item = new BMenuItem(B_TRANSLATE("Preferences" B_UTF8_ELLIPSIS),
2216 		new BMessage(kShowSettingsWindow), ',');
2217 	item->SetTarget(be_app);
2218 	menu->AddItem(item);
2219 }
2220 
2221 
2222 void
2223 BContainerWindow::AddShortcuts()
2224 {
2225 	// add equivalents of the menu shortcuts to the menuless desktop window
2226 	ASSERT(!IsTrash());
2227 	ASSERT(!PoseView()->IsFilePanel());
2228 	ASSERT(!TargetModel()->IsQuery());
2229 	ASSERT(!TargetModel()->IsVirtualDirectory());
2230 
2231 	AddShortcut('X', B_COMMAND_KEY | B_SHIFT_KEY,
2232 		new BMessage(kCutMoreSelectionToClipboard), this);
2233 	AddShortcut('C', B_COMMAND_KEY | B_SHIFT_KEY,
2234 		new BMessage(kCopyMoreSelectionToClipboard), this);
2235 	AddShortcut('F', B_COMMAND_KEY,
2236 		new BMessage(kFindButton), PoseView());
2237 	AddShortcut('N', B_COMMAND_KEY,
2238 		new BMessage(kNewFolder), PoseView());
2239 	AddShortcut('O', B_COMMAND_KEY,
2240 		new BMessage(kOpenSelection), PoseView());
2241 	AddShortcut('I', B_COMMAND_KEY,
2242 		new BMessage(kGetInfo), PoseView());
2243 	AddShortcut('E', B_COMMAND_KEY,
2244 		new BMessage(kEditItem), PoseView());
2245 	AddShortcut('D', B_COMMAND_KEY,
2246 		new BMessage(kDuplicateSelection), PoseView());
2247 	AddShortcut('T', B_COMMAND_KEY,
2248 		new BMessage(kMoveToTrash), PoseView());
2249 	AddShortcut('K', B_COMMAND_KEY,
2250 		new BMessage(kCleanup), PoseView());
2251 	AddShortcut('A', B_COMMAND_KEY,
2252 		new BMessage(B_SELECT_ALL), PoseView());
2253 	AddShortcut('S', B_COMMAND_KEY,
2254 		new BMessage(kInvertSelection), PoseView());
2255 	AddShortcut('A', B_COMMAND_KEY | B_SHIFT_KEY,
2256 		new BMessage(kShowSelectionWindow), PoseView());
2257 	AddShortcut('G', B_COMMAND_KEY,
2258 		new BMessage(kEditQuery), PoseView());
2259 		// it is ok to add a global Edit query shortcut here, PoseView will
2260 		// filter out cases where selected pose is not a query
2261 	AddShortcut('U', B_COMMAND_KEY,
2262 		new BMessage(kUnmountVolume), PoseView());
2263 	AddShortcut(B_UP_ARROW, B_COMMAND_KEY,
2264 		new BMessage(kOpenParentDir), PoseView());
2265 	AddShortcut('O', B_COMMAND_KEY | B_CONTROL_KEY,
2266 		new BMessage(kOpenSelectionWith), PoseView());
2267 
2268 	BMessage* decreaseSize = new BMessage(kIconMode);
2269 	decreaseSize->AddInt32("scale", 0);
2270 	AddShortcut('-', B_COMMAND_KEY, decreaseSize, PoseView());
2271 
2272 	BMessage* increaseSize = new BMessage(kIconMode);
2273 	increaseSize->AddInt32("scale", 1);
2274 	AddShortcut('+', B_COMMAND_KEY, increaseSize, PoseView());
2275 }
2276 
2277 
2278 void
2279 BContainerWindow::MenusBeginning()
2280 {
2281 	if (fMenuBar == NULL)
2282 		return;
2283 
2284 	if (CurrentMessage() != NULL && CurrentMessage()->what == B_MOUSE_DOWN) {
2285 		// don't commit active pose if only a keyboard shortcut is
2286 		// invoked - this would prevent Cut/Copy/Paste from working
2287 		fPoseView->CommitActivePose();
2288 	}
2289 
2290 	// File menu
2291 	int32 selectCount = PoseView()->SelectionList()->CountItems();
2292 
2293 	SetupOpenWithMenu(fFileMenu);
2294 	SetupMoveCopyMenus(selectCount
2295 		? PoseView()->SelectionList()->FirstItem()->TargetModel()->EntryRef()
2296 		: NULL, fFileMenu);
2297 
2298 	if (TargetModel()->IsRoot()) {
2299 		BVolume boot;
2300 		BVolumeRoster().GetBootVolume(&boot);
2301 
2302 		bool ejectableVolumeSelected = false;
2303 
2304 		int32 count = PoseView()->SelectionList()->CountItems();
2305 		for (int32 index = 0; index < count; 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()->SelectionList()->CountItems();
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 	if (PoseView()->SelectionList()->CountItems() == 0) {
2473 		// no selection, nothing to open
2474 		return;
2475 	}
2476 
2477 	if (TargetModel()->IsRoot()) {
2478 		// don't add ourselves if we are root
2479 		return;
2480 	}
2481 
2482 	// ToDo:
2483 	// check if only item in selection list is the root
2484 	// and do not add if true
2485 
2486 	// add after "Open"
2487 	BMenuItem* item = parent->FindItem(kOpenSelection);
2488 
2489 	int32 count = PoseView()->SelectionList()->CountItems();
2490 	if (count == 0)
2491 		return;
2492 
2493 	// build a list of all refs to open
2494 	BMessage message(B_REFS_RECEIVED);
2495 	for (int32 index = 0; index < count; index++) {
2496 		BPose* pose = PoseView()->SelectionList()->ItemAt(index);
2497 		message.AddRef("refs", pose->TargetModel()->EntryRef());
2498 	}
2499 
2500 	// add Tracker token so that refs received recipients can script us
2501 	message.AddMessenger("TrackerViewToken", BMessenger(PoseView()));
2502 
2503 	int32 index = item->Menu()->IndexOf(item);
2504 	fOpenWithItem = new BMenuItem(
2505 		new OpenWithMenu(B_TRANSLATE("Open with" B_UTF8_ELLIPSIS),
2506 			&message, this, be_app), new BMessage(kOpenSelectionWith));
2507 	fOpenWithItem->SetTarget(PoseView());
2508 	fOpenWithItem->SetShortcut('O', B_COMMAND_KEY | B_CONTROL_KEY);
2509 
2510 	item->Menu()->AddItem(fOpenWithItem, index + 1);
2511 }
2512 
2513 
2514 void
2515 BContainerWindow::PopulateMoveCopyNavMenu(BNavMenu* navMenu, uint32 what,
2516 	const entry_ref* ref, bool addLocalOnly)
2517 {
2518 	BVolume volume;
2519 	BVolumeRoster volumeRoster;
2520 	BDirectory directory;
2521 	BEntry entry;
2522 	BPath path;
2523 	Model model;
2524 	dev_t device = ref->device;
2525 
2526 	int32 volumeCount = 0;
2527 
2528 	navMenu->RemoveItems(0, navMenu->CountItems(), true);
2529 
2530 	// count persistent writable volumes
2531 	volumeRoster.Rewind();
2532 	while (volumeRoster.GetNextVolume(&volume) == B_OK) {
2533 		if (!volume.IsReadOnly() && volume.IsPersistent())
2534 			volumeCount++;
2535 	}
2536 
2537 	// add the current folder
2538 	if (entry.SetTo(ref) == B_OK
2539 		&& entry.GetParent(&entry) == B_OK
2540 		&& model.SetTo(&entry) == B_OK) {
2541 		BNavMenu* menu = new BNavMenu(B_TRANSLATE("Current folder"), what,
2542 			this);
2543 		menu->SetNavDir(model.EntryRef());
2544 		menu->SetShowParent(true);
2545 
2546 		BMenuItem* item = new SpecialModelMenuItem(&model,menu);
2547 		item->SetMessage(new BMessage((uint32)what));
2548 
2549 		navMenu->AddItem(item);
2550 	}
2551 
2552 	// add the recent folder menu
2553 	// the "Tracker" settings directory is only used to get its icon
2554 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
2555 		path.Append("Tracker");
2556 		if (entry.SetTo(path.Path()) == B_OK
2557 			&& model.SetTo(&entry) == B_OK) {
2558 			BMenu* menu = new RecentsMenu(B_TRANSLATE("Recent folders"),
2559 				kRecentFolders, what, this);
2560 
2561 			BMenuItem* item = new SpecialModelMenuItem(&model,menu);
2562 			item->SetMessage(new BMessage((uint32)what));
2563 
2564 			navMenu->AddItem(item);
2565 		}
2566 	}
2567 
2568 	// add Desktop
2569 	FSGetBootDeskDir(&directory);
2570 	if (directory.InitCheck() == B_OK && directory.GetEntry(&entry) == B_OK
2571 		&& model.SetTo(&entry) == B_OK) {
2572 		navMenu->AddNavDir(&model, what, this, true);
2573 			// ask NavMenu to populate submenu for us
2574 	}
2575 
2576 	// add the home dir
2577 	if (find_directory(B_USER_DIRECTORY, &path) == B_OK
2578 		&& entry.SetTo(path.Path()) == B_OK && model.SetTo(&entry) == B_OK) {
2579 		navMenu->AddNavDir(&model, what, this, true);
2580 	}
2581 
2582 	navMenu->AddSeparatorItem();
2583 
2584 	// either add all mounted volumes (for copy), or all the top-level
2585 	// directories from the same device (for move)
2586 	// ToDo: can be changed if cross-device moves are implemented
2587 
2588 	if (addLocalOnly || volumeCount < 2) {
2589 		// add volume this item lives on
2590 		if (volume.SetTo(device) == B_OK
2591 			&& volume.GetRootDirectory(&directory) == B_OK
2592 			&& directory.GetEntry(&entry) == B_OK
2593 			&& model.SetTo(&entry) == B_OK) {
2594 			navMenu->AddNavDir(&model, what, this, false);
2595 				// do not have submenu populated
2596 
2597 			navMenu->SetNavDir(model.EntryRef());
2598 		}
2599 	} else {
2600 		// add all persistent writable volumes
2601 		volumeRoster.Rewind();
2602 		while (volumeRoster.GetNextVolume(&volume) == B_OK) {
2603 			if (volume.IsReadOnly() || !volume.IsPersistent())
2604 				continue;
2605 
2606 			// add root dir
2607 			if (volume.GetRootDirectory(&directory) == B_OK
2608 				&& directory.GetEntry(&entry) == B_OK
2609 				&& model.SetTo(&entry) == B_OK) {
2610 				navMenu->AddNavDir(&model, what, this, true);
2611 					// ask NavMenu to populate submenu for us
2612 			}
2613 		}
2614 	}
2615 }
2616 
2617 
2618 void
2619 BContainerWindow::SetupMoveCopyMenus(const entry_ref* item_ref, BMenu* parent)
2620 {
2621 	if (IsTrash() || InTrash() || IsPrintersDir() || fMoveToItem == NULL
2622 		|| fCopyToItem == NULL || fCreateLinkItem == NULL
2623 		|| TargetModel()->IsRoot()) {
2624 		return;
2625 	}
2626 
2627 	// re-parent items to this menu since they're shared
2628 	BMenuItem* trash = parent->FindItem(kMoveToTrash);
2629 	int32 index = trash != NULL ? parent->IndexOf(trash) + 2 : 0;
2630 
2631 	if (fMoveToItem->Menu() != parent) {
2632 		if (fMoveToItem->Menu() != NULL)
2633 			fMoveToItem->Menu()->RemoveItem(fMoveToItem);
2634 
2635 		parent->AddItem(fMoveToItem, index++);
2636 	}
2637 
2638 	if (fCopyToItem->Menu() != parent) {
2639 		if (fCopyToItem->Menu() != NULL)
2640 			fCopyToItem->Menu()->RemoveItem(fCopyToItem);
2641 
2642 		parent->AddItem(fCopyToItem, index++);
2643 	}
2644 
2645 	if (fCreateLinkItem->Menu() != parent) {
2646 		if (fCreateLinkItem->Menu() != NULL)
2647 			fCreateLinkItem->Menu()->RemoveItem(fCreateLinkItem);
2648 
2649 		parent->AddItem(fCreateLinkItem, index);
2650 	}
2651 
2652 	// Set the "Create Link" item label here so it
2653 	// appears correctly when menus are disabled, too.
2654 	if ((modifiers() & B_SHIFT_KEY) != 0)
2655 		fCreateLinkItem->SetLabel(B_TRANSLATE("Create relative link"));
2656 	else
2657 		fCreateLinkItem->SetLabel(B_TRANSLATE("Create link"));
2658 
2659 	// only enable once the menus are built
2660 	fMoveToItem->SetEnabled(false);
2661 	fCopyToItem->SetEnabled(false);
2662 	fCreateLinkItem->SetEnabled(false);
2663 
2664 	// get ref for item which is selected
2665 	BEntry entry;
2666 	if (entry.SetTo(item_ref) != B_OK)
2667 		return;
2668 
2669 	Model tempModel(&entry);
2670 	if (tempModel.InitCheck() != B_OK)
2671 		return;
2672 
2673 	if (tempModel.IsRoot() || tempModel.IsVolume())
2674 		return;
2675 
2676 	// configure "Move to" menu item
2677 	PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>(fMoveToItem->Submenu()),
2678 		kMoveSelectionTo, item_ref, true);
2679 
2680 	// configure "Copy to" menu item
2681 	// add all mounted volumes (except the one this item lives on)
2682 	PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>(fCopyToItem->Submenu()),
2683 		kCopySelectionTo, item_ref, false);
2684 
2685 	// Set "Create Link" menu item message and
2686 	// add all mounted volumes (except the one this item lives on)
2687 	if ((modifiers() & B_SHIFT_KEY) != 0) {
2688 		fCreateLinkItem->SetMessage(new BMessage(kCreateRelativeLink));
2689 		PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>
2690 				(fCreateLinkItem->Submenu()),
2691 			kCreateRelativeLink, item_ref, false);
2692 	} else {
2693 		fCreateLinkItem->SetMessage(new BMessage(kCreateLink));
2694 		PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>
2695 				(fCreateLinkItem->Submenu()),
2696 			kCreateLink, item_ref, false);
2697 	}
2698 
2699 	fMoveToItem->SetEnabled(true);
2700 	fCopyToItem->SetEnabled(true);
2701 	fCreateLinkItem->SetEnabled(true);
2702 
2703 	// Set the "Identify" item label
2704 	BMenuItem* identifyItem = parent->FindItem(kIdentifyEntry);
2705 	if (identifyItem != NULL) {
2706 		if ((modifiers() & B_SHIFT_KEY) != 0) {
2707 			identifyItem->SetLabel(B_TRANSLATE("Force identify"));
2708 			identifyItem->Message()->ReplaceBool("force", true);
2709 		} else {
2710 			identifyItem->SetLabel(B_TRANSLATE("Identify"));
2711 			identifyItem->Message()->ReplaceBool("force", false);
2712 		}
2713 	}
2714 }
2715 
2716 
2717 uint32
2718 BContainerWindow::ShowDropContextMenu(BPoint loc)
2719 {
2720 	BPoint global(loc);
2721 
2722 	PoseView()->ConvertToScreen(&global);
2723 	PoseView()->CommitActivePose();
2724 
2725 	// Change the "Create Link" item - allow user to
2726 	// create relative links with the Shift key down.
2727 	BMenuItem* item = fDropContextMenu->FindItem(kCreateLink);
2728 	if (item == NULL)
2729 		item = fDropContextMenu->FindItem(kCreateRelativeLink);
2730 
2731 	if (item != NULL && (modifiers() & B_SHIFT_KEY) != 0) {
2732 		item->SetLabel(B_TRANSLATE("Create relative link here"));
2733 		item->SetMessage(new BMessage(kCreateRelativeLink));
2734 	} else if (item != NULL) {
2735 		item->SetLabel(B_TRANSLATE("Create link here"));
2736 		item->SetMessage(new BMessage(kCreateLink));
2737 	}
2738 
2739 	item = fDropContextMenu->Go(global, true, true);
2740 	if (item != NULL)
2741 		return item->Command();
2742 
2743 	return 0;
2744 }
2745 
2746 
2747 void
2748 BContainerWindow::ShowContextMenu(BPoint loc, const entry_ref* ref, BView*)
2749 {
2750 	ASSERT(IsLocked());
2751 	BPoint global(loc);
2752 	PoseView()->ConvertToScreen(&global);
2753 	PoseView()->CommitActivePose();
2754 
2755 	if (ref != NULL) {
2756 		// clicked on a pose, show file or volume context menu
2757 		Model model(ref);
2758 
2759 		if (model.IsTrash()) {
2760 			if (fTrashContextMenu->Window() || Dragging())
2761 				return;
2762 
2763 			DeleteSubmenu(fNavigationItem);
2764 
2765 			// selected item was trash, show the trash context menu instead
2766 
2767 			EnableNamedMenuItem(fTrashContextMenu, kEmptyTrash,
2768 				static_cast<TTracker*>(be_app)->TrashFull());
2769 
2770 			SetupNavigationMenu(ref, fTrashContextMenu);
2771 			fTrashContextMenu->Go(global, true, true, true);
2772 		} else {
2773 			bool showAsVolume = false;
2774 			bool isFilePanel = PoseView()->IsFilePanel();
2775 
2776 			if (Dragging()) {
2777 				fContextMenu = NULL;
2778 
2779 				BEntry entry;
2780 				model.GetEntry(&entry);
2781 
2782 				// only show for directories (directory, volume, root)
2783 				//
2784 				// don't show a popup for the trash or printers
2785 				// trash is handled in DeskWindow
2786 				//
2787 				// since this menu is opened asynchronously
2788 				// we need to make sure we don't open it more
2789 				// than once, the IsShowing flag is set in
2790 				// SlowContextPopup::AttachedToWindow and
2791 				// reset in DetachedFromWindow
2792 				// see the notes in SlowContextPopup::AttachedToWindow
2793 
2794 				if (!FSIsPrintersDir(&entry)
2795 					&& !fDragContextMenu->IsShowing()) {
2796 					//printf("ShowContextMenu - target is %s %i\n",
2797 					//	ref->name, IsShowing(ref));
2798 					fDragContextMenu->ClearMenu();
2799 
2800 					// in case the ref is a symlink, resolve it
2801 					// only pop open for directories
2802 					BEntry resolvedEntry(ref, true);
2803 					if (!resolvedEntry.IsDirectory())
2804 						return;
2805 
2806 					entry_ref resolvedRef;
2807 					resolvedEntry.GetRef(&resolvedRef);
2808 
2809 					// use the resolved ref for the menu
2810 					fDragContextMenu->SetNavDir(&resolvedRef);
2811 					fDragContextMenu->SetTypesList(fCachedTypesList);
2812 					fDragContextMenu->SetTarget(BMessenger(this));
2813 					BPoseView* poseView = PoseView();
2814 					if (poseView != NULL) {
2815 						BMessenger target(poseView);
2816 						fDragContextMenu->InitTrackingHook(
2817 							&BPoseView::MenuTrackingHook, &target,
2818 							fDragMessage);
2819 					}
2820 
2821 					// this is now asynchronous so that we don't
2822 					// deadlock in Window::Quit,
2823 					fDragContextMenu->Go(global, true, false, true);
2824 				}
2825 
2826 				return;
2827 			} else if (TargetModel()->IsRoot() || model.IsVolume()) {
2828 				fContextMenu = fVolumeContextMenu;
2829 				showAsVolume = true;
2830 			} else
2831 				fContextMenu = fFileContextMenu;
2832 
2833 			// clean up items from last context menu
2834 
2835 			if (fContextMenu != NULL) {
2836 				if (fContextMenu->Window())
2837 					return;
2838 				else
2839 					MenusEnded();
2840 
2841 				if (model.InitCheck() == B_OK) { // ??? Do I need this ???
2842 					if (showAsVolume) {
2843 						// non-volume enable/disable copy, move, identify
2844 						EnableNamedMenuItem(fContextMenu, kDuplicateSelection,
2845 							false);
2846 						EnableNamedMenuItem(fContextMenu, kMoveToTrash, false);
2847 						EnableNamedMenuItem(fContextMenu, kIdentifyEntry,
2848 							false);
2849 
2850 						// volume model, enable/disable the Unmount item
2851 						bool ejectableVolumeSelected = false;
2852 
2853 						BVolume boot;
2854 						BVolumeRoster().GetBootVolume(&boot);
2855 						BVolume volume;
2856 						volume.SetTo(model.NodeRef()->device);
2857 						if (volume != boot)
2858 							ejectableVolumeSelected = true;
2859 
2860 						EnableNamedMenuItem(fContextMenu,
2861 							B_TRANSLATE("Unmount"),	ejectableVolumeSelected);
2862 					}
2863 				}
2864 
2865 				SetupNavigationMenu(ref, fContextMenu);
2866 				if (!showAsVolume && !isFilePanel) {
2867 					SetupMoveCopyMenus(ref, fContextMenu);
2868 					SetupOpenWithMenu(fContextMenu);
2869 				}
2870 
2871 				UpdateMenu(fContextMenu, kPosePopUpContext);
2872 
2873 				fContextMenu->Go(global, true, true, true);
2874 			}
2875 		}
2876 	} else if (fWindowContextMenu != NULL) {
2877 		if (fWindowContextMenu->Window())
2878 			return;
2879 
2880 		// Repopulate desktop menu if IsDesktop
2881 		if (fIsDesktop)
2882 			RepopulateMenus();
2883 
2884 		MenusEnded();
2885 
2886 		// clicked on a window, show window context menu
2887 
2888 		SetupNavigationMenu(ref, fWindowContextMenu);
2889 		UpdateMenu(fWindowContextMenu, kWindowPopUpContext);
2890 
2891 		fWindowContextMenu->Go(global, true, true, true);
2892 	}
2893 
2894 	fContextMenu = NULL;
2895 }
2896 
2897 
2898 void
2899 BContainerWindow::AddFileContextMenus(BMenu* menu)
2900 {
2901 	menu->AddItem(new BMenuItem(B_TRANSLATE("Open"),
2902 		new BMessage(kOpenSelection), 'O'));
2903 	menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
2904 		new BMessage(kGetInfo), 'I'));
2905 	menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"),
2906 		new BMessage(kEditItem), 'E'));
2907 
2908 	if (!IsTrash() && !InTrash() && !IsPrintersDir()) {
2909 		menu->AddItem(new BMenuItem(B_TRANSLATE("Duplicate"),
2910 			new BMessage(kDuplicateSelection), 'D'));
2911 	}
2912 
2913 	if (!IsTrash() && !InTrash()) {
2914 		menu->AddItem(new BMenuItem(TrackerSettings().DontMoveFilesToTrash()
2915 			? B_TRANSLATE("Delete")	: B_TRANSLATE("Move to Trash"),
2916 			new BMessage(kMoveToTrash), 'T'));
2917 		if (!IsPrintersDir()) {
2918 			// add separator for copy to/move to items (navigation items)
2919 			menu->AddSeparatorItem();
2920 		}
2921 	} else {
2922 		menu->AddItem(new BMenuItem(B_TRANSLATE("Delete"),
2923 			new BMessage(kDelete), 0));
2924 		menu->AddItem(new BMenuItem(B_TRANSLATE("Restore"),
2925 			new BMessage(kRestoreFromTrash), 0));
2926 	}
2927 
2928 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
2929 	menu->AddSeparatorItem();
2930 	BMenuItem* cutItem = new BMenuItem(B_TRANSLATE("Cut"),
2931 		new BMessage(B_CUT), 'X');
2932 	menu->AddItem(cutItem);
2933 	BMenuItem* copyItem = new BMenuItem(B_TRANSLATE("Copy"),
2934 		new BMessage(B_COPY), 'C');
2935 	menu->AddItem(copyItem);
2936 #endif
2937 
2938 	menu->AddSeparatorItem();
2939 
2940 	BMessage* message = new BMessage(kIdentifyEntry);
2941 	message->AddBool("force", false);
2942 	menu->AddItem(new BMenuItem(B_TRANSLATE("Identify"), message));
2943 
2944 	BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons"));
2945 	addOnMenuItem->SetFont(be_plain_font);
2946 	menu->AddItem(addOnMenuItem);
2947 
2948 	// set targets as needed
2949 	menu->SetTargetForItems(PoseView());
2950 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
2951 	cutItem->SetTarget(this);
2952 	copyItem->SetTarget(this);
2953 #endif
2954 }
2955 
2956 
2957 void
2958 BContainerWindow::AddVolumeContextMenus(BMenu* menu)
2959 {
2960 	menu->AddItem(new BMenuItem(B_TRANSLATE("Open"),
2961 		new BMessage(kOpenSelection), 'O'));
2962 	menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
2963 		new BMessage(kGetInfo), 'I'));
2964 	menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"),
2965 		new BMessage(kEditItem), 'E'));
2966 
2967 	menu->AddSeparatorItem();
2968 	menu->AddItem(new MountMenu(B_TRANSLATE("Mount")));
2969 
2970 	BMenuItem* item = new BMenuItem(B_TRANSLATE("Unmount"),
2971 		new BMessage(kUnmountVolume), 'U');
2972 	item->SetEnabled(false);
2973 	menu->AddItem(item);
2974 
2975 	menu->AddSeparatorItem();
2976 	menu->AddItem(new BMenu(B_TRANSLATE("Add-ons")));
2977 
2978 	menu->SetTargetForItems(PoseView());
2979 }
2980 
2981 
2982 void
2983 BContainerWindow::AddWindowContextMenus(BMenu* menu)
2984 {
2985 	// create context sensitive menu for empty area of window
2986 	// since we check view mode before display, this should be a radio
2987 	// mode menu
2988 
2989 	Model* targetModel = TargetModel();
2990 	ASSERT(targetModel != NULL);
2991 
2992 	bool needSeparator = true;
2993 	if (IsTrash()) {
2994 		menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"),
2995 			new BMessage(kEmptyTrash)));
2996 	} else if (IsPrintersDir()) {
2997 		menu->AddItem(new BMenuItem(B_TRANSLATE("Add printer" B_UTF8_ELLIPSIS),
2998 			new BMessage(kAddPrinter), 'N'));
2999 	} else if (InTrash() || targetModel->IsRoot()) {
3000 		needSeparator = false;
3001 	} else {
3002 		TemplatesMenu* templatesMenu = new TemplatesMenu(PoseView(),
3003 			B_TRANSLATE("New"));
3004 		menu->AddItem(templatesMenu);
3005 		templatesMenu->SetTargetForItems(PoseView());
3006 		templatesMenu->SetFont(be_plain_font);
3007 	}
3008 
3009 	if (needSeparator)
3010 		menu->AddSeparatorItem();
3011 
3012 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
3013 	BMenuItem* pasteItem = new BMenuItem("Paste", new BMessage(B_PASTE), 'V');
3014 	menu->AddItem(pasteItem);
3015 	menu->AddSeparatorItem();
3016 #endif
3017 
3018 	BMenu* arrangeBy = new BMenu(B_TRANSLATE("Arrange by"));
3019 	PopulateArrangeByMenu(arrangeBy);
3020 	menu->AddItem(arrangeBy);
3021 
3022 	menu->AddItem(new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS),
3023 		new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY));
3024 	menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"),
3025 		new BMessage(B_SELECT_ALL), 'A'));
3026 	if (!IsTrash()) {
3027 		menu->AddItem(new BMenuItem(B_TRANSLATE("Open parent"),
3028 			new BMessage(kOpenParentDir), B_UP_ARROW));
3029 	}
3030 
3031 	if (targetModel->IsRoot()) {
3032 		menu->AddSeparatorItem();
3033 		menu->AddItem(new MountMenu(B_TRANSLATE("Mount")));
3034 	}
3035 
3036 	menu->AddSeparatorItem();
3037 	BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons"));
3038 	addOnMenuItem->SetFont(be_plain_font);
3039 	menu->AddItem(addOnMenuItem);
3040 
3041 #if DEBUG
3042 	menu->AddSeparatorItem();
3043 	BMenuItem* testing = new BMenuItem("Test icon cache",
3044 		new BMessage(kTestIconCache));
3045 	menu->AddItem(testing);
3046 #endif
3047 
3048 	// target items as needed
3049 	menu->SetTargetForItems(PoseView());
3050 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
3051 	pasteItem->SetTarget(this);
3052 #endif
3053 }
3054 
3055 
3056 void
3057 BContainerWindow::AddDropContextMenus(BMenu* menu)
3058 {
3059 	menu->AddItem(new BMenuItem(B_TRANSLATE("Create link here"),
3060 		new BMessage(kCreateLink)));
3061 	menu->AddItem(new BMenuItem(B_TRANSLATE("Move here"),
3062 		new BMessage(kMoveSelectionTo)));
3063 	menu->AddItem(new BMenuItem(B_TRANSLATE("Copy here"),
3064 		new BMessage(kCopySelectionTo)));
3065 	menu->AddSeparatorItem();
3066 	menu->AddItem(new BMenuItem(B_TRANSLATE("Cancel"),
3067 		new BMessage(kCancelButton)));
3068 }
3069 
3070 
3071 void
3072 BContainerWindow::AddTrashContextMenus(BMenu* menu)
3073 {
3074 	// setup special trash context menu
3075 	menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"),
3076 		new BMessage(kEmptyTrash)));
3077 	menu->AddItem(new BMenuItem(B_TRANSLATE("Open"),
3078 		new BMessage(kOpenSelection), 'O'));
3079 	menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
3080 		new BMessage(kGetInfo), 'I'));
3081 	menu->SetTargetForItems(PoseView());
3082 }
3083 
3084 
3085 void
3086 BContainerWindow::EachAddon(bool (*eachAddon)(const Model*, const char*,
3087 		uint32 shortcut, uint32 modifiers, bool primary, void* context,
3088 		BContainerWindow* window, BMenu* menu),
3089 	void* passThru, BStringList& mimeTypes, BMenu* menu)
3090 {
3091 	AutoLock<LockingList<AddonShortcut> > lock(fAddonsList);
3092 	if (lock.IsLocked()) {
3093 		for (int i = fAddonsList->CountItems() - 1; i >= 0; i--) {
3094 			struct AddonShortcut* item = fAddonsList->ItemAt(i);
3095 			bool primary = false;
3096 
3097 			if (mimeTypes.CountStrings() > 0) {
3098 				BFile file(item->model->EntryRef(), B_READ_ONLY);
3099 				if (file.InitCheck() == B_OK) {
3100 					BAppFileInfo info(&file);
3101 					if (info.InitCheck() == B_OK) {
3102 						bool secondary = true;
3103 
3104 						// does this add-on has types set at all?
3105 						BMessage message;
3106 						if (info.GetSupportedTypes(&message) == B_OK) {
3107 							type_code typeCode;
3108 							int32 count;
3109 							if (message.GetInfo("types", &typeCode,
3110 									&count) == B_OK) {
3111 								secondary = false;
3112 							}
3113 						}
3114 
3115 						// check all supported types if it has some set
3116 						if (!secondary) {
3117 							for (int32 i = mimeTypes.CountStrings();
3118 									!primary && i-- > 0;) {
3119 								BString type = mimeTypes.StringAt(i);
3120 								if (info.IsSupportedType(type.String())) {
3121 									BMimeType mimeType(type.String());
3122 									if (info.Supports(&mimeType))
3123 										primary = true;
3124 									else
3125 										secondary = true;
3126 								}
3127 							}
3128 						}
3129 
3130 						if (!secondary && !primary)
3131 							continue;
3132 					}
3133 				}
3134 			}
3135 			((eachAddon)(item->model, item->model->Name(), item->key,
3136 				item->modifiers, primary, passThru, this, menu));
3137 		}
3138 	}
3139 }
3140 
3141 
3142 void
3143 BContainerWindow::BuildMimeTypeList(BStringList& mimeTypes)
3144 {
3145 	int32 count = PoseView()->SelectionList()->CountItems();
3146 	if (count <= 0) {
3147 		// just add the type of the current directory
3148 		AddMimeTypeString(mimeTypes, TargetModel());
3149 	} else {
3150 		_UpdateSelectionMIMEInfo();
3151 		for (int32 index = 0; index < count; index++) {
3152 			BPose* pose = PoseView()->SelectionList()->ItemAt(index);
3153 			AddMimeTypeString(mimeTypes, pose->TargetModel());
3154 			// If it's a symlink, resolves it and add the Target's MimeType
3155 			if (pose->TargetModel()->IsSymLink()) {
3156 				Model* resolved = new Model(
3157 					pose->TargetModel()->EntryRef(), true, true);
3158 				if (resolved->InitCheck() == B_OK)
3159 					AddMimeTypeString(mimeTypes, resolved);
3160 
3161 				delete resolved;
3162 			}
3163 		}
3164 	}
3165 }
3166 
3167 
3168 void
3169 BContainerWindow::BuildAddOnMenu(BMenu* parentMenu)
3170 {
3171 	BMenuItem* item = parentMenu->FindItem(B_TRANSLATE("Add-ons"));
3172 	if (parentMenu->IndexOf(item) == 0) {
3173 		// the folder of the context menu seems to be named "Add-Ons"
3174 		// so we just take the last menu item, which is correct if not
3175 		// build with debug option
3176 		item = parentMenu->ItemAt(parentMenu->CountItems() - 1);
3177 	}
3178 	if (item == NULL)
3179 		return;
3180 
3181 	BFont font;
3182 	parentMenu->GetFont(&font);
3183 
3184 	BMenu* menu = item->Submenu();
3185 	if (menu == NULL)
3186 		return;
3187 
3188 	menu->SetFont(&font);
3189 
3190 	// found the addons menu, empty it first
3191 	for (;;) {
3192 		item = menu->RemoveItem((int32)0);
3193 		if (!item)
3194 			break;
3195 		delete item;
3196 	}
3197 
3198 	BObjectList<BMenuItem> primaryList;
3199 	BObjectList<BMenuItem> secondaryList;
3200 	BStringList mimeTypes(10);
3201 	BuildMimeTypeList(mimeTypes);
3202 
3203 	AddOneAddonParams params;
3204 	params.primaryList = &primaryList;
3205 	params.secondaryList = &secondaryList;
3206 
3207 	// build a list of the MIME types of the selected items
3208 
3209 	EachAddon(AddOneAddon, &params, mimeTypes, parentMenu);
3210 
3211 	primaryList.SortItems(CompareLabels);
3212 	secondaryList.SortItems(CompareLabels);
3213 
3214 	int32 count = primaryList.CountItems();
3215 	for (int32 index = 0; index < count; index++)
3216 		menu->AddItem(primaryList.ItemAt(index));
3217 
3218 	if (count > 0)
3219 		menu->AddSeparatorItem();
3220 
3221 	count = secondaryList.CountItems();
3222 	for (int32 index = 0; index < count; index++)
3223 		menu->AddItem(secondaryList.ItemAt(index));
3224 
3225 	menu->SetTargetForItems(this);
3226 }
3227 
3228 
3229 void
3230 BContainerWindow::UpdateMenu(BMenu* menu, UpdateMenuContext context)
3231 {
3232 	const int32 selectCount = PoseView()->SelectionList()->CountItems();
3233 	const int32 count = PoseView()->CountItems();
3234 
3235 	if (context == kMenuBarContext) {
3236 		EnableNamedMenuItem(menu, kOpenSelection, selectCount > 0);
3237 		EnableNamedMenuItem(menu, kGetInfo, selectCount > 0);
3238 		EnableNamedMenuItem(menu, kIdentifyEntry, selectCount > 0);
3239 		EnableNamedMenuItem(menu, kMoveToTrash, selectCount > 0);
3240 		EnableNamedMenuItem(menu, kRestoreFromTrash, selectCount > 0);
3241 		EnableNamedMenuItem(menu, kDelete, selectCount > 0);
3242 		EnableNamedMenuItem(menu, kDuplicateSelection, selectCount > 0);
3243 	}
3244 
3245 	Model* selectedModel = NULL;
3246 	if (selectCount == 1) {
3247 		selectedModel = PoseView()->SelectionList()->FirstItem()->
3248 			TargetModel();
3249 	}
3250 
3251 	if (context == kMenuBarContext || context == kPosePopUpContext) {
3252 		SetUpEditQueryItem(menu);
3253 		EnableNamedMenuItem(menu, kEditItem, selectCount == 1
3254 			&& (context == kPosePopUpContext || !PoseView()->ActivePose())
3255 			&& selectedModel != NULL
3256 			&& !selectedModel->IsDesktop()
3257 			&& !selectedModel->IsRoot()
3258 			&& !selectedModel->IsTrash()
3259 			&& !selectedModel->HasLocalizedName());
3260 		SetCutItem(menu);
3261 		SetCopyItem(menu);
3262 		SetPasteItem(menu);
3263 	}
3264 
3265 	if (context == kMenuBarContext || context == kWindowPopUpContext) {
3266 		uint32 viewMode = PoseView()->ViewMode();
3267 
3268 		BMenu* iconSizeMenu = NULL;
3269 		if (BMenuItem* item = menu->FindItem(kIconMode))
3270 			iconSizeMenu = item->Submenu();
3271 
3272 		if (iconSizeMenu != NULL) {
3273 			if (viewMode == kIconMode) {
3274 				int32 iconSize = PoseView()->UnscaledIconSizeInt();
3275 				BMenuItem* item = iconSizeMenu->ItemAt(0);
3276 				for (int32 i = 0; (item = iconSizeMenu->ItemAt(i)) != NULL;
3277 						i++) {
3278 					BMessage* message = item->Message();
3279 					if (message == NULL) {
3280 						item->SetMarked(false);
3281 						continue;
3282 					}
3283 					int32 size;
3284 					if (message->FindInt32("size", &size) != B_OK)
3285 						size = -1;
3286 					item->SetMarked(iconSize == size);
3287 				}
3288 			} else {
3289 				BMenuItem* item;
3290 				for (int32 i = 0; (item = iconSizeMenu->ItemAt(i)) != NULL; i++)
3291 					item->SetMarked(false);
3292 			}
3293 		}
3294 
3295 		MarkNamedMenuItem(menu, kIconMode, viewMode == kIconMode);
3296 		MarkNamedMenuItem(menu, kListMode, viewMode == kListMode);
3297 		MarkNamedMenuItem(menu, kMiniIconMode, viewMode == kMiniIconMode);
3298 
3299 		SetCloseItem(menu);
3300 		SetArrangeMenu(menu);
3301 		SetPasteItem(menu);
3302 
3303 		BEntry entry(TargetModel()->EntryRef());
3304 		BDirectory parent;
3305 		entry_ref ref;
3306 		BEntry root("/");
3307 
3308 		bool parentIsRoot = (entry.GetParent(&parent) == B_OK
3309 			&& parent.GetEntry(&entry) == B_OK
3310 			&& entry.GetRef(&ref) == B_OK
3311 			&& entry == root);
3312 
3313 		EnableNamedMenuItem(menu, kOpenParentDir, !TargetModel()->IsDesktop()
3314 			&& !TargetModel()->IsRoot()
3315 			&& (!parentIsRoot
3316 				|| TrackerSettings().SingleWindowBrowse()
3317 				|| TrackerSettings().ShowDisksIcon()
3318 				|| (modifiers() & B_CONTROL_KEY) != 0));
3319 
3320 		EnableNamedMenuItem(menu, kEmptyTrash, count > 0);
3321 		EnableNamedMenuItem(menu, B_SELECT_ALL, count > 0);
3322 
3323 		BMenuItem* item = menu->FindItem(B_TRANSLATE("New"));
3324 		if (item != NULL) {
3325 			TemplatesMenu* templatesMenu = dynamic_cast<TemplatesMenu*>(
3326 				item->Submenu());
3327 			if (templatesMenu != NULL)
3328 				templatesMenu->UpdateMenuState();
3329 		}
3330 	}
3331 
3332 	BuildAddOnMenu(menu);
3333 }
3334 
3335 
3336 BMessage*
3337 BContainerWindow::AddOnMessage(int32 what)
3338 {
3339 	BMessage* message = new BMessage(what);
3340 
3341 	// add selected refs to message
3342 	BObjectList<BPose>* selectionList = PoseView()->SelectionList();
3343 
3344 	int32 index = 0;
3345 	BPose* pose;
3346 	while ((pose = selectionList->ItemAt(index++)) != NULL)
3347 		message->AddRef("refs", pose->TargetModel()->EntryRef());
3348 
3349 	message->AddRef("dir_ref", TargetModel()->EntryRef());
3350 	message->AddMessenger("TrackerViewToken", BMessenger(PoseView()));
3351 
3352 	return message;
3353 }
3354 
3355 
3356 void
3357 BContainerWindow::LoadAddOn(BMessage* message)
3358 {
3359 	UpdateIfNeeded();
3360 
3361 	entry_ref addonRef;
3362 	status_t result = message->FindRef("refs", &addonRef);
3363 	if (result != B_OK) {
3364 		BString buffer(B_TRANSLATE("Error %error loading add-On %name."));
3365 		buffer.ReplaceFirst("%error", strerror(result));
3366 		buffer.ReplaceFirst("%name", addonRef.name);
3367 
3368 		BAlert* alert = new BAlert("", buffer.String(),	B_TRANSLATE("Cancel"),
3369 			0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
3370 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
3371 		alert->Go();
3372 		return;
3373 	}
3374 
3375 	// add selected refs to message
3376 	BMessage* refs = AddOnMessage(B_REFS_RECEIVED);
3377 
3378 	LaunchInNewThread("Add-on", B_NORMAL_PRIORITY, &AddOnThread, refs,
3379 		addonRef, *TargetModel()->EntryRef());
3380 }
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