From 42cc92ea91cb770e9e0e64e5bede18aa5cfce25d Mon Sep 17 00:00:00 2001 From: randomguy3 Date: Wed, 18 Jan 2012 12:09:21 +0000 Subject: GTK: Arrow heads (and tails)! git-svn-id: https://tikzit.svn.sourceforge.net/svnroot/tikzit/trunk@396 7c02a99a-9b00-45e3-bf44-6f3dd7fddb64 --- tikzit/NEWS | 1 + tikzit/src/gtk/Edge+Render.h | 3 +- tikzit/src/gtk/Edge+Render.m | 30 ++++-- tikzit/src/gtk/EdgeStyleEditor.h | 2 + tikzit/src/gtk/EdgeStyleEditor.m | 199 ++++++++++++++++++++++++++----------- tikzit/src/gtk/EdgeStyleSelector.m | 38 ++----- 6 files changed, 176 insertions(+), 97 deletions(-) diff --git a/tikzit/NEWS b/tikzit/NEWS index 33686b5..3fc51da 100644 --- a/tikzit/NEWS +++ b/tikzit/NEWS @@ -8,6 +8,7 @@ tikzit 0.8 (2012-01-??): * Make everything look a bit better * Edges now start from the edge of a node, not the centre, which is what tikz does + * Edges can now have arrow heads and arrow tails tikzit 0.7 (2011-12-06): * Add bounding box support diff --git a/tikzit/src/gtk/Edge+Render.h b/tikzit/src/gtk/Edge+Render.h index 11f02b1..e88b28a 100644 --- a/tikzit/src/gtk/Edge+Render.h +++ b/tikzit/src/gtk/Edge+Render.h @@ -23,7 +23,8 @@ @interface Edge (Render) + (float) controlPointRadius; -- (void) renderControlsToSurface:(id)surface withContext:(id)context; +- (void) renderControlsInContext:(id)context withTransformer:(Transformer*)transformer; +- (void) renderBasicEdgeInContext:(id)context withTransformer:(Transformer*)t selected:(BOOL)selected; - (void) renderToSurface:(id)surface withContext:(id)context selected:(BOOL)selected; - (NSRect) renderedBoundsWithTransformer:(Transformer*)t whenSelected:(BOOL)selected; - (BOOL) hitByPoint:(NSPoint)p onSurface:(id)surface withFuzz:(float)fuzz; diff --git a/tikzit/src/gtk/Edge+Render.m b/tikzit/src/gtk/Edge+Render.m index 2c89b48..61a78d7 100644 --- a/tikzit/src/gtk/Edge+Render.m +++ b/tikzit/src/gtk/Edge+Render.m @@ -42,9 +42,7 @@ static const float cpLineWidth = 1.0; } } -- (void) renderControlsToSurface:(id )surface withContext:(id)context { - Transformer *transformer = [surface transformer]; - +- (void) renderControlsInContext:(id)context withTransformer:(Transformer*)transformer { [context saveState]; [self updateControls]; @@ -124,38 +122,45 @@ static const float cpLineWidth = 1.0; [context restoreState]; } -- (void) createArrowStrokePathInContext:(id)context withTransformer:(Transformer*)transformer { - [context startPath]; +- (void) renderArrowStrokePathInContext:(id)context withTransformer:(Transformer*)transformer color:(RColor)color { if ([self style] != nil) { switch ([[self style] headStyle]) { case AH_None: break; case AH_Plain: + [context startPath]; [context moveTo:[transformer toScreen:[self leftHeadNormal]]]; [context lineTo:[transformer toScreen:head]]; [context lineTo:[transformer toScreen:[self rightHeadNormal]]]; + [context strokePathWithColor:color]; break; case AH_Latex: + [context startPath]; [context moveTo:[transformer toScreen:[self leftHeadNormal]]]; [context lineTo:[transformer toScreen:head]]; [context lineTo:[transformer toScreen:[self rightHeadNormal]]]; [context closeSubPath]; + [context strokePathWithColor:color andFillWithColor:color]; break; } switch ([[self style] tailStyle]) { case AH_None: break; case AH_Plain: + [context startPath]; [context moveTo:[transformer toScreen:[self leftTailNormal]]]; [context lineTo:[transformer toScreen:tail]]; [context lineTo:[transformer toScreen:[self rightTailNormal]]]; + [context strokePathWithColor:color]; break; case AH_Latex: + [context startPath]; [context moveTo:[transformer toScreen:[self leftTailNormal]]]; [context lineTo:[transformer toScreen:tail]]; [context lineTo:[transformer toScreen:[self rightTailNormal]]]; [context closeSubPath]; + [context strokePathWithColor:color andFillWithColor:color]; break; } } @@ -190,10 +195,10 @@ static const float cpLineWidth = 1.0; } } -- (void) renderToSurface:(id )surface withContext:(id)context selected:(BOOL)selected { +- (void) renderBasicEdgeInContext:(id)context withTransformer:(Transformer*)t selected:(BOOL)selected { [self updateControls]; - [context saveState]; + const CGFloat lineWidth = style ? [style thickness] : edgeWidth; [context setLineWidth:lineWidth]; RColor color = BlackRColor; @@ -201,16 +206,19 @@ static const float cpLineWidth = 1.0; color.alpha = 0.5; } - [self createStrokePathInContext:context withTransformer:[surface transformer]]; + [self createStrokePathInContext:context withTransformer:t]; [context strokePathWithColor:color]; - [self createArrowStrokePathInContext:context withTransformer:[surface transformer]]; - [context strokePathWithColor:color andFillWithColor:color]; + [self renderArrowStrokePathInContext:context withTransformer:t color:color]; [context restoreState]; +} + +- (void) renderToSurface:(id )surface withContext:(id)context selected:(BOOL)selected { + [self renderBasicEdgeInContext:context withTransformer:[surface transformer] selected:selected]; if (selected) { - [self renderControlsToSurface:surface withContext:context]; + [self renderControlsInContext:context withTransformer:[surface transformer]]; } if ([self hasEdgeNode]) { diff --git a/tikzit/src/gtk/EdgeStyleEditor.h b/tikzit/src/gtk/EdgeStyleEditor.h index 70a8747..6e55592 100644 --- a/tikzit/src/gtk/EdgeStyleEditor.h +++ b/tikzit/src/gtk/EdgeStyleEditor.h @@ -25,6 +25,8 @@ GtkTable *table; GtkEntry *nameEdit; GtkComboBox *decorationCombo; + GtkComboBox *headArrowCombo; + GtkComboBox *tailArrowCombo; GtkAdjustment *thicknessAdj; BOOL blockSignals; } diff --git a/tikzit/src/gtk/EdgeStyleEditor.m b/tikzit/src/gtk/EdgeStyleEditor.m index a0b3df2..477ce28 100644 --- a/tikzit/src/gtk/EdgeStyleEditor.m +++ b/tikzit/src/gtk/EdgeStyleEditor.m @@ -28,18 +28,44 @@ #pragma GCC diagnostic pop enum { - ED_NAME_COL = 0, - ED_ICON_COL, - ED_VALUE_COL, - ED_N_COLS + DEC_NAME_COL = 0, + DEC_PREVIEW_COL, + DEC_VALUE_COL, + DEC_N_COLS }; +struct dec_info { + const gchar *name; + const GdkPixdata *pixdata; + int value; +}; +static struct dec_info ed_entries[] = { + { "None", &ED_none_pixdata, ED_None }, + { "Arrow", &ED_arrow_pixdata, ED_Arrow }, + { "Tick", &ED_tick_pixdata, ED_Tick } +}; +static guint n_ed_entries = G_N_ELEMENTS (ed_entries); +static struct dec_info ah_head_entries[] = { + { "None", &AH_none_pixdata, AH_None }, + { "Plain", &AH_plain_head_pixdata, AH_Plain }, + { "Latex", &AH_latex_head_pixdata, AH_Latex } +}; +static guint n_ah_head_entries = G_N_ELEMENTS (ah_head_entries); +static struct dec_info ah_tail_entries[] = { + { "None", &AH_none_pixdata, AH_None }, + { "Plain", &AH_plain_tail_pixdata, AH_Plain }, + { "Latex", &AH_latex_tail_pixdata, AH_Latex } +}; +static guint n_ah_tail_entries = G_N_ELEMENTS (ah_tail_entries); + static const guint row_count = 5; // {{{ Internal interfaces // {{{ GTK+ Callbacks static void style_name_edit_changed_cb (GtkEditable *widget, EdgeStyleEditor *editor); static void decoration_combo_changed_cb (GtkComboBox *widget, EdgeStyleEditor *editor); +static void head_arrow_combo_changed_cb (GtkComboBox *widget, EdgeStyleEditor *editor); +static void tail_arrow_combo_changed_cb (GtkComboBox *widget, EdgeStyleEditor *editor); static void thickness_adjustment_changed_cb (GtkAdjustment *widget, EdgeStyleEditor *editor); // }}} // {{{ Notifications @@ -47,6 +73,8 @@ static void thickness_adjustment_changed_cb (GtkAdjustment *widget, EdgeStyleEdi @interface EdgeStyleEditor (Notifications) - (void) nameChangedTo:(NSString*)value; - (void) edgeDecorationChangedTo:(EdgeDectorationStyle)value; +- (void) headArrowChangedTo:(ArrowHeadStyle)value; +- (void) tailArrowChangedTo:(ArrowHeadStyle)value; - (void) thicknessChangedTo:(double)value; @end @@ -54,9 +82,9 @@ static void thickness_adjustment_changed_cb (GtkAdjustment *widget, EdgeStyleEdi // {{{ Private @interface EdgeStyleEditor (Private) -- (void) loadDecorationStylesInto:(GtkListStore*)list; -- (void) clearEdgeDecorationStyle; -- (void) setEdgeDecorationStyle:(EdgeDectorationStyle)value; +- (void) load:(guint)count decorationStylesFrom:(struct dec_info*)info into:(GtkListStore*)list; +- (void) clearDecCombo:(GtkComboBox*)combo; +- (void) setDecCombo:(GtkComboBox*)combo toValue:(int)value; @end // }}} @@ -88,6 +116,22 @@ static void thickness_adjustment_changed_cb (GtkAdjustment *widget, EdgeStyleEdi 0); // y padding } +- (GtkComboBox*) _createDecComboWithEntries:(struct dec_info*)entries count:(guint)n { + GtkListStore *store = gtk_list_store_new (DEC_N_COLS, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_INT); + [self load:n decorationStylesFrom:entries into:store]; + + GtkComboBox *combo = GTK_COMBO_BOX (gtk_combo_box_new_with_model (GTK_TREE_MODEL (store))); + g_object_unref (store); + GtkCellRenderer *cellRend = gtk_cell_renderer_pixbuf_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), + cellRend, + TRUE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), cellRend, "pixbuf", DEC_PREVIEW_COL); + g_object_ref_sink (combo); + + return combo; +} + - (id) init { self = [super init]; @@ -116,18 +160,7 @@ static void thickness_adjustment_changed_cb (GtkAdjustment *widget, EdgeStyleEdi /** * Edge decoration style */ - GtkListStore *store = gtk_list_store_new (ED_N_COLS, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_INT); - [self loadDecorationStylesInto:store]; - - decorationCombo = GTK_COMBO_BOX (gtk_combo_box_new_with_model (GTK_TREE_MODEL (store))); - g_object_unref (store); - GtkCellRenderer *cellRend = gtk_cell_renderer_pixbuf_new (); - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (decorationCombo), - cellRend, - TRUE); - gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (decorationCombo), cellRend, "pixbuf", ED_ICON_COL); - g_object_ref_sink (decorationCombo); - + decorationCombo = [self _createDecComboWithEntries:ed_entries count:n_ed_entries]; [self _addWidget:GTK_WIDGET (decorationCombo) withLabel:"Decoration" atRow:1]; @@ -137,6 +170,32 @@ static void thickness_adjustment_changed_cb (GtkAdjustment *widget, EdgeStyleEdi self); + /** + * Head arrow style + */ + headArrowCombo = [self _createDecComboWithEntries:ah_head_entries count:n_ah_head_entries]; + [self _addWidget:GTK_WIDGET (headArrowCombo) + withLabel:"Arrow head" + atRow:2]; + g_signal_connect (G_OBJECT (headArrowCombo), + "changed", + G_CALLBACK (head_arrow_combo_changed_cb), + self); + + + /** + * Tail arrow style + */ + tailArrowCombo = [self _createDecComboWithEntries:ah_tail_entries count:n_ah_tail_entries]; + [self _addWidget:GTK_WIDGET (tailArrowCombo) + withLabel:"Arrow tail" + atRow:3]; + g_signal_connect (G_OBJECT (tailArrowCombo), + "changed", + G_CALLBACK (tail_arrow_combo_changed_cb), + self); + + /** * Thickness */ @@ -187,12 +246,16 @@ static void thickness_adjustment_changed_cb (GtkAdjustment *widget, EdgeStyleEdi gtk_entry_set_text(nameEdit, [[style name] UTF8String]); - [self setEdgeDecorationStyle:[style decorationStyle]]; + [self setDecCombo:decorationCombo toValue:[style decorationStyle]]; + [self setDecCombo:headArrowCombo toValue:[style headStyle]]; + [self setDecCombo:tailArrowCombo toValue:[style tailStyle]]; gtk_adjustment_set_value(thicknessAdj, [style thickness]); } else { gtk_entry_set_text(nameEdit, ""); - [self clearEdgeDecorationStyle]; + [self clearDecCombo:decorationCombo]; + [self clearDecCombo:headArrowCombo]; + [self clearDecCombo:tailArrowCombo]; gtk_adjustment_set_value(thicknessAdj, 1.0); gtk_widget_set_sensitive (GTK_WIDGET (table), FALSE); } @@ -219,6 +282,14 @@ static void thickness_adjustment_changed_cb (GtkAdjustment *widget, EdgeStyleEdi [style setDecorationStyle:value]; } +- (void) headArrowChangedTo:(ArrowHeadStyle)value { + [style setHeadStyle:value]; +} + +- (void) tailArrowChangedTo:(ArrowHeadStyle)value { + [style setTailStyle:value]; +} + - (void) thicknessChangedTo:(double)value { [style setThickness:(float)value]; } @@ -230,50 +301,34 @@ static void thickness_adjustment_changed_cb (GtkAdjustment *widget, EdgeStyleEdi @implementation EdgeStyleEditor (Private) - (BOOL) signalsBlocked { return blockSignals; } -- (void) loadDecorationStylesInto:(GtkListStore*)list { +- (void) load:(guint)count decorationStylesFrom:(struct dec_info*)info into:(GtkListStore*)list { GtkTreeIter iter; - GdkPixbuf *buf = gdk_pixbuf_from_pixdata (&ED_none_pixdata, FALSE, NULL); - gtk_list_store_append (list, &iter); - gtk_list_store_set (list, &iter, - ED_NAME_COL, "None", - ED_ICON_COL, buf, - ED_VALUE_COL, ED_None, - -1); - g_object_unref (buf); - - buf = gdk_pixbuf_from_pixdata (&ED_arrow_pixdata, FALSE, NULL); - gtk_list_store_append (list, &iter); - gtk_list_store_set (list, &iter, - ED_NAME_COL, "Arrow", - ED_ICON_COL, buf, - ED_VALUE_COL, ED_Arrow, - -1); - g_object_unref (buf); - - buf = gdk_pixbuf_from_pixdata (&ED_tick_pixdata, FALSE, NULL); - gtk_list_store_append (list, &iter); - gtk_list_store_set (list, &iter, - ED_NAME_COL, "Tick", - ED_ICON_COL, buf, - ED_VALUE_COL, ED_Tick, - -1); - g_object_unref (buf); + for (guint i = 0; i < count; ++i) { + GdkPixbuf *buf = gdk_pixbuf_from_pixdata (info[i].pixdata, FALSE, NULL); + gtk_list_store_append (list, &iter); + gtk_list_store_set (list, &iter, + DEC_NAME_COL, info[i].name, + DEC_PREVIEW_COL, buf, + DEC_VALUE_COL, info[i].value, + -1); + g_object_unref (buf); + } } -- (void) clearEdgeDecorationStyle { - gtk_combo_box_set_active (decorationCombo, -1); +- (void) clearDecCombo:(GtkComboBox*)combo { + gtk_combo_box_set_active (combo, -1); } -- (void) setEdgeDecorationStyle:(EdgeDectorationStyle)value { - GtkTreeModel *model = gtk_combo_box_get_model (decorationCombo); +- (void) setDecCombo:(GtkComboBox*)combo toValue:(int)value { + GtkTreeModel *model = gtk_combo_box_get_model (combo); GtkTreeIter iter; if (gtk_tree_model_get_iter_first (model, &iter)) { do { - EdgeDectorationStyle ed_style; - gtk_tree_model_get (model, &iter, ED_VALUE_COL, &ed_style, -1); - if (ed_style == value) { - gtk_combo_box_set_active_iter (decorationCombo, &iter); + int rowValue; + gtk_tree_model_get (model, &iter, DEC_VALUE_COL, &rowValue, -1); + if (rowValue == value) { + gtk_combo_box_set_active_iter (combo, &iter); return; } } while (gtk_tree_model_iter_next (model, &iter)); @@ -305,12 +360,42 @@ static void decoration_combo_changed_cb (GtkComboBox *widget, EdgeStyleEditor *e GtkTreeIter iter; gtk_combo_box_get_active_iter (widget, &iter); EdgeDectorationStyle dec = ED_None; - gtk_tree_model_get (gtk_combo_box_get_model (widget), &iter, ED_VALUE_COL, &dec, -1); + gtk_tree_model_get (gtk_combo_box_get_model (widget), &iter, DEC_VALUE_COL, &dec, -1); [editor edgeDecorationChangedTo:dec]; [pool drain]; } +static void head_arrow_combo_changed_cb (GtkComboBox *widget, EdgeStyleEditor *editor) { + if ([editor signalsBlocked]) + return; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + GtkTreeIter iter; + gtk_combo_box_get_active_iter (widget, &iter); + ArrowHeadStyle dec = AH_None; + gtk_tree_model_get (gtk_combo_box_get_model (widget), &iter, DEC_VALUE_COL, &dec, -1); + [editor headArrowChangedTo:dec]; + + [pool drain]; +} + +static void tail_arrow_combo_changed_cb (GtkComboBox *widget, EdgeStyleEditor *editor) { + if ([editor signalsBlocked]) + return; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + GtkTreeIter iter; + gtk_combo_box_get_active_iter (widget, &iter); + ArrowHeadStyle dec = AH_None; + gtk_tree_model_get (gtk_combo_box_get_model (widget), &iter, DEC_VALUE_COL, &dec, -1); + [editor tailArrowChangedTo:dec]; + + [pool drain]; +} + static void thickness_adjustment_changed_cb (GtkAdjustment *adj, EdgeStyleEditor *editor) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [editor thicknessChangedTo:gtk_adjustment_get_value (adj)]; diff --git a/tikzit/src/gtk/EdgeStyleSelector.m b/tikzit/src/gtk/EdgeStyleSelector.m index 243d176..4e7736f 100644 --- a/tikzit/src/gtk/EdgeStyleSelector.m +++ b/tikzit/src/gtk/EdgeStyleSelector.m @@ -18,6 +18,9 @@ #import "EdgeStyleSelector.h" #import "CairoRenderContext.h" +#import "Edge.h" +#import "Edge+Render.h" +#import "Node.h" #import "Shape.h" #import "Shape+Render.h" #import "ShapeNames.h" @@ -373,37 +376,16 @@ enum { NSRect pixbufBounds = NSMakeRect(0.0, 0.0, width, height); NSRect graphBounds = [transformer rectFromScreen:pixbufBounds]; - NSPoint mid = NSMakePoint (NSMidX (graphBounds), NSMidY (graphBounds)); - NSPoint start = NSMakePoint (NSMinX (graphBounds) + 0.1f, mid.y); - NSPoint end = NSMakePoint (NSMaxX (graphBounds) - 0.1f, mid.y); - NSPoint midTan = NSMakePoint (mid.x + 0.1f, mid.y); - NSPoint leftNormal = NSMakePoint (mid.x, mid.y - 0.1f); - NSPoint rightNormal = NSMakePoint (mid.x, mid.y + 0.1f); + NSPoint start = NSMakePoint (NSMinX (graphBounds) + 0.1f, NSMidY (graphBounds)); + NSPoint end = NSMakePoint (NSMaxX (graphBounds) - 0.1f, NSMidY (graphBounds)); + Node *src = [Node nodeWithPoint:start]; + Node *tgt = [Node nodeWithPoint:end]; + Edge *e = [Edge edgeWithSource:src andTarget:tgt]; + [e setStyle:style]; CairoRenderContext *context = [[CairoRenderContext alloc] initForSurface:surface]; [context clearSurface]; - - [context startPath]; - [context moveTo:[transformer toScreen:start]]; - [context lineTo:[transformer toScreen:end]]; - - switch ([style decorationStyle]) { - case ED_None: - break; - case ED_Tick: - [context moveTo:[transformer toScreen:leftNormal]]; - [context lineTo:[transformer toScreen:rightNormal]]; - break; - case ED_Arrow: - [context moveTo:[transformer toScreen:leftNormal]]; - [context lineTo:[transformer toScreen:midTan]]; - [context lineTo:[transformer toScreen:rightNormal]]; - break; - } - - [context setLineWidth:[style thickness]]; - [context strokePathWithColor:BlackRColor]; - + [e renderBasicEdgeInContext:context withTransformer:transformer selected:NO]; [context release]; return [self pixbufFromSurface:surface]; -- cgit v1.2.3