Skip to content

Commit 7be3fbf

Browse files
committed
nemo-action.c: Add Locations and Files fields.
These allow filename and glob matching against the selection's files or their parent: - Glob patterns (*.foo) can be used against filenames (not paths). - Exact filenames as well as full paths can be used for exact matches. See sample action for more behavior info.
1 parent ef4adc0 commit 7be3fbf

2 files changed

Lines changed: 263 additions & 1 deletion

File tree

files/usr/share/nemo/actions/sample.nemo_action

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,55 @@ Extensions=any;
122122
# sftp://joe@10.0.0.200/ matches
123123
# file:///home/joe/.bashrc does not
124124

125-
#UriScheme=file
125+
#UriScheme=file
126+
127+
# Locations - semicolor-separated array of directory names and globs to check against the
128+
# selection's parent location.
129+
#
130+
# If Locations is defined, an action will *only* be valid under one of the following conditions:
131+
# - The current location's filename (foo.bar, no path) is captured by *at least one*
132+
# of any defined glob patterns.
133+
# - The current location's filename exactly matches *at least one* defined filename.
134+
#
135+
# If an entry is prefixed with '!', the above conditions are reversed, and an action is
136+
# considered *invalid* if any matches are made.
137+
#
138+
# NOTE: Allowed patterns are considered before forbidden ones. This means that a location
139+
# would be invalidated by not matching any allowed patterns before being invalidated
140+
# by matching forbidden ones.
141+
#
142+
# Example desired condition: Match any dot-file locations except '.config':
143+
#
144+
# Locations=.*;!.config
145+
#
146+
# Action would be visible when right-clicking the file '.foo/bar', but not '.config/bar'
147+
#
148+
# Optional
149+
150+
#Locations=.*;!.config;
151+
152+
# Files - semicolor-separated array of file names and globs to check against the currently
153+
# selected file list.
154+
#
155+
# If Files is defined, an action will *only* be valid under the following conditions:
156+
# - All selected filenames (foo.bar, no path) are captured by *at least one* of any
157+
# defined glob patterns.
158+
# - All file absolute paths exactly match *at least one* of any defined absolute paths.
159+
# - All filenames exactly match *at least one* of the defined filenames.
160+
#
161+
# If an entry is prefixed with '!', the above conditions are reversed, and an action is
162+
# considered *invalid* if any matches are made.
163+
#
164+
# NOTE: Allowed patterns are considered before forbidden ones. This means that a filename
165+
# would be invalidated by not matching any allowed patterns before being invalidated
166+
# by matching forbidden ones.
167+
#
168+
# Example desired condition: Match .bash* but not '.bashrc':
169+
#
170+
# Files=.bash*;!.bashrc;
171+
#
172+
# Action would be visible when right-clicking the file '.bash_history', but not '.bashrc'
173+
#
174+
# Optional
175+
176+
#Files=.bash*;!.bashrc;

libnemo-private/nemo-action.c

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@
3535
#define g_drive_is_removable g_drive_is_media_removable
3636
#endif
3737

38+
#ifndef GLIB_VERSION_2_70
39+
#define g_pattern_spec_match g_pattern_match
40+
#endif
41+
3842
typedef struct {
3943
SelectionType selection_type;
4044
gchar **extensions;
@@ -58,6 +62,15 @@ typedef struct {
5862
gboolean run_in_terminal;
5963
gchar *uri_scheme;
6064

65+
GList *allowed_location_patterns;
66+
GList *forbidden_location_patterns;
67+
GList *allowed_location_filenames;
68+
GList *forbidden_location_filenames;
69+
70+
GList *allowed_patterns;
71+
GList *forbidden_patterns;
72+
GList *allowed_filenames;
73+
GList *forbidden_filenames;
6174

6275
gboolean constructing;
6376
} NemoActionPrivate;
@@ -547,6 +560,51 @@ strip_custom_modifier (const gchar *raw, gboolean *custom, gchar **out)
547560
}
548561
}
549562

