summaryrefslogtreecommitdiffstats
path: root/window-choose.c
diff options
context:
space:
mode:
authorNicholas Marriott <nicm@openbsd.org>2012-10-02 08:16:28 +0000
committerNicholas Marriott <nicm@openbsd.org>2012-10-02 08:16:28 +0000
commit9107b0c69a12ebc6161e1a11c26a8c2265c61fcc (patch)
treef88c3a93c7be09e81d00d1ff70dbed5a34d4cf09 /window-choose.c
parentea289bc4579c30e5d752264c2779376c0f3f9657 (diff)
Allow session tree (C-b s) to expand and collapse sessions with
left/right/space keys. From Thomas Adam.
Diffstat (limited to 'window-choose.c')
-rw-r--r--window-choose.c219
1 files changed, 217 insertions, 2 deletions
diff --git a/window-choose.c b/window-choose.c
index 7bab2ffe..55f95de7 100644
--- a/window-choose.c
+++ b/window-choose.c
@@ -40,6 +40,11 @@ void window_choose_write_line(
void window_choose_scroll_up(struct window_pane *);
void window_choose_scroll_down(struct window_pane *);
+void window_choose_collapse(struct window_pane *, struct session *);
+void window_choose_expand(struct window_pane *, struct session *, u_int);
+void window_choose_collapse_all(struct window_pane *);
+void window_choose_expand_all(struct window_pane *);
+
enum window_choose_input_type {
WINDOW_CHOOSE_NORMAL = -1,
WINDOW_CHOOSE_GOTO_ITEM,
@@ -60,6 +65,7 @@ struct window_choose_mode_data {
struct mode_key_data mdata;
ARRAY_DECL(, struct window_choose_mode_item) list;
+ ARRAY_DECL(, struct window_choose_mode_item) old_list;
int width;
u_int top;
u_int selected;
@@ -89,6 +95,7 @@ window_choose_add(struct window_pane *wp, struct window_choose_data *wcd)
item->name = format_expand(wcd->ft, wcd->ft_template);
item->wcd = wcd;
item->pos = ARRAY_LENGTH(&data->list) - 1;
+ item->state = 0;
data->width = xsnprintf (tmp, sizeof tmp , "%u", item->pos);
}
@@ -108,6 +115,9 @@ window_choose_ready(struct window_pane *wp, u_int cur,
data->callbackfn = callbackfn;
data->freefn = freefn;
+ ARRAY_CONCAT(&data->old_list, &data->list);
+
+ window_choose_collapse_all(wp);
window_choose_redraw_screen(wp);
}
@@ -127,6 +137,7 @@ window_choose_init(struct window_pane *wp)
data->input_prompt = NULL;
ARRAY_INIT(&data->list);
+ ARRAY_INIT(&data->old_list);
data->top = 0;
s = &data->screen;
@@ -154,9 +165,11 @@ window_choose_data_create(struct cmd_ctx *ctx)
wcd->ft_template = NULL;
wcd->command = NULL;
wcd->wl = NULL;
+ wcd->tree_session = NULL;
wcd->client = ctx->curclient;
wcd->session = ctx->curclient->session;
wcd->idx = -1;
+ wcd->type = 0;
return (wcd);
}
@@ -175,6 +188,7 @@ window_choose_free(struct window_pane *wp)
free(item->name);
}
ARRAY_FREE(&data->list);
+ ARRAY_FREE(&data->old_list);
free(data->input_str);
screen_free(&data->screen);
@@ -228,6 +242,166 @@ window_choose_prompt_input(enum window_choose_input_type input_type,
window_choose_redraw_screen(wp);
}
+void
+window_choose_collapse(struct window_pane *wp, struct session *s)
+{
+ struct window_choose_mode_data *data = wp->modedata;
+ struct window_choose_mode_item *item, *chosen;
+ struct window_choose_data *wcd;
+ u_int i, pos;
+
+ ARRAY_DECL(, struct window_choose_mode_item) list_copy;
+ ARRAY_INIT(&list_copy);
+
+ pos = data->selected;
+
+ chosen = &ARRAY_ITEM(&data->list, pos);
+ chosen->state &= ~TREE_EXPANDED;
+
+ /*
+ * Trying to mangle the &data->list in-place has lots of problems, so
+ * assign the actual result we want to render and copy the new one over
+ * the top of it.
+ */
+ for (i = 0; i < ARRAY_LENGTH(&data->list); i++)
+ {
+ item = &ARRAY_ITEM(&data->list, i);
+ wcd = item->wcd;
+
+ if (s == wcd->tree_session) {
+ /* We only show the session when collapsed. */
+ if (wcd->type & TREE_SESSION) {
+ item->state &= ~TREE_EXPANDED;
+
+ ARRAY_ADD(&list_copy,
+ ARRAY_ITEM(&data->list, i));
+ /*
+ * Update the selection to this session item so
+ * we don't end up highlighting a non-existent
+ * item.
+ */
+ data->selected = i;
+ }
+ } else
+ ARRAY_ADD(&list_copy, ARRAY_ITEM(&data->list, i));
+ }
+
+ if (!ARRAY_EMPTY(&list_copy)) {
+ ARRAY_FREE(&data->list);
+ ARRAY_CONCAT(&data->list, &list_copy);
+ }
+}
+
+void
+window_choose_collapse_all(struct window_pane *wp)
+{
+ struct window_choose_mode_data *data = wp->modedata;
+ struct window_choose_mode_item *item, *chosen;
+ struct session *s;
+ u_int i;
+
+ chosen = &ARRAY_ITEM(&data->list, data->selected);
+
+ RB_FOREACH(s, sessions, &sessions)
+ window_choose_collapse(wp, s);
+
+ /* Reset the selection back to the starting session. */
+ for (i = 0; i < ARRAY_LENGTH(&data->list); i++) {
+ item = &ARRAY_ITEM(&data->list, i);
+
+ if (chosen->wcd->session != item->wcd->tree_session)
+ continue;
+
+ if (item->wcd->type & TREE_SESSION)
+ data->selected = i;
+ }
+ window_choose_redraw_screen(wp);
+}
+
+void
+window_choose_expand_all(struct window_pane *wp)
+{
+ struct window_choose_mode_data *data = wp->modedata;
+ struct window_choose_mode_item *item;
+ struct session *s;
+ u_int i;
+
+ RB_FOREACH(s, sessions, &sessions) {
+ for (i = 0; i < ARRAY_LENGTH(&data->list); i++) {
+ item = &ARRAY_ITEM(&data->list, i);
+
+ if (s != item->wcd->tree_session)
+ continue;
+
+ if (item->wcd->type & TREE_SESSION)
+ window_choose_expand(wp, s, i);
+ }
+ }
+
+ window_choose_redraw_screen(wp);
+}
+
+void
+window_choose_expand(struct window_pane *wp, struct session *s, u_int pos)
+{
+ struct window_choose_mode_data *data = wp->modedata;
+ struct window_choose_mode_item *item, *chosen;
+ struct window_choose_data *wcd;
+ u_int i, items;
+
+ chosen = &ARRAY_ITEM(&data->list, pos);
+ items = ARRAY_LENGTH(&data->old_list) - 1;
+
+ /* It's not possible to expand anything other than sessions. */
+ if (!(chosen->wcd->type & TREE_SESSION))
+ return;
+
+ /* Don't re-expand a session which is already expanded. */
+ if (chosen->state & TREE_EXPANDED)
+ return;
+
+ /* Mark the session entry as expanded. */
+ chosen->state |= TREE_EXPANDED;
+
+ /*
+ * Go back through the original list of all sessions and windows, and
+ * pull out the windows where the session matches the selection chosen
+ * to expand.
+ */
+ for (i = items; i > 0; i--) {
+ item = &ARRAY_ITEM(&data->old_list, i);
+ item->state |= TREE_EXPANDED;
+ wcd = item->wcd;
+
+ if (s == wcd->tree_session) {
+ /*
+ * Since the session is already displayed, we only care
+ * to add back in window for it.
+ */
+ if (wcd->type & TREE_WINDOW) {
+ /*
+ * If the insertion point for adding the
+ * windows to the session falls inside the
+ * range of the list, then we insert these
+ * entries in order *AFTER* the selected
+ * session.
+ */
+ if (pos < i ) {
+ ARRAY_INSERT(&data->list,
+ pos + 1,
+ ARRAY_ITEM(&data->old_list,
+ i));
+ } else {
+ /* Ran out of room, add to the end. */
+ ARRAY_ADD(&data->list,
+ ARRAY_ITEM(&data->old_list,
+ i));
+ }
+ }
+ }
+ }
+}
+
/* ARGSUSED */
void
window_choose_key(struct window_pane *wp, unused struct session *sess, int key)
@@ -237,7 +411,7 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key)
struct screen_write_ctx ctx;
struct window_choose_mode_item *item;
size_t input_len;
- u_int items, n;
+ u_int items, n;
int idx;
items = ARRAY_LENGTH(&data->list);
@@ -285,6 +459,37 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key)
window_choose_fire_callback(wp, item->wcd);
window_pane_reset_mode(wp);
break;
+ case MODEKEYCHOICE_TREE_TOGGLE:
+ item = &ARRAY_ITEM(&data->list, data->selected);
+ if (item->state & TREE_EXPANDED)
+ window_choose_collapse(wp, item->wcd->tree_session);
+ else {
+ window_choose_expand(wp, item->wcd->tree_session,
+ data->selected);
+ }
+ window_choose_redraw_screen(wp);
+ break;
+ case MODEKEYCHOICE_TREE_COLLAPSE:
+ item = &ARRAY_ITEM(&data->list, data->selected);
+ if (item->state & TREE_EXPANDED) {
+ window_choose_collapse(wp, item->wcd->tree_session);
+ window_choose_redraw_screen(wp);
+ }
+ break;
+ case MODEKEYCHOICE_TREE_COLLAPSE_ALL:
+ window_choose_collapse_all(wp);
+ break;
+ case MODEKEYCHOICE_TREE_EXPAND:
+ item = &ARRAY_ITEM(&data->list, data->selected);
+ if (!(item->state & TREE_EXPANDED)) {
+ window_choose_expand(wp, item->wcd->tree_session,
+ data->selected);
+ window_choose_redraw_screen(wp);
+ }
+ break;
+ case MODEKEYCHOICE_TREE_EXPAND_ALL:
+ window_choose_expand_all(wp);
+ break;
case MODEKEYCHOICE_UP:
if (items == 0)
break;
@@ -469,7 +674,13 @@ window_choose_write_line(
else
xsnprintf (label, sizeof label, "(%d)", item->pos);
screen_write_nputs(ctx, screen_size_x(s) - 1, &gc, utf8flag,
- "%*s %s", data->width + 2, label, item->name);
+ "%*s %s %s", data->width + 2, label,
+ /*
+ * Add indication to tree if necessary about whether it's
+ * expanded or not.
+ */
+ (item->wcd->type & TREE_SESSION) ?
+ (item->state & TREE_EXPANDED ? "-" : "+") : "", item->name);
}
while (s->cx < screen_size_x(s))
screen_write_putc(ctx, &gc, ' ');
@@ -623,6 +834,8 @@ window_choose_add_session(struct window_pane *wp, struct cmd_ctx *ctx,
wcd = window_choose_data_create(ctx);
wcd->idx = s->idx;
+ wcd->tree_session = s;
+ wcd->type = TREE_SESSION;
wcd->command = cmd_template_replace(action, s->name, 1);
wcd->ft_template = xstrdup(template);
format_add(wcd->ft, "line", "%u", idx);
@@ -684,6 +897,8 @@ window_choose_add_window(struct window_pane *wp, struct cmd_ctx *ctx,
wcd->idx = wl->idx;
wcd->wl = wl;
+ wcd->tree_session = s;
+ wcd->type = TREE_WINDOW;
wcd->ft_template = xstrdup(template);
format_add(wcd->ft, "line", "%u", idx);
format_session(wcd->ft, s);