Skip to content

Commit

Permalink
Add support for url args in DesktopAction, fix #15 Add expandExecValu…
Browse files Browse the repository at this point in the history
…e for DesktopAction. Update docs, tests and README.md
  • Loading branch information
FreeSlave committed Jul 18, 2020
1 parent e4f8b2a commit 645c0cb
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 34 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ This should start command line application in terminal emulator (will be detecte
Additional application actions are supported too:

dub examples/util.d exec /usr/share/applications/steam.desktop --action=Settings
dub examples/util.d exec /usr/share/applications/qpdfview.desktop --action NonUniqueInstance /path/to/pdf/file

Running of multiple application instances if it does not support handling multiple urls:

Expand All @@ -132,7 +133,7 @@ Open a link with preferred application:

dub examples/util.d open /usr/share/desktop-base/debian-homepage.desktop

Looks up the .desktop file type and executes it if it's an application or opens a link if it's a link.
Look up the .desktop file type and executes it if it's an application or opens a link if it's a link.

dub examples/util.d start /path/to/file.desktop

Expand All @@ -144,7 +145,7 @@ Read basic information about desktop file:

dub examples/util.d read /usr/share/applications/kde4/kate.desktop

When passing base name of desktop file instead of path it's treated like desktop file id and desktop file is searched in system applications paths.
When passing base name of desktop file instead of path it's treated as a desktop file id and desktop file is searched in system applications paths.

dub examples/util.d exec python2.7.desktop
dub examples/util.d exec kde4-kate.desktop
Expand Down Expand Up @@ -188,7 +189,7 @@ Uses the alternative way of starting desktop file. Instead of constructing Deskt
dub examples/fire.d python2.7.desktop
dub examples/fire.d geany.desktop dub.json

Running of multiple application instances if it does not support handling multiple urls:
Running multiple application instances if it does not support handling multiple urls:

dub examples/fire.d leafpad.desktop dub.json README.md

Expand Down
9 changes: 6 additions & 3 deletions examples/util.d
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void main(string[] args)
string action;
string[] appPaths;
getopt(args,
"action", "Action to run", &action,
"action", "Desktop Action to run (only with 'exec' command)", &action,
"appPath", "Path of applications directory", &appPaths
);

Expand Down Expand Up @@ -95,9 +95,12 @@ void main(string[] args)
if (action.length) {
auto desktopAction = df.action(action);
if (desktopAction is null) {
stderr.writefln("No such action %s", action);
stderr.writefln("No such action (%s)", action);
} else {
desktopAction.start();
string[] urls = args[3..$];
string[] actionArgs = desktopAction.expandExecValue(urls, locale);
writefln("Exec: %(%s %)", actionArgs);
desktopAction.start(urls, locale);
}
} else {
string[] urls = args[3..$];
Expand Down
64 changes: 47 additions & 17 deletions source/desktopfile/file.d
Original file line number Diff line number Diff line change
Expand Up @@ -76,21 +76,44 @@ public:
}

/**
* Start this action.
* Expand "Exec" value into the array of command line arguments to use to start the action.
* It applies unquoting and unescaping.
* See_Also: $(D execValue), $(D desktopfile.utils.expandExecArgs), $(D start)
*/
@safe string[] expandExecValue(in string[] urls = null, string locale = null) const
{
return expandExecArgs(unquoteExec(execValue()), urls, localizedIconName(locale), localizedDisplayName(locale));
}

/**
* Start this action with provided urls.
* Throws:
* $(B ProcessException) on failure to start the process.
* $(D desktopfile.utils.DesktopExecException) if exec string is invalid.
* See_Also: $(D execValue)
* See_Also: $(D execValue), $(D desktopfile.utils.spawnApplication)
*/
@safe void start(string locale = null) const {
@safe void start(in string[] urls, string locale = null) const
{
auto unquotedArgs = unquoteExec(execValue());

SpawnParams params;
params.urls = urls;
params.iconName = localizedIconName(locale);
params.displayName = localizedDisplayName(locale);

return spawnApplication(unquotedArgs, params);
}

/// ditto, but using a single url
@safe void start(string url, string locale) const
{
return start([url], locale);
}

/// ditto, but without any urls.
@safe void start(string locale = null) const {
return start(string[].init, locale);
}
}

/**
Expand Down Expand Up @@ -722,6 +745,7 @@ public:
Key=Value
Actions=Action1;
[Desktop Action Action1]
Name=Action1 Name
Key=Value`;

alias DesktopFile.DesktopReadOptions DesktopReadOptions;
Expand Down Expand Up @@ -852,21 +876,21 @@ public:
}

/**
* Constructs DesktopFile with "Desktop Entry" group and Version set to 1.0
* Constructs DesktopFile with "Desktop Entry" group and Version set to 1.1
*/
@safe this() {
super();
_desktopEntry = new DesktopEntry();
insertGroup(_desktopEntry);
_desktopEntry.setEscapedValue("Version", "1.0");
_desktopEntry.setEscapedValue("Version", "1.1");
}

///
unittest
{
auto df = new DesktopFile();
assert(df.desktopEntry());
assert(df.desktopEntry().escapedValue("Version") == "1.0");
assert(df.desktopEntry().escapedValue("Version") == "1.1");
assert(df.categories().empty);
assert(df.type() == DesktopFile.Type.Unknown);
}
Expand Down Expand Up @@ -919,7 +943,7 @@ public:

static if (isFreedesktop) {
/**
* See $(LINK2 http://standards.freedesktop.org/desktop-entry-spec/latest/ape.html, Desktop File ID)
* See $(LINK2 https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s02.html#desktop-file-id, Desktop File ID)
* Returns: Desktop file ID or empty string if file does not have an ID.
* Note: This function retrieves applications paths each time it's called and therefore can impact performance. To avoid this issue use overload with argument.
* See_Also: $(D desktopfile.paths.applicationsPaths), $(D desktopfile.utils.desktopId)
Expand All @@ -941,7 +965,7 @@ public:
}

/**
* See $(LINK2 http://standards.freedesktop.org/desktop-entry-spec/latest/ape.html, Desktop File ID)
* See $(LINK2 https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s02.html#desktop-file-id, Desktop File ID)
* Params:
* appPaths = range of base application paths to check if this file belongs to one of them.
* Returns: Desktop file ID or empty string if file does not have an ID.
Expand Down Expand Up @@ -1077,7 +1101,7 @@ Type=Directory`;
}

/**
* Get $(LINK2 http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s10.html, additional application action) by name.
* Get $(LINK2 https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s11.html, additional application action) by name.
* Returns: $(D DesktopAction) with given action name or null if not found or found section does not have a name.
* See_Also: $(D actions), $(D byAction)
*/
Expand Down Expand Up @@ -1144,14 +1168,14 @@ Icon[ru]=folder_ru`;
* If the program should be run in terminal it tries to find system defined terminal emulator to run in.
* Params:
* urls = urls application will start with.
* locale = locale that may be needed to be placed in urls if Exec value has %c code.
* terminalCommand = preferable terminal emulator command. If not set then terminal is determined via getTerminalCommand.
* locale = locale that may be needed to be placed in params if Exec value has %c code.
* terminalCommand = preferable terminal emulator command. If not set then terminal is determined via $(D desktopfile.utils.getTerminalCommand).
* Note:
* This function does not check if the type of desktop file is Application. It relies only on "Exec" value.
* Throws:
* ProcessException on failure to start the process.
* $(B ProcessException) on failure to start the process.
* $(D desktopfile.utils.DesktopExecException) if exec string is invalid.
* See_Also: $(D desktopfile.utils.getTerminalCommand), $(D start), $(D expandExecValue)
* See_Also: $(D desktopfile.utils.spawnApplication), $(D desktopfile.utils.getTerminalCommand), $(D start), $(D expandExecValue)
*/
@trusted void startApplication(in string[] urls = null, string locale = null, lazy const(string)[] terminalCommand = getTerminalCommand) const
{
Expand Down Expand Up @@ -1201,8 +1225,8 @@ Icon[ru]=folder_ru`;
* Note:
* This function does not check if the type of desktop file is Link. It relies only on "URL" value.
* Throws:
* ProcessException on failure to start the process.
* Exception if desktop file does not define URL or it's empty.
* $(B ProcessException) on failure to start the process.
* $(B Exception) if desktop file does not define URL or it's empty.
* See_Also: $(D start)
*/
@trusted void startLink() const {
Expand All @@ -1221,8 +1245,10 @@ Icon[ru]=folder_ru`;
/**
* Starts application or open link depending on desktop entry type.
* Throws:
* ProcessException on failure to start the process.
* Exception if type is $(D DesktopEntry.Type.Unknown) or $(D DesktopEntry.Type.Directory).
* $(B ProcessException) on failure to start the process.
* $(D desktopfile.utils.DesktopExecException) if type is $(D DesktopEntry.Type.Application) and the exec string is invalid.
* $(B Exception) if type is $(D DesktopEntry.Type.Unknown) or $(D DesktopEntry.Type.Directory),
* or if type is $(D DesktopEntry.Type.Link), but no url provided.
* See_Also: $(D startApplication), $(D startLink)
*/
@trusted void start() const
Expand Down Expand Up @@ -1339,6 +1365,10 @@ Name=Notspecified Action`;
tuple(desktopAction.displayName(), desktopAction.localizedDisplayName("ru"), desktopAction.iconName(), desktopAction.execValue())),
[tuple("Open directory", "Открыть папку", "open", "doublecmd %u"), tuple("Settings", "Настройки", "edit", "doublecmd settings")]));

