Skip to content

Commit

Permalink
Merge pull request #43 from metsci/master
Browse files Browse the repository at this point in the history
2.5.1 Pull Request
  • Loading branch information
ulmangt committed Aug 1, 2016
2 parents 12e594b + 5dfb293 commit f5821f6
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 218 deletions.
Expand Up @@ -150,7 +150,13 @@ public enum OverlapRenderingMode
@Override
public TimeSpan applyConstraint( Event event, TimeSpan proposedTimeSpan )
{
if ( !isEditable ) return event.getTimeSpan( );
// if the timeline is not editable, don't allow the change
// if the timeline is not resizable and one or both of the endpoints are fixed,
// then it is effectively not editable, don't allow the change
if ( !isEditable || ( !isResizeable && ( !isEndTimeMoveable || !isStartTimeMoveable ) ) )
{
return event.getTimeSpan( );
}

TimeStamp oldStart = event.getStartTime( );
TimeStamp oldEnd = event.getEndTime( );
Expand Down
Expand Up @@ -95,15 +95,15 @@ public void mouseMoved( GlimpseMouseEvent e )
if ( dragType == Location.Center )
{
double diff = time.durationAfter( anchorTime );
dragEvent.setTimes( eventStart.add( diff ), eventEnd.add( diff ) );
dragEvent.setTimes( eventStart.add( diff ), eventEnd.add( diff ), false );
}
else if ( dragType == Location.End && eventStart.isBefore( time ) )
{
dragEvent.setTimes( eventStart, time );
dragEvent.setTimes( eventStart, time, false );
}
else if ( dragType == Location.Start && eventEnd.isAfter( time ) )
{
dragEvent.setTimes( time, eventEnd );
dragEvent.setTimes( time, eventEnd, false );
}

e.setHandled( true );
Expand Down
190 changes: 190 additions & 0 deletions docking/src/main/java/com/metsci/glimpse/docking/AppConfigUtils.java
@@ -0,0 +1,190 @@
/*
* Copyright (c) 2016, Metron, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Metron, Inc. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL METRON, INC. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.metsci.glimpse.docking;

import static java.util.logging.Level.WARNING;
import static javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.logging.Logger;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

public class AppConfigUtils
{
private static final Logger logger = Logger.getLogger( AppConfigUtils.class.getName( ) );


/**
* Save the specified config object to an XML file in the application's config directory
* (see {@link AppConfigUtils#createAppDir(String)}.
* <p>
* Every class that could possibly be serialized, including the class of the config object,
* the classes of its fields, and any relevant subclasses, should be included in {@code dataClasses}.
* <p>
* In many cases it is helpful to have a separate package of classes just for serialization.
* Notably, this makes it easier to avoid accidentally changing the serialization format
* (e.g. by renaming a class or package). See the {@code com.metsci.glimpse.docking.xml}
* package (including its {@code package-info.java}) for an example.
* <p>
* <strong>NOTE:</strong> This function catches exceptions and logs them, instead of letting
* them propagate up the call stack. Ordinarily this would be a terrible idea, but for this
* function, terse usage is more important than careful error handling.
*/
public static void saveAppConfig( String appName, String filename, Object config, Collection<Class<?>> dataClasses )
{
try
{
File file = new File( createAppDir( appName ), filename );
newMarshaller( dataClasses ).marshal( config, file );
}
catch ( Exception e )
{
logger.log( WARNING, "Failed to write app config to file: app-name = " + appName + ", filename = " + filename, e );
}
}

/**
* Load a config object from the specified XML file in the application's config directory
* (see {@link AppConfigUtils#createAppDir(String)}. If that fails, load the config object
* from {@code fallbackUrl} (if it is non-null).
* <p>
* Every class that could possibly be deserialized, including the class of the config object,
* the classes of its fields, and any relevant subclasses, should be included in {@code dataClasses}.
* <p>
* In many cases it is helpful to have a separate package of classes just for serialization.
* Notably, this makes it easier to avoid accidentally changing the serialization format
* (e.g. by renaming a class or package). See the {@code com.metsci.glimpse.docking.xml}
* package (including its {@code package-info.java}) for an example.
* <p>
* <strong>NOTE:</strong> This function catches exceptions and logs them, instead of letting
* them propagate up the call stack. Ordinarily this would be a terrible idea, but for this
* function, terse usage is more important than careful error handling.
*/
public static <T> T loadAppConfig( String appName, String filename, URL fallbackUrl, Class<T> configClass, Collection<Class<?>> dataClasses )
{
try
{
File file = new File( createAppDir( appName ), filename );
if ( file.exists( ) )
{
Object unmarshalled = newUnmarshaller( dataClasses ).unmarshal( file );
return castUnmarshalled( unmarshalled, configClass );
}
}
catch ( Exception e )
{
logger.log( WARNING, "Failed to load application config from file: app-name = " + appName + ", filename = " + filename, e );
}

if ( fallbackUrl != null )
{
InputStream fallbackStream = null;
try
{
fallbackStream = fallbackUrl.openStream( );
Object unmarshalled = newUnmarshaller( dataClasses ).unmarshal( fallbackStream );
return castUnmarshalled( unmarshalled, configClass );
}
catch ( Exception e )
{
logger.log( WARNING, "Failed to load fallback application config from resource: resource = " + fallbackUrl.toString( ), e );
}
finally
{
if ( fallbackStream != null )
{
try
{
fallbackStream.close( );
}
catch ( IOException e )
{
logger.log( WARNING, "Failed to close fallback application config resource: resource = " + fallbackUrl.toString( ), e );
}
}
}
}

return null;
}

public static File createAppDir( String appName )
{
String homePath = System.getProperty( "user.home" );
if ( homePath == null ) throw new RuntimeException( "Property user.home is not defined" );

File appDir = new File( homePath, "." + appName );
appDir.mkdirs( );

if ( !appDir.isDirectory( ) ) throw new RuntimeException( "Failed to create app dir: " + appDir.getAbsolutePath( ) );
if ( !appDir.canRead( ) ) throw new RuntimeException( "Do not have read permission on app dir: " + appDir.getAbsolutePath( ) );
if ( !appDir.canWrite( ) ) throw new RuntimeException( "Do not have write permission on app dir: " + appDir.getAbsolutePath( ) );

return appDir;
}

public static Marshaller newMarshaller( Collection<Class<?>> dataClasses ) throws IOException, JAXBException
{
Class<?>[] classes = dataClasses.toArray( new Class[ 0 ] );
Marshaller marshaller = JAXBContext.newInstance( classes ).createMarshaller( );
marshaller.setProperty( JAXB_FORMATTED_OUTPUT, true );
return marshaller;
}

public static Unmarshaller newUnmarshaller( Collection<Class<?>> dataClasses ) throws JAXBException, IOException
{
Class<?>[] classes = dataClasses.toArray( new Class[ 0 ] );
Unmarshaller unmarshaller = JAXBContext.newInstance( classes ).createUnmarshaller( );
return unmarshaller;
}

public static <T> T castUnmarshalled( Object unmarshalled, Class<T> clazz )
{
if ( clazz.isInstance( unmarshalled ) )
{
return clazz.cast( unmarshalled );
}
else if ( unmarshalled instanceof JAXBElement )
{
return castUnmarshalled( ( ( JAXBElement<?> ) unmarshalled ).getValue( ), clazz );
}
else
{
throw new ClassCastException( "Unmarshalled object is neither a " + clazz.getName( ) + " nor a " + JAXBElement.class.getName( ) + ": classname = " + unmarshalled.getClass( ).getName( ) );
}
}

}
Expand Up @@ -67,6 +67,11 @@ protected void updateNormalBounds( )
}
}

