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