From 31302dd0d5c2b27c053d2986d8ed54b2f6f0e2fd Mon Sep 17 00:00:00 2001 From: Yuri Tatishchev Date: Mon, 24 Mar 2025 23:18:10 -0700 Subject: [PATCH] hw3: prototype completed --- hw3/.idea/misc.xml | 2 +- hw3/src/Photo.java | 10 -- hw3/src/PhotoAlbumApp.java | 18 +++ hw3/src/controller/PhotoAlbumController.java | 100 ++++++++++++++ hw3/src/{ => iterator}/AlbumIterator.java | 8 +- hw3/src/iterator/PhotoIterator.java | 49 +++++++ hw3/src/model/Photo.java | 49 +++++++ hw3/src/model/PhotoAlbumModel.java | 75 +++++++++++ hw3/src/strategy/SortByDate.java | 14 ++ hw3/src/strategy/SortByName.java | 14 ++ hw3/src/strategy/SortBySize.java | 14 ++ hw3/src/{ => strategy}/SortingStrategy.java | 3 +- hw3/src/view/PhotoAlbumView.java | 131 +++++++++++++++++++ 13 files changed, 474 insertions(+), 13 deletions(-) delete mode 100644 hw3/src/Photo.java create mode 100644 hw3/src/PhotoAlbumApp.java create mode 100644 hw3/src/controller/PhotoAlbumController.java rename hw3/src/{ => iterator}/AlbumIterator.java (73%) create mode 100644 hw3/src/iterator/PhotoIterator.java create mode 100644 hw3/src/model/Photo.java create mode 100644 hw3/src/model/PhotoAlbumModel.java create mode 100644 hw3/src/strategy/SortByDate.java create mode 100644 hw3/src/strategy/SortByName.java create mode 100644 hw3/src/strategy/SortBySize.java rename hw3/src/{ => strategy}/SortingStrategy.java (84%) create mode 100644 hw3/src/view/PhotoAlbumView.java diff --git a/hw3/.idea/misc.xml b/hw3/.idea/misc.xml index f03c948..81a8e88 100644 --- a/hw3/.idea/misc.xml +++ b/hw3/.idea/misc.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/hw3/src/Photo.java b/hw3/src/Photo.java deleted file mode 100644 index b51e4ee..0000000 --- a/hw3/src/Photo.java +++ /dev/null @@ -1,10 +0,0 @@ -import java.util.Date; - -public class Photo { - private String name; - private String filePath; - private Date dateAdded; - private long fileSize; - - // Constructor, getters, and setters -} diff --git a/hw3/src/PhotoAlbumApp.java b/hw3/src/PhotoAlbumApp.java new file mode 100644 index 0000000..dccdb7f --- /dev/null +++ b/hw3/src/PhotoAlbumApp.java @@ -0,0 +1,18 @@ +import controller.PhotoAlbumController; +import model.PhotoAlbumModel; +import view.PhotoAlbumView; + +public class PhotoAlbumApp { + public static void main(String[] args) { + // Create MVC components + PhotoAlbumModel model = new PhotoAlbumModel(); + PhotoAlbumView view = new PhotoAlbumView(); + PhotoAlbumController controller = new PhotoAlbumController(model, view); + + // Initialize view with the controller + view.setController(controller); + + // Display the main window + javax.swing.SwingUtilities.invokeLater(() -> view.setVisible(true)); + } +} \ No newline at end of file diff --git a/hw3/src/controller/PhotoAlbumController.java b/hw3/src/controller/PhotoAlbumController.java new file mode 100644 index 0000000..e8963b2 --- /dev/null +++ b/hw3/src/controller/PhotoAlbumController.java @@ -0,0 +1,100 @@ +package controller; + +import model.Photo; +import model.PhotoAlbumModel; +import strategy.SortByDate; +import strategy.SortByName; +import strategy.SortBySize; +import view.PhotoAlbumView; + +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import java.io.File; +import java.util.Date; +import java.util.List; + +public class PhotoAlbumController { + private PhotoAlbumModel model; + private PhotoAlbumView view; + + public PhotoAlbumController(PhotoAlbumModel model, PhotoAlbumView view) { + this.model = model; + this.view = view; + } + + public PhotoAlbumModel getModel() { + return model; + } + + public void handleAddPhoto() { + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setFileFilter(new FileNameExtensionFilter("Image files", "jpg", "jpeg", "png", "gif")); + + if (fileChooser.showOpenDialog(view) == JFileChooser.APPROVE_OPTION) { + File file = fileChooser.getSelectedFile(); + String name = JOptionPane.showInputDialog(view, "Enter photo name:"); + + if (name != null && !name.trim().isEmpty()) { + Photo photo = new Photo( + name, + file.getAbsolutePath(), + new Date(), + file.length() + ); + model.addPhoto(photo); + } + } + } + + public void handleDeletePhoto() { + String name = JOptionPane.showInputDialog(view, "Enter photo name to delete:"); + if (name != null && !name.trim().isEmpty()) { + model.deletePhoto(name); + } + } + + public void handleNext() { + List photos = model.getPhotos(); + if (photos.isEmpty()) return; + + Photo current = model.getCurrentPhoto(); + int index = photos.indexOf(current); + + if (index < photos.size() - 1) { + model.setCurrentPhoto(photos.get(index + 1)); + } + } + + public void handlePrevious() { + List photos = model.getPhotos(); + if (photos.isEmpty()) return; + + Photo current = model.getCurrentPhoto(); + int index = photos.indexOf(current); + + if (index > 0) { + model.setCurrentPhoto(photos.get(index - 1)); + } + } + + public void handleSort(int strategy) { + switch (strategy) { + case 0: + model.setSortingStrategy(new SortByName()); + break; + case 1: + model.setSortingStrategy(new SortByDate()); + break; + case 2: + model.setSortingStrategy(new SortBySize()); + break; + } + } + + public void handlePhotoSelection(int index) { + List photos = model.getPhotos(); + if (index >= 0 && index < photos.size()) { + model.setCurrentPhoto(photos.get(index)); + } + } +} diff --git a/hw3/src/AlbumIterator.java b/hw3/src/iterator/AlbumIterator.java similarity index 73% rename from hw3/src/AlbumIterator.java rename to hw3/src/iterator/AlbumIterator.java index 8c70dc8..5c29213 100644 --- a/hw3/src/AlbumIterator.java +++ b/hw3/src/iterator/AlbumIterator.java @@ -1,4 +1,10 @@ -public interface AlbumIterator { +package iterator; + +import model.Photo; + +import java.util.Iterator; + +public interface AlbumIterator extends Iterator { boolean hasNext(); // to check if there is a next element boolean hasPrevious(); // to check if there is a previous element Photo current(); // to get the photo at the current position diff --git a/hw3/src/iterator/PhotoIterator.java b/hw3/src/iterator/PhotoIterator.java new file mode 100644 index 0000000..5593e8a --- /dev/null +++ b/hw3/src/iterator/PhotoIterator.java @@ -0,0 +1,49 @@ +package iterator; + +import model.Photo; +import java.util.List; +import java.util.NoSuchElementException; + +public class PhotoIterator implements AlbumIterator { + private List photos; + private int currentPosition; + + public PhotoIterator(List photos) { + this.photos = photos; + this.currentPosition = 0; + } + + @Override + public boolean hasNext() { + return currentPosition < photos.size() - 1; + } + + @Override + public boolean hasPrevious() { + return currentPosition > 0; + } + + @Override + public Photo current() { + if (photos.isEmpty()) { + throw new NoSuchElementException("The photo album is empty."); + } + return photos.get(currentPosition); + } + + @Override + public Photo next() { + if (!hasNext()) { + throw new NoSuchElementException("No next photo in the album."); + } + return photos.get(++currentPosition); + } + + @Override + public Photo previous() { + if (!hasPrevious()) { + throw new NoSuchElementException("No previous photo in the album."); + } + return photos.get(--currentPosition); + } +} diff --git a/hw3/src/model/Photo.java b/hw3/src/model/Photo.java new file mode 100644 index 0000000..d0727f0 --- /dev/null +++ b/hw3/src/model/Photo.java @@ -0,0 +1,49 @@ +package model; + +import java.util.Date; + +public class Photo { + private String name; + private String filePath; + private Date dateAdded; + private long fileSize; + + public Photo(String name, String filePath, Date dateAdded, long fileSize) { + this.name = name; + this.filePath = filePath; + this.dateAdded = dateAdded; + this.fileSize = fileSize; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getFilePath() { + return filePath; + } + + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + public Date getDateAdded() { + return dateAdded; + } + + public void setDateAdded(Date dateAdded) { + this.dateAdded = dateAdded; + } + + public long getFileSize() { + return fileSize; + } + + public void setFileSize(long fileSize) { + this.fileSize = fileSize; + } +} diff --git a/hw3/src/model/PhotoAlbumModel.java b/hw3/src/model/PhotoAlbumModel.java new file mode 100644 index 0000000..147adfb --- /dev/null +++ b/hw3/src/model/PhotoAlbumModel.java @@ -0,0 +1,75 @@ +package model; + +import strategy.SortingStrategy; +import iterator.AlbumIterator; +import java.util.*; + +public class PhotoAlbumModel { + private List photos; + private SortingStrategy sortingStrategy; + private List listeners; + private Photo currentPhoto; + + public interface ModelChangeListener { + void onModelChanged(); + } + + public PhotoAlbumModel() { + photos = new ArrayList<>(); + listeners = new ArrayList<>(); + } + + public void addPhoto(Photo photo) { + photos.add(photo); + if (photos.size() == 1) { + currentPhoto = photo; + } + sortPhotos(); + notifyListeners(); + } + + public void deletePhoto(String name) { + photos.removeIf(photo -> photo.getName().equals(name)); + if (currentPhoto != null && currentPhoto.getName().equals(name)) { + currentPhoto = photos.isEmpty() ? null : photos.get(0); + } + notifyListeners(); + } + + public void setSortingStrategy(SortingStrategy strategy) { + this.sortingStrategy = strategy; + sortPhotos(); + notifyListeners(); + } + + private void sortPhotos() { + if (sortingStrategy != null) { + photos = sortingStrategy.sort(photos); + } + } + + public void addListener(ModelChangeListener listener) { + listeners.add(listener); + } + + private void notifyListeners() { + for (ModelChangeListener listener : listeners) { + listener.onModelChanged(); + } + } + + public List getPhotos() { + return new ArrayList<>(photos); + } + + public Photo getCurrentPhoto() { + return currentPhoto; + } + + public void setCurrentPhoto(Photo photo) { + if (photos.contains(photo)) { + currentPhoto = photo; + notifyListeners(); + } + } +} diff --git a/hw3/src/strategy/SortByDate.java b/hw3/src/strategy/SortByDate.java new file mode 100644 index 0000000..053786c --- /dev/null +++ b/hw3/src/strategy/SortByDate.java @@ -0,0 +1,14 @@ +package strategy; + +import model.Photo; + +import java.util.Comparator; +import java.util.List; + +public class SortByDate implements SortingStrategy { + @Override + public List sort(List photos) { + photos.sort(Comparator.comparing(Photo::getDateAdded)); + return photos; + } +} diff --git a/hw3/src/strategy/SortByName.java b/hw3/src/strategy/SortByName.java new file mode 100644 index 0000000..3b8d23c --- /dev/null +++ b/hw3/src/strategy/SortByName.java @@ -0,0 +1,14 @@ +package strategy; + +import model.Photo; + +import java.util.Comparator; +import java.util.List; + +public class SortByName implements SortingStrategy { + @Override + public List sort(List photos) { + photos.sort(Comparator.comparing(Photo::getName)); + return photos; + } +} diff --git a/hw3/src/strategy/SortBySize.java b/hw3/src/strategy/SortBySize.java new file mode 100644 index 0000000..69822ca --- /dev/null +++ b/hw3/src/strategy/SortBySize.java @@ -0,0 +1,14 @@ +package strategy; + +import model.Photo; + +import java.util.Comparator; +import java.util.List; + +public class SortBySize implements SortingStrategy { + @Override + public List sort(List photos) { + photos.sort(Comparator.comparing(Photo::getFileSize)); + return photos; + } +} diff --git a/hw3/src/SortingStrategy.java b/hw3/src/strategy/SortingStrategy.java similarity index 84% rename from hw3/src/SortingStrategy.java rename to hw3/src/strategy/SortingStrategy.java index 8a68125..83b209f 100644 --- a/hw3/src/SortingStrategy.java +++ b/hw3/src/strategy/SortingStrategy.java @@ -1,6 +1,7 @@ +package strategy; + import java.util.List; public interface SortingStrategy { List sort(List photos); - } diff --git a/hw3/src/view/PhotoAlbumView.java b/hw3/src/view/PhotoAlbumView.java new file mode 100644 index 0000000..dd25592 --- /dev/null +++ b/hw3/src/view/PhotoAlbumView.java @@ -0,0 +1,131 @@ +package view; + +import controller.PhotoAlbumController; +import model.Photo; +import model.PhotoAlbumModel; + +import javax.swing.*; +import java.awt.*; +import java.util.List; + +public class PhotoAlbumView extends JFrame implements PhotoAlbumModel.ModelChangeListener { + private PhotoAlbumController controller; + private PhotoAlbumModel model; + + private JList photoList; + private DefaultListModel listModel; + private JLabel currentPhotoLabel; + private JButton addButton; + private JButton deleteButton; + private JButton previousButton; + private JButton nextButton; + private JComboBox sortingCombo; + + public PhotoAlbumView() { + initializeComponents(); + setupLayout(); + } + + private void initializeComponents() { + setTitle("Photo Album Manager"); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setSize(800, 600); + + listModel = new DefaultListModel<>(); + photoList = new JList<>(listModel); + currentPhotoLabel = new JLabel("No photo selected", SwingConstants.CENTER); + + addButton = new JButton("Add Photo"); + deleteButton = new JButton("Delete Photo"); + previousButton = new JButton("Previous"); + nextButton = new JButton("Next"); + + String[] sortOptions = {"Sort by Name", "Sort by Date", "Sort by Size"}; + sortingCombo = new JComboBox<>(sortOptions); + } + + private void setupLayout() { + setLayout(new BorderLayout()); + + // Left panel with photo list + JScrollPane listScrollPane = new JScrollPane(photoList); + listScrollPane.setPreferredSize(new Dimension(200, 0)); + add(listScrollPane, BorderLayout.WEST); + + // Center panel with current photo + JPanel centerPanel = new JPanel(new BorderLayout()); + centerPanel.add(currentPhotoLabel, BorderLayout.CENTER); + add(centerPanel, BorderLayout.CENTER); + + // Bottom panel with controls + JPanel controlPanel = new JPanel(); + controlPanel.add(previousButton); + controlPanel.add(nextButton); + controlPanel.add(addButton); + controlPanel.add(deleteButton); + controlPanel.add(sortingCombo); + add(controlPanel, BorderLayout.SOUTH); + } + + public void setController(PhotoAlbumController controller) { + this.controller = controller; + this.model = controller.getModel(); + model.addListener(this); + + addButton.addActionListener(e -> controller.handleAddPhoto()); + deleteButton.addActionListener(e -> controller.handleDeletePhoto()); + nextButton.addActionListener(e -> controller.handleNext()); + previousButton.addActionListener(e -> controller.handlePrevious()); + sortingCombo.addActionListener(e -> controller.handleSort(sortingCombo.getSelectedIndex())); + photoList.addListSelectionListener(e -> { + if (!e.getValueIsAdjusting()) { + int index = photoList.getSelectedIndex(); + if (index >= 0) { + controller.handlePhotoSelection(index); + } + } + }); + } + + @Override + public void onModelChanged() { + updatePhotoList(); + updateCurrentPhoto(); + } + + private void updatePhotoList() { + listModel.clear(); + List photos = model.getPhotos(); + for (Photo photo : photos) { + listModel.addElement(photo.getName()); + } + } + + private void updateCurrentPhoto() { + Photo current = model.getCurrentPhoto(); + if (current != null) { + ImageIcon icon = loadImage(current.getFilePath()); + if (icon != null) { + currentPhotoLabel.setIcon(icon); + currentPhotoLabel.setText(""); + } else { + currentPhotoLabel.setIcon(null); + currentPhotoLabel.setText("Unable to load image"); + } + } else { + currentPhotoLabel.setIcon(null); + currentPhotoLabel.setText("No photo selected"); + } + } + + private ImageIcon loadImage(String path) { + try { + ImageIcon icon = new ImageIcon(path); + Image img = icon.getImage(); + Image scaledImg = img.getScaledInstance(400, 300, Image.SCALE_SMOOTH); + return new ImageIcon(scaledImg); + } catch (Exception e) { + return null; + } + } +}