563+
static void
564+
populate_patterns_and_filenames (NemoAction *action,
565+
gchar **array,
566+
GList **allowed_patterns,
567+
GList **forbidden_patterns,
568+
GList **allowed_filenames,
569+
GList **forbidden_filenames)
570+
{
571+
*allowed_patterns = NULL;
572+
*forbidden_patterns = NULL;
573+
*allowed_filenames = NULL;
574+
*forbidden_filenames = NULL;
575+
576+
if (array == NULL) {
577+
return;
578+
}
579+
580+
gint i;
581+
582+
for (i = 0; i < g_strv_length (array); i++) {
583+
const gchar *str = array[i];
584+
585+
if (g_strstr_len (str, -1, "?") || g_strstr_len (str, -1, "*")) {
586+
if (g_str_has_prefix (array[i], "!")) {
587+
*forbidden_patterns = g_list_prepend (*forbidden_patterns, g_pattern_spec_new (array[i] + 1));
588+
} else {
589+
*allowed_patterns = g_list_prepend (*allowed_patterns, g_pattern_spec_new (array[i]));
590+
}
591+
592+
continue;
593+
}
594+
595+
if (g_str_has_prefix (array[i], "!")) {
596+
*forbidden_filenames = g_list_prepend (*forbidden_filenames, g_strdup (array[i] + 1));
597+
} else {
598+
*allowed_filenames = g_list_prepend (*allowed_filenames, g_strdup (array[i]));
599+
}
600+
}
601+
602+
*allowed_patterns = g_list_reverse (*allowed_patterns);
603+
*forbidden_patterns = g_list_reverse (*forbidden_patterns);
604+
*allowed_filenames = g_list_reverse (*allowed_filenames);
605+
*forbidden_filenames = g_list_reverse (*forbidden_filenames);
606+
}
607+
550608
void
551609
nemo_action_constructed (GObject *object)
552610
{
@@ -679,6 +737,32 @@ nemo_action_constructed (GObject *object)
679737
KEY_TERMINAL,
680738
NULL);
681739

740+
gchar **locations = g_key_file_get_string_list (key_file,
741+
ACTION_FILE_GROUP,
742+
KEY_LOCATIONS,
743+
NULL,
744+
NULL);
745+
746+
populate_patterns_and_filenames (action, locations,
747+
&priv->allowed_location_patterns,
748+
&priv->forbidden_location_patterns,
749+
&priv->allowed_location_filenames,
750+
&priv->forbidden_location_filenames);
751+
g_strfreev (locations);
752+
753+
gchar **files = g_key_file_get_string_list (key_file,
754+
ACTION_FILE_GROUP,
755+
KEY_FILES,
756+
NULL,
757+
NULL);
758+
759+
populate_patterns_and_filenames (action, files,
760+
&priv->allowed_patterns,
761+
&priv->forbidden_patterns,
762+
&priv->allowed_filenames,
763+
&priv->forbidden_filenames);
764+
g_strfreev (files);
765+
682766
gboolean is_desktop = FALSE;
683767

684768
if (conditions && condition_count > 0) {
@@ -907,6 +991,14 @@ nemo_action_finalize (GObject *object)
907991
g_list_free_full (priv->dbus, (GDestroyNotify) dbus_condition_free);
908992
g_list_free_full (priv->gsettings, (GDestroyNotify) gsettings_condition_free);
909993

994+
g_list_free_full (priv->allowed_location_patterns, (GDestroyNotify) g_pattern_spec_free);
995+
g_list_free_full (priv->forbidden_location_patterns, (GDestroyNotify) g_pattern_spec_free);
996+
g_list_free_full (priv->allowed_patterns, (GDestroyNotify) g_pattern_spec_free);
997+
g_list_free_full (priv->forbidden_patterns, (GDestroyNotify) g_pattern_spec_free);
998+
g_list_free_full (priv->allowed_location_filenames, (GDestroyNotify) g_free);
999+
g_list_free_full (priv->forbidden_location_filenames, (GDestroyNotify) g_free);
1000+
g_list_free_full (priv->allowed_filenames, (GDestroyNotify) g_free);
1001+
g_list_free_full (priv->forbidden_filenames, (GDestroyNotify) g_free);
9101002

9111003
g_clear_handle_id (&priv->dbus_recalc_timeout_id, g_source_remove);
9121004
g_clear_handle_id (&priv->gsettings_recalc_timeout_id, g_source_remove);
@@ -1548,7 +1640,113 @@ get_is_dir (NemoFile *file)
15481640
return ret;
15491641
}
15501642

