diff --git a/.gitignore b/.gitignore
index 2c6eb3893..5ff6309b7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,38 @@
-/.metadata
-/robots/.settings
-/robots/bin
-eclipse.bat
\ No newline at end of file
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 000000000..26d33521a
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/checkstyle-idea.xml b/.idea/checkstyle-idea.xml
new file mode 100644
index 000000000..c5dcbb130
--- /dev/null
+++ b/.idea/checkstyle-idea.xml
@@ -0,0 +1,16 @@
+
+
+
+ 10.13.0
+ JavaOnly
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 000000000..aa00ffab7
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 000000000..2a60c9b9c
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 000000000..2b63946d5
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 000000000..35eb1ddfb
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
deleted file mode 100644
index d79735aee..000000000
--- a/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# Robots
-The project to learn OO design concepts and MDI application development in Java
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 000000000..866f4f846
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,39 @@
+
+
+ 4.0.0
+
+ org.example
+ Robots
+ 1.0-SNAPSHOT
+
+
+ 20
+ 20
+ UTF-8
+
+
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+ junit
+ junit
+ 4.13.2
+ compile
+
+
+ org.junit.jupiter
+ junit-jupiter
+ RELEASE
+ compile
+
+
+
+
+
\ No newline at end of file
diff --git a/robots/.classpath b/robots/.classpath
deleted file mode 100644
index fceb4801b..000000000
--- a/robots/.classpath
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/robots/.gitignore b/robots/.gitignore
deleted file mode 100644
index 2757ffa76..000000000
--- a/robots/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/bin/
-/.settings/
diff --git a/robots/.project b/robots/.project
deleted file mode 100644
index 78e165663..000000000
--- a/robots/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
- Robots
-
-
-
-
-
- org.eclipse.jdt.core.javabuilder
-
-
-
-
-
- org.eclipse.jdt.core.javanature
-
-
diff --git a/robots/src/gui/MainApplicationFrame.java b/robots/src/gui/MainApplicationFrame.java
deleted file mode 100644
index 62e943ee1..000000000
--- a/robots/src/gui/MainApplicationFrame.java
+++ /dev/null
@@ -1,156 +0,0 @@
-package gui;
-
-import java.awt.Dimension;
-import java.awt.Toolkit;
-import java.awt.event.KeyEvent;
-
-import javax.swing.JDesktopPane;
-import javax.swing.JFrame;
-import javax.swing.JInternalFrame;
-import javax.swing.JMenu;
-import javax.swing.JMenuBar;
-import javax.swing.JMenuItem;
-import javax.swing.SwingUtilities;
-import javax.swing.UIManager;
-import javax.swing.UnsupportedLookAndFeelException;
-
-import log.Logger;
-
-/**
- * Что требуется сделать:
- * 1. Метод создания меню перегружен функционалом и трудно читается.
- * Следует разделить его на серию более простых методов (или вообще выделить отдельный класс).
- *
- */
-public class MainApplicationFrame extends JFrame
-{
- private final JDesktopPane desktopPane = new JDesktopPane();
-
- public MainApplicationFrame() {
- //Make the big window be indented 50 pixels from each edge
- //of the screen.
- int inset = 50;
- Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
- setBounds(inset, inset,
- screenSize.width - inset*2,
- screenSize.height - inset*2);
-
- setContentPane(desktopPane);
-
-
- LogWindow logWindow = createLogWindow();
- addWindow(logWindow);
-
- GameWindow gameWindow = new GameWindow();
- gameWindow.setSize(400, 400);
- addWindow(gameWindow);
-
- setJMenuBar(generateMenuBar());
- setDefaultCloseOperation(EXIT_ON_CLOSE);
- }
-
- protected LogWindow createLogWindow()
- {
- LogWindow logWindow = new LogWindow(Logger.getDefaultLogSource());
- logWindow.setLocation(10,10);
- logWindow.setSize(300, 800);
- setMinimumSize(logWindow.getSize());
- logWindow.pack();
- Logger.debug("Протокол работает");
- return logWindow;
- }
-
- protected void addWindow(JInternalFrame frame)
- {
- desktopPane.add(frame);
- frame.setVisible(true);
- }
-
-// protected JMenuBar createMenuBar() {
-// JMenuBar menuBar = new JMenuBar();
-//
-// //Set up the lone menu.
-// JMenu menu = new JMenu("Document");
-// menu.setMnemonic(KeyEvent.VK_D);
-// menuBar.add(menu);
-//
-// //Set up the first menu item.
-// JMenuItem menuItem = new JMenuItem("New");
-// menuItem.setMnemonic(KeyEvent.VK_N);
-// menuItem.setAccelerator(KeyStroke.getKeyStroke(
-// KeyEvent.VK_N, ActionEvent.ALT_MASK));
-// menuItem.setActionCommand("new");
-//// menuItem.addActionListener(this);
-// menu.add(menuItem);
-//
-// //Set up the second menu item.
-// menuItem = new JMenuItem("Quit");
-// menuItem.setMnemonic(KeyEvent.VK_Q);
-// menuItem.setAccelerator(KeyStroke.getKeyStroke(
-// KeyEvent.VK_Q, ActionEvent.ALT_MASK));
-// menuItem.setActionCommand("quit");
-//// menuItem.addActionListener(this);
-// menu.add(menuItem);
-//
-// return menuBar;
-// }
-
- private JMenuBar generateMenuBar()
- {
- JMenuBar menuBar = new JMenuBar();
-
- JMenu lookAndFeelMenu = new JMenu("Режим отображения");
- lookAndFeelMenu.setMnemonic(KeyEvent.VK_V);
- lookAndFeelMenu.getAccessibleContext().setAccessibleDescription(
- "Управление режимом отображения приложения");
-
- {
- JMenuItem systemLookAndFeel = new JMenuItem("Системная схема", KeyEvent.VK_S);
- systemLookAndFeel.addActionListener((event) -> {
- setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
- this.invalidate();
- });
- lookAndFeelMenu.add(systemLookAndFeel);
- }
-
- {
- JMenuItem crossplatformLookAndFeel = new JMenuItem("Универсальная схема", KeyEvent.VK_S);
- crossplatformLookAndFeel.addActionListener((event) -> {
- setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
- this.invalidate();
- });
- lookAndFeelMenu.add(crossplatformLookAndFeel);
- }
-
- JMenu testMenu = new JMenu("Тесты");
- testMenu.setMnemonic(KeyEvent.VK_T);
- testMenu.getAccessibleContext().setAccessibleDescription(
- "Тестовые команды");
-
- {
- JMenuItem addLogMessageItem = new JMenuItem("Сообщение в лог", KeyEvent.VK_S);
- addLogMessageItem.addActionListener((event) -> {
- Logger.debug("Новая строка");
- });
- testMenu.add(addLogMessageItem);
- }
-
- menuBar.add(lookAndFeelMenu);
- menuBar.add(testMenu);
- return menuBar;
- }
-
- private void setLookAndFeel(String className)
- {
- try
- {
- UIManager.setLookAndFeel(className);
- SwingUtilities.updateComponentTreeUI(this);
- }
- catch (ClassNotFoundException | InstantiationException
- | IllegalAccessException | UnsupportedLookAndFeelException e)
- {
- // just ignore
- }
- }
-}
diff --git a/robots/src/log/LogWindowSource.java b/robots/src/log/LogWindowSource.java
deleted file mode 100644
index ca0ce4426..000000000
--- a/robots/src/log/LogWindowSource.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package log;
-
-import java.util.ArrayList;
-import java.util.Collections;
-
-/**
- * Что починить:
- * 1. Этот класс порождает утечку ресурсов (связанные слушатели оказываются
- * удерживаемыми в памяти)
- * 2. Этот класс хранит активные сообщения лога, но в такой реализации он
- * их лишь накапливает. Надо же, чтобы количество сообщений в логе было ограничено
- * величиной m_iQueueLength (т.е. реально нужна очередь сообщений
- * ограниченного размера)
- */
-public class LogWindowSource
-{
- private int m_iQueueLength;
-
- private ArrayList m_messages;
- private final ArrayList m_listeners;
- private volatile LogChangeListener[] m_activeListeners;
-
- public LogWindowSource(int iQueueLength)
- {
- m_iQueueLength = iQueueLength;
- m_messages = new ArrayList(iQueueLength);
- m_listeners = new ArrayList();
- }
-
- public void registerListener(LogChangeListener listener)
- {
- synchronized(m_listeners)
- {
- m_listeners.add(listener);
- m_activeListeners = null;
- }
- }
-
- public void unregisterListener(LogChangeListener listener)
- {
- synchronized(m_listeners)
- {
- m_listeners.remove(listener);
- m_activeListeners = null;
- }
- }
-
- public void append(LogLevel logLevel, String strMessage)
- {
- LogEntry entry = new LogEntry(logLevel, strMessage);
- m_messages.add(entry);
- LogChangeListener [] activeListeners = m_activeListeners;
- if (activeListeners == null)
- {
- synchronized (m_listeners)
- {
- if (m_activeListeners == null)
- {
- activeListeners = m_listeners.toArray(new LogChangeListener [0]);
- m_activeListeners = activeListeners;
- }
- }
- }
- for (LogChangeListener listener : activeListeners)
- {
- listener.onLogChanged();
- }
- }
-
- public int size()
- {
- return m_messages.size();
- }
-
- public Iterable range(int startFrom, int count)
- {
- if (startFrom < 0 || startFrom >= m_messages.size())
- {
- return Collections.emptyList();
- }
- int indexTo = Math.min(startFrom + count, m_messages.size());
- return m_messages.subList(startFrom, indexTo);
- }
-
- public Iterable all()
- {
- return m_messages;
- }
-}
diff --git a/robots/src/gui/GameVisualizer.java b/src/main/java/org/example/gui/GameVisualizer.java
similarity index 96%
rename from robots/src/gui/GameVisualizer.java
rename to src/main/java/org/example/gui/GameVisualizer.java
index f82cfd8f8..96952cb29 100644
--- a/robots/src/gui/GameVisualizer.java
+++ b/src/main/java/org/example/gui/GameVisualizer.java
@@ -139,6 +139,9 @@ private void moveRobot(double velocity, double angularVelocity, double duration)
{
newY = m_robotPositionY + velocity * duration * Math.sin(m_robotDirection);
}
+ // Проверяем, не выходит ли червячок за границы экрана
+ newX = applyLimits(newX, 0, getWidth());
+ newY = applyLimits(newY, 0, getHeight());
m_robotPositionX = newX;
m_robotPositionY = newY;
double newDirection = asNormalizedRadians(m_robotDirection + angularVelocity * duration);
diff --git a/robots/src/gui/GameWindow.java b/src/main/java/org/example/gui/GameWindow.java
similarity index 67%
rename from robots/src/gui/GameWindow.java
rename to src/main/java/org/example/gui/GameWindow.java
index ecb63c00f..5cc47c187 100644
--- a/robots/src/gui/GameWindow.java
+++ b/src/main/java/org/example/gui/GameWindow.java
@@ -2,19 +2,23 @@
import java.awt.BorderLayout;
-import javax.swing.JInternalFrame;
-import javax.swing.JPanel;
+import javax.swing.*;
+import javax.swing.event.InternalFrameEvent;
+import javax.swing.event.InternalFrameListener;
-public class GameWindow extends JInternalFrame
+public class GameWindow extends inter
{
private final GameVisualizer m_visualizer;
- public GameWindow()
+ public GameWindow()
{
super("Игровое поле", true, true, true, true);
+
m_visualizer = new GameVisualizer();
+
JPanel panel = new JPanel(new BorderLayout());
panel.add(m_visualizer, BorderLayout.CENTER);
getContentPane().add(panel);
pack();
}
+
}
diff --git a/robots/src/gui/LogWindow.java b/src/main/java/org/example/gui/LogWindow.java
similarity index 70%
rename from robots/src/gui/LogWindow.java
rename to src/main/java/org/example/gui/LogWindow.java
index 723d3e2fc..429a4f960 100644
--- a/robots/src/gui/LogWindow.java
+++ b/src/main/java/org/example/gui/LogWindow.java
@@ -1,29 +1,29 @@
package gui;
-import java.awt.BorderLayout;
-import java.awt.EventQueue;
-import java.awt.TextArea;
+import java.awt.*;
+import java.awt.event.WindowListener;
-import javax.swing.JInternalFrame;
-import javax.swing.JPanel;
+import javax.swing.*;
+import javax.swing.event.InternalFrameEvent;
+import javax.swing.event.InternalFrameListener;
import log.LogChangeListener;
import log.LogEntry;
import log.LogWindowSource;
-public class LogWindow extends JInternalFrame implements LogChangeListener
+public class LogWindow extends inter implements LogChangeListener
{
private LogWindowSource m_logSource;
private TextArea m_logContent;
- public LogWindow(LogWindowSource logSource)
+ public LogWindow(LogWindowSource logSource)
{
super("Протокол работы", true, true, true, true);
m_logSource = logSource;
m_logSource.registerListener(this);
m_logContent = new TextArea("");
m_logContent.setSize(200, 500);
-
+
JPanel panel = new JPanel(new BorderLayout());
panel.add(m_logContent, BorderLayout.CENTER);
getContentPane().add(panel);
@@ -31,6 +31,8 @@ public LogWindow(LogWindowSource logSource)
updateLogContent();
}
+
+
private void updateLogContent()
{
StringBuilder content = new StringBuilder();
@@ -41,7 +43,13 @@ private void updateLogContent()
m_logContent.setText(content.toString());
m_logContent.invalidate();
}
-
+
+ @Override
+ public void doDefaultCloseAction() {
+ m_logSource.unregisterListener(this);
+ super.doDefaultCloseAction();
+ }
+
@Override
public void onLogChanged()
{
diff --git a/src/main/java/org/example/gui/MainApplicationFrame.java b/src/main/java/org/example/gui/MainApplicationFrame.java
new file mode 100644
index 000000000..112899997
--- /dev/null
+++ b/src/main/java/org/example/gui/MainApplicationFrame.java
@@ -0,0 +1,95 @@
+package gui;
+
+import java.awt.*;
+import java.awt.event.WindowAdapter;
+
+import javax.swing.*;
+
+
+import log.Logger;
+
+
+
+/**
+ * Что требуется сделать:
+ * 1. Метод создания меню перегружен функционалом и трудно читается.
+ * Следует разделить его на серию более простых методов (или вообще выделить отдельный класс).
+ *
+ */
+
+public class MainApplicationFrame extends JFrame //
+{
+ private final JDesktopPane desktopPane = new JDesktopPane();
+
+ public MainApplicationFrame() {
+ //Make the big window be indented 50 pixels from each edge
+ //of the screen.
+ int inset = 50;
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+ setBounds(inset, inset,
+ screenSize.width - inset*2,
+ screenSize.height - inset*2);
+
+ setContentPane(desktopPane);
+
+
+ LogWindow logWindow = createLogWindow();
+ addWindow(logWindow);
+
+ GameWindow gameWindow = new GameWindow();
+ gameWindow.setSize(400, 400);
+ addWindow(gameWindow);
+
+ MenuBar menuBar = new MenuBar(this);
+ setJMenuBar(menuBar.generateMenuBar());
+ setDefaultCloseOperation(EXIT_ON_CLOSE);
+ }
+
+ protected LogWindow createLogWindow()
+ {
+ LogWindow logWindow = new LogWindow(Logger.getDefaultLogSource());
+ logWindow.setLocation(10,10);
+ logWindow.setSize(300, 800);
+ setMinimumSize(logWindow.getSize());
+ logWindow.pack();
+ Logger.debug("Протокол работает");
+ return logWindow;
+ }
+
+ protected void addWindow(JInternalFrame frame)
+ {
+ desktopPane.add(frame);
+ frame.setVisible(true);
+ }
+
+// protected JMenuBar createMenuBar() {
+// JMenuBar menuBar = new JMenuBar();
+//
+// //Set up the lone menu.(Настройте одиночное меню.)
+// JMenu menu = new JMenu("Document");
+// menu.setMnemonic(KeyEvent.VK_D);
+// menuBar.add(menu);
+//
+// //Set up the first menu item.(Настройте первый пункт меню.)
+// JMenuItem menuItem = new JMenuItem("New");
+// menuItem.setMnemonic(KeyEvent.VK_N);
+// menuItem.setAccelerator(KeyStroke.getKeyStroke(
+// KeyEvent.VK_N, ActionEvent.ALT_MASK));
+// menuItem.setActionCommand("new");
+//// menuItem.addActionListener(this);
+// menu.add(menuItem);
+//
+// //Set up the second menu item.(Настройте второй пункт меню.)
+// menuItem = new JMenuItem("Quit");
+// menuItem.setMnemonic(KeyEvent.VK_Q);
+// menuItem.setAccelerator(KeyStroke.getKeyStroke(
+// KeyEvent.VK_Q, ActionEvent.ALT_MASK));
+// menuItem.setActionCommand("quit");
+//// menuItem.addActionListener(this);
+// menu.add(menuItem);
+//
+// return menuBar;
+// }
+
+
+}
diff --git a/src/main/java/org/example/gui/MenuBar.java b/src/main/java/org/example/gui/MenuBar.java
new file mode 100644
index 000000000..fae7d6134
--- /dev/null
+++ b/src/main/java/org/example/gui/MenuBar.java
@@ -0,0 +1,121 @@
+package gui;
+
+import log.Logger;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+public class MenuBar {
+ static MainApplicationFrame mainFrame; // Используем вместо this, т.к. this имеет ввиду MainApplicationFrame объект
+ // определяем MenuBar передавая ему аргумент MainApplicationFrame
+ public MenuBar(MainApplicationFrame mainFrame){
+ this.mainFrame = mainFrame;
+ }
+ public static JMenuBar generateMenuBar() {
+ JMenuBar menuBar = new JMenuBar();
+
+ menuBar.add(createLookAndFeelMenu());
+ menuBar.add(createTestMenu());
+ //menuBar.add(createSettingMenu());
+ menuBar.add(createExitButton());
+ return menuBar;
+ }
+
+// private static JMenu createSettingMenu() {
+// ResourceBundle rb = ResourceBundle.getBundle("Language", Locale.getDefault());
+// JMenu settingMenu = new JMenu("Настройки");
+// JMenu settingLanguage = new JMenu(rb.getString("Language"));
+// Locale ru = new Locale("ru", "RU");
+// Locale en = new Locale("en", "US");
+//
+// {
+// JMenuItem english = new JMenuItem("Английский", KeyEvent.VK_S);
+// english.addActionListener((event) -> {
+// Locale.setDefault(new Locale("en", "US"));
+// });
+// settingLanguage.add(english);
+// }
+//
+// {
+// JMenuItem russian = new JMenuItem("Русский");
+//
+// settingLanguage.add(russian);
+// }
+//
+// settingMenu.add(settingLanguage);
+// return settingMenu;
+// }
+
+ private static JMenu createLookAndFeelMenu() {
+
+ JMenu lookAndFeelMenu = new JMenu("Режим отображения");
+ lookAndFeelMenu.setMnemonic(KeyEvent.VK_V);
+ lookAndFeelMenu.getAccessibleContext().setAccessibleDescription(
+ "Управление режимом отображения приложения");
+
+ {
+ JMenuItem systemLookAndFeel = new JMenuItem("Системная схема", KeyEvent.VK_S);
+ systemLookAndFeel.addActionListener((event) -> {
+ setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ mainFrame.invalidate();
+ });
+ lookAndFeelMenu.add(systemLookAndFeel);
+ }
+
+ {
+ JMenuItem crossplatformLookAndFeel = new JMenuItem("Универсальная схема", KeyEvent.VK_S);
+ crossplatformLookAndFeel.addActionListener((event) -> {
+ setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
+ mainFrame.invalidate();
+ });
+ lookAndFeelMenu.add(crossplatformLookAndFeel);
+ }
+
+ return lookAndFeelMenu;
+ }
+
+
+ private static JMenu createTestMenu() {
+ JMenu testMenu = new JMenu("Тесты");
+ testMenu.setMnemonic(KeyEvent.VK_T);
+ testMenu.getAccessibleContext().setAccessibleDescription(
+ "Тестовые команды");
+
+ {
+ JMenuItem addLogMessageItem = new JMenuItem("Сообщение в лог", KeyEvent.VK_S);
+ addLogMessageItem.addActionListener((event) -> {
+ Logger.debug("Новая строка");
+ });
+ testMenu.add(addLogMessageItem);
+ }
+ return testMenu;
+ }
+
+ private static JMenu createExitButton() {
+ JMenu menu = new JMenu("Выйти");
+ menu.setMnemonic(KeyEvent.VK_T);//KeyEvent.VK_T
+
+ {
+ JMenuItem exit1 = new JMenuItem("Выйти", KeyEvent.VK_S);
+ exit1.setFocusable(false);
+ exit1.addActionListener((event) -> {
+ System.exit(0);
+ });
+ menu.add(exit1);
+ }
+ return menu;
+ }
+
+ private static void setLookAndFeel(String className) {
+ try {
+ UIManager.setLookAndFeel(className);
+ SwingUtilities.updateComponentTreeUI(mainFrame);
+ } catch (ClassNotFoundException | InstantiationException
+ | IllegalAccessException | UnsupportedLookAndFeelException e) {
+ }
+ }
+}
diff --git a/robots/src/gui/RobotsProgram.java b/src/main/java/org/example/gui/RobotsProgram.java
similarity index 100%
rename from robots/src/gui/RobotsProgram.java
rename to src/main/java/org/example/gui/RobotsProgram.java
diff --git a/src/main/java/org/example/gui/inter.java b/src/main/java/org/example/gui/inter.java
new file mode 100644
index 000000000..9aee57e09
--- /dev/null
+++ b/src/main/java/org/example/gui/inter.java
@@ -0,0 +1,62 @@
+package gui;
+
+import javax.swing.*;
+import javax.swing.event.InternalFrameEvent;
+import javax.swing.event.InternalFrameListener;
+
+public abstract class inter extends JInternalFrame{
+
+ public inter(String str, boolean b, boolean b1, boolean b2, boolean b3){
+ super(str, b, b1, b2, b3);
+ setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+ addInternalFrameListener(new InternalFrameListener() {
+
+ @Override
+ public void internalFrameOpened(InternalFrameEvent e) {
+
+ }
+
+ @Override
+ public void internalFrameClosing(InternalFrameEvent event) {
+ Object[] options = { "Да", "Нет!" };
+ int n = JOptionPane
+ .showOptionDialog(event.getInternalFrame(), "Закрыть окно?",
+ "Подтверждение", JOptionPane.YES_NO_OPTION,
+ JOptionPane.QUESTION_MESSAGE, null, options,
+ options[0]);
+ if (n == 0) {
+ event.getInternalFrame().setVisible(false);
+ setDefaultCloseOperation(EXIT_ON_CLOSE);
+ }
+ }
+
+ @Override
+ public void internalFrameClosed(InternalFrameEvent e) {
+
+ }
+
+ @Override
+ public void internalFrameIconified(InternalFrameEvent e) {
+
+ }
+
+ @Override
+ public void internalFrameDeiconified(InternalFrameEvent e) {
+
+ }
+
+ @Override
+ public void internalFrameActivated(InternalFrameEvent e) {
+
+ }
+
+ @Override
+ public void internalFrameDeactivated(InternalFrameEvent e) {
+
+ }
+
+ });
+ }
+
+
+}
diff --git a/robots/src/log/LogChangeListener.java b/src/main/java/org/example/log/LogChangeListener.java
similarity index 100%
rename from robots/src/log/LogChangeListener.java
rename to src/main/java/org/example/log/LogChangeListener.java
diff --git a/robots/src/log/LogEntry.java b/src/main/java/org/example/log/LogEntry.java
similarity index 100%
rename from robots/src/log/LogEntry.java
rename to src/main/java/org/example/log/LogEntry.java
diff --git a/robots/src/log/LogLevel.java b/src/main/java/org/example/log/LogLevel.java
similarity index 100%
rename from robots/src/log/LogLevel.java
rename to src/main/java/org/example/log/LogLevel.java
diff --git a/src/main/java/org/example/log/LogWindowSource.java b/src/main/java/org/example/log/LogWindowSource.java
new file mode 100644
index 000000000..b7a6928b3
--- /dev/null
+++ b/src/main/java/org/example/log/LogWindowSource.java
@@ -0,0 +1,99 @@
+package log;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+
+/**
+ * пофикшены:
+ * 1. утечка ресурсов (связанные слушатели оказываются удерживаемыми в памяти)
+ * 2. ограничения; раньше класс хранил активные сообщения лога, но в такой реализации он
+ * их лишь накапливал. Сейчас количество сообщений в логе было ограничено
+ * величиной m_iQueueLength (т.е. реально нужна очередь сообщений
+ * ограниченного размера)
+ *
+ * изменения:
+ * Используется ArrayBlockingQueue для хранения сообщений лога, что позволяет избежать утечек ресурсов и ограничить
+ * размер лога величиной m_iQueueLength.
+ * Избавлены от необходимости хранить все слушатели в виде массива, который нужно было пересоздавать каждый раз при
+ * добавлении или удалении слушателя. Вместо этого используется обычный список m_listeners, синхронизированный
+ * на запись/удаление.
+ * Используется offer() вместо add() при добавлении сообщения в очередь, чтобы избежать исключения в случае,
+ * если очередь заполнена.
+ * Метод size() теперь возвращает размер m_queue.
+ * Метод range() теперь проверяет, что count неотрицательное число и возвращает пустой список, если startFrom выходит
+ * за пределы очереди или count равен нулю.
+ * Метод range() теперь использует метод ArrayList.subList() непосредственно над m_queue, а не создает новый список,
+ * что улучшает производительность и уменьшает использование памяти.
+ * Метод all() теперь создает новый список из m_queue, вместо создания списка итератором.
+ */
+public class LogWindowSource {
+ private final int m_iQueueLength;
+ private final ArrayBlockingQueue m_queue;
+ private final List m_listeners;
+ private volatile LogChangeListener[] m_activeListeners;
+
+ public LogWindowSource(int iQueueLength) {
+ m_iQueueLength = iQueueLength;
+ m_queue = new ArrayBlockingQueue<>(iQueueLength);
+ m_listeners = new ArrayList<>();
+ }
+
+ public void registerListener(LogChangeListener listener) {
+ synchronized(m_listeners) {
+ m_listeners.add(listener);
+ m_activeListeners = null;
+ }
+ }
+
+ public void unregisterListener(LogChangeListener listener) {
+ synchronized(m_listeners) {
+ m_listeners.remove(listener);
+ m_activeListeners = null;
+ }
+ }
+
+ public void append(LogLevel logLevel, String strMessage) {
+ boolean added = false;
+ while (!added) {
+ LogEntry entry = new LogEntry(logLevel, strMessage);
+ if (m_queue.offer(entry)) {
+ added = true;
+ if (m_queue.size() > m_iQueueLength) {
+ m_queue.remove();
+ }
+ LogChangeListener[] activeListeners = m_activeListeners;
+ if (activeListeners == null) {
+ synchronized (m_listeners) {
+ if (m_activeListeners == null) {
+ activeListeners = m_listeners.toArray(new LogChangeListener[0]);
+ m_activeListeners = activeListeners;
+ }
+ }
+ }
+ for (LogChangeListener listener : activeListeners) {
+ listener.onLogChanged();
+ }
+ } else {
+ m_queue.remove();
+ }
+ }
+ }
+
+ public int size() {
+ return m_queue.size();
+ }
+
+ public Iterable range(int startFrom, int count) {
+ if (startFrom < 0 || startFrom >= m_queue.size() || count <= 0) {
+ return Collections.emptyList();
+ }
+ int indexTo = Math.min(startFrom + count, m_queue.size());
+ return new ArrayList<>(m_queue).subList(startFrom, indexTo);
+ }
+
+ public Iterable all() {
+ return new ArrayList<>(m_queue);
+ }
+}
\ No newline at end of file
diff --git a/robots/src/log/Logger.java b/src/main/java/org/example/log/Logger.java
similarity index 100%
rename from robots/src/log/Logger.java
rename to src/main/java/org/example/log/Logger.java
diff --git a/src/main/java/org/example/resourses/english.properties b/src/main/java/org/example/resourses/english.properties
new file mode 100644
index 000000000..2dfb3ee48
--- /dev/null
+++ b/src/main/java/org/example/resourses/english.properties
@@ -0,0 +1 @@
+Language=Language
\ No newline at end of file
diff --git a/src/main/java/org/example/resourses/russian.properties b/src/main/java/org/example/resourses/russian.properties
new file mode 100644
index 000000000..2df80d8cd
--- /dev/null
+++ b/src/main/java/org/example/resourses/russian.properties
@@ -0,0 +1 @@
+Language=\u041F\u0440\u043E\u0442\u043E\u043A\u043E\u043B \u0440\u0430\u0431\u043E\u0442\u044B
\ No newline at end of file
diff --git a/src/main/java/org/example/testsLogs/LogWindowSourceTest.java b/src/main/java/org/example/testsLogs/LogWindowSourceTest.java
new file mode 100644
index 000000000..45f845641
--- /dev/null
+++ b/src/main/java/org/example/testsLogs/LogWindowSourceTest.java
@@ -0,0 +1,63 @@
+package testsLogs;
+import log.LogWindowSource;
+import log.LogLevel;
+import log.LogEntry;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class LogWindowSourceTest {
+
+ @Test
+ public void testLogAppending() {
+ LogWindowSource logWindow = new LogWindowSource(5); // Создаем лог с ограничением в 5 сообщений
+ logWindow.append(LogLevel.Info, "Message 1");
+ logWindow.append(LogLevel.Warning, "Message 2");
+ logWindow.append(LogLevel.Error, "Message 3");
+
+ assertEquals(3, logWindow.size()); // Проверяем, что размер лога равен 3 после добавления 3 сообщений
+ }
+
+ @Test
+ public void testLogLimit() {
+ LogWindowSource logWindow = new LogWindowSource(3); // Создаем лог с ограничением в 3 сообщения
+ logWindow.append(LogLevel.Info, "Message 1");
+ logWindow.append(LogLevel.Warning, "Message 2");
+ logWindow.append(LogLevel.Error, "Message 3");
+ logWindow.append(LogLevel.Debug, "Message 4"); // Превышаем лимит, первое сообщение должно быть удалено
+
+ assertEquals(3, logWindow.size()); // Проверяем, что размер лога равен 3 после добавления 4 сообщений
+ assertEquals("Message 2", logWindow.all().iterator().next().getMessage()); // Проверяем, что первое сообщение было удалено
+ }
+
+ @Test
+ public void testLogRange() {
+ LogWindowSource logWindow = new LogWindowSource(5); // Создаем лог с ограничением в 5 сообщений
+ logWindow.append(LogLevel.Info, "Message 1");
+ logWindow.append(LogLevel.Warning, "Message 2");
+ logWindow.append(LogLevel.Error, "Message 3");
+ logWindow.append(LogLevel.Debug, "Message 4");
+ logWindow.append(LogLevel.Info, "Message 5");
+
+ Iterable range = logWindow.range(1, 3); // Получаем диапазон сообщений с индекса 1 до 3
+
+ int count = 0;
+ for (LogEntry entry : range) {
+ count++;
+ }
+ assertEquals(3, count); // Проверяем, что в полученном диапазоне содержится 3 сообщения
+ }
+
+ @Test
+ public void testLogRangeOutOfBounds() {
+ LogWindowSource logWindow = new LogWindowSource(5); // Создаем лог с ограничением в 5 сообщений
+ logWindow.append(LogLevel.Info, "Message 1");
+
+ Iterable range = logWindow.range(2, 3); // Пытаемся получить диапазон с индекса 2, хотя в логе только 1 сообщение
+
+ int count = 0;
+ for (LogEntry entry : range) {
+ count++;
+ }
+ assertEquals(0, count); // Проверяем, что полученный диапазон пустой
+ }
+}
diff --git a/src/main/resources/resources_en_US.properties b/src/main/resources/resources_en_US.properties
new file mode 100644
index 000000000..0c2f442e7
--- /dev/null
+++ b/src/main/resources/resources_en_US.properties
@@ -0,0 +1 @@
+Language = Language
\ No newline at end of file
diff --git a/src/main/resources/resources_ru_RU.properties b/src/main/resources/resources_ru_RU.properties
new file mode 100644
index 000000000..c2844abdc
--- /dev/null
+++ b/src/main/resources/resources_ru_RU.properties
@@ -0,0 +1 @@
+Language = ttt
\ No newline at end of file