001package ball.swing; 002/*- 003 * ########################################################################## 004 * Utilities 005 * $Id: ClosedFutureJFrame.java 6118 2020-06-04 19:31:45Z ball $ 006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/ball-util/trunk/src/main/java/ball/swing/ClosedFutureJFrame.java $ 007 * %% 008 * Copyright (C) 2008 - 2020 Allen D. Ball 009 * %% 010 * Licensed under the Apache License, Version 2.0 (the "License"); 011 * you may not use this file except in compliance with the License. 012 * You may obtain a copy of the License at 013 * 014 * http://www.apache.org/licenses/LICENSE-2.0 015 * 016 * Unless required by applicable law or agreed to in writing, software 017 * distributed under the License is distributed on an "AS IS" BASIS, 018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 019 * See the License for the specific language governing permissions and 020 * limitations under the License. 021 * ########################################################################## 022 */ 023import java.awt.event.WindowAdapter; 024import java.awt.event.WindowEvent; 025import java.util.concurrent.ExecutionException; 026import java.util.concurrent.Future; 027import java.util.concurrent.TimeUnit; 028import java.util.concurrent.TimeoutException; 029import javax.swing.JFrame; 030import lombok.ToString; 031 032/** 033 * {@link JFrame} implementation that provides a {@link Future} indicating 034 * when the {@link JFrame} is closed (or made invisible). 035 * 036 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 037 * @version $Revision: 6118 $ 038 */ 039public class ClosedFutureJFrame extends JFrame { 040 private static final long serialVersionUID = -1292559417822522869L; 041 042 /** @serial */ private final Object lock = new Object(); 043 /** @serial */ private boolean isStarted = false; 044 /** @serial */ private boolean isCancelled = false; 045 /** @serial */ private final FutureImpl future = new FutureImpl(); 046 047 /** 048 * Sole constructor. 049 * 050 * @param title The frame title. 051 */ 052 public ClosedFutureJFrame(String title) { 053 super(title); 054 055 setDefaultCloseOperation(HIDE_ON_CLOSE); 056 addWindowListener(new WindowListenerImpl()); 057 } 058 059 @Override 060 public void setVisible(boolean visible) { 061 synchronized (lock) { 062 isStarted |= (visible && (! isCancelled)); 063 super.setVisible(visible && (! isCancelled)); 064 065 if (! isVisible()) { 066 lock.notifyAll(); 067 } 068 } 069 } 070 071 /** 072 * Method to get {@link Future} indicating when {@link.this} 073 * {@link JFrame} is closed. 074 * 075 * @return The closed {@link Future}. 076 */ 077 public Future<Boolean> closedFuture() { return future; } 078 079 @ToString 080 private class WindowListenerImpl extends WindowAdapter { 081 public WindowListenerImpl() { super(); } 082 083 @Override 084 public void windowClosing(WindowEvent event) { setVisible(false); } 085 } 086 087 @ToString 088 private class FutureImpl implements Future<Boolean> { 089 public FutureImpl() { } 090 091 @Override 092 public boolean cancel(boolean mayInterruptIfRunning) { 093 boolean cancelled = false; 094 095 if (isStarted) { 096 if (mayInterruptIfRunning) { 097 cancelled = isVisible(); 098 isCancelled |= true; 099 setVisible(false); 100 } 101 } else { 102 cancelled = true; 103 isCancelled |= true; 104 } 105 106 return cancelled; 107 } 108 109 @Override 110 public boolean isCancelled() { return isCancelled; } 111 112 @Override 113 public boolean isDone() { return (isCancelled() || (! isVisible())); } 114 115 @Override 116 public Boolean get() throws InterruptedException, ExecutionException { 117 Boolean result = null; 118 119 try { 120 result = get(0, TimeUnit.MILLISECONDS); 121 } catch (TimeoutException exception) { 122 throw new IllegalStateException(exception); 123 } 124 125 return result; 126 } 127 128 @Override 129 public Boolean get(long timeout, 130 TimeUnit unit) throws InterruptedException, 131 ExecutionException, 132 TimeoutException { 133 Boolean result = null; 134 135 synchronized (lock) { 136 if (! isDone()) { 137 lock.wait(unit.toMillis(timeout), 138 (int) (unit.toNanos(timeout) % 1000000)); 139 } 140 141 result = isDone() ? true : null; 142 } 143 144 return result; 145 } 146 } 147}