Compare commits

...

22 Commits

Author SHA1 Message Date
Zlatin Balevsky
b412f9fb0c Release 0.5.3 2019-10-23 09:01:19 +01:00
Zlatin Balevsky
b24d04811d set apple quit strategy 2019-10-23 08:55:10 +01:00
Zlatin Balevsky
771f645df0 proper close 2019-10-23 08:48:53 +01:00
Zlatin Balevsky
b6483ad0f4 add an exit menu 2019-10-23 08:45:03 +01:00
Zlatin Balevsky
decb72c8ef show a warning that MW will continue running 2019-10-23 08:31:23 +01:00
Zlatin Balevsky
439b3bf18b fixes 2019-10-23 06:46:20 +01:00
Zlatin Balevsky
06679ffee0 only show MW if the core has loaded 2019-10-23 06:39:25 +01:00
Zlatin Balevsky
1d5b12e2d7 if core is not initialized, just shutdown 2019-10-23 06:31:08 +01:00
Zlatin Balevsky
4e6e1b6f5b Do not show warnings if core is already shutting down 2019-10-23 06:15:34 +01:00
Zlatin Balevsky
f0b5361d7b smaller icon 2019-10-23 06:06:37 +01:00
Zlatin Balevsky
e0c6bfbf51 show the clsoing window if tray is disabled 2019-10-23 06:01:21 +01:00
Zlatin Balevsky
2a0ecd8a47 fix constructor 2019-10-23 05:48:14 +01:00
Zlatin Balevsky
fb1804e849 Use explicit event to shutdown the application. This fixes closing on Linux 2019-10-23 05:45:50 +01:00
Zlatin Balevsky
d4eaa0df8d do not shutdown core on awt thread 2019-10-22 23:37:44 +01:00
Zlatin Balevsky
ffde6ac86f show a window while MW is shutting down 2019-10-22 23:26:54 +01:00
Zlatin Balevsky
7ad677ead2 add an explicit menu to show MW 2019-10-22 21:48:51 +01:00
Zlatin Balevsky
ddb0568aab do not auto-shutdown 2019-10-22 21:40:47 +01:00
Zlatin Balevsky
ff50a84a48 try to get a tray icon working 2019-10-22 21:34:50 +01:00
Zlatin Balevsky
770396ba41 update test 2019-10-22 10:31:28 +01:00
Zlatin Balevsky
b55852e993 typo 2019-10-22 10:16:41 +01:00
Zlatin Balevsky
a6945275a4 i2p 0.9.43 2019-10-22 08:27:08 +01:00
Zlatin Balevsky
7241809e55 update readme 2019-10-22 00:42:18 +01:00
19 changed files with 298 additions and 11 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.4.15 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
The current stable release - 0.5.2 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
### Building

View File

