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