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
);
152 return calloc(1, sizeof(struct graph
));
156 done_graph(struct graph
*graph
)
158 free(graph
->prev_row
.columns
);
159 free(graph
->row
.columns
);
160 free(graph
->next_row
.columns
);
161 free(graph
->parents
.columns
);
165 #define graph_column_has_commit(col) ((col)->id[0])
168 graph_find_column_by_id(struct graph_row
*row
, const char *id
)
170 size_t free_column
= row
->size
;
173 for (i
= 0; i
< row
->size
; i
++) {
174 if (!graph_column_has_commit(&row
->columns
[i
]) && free_column
== row
->size
)
176 else if (!strcmp(row
->columns
[i
].id
, id
))
184 graph_find_free_column(struct graph_row
*row
)
188 for (i
= 0; i
< row
->size
; i
++) {
189 if (!graph_column_has_commit(&row
->columns
[i
]))
196 static struct graph_column
*
197 graph_insert_column(struct graph
*graph
, struct graph_row
*row
, size_t pos
, const char *id
)
199 struct graph_column
*column
;
201 if (!realloc_graph_columns(&row
->columns
, row
->size
, 1))
204 column
= &row
->columns
[pos
];
205 if (pos
< row
->size
) {
206 memmove(column
+ 1, column
, sizeof(*column
) * (row
->size
- pos
));
210 memset(column
, 0, sizeof(*column
));
211 string_copy_rev(column
->id
, id
);
212 column
->symbol
.boundary
= !!graph
->is_boundary
;
217 struct graph_column
*
218 graph_add_parent(struct graph
*graph
, const char *parent
)
220 return graph_insert_column(graph
, &graph
->parents
, graph
->parents
.size
, parent
);
224 graph_needs_expansion(struct graph
*graph
)
226 return graph
->position
+ graph
->parents
.size
> graph
->row
.size
;
230 graph_expand(struct graph
*graph
)
232 while (graph_needs_expansion(graph
)) {
233 if (!graph_insert_column(graph
, &graph
->prev_row
, graph
->prev_row
.size
, ""))
236 if (!graph_insert_column(graph
, &graph
->row
, graph
->row
.size
, ""))
239 if (!graph_insert_column(graph
, &graph
->next_row
, graph
->next_row
.size
, ""))
247 graph_needs_collapsing(struct graph
*graph
)
249 return graph
->row
.size
> 1
250 && !graph_column_has_commit(&graph
->row
.columns
[graph
->row
.size
- 1]);
254 graph_collapse(struct graph
*graph
)
256 while (graph_needs_collapsing(graph
)) {
257 graph
->prev_row
.size
--;
259 graph
->next_row
.size
--;
266 graph_canvas_append_symbol(struct graph
*graph
, struct graph_canvas
*canvas
, struct graph_symbol
*symbol
)
268 if (realloc_graph_symbols(&canvas
->symbols
, canvas
->size
, 1))
269 canvas
->symbols
[canvas
->size
++] = *symbol
;
273 graph_row_clear_commit(struct graph_row
*row
, const char *id
)
277 for (i
= 0; i
< row
->size
; i
++) {
278 if (strcmp(row
->columns
[i
].id
, id
) == 0) {
279 row
->columns
[i
].id
[0] = 0;
285 graph_insert_parents(struct graph
*graph
)
287 struct graph_row
*prev_row
= &graph
->prev_row
;
288 struct graph_row
*row
= &graph
->row
;
289 struct graph_row
*next_row
= &graph
->next_row
;
290 struct graph_row
*parents
= &graph
->parents
;
293 for (i
= 0; i
< parents
->size
; i
++) {
294 struct graph_column
*new = &parents
->columns
[i
];
296 if (graph_column_has_commit(new)) {
297 size_t match
= graph_find_free_column(next_row
);
299 if (match
== next_row
->size
&& next_row
->columns
[next_row
->size
- 1].id
) {
300 graph_insert_column(graph
, next_row
, next_row
->size
, new->id
);
301 graph_insert_column(graph
, row
, row
->size
, "");
302 graph_insert_column(graph
, prev_row
, prev_row
->size
, "");
304 next_row
->columns
[match
] = *new;
311 commit_is_in_row(const char *id
, struct graph_row
*row
)
315 for (i
= 0; i
< row
->size
; i
++) {
316 if (!graph_column_has_commit(&row
->columns
[i
]))
319 if (strcmp(id
, row
->columns
[i
].id
) == 0)
326 graph_remove_collapsed_columns(struct graph
*graph
)
328 struct graph_row
*row
= &graph
->next_row
;
331 for (i
= row
->size
- 1; i
> 0; i
--) {
332 if (i
== graph
->position
)
335 if (i
== graph
->position
+ 1)
338 if (strcmp(row
->columns
[i
].id
, graph
->id
) == 0)
341 if (strcmp(row
->columns
[i
].id
, row
->columns
[i
- 1].id
) != 0)
344 if (commit_is_in_row(row
->columns
[i
].id
, &graph
->parents
) && !graph_column_has_commit(&graph
->prev_row
.columns
[i
]))
347 if (strcmp(row
->columns
[i
- 1].id
, graph
->prev_row
.columns
[i
- 1].id
) != 0 || graph
->prev_row
.columns
[i
- 1].symbol
.shift_left
)
348 row
->columns
[i
] = row
->columns
[i
+ 1];
353 graph_fill_empty_columns(struct graph
*graph
)
355 struct graph_row
*row
= &graph
->next_row
;
358 for (i
= row
->size
- 2; i
>= 0; i
--) {
359 if (!graph_column_has_commit(&row
->columns
[i
])) {
360 row
->columns
[i
] = row
->columns
[i
+ 1];
366 graph_generate_next_row(struct graph
*graph
)
368 graph_row_clear_commit(&graph
->next_row
, graph
->id
);
369 graph_insert_parents(graph
);
370 graph_remove_collapsed_columns(graph
);
371 graph_fill_empty_columns(graph
);
375 commits_in_row(struct graph_row
*row
)
380 for (i
= 0; i
< row
->size
;i
++) {
381 if (graph_column_has_commit(&row
->columns
[i
]))
389 graph_commit_next_row(struct graph
*graph
)
393 for (i
= 0; i
< graph
->row
.size
; i
++) {
394 graph
->prev_row
.columns
[i
] = graph
->row
.columns
[i
];
396 if (i
== graph
->position
&& commits_in_row(&graph
->parents
) > 0)
397 graph
->prev_row
.columns
[i
] = graph
->next_row
.columns
[i
];
399 if (!graph_column_has_commit(&graph
->prev_row
.columns
[i
]))
400 graph
->prev_row
.columns
[i
] = graph
->next_row
.columns
[i
];
402 graph
->row
.columns
[i
] = graph
->next_row
.columns
[i
];
405 graph
->prev_position
= graph
->position
;
409 continued_down(struct graph_row
*row
, struct graph_row
*next_row
, int pos
)
411 if (strcmp(row
->columns
[pos
].id
, next_row
->columns
[pos
].id
) != 0)
414 if (row
->columns
[pos
].symbol
.shift_left
)
421 shift_left(struct graph_row
*row
, struct graph_row
*prev_row
, int pos
)
425 if (!graph_column_has_commit(&row
->columns
[pos
]))
428 for (i
= pos
- 1; i
>= 0; i
--) {
429 if (!graph_column_has_commit(&row
->columns
[i
]))
432 if (strcmp(row
->columns
[i
].id
, row
->columns
[pos
].id
) != 0)
435 if (!continued_down(prev_row
, row
, i
))
445 new_column(struct graph_row
*row
, struct graph_row
*prev_row
, int pos
)
449 if (!graph_column_has_commit(&prev_row
->columns
[pos
]))
452 for (i
= pos
; i
< row
->size
; i
++) {
453 if (strcmp(row
->columns
[pos
].id
, prev_row
->columns
[i
].id
) == 0)
461 continued_right(struct graph_row
*row
, int pos
, int commit_pos
)
465 if (pos
< commit_pos
)
470 for (i
= pos
+ 1; i
< end
; i
++) {
471 if (strcmp(row
->columns
[pos
].id
, row
->columns
[i
].id
) == 0)
479 continued_left(struct graph_row
*row
, int pos
, int commit_pos
)
483 if (pos
< commit_pos
)
488 for (i
= start
; i
< pos
; i
++) {
489 if (!graph_column_has_commit(&row
->columns
[i
]))
492 if (strcmp(row
->columns
[pos
].id
, row
->columns
[i
].id
) == 0)
500 parent_down(struct graph_row
*parents
, struct graph_row
*next_row
, int pos
)
504 for (parent
= 0; parent
< parents
->size
; parent
++) {
505 if (!graph_column_has_commit(&parents
->columns
[parent
]))
508 if (strcmp(parents
->columns
[parent
].id
, next_row
->columns
[pos
].id
) == 0)
516 parent_right(struct graph_row
*parents
, struct graph_row
*row
, struct graph_row
*next_row
, int pos
)
520 for (parent
= 0; parent
< parents
->size
; parent
++) {
521 if (!graph_column_has_commit(&parents
->columns
[parent
]))
524 for (i
= pos
+ 1; i
< next_row
->size
; i
++) {
525 if (strcmp(parents
->columns
[parent
].id
, next_row
->columns
[i
].id
) != 0)
528 if (strcmp(parents
->columns
[parent
].id
, row
->columns
[i
].id
) != 0)
537 flanked(struct graph_row
*row
, int pos
, int commit_pos
, const char *commit_id
)
541 if (pos
< commit_pos
) {
549 for (i
= start
; i
< end
; i
++) {
550 if (strcmp(row
->columns
[i
].id
, commit_id
) == 0)
558 below_commit(int pos
, struct graph
*graph
)
560 if (!pos
== graph
->prev_position
)
563 if (!strcmp(graph
->row
.columns
[pos
].id
, graph
->prev_row
.columns
[pos
].id
) == 0)
570 graph_generate_symbols(struct graph
*graph
, struct graph_canvas
*canvas
)
572 struct graph_row
*prev_row
= &graph
->prev_row
;
573 struct graph_row
*row
= &graph
->row
;
574 struct graph_row
*next_row
= &graph
->next_row
;
575 struct graph_row
*parents
= &graph
->parents
;
578 for (pos
= 0; pos
< row
->size
; pos
++) {
579 struct graph_column
*column
= &row
->columns
[pos
];
580 struct graph_symbol
*symbol
= &column
->symbol
;
581 char *id
= next_row
->columns
[pos
].id
;
583 symbol
->commit
= (pos
== graph
->position
);
584 symbol
->boundary
= (pos
== graph
->position
&& next_row
->columns
[pos
].symbol
.boundary
);
585 symbol
->initial
= (commits_in_row(parents
) < 1);
586 symbol
->merge
= (commits_in_row(parents
) > 1);
588 symbol
->continued_down
= continued_down(row
, next_row
, pos
);
589 symbol
->continued_up
= continued_down(prev_row
, row
, pos
);
590 symbol
->continued_right
= continued_right(row
, pos
, graph
->position
);
591 symbol
->continued_left
= continued_left(row
, pos
, graph
->position
);
592 symbol
->continued_up_left
= continued_left(prev_row
, pos
, prev_row
->size
);
594 symbol
->parent_down
= parent_down(parents
, next_row
, pos
);
595 symbol
->parent_right
= (pos
> graph
->position
&& parent_right(parents
, row
, next_row
, pos
));
597 symbol
->below_commit
= below_commit(pos
, graph
);
598 symbol
->flanked
= flanked(row
, pos
, graph
->position
, graph
->id
);
599 symbol
->next_right
= continued_right(next_row
, pos
, 0);
600 symbol
->matches_commit
= (strcmp(column
->id
, graph
->id
) == 0);
602 symbol
->shift_left
= shift_left(row
, prev_row
, pos
);
603 symbol
->continue_shift
= shift_left(row
, prev_row
, pos
+ 1);
604 symbol
->below_shift
= prev_row
->columns
[pos
].symbol
.shift_left
;
606 symbol
->new_column
= new_column(row
, prev_row
, pos
);
607 symbol
->empty
= (!graph_column_has_commit(&row
->columns
[pos
]));
609 if (graph_column_has_commit(column
)) {
612 symbol
->color
= get_color(graph
, id
);
614 graph_canvas_append_symbol(graph
, canvas
, symbol
);
617 colors_remove_id(&graph
->colors
, graph
->id
);
621 graph_render_parents(struct graph
*graph
, struct graph_canvas
*canvas
)
623 if (graph
->parents
.size
== 0 &&
624 !graph_add_parent(graph
, ""))
627 if (!graph_expand(graph
))
630 graph_generate_next_row(graph
);
631 graph_generate_symbols(graph
, canvas
);
632 graph_commit_next_row(graph
);
634 graph
->parents
.size
= graph
->position
= 0;
636 if (!graph_collapse(graph
))
643 graph_add_commit(struct graph
*graph
, struct graph_canvas
*canvas
,
644 const char *id
, const char *parents
, bool is_boundary
)
646 graph
->position
= graph_find_column_by_id(&graph
->row
, id
);
647 string_copy_rev(graph
->id
, id
);
648 graph
->is_boundary
= is_boundary
;
650 while ((parents
= strchr(parents
, ' '))) {
652 if (!graph_add_parent(graph
, parents
))
654 graph
->has_parents
= TRUE
;
661 graph_symbol_forks(struct graph_symbol
*symbol
)
663 if (!symbol
->continued_down
)
666 if (!symbol
->continued_right
)
669 if (!symbol
->continued_up
)
676 graph_symbol_cross_over(struct graph_symbol
*symbol
)
681 if (!symbol
->continued_down
)
684 if (!symbol
->continued_up
&& !symbol
->new_column
&& !symbol
->below_commit
)
687 if (symbol
->shift_left
)
690 if (symbol
->parent_right
&& symbol
->merge
)
700 graph_symbol_turn_left(struct graph_symbol
*symbol
)
702 if (symbol
->matches_commit
&& symbol
->continued_right
&& !symbol
->continued_down
)
705 if (symbol
->continue_shift
)
708 if (symbol
->continued_up
|| symbol
->new_column
|| symbol
->below_commit
) {
709 if (symbol
->matches_commit
)
712 if (symbol
->shift_left
)
720 graph_symbol_turn_down_cross_over(struct graph_symbol
*symbol
)
722 if (!symbol
->continued_down
)
725 if (!symbol
->continued_right
)
738 graph_symbol_turn_down(struct graph_symbol
*symbol
)
740 if (!symbol
->continued_down
)
743 if (!symbol
->continued_right
)
750 graph_symbol_merge(struct graph_symbol
*symbol
)
752 if (symbol
->continued_down
)
755 if (!symbol
->parent_down
)
758 if (symbol
->parent_right
)
765 graph_symbol_multi_merge(struct graph_symbol
*symbol
)
767 if (!symbol
->parent_down
)
770 if (!symbol
->parent_right
)
777 graph_symbol_vertical_bar(struct graph_symbol
*symbol
)
782 if (symbol
->shift_left
)
785 if (!symbol
->continued_down
)
788 if (symbol
->continued_up
)
791 if (symbol
->parent_right
)
797 if (symbol
->continued_right
)
804 graph_symbol_horizontal_bar(struct graph_symbol
*symbol
)
806 if (symbol
->shift_left
)
809 if (symbol
->continued_down
)
812 if (!symbol
->parent_right
&& !symbol
->continued_right
)
815 if ((symbol
->continued_up
&& !symbol
->continued_up_left
))
818 if (!symbol
->below_commit
)
825 graph_symbol_multi_branch(struct graph_symbol
*symbol
)
827 if (symbol
->continued_down
)
830 if (!symbol
->continued_right
)
833 if (symbol
->below_shift
)
836 if (symbol
->continued_up
|| symbol
->new_column
|| symbol
->below_commit
) {
837 if (symbol
->matches_commit
)
840 if (symbol
->shift_left
)
848 graph_symbol_to_utf8(struct graph_symbol
*symbol
)
850 if (symbol
->commit
) {
851 if (symbol
->boundary
)
853 else if (symbol
->initial
)
855 else if (symbol
->merge
)
860 if (graph_symbol_cross_over(symbol
))
863 if (graph_symbol_vertical_bar(symbol
))
866 if (graph_symbol_turn_left(symbol
))
869 if (graph_symbol_multi_branch(symbol
))
872 if (graph_symbol_horizontal_bar(symbol
))
875 if (graph_symbol_forks(symbol
))
878 if (graph_symbol_turn_down_cross_over(symbol
))
881 if (graph_symbol_turn_down(symbol
))
884 if (graph_symbol_merge(symbol
))
887 if (graph_symbol_multi_merge(symbol
))
894 graph_symbol_to_chtype(struct graph_symbol
*symbol
)
896 static chtype graphics
[2];
898 if (symbol
->commit
) {
900 if (symbol
->boundary
)
902 else if (symbol
->initial
)
904 else if (symbol
->merge
)
907 graphics
[1] = 'o'; //ACS_DIAMOND; //'*';
910 } else if (graph_symbol_cross_over(symbol
)) {
911 graphics
[0] = ACS_HLINE
;
912 graphics
[1] = ACS_VLINE
;
914 } else if (graph_symbol_vertical_bar(symbol
)) {
916 graphics
[1] = ACS_VLINE
;
918 } else if (graph_symbol_turn_left(symbol
)) {
919 graphics
[0] = ACS_HLINE
;
920 graphics
[1] = ACS_LRCORNER
;
922 } else if (graph_symbol_multi_branch(symbol
)) {
923 graphics
[0] = ACS_HLINE
;
924 graphics
[1] = ACS_BTEE
;
926 } else if (graph_symbol_horizontal_bar(symbol
)) {
927 graphics
[0] = graphics
[1] = ACS_HLINE
;
929 } else if (graph_symbol_forks(symbol
)) {
931 graphics
[1] = ACS_LTEE
;
933 } else if (graph_symbol_turn_down_cross_over(symbol
)) {
934 graphics
[0] = ACS_HLINE
;
935 graphics
[1] = ACS_ULCORNER
;
937 } else if (graph_symbol_turn_down(symbol
)) {
939 graphics
[1] = ACS_ULCORNER
;
941 } else if (graph_symbol_merge(symbol
)) {
942 graphics
[0] = ACS_HLINE
;
943 graphics
[1] = ACS_URCORNER
;
945 } else if (graph_symbol_multi_merge(symbol
)) {
946 graphics
[0] = ACS_HLINE
;
947 graphics
[1] = ACS_TTEE
;
950 graphics
[0] = graphics
[1] = ' ';
957 graph_symbol_to_ascii(struct graph_symbol
*symbol
)
959 if (symbol
->commit
) {
960 if (symbol
->boundary
)
962 else if (symbol
->initial
)
964 else if (symbol
->merge
)
969 if (graph_symbol_cross_over(symbol
))
972 if (graph_symbol_vertical_bar(symbol
))
975 if (graph_symbol_turn_left(symbol
))
978 if (graph_symbol_multi_branch(symbol
))
981 if (graph_symbol_horizontal_bar(symbol
))
984 if (graph_symbol_forks(symbol
))
987 if (graph_symbol_turn_down_cross_over(symbol
))
990 if (graph_symbol_turn_down(symbol
))
993 if (graph_symbol_merge(symbol
))
996 if (graph_symbol_multi_merge(symbol
))
1002 /* vim: set ts=8 sw=8 noexpandtab: */