Coverage Report

Created: 2026-08-13 06:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/window-tree.c
Line
Count
Source
1
/* $OpenBSD: window-tree.c,v 1.94 2026/07/14 17:17:18 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
21
#include <ctype.h>
22
#include <stdlib.h>
23
#include <string.h>
24
25
#include "tmux.h"
26
27
static struct screen  *window_tree_init(struct window_mode_entry *,
28
           struct cmdq_item *, struct cmd_find_state *,
29
           struct args *);
30
static void    window_tree_free(struct window_mode_entry *);
31
static void    window_tree_resize(struct window_mode_entry *, u_int,
32
           u_int);
33
static void    window_tree_update(struct window_mode_entry *);
34
static void    window_tree_key(struct window_mode_entry *,
35
           struct client *, struct session *,
36
           struct winlink *, key_code, struct mouse_event *);
37
38
0
#define WINDOW_TREE_DEFAULT_COMMAND "switch-client -Zt '%%'"
39
40
#define WINDOW_TREE_DEFAULT_FORMAT \
41
0
  "#{?pane_format," \
42
0
    "#{?pane_marked,#[fg=thememagenta],}#{?pane_floating_flag,#[underscore],}" \
43
0
    "#{pane_current_command}#[fg=themelightgrey]#{pane_flags}" \
44
0
    "#{?#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}},: \"#{pane_title}\",}" \
45
0
  ",window_format," \
46
0
    "#{?window_marked_flag,#[fg=thememagenta],}" \
47
0
    "#{window_name}#[fg=themelightgrey]#{window_flags}" \
48
0
    "#{?#{&&:#{==:#{window_panes},1},#{&&:#{pane_title},#{!=:#{pane_title},#{host_short}}}},: \"#{pane_title}\",}" \
49
0
  "," \
50
0
    "#[fg=themelightgrey]#{session_windows} windows" \
51
0
    "#{?session_grouped, " \
52
0
      "(group #{session_group}: " \
53
0
      "#{session_group_list})," \
54
0
    "}" \
55
0
    "#{?session_attached, (attached),}" \
56
0
  "}"
57
58
#define WINDOW_TREE_DEFAULT_KEY_FORMAT \
59
0
  "#{?#{e|<:#{line},10}," \
60
0
    "#{line}" \
61
0
  ",#{e|<:#{line},36}," \
62
0
          "M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
63
0
  "}"
64
65
static const struct menu_item window_tree_menu_items[] = {
66
  { "Select", '\r', NULL },
67
  { "Expand", KEYC_RIGHT, NULL },
68
  { "Mark", 'm', NULL },
69
  { "", KEYC_NONE, NULL },
70
  { "Tag", 't', NULL },
71
  { "Tag All", '\024', NULL },
72
  { "Tag None", 'T', NULL },
73
  { "", KEYC_NONE, NULL },
74
  { "Kill", 'x', NULL },
75
  { "Kill Tagged", 'X', NULL },
76
  { "", KEYC_NONE, NULL },
77
  { "Cancel", 'q', NULL },
78
79
  { NULL, KEYC_NONE, NULL }
80
};
81
82
const struct window_mode window_tree_mode = {
83
  .name = "tree-mode",
84
  .default_format = WINDOW_TREE_DEFAULT_FORMAT,
85
86
  .init = window_tree_init,
87
  .free = window_tree_free,
88
  .resize = window_tree_resize,
89
  .update = window_tree_update,
90
  .key = window_tree_key,
91
};
92
93
enum window_tree_type {
94
  WINDOW_TREE_NONE,
95
  WINDOW_TREE_SESSION,
96
  WINDOW_TREE_WINDOW,
97
  WINDOW_TREE_PANE,
98
};
99
100
struct window_tree_itemdata {
101
  enum window_tree_type type;
102
  int     session;
103
  int     winlink;
104
  int     pane;
105
};
106
107
struct window_tree_modedata {
108
  struct window_pane     *wp;
109
  int         dead;
110
  int         references;
111
112
  struct mode_tree_data    *data;
113
  char         *format;
114
  char         *key_format;
115
  char         *command;
116
  int         squash_groups;
117
  int         hide_preview_this_pane;
118
  int         preview_is_info;
119
  int         prompt_flags;
120
121
  struct window_tree_itemdata **item_list;
122
  u_int         item_size;
123
124
  const char       *entered;
125
126
  struct cmd_find_state     fs;
127
  enum window_tree_type     type;
128
129
  int         offset;
130
131
  int         left;
132
  int         right;
133
  u_int         start;
134
  u_int         end;
135
  u_int         each;
136
};
137
138
static enum sort_order window_tree_order_seq[] = {
139
  SORT_INDEX,
140
  SORT_NAME,
141
  SORT_ACTIVITY,
142
  SORT_Z,
143
  SORT_END,
144
};
145
146
#define WINDOW_TREE_FLAG(label, cond) \
147
  "#{?" cond ",#[fg=themegreen],#[fg=themelightgrey]}" label "#[default]"
148
149
static const char *window_tree_pane_info_lines[] = {
150
  "#[fg=themelightgrey]Pane          #[#{E:tree-mode-border-style},acs]x#[default] "
151
  "#{pane_index} #[fg=themelightgrey](#{pane_id})#[default]",
152
  "#[fg=themelightgrey]Title         #[#{E:tree-mode-border-style},acs]x#[default] "
153
  "#{pane_title}",
154
  "#[fg=themelightgrey]Command       #[#{E:tree-mode-border-style},acs]x#[default] "
155
  "#{pane_current_command} #[fg=themelightgrey](PID #{pane_pid})#[default]",
156
  "#[fg=themelightgrey]Path          #[#{E:tree-mode-border-style},acs]x#[default] "
157
  "#{pane_current_path}",
158
  "#[fg=themelightgrey]TTY           #[#{E:tree-mode-border-style},acs]x#[default] "
159
  "#{pane_tty}",
160
  "#[fg=themelightgrey]Position      #[#{E:tree-mode-border-style},acs]x#[default] "
161
  "#{pane_x},#{pane_y} #{pane_width}x#{pane_height}",
162
  "#[fg=themelightgrey]Mode          #[#{E:tree-mode-border-style},acs]x#[default] "
163
  "#{?pane_in_mode,#{pane_mode},none}",
164
  "#[fg=themelightgrey]Flags         #[#{E:tree-mode-border-style},acs]x#[default] "
165
  WINDOW_TREE_FLAG("active", "pane_active") " "
166
  WINDOW_TREE_FLAG("zoomed", "window_zoomed_flag") " "
167
  WINDOW_TREE_FLAG("marked", "pane_marked") " "
168
  WINDOW_TREE_FLAG("sync", "pane_synchronized") " "
169
  WINDOW_TREE_FLAG("dead", "pane_dead") " "
170
  WINDOW_TREE_FLAG("piped", "pane_pipe"),
171
};
172
173
static const char *window_tree_window_info_lines[] = {
174
  "#[fg=themelightgrey]Window        #[#{E:tree-mode-border-style},acs]x#[default] "
175
  "#{window_index}: #{window_name} #[fg=themelightgrey](#{window_id})#[default]",
176
  "#[fg=themelightgrey]Size          #[#{E:tree-mode-border-style},acs]x#[default] "
177
  "#{window_width}x#{window_height}",
178
  "#[fg=themelightgrey]Panes         #[#{E:tree-mode-border-style},acs]x#[default] "
179
  "#{window_panes}",
180
  "#[fg=themelightgrey]Activity Time #[#{E:tree-mode-border-style},acs]x#[default] "
181
  "#{t:window_activity} #[fg=themelightgrey](#{t/r:window_activity})#[default]",
182
  "#[fg=themelightgrey]Sessions      #[#{E:tree-mode-border-style},acs]x#[default] "
183
  "#{s/,/ /:window_linked_sessions_list}",
184
  "#[fg=themelightgrey]Flags         #[#{E:tree-mode-border-style},acs]x#[default] "
185
  WINDOW_TREE_FLAG("active", "window_active") " "
186
  WINDOW_TREE_FLAG("last", "window_last_flag") " "
187
  WINDOW_TREE_FLAG("bell", "window_bell_flag") " "
188
  WINDOW_TREE_FLAG("activity", "window_activity_flag") " "
189
  WINDOW_TREE_FLAG("silence", "window_silence_flag") " "
190
  WINDOW_TREE_FLAG("zoomed", "window_zoomed_flag") " "
191
  WINDOW_TREE_FLAG("marked", "window_marked_flag"),
192
};
193
194
static const char *window_tree_session_info_lines[] = {
195
  "#[fg=themelightgrey]Session       #[#{E:tree-mode-border-style},acs]x#[default] "
196
  "#{session_name} #[fg=themelightgrey](#{session_id})#[default]",
197
  "#[fg=themelightgrey]Created Time  #[#{E:tree-mode-border-style},acs]x#[default] "
198
  "#{t:session_created} #[fg=themelightgrey](#{t/r:session_created})#[default]",
199
  "#[fg=themelightgrey]Activity Time #[#{E:tree-mode-border-style},acs]x#[default] "
200
  "#{t:session_activity} #[fg=themelightgrey](#{t/r:session_activity})#[default]",
201
  "#[fg=themelightgrey]Attached Time #[#{E:tree-mode-border-style},acs]x#[default] "
202
  "#{?#{t:session_last_attached},#{t:session_last_attached} #[fg=themelightgrey](#{t/r:session_last_attached})#[default],never}",
203
  "#[fg=themelightgrey]Clients       #[#{E:tree-mode-border-style},acs]x#[default] "
204
  "#{s/,/ /:session_attached_list}",
205
  "#[fg=themelightgrey]Windows       #[#{E:tree-mode-border-style},acs]x#[default] "
206
  "#{session_windows}",
207
  "#[fg=themelightgrey]Path          #[#{E:tree-mode-border-style},acs]x#[default] "
208
  "#{session_path}",
209
  "#[fg=themelightgrey]Group         #[#{E:tree-mode-border-style},acs]x#[default] "
210
  "#{?session_grouped,#{session_group} (#{session_group_size}),none}",
211
  "#[fg=themelightgrey]Flags         #[#{E:tree-mode-border-style},acs]x#[default] "
212
  WINDOW_TREE_FLAG("attached", "session_attached") " "
213
  WINDOW_TREE_FLAG("grouped", "session_grouped") " "
214
  WINDOW_TREE_FLAG("marked", "session_marked") " "
215
  WINDOW_TREE_FLAG("bell", "session_bell_flag") " "
216
  WINDOW_TREE_FLAG("activity", "session_activity_flag") " "
217
  WINDOW_TREE_FLAG("silence", "session_silence_flag"),
218
};
219
220
static void
221
window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp,
222
    struct winlink **wlp, struct window_pane **wp)
223
0
{
224
0
  *wp = NULL;
225
0
  *wlp = NULL;
226
0
  *sp = session_find_by_id(item->session);
227
0
  if (*sp == NULL)
228
0
    return;
229
0
  if (item->type == WINDOW_TREE_SESSION) {
230
0
    *wlp = (*sp)->curw;
231
0
    *wp = (*wlp)->window->active;
232
0
    return;
233
0
  }
234
235
0
  *wlp = winlink_find_by_index(&(*sp)->windows, item->winlink);
236
0
  if (*wlp == NULL) {
237
0
    *sp = NULL;
238
0
    return;
239
0
  }
240
0
  if (item->type == WINDOW_TREE_WINDOW) {
241
0
    *wp = (*wlp)->window->active;
242
0
    return;
243
0
  }
244
245
0
  *wp = window_pane_find_by_id(item->pane);
246
0
  if (!window_has_pane((*wlp)->window, *wp))
247
0
    *wp = NULL;
248
0
  if (*wp == NULL) {
249
0
    *sp = NULL;
250
0
    *wlp = NULL;
251
0
    return;
252
0
  }
253
0
}
254
255
static struct window_tree_itemdata *
256
window_tree_add_item(struct window_tree_modedata *data)
257
0
{
258
0
  struct window_tree_itemdata *item;
259
260
0
  data->item_list = xreallocarray(data->item_list, data->item_size + 1,
261
0
      sizeof *data->item_list);
262
0
  item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
263
0
  return (item);
264
0
}
265
266
static void
267
window_tree_free_item(struct window_tree_itemdata *item)
268
0
{
269
0
  free(item);
270
0
}
271
272
static void
273
window_tree_build_pane(struct session *s, struct winlink *wl,
274
    struct window_pane *wp, void *modedata, struct mode_tree_item *parent)
275
0
{
276
0
  struct window_tree_modedata *data = modedata;
277
0
  struct window_tree_itemdata *item;
278
0
  struct mode_tree_item   *mti;
279
0
  char        *name, *text;
280
0
  u_int        idx;
281
0
  struct format_tree    *ft;
282
283
0
  window_pane_index(wp, &idx);
284
285
0
  item = window_tree_add_item(data);
286
0
  item->type = WINDOW_TREE_PANE;
287
0
  item->session = s->id;
288
0
  item->winlink = wl->idx;
289
0
  item->pane = wp->id;
290
291
0
  ft = format_create(NULL, NULL, FORMAT_PANE|wp->id, 0);
292
0
  format_defaults(ft, NULL, s, wl, wp);
293
0
  text = format_expand(ft, data->format);
294
0
  xasprintf(&name, "%u", idx);
295
0
  format_free(ft);
296
297
0
  mti = mode_tree_add(data->data, parent, item, (uint64_t)wp, name, text,
298
0
      -1);
299
0
  free(text);
300
0
  free(name);
301
0
  mode_tree_align(mti, 1);
302
0
}
303
304
static int
305
window_tree_filter_pane(struct session *s, struct winlink *wl,
306
    struct window_pane *wp, const char *filter)
307
0
{
308
0
  char  *cp;
309
0
  int  result;
310
311
0
  if (filter == NULL)
312
0
    return (1);
313
314
0
  cp = format_single(NULL, filter, NULL, s, wl, wp);
315
0
  result = format_true(cp);
316
0
  free(cp);
317
318
0
  return (result);
319
0
}
320
321
static int
322
window_tree_build_window(struct session *s, struct winlink *wl,
323
    void *modedata, struct sort_criteria *sort_crit,
324
    struct mode_tree_item *parent, const char *filter)
325
0
{
326
0
  struct window_tree_modedata *data = modedata;
327
0
  struct window_tree_itemdata *item;
328
0
  struct mode_tree_item   *mti;
329
0
  char        *name, *text;
330
0
  struct window_pane    **l;
331
0
  u_int        n, i, found;
332
0
  int        expanded;
333
0
  struct format_tree    *ft;
334
0
  uint64_t       tag = FORMAT_NONE;
335
336
0
  item = window_tree_add_item(data);
337
0
  item->type = WINDOW_TREE_WINDOW;
338
0
  item->session = s->id;
339
0
  item->winlink = wl->idx;
340
0
  item->pane = -1;
341
342
0
  if (wl->window != NULL && wl->window->active != NULL)
343
0
    tag = FORMAT_PANE|wl->window->active->id;
344
0
  ft = format_create(NULL, NULL, tag, 0);
345
0
  format_defaults(ft, NULL, s, wl, NULL);
346
0
  text = format_expand(ft, data->format);
347
0
  xasprintf(&name, "%u", wl->idx);
348
0
  format_free(ft);
349
350
0
  if (data->type == WINDOW_TREE_SESSION ||
351
0
      data->type == WINDOW_TREE_WINDOW)
352
0
    expanded = 0;
353
0
  else
354
0
    expanded = 1;
355
0
  mti = mode_tree_add(data->data, parent, item, (uint64_t)wl, name, text,
356
0
      expanded);
357
0
  free(text);
358
0
  free(name);
359
0
  mode_tree_align(mti, 1);
360
361
0
  l = sort_get_panes_window(wl->window, &n, sort_crit);
362
0
  found = 0;
363
0
  for (i = 0; i < n; i++) {
364
0
    if (!window_tree_filter_pane(s, wl, l[i], filter))
365
0
      continue;
366
0
    found++;
367
0
    if (data->hide_preview_this_pane && l[i] == data->wp)
368
0
      continue;
369
0
    window_tree_build_pane(s, wl, l[i], modedata, mti);
370
0
  }
371
0
  if (found == 0) {
372
0
    window_tree_free_item(item);
373
0
    data->item_size--;
374
0
    mode_tree_remove(data->data, mti);
375
0
    return (0);
376
0
  }
377
0
  return (1);
378
0
}
379
380
static void
381
window_tree_build_session(struct session *s, void *modedata,
382
    struct sort_criteria *sort_crit, const char *filter)
383
0
{
384
0
  struct window_tree_modedata *data = modedata;
385
0
  struct window_tree_itemdata *item;
386
0
  struct mode_tree_item   *mti;
387
0
  char        *text;
388
0
  struct winlink      *wl = s->curw, **l;
389
0
  u_int        n, i, empty;
390
0
  int        expanded;
391
0
  struct format_tree    *ft;
392
0
  uint64_t       tag = FORMAT_NONE;
393
394
0
  item = window_tree_add_item(data);
395
0
  item->type = WINDOW_TREE_SESSION;
396
0
  item->session = s->id;
397
0
  item->winlink = -1;
398
0
  item->pane = -1;
399
400
0
  if (wl != NULL && wl->window != NULL && wl->window->active != NULL)
401
0
    tag = FORMAT_PANE|wl->window->active->id;
402
0
  ft = format_create(NULL, NULL, tag, 0);
403
0
  format_defaults(ft, NULL, s, NULL, NULL);
404
0
  text = format_expand(ft, data->format);
405
0
  format_free(ft);
406
407
0
  if (data->type == WINDOW_TREE_SESSION)
408
0
    expanded = 0;
409
0
  else
410
0
    expanded = 1;
411
0
  mti = mode_tree_add(data->data, NULL, item, (uint64_t)s, s->name, text,
412
0
      expanded);
413
0
  free(text);
414
415
0
  l = sort_get_winlinks_session(s, &n, sort_crit);
416
0
  empty = 0;
417
0
  for (i = 0; i < n; i++) {
418
0
    if (!window_tree_build_window(s, l[i], modedata, sort_crit, mti,
419
0
        filter))
420
0
      empty++;
421
0
  }
422
0
  if (empty == n) {
423
0
    window_tree_free_item(item);
424
0
    data->item_size--;
425
0
    mode_tree_remove(data->data, mti);
426
0
  }
427
0
}
428
429
static void
430
window_tree_build(void *modedata, struct sort_criteria *sort_crit,
431
    uint64_t *tag, const char *filter)
432
0
{
433
0
  struct window_tree_modedata *data = modedata;
434
0
  int        squash_groups = data->squash_groups;
435
0
  struct session      *s, **l;
436
0
  struct session_group    *sg, *current;
437
0
  u_int        n, i;
438
439
0
  current = session_group_contains(data->fs.s);
440
441
0
  for (i = 0; i < data->item_size; i++)
442
0
    window_tree_free_item(data->item_list[i]);
443
0
  free(data->item_list);
444
0
  data->item_list = NULL;
445
0
  data->item_size = 0;
446
447
0
  l = sort_get_sessions(&n, sort_crit);
448
0
  if (n == 0)
449
0
    return;
450
0
  for (i = 0; i < n; i++) {
451
0
    s = l[i];
452
0
    if (squash_groups && (sg = session_group_contains(s)) != NULL) {
453
0
      if ((sg == current && s != data->fs.s) ||
454
0
          (sg != current && s != TAILQ_FIRST(&sg->sessions)))
455
0
        continue;
456
0
    }
457
0
    window_tree_build_session(s, modedata, sort_crit, filter);
458
0
  }
459
460
0
  switch (data->type) {
461
0
  case WINDOW_TREE_NONE:
462
0
    break;
463
0
  case WINDOW_TREE_SESSION:
464
0
    *tag = (uint64_t)data->fs.s;
465
0
    break;
466
0
  case WINDOW_TREE_WINDOW:
467
0
    *tag = (uint64_t)data->fs.wl;
468
0
    break;
469
0
  case WINDOW_TREE_PANE:
470
0
    if (window_count_panes(data->fs.wl->window, 1) == 1)
471
0
      *tag = (uint64_t)data->fs.wl;
472
0
    else
473
0
      *tag = (uint64_t)data->fs.wp;
474
0
    break;
475
0
  }
476
0
}
477
478
static void
479
window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py,
480
    u_int sx, u_int sy, const struct grid_cell *border_gc,
481
    const struct grid_cell *label_gc, const char *label)
482
0
{
483
0
  u_int  width, ox, oy;
484
0
  char  *new_label = NULL;
485
486
0
  if (sx < 5 || sy < 3)
487
0
    return;
488
0
  width = format_width(label);
489
0
  if (width > sx - 4) {
490
0
    label = new_label = format_trim_left(label, sx - 4);
491
0
    width = format_width(new_label);
492
0
  }
493
0
  if (width == 0)
494
0
    return;
495
0
  ox = (sx - width + 1) / 2;
496
0
  oy = (sy + 1) / 2;
497
498
0
  screen_write_cursormove(ctx, px + ox - 2, py + oy - 1, 0);
499
0
  screen_write_box(ctx, width + 4, 3, BOX_LINES_DEFAULT,
500
0
      border_gc, NULL);
501
0
  screen_write_cursormove(ctx, px + ox - 1, py + oy, 0);
502
0
  screen_write_clearcharacter(ctx, width + 2, border_gc->bg);
503
0
  screen_write_cursormove(ctx, px + ox, py + oy, 0);
504
0
  format_draw(ctx, label_gc, width, label, NULL, 0);
505
0
  free(new_label);
506
0
}
507
508
static void
509
window_tree_border_cell(struct grid_cell *gc, struct options *oo,
510
    struct format_tree *ft)
511
0
{
512
0
  memcpy(gc, &grid_default_cell, sizeof *gc);
513
0
  style_apply(gc, oo, "tree-mode-border-style", ft);
514
0
}
515
516
static void
517
window_tree_draw_session(struct window_tree_modedata *data, struct session *s,
518
    struct screen_write_ctx *ctx, u_int sx, u_int sy)
519
0
{
520
0
  struct winlink    *wl;
521
0
  struct window   *w;
522
0
  u_int      cx = ctx->s->cx, cy = ctx->s->cy;
523
0
  u_int      loop, total, visible, each, width, offset;
524
0
  u_int      current, start, end, remaining, i;
525
0
  struct grid_cell   gc, label_gc;
526
0
  int      left, right;
527
0
  char      *label;
528
0
  const char    *format;
529
0
  struct format_tree  *ft;
530
0
  struct options    *oo;
531
532
0
  total = winlink_count(&s->windows);
533
534
0
  if (sx / total < 24) {
535
0
    visible = sx / 24;
536
0
    if (visible == 0)
537
0
      visible = 1;
538
0
  } else
539
0
    visible = total;
540
541
0
  current = 0;
542
0
  RB_FOREACH(wl, winlinks, &s->windows) {
543
0
    if (wl == s->curw)
544
0
      break;
545
0
    current++;
546
0
  }
547
548
0
  if (current < visible) {
549
0
    start = 0;
550
0
    end = visible;
551
0
  } else if (current >= total - visible) {
552
0
    start = total - visible;
553
0
    end = total;
554
0
  } else {
555
0
    start = current - (visible / 2);
556
0
    end = start + visible;
557
0
  }
558
559
0
  if (data->offset < -(int)start)
560
0
    data->offset = -(int)start;
561
0
  if (data->offset > (int)(total - end))
562
0
    data->offset = (int)(total - end);
563
0
  start += data->offset;
564
0
  end += data->offset;
565
566
0
  left = (start != 0);
567
0
  right = (end != total);
568
0
  if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
569
0
    left = right = 0;
570
0
  if (left && right) {
571
0
    each = (sx - 6) / visible;
572
0
    remaining = (sx - 6) - (visible * each);
573
0
  } else if (left || right) {
574
0
    each = (sx - 3) / visible;
575
0
    remaining = (sx - 3) - (visible * each);
576
0
  } else {
577
0
    each = sx / visible;
578
0
    remaining = sx - (visible * each);
579
0
  }
580
0
  if (each == 0)
581
0
    return;
582
583
0
  window_tree_border_cell(&gc, data->wp->window->options, NULL);
584
0
  if (left) {
585
0
    data->left = cx + 2;
586
0
    screen_write_cursormove(ctx, cx + 2, cy, 0);
587
0
    screen_write_vline(ctx, sy, 0, 0, &gc);
588
0
    screen_write_cursormove(ctx, cx, cy + sy / 2, 0);
589
0
    screen_write_puts(ctx, &gc, "<");
590
0
  } else
591
0
    data->left = -1;
592
0
  if (right) {
593
0
    data->right = cx + sx - 3;
594
0
    screen_write_cursormove(ctx, cx + sx - 3, cy, 0);
595
0
    screen_write_vline(ctx, sy, 0, 0, &gc);
596
0
    screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0);
597
0
    screen_write_puts(ctx, &gc, ">");
598
0
  } else
599
0
    data->right = -1;
600
601
0
  data->start = start;
602
0
  data->end = end;
603
0
  data->each = each;
604
605
0
  i = loop = 0;
606
0
  RB_FOREACH(wl, winlinks, &s->windows) {
607
0
    if (loop == end)
608
0
      break;
609
0
    if (loop < start) {
610
0
      loop++;
611
0
      continue;
612
0
    }
613
0
    w = wl->window;
614
0
    oo = w->options;
615
616
0
    ft = format_create(NULL, NULL, FORMAT_WINDOW|w->id, 0);
617
0
    format_defaults(ft, NULL, s, wl, NULL);
618
619
0
    window_tree_border_cell(&gc, oo, ft);
620
0
    memcpy(&label_gc, &grid_default_cell, sizeof label_gc);
621
0
    style_apply(&label_gc, oo, "tree-mode-preview-style", ft);
622
0
    label_gc.bg = gc.bg;
623
624
0
    if (left)
625
0
      offset = 3 + (i * each);
626
0
    else
627
0
      offset = (i * each);
628
0
    if (loop == end - 1)
629
0
      width = each + remaining;
630
0
    else
631
0
      width = each - 1;
632
633
0
    screen_write_cursormove(ctx, cx + offset, cy, 0);
634
0
    screen_write_preview(ctx, &w->active->base, width, sy);
635
636
0
    format = options_get_string(oo, "tree-mode-preview-format");
637
0
    if (*format != '\0') {
638
0
      label = format_expand(ft, format);
639
0
      if (*label != '\0') {
640
0
        window_tree_draw_label(ctx, cx + offset, cy,
641
0
            width, sy, &gc, &label_gc, label);
642
0
      }
643
0
      free(label);
644
0
    }
645
646
0
    if (loop != end - 1) {
647
0
      screen_write_cursormove(ctx, cx + offset + width, cy,
648
0
          0);
649
0
      screen_write_vline(ctx, sy, 0, 0, &gc);
650
0
    }
651
0
    loop++;
652
653
0
    i++;
654
0
  }
655
0
}
656
657
static void
658
window_tree_draw_window(struct window_tree_modedata *data, struct session *s,
659
    struct winlink *wl, struct screen_write_ctx *ctx, u_int sx, u_int sy)
660
0
{
661
0
  struct window   *w = wl->window;
662
0
  struct window_pane  *wp;
663
0
  u_int      cx = ctx->s->cx, cy = ctx->s->cy;
664
0
  u_int      loop, total, visible, each, width, offset;
665
0
  u_int      current, start, end, remaining, i;
666
0
  struct grid_cell   gc, label_gc;
667
0
  int      left, right;
668
0
  char      *label;
669
0
  const char    *format;
670
0
  struct format_tree  *ft;
671
0
  struct options    *oo;
672
673
0
  total = window_count_panes(w, 1);
674
0
  if (data->hide_preview_this_pane && data->wp->window == w)
675
0
    total--;
676
0
  if (total == 0)
677
0
    return;
678
679
0
  if (sx / total < 24) {
680
0
    visible = sx / 24;
681
0
    if (visible == 0)
682
0
      visible = 1;
683
0
  } else
684
0
    visible = total;
685
686
0
  current = 0;
687
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
688
0
    if (data->hide_preview_this_pane && wp == data->wp)
689
0
      continue;
690
0
    if (wp == w->active)
691
0
      break;
692
0
    current++;
693
0
  }
694
695
0
  if (current < visible) {
696
0
    start = 0;
697
0
    end = visible;
698
0
  } else if (current >= total - visible) {
699
0
    start = total - visible;
700
0
    end = total;
701
0
  } else {
702
0
    start = current - (visible / 2);
703
0
    end = start + visible;
704
0
  }
705
706
0
  if (data->offset < -(int)start)
707
0
    data->offset = -(int)start;
708
0
  if (data->offset > (int)(total - end))
709
0
    data->offset = (int)(total - end);
710
0
  start += data->offset;
711
0
  end += data->offset;
712
713
0
  left = (start != 0);
714
0
  right = (end != total);
715
0
  if (((left && right) && sx <= 6) || ((left || right) && sx <= 3))
716
0
    left = right = 0;
717
0
  if (left && right) {
718
0
    each = (sx - 6) / visible;
719
0
    remaining = (sx - 6) - (visible * each);
720
0
  } else if (left || right) {
721
0
    each = (sx - 3) / visible;
722
0
    remaining = (sx - 3) - (visible * each);
723
0
  } else {
724
0
    each = sx / visible;
725
0
    remaining = sx - (visible * each);
726
0
  }
727
0
  if (each == 0)
728
0
    return;
729
730
0
  window_tree_border_cell(&gc, data->wp->window->options, NULL);
731
0
  if (left) {
732
0
    data->left = cx + 2;
733
0
    screen_write_cursormove(ctx, cx + 2, cy, 0);
734
0
    screen_write_vline(ctx, sy, 0, 0, &gc);
735
0
    screen_write_cursormove(ctx, cx, cy + sy / 2, 0);
736
0
    screen_write_puts(ctx, &gc, "<");
737
0
  } else
738
0
    data->left = -1;
739
0
  if (right) {
740
0
    data->right = cx + sx - 3;
741
0
    screen_write_cursormove(ctx, cx + sx - 3, cy, 0);
742
0
    screen_write_vline(ctx, sy, 0, 0, &gc);
743
0
    screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0);
744
0
    screen_write_puts(ctx, &gc, ">");
745
0
  } else
746
0
    data->right = -1;
747
748
0
  data->start = start;
749
0
  data->end = end;
750
0
  data->each = each;
751
752
0
  i = loop = 0;
753
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
754
0
    if (data->hide_preview_this_pane && wp == data->wp)
755
0
      continue;
756
0
    if (loop == end)
757
0
      break;
758
0
    if (loop < start) {
759
0
      loop++;
760
0
      continue;
761
0
    }
762
0
    oo = wp->options;
763
764
0
    ft = format_create(NULL, NULL, FORMAT_PANE|wp->id, 0);
765
0
    format_defaults(ft, NULL, s, wl, wp);
766
767
0
    window_tree_border_cell(&gc, oo, ft);
768
0
    memcpy(&label_gc, &grid_default_cell, sizeof label_gc);
769
0
    style_apply(&label_gc, oo, "tree-mode-preview-style", ft);
770
0
    label_gc.bg = gc.bg;
771
772
0
    if (left)
773
0
      offset = 3 + (i * each);
774
0
    else
775
0
      offset = (i * each);
776
0
    if (loop == end - 1)
777
0
      width = each + remaining;
778
0
    else
779
0
      width = each - 1;
780
781
0
    screen_write_cursormove(ctx, cx + offset, cy, 0);
782
0
    screen_write_preview(ctx, &wp->base, width, sy);
783
784
0
    format = options_get_string(oo, "tree-mode-preview-format");
785
0
    if (*format != '\0') {
786
0
      label = format_expand(ft, format);
787
0
      if (*label != '\0') {
788
0
        window_tree_draw_label(ctx, cx + offset, cy,
789
0
            width, sy, &gc, &label_gc, label);
790
0
      }
791
0
      free(label);
792
0
    }
793
794
0
    if (loop != end - 1) {
795
0
      screen_write_cursormove(ctx, cx + offset + width, cy,
796
0
          0);
797
0
      screen_write_vline(ctx, sy, 0, 0, &gc);
798
0
    }
799
0
    loop++;
800
801
0
    i++;
802
0
  }
803
0
}
804
805
static void
806
window_tree_draw_info(struct window_tree_modedata *data, void *itemdata,
807
    struct screen_write_ctx *ctx, u_int sx, u_int sy)
808
0
{
809
0
  struct window_tree_itemdata *item = itemdata;
810
0
  struct screen     *s = ctx->s;
811
0
  struct session      *sp;
812
0
  struct winlink      *wl;
813
0
  struct window_pane    *wp;
814
0
  struct grid_cell     gc;
815
0
  u_int        cx = s->cx, cy = s->cy, i, j, k;
816
0
  struct format_tree    *ft;
817
0
  char        *expanded;
818
0
  const char *const   *lines[3];
819
0
  u_int        count[3], n = 0;
820
821
0
  window_tree_pull_item(item, &sp, &wl, &wp);
822
0
  if (sp == NULL || wp == NULL)
823
0
    return;
824
825
0
  if (item->type == WINDOW_TREE_PANE) {
826
0
    lines[n] = window_tree_pane_info_lines;
827
0
    count[n++] = nitems(window_tree_pane_info_lines);
828
0
  }
829
0
  if (item->type == WINDOW_TREE_PANE ||
830
0
      item->type == WINDOW_TREE_WINDOW) {
831
0
    lines[n] = window_tree_window_info_lines;
832
0
    count[n++] = nitems(window_tree_window_info_lines);
833
0
  }
834
0
  lines[n] = window_tree_session_info_lines;
835
0
  count[n++] = nitems(window_tree_session_info_lines);
836
837
0
  ft = format_create(NULL, NULL, FORMAT_NONE, 0);
838
0
  format_defaults(ft, NULL, sp, wl, wp);
839
840
0
  i = 0;
841
0
  for (j = 0; j < n; j++) {
842
0
    if (j != 0) {
843
0
      if (i == sy)
844
0
        break;
845
0
      window_tree_border_cell(&gc, data->wp->window->options,
846
0
          NULL);
847
0
      screen_write_cursormove(ctx, cx, cy + i, 0);
848
0
      screen_write_hline(ctx, sx, 0, 0, BOX_LINES_DEFAULT,
849
0
          &gc);
850
0
      if (sx > 14) {
851
0
        gc.attr |= GRID_ATTR_CHARSET;
852
0
        screen_write_cursormove(ctx, cx + 14, cy + i,
853
0
            0);
854
0
        screen_write_putc(ctx, &gc, 'n');
855
0
      }
856
0
      i++;
857
0
    }
858
0
    for (k = 0; k < count[j]; k++) {
859
0
      if (i == sy)
860
0
        break;
861
0
      expanded = format_expand(ft, lines[j][k]);
862
0
      screen_write_cursormove(ctx, cx, cy + i, 0);
863
0
      format_draw(ctx, &grid_default_cell, sx, expanded, NULL,
864
0
          0);
865
0
      free(expanded);
866
0
      i++;
867
0
    }
868
0
    if (i == sy)
869
0
      break;
870
0
  }
871
0
  if (sx > 14 && i < sy) {
872
0
    window_tree_border_cell(&gc, data->wp->window->options, NULL);
873
0
    screen_write_cursormove(ctx, cx + 14, cy + i, 0);
874
0
    screen_write_vline(ctx, sy - i, 0, 0, &gc);
875
0
  }
876
877
0
  format_free(ft);
878
0
}
879
880
static void
881
window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx,
882
    u_int sx, u_int sy)
883
0
{
884
0
  struct window_tree_modedata *data = modedata;
885
0
  struct window_tree_itemdata *item = itemdata;
886
0
  struct session      *sp;
887
0
  struct winlink      *wl;
888
0
  struct window_pane    *wp;
889
890
0
  window_tree_pull_item(item, &sp, &wl, &wp);
891
0
  if (wp == NULL)
892
0
    return;
893
894
0
  if (data->preview_is_info) {
895
0
    window_tree_draw_info(data, item, ctx, sx, sy);
896
0
    return;
897
0
  }
898
899
0
  switch (item->type) {
900
0
  case WINDOW_TREE_NONE:
901
0
    break;
902
0
  case WINDOW_TREE_SESSION:
903
0
    window_tree_draw_session(modedata, sp, ctx, sx, sy);
904
0
    break;
905
0
  case WINDOW_TREE_WINDOW:
906
0
    window_tree_draw_window(modedata, sp, wl, ctx, sx, sy);
907
0
    break;
908
0
  case WINDOW_TREE_PANE:
909
0
    if (!data->hide_preview_this_pane || wp != data->wp)
910
0
      screen_write_preview(ctx, &wp->base, sx, sy);
911
0
    break;
912
0
  }
913
0
}
914
915
static int
916
window_tree_search(__unused void *modedata, void *itemdata, const char *ss,
917
    int icase)
918
0
{
919
0
  struct window_tree_itemdata *item = itemdata;
920
0
  struct session      *s;
921
0
  struct winlink      *wl;
922
0
  struct window_pane    *wp;
923
0
  char        *cmd;
924
0
  int        retval;
925
926
0
  window_tree_pull_item(item, &s, &wl, &wp);
927
928
0
  switch (item->type) {
929
0
  case WINDOW_TREE_NONE:
930
0
    return (0);
931
0
  case WINDOW_TREE_SESSION:
932
0
    if (s == NULL)
933
0
      return (0);
934
0
    if (icase)
935
0
      return (strcasestr(s->name, ss) != NULL);
936
0
    return (strstr(s->name, ss) != NULL);
937
0
  case WINDOW_TREE_WINDOW:
938
0
    if (s == NULL || wl == NULL)
939
0
      return (0);
940
0
    if (icase)
941
0
      return (strcasestr(wl->window->name, ss) != NULL);
942
0
    return (strstr(wl->window->name, ss) != NULL);
943
0
  case WINDOW_TREE_PANE:
944
0
    if (s == NULL || wl == NULL || wp == NULL)
945
0
      break;
946
0
    cmd = osdep_get_name(wp->fd, wp->tty);
947
0
    if (cmd == NULL || *cmd == '\0') {
948
0
      free(cmd);
949
0
      return (0);
950
0
    }
951
0
    if (icase)
952
0
      retval = (strcasestr(cmd, ss) != NULL);
953
0
    else
954
0
      retval = (strstr(cmd, ss) != NULL);
955
0
    free(cmd);
956
0
    return (retval);
957
0
  }
958
0
  return (0);
959
0
}
960
961
static void
962
window_tree_menu(void *modedata, struct client *c, key_code key)
963
0
{
964
0
  struct window_tree_modedata *data = modedata;
965
0
  struct window_pane    *wp = data->wp;
966
0
  struct window_mode_entry  *wme;
967
968
0
  wme = TAILQ_FIRST(&wp->modes);
969
0
  if (wme == NULL || wme->data != modedata)
970
0
    return;
971
0
  window_tree_key(wme, c, NULL, NULL, key, NULL);
972
0
}
973
974
static key_code
975
window_tree_get_key(void *modedata, void *itemdata, u_int line)
976
0
{
977
0
  struct window_tree_modedata *data = modedata;
978
0
  struct window_tree_itemdata *item = itemdata;
979
0
  struct format_tree    *ft;
980
0
  struct session      *s;
981
0
  struct winlink      *wl;
982
0
  struct window_pane    *wp;
983
0
  char        *expanded;
984
0
  key_code       key;
985
986
0
  ft = format_create(NULL, NULL, FORMAT_NONE, 0);
987
0
  window_tree_pull_item(item, &s, &wl, &wp);
988
0
  if (item->type == WINDOW_TREE_SESSION)
989
0
    format_defaults(ft, NULL, s, NULL, NULL);
990
0
  else if (item->type == WINDOW_TREE_WINDOW)
991
0
    format_defaults(ft, NULL, s, wl, NULL);
992
0
  else
993
0
    format_defaults(ft, NULL, s, wl, wp);
994
0
  format_add(ft, "line", "%u", line);
995
996
0
  expanded = format_expand(ft, data->key_format);
997
0
  key = key_string_lookup_string(expanded);
998
0
  free(expanded);
999
0
  format_free(ft);
1000
0
  return (key);
1001
0
}
1002
1003
static int
1004
window_tree_swap(void *cur_itemdata, void *other_itemdata,
1005
    struct sort_criteria *sort_crit)
1006
0
{
1007
0
  struct window_tree_itemdata *cur = cur_itemdata;
1008
0
  struct window_tree_itemdata *other = other_itemdata;
1009
0
  struct session      *cur_session, *other_session;
1010
0
  struct winlink      *cur_winlink, *other_winlink;
1011
0
  struct window     *cur_window, *other_window;
1012
0
  struct window_pane    *cur_pane, *other_pane;
1013
1014
0
  if (cur->type != other->type)
1015
0
    return (0);
1016
0
  if (cur->type != WINDOW_TREE_WINDOW)
1017
0
    return (0);
1018
1019
0
  window_tree_pull_item(cur, &cur_session, &cur_winlink, &cur_pane);
1020
0
  window_tree_pull_item(other, &other_session, &other_winlink,
1021
0
      &other_pane);
1022
1023
0
  if (cur_session != other_session)
1024
0
    return (0);
1025
1026
  /*
1027
   * Swapping indexes would not swap positions in the tree, so prevent
1028
   * swapping to avoid confusing the user.
1029
   */
