diff --git a/core/src/main/groovy/com/muwire/core/Core.groovy b/core/src/main/groovy/com/muwire/core/Core.groovy index f49e22eb..364151a6 100644 --- a/core/src/main/groovy/com/muwire/core/Core.groovy +++ b/core/src/main/groovy/com/muwire/core/Core.groovy @@ -282,7 +282,7 @@ public class Core { i2pAcceptor, hostCache, trustService, searchManager, uploadManager, connectionEstablisher) log.info("initializing directory watcher") - directoryWatcher = new DirectoryWatcher(eventBus, fileManager) + directoryWatcher = new DirectoryWatcher(eventBus, fileManager, home, props) eventBus.register(FileSharedEvent.class, directoryWatcher) eventBus.register(AllFilesLoadedEvent.class, directoryWatcher) eventBus.register(DirectoryUnsharedEvent.class, directoryWatcher) @@ -290,6 +290,8 @@ public class Core { log.info("initializing hasher service") hasherService = new HasherService(new FileHasher(), eventBus, fileManager) eventBus.register(FileSharedEvent.class, hasherService) + eventBus.register(FileUnsharedEvent.class, hasherService) + eventBus.register(DirectoryUnsharedEvent.class, hasherService) log.info("initializing trust subscriber") trustSubscriber = new TrustSubscriber(eventBus, i2pConnector, props) diff --git a/core/src/main/groovy/com/muwire/core/MuWireSettings.groovy b/core/src/main/groovy/com/muwire/core/MuWireSettings.groovy index a4b7b8a8..6dc90f6a 100644 --- a/core/src/main/groovy/com/muwire/core/MuWireSettings.groovy +++ b/core/src/main/groovy/com/muwire/core/MuWireSettings.groovy @@ -6,6 +6,7 @@ import com.muwire.core.hostcache.CrawlerResponse import com.muwire.core.util.DataUtil import net.i2p.data.Base64 +import net.i2p.util.ConcurrentHashSet class MuWireSettings { @@ -113,7 +114,7 @@ class MuWireSettings { } private static Set readEncodedSet(Properties props, String property) { - Set rv = new HashSet<>() + Set rv = new ConcurrentHashSet<>() if (props.containsKey(property)) { String[] encoded = props.getProperty(property).split(",") encoded.each { rv << DataUtil.readi18nString(Base64.decode(it)) } diff --git a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy index ccaf09bb..d90ae925 100644 --- a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy +++ b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy @@ -13,6 +13,7 @@ import java.nio.file.WatchService import java.util.concurrent.ConcurrentHashMap import com.muwire.core.EventBus +import com.muwire.core.MuWireSettings import com.muwire.core.SharedFile import groovy.util.logging.Log @@ -31,6 +32,8 @@ class DirectoryWatcher { kinds = [ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE] } + private final File home + private final MuWireSettings muOptions private final EventBus eventBus private final FileManager fileManager private final Thread watcherThread, publisherThread @@ -39,7 +42,9 @@ class DirectoryWatcher { private WatchService watchService private volatile boolean shutdown - DirectoryWatcher(EventBus eventBus, FileManager fileManager) { + DirectoryWatcher(EventBus eventBus, FileManager fileManager, File home, MuWireSettings muOptions) { + this.home = home + this.muOptions = muOptions this.eventBus = eventBus this.fileManager = fileManager this.watcherThread = new Thread({watch() } as Runnable, "directory-watcher") @@ -64,15 +69,28 @@ class DirectoryWatcher { void onFileSharedEvent(FileSharedEvent e) { if (!e.file.isDirectory()) return - Path path = e.file.getCanonicalFile().toPath() + File canonical = e.file.getCanonicalFile() + Path path = canonical.toPath() WatchKey wk = path.register(watchService, kinds) - watchedDirectories.put(e.file, wk) - + watchedDirectories.put(canonical, wk) + + if (muOptions.watchedDirectories.add(canonical.toString())) + saveMuSettings() } void onDirectoryUnsharedEvent(DirectoryUnsharedEvent e) { WatchKey wk = watchedDirectories.remove(e.directory) wk?.cancel() + + if (muOptions.watchedDirectories.remove(e.directory.toString())) + saveMuSettings() + } + + private void saveMuSettings() { + File muSettingsFile = new File(home, "MuWire.properties") + muSettingsFile.withOutputStream { + muOptions.write(it) + } } private void watch() { diff --git a/core/src/main/groovy/com/muwire/core/files/HasherService.groovy b/core/src/main/groovy/com/muwire/core/files/HasherService.groovy index f1cc5758..13c4b5a5 100644 --- a/core/src/main/groovy/com/muwire/core/files/HasherService.groovy +++ b/core/src/main/groovy/com/muwire/core/files/HasherService.groovy @@ -11,6 +11,7 @@ class HasherService { final FileHasher hasher final EventBus eventBus final FileManager fileManager + final Set hashed = new HashSet<>() Executor executor HasherService(FileHasher hasher, EventBus eventBus, FileManager fileManager) { @@ -24,13 +25,22 @@ class HasherService { } void onFileSharedEvent(FileSharedEvent evt) { - if (fileManager.fileToSharedFile.containsKey(evt.file.getCanonicalFile())) + File canonical = evt.file.getCanonicalFile() + if (fileManager.fileToSharedFile.containsKey(canonical)) return - executor.execute( { -> process(evt.file) } as Runnable) + if (hashed.add(canonical)) + executor.execute( { -> process(canonical) } as Runnable) + } + + void onFileUnsharedEvent(FileUnsharedEvent evt) { + hashed.remove(evt.unsharedFile.file) + } + + void onDirectoryUnsharedEvent(DirectoryUnsharedEvent evt) { + hashed.remove(evt.directory) } private void process(File f) { - f = f.getCanonicalFile() if (f.isDirectory()) { f.listFiles().each {eventBus.publish new FileSharedEvent(file: it) } } else { diff --git a/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy b/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy index 5489a2d2..5ef846a1 100644 --- a/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy @@ -2,6 +2,7 @@ package com.muwire.gui import java.util.concurrent.ConcurrentHashMap import java.nio.file.Path +import java.nio.file.Paths import java.util.Calendar import java.util.UUID @@ -26,6 +27,7 @@ import com.muwire.core.content.ContentControlEvent import com.muwire.core.download.DownloadStartedEvent import com.muwire.core.download.Downloader import com.muwire.core.files.AllFilesLoadedEvent +import com.muwire.core.files.DirectoryUnsharedEvent import com.muwire.core.files.FileDownloadedEvent import com.muwire.core.files.FileHashedEvent import com.muwire.core.files.FileHashingEvent @@ -350,6 +352,7 @@ class MainFrameModel { runInsideUIAsync { shared.remove(e.unsharedFile) loadedFiles = shared.size() + def dmtn = fileToNode.remove(e.unsharedFile) if (dmtn != null) { loadedFiles = fileToNode.size() @@ -359,12 +362,15 @@ class MainFrameModel { if (parent == treeRoot) break if (parent.getChildCount() == 0) { + File file = parent.getUserObject().file + if (core.muOptions.watchedDirectories.contains(file.toString())) + core.eventBus.publish(new DirectoryUnsharedEvent(directory : parent.getUserObject().file)) dmtn = parent continue } break } - } + } view.refreshSharedFiles() } } @@ -514,22 +520,28 @@ class MainFrameModel { } private void insertIntoTree(SharedFile file) { - Path folder = file.getFile().toPath() - folder = folder.subpath(0, folder.getNameCount() - 1) + List parents = new ArrayList<>() + File tmp = file.file.getParentFile() + while(tmp.getParent() != null) { + parents << tmp + tmp = tmp.getParentFile() + } + Collections.reverse(parents) TreeNode node = treeRoot - for(Path path : folder) { + for(File path : parents) { boolean exists = false def children = node.children() def child = null while(children.hasMoreElements()) { child = children.nextElement() - if (child.getUserObject() == path.toString()) { + def userObject = child.getUserObject() + if (userObject != null && userObject.file == path) { exists = true break } } if (!exists) { - child = new DefaultMutableTreeNode(path.toString()) + child = new DefaultMutableTreeNode(new InterimTreeNode(path)) node.add(child) } node = child diff --git a/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy b/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy index 8a0ab1a7..4e6be168 100644 --- a/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy @@ -418,10 +418,7 @@ class MainFrameView { public boolean importData(TransferHandler.TransferSupport support) { def files = support.getTransferable().getTransferData(DataFlavor.javaFileListFlavor) files.each { - if (it.isDirectory()) - watchDirectory(it) - else - model.core.eventBus.publish(new FileSharedEvent(file : it)) + model.core.eventBus.publish(new FileSharedEvent(file : it)) } showUploadsWindow.call() true @@ -692,7 +689,7 @@ class MainFrameView { getLeafs(children.nextElement(), dest) } } - + def copyHashToClipboard() { def selectedFiles = selectedSharedFiles() if (selectedFiles == null) @@ -863,7 +860,8 @@ class MainFrameView { int rv = chooser.showOpenDialog(null) if (rv == JFileChooser.APPROVE_OPTION) { chooser.getSelectedFiles().each { - model.core.eventBus.publish(new FileSharedEvent(file : it)) + File canonical = it.getCanonicalFile() + model.core.eventBus.publish(new FileSharedEvent(file : canonical)) } } } diff --git a/gui/src/main/groovy/com/muwire/gui/InterimTreeNode.groovy b/gui/src/main/groovy/com/muwire/gui/InterimTreeNode.groovy new file mode 100644 index 00000000..4673fa4f --- /dev/null +++ b/gui/src/main/groovy/com/muwire/gui/InterimTreeNode.groovy @@ -0,0 +1,18 @@ +package com.muwire.gui + +class InterimTreeNode { + private final File file + InterimTreeNode(File file) { + this.file = file + } + + public boolean equals(Object o) { + if (!(o instanceof InterimTreeNode)) + return false + file == o.file + } + + public String toString() { + file.getName() + } +} diff --git a/gui/src/main/groovy/com/muwire/gui/SharedTreeRenderer.groovy b/gui/src/main/groovy/com/muwire/gui/SharedTreeRenderer.groovy index 0e3622f8..3e08c92f 100644 --- a/gui/src/main/groovy/com/muwire/gui/SharedTreeRenderer.groovy +++ b/gui/src/main/groovy/com/muwire/gui/SharedTreeRenderer.groovy @@ -23,7 +23,7 @@ class SharedTreeRenderer extends DefaultTreeCellRenderer { def userObject = value.getUserObject() def defaultRenderer = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus) - if (userObject instanceof String || userObject == null) + if (userObject instanceof InterimTreeNode || userObject == null) return defaultRenderer