Compare commits

..

37 Commits

Author SHA1 Message Date
Zlatin Balevsky
5e329dfa2c Release 0.5.4 2019-10-26 06:42:14 +01:00
Zlatin Balevsky
742f6da870 update notifications 2019-10-26 06:12:54 +01:00
Zlatin Balevsky
7f46347c0f retry failed downloads 2019-10-26 05:33:22 +01:00
Zlatin Balevsky
b308ac2f37 searches by hash 2019-10-26 05:14:04 +01:00
Zlatin Balevsky
9cdabb51d1 count shared files in dashboard 2019-10-25 22:51:26 +01:00
Zlatin Balevsky
45f0736a5e account for hashing errors 2019-10-25 22:51:15 +01:00
Zlatin Balevsky
fe753ff978 add a download details view 2019-10-25 22:36:25 +01:00
Zlatin Balevsky
ac717b5205 center things horizontally 2019-10-25 22:02:04 +01:00
Zlatin Balevsky
6f624e3afc add some stats to main window 2019-10-25 21:51:16 +01:00
Zlatin Balevsky
623d675ed9 Ability to view comments 2019-10-25 18:57:07 +01:00
Zlatin Balevsky
546b71b632 implement adding comments to shared files 2019-10-25 18:32:55 +01:00
Zlatin Balevsky
804113bb1b typo 2019-10-25 17:46:59 +01:00
Zlatin Balevsky
ab9e10f438 add a note about the CLI 2019-10-25 17:43:15 +01:00
Zlatin Balevsky
00520acdf0 implement browse host 2019-10-25 17:30:16 +01:00
Zlatin Balevsky
8c44d196a7 move gui result processing on gui thread 2019-10-25 13:14:38 +01:00
Zlatin Balevsky
9c5fa0a2ce hook up trust to results 2019-10-25 10:36:26 +01:00
Zlatin Balevsky
d7bca05725 implement trust list review window 2019-10-25 10:00:52 +01:00
Zlatin Balevsky
45fcb2209e Trust List actions 2019-10-25 08:48:07 +01:00
Zlatin Balevsky
7bf0373b80 trust and distrust actions 2019-10-25 08:24:07 +01:00
Zlatin Balevsky
5925b42597 wip on trust window 2019-10-25 07:39:01 +01:00
Zlatin Balevsky
13243b05ad center shutdown dialog 2019-10-25 06:14:14 +01:00
Zlatin Balevsky
43987be463 prevent RejectedExecutionExceptions on shutdown 2019-10-25 06:13:20 +01:00
Zlatin Balevsky
fcd3414e02 refresh number of connections automatically 2019-10-25 06:08:41 +01:00
Zlatin Balevsky
70913ea8fb correct startup sequence, add listeners for allFilesLoadedEvent 2019-10-25 06:01:16 +01:00
Zlatin Balevsky
b30e552498 share and unshare a directory 2019-10-24 22:35:29 +01:00
Zlatin Balevsky
bae66de4eb implement share file dialog 2019-10-24 22:03:20 +01:00
Zlatin Balevsky
626e145e25 properly set size of tables 2019-10-24 19:22:39 +01:00
Zlatin Balevsky
bf72c76f13 limit the size of the table based on the terminal size 2019-10-24 19:12:50 +01:00
Zlatin Balevsky
fce8bbfd97 wip on shared files window 2019-10-24 18:34:27 +01:00
Zlatin Balevsky
1cc7925155 uploads window 2019-10-24 17:24:01 +01:00
Zlatin Balevsky
12b51ceb02 add an ETA column 2019-10-24 16:51:11 +01:00
Zlatin Balevsky
62811861a4 working downloads window 2019-10-24 16:36:10 +01:00
Zlatin Balevsky
837aa6974b display search results in new window 2019-10-24 14:39:25 +01:00
Zlatin Balevsky
94e7c42d19 add option to specify I2CP host and port. Show failure message is I2CP connect fails 2019-10-24 08:15:02 +01:00
Zlatin Balevsky
877bf12a93 fixed progress dialog, wip on search view 2019-10-24 07:49:15 +01:00
Zlatin Balevsky
224266b2dd basic initialization of the core 2019-10-23 22:25:54 +01:00
Zlatin Balevsky
8f16614dc3 start a new project for an interactive cli 2019-10-23 19:38:16 +01:00
30 changed files with 2152 additions and 8 deletions

View File