1030
0
  if (sort_would_window_tree_swap(sort_crit, cur_winlink, other_winlink))
1031
0
    return (0);
1032
1033
0
  other_window = other_winlink->window;
1034
0
  TAILQ_REMOVE(&other_window->winlinks, other_winlink, wentry);
1035
0
  cur_window = cur_winlink->window;
1036
0
  TAILQ_REMOVE(&cur_window->winlinks, cur_winlink, wentry);
1037
1038
0
  other_winlink->window = cur_window;
1039
0
  TAILQ_INSERT_TAIL(&cur_window->winlinks, other_winlink, wentry);
1040
0
  cur_winlink->window = other_window;
1041
0
  TAILQ_INSERT_TAIL(&other_window->winlinks, cur_winlink, wentry);
1042
1043
0
  if (cur_session->curw == cur_winlink)
1044
0
    session_set_current(cur_session, other_winlink);
1045
0
  else if (cur_session->curw == other_winlink)
1046
0
    session_set_current(cur_session, cur_winlink);
1047
0
  session_group_synchronize_from(cur_session);
1048
0
  server_redraw_session_group(cur_session);
1049
0
  recalculate_sizes();
1050
1051
0
  return (1);
1052
0
}
1053
1054
static void
1055
window_tree_sort(struct sort_criteria *sort_crit)
1056
0
{
1057
0
  sort_crit->order_seq = window_tree_order_seq;
1058
0
  if (sort_crit->order == SORT_END)
1059
0
    sort_crit->order = sort_crit->order_seq[0];
1060
0
}
1061
1062
static const char* window_tree_help_lines[] = {
1063
  "#[fg=themelightgrey]"
1064
  "      Enter #[#{E:tree-mode-border-style},acs]x#[default] Choose selected item",
1065
  "#[fg=themelightgrey]"
1066
  "       S-Up #[#{E:tree-mode-border-style},acs]x#[default] Swap current and previous window",
1067
  "#[fg=themelightgrey]"
1068
  "     S-Down #[#{E:tree-mode-border-style},acs]x#[default] Swap current and next window",
1069
  "#[fg=themelightgrey]"
1070
  "          x #[#{E:tree-mode-border-style},acs]x#[default] Kill selected item",
1071
  "#[fg=themelightgrey]"
1072
  "          X #[#{E:tree-mode-border-style},acs]x#[default] Kill tagged items",
1073
  "#[fg=themelightgrey]"
1074
  "          < #[#{E:tree-mode-border-style},acs]x#[default] Scroll previews left",
1075
  "#[fg=themelightgrey]"
1076
  "          > #[#{E:tree-mode-border-style},acs]x#[default] Scroll previews right",
1077
  "#[fg=themelightgrey]"
1078
  "          m #[#{E:tree-mode-border-style},acs]x#[default] Set the marked pane",
1079
  "#[fg=themelightgrey]"
1080
  "          M #[#{E:tree-mode-border-style},acs]x#[default] Clear the marked pane",
1081
  "#[fg=themelightgrey]"
1082
  "          i #[#{E:tree-mode-border-style},acs]x#[default] Toggle session, window and pane information",
1083
  "#[fg=themelightgrey]"
1084
  "          : #[#{E:tree-mode-border-style},acs]x#[default] Run a command for each tagged item",
1085
  "#[fg=themelightgrey]"
1086
  "          f #[#{E:tree-mode-border-style},acs]x#[default] Enter a format",
1087
  "#[fg=themelightgrey]"
1088
  "          H #[#{E:tree-mode-border-style},acs]x#[default] Jump to the starting pane",
1089
  NULL
1090
};
1091
1092
static const char**
1093
window_tree_help(u_int *width, const char **item)
1094
0
{
1095
0
  *width = 51;
1096
0
  *item = "item";
1097
0
  return (window_tree_help_lines);
1098
0
}
1099
1100
static struct screen *
1101
window_tree_init(struct window_mode_entry *wme,
1102
    __unused struct cmdq_item *item, struct cmd_find_state *fs,
1103
    struct args *args)
