btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / kits / tracker / ContainerWindow.cpp
bloba95d9d9975e950cf03cd7de6644c46432a121cb6
1 /*
2 Open Tracker License
4 Terms and Conditions
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
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:
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.
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.
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.
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.
36 #include "ContainerWindow.h"
38 #include <Alert.h>
39 #include <Application.h>
40 #include <AppFileInfo.h>
41 #include <Catalog.h>
42 #include <ControlLook.h>
43 #include <Debug.h>
44 #include <Directory.h>
45 #include <Entry.h>
46 #include <FindDirectory.h>
47 #include <GridView.h>
48 #include <GroupLayout.h>
49 #include <Keymap.h>
50 #include <Locale.h>
51 #include <MenuItem.h>
52 #include <MenuBar.h>
53 #include <NodeMonitor.h>
54 #include <Path.h>
55 #include <PopUpMenu.h>
56 #include <Roster.h>
57 #include <Screen.h>
58 #include <UnicodeChar.h>
59 #include <Volume.h>
60 #include <VolumeRoster.h>
61 #include <WindowPrivate.h>
63 #include <fs_attr.h>
64 #include <image.h>
65 #include <strings.h>
66 #include <stdlib.h>
68 #include <algorithm>
69 #include <memory>
71 #include "Attributes.h"
72 #include "AttributeStream.h"
73 #include "AutoLock.h"
74 #include "BackgroundImage.h"
75 #include "Commands.h"
76 #include "CountView.h"
77 #include "DeskWindow.h"
78 #include "FavoritesMenu.h"
79 #include "FindPanel.h"
80 #include "FSClipboard.h"
81 #include "FSUndoRedo.h"
82 #include "FSUtils.h"
83 #include "IconMenuItem.h"
84 #include "OpenWithWindow.h"
85 #include "MimeTypes.h"
86 #include "MountMenu.h"
87 #include "Navigator.h"
88 #include "NavMenu.h"
89 #include "PoseView.h"
90 #include "QueryContainerWindow.h"
91 #include "SelectionWindow.h"
92 #include "TitleView.h"
93 #include "Tracker.h"
94 #include "TrackerSettings.h"
95 #include "Thread.h"
96 #include "TemplatesMenu.h"
99 #undef B_TRANSLATION_CONTEXT
100 #define B_TRANSLATION_CONTEXT "ContainerWindow"
103 #ifdef _IMPEXP_BE
104 _IMPEXP_BE
105 #endif
106 void do_minimize_team(BRect zoomRect, team_id team, bool zoom);
109 // Amount you have to move the mouse before a drag starts
110 const float kDragSlop = 3.0f;
113 namespace BPrivate {
115 class DraggableContainerIcon : public BView {
116 public:
117 DraggableContainerIcon();
119 virtual void MouseDown(BPoint where);
120 virtual void MouseUp(BPoint);
121 virtual void MouseMoved(BPoint point, uint32, const BMessage*);
122 virtual void Draw(BRect updateRect);
124 private:
125 uint32 fDragButton;
126 BPoint fClickPoint;
127 bool fDragStarted;
130 } // namespace BPrivate
133 struct AddOneAddonParams {
134 BObjectList<BMenuItem>* primaryList;
135 BObjectList<BMenuItem>* secondaryList;
138 struct StaggerOneParams {
139 bool rectFromParent;
143 const int32 kWindowStaggerBy = 17;
146 BRect BContainerWindow::sNewWindRect(85, 50, 548, 280);
148 LockingList<AddonShortcut>* BContainerWindow::fAddonsList
149 = new LockingList<struct AddonShortcut>(10, true);
152 namespace BPrivate {
154 filter_result
155 ActivateWindowFilter(BMessage*, BHandler** target, BMessageFilter*)
157 BView* view = dynamic_cast<BView*>(*target);
159 // activate the window if no PoseView or DraggableContainerIcon had been
160 // pressed (those will activate the window themselves, if necessary)
161 if (view != NULL
162 && dynamic_cast<BPoseView*>(view) == NULL
163 && dynamic_cast<DraggableContainerIcon*>(view) == NULL
164 && view->Window() != NULL) {
165 view->Window()->Activate(true);
168 return B_DISPATCH_MESSAGE;
173 CompareLabels(const BMenuItem* item1, const BMenuItem* item2)
175 return strcasecmp(item1->Label(), item2->Label());
178 } // namespace BPrivate
181 static bool
182 AddOneAddon(const Model* model, const char* name, uint32 shortcut,
183 uint32 modifiers, bool primary, void* context)
185 AddOneAddonParams* params = (AddOneAddonParams*)context;
187 BMessage* message = new BMessage(kLoadAddOn);
188 message->AddRef("refs", model->EntryRef());
190 ModelMenuItem* item = new ModelMenuItem(model, name, message,
191 (char)shortcut, modifiers);
193 if (primary)
194 params->primaryList->AddItem(item);
195 else
196 params->secondaryList->AddItem(item);
198 return false;
202 static int32
203 AddOnThread(BMessage* refsMessage, entry_ref addonRef, entry_ref directoryRef)
205 std::auto_ptr<BMessage> refsMessagePtr(refsMessage);
207 BEntry entry(&addonRef);
208 BPath path;
209 status_t result = entry.InitCheck();
210 if (result == B_OK)
211 result = entry.GetPath(&path);
213 if (result == B_OK) {
214 image_id addonImage = load_add_on(path.Path());
215 if (addonImage >= 0) {
216 void (*processRefs)(entry_ref, BMessage*, void*);
217 result = get_image_symbol(addonImage, "process_refs", 2,
218 (void**)&processRefs);
220 if (result >= 0) {
221 // call add-on code
222 (*processRefs)(directoryRef, refsMessagePtr.get(), NULL);
224 unload_add_on(addonImage);
225 return B_OK;
226 } else
227 PRINT(("couldn't find process_refs\n"));
229 unload_add_on(addonImage);
230 } else
231 result = addonImage;
234 BString buffer(B_TRANSLATE("Error %error loading Add-On %name."));
235 buffer.ReplaceFirst("%error", strerror(result));
236 buffer.ReplaceFirst("%name", addonRef.name);
238 BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"),
239 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
240 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
241 alert->Go();
243 return result;
247 static bool
248 NodeHasSavedState(const BNode* node)
250 attr_info info;
251 return node->GetAttrInfo(kAttrWindowFrame, &info) == B_OK;
255 static bool
256 OffsetFrameOne(const char* DEBUG_ONLY(name), uint32, off_t, void* castToRect,
257 void* castToParams)
259 ASSERT(strcmp(name, kAttrWindowFrame) == 0);
260 StaggerOneParams* params = (StaggerOneParams*)castToParams;
262 if (!params->rectFromParent)
263 return false;
265 if (!castToRect)
266 return false;
268 ((BRect*)castToRect)->OffsetBy(kWindowStaggerBy, kWindowStaggerBy);
270 return true;
274 static void
275 AddMimeTypeString(BStringList& list, Model* model)
277 if (model == NULL)
278 return;
280 const char* modelMimeType = model->MimeType();
281 if (modelMimeType == NULL || *modelMimeType == '\0')
282 return;
284 BString type = BString(modelMimeType);
285 if (list.HasString(type, true))
286 return;
288 list.Add(type);
292 // #pragma mark - DraggableContainerIcon
295 DraggableContainerIcon::DraggableContainerIcon()
297 BView("DraggableContainerIcon", B_WILL_DRAW),
298 fDragButton(0),
299 fDragStarted(false)
304 void
305 DraggableContainerIcon::MouseDown(BPoint where)
307 // we only like container windows
308 BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
309 ThrowOnAssert(window != NULL);
311 // we don't like the Trash icon (because it cannot be moved)
312 if (window->IsTrash() || window->IsPrintersDir())
313 return;
315 uint32 buttons;
316 window->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
318 if (IconCache::sIconCache->IconHitTest(where, window->TargetModel(),
319 kNormalIcon, B_MINI_ICON)) {
320 // The click hit the icon, initiate a drag
321 fDragButton = buttons
322 & (B_PRIMARY_MOUSE_BUTTON | B_SECONDARY_MOUSE_BUTTON);
323 fDragStarted = false;
324 fClickPoint = where;
325 } else
326 fDragButton = 0;
328 if (!fDragButton)
329 Window()->Activate(true);
333 void
334 DraggableContainerIcon::MouseUp(BPoint)
336 if (!fDragStarted)
337 Window()->Activate(true);
339 fDragButton = 0;
340 fDragStarted = false;
344 void
345 DraggableContainerIcon::MouseMoved(BPoint where, uint32, const BMessage*)
347 if (fDragButton == 0 || fDragStarted
348 || (abs((int32)(where.x - fClickPoint.x)) <= kDragSlop
349 && abs((int32)(where.y - fClickPoint.y)) <= kDragSlop))
350 return;
352 BContainerWindow* window = static_cast<BContainerWindow*>(Window());
353 // we can only get here in a BContainerWindow
354 Model* model = window->TargetModel();
356 // Find the required height
357 BFont font;
358 GetFont(&font);
360 font_height fontHeight;
361 font.GetHeight(&fontHeight);
362 float height = ceilf(fontHeight.ascent + fontHeight.descent
363 + fontHeight.leading + 2 + Bounds().Height() + 8);
365 BRect rect(0, 0, std::max(Bounds().Width(),
366 font.StringWidth(model->Name()) + 4), height);
367 BBitmap* dragBitmap = new BBitmap(rect, B_RGBA32, true);
369 dragBitmap->Lock();
370 BView* view = new BView(dragBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
371 dragBitmap->AddChild(view);
372 view->SetOrigin(0, 0);
373 BRect clipRect(view->Bounds());
374 BRegion newClip;
375 newClip.Set(clipRect);
376 view->ConstrainClippingRegion(&newClip);
378 // Transparent draw magic
379 view->SetHighColor(0, 0, 0, 0);
380 view->FillRect(view->Bounds());
381 view->SetDrawingMode(B_OP_ALPHA);
383 rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
384 textColor.alpha = 128;
385 // set the level of transparency by value
386 view->SetHighColor(textColor);
387 view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
389 // Draw the icon
390 float hIconOffset = (rect.Width() - Bounds().Width()) / 2;
391 IconCache::sIconCache->Draw(model, view, BPoint(hIconOffset, 0),
392 kNormalIcon, B_MINI_ICON, true);
394 // See if we need to truncate the string
395 BString nameString = model->Name();
396 if (view->StringWidth(model->Name()) > rect.Width())
397 view->TruncateString(&nameString, B_TRUNCATE_END, rect.Width() - 5);
399 // Draw the label
400 float leftText = (view->StringWidth(nameString.String())
401 - Bounds().Width()) / 2;
402 view->MovePenTo(BPoint(hIconOffset - leftText + 2, Bounds().Height()
403 + (fontHeight.ascent + 2)));
404 view->DrawString(nameString.String());
406 view->Sync();
407 dragBitmap->Unlock();
409 BMessage message(B_SIMPLE_DATA);
410 message.AddRef("refs", model->EntryRef());
411 message.AddPoint("click_pt", fClickPoint);
413 BPoint tmpLoc;
414 uint32 button;
415 GetMouse(&tmpLoc, &button);
416 if (button)
417 message.AddInt32("buttons", (int32)button);
419 if ((button & B_PRIMARY_MOUSE_BUTTON) != 0) {
420 // add an action specifier to the message, so that it is not copied
421 message.AddInt32("be:actions", (modifiers() & B_OPTION_KEY) != 0
422 ? B_COPY_TARGET : B_MOVE_TARGET);
425 fDragStarted = true;
426 fDragButton = 0;
428 DragMessage(&message, dragBitmap, B_OP_ALPHA,
429 BPoint(fClickPoint.x + hIconOffset, fClickPoint.y), this);
433 void
434 DraggableContainerIcon::Draw(BRect updateRect)
436 BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
437 ThrowOnAssert(window != NULL);
439 BRect rect(Bounds());
440 rgb_color base = ui_color(B_MENU_BACKGROUND_COLOR);
441 be_control_look->DrawBorder(this, rect, updateRect, base, B_PLAIN_BORDER,
442 0, BControlLook::B_BOTTOM_BORDER);
443 be_control_look->DrawMenuBarBackground(this, rect, updateRect, base, 0,
444 BControlLook::B_ALL_BORDERS & ~BControlLook::B_LEFT_BORDER);
446 // Draw the icon, straddling the border
447 SetDrawingMode(B_OP_ALPHA);
448 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
449 float iconOffsetX = (Bounds().Width() - B_MINI_ICON) / 2;
450 float iconOffsetY = (Bounds().Height() - B_MINI_ICON) / 2;
451 IconCache::sIconCache->Draw(window->TargetModel(), this,
452 BPoint(iconOffsetX, iconOffsetY), kNormalIcon, B_MINI_ICON, true);
456 // #pragma mark - BContainerWindow
459 BContainerWindow::BContainerWindow(LockingList<BWindow>* list,
460 uint32 containerWindowFlags, window_look look, window_feel feel,
461 uint32 flags, uint32 workspace, bool useLayouts, bool isDeskWindow)
463 BWindow(InitialWindowRect(feel), "TrackerWindow", look, feel, flags,
464 workspace),
465 fUseLayouts(useLayouts),
466 fMenuContainer(NULL),
467 fPoseContainer(NULL),
468 fBorderedView(NULL),
469 fVScrollBarContainer(NULL),
470 fCountContainer(NULL),
471 fFileContextMenu(NULL),
472 fWindowContextMenu(NULL),
473 fDropContextMenu(NULL),
474 fVolumeContextMenu(NULL),
475 fTrashContextMenu(NULL),
476 fDragContextMenu(NULL),
477 fMoveToItem(NULL),
478 fCopyToItem(NULL),
479 fCreateLinkItem(NULL),
480 fOpenWithItem(NULL),
481 fNavigationItem(NULL),
482 fMenuBar(NULL),
483 fDraggableIcon(NULL),
484 fNavigator(NULL),
485 fPoseView(NULL),
486 fWindowList(list),
487 fAttrMenu(NULL),
488 fWindowMenu(NULL),
489 fFileMenu(NULL),
490 fArrangeByMenu(NULL),
491 fSelectionWindow(NULL),
492 fTaskLoop(NULL),
493 fIsTrash(false),
494 fInTrash(false),
495 fIsPrinters(false),
496 fIsDesktop(isDeskWindow),
497 fContainerWindowFlags(containerWindowFlags),
498 fBackgroundImage(NULL),
499 fSavedZoomRect(0, 0, -1, -1),
500 fContextMenu(NULL),
501 fDragMessage(NULL),
502 fCachedTypesList(NULL),
503 fStateNeedsSaving(false),
504 fSaveStateIsEnabled(true),
505 fIsWatchingPath(false)
507 InitIconPreloader();
509 if (list != NULL) {
510 ASSERT(list->IsLocked());
511 list->AddItem(this);
514 if (useLayouts) {
515 SetFlags(Flags() | B_AUTO_UPDATE_SIZE_LIMITS);
517 fRootLayout = new BGroupLayout(B_VERTICAL, 0);
518 fRootLayout->SetInsets(0);
519 SetLayout(fRootLayout);
520 fRootLayout->Owner()->AdoptSystemColors();
522 if (!fIsDesktop) {
523 fMenuContainer = new BGroupView(B_HORIZONTAL, 0);
524 fRootLayout->AddView(fMenuContainer);
526 fPoseContainer = new BGridView(0.0, 0.0);
527 fRootLayout->AddView(fPoseContainer);
529 fBorderedView = new BorderedView;
530 fPoseContainer->GridLayout()->AddView(fBorderedView, 0, 1);
532 fCountContainer = new BGroupView(B_HORIZONTAL, 0);
533 fPoseContainer->GridLayout()->AddView(fCountContainer, 0, 2);
537 AddCommonFilter(new BMessageFilter(B_MOUSE_DOWN, ActivateWindowFilter));
539 Run();
541 // watch out for settings changes
542 TTracker* tracker = dynamic_cast<TTracker*>(be_app);
543 if (tracker != NULL && tracker->Lock()) {
544 tracker->StartWatching(this, kWindowsShowFullPathChanged);
545 tracker->StartWatching(this, kSingleWindowBrowseChanged);
546 tracker->StartWatching(this, kShowNavigatorChanged);
547 tracker->StartWatching(this, kDontMoveFilesToTrashChanged);
548 tracker->Unlock();
551 // ToDo: remove me once we have undo/redo menu items
552 // (that is, move them to AddShortcuts())
553 AddShortcut('Z', B_COMMAND_KEY, new BMessage(B_UNDO), this);
554 AddShortcut('Z', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(B_REDO), this);
558 BContainerWindow::~BContainerWindow()
560 ASSERT(IsLocked());
562 // stop the watchers
563 TTracker* tracker = dynamic_cast<TTracker*>(be_app);
564 if (tracker != NULL && tracker->Lock()) {
565 tracker->StopWatching(this, kWindowsShowFullPathChanged);
566 tracker->StopWatching(this, kSingleWindowBrowseChanged);
567 tracker->StopWatching(this, kShowNavigatorChanged);
568 tracker->StopWatching(this, kDontMoveFilesToTrashChanged);
569 tracker->Unlock();
572 delete fTaskLoop;
573 delete fBackgroundImage;
574 delete fDragMessage;
575 delete fCachedTypesList;
577 if (fSelectionWindow != NULL && fSelectionWindow->Lock())
578 fSelectionWindow->Quit();
582 BRect
583 BContainerWindow::InitialWindowRect(window_feel feel)
585 if (feel != kDesktopWindowFeel)
586 return sNewWindRect;
588 // do not offset desktop window
589 BRect result = sNewWindRect;
590 result.OffsetTo(0, 0);
591 return result;
595 void
596 BContainerWindow::Minimize(bool minimize)
598 if (minimize && (modifiers() & B_OPTION_KEY) != 0)
599 do_minimize_team(BRect(0, 0, 0, 0), be_app->Team(), true);
600 else
601 _inherited::Minimize(minimize);
605 bool
606 BContainerWindow::QuitRequested()
608 // this is a response to the DeskBar sending us a B_QUIT, when it really
609 // means to say close all your windows. It might be better to have it
610 // send a kCloseAllWindows message and have windowless apps stay running,
611 // which is what we will do for the Tracker
612 if (CurrentMessage() != NULL
613 && ((CurrentMessage()->FindInt32("modifiers") & B_CONTROL_KEY)) != 0) {
614 be_app->PostMessage(kCloseAllWindows);
617 Hide();
618 // this will close the window instantly, even if
619 // the file system is very busy right now
620 return true;
624 void
625 BContainerWindow::Quit()
627 // get rid of context menus
628 if (fNavigationItem) {
629 BMenu* menu = fNavigationItem->Menu();
630 if (menu != NULL)
631 menu->RemoveItem(fNavigationItem);
633 delete fNavigationItem;
634 fNavigationItem = NULL;
637 if (fOpenWithItem != NULL && fOpenWithItem->Menu() == NULL) {
638 delete fOpenWithItem;
639 fOpenWithItem = NULL;
642 if (fMoveToItem != NULL && fMoveToItem->Menu() == NULL) {
643 delete fMoveToItem;
644 fMoveToItem = NULL;
647 if (fCopyToItem != NULL && fCopyToItem->Menu() == NULL) {
648 delete fCopyToItem;
649 fCopyToItem = NULL;
652 if (fCreateLinkItem != NULL && fCreateLinkItem->Menu() == NULL) {
653 delete fCreateLinkItem;
654 fCreateLinkItem = NULL;
657 if (fAttrMenu != NULL && fAttrMenu->Supermenu() == NULL) {
658 delete fAttrMenu;
659 fAttrMenu = NULL;
662 delete fFileContextMenu;
663 fFileContextMenu = NULL;
665 delete fWindowContextMenu;
666 fWindowContextMenu = NULL;
668 delete fDropContextMenu;
669 fDropContextMenu = NULL;
671 delete fVolumeContextMenu;
672 fVolumeContextMenu = NULL;
674 delete fDragContextMenu;
675 fDragContextMenu = NULL;
677 int32 windowCount = 0;
679 // This is a deadlock code sequence - need to change this
680 // to acquire the window list while this container window is unlocked
681 if (fWindowList != NULL) {
682 AutoLock<LockingList<BWindow> > lock(fWindowList);
683 if (lock.IsLocked()) {
684 fWindowList->RemoveItem(this, false);
685 windowCount = fWindowList->CountItems();
689 if (StateNeedsSaving())
690 SaveState();
692 if (fWindowList != NULL && windowCount == 0)
693 be_app->PostMessage(B_QUIT_REQUESTED);
695 _inherited::Quit();
699 BPoseView*
700 BContainerWindow::NewPoseView(Model* model, uint32 viewMode)
702 return new BPoseView(model, viewMode);
706 void
707 BContainerWindow::UpdateIfTrash(Model* model)
709 BEntry entry(model->EntryRef());
711 if (entry.InitCheck() == B_OK) {
712 fIsTrash = model->IsTrash();
713 fInTrash = FSInTrashDir(model->EntryRef());
714 fIsPrinters = FSIsPrintersDir(&entry);
719 void
720 BContainerWindow::CreatePoseView(Model* model)
722 UpdateIfTrash(model);
724 fPoseView = NewPoseView(model, kListMode);
725 fBorderedView->GroupLayout()->AddView(fPoseView);
726 fBorderedView->GroupLayout()->SetInsets(1, 0, 1, 1);
727 fBorderedView->EnableBorderHighlight(false);
729 TrackerSettings settings;
730 if (settings.SingleWindowBrowse() && model->IsDirectory()
731 && !fPoseView->IsFilePanel()) {
732 fNavigator = new BNavigator(model);
733 fPoseContainer->GridLayout()->AddView(fNavigator, 0, 0, 2);
734 if (!settings.ShowNavigator())
735 fNavigator->Hide();
738 SetPathWatchingEnabled(settings.ShowNavigator()
739 || settings.ShowFullPathInTitleBar());
743 void
744 BContainerWindow::AddContextMenus()
746 // create context sensitive menus
747 fFileContextMenu = new BPopUpMenu("FileContext", false, false);
748 fFileContextMenu->SetFont(be_plain_font);
749 AddFileContextMenus(fFileContextMenu);
751 fVolumeContextMenu = new BPopUpMenu("VolumeContext", false, false);
752 fVolumeContextMenu->SetFont(be_plain_font);
753 AddVolumeContextMenus(fVolumeContextMenu);
755 fWindowContextMenu = new BPopUpMenu("WindowContext", false, false);
756 fWindowContextMenu->SetFont(be_plain_font);
757 AddWindowContextMenus(fWindowContextMenu);
759 fDropContextMenu = new BPopUpMenu("DropContext", false, false);
760 fDropContextMenu->SetFont(be_plain_font);
761 AddDropContextMenus(fDropContextMenu);
763 fDragContextMenu = new BSlowContextMenu("DragContext");
764 // will get added and built dynamically in ShowContextMenu
766 fTrashContextMenu = new BPopUpMenu("TrashContext", false, false);
767 fTrashContextMenu->SetFont(be_plain_font);
768 AddTrashContextMenus(fTrashContextMenu);
772 void
773 BContainerWindow::RepopulateMenus()
775 // Avoid these menus to be destroyed:
776 if (fMoveToItem && fMoveToItem->Menu())
777 fMoveToItem->Menu()->RemoveItem(fMoveToItem);
779 if (fCopyToItem && fCopyToItem->Menu())
780 fCopyToItem->Menu()->RemoveItem(fCopyToItem);
782 if (fCreateLinkItem && fCreateLinkItem->Menu())
783 fCreateLinkItem->Menu()->RemoveItem(fCreateLinkItem);
785 if (fOpenWithItem && fOpenWithItem->Menu()) {
786 fOpenWithItem->Menu()->RemoveItem(fOpenWithItem);
787 delete fOpenWithItem;
788 fOpenWithItem = NULL;
791 if (fNavigationItem) {
792 BMenu* menu = fNavigationItem->Menu();
793 if (menu) {
794 menu->RemoveItem(fNavigationItem);
795 BMenuItem* item = menu->RemoveItem((int32)0);
796 ASSERT(item != fNavigationItem);
797 delete item;
801 delete fFileContextMenu;
802 fFileContextMenu = new BPopUpMenu("FileContext", false, false);
803 fFileContextMenu->SetFont(be_plain_font);
804 AddFileContextMenus(fFileContextMenu);
806 delete fWindowContextMenu;
807 fWindowContextMenu = new BPopUpMenu("WindowContext", false, false);
808 fWindowContextMenu->SetFont(be_plain_font);
809 AddWindowContextMenus(fWindowContextMenu);
811 if (fMenuBar != NULL) {
812 fMenuBar->RemoveItem(fFileMenu);
813 delete fFileMenu;
814 fFileMenu = new BMenu(B_TRANSLATE("File"));
815 AddFileMenu(fFileMenu);
816 fMenuBar->AddItem(fFileMenu);
818 fMenuBar->RemoveItem(fWindowMenu);
819 delete fWindowMenu;
820 fWindowMenu = new BMenu(B_TRANSLATE("Window"));
821 fMenuBar->AddItem(fWindowMenu);
822 AddWindowMenu(fWindowMenu);
824 // just create the attribute, decide to add it later
825 fMenuBar->RemoveItem(fAttrMenu);
826 delete fAttrMenu;
827 fAttrMenu = new BMenu(B_TRANSLATE("Attributes"));
828 NewAttributeMenu(fAttrMenu);
829 if (PoseView()->ViewMode() == kListMode)
830 ShowAttributeMenu();
832 PopulateArrangeByMenu(fArrangeByMenu);
834 int32 selectCount = PoseView()->SelectionList()->CountItems();
836 SetupOpenWithMenu(fFileMenu);
837 SetupMoveCopyMenus(selectCount ? PoseView()->SelectionList()
838 ->FirstItem()->TargetModel()->EntryRef() : NULL,
839 fFileMenu);
844 void
845 BContainerWindow::Init(const BMessage* message)
847 BEntry entry;
849 ASSERT(fPoseView != NULL);
850 if (fPoseView == NULL)
851 return;
853 // deal with new unconfigured folders
854 if (NeedsDefaultStateSetup())
855 SetUpDefaultState();
857 if (ShouldAddScrollBars())
858 fPoseView->AddScrollBars();
860 fMoveToItem = new BMenuItem(new BNavMenu(B_TRANSLATE("Move to"),
861 kMoveSelectionTo, this));
862 fCopyToItem = new BMenuItem(new BNavMenu(B_TRANSLATE("Copy to"),
863 kCopySelectionTo, this));
864 fCreateLinkItem = new BMenuItem(new BNavMenu(B_TRANSLATE("Create link"),
865 kCreateLink, this), new BMessage(kCreateLink));
867 TrackerSettings settings;
869 if (ShouldAddMenus()) {
870 fMenuBar = new BMenuBar("MenuBar");
871 fMenuContainer->GroupLayout()->AddView(fMenuBar);
872 AddMenus();
874 if (!TargetModel()->IsRoot() && !IsTrash())
875 _AddFolderIcon();
876 } else {
877 // add equivalents of the menu shortcuts to the menuless
878 // desktop window
879 AddShortcuts();
882 AddContextMenus();
883 AddShortcut('T', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(kDelete),
884 PoseView());
885 AddShortcut('K', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(kCleanupAll),
886 PoseView());
887 AddShortcut('Q', B_COMMAND_KEY | B_OPTION_KEY | B_SHIFT_KEY
888 | B_CONTROL_KEY, new BMessage(kQuitTracker));
890 AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY, new BMessage(kOpenSelection),
891 PoseView());
893 SetSingleWindowBrowseShortcuts(settings.SingleWindowBrowse());
895 #if DEBUG
896 // add some debugging shortcuts
897 AddShortcut('D', B_COMMAND_KEY | B_CONTROL_KEY, new BMessage('dbug'),
898 PoseView());
899 AddShortcut('C', B_COMMAND_KEY | B_CONTROL_KEY, new BMessage('dpcc'),
900 PoseView());
901 AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY, new BMessage('dpfl'),
902 PoseView());
903 AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY,
904 new BMessage('dpfL'), PoseView());
905 #endif
907 BKeymap keymap;
908 keymap.SetToCurrent();
909 BObjectList<const char> unmodified(3, true);
910 if (keymap.GetModifiedCharacters("+", B_SHIFT_KEY, 0, &unmodified)
911 == B_OK) {
912 int32 count = unmodified.CountItems();
913 for (int32 i = 0; i < count; i++) {
914 uint32 key = BUnicodeChar::FromUTF8(unmodified.ItemAt(i));
915 if (!HasShortcut(key, 0)) {
916 // Add semantic zoom in shortcut, bug #6692
917 BMessage* increaseSize = new BMessage(kIconMode);
918 increaseSize->AddInt32("scale", 1);
919 AddShortcut(key, B_COMMAND_KEY, increaseSize, PoseView());
923 unmodified.MakeEmpty();
925 if (message != NULL)
926 RestoreState(*message);
927 else
928 RestoreState();
930 if (ShouldAddMenus() && PoseView()->ViewMode() == kListMode) {
931 // for now only show attributes in list view
932 // eventually enable attribute menu to allow users to select
933 // using different attributes as titles in icon view modes
934 ShowAttributeMenu();
936 MarkAttributeMenu(fAttrMenu);
937 CheckScreenIntersect();
939 if (fBackgroundImage != NULL && !fIsDesktop
940 && PoseView()->ViewMode() != kListMode) {
941 fBackgroundImage->Show(PoseView(), current_workspace());
944 Show();
946 // done showing, turn the B_NO_WORKSPACE_ACTIVATION flag off;
947 // it was on to prevent workspace jerking during boot
948 SetFlags(Flags() & ~B_NO_WORKSPACE_ACTIVATION);
951 void
952 BContainerWindow::InitLayout()
954 fBorderedView->GroupLayout()->AddView(0, fPoseView->TitleView());
956 BLayoutItem* item = fCountContainer->GroupLayout()->AddView(
957 fPoseView->CountView());
958 item->SetExplicitMinSize(BSize(kCountViewWidth, B_H_SCROLL_BAR_HEIGHT));
959 item->SetExplicitMaxSize(BSize(kCountViewWidth, B_SIZE_UNSET));
961 // Eliminate the extra borders
962 fMenuContainer->GroupLayout()->SetInsets(0, 0, -1, 0);
963 fPoseContainer->GridLayout()->SetInsets(-1, 0, -1, -1);
964 fCountContainer->GroupLayout()->SetInsets(0, -1, 0, 0);
966 if (fPoseView->VScrollBar() != NULL) {
967 fVScrollBarContainer = new BGroupView(B_VERTICAL, 0);
968 fVScrollBarContainer->GroupLayout()->AddView(fPoseView->VScrollBar());
969 fVScrollBarContainer->GroupLayout()->SetInsets(-1, -1, 0, 0);
970 fPoseContainer->GridLayout()->AddView(fVScrollBarContainer, 1, 1);
972 if (fPoseView->HScrollBar() != NULL) {
973 BGroupView* hScrollBarContainer = new BGroupView(B_VERTICAL, 0);
974 hScrollBarContainer->GroupLayout()->AddView(fPoseView->HScrollBar());
975 hScrollBarContainer->GroupLayout()->SetInsets(0, -1, 0, -1);
976 fCountContainer->GroupLayout()->AddView(hScrollBarContainer);
980 void
981 BContainerWindow::RestoreState()
983 UpdateTitle();
985 WindowStateNodeOpener opener(this, false);
986 RestoreWindowState(opener.StreamNode());
987 fPoseView->Init(opener.StreamNode());
989 RestoreStateCommon();
993 void
994 BContainerWindow::RestoreState(const BMessage &message)
996 UpdateTitle();
998 RestoreWindowState(message);
999 fPoseView->Init(message);
1001 RestoreStateCommon();
1005 void
1006 BContainerWindow::RestoreStateCommon()
1008 if (!fIsDesktop && fUseLayouts)
1009 InitLayout();
1011 if (BootedInSafeMode())
1012 // don't pick up backgrounds in safe mode
1013 return;
1015 WindowStateNodeOpener opener(this, false);
1017 if (!TargetModel()->IsRoot() && opener.Node() != NULL) {
1018 // don't pick up background image for root disks
1019 // to do this, would have to have a unique attribute for the
1020 // disks window that doesn't collide with the desktop
1021 // for R4 this was not done to make things simpler
1022 // the default image will still work though
1023 fBackgroundImage = BackgroundImage::GetBackgroundImage(
1024 opener.Node(), fIsDesktop);
1025 // look for background image info in the window's node
1028 BNode defaultingNode;
1029 if (fBackgroundImage == NULL && !fIsDesktop
1030 && DefaultStateSourceNode(kDefaultFolderTemplate, &defaultingNode)) {
1031 // look for background image info in the source for defaults
1032 fBackgroundImage = BackgroundImage::GetBackgroundImage(&defaultingNode,
1033 fIsDesktop);
1038 void
1039 BContainerWindow::UpdateTitle()
1041 // set title to full path, if necessary
1042 if (TrackerSettings().ShowFullPathInTitleBar()) {
1043 // use the Entry's full path
1044 BPath path;
1045 TargetModel()->GetPath(&path);
1046 SetTitle(path.Path());
1047 } else {
1048 // use the default look
1049 SetTitle(TargetModel()->Name());
1052 if (Navigator() != NULL) {
1053 Navigator()->UpdateLocation(PoseView()->TargetModel(),
1054 kActionUpdatePath);
1059 void
1060 BContainerWindow::UpdateBackgroundImage()
1062 if (BootedInSafeMode())
1063 return;
1065 WindowStateNodeOpener opener(this, false);
1067 if (!TargetModel()->IsRoot() && opener.Node() != NULL) {
1068 fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage,
1069 opener.Node(), fIsDesktop, PoseView());
1072 // look for background image info in the window's node
1073 BNode defaultingNode;
1074 if (!fBackgroundImage && !fIsDesktop
1075 && DefaultStateSourceNode(kDefaultFolderTemplate, &defaultingNode)) {
1076 // look for background image info in the source for defaults
1077 fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage,
1078 &defaultingNode, fIsDesktop, PoseView());
1083 void
1084 BContainerWindow::FrameResized(float, float)
1086 if (PoseView() != NULL && !fIsDesktop) {
1087 BRect extent = PoseView()->Extent();
1088 float offsetX = extent.left - PoseView()->Bounds().left;
1089 float offsetY = extent.top - PoseView()->Bounds().top;
1091 // scroll when the size augmented, there is a negative offset
1092 // and we have resized over the bottom right corner of the extent
1093 BPoint scroll(B_ORIGIN);
1094 if (offsetX < 0 && PoseView()->Bounds().right > extent.right
1095 && Bounds().Width() > fPreviousBounds.Width()) {
1096 scroll.x = std::max(fPreviousBounds.Width() - Bounds().Width(),
1097 offsetX);
1100 if (offsetY < 0 && PoseView()->Bounds().bottom > extent.bottom
1101 && Bounds().Height() > fPreviousBounds.Height()) {
1102 scroll.y = std::max(fPreviousBounds.Height() - Bounds().Height(),
1103 offsetY);
1106 if (scroll != B_ORIGIN)
1107 PoseView()->ScrollBy(scroll.x, scroll.y);
1109 PoseView()->UpdateScrollRange();
1110 PoseView()->ResetPosePlacementHint();
1113 fPreviousBounds = Bounds();
1114 fStateNeedsSaving = true;
1118 void
1119 BContainerWindow::FrameMoved(BPoint)
1121 fStateNeedsSaving = true;
1125 void
1126 BContainerWindow::WorkspacesChanged(uint32, uint32)
1128 fStateNeedsSaving = true;
1132 void
1133 BContainerWindow::ViewModeChanged(uint32 oldMode, uint32 newMode)
1135 if (fBackgroundImage == NULL)
1136 return;
1138 if (newMode == kListMode)
1139 fBackgroundImage->Remove();
1140 else if (oldMode == kListMode)
1141 fBackgroundImage->Show(PoseView(), current_workspace());
1145 void
1146 BContainerWindow::CheckScreenIntersect()
1148 BScreen screen(this);
1149 BRect screenFrame(screen.Frame());
1150 BRect frame(Frame());
1152 if (sNewWindRect.bottom > screenFrame.bottom)
1153 sNewWindRect.OffsetTo(85, 50);
1155 if (sNewWindRect.right > screenFrame.right)
1156 sNewWindRect.OffsetTo(85, 50);
1158 if (!frame.Intersects(screenFrame))
1159 MoveTo(sNewWindRect.LeftTop());
1163 void
1164 BContainerWindow::SaveState(bool hide)
1166 if (SaveStateIsEnabled()) {
1167 WindowStateNodeOpener opener(this, true);
1168 if (opener.StreamNode() != NULL)
1169 SaveWindowState(opener.StreamNode());
1171 if (hide)
1172 Hide();
1174 if (opener.StreamNode())
1175 fPoseView->SaveState(opener.StreamNode());
1177 fStateNeedsSaving = false;
1182 void
1183 BContainerWindow::SaveState(BMessage& message) const
1185 if (SaveStateIsEnabled()) {
1186 SaveWindowState(message);
1187 fPoseView->SaveState(message);
1192 bool
1193 BContainerWindow::StateNeedsSaving() const
1195 return fPoseView != NULL && (fStateNeedsSaving || fPoseView->StateNeedsSaving());
1199 status_t
1200 BContainerWindow::GetLayoutState(BNode* node, BMessage* message)
1202 if (node == NULL || message == NULL)
1203 return B_BAD_VALUE;
1205 status_t result = node->InitCheck();
1206 if (result != B_OK)
1207 return result;
1209 // ToDo: get rid of this, use AttrStream instead
1210 node->RewindAttrs();
1211 char attrName[256];
1212 while (node->GetNextAttrName(attrName) == B_OK) {
1213 attr_info info;
1214 if (node->GetAttrInfo(attrName, &info) != B_OK)
1215 continue;
1217 // filter out attributes that are not related to window position
1218 // and column resizing
1219 // more can be added as needed
1220 if (strcmp(attrName, kAttrWindowFrame) != 0
1221 && strcmp(attrName, kAttrColumns) != 0
1222 && strcmp(attrName, kAttrViewState) != 0
1223 && strcmp(attrName, kAttrColumnsForeign) != 0
1224 && strcmp(attrName, kAttrViewStateForeign) != 0) {
1225 continue;
1228 char* buffer = new char[info.size];
1229 if (node->ReadAttr(attrName, info.type, 0, buffer,
1230 (size_t)info.size) == info.size) {
1231 message->AddData(attrName, info.type, buffer, (ssize_t)info.size);
1233 delete[] buffer;
1236 return B_OK;
1240 status_t
1241 BContainerWindow::SetLayoutState(BNode* node, const BMessage* message)
1243 status_t result = node->InitCheck();
1244 if (result != B_OK)
1245 return result;
1247 for (int32 globalIndex = 0; ;) {
1248 #if B_BEOS_VERSION_DANO
1249 const char* name;
1250 #else
1251 char* name;
1252 #endif
1253 type_code type;
1254 int32 count;
1255 status_t result = message->GetInfo(B_ANY_TYPE, globalIndex, &name,
1256 &type, &count);
1257 if (result != B_OK)
1258 break;
1260 for (int32 index = 0; index < count; index++) {
1261 const void* buffer;
1262 ssize_t size;
1263 result = message->FindData(name, type, index, &buffer, &size);
1264 if (result != B_OK) {
1265 PRINT(("error reading %s \n", name));
1266 return result;
1269 if (node->WriteAttr(name, type, 0, buffer,
1270 (size_t)size) != size) {
1271 PRINT(("error writing %s \n", name));
1272 return result;
1274 globalIndex++;
1278 return B_OK;
1282 bool
1283 BContainerWindow::ShouldAddMenus() const
1285 return true;
1289 bool
1290 BContainerWindow::ShouldAddScrollBars() const
1292 return true;
1296 Model*
1297 BContainerWindow::TargetModel() const
1299 return fPoseView->TargetModel();
1303 void
1304 BContainerWindow::SelectionChanged()
1309 void
1310 BContainerWindow::Zoom(BPoint, float, float)
1312 BRect oldZoomRect(fSavedZoomRect);
1313 fSavedZoomRect = Frame();
1314 ResizeToFit();
1316 if (fSavedZoomRect == Frame() && oldZoomRect.IsValid())
1317 ResizeTo(oldZoomRect.Width(), oldZoomRect.Height());
1321 void
1322 BContainerWindow::ResizeToFit()
1324 BScreen screen(this);
1325 BRect screenFrame(screen.Frame());
1327 screenFrame.InsetBy(5, 5);
1328 BMessage decoratorSettings;
1329 GetDecoratorSettings(&decoratorSettings);
1331 float tabHeight = 15;
1332 BRect tabRect;
1333 if (decoratorSettings.FindRect("tab frame", &tabRect) == B_OK)
1334 tabHeight = tabRect.Height();
1335 screenFrame.top += tabHeight;
1337 BRect frame(Frame());
1339 float widthDiff = frame.Width() - PoseView()->Frame().Width();
1340 float heightDiff = frame.Height() - PoseView()->Frame().Height();
1342 // move frame left top on screen
1343 BPoint leftTop(frame.LeftTop());
1344 leftTop.ConstrainTo(screenFrame);
1345 frame.OffsetTo(leftTop);
1347 // resize to extent size
1348 BRect extent(PoseView()->Extent());
1349 frame.right = frame.left + extent.Width() + widthDiff;
1350 frame.bottom = frame.top + extent.Height() + heightDiff;
1352 // make sure entire window fits on screen
1353 frame = frame & screenFrame;
1355 ResizeTo(frame.Width(), frame.Height());
1356 MoveTo(frame.LeftTop());
1357 PoseView()->DisableScrollBars();
1359 // scroll if there is an offset
1360 PoseView()->ScrollBy(
1361 extent.left - PoseView()->Bounds().left,
1362 extent.top - PoseView()->Bounds().top);
1364 PoseView()->UpdateScrollRange();
1365 PoseView()->EnableScrollBars();
1369 void
1370 BContainerWindow::MessageReceived(BMessage* message)
1372 switch (message->what) {
1373 case B_CUT:
1374 case B_COPY:
1375 case B_PASTE:
1376 case kCutMoreSelectionToClipboard:
1377 case kCopyMoreSelectionToClipboard:
1378 case kPasteLinksFromClipboard:
1380 BView* view = CurrentFocus();
1381 if (dynamic_cast<BTextView*>(view) == NULL) {
1382 // The selected item is not a BTextView, so forward the
1383 // message to the PoseView.
1384 if (fPoseView != NULL)
1385 fPoseView->MessageReceived(message);
1386 } else {
1387 // Since we catch the generic clipboard shortcuts in a way that
1388 // means the BTextView will never get them, we must
1389 // manually forward them ourselves.
1391 // However, we have to take care to not forward the custom
1392 // clipboard messages, else we would wind up in infinite
1393 // recursion.
1394 if (message->what == B_CUT || message->what == B_COPY
1395 || message->what == B_PASTE) {
1396 view->MessageReceived(message);
1399 break;
1402 case B_UNDO: {
1403 BView* view = CurrentFocus();
1404 if (dynamic_cast<BTextView*>(view) == NULL) {
1405 FSUndo();
1406 } else {
1407 view->MessageReceived(message);
1409 break;
1412 case B_REDO: {
1413 BView* view = CurrentFocus();
1414 if (dynamic_cast<BTextView*>(view) == NULL) {
1415 FSRedo();
1416 } else {
1417 view->MessageReceived(message);
1419 break;
1422 case kNewFolder:
1423 PostMessage(message, PoseView());
1424 break;
1426 case kRestoreState:
1427 if (message->HasMessage("state")) {
1428 BMessage state;
1429 message->FindMessage("state", &state);
1430 Init(&state);
1431 } else
1432 Init();
1433 break;
1435 case kResizeToFit:
1436 ResizeToFit();
1437 break;
1439 case kLoadAddOn:
1440 LoadAddOn(message);
1441 break;
1443 case kCopySelectionTo:
1445 entry_ref ref;
1446 if (message->FindRef("refs", &ref) != B_OK)
1447 break;
1449 BRoster().AddToRecentFolders(&ref);
1451 Model model(&ref);
1452 if (model.InitCheck() != B_OK)
1453 break;
1455 if (*model.NodeRef() == *TargetModel()->NodeRef())
1456 PoseView()->DuplicateSelection();
1457 else
1458 PoseView()->MoveSelectionInto(&model, this, true);
1459 break;
1462 case kMoveSelectionTo:
1464 entry_ref ref;
1465 if (message->FindRef("refs", &ref) != B_OK)
1466 break;
1468 BRoster().AddToRecentFolders(&ref);
1470 Model model(&ref);
1471 if (model.InitCheck() != B_OK)
1472 break;
1474 PoseView()->MoveSelectionInto(&model, this, false, true);
1475 break;
1478 case kCreateLink:
1479 case kCreateRelativeLink:
1481 entry_ref ref;
1482 if (message->FindRef("refs", &ref) == B_OK) {
1483 BRoster().AddToRecentFolders(&ref);
1485 Model model(&ref);
1486 if (model.InitCheck() != B_OK)
1487 break;
1489 PoseView()->MoveSelectionInto(&model, this, false, false,
1490 message->what == kCreateLink,
1491 message->what == kCreateRelativeLink);
1492 } else if (!TargetModel()->IsQuery()
1493 && !TargetModel()->IsVirtualDirectory()) {
1494 // no destination specified, create link in same dir as item
1495 PoseView()->MoveSelectionInto(TargetModel(), this, false, false,
1496 message->what == kCreateLink,
1497 message->what == kCreateRelativeLink);
1499 break;
1502 case kShowSelectionWindow:
1503 ShowSelectionWindow();
1504 break;
1506 case kSelectMatchingEntries:
1507 PoseView()->SelectMatchingEntries(message);
1508 break;
1510 case kFindButton:
1511 (new FindWindow())->Show();
1512 break;
1514 case kQuitTracker:
1515 be_app->PostMessage(B_QUIT_REQUESTED);
1516 break;
1518 case kRestoreBackgroundImage:
1519 UpdateBackgroundImage();
1520 break;
1522 case kSwitchDirectory:
1524 entry_ref ref;
1525 if (message->FindRef("refs", &ref) == B_OK) {
1526 BEntry entry;
1527 if (entry.SetTo(&ref) == B_OK) {
1528 if (StateNeedsSaving())
1529 SaveState(false);
1531 bool wasInTrash = IsTrash() || InTrash();
1532 bool isRoot = PoseView()->TargetModel()->IsRoot();
1534 // Switch dir and apply new state
1535 WindowStateNodeOpener opener(this, false);
1536 opener.SetTo(&entry, false);
1538 // Update PoseView
1539 PoseView()->SwitchDir(&ref, opener.StreamNode());
1541 fIsTrash = FSIsTrashDir(&entry);
1542 fInTrash = FSInTrashDir(&ref);
1544 if (wasInTrash ^ (IsTrash() || InTrash())
1545 || isRoot != PoseView()->TargetModel()->IsRoot())
1546 RepopulateMenus();
1548 if (Navigator() != NULL) {
1549 // update Navigation bar
1550 int32 action = kActionSet;
1551 if (message->FindInt32("action", &action) != B_OK) {
1552 // Design problem? Why does FindInt32 touch
1553 // 'action' at all if he can't find it??
1554 action = kActionSet;
1556 Navigator()->UpdateLocation(PoseView()->TargetModel(),
1557 action);
1560 TrackerSettings settings;
1561 if (settings.ShowNavigator()
1562 || settings.ShowFullPathInTitleBar()) {
1563 SetPathWatchingEnabled(true);
1565 SetSingleWindowBrowseShortcuts(
1566 settings.SingleWindowBrowse());
1568 // Update draggable folder icon
1569 if (fMenuBar != NULL) {
1570 if (!TargetModel()->IsRoot() && !IsTrash()) {
1571 // Folder icon should be visible, but in single
1572 // window navigation, it might not be.
1573 if (fDraggableIcon != NULL) {
1574 IconCache::sIconCache->IconChanged(
1575 TargetModel());
1576 fDraggableIcon->Invalidate();
1577 } else
1578 _AddFolderIcon();
1579 } else if (fDraggableIcon != NULL)
1580 fDraggableIcon->RemoveSelf();
1583 // Update window title
1584 UpdateTitle();
1587 break;
1590 case B_REFS_RECEIVED:
1591 if (Dragging()) {
1592 // ref in this message is the target,
1593 // the end point of the drag
1595 entry_ref ref;
1596 if (message->FindRef("refs", &ref) == B_OK) {
1597 fWaitingForRefs = false;
1598 BEntry entry(&ref, true);
1599 // don't copy to printers dir
1600 if (!FSIsPrintersDir(&entry)) {
1601 if (entry.InitCheck() == B_OK
1602 && entry.IsDirectory()) {
1603 Model targetModel(&entry, true, false);
1604 BPoint dropPoint;
1605 uint32 buttons;
1606 PoseView()->GetMouse(&dropPoint, &buttons, true);
1607 PoseView()->HandleDropCommon(fDragMessage,
1608 &targetModel, NULL, PoseView(), dropPoint);
1612 DragStop();
1614 break;
1616 case B_OBSERVER_NOTICE_CHANGE:
1618 int32 observerWhat;
1619 if (message->FindInt32("be:observe_change_what", &observerWhat)
1620 == B_OK) {
1621 TrackerSettings settings;
1622 switch (observerWhat) {
1623 case kWindowsShowFullPathChanged:
1624 UpdateTitle();
1625 if (!IsPathWatchingEnabled()
1626 && settings.ShowFullPathInTitleBar()) {
1627 SetPathWatchingEnabled(true);
1629 if (IsPathWatchingEnabled()
1630 && !(settings.ShowNavigator()
1631 || settings.ShowFullPathInTitleBar())) {
1632 SetPathWatchingEnabled(false);
1634 break;
1636 case kSingleWindowBrowseChanged:
1637 if (settings.SingleWindowBrowse()
1638 && !Navigator()
1639 && TargetModel()->IsDirectory()
1640 && !PoseView()->IsFilePanel()
1641 && !PoseView()->IsDesktopWindow()) {
1642 fNavigator = new BNavigator(TargetModel());
1643 fPoseContainer->GridLayout()->AddView(fNavigator,
1644 0, 0, 2);
1645 fNavigator->Hide();
1646 SetPathWatchingEnabled(settings.ShowNavigator()
1647 || settings.ShowFullPathInTitleBar());
1650 if (!settings.SingleWindowBrowse()
1651 && !fIsDesktop && TargetModel()->IsDesktop()) {
1652 // Close the "Desktop" window, but not the Desktop
1653 this->Quit();
1656 SetSingleWindowBrowseShortcuts(
1657 settings.SingleWindowBrowse());
1658 break;
1660 case kShowNavigatorChanged:
1661 ShowNavigator(settings.ShowNavigator());
1662 if (!IsPathWatchingEnabled()
1663 && settings.ShowNavigator()) {
1664 SetPathWatchingEnabled(true);
1666 if (IsPathWatchingEnabled()
1667 && !(settings.ShowNavigator()
1668 || settings.ShowFullPathInTitleBar())) {
1669 SetPathWatchingEnabled(false);
1671 SetSingleWindowBrowseShortcuts(
1672 settings.SingleWindowBrowse());
1673 break;
1675 case kDontMoveFilesToTrashChanged:
1677 bool dontMoveToTrash
1678 = settings.DontMoveFilesToTrash();
1680 BMenuItem* item
1681 = fFileContextMenu->FindItem(kMoveToTrash);
1682 if (item != NULL) {
1683 item->SetLabel(dontMoveToTrash
1684 ? B_TRANSLATE("Delete")
1685 : B_TRANSLATE("Move to Trash"));
1687 // Deskbar doesn't have a menu bar, so check if
1688 // there is fMenuBar
1689 if (fMenuBar && fFileMenu) {
1690 item = fFileMenu->FindItem(kMoveToTrash);
1691 if (item != NULL) {
1692 item->SetLabel(dontMoveToTrash
1693 ? B_TRANSLATE("Delete")
1694 : B_TRANSLATE("Move to Trash"));
1697 UpdateIfNeeded();
1698 break;
1701 default:
1702 _inherited::MessageReceived(message);
1703 break;
1706 break;
1709 case B_NODE_MONITOR:
1710 UpdateTitle();
1711 break;
1713 default:
1714 _inherited::MessageReceived(message);
1715 break;
1720 void
1721 BContainerWindow::SetCutItem(BMenu* menu)
1723 BMenuItem* item;
1724 if ((item = menu->FindItem(B_CUT)) == NULL
1725 && (item = menu->FindItem(kCutMoreSelectionToClipboard)) == NULL)
1726 return;
1728 item->SetEnabled(PoseView()->SelectionList()->CountItems() > 0
1729 || PoseView() != CurrentFocus());
1731 if (modifiers() & B_SHIFT_KEY) {
1732 item->SetLabel(B_TRANSLATE("Cut more"));
1733 item->SetShortcut('X', B_COMMAND_KEY | B_SHIFT_KEY);
1734 item->SetMessage(new BMessage(kCutMoreSelectionToClipboard));
1735 } else {
1736 item->SetLabel(B_TRANSLATE("Cut"));
1737 item->SetShortcut('X', B_COMMAND_KEY);
1738 item->SetMessage(new BMessage(B_CUT));
1743 void
1744 BContainerWindow::SetCopyItem(BMenu* menu)
1746 BMenuItem* item;
1747 if ((item = menu->FindItem(B_COPY)) == NULL
1748 && (item = menu->FindItem(kCopyMoreSelectionToClipboard)) == NULL) {
1749 return;
1752 item->SetEnabled(PoseView()->SelectionList()->CountItems() > 0
1753 || PoseView() != CurrentFocus());
1755 if (modifiers() & B_SHIFT_KEY) {
1756 item->SetLabel(B_TRANSLATE("Copy more"));
1757 item->SetShortcut('C', B_COMMAND_KEY | B_SHIFT_KEY);
1758 item->SetMessage(new BMessage(kCopyMoreSelectionToClipboard));
1759 } else {
1760 item->SetLabel(B_TRANSLATE("Copy"));
1761 item->SetShortcut('C', B_COMMAND_KEY);
1762 item->SetMessage(new BMessage(B_COPY));
1767 void
1768 BContainerWindow::SetPasteItem(BMenu* menu)
1770 BMenuItem* item;
1771 if ((item = menu->FindItem(B_PASTE)) == NULL
1772 && (item = menu->FindItem(kPasteLinksFromClipboard)) == NULL) {
1773 return;
1776 item->SetEnabled(FSClipboardHasRefs() || PoseView() != CurrentFocus());
1778 if (modifiers() & B_SHIFT_KEY) {
1779 item->SetLabel(B_TRANSLATE("Paste links"));
1780 item->SetShortcut('V', B_COMMAND_KEY | B_SHIFT_KEY);
1781 item->SetMessage(new BMessage(kPasteLinksFromClipboard));
1782 } else {
1783 item->SetLabel(B_TRANSLATE("Paste"));
1784 item->SetShortcut('V', B_COMMAND_KEY);
1785 item->SetMessage(new BMessage(B_PASTE));
1790 void
1791 BContainerWindow::SetArrangeMenu(BMenu* menu)
1793 BMenuItem* item;
1794 if ((item = menu->FindItem(kCleanup)) == NULL
1795 && (item = menu->FindItem(kCleanupAll)) == NULL) {
1796 return;
1799 item->Menu()->SetEnabled(PoseView()->CountItems() > 0
1800 && (PoseView()->ViewMode() != kListMode));
1802 BMenu* arrangeMenu;
1804 if (modifiers() & B_SHIFT_KEY) {
1805 item->SetLabel(B_TRANSLATE("Clean up all"));
1806 item->SetShortcut('K', B_COMMAND_KEY | B_SHIFT_KEY);
1807 item->SetMessage(new BMessage(kCleanupAll));
1808 arrangeMenu = item->Menu();
1809 } else {
1810 item->SetLabel(B_TRANSLATE("Clean up"));
1811 item->SetShortcut('K', B_COMMAND_KEY);
1812 item->SetMessage(new BMessage(kCleanup));
1813 arrangeMenu = item->Menu();
1816 MarkArrangeByMenu(arrangeMenu);
1820 void
1821 BContainerWindow::SetCloseItem(BMenu* menu)
1823 BMenuItem* item;
1824 if ((item = menu->FindItem(B_QUIT_REQUESTED)) == NULL
1825 && (item = menu->FindItem(kCloseAllWindows)) == NULL) {
1826 return;
1829 if (modifiers() & B_SHIFT_KEY) {
1830 item->SetLabel(B_TRANSLATE("Close all"));
1831 item->SetShortcut('W', B_COMMAND_KEY | B_SHIFT_KEY);
1832 item->SetTarget(be_app);
1833 item->SetMessage(new BMessage(kCloseAllWindows));
1834 } else {
1835 item->SetLabel(B_TRANSLATE("Close"));
1836 item->SetShortcut('W', B_COMMAND_KEY);
1837 item->SetTarget(this);
1838 item->SetMessage(new BMessage(B_QUIT_REQUESTED));
1843 bool
1844 BContainerWindow::IsShowing(const node_ref* node) const
1846 return PoseView()->Represents(node);
1850 bool
1851 BContainerWindow::IsShowing(const entry_ref* entry) const
1853 return PoseView()->Represents(entry);
1857 void
1858 BContainerWindow::AddMenus()
1860 fFileMenu = new BMenu(B_TRANSLATE("File"));
1861 AddFileMenu(fFileMenu);
1862 fMenuBar->AddItem(fFileMenu);
1863 fWindowMenu = new BMenu(B_TRANSLATE("Window"));
1864 fMenuBar->AddItem(fWindowMenu);
1865 AddWindowMenu(fWindowMenu);
1866 // just create the attribute, decide to add it later
1867 fAttrMenu = new BMenu(B_TRANSLATE("Attributes"));
1868 NewAttributeMenu(fAttrMenu);
1869 PopulateArrangeByMenu(fArrangeByMenu);
1873 void
1874 BContainerWindow::AddFileMenu(BMenu* menu)
1876 if (!PoseView()->IsFilePanel()) {
1877 menu->AddItem(new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS),
1878 new BMessage(kFindButton), 'F'));
1881 if (!TargetModel()->IsQuery() && !TargetModel()->IsVirtualDirectory()
1882 && !IsTrash() && !IsPrintersDir() && !TargetModel()->IsRoot()) {
1883 if (!PoseView()->IsFilePanel()) {
1884 TemplatesMenu* templatesMenu = new TemplatesMenu(PoseView(),
1885 B_TRANSLATE("New"));
1886 menu->AddItem(templatesMenu);
1887 templatesMenu->SetTargetForItems(PoseView());
1888 } else {
1889 menu->AddItem(new BMenuItem(B_TRANSLATE("New folder"),
1890 new BMessage(kNewFolder), 'N'));
1893 menu->AddSeparatorItem();
1895 menu->AddItem(new BMenuItem(B_TRANSLATE("Open"),
1896 new BMessage(kOpenSelection), 'O'));
1897 menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
1898 new BMessage(kGetInfo), 'I'));
1899 menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"),
1900 new BMessage(kEditItem), 'E'));
1902 if (IsTrash() || InTrash()) {
1903 menu->AddItem(new BMenuItem(B_TRANSLATE("Restore"),
1904 new BMessage(kRestoreFromTrash)));
1905 if (IsTrash()) {
1906 // add as first item in menu
1907 menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"),
1908 new BMessage(kEmptyTrash)), 0);
1909 menu->AddItem(new BSeparatorItem(), 1);
1911 } else if (IsPrintersDir()) {
1912 menu->AddItem(new BMenuItem(B_TRANSLATE("Add printer" B_UTF8_ELLIPSIS),
1913 new BMessage(kAddPrinter), 'N'), 0);
1914 menu->AddItem(new BSeparatorItem(), 1);
1915 menu->AddItem(new BMenuItem(B_TRANSLATE("Make active printer"),
1916 new BMessage(kMakeActivePrinter)));
1917 } else if (TargetModel()->IsRoot()) {
1918 BMenuItem* item = new BMenuItem(B_TRANSLATE("Unmount"),
1919 new BMessage(kUnmountVolume), 'U');
1920 item->SetEnabled(false);
1921 menu->AddItem(item);
1922 menu->AddItem(new BMenuItem(
1923 B_TRANSLATE("Mount settings" B_UTF8_ELLIPSIS),
1924 new BMessage(kRunAutomounterSettings)));
1925 } else {
1926 menu->AddItem(new BMenuItem(B_TRANSLATE("Duplicate"),
1927 new BMessage(kDuplicateSelection), 'D'));
1928 menu->AddItem(new BMenuItem(TrackerSettings().DontMoveFilesToTrash()
1929 ? B_TRANSLATE("Delete") : B_TRANSLATE("Move to Trash"),
1930 new BMessage(kMoveToTrash), 'T'));
1931 menu->AddSeparatorItem();
1933 // The "Move To", "Copy To", "Create Link" menus are inserted
1934 // at this place, have a look at:
1935 // BContainerWindow::SetupMoveCopyMenus()
1938 BMenuItem* cutItem = NULL;
1939 BMenuItem* copyItem = NULL;
1940 BMenuItem* pasteItem = NULL;
1941 if (!IsPrintersDir()) {
1942 menu->AddSeparatorItem();
1944 if (!TargetModel()->IsRoot()) {
1945 cutItem = new(std::nothrow) BMenuItem(B_TRANSLATE("Cut"),
1946 new BMessage(B_CUT), 'X');
1947 menu->AddItem(cutItem);
1948 copyItem = new(std::nothrow) BMenuItem(B_TRANSLATE("Copy"),
1949 new BMessage(B_COPY), 'C');
1950 menu->AddItem(copyItem);
1951 pasteItem = new(std::nothrow) BMenuItem(B_TRANSLATE("Paste"),
1952 new BMessage(B_PASTE), 'V');
1953 menu->AddItem(pasteItem);
1954 menu->AddSeparatorItem();
1955 menu->AddItem(new BMenuItem(B_TRANSLATE("Identify"),
1956 new BMessage(kIdentifyEntry)));
1958 BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons"));
1959 addOnMenuItem->SetFont(be_plain_font);
1960 menu->AddItem(addOnMenuItem);
1963 menu->SetTargetForItems(PoseView());
1964 if (cutItem != NULL)
1965 cutItem->SetTarget(this);
1967 if (copyItem != NULL)
1968 copyItem->SetTarget(this);
1970 if (pasteItem != NULL)
1971 pasteItem->SetTarget(this);
1975 void
1976 BContainerWindow::AddWindowMenu(BMenu* menu)
1978 BMenuItem* item;
1980 BMenu* iconSizeMenu = new BMenu(B_TRANSLATE("Icon view"));
1982 BMessage* message = new BMessage(kIconMode);
1983 message->AddInt32("size", 32);
1984 item = new BMenuItem(B_TRANSLATE("32 x 32"), message);
1985 item->SetTarget(PoseView());
1986 iconSizeMenu->AddItem(item);
1988 message = new BMessage(kIconMode);
1989 message->AddInt32("size", 40);
1990 item = new BMenuItem(B_TRANSLATE("40 x 40"), message);
1991 item->SetTarget(PoseView());
1992 iconSizeMenu->AddItem(item);
1994 message = new BMessage(kIconMode);
1995 message->AddInt32("size", 48);
1996 item = new BMenuItem(B_TRANSLATE("48 x 48"), message);
1997 item->SetTarget(PoseView());
1998 iconSizeMenu->AddItem(item);
2000 message = new BMessage(kIconMode);
2001 message->AddInt32("size", 64);
2002 item = new BMenuItem(B_TRANSLATE("64 x 64"), message);
2003 item->SetTarget(PoseView());
2004 iconSizeMenu->AddItem(item);
2006 message = new BMessage(kIconMode);
2007 message->AddInt32("size", 96);
2008 item = new BMenuItem(B_TRANSLATE("96 x 96"), message);
2009 item->SetTarget(PoseView());
2010 iconSizeMenu->AddItem(item);
2012 message = new BMessage(kIconMode);
2013 message->AddInt32("size", 128);
2014 item = new BMenuItem(B_TRANSLATE("128 x 128"), message);
2015 item->SetTarget(PoseView());
2016 iconSizeMenu->AddItem(item);
2018 iconSizeMenu->AddSeparatorItem();
2020 message = new BMessage(kIconMode);
2021 message->AddInt32("scale", 0);
2022 item = new BMenuItem(B_TRANSLATE("Decrease size"), message, '-');
2023 item->SetTarget(PoseView());
2024 iconSizeMenu->AddItem(item);
2026 message = new BMessage(kIconMode);
2027 message->AddInt32("scale", 1);
2028 item = new BMenuItem(B_TRANSLATE("Increase size"), message, '+');
2029 item->SetTarget(PoseView());
2030 iconSizeMenu->AddItem(item);
2032 // A sub menu where the super item can be invoked.
2033 menu->AddItem(iconSizeMenu);
2034 iconSizeMenu->Superitem()->SetShortcut('1', B_COMMAND_KEY);
2035 iconSizeMenu->Superitem()->SetMessage(new BMessage(kIconMode));
2036 iconSizeMenu->Superitem()->SetTarget(PoseView());
2038 item = new BMenuItem(B_TRANSLATE("Mini icon view"),
2039 new BMessage(kMiniIconMode), '2');
2040 item->SetTarget(PoseView());
2041 menu->AddItem(item);
2043 BMenu* listViewMenu = new BMenu(B_TRANSLATE("List view"));
2045 message = new BMessage(kListMode);
2046 message->AddInt32("icon_size", B_MINI_ICON);
2047 item = new BMenuItem(listViewMenu, message);
2048 item->SetShortcut('3', B_COMMAND_KEY);
2049 item->SetTarget(PoseView());
2050 menu->AddItem(item);
2052 message = new BMessage(kListMode);
2053 message->AddInt32("icon_size", B_MINI_ICON);
2054 item = new BMenuItem(B_TRANSLATE("Mini"), message);
2055 item->SetTarget(PoseView());
2056 listViewMenu->AddItem(item);
2058 message = new BMessage(kListMode);
2059 message->AddInt32("icon_size", B_LARGE_ICON);
2060 item = new BMenuItem(B_TRANSLATE("Large"), message);
2061 item->SetTarget(PoseView());
2062 listViewMenu->AddItem(item);
2064 listViewMenu->SetTargetForItems(PoseView());
2066 menu->AddSeparatorItem();
2068 item = new BMenuItem(B_TRANSLATE("Resize to fit"),
2069 new BMessage(kResizeToFit), 'Y');
2070 item->SetTarget(this);
2071 menu->AddItem(item);
2073 fArrangeByMenu = new BMenu(B_TRANSLATE("Arrange by"));
2074 menu->AddItem(fArrangeByMenu);
2076 item = new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS),
2077 new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY);
2078 item->SetTarget(PoseView());
2079 menu->AddItem(item);
2081 item = new BMenuItem(B_TRANSLATE("Select all"),
2082 new BMessage(B_SELECT_ALL), 'A');
2083 item->SetTarget(PoseView());
2084 menu->AddItem(item);
2086 item = new BMenuItem(B_TRANSLATE("Invert selection"),
2087 new BMessage(kInvertSelection), 'S');
2088 item->SetTarget(PoseView());
2089 menu->AddItem(item);
2091 if (!IsTrash()) {
2092 item = new BMenuItem(B_TRANSLATE("Open parent"),
2093 new BMessage(kOpenParentDir), B_UP_ARROW);
2094 item->SetTarget(PoseView());
2095 menu->AddItem(item);
2098 item = new BMenuItem(B_TRANSLATE("Close"),
2099 new BMessage(B_QUIT_REQUESTED), 'W');
2100 item->SetTarget(this);
2101 menu->AddItem(item);
2103 item = new BMenuItem(B_TRANSLATE("Close all in workspace"),
2104 new BMessage(kCloseAllInWorkspace), 'Q');
2105 item->SetTarget(be_app);
2106 menu->AddItem(item);
2108 menu->AddSeparatorItem();
2110 item = new BMenuItem(B_TRANSLATE("Preferences" B_UTF8_ELLIPSIS),
2111 new BMessage(kShowSettingsWindow));
2112 item->SetTarget(be_app);
2113 menu->AddItem(item);
2117 void
2118 BContainerWindow::AddShortcuts()
2120 // add equivalents of the menu shortcuts to the menuless desktop window
2121 ASSERT(!IsTrash());
2122 ASSERT(!PoseView()->IsFilePanel());
2123 ASSERT(!TargetModel()->IsQuery());
2124 ASSERT(!TargetModel()->IsVirtualDirectory());
2126 AddShortcut('X', B_COMMAND_KEY | B_SHIFT_KEY,
2127 new BMessage(kCutMoreSelectionToClipboard), this);
2128 AddShortcut('C', B_COMMAND_KEY | B_SHIFT_KEY,
2129 new BMessage(kCopyMoreSelectionToClipboard), this);
2130 AddShortcut('F', B_COMMAND_KEY,
2131 new BMessage(kFindButton), PoseView());
2132 AddShortcut('N', B_COMMAND_KEY,
2133 new BMessage(kNewFolder), PoseView());
2134 AddShortcut('O', B_COMMAND_KEY,
2135 new BMessage(kOpenSelection), PoseView());
2136 AddShortcut('I', B_COMMAND_KEY,
2137 new BMessage(kGetInfo), PoseView());
2138 AddShortcut('E', B_COMMAND_KEY,
2139 new BMessage(kEditItem), PoseView());
2140 AddShortcut('D', B_COMMAND_KEY,
2141 new BMessage(kDuplicateSelection), PoseView());
2142 AddShortcut('T', B_COMMAND_KEY,
2143 new BMessage(kMoveToTrash), PoseView());
2144 AddShortcut('K', B_COMMAND_KEY,
2145 new BMessage(kCleanup), PoseView());
2146 AddShortcut('A', B_COMMAND_KEY,
2147 new BMessage(B_SELECT_ALL), PoseView());
2148 AddShortcut('S', B_COMMAND_KEY,
2149 new BMessage(kInvertSelection), PoseView());
2150 AddShortcut('A', B_COMMAND_KEY | B_SHIFT_KEY,
2151 new BMessage(kShowSelectionWindow), PoseView());
2152 AddShortcut('G', B_COMMAND_KEY,
2153 new BMessage(kEditQuery), PoseView());
2154 // it is ok to add a global Edit query shortcut here, PoseView will
2155 // filter out cases where selected pose is not a query
2156 AddShortcut('U', B_COMMAND_KEY,
2157 new BMessage(kUnmountVolume), PoseView());
2158 AddShortcut(B_UP_ARROW, B_COMMAND_KEY,
2159 new BMessage(kOpenParentDir), PoseView());
2160 AddShortcut('O', B_COMMAND_KEY | B_CONTROL_KEY,
2161 new BMessage(kOpenSelectionWith), PoseView());
2163 BMessage* decreaseSize = new BMessage(kIconMode);
2164 decreaseSize->AddInt32("scale", 0);
2165 AddShortcut('-', B_COMMAND_KEY, decreaseSize, PoseView());
2167 BMessage* increaseSize = new BMessage(kIconMode);
2168 increaseSize->AddInt32("scale", 1);
2169 AddShortcut('+', B_COMMAND_KEY, increaseSize, PoseView());
2173 void
2174 BContainerWindow::MenusBeginning()
2176 if (fMenuBar == NULL)
2177 return;
2179 if (CurrentMessage() != NULL && CurrentMessage()->what == B_MOUSE_DOWN) {
2180 // don't commit active pose if only a keyboard shortcut is
2181 // invoked - this would prevent Cut/Copy/Paste from working
2182 fPoseView->CommitActivePose();
2185 // File menu
2186 int32 selectCount = PoseView()->SelectionList()->CountItems();
2188 SetupOpenWithMenu(fFileMenu);
2189 SetupMoveCopyMenus(selectCount
2190 ? PoseView()->SelectionList()->FirstItem()->TargetModel()->EntryRef()
2191 : NULL, fFileMenu);
2193 if (TargetModel()->IsRoot()) {
2194 BVolume boot;
2195 BVolumeRoster().GetBootVolume(&boot);
2197 bool ejectableVolumeSelected = false;
2199 int32 count = PoseView()->SelectionList()->CountItems();
2200 for (int32 index = 0; index < count; index++) {
2201 Model* model
2202 = PoseView()->SelectionList()->ItemAt(index)->TargetModel();
2203 if (model->IsVolume()) {
2204 BVolume volume;
2205 volume.SetTo(model->NodeRef()->device);
2206 if (volume != boot) {
2207 ejectableVolumeSelected = true;
2208 break;
2212 BMenuItem* item = fMenuBar->FindItem(kUnmountVolume);
2213 if (item != NULL)
2214 item->SetEnabled(ejectableVolumeSelected);
2217 UpdateMenu(fMenuBar, kMenuBarContext);
2219 AddMimeTypesToMenu(fAttrMenu);
2221 if (IsPrintersDir()) {
2222 EnableNamedMenuItem(fFileMenu, B_TRANSLATE("Make active printer"),
2223 selectCount == 1);
2228 void
2229 BContainerWindow::MenusEnded()
2231 // when we're done we want to clear nav menus for next time
2232 DeleteSubmenu(fNavigationItem);
2233 DeleteSubmenu(fMoveToItem);
2234 DeleteSubmenu(fCopyToItem);
2235 DeleteSubmenu(fCreateLinkItem);
2236 DeleteSubmenu(fOpenWithItem);
2240 void
2241 BContainerWindow::SetupNavigationMenu(const entry_ref* ref, BMenu* parent)
2243 // start by removing nav item (and separator) from old menu
2244 if (fNavigationItem != NULL) {
2245 BMenu* menu = fNavigationItem->Menu();
2246 if (menu != NULL) {
2247 menu->RemoveItem(fNavigationItem);
2248 BMenuItem* item = menu->RemoveItem((int32)0);
2249 ASSERT(item != fNavigationItem);
2250 delete item;
2254 // if we weren't passed a ref then we're navigating this window
2255 if (ref == NULL)
2256 ref = TargetModel()->EntryRef();
2258 BEntry entry;
2259 if (entry.SetTo(ref) != B_OK)
2260 return;
2262 // only navigate directories and queries (check for symlink here)
2263 Model model(&entry);
2264 entry_ref resolvedRef;
2266 if (model.InitCheck() != B_OK
2267 || (!model.IsContainer() && !model.IsSymLink())) {
2268 return;
2271 if (model.IsSymLink()) {
2272 if (entry.SetTo(model.EntryRef(), true) != B_OK)
2273 return;
2275 Model resolvedModel(&entry);
2276 if (resolvedModel.InitCheck() != B_OK || !resolvedModel.IsContainer())
2277 return;
2279 entry.GetRef(&resolvedRef);
2280 ref = &resolvedRef;
2283 if (fNavigationItem == NULL) {
2284 fNavigationItem = new ModelMenuItem(&model,
2285 new BNavMenu(model.Name(), B_REFS_RECEIVED, be_app, this));
2288 // setup a navigation menu item which will dynamically load items
2289 // as menu items are traversed
2290 BNavMenu* navMenu = dynamic_cast<BNavMenu*>(fNavigationItem->Submenu());
2291 navMenu->SetNavDir(ref);
2292 fNavigationItem->SetLabel(model.Name());
2293 fNavigationItem->SetEntry(&entry);
2295 parent->AddItem(fNavigationItem, 0);
2296 parent->AddItem(new BSeparatorItem(), 1);
2298 BMessage* message = new BMessage(B_REFS_RECEIVED);
2299 message->AddRef("refs", ref);
2300 fNavigationItem->SetMessage(message);
2301 fNavigationItem->SetTarget(be_app);
2303 if (!Dragging())
2304 parent->SetTrackingHook(NULL, NULL);
2308 void
2309 BContainerWindow::SetUpEditQueryItem(BMenu* menu)
2311 ASSERT(menu);
2312 // File menu
2313 int32 selectCount = PoseView()->SelectionList()->CountItems();
2315 // add Edit query if appropriate
2316 bool queryInSelection = false;
2317 if (selectCount && selectCount < 100) {
2318 // only do this for a limited number of selected poses
2320 // if any queries selected, add an edit query menu item
2321 for (int32 index = 0; index < selectCount; index++) {
2322 BPose* pose = PoseView()->SelectionList()->ItemAt(index);
2323 Model model(pose->TargetModel()->EntryRef(), true);
2324 if (model.InitCheck() != B_OK)
2325 continue;
2327 if (model.IsQuery() || model.IsQueryTemplate()) {
2328 queryInSelection = true;
2329 break;
2334 bool poseViewIsQuery = TargetModel()->IsQuery();
2335 // if the view is a query pose view, add edit query menu item
2337 BMenuItem* item = menu->FindItem(kEditQuery);
2338 if (!poseViewIsQuery && !queryInSelection && item != NULL)
2339 item->Menu()->RemoveItem(item);
2340 else if ((poseViewIsQuery || queryInSelection) && item == NULL) {
2341 // add edit query item after Open
2342 item = menu->FindItem(kOpenSelection);
2343 if (item) {
2344 int32 itemIndex = item->Menu()->IndexOf(item);
2345 BMenuItem* query = new BMenuItem(B_TRANSLATE("Edit query"),
2346 new BMessage(kEditQuery), 'G');
2347 item->Menu()->AddItem(query, itemIndex + 1);
2348 query->SetTarget(PoseView());
2354 void
2355 BContainerWindow::SetupOpenWithMenu(BMenu* parent)
2357 // start by removing nav item (and separator) from old menu
2358 if (fOpenWithItem) {
2359 BMenu* menu = fOpenWithItem->Menu();
2360 if (menu != NULL)
2361 menu->RemoveItem(fOpenWithItem);
2363 delete fOpenWithItem;
2364 fOpenWithItem = 0;
2367 if (PoseView()->SelectionList()->CountItems() == 0) {
2368 // no selection, nothing to open
2369 return;
2372 if (TargetModel()->IsRoot()) {
2373 // don't add ourselves if we are root
2374 return;
2377 // ToDo:
2378 // check if only item in selection list is the root
2379 // and do not add if true
2381 // add after "Open"
2382 BMenuItem* item = parent->FindItem(kOpenSelection);
2384 int32 count = PoseView()->SelectionList()->CountItems();
2385 if (count == 0)
2386 return;
2388 // build a list of all refs to open
2389 BMessage message(B_REFS_RECEIVED);
2390 for (int32 index = 0; index < count; index++) {
2391 BPose* pose = PoseView()->SelectionList()->ItemAt(index);
2392 message.AddRef("refs", pose->TargetModel()->EntryRef());
2395 // add Tracker token so that refs received recipients can script us
2396 message.AddMessenger("TrackerViewToken", BMessenger(PoseView()));
2398 int32 index = item->Menu()->IndexOf(item);
2399 fOpenWithItem = new BMenuItem(
2400 new OpenWithMenu(B_TRANSLATE("Open with" B_UTF8_ELLIPSIS),
2401 &message, this, be_app), new BMessage(kOpenSelectionWith));
2402 fOpenWithItem->SetTarget(PoseView());
2403 fOpenWithItem->SetShortcut('O', B_COMMAND_KEY | B_CONTROL_KEY);
2405 item->Menu()->AddItem(fOpenWithItem, index + 1);
2409 void
2410 BContainerWindow::PopulateMoveCopyNavMenu(BNavMenu* navMenu, uint32 what,
2411 const entry_ref* ref, bool addLocalOnly)
2413 BVolume volume;
2414 BVolumeRoster volumeRoster;
2415 BDirectory directory;
2416 BEntry entry;
2417 BPath path;
2418 Model model;
2419 dev_t device = ref->device;
2421 int32 volumeCount = 0;
2423 navMenu->RemoveItems(0, navMenu->CountItems(), true);
2425 // count persistent writable volumes
2426 volumeRoster.Rewind();
2427 while (volumeRoster.GetNextVolume(&volume) == B_OK)
2428 if (!volume.IsReadOnly() && volume.IsPersistent())
2429 volumeCount++;
2431 // add the current folder
2432 if (entry.SetTo(ref) == B_OK
2433 && entry.GetParent(&entry) == B_OK
2434 && model.SetTo(&entry) == B_OK) {
2435 BNavMenu* menu = new BNavMenu(B_TRANSLATE("Current folder"), what,
2436 this);
2437 menu->SetNavDir(model.EntryRef());
2438 menu->SetShowParent(true);
2440 BMenuItem* item = new SpecialModelMenuItem(&model,menu);
2441 item->SetMessage(new BMessage((uint32)what));
2443 navMenu->AddItem(item);
2446 // add the recent folder menu
2447 // the "Tracker" settings directory is only used to get its icon
2448 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
2449 path.Append("Tracker");
2450 if (entry.SetTo(path.Path()) == B_OK
2451 && model.SetTo(&entry) == B_OK) {
2452 BMenu* menu = new RecentsMenu(B_TRANSLATE("Recent folders"),
2453 kRecentFolders, what, this);
2455 BMenuItem* item = new SpecialModelMenuItem(&model,menu);
2456 item->SetMessage(new BMessage((uint32)what));
2458 navMenu->AddItem(item);
2462 // add Desktop
2463 FSGetBootDeskDir(&directory);
2464 if (directory.InitCheck() == B_OK
2465 && directory.GetEntry(&entry) == B_OK
2466 && model.SetTo(&entry) == B_OK)
2467 navMenu->AddNavDir(&model, what, this, true);
2468 // ask NavMenu to populate submenu for us
2470 // add the home dir
2471 if (find_directory(B_USER_DIRECTORY, &path) == B_OK
2472 && entry.SetTo(path.Path()) == B_OK
2473 && model.SetTo(&entry) == B_OK)
2474 navMenu->AddNavDir(&model, what, this, true);
2476 navMenu->AddSeparatorItem();
2478 // either add all mounted volumes (for copy), or all the top-level
2479 // directories from the same device (for move)
2480 // ToDo: can be changed if cross-device moves are implemented
2482 if (addLocalOnly || volumeCount < 2) {
2483 // add volume this item lives on
2484 if (volume.SetTo(device) == B_OK
2485 && volume.GetRootDirectory(&directory) == B_OK
2486 && directory.GetEntry(&entry) == B_OK
2487 && model.SetTo(&entry) == B_OK) {
2488 navMenu->AddNavDir(&model, what, this, false);
2489 // do not have submenu populated
2491 navMenu->SetNavDir(model.EntryRef());
2493 } else {
2494 // add all persistent writable volumes
2495 volumeRoster.Rewind();
2496 while (volumeRoster.GetNextVolume(&volume) == B_OK) {
2497 if (volume.IsReadOnly() || !volume.IsPersistent())
2498 continue;
2500 // add root dir
2501 if (volume.GetRootDirectory(&directory) == B_OK
2502 && directory.GetEntry(&entry) == B_OK
2503 && model.SetTo(&entry) == B_OK) {
2504 navMenu->AddNavDir(&model, what, this, true);
2505 // ask NavMenu to populate submenu for us
2512 void
2513 BContainerWindow::SetupMoveCopyMenus(const entry_ref* item_ref, BMenu* parent)
2515 if (IsTrash() || InTrash() || IsPrintersDir() || !fMoveToItem
2516 || !fCopyToItem || !fCreateLinkItem || TargetModel()->IsRoot()) {
2517 return;
2520 // Grab the modifiers state since we use it twice
2521 uint32 modifierKeys = modifiers();
2523 // re-parent items to this menu since they're shared
2524 int32 index;
2525 BMenuItem* trash = parent->FindItem(kMoveToTrash);
2526 if (trash)
2527 index = parent->IndexOf(trash) + 2;
2528 else
2529 index = 0;
2531 if (fMoveToItem->Menu() != parent) {
2532 if (fMoveToItem->Menu())
2533 fMoveToItem->Menu()->RemoveItem(fMoveToItem);
2535 parent->AddItem(fMoveToItem, index++);
2538 if (fCopyToItem->Menu() != parent) {
2539 if (fCopyToItem->Menu())
2540 fCopyToItem->Menu()->RemoveItem(fCopyToItem);
2542 parent->AddItem(fCopyToItem, index++);
2545 if (fCreateLinkItem->Menu() != parent) {
2546 if (fCreateLinkItem->Menu())
2547 fCreateLinkItem->Menu()->RemoveItem(fCreateLinkItem);
2549 parent->AddItem(fCreateLinkItem, index);
2552 // Set the "Create Link" item label here so it
2553 // appears correctly when menus are disabled, too.
2554 if (modifierKeys & B_SHIFT_KEY)
2555 fCreateLinkItem->SetLabel(B_TRANSLATE("Create relative link"));
2556 else
2557 fCreateLinkItem->SetLabel(B_TRANSLATE("Create link"));
2559 // only enable once the menus are built
2560 fMoveToItem->SetEnabled(false);
2561 fCopyToItem->SetEnabled(false);
2562 fCreateLinkItem->SetEnabled(false);
2564 // get ref for item which is selected
2565 BEntry entry;
2566 if (entry.SetTo(item_ref) != B_OK)
2567 return;
2569 Model tempModel(&entry);
2570 if (tempModel.InitCheck() != B_OK)
2571 return;
2573 if (tempModel.IsRoot() || tempModel.IsVolume())
2574 return;
2576 // configure "Move to" menu item
2577 PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>(fMoveToItem->Submenu()),
2578 kMoveSelectionTo, item_ref, true);
2580 // configure "Copy to" menu item
2581 // add all mounted volumes (except the one this item lives on)
2582 PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>(fCopyToItem->Submenu()),
2583 kCopySelectionTo, item_ref, false);
2585 // Set "Create Link" menu item message and
2586 // add all mounted volumes (except the one this item lives on)
2587 if (modifierKeys & B_SHIFT_KEY) {
2588 fCreateLinkItem->SetMessage(new BMessage(kCreateRelativeLink));
2589 PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>
2590 (fCreateLinkItem->Submenu()),
2591 kCreateRelativeLink, item_ref, false);
2592 } else {
2593 fCreateLinkItem->SetMessage(new BMessage(kCreateLink));
2594 PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>
2595 (fCreateLinkItem->Submenu()),
2596 kCreateLink, item_ref, false);
2599 fMoveToItem->SetEnabled(true);
2600 fCopyToItem->SetEnabled(true);
2601 fCreateLinkItem->SetEnabled(true);
2603 // Set the "Identify" item label
2604 BMenuItem* identifyItem = parent->FindItem(kIdentifyEntry);
2605 if (identifyItem != NULL) {
2606 if (modifierKeys & B_SHIFT_KEY) {
2607 identifyItem->SetLabel(B_TRANSLATE("Force identify"));
2608 identifyItem->Message()->ReplaceBool("force", true);
2609 } else {
2610 identifyItem->SetLabel(B_TRANSLATE("Identify"));
2611 identifyItem->Message()->ReplaceBool("force", false);
2617 uint32
2618 BContainerWindow::ShowDropContextMenu(BPoint loc)
2620 BPoint global(loc);
2622 PoseView()->ConvertToScreen(&global);
2623 PoseView()->CommitActivePose();
2625 // Change the "Create Link" item - allow user to
2626 // create relative links with the Shift key down.
2627 BMenuItem* item = fDropContextMenu->FindItem(kCreateLink);
2628 if (item == NULL)
2629 item = fDropContextMenu->FindItem(kCreateRelativeLink);
2630 if (item && (modifiers() & B_SHIFT_KEY)) {
2631 item->SetLabel(B_TRANSLATE("Create relative link here"));
2632 item->SetMessage(new BMessage(kCreateRelativeLink));
2633 } else if (item) {
2634 item->SetLabel(B_TRANSLATE("Create link here"));
2635 item->SetMessage(new BMessage(kCreateLink));
2638 item = fDropContextMenu->Go(global, true, true);
2639 if (item)
2640 return item->Command();
2642 return 0;
2646 void
2647 BContainerWindow::ShowContextMenu(BPoint loc, const entry_ref* ref, BView*)
2649 ASSERT(IsLocked());
2650 BPoint global(loc);
2651 PoseView()->ConvertToScreen(&global);
2652 PoseView()->CommitActivePose();
2654 if (ref != NULL) {
2655 // clicked on a pose, show file or volume context menu
2656 Model model(ref);
2658 if (model.IsTrash()) {
2659 if (fTrashContextMenu->Window() || Dragging())
2660 return;
2662 DeleteSubmenu(fNavigationItem);
2664 // selected item was trash, show the trash context menu instead
2666 EnableNamedMenuItem(fTrashContextMenu, kEmptyTrash,
2667 static_cast<TTracker*>(be_app)->TrashFull());
2669 SetupNavigationMenu(ref, fTrashContextMenu);
2670 fTrashContextMenu->Go(global, true, true, true);
2671 } else {
2672 bool showAsVolume = false;
2673 bool filePanel = PoseView()->IsFilePanel();
2675 if (Dragging()) {
2676 fContextMenu = NULL;
2678 BEntry entry;
2679 model.GetEntry(&entry);
2681 // only show for directories (directory, volume, root)
2683 // don't show a popup for the trash or printers
2684 // trash is handled in DeskWindow
2686 // since this menu is opened asynchronously
2687 // we need to make sure we don't open it more
2688 // than once, the IsShowing flag is set in
2689 // SlowContextPopup::AttachedToWindow and
2690 // reset in DetachedFromWindow
2691 // see the notes in SlowContextPopup::AttachedToWindow
2693 if (!FSIsPrintersDir(&entry)
2694 && !fDragContextMenu->IsShowing()) {
2695 //printf("ShowContextMenu - target is %s %i\n",
2696 // ref->name, IsShowing(ref));
2697 fDragContextMenu->ClearMenu();
2699 // in case the ref is a symlink, resolve it
2700 // only pop open for directories
2701 BEntry resolvedEntry(ref, true);
2702 if (!resolvedEntry.IsDirectory())
2703 return;
2705 entry_ref resolvedRef;
2706 resolvedEntry.GetRef(&resolvedRef);
2708 // use the resolved ref for the menu
2709 fDragContextMenu->SetNavDir(&resolvedRef);
2710 fDragContextMenu->SetTypesList(fCachedTypesList);
2711 fDragContextMenu->SetTarget(BMessenger(this));
2712 BPoseView* poseView = PoseView();
2713 if (poseView != NULL) {
2714 BMessenger target(poseView);
2715 fDragContextMenu->InitTrackingHook(
2716 &BPoseView::MenuTrackingHook, &target,
2717 fDragMessage);
2720 // this is now asynchronous so that we don't
2721 // deadlock in Window::Quit,
2722 fDragContextMenu->Go(global, true, false, true);
2725 return;
2726 } else if (TargetModel()->IsRoot() || model.IsVolume()) {
2727 fContextMenu = fVolumeContextMenu;
2728 showAsVolume = true;
2729 } else
2730 fContextMenu = fFileContextMenu;
2732 // clean up items from last context menu
2734 if (fContextMenu != NULL) {
2735 if (fContextMenu->Window())
2736 return;
2737 else
2738 MenusEnded();
2740 if (model.InitCheck() == B_OK) { // ??? Do I need this ???
2741 if (showAsVolume) {
2742 // non-volume enable/disable copy, move, identify
2743 EnableNamedMenuItem(fContextMenu, kDuplicateSelection,
2744 false);
2745 EnableNamedMenuItem(fContextMenu, kMoveToTrash, false);
2746 EnableNamedMenuItem(fContextMenu, kIdentifyEntry,
2747 false);
2749 // volume model, enable/disable the Unmount item
2750 bool ejectableVolumeSelected = false;
2752 BVolume boot;
2753 BVolumeRoster().GetBootVolume(&boot);
2754 BVolume volume;
2755 volume.SetTo(model.NodeRef()->device);
2756 if (volume != boot)
2757 ejectableVolumeSelected = true;
2759 EnableNamedMenuItem(fContextMenu,
2760 B_TRANSLATE("Unmount"), ejectableVolumeSelected);
2764 SetupNavigationMenu(ref, fContextMenu);
2765 if (!showAsVolume && !filePanel) {
2766 SetupMoveCopyMenus(ref, fContextMenu);
2767 SetupOpenWithMenu(fContextMenu);
2770 UpdateMenu(fContextMenu, kPosePopUpContext);
2772 fContextMenu->Go(global, true, true, true);
2775 } else if (fWindowContextMenu != NULL) {
2776 if (fWindowContextMenu->Window())
2777 return;
2779 // Repopulate desktop menu if IsDesktop
2780 if (fIsDesktop)
2781 RepopulateMenus();
2783 MenusEnded();
2785 // clicked on a window, show window context menu
2787 SetupNavigationMenu(ref, fWindowContextMenu);
2788 UpdateMenu(fWindowContextMenu, kWindowPopUpContext);
2790 fWindowContextMenu->Go(global, true, true, true);
2793 fContextMenu = NULL;
2797 void
2798 BContainerWindow::AddFileContextMenus(BMenu* menu)
2800 menu->AddItem(new BMenuItem(B_TRANSLATE("Open"),
2801 new BMessage(kOpenSelection), 'O'));
2802 menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
2803 new BMessage(kGetInfo), 'I'));
2804 menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"),
2805 new BMessage(kEditItem), 'E'));
2807 if (!IsTrash() && !InTrash() && !IsPrintersDir()) {
2808 menu->AddItem(new BMenuItem(B_TRANSLATE("Duplicate"),
2809 new BMessage(kDuplicateSelection), 'D'));
2812 if (!IsTrash() && !InTrash()) {
2813 menu->AddItem(new BMenuItem(TrackerSettings().DontMoveFilesToTrash()
2814 ? B_TRANSLATE("Delete") : B_TRANSLATE("Move to Trash"),
2815 new BMessage(kMoveToTrash), 'T'));
2816 if (!IsPrintersDir()) {
2817 // add separator for copy to/move to items (navigation items)
2818 menu->AddSeparatorItem();
2820 } else {
2821 menu->AddItem(new BMenuItem(B_TRANSLATE("Delete"),
2822 new BMessage(kDelete), 0));
2823 menu->AddItem(new BMenuItem(B_TRANSLATE("Restore"),
2824 new BMessage(kRestoreFromTrash), 0));
2827 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
2828 menu->AddSeparatorItem();
2829 BMenuItem* cutItem = new BMenuItem(B_TRANSLATE("Cut"),
2830 new BMessage(B_CUT), 'X');
2831 menu->AddItem(cutItem);
2832 BMenuItem* copyItem = new BMenuItem(B_TRANSLATE("Copy"),
2833 new BMessage(B_COPY), 'C');
2834 menu->AddItem(copyItem);
2835 #endif
2837 menu->AddSeparatorItem();
2838 BMessage* message = new BMessage(kIdentifyEntry);
2839 message->AddBool("force", false);
2840 menu->AddItem(new BMenuItem(B_TRANSLATE("Identify"), message));
2841 BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons"));
2842 addOnMenuItem->SetFont(be_plain_font);
2843 menu->AddItem(addOnMenuItem);
2845 // set targets as needed
2846 menu->SetTargetForItems(PoseView());
2847 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
2848 cutItem->SetTarget(this);
2849 copyItem->SetTarget(this);
2850 #endif
2854 void
2855 BContainerWindow::AddVolumeContextMenus(BMenu* menu)
2857 menu->AddItem(new BMenuItem(B_TRANSLATE("Open"),
2858 new BMessage(kOpenSelection), 'O'));
2859 menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
2860 new BMessage(kGetInfo), 'I'));
2861 menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"),
2862 new BMessage(kEditItem), 'E'));
2864 menu->AddSeparatorItem();
2865 menu->AddItem(new MountMenu(B_TRANSLATE("Mount")));
2867 BMenuItem* item = new BMenuItem(B_TRANSLATE("Unmount"),
2868 new BMessage(kUnmountVolume), 'U');
2869 item->SetEnabled(false);
2870 menu->AddItem(item);
2872 menu->AddSeparatorItem();
2873 menu->AddItem(new BMenu(B_TRANSLATE("Add-ons")));
2875 menu->SetTargetForItems(PoseView());
2879 void
2880 BContainerWindow::AddWindowContextMenus(BMenu* menu)
2882 // create context sensitive menu for empty area of window
2883 // since we check view mode before display, this should be a radio
2884 // mode menu
2886 Model* targetModel = TargetModel();
2887 ASSERT(targetModel != NULL);
2889 bool needSeparator = true;
2890 if (IsTrash()) {
2891 menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"),
2892 new BMessage(kEmptyTrash)));
2893 } else if (IsPrintersDir()) {
2894 menu->AddItem(new BMenuItem(B_TRANSLATE("Add printer" B_UTF8_ELLIPSIS),
2895 new BMessage(kAddPrinter), 'N'));
2896 } else if (InTrash() || targetModel->IsRoot()) {
2897 needSeparator = false;
2898 } else {
2899 TemplatesMenu* templatesMenu = new TemplatesMenu(PoseView(),
2900 B_TRANSLATE("New"));
2901 menu->AddItem(templatesMenu);
2902 templatesMenu->SetTargetForItems(PoseView());
2903 templatesMenu->SetFont(be_plain_font);
2906 if (needSeparator)
2907 menu->AddSeparatorItem();
2909 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
2910 BMenuItem* pasteItem = new BMenuItem("Paste", new BMessage(B_PASTE), 'V');
2911 menu->AddItem(pasteItem);
2912 menu->AddSeparatorItem();
2913 #endif
2915 BMenu* arrangeBy = new BMenu(B_TRANSLATE("Arrange by"));
2916 PopulateArrangeByMenu(arrangeBy);
2917 menu->AddItem(arrangeBy);
2919 menu->AddItem(new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS),
2920 new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY));
2921 menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"),
2922 new BMessage(B_SELECT_ALL), 'A'));
2923 if (!IsTrash()) {
2924 menu->AddItem(new BMenuItem(B_TRANSLATE("Open parent"),
2925 new BMessage(kOpenParentDir), B_UP_ARROW));
2928 if (targetModel->IsRoot()) {
2929 menu->AddSeparatorItem();
2930 menu->AddItem(new MountMenu(B_TRANSLATE("Mount")));
2933 menu->AddSeparatorItem();
2934 BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons"));
2935 addOnMenuItem->SetFont(be_plain_font);
2936 menu->AddItem(addOnMenuItem);
2938 #if DEBUG
2939 menu->AddSeparatorItem();
2940 BMenuItem* testing = new BMenuItem("Test icon cache",
2941 new BMessage(kTestIconCache));
2942 menu->AddItem(testing);
2943 #endif
2945 // target items as needed
2946 menu->SetTargetForItems(PoseView());
2947 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
2948 pasteItem->SetTarget(this);
2949 #endif
2953 void
2954 BContainerWindow::AddDropContextMenus(BMenu* menu)
2956 menu->AddItem(new BMenuItem(B_TRANSLATE("Create link here"),
2957 new BMessage(kCreateLink)));
2958 menu->AddItem(new BMenuItem(B_TRANSLATE("Move here"),
2959 new BMessage(kMoveSelectionTo)));
2960 menu->AddItem(new BMenuItem(B_TRANSLATE("Copy here"),
2961 new BMessage(kCopySelectionTo)));
2962 menu->AddSeparatorItem();
2963 menu->AddItem(new BMenuItem(B_TRANSLATE("Cancel"),
2964 new BMessage(kCancelButton)));
2968 void
2969 BContainerWindow::AddTrashContextMenus(BMenu* menu)
2971 // setup special trash context menu
2972 menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"),
2973 new BMessage(kEmptyTrash)));
2974 menu->AddItem(new BMenuItem(B_TRANSLATE("Open"),
2975 new BMessage(kOpenSelection), 'O'));
2976 menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
2977 new BMessage(kGetInfo), 'I'));
2978 menu->SetTargetForItems(PoseView());
2982 void
2983 BContainerWindow::EachAddon(bool (*eachAddon)(const Model*, const char*,
2984 uint32 shortcut, uint32 modifiers, bool primary, void* context),
2985 void* passThru, BStringList& mimeTypes)
2987 AutoLock<LockingList<AddonShortcut> > lock(fAddonsList);
2988 if (lock.IsLocked()) {
2989 for (int i = fAddonsList->CountItems() - 1; i >= 0; i--) {
2990 struct AddonShortcut* item = fAddonsList->ItemAt(i);
2991 bool primary = false;
2993 if (mimeTypes.CountStrings() > 0) {
2994 BFile file(item->model->EntryRef(), B_READ_ONLY);
2995 if (file.InitCheck() == B_OK) {
2996 BAppFileInfo info(&file);
2997 if (info.InitCheck() == B_OK) {
2998 bool secondary = true;
3000 // does this add-on has types set at all?
3001 BMessage message;
3002 if (info.GetSupportedTypes(&message) == B_OK) {
3003 type_code typeCode;
3004 int32 count;
3005 if (message.GetInfo("types", &typeCode,
3006 &count) == B_OK) {
3007 secondary = false;
3011 // check all supported types if it has some set
3012 if (!secondary) {
3013 for (int32 i = mimeTypes.CountStrings();
3014 !primary && i-- > 0;) {
3015 BString type = mimeTypes.StringAt(i);
3016 if (info.IsSupportedType(type.String())) {
3017 BMimeType mimeType(type.String());
3018 if (info.Supports(&mimeType))
3019 primary = true;
3020 else
3021 secondary = true;
3026 if (!secondary && !primary)
3027 continue;
3031 ((eachAddon)(item->model, item->model->Name(), item->key,
3032 item->modifiers, primary, passThru));
3038 void
3039 BContainerWindow::BuildMimeTypeList(BStringList& mimeTypes)
3041 int32 count = PoseView()->SelectionList()->CountItems();
3042 if (count <= 0) {
3043 // just add the type of the current directory
3044 AddMimeTypeString(mimeTypes, TargetModel());
3045 } else {
3046 _UpdateSelectionMIMEInfo();
3047 for (int32 index = 0; index < count; index++) {
3048 BPose* pose = PoseView()->SelectionList()->ItemAt(index);
3049 AddMimeTypeString(mimeTypes, pose->TargetModel());
3050 // If it's a symlink, resolves it and add the Target's MimeType
3051 if (pose->TargetModel()->IsSymLink()) {
3052 Model* resolved = new Model(
3053 pose->TargetModel()->EntryRef(), true, true);
3054 if (resolved->InitCheck() == B_OK)
3055 AddMimeTypeString(mimeTypes, resolved);
3057 delete resolved;
3064 void
3065 BContainerWindow::BuildAddOnMenu(BMenu* menu)
3067 BMenuItem* item = menu->FindItem(B_TRANSLATE("Add-ons"));
3068 if (menu->IndexOf(item) == 0) {
3069 // the folder of the context menu seems to be named "Add-Ons"
3070 // so we just take the last menu item, which is correct if not
3071 // build with debug option
3072 item = menu->ItemAt(menu->CountItems() - 1);
3074 if (item == NULL)
3075 return;
3077 BFont font;
3078 menu->GetFont(&font);
3080 menu = item->Submenu();
3081 if (menu == NULL)
3082 return;
3084 menu->SetFont(&font);
3086 // found the addons menu, empty it first
3087 for (;;) {
3088 item = menu->RemoveItem((int32)0);
3089 if (!item)
3090 break;
3091 delete item;
3094 BObjectList<BMenuItem> primaryList;
3095 BObjectList<BMenuItem> secondaryList;
3096 BStringList mimeTypes(10);
3097 BuildMimeTypeList(mimeTypes);
3099 AddOneAddonParams params;
3100 params.primaryList = &primaryList;
3101 params.secondaryList = &secondaryList;
3103 // build a list of the MIME types of the selected items
3105 EachAddon(AddOneAddon, &params, mimeTypes);
3107 primaryList.SortItems(CompareLabels);
3108 secondaryList.SortItems(CompareLabels);
3110 int32 count = primaryList.CountItems();
3111 for (int32 index = 0; index < count; index++)
3112 menu->AddItem(primaryList.ItemAt(index));
3114 if (count > 0)
3115 menu->AddSeparatorItem();
3117 count = secondaryList.CountItems();
3118 for (int32 index = 0; index < count; index++)
3119 menu->AddItem(secondaryList.ItemAt(index));
3121 menu->SetTargetForItems(this);
3125 void
3126 BContainerWindow::UpdateMenu(BMenu* menu, UpdateMenuContext context)
3128 const int32 selectCount = PoseView()->SelectionList()->CountItems();
3129 const int32 count = PoseView()->CountItems();
3131 if (context == kMenuBarContext) {
3132 EnableNamedMenuItem(menu, kOpenSelection, selectCount > 0);
3133 EnableNamedMenuItem(menu, kGetInfo, selectCount > 0);
3134 EnableNamedMenuItem(menu, kIdentifyEntry, selectCount > 0);
3135 EnableNamedMenuItem(menu, kMoveToTrash, selectCount > 0);
3136 EnableNamedMenuItem(menu, kRestoreFromTrash, selectCount > 0);
3137 EnableNamedMenuItem(menu, kDelete, selectCount > 0);
3138 EnableNamedMenuItem(menu, kDuplicateSelection, selectCount > 0);
3141 Model* selectedModel = NULL;
3142 if (selectCount == 1) {
3143 selectedModel = PoseView()->SelectionList()->FirstItem()->
3144 TargetModel();
3147 if (context == kMenuBarContext || context == kPosePopUpContext) {
3148 SetUpEditQueryItem(menu);
3149 EnableNamedMenuItem(menu, kEditItem, selectCount == 1
3150 && (context == kPosePopUpContext || !PoseView()->ActivePose())
3151 && selectedModel != NULL
3152 && !selectedModel->IsDesktop()
3153 && !selectedModel->IsRoot()
3154 && !selectedModel->IsTrash()
3155 && !selectedModel->HasLocalizedName());
3156 SetCutItem(menu);
3157 SetCopyItem(menu);
3158 SetPasteItem(menu);
3161 if (context == kMenuBarContext || context == kWindowPopUpContext) {
3162 uint32 viewMode = PoseView()->ViewMode();
3164 BMenu* iconSizeMenu = NULL;
3165 if (BMenuItem* item = menu->FindItem(kIconMode))
3166 iconSizeMenu = item->Submenu();
3168 if (iconSizeMenu != NULL) {
3169 if (viewMode == kIconMode) {
3170 int32 iconSize = PoseView()->IconSizeInt();
3171 BMenuItem* item = iconSizeMenu->ItemAt(0);
3172 for (int32 i = 0; (item = iconSizeMenu->ItemAt(i)) != NULL;
3173 i++) {
3174 BMessage* message = item->Message();
3175 if (message == NULL) {
3176 item->SetMarked(false);
3177 continue;
3179 int32 size;
3180 if (message->FindInt32("size", &size) != B_OK)
3181 size = -1;
3182 item->SetMarked(iconSize == size);
3184 } else {
3185 BMenuItem* item;
3186 for (int32 i = 0; (item = iconSizeMenu->ItemAt(i)) != NULL; i++)
3187 item->SetMarked(false);
3191 BMenu* listSizeMenu = NULL;
3192 if (BMenuItem* item = menu->FindItem(kListMode))
3193 listSizeMenu = item->Submenu();
3195 if (listSizeMenu != NULL) {
3196 if (viewMode == kListMode) {
3197 int32 iconSize = PoseView()->IconSizeInt();
3198 BMenuItem* item = listSizeMenu->ItemAt(0);
3199 for (int32 i = 0; (item = listSizeMenu->ItemAt(i)) != NULL;
3200 i++) {
3201 BMessage* message = item->Message();
3202 if (message == NULL) {
3203 item->SetMarked(false);
3204 continue;
3206 int32 size;
3207 if (message->FindInt32("icon_size", &size) != B_OK)
3208 size = -1;
3209 item->SetMarked(iconSize == size);
3211 } else {
3212 BMenuItem* item;
3213 for (int32 i = 0; (item = listSizeMenu->ItemAt(i)) != NULL; i++)
3214 item->SetMarked(false);
3218 MarkNamedMenuItem(menu, kIconMode, viewMode == kIconMode);
3219 MarkNamedMenuItem(menu, kListMode, viewMode == kListMode);
3220 MarkNamedMenuItem(menu, kMiniIconMode, viewMode == kMiniIconMode);
3222 SetCloseItem(menu);
3223 SetArrangeMenu(menu);
3224 SetPasteItem(menu);
3226 BEntry entry(TargetModel()->EntryRef());
3227 BDirectory parent;
3228 entry_ref ref;
3229 BEntry root("/");
3231 bool parentIsRoot = (entry.GetParent(&parent) == B_OK
3232 && parent.GetEntry(&entry) == B_OK
3233 && entry.GetRef(&ref) == B_OK
3234 && entry == root);
3236 EnableNamedMenuItem(menu, kOpenParentDir, !TargetModel()->IsDesktop()
3237 && !TargetModel()->IsRoot()
3238 && (!parentIsRoot
3239 || TrackerSettings().SingleWindowBrowse()
3240 || TrackerSettings().ShowDisksIcon()
3241 || (modifiers() & B_CONTROL_KEY) != 0));
3243 EnableNamedMenuItem(menu, kEmptyTrash, count > 0);
3244 EnableNamedMenuItem(menu, B_SELECT_ALL, count > 0);
3246 BMenuItem* item = menu->FindItem(B_TRANSLATE("New"));
3247 if (item != NULL) {
3248 TemplatesMenu* templatesMenu = dynamic_cast<TemplatesMenu*>(
3249 item->Submenu());
3250 if (templatesMenu != NULL)
3251 templatesMenu->UpdateMenuState();
3255 BuildAddOnMenu(menu);
3259 void
3260 BContainerWindow::LoadAddOn(BMessage* message)
3262 UpdateIfNeeded();
3264 entry_ref addonRef;
3265 status_t result = message->FindRef("refs", &addonRef);
3266 if (result != B_OK) {
3267 BString buffer(B_TRANSLATE("Error %error loading add-On %name."));
3268 buffer.ReplaceFirst("%error", strerror(result));
3269 buffer.ReplaceFirst("%name", addonRef.name);
3271 BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"),
3272 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
3273 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
3274 alert->Go();
3275 return;
3278 // add selected refs to message
3279 BMessage* refs = new BMessage(B_REFS_RECEIVED);
3280 BObjectList<BPose>* selectionList = PoseView()->SelectionList();
3282 int32 index = 0;
3283 BPose* pose;
3284 while ((pose = selectionList->ItemAt(index++)) != NULL)
3285 refs->AddRef("refs", pose->TargetModel()->EntryRef());
3287 refs->AddMessenger("TrackerViewToken", BMessenger(PoseView()));
3289 LaunchInNewThread("Add-on", B_NORMAL_PRIORITY, &AddOnThread, refs,
3290 addonRef, *TargetModel()->EntryRef());
3294 void
3295 BContainerWindow::_UpdateSelectionMIMEInfo()
3297 BPose* pose;
3298 int32 index = 0;
3299 while ((pose = PoseView()->SelectionList()->ItemAt(index++)) != NULL) {
3300 BString mimeType(pose->TargetModel()->MimeType());
3301 if (!mimeType.Length() || mimeType.ICompare(B_FILE_MIMETYPE) == 0) {
3302 pose->TargetModel()->Mimeset(true);
3303 if (pose->TargetModel()->IsSymLink()) {
3304 Model* resolved = new Model(pose->TargetModel()->EntryRef(),
3305 true, true);
3306 if (resolved->InitCheck() == B_OK) {
3307 mimeType.SetTo(resolved->MimeType());
3308 if (!mimeType.Length()
3309 || mimeType.ICompare(B_FILE_MIMETYPE) == 0) {
3310 resolved->Mimeset(true);
3313 delete resolved;
3320 void
3321 BContainerWindow::_AddFolderIcon()
3323 if (fMenuBar == NULL) {
3324 // We don't want to add the icon if there's no menubar
3325 return;
3328 float iconSize = fMenuBar->Bounds().Height() - 2;
3329 if (iconSize < 16)
3330 iconSize = 16;
3332 fDraggableIcon = new(std::nothrow) DraggableContainerIcon();
3333 if (fDraggableIcon != NULL) {
3334 BLayoutItem* item = fMenuContainer->GroupLayout()->AddView(
3335 fDraggableIcon);
3336 item->SetExplicitMinSize(BSize(iconSize + 5, iconSize));
3337 item->SetExplicitMaxSize(BSize(iconSize + 5, item->MaxSize().Height()));
3339 fMenuBar->SetBorders(
3340 BControlLook::B_ALL_BORDERS & ~BControlLook::B_RIGHT_BORDER);
3345 BMenuItem*
3346 BContainerWindow::NewAttributeMenuItem(const char* label, const char* name,
3347 int32 type, float width, int32 align, bool editable, bool statField)
3349 return NewAttributeMenuItem(label, name, type, NULL, width, align,
3350 editable, statField);
3354 BMenuItem*
3355 BContainerWindow::NewAttributeMenuItem(const char* label, const char* name,
3356 int32 type, const char* displayAs, float width, int32 align,
3357 bool editable, bool statField)
3359 BMessage* message = new BMessage(kAttributeItem);
3360 message->AddString("attr_name", name);
3361 message->AddInt32("attr_type", type);
3362 message->AddInt32("attr_hash", (int32)AttrHashString(name, (uint32)type));
3363 message->AddFloat("attr_width", width);
3364 message->AddInt32("attr_align", align);
3365 if (displayAs != NULL)
3366 message->AddString("attr_display_as", displayAs);
3367 message->AddBool("attr_editable", editable);
3368 message->AddBool("attr_statfield", statField);
3370 BMenuItem* menuItem = new BMenuItem(label, message);
3371 menuItem->SetTarget(PoseView());
3373 return menuItem;
3377 void
3378 BContainerWindow::NewAttributeMenu(BMenu* menu)
3380 ASSERT(PoseView());
3382 BMenuItem* item;
3383 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Copy layout"),
3384 new BMessage(kCopyAttributes)));
3385 item->SetTarget(PoseView());
3386 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Paste layout"),
3387 new BMessage(kPasteAttributes)));
3388 item->SetTarget(PoseView());
3389 menu->AddSeparatorItem();
3391 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Name"),
3392 kAttrStatName, B_STRING_TYPE, 145, B_ALIGN_LEFT, true, true));
3394 if (gLocalizedNamePreferred) {
3395 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Real name"),
3396 kAttrRealName, B_STRING_TYPE, 145, B_ALIGN_LEFT, true, true));
3399 menu->AddItem(NewAttributeMenuItem (B_TRANSLATE("Size"), kAttrStatSize,
3400 B_OFF_T_TYPE, 80, B_ALIGN_RIGHT, false, true));
3402 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Modified"),
3403 kAttrStatModified, B_TIME_TYPE, 150, B_ALIGN_LEFT, false, true));
3405 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Created"),
3406 kAttrStatCreated, B_TIME_TYPE, 150, B_ALIGN_LEFT, false, true));
3408 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Kind"),
3409 kAttrMIMEType, B_MIME_STRING_TYPE, 145, B_ALIGN_LEFT, false, false));
3411 if (IsTrash() || InTrash()) {
3412 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Original name"),
3413 kAttrOriginalPath, B_STRING_TYPE, 225, B_ALIGN_LEFT, false,
3414 false));
3415 } else {
3416 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Location"), kAttrPath,
3417 B_STRING_TYPE, 225, B_ALIGN_LEFT, false, false));
3420 #ifdef OWNER_GROUP_ATTRIBUTES
3421 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Owner"), kAttrStatOwner,
3422 B_STRING_TYPE, 60, B_ALIGN_LEFT, false, true));
3424 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Group"), kAttrStatGroup,
3425 B_STRING_TYPE, 60, B_ALIGN_LEFT, false, true));
3426 #endif
3428 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Permissions"),
3429 kAttrStatMode, B_STRING_TYPE, 80, B_ALIGN_LEFT, false, true));
3433 void
3434 BContainerWindow::ShowAttributeMenu()
3436 ASSERT(fAttrMenu);
3437 fMenuBar->AddItem(fAttrMenu);
3441 void
3442 BContainerWindow::HideAttributeMenu()
3444 ASSERT(fAttrMenu);
3445 fMenuBar->RemoveItem(fAttrMenu);
3449 void
3450 BContainerWindow::MarkAttributeMenu()
3452 MarkAttributeMenu(fAttrMenu);
3456 void
3457 BContainerWindow::MarkAttributeMenu(BMenu* menu)
3459 if (!menu)
3460 return;
3462 int32 count = menu->CountItems();
3463 for (int32 index = 0; index < count; index++) {
3464 BMenuItem* item = menu->ItemAt(index);
3465 int32 attrHash;
3466 if (item->Message()) {
3467 if (item->Message()->FindInt32("attr_hash", &attrHash) == B_OK)
3468 item->SetMarked(PoseView()->ColumnFor((uint32)attrHash) != 0);
3469 else
3470 item->SetMarked(false);
3473 BMenu* submenu = item->Submenu();
3474 if (submenu) {
3475 int32 count2 = submenu->CountItems();
3476 for (int32 subindex = 0; subindex < count2; subindex++) {
3477 item = submenu->ItemAt(subindex);
3478 if (item->Message()) {
3479 if (item->Message()->FindInt32("attr_hash", &attrHash)
3480 == B_OK) {
3481 item->SetMarked(PoseView()->ColumnFor((uint32)attrHash)
3482 != 0);
3483 } else
3484 item->SetMarked(false);
3492 void
3493 BContainerWindow::MarkArrangeByMenu(BMenu* menu)
3495 if (!menu)
3496 return;
3498 int32 count = menu->CountItems();
3499 for (int32 index = 0; index < count; index++) {
3500 BMenuItem* item = menu->ItemAt(index);
3501 if (item->Message()) {
3502 uint32 attrHash;
3503 if (item->Message()->FindInt32("attr_hash",
3504 (int32*)&attrHash) == B_OK) {
3505 item->SetMarked(PoseView()->PrimarySort() == attrHash);
3506 } else if (item->Command() == kArrangeReverseOrder)
3507 item->SetMarked(PoseView()->ReverseSort());
3513 void
3514 BContainerWindow::AddMimeTypesToMenu()
3516 AddMimeTypesToMenu(fAttrMenu);
3520 // Adds a menu for a specific MIME type if it doesn't exist already.
3521 // Returns the menu, if it existed or not.
3522 BMenu*
3523 BContainerWindow::AddMimeMenu(const BMimeType& mimeType, bool isSuperType,
3524 BMenu* menu, int32 start)
3526 AutoLock<BLooper> _(menu->Looper());
3528 if (!mimeType.IsValid())
3529 return NULL;
3531 // Check if we already have an entry for this MIME type in the menu.
3532 for (int32 i = start; BMenuItem* item = menu->ItemAt(i); i++) {
3533 BMessage* message = item->Message();
3534 if (message == NULL)
3535 continue;
3537 const char* type;
3538 if (message->FindString("mimetype", &type) == B_OK
3539 && !strcmp(mimeType.Type(), type)) {
3540 return item->Submenu();
3544 BMessage attrInfo;
3545 char description[B_MIME_TYPE_LENGTH];
3546 const char* label = mimeType.Type();
3548 if (!mimeType.IsInstalled())
3549 return NULL;
3551 // only add things to menu which have "user-visible" data
3552 if (mimeType.GetAttrInfo(&attrInfo) != B_OK)
3553 return NULL;
3555 if (mimeType.GetShortDescription(description) == B_OK && description[0])
3556 label = description;
3558 // go through each field in meta mime and add it to a menu
3559 BMenu* mimeMenu = NULL;
3560 if (isSuperType) {
3561 // If it is a supertype, we create the menu anyway as it may have
3562 // submenus later on.
3563 mimeMenu = new BMenu(label);
3564 BFont font;
3565 menu->GetFont(&font);
3566 mimeMenu->SetFont(&font);
3569 int32 index = -1;
3570 const char* publicName;
3571 while (attrInfo.FindString("attr:public_name", ++index, &publicName)
3572 == B_OK) {
3573 if (!attrInfo.FindBool("attr:viewable", index)) {
3574 // don't add if attribute not viewable
3575 continue;
3578 int32 type;
3579 int32 align;
3580 int32 width;
3581 bool editable;
3582 const char* attrName;
3583 if (attrInfo.FindString("attr:name", index, &attrName) != B_OK
3584 || attrInfo.FindInt32("attr:type", index, &type) != B_OK
3585 || attrInfo.FindBool("attr:editable", index, &editable) != B_OK
3586 || attrInfo.FindInt32("attr:width", index, &width) != B_OK
3587 || attrInfo.FindInt32("attr:alignment", index, &align) != B_OK)
3588 continue;
3590 BString displayAs;
3591 attrInfo.FindString("attr:display_as", index, &displayAs);
3593 if (mimeMenu == NULL) {
3594 // do a lazy allocation of the menu
3595 mimeMenu = new BMenu(label);
3596 BFont font;
3597 menu->GetFont(&font);
3598 mimeMenu->SetFont(&font);
3600 mimeMenu->AddItem(NewAttributeMenuItem(publicName, attrName, type,
3601 displayAs.String(), width, align, editable, false));
3604 if (mimeMenu == NULL)
3605 return NULL;
3607 BMessage* message = new BMessage(kMIMETypeItem);
3608 message->AddString("mimetype", mimeType.Type());
3609 menu->AddItem(new IconMenuItem(mimeMenu, message, mimeType.Type()));
3611 return mimeMenu;
3615 void
3616 BContainerWindow::AddMimeTypesToMenu(BMenu* menu)
3618 if (!menu)
3619 return;
3621 // Remove old mime type menus
3622 int32 start = menu->CountItems();
3623 while (start > 0 && menu->ItemAt(start - 1)->Submenu() != NULL) {
3624 delete menu->RemoveItem(start - 1);
3625 start--;
3628 // Add a separator item if there is none yet
3629 if (start > 0
3630 && dynamic_cast<BSeparatorItem*>(menu->ItemAt(start - 1)) == NULL)
3631 menu->AddSeparatorItem();
3633 // Add MIME type in case we're a default query type window
3634 BPath path;
3635 if (TargetModel() != NULL) {
3636 TargetModel()->GetPath(&path);
3637 if (path.InitCheck() == B_OK
3638 && strstr(path.Path(), "/" kQueryTemplates "/") != NULL) {
3639 // demangle MIME type name
3640 BString name(TargetModel()->Name());
3641 name.ReplaceFirst('_', '/');
3643 PoseView()->AddMimeType(name.String());
3647 // Add MIME type menus
3649 int32 typeCount = PoseView()->CountMimeTypes();
3651 for (int32 index = 0; index < typeCount; index++) {
3652 BMimeType mimeType(PoseView()->MimeTypeAt(index));
3653 if (mimeType.InitCheck() == B_OK) {
3654 BMimeType superType;
3655 mimeType.GetSupertype(&superType);
3656 if (superType.InitCheck() == B_OK) {
3657 BMenu* superMenu = AddMimeMenu(superType, true, menu, start);
3658 if (superMenu != NULL) {
3659 // We have a supertype menu.
3660 AddMimeMenu(mimeType, false, superMenu, 0);
3666 // remove empty super menus, promote sub-types if needed
3668 for (int32 index = 0; index < typeCount; index++) {
3669 BMimeType mimeType(PoseView()->MimeTypeAt(index));
3670 BMimeType superType;
3671 mimeType.GetSupertype(&superType);
3673 BMenu* superMenu = AddMimeMenu(superType, true, menu, start);
3674 if (superMenu == NULL)
3675 continue;
3677 int32 itemsFound = 0;
3678 int32 menusFound = 0;
3679 for (int32 i = 0; BMenuItem* item = superMenu->ItemAt(i); i++) {
3680 if (item->Submenu() != NULL)
3681 menusFound++;
3682 else
3683 itemsFound++;
3686 if (itemsFound == 0) {
3687 if (menusFound != 0) {
3688 // promote types to the top level
3689 while (BMenuItem* item = superMenu->RemoveItem((int32)0)) {
3690 menu->AddItem(item);
3694 menu->RemoveItem(superMenu->Superitem());
3695 delete superMenu->Superitem();
3699 // remove separator if it's the only item in menu
3700 BMenuItem* item = menu->ItemAt(menu->CountItems() - 1);
3701 if (dynamic_cast<BSeparatorItem*>(item) != NULL) {
3702 menu->RemoveItem(item);
3703 delete item;
3706 MarkAttributeMenu(menu);
3710 BHandler*
3711 BContainerWindow::ResolveSpecifier(BMessage* message, int32 index,
3712 BMessage* specifier, int32 form, const char* property)
3714 if (strcmp(property, "Poses") == 0) {
3715 // PRINT(("BContainerWindow::ResolveSpecifier %s\n", property));
3716 message->PopSpecifier();
3717 return PoseView();
3720 return _inherited::ResolveSpecifier(message, index, specifier,
3721 form, property);
3725 PiggybackTaskLoop*
3726 BContainerWindow::DelayedTaskLoop()
3728 if (!fTaskLoop)
3729 fTaskLoop = new PiggybackTaskLoop;
3731 return fTaskLoop;
3735 bool
3736 BContainerWindow::NeedsDefaultStateSetup()
3738 if (TargetModel() == NULL)
3739 return false;
3741 if (TargetModel()->IsRoot()) {
3742 // don't try to set up anything if we are root
3743 return false;
3746 WindowStateNodeOpener opener(this, false);
3747 if (opener.StreamNode() == NULL) {
3748 // can't read state, give up
3749 return false;
3752 return !NodeHasSavedState(opener.Node());
3756 bool
3757 BContainerWindow::DefaultStateSourceNode(const char* name, BNode* result,
3758 bool createNew, bool createFolder)
3760 //PRINT(("looking for default state in tracker settings dir\n"));
3761 BPath settingsPath;
3762 if (FSFindTrackerSettingsDir(&settingsPath) != B_OK)
3763 return false;
3765 BDirectory dir(settingsPath.Path());
3767 BPath path(settingsPath);
3768 path.Append(name);
3769 if (!BEntry(path.Path()).Exists()) {
3770 if (!createNew)
3771 return false;
3773 BPath tmpPath(settingsPath);
3774 for (;;) {
3775 // deal with several levels of folders
3776 const char* nextSlash = strchr(name, '/');
3777 if (!nextSlash)
3778 break;
3780 BString tmp;
3781 tmp.SetTo(name, nextSlash - name);
3782 tmpPath.Append(tmp.String());
3784 mkdir(tmpPath.Path(), 0777);
3786 name = nextSlash + 1;
3787 if (!name[0]) {
3788 // can't deal with a slash at end
3789 return false;
3793 if (createFolder) {
3794 if (mkdir(path.Path(), 0777) < 0)
3795 return false;
3796 } else {
3797 BFile file;
3798 if (dir.CreateFile(name, &file) != B_OK)
3799 return false;
3803 //PRINT(("using default state from %s\n", path.Path()));
3804 result->SetTo(path.Path());
3805 return result->InitCheck() == B_OK;
3809 void
3810 BContainerWindow::SetUpDefaultState()
3812 BNode defaultingNode;
3813 // this is where we'll ulitimately get the state from
3814 bool gotDefaultingNode = 0;
3815 bool shouldStagger = false;
3817 ASSERT(TargetModel() != NULL);
3819 PRINT(("folder %s does not have any saved state\n", TargetModel()->Name()));
3821 WindowStateNodeOpener opener(this, true);
3822 // this is our destination node, whatever it is for this window
3823 if (opener.StreamNode() == NULL)
3824 return;
3826 if (!TargetModel()->IsRoot()) {
3827 BDirectory deskDir;
3828 FSGetDeskDir(&deskDir);
3830 // try copying state from our parent directory, unless it is the
3831 // desktop folder
3832 BEntry entry(TargetModel()->EntryRef());
3833 BNode parent;
3834 if (FSGetParentVirtualDirectoryAware(entry, parent) == B_OK
3835 && parent != deskDir) {
3836 PRINT(("looking at parent for state\n"));
3837 if (NodeHasSavedState(&parent)) {
3838 PRINT(("got state from parent\n"));
3839 defaultingNode = parent;
3840 gotDefaultingNode = true;
3841 // when getting state from parent, stagger the window
3842 shouldStagger = true;
3847 if (!gotDefaultingNode
3848 // parent didn't have any state, use the template directory from
3849 // tracker settings folder for what our state should be
3850 // For simplicity we are not picking up the most recent
3851 // changes that didn't get committed if home is still open in
3852 // a window, that's probably not a problem; would be OK if state
3853 // got committed after every change
3854 && !DefaultStateSourceNode(kDefaultFolderTemplate, &defaultingNode,
3855 true)) {
3856 return;
3859 if (fIsDesktop) {
3860 // don't copy over the attributes if we are the Desktop
3861 return;
3864 // copy over the attributes
3866 // set up a filter of the attributes we want copied
3867 const char* allowAttrs[] = {
3868 kAttrWindowFrame,
3869 kAttrWindowWorkspace,
3870 kAttrViewState,
3871 kAttrViewStateForeign,
3872 kAttrColumns,
3873 kAttrColumnsForeign,
3877 // copy over attributes that apply; transform them properly, stripping
3878 // parts that do not apply, adding a window stagger, etc.
3880 StaggerOneParams params;
3881 params.rectFromParent = shouldStagger;
3882 SelectiveAttributeTransformer frameOffsetter(kAttrWindowFrame,
3883 OffsetFrameOne, &params);
3884 SelectiveAttributeTransformer scrollOriginCleaner(kAttrViewState,
3885 ClearViewOriginOne, &params);
3887 // do it
3888 AttributeStreamMemoryNode memoryNode;
3889 NamesToAcceptAttrFilter filter(allowAttrs);
3890 AttributeStreamFileNode fileNode(&defaultingNode);
3892 *opener.StreamNode() << scrollOriginCleaner << frameOffsetter
3893 << memoryNode << filter << fileNode;
3897 void
3898 BContainerWindow::RestoreWindowState(AttributeStreamNode* node)
3900 if (node == NULL || fIsDesktop) {
3901 // don't restore any window state if we are the Desktop
3902 return;
3905 const char* rectAttributeName;
3906 const char* workspaceAttributeName;
3907 if (TargetModel()->IsRoot()) {
3908 rectAttributeName = kAttrDisksFrame;
3909 workspaceAttributeName = kAttrDisksWorkspace;
3910 } else {
3911 rectAttributeName = kAttrWindowFrame;
3912 workspaceAttributeName = kAttrWindowWorkspace;
3915 BRect frame(Frame());
3916 if (node->Read(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame)
3917 == sizeof(BRect)) {
3918 MoveTo(frame.LeftTop());
3919 ResizeTo(frame.Width(), frame.Height());
3920 } else
3921 sNewWindRect.OffsetBy(kWindowStaggerBy, kWindowStaggerBy);
3923 fPreviousBounds = Bounds();
3925 uint32 workspace;
3926 if (((fContainerWindowFlags & kRestoreWorkspace) != 0)
3927 && node->Read(workspaceAttributeName, 0, B_INT32_TYPE, sizeof(uint32),
3928 &workspace) == sizeof(uint32))
3929 SetWorkspaces(workspace);
3931 if ((fContainerWindowFlags & kIsHidden) != 0)
3932 Minimize(true);
3934 // restore window decor settings
3935 int32 size = node->Contains(kAttrWindowDecor, B_RAW_TYPE);
3936 if (size > 0) {
3937 char buffer[size];
3938 if (((fContainerWindowFlags & kRestoreDecor) != 0)
3939 && node->Read(kAttrWindowDecor, 0, B_RAW_TYPE, size, buffer)
3940 == size) {
3941 BMessage decorSettings;
3942 if (decorSettings.Unflatten(buffer) == B_OK)
3943 SetDecoratorSettings(decorSettings);
3949 void
3950 BContainerWindow::RestoreWindowState(const BMessage& message)
3952 if (fIsDesktop) {
3953 // don't restore any window state if we are the Desktop
3954 return;
3957 const char* rectAttributeName;
3958 const char* workspaceAttributeName;
3959 if (TargetModel()->IsRoot()) {
3960 rectAttributeName = kAttrDisksFrame;
3961 workspaceAttributeName = kAttrDisksWorkspace;
3962 } else {
3963 rectAttributeName = kAttrWindowFrame;
3964 workspaceAttributeName = kAttrWindowWorkspace;
3967 BRect frame(Frame());
3968 if (message.FindRect(rectAttributeName, &frame) == B_OK) {
3969 MoveTo(frame.LeftTop());
3970 ResizeTo(frame.Width(), frame.Height());
3971 } else
3972 sNewWindRect.OffsetBy(kWindowStaggerBy, kWindowStaggerBy);
3974 uint32 workspace;
3975 if ((fContainerWindowFlags & kRestoreWorkspace)
3976 && message.FindInt32(workspaceAttributeName,
3977 (int32*)&workspace) == B_OK) {
3978 SetWorkspaces(workspace);
3981 if (fContainerWindowFlags & kIsHidden)
3982 Minimize(true);
3984 // restore window decor settings
3985 BMessage decorSettings;
3986 if ((fContainerWindowFlags & kRestoreDecor)
3987 && message.FindMessage(kAttrWindowDecor, &decorSettings) == B_OK) {
3988 SetDecoratorSettings(decorSettings);
3993 void
3994 BContainerWindow::SaveWindowState(AttributeStreamNode* node)
3996 if (fIsDesktop) {
3997 // don't save window state if we are the Desktop
3998 return;
4001 ASSERT(node != NULL);
4003 const char* rectAttributeName;
4004 const char* workspaceAttributeName;
4005 if (TargetModel() != NULL && TargetModel()->IsRoot()) {
4006 rectAttributeName = kAttrDisksFrame;
4007 workspaceAttributeName = kAttrDisksWorkspace;
4008 } else {
4009 rectAttributeName = kAttrWindowFrame;
4010 workspaceAttributeName = kAttrWindowWorkspace;
4013 // node is null if it already got deleted
4014 BRect frame(Frame());
4015 node->Write(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame);
4017 uint32 workspaces = Workspaces();
4018 node->Write(workspaceAttributeName, 0, B_INT32_TYPE, sizeof(uint32),
4019 &workspaces);
4021 BMessage decorSettings;
4022 if (GetDecoratorSettings(&decorSettings) == B_OK) {
4023 int32 size = decorSettings.FlattenedSize();
4024 char buffer[size];
4025 if (decorSettings.Flatten(buffer, size) == B_OK) {
4026 node->Write(kAttrWindowDecor, 0, B_RAW_TYPE, size, buffer);
4032 void
4033 BContainerWindow::SaveWindowState(BMessage& message) const
4035 const char* rectAttributeName;
4036 const char* workspaceAttributeName;
4038 if (TargetModel() != NULL && TargetModel()->IsRoot()) {
4039 rectAttributeName = kAttrDisksFrame;
4040 workspaceAttributeName = kAttrDisksWorkspace;
4041 } else {
4042 rectAttributeName = kAttrWindowFrame;
4043 workspaceAttributeName = kAttrWindowWorkspace;
4046 // node is null if it already got deleted
4047 BRect frame(Frame());
4048 message.AddRect(rectAttributeName, frame);
4049 message.AddInt32(workspaceAttributeName, (int32)Workspaces());
4051 BMessage decorSettings;
4052 if (GetDecoratorSettings(&decorSettings) == B_OK) {
4053 message.AddMessage(kAttrWindowDecor, &decorSettings);
4058 status_t
4059 BContainerWindow::DragStart(const BMessage* dragMessage)
4061 if (dragMessage == NULL)
4062 return B_ERROR;
4064 // if already dragging, or
4065 // if all the refs match
4066 if (Dragging()
4067 && SpringLoadedFolderCompareMessages(dragMessage, fDragMessage)) {
4068 return B_OK;
4071 // cache the current drag message
4072 // build a list of the mimetypes in the message
4073 SpringLoadedFolderCacheDragData(dragMessage, &fDragMessage,
4074 &fCachedTypesList);
4076 fWaitingForRefs = true;
4078 return B_OK;
4082 void
4083 BContainerWindow::DragStop()
4085 delete fDragMessage;
4086 fDragMessage = NULL;
4088 delete fCachedTypesList;
4089 fCachedTypesList = NULL;
4091 fWaitingForRefs = false;
4095 void
4096 BContainerWindow::ShowSelectionWindow()
4098 if (fSelectionWindow == NULL) {
4099 fSelectionWindow = new SelectionWindow(this);
4100 fSelectionWindow->Show();
4101 } else if (fSelectionWindow->Lock()) {
4102 // The window is already there, just bring it close
4103 fSelectionWindow->MoveCloseToMouse();
4104 if (fSelectionWindow->IsHidden())
4105 fSelectionWindow->Show();
4107 fSelectionWindow->Unlock();
4112 void
4113 BContainerWindow::ShowNavigator(bool show)
4115 if (PoseView()->IsDesktopWindow() || !TargetModel()->IsDirectory()
4116 || fPoseView->IsFilePanel()) {
4117 return;
4120 if (show) {
4121 if (Navigator() && !Navigator()->IsHidden())
4122 return;
4124 if (Navigator() == NULL) {
4125 fNavigator = new BNavigator(TargetModel());
4126 fPoseContainer->GridLayout()->AddView(fNavigator, 0, 0, 2);
4129 if (Navigator()->IsHidden())
4130 Navigator()->Show();
4132 if (PoseView()->VScrollBar())
4133 PoseView()->UpdateScrollRange();
4134 } else {
4135 if (!Navigator() || Navigator()->IsHidden())
4136 return;
4138 if (PoseView()->VScrollBar())
4139 PoseView()->UpdateScrollRange();
4141 fNavigator->Hide();
4146 void
4147 BContainerWindow::SetSingleWindowBrowseShortcuts(bool enabled)
4149 if (PoseView()->IsDesktopWindow())
4150 return;
4152 if (enabled) {
4153 if (!Navigator())
4154 return;
4156 RemoveShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY);
4157 RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY);
4158 RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY | B_OPTION_KEY);
4160 AddShortcut(B_LEFT_ARROW, B_COMMAND_KEY,
4161 new BMessage(kNavigatorCommandBackward), Navigator());
4162 AddShortcut(B_RIGHT_ARROW, B_COMMAND_KEY,
4163 new BMessage(kNavigatorCommandForward), Navigator());
4164 AddShortcut(B_UP_ARROW, B_COMMAND_KEY,
4165 new BMessage(kNavigatorCommandUp), Navigator());
4167 AddShortcut(B_LEFT_ARROW, B_OPTION_KEY | B_COMMAND_KEY,
4168 new BMessage(kNavigatorCommandBackward), Navigator());
4169 AddShortcut(B_RIGHT_ARROW, B_OPTION_KEY | B_COMMAND_KEY,
4170 new BMessage(kNavigatorCommandForward), Navigator());
4171 AddShortcut(B_UP_ARROW, B_OPTION_KEY | B_COMMAND_KEY,
4172 new BMessage(kNavigatorCommandUp), Navigator());
4173 AddShortcut(B_DOWN_ARROW, B_OPTION_KEY | B_COMMAND_KEY,
4174 new BMessage(kOpenSelection), PoseView());
4175 AddShortcut('L', B_COMMAND_KEY,
4176 new BMessage(kNavigatorCommandSetFocus), Navigator());
4178 } else {
4179 RemoveShortcut(B_LEFT_ARROW, B_COMMAND_KEY);
4180 RemoveShortcut(B_RIGHT_ARROW, B_COMMAND_KEY);
4181 RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY);
4182 // This is added again, below, with a new meaning.
4184 RemoveShortcut(B_LEFT_ARROW, B_OPTION_KEY | B_COMMAND_KEY);
4185 RemoveShortcut(B_RIGHT_ARROW, B_OPTION_KEY | B_COMMAND_KEY);
4186 RemoveShortcut(B_UP_ARROW, B_OPTION_KEY | B_COMMAND_KEY);
4187 RemoveShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY);
4188 // This also changes meaning, added again below.
4190 AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY,
4191 new BMessage(kOpenSelection), PoseView());
4192 AddShortcut(B_UP_ARROW, B_COMMAND_KEY,
4193 new BMessage(kOpenParentDir), PoseView());
4194 // We change the meaning from kNavigatorCommandUp
4195 // to kOpenParentDir.
4196 AddShortcut(B_UP_ARROW, B_COMMAND_KEY | B_OPTION_KEY,
4197 new BMessage(kOpenParentDir), PoseView());
4198 // command + option results in closing the parent window
4199 RemoveShortcut('L', B_COMMAND_KEY);
4204 void
4205 BContainerWindow::SetPathWatchingEnabled(bool enable)
4207 if (IsPathWatchingEnabled()) {
4208 stop_watching(this);
4209 fIsWatchingPath = false;
4212 if (enable) {
4213 if (TargetModel() != NULL) {
4214 BEntry entry;
4216 TargetModel()->GetEntry(&entry);
4217 status_t err;
4218 do {
4219 err = entry.GetParent(&entry);
4220 if (err != B_OK)
4221 break;
4223 char name[B_FILE_NAME_LENGTH];
4224 entry.GetName(name);
4225 if (strcmp(name, "/") == 0)
4226 break;
4228 node_ref ref;
4229 entry.GetNodeRef(&ref);
4230 watch_node(&ref, B_WATCH_NAME, this);
4231 } while (err == B_OK);
4233 fIsWatchingPath = err == B_OK;
4234 } else
4235 fIsWatchingPath = false;
4240 void
4241 BContainerWindow::PulseTaskLoop()
4243 if (fTaskLoop)
4244 fTaskLoop->PulseMe();
4248 void
4249 BContainerWindow::PopulateArrangeByMenu(BMenu* menu)
4251 if (!fAttrMenu || !menu)
4252 return;
4253 // empty fArrangeByMenu...
4254 BMenuItem* item;
4255 while ((item = menu->RemoveItem((int32)0)) != NULL)
4256 delete item;
4258 int32 itemCount = fAttrMenu->CountItems();
4259 for (int32 i = 0; i < itemCount; i++) {
4260 item = fAttrMenu->ItemAt(i);
4261 if (item->Command() == kAttributeItem) {
4262 BMessage* message = new BMessage(*(item->Message()));
4263 message->what = kArrangeBy;
4264 BMenuItem* newItem = new BMenuItem(item->Label(), message);
4265 newItem->SetTarget(PoseView());
4266 menu->AddItem(newItem);
4270 menu->AddSeparatorItem();
4272 item = new BMenuItem(B_TRANSLATE("Reverse order"),
4273 new BMessage(kArrangeReverseOrder));
4275 item->SetTarget(PoseView());
4276 menu->AddItem(item);
4277 menu->AddSeparatorItem();
4280 item = new BMenuItem(B_TRANSLATE("Clean up"), new BMessage(kCleanup),
4281 'K');
4282 item->SetTarget(PoseView());
4283 menu->AddItem(item);
4287 // #pragma mark - WindowStateNodeOpener
4290 WindowStateNodeOpener::WindowStateNodeOpener(BContainerWindow* window,
4291 bool forWriting)
4293 fModelOpener(NULL),
4294 fNode(NULL),
4295 fStreamNode(NULL)
4297 if (window->TargetModel() && window->TargetModel()->IsRoot()) {
4298 BDirectory dir;
4299 if (FSGetDeskDir(&dir) == B_OK) {
4300 fNode = new BDirectory(dir);
4301 fStreamNode = new AttributeStreamFileNode(fNode);
4303 } else if (window->TargetModel()){
4304 fModelOpener = new ModelNodeLazyOpener(window->TargetModel(),
4305 forWriting, false);
4306 if (fModelOpener->IsOpen(forWriting)) {
4307 fStreamNode = new AttributeStreamFileNode(
4308 fModelOpener->TargetModel()->Node());
4313 WindowStateNodeOpener::~WindowStateNodeOpener()
4315 delete fModelOpener;
4316 delete fNode;
4317 delete fStreamNode;
4321 void
4322 WindowStateNodeOpener::SetTo(const BDirectory* node)
4324 delete fModelOpener;
4325 delete fNode;
4326 delete fStreamNode;
4328 fModelOpener = NULL;
4329 fNode = new BDirectory(*node);
4330 fStreamNode = new AttributeStreamFileNode(fNode);
4334 void
4335 WindowStateNodeOpener::SetTo(const BEntry* entry, bool forWriting)
4337 delete fModelOpener;
4338 delete fNode;
4339 delete fStreamNode;
4341 fModelOpener = NULL;
4342 fNode = new BFile(entry, (uint32)(forWriting ? O_RDWR : O_RDONLY));
4343 fStreamNode = new AttributeStreamFileNode(fNode);
4347 void
4348 WindowStateNodeOpener::SetTo(Model* model, bool forWriting)
4350 delete fModelOpener;
4351 delete fNode;
4352 delete fStreamNode;
4354 fNode = NULL;
4355 fStreamNode = NULL;
4356 fModelOpener = new ModelNodeLazyOpener(model, forWriting, false);
4357 if (fModelOpener->IsOpen(forWriting)) {
4358 fStreamNode = new AttributeStreamFileNode(
4359 fModelOpener->TargetModel()->Node());
4364 AttributeStreamNode*
4365 WindowStateNodeOpener::StreamNode() const
4367 return fStreamNode;
4371 BNode*
4372 WindowStateNodeOpener::Node() const
4374 if (!fStreamNode)
4375 return NULL;
4377 if (fNode)
4378 return fNode;
4380 return fModelOpener->TargetModel()->Node();
4384 // #pragma mark - BorderedView
4387 BorderedView::BorderedView()
4389 BGroupView(B_VERTICAL, 0),
4390 fEnableBorderHighlight(true)
4392 GroupLayout()->SetInsets(1);
4396 void
4397 BorderedView::WindowActivated(bool active)
4399 BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
4400 if (window == NULL)
4401 return;
4403 if (window->PoseView()->IsFocus())
4404 PoseViewFocused(active); // Update border color
4408 void BorderedView::EnableBorderHighlight(bool enable)
4410 fEnableBorderHighlight = enable;
4411 PoseViewFocused(false);
4415 void
4416 BorderedView::PoseViewFocused(bool focused)
4418 BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
4419 if (window == NULL)
4420 return;
4422 color_which base = B_DOCUMENT_BACKGROUND_COLOR;
4423 float tint = B_DARKEN_2_TINT;
4424 if (focused && window->IsActive() && fEnableBorderHighlight) {
4425 base = B_KEYBOARD_NAVIGATION_COLOR;
4426 tint = B_NO_TINT;
4429 BScrollBar* hScrollBar = window->PoseView()->HScrollBar();
4430 if (hScrollBar != NULL)
4431 hScrollBar->SetBorderHighlighted(focused);
4433 BScrollBar* vScrollBar = window->PoseView()->VScrollBar();
4434 if (vScrollBar != NULL)
4435 vScrollBar->SetBorderHighlighted(focused);
4437 SetViewUIColor(base, tint);
4438 Invalidate();
4442 void
4443 BorderedView::Pulse()
4445 BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
4446 if (window != NULL)
4447 window->PulseTaskLoop();