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