RemoteDrawingEngine: Reduce RP_READ_BITMAP result timeout.
[haiku.git] / src / kits / tracker / FilePanelPriv.cpp
blob91b26319057569958136e95a67371b4b774fc623
1 /*
2 Open Tracker License
4 Terms and Conditions
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
36 #include "FilePanelPriv.h"
38 #include <string.h>
40 #include <Alert.h>
41 #include <Application.h>
42 #include <Button.h>
43 #include <Catalog.h>
44 #include <Debug.h>
45 #include <Directory.h>
46 #include <FindDirectory.h>
47 #include <Locale.h>
48 #include <MenuBar.h>
49 #include <MenuField.h>
50 #include <MenuItem.h>
51 #include <MessageFilter.h>
52 #include <NodeInfo.h>
53 #include <NodeMonitor.h>
54 #include <Path.h>
55 #include <Roster.h>
56 #include <SymLink.h>
57 #include <ScrollView.h>
58 #include <String.h>
59 #include <StopWatch.h>
60 #include <TextControl.h>
61 #include <TextView.h>
62 #include <Volume.h>
63 #include <VolumeRoster.h>
65 #include "Attributes.h"
66 #include "AttributeStream.h"
67 #include "AutoLock.h"
68 #include "Commands.h"
69 #include "CountView.h"
70 #include "DesktopPoseView.h"
71 #include "DirMenu.h"
72 #include "FavoritesMenu.h"
73 #include "FSUtils.h"
74 #include "FSClipboard.h"
75 #include "IconMenuItem.h"
76 #include "MimeTypes.h"
77 #include "NavMenu.h"
78 #include "Tracker.h"
79 #include "Utilities.h"
81 #include "tracker_private.h"
84 #undef B_TRANSLATION_CONTEXT
85 #define B_TRANSLATION_CONTEXT "FilePanelPriv"
88 const char* kDefaultFilePanelTemplate = "FilePanelSettings";
91 static uint32
92 GetLinkFlavor(const Model* model, bool resolve = true)
94 if (model && model->IsSymLink()) {
95 if (!resolve)
96 return B_SYMLINK_NODE;
97 model = model->LinkTo();
99 if (!model)
100 return 0;
102 if (model->IsDirectory())
103 return B_DIRECTORY_NODE;
105 return B_FILE_NODE;
109 static filter_result
110 key_down_filter(BMessage* message, BHandler** handler, BMessageFilter* filter)
112 ASSERT(filter != NULL);
113 if (filter == NULL)
114 return B_DISPATCH_MESSAGE;
116 TFilePanel* panel = dynamic_cast<TFilePanel*>(filter->Looper());
117 ASSERT(panel != NULL);
119 if (panel == NULL)
120 return B_DISPATCH_MESSAGE;
122 BPoseView* view = panel->PoseView();
123 if (panel->TrackingMenu())
124 return B_DISPATCH_MESSAGE;
126 uchar key;
127 if (message->FindInt8("byte", (int8*)&key) != B_OK)
128 return B_DISPATCH_MESSAGE;
130 int32 modifier = 0;
131 message->FindInt32("modifiers", &modifier);
133 if (modifier & B_COMMAND_KEY && key == B_UP_ARROW) {
134 filter->Looper()->PostMessage(kOpenParentDir);
135 return B_SKIP_MESSAGE;
138 if (modifier & B_COMMAND_KEY && key == 'w') {
139 filter->Looper()->PostMessage(kCancelButton);
140 return B_SKIP_MESSAGE;
143 if (!modifier && key == B_ESCAPE) {
144 if (view->ActivePose())
145 view->CommitActivePose(false);
146 else if (view->IsFiltering())
147 filter->Looper()->PostMessage(B_CANCEL, *handler);
148 else
149 filter->Looper()->PostMessage(kCancelButton);
151 return B_SKIP_MESSAGE;
154 if (key == B_RETURN && view->ActivePose()) {
155 view->CommitActivePose();
157 return B_SKIP_MESSAGE;
160 return B_DISPATCH_MESSAGE;
164 // #pragma mark - TFilePanel
167 TFilePanel::TFilePanel(file_panel_mode mode, BMessenger* target,
168 const BEntry* startDir, uint32 nodeFlavors, bool multipleSelection,
169 BMessage* message, BRefFilter* filter, uint32 containerWindowFlags,
170 window_look look, window_feel feel, bool hideWhenDone)
172 BContainerWindow(0, containerWindowFlags, look, feel, 0,
173 B_CURRENT_WORKSPACE, false),
174 fDirMenu(NULL),
175 fDirMenuField(NULL),
176 fTextControl(NULL),
177 fClientObject(NULL),
178 fSelectionIterator(0),
179 fMessage(NULL),
180 fHideWhenDone(hideWhenDone),
181 fIsTrackingMenu(false)
183 InitIconPreloader();
185 fIsSavePanel = (mode == B_SAVE_PANEL);
187 BRect windRect(85, 50, 568, 296);
188 MoveTo(windRect.LeftTop());
189 ResizeTo(windRect.Width(), windRect.Height());
191 fNodeFlavors = (nodeFlavors == 0) ? B_FILE_NODE : nodeFlavors;
193 if (target)
194 fTarget = *target;
195 else
196 fTarget = BMessenger(be_app);
198 if (message)
199 SetMessage(message);
200 else if (fIsSavePanel)
201 fMessage = new BMessage(B_SAVE_REQUESTED);
202 else
203 fMessage = new BMessage(B_REFS_RECEIVED);
205 gLocalizedNamePreferred
206 = BLocaleRoster::Default()->IsFilesystemTranslationPreferred();
208 // check for legal starting directory
209 Model* model = new Model();
210 bool useRoot = true;
212 if (startDir) {
213 if (model->SetTo(startDir) == B_OK && model->IsDirectory())
214 useRoot = false;
215 else {
216 delete model;
217 model = new Model();
221 if (useRoot) {
222 BPath path;
223 if (find_directory(B_USER_DIRECTORY, &path) == B_OK) {
224 BEntry entry(path.Path(), true);
225 if (entry.InitCheck() == B_OK && model->SetTo(&entry) == B_OK)
226 useRoot = false;
230 if (useRoot) {
231 BVolume volume;
232 BDirectory root;
233 BVolumeRoster volumeRoster;
234 volumeRoster.GetBootVolume(&volume);
235 volume.GetRootDirectory(&root);
237 BEntry entry;
238 root.GetEntry(&entry);
239 model->SetTo(&entry);
242 fTaskLoop = new PiggybackTaskLoop;
244 AutoLock<BWindow> lock(this);
245 fBorderedView = new BorderedView;
246 CreatePoseView(model);
247 fBorderedView->GroupLayout()->SetInsets(1);
248 fPoseView->SetRefFilter(filter);
249 if (!fIsSavePanel)
250 fPoseView->SetMultipleSelection(multipleSelection);
252 fPoseView->SetFlags(fPoseView->Flags() | B_NAVIGABLE);
253 fPoseView->SetPoseEditing(false);
254 AddCommonFilter(new BMessageFilter(B_KEY_DOWN, key_down_filter));
255 AddCommonFilter(new BMessageFilter(B_SIMPLE_DATA,
256 TFilePanel::MessageDropFilter));
257 AddCommonFilter(new BMessageFilter(B_NODE_MONITOR, TFilePanel::FSFilter));
259 // inter-application observing
260 BMessenger tracker(kTrackerSignature);
261 BHandler::StartWatching(tracker, kDesktopFilePanelRootChanged);
263 Init();
267 TFilePanel::~TFilePanel()
269 BMessenger tracker(kTrackerSignature);
270 BHandler::StopWatching(tracker, kDesktopFilePanelRootChanged);
272 delete fMessage;
276 filter_result
277 TFilePanel::MessageDropFilter(BMessage* message, BHandler**,
278 BMessageFilter* filter)
280 if (message == NULL || !message->WasDropped())
281 return B_DISPATCH_MESSAGE;
283 ASSERT(filter != NULL);
284 if (filter == NULL)
285 return B_DISPATCH_MESSAGE;
287 TFilePanel* panel = dynamic_cast<TFilePanel*>(filter->Looper());
288 ASSERT(panel != NULL);
290 if (panel == NULL)
291 return B_DISPATCH_MESSAGE;
293 uint32 type;
294 int32 count;
295 if (message->GetInfo("refs", &type, &count) != B_OK)
296 return B_SKIP_MESSAGE;
298 if (count != 1)
299 return B_SKIP_MESSAGE;
301 entry_ref ref;
302 if (message->FindRef("refs", &ref) != B_OK)
303 return B_SKIP_MESSAGE;
305 BEntry entry(&ref);
306 if (entry.InitCheck() != B_OK)
307 return B_SKIP_MESSAGE;
309 // if the entry is a symlink
310 // resolve it and see if it is a directory
311 // pass it on if it is
312 if (entry.IsSymLink()) {
313 entry_ref resolvedRef;
315 entry.GetRef(&resolvedRef);
316 BEntry resolvedEntry(&resolvedRef, true);
318 if (resolvedEntry.IsDirectory()) {
319 // both entry and ref need to be the correct locations
320 // for the last setto
321 resolvedEntry.GetRef(&ref);
322 entry.SetTo(&ref);
326 // if not a directory, set to the parent, and select the child
327 if (!entry.IsDirectory()) {
328 node_ref child;
329 if (entry.GetNodeRef(&child) != B_OK)
330 return B_SKIP_MESSAGE;
332 BPath path(&entry);
334 if (entry.GetParent(&entry) != B_OK)
335 return B_SKIP_MESSAGE;
337 entry.GetRef(&ref);
339 panel->fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
340 (&TFilePanel::SelectChildInParent, panel,
341 const_cast<const entry_ref*>(&ref),
342 const_cast<const node_ref*>(&child)),
343 ref == *panel->TargetModel()->EntryRef() ? 0 : 100000, 200000,
344 5000000);
345 // if the target directory is already current, we won't
346 // delay the initial selection try
348 // also set the save name to the dragged in entry
349 if (panel->IsSavePanel())
350 panel->SetSaveText(path.Leaf());
353 panel->SetTo(&ref);
355 return B_SKIP_MESSAGE;
359 filter_result
360 TFilePanel::FSFilter(BMessage* message, BHandler**, BMessageFilter* filter)
362 if (message == NULL)
363 return B_DISPATCH_MESSAGE;
365 ASSERT(filter != NULL);
366 if (filter == NULL)
367 return B_DISPATCH_MESSAGE;
369 TFilePanel* panel = dynamic_cast<TFilePanel*>(filter->Looper());
370 ASSERT(panel != NULL);
372 if (panel == NULL)
373 return B_DISPATCH_MESSAGE;
375 switch (message->FindInt32("opcode")) {
376 case B_ENTRY_MOVED:
378 node_ref itemNode;
379 message->FindInt64("node", (int64*)&itemNode.node);
381 node_ref dirNode;
382 message->FindInt32("device", &dirNode.device);
383 itemNode.device = dirNode.device;
384 message->FindInt64("to directory", (int64*)&dirNode.node);
386 const char* name;
387 if (message->FindString("name", &name) != B_OK)
388 break;
390 // if current directory moved, update entry ref and menu
391 // but not wind title
392 if (*(panel->TargetModel()->NodeRef()) == itemNode) {
393 panel->TargetModel()->UpdateEntryRef(&dirNode, name);
394 panel->SetTo(panel->TargetModel()->EntryRef());
395 return B_SKIP_MESSAGE;
397 break;
400 case B_ENTRY_REMOVED:
402 node_ref itemNode;
403 message->FindInt32("device", &itemNode.device);
404 message->FindInt64("node", (int64*)&itemNode.node);
406 // if folder we're watching is deleted, switch to root
407 // or Desktop
408 if (*(panel->TargetModel()->NodeRef()) == itemNode) {
409 BVolumeRoster volumeRoster;
410 BVolume volume;
411 volumeRoster.GetBootVolume(&volume);
413 BDirectory root;
414 volume.GetRootDirectory(&root);
416 BEntry entry;
417 entry_ref ref;
418 root.GetEntry(&entry);
419 entry.GetRef(&ref);
421 panel->SwitchDirToDesktopIfNeeded(ref);
423 panel->SetTo(&ref);
424 return B_SKIP_MESSAGE;
426 break;
430 return B_DISPATCH_MESSAGE;
434 void
435 TFilePanel::DispatchMessage(BMessage* message, BHandler* handler)
437 _inherited::DispatchMessage(message, handler);
438 if (message->what == B_KEY_DOWN || message->what == B_MOUSE_DOWN)
439 AdjustButton();
443 BFilePanelPoseView*
444 TFilePanel::PoseView() const
446 ASSERT(dynamic_cast<BFilePanelPoseView*>(fPoseView) != NULL);
448 return static_cast<BFilePanelPoseView*>(fPoseView);
452 bool
453 TFilePanel::QuitRequested()
455 // If we have a client object then this window will simply hide
456 // itself, to be closed later when the client object itself is
457 // destroyed. If we have no client then we must have been started
458 // from the "easy" functions which simply instantiate a TFilePanel
459 // and expect it to go away by itself
461 if (fClientObject != NULL) {
462 Hide();
463 if (fClientObject != NULL)
464 fClientObject->WasHidden();
466 BMessage message(*fMessage);
467 message.what = B_CANCEL;
468 message.AddInt32("old_what", (int32)fMessage->what);
469 message.AddPointer("source", fClientObject);
470 fTarget.SendMessage(&message);
472 return false;
475 return _inherited::QuitRequested();
479 BRefFilter*
480 TFilePanel::Filter() const
482 return fPoseView->RefFilter();
486 void
487 TFilePanel::SetTarget(BMessenger target)
489 fTarget = target;
493 void
494 TFilePanel::SetMessage(BMessage* message)
496 delete fMessage;
497 fMessage = new BMessage(*message);
501 void
502 TFilePanel::SetRefFilter(BRefFilter* filter)
504 ASSERT(filter != NULL);
505 if (filter == NULL)
506 return;
508 fPoseView->SetRefFilter(filter);
509 fPoseView->CommitActivePose();
510 fPoseView->Refresh();
512 if (fMenuBar == NULL)
513 return;
515 BMenuItem* favoritesItem = fMenuBar->FindItem(B_TRANSLATE("Favorites"));
516 if (favoritesItem == NULL)
517 return;
519 FavoritesMenu* favoritesSubMenu
520 = dynamic_cast<FavoritesMenu*>(favoritesItem->Submenu());
521 if (favoritesSubMenu != NULL)
522 favoritesSubMenu->SetRefFilter(filter);
526 void
527 TFilePanel::SetTo(const entry_ref* ref)
529 if (ref == NULL)
530 return;
532 entry_ref setToRef(*ref);
534 bool isDesktop = SwitchDirToDesktopIfNeeded(setToRef);
536 BEntry entry(&setToRef);
537 if (entry.InitCheck() != B_OK || !entry.IsDirectory())
538 return;
540 SwitchDirMenuTo(&setToRef);
542 PoseView()->SetIsDesktop(isDesktop);
543 fPoseView->SwitchDir(&setToRef);
545 AddShortcut('H', B_COMMAND_KEY, new BMessage(kSwitchToHome));
546 // our shortcut got possibly removed because the home
547 // menu item got removed - we shouldn't really have to do
548 // this - this is a workaround for a kit bug.
552 void
553 TFilePanel::Rewind()
555 fSelectionIterator = 0;
559 void
560 TFilePanel::SetClientObject(BFilePanel* panel)
562 fClientObject = panel;
566 bool
567 TFilePanel::IsOpenButtonAlwaysEnabled() const
569 return !fIsSavePanel && (fNodeFlavors & B_DIRECTORY_NODE) != 0;
573 void
574 TFilePanel::AdjustButton()
576 // adjust button state
577 BButton* button = dynamic_cast<BButton*>(FindView("default button"));
578 if (button == NULL)
579 return;
581 BTextControl* textControl
582 = dynamic_cast<BTextControl*>(FindView("text view"));
583 BObjectList<BPose>* selectionList = fPoseView->SelectionList();
584 BString buttonText = fButtonText;
585 bool enabled = false;
587 if (fIsSavePanel && textControl != NULL) {
588 enabled = textControl->Text()[0] != '\0';
589 if (fPoseView->IsFocus()) {
590 fPoseView->ShowSelection(true);
591 if (selectionList->CountItems() == 1) {
592 Model* model = selectionList->FirstItem()->TargetModel();
593 if (model->ResolveIfLink()->IsDirectory()) {
594 enabled = true;
595 buttonText = B_TRANSLATE("Open");
596 } else {
597 // insert the name of the selected model into
598 // the text field
599 textControl->SetText(model->Name());
600 textControl->MakeFocus(true);
603 } else
604 fPoseView->ShowSelection(false);
605 } else {
606 int32 count = selectionList->CountItems();
607 if (count) {
608 enabled = true;
610 // go through selection list looking at content
611 for (int32 index = 0; index < count; index++) {
612 Model* model = selectionList->ItemAt(index)->TargetModel();
614 uint32 modelFlavor = GetLinkFlavor(model, false);
615 uint32 linkFlavor = GetLinkFlavor(model, true);
617 // if only one item is selected and we're not in dir
618 // selection mode then we don't disable button ever
619 if ((modelFlavor == B_DIRECTORY_NODE
620 || linkFlavor == B_DIRECTORY_NODE)
621 && count == 1)
622 break;
624 if ((fNodeFlavors & modelFlavor) == 0
625 && (fNodeFlavors & linkFlavor) == 0) {
626 enabled = false;
627 break;
633 button->SetLabel(buttonText.String());
634 button->SetEnabled(IsOpenButtonAlwaysEnabled() || enabled);
638 void
639 TFilePanel::SelectionChanged()
641 AdjustButton();
643 if (fClientObject)
644 fClientObject->SelectionChanged();
648 status_t
649 TFilePanel::GetNextEntryRef(entry_ref* ref)
651 if (!ref)
652 return B_ERROR;
654 BPose* pose = fPoseView->SelectionList()->ItemAt(fSelectionIterator++);
655 if (!pose)
656 return B_ERROR;
658 *ref = *pose->TargetModel()->EntryRef();
659 return B_OK;
663 BPoseView*
664 TFilePanel::NewPoseView(Model* model, uint32)
666 return new BFilePanelPoseView(model);
670 void
671 TFilePanel::Init(const BMessage*)
673 BRect windRect(Bounds());
674 fBackView = new BView(Bounds(), "View", B_FOLLOW_ALL, 0);
675 fBackView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
676 AddChild(fBackView);
678 // add poseview menu bar
679 fMenuBar = new BMenuBar(BRect(0, 0, windRect.Width(), 1), "MenuBar");
680 fMenuBar->SetBorder(B_BORDER_FRAME);
681 fBackView->AddChild(fMenuBar);
683 // add directory menu and menufield
684 fDirMenu = new BDirMenu(0, this, kSwitchDirectory, "refs");
686 font_height ht;
687 be_plain_font->GetHeight(&ht);
688 float f_height = ht.ascent + ht.descent + ht.leading;
690 BRect rect;
691 rect.top = fMenuBar->Bounds().Height() + 8;
692 rect.left = windRect.left + 8;
693 rect.right = rect.left + 300;
694 rect.bottom = rect.top + (f_height > 22 ? f_height : 22);
696 fDirMenuField = new BMenuField(rect, "DirMenuField", "", fDirMenu);
697 fDirMenuField->MenuBar()->SetFont(be_plain_font);
698 fDirMenuField->SetDivider(0);
699 fDirMenuField->MenuBar()->SetMaxContentWidth(rect.Width() - 26.0f);
700 // Make room for the icon
702 fDirMenuField->MenuBar()->RemoveItem((int32)0);
703 fDirMenu->SetMenuBar(fDirMenuField->MenuBar());
704 // the above is a weird call from BDirMenu
705 // ToDo: clean up
707 BEntry entry(TargetModel()->EntryRef());
708 if (entry.InitCheck() == B_OK)
709 fDirMenu->Populate(&entry, 0, true, true, false, true);
710 else
711 fDirMenu->Populate(0, 0, true, true, false, true);
713 fBackView->AddChild(fDirMenuField);
715 // add file name text view
716 if (fIsSavePanel) {
717 BRect rect(windRect);
718 rect.top = rect.bottom - 35;
719 rect.left = 8;
720 rect.right = rect.left + 170;
721 rect.bottom = rect.top + 13;
723 fTextControl = new BTextControl(rect, "text view",
724 B_TRANSLATE("save text"), "", NULL,
725 B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
726 DisallowMetaKeys(fTextControl->TextView());
727 DisallowFilenameKeys(fTextControl->TextView());
728 fBackView->AddChild(fTextControl);
729 fTextControl->SetDivider(0.0f);
730 fTextControl->TextView()->SetMaxBytes(B_FILE_NAME_LENGTH - 1);
732 fButtonText.SetTo(B_TRANSLATE("Save"));
733 } else
734 fButtonText.SetTo(B_TRANSLATE("Open"));
736 // Add PoseView
737 PoseView()->SetName("ActualPoseView");
738 fBorderedView->SetName("PoseView");
739 fBorderedView->SetResizingMode(B_FOLLOW_ALL);
740 fBorderedView->EnableBorderHighlight(true);
742 rect = windRect;
743 rect.OffsetTo(10, fDirMenuField->Frame().bottom + 10);
744 rect.bottom = windRect.bottom - 60;
745 rect.right -= B_V_SCROLL_BAR_WIDTH + 20;
746 fBorderedView->MoveTo(rect.LeftTop());
747 fBorderedView->ResizeTo(rect.Width(), rect.Height());
749 PoseView()->AddScrollBars();
750 PoseView()->SetDragEnabled(false);
751 PoseView()->SetDropEnabled(false);
752 PoseView()->SetSelectionHandler(this);
753 PoseView()->SetSelectionChangedHook(true);
754 PoseView()->DisableSaveLocation();
756 // horizontal
757 rect = fBorderedView->Frame();
758 rect.top = rect.bottom;
759 rect.bottom = rect.top + (float)B_H_SCROLL_BAR_HEIGHT;
760 PoseView()->HScrollBar()->MoveTo(rect.LeftTop());
761 PoseView()->HScrollBar()->ResizeTo(rect.Size());
762 PoseView()->HScrollBar()->SetResizingMode(B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM);
763 fBackView->AddChild(PoseView()->HScrollBar());
765 // vertical
766 rect = fBorderedView->Frame();
767 rect.left = rect.right;
768 rect.right = rect.left + (float)B_V_SCROLL_BAR_WIDTH;
769 PoseView()->VScrollBar()->MoveTo(rect.LeftTop());
770 PoseView()->VScrollBar()->ResizeTo(rect.Size());
771 PoseView()->VScrollBar()->SetResizingMode(B_FOLLOW_TOP_BOTTOM | B_FOLLOW_RIGHT);
772 fBackView->AddChild(PoseView()->VScrollBar());
774 if (fIsSavePanel)
775 fBackView->AddChild(fBorderedView, fTextControl);
776 else
777 fBackView->AddChild(fBorderedView);
779 AddShortcut('W', B_COMMAND_KEY, new BMessage(kCancelButton));
780 AddShortcut('H', B_COMMAND_KEY, new BMessage(kSwitchToHome));
781 AddShortcut('A', B_COMMAND_KEY | B_SHIFT_KEY,
782 new BMessage(kShowSelectionWindow));
783 AddShortcut('A', B_COMMAND_KEY, new BMessage(B_SELECT_ALL), PoseView());
784 AddShortcut('S', B_COMMAND_KEY, new BMessage(kInvertSelection),
785 PoseView());
786 AddShortcut('Y', B_COMMAND_KEY, new BMessage(kResizeToFit), PoseView());
787 AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY, new BMessage(kOpenDir));
788 AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY,
789 new BMessage(kOpenDir));
790 AddShortcut(B_UP_ARROW, B_COMMAND_KEY, new BMessage(kOpenParentDir));
791 AddShortcut(B_UP_ARROW, B_COMMAND_KEY | B_OPTION_KEY,
792 new BMessage(kOpenParentDir));
794 // New code to make buttons font sensitive
795 rect = windRect;
796 rect.top = rect.bottom - 35;
797 rect.bottom -= 10;
798 rect.right -= 25;
799 float default_width
800 = be_plain_font->StringWidth(fButtonText.String()) + 20;
801 rect.left = default_width > 75
802 ? rect.right - default_width : rect.right - 75;
804 BButton* default_button = new BButton(rect, "default button",
805 fButtonText.String(), new BMessage(kDefaultButton),
806 B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM);
807 fBackView->AddChild(default_button);
809 rect.right = rect.left -= 10;
810 float cancel_width
811 = be_plain_font->StringWidth(B_TRANSLATE("Cancel")) + 20;
812 rect.left = cancel_width > 75
813 ? rect.right - cancel_width : rect.right - 75;
815 BButton* cancel_button = new BButton(rect, "cancel button",
816 B_TRANSLATE("Cancel"), new BMessage(kCancelButton),
817 B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM);
818 fBackView->AddChild(cancel_button);
820 if (!fIsSavePanel && (fNodeFlavors & B_DIRECTORY_NODE) == 0)
821 default_button->SetEnabled(false);
823 default_button->MakeDefault(true);
825 RestoreState();
827 AddMenus();
828 AddContextMenus();
830 FavoritesMenu* favorites = new FavoritesMenu(B_TRANSLATE("Favorites"),
831 new BMessage(kSwitchDirectory), new BMessage(B_REFS_RECEIVED),
832 BMessenger(this), IsSavePanel(), fPoseView->RefFilter());
833 favorites->AddItem(new BMenuItem(B_TRANSLATE("Add current folder"),
834 new BMessage(kAddCurrentDir)));
835 favorites->AddItem(new BMenuItem(
836 B_TRANSLATE("Edit favorites" B_UTF8_ELLIPSIS),
837 new BMessage(kEditFavorites)));
839 fMenuBar->AddItem(favorites);
841 // configure menus
842 BMenuItem* item = fMenuBar->FindItem(B_TRANSLATE("Window"));
843 if (item) {
844 fMenuBar->RemoveItem(item);
845 delete item;
848 item = fMenuBar->FindItem(B_TRANSLATE("File"));
849 if (item) {
850 BMenu* menu = item->Submenu();
851 if (menu) {
852 item = menu->FindItem(kOpenSelection);
853 if (item && menu->RemoveItem(item))
854 delete item;
856 item = menu->FindItem(kDuplicateSelection);
857 if (item && menu->RemoveItem(item))
858 delete item;
860 // remove add-ons menu, identifier menu, separator
861 item = menu->FindItem(B_TRANSLATE("Add-ons"));
862 if (item) {
863 int32 index = menu->IndexOf(item);
864 delete menu->RemoveItem(index);
865 delete menu->RemoveItem(--index);
866 delete menu->RemoveItem(--index);
869 // remove separator
870 item = menu->FindItem(B_CUT);
871 if (item) {
872 item = menu->ItemAt(menu->IndexOf(item)-1);
873 if (item && menu->RemoveItem(item))
874 delete item;
879 PoseView()->ScrollTo(B_ORIGIN);
880 PoseView()->UpdateScrollRange();
881 PoseView()->ScrollTo(B_ORIGIN);
883 if (fTextControl) {
884 fTextControl->MakeFocus();
885 fTextControl->TextView()->SelectAll();
886 } else
887 PoseView()->MakeFocus();
889 app_info info;
890 BString title;
891 if (be_app->GetAppInfo(&info) == B_OK) {
892 if (!gLocalizedNamePreferred
893 || BLocaleRoster::Default()->GetLocalizedFileName(
894 title, info.ref, false) != B_OK)
895 title = info.ref.name;
896 title << ": ";
898 title << fButtonText; // Open or Save
900 SetTitle(title.String());
902 SetSizeLimits(370, 10000, 200, 10000);
906 void
907 TFilePanel::RestoreState()
909 BNode defaultingNode;
910 if (DefaultStateSourceNode(kDefaultFilePanelTemplate, &defaultingNode,
911 false)) {
912 AttributeStreamFileNode streamNodeSource(&defaultingNode);
913 RestoreWindowState(&streamNodeSource);
914 PoseView()->Init(&streamNodeSource);
915 } else {
916 RestoreWindowState(NULL);
917 PoseView()->Init(NULL);
920 // Finish UI creation now that the PoseView is initialized
921 fBorderedView->GroupLayout()->AddView(0, fPoseView->TitleView());
923 BRect rect(fBorderedView->Frame());
924 rect.right = rect.left + kCountViewWidth;
925 rect.top = rect.bottom + 1;
926 rect.bottom = rect.top + PoseView()->HScrollBar()->Bounds().Height() - 1;
927 PoseView()->CountView()->MoveTo(rect.LeftTop());
928 PoseView()->CountView()->ResizeTo(rect.Size());
929 PoseView()->CountView()->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
930 fBackView->AddChild(PoseView()->CountView(), fBorderedView);
932 PoseView()->HScrollBar()->MoveBy(kCountViewWidth + 1, 0);
933 PoseView()->HScrollBar()->ResizeBy(-kCountViewWidth - 1, 0);
935 // The Be Book states that the BTitleView will have a name of "TitleView",
936 // and so some apps will try to grab it by that name and move it around.
937 // They don't need to, because resizing "PoseView" (really the BorderedView)
938 // will resize the BTitleView as well. So just create a dummy view here
939 // so that they don't get NULL when trying to find the view.
940 fPoseView->TitleView()->SetName("ActualTitleView");
941 BView* dummyTitleView = new BView(BRect(), "TitleView", B_FOLLOW_NONE, 0);
942 fBackView->AddChild(dummyTitleView);
943 dummyTitleView->Hide();
947 void
948 TFilePanel::SaveState(bool)
950 BNode defaultingNode;
951 if (DefaultStateSourceNode(kDefaultFilePanelTemplate, &defaultingNode,
952 true, false)) {
953 AttributeStreamFileNode streamNodeDestination(&defaultingNode);
954 SaveWindowState(&streamNodeDestination);
955 PoseView()->SaveState(&streamNodeDestination);
960 void
961 TFilePanel::SaveState(BMessage &message) const
963 _inherited::SaveState(message);
967 void
968 TFilePanel::RestoreWindowState(AttributeStreamNode* node)
970 SetSizeLimits(360, 10000, 200, 10000);
971 if (!node)
972 return;
974 const char* rectAttributeName = kAttrWindowFrame;
975 BRect frame(Frame());
976 if (node->Read(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame)
977 == sizeof(BRect)) {
978 MoveTo(frame.LeftTop());
979 ResizeTo(frame.Width(), frame.Height());
984 void
985 TFilePanel::RestoreState(const BMessage &message)
987 _inherited::RestoreState(message);
991 void
992 TFilePanel::RestoreWindowState(const BMessage &message)
994 _inherited::RestoreWindowState(message);
998 void
999 TFilePanel::AddFileContextMenus(BMenu* menu)
1001 menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
1002 new BMessage(kGetInfo), 'I'));
1003 menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"),
1004 new BMessage(kEditItem), 'E'));
1005 menu->AddItem(new BMenuItem(TrackerSettings().DontMoveFilesToTrash()
1006 ? B_TRANSLATE("Delete")
1007 : B_TRANSLATE("Move to Trash"),
1008 new BMessage(kMoveToTrash), 'T'));
1009 menu->AddSeparatorItem();
1010 menu->AddItem(new BMenuItem(B_TRANSLATE("Cut"),
1011 new BMessage(B_CUT), 'X'));
1012 menu->AddItem(new BMenuItem(B_TRANSLATE("Copy"),
1013 new BMessage(B_COPY), 'C'));
1014 //menu->AddItem(pasteItem = new BMenuItem("Paste", new BMessage(B_PASTE),
1015 // 'V'));
1017 menu->SetTargetForItems(PoseView());
1021 void
1022 TFilePanel::AddVolumeContextMenus(BMenu* menu)
1024 menu->AddItem(new BMenuItem(B_TRANSLATE("Open"),
1025 new BMessage(kOpenSelection), 'O'));
1026 menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"),
1027 new BMessage(kGetInfo), 'I'));
1028 menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"),
1029 new BMessage(kEditItem), 'E'));
1030 menu->AddSeparatorItem();
1031 menu->AddItem(new BMenuItem(B_TRANSLATE("Cut"), new BMessage(B_CUT),
1032 'X'));
1033 menu->AddItem(new BMenuItem(B_TRANSLATE("Copy"),
1034 new BMessage(B_COPY), 'C'));
1035 //menu->AddItem(pasteItem = new BMenuItem("Paste", new BMessage(B_PASTE),
1036 // 'V'));
1038 menu->SetTargetForItems(PoseView());
1042 void
1043 TFilePanel::AddWindowContextMenus(BMenu* menu)
1045 BMenuItem* item = new BMenuItem(B_TRANSLATE("New folder"),
1046 new BMessage(kNewFolder), 'N');
1047 item->SetTarget(PoseView());
1048 menu->AddItem(item);
1049 menu->AddSeparatorItem();
1051 item = new BMenuItem(B_TRANSLATE("Paste"), new BMessage(B_PASTE), 'V');
1052 item->SetTarget(PoseView());
1053 menu->AddItem(item);
1054 menu->AddSeparatorItem();
1056 item = new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS),
1057 new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY);
1058 item->SetTarget(PoseView());
1059 menu->AddItem(item);
1061 item = new BMenuItem(B_TRANSLATE("Select all"),
1062 new BMessage(B_SELECT_ALL), 'A');
1063 item->SetTarget(PoseView());
1064 menu->AddItem(item);
1066 item = new BMenuItem(B_TRANSLATE("Invert selection"),
1067 new BMessage(kInvertSelection), 'S');
1068 item->SetTarget(PoseView());
1069 menu->AddItem(item);
1071 item = new BMenuItem(B_TRANSLATE("Go to parent"),
1072 new BMessage(kOpenParentDir), B_UP_ARROW);
1073 item->SetTarget(this);
1074 menu->AddItem(item);
1078 void
1079 TFilePanel::AddDropContextMenus(BMenu*)
1084 void
1085 TFilePanel::MenusBeginning()
1087 int32 count = PoseView()->SelectionList()->CountItems();
1089 EnableNamedMenuItem(fMenuBar, kNewFolder, !TargetModel()->IsRoot());
1090 EnableNamedMenuItem(fMenuBar, kMoveToTrash, !TargetModel()->IsRoot()
1091 && count);
1092 EnableNamedMenuItem(fMenuBar, kGetInfo, count != 0);
1093 EnableNamedMenuItem(fMenuBar, kEditItem, count == 1);
1095 SetCutItem(fMenuBar);
1096 SetCopyItem(fMenuBar);
1097 SetPasteItem(fMenuBar);
1099 fIsTrackingMenu = true;
1103 void
1104 TFilePanel::MenusEnded()
1106 fIsTrackingMenu = false;
1110 void
1111 TFilePanel::ShowContextMenu(BPoint point, const entry_ref* ref, BView* view)
1113 EnableNamedMenuItem(fWindowContextMenu, kNewFolder,
1114 !TargetModel()->IsRoot());
1115 EnableNamedMenuItem(fWindowContextMenu, kOpenParentDir,
1116 !TargetModel()->IsRoot());
1117 EnableNamedMenuItem(fWindowContextMenu, kMoveToTrash,
1118 !TargetModel()->IsRoot());
1120 _inherited::ShowContextMenu(point, ref, view);
1124 void
1125 TFilePanel::SetupNavigationMenu(const entry_ref*, BMenu*)
1127 // do nothing here so nav menu doesn't get added
1131 void
1132 TFilePanel::SetButtonLabel(file_panel_button selector, const char* text)
1134 switch (selector) {
1135 case B_CANCEL_BUTTON:
1137 BButton* button
1138 = dynamic_cast<BButton*>(FindView("cancel button"));
1139 if (button == NULL)
1140 break;
1142 float old_width = button->StringWidth(button->Label());
1143 button->SetLabel(text);
1144 float delta = old_width - button->StringWidth(text);
1145 if (delta) {
1146 button->MoveBy(delta, 0);
1147 button->ResizeBy(-delta, 0);
1150 break;
1152 case B_DEFAULT_BUTTON:
1154 fButtonText = text;
1155 float delta = 0;
1156 BButton* button
1157 = dynamic_cast<BButton*>(FindView("default button"));
1158 if (button != NULL) {
1159 float old_width = button->StringWidth(button->Label());
1160 button->SetLabel(text);
1161 delta = old_width - button->StringWidth(text);
1162 if (delta) {
1163 button->MoveBy(delta, 0);
1164 button->ResizeBy(-delta, 0);
1168 // now must move cancel button
1169 button = dynamic_cast<BButton*>(FindView("cancel button"));
1170 if (button != NULL)
1171 button->MoveBy(delta, 0);
1173 break;
1178 void
1179 TFilePanel::SetSaveText(const char* text)
1181 if (text == NULL)
1182 return;
1184 BTextControl* textControl
1185 = dynamic_cast<BTextControl*>(FindView("text view"));
1186 if (textControl != NULL) {
1187 textControl->SetText(text);
1188 if (textControl->TextView() != NULL)
1189 textControl->TextView()->SelectAll();
1194 void
1195 TFilePanel::MessageReceived(BMessage* message)
1197 entry_ref ref;
1199 switch (message->what) {
1200 case B_REFS_RECEIVED:
1201 // item was double clicked in file panel (PoseView)
1202 if (message->FindRef("refs", &ref) == B_OK) {
1203 BEntry entry(&ref, true);
1204 if (entry.InitCheck() == B_OK) {
1205 // Double-click on dir or link-to-dir ALWAYS opens the
1206 // dir. If more than one dir is selected, the first is
1207 // entered.
1208 if (entry.IsDirectory()) {
1209 entry.GetRef(&ref);
1210 bool isDesktop = SwitchDirToDesktopIfNeeded(ref);
1212 PoseView()->SetIsDesktop(isDesktop);
1213 entry.SetTo(&ref);
1214 PoseView()->SwitchDir(&ref);
1215 SwitchDirMenuTo(&ref);
1216 } else {
1217 // Otherwise, we have a file or a link to a file.
1218 // AdjustButton has already tested the flavor;
1219 // all we have to do is see if the button is enabled.
1220 BButton* button = dynamic_cast<BButton*>(
1221 FindView("default button"));
1222 if (button == NULL)
1223 break;
1225 if (IsSavePanel()) {
1226 int32 count = 0;
1227 type_code type;
1228 message->GetInfo("refs", &type, &count);
1230 // Don't allow saves of multiple files
1231 if (count > 1) {
1232 ShowCenteredAlert(
1233 B_TRANSLATE(
1234 "Sorry, saving more than one "
1235 "item is not allowed."),
1236 B_TRANSLATE("Cancel"));
1237 } else {
1238 // if we are a savepanel, set up the
1239 // filepanel correctly then pass control
1240 // so we follow the same path as if the user
1241 // clicked the save button
1243 // set the 'name' fld to the current ref's
1244 // name notify the panel that the default
1245 // button should be enabled
1246 SetSaveText(ref.name);
1247 SelectionChanged();
1249 HandleSaveButton();
1251 break;
1254 // send handler a message and close
1255 BMessage openMessage(*fMessage);
1256 for (int32 index = 0; ; index++) {
1257 if (message->FindRef("refs", index, &ref) != B_OK)
1258 break;
1259 openMessage.AddRef("refs", &ref);
1261 OpenSelectionCommon(&openMessage);
1265 break;
1267 case kSwitchDirectory:
1269 entry_ref ref;
1270 // this comes from dir menu or nav menu, so switch directories
1271 if (message->FindRef("refs", &ref) == B_OK) {
1272 BEntry entry(&ref, true);
1273 if (entry.GetRef(&ref) == B_OK)
1274 SetTo(&ref);
1276 break;
1279 case kSwitchToHome:
1281 BPath homePath;
1282 entry_ref ref;
1283 if (find_directory(B_USER_DIRECTORY, &homePath) != B_OK
1284 || get_ref_for_path(homePath.Path(), &ref) != B_OK) {
1285 break;
1288 SetTo(&ref);
1289 break;
1292 case kAddCurrentDir:
1294 BPath path;
1295 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true)
1296 != B_OK) {
1297 break;
1300 path.Append(kGoDirectory);
1301 BDirectory goDirectory(path.Path());
1303 if (goDirectory.InitCheck() == B_OK) {
1304 BEntry entry(TargetModel()->EntryRef());
1305 entry.GetPath(&path);
1307 BSymLink link;
1308 goDirectory.CreateSymLink(TargetModel()->Name(), path.Path(),
1309 &link);
1311 break;
1314 case kEditFavorites:
1316 BPath path;
1317 if (find_directory (B_USER_SETTINGS_DIRECTORY, &path, true)
1318 != B_OK) {
1319 break;
1322 path.Append(kGoDirectory);
1323 BMessenger msgr(kTrackerSignature);
1324 if (msgr.IsValid()) {
1325 BMessage message(B_REFS_RECEIVED);
1326 entry_ref ref;
1327 if (get_ref_for_path(path.Path(), &ref) == B_OK) {
1328 message.AddRef("refs", &ref);
1329 msgr.SendMessage(&message);
1332 break;
1335 case kCancelButton:
1336 PostMessage(B_QUIT_REQUESTED);
1337 break;
1339 case kResizeToFit:
1340 ResizeToFit();
1341 break;
1343 case kOpenDir:
1344 OpenDirectory();
1345 break;
1347 case kOpenParentDir:
1348 OpenParent();
1349 break;
1351 case kDefaultButton:
1352 if (fIsSavePanel) {
1353 if (PoseView()->IsFocus()
1354 && PoseView()->SelectionList()->CountItems() == 1) {
1355 Model* model = (PoseView()->SelectionList()->
1356 FirstItem())->TargetModel();
1357 if (model->ResolveIfLink()->IsDirectory()) {
1358 PoseView()->CommitActivePose();
1359 PoseView()->OpenSelection();
1360 break;
1364 HandleSaveButton();
1365 } else
1366 HandleOpenButton();
1367 break;
1369 case B_OBSERVER_NOTICE_CHANGE:
1371 int32 observerWhat;
1372 if (message->FindInt32("be:observe_change_what", &observerWhat)
1373 == B_OK) {
1374 switch (observerWhat) {
1375 case kDesktopFilePanelRootChanged:
1377 bool desktopIsRoot = true;
1378 if (message->FindBool("DesktopFilePanelRoot",
1379 &desktopIsRoot) == B_OK) {
1380 TrackerSettings().
1381 SetDesktopFilePanelRoot(desktopIsRoot);
1383 SetTo(TargetModel()->EntryRef());
1384 break;
1388 break;
1391 default:
1392 _inherited::MessageReceived(message);
1393 break;
1398 void
1399 TFilePanel::OpenDirectory()
1401 BObjectList<BPose>* list = PoseView()->SelectionList();
1402 if (list->CountItems() != 1)
1403 return;
1405 Model* model = list->FirstItem()->TargetModel();
1406 if (model->ResolveIfLink()->IsDirectory()) {
1407 BMessage message(B_REFS_RECEIVED);
1408 message.AddRef("refs", model->EntryRef());
1409 PostMessage(&message);
1414 void
1415 TFilePanel::OpenParent()
1417 if (!CanOpenParent())
1418 return;
1420 BEntry parentEntry;
1421 BDirectory dir;
1423 Model oldModel(*PoseView()->TargetModel());
1424 BEntry entry(oldModel.EntryRef());
1426 if (entry.InitCheck() == B_OK
1427 && entry.GetParent(&dir) == B_OK
1428 && dir.GetEntry(&parentEntry) == B_OK
1429 && entry != parentEntry) {
1431 entry_ref ref;
1432 parentEntry.GetRef(&ref);
1434 PoseView()->SetIsDesktop(SwitchDirToDesktopIfNeeded(ref));
1435 PoseView()->SwitchDir(&ref);
1436 SwitchDirMenuTo(&ref);
1438 // make sure the child get's selected in the new view once it
1439 // shows up
1440 fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
1441 (&TFilePanel::SelectChildInParent, this,
1442 const_cast<const entry_ref*>(&ref),
1443 oldModel.NodeRef()), 100000, 200000, 5000000);
1448 bool
1449 TFilePanel::CanOpenParent() const
1451 if (TrackerSettings().DesktopFilePanelRoot()) {
1452 // don't allow opening Desktop folder's parent
1453 if (TargetModel()->IsDesktop())
1454 return false;
1457 // block on "/"
1458 BEntry root("/");
1459 node_ref rootRef;
1460 root.GetNodeRef(&rootRef);
1462 return rootRef != *TargetModel()->NodeRef();
1466 bool
1467 TFilePanel::SwitchDirToDesktopIfNeeded(entry_ref &ref)
1469 // support showing Desktop as root of everything
1470 // This call implements the worm hole that maps Desktop as
1471 // a root above the disks
1472 TrackerSettings settings;
1473 if (!settings.DesktopFilePanelRoot())
1474 // Tracker isn't set up that way, just let Disks show
1475 return false;
1477 BEntry entry(&ref);
1478 BEntry root("/");
1480 BDirectory desktopDir;
1481 FSGetDeskDir(&desktopDir);
1482 if (FSIsDeskDir(&entry)
1483 // navigated into non-boot desktop, switch to boot desktop
1484 || (entry == root && !settings.ShowDisksIcon())) {
1485 // hit "/" level, map to desktop
1487 desktopDir.GetEntry(&entry);
1488 entry.GetRef(&ref);
1489 return true;
1491 return FSIsDeskDir(&entry);
1495 bool
1496 TFilePanel::SelectChildInParent(const entry_ref*, const node_ref* child)
1498 AutoLock<TFilePanel> lock(this);
1500 if (!IsLocked())
1501 return false;
1503 int32 index;
1504 BPose* pose = PoseView()->FindPose(child, &index);
1505 if (!pose)
1506 return false;
1508 PoseView()->UpdateScrollRange();
1509 // ToDo: Scroll range should be updated by now, for some
1510 // reason sometimes it is not right, force it here
1511 PoseView()->SelectPose(pose, index, true);
1512 return true;
1516 int32
1517 TFilePanel::ShowCenteredAlert(const char* text, const char* button1,
1518 const char* button2, const char* button3)
1520 BAlert* alert = new BAlert("", text, button1, button2, button3,
1521 B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1522 alert->MoveTo(Frame().left + 10, Frame().top + 10);
1524 #if 0
1525 if (button1 != NULL && !strncmp(button1, "Cancel", 7))
1526 alert->SetShortcut(0, B_ESCAPE);
1527 else if (button2 != NULL && !strncmp(button2, "Cancel", 7))
1528 alert->SetShortcut(1, B_ESCAPE);
1529 else if (button3 != NULL && !strncmp(button3, "Cancel", 7))
1530 alert->SetShortcut(2, B_ESCAPE);
1531 #endif
1533 return alert->Go();
1537 void
1538 TFilePanel::HandleSaveButton()
1540 BDirectory dir;
1542 if (TargetModel()->IsRoot()) {
1543 ShowCenteredAlert(
1544 B_TRANSLATE("Sorry, you can't save things at the root of "
1545 "your system."),
1546 B_TRANSLATE("Cancel"));
1547 return;
1550 // check for some illegal file names
1551 if (strcmp(fTextControl->Text(), ".") == 0
1552 || strcmp(fTextControl->Text(), "..") == 0) {
1553 ShowCenteredAlert(
1554 B_TRANSLATE("The specified name is illegal. Please choose "
1555 "another name."),
1556 B_TRANSLATE("Cancel"));
1557 fTextControl->TextView()->SelectAll();
1558 return;
1561 if (dir.SetTo(TargetModel()->EntryRef()) != B_OK) {
1562 ShowCenteredAlert(
1563 B_TRANSLATE("There was a problem trying to save in the folder "
1564 "you specified. Please try another one."),
1565 B_TRANSLATE("Cancel"));
1566 return;
1569 if (dir.Contains(fTextControl->Text())) {
1570 if (dir.Contains(fTextControl->Text(), B_DIRECTORY_NODE)) {
1571 ShowCenteredAlert(
1572 B_TRANSLATE("The specified name is already used as the name "
1573 "of a folder. Please choose another name."),
1574 B_TRANSLATE("Cancel"));
1575 fTextControl->TextView()->SelectAll();
1576 return;
1577 } else {
1578 // if this was invoked by a dbl click, it is an explicit
1579 // replacement of the file.
1580 BString str(B_TRANSLATE("The file \"%name\" already exists in "
1581 "the specified folder. Do you want to replace it?"));
1582 str.ReplaceFirst("%name", fTextControl->Text());
1584 if (ShowCenteredAlert(str.String(), B_TRANSLATE("Cancel"),
1585 B_TRANSLATE("Replace")) == 0) {
1586 // user canceled
1587 fTextControl->TextView()->SelectAll();
1588 return;
1590 // user selected "Replace" - let app deal with it
1594 BMessage message(*fMessage);
1595 message.AddRef("directory", TargetModel()->EntryRef());
1596 message.AddString("name", fTextControl->Text());
1598 if (fClientObject)
1599 fClientObject->SendMessage(&fTarget, &message);
1600 else
1601 fTarget.SendMessage(&message);
1603 // close window if we're dealing with standard message
1604 if (fHideWhenDone)
1605 PostMessage(B_QUIT_REQUESTED);
1609 void
1610 TFilePanel::OpenSelectionCommon(BMessage* openMessage)
1612 if (!openMessage->HasRef("refs"))
1613 return;
1615 for (int32 index = 0; ; index++) {
1616 entry_ref ref;
1617 if (openMessage->FindRef("refs", index, &ref) != B_OK)
1618 break;
1620 BEntry entry(&ref, true);
1621 if (entry.InitCheck() == B_OK) {
1622 if (entry.IsDirectory())
1623 BRoster().AddToRecentFolders(&ref);
1624 else
1625 BRoster().AddToRecentDocuments(&ref);
1629 BRoster().AddToRecentFolders(TargetModel()->EntryRef());
1631 if (fClientObject)
1632 fClientObject->SendMessage(&fTarget, openMessage);
1633 else
1634 fTarget.SendMessage(openMessage);
1636 // close window if we're dealing with standard message
1637 if (fHideWhenDone)
1638 PostMessage(B_QUIT_REQUESTED);
1642 void
1643 TFilePanel::HandleOpenButton()
1645 PoseView()->CommitActivePose();
1646 BObjectList<BPose>* selection = PoseView()->SelectionList();
1648 // if we have only one directory and we're not opening dirs, enter.
1649 if ((fNodeFlavors & B_DIRECTORY_NODE) == 0
1650 && selection->CountItems() == 1) {
1651 Model* model = selection->FirstItem()->TargetModel();
1653 if (model->IsDirectory()
1654 || (model->IsSymLink() && !(fNodeFlavors & B_SYMLINK_NODE)
1655 && model->ResolveIfLink()->IsDirectory())) {
1657 BMessage message(B_REFS_RECEIVED);
1658 message.AddRef("refs", model->EntryRef());
1659 PostMessage(&message);
1660 return;
1664 if (selection->CountItems()) {
1665 // there are items selected
1666 // message->fMessage->message from here to end
1667 BMessage message(*fMessage);
1668 // go through selection and add appropriate items
1669 for (int32 index = 0; index < selection->CountItems(); index++) {
1670 Model* model = selection->ItemAt(index)->TargetModel();
1672 if (((fNodeFlavors & B_DIRECTORY_NODE) != 0
1673 && model->ResolveIfLink()->IsDirectory())
1674 || ((fNodeFlavors & B_SYMLINK_NODE) != 0 && model->IsSymLink())
1675 || ((fNodeFlavors & B_FILE_NODE) != 0
1676 && model->ResolveIfLink()->IsFile())) {
1677 message.AddRef("refs", model->EntryRef());
1681 OpenSelectionCommon(&message);
1682 } else if (IsOpenButtonAlwaysEnabled()) {
1683 BMessage message(*fMessage);
1684 message.AddRef("refs", TargetModel()->EntryRef());
1685 OpenSelectionCommon(&message);
1690 void
1691 TFilePanel::SwitchDirMenuTo(const entry_ref* ref)
1693 BEntry entry(ref);
1694 for (int32 index = fDirMenu->CountItems() - 1; index >= 0; index--)
1695 delete fDirMenu->RemoveItem(index);
1697 fDirMenuField->MenuBar()->RemoveItem((int32)0);
1698 fDirMenu->Populate(&entry, 0, true, true, false, true);
1700 ModelMenuItem* item = dynamic_cast<ModelMenuItem*>(
1701 fDirMenuField->MenuBar()->ItemAt(0));
1702 ASSERT(item != NULL);
1704 if (item != NULL)
1705 item->SetEntry(&entry);
1709 void
1710 TFilePanel::WindowActivated(bool active)
1712 // force focus to update properly
1713 fBackView->Invalidate();
1714 _inherited::WindowActivated(active);
1718 // #pragma mark -
1721 BFilePanelPoseView::BFilePanelPoseView(Model* model)
1723 BPoseView(model, kListMode),
1724 fIsDesktop(model->IsDesktop())
1729 void
1730 BFilePanelPoseView::StartWatching()
1732 TTracker::WatchNode(0, B_WATCH_MOUNT, this);
1734 // inter-application observing
1735 BMessenger tracker(kTrackerSignature);
1736 BHandler::StartWatching(tracker, kVolumesOnDesktopChanged);
1740 void
1741 BFilePanelPoseView::StopWatching()
1743 stop_watching(this);
1745 // inter-application observing
1746 BMessenger tracker(kTrackerSignature);
1747 BHandler::StopWatching(tracker, kVolumesOnDesktopChanged);
1751 bool
1752 BFilePanelPoseView::FSNotification(const BMessage* message)
1754 switch (message->FindInt32("opcode")) {
1755 case B_DEVICE_MOUNTED:
1757 if (IsDesktopView()) {
1758 // Pretty much copied straight from DesktopPoseView.
1759 // Would be better if the code could be shared somehow.
1760 dev_t device;
1761 if (message->FindInt32("new device", &device) != B_OK)
1762 break;
1764 ASSERT(TargetModel() != NULL);
1765 TrackerSettings settings;
1767 BVolume volume(device);
1768 if (volume.InitCheck() != B_OK)
1769 break;
1771 if (settings.MountVolumesOntoDesktop()
1772 && (!volume.IsShared()
1773 || settings.MountSharedVolumesOntoDesktop())) {
1774 // place an icon for the volume onto the desktop
1775 CreateVolumePose(&volume, true);
1778 break;
1781 case B_DEVICE_UNMOUNTED:
1783 dev_t device;
1784 if (message->FindInt32("device", &device) == B_OK) {
1785 if (TargetModel() != NULL
1786 && TargetModel()->NodeRef()->device == device) {
1787 // Volume currently shown in this file panel
1788 // disappeared, reset location to home directory
1789 BMessage message(kSwitchToHome);
1790 MessageReceived(&message);
1793 break;
1796 return _inherited::FSNotification(message);
1800 void
1801 BFilePanelPoseView::RestoreState(AttributeStreamNode* node)
1803 _inherited::RestoreState(node);
1804 fViewState->SetViewMode(kListMode);
1808 void
1809 BFilePanelPoseView::RestoreState(const BMessage &message)
1811 _inherited::RestoreState(message);
1815 void
1816 BFilePanelPoseView::SavePoseLocations(BRect*)
1821 EntryListBase*
1822 BFilePanelPoseView::InitDirentIterator(const entry_ref* ref)
1824 if (IsDesktopView())
1825 return DesktopPoseView::InitDesktopDirentIterator(this, ref);
1827 return _inherited::InitDirentIterator(ref);
1831 void
1832 BFilePanelPoseView::AddPosesCompleted()
1834 _inherited::AddPosesCompleted();
1835 if (IsDesktopView())
1836 CreateTrashPose();
1840 void
1841 BFilePanelPoseView::SetIsDesktop(bool on)
1843 fIsDesktop = on;
1847 bool
1848 BFilePanelPoseView::IsDesktopView() const
1850 return fIsDesktop;
1854 void
1855 BFilePanelPoseView::ShowVolumes(bool visible, bool showShared)
1857 if (IsDesktopView()) {
1858 if (!visible)
1859 RemoveRootPoses();
1860 else
1861 AddRootPoses(true, showShared);
1864 TFilePanel* filepanel = dynamic_cast<TFilePanel*>(Window());
1865 if (filepanel != NULL && TargetModel() != NULL)
1866 filepanel->SetTo(TargetModel()->EntryRef());
1870 void
1871 BFilePanelPoseView::AdaptToVolumeChange(BMessage* message)
1873 bool showDisksIcon;
1874 bool mountVolumesOnDesktop;
1875 bool mountSharedVolumesOntoDesktop;
1877 message->FindBool("ShowDisksIcon", &showDisksIcon);
1878 message->FindBool("MountVolumesOntoDesktop", &mountVolumesOnDesktop);
1879 message->FindBool("MountSharedVolumesOntoDesktop",
1880 &mountSharedVolumesOntoDesktop);
1882 BEntry entry("/");
1883 Model model(&entry);
1884 if (model.InitCheck() == B_OK) {
1885 BMessage monitorMsg;
1886 monitorMsg.what = B_NODE_MONITOR;
1888 if (showDisksIcon)
1889 monitorMsg.AddInt32("opcode", B_ENTRY_CREATED);
1890 else
1891 monitorMsg.AddInt32("opcode", B_ENTRY_REMOVED);
1893 monitorMsg.AddInt32("device", model.NodeRef()->device);
1894 monitorMsg.AddInt64("node", model.NodeRef()->node);
1895 monitorMsg.AddInt64("directory", model.EntryRef()->directory);
1896 monitorMsg.AddString("name", model.EntryRef()->name);
1897 TrackerSettings().SetShowDisksIcon(showDisksIcon);
1898 if (Window())
1899 Window()->PostMessage(&monitorMsg, this);
1902 ShowVolumes(mountVolumesOnDesktop, mountSharedVolumesOntoDesktop);
1906 void
1907 BFilePanelPoseView::AdaptToDesktopIntegrationChange(BMessage* message)
1909 bool mountVolumesOnDesktop = true;
1910 bool mountSharedVolumesOntoDesktop = true;
1912 message->FindBool("MountVolumesOntoDesktop", &mountVolumesOnDesktop);
1913 message->FindBool("MountSharedVolumesOntoDesktop",
1914 &mountSharedVolumesOntoDesktop);
1916 ShowVolumes(false, mountSharedVolumesOntoDesktop);
1917 ShowVolumes(mountVolumesOnDesktop, mountSharedVolumesOntoDesktop);