Remove unused variable in run_prompt_command()
[tig.git] / src / draw.c
blob897646d19c88bb4504b9b9d94e6a66366b2aa7b2
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 const char *tmp = text;
137 int text_width = 0;
138 int trimmed = FALSE;
139 size_t len = utf8_length(&tmp, 0, &text_width, max, &trimmed, FALSE, 1);
141 if (draw_text_expanded(view, type, text, text_width, max < overflow))
142 return TRUE;
144 text += len;
145 type = LINE_OVERFLOW;
148 if (*text && draw_text(view, type, text))
149 return TRUE;
151 return VIEW_MAX_LEN(view) <= 0;
154 bool PRINTF_LIKE(3, 4)
155 draw_formatted(struct view *view, enum line_type type, const char *format, ...)
157 char text[SIZEOF_STR];
158 int retval;
160 FORMAT_BUFFER(text, sizeof(text), format, retval, TRUE);
161 return retval >= 0 ? draw_text(view, type, text) : VIEW_MAX_LEN(view) <= 0;
164 bool
165 draw_graphic(struct view *view, enum line_type type, const chtype graphic[], size_t size, bool separator)
167 size_t skip = view->pos.col > view->col ? view->pos.col - view->col : 0;
168 int max = VIEW_MAX_LEN(view);
169 int i;
171 if (max < size)
172 size = max;
174 set_view_attr(view, type);
175 /* Using waddch() instead of waddnstr() ensures that
176 * they'll be rendered correctly for the cursor line. */
177 for (i = skip; i < size; i++)
178 waddch(view->win, graphic[i]);
180 view->col += size;
181 if (separator) {
182 if (size < max && skip <= size)
183 waddch(view->win, ' ');
184 view->col++;
187 return VIEW_MAX_LEN(view) <= 0;
190 bool
191 draw_field(struct view *view, enum line_type type, const char *text, int width, enum align align, bool trim)
193 int max = MIN(VIEW_MAX_LEN(view), width + 1);
194 int col = view->col;
196 if (!text)
197 return draw_space(view, type, max, max);
199 if (align == ALIGN_RIGHT) {
200 int textlen = utf8_width_max(text, max);
201 int leftpad = max - textlen - 1;
203 if (leftpad > 0) {
204 if (draw_space(view, type, leftpad, leftpad))
205 return TRUE;
206 max -= leftpad;
207 col += leftpad;;
211 return draw_chars(view, type, text, max - 1, trim)
212 || draw_space(view, LINE_DEFAULT, max - (view->col - col), max);
215 static bool
216 draw_date(struct view *view, struct view_column *column, const struct time *time)
218 enum date date = column->opt.date.display;
219 const char *text = mkdate(time, date);
220 enum align align = date == DATE_RELATIVE ? ALIGN_RIGHT : ALIGN_LEFT;
222 if (date == DATE_NO)
223 return FALSE;
225 return draw_field(view, LINE_DATE, text, column->width, align, FALSE);
228 static bool
229 draw_author(struct view *view, struct view_column *column, const struct ident *author)
231 bool trim = author_trim(column->width);
232 const char *text = mkauthor(author, column->opt.author.width, column->opt.author.display);
234 if (column->opt.author.display == AUTHOR_NO)
235 return FALSE;
237 return draw_field(view, LINE_AUTHOR, text, column->width, ALIGN_LEFT, trim);
240 static bool
241 draw_id(struct view *view, struct view_column *column, const char *id)
243 enum line_type type = LINE_ID;
245 if (!column->opt.id.display)
246 return FALSE;
248 if (column->opt.id.color && id) {
249 hashval_t color = iterative_hash(id, SIZEOF_REV - 1, 0);
251 type = palette_colors[color % ARRAY_SIZE(palette_colors)];
254 return draw_field(view, type, id, column->width, ALIGN_LEFT, FALSE);
257 static bool
258 draw_filename(struct view *view, struct view_column *column, const char *filename, mode_t mode)
260 size_t width = filename ? utf8_width(filename) : 0;
261 bool trim = width >= column->width;
262 enum line_type type = S_ISDIR(mode) ? LINE_DIRECTORY : LINE_FILE;
263 int column_width = column->width ? column->width : width;
265 if (column->opt.file_name.display == FILENAME_NO)
266 return FALSE;
268 return draw_field(view, type, filename, column_width, ALIGN_LEFT, trim);
271 static bool
272 draw_file_size(struct view *view, struct view_column *column, unsigned long size, mode_t mode)
274 const char *str = S_ISDIR(mode) ? NULL : mkfilesize(size, column->opt.file_size.display);
276 if (!column->width || column->opt.file_size.display == FILE_SIZE_NO)
277 return FALSE;
279 return draw_field(view, LINE_FILE_SIZE, str, column->width, ALIGN_RIGHT, FALSE);
282 static bool
283 draw_mode(struct view *view, struct view_column *column, mode_t mode)
285 const char *str = mkmode(mode);
287 if (!column->width || !column->opt.mode.display)
288 return FALSE;
290 return draw_field(view, LINE_MODE, str, column->width, ALIGN_LEFT, FALSE);
293 static bool
294 draw_lineno_custom(struct view *view, struct view_column *column, unsigned int lineno)
296 char number[10];
297 unsigned long digits3 = column->width < 3 ? 3 : column->width;
298 int max = MIN(VIEW_MAX_LEN(view), digits3);
299 char *text = NULL;
300 chtype separator = opt_line_graphics ? ACS_VLINE : '|';
302 if (!column->opt.line_number.display)
303 return FALSE;
305 if (lineno == 1 || (lineno % column->opt.line_number.interval) == 0) {
306 static char fmt[] = "%ld";
308 fmt[1] = '0' + (digits3 <= 9 ? digits3 : 1);
309 if (string_format(number, fmt, lineno))
310 text = number;
312 if (text)
313 draw_chars(view, LINE_LINE_NUMBER, text, max, TRUE);
314 else
315 draw_space(view, LINE_LINE_NUMBER, max, digits3);
316 return draw_graphic(view, LINE_DEFAULT, &separator, 1, TRUE);
319 bool
320 draw_lineno(struct view *view, struct view_column *column, unsigned int lineno)
322 lineno += view->pos.offset + 1;
323 return draw_lineno_custom(view, column, lineno);
326 static bool
327 draw_ref(struct view *view, struct view_column *column, const struct ref *ref)
329 enum line_type type = !ref || !ref->valid ? LINE_DEFAULT : get_line_type_from_ref(ref);
330 const char *name = ref ? ref->name : NULL;
332 return draw_field(view, type, name, column->width, ALIGN_LEFT, FALSE);
335 static bool
336 draw_refs(struct view *view, struct view_column *column, const struct ref_list *refs)
338 size_t i;
340 if (!column->opt.commit_title.refs || !refs)
341 return FALSE;
343 for (i = 0; i < refs->size; i++) {
344 struct ref *ref = refs->refs[i];
345 enum line_type type = get_line_type_from_ref(ref);
346 const struct ref_format *format = get_ref_format(ref);
348 if (!strcmp(format->start, "hide:") && !*format->end)
349 continue;
351 if (draw_formatted(view, type, "%s%s%s", format->start, ref->name, format->end))
352 return TRUE;
354 if (draw_text(view, LINE_DEFAULT, " "))
355 return TRUE;
358 return FALSE;
361 static bool
362 draw_status(struct view *view, struct view_column *column,
363 enum line_type type, const char *status)
365 const char *label = mkstatus(status ? *status : 0, column->opt.status.display);
367 return draw_field(view, type, label, column->width, ALIGN_LEFT, FALSE);
371 * Revision graph
374 static enum line_type get_graph_color(struct graph_symbol *symbol)
376 if (symbol->commit)
377 return LINE_GRAPH_COMMIT;
378 assert(symbol->color < ARRAY_SIZE(palette_colors));
379 return palette_colors[symbol->color];
382 static bool
383 draw_graph_utf8(struct view *view, struct graph_symbol *symbol, enum line_type color, bool first)
385 const char *chars = graph_symbol_to_utf8(symbol);
387 return draw_text(view, color, chars + !!first);
390 static bool
391 draw_graph_ascii(struct view *view, struct graph_symbol *symbol, enum line_type color, bool first)
393 const char *chars = graph_symbol_to_ascii(symbol);
395 return draw_text(view, color, chars + !!first);
398 static bool
399 draw_graph_chtype(struct view *view, struct graph_symbol *symbol, enum line_type color, bool first)
401 const chtype *chars = graph_symbol_to_chtype(symbol);
403 return draw_graphic(view, color, chars + !!first, 2 - !!first, FALSE);
406 typedef bool (*draw_graph_fn)(struct view *, struct graph_symbol *, enum line_type, bool);
408 static bool
409 draw_graph(struct view *view, const struct graph_canvas *canvas)
411 static const draw_graph_fn fns[] = {
412 draw_graph_ascii,
413 draw_graph_chtype,
414 draw_graph_utf8
416 draw_graph_fn fn = fns[opt_line_graphics];
417 int i;
419 for (i = 0; i < canvas->size; i++) {
420 struct graph_symbol *symbol = &canvas->symbols[i];
421 enum line_type color = get_graph_color(symbol);
423 if (fn(view, symbol, color, i == 0))
424 return TRUE;
427 return draw_text(view, LINE_DEFAULT, " ");
430 static bool
431 draw_commit_title(struct view *view, struct view_column *column,
432 const struct graph_canvas *graph, const struct ref_list *refs,
433 const char *commit_title)
435 if (graph && column->opt.commit_title.graph &&
436 draw_graph(view, graph))
437 return TRUE;
438 if (draw_refs(view, column, refs))
439 return TRUE;
440 return draw_text_overflow(view, commit_title, LINE_DEFAULT,
441 column->opt.commit_title.overflow, 0);
444 static bool
445 draw_diff_stat_part(struct view *view, enum line_type *type, const char **text, char c, enum line_type next_type)
447 const char *sep = c == '|' ? strrchr(*text, c) : strchr(*text, c);
449 if (sep != NULL) {
450 draw_text_expanded(view, *type, *text, sep - *text, FALSE);
451 *text = sep;
452 *type = next_type;
455 return sep != NULL;
458 static void
459 draw_diff_stat(struct view *view, enum line_type *type, const char **text)
461 draw_diff_stat_part(view, type, text, '|', LINE_DEFAULT);
462 if (draw_diff_stat_part(view, type, text, 'B', LINE_DEFAULT)) {
463 /* Handle binary diffstat: Bin <deleted> -> <added> bytes */
464 draw_diff_stat_part(view, type, text, ' ', LINE_DIFF_DEL);
465 draw_diff_stat_part(view, type, text, '-', LINE_DEFAULT);
466 draw_diff_stat_part(view, type, text, ' ', LINE_DIFF_ADD);
467 draw_diff_stat_part(view, type, text, 'b', LINE_DEFAULT);
469 } else {
470 draw_diff_stat_part(view, type, text, '+', LINE_DIFF_ADD);
471 draw_diff_stat_part(view, type, text, '-', LINE_DIFF_DEL);
475 bool
476 view_column_draw(struct view *view, struct line *line, unsigned int lineno)
478 struct view_column *column = view->columns;
479 struct view_column_data column_data = {};
481 if (!view->ops->get_column_data(view, line, &column_data))
482 return TRUE;
484 if (column_data.section)
485 column = column_data.section;
487 for (; column; column = column->next) {
488 mode_t mode = column_data.mode ? *column_data.mode : 0;
490 if (column->hidden)
491 continue;
493 switch (column->type) {
494 case VIEW_COLUMN_DATE:
495 if (draw_date(view, column, column_data.date))
496 return TRUE;
497 continue;
499 case VIEW_COLUMN_AUTHOR:
500 if (draw_author(view, column, column_data.author))
501 return TRUE;
502 continue;
504 case VIEW_COLUMN_REF:
505 if (draw_ref(view, column, column_data.ref))
506 return TRUE;
507 continue;
509 case VIEW_COLUMN_ID:
510 if (draw_id(view, column, column_data.reflog ? column_data.reflog : column_data.id))
511 return TRUE;
512 continue;
514 case VIEW_COLUMN_LINE_NUMBER:
515 if (draw_lineno(view, column, column_data.line_number ? *column_data.line_number : lineno))
516 return TRUE;
517 continue;
519 case VIEW_COLUMN_MODE:
520 if (draw_mode(view, column, mode))
521 return TRUE;
522 continue;
524 case VIEW_COLUMN_FILE_SIZE:
525 if (draw_file_size(view, column, column_data.file_size ? *column_data.file_size : 0, mode))
526 return TRUE;
527 continue;
529 case VIEW_COLUMN_COMMIT_TITLE:
530 if (draw_commit_title(view, column, column_data.graph,
531 column_data.refs, column_data.commit_title))
532 return TRUE;
533 continue;
535 case VIEW_COLUMN_FILE_NAME:
536 if (draw_filename(view, column, column_data.file_name, mode))
537 return TRUE;
538 continue;
540 case VIEW_COLUMN_SECTION:
541 if (draw_text(view, column->opt.section.type, column->opt.section.text))
542 return TRUE;
543 continue;
545 case VIEW_COLUMN_STATUS:
546 if (draw_status(view, column, line->type, column_data.status))
547 return TRUE;
548 continue;
550 case VIEW_COLUMN_TEXT:
552 enum line_type type = line->type;
553 const char *text = column_data.text;
555 if (line->wrapped && draw_text(view, LINE_DELIMITER, "+"))
556 return TRUE;
558 if (line->graph_indent) {
559 size_t indent = get_graph_indent(text);
561 if (draw_text_expanded(view, LINE_DEFAULT, text, indent, FALSE))
562 return TRUE;
563 text += indent;
565 if (type == LINE_DIFF_STAT)
566 draw_diff_stat(view, &type, &text);
567 if (line->commit_title) {
568 if (draw_text_overflow(view, text, LINE_DEFAULT,
569 column->opt.text.commit_title_overflow, 4))
570 return TRUE;
571 } else if (draw_text(view, type, text)) {
572 return TRUE;
575 continue;
579 return TRUE;
582 bool
583 draw_view_line(struct view *view, unsigned int lineno)
585 struct line *line;
586 bool selected = (view->pos.offset + lineno == view->pos.lineno);
588 /* FIXME: Disabled during code split.
589 assert(view_is_displayed(view));
592 if (view->pos.offset + lineno >= view->lines)
593 return FALSE;
595 line = &view->line[view->pos.offset + lineno];
597 wmove(view->win, lineno, 0);
598 if (line->cleareol)
599 wclrtoeol(view->win);
600 view->col = 0;
601 view->curline = line;
602 view->curtype = LINE_NONE;
603 line->selected = FALSE;
604 line->dirty = line->cleareol = 0;
606 if (selected) {
607 set_view_attr(view, LINE_CURSOR);
608 line->selected = TRUE;
609 view->ops->select(view, line);
612 return view->ops->draw(view, line, lineno);
615 void
616 redraw_view_dirty(struct view *view)
618 bool dirty = FALSE;
619 int lineno;
621 for (lineno = 0; lineno < view->height; lineno++) {
622 if (view->pos.offset + lineno >= view->lines)
623 break;
624 if (!view->line[view->pos.offset + lineno].dirty)
625 continue;
626 dirty = TRUE;
627 if (!draw_view_line(view, lineno))
628 break;
631 if (!dirty)
632 return;
633 wnoutrefresh(view->win);
636 void
637 redraw_view_from(struct view *view, int lineno)
639 assert(0 <= lineno && lineno < view->height);
641 if (view->columns && view_column_info_changed(view, FALSE)) {
642 int i;
644 view_column_reset(view);
645 for (i = 0; i < view->lines; i++) {
646 view_column_info_update(view, &view->line[i]);
650 for (; lineno < view->height; lineno++) {
651 if (!draw_view_line(view, lineno))
652 break;
655 wnoutrefresh(view->win);
658 void
659 redraw_view(struct view *view)
661 werase(view->win);
662 redraw_view_from(view, 0);
665 /* vim: set ts=8 sw=8 noexpandtab: */