commit 26bd00e7f3f0c691115bc318a67b8a5ad05bca6a Author: J.Henezi Date: Wed Jan 8 15:09:57 2025 +0100 mini clock tool diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f68d109 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +### IntelliJ IDEA ### +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### 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 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/artifacts/WatchTheClock_jar.xml b/.idea/artifacts/WatchTheClock_jar.xml new file mode 100644 index 0000000..16bc70e --- /dev/null +++ b/.idea/artifacts/WatchTheClock_jar.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/out/artifacts/WatchTheClock_jar + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6f29fee --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..65c1387 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/WatchTheClock.iml b/WatchTheClock.iml new file mode 100644 index 0000000..54b8c26 --- /dev/null +++ b/WatchTheClock.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/META-INF/MANIFEST.MF b/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..5ee19cb --- /dev/null +++ b/resources/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: Main + diff --git a/resources/alert.wav b/resources/alert.wav new file mode 100644 index 0000000..8589522 Binary files /dev/null and b/resources/alert.wav differ diff --git a/resources/clock.ico b/resources/clock.ico new file mode 100644 index 0000000..d9fe416 Binary files /dev/null and b/resources/clock.ico differ diff --git a/resources/clock.png b/resources/clock.png new file mode 100644 index 0000000..4d5e203 Binary files /dev/null and b/resources/clock.png differ diff --git a/src/AlarmLogic.java b/src/AlarmLogic.java new file mode 100644 index 0000000..488b91d --- /dev/null +++ b/src/AlarmLogic.java @@ -0,0 +1,131 @@ +import javax.sound.sampled.*; +import javax.swing.*; +import java.awt.*; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +public class AlarmLogic { + private JTextField hoursField; + private JTextField minutesField; + private JButton activateButton; + private JCheckBox soundCheckbox; + private Timer timer; + private boolean isActive; + private Clip alertSound; + + public AlarmLogic(JTextField hours, JTextField minutes, + JButton activateBtn, JCheckBox soundCb) { + this.hoursField = hours; + this.minutesField = minutes; + this.activateButton = activateBtn; + this.soundCheckbox = soundCb; + this.isActive = false; + + // Sound laden + try { + AudioInputStream audioStream = AudioSystem.getAudioInputStream( + getClass().getResource("/alert.wav")); + alertSound = AudioSystem.getClip(); + alertSound.open(audioStream); + } catch (Exception e) { + e.printStackTrace(); + } + + // Timer für Überprüfung jede Sekunde + timer = new Timer(1000, e -> checkAlarm()); + } + + public void toggleActivation() { + if (!isActive) { + // Überprüfe Eingaben + try { + int hours = Integer.parseInt(hoursField.getText()); + int minutes = Integer.parseInt(minutesField.getText()); + + if (hours >= 0 && hours <= 23 && minutes >= 0 && minutes <= 59) { + isActive = true; + activateButton.setText("Deactivate"); + activateButton.setBackground(new Color(244, 67, 54)); + setFieldsEditable(false); + timer.start(); + } else { + JOptionPane.showMessageDialog(null, + "Bitte gültige Zeit eingeben!\nStunden: 0-23\nMinuten: 0-59", + "Ungültige Zeit", + JOptionPane.ERROR_MESSAGE); + } + } catch (NumberFormatException ex) { + JOptionPane.showMessageDialog(null, + "Bitte nur Zahlen eingeben!", + "Ungültige Eingabe", + JOptionPane.ERROR_MESSAGE); + } + } else { + deactivateAlarm(); + } + } + + private void checkAlarm() { + LocalTime now = LocalTime.now(); + int currentHour = now.getHour(); + int currentMinute = now.getMinute(); + + int targetHour = Integer.parseInt(hoursField.getText()); + int targetMinute = Integer.parseInt(minutesField.getText()); + + if (currentHour == targetHour && currentMinute == targetMinute) { + // Alarm auslösen + if (soundCheckbox.isSelected() && alertSound != null) { + alertSound.loop(Clip.LOOP_CONTINUOUSLY); + } + + String currentTime = now.format(DateTimeFormatter.ofPattern("HH:mm")); + Object[] options = {"OK", "+5 min"}; + int choice = JOptionPane.showOptionDialog(null, + "Es ist " + currentTime + "!", + "Alarm", + JOptionPane.DEFAULT_OPTION, + JOptionPane.INFORMATION_MESSAGE, + null, + options, + options[0]); + + if (choice == 0) { // OK + deactivateAlarm(); + } else if (choice == 1) { // +5 min + if (alertSound != null) { + alertSound.stop(); + alertSound.setFramePosition(0); + } + + // Neue Alarmzeit berechnen (+5 Minuten) + LocalTime newAlarmTime = LocalTime.of(targetHour, targetMinute).plusMinutes(5); + hoursField.setText(String.valueOf(newAlarmTime.getHour())); + minutesField.setText(String.format("%02d", newAlarmTime.getMinute())); + + // Zeige neue Alarmzeit an + JOptionPane.showMessageDialog(null, + "Neuer Alarm um " + newAlarmTime.format(DateTimeFormatter.ofPattern("HH:mm")), + "Schlummerfunktion", + JOptionPane.INFORMATION_MESSAGE); + } + } + } + + private void deactivateAlarm() { + isActive = false; + timer.stop(); + if (alertSound != null) { + alertSound.stop(); + alertSound.setFramePosition(0); + } + activateButton.setText("Activate"); + activateButton.setBackground(new Color(76, 175, 80)); + setFieldsEditable(true); + } + + private void setFieldsEditable(boolean editable) { + hoursField.setEditable(editable); + minutesField.setEditable(editable); + } +} \ No newline at end of file diff --git a/src/CountdownLogic.java b/src/CountdownLogic.java new file mode 100644 index 0000000..e639a56 --- /dev/null +++ b/src/CountdownLogic.java @@ -0,0 +1,119 @@ +import javax.swing.*; +import javax.sound.sampled.*; +import java.awt.*; + +public class CountdownLogic { + private JTextField hoursField; + private JTextField minutesField; + private JTextField secondsField; + private JButton startButton; + private JCheckBox soundCheckbox; + private Timer timer; + private long remainingTime; + private boolean isRunning; + private Clip alertSound; + + public CountdownLogic(JTextField hours, JTextField minutes, JTextField seconds, + JButton startBtn, JCheckBox soundCb) { + this.hoursField = hours; + this.minutesField = minutes; + this.secondsField = seconds; + this.startButton = startBtn; + this.soundCheckbox = soundCb; + this.isRunning = false; + + // Sound laden + try { + AudioInputStream audioStream = AudioSystem.getAudioInputStream( + getClass().getResource("/alert.wav")); + alertSound = AudioSystem.getClip(); + alertSound.open(audioStream); + } catch (Exception e) { + e.printStackTrace(); + } + + timer = new Timer(1000, e -> updateCountdown()); + } + + public void toggleStartStop() { + if (!isRunning) { + // Startzeit berechnen + long hours = Long.parseLong(hoursField.getText()); + long minutes = Long.parseLong(minutesField.getText()); + long seconds = Long.parseLong(secondsField.getText()); + + remainingTime = hours * 3600 + minutes * 60 + seconds; + + if (remainingTime > 0) { + isRunning = true; + startButton.setText("Pause"); + startButton.setBackground(new Color(255, 152, 0)); + timer.start(); + setFieldsEditable(false); + } + } else { + isRunning = false; + startButton.setText("Start"); + startButton.setBackground(new Color(76, 175, 80)); + timer.stop(); + setFieldsEditable(true); + } + } + + private void updateCountdown() { + if (remainingTime > 0) { + remainingTime--; + updateDisplay(); + } else { + timer.stop(); + isRunning = false; + startButton.setText("Start"); + startButton.setBackground(new Color(76, 175, 80)); + setFieldsEditable(true); + + // Sound in Schleife starten wenn aktiviert + if (soundCheckbox.isSelected() && alertSound != null) { + alertSound.loop(Clip.LOOP_CONTINUOUSLY); + } + + // Benachrichtigung anzeigen und danach Sound stoppen + JOptionPane.showMessageDialog(null, + "Countdown beendet!", + "Zeit abgelaufen", + JOptionPane.INFORMATION_MESSAGE); + + // Sound stoppen nach OK-Klick + if (alertSound != null) { + alertSound.stop(); + alertSound.setFramePosition(0); + } + } + } + + private void updateDisplay() { + long hours = remainingTime / 3600; + long minutes = (remainingTime % 3600) / 60; + long seconds = remainingTime % 60; + + hoursField.setText(String.valueOf(hours)); + minutesField.setText(String.format("%02d", minutes)); + secondsField.setText(String.format("%02d", seconds)); + } + + private void setFieldsEditable(boolean editable) { + hoursField.setEditable(editable); + minutesField.setEditable(editable); + secondsField.setEditable(editable); + } + + public void reset() { + timer.stop(); + isRunning = false; + startButton.setText("Start"); + startButton.setBackground(new Color(76, 175, 80)); + hoursField.setText("0"); + minutesField.setText("0"); + secondsField.setText("0"); + setFieldsEditable(true); + } +} \ No newline at end of file diff --git a/src/Main.java b/src/Main.java new file mode 100644 index 0000000..9c3b5e4 --- /dev/null +++ b/src/Main.java @@ -0,0 +1,471 @@ +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.DocumentFilter; +import javax.swing.text.AbstractDocument; +import javax.swing.event.HyperlinkEvent; +import java.awt.Desktop; + +public class Main { + private static JFrame frame; + private static String baseTitle = "WatchTheClock"; + private static String currentTitle = ""; + + public static void main(String[] args) { + // Setze den Anwendungsnamen für Windows + System.setProperty("sun.java.command", baseTitle); + + frame = new JFrame(baseTitle); + + // Icon setzen + try { + Image icon = new ImageIcon(Main.class.getResource("/clock.png")).getImage(); + frame.setIconImage(icon); + } catch (Exception e) { + System.err.println("Icon konnte nicht geladen werden: " + e.getMessage()); + } + + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(450, 300); + frame.setMinimumSize(new Dimension(400, 280)); + frame.setLocationRelativeTo(null); + + // Erstellt JTabbedPane für die verschiedenen Funktionen + JTabbedPane tabbedPane = new JTabbedPane(); + frame.add(tabbedPane); + + // Fügt die Tabs hinzu + tabbedPane.addTab("Stopwatch", createStopwatchPanel()); + tabbedPane.addTab("Countdown", createCountdownPanel()); + tabbedPane.addTab("Alarm", createAlarmPanel()); + tabbedPane.addTab("Notes", createNotesPanel()); + tabbedPane.addTab("ToDo", createTodoPanel()); + + // Menüleiste erstellen + JMenuBar menuBar = new JMenuBar(); + JMenu settingsMenu = new JMenu("⚙"); + JMenu helpMenu = new JMenu("?"); + + // Settings-Menü Items + JCheckBoxMenuItem alwaysOnTopItem = new JCheckBoxMenuItem("Always on Top"); + alwaysOnTopItem.addActionListener(e -> frame.setAlwaysOnTop(alwaysOnTopItem.isSelected())); + settingsMenu.add(alwaysOnTopItem); + + // Titel-Menüpunkt + JMenuItem titleItem = new JMenuItem("Titel ändern"); + titleItem.addActionListener(e -> changeTitleDialog()); + settingsMenu.add(titleItem); + + // Info-Menüpunkt + JMenuItem aboutItem = new JMenuItem("About"); + aboutItem.addActionListener(e -> { + JEditorPane editorPane = new JEditorPane("text/html", + "
" + + "WatchTheClock v0.1