1104
0
{
1105
0
  struct window_pane    *wp = wme->wp;
1106
0
  struct window_tree_modedata *data;
1107
0
  struct screen     *s;
1108
1109
0
  wme->data = data = xcalloc(1, sizeof *data);
1110
0
  data->wp = wp;
1111
0
  data->references = 1;
1112
1113
0
  if (args_has(args, 's'))
1114
0
    data->type = WINDOW_TREE_SESSION;
1115
0
  else if (args_has(args, 'w'))
1116
0
    data->type = WINDOW_TREE_WINDOW;
1117
0
  else
1118
0
    data->type = WINDOW_TREE_PANE;
1119
0
  memcpy(&data->fs, fs, sizeof data->fs);
1120
1121
0
  if (args == NULL || !args_has(args, 'F'))
1122
0
    data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT);
1123
0
  else
1124
0
    data->format = xstrdup(args_get(args, 'F'));
1125
0
  if (args == NULL || !args_has(args, 'K'))
1126
0
    data->key_format = xstrdup(WINDOW_TREE_DEFAULT_KEY_FORMAT);
1127
0
  else
1128
0
    data->key_format = xstrdup(args_get(args, 'K'));
1129
0
  if (args == NULL || args_count(args) == 0)
1130
0
    data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
1131
0
  else
