Partly restore old refresh behavior
[tig.git] / src / draw.c
blob7789169b3f4055b898d950a1b9bde3e2848fea58
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"
20 * View drawing.
23 static inline void
24 set_view_attr(struct view *view, enum line_type type)
26 if (!view->curline->selected && view->curtype != type) {
27 (void) wattrset(view->win, get_view_attr(view, type));
28 wchgat(view->win, -1, 0, get_view_color(view, type), NULL);
29 view->curtype = type;
33 #define VIEW_MAX_LEN(view) ((view)->width + (view)->pos.col - (view)->col)
35 static bool
36 draw_chars(struct view *view, enum line_type type, const char *string,
37 int max_len, bool use_tilde)
39 int len = 0;
40 int col = 0;
41 int trimmed = FALSE;
42 size_t skip = view->pos.col > view->col ? view->pos.col - view->col : 0;
44 if (max_len <= 0)
45 return VIEW_MAX_LEN(view) <= 0;
47 len = utf8_length(&string, skip, &col, max_len, &trimmed, use_tilde, opt_tab_size);
49 if (opt_iconv_out != ICONV_NONE) {
50 string = encoding_iconv(opt_iconv_out, string, len);
51 if (!string)
52 return VIEW_MAX_LEN(view) <= 0;
55 set_view_attr(view, type);
56 if (len > 0) {
57 waddnstr(view->win, string, len);
59 if (trimmed && use_tilde) {
60 set_view_attr(view, LINE_DELIMITER);
61 waddch(view->win, '~');
62 col++;
66 view->col += col;
67 return VIEW_MAX_LEN(view) <= 0;
70 static bool
71 draw_space(struct view *view, enum line_type type, int max, int spaces)
73 static char space[] = " ";
75 spaces = MIN(max, spaces);
77 while (spaces > 0) {
78 int len = MIN(spaces, sizeof(space) - 1);
80 if (draw_chars(view, type, space, len, FALSE))
81 return TRUE;
82 spaces -= len;
85 return VIEW_MAX_LEN(view) <= 0;
88 static bool
89 draw_text_expanded(struct view *view, enum line_type type, const char *string, int max_len, bool use_tilde)
91 static char text[SIZEOF_STR];
93 do {
94 size_t pos = string_expand(text, sizeof(text), string, opt_tab_size);
96 if (draw_chars(view, type, text, max_len, use_tilde))
97 return TRUE;
98 string += pos;
99 } while (*string);
101 return VIEW_MAX_LEN(view) <= 0;
104 bool
105 draw_text(struct view *view, enum line_type type, const char *string)
107 return draw_text_expanded(view, type, string, VIEW_MAX_LEN(view), FALSE);
110 static bool
111 draw_text_overflow(struct view *view, const char *text, enum line_type type,
112 int overflow_length, int offset)
114 bool on = overflow_length > 0;
116 if (on) {
117 int overflow = overflow_length + offset;
118 int max = MIN(VIEW_MAX_LEN(view), overflow);
119 int len = strlen(text);
121 if (draw_text_expanded(view, type, text, max, max < overflow))
122 return TRUE;
124 text = len > overflow ? text + overflow : "";
125 type = LINE_OVERFLOW;
128 if (*text && draw_text(view, type, text))
129 return TRUE;
131 return VIEW_MAX_LEN(view) <= 0;
134 bool PRINTF_LIKE(3, 4)
135 draw_formatted(struct view *view, enum line_type type, const char *format, ...)
137 char text[SIZEOF_STR];
138 int retval;
140 FORMAT_BUFFER(text, sizeof(text), format, retval, TRUE);
141 return retval >= 0 ? draw_text(view, type, text) : VIEW_MAX_LEN(view) <= 0;
144 bool
145 draw_graphic(struct view *view, enum line_type type, const chtype graphic[], size_t size, bool separator)
147 size_t skip = view->pos.col > view->col ? view->pos.col - view->col : 0;
148 int max = VIEW_MAX_LEN(view);
149 int i;
151 if (max < size)
152 size = max;
154 set_view_attr(view, type);
155 /* Using waddch() instead of waddnstr() ensures that
156 * they'll be rendered correctly for the cursor line. */
157 for (i = skip; i < size; i++)
158 waddch(view->win, graphic[i]);
160 view->col += size;
161 if (separator) {
162 if (size < max && skip <= size)
163 waddch(view->win, ' ');
164 view->col++;
167 return VIEW_MAX_LEN(view) <= 0;
170 bool
171 draw_field(struct view *view, enum line_type type, const char *text, int width, enum align align, bool trim)
173 int max = MIN(VIEW_MAX_LEN(view), width + 1);
174 int col = view->col;
176 if (!text)
177 return draw_space(view, type, max, max);
179 if (align == ALIGN_RIGHT) {
180 int textlen = utf8_width_max(text, max);
181 int leftpad = max - textlen - 1;
183 if (leftpad > 0) {
184 if (draw_space(view, type, leftpad, leftpad))
185 return TRUE;
186 max -= leftpad;
187 col += leftpad;;
191 return draw_chars(view, type, text, max - 1, trim)
192 || draw_space(view, LINE_DEFAULT, max - (view->col - col), max);
195 static bool
196 draw_date(struct view *view, struct view_column *column, const struct time *time)
198 enum date date = column->opt.date.display;
199 const char *text = mkdate(time, date);
200 enum align align = date == DATE_RELATIVE ? ALIGN_RIGHT : ALIGN_LEFT;
202 if (date == DATE_NO)
203 return FALSE;
205 return draw_field(view, LINE_DATE, text, column->width, align, FALSE);
208 static bool
209 draw_author(struct view *view, struct view_column *column, const struct ident *author)
211 bool trim = author_trim(column->width);
212 const char *text = mkauthor(author, column->opt.author.width, column->opt.author.display);
214 if (column->opt.author.display == AUTHOR_NO)
215 return FALSE;
217 return draw_field(view, LINE_AUTHOR, text, column->width, ALIGN_LEFT, trim);
220 static bool
221 draw_id(struct view *view, struct view_column *column, const char *id)
223 static const enum line_type colors[] = {
224 LINE_PALETTE_0,
225 LINE_PALETTE_1,
226 LINE_PALETTE_2,
227 LINE_PALETTE_3,
228 LINE_PALETTE_4,
229 LINE_PALETTE_5,
230 LINE_PALETTE_6,
231 LINE_PALETTE_7,
232 LINE_PALETTE_8,
233 LINE_PALETTE_9,
234 LINE_PALETTE_10,
235 LINE_PALETTE_11,
236 LINE_PALETTE_12,
237 LINE_PALETTE_13,
239 enum line_type type = LINE_ID;
241 if (!column->opt.id.display)
242 return FALSE;
244 if (column->opt.id.color && id) {
245 hashval_t color = iterative_hash(id, SIZEOF_REV - 1, 0);
247 type = colors[color % ARRAY_SIZE(colors)];
250 return draw_field(view, type, id, column->width, ALIGN_LEFT, FALSE);
253 static bool
254 draw_filename(struct view *view, struct view_column *column, const char *filename, mode_t mode)
256 size_t width = filename ? utf8_width(filename) : 0;
257 bool trim = width >= column->width;
258 enum line_type type = S_ISDIR(mode) ? LINE_DIRECTORY : LINE_FILE;
259 int column_width = column->width ? column->width : width;
261 if (column->opt.file_name.display == FILENAME_NO)
262 return FALSE;
264 return draw_field(view, type, filename, column_width, ALIGN_LEFT, trim);
267 static bool
268 draw_file_size(struct view *view, struct view_column *column, unsigned long size, mode_t mode)
270 const char *str = S_ISDIR(mode) ? NULL : mkfilesize(size, column->opt.file_size.display);
272 if (!column->width || column->opt.file_size.display == FILE_SIZE_NO)
273 return FALSE;
275 return draw_field(view, LINE_FILE_SIZE, str, column->width, ALIGN_RIGHT, FALSE);
278 static bool
279 draw_mode(struct view *view, struct view_column *column, mode_t mode)
281 const char *str = mkmode(mode);
283 if (!column->width || !column->opt.mode.display)
284 return FALSE;
286 return draw_field(view, LINE_MODE, str, column->width, ALIGN_LEFT, FALSE);
289 static bool
290 draw_lineno_custom(struct view *view, struct view_column *column, unsigned int lineno)
292 char number[10];
293 unsigned long digits3 = column->width < 3 ? 3 : column->width;
294 int max = MIN(VIEW_MAX_LEN(view), digits3);
295 char *text = NULL;
296 chtype separator = opt_line_graphics ? ACS_VLINE : '|';
298 if (!column->opt.line_number.display)
299 return FALSE;
301 if (lineno == 1 || (lineno % column->opt.line_number.interval) == 0) {
302 static char fmt[] = "%ld";
304 fmt[1] = '0' + (digits3 <= 9 ? digits3 : 1);
305 if (string_format(number, fmt, lineno))
306 text = number;
308 if (text)
309 draw_chars(view, LINE_LINE_NUMBER, text, max, TRUE);
310 else
311 draw_space(view, LINE_LINE_NUMBER, max, digits3);
312 return draw_graphic(view, LINE_DEFAULT, &separator, 1, TRUE);
315 bool
316 draw_lineno(struct view *view, struct view_column *column, unsigned int lineno)
318 lineno += view->pos.offset + 1;
319 return draw_lineno_custom(view, column, lineno);
322 static bool
323 draw_ref(struct view *view, struct view_column *column, const struct ref *ref)
325 enum line_type type = !ref || !ref->valid ? LINE_DEFAULT : get_line_type_from_ref(ref);
326 const char *name = ref ? ref->name : NULL;
328 return draw_field(view, type, name, column->width, ALIGN_LEFT, FALSE);
331 static bool
332 draw_refs(struct view *view, struct view_column *column, const struct ref_list *refs)
334 size_t i;
336 if (!column->opt.commit_title.refs || !refs)
337 return FALSE;
339 for (i = 0; i < refs->size; i++) {
340 struct ref *ref = refs->refs[i];
341 enum line_type type = get_line_type_from_ref(ref);
342 const struct ref_format *format = get_ref_format(ref);
344 if (!strcmp(format->start, "hide:") && !*format->end)
345 continue;
347 if (draw_formatted(view, type, "%s%s%s", format->start, ref->name, format->end))
348 return TRUE;
350 if (draw_text(view, LINE_DEFAULT, " "))
351 return TRUE;
354 return FALSE;
357 static bool
358 draw_status(struct view *view, struct view_column *column,
359 enum line_type type, const char *status)
361 const char *label = mkstatus(status ? *status : 0, column->opt.status.display);
363 return draw_field(view, type, label, column->width, ALIGN_LEFT, FALSE);
367 * Revision graph
370 static const enum line_type graph_colors[] = {
371 LINE_PALETTE_0,
372 LINE_PALETTE_1,
373 LINE_PALETTE_2,
374 LINE_PALETTE_3,
375 LINE_PALETTE_4,
376 LINE_PALETTE_5,
377 LINE_PALETTE_6,
380 static enum line_type get_graph_color(struct graph_symbol *symbol)
382 if (symbol->commit)
383 return LINE_GRAPH_COMMIT;
384 assert(symbol->color < ARRAY_SIZE(graph_colors));
385 return graph_colors[symbol->color];
388 static bool
389 draw_graph_utf8(struct view *view, struct graph_symbol *symbol, enum line_type color, bool first)
391 const char *chars = graph_symbol_to_utf8(symbol);
393 return draw_text(view, color, chars + !!first);
396 static bool
397 draw_graph_ascii(struct view *view, struct graph_symbol *symbol, enum line_type color, bool first)
399 const char *chars = graph_symbol_to_ascii(symbol);
401 return draw_text(view, color, chars + !!first);
404 static bool
405 draw_graph_chtype(struct view *view, struct graph_symbol *symbol, enum line_type color, bool first)
407 const chtype *chars = graph_symbol_to_chtype(symbol);
409 return draw_graphic(view, color, chars + !!first, 2 - !!first, FALSE);
412 typedef bool (*draw_graph_fn)(struct view *, struct graph_symbol *, enum line_type, bool);
414 static bool
415 draw_graph(struct view *view, const struct graph_canvas *canvas)
417 static const draw_graph_fn fns[] = {
418 draw_graph_ascii,
419 draw_graph_chtype,
420 draw_graph_utf8
422 draw_graph_fn fn = fns[opt_line_graphics];
423 int i;
425 for (i = 0; i < canvas->size; i++) {
426 struct graph_symbol *symbol = &canvas->symbols[i];
427 enum line_type color = get_graph_color(symbol);
429 if (fn(view, symbol, color, i == 0))
430 return TRUE;
433 return draw_text(view, LINE_DEFAULT, " ");
436 static bool
437 draw_commit_title(struct view *view, struct view_column *column,
438 const struct graph_canvas *graph, const struct ref_list *refs,
439 const char *commit_title)
441 if (graph && column->opt.commit_title.graph &&
442 draw_graph(view, graph))
443 return TRUE;
444 if (draw_refs(view, column, refs))
445 return TRUE;
446 return draw_text_overflow(view, commit_title, LINE_DEFAULT,
447 column->opt.commit_title.overflow, 0);
450 static bool
451 draw_diff_stat_part(struct view *view, enum line_type *type, const char **text, char c, enum line_type next_type)
453 const char *sep = c == '|' ? strrchr(*text, c) : strchr(*text, c);
455 if (sep != NULL) {
456 draw_text_expanded(view, *type, *text, sep - *text, FALSE);
457 *text = sep;
458 *type = next_type;
461 return sep != NULL;
464 static void
465 draw_diff_stat(struct view *view, enum line_type *type, const char **text)
467 draw_diff_stat_part(view, type, text, '|', LINE_DEFAULT);
468 if (draw_diff_stat_part(view, type, text, 'B', LINE_DEFAULT)) {
469 /* Handle binary diffstat: Bin <deleted> -> <added> bytes */
470 draw_diff_stat_part(view, type, text, ' ', LINE_DIFF_DEL);
471 draw_diff_stat_part(view, type, text, '-', LINE_DEFAULT);
472 draw_diff_stat_part(view, type, text, ' ', LINE_DIFF_ADD);
473 draw_diff_stat_part(view, type, text, 'b', LINE_DEFAULT);
475 } else {
476 draw_diff_stat_part(view, type, text, '+', LINE_DIFF_ADD);
477 draw_diff_stat_part(view, type, text, '-', LINE_DIFF_DEL);
481 bool
482 view_column_draw(struct view *view, struct line *line, unsigned int lineno)
484 struct view_column *column = view->columns;
485 struct view_column_data column_data = {};
487 if (!view->ops->get_column_data(view, line, &column_data))
488 return TRUE;
490 if (column_data.section)
491 column = column_data.section;
493 for (; column; column = column->next) {
494 mode_t mode = column_data.mode ? *column_data.mode : 0;
496 if (column->hidden)
497 continue;
499 switch (column->type) {
500 case VIEW_COLUMN_DATE:
501 if (draw_date(view, column, column_data.date))
502 return TRUE;
503 continue;
505 case VIEW_COLUMN_AUTHOR:
506 if (draw_author(view, column, column_data.author))
507 return TRUE;
508 continue;
510 case VIEW_COLUMN_REF:
511 if (draw_ref(view, column, column_data.ref))
512 return TRUE;
513 continue;
515 case VIEW_COLUMN_ID:
516 if (draw_id(view, column, column_data.reflog ? column_data.reflog : column_data.id))
517 return TRUE;
518 continue;
520 case VIEW_COLUMN_LINE_NUMBER:
521 if (draw_lineno(view, column, column_data.line_number ? *column_data.line_number : lineno))
522 return TRUE;
523 continue;
525 case VIEW_COLUMN_MODE:
526 if (draw_mode(view, column, mode))
527 return TRUE;
528 continue;
530 case VIEW_COLUMN_FILE_SIZE:
531 if (draw_file_size(view, column, column_data.file_size ? *column_data.file_size : 0, mode))
532 return TRUE;
533 continue;
535 case VIEW_COLUMN_COMMIT_TITLE:
536 if (draw_commit_title(view, column, column_data.graph,
537 column_data.refs, column_data.commit_title))
538 return TRUE;
539 continue;
541 case VIEW_COLUMN_FILE_NAME:
542 if (draw_filename(view, column, column_data.file_name, mode))
543 return TRUE;
544 continue;
546 case VIEW_COLUMN_SECTION:
547 if (draw_text(view, column->opt.section.type, column->opt.section.text))
548 return TRUE;
549 continue;
551 case VIEW_COLUMN_STATUS:
552 if (draw_status(view, column, line->type, column_data.status))
553 return TRUE;
554 continue;
556 case VIEW_COLUMN_TEXT:
558 enum line_type type = line->type;
559 const char *text = column_data.text;
561 if (line->wrapped && draw_text(view, LINE_DELIMITER, "+"))
562 return TRUE;
564 if (line->graph_indent) {
565 size_t indent = get_graph_indent(text);
567 if (draw_text_expanded(view, LINE_DEFAULT, text, indent, FALSE))
568 return TRUE;
569 text += indent;
571 if (type == LINE_DIFF_STAT)
572 draw_diff_stat(view, &type, &text);
573 if (line->commit_title) {
574 if (draw_text_overflow(view, text, LINE_DEFAULT,
575 column->opt.text.commit_title_overflow, 4))
576 return TRUE;
577 } else if (draw_text(view, type, text)) {
578 return TRUE;
581 continue;
585 return TRUE;
588 bool
589 draw_view_line(struct view *view, unsigned int lineno)
591 struct line *line;
592 bool selected = (view->pos.offset + lineno == view->pos.lineno);
594 /* FIXME: Disabled during code split.
595 assert(view_is_displayed(view));
598 if (view->pos.offset + lineno >= view->lines)
599 return FALSE;
601 line = &view->line[view->pos.offset + lineno];
603 wmove(view->win, lineno, 0);
604 if (line->cleareol)
605 wclrtoeol(view->win);
606 view->col = 0;
607 view->curline = line;
608 view->curtype = LINE_NONE;
609 line->selected = FALSE;
610 line->dirty = line->cleareol = 0;
612 if (selected) {
613 set_view_attr(view, LINE_CURSOR);
614 line->selected = TRUE;
615 view->ops->select(view, line);
618 return view->ops->draw(view, line, lineno);
621 void
622 redraw_view_dirty(struct view *view)
624 bool dirty = FALSE;
625 int lineno;
627 for (lineno = 0; lineno < view->height; lineno++) {
628 if (view->pos.offset + lineno >= view->lines)
629 break;
630 if (!view->line[view->pos.offset + lineno].dirty)
631 continue;
632 dirty = TRUE;
633 if (!draw_view_line(view, lineno))
634 break;
637 if (!dirty)
638 return;
639 wnoutrefresh(view->win);
642 void
643 redraw_view_from(struct view *view, int lineno)
645 assert(0 <= lineno && lineno < view->height);
647 if (view->columns && view_column_info_changed(view, FALSE)) {
648 int i;
650 view_column_reset(view);
651 for (i = 0; i < view->lines; i++) {
652 view_column_info_update(view, &view->line[i]);
656 for (; lineno < view->height; lineno++) {
657 if (!draw_view_line(view, lineno))
658 break;
661 wnoutrefresh(view->win);
664 void
665 redraw_view(struct view *view)
667 werase(view->win);
668 redraw_view_from(view, 0);
671 /* vim: set ts=8 sw=8 noexpandtab: */