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