mirror of
https://gitlab.gnome.org/GNOME/gitg
synced 2024-08-25 02:47:01 +00:00
Factorized lane calculation and added lane colors
Lane calculation is now a separate object Lane coloring is added
This commit is contained in:
parent
a5728d475c
commit
6779aa35d0
|
@ -23,6 +23,9 @@ gitg_SOURCES = \
|
|||
gitg-runner.c \
|
||||
gitg-revision-view.c \
|
||||
gitg-utils.c \
|
||||
gitg-lane.c \
|
||||
gitg-lanes.c \
|
||||
gitg-color.c \
|
||||
sexy-icon-entry.c
|
||||
|
||||
gitg_LDADD = $(PACKAGE_LIBS)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include <math.h>
|
||||
#include "gitg-cell-renderer-path.h"
|
||||
#include "gitg-lane.h"
|
||||
#include "gitg-utils.h"
|
||||
|
||||
#define GITG_CELL_RENDERER_PATH_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GITG_TYPE_CELL_RENDERER_PATH, GitgCellRendererPathPrivate))
|
||||
|
||||
|
@ -21,8 +23,8 @@ enum
|
|||
struct _GitgCellRendererPathPrivate
|
||||
{
|
||||
gint8 lane;
|
||||
gint8 *lanes;
|
||||
gint8 *next_lanes;
|
||||
GitgLane **lanes;
|
||||
GitgLane **next_lanes;
|
||||
guint lane_width;
|
||||
guint dot_width;
|
||||
};
|
||||
|
@ -34,16 +36,7 @@ G_DEFINE_TYPE(GitgCellRendererPath, gitg_cell_renderer_path, GTK_TYPE_CELL_RENDE
|
|||
static gint
|
||||
num_lanes(GitgCellRendererPath *self)
|
||||
{
|
||||
gint ret = 1;
|
||||
gint8 *ptr = self->priv->lanes;
|
||||
|
||||
while (ptr && *ptr != -2)
|
||||
{
|
||||
if (*ptr++ == -1)
|
||||
++ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return gitg_utils_null_length((gconstpointer *)self->priv->lanes);
|
||||
}
|
||||
|
||||
inline static gint
|
||||
|
@ -77,7 +70,7 @@ renderer_get_size(GtkCellRenderer *renderer, GtkWidget *widget, GdkRectangle *ar
|
|||
}
|
||||
|
||||
static void
|
||||
draw_paths_real(GitgCellRendererPath *self, cairo_t *cr, GdkRectangle *area, gint8 *lanes, gboolean top, gdouble yoffset)
|
||||
draw_paths_real(GitgCellRendererPath *self, cairo_t *cr, GdkRectangle *area, GitgLane **lanes, gboolean top, gdouble yoffset)
|
||||
{
|
||||
if (!lanes)
|
||||
return;
|
||||
|
@ -85,24 +78,27 @@ draw_paths_real(GitgCellRendererPath *self, cairo_t *cr, GdkRectangle *area, gin
|
|||
gint8 to = 0;
|
||||
gdouble cw = self->priv->lane_width;
|
||||
gdouble ch = area->height / 2.0;
|
||||
GitgLane *lane;
|
||||
|
||||
for (; *lanes != -2; ++lanes)
|
||||
while ((lane = *lanes++))
|
||||
{
|
||||
if (*lanes == -1)
|
||||
GSList *item;
|
||||
|
||||
for (item = lane->from; item; item = item->next)
|
||||
{
|
||||
++to;
|
||||
continue;
|
||||
gint8 from = (gint8)GPOINTER_TO_INT(item->data);
|
||||
gdouble xf = 0.0;
|
||||
|
||||
if (from != to)
|
||||
xf = 0.5 * (to - from);
|
||||
|
||||
gitg_color_set_cairo_source(lane->color, cr);
|
||||
cairo_move_to(cr, area->x + (from + (top ? xf : 0)) * cw + cw / 2.0, area->y + yoffset * ch);
|
||||
cairo_line_to(cr, area->x + (to - (top ? 0 : xf)) * cw + cw / 2.0, area->y + (yoffset + 1) * ch);
|
||||
cairo_stroke(cr);
|
||||
}
|
||||
|
||||
gint8 from = *lanes;
|
||||
gdouble xf = 0.0;
|
||||
|
||||
if (from != to)
|
||||
xf = 0.5 * (to - from);
|
||||
|
||||
cairo_move_to(cr, area->x + (from + (top ? xf : 0)) * cw + cw / 2.0, area->y + yoffset * ch);
|
||||
cairo_line_to(cr, area->x + (to - (top ? 0 : xf)) * cw + cw / 2.0, area->y + (yoffset + 1) * ch);
|
||||
cairo_stroke(cr);
|
||||
++to;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,8 +117,8 @@ draw_bottom_paths(GitgCellRendererPath *self, cairo_t *cr, GdkRectangle *area)
|
|||
static void
|
||||
draw_paths(GitgCellRendererPath *self, cairo_t *cr, GdkRectangle *area)
|
||||
{
|
||||
cairo_set_line_width(cr, 1.5);
|
||||
cairo_set_source_rgb(cr, 0.45, 0.6, 0.74);
|
||||
cairo_set_line_width(cr, 2);
|
||||
//cairo_set_source_rgb(cr, 0.45, 0.6, 0.74);
|
||||
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
|
||||
draw_top_paths(self, cr, area);
|
||||
draw_bottom_paths(self, cr, area);
|
||||
|
@ -142,10 +138,11 @@ renderer_render(GtkCellRenderer *renderer, GdkDrawable *window, GtkWidget *widge
|
|||
|
||||
cairo_set_line_width(cr, 2.0);
|
||||
cairo_arc(cr, area->x + offset + radius, area->y + area->height / 2.0, radius, 0, 2 * M_PI);
|
||||
cairo_set_source_rgb(cr, 0.12, 0.24, 0.36);
|
||||
cairo_set_source_rgb(cr, 0, 0, 0);
|
||||
|
||||
cairo_stroke_preserve(cr);
|
||||
cairo_set_source_rgb(cr, 0.3, 0.6, 0.7);
|
||||
gitg_color_set_cairo_source(self->priv->lanes[self->priv->lane]->color, cr);
|
||||
|
||||
cairo_fill(cr);
|
||||
|
||||
cairo_destroy(cr);
|
||||
|
@ -196,10 +193,10 @@ gitg_cell_renderer_path_set_property(GObject *object, guint prop_id, const GValu
|
|||
self->priv->lane = g_value_get_int(value);
|
||||
break;
|
||||
case PROP_LANES:
|
||||
self->priv->lanes = (gint8 *)g_value_get_pointer(value);
|
||||
self->priv->lanes = (GitgLane **)g_value_get_pointer(value);
|
||||
break;
|
||||
case PROP_NEXT_LANES:
|
||||
self->priv->next_lanes = (gint8 *)g_value_get_pointer(value);
|
||||
self->priv->next_lanes = (GitgLane **)g_value_get_pointer(value);
|
||||
break;
|
||||
case PROP_LANE_WIDTH:
|
||||
self->priv->lane_width = g_value_get_uint(value);
|
||||
|
|
117
gitg/gitg-color.c
Normal file
117
gitg/gitg-color.c
Normal file
|
@ -0,0 +1,117 @@
|
|||
#include "gitg-color.h"
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
static gint8 current_index = 0;
|
||||
|
||||
static gchar const *palette[] = {
|
||||
"#c4a000",
|
||||
"#4e9a06",
|
||||
"#ce5c00",
|
||||
"#204a87",
|
||||
"#2e3436",
|
||||
"#6c3566",
|
||||
"#a40000",
|
||||
|
||||
"#8ae234",
|
||||
"#fcaf3e",
|
||||
"#729fcf",
|
||||
"#fce94f",
|
||||
"#888a85",
|
||||
"#ad7fa8",
|
||||
"#e9b96e",
|
||||
"#ef2929"
|
||||
};
|
||||
|
||||
void
|
||||
gitg_color_reset()
|
||||
{
|
||||
current_index = 0;
|
||||
}
|
||||
|
||||
void
|
||||
gitg_color_get(GitgColor *color, gdouble *r, gdouble *g, gdouble *b)
|
||||
{
|
||||
gchar const *spec = palette[color->index];
|
||||
GdkColor c;
|
||||
|
||||
gdk_color_parse(spec, &c);
|
||||
|
||||
*r = c.red / 65535.0;
|
||||
*g = c.green / 65535.0;
|
||||
*b = c.blue / 65535.0;
|
||||
}
|
||||
|
||||
void
|
||||
gitg_color_set_cairo_source(GitgColor *color, cairo_t *cr)
|
||||
{
|
||||
gdouble r, g, b;
|
||||
|
||||
gitg_color_get(color, &r, &g, &b);
|
||||
cairo_set_source_rgb(cr, r, g, b);
|
||||
}
|
||||
|
||||
static gint8
|
||||
next_index()
|
||||
{
|
||||
gint8 next = current_index++;
|
||||
|
||||
if (current_index == sizeof(palette) / sizeof(gchar const *))
|
||||
current_index = 0;
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
GitgColor *
|
||||
gitg_color_next()
|
||||
{
|
||||
GitgColor *res = g_new(GitgColor, 1);
|
||||
res->ref_count = 1;
|
||||
res->index = next_index();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
GitgColor *
|
||||
gitg_color_next_index(GitgColor *color)
|
||||
{
|
||||
color->index = next_index();
|
||||
return color;
|
||||
}
|
||||
|
||||
GitgColor *
|
||||
gitg_color_copy(GitgColor *color)
|
||||
{
|
||||
GitgColor *copy = g_new(GitgColor, 1);
|
||||
copy->ref_count = 1;
|
||||
copy->index = color->index;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
GitgColor *
|
||||
gitg_color_ref(GitgColor *color)
|
||||
{
|
||||
if (!color)
|
||||
return NULL;
|
||||
|
||||
++color->ref_count;
|
||||
return color;
|
||||
}
|
||||
|
||||
GitgColor *
|
||||
gitg_color_unref(GitgColor *color)
|
||||
{
|
||||
if (!color)
|
||||
return NULL;
|
||||
|
||||
--color->ref_count;
|
||||
|
||||
if (color->ref_count == 0)
|
||||
{
|
||||
g_free(color);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
25
gitg/gitg-color.h
Normal file
25
gitg/gitg-color.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef __GITG_COLOR_H__
|
||||
#define __GITG_COLOR_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <cairo.h>
|
||||
|
||||
typedef struct _GitgColor GitgColor;
|
||||
|
||||
struct _GitgColor
|
||||
{
|
||||
gulong ref_count;
|
||||
gint8 index;
|
||||
};
|
||||
|
||||
void gitg_color_reset();
|
||||
void gitg_color_get(GitgColor *color, gdouble *r, gdouble *g, gdouble *b);
|
||||
void gitg_color_set_cairo_source(GitgColor *color, cairo_t *cr);
|
||||
|
||||
GitgColor *gitg_color_next();
|
||||
GitgColor *gitg_color_next_index(GitgColor *color);
|
||||
GitgColor *gitg_color_ref(GitgColor *color);
|
||||
GitgColor *gitg_color_copy(GitgColor *color);
|
||||
GitgColor *gitg_color_unref(GitgColor *color);
|
||||
|
||||
#endif /* __GITG_COLOR_H__ */
|
39
gitg/gitg-lane.c
Normal file
39
gitg/gitg-lane.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include "gitg-lane.h"
|
||||
|
||||
/* GitgLane functions */
|
||||
GitgLane *
|
||||
gitg_lane_copy(GitgLane *lane)
|
||||
{
|
||||
GitgLane *copy = g_new(GitgLane, 1);
|
||||
copy->color = gitg_color_ref(lane->color);
|
||||
copy->from = g_slist_copy(lane->from);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
GitgLane *
|
||||
gitg_lane_dup(GitgLane *lane)
|
||||
{
|
||||
GitgLane *dup = g_new(GitgLane, 1);
|
||||
dup->color = gitg_color_copy(lane->color);
|
||||
dup->from = g_slist_copy(lane->from);
|
||||
|
||||
return dup;
|
||||
}
|
||||
|
||||
void
|
||||
gitg_lane_free(GitgLane *lane)
|
||||
{
|
||||
gitg_color_unref(lane->color);
|
||||
g_slist_free(lane->from);
|
||||
}
|
||||
|
||||
GitgLane *
|
||||
gitg_lane_new()
|
||||
{
|
||||
GitgLane *lane = g_new(GitgLane, 1);
|
||||
lane->color = gitg_color_next();
|
||||
lane->from = NULL;
|
||||
|
||||
return lane;
|
||||
}
|
18
gitg/gitg-lane.h
Normal file
18
gitg/gitg-lane.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef __GITG_LANE_H__
|
||||
#define __GITG_LANE_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include "gitg-color.h"
|
||||
|
||||
typedef struct _GitgLane
|
||||
{
|
||||
GitgColor *color; /** Pointer to color */
|
||||
GSList *from; /** List of lanes merging on this lane */
|
||||
} GitgLane;
|
||||
|
||||
GitgLane *gitg_lane_new();
|
||||
GitgLane *gitg_lane_copy(GitgLane *lane);
|
||||
GitgLane *gitg_lane_dup(GitgLane *lane);
|
||||
void gitg_lane_free();
|
||||
|
||||
#endif /* __GITG_LANE_H__ */
|
240
gitg/gitg-lanes.c
Normal file
240
gitg/gitg-lanes.c
Normal file
|
@ -0,0 +1,240 @@
|
|||
#include "gitg-lanes.h"
|
||||
#include "gitg-utils.h"
|
||||
|
||||
#define GITG_LANES_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GITG_TYPE_LANES, GitgLanesPrivate))
|
||||
|
||||
typedef struct _LaneContainer
|
||||
{
|
||||
GitgLane *lane;
|
||||
gchar const *hash;
|
||||
} LaneContainer;
|
||||
|
||||
struct _GitgLanesPrivate
|
||||
{
|
||||
GSList *lanes;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(GitgLanes, gitg_lanes, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
lane_container_free(LaneContainer *container)
|
||||
{
|
||||
gitg_lane_free(container->lane);
|
||||
|
||||
g_free(container);
|
||||
}
|
||||
|
||||
static void
|
||||
free_lanes(GitgLanes *lanes)
|
||||
{
|
||||
GSList *item;
|
||||
|
||||
for (item = lanes->priv->lanes; item; item = item->next)
|
||||
lane_container_free((LaneContainer *)item->data);
|
||||
|
||||
g_slist_free(lanes->priv->lanes);
|
||||
}
|
||||
|
||||
static LaneContainer *
|
||||
find_lane_by_hash(GitgLanes *lanes, gchar const *hash, gint8 *pos)
|
||||
{
|
||||
GSList *item;
|
||||
gint8 p = 0;
|
||||
|
||||
if (!hash)
|
||||
return NULL;
|
||||
|
||||
for (item = lanes->priv->lanes; item; item = item->next)
|
||||
{
|
||||
LaneContainer *container = (LaneContainer *)(item->data);
|
||||
|
||||
if (container && container->hash && gitg_utils_hash_equal(container->hash, hash))
|
||||
{
|
||||
if (pos)
|
||||
*pos = p;
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
++p;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* GitgLanes functions */
|
||||
static void
|
||||
gitg_lanes_finalize(GObject *object)
|
||||
{
|
||||
GitgLanes *self = GITG_LANES(object);
|
||||
|
||||
free_lanes(self);
|
||||
|
||||
G_OBJECT_CLASS(gitg_lanes_parent_class)->finalize(object);
|
||||
}
|
||||
|
||||
static void
|
||||
gitg_lanes_class_init(GitgLanesClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
||||
|
||||
object_class->finalize = gitg_lanes_finalize;
|
||||
|
||||
g_type_class_add_private(object_class, sizeof(GitgLanesPrivate));
|
||||
}
|
||||
|
||||
static void
|
||||
gitg_lanes_init(GitgLanes *self)
|
||||
{
|
||||
self->priv = GITG_LANES_GET_PRIVATE(self);
|
||||
}
|
||||
|
||||
GitgLanes *
|
||||
gitg_lanes_new()
|
||||
{
|
||||
return GITG_LANES(g_object_new(GITG_TYPE_LANES, NULL));
|
||||
}
|
||||
|
||||
static LaneContainer *
|
||||
lane_container_new(gchar const *hash)
|
||||
{
|
||||
LaneContainer *ret = g_new(LaneContainer, 1);
|
||||
ret->hash = hash;
|
||||
ret->lane = gitg_lane_new();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GitgLane **
|
||||
flatten_lanes(GitgLanes *lanes)
|
||||
{
|
||||
gint len = g_slist_length(lanes->priv->lanes);
|
||||
GitgLane **res = g_new(GitgLane *, len + 1);
|
||||
GSList *item = lanes->priv->lanes;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < len; ++i)
|
||||
{
|
||||
res[i] = gitg_lane_copy(((LaneContainer *)(item->data))->lane);
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
res[len] = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
GitgLane **
|
||||
gitg_lanes_reset(GitgLanes *lanes, gchar const *hash)
|
||||
{
|
||||
free_lanes(lanes);
|
||||
|
||||
gitg_color_reset();
|
||||
lanes->priv->lanes = g_slist_prepend(NULL, lane_container_new(hash));
|
||||
|
||||
return flatten_lanes(lanes);
|
||||
}
|
||||
|
||||
static void
|
||||
init_next_layer(GitgLanes *lanes)
|
||||
{
|
||||
GSList *item;
|
||||
gint8 index = 0;
|
||||
|
||||
// Initialize new set of lanes based on 'lanes'. It copies the lane (refs
|
||||
// the color) and adds the lane index as a merge (so it basicly represents
|
||||
// a passthrough)
|
||||
for (item = lanes->priv->lanes; item; item = item->next)
|
||||
{
|
||||
LaneContainer *container = (LaneContainer *)(item->data);
|
||||
GitgLane *lane = gitg_lane_copy(container->lane);
|
||||
g_slist_free(lane->from);
|
||||
|
||||
gitg_lane_free(container->lane);
|
||||
container->lane = lane;
|
||||
container->lane->from = g_slist_prepend(NULL, GINT_TO_POINTER((gint)(index++)));
|
||||
|
||||
++lanes;
|
||||
}
|
||||
}
|
||||
|
||||
GitgLane **
|
||||
gitg_lanes_next(GitgLanes *lanes, GitgRevision *previous, GitgRevision *current, gint8 *currentpos)
|
||||
{
|
||||
init_next_layer(lanes);
|
||||
|
||||
gint8 mypos;
|
||||
LaneContainer *mylane = find_lane_by_hash(lanes, gitg_revision_get_hash(previous), &mypos);
|
||||
|
||||
if (mylane)
|
||||
{
|
||||
GitgColor *nc = gitg_color_copy(mylane->lane->color);
|
||||
gitg_color_unref(mylane->lane->color);
|
||||
mylane->lane->color = nc;
|
||||
|
||||
mylane->hash = NULL;
|
||||
}
|
||||
|
||||
guint num;
|
||||
Hash *parents = gitg_revision_get_parents_hash(previous, &num);
|
||||
int i;
|
||||
|
||||
// Iterate over all parents and find them a lane
|
||||
for (i = 0; i < num; ++i)
|
||||
{
|
||||
gint8 lnpos;
|
||||
LaneContainer *lane = find_lane_by_hash(lanes, parents[i], &lnpos);
|
||||
|
||||
if (lane)
|
||||
{
|
||||
// There already is a lane for this parent. This means that we add
|
||||
// mypos as a merge for the lane, also this means the color of
|
||||
// this lane incluis the merge should change to one color
|
||||
|
||||
lane->lane->from = g_slist_append(lane->lane->from, GINT_TO_POINTER((gint)mypos));
|
||||
gitg_color_next_index(lane->lane->color);
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (mylane && mylane->hash == NULL)
|
||||
{
|
||||
// There is no parent yet which can proceed on the current
|
||||
// revision lane, so set it now
|
||||
mylane->hash = (gchar const *)parents[i];
|
||||
|
||||
// If there is more than one parent, then also change the color
|
||||
// since this revision is actually a merge
|
||||
if (num > 1)
|
||||
{
|
||||
gitg_color_unref(mylane->lane->color);
|
||||
mylane->lane->color = gitg_color_next();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generate a new lane for this parent
|
||||
LaneContainer *newlane = lane_container_new(parents[i]);
|
||||
|
||||
newlane->lane->from = g_slist_prepend(NULL, GINT_TO_POINTER((gint)mypos));
|
||||
lanes->priv->lanes = g_slist_append(lanes->priv->lanes, newlane);
|
||||
|
||||
if (!mylane)
|
||||
mylane = newlane;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the current lane if it is no longer needed
|
||||
if (mylane && mylane->hash == NULL)
|
||||
lanes->priv->lanes = g_slist_remove(lanes->priv->lanes, mylane);
|
||||
|
||||
// Determine the lane of the current revision
|
||||
LaneContainer *lane = find_lane_by_hash(lanes, gitg_revision_get_hash(current), currentpos);
|
||||
|
||||
if (!lane)
|
||||
{
|
||||
// No lane for this revision reserved, we therefore add a new one
|
||||
lanes->priv->lanes = g_slist_append(lanes->priv->lanes, lane_container_new(gitg_revision_get_hash(current)));
|
||||
*currentpos = g_slist_length(lanes->priv->lanes) - 1;
|
||||
}
|
||||
|
||||
return flatten_lanes(lanes);
|
||||
}
|
42
gitg/gitg-lanes.h
Normal file
42
gitg/gitg-lanes.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
#ifndef __GITG_LANES_H__
|
||||
#define __GITG_LANES_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include "gitg-revision.h"
|
||||
#include "gitg-lane.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GITG_TYPE_LANES (gitg_lanes_get_type ())
|
||||
#define GITG_LANES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GITG_TYPE_LANES, GitgLanes))
|
||||
#define GITG_LANES_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GITG_TYPE_LANES, GitgLanes const))
|
||||
#define GITG_LANES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GITG_TYPE_LANES, GitgLanesClass))
|
||||
#define GITG_IS_LANES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GITG_TYPE_LANES))
|
||||
#define GITG_IS_LANES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GITG_TYPE_LANES))
|
||||
#define GITG_LANES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GITG_TYPE_LANES, GitgLanesClass))
|
||||
|
||||
typedef struct _GitgLanes GitgLanes;
|
||||
typedef struct _GitgLanesClass GitgLanesClass;
|
||||
typedef struct _GitgLanesPrivate GitgLanesPrivate;
|
||||
|
||||
struct _GitgLanes {
|
||||
GObject parent;
|
||||
|
||||
GitgLanesPrivate *priv;
|
||||
};
|
||||
|
||||
struct _GitgLanesClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
GType gitg_lanes_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GitgLanes *gitg_lanes_new(void);
|
||||
GitgLane **gitg_lanes_reset(GitgLanes *lanes, gchar const *hash);
|
||||
GitgLane **gitg_lanes_next(GitgLanes *lanes, GitgRevision *previous, GitgRevision *current, gint8 *mylane);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
#endif /* __GITG_LANES_H__ */
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
#include "gitg-repository.h"
|
||||
#include "gitg-utils.h"
|
||||
#include "gitg-lanes.h"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <time.h>
|
||||
|
||||
|
@ -36,7 +38,7 @@ struct _GitgRepositoryPrivate
|
|||
GType column_types[N_COLUMNS];
|
||||
|
||||
GitgRevision **storage;
|
||||
GSList *lanes;
|
||||
GitgLanes *lanes;
|
||||
|
||||
gulong size;
|
||||
gulong allocated;
|
||||
|
@ -118,7 +120,9 @@ tree_model_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter)
|
|||
static gchar *
|
||||
timestamp_to_str(guint64 timestamp)
|
||||
{
|
||||
struct tm *tms = localtime((time_t *)×tamp);
|
||||
time_t t = timestamp;
|
||||
|
||||
struct tm *tms = localtime(&t);
|
||||
char buf[255];
|
||||
|
||||
strftime(buf, 255, "%c", tms);
|
||||
|
@ -241,16 +245,6 @@ tree_model_iter_parent(GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static GType
|
||||
gitg_repository_get_column_type(GtkTreeModel *self, int column)
|
||||
{
|
||||
/* validate our parameters */
|
||||
g_return_val_if_fail(GITG_IS_REPOSITORY(self), G_TYPE_INVALID);
|
||||
g_return_val_if_fail(column >= 0 && column < N_COLUMNS, G_TYPE_INVALID);
|
||||
|
||||
return GITG_REPOSITORY(self)->priv->column_types[column];
|
||||
}
|
||||
|
||||
static void
|
||||
gitg_repository_tree_model_iface_init(GtkTreeModelIface *iface)
|
||||
{
|
||||
|
@ -295,9 +289,6 @@ do_clear(GitgRepository *repository, gboolean emit)
|
|||
repository->priv->storage = NULL;
|
||||
repository->priv->size = 0;
|
||||
repository->priv->allocated = 0;
|
||||
|
||||
g_slist_free(repository->priv->lanes);
|
||||
repository->priv->lanes = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -309,6 +300,8 @@ gitg_repository_finalize(GObject *object)
|
|||
gitg_runner_cancel(rp->priv->loader);
|
||||
g_object_unref(rp->priv->loader);
|
||||
|
||||
g_object_unref(rp->priv->lanes);
|
||||
|
||||
// Clear the model to remove all revision objects
|
||||
do_clear(rp, FALSE);
|
||||
|
||||
|
@ -397,175 +390,6 @@ hash_hash(gconstpointer v)
|
|||
return h;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
hash_equal(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
return memcmp(a, b, 20) == 0;
|
||||
}
|
||||
|
||||
static GSList *
|
||||
find_lane_by_hash(GSList *lanes, gchar const *hash, gint8 *pos)
|
||||
{
|
||||
GSList *item;
|
||||
gint8 p = 0;
|
||||
|
||||
if (!hash)
|
||||
return NULL;
|
||||
|
||||
for (item = lanes; item; item = item->next)
|
||||
{
|
||||
if (item->data && hash_equal(item->data, hash))
|
||||
{
|
||||
if (pos)
|
||||
*pos = p;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
++p;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GSList *
|
||||
init_indices(GitgRepository *self)
|
||||
{
|
||||
GSList *item;
|
||||
GSList *res = NULL;
|
||||
gint8 num = 0;
|
||||
|
||||
for (item = self->priv->lanes; item; item = item->next)
|
||||
{
|
||||
GArray *ar = g_array_new(FALSE, FALSE, sizeof(gint8));
|
||||
|
||||
g_array_append_vals(ar, &num, 1);
|
||||
res = g_slist_prepend(res, ar);
|
||||
++num;
|
||||
}
|
||||
|
||||
return g_slist_reverse(res);
|
||||
}
|
||||
|
||||
static gint8 *
|
||||
flatten_indices(GSList *indices)
|
||||
{
|
||||
gint8 *ret = g_new(gint8, 1);
|
||||
GSList *item;
|
||||
gint num = 0;
|
||||
|
||||
for (item = indices; item; item = item->next)
|
||||
{
|
||||
if (item != indices)
|
||||
{
|
||||
ret = g_renew(gint8, ret, ++num + 1);
|
||||
ret[num - 1] = -1;
|
||||
}
|
||||
|
||||
GArray *ar = (GArray *)(item->data);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < ar->len; ++i)
|
||||
{
|
||||
ret = g_renew(gint8, ret, ++num + 1);
|
||||
ret[num - 1] = g_array_index(ar, gint8, i);
|
||||
}
|
||||
|
||||
g_array_free(ar, TRUE);
|
||||
}
|
||||
|
||||
ret[num] = -2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gint8 *
|
||||
calculate_lane_ids(GitgRepository *self, GitgRevision *rv)
|
||||
{
|
||||
// Determine the new lanes given the previous ones and the parents of the
|
||||
// new revision
|
||||
gint numlanes = g_slist_length(self->priv->lanes);
|
||||
GSList *indices = init_indices(self);
|
||||
|
||||
gint8 myid;
|
||||
GSList *mylane = find_lane_by_hash(self->priv->lanes, gitg_revision_get_hash(rv), &myid);
|
||||
|
||||
if (mylane)
|
||||
mylane->data = NULL;
|
||||
else
|
||||
g_warning("My lane not found for %s", gitg_revision_get_sha1(rv));
|
||||
|
||||
guint num;
|
||||
Hash *parents = gitg_revision_get_parents_hash(rv, &num);
|
||||
int i;
|
||||
|
||||
// Iterate over all parents and find them a lane
|
||||
for (i = 0; i < num; ++i)
|
||||
{
|
||||
gint8 lnpos;
|
||||
GSList *lane = find_lane_by_hash(self->priv->lanes, parents[i], &lnpos);
|
||||
|
||||
if (lane)
|
||||
{
|
||||
// There already is a lane for this hash. Add myid to the
|
||||
// list for lnpos
|
||||
g_array_append_vals((GArray *)g_slist_nth_data(indices, lnpos), &myid, 1);
|
||||
continue;
|
||||
}
|
||||
else if (mylane && mylane->data == NULL)
|
||||
{
|
||||
// There is no parent yet which can proceed on the current
|
||||
// revision lane, so set it now
|
||||
mylane->data = (gpointer)parents[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generate a new lane for this parent
|
||||
self->priv->lanes = g_slist_append(self->priv->lanes, (gpointer)parents[i]);
|
||||
|
||||
GArray *n = g_array_new(FALSE, FALSE, sizeof(gint8));
|
||||
g_array_append_vals(n, &myid, 1);
|
||||
indices = g_slist_append(indices, n);
|
||||
|
||||
if (!mylane)
|
||||
mylane = g_slist_last(self->priv->lanes);
|
||||
}
|
||||
}
|
||||
|
||||
if (mylane && mylane->data == NULL)
|
||||
{
|
||||
// Remove the current lane since it is no longer needed
|
||||
gpointer item = g_slist_nth_data(indices, myid);
|
||||
|
||||
g_array_free((GArray *)item, TRUE);
|
||||
indices = g_slist_remove(indices, item);
|
||||
|
||||
self->priv->lanes = g_slist_remove(self->priv->lanes, mylane->data);
|
||||
}
|
||||
|
||||
gint8 *res = flatten_indices(indices);
|
||||
g_slist_free(indices);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static gint8 *
|
||||
append_mylane(GitgRepository *self, gint8 *indices, GitgRevision *rv)
|
||||
{
|
||||
gint8 *ptr = indices;
|
||||
|
||||
while (*ptr++ != -2)
|
||||
;
|
||||
|
||||
gint num = ptr - indices;
|
||||
self->priv->lanes = g_slist_append(self->priv->lanes, (gpointer)gitg_revision_get_hash(rv));
|
||||
|
||||
indices = g_renew(gint8, indices, num + 1);
|
||||
indices[num - 1] = -1;
|
||||
indices[num] = -2;
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
static void
|
||||
on_loader_update(GitgRunner *object, gchar **buffer, GitgRepository *self)
|
||||
{
|
||||
|
@ -586,29 +410,16 @@ on_loader_update(GitgRunner *object, gchar **buffer, GitgRepository *self)
|
|||
gint64 timestamp = g_ascii_strtoll(components[4], NULL, 0);
|
||||
|
||||
GitgRevision *rv = gitg_revision_new(components[0], components[1], components[2], components[3], timestamp);
|
||||
gint8 *indices = NULL;
|
||||
GitgLane **lanes = NULL;
|
||||
|
||||
gint8 mylane = 0;
|
||||
|
||||
if (self->priv->size == 0)
|
||||
{
|
||||
self->priv->lanes = g_slist_append(self->priv->lanes, (gpointer)gitg_revision_get_hash(rv));
|
||||
indices = g_new0(gint8, 2);
|
||||
indices[1] = -2;
|
||||
}
|
||||
lanes = gitg_lanes_reset(self->priv->lanes, gitg_revision_get_hash(rv));
|
||||
else
|
||||
{
|
||||
indices = calculate_lane_ids(self, self->priv->storage[self->priv->size - 1]);
|
||||
|
||||
if (find_lane_by_hash(self->priv->lanes, gitg_revision_get_hash(rv), &mylane) == NULL)
|
||||
{
|
||||
// No lane for this revision reserved, how this happens, I
|
||||
// don't know
|
||||
indices = append_mylane(self, indices, rv);
|
||||
mylane = g_slist_length(self->priv->lanes) - 1;
|
||||
}
|
||||
}
|
||||
lanes = gitg_lanes_next(self->priv->lanes, self->priv->storage[self->priv->size - 1], rv, &mylane);
|
||||
|
||||
gitg_revision_set_lanes(rv, indices);
|
||||
gitg_revision_set_lanes(rv, lanes);
|
||||
gitg_revision_set_mylane(rv, mylane);
|
||||
|
||||
gitg_repository_add(self, rv, NULL);
|
||||
|
@ -622,13 +433,14 @@ static void
|
|||
gitg_repository_init(GitgRepository *object)
|
||||
{
|
||||
object->priv = GITG_REPOSITORY_GET_PRIVATE(object);
|
||||
object->priv->hashtable = g_hash_table_new_full(hash_hash, hash_equal, NULL, NULL);
|
||||
object->priv->hashtable = g_hash_table_new_full(hash_hash, gitg_utils_hash_equal, NULL, NULL);
|
||||
|
||||
object->priv->column_types[0] = GITG_TYPE_REVISION;
|
||||
object->priv->column_types[1] = G_TYPE_STRING;
|
||||
object->priv->column_types[2] = G_TYPE_STRING;
|
||||
object->priv->column_types[3] = G_TYPE_STRING;
|
||||
|
||||
object->priv->lanes = gitg_lanes_new();
|
||||
object->priv->grow_size = 1000;
|
||||
object->priv->stamp = g_random_int();
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ struct _GitgRevisionPrivate
|
|||
Hash *parents;
|
||||
guint num_parents;
|
||||
|
||||
gint8 *lanes;
|
||||
GitgLane **lanes;
|
||||
gint8 mylane;
|
||||
|
||||
gint64 timestamp;
|
||||
|
@ -19,6 +19,21 @@ struct _GitgRevisionPrivate
|
|||
|
||||
G_DEFINE_TYPE(GitgRevision, gitg_revision, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
free_lanes(GitgRevision *rv)
|
||||
{
|
||||
if (!rv->priv->lanes)
|
||||
return;
|
||||
|
||||
GitgLane **lanes = rv->priv->lanes;
|
||||
GitgLane *lane;
|
||||
|
||||
while ((lane = *lanes++))
|
||||
gitg_lane_free(lane);
|
||||
|
||||
g_free(rv->priv->lanes);
|
||||
}
|
||||
|
||||
static void
|
||||
gitg_revision_finalize(GObject *object)
|
||||
{
|
||||
|
@ -27,7 +42,8 @@ gitg_revision_finalize(GObject *object)
|
|||
g_free(rv->priv->author);
|
||||
g_free(rv->priv->subject);
|
||||
g_free(rv->priv->parents);
|
||||
g_free(rv->priv->lanes);
|
||||
|
||||
free_lanes(rv);
|
||||
|
||||
G_OBJECT_CLASS(gitg_revision_parent_class)->finalize(object);
|
||||
}
|
||||
|
@ -146,7 +162,7 @@ gitg_revision_get_parents(GitgRevision *revision)
|
|||
return ret;
|
||||
}
|
||||
|
||||
gint8 *
|
||||
GitgLane **
|
||||
gitg_revision_get_lanes(GitgRevision *revision)
|
||||
{
|
||||
g_return_val_if_fail(GITG_IS_REVISION(revision), NULL);
|
||||
|
@ -154,11 +170,11 @@ gitg_revision_get_lanes(GitgRevision *revision)
|
|||
}
|
||||
|
||||
void
|
||||
gitg_revision_set_lanes(GitgRevision *revision, gint8 *lanes)
|
||||
gitg_revision_set_lanes(GitgRevision *revision, GitgLane **lanes)
|
||||
{
|
||||
g_return_if_fail(GITG_IS_REVISION(revision));
|
||||
|
||||
g_free(revision->priv->lanes);
|
||||
free_lanes(revision);
|
||||
revision->priv->lanes = lanes;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define __GITG_REVISION_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include "gitg-lane.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
@ -43,8 +44,8 @@ inline Hash *gitg_revision_get_parents_hash(GitgRevision *revision, guint *num_p
|
|||
gchar *gitg_revision_get_sha1(GitgRevision *revision);
|
||||
gchar **gitg_revision_get_parents(GitgRevision *revision);
|
||||
|
||||
gint8 *gitg_revision_get_lanes(GitgRevision *revision);
|
||||
void gitg_revision_set_lanes(GitgRevision *revision, gint8 *lane_ids);
|
||||
GitgLane **gitg_revision_get_lanes(GitgRevision *revision);
|
||||
void gitg_revision_set_lanes(GitgRevision *revision, GitgLane **lanes);
|
||||
|
||||
gint8 gitg_revision_get_mylane(GitgRevision *revision);
|
||||
void gitg_revision_set_mylane(GitgRevision *revision, gint8 mylane);
|
||||
|
|
|
@ -214,7 +214,7 @@ on_renderer_path(GtkTreeViewColumn *column, GitgCellRendererPath *renderer, GtkT
|
|||
gtk_tree_model_get(model, iter, 0, &rv, -1);
|
||||
GtkTreeIter iter1 = *iter;
|
||||
|
||||
gint8 *next_lanes = NULL;
|
||||
GitgLane **next_lanes = NULL;
|
||||
|
||||
if (gtk_tree_model_iter_next(model, &iter1))
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue