001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package icy.gui.frame; 020 021import icy.action.IcyAbstractAction; 022import icy.common.MenuCallback; 023import icy.gui.util.ComponentUtil; 024import icy.gui.util.LookAndFeelUtil; 025import icy.resource.ResourceUtil; 026import icy.resource.icon.IcyIcon; 027import icy.system.SystemUtil; 028import icy.system.thread.ThreadUtil; 029 030import java.awt.HeadlessException; 031import java.awt.Rectangle; 032import java.awt.event.ActionEvent; 033import java.awt.event.KeyEvent; 034import java.awt.event.WindowAdapter; 035import java.awt.event.WindowEvent; 036import java.beans.PropertyChangeEvent; 037import java.beans.PropertyChangeListener; 038 039import javax.swing.JFrame; 040import javax.swing.JMenu; 041import javax.swing.JRootPane; 042 043import org.pushingpixels.substance.internal.utils.SubstanceTitlePane; 044 045/** 046 * @author Stephane 047 */ 048public class IcyExternalFrame extends JFrame 049{ 050 /** 051 * 052 */ 053 private static final long serialVersionUID = 9130936218505070807L; 054 055 private class CloseAction extends IcyAbstractAction 056 { 057 /** 058 * 059 */ 060 private static final long serialVersionUID = 4933605299188863452L; 061 062 public CloseAction() 063 { 064 super("Close", new IcyIcon(ResourceUtil.ICON_CLOSE, 20), "Close window", KeyEvent.VK_F4, SystemUtil 065 .getMenuCtrlMask()); 066 } 067 068 @Override 069 public boolean doAction(ActionEvent e) 070 { 071 close(); 072 return true; 073 } 074 } 075 076 /** 077 * internals 078 */ 079 private SubstanceTitlePane titlePane; 080 // private JMenuBar systemMenuBar; 081 MenuCallback systemMenuCallback; 082 private boolean titleBarVisible; 083 private boolean closeItemVisible; 084 private boolean initialized = false; 085 086 /** 087 * @param title 088 * @throws HeadlessException 089 */ 090 public IcyExternalFrame(String title) throws HeadlessException 091 { 092 super(title); 093 094 getRootPane().addPropertyChangeListener("titlePane", new PropertyChangeListener() 095 { 096 @Override 097 public void propertyChange(PropertyChangeEvent evt) 098 { 099 // invoke later so the titlePane variable is up to date 100 ThreadUtil.invokeLater(new Runnable() 101 { 102 @Override 103 public void run() 104 { 105 updateTitlePane(); 106 } 107 }); 108 } 109 }); 110 111 addWindowListener(new WindowAdapter() 112 { 113 @Override 114 public void windowClosed(WindowEvent e) 115 { 116 // release the system menu callback as it can lead to some memory leak 117 // (cycling reference) 118 systemMenuCallback = null; 119 } 120 }); 121 setIconImages(ResourceUtil.getIcyIconImages()); 122 setVisible(false); 123 124 systemMenuCallback = null; 125 closeItemVisible = true; 126 updateTitlePane(LookAndFeelUtil.getTitlePane(this)); 127 128 titleBarVisible = true; 129 initialized = true; 130 } 131 132 /** 133 * update internals informations linked to title pane with specified pane 134 */ 135 protected void updateTitlePane(final SubstanceTitlePane pane) 136 { 137 // update pane save 138 if (pane != null) 139 titlePane = pane; 140 // update menu 141 // if (titlePane != null) 142 // systemMenuBar = titlePane.getMenuBar(); 143 // refresh system menu whatever 144 updateSystemMenu(); 145 } 146 147 /** 148 * update internals informations linked to title pane 149 */ 150 protected void updateTitlePane() 151 { 152 if (initialized) 153 { 154 // title pane can have changed 155 updateTitlePane(LookAndFeelUtil.getTitlePane(this)); 156 157 if (!titleBarVisible) 158 setTitleBarVisible(false); 159 } 160 } 161 162 /** 163 * Refresh system menu 164 */ 165 public void updateSystemMenu() 166 { 167 if (titlePane != null) 168 { 169 final JMenu menu; 170 171 if (systemMenuCallback != null) 172 menu = systemMenuCallback.getMenu(); 173 else 174 menu = getDefaultSystemMenu(); 175 176 // ensure compatibility with heavyweight component 177 menu.getPopupMenu().setLightWeightPopupEnabled(false); 178 179 // rebuild menu 180 titlePane.setSystemMenu(menu); 181 // systemMenuBar.removeAll(); 182 // systemMenuBar.add(menu); 183 // systemMenuBar.validate(); 184 } 185 } 186 187 public void setTitleBarVisible(boolean value) 188 { 189 if (value) 190 getRootPane().setWindowDecorationStyle(JRootPane.FRAME); 191 else 192 getRootPane().setWindowDecorationStyle(JRootPane.NONE); 193 194 validate(); 195 } 196 197 /** 198 * close frame 199 */ 200 public void close() 201 { 202 dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING)); 203 } 204 205 /** 206 * Implement isMinimized method 207 */ 208 public boolean isMinimized() 209 { 210 return ComponentUtil.isMinimized(this); 211 } 212 213 /** 214 * Implement isMaximized method 215 */ 216 public boolean isMaximized() 217 { 218 return ComponentUtil.isMaximized(this); 219 } 220 221 /** 222 * Implement setMinimized method 223 */ 224 public void setMinimized(final boolean value) 225 { 226 ComponentUtil.setMinimized(this, value); 227 } 228 229 /** 230 * Implement setMaximized method 231 */ 232 public void setMaximized(final boolean value) 233 { 234 ComponentUtil.setMaximized(this, value); 235 } 236 237 /** 238 * @return the titleBarVisible 239 */ 240 public boolean isTitleBarVisible() 241 { 242 return getRootPane().getWindowDecorationStyle() != JRootPane.NONE; 243 } 244 245 /** 246 * @return the closeItemVisible 247 */ 248 public boolean isCloseItemVisible() 249 { 250 return closeItemVisible; 251 } 252 253 /** 254 * @param value 255 * the closeItemVisible to set 256 */ 257 public void setCloseItemVisible(boolean value) 258 { 259 if (closeItemVisible != value) 260 { 261 closeItemVisible = value; 262 263 ThreadUtil.invokeLater(new Runnable() 264 { 265 @Override 266 public void run() 267 { 268 updateSystemMenu(); 269 } 270 }); 271 } 272 } 273 274 /** 275 * Return the default system menu 276 */ 277 public JMenu getDefaultSystemMenu() 278 { 279 final JMenu result = new JMenu(); 280 281 if (closeItemVisible) 282 result.add(new CloseAction()); 283 284 return result; 285 } 286 287 /** 288 * @return the systemMenuCallback 289 */ 290 public MenuCallback getSystemMenuCallback() 291 { 292 return systemMenuCallback; 293 } 294 295 /** 296 * @param value 297 * the systemMenuCallback to set 298 */ 299 public void setSystemMenuCallback(MenuCallback value) 300 { 301 if (systemMenuCallback != value) 302 { 303 systemMenuCallback = value; 304 305 ThreadUtil.invokeLater(new Runnable() 306 { 307 @Override 308 public void run() 309 { 310 updateSystemMenu(); 311 } 312 }); 313 } 314 } 315 316 @Override 317 public void reshape(int x, int y, int width, int height) 318 { 319 final Rectangle r = new Rectangle(x, y, width, height); 320 321 // prevent to go completely off screen 322 ComponentUtil.fixPosition(this, r); 323 324 super.reshape(r.x, r.y, r.width, r.height); 325 } 326 327// @Override 328// public synchronized void setMaximizedBounds(Rectangle bounds) 329// { 330// Rectangle bnds = SystemUtil.getScreenBounds(ComponentUtil.getScreen(this), true); 331// 332// if (bnds.isEmpty()) 333// bnds = bounds; 334// // at least use the location from original bounds 335// else if (bounds != null) 336// bnds.setLocation(bounds.getLocation()); 337// else 338// bnds.setLocation(0, 0); 339// 340// super.setMaximizedBounds(bnds); 341// } 342}