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.
36 #include "ContainerWindow.h"
39 #include <Application.h>
40 #include <AppFileInfo.h>
42 #include <ControlLook.h>
44 #include <Directory.h>
46 #include <FindDirectory.h>
48 #include <GroupLayout.h>
53 #include <NodeMonitor.h>
55 #include <PopUpMenu.h>
58 #include <UnicodeChar.h>
60 #include <VolumeRoster.h>
61 #include <WindowPrivate.h>
71 #include "Attributes.h"
72 #include "AttributeStream.h"
74 #include "BackgroundImage.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"
83 #include "IconMenuItem.h"
84 #include "OpenWithWindow.h"
85 #include "MimeTypes.h"
86 #include "MountMenu.h"
87 #include "Navigator.h"
90 #include "QueryContainerWindow.h"
91 #include "SelectionWindow.h"
92 #include "TitleView.h"
94 #include "TrackerSettings.h"
96 #include "TemplatesMenu.h"
99 #undef B_TRANSLATION_CONTEXT
100 #define B_TRANSLATION_CONTEXT "ContainerWindow"
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
;
115 class DraggableContainerIcon
: public BView
{
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
);
130 } // namespace BPrivate
133 struct AddOneAddonParams
{
134 BObjectList
<BMenuItem
>* primaryList
;
135 BObjectList
<BMenuItem
>* secondaryList
;
138 struct StaggerOneParams
{
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);
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)
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
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
);
194 params
->primaryList
->AddItem(item
);
196 params
->secondaryList
->AddItem(item
);
203 AddOnThread(BMessage
* refsMessage
, entry_ref addonRef
, entry_ref directoryRef
)
205 std::auto_ptr
<BMessage
> refsMessagePtr(refsMessage
);
207 BEntry
entry(&addonRef
);
209 status_t result
= entry
.InitCheck();
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
);
222 (*processRefs
)(directoryRef
, refsMessagePtr
.get(), NULL
);
224 unload_add_on(addonImage
);
227 PRINT(("couldn't find process_refs\n"));
229 unload_add_on(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
);
248 NodeHasSavedState(const BNode
* node
)
251 return node
->GetAttrInfo(kAttrWindowFrame
, &info
) == B_OK
;
256 OffsetFrameOne(const char* DEBUG_ONLY(name
), uint32
, off_t
, void* castToRect
,
259 ASSERT(strcmp(name
, kAttrWindowFrame
) == 0);
260 StaggerOneParams
* params
= (StaggerOneParams
*)castToParams
;
262 if (!params
->rectFromParent
)
268 ((BRect
*)castToRect
)->OffsetBy(kWindowStaggerBy
, kWindowStaggerBy
);
275 AddMimeTypeString(BStringList
& list
, Model
* model
)
280 const char* modelMimeType
= model
->MimeType();
281 if (modelMimeType
== NULL
|| *modelMimeType
== '\0')
284 BString type
= BString(modelMimeType
);
285 if (list
.HasString(type
, true))
292 // #pragma mark - DraggableContainerIcon
295 DraggableContainerIcon::DraggableContainerIcon()
297 BView("DraggableContainerIcon", B_WILL_DRAW
),
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())
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;
329 Window()->Activate(true);
334 DraggableContainerIcon::MouseUp(BPoint
)
337 Window()->Activate(true);
340 fDragStarted
= false;
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
))
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
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);
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());
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
);
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);
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());
407 dragBitmap
->Unlock();
409 BMessage
message(B_SIMPLE_DATA
);
410 message
.AddRef("refs", model
->EntryRef());
411 message
.AddPoint("click_pt", fClickPoint
);
415 GetMouse(&tmpLoc
, &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
);
428 DragMessage(&message
, dragBitmap
, B_OP_ALPHA
,
429 BPoint(fClickPoint
.x
+ hIconOffset
, fClickPoint
.y
), this);
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
,
465 fUseLayouts(useLayouts
),
466 fMenuContainer(NULL
),
467 fPoseContainer(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
),
479 fCreateLinkItem(NULL
),
481 fNavigationItem(NULL
),
483 fDraggableIcon(NULL
),
490 fArrangeByMenu(NULL
),
491 fSelectionWindow(NULL
),
496 fIsDesktop(isDeskWindow
),
497 fContainerWindowFlags(containerWindowFlags
),
498 fBackgroundImage(NULL
),
499 fSavedZoomRect(0, 0, -1, -1),
502 fCachedTypesList(NULL
),
503 fStateNeedsSaving(false),
504 fSaveStateIsEnabled(true),
505 fIsWatchingPath(false)
510 ASSERT(list
->IsLocked());
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();
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
));
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
);
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()
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
);
573 delete fBackgroundImage
;
575 delete fCachedTypesList
;
577 if (fSelectionWindow
!= NULL
&& fSelectionWindow
->Lock())
578 fSelectionWindow
->Quit();
583 BContainerWindow::InitialWindowRect(window_feel feel
)
585 if (feel
!= kDesktopWindowFeel
)
588 // do not offset desktop window
589 BRect result
= sNewWindRect
;
590 result
.OffsetTo(0, 0);
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);
601 _inherited::Minimize(minimize
);
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
);
618 // this will close the window instantly, even if
619 // the file system is very busy right now
625 BContainerWindow::Quit()
627 // get rid of context menus
628 if (fNavigationItem
) {
629 BMenu
* menu
= fNavigationItem
->Menu();
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
) {
647 if (fCopyToItem
!= NULL
&& fCopyToItem
->Menu() == NULL
) {
652 if (fCreateLinkItem
!= NULL
&& fCreateLinkItem
->Menu() == NULL
) {
653 delete fCreateLinkItem
;
654 fCreateLinkItem
= NULL
;
657 if (fAttrMenu
!= NULL
&& fAttrMenu
->Supermenu() == 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())
692 if (fWindowList
!= NULL
&& windowCount
== 0)
693 be_app
->PostMessage(B_QUIT_REQUESTED
);
700 BContainerWindow::NewPoseView(Model
* model
, uint32 viewMode
)
702 return new BPoseView(model
, viewMode
);
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
);
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())
738 SetPathWatchingEnabled(settings
.ShowNavigator()
739 || settings
.ShowFullPathInTitleBar());
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
);
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();
794 menu
->RemoveItem(fNavigationItem
);
795 BMenuItem
* item
= menu
->RemoveItem((int32
)0);
796 ASSERT(item
!= fNavigationItem
);
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
);
814 fFileMenu
= new BMenu(B_TRANSLATE("File"));
815 AddFileMenu(fFileMenu
);
816 fMenuBar
->AddItem(fFileMenu
);
818 fMenuBar
->RemoveItem(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
);
827 fAttrMenu
= new BMenu(B_TRANSLATE("Attributes"));
828 NewAttributeMenu(fAttrMenu
);
829 if (PoseView()->ViewMode() == kListMode
)
832 PopulateArrangeByMenu(fArrangeByMenu
);
834 int32 selectCount
= PoseView()->SelectionList()->CountItems();
836 SetupOpenWithMenu(fFileMenu
);
837 SetupMoveCopyMenus(selectCount
? PoseView()->SelectionList()
838 ->FirstItem()->TargetModel()->EntryRef() : NULL
,
845 BContainerWindow::Init(const BMessage
* message
)
849 ASSERT(fPoseView
!= NULL
);
850 if (fPoseView
== NULL
)
853 // deal with new unconfigured folders
854 if (NeedsDefaultStateSetup())
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
);
874 if (!TargetModel()->IsRoot() && !IsTrash())
877 // add equivalents of the menu shortcuts to the menuless
883 AddShortcut('T', B_COMMAND_KEY
| B_SHIFT_KEY
, new BMessage(kDelete
),
885 AddShortcut('K', B_COMMAND_KEY
| B_SHIFT_KEY
, new BMessage(kCleanupAll
),
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
),
893 SetSingleWindowBrowseShortcuts(settings
.SingleWindowBrowse());
896 // add some debugging shortcuts
897 AddShortcut('D', B_COMMAND_KEY
| B_CONTROL_KEY
, new BMessage('dbug'),
899 AddShortcut('C', B_COMMAND_KEY
| B_CONTROL_KEY
, new BMessage('dpcc'),
901 AddShortcut('F', B_COMMAND_KEY
| B_CONTROL_KEY
, new BMessage('dpfl'),
903 AddShortcut('F', B_COMMAND_KEY
| B_CONTROL_KEY
| B_OPTION_KEY
,
904 new BMessage('dpfL'), PoseView());
908 keymap
.SetToCurrent();
909 BObjectList
<const char> unmodified(3, true);
910 if (keymap
.GetModifiedCharacters("+", B_SHIFT_KEY
, 0, &unmodified
)
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();
926 RestoreState(*message
);
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
936 MarkAttributeMenu(fAttrMenu
);
937 CheckScreenIntersect();
939 if (fBackgroundImage
!= NULL
&& !fIsDesktop
940 && PoseView()->ViewMode() != kListMode
) {
941 fBackgroundImage
->Show(PoseView(), current_workspace());
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
);
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
);
981 BContainerWindow::RestoreState()
985 WindowStateNodeOpener
opener(this, false);
986 RestoreWindowState(opener
.StreamNode());
987 fPoseView
->Init(opener
.StreamNode());
989 RestoreStateCommon();
994 BContainerWindow::RestoreState(const BMessage
&message
)
998 RestoreWindowState(message
);
999 fPoseView
->Init(message
);
1001 RestoreStateCommon();
1006 BContainerWindow::RestoreStateCommon()
1008 if (!fIsDesktop
&& fUseLayouts
)
1011 if (BootedInSafeMode())
1012 // don't pick up backgrounds in safe mode
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
,
1039 BContainerWindow::UpdateTitle()
1041 // set title to full path, if necessary
1042 if (TrackerSettings().ShowFullPathInTitleBar()) {
1043 // use the Entry's full path
1045 TargetModel()->GetPath(&path
);
1046 SetTitle(path
.Path());
1048 // use the default look
1049 SetTitle(TargetModel()->Name());
1052 if (Navigator() != NULL
) {
1053 Navigator()->UpdateLocation(PoseView()->TargetModel(),
1060 BContainerWindow::UpdateBackgroundImage()
1062 if (BootedInSafeMode())
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());
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(),
1100 if (offsetY
< 0 && PoseView()->Bounds().bottom
> extent
.bottom
1101 && Bounds().Height() > fPreviousBounds
.Height()) {
1102 scroll
.y
= std::max(fPreviousBounds
.Height() - Bounds().Height(),
1106 if (scroll
!= B_ORIGIN
)
1107 PoseView()->ScrollBy(scroll
.x
, scroll
.y
);
1109 PoseView()->UpdateScrollRange();
1110 PoseView()->ResetPosePlacementHint();
1113 fPreviousBounds
= Bounds();
1114 fStateNeedsSaving
= true;
1119 BContainerWindow::FrameMoved(BPoint
)
1121 fStateNeedsSaving
= true;
1126 BContainerWindow::WorkspacesChanged(uint32
, uint32
)
1128 fStateNeedsSaving
= true;
1133 BContainerWindow::ViewModeChanged(uint32 oldMode
, uint32 newMode
)
1135 if (fBackgroundImage
== NULL
)
1138 if (newMode
== kListMode
)
1139 fBackgroundImage
->Remove();
1140 else if (oldMode
== kListMode
)
1141 fBackgroundImage
->Show(PoseView(), current_workspace());
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());
1164 BContainerWindow::SaveState(bool hide
)
1166 if (SaveStateIsEnabled()) {
1167 WindowStateNodeOpener
opener(this, true);
1168 if (opener
.StreamNode() != NULL
)
1169 SaveWindowState(opener
.StreamNode());
1174 if (opener
.StreamNode())
1175 fPoseView
->SaveState(opener
.StreamNode());
1177 fStateNeedsSaving
= false;
1183 BContainerWindow::SaveState(BMessage
& message
) const
1185 if (SaveStateIsEnabled()) {
1186 SaveWindowState(message
);
1187 fPoseView
->SaveState(message
);
1193 BContainerWindow::StateNeedsSaving() const
1195 return fPoseView
!= NULL
&& (fStateNeedsSaving
|| fPoseView
->StateNeedsSaving());
1200 BContainerWindow::GetLayoutState(BNode
* node
, BMessage
* message
)
1202 if (node
== NULL
|| message
== NULL
)
1205 status_t result
= node
->InitCheck();
1209 // ToDo: get rid of this, use AttrStream instead
1210 node
->RewindAttrs();
1212 while (node
->GetNextAttrName(attrName
) == B_OK
) {
1214 if (node
->GetAttrInfo(attrName
, &info
) != B_OK
)
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) {
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
);
1241 BContainerWindow::SetLayoutState(BNode
* node
, const BMessage
* message
)
1243 status_t result
= node
->InitCheck();
1247 for (int32 globalIndex
= 0; ;) {
1248 #if B_BEOS_VERSION_DANO
1255 status_t result
= message
->GetInfo(B_ANY_TYPE
, globalIndex
, &name
,
1260 for (int32 index
= 0; index
< count
; index
++) {
1263 result
= message
->FindData(name
, type
, index
, &buffer
, &size
);
1264 if (result
!= B_OK
) {
1265 PRINT(("error reading %s \n", name
));
1269 if (node
->WriteAttr(name
, type
, 0, buffer
,
1270 (size_t)size
) != size
) {
1271 PRINT(("error writing %s \n", name
));
1283 BContainerWindow::ShouldAddMenus() const
1290 BContainerWindow::ShouldAddScrollBars() const
1297 BContainerWindow::TargetModel() const
1299 return fPoseView
->TargetModel();
1304 BContainerWindow::SelectionChanged()
1310 BContainerWindow::Zoom(BPoint
, float, float)
1312 BRect
oldZoomRect(fSavedZoomRect
);
1313 fSavedZoomRect
= Frame();
1316 if (fSavedZoomRect
== Frame() && oldZoomRect
.IsValid())
1317 ResizeTo(oldZoomRect
.Width(), oldZoomRect
.Height());
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;
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();
1370 BContainerWindow::MessageReceived(BMessage
* message
)
1372 switch (message
->what
) {
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
);
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
1394 if (message
->what
== B_CUT
|| message
->what
== B_COPY
1395 || message
->what
== B_PASTE
) {
1396 view
->MessageReceived(message
);
1403 BView
* view
= CurrentFocus();
1404 if (dynamic_cast<BTextView
*>(view
) == NULL
) {
1407 view
->MessageReceived(message
);
1413 BView
* view
= CurrentFocus();
1414 if (dynamic_cast<BTextView
*>(view
) == NULL
) {
1417 view
->MessageReceived(message
);
1423 PostMessage(message
, PoseView());
1427 if (message
->HasMessage("state")) {
1429 message
->FindMessage("state", &state
);
1443 case kCopySelectionTo
:
1446 if (message
->FindRef("refs", &ref
) != B_OK
)
1449 BRoster().AddToRecentFolders(&ref
);
1452 if (model
.InitCheck() != B_OK
)
1455 if (*model
.NodeRef() == *TargetModel()->NodeRef())
1456 PoseView()->DuplicateSelection();
1458 PoseView()->MoveSelectionInto(&model
, this, true);
1462 case kMoveSelectionTo
:
1465 if (message
->FindRef("refs", &ref
) != B_OK
)
1468 BRoster().AddToRecentFolders(&ref
);
1471 if (model
.InitCheck() != B_OK
)
1474 PoseView()->MoveSelectionInto(&model
, this, false, true);
1479 case kCreateRelativeLink
:
1482 if (message
->FindRef("refs", &ref
) == B_OK
) {
1483 BRoster().AddToRecentFolders(&ref
);
1486 if (model
.InitCheck() != B_OK
)
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
);
1502 case kShowSelectionWindow
:
1503 ShowSelectionWindow();
1506 case kSelectMatchingEntries
:
1507 PoseView()->SelectMatchingEntries(message
);
1511 (new FindWindow())->Show();
1515 be_app
->PostMessage(B_QUIT_REQUESTED
);
1518 case kRestoreBackgroundImage
:
1519 UpdateBackgroundImage();
1522 case kSwitchDirectory
:
1525 if (message
->FindRef("refs", &ref
) == B_OK
) {
1527 if (entry
.SetTo(&ref
) == B_OK
) {
1528 if (StateNeedsSaving())
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);
1539 PoseView()->SwitchDir(&ref
, opener
.StreamNode());
1541 fIsTrash
= FSIsTrashDir(&entry
);
1542 fInTrash
= FSInTrashDir(&ref
);
1544 if (wasInTrash
^ (IsTrash() || InTrash())
1545 || isRoot
!= PoseView()->TargetModel()->IsRoot())
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(),
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(
1576 fDraggableIcon
->Invalidate();
1579 } else if (fDraggableIcon
!= NULL
)
1580 fDraggableIcon
->RemoveSelf();
1583 // Update window title
1590 case B_REFS_RECEIVED
:
1592 // ref in this message is the target,
1593 // the end point of the drag
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);
1606 PoseView()->GetMouse(&dropPoint
, &buttons
, true);
1607 PoseView()->HandleDropCommon(fDragMessage
,
1608 &targetModel
, NULL
, PoseView(), dropPoint
);
1616 case B_OBSERVER_NOTICE_CHANGE
:
1619 if (message
->FindInt32("be:observe_change_what", &observerWhat
)
1621 TrackerSettings settings
;
1622 switch (observerWhat
) {
1623 case kWindowsShowFullPathChanged
:
1625 if (!IsPathWatchingEnabled()
1626 && settings
.ShowFullPathInTitleBar()) {
1627 SetPathWatchingEnabled(true);
1629 if (IsPathWatchingEnabled()
1630 && !(settings
.ShowNavigator()
1631 || settings
.ShowFullPathInTitleBar())) {
1632 SetPathWatchingEnabled(false);
1636 case kSingleWindowBrowseChanged
:
1637 if (settings
.SingleWindowBrowse()
1639 && TargetModel()->IsDirectory()
1640 && !PoseView()->IsFilePanel()
1641 && !PoseView()->IsDesktopWindow()) {
1642 fNavigator
= new BNavigator(TargetModel());
1643 fPoseContainer
->GridLayout()->AddView(fNavigator
,
1646 SetPathWatchingEnabled(settings
.ShowNavigator()
1647 || settings
.ShowFullPathInTitleBar());
1650 if (!settings
.SingleWindowBrowse()
1651 && !fIsDesktop
&& TargetModel()->IsDesktop()) {
1652 // Close the "Desktop" window, but not the Desktop
1656 SetSingleWindowBrowseShortcuts(
1657 settings
.SingleWindowBrowse());
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());
1675 case kDontMoveFilesToTrashChanged
:
1677 bool dontMoveToTrash
1678 = settings
.DontMoveFilesToTrash();
1681 = fFileContextMenu
->FindItem(kMoveToTrash
);
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
);
1692 item
->SetLabel(dontMoveToTrash
1693 ? B_TRANSLATE("Delete")
1694 : B_TRANSLATE("Move to Trash"));
1702 _inherited::MessageReceived(message
);
1709 case B_NODE_MONITOR
:
1714 _inherited::MessageReceived(message
);
1721 BContainerWindow::SetCutItem(BMenu
* menu
)
1724 if ((item
= menu
->FindItem(B_CUT
)) == NULL
1725 && (item
= menu
->FindItem(kCutMoreSelectionToClipboard
)) == NULL
)
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
));
1736 item
->SetLabel(B_TRANSLATE("Cut"));
1737 item
->SetShortcut('X', B_COMMAND_KEY
);
1738 item
->SetMessage(new BMessage(B_CUT
));
1744 BContainerWindow::SetCopyItem(BMenu
* menu
)
1747 if ((item
= menu
->FindItem(B_COPY
)) == NULL
1748 && (item
= menu
->FindItem(kCopyMoreSelectionToClipboard
)) == NULL
) {
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
));
1760 item
->SetLabel(B_TRANSLATE("Copy"));
1761 item
->SetShortcut('C', B_COMMAND_KEY
);
1762 item
->SetMessage(new BMessage(B_COPY
));
1768 BContainerWindow::SetPasteItem(BMenu
* menu
)
1771 if ((item
= menu
->FindItem(B_PASTE
)) == NULL
1772 && (item
= menu
->FindItem(kPasteLinksFromClipboard
)) == NULL
) {
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
));
1783 item
->SetLabel(B_TRANSLATE("Paste"));
1784 item
->SetShortcut('V', B_COMMAND_KEY
);
1785 item
->SetMessage(new BMessage(B_PASTE
));
1791 BContainerWindow::SetArrangeMenu(BMenu
* menu
)
1794 if ((item
= menu
->FindItem(kCleanup
)) == NULL
1795 && (item
= menu
->FindItem(kCleanupAll
)) == NULL
) {
1799 item
->Menu()->SetEnabled(PoseView()->CountItems() > 0
1800 && (PoseView()->ViewMode() != kListMode
));
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();
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
);
1821 BContainerWindow::SetCloseItem(BMenu
* menu
)
1824 if ((item
= menu
->FindItem(B_QUIT_REQUESTED
)) == NULL
1825 && (item
= menu
->FindItem(kCloseAllWindows
)) == NULL
) {
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
));
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
));
1844 BContainerWindow::IsShowing(const node_ref
* node
) const
1846 return PoseView()->Represents(node
);
1851 BContainerWindow::IsShowing(const entry_ref
* entry
) const
1853 return PoseView()->Represents(entry
);
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
);
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());
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
)));
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
)));
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);
1976 BContainerWindow::AddWindowMenu(BMenu
* menu
)
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
);
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
);
2118 BContainerWindow::AddShortcuts()
2120 // add equivalents of the menu shortcuts to the menuless desktop window
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());
2174 BContainerWindow::MenusBeginning()
2176 if (fMenuBar
== NULL
)
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();
2186 int32 selectCount
= PoseView()->SelectionList()->CountItems();
2188 SetupOpenWithMenu(fFileMenu
);
2189 SetupMoveCopyMenus(selectCount
2190 ? PoseView()->SelectionList()->FirstItem()->TargetModel()->EntryRef()
2193 if (TargetModel()->IsRoot()) {
2195 BVolumeRoster().GetBootVolume(&boot
);
2197 bool ejectableVolumeSelected
= false;
2199 int32 count
= PoseView()->SelectionList()->CountItems();
2200 for (int32 index
= 0; index
< count
; index
++) {
2202 = PoseView()->SelectionList()->ItemAt(index
)->TargetModel();
2203 if (model
->IsVolume()) {
2205 volume
.SetTo(model
->NodeRef()->device
);
2206 if (volume
!= boot
) {
2207 ejectableVolumeSelected
= true;
2212 BMenuItem
* item
= fMenuBar
->FindItem(kUnmountVolume
);
2214 item
->SetEnabled(ejectableVolumeSelected
);
2217 UpdateMenu(fMenuBar
, kMenuBarContext
);
2219 AddMimeTypesToMenu(fAttrMenu
);
2221 if (IsPrintersDir()) {
2222 EnableNamedMenuItem(fFileMenu
, B_TRANSLATE("Make active printer"),
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
);
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();
2247 menu
->RemoveItem(fNavigationItem
);
2248 BMenuItem
* item
= menu
->RemoveItem((int32
)0);
2249 ASSERT(item
!= fNavigationItem
);
2254 // if we weren't passed a ref then we're navigating this window
2256 ref
= TargetModel()->EntryRef();
2259 if (entry
.SetTo(ref
) != B_OK
)
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())) {
2271 if (model
.IsSymLink()) {
2272 if (entry
.SetTo(model
.EntryRef(), true) != B_OK
)
2275 Model
resolvedModel(&entry
);
2276 if (resolvedModel
.InitCheck() != B_OK
|| !resolvedModel
.IsContainer())
2279 entry
.GetRef(&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
);
2304 parent
->SetTrackingHook(NULL
, NULL
);
2309 BContainerWindow::SetUpEditQueryItem(BMenu
* 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
)
2327 if (model
.IsQuery() || model
.IsQueryTemplate()) {
2328 queryInSelection
= true;
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
);
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());
2355 BContainerWindow::SetupOpenWithMenu(BMenu
* parent
)
2357 // start by removing nav item (and separator) from old menu
2358 if (fOpenWithItem
) {
2359 BMenu
* menu
= fOpenWithItem
->Menu();
2361 menu
->RemoveItem(fOpenWithItem
);
2363 delete fOpenWithItem
;
2367 if (PoseView()->SelectionList()->CountItems() == 0) {
2368 // no selection, nothing to open
2372 if (TargetModel()->IsRoot()) {
2373 // don't add ourselves if we are root
2378 // check if only item in selection list is the root
2379 // and do not add if true
2382 BMenuItem
* item
= parent
->FindItem(kOpenSelection
);
2384 int32 count
= PoseView()->SelectionList()->CountItems();
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);
2410 BContainerWindow::PopulateMoveCopyNavMenu(BNavMenu
* navMenu
, uint32 what
,
2411 const entry_ref
* ref
, bool addLocalOnly
)
2414 BVolumeRoster volumeRoster
;
2415 BDirectory directory
;
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())
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
,
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
);
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
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());
2494 // add all persistent writable volumes
2495 volumeRoster
.Rewind();
2496 while (volumeRoster
.GetNextVolume(&volume
) == B_OK
) {
2497 if (volume
.IsReadOnly() || !volume
.IsPersistent())
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
2513 BContainerWindow::SetupMoveCopyMenus(const entry_ref
* item_ref
, BMenu
* parent
)
2515 if (IsTrash() || InTrash() || IsPrintersDir() || !fMoveToItem
2516 || !fCopyToItem
|| !fCreateLinkItem
|| TargetModel()->IsRoot()) {
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
2525 BMenuItem
* trash
= parent
->FindItem(kMoveToTrash
);
2527 index
= parent
->IndexOf(trash
) + 2;
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"));
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
2566 if (entry
.SetTo(item_ref
) != B_OK
)
2569 Model
tempModel(&entry
);
2570 if (tempModel
.InitCheck() != B_OK
)
2573 if (tempModel
.IsRoot() || tempModel
.IsVolume())
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);
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);
2610 identifyItem
->SetLabel(B_TRANSLATE("Identify"));
2611 identifyItem
->Message()->ReplaceBool("force", false);
2618 BContainerWindow::ShowDropContextMenu(BPoint 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
);
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
));
2634 item
->SetLabel(B_TRANSLATE("Create link here"));
2635 item
->SetMessage(new BMessage(kCreateLink
));
2638 item
= fDropContextMenu
->Go(global
, true, true);
2640 return item
->Command();
2647 BContainerWindow::ShowContextMenu(BPoint loc
, const entry_ref
* ref
, BView
*)
2651 PoseView()->ConvertToScreen(&global
);
2652 PoseView()->CommitActivePose();
2655 // clicked on a pose, show file or volume context menu
2658 if (model
.IsTrash()) {
2659 if (fTrashContextMenu
->Window() || Dragging())
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);
2672 bool showAsVolume
= false;
2673 bool filePanel
= PoseView()->IsFilePanel();
2676 fContextMenu
= NULL
;
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())
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
,
2720 // this is now asynchronous so that we don't
2721 // deadlock in Window::Quit,
2722 fDragContextMenu
->Go(global
, true, false, true);
2726 } else if (TargetModel()->IsRoot() || model
.IsVolume()) {
2727 fContextMenu
= fVolumeContextMenu
;
2728 showAsVolume
= true;
2730 fContextMenu
= fFileContextMenu
;
2732 // clean up items from last context menu
2734 if (fContextMenu
!= NULL
) {
2735 if (fContextMenu
->Window())
2740 if (model
.InitCheck() == B_OK
) { // ??? Do I need this ???
2742 // non-volume enable/disable copy, move, identify
2743 EnableNamedMenuItem(fContextMenu
, kDuplicateSelection
,
2745 EnableNamedMenuItem(fContextMenu
, kMoveToTrash
, false);
2746 EnableNamedMenuItem(fContextMenu
, kIdentifyEntry
,
2749 // volume model, enable/disable the Unmount item
2750 bool ejectableVolumeSelected
= false;
2753 BVolumeRoster().GetBootVolume(&boot
);
2755 volume
.SetTo(model
.NodeRef()->device
);
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())
2779 // Repopulate desktop menu if IsDesktop
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
;
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();
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
);
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);
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());
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
2886 Model
* targetModel
= TargetModel();
2887 ASSERT(targetModel
!= NULL
);
2889 bool needSeparator
= true;
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;
2899 TemplatesMenu
* templatesMenu
= new TemplatesMenu(PoseView(),
2900 B_TRANSLATE("New"));
2901 menu
->AddItem(templatesMenu
);
2902 templatesMenu
->SetTargetForItems(PoseView());
2903 templatesMenu
->SetFont(be_plain_font
);
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();
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'));
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
);
2939 menu
->AddSeparatorItem();
2940 BMenuItem
* testing
= new BMenuItem("Test icon cache",
2941 new BMessage(kTestIconCache
));
2942 menu
->AddItem(testing
);
2945 // target items as needed
2946 menu
->SetTargetForItems(PoseView());
2947 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
2948 pasteItem
->SetTarget(this);
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
)));
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());
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?
3002 if (info
.GetSupportedTypes(&message
) == B_OK
) {
3005 if (message
.GetInfo("types", &typeCode
,
3011 // check all supported types if it has some set
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
))
3026 if (!secondary
&& !primary
)
3031 ((eachAddon
)(item
->model
, item
->model
->Name(), item
->key
,
3032 item
->modifiers
, primary
, passThru
));
3039 BContainerWindow::BuildMimeTypeList(BStringList
& mimeTypes
)
3041 int32 count
= PoseView()->SelectionList()->CountItems();
3043 // just add the type of the current directory
3044 AddMimeTypeString(mimeTypes
, TargetModel());
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
);
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);
3078 menu
->GetFont(&font
);
3080 menu
= item
->Submenu();
3084 menu
->SetFont(&font
);
3086 // found the addons menu, empty it first
3088 item
= menu
->RemoveItem((int32
)0);
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
, ¶ms
, 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
));
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);
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()->
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());
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
;
3174 BMessage
* message
= item
->Message();
3175 if (message
== NULL
) {
3176 item
->SetMarked(false);
3180 if (message
->FindInt32("size", &size
) != B_OK
)
3182 item
->SetMarked(iconSize
== size
);
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
;
3201 BMessage
* message
= item
->Message();
3202 if (message
== NULL
) {
3203 item
->SetMarked(false);
3207 if (message
->FindInt32("icon_size", &size
) != B_OK
)
3209 item
->SetMarked(iconSize
== size
);
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
);
3223 SetArrangeMenu(menu
);
3226 BEntry
entry(TargetModel()->EntryRef());
3231 bool parentIsRoot
= (entry
.GetParent(&parent
) == B_OK
3232 && parent
.GetEntry(&entry
) == B_OK
3233 && entry
.GetRef(&ref
) == B_OK
3236 EnableNamedMenuItem(menu
, kOpenParentDir
, !TargetModel()->IsDesktop()
3237 && !TargetModel()->IsRoot()
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"));
3248 TemplatesMenu
* templatesMenu
= dynamic_cast<TemplatesMenu
*>(
3250 if (templatesMenu
!= NULL
)
3251 templatesMenu
->UpdateMenuState();
3255 BuildAddOnMenu(menu
);
3260 BContainerWindow::LoadAddOn(BMessage
* message
)
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
);
3278 // add selected refs to message
3279 BMessage
* refs
= new BMessage(B_REFS_RECEIVED
);
3280 BObjectList
<BPose
>* selectionList
= PoseView()->SelectionList();
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());
3295 BContainerWindow::_UpdateSelectionMIMEInfo()
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(),
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);
3321 BContainerWindow::_AddFolderIcon()
3323 if (fMenuBar
== NULL
) {
3324 // We don't want to add the icon if there's no menubar
3328 float iconSize
= fMenuBar
->Bounds().Height() - 2;
3332 fDraggableIcon
= new(std::nothrow
) DraggableContainerIcon();
3333 if (fDraggableIcon
!= NULL
) {
3334 BLayoutItem
* item
= fMenuContainer
->GroupLayout()->AddView(
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
);
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
);
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());
3378 BContainerWindow::NewAttributeMenu(BMenu
* menu
)
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,
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));
3428 menu
->AddItem(NewAttributeMenuItem(B_TRANSLATE("Permissions"),
3429 kAttrStatMode
, B_STRING_TYPE
, 80, B_ALIGN_LEFT
, false, true));
3434 BContainerWindow::ShowAttributeMenu()
3437 fMenuBar
->AddItem(fAttrMenu
);
3442 BContainerWindow::HideAttributeMenu()
3445 fMenuBar
->RemoveItem(fAttrMenu
);
3450 BContainerWindow::MarkAttributeMenu()
3452 MarkAttributeMenu(fAttrMenu
);
3457 BContainerWindow::MarkAttributeMenu(BMenu
* menu
)
3462 int32 count
= menu
->CountItems();
3463 for (int32 index
= 0; index
< count
; index
++) {
3464 BMenuItem
* item
= menu
->ItemAt(index
);
3466 if (item
->Message()) {
3467 if (item
->Message()->FindInt32("attr_hash", &attrHash
) == B_OK
)
3468 item
->SetMarked(PoseView()->ColumnFor((uint32
)attrHash
) != 0);
3470 item
->SetMarked(false);
3473 BMenu
* submenu
= item
->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
)
3481 item
->SetMarked(PoseView()->ColumnFor((uint32
)attrHash
)
3484 item
->SetMarked(false);
3493 BContainerWindow::MarkArrangeByMenu(BMenu
* menu
)
3498 int32 count
= menu
->CountItems();
3499 for (int32 index
= 0; index
< count
; index
++) {
3500 BMenuItem
* item
= menu
->ItemAt(index
);
3501 if (item
->Message()) {
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());
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.
3523 BContainerWindow::AddMimeMenu(const BMimeType
& mimeType
, bool isSuperType
,
3524 BMenu
* menu
, int32 start
)
3526 AutoLock
<BLooper
> _(menu
->Looper());
3528 if (!mimeType
.IsValid())
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
)
3538 if (message
->FindString("mimetype", &type
) == B_OK
3539 && !strcmp(mimeType
.Type(), type
)) {
3540 return item
->Submenu();
3545 char description
[B_MIME_TYPE_LENGTH
];
3546 const char* label
= mimeType
.Type();
3548 if (!mimeType
.IsInstalled())
3551 // only add things to menu which have "user-visible" data
3552 if (mimeType
.GetAttrInfo(&attrInfo
) != B_OK
)
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
;
3561 // If it is a supertype, we create the menu anyway as it may have
3562 // submenus later on.
3563 mimeMenu
= new BMenu(label
);
3565 menu
->GetFont(&font
);
3566 mimeMenu
->SetFont(&font
);
3570 const char* publicName
;
3571 while (attrInfo
.FindString("attr:public_name", ++index
, &publicName
)
3573 if (!attrInfo
.FindBool("attr:viewable", index
)) {
3574 // don't add if attribute not viewable
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
)
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
);
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
)
3607 BMessage
* message
= new BMessage(kMIMETypeItem
);
3608 message
->AddString("mimetype", mimeType
.Type());
3609 menu
->AddItem(new IconMenuItem(mimeMenu
, message
, mimeType
.Type()));
3616 BContainerWindow::AddMimeTypesToMenu(BMenu
* menu
)
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);
3628 // Add a separator item if there is none yet
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
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
)
3677 int32 itemsFound
= 0;
3678 int32 menusFound
= 0;
3679 for (int32 i
= 0; BMenuItem
* item
= superMenu
->ItemAt(i
); i
++) {
3680 if (item
->Submenu() != NULL
)
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
);
3706 MarkAttributeMenu(menu
);
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();
3720 return _inherited::ResolveSpecifier(message
, index
, specifier
,
3726 BContainerWindow::DelayedTaskLoop()
3729 fTaskLoop
= new PiggybackTaskLoop
;
3736 BContainerWindow::NeedsDefaultStateSetup()
3738 if (TargetModel() == NULL
)
3741 if (TargetModel()->IsRoot()) {
3742 // don't try to set up anything if we are root
3746 WindowStateNodeOpener
opener(this, false);
3747 if (opener
.StreamNode() == NULL
) {
3748 // can't read state, give up
3752 return !NodeHasSavedState(opener
.Node());
3757 BContainerWindow::DefaultStateSourceNode(const char* name
, BNode
* result
,
3758 bool createNew
, bool createFolder
)
3760 //PRINT(("looking for default state in tracker settings dir\n"));
3762 if (FSFindTrackerSettingsDir(&settingsPath
) != B_OK
)
3765 BDirectory
dir(settingsPath
.Path());
3767 BPath
path(settingsPath
);
3769 if (!BEntry(path
.Path()).Exists()) {
3773 BPath
tmpPath(settingsPath
);
3775 // deal with several levels of folders
3776 const char* nextSlash
= strchr(name
, '/');
3781 tmp
.SetTo(name
, nextSlash
- name
);
3782 tmpPath
.Append(tmp
.String());
3784 mkdir(tmpPath
.Path(), 0777);
3786 name
= nextSlash
+ 1;
3788 // can't deal with a slash at end
3794 if (mkdir(path
.Path(), 0777) < 0)
3798 if (dir
.CreateFile(name
, &file
) != B_OK
)
3803 //PRINT(("using default state from %s\n", path.Path()));
3804 result
->SetTo(path
.Path());
3805 return result
->InitCheck() == B_OK
;
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
)
3826 if (!TargetModel()->IsRoot()) {
3828 FSGetDeskDir(&deskDir
);
3830 // try copying state from our parent directory, unless it is the
3832 BEntry
entry(TargetModel()->EntryRef());
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
,
3860 // don't copy over the attributes if we are the Desktop
3864 // copy over the attributes
3866 // set up a filter of the attributes we want copied
3867 const char* allowAttrs
[] = {
3869 kAttrWindowWorkspace
,
3871 kAttrViewStateForeign
,
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
, ¶ms
);
3884 SelectiveAttributeTransformer
scrollOriginCleaner(kAttrViewState
,
3885 ClearViewOriginOne
, ¶ms
);
3888 AttributeStreamMemoryNode memoryNode
;
3889 NamesToAcceptAttrFilter
filter(allowAttrs
);
3890 AttributeStreamFileNode
fileNode(&defaultingNode
);
3892 *opener
.StreamNode() << scrollOriginCleaner
<< frameOffsetter
3893 << memoryNode
<< filter
<< fileNode
;
3898 BContainerWindow::RestoreWindowState(AttributeStreamNode
* node
)
3900 if (node
== NULL
|| fIsDesktop
) {
3901 // don't restore any window state if we are the Desktop
3905 const char* rectAttributeName
;
3906 const char* workspaceAttributeName
;
3907 if (TargetModel()->IsRoot()) {
3908 rectAttributeName
= kAttrDisksFrame
;
3909 workspaceAttributeName
= kAttrDisksWorkspace
;
3911 rectAttributeName
= kAttrWindowFrame
;
3912 workspaceAttributeName
= kAttrWindowWorkspace
;
3915 BRect
frame(Frame());
3916 if (node
->Read(rectAttributeName
, 0, B_RECT_TYPE
, sizeof(BRect
), &frame
)
3918 MoveTo(frame
.LeftTop());
3919 ResizeTo(frame
.Width(), frame
.Height());
3921 sNewWindRect
.OffsetBy(kWindowStaggerBy
, kWindowStaggerBy
);
3923 fPreviousBounds
= Bounds();
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)
3934 // restore window decor settings
3935 int32 size
= node
->Contains(kAttrWindowDecor
, B_RAW_TYPE
);
3938 if (((fContainerWindowFlags
& kRestoreDecor
) != 0)
3939 && node
->Read(kAttrWindowDecor
, 0, B_RAW_TYPE
, size
, buffer
)
3941 BMessage decorSettings
;
3942 if (decorSettings
.Unflatten(buffer
) == B_OK
)
3943 SetDecoratorSettings(decorSettings
);
3950 BContainerWindow::RestoreWindowState(const BMessage
& message
)
3953 // don't restore any window state if we are the Desktop
3957 const char* rectAttributeName
;
3958 const char* workspaceAttributeName
;
3959 if (TargetModel()->IsRoot()) {
3960 rectAttributeName
= kAttrDisksFrame
;
3961 workspaceAttributeName
= kAttrDisksWorkspace
;
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());
3972 sNewWindRect
.OffsetBy(kWindowStaggerBy
, kWindowStaggerBy
);
3975 if ((fContainerWindowFlags
& kRestoreWorkspace
)
3976 && message
.FindInt32(workspaceAttributeName
,
3977 (int32
*)&workspace
) == B_OK
) {
3978 SetWorkspaces(workspace
);
3981 if (fContainerWindowFlags
& kIsHidden
)
3984 // restore window decor settings
3985 BMessage decorSettings
;
3986 if ((fContainerWindowFlags
& kRestoreDecor
)
3987 && message
.FindMessage(kAttrWindowDecor
, &decorSettings
) == B_OK
) {
3988 SetDecoratorSettings(decorSettings
);
3994 BContainerWindow::SaveWindowState(AttributeStreamNode
* node
)
3997 // don't save window state if we are the Desktop
4001 ASSERT(node
!= NULL
);
4003 const char* rectAttributeName
;
4004 const char* workspaceAttributeName
;
4005 if (TargetModel() != NULL
&& TargetModel()->IsRoot()) {
4006 rectAttributeName
= kAttrDisksFrame
;
4007 workspaceAttributeName
= kAttrDisksWorkspace
;
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
),
4021 BMessage decorSettings
;
4022 if (GetDecoratorSettings(&decorSettings
) == B_OK
) {
4023 int32 size
= decorSettings
.FlattenedSize();
4025 if (decorSettings
.Flatten(buffer
, size
) == B_OK
) {
4026 node
->Write(kAttrWindowDecor
, 0, B_RAW_TYPE
, size
, buffer
);
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
;
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
);
4059 BContainerWindow::DragStart(const BMessage
* dragMessage
)
4061 if (dragMessage
== NULL
)
4064 // if already dragging, or
4065 // if all the refs match
4067 && SpringLoadedFolderCompareMessages(dragMessage
, fDragMessage
)) {
4071 // cache the current drag message
4072 // build a list of the mimetypes in the message
4073 SpringLoadedFolderCacheDragData(dragMessage
, &fDragMessage
,
4076 fWaitingForRefs
= true;
4083 BContainerWindow::DragStop()
4085 delete fDragMessage
;
4086 fDragMessage
= NULL
;
4088 delete fCachedTypesList
;
4089 fCachedTypesList
= NULL
;
4091 fWaitingForRefs
= false;
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();
4113 BContainerWindow::ShowNavigator(bool show
)
4115 if (PoseView()->IsDesktopWindow() || !TargetModel()->IsDirectory()
4116 || fPoseView
->IsFilePanel()) {
4121 if (Navigator() && !Navigator()->IsHidden())
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();
4135 if (!Navigator() || Navigator()->IsHidden())
4138 if (PoseView()->VScrollBar())
4139 PoseView()->UpdateScrollRange();
4147 BContainerWindow::SetSingleWindowBrowseShortcuts(bool enabled
)
4149 if (PoseView()->IsDesktopWindow())
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());
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
);
4205 BContainerWindow::SetPathWatchingEnabled(bool enable
)
4207 if (IsPathWatchingEnabled()) {
4208 stop_watching(this);
4209 fIsWatchingPath
= false;
4213 if (TargetModel() != NULL
) {
4216 TargetModel()->GetEntry(&entry
);
4219 err
= entry
.GetParent(&entry
);
4223 char name
[B_FILE_NAME_LENGTH
];
4224 entry
.GetName(name
);
4225 if (strcmp(name
, "/") == 0)
4229 entry
.GetNodeRef(&ref
);
4230 watch_node(&ref
, B_WATCH_NAME
, this);
4231 } while (err
== B_OK
);
4233 fIsWatchingPath
= err
== B_OK
;
4235 fIsWatchingPath
= false;
4241 BContainerWindow::PulseTaskLoop()
4244 fTaskLoop
->PulseMe();
4249 BContainerWindow::PopulateArrangeByMenu(BMenu
* menu
)
4251 if (!fAttrMenu
|| !menu
)
4253 // empty fArrangeByMenu...
4255 while ((item
= menu
->RemoveItem((int32
)0)) != NULL
)
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
),
4282 item
->SetTarget(PoseView());
4283 menu
->AddItem(item
);
4287 // #pragma mark - WindowStateNodeOpener
4290 WindowStateNodeOpener::WindowStateNodeOpener(BContainerWindow
* window
,
4297 if (window
->TargetModel() && window
->TargetModel()->IsRoot()) {
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(),
4306 if (fModelOpener
->IsOpen(forWriting
)) {
4307 fStreamNode
= new AttributeStreamFileNode(
4308 fModelOpener
->TargetModel()->Node());
4313 WindowStateNodeOpener::~WindowStateNodeOpener()
4315 delete fModelOpener
;
4322 WindowStateNodeOpener::SetTo(const BDirectory
* node
)
4324 delete fModelOpener
;
4328 fModelOpener
= NULL
;
4329 fNode
= new BDirectory(*node
);
4330 fStreamNode
= new AttributeStreamFileNode(fNode
);
4335 WindowStateNodeOpener::SetTo(const BEntry
* entry
, bool forWriting
)
4337 delete fModelOpener
;
4341 fModelOpener
= NULL
;
4342 fNode
= new BFile(entry
, (uint32
)(forWriting
? O_RDWR
: O_RDONLY
));
4343 fStreamNode
= new AttributeStreamFileNode(fNode
);
4348 WindowStateNodeOpener::SetTo(Model
* model
, bool forWriting
)
4350 delete fModelOpener
;
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
4372 WindowStateNodeOpener::Node() const
4380 return fModelOpener
->TargetModel()->Node();
4384 // #pragma mark - BorderedView
4387 BorderedView::BorderedView()
4389 BGroupView(B_VERTICAL
, 0),
4390 fEnableBorderHighlight(true)
4392 GroupLayout()->SetInsets(1);
4397 BorderedView::WindowActivated(bool active
)
4399 BContainerWindow
* window
= dynamic_cast<BContainerWindow
*>(Window());
4403 if (window
->PoseView()->IsFocus())
4404 PoseViewFocused(active
); // Update border color
4408 void BorderedView::EnableBorderHighlight(bool enable
)
4410 fEnableBorderHighlight
= enable
;
4411 PoseViewFocused(false);
4416 BorderedView::PoseViewFocused(bool focused
)
4418 BContainerWindow
* window
= dynamic_cast<BContainerWindow
*>(Window());
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
;
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
);
4443 BorderedView::Pulse()
4445 BContainerWindow
* window
= dynamic_cast<BContainerWindow
*>(Window());
4447 window
->PulseTaskLoop();