package hexenschach.gui;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.DisplayMode;
import java.awt.FlowLayout;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.BorderLayout;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.ArrayList;
import hexenschach.gameplay.Gameplay;
import hexenschach.gameplay.Persistence;
import hexenschach.figures.Figure;
import hexenschach.gui.resources.Images;
import hexenschach.gui.event.GameEvent;
import hexenschach.gui.event.GameListener;
import hexenschach.gui.event.GameEventType;
/**
* GUI ist die zentrale Klasse der grafischen Oberflaeche.
* Sie setzt das GUI komplett zusammen und zeigt es an.
* Dazu genuegt es, ein Objekt von GUI zu erzeugen.
* Alle Methoden, die von anderen Klassen auf dem GUI aufgerufen
* werden sollen, sind im Interface UserInterface spezifiziert.
* @author Tobias Marquardt
*/
public class GUI extends JFrame implements UserInterfaceAPI, GUIContainer {
private static final long serialVersionUID = -2060188921456846015L;
// Panels:
private MainMenu mainMenu;
private BoardPanel board;
private GamePanel gamePanel;
private MainBackground background;
private Gameplay gameplay;
private Image icon; // Icon für Task- und Menüleiste
private Persistence persistence;
// GUI muss die Direkthilfe kennen, um setDirectHelp() weiterleiten zu können.
private DirectHelp directHelp;
static final int xSize = 1008;
static final int ySize = 689;
// Position
private int xPosition;
private int yPosition;
//Liste für alle Listener, die auf ein GameEvent im GUI lauschen
private ArrayList <GameListener> listener = new ArrayList <GameListener>();
/**
* Erzeugt ein neues GUI und stellt es dar.
* @param p Persistenz-Objekt mit dessen Hilfe Spielstände
* geladen und gespeichert werden sollen.
*/
public GUI(Persistence p) {
persistence = p;
initLayout();
initComponents();
initContainerProperties();
initListener();
}
public void initLayout() {
setLayout(new BorderLayout());
}
public void initComponents() {
/*
* Es werden vier JPanels erzeugt.
* Das Hintergrundpanel wird direkt in den JFrame gelegt.
* Die 3 Hauptpanel der Oberfläche werden auf den Hintergrund gelegt.
*/
//Panels erzeugen
mainMenu = new MainMenu(this);
board = new BoardPanel();
gamePanel = new GamePanel(this);
background = new MainBackground(Images.getMainBackground(), Images.getLabeledMainBackground(), xSize, ySize);
// Background besitzt standardmäßig ein Null-Layout. Dieses wird hier auf ein BorderLayout geändert.
BorderLayout backgroundLayout = new BorderLayout();
backgroundLayout.setVgap(0);
backgroundLayout.setHgap(0);
background.setLayout(backgroundLayout);
// Panels hinzufügen
add(background, BorderLayout.CENTER);
background.add(mainMenu, BorderLayout.NORTH);
JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 50));
panel.setOpaque(false);
panel.add(board);
background.add(panel, BorderLayout.CENTER);
background.add(gamePanel, BorderLayout.SOUTH);
}
public void initContainerProperties() {
// In der X-Achse zentriert, an den oberen Rand des Bildschirm setzen
xPosition = calculateXPosition();
setBounds(xPosition, 0, xSize, ySize);
icon = Images.getMainIcon();
setIconImage(icon);
setTitle(Dictionary.GUITitle);
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
setResizable(false);
pack();
}
/**
* Meldet ein Objekt als Listener für alle GameEvents an.
* @param gl GameListener
*/
public void addGameListener(GameListener gl) {
if(! listener.contains(gl))
listener.add(gl);
}
/**
* Zeigt das GUI an.
*/
public void start() {
setVisible(true);
}
/**
* Weißt dem GUI ein neues Gameplay zu,
* auf dem alle Aktionen ausgeführt werden sollen.
* Kann ein komplett neu erzeugtes Gameplay-Objekt sein
* oder ein in Persistence gespeichertes.
* @param gameplay
*/
public void setGameplay(Gameplay gameplay) {
this.gameplay = gameplay;
board.setGameplay(gameplay);
// Event erzeugen
GameEvent event = new GameEvent(this, GameEventType.GAME_STARTED);
gameEventHappened(event);
}
/**
* Da GUI die Klasse ist, an der alle GameEventListener lauschen,
* muss GUI die Listener bei einem Event benachrichtigen.
* Dies geschieht hier zentral in dieser Methode, in der je nach Art des GameEvents
* eine andere Methode auf den Listenern aufgerufen wird.
* Die Methode hat Default Sichtbarkeit, da auch andere Klassen GameEvents erzeugen können,
* diese aber keine Listener haben.
* In diesem Fall kann das GameEvent hier übergeben werden, um somit die Listener zu benachrichtigen,
* so als wäre das Event in GUI selbst passiert.
* @param event
*/
void gameEventHappened(GameEvent event) {
// Alle Listener benachrichtigen
switch (event.type) {
case GAME_STARTED:
for(GameListener gl:listener)
gl.gameStarted(event);
break;
case GAME_SAVED:
for(GameListener gl:listener)
gl.gameSaved(event);
break;
case GAME_FINISHED:
for(GameListener gl:listener)
gl.gameFinished(event);
break;
}
}
/**
* Liefert das aktuelle Gameplay zurück.
* @return Gameplay-Objekt
*/
public Gameplay getGameplay() {
return gameplay;
}
/**
* Liefert das Persistence-Objekt zurück,
* mit dem Spielstände gespeichert und geladen werden können.
* @return Persitence-Objekt
*/
public Persistence getPersistence() {
return persistence;
}
/**
* Berechnet die optimale x-Position des Fensters,
* um dieses zu zentrieren.
* Abhängig von der Größe und der Bildschirmauflösung
* @return x-Koordinate in Pixeln
*/
private int calculateXPosition() {
GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice device = environment.getDefaultScreenDevice();
DisplayMode display = device.getDisplayMode();
int x = display.getWidth();
return (x - xSize)/2;
}
/**
* Teilt dem GUI das DirectHelp-Objekt mit,
* in welchem es Texte setzen soll.
* Wichtig: Sobald ein DirectHelp erzeugt wurde, von diesem aufrufen und
* setzen!
* @param dh
*/
void setDirectHelpReference(DirectHelp dh) {
directHelp = dh;
}
public boolean moveFigure(String from, String to) {
return board.moveFigure(from, to);
}
public void resetHighlight() {
board.resetHighlight();
}
public boolean setFigure(String field, Figure figure) {
System.out.println(field);
return board.setFigure(field, figure);
}
public void setDirectHelp(String text) {
directHelp.setText(text);
}
public boolean highlightField(String field, FieldHighlightStatus status) {
return board.highlightField(field, status);
}
private void initListener() {
//WindowListener hinzufügen, um darauf reagieren zu können,
//wenn der Nutzer das Fenster versucht zu schließen.
this.addWindowListener(new WindowListener() {
@Override
public void windowActivated(WindowEvent e) {}
@Override
public void windowClosed(WindowEvent e) {}
@Override
public void windowClosing(WindowEvent e) {
closingWindow();
}
@Override
public void windowDeactivated(WindowEvent e) {}
@Override
public void windowDeiconified(WindowEvent e) {}
@Override
public void windowIconified(WindowEvent e) {}
@Override
public void windowOpened(WindowEvent e) {}
});
}
/**
* Zeigt eine Sicherheitsabfrage an, die fragt,
* ob das Spiel wirklich beendet werden soll.
* Je nach Wahl des Nutzers, wird das Programm beendet.
*/
private void closingWindow() {
QuestionDialog closeDialog = new QuestionDialog(this, Dictionary.GUIClosingQuestion) {
private static final long serialVersionUID = -1610884025472315308L;
@Override
public void yesButtonClicked() {
System.exit(0);
}
@Override
public void noButtonClicked() {
setVisible(false);
}
@Override
public void cancelButtonClicked() {
setVisible(false);
}
};
closeDialog.setVisible(true);
}
/**
* Sagt dem Hintergrund, ob die Beschriftung angezeigt werden soll,
* oder nicht.
* @param labeled
*/
void setLabeledBackground(boolean labeled) {
background.setLabeled(labeled);
}
public void gameFinished() {
gameEventHappened(new GameEvent(this, GameEventType.GAME_FINISHED));
}
}