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