Compare commits
26 Commits
muwire-0.1
...
muwire-0.1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
040248560a | ||
![]() |
77caaf83de | ||
![]() |
cc5ece5103 | ||
![]() |
db7e21e343 | ||
![]() |
a388eaec1d | ||
![]() |
8ff39072c7 | ||
![]() |
55d2ac9b24 | ||
![]() |
6ebe492fd8 | ||
![]() |
165cd542ec | ||
![]() |
5ca0c8b00d | ||
![]() |
b6a38e3f23 | ||
![]() |
34d9165bd5 | ||
![]() |
2e52dd5c49 | ||
![]() |
2a315dd734 | ||
![]() |
6b661b99c5 | ||
![]() |
5dacd60bbb | ||
![]() |
f8f7cfe836 | ||
![]() |
0b4f261bc1 | ||
![]() |
042d67d784 | ||
![]() |
800df88f14 | ||
![]() |
4d1eac50a0 | ||
![]() |
c48df7f14b | ||
![]() |
9d04148001 | ||
![]() |
bb4d522572 | ||
![]() |
8052501e52 | ||
![]() |
66cc6d8ab7 |
@@ -31,4 +31,4 @@ The first time you run MuWire it will ask you to select a nickname. This nickna
|
|||||||
### Known bugs and limitations
|
### Known bugs and limitations
|
||||||
|
|
||||||
* Many UI features you would expect are not there yet
|
* Many UI features you would expect are not there yet
|
||||||
|
* Sorting the results table sometimes causes the wrong result to be downloaded
|
||||||
|
@@ -34,7 +34,7 @@ class Cli {
|
|||||||
|
|
||||||
Core core
|
Core core
|
||||||
try {
|
try {
|
||||||
core = new Core(props, home, "0.1.7")
|
core = new Core(props, home, "0.1.11")
|
||||||
} catch (Exception bad) {
|
} catch (Exception bad) {
|
||||||
bad.printStackTrace(System.out)
|
bad.printStackTrace(System.out)
|
||||||
println "Failed to initialize core, exiting"
|
println "Failed to initialize core, exiting"
|
||||||
|
@@ -53,7 +53,7 @@ class CliDownloader {
|
|||||||
|
|
||||||
Core core
|
Core core
|
||||||
try {
|
try {
|
||||||
core = new Core(props, home, "0.1.7")
|
core = new Core(props, home, "0.1.11")
|
||||||
} catch (Exception bad) {
|
} catch (Exception bad) {
|
||||||
bad.printStackTrace(System.out)
|
bad.printStackTrace(System.out)
|
||||||
println "Failed to initialize core, exiting"
|
println "Failed to initialize core, exiting"
|
||||||
|
@@ -69,6 +69,7 @@ public class Core {
|
|||||||
private final ConnectionAcceptor connectionAcceptor
|
private final ConnectionAcceptor connectionAcceptor
|
||||||
private final ConnectionEstablisher connectionEstablisher
|
private final ConnectionEstablisher connectionEstablisher
|
||||||
private final HasherService hasherService
|
private final HasherService hasherService
|
||||||
|
private final DownloadManager downloadManager
|
||||||
|
|
||||||
public Core(MuWireSettings props, File home, String myVersion) {
|
public Core(MuWireSettings props, File home, String myVersion) {
|
||||||
this.home = home
|
this.home = home
|
||||||
@@ -92,9 +93,9 @@ public class Core {
|
|||||||
if (i2pOptionsFile.exists()) {
|
if (i2pOptionsFile.exists()) {
|
||||||
i2pOptionsFile.withInputStream { i2pOptions.load(it) }
|
i2pOptionsFile.withInputStream { i2pOptions.load(it) }
|
||||||
|
|
||||||
if (!i2pOptions.hasProperty("inbound.nickname"))
|
if (!i2pOptions.containsKey("inbound.nickname"))
|
||||||
i2pOptions["inbound.nickname"] = "MuWire"
|
i2pOptions["inbound.nickname"] = "MuWire"
|
||||||
if (!i2pOptions.hasProperty("outbound.nickname"))
|
if (!i2pOptions.containsKey("outbound.nickname"))
|
||||||
i2pOptions["outbound.nickname"] = "MuWire"
|
i2pOptions["outbound.nickname"] = "MuWire"
|
||||||
} else {
|
} else {
|
||||||
i2pOptions["inbound.nickname"] = "MuWire"
|
i2pOptions["inbound.nickname"] = "MuWire"
|
||||||
@@ -160,7 +161,7 @@ public class Core {
|
|||||||
eventBus.register(SearchEvent.class, fileManager)
|
eventBus.register(SearchEvent.class, fileManager)
|
||||||
|
|
||||||
log.info "initializing persistence service"
|
log.info "initializing persistence service"
|
||||||
persisterService = new PersisterService(new File(home, "files.json"), eventBus, 5000, fileManager)
|
persisterService = new PersisterService(new File(home, "files.json"), eventBus, 15000, fileManager)
|
||||||
|
|
||||||
log.info("initializing host cache")
|
log.info("initializing host cache")
|
||||||
File hostStorage = new File(home, "hosts.json")
|
File hostStorage = new File(home, "hosts.json")
|
||||||
@@ -195,7 +196,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, home, me)
|
downloadManager = new DownloadManager(eventBus, i2pConnector, home, me)
|
||||||
eventBus.register(UIDownloadEvent.class, downloadManager)
|
eventBus.register(UIDownloadEvent.class, downloadManager)
|
||||||
eventBus.register(UILoadedEvent.class, downloadManager)
|
eventBus.register(UILoadedEvent.class, downloadManager)
|
||||||
eventBus.register(FileDownloadedEvent.class, downloadManager)
|
eventBus.register(FileDownloadedEvent.class, downloadManager)
|
||||||
@@ -233,7 +234,14 @@ public class Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
|
log.info("shutting down connection manager")
|
||||||
connectionManager.shutdown()
|
connectionManager.shutdown()
|
||||||
|
log.info("shutting down download manageer")
|
||||||
|
downloadManager.shutdown()
|
||||||
|
log.info("shutting down connection acceeptor")
|
||||||
|
connectionAcceptor.stop()
|
||||||
|
log.info("shutting down connection establisher")
|
||||||
|
connectionEstablisher.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
static main(args) {
|
static main(args) {
|
||||||
@@ -260,7 +268,7 @@ public class Core {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Core core = new Core(props, home, "0.1.7")
|
Core core = new Core(props, home, "0.1.11")
|
||||||
core.startServices()
|
core.startServices()
|
||||||
|
|
||||||
// ... at the end, sleep or execute script
|
// ... at the end, sleep or execute script
|
||||||
|
@@ -76,9 +76,9 @@ abstract class Connection implements Closeable {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.info("closing $name")
|
log.info("closing $name")
|
||||||
endpoint.close()
|
|
||||||
reader.interrupt()
|
reader.interrupt()
|
||||||
writer.interrupt()
|
writer.interrupt()
|
||||||
|
endpoint.close()
|
||||||
eventBus.publish(new DisconnectionEvent(destination: endpoint.destination))
|
eventBus.publish(new DisconnectionEvent(destination: endpoint.destination))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -38,6 +38,8 @@ class ConnectionAcceptor {
|
|||||||
|
|
||||||
final ExecutorService acceptorThread
|
final ExecutorService acceptorThread
|
||||||
final ExecutorService handshakerThreads
|
final ExecutorService handshakerThreads
|
||||||
|
|
||||||
|
private volatile shutdown
|
||||||
|
|
||||||
ConnectionAcceptor(EventBus eventBus, UltrapeerConnectionManager manager,
|
ConnectionAcceptor(EventBus eventBus, UltrapeerConnectionManager manager,
|
||||||
MuWireSettings settings, I2PAcceptor acceptor, HostCache hostCache,
|
MuWireSettings settings, I2PAcceptor acceptor, HostCache hostCache,
|
||||||
@@ -73,11 +75,13 @@ class ConnectionAcceptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void stop() {
|
void stop() {
|
||||||
|
shutdown = true
|
||||||
acceptorThread.shutdownNow()
|
acceptorThread.shutdownNow()
|
||||||
handshakerThreads.shutdownNow()
|
handshakerThreads.shutdownNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
private void acceptLoop() {
|
private void acceptLoop() {
|
||||||
|
try {
|
||||||
while(true) {
|
while(true) {
|
||||||
def incoming = acceptor.accept()
|
def incoming = acceptor.accept()
|
||||||
log.info("accepted connection from ${incoming.destination.toBase32()}")
|
log.info("accepted connection from ${incoming.destination.toBase32()}")
|
||||||
@@ -93,6 +97,11 @@ class ConnectionAcceptor {
|
|||||||
}
|
}
|
||||||
handshakerThreads.execute({processIncoming(incoming)} as Runnable)
|
handshakerThreads.execute({processIncoming(incoming)} as Runnable)
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.log(Level.WARNING, "exception in accept loop",e)
|
||||||
|
if (!shutdown)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processIncoming(Endpoint e) {
|
private void processIncoming(Endpoint e) {
|
||||||
|
@@ -34,7 +34,7 @@ class Endpoint implements Closeable {
|
|||||||
try {outputStream.close()} catch (Exception ignore) {}
|
try {outputStream.close()} catch (Exception ignore) {}
|
||||||
}
|
}
|
||||||
if (toClose != null) {
|
if (toClose != null) {
|
||||||
try {toClose.close()} catch (Exception ignore) {}
|
try {toClose.reset()} catch (Exception ignore) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -104,8 +104,8 @@ class UltrapeerConnectionManager extends ConnectionManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
void shutdown() {
|
void shutdown() {
|
||||||
peerConnections.each {k,v -> v.close() }
|
peerConnections.values().stream().parallel().forEach({v -> v.close()})
|
||||||
leafConnections.each {k,v -> v.close() }
|
leafConnections.values().stream().parallel().forEach({v -> v.close()})
|
||||||
peerConnections.clear()
|
peerConnections.clear()
|
||||||
leafConnections.clear()
|
leafConnections.clear()
|
||||||
}
|
}
|
||||||
|
@@ -99,6 +99,7 @@ public class DownloadManager {
|
|||||||
}
|
}
|
||||||
def downloader = new Downloader(eventBus, this, me, file, (long)json.length,
|
def downloader = new Downloader(eventBus, this, me, file, (long)json.length,
|
||||||
infoHash, json.pieceSizePow2, connector, destinations, incompletes)
|
infoHash, json.pieceSizePow2, connector, destinations, incompletes)
|
||||||
|
downloaders.add(downloader)
|
||||||
downloader.download()
|
downloader.download()
|
||||||
eventBus.publish(new DownloadStartedEvent(downloader : downloader))
|
eventBus.publish(new DownloadStartedEvent(downloader : downloader))
|
||||||
}
|
}
|
||||||
@@ -134,4 +135,9 @@ public class DownloadManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
downloaders.each { it.stop() }
|
||||||
|
Downloader.executorService.shutdownNow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -169,6 +169,12 @@ public class Downloader {
|
|||||||
|
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
cancelled = true
|
cancelled = true
|
||||||
|
stop()
|
||||||
|
file.delete()
|
||||||
|
piecesFile.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
activeWorkers.values().each {
|
activeWorkers.values().each {
|
||||||
it.cancel()
|
it.cancel()
|
||||||
}
|
}
|
||||||
|
@@ -20,12 +20,12 @@ class FileHasher {
|
|||||||
* @return the size of each piece in power of 2
|
* @return the size of each piece in power of 2
|
||||||
*/
|
*/
|
||||||
static int getPieceSize(long size) {
|
static int getPieceSize(long size) {
|
||||||
if (size <= 0x1 << 27)
|
if (size <= 0x1 << 30)
|
||||||
return 17
|
return 17
|
||||||
|
|
||||||
for (int i = 28; i <= 37; i++) {
|
for (int i = 31; i <= 37; i++) {
|
||||||
if (size <= 0x1L << i) {
|
if (size <= 0x1L << i) {
|
||||||
return i-10
|
return i-13
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
group = com.muwire
|
group = com.muwire
|
||||||
version = 0.1.7
|
version = 0.1.11
|
||||||
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
|
||||||
|
@@ -181,6 +181,10 @@ class MainFrameController {
|
|||||||
model.hashSearch = true
|
model.hashSearch = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void unshareSelectedFiles() {
|
||||||
|
println "unsharing selected files"
|
||||||
|
}
|
||||||
|
|
||||||
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()
|
||||||
|
@@ -66,6 +66,38 @@ class OptionsController {
|
|||||||
settings.write(it)
|
settings.write(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UI Setttings
|
||||||
|
|
||||||
|
UISettings uiSettings = application.context.get("ui-settings")
|
||||||
|
text = view.lnfField.text
|
||||||
|
model.lnf = text
|
||||||
|
uiSettings.lnf = text
|
||||||
|
|
||||||
|
text = view.fontField.text
|
||||||
|
model.font = text
|
||||||
|
uiSettings.font = text
|
||||||
|
|
||||||
|
boolean showMonitor = view.monitorCheckbox.model.isSelected()
|
||||||
|
model.showMonitor = showMonitor
|
||||||
|
uiSettings.showMonitor = showMonitor
|
||||||
|
|
||||||
|
boolean clearCancelledDownloads = view.clearCancelledDownloadsCheckbox.model.isSelected()
|
||||||
|
model.clearCancelledDownloads = clearCancelledDownloads
|
||||||
|
uiSettings.clearCancelledDownloads = clearCancelledDownloads
|
||||||
|
|
||||||
|
boolean clearFinishedDownloads = view.clearFinishedDownloadsCheckbox.model.isSelected()
|
||||||
|
model.clearFinishedDownloads = clearFinishedDownloads
|
||||||
|
uiSettings.clearFinishedDownloads = clearFinishedDownloads
|
||||||
|
|
||||||
|
boolean excludeLocalResult = view.excludeLocalResultCheckbox.model.isSelected()
|
||||||
|
model.excludeLocalResult = excludeLocalResult
|
||||||
|
uiSettings.excludeLocalResult = excludeLocalResult
|
||||||
|
|
||||||
|
File uiSettingsFile = new File(core.home, "gui.properties")
|
||||||
|
uiSettingsFile.withOutputStream {
|
||||||
|
uiSettings.write(it)
|
||||||
|
}
|
||||||
|
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -18,6 +18,7 @@ import static griffon.util.GriffonApplicationUtils.isMacOSX
|
|||||||
import static groovy.swing.SwingBuilder.lookAndFeel
|
import static groovy.swing.SwingBuilder.lookAndFeel
|
||||||
|
|
||||||
import java.awt.Font
|
import java.awt.Font
|
||||||
|
import java.util.logging.Level
|
||||||
|
|
||||||
@Log
|
@Log
|
||||||
class Initialize extends AbstractLifecycleHandler {
|
class Initialize extends AbstractLifecycleHandler {
|
||||||
@@ -50,7 +51,12 @@ class Initialize extends AbstractLifecycleHandler {
|
|||||||
uiSettings = new UISettings(props)
|
uiSettings = new UISettings(props)
|
||||||
|
|
||||||
log.info("settting user-specified lnf $uiSettings.lnf")
|
log.info("settting user-specified lnf $uiSettings.lnf")
|
||||||
lookAndFeel(uiSettings.lnf)
|
try {
|
||||||
|
lookAndFeel(uiSettings.lnf)
|
||||||
|
} catch (Throwable bad) {
|
||||||
|
log.log(Level.WARNING,"couldn't set desired look and feeel, switching to defaults", bad)
|
||||||
|
uiSettings.lnf = lookAndFeel("system","gtk","metal").getID()
|
||||||
|
}
|
||||||
|
|
||||||
if (uiSettings.font != null) {
|
if (uiSettings.font != null) {
|
||||||
log.info("setting user-specified font $uiSettings.font")
|
log.info("setting user-specified font $uiSettings.font")
|
||||||
@@ -78,7 +84,7 @@ class Initialize extends AbstractLifecycleHandler {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LookAndFeel chosen = lookAndFeel('system', 'gtk')
|
LookAndFeel chosen = lookAndFeel('system', 'gtk')
|
||||||
uiSettings.lnf = chosen.name
|
uiSettings.lnf = chosen.getID()
|
||||||
log.info("ended up applying $chosen.name")
|
log.info("ended up applying $chosen.name")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -79,11 +79,28 @@ class MainFrameModel {
|
|||||||
|
|
||||||
void mvcGroupInit(Map<String, Object> args) {
|
void mvcGroupInit(Map<String, Object> args) {
|
||||||
|
|
||||||
|
UISettings uiSettings = application.context.get("ui-settings")
|
||||||
|
|
||||||
Timer timer = new Timer("download-pumper", true)
|
Timer timer = new Timer("download-pumper", true)
|
||||||
timer.schedule({
|
timer.schedule({
|
||||||
runInsideUIAsync {
|
runInsideUIAsync {
|
||||||
if (!mvcGroup.alive)
|
if (!mvcGroup.alive)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
// remove cancelled or finished downloads
|
||||||
|
def toRemove = []
|
||||||
|
downloads.each {
|
||||||
|
if (uiSettings.clearCancelledDownloads &&
|
||||||
|
it.downloader.getCurrentState() == Downloader.DownloadState.CANCELLED)
|
||||||
|
toRemove << it
|
||||||
|
if (uiSettings.clearFinishedDownloads &&
|
||||||
|
it.downloader.getCurrentState() == Downloader.DownloadState.FINISHED)
|
||||||
|
toRemove << it
|
||||||
|
}
|
||||||
|
toRemove.each {
|
||||||
|
downloads.remove(it)
|
||||||
|
}
|
||||||
|
|
||||||
builder.getVariable("uploads-table")?.model.fireTableDataChanged()
|
builder.getVariable("uploads-table")?.model.fireTableDataChanged()
|
||||||
|
|
||||||
updateTablePreservingSelection("downloads-table")
|
updateTablePreservingSelection("downloads-table")
|
||||||
|
@@ -20,6 +20,14 @@ class OptionsModel {
|
|||||||
@Observable String outboundLength
|
@Observable String outboundLength
|
||||||
@Observable String outboundQuantity
|
@Observable String outboundQuantity
|
||||||
|
|
||||||
|
// gui options
|
||||||
|
@Observable boolean showMonitor
|
||||||
|
@Observable String lnf
|
||||||
|
@Observable String font
|
||||||
|
@Observable boolean clearCancelledDownloads
|
||||||
|
@Observable boolean clearFinishedDownloads
|
||||||
|
@Observable boolean excludeLocalResult
|
||||||
|
|
||||||
void mvcGroupInit(Map<String, String> args) {
|
void mvcGroupInit(Map<String, String> args) {
|
||||||
MuWireSettings settings = application.context.get("muwire-settings")
|
MuWireSettings settings = application.context.get("muwire-settings")
|
||||||
downloadRetryInterval = settings.downloadRetryInterval
|
downloadRetryInterval = settings.downloadRetryInterval
|
||||||
@@ -32,5 +40,13 @@ class OptionsModel {
|
|||||||
inboundQuantity = core.i2pOptions["inbound.quantity"]
|
inboundQuantity = core.i2pOptions["inbound.quantity"]
|
||||||
outboundLength = core.i2pOptions["outbound.length"]
|
outboundLength = core.i2pOptions["outbound.length"]
|
||||||
outboundQuantity = core.i2pOptions["outbound.quantity"]
|
outboundQuantity = core.i2pOptions["outbound.quantity"]
|
||||||
|
|
||||||
|
UISettings uiSettings = application.context.get("ui-settings")
|
||||||
|
showMonitor = uiSettings.showMonitor
|
||||||
|
lnf = uiSettings.lnf
|
||||||
|
font = uiSettings.font
|
||||||
|
clearCancelledDownloads = uiSettings.clearCancelledDownloads
|
||||||
|
clearFinishedDownloads = uiSettings.clearFinishedDownloads
|
||||||
|
excludeLocalResult = uiSettings.excludeLocalResult
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -19,6 +19,7 @@ class SearchTabModel {
|
|||||||
FactoryBuilderSupport builder
|
FactoryBuilderSupport builder
|
||||||
|
|
||||||
Core core
|
Core core
|
||||||
|
UISettings uiSettings
|
||||||
String uuid
|
String uuid
|
||||||
def results = []
|
def results = []
|
||||||
def hashBucket = [:]
|
def hashBucket = [:]
|
||||||
@@ -26,6 +27,7 @@ class SearchTabModel {
|
|||||||
|
|
||||||
void mvcGroupInit(Map<String, String> args) {
|
void mvcGroupInit(Map<String, String> args) {
|
||||||
core = mvcGroup.parentGroup.model.core
|
core = mvcGroup.parentGroup.model.core
|
||||||
|
uiSettings = application.context.get("ui-settings")
|
||||||
mvcGroup.parentGroup.model.results[UUID.fromString(uuid)] = mvcGroup
|
mvcGroup.parentGroup.model.results[UUID.fromString(uuid)] = mvcGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,6 +36,9 @@ class SearchTabModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void handleResult(UIResultEvent e) {
|
void handleResult(UIResultEvent e) {
|
||||||
|
if (uiSettings.excludeLocalResult &&
|
||||||
|
e.sender == core.me)
|
||||||
|
return
|
||||||
runInsideUIAsync {
|
runInsideUIAsync {
|
||||||
def bucket = hashBucket.get(e.infohash)
|
def bucket = hashBucket.get(e.infohash)
|
||||||
if (bucket == null) {
|
if (bucket == null) {
|
||||||
|
@@ -10,6 +10,8 @@ import javax.swing.Box
|
|||||||
import javax.swing.BoxLayout
|
import javax.swing.BoxLayout
|
||||||
import javax.swing.JFileChooser
|
import javax.swing.JFileChooser
|
||||||
import javax.swing.JLabel
|
import javax.swing.JLabel
|
||||||
|
import javax.swing.JMenuItem
|
||||||
|
import javax.swing.JPopupMenu
|
||||||
import javax.swing.JSplitPane
|
import javax.swing.JSplitPane
|
||||||
import javax.swing.ListSelectionModel
|
import javax.swing.ListSelectionModel
|
||||||
import javax.swing.SwingConstants
|
import javax.swing.SwingConstants
|
||||||
@@ -26,6 +28,8 @@ import java.awt.FlowLayout
|
|||||||
import java.awt.GridBagConstraints
|
import java.awt.GridBagConstraints
|
||||||
import java.awt.GridBagLayout
|
import java.awt.GridBagLayout
|
||||||
import java.awt.Insets
|
import java.awt.Insets
|
||||||
|
import java.awt.event.MouseAdapter
|
||||||
|
import java.awt.event.MouseEvent
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
import javax.annotation.Nonnull
|
import javax.annotation.Nonnull
|
||||||
@@ -41,6 +45,7 @@ class MainFrameView {
|
|||||||
def lastDownloadSortEvent
|
def lastDownloadSortEvent
|
||||||
|
|
||||||
void initUI() {
|
void initUI() {
|
||||||
|
UISettings 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,
|
||||||
@@ -63,7 +68,8 @@ 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)
|
if (settings.showMonitor)
|
||||||
|
button(text: "Monitor", actionPerformed : showMonitorWindow)
|
||||||
button(text: "Trust", actionPerformed : showTrustWindow)
|
button(text: "Trust", actionPerformed : showTrustWindow)
|
||||||
}
|
}
|
||||||
panel(id: "top-panel", constraints: BorderLayout.CENTER) {
|
panel(id: "top-panel", constraints: BorderLayout.CENTER) {
|
||||||
@@ -142,8 +148,7 @@ class MainFrameView {
|
|||||||
table(id : "shared-files-table", autoCreateRowSorter: true) {
|
table(id : "shared-files-table", autoCreateRowSorter: true) {
|
||||||
tableModel(list : model.shared) {
|
tableModel(list : model.shared) {
|
||||||
closureColumn(header : "Name", preferredWidth : 550, type : String, read : {row -> row.file.getAbsolutePath()})
|
closureColumn(header : "Name", preferredWidth : 550, type : String, read : {row -> row.file.getAbsolutePath()})
|
||||||
closureColumn(header : "Size", preferredWidth : 50, type : String,
|
closureColumn(header : "Size", preferredWidth : 50, type : Long, read : {row -> row.file.length() })
|
||||||
read : {row -> DataHelper.formatSize2Decimal(row.file.length(),false) + "B"})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,7 +269,14 @@ class MainFrameView {
|
|||||||
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||||
selectionModel.addListSelectionListener({
|
selectionModel.addListSelectionListener({
|
||||||
int selectedRow = selectedDownloaderRow()
|
int selectedRow = selectedDownloaderRow()
|
||||||
def downloader = model.downloads[selectedRow].downloader
|
if (selectedRow < 0) {
|
||||||
|
model.cancelButtonEnabled = false
|
||||||
|
model.retryButtonEnabled = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
def downloader = model.downloads[selectedRow]?.downloader
|
||||||
|
if (downloader == null)
|
||||||
|
return
|
||||||
switch(downloader.getCurrentState()) {
|
switch(downloader.getCurrentState()) {
|
||||||
case Downloader.DownloadState.CONNECTING :
|
case Downloader.DownloadState.CONNECTING :
|
||||||
case Downloader.DownloadState.DOWNLOADING :
|
case Downloader.DownloadState.DOWNLOADING :
|
||||||
@@ -286,6 +298,31 @@ class MainFrameView {
|
|||||||
downloadsTable.setDefaultRenderer(Integer.class, centerRenderer)
|
downloadsTable.setDefaultRenderer(Integer.class, centerRenderer)
|
||||||
|
|
||||||
downloadsTable.rowSorter.addRowSorterListener({evt -> lastDownloadSortEvent = evt})
|
downloadsTable.rowSorter.addRowSorterListener({evt -> lastDownloadSortEvent = evt})
|
||||||
|
|
||||||
|
// shared files table
|
||||||
|
def sharedFilesTable = builder.getVariable("shared-files-table")
|
||||||
|
sharedFilesTable.columnModel.getColumn(1).setCellRenderer(new SizeRenderer())
|
||||||
|
|
||||||
|
JPopupMenu sharedFilesMenu = new JPopupMenu()
|
||||||
|
JMenuItem unshareSelectedFiles = new JMenuItem("Unshare selected files")
|
||||||
|
unshareSelectedFiles.addActionListener({mvcGroup.controller.unshareSelectedFiles()})
|
||||||
|
sharedFilesMenu.add(unshareSelectedFiles)
|
||||||
|
sharedFilesTable.addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseReleased(MouseEvent e) {
|
||||||
|
if (e.isPopupTrigger())
|
||||||
|
showPopupMenu(sharedFilesMenu, e)
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
if (e.isPopupTrigger())
|
||||||
|
showPopupMenu(sharedFilesMenu, e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
def showPopupMenu(JPopupMenu menu, MouseEvent event) {
|
||||||
|
menu.show(event.getComponent(), event.getX(), event.getY())
|
||||||
}
|
}
|
||||||
|
|
||||||
int selectedDownloaderRow() {
|
int selectedDownloaderRow() {
|
||||||
|
@@ -25,6 +25,8 @@ class OptionsView {
|
|||||||
def d
|
def d
|
||||||
def p
|
def p
|
||||||
def i
|
def i
|
||||||
|
def u
|
||||||
|
|
||||||
def retryField
|
def retryField
|
||||||
def updateField
|
def updateField
|
||||||
def allowUntrustedCheckbox
|
def allowUntrustedCheckbox
|
||||||
@@ -35,6 +37,13 @@ class OptionsView {
|
|||||||
def outboundLengthField
|
def outboundLengthField
|
||||||
def outboundQuantityField
|
def outboundQuantityField
|
||||||
|
|
||||||
|
def lnfField
|
||||||
|
def monitorCheckbox
|
||||||
|
def fontField
|
||||||
|
def clearCancelledDownloadsCheckbox
|
||||||
|
def clearFinishedDownloadsCheckbox
|
||||||
|
def excludeLocalResultCheckbox
|
||||||
|
|
||||||
def buttonsPanel
|
def buttonsPanel
|
||||||
|
|
||||||
def mainFrame
|
def mainFrame
|
||||||
@@ -72,6 +81,22 @@ class OptionsView {
|
|||||||
label(text : "Outbound Quantity", constraints : gbc(gridx:0, gridy:4))
|
label(text : "Outbound Quantity", constraints : gbc(gridx:0, gridy:4))
|
||||||
outboundQuantityField = textField(text : bind {model.outboundQuantity}, columns : 2, constraints : gbc(gridx:1, gridy:4))
|
outboundQuantityField = textField(text : bind {model.outboundQuantity}, columns : 2, constraints : gbc(gridx:1, gridy:4))
|
||||||
}
|
}
|
||||||
|
u = builder.panel {
|
||||||
|
gridBagLayout()
|
||||||
|
label(text : "Changing these settings requires a restart", constraints : gbc(gridx : 0, gridy : 0, gridwidth: 2))
|
||||||
|
label(text : "Look And Feel", constraints : gbc(gridx: 0, gridy:1))
|
||||||
|
lnfField = textField(text : bind {model.lnf}, columns : 4, constraints : gbc(gridx : 1, gridy : 1))
|
||||||
|
label(text : "Font", constraints : gbc(gridx: 0, gridy : 2))
|
||||||
|
fontField = textField(text : bind {model.font}, columns : 4, constraints : gbc(gridx : 1, gridy:2))
|
||||||
|
label(text : "Show Monitor", constraints : gbc(gridx :0, gridy: 3))
|
||||||
|
monitorCheckbox = checkBox(selected : bind {model.showMonitor}, constraints : gbc(gridx : 1, gridy: 3))
|
||||||
|
label(text : "Clear Cancelled Downloads", constraints: gbc(gridx: 0, gridy:4))
|
||||||
|
clearCancelledDownloadsCheckbox = checkBox(selected : bind {model.clearCancelledDownloads}, constraints : gbc(gridx : 1, gridy:4))
|
||||||
|
label(text : "Clear Finished Downloads", constraints: gbc(gridx: 0, gridy:5))
|
||||||
|
clearFinishedDownloadsCheckbox = checkBox(selected : bind {model.clearFinishedDownloads}, constraints : gbc(gridx : 1, gridy:5))
|
||||||
|
label(text : "Exclude Local Files From Results", constraints: gbc(gridx:0, gridy:6))
|
||||||
|
excludeLocalResultCheckbox = checkBox(selected : bind {model.excludeLocalResult}, constraints : gbc(gridx: 1, gridy : 6))
|
||||||
|
}
|
||||||
buttonsPanel = builder.panel {
|
buttonsPanel = builder.panel {
|
||||||
gridBagLayout()
|
gridBagLayout()
|
||||||
button(text : "Save", constraints : gbc(gridx : 1, gridy: 2), saveAction)
|
button(text : "Save", constraints : gbc(gridx : 1, gridy: 2), saveAction)
|
||||||
@@ -81,8 +106,9 @@ class OptionsView {
|
|||||||
|
|
||||||
void mvcGroupInit(Map<String,String> args) {
|
void mvcGroupInit(Map<String,String> args) {
|
||||||
def tabbedPane = new JTabbedPane()
|
def tabbedPane = new JTabbedPane()
|
||||||
tabbedPane.addTab("MuWire Options", p)
|
tabbedPane.addTab("MuWire", p)
|
||||||
tabbedPane.addTab("I2P Options", i)
|
tabbedPane.addTab("I2P", i)
|
||||||
|
tabbedPane.addTab("GUI", u)
|
||||||
|
|
||||||
JPanel panel = new JPanel()
|
JPanel panel = new JPanel()
|
||||||
panel.setLayout(new BorderLayout())
|
panel.setLayout(new BorderLayout())
|
||||||
|
@@ -6,12 +6,19 @@ import griffon.inject.MVCMember
|
|||||||
import griffon.metadata.ArtifactProviderFor
|
import griffon.metadata.ArtifactProviderFor
|
||||||
import net.i2p.data.DataHelper
|
import net.i2p.data.DataHelper
|
||||||
|
|
||||||
|
import javax.swing.JComponent
|
||||||
import javax.swing.JLabel
|
import javax.swing.JLabel
|
||||||
|
import javax.swing.JTable
|
||||||
import javax.swing.ListSelectionModel
|
import javax.swing.ListSelectionModel
|
||||||
import javax.swing.SwingConstants
|
import javax.swing.SwingConstants
|
||||||
import javax.swing.table.DefaultTableCellRenderer
|
import javax.swing.table.DefaultTableCellRenderer
|
||||||
|
|
||||||
|
import com.muwire.core.util.DataUtil
|
||||||
|
|
||||||
import java.awt.BorderLayout
|
import java.awt.BorderLayout
|
||||||
|
import java.awt.Color
|
||||||
|
import java.awt.event.MouseAdapter
|
||||||
|
import java.awt.event.MouseEvent
|
||||||
|
|
||||||
import javax.annotation.Nonnull
|
import javax.annotation.Nonnull
|
||||||
|
|
||||||
@@ -35,7 +42,7 @@ class SearchTabView {
|
|||||||
resultsTable = table(id : "results-table", autoCreateRowSorter : true) {
|
resultsTable = table(id : "results-table", autoCreateRowSorter : true) {
|
||||||
tableModel(list: model.results) {
|
tableModel(list: model.results) {
|
||||||
closureColumn(header: "Name", preferredWidth: 350, type: String, read : {row -> row.name.replace('<','_')})
|
closureColumn(header: "Name", preferredWidth: 350, type: String, read : {row -> row.name.replace('<','_')})
|
||||||
closureColumn(header: "Size", preferredWidth: 50, type: String, read : {row -> DataHelper.formatSize2Decimal(row.size, false)+"B"})
|
closureColumn(header: "Size", preferredWidth: 50, type: Long, read : {row -> row.size})
|
||||||
closureColumn(header: "Sources", preferredWidth: 10, type : Integer, read : { row -> model.hashBucket[row.infohash].size()})
|
closureColumn(header: "Sources", preferredWidth: 10, type : Integer, read : { row -> model.hashBucket[row.infohash].size()})
|
||||||
closureColumn(header: "Sender", preferredWidth: 170, type: String, read : {row -> row.sender.getHumanReadableName()})
|
closureColumn(header: "Sender", preferredWidth: 170, type: String, read : {row -> row.sender.getHumanReadableName()})
|
||||||
closureColumn(header: "Trust", preferredWidth: 50, type: String, read : {row ->
|
closureColumn(header: "Trust", preferredWidth: 50, type: String, read : {row ->
|
||||||
@@ -86,7 +93,18 @@ class SearchTabView {
|
|||||||
resultsTable.setDefaultRenderer(Integer.class,centerRenderer)
|
resultsTable.setDefaultRenderer(Integer.class,centerRenderer)
|
||||||
resultsTable.columnModel.getColumn(4).setCellRenderer(centerRenderer)
|
resultsTable.columnModel.getColumn(4).setCellRenderer(centerRenderer)
|
||||||
|
|
||||||
|
resultsTable.columnModel.getColumn(1).setCellRenderer(new SizeRenderer())
|
||||||
|
|
||||||
|
|
||||||
resultsTable.rowSorter.addRowSorterListener({ evt -> lastSortEvent = evt})
|
resultsTable.rowSorter.addRowSorterListener({ evt -> lastSortEvent = evt})
|
||||||
|
|
||||||
|
resultsTable.addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
if (e.button == MouseEvent.BUTTON1 && e.clickCount == 2)
|
||||||
|
mvcGroup.parentGroup.controller.download()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
def closeTab = {
|
def closeTab = {
|
||||||
|
29
gui/src/main/groovy/com/muwire/gui/SizeRenderer.groovy
Normal file
29
gui/src/main/groovy/com/muwire/gui/SizeRenderer.groovy
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package com.muwire.gui
|
||||||
|
|
||||||
|
import javax.swing.JComponent
|
||||||
|
import javax.swing.JLabel
|
||||||
|
import javax.swing.JTable
|
||||||
|
import javax.swing.table.DefaultTableCellRenderer
|
||||||
|
|
||||||
|
import net.i2p.data.DataHelper
|
||||||
|
|
||||||
|
class SizeRenderer extends DefaultTableCellRenderer {
|
||||||
|
SizeRenderer() {
|
||||||
|
setHorizontalAlignment(JLabel.CENTER)
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
JComponent getTableCellRendererComponent(JTable table, Object value,
|
||||||
|
boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
|
Long l = (Long) value
|
||||||
|
String formatted = DataHelper.formatSize2Decimal(l, false)+"B"
|
||||||
|
setText(formatted)
|
||||||
|
if (isSelected) {
|
||||||
|
setForeground(table.getSelectionForeground())
|
||||||
|
setBackground(table.getSelectionBackground())
|
||||||
|
} else {
|
||||||
|
setForeground(table.getForeground())
|
||||||
|
setBackground(table.getBackground())
|
||||||
|
}
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
@@ -5,19 +5,30 @@ class UISettings {
|
|||||||
String lnf
|
String lnf
|
||||||
boolean showMonitor
|
boolean showMonitor
|
||||||
String font
|
String font
|
||||||
|
boolean clearCancelledDownloads
|
||||||
|
boolean clearFinishedDownloads
|
||||||
|
boolean excludeLocalResult
|
||||||
|
|
||||||
UISettings(Properties props) {
|
UISettings(Properties props) {
|
||||||
lnf = props.getProperty("lnf", "system")
|
lnf = props.getProperty("lnf", "system")
|
||||||
showMonitor = Boolean.parseBoolean(props.getProperty("showMonitor", "true"))
|
showMonitor = Boolean.parseBoolean(props.getProperty("showMonitor", "true"))
|
||||||
font = props.getProperty("font",null)
|
font = props.getProperty("font",null)
|
||||||
|
clearCancelledDownloads = Boolean.parseBoolean(props.getProperty("clearCancelledDownloads","false"))
|
||||||
|
clearFinishedDownloads = Boolean.parseBoolean(props.getProperty("clearFinishedDownloads","false"))
|
||||||
|
excludeLocalResult = Boolean.parseBoolean(props.getProperty("excludeLocalResult","false"))
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(OutputStream out) throws IOException {
|
void write(OutputStream out) throws IOException {
|
||||||
Properties props = new Properties()
|
Properties props = new Properties()
|
||||||
props.setProperty("lnf", lnf)
|
props.setProperty("lnf", lnf)
|
||||||
props.setProperty("showMonitor", showMonitor)
|
props.setProperty("showMonitor", String.valueOf(showMonitor))
|
||||||
|
props.setProperty("clearCancelledDownloads", String.valueOf(clearCancelledDownloads))
|
||||||
|
props.setProperty("clearFinishedDownloads", String.valueOf(clearFinishedDownloads))
|
||||||
|
props.setProperty("excludeLocalResult", String.valueOf(excludeLocalResult))
|
||||||
if (font != null)
|
if (font != null)
|
||||||
props.setProperty("font", font)
|
props.setProperty("font", font)
|
||||||
|
|
||||||
|
|
||||||
props.store(out, "UI Properties")
|
props.store(out, "UI Properties")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user