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