@@ -4,7 +4,7 @@ MuWire is an easy to use file-sharing program which offers anonymity using [I2P
It is inspired by the LimeWire Gnutella client and developped by a former LimeWire developer.
The current stable release - 0.5.2 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
The current stable release - 0.5.3 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
### Building
@@ -21,7 +21,7 @@ If you want to run the unit tests, type
If you want to build binary bundles that do not depend on Java or I2P, see the https://github.com/zlatinb/muwire-pkg project
### Running
### Running the GUI
After you build the application, look inside `gui/build/distributions`. Untar/unzip one of the `shadow` files and then run the jar contained inside by typing `java -jar gui-x.y.z.jar` in a terminal or command prompt.
@@ -29,6 +29,12 @@ If you have an I2P router running on the same machine that is all you need to do
[Default I2CP port]\: `7654`
### Running the CLI
Look inside `cli-lanterna/build/distributions`. Untar/unzip one of the `shadow` files and then run the jar contained inside by typing `java -jar cli-lanterna-x.y.z.jar` in a terminal. The CLI will ask you about the router host and port on startup, no need to edit any files.
The CLI is under active development and doesn't have all the features of the GUI.
### GPG Fingerprint
```

23
cli-lanterna/build.gradle Normal file
View File

@@ -0,0 +1,23 @@
buildscript {
repositories {
jcenter()
mavenLocal()
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
}
}
apply plugin : 'application'
mainClassName = 'com.muwire.clilanterna.CliLanterna'
apply plugin : 'com.github.johnrengelman.shadow'
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
dependencies {
compile project(":core")
compile 'com.googlecode.lanterna:lanterna:3.0.1'
}

View File

@@ -0,0 +1,65 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.gui2.TextBox
import com.googlecode.lanterna.gui2.TextGUI
import com.googlecode.lanterna.gui2.Window
import com.googlecode.lanterna.TerminalSize
import com.googlecode.lanterna.gui2.BasicWindow
import com.googlecode.lanterna.gui2.Button
import com.googlecode.lanterna.gui2.GridLayout
import com.googlecode.lanterna.gui2.GridLayout.Alignment
import com.googlecode.lanterna.gui2.LayoutData
import com.googlecode.lanterna.gui2.Panel
import com.muwire.core.Core
import com.muwire.core.SharedFile
import com.muwire.core.files.UICommentEvent
import com.muwire.core.util.DataUtil
import net.i2p.data.Base64
class AddCommentView extends BasicWindow {
private final TextGUI textGUI
private final Core core
private final TextBox textBox
private final LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER)
AddCommentView(TextGUI textGUI, Core core, SharedFile sharedFile, TerminalSize terminalSize) {
super("Add Comment To "+sharedFile.getFile().getName())
this.textGUI = textGUI
this.core = core
setHints([Window.Hint.CENTERED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(1))
String oldComment = sharedFile.getComment()
if (oldComment == null)
oldComment = ""
else
oldComment = DataUtil.readi18nString(Base64.decode(oldComment))
TerminalSize boxSize = new TerminalSize((terminalSize.getColumns() / 2).toInteger(), (terminalSize.getRows() / 2).toInteger())
textBox = new TextBox(boxSize,oldComment,TextBox.Style.MULTI_LINE)
contentPanel.addComponent(textBox, layoutData)
Panel buttonsPanel = new Panel()
buttonsPanel.setLayoutManager(new GridLayout(2))
contentPanel.addComponent(buttonsPanel, layoutData)
Button saveButton = new Button("Save", {
String newComment = textBox.getText()
newComment = Base64.encode(DataUtil.encodei18nString(newComment))
String encodedOldComment = sharedFile.getComment()
sharedFile.setComment(newComment)
core.eventBus.publish(new UICommentEvent(sharedFile : sharedFile, oldComment : encodedOldComment))
close()
})
Button cancelButton = new Button("Cancel", {close()})
buttonsPanel.addComponent(saveButton, layoutData)
buttonsPanel.addComponent(cancelButton, layoutData)
setComponent(contentPanel)
}
}

View File

@@ -0,0 +1,75 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.gui2.Label
import com.googlecode.lanterna.gui2.TextGUIThread
import com.googlecode.lanterna.gui2.table.TableModel
import com.muwire.core.Core
import com.muwire.core.Persona
import com.muwire.core.search.BrowseStatus
import com.muwire.core.search.BrowseStatusEvent
import com.muwire.core.search.UIBrowseEvent
import com.muwire.core.search.UIResultEvent
import net.i2p.data.Base64
import net.i2p.data.DataHelper
class BrowseModel {
private final Persona persona
private final Core core
private final TextGUIThread guiThread
private final TableModel model = new TableModel("Name","Size","Hash","Comment")
private Map<String, UIResultEvent> rootToResult = new HashMap<>()
private int totalResults
private Label status
private Label percentage
BrowseModel(Persona persona, Core core, TextGUIThread guiThread) {
this.persona = persona
this.core = core
this.guiThread = guiThread
core.eventBus.register(BrowseStatusEvent.class, this)
core.eventBus.register(UIResultEvent.class, this)
core.eventBus.publish(new UIBrowseEvent(host : persona))
}
void unregister() {
core.eventBus.unregister(BrowseStatusEvent.class, this)
core.eventBus.unregister(UIResultEvent.class, this)
}
void onBrowseStatusEvent(BrowseStatusEvent e) {
guiThread.invokeLater {
status.setText(e.status.toString())
if (e.status == BrowseStatus.FETCHING)
totalResults = e.totalResults
}
}
void onUIResultEvent(UIResultEvent e) {
guiThread.invokeLater {
String size = DataHelper.formatSize2Decimal(e.size, false) + "B"
String infoHash = Base64.encode(e.infohash.getRoot())
String comment = String.valueOf(e.comment != null)
model.addRow(e.name, size, infoHash, comment)
rootToResult.put(infoHash, e)
String percentageString = ""
if (totalResults != 0) {
double percentage = Math.round( (model.getRowCount() * 100 / totalResults).toDouble() )
percentageString = String.valueOf(percentage)+"%"
}
percentage.setText(percentageString)
}
}
void setStatusLabel(Label status) {
this.status = status
}
void setPercentageLabel(Label percentage) {
this.percentage = percentage
}
}

View File

@@ -0,0 +1,111 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.TerminalSize
import com.googlecode.lanterna.gui2.BasicWindow
import com.googlecode.lanterna.gui2.Button
import com.googlecode.lanterna.gui2.GridLayout
import com.googlecode.lanterna.gui2.GridLayout.Alignment
import com.googlecode.lanterna.gui2.Label
import com.googlecode.lanterna.gui2.LayoutData
import com.googlecode.lanterna.gui2.Panel
import com.googlecode.lanterna.gui2.TextGUI
import com.googlecode.lanterna.gui2.Window
import com.googlecode.lanterna.gui2.dialogs.MessageDialog
import com.googlecode.lanterna.gui2.dialogs.MessageDialogButton
import com.googlecode.lanterna.gui2.table.Table
import com.muwire.core.Core
import com.muwire.core.download.UIDownloadEvent
import com.muwire.core.search.UIResultEvent
class BrowseView extends BasicWindow {
private final BrowseModel model
private final TextGUI textGUI
private final Core core
private final Table table
private final TerminalSize terminalSize
private final LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER, true, false)
BrowseView(BrowseModel model, TextGUI textGUI, Core core, TerminalSize terminalSize) {
super("Browse "+model.persona.getHumanReadableName())
this.model = model
this.textGUI = textGUI
this.core = core
this.terminalSize = terminalSize
setHints([Window.Hint.EXPANDED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(1))
Label statusLabel = new Label("")
Label percentageLabel = new Label("")
model.setStatusLabel(statusLabel)
model.setPercentageLabel(percentageLabel)
Panel topPanel = new Panel()
topPanel.setLayoutManager(new GridLayout(2))
topPanel.addComponent(statusLabel, layoutData)
topPanel.addComponent(percentageLabel, layoutData)
contentPanel.addComponent(topPanel, layoutData)
table = new Table("Name","Size","Hash","Comment")
table.with {
setCellSelection(false)
setTableModel(model.model)
setVisibleRows(terminalSize.getRows())
setSelectAction({rowSelected()})
}
contentPanel.addComponent(table, layoutData)
Button closeButton = new Button("Close",{
model.unregister()
close()
})
contentPanel.addComponent(closeButton, layoutData)
setComponent(contentPanel)
}
private void rowSelected() {
int selectedRow = table.getSelectedRow()
def row = model.model.getRow(selectedRow)
String infoHash = row[2]
boolean comment = Boolean.parseBoolean(row[3])
if (comment) {
Window prompt = new BasicWindow("Download Or View Comment")
prompt.setHints([Window.Hint.CENTERED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(3))
Button downloadButton = new Button("Download", {download(infoHash)})
Button viewButton = new Button("View Comment", {viewComment(infoHash)})
Button closeButton = new Button("Cancel", {prompt.close()})
contentPanel.with {
addComponent(downloadButton, layoutData)
addComponent(viewButton, layoutData)
addComponent(closeButton, layoutData)
}
prompt.setComponent(contentPanel)
downloadButton.takeFocus()
textGUI.addWindowAndWait(prompt)
} else {
download(infoHash)
}
}
private void download(String infoHash) {
UIResultEvent result = model.rootToResult[infoHash]
def file = new File(core.muOptions.downloadLocation, result.name)
core.eventBus.publish(new UIDownloadEvent(result : [result], sources : result.sources,
target : file, sequential : false))
MessageDialog.showMessageDialog(textGUI, "Download started", "Started download of "+result.name, MessageDialogButton.OK)
}
private void viewComment(String infoHash) {
UIResultEvent result = model.rootToResult[infoHash]
ViewCommentView view = new ViewCommentView(result, terminalSize)
textGUI.addWindowAndWait(view)
}
}

View File

@@ -0,0 +1,182 @@
package com.muwire.clilanterna
import java.nio.charset.StandardCharsets
import java.util.concurrent.CountDownLatch
import java.util.logging.Level
import java.util.logging.LogManager
import com.googlecode.lanterna.gui2.BasicWindow
import com.googlecode.lanterna.gui2.Border
import com.googlecode.lanterna.gui2.BorderLayout
import com.googlecode.lanterna.gui2.Borders
import com.googlecode.lanterna.gui2.Button
import com.googlecode.lanterna.gui2.GridLayout
import com.googlecode.lanterna.gui2.Label
import com.googlecode.lanterna.gui2.MultiWindowTextGUI
import com.googlecode.lanterna.gui2.Panel
import com.googlecode.lanterna.gui2.SeparateTextGUIThread
import com.googlecode.lanterna.gui2.Window
import com.googlecode.lanterna.gui2.WindowBasedTextGUI
import com.googlecode.lanterna.gui2.GridLayout.Alignment
import com.googlecode.lanterna.gui2.dialogs.MessageDialog
import com.googlecode.lanterna.gui2.dialogs.MessageDialogButton
import com.googlecode.lanterna.gui2.dialogs.TextInputDialog
import com.googlecode.lanterna.gui2.dialogs.TextInputDialogBuilder
import com.googlecode.lanterna.gui2.dialogs.WaitingDialog
import com.googlecode.lanterna.screen.Screen
import com.googlecode.lanterna.terminal.DefaultTerminalFactory
import com.googlecode.lanterna.terminal.Terminal
import com.muwire.core.Core
import com.muwire.core.MuWireSettings
import com.muwire.core.UILoadedEvent
import com.muwire.core.files.AllFilesLoadedEvent
class CliLanterna {
private static final String MW_VERSION = "0.5.4"
private static volatile Core core
private static WindowBasedTextGUI textGUI
public static void main(String[] args) {
if (System.getProperty("java.util.logging.config.file") == null) {
def names = LogManager.getLogManager().getLoggerNames()
while(names.hasMoreElements()) {
def name = names.nextElement()
LogManager.getLogManager().getLogger(name).setLevel(Level.SEVERE)
}
}
def home = System.getProperty("user.home") + File.separator + ".MuWire"
home = new File(home)
if (!home.exists())
home.mkdirs()
def propsFile = new File(home,"MuWire.properties")
DefaultTerminalFactory terminalFactory = new DefaultTerminalFactory()
Screen screen = terminalFactory.createScreen()
textGUI = new MultiWindowTextGUI( new SeparateTextGUIThread.Factory(), screen)
textGUI.getGUIThread().start()
screen.startScreen()
def props
if (!propsFile.exists()) {
String nickname = TextInputDialog.showDialog(textGUI, "Select a nickname", "", "")
String defaultDownloadLocation = System.getProperty("user.home")+File.separator+"Downloads"
String downloadLocation = TextInputDialog.showDialog(textGUI, "Select download location", "", defaultDownloadLocation)
String defaultIncompletesLocation = System.getProperty("user.home")+File.separator+".MuWire"+File.separator+"incompletes"
String incompletesLocation = TextInputDialog.showDialog(textGUI, "Select incompletes location", "", defaultIncompletesLocation)
File downloadLocationFile = new File(downloadLocation)
if (!downloadLocationFile.exists())
downloadLocationFile.mkdirs()
File incompletesLocationFile = new File(incompletesLocation)
if (!incompletesLocationFile.exists())
incompletesLocationFile.mkdirs()
props = new MuWireSettings()
props.setNickname(nickname)
props.setDownloadLocation(downloadLocationFile)
props.incompleteLocation = incompletesLocationFile
propsFile.withOutputStream {
props.write(it)
}
} else {
props = new Properties()
propsFile.withInputStream {
props.load(it)
}
props = new MuWireSettings(props)
}
props.updateType = "cli-lanterna"
def i2pPropsFile = new File(home, "i2p.properties")
if (!i2pPropsFile.exists()) {
String i2pHost = TextInputDialog.showDialog(textGUI, "I2P router host", "Specifiy the host I2P router is on", "127.0.0.1")
int i2pPort = TextInputDialog.showNumberDialog(textGUI, "I2CP port", "Specify the I2CP port", "7654").toInteger()
Properties i2pProps = new Properties()
i2pProps["i2cp.tcp.host"] = i2pHost
i2pProps["i2cp.tcp.port"] = String.valueOf(i2pPort)
i2pPropsFile.withOutputStream { i2pProps.store(it, "") }
}
Window window = new BasicWindow("MuWire "+ MW_VERSION)
window.setHints([Window.Hint.CENTERED])
Panel contentPanel = new Panel()
contentPanel.withBorder(Borders.doubleLine())
BorderLayout layout = new BorderLayout()
contentPanel.setLayoutManager(layout)
Panel welcomeNamePanel = new Panel()
contentPanel.addComponent(welcomeNamePanel, BorderLayout.Location.CENTER)
welcomeNamePanel.setLayoutManager(new GridLayout(1))
Label welcomeLabel = new Label("Welcome to MuWire "+ props.nickname)
welcomeNamePanel.addComponent(welcomeLabel, GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER))
Panel connectButtonPanel = new Panel()
contentPanel.addComponent(connectButtonPanel, BorderLayout.Location.BOTTOM)
connectButtonPanel.setLayoutManager(new GridLayout(1))
Button connectButton = new Button("Connect", {
WaitingDialog waiting = new WaitingDialog("Connecting", "Please wait")
waiting.showDialog(textGUI, false)
CountDownLatch latch = new CountDownLatch(1)
Thread connector = new Thread({
try {
core = new Core(props, home, MW_VERSION)
} finally {
latch.countDown()
}
})
connector.start()
while(latch.getCount() > 0) {
textGUI.updateScreen()
Thread.sleep(10)
}
waiting.close()
window.close()
} as Runnable)
welcomeNamePanel.addComponent(connectButton, GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER))
window.setComponent(contentPanel)
textGUI.addWindowAndWait(window)
if (core == null) {
MessageDialog.showMessageDialog(textGUI, "Failed", "MuWire failed to load", MessageDialogButton.Close)
System.exit(1)
}
window = new MainWindowView("MuWire "+MW_VERSION, core, textGUI, screen)
core.startServices()
core.eventBus.publish(new UILoadedEvent())
textGUI.addWindowAndWait(window)
CountDownLatch latch = new CountDownLatch(1)
Thread stopper = new Thread({
core.shutdown()
latch.countDown()
} as Runnable)
WaitingDialog waitingForShutdown = new WaitingDialog("MuWire is shutting down","Please wait")
waitingForShutdown.setHints([Window.Hint.CENTERED])
waitingForShutdown.showDialog(textGUI, false)
stopper.start()
while(latch.getCount() > 0) {
textGUI.updateScreen()
Thread.sleep(10)
}
waitingForShutdown.close()
screen.stopScreen()
System.exit(0)
}
}

View File

@@ -0,0 +1,67 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.gui2.BasicWindow
import com.googlecode.lanterna.gui2.Button
import com.googlecode.lanterna.gui2.GridLayout
import com.googlecode.lanterna.gui2.GridLayout.Alignment
import com.googlecode.lanterna.gui2.Label
import com.googlecode.lanterna.gui2.LayoutData
import com.googlecode.lanterna.gui2.Panel
import com.googlecode.lanterna.gui2.TextGUIThread
import com.googlecode.lanterna.gui2.Window
import com.muwire.core.download.Downloader
class DownloadDetailsView extends BasicWindow {
private final Downloader downloader
private final LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER)
private Label knownSources, activeSources, donePieces
DownloadDetailsView(Downloader downloader) {
super("Download details for "+downloader.file.getName())
this.downloader = downloader
setHints([Window.Hint.CENTERED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(2))
knownSources = new Label("0")
activeSources = new Label("0")
donePieces = new Label("0")
refresh()
Button refreshButton = new Button("Refresh",{refresh()})
Button closeButton = new Button("Close", {close()})
contentPanel.with {
addComponent(new Label("Target Location"), layoutData)
addComponent(new Label(downloader.file.getAbsolutePath()), layoutData)
addComponent(new Label("Piece Size"), layoutData)
addComponent(new Label(String.valueOf(downloader.pieceSize)), layoutData)
addComponent(new Label("Total Pieces"), layoutData)
addComponent(new Label(String.valueOf(downloader.nPieces)), layoutData)
addComponent(new Label("Done Pieces"), layoutData)
addComponent(donePieces, layoutData)
addComponent(new Label("Known Sources"), layoutData)
addComponent(knownSources, layoutData)
addComponent(new Label("Active Sources"), layoutData)
addComponent(activeSources, layoutData)
addComponent(refreshButton, layoutData)
addComponent(closeButton, layoutData)
}
setComponent(contentPanel)
}
private void refresh() {
int done = downloader.donePieces()
int known = downloader.activeWorkers.size()
int active = downloader.activeWorkers()
knownSources.setText(String.valueOf(known))
activeSources.setText(String.valueOf(active))
donePieces.setText(String.valueOf(done))
}
}

View File

@@ -0,0 +1,15 @@
package com.muwire.clilanterna
import com.muwire.core.download.Downloader
class DownloaderWrapper {
final Downloader downloader
DownloaderWrapper(Downloader downloader) {
this.downloader = downloader
}
@Override
public String toString() {
downloader.file.getName()
}
}

View File

@@ -0,0 +1,90 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.gui2.TextGUIThread
import com.googlecode.lanterna.gui2.table.TableModel
import com.muwire.core.Core
import com.muwire.core.download.DownloadStartedEvent
import com.muwire.core.download.Downloader
import com.muwire.core.files.FileDownloadedEvent
import net.i2p.data.DataHelper
class DownloadsModel {
private final TextGUIThread guiThread
private final Core core
private final List<Downloader> downloaders = new ArrayList<>()
private final TableModel model = new TableModel("Name", "Status", "Progress", "Speed", "ETA")
private long lastRetryTime
DownloadsModel(TextGUIThread guiThread, Core core) {
this.guiThread = guiThread
this.core = core
core.eventBus.register(DownloadStartedEvent.class, this)
Timer timer = new Timer(true)
Runnable guiRunnable = {
refreshModel()
resumeDownloads()
}
timer.schedule({
if (core.shutdown.get())
return
guiThread.invokeLater(guiRunnable)
} as TimerTask, 1000,1000)
}
void onDownloadStartedEvent(DownloadStartedEvent e) {
guiThread.invokeLater({
downloaders.add(e.downloader)
refreshModel()
})
}
private void refreshModel() {
int rowCount = model.getRowCount()
rowCount.times { model.removeRow(0) }
downloaders.each {
String status = it.getCurrentState().toString()
int speedInt = it.speed()
String speed = DataHelper.formatSize2Decimal(speedInt, false) + "B/sec"
int pieces = it.nPieces
int done = it.donePieces()
int percent = -1
if (pieces != 0)
percent = (done * 100 / pieces)
String totalSize = DataHelper.formatSize2Decimal(it.length, false) + "B"
String progress = (String.format("%2d", percent) + "% of ${totalSize}".toString())
String ETA
if (speedInt == 0)
ETA = "Unknown"
else {
long remaining = (pieces - done) * it.pieceSize / speedInt
ETA = DataHelper.formatDuration(remaining * 1000)
}
model.addRow([new DownloaderWrapper(it), status, progress, speed, ETA])
}
}
private void resumeDownloads() {
int retryInterval = core.muOptions.downloadRetryInterval
if (retryInterval == 0)
return
retryInterval *= 1000
long now = System.currentTimeMillis()
if (now - lastRetryTime > retryInterval) {
lastRetryTime = now
downloaders.each {
def state = it.getCurrentState()
if (state == Downloader.DownloadState.FAILED || state == Downloader.DownloadState.DOWNLOADING)
it.resume()
}
}
}
}

View File

@@ -0,0 +1,78 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.TerminalSize
import com.googlecode.lanterna.gui2.BasicWindow
import com.googlecode.lanterna.gui2.Button
import com.googlecode.lanterna.gui2.GridLayout
import com.googlecode.lanterna.gui2.GridLayout.Alignment
import com.googlecode.lanterna.gui2.LayoutData
import com.googlecode.lanterna.gui2.Panel
import com.googlecode.lanterna.gui2.TextGUI
import com.googlecode.lanterna.gui2.Window
import com.googlecode.lanterna.gui2.dialogs.MessageDialog
import com.googlecode.lanterna.gui2.dialogs.MessageDialogButton
import com.googlecode.lanterna.gui2.table.Table
import com.muwire.core.Core
import com.muwire.core.download.Downloader
import com.muwire.core.download.UIDownloadCancelledEvent
class DownloadsView extends BasicWindow {
private final Core core
private final DownloadsModel model
private final TextGUI textGUI
private final Table table
DownloadsView(Core core, DownloadsModel model, TextGUI textGUI, TerminalSize terminalSize) {
this.core = core
this.model = model
this.textGUI = textGUI
setHints([Window.Hint.EXPANDED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(1))
table = new Table("Name","Status","Progress","Speed","ETA")
table.setCellSelection(false)
table.setSelectAction({rowSelected()})
table.setTableModel(model.model)
table.setVisibleRows(terminalSize.getRows())
contentPanel.addComponent(table, GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER,true,false))
Button closeButton = new Button("Close",{close()})
contentPanel.addComponent(closeButton, GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER,true,false))
setComponent(contentPanel)
closeButton.takeFocus()
}
private void rowSelected() {
int selectedRow = table.getSelectedRow()
def row = model.model.getRow(selectedRow)
Downloader downloader = row[0].downloader
Window prompt = new BasicWindow("Kill Download?")
prompt.setHints([Window.Hint.CENTERED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(3))
LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER)
Button killDownload = new Button("Kill Download", {
downloader.cancel()
core.eventBus.publish(new UIDownloadCancelledEvent(downloader : downloader))
MessageDialog.showMessageDialog(textGUI, "Download Killed", downloader.file.getName()+ " has been killed", MessageDialogButton.OK)
})
Button viewDetails = new Button("View Details", {
textGUI.addWindowAndWait(new DownloadDetailsView(downloader))
})
Button close = new Button("Close", {
prompt.close()
})
contentPanel.addComponent(killDownload,layoutData)
contentPanel.addComponent(viewDetails, layoutData)
contentPanel.addComponent(close, layoutData)
prompt.setComponent(contentPanel)
close.takeFocus()
textGUI.addWindowAndWait(prompt)
}
}

View File

@@ -0,0 +1,77 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.gui2.TextGUIThread
import com.googlecode.lanterna.gui2.table.TableModel
import com.muwire.core.Core
import com.muwire.core.SharedFile
import com.muwire.core.files.AllFilesLoadedEvent
import com.muwire.core.files.FileHashedEvent
import com.muwire.core.files.FileLoadedEvent
import com.muwire.core.files.FileSharedEvent
import com.muwire.core.files.FileUnsharedEvent
import com.muwire.core.trust.TrustSubscriptionEvent
import net.i2p.data.DataHelper
class FilesModel {
private final TextGUIThread guiThread
private final Core core
private final List<SharedFile> sharedFiles = new ArrayList<>()
private final TableModel model = new TableModel("Name","Size","Comment")
FilesModel(TextGUIThread guiThread, Core core) {
this.guiThread = guiThread
this.core = core
core.eventBus.register(FileLoadedEvent.class, this)
core.eventBus.register(FileUnsharedEvent.class, this)
core.eventBus.register(FileHashedEvent.class, this)
core.eventBus.register(AllFilesLoadedEvent.class, this)
Runnable refreshModel = {refreshModel()}
Timer timer = new Timer(true)
timer.schedule({
guiThread.invokeLater(refreshModel)
} as TimerTask, 1000,1000)
}
void onAllFilesLoadedEvent(AllFilesLoadedEvent e) {
def eventBus = core.eventBus
guiThread.invokeLater {
core.muOptions.watchedDirectories.each {
eventBus.publish(new FileSharedEvent(file : new File(it)))
}
}
}
void onFileLoadedEvent(FileLoadedEvent e) {
guiThread.invokeLater {
sharedFiles.add(e.loadedFile)
}
}
void onFileHashedEvent(FileHashedEvent e) {
guiThread.invokeLater {
if (e.sharedFile != null)
sharedFiles.add(e.sharedFile)
}
}
void onFileUnsharedEvent(FileUnsharedEvent e) {
guiThread.invokeLater {
sharedFiles.remove(e.unsharedFile)
}
}
private void refreshModel() {
int rowCount = model.getRowCount()
rowCount.times { model.removeRow(0) }
sharedFiles.each {
long size = it.getCachedLength()
boolean comment = it.comment != null
model.addRow(new SharedFileWrapper(it), DataHelper.formatSize2(size, false)+"B", comment)
}
}
}

View File

@@ -0,0 +1,131 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.TerminalSize
import com.googlecode.lanterna.gui2.BasicWindow
import com.googlecode.lanterna.gui2.Button
import com.googlecode.lanterna.gui2.GridLayout
import com.googlecode.lanterna.gui2.GridLayout.Alignment
import com.googlecode.lanterna.gui2.LayoutData
import com.googlecode.lanterna.gui2.Panel
import com.googlecode.lanterna.gui2.TextGUI
import com.googlecode.lanterna.gui2.Window
import com.googlecode.lanterna.gui2.dialogs.FileDialog
import com.googlecode.lanterna.gui2.dialogs.MessageDialog
import com.googlecode.lanterna.gui2.dialogs.MessageDialogButton
import com.googlecode.lanterna.gui2.dialogs.TextInputDialog
import com.googlecode.lanterna.gui2.table.Table
import com.muwire.core.Core
import com.muwire.core.SharedFile
import com.muwire.core.files.DirectoryUnsharedEvent
import com.muwire.core.files.FileSharedEvent
import com.muwire.core.files.FileUnsharedEvent
import com.muwire.core.files.UIPersistFilesEvent
class FilesView extends BasicWindow {
private final FilesModel model
private final TextGUI textGUI
private final Core core
private final Table table
private final TerminalSize terminalSize
FilesView(FilesModel model, TextGUI textGUI, Core core, TerminalSize terminalSize) {
super("Shared Files")
this.model = model
this.core = core
this.textGUI = textGUI
this.terminalSize = terminalSize
setHints([Window.Hint.EXPANDED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(1))
LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER, true, false)
table = new Table("Name","Size","Comment")
table.setCellSelection(false)
table.setTableModel(model.model)
table.setSelectAction({rowSelected()})
table.setVisibleRows(terminalSize.getRows())
contentPanel.addComponent(table, layoutData)
Panel buttonsPanel = new Panel()
buttonsPanel.setLayoutManager(new GridLayout(4))
Button shareFile = new Button("Share File", {shareFile()})
Button shareDirectory = new Button("Share Directory", {shareDirectory()})
Button unshareDirectory = new Button("Unshare Directory",{unshareDirectory()})
Button close = new Button("Close", {close()})
buttonsPanel.with {
addComponent(shareFile, layoutData)
addComponent(shareDirectory, layoutData)
addComponent(unshareDirectory, layoutData)
addComponent(close, layoutData)
}
contentPanel.addComponent(buttonsPanel, layoutData)
setComponent(contentPanel)
close.takeFocus()
}
private void rowSelected() {
int selectedRow = table.getSelectedRow()
def row = model.model.getRow(selectedRow)
SharedFile sf = row[0].sharedFile
Window prompt = new BasicWindow("Unshare or add comment to "+sf.getFile().getName()+" ?")
prompt.setHints([Window.Hint.CENTERED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(3))
Button unshareButton = new Button("Unshare", {
core.eventBus.publish(new FileUnsharedEvent(unsharedFile : sf))
core.eventBus.publish(new UIPersistFilesEvent())
MessageDialog.showMessageDialog(textGUI, "File Unshared", "Unshared "+sf.getFile().getName(), MessageDialogButton.OK)
} )
Button addCommentButton = new Button("Add Comment", {
AddCommentView view = new AddCommentView(textGUI, core, sf, terminalSize)
textGUI.addWindowAndWait(view)
})
Button closeButton = new Button("Close", {prompt.close()})
LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER)
contentPanel.addComponent(unshareButton, layoutData)
contentPanel.addComponent(addCommentButton, layoutData)
contentPanel.addComponent(closeButton, layoutData)
prompt.setComponent(contentPanel)
textGUI.addWindowAndWait(prompt)
}
private void shareFile() {
TerminalSize terminalSize = new TerminalSize(terminalSize.getColumns() - 10, terminalSize.getRows() - 10)
FileDialog fileDialog = new FileDialog("Share File", "Select a file to share", "Share", terminalSize, false, null)
File f = fileDialog.showDialog(textGUI)
f = f.getCanonicalFile()
core.eventBus.publish(new FileSharedEvent(file : f))
MessageDialog.showMessageDialog(textGUI, "File Shared", f.getName()+" has been shared", MessageDialogButton.OK)
}
private void shareDirectory() {
String directoryName = TextInputDialog.showDialog(textGUI, "Share a directory", "Enter the directory to share", "")
if (directoryName == null)
return
File directory = new File(directoryName)
directory = directory.getCanonicalFile()
core.eventBus.publish(new FileSharedEvent(file : directory))
MessageDialog.showMessageDialog(textGUI, "Directory Shared", directory.getName()+" has been shared", MessageDialogButton.OK)
}
private void unshareDirectory() {
String directoryName = TextInputDialog.showDialog(textGUI, "Unshare a directory", "Enter the directory to unshare", "")
if (directoryName == null)
return
File directory = new File(directoryName)
directory = directory.getCanonicalFile()
core.eventBus.publish(new DirectoryUnsharedEvent(directory : directory))
MessageDialog.showMessageDialog(textGUI, "Directory Unshared", directory.getName()+" has been unshared", MessageDialogButton.OK)
}
}

View File

@@ -0,0 +1,272 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.TerminalPosition
import com.googlecode.lanterna.TerminalSize
import com.googlecode.lanterna.gui2.BasicWindow
import com.googlecode.lanterna.gui2.BorderLayout
import com.googlecode.lanterna.gui2.Borders
import com.googlecode.lanterna.gui2.Button
import com.googlecode.lanterna.gui2.GridLayout
import com.googlecode.lanterna.gui2.GridLayout.Alignment
import com.googlecode.lanterna.gui2.dialogs.MessageDialog
import com.googlecode.lanterna.gui2.dialogs.MessageDialogButton
import com.googlecode.lanterna.gui2.Label
import com.googlecode.lanterna.gui2.LayoutData
import com.googlecode.lanterna.gui2.Panel
import com.googlecode.lanterna.gui2.Panels
import com.googlecode.lanterna.gui2.TextGUI
import com.googlecode.lanterna.gui2.Window
import com.googlecode.lanterna.screen.Screen
import com.googlecode.lanterna.gui2.TextBox
import com.muwire.core.Core
import com.muwire.core.DownloadedFile
import com.muwire.core.connection.ConnectionEvent
import com.muwire.core.files.FileDownloadedEvent
import com.muwire.core.files.FileHashedEvent
import com.muwire.core.files.FileLoadedEvent
import com.muwire.core.files.FileUnsharedEvent
import com.muwire.core.hostcache.HostDiscoveredEvent
import com.muwire.core.update.UpdateAvailableEvent
import com.muwire.core.update.UpdateDownloadedEvent
import net.i2p.data.Base64
class MainWindowView extends BasicWindow {
private final Core core
private final TextGUI textGUI
private final Screen screen
private final TextBox searchTextBox
private final DownloadsModel downloadsModel
private final UploadsModel uploadsModel
private final FilesModel filesModel
private final TrustModel trustModel
private final Label connectionCount, incoming, outgoing
private final Label known, failing, hopeless
private final Label sharedFiles
private final Label updateStatus
public MainWindowView(String title, Core core, TextGUI textGUI, Screen screen) {
super(title);
this.core = core
this.textGUI = textGUI
this.screen = screen
downloadsModel = new DownloadsModel(textGUI.getGUIThread(),core)
uploadsModel = new UploadsModel(textGUI.getGUIThread(), core)
filesModel = new FilesModel(textGUI.getGUIThread(),core)
trustModel = new TrustModel(textGUI.getGUIThread(), core)
setHints([Window.Hint.EXPANDED])
Panel contentPanel = new Panel()
setComponent(contentPanel)
BorderLayout borderLayout = new BorderLayout()
contentPanel.setLayoutManager(borderLayout)
Panel buttonsPanel = new Panel()
contentPanel.addComponent(buttonsPanel, BorderLayout.Location.TOP)
GridLayout gridLayout = new GridLayout(7)
buttonsPanel.setLayoutManager(gridLayout)
searchTextBox = new TextBox(new TerminalSize(40, 1))
Button searchButton = new Button("Search", { search() })
Button downloadsButton = new Button("Downloads", {download()})
Button uploadsButton = new Button("Uploads", {upload()})
Button filesButton = new Button("Files", { files() })
Button trustButton = new Button("Trust", {trust()})
Button quitButton = new Button("Quit", {close()})
LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER)
buttonsPanel.with {
addComponent(searchTextBox, layoutData)
addComponent(searchButton, layoutData)
addComponent(downloadsButton, layoutData)
addComponent(uploadsButton, layoutData)
addComponent(filesButton, layoutData)
addComponent(trustButton, layoutData)
addComponent(quitButton, layoutData)
}
Panel bottomPanel = new Panel()
contentPanel.addComponent(bottomPanel, BorderLayout.Location.BOTTOM)
BorderLayout bottomLayout = new BorderLayout()
bottomPanel.setLayoutManager(bottomLayout)
Label persona = new Label(core.me.getHumanReadableName())
bottomPanel.addComponent(persona, BorderLayout.Location.LEFT)
Panel connectionsPanel = new Panel()
connectionsPanel.setLayoutManager(new GridLayout(2))
Label connections = new Label("Connections:")
connectionsPanel.addComponent(connections, GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER))
connectionCount = new Label("0")
connectionsPanel.addComponent(connectionCount, GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER))
bottomPanel.addComponent(connectionsPanel, BorderLayout.Location.RIGHT)
Panel centralPanel = new Panel()
centralPanel.setLayoutManager(new GridLayout(1))
contentPanel.addComponent(centralPanel, BorderLayout.Location.CENTER)
Panel statusPanel = new Panel()
statusPanel.setLayoutManager(new GridLayout(2))
statusPanel.withBorder(Borders.doubleLine("Stats"))
centralPanel.addComponent(statusPanel, GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER, true, true))
incoming = new Label("0")
outgoing = new Label("0")
known = new Label("0")
failing = new Label("0")
hopeless = new Label("0")
sharedFiles = new Label("0")
updateStatus = new Label("Unknown")
statusPanel.with {
addComponent(new Label("Incoming Connections: "), layoutData)
addComponent(incoming, layoutData)
addComponent(new Label("Outgoing Connections: "), layoutData)
addComponent(outgoing, layoutData)
addComponent(new Label("Known Hosts: "), layoutData)
addComponent(known, layoutData)
addComponent(new Label("Failing Hosts: "), layoutData)
addComponent(failing, layoutData)
addComponent(new Label("Hopeless Hosts: "), layoutData)
addComponent(hopeless, layoutData)
addComponent(new Label("Shared Files: "), layoutData)
addComponent(sharedFiles, layoutData)
addComponent(new Label("Update Status: "), layoutData)
addComponent(updateStatus, layoutData)
}
refreshStats()
searchButton.takeFocus()
core.eventBus.register(ConnectionEvent.class, this)
core.eventBus.register(HostDiscoveredEvent.class, this)
core.eventBus.register(FileLoadedEvent.class, this)
core.eventBus.register(FileHashedEvent.class, this)
core.eventBus.register(FileUnsharedEvent.class, this)
core.eventBus.register(FileDownloadedEvent.class, this)
core.eventBus.register(UpdateAvailableEvent.class, this)
core.eventBus.register(UpdateDownloadedEvent.class, this)
}
void onConnectionEvent(ConnectionEvent e) {
textGUI.getGUIThread().invokeLater {
connectionCount.setText(String.valueOf(core.connectionManager.connections.size()))
refreshStats()
}
}
void onHostDiscoveredEvent(HostDiscoveredEvent e) {
textGUI.getGUIThread().invokeLater {
refreshStats()
}
}
void onFileLoadedEvent(FileLoadedEvent e) {
textGUI.getGUIThread().invokeLater {
refreshStats()
}
}
void onFileHashedEvent(FileHashedEvent e) {
textGUI.getGUIThread().invokeLater {
refreshStats()
}
}
void onFileUnsharedEvent(FileUnsharedEvent e) {
textGUI.getGUIThread().invokeLater {
refreshStats()
}
}
void onFileDownloadedEvent(FileDownloadedEvent e) {
textGUI.getGUIThread().invokeLater {
refreshStats()
}
}
void onUpdateAvailableEvent(UpdateAvailableEvent e) {
textGUI.getGUIThread().invokeLater {
String label = "$e.version is available with hash $e.infoHash"
updateStatus.setText(label)
String message = "Version $e.version is available from $e.signer, search for $e.infoHash"
MessageDialog.showMessageDialog(textGUI, "Update Available", message, MessageDialogButton.OK)
}
}
void onUpdateDownloadedEvent(UpdateDownloadedEvent e) {
textGUI.getGUIThread().invokeLater {
String label = "$e.version downloaded"
updateStatus.setText(label)
String message = "Version $e.version from $e.signer has been downloaded. You can update now."
MessageDialog.showMessageDialog(textGUI, "Update Available", message, MessageDialogButton.OK)
}
}
private TerminalSize sizeForTables() {
TerminalSize full = screen.getTerminalSize()
return new TerminalSize(full.getColumns(), full.getRows() - 10)
}
private void search() {
String query = searchTextBox.getText()
query = query.trim()
if (query.length() == 0)
return
if (query.length() > 128)
query = query.substring(0, 128)
SearchModel model = new SearchModel(query, core, textGUI.getGUIThread())
textGUI.addWindowAndWait(new SearchView(model,core, textGUI, sizeForTables()))
}
private void download() {
textGUI.addWindowAndWait(new DownloadsView(core, downloadsModel, textGUI, sizeForTables()))
}
private void upload() {
textGUI.addWindowAndWait(new UploadsView(uploadsModel, sizeForTables()))
}
private void files() {
textGUI.addWindowAndWait(new FilesView(filesModel, textGUI, core, sizeForTables()))
}
private void trust() {
textGUI.addWindowAndWait(new TrustView(trustModel, textGUI, core, sizeForTables()))
}
private void refreshStats() {
int inCon = 0
int outCon = 0
core.connectionManager.getConnections().each {
if (it.isIncoming())
inCon++
else
outCon++
}
int knownHosts = core.hostCache.hosts.size()
int failingHosts = core.hostCache.countFailingHosts()
int hopelessHosts = core.hostCache.countHopelessHosts()
int shared = core.fileManager.fileToSharedFile.size()
incoming.setText(String.valueOf(inCon))
outgoing.setText(String.valueOf(outCon))
known.setText(String.valueOf(knownHosts))
failing.setText(String.valueOf(failingHosts))
hopeless.setText(String.valueOf(hopelessHosts))
sharedFiles.setText(String.valueOf(shared))
}
}

View File

@@ -0,0 +1,15 @@
package com.muwire.clilanterna
import com.muwire.core.Persona
class PersonaWrapper {
private final Persona persona
PersonaWrapper(Persona persona) {
this.persona = persona
}
@Override
public String toString() {
persona.getHumanReadableName()
}
}

View File

@@ -0,0 +1,28 @@
package com.muwire.clilanterna
import com.muwire.core.search.UIResultBatchEvent
import com.muwire.core.search.UIResultEvent
import net.i2p.data.Base64
import net.i2p.data.DataHelper
import com.googlecode.lanterna.gui2.table.TableModel
class ResultsModel {
private final UIResultBatchEvent results
final TableModel model
final Map<String, UIResultEvent> rootToResult = new HashMap<>()
ResultsModel(UIResultBatchEvent results) {
this.results = results
model = new TableModel("Name","Size","Hash","Sources","Comment")
results.results.each {
String size = DataHelper.formatSize2Decimal(it.size, false) + "B"
String infoHash = Base64.encode(it.infohash.getRoot())
String sources = String.valueOf(it.sources.size())
String comment = String.valueOf(it.comment != null)
model.addRow(it.name, size, infoHash, sources, comment)
rootToResult.put(infoHash, it)
}
}
}

View File

@@ -0,0 +1,94 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.TerminalSize
import com.googlecode.lanterna.gui2.BasicWindow
import com.googlecode.lanterna.gui2.Button
import com.googlecode.lanterna.gui2.GridLayout
import com.googlecode.lanterna.gui2.LayoutData
import com.googlecode.lanterna.gui2.GridLayout.Alignment
import com.googlecode.lanterna.gui2.Panel
import com.googlecode.lanterna.gui2.TextGUI
import com.googlecode.lanterna.gui2.Window
import com.googlecode.lanterna.gui2.dialogs.MessageDialog
import com.googlecode.lanterna.gui2.dialogs.MessageDialogButton
import com.googlecode.lanterna.gui2.table.Table
import com.muwire.core.Core
import com.muwire.core.download.UIDownloadEvent
import com.muwire.core.search.UIResultEvent
class ResultsView extends BasicWindow {
private final ResultsModel model
private final TextGUI textGUI
private final Core core
private final Table table
private final TerminalSize terminalSize
ResultsView(ResultsModel model, Core core, TextGUI textGUI, TerminalSize terminalSize) {
super(model.results.results[0].sender.getHumanReadableName() + " Results")
this.model = model
this.core = core
this.textGUI = textGUI
this.terminalSize = terminalSize
setHints([Window.Hint.EXPANDED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(1))
table = new Table("Name","Size","Hash","Sources","Comment")
table.setCellSelection(false)
table.setSelectAction({rowSelected()})
table.setTableModel(model.model)
table.setVisibleRows(terminalSize.getRows())
contentPanel.addComponent(table, GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER, true, false))
Button closeButton = new Button("Close", {close()})
contentPanel.addComponent(closeButton, GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER, true, false))
setComponent(contentPanel)
closeButton.takeFocus()
}
private void rowSelected() {
int selectedRow = table.getSelectedRow()
def rows = model.model.getRow(selectedRow)
boolean comment = Boolean.parseBoolean(rows[4])
if (comment) {
Window prompt = new BasicWindow("Download Or View Comment")
prompt.setHints([Window.Hint.CENTERED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(3))
Button downloadButton = new Button("Download", {download(rows[2])})
Button viewButton = new Button("View Comment", {viewComment(rows[2])})
Button closeButton = new Button("Cancel", {prompt.close()})
LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER)
contentPanel.addComponent(downloadButton, layoutData)
contentPanel.addComponent(viewButton, layoutData)
contentPanel.addComponent(closeButton, layoutData)
prompt.setComponent(contentPanel)
downloadButton.takeFocus()
textGUI.addWindowAndWait(prompt)
} else {
download(rows[2])
}
}
private void download(String infohash) {
UIResultEvent result = model.rootToResult[infohash]
def file = new File(core.muOptions.downloadLocation, result.name)
core.eventBus.publish(new UIDownloadEvent(result : [result], sources : result.sources,
target : file, sequential : false))
MessageDialog.showMessageDialog(textGUI, "Download Started", "Started download of "+result.name, MessageDialogButton.OK)
}
private void viewComment(String infohash) {
UIResultEvent result = model.rootToResult[infohash]
ViewCommentView view = new ViewCommentView(result, terminalSize)
textGUI.addWindowAndWait(view)
}
}

View File

@@ -0,0 +1,75 @@
package com.muwire.clilanterna
import com.muwire.core.Core
import com.muwire.core.Persona
import com.muwire.core.SplitPattern
import com.muwire.core.search.QueryEvent
import com.muwire.core.search.SearchEvent
import com.muwire.core.search.UIResultBatchEvent
import com.muwire.core.search.UIResultEvent
import net.i2p.data.Base64
import com.googlecode.lanterna.gui2.TextGUIThread
import com.googlecode.lanterna.gui2.table.TableModel
class SearchModel {
private final TextGUIThread guiThread
private final String query
private final Core core
final TableModel model
private final Map<Persona, UIResultBatchEvent> resultsPerSender = new HashMap<>()
SearchModel(String query, Core core, TextGUIThread guiThread) {
this.query = query
this.core = core
this.guiThread = guiThread
this.model = new TableModel("Sender","Results","Browse","Trust")
core.eventBus.register(UIResultBatchEvent.class, this)
boolean hashSearch = false
byte [] root = null
if (query.length() == 44 && query.indexOf(" ") < 0) {
try {
root = Base64.decode(query)
hashSearch = true
} catch (Exception e) {
// not hash search
}
}
def searchEvent
if (hashSearch) {
searchEvent = new SearchEvent(searchHash : root, uuid : UUID.randomUUID(), oobInfohash : true, compressedResults : true)
} else {
def replaced = query.toLowerCase().trim().replaceAll(SplitPattern.SPLIT_PATTERN, " ")
def terms = replaced.split(" ")
def nonEmpty = []
terms.each { if (it.length() > 0) nonEmpty << it }
searchEvent = new SearchEvent(searchTerms : nonEmpty, uuid : UUID.randomUUID(), oobInfohash: true,
searchComments : core.muOptions.searchComments, compressedResults : true)
}
boolean firstHop = core.muOptions.allowUntrusted || core.muOptions.searchExtraHop
core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : firstHop,
replyTo: core.me.destination, receivedOn: core.me.destination,
originator : core.me))
}
void unregister() {
core.eventBus.unregister(UIResultBatchEvent.class, this)
}
void onUIResultBatchEvent(UIResultBatchEvent e) {
guiThread.invokeLater {
Persona sender = e.results[0].sender
resultsPerSender.put(sender, e)
String browse = String.valueOf(e.results[0].browse)
String results = String.valueOf(e.results.length)
String trust = core.trustService.getLevel(sender.destination).toString()
model.addRow([new PersonaWrapper(sender), results, browse, trust])
}
}
}

View File

@@ -0,0 +1,113 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.TerminalSize
import com.googlecode.lanterna.gui2.BasicWindow
import com.googlecode.lanterna.gui2.Button
import com.googlecode.lanterna.gui2.GridLayout
import com.googlecode.lanterna.gui2.GridLayout.Alignment
import com.googlecode.lanterna.gui2.LayoutData
import com.googlecode.lanterna.gui2.Panel
import com.googlecode.lanterna.gui2.TextGUI
import com.googlecode.lanterna.gui2.Window
import com.googlecode.lanterna.gui2.dialogs.MessageDialog
import com.googlecode.lanterna.gui2.dialogs.MessageDialogButton
import com.googlecode.lanterna.gui2.table.Table
import com.muwire.core.Core
import com.muwire.core.Persona
import com.muwire.core.trust.TrustEvent
import com.muwire.core.trust.TrustLevel
class SearchView extends BasicWindow {
private final Core core
private final SearchModel model
private final Table table
private final TextGUI textGUI
private final TerminalSize terminalSize
SearchView(SearchModel model, Core core, TextGUI textGUI, TerminalSize terminalSize) {
super(model.query)
this.core = core
this.model = model
this.textGUI = textGUI
this.terminalSize = terminalSize
setHints([Window.Hint.EXPANDED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(1))
table = new Table("Sender","Results","Browse","Trust")
table.setCellSelection(false)
table.setSelectAction({rowSelected()})
table.setTableModel(model.model)
table.setVisibleRows(terminalSize.getRows())
contentPanel.addComponent(table, GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER, true, false))
Button closeButton = new Button("Close", {
model.unregister()
close()
})
contentPanel.addComponent(closeButton, GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER, true, false))
setComponent(contentPanel)
closeButton.takeFocus()
}
private void rowSelected() {
int selectedRow = table.getSelectedRow()
def rows = model.model.getRow(selectedRow)
Persona persona = rows[0].persona
boolean browse = Boolean.parseBoolean(rows[2])
Window prompt = new BasicWindow("Show Or Browse "+rows[0]+"?")
prompt.setHints([Window.Hint.CENTERED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(6))
LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER)
Button showResults = new Button("Show Results", {
showResults(persona)
})
Button browseHost = new Button("Browse Host", {
BrowseModel model = new BrowseModel(persona, core, textGUI.getGUIThread())
BrowseView view = new BrowseView(model, textGUI, core, terminalSize)
textGUI.addWindowAndWait(view)
})
Button trustHost = new Button("Trust",{
core.eventBus.publish(new TrustEvent(persona : persona, level : TrustLevel.TRUSTED))
MessageDialog.showMessageDialog(textGUI, "Marked Trusted", persona.getHumanReadableName() + " has been marked trusted",
MessageDialogButton.OK)
})
Button neutralHost = new Button("Neutral",{
core.eventBus.publish(new TrustEvent(persona : persona, level : TrustLevel.NEUTRAL))
MessageDialog.showMessageDialog(textGUI, "Marked Neutral", persona.getHumanReadableName() + " has been marked neutral",
MessageDialogButton.OK)
})
Button distrustHost = new Button("Distrust", {
core.eventBus.publish(new TrustEvent(persona : persona, level : TrustLevel.DISTRUSTED))
MessageDialog.showMessageDialog(textGUI, "Marked Distrusted", persona.getHumanReadableName() + " has been marked distrusted",
MessageDialogButton.OK)
})
Button closePrompt = new Button("Close", {prompt.close()})
contentPanel.with {
addComponent(showResults, layoutData)
if (browse)
addComponent(browseHost, layoutData)
addComponent(trustHost, layoutData)
addComponent(neutralHost, layoutData)
addComponent(distrustHost, layoutData)
addComponent(closePrompt, layoutData)
}
prompt.setComponent(contentPanel)
showResults.takeFocus()
textGUI.addWindowAndWait(prompt)
}
private void showResults(Persona persona) {
def results = model.resultsPerSender.get(persona)
ResultsModel resultsModel = new ResultsModel(results)
ResultsView resultsView = new ResultsView(resultsModel, core, textGUI, terminalSize)
textGUI.addWindowAndWait(resultsView)
}
}

View File

@@ -0,0 +1,16 @@
package com.muwire.clilanterna
import com.muwire.core.SharedFile
class SharedFileWrapper {
private final SharedFile sharedFile
SharedFileWrapper(SharedFile sharedFile) {
this.sharedFile = sharedFile
}
@Override
public String toString() {
sharedFile.getCachedPath()
}
}

View File

@@ -0,0 +1,49 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.gui2.TextGUIThread
import com.googlecode.lanterna.gui2.table.TableModel
import com.muwire.core.Core
import com.muwire.core.trust.RemoteTrustList
import com.muwire.core.trust.TrustEvent
class TrustListModel {
private final TextGUIThread guiThread
private final RemoteTrustList trustList
private final Core core
private final TableModel trustedTableModel, distrustedTableModel
TrustListModel(RemoteTrustList trustList, Core core) {
this.trustList = trustList
this.core = core
trustedTableModel = new TableModel("Trusted User","Your Trust")
distrustedTableModel = new TableModel("Distrusted User", "Your Trust")
refreshModels()
core.eventBus.register(TrustEvent.class, this)
}
void onTrustEvent(TrustEvent e) {
guiThread.invokeLater {
refreshModels()
}
}
private void refreshModels() {
int trustRows = trustedTableModel.getRowCount()
trustRows.times { trustedTableModel.removeRow(0) }
int distrustRows = distrustedTableModel.getRowCount()
distrustRows.times { distrustedTableModel.removeRow(0) }
trustList.good.each {
trustedTableModel.addRow(new PersonaWrapper(it), core.trustService.getLevel(it.destination))
}
trustList.bad.each {
distrustedTableModel.addRow(new PersonaWrapper(it), core.trustService.getLevel(it.destination))
}
}
void unregister() {
core.eventBus.unregister(TrustEvent.class, this)
}
}

View File

@@ -0,0 +1,118 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.TerminalSize
import com.googlecode.lanterna.gui2.BasicWindow
import com.googlecode.lanterna.gui2.Button
import com.googlecode.lanterna.gui2.GridLayout
import com.googlecode.lanterna.gui2.GridLayout.Alignment
import com.googlecode.lanterna.gui2.Label
import com.googlecode.lanterna.gui2.LayoutData
import com.googlecode.lanterna.gui2.Panel
import com.googlecode.lanterna.gui2.TextGUI
import com.googlecode.lanterna.gui2.Window
import com.googlecode.lanterna.gui2.dialogs.MessageDialog
import com.googlecode.lanterna.gui2.dialogs.MessageDialogButton
import com.googlecode.lanterna.gui2.table.Table
import com.muwire.core.Core
import com.muwire.core.Persona
import com.muwire.core.trust.TrustEvent
import com.muwire.core.trust.TrustLevel
class TrustListView extends BasicWindow {
private final TrustListModel model
private final TextGUI textGUI
private final Core core
private final TerminalSize terminalSize
private final Table trusted, distrusted
TrustListView(TrustListModel model, TextGUI textGUI, Core core, TerminalSize terminalSize) {
this.model = model
this.textGUI = textGUI
this.core = core
this.terminalSize = terminalSize
int tableSize = terminalSize.getRows() - 10
setHints([Window.Hint.EXPANDED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(1))
LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER, true, false)
Label nameLabel = new Label("Trust list for "+model.trustList.persona.getHumanReadableName())
Label lastUpdatedLabel = new Label("Last updated "+new Date(model.trustList.timestamp))
contentPanel.addComponent(nameLabel, layoutData)
contentPanel.addComponent(lastUpdatedLabel, layoutData)
Panel topPanel = new Panel()
topPanel.setLayoutManager(new GridLayout(2))
trusted = new Table("Trusted User","Your Trust")
trusted.with {
setCellSelection(false)
setTableModel(model.trustedTableModel)
setVisibleRows(tableSize)
}
trusted.setSelectAction({ actionsForUser(true) })
topPanel.addComponent(trusted, layoutData)
distrusted = new Table("Distrusted User", "Your Trust")
distrusted.with {
setCellSelection(false)
setTableModel(model.distrustedTableModel)
setVisibleRows(tableSize)
}
distrusted.setSelectAction({actionsForUser(false)})
topPanel.addComponent(distrusted, layoutData)
Button closeButton = new Button("Close",{close()})
contentPanel.addComponent(topPanel, layoutData)
contentPanel.addComponent(closeButton, layoutData)
setComponent(contentPanel)
}
private void actionsForUser(boolean trustedUser) {
def table = trustedUser ? trusted : distrusted
def model = trustedUser ? model.trustedTableModel : model.distrustedTableModel
int selectedRow = table.getSelectedRow()
def row = model.getRow(selectedRow)
Persona persona = row[0].persona
Window prompt = new BasicWindow("Actions for "+persona.getHumanReadableName())
prompt.setHints([Window.Hint.CENTERED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(4))
LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER)
Button trustButton = new Button("Trust",{
core.eventBus.publish(new TrustEvent(persona : persona, level : TrustLevel.TRUSTED))
MessageDialog.showMessageDialog(textGUI, "Marked Trusted", persona.getHumanReadableName() + "has been marked trusted",
MessageDialogButton.OK)
})
Button neutralButton = new Button("Neutral",{
core.eventBus.publish(new TrustEvent(persona : persona, level : TrustLevel.NEUTRAL))
MessageDialog.showMessageDialog(textGUI, "Marked Neutral", persona.getHumanReadableName() + "has been marked neutral",
MessageDialogButton.OK)
})
Button distrustButton = new Button("Distrust",{
core.eventBus.publish(new TrustEvent(persona : persona, level : TrustLevel.DISTRUSTED))
MessageDialog.showMessageDialog(textGUI, "Marked Distrusted", persona.getHumanReadableName() + "has been marked distrusted",
MessageDialogButton.OK)
})
Button closeButton = new Button("Close",{prompt.close()})
contentPanel.with {
addComponent(trustButton,layoutData)
addComponent(neutralButton, layoutData)
addComponent(distrustButton, layoutData)
addComponent(closeButton, layoutData)
}
prompt.setComponent(contentPanel)
textGUI.addWindowAndWait(prompt)
}
}

View File

@@ -0,0 +1,15 @@
package com.muwire.clilanterna
import com.muwire.core.trust.RemoteTrustList
class TrustListWrapper {
private final RemoteTrustList trustList
TrustListWrapper(RemoteTrustList trustList) {
this.trustList = trustList
}
@Override
public String toString() {
trustList.persona.getHumanReadableName()
}
}

View File

@@ -0,0 +1,78 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.gui2.TextGUIThread
import com.googlecode.lanterna.gui2.table.TableModel
import com.muwire.core.Core
import com.muwire.core.files.AllFilesLoadedEvent
import com.muwire.core.trust.TrustEvent
import com.muwire.core.trust.TrustSubscriptionEvent
import com.muwire.core.trust.TrustSubscriptionUpdatedEvent
class TrustModel {
private final TextGUIThread guiThread
private final Core core
private final TableModel modelTrusted, modelDistrusted, modelSubscriptions
TrustModel(TextGUIThread guiThread, Core core) {
this.guiThread = guiThread
this.core = core
modelTrusted = new TableModel("Trusted Users")
modelDistrusted = new TableModel("Distrusted Users")
modelSubscriptions = new TableModel("Name","Trusted","Distrusted","Status","Last Updated")
core.eventBus.register(TrustEvent.class, this)
core.eventBus.register(AllFilesLoadedEvent.class, this)
core.eventBus.register(TrustSubscriptionUpdatedEvent.class, this)
}
void onTrustEvent(TrustEvent e) {
guiThread.invokeLater {
refreshModels()
}
}
void onTrustSubscriptionUpdatedEvent(TrustSubscriptionUpdatedEvent e) {
guiThread.invokeLater {
refreshModels()
}
}
void onAllFilesLoadedEvent(AllFilesLoadedEvent e) {
guiThread.invokeLater {
refreshModels()
}
core.muOptions.trustSubscriptions.each {
core.eventBus.publish(new TrustSubscriptionEvent(persona : it, subscribe : true))
}
}
private void refreshModels() {
int trustedRows = modelTrusted.getRowCount()
trustedRows.times { modelTrusted.removeRow(0) }
int distrustedRows = modelDistrusted.getRowCount()
distrustedRows.times { modelDistrusted.removeRow(0) }
int subsRows = modelSubscriptions.getRowCount()
subsRows.times { modelSubscriptions.removeRow(0) }
core.trustService.good.values().each {
modelTrusted.addRow(new PersonaWrapper(it))
}
core.trustService.bad.values().each {
modelDistrusted.addRow(new PersonaWrapper(it))
}
core.trustSubscriber.remoteTrustLists.values().each {
def name = new TrustListWrapper(it)
String trusted = String.valueOf(it.good.size())
String distrusted = String.valueOf(it.bad.size())
String status = it.status
String lastUpdated = it.timestamp == 0 ? "Never" : new Date(it.timestamp)
modelSubscriptions.addRow(name, trusted, distrusted, status, lastUpdated)
}
}
}

View File

@@ -0,0 +1,204 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.TerminalSize
import com.googlecode.lanterna.gui2.BasicWindow
import com.googlecode.lanterna.gui2.Button
import com.googlecode.lanterna.gui2.GridLayout
import com.googlecode.lanterna.gui2.LayoutData
import com.googlecode.lanterna.gui2.Panel
import com.googlecode.lanterna.gui2.TextGUI
import com.googlecode.lanterna.gui2.Window
import com.googlecode.lanterna.gui2.dialogs.MessageDialog
import com.googlecode.lanterna.gui2.dialogs.MessageDialogBuilder
import com.googlecode.lanterna.gui2.dialogs.MessageDialogButton
import com.googlecode.lanterna.gui2.GridLayout.Alignment
import com.googlecode.lanterna.gui2.Label
import com.googlecode.lanterna.gui2.table.Table
import com.muwire.core.Core
import com.muwire.core.Persona
import com.muwire.core.trust.TrustEvent
import com.muwire.core.trust.TrustLevel
import com.muwire.core.trust.TrustSubscriptionEvent
class TrustView extends BasicWindow {
private final TrustModel model
private final TextGUI textGUI
private final Core core
private final TerminalSize terminalSize
private final Table trusted, distrusted, subscriptions
TrustView(TrustModel model, TextGUI textGUI, Core core, TerminalSize terminalSize) {
this.model = model
this.textGUI = textGUI
this.core = core
this.terminalSize = terminalSize
int tableSize = (terminalSize.getRows() / 2 - 10).toInteger()
setHints([Window.Hint.EXPANDED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(1))
LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER, true, false)
Panel topPanel = new Panel()
topPanel.setLayoutManager(new GridLayout(2))
trusted = new Table("Trusted Users")
trusted.setCellSelection(false)
trusted.setSelectAction({trustedActions()})
trusted.setTableModel(model.modelTrusted)
trusted.setVisibleRows(tableSize)
topPanel.addComponent(trusted, layoutData)
distrusted = new Table("Distrusted users")
distrusted.setCellSelection(false)
distrusted.setSelectAction({distrustedActions()})
distrusted.setTableModel(model.modelDistrusted)
distrusted.setVisibleRows(tableSize)
topPanel.addComponent(distrusted, layoutData)
Panel bottomPanel = new Panel()
bottomPanel.setLayoutManager(new GridLayout(1))
Label tableName = new Label("Trust List Subscriptions")
bottomPanel.addComponent(tableName, layoutData)
subscriptions = new Table("Name","Trusted","Distrusted","Status","Last Updated")
subscriptions.setCellSelection(false)
subscriptions.setSelectAction({trustListActions()})
subscriptions.setTableModel(model.modelSubscriptions)
subscriptions.setVisibleRows(tableSize)
bottomPanel.addComponent(subscriptions, layoutData)
Button closeButton = new Button("Close", {close()})
contentPanel.addComponent(topPanel, layoutData)
contentPanel.addComponent(bottomPanel, layoutData)
contentPanel.addComponent(closeButton, layoutData)
setComponent(contentPanel)
}
private void trustedActions() {
int selectedRow = trusted.getSelectedRow()
def row = model.modelTrusted.getRow(selectedRow)
Persona persona = row[0].persona
Window prompt = new BasicWindow("Change Trust For "+persona.getHumanReadableName())
prompt.setHints([Window.Hint.CENTERED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(4))
LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER)
Button subscribe = new Button("Subscribe", {
core.muOptions.trustSubscriptions.add(persona)
saveMuSettings()
core.eventBus.publish(new TrustSubscriptionEvent(persona : persona, subscribe : true))
MessageDialog.showMessageDialog(textGUI, "Subscribed", "Subscribed from trust list of " + persona.getHumanReadableName(),
MessageDialogButton.OK)
model.refreshModels()
})
Button markNeutral = new Button("Mark Neutral", {
core.eventBus.publish(new TrustEvent(persona : persona, level : TrustLevel.NEUTRAL))
MessageDialog.showMessageDialog(textGUI, "Marked Neutral", persona.getHumanReadableName() + "has been marked neutral",
MessageDialogButton.OK)
})
Button markDistrusted = new Button("Mark Distrusted", {
core.eventBus.publish(new TrustEvent(persona : persona, level : TrustLevel.DISTRUSTED))
MessageDialog.showMessageDialog(textGUI, "Marked Distrusted", persona.getHumanReadableName() + "has been marked distrusted",
MessageDialogButton.OK)
})
Button closeButton = new Button("Close", {prompt.close()})
contentPanel.with {
addComponent(subscribe, layoutData)
addComponent(markNeutral, layoutData)
addComponent(markDistrusted, layoutData)
addComponent(closeButton, layoutData)
}
prompt.setComponent(contentPanel)
textGUI.addWindowAndWait(prompt)
}
private void distrustedActions() {
int selectedRow = trusted.getSelectedRow()
def row = model.modelDistrusted.getRow(selectedRow)
Persona persona = row[0].persona
Window prompt = new BasicWindow("Change Trust For "+persona.getHumanReadableName())
prompt.setHints([Window.Hint.CENTERED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(3))
LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER)
Button markNeutral = new Button("Mark Neutral", {
core.eventBus.publish(new TrustEvent(persona : persona, level : TrustLevel.NEUTRAL))
MessageDialog.showMessageDialog(textGUI, "Marked Neutral", persona.getHumanReadableName() + "has been marked neutral",
MessageDialogButton.OK)
})
Button markDistrusted = new Button("Mark Trusted", {
core.eventBus.publish(new TrustEvent(persona : persona, level : TrustLevel.TRUSTED))
MessageDialog.showMessageDialog(textGUI, "Marked Trusted", persona.getHumanReadableName() + "has been marked trusted",
MessageDialogButton.OK)
})
Button closeButton = new Button("Close", {prompt.close()})
contentPanel.with {
addComponent(markDistrusted, layoutData)
addComponent(markNeutral, layoutData)
addComponent(closeButton, layoutData)
}
prompt.setComponent(contentPanel)
textGUI.addWindowAndWait(prompt)
}
private void trustListActions() {
int selectedRow = subscriptions.getSelectedRow()
def row = model.modelSubscriptions.getRow(selectedRow)
def trustList = row[0].trustList
Persona persona = trustList.persona
Window prompt = new BasicWindow("Trust List Actions")
prompt.setHints([Window.Hint.CENTERED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(4))
LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER)
Button reviewButton = new Button("Review",{review(trustList)})
Button updateButton = new Button("Update",{
core.eventBus.publish(new TrustSubscriptionEvent(persona : persona, subscribe : true))
MessageDialog.showMessageDialog(textGUI, "Updating...", "Trust list will update soon", MessageDialogButton.OK)
})
Button unsubscribeButton = new Button("Unsubscribe", {
core.muOptions.trustSubscriptions.remove(persona)
saveMuSettings()
core.eventBus.publish(new TrustSubscriptionEvent(persona : persona, subscribe : false))
MessageDialog.showMessageDialog(textGUI, "Unsubscribed", "Unsubscribed from trust list of " + persona.getHumanReadableName(),
MessageDialogButton.OK)
model.refreshModels()
})
Button closeButton = new Button("Close", {prompt.close()})
contentPanel.with {
addComponent(reviewButton, layoutData)
addComponent(updateButton, layoutData)
addComponent(unsubscribeButton, layoutData)
addComponent(closeButton, layoutData)
}
prompt.setComponent(contentPanel)
textGUI.addWindowAndWait(prompt)
}
private void review(def trustList) {
TrustListModel model = new TrustListModel(trustList, core)
TrustListView view = new TrustListView(model, textGUI, core, terminalSize)
textGUI.addWindowAndWait(view)
model.unregister()
}
private void saveMuSettings() {
File settingsFile = new File(core.home,"MuWire.properties")
settingsFile.withOutputStream { core.muOptions.write(it) }
}
}

View File

@@ -0,0 +1,66 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.gui2.TextGUIThread
import com.googlecode.lanterna.gui2.table.TableModel
import com.muwire.core.Core
import com.muwire.core.upload.UploadEvent
import com.muwire.core.upload.UploadFinishedEvent
import com.muwire.core.upload.Uploader
import net.i2p.data.DataHelper
class UploadsModel {
private final TextGUIThread guiThread
private final Core core
private final List<Uploader> uploaders = new ArrayList<>()
private final TableModel model = new TableModel("Name","Progress","Downloader","Remote Pieces")
UploadsModel(TextGUIThread guiThread, Core core) {
this.guiThread = guiThread
this.core = core
core.eventBus.register(UploadEvent.class, this)
core.eventBus.register(UploadFinishedEvent.class, this)
Timer timer = new Timer(true)
Runnable refreshModel = {refreshModel()}
timer.schedule({
guiThread.invokeLater(refreshModel)
} as TimerTask, 1000, 1000)
}
void onUploadEvent(UploadEvent e) {
guiThread.invokeLater({uploaders.add(e.uploader)})
}
void onUploadFinishedEvent(UploadFinishedEvent e) {
guiThread.invokeLater({uploaders.remove(e.uploader)})
}
private void refreshModel() {
int uploadersSize = model.getRowCount()
uploadersSize.times { model.removeRow(0) }
uploaders.each {
String name = it.getName()
int percent = it.getProgress()
String percentString = "$percent% of piece".toString()
String downloader = it.getDownloader()
int pieces = it.getTotalPieces()
int done = it.getDonePieces()
int percentTotal = -1
if (pieces != 0)
percentTotal = (done * 100) / pieces
long size = it.getTotalSize()
String totalSize = ""
if (size > 0)
totalSize = " of " + DataHelper.formatSize2Decimal(size, false) + "B"
String remotePieces = String.format("%02d", percentTotal) + "% ${totalSize} ($done/$pieces) pcs".toString()
model.addRow([name, percentString, downloader, remotePieces])
}
}
}

View File

@@ -0,0 +1,39 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.TerminalSize
import com.googlecode.lanterna.gui2.BasicWindow
import com.googlecode.lanterna.gui2.Button
import com.googlecode.lanterna.gui2.GridLayout
import com.googlecode.lanterna.gui2.GridLayout.Alignment
import com.googlecode.lanterna.gui2.LayoutData
import com.googlecode.lanterna.gui2.Panel
import com.googlecode.lanterna.gui2.TextGUI
import com.googlecode.lanterna.gui2.Window
import com.googlecode.lanterna.gui2.table.Table
class UploadsView extends BasicWindow {
private final UploadsModel model
private final Table table
UploadsView(UploadsModel model, TerminalSize terminalSize) {
this.model = model
setHints([Window.Hint.EXPANDED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(1))
LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER, true, false)
table = new Table("Name","Progress","Downloader","Remote Pieces")
table.setCellSelection(false)
table.setTableModel(model.model)
table.setVisibleRows(terminalSize.getRows())
contentPanel.addComponent(table, layoutData)
Button closeButton = new Button("Close",{close()})
contentPanel.addComponent(closeButton, layoutData)
setComponent(contentPanel)
closeButton.takeFocus()
}
}

View File

@@ -0,0 +1,39 @@
package com.muwire.clilanterna
import com.googlecode.lanterna.TerminalSize
import com.googlecode.lanterna.gui2.BasicWindow
import com.googlecode.lanterna.gui2.Button
import com.googlecode.lanterna.gui2.GridLayout
import com.googlecode.lanterna.gui2.GridLayout.Alignment
import com.googlecode.lanterna.gui2.LayoutData
import com.googlecode.lanterna.gui2.Panel
import com.googlecode.lanterna.gui2.TextBox
import com.googlecode.lanterna.gui2.Window
import com.muwire.core.SharedFile
import com.muwire.core.search.UIResultEvent
import com.muwire.core.util.DataUtil
import net.i2p.data.Base64
class ViewCommentView extends BasicWindow {
private final TextBox textBox
private final LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER)
ViewCommentView(UIResultEvent result, TerminalSize terminalSize) {
super("View Comments For "+result.getName())
setHints([Window.Hint.CENTERED])
Panel contentPanel = new Panel()
contentPanel.setLayoutManager(new GridLayout(1))
TerminalSize boxSize = new TerminalSize((terminalSize.getColumns() / 2).toInteger(), (terminalSize.getRows() / 2).toInteger())
textBox = new TextBox(boxSize, result.comment, TextBox.Style.MULTI_LINE)
contentPanel.addComponent(textBox, layoutData)
Button closeButton = new Button("Close", {close()})
contentPanel.addComponent(closeButton, layoutData)
setComponent(contentPanel)
}
}

View File

@@ -62,7 +62,7 @@ class ConnectionEstablisher {
void stop() {
timer.cancel()
executor.shutdownNow()
closer.shutdown()
closer.shutdownNow()
}
private void connectIfNeeded() {
@@ -123,10 +123,12 @@ class ConnectionEstablisher {
}
private void fail(Endpoint endpoint) {
closer.execute {
endpoint.close()
eventBus.publish(new ConnectionEvent(endpoint: endpoint, incoming: false, leaf: false, status: ConnectionAttemptStatus.FAILED))
} as Runnable
if (!closer.isShutdown()) {
closer.execute {
endpoint.close()
eventBus.publish(new ConnectionEvent(endpoint: endpoint, incoming: false, leaf: false, status: ConnectionAttemptStatus.FAILED))
} as Runnable
}
}
private void readK(Endpoint e) {

View File

@@ -1,5 +1,5 @@
group = com.muwire
version = 0.5.3
version = 0.5.4
i2pVersion = 0.9.43
groovyVersion = 2.4.15
slf4jVersion = 1.7.25

View File

@@ -4,5 +4,6 @@ include 'update-server'
include 'core'
include 'gui'
include 'cli'
include 'cli-lanterna'
// include 'webui'
// include 'plug'