Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ Exec=gnome-disk-image-mounter %f
- `Name`: Text displayed in the right click context menu.
- `MimeType`: Mimetype(s) of files it should be shown for.
- `Exec`: Command to execute. [More info](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables).
- `Submenu`: Optional menu path. Contracts that share the same value are grouped under that submenu by the client. `/` separates nested labels (`Archive/Compress`). A missing or empty value keeps the item at the top level. Localized keys work the same way as `Name` (`Submenu[pt_BR]=Arquivo`). The Contractor service returns the translated path; the client draws the submenu and hides a group when none of its contracts match the current files.

`Submenu` is available from `list_all_contract_entries` and `get_contract_entries_by_mimelist_and_file_size`. The older methods still return a flat list without this field.

Tips:
- Use `pkexec` to ask for root permissions. Example: `Exec=pkexec chmod +x %U`
Expand Down
21 changes: 21 additions & 0 deletions src/Contract.vala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ namespace Contractor {
public string name { get; private set; }
public string icon { get; private set; default = ""; }
public string description { get; private set; default = ""; }
/**
* optional submenu path displayed by clients. Empty means a top-level
* item. '/' separates nested menu labels.
*/
public string submenu { get; private set; default = ""; }
/**
* the maximal file size a file or list of files are allowed to have to
* be applicable for this contract
Expand Down Expand Up @@ -101,6 +106,20 @@ namespace Contractor {
};
}

/**
* creates and returns a ContractMenuEntry filled with data from this
* Contract, including the optional submenu path
*/
public ContractMenuEntry get_menu_entry () {
return ContractMenuEntry () {
id = id,
name = name,
description = description,
icon = icon,
submenu = submenu
};
}

/**
* loads mandatory fields from the key file
*/
Expand Down Expand Up @@ -132,6 +151,8 @@ namespace Contractor {
} catch (Error err) {
debug ("Contract '%s' does not provide a max file size (%s)", id, err.message);
}

submenu = keyfile.get_submenu ();
}
}
}
18 changes: 18 additions & 0 deletions src/ContractKeyFile.vala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class Contractor.ContractKeyFile : Object {
private const string ICON_KEY = KeyFileDesktop.KEY_ICON;
private const string MIMETYPE_KEY = KeyFileDesktop.KEY_MIME_TYPE;
private const string MAX_FILE_SIZE_KEY = "MaxFileSize";
private const string SUBMENU_KEY = "Submenu";
private const string EXEC_KEY = KeyFileDesktop.KEY_EXEC;
private const string TRY_EXEC_KEY = KeyFileDesktop.KEY_TRY_EXEC;

Expand Down Expand Up @@ -90,6 +91,23 @@ public class Contractor.ContractKeyFile : Object {
return get_locale_string (DESCRIPTION_KEY);
}

/**
* gets the optional submenu path from the key file
*
* A missing or empty key means the contract stays at the top level.
* '/' separates nested menu labels. The returned string is localized
* the same way as the contract name.
*
* @return the submenu path, or an empty string when unset
*/
public string get_submenu () {
try {
return get_locale_string (SUBMENU_KEY);
} catch (Error err) {
return "";
}
}

/**
* gets the contracts icon from the key file
*
Expand Down
47 changes: 47 additions & 0 deletions src/DBusService.vala
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,37 @@ namespace Contractor {
return convert_to_generic_contracts (contracts);
}

/**
* Gets every contract, including the optional submenu path.
* Clients group entries that share the same submenu string.
* An empty submenu means a top-level item.
*
* @return an array of ContractMenuEntry containing all contracts
*/
public ContractMenuEntry[] list_all_contract_entries () {
var contracts = contract_source.get_contracts ();
return convert_to_contract_menu_entries (contracts);
}

/**
* Gets contracts filtered by MIME types and file size, including the
* optional submenu path.
*
* Pass file_size -1 to skip the file size filter. A submenu is only
* relevant for contracts that pass these filters; clients should group
* the returned entries and omit empty groups.
*
* @param mime_types an array of MIME type strings, e.g. text, image
* @param file_size the file size in bytes, or -1 for any size
*
* @return an array of ContractMenuEntry
*/
public ContractMenuEntry[] get_contract_entries_by_mimelist_and_file_size (string[] mime_types, int64 file_size) throws Error {
var all_contracts = contract_source.get_contracts ();
var contracts = ContractMatcher.get_contracts_for_types_and_file_size (mime_types, file_size, all_contracts);
return convert_to_contract_menu_entries (contracts);
}

/**
* Converts a Collection of Contracts into an array of
* GenericContracts.
Expand All @@ -167,5 +198,21 @@ namespace Contractor {

return generic_contracts;
}

/**
* Converts a Collection of Contracts into an array of
* ContractMenuEntry values, including each submenu path.
*
* @return an array of ContractMenuEntry
*/
private static ContractMenuEntry[] convert_to_contract_menu_entries (Gee.Collection<Contract> contracts) {
var entries = new ContractMenuEntry[0];

foreach (var contract in contracts) {
entries += contract.get_menu_entry ();
}

return entries;
}
}
}
12 changes: 12 additions & 0 deletions src/GenericContract.vala
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,15 @@ public struct Contractor.GenericContract {
string description;
string icon;
}

/**
* A contract exposed to clients that can group actions into submenus.
* submenu is empty for a top-level item. '/' separates nested labels.
*/
public struct Contractor.ContractMenuEntry {
string id;
string name;
string description;
string icon;
string submenu;
}