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