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
.NSAlert
;
23 import ch
.cyberduck
.binding
.application
.NSApplication
;
24 import ch
.cyberduck
.binding
.application
.NSButton
;
25 import ch
.cyberduck
.binding
.application
.NSCell
;
26 import ch
.cyberduck
.binding
.application
.NSColor
;
27 import ch
.cyberduck
.binding
.application
.NSFont
;
28 import ch
.cyberduck
.binding
.application
.NSImage
;
29 import ch
.cyberduck
.binding
.application
.NSMenu
;
30 import ch
.cyberduck
.binding
.application
.NSMenuItem
;
31 import ch
.cyberduck
.binding
.application
.NSPasteboard
;
32 import ch
.cyberduck
.binding
.application
.NSPopUpButton
;
33 import ch
.cyberduck
.binding
.application
.NSWindow
;
34 import ch
.cyberduck
.binding
.application
.NSWorkspace
;
35 import ch
.cyberduck
.binding
.application
.SheetCallback
;
36 import ch
.cyberduck
.binding
.foundation
.NSAppleEventDescriptor
;
37 import ch
.cyberduck
.binding
.foundation
.NSAppleEventManager
;
38 import ch
.cyberduck
.binding
.foundation
.NSArray
;
39 import ch
.cyberduck
.binding
.foundation
.NSAttributedString
;
40 import ch
.cyberduck
.binding
.foundation
.NSBundle
;
41 import ch
.cyberduck
.binding
.foundation
.NSDictionary
;
42 import ch
.cyberduck
.binding
.foundation
.NSNotification
;
43 import ch
.cyberduck
.binding
.foundation
.NSNotificationCenter
;
44 import ch
.cyberduck
.binding
.foundation
.NSObject
;
45 import ch
.cyberduck
.core
.*;
46 import ch
.cyberduck
.core
.aquaticprime
.License
;
47 import ch
.cyberduck
.core
.aquaticprime
.LicenseFactory
;
48 import ch
.cyberduck
.core
.bonjour
.NotificationRendezvousListener
;
49 import ch
.cyberduck
.core
.bonjour
.Rendezvous
;
50 import ch
.cyberduck
.core
.bonjour
.RendezvousFactory
;
51 import ch
.cyberduck
.core
.exception
.AccessDeniedException
;
52 import ch
.cyberduck
.core
.exception
.BackgroundException
;
53 import ch
.cyberduck
.core
.importer
.CrossFtpBookmarkCollection
;
54 import ch
.cyberduck
.core
.importer
.FetchBookmarkCollection
;
55 import ch
.cyberduck
.core
.importer
.FilezillaBookmarkCollection
;
56 import ch
.cyberduck
.core
.importer
.FireFtpBookmarkCollection
;
57 import ch
.cyberduck
.core
.importer
.FlowBookmarkCollection
;
58 import ch
.cyberduck
.core
.importer
.InterarchyBookmarkCollection
;
59 import ch
.cyberduck
.core
.importer
.ThirdpartyBookmarkCollection
;
60 import ch
.cyberduck
.core
.importer
.TransmitBookmarkCollection
;
61 import ch
.cyberduck
.core
.local
.Application
;
62 import ch
.cyberduck
.core
.local
.ApplicationLauncherFactory
;
63 import ch
.cyberduck
.core
.local
.BrowserLauncherFactory
;
64 import ch
.cyberduck
.core
.notification
.NotificationServiceFactory
;
65 import ch
.cyberduck
.core
.preferences
.Preferences
;
66 import ch
.cyberduck
.core
.preferences
.PreferencesFactory
;
67 import ch
.cyberduck
.core
.resources
.IconCacheFactory
;
68 import ch
.cyberduck
.core
.serializer
.HostDictionary
;
69 import ch
.cyberduck
.core
.sparkle
.Updater
;
70 import ch
.cyberduck
.core
.threading
.AbstractBackgroundAction
;
71 import ch
.cyberduck
.core
.transfer
.DownloadTransfer
;
72 import ch
.cyberduck
.core
.transfer
.TransferItem
;
73 import ch
.cyberduck
.core
.transfer
.UploadTransfer
;
74 import ch
.cyberduck
.core
.urlhandler
.SchemeHandlerFactory
;
75 import ch
.cyberduck
.ui
.browser
.Column
;
76 import ch
.cyberduck
.ui
.browser
.DownloadDirectoryFinder
;
77 import ch
.cyberduck
.ui
.cocoa
.delegate
.ArchiveMenuDelegate
;
78 import ch
.cyberduck
.ui
.cocoa
.delegate
.BookmarkMenuDelegate
;
79 import ch
.cyberduck
.ui
.cocoa
.delegate
.CopyURLMenuDelegate
;
80 import ch
.cyberduck
.ui
.cocoa
.delegate
.EditMenuDelegate
;
81 import ch
.cyberduck
.ui
.cocoa
.delegate
.OpenURLMenuDelegate
;
82 import ch
.cyberduck
.ui
.cocoa
.delegate
.URLMenuDelegate
;
84 import org
.apache
.commons
.lang3
.StringUtils
;
85 import org
.apache
.log4j
.Logger
;
86 import org
.rococoa
.Foundation
;
87 import org
.rococoa
.ID
;
88 import org
.rococoa
.Rococoa
;
89 import org
.rococoa
.cocoa
.foundation
.NSInteger
;
90 import org
.rococoa
.cocoa
.foundation
.NSRect
;
91 import org
.rococoa
.cocoa
.foundation
.NSUInteger
;
93 import java
.text
.MessageFormat
;
94 import java
.util
.ArrayList
;
95 import java
.util
.Arrays
;
96 import java
.util
.Calendar
;
97 import java
.util
.Collections
;
98 import java
.util
.Date
;
99 import java
.util
.EnumSet
;
100 import java
.util
.HashMap
;
101 import java
.util
.List
;
102 import java
.util
.Map
;
103 import java
.util
.concurrent
.CountDownLatch
;
106 * Setting the main menu and implements application delegate methods
110 public class MainController
extends BundleController
implements NSApplication
.Delegate
{
111 private static Logger log
= Logger
.getLogger(MainController
.class);
114 * Apple event constants<br>
115 * **********************************************************************************************<br>
116 * <i>native declaration : /Developer/SDKs/MacOSX10.5.sdk/usr/include/AvailabilityMacros.h:117</i>
118 public static final int kInternetEventClass
= 1196773964;
120 * Apple event constants<br>
121 * **********************************************************************************************<br>
122 * <i>native declaration : /Developer/SDKs/MacOSX10.5.sdk/usr/include/AvailabilityMacros.h:118</i>
124 public static final int kAEGetURL
= 1196773964;
126 * Apple event constants<br>
127 * **********************************************************************************************<br>
128 * <i>native declaration : /Developer/SDKs/MacOSX10.5.sdk/usr/include/AvailabilityMacros.h:119</i>
130 public static final int kAEFetchURL
= 1179996748;
133 public static final int keyAEResult
= 757935405;
135 private final Preferences preferences
= PreferencesFactory
.get();
137 public MainController() {
142 public void awakeFromNib() {
143 NSAppleEventManager
.sharedAppleEventManager().setEventHandler_andSelector_forEventClass_andEventID(
144 this.id(), Foundation
.selector("handleGetURLEvent:withReplyEvent:"), kInternetEventClass
, kAEGetURL
);
146 super.awakeFromNib();
149 private PathKindDetector detector
= new DefaultPathKindDetector();
152 * Extract the URL from the Apple event and handle it here.
154 public void handleGetURLEvent_withReplyEvent(NSAppleEventDescriptor event
, NSAppleEventDescriptor reply
) {
155 log
.debug("Received URL from Apple Event:" + event
);
156 final NSAppleEventDescriptor param
= event
.paramDescriptorForKeyword(keyAEResult
);
158 log
.error("No URL parameter");
161 final String url
= param
.stringValue();
162 if(StringUtils
.isEmpty(url
)) {
163 log
.error("URL parameter is empty");
166 if("x-cyberduck-action:update".equals(url
)) {
167 this.updateMenuClicked(null);
170 final Host h
= HostParser
.parse(url
);
171 if(Path
.Type
.file
== detector
.detect(h
.getDefaultPath())) {
172 final Path file
= new Path(h
.getDefaultPath(), EnumSet
.of(Path
.Type
.file
));
173 TransferControllerFactory
.get().start(new DownloadTransfer(h
, file
,
174 LocalFactory
.get(preferences
.getProperty("queue.download.folder"), file
.getName())));
177 for(BrowserController browser
: MainController
.getBrowsers()) {
178 if(browser
.isMounted()) {
179 if(new HostUrlProvider().get(browser
.getSession().getHost()).equals(
180 new HostUrlProvider().get(h
))) {
181 // Handle browser window already connected to the same host. #4215
182 browser
.window().makeKeyAndOrderFront(null);
187 final BrowserController browser
= newDocument(false);
193 private Updater updater
;
196 public void updateMenuClicked(ID sender
) {
197 if(null == updater
) {
198 updater
= Updater
.create();
200 updater
.checkForUpdates(null);
204 private NSMenu applicationMenu
;
206 public void setApplicationMenu(NSMenu menu
) {
207 this.applicationMenu
= menu
;
208 this.updateLicenseMenu();
209 this.updateUpdateMenu();
213 * Set name of key in menu item
215 private void updateLicenseMenu() {
216 final License key
= LicenseFactory
.find();
217 if(null == Updater
.getFeed() && key
.isReceipt()) {
218 this.applicationMenu
.removeItemAtIndex(new NSInteger(5));
219 this.applicationMenu
.removeItemAtIndex(new NSInteger(4));
222 NSDictionary KEY_FONT_ATTRIBUTES
= NSDictionary
.dictionaryWithObjectsForKeys(
223 NSArray
.arrayWithObjects(NSFont
.userFontOfSize(NSFont
.smallSystemFontSize()), NSColor
.darkGrayColor()),
224 NSArray
.arrayWithObjects(NSAttributedString
.FontAttributeName
, NSAttributedString
.ForegroundColorAttributeName
)
226 this.applicationMenu
.itemAtIndex(new NSInteger(5)).setAttributedTitle(
227 NSAttributedString
.attributedStringWithAttributes(key
.toString(), KEY_FONT_ATTRIBUTES
)
233 * Remove software update menu item if no update feed available
235 private void updateUpdateMenu() {
236 if(null == Updater
.getFeed()) {
237 this.applicationMenu
.removeItemAtIndex(new NSInteger(1));
242 private NSMenu encodingMenu
;
244 public void setEncodingMenu(NSMenu encodingMenu
) {
245 this.encodingMenu
= encodingMenu
;
246 for(String charset
: new DefaultCharsetProvider().availableCharsets()) {
247 this.encodingMenu
.addItemWithTitle_action_keyEquivalent(charset
, Foundation
.selector("encodingMenuClicked:"), StringUtils
.EMPTY
);
252 private NSMenu columnMenu
;
254 public void setColumnMenu(NSMenu columnMenu
) {
255 this.columnMenu
= columnMenu
;
256 Map
<String
, String
> columns
= new HashMap
<String
, String
>();
257 columns
.put(String
.format("browser.column.%s", Column
.kind
.name()), LocaleFactory
.localizedString("Kind"));
258 columns
.put(String
.format("browser.column.%s", Column
.extension
.name()), LocaleFactory
.localizedString("Extension"));
259 columns
.put(String
.format("browser.column.%s", Column
.size
.name()), LocaleFactory
.localizedString("Size"));
260 columns
.put(String
.format("browser.column.%s", Column
.modified
.name()), LocaleFactory
.localizedString("Modified"));
261 columns
.put(String
.format("browser.column.%s", Column
.owner
.name()), LocaleFactory
.localizedString("Owner"));
262 columns
.put(String
.format("browser.column.%s", Column
.group
.name()), LocaleFactory
.localizedString("Group"));
263 columns
.put(String
.format("browser.column.%s", Column
.permission
.name()), LocaleFactory
.localizedString("Permissions"));
264 columns
.put(String
.format("browser.column.%s", Column
.region
.name()), LocaleFactory
.localizedString("Region"));
265 columns
.put(String
.format("browser.column.%s", Column
.version
.name()), LocaleFactory
.localizedString("Version"));
266 for(Map
.Entry
<String
, String
> entry
: columns
.entrySet()) {
267 NSMenuItem item
= this.columnMenu
.addItemWithTitle_action_keyEquivalent(entry
.getValue(),
268 Foundation
.selector("columnMenuClicked:"), StringUtils
.EMPTY
);
269 final String identifier
= entry
.getKey();
270 item
.setState(preferences
.getBoolean(identifier
) ? NSCell
.NSOnState
: NSCell
.NSOffState
);
271 item
.setRepresentedObject(identifier
);
276 public void columnMenuClicked(final NSMenuItem sender
) {
277 final String identifier
= sender
.representedObject();
278 final boolean enabled
= !preferences
.getBoolean(identifier
);
279 sender
.setState(enabled ? NSCell
.NSOnState
: NSCell
.NSOffState
);
280 preferences
.setProperty(identifier
, enabled
);
281 BrowserController
.updateBrowserTableColumns();
285 private NSMenu editMenu
;
288 private EditMenuDelegate editMenuDelegate
;
290 public void setEditMenu(NSMenu editMenu
) {
291 this.editMenu
= editMenu
;
292 this.editMenuDelegate
= new EditMenuDelegate() {
294 protected Path
getEditable() {
295 final List
<BrowserController
> b
= MainController
.getBrowsers();
296 for(BrowserController controller
: b
) {
297 if(controller
.window().isKeyWindow()) {
298 final Path selected
= controller
.getSelectedPath();
299 if(null == selected
) {
302 if(controller
.isEditable(selected
)) {
312 protected ID
getTarget() {
313 return MainController
.getBrowser().id();
316 this.editMenu
.setDelegate(editMenuDelegate
.id());
320 private NSMenu urlMenu
;
323 private URLMenuDelegate urlMenuDelegate
;
325 public void setUrlMenu(NSMenu urlMenu
) {
326 this.urlMenu
= urlMenu
;
327 this.urlMenuDelegate
= new CopyURLMenuDelegate() {
329 protected Session
<?
> getSession() {
330 final List
<BrowserController
> b
= MainController
.getBrowsers();
331 for(BrowserController controller
: b
) {
332 if(controller
.window().isKeyWindow()) {
333 if(controller
.isMounted()) {
334 return controller
.getSession();
342 protected List
<Path
> getSelected() {
343 final List
<BrowserController
> b
= MainController
.getBrowsers();
344 for(BrowserController controller
: b
) {
345 if(controller
.window().isKeyWindow()) {
346 List
<Path
> selected
= controller
.getSelectedPaths();
347 if(selected
.isEmpty()) {
348 if(controller
.isMounted()) {
349 return Collections
.singletonList(controller
.workdir());
355 return Collections
.emptyList();
358 this.urlMenu
.setDelegate(urlMenuDelegate
.id());
362 private NSMenu openUrlMenu
;
365 private URLMenuDelegate openUrlMenuDelegate
;
367 public void setOpenUrlMenu(NSMenu openUrlMenu
) {
368 this.openUrlMenu
= openUrlMenu
;
369 this.openUrlMenuDelegate
= new OpenURLMenuDelegate() {
371 protected Session
<?
> getSession() {
372 final List
<BrowserController
> b
= MainController
.getBrowsers();
373 for(BrowserController controller
: b
) {
374 if(controller
.window().isKeyWindow()) {
375 if(controller
.isMounted()) {
376 return controller
.getSession();
384 protected List
<Path
> getSelected() {
385 final List
<BrowserController
> b
= MainController
.getBrowsers();
386 for(BrowserController controller
: b
) {
387 if(controller
.window().isKeyWindow()) {
388 List
<Path
> selected
= controller
.getSelectedPaths();
389 if(selected
.isEmpty()) {
390 if(controller
.isMounted()) {
391 return Collections
.singletonList(controller
.workdir());
397 return Collections
.emptyList();
400 this.openUrlMenu
.setDelegate(openUrlMenuDelegate
.id());
404 private NSMenu archiveMenu
;
407 private ArchiveMenuDelegate archiveMenuDelegate
;
409 public void setArchiveMenu(NSMenu archiveMenu
) {
410 this.archiveMenu
= archiveMenu
;
411 this.archiveMenuDelegate
= new ArchiveMenuDelegate();
412 this.archiveMenu
.setDelegate(archiveMenuDelegate
.id());
416 private NSMenu bookmarkMenu
;
419 private BookmarkMenuDelegate bookmarkMenuDelegate
;
421 public void setBookmarkMenu(NSMenu bookmarkMenu
) {
422 this.bookmarkMenu
= bookmarkMenu
;
423 this.bookmarkMenuDelegate
= new BookmarkMenuDelegate();
424 this.bookmarkMenu
.setDelegate(bookmarkMenuDelegate
.id());
428 public void bugreportMenuClicked(final ID sender
) {
429 BrowserLauncherFactory
.get().open(
430 MessageFormat
.format(preferences
.getProperty("website.bug"),
431 preferences
.getProperty("application.version")));
435 public void helpMenuClicked(final ID sender
) {
436 BrowserLauncherFactory
.get().open(preferences
.getProperty("website.help"));
440 public void licenseMenuClicked(final ID sender
) {
441 ApplicationLauncherFactory
.get().open(
442 LocalFactory
.get(NSBundle
.mainBundle().pathForResource_ofType("License", "txt")));
446 public void acknowledgmentsMenuClicked(final ID sender
) {
447 ApplicationLauncherFactory
.get().open(
448 LocalFactory
.get(NSBundle
.mainBundle().pathForResource_ofType("Acknowledgments", "rtf")));
452 public void websiteMenuClicked(final ID sender
) {
453 BrowserLauncherFactory
.get().open(preferences
.getProperty("website.home"));
457 public void forumMenuClicked(final ID sender
) {
458 BrowserLauncherFactory
.get().open(preferences
.getProperty("website.forum"));
462 public void donateMenuClicked(final ID sender
) {
463 BrowserLauncherFactory
.get().open(preferences
.getProperty("website.donate"));
467 public void aboutMenuClicked(final ID sender
) {
468 final NSDictionary dict
= NSDictionary
.dictionaryWithObjectsForKeys(
469 NSArray
.arrayWithObjects(
470 preferences
.getProperty("application.name"),
471 preferences
.getProperty("application.version"),
472 preferences
.getProperty("application.revision")),
473 NSArray
.arrayWithObjects(
475 "ApplicationVersion",
478 NSApplication
.sharedApplication().orderFrontStandardAboutPanelWithOptions(dict
);
482 public void feedbackMenuClicked(final ID sender
) {
483 BrowserLauncherFactory
.get().open(preferences
.getProperty("mail.feedback")
484 + "?subject=" + preferences
.getProperty("application.name") + "-" + preferences
.getProperty("application.version"));
488 public void preferencesMenuClicked(final ID sender
) {
489 PreferencesController controller
= PreferencesControllerFactory
.instance();
490 controller
.window().makeKeyAndOrderFront(null);
494 public void newDownloadMenuClicked(final ID sender
) {
495 this.showTransferQueueClicked(sender
);
496 SheetController c
= new DownloadController(TransferControllerFactory
.get());
501 public void newBrowserMenuClicked(final ID sender
) {
502 this.openDefaultBookmark(MainController
.newDocument(true));
506 public void showTransferQueueClicked(final ID sender
) {
507 TransferController c
= TransferControllerFactory
.get();
508 c
.window().makeKeyAndOrderFront(null);
512 public void showActivityWindowClicked(final ID sender
) {
513 ActivityController c
= ActivityControllerFactory
.get();
518 c
.window().orderFront(null);
523 public boolean application_openFile(final NSApplication app
, final String filename
) {
524 if(log
.isDebugEnabled()) {
525 log
.debug(String
.format("Open file %s", filename
));
527 final Local f
= LocalFactory
.get(filename
);
529 if("duck".equals(f
.getExtension())) {
532 bookmark
= HostReaderFactory
.get().read(f
);
533 if(null == bookmark
) {
536 MainController
.newDocument().mount(bookmark
);
539 catch(AccessDeniedException e
) {
540 log
.error(e
.getMessage());
544 else if("cyberducklicense".equals(f
.getExtension())) {
545 final License l
= LicenseFactory
.get(f
);
548 f
.copy(LocalFactory
.get(preferences
.getProperty("application.support.path"), f
.getName()));
549 final NSAlert alert
= NSAlert
.alert(
551 LocaleFactory
.localizedString("Thanks for your support! Your contribution helps to further advance development to make Cyberduck even better.", "License")
553 + LocaleFactory
.localizedString("Your donation key has been copied to the Application Support folder.", "License"),
554 LocaleFactory
.localizedString("Continue", "License"), //default
558 alert
.setAlertStyle(NSAlert
.NSInformationalAlertStyle
);
559 if(this.alert(alert
) == SheetCallback
.DEFAULT_OPTION
) {
560 for(BrowserController c
: MainController
.getBrowsers()) {
561 c
.removeDonateWindowTitle();
563 this.updateLicenseMenu();
566 catch(AccessDeniedException e
) {
567 log
.error(e
.getMessage());
572 final NSAlert alert
= NSAlert
.alert(
573 LocaleFactory
.localizedString("Not a valid donation key", "License"),
574 LocaleFactory
.localizedString("This donation key does not appear to be valid.", "License"),
575 LocaleFactory
.localizedString("Continue", "License"), //default
578 alert
.setAlertStyle(NSAlert
.NSWarningAlertStyle
);
579 alert
.setShowsHelp(true);
580 alert
.setDelegate(new ProxyController() {
582 public boolean alertShowHelp(NSAlert alert
) {
583 StringBuilder site
= new StringBuilder(preferences
.getProperty("website.help"));
584 site
.append("/").append("faq");
585 BrowserLauncherFactory
.get().open(site
.toString());
594 else if("cyberduckprofile".equals(f
.getExtension())) {
596 final Protocol profile
= ProfileReaderFactory
.get().read(f
);
597 if(null == profile
) {
600 if(profile
.isEnabled()) {
601 if(log
.isDebugEnabled()) {
602 log
.debug(String
.format("Register profile %s", profile
));
604 ProtocolFactory
.register(profile
);
605 final Host host
= new Host(profile
, profile
.getDefaultHostname(), profile
.getDefaultPort());
606 MainController
.newDocument().addBookmark(host
);
607 // Register in application support
608 final Local profiles
= LocalFactory
.get(preferences
.getProperty("application.support.path"),
609 PreferencesFactory
.get().getProperty("profiles.folder.name"));
611 f
.copy(LocalFactory
.get(profiles
, f
.getName()));
614 catch(AccessDeniedException e
) {
615 log
.error(e
.getMessage());
621 this.background(new AbstractBackgroundAction
<Void
>() {
623 public Void
run() throws BackgroundException
{
624 // Wait until bookmarks are loaded
626 bookmarksSemaphore
.await();
628 catch(InterruptedException e
) {
629 log
.error(String
.format("Error awaiting bookmarks to load %s", e
.getMessage()));
635 public void cleanup() {
640 public String
getActivity() {
650 private boolean upload(final Local f
) {
651 return this.upload(Collections
.singletonList(f
));
654 private boolean upload(final List
<Local
> files
) {
658 for(BrowserController controller
: MainController
.getBrowsers()) {
659 if(controller
.isMounted()) {
660 open
= controller
.getSession().getHost();
661 workdir
= controller
.workdir();
662 if(1 == MainController
.getBrowsers().size()) {
663 // If only one browser window upload to current working directory with no bookmark selection
664 this.upload(open
, files
, workdir
);
670 if(BookmarkCollection
.defaultCollection().isEmpty()) {
671 log
.warn("No bookmark for upload");
674 final NSPopUpButton bookmarksPopup
= NSPopUpButton
.buttonWithFrame(new NSRect(0, 26));
675 bookmarksPopup
.setToolTip(LocaleFactory
.localizedString("Bookmarks"));
676 for(Host b
: BookmarkCollection
.defaultCollection()) {
677 String title
= BookmarkNameProvider
.toString(b
);
679 while(bookmarksPopup
.itemWithTitle(title
) != null) {
680 title
= BookmarkNameProvider
.toString(b
) + "-" + i
;
683 bookmarksPopup
.addItemWithTitle(title
);
684 bookmarksPopup
.lastItem().setImage(IconCacheFactory
.<NSImage
>get().iconNamed(b
.getProtocol().icon(), 16));
685 bookmarksPopup
.lastItem().setRepresentedObject(b
.getUuid());
687 bookmarksPopup
.selectItemAtIndex(bookmarksPopup
.indexOfItem(bookmarksPopup
.lastItem()));
692 for(Host bookmark
: BookmarkCollection
.defaultCollection()) {
693 boolean found
= false;
694 // Pick the bookmark with the same download location
695 for(Local file
: files
) {
696 if(file
.isChild(new DownloadDirectoryFinder().find(bookmark
))) {
697 bookmarksPopup
.selectItemAtIndex(new NSInteger(i
));
708 if(-1 == bookmarksPopup
.indexOfSelectedItem().intValue()) {
709 // No bookmark for current browser found
710 bookmarksPopup
.selectItemAtIndex(new NSInteger(0));
712 final TransferController t
= TransferControllerFactory
.get();
713 final Host mount
= open
;
714 final Path destination
= workdir
;
715 AlertController alert
= new AlertController(t
, NSAlert
.alert("Select Bookmark",
716 MessageFormat
.format("Upload {0} to the selected bookmark.",
717 files
.size() == 1 ? files
.iterator().next().getName()
718 : MessageFormat
.format(LocaleFactory
.localizedString("{0} Files"), String
.valueOf(files
.size()))
720 LocaleFactory
.localizedString("Upload", "Transfer"),
721 LocaleFactory
.localizedString("Cancel"),
725 public void callback(int returncode
) {
726 if(DEFAULT_OPTION
== returncode
) {
727 final String selected
= bookmarksPopup
.selectedItem().representedObject();
728 for(Host bookmark
: BookmarkCollection
.defaultCollection()) {
729 // Determine selected bookmark
730 if(bookmark
.getUuid().equals(selected
)) {
731 if(bookmark
.equals(mount
)) {
732 // Use current working directory of browser for destination
733 upload(bookmark
, files
, destination
);
736 // No mounted browser
737 if(StringUtils
.isNotBlank(bookmark
.getDefaultPath())) {
738 upload(bookmark
, files
, new Path(bookmark
.getDefaultPath(), EnumSet
.of(Path
.Type
.directory
)));
741 upload(bookmark
, files
, destination
);
751 protected boolean validateInput() {
752 return StringUtils
.isNotEmpty(bookmarksPopup
.selectedItem().representedObject());
755 alert
.setAccessoryView(bookmarksPopup
);
760 private void upload(final Host bookmark
, final List
<Local
> files
, final Path destination
) {
761 final List
<TransferItem
> roots
= new ArrayList
<TransferItem
>();
762 for(Local file
: files
) {
763 roots
.add(new TransferItem(new Path(destination
, file
.getName(),
764 file
.isDirectory() ? EnumSet
.of(Path
.Type
.directory
) : EnumSet
.of(Path
.Type
.file
)), file
));
766 final TransferController t
= TransferControllerFactory
.get();
767 t
.start(new UploadTransfer(bookmark
, roots
));
771 * Sent directly by theApplication to the delegate. The method should attempt to open the file filename,
772 * returning true if the file is successfully opened, and false otherwise. By design, a
773 * file opened through this method is assumed to be temporary its the application's
774 * responsibility to remove the file at the appropriate time.
777 public boolean application_openTempFile(NSApplication app
, String filename
) {
778 if(log
.isDebugEnabled()) {
779 log
.debug("applicationOpenTempFile:" + filename
);
781 return this.application_openFile(app
, filename
);
785 * Invoked immediately before opening an untitled file. Return false to prevent
786 * the application from opening an untitled file; return true otherwise.
787 * Note that applicationOpenUntitledFile is invoked if this method returns true.
790 public boolean applicationShouldOpenUntitledFile(NSApplication sender
) {
791 if(log
.isDebugEnabled()) {
792 log
.debug("applicationShouldOpenUntitledFile");
794 return preferences
.getBoolean("browser.open.untitled");
798 * @return true if the file was successfully opened, false otherwise.
801 public boolean applicationOpenUntitledFile(NSApplication app
) {
802 if(log
.isDebugEnabled()) {
803 log
.debug("applicationOpenUntitledFile");
809 * Mounts the default bookmark if any
811 private void openDefaultBookmark(BrowserController controller
) {
812 String defaultBookmark
= preferences
.getProperty("browser.open.bookmark.default");
813 if(null == defaultBookmark
) {
814 log
.info("No default bookmark configured");
815 return; //No default bookmark given
817 Host bookmark
= BookmarkCollection
.defaultCollection().lookup(defaultBookmark
);
818 if(null == bookmark
) {
819 log
.info("Default bookmark no more available");
822 for(BrowserController browser
: getBrowsers()) {
823 if(browser
.getSession() != null) {
824 if(browser
.getSession().getHost().equals(bookmark
)) {
825 log
.debug("Default bookmark already mounted");
830 if(log
.isDebugEnabled()) {
831 log
.debug(String
.format("Mounting default bookmark %s", bookmark
));
833 controller
.mount(bookmark
);
837 * These events are sent whenever the Finder reactivates an already running application
838 * because someone double-clicked it again or used the dock to activate it. By default
839 * the Application Kit will handle this event by checking whether there are any visible
840 * NSWindows (not NSPanels), and, if there are none, it goes through the standard untitled
841 * document creation (the same as it does if theApplication is launched without any document
842 * to open). For most document-based applications, an untitled document will be created.
843 * The application delegate will also get a chance to respond to the normal untitled document
844 * delegations. If you implement this method in your application delegate, it will be called
845 * before any of the default behavior happens. If you return true, then NSApplication will
846 * go on to do its normal thing. If you return false, then NSApplication will do nothing.
847 * So, you can either implement this method, do nothing, and return false if you do not
848 * want anything to happen at all (not recommended), or you can implement this method,
849 * handle the event yourself in some custom way, and return false.
852 public boolean applicationShouldHandleReopen_hasVisibleWindows(final NSApplication app
, final boolean visibleWindowsFound
) {
853 if(log
.isDebugEnabled()) {
854 log
.debug(String
.format("Should handle reopen with windows %s", visibleWindowsFound
));
855 } // While an application is open, the Dock icon has a symbol below it.
856 // When a user clicks an open application’s icon in the Dock, the application
857 // becomes active and all open unminimized windows are brought to the front;
858 // minimized document windows remain in the Dock. If there are no unminimized
859 // windows when the user clicks the Dock icon, the last minimized window should
860 // be expanded and made active. If no documents are open, the application should
861 // open a new window. (If your application is not document-based, display the
862 // application’s main window.)
863 if(MainController
.getBrowsers().isEmpty() && !TransferControllerFactory
.get().isVisible()) {
864 this.openDefaultBookmark(MainController
.newDocument());
866 NSWindow miniaturized
= null;
867 for(BrowserController controller
: MainController
.getBrowsers()) {
868 if(!controller
.window().isMiniaturized()) {
871 if(null == miniaturized
) {
872 miniaturized
= controller
.window();
875 if(null == miniaturized
) {
878 miniaturized
.deminiaturize(null);
882 // User bookmarks and thirdparty applications
883 private final CountDownLatch bookmarksSemaphore
= new CountDownLatch(1);
884 private final CountDownLatch thirdpartySemaphore
= new CountDownLatch(1);
887 * Sent by the default notification center after the application has been launched and initialized but
888 * before it has received its first event. aNotification is always an
889 * ApplicationDidFinishLaunchingNotification. You can retrieve the NSApplication
890 * object in question by sending object to aNotification. The delegate can implement
891 * this method to perform further initialization. If the user started up the application
892 * by double-clicking a file, the delegate receives the applicationOpenFile message before receiving
893 * applicationDidFinishLaunching. (applicationWillFinishLaunching is sent before applicationOpenFile.)
896 public void applicationDidFinishLaunching(NSNotification notification
) {
897 if(preferences
.getBoolean("browser.open.untitled")) {
898 MainController
.newDocument();
900 if(preferences
.getBoolean("queue.window.open.default")) {
901 this.showTransferQueueClicked(null);
903 if(preferences
.getBoolean("browser.serialize")) {
904 this.background(new AbstractBackgroundAction
<Void
>() {
906 public Void
run() throws BackgroundException
{
912 public void cleanup() {
913 for(Host host
: sessions
) {
914 if(log
.isInfoEnabled()) {
915 log
.info(String
.format("New browser for saved session %s", host
));
917 final BrowserController browser
= MainController
.newDocument(false, host
.getUuid());
924 // Load all bookmarks in background
925 this.background(new AbstractBackgroundAction
<Void
>() {
927 public Void
run() throws BackgroundException
{
928 final BookmarkCollection c
= BookmarkCollection
.defaultCollection();
930 bookmarksSemaphore
.countDown();
935 public void cleanup() {
936 if(preferences
.getBoolean("browser.open.untitled")) {
937 if(preferences
.getProperty("browser.open.bookmark.default") != null) {
938 openDefaultBookmark(MainController
.newDocument());
941 // Set delegate for NSService
942 NSApplication
.sharedApplication().setServicesProvider(MainController
.this.id());
946 public String
getActivity() {
947 return "Loading Bookmarks";
950 this.background(new AbstractBackgroundAction
<Void
>() {
952 public Void
run() throws BackgroundException
{
953 HistoryCollection
.defaultCollection().load();
958 public String
getActivity() {
959 return "Loading History";
962 this.background(new AbstractBackgroundAction
<Void
>() {
964 public Void
run() throws BackgroundException
{
965 TransferCollection
.defaultCollection().load();
970 public String
getActivity() {
971 return "Loading Transfers";
974 this.background(new AbstractBackgroundAction
<Void
>() {
976 public Void
run() throws BackgroundException
{
977 // Make sure we register to Growl first
978 NotificationServiceFactory
.get().setup();
983 public String
getActivity() {
984 return "Registering Growl";
987 final Rendezvous bonjour
= RendezvousFactory
.instance();
988 bonjour
.addListener(new NotificationRendezvousListener(bonjour
));
989 if(preferences
.getBoolean("defaulthandler.reminder")
990 && preferences
.getInteger("uses") > 0) {
991 if(!SchemeHandlerFactory
.get().isDefaultHandler(
992 Arrays
.asList(Scheme
.ftp
, Scheme
.ftps
, Scheme
.sftp
),
993 new Application(NSBundle
.mainBundle().infoDictionary().objectForKey("CFBundleIdentifier").toString()))) {
994 final NSAlert alert
= NSAlert
.alert(
995 LocaleFactory
.localizedString("Set Cyberduck as default application for FTP and SFTP locations?", "Configuration"),
996 LocaleFactory
.localizedString("As the default application, Cyberduck will open when you click on FTP or SFTP links " +
997 "in other applications, such as your web browser. You can change this setting in the Preferences later.", "Configuration"),
998 LocaleFactory
.localizedString("Change", "Configuration"), //default
1000 LocaleFactory
.localizedString("Cancel", "Configuration")
1002 alert
.setAlertStyle(NSAlert
.NSInformationalAlertStyle
);
1003 alert
.setShowsSuppressionButton(true);
1004 alert
.suppressionButton().setTitle(LocaleFactory
.localizedString("Don't ask again", "Configuration"));
1005 int choice
= alert
.runModal(); //alternate
1006 if(alert
.suppressionButton().state() == NSCell
.NSOnState
) {
1007 // Never show again.
1008 preferences
.setProperty("defaulthandler.reminder", false);
1010 if(choice
== SheetCallback
.DEFAULT_OPTION
) {
1011 SchemeHandlerFactory
.get().setDefaultHandler(
1012 Arrays
.asList(Scheme
.ftp
, Scheme
.ftps
, Scheme
.sftp
),
1013 new Application(NSBundle
.mainBundle().infoDictionary().objectForKey("CFBundleIdentifier").toString())
1018 // NSWorkspace notifications are posted to a notification center provided by
1019 // the NSWorkspace object, instead of going through the application’s default
1020 // notification center as most notifications do. To receive NSWorkspace notifications,
1021 // your application must register an observer with the NSWorkspace notification center.
1022 NSWorkspace
.sharedWorkspace().notificationCenter().addObserver(this.id(),
1023 Foundation
.selector("workspaceWillPowerOff:"),
1024 NSWorkspace
.WorkspaceWillPowerOffNotification
,
1026 NSWorkspace
.sharedWorkspace().notificationCenter().addObserver(this.id(),
1027 Foundation
.selector("workspaceWillLogout:"),
1028 NSWorkspace
.WorkspaceSessionDidResignActiveNotification
,
1030 NSWorkspace
.sharedWorkspace().notificationCenter().addObserver(this.id(),
1031 Foundation
.selector("workspaceWillSleep:"),
1032 NSWorkspace
.WorkspaceWillSleepNotification
,
1034 NSNotificationCenter
.defaultCenter().addObserver(this.id(),
1035 Foundation
.selector("applicationWillRestartAfterUpdate:"),
1036 "SUUpdaterWillRestartNotificationName",
1038 this.background(new AbstractBackgroundAction
<Void
>() {
1040 public Void
run() throws BackgroundException
{
1045 // Import thirdparty bookmarks.
1046 this.background(new AbstractBackgroundAction
<Void
>() {
1047 private List
<ThirdpartyBookmarkCollection
> thirdpartyBookmarkCollections
= Collections
.emptyList();
1051 thirdpartyBookmarkCollections
= this.getThirdpartyBookmarks();
1052 for(ThirdpartyBookmarkCollection t
: thirdpartyBookmarkCollections
) {
1053 if(!t
.isInstalled()) {
1054 if(log
.isInfoEnabled()) {
1055 log
.info(String
.format("No application installed for %s", t
.getBundleIdentifier()));
1062 catch(AccessDeniedException e
) {
1063 log
.warn(String
.format("Failure %s loading bookmarks from %s", e
, t
));
1067 preferences
.setProperty(t
.getConfiguration(), true);
1071 bookmarksSemaphore
.await();
1073 catch(InterruptedException e
) {
1074 log
.error(String
.format("Error awaiting bookmarks to load %s", e
.getMessage()));
1080 public void cleanup() {
1081 for(ThirdpartyBookmarkCollection t
: thirdpartyBookmarkCollections
) {
1082 final BookmarkCollection bookmarks
= BookmarkCollection
.defaultCollection();
1083 t
.filter(bookmarks
);
1085 preferences
.setProperty(t
.getConfiguration(), true);
1088 final NSAlert alert
= NSAlert
.alert(
1089 MessageFormat
.format(LocaleFactory
.localizedString("Import {0} Bookmarks", "Configuration"), t
.getName()),
1090 MessageFormat
.format(LocaleFactory
.localizedString("{0} bookmarks found. Do you want to add these to your bookmarks?", "Configuration"), t
.size()),
1091 LocaleFactory
.localizedString("Import", "Configuration"), //default
1093 LocaleFactory
.localizedString("Cancel", "Configuration"));
1094 alert
.setShowsSuppressionButton(true);
1095 alert
.suppressionButton().setTitle(LocaleFactory
.localizedString("Don't ask again", "Configuration"));
1096 alert
.setAlertStyle(NSAlert
.NSInformationalAlertStyle
);
1097 int choice
= alert
.runModal(); //alternate
1098 if(alert
.suppressionButton().state() == NSCell
.NSOnState
) {
1099 // Never show again.
1100 preferences
.setProperty(t
.getConfiguration(), true);
1102 if(choice
== SheetCallback
.DEFAULT_OPTION
) {
1103 bookmarks
.addAll(t
);
1105 preferences
.setProperty(t
.getConfiguration(), true);
1108 thirdpartySemaphore
.countDown();
1112 public String
getActivity() {
1113 return "Loading thirdparty bookmarks";
1116 private List
<ThirdpartyBookmarkCollection
> getThirdpartyBookmarks() {
1117 return Arrays
.asList(new TransmitBookmarkCollection(), new FilezillaBookmarkCollection(), new FetchBookmarkCollection(),
1118 new FlowBookmarkCollection(), new InterarchyBookmarkCollection(), new CrossFtpBookmarkCollection(), new FireFtpBookmarkCollection());
1124 * NSService implementation
1126 public void serviceUploadFileUrl_(final NSPasteboard pboard
, final String userData
) {
1127 if(log
.isDebugEnabled()) {
1128 log
.debug(String
.format("serviceUploadFileUrl_: with user data %s", userData
));
1130 if(pboard
.availableTypeFromArray(NSArray
.arrayWithObject(NSPasteboard
.FilenamesPboardType
)) != null) {
1131 NSObject o
= pboard
.propertyListForType(NSPasteboard
.FilenamesPboardType
);
1133 if(o
.isKindOfClass(Rococoa
.createClass("NSArray", NSArray
._Class
.class))) {
1134 final NSArray elements
= Rococoa
.cast(o
, NSArray
.class);
1135 List
<Local
> files
= new ArrayList
<Local
>();
1136 for(int i
= 0; i
< elements
.count().intValue(); i
++) {
1137 files
.add(LocalFactory
.get(elements
.objectAtIndex(new NSUInteger(i
)).toString()));
1148 private AbstractHostCollection sessions
= new FolderBookmarkCollection(
1149 LocalFactory
.get(preferences
.getProperty("application.support.path"), "Sessions"), "session");
1152 * Display donation reminder dialog
1154 private boolean displayDonationPrompt
= true;
1157 private WindowController donationController
;
1161 * Invoked from within the terminate method immediately before the
1162 * application terminates. sender is the NSApplication to be terminated.
1163 * If this method returns false, the application is not terminated,
1164 * and control returns to the main event loop.
1166 * @param app Application instance
1167 * @return Return true to allow the application to terminate.
1170 public NSUInteger
applicationShouldTerminate(final NSApplication app
) {
1171 if(log
.isDebugEnabled()) {
1172 log
.debug("Application should quit with notification");
1174 // Determine if there are any running transfers
1175 final NSUInteger result
= TransferControllerFactory
.applicationShouldTerminate(app
);
1176 if(!result
.equals(NSApplication
.NSTerminateNow
)) {
1179 // Determine if there are any open connections
1180 for(BrowserController browser
: MainController
.getBrowsers()) {
1181 if(preferences
.getBoolean("browser.serialize")) {
1182 if(browser
.isMounted()) {
1183 // The workspace should be saved. Serialize all open browser sessions
1184 final Host serialized
1185 = new HostDictionary().deserialize(browser
.getSession().getHost().serialize(SerializerFactory
.get()));
1186 serialized
.setWorkdir(browser
.workdir());
1187 sessions
.add(serialized
);
1188 browser
.window().saveFrameUsingName(serialized
.getUuid());
1191 if(browser
.isConnected()) {
1192 if(preferences
.getBoolean("browser.disconnect.confirm")) {
1193 final NSAlert alert
= NSAlert
.alert(LocaleFactory
.localizedString("Quit"),
1194 LocaleFactory
.localizedString("You are connected to at least one remote site. Do you want to review open browsers?"),
1195 LocaleFactory
.localizedString("Quit Anyway"), //default
1196 LocaleFactory
.localizedString("Cancel"), //other
1197 LocaleFactory
.localizedString("Review…"));
1198 alert
.setAlertStyle(NSAlert
.NSWarningAlertStyle
);
1199 alert
.setShowsSuppressionButton(true);
1200 alert
.suppressionButton().setTitle(LocaleFactory
.localizedString("Don't ask again", "Configuration"));
1201 int choice
= alert
.runModal(); //alternate
1202 if(alert
.suppressionButton().state() == NSCell
.NSOnState
) {
1203 // Never show again.
1204 preferences
.setProperty("browser.disconnect.confirm", false);
1206 if(choice
== SheetCallback
.ALTERNATE_OPTION
) {
1207 // Cancel. Quit has been interrupted. Delete any saved sessions so far.
1209 return NSApplication
.NSTerminateCancel
;
1211 if(choice
== SheetCallback
.CANCEL_OPTION
) {
1212 // Review if at least one window requested to terminate later, we shall wait.
1213 // This will iterate over all mounted browsers.
1214 if(NSApplication
.NSTerminateNow
.equals(BrowserController
.applicationShouldTerminate(app
))) {
1215 return this.applicationShouldTerminateAfterDonationPrompt(app
);
1217 return NSApplication
.NSTerminateLater
;
1219 if(choice
== SheetCallback
.DEFAULT_OPTION
) {
1221 return this.applicationShouldTerminateAfterDonationPrompt(app
);
1225 browser
.windowShouldClose(browser
.window());
1229 return this.applicationShouldTerminateAfterDonationPrompt(app
);
1232 public NSUInteger
applicationShouldTerminateAfterDonationPrompt(final NSApplication app
) {
1233 if(log
.isDebugEnabled()) {
1234 log
.debug("applicationShouldTerminateAfterDonationPrompt");
1236 if(!displayDonationPrompt
) {
1237 // Already displayed
1238 return NSApplication
.NSTerminateNow
;
1240 final License l
= LicenseFactory
.find();
1242 final String lastversion
= preferences
.getProperty("donate.reminder");
1243 if(NSBundle
.mainBundle().infoDictionary().objectForKey("CFBundleShortVersionString").toString().equals(lastversion
)) {
1244 // Do not display if same version is installed
1245 return NSApplication
.NSTerminateNow
;
1247 final Calendar nextreminder
= Calendar
.getInstance();
1248 nextreminder
.setTimeInMillis(preferences
.getLong("donate.reminder.date"));
1249 // Display donationPrompt every n days
1250 nextreminder
.add(Calendar
.DAY_OF_YEAR
, preferences
.getInteger("y"));
1251 if(log
.isDebugEnabled()) {
1252 log
.debug(String
.format("Next reminder %s", nextreminder
.getTime().toString()));
1254 // Display after upgrade
1255 if(nextreminder
.getTime().after(new Date(System
.currentTimeMillis()))) {
1256 // Do not display if shown in the reminder interval
1257 return NSApplication
.NSTerminateNow
;
1259 // Make sure prompt is not loaded twice upon next quit event
1260 displayDonationPrompt
= false;
1261 final int uses
= preferences
.getInteger("uses");
1262 donationController
= new WindowController() {
1264 protected String
getBundleName() {
1269 private NSButton neverShowDonationCheckbox
;
1271 public void setNeverShowDonationCheckbox(NSButton neverShowDonationCheckbox
) {
1272 this.neverShowDonationCheckbox
= neverShowDonationCheckbox
;
1273 this.neverShowDonationCheckbox
.setTarget(this.id());
1274 this.neverShowDonationCheckbox
.setState(
1275 preferences
.getProperty("donate.reminder").equals(
1276 NSBundle
.mainBundle().infoDictionary().objectForKey("CFBundleShortVersionString").toString())
1277 ? NSCell
.NSOnState
: NSCell
.NSOffState
1282 public void awakeFromNib() {
1283 this.window().setTitle(this.window().title() + " (" + uses
+ ")");
1284 this.window().center();
1285 this.window().makeKeyAndOrderFront(null);
1287 super.awakeFromNib();
1290 public void closeDonationSheet(final NSButton sender
) {
1291 if(sender
.tag() == SheetCallback
.DEFAULT_OPTION
) {
1292 BrowserLauncherFactory
.get().open(preferences
.getProperty("website.donate"));
1298 public void windowWillClose(NSNotification notification
) {
1300 super.windowWillClose(notification
);
1303 private void terminate() {
1304 if(neverShowDonationCheckbox
.state() == NSCell
.NSOnState
) {
1305 preferences
.setProperty("donate.reminder",
1306 NSBundle
.mainBundle().infoDictionary().objectForKey("CFBundleShortVersionString").toString());
1308 // Remember this reminder date
1309 preferences
.setProperty("donate.reminder.date", System
.currentTimeMillis());
1311 app
.replyToApplicationShouldTerminate(true);
1314 donationController
.loadBundle();
1315 // Delay application termination. Dismissing the donation dialog will reply to quit.
1316 return NSApplication
.NSTerminateLater
;
1318 return NSApplication
.NSTerminateNow
;
1322 * Quits the Rendezvous daemon and saves all preferences
1324 * @param notification Notification name
1327 public void applicationWillTerminate(NSNotification notification
) {
1328 if(log
.isDebugEnabled()) {
1329 log
.debug(String
.format("Application will quit with notification %s", notification
));
1333 //Terminating rendezvous discovery
1334 RendezvousFactory
.instance().quit();
1336 //Writing usage info
1337 preferences
.setProperty("uses", preferences
.getInteger("uses") + 1);
1341 public void applicationWillRestartAfterUpdate(ID updater
) {
1342 // Disable donation prompt after udpate install
1343 displayDonationPrompt
= false;
1347 * Posted when the user has requested a logout or that the machine be powered off.
1349 * @param notification Notification name
1351 public void workspaceWillPowerOff(NSNotification notification
) {
1352 if(log
.isDebugEnabled()) {
1353 log
.debug(String
.format("Workspace will power off with notification %s", notification
));
1358 * Posted before a user session is switched out. This allows an application to
1359 * disable some processing when its user session is switched out, and reenable when that
1360 * session gets switched back in, for example.
1362 * @param notification Notification name
1364 public void workspaceWillLogout(NSNotification notification
) {
1365 if(log
.isDebugEnabled()) {
1366 log
.debug(String
.format("Workspace will logout with notification %s", notification
));
1370 public void workspaceWillSleep(NSNotification notification
) {
1371 if(log
.isDebugEnabled()) {
1372 log
.debug(String
.format("Workspace will sleep with notification %s", notification
));
1377 * Makes a unmounted browser window the key window and brings it to the front
1379 * @return A reference to a browser window
1381 public static BrowserController
newDocument() {
1382 return MainController
.newDocument(false);
1388 private static List
<BrowserController
> browsers
1389 = new ArrayList
<BrowserController
>();
1391 public static List
<BrowserController
> getBrowsers() {
1396 * Browser with key focus
1398 * @return Null if no browser window is open
1400 public static BrowserController
getBrowser() {
1401 for(BrowserController browser
: MainController
.getBrowsers()) {
1402 if(browser
.window().isKeyWindow()) {
1411 * Makes a unmounted browser window the key window and brings it to the front
1413 * @param force If true, open a new browser regardless of any unused browser window
1415 public static BrowserController
newDocument(final boolean force
) {
1416 return newDocument(force
, null);
1420 * @param frame Frame autosave name
1422 public static BrowserController
newDocument(final boolean force
, final String frame
) {
1423 final List
<BrowserController
> browsers
= MainController
.getBrowsers();
1425 for(BrowserController controller
: browsers
) {
1426 if(controller
.getSession() == null) {
1427 controller
.window().makeKeyAndOrderFront(null);
1432 final BrowserController controller
= new BrowserController();
1433 controller
.addListener(new WindowListener() {
1435 public void windowWillClose() {
1436 browsers
.remove(controller
);
1439 if(StringUtils
.isNotBlank(frame
)) {
1440 controller
.window().setFrameUsingName(frame
);
1442 controller
.window().makeKeyAndOrderFront(null);
1443 browsers
.add(controller
);
1448 * We are not a Windows application. Long live the application wide menu bar.
1451 public boolean applicationShouldTerminateAfterLastWindowClosed(NSApplication app
) {
1456 protected String
getBundleName() {