Skip to content

Commit

Permalink
Merge branch 'feature/open' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
gscatto committed Apr 16, 2020
2 parents 232dd02 + 7987c8f commit d168958
Show file tree
Hide file tree
Showing 2 changed files with 259 additions and 0 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Freeplane "Desktop Actions" Addon

This add-on provides a script to open or browse files, directories and URIs using the [java.awt.Desktop](https://docs.oracle.com/javase/8/docs/api/java/awt/Desktop.html) class: it looks for the associated application registered on the current platform, and launch it to handle the resource.

## The "Open or browse resource.." action

First, it tries to resolve the text contained in the selected node to an _URI_ resource or a _path_ (be it an _absolute_ or _relative_ referring to a _file_ or a _directory_).

For example, consider the following directory tree:

```
svu.mm
theme.mp3
/images
benson.jpg
stabler.jpg
```

Then, the following scenarios apply for the `svu.mm` map:

- Opening a node containing the text `images/benson.jpg` or `images/stabler.jpg` launches the default image viewer for our viewing pleasure.
- Opening a node containing the text `theme.mp3` instead launches the default media player which plays that audio file, recommended.
- Opening a node containing the text `https://en.wikipedia.org/wiki/Law_%26_Order:_Special_Victims_Unit` launches the default browser and displays the page.
236 changes: 236 additions & 0 deletions scripts/Open.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
// @ExecutionModes({ON_SELECTED_NODE})

/*
Open.groovy
It launches the associated application to open or browse the resource using the java.awt.Desktop class (see https://docs.oracle.com/javase/8/docs/api/java/awt/Desktop). It handles files and directories by their path (being absolute or relative to the directory containing the map file) and URIs.
*/

package giulioscattolin.freeplane.desktopactions

import java.awt.Desktop
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import org.freeplane.plugin.script.FreeplaneScriptBaseClass

class Open extends FreeplaneScriptBaseClass {
def run() {
// Define flags for action support.
def isOpenActionSupportedFlag = false
def isBrowseActionSupportedFlag = false

// Obtain the desktop instance.
def desktop = obtainDesktopInstance()
// If desktop is an invalid reference..
if (desktop == null) {
// Quit.
return
}

// If the OPEN action is supported..
if (isOpenActionSupported(desktop)) {
// Activate the appropriate flag.
isOpenActionSupportedFlag = true

// If the resource has been opened successfully using the content of the selected node as an absolute path..
if (openResourceUsingAbsolutePath(node.text, desktop)) {
// Quit.
return
}

// If the resource has been opened successfully using the content of the selected node as a relative path to the directory containing the map file..
if (openResourceUsingPathRelativeToMapFileDirectory(node.text, desktop)) {
// Quit.
return
}
}
// If the BROWSE action is supported..
if (isBrowseActionSupported(desktop)) {
// Activate the appropriate flag.
isBrowseActionSupportedFlag = true

// If the resource has been opened successfully using the content of the selected node as an URI..
if (openResourceUsingURI(node.text, desktop)) {
// Quit.
return
}
}

if (isOpenActionSupportedFlag && isBrowseActionSupportedFlag) {
// There are no more options, the resource won't be opened.
// Inform the user.
setStatusInfo("The resource cannot be opened because the node does not contain the path of an existent file / directory or a valid URI.")
// Finally, quit.
}

// The user should have already been informed, quit.
}

def obtainDesktopInstance() {
// If the Desktop object is supported..
if (Desktop.isDesktopSupported()) {
// Return the Desktop instance.
return Desktop.getDesktop()
} else {
// Inform the user.
setStatusInfo("The resource cannot be opened because the Desktop class is not supported on the current platform.")
// Finally, report error.
return null
}
}

def isOpenActionSupported(desktop) {
// If the OPEN action is supported..
if (desktop.isSupported(Desktop.Action.OPEN)) {
// Return true
return true
} else {
// Inform the user.
setStatusInfo("The resource cannot be opened because the OPEN action is not supported on the current platform.")
// Finally, return false.
return false
}
}

def isBrowseActionSupported(desktop) {
// If the BROWSE action is supported..
if (desktop.isSupported(Desktop.Action.BROWSE)) {
// Return true
return true
} else {
// Inform the user.
setStatusInfo("The resource cannot be opened because the BROWSE action is not supported on the current platform.")
// Finally, return false.
return false
}
}

def openResourceUsingAbsolutePath(nodeText, desktop) {
// Supposing the content of the selected node is the absolute path of a resource, create a Path instance from its string representation.
def path = Paths.get(nodeText)
// Obtain the associated file instance.
def file = path.toFile()
// If the file exists..
if (file.exists()) {
// Inform the user.
setStatusInfo("Opening the resource using its absolute path..")

// If the file is opened successfully..
if (openFile(file, desktop)) {
// Report success.
return true
} else {
// Report insuccess.
return false
}
} else {
// Report insuccess.
return false
}
}

def openResourceUsingPathRelativeToMapFileDirectory(nodeText, desktop) {
// Supposing the content of the selected node is the path of a resource relative to the directory containing the map file, obtain the parent directory as a file.
def parentDirectory = node.map.file.getParentFile()
// Create the appropriate File instance.
File file = new File(parentDirectory, node.text)
// If the file exists..
if (file.exists()) {
// Inform the user.
setStatusInfo("Opening the resource using the path relative to map file directory..")

// If the file is opened successfully..
if (openFile(file, desktop)) {
// Report success.
return true
} else {
// Report insuccess.
return false
}
} else {
// Report insuccess.
return false
}
}

def openFile(file, desktop) {
// Try to open the file..
try {
desktop.open(file)
} catch (IOException e) {
// If the specified file has no associated application or the associated application fails to be launched, inform the user.
setStatusInfo("The resource cannot be opened because it has no associated application or the associated application failed to be launched.")

// Report insuccess.
return false
} catch (SecurityException e) {
// If a security manager exists and its SecurityManager.checkRead (java.lang.String) method denies read access to the file, or it denies the AWTPermission("showWindowWithoutWarningBanner") permission, or the calling thread is not allowed to create a subprocess.
setStatusInfo("The resource cannot be opened because a security exception has been thrown.")

// Report insuccess.
return false
}

// Inform the user.
setStatusInfo("The resource has been successfully opened!")
// Report success.
return true
}

def openResourceUsingURI(nodeText, desktop) {
// Supposing the content of the selected node is the URI of the resource, tries to create an URI instance.
try {
def uri = new URI(nodeText)

// If the uri is browsed successfully..
if (browseUri(uri, desktop)) {
// Report success.
return true
}
} catch (URISyntaxException e) {
// The given string violates RFC 2396.
// Ignore and continue.
}

// Report insuccess.
return false
}

def browseUri(uri, desktop) {
// Try to open the file..
try {
desktop.browse(uri)
} catch (IOException e) {
// If the user default browser is not found, or it fails to be launched, or the default handler application failed to be launched, inform the user.
setStatusInfo("The resource cannot be opened because the user default browser is not found, or it fails to be launched, or the default handler application failed to be launched.")

// Report insuccess.
return false
} catch (SecurityException e) {
// If a security manager exists and its SecurityManager.checkRead (java.lang.String) method denies read access to the file, or it denies the AWTPermission("showWindowWithoutWarningBanner") permission, or the calling thread is not allowed to create a subprocess.
setStatusInfo("The resource cannot be opened because a security exception has been thrown.")

// Report insuccess.
return false
} catch (IllegalArgumentException e) {
// If the necessary permissions are not available and the URI can not be converted to a URL.
setStatusInfo("The resource cannot be opened because the necessary permissions are not available and the URI can not be converted to an URL.")

// Report insuccess.
return false
}

// Inform the user.
setStatusInfo("The resource has been successfully browsed!")
// Report success.
return true
}

def setStatusInfo(text) {
c.setStatusInfo(text)
}
}

0 comments on commit d168958

Please sign in to comment.