diff --git a/docs/userguide b/docs/userguide index 3d935e40..d6dbde00 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1857,9 +1857,13 @@ window, you cannot simply bind it to a key. +i3-input+ is a tool created 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. +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 +another mark. + *Syntax*: ------------------------------ -mark identifier +mark [--toggle] identifier [con_mark="identifier"] focus unmark identifier ------------------------------ diff --git a/include/commands.h b/include/commands.h index 780a9e8e..0f7e3635 100644 --- a/include/commands.h +++ b/include/commands.h @@ -109,10 +109,10 @@ void cmd_workspace_back_and_forth(I3_CMD); void cmd_workspace_name(I3_CMD, char *name); /** - * Implementation of 'mark ' + * Implementation of 'mark [--toggle] ' * */ -void cmd_mark(I3_CMD, char *mark); +void cmd_mark(I3_CMD, char *mark, char *toggle); /** * Implementation of 'unmark [mark]' diff --git a/parser-specs/commands.spec b/parser-specs/commands.spec index 315a9218..87db6cf0 100644 --- a/parser-specs/commands.spec +++ b/parser-specs/commands.spec @@ -189,10 +189,12 @@ state FLOATING: floating = 'enable', 'disable', 'toggle' -> call cmd_floating($floating) -# mark +# mark [--toggle] state MARK: + toggle = '--toggle' + -> mark = string - -> call cmd_mark($mark) + -> call cmd_mark($mark, $toggle) # unmark [mark] state UNMARK: diff --git a/src/commands.c b/src/commands.c index 9b51b3ec..ea0b9032 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1037,26 +1037,41 @@ void cmd_workspace_name(I3_CMD, char *name) { } /* - * Implementation of 'mark ' + * Implementation of 'mark [--toggle] ' * */ -void cmd_mark(I3_CMD, char *mark) { - DLOG("Clearing all windows which have that mark first\n"); - - Con *con; - TAILQ_FOREACH(con, &all_cons, all_cons) { - if (con->mark && strcmp(con->mark, mark) == 0) - FREE(con->mark); - } - - DLOG("marking window with str %s\n", mark); - owindow *current; - +void cmd_mark(I3_CMD, char *mark, char *toggle) { HANDLE_EMPTY_MATCH; + owindow *current; TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); - current->con->mark = sstrdup(mark); + if (toggle != NULL && current->con->mark && strcmp(current->con->mark, mark) == 0) { + DLOG("removing window mark %s\n", mark); + FREE(current->con->mark); + } else { + DLOG("marking window with str %s\n", mark); + FREE(current->con->mark); + current->con->mark = sstrdup(mark); + } + } + + DLOG("Clearing all non-matched windows with this mark\n"); + Con *con; + TAILQ_FOREACH(con, &all_cons, all_cons) { + /* Skip matched windows, we took care of them already. */ + bool matched = false; + TAILQ_FOREACH(current, &owindows, owindows) { + if (current->con == con) { + matched = true; + break; + } + } + if (matched) + continue; + + if (con->mark && strcmp(con->mark, mark) == 0) + FREE(con->mark); } cmd_output->needs_tree_render = true; diff --git a/testcases/t/210-mark-unmark.t b/testcases/t/210-mark-unmark.t index f285338b..0083547f 100644 --- a/testcases/t/210-mark-unmark.t +++ b/testcases/t/210-mark-unmark.t @@ -16,11 +16,19 @@ # # checks if mark and unmark work correctly use i3test; +use List::Util qw(first); sub get_marks { return i3(get_socket_path())->get_marks->recv; } +sub get_mark_for_window_on_workspace { + my ($ws, $con) = @_; + + my $current = first { $_->{window} == $con->{id} } @{get_ws_content($ws)}; + return $current->{mark}; +} + ############################################################## # 1: check that there are no marks set yet ############################################################## @@ -76,4 +84,61 @@ cmd 'unmark'; is_deeply(get_marks(), [], 'all marks removed'); +############################################################## +# 4: mark a con, use same mark to mark another con, +# check that only the latter is marked +############################################################## + +my $first = open_window; +my $second = open_window; + +cmd 'mark important'; +cmd 'focus left'; +cmd 'mark important'; + +is(get_mark_for_window_on_workspace($tmp, $first), 'important', 'first container now has the mark'); +ok(!get_mark_for_window_on_workspace($tmp, $second), 'second container lost the mark'); + +############################################################## +# 5: mark a con, toggle the mark, check that the mark is gone +############################################################## + +my $con = open_window; +cmd 'mark important'; +cmd 'mark --toggle important'; +ok(!get_mark_for_window_on_workspace($tmp, $con), 'container no longer has the mark'); + +############################################################## +# 6: toggle a mark on an unmarked con, check it is marked +############################################################## + +my $con = open_window; +cmd 'mark --toggle important'; +is(get_mark_for_window_on_workspace($tmp, $con), 'important', 'container now has the mark'); + +############################################################## +# 7: mark a con, toggle a different mark, check it is marked +# with the new mark +############################################################## + +my $con = open_window; +cmd 'mark boring'; +cmd 'mark --toggle important'; +is(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 +############################################################## + +my $first = open_window; +my $second = open_window; + +cmd 'mark important'; +cmd 'focus left'; +cmd 'mark --toggle important'; + +is(get_mark_for_window_on_workspace($tmp, $first), 'important', 'left container has the mark now'); +ok(!get_mark_for_window_on_workspace($tmp, $second), 'second containr no longer has the mark'); + done_testing;