1132
0
    data->command = xstrdup(args_string(args, 0));
1133
0
  data->squash_groups = !args_has(args, 'G');
1134
0
  data->hide_preview_this_pane = args_has(args, 'h');
1135
0
  if (args_has(args, 'y'))
1136
0
    data->prompt_flags = PROMPT_ACCEPT;
1137
1138
0
  data->data = mode_tree_start(wp, args, window_tree_build,
1139
0
      window_tree_draw, window_tree_search, window_tree_menu, NULL,
1140
0
      window_tree_get_key, window_tree_swap, window_tree_sort,
1141
0
      window_tree_help, data, window_tree_menu_items, &s);
1142
0
  mode_tree_zoom(data->data, args);
1143
0
  mode_tree_view_name(data->data, "preview");
1144
1145
0
  mode_tree_build(data->data);
1146
0
  mode_tree_draw(data->data);
1147
1148
0
  data->type = WINDOW_TREE_NONE;
1149
1150
0
  return (s);
1151
0
}
1152
1153
static void
1154
window_tree_destroy(struct window_tree_modedata *data)
1155
0
{
1156
0
  u_int i;
1157
1158
0
  if (--data->references != 0)
1159
0
    return;
1160
1161
0
  for (i = 0; i < data->item_size; i++)
1162
0
    window_tree_free_item(data->item_list[i]);
1163
0
  free(data->item_list);
1164
1165
0
  free(data->format);
1166
0
  free(data->key_format);
1167
0
  free(data->command);
1168
1169
0
  free(data);
1170
0
}
1171
1172
static void
1173
window_tree_free(struct window_mode_entry *wme)
1174
0
{
1175
0
  struct window_tree_modedata *data = wme->data;
1176
1177
0
  if (data == NULL)
1178
0
    return;
1179
1180
0
  data->dead = 1;
1181
0
  mode_tree_free(data->data);
1182
0
  window_tree_destroy(data);
1183
0
}
1184
1185
static void
1186
window_tree_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
1187
0
{
1188
0
  struct window_tree_modedata *data = wme->data;
1189
1190
0
  mode_tree_resize(data->data, sx, sy);
1191
0
}
1192
1193
static void
1194
window_tree_update(struct window_mode_entry *wme)
1195
0
{
1196
0
  struct window_tree_modedata *data = wme->data;
1197
1198
0
  mode_tree_build(data->data);
1199
0
  mode_tree_draw(data->data);
1200
0
  data->wp->flags |= PANE_REDRAW;
1201
0
}
1202
1203
static char *
1204
window_tree_get_target(struct window_tree_itemdata *item,
1205
    struct cmd_find_state *fs)
