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

pull/24/head
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 100644

Binary file not shown.

View File

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

View File

@ -143,6 +143,8 @@ class OptionsController {
// boolean showSearchHashes = view.showSearchHashesCheckbox.model.isSelected()
// model.showSearchHashes = showSearchHashes
// uiSettings.showSearchHashes = showSearchHashes
uiSettings.sharedFilesAsTree = model.sharedFilesAsTree
File uiSettingsFile = new File(core.home, "gui.properties")
uiSettingsFile.withOutputStream {
@ -168,4 +170,14 @@ class OptionsController {
if (rv == JFileChooser.APPROVE_OPTION)
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
import java.util.concurrent.ConcurrentHashMap
import java.nio.file.Path
import java.util.Calendar
import java.util.UUID
@ -8,12 +9,16 @@ import javax.annotation.Nonnull
import javax.inject.Inject
import javax.swing.JOptionPane
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.InfoHash
import com.muwire.core.MuWireSettings
import com.muwire.core.Persona
import com.muwire.core.RouterDisconnectedEvent
import com.muwire.core.SharedFile
import com.muwire.core.connection.ConnectionAttemptStatus
import com.muwire.core.connection.ConnectionEvent
import com.muwire.core.connection.DisconnectionEvent
@ -57,6 +62,8 @@ class MainFrameModel {
FactoryBuilderSupport builder
@MVCMember @Nonnull
MainFrameController controller
@MVCMember @Nonnull
MainFrameView view
@Inject @Nonnull GriffonApplication application
@Observable boolean coreInitialized = false
@Observable boolean routerPresent
@ -64,7 +71,10 @@ class MainFrameModel {
def results = new ConcurrentHashMap<>()
def downloads = []
def uploads = []
def shared = []
def shared
def sharedTree
def treeRoot
final Map<SharedFile, TreeNode> fileToNode = new HashMap<>()
def watched = []
def connectionList = []
def searches = new LinkedList()
@ -122,6 +132,13 @@ class MainFrameModel {
void mvcGroupInit(Map<String, Object> args) {
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.schedule({
@ -303,7 +320,6 @@ class MainFrameModel {
void onFileHashingEvent(FileHashingEvent e) {
runInsideUIAsync {
loadedFiles = shared.size()
hashingFile = e.hashingFile
}
}
@ -315,28 +331,53 @@ class MainFrameModel {
if (e.error != null)
return // TODO do something
runInsideUIAsync {
shared << e.sharedFile
loadedFiles = shared.size()
JTable table = builder.getVariable("shared-files-table")
table.model.fireTableDataChanged()
if (!uiSettings.sharedFilesAsTree) {
shared << e.sharedFile
loadedFiles = shared.size()
JTable table = builder.getVariable("shared-files-table")
table.model.fireTableDataChanged()
} else {
insertIntoTree(e.sharedFile)
}
}
}
void onFileLoadedEvent(FileLoadedEvent e) {
runInsideUIAsync {
shared << e.loadedFile
loadedFiles = shared.size()
JTable table = builder.getVariable("shared-files-table")
table.model.fireTableDataChanged()
if (!uiSettings.sharedFilesAsTree) {
shared << e.loadedFile
loadedFiles = shared.size()
JTable table = builder.getVariable("shared-files-table")
table.model.fireTableDataChanged()
} else {
insertIntoTree(e.loadedFile)
}
}
}
void onFileUnsharedEvent(FileUnsharedEvent e) {
runInsideUIAsync {
shared.remove(e.unsharedFile)
loadedFiles = shared.size()
JTable table = builder.getVariable("shared-files-table")
table.model.fireTableDataChanged()
if (!uiSettings.sharedFilesAsTree) {
shared.remove(e.unsharedFile)
loadedFiles = shared.size()
} 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)
return
runInsideUIAsync {
shared << e.downloadedFile
JTable table = builder.getVariable("shared-files-table")
table.model.fireTableDataChanged()
if (!uiSettings.sharedFilesAsTree) {
shared << e.downloadedFile
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 {
Destination destination

View File

@ -31,6 +31,7 @@ class OptionsModel {
@Observable boolean clearFinishedDownloads
@Observable boolean excludeLocalResult
@Observable boolean showSearchHashes
@Observable boolean sharedFilesAsTree
// bw options
@Observable String inBw
@ -67,6 +68,7 @@ class OptionsModel {
clearFinishedDownloads = uiSettings.clearFinishedDownloads
excludeLocalResult = uiSettings.excludeLocalResult
showSearchHashes = uiSettings.showSearchHashes
sharedFilesAsTree = uiSettings.sharedFilesAsTree
if (core.router != null) {
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.JSplitPane
import javax.swing.JTable
import javax.swing.JTree
import javax.swing.ListSelectionModel
import javax.swing.SwingConstants
import javax.swing.TransferHandler
import javax.swing.border.Border
import javax.swing.table.DefaultTableCellRenderer
import javax.swing.tree.TreeNode
import javax.swing.tree.TreePath
import com.muwire.core.Constants
import com.muwire.core.MuWireSettings
@ -59,8 +62,10 @@ class MainFrameView {
def lastWatchedSortEvent
def trustTablesSortEvents = [:]
UISettings settings
void initUI() {
UISettings settings = application.context.get("ui-settings")
settings = application.context.get("ui-settings")
builder.with {
application(size : [1024,768], id: 'main-frame',
locationRelativeTo : null,
@ -207,12 +212,18 @@ class MainFrameView {
panel {
borderLayout()
scrollPane(constraints : BorderLayout.CENTER) {
table(id : "shared-files-table", autoCreateRowSorter: true) {
tableModel(list : model.shared) {
closureColumn(header : "Name", preferredWidth : 500, type : String, read : {row -> row.getCachedPath()})
closureColumn(header : "Size", preferredWidth : 100, type : Long, read : {row -> row.getCachedLength() })
closureColumn(header : "Comments", preferredWidth : 100, type : Boolean, read : {it.getComment() != null})
if (!settings.sharedFilesAsTree) {
table(id : "shared-files-table", autoCreateRowSorter: true) {
tableModel(list : model.shared) {
closureColumn(header : "Name", preferredWidth : 500, type : String, read : {row -> row.getCachedPath()})
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)
panel {
label("Shared:")
label(text : bind {model.loadedFiles.toString()})
label(text : bind {model.loadedFiles}, id : "shared-files-count")
}
panel {
button(text : "Add Comment", enabled : bind {model.addCommentButtonEnabled}, addCommentAction)
@ -489,13 +500,7 @@ class MainFrameView {
}
})
// shared files table
def sharedFilesTable = builder.getVariable("shared-files-table")
sharedFilesTable.columnModel.getColumn(1).setCellRenderer(new SizeRenderer())
sharedFilesTable.rowSorter.addRowSorterListener({evt -> lastSharedSortEvent = evt})
sharedFilesTable.rowSorter.setSortsOnUpdates(true)
// shared files menu
JPopupMenu sharedFilesMenu = new JPopupMenu()
JMenuItem copyHashToClipboard = new JMenuItem("Copy hash to clipboard")
copyHashToClipboard.addActionListener({mvcGroup.view.copyHashToClipboard()})
@ -506,7 +511,8 @@ class MainFrameView {
JMenuItem commentSelectedFiles = new JMenuItem("Comment selected files")
commentSelectedFiles.addActionListener({mvcGroup.controller.addComment()})
sharedFilesMenu.add(commentSelectedFiles)
sharedFilesTable.addMouseListener(new MouseAdapter() {
def sharedFilesMouseListener = new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger())
@ -517,15 +523,36 @@ class MainFrameView {
if (e.isPopupTrigger())
showPopupMenu(sharedFilesMenu, e)
}
})
}
selectionModel = sharedFilesTable.getSelectionModel()
selectionModel.addListSelectionListener({
def selectedFiles = selectedSharedFiles()
if (selectedFiles == null || selectedFiles.isEmpty())
return
model.addCommentButtonEnabled = true
})
// shared files table or tree
if (!settings.sharedFilesAsTree) {
def sharedFilesTable = builder.getVariable("shared-files-table")
sharedFilesTable.columnModel.getColumn(1).setCellRenderer(new SizeRenderer())
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
def searchesTable = builder.getVariable("searches-table")
@ -656,20 +683,40 @@ class MainFrameView {
}
def selectedSharedFiles() {
def sharedFilesTable = builder.getVariable("shared-files-table")
int[] selected = sharedFilesTable.getSelectedRows()
if (selected.length == 0)
return null
List<SharedFile> rv = new ArrayList<>()
if (lastSharedSortEvent != null) {
for (int i = 0; i < selected.length; i ++) {
selected[i] = sharedFilesTable.rowSorter.convertRowIndexToModel(selected[i])
if (!settings.sharedFilesAsTree) {
def sharedFilesTable = builder.getVariable("shared-files-table")
int[] selected = sharedFilesTable.getSelectedRows()
if (selected.length == 0)
return null
List<SharedFile> rv = new ArrayList<>()
if (lastSharedSortEvent != null) {
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() {
@ -876,4 +923,12 @@ class MainFrameView {
selectedRow = table.rowSorter.convertRowIndexToModel(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))
// 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))
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 {
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 excludeLocalResult
boolean showSearchHashes
boolean sharedFilesAsTree
UISettings(Properties props) {
lnf = props.getProperty("lnf", "system")
@ -18,6 +19,7 @@ class UISettings {
clearFinishedDownloads = Boolean.parseBoolean(props.getProperty("clearFinishedDownloads","false"))
excludeLocalResult = Boolean.parseBoolean(props.getProperty("excludeLocalResult","true"))
showSearchHashes = Boolean.parseBoolean(props.getProperty("showSearchHashes","true"))
sharedFilesAsTree = Boolean.parseBoolean(props.getProperty("sharedFilesAsTree","true"))
}
void write(OutputStream out) throws IOException {
@ -28,6 +30,7 @@ class UISettings {
props.setProperty("clearFinishedDownloads", String.valueOf(clearFinishedDownloads))
props.setProperty("excludeLocalResult", String.valueOf(excludeLocalResult))
props.setProperty("showSearchHashes", String.valueOf(showSearchHashes))
props.setProperty("sharedFilesAsTree", String.valueOf(sharedFilesAsTree))
if (font != null)
props.setProperty("font", font)