public void setNormalBounds( int x, int y, int width, int height )
{
this.normalBounds = new Rectangle( x, y, width, height );
}

public Rectangle getNormalBounds( )
{
return new Rectangle( normalBounds );
Expand Down
Expand Up @@ -520,8 +520,8 @@ public void restoreArrangement( GroupArrangement groupArr, TileFactory tileFacto
if ( dockerRoot != null )
{
DockingFrame frame = addNewFrame( );
frame.setLocation( frameArr.x, frameArr.y );
frame.setSize( frameArr.width, frameArr.height );
frame.setBounds( frameArr.x, frameArr.y, frameArr.width, frameArr.height );
frame.setNormalBounds( frameArr.x, frameArr.y, frameArr.width, frameArr.height );
frame.setExtendedState( getFrameExtendedState( frameArr ) );
frame.setVisible( true );

Expand Down
84 changes: 11 additions & 73 deletions docking/src/main/java/com/metsci/glimpse/docking/DockingUtils.java
Expand Up @@ -26,25 +26,23 @@
*/
package com.metsci.glimpse.docking;

import static com.metsci.glimpse.docking.DockingXmlUtils.readArrangementXml;
import static com.metsci.glimpse.docking.DockingXmlUtils.writeArrangementXml;
import static com.metsci.glimpse.docking.AppConfigUtils.loadAppConfig;
import static com.metsci.glimpse.docking.AppConfigUtils.saveAppConfig;
import static java.awt.ComponentOrientation.RIGHT_TO_LEFT;
import static java.awt.Frame.MAXIMIZED_HORIZ;
import static java.awt.Frame.MAXIMIZED_VERT;
import static java.util.logging.Level.WARNING;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableCollection;

import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
Expand All @@ -56,12 +54,14 @@
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;

import com.metsci.glimpse.docking.xml.DockerArrangementNode;
import com.metsci.glimpse.docking.xml.DockerArrangementSplit;
import com.metsci.glimpse.docking.xml.DockerArrangementTile;
import com.metsci.glimpse.docking.xml.FrameArrangement;
import com.metsci.glimpse.docking.xml.GroupArrangement;

public class DockingUtils
{
private static final Logger logger = Logger.getLogger( DockingUtils.class.getName( ) );

public static void requireSwingThread( )
{
Expand Down Expand Up @@ -229,78 +229,16 @@ public static ImageIcon requireIcon( String resourcePath )
}
}

public static File createAppDir( String appName )
{
String homePath = System.getProperty( "user.home" );
if ( homePath == null ) throw new RuntimeException( "Property user.home is not defined" );

File appDir = new File( homePath, "." + appName );
appDir.mkdirs( );

if ( !appDir.isDirectory( ) ) throw new RuntimeException( "Failed to create app dir: " + appDir.getAbsolutePath( ) );
if ( !appDir.canRead( ) ) throw new RuntimeException( "Do not have read permission on app dir: " + appDir.getAbsolutePath( ) );
if ( !appDir.canWrite( ) ) throw new RuntimeException( "Do not have write permission on app dir: " + appDir.getAbsolutePath( ) );

return appDir;
}
public static final Collection<Class<?>> dockingXmlClasses = unmodifiableCollection( asList( GroupArrangement.class, FrameArrangement.class, DockerArrangementNode.class, DockerArrangementSplit.class, DockerArrangementTile.class ) );

public static void saveDockingArrangement( String appName, GroupArrangement groupArr )
{
try
{
File arrFile = new File( createAppDir( appName ), "arrangement.xml" );
writeArrangementXml( groupArr, arrFile );
}
catch ( Exception e )
{
logger.log( WARNING, "Failed to write docking arrangement to file: app-name = " + appName, e );
}
saveAppConfig( appName, "arrangement.xml", groupArr, dockingXmlClasses );
}

public static GroupArrangement loadDockingArrangement( String appName, URL fallback )
public static GroupArrangement loadDockingArrangement( String appName, URL fallbackUrl )
{
try
{
File arrFile = new File( createAppDir( appName ), "arrangement.xml" );
if ( arrFile.exists( ) )
{
return readArrangementXml( arrFile );
}
}
catch ( Exception e )
{
logger.log( WARNING, "Failed to load docking arrangement from file: app-name = " + appName, e );
}

if ( fallback != null )
{
InputStream fallbackStream = null;
try
{
fallbackStream = fallback.openStream( );
return readArrangementXml( fallbackStream );
}
catch ( Exception e )
{
logger.log( WARNING, "Failed to load default docking arrangement from resource: resource = " + fallback.toString( ), e );
}
finally
{
if ( fallbackStream != null )
{
try
{
fallbackStream.close( );
}
catch ( IOException e )
{
logger.log( WARNING, "Failed to close default docking arrangement resource: resource = " + fallback.toString( ), e );
}
}
}
}

return null;
return loadAppConfig( appName, "arrangement.xml", fallbackUrl, GroupArrangement.class, dockingXmlClasses );
}

public static <C extends Component> C findLargestComponent( Collection<C> components )
Expand Down

0 comments on commit f5821f6

Please sign in to comment.