Compare commits
19 Commits
muwire-0.0
...
muwire-0.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
656b62fc2e | ||
![]() |
13b3f0f63b | ||
![]() |
98ea8154a5 | ||
![]() |
82377aa9df | ||
![]() |
bd2368e23a | ||
![]() |
70078c309b | ||
![]() |
15a0eda713 | ||
![]() |
9645716e18 | ||
![]() |
03d6af39ed | ||
![]() |
9435cb003b | ||
![]() |
63399803d5 | ||
![]() |
4d6541030f | ||
![]() |
16c51e7cd6 | ||
![]() |
9d75550b6f | ||
![]() |
1996681677 | ||
![]() |
9dac1891b2 | ||
![]() |
1255ac936b | ||
![]() |
2db3276b07 | ||
![]() |
7e3b0795af |
@@ -54,6 +54,7 @@ public class Core {
|
|||||||
|
|
||||||
final EventBus eventBus
|
final EventBus eventBus
|
||||||
final Persona me
|
final Persona me
|
||||||
|
final File home
|
||||||
|
|
||||||
private final TrustService trustService
|
private final TrustService trustService
|
||||||
private final PersisterService persisterService
|
private final PersisterService persisterService
|
||||||
@@ -65,6 +66,7 @@ public class Core {
|
|||||||
private final HasherService hasherService
|
private final HasherService hasherService
|
||||||
|
|
||||||
public Core(MuWireSettings props, File home) {
|
public Core(MuWireSettings props, File home) {
|
||||||
|
this.home = home
|
||||||
log.info "Initializing I2P context"
|
log.info "Initializing I2P context"
|
||||||
I2PAppContext.getGlobalContext().logManager()
|
I2PAppContext.getGlobalContext().logManager()
|
||||||
I2PAppContext.getGlobalContext()._logManager = new MuWireLogManager()
|
I2PAppContext.getGlobalContext()._logManager = new MuWireLogManager()
|
||||||
@@ -164,7 +166,7 @@ public class Core {
|
|||||||
eventBus.register(ResultsEvent.class, searchManager)
|
eventBus.register(ResultsEvent.class, searchManager)
|
||||||
|
|
||||||
log.info("initializing download manager")
|
log.info("initializing download manager")
|
||||||
DownloadManager downloadManager = new DownloadManager(eventBus, i2pConnector)
|
DownloadManager downloadManager = new DownloadManager(eventBus, i2pConnector, new File(home, "incompletes"))
|
||||||
eventBus.register(UIDownloadEvent.class, downloadManager)
|
eventBus.register(UIDownloadEvent.class, downloadManager)
|
||||||
|
|
||||||
log.info("initializing upload manager")
|
log.info("initializing upload manager")
|
||||||
@@ -197,6 +199,10 @@ public class Core {
|
|||||||
hostCache.waitForLoad()
|
hostCache.waitForLoad()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
connectionManager.shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
static main(args) {
|
static main(args) {
|
||||||
def home = System.getProperty("user.home") + File.separator + ".MuWire"
|
def home = System.getProperty("user.home") + File.separator + ".MuWire"
|
||||||
home = new File(home)
|
home = new File(home)
|
||||||
|
@@ -82,7 +82,6 @@ abstract class Connection implements Closeable {
|
|||||||
read()
|
read()
|
||||||
}
|
}
|
||||||
} catch (SocketTimeoutException e) {
|
} catch (SocketTimeoutException e) {
|
||||||
close()
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.log(Level.WARNING,"unhandled exception in reader",e)
|
log.log(Level.WARNING,"unhandled exception in reader",e)
|
||||||
} finally {
|
} finally {
|
||||||
|
@@ -59,6 +59,8 @@ abstract class ConnectionManager {
|
|||||||
|
|
||||||
abstract void onDisconnectionEvent(DisconnectionEvent e)
|
abstract void onDisconnectionEvent(DisconnectionEvent e)
|
||||||
|
|
||||||
|
abstract void shutdown()
|
||||||
|
|
||||||
protected void sendPings() {
|
protected void sendPings() {
|
||||||
final long now = System.currentTimeMillis()
|
final long now = System.currentTimeMillis()
|
||||||
getConnections().each {
|
getConnections().each {
|
||||||
|
@@ -71,4 +71,8 @@ class LeafConnectionManager extends ConnectionManager {
|
|||||||
log.severe("removed destination not present in connection manager ${e.destination.toBase32()}")
|
log.severe("removed destination not present in connection manager ${e.destination.toBase32()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void shutdown() {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -101,6 +101,14 @@ class UltrapeerConnectionManager extends ConnectionManager {
|
|||||||
log.severe("Removed connection not present in either leaf or peer map ${e.destination.toBase32()}")
|
log.severe("Removed connection not present in either leaf or peer map ${e.destination.toBase32()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void shutdown() {
|
||||||
|
peerConnections.each {k,v -> v.close() }
|
||||||
|
leafConnections.each {k,v -> v.close() }
|
||||||
|
peerConnections.clear()
|
||||||
|
leafConnections.clear()
|
||||||
|
}
|
||||||
|
|
||||||
void forwardQueryToLeafs(QueryEvent e) {
|
void forwardQueryToLeafs(QueryEvent e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,25 @@
|
|||||||
|
package com.muwire.core.download
|
||||||
|
|
||||||
|
class BadHashException extends Exception {
|
||||||
|
|
||||||
|
public BadHashException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BadHashException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BadHashException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BadHashException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BadHashException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -11,10 +11,13 @@ public class DownloadManager {
|
|||||||
private final EventBus eventBus
|
private final EventBus eventBus
|
||||||
private final I2PConnector connector
|
private final I2PConnector connector
|
||||||
private final Executor executor
|
private final Executor executor
|
||||||
|
private final File incompletes
|
||||||
|
|
||||||
public DownloadManager(EventBus eventBus, I2PConnector connector) {
|
public DownloadManager(EventBus eventBus, I2PConnector connector, File incompletes) {
|
||||||
this.eventBus = eventBus
|
this.eventBus = eventBus
|
||||||
this.connector = connector
|
this.connector = connector
|
||||||
|
this.incompletes = incompletes
|
||||||
|
incompletes.mkdir()
|
||||||
this.executor = Executors.newCachedThreadPool({ r ->
|
this.executor = Executors.newCachedThreadPool({ r ->
|
||||||
Thread rv = new Thread(r)
|
Thread rv = new Thread(r)
|
||||||
rv.setName("download-worker")
|
rv.setName("download-worker")
|
||||||
@@ -25,9 +28,14 @@ public class DownloadManager {
|
|||||||
|
|
||||||
|
|
||||||
public void onUIDownloadEvent(UIDownloadEvent e) {
|
public void onUIDownloadEvent(UIDownloadEvent e) {
|
||||||
def downloader = new Downloader(e.target, e.result.size,
|
def downloader = new Downloader(this, e.target, e.result.size,
|
||||||
e.result.infohash, e.result.pieceSize, connector, e.result.sender.destination)
|
e.result.infohash, e.result.pieceSize, connector, e.result.sender.destination,
|
||||||
|
incompletes)
|
||||||
executor.execute({downloader.download()} as Runnable)
|
executor.execute({downloader.download()} as Runnable)
|
||||||
eventBus.publish(new DownloadStartedEvent(downloader : downloader))
|
eventBus.publish(new DownloadStartedEvent(downloader : downloader))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void resume(Downloader downloader) {
|
||||||
|
executor.execute({downloader.download() as Runnable})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -127,11 +127,8 @@ class DownloadSession {
|
|||||||
byte [] hash = digest.digest()
|
byte [] hash = digest.digest()
|
||||||
byte [] expected = new byte[32]
|
byte [] expected = new byte[32]
|
||||||
System.arraycopy(infoHash.getHashList(), piece * 32, expected, 0, 32)
|
System.arraycopy(infoHash.getHashList(), piece * 32, expected, 0, 32)
|
||||||
if (hash != expected) {
|
if (hash != expected)
|
||||||
log.warning("hash mismatch")
|
throw new BadHashException()
|
||||||
endpoint.close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pieces.markDownloaded(piece)
|
pieces.markDownloaded(piece)
|
||||||
} finally {
|
} finally {
|
||||||
|
@@ -2,14 +2,20 @@ package com.muwire.core.download
|
|||||||
|
|
||||||
import com.muwire.core.InfoHash
|
import com.muwire.core.InfoHash
|
||||||
import com.muwire.core.connection.Endpoint
|
import com.muwire.core.connection.Endpoint
|
||||||
|
|
||||||
|
import java.util.logging.Level
|
||||||
|
|
||||||
import com.muwire.core.Constants
|
import com.muwire.core.Constants
|
||||||
import com.muwire.core.connection.I2PConnector
|
import com.muwire.core.connection.I2PConnector
|
||||||
|
|
||||||
|
import groovy.util.logging.Log
|
||||||
import net.i2p.data.Destination
|
import net.i2p.data.Destination
|
||||||
|
|
||||||
|
@Log
|
||||||
public class Downloader {
|
public class Downloader {
|
||||||
public enum DownloadState { CONNECTING, DOWNLOADING, FINISHED }
|
public enum DownloadState { CONNECTING, DOWNLOADING, FAILED, CANCELLED, FINISHED }
|
||||||
|
|
||||||
|
private final DownloadManager downloadManager
|
||||||
private final File file
|
private final File file
|
||||||
private final Pieces pieces
|
private final Pieces pieces
|
||||||
private final long length
|
private final long length
|
||||||
@@ -18,17 +24,24 @@ public class Downloader {
|
|||||||
private final I2PConnector connector
|
private final I2PConnector connector
|
||||||
private final Destination destination
|
private final Destination destination
|
||||||
private final int nPieces
|
private final int nPieces
|
||||||
|
private final File piecesFile
|
||||||
|
|
||||||
private Endpoint endpoint
|
private Endpoint endpoint
|
||||||
private volatile DownloadSession currentSession
|
private volatile DownloadSession currentSession
|
||||||
private volatile DownloadState currentState
|
private volatile DownloadState currentState
|
||||||
|
private volatile boolean cancelled
|
||||||
|
private volatile Thread downloadThread
|
||||||
|
|
||||||
public Downloader(File file, long length, InfoHash infoHash, int pieceSizePow2, I2PConnector connector, Destination destination) {
|
public Downloader(DownloadManager downloadManager, File file, long length, InfoHash infoHash,
|
||||||
|
int pieceSizePow2, I2PConnector connector, Destination destination,
|
||||||
|
File incompletes) {
|
||||||
|
this.downloadManager = downloadManager
|
||||||
this.file = file
|
this.file = file
|
||||||
this.infoHash = infoHash
|
this.infoHash = infoHash
|
||||||
this.length = length
|
this.length = length
|
||||||
this.connector = connector
|
this.connector = connector
|
||||||
this.destination = destination
|
this.destination = destination
|
||||||
|
this.piecesFile = new File(incompletes, file.getName()+".pieces")
|
||||||
this.pieceSize = 1 << pieceSizePow2
|
this.pieceSize = 1 << pieceSizePow2
|
||||||
|
|
||||||
int nPieces
|
int nPieces
|
||||||
@@ -43,14 +56,45 @@ public class Downloader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void download() {
|
void download() {
|
||||||
Endpoint endpoint = connector.connect(destination)
|
readPieces()
|
||||||
|
downloadThread = Thread.currentThread()
|
||||||
|
Endpoint endpoint = null
|
||||||
|
try {
|
||||||
|
endpoint = connector.connect(destination)
|
||||||
currentState = DownloadState.DOWNLOADING
|
currentState = DownloadState.DOWNLOADING
|
||||||
while(!pieces.isComplete()) {
|
while(!pieces.isComplete()) {
|
||||||
currentSession = new DownloadSession(pieces, infoHash, endpoint, file, pieceSize, length)
|
currentSession = new DownloadSession(pieces, infoHash, endpoint, file, pieceSize, length)
|
||||||
currentSession.request()
|
currentSession.request()
|
||||||
|
writePieces()
|
||||||
}
|
}
|
||||||
currentState = DownloadState.FINISHED
|
currentState = DownloadState.FINISHED
|
||||||
endpoint.close()
|
piecesFile.delete()
|
||||||
|
} catch (Exception bad) {
|
||||||
|
log.log(Level.WARNING,"Exception while downloading",bad)
|
||||||
|
if (cancelled)
|
||||||
|
currentState = DownloadState.CANCELLED
|
||||||
|
else if (currentState != DownloadState.FINISHED)
|
||||||
|
currentState = DownloadState.FAILED
|
||||||
|
} finally {
|
||||||
|
endpoint?.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void readPieces() {
|
||||||
|
if (!piecesFile.exists())
|
||||||
|
return
|
||||||
|
piecesFile.withReader {
|
||||||
|
int piece = Integer.parseInt(it.readLine())
|
||||||
|
pieces.markDownloaded(piece)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void writePieces() {
|
||||||
|
piecesFile.withPrintWriter { writer ->
|
||||||
|
pieces.getDownloaded().each { piece ->
|
||||||
|
writer.println(piece)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long donePieces() {
|
public long donePieces() {
|
||||||
@@ -66,4 +110,13 @@ public class Downloader {
|
|||||||
public DownloadState getCurrentState() {
|
public DownloadState getCurrentState() {
|
||||||
currentState
|
currentState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void cancel() {
|
||||||
|
cancelled = true
|
||||||
|
downloadThread?.interrupt()
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resume() {
|
||||||
|
downloadManager.resume(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -33,6 +33,14 @@ class Pieces {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def getDownloaded() {
|
||||||
|
def rv = []
|
||||||
|
for (int i = bitSet.nextSetBit(0); i >= 0; i = bitSet.nextSetBit(i+1)) {
|
||||||
|
rv << i
|
||||||
|
}
|
||||||
|
rv
|
||||||
|
}
|
||||||
|
|
||||||
synchronized void markDownloaded(int piece) {
|
synchronized void markDownloaded(int piece) {
|
||||||
bitSet.set(piece)
|
bitSet.set(piece)
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
group = com.muwire
|
group = com.muwire
|
||||||
version = 0.0.3
|
version = 0.0.4
|
||||||
groovyVersion = 2.4.15
|
groovyVersion = 2.4.15
|
||||||
slf4jVersion = 1.7.25
|
slf4jVersion = 1.7.25
|
||||||
spockVersion = 1.1-groovy-2.4
|
spockVersion = 1.1-groovy-2.4
|
||||||
|
@@ -49,10 +49,16 @@ class MainFrameController {
|
|||||||
def group = selected.getClientProperty("mvc-group")
|
def group = selected.getClientProperty("mvc-group")
|
||||||
def table = selected.getClientProperty("results-table")
|
def table = selected.getClientProperty("results-table")
|
||||||
int row = table.getSelectedRow()
|
int row = table.getSelectedRow()
|
||||||
|
if (row == -1)
|
||||||
|
return
|
||||||
group.model.results[row]
|
group.model.results[row]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def selectedDownload() {
|
||||||
|
def selected = builder.getVariable("downloads-table").getSelectedRow()
|
||||||
|
model.downloads[selected].downloader
|
||||||
|
}
|
||||||
|
|
||||||
@ControllerAction
|
@ControllerAction
|
||||||
void download() {
|
void download() {
|
||||||
def result = selectedResult()
|
def result = selectedResult()
|
||||||
@@ -78,6 +84,18 @@ class MainFrameController {
|
|||||||
core.eventBus.publish( new TrustEvent(destination : result.sender.destination, level : TrustLevel.DISTRUSTED))
|
core.eventBus.publish( new TrustEvent(destination : result.sender.destination, level : TrustLevel.DISTRUSTED))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ControllerAction
|
||||||
|
void cancel() {
|
||||||
|
def downloader = selectedDownload()
|
||||||
|
downloader.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@ControllerAction
|
||||||
|
void resume() {
|
||||||
|
def downloader = selectedDownload()
|
||||||
|
downloader.resume()
|
||||||
|
}
|
||||||
|
|
||||||
void mvcGroupInit(Map<String, String> args) {
|
void mvcGroupInit(Map<String, String> args) {
|
||||||
application.addPropertyChangeListener("core", {e->
|
application.addPropertyChangeListener("core", {e->
|
||||||
core = e.getNewValue()
|
core = e.getNewValue()
|
||||||
|
@@ -21,7 +21,11 @@ class Initialize extends AbstractLifecycleHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
void execute() {
|
void execute() {
|
||||||
lookAndFeel((isMacOSX ? 'system' : 'nimbus'), 'gtk', ['metal', [boldFonts: false]])
|
if (isMacOSX()) {
|
||||||
|
lookAndFeel('nimbus') // otherwise the file chooser doesn't open???
|
||||||
|
} else {
|
||||||
|
lookAndFeel('system', 'gtk')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -16,6 +16,7 @@ import static griffon.util.GriffonApplicationUtils.isMacOSX
|
|||||||
import static groovy.swing.SwingBuilder.lookAndFeel
|
import static groovy.swing.SwingBuilder.lookAndFeel
|
||||||
|
|
||||||
import java.beans.PropertyChangeEvent
|
import java.beans.PropertyChangeEvent
|
||||||
|
import java.util.logging.Level
|
||||||
|
|
||||||
@Log
|
@Log
|
||||||
class Ready extends AbstractLifecycleHandler {
|
class Ready extends AbstractLifecycleHandler {
|
||||||
@@ -81,8 +82,15 @@ class Ready extends AbstractLifecycleHandler {
|
|||||||
props.write(it)
|
props.write(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Core core
|
||||||
Core core = new Core(props, home)
|
try {
|
||||||
|
core = new Core(props, home)
|
||||||
|
} catch (Exception bad) {
|
||||||
|
log.log(Level.SEVERE,"couldn't initialize core",bad)
|
||||||
|
JOptionPane.showMessageDialog(null, "Couldn't connect to I2P router. Make sure I2P is running and restart MuWire",
|
||||||
|
"Can't connect to I2P router", JOptionPane.WARNING_MESSAGE)
|
||||||
|
System.exit(0)
|
||||||
|
}
|
||||||
core.startServices()
|
core.startServices()
|
||||||
application.context.put("muwire-settings", props)
|
application.context.put("muwire-settings", props)
|
||||||
application.context.put("core",core)
|
application.context.put("core",core)
|
||||||
|
25
gui/griffon-app/lifecycle/Shutdown.groovy
Normal file
25
gui/griffon-app/lifecycle/Shutdown.groovy
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
import javax.annotation.Nonnull
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
import org.codehaus.griffon.runtime.core.AbstractLifecycleHandler
|
||||||
|
|
||||||
|
import com.muwire.core.Core
|
||||||
|
|
||||||
|
import griffon.core.GriffonApplication
|
||||||
|
import groovy.util.logging.Log
|
||||||
|
|
||||||
|
@Log
|
||||||
|
class Shutdown extends AbstractLifecycleHandler {
|
||||||
|
@Inject
|
||||||
|
Shutdown(@Nonnull GriffonApplication application) {
|
||||||
|
super(application)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void execute() {
|
||||||
|
log.info("shutting down")
|
||||||
|
Core core = application.context.get("core")
|
||||||
|
core.shutdown()
|
||||||
|
}
|
||||||
|
}
|
@@ -15,6 +15,7 @@ import com.muwire.core.download.DownloadStartedEvent
|
|||||||
import com.muwire.core.files.FileHashedEvent
|
import com.muwire.core.files.FileHashedEvent
|
||||||
import com.muwire.core.files.FileLoadedEvent
|
import com.muwire.core.files.FileLoadedEvent
|
||||||
import com.muwire.core.files.FileSharedEvent
|
import com.muwire.core.files.FileSharedEvent
|
||||||
|
import com.muwire.core.search.QueryEvent
|
||||||
import com.muwire.core.search.UIResultEvent
|
import com.muwire.core.search.UIResultEvent
|
||||||
import com.muwire.core.trust.TrustEvent
|
import com.muwire.core.trust.TrustEvent
|
||||||
import com.muwire.core.trust.TrustService
|
import com.muwire.core.trust.TrustService
|
||||||
@@ -27,6 +28,7 @@ import griffon.core.mvc.MVCGroup
|
|||||||
import griffon.inject.MVCMember
|
import griffon.inject.MVCMember
|
||||||
import griffon.transform.FXObservable
|
import griffon.transform.FXObservable
|
||||||
import griffon.transform.Observable
|
import griffon.transform.Observable
|
||||||
|
import net.i2p.data.Destination
|
||||||
import griffon.metadata.ArtifactProviderFor
|
import griffon.metadata.ArtifactProviderFor
|
||||||
|
|
||||||
@ArtifactProviderFor(GriffonModel)
|
@ArtifactProviderFor(GriffonModel)
|
||||||
@@ -37,11 +39,17 @@ class MainFrameModel {
|
|||||||
@Observable boolean coreInitialized = false
|
@Observable boolean coreInitialized = false
|
||||||
|
|
||||||
def results = new ConcurrentHashMap<>()
|
def results = new ConcurrentHashMap<>()
|
||||||
@Observable def downloads = []
|
def downloads = []
|
||||||
@Observable def uploads = []
|
def uploads = []
|
||||||
@Observable def shared = []
|
def shared = []
|
||||||
|
def connectionList = []
|
||||||
|
def searches = new LinkedList()
|
||||||
|
|
||||||
@Observable int connections
|
@Observable int connections
|
||||||
@Observable String me
|
@Observable String me
|
||||||
|
@Observable boolean searchButtonsEnabled
|
||||||
|
@Observable boolean cancelButtonEnabled
|
||||||
|
@Observable boolean retryButtonEnabled
|
||||||
|
|
||||||
private final Set<InfoHash> infoHashes = new HashSet<>()
|
private final Set<InfoHash> infoHashes = new HashSet<>()
|
||||||
|
|
||||||
@@ -61,12 +69,19 @@ class MainFrameModel {
|
|||||||
core.eventBus.register(UploadEvent.class, this)
|
core.eventBus.register(UploadEvent.class, this)
|
||||||
core.eventBus.register(UploadFinishedEvent.class, this)
|
core.eventBus.register(UploadFinishedEvent.class, this)
|
||||||
core.eventBus.register(TrustEvent.class, this)
|
core.eventBus.register(TrustEvent.class, this)
|
||||||
|
core.eventBus.register(QueryEvent.class, this)
|
||||||
})
|
})
|
||||||
Timer timer = new Timer("download-pumper", true)
|
Timer timer = new Timer("download-pumper", true)
|
||||||
timer.schedule({
|
timer.schedule({
|
||||||
runInsideUIAsync {
|
runInsideUIAsync {
|
||||||
builder.getVariable("downloads-table").model.fireTableDataChanged()
|
if (!mvcGroup.alive)
|
||||||
builder.getVariable("uploads-table").model.fireTableDataChanged()
|
return
|
||||||
|
builder.getVariable("uploads-table")?.model.fireTableDataChanged()
|
||||||
|
|
||||||
|
def downloadTable = builder.getVariable("downloads-table")
|
||||||
|
int selectedRow = downloadTable.getSelectedRow()
|
||||||
|
downloadTable.model.fireTableDataChanged()
|
||||||
|
downloadTable.selectionModel.setSelectionInterval(selectedRow,selectedRow)
|
||||||
}
|
}
|
||||||
}, 1000, 1000)
|
}, 1000, 1000)
|
||||||
}
|
}
|
||||||
@@ -83,14 +98,23 @@ class MainFrameModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onConnectionEvent(ConnectionEvent e) {
|
void onConnectionEvent(ConnectionEvent e) {
|
||||||
|
if (e.getStatus() != ConnectionAttemptStatus.SUCCESSFUL)
|
||||||
|
return
|
||||||
runInsideUIAsync {
|
runInsideUIAsync {
|
||||||
connections = core.connectionManager.getConnections().size()
|
connections = core.connectionManager.getConnections().size()
|
||||||
|
|
||||||
|
connectionList.add(e.endpoint.destination)
|
||||||
|
JTable table = builder.getVariable("connections-table")
|
||||||
|
table.model.fireTableDataChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDisconnectionEvent(DisconnectionEvent e) {
|
void onDisconnectionEvent(DisconnectionEvent e) {
|
||||||
runInsideUIAsync {
|
runInsideUIAsync {
|
||||||
connections = core.connectionManager.getConnections().size()
|
connections = core.connectionManager.getConnections().size()
|
||||||
|
connectionList.remove(e.destination)
|
||||||
|
JTable table = builder.getVariable("connections-table")
|
||||||
|
table.model.fireTableDataChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,4 +164,24 @@ class MainFrameModel {
|
|||||||
table.model.fireTableDataChanged()
|
table.model.fireTableDataChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onQueryEvent(QueryEvent e) {
|
||||||
|
if (e.replyTo == core.me.destination)
|
||||||
|
return
|
||||||
|
StringBuilder sb = new StringBuilder()
|
||||||
|
e.searchEvent.searchTerms?.each {
|
||||||
|
sb.append(it)
|
||||||
|
sb.append(" ")
|
||||||
|
}
|
||||||
|
def search = sb.toString()
|
||||||
|
if (search.trim().size() == 0)
|
||||||
|
return
|
||||||
|
runInsideUIAsync {
|
||||||
|
searches.addFirst(search)
|
||||||
|
while(searches.size() > 200)
|
||||||
|
searches.removeLast()
|
||||||
|
JTable table = builder.getVariable("searches-table")
|
||||||
|
table.model.fireTableDataChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
BIN
gui/griffon-app/resources/close_tab.png
Normal file
BIN
gui/griffon-app/resources/close_tab.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 298 B |
@@ -9,9 +9,11 @@ import javax.swing.Box
|
|||||||
import javax.swing.BoxLayout
|
import javax.swing.BoxLayout
|
||||||
import javax.swing.JFileChooser
|
import javax.swing.JFileChooser
|
||||||
import javax.swing.JSplitPane
|
import javax.swing.JSplitPane
|
||||||
|
import javax.swing.ListSelectionModel
|
||||||
import javax.swing.SwingConstants
|
import javax.swing.SwingConstants
|
||||||
import javax.swing.border.Border
|
import javax.swing.border.Border
|
||||||
|
|
||||||
|
import com.muwire.core.download.Downloader
|
||||||
import com.muwire.core.files.FileSharedEvent
|
import com.muwire.core.files.FileSharedEvent
|
||||||
|
|
||||||
import java.awt.BorderLayout
|
import java.awt.BorderLayout
|
||||||
@@ -48,6 +50,7 @@ class MainFrameView {
|
|||||||
gridLayout(rows:1, cols: 2)
|
gridLayout(rows:1, cols: 2)
|
||||||
button(text: "Searches", actionPerformed : showSearchWindow)
|
button(text: "Searches", actionPerformed : showSearchWindow)
|
||||||
button(text: "Uploads", actionPerformed : showUploadsWindow)
|
button(text: "Uploads", actionPerformed : showUploadsWindow)
|
||||||
|
button(text: "Monitor", actionPerformed : showMonitorWindow)
|
||||||
}
|
}
|
||||||
panel(constraints: BorderLayout.CENTER) {
|
panel(constraints: BorderLayout.CENTER) {
|
||||||
borderLayout()
|
borderLayout()
|
||||||
@@ -69,9 +72,9 @@ class MainFrameView {
|
|||||||
borderLayout()
|
borderLayout()
|
||||||
tabbedPane(id : "result-tabs", constraints: BorderLayout.CENTER)
|
tabbedPane(id : "result-tabs", constraints: BorderLayout.CENTER)
|
||||||
panel(constraints : BorderLayout.SOUTH) {
|
panel(constraints : BorderLayout.SOUTH) {
|
||||||
button(text : "Download", downloadAction)
|
button(text : "Download", enabled : bind {model.searchButtonsEnabled}, downloadAction)
|
||||||
button(text : "Trust", trustAction)
|
button(text : "Trust", enabled: bind {model.searchButtonsEnabled }, trustAction)
|
||||||
button(text : "Distrust", distrustAction)
|
button(text : "Distrust", enabled : bind {model.searchButtonsEnabled}, distrustAction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panel (constraints : JSplitPane.BOTTOM) {
|
panel (constraints : JSplitPane.BOTTOM) {
|
||||||
@@ -94,6 +97,10 @@ class MainFrameView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
panel (constraints : BorderLayout.SOUTH) {
|
||||||
|
button(text: "Cancel", enabled : bind {model.cancelButtonEnabled }, cancelAction )
|
||||||
|
button(text: "Retry", enabled : bind {model.retryButtonEnabled}, resumeAction)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,7 +109,7 @@ class MainFrameView {
|
|||||||
panel {
|
panel {
|
||||||
borderLayout()
|
borderLayout()
|
||||||
panel (constraints : BorderLayout.NORTH) {
|
panel (constraints : BorderLayout.NORTH) {
|
||||||
button(text : "Shared files", actionPerformed : shareFiles)
|
button(text : "Click here to share files", actionPerformed : shareFiles)
|
||||||
}
|
}
|
||||||
scrollPane ( constraints : BorderLayout.CENTER) {
|
scrollPane ( constraints : BorderLayout.CENTER) {
|
||||||
table(id : "shared-files-table") {
|
table(id : "shared-files-table") {
|
||||||
@@ -115,7 +122,9 @@ class MainFrameView {
|
|||||||
}
|
}
|
||||||
panel {
|
panel {
|
||||||
borderLayout()
|
borderLayout()
|
||||||
label("Uploads", constraints : BorderLayout.NORTH)
|
panel (constraints : BorderLayout.NORTH){
|
||||||
|
label("Uploads")
|
||||||
|
}
|
||||||
scrollPane (constraints : BorderLayout.CENTER) {
|
scrollPane (constraints : BorderLayout.CENTER) {
|
||||||
table(id : "uploads-table") {
|
table(id : "uploads-table") {
|
||||||
tableModel(list : model.uploads) {
|
tableModel(list : model.uploads) {
|
||||||
@@ -132,6 +141,35 @@ class MainFrameView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
panel (constraints: "monitor window") {
|
||||||
|
gridLayout(rows : 1, cols : 2)
|
||||||
|
panel {
|
||||||
|
borderLayout()
|
||||||
|
panel (constraints : BorderLayout.NORTH){
|
||||||
|
label("Connections")
|
||||||
|
}
|
||||||
|
scrollPane(constraints : BorderLayout.CENTER) {
|
||||||
|
table(id : "connections-table") {
|
||||||
|
tableModel(list : model.connectionList) {
|
||||||
|
closureColumn(header : "Destination", type: String, read : { row -> row.toBase32() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panel {
|
||||||
|
borderLayout()
|
||||||
|
panel (constraints : BorderLayout.NORTH){
|
||||||
|
label("Incoming searches")
|
||||||
|
}
|
||||||
|
scrollPane(constraints : BorderLayout.CENTER) {
|
||||||
|
table(id : "searches-table") {
|
||||||
|
tableModel(list : model.searches) {
|
||||||
|
closureColumn(header : "Keywords", type : String, read : { it })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
panel (border: etchedBorder(), constraints : BorderLayout.SOUTH) {
|
panel (border: etchedBorder(), constraints : BorderLayout.SOUTH) {
|
||||||
borderLayout()
|
borderLayout()
|
||||||
@@ -146,6 +184,30 @@ class MainFrameView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mvcGroupInit(Map<String, String> args) {
|
||||||
|
def downloadsTable = builder.getVariable("downloads-table")
|
||||||
|
def selectionModel = downloadsTable.getSelectionModel()
|
||||||
|
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||||
|
selectionModel.addListSelectionListener({
|
||||||
|
int selectedRow = downloadsTable.getSelectedRow()
|
||||||
|
def downloader = model.downloads[selectedRow].downloader
|
||||||
|
switch(downloader.getCurrentState()) {
|
||||||
|
case Downloader.DownloadState.CONNECTING :
|
||||||
|
case Downloader.DownloadState.DOWNLOADING :
|
||||||
|
model.cancelButtonEnabled = true
|
||||||
|
model.retryButtonEnabled = false
|
||||||
|
break
|
||||||
|
case Downloader.DownloadState.FAILED:
|
||||||
|
model.cancelButtonEnabled = false
|
||||||
|
model.retryButtonEnabled = true
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
model.cancelButtonEnabled = false
|
||||||
|
model.retryButtonEnabled = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
def showSearchWindow = {
|
def showSearchWindow = {
|
||||||
def cardsPanel = builder.getVariable("cards-panel")
|
def cardsPanel = builder.getVariable("cards-panel")
|
||||||
cardsPanel.getLayout().show(cardsPanel, "search window")
|
cardsPanel.getLayout().show(cardsPanel, "search window")
|
||||||
@@ -156,6 +218,11 @@ class MainFrameView {
|
|||||||
cardsPanel.getLayout().show(cardsPanel, "uploads window")
|
cardsPanel.getLayout().show(cardsPanel, "uploads window")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def showMonitorWindow = {
|
||||||
|
def cardsPanel = builder.getVariable("cards-panel")
|
||||||
|
cardsPanel.getLayout().show(cardsPanel,"monitor window")
|
||||||
|
}
|
||||||
|
|
||||||
def shareFiles = {
|
def shareFiles = {
|
||||||
def chooser = new JFileChooser()
|
def chooser = new JFileChooser()
|
||||||
chooser.setDialogTitle("Select file or directory to share")
|
chooser.setDialogTitle("Select file or directory to share")
|
||||||
|
@@ -4,6 +4,8 @@ import griffon.core.artifact.GriffonView
|
|||||||
import griffon.core.mvc.MVCGroup
|
import griffon.core.mvc.MVCGroup
|
||||||
import griffon.inject.MVCMember
|
import griffon.inject.MVCMember
|
||||||
import griffon.metadata.ArtifactProviderFor
|
import griffon.metadata.ArtifactProviderFor
|
||||||
|
|
||||||
|
import javax.swing.ListSelectionModel
|
||||||
import javax.swing.SwingConstants
|
import javax.swing.SwingConstants
|
||||||
|
|
||||||
import java.awt.BorderLayout
|
import java.awt.BorderLayout
|
||||||
@@ -39,6 +41,12 @@ class SearchTabView {
|
|||||||
this.pane = pane
|
this.pane = pane
|
||||||
this.pane.putClientProperty("mvc-group", mvcGroup)
|
this.pane.putClientProperty("mvc-group", mvcGroup)
|
||||||
this.pane.putClientProperty("results-table",resultsTable)
|
this.pane.putClientProperty("results-table",resultsTable)
|
||||||
|
|
||||||
|
def selectionModel = resultsTable.getSelectionModel()
|
||||||
|
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||||
|
selectionModel.addListSelectionListener( {
|
||||||
|
mvcGroup.parentGroup.model.searchButtonsEnabled = true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +63,7 @@ class SearchTabView {
|
|||||||
panel {
|
panel {
|
||||||
label(text : searchTerms, constraints : BorderLayout.CENTER)
|
label(text : searchTerms, constraints : BorderLayout.CENTER)
|
||||||
}
|
}
|
||||||
button(text : "x", preferredSize : [17,17], constraints : BorderLayout.EAST, // TODO: in osx is probably WEST
|
button(icon : imageIcon("/close_tab.png"), preferredSize : [20,20], constraints : BorderLayout.EAST, // TODO: in osx is probably WEST
|
||||||
actionPerformed : closeTab )
|
actionPerformed : closeTab )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,6 +74,7 @@ class SearchTabView {
|
|||||||
def closeTab = {
|
def closeTab = {
|
||||||
int index = parent.indexOfTab(searchTerms)
|
int index = parent.indexOfTab(searchTerms)
|
||||||
parent.removeTabAt(index)
|
parent.removeTabAt(index)
|
||||||
|
mvcGroup.parentGroup.model.searchButtonsEnabled = false
|
||||||
mvcGroup.destroy()
|
mvcGroup.destroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user