From 08d9bf881af38bc97a290244c9339bb88d1a7dc5 Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Thu, 4 Aug 2022 21:26:16 +0100 Subject: [PATCH] render Mu links in chat --- .../com/muwire/gui/MainFrameController.groovy | 4 +- .../views/com/muwire/gui/ChatRoomView.groovy | 22 ++++++- .../com/muwire/gui/chat/ChatEntry.groovy | 61 +++++++++++++++-- .../com/muwire/gui/chat/FileLinkPanel.groovy | 39 ----------- .../com/muwire/gui/chat/MuLinkPanel.groovy | 65 +++++++++++++++++++ 5 files changed, 142 insertions(+), 49 deletions(-) delete mode 100644 gui/src/main/groovy/com/muwire/gui/chat/FileLinkPanel.groovy create mode 100644 gui/src/main/groovy/com/muwire/gui/chat/MuLinkPanel.groovy diff --git a/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy b/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy index 8cade985..56ca9a69 100644 --- a/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy +++ b/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy @@ -231,7 +231,7 @@ class MainFrameController { originator : core.me, sig : sig.data, queryTime : timestamp, sig2 : sig2)) } - private void downloadLink(FileMuLink link) { + void downloadLink(FileMuLink link) { view.showDownloadsWindow.call() def event = new UIDownloadLinkEvent(host: link.host, infoHash: link.infoHash, @@ -241,7 +241,7 @@ class MainFrameController { core.eventBus.publish event } - private void fetchCollectionLink(CollectionMuLink link) { + void fetchCollectionLink(CollectionMuLink link) { UUID uuid = UUID.randomUUID() diff --git a/gui/griffon-app/views/com/muwire/gui/ChatRoomView.groovy b/gui/griffon-app/views/com/muwire/gui/ChatRoomView.groovy index 20bde1cf..48560e22 100644 --- a/gui/griffon-app/views/com/muwire/gui/ChatRoomView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/ChatRoomView.groovy @@ -1,16 +1,20 @@ package com.muwire.gui +import com.muwire.core.download.UIDownloadLinkEvent import com.muwire.core.trust.TrustLevel import com.muwire.gui.chat.ChatEntry import com.muwire.gui.chat.ChatEntryPane import com.muwire.gui.contacts.POPLabel +import com.muwire.gui.mulinks.FileMuLink +import com.muwire.gui.mulinks.MuLink import com.muwire.gui.profile.PersonaOrProfile import com.muwire.gui.profile.PersonaOrProfileCellRenderer import com.muwire.gui.profile.PersonaOrProfileComparator import com.muwire.gui.profile.ProfileConstants import griffon.core.GriffonApplication import griffon.core.artifact.GriffonView +import griffon.core.mvc.MVCGroup import javax.inject.Inject import javax.swing.BorderFactory @@ -20,6 +24,7 @@ import javax.swing.border.Border import javax.swing.text.SimpleAttributeSet import java.awt.Dimension import java.text.SimpleDateFormat +import java.util.function.Consumer import static com.muwire.gui.Translator.trans import griffon.inject.MVCMember @@ -67,6 +72,8 @@ class ChatRoomView { def lastMembersTableSortEvent UISettings settings + private final Consumer muLinkConsumer = new MuLinkConsumer() + void initUI() { settings = application.context.get("ui-settings") int rowHeight = application.context.get("row-height") @@ -252,7 +259,7 @@ class ChatRoomView { StyledDocument doc = roomTextArea.getStyledDocument() - def textField = new ChatEntry(text, settings, model::getByPersona, timestamp, sender) + def textField = new ChatEntry(text, settings, model::getByPersona, muLinkConsumer, timestamp, sender) def style = doc.addStyle("newStyle", null) StyleConstants.setComponent(style, textField) doc.insertString(doc.getEndPosition().getOffset() - 1, " ", style) @@ -270,4 +277,17 @@ class ChatRoomView { Element element = doc.getParagraphElement(0) doc.remove(0, element.getEndOffset()) } + + private class MuLinkConsumer implements Consumer { + + @Override + void accept(MuLink muLink) { + MVCGroup mainFrame = application.mvcGroupManager.findGroup("MainFrame") + if (muLink.getLinkType() == MuLink.LinkType.FILE) { + mainFrame.controller.downloadLink(muLink) + } else if (muLink.getLinkType() == MuLink.LinkType.COLLECTION) { + mainFrame.controller.fetchCollectionLink(muLink) + } + } + } } \ No newline at end of file diff --git a/gui/src/main/groovy/com/muwire/gui/chat/ChatEntry.groovy b/gui/src/main/groovy/com/muwire/gui/chat/ChatEntry.groovy index 68ac3d33..ff64b557 100644 --- a/gui/src/main/groovy/com/muwire/gui/chat/ChatEntry.groovy +++ b/gui/src/main/groovy/com/muwire/gui/chat/ChatEntry.groovy @@ -1,25 +1,25 @@ package com.muwire.gui.chat -import com.muwire.core.Constants + import com.muwire.core.Persona import com.muwire.core.util.DataUtil import com.muwire.gui.UISettings import com.muwire.gui.contacts.POPLabel +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 net.i2p.data.Base64 -import javax.swing.BorderFactory -import javax.swing.JLabel -import javax.swing.JTextPane +import javax.swing.* import javax.swing.border.Border import javax.swing.text.Document import javax.swing.text.SimpleAttributeSet import javax.swing.text.StyleConstants import javax.swing.text.StyledDocument -import java.awt.Dimension import java.text.SimpleDateFormat -import java.util.function.Function +import java.util.function.Consumer +import java.util.function.Function class ChatEntry extends JTextPane { @@ -27,17 +27,22 @@ class ChatEntry extends JTextPane { private static final char AT = "@".toCharacter() private static final char EQUALS = "=".toCharacter() + private static final char LT = "<".toCharacter() + private static final char GT = ">".toCharacter() private final UISettings settings private final Function function + private final Consumer linkConsumer private final List tokens = [] ChatEntry(String text, UISettings settings, Function function, + Consumer linkConsumer, long timestamp, PersonaOrProfile sender) { super() this.settings = settings this.function = function + this.linkConsumer = linkConsumer setEditable(false) setAlignmentY(0f) @@ -94,6 +99,12 @@ class ChatEntry extends JTextPane { tokens << new TextChatToken(stringBuilder.toString()) return new PersonaParsingState() } + if (c == LT) { + consumed = true + stringBuilder.setLength(stringBuilder.length() - 1) + tokens << new TextChatToken(stringBuilder.toString()) + return new MuLinkParsingState() + } this } } @@ -129,6 +140,25 @@ class ChatEntry extends JTextPane { } } + private class MuLinkParsingState extends ParsingState { + + @Override + ParsingState consume(char c) { + if (c == GT) { + stringBuilder.setLength(stringBuilder.length() - 1) + String payload = stringBuilder.toString() + try { + MuLink link = MuLink.parse(payload) + tokens << new LinkChatToken(link) + } catch (InvalidMuLinkException e) { + tokens << new TextChatToken(payload + ">") + } + return new TextParsingState() + } + return this + } + } + private interface ChatToken { void render() } @@ -163,4 +193,21 @@ class ChatEntry extends JTextPane { style) } } + + private class LinkChatToken implements ChatToken { + private final MuLink link + LinkChatToken(MuLink link){ + this.link = link + } + void render() { + StyledDocument document = getStyledDocument() + JPanel panel = new MuLinkPanel(link, settings, linkConsumer) + + def style = document.addStyle("newStyle", null) + StyleConstants.setComponent(style, panel) + document.insertString(document.getEndPosition().getOffset() - 1, + " ", + style) + } + } } diff --git a/gui/src/main/groovy/com/muwire/gui/chat/FileLinkPanel.groovy b/gui/src/main/groovy/com/muwire/gui/chat/FileLinkPanel.groovy deleted file mode 100644 index b0c33b47..00000000 --- a/gui/src/main/groovy/com/muwire/gui/chat/FileLinkPanel.groovy +++ /dev/null @@ -1,39 +0,0 @@ -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/MuLinkPanel.groovy b/gui/src/main/groovy/com/muwire/gui/chat/MuLinkPanel.groovy new file mode 100644 index 00000000..098af2f5 --- /dev/null +++ b/gui/src/main/groovy/com/muwire/gui/chat/MuLinkPanel.groovy @@ -0,0 +1,65 @@ +package com.muwire.gui.chat + +import com.muwire.gui.UISettings +import com.muwire.gui.mulinks.CollectionMuLink +import com.muwire.gui.mulinks.FileMuLink +import com.muwire.gui.mulinks.MuLink + +import javax.swing.BorderFactory +import javax.swing.Icon +import javax.swing.ImageIcon +import javax.swing.JButton +import javax.swing.JPanel +import javax.swing.border.Border +import java.awt.BorderLayout +import java.awt.Dimension +import java.awt.Insets +import java.util.function.Consumer + +class MuLinkPanel extends JPanel { + + private static final Icon DOWN_ICON + static { + DOWN_ICON = new ImageIcon(MuLinkPanel.class.getClassLoader().getResource("down_arrow.png")) + } + + private final UISettings settings + private final Consumer linkConsumer + + MuLinkPanel(MuLink link, UISettings settings, Consumer linkConsumer) { + super() + this.settings = settings + this.linkConsumer = linkConsumer + + setLayout(new BorderLayout()) + def label = null + if (link.getLinkType() == MuLink.LinkType.FILE) + label = new FileLinkLabel((FileMuLink)link, settings, false) + else if (link.getLinkType() == MuLink.LinkType.COLLECTION) + label = new CollectionLinkLabel((CollectionMuLink)link, settings, false) + add(label, BorderLayout.CENTER) + + JButton button = new JButton() + button.setIcon(DOWN_ICON) + button.addActionListener({linkConsumer.accept(link)}) + add(button, BorderLayout.EAST) + + def labelDim = label.getMaximumSize() + double preferredY = labelDim.getHeight() + double preferredX = labelDim.getWidth() + DOWN_ICON.getIconWidth() + + Border border = BorderFactory.createEtchedBorder() + setBorder(border) + Insets insets = border.getBorderInsets(this) + preferredX += insets.left + preferredX += insets.right + preferredY += insets.top + preferredY += insets.bottom + + preferredX += 20 + + setMaximumSize([(int)preferredX, (int)preferredY] as Dimension) + float alignmentY = 0.5f + (settings.fontSize * 1f / preferredY) / 2 + setAlignmentY(alignmentY) + } +}