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"
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
);
33 #define VIEW_MAX_LEN(view) ((view)->width + (view)->pos.col - (view)->col)
36 draw_chars(struct view
*view
, enum line_type type
, const char *string
,
37 int max_len
, bool use_tilde
)
42 size_t skip
= view
->pos
.col
> view
->col
? view
->pos
.col
- view
->col
: 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
);
52 return VIEW_MAX_LEN(view
) <= 0;
55 set_view_attr(view
, type
);
57 waddnstr(view
->win
, string
, len
);
59 if (trimmed
&& use_tilde
) {
60 set_view_attr(view
, LINE_DELIMITER
);
61 waddch(view
->win
, '~');
67 return VIEW_MAX_LEN(view
) <= 0;
71 draw_space(struct view
*view
, enum line_type type
, int max
, int spaces
)
73 static char space
[] = " ";
75 spaces
= MIN(max
, spaces
);
78 int len
= MIN(spaces
, sizeof(space
) - 1);
80 if (draw_chars(view
, type
, space
, len
, FALSE
))
85 return VIEW_MAX_LEN(view
) <= 0;
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
];
94 size_t pos
= string_expand(text
, sizeof(text
), string
, opt_tab_size
);
96 if (draw_chars(view
, type
, text
, max_len
, use_tilde
))
101 return VIEW_MAX_LEN(view
) <= 0;
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
);
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;
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
))
124 text
= len
> overflow
? text
+ overflow
: "";
125 type
= LINE_OVERFLOW
;
128 if (*text
&& draw_text(view
, type
, text
))
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
];
140 FORMAT_BUFFER(text
, sizeof(text
), format
, retval
, TRUE
);
141 return retval
>= 0 ? draw_text(view
, type
, text
) : VIEW_MAX_LEN(view
) <= 0;
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
);
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
]);
162 if (size
< max
&& skip
<= size
)
163 waddch(view
->win
, ' ');
167 return VIEW_MAX_LEN(view
) <= 0;
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);
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;
184 if (draw_space(view
, type
, leftpad
, leftpad
))
191 return draw_chars(view
, type
, text
, max
- 1, trim
)
192 || draw_space(view
, LINE_DEFAULT
, max
- (view
->col
- col
), max
);
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
);
204 return draw_field(view
, LINE_DATE
, text
, column
->width
, ALIGN_LEFT
, FALSE
);
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
)
216 return draw_field(view
, LINE_AUTHOR
, text
, column
->width
, ALIGN_LEFT
, trim
);
220 draw_id(struct view
*view
, struct view_column
*column
, const char *id
)
222 static const enum line_type colors
[] = {
231 enum line_type type
= LINE_ID
;
233 if (!column
->opt
.id
.show
)
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
);
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
)
253 return draw_field(view
, type
, filename
, column_width
, ALIGN_LEFT
, trim
);
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
)
264 return draw_field(view
, LINE_FILE_SIZE
, str
, column
->width
, ALIGN_RIGHT
, FALSE
);
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
)
275 return draw_field(view
, LINE_MODE
, str
, column
->width
, ALIGN_LEFT
, FALSE
);
279 draw_lineno_custom(struct view
*view
, struct view_column
*column
, unsigned int lineno
)
282 unsigned long digits3
= column
->width
< 3 ? 3 : column
->width
;
283 int max
= MIN(VIEW_MAX_LEN(view
), digits3
);
285 chtype separator
= opt_line_graphics
? ACS_VLINE
: '|';
287 if (!column
->opt
.line_number
.show
)
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
))
298 draw_chars(view
, LINE_LINE_NUMBER
, text
, max
, TRUE
);
300 draw_space(view
, LINE_LINE_NUMBER
, max
, digits3
);
301 return draw_graphic(view
, LINE_DEFAULT
, &separator
, 1, TRUE
);
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
);
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
);
321 draw_refs(struct view
*view
, struct view_column
*column
, const struct ref_list
*refs
)
325 if (!column
->opt
.commit_title
.refs
|| !refs
)
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
)
336 if (draw_formatted(view
, type
, "%s%s%s", format
->start
, ref
->name
, format
->end
))
339 if (draw_text(view
, LINE_DEFAULT
, " "))
350 static const enum line_type graph_colors
[] = {
360 static enum line_type
get_graph_color(struct graph_symbol
*symbol
)
363 return LINE_GRAPH_COMMIT
;
364 assert(symbol
->color
< ARRAY_SIZE(graph_colors
));
365 return graph_colors
[symbol
->color
];
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
);
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
);
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);
395 draw_graph(struct view
*view
, const struct graph_canvas
*canvas
)
397 static const draw_graph_fn fns
[] = {
402 draw_graph_fn fn
= fns
[opt_line_graphics
];
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))
413 return draw_text(view
, LINE_DEFAULT
, " ");
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
);
422 draw_text_expanded(view
, *type
, *text
, sep
- *text
, FALSE
);
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
);
442 draw_diff_stat_part(view
, type
, text
, '+', LINE_DIFF_ADD
);
443 draw_diff_stat_part(view
, type
, text
, '-', LINE_DIFF_DEL
);
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
))
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;
465 switch (column
->type
) {
466 case VIEW_COLUMN_DATE
:
467 if (draw_date(view
, column
, column_data
.date
))
471 case VIEW_COLUMN_AUTHOR
:
472 if (draw_author(view
, column
, column_data
.author
))
476 case VIEW_COLUMN_REF
:
477 if (draw_ref(view
, column
, column_data
.ref
))
482 if (draw_id(view
, column
, column_data
.id
))
486 case VIEW_COLUMN_LINE_NUMBER
:
487 if (draw_lineno(view
, column
, column_data
.line_number
? *column_data
.line_number
: lineno
))
491 case VIEW_COLUMN_MODE
:
492 if (draw_mode(view
, column
, mode
))
496 case VIEW_COLUMN_FILE_SIZE
:
497 if (draw_file_size(view
, column
, column_data
.file_size
? *column_data
.file_size
: 0, mode
))
501 case VIEW_COLUMN_COMMIT_TITLE
:
502 if (column_data
.graph
&& draw_graph(view
, column_data
.graph
))
504 if (column_data
.refs
&& draw_refs(view
, column
, column_data
.refs
))
506 if (draw_text_overflow(view
, column_data
.commit_title
, LINE_DEFAULT
,
507 column
->opt
.commit_title
.overflow
, 0))
511 case VIEW_COLUMN_FILE_NAME
:
512 if (draw_filename(view
, column
, column_data
.file_name
, mode
))
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
, "+"))
524 if (line
->graph_indent
) {
525 size_t indent
= get_graph_indent(text
);
527 if (draw_text_expanded(view
, LINE_DEFAULT
, text
, indent
, FALSE
))
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))
537 } else if (draw_text(view
, type
, text
)) {
549 draw_view_line(struct view
*view
, unsigned int lineno
)
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
)
561 line
= &view
->line
[view
->pos
.offset
+ lineno
];
563 wmove(view
->win
, lineno
, 0);
565 wclrtoeol(view
->win
);
567 view
->curline
= line
;
568 view
->curtype
= LINE_NONE
;
569 line
->selected
= FALSE
;
570 line
->dirty
= line
->cleareol
= 0;
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
);
582 redraw_view_dirty(struct view
*view
)
587 for (lineno
= 0; lineno
< view
->height
; lineno
++) {
588 if (view
->pos
.offset
+ lineno
>= view
->lines
)
590 if (!view
->line
[view
->pos
.offset
+ lineno
].dirty
)
593 if (!draw_view_line(view
, lineno
))
599 wnoutrefresh(view
->win
);
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
)) {
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
))
621 wnoutrefresh(view
->win
);
625 redraw_view(struct view
*view
)
628 redraw_view_from(view
, 0);
631 /* vim: set ts=8 sw=8 noexpandtab: */