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