1206
0
{
1207
0
  struct session    *s;
1208
0
  struct winlink    *wl;
1209
0
  struct window_pane  *wp;
1210
0
  char      *target;
1211
1212
0
  window_tree_pull_item(item, &s, &wl, &wp);
1213
1214
0
  target = NULL;
1215
0
  switch (item->type) {
1216
0
  case WINDOW_TREE_NONE:
1217
0
    break;
1218
0
  case WINDOW_TREE_SESSION:
1219
0
    if (s == NULL)
1220
0
      break;
1221
0
    xasprintf(&target, "=%s:", s->name);
1222
0
    break;
1223
0
  case WINDOW_TREE_WINDOW:
1224
0
    if (s == NULL || wl == NULL)
1225
0
      break;
1226
0
    xasprintf(&target, "=%s:%u.", s->name, wl->idx);
1227
0
    break;
1228
0
  case WINDOW_TREE_PANE:
1229
0
    if (s == NULL || wl == NULL || wp == NULL)
1230
0
      break;
1231
0
    xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id);
1232
0
    break;
1233
0
  }
1234
0
  if (target == NULL)
1235
0
    cmd_find_clear_state(fs, 0);
1236
0
  else
1237
0
    cmd_find_from_winlink_pane(fs, wl, wp, 0);
