1 package ch
.cyberduck
.ui
.cocoa
;
4 * Copyright (c) 2005 David Kocher. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * Bug fixes, suggestions and comments should be sent to:
18 * dkocher@cyberduck.ch
21 import ch
.cyberduck
.binding
.ProxyController
;
22 import ch
.cyberduck
.binding
.application
.*;
23 import ch
.cyberduck
.binding
.foundation
.NSArray
;
24 import ch
.cyberduck
.binding
.foundation
.NSAttributedString
;
25 import ch
.cyberduck
.binding
.foundation
.NSDictionary
;
26 import ch
.cyberduck
.binding
.foundation
.NSEnumerator
;
27 import ch
.cyberduck
.binding
.foundation
.NSIndexSet
;
28 import ch
.cyberduck
.binding
.foundation
.NSNotification
;
29 import ch
.cyberduck
.binding
.foundation
.NSNotificationCenter
;
30 import ch
.cyberduck
.binding
.foundation
.NSObject
;
31 import ch
.cyberduck
.binding
.foundation
.NSRange
;
32 import ch
.cyberduck
.binding
.foundation
.NSString
;
33 import ch
.cyberduck
.core
.*;
34 import ch
.cyberduck
.core
.aquaticprime
.LicenseFactory
;
35 import ch
.cyberduck
.core
.bonjour
.RendezvousCollection
;
36 import ch
.cyberduck
.core
.editor
.DefaultEditorListener
;
37 import ch
.cyberduck
.core
.editor
.Editor
;
38 import ch
.cyberduck
.core
.editor
.EditorFactory
;
39 import ch
.cyberduck
.core
.exception
.AccessDeniedException
;
40 import ch
.cyberduck
.core
.exception
.BackgroundException
;
41 import ch
.cyberduck
.core
.exception
.ConnectionCanceledException
;
42 import ch
.cyberduck
.core
.features
.Command
;
43 import ch
.cyberduck
.core
.features
.Compress
;
44 import ch
.cyberduck
.core
.features
.Location
;
45 import ch
.cyberduck
.core
.features
.Move
;
46 import ch
.cyberduck
.core
.features
.Symlink
;
47 import ch
.cyberduck
.core
.features
.Touch
;
48 import ch
.cyberduck
.core
.features
.Versioning
;
49 import ch
.cyberduck
.core
.local
.Application
;
50 import ch
.cyberduck
.core
.local
.ApplicationFinder
;
51 import ch
.cyberduck
.core
.local
.ApplicationFinderFactory
;
52 import ch
.cyberduck
.core
.local
.ApplicationQuitCallback
;
53 import ch
.cyberduck
.core
.local
.BrowserLauncherFactory
;
54 import ch
.cyberduck
.core
.local
.TemporaryFileServiceFactory
;
55 import ch
.cyberduck
.core
.pasteboard
.HostPasteboard
;
56 import ch
.cyberduck
.core
.pasteboard
.PathPasteboard
;
57 import ch
.cyberduck
.core
.pasteboard
.PathPasteboardFactory
;
58 import ch
.cyberduck
.core
.preferences
.Preferences
;
59 import ch
.cyberduck
.core
.preferences
.PreferencesFactory
;
60 import ch
.cyberduck
.core
.resources
.IconCacheFactory
;
61 import ch
.cyberduck
.core
.serializer
.HostDictionary
;
62 import ch
.cyberduck
.core
.sftp
.SFTPSession
;
63 import ch
.cyberduck
.core
.ssl
.DefaultTrustManagerHostnameCallback
;
64 import ch
.cyberduck
.core
.ssl
.KeychainX509KeyManager
;
65 import ch
.cyberduck
.core
.ssl
.KeychainX509TrustManager
;
66 import ch
.cyberduck
.core
.ssl
.SSLSession
;
67 import ch
.cyberduck
.core
.threading
.BackgroundAction
;
68 import ch
.cyberduck
.core
.threading
.DefaultMainAction
;
69 import ch
.cyberduck
.core
.threading
.MainAction
;
70 import ch
.cyberduck
.core
.threading
.TransferBackgroundAction
;
71 import ch
.cyberduck
.core
.threading
.WorkerBackgroundAction
;
72 import ch
.cyberduck
.core
.transfer
.CopyTransfer
;
73 import ch
.cyberduck
.core
.transfer
.DisabledTransferErrorCallback
;
74 import ch
.cyberduck
.core
.transfer
.DownloadTransfer
;
75 import ch
.cyberduck
.core
.transfer
.SyncTransfer
;
76 import ch
.cyberduck
.core
.transfer
.Transfer
;
77 import ch
.cyberduck
.core
.transfer
.TransferAction
;
78 import ch
.cyberduck
.core
.transfer
.TransferAdapter
;
79 import ch
.cyberduck
.core
.transfer
.TransferCallback
;
80 import ch
.cyberduck
.core
.transfer
.TransferItem
;
81 import ch
.cyberduck
.core
.transfer
.TransferOptions
;
82 import ch
.cyberduck
.core
.transfer
.TransferProgress
;
83 import ch
.cyberduck
.core
.transfer
.TransferPrompt
;
84 import ch
.cyberduck
.core
.transfer
.UploadTransfer
;
85 import ch
.cyberduck
.core
.urlhandler
.SchemeHandlerFactory
;
86 import ch
.cyberduck
.core
.worker
.DeleteWorker
;
87 import ch
.cyberduck
.core
.worker
.DisconnectWorker
;
88 import ch
.cyberduck
.core
.worker
.MountWorker
;
89 import ch
.cyberduck
.core
.worker
.MoveWorker
;
90 import ch
.cyberduck
.core
.worker
.RevertWorker
;
91 import ch
.cyberduck
.core
.worker
.SessionListWorker
;
92 import ch
.cyberduck
.ui
.browser
.Column
;
93 import ch
.cyberduck
.ui
.browser
.DownloadDirectoryFinder
;
94 import ch
.cyberduck
.ui
.browser
.PathReloadFinder
;
95 import ch
.cyberduck
.ui
.browser
.RegexFilter
;
96 import ch
.cyberduck
.ui
.browser
.SearchFilter
;
97 import ch
.cyberduck
.ui
.browser
.UploadDirectoryFinder
;
98 import ch
.cyberduck
.ui
.browser
.UploadTargetFinder
;
99 import ch
.cyberduck
.ui
.cocoa
.delegate
.ArchiveMenuDelegate
;
100 import ch
.cyberduck
.ui
.cocoa
.delegate
.CopyURLMenuDelegate
;
101 import ch
.cyberduck
.ui
.cocoa
.delegate
.EditMenuDelegate
;
102 import ch
.cyberduck
.ui
.cocoa
.delegate
.OpenURLMenuDelegate
;
103 import ch
.cyberduck
.ui
.cocoa
.delegate
.URLMenuDelegate
;
104 import ch
.cyberduck
.ui
.cocoa
.quicklook
.QLPreviewPanel
;
105 import ch
.cyberduck
.ui
.cocoa
.quicklook
.QLPreviewPanelController
;
106 import ch
.cyberduck
.ui
.cocoa
.quicklook
.QuickLook
;
107 import ch
.cyberduck
.ui
.cocoa
.quicklook
.QuickLookFactory
;
108 import ch
.cyberduck
.ui
.cocoa
.threading
.BrowserControllerBackgroundAction
;
109 import ch
.cyberduck
.ui
.cocoa
.threading
.WindowMainAction
;
110 import ch
.cyberduck
.ui
.cocoa
.view
.BookmarkCell
;
111 import ch
.cyberduck
.ui
.cocoa
.view
.OutlineCell
;
113 import org
.apache
.commons
.collections
.CollectionUtils
;
114 import org
.apache
.commons
.lang3
.StringUtils
;
115 import org
.apache
.log4j
.Logger
;
116 import org
.rococoa
.Foundation
;
117 import org
.rococoa
.ID
;
118 import org
.rococoa
.Rococoa
;
119 import org
.rococoa
.Selector
;
120 import org
.rococoa
.cocoa
.CGFloat
;
121 import org
.rococoa
.cocoa
.foundation
.NSInteger
;
122 import org
.rococoa
.cocoa
.foundation
.NSPoint
;
123 import org
.rococoa
.cocoa
.foundation
.NSRect
;
124 import org
.rococoa
.cocoa
.foundation
.NSSize
;
125 import org
.rococoa
.cocoa
.foundation
.NSUInteger
;
127 import java
.security
.cert
.CertificateException
;
128 import java
.security
.cert
.X509Certificate
;
129 import java
.text
.MessageFormat
;
130 import java
.util
.ArrayList
;
131 import java
.util
.Collections
;
132 import java
.util
.Comparator
;
133 import java
.util
.EnumSet
;
134 import java
.util
.HashMap
;
135 import java
.util
.HashSet
;
136 import java
.util
.Iterator
;
137 import java
.util
.List
;
138 import java
.util
.Locale
;
139 import java
.util
.Map
;
140 import java
.util
.Set
;
145 public class BrowserController
extends WindowController
146 implements ProgressListener
, TranscriptListener
, NSToolbar
.Delegate
, QLPreviewPanelController
{
147 private static Logger log
= Logger
.getLogger(BrowserController
.class);
152 private static final Filter
<Path
> NULL_FILTER
= new NullFilter
<Path
>();
155 * Filter hidden files.
157 private static final Filter
<Path
> HIDDEN_FILTER
= new RegexFilter();
159 private final BookmarkCollection bookmarks
160 = BookmarkCollection
.defaultCollection();
165 private Session
<?
> session
;
170 private TranscriptController transcript
;
172 private final QuickLook quicklook
= QuickLookFactory
.get();
174 private Preferences preferences
175 = PreferencesFactory
.get();
178 * Hide files beginning with '.'
180 private boolean showHiddenFiles
;
182 private Filter
<Path
> filenameFilter
;
185 if(PreferencesFactory
.get().getBoolean("browser.showHidden")) {
186 this.filenameFilter
= new NullFilter
<Path
>();
187 this.showHiddenFiles
= true;
190 this.filenameFilter
= new RegexFilter();
191 this.showHiddenFiles
= false;
195 private final NSTextFieldCell outlineCellPrototype
= OutlineCell
.outlineCell();
196 private final NSImageCell imageCellPrototype
= NSImageCell
.imageCell();
197 private final NSTextFieldCell textCellPrototype
= NSTextFieldCell
.textFieldCell();
198 private final NSTextFieldCell filenameCellPrototype
= NSTextFieldCell
.textFieldCell();
200 private final TableColumnFactory browserListColumnsFactory
= new TableColumnFactory();
201 private final TableColumnFactory browserOutlineColumnsFactory
= new TableColumnFactory();
202 private final TableColumnFactory bookmarkTableColumnFactory
= new TableColumnFactory();
204 // setting appearance attributes()
205 private final NSLayoutManager layoutManager
= NSLayoutManager
.layoutManager();
208 private BrowserOutlineViewModel browserOutlineModel
;
211 private NSOutlineView browserOutlineView
;
214 private AbstractBrowserTableDelegate
<Path
> browserOutlineViewDelegate
;
217 private BrowserListViewModel browserListModel
;
220 private NSTableView browserListView
;
223 private AbstractBrowserTableDelegate
<Path
> browserListViewDelegate
;
225 private NSToolbar toolbar
;
227 private final Navigation navigation
= new Navigation();
229 private PathPasteboard pasteboard
;
231 private ListProgressListener listener
232 = new PromptLimitedListProgressListener(this);
235 * Caching files listings of previously listed directories
237 private PathCache cache
238 = new PathCache(preferences
.getInteger("browser.cache.size"));
240 private List
<Editor
> editors
241 = new ArrayList
<Editor
>();
243 public BrowserController() {
248 protected String
getBundleName() {
252 protected void validateToolbar() {
253 this.window().toolbar().validateVisibleItems();
256 public static void updateBookmarkTableRowHeight() {
257 for(BrowserController controller
: MainController
.getBrowsers()) {
258 controller
._updateBookmarkCell();
262 public static void updateBrowserTableAttributes() {
263 for(BrowserController controller
: MainController
.getBrowsers()) {
264 controller
._updateBrowserAttributes(controller
.browserListView
);
265 controller
._updateBrowserAttributes(controller
.browserOutlineView
);
269 public static void updateBrowserTableColumns() {
270 for(BrowserController controller
: MainController
.getBrowsers()) {
271 controller
._updateBrowserColumns(controller
.browserListView
, controller
.browserListViewDelegate
);
272 controller
._updateBrowserColumns(controller
.browserOutlineView
, controller
.browserOutlineViewDelegate
);
277 public void awakeFromNib() {
278 super.awakeFromNib();
280 this.toolbar
= NSToolbar
.toolbarWithIdentifier("Cyberduck Toolbar");
281 this.toolbar
.setDelegate((this.id()));
282 this.toolbar
.setAllowsUserCustomization(true);
283 this.toolbar
.setAutosavesConfiguration(true);
284 this.window().setToolbar(toolbar
);
285 this.window().makeFirstResponder(quickConnectPopup
);
286 this._updateBrowserColumns(browserListView
, browserListViewDelegate
);
287 this._updateBrowserColumns(browserOutlineView
, browserOutlineViewDelegate
);
288 if(preferences
.getBoolean("browser.transcript.open")) {
289 this.logDrawer
.open();
291 if(LicenseFactory
.find().equals(LicenseFactory
.EMPTY_LICENSE
)) {
292 this.addDonateWindowTitle();
294 this.setNavigation(false);
295 this.selectBookmarks();
298 protected Comparator
<Path
> getComparator() {
299 return this.getSelectedBrowserDelegate().getSortingComparator();
302 protected Filter
<Path
> getFilter() {
303 return this.filenameFilter
;
306 public PathPasteboard
getPasteboard() {
310 protected void setPathFilter(final String search
) {
311 if(log
.isDebugEnabled()) {
312 log
.debug(String
.format("Set path filter to %s", search
));
314 if(StringUtils
.isBlank(search
)) {
315 this.searchField
.setStringValue(StringUtils
.EMPTY
);
316 // Revert to the last used default filter
317 if(this.isShowHiddenFiles()) {
318 this.filenameFilter
= NULL_FILTER
;
321 this.filenameFilter
= HIDDEN_FILTER
;
325 // Setting up a custom filter for the directory listing
326 this.filenameFilter
= new SearchFilter(search
);
330 public void setShowHiddenFiles(boolean showHidden
) {
332 this.filenameFilter
= NULL_FILTER
;
333 this.showHiddenFiles
= true;
336 this.filenameFilter
= HIDDEN_FILTER
;
337 this.showHiddenFiles
= false;
341 public boolean isShowHiddenFiles() {
342 return this.showHiddenFiles
;
346 * Marks the current browser as the first responder
348 private void getFocus() {
350 if(this.getSelectedTabView() == TAB_BOOKMARKS
) {
351 view
= bookmarkTable
;
354 if(this.isMounted()) {
355 view
= this.getSelectedBrowserView();
358 view
= quickConnectPopup
;
362 this.window().makeFirstResponder(view
);
367 protected void reload() {
368 if(this.isMounted()) {
369 this.reload(Collections
.singleton(workdir
), this.getSelectedPaths(), false);
372 final NSTableView browser
= this.getSelectedBrowserView();
373 final BrowserTableDataSource model
= this.getSelectedBrowserModel();
374 model
.render(browser
, Collections
.<Path
>emptyList());
379 * Make the browser reload its content. Will make use of the cache.
381 * @param selected The items to be selected
383 protected void reload(final List
<Path
> changed
, final List
<Path
> selected
) {
384 this.reload(new PathReloadFinder().find(changed
), selected
, true);
388 * Make the browser reload its content. Will make use of the cache.
390 * @param folders Folders to render
391 * @param selected The items to be selected
393 protected void reload(final Set
<Path
> folders
, final List
<Path
> selected
) {
394 this.reload(folders
, selected
, true);
397 protected void reload(final Set
<Path
> folders
, final List
<Path
> selected
, final boolean invalidate
) {
398 if(log
.isDebugEnabled()) {
399 log
.debug(String
.format("Reload data with selected files %s", selected
));
401 final BrowserTableDataSource model
= this.getSelectedBrowserModel();
402 final NSTableView browser
= this.getSelectedBrowserView();
403 if(folders
.isEmpty()) {
404 // Render empty browser
405 model
.render(browser
, Collections
.<Path
>emptyList());
407 for(final Path folder
: folders
) {
410 cache
.invalidate(folder
);
413 if(cache
.isCached(folder
)) {
414 model
.render(browser
, Collections
.singletonList(folder
));
419 // Delay render until path is cached in the background
420 this.background(new WorkerBackgroundAction
<AttributedList
<Path
>>(this, session
, cache
,
421 new SessionListWorker(session
, cache
, folder
, listener
) {
423 public void cleanup(final AttributedList
<Path
> list
) {
424 model
.render(browser
, Collections
.singletonList(folder
));
433 private void select(final List
<Path
> selected
) {
434 final NSTableView browser
= this.getSelectedBrowserView();
435 if(CollectionUtils
.isEqualCollection(this.getSelectedPaths(), selected
)) {
438 browser
.deselectAll(null);
439 for(Path path
: selected
) {
440 select(path
, true, true);
445 * @param file Path to select
446 * @param expand Keep previous selection
447 * @param scroll Scroll to selection
449 private void select(final Path file
, final boolean expand
, final boolean scroll
) {
450 final NSTableView browser
= this.getSelectedBrowserView();
451 final BrowserTableDataSource model
= this.getSelectedBrowserModel();
452 if(log
.isDebugEnabled()) {
453 log
.debug(String
.format("Select row for reference %s", file
));
455 int row
= model
.indexOf(browser
, file
);
457 log
.warn(String
.format("Failed to find row for %s", file
));
460 final NSInteger index
= new NSInteger(row
);
461 browser
.selectRowIndexes(NSIndexSet
.indexSetWithIndex(index
), expand
);
463 browser
.scrollRowToVisible(index
);
467 private void updateQuickLookSelection(final List
<Path
> selected
) {
468 if(quicklook
.isAvailable()) {
469 final List
<TransferItem
> downloads
= new ArrayList
<TransferItem
>();
470 for(Path path
: selected
) {
474 downloads
.add(new TransferItem(
475 path
, TemporaryFileServiceFactory
.get().create(session
.getHost().getUuid(), path
)));
477 if(downloads
.size() > 0) {
478 final Transfer download
= new DownloadTransfer(session
.getHost(), downloads
);
479 final TransferOptions options
= new TransferOptions();
480 background(new TransferBackgroundAction(this, session
, cache
, new TransferAdapter() {
482 public void progress(final TransferProgress status
) {
483 message(status
.getProgress());
485 }, this, this, download
, options
,
486 new TransferPrompt() {
488 public TransferAction
prompt(final TransferItem item
) {
489 return TransferAction
.comparison
;
493 public boolean isSelected(final TransferItem file
) {
498 public void message(final String message
) {
499 BrowserController
.this.message(message
);
501 }, new DisabledTransferErrorCallback()
504 public void cleanup() {
506 final List
<Local
> previews
= new ArrayList
<Local
>();
507 for(TransferItem download
: downloads
) {
508 previews
.add(download
.local
);
510 // Change files in Quick Look
511 quicklook
.select(previews
);
512 // Open Quick Look Preview Panel
517 public String
getActivity() {
518 return LocaleFactory
.localizedString("Quick Look", "Status");
526 * @return The first selected path found or null if there is no selection
528 protected Path
getSelectedPath() {
529 final List
<Path
> s
= this.getSelectedPaths();
537 * @return All selected paths or an empty list if there is no selection
539 protected List
<Path
> getSelectedPaths() {
540 final AbstractBrowserTableDelegate
<Path
> delegate
= this.getSelectedBrowserDelegate();
541 final NSTableView view
= this.getSelectedBrowserView();
542 final NSIndexSet iterator
= view
.selectedRowIndexes();
543 final List
<Path
> selected
= new ArrayList
<Path
>();
544 for(NSUInteger index
= iterator
.firstIndex(); !index
.equals(NSIndexSet
.NSNotFound
); index
= iterator
.indexGreaterThanIndex(index
)) {
545 final Path file
= delegate
.pathAtRow(index
.intValue());
554 protected int getSelectionCount() {
555 return this.getSelectedBrowserView().numberOfSelectedRows().intValue();
559 public void setWindow(NSWindow window
) {
560 window
.setTitle(preferences
.getProperty("application.name"));
561 window
.setMiniwindowImage(IconCacheFactory
.<NSImage
>get().iconNamed("cyberduck-document.icns"));
562 window
.setMovableByWindowBackground(true);
563 window
.setCollectionBehavior(window
.collectionBehavior() | NSWindow
.NSWindowCollectionBehavior
.NSWindowCollectionBehaviorFullScreenPrimary
);
564 window
.setContentMinSize(new NSSize(400d
, 200d
));
565 // Accept file promises made myself
566 window
.registerForDraggedTypes(NSArray
.arrayWithObject(NSPasteboard
.FilesPromisePboardType
));
567 super.setWindow(window
);
571 * @return NSDragOperation
573 public NSUInteger
draggingEntered(final NSDraggingInfo sender
) {
574 return this.draggingUpdated(sender
);
577 public NSUInteger
draggingUpdated(final NSDraggingInfo sender
) {
578 final NSPasteboard pasteboard
= sender
.draggingPasteboard();
579 if(pasteboard
.types().indexOfObject(NSString
.stringWithString(NSPasteboard
.FilesPromisePboardType
)) != null) {
580 final NSView hit
= sender
.draggingDestinationWindow().contentView().hitTest(sender
.draggingLocation());
582 if(hit
.equals(bookmarkButton
)) {
583 if(historyButton
.state() == NSCell
.NSOnState
584 || bonjourButton
.state() == NSCell
.NSOnState
) {
585 return NSDraggingInfo
.NSDragOperationCopy
;
590 return NSDraggingInfo
.NSDragOperationNone
;
593 public boolean prepareForDragOperation(final NSDraggingInfo sender
) {
594 // Continue to performDragOperation
598 public boolean performDragOperation(final NSDraggingInfo sender
) {
599 for(Host bookmark
: HostPasteboard
.getPasteboard()) {
600 final Host duplicate
= new HostDictionary().deserialize(bookmark
.serialize(SerializerFactory
.get()));
601 // Make sure a new UUID is assigned for duplicate
602 duplicate
.setUuid(null);
603 bookmarks
.add(0, duplicate
);
609 private NSDrawer logDrawer
;
611 public void drawerDidOpen(NSNotification notification
) {
612 preferences
.setProperty("browser.transcript.open", true);
615 public void drawerDidClose(NSNotification notification
) {
616 preferences
.setProperty("browser.transcript.open", false);
620 public NSSize
drawerWillResizeContents_toSize(final NSDrawer sender
, final NSSize contentSize
) {
624 public void setLogDrawer(NSDrawer drawer
) {
625 this.logDrawer
= drawer
;
626 this.transcript
= new TranscriptController() {
628 public boolean isOpen() {
629 return logDrawer
.state() == NSDrawer
.OpenState
;
632 this.logDrawer
.setContentView(this.transcript
.getLogView());
633 this.logDrawer
.setDelegate(this.id());
636 private NSButton donateButton
;
638 public void setDonateButton(NSButton donateButton
) {
639 this.donateButton
= donateButton
;
640 this.donateButton
.setTitle(LocaleFactory
.localizedString("Get a donation key!", "License"));
641 this.donateButton
.setAction(Foundation
.selector("donateMenuClicked:"));
642 this.donateButton
.sizeToFit();
645 private void addDonateWindowTitle() {
646 NSView parent
= this.window().contentView().superview();
647 NSSize bounds
= parent
.frame().size
;
648 NSSize size
= donateButton
.frame().size
;
649 donateButton
.setFrame(new NSRect(
651 bounds
.width
.intValue() - size
.width
.intValue() - 40,
652 bounds
.height
.intValue() - size
.height
.intValue() + 3),
654 size
.width
.intValue(),
655 size
.height
.intValue())
658 donateButton
.setAutoresizingMask(new NSUInteger(NSView
.NSViewMinXMargin
| NSView
.NSViewMinYMargin
));
659 parent
.addSubview(donateButton
);
662 public void removeDonateWindowTitle() {
663 donateButton
.removeFromSuperview();
666 private static final int TAB_BOOKMARKS
= 0;
667 private static final int TAB_LIST_VIEW
= 1;
668 private static final int TAB_OUTLINE_VIEW
= 2;
670 private int getSelectedTabView() {
671 return browserTabView
.indexOfTabViewItem(browserTabView
.selectedTabViewItem());
674 private NSTabView browserTabView
;
676 public void setBrowserTabView(NSTabView browserTabView
) {
677 this.browserTabView
= browserTabView
;
681 * @return The currently selected browser view (which is either an outlineview or a plain tableview)
683 public NSTableView
getSelectedBrowserView() {
684 switch(preferences
.getInteger("browser.view")) {
685 case SWITCH_LIST_VIEW
: {
686 return browserListView
;
688 case SWITCH_OUTLINE_VIEW
: {
689 return browserOutlineView
;
692 throw new FactoryException("No selected browser view");
696 * @return The datasource of the currently selected browser view
698 public BrowserTableDataSource
getSelectedBrowserModel() {
699 switch(this.browserSwitchView
.selectedSegment()) {
700 case SWITCH_LIST_VIEW
: {
701 return browserListModel
;
703 case SWITCH_OUTLINE_VIEW
: {
704 return browserOutlineModel
;
707 throw new FactoryException("No selected browser view");
710 public AbstractBrowserTableDelegate
<Path
> getSelectedBrowserDelegate() {
711 switch(this.browserSwitchView
.selectedSegment()) {
712 case SWITCH_LIST_VIEW
: {
713 return browserListViewDelegate
;
715 case SWITCH_OUTLINE_VIEW
: {
716 return browserOutlineViewDelegate
;
719 throw new FactoryException("No selected browser view");
723 private NSMenu editMenu
;
726 private EditMenuDelegate editMenuDelegate
;
728 public void setEditMenu(NSMenu editMenu
) {
729 this.editMenu
= editMenu
;
730 this.editMenuDelegate
= new EditMenuDelegate() {
732 protected Path
getEditable() {
733 final Path selected
= BrowserController
.this.getSelectedPath();
734 if(null == selected
) {
737 if(isEditable(selected
)) {
744 protected ID
getTarget() {
745 return BrowserController
.this.id();
748 this.editMenu
.setDelegate(editMenuDelegate
.id());
752 private NSMenu urlMenu
;
755 private URLMenuDelegate urlMenuDelegate
;
757 public void setUrlMenu(NSMenu urlMenu
) {
758 this.urlMenu
= urlMenu
;
759 this.urlMenuDelegate
= new CopyURLMenuDelegate() {
761 protected Session
<?
> getSession() {
762 return BrowserController
.this.getSession();
766 protected List
<Path
> getSelected() {
767 final List
<Path
> s
= BrowserController
.this.getSelectedPaths();
769 if(BrowserController
.this.isMounted()) {
770 return Collections
.singletonList(BrowserController
.this.workdir());
776 this.urlMenu
.setDelegate(urlMenuDelegate
.id());
780 private NSMenu openUrlMenu
;
783 private URLMenuDelegate openUrlMenuDelegate
;
785 public void setOpenUrlMenu(NSMenu openUrlMenu
) {
786 this.openUrlMenu
= openUrlMenu
;
787 this.openUrlMenuDelegate
= new OpenURLMenuDelegate() {
789 protected Session
<?
> getSession() {
790 return BrowserController
.this.getSession();
794 protected List
<Path
> getSelected() {
795 final List
<Path
> s
= BrowserController
.this.getSelectedPaths();
797 if(BrowserController
.this.isMounted()) {
798 return Collections
.singletonList(BrowserController
.this.workdir());
804 this.openUrlMenu
.setDelegate(openUrlMenuDelegate
.id());
808 private NSMenu archiveMenu
;
811 private ArchiveMenuDelegate archiveMenuDelegate
;
813 public void setArchiveMenu(NSMenu archiveMenu
) {
814 this.archiveMenu
= archiveMenu
;
815 this.archiveMenuDelegate
= new ArchiveMenuDelegate();
816 this.archiveMenu
.setDelegate(archiveMenuDelegate
.id());
820 private NSButton bonjourButton
;
822 public void setBonjourButton(NSButton bonjourButton
) {
823 this.bonjourButton
= bonjourButton
;
824 NSImage img
= IconCacheFactory
.<NSImage
>get().iconNamed("rendezvous.tiff", 16);
825 img
.setTemplate(false);
826 this.bonjourButton
.setImage(img
);
827 this.setRecessedBezelStyle(this.bonjourButton
);
828 this.bonjourButton
.setTarget(this.id());
829 this.bonjourButton
.setAction(Foundation
.selector("bookmarkButtonClicked:"));
833 private NSButton historyButton
;
835 public void setHistoryButton(NSButton historyButton
) {
836 this.historyButton
= historyButton
;
837 NSImage img
= IconCacheFactory
.<NSImage
>get().iconNamed("history.tiff", 16);
838 img
.setTemplate(false);
839 this.historyButton
.setImage(img
);
840 this.setRecessedBezelStyle(this.historyButton
);
841 this.historyButton
.setTarget(this.id());
842 this.historyButton
.setAction(Foundation
.selector("bookmarkButtonClicked:"));
846 private NSButton bookmarkButton
;
848 public void setBookmarkButton(NSButton bookmarkButton
) {
849 this.bookmarkButton
= bookmarkButton
;
850 NSImage img
= IconCacheFactory
.<NSImage
>get().iconNamed("bookmarks.tiff", 16);
851 img
.setTemplate(false);
852 this.bookmarkButton
.setImage(img
);
853 this.setRecessedBezelStyle(this.bookmarkButton
);
854 this.bookmarkButton
.setTarget(this.id());
855 this.bookmarkButton
.setAction(Foundation
.selector("bookmarkButtonClicked:"));
856 this.bookmarkButton
.setState(NSCell
.NSOnState
); // Set as default selected bookmark source
859 public void bookmarkButtonClicked(final NSButton sender
) {
860 if(sender
!= bonjourButton
) {
861 bonjourButton
.setState(NSCell
.NSOffState
);
863 if(sender
!= historyButton
) {
864 historyButton
.setState(NSCell
.NSOffState
);
866 if(sender
!= bookmarkButton
) {
867 bookmarkButton
.setState(NSCell
.NSOffState
);
869 sender
.setState(NSCell
.NSOnState
);
870 this.selectBookmarks();
873 private void setRecessedBezelStyle(final NSButton b
) {
874 b
.setBezelStyle(NSButton
.NSRecessedBezelStyle
);
875 b
.setButtonType(NSButton
.NSMomentaryPushButtonButton
);
876 b
.setImagePosition(NSCell
.NSImageLeft
);
877 b
.setFont(NSFont
.boldSystemFontOfSize(11f
));
878 b
.setShowsBorderOnlyWhileMouseInside(true);
881 public void sortBookmarksByNickame(final ID sender
) {
882 bookmarks
.sortByNickname();
883 this.reloadBookmarks();
886 public void sortBookmarksByHostname(final ID sender
) {
887 bookmarks
.sortByHostname();
888 this.reloadBookmarks();
891 public void sortBookmarksByProtocol(final ID sender
) {
892 bookmarks
.sortByProtocol();
893 this.reloadBookmarks();
896 private NSSegmentedControl bookmarkSwitchView
;
898 private static final int SWITCH_BOOKMARK_VIEW
= 0;
900 public void setBookmarkSwitchView(NSSegmentedControl bookmarkSwitchView
) {
901 this.bookmarkSwitchView
= bookmarkSwitchView
;
902 this.bookmarkSwitchView
.setSegmentCount(1);
903 this.bookmarkSwitchView
.setToolTip(LocaleFactory
.localizedString("Bookmarks"));
904 final NSImage image
= IconCacheFactory
.<NSImage
>get().iconNamed("book.tiff");
905 this.bookmarkSwitchView
.setImage_forSegment(image
, SWITCH_BOOKMARK_VIEW
);
906 final NSSegmentedCell cell
= Rococoa
.cast(this.bookmarkSwitchView
.cell(), NSSegmentedCell
.class);
907 cell
.setTrackingMode(NSSegmentedCell
.NSSegmentSwitchTrackingSelectAny
);
908 cell
.setControlSize(NSCell
.NSRegularControlSize
);
909 this.bookmarkSwitchView
.setTarget(this.id());
910 this.bookmarkSwitchView
.setAction(Foundation
.selector("bookmarkSwitchClicked:"));
911 this.bookmarkSwitchView
.setSelectedSegment(SWITCH_BOOKMARK_VIEW
);
915 public void bookmarkSwitchClicked(final ID sender
) {
917 final boolean open
= this.getSelectedTabView() != TAB_BOOKMARKS
;
918 bookmarkSwitchView
.setSelected_forSegment(open
, SWITCH_BOOKMARK_VIEW
);
919 this.setNavigation(!open
&& this.isMounted());
921 this.selectBookmarks();
924 this.selectBrowser(preferences
.getInteger("browser.view"));
928 private NSSegmentedControl browserSwitchView
;
930 private static final int SWITCH_LIST_VIEW
= 0;
931 private static final int SWITCH_OUTLINE_VIEW
= 1;
933 public void setBrowserSwitchView(NSSegmentedControl view
) {
934 browserSwitchView
= view
;
935 browserSwitchView
.setSegmentCount(2); // list, outline
936 final NSImage list
= IconCacheFactory
.<NSImage
>get().iconNamed("list.tiff");
937 list
.setTemplate(true);
938 browserSwitchView
.setImage_forSegment(list
, SWITCH_LIST_VIEW
);
939 final NSImage outline
= IconCacheFactory
.<NSImage
>get().iconNamed("outline.tiff");
940 outline
.setTemplate(true);
941 browserSwitchView
.setImage_forSegment(outline
, SWITCH_OUTLINE_VIEW
);
942 browserSwitchView
.setTarget(this.id());
943 browserSwitchView
.setAction(Foundation
.selector("browserSwitchButtonClicked:"));
944 final NSSegmentedCell cell
= Rococoa
.cast(browserSwitchView
.cell(), NSSegmentedCell
.class);
945 cell
.setTrackingMode(NSSegmentedCell
.NSSegmentSwitchTrackingSelectOne
);
946 cell
.setControlSize(NSCell
.NSRegularControlSize
);
947 browserSwitchView
.setSelectedSegment(preferences
.getInteger("browser.view"));
951 public void browserSwitchButtonClicked(final NSSegmentedControl sender
) {
952 // Highlight selected browser view
953 this.selectBrowser(sender
.selectedSegment());
957 public void browserSwitchMenuClicked(final NSMenuItem sender
) {
958 // Highlight selected browser view
959 this.selectBrowser(sender
.tag());
962 private void selectBrowser(int selected
) {
963 bookmarkSwitchView
.setSelected_forSegment(false, SWITCH_BOOKMARK_VIEW
);
964 browserSwitchView
.setSelectedSegment(selected
);
966 case SWITCH_LIST_VIEW
:
967 browserTabView
.selectTabViewItemAtIndex(TAB_LIST_VIEW
);
969 case SWITCH_OUTLINE_VIEW
:
970 browserTabView
.selectTabViewItemAtIndex(TAB_OUTLINE_VIEW
);
973 // Save selected browser view
974 preferences
.setProperty("browser.view", selected
);
975 // Remove any custom file filter
976 this.setPathFilter(null);
979 // Focus on browser view
983 private void selectBookmarks() {
984 bookmarkSwitchView
.setSelected_forSegment(true, SWITCH_BOOKMARK_VIEW
);
986 browserTabView
.selectTabViewItemAtIndex(TAB_BOOKMARKS
);
987 final AbstractHostCollection source
;
988 if(bookmarkButton
.state() == NSCell
.NSOnState
) {
991 else if(bonjourButton
.state() == NSCell
.NSOnState
) {
992 source
= RendezvousCollection
.defaultCollection();
994 else if(historyButton
.state() == NSCell
.NSOnState
) {
995 source
= HistoryCollection
.defaultCollection();
998 source
= AbstractHostCollection
.empty();
1000 if(!source
.isLoaded()) {
1001 browserSpinner
.startAnimation(null);
1002 source
.addListener(new AbstractCollectionListener
<Host
>() {
1004 public void collectionLoaded() {
1005 invoke(new WindowMainAction(BrowserController
.this) {
1008 browserSpinner
.stopAnimation(null);
1009 bookmarkTable
.setGridStyleMask(NSTableView
.NSTableViewSolidHorizontalGridLineMask
);
1012 source
.removeListener(this);
1017 browserSpinner
.stopAnimation(null);
1018 bookmarkTable
.setGridStyleMask(NSTableView
.NSTableViewSolidHorizontalGridLineMask
);
1020 bookmarkModel
.setSource(source
);
1021 this.setBookmarkFilter(null);
1022 this.reloadBookmarks();
1023 if(this.isMounted()) {
1024 int row
= this.bookmarkModel
.getSource().indexOf(session
.getHost());
1026 this.bookmarkTable
.selectRowIndexes(NSIndexSet
.indexSetWithIndex(new NSInteger(row
)), false);
1027 this.bookmarkTable
.scrollRowToVisible(new NSInteger(row
));
1034 * Reload bookmark table from currently selected model
1036 public void reloadBookmarks() {
1037 bookmarkTable
.reloadData();
1041 private abstract class AbstractBrowserOutlineViewDelegate
<E
> extends AbstractBrowserTableDelegate
<E
>
1042 implements NSOutlineView
.Delegate
{
1044 protected AbstractBrowserOutlineViewDelegate(final NSTableColumn selectedColumn
) {
1045 super(selectedColumn
);
1048 public String
outlineView_toolTipForCell_rect_tableColumn_item_mouseLocation(NSOutlineView t
, NSCell cell
,
1049 ID rect
, NSTableColumn c
,
1050 NSObject item
, NSPoint mouseLocation
) {
1051 return this.tooltip(cache
.lookup(new NSObjectPathReference(item
)));
1054 public String
outlineView_typeSelectStringForTableColumn_item(final NSOutlineView view
,
1055 final NSTableColumn tableColumn
,
1056 final NSObject item
) {
1057 if(tableColumn
.identifier().equals(Column
.filename
.name())) {
1058 return browserOutlineModel
.outlineView_objectValueForTableColumn_byItem(view
, tableColumn
, item
).toString();
1064 protected void setBrowserColumnSortingIndicator(NSImage image
, String columnIdentifier
) {
1065 browserOutlineView
.setIndicatorImage_inTableColumn(image
,
1066 browserOutlineView
.tableColumnWithIdentifier(columnIdentifier
));
1070 protected Path
pathAtRow(final int row
) {
1071 if(row
< browserOutlineView
.numberOfRows().intValue()) {
1072 return cache
.lookup(new NSObjectPathReference(browserOutlineView
.itemAtRow(new NSInteger(row
))));
1074 log
.warn(String
.format("No item at row %d", row
));
1079 private abstract class AbstractBrowserListViewDelegate
<E
> extends AbstractBrowserTableDelegate
<E
>
1080 implements NSTableView
.Delegate
{
1082 protected AbstractBrowserListViewDelegate(final NSTableColumn selectedColumn
) {
1083 super(selectedColumn
);
1086 public String
tableView_toolTipForCell_rect_tableColumn_row_mouseLocation(NSTableView t
, NSCell cell
,
1087 ID rect
, NSTableColumn c
,
1088 NSInteger row
, NSPoint mouseLocation
) {
1089 return this.tooltip(browserListModel
.get(workdir()).get(row
.intValue()));
1093 protected void setBrowserColumnSortingIndicator(NSImage image
, String columnIdentifier
) {
1094 browserListView
.setIndicatorImage_inTableColumn(image
,
1095 browserListView
.tableColumnWithIdentifier(columnIdentifier
));
1098 public String
tableView_typeSelectStringForTableColumn_row(final NSTableView view
,
1099 final NSTableColumn tableColumn
,
1100 final NSInteger row
) {
1101 if(tableColumn
.identifier().equals(Column
.filename
.name())) {
1102 return browserListModel
.tableView_objectValueForTableColumn_row(view
, tableColumn
, row
).toString();
1108 protected Path
pathAtRow(int row
) {
1109 final AttributedList
<Path
> children
= browserListModel
.get(workdir());
1110 if(row
< children
.size()) {
1111 return children
.get(row
);
1113 log
.warn(String
.format("No item at row %d", row
));
1118 private abstract class AbstractBrowserTableDelegate
<E
> extends AbstractPathTableDelegate
{
1120 protected AbstractBrowserTableDelegate(final NSTableColumn selectedColumn
) {
1121 super(selectedColumn
);
1125 public boolean isColumnRowEditable(NSTableColumn column
, int row
) {
1126 if(preferences
.getBoolean("browser.editable")) {
1127 return column
.identifier().equals(Column
.filename
.name());
1133 public void tableRowDoubleClicked(final ID sender
) {
1134 BrowserController
.this.insideButtonClicked(sender
);
1137 public void spaceKeyPressed(final ID sender
) {
1138 quicklookButtonClicked(sender
);
1142 public void deleteKeyPressed(final ID sender
) {
1143 BrowserController
.this.deleteFileButtonClicked(sender
);
1147 public void tableColumnClicked(final NSTableView view
, final NSTableColumn tableColumn
) {
1148 if(this.selectedColumnIdentifier().equals(tableColumn
.identifier())) {
1149 this.setSortedAscending(!this.isSortedAscending());
1152 // Remove sorting indicator on previously selected column
1153 this.setBrowserColumnSortingIndicator(null, this.selectedColumnIdentifier());
1154 // Set the newly selected column
1155 this.setSelectedColumn(tableColumn
);
1156 // Update the default value
1157 preferences
.setProperty("browser.sort.column", this.selectedColumnIdentifier());
1159 this.setBrowserColumnSortingIndicator(
1160 this.isSortedAscending() ?
1161 IconCacheFactory
.<NSImage
>get().iconNamed("NSAscendingSortIndicator") :
1162 IconCacheFactory
.<NSImage
>get().iconNamed("NSDescendingSortIndicator"),
1163 tableColumn
.identifier()
1169 public void columnDidResize(final String columnIdentifier
, final float width
) {
1170 preferences
.setProperty(String
.format("browser.column.%s.width", columnIdentifier
), width
);
1174 public void selectionDidChange(NSNotification notification
) {
1175 final List
<Path
> selected
= getSelectedPaths();
1176 if(quicklook
.isOpen()) {
1177 updateQuickLookSelection(selected
);
1179 if(preferences
.getBoolean("browser.info.inspector")) {
1180 InfoController c
= InfoControllerFactory
.get(BrowserController
.this);
1182 // Currently open info panel
1183 c
.setFiles(selected
);
1188 protected abstract Path
pathAtRow(int row
);
1190 protected abstract void setBrowserColumnSortingIndicator(NSImage image
, String columnIdentifier
);
1192 private static final double kSwipeGestureLeft
= 1.000000;
1193 private static final double kSwipeGestureRight
= -1.000000;
1194 private static final double kSwipeGestureUp
= 1.000000;
1195 private static final double kSwipeGestureDown
= -1.000000;
1198 * Available in Mac OS X v10.6 and later.
1200 * @param event Swipe event
1203 public void swipeWithEvent(NSEvent event
) {
1204 if(event
.deltaX().doubleValue() == kSwipeGestureLeft
) {
1205 BrowserController
.this.backButtonClicked(event
.id());
1207 else if(event
.deltaX().doubleValue() == kSwipeGestureRight
) {
1208 BrowserController
.this.forwardButtonClicked(event
.id());
1210 else if(event
.deltaY().doubleValue() == kSwipeGestureUp
) {
1211 NSInteger row
= getSelectedBrowserView().selectedRow();
1213 if(-1 == row
.intValue()) {
1214 // No current selection
1215 next
= new NSInteger(0);
1218 next
= new NSInteger(row
.longValue() - 1);
1220 BrowserController
.this.getSelectedBrowserView().selectRowIndexes(
1221 NSIndexSet
.indexSetWithIndex(next
), false);
1223 else if(event
.deltaY().doubleValue() == kSwipeGestureDown
) {
1224 NSInteger row
= getSelectedBrowserView().selectedRow();
1226 if(-1 == row
.intValue()) {
1227 // No current selection
1228 next
= new NSInteger(0);
1231 next
= new NSInteger(row
.longValue() + 1);
1233 BrowserController
.this.getSelectedBrowserView().selectRowIndexes(
1234 NSIndexSet
.indexSetWithIndex(next
), false);
1240 * QuickLook support for 10.6+
1242 * @param panel The Preview Panel looking for a controller.
1244 * @ Sent to each object in the responder chain to find a controller.
1247 public boolean acceptsPreviewPanelControl(QLPreviewPanel panel
) {
1252 * QuickLook support for 10.6+
1253 * The receiver should setup the preview panel (data source, delegate, binding, etc.) here.
1255 * @param panel The Preview Panel the receiver will control.
1256 * @ Sent to the object taking control of the Preview Panel.
1259 public void beginPreviewPanelControl(QLPreviewPanel panel
) {
1260 quicklook
.willBeginQuickLook();
1264 * QuickLook support for 10.6+
1265 * The receiver should unsetup the preview panel (data source, delegate, binding, etc.) here.
1267 * @param panel The Preview Panel that the receiver will stop controlling.
1268 * @ Sent to the object in control of the Preview Panel just before stopping its control.
1271 public void endPreviewPanelControl(QLPreviewPanel panel
) {
1272 quicklook
.didEndQuickLook();
1275 public void setBrowserOutlineView(NSOutlineView view
) {
1276 browserOutlineView
= view
;
1277 // receive drag events from types
1278 browserOutlineView
.registerForDraggedTypes(NSArray
.arrayWithObjects(
1279 NSPasteboard
.URLPboardType
,
1280 // Accept files dragged from the Finder for uploading
1281 NSPasteboard
.FilenamesPboardType
,
1282 // Accept file promises made myself
1283 NSPasteboard
.FilesPromisePboardType
1285 // setting appearance attributes()
1286 this._updateBrowserAttributes(browserOutlineView
);
1287 // selection properties
1288 browserOutlineView
.setAllowsMultipleSelection(true);
1289 browserOutlineView
.setAllowsEmptySelection(true);
1290 browserOutlineView
.setAllowsColumnResizing(true);
1291 browserOutlineView
.setAllowsColumnSelection(false);
1292 browserOutlineView
.setAllowsColumnReordering(true);
1294 browserOutlineView
.setRowHeight(new CGFloat(layoutManager
.defaultLineHeightForFont(
1295 NSFont
.systemFontOfSize(preferences
.getFloat("browser.font.size"))).intValue() + 2));
1298 NSTableColumn c
= browserOutlineColumnsFactory
.create(Column
.filename
.name());
1299 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Filename"));
1300 c
.setMinWidth(new CGFloat(100));
1301 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1302 Column
.filename
.name())));
1303 c
.setMaxWidth(new CGFloat(1000));
1304 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1305 c
.setDataCell(outlineCellPrototype
);
1306 browserOutlineView
.addTableColumn(c
);
1307 browserOutlineView
.setOutlineTableColumn(c
);
1309 browserOutlineView
.setDataSource((browserOutlineModel
= new BrowserOutlineViewModel(this, cache
)).id());
1310 browserOutlineView
.setDelegate((browserOutlineViewDelegate
= new AbstractBrowserOutlineViewDelegate
<Path
>(
1311 browserOutlineView
.tableColumnWithIdentifier(Column
.filename
.name())
1314 public void enterKeyPressed(final ID sender
) {
1315 if(preferences
.getBoolean("browser.enterkey.rename")) {
1316 if(browserOutlineView
.numberOfSelectedRows().intValue() == 1) {
1317 renameFileButtonClicked(sender
);
1321 this.tableRowDoubleClicked(sender
);
1326 * @see NSOutlineView.Delegate
1329 public void outlineView_willDisplayCell_forTableColumn_item(NSOutlineView view
, NSTextFieldCell cell
,
1330 NSTableColumn tableColumn
, NSObject item
) {
1334 final Path path
= cache
.lookup(new NSObjectPathReference(item
));
1338 if(tableColumn
.identifier().equals(Column
.filename
.name())) {
1339 cell
.setEditable(session
.getFeature(Move
.class).isSupported(path
));
1340 (Rococoa
.cast(cell
, OutlineCell
.class)).setIcon(browserOutlineModel
.iconForPath(path
));
1342 if(!BrowserController
.this.isConnected() || !HIDDEN_FILTER
.accept(path
)) {
1343 cell
.setTextColor(NSColor
.disabledControlTextColor());
1346 cell
.setTextColor(NSColor
.controlTextColor());
1351 * @see NSOutlineView.Delegate
1354 public boolean outlineView_shouldExpandItem(final NSOutlineView view
, final NSObject item
) {
1355 NSEvent event
= NSApplication
.sharedApplication().currentEvent();
1357 if(NSEvent
.NSLeftMouseDragged
== event
.type()) {
1358 if(!preferences
.getBoolean("browser.view.autoexpand")) {
1359 if(log
.isDebugEnabled()) {
1360 log
.debug("Returning false to #outlineViewShouldExpandItem while dragging because browser.view.autoexpand == false");
1362 // See tickets #98 and #633
1365 final NSInteger draggingColumn
= view
.columnAtPoint(view
.convertPoint_fromView(event
.locationInWindow(), null));
1366 if(draggingColumn
.intValue() != 0) {
1367 if(log
.isDebugEnabled()) {
1368 log
.debug("Returning false to #outlineViewShouldExpandItem for column:" + draggingColumn
);
1379 public boolean outlineView_isGroupItem(final NSOutlineView view
, final NSObject item
) {
1384 public void outlineViewItemWillExpand(final NSNotification notification
) {
1385 final NSObject object
= Rococoa
.cast(notification
.userInfo(), NSDictionary
.class).objectForKey("NSObject");
1386 final NSObjectPathReference reference
= new NSObjectPathReference(object
);
1387 final Path directory
= cache
.lookup(reference
);
1388 if(null == directory
) {
1391 reload(Collections
.singleton(directory
), getSelectedPaths(), false);
1395 * @see NSOutlineView.Delegate
1398 public void outlineViewItemDidExpand(final NSNotification notification
) {
1403 public void outlineViewItemWillCollapse(final NSNotification notification
) {
1408 * @see NSOutlineView.Delegate
1411 public void outlineViewItemDidCollapse(final NSNotification notification
) {
1416 protected boolean isTypeSelectSupported() {
1423 public void setBrowserListView(NSTableView view
) {
1424 browserListView
= view
;
1425 // receive drag events from types
1426 browserListView
.registerForDraggedTypes(NSArray
.arrayWithObjects(
1427 NSPasteboard
.URLPboardType
,
1428 // Accept files dragged from the Finder for uploading
1429 NSPasteboard
.FilenamesPboardType
,
1430 // Accept file promises made myself
1431 NSPasteboard
.FilesPromisePboardType
1433 // setting appearance attributes()
1434 this._updateBrowserAttributes(browserListView
);
1435 // selection properties
1436 browserListView
.setAllowsMultipleSelection(true);
1437 browserListView
.setAllowsEmptySelection(true);
1438 browserListView
.setAllowsColumnResizing(true);
1439 browserListView
.setAllowsColumnSelection(false);
1440 browserListView
.setAllowsColumnReordering(true);
1442 browserListView
.setRowHeight(new CGFloat(layoutManager
.defaultLineHeightForFont(
1443 NSFont
.systemFontOfSize(preferences
.getFloat("browser.font.size"))).intValue() + 2));
1446 NSTableColumn c
= browserListColumnsFactory
.create(Column
.icon
.name());
1447 c
.headerCell().setStringValue(StringUtils
.EMPTY
);
1448 c
.setMinWidth((20));
1449 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1450 Column
.icon
.name())));
1451 c
.setMaxWidth((20));
1452 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
);
1453 c
.setDataCell(imageCellPrototype
);
1454 c
.dataCell().setAlignment(NSText
.NSCenterTextAlignment
);
1455 browserListView
.addTableColumn(c
);
1458 NSTableColumn c
= browserListColumnsFactory
.create(Column
.filename
.name());
1459 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Filename"));
1460 c
.setMinWidth((100));
1461 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1462 Column
.filename
.name())));
1463 c
.setMaxWidth((1000));
1464 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1465 c
.setDataCell(filenameCellPrototype
);
1466 this.browserListView
.addTableColumn(c
);
1469 browserListView
.setDataSource((browserListModel
= new BrowserListViewModel(this, cache
)).id());
1470 browserListView
.setDelegate((browserListViewDelegate
= new AbstractBrowserListViewDelegate
<Path
>(
1471 browserListView
.tableColumnWithIdentifier(Column
.filename
.name())
1474 public void enterKeyPressed(final ID sender
) {
1475 if(preferences
.getBoolean("browser.enterkey.rename")) {
1476 if(browserListView
.numberOfSelectedRows().intValue() == 1) {
1477 renameFileButtonClicked(sender
);
1481 this.tableRowDoubleClicked(sender
);
1486 public void tableView_willDisplayCell_forTableColumn_row(NSTableView view
, NSTextFieldCell cell
, NSTableColumn tableColumn
, NSInteger row
) {
1487 final String identifier
= tableColumn
.identifier();
1488 final Path path
= browserListModel
.get(BrowserController
.this.workdir()).get(row
.intValue());
1489 if(identifier
.equals(Column
.filename
.name())) {
1490 cell
.setEditable(session
.getFeature(Move
.class).isSupported(path
));
1492 if(cell
.isKindOfClass(Foundation
.getClass(NSTextFieldCell
.class.getSimpleName()))) {
1493 if(!BrowserController
.this.isConnected() || !HIDDEN_FILTER
.accept(path
)) {
1494 cell
.setTextColor(NSColor
.disabledControlTextColor());
1497 cell
.setTextColor(NSColor
.controlTextColor());
1503 protected boolean isTypeSelectSupported() {
1509 protected void _updateBrowserAttributes(NSTableView tableView
) {
1510 tableView
.setUsesAlternatingRowBackgroundColors(preferences
.getBoolean("browser.alternatingRows"));
1511 if(preferences
.getBoolean("browser.horizontalLines") && preferences
.getBoolean("browser.verticalLines")) {
1512 tableView
.setGridStyleMask(new NSUInteger(NSTableView
.NSTableViewSolidHorizontalGridLineMask
.intValue() | NSTableView
.NSTableViewSolidVerticalGridLineMask
.intValue()));
1514 else if(preferences
.getBoolean("browser.verticalLines")) {
1515 tableView
.setGridStyleMask(NSTableView
.NSTableViewSolidVerticalGridLineMask
);
1517 else if(preferences
.getBoolean("browser.horizontalLines")) {
1518 tableView
.setGridStyleMask(NSTableView
.NSTableViewSolidHorizontalGridLineMask
);
1521 tableView
.setGridStyleMask(NSTableView
.NSTableViewGridNone
);
1525 protected void _updateBookmarkCell() {
1526 final int size
= preferences
.getInteger("bookmark.icon.size");
1527 final double width
= size
* 1.5;
1528 final NSTableColumn c
= bookmarkTable
.tableColumnWithIdentifier(BookmarkTableDataSource
.Column
.icon
.name());
1529 c
.setMinWidth(width
);
1530 c
.setMaxWidth(width
);
1532 // Notify the table about the changed row height.
1533 bookmarkTable
.noteHeightOfRowsWithIndexesChanged(
1534 NSIndexSet
.indexSetWithIndexesInRange(NSRange
.NSMakeRange(new NSUInteger(0), new NSUInteger(bookmarkTable
.numberOfRows()))));
1537 private void _updateBrowserColumns(final NSTableView table
, final AbstractBrowserTableDelegate
<Path
> delegate
) {
1538 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.size
.name()));
1539 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.size
.name()))) {
1540 NSTableColumn c
= browserListColumnsFactory
.create(Column
.size
.name());
1541 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Size"));
1543 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1544 Column
.size
.name())));
1545 c
.setMaxWidth(150f
);
1546 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1547 c
.setDataCell(textCellPrototype
);
1548 table
.addTableColumn(c
);
1550 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.modified
.name()));
1551 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.modified
.name()))) {
1552 NSTableColumn c
= browserListColumnsFactory
.create(Column
.modified
.name());
1553 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Modified"));
1554 c
.setMinWidth(100f
);
1555 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1556 Column
.modified
.name())));
1558 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1559 c
.setDataCell(textCellPrototype
);
1560 table
.addTableColumn(c
);
1562 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.owner
.name()));
1563 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.owner
.name()))) {
1564 NSTableColumn c
= browserListColumnsFactory
.create(Column
.owner
.name());
1565 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Owner"));
1567 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1568 Column
.owner
.name())));
1570 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1571 c
.setDataCell(textCellPrototype
);
1572 table
.addTableColumn(c
);
1574 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.group
.name()));
1575 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.group
.name()))) {
1576 NSTableColumn c
= browserListColumnsFactory
.create(Column
.group
.name());
1577 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Group"));
1579 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1580 Column
.group
.name())));
1582 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1583 c
.setDataCell(textCellPrototype
);
1584 table
.addTableColumn(c
);
1586 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.permission
.name()));
1587 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.permission
.name()))) {
1588 NSTableColumn c
= browserListColumnsFactory
.create(Column
.permission
.name());
1589 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Permissions"));
1591 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1592 Column
.permission
.name())));
1594 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1595 c
.setDataCell(textCellPrototype
);
1596 table
.addTableColumn(c
);
1598 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.kind
.name()));
1599 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.kind
.name()))) {
1600 NSTableColumn c
= browserListColumnsFactory
.create(Column
.kind
.name());
1601 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Kind"));
1603 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1604 Column
.kind
.name())));
1606 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1607 c
.setDataCell(textCellPrototype
);
1608 table
.addTableColumn(c
);
1610 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.extension
.name()));
1611 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.extension
.name()))) {
1612 NSTableColumn c
= browserListColumnsFactory
.create(Column
.extension
.name());
1613 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Extension"));
1615 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1616 Column
.extension
.name())));
1618 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1619 c
.setDataCell(textCellPrototype
);
1620 table
.addTableColumn(c
);
1622 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.region
.name()));
1623 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.region
.name()))) {
1624 NSTableColumn c
= browserListColumnsFactory
.create(Column
.region
.name());
1625 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Region"));
1627 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1628 Column
.region
.name())));
1630 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1631 c
.setDataCell(textCellPrototype
);
1632 table
.addTableColumn(c
);
1634 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.version
.name()));
1635 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.version
.name()))) {
1636 NSTableColumn c
= browserListColumnsFactory
.create(Column
.version
.name());
1637 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Version"));
1639 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1640 Column
.version
.name())));
1642 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1643 c
.setDataCell(textCellPrototype
);
1644 table
.addTableColumn(c
);
1646 NSTableColumn selected
= table
.tableColumnWithIdentifier(preferences
.getProperty("browser.sort.column"));
1647 if(null == selected
) {
1648 selected
= table
.tableColumnWithIdentifier(Column
.filename
.name());
1650 delegate
.setSelectedColumn(selected
);
1651 table
.setIndicatorImage_inTableColumn(this.getSelectedBrowserDelegate().isSortedAscending() ?
1652 IconCacheFactory
.<NSImage
>get().iconNamed("NSAscendingSortIndicator") :
1653 IconCacheFactory
.<NSImage
>get().iconNamed("NSDescendingSortIndicator"),
1657 table
.setAutosaveName("browser.autosave");
1658 table
.setAutosaveTableColumns(true);
1663 private BookmarkTableDataSource bookmarkModel
;
1666 private NSTableView bookmarkTable
;
1669 private AbstractTableDelegate
<Host
> bookmarkTableDelegate
;
1671 public void setBookmarkTable(NSTableView view
) {
1672 bookmarkTable
= view
;
1673 bookmarkTable
.setSelectionHighlightStyle(NSTableView
.NSTableViewSelectionHighlightStyleSourceList
);
1674 bookmarkTable
.setDataSource((this.bookmarkModel
= new BookmarkTableDataSource(this)).id());
1676 NSTableColumn c
= bookmarkTableColumnFactory
.create(BookmarkTableDataSource
.Column
.icon
.name());
1677 c
.headerCell().setStringValue(StringUtils
.EMPTY
);
1678 c
.setResizingMask(NSTableColumn
.NSTableColumnNoResizing
);
1679 c
.setDataCell(imageCellPrototype
);
1680 bookmarkTable
.addTableColumn(c
);
1683 NSTableColumn c
= bookmarkTableColumnFactory
.create(BookmarkTableDataSource
.Column
.bookmark
.name());
1684 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Bookmarks"));
1686 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
);
1687 c
.setDataCell(BookmarkCell
.bookmarkCell());
1688 bookmarkTable
.addTableColumn(c
);
1691 NSTableColumn c
= bookmarkTableColumnFactory
.create(BookmarkTableDataSource
.Column
.status
.name());
1692 c
.headerCell().setStringValue(StringUtils
.EMPTY
);
1696 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
);
1697 c
.setDataCell(imageCellPrototype
);
1698 c
.dataCell().setAlignment(NSText
.NSCenterTextAlignment
);
1699 bookmarkTable
.addTableColumn(c
);
1701 bookmarkTable
.setDelegate((bookmarkTableDelegate
= new AbstractTableDelegate
<Host
>(
1702 bookmarkTable
.tableColumnWithIdentifier(BookmarkTableDataSource
.Column
.bookmark
.name())
1705 public String
tooltip(Host bookmark
) {
1706 return new HostUrlProvider().get(bookmark
);
1710 public void tableRowDoubleClicked(final ID sender
) {
1711 BrowserController
.this.connectBookmarkButtonClicked(sender
);
1715 public void enterKeyPressed(final ID sender
) {
1716 this.tableRowDoubleClicked(sender
);
1720 public void deleteKeyPressed(final ID sender
) {
1721 if(bookmarkModel
.getSource().allowsDelete()) {
1722 BrowserController
.this.deleteBookmarkButtonClicked(sender
);
1727 public void tableColumnClicked(NSTableView view
, NSTableColumn tableColumn
) {
1732 public void selectionDidChange(NSNotification notification
) {
1733 addBookmarkButton
.setEnabled(bookmarkModel
.getSource().allowsAdd());
1734 final int selected
= bookmarkTable
.numberOfSelectedRows().intValue();
1735 editBookmarkButton
.setEnabled(bookmarkModel
.getSource().allowsEdit() && selected
== 1);
1736 deleteBookmarkButton
.setEnabled(bookmarkModel
.getSource().allowsDelete() && selected
> 0);
1739 public CGFloat
tableView_heightOfRow(NSTableView view
, NSInteger row
) {
1740 final int size
= preferences
.getInteger("bookmark.icon.size");
1741 if(BookmarkCell
.SMALL_BOOKMARK_SIZE
== size
) {
1742 return new CGFloat(18);
1744 if(BookmarkCell
.MEDIUM_BOOKMARK_SIZE
== size
) {
1745 return new CGFloat(45);
1747 return new CGFloat(70);
1751 public boolean isTypeSelectSupported() {
1755 public String
tableView_typeSelectStringForTableColumn_row(NSTableView view
,
1756 NSTableColumn tableColumn
,
1758 return BookmarkNameProvider
.toString(bookmarkModel
.getSource().get(row
.intValue()));
1761 public boolean tableView_isGroupRow(NSTableView view
, NSInteger row
) {
1765 private static final double kSwipeGestureLeft
= 1.000000;
1766 private static final double kSwipeGestureRight
= -1.000000;
1767 private static final double kSwipeGestureUp
= 1.000000;
1768 private static final double kSwipeGestureDown
= -1.000000;
1771 * Available in Mac OS X v10.6 and later.
1773 * @param event Swipe event
1776 public void swipeWithEvent(NSEvent event
) {
1777 if(event
.deltaY().doubleValue() == kSwipeGestureUp
) {
1778 NSInteger row
= bookmarkTable
.selectedRow();
1780 if(-1 == row
.intValue()) {
1781 // No current selection
1782 next
= new NSInteger(0);
1785 next
= new NSInteger(row
.longValue() - 1);
1787 bookmarkTable
.selectRowIndexes(
1788 NSIndexSet
.indexSetWithIndex(next
), false);
1790 else if(event
.deltaY().doubleValue() == kSwipeGestureDown
) {
1791 NSInteger row
= bookmarkTable
.selectedRow();
1793 if(-1 == row
.intValue()) {
1794 // No current selection
1795 next
= new NSInteger(0);
1798 next
= new NSInteger(row
.longValue() + 1);
1800 bookmarkTable
.selectRowIndexes(
1801 NSIndexSet
.indexSetWithIndex(next
), false);
1805 // receive drag events from types
1806 bookmarkTable
.registerForDraggedTypes(NSArray
.arrayWithObjects(
1807 NSPasteboard
.URLPboardType
,
1808 NSPasteboard
.StringPboardType
,
1809 // Accept bookmark files dragged from the Finder
1810 NSPasteboard
.FilenamesPboardType
,
1811 // Accept file promises made myself
1812 NSPasteboard
.FilesPromisePboardType
1814 this._updateBookmarkCell();
1816 final int size
= preferences
.getInteger("bookmark.icon.size");
1817 if(BookmarkCell
.SMALL_BOOKMARK_SIZE
== size
) {
1818 bookmarkTable
.setRowHeight(new CGFloat(18));
1820 else if(BookmarkCell
.MEDIUM_BOOKMARK_SIZE
== size
) {
1821 bookmarkTable
.setRowHeight(new CGFloat(45));
1824 bookmarkTable
.setRowHeight(new CGFloat(70));
1827 // setting appearance attributes()
1828 bookmarkTable
.setUsesAlternatingRowBackgroundColors(preferences
.getBoolean("browser.alternatingRows"));
1829 bookmarkTable
.setGridStyleMask(NSTableView
.NSTableViewGridNone
);
1831 // selection properties
1832 bookmarkTable
.setAllowsMultipleSelection(true);
1833 bookmarkTable
.setAllowsEmptySelection(true);
1834 bookmarkTable
.setAllowsColumnResizing(false);
1835 bookmarkTable
.setAllowsColumnSelection(false);
1836 bookmarkTable
.setAllowsColumnReordering(false);
1837 bookmarkTable
.sizeToFit();
1841 private NSPopUpButton actionPopupButton
;
1843 public void setActionPopupButton(NSPopUpButton actionPopupButton
) {
1844 this.actionPopupButton
= actionPopupButton
;
1845 this.actionPopupButton
.setPullsDown(true);
1846 this.actionPopupButton
.setAutoenablesItems(true);
1850 private NSComboBox quickConnectPopup
;
1852 private ProxyController quickConnectPopupModel
= new QuickConnectModel();
1854 public void setQuickConnectPopup(NSComboBox quickConnectPopup
) {
1855 this.quickConnectPopup
= quickConnectPopup
;
1856 this.quickConnectPopup
.setTarget(this.id());
1857 this.quickConnectPopup
.setCompletes(true);
1858 this.quickConnectPopup
.setAction(Foundation
.selector("quickConnectSelectionChanged:"));
1859 // Make sure action is not sent twice.
1860 this.quickConnectPopup
.cell().setSendsActionOnEndEditing(false);
1861 this.quickConnectPopup
.setUsesDataSource(true);
1862 this.quickConnectPopup
.setDataSource(quickConnectPopupModel
.id());
1863 NSNotificationCenter
.defaultCenter().addObserver(this.id(),
1864 Foundation
.selector("quickConnectWillPopUp:"),
1865 NSComboBox
.ComboBoxWillPopUpNotification
,
1866 this.quickConnectPopup
);
1867 this.quickConnectWillPopUp(null);
1870 private class QuickConnectModel
extends ProxyController
implements NSComboBox
.DataSource
{
1872 public NSInteger
numberOfItemsInComboBox(final NSComboBox combo
) {
1873 return new NSInteger(bookmarks
.size());
1877 public NSObject
comboBox_objectValueForItemAtIndex(final NSComboBox sender
, final NSInteger row
) {
1878 return NSString
.stringWithString(
1879 BookmarkNameProvider
.toString(bookmarks
.get(row
.intValue()))
1884 public void quickConnectWillPopUp(NSNotification notification
) {
1885 int size
= bookmarks
.size();
1886 quickConnectPopup
.setNumberOfVisibleItems(size
> 10 ?
new NSInteger(10) : new NSInteger(size
));
1890 public void quickConnectSelectionChanged(final ID sender
) {
1891 String input
= quickConnectPopup
.stringValue();
1892 if(StringUtils
.isBlank(input
)) {
1895 input
= input
.trim();
1896 // First look for equivalent bookmarks
1897 for(Host h
: bookmarks
) {
1898 if(BookmarkNameProvider
.toString(h
).equals(input
)) {
1903 // Try to parse the input as a URL and extract protocol, hostname, username and password if any.
1904 this.mount(HostParser
.parse(input
));
1908 private NSTextField searchField
;
1910 public void setSearchField(NSTextField searchField
) {
1911 this.searchField
= searchField
;
1912 NSNotificationCenter
.defaultCenter().addObserver(this.id(),
1913 Foundation
.selector("searchFieldTextDidChange:"),
1914 NSControl
.NSControlTextDidChangeNotification
,
1919 public void searchButtonClicked(final ID sender
) {
1920 this.window().makeFirstResponder(searchField
);
1923 public void searchFieldTextDidChange(NSNotification notification
) {
1924 if(this.getSelectedTabView() == TAB_BOOKMARKS
) {
1925 this.setBookmarkFilter(searchField
.stringValue());
1927 else { // TAB_LIST_VIEW || TAB_OUTLINE_VIEW
1928 this.setPathFilter(searchField
.stringValue());
1933 private void setBookmarkFilter(final String searchString
) {
1934 if(StringUtils
.isBlank(searchString
)) {
1935 searchField
.setStringValue(StringUtils
.EMPTY
);
1936 bookmarkModel
.setFilter(null);
1939 bookmarkModel
.setFilter(new HostFilter() {
1941 public boolean accept(Host host
) {
1942 return StringUtils
.lowerCase(BookmarkNameProvider
.toString(host
)).contains(searchString
.toLowerCase(Locale
.ROOT
))
1943 || ((null != host
.getComment()) && StringUtils
.lowerCase(host
.getComment()).contains(searchString
.toLowerCase(Locale
.ROOT
)))
1944 || ((null != host
.getCredentials().getUsername()) && StringUtils
.lowerCase(host
.getCredentials().getUsername()).contains(searchString
.toLowerCase(Locale
.ROOT
)))
1945 || StringUtils
.lowerCase(host
.getHostname()).contains(searchString
.toLowerCase(Locale
.ROOT
));
1949 this.reloadBookmarks();
1952 // ----------------------------------------------------------
1954 // ----------------------------------------------------------
1957 public void connectBookmarkButtonClicked(final ID sender
) {
1958 if(bookmarkTable
.numberOfSelectedRows().intValue() == 1) {
1959 final Host selected
= bookmarkModel
.getSource().get(bookmarkTable
.selectedRow().intValue());
1960 this.mount(selected
);
1965 private NSButton editBookmarkButton
;
1967 public void setEditBookmarkButton(NSButton editBookmarkButton
) {
1968 this.editBookmarkButton
= editBookmarkButton
;
1969 this.editBookmarkButton
.setEnabled(false);
1970 this.editBookmarkButton
.setTarget(this.id());
1971 this.editBookmarkButton
.setAction(Foundation
.selector("editBookmarkButtonClicked:"));
1975 public void editBookmarkButtonClicked(final ID sender
) {
1976 final BookmarkController c
= BookmarkControllerFactory
.create(
1977 bookmarkModel
.getSource().get(bookmarkTable
.selectedRow().intValue())
1979 c
.window().makeKeyAndOrderFront(null);
1983 public void duplicateBookmarkButtonClicked(final ID sender
) {
1984 final Host selected
= bookmarkModel
.getSource().get(bookmarkTable
.selectedRow().intValue());
1985 this.selectBookmarks();
1986 final Host duplicate
= new HostDictionary().deserialize(selected
.serialize(SerializerFactory
.get()));
1987 // Make sure a new UUID is asssigned for duplicate
1988 duplicate
.setUuid(null);
1989 this.addBookmark(duplicate
);
1993 private NSButton addBookmarkButton
;
1995 public void setAddBookmarkButton(NSButton addBookmarkButton
) {
1996 this.addBookmarkButton
= addBookmarkButton
;
1997 this.addBookmarkButton
.setTarget(this.id());
1998 this.addBookmarkButton
.setAction(Foundation
.selector("addBookmarkButtonClicked:"));
2002 public void addBookmarkButtonClicked(final ID sender
) {
2003 final Host bookmark
;
2004 if(this.isMounted()) {
2005 Path selected
= this.getSelectedPath();
2006 if(null == selected
|| !selected
.isDirectory()) {
2007 selected
= this.workdir();
2009 bookmark
= new HostDictionary().deserialize(session
.getHost().serialize(SerializerFactory
.get()));
2010 // Make sure a new UUID is asssigned for duplicate
2011 bookmark
.setUuid(null);
2012 bookmark
.setDefaultPath(selected
.getAbsolute());
2015 bookmark
= new Host(ProtocolFactory
.forName(preferences
.getProperty("connection.protocol.default")),
2016 preferences
.getProperty("connection.hostname.default"),
2017 preferences
.getInteger("connection.port.default"));
2019 this.selectBookmarks();
2020 this.addBookmark(bookmark
);
2023 public void addBookmark(Host item
) {
2024 bookmarkModel
.setFilter(null);
2025 bookmarkModel
.getSource().add(item
);
2026 final int row
= bookmarkModel
.getSource().lastIndexOf(item
);
2027 final NSInteger index
= new NSInteger(row
);
2028 bookmarkTable
.selectRowIndexes(NSIndexSet
.indexSetWithIndex(index
), false);
2029 bookmarkTable
.scrollRowToVisible(index
);
2030 final BookmarkController c
= BookmarkControllerFactory
.create(item
);
2031 c
.window().makeKeyAndOrderFront(null);
2035 private NSButton deleteBookmarkButton
;
2037 public void setDeleteBookmarkButton(NSButton deleteBookmarkButton
) {
2038 this.deleteBookmarkButton
= deleteBookmarkButton
;
2039 this.deleteBookmarkButton
.setEnabled(false);
2040 this.deleteBookmarkButton
.setTarget(this.id());
2041 this.deleteBookmarkButton
.setAction(Foundation
.selector("deleteBookmarkButtonClicked:"));
2045 public void deleteBookmarkButtonClicked(final ID sender
) {
2046 NSIndexSet iterator
= bookmarkTable
.selectedRowIndexes();
2047 final List
<Host
> selected
= new ArrayList
<Host
>();
2048 for(NSUInteger index
= iterator
.firstIndex(); !index
.equals(NSIndexSet
.NSNotFound
); index
= iterator
.indexGreaterThanIndex(index
)) {
2049 selected
.add(bookmarkModel
.getSource().get(index
.intValue()));
2051 StringBuilder alertText
= new StringBuilder(
2052 LocaleFactory
.localizedString("Do you want to delete the selected bookmark?"));
2054 Iterator
<Host
> iter
= selected
.iterator();
2055 while(i
< 10 && iter
.hasNext()) {
2056 alertText
.append("\n").append(Character
.toString('\u2022')).append(" ").append(
2057 BookmarkNameProvider
.toString(iter
.next())
2061 if(iter
.hasNext()) {
2062 alertText
.append("\n").append(Character
.toString('\u2022')).append(" " + "…");
2064 final NSAlert alert
= NSAlert
.alert(LocaleFactory
.localizedString("Delete Bookmark"),
2065 alertText
.toString(),
2066 LocaleFactory
.localizedString("Delete"),
2067 LocaleFactory
.localizedString("Cancel"),
2069 this.alert(alert
, new SheetCallback() {
2071 public void callback(int returncode
) {
2072 if(returncode
== DEFAULT_OPTION
) {
2073 bookmarkTable
.deselectAll(null);
2074 bookmarkModel
.getSource().removeAll(selected
);
2080 // ----------------------------------------------------------
2081 // Browser navigation
2082 // ----------------------------------------------------------
2084 private static final int NAVIGATION_LEFT_SEGMENT_BUTTON
= 0;
2085 private static final int NAVIGATION_RIGHT_SEGMENT_BUTTON
= 1;
2087 private static final int NAVIGATION_UP_SEGMENT_BUTTON
= 0;
2089 private NSSegmentedControl navigationButton
;
2091 public void setNavigationButton(NSSegmentedControl navigationButton
) {
2092 this.navigationButton
= navigationButton
;
2093 this.navigationButton
.setTarget(this.id());
2094 this.navigationButton
.setAction(Foundation
.selector("navigationButtonClicked:"));
2095 this.navigationButton
.setImage_forSegment(IconCacheFactory
.<NSImage
>get().iconNamed("nav-backward.tiff"),
2096 NAVIGATION_LEFT_SEGMENT_BUTTON
);
2097 this.navigationButton
.setImage_forSegment(IconCacheFactory
.<NSImage
>get().iconNamed("nav-forward.tiff"),
2098 NAVIGATION_RIGHT_SEGMENT_BUTTON
);
2102 public void navigationButtonClicked(NSSegmentedControl sender
) {
2103 switch(sender
.selectedSegment()) {
2104 case NAVIGATION_LEFT_SEGMENT_BUTTON
: {
2105 this.backButtonClicked(sender
.id());
2108 case NAVIGATION_RIGHT_SEGMENT_BUTTON
: {
2109 this.forwardButtonClicked(sender
.id());
2116 public void backButtonClicked(final ID sender
) {
2117 final Path selected
= navigation
.back();
2118 if(selected
!= null) {
2119 final Path previous
= this.workdir();
2120 if(previous
.getParent().equals(selected
)) {
2121 this.setWorkdir(selected
, previous
);
2124 this.setWorkdir(selected
);
2130 public void forwardButtonClicked(final ID sender
) {
2131 final Path selected
= navigation
.forward();
2132 if(selected
!= null) {
2133 this.setWorkdir(selected
);
2138 private NSSegmentedControl upButton
;
2140 public void setUpButton(NSSegmentedControl upButton
) {
2141 this.upButton
= upButton
;
2142 this.upButton
.setTarget(this.id());
2143 this.upButton
.setAction(Foundation
.selector("upButtonClicked:"));
2144 this.upButton
.setImage_forSegment(IconCacheFactory
.<NSImage
>get().iconNamed("nav-up.tiff"),
2145 NAVIGATION_UP_SEGMENT_BUTTON
);
2148 public void upButtonClicked(final ID sender
) {
2149 final Path previous
= this.workdir();
2150 this.setWorkdir(previous
.getParent(), previous
);
2153 private Path workdir
;
2156 private NSPopUpButton pathPopupButton
;
2158 public void setPathPopup(NSPopUpButton pathPopupButton
) {
2159 this.pathPopupButton
= pathPopupButton
;
2160 this.pathPopupButton
.setTarget(this.id());
2161 this.pathPopupButton
.setAction(Foundation
.selector("pathPopupSelectionChanged:"));
2165 public void pathPopupSelectionChanged(final NSPopUpButton sender
) {
2166 final String selected
= sender
.selectedItem().representedObject();
2167 if(selected
!= null) {
2168 final Path workdir
= this.workdir();
2170 while(!p
.getAbsolute().equals(selected
)) {
2174 if(workdir
.getParent().equals(p
)) {
2175 this.setWorkdir(p
, workdir
);
2184 private NSPopUpButton encodingPopup
;
2186 public void setEncodingPopup(NSPopUpButton encodingPopup
) {
2187 this.encodingPopup
= encodingPopup
;
2188 this.encodingPopup
.setTarget(this.id());
2189 this.encodingPopup
.setAction(Foundation
.selector("encodingButtonClicked:"));
2190 this.encodingPopup
.removeAllItems();
2191 this.encodingPopup
.addItemsWithTitles(NSArray
.arrayWithObjects(new DefaultCharsetProvider().availableCharsets()));
2192 this.encodingPopup
.selectItemWithTitle(preferences
.getProperty("browser.charset.encoding"));
2196 public void encodingButtonClicked(final NSPopUpButton sender
) {
2197 this.encodingChanged(sender
.titleOfSelectedItem());
2201 public void encodingMenuClicked(final NSMenuItem sender
) {
2202 this.encodingChanged(sender
.title());
2205 public void encodingChanged(final String encoding
) {
2206 if(null == encoding
) {
2209 this.setEncoding(encoding
);
2210 if(this.isMounted()) {
2211 if(session
.getEncoding().equals(encoding
)) {
2214 session
.getHost().setEncoding(encoding
);
2215 this.mount(session
.getHost());
2220 * @param encoding Character encoding
2222 private void setEncoding(final String encoding
) {
2223 this.encodingPopup
.selectItemWithTitle(encoding
);
2226 // ----------------------------------------------------------
2228 // ----------------------------------------------------------
2231 public void toggleLogDrawer(final ID sender
) {
2232 this.logDrawer
.toggle(this.id());
2235 // ----------------------------------------------------------
2237 // ----------------------------------------------------------
2240 protected NSProgressIndicator statusSpinner
;
2242 public void setStatusSpinner(NSProgressIndicator statusSpinner
) {
2243 this.statusSpinner
= statusSpinner
;
2244 this.statusSpinner
.setDisplayedWhenStopped(false);
2245 this.statusSpinner
.setIndeterminate(true);
2249 protected NSProgressIndicator browserSpinner
;
2251 public void setBrowserSpinner(NSProgressIndicator browserSpinner
) {
2252 this.browserSpinner
= browserSpinner
;
2255 public NSProgressIndicator
getBrowserSpinner() {
2256 return browserSpinner
;
2260 private NSTextField statusLabel
;
2262 public void setStatusLabel(NSTextField statusLabel
) {
2263 this.statusLabel
= statusLabel
;
2266 public void setStatus() {
2267 final BackgroundAction current
= this.getActions().getCurrent();
2268 this.message(null != current ? current
.getActivity() : null);
2272 public void stop(final BackgroundAction action
) {
2273 statusSpinner
.stopAnimation(null);
2277 public void start(final BackgroundAction action
) {
2278 statusSpinner
.startAnimation(null);
2282 * @param label Status message
2285 public void message(final String label
) {
2286 if(StringUtils
.isNotBlank(label
)) {
2287 // Update the status label at the bottom of the browser window
2288 statusLabel
.setAttributedStringValue(NSAttributedString
.attributedStringWithAttributes(label
,
2289 TRUNCATE_MIDDLE_ATTRIBUTES
));
2292 if(getSelectedTabView() == TAB_BOOKMARKS
) {
2293 statusLabel
.setAttributedStringValue(
2294 NSAttributedString
.attributedStringWithAttributes(String
.format("%s %s", bookmarkTable
.numberOfRows(),
2295 LocaleFactory
.localizedString("Bookmarks")),
2296 TRUNCATE_MIDDLE_ATTRIBUTES
2303 statusLabel
.setAttributedStringValue(
2304 NSAttributedString
.attributedStringWithAttributes(MessageFormat
.format(LocaleFactory
.localizedString("{0} Files"),
2305 String
.valueOf(getSelectedBrowserView().numberOfRows())),
2306 TRUNCATE_MIDDLE_ATTRIBUTES
2311 statusLabel
.setStringValue(StringUtils
.EMPTY
);
2318 public void log(final boolean request
, final String message
) {
2319 transcript
.log(request
, message
);
2323 private NSButton securityLabel
;
2325 public void setSecurityLabel(NSButton securityLabel
) {
2326 this.securityLabel
= securityLabel
;
2327 this.securityLabel
.setEnabled(false);
2328 this.securityLabel
.setTarget(this.id());
2329 this.securityLabel
.setAction(Foundation
.selector("securityLabelClicked:"));
2333 public void securityLabelClicked(final ID sender
) {
2334 if(session
instanceof SSLSession
) {
2335 final SSLSession
<?
> secured
= (SSLSession
) session
;
2336 final List
<X509Certificate
> certificates
= secured
.getAcceptedIssuers();
2338 CertificateStoreFactory
.get().display(certificates
);
2340 catch(CertificateException e
) {
2341 log
.warn(String
.format("Failure decoding certificate %s", e
.getMessage()));
2346 // ----------------------------------------------------------
2347 // Selector methods for the toolbar items
2348 // ----------------------------------------------------------
2350 public void quicklookButtonClicked(final ID sender
) {
2351 if(quicklook
.isOpen()) {
2355 this.updateQuickLookSelection(this.getSelectedPaths());
2360 * Marks all expanded directories as invalid and tells the
2361 * browser table to reload its data
2363 * @param sender Toolbar button
2366 public void reloadButtonClicked(final ID sender
) {
2367 if(this.isMounted()) {
2368 final Set
<Path
> folders
= new HashSet
<Path
>();
2369 switch(browserSwitchView
.selectedSegment()) {
2370 case SWITCH_OUTLINE_VIEW
: {
2371 for(int i
= 0; i
< browserOutlineView
.numberOfRows().intValue(); i
++) {
2372 final NSObject item
= browserOutlineView
.itemAtRow(new NSInteger(i
));
2373 if(browserOutlineView
.isItemExpanded(item
)) {
2374 final Path folder
= cache
.lookup(new NSObjectPathReference(item
));
2375 if(null == folder
) {
2378 folders
.add(folder
);
2384 folders
.add(workdir
);
2385 this.reload(folders
, this.getSelectedPaths(), true);
2390 * Open a new browser with the current selected folder as the working directory
2392 * @param sender Toolbar button
2395 public void newBrowserButtonClicked(final ID sender
) {
2396 Path selected
= this.getSelectedPath();
2397 if(null == selected
|| !selected
.isDirectory()) {
2398 selected
= this.workdir();
2400 BrowserController c
= MainController
.newDocument(true);
2401 final Host host
= new HostDictionary().deserialize(session
.getHost().serialize(SerializerFactory
.get()));
2402 host
.setDefaultPath(selected
.getAbsolute());
2407 * @param source The original file to duplicate
2408 * @param destination The destination of the duplicated file
2410 protected void duplicatePath(final Path source
, final Path destination
) {
2411 this.duplicatePaths(Collections
.singletonMap(source
, destination
));
2415 * @param selected A map with the original files as the key and the destination
2416 * files as the value
2418 protected void duplicatePaths(final Map
<Path
, Path
> selected
) {
2419 this.checkOverwrite(new ArrayList
<Path
>(selected
.values()), new DefaultMainAction() {
2422 transfer(new CopyTransfer(session
.getHost(), session
.getHost(), selected
),
2423 new ArrayList
<Path
>(selected
.values()), true);
2429 * @param path The existing file
2430 * @param renamed The renamed file
2432 protected void renamePath(final Path path
, final Path renamed
) {
2433 this.renamePaths(Collections
.singletonMap(path
, renamed
));
2437 * @param selected A map with the original files as the key and the destination
2438 * files as the value
2440 protected void renamePaths(final Map
<Path
, Path
> selected
) {
2441 this.checkMove(selected
, new DefaultMainAction() {
2444 final ArrayList
<Path
> changed
= new ArrayList
<Path
>();
2445 changed
.addAll(selected
.keySet());
2446 changed
.addAll(selected
.values());
2447 background(new WorkerBackgroundAction
<Boolean
>(BrowserController
.this, session
, cache
,
2448 new MoveWorker(session
, selected
, BrowserController
.this) {
2450 public void cleanup(final Boolean result
) {
2451 reload(changed
, new ArrayList
<Path
>(selected
.values()));
2461 * Displays a warning dialog about already existing files
2463 * @param selected The files to check
2465 private void checkOverwrite(final List
<Path
> selected
, final MainAction action
) {
2466 StringBuilder alertText
= new StringBuilder(
2467 LocaleFactory
.localizedString("A file with the same name already exists. Do you want to replace the existing file?"));
2469 Iterator
<Path
> iter
;
2470 boolean shouldWarn
= false;
2471 for(iter
= selected
.iterator(); iter
.hasNext(); ) {
2472 final Path item
= iter
.next();
2473 if(cache
.get(item
.getParent()).contains(item
)) {
2475 alertText
.append("\n").append(Character
.toString('\u2022')).append(" ").append(item
.getName());
2482 alertText
.append("\n").append(Character
.toString('\u2022')).append(" ...)");
2485 NSAlert alert
= NSAlert
.alert(
2486 LocaleFactory
.localizedString("Overwrite"), //title
2487 alertText
.toString(),
2488 LocaleFactory
.localizedString("Overwrite"), // defaultbutton
2489 LocaleFactory
.localizedString("Cancel"), //alternative button
2492 this.alert(alert
, new SheetCallback() {
2494 public void callback(final int returncode
) {
2495 if(returncode
== DEFAULT_OPTION
) {
2507 * Displays a warning dialog about files to be moved
2509 * @param selected The files to check for existence
2511 private void checkMove(final Map
<Path
, Path
> selected
, final MainAction action
) {
2512 if(preferences
.getBoolean("browser.move.confirm")) {
2513 StringBuilder alertText
= new StringBuilder(
2514 LocaleFactory
.localizedString("Do you want to move the selected files?"));
2516 boolean rename
= false;
2517 Iterator
<Map
.Entry
<Path
, Path
>> iter
;
2518 for(iter
= selected
.entrySet().iterator(); i
< 10 && iter
.hasNext(); ) {
2519 final Map
.Entry
<Path
, Path
> next
= iter
.next();
2520 if(next
.getKey().getParent().equals(next
.getValue().getParent())) {
2523 alertText
.append(String
.format("\n%s %s", Character
.toString('\u2022'), next
.getKey().getName()));
2526 if(iter
.hasNext()) {
2527 alertText
.append(String
.format("\n%s ...)", Character
.toString('\u2022')));
2529 final NSAlert alert
= NSAlert
.alert(
2530 rename ? LocaleFactory
.localizedString("Rename") : LocaleFactory
.localizedString("Move"), //title
2531 alertText
.toString(),
2532 rename ? LocaleFactory
.localizedString("Rename") : LocaleFactory
.localizedString("Move"), // default button
2533 LocaleFactory
.localizedString("Cancel"), //alternative button
2536 alert
.setShowsSuppressionButton(true);
2537 alert
.suppressionButton().setTitle(LocaleFactory
.localizedString("Don't ask again", "Configuration"));
2538 this.alert(alert
, new SheetCallback() {
2540 public void callback(final int returncode
) {
2541 if(alert
.suppressionButton().state() == NSCell
.NSOnState
) {
2542 // Never show again.
2543 preferences
.setProperty("browser.move.confirm", false);
2545 if(returncode
== DEFAULT_OPTION
) {
2546 checkOverwrite(new ArrayList
<Path
>(selected
.values()), action
);
2552 this.checkOverwrite(new ArrayList
<Path
>(selected
.values()), action
);
2557 * Recursively deletes the file
2559 * @param file File or directory
2561 public void deletePath(final Path file
) {
2562 this.deletePaths(Collections
.singletonList(file
));
2566 * Recursively deletes the files
2568 * @param selected The files selected in the browser to delete
2570 public void deletePaths(final List
<Path
> selected
) {
2571 final List
<Path
> normalized
= PathNormalizer
.normalize(selected
);
2572 if(normalized
.isEmpty()) {
2575 StringBuilder alertText
=
2576 new StringBuilder(LocaleFactory
.localizedString("Really delete the following files? This cannot be undone."));
2578 Iterator
<Path
> iter
;
2579 for(iter
= normalized
.iterator(); i
< 10 && iter
.hasNext(); ) {
2580 alertText
.append("\n").append(Character
.toString('\u2022')).append(" ").append(iter
.next().getName());
2583 if(iter
.hasNext()) {
2584 alertText
.append("\n").append(Character
.toString('\u2022')).append(" " + "…");
2586 NSAlert alert
= NSAlert
.alert(LocaleFactory
.localizedString("Delete"), //title
2587 alertText
.toString(),
2588 LocaleFactory
.localizedString("Delete"), // defaultbutton
2589 LocaleFactory
.localizedString("Cancel"), //alternative button
2592 this.alert(alert
, new SheetCallback() {
2594 public void callback(final int returncode
) {
2595 if(returncode
== DEFAULT_OPTION
) {
2596 BrowserController
.this.deletePathsImpl(normalized
);
2602 private void deletePathsImpl(final List
<Path
> files
) {
2603 this.background(new WorkerBackgroundAction
<Boolean
>(this, session
, cache
,
2604 new DeleteWorker(session
, LoginCallbackFactory
.get(BrowserController
.this), files
, this) {
2606 public void cleanup(final Boolean result
) {
2607 reload(files
, Collections
.<Path
>emptyList());
2614 public void revertPaths(final List
<Path
> files
) {
2615 this.background(new WorkerBackgroundAction
<Boolean
>(this, session
, cache
,
2616 new RevertWorker(session
, files
) {
2618 public void cleanup(final Boolean result
) {
2619 reload(files
, files
);
2626 * @param selected File
2627 * @return True if the selected path is editable (not a directory and no known binary file)
2629 protected boolean isEditable(final Path selected
) {
2630 if(this.isMounted()) {
2631 if(session
.getHost().getCredentials().isAnonymousLogin()) {
2634 return selected
.isFile();
2640 public void gotoButtonClicked(final ID sender
) {
2641 SheetController sheet
= new GotoController(this, cache
);
2646 public void createFileButtonClicked(final ID sender
) {
2647 SheetController sheet
= new CreateFileController(this, cache
);
2652 public void createSymlinkButtonClicked(final ID sender
) {
2653 SheetController sheet
= new CreateSymlinkController(this, cache
);
2658 public void duplicateFileButtonClicked(final ID sender
) {
2659 SheetController sheet
= new DuplicateFileController(this, cache
);
2664 public void createFolderButtonClicked(final ID sender
) {
2665 final Location feature
= session
.getFeature(Location
.class);
2666 SheetController sheet
= new FolderController(this, cache
,
2667 feature
!= null ? feature
.getLocations() : Collections
.<Location
.Name
>emptySet());
2672 public void renameFileButtonClicked(final ID sender
) {
2673 final NSTableView browser
= this.getSelectedBrowserView();
2674 browser
.editRow(browser
.columnWithIdentifier(Column
.filename
.name()),
2675 browser
.selectedRow(), true);
2676 final Path selected
= this.getSelectedPath();
2677 if(StringUtils
.isNotBlank(selected
.getExtension())) {
2678 NSText view
= browser
.currentEditor();
2679 int index
= selected
.getName().indexOf(selected
.getExtension()) - 1;
2681 view
.setSelectedRange(NSRange
.NSMakeRange(new NSUInteger(0), new NSUInteger(index
)));
2687 public void sendCustomCommandClicked(final ID sender
) {
2688 SheetController sheet
= new CommandController(this, session
);
2693 public void editMenuClicked(final NSMenuItem sender
) {
2694 final EditorFactory factory
= EditorFactory
.instance();
2695 for(Path selected
: this.getSelectedPaths()) {
2696 final Editor editor
= factory
.create(this, session
,
2697 new Application(sender
.representedObject()), selected
);
2703 public void editButtonClicked(final ID sender
) {
2704 final EditorFactory factory
= EditorFactory
.instance();
2705 for(Path selected
: this.getSelectedPaths()) {
2706 this.edit(selected
);
2710 protected void edit(final Path file
) {
2711 this.edit(EditorFactory
.instance().create(this, session
, file
));
2714 protected void edit(final Editor editor
) {
2715 editors
.add(editor
);
2716 this.background(new WorkerBackgroundAction
<Transfer
>(this, session
, editor
.open(new ApplicationQuitCallback() {
2718 public void callback() {
2719 editors
.remove(editor
);
2721 }, new DisabledTransferErrorCallback(), new DefaultEditorListener(this, session
, editor
))));
2725 public void openBrowserButtonClicked(final ID sender
) {
2726 final DescriptiveUrlBag list
;
2727 if(this.getSelectionCount() == 1) {
2728 list
= session
.getFeature(UrlProvider
.class).toUrl(this.getSelectedPath());
2731 list
= session
.getFeature(UrlProvider
.class).toUrl(this.workdir());
2733 if(!list
.isEmpty()) {
2734 BrowserLauncherFactory
.get().open(list
.find(DescriptiveUrl
.Type
.http
).getUrl());
2739 public void infoButtonClicked(final ID sender
) {
2740 if(this.getSelectionCount() > 0) {
2741 InfoController c
= InfoControllerFactory
.create(this, this.getSelectedPaths());
2742 c
.window().makeKeyAndOrderFront(null);
2747 public void revertFileButtonClicked(final ID sender
) {
2748 this.revertPaths(this.getSelectedPaths());
2752 public void deleteFileButtonClicked(final ID sender
) {
2753 this.deletePaths(this.getSelectedPaths());
2756 private NSOpenPanel downloadToPanel
;
2759 public void downloadToButtonClicked(final ID sender
) {
2760 downloadToPanel
= NSOpenPanel
.openPanel();
2761 downloadToPanel
.setCanChooseDirectories(true);
2762 downloadToPanel
.setCanCreateDirectories(true);
2763 downloadToPanel
.setCanChooseFiles(false);
2764 downloadToPanel
.setAllowsMultipleSelection(false);
2765 downloadToPanel
.setPrompt(LocaleFactory
.localizedString("Choose"));
2766 downloadToPanel
.beginSheetForDirectory(new DownloadDirectoryFinder().find(session
.getHost()).getAbsolute(),
2767 null, this.window
, this.id(),
2768 Foundation
.selector("downloadToPanelDidEnd:returnCode:contextInfo:"), null);
2771 public void downloadToPanelDidEnd_returnCode_contextInfo(final NSOpenPanel sheet
, final int returncode
, final ID contextInfo
) {
2772 sheet
.orderOut(this.id());
2773 if(returncode
== SheetCallback
.DEFAULT_OPTION
) {
2774 if(sheet
.filename() != null) {
2775 final Local target
= LocalFactory
.get(sheet
.filename());
2776 new DownloadDirectoryFinder().save(session
.getHost(), target
);
2777 final List
<TransferItem
> downloads
= new ArrayList
<TransferItem
>();
2778 for(Path file
: this.getSelectedPaths()) {
2779 downloads
.add(new TransferItem(file
, LocalFactory
.get(target
, file
.getName())));
2781 this.transfer(new DownloadTransfer(session
.getHost(), downloads
), Collections
.<Path
>emptyList());
2784 downloadToPanel
= null;
2788 private NSSavePanel downloadAsPanel
;
2791 public void downloadAsButtonClicked(final ID sender
) {
2792 downloadAsPanel
= NSSavePanel
.savePanel();
2793 downloadAsPanel
.setMessage(LocaleFactory
.localizedString("Download the selected file to…"));
2794 downloadAsPanel
.setNameFieldLabel(LocaleFactory
.localizedString("Download As:"));
2795 downloadAsPanel
.setPrompt(LocaleFactory
.localizedString("Download", "Transfer"));
2796 downloadAsPanel
.setCanCreateDirectories(true);
2797 downloadAsPanel
.beginSheetForDirectory(new DownloadDirectoryFinder().find(session
.getHost()).getAbsolute(),
2798 this.getSelectedPath().getName(), this.window
, this.id(),
2799 Foundation
.selector("downloadAsPanelDidEnd:returnCode:contextInfo:"), null);
2802 public void downloadAsPanelDidEnd_returnCode_contextInfo(final NSSavePanel sheet
, final int returncode
, final ID contextInfo
) {
2803 sheet
.orderOut(this.id());
2804 if(returncode
== SheetCallback
.DEFAULT_OPTION
) {
2805 if(sheet
.filename() != null) {
2806 final Local target
= LocalFactory
.get(sheet
.filename());
2807 new DownloadDirectoryFinder().save(session
.getHost(), target
.getParent());
2808 final List
<TransferItem
> downloads
2809 = Collections
.singletonList(new TransferItem(this.getSelectedPath(), target
));
2810 this.transfer(new DownloadTransfer(session
.getHost(), downloads
), Collections
.<Path
>emptyList());
2816 private NSOpenPanel syncPanel
;
2819 public void syncButtonClicked(final ID sender
) {
2820 final Path selection
;
2821 if(this.getSelectionCount() == 1 &&
2822 this.getSelectedPath().isDirectory()) {
2823 selection
= this.getSelectedPath();
2826 selection
= this.workdir();
2828 syncPanel
= NSOpenPanel
.openPanel();
2829 syncPanel
.setCanChooseDirectories(selection
.isDirectory());
2830 syncPanel
.setTreatsFilePackagesAsDirectories(true);
2831 syncPanel
.setCanChooseFiles(selection
.isFile());
2832 syncPanel
.setCanCreateDirectories(true);
2833 syncPanel
.setAllowsMultipleSelection(false);
2834 syncPanel
.setMessage(MessageFormat
.format(LocaleFactory
.localizedString("Synchronize {0} with"),
2835 selection
.getName()));
2836 syncPanel
.setPrompt(LocaleFactory
.localizedString("Choose"));
2837 syncPanel
.beginSheetForDirectory(new UploadDirectoryFinder().find(session
.getHost()).getAbsolute(),
2838 null, this.window
, this.id(),
2839 Foundation
.selector("syncPanelDidEnd:returnCode:contextInfo:"), null //context info
2843 public void syncPanelDidEnd_returnCode_contextInfo(final NSOpenPanel sheet
, final int returncode
, final ID contextInfo
) {
2844 sheet
.orderOut(this.id());
2845 if(returncode
== SheetCallback
.DEFAULT_OPTION
) {
2846 if(sheet
.filename() != null) {
2847 final Local target
= LocalFactory
.get(sheet
.filename());
2848 new UploadDirectoryFinder().save(session
.getHost(), target
.getParent());
2849 final Path selected
;
2850 if(this.getSelectionCount() == 1 && this.getSelectedPath().isDirectory()) {
2851 selected
= this.getSelectedPath();
2854 selected
= this.workdir();
2856 this.transfer(new SyncTransfer(session
.getHost(), new TransferItem(selected
, target
)));
2862 public void downloadButtonClicked(final ID sender
) {
2863 final List
<TransferItem
> downloads
= new ArrayList
<TransferItem
>();
2864 final Local folder
= new DownloadDirectoryFinder().find(session
.getHost());
2865 for(Path file
: this.getSelectedPaths()) {
2866 downloads
.add(new TransferItem(
2867 file
, LocalFactory
.get(folder
, file
.getName())));
2869 this.transfer(new DownloadTransfer(session
.getHost(), downloads
), Collections
.<Path
>emptyList());
2872 private NSOpenPanel uploadPanel
;
2874 private NSButton uploadPanelHiddenFilesCheckbox
;
2877 public void uploadButtonClicked(final ID sender
) {
2878 uploadPanel
= NSOpenPanel
.openPanel();
2879 uploadPanel
.setCanChooseDirectories(true);
2880 uploadPanel
.setCanChooseFiles(session
.getFeature(Touch
.class).isSupported(
2881 new UploadTargetFinder(workdir
).find(this.getSelectedPath())
2883 uploadPanel
.setCanCreateDirectories(false);
2884 uploadPanel
.setTreatsFilePackagesAsDirectories(true);
2885 uploadPanel
.setAllowsMultipleSelection(true);
2886 uploadPanel
.setPrompt(LocaleFactory
.localizedString("Upload", "Transfer"));
2887 if(uploadPanel
.respondsToSelector(Foundation
.selector("setShowsHiddenFiles:"))) {
2888 uploadPanelHiddenFilesCheckbox
= NSButton
.buttonWithFrame(new NSRect(0, 0));
2889 uploadPanelHiddenFilesCheckbox
.setTitle(LocaleFactory
.localizedString("Show Hidden Files"));
2890 uploadPanelHiddenFilesCheckbox
.setTarget(this.id());
2891 uploadPanelHiddenFilesCheckbox
.setAction(Foundation
.selector("uploadPanelSetShowHiddenFiles:"));
2892 uploadPanelHiddenFilesCheckbox
.setButtonType(NSButton
.NSSwitchButton
);
2893 uploadPanelHiddenFilesCheckbox
.setState(NSCell
.NSOffState
);
2894 uploadPanelHiddenFilesCheckbox
.sizeToFit();
2895 uploadPanel
.setAccessoryView(uploadPanelHiddenFilesCheckbox
);
2897 uploadPanel
.beginSheetForDirectory(new UploadDirectoryFinder().find(session
.getHost()).getAbsolute(),
2900 Foundation
.selector("uploadPanelDidEnd:returnCode:contextInfo:"),
2904 public void uploadPanelSetShowHiddenFiles(ID sender
) {
2905 uploadPanel
.setShowsHiddenFiles(uploadPanelHiddenFilesCheckbox
.state() == NSCell
.NSOnState
);
2908 public void uploadPanelDidEnd_returnCode_contextInfo(final NSOpenPanel sheet
, final int returncode
, final ID contextInfo
) {
2909 sheet
.orderOut(this.id());
2910 if(returncode
== SheetCallback
.DEFAULT_OPTION
) {
2911 final Path destination
= new UploadTargetFinder(workdir
).find(this.getSelectedPath());
2912 // Selected files on the local filesystem
2913 final NSArray selected
= sheet
.filenames();
2914 final NSEnumerator iterator
= selected
.objectEnumerator();
2915 final List
<TransferItem
> uploads
= new ArrayList
<TransferItem
>();
2917 while((next
= iterator
.nextObject()) != null) {
2918 final Local local
= LocalFactory
.get(next
.toString());
2919 new UploadDirectoryFinder().save(session
.getHost(), local
.getParent());
2920 uploads
.add(new TransferItem(
2921 new Path(destination
, local
.getName(),
2922 local
.isDirectory() ? EnumSet
.of(Path
.Type
.directory
) : EnumSet
.of(Path
.Type
.file
)), local
2925 this.transfer(new UploadTransfer(session
.getHost(), uploads
));
2928 uploadPanelHiddenFilesCheckbox
= null;
2931 protected void transfer(final Transfer transfer
) {
2932 final List
<Path
> selected
= new ArrayList
<Path
>();
2933 for(TransferItem i
: transfer
.getRoots()) {
2934 selected
.add(i
.remote
);
2936 this.transfer(transfer
, selected
);
2940 * Transfers the files either using the queue or using the browser session if #connection.pool.max is 1
2942 * @param transfer Transfer Operation
2944 protected void transfer(final Transfer transfer
, final List
<Path
> selected
) {
2945 // Determine from current browser session if new connection should be opened for transfers
2946 this.transfer(transfer
, selected
, session
.getTransferType().equals(Host
.TransferType
.browser
));
2950 * @param transfer Transfer Operation
2951 * @param browser Transfer in browser window
2953 protected void transfer(final Transfer transfer
, final List
<Path
> selected
, boolean browser
) {
2954 final TransferCallback callback
= new TransferCallback() {
2956 public void complete(final Transfer transfer
) {
2957 invoke(new WindowMainAction(BrowserController
.this) {
2960 reload(selected
, selected
);
2966 this.background(new TransferBackgroundAction(this, session
, cache
, new TransferAdapter() {
2968 public void progress(final TransferProgress status
) {
2969 message(status
.getProgress());
2971 }, transfer
, new TransferOptions()) {
2973 public void finish() {
2974 if(transfer
.isComplete()) {
2975 callback
.complete(transfer
);
2982 TransferControllerFactory
.get().start(transfer
, new TransferOptions(), callback
);
2987 public void insideButtonClicked(final ID sender
) {
2988 final Path selected
= this.getSelectedPath(); //first row selected
2989 if(null == selected
) {
2992 if(selected
.isDirectory()) {
2993 this.setWorkdir(selected
);
2995 else if(selected
.isFile() || this.getSelectionCount() > 1) {
2996 if(preferences
.getBoolean("browser.doubleclick.edit")) {
2997 this.editButtonClicked(null);
3000 this.downloadButtonClicked(null);
3006 public void connectButtonClicked(final ID sender
) {
3007 final SheetController controller
= ConnectionControllerFactory
.create(this);
3008 this.addListener(new WindowListener() {
3010 public void windowWillClose() {
3011 controller
.invalidate();
3014 controller
.beginSheet();
3018 public void disconnectButtonClicked(final ID sender
) {
3019 if(this.isActivityRunning()) {
3020 // Remove all pending actions
3021 for(BackgroundAction action
: this.getActions().toArray(
3022 new BackgroundAction
[this.getActions().size()])) {
3026 this.disconnect(new Runnable() {
3029 if(preferences
.getBoolean("browser.disconnect.bookmarks.show")) {
3033 selectBrowser(preferences
.getInteger("browser.view"));
3040 public void showHiddenFilesClicked(final NSMenuItem sender
) {
3041 if(sender
.state() == NSCell
.NSOnState
) {
3042 this.setShowHiddenFiles(false);
3043 sender
.setState(NSCell
.NSOffState
);
3045 else if(sender
.state() == NSCell
.NSOffState
) {
3046 this.setShowHiddenFiles(true);
3047 sender
.setState(NSCell
.NSOnState
);
3049 if(this.isMounted()) {
3055 * @return This browser's session or null if not mounted
3057 public Session
<?
> getSession() {
3061 public Cache
<Path
> getCache() {
3066 * @return true if the remote file system has been mounted
3068 public boolean isMounted() {
3069 return session
!= null && workdir
!= null;
3073 * @return true if mounted and the connection to the server is alive
3075 public boolean isConnected() {
3076 if(this.isMounted()) {
3077 return session
.isConnected();
3085 * Indicates whether the receiver can send and receive the specified pasteboard types.
3087 * Either sendType or returnType—but not both—may be empty. If sendType is empty,
3088 * the service doesn’t require input from the application requesting the service.
3089 * If returnType is empty, the service doesn’t return data.
3091 * @param sendType The pasteboard type the application needs to send.
3092 * @param returnType The pasteboard type the application needs to receive.
3093 * @return The object that can send and receive the specified types or nil
3094 * if the receiver knows of no object that can send and receive data of that type.
3096 public ID
validRequestorForSendType_returnType(String sendType
, String returnType
) {
3097 log
.debug("validRequestorForSendType_returnType:" + sendType
+ "," + returnType
);
3098 if(StringUtils
.isNotEmpty(sendType
)) {
3099 // Cannot send any data type
3102 if(StringUtils
.isNotEmpty(returnType
)) {
3103 // Can receive filenames
3104 if(NSPasteboard
.FilenamesPboardType
.equals(sendType
)) {
3114 * Reads data from the pasteboard and uses it to replace the current selection.
3116 * @param pboard Pasteboard
3117 * @return YES if your implementation was able to read the pasteboard data successfully; otherwise, NO.
3119 public boolean readSelectionFromPasteboard(NSPasteboard pboard
) {
3120 return this.upload(pboard
);
3126 * Writes the current selection to the pasteboard.
3128 * @param pboard Pasteboard
3129 * @param types Types in pasteboard
3130 * @return YES if your implementation was able to write one or more types to the pasteboard; otherwise, NO.
3132 public boolean writeSelectionToPasteboard_types(NSPasteboard pboard
, NSArray types
) {
3137 public void copy(final ID sender
) {
3139 pasteboard
.setCopy(true);
3140 final List
<Path
> s
= this.getSelectedPaths();
3142 // Writing data for private use when the item gets dragged to the transfer queue.
3145 final NSPasteboard clipboard
= NSPasteboard
.generalPasteboard();
3147 s
.add(this.workdir());
3149 clipboard
.declareTypes(NSArray
.arrayWithObject(
3150 NSString
.stringWithString(NSPasteboard
.StringPboardType
)), null);
3151 StringBuilder copy
= new StringBuilder();
3152 for(Iterator
<Path
> i
= s
.iterator(); i
.hasNext(); ) {
3153 copy
.append(i
.next().getAbsolute());
3158 if(!clipboard
.setStringForType(copy
.toString(), NSPasteboard
.StringPboardType
)) {
3159 log
.error("Error writing to NSPasteboard.StringPboardType.");
3164 public void cut(final ID sender
) {
3166 pasteboard
.setCut(true);
3167 for(Path s
: this.getSelectedPaths()) {
3168 // Writing data for private use when the item gets dragged to the transfer queue.
3171 final NSPasteboard clipboard
= NSPasteboard
.generalPasteboard();
3172 clipboard
.declareTypes(NSArray
.arrayWithObject(NSString
.stringWithString(NSPasteboard
.StringPboardType
)), null);
3173 if(!clipboard
.setStringForType(this.getSelectedPath().getAbsolute(), NSPasteboard
.StringPboardType
)) {
3174 log
.error("Error writing to NSPasteboard.StringPboardType.");
3179 public void paste(final ID sender
) {
3180 if(pasteboard
.isEmpty()) {
3181 NSPasteboard pboard
= NSPasteboard
.generalPasteboard();
3182 this.upload(pboard
);
3185 final Map
<Path
, Path
> files
= new HashMap
<Path
, Path
>();
3186 final Path parent
= this.workdir();
3187 for(final Path next
: pasteboard
) {
3188 Path renamed
= new Path(parent
, next
.getName(), next
.getType());
3189 files
.put(next
, renamed
);
3192 if(pasteboard
.isCut()) {
3193 this.renamePaths(files
);
3195 if(pasteboard
.isCopy()) {
3196 this.duplicatePaths(files
);
3202 * @param pboard Pasteboard with filenames
3203 * @return True if filenames are found in pasteboard and upload has started
3205 private boolean upload(NSPasteboard pboard
) {
3206 if(!this.isMounted()) {
3209 if(pboard
.availableTypeFromArray(NSArray
.arrayWithObject(NSPasteboard
.FilenamesPboardType
)) != null) {
3210 NSObject o
= pboard
.propertyListForType(NSPasteboard
.FilenamesPboardType
);
3212 if(o
.isKindOfClass(Rococoa
.createClass("NSArray", NSArray
._Class
.class))) {
3213 final NSArray elements
= Rococoa
.cast(o
, NSArray
.class);
3214 final Path workdir
= this.workdir();
3215 final List
<TransferItem
> uploads
= new ArrayList
<TransferItem
>();
3216 for(int i
= 0; i
< elements
.count().intValue(); i
++) {
3217 final Local local
= LocalFactory
.get(elements
.objectAtIndex(new NSUInteger(i
)).toString());
3218 uploads
.add(new TransferItem(new Path(workdir
, local
.getName(),
3219 local
.isDirectory() ? EnumSet
.of(Path
.Type
.directory
) : EnumSet
.of(Path
.Type
.file
)), local
));
3221 this.transfer(new UploadTransfer(session
.getHost(), uploads
));
3229 public void openTerminalButtonClicked(final ID sender
) {
3230 Path workdir
= null;
3231 if(this.getSelectionCount() == 1) {
3232 Path selected
= this.getSelectedPath();
3233 if(selected
.isDirectory()) {
3237 if(null == workdir
) {
3238 workdir
= this.workdir();
3241 TerminalServiceFactory
.get().open(session
.getHost(), workdir
);
3243 catch(AccessDeniedException e
) {
3244 this.alert(session
.getHost(), e
, new StringBuilder());
3249 public void archiveMenuClicked(final NSMenuItem sender
) {
3250 final Archive archive
= Archive
.forName(sender
.representedObject());
3251 this.archiveClicked(archive
);
3255 public void archiveButtonClicked(final NSToolbarItem sender
) {
3256 this.archiveClicked(Archive
.TARGZ
);
3260 * @param archive Archive format
3262 private void archiveClicked(final Archive archive
) {
3263 final List
<Path
> changed
= this.getSelectedPaths();
3264 this.checkOverwrite(Collections
.singletonList(archive
.getArchive(changed
)), new DefaultMainAction() {
3267 background(new BrowserControllerBackgroundAction(BrowserController
.this) {
3269 public Boolean
run() throws BackgroundException
{
3270 final Compress feature
= BrowserController
.this.session
.getFeature(Compress
.class);
3271 feature
.archive(archive
, workdir
, changed
, this, transcript
);
3276 public void cleanup() {
3279 reload(changed
, Collections
.singletonList(archive
.getArchive(changed
)));
3283 public String
getActivity() {
3284 return archive
.getCompressCommand(workdir
, changed
);
3292 public void unarchiveButtonClicked(final ID sender
) {
3293 final List
<Path
> expanded
= new ArrayList
<Path
>();
3294 final List
<Path
> selected
= this.getSelectedPaths();
3295 for(final Path s
: selected
) {
3296 final Archive archive
= Archive
.forName(s
.getName());
3297 if(null == archive
) {
3300 this.checkOverwrite(archive
.getExpanded(Collections
.singletonList(s
)), new DefaultMainAction() {
3303 background(new BrowserControllerBackgroundAction(BrowserController
.this) {
3305 public Boolean
run() throws BackgroundException
{
3306 final Compress feature
= BrowserController
.this.session
.getFeature(Compress
.class);
3307 feature
.unarchive(archive
, s
, this, transcript
);
3312 public void cleanup() {
3314 expanded
.addAll(archive
.getExpanded(Collections
.singletonList(s
)));
3316 reload(selected
, expanded
);
3320 public String
getActivity() {
3321 return archive
.getDecompressCommand(s
);
3330 * Accessor to the working directory
3332 * @return The current working directory or null if no file system is mounted
3334 protected Path
workdir() {
3338 public void setWorkdir(final Path directory
) {
3339 this.setWorkdir(directory
, Collections
.<Path
>emptyList());
3342 public void setWorkdir(final Path directory
, Path selected
) {
3343 this.setWorkdir(directory
, Collections
.singletonList(selected
));
3347 * Sets the current working directory. This will update the path selection menu and also add this path to the browsing history.
3349 * @param directory The new working directory to display or null to detach any working directory from the browser
3350 * @param selected Selected files in browser
3352 public void setWorkdir(final Path directory
, final List
<Path
> selected
) {
3353 if(log
.isDebugEnabled()) {
3354 log
.debug(String
.format("Set working directory to %s", directory
));
3356 // Remove any custom file filter
3357 this.setPathFilter(null);
3358 final NSTableView browser
= this.getSelectedBrowserView();
3359 window
.endEditingFor(browser
);
3360 // Update the working directory if listing is successful
3361 workdir
= directory
;
3362 if(this.isMounted()) {
3363 this.reload(Collections
.singleton(directory
), selected
, false);
3366 this.reload(Collections
.<Path
>emptySet(), selected
, false);
3368 this.setNavigation(this.isMounted());
3371 private void setNavigation(boolean enabled
) {
3373 searchField
.setStringValue(StringUtils
.EMPTY
);
3375 pathPopupButton
.removeAllItems();
3377 // Update the current working directory
3378 navigation
.add(workdir
);
3381 this.addNavigation(p
);
3385 this.addNavigation(p
);
3387 pathPopupButton
.setEnabled(enabled
);
3388 navigationButton
.setEnabled_forSegment(enabled
&& navigation
.getBack().size() > 1, NAVIGATION_LEFT_SEGMENT_BUTTON
);
3389 navigationButton
.setEnabled_forSegment(enabled
&& navigation
.getForward().size() > 0, NAVIGATION_RIGHT_SEGMENT_BUTTON
);
3390 upButton
.setEnabled_forSegment(enabled
&& !workdir
.isRoot(), NAVIGATION_UP_SEGMENT_BUTTON
);
3393 private void addNavigation(final Path p
) {
3394 pathPopupButton
.addItemWithTitle(p
.getAbsolute());
3395 pathPopupButton
.lastItem().setRepresentedObject(p
.getAbsolute());
3397 pathPopupButton
.lastItem().setImage(IconCacheFactory
.<NSImage
>get().volumeIcon(session
.getHost().getProtocol(), 16));
3400 pathPopupButton
.lastItem().setImage(IconCacheFactory
.<NSImage
>get().fileIcon(p
, 16));
3405 * Initializes a session for the passed host. Setting up the listeners and adding any callback
3406 * controllers needed for login, trust management and hostkey verification.
3408 * @param host Bookmark
3409 * @return A session object bound to this browser controller
3411 private Session
init(final Host host
) {
3412 session
= SessionFactory
.create(host
,
3413 new KeychainX509TrustManager(new DefaultTrustManagerHostnameCallback(host
)),
3414 new KeychainX509KeyManager());
3417 pasteboard
= PathPasteboardFactory
.getPasteboard(session
);
3418 this.setWorkdir(null);
3419 this.setEncoding(session
.getEncoding());
3424 * Open connection in browser
3426 * @param host Bookmark
3428 public void mount(final Host host
) {
3429 if(log
.isDebugEnabled()) {
3430 log
.debug(String
.format("Mount session for %s", host
));
3432 this.unmount(new Runnable() {
3435 // The browser has no session, we are allowed to proceed
3436 // Initialize the browser with the new session attaching all listeners
3437 final Session session
= init(host
);
3438 background(new WorkerBackgroundAction
<Path
>(BrowserController
.this, session
, cache
,
3439 new MountWorker(session
, cache
, listener
) {
3441 public void cleanup(final Path workdir
) {
3442 if(null == workdir
) {
3446 // Update status icon
3447 bookmarkTable
.setNeedsDisplay();
3448 // Set the working directory
3449 setWorkdir(workdir
);
3451 selectBrowser(preferences
.getInteger("browser.view"));
3452 // Set the window title
3453 window
.setRepresentedFilename(HistoryCollection
.defaultCollection().getFile(host
).getAbsolute());
3454 if(preferences
.getBoolean("browser.disconnect.confirm")) {
3455 window
.setDocumentEdited(true);
3457 securityLabel
.setImage(session
.isSecured() ? IconCacheFactory
.<NSImage
>get().iconNamed("locked.tiff")
3458 : IconCacheFactory
.<NSImage
>get().iconNamed("unlocked.tiff"));
3459 securityLabel
.setEnabled(session
instanceof SSLSession
);
3465 public void init() {
3467 window
.setTitle(BookmarkNameProvider
.toString(host
, true));
3468 window
.setRepresentedFilename(StringUtils
.EMPTY
);
3469 // Update status icon
3470 bookmarkTable
.setNeedsDisplay();
3480 * @return True if succeeded
3482 public boolean unmount() {
3483 return this.unmount(new Runnable() {
3492 * @param disconnected Callback after the session has been disconnected
3493 * @return True if the unmount process has finished, false if the user has to agree first
3494 * to close the connection
3496 public boolean unmount(final Runnable disconnected
) {
3497 return this.unmount(new SheetCallback() {
3499 public void callback(int returncode
) {
3500 if(returncode
== DEFAULT_OPTION
) {
3501 unmountImpl(disconnected
);
3508 * @param callback Confirmation callback
3509 * @param disconnected Action to run after disconnected
3510 * @return True if succeeded
3512 public boolean unmount(final SheetCallback callback
, final Runnable disconnected
) {
3513 if(log
.isDebugEnabled()) {
3514 log
.debug(String
.format("Unmount session %s", session
));
3516 if(this.isConnected() || this.isActivityRunning()) {
3517 if(preferences
.getBoolean("browser.disconnect.confirm")) {
3518 // Defer the unmount to the callback function
3519 final NSAlert alert
= NSAlert
.alert(
3520 MessageFormat
.format(LocaleFactory
.localizedString("Disconnect from {0}"), session
.getHost().getHostname()), //title
3521 LocaleFactory
.localizedString("The connection will be closed."), // message
3522 LocaleFactory
.localizedString("Disconnect"), // defaultbutton
3523 LocaleFactory
.localizedString("Cancel"), // alternate button
3526 alert
.setShowsSuppressionButton(true);
3527 alert
.suppressionButton().setTitle(LocaleFactory
.localizedString("Don't ask again", "Configuration"));
3528 this.alert(alert
, new SheetCallback() {
3530 public void callback(int returncode
) {
3531 if(alert
.suppressionButton().state() == NSCell
.NSOnState
) {
3532 // Never show again.
3533 preferences
.setProperty("browser.disconnect.confirm", false);
3535 callback
.callback(returncode
);
3542 this.unmountImpl(disconnected
);
3543 // Unmount succeeded
3548 * @param disconnected Action to run after disconnected
3550 private void unmountImpl(final Runnable disconnected
) {
3551 final List
<Editor
> list
= editors
;
3552 this.disconnect(new Runnable() {
3556 for(Editor e
: list
) {
3559 window
.setTitle(preferences
.getProperty("application.name"));
3560 window
.setRepresentedFilename(StringUtils
.EMPTY
);
3569 * Unmount this session
3571 private void disconnect(final Runnable disconnected
) {
3572 final InfoController c
= InfoControllerFactory
.get(BrowserController
.this);
3576 if(session
!= null) {
3577 this.background(new WorkerBackgroundAction
<Void
>(this, session
, cache
, new DisconnectWorker(session
)) {
3579 public void prepare() throws ConnectionCanceledException
{
3580 if(!session
.isConnected()) {
3581 throw new ConnectionCanceledException();
3587 protected boolean connect(Session session
) throws BackgroundException
{
3592 public void cleanup() {
3594 window
.setDocumentEdited(false);
3605 public void printDocument(final ID sender
) {
3606 this.print(this.getSelectedBrowserView());
3610 * @param app Singleton
3611 * @return NSApplication.TerminateLater if the application should not yet be terminated
3613 public static NSUInteger
applicationShouldTerminate(final NSApplication app
) {
3614 // Determine if there are any open connections
3615 for(final BrowserController controller
: MainController
.getBrowsers()) {
3616 if(!controller
.unmount(new SheetCallback() {
3618 public void callback(final int returncode
) {
3619 if(returncode
== DEFAULT_OPTION
) { //Disconnect
3620 controller
.window().close();
3621 if(NSApplication
.NSTerminateNow
.equals(BrowserController
.applicationShouldTerminate(app
))) {
3622 app
.replyToApplicationShouldTerminate(true);
3626 app
.replyToApplicationShouldTerminate(false);
3637 return NSApplication
.NSTerminateCancel
;
3640 return NSApplication
.NSTerminateNow
;
3644 public boolean windowShouldClose(final NSWindow sender
) {
3645 return this.unmount(new Runnable() {
3654 * @param item Menu item
3655 * @return True if the menu should be enabled
3657 public boolean validateMenuItem(NSMenuItem item
) {
3658 final Selector action
= item
.action();
3659 if(action
.equals(Foundation
.selector("paste:"))) {
3660 final String title
= "Paste {0}";
3661 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
), StringUtils
.EMPTY
).trim());
3662 if(this.isMounted()) {
3663 if(pasteboard
.isEmpty()) {
3664 if(NSPasteboard
.generalPasteboard().availableTypeFromArray(NSArray
.arrayWithObject(NSPasteboard
.FilenamesPboardType
)) != null) {
3665 NSObject o
= NSPasteboard
.generalPasteboard().propertyListForType(NSPasteboard
.FilenamesPboardType
);
3667 if(o
.isKindOfClass(Rococoa
.createClass("NSArray", NSArray
._Class
.class))) {
3668 final NSArray elements
= Rococoa
.cast(o
, NSArray
.class);
3669 if(elements
.count().intValue() == 1) {
3670 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
),
3671 "\"" + elements
.objectAtIndex(new NSUInteger(0)) + "\"").trim());
3674 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
),
3675 MessageFormat
.format(LocaleFactory
.localizedString("{0} Files"),
3676 String
.valueOf(elements
.count().intValue()))
3684 if(pasteboard
.size() == 1) {
3685 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
),
3686 "\"" + pasteboard
.get(0).getName() + "\"").trim());
3689 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
),
3690 MessageFormat
.format(LocaleFactory
.localizedString("{0} Files"), String
.valueOf(pasteboard
.size()))).trim());
3695 else if(action
.equals(Foundation
.selector("cut:")) || action
.equals(Foundation
.selector("copy:"))) {
3696 String title
= null;
3697 if(action
.equals(Foundation
.selector("cut:"))) {
3700 else if(action
.equals(Foundation
.selector("copy:"))) {
3703 if(this.isMounted()) {
3704 int count
= this.getSelectionCount();
3706 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
), StringUtils
.EMPTY
).trim());
3708 else if(1 == count
) {
3709 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
),
3710 "\"" + this.getSelectedPath().getName() + "\"").trim());
3713 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
),
3714 MessageFormat
.format(LocaleFactory
.localizedString("{0} Files"), String
.valueOf(this.getSelectionCount()))).trim());
3718 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
), StringUtils
.EMPTY
).trim());
3721 else if(action
.equals(Foundation
.selector("showHiddenFilesClicked:"))) {
3722 item
.setState(this.getFilter() instanceof NullFilter ? NSCell
.NSOnState
: NSCell
.NSOffState
);
3724 else if(action
.equals(Foundation
.selector("encodingMenuClicked:"))) {
3725 if(this.isMounted()) {
3726 item
.setState(session
.getEncoding().equalsIgnoreCase(
3727 item
.title()) ? NSCell
.NSOnState
: NSCell
.NSOffState
);
3730 item
.setState(preferences
.getProperty("browser.charset.encoding").equalsIgnoreCase(
3731 item
.title()) ? NSCell
.NSOnState
: NSCell
.NSOffState
);
3734 else if(action
.equals(Foundation
.selector("browserSwitchMenuClicked:"))) {
3735 if(item
.tag() == preferences
.getInteger("browser.view")) {
3736 item
.setState(NSCell
.NSOnState
);
3739 item
.setState(NSCell
.NSOffState
);
3742 else if(action
.equals(Foundation
.selector("archiveMenuClicked:"))) {
3743 final Archive archive
= Archive
.forName(item
.representedObject());
3744 item
.setTitle(archive
.getTitle(this.getSelectedPaths()));
3746 else if(action
.equals(Foundation
.selector("quicklookButtonClicked:"))) {
3747 item
.setKeyEquivalent(" ");
3748 item
.setKeyEquivalentModifierMask(0);
3750 return this.validateItem(action
);
3754 * @return Browser tab active
3756 private boolean isBrowser() {
3757 return this.getSelectedTabView() == TAB_LIST_VIEW
3758 || this.getSelectedTabView() == TAB_OUTLINE_VIEW
;
3762 * @return Bookmarks tab active
3764 private boolean isBookmarks() {
3765 return this.getSelectedTabView() == TAB_BOOKMARKS
;
3769 * @param action the method selector
3770 * @return true if the item by that identifier should be enabled
3772 private boolean validateItem(final Selector action
) {
3773 if(action
.equals(Foundation
.selector("cut:"))) {
3774 return this.isBrowser() && this.isMounted() && this.getSelectionCount() > 0;
3776 else if(action
.equals(Foundation
.selector("copy:"))) {
3777 return this.isBrowser() && this.isMounted() && this.getSelectionCount() > 0;
3779 else if(action
.equals(Foundation
.selector("paste:"))) {
3780 if(this.isBrowser() && this.isMounted()) {
3781 if(pasteboard
.isEmpty()) {
3782 NSPasteboard pboard
= NSPasteboard
.generalPasteboard();
3783 if(pboard
.availableTypeFromArray(NSArray
.arrayWithObject(NSPasteboard
.FilenamesPboardType
)) != null) {
3784 Object o
= pboard
.propertyListForType(NSPasteboard
.FilenamesPboardType
);
3795 else if(action
.equals(Foundation
.selector("encodingMenuClicked:"))) {
3796 return this.isBrowser() && !this.isActivityRunning();
3798 else if(action
.equals(Foundation
.selector("connectBookmarkButtonClicked:"))) {
3799 if(this.isBookmarks()) {
3800 return bookmarkTable
.numberOfSelectedRows().intValue() == 1;
3804 else if(action
.equals(Foundation
.selector("addBookmarkButtonClicked:"))) {
3805 if(this.isBookmarks()) {
3806 return bookmarkModel
.getSource().allowsAdd();
3810 else if(action
.equals(Foundation
.selector("deleteBookmarkButtonClicked:"))) {
3811 if(this.isBookmarks()) {
3812 return bookmarkModel
.getSource().allowsDelete() && bookmarkTable
.selectedRow().intValue() != -1;
3816 else if(action
.equals(Foundation
.selector("duplicateBookmarkButtonClicked:"))) {
3817 if(this.isBookmarks()) {
3818 return bookmarkModel
.getSource().allowsEdit() && bookmarkTable
.numberOfSelectedRows().intValue() == 1;
3822 else if(action
.equals(Foundation
.selector("editBookmarkButtonClicked:"))) {
3823 if(this.isBookmarks()) {
3824 return bookmarkModel
.getSource().allowsEdit() && bookmarkTable
.numberOfSelectedRows().intValue() == 1;
3828 else if(action
.equals(Foundation
.selector("editButtonClicked:"))) {
3829 if(this.isBrowser() && this.isMounted() && this.getSelectionCount() > 0) {
3830 final EditorFactory factory
= EditorFactory
.instance();
3831 for(Path s
: this.getSelectedPaths()) {
3832 if(!this.isEditable(s
)) {
3835 // Choose editor for selected file
3836 if(null == factory
.getEditor(s
.getName())) {
3844 else if(action
.equals(Foundation
.selector("editMenuClicked:"))) {
3845 if(this.isBrowser() && this.isMounted() && this.getSelectionCount() > 0) {
3846 for(Path s
: this.getSelectedPaths()) {
3847 if(!this.isEditable(s
)) {
3855 else if(action
.equals(Foundation
.selector("searchButtonClicked:"))) {
3856 return this.isMounted() || this.isBookmarks();
3858 else if(action
.equals(Foundation
.selector("quicklookButtonClicked:"))) {
3859 return this.isBrowser() && this.isMounted() && quicklook
.isAvailable() && this.getSelectionCount() > 0;
3861 else if(action
.equals(Foundation
.selector("openBrowserButtonClicked:"))) {
3862 return this.isMounted();
3864 else if(action
.equals(Foundation
.selector("sendCustomCommandClicked:"))) {
3865 return this.isBrowser() && this.isMounted() && session
.getFeature(Command
.class) != null;
3867 else if(action
.equals(Foundation
.selector("gotoButtonClicked:"))) {
3868 return this.isBrowser() && this.isMounted();
3870 else if(action
.equals(Foundation
.selector("infoButtonClicked:"))) {
3871 return this.isBrowser() && this.isMounted() && this.getSelectionCount() > 0;
3873 else if(action
.equals(Foundation
.selector("createFolderButtonClicked:"))) {
3874 return this.isBrowser() && this.isMounted();
3876 else if(action
.equals(Foundation
.selector("createFileButtonClicked:"))) {
3877 return this.isBrowser() && this.isMounted() && session
.getFeature(Touch
.class).isSupported(
3878 new UploadTargetFinder(workdir
).find(this.getSelectedPath())
3881 else if(action
.equals(Foundation
.selector("uploadButtonClicked:"))) {
3882 return this.isBrowser() && this.isMounted() && session
.getFeature(Touch
.class).isSupported(
3883 new UploadTargetFinder(workdir
).find(this.getSelectedPath())
3886 else if(action
.equals(Foundation
.selector("createSymlinkButtonClicked:"))) {
3887 return this.isBrowser() && this.isMounted() && session
.getFeature(Symlink
.class) != null
3888 && this.getSelectionCount() == 1;
3890 else if(action
.equals(Foundation
.selector("duplicateFileButtonClicked:"))) {
3891 return this.isBrowser() && this.isMounted() && this.getSelectionCount() == 1;
3893 else if(action
.equals(Foundation
.selector("renameFileButtonClicked:"))) {
3894 if(this.isBrowser() && this.isMounted() && this.getSelectionCount() == 1) {
3895 final Path selected
= this.getSelectedPath();
3896 if(null == selected
) {
3899 return session
.getFeature(Move
.class).isSupported(selected
);
3903 else if(action
.equals(Foundation
.selector("deleteFileButtonClicked:"))) {
3904 return this.isBrowser() && this.isMounted() && this.getSelectionCount() > 0;
3906 else if(action
.equals(Foundation
.selector("revertFileButtonClicked:"))) {
3907 if(this.isBrowser() && this.isMounted() && this.getSelectionCount() == 1) {
3908 return session
.getFeature(Versioning
.class) != null;
3912 else if(action
.equals(Foundation
.selector("reloadButtonClicked:"))) {
3913 return this.isBrowser() && this.isMounted();
3915 else if(action
.equals(Foundation
.selector("newBrowserButtonClicked:"))) {
3916 return this.isMounted();
3918 else if(action
.equals(Foundation
.selector("syncButtonClicked:"))) {
3919 return this.isBrowser() && this.isMounted();
3921 else if(action
.equals(Foundation
.selector("downloadAsButtonClicked:"))) {
3922 return this.isBrowser() && this.isMounted() && this.getSelectionCount() == 1;
3924 else if(action
.equals(Foundation
.selector("downloadToButtonClicked:")) || action
.equals(Foundation
.selector("downloadButtonClicked:"))) {
3925 return this.isBrowser() && this.isMounted() && this.getSelectionCount() > 0;
3927 else if(action
.equals(Foundation
.selector("insideButtonClicked:"))) {
3928 return this.isBrowser() && this.isMounted() && this.getSelectionCount() > 0;
3930 else if(action
.equals(Foundation
.selector("upButtonClicked:"))) {
3931 return this.isBrowser() && this.isMounted() && !this.workdir().isRoot();
3933 else if(action
.equals(Foundation
.selector("backButtonClicked:"))) {
3934 return this.isBrowser() && this.isMounted() && navigation
.getBack().size() > 1;
3936 else if(action
.equals(Foundation
.selector("forwardButtonClicked:"))) {
3937 return this.isBrowser() && this.isMounted() && navigation
.getForward().size() > 0;
3939 else if(action
.equals(Foundation
.selector("printDocument:"))) {
3940 return this.isBrowser() && this.isMounted();
3942 else if(action
.equals(Foundation
.selector("disconnectButtonClicked:"))) {
3943 if(this.isBrowser()) {
3944 if(!this.isConnected()) {
3945 return this.isActivityRunning();
3947 return this.isConnected();
3950 else if(action
.equals(Foundation
.selector("gotofolderButtonClicked:"))) {
3951 return this.isBrowser() && this.isMounted();
3953 else if(action
.equals(Foundation
.selector("openTerminalButtonClicked:"))) {
3954 return this.isBrowser() && this.isMounted()
3955 && session
instanceof SFTPSession
&& TerminalServiceFactory
.get() != null;
3957 else if(action
.equals(Foundation
.selector("archiveButtonClicked:")) || action
.equals(Foundation
.selector("archiveMenuClicked:"))) {
3958 if(this.isBrowser() && this.isMounted()) {
3959 if(session
.getFeature(Compress
.class) == null) {
3962 if(this.getSelectionCount() > 0) {
3963 for(Path s
: this.getSelectedPaths()) {
3964 if(s
.isFile() && Archive
.isArchive(s
.getName())) {
3965 // At least one file selected is already an archive. No distinct action possible
3974 else if(action
.equals(Foundation
.selector("unarchiveButtonClicked:"))) {
3975 if(this.isBrowser() && this.isMounted()) {
3976 if(session
.getFeature(Compress
.class) == null) {
3979 if(this.getSelectionCount() > 0) {
3980 for(Path s
: this.getSelectedPaths()) {
3981 if(s
.isDirectory()) {
3984 if(!Archive
.isArchive(s
.getName())) {
3993 return true; // by default everything is enabled
3996 private static final String TOOLBAR_NEW_CONNECTION
= "New Connection";
3997 private static final String TOOLBAR_BROWSER_VIEW
= "Browser View";
3998 private static final String TOOLBAR_TRANSFERS
= "Transfers";
3999 private static final String TOOLBAR_QUICK_CONNECT
= "Quick Connect";
4000 private static final String TOOLBAR_TOOLS
= "Tools";
4001 private static final String TOOLBAR_REFRESH
= "Refresh";
4002 private static final String TOOLBAR_ENCODING
= "Encoding";
4003 private static final String TOOLBAR_SYNCHRONIZE
= "Synchronize";
4004 private static final String TOOLBAR_DOWNLOAD
= "Download";
4005 private static final String TOOLBAR_UPLOAD
= "Upload";
4006 private static final String TOOLBAR_EDIT
= "Edit";
4007 private static final String TOOLBAR_DELETE
= "Delete";
4008 private static final String TOOLBAR_NEW_FOLDER
= "New Folder";
4009 private static final String TOOLBAR_NEW_BOOKMARK
= "New Bookmark";
4010 private static final String TOOLBAR_GET_INFO
= "Get Info";
4011 private static final String TOOLBAR_WEBVIEW
= "Open";
4012 private static final String TOOLBAR_DISCONNECT
= "Disconnect";
4013 private static final String TOOLBAR_TERMINAL
= "Terminal";
4014 private static final String TOOLBAR_ARCHIVE
= "Archive";
4015 private static final String TOOLBAR_QUICKLOOK
= "Quick Look";
4016 private static final String TOOLBAR_LOG
= "Log";
4019 public boolean validateToolbarItem(final NSToolbarItem item
) {
4020 final String identifier
= item
.itemIdentifier();
4021 switch(identifier
) {
4022 case TOOLBAR_EDIT
: {
4023 Application editor
= null;
4024 final Path selected
= this.getSelectedPath();
4025 if(null != selected
) {
4026 if(this.isEditable(selected
)) {
4027 // Choose editor for selected file
4028 final EditorFactory factory
= EditorFactory
.instance();
4029 editor
= factory
.getEditor(selected
.getName());
4032 if(null == editor
) {
4034 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("pencil.tiff", 32));
4037 item
.setImage(IconCacheFactory
.<NSImage
>get().applicationIcon(editor
, 32));
4041 case TOOLBAR_DISCONNECT
:
4042 if(this.isActivityRunning()) {
4043 item
.setLabel(LocaleFactory
.localizedString("Stop"));
4044 item
.setPaletteLabel(LocaleFactory
.localizedString("Stop"));
4045 item
.setToolTip(LocaleFactory
.localizedString("Cancel current operation in progress"));
4046 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("stop", 32));
4049 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_DISCONNECT
));
4050 item
.setPaletteLabel(LocaleFactory
.localizedString(TOOLBAR_DISCONNECT
));
4051 item
.setToolTip(LocaleFactory
.localizedString("Disconnect from server"));
4052 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("eject.tiff", 32));
4055 case TOOLBAR_ARCHIVE
: {
4056 final Path selected
= getSelectedPath();
4057 if(null != selected
) {
4058 if(Archive
.isArchive(selected
.getName())) {
4059 item
.setLabel(LocaleFactory
.localizedString("Unarchive", "Archive"));
4060 item
.setPaletteLabel(LocaleFactory
.localizedString("Unarchive"));
4061 item
.setAction(Foundation
.selector("unarchiveButtonClicked:"));
4064 item
.setLabel(LocaleFactory
.localizedString("Archive", "Archive"));
4065 item
.setPaletteLabel(LocaleFactory
.localizedString("Archive"));
4066 item
.setAction(Foundation
.selector("archiveButtonClicked:"));
4072 return validateItem(item
.action());
4076 * Keep reference to weak toolbar items. A toolbar may ask again for a kind of toolbar
4077 * item already supplied to it, in which case this method may return the same toolbar
4078 * item it returned before
4080 private Map
<String
, NSToolbarItem
> toolbarItems
4081 = new HashMap
<String
, NSToolbarItem
>();
4084 public NSToolbarItem
toolbar_itemForItemIdentifier_willBeInsertedIntoToolbar(NSToolbar toolbar
, final String itemIdentifier
, boolean inserted
) {
4085 if(log
.isDebugEnabled()) {
4086 log
.debug("toolbar_itemForItemIdentifier_willBeInsertedIntoToolbar:" + itemIdentifier
);
4088 if(!toolbarItems
.containsKey(itemIdentifier
)) {
4089 toolbarItems
.put(itemIdentifier
, NSToolbarItem
.itemWithIdentifier(itemIdentifier
));
4091 final NSToolbarItem item
= toolbarItems
.get(itemIdentifier
);
4092 switch(itemIdentifier
) {
4093 case TOOLBAR_BROWSER_VIEW
:
4094 item
.setLabel(LocaleFactory
.localizedString("View"));
4095 item
.setPaletteLabel(LocaleFactory
.localizedString("View"));
4096 item
.setToolTip(LocaleFactory
.localizedString("Switch Browser View"));
4097 item
.setView(browserSwitchView
);
4098 // Add a menu representation for text mode of toolbar
4099 NSMenuItem viewMenu
= NSMenuItem
.itemWithTitle(LocaleFactory
.localizedString("View"), null, StringUtils
.EMPTY
);
4100 NSMenu viewSubmenu
= NSMenu
.menu();
4101 viewSubmenu
.addItemWithTitle_action_keyEquivalent(LocaleFactory
.localizedString("List"),
4102 Foundation
.selector("browserSwitchMenuClicked:"), StringUtils
.EMPTY
);
4103 viewSubmenu
.itemWithTitle(LocaleFactory
.localizedString("List")).setTag(0);
4104 viewSubmenu
.addItemWithTitle_action_keyEquivalent(LocaleFactory
.localizedString("Outline"),
4105 Foundation
.selector("browserSwitchMenuClicked:"), StringUtils
.EMPTY
);
4106 viewSubmenu
.itemWithTitle(LocaleFactory
.localizedString("Outline")).setTag(1);
4107 viewMenu
.setSubmenu(viewSubmenu
);
4108 item
.setMenuFormRepresentation(viewMenu
);
4110 case TOOLBAR_NEW_CONNECTION
:
4111 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_NEW_CONNECTION
));
4112 item
.setPaletteLabel(LocaleFactory
.localizedString(TOOLBAR_NEW_CONNECTION
));
4113 item
.setToolTip(LocaleFactory
.localizedString("Connect to server"));
4114 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("connect.tiff", 32));
4115 item
.setTarget(this.id());
4116 item
.setAction(Foundation
.selector("connectButtonClicked:"));
4118 case TOOLBAR_TRANSFERS
:
4119 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_TRANSFERS
));
4120 item
.setPaletteLabel(LocaleFactory
.localizedString(TOOLBAR_TRANSFERS
));
4121 item
.setToolTip(LocaleFactory
.localizedString("Show Transfers window"));
4122 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("queue.tiff", 32));
4123 item
.setAction(Foundation
.selector("showTransferQueueClicked:"));
4126 item
.setLabel(LocaleFactory
.localizedString("Action"));
4127 item
.setPaletteLabel(LocaleFactory
.localizedString("Action"));
4128 if(inserted
|| !Factory
.Platform
.osversion
.matches("10\\.5.*")) {
4129 final NSInteger index
= new NSInteger(0);
4130 actionPopupButton
.insertItemWithTitle_atIndex(StringUtils
.EMPTY
, index
);
4131 actionPopupButton
.itemAtIndex(index
).setImage(IconCacheFactory
.<NSImage
>get().iconNamed("gear.tiff"));
4132 item
.setView(actionPopupButton
);
4133 // Add a menu representation for text mode of toolbar
4134 NSMenuItem toolMenu
= NSMenuItem
.itemWithTitle(LocaleFactory
.localizedString("Action"), null, StringUtils
.EMPTY
);
4135 NSMenu toolSubmenu
= NSMenu
.menu();
4136 for(int i
= 1; i
< actionPopupButton
.menu().numberOfItems().intValue(); i
++) {
4137 NSMenuItem template
= actionPopupButton
.menu().itemAtIndex(new NSInteger(i
));
4138 toolSubmenu
.addItem(NSMenuItem
.itemWithTitle(template
.title(),
4140 template
.keyEquivalent()));
4142 toolMenu
.setSubmenu(toolSubmenu
);
4143 item
.setMenuFormRepresentation(toolMenu
);
4146 NSToolbarItem temporary
= NSToolbarItem
.itemWithIdentifier(itemIdentifier
);
4147 temporary
.setPaletteLabel(LocaleFactory
.localizedString("Action"));
4148 temporary
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("advanced.tiff", 32));
4152 case TOOLBAR_QUICK_CONNECT
:
4153 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_QUICK_CONNECT
));
4154 item
.setPaletteLabel(LocaleFactory
.localizedString(TOOLBAR_QUICK_CONNECT
));
4155 item
.setToolTip(LocaleFactory
.localizedString("Connect to server"));
4156 item
.setView(quickConnectPopup
);
4158 case TOOLBAR_ENCODING
:
4159 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_ENCODING
));
4160 item
.setPaletteLabel(LocaleFactory
.localizedString(TOOLBAR_ENCODING
));
4161 item
.setToolTip(LocaleFactory
.localizedString("Character Encoding"));
4162 item
.setView(this.encodingPopup
);
4163 // Add a menu representation for text mode of toolbar
4164 NSMenuItem encodingMenu
= NSMenuItem
.itemWithTitle(LocaleFactory
.localizedString(TOOLBAR_ENCODING
),
4165 Foundation
.selector("encodingMenuClicked:"), StringUtils
.EMPTY
);
4166 String
[] charsets
= new DefaultCharsetProvider().availableCharsets();
4167 NSMenu charsetMenu
= NSMenu
.menu();
4168 for(String charset
: charsets
) {
4169 charsetMenu
.addItemWithTitle_action_keyEquivalent(charset
, Foundation
.selector("encodingMenuClicked:"), StringUtils
.EMPTY
);
4171 encodingMenu
.setSubmenu(charsetMenu
);
4172 item
.setMenuFormRepresentation(encodingMenu
);
4173 item
.setMinSize(this.encodingPopup
.frame().size
);
4174 item
.setMaxSize(this.encodingPopup
.frame().size
);
4176 case TOOLBAR_REFRESH
:
4177 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_REFRESH
));
4178 item
.setPaletteLabel(LocaleFactory
.localizedString(TOOLBAR_REFRESH
));
4179 item
.setToolTip(LocaleFactory
.localizedString("Refresh directory listing"));
4180 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("reload.tiff", 32));
4181 item
.setTarget(this.id());
4182 item
.setAction(Foundation
.selector("reloadButtonClicked:"));
4184 case TOOLBAR_DOWNLOAD
:
4185 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_DOWNLOAD
));
4186 item
.setPaletteLabel(LocaleFactory
.localizedString(TOOLBAR_DOWNLOAD
));
4187 item
.setToolTip(LocaleFactory
.localizedString("Download file"));
4188 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("download.tiff", 32));
4189 item
.setTarget(this.id());
4190 item
.setAction(Foundation
.selector("downloadButtonClicked:"));
4192 case TOOLBAR_UPLOAD
:
4193 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_UPLOAD
));
4194 item
.setPaletteLabel(LocaleFactory
.localizedString(TOOLBAR_UPLOAD
));
4195 item
.setToolTip(LocaleFactory
.localizedString("Upload local file to the remote host"));
4196 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("upload.tiff", 32));
4197 item
.setTarget(this.id());
4198 item
.setAction(Foundation
.selector("uploadButtonClicked:"));
4200 case TOOLBAR_SYNCHRONIZE
:
4201 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_SYNCHRONIZE
));
4202 item
.setPaletteLabel(LocaleFactory
.localizedString(TOOLBAR_SYNCHRONIZE
));
4203 item
.setToolTip(LocaleFactory
.localizedString("Synchronize files"));
4204 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("sync.tiff", 32));
4205 item
.setTarget(this.id());
4206 item
.setAction(Foundation
.selector("syncButtonClicked:"));
4208 case TOOLBAR_GET_INFO
:
4209 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_GET_INFO
));
4210 item
.setPaletteLabel(LocaleFactory
.localizedString(TOOLBAR_GET_INFO
));
4211 item
.setToolTip(LocaleFactory
.localizedString("Show file attributes"));
4212 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("info.tiff", 32));
4213 item
.setTarget(this.id());
4214 item
.setAction(Foundation
.selector("infoButtonClicked:"));
4216 case TOOLBAR_WEBVIEW
:
4217 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_WEBVIEW
));
4218 item
.setPaletteLabel(LocaleFactory
.localizedString("Open in Web Browser"));
4219 item
.setToolTip(LocaleFactory
.localizedString("Open in Web Browser"));
4220 final Application browser
= SchemeHandlerFactory
.get().getDefaultHandler(Scheme
.http
);
4221 if(Application
.notfound
.equals(browser
)) {
4222 item
.setEnabled(false);
4223 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("notfound.tiff", 32));
4226 item
.setImage(IconCacheFactory
.<NSImage
>get().applicationIcon(browser
, 32));
4228 item
.setTarget(this.id());
4229 item
.setAction(Foundation
.selector("openBrowserButtonClicked:"));
4232 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_EDIT
));
4233 item
.setPaletteLabel(LocaleFactory
.localizedString(TOOLBAR_EDIT
));
4234 item
.setToolTip(LocaleFactory
.localizedString("Edit file in external editor"));
4235 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("pencil.tiff"));
4236 item
.setTarget(this.id());
4237 item
.setAction(Foundation
.selector("editButtonClicked:"));
4238 // Add a menu representation for text mode of toolbar
4239 NSMenuItem toolbarMenu
= NSMenuItem
.itemWithTitle(LocaleFactory
.localizedString(TOOLBAR_EDIT
),
4240 Foundation
.selector("editButtonClicked:"), StringUtils
.EMPTY
);
4241 NSMenu editMenu
= NSMenu
.menu();
4242 editMenu
.setAutoenablesItems(true);
4243 editMenu
.setDelegate(editMenuDelegate
.id());
4244 toolbarMenu
.setSubmenu(editMenu
);
4245 item
.setMenuFormRepresentation(toolbarMenu
);
4247 case TOOLBAR_DELETE
:
4248 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_DELETE
));
4249 item
.setPaletteLabel(LocaleFactory
.localizedString(TOOLBAR_DELETE
));
4250 item
.setToolTip(LocaleFactory
.localizedString("Delete file"));
4251 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("delete.tiff", 32));
4252 item
.setTarget(this.id());
4253 item
.setAction(Foundation
.selector("deleteFileButtonClicked:"));
4255 case TOOLBAR_NEW_FOLDER
:
4256 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_NEW_FOLDER
));
4257 item
.setPaletteLabel(LocaleFactory
.localizedString(TOOLBAR_NEW_FOLDER
));
4258 item
.setToolTip(LocaleFactory
.localizedString("Create New Folder"));
4259 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("newfolder.tiff", 32));
4260 item
.setTarget(this.id());
4261 item
.setAction(Foundation
.selector("createFolderButtonClicked:"));
4263 case TOOLBAR_NEW_BOOKMARK
:
4264 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_NEW_BOOKMARK
));
4265 item
.setPaletteLabel(LocaleFactory
.localizedString(TOOLBAR_NEW_BOOKMARK
));
4266 item
.setToolTip(LocaleFactory
.localizedString("New Bookmark"));
4267 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("bookmark", 32));
4268 item
.setTarget(this.id());
4269 item
.setAction(Foundation
.selector("addBookmarkButtonClicked:"));
4271 case TOOLBAR_DISCONNECT
:
4272 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_DISCONNECT
));
4273 item
.setPaletteLabel(LocaleFactory
.localizedString(TOOLBAR_DISCONNECT
));
4274 item
.setToolTip(LocaleFactory
.localizedString("Disconnect from server"));
4275 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("eject.tiff", 32));
4276 item
.setTarget(this.id());
4277 item
.setAction(Foundation
.selector("disconnectButtonClicked:"));
4279 case TOOLBAR_TERMINAL
:
4280 final ApplicationFinder finder
= ApplicationFinderFactory
.get();
4281 final Application application
4282 = finder
.getDescription(preferences
.getProperty("terminal.bundle.identifier"));
4283 item
.setLabel(application
.getName());
4284 item
.setPaletteLabel(application
.getName());
4285 item
.setImage(IconCacheFactory
.<NSImage
>get().applicationIcon(application
, 32));
4286 item
.setTarget(this.id());
4287 item
.setAction(Foundation
.selector("openTerminalButtonClicked:"));
4289 case TOOLBAR_ARCHIVE
:
4290 item
.setLabel(LocaleFactory
.localizedString("Archive", "Archive"));
4291 item
.setPaletteLabel(LocaleFactory
.localizedString("Archive", "Archive"));
4292 item
.setImage(IconCacheFactory
.<NSImage
>get().applicationIcon(new Application("com.apple.archiveutility"), 32));
4293 item
.setTarget(this.id());
4294 item
.setAction(Foundation
.selector("archiveButtonClicked:"));
4296 case TOOLBAR_QUICKLOOK
:
4297 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_QUICKLOOK
));
4298 item
.setPaletteLabel(LocaleFactory
.localizedString(TOOLBAR_QUICKLOOK
));
4299 if(quicklook
.isAvailable()) {
4300 quicklookButton
= NSButton
.buttonWithFrame(new NSRect(29, 23));
4301 quicklookButton
.setBezelStyle(NSButtonCell
.NSTexturedRoundedBezelStyle
);
4302 quicklookButton
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("NSQuickLookTemplate"));
4303 quicklookButton
.sizeToFit();
4304 quicklookButton
.setTarget(this.id());
4305 quicklookButton
.setAction(Foundation
.selector("quicklookButtonClicked:"));
4306 item
.setView(quicklookButton
);
4309 item
.setEnabled(false);
4310 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("notfound.tiff", 32));
4314 item
.setLabel(LocaleFactory
.localizedString(TOOLBAR_LOG
));
4315 item
.setPaletteLabel(LocaleFactory
.localizedString(TOOLBAR_LOG
));
4316 item
.setToolTip(LocaleFactory
.localizedString("Toggle Log Drawer"));
4317 item
.setImage(IconCacheFactory
.<NSImage
>get().iconNamed("log", 32));
4318 item
.setTarget(this.id());
4319 item
.setAction(Foundation
.selector("toggleLogDrawer:"));
4322 // Returning null will inform the toolbar this kind of item is not supported.
4327 private NSButton quicklookButton
;
4330 * @param toolbar Window toolbar
4331 * @return The default configuration of toolbar items
4334 public NSArray
toolbarDefaultItemIdentifiers(NSToolbar toolbar
) {
4335 return NSArray
.arrayWithObjects(
4336 TOOLBAR_NEW_CONNECTION
,
4337 NSToolbarItem
.NSToolbarSeparatorItemIdentifier
,
4338 TOOLBAR_QUICK_CONNECT
,
4340 NSToolbarItem
.NSToolbarSeparatorItemIdentifier
,
4343 NSToolbarItem
.NSToolbarFlexibleSpaceItemIdentifier
,
4349 * @param toolbar Window toolbar
4350 * @return All available toolbar items
4353 public NSArray
toolbarAllowedItemIdentifiers(NSToolbar toolbar
) {
4354 return NSArray
.arrayWithObjects(
4355 TOOLBAR_NEW_CONNECTION
,
4356 TOOLBAR_BROWSER_VIEW
,
4358 TOOLBAR_QUICK_CONNECT
,
4362 TOOLBAR_SYNCHRONIZE
,
4368 TOOLBAR_NEW_BOOKMARK
,
4376 NSToolbarItem
.NSToolbarCustomizeToolbarItemIdentifier
,
4377 NSToolbarItem
.NSToolbarSpaceItemIdentifier
,
4378 NSToolbarItem
.NSToolbarSeparatorItemIdentifier
,
4379 NSToolbarItem
.NSToolbarFlexibleSpaceItemIdentifier
4384 public NSArray
toolbarSelectableItemIdentifiers(NSToolbar toolbar
) {
4385 return NSArray
.array();
4389 * Overrriden to remove any listeners from the session
4392 public void invalidate() {
4393 if(quicklook
.isAvailable()) {
4394 if(quicklook
.isOpen()) {
4398 bookmarkTable
.setDelegate(null);
4399 bookmarkTable
.setDataSource(null);
4400 bookmarkModel
.invalidate();
4402 browserListView
.setDelegate(null);
4403 browserListView
.setDataSource(null);
4404 browserListModel
.invalidate();
4406 browserOutlineView
.setDelegate(null);
4407 browserOutlineView
.setDataSource(null);
4408 browserOutlineModel
.invalidate();
4410 toolbar
.setDelegate(null);
4411 toolbarItems
.clear();
4413 browserListColumnsFactory
.clear();
4414 browserOutlineColumnsFactory
.clear();
4415 bookmarkTableColumnFactory
.clear();
4417 quickConnectPopup
.setDelegate(null);
4418 quickConnectPopup
.setDataSource(null);
4420 archiveMenu
.setDelegate(null);
4421 editMenu
.setDelegate(null);