@@ -35,7 +35,7 @@ class Cli {
Core core
try {
core = new Core(props, home, "0.5.2")
core = new Core(props, home, "0.5.3")
} catch (Exception bad) {
bad.printStackTrace(System.out)
println "Failed to initialize core, exiting"

View File

@@ -53,7 +53,7 @@ class CliDownloader {
Core core
try {
core = new Core(props, home, "0.5.2")
core = new Core(props, home, "0.5.3")
} catch (Exception bad) {
bad.printStackTrace(System.out)
println "Failed to initialize core, exiting"

View File

@@ -375,7 +375,7 @@ public class Core {
}
}
Core core = new Core(props, home, "0.5.2")
Core core = new Core(props, home, "0.5.3")
core.startServices()
// ... at the end, sleep or execute script

View File

@@ -95,7 +95,7 @@ class ConnectionAcceptorTest {
connectionEstablisher = connectionEstablisherMock.proxyInstance()
acceptor = new ConnectionAcceptor(eventBus, connectionManager, settings, i2pAcceptor,
hostCache, trustService, searchManager, uploadManager, connectionEstablisher)
hostCache, trustService, searchManager, uploadManager, null, connectionEstablisher)
acceptor.start()
Thread.sleep(100)
}

View File

@@ -1,6 +1,6 @@
group = com.muwire
version = 0.5.2
i2pVersion = 0.9.42
version = 0.5.3
i2pVersion = 0.9.43
groovyVersion = 2.4.15
slf4jVersion = 1.7.25
spockVersion = 1.1-groovy-2.4

View File

@@ -1,8 +1,8 @@
application {
title = 'MuWire'
startupGroups = ['EventList', 'MainFrame']
autoShutdown = true
startupGroups = ['EventList', 'MainFrame', 'ShutdownWindow']
autoShutdown = false
}
mvcGroups {
@@ -16,6 +16,11 @@ mvcGroups {
view = 'com.muwire.gui.MainFrameView'
controller = 'com.muwire.gui.MainFrameController'
}
'ShutdownWindow' {
model = 'com.muwire.gui.ShutdownWindowModel'
view = 'com.muwire.gui.ShutdownWindowView'
controller = 'com.muwire.gui.ShutdownWindowController'
}
'SearchTab' {
model = 'com.muwire.gui.SearchTabModel'
view = 'com.muwire.gui.SearchTabView'
@@ -61,4 +66,9 @@ mvcGroups {
view = 'com.muwire.gui.BrowseView'
controller = 'com.muwire.gui.BrowseController'
}
'close-warning' {
model = 'com.muwire.gui.CloseWarningModel'
view = 'com.muwire.gui.CloseWarningView'
controller = 'com.muwire.gui.CloseWarningController'
}
}

View File

@@ -0,0 +1,39 @@
package com.muwire.gui
import griffon.core.artifact.GriffonController
import griffon.core.controller.ControllerAction
import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor
import javax.annotation.Nonnull
@ArtifactProviderFor(GriffonController)
class CloseWarningController {
@MVCMember @Nonnull
CloseWarningModel model
@MVCMember @Nonnull
CloseWarningView view
UISettings settings
File home
void mvcGroupInit(Map<String, String> args) {
model.closeWarning = settings.closeWarning
}
@ControllerAction
void close() {
boolean showWarning = !view.checkbox.model.isSelected()
model.closeWarning = showWarning
settings.closeWarning = showWarning
File props = new File(home, "gui.properties")
props.withOutputStream {
settings.write(it)
}
view.dialog.setVisible(false)
view.mainFrame.setVisible(false)
mvcGroup.destroy()
}
}

View File

@@ -0,0 +1,11 @@
package com.muwire.gui
import griffon.core.artifact.GriffonController
import griffon.core.controller.ControllerAction
import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor
import javax.annotation.Nonnull
@ArtifactProviderFor(GriffonController)
class ShutdownWindowController {
}

View File

@@ -10,7 +10,9 @@ import com.muwire.gui.UISettings
import javax.annotation.Nonnull
import javax.inject.Inject
import javax.swing.ImageIcon
import javax.swing.JLabel
import javax.swing.JPopupMenu
import javax.swing.JTable
import javax.swing.LookAndFeel
import javax.swing.UIManager
@@ -20,7 +22,11 @@ import static griffon.util.GriffonApplicationUtils.isMacOSX
import static groovy.swing.SwingBuilder.lookAndFeel
import java.awt.Font
import java.awt.MenuItem
import java.awt.PopupMenu
import java.awt.SystemTray
import java.awt.Toolkit
import java.awt.TrayIcon
import java.util.logging.Level
import java.util.logging.LogManager
@@ -43,6 +49,56 @@ class Initialize extends AbstractLifecycleHandler {
}
}
System.setProperty("apple.eawt.quitStrategy", "CLOSE_ALL_WINDOWS");
if (SystemTray.isSupported() && (SystemVersion.isMac() || SystemVersion.isWindows())) {
try {
def tray = SystemTray.getSystemTray()
def url = Initialize.class.getResource("/MuWire-16x16.png")
def image = new ImageIcon(url, "tray icon").getImage()
def popupMenu = new PopupMenu()
def trayIcon = new TrayIcon(image, "MuWire", popupMenu)
def exit = new MenuItem("Exit")
exit.addActionListener({
application.getWindowManager().findWindow("main-frame").setVisible(false)
application.getWindowManager().findWindow("shutdown-window").setVisible(true)
Core core = application.getContext().get("core")
if (core != null) {
Thread t = new Thread({
core.shutdown()
application.shutdown()
}as Runnable)
t.start()
} else
application.shutdown()
tray.remove(trayIcon)
})
def showMW = {e ->
def mainFrame = application.getWindowManager().findWindow("main-frame")
if (mainFrame != null) {
Core core = application.getContext().get("core")
if (core != null)
mainFrame.setVisible(true)
}
}
def show = new MenuItem("Open MuWire")
show.addActionListener(showMW)
popupMenu.add(show)
popupMenu.add(exit)
tray.add(trayIcon)
trayIcon.addActionListener(showMW)
application.getContext().put("tray-icon", true)
} catch (Exception bad) {
log.log(Level.WARNING,"couldn't set tray icon",bad)
}
}
log.info "Loading home dir"
def portableHome = System.getProperty("portable.home")
def home = portableHome == null ?

View File

@@ -20,6 +20,9 @@ class Shutdown extends AbstractLifecycleHandler {
void execute() {
log.info("shutting down")
Core core = application.context.get("core")
core.shutdown()
if (core != null) {
Thread t = new Thread({ core.shutdown() } as Runnable)
t.start()
}
}
}

View File

@@ -0,0 +1,15 @@
package com.muwire.gui
import javax.annotation.Nonnull
import griffon.core.artifact.GriffonController
import griffon.core.artifact.GriffonModel
import griffon.core.controller.ControllerAction
import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor
import javax.annotation.Nonnull
@ArtifactProviderFor(GriffonModel)
class CloseWarningModel {
@griffon.transform.Observable boolean closeWarning
}

View File

@@ -499,6 +499,8 @@ class MainFrameModel {
}
void onRouterDisconnectedEvent(RouterDisconnectedEvent e) {
if (core.getShutdown().get())
return
runInsideUIAsync {
JOptionPane.showMessageDialog(null, "MuWire lost connection to the I2P router and will now exit.",
"Connection to I2P router lost", JOptionPane.WARNING_MESSAGE)

View File

@@ -0,0 +1,9 @@
package com.muwire.gui
import griffon.core.artifact.GriffonModel
import griffon.transform.Observable
import griffon.metadata.ArtifactProviderFor
@ArtifactProviderFor(GriffonModel)
class ShutdownWindowModel {
}

View File

@@ -0,0 +1,58 @@
package com.muwire.gui
import griffon.core.artifact.GriffonView
import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor
import javax.swing.JDialog
import javax.swing.SwingConstants
import java.awt.GridBagConstraints
import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent
import javax.annotation.Nonnull
@ArtifactProviderFor(GriffonView)
class CloseWarningView {
@MVCMember @Nonnull
FactoryBuilderSupport builder
@MVCMember @Nonnull
CloseWarningModel model
def mainFrame
def dialog
def panel
def checkbox
void initUI() {
mainFrame = application.windowManager.findWindow("main-frame")
dialog = new JDialog(mainFrame, "MuWire will continue running", true)
panel = builder.panel {
gridBagLayout()
label(text : "MuWire will continue running. You can close it from the system tray", constraints : gbc(gridx: 0, gridy: 0, gridwidth : 2))
label(text : "\n", constraints : gbc(gridx : 0, gridy : 1)) // TODO: real padding
label(text : "Do not show this warning again", constraints : gbc(gridx: 0, gridy : 2, weightx: 100, anchor : GridBagConstraints.LINE_END))
checkbox = checkBox(selected : bind {model.closeWarning}, constraints : gbc(gridx: 1, gridy :2))
panel (constraints : gbc(gridx: 0, gridy : 3, gridwidth : 2)) {
button(text : "Ok", closeAction)
}
}
dialog.getContentPane().add(panel)
dialog.pack()
dialog.setResizable(false)
dialog.setLocationRelativeTo(mainFrame)
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
dialog.addWindowListener(new WindowAdapter() {
public void windowClosed(WindowEvent e) {
mainFrame.setVisible(false)
mvcGroup.destroy()
}
})
}
void mvcGroupInit(Map<String,String> args) {
dialog.show()
}
}

View File

@@ -1,5 +1,6 @@
package com.muwire.gui
import griffon.core.GriffonApplication
import griffon.core.artifact.GriffonView
import griffon.core.env.Metadata
import griffon.inject.MVCMember
@@ -11,6 +12,7 @@ import javax.swing.BorderFactory
import javax.swing.Box
import javax.swing.BoxLayout
import javax.swing.JFileChooser
import javax.swing.JFrame
import javax.swing.JLabel
import javax.swing.JMenuItem
import javax.swing.JPopupMenu
@@ -19,6 +21,7 @@ import javax.swing.JTable
import javax.swing.JTree
import javax.swing.ListSelectionModel
import javax.swing.SwingConstants
import javax.swing.SwingUtilities
import javax.swing.TransferHandler
import javax.swing.border.Border
import javax.swing.table.DefaultTableCellRenderer
@@ -26,6 +29,7 @@ import javax.swing.tree.TreeNode
import javax.swing.tree.TreePath
import com.muwire.core.Constants
import com.muwire.core.Core
import com.muwire.core.MuWireSettings
import com.muwire.core.SharedFile
import com.muwire.core.download.Downloader
@@ -42,6 +46,8 @@ import java.awt.datatransfer.DataFlavor
import java.awt.datatransfer.StringSelection
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent
import java.nio.charset.StandardCharsets
import javax.annotation.Nonnull
@@ -54,6 +60,7 @@ class MainFrameView {
@MVCMember @Nonnull
MainFrameModel model
@Inject @Nonnull GriffonApplication application
@Inject Metadata metadata
def downloadsTable
@@ -68,6 +75,7 @@ class MainFrameView {
builder.with {
application(size : [1024,768], id: 'main-frame',
locationRelativeTo : null,
defaultCloseOperation : JFrame.DO_NOTHING_ON_CLOSE,
title: application.configuration['application.title'] + " " +
metadata["application.version"] + " revision " + metadata["build.revision"],
iconImage: imageIcon('/MuWire-48x48.png').image,
@@ -77,6 +85,9 @@ class MainFrameView {
pack : false,
visible : bind { model.coreInitialized }) {
menuBar {
menu (text : "File") {
menuItem("Exit", actionPerformed : {closeApplication()})
}
menu (text : "Options") {
menuItem("Configuration", actionPerformed : {mvcGroup.createMVCGroup("Options")})
menuItem("Content Control", actionPerformed : {
@@ -432,6 +443,22 @@ class MainFrameView {
true
}
})
mainFrame.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
if (application.getContext().get("tray-icon")) {
if (settings.closeWarning) {
runInsideUIAsync {
Map<String, Object> args2 = new HashMap<>()
args2.put("settings", settings)
args2.put("home", model.core.home)
mvcGroup.createMVCGroup("close-warning", "Close Warning", args2)
}
}
} else {
closeApplication()
}
}})
def downloadsTable = builder.getVariable("downloads-table")
def selectionModel = downloadsTable.getSelectionModel()
@@ -894,4 +921,18 @@ class MainFrameView {
tree.setSelectionPaths(selectedPaths)
builder.getVariable("shared-files-table").model.fireTableDataChanged()
}
private void closeApplication() {
def mainFrame = builder.getVariable("main-frame")
mainFrame.setVisible(false)
application.getWindowManager().findWindow("shutdown-window").setVisible(true)
Core core = application.getContext().get("core")
if (core != null) {
Thread t = new Thread({
core.shutdown()
application.shutdown()
}as Runnable)
t.start()
}
}
}

View File

@@ -188,7 +188,7 @@ class OptionsView {
label(text : "Automatically clear cancelled downloads", constraints: gbc(gridx: 0, gridy:0, anchor : GridBagConstraints.LINE_START, weightx: 100))
clearCancelledDownloadsCheckbox = checkBox(selected : bind {model.clearCancelledDownloads},
constraints : gbc(gridx : 1, gridy:0, anchor : GridBagConstraints.LINE_END))
label(text : "Automatically flear finished downloads", constraints: gbc(gridx: 0, gridy:1, anchor : GridBagConstraints.LINE_START, weightx: 100))
label(text : "Automatically clear finished downloads", constraints: gbc(gridx: 0, gridy:1, anchor : GridBagConstraints.LINE_START, weightx: 100))
clearFinishedDownloadsCheckbox = checkBox(selected : bind {model.clearFinishedDownloads},
constraints : gbc(gridx : 1, gridy:1, anchor : GridBagConstraints.LINE_END))
label(text : "Smooth download speed over (seconds)", constraints : gbc(gridx: 0, gridy : 2, anchor : GridBagConstraints.LINE_START, weightx: 100))

View File

@@ -0,0 +1,40 @@
package com.muwire.gui
import griffon.core.GriffonApplication
import griffon.core.artifact.GriffonView
import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor
import javax.swing.Box
import javax.swing.SwingConstants
import javax.annotation.Nonnull
import javax.inject.Inject
@ArtifactProviderFor(GriffonView)
class ShutdownWindowView {
@MVCMember @Nonnull
FactoryBuilderSupport builder
@MVCMember @Nonnull
ShutdownWindowModel model
void initUI() {
builder.with {
app = application(size: [320, 80], id: 'shutdown-window',
locationRelativeTo : null,
title: application.configuration['application.title'],
iconImage: imageIcon('/MuWire-48x48.png').image,
iconImages: [imageIcon('/MuWire-48x48.png').image,
imageIcon('/MuWire-32x32.png').image,
imageIcon('/MuWire-16x16.png').image],
visible: false ) {
panel {
vbox {
label("MuWire is shutting down, please wait...")
Box.createVerticalGlue()
progressBar(indeterminate : true)
}
}
}
}
}
}

View File

@@ -11,6 +11,7 @@ class UISettings {
boolean clearFinishedDownloads
boolean excludeLocalResult
boolean showSearchHashes
boolean closeWarning
UISettings(Properties props) {
lnf = props.getProperty("lnf", "system")
@@ -22,6 +23,7 @@ class UISettings {
showSearchHashes = Boolean.parseBoolean(props.getProperty("showSearchHashes","true"))
autoFontSize = Boolean.parseBoolean(props.getProperty("autoFontSize","false"))
fontSize = Integer.parseInt(props.getProperty("fontSize","12"))
closeWarning = Boolean.parseBoolean(props.getProperty("closeWarning","true"))
}
void write(OutputStream out) throws IOException {
@@ -34,6 +36,7 @@ class UISettings {
props.setProperty("showSearchHashes", String.valueOf(showSearchHashes))
props.setProperty("autoFontSize", String.valueOf(autoFontSize))
props.setProperty("fontSize", String.valueOf(fontSize))
props.setProperty("closeWarning", String.valueOf(closeWarning))
if (font != null)
props.setProperty("font", font)