diff --git a/imageloader/.gitignore b/imageloader/.gitignore
new file mode 100644
index 0000000..d34da8a
--- /dev/null
+++ b/imageloader/.gitignore
@@ -0,0 +1,15 @@
+# ---> Java
+*.class
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+target/
diff --git a/imageloader/README.md b/imageloader/README.md
new file mode 100644
index 0000000..9bc6d9e
--- /dev/null
+++ b/imageloader/README.md
@@ -0,0 +1,2 @@
+# swing-image-loader
+
diff --git a/imageloader/pom.xml b/imageloader/pom.xml
new file mode 100644
index 0000000..227b98c
--- /dev/null
+++ b/imageloader/pom.xml
@@ -0,0 +1,17 @@
+
+
+ 4.0.0
+
+
+ 1.8
+ 1.8
+
+
+ fr.uha.gabalier
+ swing-image-loader
+ 0.1
+
+
+
\ No newline at end of file
diff --git a/imageloader/src/main/java/fr/uha/gabalier/controller/Controller.java b/imageloader/src/main/java/fr/uha/gabalier/controller/Controller.java
new file mode 100644
index 0000000..e8c33eb
--- /dev/null
+++ b/imageloader/src/main/java/fr/uha/gabalier/controller/Controller.java
@@ -0,0 +1,29 @@
+package fr.uha.gabalier.controller;
+
+import fr.uha.gabalier.view.View;
+
+public class Controller {
+
+ private T model;
+ private View view;
+
+ protected Controller(T model) {
+ this.model = model;
+ }
+
+ protected T getModel() {
+ return model;
+ }
+
+ protected void setModel(T model) {
+ this.model = model;
+ }
+
+ protected View getView() {
+ return view;
+ }
+
+ public void setView(View v) {
+ this.view = v;
+ }
+}
diff --git a/imageloader/src/main/java/fr/uha/gabalier/controller/ImageController.java b/imageloader/src/main/java/fr/uha/gabalier/controller/ImageController.java
new file mode 100644
index 0000000..f689ef8
--- /dev/null
+++ b/imageloader/src/main/java/fr/uha/gabalier/controller/ImageController.java
@@ -0,0 +1,58 @@
+package fr.uha.gabalier.controller;
+
+import fr.uha.gabalier.view.Preview;
+import fr.uha.gabalier.view.View;
+
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.List;
+
+public class ImageController extends Controller> implements ActionListener {
+
+ private int index;
+
+ public ImageController(List newModel) {
+ super(newModel);
+ this.index = 0;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent evt) {
+ switch (evt.getActionCommand()) {
+ case Preview.LEFT_COMMAND :
+ doLeft();
+ break;
+ case Preview.RIGHT_COMMAND:
+ doRight();
+ break;
+ }
+ }
+
+ private void doRight() {
+ if (getModel() == null) return;
+ if (getModel().size() == 0) return;
+ if (++index > getModel().size() - 1) index = 0;
+ getView().repaint();
+ }
+
+ private void doLeft() {
+ if (getModel() == null) return;
+ if (getModel().size() == 0) return;
+ if (--index < 0) index = getModel().size() - 1;
+ getView().repaint();
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public void resetIndex() {
+ this.index = 0;
+ }
+
+ public void setModel(List model) {
+ super.setModel(model);
+ this.index = 0;
+ }
+}
diff --git a/imageloader/src/main/java/fr/uha/gabalier/core/ImageLoader.java b/imageloader/src/main/java/fr/uha/gabalier/core/ImageLoader.java
new file mode 100644
index 0000000..f16dbfe
--- /dev/null
+++ b/imageloader/src/main/java/fr/uha/gabalier/core/ImageLoader.java
@@ -0,0 +1,70 @@
+package fr.uha.gabalier.core;
+
+import fr.uha.gabalier.util.ImageLib;
+import fr.uha.gabalier.view.Preview;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+
+public class ImageLoader extends SwingWorker, Integer> {
+
+ private final File root;
+ private Preview app;
+ private List images;
+ private final int iconWidth;
+
+ public ImageLoader(Preview app, int iconWidth, File root) {
+ this.root = root;
+ this.app = app;
+ this.iconWidth = iconWidth;
+ this.images = new ArrayList<>();
+ }
+
+ private void loadImages(File current) throws NullPointerException {
+ if (current.isFile()) {
+ final Image icon = ImageLib.createScaledImage(current, this.iconWidth, -1);
+ if (icon != null) {
+ images.add(icon);
+ publish(images.size());
+ }
+ return;
+ }
+
+ final File[] files = current.listFiles();
+ if (files == null) throw new NullPointerException();
+ for (File f : files) {
+ loadImages(f);
+ }
+ }
+
+ @Override
+ protected List doInBackground() throws Exception {
+ loadImages(root);
+ return images;
+ }
+
+ @Override
+ protected void process(List data) {
+ if (this.isCancelled()) return;
+ app.setStatus(String.valueOf(data.get(data.size() - 1)) + " images loaded");
+ }
+
+ @Override
+ protected void done() {
+ try {
+ app.setModel(this.get());
+ app.setStatus(app.getModel().size() + " images loaded");
+ } catch (CancellationException e) {
+ app.setStatus("Cancelled");
+ } catch (InterruptedException | ExecutionException e) {
+ e.printStackTrace();
+ } finally {
+ app.postLoad();
+ }
+ }
+}
diff --git a/imageloader/src/main/java/fr/uha/gabalier/util/ImageLib.java b/imageloader/src/main/java/fr/uha/gabalier/util/ImageLib.java
new file mode 100644
index 0000000..cd962ee
--- /dev/null
+++ b/imageloader/src/main/java/fr/uha/gabalier/util/ImageLib.java
@@ -0,0 +1,24 @@
+package fr.uha.gabalier.util;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+public final class ImageLib {
+
+ public static Image createScaledImage(File file, int iconWidth, int iconHeight) {
+ final BufferedImage img;
+ try {
+ if ((img = ImageIO.read(file)) != null) {
+ return img.getScaledInstance(iconWidth, iconHeight, Image.SCALE_DEFAULT);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private ImageLib() {}
+}
diff --git a/imageloader/src/main/java/fr/uha/gabalier/view/ImageView.java b/imageloader/src/main/java/fr/uha/gabalier/view/ImageView.java
new file mode 100644
index 0000000..3e25f18
--- /dev/null
+++ b/imageloader/src/main/java/fr/uha/gabalier/view/ImageView.java
@@ -0,0 +1,25 @@
+package fr.uha.gabalier.view;
+
+import fr.uha.gabalier.controller.ImageController;
+
+import java.awt.*;
+import java.util.List;
+
+public class ImageView extends View> {
+
+ public ImageView(List model) {
+ super(model);
+ this.setPreferredSize(new Dimension(Preview.VIEW_WIDTH, Preview.VIEW_HEIGHT));
+ }
+
+ @Override
+ public void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ final List model = this.getModel();
+ if (model == null) return;
+ if (model.size() == 0) return;
+
+ final int index = ((ImageController) this.getController()).getIndex();
+ g.drawImage(model.get(index), 10, 10, this);
+ }
+}
diff --git a/imageloader/src/main/java/fr/uha/gabalier/view/Preview.java b/imageloader/src/main/java/fr/uha/gabalier/view/Preview.java
new file mode 100644
index 0000000..8b9b2eb
--- /dev/null
+++ b/imageloader/src/main/java/fr/uha/gabalier/view/Preview.java
@@ -0,0 +1,130 @@
+package fr.uha.gabalier.view;
+
+import fr.uha.gabalier.controller.ImageController;
+import fr.uha.gabalier.core.ImageLoader;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Preview extends JFrame {
+
+ static final int VIEW_WIDTH = 256;
+ static final int VIEW_HEIGHT = 128;
+
+ public static final String LEFT_COMMAND = "<";
+ public static final String RIGHT_COMMAND = ">";
+ private static final int ICON_WIDTH = 256;
+
+ private List model;
+ private ImageLoader loader;
+ private ImageView imageView;
+
+ // UI elements
+ private JMenuItem loadItem;
+ private JMenuItem cancelItem;
+ private JButton rightNavButton;
+ private JButton leftNavButton;
+ private final JTextField statusText;
+
+ public Preview() throws HeadlessException {
+ model = new ArrayList<>();
+ // UI elements
+ JMenuBar menuBar = new JMenuBar();
+ setJMenuBar(menuBar);
+ JMenu fileMenu = new JMenu("File");
+ menuBar.add(fileMenu);
+
+ loadItem = new JMenuItem("Load");
+ fileMenu.add(loadItem);
+ loadItem.addActionListener(actionEvent -> doLoad());
+ cancelItem = new JMenuItem("Cancel");
+ fileMenu.add(cancelItem);
+ cancelItem.addActionListener(actionEvent -> doCancel());
+ final JMenuItem exitItem = new JMenuItem("Exit");
+ exitItem.addActionListener(actionEvent -> doExit());
+ exitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, InputEvent.CTRL_DOWN_MASK));
+ fileMenu.add(exitItem);
+
+ setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
+
+ imageView = new ImageView(model);
+ final ImageController controller = new ImageController(model);
+ imageView.setController(controller);
+ controller.setView(imageView);
+ getContentPane().add(imageView);
+
+ final JPanel navPanel = new JPanel();
+ getContentPane().add(navPanel);
+ leftNavButton = new JButton(LEFT_COMMAND);
+ leftNavButton.addActionListener(controller);
+ navPanel.add(leftNavButton);
+ rightNavButton = new JButton(RIGHT_COMMAND);
+ rightNavButton.addActionListener(controller);
+ navPanel.add(rightNavButton);
+
+ final JPanel statusPanel = new JPanel();
+ getContentPane().add(statusPanel);
+ statusText = new JTextField("0000 images loaded");
+ statusPanel.add(new JLabel("status :"));
+ statusPanel.add(statusText);
+
+ pack();
+ setVisible(true);
+ }
+
+ public void setModel(List images) {
+ this.model.clear();
+ this.model.addAll(images);
+ ((ImageController) imageView.getController()).resetIndex();
+ }
+
+ public List getModel() {
+ return model;
+ }
+
+ public void setStatus(String status) {
+ this.statusText.setText(status);
+ }
+
+ private void doLoad() {
+ final JFileChooser fc = new JFileChooser();
+ fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+ final int ret = fc.showOpenDialog(this);
+ if (ret == JFileChooser.APPROVE_OPTION) {
+ loader = new ImageLoader(this, ICON_WIDTH, fc.getSelectedFile());
+ preLoad();
+ loader.execute();
+ }
+ }
+
+ private void doExit() {
+ System.exit(0);
+ }
+
+ private void doCancel() {
+ loader.cancel(true);
+ }
+
+ public void preLoad() {
+ loadItem.setEnabled(false);
+ cancelItem.setEnabled(true);
+ rightNavButton.setEnabled(false);
+ leftNavButton.setEnabled(false);
+ }
+
+ public void postLoad() {
+ loadItem.setEnabled(true);
+ cancelItem.setEnabled(false);
+ rightNavButton.setEnabled(true);
+ leftNavButton.setEnabled(true);
+ }
+
+ public static void main(String[] args) {
+ EventQueue.invokeLater(Preview::new);
+ }
+
+}
diff --git a/imageloader/src/main/java/fr/uha/gabalier/view/View.java b/imageloader/src/main/java/fr/uha/gabalier/view/View.java
new file mode 100644
index 0000000..6ef11ea
--- /dev/null
+++ b/imageloader/src/main/java/fr/uha/gabalier/view/View.java
@@ -0,0 +1,27 @@
+package fr.uha.gabalier.view;
+
+import fr.uha.gabalier.controller.Controller;
+
+import javax.swing.*;
+
+public class View extends JPanel {
+
+ private final T model;
+ private Controller controller;
+
+ protected View(T model) {
+ this.model = model;
+ }
+
+ protected T getModel() {
+ return model;
+ }
+
+ protected Controller getController() {
+ return controller;
+ }
+
+ protected void setController(Controller controller) {
+ this.controller = controller;
+ }
+}