Skip to content

Commit

Permalink
display image files in presentation #232
Browse files Browse the repository at this point in the history
currently the files won't be saved anywhere by SDB but are ephemeral, so the users have to take care of saving the images themselves
  • Loading branch information
mathisdt committed Jan 14, 2024
1 parent 4c0ab56 commit af285fd
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 112 deletions.
98 changes: 70 additions & 28 deletions src/main/java/org/zephyrsoft/sdb2/gui/MainWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
import org.zephyrsoft.sdb2.model.AddressablePart;
import org.zephyrsoft.sdb2.model.ExportFormat;
import org.zephyrsoft.sdb2.model.FilterTypeEnum;
import org.zephyrsoft.sdb2.model.ImageSong;
import org.zephyrsoft.sdb2.model.ScreenContentsEnum;
import org.zephyrsoft.sdb2.model.SelectableDisplay;
import org.zephyrsoft.sdb2.model.Song;
Expand Down Expand Up @@ -318,6 +319,7 @@ public class MainWindow extends JFrame implements UIScroller, OnIndexChangeListe

private final ConcurrentMap<String, UndoManager> songEditingUndoManagers = new ConcurrentHashMap<>();
private final ConcurrentMap<String, KeyListener> songEditingKeyListeners = new ConcurrentHashMap<>();
private JButton btnAddImage;

@Override
public List<PartButtonGroup> getUIParts() {
Expand Down Expand Up @@ -1346,6 +1348,27 @@ private void setDefaultDividerLocation() {
splitPanePresentDividerLocationSet = true;
}
}

protected void handleAddImage() {
// select target
JFileChooser chooser = new JFileChooser();
chooser.setDialogTitle("choose image file to add");
CustomFileFilter filter = new CustomFileFilter("Images", ".png", ".jpg", ".jpeg", ".gif");
chooser.addChoosableFileFilter(filter);
chooser.setFileFilter(filter);
chooser.setApproveButtonText("Add Image");
int result = chooser.showOpenDialog(MainWindow.this);

if (result == JFileChooser.APPROVE_OPTION) {
try {
File target = chooser.getSelectedFile();
presentModel.addSong(new ImageSong(target));
presentList.setSelectedIndex(presentModel.getSize() - 1);
} catch (Throwable ex) {
handleError(ex);
}
}
}

