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
), FALSE
);
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
.display
;
199 const char *text
= mkdate(time
, date
);
200 enum align align
= date
== DATE_RELATIVE
? ALIGN_RIGHT
: ALIGN_LEFT
;
205 return draw_field(view
, LINE_DATE
, text
, column
->width
, align
, FALSE
);
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
)
217 return draw_field(view
, LINE_AUTHOR
, text
, column
->width
, ALIGN_LEFT
, trim
);
221 draw_id(struct view
*view
, struct view_column
*column
, const char *id
)
223 static const enum line_type colors
[] = {
239 enum line_type type
= LINE_ID
;
241 if (!column
->opt
.id
.display
)
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
);
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
)
264 return draw_field(view
, type
, filename
, column_width
, ALIGN_LEFT
, trim
);
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
)
275 return draw_field(view
, LINE_FILE_SIZE
, str
, column
->width
, ALIGN_RIGHT
, FALSE
);
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
)
286 return draw_field(view
, LINE_MODE
, str
, column
->width
, ALIGN_LEFT
, FALSE
);
290 draw_lineno_custom(struct view
*view
, struct view_column
*column
, unsigned int lineno
)
293 unsigned long digits3
= column
->width
< 3 ? 3 : column
->width
;
294 int max
= MIN(VIEW_MAX_LEN(view
), digits3
);
296 chtype separator
= opt_line_graphics
? ACS_VLINE
: '|';
298 if (!column
->opt
.line_number
.display
)
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
))
309 draw_chars(view
, LINE_LINE_NUMBER
, text
, max
, TRUE
);
311 draw_space(view
, LINE_LINE_NUMBER
, max
, digits3
);
312 return draw_graphic(view
, LINE_DEFAULT
, &separator
, 1, TRUE
);
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
);
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
);
332 draw_refs(struct view
*view
, struct view_column
*column
, const struct ref_list
*refs
)
336 if (!column
->opt
.commit_title
.refs
|| !refs
)
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
)
347 if (draw_formatted(view
, type
, "%s%s%s", format
->start
, ref
->name
, format
->end
))
350 if (draw_text(view
, LINE_DEFAULT
, " "))
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
);
370 static const enum line_type graph_colors
[] = {
380 static enum line_type
get_graph_color(struct graph_symbol
*symbol
)
383 return LINE_GRAPH_COMMIT
;
384 assert(symbol
->color
< ARRAY_SIZE(graph_colors
));
385 return graph_colors
[symbol
->color
];
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
);
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
);
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);
415 draw_graph(struct view
*view
, const struct graph_canvas
*canvas
)
417 static const draw_graph_fn fns
[] = {
422 draw_graph_fn fn
= fns
[opt_line_graphics
];
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))
433 return draw_text(view
, LINE_DEFAULT
, " ");
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
))
444 if (draw_refs(view
, column
, refs
))
446 return draw_text_overflow(view
, commit_title
, LINE_DEFAULT
,
447 column
->opt
.commit_title
.overflow
, 0);
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
);
456 draw_text_expanded(view
, *type
, *text
, sep
- *text
, FALSE
);
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
);
476 draw_diff_stat_part(view
, type
, text
, '+', LINE_DIFF_ADD
);
477 draw_diff_stat_part(view
, type
, text
, '-', LINE_DIFF_DEL
);
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
))
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;
499 switch (column
->type
) {
500 case VIEW_COLUMN_DATE
:
501 if (draw_date(view
, column
, column_data
.date
))
505 case VIEW_COLUMN_AUTHOR
:
506 if (draw_author(view
, column
, column_data
.author
))
510 case VIEW_COLUMN_REF
:
511 if (draw_ref(view
, column
, column_data
.ref
))
516 if (draw_id(view
, column
, column_data
.reflog
? column_data
.reflog
: column_data
.id
))
520 case VIEW_COLUMN_LINE_NUMBER
:
521 if (draw_lineno(view
, column
, column_data
.line_number
? *column_data
.line_number
: lineno
))
525 case VIEW_COLUMN_MODE
:
526 if (draw_mode(view
, column
, mode
))
530 case VIEW_COLUMN_FILE_SIZE
:
531 if (draw_file_size(view
, column
, column_data
.file_size
? *column_data
.file_size
: 0, mode
))
535 case VIEW_COLUMN_COMMIT_TITLE
:
536 if (draw_commit_title(view
, column
, column_data
.graph
,
537 column_data
.refs
, column_data
.commit_title
))
541 case VIEW_COLUMN_FILE_NAME
:
542 if (draw_filename(view
, column
, column_data
.file_name
, mode
))
546 case VIEW_COLUMN_SECTION
:
547 if (draw_text(view
, column
->opt
.section
.type
, column
->opt
.section
.text
))
551 case VIEW_COLUMN_STATUS
:
552 if (draw_status(view
, column
, line
->type
, column_data
.status
))
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
, "+"))
564 if (line
->graph_indent
) {
565 size_t indent
= get_graph_indent(text
);
567 if (draw_text_expanded(view
, LINE_DEFAULT
, text
, indent
, FALSE
))
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))
577 } else if (draw_text(view
, type
, text
)) {
589 draw_view_line(struct view
*view
, unsigned int lineno
)
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
)
601 line
= &view
->line
[view
->pos
.offset
+ lineno
];
603 wmove(view
->win
, lineno
, 0);
605 wclrtoeol(view
->win
);
607 view
->curline
= line
;
608 view
->curtype
= LINE_NONE
;
609 line
->selected
= FALSE
;
610 line
->dirty
= line
->cleareol
= 0;
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
);
622 redraw_view_dirty(struct view
*view
)
627 for (lineno
= 0; lineno
< view
->height
; lineno
++) {
628 if (view
->pos
.offset
+ lineno
>= view
->lines
)
630 if (!view
->line
[view
->pos
.offset
+ lineno
].dirty
)
633 if (!draw_view_line(view
, lineno
))
639 wnoutrefresh(view
->win
);
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
)) {
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
))
661 wnoutrefresh(view
->win
);
665 redraw_view(struct view
*view
)
668 redraw_view_from(view
, 0);
671 /* vim: set ts=8 sw=8 noexpandtab: */