Introduce "--add" for marking windows.

In order to keep compatibility to before allowing multiple marks on a window,
we introduce a flag "--add" that must be set to put more than one mark on a
window. The default, which is also available as "--replace", keeps the old
behavior of overwriting a mark when setting a new one.

fixes #2014
This commit is contained in:
Ingo Bürk 2015-10-19 18:31:21 +02:00
parent 9bb2f038ab
commit 7a77c5f0bb
10 changed files with 61 additions and 32 deletions

View File

@ -2115,17 +2115,21 @@ for this purpose: It lets you input a command and sends the command to i3. It
can also prefix this command and display a custom prompt for the input dialog. can also prefix this command and display a custom prompt for the input dialog.
The additional +--toggle+ option will remove the mark if the window already has The additional +--toggle+ option will remove the mark if the window already has
this mark, add it if the window has none or replace the current mark if it has this mark or add it otherwise. Note that you may need to use this in
another mark. combination with +--add+ (see below) as any other marks will otherwise be
removed.
By default, a window can only have one mark. You can use the +--add+ flag to
put more than one mark on a window.
Refer to <<show_marks>> if you don't want marks to be shown in the window decoration. Refer to <<show_marks>> if you don't want marks to be shown in the window decoration.
*Syntax*: *Syntax*:
------------------------------ ----------------------------------------------
mark [--toggle] <identifier> mark [--add|--replace] [--toggle] <identifier>
[con_mark="identifier"] focus [con_mark="identifier"] focus
unmark <identifier> unmark <identifier>
------------------------------ ----------------------------------------------
*Example (in a terminal)*: *Example (in a terminal)*:
------------------------------ ------------------------------

View File

@ -115,10 +115,10 @@ void cmd_workspace_back_and_forth(I3_CMD);
void cmd_workspace_name(I3_CMD, const char *name); void cmd_workspace_name(I3_CMD, const char *name);
/** /**
* Implementation of 'mark [--toggle] <mark>' * Implementation of 'mark [--add|--replace] [--toggle] <mark>'
* *
*/ */
void cmd_mark(I3_CMD, const char *mark, const char *toggle); void cmd_mark(I3_CMD, const char *mark, const char *mode, const char *toggle);
/** /**
* Implementation of 'unmark [mark]' * Implementation of 'unmark [mark]'

View File

@ -158,13 +158,13 @@ bool con_has_mark(Con *con, const char *mark);
* Otherwise, the mark is assigned to the container. * Otherwise, the mark is assigned to the container.
* *
*/ */
void con_mark_toggle(Con *con, const char *mark); void con_mark_toggle(Con *con, const char *mark, mark_mode_t mode);
/** /**
* Assigns a mark to the container. * Assigns a mark to the container.
* *
*/ */
void con_mark(Con *con, const char *mark); void con_mark(Con *con, const char *mark, mark_mode_t mode);
/** /**
* If mark is NULL, this removes all existing marks. * If mark is NULL, this removes all existing marks.

View File

@ -75,6 +75,9 @@ typedef enum { ADJ_NONE = 0,
ADJ_UPPER_SCREEN_EDGE = (1 << 2), ADJ_UPPER_SCREEN_EDGE = (1 << 2),
ADJ_LOWER_SCREEN_EDGE = (1 << 4) } adjacent_t; ADJ_LOWER_SCREEN_EDGE = (1 << 4) } adjacent_t;
typedef enum { MM_REPLACE,
MM_ADD } mark_mode_t;
/** /**
* Container layouts. See Con::layout. * Container layouts. See Con::layout.
*/ */

View File

@ -199,12 +199,14 @@ state FLOATING:
floating = 'enable', 'disable', 'toggle' floating = 'enable', 'disable', 'toggle'
-> call cmd_floating($floating) -> call cmd_floating($floating)
# mark [--toggle] <mark> # mark [--add|--replace] [--toggle] <mark>
state MARK: state MARK:
mode = '--add', '--replace'
->
toggle = '--toggle' toggle = '--toggle'
-> ->
mark = string mark = string
-> call cmd_mark($mark, $toggle) -> call cmd_mark($mark, $mode, $toggle)
# unmark [mark] # unmark [mark]
state UNMARK: state UNMARK:

