Change test scripts to always wait for previous command to finish
[tig.git] / src / draw.c
blob49fc14dd0362d72654f792ee0275f73eb129a223
1 /* Copyright (c) 2006-2014 Jonas Fonseca <jonas.fonseca@gmail.com>
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of
6 * the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include "tig/tig.h"
15 #include "tig/graph.h"
16 #include "tig/draw.h"
17 #include "tig/options.h"
19 static const enum line_type palette_colors[] = {
20 LINE_PALETTE_0,
21 LINE_PALETTE_1,
22 LINE_PALETTE_2,
23 LINE_PALETTE_3,
24 LINE_PALETTE_4,
25 LINE_PALETTE_5,
26 LINE_PALETTE_6,
27 LINE_PALETTE_7,
28 LINE_PALETTE_8,
29 LINE_PALETTE_9,
30 LINE_PALETTE_10,
31 LINE_PALETTE_11,
32 LINE_PALETTE_12,
33 LINE_PALETTE_13,
37 * View drawing.
40 static inline void
41 set_view_attr(struct view *view, enum line_type type)
43 if (!view->curline->selected && view->curtype != type) {
44 (void) wattrset(view->win, get_view_attr(view, type));
45 wchgat(view->win, -1, 0, get_view_color(view, type), NULL);
46 view->curtype = type;
50 #define VIEW_MAX_LEN(view) ((view)->width + (view)->pos.col - (view)->col)
52 static bool
53 draw_chars(struct view *view, enum line_type type, const char *string,
54 int max_len, bool use_tilde)
56 int len = 0;
57 int col = 0;
58 int trimmed = FALSE;
59 size_t skip = view->pos.col > view->col ? view->pos.col - view->col : 0;
61 if (max_len <= 0)
62 return VIEW_MAX_LEN(view) <= 0;
64 len = utf8_length(&string, skip, &col, max_len, &trimmed, use_tilde, opt_tab_size);
66 if (opt_iconv_out != ICONV_NONE) {
67 string = encoding_iconv(opt_iconv_out, string, len);
68 if (!string)
69 return VIEW_MAX_LEN(view) <= 0;
72 set_view_attr(view, type);
73 if (len > 0) {
74 waddnstr(view->win, string, len);
76 if (trimmed && use_tilde) {
77 set_view_attr(view, LINE_DELIMITER);
78 waddch(view->win, '~');
79 col++;
83 view->col += col;
84 return VIEW_MAX_LEN(view) <= 0;
87 static bool
88 draw_space(struct view *view, enum line_type type, int max, int spaces)
90 static char space[] = " ";
92 spaces = MIN(max, spaces);
94 while (spaces > 0) {
95 int len = MIN(spaces, sizeof(space) - 1);
97 if (draw_chars(view, type, space, len, FALSE))
98 return TRUE;
99 spaces -= len;
102 return VIEW_MAX_LEN(view) <= 0;
105 static bool
106 draw_text_expanded(struct view *view, enum line_type type, const char *string, int max_len, bool use_tilde)
108 static char text[SIZEOF_STR];
110 do {
111 size_t pos = string_expand(text, sizeof(text), string, opt_tab_size);
113 if (draw_chars(view, type, text, max_len, use_tilde))
114 return TRUE;
115 string += pos;
116 } while (*string);
118 return VIEW_MAX_LEN(view) <= 0;
121 bool
122 draw_text(struct view *view, enum line_type type, const char *string)
124 return draw_text_expanded(view, type, string, VIEW_MAX_LEN(view), FALSE);
127 static bool
128 draw_text_overflow(struct view *view, const char *text, enum line_type type,
129 int overflow_length, int offset)
131 bool on = overflow_length > 0;
133 if (on) {
134 int overflow = overflow_length + offset;
135 int max = MIN(VIEW_MAX_LEN(view), overflow);
136 int len = strlen(text);
138 if (draw_text_expanded(view, type, text, max, max < overflow))
139 return TRUE;
141 text = len > overflow ? text + overflow : "";
142 type = LINE_OVERFLOW;
145 if (*text && draw_text(view, type, text))
146 return TRUE;
148 return VIEW_MAX_LEN(view) <= 0;
151 bool PRINTF_LIKE(3, 4)
152 draw_formatted(struct view *view, enum line_type type, const char *format, ...)
154 char text[SIZEOF_STR];
155 int retval;
157 FORMAT_BUFFER(text, sizeof(text), format, retval, TRUE);
158 return retval >= 0 ? draw_text(view, type, text) : VIEW_MAX_LEN(view) <= 0;
161 bool
162 draw_graphic(struct view *view, enum line_type type, const chtype graphic[], size_t size, bool separator)
164 size_t skip = view->pos.col > view->col ? view->pos.col - view->col : 0;
165 int max = VIEW_MAX_LEN(view);
166 int i;
168 if (max < size)
169 size = max;
171 set_view_attr(view, type);
172 /* Using waddch() instead of waddnstr() ensures that
173 * they'll be rendered correctly for the cursor line. */
174 for (i = skip; i < size; i++)
175 waddch(view->win, graphic[i]);
177 view->col += size;
178 if (separator) {
179 if (size < max && skip <= size)
180 waddch(view->win, ' ');
181 view->col++;
184 return VIEW_MAX_LEN(view) <= 0;
187 bool
188 draw_field(struct view *view, enum line_type type, const char *text, int width, enum align align, bool trim)
190 int max = MIN(VIEW_MAX_LEN(view), width + 1);
191 int col = view->col;
193 if (!text)
194 return draw_space(view, type, max, max);
196 if (align == ALIGN_RIGHT) {
197 int textlen = utf8_width_max(text, max);
198 int leftpad = max - textlen - 1;
200 if (leftpad > 0) {
201 if (draw_space(view, type, leftpad, leftpad))
202 return TRUE;
203 max -= leftpad;
204 col += leftpad;;
208 return draw_chars(view, type, text, max - 1, trim)
209 || draw_space(view, LINE_DEFAULT, max - (view->col - col), max);
212 static bool
213 draw_date(struct view *view, struct view_column *column, const struct time *time)
215 enum date date = column->opt.date.display;
216 const char *text = mkdate(time, date);
217 enum align align = date == DATE_RELATIVE ? ALIGN_RIGHT : ALIGN_LEFT;
219 if (date == DATE_NO)
220 return FALSE;
222 return draw_field(view, LINE_DATE, text, column->width, align, FALSE);
225 static bool
226 draw_author(struct view *view, struct view_column *column, const struct ident *author)
228 bool trim = author_trim(column->width);
229 const char *text = mkauthor(author, column->opt.author.width, column->opt.author.display);
231 if (column->opt.author.display == AUTHOR_NO)
232 return FALSE;
234 return draw_field(view, LINE_AUTHOR, text, column->width, ALIGN_LEFT, trim);
237 static bool
238 draw_id(struct view *view, struct view_column *column, const char *id)
240 enum line_type type = LINE_ID;
242 if (!column->opt.id.display)
243 return FALSE;
245 if (column->opt.id.color && id) {
246 hashval_t color = iterative_hash(id, SIZEOF_REV - 1, 0);
248 type = palette_colors[color % ARRAY_SIZE(palette_colors)];
251 return draw_field(view, type, id, column->width, ALIGN_LEFT, FALSE);
254 static bool
255 draw_filename(struct view *view, struct view_column *column, const char *filename, mode_t mode)
257 size_t width = filename ? utf8_width(filename) : 0;
258 bool trim = width >= column->width;
259 enum line_type type = S_ISDIR(mode) ? LINE_DIRECTORY : LINE_FILE;
260 int column_width = column->width ? column->width : width;
262 if (column->opt.file_name.display == FILENAME_NO)
263 return FALSE;
265 return draw_field(view, type, filename, column_width, ALIGN_LEFT, trim);
268 static bool
269 draw_file_size(struct view *view, struct view_column *column, unsigned long size, mode_t mode)
271 const char *str = S_ISDIR(mode) ? NULL : mkfilesize(size, column->opt.file_size.display);
273 if (!column->width || column->opt.file_size.display == FILE_SIZE_NO)
274 return FALSE;
276 return draw_field(view, LINE_FILE_SIZE, str, column->width, ALIGN_RIGHT, FALSE);
279 static bool
280 draw_mode(struct view *view, struct view_column *column, mode_t mode)
282 const char *str = mkmode(mode);
284 if (!column->width || !column->opt.mode.display)
285 return FALSE;
287 return draw_field(view, LINE_MODE, str, column->width, ALIGN_LEFT, FALSE);
290 static bool
291 draw_lineno_custom(struct view *view, struct view_column *column, unsigned int lineno)
293 char number[10];
294 unsigned long digits3 = column->width < 3 ? 3 : column->width;
295 int max = MIN(VIEW_MAX_LEN(view), digits3);
296 char *text = NULL;
297 chtype separator = opt_line_graphics ? ACS_VLINE : '|';
299 if (!column->opt.line_number.display)
300 return FALSE;
302 if (lineno == 1 || (lineno % column->opt.line_number.interval) == 0) {
303 static char fmt[] = "%ld";
305 fmt[1] = '0' + (digits3 <= 9 ? digits3 : 1);
306 if (string_format(number, fmt, lineno))
307 text = number;
309 if (text)
310 draw_chars(view, LINE_LINE_NUMBER, text, max, TRUE);
311 else
312 draw_space(view, LINE_LINE_NUMBER, max, digits3);
313 return draw_graphic(view, LINE_DEFAULT, &separator, 1, TRUE);
316 bool
317 draw_lineno(struct view *view, struct view_column *column, unsigned int lineno)
319 lineno += view->pos.offset + 1;
320 return draw_lineno_custom(view, column, lineno);
323 static bool
324 draw_ref(struct view *view, struct view_column *column, const struct ref *ref)
326 enum line_type type = !ref || !ref->valid ? LINE_DEFAULT : get_line_type_from_ref(ref);
327 const char *name = ref ? ref->name : NULL;
329 return draw_field(view, type, name, column->width, ALIGN_LEFT, FALSE);
332 static bool
333 draw_refs(struct view *view, struct view_column *column, const struct ref_list *refs)
335 size_t i;
337 if (!column->opt.commit_title.refs || !refs)
338 return FALSE;
340 for (i = 0; i < refs->size; i++) {
341 struct ref *ref = refs->refs[i];
342 enum line_type type = get_line_type_from_ref(ref);
343 const struct ref_format *format = get_ref_format(ref);
345 if (!strcmp(format->start, "hide:") && !*format->end)
346 continue;
348 if (draw_formatted(view, type, "%s%s%s", format->start, ref->name, format->end))
349 return TRUE;
351 if (draw_text(view, LINE_DEFAULT, " "))
352 return TRUE;
355 return FALSE;
358 static bool
359 draw_status(struct view *view, struct view_column *column,
360 enum line_type type, const char *status)
362 const char *label = mkstatus(status ? *status : 0, column->opt.status.display);
364 return draw_field(view, type, label, column->width, ALIGN_LEFT, FALSE);
368 * Revision graph
371 static enum line_type get_graph_color(struct graph_symbol *symbol)
373 if (symbol->commit)
374 return LINE_GRAPH_COMMIT;
375 assert(symbol->color < ARRAY_SIZE(palette_colors));
376 return palette_colors[symbol->color];
379 static bool
380 draw_graph_utf8(struct view *view, struct graph_symbol *symbol, enum line_type color, bool first)
382 const char *chars = graph_symbol_to_utf8(symbol);
384 return draw_text(view, color, chars + !!first);
387 static bool
388 draw_graph_ascii(struct view *view, struct graph_symbol *symbol, enum line_type color, bool first)
390 const char *chars = graph_symbol_to_ascii(symbol);
392 return draw_text(view, color, chars + !!first);
395 static bool
396 draw_graph_chtype(struct view *view, struct graph_symbol *symbol, enum line_type color, bool first)
398 const chtype *chars = graph_symbol_to_chtype(symbol);
400 return draw_graphic(view, color, chars + !!first, 2 - !!first, FALSE);
403 typedef bool (*draw_graph_fn)(struct view *, struct graph_symbol *, enum line_type, bool);
405 static bool
406 draw_graph(struct view *view, const struct graph_canvas *canvas)
408 static const draw_graph_fn fns[] = {
409 draw_graph_ascii,
410 draw_graph_chtype,
411 draw_graph_utf8
413 draw_graph_fn fn = fns[opt_line_graphics];
414 int i;
416 for (i = 0; i < canvas->size; i++) {
417 struct graph_symbol *symbol = &canvas->symbols[i];
418 enum line_type color = get_graph_color(symbol);
420 if (fn(view, symbol, color, i == 0))
421 return TRUE;
424 return draw_text(view, LINE_DEFAULT, " ");
427 static bool
428 draw_commit_title(struct view *view, struct view_column *column,
429 const struct graph_canvas *graph, const struct ref_list *refs,
430 const char *commit_title)
432 if (graph && column->opt.commit_title.graph &&
433 draw_graph(view, graph))
434 return TRUE;
435 if (draw_refs(view, column, refs))
436 return TRUE;
437 return draw_text_overflow(view, commit_title, LINE_DEFAULT,
438 column->opt.commit_title.overflow, 0);
441 static bool
442 draw_diff_stat_part(struct view *view, enum line_type *type, const char **text, char c, enum line_type next_type)
444 const char *sep = c == '|' ? strrchr(*text, c) : strchr(*text, c);
446 if (sep != NULL) {
447 draw_text_expanded(view, *type, *text, sep - *text, FALSE);
448 *text = sep;
449 *type = next_type;
452 return sep != NULL;
455 static void
456 draw_diff_stat(struct view *view, enum line_type *type, const char **text)
458 draw_diff_stat_part(view, type, text, '|', LINE_DEFAULT);
459 if (draw_diff_stat_part(view, type, text, 'B', LINE_DEFAULT)) {
460 /* Handle binary diffstat: Bin <deleted> -> <added> bytes */
461 draw_diff_stat_part(view, type, text, ' ', LINE_DIFF_DEL);
462 draw_diff_stat_part(view, type, text, '-', LINE_DEFAULT);
463 draw_diff_stat_part(view, type, text, ' ', LINE_DIFF_ADD);
464 draw_diff_stat_part(view, type, text, 'b', LINE_DEFAULT);
466 } else {
467 draw_diff_stat_part(view, type, text, '+', LINE_DIFF_ADD);
468 draw_diff_stat_part(view, type, text, '-', LINE_DIFF_DEL);
472 bool
473 view_column_draw(struct view *view, struct line *line, unsigned int lineno)
475 struct view_column *column = view->columns;
476 struct view_column_data column_data = {};
478 if (!view->ops->get_column_data(view, line, &column_data))
479 return TRUE;
481 if (column_data.section)
482 column = column_data.section;
484 for (; column; column = column->next) {
485 mode_t mode = column_data.mode ? *column_data.mode : 0;
487 if (column->hidden)
488 continue;
490 switch (column->type) {
491 case VIEW_COLUMN_DATE:
492 if (draw_date(view, column, column_data.date))
493 return TRUE;
494 continue;
496 case VIEW_COLUMN_AUTHOR:
497 if (draw_author(view, column, column_data.author))
498 return TRUE;
499 continue;
501 case VIEW_COLUMN_REF:
502 if (draw_ref(view, column, column_data.ref))
503 return TRUE;
504 continue;
506 case VIEW_COLUMN_ID:
507 if (draw_id(view, column, column_data.reflog ? column_data.reflog : column_data.id))
508 return TRUE;
509 continue;
511 case VIEW_COLUMN_LINE_NUMBER:
512 if (draw_lineno(view, column, column_data.line_number ? *column_data.line_number : lineno))
513 return TRUE;
514 continue;
516 case VIEW_COLUMN_MODE:
517 if (draw_mode(view, column, mode))
518 return TRUE;
519 continue;
521 case VIEW_COLUMN_FILE_SIZE:
522 if (draw_file_size(view, column, column_data.file_size ? *column_data.file_size : 0, mode))
523 return TRUE;
524 continue;
526 case VIEW_COLUMN_COMMIT_TITLE:
527 if (draw_commit_title(view, column, column_data.graph,
528 column_data.refs, column_data.commit_title))
529 return TRUE;
530 continue;
532 case VIEW_COLUMN_FILE_NAME:
533 if (draw_filename(view, column, column_data.file_name, mode))
534 return TRUE;
535 continue;
537 case VIEW_COLUMN_SECTION:
538 if (draw_text(view, column->opt.section.type, column->opt.section.text))
539 return TRUE;
540 continue;
542 case VIEW_COLUMN_STATUS:
543 if (draw_status(view, column, line->type, column_data.status))
544 return TRUE;
545 continue;
547 case VIEW_COLUMN_TEXT:
549 enum line_type type = line->type;
550 const char *text = column_data.text;
552 if (line->wrapped && draw_text(view, LINE_DELIMITER, "+"))
553 return TRUE;
555 if (line->graph_indent) {
556 size_t indent = get_graph_indent(text);
558 if (draw_text_expanded(view, LINE_DEFAULT, text, indent, FALSE))
559 return TRUE;
560 text += indent;
562 if (type == LINE_DIFF_STAT)
563 draw_diff_stat(view, &type, &text);
564 if (line->commit_title) {
565 if (draw_text_overflow(view, text, LINE_DEFAULT,
566 column->opt.text.commit_title_overflow, 4))
567 return TRUE;
568 } else if (draw_text(view, type, text)) {
569 return TRUE;
572 continue;
576 return TRUE;
579 bool
580 draw_view_line(struct view *view, unsigned int lineno)
582 struct line *line;
583 bool selected = (view->pos.offset + lineno == view->pos.lineno);
585 /* FIXME: Disabled during code split.
586 assert(view_is_displayed(view));
589 if (view->pos.offset + lineno >= view->lines)
590 return FALSE;
592 line = &view->line[view->pos.offset + lineno];
594 wmove(view->win, lineno, 0);
595 if (line->cleareol)
596 wclrtoeol(view->win);
597 view->col = 0;
598 view->curline = line;
599 view->curtype = LINE_NONE;
600 line->selected = FALSE;
601 line->dirty = line->cleareol = 0;
603 if (selected) {
604 set_view_attr(view, LINE_CURSOR);
605 line->selected = TRUE;
606 view->ops->select(view, line);
609 return view->ops->draw(view, line, lineno);
612 void
613 redraw_view_dirty(struct view *view)
615 bool dirty = FALSE;
616 int lineno;
618 for (lineno = 0; lineno < view->height; lineno++) {
619 if (view->pos.offset + lineno >= view->lines)
620 break;
621 if (!view->line[view->pos.offset + lineno].dirty)
622 continue;
623 dirty = TRUE;
624 if (!draw_view_line(view, lineno))
625 break;
628 if (!dirty)
629 return;
630 wnoutrefresh(view->win);
633 void
634 redraw_view_from(struct view *view, int lineno)
636 assert(0 <= lineno && lineno < view->height);
638 if (view->columns && view_column_info_changed(view, FALSE)) {
639 int i;
641 view_column_reset(view);
642 for (i = 0; i < view->lines; i++) {
643 view_column_info_update(view, &view->line[i]);
647 for (; lineno < view->height; lineno++) {
648 if (!draw_view_line(view, lineno))
649 break;
652 wnoutrefresh(view->win);
655 void
656 redraw_view(struct view *view)
658 werase(view->win);
659 redraw_view_from(view, 0);
662 /* vim: set ts=8 sw=8 noexpandtab: */