diff options
-rw-r--r-- | liblogdiag/ld-category-symbol-view.c | 230 |
1 files changed, 210 insertions, 20 deletions
diff --git a/liblogdiag/ld-category-symbol-view.c b/liblogdiag/ld-category-symbol-view.c index 36c0d0f..e9e9e6c 100644 --- a/liblogdiag/ld-category-symbol-view.c +++ b/liblogdiag/ld-category-symbol-view.c @@ -21,17 +21,41 @@ * onto #LdDiagramView. */ +/* Milimetres per inch. */ +#define MM_PER_INCH 25.4 +/* The default screen resolution in DPI units. */ +#define DEFAULT_SCREEN_RESOLUTION 96 + +#define SYMBOL_WIDTH 50 /* Width of a symbol. */ +#define SYMBOL_HEIGHT 40 /* Height of a symbol. */ +#define SYMBOL_SPACING 10 /* Spacing between symbols, and also borders. */ + /* * LdCategorySymbolViewPrivate: * @category: a category object assigned as a model. + * @path: path to the category within the library. + * @layout: (element-type SymbolData *): current layout of symbols. + * @height_negotiation: whether we are negotiating height right now. */ struct _LdCategorySymbolViewPrivate { LdCategory *category; gchar *path; + GSList *layout; guint height_negotiation : 1; }; +typedef struct +{ + LdSymbol *symbol; /* The associated symbol, ref'ed. */ + gchar *path; /* Path to the symbol. */ + + GdkRectangle rect; /* Clipping rectangle. */ + gdouble scale; /* Scale to draw the symbol in. */ + gdouble dx, dy; /* Delta into .rect. */ +} +SymbolData; + enum { PROP_0, @@ -99,12 +123,29 @@ ld_category_symbol_view_init (LdCategorySymbolView *self) } static void +symbol_data_free (SymbolData *self) +{ + g_object_unref (self->symbol); + g_free (self->path); + g_slice_free (SymbolData, self); +} + +static void +layout_destroy (LdCategorySymbolView *self) +{ + g_slist_foreach (self->priv->layout, (GFunc) symbol_data_free, NULL); + g_slist_free (self->priv->layout); + self->priv->layout = NULL; +} + +static void ld_category_symbol_view_finalize (GObject *gobject) { LdCategorySymbolView *self; self = LD_CATEGORY_SYMBOL_VIEW (gobject); + layout_destroy (self); if (self->priv->category) g_object_unref (self->priv->category); g_free (self->priv->path); @@ -148,6 +189,163 @@ ld_category_symbol_view_set_property (GObject *object, guint property_id, } } + +typedef struct +{ + guint total_height; /* Total height required to show the symbols. */ + guint max_width; /* Width available to the widget. */ + + GSList *cur_row; /* Current row of symbols. */ + guint cur_width; /* Current width of the row. */ + guint cur_height_up; /* Current max. upper height of symbols. */ + guint cur_height_down; /* Current max. lower height of symbols. */ +} +LayoutContext; + +static GSList * +layout_finish_row (LayoutContext *ctx) +{ + GSList *item, *result; + gint row_height, h_delta; + + row_height = SYMBOL_SPACING + ctx->cur_height_up + ctx->cur_height_down; + h_delta = (ctx->max_width - ctx->cur_width) / 2; + + for (item = ctx->cur_row; item; item = item->next) + { + SymbolData *data; + + data = item->data; + data->rect.x += h_delta; + data->rect.height = row_height; + data->dy = SYMBOL_SPACING * 0.5 + ctx->cur_height_up; + } + + result = g_slist_reverse (ctx->cur_row); + + ctx->cur_row = NULL; + ctx->total_height += row_height; + + ctx->cur_width = SYMBOL_SPACING; + ctx->cur_height_up = 0; + ctx->cur_height_down = 0; + + return result; +} + +static gint +layout_for_width (LdCategorySymbolView *self, gint width) +{ + GSList *symbols, *iter; + LayoutContext ctx = {SYMBOL_SPACING, 0, NULL, SYMBOL_SPACING, 0, 0}; + + layout_destroy (self); + ctx.max_width = width; + + symbols = (GSList *) ld_category_get_symbols (self->priv->category); + for (iter = symbols; iter; iter = iter->next) + { + SymbolData *data; + LdRectangle area; + LdSymbol *symbol; + gint real_width, height_up, height_down; + + symbol = LD_SYMBOL (iter->data); + ld_symbol_get_area (symbol, &area); + + data = g_slice_new (SymbolData); + data->symbol = g_object_ref (symbol); + data->path = g_build_path (LD_LIBRARY_IDENTIFIER_SEPARATOR, + self->priv->path, ld_symbol_get_name (symbol), NULL); + + /* Compute the scale to fit the symbol to an area of + * SYMBOL_WIDTH * SYMBOL_HEIGHT, vertically centred. */ + data->scale = SYMBOL_HEIGHT * 0.5 + / MAX (ABS (area.y), ABS (area.y + area.height)) * 0.5; + if (data->scale * area.width > SYMBOL_WIDTH) + data->scale = SYMBOL_WIDTH / area.width; + + real_width = data->scale * area.width + 0.5; + data->rect.width = real_width + SYMBOL_SPACING; + /* Now I have no idea what this does but it worked before. + * When I do, I have to write it in here. */ + data->dx = data->rect.width * 0.5 + data->scale + * (area.width * 0.5 - ABS (area.x + area.width)); + + if (ctx.cur_width + real_width + SYMBOL_SPACING > ctx.max_width + && ctx.cur_row != NULL) + { + self->priv->layout = g_slist_concat (self->priv->layout, + layout_finish_row (&ctx)); + } + + /* Half of the spacing is included on each side of the rect. */ + data->rect.x = ctx.cur_width - SYMBOL_SPACING / 2; + data->rect.y = ctx.total_height - SYMBOL_SPACING / 2; + + height_up = data->scale * ABS (area.y); + height_down = data->scale * ABS (area.y + area.height); + + if (height_up > ctx.cur_height_up) + ctx.cur_height_up = height_up; + if (height_down > ctx.cur_height_down) + ctx.cur_height_down = height_down; + + ctx.cur_row = g_slist_prepend (ctx.cur_row, data); + ctx.cur_width += real_width + SYMBOL_SPACING; + } + + if (ctx.cur_row != NULL) + self->priv->layout = g_slist_concat (self->priv->layout, + layout_finish_row (&ctx)); + + return ctx.total_height; +} + + +static gboolean +on_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) +{ + LdCategorySymbolView *self; + cairo_t *cr; + GSList *iter; + + self = LD_CATEGORY_SYMBOL_VIEW (widget); + cr = gdk_cairo_create (gtk_widget_get_window (widget)); + gdk_cairo_rectangle (cr, &event->area); + cairo_clip (cr); + + gdk_cairo_set_source_color (cr, + >k_widget_get_style (widget)->base[GTK_STATE_NORMAL]); + cairo_paint (cr); + + for (iter = self->priv->layout; iter; iter = iter->next) + { + SymbolData *data; + + data = iter->data; + if (!gdk_rectangle_intersect (&data->rect, &event->area, NULL)) + continue; + + cairo_save (cr); + gdk_cairo_rectangle (cr, &data->rect); + cairo_clip (cr); + + cairo_translate (cr, data->rect.x + data->dx, data->rect.y + data->dy); + cairo_scale (cr, data->scale, data->scale); + + gdk_cairo_set_source_color (cr, + >k_widget_get_style (widget)->text[GTK_STATE_NORMAL]); + cairo_set_line_width (cr, 1 / data->scale); + ld_symbol_draw (data->symbol, cr); + + cairo_restore (cr); + } + + cairo_destroy (cr); + return FALSE; +} + static void on_size_request (GtkWidget *widget, GtkRequisition *requisition, gpointer user_data) @@ -156,17 +354,25 @@ on_size_request (GtkWidget *widget, GtkRequisition *requisition, self = LD_CATEGORY_SYMBOL_VIEW (widget); - requisition->width = 10; + if (!self->priv->category + || !ld_category_get_symbols (self->priv->category)) + { + requisition->width = 0; + requisition->height = 0; + return; + } + + requisition->width = SYMBOL_WIDTH + 2 * SYMBOL_SPACING; if (self->priv->height_negotiation) { GtkAllocation alloc; gtk_widget_get_allocation (widget, &alloc); - requisition->height = 5000 / alloc.width; + requisition->height = layout_for_width (self, alloc.width); } else - requisition->height = 10; + requisition->height = SYMBOL_HEIGHT + 2 * SYMBOL_SPACING; } static void @@ -186,23 +392,6 @@ on_size_allocate (GtkWidget *widget, GdkRectangle *allocation, } } -static gboolean -on_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) -{ - cairo_t *cr; - - cr = gdk_cairo_create (gtk_widget_get_window (widget)); - gdk_cairo_rectangle (cr, &event->area); - cairo_clip (cr); - - gdk_cairo_set_source_color (cr, - >k_widget_get_style (widget)->base[GTK_STATE_NORMAL]); - cairo_paint (cr); - - cairo_destroy (cr); - return FALSE; -} - /* ===== Generic interface etc. ============================================ */ /** @@ -251,6 +440,7 @@ ld_category_symbol_view_set_category (LdCategorySymbolView *self, g_object_ref (category); g_object_notify (G_OBJECT (self), "category"); + gtk_widget_queue_resize (GTK_WIDGET (self)); } /** |