Tree view of the shared files. The count is wrong for some reason

This commit is contained in:
Zlatin Balevsky
2019-10-14 20:13:25 +01:00
parent c7284623bc
commit 60ddb85461
10 changed files with 248 additions and 52 deletions

BIN
gui/griffon-app/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -27,7 +27,7 @@ class AddCommentController {
model.selectedFiles.each { model.selectedFiles.each {
it.setComment(comment) it.setComment(comment)
} }
mvcGroup.parentGroup.view.builder.getVariable("shared-files-table").model.fireTableDataChanged() mvcGroup.parentGroup.view.refreshSharedFiles()
cancel() cancel()
} }

View File

@@ -143,6 +143,8 @@ class OptionsController {
// boolean showSearchHashes = view.showSearchHashesCheckbox.model.isSelected() // boolean showSearchHashes = view.showSearchHashesCheckbox.model.isSelected()
// model.showSearchHashes = showSearchHashes // model.showSearchHashes = showSearchHashes
// uiSettings.showSearchHashes = showSearchHashes // uiSettings.showSearchHashes = showSearchHashes
uiSettings.sharedFilesAsTree = model.sharedFilesAsTree
File uiSettingsFile = new File(core.home, "gui.properties") File uiSettingsFile = new File(core.home, "gui.properties")
uiSettingsFile.withOutputStream { uiSettingsFile.withOutputStream {
@@ -168,4 +170,14 @@ class OptionsController {
if (rv == JFileChooser.APPROVE_OPTION) if (rv == JFileChooser.APPROVE_OPTION)
model.downloadLocation = chooser.getSelectedFile().getAbsolutePath() model.downloadLocation = chooser.getSelectedFile().getAbsolutePath()
} }
@ControllerAction
void sharedTree() {
model.sharedFilesAsTree = true
}
@ControllerAction
void sharedTable() {
model.sharedFilesAsTree = false
}
} }

View File