1238
0
  return (target);
1239
0
}
1240
1241
static void
1242
window_tree_command_each(void *modedata, void *itemdata, struct client *c,
1243
    __unused key_code key)
1244
0
{
1245
0
  struct window_tree_modedata *data = modedata;
1246
0
  struct window_tree_itemdata *item = itemdata;
1247
0
  char        *name;
1248
0
  struct cmd_find_state    fs;
1249
1250
0
  name = window_tree_get_target(item, &fs);
1251
0
  if (name != NULL)
1252
0
    mode_tree_run_command(c, &fs, data->entered, name);
1253
0
  free(name);
1254
0
}
1255
1256
static enum cmd_retval
1257
window_tree_command_done(__unused struct cmdq_item *item, void *modedata)
1258
0
{
1259
0
  struct window_tree_modedata *data = modedata;
1260
1261
0
  if (!data->dead) {
1262
0
    mode_tree_build(data->data);
1263
0
    mode_tree_draw(data->data);
1264
0
    data->wp->flags |= PANE_REDRAW;
1265
0
  }
1266
0
  window_tree_destroy(data);
1267
0
  return (CMD_RETURN_NORMAL);
1268
0
}
1269
1270
static enum prompt_result
1271
window_tree_command_callback(struct client *c, void *modedata, const char *s,
1272
    __unused enum prompt_key_result key)
