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
.NSPanel
;
25 import ch
.cyberduck
.binding
.application
.NSWindow
;
26 import ch
.cyberduck
.binding
.application
.SheetCallback
;
27 import ch
.cyberduck
.binding
.foundation
.NSThread
;
28 import ch
.cyberduck
.core
.threading
.ControllerMainAction
;
30 import org
.apache
.log4j
.Logger
;
31 import org
.rococoa
.Foundation
;
32 import org
.rococoa
.ID
;
34 import java
.util
.HashSet
;
36 import java
.util
.concurrent
.CountDownLatch
;
41 public abstract class SheetController
extends WindowController
implements SheetCallback
{
42 private static Logger log
= Logger
.getLogger(SheetController
.class);
45 * Keep a reference to the sheet to protect it from being
46 * deallocated as a weak reference before the callback from the runtime
48 protected static final Set
<SheetController
> sheetRegistry
49 = new HashSet
<SheetController
>();
52 * The controller of the parent window
54 protected final WindowController parent
;
57 * Dismiss button clicked
59 private int returncode
;
61 private CountDownLatch signal
;
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 * Translate return codes from sheet selection
84 * @param selected Button pressed
85 * @return Sheet callback constant
86 * @see SheetCallback#DEFAULT_OPTION
87 * @see SheetCallback#CANCEL_OPTION
89 protected int getCallbackOption(NSButton selected
) {
90 if(selected
.tag() == NSPanel
.NSCancelButton
) {
91 return SheetCallback
.CANCEL_OPTION
;
93 return SheetCallback
.DEFAULT_OPTION
;
97 * This must be the target action for any button in the sheet dialog. Will validate the input
98 * and close the sheet; #sheetDidClose will be called afterwards
100 * @param sender A button in the sheet dialog
103 public void closeSheet(final NSButton sender
) {
104 if(log
.isDebugEnabled()) {
105 log
.debug(String
.format("Close sheet with button %s", sender
.title()));
107 if(this.getCallbackOption(sender
) == DEFAULT_OPTION
|| this.getCallbackOption(sender
) == ALTERNATE_OPTION
) {
108 if(!this.validateInput()) {
109 AppKitFunctionsLibrary
.beep();
113 NSApplication
.sharedApplication().endSheet(this.window(), this.getCallbackOption(sender
));
117 * @return The tag of the button this sheet was dismissed with
119 public int returnCode() {
120 return this.returncode
;
124 * Check input fields for any errors
126 * @return true if a valid input has been given
128 protected boolean validateInput() {
132 public void beginSheet() {
133 synchronized(parent
.window()) {
134 signal
= new CountDownLatch(1);
135 if(NSThread
.isMainThread()) {
136 // No need to call invoke on main thread
137 this.beginSheetImpl();
140 invoke(new ControllerMainAction(this) {
143 //Invoke again on main thread
147 if(log
.isDebugEnabled()) {
148 log
.debug("Await sheet dismiss");
150 // Synchronize on parent controller. Only display one sheet at once.
154 catch(InterruptedException e
) {
155 log
.error("Error waiting for sheet dismiss", e
);
156 this.callback(CANCEL_OPTION
);
162 protected void beginSheetImpl() {
164 parent
.window().makeKeyAndOrderFront(null);
165 NSApplication
.sharedApplication().beginSheet(this.window(), //window
166 parent
.window(), // modalForWindow
167 this.id(), // modalDelegate
168 Foundation
.selector("sheetDidClose:returnCode:contextInfo:"),
173 * Called by the runtime after a sheet has been dismissed. Ends any modal session and
174 * sends the returncode to the callback implementation. Also invalidates this controller to be
175 * garbage collected and notifies the lock object
177 * @param sheet Sheet window
178 * @param returncode Identifier for the button clicked by the user
179 * @param contextInfo Not used
181 public void sheetDidClose_returnCode_contextInfo(final NSWindow sheet
, final int returncode
, ID contextInfo
) {
182 sheet
.orderOut(null);
183 this.returncode
= returncode
;
184 this.callback(returncode
);
186 if(!this.isSingleton()) {
192 public void invalidate() {
193 sheetRegistry
.remove(this);
198 * @return True if the class is a singleton and the object should
199 * not be invlidated upon the sheet is closed
200 * @see #sheetDidClose_returnCode_contextInfo(ch.cyberduck.binding.application.NSWindow, int, org.rococoa.ID)
203 public boolean isSingleton() {