diff --git a/src/main/java/de/mannheim/th/chess/controller/ButtonMovePieceListener.java b/src/main/java/de/mannheim/th/chess/controller/ButtonMovePieceListener.java new file mode 100644 index 0000000..402fad5 --- /dev/null +++ b/src/main/java/de/mannheim/th/chess/controller/ButtonMovePieceListener.java @@ -0,0 +1,39 @@ +package de.mannheim.th.chess.controller; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import com.github.bhlangonijr.chesslib.move.Move; + +import de.mannheim.th.chess.domain.Game; +import de.mannheim.th.chess.ui.SpielFrame; +import de.mannheim.th.chess.ui.SpielFrame.BoardMode; + +public class ButtonMovePieceListener implements ActionListener { + private SpielFrame sf; + private Game game; + private Move mv; + + public ButtonMovePieceListener(SpielFrame sf, Game game, Move mv) { + this.sf = sf; + this.game = game; + this.mv = mv; + } + + @Override + public void actionPerformed(ActionEvent e) { + this.game.playMove(this.mv); + if (this.game.isDraw()) { + this.game.stopClock(); + this.sf.setBoardMode(BoardMode.finished); + this.sf.showDraw(); + } else if (this.game.isMate()) { + this.game.stopClock(); + this.sf.setBoardMode(BoardMode.finished); + this.sf.showWin(game.getActivePlayer()); + } + this.sf.setBoardMode(BoardMode.normal); + this.sf.setCursor(null); + this.sf.erstelleBrett(); + } +} diff --git a/src/main/java/de/mannheim/th/chess/controller/ButtonSelectPieceListener.java b/src/main/java/de/mannheim/th/chess/controller/ButtonSelectPieceListener.java new file mode 100644 index 0000000..94fa58c --- /dev/null +++ b/src/main/java/de/mannheim/th/chess/controller/ButtonSelectPieceListener.java @@ -0,0 +1,43 @@ +package de.mannheim.th.chess.controller; + +import java.awt.Cursor; +import java.awt.Image; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import com.github.bhlangonijr.chesslib.Square; + +import de.mannheim.th.chess.ui.SpielFrame; + +public class ButtonSelectPieceListener implements ActionListener { + private SpielFrame sf; + private Square selectedSquare; + + public ButtonSelectPieceListener(SpielFrame sf, Square sq) { + this.sf = sf; + this.selectedSquare = sq; + } + + @Override + public void actionPerformed(ActionEvent e) { + sf.setBoardMode(SpielFrame.BoardMode.pieceSelected); + sf.setSelectedSquare(this.selectedSquare); + + String symbolChoosed = sf.getBelegung().get(e.getSource()); + + // setzt cursor auf spielfigur für die animation + String pfad = "src/main/resources/" + (int) symbolChoosed.toCharArray()[2] + ".png"; + + // Bild laden und Cursor im gesamten Frame setzen + Image image = Toolkit.getDefaultToolkit().getImage(pfad); + Image scaled = image.getScaledInstance(32, 32, Image.SCALE_SMOOTH); + Cursor figurCursor = Toolkit.getDefaultToolkit().createCustomCursor(scaled, new Point(0, 0), + "figurCursor"); + sf.setCursor(figurCursor); + + sf.erstelleBrett(); + } + +} diff --git a/src/main/java/de/mannheim/th/chess/controller/ButtonToNormalListener.java b/src/main/java/de/mannheim/th/chess/controller/ButtonToNormalListener.java new file mode 100644 index 0000000..c4261de --- /dev/null +++ b/src/main/java/de/mannheim/th/chess/controller/ButtonToNormalListener.java @@ -0,0 +1,24 @@ +package de.mannheim.th.chess.controller; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import de.mannheim.th.chess.ui.SpielFrame; +import de.mannheim.th.chess.ui.SpielFrame.BoardMode; + +public class ButtonToNormalListener implements ActionListener { + private SpielFrame sf; + + public ButtonToNormalListener(SpielFrame sf) { + this.sf = sf; + } + + @Override + public void actionPerformed(ActionEvent e) { + this.sf.setBoardMode(BoardMode.normal); + this.sf.setSelectedSquare(null); + this.sf.setCursor(null); + this.sf.erstelleBrett(); + } + +} diff --git a/src/main/java/de/mannheim/th/chess/ui/MainFrame.java b/src/main/java/de/mannheim/th/chess/ui/MainFrame.java index 96235ee..fc5a437 100644 --- a/src/main/java/de/mannheim/th/chess/ui/MainFrame.java +++ b/src/main/java/de/mannheim/th/chess/ui/MainFrame.java @@ -1,15 +1,11 @@ package de.mannheim.th.chess.ui; -import java.awt.EventQueue; - import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.border.EmptyBorder; -import javax.swing.JTextField; import javax.swing.JLabel; import javax.swing.JOptionPane; -import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; @@ -20,10 +16,8 @@ import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFileChooser; -import java.awt.BorderLayout; import java.awt.Component; import java.awt.Font; -import java.awt.Graphics; import java.awt.Color; public class MainFrame extends JFrame { @@ -33,22 +27,6 @@ public class MainFrame extends JFrame { private static final long serialVersionUID = 1L; private JPanel contentPane; - /** - * Launch the application. - */ - public static void main(String[] args) { - EventQueue.invokeLater(new Runnable() { - public void run() { - try { - MainFrame frame = new MainFrame(); - frame.setVisible(true); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - /** * Create the frame. */ diff --git a/src/main/java/de/mannheim/th/chess/ui/SpielFrame.java b/src/main/java/de/mannheim/th/chess/ui/SpielFrame.java index 6d11ac5..46563e8 100644 --- a/src/main/java/de/mannheim/th/chess/ui/SpielFrame.java +++ b/src/main/java/de/mannheim/th/chess/ui/SpielFrame.java @@ -5,11 +5,13 @@ import org.apache.logging.log4j.Logger; import com.github.bhlangonijr.chesslib.Piece; import com.github.bhlangonijr.chesslib.Square; -import com.github.bhlangonijr.chesslib.game.Player; import com.github.bhlangonijr.chesslib.move.Move; import de.mannheim.th.chess.App; import de.mannheim.th.chess.domain.Game; +import de.mannheim.th.chess.controller.ButtonMovePieceListener; +import de.mannheim.th.chess.controller.ButtonSelectPieceListener; +import de.mannheim.th.chess.controller.ButtonToNormalListener; import java.awt.EventQueue; import java.awt.Font; @@ -22,274 +24,238 @@ import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSplitPane; -import javax.swing.JTextArea; -import javax.swing.JTextField; import java.awt.BorderLayout; import java.awt.Color; -import java.awt.Cursor; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; import java.awt.GridLayout; -import java.awt.Image; -import java.awt.Point; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; public class SpielFrame extends JFrame { - private static final Logger logger = LogManager.getLogger(App.class); + private static final Logger logger = LogManager.getLogger(App.class); - private static final long serialVersionUID = 1L; - private JPanel contentPane; - private ArrayList buttons = new ArrayList<>(); - private HashMap belegungen = new HashMap<>(); - private JPanel panelLinks, panelRechts; - private Game game; - private String symbolChoosed; + private static final long serialVersionUID = 1L; + private ArrayList buttons = new ArrayList<>(); + private HashMap belegungen = new HashMap<>(); + private JPanel panelLinks, panelRechts, contentPane; + private Game game; - private BoardMode mode; - private Square selectedSquare; + private BoardMode mode; + private Square selectedSquare; - enum BoardMode { - normal, pieceSelected, finished - } + public enum BoardMode { + normal, pieceSelected, finished + } - /** - * Launch the application. Die Main-Methode für den WindowBuilder. - */ - public static void main(String[] args) { - EventQueue.invokeLater(new Runnable() { - public void run() { - try { - SpielFrame frame = new SpielFrame(); - frame.setVisible(true); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } + /** + * Create the frame. + */ + public SpielFrame() { - /** - * Create the frame. - */ - public SpielFrame() { + game = new Game(); + mode = BoardMode.normal; - game = new Game(); - mode = BoardMode.normal; + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setBounds(100, 100, 1920, 1080); + setTitle("Schach"); + setAlwaysOnTop(true); - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - setBounds(100, 100, 1920, 1080); - setTitle("Schach"); - setAlwaysOnTop(true); + contentPane = new JPanel(); + contentPane.setLayout(new BorderLayout()); + setContentPane(contentPane); - JPanel contentPane = new JPanel(); - contentPane.setLayout(new BorderLayout()); - setContentPane(contentPane); + // Linkes Panel mit GridLayout 8x8 für Schachbrett + panelLinks = new JPanel(new GridLayout(8, 8)); - // Linkes Panel mit GridLayout 8x8 für Schachbrett - panelLinks = new JPanel(new GridLayout(8, 8)); + erstelleBrett(); - erstelleBrett(); + // Rechtes Panel für Steuerung oder zusätzliche Eingaben + panelRechts = new JPanel(); + panelRechts.setBackground(Color.LIGHT_GRAY); - // Rechtes Panel für Steuerung oder zusätzliche Eingaben - panelRechts = new JPanel(); - panelRechts.setBackground(Color.LIGHT_GRAY); + // JSplitPane horizontal (linke und rechte Hälfte) + JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, panelLinks, panelRechts); + splitPane.setResizeWeight(0.70); + splitPane.setDividerSize(5); + splitPane.setEnabled(false); - // JSplitPane horizontal (linke und rechte Hälfte) - JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, panelLinks, panelRechts); - splitPane.setResizeWeight(0.70); - splitPane.setDividerSize(5); - splitPane.setEnabled(false); + contentPane.add(splitPane, BorderLayout.CENTER); + setVisible(true); + } - contentPane.add(splitPane, BorderLayout.CENTER); - setVisible(true); - } + public void setBoardMode(BoardMode bm) { + this.mode = bm; + } - /** - * Erstellt alle Buttons und fügt sie dem Frame hinzu. - */ - private void erstelleBrett() { + public void setSelectedSquare(Square sq) { + this.selectedSquare = sq; + } - this.clearButtons(); - this.setDefaultBackground(); - this.setButtonsActions(); + public HashMap getBelegung() { + return this.belegungen; + } - ladeBrett(); + /** + * Erstellt alle Buttons und fügt sie dem Frame hinzu. + */ + public void erstelleBrett() { - panelLinks.revalidate(); - panelLinks.repaint(); + this.clearButtons(); + this.setDefaultBackground(); + this.setButtonsActions(); - // // Bild laden und Cursor im gesamten Frame setzen - // Image image = Toolkit.getDefaultToolkit().getImage(pfad); - // Image scaled = image.getScaledInstance(32, 32, Image.SCALE_SMOOTH); - // Cursor figurCursor = Toolkit.getDefaultToolkit().createCustomCursor(scaled, - // new Point(0, 0), - // "figurCursor"); - // setCursor(figurCursor); + ladeBrett(); - // }else - // { - // - // // wenn gerade Figur ausgewählt wird... - // buttonChoosed = (JButton) e.getSource(); - // symbolChoosed = belegungen.get(buttonChoosed); - // // System.out.println(symbolChoosed+" wurde gewählt."); - // // setzt cursor auf spielfigur für die animation - // String pfad = "src/main/resources/" + (int) symbolChoosed.toCharArray()[2] + - // ".png"; - // - // // Bild laden und Cursor im gesamten Frame setzen - // Image image = Toolkit.getDefaultToolkit().getImage(pfad); - // Image scaled = image.getScaledInstance(32, 32, Image.SCALE_SMOOTH); - // Cursor figurCursor = Toolkit.getDefaultToolkit().createCustomCursor(scaled, - // new Point(0, 0), - // "figurCursor"); - // setCursor(figurCursor); - } + panelLinks.revalidate(); + panelLinks.repaint(); - private int mirrowedGrid(int i) { - return 63 - (((i / 8) * 8) + (7 - i % 8)); - } + // // Bild laden und Cursor im gesamten Frame setzen + // Image image = Toolkit.getDefaultToolkit().getImage(pfad); + // Image scaled = image.getScaledInstance(32, 32, Image.SCALE_SMOOTH); + // Cursor figurCursor = Toolkit.getDefaultToolkit().createCustomCursor(scaled, + // new Point(0, 0), + // "figurCursor"); + // setCursor(figurCursor); - /** - * holt sich FEN-Zeichenkette und extrahiert daraus die Positionen der Figuren - */ - private void ladeBrett() { - // System.out.println(game.toFEN()); + // }else + // { + // + // // wenn gerade Figur ausgewählt wird... + // buttonChoosed = (JButton) e.getSource(); + // symbolChoosed = belegungen.get(buttonChoosed); + // // System.out.println(symbolChoosed+" wurde gewählt."); + // // setzt cursor auf spielfigur für die animation + // String pfad = "src/main/resources/" + (int) symbolChoosed.toCharArray()[2] + + // ".png"; + // + // // Bild laden und Cursor im gesamten Frame setzen + // Image image = Toolkit.getDefaultToolkit().getImage(pfad); + // Image scaled = image.getScaledInstance(32, 32, Image.SCALE_SMOOTH); + // Cursor figurCursor = Toolkit.getDefaultToolkit().createCustomCursor(scaled, + // new Point(0, 0), + // "figurCursor"); + // setCursor(figurCursor); + } - char[] fen = game.toFEN().replaceAll("/", "").split(" ")[0].toCharArray(); - int i = 0; - for (int j = 0; j < fen.length; j++) { - if (Character.isDigit(fen[j])) { - int leerfelder = Character.getNumericValue(fen[j]); - for (int k = 0; k < leerfelder; k++) { - belegungen.put(buttons.get(i), "n-n"); - // buttons.get(i).setEnabled(false); // erstmal deaktivieren, weil leere Felder - // nicht ckickbar sein sollten. - i++; - } - continue; - } else if (fen[j] >= 65 && fen[j] <= 90) { // ein Großbuchstabe, also - belegungen.put(buttons.get(i), "w-" + fen[j]); - } else if (fen[j] >= 97 && fen[j] <= 122) { // ein Kleinbuchstabe, also - belegungen.put(buttons.get(i), "b-" + fen[j]); - // buttons.get(i).setEnabled(false); // erstmal deaktivieren, damit weiß - // beginnen kann - } - buttons.get(i).setIcon(new ImageIcon("src/main/resources/" + (int) fen[j] + ".png")); - buttons.get(i).setDisabledIcon(new ImageIcon("src/main/resources/" + (int) fen[j] + ".png")); + private int mirrowedGrid(int i) { + return 63 - (((i / 8) * 8) + (7 - i % 8)); + } - i++; + /** + * holt sich FEN-Zeichenkette und extrahiert daraus die Positionen der Figuren + */ + private void ladeBrett() { + // System.out.println(game.toFEN()); - } - } + char[] fen = game.toFEN().replaceAll("/", "").split(" ")[0].toCharArray(); + int i = 0; + for (int j = 0; j < fen.length; j++) { + if (Character.isDigit(fen[j])) { + int leerfelder = Character.getNumericValue(fen[j]); + for (int k = 0; k < leerfelder; k++) { + belegungen.put(buttons.get(i), "n-n"); + // buttons.get(i).setEnabled(false); // erstmal deaktivieren, weil leere Felder + // nicht ckickbar sein sollten. + i++; + } + continue; + } else if (fen[j] >= 65 && fen[j] <= 90) { // ein Großbuchstabe, also + belegungen.put(buttons.get(i), "w-" + fen[j]); + } else if (fen[j] >= 97 && fen[j] <= 122) { // ein Kleinbuchstabe, also + belegungen.put(buttons.get(i), "b-" + fen[j]); + // buttons.get(i).setEnabled(false); // erstmal deaktivieren, damit weiß + // beginnen kann + } + buttons.get(i).setIcon(new ImageIcon("src/main/resources/" + (int) fen[j] + ".png")); + buttons.get(i).setDisabledIcon(new ImageIcon("src/main/resources/" + (int) fen[j] + ".png")); - /** - * Clears the existing buttons from the button list, panellinks and fills them - * with new blank ones. - */ - private void clearButtons() { - buttons.clear(); - panelLinks.removeAll(); + i++; - for (int i = 0; i < 64; i++) { - JButton b = new JButton(); + } + } - b.setEnabled(false); + /** + * Clears the existing buttons from the button list, panellinks and fills them + * with new blank ones. + */ + private void clearButtons() { + buttons.clear(); + panelLinks.removeAll(); - // style - b.setFocusPainted(false); - b.setFont(new Font("Arial", Font.PLAIN, 30)); - b.setForeground(Color.WHITE); - b.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1)); - b.setName(i + ""); + for (int i = 0; i < 64; i++) { + JButton b = new JButton(); - buttons.add(b); - } - } + b.setEnabled(false); - /** - * Sets the default background color for the buttons in the grid. - */ - private void setDefaultBackground() { - for (int i = 0; i < 64; i++) { - JButton b = buttons.get(i); - if ((i / 8 + i % 8) % 2 == 0) { - logger.info("Helles Feld erstellt." + i); - b.setBackground(new Color(90, 90, 90)); - } else { - logger.info("Dunkles Feld erstellt." + i); - b.setBackground(new Color(65, 65, 65)); - } - } - } - - + // style + b.setFocusPainted(false); + b.setFont(new Font("Arial", Font.PLAIN, 30)); + b.setForeground(Color.WHITE); + b.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1)); + b.setName(i + ""); - /* - * Switches the button actions depending on the boardmode - */ - private void setButtonsActions() { + buttons.add(b); + } + } - List selectables; + /** + * Sets the default background color for the buttons in the grid. + */ + private void setDefaultBackground() { + for (int i = 0; i < 64; i++) { + JButton b = buttons.get(i); + if ((i / 8 + i % 8) % 2 == 0) { + logger.info("Helles Feld erstellt." + i); + b.setBackground(new Color(90, 90, 90)); + } else { + logger.info("Dunkles Feld erstellt." + i); + b.setBackground(new Color(65, 65, 65)); + } + } + } - switch (this.mode) { - case BoardMode.normal: - selectables = game.getAllLegalMoveableSquares(); + /* + * Switches the button actions depending on the boardmode + */ + private void setButtonsActions() { - for (Square square : selectables) { - final Square currentSquare = square; // ActionListener need it to be final - JButton b = buttons.get(mirrowedGrid(square.ordinal())); - b.setEnabled(true); - // b.setBackground(Color.green); - b.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mode = BoardMode.pieceSelected; - selectedSquare = currentSquare; + List selectables; - symbolChoosed = belegungen.get(b); + switch (this.mode) { + case BoardMode.normal: + selectables = game.getAllLegalMoveableSquares(); - // setzt cursor auf spielfigur für die animation - String pfad = "src/main/resources/" + (int) symbolChoosed.toCharArray()[2] + ".png"; + for (Square square : selectables) { + JButton b = buttons.get(mirrowedGrid(square.ordinal())); + b.setEnabled(true); + // b.setBackground(Color.green); + b.addActionListener(new ButtonSelectPieceListener(this, square)); + } - // Bild laden und Cursor im gesamten Frame setzen - Image image = Toolkit.getDefaultToolkit().getImage(pfad); - Image scaled = image.getScaledInstance(32, 32, Image.SCALE_SMOOTH); - Cursor figurCursor = Toolkit.getDefaultToolkit().createCustomCursor(scaled, new Point(0, 0), - "figurCursor"); - setCursor(figurCursor); + break; - erstelleBrett(); - } - }); - } + case BoardMode.pieceSelected: - break; + JButton s = buttons.get(mirrowedGrid(selectedSquare.ordinal())); + s.setEnabled(true); + s.setBackground(new Color(165, 42, 42)); + s.addActionListener(new ButtonToNormalListener(this)); // cancel action - case BoardMode.pieceSelected: + selectables = game.getLegalMoveableSquares(selectedSquare); - JButton s = buttons.get(mirrowedGrid(selectedSquare.ordinal())); - s.setEnabled(true); - s.setBackground(new Color(165, 42, 42)); - s.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mode = BoardMode.normal; - selectedSquare = null; - setCursor(null); - erstelleBrett(); - } - }); // cancel action + for ( - selectables = game.getLegalMoveableSquares(selectedSquare); + Square square : selectables) { + JButton b = buttons.get(mirrowedGrid(square.ordinal())); + final Move move = new Move(selectedSquare, square); + b.setEnabled(true); + b.setBackground(new Color(230, 100, 100)); + b.addActionListener(new ButtonMovePieceListener(this, this.game, move)); + } +<<<<<<< HEAD for (Square square : selectables) { JButton b = buttons.get(mirrowedGrid(square.ordinal())); final Move move = new Move(selectedSquare, square); @@ -322,42 +288,30 @@ public class SpielFrame extends JFrame { } }); } +======= + break; +>>>>>>> branch 'buttonActions' of https://gitty.informatik.hs-mannheim.de/3020772/Schach.git - break; - - case finished: - clearButtons(); - break; - default: - break; + case finished: + clearButtons(); + break; + default: + break; - } + } - for (JButton b : buttons) { - panelLinks.add(b); - } - } - - private void showDraw() { - JFrame frame = new JFrame("Result"); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.setSize(300, 150); - frame.setLayout(null); + for (JButton b : buttons) { + panelLinks.add(b); + } + } - JLabel jl = new JLabel("1/2 - 1/2"); - jl.setBounds(50, 30, 200, 25); - jl.setFont(new Font("Tahoma", Font.BOLD, 20)); - frame.add(jl); - frame.setVisible(true); - - } - - private void showWin(int player) { - JFrame frame = new JFrame("Result"); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.setSize(300, 150); - frame.setLayout(null); + public void showDraw() { + JFrame frame = new JFrame("Result"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(300, 150); + frame.setLayout(null); +<<<<<<< HEAD JLabel jl = new JLabel(String.format("%d - %d", player / 2, player % 2)); jl.setBounds(50, 30, 200, 25); jl.setFont(new Font("Tahoma", Font.BOLD, 20)); @@ -395,5 +349,27 @@ public class SpielFrame extends JFrame { return result[0]; } +======= + JLabel jl = new JLabel("1/2 - 1/2"); + jl.setBounds(50, 30, 200, 25); + jl.setFont(new Font("Tahoma", Font.BOLD, 20)); + frame.add(jl); + frame.setVisible(true); + + } + + public void showWin(int player) { + JFrame frame = new JFrame("Result"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(300, 150); + frame.setLayout(null); + + JLabel jl = new JLabel(String.format("%d - %d", player / 2, player % 2)); + jl.setBounds(50, 30, 200, 25); + jl.setFont(new Font("Tahoma", Font.BOLD, 20)); + frame.add(jl); + frame.setVisible(true); + } +>>>>>>> branch 'buttonActions' of https://gitty.informatik.hs-mannheim.de/3020772/Schach.git } diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml new file mode 100644 index 0000000..602b5ab --- /dev/null +++ b/src/main/resources/log4j2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file