@@ -1,6 +1,7 @@
package com.muwire.gui package com.muwire.gui
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.nio.file.Path
import java.util.Calendar import java.util.Calendar
import java.util.UUID import java.util.UUID
@@ -8,12 +9,16 @@ import javax.annotation.Nonnull
import javax.inject.Inject import javax.inject.Inject
import javax.swing.JOptionPane import javax.swing.JOptionPane
import javax.swing.JTable import javax.swing.JTable
import javax.swing.tree.DefaultMutableTreeNode
import javax.swing.tree.DefaultTreeModel
import javax.swing.tree.TreeNode
import com.muwire.core.Core import com.muwire.core.Core
import com.muwire.core.InfoHash import com.muwire.core.InfoHash
import com.muwire.core.MuWireSettings import com.muwire.core.MuWireSettings
import com.muwire.core.Persona import com.muwire.core.Persona
import com.muwire.core.RouterDisconnectedEvent import com.muwire.core.RouterDisconnectedEvent
import com.muwire.core.SharedFile
import com.muwire.core.connection.ConnectionAttemptStatus import com.muwire.core.connection.ConnectionAttemptStatus
import com.muwire.core.connection.ConnectionEvent import com.muwire.core.connection.ConnectionEvent
import com.muwire.core.connection.DisconnectionEvent import com.muwire.core.connection.DisconnectionEvent
@@ -57,6 +62,8 @@ class MainFrameModel {
FactoryBuilderSupport builder FactoryBuilderSupport builder
@MVCMember @Nonnull @MVCMember @Nonnull
MainFrameController controller MainFrameController controller
@MVCMember @Nonnull
MainFrameView view
@Inject @Nonnull GriffonApplication application @Inject @Nonnull GriffonApplication application
@Observable boolean coreInitialized = false @Observable boolean coreInitialized = false
@Observable boolean routerPresent @Observable boolean routerPresent
@@ -64,7 +71,10 @@ class MainFrameModel {
def results = new ConcurrentHashMap<>() def results = new ConcurrentHashMap<>()
def downloads = [] def downloads = []
def uploads = [] def uploads = []
def shared = [] def shared
def sharedTree
def treeRoot
final Map<SharedFile, TreeNode> fileToNode = new HashMap<>()
def watched = [] def watched = []
def connectionList = [] def connectionList = []
def searches = new LinkedList() def searches = new LinkedList()
@@ -122,6 +132,13 @@ class MainFrameModel {
void mvcGroupInit(Map<String, Object> args) { void mvcGroupInit(Map<String, Object> args) {
uiSettings = application.context.get("ui-settings") uiSettings = application.context.get("ui-settings")
if (!uiSettings.sharedFilesAsTree)
shared = []
else {
treeRoot = new DefaultMutableTreeNode()
sharedTree = new DefaultTreeModel(treeRoot)
}
Timer timer = new Timer("download-pumper", true) Timer timer = new Timer("download-pumper", true)
timer.schedule({ timer.schedule({
@@ -303,7 +320,6 @@ class MainFrameModel {
void onFileHashingEvent(FileHashingEvent e) { void onFileHashingEvent(FileHashingEvent e) {
runInsideUIAsync { runInsideUIAsync {
loadedFiles = shared.size()
hashingFile = e.hashingFile hashingFile = e.hashingFile
} }
} }
@@ -315,28 +331,53 @@ class MainFrameModel {
if (e.error != null) if (e.error != null)
return // TODO do something return // TODO do something
runInsideUIAsync { runInsideUIAsync {
shared << e.sharedFile if (!uiSettings.sharedFilesAsTree) {
loadedFiles = shared.size() shared << e.sharedFile
JTable table = builder.getVariable("shared-files-table") loadedFiles = shared.size()
table.model.fireTableDataChanged() JTable table = builder.getVariable("shared-files-table")
table.model.fireTableDataChanged()
} else {
insertIntoTree(e.sharedFile)
}
} }
} }
void onFileLoadedEvent(FileLoadedEvent e) { void onFileLoadedEvent(FileLoadedEvent e) {
runInsideUIAsync { runInsideUIAsync {
shared << e.loadedFile if (!uiSettings.sharedFilesAsTree) {
loadedFiles = shared.size() shared << e.loadedFile
JTable table = builder.getVariable("shared-files-table") loadedFiles = shared.size()
table.model.fireTableDataChanged() JTable table = builder.getVariable("shared-files-table")
table.model.fireTableDataChanged()
} else {
insertIntoTree(e.loadedFile)
}
} }
} }
void onFileUnsharedEvent(FileUnsharedEvent e) { void onFileUnsharedEvent(FileUnsharedEvent e) {
runInsideUIAsync { runInsideUIAsync {
shared.remove(e.unsharedFile) if (!uiSettings.sharedFilesAsTree) {
loadedFiles = shared.size() shared.remove(e.unsharedFile)
JTable table = builder.getVariable("shared-files-table") loadedFiles = shared.size()
table.model.fireTableDataChanged() } else {
def dmtn = fileToNode.remove(e.unsharedFile)
if (dmtn != null) {
loadedFiles = fileToNode.size()
while (true) {
def parent = dmtn.getParent()
parent.remove(dmtn)
if (parent == treeRoot)
break
if (parent.getChildCount() == 0) {
dmtn = parent
continue
}
break
}
}
}
view.refreshSharedFiles()
} }
} }
@@ -476,11 +517,44 @@ class MainFrameModel {
if (!core.muOptions.shareDownloadedFiles) if (!core.muOptions.shareDownloadedFiles)
return return
runInsideUIAsync { runInsideUIAsync {
shared << e.downloadedFile if (!uiSettings.sharedFilesAsTree) {
JTable table = builder.getVariable("shared-files-table") shared << e.downloadedFile
table.model.fireTableDataChanged() JTable table = builder.getVariable("shared-files-table")
table.model.fireTableDataChanged()
} else {
insertIntoTree(e.downloadedFile)
}
} }
} }
private void insertIntoTree(SharedFile file) {
Path folder = file.getFile().toPath()
folder = folder.subpath(0, folder.getNameCount() - 1)
TreeNode node = treeRoot
for(Path path : folder) {
boolean exists = false
def children = node.children()
def child = null
while(children.hasMoreElements()) {
child = children.nextElement()
if (child.getUserObject() == path.toString()) {
exists = true
break
}
}
if (!exists) {
child = new DefaultMutableTreeNode(path.toString())
node.add(child)
}
node = child
}
def dmtn = new DefaultMutableTreeNode(file)
fileToNode[file] = dmtn
node.add(dmtn)
loadedFiles = fileToNode.size()
view.refreshSharedFiles()
}
private static class UIConnection { private static class UIConnection {
Destination destination Destination destination

View File

@@ -31,6 +31,7 @@ class OptionsModel {
@Observable boolean clearFinishedDownloads @Observable boolean clearFinishedDownloads
@Observable boolean excludeLocalResult @Observable boolean excludeLocalResult
@Observable boolean showSearchHashes @Observable boolean showSearchHashes
@Observable boolean sharedFilesAsTree
// bw options // bw options
@Observable String inBw @Observable String inBw
@@ -67,6 +68,7 @@ class OptionsModel {
clearFinishedDownloads = uiSettings.clearFinishedDownloads clearFinishedDownloads = uiSettings.clearFinishedDownloads
excludeLocalResult = uiSettings.excludeLocalResult excludeLocalResult = uiSettings.excludeLocalResult
showSearchHashes = uiSettings.showSearchHashes showSearchHashes = uiSettings.showSearchHashes
sharedFilesAsTree = uiSettings.sharedFilesAsTree
if (core.router != null) { if (core.router != null) {
inBw = String.valueOf(settings.inBw) inBw = String.valueOf(settings.inBw)

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 B

View File

@@ -16,11 +16,14 @@ import javax.swing.JMenuItem
import javax.swing.JPopupMenu import javax.swing.JPopupMenu
import javax.swing.JSplitPane import javax.swing.JSplitPane
import javax.swing.JTable import javax.swing.JTable
import javax.swing.JTree
import javax.swing.ListSelectionModel import javax.swing.ListSelectionModel
import javax.swing.SwingConstants import javax.swing.SwingConstants
import javax.swing.TransferHandler import javax.swing.TransferHandler
import javax.swing.border.Border import javax.swing.border.Border
import javax.swing.table.DefaultTableCellRenderer import javax.swing.table.DefaultTableCellRenderer
import javax.swing.tree.TreeNode
import javax.swing.tree.TreePath
import com.muwire.core.Constants import com.muwire.core.Constants
import com.muwire.core.MuWireSettings import com.muwire.core.MuWireSettings
@@ -59,8 +62,10 @@ class MainFrameView {
def lastWatchedSortEvent def lastWatchedSortEvent
def trustTablesSortEvents = [:] def trustTablesSortEvents = [:]
UISettings settings
void initUI() { void initUI() {
UISettings settings = application.context.get("ui-settings") settings = application.context.get("ui-settings")
builder.with { builder.with {
application(size : [1024,768], id: 'main-frame', application(size : [1024,768], id: 'main-frame',
locationRelativeTo : null, locationRelativeTo : null,
@@ -207,12 +212,18 @@ class MainFrameView {
panel { panel {
borderLayout() borderLayout()
scrollPane(constraints : BorderLayout.CENTER) { scrollPane(constraints : BorderLayout.CENTER) {
table(id : "shared-files-table", autoCreateRowSorter: true) { if (!settings.sharedFilesAsTree) {
tableModel(list : model.shared) { table(id : "shared-files-table", autoCreateRowSorter: true) {
closureColumn(header : "Name", preferredWidth : 500, type : String, read : {row -> row.getCachedPath()}) tableModel(list : model.shared) {
closureColumn(header : "Size", preferredWidth : 100, type : Long, read : {row -> row.getCachedLength() }) closureColumn(header : "Name", preferredWidth : 500, type : String, read : {row -> row.getCachedPath()})
closureColumn(header : "Comments", preferredWidth : 100, type : Boolean, read : {it.getComment() != null}) closureColumn(header : "Size", preferredWidth : 100, type : Long, read : {row -> row.getCachedLength() })
closureColumn(header : "Comments", preferredWidth : 100, type : Boolean, read : {it.getComment() != null})
}
} }
} else {
def jtree = new JTree(model.sharedTree)
jtree.setCellRenderer(new SharedTreeRenderer())
tree(id : "shared-files-tree", rootVisible : false, jtree)
} }
} }
} }
@@ -227,7 +238,7 @@ class MainFrameView {
gridLayout(rows : 1, cols : 2) gridLayout(rows : 1, cols : 2)
panel { panel {
label("Shared:") label("Shared:")
label(text : bind {model.loadedFiles.toString()}) label(text : bind {model.loadedFiles}, id : "shared-files-count")
} }
panel { panel {
button(text : "Add Comment", enabled : bind {model.addCommentButtonEnabled}, addCommentAction) button(text : "Add Comment", enabled : bind {model.addCommentButtonEnabled}, addCommentAction)
@@ -489,13 +500,7 @@ class MainFrameView {
} }
}) })
// shared files table // shared files menu
def sharedFilesTable = builder.getVariable("shared-files-table")
sharedFilesTable.columnModel.getColumn(1).setCellRenderer(new SizeRenderer())
sharedFilesTable.rowSorter.addRowSorterListener({evt -> lastSharedSortEvent = evt})
sharedFilesTable.rowSorter.setSortsOnUpdates(true)
JPopupMenu sharedFilesMenu = new JPopupMenu() JPopupMenu sharedFilesMenu = new JPopupMenu()
JMenuItem copyHashToClipboard = new JMenuItem("Copy hash to clipboard") JMenuItem copyHashToClipboard = new JMenuItem("Copy hash to clipboard")
copyHashToClipboard.addActionListener({mvcGroup.view.copyHashToClipboard()}) copyHashToClipboard.addActionListener({mvcGroup.view.copyHashToClipboard()})
@@ -506,7 +511,8 @@ class MainFrameView {
JMenuItem commentSelectedFiles = new JMenuItem("Comment selected files") JMenuItem commentSelectedFiles = new JMenuItem("Comment selected files")
commentSelectedFiles.addActionListener({mvcGroup.controller.addComment()}) commentSelectedFiles.addActionListener({mvcGroup.controller.addComment()})
sharedFilesMenu.add(commentSelectedFiles) sharedFilesMenu.add(commentSelectedFiles)
sharedFilesTable.addMouseListener(new MouseAdapter() {
def sharedFilesMouseListener = new MouseAdapter() {
@Override @Override
public void mouseReleased(MouseEvent e) { public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) if (e.isPopupTrigger())
@@ -517,15 +523,36 @@ class MainFrameView {
if (e.isPopupTrigger()) if (e.isPopupTrigger())
showPopupMenu(sharedFilesMenu, e) showPopupMenu(sharedFilesMenu, e)
} }
}) }
selectionModel = sharedFilesTable.getSelectionModel() // shared files table or tree
selectionModel.addListSelectionListener({ if (!settings.sharedFilesAsTree) {
def selectedFiles = selectedSharedFiles() def sharedFilesTable = builder.getVariable("shared-files-table")
if (selectedFiles == null || selectedFiles.isEmpty()) sharedFilesTable.columnModel.getColumn(1).setCellRenderer(new SizeRenderer())
return
model.addCommentButtonEnabled = true sharedFilesTable.rowSorter.addRowSorterListener({evt -> lastSharedSortEvent = evt})
}) sharedFilesTable.rowSorter.setSortsOnUpdates(true)
sharedFilesTable.addMouseListener(sharedFilesMouseListener)
selectionModel = sharedFilesTable.getSelectionModel()
selectionModel.addListSelectionListener({
def selectedFiles = selectedSharedFiles()
if (selectedFiles == null || selectedFiles.isEmpty())
return
model.addCommentButtonEnabled = true
})
} else {
def sharedFilesTree = builder.getVariable("shared-files-tree")
sharedFilesTree.addMouseListener(sharedFilesMouseListener)
sharedFilesTree.addTreeSelectionListener({
def selectedNode = sharedFilesTree.getLastSelectedPathComponent()
model.addCommentButtonEnabled = selectedNode != null
})
// TODO: other stuff
}
// searches table // searches table
def searchesTable = builder.getVariable("searches-table") def searchesTable = builder.getVariable("searches-table")
@@ -656,20 +683,40 @@ class MainFrameView {
} }
def selectedSharedFiles() { def selectedSharedFiles() {
def sharedFilesTable = builder.getVariable("shared-files-table") if (!settings.sharedFilesAsTree) {
int[] selected = sharedFilesTable.getSelectedRows() def sharedFilesTable = builder.getVariable("shared-files-table")
if (selected.length == 0) int[] selected = sharedFilesTable.getSelectedRows()
return null if (selected.length == 0)
List<SharedFile> rv = new ArrayList<>() return null
if (lastSharedSortEvent != null) { List<SharedFile> rv = new ArrayList<>()
for (int i = 0; i < selected.length; i ++) { if (lastSharedSortEvent != null) {
selected[i] = sharedFilesTable.rowSorter.convertRowIndexToModel(selected[i]) for (int i = 0; i < selected.length; i ++) {
selected[i] = sharedFilesTable.rowSorter.convertRowIndexToModel(selected[i])
}
} }
selected.each {
rv.add(model.shared[it])
}
return rv
} else {
def sharedFilesTree = builder.getVariable("shared-files-tree")
List<SharedFile> rv = new ArrayList<>()
for (TreePath path : sharedFilesTree.getSelectionPaths()) {
getLeafs(path.getLastPathComponent(), rv)
}
return rv
} }
selected.each { }
rv.add(model.shared[it])
private static void getLeafs(TreeNode node, List<SharedFile> dest) {
if (node.isLeaf()) {
dest.add(node.getUserObject())
return
}
def children = node.children()
while(children.hasMoreElements()) {
getLeafs(children.nextElement(), dest)
} }
rv
} }
def copyHashToClipboard() { def copyHashToClipboard() {
@@ -876,4 +923,12 @@ class MainFrameView {
selectedRow = table.rowSorter.convertRowIndexToModel(selectedRow) selectedRow = table.rowSorter.convertRowIndexToModel(selectedRow)
selectedRow selectedRow
} }
public void refreshSharedFiles() {
if (settings.sharedFilesAsTree) {
model.sharedTree.nodeStructureChanged(model.treeRoot)
} else {
builder.getVariable("shared-files-table").model.fireTableDataChanged()
}
}
} }

View File

@@ -126,6 +126,12 @@ class OptionsView {
excludeLocalResultCheckbox = checkBox(selected : bind {model.excludeLocalResult}, constraints : gbc(gridx: 1, gridy : 6)) excludeLocalResultCheckbox = checkBox(selected : bind {model.excludeLocalResult}, constraints : gbc(gridx: 1, gridy : 6))
// label(text : "Show Hash Searches In Monitor", constraints: gbc(gridx:0, gridy:7)) // label(text : "Show Hash Searches In Monitor", constraints: gbc(gridx:0, gridy:7))
// showSearchHashesCheckbox = checkBox(selected : bind {model.showSearchHashes}, constraints : gbc(gridx: 1, gridy: 7)) // showSearchHashesCheckbox = checkBox(selected : bind {model.showSearchHashes}, constraints : gbc(gridx: 1, gridy: 7))
label(text : "Show Shared Files as", constraints: gbc(gridx: 0, gridy:8))
panel( constraints : gbc(gridx: 1, gridy: 8)) {
buttonGroup(id : "viewShared")
radioButton(text: "Tree", selected : bind {model.sharedFilesAsTree}, buttonGroup: viewShared, sharedTreeAction)
radioButton(text: "Table", selected : bind {!model.sharedFilesAsTree}, buttonGroup: viewShared, sharedTableAction)
}
} }
bandwidth = builder.panel { bandwidth = builder.panel {
gridBagLayout() gridBagLayout()

View File

@@ -0,0 +1,44 @@
package com.muwire.gui
import java.awt.Component
import javax.swing.ImageIcon
import javax.swing.JTree
import javax.swing.tree.DefaultTreeCellRenderer
import com.muwire.core.SharedFile
import net.i2p.data.DataHelper
class SharedTreeRenderer extends DefaultTreeCellRenderer {
private final ImageIcon commentIcon
SharedTreeRenderer() {
commentIcon = new ImageIcon((URL) SharedTreeRenderer.class.getResource("/comment.png"))
}
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
def userObject = value.getUserObject()
def defaultRenderer = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus)
if (userObject instanceof String || userObject == null)
return defaultRenderer
SharedFile sf = (SharedFile) userObject
String name = sf.getFile().getName()
long length = sf.getCachedLength()
String formatted = DataHelper.formatSize2Decimal(length, false)+"B"
setText("$name ($formatted)")
setEnabled(true)
if (sf.comment != null) {
setIcon(commentIcon)
}
this
}
}

View File

@@ -9,6 +9,7 @@ class UISettings {
boolean clearFinishedDownloads boolean clearFinishedDownloads
boolean excludeLocalResult boolean excludeLocalResult
boolean showSearchHashes boolean showSearchHashes
boolean sharedFilesAsTree
UISettings(Properties props) { UISettings(Properties props) {
lnf = props.getProperty("lnf", "system") lnf = props.getProperty("lnf", "system")
@@ -18,6 +19,7 @@ class UISettings {
clearFinishedDownloads = Boolean.parseBoolean(props.getProperty("clearFinishedDownloads","false")) clearFinishedDownloads = Boolean.parseBoolean(props.getProperty("clearFinishedDownloads","false"))
excludeLocalResult = Boolean.parseBoolean(props.getProperty("excludeLocalResult","true")) excludeLocalResult = Boolean.parseBoolean(props.getProperty("excludeLocalResult","true"))
showSearchHashes = Boolean.parseBoolean(props.getProperty("showSearchHashes","true")) showSearchHashes = Boolean.parseBoolean(props.getProperty("showSearchHashes","true"))
sharedFilesAsTree = Boolean.parseBoolean(props.getProperty("sharedFilesAsTree","true"))
} }
void write(OutputStream out) throws IOException { void write(OutputStream out) throws IOException {
@@ -28,6 +30,7 @@ class UISettings {
props.setProperty("clearFinishedDownloads", String.valueOf(clearFinishedDownloads)) props.setProperty("clearFinishedDownloads", String.valueOf(clearFinishedDownloads))
props.setProperty("excludeLocalResult", String.valueOf(excludeLocalResult)) props.setProperty("excludeLocalResult", String.valueOf(excludeLocalResult))
props.setProperty("showSearchHashes", String.valueOf(showSearchHashes)) props.setProperty("showSearchHashes", String.valueOf(showSearchHashes))
props.setProperty("sharedFilesAsTree", String.valueOf(sharedFilesAsTree))
if (font != null) if (font != null)
props.setProperty("font", font) props.setProperty("font", font)