1643+
static gboolean
1644+
check_is_allowed (NemoAction *action,
1645+
NemoFile *parent,
1646+
GList *selection,
1647+
GList *allowed_patterns,
1648+
GList *forbidden_patterns,
1649+
GList *allowed_names,
1650+
GList *forbidden_names)
15511651
{
1652+
// If there are no pattern/name specs, always allow the location..
1653+
if ((allowed_patterns == NULL && forbidden_patterns == NULL && allowed_names == NULL && forbidden_names == NULL)) {
1654+
return TRUE;
1655+
}
1656+
1657+
GList *l, *files;
1658+
1659+
if (parent != NULL) {
1660+
DEBUG ("Checking pattern/name matching for location: %s", nemo_file_peek_name (parent));
1661+
files = g_list_prepend (NULL, parent);
1662+
}
1663+
else {
1664+
DEBUG ("Checking pattern/name matching for selected files");
1665+
files = selection;
1666+
}
1667+
1668+
gboolean allowed = TRUE;
1669+
1670+
for (l = files; l != NULL; l = l->next) {
1671+
NemoFile *file = NEMO_FILE (l->data);
1672+
1673+
g_autofree gchar *path = nemo_file_get_path (file);
1674+
// If the first file (or the single parent) isn't native, no need to
1675+
// check all selected items, just exit early.
1676+
if (path == NULL) {
1677+
goto check_allowed_done;
1678+
}
1679+
1680+
const gchar *name = nemo_file_peek_name (file);
1681+
gboolean allowed_allowed = TRUE;
1682+
gboolean forbidden_allowed = TRUE;
1683+
GList *ll;
1684+
1685+
if (allowed_patterns != NULL || allowed_names != NULL) {
1686+
allowed_allowed = FALSE;
1687+
1688+
for (ll = allowed_patterns; ll != NULL; ll = ll->next) {
1689+
if (g_pattern_spec_match ((GPatternSpec *) ll->data, strlen (name), name, NULL)) {
1690+
allowed_allowed = TRUE;
1691+
break;
1692+
}
1693+
}
1694+
1695+
for (ll = allowed_names; ll != NULL; ll = ll->next) {
1696+
gchar *aname = (gchar *) ll->data;
1697+
1698+
if (name[0] == '/' && g_strcmp0 (path, aname) == 0) {
1699+
allowed_allowed = TRUE;
1700+
break;
1701+
}
1702+
else
1703+
if (g_str_has_suffix (name, aname)) {
1704+
allowed_allowed = TRUE;
1705+
break;
1706+
}
1707+
}
1708+
}
1709+
1710+
if (forbidden_patterns != NULL || forbidden_names != NULL) {
1711+
// (forbidden_allowed = TRUE;)
1712+
1713+
for (ll = forbidden_patterns; ll != NULL; ll = ll->next) {
1714+
if (g_pattern_spec_match ((GPatternSpec *) ll->data, strlen (name), name, NULL)) {
1715+
forbidden_allowed = FALSE;
1716+
break;
1717+
}
1718+
}
1719+
1720+
for (ll = forbidden_names; ll != NULL; ll = ll->next) {
1721+
gchar *fname = (gchar *) ll->data;
1722+
if (name[0] == '/' && g_strcmp0 (path, fname) == 0) {
1723+
forbidden_allowed = FALSE;
1724+
break;
1725+
}
1726+
else
1727+
if (g_str_has_suffix (name, fname)) {
1728+
forbidden_allowed = FALSE;
1729+
break;
1730+
}
1731+
}
1732+
}
1733+
1734+
DEBUG ("Final result - allowed pass: %d, forbidden pass: %d", allowed_allowed, forbidden_allowed);
1735+
if (!(allowed_allowed && forbidden_allowed)) {
1736+
allowed = FALSE;
1737+
break;
1738+
}
1739+
}
1740+
1741+
check_allowed_done:
1742+
if (parent != NULL) {
1743+
g_list_free (files);
1744+
}
1745+
1746+
return allowed;
1747+
}
1748+
1749+
15521750
static gboolean
15531751
get_visibility (NemoAction *action,
15541752
GList *selection,
@@ -1568,6 +1766,19 @@ get_visibility (NemoAction *action,
15681766
return FALSE;
15691767
}
15701768

1769+
if (!check_is_allowed (action, parent, NULL,
1770+
priv->allowed_location_patterns,
1771+
priv->forbidden_location_patterns,
1772+
priv->allowed_location_filenames,
1773+
priv->forbidden_location_filenames)) {
1774+
return FALSE;
1775+
}
1776+
1777+
if (!check_is_allowed (action, NULL, selection,
1778+
priv->allowed_patterns,
1779+
priv->forbidden_patterns,
1780+
priv->allowed_filenames,
1781+
priv->forbidden_filenames)) {
15711782
return FALSE;
15721783
}
15731784

0 commit comments

Comments
 (0)