1 /* Copyright (c) 2006-2015 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"
18 #include "compat/hashtab.h"
20 static const enum line_type palette_colors
[] = {
42 set_view_attr(struct view
*view
, enum line_type type
)
44 if (!view
->curline
->selected
&& view
->curtype
!= type
) {
45 (void) wattrset(view
->win
, get_view_attr(view
, type
));
46 wchgat(view
->win
, -1, 0, get_view_color(view
, type
), NULL
);
51 #define VIEW_MAX_LEN(view) ((view)->width + (view)->pos.col - (view)->col)
54 draw_chars(struct view
*view
, enum line_type type
, const char *string
, int length
,
55 int max_width
, bool use_tilde
)
60 size_t skip
= view
->pos
.col
> view
->col
? view
->pos
.col
- view
->col
: 0;
63 return VIEW_MAX_LEN(view
) <= 0;
65 len
= utf8_length(&string
, length
, skip
, &col
, max_width
, &trimmed
, use_tilde
, opt_tab_size
);
67 if (opt_iconv_out
!= ICONV_NONE
) {
68 string
= encoding_iconv(opt_iconv_out
, string
, len
);
70 return VIEW_MAX_LEN(view
) <= 0;
73 set_view_attr(view
, type
);
75 waddnstr(view
->win
, string
, len
);
77 if (trimmed
&& use_tilde
) {
78 set_view_attr(view
, LINE_DELIMITER
);
79 waddch(view
->win
, '~');
85 return VIEW_MAX_LEN(view
) <= 0;
89 draw_space(struct view
*view
, enum line_type type
, int max
, int spaces
)
91 static char space
[] = " ";
93 spaces
= MIN(max
, spaces
);
96 int len
= MIN(spaces
, sizeof(space
) - 1);
98 if (draw_chars(view
, type
, space
, -1, len
, false))
103 return VIEW_MAX_LEN(view
) <= 0;
107 draw_text_expanded(struct view
*view
, enum line_type type
, const char *string
, int length
, int max_width
, bool use_tilde
)
109 static char text
[SIZEOF_STR
];
112 length
= strlen(string
);
115 size_t pos
= string_expand(text
, sizeof(text
), string
, length
, opt_tab_size
);
117 if (draw_chars(view
, type
, text
, -1, max_width
, use_tilde
))
121 } while (*string
&& length
> 0);
123 return VIEW_MAX_LEN(view
) <= 0;
127 draw_textn(struct view
*view
, enum line_type type
, const char *string
, int length
)
129 return draw_text_expanded(view
, type
, string
, length
, VIEW_MAX_LEN(view
), false);
133 draw_text(struct view
*view
, enum line_type type
, const char *string
)
135 return draw_textn(view
, type
, string
, -1);
139 draw_text_overflow(struct view
*view
, const char *text
, enum line_type type
,
140 int overflow_length
, int offset
)
142 bool on
= overflow_length
> 0;
145 int overflow
= overflow_length
+ offset
;
146 int max
= MIN(VIEW_MAX_LEN(view
), overflow
);
147 const char *tmp
= text
;
150 size_t len
= utf8_length(&tmp
, -1, 0, &text_width
, max
, &trimmed
, false, 1);
152 if (draw_text_expanded(view
, type
, text
, -1, text_width
, max
< overflow
))
156 type
= LINE_OVERFLOW
;
159 if (*text
&& draw_text(view
, type
, text
))
162 return VIEW_MAX_LEN(view
) <= 0;
165 bool PRINTF_LIKE(3, 4)
166 draw_formatted(struct view
*view
, enum line_type type
, const char *format
, ...)
168 char text
[SIZEOF_STR
];
171 FORMAT_BUFFER(text
, sizeof(text
), format
, retval
, true);
172 return retval
>= 0 ? draw_text(view
, type
, text
) : VIEW_MAX_LEN(view
) <= 0;
176 draw_graphic(struct view
*view
, enum line_type type
, const chtype graphic
[], size_t size
, bool separator
)
178 size_t skip
= view
->pos
.col
> view
->col
? view
->pos
.col
- view
->col
: 0;
179 int max
= VIEW_MAX_LEN(view
);
185 set_view_attr(view
, type
);
186 /* Using waddch() instead of waddnstr() ensures that
187 * they'll be rendered correctly for the cursor line. */
188 for (i
= skip
; i
< size
; i
++)
189 waddch(view
->win
, graphic
[i
]);
193 if (size
< max
&& skip
<= size
)
194 waddch(view
->win
, ' ');
198 return VIEW_MAX_LEN(view
) <= 0;
202 draw_field(struct view
*view
, enum line_type type
, const char *text
, int width
, enum align align
, bool trim
)
204 int max
= MIN(VIEW_MAX_LEN(view
), width
+ 1);
208 return draw_space(view
, type
, max
, max
);
210 if (align
== ALIGN_RIGHT
) {
211 int textlen
= utf8_width_max(text
, max
);
212 int leftpad
= max
- textlen
- 1;
215 if (draw_space(view
, type
, leftpad
, leftpad
))
222 return draw_chars(view
, type
, text
, -1, max
- 1, trim
)
223 || draw_space(view
, type
, max
- (view
->col
- col
), max
);
227 draw_date(struct view
*view
, struct view_column
*column
, const struct time
*time
)
229 struct date_options
*opt
= &column
->opt
.date
;
230 enum date date
= opt
->display
;
231 const char *text
= mkdate(time
, date
, opt
->local
, opt
->format
);
232 enum align align
= date
== DATE_RELATIVE
? ALIGN_RIGHT
: ALIGN_LEFT
;
237 return draw_field(view
, LINE_DATE
, text
, column
->width
, align
, false);
241 draw_author(struct view
*view
, struct view_column
*column
, const struct ident
*author
)
243 bool trim
= author_trim(column
->width
);
244 const char *text
= mkauthor(author
, column
->opt
.author
.width
, column
->opt
.author
.display
);
246 if (column
->opt
.author
.display
== AUTHOR_NO
)
249 return draw_field(view
, LINE_AUTHOR
, text
, column
->width
, ALIGN_LEFT
, trim
);
253 draw_id(struct view
*view
, struct view_column
*column
, const char *id
)
255 enum line_type type
= LINE_ID
;
257 if (!column
->opt
.id
.display
)
260 if (column
->opt
.id
.color
&& id
) {
261 hashval_t color
= iterative_hash(id
, SIZEOF_REV
- 1, 0);
263 type
= palette_colors
[color
% ARRAY_SIZE(palette_colors
)];
266 return draw_field(view
, type
, id
, column
->width
, ALIGN_LEFT
, false);
270 draw_filename(struct view
*view
, struct view_column
*column
, const char *filename
, mode_t mode
)
272 size_t width
= filename
? utf8_width(filename
) : 0;
273 bool trim
= width
>= column
->width
;
274 enum line_type type
= S_ISDIR(mode
) ? LINE_DIRECTORY
: LINE_FILE
;
275 int column_width
= column
->width
? column
->width
: width
;
277 if (column
->opt
.file_name
.display
== FILENAME_NO
)
280 return draw_field(view
, type
, filename
, column_width
, ALIGN_LEFT
, trim
);
284 draw_file_size(struct view
*view
, struct view_column
*column
, unsigned long size
, mode_t mode
)
286 const char *str
= S_ISDIR(mode
) ? NULL
: mkfilesize(size
, column
->opt
.file_size
.display
);
288 if (!column
->width
|| column
->opt
.file_size
.display
== FILE_SIZE_NO
)
291 return draw_field(view
, LINE_FILE_SIZE
, str
, column
->width
, ALIGN_RIGHT
, false);
295 draw_mode(struct view
*view
, struct view_column
*column
, mode_t mode
)
297 const char *str
= mkmode(mode
);
299 if (!column
->width
|| !column
->opt
.mode
.display
)
302 return draw_field(view
, LINE_MODE
, str
, column
->width
, ALIGN_LEFT
, false);
306 draw_lineno_custom(struct view
*view
, struct view_column
*column
, unsigned int lineno
)
309 unsigned long digits3
= column
->width
< 3 ? 3 : column
->width
;
310 int max
= MIN(VIEW_MAX_LEN(view
), digits3
);
312 chtype separator
= opt_line_graphics
? ACS_VLINE
: '|';
313 struct line_number_options
*opts
= &column
->opt
.line_number
;
314 int interval
= opts
->interval
> 0 ? opts
->interval
: 5;
316 if (!column
->opt
.line_number
.display
)
319 if (lineno
== 1 || (lineno
% interval
) == 0) {
320 static char fmt
[] = "%ld";
322 fmt
[1] = '0' + (digits3
<= 9 ? digits3
: 1);
323 if (string_format(number
, fmt
, lineno
))
327 draw_chars(view
, LINE_LINE_NUMBER
, text
, -1, max
, true);
329 draw_space(view
, LINE_LINE_NUMBER
, max
, digits3
);
330 return draw_graphic(view
, LINE_DEFAULT
, &separator
, 1, true);
334 draw_lineno(struct view
*view
, struct view_column
*column
, unsigned int lineno
)
336 lineno
+= view
->pos
.offset
+ 1;
337 return draw_lineno_custom(view
, column
, lineno
);
341 draw_ref(struct view
*view
, struct view_column
*column
, const struct ref
*ref
)
343 enum line_type type
= !ref
|| !ref
->valid
? LINE_DEFAULT
: get_line_type_from_ref(ref
);
344 const char *name
= ref
? ref
->name
: NULL
;
346 return draw_field(view
, type
, name
, column
->width
, ALIGN_LEFT
, false);
350 draw_refs(struct view
*view
, struct view_column
*column
, const struct ref
*refs
)
352 if (!column
->opt
.commit_title
.refs
|| !refs
)
355 for (; refs
; refs
= refs
->next
) {
356 const struct ref
*ref
= refs
;
357 enum line_type type
= get_line_type_from_ref(ref
);
358 const struct ref_format
*format
= get_ref_format(opt_reference_format
, ref
);
360 if (!strcmp(format
->start
, "hide:") && !*format
->end
)
363 if (draw_formatted(view
, type
, "%s%s%s", format
->start
, ref
->name
, format
->end
))
366 if (draw_text(view
, LINE_DEFAULT
, " "))
374 draw_status(struct view
*view
, struct view_column
*column
,
375 enum line_type type
, const char *status
)
377 const char *label
= mkstatus(status
? *status
: 0, column
->opt
.status
.display
);
379 return draw_field(view
, type
, label
, column
->width
, ALIGN_LEFT
, false);
386 static inline enum line_type
387 get_graph_color(int color_id
)
389 if (color_id
== GRAPH_COMMIT_COLOR
)
390 return LINE_GRAPH_COMMIT
;
391 assert(color_id
< ARRAY_SIZE(palette_colors
));
392 return palette_colors
[color_id
];
396 draw_graph_utf8(void *view
, const struct graph
*graph
, const struct graph_symbol
*symbol
, int color_id
, bool first
)
398 const char *chars
= graph
->symbol_to_utf8(symbol
);
400 return draw_text(view
, get_graph_color(color_id
), chars
+ !!first
);
404 draw_graph_ascii(void *view
, const struct graph
*graph
, const struct graph_symbol
*symbol
, int color_id
, bool first
)
406 const char *chars
= graph
->symbol_to_ascii(symbol
);
408 return draw_text(view
, get_graph_color(color_id
), chars
+ !!first
);
412 draw_graph_chtype(void *view
, const struct graph
*graph
, const struct graph_symbol
*symbol
, int color_id
, bool first
)
414 const chtype
*chars
= graph
->symbol_to_chtype(symbol
);
416 return draw_graphic(view
, get_graph_color(color_id
), chars
+ !!first
, 2 - !!first
, false);
420 draw_graph(struct view
*view
, const struct graph
*graph
, const struct graph_canvas
*canvas
)
422 static const graph_symbol_iterator_fn fns
[] = {
428 graph
->foreach_symbol(graph
, canvas
, fns
[opt_line_graphics
], view
);
429 return draw_text(view
, LINE_DEFAULT
, " ");
433 draw_commit_title(struct view
*view
, struct view_column
*column
,
434 const struct graph
*graph
, const struct graph_canvas
*graph_canvas
,
435 const struct ref
*refs
, const char *commit_title
)
437 if (graph
&& graph_canvas
&& column
->opt
.commit_title
.graph
&&
438 draw_graph(view
, graph
, graph_canvas
))
440 if (draw_refs(view
, column
, refs
))
442 return draw_text_overflow(view
, commit_title
, LINE_DEFAULT
,
443 column
->opt
.commit_title
.overflow
, 0);
447 view_column_draw(struct view
*view
, struct line
*line
, unsigned int lineno
)
449 struct view_column
*column
= view
->columns
;
450 struct view_column_data column_data
= {0};
452 if (!view
->ops
->get_column_data(view
, line
, &column_data
))
455 if (column_data
.section
)
456 column
= column_data
.section
;
458 for (; column
; column
= column
->next
) {
459 mode_t mode
= column_data
.mode
? *column_data
.mode
: 0;
464 switch (column
->type
) {
465 case VIEW_COLUMN_DATE
:
466 if (draw_date(view
, column
, column_data
.date
))
470 case VIEW_COLUMN_AUTHOR
:
471 if (draw_author(view
, column
, column_data
.author
))
475 case VIEW_COLUMN_REF
:
476 if (draw_ref(view
, column
, column_data
.ref
))
481 if (draw_id(view
, column
, column_data
.reflog
? column_data
.reflog
: column_data
.id
))
485 case VIEW_COLUMN_LINE_NUMBER
:
486 if (draw_lineno(view
, column
, column_data
.line_number
? *column_data
.line_number
: lineno
))
490 case VIEW_COLUMN_MODE
:
491 if (draw_mode(view
, column
, mode
))
495 case VIEW_COLUMN_FILE_SIZE
:
496 if (draw_file_size(view
, column
, column_data
.file_size
? *column_data
.file_size
: 0, mode
))
500 case VIEW_COLUMN_COMMIT_TITLE
:
501 if (draw_commit_title(view
, column
, column_data
.graph
, column_data
.graph_canvas
,
502 column_data
.refs
, column_data
.commit_title
))
506 case VIEW_COLUMN_FILE_NAME
:
507 if (draw_filename(view
, column
, column_data
.file_name
, mode
))
511 case VIEW_COLUMN_SECTION
:
512 if (draw_text(view
, column
->opt
.section
.type
, column
->opt
.section
.text
))
516 case VIEW_COLUMN_STATUS
:
517 if (draw_status(view
, column
, line
->type
, column_data
.status
))
521 case VIEW_COLUMN_TEXT
:
523 enum line_type type
= line
->type
;
524 const char *text
= column_data
.text
;
526 if (line
->wrapped
&& draw_text(view
, LINE_DELIMITER
, "+"))
529 if (line
->graph_indent
) {
530 size_t indent
= get_graph_indent(text
);
532 if (draw_text_expanded(view
, LINE_DEFAULT
, text
, -1, indent
, false))
537 if (line
->commit_title
) {
538 if (draw_text_overflow(view
, text
, LINE_DEFAULT
,
539 column
->opt
.text
.commit_title_overflow
, 4))
542 } else if (column_data
.box
) {
543 const struct box
*box
= column_data
.box
;
544 const char *text
= box
->text
;
547 for (i
= 0; i
< box
->cells
; i
++) {
548 const struct box_cell
*cell
= &box
->cell
[i
];
550 if (draw_textn(view
, cell
->type
, text
, cell
->length
))
553 text
+= cell
->length
;
556 } else if (draw_text(view
, type
, text
)) {
568 draw_view_line_search_result(struct view
*view
, unsigned int lineno
)
570 size_t bufsize
= view
->width
* 4;
571 char *buf
= malloc(bufsize
);
572 regmatch_t pmatch
[1];
575 if (!buf
|| mvwinnstr(view
->win
, lineno
, 0, buf
, bufsize
) == ERR
||
576 regexec(view
->regex
, buf
, ARRAY_SIZE(pmatch
), pmatch
, 0)) {
581 for (i
= 0; i
< ARRAY_SIZE(pmatch
); i
++) {
582 regoff_t start
= pmatch
[i
].rm_so
;
587 mvwchgat(view
->win
, lineno
,
588 utf8_width_of(buf
, start
, -1),
589 utf8_width_of(buf
+ start
, pmatch
[i
].rm_eo
- start
, -1),
590 get_view_attr(view
, LINE_SEARCH_RESULT
),
591 get_view_color(view
, LINE_SEARCH_RESULT
),
599 draw_view_line(struct view
*view
, unsigned int lineno
)
602 bool selected
= (view
->pos
.offset
+ lineno
== view
->pos
.lineno
);
605 /* FIXME: Disabled during code split.
606 assert(view_is_displayed(view));
609 if (view
->pos
.offset
+ lineno
>= view
->lines
)
612 line
= &view
->line
[view
->pos
.offset
+ lineno
];
614 wmove(view
->win
, lineno
, 0);
616 wclrtoeol(view
->win
);
618 view
->curline
= line
;
619 view
->curtype
= LINE_NONE
;
620 line
->selected
= false;
621 line
->dirty
= line
->cleareol
= 0;
624 set_view_attr(view
, LINE_CURSOR
);
625 line
->selected
= true;
626 view
->ops
->select(view
, line
);
629 ok
= view
->ops
->draw(view
, line
, lineno
);
631 if (ok
&& line
->search_result
&& view
->regex
)
632 draw_view_line_search_result(view
, lineno
);
638 redraw_view_dirty(struct view
*view
)
643 for (lineno
= 0; lineno
< view
->height
; lineno
++) {
644 if (view
->pos
.offset
+ lineno
>= view
->lines
)
646 if (!view
->line
[view
->pos
.offset
+ lineno
].dirty
)
649 if (!draw_view_line(view
, lineno
))
655 wnoutrefresh(view
->win
);
659 redraw_view_from(struct view
*view
, int lineno
)
661 assert(0 <= lineno
&& lineno
< view
->height
);
663 if (view
->columns
&& view_column_info_changed(view
, false)) {
666 view_column_reset(view
);
667 for (i
= 0; i
< view
->lines
; i
++) {
668 view_column_info_update(view
, &view
->line
[i
]);
672 for (; lineno
< view
->height
; lineno
++) {
673 if (!draw_view_line(view
, lineno
))
677 wnoutrefresh(view
->win
);
681 redraw_view(struct view
*view
)
684 redraw_view_from(view
, 0);
687 /* vim: set ts=8 sw=8 noexpandtab: */