diff --git a/core/src/main/groovy/com/muwire/core/download/DownloadManager.groovy b/core/src/main/groovy/com/muwire/core/download/DownloadManager.groovy index 0126e2a1..14ed52ac 100644 --- a/core/src/main/groovy/com/muwire/core/download/DownloadManager.groovy +++ b/core/src/main/groovy/com/muwire/core/download/DownloadManager.groovy @@ -112,7 +112,7 @@ public class DownloadManager { public void onUIDownloadAttachmentEvent(UIDownloadAttachmentEvent e) { Set sender = new HashSet<>() - sender.add(e.sender) + sender.add(e.sender.destination) File target = muSettings.downloadLocation target = new File(target, e.attachment.name) diff --git a/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy b/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy index 12b96c02..ad51da92 100644 --- a/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy +++ b/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy @@ -28,6 +28,7 @@ import com.muwire.core.SharedFile import com.muwire.core.SplitPattern import com.muwire.core.collections.FileCollection import com.muwire.core.collections.UICollectionDeletedEvent +import com.muwire.core.collections.UIDownloadCollectionEvent import com.muwire.core.download.Downloader import com.muwire.core.download.UIDownloadCancelledEvent import com.muwire.core.download.UIDownloadPausedEvent @@ -43,6 +44,8 @@ import com.muwire.core.filefeeds.UIFilePublishedEvent import com.muwire.core.filefeeds.UIFileUnpublishedEvent import com.muwire.core.files.FileUnsharedEvent import com.muwire.core.messenger.MWMessage +import com.muwire.core.messenger.MWMessageAttachment +import com.muwire.core.messenger.UIDownloadAttachmentEvent import com.muwire.core.search.QueryEvent import com.muwire.core.search.SearchEvent import com.muwire.core.trust.RemoteTrustList @@ -798,6 +801,44 @@ class MainFrameController { mvcGroup.createMVCGroup("new-message", UUID.randomUUID().toString(), params) } + @ControllerAction + void downloadAttachment() { + List selected = view.selectedMessageAttachments() + if (selected.isEmpty()) + return + + doDownloadAttachments(selected) + } + + @ControllerAction + void downloadAllAttachments() { + doDownloadAttachments(model.messageAttachments) + } + + private void doDownloadAttachments(List attachments) { + int messageRow = view.selectedMessageHeader() + if (messageRow < 0) + return + + MWMessage message = model.messageHeaders.get(messageRow) + attachments.each { + if (it instanceof MWMessageAttachment) + core.eventBus.publish(new UIDownloadAttachmentEvent(attachment : it, sender : message.sender)) + else { + def event = new UIDownloadCollectionEvent( + collection : it, + items : it.getFiles(), + host : message.sender, + infoHash : it.getInfoHash(), + full : true + ) + core.eventBus.publish(event) + } + } + + view.showDownloadsWindow.call() + } + void startChat(Persona p) { if (!mvcGroup.getChildrenGroups().containsKey(p.getHumanReadableName())) { def params = [:] diff --git a/gui/griffon-app/i18n/messages.properties b/gui/griffon-app/i18n/messages.properties index e21b02cd..06a8525e 100644 --- a/gui/griffon-app/i18n/messages.properties +++ b/gui/griffon-app/i18n/messages.properties @@ -575,6 +575,8 @@ SENT=Sent SUBJECT=Subject COMPOSE=Compose REPLY=Reply +DOWNLOAD_ALL=Download All +COLLECTION=Collection ## New message window RECIPIENT=Recipient diff --git a/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy b/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy index f3ed6dc3..e177cef0 100644 --- a/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy @@ -120,6 +120,7 @@ class MainFrameModel { List messageHeaders = new ArrayList<>() Map> messageHeadersMap = new HashMap<>() int folderIdx + List messageAttachments = new ArrayList<>() private final static int INBOX = 0 private final static int OUTBOX = 1 @@ -164,6 +165,7 @@ class MainFrameModel { @Observable boolean deleteCollectionButtonEnabled @Observable boolean messageButtonsEnabled + @Observable boolean messageAttachmentsButtonEnabled @Observable boolean searchesPaneButtonEnabled @Observable boolean downloadsPaneButtonEnabled diff --git a/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy b/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy index 487d6811..aa387748 100644 --- a/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy @@ -51,6 +51,7 @@ import com.muwire.core.filefeeds.FeedFetchStatus import com.muwire.core.filefeeds.FeedItem import com.muwire.core.files.FileSharedEvent import com.muwire.core.messenger.MWMessage +import com.muwire.core.messenger.MWMessageAttachment import com.muwire.core.trust.RemoteTrustList import com.muwire.core.upload.Uploader @@ -110,6 +111,9 @@ class MainFrameView { JTable messageHeaderTable def lastMessageHeaderTableSortEvent JTextArea messageBody + JTable messageAttachmentsTable + def lastMessageAttachmentsTableSortEvent + JSplitPane messageSplitPane void initUI() { chatNotificator = new ChatNotificator(application.getMvcGroupManager()) @@ -677,8 +681,40 @@ class MainFrameView { } panel { borderLayout() - scrollPane(constraints : BorderLayout.CENTER) { - textArea(id : "message-body-textarea", editable : false) + panel(constraints : BorderLayout.CENTER) { + gridLayout(rows : 1, cols : 1) + splitPane(id : "message-attachments-split-pane", orientation : JSplitPane.VERTICAL_SPLIT, + continuousLayout : true, dividerLocation : 100) { + scrollPane { + textArea(id : "message-body-textarea", editable : false) + } + panel { + borderLayout() + scrollPane(constraints : BorderLayout.CENTER) { + table(id : "message-attachments-table", autoCreateRowSorter : true, rowHeight : rowHeight) { + tableModel(list : model.messageAttachments) { + closureColumn(header : trans("NAME"), preferredWidth : 300, type : String, read : {it.name}) + closureColumn(header : trans("SIZE"), preferredWidth : 20, type : Long, read :{ + if (it instanceof MWMessageAttachment) + return it.length + else + return it.totalSize() + }) + closureColumn(header : trans("COLLECTION"), preferredWidth : 20, type: Boolean, read : { + it instanceof FileCollection + }) + } + } + } + panel(constraints: BorderLayout.EAST) { + gridBagLayout() + button(text : trans("DOWNLOAD"), enabled : bind{model.messageAttachmentsButtonEnabled}, + constraints : gbc(gridx:0, gridy:0), downloadAttachmentAction) + button(text : trans("DOWNLOAD_ALL"), enabled : bind{model.messageAttachmentsButtonEnabled}, + constraints : gbc(gridx: 0, gridy: 1), downloadAllAttachmentsAction) + } + } + } } panel(constraints : BorderLayout.SOUTH) { button(text : trans("COMPOSE"), enabled : bind{model.messageButtonsEnabled}, messageComposeAction) @@ -736,6 +772,8 @@ class MainFrameView { messageFolderList = builder.getVariable("message-folders-list") messageHeaderTable = builder.getVariable("message-header-table") messageBody = builder.getVariable("message-body-textarea") + messageAttachmentsTable = builder.getVariable("message-attachments-table") + messageSplitPane = builder.getVariable("message-attachments-split-pane") } @@ -1251,15 +1289,39 @@ class MainFrameView { int selectedRow = selectedMessageHeader() if (selectedRow < 0) { model.messageButtonsEnabled = false + model.messageAttachmentsButtonEnabled = false messageBody.setText("") } else { MWMessage selected = model.messageHeaders.getAt(selectedRow) messageBody.setText(selected.body) model.messageButtonsEnabled = true + + if (selected.attachments.isEmpty()) + messageSplitPane.setDividerLocation(1.0d) + else { + messageSplitPane.setDividerLocation(0.7d) + model.messageAttachments.clear() + model.messageAttachments.addAll(selected.attachments) + model.messageAttachments.addAll(selected.collections) + messageAttachmentsTable.model.fireTableDataChanged() + } } }) + messageAttachmentsTable.setDefaultRenderer(Long.class, new SizeRenderer()) + messageAttachmentsTable.rowSorter.addRowSorterListener({evt -> lastMessageAttachmentsTableSortEvent = evt}) + messageAttachmentsTable.rowSorter.setSortsOnUpdates(true) + selectionModel = messageAttachmentsTable.getSelectionModel() + selectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) + selectionModel.addListSelectionListener({ + List selected = selectedMessageAttachments() + if (selected.isEmpty()) { + model.messageAttachmentsButtonEnabled = false + } else { + model.messageAttachmentsButtonEnabled = true + } + }) // chat tabs def chatTabbedPane = builder.getVariable("chat-tabs") @@ -1852,6 +1914,21 @@ class MainFrameView { selectedRow } + List selectedMessageAttachments() { + int[] rows = messageAttachmentsTable.getSelectedRows() + if (rows.length == 0) + return Collections.emptyList() + if (lastMessageAttachmentsTableSortEvent != null) { + for (int i = 0; i < rows.length; i++) { + rows[i] = messageAttachmentsTable.rowSorter.convertRowIndexToModel(rows[i]) + } + } + List rv = new ArrayList() + for (int i = 0; i < rows.length; i++) + rv.add(model.messageAttachments.get(rows[i])) + rv + } + void closeApplication() { Core core = application.getContext().get("core")