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.
16 #include "tig/graph.h"
18 DEFINE_ALLOCATOR(realloc_graph_columns
, struct graph_column
, 32)
19 DEFINE_ALLOCATOR(realloc_graph_symbols
, struct graph_symbol
, 1)
27 id_color_new(const char *id
, size_t color
)
29 struct id_color
*node
= malloc(sizeof(struct id_color
));
31 node
->id
= (char *) malloc(strlen(id
) + 1);
39 id_color_delete(struct id_color
*node
)
46 id_color_eq(const void *entry
, const void *element
)
48 return strcmp(((const struct id_color
*) entry
)->id
, ((const struct id_color
*) element
)->id
) == 0;
54 id_color_delete((struct id_color
*) key
);
58 id_color_hash(const void *node
)
60 return htab_hash_string(((const struct id_color
*) node
)->id
);
64 colors_add_id(struct colors
*colors
, const char *id
, const size_t color
)
66 struct id_color
*node
= id_color_new(id
, color
);
67 void **slot
= htab_find_slot(colors
->id_map
, node
, INSERT
);
69 if (slot
!= NULL
&& *slot
== NULL
) {
71 colors
->count
[color
]++;
73 id_color_delete(node
);
78 colors_remove_id(struct colors
*colors
, const char *id
)
80 struct id_color
*node
= id_color_new(id
, 0);
81 void **slot
= htab_find_slot(colors
->id_map
, node
, NO_INSERT
);
83 if (slot
!= NULL
&& *slot
!= NULL
) {
84 colors
->count
[((struct id_color
*) *slot
)->color
]--;
85 htab_clear_slot(colors
->id_map
, slot
);
88 id_color_delete(node
);
92 colors_get_color(struct colors
*colors
, const char *id
)
94 struct id_color
*key
= id_color_new(id
, 0);
95 struct id_color
*node
= (struct id_color
*) htab_find(colors
->id_map
, key
);
100 return (size_t) -1; // Max value of size_t. ID not found.
106 colors_get_free_color(struct colors
*colors
)
108 size_t free_color
= 0;
109 size_t lowest
= (size_t) -1; // Max value of size_t
112 for (i
= 0; i
< ARRAY_SIZE(colors
->count
); i
++) {
113 if (colors
->count
[i
] < lowest
) {
114 lowest
= colors
->count
[i
];
122 colors_init(struct colors
*colors
)
124 if (colors
->id_map
== NULL
) {
127 colors
->id_map
= htab_create_alloc(size
, id_color_hash
, id_color_eq
, key_del
, calloc
, free
);
132 get_color(struct graph
*graph
, char *new_id
)
136 colors_init(&graph
->colors
);
137 color
= colors_get_color(&graph
->colors
, new_id
);
139 if (color
< (size_t) -1) {
143 color
= colors_get_free_color(&graph
->colors
);
144 colors_add_id(&graph
->colors
, new_id
, color
);
150 done_graph(struct graph
*graph
)
152 free(graph
->prev_row
.columns
);
153 free(graph
->row
.columns
);
154 free(graph
->next_row
.columns
);
155 free(graph
->parents
.columns
);
156 memset(graph
, 0, sizeof(*graph
));
159 #define graph_column_has_commit(col) ((col)->id[0])
162 graph_find_column_by_id(struct graph_row
*row
, const char *id
)
164 size_t free_column
= row
->size
;
167 for (i
= 0; i
< row
->size
; i
++) {
168 if (!graph_column_has_commit(&row
->columns
[i
]) && free_column
== row
->size
)
170 else if (!strcmp(row
->columns
[i
].id
, id
))
178 graph_find_free_column(struct graph_row
*row
)
182 for (i
= 0; i
< row
->size
; i
++) {
183 if (!graph_column_has_commit(&row
->columns
[i
]))
190 static struct graph_column
*
191 graph_insert_column(struct graph
*graph
, struct graph_row
*row
, size_t pos
, const char *id
)
193 struct graph_column
*column
;
195 if (!realloc_graph_columns(&row
->columns
, row
->size
, 1))
198 column
= &row
->columns
[pos
];
199 if (pos
< row
->size
) {
200 memmove(column
+ 1, column
, sizeof(*column
) * (row
->size
- pos
));
204 memset(column
, 0, sizeof(*column
));
205 string_copy_rev(column
->id
, id
);
206 column
->symbol
.boundary
= !!graph
->is_boundary
;
211 struct graph_column
*
212 graph_add_parent(struct graph
*graph
, const char *parent
)
214 return graph_insert_column(graph
, &graph
->parents
, graph
->parents
.size
, parent
);
218 graph_needs_expansion(struct graph
*graph
)
220 return graph
->position
+ graph
->parents
.size
> graph
->row
.size
;
224 graph_expand(struct graph
*graph
)
226 while (graph_needs_expansion(graph
)) {
227 if (!graph_insert_column(graph
, &graph
->prev_row
, graph
->prev_row
.size
, ""))
230 if (!graph_insert_column(graph
, &graph
->row
, graph
->row
.size
, ""))
233 if (!graph_insert_column(graph
, &graph
->next_row
, graph
->next_row
.size
, ""))
241 graph_needs_collapsing(struct graph
*graph
)
243 return graph
->row
.size
> 1
244 && !graph_column_has_commit(&graph
->row
.columns
[graph
->row
.size
- 1]);
248 graph_collapse(struct graph
*graph
)
250 while (graph_needs_collapsing(graph
)) {
251 graph
->prev_row
.size
--;
253 graph
->next_row
.size
--;
260 graph_canvas_append_symbol(struct graph
*graph
, struct graph_symbol
*symbol
)
262 struct graph_canvas
*canvas
= graph
->canvas
;
264 if (realloc_graph_symbols(&canvas
->symbols
, canvas
->size
, 1))
265 canvas
->symbols
[canvas
->size
++] = *symbol
;
269 graph_row_clear_commit(struct graph_row
*row
, const char *id
)
273 for (i
= 0; i
< row
->size
; i
++) {
274 if (strcmp(row
->columns
[i
].id
, id
) == 0) {
275 row
->columns
[i
].id
[0] = 0;
281 graph_insert_parents(struct graph
*graph
)
283 struct graph_row
*prev_row
= &graph
->prev_row
;
284 struct graph_row
*row
= &graph
->row
;
285 struct graph_row
*next_row
= &graph
->next_row
;
286 struct graph_row
*parents
= &graph
->parents
;
289 for (i
= 0; i
< parents
->size
; i
++) {
290 struct graph_column
*new = &parents
->columns
[i
];
292 if (graph_column_has_commit(new)) {
293 size_t match
= graph_find_free_column(next_row
);
295 if (match
== next_row
->size
&& next_row
->columns
[next_row
->size
- 1].id
) {
296 graph_insert_column(graph
, next_row
, next_row
->size
, new->id
);
297 graph_insert_column(graph
, row
, row
->size
, "");
298 graph_insert_column(graph
, prev_row
, prev_row
->size
, "");
300 next_row
->columns
[match
] = *new;
307 commit_is_in_row(const char *id
, struct graph_row
*row
)
311 for (i
= 0; i
< row
->size
; i
++) {
312 if (!graph_column_has_commit(&row
->columns
[i
]))
315 if (strcmp(id
, row
->columns
[i
].id
) == 0)
322 graph_remove_collapsed_columns(struct graph
*graph
)
324 struct graph_row
*row
= &graph
->next_row
;
327 for (i
= row
->size
- 1; i
> 0; i
--) {
328 if (i
== graph
->position
)
331 if (i
== graph
->position
+ 1)
334 if (strcmp(row
->columns
[i
].id
, graph
->id
) == 0)
337 if (strcmp(row
->columns
[i
].id
, row
->columns
[i
- 1].id
) != 0)
340 if (commit_is_in_row(row
->columns
[i
].id
, &graph
->parents
) && !graph_column_has_commit(&graph
->prev_row
.columns
[i
]))
343 if (strcmp(row
->columns
[i
- 1].id
, graph
->prev_row
.columns
[i
- 1].id
) != 0 || graph
->prev_row
.columns
[i
- 1].symbol
.shift_left
)
344 row
->columns
[i
] = row
->columns
[i
+ 1];
349 graph_fill_empty_columns(struct graph
*graph
)
351 struct graph_row
*row
= &graph
->next_row
;
354 for (i
= row
->size
- 2; i
>= 0; i
--) {
355 if (!graph_column_has_commit(&row
->columns
[i
])) {
356 row
->columns
[i
] = row
->columns
[i
+ 1];
362 graph_generate_next_row(struct graph
*graph
)
364 graph_row_clear_commit(&graph
->next_row
, graph
->id
);
365 graph_insert_parents(graph
);
366 graph_remove_collapsed_columns(graph
);
367 graph_fill_empty_columns(graph
);
371 commits_in_row(struct graph_row
*row
)
376 for (i
= 0; i
< row
->size
;i
++) {
377 if (graph_column_has_commit(&row
->columns
[i
]))
385 graph_commit_next_row(struct graph
*graph
)
389 for (i
= 0; i
< graph
->row
.size
; i
++) {
390 graph
->prev_row
.columns
[i
] = graph
->row
.columns
[i
];
392 if (i
== graph
->position
&& commits_in_row(&graph
->parents
) > 0)
393 graph
->prev_row
.columns
[i
] = graph
->next_row
.columns
[i
];
395 if (!graph_column_has_commit(&graph
->prev_row
.columns
[i
]))
396 graph
->prev_row
.columns
[i
] = graph
->next_row
.columns
[i
];
398 graph
->row
.columns
[i
] = graph
->next_row
.columns
[i
];
401 graph
->prev_position
= graph
->position
;
405 continued_down(struct graph_row
*row
, struct graph_row
*next_row
, int pos
)
407 if (strcmp(row
->columns
[pos
].id
, next_row
->columns
[pos
].id
) != 0)
410 if (row
->columns
[pos
].symbol
.shift_left
)
417 shift_left(struct graph_row
*row
, struct graph_row
*prev_row
, int pos
)
421 if (!graph_column_has_commit(&row
->columns
[pos
]))
424 for (i
= pos
- 1; i
>= 0; i
--) {
425 if (!graph_column_has_commit(&row
->columns
[i
]))
428 if (strcmp(row
->columns
[i
].id
, row
->columns
[pos
].id
) != 0)
431 if (!continued_down(prev_row
, row
, i
))
441 new_column(struct graph_row
*row
, struct graph_row
*prev_row
, int pos
)
445 if (!graph_column_has_commit(&prev_row
->columns
[pos
]))
448 for (i
= pos
; i
< row
->size
; i
++) {
449 if (strcmp(row
->columns
[pos
].id
, prev_row
->columns
[i
].id
) == 0)
457 continued_right(struct graph_row
*row
, int pos
, int commit_pos
)
461 if (pos
< commit_pos
)
466 for (i
= pos
+ 1; i
< end
; i
++) {
467 if (strcmp(row
->columns
[pos
].id
, row
->columns
[i
].id
) == 0)
475 continued_left(struct graph_row
*row
, int pos
, int commit_pos
)
479 if (pos
< commit_pos
)
484 for (i
= start
; i
< pos
; i
++) {
485 if (!graph_column_has_commit(&row
->columns
[i
]))
488 if (strcmp(row
->columns
[pos
].id
, row
->columns
[i
].id
) == 0)
496 parent_down(struct graph_row
*parents
, struct graph_row
*next_row
, int pos
)
500 for (parent
= 0; parent
< parents
->size
; parent
++) {
501 if (!graph_column_has_commit(&parents
->columns
[parent
]))
504 if (strcmp(parents
->columns
[parent
].id
, next_row
->columns
[pos
].id
) == 0)
512 parent_right(struct graph_row
*parents
, struct graph_row
*row
, struct graph_row
*next_row
, int pos
)
516 for (parent
= 0; parent
< parents
->size
; parent
++) {
517 if (!graph_column_has_commit(&parents
->columns
[parent
]))
520 for (i
= pos
+ 1; i
< next_row
->size
; i
++) {
521 if (strcmp(parents
->columns
[parent
].id
, next_row
->columns
[i
].id
) != 0)
524 if (strcmp(parents
->columns
[parent
].id
, row
->columns
[i
].id
) != 0)
533 flanked(struct graph_row
*row
, int pos
, int commit_pos
, const char *commit_id
)
537 if (pos
< commit_pos
) {
545 for (i
= start
; i
< end
; i
++) {
546 if (strcmp(row
->columns
[i
].id
, commit_id
) == 0)
554 below_commit(int pos
, struct graph
*graph
)
556 if (!pos
== graph
->prev_position
)
559 if (!strcmp(graph
->row
.columns
[pos
].id
, graph
->prev_row
.columns
[pos
].id
) == 0)
566 graph_generate_symbols(struct graph
*graph
)
568 struct graph_row
*prev_row
= &graph
->prev_row
;
569 struct graph_row
*row
= &graph
->row
;
570 struct graph_row
*next_row
= &graph
->next_row
;
571 struct graph_row
*parents
= &graph
->parents
;
574 for (pos
= 0; pos
< row
->size
; pos
++) {
575 struct graph_column
*column
= &row
->columns
[pos
];
576 struct graph_symbol
*symbol
= &column
->symbol
;
577 char *id
= next_row
->columns
[pos
].id
;
579 symbol
->commit
= (pos
== graph
->position
);
580 symbol
->boundary
= (pos
== graph
->position
&& next_row
->columns
[pos
].symbol
.boundary
);
581 symbol
->initial
= (commits_in_row(parents
) < 1);
582 symbol
->merge
= (commits_in_row(parents
) > 1);
584 symbol
->continued_down
= continued_down(row
, next_row
, pos
);
585 symbol
->continued_up
= continued_down(prev_row
, row
, pos
);
586 symbol
->continued_right
= continued_right(row
, pos
, graph
->position
);
587 symbol
->continued_left
= continued_left(row
, pos
, graph
->position
);
588 symbol
->continued_up_left
= continued_left(prev_row
, pos
, prev_row
->size
);
590 symbol
->parent_down
= parent_down(parents
, next_row
, pos
);
591 symbol
->parent_right
= (pos
> graph
->position
&& parent_right(parents
, row
, next_row
, pos
));
593 symbol
->below_commit
= below_commit(pos
, graph
);
594 symbol
->flanked
= flanked(row
, pos
, graph
->position
, graph
->id
);
595 symbol
->next_right
= continued_right(next_row
, pos
, 0);
596 symbol
->matches_commit
= (strcmp(column
->id
, graph
->id
) == 0);
598 symbol
->shift_left
= shift_left(row
, prev_row
, pos
);
599 symbol
->continue_shift
= shift_left(row
, prev_row
, pos
+ 1);
600 symbol
->below_shift
= prev_row
->columns
[pos
].symbol
.shift_left
;
602 symbol
->new_column
= new_column(row
, prev_row
, pos
);
603 symbol
->empty
= (!graph_column_has_commit(&row
->columns
[pos
]));
605 if (graph_column_has_commit(column
)) {
608 symbol
->color
= get_color(graph
, id
);
610 graph_canvas_append_symbol(graph
, symbol
);
613 colors_remove_id(&graph
->colors
, graph
->id
);
617 graph_render_parents(struct graph
*graph
)
619 if (graph
->parents
.size
== 0 &&
620 !graph_add_parent(graph
, ""))
623 if (!graph_expand(graph
))
626 graph_generate_next_row(graph
);
627 graph_generate_symbols(graph
);
628 graph_commit_next_row(graph
);
630 graph
->parents
.size
= graph
->position
= 0;
632 if (!graph_collapse(graph
))
639 graph_add_commit(struct graph
*graph
, struct graph_canvas
*canvas
,
640 const char *id
, const char *parents
, bool is_boundary
)
642 graph
->position
= graph_find_column_by_id(&graph
->row
, id
);
643 string_copy_rev(graph
->id
, id
);
644 graph
->canvas
= canvas
;
645 graph
->is_boundary
= is_boundary
;
647 while ((parents
= strchr(parents
, ' '))) {
649 if (!graph_add_parent(graph
, parents
))
651 graph
->has_parents
= TRUE
;
658 graph_symbol_forks(struct graph_symbol
*symbol
)
660 if (!symbol
->continued_down
)
663 if (!symbol
->continued_right
)
666 if (!symbol
->continued_up
)
673 graph_symbol_cross_over(struct graph_symbol
*symbol
)
678 if (!symbol
->continued_down
)
681 if (!symbol
->continued_up
&& !symbol
->new_column
&& !symbol
->below_commit
)
684 if (symbol
->shift_left
)
687 if (symbol
->parent_right
&& symbol
->merge
)
697 graph_symbol_turn_left(struct graph_symbol
*symbol
)
699 if (symbol
->matches_commit
&& symbol
->continued_right
&& !symbol
->continued_down
)
702 if (symbol
->continue_shift
)
705 if (symbol
->continued_up
|| symbol
->new_column
|| symbol
->below_commit
) {
706 if (symbol
->matches_commit
)
709 if (symbol
->shift_left
)
717 graph_symbol_turn_down_cross_over(struct graph_symbol
*symbol
)
719 if (!symbol
->continued_down
)
722 if (!symbol
->continued_right
)
735 graph_symbol_turn_down(struct graph_symbol
*symbol
)
737 if (!symbol
->continued_down
)
740 if (!symbol
->continued_right
)
747 graph_symbol_merge(struct graph_symbol
*symbol
)
749 if (symbol
->continued_down
)
752 if (!symbol
->parent_down
)
755 if (symbol
->parent_right
)
762 graph_symbol_multi_merge(struct graph_symbol
*symbol
)
764 if (!symbol
->parent_down
)
767 if (!symbol
->parent_right
)
774 graph_symbol_vertical_bar(struct graph_symbol
*symbol
)
779 if (symbol
->shift_left
)
782 if (!symbol
->continued_down
)
785 if (symbol
->continued_up
)
788 if (symbol
->parent_right
)
794 if (symbol
->continued_right
)
801 graph_symbol_horizontal_bar(struct graph_symbol
*symbol
)
803 if (symbol
->shift_left
)
806 if (symbol
->continued_down
)
809 if (!symbol
->parent_right
&& !symbol
->continued_right
)
812 if ((symbol
->continued_up
&& !symbol
->continued_up_left
))
815 if (!symbol
->below_commit
)
822 graph_symbol_multi_branch(struct graph_symbol
*symbol
)
824 if (symbol
->continued_down
)
827 if (!symbol
->continued_right
)
830 if (symbol
->below_shift
)
833 if (symbol
->continued_up
|| symbol
->new_column
|| symbol
->below_commit
) {
834 if (symbol
->matches_commit
)
837 if (symbol
->shift_left
)
845 graph_symbol_to_utf8(struct graph_symbol
*symbol
)
847 if (symbol
->commit
) {
848 if (symbol
->boundary
)
850 else if (symbol
->initial
)
852 else if (symbol
->merge
)
857 if (graph_symbol_cross_over(symbol
))
860 if (graph_symbol_vertical_bar(symbol
))
863 if (graph_symbol_turn_left(symbol
))
866 if (graph_symbol_multi_branch(symbol
))
869 if (graph_symbol_horizontal_bar(symbol
))
872 if (graph_symbol_forks(symbol
))
875 if (graph_symbol_turn_down_cross_over(symbol
))
878 if (graph_symbol_turn_down(symbol
))
881 if (graph_symbol_merge(symbol
))
884 if (graph_symbol_multi_merge(symbol
))
891 graph_symbol_to_chtype(struct graph_symbol
*symbol
)
893 static chtype graphics
[2];
895 if (symbol
->commit
) {
897 if (symbol
->boundary
)
899 else if (symbol
->initial
)
901 else if (symbol
->merge
)
904 graphics
[1] = 'o'; //ACS_DIAMOND; //'*';
907 } else if (graph_symbol_cross_over(symbol
)) {
908 graphics
[0] = ACS_HLINE
;
909 graphics
[1] = ACS_VLINE
;
911 } else if (graph_symbol_vertical_bar(symbol
)) {
913 graphics
[1] = ACS_VLINE
;
915 } else if (graph_symbol_turn_left(symbol
)) {
916 graphics
[0] = ACS_HLINE
;
917 graphics
[1] = ACS_LRCORNER
;
919 } else if (graph_symbol_multi_branch(symbol
)) {
920 graphics
[0] = ACS_HLINE
;
921 graphics
[1] = ACS_BTEE
;
923 } else if (graph_symbol_horizontal_bar(symbol
)) {
924 graphics
[0] = graphics
[1] = ACS_HLINE
;
926 } else if (graph_symbol_forks(symbol
)) {
928 graphics
[1] = ACS_LTEE
;
930 } else if (graph_symbol_turn_down_cross_over(symbol
)) {
931 graphics
[0] = ACS_HLINE
;
932 graphics
[1] = ACS_ULCORNER
;
934 } else if (graph_symbol_turn_down(symbol
)) {
936 graphics
[1] = ACS_ULCORNER
;
938 } else if (graph_symbol_merge(symbol
)) {
939 graphics
[0] = ACS_HLINE
;
940 graphics
[1] = ACS_URCORNER
;
942 } else if (graph_symbol_multi_merge(symbol
)) {
943 graphics
[0] = ACS_HLINE
;
944 graphics
[1] = ACS_TTEE
;
947 graphics
[0] = graphics
[1] = ' ';
954 graph_symbol_to_ascii(struct graph_symbol
*symbol
)
956 if (symbol
->commit
) {
957 if (symbol
->boundary
)
959 else if (symbol
->initial
)
961 else if (symbol
->merge
)
966 if (graph_symbol_cross_over(symbol
))
969 if (graph_symbol_vertical_bar(symbol
))
972 if (graph_symbol_turn_left(symbol
))
975 if (graph_symbol_multi_branch(symbol
))
978 if (graph_symbol_horizontal_bar(symbol
))
981 if (graph_symbol_forks(symbol
))
984 if (graph_symbol_turn_down_cross_over(symbol
))
987 if (graph_symbol_turn_down(symbol
))
990 if (graph_symbol_merge(symbol
))
993 if (graph_symbol_multi_merge(symbol
))
999 /* vim: set ts=8 sw=8 noexpandtab: */