package envoy.client.ui.control; import java.io.ByteArrayInputStream; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Map; import java.util.logging.*; import javafx.geometry.*; import javafx.scene.control.*; import javafx.scene.image.*; import javafx.scene.layout.*; import envoy.data.*; import envoy.data.Message.MessageStatus; import envoy.util.EnvoyLog; import envoy.client.data.*; import envoy.client.net.Client; import envoy.client.util.*; /** * This class transforms a single {@link Message} into a UI component. * * @author Leon Hofmeister * @author Maximilian Käfer * @since Envoy Client v0.1-beta */ public final class MessageControl extends Label { private final boolean ownMessage; private final LocalDB localDB = context.getLocalDB(); private final Client client = context.getClient(); private static final Context context = Context.getInstance(); private static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss") .withZone(ZoneId.systemDefault()); private static final Map statusImages = IconUtil.loadByEnum(MessageStatus.class, 16); private static final Logger logger = EnvoyLog.getLogger(MessageControl.class); /** * @param message the message that should be formatted * @since Envoy Client v0.1-beta */ public MessageControl(Message message) { ownMessage = message.getSenderID() == localDB.getUser().getID(); // Creating the underlying VBox and the dateLabel final var hbox = new HBox(); if (message.getSenderID() != localDB.getUser().getID() && message instanceof GroupMessage) { // Displaying the name of the sender in a group final var label = new Label(); label.getStyleClass().add("group-member-names"); label.setText(localDB.getUsers() .values() .stream() .filter(c -> c.getID() == message.getSenderID()) .findFirst() .map(User::getName) .orElse("Unknown User")); label.setPadding(new Insets(0, 5, 0, 0)); hbox.getChildren().add(label); } hbox.getChildren().add(new Label(dateFormat.format(message.getCreationDate()))); final var vbox = new VBox(hbox); // Creating the actions for the MenuItems final var contextMenu = new ContextMenu(); final var items = contextMenu.getItems(); // Copy message action if (!message.getText().isEmpty()) { final var copyMenuItem = new MenuItem("Copy Text"); copyMenuItem.setOnAction(e -> MessageUtil.copyMessageText(message)); items.add(copyMenuItem); } // Delete message final var deleteMenuItem = new MenuItem("Delete locally"); deleteMenuItem.setOnAction(e -> MessageUtil.deleteMessage(message)); items.add(deleteMenuItem); // As long as these types of messages are not implemented and no caches are // defined for them, we only want them to appear when being online if (client.isOnline()) { // Forward menu item final var forwardMenuItem = new MenuItem("Forward"); forwardMenuItem.setOnAction(e -> MessageUtil.forwardMessage(message)); items.add(forwardMenuItem); // Quote menu item final var quoteMenuItem = new MenuItem("Quote"); quoteMenuItem.setOnAction(e -> MessageUtil.quoteMessage(message)); items.add(quoteMenuItem); } // Info actions final var infoMenuItem = new MenuItem("Info"); infoMenuItem.setOnAction(e -> loadMessageInfoScene(message)); items.add(infoMenuItem); // Handling message attachment display // TODO: Add missing attachment types 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; case VOICE: vbox.getChildren().add(new AudioControl(message.getAttachment().getData())); break; case DOCUMENT: break; } final var saveAttachment = new MenuItem("Save attachment"); saveAttachment.setOnAction(e -> MessageUtil.saveAttachment(message)); items.add(saveAttachment); } // Creating the textLabel final var textLabel = new Label(message.getText()); textLabel.setMaxWidth(430); textLabel.setWrapText(true); final var hBoxBottom = new HBox(); hBoxBottom.getChildren().add(textLabel); // Setting the message status icon and background color if (message.getSenderID() == localDB.getUser().getID()) { final var statusIcon = new ImageView(statusImages.get(message.getStatus())); statusIcon.setPreserveRatio(true); final var space = new Region(); HBox.setHgrow(space, Priority.ALWAYS); hBoxBottom.getChildren().add(space); hBoxBottom.getChildren().add(statusIcon); hBoxBottom.setAlignment(Pos.BOTTOM_RIGHT); getStyleClass().add("own-message"); hbox.setAlignment(Pos.CENTER_RIGHT); } else getStyleClass().add("received-message"); vbox.getChildren().add(hBoxBottom); // Adjusting height and weight of the cell to the corresponding ListView paddingProperty().setValue(new Insets(5, 20, 5, 20)); setContextMenu(contextMenu); setGraphic(vbox); } private void loadMessageInfoScene(Message message) { logger.log(Level.FINEST, "message info scene was requested for " + message); } /** * @return whether the message stored by this {@code MessageControl} has been sent by this user * of Envoy * @since Envoy Client v0.1-beta */ public boolean isOwnMessage() { return ownMessage; } }