diff --git a/gui/src/main/groovy/com/muwire/gui/chat/ChatEntryPane.groovy b/gui/src/main/groovy/com/muwire/gui/chat/ChatEntryPane.groovy index 3798773c..ba66f483 100644 --- a/gui/src/main/groovy/com/muwire/gui/chat/ChatEntryPane.groovy +++ b/gui/src/main/groovy/com/muwire/gui/chat/ChatEntryPane.groovy @@ -1,8 +1,12 @@ package com.muwire.gui.chat - +import com.muwire.gui.CopyPasteSupport import com.muwire.gui.UISettings import com.muwire.gui.contacts.POPLabel +import com.muwire.gui.mulinks.CollectionMuLink +import com.muwire.gui.mulinks.FileMuLink +import com.muwire.gui.mulinks.InvalidMuLinkException +import com.muwire.gui.mulinks.MuLink import com.muwire.gui.profile.PersonaOrProfile import com.muwire.gui.profile.ProfileConstants import sun.swing.UIAction @@ -14,6 +18,7 @@ import javax.swing.event.MenuKeyListener import javax.swing.text.* import java.awt.* import java.awt.event.ActionEvent +import java.awt.event.InputEvent import java.awt.event.KeyAdapter import java.awt.event.KeyEvent import java.util.List @@ -90,6 +95,10 @@ class ChatEntryPane extends JTextPane { KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0) Object enterObject = inputMap.get(enter) actionMap.put(enterObject, noAction) + + Action originalPasteAction = actionMap.get(DefaultEditorKit.pasteAction) + Action linkPasteAction = new LinkPasteAction(originalPasteAction) + actionMap.put(DefaultEditorKit.pasteAction, linkPasteAction) } private String getTextSinceAt(){ @@ -211,9 +220,17 @@ class ChatEntryPane extends JTextPane { sb.append(c) continue } - POPLabel label = (POPLabel) component - sb.append(label.personaOrProfile.getPersona().toBase64()) - sb.append(AT) + if (component instanceof POPLabel) { + POPLabel label = (POPLabel) component + sb.append(label.personaOrProfile.getPersona().toBase64()) + sb.append(AT) + } + if (component instanceof MuLinkLabel) { + MuLinkLabel label = (MuLinkLabel) component + sb.append("<") + sb.append(label.link.toLink()) + sb.append(">") + } } sb.toString() } @@ -256,7 +273,7 @@ class ChatEntryPane extends JTextPane { position++ } } else { - while (position > 0) { + while (position >= 0) { if (!isInComponent(position)) break delegate.actionPerformed(e) @@ -266,6 +283,41 @@ class ChatEntryPane extends JTextPane { } } + private class LinkPasteAction extends UIAction { + private final Action delegate + LinkPasteAction(Action delegate) { + super("Paste") + this.delegate = delegate + } + + @Override + void actionPerformed(ActionEvent e) { + if(!CopyPasteSupport.canPasteString()) + return + + + String string = CopyPasteSupport.pasteFromClipboard() + try { + MuLink link = MuLink.parse(string) + JLabel label = null + if (link.getLinkType() == MuLink.LinkType.FILE) { + label = new FileLinkLabel((FileMuLink) link, settings, true) + } else if (link.getLinkType() == MuLink.LinkType.COLLECTION) { + label = new CollectionLinkLabel((CollectionMuLink)link, settings, true) + } + + final int position = getCaret().getDot() + StyledDocument document = getStyledDocument() + Style style = document.addStyle("newStyle", null) + StyleConstants.setComponent(style, label) + document.insertString(position, " ", style) + + } catch (InvalidMuLinkException notALink) { + delegate.actionPerformed(e) + } + } + } + private static class NullAction extends UIAction { NullAction() { super("nothing") diff --git a/gui/src/main/groovy/com/muwire/gui/chat/CollectionLinkLabel.groovy b/gui/src/main/groovy/com/muwire/gui/chat/CollectionLinkLabel.groovy new file mode 100644 index 00000000..9c9e5ad9 --- /dev/null +++ b/gui/src/main/groovy/com/muwire/gui/chat/CollectionLinkLabel.groovy @@ -0,0 +1,22 @@ +package com.muwire.gui.chat + +import com.muwire.gui.HTMLSanitizer +import com.muwire.gui.SizeFormatter +import com.muwire.gui.UISettings +import com.muwire.gui.mulinks.CollectionMuLink + +class CollectionLinkLabel extends MuLinkLabel { + CollectionLinkLabel(CollectionMuLink link, UISettings settings, boolean border) { + super(link, settings, border) + } + + @Override + protected String getVisibleText() { + CollectionMuLink link = (CollectionMuLink) this.link + + StringBuffer sb = new StringBuffer() + SizeFormatter.format(link.totalSize, sb) + + return HTMLSanitizer.escape(link.name) + " (" + link.numFiles + " files " + sb.toString()+")" + } +} diff --git a/gui/src/main/groovy/com/muwire/gui/chat/FileLinkLabel.groovy b/gui/src/main/groovy/com/muwire/gui/chat/FileLinkLabel.groovy new file mode 100644 index 00000000..6bcc554d --- /dev/null +++ b/gui/src/main/groovy/com/muwire/gui/chat/FileLinkLabel.groovy @@ -0,0 +1,22 @@ +package com.muwire.gui.chat + +import com.muwire.gui.HTMLSanitizer +import com.muwire.gui.SizeFormatter +import com.muwire.gui.UISettings +import com.muwire.gui.mulinks.FileMuLink + +class FileLinkLabel extends MuLinkLabel { + + FileLinkLabel(FileMuLink link, UISettings settings, boolean border) { + super(link, settings, border) + } + + protected String getVisibleText() { + FileMuLink link = (FileMuLink) this.link + + StringBuffer sb = new StringBuffer() + SizeFormatter.format(link.fileSize, sb) + + HTMLSanitizer.escape(link.name) + " (" + sb.toString() + ")" + } +} diff --git a/gui/src/main/groovy/com/muwire/gui/chat/FileLinkPanel.groovy b/gui/src/main/groovy/com/muwire/gui/chat/FileLinkPanel.groovy new file mode 100644 index 00000000..b0c33b47 --- /dev/null +++ b/gui/src/main/groovy/com/muwire/gui/chat/FileLinkPanel.groovy @@ -0,0 +1,39 @@ +package com.muwire.gui.chat + +import com.muwire.gui.UISettings +import com.muwire.gui.mulinks.FileMuLink + +import javax.swing.Icon +import javax.swing.ImageIcon +import javax.swing.JButton +import javax.swing.JPanel +import java.awt.BorderLayout +import java.awt.Image +import java.util.function.Consumer + +class FileLinkPanel extends JPanel { + + private static final Icon DOWN_ICON + static { + DOWN_ICON = new ImageIcon(FileLinkPanel.class.getClassLoader().getResource("down_arrow.png")) + } + + private final FileMuLink link + private final UISettings settings + private final Consumer linkConsumer + + FileLinkPanel(FileMuLink link, UISettings settings, Consumer linkConsumer) { + super() + this.linkConsumer = linkConsumer + + setLayout(new BorderLayout()) + + def label = new FileLinkLabel(link, settings, false) + add(label, BorderLayout.CENTER) + + JButton button = new JButton() + button.setIcon(DOWN_ICON) + button.addActionListener({linkConsumer.accept(link)}) + add(button, BorderLayout.EAST) + } +} diff --git a/gui/src/main/groovy/com/muwire/gui/chat/MuLinkLabel.groovy b/gui/src/main/groovy/com/muwire/gui/chat/MuLinkLabel.groovy new file mode 100644 index 00000000..178bbf2c --- /dev/null +++ b/gui/src/main/groovy/com/muwire/gui/chat/MuLinkLabel.groovy @@ -0,0 +1,46 @@ +package com.muwire.gui.chat + + +import com.muwire.gui.UISettings +import com.muwire.gui.mulinks.MuLink + +import javax.swing.* +import javax.swing.border.Border +import java.awt.* + +abstract class MuLinkLabel extends JLabel { + final MuLink link + + private final UISettings settings + + MuLinkLabel(MuLink link, UISettings settings, boolean border) { + super() + this.link = link + this.settings = settings + + String visibleText = getVisibleText() + setText("$visibleText") + + int preferredX = 0, preferredY = 24 + + FontMetrics fontMetrics = getFontMetrics(getFont()) + for (int i = 0; i < visibleText.length(); i++) { + char c = text.charAt(i) + preferredX += fontMetrics.charWidth(c) + } + + if (border) { + Border b = BorderFactory.createEtchedBorder() + setBorder(b) + Insets insets = b.getBorderInsets(this) + preferredX += insets.left + preferredX += insets.right + } + + setMaximumSize([preferredX, preferredY] as Dimension) + float alignmentY = 0.5f + (settings.fontSize * 1f / preferredY) / 2 + setAlignmentY(alignmentY) + } + + protected abstract String getVisibleText() +}