DesktopAction desktopAction = df.action("OpenDirectory");
assert(desktopAction !is null);
assert(desktopAction.expandExecValue(["path/to/file"]) == ["doublecmd", "path/to/file"]);

assert(df.action("NotPresented") is null);
assert(df.action("Notspecified") is null);
assert(df.action("X-NoName") is null);
Expand Down
18 changes: 7 additions & 11 deletions source/desktopfile/utils.d
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ class DesktopExecException : Exception
}

/**
* Parameters for spawnApplication.
* Parameters for $(D spawnApplication).
*/
struct SpawnParams
{
/// Urls of file paths to open
/// Urls or file paths to open
const(string)[] urls;

/// Icon to use in place of %i field code.
Expand Down Expand Up @@ -212,7 +212,7 @@ private @trusted string escapeQuotedArgument(string value) pure {
* Note:
* Although Desktop Entry Specification says that arguments must be quoted by double quote, for compatibility reasons this implementation also recognizes single quotes.
* See_Also:
* $(LINK2 http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html, specification)
* $(LINK2 https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s07.html, specification)
*/
@trusted auto unquoteExec(string unescapedValue) pure
{
Expand Down Expand Up @@ -850,8 +850,6 @@ struct FireOptions
bool allowMultipleInstances = true;
}

deprecated("Use FireOptions") alias FireOptions ShootOptions;

package bool readDesktopEntryValues(IniLikeReader)(IniLikeReader reader, string locale, string fileName,
out string iconName, out string name,
out string execValue, out string url,
Expand Down Expand Up @@ -927,7 +925,7 @@ unittest
* $(B ProcessException) on failure to start the process.
* $(D DesktopExecException) if exec string is invalid.
* $(B Exception) on other errors.
* See_Also: $(D FireOptions)
* See_Also: $(D FireOptions), $(D spawnApplication), $(D getTerminalCommand)
*/
void fireDesktopFile(IniLikeReader)(IniLikeReader reader, string fileName = null, FireOptions options = FireOptions.init)
{
Expand Down Expand Up @@ -1057,10 +1055,8 @@ unittest
fireDesktopFile(iniLikeFileReader(fileName), fileName, options);
}

deprecated("Use fireDesktopFile") alias fireDesktopFile shootDesktopFile;

/**
* See $(LINK2 http://standards.freedesktop.org/desktop-entry-spec/latest/ape.html, Desktop File ID)
* See $(LINK2 https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s02.html#desktop-file-id, Desktop File ID)
* Params:
* fileName = Desktop file.
* appsPaths = Range of base application paths.
Expand Down Expand Up @@ -1117,7 +1113,7 @@ unittest
static if (isFreedesktop)
{
/**
* See $(LINK2 http://standards.freedesktop.org/desktop-entry-spec/latest/ape.html, Desktop File ID)
* See $(LINK2 https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s02.html#desktop-file-id, Desktop File ID)
* Returns: Desktop file ID or empty string if file does not have an ID.
* Params:
* fileName = Desktop file.
Expand Down Expand Up @@ -1191,7 +1187,7 @@ static if (isFreedesktop)
*
* This is not actually part of Desktop File Specification but many desktop envrionments have this concept.
* The trusted .desktop file is a file the current user has executable access on or the owner of which is root.
* This function should be applicable only to desktop files of $(D DesktopEntry.Type.Application) type.
* This function should be applicable only to desktop files of $(D desktopfile.file.DesktopEntry.Type.Application) type.
* Note: Always returns true on non-posix systems.
*/
@trusted bool isTrusted(string appFileName) nothrow
Expand Down

0 comments on commit 645c0cb

Please sign in to comment.