1 /* Copyright (c) 2006-2010 Jonas Fonseca <fonseca@diku.dk>
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.
17 DEFINE_ALLOCATOR(realloc_graph_columns
, struct graph_column
, 32)
18 DEFINE_ALLOCATOR(realloc_graph_symbols
, struct graph_symbol
, 1)
20 static size_t get_free_graph_color(struct graph
*graph
)
24 for (free_color
= i
= 0; i
< ARRAY_SIZE(graph
->colors
); i
++) {
25 if (graph
->colors
[i
] < graph
->colors
[free_color
])
29 graph
->colors
[free_color
]++;
34 done_graph(struct graph
*graph
)
36 free(graph
->row
.columns
);
37 free(graph
->parents
.columns
);
38 memset(graph
, 0, sizeof(*graph
));
41 #define graph_column_has_commit(col) ((col)->id[0])
44 graph_find_column_by_id(struct graph_row
*row
, const char *id
)
46 size_t free_column
= row
->size
;
49 for (i
= 0; i
< row
->size
; i
++) {
50 if (!graph_column_has_commit(&row
->columns
[i
]))
52 else if (!strcmp(row
->columns
[i
].id
, id
))
59 static struct graph_column
*
60 graph_insert_column(struct graph
*graph
, struct graph_row
*row
, size_t pos
, const char *id
)
62 struct graph_column
*column
;
64 if (!realloc_graph_columns(&row
->columns
, row
->size
, 1))
67 column
= &row
->columns
[pos
];
68 if (pos
< row
->size
) {
69 memmove(column
+ 1, column
, sizeof(*column
) * (row
->size
- pos
));
73 memset(column
, 0, sizeof(*column
));
74 string_copy_rev(column
->id
, id
);
75 column
->symbol
.boundary
= !!graph
->is_boundary
;
81 graph_add_parent(struct graph
*graph
, const char *parent
)
83 return graph_insert_column(graph
, &graph
->parents
, graph
->parents
.size
, parent
);
87 graph_needs_expansion(struct graph
*graph
)
89 if (graph
->position
+ graph
->parents
.size
> graph
->row
.size
)
91 return graph
->parents
.size
> 1
92 && graph
->position
< 0
93 && graph
->expanded
< graph
->parents
.size
;
97 graph_expand(struct graph
*graph
)
99 while (graph_needs_expansion(graph
)) {
100 if (!graph_insert_column(graph
, &graph
->row
, graph
->position
+ graph
->expanded
, ""))
109 graph_needs_collapsing(struct graph
*graph
)
111 return graph
->row
.size
> 1
112 && !graph_column_has_commit(&graph
->row
.columns
[graph
->row
.size
- 1]);
116 graph_collapse(struct graph
*graph
)
118 while (graph_needs_collapsing(graph
)) {
126 graph_reorder_parents(struct graph
*graph
)
128 struct graph_row
*row
= &graph
->row
;
129 struct graph_row
*parents
= &graph
->parents
;
132 if (parents
->size
== 1)
135 for (i
= 0; i
< parents
->size
; i
++) {
136 struct graph_column
*column
= &parents
->columns
[i
];
137 size_t match
= graph_find_column_by_id(row
, column
->id
);
139 if (match
< graph
->position
&& graph_column_has_commit(&row
->columns
[match
])) {
140 //die("Reorder: %s -> %s", graph->commit->id, column->id);
141 // row->columns[match].symbol.initial = 1;
147 graph_canvas_append_symbol(struct graph
*graph
, struct graph_symbol
*symbol
)
149 struct graph_canvas
*canvas
= graph
->canvas
;
151 if (realloc_graph_symbols(&canvas
->symbols
, canvas
->size
, 1))
152 canvas
->symbols
[canvas
->size
++] = *symbol
;
156 graph_insert_parents(struct graph
*graph
)
158 struct graph_row
*row
= &graph
->row
;
159 struct graph_row
*parents
= &graph
->parents
;
160 size_t orig_size
= row
->size
;
161 bool branched
= FALSE
;
162 bool merge
= parents
->size
> 1;
165 assert(!graph_needs_expansion(graph
));
167 for (pos
= 0; pos
< graph
->position
; pos
++) {
168 struct graph_column
*column
= &row
->columns
[pos
];
169 struct graph_symbol symbol
= column
->symbol
;
171 if (graph_column_has_commit(column
)) {
172 size_t match
= graph_find_column_by_id(parents
, column
->id
);
174 if (match
< parents
->size
) {
175 column
->symbol
.initial
= 1;
180 symbol
.vbranch
= !!branched
;
181 if (!strcmp(column
->id
, graph
->id
)) {
186 graph_canvas_append_symbol(graph
, &symbol
);
189 for (; pos
< graph
->position
+ parents
->size
; pos
++) {
190 struct graph_column
*old
= &row
->columns
[pos
];
191 struct graph_column
*new = &parents
->columns
[pos
- graph
->position
];
192 struct graph_symbol symbol
= old
->symbol
;
194 symbol
.merge
= !!merge
;
196 if (pos
== graph
->position
) {
199 if (new->symbol->boundary) {
202 if (!graph_column_has_commit(new)) {
206 } else if (!strcmp(old
->id
, new->id
) && orig_size
== row
->size
) {
211 } else if (parents
->size
> 1) {
213 symbol
.vbranch
= !(pos
== graph
->position
+ parents
->size
- 1);
215 } else if (graph_column_has_commit(old
)) {
219 graph_canvas_append_symbol(graph
, &symbol
);
220 if (!graph_column_has_commit(old
))
221 new->symbol
.color
= get_free_graph_color(graph
);
225 for (; pos
< row
->size
; pos
++) {
226 bool too
= !strcmp(row
->columns
[row
->size
- 1].id
, graph
->id
);
227 struct graph_symbol symbol
= row
->columns
[pos
].symbol
;
229 symbol
.vbranch
= !!too
;
230 if (row
->columns
[pos
].id
[0]) {
232 if (!strcmp(row
->columns
[pos
].id
, graph
->id
)) {
234 if (too
&& pos
!= row
->size
- 1) {
239 row
->columns
[pos
].id
[0] = 0;
242 graph_canvas_append_symbol(graph
, &symbol
);
245 graph
->parents
.size
= graph
->expanded
= graph
->position
= 0;
251 graph_render_parents(struct graph
*graph
)
253 if (!graph_expand(graph
))
255 graph_reorder_parents(graph
);
256 graph_insert_parents(graph
);
257 if (!graph_collapse(graph
))
264 graph_add_commit(struct graph
*graph
, struct graph_canvas
*canvas
,
265 const char *id
, const char *parents
, bool is_boundary
)
267 graph
->position
= graph_find_column_by_id(&graph
->row
, id
);
269 graph
->canvas
= canvas
;
270 graph
->is_boundary
= is_boundary
;
272 while ((parents
= strchr(parents
, ' '))) {
274 if (!graph_add_parent(graph
, parents
))
276 graph
->has_parents
= TRUE
;
279 if (graph
->parents
.size
== 0 &&
280 !graph_add_parent(graph
, ""))
287 graph_symbol_to_utf8(struct graph_symbol
*symbol
)
289 if (symbol
->commit
) {
290 if (symbol
->boundary
)
292 else if (symbol
->initial
)
294 else if (symbol
->merge
)
300 if (symbol
->branch
) {
308 if (symbol
->branch
) {
309 if (symbol
->branched
) {
326 graph_symbol_to_chtype(struct graph_symbol
*symbol
)
328 static chtype graphics
[2];
330 if (symbol
->commit
) {
332 if (symbol
->boundary
)
334 else if (symbol
->initial
)
336 else if (symbol
->merge
)
339 graphics
[1] = 'o'; //ACS_DIAMOND; //'*';
344 graphics
[0] = ACS_HLINE
;
346 graphics
[1] = ACS_RTEE
;
348 graphics
[1] = ACS_URCORNER
;
352 if (symbol
->branch
) {
353 graphics
[0] = ACS_HLINE
;
354 if (symbol
->branched
) {
356 graphics
[1] = ACS_BTEE
;
358 graphics
[1] = ACS_LRCORNER
;
362 if (!symbol
->vbranch
)
364 graphics
[1] = ACS_VLINE
;
368 if (symbol
->vbranch
) {
369 graphics
[0] = graphics
[1] = ACS_HLINE
;
371 graphics
[0] = graphics
[1] = ' ';
377 graph_symbol_to_ascii(struct graph_symbol
*symbol
)
379 if (symbol
->commit
) {
380 if (symbol
->boundary
)
382 else if (symbol
->initial
)
384 else if (symbol
->merge
)
395 if (symbol
->branch
) {
396 if (symbol
->branched
) {