Move ref column drawing to draw_ref
[tig.git] / src / draw.c
blobeda62e3b42bb5d9dfe17c6576f95b471318c5dbe
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), TRUE);
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.show;
199 const char *text = mkdate(time, date);
201 if (date == DATE_NO)
202 return FALSE;
204 return draw_field(view, LINE_DATE, text, column->width, ALIGN_LEFT, FALSE);
207 static bool
208 draw_author(struct view *view, struct view_column *column, const struct ident *author)
210 bool trim = author_trim(column->width);
211 const char *text = mkauthor(author, column->width, column->opt.author.show);
213 if (column->opt.author.show == AUTHOR_NO)
214 return FALSE;
216 return draw_field(view, LINE_AUTHOR, text, column->width, ALIGN_LEFT, trim);
219 static bool
220 draw_id(struct view *view, struct view_column *column, const char *id)
222 static const enum line_type colors[] = {
223 LINE_PALETTE_0,
224 LINE_PALETTE_1,
225 LINE_PALETTE_2,
226 LINE_PALETTE_3,
227 LINE_PALETTE_4,
228 LINE_PALETTE_5,
229 LINE_PALETTE_6,
231 enum line_type type = LINE_ID;
233 if (!column->opt.id.show)
234 return FALSE;
236 if (column->opt.id.color)
237 type = colors[((long) id) % ARRAY_SIZE(colors)];
239 return draw_field(view, type, id, column->width, ALIGN_LEFT, FALSE);
242 static bool
243 draw_filename(struct view *view, struct view_column *column, const char *filename, mode_t mode)
245 size_t width = filename ? utf8_width(filename) : 0;
246 bool trim = width >= column->width;
247 enum line_type type = S_ISDIR(mode) ? LINE_DIRECTORY : LINE_FILE;
248 int column_width = column->width ? column->width : width;
250 if (column->opt.file_name.show == FILENAME_NO)
251 return FALSE;
253 return draw_field(view, type, filename, column_width, ALIGN_LEFT, trim);
256 static bool
257 draw_file_size(struct view *view, struct view_column *column, unsigned long size, mode_t mode)
259 const char *str = S_ISDIR(mode) ? NULL : mkfilesize(size, column->opt.file_size.show);
261 if (!column->width || column->opt.file_size.show == FILE_SIZE_NO)
262 return FALSE;
264 return draw_field(view, LINE_FILE_SIZE, str, column->width, ALIGN_RIGHT, FALSE);
267 static bool
268 draw_mode(struct view *view, struct view_column *column, mode_t mode)
270 const char *str = mkmode(mode);
272 if (!column->width || !column->opt.mode.show)
273 return FALSE;
275 return draw_field(view, LINE_MODE, str, column->width, ALIGN_LEFT, FALSE);
278 static bool
279 draw_lineno_custom(struct view *view, struct view_column *column, unsigned int lineno)
281 char number[10];
282 unsigned long digits3 = column->width < 3 ? 3 : column->width;
283 int max = MIN(VIEW_MAX_LEN(view), digits3);
284 char *text = NULL;
285 chtype separator = opt_line_graphics ? ACS_VLINE : '|';
287 if (!column->opt.line_number.show)
288 return FALSE;
290 if (lineno == 1 || (lineno % column->opt.line_number.interval) == 0) {
291 static char fmt[] = "%ld";
293 fmt[1] = '0' + (digits3 <= 9 ? digits3 : 1);
294 if (string_format(number, fmt, lineno))
295 text = number;
297 if (text)
298 draw_chars(view, LINE_LINE_NUMBER, text, max, TRUE);
299 else
300 draw_space(view, LINE_LINE_NUMBER, max, digits3);
301 return draw_graphic(view, LINE_DEFAULT, &separator, 1, TRUE);
304 bool
305 draw_lineno(struct view *view, struct view_column *column, unsigned int lineno)
307 lineno += view->pos.offset + 1;
308 return draw_lineno_custom(view, column, lineno);
311 static bool
312 draw_ref(struct view *view, struct view_column *column, const struct ref *ref)
314 enum line_type type = !ref || !ref->valid ? LINE_DEFAULT : get_line_type_from_ref(ref);
315 const char *name = ref ? ref->name : NULL;
317 return draw_field(view, type, name, column->width, ALIGN_LEFT, FALSE);
320 static bool
321 draw_refs(struct view *view, struct view_column *column, const struct ref_list *refs)
323 size_t i;
325 if (!column->opt.commit_title.refs || !refs)
326 return FALSE;
328 for (i = 0; i < refs->size; i++) {
329 struct ref *ref = refs->refs[i];
330 enum line_type type = get_line_type_from_ref(ref);
331 const struct ref_format *format = get_ref_format(ref);
333 if (!strcmp(format->start, "hide:") && !*format->end)
334 continue;
336 if (draw_formatted(view, type, "%s%s%s", format->start, ref->name, format->end))
337 return TRUE;
339 if (draw_text(view, LINE_DEFAULT, " "))
340 return TRUE;
343 return FALSE;
347 * Revision graph
350 static const enum line_type graph_colors[] = {
351 LINE_PALETTE_0,
352 LINE_PALETTE_1,
353 LINE_PALETTE_2,
354 LINE_PALETTE_3,
355 LINE_PALETTE_4,
356 LINE_PALETTE_5,
357 LINE_PALETTE_6,
360 static enum line_type get_graph_color(struct graph_symbol *symbol)
362 if (symbol->commit)
363 return LINE_GRAPH_COMMIT;
364 assert(symbol->color < ARRAY_SIZE(graph_colors));
365 return graph_colors[symbol->color];
368 static bool
369 draw_graph_utf8(struct view *view, struct graph_symbol *symbol, enum line_type color, bool first)
371 const char *chars = graph_symbol_to_utf8(symbol);
373 return draw_text(view, color, chars + !!first);
376 static bool
377 draw_graph_ascii(struct view *view, struct graph_symbol *symbol, enum line_type color, bool first)
379 const char *chars = graph_symbol_to_ascii(symbol);
381 return draw_text(view, color, chars + !!first);
384 static bool
385 draw_graph_chtype(struct view *view, struct graph_symbol *symbol, enum line_type color, bool first)
387 const chtype *chars = graph_symbol_to_chtype(symbol);
389 return draw_graphic(view, color, chars + !!first, 2 - !!first, FALSE);
392 typedef bool (*draw_graph_fn)(struct view *, struct graph_symbol *, enum line_type, bool);
394 static bool
395 draw_graph(struct view *view, const struct graph_canvas *canvas)
397 static const draw_graph_fn fns[] = {
398 draw_graph_ascii,
399 draw_graph_chtype,
400 draw_graph_utf8
402 draw_graph_fn fn = fns[opt_line_graphics];
403 int i;
405 for (i = 0; i < canvas->size; i++) {
406 struct graph_symbol *symbol = &canvas->symbols[i];
407 enum line_type color = get_graph_color(symbol);
409 if (fn(view, symbol, color, i == 0))
410 return TRUE;
413 return draw_text(view, LINE_DEFAULT, " ");
416 static bool
417 draw_diff_stat_part(struct view *view, enum line_type *type, const char **text, char c, enum line_type next_type)
419 const char *sep = c == '|' ? strrchr(*text, c) : strchr(*text, c);
421 if (sep != NULL) {
422 draw_text_expanded(view, *type, *text, sep - *text, FALSE);
423 *text = sep;
424 *type = next_type;
427 return sep != NULL;
430 static void
431 draw_diff_stat(struct view *view, enum line_type *type, const char **text)
433 draw_diff_stat_part(view, type, text, '|', LINE_DEFAULT);
434 if (draw_diff_stat_part(view, type, text, 'B', LINE_DEFAULT)) {
435 /* Handle binary diffstat: Bin <deleted> -> <added> bytes */
436 draw_diff_stat_part(view, type, text, ' ', LINE_DIFF_DEL);
437 draw_diff_stat_part(view, type, text, '-', LINE_DEFAULT);
438 draw_diff_stat_part(view, type, text, ' ', LINE_DIFF_ADD);
439 draw_diff_stat_part(view, type, text, 'b', LINE_DEFAULT);
441 } else {
442 draw_diff_stat_part(view, type, text, '+', LINE_DIFF_ADD);
443 draw_diff_stat_part(view, type, text, '-', LINE_DIFF_DEL);
447 bool
448 view_column_draw(struct view *view, struct line *line, unsigned int lineno)
450 struct view_column *column = view->columns;
451 struct view_column_data column_data = {};
453 if (!view->ops->get_column_data(view, line, &column_data))
454 return TRUE;
456 if (column_data.section)
457 column = column_data.section;
459 for (; column; column = column->next) {
460 mode_t mode = column_data.mode ? *column_data.mode : 0;
462 if (column->hidden)
463 continue;
465 switch (column->type) {
466 case VIEW_COLUMN_DATE:
467 if (draw_date(view, column, column_data.date))
468 return TRUE;
469 continue;
471 case VIEW_COLUMN_AUTHOR:
472 if (draw_author(view, column, column_data.author))
473 return TRUE;
474 continue;
476 case VIEW_COLUMN_REF:
477 if (draw_ref(view, column, column_data.ref))
478 return TRUE;
479 continue;
481 case VIEW_COLUMN_ID:
482 if (draw_id(view, column, column_data.id))
483 return TRUE;
484 continue;
486 case VIEW_COLUMN_LINE_NUMBER:
487 if (draw_lineno(view, column, column_data.line_number ? *column_data.line_number : lineno))
488 return TRUE;
489 continue;
491 case VIEW_COLUMN_MODE:
492 if (draw_mode(view, column, mode))
493 return TRUE;
494 continue;
496 case VIEW_COLUMN_FILE_SIZE:
497 if (draw_file_size(view, column, column_data.file_size ? *column_data.file_size : 0, mode))
498 return TRUE;
499 continue;
501 case VIEW_COLUMN_COMMIT_TITLE:
502 if (column_data.graph && draw_graph(view, column_data.graph))
503 return TRUE;
504 if (column_data.refs && draw_refs(view, column, column_data.refs))
505 return TRUE;
506 if (draw_text_overflow(view, column_data.commit_title, LINE_DEFAULT,
507 column->opt.commit_title.overflow, 0))
508 return TRUE;
509 continue;
511 case VIEW_COLUMN_FILE_NAME:
512 if (draw_filename(view, column, column_data.file_name, mode))
513 return TRUE;
514 continue;
516 case VIEW_COLUMN_TEXT:
518 enum line_type type = line->type;
519 const char *text = column_data.text;
521 if (line->wrapped && draw_text(view, LINE_DELIMITER, "+"))
522 return TRUE;
524 if (line->graph_indent) {
525 size_t indent = get_graph_indent(text);
527 if (draw_text_expanded(view, LINE_DEFAULT, text, indent, FALSE))
528 return TRUE;
529 text += indent;
531 if (type == LINE_DIFF_STAT)
532 draw_diff_stat(view, &type, &text);
533 if (line->commit_title) {
534 if (draw_text_overflow(view, text, LINE_DEFAULT,
535 column->opt.text.commit_title_overflow, 4))
536 return TRUE;
537 } else if (draw_text(view, type, text)) {
538 return TRUE;
541 continue;
545 return TRUE;
548 bool
549 draw_view_line(struct view *view, unsigned int lineno)
551 struct line *line;
552 bool selected = (view->pos.offset + lineno == view->pos.lineno);
554 /* FIXME: Disabled during code split.
555 assert(view_is_displayed(view));
558 if (view->pos.offset + lineno >= view->lines)
559 return FALSE;
561 line = &view->line[view->pos.offset + lineno];
563 wmove(view->win, lineno, 0);
564 if (line->cleareol)
565 wclrtoeol(view->win);
566 view->col = 0;
567 view->curline = line;
568 view->curtype = LINE_NONE;
569 line->selected = FALSE;
570 line->dirty = line->cleareol = 0;
572 if (selected) {
573 set_view_attr(view, LINE_CURSOR);
574 line->selected = TRUE;
575 view->ops->select(view, line);
578 return view->ops->draw(view, line, lineno);
581 void
582 redraw_view_dirty(struct view *view)
584 bool dirty = FALSE;
585 int lineno;
587 for (lineno = 0; lineno < view->height; lineno++) {
588 if (view->pos.offset + lineno >= view->lines)
589 break;
590 if (!view->line[view->pos.offset + lineno].dirty)
591 continue;
592 dirty = TRUE;
593 if (!draw_view_line(view, lineno))
594 break;
597 if (!dirty)
598 return;
599 wnoutrefresh(view->win);
602 void
603 redraw_view_from(struct view *view, int lineno)
605 assert(0 <= lineno && lineno < view->height);
607 if (view->columns && view_column_info_changed(view, FALSE)) {
608 int i;
610 view_column_reset(view);
611 for (i = 0; i < view->lines; i++) {
612 view_column_info_update(view, &view->line[i]);
616 for (; lineno < view->height; lineno++) {
617 if (!draw_view_line(view, lineno))
618 break;
621 wnoutrefresh(view->win);
624 void
625 redraw_view(struct view *view)
627 werase(view->win);
628 redraw_view_from(view, 0);
631 /* vim: set ts=8 sw=8 noexpandtab: */