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