Compare commits
20 Commits
muwire-0.1
...
muwire-0.2
Author | SHA1 | Date | |
---|---|---|---|
![]() |
041fc3bef3 | ||
![]() |
03c3b1ebf1 | ||
![]() |
aece390daa | ||
![]() |
cf63be68e8 | ||
![]() |
88ece4dc23 | ||
![]() |
13767d58f2 | ||
![]() |
05a1ccd3d8 | ||
![]() |
6807c14a5f | ||
![]() |
684be0c50e | ||
![]() |
6655c262c6 | ||
![]() |
b1ccd55030 | ||
![]() |
a3becd0f7e | ||
![]() |
af2f3e0ebf | ||
![]() |
e2b7ffa1db | ||
![]() |
0e0176acfc | ||
![]() |
7f09bb079c | ||
![]() |
77e48b01bb | ||
![]() |
12db6857c1 | ||
![]() |
acd67733a5 | ||
![]() |
8d3ce7aa8e |
42
TODO.md
Normal file
42
TODO.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# TODO List
|
||||||
|
|
||||||
|
Not in any particular order yet
|
||||||
|
|
||||||
|
### Big Items
|
||||||
|
|
||||||
|
##### Alternate Locations
|
||||||
|
|
||||||
|
This helps peers discover new sources for a file while the download is in progress. Also makes sharing of partial files possible.
|
||||||
|
|
||||||
|
##### Bloom Filters
|
||||||
|
|
||||||
|
This reduces query traffic by not sending last hop queries to peers that definitely do not have the file
|
||||||
|
|
||||||
|
##### Two-tier Topology
|
||||||
|
|
||||||
|
This helps with scalability
|
||||||
|
|
||||||
|
##### Trust List Sharing
|
||||||
|
|
||||||
|
For helping users make better decisions whom to trust
|
||||||
|
|
||||||
|
##### Content Control Panel
|
||||||
|
|
||||||
|
To allow every user to not route queries for content they do not like. This is mostly GUI work, the backend part is simple
|
||||||
|
|
||||||
|
##### Packaging With JRE, Embedded Router
|
||||||
|
|
||||||
|
For ease of deployment for new users, and so that users do not need to run a separate I2P router
|
||||||
|
|
||||||
|
##### Web UI, REST Interface, etc.
|
||||||
|
|
||||||
|
Basically any non-gui non-cli user interface
|
||||||
|
|
||||||
|
### Small Items
|
||||||
|
|
||||||
|
* Detect if router is dead and show warning or exit
|
||||||
|
* Wrapper of some kind for in-place upgrades
|
||||||
|
* Download file sequentially
|
||||||
|
* Unsharing of files
|
||||||
|
* Multiple-selection download, Ctrl-A
|
||||||
|
* Automatic sharing of new files in shared directories (more like medium item)
|
@@ -34,7 +34,7 @@ class Cli {
|
|||||||
|
|
||||||
Core core
|
Core core
|
||||||
try {
|
try {
|
||||||
core = new Core(props, home, "0.1.13")
|
core = new Core(props, home, "0.2.1")
|
||||||
} catch (Exception bad) {
|
} catch (Exception bad) {
|
||||||
bad.printStackTrace(System.out)
|
bad.printStackTrace(System.out)
|
||||||
println "Failed to initialize core, exiting"
|
println "Failed to initialize core, exiting"
|
||||||
|
@@ -53,7 +53,7 @@ class CliDownloader {
|
|||||||
|
|
||||||
Core core
|
Core core
|
||||||
try {
|
try {
|
||||||
core = new Core(props, home, "0.1.13")
|
core = new Core(props, home, "0.2.1")
|
||||||
} catch (Exception bad) {
|
} catch (Exception bad) {
|
||||||
bad.printStackTrace(System.out)
|
bad.printStackTrace(System.out)
|
||||||
println "Failed to initialize core, exiting"
|
println "Failed to initialize core, exiting"
|
||||||
|
@@ -268,7 +268,7 @@ public class Core {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Core core = new Core(props, home, "0.1.13")
|
Core core = new Core(props, home, "0.2.1")
|
||||||
core.startServices()
|
core.startServices()
|
||||||
|
|
||||||
// ... at the end, sleep or execute script
|
// ... at the end, sleep or execute script
|
||||||
|
@@ -13,6 +13,7 @@ class MuWireSettings {
|
|||||||
String sharedFiles
|
String sharedFiles
|
||||||
CrawlerResponse crawlerResponse
|
CrawlerResponse crawlerResponse
|
||||||
boolean shareDownloadedFiles
|
boolean shareDownloadedFiles
|
||||||
|
boolean watchSharedDirectories
|
||||||
|
|
||||||
MuWireSettings() {
|
MuWireSettings() {
|
||||||
this(new Properties())
|
this(new Properties())
|
||||||
@@ -29,6 +30,7 @@ class MuWireSettings {
|
|||||||
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","15"))
|
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","15"))
|
||||||
updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","36"))
|
updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","36"))
|
||||||
shareDownloadedFiles = Boolean.parseBoolean(props.getProperty("shareDownloadedFiles","true"))
|
shareDownloadedFiles = Boolean.parseBoolean(props.getProperty("shareDownloadedFiles","true"))
|
||||||
|
watchSharedDirectories = Boolean.parseBoolean(props.getProperty("watchSharedDirectories","true"))
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(OutputStream out) throws IOException {
|
void write(OutputStream out) throws IOException {
|
||||||
@@ -41,6 +43,7 @@ class MuWireSettings {
|
|||||||
props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval))
|
props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval))
|
||||||
props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval))
|
props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval))
|
||||||
props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles))
|
props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles))
|
||||||
|
props.setProperty("watchSharedDirectories", String.valueOf(watchSharedDirectories))
|
||||||
if (sharedFiles != null)
|
if (sharedFiles != null)
|
||||||
props.setProperty("sharedFiles", sharedFiles)
|
props.setProperty("sharedFiles", sharedFiles)
|
||||||
props.store(out, "")
|
props.store(out, "")
|
||||||
|
@@ -0,0 +1,4 @@
|
|||||||
|
package com.muwire.core.files
|
||||||
|
|
||||||
|
class DirectoryWatcher {
|
||||||
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
group = com.muwire
|
group = com.muwire
|
||||||
version = 0.1.13
|
version = 0.2.1
|
||||||
groovyVersion = 2.4.15
|
groovyVersion = 2.4.15
|
||||||
slf4jVersion = 1.7.25
|
slf4jVersion = 1.7.25
|
||||||
spockVersion = 1.1-groovy-2.4
|
spockVersion = 1.1-groovy-2.4
|
||||||
|
@@ -49,9 +49,20 @@ class MainFrameController {
|
|||||||
def group = mvcGroup.createMVCGroup("SearchTab", uuid.toString(), params)
|
def group = mvcGroup.createMVCGroup("SearchTab", uuid.toString(), params)
|
||||||
model.results[uuid.toString()] = group
|
model.results[uuid.toString()] = group
|
||||||
|
|
||||||
|
boolean hashSearch = false
|
||||||
|
byte [] root = null
|
||||||
|
if (search.length() == 44 && search.indexOf(" ") < 0) {
|
||||||
|
try {
|
||||||
|
root = Base64.decode(search)
|
||||||
|
hashSearch = true
|
||||||
|
} catch (Exception e) {
|
||||||
|
// not a hash search
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def searchEvent
|
def searchEvent
|
||||||
if (model.hashSearch) {
|
if (hashSearch) {
|
||||||
searchEvent = new SearchEvent(searchHash : Base64.decode(search), uuid : uuid)
|
searchEvent = new SearchEvent(searchHash : root, uuid : uuid)
|
||||||
} else {
|
} else {
|
||||||
// this can be improved a lot
|
// this can be improved a lot
|
||||||
def replaced = search.toLowerCase().trim().replaceAll(Constants.SPLIT_PATTERN, " ")
|
def replaced = search.toLowerCase().trim().replaceAll(Constants.SPLIT_PATTERN, " ")
|
||||||
@@ -95,10 +106,11 @@ class MainFrameController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int selectedDownload() {
|
private int selectedDownload() {
|
||||||
def selected = builder.getVariable("downloads-table").getSelectedRow()
|
def downloadsTable = builder.getVariable("downloads-table")
|
||||||
|
def selected = downloadsTable.getSelectedRow()
|
||||||
def sortEvt = mvcGroup.view.lastDownloadSortEvent
|
def sortEvt = mvcGroup.view.lastDownloadSortEvent
|
||||||
if (sortEvt != null)
|
if (sortEvt != null)
|
||||||
selected = sortEvt.convertPreviousRowIndexToModel(selected)
|
selected = downloadsTable.rowSorter.convertRowIndexToModel(selected)
|
||||||
selected
|
selected
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,16 +186,6 @@ class MainFrameController {
|
|||||||
markTrust("trusted-table", TrustLevel.NEUTRAL, model.trusted)
|
markTrust("trusted-table", TrustLevel.NEUTRAL, model.trusted)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ControllerAction
|
|
||||||
void keywordSearch() {
|
|
||||||
model.hashSearch = false
|
|
||||||
}
|
|
||||||
|
|
||||||
@ControllerAction
|
|
||||||
void hashSearch() {
|
|
||||||
model.hashSearch = true
|
|
||||||
}
|
|
||||||
|
|
||||||
void unshareSelectedFiles() {
|
void unshareSelectedFiles() {
|
||||||
println "unsharing selected files"
|
println "unsharing selected files"
|
||||||
}
|
}
|
||||||
|
@@ -93,6 +93,10 @@ class OptionsController {
|
|||||||
model.excludeLocalResult = excludeLocalResult
|
model.excludeLocalResult = excludeLocalResult
|
||||||
uiSettings.excludeLocalResult = excludeLocalResult
|
uiSettings.excludeLocalResult = excludeLocalResult
|
||||||
|
|
||||||
|
boolean showSearchHashes = view.showSearchHashesCheckbox.model.isSelected()
|
||||||
|
model.showSearchHashes = showSearchHashes
|
||||||
|
uiSettings.showSearchHashes = showSearchHashes
|
||||||
|
|
||||||
File uiSettingsFile = new File(core.home, "gui.properties")
|
File uiSettingsFile = new File(core.home, "gui.properties")
|
||||||
uiSettingsFile.withOutputStream {
|
uiSettingsFile.withOutputStream {
|
||||||
uiSettings.write(it)
|
uiSettings.write(it)
|
||||||
|
@@ -34,6 +34,7 @@ import griffon.core.mvc.MVCGroup
|
|||||||
import griffon.inject.MVCMember
|
import griffon.inject.MVCMember
|
||||||
import griffon.transform.FXObservable
|
import griffon.transform.FXObservable
|
||||||
import griffon.transform.Observable
|
import griffon.transform.Observable
|
||||||
|
import net.i2p.data.Base64
|
||||||
import net.i2p.data.Destination
|
import net.i2p.data.Destination
|
||||||
import griffon.metadata.ArtifactProviderFor
|
import griffon.metadata.ArtifactProviderFor
|
||||||
|
|
||||||
@@ -56,8 +57,6 @@ class MainFrameModel {
|
|||||||
def trusted = []
|
def trusted = []
|
||||||
def distrusted = []
|
def distrusted = []
|
||||||
|
|
||||||
boolean hashSearch
|
|
||||||
|
|
||||||
@Observable int connections
|
@Observable int connections
|
||||||
@Observable String me
|
@Observable String me
|
||||||
@Observable boolean searchButtonsEnabled
|
@Observable boolean searchButtonsEnabled
|
||||||
@@ -69,6 +68,8 @@ class MainFrameModel {
|
|||||||
volatile Core core
|
volatile Core core
|
||||||
|
|
||||||
private long lastRetryTime = System.currentTimeMillis()
|
private long lastRetryTime = System.currentTimeMillis()
|
||||||
|
|
||||||
|
UISettings uiSettings
|
||||||
|
|
||||||
void updateTablePreservingSelection(String tableName) {
|
void updateTablePreservingSelection(String tableName) {
|
||||||
def downloadTable = builder.getVariable(tableName)
|
def downloadTable = builder.getVariable(tableName)
|
||||||
@@ -79,7 +80,7 @@ class MainFrameModel {
|
|||||||
|
|
||||||
void mvcGroupInit(Map<String, Object> args) {
|
void mvcGroupInit(Map<String, Object> args) {
|
||||||
|
|
||||||
UISettings uiSettings = application.context.get("ui-settings")
|
uiSettings = application.context.get("ui-settings")
|
||||||
|
|
||||||
Timer timer = new Timer("download-pumper", true)
|
Timer timer = new Timer("download-pumper", true)
|
||||||
timer.schedule({
|
timer.schedule({
|
||||||
@@ -177,7 +178,8 @@ class MainFrameModel {
|
|||||||
topPanel.getLayout().show(topPanel, "top-search-panel")
|
topPanel.getLayout().show(topPanel, "top-search-panel")
|
||||||
}
|
}
|
||||||
|
|
||||||
connectionList.add(e.endpoint.destination)
|
UIConnection con = new UIConnection(destination : e.endpoint.destination, incoming : e.incoming)
|
||||||
|
connectionList.add(con)
|
||||||
JTable table = builder.getVariable("connections-table")
|
JTable table = builder.getVariable("connections-table")
|
||||||
table.model.fireTableDataChanged()
|
table.model.fireTableDataChanged()
|
||||||
}
|
}
|
||||||
@@ -192,7 +194,8 @@ class MainFrameModel {
|
|||||||
topPanel.getLayout().show(topPanel, "top-connect-panel")
|
topPanel.getLayout().show(topPanel, "top-connect-panel")
|
||||||
}
|
}
|
||||||
|
|
||||||
connectionList.remove(e.destination)
|
UIConnection con = new UIConnection(destination : e.destination)
|
||||||
|
connectionList.remove(con)
|
||||||
JTable table = builder.getVariable("connections-table")
|
JTable table = builder.getVariable("connections-table")
|
||||||
table.model.fireTableDataChanged()
|
table.model.fireTableDataChanged()
|
||||||
}
|
}
|
||||||
@@ -258,14 +261,23 @@ class MainFrameModel {
|
|||||||
void onQueryEvent(QueryEvent e) {
|
void onQueryEvent(QueryEvent e) {
|
||||||
if (e.replyTo == core.me.destination)
|
if (e.replyTo == core.me.destination)
|
||||||
return
|
return
|
||||||
StringBuilder sb = new StringBuilder()
|
|
||||||
e.searchEvent.searchTerms?.each {
|
def search
|
||||||
sb.append(it)
|
if (e.searchEvent.searchHash != null) {
|
||||||
sb.append(" ")
|
if (!uiSettings.showSearchHashes) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
search = Base64.encode(e.searchEvent.searchHash)
|
||||||
|
} else {
|
||||||
|
StringBuilder sb = new StringBuilder()
|
||||||
|
e.searchEvent.searchTerms?.each {
|
||||||
|
sb.append(it)
|
||||||
|
sb.append(" ")
|
||||||
|
}
|
||||||
|
search = sb.toString()
|
||||||
|
if (search.trim().size() == 0)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
def search = sb.toString()
|
|
||||||
if (search.trim().size() == 0)
|
|
||||||
return
|
|
||||||
runInsideUIAsync {
|
runInsideUIAsync {
|
||||||
searches.addFirst(new IncomingSearch(search : search, replyTo : e.replyTo, originator : e.originator))
|
searches.addFirst(new IncomingSearch(search : search, replyTo : e.replyTo, originator : e.originator))
|
||||||
while(searches.size() > 200)
|
while(searches.size() > 200)
|
||||||
@@ -303,4 +315,22 @@ class MainFrameModel {
|
|||||||
table.model.fireTableDataChanged()
|
table.model.fireTableDataChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class UIConnection {
|
||||||
|
Destination destination
|
||||||
|
boolean incoming
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
destination.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof UIConnection))
|
||||||
|
return false
|
||||||
|
UIConnection other = (UIConnection) o
|
||||||
|
return destination == other.destination
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -27,6 +27,7 @@ class OptionsModel {
|
|||||||
@Observable boolean clearCancelledDownloads
|
@Observable boolean clearCancelledDownloads
|
||||||
@Observable boolean clearFinishedDownloads
|
@Observable boolean clearFinishedDownloads
|
||||||
@Observable boolean excludeLocalResult
|
@Observable boolean excludeLocalResult
|
||||||
|
@Observable boolean showSearchHashes
|
||||||
|
|
||||||
void mvcGroupInit(Map<String, String> args) {
|
void mvcGroupInit(Map<String, String> args) {
|
||||||
MuWireSettings settings = application.context.get("muwire-settings")
|
MuWireSettings settings = application.context.get("muwire-settings")
|
||||||
@@ -48,5 +49,6 @@ class OptionsModel {
|
|||||||
clearCancelledDownloads = uiSettings.clearCancelledDownloads
|
clearCancelledDownloads = uiSettings.clearCancelledDownloads
|
||||||
clearFinishedDownloads = uiSettings.clearFinishedDownloads
|
clearFinishedDownloads = uiSettings.clearFinishedDownloads
|
||||||
excludeLocalResult = uiSettings.excludeLocalResult
|
excludeLocalResult = uiSettings.excludeLocalResult
|
||||||
|
showSearchHashes = uiSettings.showSearchHashes
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -3,6 +3,7 @@ package com.muwire.gui
|
|||||||
import griffon.core.artifact.GriffonView
|
import griffon.core.artifact.GriffonView
|
||||||
import griffon.inject.MVCMember
|
import griffon.inject.MVCMember
|
||||||
import griffon.metadata.ArtifactProviderFor
|
import griffon.metadata.ArtifactProviderFor
|
||||||
|
import net.i2p.data.Base64
|
||||||
import net.i2p.data.DataHelper
|
import net.i2p.data.DataHelper
|
||||||
|
|
||||||
import javax.swing.BorderFactory
|
import javax.swing.BorderFactory
|
||||||
@@ -13,6 +14,7 @@ import javax.swing.JLabel
|
|||||||
import javax.swing.JMenuItem
|
import javax.swing.JMenuItem
|
||||||
import javax.swing.JPopupMenu
|
import javax.swing.JPopupMenu
|
||||||
import javax.swing.JSplitPane
|
import javax.swing.JSplitPane
|
||||||
|
import javax.swing.JTable
|
||||||
import javax.swing.ListSelectionModel
|
import javax.swing.ListSelectionModel
|
||||||
import javax.swing.SwingConstants
|
import javax.swing.SwingConstants
|
||||||
import javax.swing.border.Border
|
import javax.swing.border.Border
|
||||||
@@ -28,6 +30,8 @@ import java.awt.FlowLayout
|
|||||||
import java.awt.GridBagConstraints
|
import java.awt.GridBagConstraints
|
||||||
import java.awt.GridBagLayout
|
import java.awt.GridBagLayout
|
||||||
import java.awt.Insets
|
import java.awt.Insets
|
||||||
|
import java.awt.Toolkit
|
||||||
|
import java.awt.datatransfer.StringSelection
|
||||||
import java.awt.event.MouseAdapter
|
import java.awt.event.MouseAdapter
|
||||||
import java.awt.event.MouseEvent
|
import java.awt.event.MouseEvent
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
@@ -43,6 +47,7 @@ class MainFrameView {
|
|||||||
|
|
||||||
def downloadsTable
|
def downloadsTable
|
||||||
def lastDownloadSortEvent
|
def lastDownloadSortEvent
|
||||||
|
def lastSharedSortEvent
|
||||||
|
|
||||||
void initUI() {
|
void initUI() {
|
||||||
UISettings settings = application.context.get("ui-settings")
|
UISettings settings = application.context.get("ui-settings")
|
||||||
@@ -85,12 +90,6 @@ class MainFrameView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
panel( constraints: BorderLayout.EAST) {
|
panel( constraints: BorderLayout.EAST) {
|
||||||
panel {
|
|
||||||
buttonGroup(id : "searchButtonGroup")
|
|
||||||
radioButton(text : "Keywords", selected : true, buttonGroup : searchButtonGroup, keywordSearchAction)
|
|
||||||
radioButton(text : "Hash", selected : false, buttonGroup : searchButtonGroup, hashSearchAction)
|
|
||||||
|
|
||||||
}
|
|
||||||
button(text: "Search", searchAction)
|
button(text: "Search", searchAction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,7 +186,13 @@ class MainFrameView {
|
|||||||
scrollPane(constraints : BorderLayout.CENTER) {
|
scrollPane(constraints : BorderLayout.CENTER) {
|
||||||
table(id : "connections-table") {
|
table(id : "connections-table") {
|
||||||
tableModel(list : model.connectionList) {
|
tableModel(list : model.connectionList) {
|
||||||
closureColumn(header : "Destination", type: String, read : { row -> row.toBase32() })
|
closureColumn(header : "Destination", preferredWidth: 250, type: String, read : { row -> row.destination.toBase32() })
|
||||||
|
closureColumn(header : "Direction", preferredWidth: 20, type: String, read : { row ->
|
||||||
|
if (row.incoming)
|
||||||
|
return "In"
|
||||||
|
else
|
||||||
|
return "Out"
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -299,15 +304,21 @@ class MainFrameView {
|
|||||||
downloadsTable.setDefaultRenderer(Integer.class, centerRenderer)
|
downloadsTable.setDefaultRenderer(Integer.class, centerRenderer)
|
||||||
|
|
||||||
downloadsTable.rowSorter.addRowSorterListener({evt -> lastDownloadSortEvent = evt})
|
downloadsTable.rowSorter.addRowSorterListener({evt -> lastDownloadSortEvent = evt})
|
||||||
|
downloadsTable.rowSorter.setSortsOnUpdates(true)
|
||||||
|
|
||||||
// shared files table
|
// shared files table
|
||||||
def sharedFilesTable = builder.getVariable("shared-files-table")
|
def sharedFilesTable = builder.getVariable("shared-files-table")
|
||||||
sharedFilesTable.columnModel.getColumn(1).setCellRenderer(new SizeRenderer())
|
sharedFilesTable.columnModel.getColumn(1).setCellRenderer(new SizeRenderer())
|
||||||
|
|
||||||
|
sharedFilesTable.rowSorter.addRowSorterListener({evt -> lastSharedSortEvent = evt})
|
||||||
|
sharedFilesTable.rowSorter.setSortsOnUpdates(true)
|
||||||
|
|
||||||
JPopupMenu sharedFilesMenu = new JPopupMenu()
|
JPopupMenu sharedFilesMenu = new JPopupMenu()
|
||||||
JMenuItem unshareSelectedFiles = new JMenuItem("Unshare selected files")
|
// JMenuItem unshareSelectedFiles = new JMenuItem("Unshare selected files")
|
||||||
unshareSelectedFiles.addActionListener({mvcGroup.controller.unshareSelectedFiles()})
|
// unshareSelectedFiles.addActionListener({mvcGroup.controller.unshareSelectedFiles()})
|
||||||
sharedFilesMenu.add(unshareSelectedFiles)
|
JMenuItem copyHashToClipboard = new JMenuItem("Copy hash to clipboard")
|
||||||
|
copyHashToClipboard.addActionListener({mvcGroup.view.copyHashToClipboard(sharedFilesTable)})
|
||||||
|
sharedFilesMenu.add(copyHashToClipboard)
|
||||||
sharedFilesTable.addMouseListener(new MouseAdapter() {
|
sharedFilesTable.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void mouseReleased(MouseEvent e) {
|
public void mouseReleased(MouseEvent e) {
|
||||||
@@ -320,11 +331,53 @@ class MainFrameView {
|
|||||||
showPopupMenu(sharedFilesMenu, e)
|
showPopupMenu(sharedFilesMenu, e)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// searches table
|
||||||
|
def searchesTable = builder.getVariable("searches-table")
|
||||||
|
JPopupMenu searchTableMenu = new JPopupMenu()
|
||||||
|
JMenuItem copySearchToClipboard = new JMenuItem("Copy search to clipboard")
|
||||||
|
copySearchToClipboard.addActionListener({mvcGroup.view.copySearchToClipboard(searchesTable)})
|
||||||
|
searchTableMenu.add(copySearchToClipboard)
|
||||||
|
searchesTable.addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseReleased(MouseEvent e) {
|
||||||
|
if (e.isPopupTrigger())
|
||||||
|
showPopupMenu(searchTableMenu, e)
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
if (e.isPopupTrigger())
|
||||||
|
showPopupMenu(searchTableMenu, e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def showPopupMenu(JPopupMenu menu, MouseEvent event) {
|
def showPopupMenu(JPopupMenu menu, MouseEvent event) {
|
||||||
menu.show(event.getComponent(), event.getX(), event.getY())
|
menu.show(event.getComponent(), event.getX(), event.getY())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def copyHashToClipboard(JTable sharedFilesTable) {
|
||||||
|
int selected = sharedFilesTable.getSelectedRow()
|
||||||
|
if (selected < 0)
|
||||||
|
return
|
||||||
|
if (lastSharedSortEvent != null)
|
||||||
|
selected = sharedFilesTable.rowSorter.convertRowIndexToModel(selected)
|
||||||
|
String root = Base64.encode(model.shared[selected].infoHash.getRoot())
|
||||||
|
StringSelection selection = new StringSelection(root)
|
||||||
|
def clipboard = Toolkit.getDefaultToolkit().getSystemClipboard()
|
||||||
|
clipboard.setContents(selection, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
def copySearchToClipboard(JTable searchesTable) {
|
||||||
|
int selected = searchesTable.getSelectedRow()
|
||||||
|
if (selected < 0)
|
||||||
|
return
|
||||||
|
String search = model.searches[selected].search
|
||||||
|
StringSelection selection = new StringSelection(search)
|
||||||
|
def clipboard = Toolkit.getDefaultToolkit().getSystemClipboard()
|
||||||
|
clipboard.setContents(selection, null)
|
||||||
|
}
|
||||||
|
|
||||||
int selectedDownloaderRow() {
|
int selectedDownloaderRow() {
|
||||||
int selected = builder.getVariable("downloads-table").getSelectedRow()
|
int selected = builder.getVariable("downloads-table").getSelectedRow()
|
||||||
|
@@ -43,6 +43,7 @@ class OptionsView {
|
|||||||
def clearCancelledDownloadsCheckbox
|
def clearCancelledDownloadsCheckbox
|
||||||
def clearFinishedDownloadsCheckbox
|
def clearFinishedDownloadsCheckbox
|
||||||
def excludeLocalResultCheckbox
|
def excludeLocalResultCheckbox
|
||||||
|
def showSearchHashesCheckbox
|
||||||
|
|
||||||
def buttonsPanel
|
def buttonsPanel
|
||||||
|
|
||||||
@@ -96,6 +97,8 @@ class OptionsView {
|
|||||||
clearFinishedDownloadsCheckbox = checkBox(selected : bind {model.clearFinishedDownloads}, constraints : gbc(gridx : 1, 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))
|
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))
|
excludeLocalResultCheckbox = checkBox(selected : bind {model.excludeLocalResult}, constraints : gbc(gridx: 1, gridy : 6))
|
||||||
|
label(text : "Show Hash Searches In Monitor", constraints: gbc(gridx:0, gridy:7))
|
||||||
|
showSearchHashesCheckbox = checkBox(selected : bind {model.showSearchHashes}, constraints : gbc(gridx: 1, gridy: 7))
|
||||||
}
|
}
|
||||||
buttonsPanel = builder.panel {
|
buttonsPanel = builder.panel {
|
||||||
gridBagLayout()
|
gridBagLayout()
|
||||||
|
@@ -4,10 +4,13 @@ import griffon.core.artifact.GriffonView
|
|||||||
import griffon.core.mvc.MVCGroup
|
import griffon.core.mvc.MVCGroup
|
||||||
import griffon.inject.MVCMember
|
import griffon.inject.MVCMember
|
||||||
import griffon.metadata.ArtifactProviderFor
|
import griffon.metadata.ArtifactProviderFor
|
||||||
|
import net.i2p.data.Base64
|
||||||
import net.i2p.data.DataHelper
|
import net.i2p.data.DataHelper
|
||||||
|
|
||||||
import javax.swing.JComponent
|
import javax.swing.JComponent
|
||||||
import javax.swing.JLabel
|
import javax.swing.JLabel
|
||||||
|
import javax.swing.JMenuItem
|
||||||
|
import javax.swing.JPopupMenu
|
||||||
import javax.swing.JTable
|
import javax.swing.JTable
|
||||||
import javax.swing.ListSelectionModel
|
import javax.swing.ListSelectionModel
|
||||||
import javax.swing.SwingConstants
|
import javax.swing.SwingConstants
|
||||||
@@ -17,6 +20,8 @@ import com.muwire.core.util.DataUtil
|
|||||||
|
|
||||||
import java.awt.BorderLayout
|
import java.awt.BorderLayout
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
|
import java.awt.Toolkit
|
||||||
|
import java.awt.datatransfer.StringSelection
|
||||||
import java.awt.event.MouseAdapter
|
import java.awt.event.MouseAdapter
|
||||||
import java.awt.event.MouseEvent
|
import java.awt.event.MouseEvent
|
||||||
|
|
||||||
@@ -99,12 +104,27 @@ class SearchTabView {
|
|||||||
resultsTable.rowSorter.addRowSorterListener({ evt -> lastSortEvent = evt})
|
resultsTable.rowSorter.addRowSorterListener({ evt -> lastSortEvent = evt})
|
||||||
resultsTable.rowSorter.setSortsOnUpdates(true)
|
resultsTable.rowSorter.setSortsOnUpdates(true)
|
||||||
|
|
||||||
|
|
||||||
|
JPopupMenu menu = new JPopupMenu()
|
||||||
|
JMenuItem download = new JMenuItem("Download")
|
||||||
|
download.addActionListener({mvcGroup.parentGroup.controller.download()})
|
||||||
|
menu.add(download)
|
||||||
|
JMenuItem copyHashToClipboard = new JMenuItem("Copy hash to clipboard")
|
||||||
|
copyHashToClipboard.addActionListener({mvcGroup.view.copyHashToClipboard()})
|
||||||
|
menu.add(copyHashToClipboard)
|
||||||
resultsTable.addMouseListener(new MouseAdapter() {
|
resultsTable.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void mouseClicked(MouseEvent e) {
|
public void mouseClicked(MouseEvent e) {
|
||||||
if (e.button == MouseEvent.BUTTON1 && e.clickCount == 2)
|
if (e.button == MouseEvent.BUTTON3)
|
||||||
|
showPopupMenu(menu, e)
|
||||||
|
else if (e.button == MouseEvent.BUTTON1 && e.clickCount == 2)
|
||||||
mvcGroup.parentGroup.controller.download()
|
mvcGroup.parentGroup.controller.download()
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public void mouseReleased(MouseEvent e) {
|
||||||
|
if (e.button == MouseEvent.BUTTON3)
|
||||||
|
showPopupMenu(menu, e)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,4 +134,21 @@ class SearchTabView {
|
|||||||
mvcGroup.parentGroup.model.searchButtonsEnabled = false
|
mvcGroup.parentGroup.model.searchButtonsEnabled = false
|
||||||
mvcGroup.destroy()
|
mvcGroup.destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def showPopupMenu(JPopupMenu menu, MouseEvent e) {
|
||||||
|
println "showing popup menu"
|
||||||
|
menu.show(e.getComponent(), e.getX(), e.getY())
|
||||||
|
}
|
||||||
|
|
||||||
|
def copyHashToClipboard() {
|
||||||
|
int selected = resultsTable.getSelectedRow()
|
||||||
|
if (selected < 0)
|
||||||
|
return
|
||||||
|
if (lastSortEvent != null)
|
||||||
|
selected = resultsTable.rowSorter.convertRowIndexToModel(selected)
|
||||||
|
String hash = Base64.encode(model.results[selected].infohash.getRoot())
|
||||||
|
StringSelection selection = new StringSelection(hash)
|
||||||
|
def clipboard = Toolkit.getDefaultToolkit().getSystemClipboard()
|
||||||
|
clipboard.setContents(selection, null)
|
||||||
|
}
|
||||||
}
|
}
|
@@ -8,6 +8,7 @@ class UISettings {
|
|||||||
boolean clearCancelledDownloads
|
boolean clearCancelledDownloads
|
||||||
boolean clearFinishedDownloads
|
boolean clearFinishedDownloads
|
||||||
boolean excludeLocalResult
|
boolean excludeLocalResult
|
||||||
|
boolean showSearchHashes
|
||||||
|
|
||||||
UISettings(Properties props) {
|
UISettings(Properties props) {
|
||||||
lnf = props.getProperty("lnf", "system")
|
lnf = props.getProperty("lnf", "system")
|
||||||
@@ -16,6 +17,7 @@ class UISettings {
|
|||||||
clearCancelledDownloads = Boolean.parseBoolean(props.getProperty("clearCancelledDownloads","false"))
|
clearCancelledDownloads = Boolean.parseBoolean(props.getProperty("clearCancelledDownloads","false"))
|
||||||
clearFinishedDownloads = Boolean.parseBoolean(props.getProperty("clearFinishedDownloads","false"))
|
clearFinishedDownloads = Boolean.parseBoolean(props.getProperty("clearFinishedDownloads","false"))
|
||||||
excludeLocalResult = Boolean.parseBoolean(props.getProperty("excludeLocalResult","false"))
|
excludeLocalResult = Boolean.parseBoolean(props.getProperty("excludeLocalResult","false"))
|
||||||
|
showSearchHashes = Boolean.parseBoolean(props.getProperty("showSearchHashes","false"))
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(OutputStream out) throws IOException {
|
void write(OutputStream out) throws IOException {
|
||||||
@@ -25,6 +27,7 @@ class UISettings {
|
|||||||
props.setProperty("clearCancelledDownloads", String.valueOf(clearCancelledDownloads))
|
props.setProperty("clearCancelledDownloads", String.valueOf(clearCancelledDownloads))
|
||||||
props.setProperty("clearFinishedDownloads", String.valueOf(clearFinishedDownloads))
|
props.setProperty("clearFinishedDownloads", String.valueOf(clearFinishedDownloads))
|
||||||
props.setProperty("excludeLocalResult", String.valueOf(excludeLocalResult))
|
props.setProperty("excludeLocalResult", String.valueOf(excludeLocalResult))
|
||||||
|
props.setProperty("showSearchHashes", String.valueOf(showSearchHashes))
|
||||||
if (font != null)
|
if (font != null)
|
||||||
props.setProperty("font", font)
|
props.setProperty("font", font)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user