package org.gbcpainter.main;
import net.jcip.annotations.GuardedBy;
import org.gbcpainter.env.GameSettings;
import org.gbcpainter.env.GraphicsEnv;
import org.gbcpainter.env.LanguageDictionary;
import org.gbcpainter.loaders.textures.TextureNotFoundException;
import org.gbcpainter.loaders.textures.ResolutionTextureLoader;
import org.gbcpainter.view.ErrorDialog;
import org.gbcpainter.view.ExitListener;
import org.gbcpainter.view.PCViewHolder;
import org.gbcpainter.view.ViewManager;
import org.gbcpainter.view.menu.MainMenu;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.*;
/**
* The entry point for the PC application
* <p/>
* It initializes the graphics environment, game settings and creates the main view with the main menu
*
* @author Lorenzo Pellegrini
*/
public class MainPC extends WindowAdapter implements ExitListener, PCViewHolder {
/* Global logger data */
@NonNls
private static final String LOGGING_PACKAGE_SUFFIX_SUPPRESSION = "org.gbcpainter.";
@NonNls
private static final String PAINTER_LOG_FILE = "PainterLog.log";
@NotNull
private static FileHandler fileTxt;
@NotNull
private static Formatter formatterTxt;
//private static Logger LOGGER;
/* Window data */
private static final double PERCENTAGE_HEADER_HEIGHT = 0.25;
private static final int SIZE_REFRESH_MILLIS = 500;
private static final int DEFAULT_WINDOW_SIZE = 500;
@NonNls
private static final String WINDOW_TITLE = "Painter";
@NonNls
private static final String HEADER_TEXTURE = "Header";
@NotNull
private final Frame gameView;
private int previousClientHeight = DEFAULT_WINDOW_SIZE;
@NotNull
private final Component header;
/* Start ViewHolder data */
@NotNull
private final Set<ExitListener> listeners = new HashSet<>( 1 );
/*
The Frame, when in 'menu view', keeps an 'holder' panel that contains:
-the header in its upper part
-the view component in its lower part.
If the Frame is in 'full view', the holder is detached and the view component is attached
directly to the frame
*/
@NotNull
private final Panel holder;
@NotNull
private Component currentComponent;
/**
* True if the frame is in 'full view' mode, false if in 'menu view'
*/
private boolean fullView;
/* End ViewHolder data */
public MainPC() {
this.addListener( this );
ViewManager.setMainView( this );
this.gameView = new Frame( WINDOW_TITLE );
this.gameView.setLayout( null );
this.gameView.setSize( DEFAULT_WINDOW_SIZE, DEFAULT_WINDOW_SIZE );
this.gameView.setLocationRelativeTo( null );
GraphicsEnv.getInstance().setWindowSize( this.gameView.getWidth(), this.gameView.getHeight() );
GraphicsEnv.getInstance().setGraphicsConfiguration( this.gameView.getGraphicsConfiguration() );
GraphicsEnv.getInstance().setTextureLoader( new ResolutionTextureLoader() );
this.fullView = false;
this.currentComponent = new MainMenu();
try {
GraphicsEnv.getInstance().getTextureLoader().loadTexture( HEADER_TEXTURE, false );
} catch ( Exception e ) {
final TextureNotFoundException textureException = new TextureNotFoundException( e );
new ErrorDialog( this.gameView, LanguageDictionary.ERROR_STRING, LanguageDictionary.ERROR_LOAD_HEADER, textureException, true );
throw textureException;
}
this.header = new Label() {
@Override
public void paint( Graphics g ) {
super.paint( g );
if ( this.getHeight() != 0 && this.getWidth() != 0 ) {
try {
Image img = GraphicsEnv.getInstance().getTextureLoader().loadTexture( HEADER_TEXTURE, false );
g.drawImage( img.getScaledInstance( - 1, this.getHeight(), Image.SCALE_SMOOTH ),
0,
0,
this.getWidth(),
this.getHeight(),
null );
} catch ( Exception e ) {
final TextureNotFoundException textureException = new TextureNotFoundException( e );
new ErrorDialog( MainPC.this.gameView, LanguageDictionary.ERROR_STRING,
LanguageDictionary.ERROR_LOAD_HEADER, textureException, true );
}
}
}
};
this.holder = new Panel();
this.holder.setLayout( null );
this.holder.add( this.header );
this.holder.add( this.currentComponent );
this.gameView.add( this.holder );
this.gameView.setBackground( Color.BLACK );
this.gameView.setFont( GraphicsEnv.getInstance().getBigElementsFont() );
this.gameView.addWindowListener( this );
this.gameView.setVisible( true );
this.currentComponent.setFocusable( true );
new Thread( new Runnable() {
@Override
public void run() {
while(MainPC.this.gameView.isVisible()) {
try {
EventQueue.invokeAndWait( new Runnable() {
@Override
public void run() {
MainPC.this.adjustViewSize();
}
} );
} catch ( Exception ignored ) { }
try {
Thread.sleep( SIZE_REFRESH_MILLIS );
} catch ( InterruptedException ignored ) {}
}
}
} ).start();
}
public static void main( String[] args ) {
/*try {
setupLogger();
} catch ( IOException e ) {
e.printStackTrace();
System.exit( 1 );
}*/
try {
GameSettings.loadInstance();
//http://stackoverflow.com/questions/10867035/java-mac-os-x-application-name-correction
//if (System.getProperty("os.name").contains("Mac")) {
System.setProperty("com.apple.mrj.application.apple.menu.about.name",WINDOW_TITLE);
//}
if ( GameSettings.getInstance().getInstanceValue( GameSettings.INTEGER_SETTINGS_TYPE.OPENGL ) != 0 ) {
System.setProperty( "sun.java2d.opengl", "true" );
//System.setProperty( "sun.java2d.transaccel", "true" );
//System.setProperty( "sun.java2d.ddforcevram", "True" );
}
if ( GameSettings.getInstance().getInstanceValue( GameSettings.INTEGER_SETTINGS_TYPE.ANTIALIAS ) != 0 ) {
System.setProperty( "awt.useSystemAAFontSettings", "on" );
}
new MainPC();
} catch ( Throwable e ) {
StringBuilder builder = new StringBuilder();
builder.append( LanguageDictionary.EXCEPTION_AT_INITIALIZATION );
builder.append( System.lineSeparator() );
Throwable cause = e;
while ( cause != null ) {
builder.append( cause.getMessage() );
builder.append( System.lineSeparator() );
for (StackTraceElement stackTraceElement : cause.getStackTrace()) {
builder.append( stackTraceElement );
builder.append( System.lineSeparator() );
}
cause = cause.getCause();
if ( cause != null ) {
builder.append( "-----------------------------------------" );
builder.append( System.lineSeparator() );
}
}
System.exit( 1 );
}
}
//http://www.vogella.com/tutorials/Logging/article.html
public static void setupLogger() throws IOException {
// Get the global logger to configure it
Logger logger = Logger.getGlobal();//Logger(Logger.GLOBAL_LOGGER_NAME);
logger.setLevel( Level.ALL );
fileTxt = new FileHandler( PAINTER_LOG_FILE, false );
// create txt Formatter
formatterTxt = new Formatter() {
@Override
@NonNls
public String format( LogRecord record ) {
String className = record.getSourceClassName();
if ( className.substring( 0, LOGGING_PACKAGE_SUFFIX_SUPPRESSION.length() ).equals( LOGGING_PACKAGE_SUFFIX_SUPPRESSION ) ) {
className = className.substring( LOGGING_PACKAGE_SUFFIX_SUPPRESSION.length() );
}
return new StringBuilder()
.append( record.getLevel().getName() )
.append( "(" )
.append( className )
.append( "):" )
.append( record.getMessage() )
.append( System.lineSeparator() )
.toString();
}
};
fileTxt.setFormatter( formatterTxt );
logger.addHandler( fileTxt );
}
@Override
public void exitTriggered( @Nullable final Object source ) {
EventQueue.invokeLater( new Runnable() {
@Override
public void run() {
MainPC.this.gameView.dispose();
GraphicsEnv.getInstance().getTextureLoader().clear();
}
} );
}
@Override
public void windowClosing( final WindowEvent e ) {
this.triggerExit( this );
}
@Override
public synchronized void swap( @NotNull final Component newComponent ) {
if ( this.isFullView() ) {
this.swapToFullView( newComponent );
} else {
this.swapToMenuView( newComponent );
}
}
@Override
public synchronized void swapToFullView( @NotNull final Component newComponent ) {
final Component previousComponent = this.getCurrentComponent();
this.currentComponent = newComponent;
if ( ! this.isFullView() ) {
this.holder.remove( previousComponent );
this.gameView.remove( this.holder );
this.fullView = true;
} else {
this.gameView.remove( previousComponent );
}
this.gameView.add( this.currentComponent );
this.currentComponent.setFocusable( true );
this.currentComponent.setVisible( true );
this.adaptComponentsSizes();
}
@Override
public synchronized void swapToMenuView( @NotNull final Component newComponent ) {
final Component previousComponent = this.getCurrentComponent();
this.currentComponent = newComponent;
this.currentComponent.setVisible( false );
if ( this.isFullView() ) {
this.fullView = false;
this.gameView.remove( previousComponent );
this.holder.add( this.currentComponent );
this.gameView.add( this.holder );
} else {
this.holder.remove( previousComponent );
this.holder.add( this.currentComponent );
}
this.currentComponent.setFocusable( true );
this.currentComponent.setVisible( true );
this.adaptComponentsSizes();
}
@NotNull
@Override
public synchronized Component getCurrentComponent() {
return this.currentComponent;
}
@Override
public synchronized void addListener( @NotNull ExitListener listener ) {
this.listeners.add( listener );
}
@Override
public synchronized boolean removeListener( @NotNull ExitListener listener ) {
return this.listeners.remove( listener );
}
@Override
public synchronized void triggerExit( @Nullable Object source ) {
for (ExitListener listener : this.listeners) {
listener.exitTriggered( source );
}
}
@Override
public synchronized void switchToFullView( final boolean fullView ) {
this.fullView = fullView;
}
@Override
public synchronized boolean isFullView() {
return this.fullView;
}
private void adjustViewSize( ) {
final Insets frameInsets = MainPC.this.gameView.getInsets();
final Dimension realDimension = MainPC.this.gameView.getSize();
int viewHeight = Math.max( 1, realDimension.height - ( frameInsets.bottom + frameInsets.top ));
int viewWidth = Math.max( 1, realDimension.width - ( frameInsets.left + frameInsets.right ));
Rectangle b = MainPC.this.gameView.getBounds();
if( viewHeight != viewWidth ) {
/* height and width mismatch, select the highest and make the client a square with that size */
viewHeight = Math.max( viewWidth, viewHeight );
final Rectangle maxSize = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
/* Height and width must not exceed screen bounds */
viewHeight = Math.min( viewHeight + frameInsets.top + frameInsets.bottom, maxSize.height )
- (frameInsets.top + frameInsets.bottom);
viewHeight = Math.min( viewHeight + frameInsets.left + frameInsets.right, maxSize.width )
- (frameInsets.left + frameInsets.right);
viewWidth = viewHeight;
MainPC.this.gameView.setBounds( b.x, b.y,
viewWidth + frameInsets.left + frameInsets.right,
viewHeight + frameInsets.top + frameInsets.bottom );
}
if( viewHeight != MainPC.this.previousClientHeight) {
GraphicsEnv.getInstance().setWindowSize( viewWidth, viewHeight );
GraphicsEnv.getInstance().getTextureLoader().reloadTextures();
MainPC.this.adaptComponentsSizes();
this.previousClientHeight = viewHeight;
}
}
@GuardedBy( "this" )
private void adaptComponentsSizes() {
final Insets frameInsets = this.gameView.getInsets();
final Dimension realDimension = this.gameView.getSize();
final int viewHeight = realDimension.height - ( frameInsets.bottom + frameInsets.top );
final int viewWidth = realDimension.width - ( frameInsets.left + frameInsets.right );
realDimension.width = viewWidth;
realDimension.height = viewHeight;
if ( this.isFullView() ) {
final Dimension componentDimension = new Dimension( viewWidth, viewHeight );
this.currentComponent.setPreferredSize( componentDimension );
this.currentComponent.setBounds( frameInsets.left, frameInsets.top, componentDimension.width, componentDimension.height );
} else {
final int headerHeight = (int) ( viewHeight * PERCENTAGE_HEADER_HEIGHT );
final int componentHeight = viewHeight - headerHeight;
final Dimension headerDimension = new Dimension( viewWidth, headerHeight );
final Dimension componentDimension = new Dimension( viewWidth, componentHeight );
this.holder.setPreferredSize( realDimension );
this.holder.setMaximumSize( realDimension );
this.holder.setBounds( frameInsets.left, frameInsets.top, realDimension.width, realDimension.height );
this.header.setPreferredSize( headerDimension );
this.header.setBounds( 0, 0, headerDimension.width, headerDimension.height );
this.currentComponent.setPreferredSize( componentDimension );
this.currentComponent.setBounds( 0, headerHeight, componentDimension.width, componentDimension.height );
}
this.gameView.revalidate();
this.gameView.repaint();
}
@NotNull
@Override
public Frame getFrame() {
return this.gameView;
}
}