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