" + + "This tool is free to use and portable!

" + + "Made by Jordan Henézi

" + + "Last time updated: 08.01.2025

" + + "Website: henezi.de
" + + "Support: watchtheclock@henezi.de

" + + "Copyright © 2025" + + "
"); + + editorPane.setEditable(false); + editorPane.setBackground(new JLabel().getBackground()); + + // Links klickbar machen + editorPane.addHyperlinkListener(event -> { + if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + try { + String url = event.getURL().toString(); + if (!url.startsWith("mailto:")) { + Desktop.getDesktop().browse(event.getURL().toURI()); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + }); + + JOptionPane.showMessageDialog(frame, + editorPane, + "About WatchTheClock", + JOptionPane.INFORMATION_MESSAGE); + }); + helpMenu.add(aboutItem); + + menuBar.add(settingsMenu); + menuBar.add(helpMenu); + frame.setJMenuBar(menuBar); + + frame.setVisible(true); + } + + private static JPanel createStopwatchPanel() { + JPanel panel = new JPanel(new BorderLayout()); + + // Zeitanzeige mit mehr Platz + JLabel timeLabel = new JLabel("00:00:00"); + timeLabel.setFont(new Font("Arial", Font.BOLD, 72)); + timeLabel.setHorizontalAlignment(SwingConstants.CENTER); + + // Extra Panel für die Zeitanzeige mit weniger Padding oben + JPanel timeLabelPanel = new JPanel(new BorderLayout()); + timeLabelPanel.add(timeLabel, BorderLayout.CENTER); + timeLabelPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 20, 0)); // Reduziertes Top-Padding + panel.add(timeLabelPanel, BorderLayout.CENTER); + + // Buttons Panel + JPanel buttonPanel = new JPanel(new FlowLayout()); + JButton startButton = new JButton("Start"); + JButton resetButton = new JButton("Reset"); + startButton.setBackground(new Color(76, 175, 80)); + resetButton.setBackground(new Color(244, 67, 54)); + buttonPanel.add(startButton); + buttonPanel.add(resetButton); + panel.add(buttonPanel, BorderLayout.SOUTH); + + // Timeline Panel mit angepasster Größe + JPanel timelinePanel = new JPanel(); + timelinePanel.setLayout(null); + + JScrollPane scrollPane = new JScrollPane(timelinePanel, + JScrollPane.VERTICAL_SCROLLBAR_NEVER, + JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + scrollPane.setBorder(BorderFactory.createTitledBorder("Timeline")); + scrollPane.setPreferredSize(new Dimension(430, 85)); // Breite und Höhe angepasst + + // Mausrad-Scrolling + timelinePanel.addMouseWheelListener(e -> { + JScrollBar horizontalBar = scrollPane.getHorizontalScrollBar(); + int delta = e.getWheelRotation() * 50; // Scroll-Geschwindigkeit + int newValue = horizontalBar.getValue() + delta; + newValue = Math.max(0, Math.min(newValue, horizontalBar.getMaximum())); + horizontalBar.setValue(newValue); + }); + + // Fügt den MouseWheelListener auch zum ScrollPane hinzu + scrollPane.addMouseWheelListener(e -> { + JScrollBar horizontalBar = scrollPane.getHorizontalScrollBar(); + int delta = e.getWheelRotation() * 50; + int newValue = horizontalBar.getValue() + delta; + newValue = Math.max(0, Math.min(newValue, horizontalBar.getMaximum())); + horizontalBar.setValue(newValue); + }); + + panel.add(scrollPane, BorderLayout.NORTH); + + // Stopwatch Logik + StopwatchLogic stopwatch = new StopwatchLogic(timeLabel, timelinePanel, startButton); + startButton.addActionListener(e -> stopwatch.toggleStartStop()); + resetButton.addActionListener(e -> stopwatch.reset()); + + return panel; + } + + private static JPanel createCountdownPanel() { + JPanel panel = new JPanel(new BorderLayout()); + panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + + // Zeit-Eingabe Panel + JPanel inputPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 15, 0)); + + // Stunden Input + JPanel hoursPanel = new JPanel(new BorderLayout()); + JLabel hoursLabel = new JLabel("Hours", SwingConstants.CENTER); + JTextField hoursField = new JTextField("0", 3); + hoursField.setHorizontalAlignment(SwingConstants.CENTER); + hoursField.setFont(new Font("Arial", Font.BOLD, 24)); + hoursPanel.add(hoursLabel, BorderLayout.NORTH); + hoursPanel.add(hoursField, BorderLayout.CENTER); + + // Minuten Input + JPanel minutesPanel = new JPanel(new BorderLayout()); + JLabel minutesLabel = new JLabel("Minutes", SwingConstants.CENTER); + JTextField minutesField = new JTextField("0", 3); + minutesField.setHorizontalAlignment(SwingConstants.CENTER); + minutesField.setFont(new Font("Arial", Font.BOLD, 24)); + minutesPanel.add(minutesLabel, BorderLayout.NORTH); + minutesPanel.add(minutesField, BorderLayout.CENTER); + + // Sekunden Input + JPanel secondsPanel = new JPanel(new BorderLayout()); + JLabel secondsLabel = new JLabel("Seconds", SwingConstants.CENTER); + JTextField secondsField = new JTextField("0", 3); + secondsField.setHorizontalAlignment(SwingConstants.CENTER); + secondsField.setFont(new Font("Arial", Font.BOLD, 24)); + secondsPanel.add(secondsLabel, BorderLayout.NORTH); + secondsPanel.add(secondsField, BorderLayout.CENTER); + + // Trennpunkte zwischen den Feldern + JLabel separator1 = new JLabel(":", SwingConstants.CENTER); + JLabel separator2 = new JLabel(":", SwingConstants.CENTER); + separator1.setFont(new Font("Arial", Font.BOLD, 24)); + separator2.setFont(new Font("Arial", Font.BOLD, 24)); + + inputPanel.add(hoursPanel); + inputPanel.add(separator1); + inputPanel.add(minutesPanel); + inputPanel.add(separator2); + inputPanel.add(secondsPanel); + + // Control Panel + JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10)); + + // Start Button + JButton startButton = new JButton("Start"); + startButton.setBackground(new Color(76, 175, 80)); + startButton.setPreferredSize(new Dimension(100, 35)); + + // Reset Button + JButton resetButton = new JButton("Reset"); + resetButton.setBackground(new Color(244, 67, 54)); + resetButton.setPreferredSize(new Dimension(100, 35)); + + // Sound Checkbox + JCheckBox soundCheckbox = new JCheckBox("Play sound"); + soundCheckbox.setSelected(true); + + controlPanel.add(startButton); + controlPanel.add(resetButton); + controlPanel.add(soundCheckbox); + + panel.add(inputPanel, BorderLayout.CENTER); + panel.add(controlPanel, BorderLayout.SOUTH); + + // Nur Zahlen in den Textfeldern erlauben + DocumentFilter numberFilter = new DocumentFilter() { + @Override + public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException { + if (string.matches("\\d*")) { + super.insertString(fb, offset, string, attr); + } + } + + @Override + public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException { + if (text.matches("\\d*")) { + super.replace(fb, offset, length, text, attrs); + } + } + }; + + // FocusListener für automatische Null + FocusAdapter focusAdapter = new FocusAdapter() { + @Override + public void focusLost(FocusEvent e) { + JTextField field = (JTextField) e.getComponent(); + if (field.getText().trim().isEmpty()) { + field.setText("0"); + } + } + }; + + // Anwenden auf alle Felder + hoursField.addFocusListener(focusAdapter); + minutesField.addFocusListener(focusAdapter); + secondsField.addFocusListener(focusAdapter); + + ((AbstractDocument) hoursField.getDocument()).setDocumentFilter(numberFilter); + ((AbstractDocument) minutesField.getDocument()).setDocumentFilter(numberFilter); + ((AbstractDocument) secondsField.getDocument()).setDocumentFilter(numberFilter); + + // Countdown Logik + CountdownLogic countdown = new CountdownLogic(hoursField, minutesField, secondsField, + startButton, soundCheckbox); + startButton.addActionListener(e -> countdown.toggleStartStop()); + resetButton.addActionListener(e -> countdown.reset()); + + return panel; + } + + private static JPanel createAlarmPanel() { + JPanel panel = new JPanel(new BorderLayout()); + panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + + // Zeit-Eingabe Panel + JPanel inputPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 15, 0)); + + // Stunden Input + JPanel hoursPanel = new JPanel(new BorderLayout()); + JLabel hoursLabel = new JLabel("Hours", SwingConstants.CENTER); + JTextField hoursField = new JTextField("0", 3); + hoursField.setHorizontalAlignment(SwingConstants.CENTER); + hoursField.setFont(new Font("Arial", Font.BOLD, 24)); + hoursPanel.add(hoursLabel, BorderLayout.NORTH); + hoursPanel.add(hoursField, BorderLayout.CENTER); + + // Minuten Input + JPanel minutesPanel = new JPanel(new BorderLayout()); + JLabel minutesLabel = new JLabel("Minutes", SwingConstants.CENTER); + JTextField minutesField = new JTextField("0", 3); + minutesField.setHorizontalAlignment(SwingConstants.CENTER); + minutesField.setFont(new Font("Arial", Font.BOLD, 24)); + minutesPanel.add(minutesLabel, BorderLayout.NORTH); + minutesPanel.add(minutesField, BorderLayout.CENTER); + + // Trennpunkte + JLabel separator = new JLabel(":", SwingConstants.CENTER); + separator.setFont(new Font("Arial", Font.BOLD, 24)); + + inputPanel.add(hoursPanel); + inputPanel.add(separator); + inputPanel.add(minutesPanel); + + // Control Panel + JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10)); + + JButton activateButton = new JButton("Activate"); + activateButton.setBackground(new Color(76, 175, 80)); + activateButton.setPreferredSize(new Dimension(100, 35)); + + JCheckBox soundCheckbox = new JCheckBox("Play sound"); + soundCheckbox.setSelected(true); + + controlPanel.add(activateButton); + controlPanel.add(soundCheckbox); + + panel.add(inputPanel, BorderLayout.CENTER); + panel.add(controlPanel, BorderLayout.SOUTH); + + // Nur Zahlen in den Textfeldern erlauben + DocumentFilter numberFilter = new DocumentFilter() { + @Override + public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException { + if (string.matches("\\d*")) { + super.insertString(fb, offset, string, attr); + } + } + + @Override + public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException { + if (text.matches("\\d*")) { + super.replace(fb, offset, length, text, attrs); + } + } + }; + + ((AbstractDocument) hoursField.getDocument()).setDocumentFilter(numberFilter); + ((AbstractDocument) minutesField.getDocument()).setDocumentFilter(numberFilter); + + // Alarm Logik + AlarmLogic alarm = new AlarmLogic(hoursField, minutesField, activateButton, soundCheckbox); + activateButton.addActionListener(e -> alarm.toggleActivation()); + + return panel; + } + + private static JPanel createNotesPanel() { + JPanel panel = new JPanel(new BorderLayout()); + panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + + // Erstellt das Textfeld + JTextArea notesArea = new JTextArea(); + notesArea.setLineWrap(true); + notesArea.setWrapStyleWord(true); + notesArea.setFont(new Font("Arial", Font.PLAIN, 12)); + + // Fügt Scrollbalken hinzu + JScrollPane scrollPane = new JScrollPane(notesArea); + + panel.add(scrollPane, BorderLayout.CENTER); + + return panel; + } + + private static JPanel createTodoPanel() { + JPanel panel = new JPanel(new BorderLayout()); + panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + + // ToDo Liste + DefaultListModel todoListModel = new DefaultListModel<>(); + JList todoList = new JList<>(todoListModel); + todoList.setCellRenderer(new CheckboxListCellRenderer()); + todoList.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + int index = todoList.locationToIndex(e.getPoint()); + if (index >= 0) { + JCheckBox cb = todoListModel.getElementAt(index); + cb.setSelected(!cb.isSelected()); + todoList.repaint(); + } + } + }); + + // Input Panel für neue ToDos + JPanel inputPanel = new JPanel(new BorderLayout()); + JTextField todoInput = new JTextField(); + JButton addButton = new JButton("Add"); + addButton.setPreferredSize(new Dimension(60, 25)); + + // Optional: Tooltip hinzufügen + addButton.setToolTipText("Add new todo item"); + + // Button zum Hinzufügen neuer ToDos + ActionListener addTodo = e -> { + String text = todoInput.getText().trim(); + if (!text.isEmpty()) { + JCheckBox newTodo = new JCheckBox(text); + todoListModel.addElement(newTodo); + todoInput.setText(""); + } + }; + + addButton.addActionListener(addTodo); + todoInput.addActionListener(addTodo); // Erlaubt Enter-Taste + + inputPanel.add(todoInput, BorderLayout.CENTER); + inputPanel.add(addButton, BorderLayout.EAST); + + // Button zum Entfernen erledigter ToDos + JButton clearButton = new JButton("Clear completed"); + clearButton.addActionListener(e -> { + for (int i = todoListModel.size() - 1; i >= 0; i--) { + if (todoListModel.getElementAt(i).isSelected()) { + todoListModel.removeElementAt(i); + } + } + }); + + // Layout zusammensetzen + panel.add(new JScrollPane(todoList), BorderLayout.CENTER); + panel.add(inputPanel, BorderLayout.NORTH); + panel.add(clearButton, BorderLayout.SOUTH); + + return panel; + } + + private static class CheckboxListCellRenderer extends JCheckBox implements ListCellRenderer { + @Override + public Component getListCellRendererComponent(JList list, JCheckBox value, + int index, boolean isSelected, boolean cellHasFocus) { + setSelected(value.isSelected()); + setText(value.getText()); + setBackground(isSelected ? list.getSelectionBackground() : list.getBackground()); + setForeground(isSelected ? list.getSelectionForeground() : list.getForeground()); + return this; + } + } + + private static void changeTitleDialog() { + String newTitle = JOptionPane.showInputDialog(frame, + "Neuen Titel eingeben:", + currentTitle); + + if (newTitle != null) { + currentTitle = newTitle.trim(); + updateWindowTitle(); + } + } + + private static void updateWindowTitle() { + if (currentTitle.isEmpty()) { + frame.setTitle(baseTitle); + } else { + frame.setTitle(baseTitle + " - " + currentTitle); + } + } +} \ No newline at end of file diff --git a/src/StopwatchLogic.java b/src/StopwatchLogic.java new file mode 100644 index 0000000..ea72a89 --- /dev/null +++ b/src/StopwatchLogic.java @@ -0,0 +1,200 @@ +import javax.swing.*; +import java.util.List; +import java.util.ArrayList; +import java.awt.Color; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.awt.Font; +import java.awt.Dimension; + +public class StopwatchLogic { + private JLabel timeLabel; + private JPanel timelinePanel; + private JButton startButton; + private Timer timer; + private long startTime; + private long elapsedTime; + private boolean isRunning; + private List timeMarkers; + + // Angepasste Konstanten für kompakteres Layout + private static final int MIN_MARKER_DISTANCE = 70; + private static final int START_X = 20; + private static final int TIMELINE_HEIGHT = 65; // Reduziert von 75 + private static final int LINE_Y = 25; // Höher positioniert (war 30) + private static final int ACTION_LABEL_Y = 3; // Höher positioniert (war 5) + private static final int TIME_LABEL_Y = 40; // Höher positioniert (war 48) + + public StopwatchLogic(JLabel timeLabel, JPanel timelinePanel, JButton startButton) { + this.timeLabel = timeLabel; + this.timelinePanel = timelinePanel; + this.startButton = startButton; + this.timeMarkers = new ArrayList<>(); + + timer = new Timer(10, e -> { + if (isRunning) { + elapsedTime = System.currentTimeMillis() - startTime; + updateDisplay(); + } + }); + } + + public void toggleStartStop() { + if (isRunning) { + timer.stop(); + addTimeMarker("Pause"); + startButton.setText("Start"); + startButton.setBackground(new Color(76, 175, 80)); // Grün + } else { + if (elapsedTime == 0) { + startTime = System.currentTimeMillis(); + addTimeMarker("Start"); + } else { + startTime = System.currentTimeMillis() - elapsedTime; + addTimeMarker("Resume"); + } + timer.start(); + startButton.setText("Pause"); + startButton.setBackground(new Color(255, 152, 0)); // Orange + } + isRunning = !isRunning; + } + + public void reset() { + timer.stop(); + isRunning = false; + elapsedTime = 0; + timeMarkers.clear(); + startButton.setText("Start"); + startButton.setBackground(new Color(76, 175, 80)); + updateDisplay(); + updateTimeline(); + } + + private void updateDisplay() { + long hours = (elapsedTime / 3600000); + long minutes = (elapsedTime / 60000) % 60; + long seconds = (elapsedTime / 1000) % 60; + + timeLabel.setText(String.format("%02d:%02d:%02d", hours, minutes, seconds)); + } + + private void addTimeMarker(String action) { + TimeMarker marker = new TimeMarker(action, elapsedTime); + timeMarkers.add(marker); + updateTimeline(); + } + + private void updateTimeline() { + timelinePanel.removeAll(); + + int markerCount = timeMarkers.size(); + int minWidth = timelinePanel.getParent().getWidth() - 40; + int requiredWidth = Math.max(minWidth, markerCount * MIN_MARKER_DISTANCE + 100); + + int lastX = START_X; + + for (int i = 0; i < timeMarkers.size(); i++) { + TimeMarker currentMarker = timeMarkers.get(i); + int x = START_X + (int)((currentMarker.time * (requiredWidth - 70)) / Math.max(elapsedTime, 1000)); + + if (x - lastX < MIN_MARKER_DISTANCE) { + x = lastX + MIN_MARKER_DISTANCE; + } + + if (i % 2 == 0 && i < timeMarkers.size() - 1) { + TimeMarker nextMarker = timeMarkers.get(i + 1); + int nextX = START_X + (int)((nextMarker.time * (requiredWidth - 70)) / Math.max(elapsedTime, 1000)); + nextX = Math.max(nextX, x + MIN_MARKER_DISTANCE); + + // Grüner Block + JPanel timeBlock = new JPanel(); + timeBlock.setBackground(new Color(76, 175, 80, 50)); + timeBlock.setBounds(x, LINE_Y - 5, nextX - x, 10); + timelinePanel.add(timeBlock); + + // Dauer-Label + long durationSeconds = (nextMarker.time - currentMarker.time) / 1000; + String durationText = formatDurationDetailed(durationSeconds); + JLabel durationLabel = new JLabel(durationText); + durationLabel.setHorizontalAlignment(SwingConstants.CENTER); + durationLabel.setFont(new Font("Arial", Font.PLAIN, 10)); + int labelWidth = 70; + int labelX = x + (nextX - x) / 2 - labelWidth / 2; + durationLabel.setBounds(labelX, LINE_Y + 5, labelWidth, 15); + timelinePanel.add(durationLabel); + } + + // Aktion-Label (über der Linie) + JLabel actionLabel = new JLabel(currentMarker.action); + actionLabel.setHorizontalAlignment(SwingConstants.CENTER); + actionLabel.setFont(new Font("Arial", Font.PLAIN, 11)); + actionLabel.setBounds(x - 30, ACTION_LABEL_Y, 60, 20); + timelinePanel.add(actionLabel); + + // Zeitstempel-Label (unter der Linie) + JLabel timeLabel = new JLabel(currentMarker.getClockTime()); + timeLabel.setHorizontalAlignment(SwingConstants.CENTER); + timeLabel.setFont(new Font("Arial", Font.PLAIN, 11)); + timeLabel.setBounds(x - 35, TIME_LABEL_Y, 70, 20); + timelinePanel.add(timeLabel); + + // Vertikaler Strich + JPanel tick = new JPanel(); + tick.setBackground(Color.BLACK); + tick.setBounds(x, LINE_Y - 5, 1, 10); + timelinePanel.add(tick); + + lastX = x; + } + + // Horizontale Linie + JPanel line = new JPanel(); + line.setBackground(Color.GRAY); + line.setBounds(START_X, LINE_Y, requiredWidth - 40, 1); + timelinePanel.add(line); + + timelinePanel.setPreferredSize(new Dimension(requiredWidth, TIMELINE_HEIGHT)); + timelinePanel.revalidate(); + timelinePanel.repaint(); + } + + // Neue detaillierte Formatierungsmethode + private String formatDurationDetailed(long seconds) { + if (seconds >= 3600) { + long hours = seconds / 3600; + long minutes = (seconds % 3600) / 60; + long secs = seconds % 60; + if (minutes > 0 || secs > 0) { + return String.format("%dh %02d:%02d", hours, minutes, secs); + } + return hours + "h"; + } else if (seconds >= 60) { + long minutes = seconds / 60; + long secs = seconds % 60; + if (secs > 0) { + return String.format("%d:%02dmin", minutes, secs); + } + return minutes + "min"; + } else { + return seconds + "s"; + } + } + + private static class TimeMarker { + private String action; + private long time; + private String clockTime; + + public TimeMarker(String action, long time) { + this.action = action; + this.time = time; + SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); + this.clockTime = sdf.format(new Date()); + } + + public String getClockTime() { + return clockTime; + } + } +} \ No newline at end of file