/*******************************************************************************
gocha.org-lib-java Библеотека общего назначения
(с) Камнев Георгий Павлович 2010 GPLv2
Данная программа является свободным программным обеспечением. Вы вправе
распространять ее и/или модифицировать в соответствии с условиями версии 2
либо по вашему выбору с условиями более поздней версии
Стандартной Общественной Лицензии GNU, опубликованной Free Software Foundation.
Мы распространяем данную программу в надежде на то, что она будет вам полезной,
однако НЕ ПРЕДОСТАВЛЯЕМ НА НЕЕ НИКАКИХ ГАРАНТИЙ,
в том числе ГАРАНТИИ ТОВАРНОГО СОСТОЯНИЯ ПРИ ПРОДАЖЕ
и ПРИГОДНОСТИ ДЛЯ ИСПОЛЬЗОВАНИЯ В КОНКРЕТНЫХ ЦЕЛЯХ.
Для получения более подробной информации ознакомьтесь
со Стандартной Общественной Лицензией GNU.
Вместе с данной программой вы должны были получить экземпляр
Стандартной Общественной Лицензии GNU.
Если вы его не получили, сообщите об этом в Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*******************************************************************************/
package org.gocha.gui;
import java.awt.Dimension;
import java.awt.GraphicsEnvironment;
import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import org.gocha.types.PropertyController;
import org.gocha.types.ToStringConverter;
import org.gocha.types.ToValueConvertor;
import org.gocha.types.ValueController;
/**
* Хранит свойства указанного обекта в текстовом файле в виде ключ/значение. <br/>
* Сохраняет состояние объекта (компонента awt) при закрытии окна (и смене видимости)
* ; записывает в файл при выходе (ApplicationGlobal.fireExit).
* @author gocha
* @see org.gocha.gui.ApplicationGlobal#fireExit(java.lang.Object)
*/
public class GuiKeeper implements Closeable
{
private Object owner = null;
private String[] ownerProperties = null;
private String propertyPrefix = null;
/**
* Конструктор
*/
private GuiKeeper(){
initSave();
}
@Override
protected void finalize() throws Throwable
{
try{
close();
// System.out.println("GuiKeeper.finalize");
}catch(IOException e){
}
super.finalize();
}
/**
* Сохраняет указанные свойства и отключает слушателей
* @throws IOException Ошибка сохранения
*/
@Override
public void close() throws IOException
{
if( owner==null )return;
if( ownerProperties==null )return;
if( owner instanceof JComponent ){
((JComponent)owner).removeAncestorListener(ancestorListener);
}
if( owner instanceof Window ){
((Window)owner).removeWindowListener(windowListener);
}
storeProperties();
owner = null;
ownerProperties = null;
propertyPrefix = null;
}
private static Collection<GuiKeeper> keepers = new HashSet<GuiKeeper>();
/**
* Создает хранителя свойств указанного объекта
* @param propertyPrefix Префикс
* @param owner Объект
* @param properties Свойства
* @return Хранит свойства
*/
public static GuiKeeper keepProperties(Runnable failedRestore,JFrame owner,String ... properties){
if (owner == null) {
throw new IllegalArgumentException("owner == null");
}
return keepProperties(failedRestore, owner.getClass().getName()+":", owner, properties );
}
public static Runnable centerWindow(Window wnd){
return centerWindow(wnd, -1, -1);
}
public static Runnable centerWindow(Window wnd,double widthAspect,double heightAspect){
if (wnd == null) {
throw new IllegalArgumentException("wnd==null");
}
final Window _wnd = wnd;
final double _widthAspect = widthAspect;
final double _heightAspect = heightAspect;
final WindowListener l = new WindowAdapter() {
@Override
public void windowOpened(WindowEvent e)
{
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
int screenX = ge.getCenterPoint().x * 2;
int screenY = ge.getCenterPoint().y * 2;
int newWidth = -1;
int newHeight = -1;
if( _widthAspect>=0 ) newWidth = (int)((double)screenX * _widthAspect);
if( _heightAspect>=0 ) newHeight = (int)((double)screenY * _heightAspect);
if( newWidth==0 )newWidth = 1;
if( newHeight==0 )newHeight = 1;
Dimension size = _wnd.getSize();
if( newWidth > 0 ) size.width = newWidth;
if( newHeight > 0 ) size.height = newHeight;
if( newWidth > 0 || newHeight>0 )_wnd.setSize(size);
GuiUtil.centerWindow(_wnd);
_wnd.removeWindowListener(this);
}
};
Runnable r = new Runnable() {
@Override
public void run() {
_wnd.addWindowListener(l);
}
};
return r;
}
public static GuiKeeper keepLocationAndSize(Window wnd){
if (wnd == null) {
throw new IllegalArgumentException("wnd == null");
}
final Window w = wnd;
final WindowListener l = new WindowAdapter() {
@Override
public void windowOpened(WindowEvent e)
{
GuiUtil.centerWindow(w);
w.removeWindowListener(this);
}
};
Runnable f = new Runnable() {
@Override
public void run()
{
if( !w.isVisible() ){
w.addWindowListener(l);
}
}
};
return keepProperties(f, wnd.getClass().getName()+":", wnd, "location", "size");
}
/**
* Создает хранителя свойств указанного объекта
* @param propertyPrefix Префикс
* @param owner Объект
* @param properties Свойства
* @return Хранит свойства
*/
public static GuiKeeper keepProperties(Runnable failedRestore,String propertyPrefix,Object owner,String ... properties){
if (owner == null) {
throw new IllegalArgumentException("owner == null");
}
GuiKeeper keeper = new GuiKeeper();
keeper.owner = owner;
keeper.ownerProperties = properties;
keeper.propertyPrefix = propertyPrefix;
keeper.restoreProperties(failedRestore);
if( owner instanceof JComponent ){
((JComponent)owner).addAncestorListener(keeper.ancestorListener);
}
if( owner instanceof Window ){
((Window)owner).addWindowListener(keeper.windowListener);
}
keepers.add(keeper);
return keeper;
}
private AncestorListener ancestorListener = new AncestorListener() {
@Override
public void ancestorAdded(AncestorEvent event){
// throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void ancestorRemoved(AncestorEvent event){
storeProperties();
}
@Override
public void ancestorMoved(AncestorEvent event){
storeProperties();
}
};
private WindowListener windowListener = new WindowListener() {
@Override
public void windowOpened(WindowEvent e){
// throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void windowClosing(WindowEvent e){
storeProperties();
}
@Override
public void windowClosed(WindowEvent e){
storeProperties();
// throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void windowIconified(WindowEvent e){
// throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void windowDeiconified(WindowEvent e){
// throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void windowActivated(WindowEvent e){
// throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void windowDeactivated(WindowEvent e){
// throw new UnsupportedOperationException("Not supported yet.");
}
};
private org.gocha.types.TypesConverters convertors = org.gocha.types.DefaultTypesConvertors.instance();
public void restoreProperties(Runnable failedRestore){
if( owner==null )return;
if( ownerProperties==null )return;
int restoreCount = 0;
for( ValueController vc : PropertyController.buildControllers(owner) ){
String propName = vc.getName();
boolean setProperty = false;
for( String p : ownerProperties ){
if( p==null )continue;
if( p.equals(propName) ){
setProperty = true;
}
}
if( !setProperty )continue;
String key = propertyPrefix==null ? propName : propertyPrefix + propName;
String textValue = getProperty(key);
if( textValue==null )continue;
ToValueConvertor conv = convertors.toValueFor(vc.getType());
if( conv==null )continue;
try{
Object objValue = conv.convertToValue(textValue);
if( objValue==null )continue;
vc.setValue(objValue);
restoreCount++;
}catch(Throwable e){
Logger.getLogger(GuiKeeper.class.getName()).log(Level.WARNING,null,e);
}
}
if( restoreCount<ownerProperties.length ){
if( failedRestore!=null )failedRestore.run();
}
}
public void storeProperties(){
if( owner==null )return;
if( ownerProperties==null )return;
for( ValueController vc : PropertyController.buildControllers(owner) ){
String propName = vc.getName();
boolean getProperty = false;
for( String p : ownerProperties ){
if( p==null )continue;
if( p.equals(propName) ){
getProperty = true;
}
}
if( !getProperty )continue;
ToStringConverter conv = convertors.toStringFrom(vc.getType());
if( conv==null )continue;
try{
Object objValue = vc.getValue();
if( objValue==null )continue;
String key = propertyPrefix==null ? propName : propertyPrefix + propName;
String textValue = conv.convertToString(objValue);
if( textValue==null )continue;
setProperty(key, textValue);
}catch(Throwable e){
Logger.getLogger(GuiKeeper.class.getName()).log(Level.WARNING,null,e);
}
}
}
private final static String fileName = "guiKeeper.xml";
private static File file = null;
private static File file(){
if( file==null ){
File dir = ApplicationGlobal.instance().getLocalApplicationDirectory();
if( dir!=null ){
file = new File(dir,fileName);
}else{
Logger.getLogger(GuiKeeper.class.getName()).log(Level.SEVERE,"Не указан каталог appDir, или указан не верно");
}
}
return file;
}
private void initSave(){
if( attachedSaved )return;
AutoClose.instance().add(saveOnExit);
attachedSaved = true;
}
private static boolean attachedSaved = false;
private static Runnable saveOnExit = new Runnable() {
@Override
public void run(){
for( GuiKeeper k : keepers ){
try{
k.close();
}catch(IOException e){}
}
keepers.clear();
savePropertiesToFile();
}
};
private static void setProperty(String key,String value){
if( key==null )return;
if( value==null ){
properties().remove(key);
return;
}
properties().put(key, value);
}
private static String getProperty(String key){
if( key==null )return key;
return properties().getProperty(key);
}
private static Properties properties = null;
private static Properties properties(){
if( properties!=null )return properties;
properties = new Properties();
File f = file();
if( f!=null && f.exists() && f.canRead() && f.isFile() ){
FileInputStream fis = null;
try{
fis = new FileInputStream(f);
properties.loadFromXML(fis);
fis.close();
}catch(Exception e){
Logger.getLogger(GuiKeeper.class.getName()).log(Level.SEVERE,null,e);
}
}
return properties;
}
private static boolean savePropertiesToFile(){
File f = file();
if( f==null )return false;
if( properties==null ){
if( f.exists() && f.isFile() && f.canWrite() ){
try{
f.delete();
}catch(Exception e){
Logger.getLogger(GuiKeeper.class.getName()).log(Level.SEVERE,null,e);
}
}
return true;
}
boolean overwrite = false;
if( f.exists() && f.isFile() && f.canWrite() )overwrite = true;
if( !f.exists() )overwrite = true;
if( overwrite ){
FileOutputStream fos = null;
try{
fos = new FileOutputStream(f);
properties.storeToXML(fos, "gui properties");
fos.close();
return true;
}catch(Exception e){
Logger.getLogger(GuiKeeper.class.getName()).log(Level.SEVERE,null,e);
}
}else{
Logger.getLogger(GuiKeeper.class.getName()).warning("can't overwrite "+f.toString());
}
return false;
}
}