1273
0
{
1274
0
  struct window_tree_modedata *data = modedata;
1275
1276
0
  if (s == NULL || *s == '\0' || data->dead)
1277
0
    return (PROMPT_CLOSE);
1278
1279
0
  data->entered = s;
1280
0
  mode_tree_each_tagged(data->data, window_tree_command_each, c,
1281
0
      KEYC_NONE, 1);
1282
0
  data->entered = NULL;
1283
1284
0
  data->references++;
1285
0
  cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
1286
1287
0
  return (PROMPT_CLOSE);
1288
0
}
1289
1290
static void
1291
window_tree_command_free(void *modedata)
1292
0
{
1293
0
  struct window_tree_modedata *data = modedata;
1294
1295
0
  window_tree_destroy(data);
1296
0
}
1297
1298
static void
1299
window_tree_kill_each(__unused void *modedata, void *itemdata,
1300
    __unused struct client *c, __unused key_code key)
1301
0
{
1302
0
  struct window_tree_itemdata *item = itemdata;
1303
0
  struct session      *s;
1304
0
  struct winlink      *wl;
1305
0
  struct window_pane    *wp;
1306
1307
0
  window_tree_pull_item(item, &s, &wl, &wp);
1308
1309
0
  switch (item->type) {
1310
0
  case WINDOW_TREE_NONE:
1311
0
    break;
1312
0
  case WINDOW_TREE_SESSION:
1313
0
    if (s != NULL) {
1314
0
      server_destroy_session(s);
1315
0
      session_destroy(s, 1, __func__);
1316
0
    }
1317
0
    break;
1318
0
  case WINDOW_TREE_WINDOW:
1319
0
    if (wl != NULL)
1320
0
      server_kill_window(wl->window, 0);
1321
0
    break;
1322
0
  case WINDOW_TREE_PANE:
1323
0
    if (wp != NULL)
1324
0
      server_kill_pane(wp);
1325
0
    break;
1326
0
  }
1327
0
}
1328
1329
static enum prompt_result
1330
window_tree_kill_current_callback(struct client *c, void *modedata,
1331
    const char *s, __unused enum prompt_key_result key)
1332
0
{
1333
0
  struct window_tree_modedata *data = modedata;
1334
0
  struct mode_tree_data   *mtd = data->data;
1335
1336
0
  if (s == NULL || *s == '\0' || data->dead)
1337
0
    return (PROMPT_CLOSE);
1338
0
  if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
1339
0
    return (PROMPT_CLOSE);
1340
1341
0
  window_tree_kill_each(data, mode_tree_get_current(mtd), c, KEYC_NONE);
1342
0
  server_renumber_all();
1343
1344
0
  data->references++;
1345
0
  cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
1346
1347
0
  return (PROMPT_CLOSE);
1348
0
}
1349
1350
static enum prompt_result
1351
window_tree_kill_tagged_callback(struct client *c, void *modedata,
1352
    const char *s, __unused enum prompt_key_result key)
1353
0
{
1354
0
  struct window_tree_modedata *data = modedata;
1355
0
  struct mode_tree_data   *mtd = data->data;
1356
1357
0
  if (s == NULL || *s == '\0' || data->dead)
1358
0
    return (PROMPT_CLOSE);
1359
0
  if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
1360
0
    return (PROMPT_CLOSE);
1361
1362
0
  mode_tree_each_tagged(mtd, window_tree_kill_each, c, KEYC_NONE, 1);
1363
0
  server_renumber_all();
1364
1365
0
  data->references++;
1366
0
  cmdq_append(c, cmdq_get_callback(window_tree_command_done, data));
1367
1368
0
  return (PROMPT_CLOSE);
1369
0
}
1370
1371
static key_code
1372
window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x,
1373
    struct window_tree_itemdata *item)