protected void handleSongUnselect() {
if (presentListSelected != null) {
Expand Down Expand Up @@ -1471,40 +1494,46 @@ protected void handleSongPresent() {
* index, includes title as index 0 if the title is displayed
*/
protected void presentSong(Song song, SongPresentationPosition presentationPosition) {
Presentable presentable = song instanceof ImageSong imageSong
? new Presentable(null, imageSong.getFile().getAbsolutePath())
: new Presentable(song, null);
// not in a "contentChange" block because else the sections wouldn't be displayed:
boolean success = controller.present(new Presentable(song, null), presentationPosition);
boolean success = controller.present(presentable, presentationPosition);

controller.contentChange(() -> {
controller.stopSlideShow();
if (success) {
clearSectionButtons();
List<AddressablePart> parts = controller.getParts();
Boolean showTitle = settingsModel.get(SettingKey.SHOW_TITLE, Boolean.class);
int partIndex = showTitle ? 0 : 1;
for (AddressablePart part : parts) {
PartButtonGroup buttonGroup = new PartButtonGroup(part, partIndex, controller, this);
panelSectionButtons.add(buttonGroup, panelSectionButtonsHints);
listSectionButtons.add(buttonGroup);
partIndex++;
}

// mark active line
if (!listSectionButtons.isEmpty()) {
listSectionButtons
.get(presentationPosition != null && presentationPosition.getPartIndex() != null
? Math.max(0, presentationPosition.getPartIndex() - (showTitle ? 0 : 1))
: 0)
.setActiveLine(presentationPosition != null && presentationPosition.getLineIndex() != null
? presentationPosition.getLineIndex()
: 0);
if (controller.hasParts()) {
List<AddressablePart> parts = controller.getParts();
Boolean showTitle = settingsModel.get(SettingKey.SHOW_TITLE, Boolean.class);
int partIndex = showTitle ? 0 : 1;
for (AddressablePart part : parts) {
PartButtonGroup buttonGroup = new PartButtonGroup(part, partIndex, controller, this);
panelSectionButtons.add(buttonGroup, panelSectionButtonsHints);
listSectionButtons.add(buttonGroup);
partIndex++;
}

// mark active line
if (!listSectionButtons.isEmpty()) {
listSectionButtons
.get(presentationPosition != null && presentationPosition.getPartIndex() != null
? Math.max(0, presentationPosition.getPartIndex() - (showTitle ? 0 : 1))
: 0)
.setActiveLine(presentationPosition != null && presentationPosition.getLineIndex() != null
? presentationPosition.getLineIndex()
: 0);
}

// add empty component to consume any space that is left (so the parts appear at the top of the
// scrollpane view)
panelSectionButtons.add(new JLabel(""), panelSectionButtonsLastRowHints);

panelSectionButtons.revalidate();
panelSectionButtons.repaint();
btnJumpToPresented.setEnabled(true);
}

// add empty component to consume any space that is left (so the parts appear at the top of the
// scrollpane view)
panelSectionButtons.add(new JLabel(""), panelSectionButtonsLastRowHints);

panelSectionButtons.revalidate();
panelSectionButtons.repaint();
btnJumpToPresented.setEnabled(true);
}
});
}
Expand Down Expand Up @@ -2158,6 +2187,18 @@ public void keyReleased(KeyEvent e) {
gblPanelSelectedSongListButtons.rowWeights = new double[] { 1.0, 0.0, 0.0, 0.0, 1.0 };
selectedSongListButtons.setLayout(gblPanelSelectedSongListButtons);

btnAddImage = new JButton("");
btnAddImage.addActionListener(safeAction(e -> handleAddImage()));
btnAddImage.setToolTipText("Add Image");
btnAddImage.setIcon(ResourceTools.getIcon(getClass(), "/org/zephyrsoft/sdb2/plus.png"));
GridBagConstraints gbc_button = new GridBagConstraints();
gbc_button.anchor = GridBagConstraints.NORTH;
gbc_button.fill = GridBagConstraints.HORIZONTAL;
gbc_button.insets = new Insets(0, 0, 5, 0);
gbc_button.gridx = 0;
gbc_button.gridy = 0;
selectedSongListButtons.add(btnAddImage, gbc_button);

btnUp = new JButton("");
btnUp.addActionListener(safeAction(e -> handleSongUp()));
btnUp.setToolTipText("Up");
Expand Down Expand Up @@ -2201,6 +2242,7 @@ public void keyReleased(KeyEvent e) {
btnUnselectAll.addActionListener(safeAction(e -> handleSongUnselectAll()));
btnUnselectAll.setIcon(ResourceTools.getIcon(getClass(), "/org/zephyrsoft/sdb2/JXErrorPane16Double.png"));
GridBagConstraints gbcBtnUnselectAll = new GridBagConstraints();
gbcBtnUnselectAll.insets = new Insets(0, 0, 5, 0);
gbcBtnUnselectAll.anchor = GridBagConstraints.SOUTH;
gbcBtnUnselectAll.fill = GridBagConstraints.HORIZONTAL;
gbcBtnUnselectAll.gridx = 0;
Expand Down
192 changes: 109 additions & 83 deletions src/main/java/org/zephyrsoft/sdb2/gui/SongCell.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,9 @@
*/
package org.zephyrsoft.sdb2.gui;

import java.awt.Color;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.*;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.*;
import javax.swing.border.EmptyBorder;

import org.zephyrsoft.sdb2.model.Song;
Expand All @@ -31,81 +27,111 @@
* List entry for a {@link Song}.
*/
public class SongCell extends JPanel {

private static final long serialVersionUID = 6861947343987825552L;

private JLabel songTitle;
private JLabel firstLine;

/**
* Constructor.
*
* @param leftSpace
* space to insert left of the second line, in pixels
*/
public SongCell(Integer leftSpace) {
GridBagLayout gridBagLayout = new GridBagLayout();
gridBagLayout.columnWidths = new int[] { 0, 0 };
gridBagLayout.rowHeights = new int[] { 0, 0, 0 };
gridBagLayout.columnWeights = new double[] { 1.0, Double.MIN_VALUE };
gridBagLayout.rowWeights = new double[] { 0.0, 0.0, Double.MIN_VALUE };
setLayout(gridBagLayout);

songTitle = new JLabel("<SONG TITLE>");
songTitle.setBorder(new EmptyBorder(2, 3, 0, 3));
GridBagConstraints gbcSongTitle = new GridBagConstraints();
gbcSongTitle.fill = GridBagConstraints.HORIZONTAL;
gbcSongTitle.anchor = GridBagConstraints.NORTH;
gbcSongTitle.gridx = 0;
gbcSongTitle.gridy = 0;
add(songTitle, gbcSongTitle);

firstLine = new JLabel("<FIRST LINE>");
firstLine.setBorder(new EmptyBorder(0, 23, 2, 3));
if (leftSpace != null) {
firstLine.setBorder(new EmptyBorder(0, leftSpace + 3, 2, 3));
}
firstLine.setFont(new Font("SansSerif", Font.ITALIC, 10));
GridBagConstraints gbcFirstLine = new GridBagConstraints();
gbcFirstLine.anchor = GridBagConstraints.NORTH;
gbcFirstLine.fill = GridBagConstraints.HORIZONTAL;
gbcFirstLine.gridx = 0;
gbcFirstLine.gridy = 1;
add(firstLine, gbcFirstLine);
}

public String getSongTitle() {
return songTitle.getText();
}

public void setSongTitle(String text) {
if (StringTools.isEmpty(text)) {
// prevent a 0 pixel height:
songTitle.setText(" ");
} else {
songTitle.setText(text);
}
}

public String getFirstLine() {
return firstLine.getText();
}

public void setFirstLine(String text) {
if (StringTools.isEmpty(text)) {
// prevent a 0 pixel height:
firstLine.setText(" ");
} else {
firstLine.setText(text);
}
}

@Override
public void setForeground(Color color) {
if (songTitle != null && firstLine != null) {
songTitle.setForeground(color);
firstLine.setForeground(color);
}
}


private static final long serialVersionUID = 6861947343987825552L;
public static final int TITLE_BOTTOM_SPACE = 5;
public static final int FIRSTLINE_BOTTOM_SPACE = 2;

private JLabel songTitle;
private JLabel firstLine;
private JLabel image;

/**
* Constructor.
*
* @param leftSpace space to insert left of the second line, in pixels
*/
public SongCell(Integer leftSpace) {
GridBagLayout gridBagLayout = new GridBagLayout();
gridBagLayout.columnWidths = new int[] { 0, 0, 0 };
gridBagLayout.rowHeights = new int[] { 0, 0, 0 };
gridBagLayout.columnWeights = new double[] { 0.0, 1.0, Double.MIN_VALUE };
gridBagLayout.rowWeights = new double[] { 0.0, 0.0, Double.MIN_VALUE };
setLayout(gridBagLayout);

image = new JLabel("<IMG>");
GridBagConstraints gbc_image = new GridBagConstraints();
gbc_image.gridheight = 2;
gbc_image.insets = new Insets(0, 0, 0, 10);
gbc_image.gridx = 0;
gbc_image.gridy = 0;
add(image, gbc_image);
// invisible when no image is explicitly set:
image.setVisible(false);

songTitle = new JLabel("<SONG TITLE>");
songTitle.setBorder(new EmptyBorder(2, 3, 0, 3));
GridBagConstraints gbcSongTitle = new GridBagConstraints();
gbcSongTitle.insets = new Insets(0, 0, TITLE_BOTTOM_SPACE, 0);
gbcSongTitle.fill = GridBagConstraints.HORIZONTAL;
gbcSongTitle.anchor = GridBagConstraints.NORTH;
gbcSongTitle.gridx = 1;
gbcSongTitle.gridy = 0;
add(songTitle, gbcSongTitle);

firstLine = new JLabel("<FIRST LINE>");
firstLine.setBorder(new EmptyBorder(0, 23, FIRSTLINE_BOTTOM_SPACE, 3));
if (leftSpace != null) {
firstLine.setBorder(new EmptyBorder(0, leftSpace + 3, FIRSTLINE_BOTTOM_SPACE, 3));
}
firstLine.setFont(new Font("SansSerif", Font.ITALIC, 10));
GridBagConstraints gbcFirstLine = new GridBagConstraints();
gbcFirstLine.anchor = GridBagConstraints.NORTH;
gbcFirstLine.fill = GridBagConstraints.HORIZONTAL;
gbcFirstLine.gridx = 1;
gbcFirstLine.gridy = 1;
add(firstLine, gbcFirstLine);
}

public String getSongTitle() {
return songTitle.getText();
}

public void setSongTitle(String text) {
if (StringTools.isEmpty(text)) {
// prevent a 0 pixel height:
songTitle.setText(" ");
} else {
songTitle.setText(text);
}
}

public String getFirstLine() {
return firstLine.getText();
}

public void setFirstLine(String text) {
if (StringTools.isEmpty(text)) {
// prevent a 0 pixel height:
firstLine.setText(" ");
} else {
firstLine.setText(text);
}
}

public void setImage(final String imageFile) {
if (imageFile == null) {
this.image.setVisible(false);
} else {
ImageIcon imageIcon = new ImageIcon(imageFile);
Image image = imageIcon.getImage();
int originalWidth = image.getWidth(null);
int originalHeight = image.getHeight(null);
double factor = (songTitle.getPreferredSize().getHeight() + firstLine.getPreferredSize().getHeight()
+ TITLE_BOTTOM_SPACE + FIRSTLINE_BOTTOM_SPACE) / (double) originalHeight;
image = image.getScaledInstance((int) (originalWidth * factor), (int) (originalHeight * factor), Image.SCALE_FAST);
this.image.setIcon(new ImageIcon(image));
this.image.setText("");
this.image.setVisible(true);
}
}

@Override
public void setForeground(Color color) {
if (songTitle != null && firstLine != null) {
songTitle.setForeground(color);
firstLine.setForeground(color);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.slf4j.LoggerFactory;
import org.zephyrsoft.sdb2.Feature;
import org.zephyrsoft.sdb2.gui.SongCell;
import org.zephyrsoft.sdb2.model.ImageSong;
import org.zephyrsoft.sdb2.model.Song;
import org.zephyrsoft.sdb2.model.SongParser;
import org.zephyrsoft.sdb2.service.IndexerService;
Expand Down Expand Up @@ -130,6 +131,9 @@ public Component getListCellRendererComponent(JList<? extends Song> list, Song v
if (value != null) {
ret.setSongTitle(value.getTitle() != null ? value.getTitle() : "");
ret.setFirstLine(SongParser.getFirstLyricsLine(value));
if (value instanceof ImageSong imageSong) {
ret.setImage(imageSong.getFile().getAbsolutePath());
}
}

ret.setForeground(Color.BLACK);
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/org/zephyrsoft/sdb2/model/ImageSong.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.zephyrsoft.sdb2.model;

import java.io.File;
import java.util.UUID;

public class ImageSong extends Song {
private final File file;

public ImageSong(final File file) {
super(UUID.randomUUID().toString());
this.file = file;
}

public File getFile() {
return file;
}

@Override
public String getTitle() {
return file != null ? file.getName() : "empty";
}

@Override
public String getLyrics() {
return "";
}

}
Loading

0 comments on commit af285fd

Please sign in to comment.