package comeon.ui; import com.google.common.collect.ImmutableList; import com.google.common.eventbus.Subscribe; import com.google.common.io.Resources; import com.google.inject.Inject; import com.google.inject.Singleton; import comeon.core.Core; import comeon.model.Media; import comeon.templates.Templates; import comeon.ui.actions.MediaAddedEvent; import comeon.ui.actions.MediaRemovedEvent; import comeon.ui.add.AddMediaDialog; import comeon.ui.add.AdderWorker; import comeon.ui.media.MediaPanels; import comeon.ui.menu.MenuBar; import comeon.ui.toolbar.Toolbar; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; import java.io.IOException; import java.util.*; import java.util.List; @Singleton public final class UI extends JFrame { private static final long serialVersionUID = 1L; private static final Logger LOGGER = LoggerFactory.getLogger(UI.class); public static final ResourceBundle BUNDLE = ResourceBundle.getBundle("comeon.ui.comeon"); public static final Color NEUTRAL_GREY = Color.DARK_GRAY; public static final int PREVIEW_PANEL_HEIGHT = 90; public static final int METADATA_PANEL_WIDTH = 280; public static final List<? extends Image> ICON_IMAGES = loadIcons(); private final Box previews; private final Component previewsGlue; private final JPanel editContainer; private final Core core; @Inject public UI(final Core core, final Templates templates, final MenuBar menuBar, final Toolbar toolbar) { super(BUNDLE.getString("comeon")); this.core = core; this.setIconImages(ICON_IMAGES); this.setJMenuBar(menuBar); this.setLayout(new BorderLayout()); this.setExtendedState(Frame.MAXIMIZED_BOTH); this.setMinimumSize(new Dimension(800, 600)); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.previews = new Box(BoxLayout.X_AXIS); this.previews.setMinimumSize(new Dimension(0, PREVIEW_PANEL_HEIGHT)); this.previews.setBackground(NEUTRAL_GREY); this.previews.setOpaque(true); this.previewsGlue = Box.createHorizontalGlue(); this.previews.add(previewsGlue); final JScrollPane scrollablePreviews = new JScrollPane(previews, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); this.add(scrollablePreviews, BorderLayout.SOUTH); this.editContainer = new JPanel(new CardLayout()); this.add(editContainer, BorderLayout.CENTER); this.add(toolbar, BorderLayout.NORTH); final MediaTransferHandler transferHandler = new MediaTransferHandler(templates); this.setTransferHandler(transferHandler); } public static Window findInstance() { return Arrays.stream(getWindows()).filter(w -> UI.class.equals(w.getClass())).findFirst().orElseThrow(NoSuchElementException::new); } private static List<? extends Image> loadIcons() { try { return ImmutableList.of( ImageIO.read(Resources.getResource("comeon_1024_1024.png")), ImageIO.read(Resources.getResource("comeon_512_512.png")), ImageIO.read(Resources.getResource("comeon_128_128.png")), ImageIO.read(Resources.getResource("comeon_256_256.png")), ImageIO.read(Resources.getResource("comeon_48_48.png")), ImageIO.read(Resources.getResource("comeon_16_16.png")) ); } catch (final IOException e) { return Collections.emptyList(); } } @Subscribe public void handleMediaAddedEvent(final MediaAddedEvent event) { this.refreshMedia(); } @Subscribe public void handleMediaRemovedEvent(final MediaRemovedEvent event) { this.refreshMedia(); } private void refreshMedia() { SwingUtilities.invokeLater(() -> { previews.removeAll(); editContainer.removeAll(); validate(); core.getMedia().forEach(this::add); validate(); }); } private void add(final Media media) { final MediaPanels panels = new MediaPanels(media); final JComponent previewPanel = panels.getPreviewPanel(); this.previews.remove(previewsGlue); this.previews.add(previewPanel); this.previews.add(previewsGlue); this.editContainer.add(panels.getEditPanel(), media.getFileName()); previewPanel.addMouseListener(new PreviewPanelMouseAdapter(media)); } private final class PreviewPanelMouseAdapter extends MouseAdapter { private final Media media; private PreviewPanelMouseAdapter(Media media) { this.media = media; } @Override public void mouseClicked(final MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) { SwingUtilities.invokeLater(() -> { if (e.isControlDown()) { core.removeMedia(media); } else { ((CardLayout) editContainer.getLayout()).show(editContainer, media.getFileName()); } }); } } } private final class MediaTransferHandler extends TransferHandler { private static final long serialVersionUID = 1L; private final Templates templates; public MediaTransferHandler(final Templates templates) { this.templates = templates; } @Override public boolean canImport(final TransferSupport support) { return support.isDataFlavorSupported(DataFlavor.javaFileListFlavor); } @Override public boolean importData(TransferSupport support) { try { @SuppressWarnings("unchecked") final List<File> transferData = (List<File>) support.getTransferable().getTransferData(DataFlavor.javaFileListFlavor); final File[] preselectedFiles = transferData .parallelStream() .filter(file -> file.getName().endsWith(".jpg") || file.getName().endsWith(".jpeg")) .toArray(File[]::new); SwingUtilities.invokeLater(() -> { final AddMediaDialog dialog = new AddMediaDialog(templates, preselectedFiles); final int value = dialog.showDialog(); if (value == JOptionPane.OK_OPTION) { new AdderWorker(dialog.getModel(), core).execute(); } }); return true; } catch (final UnsupportedFlavorException | IOException e) { LOGGER.warn("Failed drag & drop transfer", e); return false; } } } }