View File

@ -1003,10 +1003,10 @@ void cmd_workspace_name(I3_CMD, const char *name) {
} }
/* /*
* Implementation of 'mark [--toggle] <mark>' * Implementation of 'mark [--add|--replace] [--toggle] <mark>'
* *
*/ */
void cmd_mark(I3_CMD, const char *mark, const char *toggle) { void cmd_mark(I3_CMD, const char *mark, const char *mode, const char *toggle) {
HANDLE_EMPTY_MATCH; HANDLE_EMPTY_MATCH;
owindow *current = TAILQ_FIRST(&owindows); owindow *current = TAILQ_FIRST(&owindows);
@ -1022,10 +1022,12 @@ void cmd_mark(I3_CMD, const char *mark, const char *toggle) {
} }
DLOG("matching: %p / %s\n", current->con, current->con->name); DLOG("matching: %p / %s\n", current->con, current->con->name);
mark_mode_t mark_mode = (mode == NULL || strcmp(mode, "--replace") == 0) ? MM_REPLACE : MM_ADD;
if (toggle != NULL) { if (toggle != NULL) {
con_mark_toggle(current->con, mark); con_mark_toggle(current->con, mark, mark_mode);
} else { } else {
con_mark(current->con, mark); con_mark(current->con, mark, mark_mode);
} }
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;

View File

@ -540,14 +540,14 @@ bool con_has_mark(Con *con, const char *mark) {
* Otherwise, the mark is assigned to the container. * Otherwise, the mark is assigned to the container.
* *
*/ */
void con_mark_toggle(Con *con, const char *mark) { void con_mark_toggle(Con *con, const char *mark, mark_mode_t mode) {
assert(con != NULL); assert(con != NULL);
DLOG("Toggling mark \"%s\" on con = %p.\n", mark, con); DLOG("Toggling mark \"%s\" on con = %p.\n", mark, con);
if (con_has_mark(con, mark)) { if (con_has_mark(con, mark)) {
con_unmark(mark); con_unmark(mark);
} else { } else {
con_mark(con, mark); con_mark(con, mark, mode);
} }
} }
@ -555,11 +555,19 @@ void con_mark_toggle(Con *con, const char *mark) {
* Assigns a mark to the container. * Assigns a mark to the container.
* *
*/ */
void con_mark(Con *con, const char *mark) { void con_mark(Con *con, const char *mark, mark_mode_t mode) {
assert(con != NULL); assert(con != NULL);
DLOG("Setting mark \"%s\" on con = %p.\n", mark, con); DLOG("Setting mark \"%s\" on con = %p.\n", mark, con);
con_unmark(mark); con_unmark(mark);
if (mode == MM_REPLACE) {
DLOG("Removing all existing marks on con = %p.\n", con);
mark_t *current;
TAILQ_FOREACH(current, &(con->marks_head), marks) {
con_unmark(current->name);
}
}
mark_t *new = scalloc(1, sizeof(mark_t)); mark_t *new = scalloc(1, sizeof(mark_t));
new->name = sstrdup(mark); new->name = sstrdup(mark);

View File

@ -246,7 +246,7 @@ static int json_string(void *ctx, const unsigned char *val, size_t len) {
char *mark; char *mark;
sasprintf(&mark, "%.*s", (int)len, val); sasprintf(&mark, "%.*s", (int)len, val);
con_mark(json_node, mark); con_mark(json_node, mark, MM_ADD);
} else { } else {
if (strcasecmp(last_key, "name") == 0) { if (strcasecmp(last_key, "name") == 0) {
json_node->name = scalloc(len + 1, 1); json_node->name = scalloc(len + 1, 1);
@ -354,7 +354,7 @@ static int json_string(void *ctx, const unsigned char *val, size_t len) {
char *buf = NULL; char *buf = NULL;
sasprintf(&buf, "%.*s", (int)len, val); sasprintf(&buf, "%.*s", (int)len, val);
con_mark(json_node, buf); con_mark(json_node, buf, MM_REPLACE);
} else if (strcasecmp(last_key, "floating") == 0) { } else if (strcasecmp(last_key, "floating") == 0) {
char *buf = NULL; char *buf = NULL;
sasprintf(&buf, "%.*s", (int)len, val); sasprintf(&buf, "%.*s", (int)len, val);

View File

@ -118,7 +118,17 @@ cmd 'mark --toggle important';
is_deeply(get_mark_for_window_on_workspace($tmp, $con), [ 'important' ], 'container now has the mark'); is_deeply(get_mark_for_window_on_workspace($tmp, $con), [ 'important' ], 'container now has the mark');
############################################################## ##############################################################
# 7: mark a con, toggle the mark on another con, # 7: mark a con, toggle a different mark, check it is marked
# with the new mark
##############################################################
$con = open_window;
cmd 'mark boring';
cmd 'mark --replace --toggle important';
is_deeply(get_mark_for_window_on_workspace($tmp, $con), [ 'important' ], 'container has the most recent mark');
##############################################################
# 8: mark a con, toggle the mark on another con,
# check only the latter has the mark # check only the latter has the mark
############################################################## ##############################################################
@ -133,7 +143,7 @@ is_deeply(get_mark_for_window_on_workspace($tmp, $first), [ 'important' ], 'left
ok(!get_mark_for_window_on_workspace($tmp, $second), 'second containr no longer has the mark'); ok(!get_mark_for_window_on_workspace($tmp, $second), 'second containr no longer has the mark');
############################################################## ##############################################################
# 8: try to mark two cons with the same mark and check that # 9: try to mark two cons with the same mark and check that
# it fails # it fails
############################################################## ##############################################################

View File

@ -38,8 +38,8 @@ sub get_mark_for_window_on_workspace {
$ws = fresh_workspace; $ws = fresh_workspace;
$con = open_window; $con = open_window;
cmd 'mark A'; cmd 'mark --add A';
cmd 'mark B'; cmd 'mark --add B';
is_deeply(sort(get_marks()), [ 'A', 'B' ], 'both marks exist'); is_deeply(sort(get_marks()), [ 'A', 'B' ], 'both marks exist');
is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A', 'B' ], 'both marks are on the same window'); is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A', 'B' ], 'both marks are on the same window');
@ -54,9 +54,9 @@ $ws = fresh_workspace;
$con = open_window; $con = open_window;
cmd 'mark A'; cmd 'mark A';
cmd 'mark --toggle B'; cmd 'mark --add --toggle B';
is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A', 'B' ], 'both marks are on the same window'); is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A', 'B' ], 'both marks are on the same window');
cmd 'mark --toggle B'; cmd 'mark --add --toggle B';
is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A' ], 'only mark B has been removed'); is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A' ], 'only mark B has been removed');
cmd 'unmark'; cmd 'unmark';
@ -67,9 +67,9 @@ cmd 'unmark';
$ws = fresh_workspace; $ws = fresh_workspace;
$con = open_window; $con = open_window;
cmd 'mark A'; cmd 'mark --add A';
cmd 'mark B'; cmd 'mark --add B';
cmd 'mark C'; cmd 'mark --add C';
cmd 'unmark B'; cmd 'unmark B';
is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A', 'C' ], 'only mark B has been removed'); is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A', 'C' ], 'only mark B has been removed');
@ -82,11 +82,11 @@ cmd 'unmark';
$ws = fresh_workspace; $ws = fresh_workspace;
$con = open_window; $con = open_window;
cmd 'mark A'; cmd 'mark --add A';
cmd 'mark B'; cmd 'mark --add B';
open_window; open_window;
cmd '[con_mark=B] mark C'; cmd '[con_mark=B] mark --add C';
is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A', 'B', 'C' ], 'matching on a mark works with multiple marks'); is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A', 'B', 'C' ], 'matching on a mark works with multiple marks');
cmd 'unmark'; cmd 'unmark';