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
|
||||
|
||||
* 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
|
||||
try {
|
||||
core = new Core(props, home, "0.1.7")
|
||||
core = new Core(props, home, "0.1.11")
|
||||
} catch (Exception bad) {
|
||||
bad.printStackTrace(System.out)
|
||||
println "Failed to initialize core, exiting"
|
||||
|
@@ -53,7 +53,7 @@ class CliDownloader {
|
||||
|
||||
Core core
|
||||
try {
|
||||
core = new Core(props, home, "0.1.7")
|
||||
core = new Core(props, home, "0.1.11")
|
||||
} catch (Exception bad) {
|
||||
bad.printStackTrace(System.out)
|
||||
println "Failed to initialize core, exiting"
|
||||
|
@@ -69,6 +69,7 @@ public class Core {
|
||||
private final ConnectionAcceptor connectionAcceptor
|
||||
private final ConnectionEstablisher connectionEstablisher
|
||||
private final HasherService hasherService
|
||||
private final DownloadManager downloadManager
|
||||
|
||||
public Core(MuWireSettings props, File home, String myVersion) {
|
||||
this.home = home
|
||||
@@ -92,9 +93,9 @@ public class Core {
|
||||
if (i2pOptionsFile.exists()) {
|
||||
i2pOptionsFile.withInputStream { i2pOptions.load(it) }
|
||||
|
||||
if (!i2pOptions.hasProperty("inbound.nickname"))
|
||||
if (!i2pOptions.containsKey("inbound.nickname"))
|
||||
i2pOptions["inbound.nickname"] = "MuWire"
|
||||
if (!i2pOptions.hasProperty("outbound.nickname"))
|
||||
if (!i2pOptions.containsKey("outbound.nickname"))
|
||||
i2pOptions["outbound.nickname"] = "MuWire"
|
||||
} else {
|
||||
i2pOptions["inbound.nickname"] = "MuWire"
|
||||
@@ -160,7 +161,7 @@ public class Core {
|
||||
eventBus.register(SearchEvent.class, fileManager)
|
||||
|
||||
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")
|
||||
File hostStorage = new File(home, "hosts.json")
|
||||
@@ -195,7 +196,7 @@ public class Core {
|
||||
eventBus.register(ResultsEvent.class, searchManager)
|
||||
|
||||
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(UILoadedEvent.class, downloadManager)
|
||||
eventBus.register(FileDownloadedEvent.class, downloadManager)
|
||||
@@ -233,7 +234,14 @@ public class Core {
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
log.info("shutting down connection manager")
|
||||
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) {
|
||||
@@ -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()
|
||||
|
||||
// ... at the end, sleep or execute script
|
||||
|
@@ -76,9 +76,9 @@ abstract class Connection implements Closeable {
|
||||
return
|
||||
}
|
||||
log.info("closing $name")
|
||||
endpoint.close()
|
||||
reader.interrupt()
|
||||
writer.interrupt()
|
||||
endpoint.close()
|
||||
eventBus.publish(new DisconnectionEvent(destination: endpoint.destination))
|
||||
}
|
||||
|
||||
|
@@ -38,6 +38,8 @@ class ConnectionAcceptor {
|
||||
|
||||
final ExecutorService acceptorThread
|
||||
final ExecutorService handshakerThreads
|
||||
|
||||
private volatile shutdown
|
||||
|
||||
ConnectionAcceptor(EventBus eventBus, UltrapeerConnectionManager manager,
|
||||
MuWireSettings settings, I2PAcceptor acceptor, HostCache hostCache,
|
||||
@@ -73,11 +75,13 @@ class ConnectionAcceptor {
|
||||
}
|
||||
|
||||
void stop() {
|
||||
shutdown = true
|
||||
acceptorThread.shutdownNow()
|
||||
handshakerThreads.shutdownNow()
|
||||
}
|
||||
|
||||
private void acceptLoop() {
|
||||
try {
|
||||
while(true) {
|
||||
def incoming = acceptor.accept()
|
||||
log.info("accepted connection from ${incoming.destination.toBase32()}")
|
||||
@@ -93,6 +97,11 @@ class ConnectionAcceptor {
|
||||
}
|
||||
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) {
|
||||
|
@@ -34,7 +34,7 @@ class Endpoint implements Closeable {
|
||||
try {outputStream.close()} catch (Exception ignore) {}
|
||||
}
|
||||
if (toClose != null) {
|
||||
try {toClose.close()} catch (Exception ignore) {}
|
||||
try {toClose.reset()} catch (Exception ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -104,8 +104,8 @@ class UltrapeerConnectionManager extends ConnectionManager {
|
||||
|
||||
@Override
|
||||
void shutdown() {
|
||||
peerConnections.each {k,v -> v.close() }
|
||||
leafConnections.each {k,v -> v.close() }
|
||||
peerConnections.values().stream().parallel().forEach({v -> v.close()})
|
||||
leafConnections.values().stream().parallel().forEach({v -> v.close()})
|
||||
peerConnections.clear()
|
||||
leafConnections.clear()
|
||||
}
|
||||
|
@@ -99,6 +99,7 @@ public class DownloadManager {
|
||||
}
|
||||
def downloader = new Downloader(eventBus, this, me, file, (long)json.length,
|
||||
infoHash, json.pieceSizePow2, connector, destinations, incompletes)
|
||||
downloaders.add(downloader)
|
||||
downloader.download()
|
||||
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() {
|
||||
cancelled = true
|
||||
stop()
|
||||
file.delete()
|
||||
piecesFile.delete()
|
||||
}
|
||||
|
||||
void stop() {
|
||||
activeWorkers.values().each {
|
||||
it.cancel()
|
||||
}
|
||||
|
@@ -20,12 +20,12 @@ class FileHasher {
|
||||
* @return the size of each piece in power of 2
|
||||
*/
|
||||
static int getPieceSize(long size) {
|
||||
if (size <= 0x1 << 27)
|
||||
if (size <= 0x1 << 30)
|
||||
return 17
|
||||
|
||||
for (int i = 28; i <= 37; i++) {
|
||||
for (int i = 31; i <= 37; i++) {
|
||||
if (size <= 0x1L << i) {
|
||||
return i-10
|
||||
return i-13
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
group = com.muwire
|
||||
version = 0.1.7
|
||||
version = 0.1.11
|
||||
groovyVersion = 2.4.15
|
||||
slf4jVersion = 1.7.25
|
||||
spockVersion = 1.1-groovy-2.4
|
||||
|
@@ -181,6 +181,10 @@ class MainFrameController {
|
||||
model.hashSearch = true
|
||||
}
|
||||
|
||||
void unshareSelectedFiles() {
|
||||
println "unsharing selected files"
|
||||
}
|
||||
|
||||
void mvcGroupInit(Map<String, String> args) {
|
||||
application.addPropertyChangeListener("core", {e->
|
||||
core = e.getNewValue()
|
||||
|
@@ -66,6 +66,38 @@ class OptionsController {
|
||||
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()
|
||||
}
|
||||
|
||||
|
@@ -18,6 +18,7 @@ import static griffon.util.GriffonApplicationUtils.isMacOSX
|
||||
import static groovy.swing.SwingBuilder.lookAndFeel
|
||||
|
||||
import java.awt.Font
|
||||
import java.util.logging.Level
|
||||
|
||||
@Log
|
||||
class Initialize extends AbstractLifecycleHandler {
|
||||
@@ -50,7 +51,12 @@ class Initialize extends AbstractLifecycleHandler {
|
||||
uiSettings = new UISettings(props)
|
||||
|
||||
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) {
|
||||
log.info("setting user-specified font $uiSettings.font")
|
||||
@@ -78,7 +84,7 @@ class Initialize extends AbstractLifecycleHandler {
|
||||
}
|
||||
} else {
|
||||
LookAndFeel chosen = lookAndFeel('system', 'gtk')
|
||||
uiSettings.lnf = chosen.name
|
||||
uiSettings.lnf = chosen.getID()
|
||||
log.info("ended up applying $chosen.name")
|
||||
}
|
||||
}
|
||||
|
@@ -79,11 +79,28 @@ class MainFrameModel {
|
||||
|
||||
void mvcGroupInit(Map<String, Object> args) {
|
||||
|
||||
UISettings uiSettings = application.context.get("ui-settings")
|
||||
|
||||
Timer timer = new Timer("download-pumper", true)
|
||||
timer.schedule({
|
||||
runInsideUIAsync {
|
||||
if (!mvcGroup.alive)
|
||||
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()
|
||||
|
||||
updateTablePreservingSelection("downloads-table")
|
||||
|
@@ -20,6 +20,14 @@ class OptionsModel {
|
||||
@Observable String outboundLength
|
||||
@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) {
|
||||
MuWireSettings settings = application.context.get("muwire-settings")
|
||||
downloadRetryInterval = settings.downloadRetryInterval
|
||||
@@ -32,5 +40,13 @@ class OptionsModel {
|
||||
inboundQuantity = core.i2pOptions["inbound.quantity"]
|
||||
outboundLength = core.i2pOptions["outbound.length"]
|
||||
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
|
||||
|
||||
Core core
|
||||
UISettings uiSettings
|
||||
String uuid
|
||||
def results = []
|
||||
def hashBucket = [:]
|
||||
@@ -26,6 +27,7 @@ class SearchTabModel {
|
||||
|
||||
void mvcGroupInit(Map<String, String> args) {
|
||||
core = mvcGroup.parentGroup.model.core
|
||||
uiSettings = application.context.get("ui-settings")
|
||||
mvcGroup.parentGroup.model.results[UUID.fromString(uuid)] = mvcGroup
|
||||
}
|
||||
|
||||
@@ -34,6 +36,9 @@ class SearchTabModel {
|
||||
}
|
||||
|
||||
void handleResult(UIResultEvent e) {
|
||||
if (uiSettings.excludeLocalResult &&
|
||||
e.sender == core.me)
|
||||
return
|
||||
runInsideUIAsync {
|
||||
def bucket = hashBucket.get(e.infohash)
|
||||
if (bucket == null) {
|
||||
|
@@ -10,6 +10,8 @@ import javax.swing.Box
|
||||
import javax.swing.BoxLayout
|
||||
import javax.swing.JFileChooser
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JMenuItem
|
||||
import javax.swing.JPopupMenu
|
||||
import javax.swing.JSplitPane
|
||||
import javax.swing.ListSelectionModel
|
||||
import javax.swing.SwingConstants
|
||||
@@ -26,6 +28,8 @@ import java.awt.FlowLayout
|
||||
import java.awt.GridBagConstraints
|
||||
import java.awt.GridBagLayout
|
||||
import java.awt.Insets
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
import javax.annotation.Nonnull
|
||||
@@ -41,6 +45,7 @@ class MainFrameView {
|
||||
def lastDownloadSortEvent
|
||||
|
||||
void initUI() {
|
||||
UISettings settings = application.context.get("ui-settings")
|
||||
builder.with {
|
||||
application(size : [1024,768], id: 'main-frame',
|
||||
locationRelativeTo : null,
|
||||
@@ -63,7 +68,8 @@ class MainFrameView {
|
||||
gridLayout(rows:1, cols: 2)
|
||||
button(text: "Searches", actionPerformed : showSearchWindow)
|
||||
button(text: "Uploads", actionPerformed : showUploadsWindow)
|
||||
button(text: "Monitor", actionPerformed : showMonitorWindow)
|
||||
if (settings.showMonitor)
|
||||
button(text: "Monitor", actionPerformed : showMonitorWindow)
|
||||
button(text: "Trust", actionPerformed : showTrustWindow)
|
||||
}
|
||||
panel(id: "top-panel", constraints: BorderLayout.CENTER) {
|
||||
@@ -142,8 +148,7 @@ class MainFrameView {
|
||||
table(id : "shared-files-table", autoCreateRowSorter: true) {
|
||||
tableModel(list : model.shared) {
|
||||
closureColumn(header : "Name", preferredWidth : 550, type : String, read : {row -> row.file.getAbsolutePath()})
|
||||
closureColumn(header : "Size", preferredWidth : 50, type : String,
|
||||
read : {row -> DataHelper.formatSize2Decimal(row.file.length(),false) + "B"})
|
||||
closureColumn(header : "Size", preferredWidth : 50, type : Long, read : {row -> row.file.length() })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -264,7 +269,14 @@ class MainFrameView {
|
||||
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||
selectionModel.addListSelectionListener({
|
||||
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()) {
|
||||
case Downloader.DownloadState.CONNECTING :
|
||||
case Downloader.DownloadState.DOWNLOADING :
|
||||
@@ -286,6 +298,31 @@ class MainFrameView {
|
||||
downloadsTable.setDefaultRenderer(Integer.class, centerRenderer)
|
||||
|
||||
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() {
|
||||
|
@@ -25,6 +25,8 @@ class OptionsView {
|
||||
def d
|
||||
def p
|
||||
def i
|
||||
def u
|
||||
|
||||
def retryField
|
||||
def updateField
|
||||
def allowUntrustedCheckbox
|
||||
@@ -35,6 +37,13 @@ class OptionsView {
|
||||
def outboundLengthField
|
||||
def outboundQuantityField
|
||||
|
||||
def lnfField
|
||||
def monitorCheckbox
|
||||
def fontField
|
||||
def clearCancelledDownloadsCheckbox
|
||||
def clearFinishedDownloadsCheckbox
|
||||
def excludeLocalResultCheckbox
|
||||
|
||||
def buttonsPanel
|
||||
|
||||
def mainFrame
|
||||
@@ -72,6 +81,22 @@ class OptionsView {
|
||||
label(text : "Outbound Quantity", constraints : gbc(gridx:0, 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 {
|
||||
gridBagLayout()
|
||||
button(text : "Save", constraints : gbc(gridx : 1, gridy: 2), saveAction)
|
||||
@@ -81,8 +106,9 @@ class OptionsView {
|
||||
|
||||
void mvcGroupInit(Map<String,String> args) {
|
||||
def tabbedPane = new JTabbedPane()
|
||||
tabbedPane.addTab("MuWire Options", p)
|
||||
tabbedPane.addTab("I2P Options", i)
|
||||
tabbedPane.addTab("MuWire", p)
|
||||
tabbedPane.addTab("I2P", i)
|
||||
tabbedPane.addTab("GUI", u)
|
||||
|
||||
JPanel panel = new JPanel()
|
||||
panel.setLayout(new BorderLayout())
|
||||
|
@@ -6,12 +6,19 @@ import griffon.inject.MVCMember
|
||||
import griffon.metadata.ArtifactProviderFor
|
||||
import net.i2p.data.DataHelper
|
||||
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JTable
|
||||
import javax.swing.ListSelectionModel
|
||||
import javax.swing.SwingConstants
|
||||
import javax.swing.table.DefaultTableCellRenderer
|
||||
|
||||
import com.muwire.core.util.DataUtil
|
||||
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Color
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
|
||||
import javax.annotation.Nonnull
|
||||
|
||||
@@ -35,7 +42,7 @@ class SearchTabView {
|
||||
resultsTable = table(id : "results-table", autoCreateRowSorter : true) {
|
||||
tableModel(list: model.results) {
|
||||
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: "Sender", preferredWidth: 170, type: String, read : {row -> row.sender.getHumanReadableName()})
|
||||
closureColumn(header: "Trust", preferredWidth: 50, type: String, read : {row ->
|
||||
@@ -86,7 +93,18 @@ class SearchTabView {
|
||||
resultsTable.setDefaultRenderer(Integer.class,centerRenderer)
|
||||
resultsTable.columnModel.getColumn(4).setCellRenderer(centerRenderer)
|
||||
|
||||
resultsTable.columnModel.getColumn(1).setCellRenderer(new SizeRenderer())
|
||||
|
||||
|
||||
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 = {
|
||||
|
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
|
||||
boolean showMonitor
|
||||
String font
|
||||
boolean clearCancelledDownloads
|
||||
boolean clearFinishedDownloads
|
||||
boolean excludeLocalResult
|
||||
|
||||
UISettings(Properties props) {
|
||||
lnf = props.getProperty("lnf", "system")
|
||||
showMonitor = Boolean.parseBoolean(props.getProperty("showMonitor", "true"))
|
||||
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 {
|
||||
Properties props = new Properties()
|
||||
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)
|
||||
props.setProperty("font", font)
|
||||
|
||||
|
||||
props.store(out, "UI Properties")
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user