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
.Location
;
43 import ch
.cyberduck
.core
.features
.Move
;
44 import ch
.cyberduck
.core
.features
.Touch
;
45 import ch
.cyberduck
.core
.local
.Application
;
46 import ch
.cyberduck
.core
.local
.ApplicationQuitCallback
;
47 import ch
.cyberduck
.core
.local
.BrowserLauncherFactory
;
48 import ch
.cyberduck
.core
.local
.TemporaryFileServiceFactory
;
49 import ch
.cyberduck
.core
.pasteboard
.HostPasteboard
;
50 import ch
.cyberduck
.core
.pasteboard
.PathPasteboard
;
51 import ch
.cyberduck
.core
.pasteboard
.PathPasteboardFactory
;
52 import ch
.cyberduck
.core
.preferences
.Preferences
;
53 import ch
.cyberduck
.core
.preferences
.PreferencesFactory
;
54 import ch
.cyberduck
.core
.resources
.IconCacheFactory
;
55 import ch
.cyberduck
.core
.serializer
.HostDictionary
;
56 import ch
.cyberduck
.core
.ssl
.DefaultTrustManagerHostnameCallback
;
57 import ch
.cyberduck
.core
.ssl
.KeychainX509KeyManager
;
58 import ch
.cyberduck
.core
.ssl
.KeychainX509TrustManager
;
59 import ch
.cyberduck
.core
.ssl
.SSLSession
;
60 import ch
.cyberduck
.core
.threading
.BackgroundAction
;
61 import ch
.cyberduck
.core
.threading
.DefaultMainAction
;
62 import ch
.cyberduck
.core
.threading
.TransferBackgroundAction
;
63 import ch
.cyberduck
.core
.threading
.WorkerBackgroundAction
;
64 import ch
.cyberduck
.core
.transfer
.DisabledTransferErrorCallback
;
65 import ch
.cyberduck
.core
.transfer
.DownloadTransfer
;
66 import ch
.cyberduck
.core
.transfer
.SyncTransfer
;
67 import ch
.cyberduck
.core
.transfer
.Transfer
;
68 import ch
.cyberduck
.core
.transfer
.TransferAction
;
69 import ch
.cyberduck
.core
.transfer
.TransferAdapter
;
70 import ch
.cyberduck
.core
.transfer
.TransferCallback
;
71 import ch
.cyberduck
.core
.transfer
.TransferItem
;
72 import ch
.cyberduck
.core
.transfer
.TransferOptions
;
73 import ch
.cyberduck
.core
.transfer
.TransferProgress
;
74 import ch
.cyberduck
.core
.transfer
.TransferPrompt
;
75 import ch
.cyberduck
.core
.transfer
.UploadTransfer
;
76 import ch
.cyberduck
.core
.worker
.DisconnectWorker
;
77 import ch
.cyberduck
.core
.worker
.MountWorker
;
78 import ch
.cyberduck
.core
.worker
.SessionListWorker
;
79 import ch
.cyberduck
.ui
.browser
.Column
;
80 import ch
.cyberduck
.ui
.browser
.DownloadDirectoryFinder
;
81 import ch
.cyberduck
.ui
.browser
.PathReloadFinder
;
82 import ch
.cyberduck
.ui
.browser
.RegexFilter
;
83 import ch
.cyberduck
.ui
.browser
.SearchFilter
;
84 import ch
.cyberduck
.ui
.browser
.UploadDirectoryFinder
;
85 import ch
.cyberduck
.ui
.browser
.UploadTargetFinder
;
86 import ch
.cyberduck
.ui
.cocoa
.delegate
.ArchiveMenuDelegate
;
87 import ch
.cyberduck
.ui
.cocoa
.delegate
.CopyURLMenuDelegate
;
88 import ch
.cyberduck
.ui
.cocoa
.delegate
.EditMenuDelegate
;
89 import ch
.cyberduck
.ui
.cocoa
.delegate
.OpenURLMenuDelegate
;
90 import ch
.cyberduck
.ui
.cocoa
.delegate
.URLMenuDelegate
;
91 import ch
.cyberduck
.ui
.cocoa
.quicklook
.QLPreviewPanel
;
92 import ch
.cyberduck
.ui
.cocoa
.quicklook
.QLPreviewPanelController
;
93 import ch
.cyberduck
.ui
.cocoa
.quicklook
.QuickLook
;
94 import ch
.cyberduck
.ui
.cocoa
.quicklook
.QuickLookFactory
;
95 import ch
.cyberduck
.ui
.cocoa
.threading
.WindowMainAction
;
96 import ch
.cyberduck
.ui
.cocoa
.view
.BookmarkCell
;
97 import ch
.cyberduck
.ui
.cocoa
.view
.OutlineCell
;
99 import org
.apache
.commons
.collections
.CollectionUtils
;
100 import org
.apache
.commons
.lang3
.StringUtils
;
101 import org
.apache
.log4j
.Logger
;
102 import org
.rococoa
.Foundation
;
103 import org
.rococoa
.ID
;
104 import org
.rococoa
.Rococoa
;
105 import org
.rococoa
.Selector
;
106 import org
.rococoa
.cocoa
.CGFloat
;
107 import org
.rococoa
.cocoa
.foundation
.NSInteger
;
108 import org
.rococoa
.cocoa
.foundation
.NSPoint
;
109 import org
.rococoa
.cocoa
.foundation
.NSRect
;
110 import org
.rococoa
.cocoa
.foundation
.NSSize
;
111 import org
.rococoa
.cocoa
.foundation
.NSUInteger
;
113 import java
.security
.cert
.CertificateException
;
114 import java
.security
.cert
.X509Certificate
;
115 import java
.text
.MessageFormat
;
116 import java
.util
.ArrayList
;
117 import java
.util
.Collections
;
118 import java
.util
.Comparator
;
119 import java
.util
.EnumSet
;
120 import java
.util
.HashMap
;
121 import java
.util
.HashSet
;
122 import java
.util
.Iterator
;
123 import java
.util
.List
;
124 import java
.util
.Locale
;
125 import java
.util
.Map
;
126 import java
.util
.Set
;
131 public class BrowserController
extends WindowController
132 implements ProgressListener
, TranscriptListener
, NSToolbar
.Delegate
, NSMenu
.Validation
, QLPreviewPanelController
{
133 private static Logger log
= Logger
.getLogger(BrowserController
.class);
138 private static final Filter
<Path
> NULL_FILTER
= new NullFilter
<Path
>();
141 * Filter hidden files.
143 private static final Filter
<Path
> HIDDEN_FILTER
= new RegexFilter();
145 private final BookmarkCollection bookmarks
146 = BookmarkCollection
.defaultCollection();
148 private final BrowserToolbarFactory browserToolbarFactory
= new BrowserToolbarFactory(this);
150 private final NSNotificationCenter notificationCenter
= NSNotificationCenter
.defaultCenter();
155 private Session
<?
> session
;
160 private TranscriptController transcript
;
162 private final QuickLook quicklook
= QuickLookFactory
.get();
164 private Preferences preferences
165 = PreferencesFactory
.get();
168 * Hide files beginning with '.'
170 private boolean showHiddenFiles
;
172 private Filter
<Path
> filenameFilter
;
175 if(PreferencesFactory
.get().getBoolean("browser.showHidden")) {
176 this.filenameFilter
= new NullFilter
<Path
>();
177 this.showHiddenFiles
= true;
180 this.filenameFilter
= new RegexFilter();
181 this.showHiddenFiles
= false;
185 private final NSTextFieldCell outlineCellPrototype
= OutlineCell
.outlineCell();
186 private final NSImageCell imageCellPrototype
= NSImageCell
.imageCell();
187 private final NSTextFieldCell textCellPrototype
= NSTextFieldCell
.textFieldCell();
188 private final NSTextFieldCell filenameCellPrototype
= NSTextFieldCell
.textFieldCell();
190 private final TableColumnFactory browserListColumnsFactory
= new TableColumnFactory();
191 private final TableColumnFactory browserOutlineColumnsFactory
= new TableColumnFactory();
192 private final TableColumnFactory bookmarkTableColumnFactory
= new TableColumnFactory();
194 // setting appearance attributes()
195 private final NSLayoutManager layoutManager
= NSLayoutManager
.layoutManager();
198 private BrowserOutlineViewModel browserOutlineModel
;
201 private NSOutlineView browserOutlineView
;
204 private AbstractBrowserTableDelegate browserOutlineViewDelegate
;
207 private BrowserListViewModel browserListModel
;
210 private NSTableView browserListView
;
213 private AbstractBrowserTableDelegate browserListViewDelegate
;
215 private NSToolbar toolbar
;
217 private final Navigation navigation
= new Navigation();
219 private PathPasteboard pasteboard
;
221 private ListProgressListener listener
222 = new PromptLimitedListProgressListener(this);
225 * Caching files listings of previously listed directories
227 private PathCache cache
228 = new PathCache(preferences
.getInteger("browser.cache.size"));
230 private List
<Editor
> editors
231 = new ArrayList
<Editor
>();
233 public BrowserController() {
238 protected String
getBundleName() {
242 protected void validateToolbar() {
243 this.window().toolbar().validateVisibleItems();
246 public static void updateBookmarkTableRowHeight() {
247 for(BrowserController controller
: MainController
.getBrowsers()) {
248 controller
._updateBookmarkCell();
252 public static void updateBrowserTableAttributes() {
253 for(BrowserController controller
: MainController
.getBrowsers()) {
254 controller
._updateBrowserAttributes(controller
.browserListView
);
255 controller
._updateBrowserAttributes(controller
.browserOutlineView
);
259 public static void updateBrowserTableColumns() {
260 for(BrowserController controller
: MainController
.getBrowsers()) {
261 controller
._updateBrowserColumns(controller
.browserListView
, controller
.browserListViewDelegate
);
262 controller
._updateBrowserColumns(controller
.browserOutlineView
, controller
.browserOutlineViewDelegate
);
267 public void awakeFromNib() {
268 super.awakeFromNib();
270 this.toolbar
= NSToolbar
.toolbarWithIdentifier("Cyberduck Toolbar");
271 this.toolbar
.setDelegate((this.id()));
272 this.toolbar
.setAllowsUserCustomization(true);
273 this.toolbar
.setAutosavesConfiguration(true);
274 this.window
.setToolbar(toolbar
);
275 this.window
.makeFirstResponder(quickConnectPopup
);
276 this._updateBrowserColumns(browserListView
, browserListViewDelegate
);
277 this._updateBrowserColumns(browserOutlineView
, browserOutlineViewDelegate
);
278 if(preferences
.getBoolean("browser.transcript.open")) {
279 this.logDrawer
.open();
281 if(LicenseFactory
.find().equals(LicenseFactory
.EMPTY_LICENSE
)) {
282 this.addDonateWindowTitle();
284 this.setNavigation(false);
285 this.selectBookmarks();
288 protected Comparator
<Path
> getComparator() {
289 return this.getSelectedBrowserDelegate().getSortingComparator();
292 protected Filter
<Path
> getFilter() {
293 return this.filenameFilter
;
296 public PathPasteboard
getPasteboard() {
300 protected void setPathFilter(final String search
) {
301 if(log
.isDebugEnabled()) {
302 log
.debug(String
.format("Set path filter to %s", search
));
304 if(StringUtils
.isBlank(search
)) {
305 this.searchField
.setStringValue(StringUtils
.EMPTY
);
306 // Revert to the last used default filter
307 if(this.isShowHiddenFiles()) {
308 this.filenameFilter
= NULL_FILTER
;
311 this.filenameFilter
= HIDDEN_FILTER
;
315 // Setting up a custom filter for the directory listing
316 this.filenameFilter
= new SearchFilter(search
);
320 public void setShowHiddenFiles(boolean showHidden
) {
322 this.filenameFilter
= NULL_FILTER
;
323 this.showHiddenFiles
= true;
326 this.filenameFilter
= HIDDEN_FILTER
;
327 this.showHiddenFiles
= false;
331 public boolean isShowHiddenFiles() {
332 return this.showHiddenFiles
;
336 * Marks the current browser as the first responder
338 private void getFocus() {
340 if(this.getSelectedTabView() == TAB_BOOKMARKS
) {
341 view
= bookmarkTable
;
344 if(this.isMounted()) {
345 view
= this.getSelectedBrowserView();
348 view
= quickConnectPopup
;
352 window
.makeFirstResponder(view
);
356 * Make the browser reload its content. Will make use of the cache.
358 protected void reload() {
359 if(this.isMounted()) {
360 this.reload(workdir
, Collections
.singleton(workdir
), this.getSelectedPaths(), false);
363 final NSTableView browser
= this.getSelectedBrowserView();
364 final BrowserTableDataSource model
= this.getSelectedBrowserModel();
365 model
.render(browser
, Collections
.<Path
>emptyList());
371 * Make the browser reload its content. Invalidates the cache.
373 * @param workdir Use working directory as the current root of the browser
374 * @param selected The items to be selected
376 protected void reload(final Path workdir
, final List
<Path
> changed
, final List
<Path
> selected
) {
377 this.reload(workdir
, new PathReloadFinder().find(changed
), selected
, true);
381 * Make the browser reload its content. Invalidates the cache.
383 * @param workdir Use working directory as the current root of the browser
384 * @param folders Folders to render
385 * @param selected The items to be selected
387 protected void reload(final Path workdir
, final Set
<Path
> folders
, final List
<Path
> selected
) {
388 this.reload(workdir
, folders
, selected
, true);
392 * Make the browser reload its content. Invalidates the cache.
394 * @param workdir Use working directory as the current root of the browser
395 * @param folders Folders to render
396 * @param selected The items to be selected
397 * @param invalidate Invalidate the cache before rendering
399 protected void reload(final Path workdir
, final Set
<Path
> folders
, final List
<Path
> selected
, final boolean invalidate
) {
400 if(log
.isDebugEnabled()) {
401 log
.debug(String
.format("Reload data with selected files %s", selected
));
403 final BrowserTableDataSource model
= this.getSelectedBrowserModel();
404 final NSTableView browser
= this.getSelectedBrowserView();
405 if(folders
.isEmpty()) {
406 // Render empty browser
407 model
.render(browser
, Collections
.<Path
>emptyList());
409 for(final Path folder
: folders
) {
412 cache
.invalidate(folder
);
415 if(cache
.isCached(folder
)) {
416 reload(browser
, model
, workdir
, selected
, folder
);
420 // Delay render until path is cached in the background
421 this.background(new WorkerBackgroundAction
<AttributedList
<Path
>>(this, session
, cache
,
422 new SessionListWorker(cache
, folder
, listener
) {
424 public void cleanup(final AttributedList
<Path
> list
) {
426 // Update the working directory if listing is successful
427 if(!(this.initialize() == list
)) {
428 reload(browser
, model
, workdir
, selected
, folder
);
439 * @param browser Browser view
440 * @param model Browser Model
441 * @param workdir Use working directory as the current root of the browser
442 * @param selected Selected files in browser
443 * @param folder Folder to render
445 private void reload(final NSTableView browser
, final BrowserTableDataSource model
, final Path workdir
, final List
<Path
> selected
, final Path folder
) {
446 this.workdir
= workdir
;
447 this.setNavigation(workdir
!= null);
449 model
.render(browser
, Collections
.singletonList(folder
));
450 this.select(selected
);
453 private void select(final List
<Path
> selected
) {
454 final NSTableView browser
= this.getSelectedBrowserView();
455 if(CollectionUtils
.isEqualCollection(this.getSelectedPaths(), selected
)) {
458 browser
.deselectAll(null);
459 for(Path path
: selected
) {
460 this.select(path
, true, true);
465 * @param file Path to select
466 * @param expand Keep previous selection
467 * @param scroll Scroll to selection
469 private void select(final Path file
, final boolean expand
, final boolean scroll
) {
470 final NSTableView browser
= this.getSelectedBrowserView();
471 final BrowserTableDataSource model
= this.getSelectedBrowserModel();
472 if(log
.isDebugEnabled()) {
473 log
.debug(String
.format("Select row for reference %s", file
));
475 int row
= model
.indexOf(browser
, file
);
477 log
.warn(String
.format("Failed to find row for %s", file
));
480 final NSInteger index
= new NSInteger(row
);
481 browser
.selectRowIndexes(NSIndexSet
.indexSetWithIndex(index
), expand
);
483 browser
.scrollRowToVisible(index
);
487 private void updateQuickLookSelection(final List
<Path
> selected
) {
488 if(quicklook
.isAvailable()) {
489 final List
<TransferItem
> downloads
= new ArrayList
<TransferItem
>();
490 for(Path path
: selected
) {
494 downloads
.add(new TransferItem(
495 path
, TemporaryFileServiceFactory
.get().create(session
.getHost().getUuid(), path
)));
497 if(downloads
.size() > 0) {
498 final Transfer download
= new DownloadTransfer(session
.getHost(), downloads
);
499 final TransferOptions options
= new TransferOptions();
500 background(new TransferBackgroundAction(this, session
, cache
, new TransferAdapter() {
502 public void progress(final TransferProgress status
) {
503 message(status
.getProgress());
505 }, this, this, download
, options
,
506 new TransferPrompt() {
508 public TransferAction
prompt(final TransferItem item
) {
509 return TransferAction
.comparison
;
513 public boolean isSelected(final TransferItem file
) {
518 public void message(final String message
) {
519 BrowserController
.this.message(message
);
521 }, new DisabledTransferErrorCallback()
524 public void cleanup() {
526 final List
<Local
> previews
= new ArrayList
<Local
>();
527 for(TransferItem download
: downloads
) {
528 previews
.add(download
.local
);
530 // Change files in Quick Look
531 quicklook
.select(previews
);
532 // Open Quick Look Preview Panel
537 public String
getActivity() {
538 return LocaleFactory
.localizedString("Quick Look", "Status");
546 * @return The first selected path found or null if there is no selection
548 protected Path
getSelectedPath() {
549 final List
<Path
> s
= this.getSelectedPaths();
557 * @return All selected paths or an empty list if there is no selection
559 protected List
<Path
> getSelectedPaths() {
560 final AbstractBrowserTableDelegate delegate
= this.getSelectedBrowserDelegate();
561 final NSTableView view
= this.getSelectedBrowserView();
562 final NSIndexSet iterator
= view
.selectedRowIndexes();
563 final List
<Path
> selected
= new ArrayList
<Path
>();
564 for(NSUInteger index
= iterator
.firstIndex(); !index
.equals(NSIndexSet
.NSNotFound
); index
= iterator
.indexGreaterThanIndex(index
)) {
565 final Path file
= delegate
.pathAtRow(index
.intValue());
574 protected int getSelectionCount() {
575 return this.getSelectedBrowserView().numberOfSelectedRows().intValue();
578 private static NSPoint cascade
= new NSPoint(0, 0);
581 public void setWindow(NSWindow window
) {
582 // Save frame rectangle
583 window
.setFrameAutosaveName("Browser");
584 window
.setTitle(preferences
.getProperty("application.name"));
585 window
.setMiniwindowImage(IconCacheFactory
.<NSImage
>get().iconNamed("cyberduck-document.icns"));
586 window
.setMovableByWindowBackground(true);
587 window
.setCollectionBehavior(window
.collectionBehavior() | NSWindow
.NSWindowCollectionBehavior
.NSWindowCollectionBehaviorFullScreenPrimary
);
588 window
.setContentMinSize(new NSSize(400d
, 200d
));
589 // Accept file promises made myself
590 window
.registerForDraggedTypes(NSArray
.arrayWithObject(NSPasteboard
.FilesPromisePboardType
));
591 super.setWindow(window
);
592 cascade
= this.cascade(cascade
);
596 * @return NSDragOperation
598 public NSUInteger
draggingEntered(final NSDraggingInfo sender
) {
599 return this.draggingUpdated(sender
);
602 public NSUInteger
draggingUpdated(final NSDraggingInfo sender
) {
603 final NSPasteboard pasteboard
= sender
.draggingPasteboard();
604 if(pasteboard
.types().indexOfObject(NSString
.stringWithString(NSPasteboard
.FilesPromisePboardType
)) != null) {
605 final NSView hit
= sender
.draggingDestinationWindow().contentView().hitTest(sender
.draggingLocation());
607 if(hit
.equals(bookmarkButton
)) {
608 if(historyButton
.state() == NSCell
.NSOnState
609 || bonjourButton
.state() == NSCell
.NSOnState
) {
610 return NSDraggingInfo
.NSDragOperationCopy
;
615 return NSDraggingInfo
.NSDragOperationNone
;
618 public boolean prepareForDragOperation(final NSDraggingInfo sender
) {
619 // Continue to performDragOperation
623 public boolean performDragOperation(final NSDraggingInfo sender
) {
624 for(Host bookmark
: HostPasteboard
.getPasteboard()) {
625 final Host duplicate
= new HostDictionary().deserialize(bookmark
.serialize(SerializerFactory
.get()));
626 // Make sure a new UUID is assigned for duplicate
627 duplicate
.setUuid(null);
628 bookmarks
.add(0, duplicate
);
634 private NSDrawer logDrawer
;
636 public void drawerDidOpen(NSNotification notification
) {
637 preferences
.setProperty("browser.transcript.open", true);
640 public void drawerDidClose(NSNotification notification
) {
641 preferences
.setProperty("browser.transcript.open", false);
645 public NSSize
drawerWillResizeContents_toSize(final NSDrawer sender
, final NSSize contentSize
) {
649 public void setLogDrawer(NSDrawer drawer
) {
650 this.logDrawer
= drawer
;
651 this.transcript
= new TranscriptController() {
653 public boolean isOpen() {
654 return logDrawer
.state() == NSDrawer
.OpenState
;
657 this.logDrawer
.setContentView(this.transcript
.getLogView());
658 this.logDrawer
.setDelegate(this.id());
661 private NSButton donateButton
;
663 public void setDonateButton(NSButton donateButton
) {
664 this.donateButton
= donateButton
;
665 this.donateButton
.setTitle(LocaleFactory
.localizedString("Get a donation key!", "License"));
666 this.donateButton
.setAction(Foundation
.selector("donateMenuClicked:"));
667 this.donateButton
.sizeToFit();
670 private void addDonateWindowTitle() {
671 NSView parent
= this.window().contentView().superview();
672 NSSize bounds
= parent
.frame().size
;
673 NSSize size
= donateButton
.frame().size
;
674 donateButton
.setFrame(new NSRect(
676 bounds
.width
.intValue() - size
.width
.intValue() - 40,
677 bounds
.height
.intValue() - size
.height
.intValue() + 3),
679 size
.width
.intValue(),
680 size
.height
.intValue())
683 donateButton
.setAutoresizingMask(new NSUInteger(NSView
.NSViewMinXMargin
| NSView
.NSViewMinYMargin
));
684 parent
.addSubview(donateButton
);
687 public void removeDonateWindowTitle() {
688 donateButton
.removeFromSuperview();
691 protected static final int TAB_BOOKMARKS
= 0;
692 protected static final int TAB_LIST_VIEW
= 1;
693 protected static final int TAB_OUTLINE_VIEW
= 2;
695 protected int getSelectedTabView() {
696 return browserTabView
.indexOfTabViewItem(browserTabView
.selectedTabViewItem());
699 private NSTabView browserTabView
;
701 public void setBrowserTabView(NSTabView browserTabView
) {
702 this.browserTabView
= browserTabView
;
706 * @return The currently selected browser view (which is either an outlineview or a plain tableview)
708 public NSTableView
getSelectedBrowserView() {
709 switch(preferences
.getInteger("browser.view")) {
710 case SWITCH_LIST_VIEW
: {
711 return browserListView
;
713 case SWITCH_OUTLINE_VIEW
: {
714 return browserOutlineView
;
717 throw new FactoryException("No selected browser view");
721 * @return The datasource of the currently selected browser view
723 public BrowserTableDataSource
getSelectedBrowserModel() {
724 switch(this.browserSwitchView
.selectedSegment()) {
725 case SWITCH_LIST_VIEW
: {
726 return browserListModel
;
728 case SWITCH_OUTLINE_VIEW
: {
729 return browserOutlineModel
;
732 throw new FactoryException("No selected browser view");
735 public AbstractBrowserTableDelegate
getSelectedBrowserDelegate() {
736 switch(this.browserSwitchView
.selectedSegment()) {
737 case SWITCH_LIST_VIEW
: {
738 return browserListViewDelegate
;
740 case SWITCH_OUTLINE_VIEW
: {
741 return browserOutlineViewDelegate
;
744 throw new FactoryException("No selected browser view");
748 private NSMenu editMenu
;
751 private EditMenuDelegate editMenuDelegate
;
753 public void setEditMenu(NSMenu editMenu
) {
754 this.editMenu
= editMenu
;
755 this.editMenuDelegate
= new EditMenuDelegate() {
757 protected Path
getEditable() {
758 final Path selected
= BrowserController
.this.getSelectedPath();
759 if(null == selected
) {
762 if(isEditable(selected
)) {
769 protected ID
getTarget() {
770 return BrowserController
.this.id();
773 this.editMenu
.setDelegate(editMenuDelegate
.id());
776 public EditMenuDelegate
getEditMenuDelegate() {
777 return editMenuDelegate
;
781 private NSMenu urlMenu
;
784 private URLMenuDelegate urlMenuDelegate
;
786 public void setUrlMenu(NSMenu urlMenu
) {
787 this.urlMenu
= urlMenu
;
788 this.urlMenuDelegate
= new CopyURLMenuDelegate() {
790 protected Session
<?
> getSession() {
791 return BrowserController
.this.getSession();
795 protected List
<Path
> getSelected() {
796 final List
<Path
> s
= BrowserController
.this.getSelectedPaths();
798 if(BrowserController
.this.isMounted()) {
799 return Collections
.singletonList(BrowserController
.this.workdir());
805 this.urlMenu
.setDelegate(urlMenuDelegate
.id());
809 private NSMenu openUrlMenu
;
812 private URLMenuDelegate openUrlMenuDelegate
;
814 public void setOpenUrlMenu(NSMenu openUrlMenu
) {
815 this.openUrlMenu
= openUrlMenu
;
816 this.openUrlMenuDelegate
= new OpenURLMenuDelegate() {
818 protected Session
<?
> getSession() {
819 return BrowserController
.this.getSession();
823 protected List
<Path
> getSelected() {
824 final List
<Path
> s
= BrowserController
.this.getSelectedPaths();
826 if(BrowserController
.this.isMounted()) {
827 return Collections
.singletonList(BrowserController
.this.workdir());
833 this.openUrlMenu
.setDelegate(openUrlMenuDelegate
.id());
837 private NSMenu archiveMenu
;
840 private ArchiveMenuDelegate archiveMenuDelegate
;
842 public void setArchiveMenu(NSMenu archiveMenu
) {
843 this.archiveMenu
= archiveMenu
;
844 this.archiveMenuDelegate
= new ArchiveMenuDelegate();
845 this.archiveMenu
.setDelegate(archiveMenuDelegate
.id());
849 private NSButton bonjourButton
;
851 public void setBonjourButton(NSButton bonjourButton
) {
852 this.bonjourButton
= bonjourButton
;
853 NSImage img
= IconCacheFactory
.<NSImage
>get().iconNamed("rendezvous.tiff", 16);
854 img
.setTemplate(false);
855 this.bonjourButton
.setImage(img
);
856 this.setRecessedBezelStyle(this.bonjourButton
);
857 this.bonjourButton
.setTarget(this.id());
858 this.bonjourButton
.setAction(Foundation
.selector("bookmarkButtonClicked:"));
862 private NSButton historyButton
;
864 public void setHistoryButton(NSButton historyButton
) {
865 this.historyButton
= historyButton
;
866 NSImage img
= IconCacheFactory
.<NSImage
>get().iconNamed("history.tiff", 16);
867 img
.setTemplate(false);
868 this.historyButton
.setImage(img
);
869 this.setRecessedBezelStyle(this.historyButton
);
870 this.historyButton
.setTarget(this.id());
871 this.historyButton
.setAction(Foundation
.selector("bookmarkButtonClicked:"));
875 private NSButton bookmarkButton
;
877 public void setBookmarkButton(NSButton bookmarkButton
) {
878 this.bookmarkButton
= bookmarkButton
;
879 NSImage img
= IconCacheFactory
.<NSImage
>get().iconNamed("bookmarks.tiff", 16);
880 img
.setTemplate(false);
881 this.bookmarkButton
.setImage(img
);
882 this.setRecessedBezelStyle(this.bookmarkButton
);
883 this.bookmarkButton
.setTarget(this.id());
884 this.bookmarkButton
.setAction(Foundation
.selector("bookmarkButtonClicked:"));
885 this.bookmarkButton
.setState(NSCell
.NSOnState
); // Set as default selected bookmark source
888 public void bookmarkButtonClicked(final NSButton sender
) {
889 if(sender
!= bonjourButton
) {
890 bonjourButton
.setState(NSCell
.NSOffState
);
892 if(sender
!= historyButton
) {
893 historyButton
.setState(NSCell
.NSOffState
);
895 if(sender
!= bookmarkButton
) {
896 bookmarkButton
.setState(NSCell
.NSOffState
);
898 sender
.setState(NSCell
.NSOnState
);
899 this.selectBookmarks();
902 private void setRecessedBezelStyle(final NSButton b
) {
903 b
.setBezelStyle(NSButton
.NSRecessedBezelStyle
);
904 b
.setButtonType(NSButton
.NSMomentaryPushButtonButton
);
905 b
.setImagePosition(NSCell
.NSImageLeft
);
906 b
.setFont(NSFont
.boldSystemFontOfSize(11f
));
907 b
.setShowsBorderOnlyWhileMouseInside(true);
911 public void sortBookmarksByNickame(final ID sender
) {
912 bookmarks
.sortByNickname();
913 this.reloadBookmarks();
917 public void sortBookmarksByHostname(final ID sender
) {
918 bookmarks
.sortByHostname();
919 this.reloadBookmarks();
923 public void sortBookmarksByProtocol(final ID sender
) {
924 bookmarks
.sortByProtocol();
925 this.reloadBookmarks();
928 private NSSegmentedControl bookmarkSwitchView
;
930 private static final int SWITCH_BOOKMARK_VIEW
= 0;
932 public void setBookmarkSwitchView(NSSegmentedControl bookmarkSwitchView
) {
933 this.bookmarkSwitchView
= bookmarkSwitchView
;
934 this.bookmarkSwitchView
.setSegmentCount(1);
935 this.bookmarkSwitchView
.setToolTip(LocaleFactory
.localizedString("Bookmarks"));
936 final NSImage image
= IconCacheFactory
.<NSImage
>get().iconNamed("book.tiff");
937 this.bookmarkSwitchView
.setImage_forSegment(image
, SWITCH_BOOKMARK_VIEW
);
938 final NSSegmentedCell cell
= Rococoa
.cast(this.bookmarkSwitchView
.cell(), NSSegmentedCell
.class);
939 cell
.setTrackingMode(NSSegmentedCell
.NSSegmentSwitchTrackingSelectAny
);
940 cell
.setControlSize(NSCell
.NSRegularControlSize
);
941 this.bookmarkSwitchView
.setTarget(this.id());
942 this.bookmarkSwitchView
.setAction(Foundation
.selector("bookmarkSwitchClicked:"));
943 this.bookmarkSwitchView
.setSelectedSegment(SWITCH_BOOKMARK_VIEW
);
947 public void bookmarkSwitchClicked(final ID sender
) {
949 final boolean open
= this.getSelectedTabView() != TAB_BOOKMARKS
;
950 bookmarkSwitchView
.setSelected_forSegment(open
, SWITCH_BOOKMARK_VIEW
);
951 this.setNavigation(!open
&& this.isMounted());
953 this.selectBookmarks();
956 this.selectBrowser(preferences
.getInteger("browser.view"));
960 private NSSegmentedControl browserSwitchView
;
962 private static final int SWITCH_LIST_VIEW
= 0;
963 private static final int SWITCH_OUTLINE_VIEW
= 1;
965 public void setBrowserSwitchView(NSSegmentedControl view
) {
966 browserSwitchView
= view
;
967 browserSwitchView
.setSegmentCount(2); // list, outline
968 final NSImage list
= IconCacheFactory
.<NSImage
>get().iconNamed("list.tiff");
969 list
.setTemplate(true);
970 browserSwitchView
.setImage_forSegment(list
, SWITCH_LIST_VIEW
);
971 final NSImage outline
= IconCacheFactory
.<NSImage
>get().iconNamed("outline.tiff");
972 outline
.setTemplate(true);
973 browserSwitchView
.setImage_forSegment(outline
, SWITCH_OUTLINE_VIEW
);
974 browserSwitchView
.setTarget(this.id());
975 browserSwitchView
.setAction(Foundation
.selector("browserSwitchButtonClicked:"));
976 final NSSegmentedCell cell
= Rococoa
.cast(browserSwitchView
.cell(), NSSegmentedCell
.class);
977 cell
.setTrackingMode(NSSegmentedCell
.NSSegmentSwitchTrackingSelectOne
);
978 cell
.setControlSize(NSCell
.NSRegularControlSize
);
979 browserSwitchView
.setSelectedSegment(preferences
.getInteger("browser.view"));
982 public NSSegmentedControl
getBrowserSwitchView() {
983 return browserSwitchView
;
987 public void browserSwitchButtonClicked(final NSSegmentedControl sender
) {
988 // Highlight selected browser view
989 this.selectBrowser(sender
.selectedSegment());
993 public void browserSwitchMenuClicked(final NSMenuItem sender
) {
994 // Highlight selected browser view
995 this.selectBrowser(sender
.tag());
998 private void selectBrowser(int selected
) {
999 bookmarkSwitchView
.setSelected_forSegment(false, SWITCH_BOOKMARK_VIEW
);
1000 browserSwitchView
.setSelectedSegment(selected
);
1002 case SWITCH_LIST_VIEW
:
1003 browserTabView
.selectTabViewItemAtIndex(TAB_LIST_VIEW
);
1005 case SWITCH_OUTLINE_VIEW
:
1006 browserTabView
.selectTabViewItemAtIndex(TAB_OUTLINE_VIEW
);
1009 // Save selected browser view
1010 preferences
.setProperty("browser.view", selected
);
1011 // Remove any custom file filter
1012 this.setPathFilter(null);
1013 // Update from model
1015 // Focus on browser view
1019 private void selectBookmarks() {
1020 bookmarkSwitchView
.setSelected_forSegment(true, SWITCH_BOOKMARK_VIEW
);
1021 // Display bookmarks
1022 browserTabView
.selectTabViewItemAtIndex(TAB_BOOKMARKS
);
1023 final AbstractHostCollection source
;
1024 if(bookmarkButton
.state() == NSCell
.NSOnState
) {
1027 else if(bonjourButton
.state() == NSCell
.NSOnState
) {
1028 source
= RendezvousCollection
.defaultCollection();
1030 else if(historyButton
.state() == NSCell
.NSOnState
) {
1031 source
= HistoryCollection
.defaultCollection();
1034 source
= AbstractHostCollection
.empty();
1036 if(!source
.isLoaded()) {
1037 browserSpinner
.startAnimation(null);
1038 source
.addListener(new AbstractCollectionListener
<Host
>() {
1040 public void collectionLoaded() {
1041 invoke(new WindowMainAction(BrowserController
.this) {
1044 browserSpinner
.stopAnimation(null);
1045 bookmarkTable
.setGridStyleMask(NSTableView
.NSTableViewSolidHorizontalGridLineMask
);
1048 source
.removeListener(this);
1053 browserSpinner
.stopAnimation(null);
1054 bookmarkTable
.setGridStyleMask(NSTableView
.NSTableViewSolidHorizontalGridLineMask
);
1056 bookmarkModel
.setSource(source
);
1057 this.setBookmarkFilter(null);
1058 this.reloadBookmarks();
1059 if(this.isMounted()) {
1060 int row
= this.bookmarkModel
.getSource().indexOf(session
.getHost());
1062 this.bookmarkTable
.selectRowIndexes(NSIndexSet
.indexSetWithIndex(new NSInteger(row
)), false);
1063 this.bookmarkTable
.scrollRowToVisible(new NSInteger(row
));
1070 * Reload bookmark table from currently selected model
1072 public void reloadBookmarks() {
1073 bookmarkTable
.reloadData();
1077 private abstract class AbstractBrowserOutlineViewDelegate
extends AbstractBrowserTableDelegate
1078 implements NSOutlineView
.Delegate
{
1080 protected AbstractBrowserOutlineViewDelegate(final NSTableColumn selectedColumn
) {
1081 super(selectedColumn
);
1084 public String
outlineView_toolTipForCell_rect_tableColumn_item_mouseLocation(NSOutlineView t
, NSCell cell
,
1085 ID rect
, NSTableColumn c
,
1086 NSObject item
, NSPoint mouseLocation
) {
1087 return this.tooltip(cache
.lookup(new NSObjectPathReference(item
)));
1090 public String
outlineView_typeSelectStringForTableColumn_item(final NSOutlineView view
,
1091 final NSTableColumn tableColumn
,
1092 final NSObject item
) {
1093 if(tableColumn
.identifier().equals(Column
.filename
.name())) {
1094 return browserOutlineModel
.outlineView_objectValueForTableColumn_byItem(view
, tableColumn
, item
).toString();
1100 protected void setBrowserColumnSortingIndicator(NSImage image
, String columnIdentifier
) {
1101 browserOutlineView
.setIndicatorImage_inTableColumn(image
,
1102 browserOutlineView
.tableColumnWithIdentifier(columnIdentifier
));
1106 protected Path
pathAtRow(final int row
) {
1107 if(row
< browserOutlineView
.numberOfRows().intValue()) {
1108 return cache
.lookup(new NSObjectPathReference(browserOutlineView
.itemAtRow(new NSInteger(row
))));
1110 log
.warn(String
.format("No item at row %d", row
));
1115 private abstract class AbstractBrowserListViewDelegate
<E
> extends AbstractBrowserTableDelegate
1116 implements NSTableView
.Delegate
{
1118 protected AbstractBrowserListViewDelegate(final NSTableColumn selectedColumn
) {
1119 super(selectedColumn
);
1122 public String
tableView_toolTipForCell_rect_tableColumn_row_mouseLocation(NSTableView t
, NSCell cell
,
1123 ID rect
, NSTableColumn c
,
1124 NSInteger row
, NSPoint mouseLocation
) {
1125 return this.tooltip(browserListModel
.get(workdir()).get(row
.intValue()));
1129 protected void setBrowserColumnSortingIndicator(NSImage image
, String columnIdentifier
) {
1130 browserListView
.setIndicatorImage_inTableColumn(image
,
1131 browserListView
.tableColumnWithIdentifier(columnIdentifier
));
1134 public String
tableView_typeSelectStringForTableColumn_row(final NSTableView view
,
1135 final NSTableColumn tableColumn
,
1136 final NSInteger row
) {
1137 if(tableColumn
.identifier().equals(Column
.filename
.name())) {
1138 return browserListModel
.tableView_objectValueForTableColumn_row(view
, tableColumn
, row
).toString();
1144 protected Path
pathAtRow(int row
) {
1145 final AttributedList
<Path
> children
= browserListModel
.get(workdir());
1146 if(row
< children
.size()) {
1147 return children
.get(row
);
1149 log
.warn(String
.format("No item at row %d", row
));
1154 private abstract class AbstractBrowserTableDelegate
extends AbstractPathTableDelegate
{
1156 protected AbstractBrowserTableDelegate(final NSTableColumn selectedColumn
) {
1157 super(selectedColumn
);
1161 public boolean isColumnRowEditable(NSTableColumn column
, int row
) {
1162 if(preferences
.getBoolean("browser.editable")) {
1163 return column
.identifier().equals(Column
.filename
.name());
1169 public void tableRowDoubleClicked(final ID sender
) {
1170 BrowserController
.this.insideButtonClicked(sender
);
1173 public void spaceKeyPressed(final ID sender
) {
1174 quicklookButtonClicked(sender
);
1178 public void deleteKeyPressed(final ID sender
) {
1179 BrowserController
.this.deleteFileButtonClicked(sender
);
1183 public void tableColumnClicked(final NSTableView view
, final NSTableColumn tableColumn
) {
1184 if(this.selectedColumnIdentifier().equals(tableColumn
.identifier())) {
1185 this.setSortedAscending(!this.isSortedAscending());
1188 // Remove sorting indicator on previously selected column
1189 this.setBrowserColumnSortingIndicator(null, this.selectedColumnIdentifier());
1190 // Set the newly selected column
1191 this.setSelectedColumn(tableColumn
);
1192 // Update the default value
1193 preferences
.setProperty("browser.sort.column", this.selectedColumnIdentifier());
1195 this.setBrowserColumnSortingIndicator(
1196 this.isSortedAscending() ?
1197 IconCacheFactory
.<NSImage
>get().iconNamed("NSAscendingSortIndicator") :
1198 IconCacheFactory
.<NSImage
>get().iconNamed("NSDescendingSortIndicator"),
1199 tableColumn
.identifier()
1205 public void columnDidResize(final String columnIdentifier
, final float width
) {
1206 preferences
.setProperty(String
.format("browser.column.%s.width", columnIdentifier
), width
);
1210 public void selectionDidChange(NSNotification notification
) {
1211 final List
<Path
> selected
= getSelectedPaths();
1212 if(quicklook
.isOpen()) {
1213 updateQuickLookSelection(selected
);
1215 if(preferences
.getBoolean("browser.info.inspector")) {
1216 InfoController c
= InfoControllerFactory
.get(BrowserController
.this);
1218 // Currently open info panel
1219 c
.setFiles(selected
);
1224 protected abstract Path
pathAtRow(int row
);
1226 protected abstract void setBrowserColumnSortingIndicator(NSImage image
, String columnIdentifier
);
1228 private static final double kSwipeGestureLeft
= 1.000000;
1229 private static final double kSwipeGestureRight
= -1.000000;
1230 private static final double kSwipeGestureUp
= 1.000000;
1231 private static final double kSwipeGestureDown
= -1.000000;
1234 * Available in Mac OS X v10.6 and later.
1236 * @param event Swipe event
1239 public void swipeWithEvent(NSEvent event
) {
1240 if(event
.deltaX().doubleValue() == kSwipeGestureLeft
) {
1241 BrowserController
.this.backButtonClicked(event
.id());
1243 else if(event
.deltaX().doubleValue() == kSwipeGestureRight
) {
1244 BrowserController
.this.forwardButtonClicked(event
.id());
1246 else if(event
.deltaY().doubleValue() == kSwipeGestureUp
) {
1247 NSInteger row
= getSelectedBrowserView().selectedRow();
1249 if(-1 == row
.intValue()) {
1250 // No current selection
1251 next
= new NSInteger(0);
1254 next
= new NSInteger(row
.longValue() - 1);
1256 BrowserController
.this.getSelectedBrowserView().selectRowIndexes(
1257 NSIndexSet
.indexSetWithIndex(next
), false);
1259 else if(event
.deltaY().doubleValue() == kSwipeGestureDown
) {
1260 NSInteger row
= getSelectedBrowserView().selectedRow();
1262 if(-1 == row
.intValue()) {
1263 // No current selection
1264 next
= new NSInteger(0);
1267 next
= new NSInteger(row
.longValue() + 1);
1269 BrowserController
.this.getSelectedBrowserView().selectRowIndexes(
1270 NSIndexSet
.indexSetWithIndex(next
), false);
1276 * QuickLook support for 10.6+
1278 * @param panel The Preview Panel looking for a controller.
1280 * @ Sent to each object in the responder chain to find a controller.
1283 public boolean acceptsPreviewPanelControl(QLPreviewPanel panel
) {
1288 * QuickLook support for 10.6+
1289 * The receiver should setup the preview panel (data source, delegate, binding, etc.) here.
1291 * @param panel The Preview Panel the receiver will control.
1292 * @ Sent to the object taking control of the Preview Panel.
1295 public void beginPreviewPanelControl(QLPreviewPanel panel
) {
1296 quicklook
.willBeginQuickLook();
1300 * QuickLook support for 10.6+
1301 * The receiver should unsetup the preview panel (data source, delegate, binding, etc.) here.
1303 * @param panel The Preview Panel that the receiver will stop controlling.
1304 * @ Sent to the object in control of the Preview Panel just before stopping its control.
1307 public void endPreviewPanelControl(QLPreviewPanel panel
) {
1308 quicklook
.didEndQuickLook();
1311 public void setBrowserOutlineView(NSOutlineView view
) {
1312 browserOutlineView
= view
;
1313 // receive drag events from types
1314 browserOutlineView
.registerForDraggedTypes(NSArray
.arrayWithObjects(
1315 NSPasteboard
.URLPboardType
,
1316 // Accept files dragged from the Finder for uploading
1317 NSPasteboard
.FilenamesPboardType
,
1318 // Accept file promises made myself
1319 NSPasteboard
.FilesPromisePboardType
1321 // setting appearance attributes()
1322 this._updateBrowserAttributes(browserOutlineView
);
1323 // selection properties
1324 browserOutlineView
.setAllowsMultipleSelection(true);
1325 browserOutlineView
.setAllowsEmptySelection(true);
1326 browserOutlineView
.setAllowsColumnResizing(true);
1327 browserOutlineView
.setAllowsColumnSelection(false);
1328 browserOutlineView
.setAllowsColumnReordering(true);
1330 browserOutlineView
.setRowHeight(new CGFloat(layoutManager
.defaultLineHeightForFont(
1331 NSFont
.systemFontOfSize(preferences
.getFloat("browser.font.size"))).intValue() + 2));
1334 NSTableColumn c
= browserOutlineColumnsFactory
.create(Column
.filename
.name());
1335 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Filename"));
1336 c
.setMinWidth(new CGFloat(100));
1337 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1338 Column
.filename
.name())));
1339 c
.setMaxWidth(new CGFloat(1000));
1340 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1341 c
.setDataCell(outlineCellPrototype
);
1342 browserOutlineView
.addTableColumn(c
);
1343 browserOutlineView
.setOutlineTableColumn(c
);
1345 browserOutlineView
.setDataSource((browserOutlineModel
= new BrowserOutlineViewModel(this, cache
)).id());
1346 browserOutlineView
.setDelegate((browserOutlineViewDelegate
= new AbstractBrowserOutlineViewDelegate(
1347 browserOutlineView
.tableColumnWithIdentifier(Column
.filename
.name())
1350 public void enterKeyPressed(final ID sender
) {
1351 if(preferences
.getBoolean("browser.enterkey.rename")) {
1352 if(browserOutlineView
.numberOfSelectedRows().intValue() == 1) {
1353 renameFileButtonClicked(sender
);
1357 this.tableRowDoubleClicked(sender
);
1362 * @see NSOutlineView.Delegate
1365 public void outlineView_willDisplayCell_forTableColumn_item(NSOutlineView view
, NSTextFieldCell cell
,
1366 NSTableColumn tableColumn
, NSObject item
) {
1370 final Path path
= cache
.lookup(new NSObjectPathReference(item
));
1374 if(tableColumn
.identifier().equals(Column
.filename
.name())) {
1375 cell
.setEditable(session
.getFeature(Move
.class).isSupported(path
));
1376 (Rococoa
.cast(cell
, OutlineCell
.class)).setIcon(browserOutlineModel
.iconForPath(path
));
1378 if(!BrowserController
.this.isConnected() || !HIDDEN_FILTER
.accept(path
)) {
1379 cell
.setTextColor(NSColor
.disabledControlTextColor());
1382 cell
.setTextColor(NSColor
.controlTextColor());
1387 * @see NSOutlineView.Delegate
1390 public boolean outlineView_shouldExpandItem(final NSOutlineView view
, final NSObject item
) {
1391 NSEvent event
= NSApplication
.sharedApplication().currentEvent();
1393 if(NSEvent
.NSLeftMouseDragged
== event
.type()) {
1394 if(!preferences
.getBoolean("browser.view.autoexpand")) {
1395 if(log
.isDebugEnabled()) {
1396 log
.debug("Returning false to #outlineViewShouldExpandItem while dragging because browser.view.autoexpand == false");
1398 // See tickets #98 and #633
1401 final NSInteger draggingColumn
= view
.columnAtPoint(view
.convertPoint_fromView(event
.locationInWindow(), null));
1402 if(draggingColumn
.intValue() != 0) {
1403 if(log
.isDebugEnabled()) {
1404 log
.debug("Returning false to #outlineViewShouldExpandItem for column:" + draggingColumn
);
1415 public boolean outlineView_isGroupItem(final NSOutlineView view
, final NSObject item
) {
1420 public void outlineViewItemWillExpand(final NSNotification notification
) {
1421 final NSObject object
= Rococoa
.cast(notification
.userInfo(), NSDictionary
.class).objectForKey("NSObject");
1422 final NSObjectPathReference reference
= new NSObjectPathReference(object
);
1423 final Path directory
= cache
.lookup(reference
);
1424 if(null == directory
) {
1427 reload(workdir
, Collections
.singleton(directory
), getSelectedPaths(), false);
1431 * @see NSOutlineView.Delegate
1434 public void outlineViewItemDidExpand(final NSNotification notification
) {
1439 public void outlineViewItemWillCollapse(final NSNotification notification
) {
1444 * @see NSOutlineView.Delegate
1447 public void outlineViewItemDidCollapse(final NSNotification notification
) {
1452 protected boolean isTypeSelectSupported() {
1459 public void setBrowserListView(NSTableView view
) {
1460 browserListView
= view
;
1461 // receive drag events from types
1462 browserListView
.registerForDraggedTypes(NSArray
.arrayWithObjects(
1463 NSPasteboard
.URLPboardType
,
1464 // Accept files dragged from the Finder for uploading
1465 NSPasteboard
.FilenamesPboardType
,
1466 // Accept file promises made myself
1467 NSPasteboard
.FilesPromisePboardType
1469 // setting appearance attributes()
1470 this._updateBrowserAttributes(browserListView
);
1471 // selection properties
1472 browserListView
.setAllowsMultipleSelection(true);
1473 browserListView
.setAllowsEmptySelection(true);
1474 browserListView
.setAllowsColumnResizing(true);
1475 browserListView
.setAllowsColumnSelection(false);
1476 browserListView
.setAllowsColumnReordering(true);
1478 browserListView
.setRowHeight(new CGFloat(layoutManager
.defaultLineHeightForFont(
1479 NSFont
.systemFontOfSize(preferences
.getFloat("browser.font.size"))).intValue() + 2));
1482 NSTableColumn c
= browserListColumnsFactory
.create(Column
.icon
.name());
1483 c
.headerCell().setStringValue(StringUtils
.EMPTY
);
1484 c
.setMinWidth((20));
1485 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1486 Column
.icon
.name())));
1487 c
.setMaxWidth((20));
1488 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
);
1489 c
.setDataCell(imageCellPrototype
);
1490 c
.dataCell().setAlignment(NSText
.NSCenterTextAlignment
);
1491 browserListView
.addTableColumn(c
);
1494 NSTableColumn c
= browserListColumnsFactory
.create(Column
.filename
.name());
1495 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Filename"));
1496 c
.setMinWidth((100));
1497 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1498 Column
.filename
.name())));
1499 c
.setMaxWidth((1000));
1500 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1501 c
.setDataCell(filenameCellPrototype
);
1502 this.browserListView
.addTableColumn(c
);
1505 browserListView
.setDataSource((browserListModel
= new BrowserListViewModel(this, cache
)).id());
1506 browserListView
.setDelegate((browserListViewDelegate
= new AbstractBrowserListViewDelegate
<Path
>(
1507 browserListView
.tableColumnWithIdentifier(Column
.filename
.name())
1510 public void enterKeyPressed(final ID sender
) {
1511 if(preferences
.getBoolean("browser.enterkey.rename")) {
1512 if(browserListView
.numberOfSelectedRows().intValue() == 1) {
1513 renameFileButtonClicked(sender
);
1517 this.tableRowDoubleClicked(sender
);
1522 public void tableView_willDisplayCell_forTableColumn_row(NSTableView view
, NSTextFieldCell cell
, NSTableColumn tableColumn
, NSInteger row
) {
1523 final String identifier
= tableColumn
.identifier();
1524 final Path path
= browserListModel
.get(BrowserController
.this.workdir()).get(row
.intValue());
1525 if(identifier
.equals(Column
.filename
.name())) {
1526 cell
.setEditable(session
.getFeature(Move
.class).isSupported(path
));
1528 if(cell
.isKindOfClass(Foundation
.getClass(NSTextFieldCell
.class.getSimpleName()))) {
1529 if(!BrowserController
.this.isConnected() || !HIDDEN_FILTER
.accept(path
)) {
1530 cell
.setTextColor(NSColor
.disabledControlTextColor());
1533 cell
.setTextColor(NSColor
.controlTextColor());
1539 protected boolean isTypeSelectSupported() {
1545 protected void _updateBrowserAttributes(NSTableView tableView
) {
1546 tableView
.setUsesAlternatingRowBackgroundColors(preferences
.getBoolean("browser.alternatingRows"));
1547 if(preferences
.getBoolean("browser.horizontalLines") && preferences
.getBoolean("browser.verticalLines")) {
1548 tableView
.setGridStyleMask(new NSUInteger(NSTableView
.NSTableViewSolidHorizontalGridLineMask
.intValue() | NSTableView
.NSTableViewSolidVerticalGridLineMask
.intValue()));
1550 else if(preferences
.getBoolean("browser.verticalLines")) {
1551 tableView
.setGridStyleMask(NSTableView
.NSTableViewSolidVerticalGridLineMask
);
1553 else if(preferences
.getBoolean("browser.horizontalLines")) {
1554 tableView
.setGridStyleMask(NSTableView
.NSTableViewSolidHorizontalGridLineMask
);
1557 tableView
.setGridStyleMask(NSTableView
.NSTableViewGridNone
);
1561 protected void _updateBookmarkCell() {
1562 final int size
= preferences
.getInteger("bookmark.icon.size");
1563 final double width
= size
* 1.5;
1564 final NSTableColumn c
= bookmarkTable
.tableColumnWithIdentifier(BookmarkTableDataSource
.Column
.icon
.name());
1565 c
.setMinWidth(width
);
1566 c
.setMaxWidth(width
);
1568 // Notify the table about the changed row height.
1569 bookmarkTable
.noteHeightOfRowsWithIndexesChanged(
1570 NSIndexSet
.indexSetWithIndexesInRange(NSRange
.NSMakeRange(new NSUInteger(0), new NSUInteger(bookmarkTable
.numberOfRows()))));
1573 private void _updateBrowserColumns(final NSTableView table
, final AbstractBrowserTableDelegate delegate
) {
1574 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.size
.name()));
1575 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.size
.name()))) {
1576 NSTableColumn c
= browserListColumnsFactory
.create(Column
.size
.name());
1577 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Size"));
1579 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1580 Column
.size
.name())));
1581 c
.setMaxWidth(150f
);
1582 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1583 c
.setDataCell(textCellPrototype
);
1584 table
.addTableColumn(c
);
1586 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.modified
.name()));
1587 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.modified
.name()))) {
1588 NSTableColumn c
= browserListColumnsFactory
.create(Column
.modified
.name());
1589 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Modified"));
1590 c
.setMinWidth(100f
);
1591 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1592 Column
.modified
.name())));
1594 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1595 c
.setDataCell(textCellPrototype
);
1596 table
.addTableColumn(c
);
1598 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.owner
.name()));
1599 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.owner
.name()))) {
1600 NSTableColumn c
= browserListColumnsFactory
.create(Column
.owner
.name());
1601 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Owner"));
1603 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1604 Column
.owner
.name())));
1606 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1607 c
.setDataCell(textCellPrototype
);
1608 table
.addTableColumn(c
);
1610 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.group
.name()));
1611 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.group
.name()))) {
1612 NSTableColumn c
= browserListColumnsFactory
.create(Column
.group
.name());
1613 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Group"));
1615 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1616 Column
.group
.name())));
1618 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1619 c
.setDataCell(textCellPrototype
);
1620 table
.addTableColumn(c
);
1622 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.permission
.name()));
1623 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.permission
.name()))) {
1624 NSTableColumn c
= browserListColumnsFactory
.create(Column
.permission
.name());
1625 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Permissions"));
1627 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1628 Column
.permission
.name())));
1630 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1631 c
.setDataCell(textCellPrototype
);
1632 table
.addTableColumn(c
);
1634 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.kind
.name()));
1635 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.kind
.name()))) {
1636 NSTableColumn c
= browserListColumnsFactory
.create(Column
.kind
.name());
1637 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Kind"));
1639 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1640 Column
.kind
.name())));
1642 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1643 c
.setDataCell(textCellPrototype
);
1644 table
.addTableColumn(c
);
1646 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.extension
.name()));
1647 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.extension
.name()))) {
1648 NSTableColumn c
= browserListColumnsFactory
.create(Column
.extension
.name());
1649 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Extension"));
1651 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1652 Column
.extension
.name())));
1654 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1655 c
.setDataCell(textCellPrototype
);
1656 table
.addTableColumn(c
);
1658 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.region
.name()));
1659 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.region
.name()))) {
1660 NSTableColumn c
= browserListColumnsFactory
.create(Column
.region
.name());
1661 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Region"));
1663 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1664 Column
.region
.name())));
1666 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1667 c
.setDataCell(textCellPrototype
);
1668 table
.addTableColumn(c
);
1670 table
.removeTableColumn(table
.tableColumnWithIdentifier(Column
.version
.name()));
1671 if(preferences
.getBoolean(String
.format("browser.column.%s", Column
.version
.name()))) {
1672 NSTableColumn c
= browserListColumnsFactory
.create(Column
.version
.name());
1673 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Version"));
1675 c
.setWidth(preferences
.getFloat(String
.format("browser.column.%s.width",
1676 Column
.version
.name())));
1678 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
| NSTableColumn
.NSTableColumnUserResizingMask
);
1679 c
.setDataCell(textCellPrototype
);
1680 table
.addTableColumn(c
);
1682 NSTableColumn selected
= table
.tableColumnWithIdentifier(preferences
.getProperty("browser.sort.column"));
1683 if(null == selected
) {
1684 selected
= table
.tableColumnWithIdentifier(Column
.filename
.name());
1686 delegate
.setSelectedColumn(selected
);
1687 table
.setIndicatorImage_inTableColumn(this.getSelectedBrowserDelegate().isSortedAscending() ?
1688 IconCacheFactory
.<NSImage
>get().iconNamed("NSAscendingSortIndicator") :
1689 IconCacheFactory
.<NSImage
>get().iconNamed("NSDescendingSortIndicator"),
1693 table
.setAutosaveName("browser.autosave");
1694 table
.setAutosaveTableColumns(true);
1699 private BookmarkTableDataSource bookmarkModel
;
1702 private NSTableView bookmarkTable
;
1705 private AbstractTableDelegate
<Host
> bookmarkTableDelegate
;
1707 public void setBookmarkTable(NSTableView view
) {
1708 bookmarkTable
= view
;
1709 bookmarkTable
.setSelectionHighlightStyle(NSTableView
.NSTableViewSelectionHighlightStyleSourceList
);
1710 bookmarkTable
.setDataSource((this.bookmarkModel
= new BookmarkTableDataSource(this)).id());
1712 NSTableColumn c
= bookmarkTableColumnFactory
.create(BookmarkTableDataSource
.Column
.icon
.name());
1713 c
.headerCell().setStringValue(StringUtils
.EMPTY
);
1714 c
.setResizingMask(NSTableColumn
.NSTableColumnNoResizing
);
1715 c
.setDataCell(imageCellPrototype
);
1716 bookmarkTable
.addTableColumn(c
);
1719 NSTableColumn c
= bookmarkTableColumnFactory
.create(BookmarkTableDataSource
.Column
.bookmark
.name());
1720 c
.headerCell().setStringValue(LocaleFactory
.localizedString("Bookmarks"));
1722 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
);
1723 c
.setDataCell(BookmarkCell
.bookmarkCell());
1724 bookmarkTable
.addTableColumn(c
);
1727 NSTableColumn c
= bookmarkTableColumnFactory
.create(BookmarkTableDataSource
.Column
.status
.name());
1728 c
.headerCell().setStringValue(StringUtils
.EMPTY
);
1732 c
.setResizingMask(NSTableColumn
.NSTableColumnAutoresizingMask
);
1733 c
.setDataCell(imageCellPrototype
);
1734 c
.dataCell().setAlignment(NSText
.NSCenterTextAlignment
);
1735 bookmarkTable
.addTableColumn(c
);
1737 bookmarkTable
.setDelegate((bookmarkTableDelegate
= new AbstractTableDelegate
<Host
>(
1738 bookmarkTable
.tableColumnWithIdentifier(BookmarkTableDataSource
.Column
.bookmark
.name())
1741 public String
tooltip(Host bookmark
) {
1742 return new HostUrlProvider().get(bookmark
);
1746 public void tableRowDoubleClicked(final ID sender
) {
1747 BrowserController
.this.connectBookmarkButtonClicked(sender
);
1751 public void enterKeyPressed(final ID sender
) {
1752 this.tableRowDoubleClicked(sender
);
1756 public void deleteKeyPressed(final ID sender
) {
1757 if(bookmarkModel
.getSource().allowsDelete()) {
1758 BrowserController
.this.deleteBookmarkButtonClicked(sender
);
1763 public void tableColumnClicked(NSTableView view
, NSTableColumn tableColumn
) {
1768 public void selectionDidChange(NSNotification notification
) {
1769 addBookmarkButton
.setEnabled(bookmarkModel
.getSource().allowsAdd());
1770 final int selected
= bookmarkTable
.numberOfSelectedRows().intValue();
1771 editBookmarkButton
.setEnabled(bookmarkModel
.getSource().allowsEdit() && selected
== 1);
1772 deleteBookmarkButton
.setEnabled(bookmarkModel
.getSource().allowsDelete() && selected
> 0);
1775 public CGFloat
tableView_heightOfRow(NSTableView view
, NSInteger row
) {
1776 final int size
= preferences
.getInteger("bookmark.icon.size");
1777 if(BookmarkCell
.SMALL_BOOKMARK_SIZE
== size
) {
1778 return new CGFloat(18);
1780 if(BookmarkCell
.MEDIUM_BOOKMARK_SIZE
== size
) {
1781 return new CGFloat(45);
1783 return new CGFloat(70);
1787 public boolean isTypeSelectSupported() {
1791 public String
tableView_typeSelectStringForTableColumn_row(NSTableView view
,
1792 NSTableColumn tableColumn
,
1794 return BookmarkNameProvider
.toString(bookmarkModel
.getSource().get(row
.intValue()));
1797 public boolean tableView_isGroupRow(NSTableView view
, NSInteger row
) {
1801 private static final double kSwipeGestureLeft
= 1.000000;
1802 private static final double kSwipeGestureRight
= -1.000000;
1803 private static final double kSwipeGestureUp
= 1.000000;
1804 private static final double kSwipeGestureDown
= -1.000000;
1807 * Available in Mac OS X v10.6 and later.
1809 * @param event Swipe event
1812 public void swipeWithEvent(NSEvent event
) {
1813 if(event
.deltaY().doubleValue() == kSwipeGestureUp
) {
1814 NSInteger row
= bookmarkTable
.selectedRow();
1816 if(-1 == row
.intValue()) {
1817 // No current selection
1818 next
= new NSInteger(0);
1821 next
= new NSInteger(row
.longValue() - 1);
1823 bookmarkTable
.selectRowIndexes(
1824 NSIndexSet
.indexSetWithIndex(next
), false);
1826 else if(event
.deltaY().doubleValue() == kSwipeGestureDown
) {
1827 NSInteger row
= bookmarkTable
.selectedRow();
1829 if(-1 == row
.intValue()) {
1830 // No current selection
1831 next
= new NSInteger(0);
1834 next
= new NSInteger(row
.longValue() + 1);
1836 bookmarkTable
.selectRowIndexes(
1837 NSIndexSet
.indexSetWithIndex(next
), false);
1841 // receive drag events from types
1842 bookmarkTable
.registerForDraggedTypes(NSArray
.arrayWithObjects(
1843 NSPasteboard
.URLPboardType
,
1844 NSPasteboard
.StringPboardType
,
1845 // Accept bookmark files dragged from the Finder
1846 NSPasteboard
.FilenamesPboardType
,
1847 // Accept file promises made myself
1848 NSPasteboard
.FilesPromisePboardType
1850 this._updateBookmarkCell();
1852 final int size
= preferences
.getInteger("bookmark.icon.size");
1853 if(BookmarkCell
.SMALL_BOOKMARK_SIZE
== size
) {
1854 bookmarkTable
.setRowHeight(new CGFloat(18));
1856 else if(BookmarkCell
.MEDIUM_BOOKMARK_SIZE
== size
) {
1857 bookmarkTable
.setRowHeight(new CGFloat(45));
1860 bookmarkTable
.setRowHeight(new CGFloat(70));
1863 // setting appearance attributes()
1864 bookmarkTable
.setUsesAlternatingRowBackgroundColors(preferences
.getBoolean("browser.alternatingRows"));
1865 bookmarkTable
.setGridStyleMask(NSTableView
.NSTableViewGridNone
);
1867 // selection properties
1868 bookmarkTable
.setAllowsMultipleSelection(true);
1869 bookmarkTable
.setAllowsEmptySelection(true);
1870 bookmarkTable
.setAllowsColumnResizing(false);
1871 bookmarkTable
.setAllowsColumnSelection(false);
1872 bookmarkTable
.setAllowsColumnReordering(false);
1873 bookmarkTable
.sizeToFit();
1876 public NSTableView
getBookmarkTable() {
1877 return bookmarkTable
;
1880 public BookmarkTableDataSource
getBookmarkModel() {
1881 return bookmarkModel
;
1885 private NSPopUpButton actionPopupButton
;
1887 public void setActionPopupButton(NSPopUpButton actionPopupButton
) {
1888 this.actionPopupButton
= actionPopupButton
;
1889 this.actionPopupButton
.setPullsDown(true);
1890 this.actionPopupButton
.setAutoenablesItems(true);
1893 public NSPopUpButton
getActionPopupButton() {
1894 return actionPopupButton
;
1898 private NSComboBox quickConnectPopup
;
1900 private ProxyController quickConnectPopupModel
= new QuickConnectModel();
1902 public void setQuickConnectPopup(NSComboBox quickConnectPopup
) {
1903 this.quickConnectPopup
= quickConnectPopup
;
1904 this.quickConnectPopup
.setTarget(this.id());
1905 this.quickConnectPopup
.setCompletes(true);
1906 this.quickConnectPopup
.setAction(Foundation
.selector("quickConnectSelectionChanged:"));
1907 // Make sure action is not sent twice.
1908 this.quickConnectPopup
.cell().setSendsActionOnEndEditing(false);
1909 this.quickConnectPopup
.setUsesDataSource(true);
1910 this.quickConnectPopup
.setDataSource(quickConnectPopupModel
.id());
1911 notificationCenter
.addObserver(this.id(),
1912 Foundation
.selector("quickConnectWillPopUp:"),
1913 NSComboBox
.ComboBoxWillPopUpNotification
,
1914 this.quickConnectPopup
);
1915 this.quickConnectWillPopUp(null);
1918 public NSComboBox
getQuickConnectPopup() {
1919 return quickConnectPopup
;
1922 private class QuickConnectModel
extends ProxyController
implements NSComboBox
.DataSource
{
1924 public NSInteger
numberOfItemsInComboBox(final NSComboBox combo
) {
1925 return new NSInteger(bookmarks
.size());
1929 public NSObject
comboBox_objectValueForItemAtIndex(final NSComboBox sender
, final NSInteger row
) {
1930 return NSString
.stringWithString(
1931 BookmarkNameProvider
.toString(bookmarks
.get(row
.intValue()))
1936 public void quickConnectWillPopUp(NSNotification notification
) {
1937 int size
= bookmarks
.size();
1938 quickConnectPopup
.setNumberOfVisibleItems(size
> 10 ?
new NSInteger(10) : new NSInteger(size
));
1942 public void quickConnectSelectionChanged(final ID sender
) {
1943 String input
= quickConnectPopup
.stringValue();
1944 if(StringUtils
.isBlank(input
)) {
1947 input
= input
.trim();
1948 // First look for equivalent bookmarks
1949 for(Host h
: bookmarks
) {
1950 if(BookmarkNameProvider
.toString(h
).equals(input
)) {
1955 // Try to parse the input as a URL and extract protocol, hostname, username and password if any.
1956 this.mount(HostParser
.parse(input
));
1960 private NSTextField searchField
;
1962 public void setSearchField(NSTextField searchField
) {
1963 this.searchField
= searchField
;
1964 notificationCenter
.addObserver(this.id(),
1965 Foundation
.selector("searchFieldTextDidChange:"),
1966 NSControl
.NSControlTextDidChangeNotification
,
1971 public void searchButtonClicked(final ID sender
) {
1972 this.window().makeFirstResponder(searchField
);
1975 public void searchFieldTextDidChange(NSNotification notification
) {
1976 if(this.getSelectedTabView() == TAB_BOOKMARKS
) {
1977 this.setBookmarkFilter(searchField
.stringValue());
1979 else { // TAB_LIST_VIEW || TAB_OUTLINE_VIEW
1980 this.setPathFilter(searchField
.stringValue());
1985 private void setBookmarkFilter(final String searchString
) {
1986 if(StringUtils
.isBlank(searchString
)) {
1987 searchField
.setStringValue(StringUtils
.EMPTY
);
1988 bookmarkModel
.setFilter(null);
1991 bookmarkModel
.setFilter(new HostFilter() {
1993 public boolean accept(Host host
) {
1994 return StringUtils
.lowerCase(BookmarkNameProvider
.toString(host
)).contains(searchString
.toLowerCase(Locale
.ROOT
))
1995 || ((null != host
.getComment()) && StringUtils
.lowerCase(host
.getComment()).contains(searchString
.toLowerCase(Locale
.ROOT
)))
1996 || ((null != host
.getCredentials().getUsername()) && StringUtils
.lowerCase(host
.getCredentials().getUsername()).contains(searchString
.toLowerCase(Locale
.ROOT
)))
1997 || StringUtils
.lowerCase(host
.getHostname()).contains(searchString
.toLowerCase(Locale
.ROOT
));
2001 this.reloadBookmarks();
2004 // ----------------------------------------------------------
2006 // ----------------------------------------------------------
2009 public void connectBookmarkButtonClicked(final ID sender
) {
2010 if(bookmarkTable
.numberOfSelectedRows().intValue() == 1) {
2011 final Host selected
= bookmarkModel
.getSource().get(bookmarkTable
.selectedRow().intValue());
2012 this.mount(selected
);
2017 private NSButton editBookmarkButton
;
2019 public void setEditBookmarkButton(NSButton editBookmarkButton
) {
2020 this.editBookmarkButton
= editBookmarkButton
;
2021 this.editBookmarkButton
.setEnabled(false);
2022 this.editBookmarkButton
.setTarget(this.id());
2023 this.editBookmarkButton
.setAction(Foundation
.selector("editBookmarkButtonClicked:"));
2027 public void editBookmarkButtonClicked(final ID sender
) {
2028 final BookmarkController c
= BookmarkControllerFactory
.create(
2029 bookmarkModel
.getSource().get(bookmarkTable
.selectedRow().intValue())
2031 c
.window().makeKeyAndOrderFront(null);
2035 public void duplicateBookmarkButtonClicked(final ID sender
) {
2036 final Host selected
= bookmarkModel
.getSource().get(bookmarkTable
.selectedRow().intValue());
2037 this.selectBookmarks();
2038 final Host duplicate
= new HostDictionary().deserialize(selected
.serialize(SerializerFactory
.get()));
2039 // Make sure a new UUID is asssigned for duplicate
2040 duplicate
.setUuid(null);
2041 this.addBookmark(duplicate
);
2045 private NSButton addBookmarkButton
;
2047 public void setAddBookmarkButton(NSButton addBookmarkButton
) {
2048 this.addBookmarkButton
= addBookmarkButton
;
2049 this.addBookmarkButton
.setTarget(this.id());
2050 this.addBookmarkButton
.setAction(Foundation
.selector("addBookmarkButtonClicked:"));
2054 public void addBookmarkButtonClicked(final ID sender
) {
2055 final Host bookmark
;
2056 if(this.isMounted()) {
2057 Path selected
= this.getSelectedPath();
2058 if(null == selected
|| !selected
.isDirectory()) {
2059 selected
= this.workdir();
2061 bookmark
= new HostDictionary().deserialize(session
.getHost().serialize(SerializerFactory
.get()));
2062 // Make sure a new UUID is asssigned for duplicate
2063 bookmark
.setUuid(null);
2064 bookmark
.setDefaultPath(selected
.getAbsolute());
2067 bookmark
= new Host(ProtocolFactory
.forName(preferences
.getProperty("connection.protocol.default")),
2068 preferences
.getProperty("connection.hostname.default"),
2069 preferences
.getInteger("connection.port.default"));
2071 this.selectBookmarks();
2072 this.addBookmark(bookmark
);
2075 public void addBookmark(Host item
) {
2076 bookmarkModel
.setFilter(null);
2077 bookmarkModel
.getSource().add(item
);
2078 final int row
= bookmarkModel
.getSource().lastIndexOf(item
);
2079 final NSInteger index
= new NSInteger(row
);
2080 bookmarkTable
.selectRowIndexes(NSIndexSet
.indexSetWithIndex(index
), false);
2081 bookmarkTable
.scrollRowToVisible(index
);
2082 final BookmarkController c
= BookmarkControllerFactory
.create(item
);
2083 c
.window().makeKeyAndOrderFront(null);
2087 private NSButton deleteBookmarkButton
;
2089 public void setDeleteBookmarkButton(NSButton deleteBookmarkButton
) {
2090 this.deleteBookmarkButton
= deleteBookmarkButton
;
2091 this.deleteBookmarkButton
.setEnabled(false);
2092 this.deleteBookmarkButton
.setTarget(this.id());
2093 this.deleteBookmarkButton
.setAction(Foundation
.selector("deleteBookmarkButtonClicked:"));
2097 public void deleteBookmarkButtonClicked(final ID sender
) {
2098 NSIndexSet iterator
= bookmarkTable
.selectedRowIndexes();
2099 final List
<Host
> selected
= new ArrayList
<Host
>();
2100 for(NSUInteger index
= iterator
.firstIndex(); !index
.equals(NSIndexSet
.NSNotFound
); index
= iterator
.indexGreaterThanIndex(index
)) {
2101 selected
.add(bookmarkModel
.getSource().get(index
.intValue()));
2103 StringBuilder alertText
= new StringBuilder(
2104 LocaleFactory
.localizedString("Do you want to delete the selected bookmark?"));
2106 Iterator
<Host
> iter
= selected
.iterator();
2107 while(i
< 10 && iter
.hasNext()) {
2108 alertText
.append("\n").append(Character
.toString('\u2022')).append(" ").append(
2109 BookmarkNameProvider
.toString(iter
.next())
2113 if(iter
.hasNext()) {
2114 alertText
.append("\n").append(Character
.toString('\u2022')).append(" " + "…");
2116 final NSAlert alert
= NSAlert
.alert(LocaleFactory
.localizedString("Delete Bookmark"),
2117 alertText
.toString(),
2118 LocaleFactory
.localizedString("Delete"),
2119 LocaleFactory
.localizedString("Cancel"),
2121 this.alert(alert
, new SheetCallback() {
2123 public void callback(int returncode
) {
2124 if(returncode
== DEFAULT_OPTION
) {
2125 bookmarkTable
.deselectAll(null);
2126 bookmarkModel
.getSource().removeAll(selected
);
2132 // ----------------------------------------------------------
2133 // Browser navigation
2134 // ----------------------------------------------------------
2136 public Navigation
getNavigation() {
2140 private static final int NAVIGATION_LEFT_SEGMENT_BUTTON
= 0;
2141 private static final int NAVIGATION_RIGHT_SEGMENT_BUTTON
= 1;
2143 private static final int NAVIGATION_UP_SEGMENT_BUTTON
= 0;
2145 private NSSegmentedControl navigationButton
;
2147 public void setNavigationButton(NSSegmentedControl navigationButton
) {
2148 this.navigationButton
= navigationButton
;
2149 this.navigationButton
.setTarget(this.id());
2150 this.navigationButton
.setAction(Foundation
.selector("navigationButtonClicked:"));
2151 this.navigationButton
.setImage_forSegment(IconCacheFactory
.<NSImage
>get().iconNamed("nav-backward.tiff"),
2152 NAVIGATION_LEFT_SEGMENT_BUTTON
);
2153 this.navigationButton
.setImage_forSegment(IconCacheFactory
.<NSImage
>get().iconNamed("nav-forward.tiff"),
2154 NAVIGATION_RIGHT_SEGMENT_BUTTON
);
2158 public void navigationButtonClicked(NSSegmentedControl sender
) {
2159 switch(sender
.selectedSegment()) {
2160 case NAVIGATION_LEFT_SEGMENT_BUTTON
: {
2161 this.backButtonClicked(sender
.id());
2164 case NAVIGATION_RIGHT_SEGMENT_BUTTON
: {
2165 this.forwardButtonClicked(sender
.id());
2172 public void backButtonClicked(final ID sender
) {
2173 final Path selected
= navigation
.back();
2174 if(selected
!= null) {
2175 final Path previous
= this.workdir();
2176 if(previous
.getParent().equals(selected
)) {
2177 this.setWorkdir(selected
, previous
);
2180 this.setWorkdir(selected
);
2186 public void forwardButtonClicked(final ID sender
) {
2187 final Path selected
= navigation
.forward();
2188 if(selected
!= null) {
2189 this.setWorkdir(selected
);
2194 private NSSegmentedControl upButton
;
2196 public void setUpButton(NSSegmentedControl upButton
) {
2197 this.upButton
= upButton
;
2198 this.upButton
.setTarget(this.id());
2199 this.upButton
.setAction(Foundation
.selector("upButtonClicked:"));
2200 this.upButton
.setImage_forSegment(IconCacheFactory
.<NSImage
>get().iconNamed("nav-up.tiff"),
2201 NAVIGATION_UP_SEGMENT_BUTTON
);
2205 public void upButtonClicked(final ID sender
) {
2206 final Path previous
= this.workdir();
2207 this.setWorkdir(previous
.getParent(), previous
);
2210 private Path workdir
;
2213 private NSPopUpButton pathPopupButton
;
2215 public void setPathPopup(NSPopUpButton pathPopupButton
) {
2216 this.pathPopupButton
= pathPopupButton
;
2217 this.pathPopupButton
.setTarget(this.id());
2218 this.pathPopupButton
.setAction(Foundation
.selector("pathPopupSelectionChanged:"));
2222 public void pathPopupSelectionChanged(final NSPopUpButton sender
) {
2223 final String selected
= sender
.selectedItem().representedObject();
2224 if(selected
!= null) {
2225 final Path workdir
= this.workdir();
2227 while(!p
.getAbsolute().equals(selected
)) {
2231 if(workdir
.getParent().equals(p
)) {
2232 this.setWorkdir(p
, workdir
);
2241 private NSPopUpButton encodingPopup
;
2243 public void setEncodingPopup(NSPopUpButton encodingPopup
) {
2244 this.encodingPopup
= encodingPopup
;
2245 this.encodingPopup
.setTarget(this.id());
2246 this.encodingPopup
.setAction(Foundation
.selector("encodingButtonClicked:"));
2247 this.encodingPopup
.removeAllItems();
2248 this.encodingPopup
.addItemsWithTitles(NSArray
.arrayWithObjects(new DefaultCharsetProvider().availableCharsets()));
2249 this.encodingPopup
.selectItemWithTitle(preferences
.getProperty("browser.charset.encoding"));
2252 public NSPopUpButton
getEncodingPopup() {
2253 return encodingPopup
;
2257 public void encodingButtonClicked(final NSPopUpButton sender
) {
2258 this.encodingChanged(sender
.titleOfSelectedItem());
2262 public void encodingMenuClicked(final NSMenuItem sender
) {
2263 this.encodingChanged(sender
.title());
2266 public void encodingChanged(final String encoding
) {
2267 if(null == encoding
) {
2270 this.setEncoding(encoding
);
2271 if(this.isMounted()) {
2272 if(session
.getEncoding().equals(encoding
)) {
2275 session
.getHost().setEncoding(encoding
);
2276 this.mount(session
.getHost());
2281 * @param encoding Character encoding
2283 private void setEncoding(final String encoding
) {
2284 this.encodingPopup
.selectItemWithTitle(encoding
);
2287 // ----------------------------------------------------------
2289 // ----------------------------------------------------------
2292 public void toggleLogDrawer(final ID sender
) {
2293 this.logDrawer
.toggle(this.id());
2296 // ----------------------------------------------------------
2298 // ----------------------------------------------------------
2301 protected NSProgressIndicator statusSpinner
;
2303 public void setStatusSpinner(NSProgressIndicator statusSpinner
) {
2304 this.statusSpinner
= statusSpinner
;
2305 this.statusSpinner
.setDisplayedWhenStopped(false);
2306 this.statusSpinner
.setIndeterminate(true);
2310 protected NSProgressIndicator browserSpinner
;
2312 public void setBrowserSpinner(NSProgressIndicator browserSpinner
) {
2313 this.browserSpinner
= browserSpinner
;
2316 public NSProgressIndicator
getBrowserSpinner() {
2317 return browserSpinner
;
2321 private NSTextField statusLabel
;
2323 public void setStatusLabel(NSTextField statusLabel
) {
2324 this.statusLabel
= statusLabel
;
2327 public void setStatus() {
2328 final BackgroundAction current
= this.getActions().getCurrent();
2329 this.message(null != current ? current
.getActivity() : null);
2333 public void stop(final BackgroundAction action
) {
2334 this.invoke(new DefaultMainAction() {
2337 statusSpinner
.stopAnimation(null);
2344 public void start(final BackgroundAction action
) {
2345 this.invoke(new DefaultMainAction() {
2348 statusSpinner
.startAnimation(null);
2351 super.start(action
);
2355 * @param label Status message
2358 public void message(final String label
) {
2359 if(StringUtils
.isNotBlank(label
)) {
2360 // Update the status label at the bottom of the browser window
2361 statusLabel
.setAttributedStringValue(NSAttributedString
.attributedStringWithAttributes(label
,
2362 TRUNCATE_MIDDLE_ATTRIBUTES
));
2365 if(getSelectedTabView() == TAB_BOOKMARKS
) {
2366 statusLabel
.setAttributedStringValue(
2367 NSAttributedString
.attributedStringWithAttributes(String
.format("%s %s", bookmarkTable
.numberOfRows(),
2368 LocaleFactory
.localizedString("Bookmarks")),
2369 TRUNCATE_MIDDLE_ATTRIBUTES
2376 statusLabel
.setAttributedStringValue(
2377 NSAttributedString
.attributedStringWithAttributes(MessageFormat
.format(LocaleFactory
.localizedString("{0} Files"),
2378 String
.valueOf(getSelectedBrowserView().numberOfRows())),
2379 TRUNCATE_MIDDLE_ATTRIBUTES
2384 statusLabel
.setStringValue(StringUtils
.EMPTY
);
2391 public void log(final boolean request
, final String message
) {
2392 transcript
.log(request
, message
);
2396 private NSButton securityLabel
;
2398 public void setSecurityLabel(NSButton securityLabel
) {
2399 this.securityLabel
= securityLabel
;
2400 this.securityLabel
.setEnabled(false);
2401 this.securityLabel
.setTarget(this.id());
2402 this.securityLabel
.setAction(Foundation
.selector("securityLabelClicked:"));
2406 public void securityLabelClicked(final ID sender
) {
2407 if(session
instanceof SSLSession
) {
2408 final SSLSession
<?
> secured
= (SSLSession
) session
;
2409 final List
<X509Certificate
> certificates
= secured
.getAcceptedIssuers();
2411 CertificateStoreFactory
.get(this).display(certificates
);
2413 catch(CertificateException e
) {
2414 log
.warn(String
.format("Failure decoding certificate %s", e
.getMessage()));
2419 // ----------------------------------------------------------
2420 // Selector methods for the toolbar items
2421 // ----------------------------------------------------------
2423 public void quicklookButtonClicked(final ID sender
) {
2424 if(quicklook
.isOpen()) {
2428 this.updateQuickLookSelection(this.getSelectedPaths());
2433 * Marks all expanded directories as invalid and tells the
2434 * browser table to reload its data
2436 * @param sender Toolbar button
2439 public void reloadButtonClicked(final ID sender
) {
2440 if(this.isMounted()) {
2441 final Set
<Path
> folders
= new HashSet
<Path
>();
2442 switch(browserSwitchView
.selectedSegment()) {
2443 case SWITCH_OUTLINE_VIEW
: {
2444 for(int i
= 0; i
< browserOutlineView
.numberOfRows().intValue(); i
++) {
2445 final NSObject item
= browserOutlineView
.itemAtRow(new NSInteger(i
));
2446 if(browserOutlineView
.isItemExpanded(item
)) {
2447 final Path folder
= cache
.lookup(new NSObjectPathReference(item
));
2448 if(null == folder
) {
2451 folders
.add(folder
);
2457 folders
.add(workdir
);
2458 this.reload(workdir
, folders
, this.getSelectedPaths(), true);
2463 * Open a new browser with the current selected folder as the working directory
2465 * @param sender Toolbar button
2468 public void newBrowserButtonClicked(final ID sender
) {
2469 Path selected
= this.getSelectedPath();
2470 if(null == selected
|| !selected
.isDirectory()) {
2471 selected
= this.workdir();
2473 BrowserController c
= MainController
.newDocument(true);
2474 final Host host
= new HostDictionary().deserialize(session
.getHost().serialize(SerializerFactory
.get()));
2475 host
.setDefaultPath(selected
.getAbsolute());
2480 * @param selected File
2481 * @return True if the selected path is editable (not a directory and no known binary file)
2483 protected boolean isEditable(final Path selected
) {
2484 if(this.isMounted()) {
2485 if(session
.getHost().getCredentials().isAnonymousLogin()) {
2488 return selected
.isFile();
2494 public void gotoButtonClicked(final ID sender
) {
2495 final SheetController sheet
= new GotoController(this, cache
);
2500 public void createFileButtonClicked(final ID sender
) {
2501 final SheetController sheet
= new CreateFileController(this, cache
);
2506 public void createSymlinkButtonClicked(final ID sender
) {
2507 final SheetController sheet
= new CreateSymlinkController(this, cache
);
2512 public void duplicateFileButtonClicked(final ID sender
) {
2513 final SheetController sheet
= new DuplicateFileController(this, cache
);
2518 public void createFolderButtonClicked(final ID sender
) {
2519 final Location feature
= session
.getFeature(Location
.class);
2520 final SheetController sheet
= new FolderController(this, cache
,
2521 feature
!= null ? feature
.getLocations() : Collections
.<Location
.Name
>emptySet());
2526 public void renameFileButtonClicked(final ID sender
) {
2527 final NSTableView browser
= this.getSelectedBrowserView();
2528 browser
.editRow(browser
.columnWithIdentifier(Column
.filename
.name()),
2529 browser
.selectedRow(), true);
2530 final Path selected
= this.getSelectedPath();
2531 if(StringUtils
.isNotBlank(selected
.getExtension())) {
2532 NSText view
= browser
.currentEditor();
2533 int index
= selected
.getName().indexOf(selected
.getExtension()) - 1;
2535 view
.setSelectedRange(NSRange
.NSMakeRange(new NSUInteger(0), new NSUInteger(index
)));
2541 public void sendCustomCommandClicked(final ID sender
) {
2542 SheetController sheet
= new CommandController(this, session
);
2547 public void editMenuClicked(final NSMenuItem sender
) {
2548 final EditorFactory factory
= EditorFactory
.instance();
2549 for(Path selected
: this.getSelectedPaths()) {
2550 final Editor editor
= factory
.create(this, session
,
2551 new Application(sender
.representedObject()), selected
);
2557 public void editButtonClicked(final ID sender
) {
2558 final EditorFactory factory
= EditorFactory
.instance();
2559 for(Path selected
: this.getSelectedPaths()) {
2560 this.edit(selected
);
2564 protected void edit(final Path file
) {
2565 this.edit(EditorFactory
.instance().create(this, session
, file
));
2568 protected void edit(final Editor editor
) {
2569 editors
.add(editor
);
2570 this.background(new WorkerBackgroundAction
<Transfer
>(this, session
, editor
.open(new ApplicationQuitCallback() {
2572 public void callback() {
2573 editors
.remove(editor
);
2575 }, new DisabledTransferErrorCallback(), new DefaultEditorListener(this, session
, editor
))));
2579 public void openBrowserButtonClicked(final ID sender
) {
2580 final DescriptiveUrlBag list
;
2581 if(this.getSelectionCount() == 1) {
2582 list
= session
.getFeature(UrlProvider
.class).toUrl(this.getSelectedPath());
2585 list
= session
.getFeature(UrlProvider
.class).toUrl(this.workdir());
2587 if(!list
.isEmpty()) {
2588 BrowserLauncherFactory
.get().open(list
.find(DescriptiveUrl
.Type
.http
).getUrl());
2593 public void infoButtonClicked(final ID sender
) {
2594 if(this.getSelectionCount() > 0) {
2595 InfoController c
= InfoControllerFactory
.create(this, this.getSelectedPaths());
2596 c
.window().makeKeyAndOrderFront(null);
2601 public void revertFileButtonClicked(final ID sender
) {
2602 new RevertController(this).revert(this.getSelectedPaths());
2606 public void deleteFileButtonClicked(final ID sender
) {
2607 new DeleteController(this).delete(this.getSelectedPaths());
2610 private NSOpenPanel downloadToPanel
;
2613 public void downloadToButtonClicked(final ID sender
) {
2614 downloadToPanel
= NSOpenPanel
.openPanel();
2615 downloadToPanel
.setCanChooseDirectories(true);
2616 downloadToPanel
.setCanCreateDirectories(true);
2617 downloadToPanel
.setCanChooseFiles(false);
2618 downloadToPanel
.setAllowsMultipleSelection(false);
2619 downloadToPanel
.setPrompt(LocaleFactory
.localizedString("Choose"));
2620 downloadToPanel
.beginSheetForDirectory(new DownloadDirectoryFinder().find(session
.getHost()).getAbsolute(),
2621 null, this.window
, this.id(),
2622 Foundation
.selector("downloadToPanelDidEnd:returnCode:contextInfo:"), null);
2625 public void downloadToPanelDidEnd_returnCode_contextInfo(final NSOpenPanel sheet
, final int returncode
, final ID contextInfo
) {
2626 sheet
.orderOut(this.id());
2627 if(returncode
== SheetCallback
.DEFAULT_OPTION
) {
2628 if(sheet
.filename() != null) {
2629 final Local target
= LocalFactory
.get(sheet
.filename());
2630 new DownloadDirectoryFinder().save(session
.getHost(), target
);
2631 final List
<TransferItem
> downloads
= new ArrayList
<TransferItem
>();
2632 for(Path file
: this.getSelectedPaths()) {
2633 downloads
.add(new TransferItem(file
, LocalFactory
.get(target
, file
.getName())));
2635 this.transfer(new DownloadTransfer(session
.getHost(), downloads
), Collections
.<Path
>emptyList());
2638 downloadToPanel
= null;
2642 private NSSavePanel downloadAsPanel
;
2645 public void downloadAsButtonClicked(final ID sender
) {
2646 downloadAsPanel
= NSSavePanel
.savePanel();
2647 downloadAsPanel
.setMessage(LocaleFactory
.localizedString("Download the selected file to…"));
2648 downloadAsPanel
.setNameFieldLabel(LocaleFactory
.localizedString("Download As:"));
2649 downloadAsPanel
.setPrompt(LocaleFactory
.localizedString("Download", "Transfer"));
2650 downloadAsPanel
.setCanCreateDirectories(true);
2651 downloadAsPanel
.beginSheetForDirectory(new DownloadDirectoryFinder().find(session
.getHost()).getAbsolute(),
2652 this.getSelectedPath().getName(), this.window
, this.id(),
2653 Foundation
.selector("downloadAsPanelDidEnd:returnCode:contextInfo:"), null);
2656 public void downloadAsPanelDidEnd_returnCode_contextInfo(final NSSavePanel sheet
, final int returncode
, final ID contextInfo
) {
2657 sheet
.orderOut(this.id());
2658 if(returncode
== SheetCallback
.DEFAULT_OPTION
) {
2659 if(sheet
.filename() != null) {
2660 final Local target
= LocalFactory
.get(sheet
.filename());
2661 new DownloadDirectoryFinder().save(session
.getHost(), target
.getParent());
2662 final List
<TransferItem
> downloads
2663 = Collections
.singletonList(new TransferItem(this.getSelectedPath(), target
));
2664 this.transfer(new DownloadTransfer(session
.getHost(), downloads
), Collections
.<Path
>emptyList());
2670 private NSOpenPanel syncPanel
;
2673 public void syncButtonClicked(final ID sender
) {
2674 final Path selection
;
2675 if(this.getSelectionCount() == 1 &&
2676 this.getSelectedPath().isDirectory()) {
2677 selection
= this.getSelectedPath();
2680 selection
= this.workdir();
2682 syncPanel
= NSOpenPanel
.openPanel();
2683 syncPanel
.setCanChooseDirectories(selection
.isDirectory());
2684 syncPanel
.setTreatsFilePackagesAsDirectories(true);
2685 syncPanel
.setCanChooseFiles(selection
.isFile());
2686 syncPanel
.setCanCreateDirectories(true);
2687 syncPanel
.setAllowsMultipleSelection(false);
2688 syncPanel
.setMessage(MessageFormat
.format(LocaleFactory
.localizedString("Synchronize {0} with"),
2689 selection
.getName()));
2690 syncPanel
.setPrompt(LocaleFactory
.localizedString("Choose"));
2691 syncPanel
.beginSheetForDirectory(new UploadDirectoryFinder().find(session
.getHost()).getAbsolute(),
2692 null, this.window
, this.id(),
2693 Foundation
.selector("syncPanelDidEnd:returnCode:contextInfo:"), null //context info
2697 public void syncPanelDidEnd_returnCode_contextInfo(final NSOpenPanel sheet
, final int returncode
, final ID contextInfo
) {
2698 sheet
.orderOut(this.id());
2699 if(returncode
== SheetCallback
.DEFAULT_OPTION
) {
2700 if(sheet
.filename() != null) {
2701 final Local target
= LocalFactory
.get(sheet
.filename());
2702 new UploadDirectoryFinder().save(session
.getHost(), target
.getParent());
2703 final Path selected
;
2704 if(this.getSelectionCount() == 1 && this.getSelectedPath().isDirectory()) {
2705 selected
= this.getSelectedPath();
2708 selected
= this.workdir();
2710 this.transfer(new SyncTransfer(session
.getHost(), new TransferItem(selected
, target
)));
2716 public void downloadButtonClicked(final ID sender
) {
2717 final List
<TransferItem
> downloads
= new ArrayList
<TransferItem
>();
2718 final Local folder
= new DownloadDirectoryFinder().find(session
.getHost());
2719 for(Path file
: this.getSelectedPaths()) {
2720 downloads
.add(new TransferItem(
2721 file
, LocalFactory
.get(folder
, file
.getName())));
2723 this.transfer(new DownloadTransfer(session
.getHost(), downloads
), Collections
.<Path
>emptyList());
2726 private NSOpenPanel uploadPanel
;
2728 private NSButton uploadPanelHiddenFilesCheckbox
;
2731 public void uploadButtonClicked(final ID sender
) {
2732 uploadPanel
= NSOpenPanel
.openPanel();
2733 uploadPanel
.setCanChooseDirectories(true);
2734 uploadPanel
.setCanChooseFiles(session
.getFeature(Touch
.class).isSupported(
2735 new UploadTargetFinder(workdir
).find(this.getSelectedPath())
2737 uploadPanel
.setCanCreateDirectories(false);
2738 uploadPanel
.setTreatsFilePackagesAsDirectories(true);
2739 uploadPanel
.setAllowsMultipleSelection(true);
2740 uploadPanel
.setPrompt(LocaleFactory
.localizedString("Upload", "Transfer"));
2741 if(uploadPanel
.respondsToSelector(Foundation
.selector("setShowsHiddenFiles:"))) {
2742 uploadPanelHiddenFilesCheckbox
= NSButton
.buttonWithFrame(new NSRect(0, 0));
2743 uploadPanelHiddenFilesCheckbox
.setTitle(LocaleFactory
.localizedString("Show Hidden Files"));
2744 uploadPanelHiddenFilesCheckbox
.setTarget(this.id());
2745 uploadPanelHiddenFilesCheckbox
.setAction(Foundation
.selector("uploadPanelSetShowHiddenFiles:"));
2746 uploadPanelHiddenFilesCheckbox
.setButtonType(NSButton
.NSSwitchButton
);
2747 uploadPanelHiddenFilesCheckbox
.setState(NSCell
.NSOffState
);
2748 uploadPanelHiddenFilesCheckbox
.sizeToFit();
2749 uploadPanel
.setAccessoryView(uploadPanelHiddenFilesCheckbox
);
2751 uploadPanel
.beginSheetForDirectory(new UploadDirectoryFinder().find(session
.getHost()).getAbsolute(),
2754 Foundation
.selector("uploadPanelDidEnd:returnCode:contextInfo:"),
2758 public void uploadPanelSetShowHiddenFiles(ID sender
) {
2759 uploadPanel
.setShowsHiddenFiles(uploadPanelHiddenFilesCheckbox
.state() == NSCell
.NSOnState
);
2762 public void uploadPanelDidEnd_returnCode_contextInfo(final NSOpenPanel sheet
, final int returncode
, final ID contextInfo
) {
2763 sheet
.orderOut(this.id());
2764 if(returncode
== SheetCallback
.DEFAULT_OPTION
) {
2765 final Path destination
= new UploadTargetFinder(workdir
).find(this.getSelectedPath());
2766 // Selected files on the local filesystem
2767 final NSArray selected
= sheet
.filenames();
2768 final NSEnumerator iterator
= selected
.objectEnumerator();
2769 final List
<TransferItem
> uploads
= new ArrayList
<TransferItem
>();
2771 while((next
= iterator
.nextObject()) != null) {
2772 final Local local
= LocalFactory
.get(next
.toString());
2773 new UploadDirectoryFinder().save(session
.getHost(), local
.getParent());
2774 uploads
.add(new TransferItem(
2775 new Path(destination
, local
.getName(),
2776 local
.isDirectory() ? EnumSet
.of(Path
.Type
.directory
) : EnumSet
.of(Path
.Type
.file
)), local
2779 this.transfer(new UploadTransfer(session
.getHost(), uploads
));
2782 uploadPanelHiddenFilesCheckbox
= null;
2785 protected void transfer(final Transfer transfer
) {
2786 final List
<Path
> selected
= new ArrayList
<Path
>();
2787 for(TransferItem i
: transfer
.getRoots()) {
2788 selected
.add(i
.remote
);
2790 this.transfer(transfer
, selected
);
2794 * Transfers the files either using the queue or using the browser session if #connection.pool.max is 1
2796 * @param transfer Transfer Operation
2798 protected void transfer(final Transfer transfer
, final List
<Path
> selected
) {
2799 // Determine from current browser session if new connection should be opened for transfers
2800 this.transfer(transfer
, selected
, session
.getTransferType().equals(Host
.TransferType
.browser
));
2804 * @param transfer Transfer Operation
2805 * @param browser Transfer in browser window
2807 protected void transfer(final Transfer transfer
, final List
<Path
> selected
, boolean browser
) {
2808 final TransferCallback callback
= new TransferCallback() {
2810 public void complete(final Transfer transfer
) {
2811 invoke(new WindowMainAction(BrowserController
.this) {
2814 reload(workdir
, selected
, selected
);
2820 this.background(new TransferBackgroundAction(this, session
, cache
, new TransferAdapter() {
2822 public void progress(final TransferProgress status
) {
2823 message(status
.getProgress());
2825 }, transfer
, new TransferOptions()) {
2827 public void finish() {
2828 if(transfer
.isComplete()) {
2829 callback
.complete(transfer
);
2836 TransferControllerFactory
.get().start(transfer
, new TransferOptions(), callback
);
2841 public void insideButtonClicked(final ID sender
) {
2842 final Path selected
= this.getSelectedPath(); //first row selected
2843 if(null == selected
) {
2846 if(selected
.isDirectory()) {
2847 this.setWorkdir(selected
);
2849 else if(selected
.isFile() || this.getSelectionCount() > 1) {
2850 if(preferences
.getBoolean("browser.doubleclick.edit")) {
2851 this.editButtonClicked(null);
2854 this.downloadButtonClicked(null);
2860 public void connectButtonClicked(final ID sender
) {
2861 final SheetController controller
= ConnectionControllerFactory
.create(this);
2862 this.addListener(new WindowListener() {
2864 public void windowWillClose() {
2865 controller
.invalidate();
2868 controller
.beginSheet();
2872 public void disconnectButtonClicked(final ID sender
) {
2873 if(this.isActivityRunning()) {
2874 // Remove all pending actions
2875 for(BackgroundAction action
: this.getActions().toArray(
2876 new BackgroundAction
[this.getActions().size()])) {
2880 this.disconnect(new Runnable() {
2883 if(preferences
.getBoolean("browser.disconnect.bookmarks.show")) {
2887 selectBrowser(preferences
.getInteger("browser.view"));
2894 public void showHiddenFilesClicked(final NSMenuItem sender
) {
2895 if(sender
.state() == NSCell
.NSOnState
) {
2896 this.setShowHiddenFiles(false);
2897 sender
.setState(NSCell
.NSOffState
);
2899 else if(sender
.state() == NSCell
.NSOffState
) {
2900 this.setShowHiddenFiles(true);
2901 sender
.setState(NSCell
.NSOnState
);
2903 if(this.isMounted()) {
2909 * @return This browser's session or null if not mounted
2911 public Session
<?
> getSession() {
2915 public Cache
<Path
> getCache() {
2920 * @return true if the remote file system has been mounted
2922 public boolean isMounted() {
2923 return session
!= null && workdir
!= null;
2927 * @return true if mounted and the connection to the server is alive
2929 public boolean isConnected() {
2930 if(this.isMounted()) {
2931 return session
.isConnected();
2939 * Indicates whether the receiver can send and receive the specified pasteboard types.
2941 * Either sendType or returnType—but not both—may be empty. If sendType is empty,
2942 * the service doesn’t require input from the application requesting the service.
2943 * If returnType is empty, the service doesn’t return data.
2945 * @param sendType The pasteboard type the application needs to send.
2946 * @param returnType The pasteboard type the application needs to receive.
2947 * @return The object that can send and receive the specified types or nil
2948 * if the receiver knows of no object that can send and receive data of that type.
2950 public ID
validRequestorForSendType_returnType(String sendType
, String returnType
) {
2951 log
.debug("validRequestorForSendType_returnType:" + sendType
+ "," + returnType
);
2952 if(StringUtils
.isNotEmpty(sendType
)) {
2953 // Cannot send any data type
2956 if(StringUtils
.isNotEmpty(returnType
)) {
2957 // Can receive filenames
2958 if(NSPasteboard
.FilenamesPboardType
.equals(sendType
)) {
2968 * Reads data from the pasteboard and uses it to replace the current selection.
2970 * @param pboard Pasteboard
2971 * @return YES if your implementation was able to read the pasteboard data successfully; otherwise, NO.
2973 public boolean readSelectionFromPasteboard(NSPasteboard pboard
) {
2974 return this.upload(pboard
);
2980 * Writes the current selection to the pasteboard.
2982 * @param pboard Pasteboard
2983 * @param types Types in pasteboard
2984 * @return YES if your implementation was able to write one or more types to the pasteboard; otherwise, NO.
2986 public boolean writeSelectionToPasteboard_types(NSPasteboard pboard
, NSArray types
) {
2991 public void copy(final ID sender
) {
2993 pasteboard
.setCopy(true);
2994 final List
<Path
> s
= this.getSelectedPaths();
2996 // Writing data for private use when the item gets dragged to the transfer queue.
2999 final NSPasteboard clipboard
= NSPasteboard
.generalPasteboard();
3001 s
.add(this.workdir());
3003 clipboard
.declareTypes(NSArray
.arrayWithObject(
3004 NSString
.stringWithString(NSPasteboard
.StringPboardType
)), null);
3005 StringBuilder copy
= new StringBuilder();
3006 for(Iterator
<Path
> i
= s
.iterator(); i
.hasNext(); ) {
3007 copy
.append(i
.next().getAbsolute());
3012 if(!clipboard
.setStringForType(copy
.toString(), NSPasteboard
.StringPboardType
)) {
3013 log
.error("Error writing to NSPasteboard.StringPboardType.");
3018 public void cut(final ID sender
) {
3020 pasteboard
.setCut(true);
3021 for(Path s
: this.getSelectedPaths()) {
3022 // Writing data for private use when the item gets dragged to the transfer queue.
3025 final NSPasteboard clipboard
= NSPasteboard
.generalPasteboard();
3026 clipboard
.declareTypes(NSArray
.arrayWithObject(NSString
.stringWithString(NSPasteboard
.StringPboardType
)), null);
3027 if(!clipboard
.setStringForType(this.getSelectedPath().getAbsolute(), NSPasteboard
.StringPboardType
)) {
3028 log
.error("Error writing to NSPasteboard.StringPboardType.");
3033 public void paste(final ID sender
) {
3034 if(pasteboard
.isEmpty()) {
3035 NSPasteboard pboard
= NSPasteboard
.generalPasteboard();
3036 this.upload(pboard
);
3039 final Map
<Path
, Path
> files
= new HashMap
<Path
, Path
>();
3040 final Path parent
= this.workdir();
3041 for(final Path next
: pasteboard
) {
3042 Path renamed
= new Path(parent
, next
.getName(), next
.getType());
3043 files
.put(next
, renamed
);
3046 if(pasteboard
.isCut()) {
3047 new MoveController(this).rename(files
);
3049 if(pasteboard
.isCopy()) {
3050 new DuplicateFileController(this, cache
).duplicate(files
);
3056 * @param pboard Pasteboard with filenames
3057 * @return True if filenames are found in pasteboard and upload has started
3059 private boolean upload(NSPasteboard pboard
) {
3060 if(!this.isMounted()) {
3063 if(pboard
.availableTypeFromArray(NSArray
.arrayWithObject(NSPasteboard
.FilenamesPboardType
)) != null) {
3064 NSObject o
= pboard
.propertyListForType(NSPasteboard
.FilenamesPboardType
);
3066 if(o
.isKindOfClass(Rococoa
.createClass("NSArray", NSArray
._Class
.class))) {
3067 final NSArray elements
= Rococoa
.cast(o
, NSArray
.class);
3068 final Path workdir
= this.workdir();
3069 final List
<TransferItem
> uploads
= new ArrayList
<TransferItem
>();
3070 for(int i
= 0; i
< elements
.count().intValue(); i
++) {
3071 final Local local
= LocalFactory
.get(elements
.objectAtIndex(new NSUInteger(i
)).toString());
3072 uploads
.add(new TransferItem(new Path(workdir
, local
.getName(),
3073 local
.isDirectory() ? EnumSet
.of(Path
.Type
.directory
) : EnumSet
.of(Path
.Type
.file
)), local
));
3075 this.transfer(new UploadTransfer(session
.getHost(), uploads
));
3083 public void openTerminalButtonClicked(final ID sender
) {
3084 Path workdir
= null;
3085 if(this.getSelectionCount() == 1) {
3086 Path selected
= this.getSelectedPath();
3087 if(selected
.isDirectory()) {
3091 if(null == workdir
) {
3092 workdir
= this.workdir();
3095 final TerminalService terminal
= TerminalServiceFactory
.get();
3096 terminal
.open(session
.getHost(), workdir
);
3098 catch(AccessDeniedException e
) {
3099 this.alert(session
.getHost(), e
, new StringBuilder());
3104 public void archiveMenuClicked(final NSMenuItem sender
) {
3105 final Archive archive
= Archive
.forName(sender
.representedObject());
3106 this.archiveClicked(archive
);
3110 public void archiveButtonClicked(final NSToolbarItem sender
) {
3111 this.archiveClicked(Archive
.TARGZ
);
3115 * @param format Archive format
3117 private void archiveClicked(final Archive format
) {
3118 new ArchiveController(this).archive(format
, this.getSelectedPaths());
3122 public void unarchiveButtonClicked(final ID sender
) {
3123 new ArchiveController(this).unarchive(this.getSelectedPaths());
3127 * Accessor to the working directory
3129 * @return The current working directory or null if no file system is mounted
3131 protected Path
workdir() {
3135 public void setWorkdir(final Path directory
) {
3136 this.setWorkdir(directory
, Collections
.<Path
>emptyList());
3139 public void setWorkdir(final Path directory
, Path selected
) {
3140 this.setWorkdir(directory
, Collections
.singletonList(selected
));
3144 * Sets the current working directory. This will update the path selection menu and also add this path to the browsing history.
3146 * @param directory The new working directory to display or null to detach any working directory from the browser
3147 * @param selected Selected files in browser
3149 public void setWorkdir(final Path directory
, final List
<Path
> selected
) {
3150 if(log
.isDebugEnabled()) {
3151 log
.debug(String
.format("Set working directory to %s", directory
));
3153 // Remove any custom file filter
3154 this.setPathFilter(null);
3155 final NSTableView browser
= this.getSelectedBrowserView();
3156 window
.endEditingFor(browser
);
3157 if(null == directory
) {
3158 this.reload(null, Collections
.<Path
>emptySet(), selected
, false);
3161 this.reload(directory
, Collections
.singleton(directory
), selected
, false);
3165 private void setNavigation(boolean enabled
) {
3167 searchField
.setStringValue(StringUtils
.EMPTY
);
3169 pathPopupButton
.removeAllItems();
3171 // Update the current working directory
3172 navigation
.add(workdir
);
3175 this.addNavigation(p
);
3179 this.addNavigation(p
);
3181 pathPopupButton
.setEnabled(enabled
);
3182 navigationButton
.setEnabled_forSegment(enabled
&& navigation
.getBack().size() > 1, NAVIGATION_LEFT_SEGMENT_BUTTON
);
3183 navigationButton
.setEnabled_forSegment(enabled
&& navigation
.getForward().size() > 0, NAVIGATION_RIGHT_SEGMENT_BUTTON
);
3184 upButton
.setEnabled_forSegment(enabled
&& !workdir
.isRoot(), NAVIGATION_UP_SEGMENT_BUTTON
);
3187 private void addNavigation(final Path p
) {
3188 pathPopupButton
.addItemWithTitle(p
.getAbsolute());
3189 pathPopupButton
.lastItem().setRepresentedObject(p
.getAbsolute());
3191 pathPopupButton
.lastItem().setImage(IconCacheFactory
.<NSImage
>get().volumeIcon(session
.getHost().getProtocol(), 16));
3194 pathPopupButton
.lastItem().setImage(IconCacheFactory
.<NSImage
>get().fileIcon(p
, 16));
3199 * Initializes a session for the passed host. Setting up the listeners and adding any callback
3200 * controllers needed for login, trust management and hostkey verification.
3202 * @param host Bookmark
3203 * @return A session object bound to this browser controller
3205 private Session
init(final Host host
) {
3206 session
= SessionFactory
.create(host
,
3207 new KeychainX509TrustManager(new DefaultTrustManagerHostnameCallback(host
), this),
3208 new KeychainX509KeyManager(this));
3211 pasteboard
= PathPasteboardFactory
.getPasteboard(session
);
3212 this.setWorkdir(null);
3213 this.setEncoding(session
.getEncoding());
3218 * Open connection in browser
3220 * @param host Bookmark
3222 public void mount(final Host host
) {
3223 if(log
.isDebugEnabled()) {
3224 log
.debug(String
.format("Mount session for %s", host
));
3226 this.unmount(new Runnable() {
3229 // The browser has no session, we are allowed to proceed
3230 // Initialize the browser with the new session attaching all listeners
3231 final Session session
= init(host
);
3232 background(new WorkerBackgroundAction
<Path
>(BrowserController
.this, session
, cache
,
3233 new MountWorker(host
, cache
, listener
) {
3235 public void cleanup(final Path workdir
) {
3236 if(null == workdir
) {
3240 // Update status icon
3241 bookmarkTable
.setNeedsDisplay();
3242 // Set the working directory
3243 setWorkdir(workdir
);
3245 selectBrowser(preferences
.getInteger("browser.view"));
3246 // Set the window title
3247 window
.setRepresentedFilename(HistoryCollection
.defaultCollection().getFile(host
).getAbsolute());
3248 if(preferences
.getBoolean("browser.disconnect.confirm")) {
3249 window
.setDocumentEdited(true);
3251 securityLabel
.setImage(session
.isSecured() ? IconCacheFactory
.<NSImage
>get().iconNamed("locked.tiff")
3252 : IconCacheFactory
.<NSImage
>get().iconNamed("unlocked.tiff"));
3253 securityLabel
.setEnabled(session
instanceof SSLSession
);
3259 public void init() {
3261 window
.setTitle(BookmarkNameProvider
.toString(host
, true));
3262 window
.setRepresentedFilename(StringUtils
.EMPTY
);
3263 // Update status icon
3264 bookmarkTable
.setNeedsDisplay();
3274 * @return True if succeeded
3276 public boolean unmount() {
3277 return this.unmount(new Runnable() {
3286 * @param disconnected Callback after the session has been disconnected
3287 * @return True if the unmount process has finished, false if the user has to agree first
3288 * to close the connection
3290 public boolean unmount(final Runnable disconnected
) {
3291 return this.unmount(new SheetCallback() {
3293 public void callback(int returncode
) {
3294 if(returncode
== DEFAULT_OPTION
) {
3295 unmountImpl(disconnected
);
3302 * @param callback Confirmation callback
3303 * @param disconnected Action to run after disconnected
3304 * @return True if succeeded
3306 public boolean unmount(final SheetCallback callback
, final Runnable disconnected
) {
3307 if(log
.isDebugEnabled()) {
3308 log
.debug(String
.format("Unmount session %s", session
));
3310 if(this.isConnected() || this.isActivityRunning()) {
3311 if(preferences
.getBoolean("browser.disconnect.confirm")) {
3312 // Defer the unmount to the callback function
3313 final NSAlert alert
= NSAlert
.alert(
3314 MessageFormat
.format(LocaleFactory
.localizedString("Disconnect from {0}"), session
.getHost().getHostname()), //title
3315 LocaleFactory
.localizedString("The connection will be closed."), // message
3316 LocaleFactory
.localizedString("Disconnect"), // defaultbutton
3317 LocaleFactory
.localizedString("Cancel"), // alternate button
3320 alert
.setShowsSuppressionButton(true);
3321 alert
.suppressionButton().setTitle(LocaleFactory
.localizedString("Don't ask again", "Configuration"));
3322 this.alert(alert
, new SheetCallback() {
3324 public void callback(int returncode
) {
3325 if(alert
.suppressionButton().state() == NSCell
.NSOnState
) {
3326 // Never show again.
3327 preferences
.setProperty("browser.disconnect.confirm", false);
3329 callback
.callback(returncode
);
3336 this.unmountImpl(disconnected
);
3337 // Unmount succeeded
3342 * @param disconnected Action to run after disconnected
3344 private void unmountImpl(final Runnable disconnected
) {
3345 final List
<Editor
> list
= editors
;
3346 this.disconnect(new Runnable() {
3350 for(Editor e
: list
) {
3353 window
.setTitle(preferences
.getProperty("application.name"));
3354 window
.setRepresentedFilename(StringUtils
.EMPTY
);
3363 * Unmount this session
3365 private void disconnect(final Runnable disconnected
) {
3366 final InfoController c
= InfoControllerFactory
.get(BrowserController
.this);
3370 if(session
!= null) {
3371 this.background(new WorkerBackgroundAction
<Void
>(this, session
, cache
, new DisconnectWorker(session
.getHost())) {
3373 public void prepare() throws ConnectionCanceledException
{
3374 if(!session
.isConnected()) {
3375 throw new ConnectionCanceledException();
3381 protected boolean connect(Session session
) throws BackgroundException
{
3386 public void cleanup() {
3388 window
.setDocumentEdited(false);
3399 public void printDocument(final ID sender
) {
3400 this.print(this.getSelectedBrowserView());
3404 * @param app Singleton
3405 * @return NSApplication.TerminateLater if the application should not yet be terminated
3407 public static NSUInteger
applicationShouldTerminate(final NSApplication app
) {
3408 // Determine if there are any open connections
3409 for(final BrowserController controller
: MainController
.getBrowsers()) {
3410 if(!controller
.unmount(new SheetCallback() {
3412 public void callback(final int returncode
) {
3413 if(returncode
== DEFAULT_OPTION
) { //Disconnect
3414 controller
.window().close();
3415 if(NSApplication
.NSTerminateNow
.equals(BrowserController
.applicationShouldTerminate(app
))) {
3416 app
.replyToApplicationShouldTerminate(true);
3420 app
.replyToApplicationShouldTerminate(false);
3431 return NSApplication
.NSTerminateCancel
;
3434 return NSApplication
.NSTerminateNow
;
3438 public boolean windowShouldClose(final NSWindow sender
) {
3439 return this.unmount(new Runnable() {
3448 * @param item Menu item
3449 * @return True if the menu should be enabled
3452 public boolean validateMenuItem(final NSMenuItem item
) {
3453 final Selector action
= item
.action();
3454 if(action
.equals(Foundation
.selector("paste:"))) {
3455 final String title
= "Paste {0}";
3456 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
), StringUtils
.EMPTY
).trim());
3457 if(this.isMounted()) {
3458 if(pasteboard
.isEmpty()) {
3459 if(NSPasteboard
.generalPasteboard().availableTypeFromArray(NSArray
.arrayWithObject(NSPasteboard
.FilenamesPboardType
)) != null) {
3460 NSObject o
= NSPasteboard
.generalPasteboard().propertyListForType(NSPasteboard
.FilenamesPboardType
);
3462 if(o
.isKindOfClass(Rococoa
.createClass("NSArray", NSArray
._Class
.class))) {
3463 final NSArray elements
= Rococoa
.cast(o
, NSArray
.class);
3464 if(elements
.count().intValue() == 1) {
3465 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
),
3466 "\"" + elements
.objectAtIndex(new NSUInteger(0)) + "\"").trim());
3469 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
),
3470 MessageFormat
.format(LocaleFactory
.localizedString("{0} Files"),
3471 String
.valueOf(elements
.count().intValue()))
3479 if(pasteboard
.size() == 1) {
3480 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
),
3481 "\"" + pasteboard
.get(0).getName() + "\"").trim());
3484 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
),
3485 MessageFormat
.format(LocaleFactory
.localizedString("{0} Files"), String
.valueOf(pasteboard
.size()))).trim());
3490 else if(action
.equals(Foundation
.selector("cut:")) || action
.equals(Foundation
.selector("copy:"))) {
3491 String title
= null;
3492 if(action
.equals(Foundation
.selector("cut:"))) {
3495 else if(action
.equals(Foundation
.selector("copy:"))) {
3498 if(this.isMounted()) {
3499 int count
= this.getSelectionCount();
3501 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
), StringUtils
.EMPTY
).trim());
3503 else if(1 == count
) {
3504 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
),
3505 "\"" + this.getSelectedPath().getName() + "\"").trim());
3508 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
),
3509 MessageFormat
.format(LocaleFactory
.localizedString("{0} Files"), String
.valueOf(this.getSelectionCount()))).trim());
3513 item
.setTitle(MessageFormat
.format(LocaleFactory
.localizedString(title
), StringUtils
.EMPTY
).trim());
3516 else if(action
.equals(Foundation
.selector("showHiddenFilesClicked:"))) {
3517 item
.setState(this.getFilter() instanceof NullFilter ? NSCell
.NSOnState
: NSCell
.NSOffState
);
3519 else if(action
.equals(Foundation
.selector("encodingMenuClicked:"))) {
3520 if(this.isMounted()) {
3521 item
.setState(session
.getEncoding().equalsIgnoreCase(
3522 item
.title()) ? NSCell
.NSOnState
: NSCell
.NSOffState
);
3525 item
.setState(preferences
.getProperty("browser.charset.encoding").equalsIgnoreCase(
3526 item
.title()) ? NSCell
.NSOnState
: NSCell
.NSOffState
);
3529 else if(action
.equals(Foundation
.selector("browserSwitchMenuClicked:"))) {
3530 if(item
.tag() == preferences
.getInteger("browser.view")) {
3531 item
.setState(NSCell
.NSOnState
);
3534 item
.setState(NSCell
.NSOffState
);
3537 else if(action
.equals(Foundation
.selector("archiveMenuClicked:"))) {
3538 final Archive archive
= Archive
.forName(item
.representedObject());
3539 item
.setTitle(archive
.getTitle(this.getSelectedPaths()));
3541 else if(action
.equals(Foundation
.selector("quicklookButtonClicked:"))) {
3542 item
.setKeyEquivalent(" ");
3543 item
.setKeyEquivalentModifierMask(0);
3545 return this.validate(action
);
3548 private boolean validate(final Selector action
) {
3549 return new BrowserToolbarValidator(this).validate(action
);
3553 public boolean validateToolbarItem(final NSToolbarItem item
) {
3554 return new BrowserToolbarValidator(this).validate(item
);
3558 public NSToolbarItem
toolbar_itemForItemIdentifier_willBeInsertedIntoToolbar(final NSToolbar toolbar
, final String itemIdentifier
, boolean inserted
) {
3559 if(log
.isDebugEnabled()) {
3560 log
.debug("toolbar_itemForItemIdentifier_willBeInsertedIntoToolbar:" + itemIdentifier
);
3562 return browserToolbarFactory
.create(itemIdentifier
);
3566 * @param toolbar Window toolbar
3567 * @return The default configuration of toolbar items
3570 public NSArray
toolbarDefaultItemIdentifiers(final NSToolbar toolbar
) {
3571 return browserToolbarFactory
.getDefault();
3575 * @param toolbar Window toolbar
3576 * @return All available toolbar items
3579 public NSArray
toolbarAllowedItemIdentifiers(final NSToolbar toolbar
) {
3580 return browserToolbarFactory
.getAllowed();
3584 public NSArray
toolbarSelectableItemIdentifiers(NSToolbar toolbar
) {
3585 return NSArray
.array();
3589 * Overrriden to remove any listeners from the session
3592 public void invalidate() {
3593 if(quicklook
.isAvailable()) {
3594 if(quicklook
.isOpen()) {
3598 bookmarkTable
.setDelegate(null);
3599 bookmarkTable
.setDataSource(null);
3600 bookmarkModel
.invalidate();
3602 browserListView
.setDelegate(null);
3603 browserListView
.setDataSource(null);
3604 browserListModel
.invalidate();
3606 browserOutlineView
.setDelegate(null);
3607 browserOutlineView
.setDataSource(null);
3608 browserOutlineModel
.invalidate();
3610 toolbar
.setDelegate(null);
3612 browserListColumnsFactory
.clear();
3613 browserOutlineColumnsFactory
.clear();
3614 bookmarkTableColumnFactory
.clear();
3616 quickConnectPopup
.setDelegate(null);
3617 quickConnectPopup
.setDataSource(null);
3619 archiveMenu
.setDelegate(null);
3620 editMenu
.setDelegate(null);
3622 notificationCenter
.removeObserver(this.id());