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
.application
.AppKitFunctionsLibrary
;
22 import ch
.cyberduck
.binding
.application
.NSApplication
;
23 import ch
.cyberduck
.binding
.application
.NSButton
;
24 import ch
.cyberduck
.binding
.application
.NSWindow
;
25 import ch
.cyberduck
.binding
.application
.SheetCallback
;
26 import ch
.cyberduck
.binding
.foundation
.NSThread
;
27 import ch
.cyberduck
.core
.threading
.ControllerMainAction
;
29 import org
.apache
.log4j
.Logger
;
30 import org
.rococoa
.Foundation
;
31 import org
.rococoa
.ID
;
33 import java
.util
.HashSet
;
35 import java
.util
.concurrent
.CountDownLatch
;
40 public abstract class SheetController
extends WindowController
implements SheetCallback
{
41 private static Logger log
= Logger
.getLogger(SheetController
.class);
44 * Keep a reference to the sheet to protect it from being
45 * deallocated as a weak reference before the callback from the runtime
47 protected static final Set
<SheetController
> sheetRegistry
48 = new HashSet
<SheetController
>();
51 * The controller of the parent window
53 protected final WindowController parent
;
56 * Dismiss button clicked
58 private int returncode
;
60 private CountDownLatch signal
61 = new CountDownLatch(1);
64 * The sheet window must be provided later with #setWindow (usually called when loading the NIB file)
66 * @param parent The controller of the parent window
68 public SheetController(final WindowController parent
) {
70 sheetRegistry
.add(this);
74 * @return Null by default, a sheet with no custom NIB
77 protected String
getBundleName() {
82 * This must be the target action for any button in the sheet dialog. Will validate the input
83 * and close the sheet; #sheetDidClose will be called afterwards
85 * @param sender A button in the sheet dialog
88 public void closeSheet(final NSButton sender
) {
89 if(log
.isDebugEnabled()) {
90 log
.debug(String
.format("Close sheet with button %s", sender
.title()));
92 final int option
= new PanelReturnCodeMapper().getOption(sender
);
93 if(option
== DEFAULT_OPTION
|| option
== ALTERNATE_OPTION
) {
94 if(!this.validateInput()) {
95 AppKitFunctionsLibrary
.beep();
99 NSApplication
.sharedApplication().endSheet(this.window(), option
);
103 * @return The tag of the button this sheet was dismissed with
105 public int returnCode() {
110 * Check input fields for any errors
112 * @return true if a valid input has been given
114 protected boolean validateInput() {
118 public void beginSheet() {
119 synchronized(parent
.window()) {
120 if(NSThread
.isMainThread()) {
122 // No need to call invoke on main thread
123 this.beginSheet(this.window());
126 final SheetController controller
= this;
127 invoke(new ControllerMainAction(this) {
130 controller
.loadBundle();
131 //Invoke again on main thread
132 controller
.beginSheet(controller
.window());
135 if(log
.isDebugEnabled()) {
136 log
.debug("Await sheet dismiss");
138 // Synchronize on parent controller. Only display one sheet at once.
142 catch(InterruptedException e
) {
143 log
.error("Error waiting for sheet dismiss", e
);
144 this.callback(CANCEL_OPTION
);
150 protected void beginSheet(final NSWindow window
) {
151 parent
.window().makeKeyAndOrderFront(null);
152 NSApplication
.sharedApplication().beginSheet(window
, //sheet
153 parent
.window(), // modalForWindow
154 this.id(), // modalDelegate
155 Foundation
.selector("sheetDidClose:returnCode:contextInfo:"),
160 * Called by the runtime after a sheet has been dismissed. Ends any modal session and
161 * sends the returncode to the callback implementation. Also invalidates this controller to be
162 * garbage collected and notifies the lock object
164 * @param sheet Sheet window
165 * @param returncode Identifier for the button clicked by the user
166 * @param contextInfo Not used
168 public void sheetDidClose_returnCode_contextInfo(final NSWindow sheet
, final int returncode
, ID contextInfo
) {
169 sheet
.orderOut(null);
170 this.returncode
= returncode
;
171 this.callback(returncode
);
173 if(!this.isSingleton()) {
179 public void invalidate() {
180 sheetRegistry
.remove(this);
185 * @return True if the class is a singleton and the object should
186 * not be invlidated upon the sheet is closed
187 * @see #sheetDidClose_returnCode_contextInfo(ch.cyberduck.binding.application.NSWindow, int, org.rococoa.ID)
190 public boolean isSingleton() {