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