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 int len
= strlen(text
);
138 if (draw_text_expanded(view
, type
, text
, max
, max
< overflow
))
141 text
= len
> overflow
? text
+ overflow
: "";
142 type
= LINE_OVERFLOW
;
145 if (*text
&& draw_text(view
, type
, text
))
148 return VIEW_MAX_LEN(view
) <= 0;
151 bool PRINTF_LIKE(3, 4)
152 draw_formatted(struct view
*view
, enum line_type type
, const char *format
, ...)
154 char text
[SIZEOF_STR
];
157 FORMAT_BUFFER(text
, sizeof(text
), format
, retval
, TRUE
);
158 return retval
>= 0 ? draw_text(view
, type
, text
) : VIEW_MAX_LEN(view
) <= 0;
162 draw_graphic(struct view
*view
, enum line_type type
, const chtype graphic
[], size_t size
, bool separator
)
164 size_t skip
= view
->pos
.col
> view
->col
? view
->pos
.col
- view
->col
: 0;
165 int max
= VIEW_MAX_LEN(view
);
171 set_view_attr(view
, type
);
172 /* Using waddch() instead of waddnstr() ensures that
173 * they'll be rendered correctly for the cursor line. */
174 for (i
= skip
; i
< size
; i
++)
175 waddch(view
->win
, graphic
[i
]);
179 if (size
< max
&& skip
<= size
)
180 waddch(view
->win
, ' ');
184 return VIEW_MAX_LEN(view
) <= 0;
188 draw_field(struct view
*view
, enum line_type type
, const char *text
, int width
, enum align align
, bool trim
)
190 int max
= MIN(VIEW_MAX_LEN(view
), width
+ 1);
194 return draw_space(view
, type
, max
, max
);
196 if (align
== ALIGN_RIGHT
) {
197 int textlen
= utf8_width_max(text
, max
);
198 int leftpad
= max
- textlen
- 1;
201 if (draw_space(view
, type
, leftpad
, leftpad
))
208 return draw_chars(view
, type
, text
, max
- 1, trim
)
209 || draw_space(view
, LINE_DEFAULT
, max
- (view
->col
- col
), max
);
213 draw_date(struct view
*view
, struct view_column
*column
, const struct time
*time
)
215 enum date date
= column
->opt
.date
.display
;
216 const char *text
= mkdate(time
, date
);
217 enum align align
= date
== DATE_RELATIVE
? ALIGN_RIGHT
: ALIGN_LEFT
;
222 return draw_field(view
, LINE_DATE
, text
, column
->width
, align
, FALSE
);
226 draw_author(struct view
*view
, struct view_column
*column
, const struct ident
*author
)
228 bool trim
= author_trim(column
->width
);
229 const char *text
= mkauthor(author
, column
->opt
.author
.width
, column
->opt
.author
.display
);
231 if (column
->opt
.author
.display
== AUTHOR_NO
)
234 return draw_field(view
, LINE_AUTHOR
, text
, column
->width
, ALIGN_LEFT
, trim
);
238 draw_id(struct view
*view
, struct view_column
*column
, const char *id
)
240 enum line_type type
= LINE_ID
;
242 if (!column
->opt
.id
.display
)
245 if (column
->opt
.id
.color
&& id
) {
246 hashval_t color
= iterative_hash(id
, SIZEOF_REV
- 1, 0);
248 type
= palette_colors
[color
% ARRAY_SIZE(palette_colors
)];
251 return draw_field(view
, type
, id
, column
->width
, ALIGN_LEFT
, FALSE
);
255 draw_filename(struct view
*view
, struct view_column
*column
, const char *filename
, mode_t mode
)
257 size_t width
= filename
? utf8_width(filename
) : 0;
258 bool trim
= width
>= column
->width
;
259 enum line_type type
= S_ISDIR(mode
) ? LINE_DIRECTORY
: LINE_FILE
;
260 int column_width
= column
->width
? column
->width
: width
;
262 if (column
->opt
.file_name
.display
== FILENAME_NO
)
265 return draw_field(view
, type
, filename
, column_width
, ALIGN_LEFT
, trim
);
269 draw_file_size(struct view
*view
, struct view_column
*column
, unsigned long size
, mode_t mode
)
271 const char *str
= S_ISDIR(mode
) ? NULL
: mkfilesize(size
, column
->opt
.file_size
.display
);
273 if (!column
->width
|| column
->opt
.file_size
.display
== FILE_SIZE_NO
)
276 return draw_field(view
, LINE_FILE_SIZE
, str
, column
->width
, ALIGN_RIGHT
, FALSE
);
280 draw_mode(struct view
*view
, struct view_column
*column
, mode_t mode
)
282 const char *str
= mkmode(mode
);
284 if (!column
->width
|| !column
->opt
.mode
.display
)
287 return draw_field(view
, LINE_MODE
, str
, column
->width
, ALIGN_LEFT
, FALSE
);
291 draw_lineno_custom(struct view
*view
, struct view_column
*column
, unsigned int lineno
)
294 unsigned long digits3
= column
->width
< 3 ? 3 : column
->width
;
295 int max
= MIN(VIEW_MAX_LEN(view
), digits3
);
297 chtype separator
= opt_line_graphics
? ACS_VLINE
: '|';
299 if (!column
->opt
.line_number
.display
)
302 if (lineno
== 1 || (lineno
% column
->opt
.line_number
.interval
) == 0) {
303 static char fmt
[] = "%ld";
305 fmt
[1] = '0' + (digits3
<= 9 ? digits3
: 1);
306 if (string_format(number
, fmt
, lineno
))
310 draw_chars(view
, LINE_LINE_NUMBER
, text
, max
, TRUE
);
312 draw_space(view
, LINE_LINE_NUMBER
, max
, digits3
);
313 return draw_graphic(view
, LINE_DEFAULT
, &separator
, 1, TRUE
);
317 draw_lineno(struct view
*view
, struct view_column
*column
, unsigned int lineno
)
319 lineno
+= view
->pos
.offset
+ 1;
320 return draw_lineno_custom(view
, column
, lineno
);
324 draw_ref(struct view
*view
, struct view_column
*column
, const struct ref
*ref
)
326 enum line_type type
= !ref
|| !ref
->valid
? LINE_DEFAULT
: get_line_type_from_ref(ref
);
327 const char *name
= ref
? ref
->name
: NULL
;
329 return draw_field(view
, type
, name
, column
->width
, ALIGN_LEFT
, FALSE
);
333 draw_refs(struct view
*view
, struct view_column
*column
, const struct ref_list
*refs
)
337 if (!column
->opt
.commit_title
.refs
|| !refs
)
340 for (i
= 0; i
< refs
->size
; i
++) {
341 struct ref
*ref
= refs
->refs
[i
];
342 enum line_type type
= get_line_type_from_ref(ref
);
343 const struct ref_format
*format
= get_ref_format(ref
);
345 if (!strcmp(format
->start
, "hide:") && !*format
->end
)
348 if (draw_formatted(view
, type
, "%s%s%s", format
->start
, ref
->name
, format
->end
))
351 if (draw_text(view
, LINE_DEFAULT
, " "))
359 draw_status(struct view
*view
, struct view_column
*column
,
360 enum line_type type
, const char *status
)
362 const char *label
= mkstatus(status
? *status
: 0, column
->opt
.status
.display
);
364 return draw_field(view
, type
, label
, column
->width
, ALIGN_LEFT
, FALSE
);
371 static enum line_type
get_graph_color(struct graph_symbol
*symbol
)
374 return LINE_GRAPH_COMMIT
;
375 assert(symbol
->color
< ARRAY_SIZE(palette_colors
));
376 return palette_colors
[symbol
->color
];
380 draw_graph_utf8(struct view
*view
, struct graph_symbol
*symbol
, enum line_type color
, bool first
)
382 const char *chars
= graph_symbol_to_utf8(symbol
);
384 return draw_text(view
, color
, chars
+ !!first
);
388 draw_graph_ascii(struct view
*view
, struct graph_symbol
*symbol
, enum line_type color
, bool first
)
390 const char *chars
= graph_symbol_to_ascii(symbol
);
392 return draw_text(view
, color
, chars
+ !!first
);
396 draw_graph_chtype(struct view
*view
, struct graph_symbol
*symbol
, enum line_type color
, bool first
)
398 const chtype
*chars
= graph_symbol_to_chtype(symbol
);
400 return draw_graphic(view
, color
, chars
+ !!first
, 2 - !!first
, FALSE
);
403 typedef bool (*draw_graph_fn
)(struct view
*, struct graph_symbol
*, enum line_type
, bool);
406 draw_graph(struct view
*view
, const struct graph_canvas
*canvas
)
408 static const draw_graph_fn fns
[] = {
413 draw_graph_fn fn
= fns
[opt_line_graphics
];
416 for (i
= 0; i
< canvas
->size
; i
++) {
417 struct graph_symbol
*symbol
= &canvas
->symbols
[i
];
418 enum line_type color
= get_graph_color(symbol
);
420 if (fn(view
, symbol
, color
, i
== 0))
424 return draw_text(view
, LINE_DEFAULT
, " ");
428 draw_commit_title(struct view
*view
, struct view_column
*column
,
429 const struct graph_canvas
*graph
, const struct ref_list
*refs
,
430 const char *commit_title
)
432 if (graph
&& column
->opt
.commit_title
.graph
&&
433 draw_graph(view
, graph
))
435 if (draw_refs(view
, column
, refs
))
437 return draw_text_overflow(view
, commit_title
, LINE_DEFAULT
,
438 column
->opt
.commit_title
.overflow
, 0);
442 draw_diff_stat_part(struct view
*view
, enum line_type
*type
, const char **text
, char c
, enum line_type next_type
)
444 const char *sep
= c
== '|' ? strrchr(*text
, c
) : strchr(*text
, c
);
447 draw_text_expanded(view
, *type
, *text
, sep
- *text
, FALSE
);
456 draw_diff_stat(struct view
*view
, enum line_type
*type
, const char **text
)
458 draw_diff_stat_part(view
, type
, text
, '|', LINE_DEFAULT
);
459 if (draw_diff_stat_part(view
, type
, text
, 'B', LINE_DEFAULT
)) {
460 /* Handle binary diffstat: Bin <deleted> -> <added> bytes */
461 draw_diff_stat_part(view
, type
, text
, ' ', LINE_DIFF_DEL
);
462 draw_diff_stat_part(view
, type
, text
, '-', LINE_DEFAULT
);
463 draw_diff_stat_part(view
, type
, text
, ' ', LINE_DIFF_ADD
);
464 draw_diff_stat_part(view
, type
, text
, 'b', LINE_DEFAULT
);
467 draw_diff_stat_part(view
, type
, text
, '+', LINE_DIFF_ADD
);
468 draw_diff_stat_part(view
, type
, text
, '-', LINE_DIFF_DEL
);
473 view_column_draw(struct view
*view
, struct line
*line
, unsigned int lineno
)
475 struct view_column
*column
= view
->columns
;
476 struct view_column_data column_data
= {};
478 if (!view
->ops
->get_column_data(view
, line
, &column_data
))
481 if (column_data
.section
)
482 column
= column_data
.section
;
484 for (; column
; column
= column
->next
) {
485 mode_t mode
= column_data
.mode
? *column_data
.mode
: 0;
490 switch (column
->type
) {
491 case VIEW_COLUMN_DATE
:
492 if (draw_date(view
, column
, column_data
.date
))
496 case VIEW_COLUMN_AUTHOR
:
497 if (draw_author(view
, column
, column_data
.author
))
501 case VIEW_COLUMN_REF
:
502 if (draw_ref(view
, column
, column_data
.ref
))
507 if (draw_id(view
, column
, column_data
.reflog
? column_data
.reflog
: column_data
.id
))
511 case VIEW_COLUMN_LINE_NUMBER
:
512 if (draw_lineno(view
, column
, column_data
.line_number
? *column_data
.line_number
: lineno
))
516 case VIEW_COLUMN_MODE
:
517 if (draw_mode(view
, column
, mode
))
521 case VIEW_COLUMN_FILE_SIZE
:
522 if (draw_file_size(view
, column
, column_data
.file_size
? *column_data
.file_size
: 0, mode
))
526 case VIEW_COLUMN_COMMIT_TITLE
:
527 if (draw_commit_title(view
, column
, column_data
.graph
,
528 column_data
.refs
, column_data
.commit_title
))
532 case VIEW_COLUMN_FILE_NAME
:
533 if (draw_filename(view
, column
, column_data
.file_name
, mode
))
537 case VIEW_COLUMN_SECTION
:
538 if (draw_text(view
, column
->opt
.section
.type
, column
->opt
.section
.text
))
542 case VIEW_COLUMN_STATUS
:
543 if (draw_status(view
, column
, line
->type
, column_data
.status
))
547 case VIEW_COLUMN_TEXT
:
549 enum line_type type
= line
->type
;
550 const char *text
= column_data
.text
;
552 if (line
->wrapped
&& draw_text(view
, LINE_DELIMITER
, "+"))
555 if (line
->graph_indent
) {
556 size_t indent
= get_graph_indent(text
);
558 if (draw_text_expanded(view
, LINE_DEFAULT
, text
, indent
, FALSE
))
562 if (type
== LINE_DIFF_STAT
)
563 draw_diff_stat(view
, &type
, &text
);
564 if (line
->commit_title
) {
565 if (draw_text_overflow(view
, text
, LINE_DEFAULT
,
566 column
->opt
.text
.commit_title_overflow
, 4))
568 } else if (draw_text(view
, type
, text
)) {
580 draw_view_line(struct view
*view
, unsigned int lineno
)
583 bool selected
= (view
->pos
.offset
+ lineno
== view
->pos
.lineno
);
585 /* FIXME: Disabled during code split.
586 assert(view_is_displayed(view));
589 if (view
->pos
.offset
+ lineno
>= view
->lines
)
592 line
= &view
->line
[view
->pos
.offset
+ lineno
];
594 wmove(view
->win
, lineno
, 0);
596 wclrtoeol(view
->win
);
598 view
->curline
= line
;
599 view
->curtype
= LINE_NONE
;
600 line
->selected
= FALSE
;
601 line
->dirty
= line
->cleareol
= 0;
604 set_view_attr(view
, LINE_CURSOR
);
605 line
->selected
= TRUE
;
606 view
->ops
->select(view
, line
);
609 return view
->ops
->draw(view
, line
, lineno
);
613 redraw_view_dirty(struct view
*view
)
618 for (lineno
= 0; lineno
< view
->height
; lineno
++) {
619 if (view
->pos
.offset
+ lineno
>= view
->lines
)
621 if (!view
->line
[view
->pos
.offset
+ lineno
].dirty
)
624 if (!draw_view_line(view
, lineno
))
630 wnoutrefresh(view
->win
);
634 redraw_view_from(struct view
*view
, int lineno
)
636 assert(0 <= lineno
&& lineno
< view
->height
);
638 if (view
->columns
&& view_column_info_changed(view
, FALSE
)) {
641 view_column_reset(view
);
642 for (i
= 0; i
< view
->lines
; i
++) {
643 view_column_info_update(view
, &view
->line
[i
]);
647 for (; lineno
< view
->height
; lineno
++) {
648 if (!draw_view_line(view
, lineno
))
652 wnoutrefresh(view
->win
);
656 redraw_view(struct view
*view
)
659 redraw_view_from(view
, 0);
662 /* vim: set ts=8 sw=8 noexpandtab: */