package org.openswing.swing.mdi.client; import java.beans.*; import java.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import java.io.*; import org.openswing.swing.client.*; import org.openswing.swing.form.client.*; import org.openswing.swing.logger.client.*; import org.openswing.swing.tree.client.*; import org.openswing.swing.util.client.*; import org.openswing.swing.util.java.*; /** * <p>Title: OpenSwing Framework</p> * <p>Description: Base Internal Frame: to use together with MDI Frame. * You can set this internal frame as modal, by means of setModal() method BUT ONLY AFTER invoking MDIFrame.add() method, * i.e. after internal frame is already visible.</p> * <p>Copyright: Copyright (C) 2006 Mauro Carniel</p> * * <p> This file is part of OpenSwing Framework. * This library is free software; you can redistribute it and/or * modify it under the terms of the (LGPL) Lesser General Public * License as published by the Free Software Foundation; * * GNU LESSER GENERAL PUBLIC LICENSE * Version 2.1, February 1999 * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * The author may be contacted at: * maurocarniel@tin.it</p> * * @author Mauro Carniel * @version 1.0 */ public class InternalFrame extends JInternalFrame { /** children windows (optional); they will be automatically closed when this window is closed */ private ArrayList frameList = new ArrayList(); /** parent frame (optional) */ private InternalFrame parentFrame; /** used when this window will be closed: if this flag is set to <code>true</code> then a warning dialog will be showed before close the window to ask if it must be close; default value: ClientSettings.ASK_BEFORE_CLOSE */ private boolean askBeforeClose = ClientSettings.ASK_BEFORE_CLOSE; /** last border of internal frame, before hiding title bar */ private Border lastBorder = null; /** flag used to hide/show title bar; default value: <code>false</code> i.e. title bar is visible */ private boolean hideTitleBar = false; /** last component added to the north of the internal frame, before hiding title bar */ private JComponent lastNorthPane = null; /** internal frame modal state; default value: <code>false</code> i.e. internal frame is not modal */ private boolean modal; /** internal frame owner */ protected javax.swing.JInternalFrame owner = this; /** used to define whether this window can be opened more times or only one instance can be created per time; default value: <code>false</code>, i.e. any number of instances of this window can be created */ private boolean uniqueInstance = false; /** * Costructor. */ public InternalFrame() { super("",true,true,true,true); setFrameIcon(new ImageIcon(ClientUtils.getImage(ClientSettings.ICON_FILENAME))); this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); this.addInternalFrameListener(new InternalFrameAdapter() { public void internalFrameClosing(InternalFrameEvent e) { try { closeFrame(); } catch (PropertyVetoException ex) { } } /** * Invoked when an internal frame is activated. */ public void internalFrameActivated(InternalFrameEvent e) { } /** * Invoked when an internal frame is de-activated. */ public void internalFrameDeactivated(InternalFrameEvent e) { if (ClientSettings.MDI_TOOLBAR!=null) ClientSettings.MDI_TOOLBAR.disableAllButtons(); } }); } /** * Create a link between this window and the parent window. * @param parent parent window */ public final void setParentFrame(InternalFrame parentFrame) { this.parentFrame = parentFrame; } /** * @return list of TreePanel/GridControl/Form objects within this that is in insert/edit mode and that contain changes, an empty list otherwise */ public final ArrayList checkComponents() { return checkComponents(this.getComponents()); } /** * @param c components to analize * @return list of TreePanel/GridControl/Form objects within c components, that are in insert/edit mode and that contain changes, otherwise an empty list */ private ArrayList checkComponents(Component[] c) { ArrayList aux = new ArrayList(); for(int i=0;i<c.length;i++) { if (c[i] instanceof GridControl) { c[i].transferFocus(); if (((GridControl)c[i]).getMode()==Consts.INSERT) aux.add(c[i]); if (((GridControl)c[i]).getMode()==Consts.EDIT && ((GridControl)c[i]).getVOListTableModel().getChangedRowNumbers().length>0) { aux.add(c[i]); } } else if (c[i] instanceof Form) { try { Component obj = DefaultKeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); if (obj!=null) { FocusListener[] f = obj.getFocusListeners(); if (f!=null) for(int j=0;j<f.length;j++) f[j].focusLost(new FocusEvent(this,FocusEvent.FOCUS_LOST)); } obj.transferFocus(); } catch (Exception ex) { ((Form) c[i]).transferFocus(); } boolean changed = ((Form)c[i]).isChanged(); if (((Form)c[i]).getMode()==Consts.INSERT && changed) aux.add(c[i]); if (((Form)c[i]).getMode()==Consts.EDIT && changed) aux.add(c[i]); } else if (c[i] instanceof TreePanel) { if (((TreePanel)c[i]).isChanged()) aux.add(c[i]); } else if (c[i] instanceof JScrollPane) { aux.addAll(checkComponents( ((JScrollPane)c[i]).getViewport().getComponents() )); } else if (c[i] instanceof Container) { aux.addAll(checkComponents( ((Container)c[i]).getComponents() )); } } return aux; } /** * Callback method invoked by "closeFrame" when internal frame contains changes and the user selects "yes" option on dialog about saving changes: * If this method is not ovverrided, then "closeFrame" will attempt to automatically save each changed objects (Form, Grid, TreePanel) * and this automation could be incorrect, in case of multiple objects to save in a specific order, * if this is the case, then "saveChanges" method should be overrided, in order to specify a custom saving strategy. * @return <code>true</code> if changes has been correctly saved, <code>false</code> otherwise, i.e. some error occours on saving data, so frame must NOT be closed; as default dehavior it throws an exception: UnsupportedOperationException, i.e. closeFrame method automatically attempts to save changes */ public boolean saveChanges() { throw new UnsupportedOperationException("No check performed"); } /** * Supports reporting constrained property changes. * This method can be called when a constrained property has changed * and it will send the appropriate <code>PropertyChangeEvent</code> * to any registered <code>VetoableChangeListeners</code>. * * @param propertyName the name of the property that was listened on * @param oldValue the old value of the property * @param newValue the new value of the property * @exception PropertyVetoException when the attempt to set the * property is vetoed by the component */ protected final void fireVetoableChange(String propertyName, Object oldValue, Object newValue) throws java.beans.PropertyVetoException { if (IS_CLOSED_PROPERTY.equals(propertyName) && Boolean.FALSE.equals(oldValue) && Boolean.TRUE.equals(newValue)) { throw new PropertyVetoException("",new PropertyChangeEvent(this,IS_CLOSED_PROPERTY,Boolean.TRUE,Boolean.FALSE)); } else super.fireVetoableChange(propertyName, oldValue, newValue); } /** * This method is called when this window will be closed. * The first method calle is beforeCloseFrame; if this will return <code>false</code> then the closing window operation will be interrupted. */ public final void closeFrame() throws PropertyVetoException { if (!beforeCloseFrame()) { return; } if (askBeforeClose) { // check if this contains a GridControl or a Form panel in insert/edit mode and they contain changes... ArrayList changedObjects = checkComponents(); if (changedObjects.size()>0) { int res = OptionPane.showConfirmDialog( this, "save changes?", "confirm window closing", JOptionPane.YES_NO_CANCEL_OPTION ); if (res==JOptionPane.YES_OPTION) { try { boolean ok = saveChanges(); if (!ok) { // save task contains errors: frame closing will be aborted... return; } } catch (UnsupportedOperationException ex1) { // "saveChanged" method was not overrided: // each changed object will be saved automatically // NOTE: this automation could be incorrect, in case of multiple objects to save in a specific order, // if this is the case, then "saveChanges" method should be overrided, in order to specify a custom saving strategy for(int i=0;i<changedObjects.size();i++) { if (changedObjects.get(i) instanceof GridControl) { if (!((GridControl)changedObjects.get(i)).save()) return; } else if (changedObjects.get(i) instanceof Form) { if (!((Form)changedObjects.get(i)).save()) { return; } } else if (changedObjects.get(i) instanceof TreePanel) { Logger.error(this.getClass().getName(), "closeFrame", "Error while saving TreePanel: you have to override 'saveChanges' method.",null); return; } } } } if (res==JOptionPane.CANCEL_OPTION) { return; } } } try{ // close all children windows... while(frameList.size()>0){ InternalFrame frame = (InternalFrame)frameList.get(0); frameList.remove(0); frame.closeFrame(); } // remove link with the parent window... if (parentFrame!=null) parentFrame.popFrame(this); } catch (Exception e){ e.printStackTrace(); } try { ClientUtils.disposeComponents(getContentPane().getComponents()); setVisible(false); dispose(); MDIFrame.getWindowMenu().removeWindow(this); } catch (Exception ex) { ex.printStackTrace(); } } /** * Remove a child window from this. * It's called by closeFrame. * @param frame child window to remove */ public final InternalFrame popFrame(InternalFrame frame) { if (frame!=null) frameList.remove(frame); return frame; } /** * Add a child window to this. * @param frame child window to add */ public final void pushFrame(InternalFrame frame) { frameList.add(frame); } /** * Callback method invoked by closeFrame. * @return <code>true</code> allows the closing operation to continue, <code>false</code> the closing operation will be interrupted */ protected boolean beforeCloseFrame() { return true; } /** * @return used when this window will be closed: if this flag is set to <code>true</code> then a warning dialog will be showed before close the window to ask if it must be close */ public final boolean isAskBeforeClose() { return askBeforeClose; } /** * Used when this window will be closed: if this flag is set to <code>true</code> then a warning dialog will be showed before close the window to ask if it must be close * @param askBeforeClose <code>true</code> to show a warning dialog before close the window to ask if it must be close */ public final void setAskBeforeClose(boolean askBeforeClose) { this.askBeforeClose = askBeforeClose; } /** * Define if title bar must be hidden or showed * @param hideTitleBar flag used to hide/show title bar; <code>true</code> to hide title bar; <code>false</code> to show it */ public final boolean isHideTitleBar() { return hideTitleBar; } /** * Define if title bar must be hidden or showed * @param hideTitleBar flag used to hide/show title bar; <code>true</code> to hide title bar; <code>false</code> to show it */ public final void setHideTitleBar(boolean hideTitleBar) { this.hideTitleBar = hideTitleBar; if (hideTitleBar && !Beans.isDesignTime()) { super.setIconifiable(false); super.setMaximizable(false); lastNorthPane = ((javax.swing.plaf.basic.BasicInternalFrameUI)this.getUI()).getNorthPane(); ((javax.swing.plaf.basic.BasicInternalFrameUI)this.getUI()).setNorthPane(null); lastBorder = super.getBorder(); super.setBorder(BorderFactory.createEmptyBorder()); } else if (lastNorthPane!=null && lastBorder!=null) { ((javax.swing.plaf.basic.BasicInternalFrameUI)this.getUI()).setNorthPane(lastNorthPane); super.setBorder(lastBorder); } } /** * @return internal frame owner; used in case of modal internal frame */ public final javax.swing.JInternalFrame getOwner() { return owner; } /** * Set the internal frame owner; used in case of modal internal frame. * @param owner internal frame owner */ public final void setOwner(javax.swing.JInternalFrame owner) { this.owner=owner; } /** * Define this internal frame as modal. * IMPORTANT NOTE: do not call this method before showing this frame, but only AFTER MDIFrame.add() invokation. * @param modal <code>true</code> to set this internal frame as modal, <code>false</code> otherwise */ public final void setModal(boolean modal) { this.modal=modal; javax.swing.JDesktopPane desktop=getDesktopPane(); if (desktop instanceof JDesktopPane) { ((DesktopPane)desktop).setModal(this,modal); } } /** * @return <code>true</code> whether this internal frame as modal, <code>false</code> otherwise */ public final boolean isModal() { return modal; } /** * Iconify this internal frame. * Please do not call this method for modal internal frames! * @param iconifiable <code>true</code> to iconify internal frame */ public final void setIconifiable(boolean iconifiable) { if (modal && iconifiable) throw new IllegalArgumentException("InternalFrame class cannot be iconifiable while modal"); super.setIconifiable(iconifiable); } /* * Creates a new EventDispatchThread to dispatch events from this. * This method returns when stopModal is invoked. */ public synchronized void startModal() { if (isVisible() && !isShowing()) { Container parent = this.getParent(); while (parent!=null) { if (!parent.isVisible()) parent.setVisible(true); parent = parent.getParent(); } } try { if (javax.swing.SwingUtilities.isEventDispatchThread()) { EventQueue theQueue = getToolkit().getSystemEventQueue(); while (isVisible()) { // This is essentially the body of EventDispatchThread AWTEvent event = theQueue.getNextEvent(); Object src = event.getSource(); // can't call theQueue.dispatchEvent, so I pasted its body here if (event instanceof ActiveEvent) { ((ActiveEvent) event).dispatch(); } else if (src instanceof Component) { ((Component) src).dispatchEvent(event); } else if (src instanceof MenuComponent) { ((MenuComponent) src).dispatchEvent(event); } else { Logger.error(this.getClass().getName(), "startModal", "Unable to dispatch event: "+event,null); } } } else { while (isVisible()) wait(); } } catch(Exception ex){ ex.printStackTrace(); } } /* * Stops the event dispatching loop created by a previous call to <code>startModal</code>. */ public synchronized void stopModal() { notifyAll(); } /** * @return used to define whether this window can be opened more times or only one instance can be created per time */ public final boolean isUniqueInstance() { return uniqueInstance; } /** * Define whether this window can be opened more times or only one instance can be created per time. * Default value: <code>false</code>, i.e. any number of instances of this window can be created. * @param uniqueInstance <code>true</code> only one instance of this window will be opened per time, <code>false</code> otherwise */ public final void setUniqueInstance(boolean uniqueInstance) { this.uniqueInstance = uniqueInstance; } }