1374
0
{
1375
0
  struct session    *s;
1376
0
  struct winlink    *wl;
1377
0
  struct window_pane  *wp;
1378
0
  u_int      loop;
1379
1380
0
  if (key != KEYC_MOUSEDOWN1_PANE)
1381
0
    return (KEYC_NONE);
1382
1383
0
  if (data->left != -1 && x <= (u_int)data->left)
1384
0
    return ('<');
1385
0
  if (data->right != -1 && x >= (u_int)data->right)
1386
0
    return ('>');
1387
1388
0
  if (data->left != -1)
1389
0
    x -= data->left;
1390
0
  else if (x != 0)
1391
0
    x--;
1392
0
  if (x == 0 || data->end == 0)
1393
0
    x = 0;
1394
0
  else {
1395
0
    x = x / data->each;
1396
0
    if (data->start + x >= data->end)
1397
0
      x = data->end - 1;
1398
0
  }
1399
1400
0
  window_tree_pull_item(item, &s, &wl, &wp);
1401
0
  if (item->type == WINDOW_TREE_SESSION) {
1402
0
    if (s == NULL)
1403
0
      return (KEYC_NONE);
1404
0
    mode_tree_expand_current(data->data);
1405
0
    loop = 0;
1406
0
    RB_FOREACH(wl, winlinks, &s->windows) {
1407
0
      if (loop == data->start + x)
1408
0
        break;
1409
0
      loop++;
1410
0
    }
1411
0
    if (wl != NULL)
1412
0
      mode_tree_set_current(data->data, (uint64_t)wl);
1413
0
    return ('\r');
1414
0
  }
1415
0
  if (item->type == WINDOW_TREE_WINDOW) {
1416
0
    if (wl == NULL)
1417
0
      return (KEYC_NONE);
1418
0
    mode_tree_expand_current(data->data);
1419
0
    loop = 0;
1420
0
    TAILQ_FOREACH(wp, &wl->window->panes, entry) {
1421
0
      if (loop == data->start + x)
1422
0
        break;
1423
0
      loop++;
1424
0
    }
1425
0
    if (wp != NULL)
1426
0
      mode_tree_set_current(data->data, (uint64_t)wp);
1427
0
    return ('\r');
1428
0
  }
1429
0
  return (KEYC_NONE);
1430
0
}
1431
1432
static void
1433
window_tree_key(struct window_mode_entry *wme, struct client *c,
1434
    __unused struct session *s, __unused struct winlink *wl, key_code key,
1435
    struct mouse_event *m)
1436
0
{
1437
0
  struct window_pane    *wp = wme->wp;
1438
0
  struct window_tree_modedata *data = wme->data;
1439
0
  struct window_tree_itemdata *item, *new_item;
1440
0
  char        *name, *prompt = NULL;
1441
0
  struct cmd_find_state    fs, *fsp = &data->fs;
1442
0
  int        finished;
1443
0
  u_int        tagged, x, y, idx;
1444
0
  struct session      *ns;
1445
0
  struct winlink      *nwl;
1446
0
  struct window_pane    *nwp;
1447
1448
0
  item = mode_tree_get_current(data->data);
1449
0
  finished = mode_tree_key(data->data, c, &key, m, &x, &y);
1450
1451
0
again:
1452
0
  if (item != (new_item = mode_tree_get_current(data->data))) {
1453
0
    item = new_item;
1454
0
    data->offset = 0;
1455
0
  }
1456
0
  if (KEYC_IS_MOUSE(key) && m != NULL) {
1457
0
    key = window_tree_mouse(data, key, x, item);
1458
0
    goto again;
1459
0
  }
1460
1461
0
  switch (key) {
1462
0
  case '<':
1463
0
    data->offset--;
1464
0
    break;
1465
0
  case '>':
1466
0
    data->offset++;
1467
0
    break;
1468
0
  case 'H':
1469
0
    mode_tree_expand(data->data, (uint64_t)fsp->s);
1470
0
    mode_tree_expand(data->data, (uint64_t)fsp->wl);
1471
0
    if (!mode_tree_set_current(data->data, (uint64_t)wme->wp))
1472
0
      mode_tree_set_current(data->data, (uint64_t)fsp->wl);
1473
0
    break;
1474
0
  case 'm':
1475
0
    window_tree_pull_item(item, &ns, &nwl, &nwp);
1476
0
    server_set_marked(ns, nwl, nwp);
1477
0
    mode_tree_build(data->data);
1478
0
    break;
1479
0
  case 'M':
1480
0
    server_clear_marked();
1481
0
    mode_tree_build(data->data);
1482
0
    break;
1483
0
  case 'i':
1484
0
    data->preview_is_info = !data->preview_is_info;
1485
0
    if (data->preview_is_info)
1486
0
      mode_tree_view_name(data->data, "info");
1487
0
    else
1488
0
      mode_tree_view_name(data->data, "preview");
1489
0
    break;
1490
0
  case 'x':
1491
0
    window_tree_pull_item(item, &ns, &nwl, &nwp);
1492
0
    switch (item->type) {
1493
0
    case WINDOW_TREE_NONE:
1494
0
      break;
1495
0
    case WINDOW_TREE_SESSION:
1496
0
      if (ns == NULL)
1497
0
        break;
1498
0
      xasprintf(&prompt, "Kill session %s? ", ns->name);
1499
0
      break;
1500
0
    case WINDOW_TREE_WINDOW:
1501
0
      if (nwl == NULL)
1502
0
        break;
1503
0
      xasprintf(&prompt, "Kill window %u? ", nwl->idx);
1504
0
      break;
1505
0
    case WINDOW_TREE_PANE:
1506
0
      if (nwp == NULL || window_pane_index(nwp, &idx) != 0)
1507
0
        break;
1508
0
      xasprintf(&prompt, "Kill pane %u? ", idx);
1509
0
      break;
1510
0
    }
1511
0
    if (prompt == NULL)
1512
0
      break;
1513
0
    data->references++;
1514
0
    mode_tree_set_prompt(data->data, c, prompt, "",
1515
0
        PROMPT_TYPE_COMMAND,
1516
0
        PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
1517
0
        window_tree_kill_current_callback, window_tree_command_free,
1518
0
        data);
1519
0
    free(prompt);
1520
0
    break;
1521
0
  case 'X':
1522
0
    tagged = mode_tree_count_tagged(data->data);
1523
0
    if (tagged == 0)
1524
0
      break;
1525
0
    xasprintf(&prompt, "Kill %u tagged? ", tagged);
1526
0
    data->references++;
1527
0
    mode_tree_set_prompt(data->data, c, prompt, "",
1528
0
        PROMPT_TYPE_COMMAND,
1529
0
        PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
1530
0
        window_tree_kill_tagged_callback, window_tree_command_free,
1531
0
        data);
1532
0
    free(prompt);
1533
0
    break;
1534
0
  case ':':
1535
0
    tagged = mode_tree_count_tagged(data->data);
1536
0
    if (tagged != 0)
1537
0
      xasprintf(&prompt, "(%u tagged) ", tagged);
1538
0
    else
1539
0
      xasprintf(&prompt, "(current) ");
1540
0
    data->references++;
1541
0
    mode_tree_set_prompt(data->data, c, prompt, "",
1542
0
        PROMPT_TYPE_COMMAND, PROMPT_NOFORMAT,
1543
0
        window_tree_command_callback, window_tree_command_free,
1544
0
        data);
1545
0
    free(prompt);
1546
0
    break;
1547
0
  case '\r':
1548
0
    name = window_tree_get_target(item, &fs);
1549
0
    if (name != NULL)
1550
0
      mode_tree_run_command(c, NULL, data->command, name);
1551
0
    finished = 1;
1552
0
    free(name);
1553
0
    break;
1554
0
  }
1555
0
  if (finished)
1556
0
    window_pane_reset_mode(wp);
1557
0
  else {
1558
0
    mode_tree_draw(data->data);
1559
0
    wp->flags |= PANE_REDRAW;
1560
0
  }
1561
0
}