diff --git a/src/main/java/envoy/client/ui/controller/ChatScene.java b/src/main/java/envoy/client/ui/controller/ChatScene.java
index 2cf116b..c5e6909 100644
--- a/src/main/java/envoy/client/ui/controller/ChatScene.java
+++ b/src/main/java/envoy/client/ui/controller/ChatScene.java
@@ -2,7 +2,10 @@ package envoy.client.ui.controller;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
+import java.io.ByteArrayInputStream;
+import java.io.File;
import java.io.IOException;
+import java.nio.file.Files;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@@ -12,10 +15,12 @@ import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType;
+import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
+import javafx.stage.FileChooser;
import envoy.client.data.*;
import envoy.client.data.audio.AudioRecorder;
@@ -60,6 +65,9 @@ public final class ChatScene implements Restorable {
@FXML
private Button voiceButton;
+ @FXML
+ private Button attachmentButton;
+
@FXML
private Button settingsButton;
@@ -87,12 +95,15 @@ public final class ChatScene implements Restorable {
private AudioRecorder recorder;
private boolean recording;
private Attachment pendingAttachment;
- private boolean postingPermanentlyDisabled = false;
+ private boolean postingPermanentlyDisabled;
- private static final Settings settings = Settings.getInstance();
- private static final EventBus eventBus = EventBus.getInstance();
- private static final Logger logger = EnvoyLog.getLogger(ChatScene.class);
- private static final int MAX_MESSAGE_LENGTH = 255;
+ private static final Settings settings = Settings.getInstance();
+ private static final EventBus eventBus = EventBus.getInstance();
+ private static final Logger logger = EnvoyLog.getLogger(ChatScene.class);
+
+ private static final Image DEFAULT_ATTACHMENT_VIEW_IMAGE = IconUtil.loadIconThemeSensitive("attachment_present", 20);
+ private static final int MAX_MESSAGE_LENGTH = 255;
+ private static final int DEFAULT_ICON_SIZE = 16;
/**
* Initializes the appearance of certain visual components.
@@ -106,8 +117,10 @@ public final class ChatScene implements Restorable {
messageList.setCellFactory(MessageListCellFactory::new);
userList.setCellFactory(ContactListCellFactory::new);
- settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings", 16)));
- voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", 20)));
+ settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings", DEFAULT_ICON_SIZE)));
+ voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE)));
+ attachmentButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("attachment", DEFAULT_ICON_SIZE)));
+ attachmentView.setImage(DEFAULT_ATTACHMENT_VIEW_IMAGE);
// Listen to received messages
eventBus.register(MessageCreationEvent.class, e -> {
@@ -226,7 +239,7 @@ public final class ChatScene implements Restorable {
recording = false;
}
pendingAttachment = null;
- attachmentView.setVisible(false);
+ updateAttachmentView(false);
remainingChars.setVisible(true);
remainingChars
@@ -234,6 +247,7 @@ public final class ChatScene implements Restorable {
}
messageTextArea.setDisable(currentChat == null || postingPermanentlyDisabled);
voiceButton.setDisable(!recorder.isSupported());
+ attachmentButton.setDisable(false);
}
/**
@@ -266,18 +280,17 @@ public final class ChatScene implements Restorable {
recording = true;
Platform.runLater(() -> {
voiceButton.setText("Recording");
- voiceButton.setGraphic(new ImageView(IconUtil.loadIcon("microphone_recording", 24)));
+ voiceButton.setGraphic(new ImageView(IconUtil.loadIcon("microphone_recording", DEFAULT_ICON_SIZE)));
});
recorder.start();
} else {
pendingAttachment = new Attachment(recorder.finish(), AttachmentType.VOICE);
recording = false;
Platform.runLater(() -> {
- voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", 20)));
+ voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE)));
voiceButton.setText(null);
checkPostConditions(false);
- attachmentView.setImage(IconUtil.loadIconThemeSensitive("attachment_present", 20));
- attachmentView.setVisible(true);
+ updateAttachmentView(true);
});
}
} catch (final EnvoyException e) {
@@ -287,6 +300,52 @@ public final class ChatScene implements Restorable {
}).start();
}
+ @FXML
+ private void attachmentButtonClicked() {
+
+ // Display file chooser
+ final var fileChooser = new FileChooser();
+ fileChooser.setTitle("Add Attachment");
+ fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
+ fileChooser.getExtensionFilters()
+ .addAll(new FileChooser.ExtensionFilter("Pictures", "*.png", "*.jpg", "*.bmp", "*.gif"),
+ new FileChooser.ExtensionFilter("Videos", "*.mp4"),
+ new FileChooser.ExtensionFilter("All Files", "*.*"));
+ final var file = fileChooser.showOpenDialog(sceneContext.getStage());
+
+ if (file != null) {
+
+ // Check max file size
+ if (file.length() > 16E6) {
+ new Alert(AlertType.WARNING, "The selected file exceeds the size limit of 16MB!").showAndWait();
+ return;
+ }
+
+ // Get attachment type (default is document)
+ AttachmentType type = AttachmentType.DOCUMENT;
+ switch (fileChooser.getSelectedExtensionFilter().getDescription()) {
+ case "Pictures":
+ type = AttachmentType.PICTURE;
+ break;
+ case "Videos":
+ type = AttachmentType.VIDEO;
+ break;
+ }
+
+ // Create the pending attachment
+ try {
+ final var fileBytes = Files.readAllBytes(file.toPath());
+ pendingAttachment = new Attachment(fileBytes, type);
+ // Setting the preview image as image of the attachmentView
+ if (type == AttachmentType.PICTURE)
+ attachmentView.setImage(new Image(new ByteArrayInputStream(fileBytes), DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE, true, true));
+ attachmentView.setVisible(true);
+ } catch (final IOException e) {
+ new Alert(AlertType.ERROR, "The selected file could not be loaded!").showAndWait();
+ }
+ }
+ }
+
/**
* Checks the text length of the {@code messageTextArea}, adjusts the
* {@code remainingChars} label and checks whether to send the message
@@ -373,16 +432,16 @@ public final class ChatScene implements Restorable {
final var text = messageTextArea.getText().strip();
try {
// Creating the message and its metadata
- final var builder = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
+ final var builder = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
.setText(text);
- // Setting an attachment, if present
- if (pendingAttachment != null) {
+ // Setting an attachment, if present
+ if (pendingAttachment != null) {
builder.setAttachment(pendingAttachment);
pendingAttachment = null;
- attachmentView.setVisible(false);
+ updateAttachmentView(false);
}
- // Building the final message
- final var message = currentChat.getRecipient() instanceof Group ? builder.buildGroupMessage((Group) currentChat.getRecipient())
+ // Building the final message
+ final var message = currentChat.getRecipient() instanceof Group ? builder.buildGroupMessage((Group) currentChat.getRecipient())
: builder.build();
// Send message
@@ -428,8 +487,22 @@ public final class ChatScene implements Restorable {
infoLabel.setVisible(true);
}
+ /**
+ * Updates the {@code attachmentView} in terms of visibility.
+ * Additionally resets the shown image to
+ * {@code DEFAULT_ATTACHMENT_VIEW_IMAGE} if another image is currently
+ * present.
+ *
+ * @param visible whether the {@code attachmentView} should be displayed
+ * @since Envoy Client v0.1-beta
+ */
+ private void updateAttachmentView(boolean visible) {
+ if (!attachmentView.getImage().equals(DEFAULT_ATTACHMENT_VIEW_IMAGE)) attachmentView.setImage(DEFAULT_ATTACHMENT_VIEW_IMAGE);
+ attachmentView.setVisible(visible);
+ }
+
// Context menu actions
-
+
@FXML
private void deleteContact() { try {} catch (final NullPointerException e) {} }
diff --git a/src/main/java/envoy/client/ui/listcell/MessageControl.java b/src/main/java/envoy/client/ui/listcell/MessageControl.java
index 55be558..8b7115b 100644
--- a/src/main/java/envoy/client/ui/listcell/MessageControl.java
+++ b/src/main/java/envoy/client/ui/listcell/MessageControl.java
@@ -2,6 +2,7 @@ package envoy.client.ui.listcell;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
+import java.io.ByteArrayInputStream;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.logging.Level;
@@ -67,6 +68,7 @@ public class MessageControl extends Label {
if (message.hasAttachment()) {
switch (message.getAttachment().getType()) {
case PICTURE:
+ vbox.getChildren().add(new ImageView(new Image(new ByteArrayInputStream(message.getAttachment().getData()), 256, 256, true, true)));
break;
case VIDEO:
break;
@@ -90,9 +92,7 @@ public class MessageControl extends Label {
statusIcon.setPreserveRatio(true);
vbox.getChildren().add(statusIcon);
getStyleClass().add("own-message");
- } else {
- getStyleClass().add("received-message");
- }
+ } else getStyleClass().add("received-message");
// Adjusting height and weight of the cell to the corresponding ListView
paddingProperty().setValue(new Insets(5, 20, 5, 20));
setContextMenu(contextMenu);
diff --git a/src/main/resources/fxml/ChatScene.fxml b/src/main/resources/fxml/ChatScene.fxml
index 4a6ac3a..fd48b75 100644
--- a/src/main/resources/fxml/ChatScene.fxml
+++ b/src/main/resources/fxml/ChatScene.fxml
@@ -51,7 +51,7 @@
-
+
@@ -82,24 +82,29 @@
-
-
+
-
+