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