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
[] = {
232 enum line_type type
= LINE_ID
;
234 if (!column
->opt
.id
.display
)
237 if (column
->opt
.id
.color
)
238 type
= colors
[((long) id
) % ARRAY_SIZE(colors
)];
240 return draw_field(view
, type
, id
, column
->width
, ALIGN_LEFT
, FALSE
);
244 draw_filename(struct view
*view
, struct view_column
*column
, const char *filename
, mode_t mode
)
246 size_t width
= filename
? utf8_width(filename
) : 0;
247 bool trim
= width
>= column
->width
;
248 enum line_type type
= S_ISDIR(mode
) ? LINE_DIRECTORY
: LINE_FILE
;
249 int column_width
= column
->width
? column
->width
: width
;
251 if (column
->opt
.file_name
.display
== FILENAME_NO
)
254 return draw_field(view
, type
, filename
, column_width
, ALIGN_LEFT
, trim
);
258 draw_file_size(struct view
*view
, struct view_column
*column
, unsigned long size
, mode_t mode
)
260 const char *str
= S_ISDIR(mode
) ? NULL
: mkfilesize(size
, column
->opt
.file_size
.display
);
262 if (!column
->width
|| column
->opt
.file_size
.display
== FILE_SIZE_NO
)
265 return draw_field(view
, LINE_FILE_SIZE
, str
, column
->width
, ALIGN_RIGHT
, FALSE
);
269 draw_mode(struct view
*view
, struct view_column
*column
, mode_t mode
)
271 const char *str
= mkmode(mode
);
273 if (!column
->width
|| !column
->opt
.mode
.display
)
276 return draw_field(view
, LINE_MODE
, str
, column
->width
, ALIGN_LEFT
, FALSE
);
280 draw_lineno_custom(struct view
*view
, struct view_column
*column
, unsigned int lineno
)
283 unsigned long digits3
= column
->width
< 3 ? 3 : column
->width
;
284 int max
= MIN(VIEW_MAX_LEN(view
), digits3
);
286 chtype separator
= opt_line_graphics
? ACS_VLINE
: '|';
288 if (!column
->opt
.line_number
.display
)
291 if (lineno
== 1 || (lineno
% column
->opt
.line_number
.interval
) == 0) {
292 static char fmt
[] = "%ld";
294 fmt
[1] = '0' + (digits3
<= 9 ? digits3
: 1);
295 if (string_format(number
, fmt
, lineno
))
299 draw_chars(view
, LINE_LINE_NUMBER
, text
, max
, TRUE
);
301 draw_space(view
, LINE_LINE_NUMBER
, max
, digits3
);
302 return draw_graphic(view
, LINE_DEFAULT
, &separator
, 1, TRUE
);
306 draw_lineno(struct view
*view
, struct view_column
*column
, unsigned int lineno
)
308 lineno
+= view
->pos
.offset
+ 1;
309 return draw_lineno_custom(view
, column
, lineno
);
313 draw_ref(struct view
*view
, struct view_column
*column
, const struct ref
*ref
)
315 enum line_type type
= !ref
|| !ref
->valid
? LINE_DEFAULT
: get_line_type_from_ref(ref
);
316 const char *name
= ref
? ref
->name
: NULL
;
318 return draw_field(view
, type
, name
, column
->width
, ALIGN_LEFT
, FALSE
);
322 draw_refs(struct view
*view
, struct view_column
*column
, const struct ref_list
*refs
)
326 if (!column
->opt
.commit_title
.refs
|| !refs
)
329 for (i
= 0; i
< refs
->size
; i
++) {
330 struct ref
*ref
= refs
->refs
[i
];
331 enum line_type type
= get_line_type_from_ref(ref
);
332 const struct ref_format
*format
= get_ref_format(ref
);
334 if (!strcmp(format
->start
, "hide:") && !*format
->end
)
337 if (draw_formatted(view
, type
, "%s%s%s", format
->start
, ref
->name
, format
->end
))
340 if (draw_text(view
, LINE_DEFAULT
, " "))
348 draw_status(struct view
*view
, struct view_column
*column
,
349 enum line_type type
, const char *status
)
351 const char *label
= mkstatus(status
? *status
: 0, column
->opt
.status
.display
);
353 return draw_field(view
, type
, label
, column
->width
, ALIGN_LEFT
, FALSE
);
360 static const enum line_type graph_colors
[] = {
370 static enum line_type
get_graph_color(struct graph_symbol
*symbol
)
373 return LINE_GRAPH_COMMIT
;
374 assert(symbol
->color
< ARRAY_SIZE(graph_colors
));
375 return graph_colors
[symbol
->color
];
379 draw_graph_utf8(struct view
*view
, struct graph_symbol
*symbol
, enum line_type color
, bool first
)
381 const char *chars
= graph_symbol_to_utf8(symbol
);
383 return draw_text(view
, color
, chars
+ !!first
);
387 draw_graph_ascii(struct view
*view
, struct graph_symbol
*symbol
, enum line_type color
, bool first
)
389 const char *chars
= graph_symbol_to_ascii(symbol
);
391 return draw_text(view
, color
, chars
+ !!first
);
395 draw_graph_chtype(struct view
*view
, struct graph_symbol
*symbol
, enum line_type color
, bool first
)
397 const chtype
*chars
= graph_symbol_to_chtype(symbol
);
399 return draw_graphic(view
, color
, chars
+ !!first
, 2 - !!first
, FALSE
);
402 typedef bool (*draw_graph_fn
)(struct view
*, struct graph_symbol
*, enum line_type
, bool);
405 draw_graph(struct view
*view
, const struct graph_canvas
*canvas
)
407 static const draw_graph_fn fns
[] = {
412 draw_graph_fn fn
= fns
[opt_line_graphics
];
415 for (i
= 0; i
< canvas
->size
; i
++) {
416 struct graph_symbol
*symbol
= &canvas
->symbols
[i
];
417 enum line_type color
= get_graph_color(symbol
);
419 if (fn(view
, symbol
, color
, i
== 0))
423 return draw_text(view
, LINE_DEFAULT
, " ");
427 draw_commit_title(struct view
*view
, struct view_column
*column
,
428 const struct graph_canvas
*graph
, const struct ref_list
*refs
,
429 const char *commit_title
)
431 if (graph
&& column
->opt
.commit_title
.graph
&&
432 draw_graph(view
, graph
))
434 if (draw_refs(view
, column
, refs
))
436 return draw_text_overflow(view
, commit_title
, LINE_DEFAULT
,
437 column
->opt
.commit_title
.overflow
, 0);
441 draw_diff_stat_part(struct view
*view
, enum line_type
*type
, const char **text
, char c
, enum line_type next_type
)
443 const char *sep
= c
== '|' ? strrchr(*text
, c
) : strchr(*text
, c
);
446 draw_text_expanded(view
, *type
, *text
, sep
- *text
, FALSE
);
455 draw_diff_stat(struct view
*view
, enum line_type
*type
, const char **text
)
457 draw_diff_stat_part(view
, type
, text
, '|', LINE_DEFAULT
);
458 if (draw_diff_stat_part(view
, type
, text
, 'B', LINE_DEFAULT
)) {
459 /* Handle binary diffstat: Bin <deleted> -> <added> bytes */
460 draw_diff_stat_part(view
, type
, text
, ' ', LINE_DIFF_DEL
);
461 draw_diff_stat_part(view
, type
, text
, '-', LINE_DEFAULT
);
462 draw_diff_stat_part(view
, type
, text
, ' ', LINE_DIFF_ADD
);
463 draw_diff_stat_part(view
, type
, text
, 'b', LINE_DEFAULT
);
466 draw_diff_stat_part(view
, type
, text
, '+', LINE_DIFF_ADD
);
467 draw_diff_stat_part(view
, type
, text
, '-', LINE_DIFF_DEL
);
472 view_column_draw(struct view
*view
, struct line
*line
, unsigned int lineno
)
474 struct view_column
*column
= view
->columns
;
475 struct view_column_data column_data
= {};
477 if (!view
->ops
->get_column_data(view
, line
, &column_data
))
480 if (column_data
.section
)
481 column
= column_data
.section
;
483 for (; column
; column
= column
->next
) {
484 mode_t mode
= column_data
.mode
? *column_data
.mode
: 0;
489 switch (column
->type
) {
490 case VIEW_COLUMN_DATE
:
491 if (draw_date(view
, column
, column_data
.date
))
495 case VIEW_COLUMN_AUTHOR
:
496 if (draw_author(view
, column
, column_data
.author
))
500 case VIEW_COLUMN_REF
:
501 if (draw_ref(view
, column
, column_data
.ref
))
506 if (draw_id(view
, column
, column_data
.reflog
? column_data
.reflog
: column_data
.id
))
510 case VIEW_COLUMN_LINE_NUMBER
:
511 if (draw_lineno(view
, column
, column_data
.line_number
? *column_data
.line_number
: lineno
))
515 case VIEW_COLUMN_MODE
:
516 if (draw_mode(view
, column
, mode
))
520 case VIEW_COLUMN_FILE_SIZE
:
521 if (draw_file_size(view
, column
, column_data
.file_size
? *column_data
.file_size
: 0, mode
))
525 case VIEW_COLUMN_COMMIT_TITLE
:
526 if (draw_commit_title(view
, column
, column_data
.graph
,
527 column_data
.refs
, column_data
.commit_title
))
531 case VIEW_COLUMN_FILE_NAME
:
532 if (draw_filename(view
, column
, column_data
.file_name
, mode
))
536 case VIEW_COLUMN_SECTION
:
537 if (draw_text(view
, column
->opt
.section
.type
, column
->opt
.section
.text
))
541 case VIEW_COLUMN_STATUS
:
542 if (draw_status(view
, column
, line
->type
, column_data
.status
))
546 case VIEW_COLUMN_TEXT
:
548 enum line_type type
= line
->type
;
549 const char *text
= column_data
.text
;
551 if (line
->wrapped
&& draw_text(view
, LINE_DELIMITER
, "+"))
554 if (line
->graph_indent
) {
555 size_t indent
= get_graph_indent(text
);
557 if (draw_text_expanded(view
, LINE_DEFAULT
, text
, indent
, FALSE
))
561 if (type
== LINE_DIFF_STAT
)
562 draw_diff_stat(view
, &type
, &text
);
563 if (line
->commit_title
) {
564 if (draw_text_overflow(view
, text
, LINE_DEFAULT
,
565 column
->opt
.text
.commit_title_overflow
, 4))
567 } else if (draw_text(view
, type
, text
)) {
579 draw_view_line(struct view
*view
, unsigned int lineno
)
582 bool selected
= (view
->pos
.offset
+ lineno
== view
->pos
.lineno
);
584 /* FIXME: Disabled during code split.
585 assert(view_is_displayed(view));
588 if (view
->pos
.offset
+ lineno
>= view
->lines
)
591 line
= &view
->line
[view
->pos
.offset
+ lineno
];
593 wmove(view
->win
, lineno
, 0);
595 wclrtoeol(view
->win
);
597 view
->curline
= line
;
598 view
->curtype
= LINE_NONE
;
599 line
->selected
= FALSE
;
600 line
->dirty
= line
->cleareol
= 0;
603 set_view_attr(view
, LINE_CURSOR
);
604 line
->selected
= TRUE
;
605 view
->ops
->select(view
, line
);
608 return view
->ops
->draw(view
, line
, lineno
);
612 redraw_view_dirty(struct view
*view
)
617 for (lineno
= 0; lineno
< view
->height
; lineno
++) {
618 if (view
->pos
.offset
+ lineno
>= view
->lines
)
620 if (!view
->line
[view
->pos
.offset
+ lineno
].dirty
)
623 if (!draw_view_line(view
, lineno
))
629 wnoutrefresh(view
->win
);
633 redraw_view_from(struct view
*view
, int lineno
)
635 assert(0 <= lineno
&& lineno
< view
->height
);
637 if (view
->columns
&& view_column_info_changed(view
, FALSE
)) {
640 view_column_reset(view
);
641 for (i
= 0; i
< view
->lines
; i
++) {
642 view_column_info_update(view
, &view
->line
[i
]);
646 for (; lineno
< view
->height
; lineno
++) {
647 if (!draw_view_line(view
, lineno
))
651 wnoutrefresh(view
->win
);
655 redraw_view(struct view
*view
)
658 redraw_view_from(view
, 0);
661 /* vim: set ts=8 sw=8 noexpandtab: */