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.
15 #include "tig/graph.h"
17 #include "tig/options.h"
19 static const enum line_type palette_colors
[] = {
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
);
50 #define VIEW_MAX_LEN(view) ((view)->width + (view)->pos.col - (view)->col)
53 draw_chars(struct view
*view
, enum line_type type
, const char *string
,
54 int max_len
, bool use_tilde
)
59 size_t skip
= view
->pos
.col
> view
->col
? view
->pos
.col
- view
->col
: 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
);
69 return VIEW_MAX_LEN(view
) <= 0;
72 set_view_attr(view
, type
);
74 waddnstr(view
->win
, string
, len
);
76 if (trimmed
&& use_tilde
) {
77 set_view_attr(view
, LINE_DELIMITER
);
78 waddch(view
->win
, '~');
84 return VIEW_MAX_LEN(view
) <= 0;
88 draw_space(struct view
*view
, enum line_type type
, int max
, int spaces
)
90 static char space
[] = " ";
92 spaces
= MIN(max
, spaces
);
95 int len
= MIN(spaces
, sizeof(space
) - 1);
97 if (draw_chars(view
, type
, space
, len
, FALSE
))
102 return VIEW_MAX_LEN(view
) <= 0;
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
];
111 size_t pos
= string_expand(text
, sizeof(text
), string
, opt_tab_size
);
113 if (draw_chars(view
, type
, text
, max_len
, use_tilde
))
118 return VIEW_MAX_LEN(view
) <= 0;
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
);
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;
134 int overflow
= overflow_length
+ offset
;
135 int max
= MIN(VIEW_MAX_LEN(view
), overflow
);
136 const char *tmp
= text
;
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
))
145 type
= LINE_OVERFLOW
;
148 if (*text
&& draw_text(view
, type
, text
))
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
];
160 FORMAT_BUFFER(text
, sizeof(text
), format
, retval
, TRUE
);
161 return retval
>= 0 ? draw_text(view
, type
, text
) : VIEW_MAX_LEN(view
) <= 0;
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
);
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
]);
182 if (size
< max
&& skip
<= size
)
183 waddch(view
->win
, ' ');
187 return VIEW_MAX_LEN(view
) <= 0;
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);
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;
204 if (draw_space(view
, type
, leftpad
, leftpad
))
211 return draw_chars(view
, type
, text
, max
- 1, trim
)
212 || draw_space(view
, LINE_DEFAULT
, max
- (view
->col
- col
), max
);
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
;
225 return draw_field(view
, LINE_DATE
, text
, column
->width
, align
, FALSE
);
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
)
237 return draw_field(view
, LINE_AUTHOR
, text
, column
->width
, ALIGN_LEFT
, trim
);
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
)
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
);
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
)
268 return draw_field(view
, type
, filename
, column_width
, ALIGN_LEFT
, trim
);
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
)
279 return draw_field(view
, LINE_FILE_SIZE
, str
, column
->width
, ALIGN_RIGHT
, FALSE
);
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
)
290 return draw_field(view
, LINE_MODE
, str
, column
->width
, ALIGN_LEFT
, FALSE
);
294 draw_lineno_custom(struct view
*view
, struct view_column
*column
, unsigned int lineno
)
297 unsigned long digits3
= column
->width
< 3 ? 3 : column
->width
;
298 int max
= MIN(VIEW_MAX_LEN(view
), digits3
);
300 chtype separator
= opt_line_graphics
? ACS_VLINE
: '|';
302 if (!column
->opt
.line_number
.display
)
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
))
313 draw_chars(view
, LINE_LINE_NUMBER
, text
, max
, TRUE
);
315 draw_space(view
, LINE_LINE_NUMBER
, max
, digits3
);
316 return draw_graphic(view
, LINE_DEFAULT
, &separator
, 1, TRUE
);
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
);
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
);
336 draw_refs(struct view
*view
, struct view_column
*column
, const struct ref_list
*refs
)
340 if (!column
->opt
.commit_title
.refs
|| !refs
)
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
)
351 if (draw_formatted(view
, type
, "%s%s%s", format
->start
, ref
->name
, format
->end
))
354 if (draw_text(view
, LINE_DEFAULT
, " "))
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
);
374 static enum line_type
get_graph_color(struct graph_symbol
*symbol
)
377 return LINE_GRAPH_COMMIT
;
378 assert(symbol
->color
< ARRAY_SIZE(palette_colors
));
379 return palette_colors
[symbol
->color
];
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
);
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
);
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);
409 draw_graph(struct view
*view
, const struct graph_canvas
*canvas
)
411 static const draw_graph_fn fns
[] = {
416 draw_graph_fn fn
= fns
[opt_line_graphics
];
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))
427 return draw_text(view
, LINE_DEFAULT
, " ");
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
))
438 if (draw_refs(view
, column
, refs
))
440 return draw_text_overflow(view
, commit_title
, LINE_DEFAULT
,
441 column
->opt
.commit_title
.overflow
, 0);
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
);
450 draw_text_expanded(view
, *type
, *text
, sep
- *text
, FALSE
);
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
);
470 draw_diff_stat_part(view
, type
, text
, '+', LINE_DIFF_ADD
);
471 draw_diff_stat_part(view
, type
, text
, '-', LINE_DIFF_DEL
);
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
))
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;
493 switch (column
->type
) {
494 case VIEW_COLUMN_DATE
:
495 if (draw_date(view
, column
, column_data
.date
))
499 case VIEW_COLUMN_AUTHOR
:
500 if (draw_author(view
, column
, column_data
.author
))
504 case VIEW_COLUMN_REF
:
505 if (draw_ref(view
, column
, column_data
.ref
))
510 if (draw_id(view
, column
, column_data
.reflog
? column_data
.reflog
: column_data
.id
))
514 case VIEW_COLUMN_LINE_NUMBER
:
515 if (draw_lineno(view
, column
, column_data
.line_number
? *column_data
.line_number
: lineno
))
519 case VIEW_COLUMN_MODE
:
520 if (draw_mode(view
, column
, mode
))
524 case VIEW_COLUMN_FILE_SIZE
:
525 if (draw_file_size(view
, column
, column_data
.file_size
? *column_data
.file_size
: 0, mode
))
529 case VIEW_COLUMN_COMMIT_TITLE
:
530 if (draw_commit_title(view
, column
, column_data
.graph
,
531 column_data
.refs
, column_data
.commit_title
))
535 case VIEW_COLUMN_FILE_NAME
:
536 if (draw_filename(view
, column
, column_data
.file_name
, mode
))
540 case VIEW_COLUMN_SECTION
:
541 if (draw_text(view
, column
->opt
.section
.type
, column
->opt
.section
.text
))
545 case VIEW_COLUMN_STATUS
:
546 if (draw_status(view
, column
, line
->type
, column_data
.status
))
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
, "+"))
558 if (line
->graph_indent
) {
559 size_t indent
= get_graph_indent(text
);
561 if (draw_text_expanded(view
, LINE_DEFAULT
, text
, indent
, FALSE
))
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))
571 } else if (draw_text(view
, type
, text
)) {
583 draw_view_line(struct view
*view
, unsigned int lineno
)
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
)
595 line
= &view
->line
[view
->pos
.offset
+ lineno
];
597 wmove(view
->win
, lineno
, 0);
599 wclrtoeol(view
->win
);
601 view
->curline
= line
;
602 view
->curtype
= LINE_NONE
;
603 line
->selected
= FALSE
;
604 line
->dirty
= line
->cleareol
= 0;
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
);
616 redraw_view_dirty(struct view
*view
)
621 for (lineno
= 0; lineno
< view
->height
; lineno
++) {
622 if (view
->pos
.offset
+ lineno
>= view
->lines
)
624 if (!view
->line
[view
->pos
.offset
+ lineno
].dirty
)
627 if (!draw_view_line(view
, lineno
))
633 wnoutrefresh(view
->win
);
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
)) {
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
))
655 wnoutrefresh(view
->win
);
659 redraw_view(struct view
*view
)
662 redraw_view_from(view
, 0);
665 /* vim: set ts=8 sw=8 noexpandtab: */