summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrandomguy3 <randomguy3@7c02a99a-9b00-45e3-bf44-6f3dd7fddb64>2012-01-18 12:09:21 +0000
committerrandomguy3 <randomguy3@7c02a99a-9b00-45e3-bf44-6f3dd7fddb64>2012-01-18 12:09:21 +0000
commit42cc92ea91cb770e9e0e64e5bede18aa5cfce25d (patch)
tree8a7c592d746e760b68eb7b0da17de4c5ec16f928
parent9e71f7db3c487633d2ec170369d0ecc5f61280cf (diff)
GTK: Arrow heads (and tails)!
git-svn-id: https://tikzit.svn.sourceforge.net/svnroot/tikzit/trunk@396 7c02a99a-9b00-45e3-bf44-6f3dd7fddb64
-rw-r--r--tikzit/NEWS1
-rw-r--r--tikzit/src/gtk/Edge+Render.h3
-rw-r--r--tikzit/src/gtk/Edge+Render.m30
-rw-r--r--tikzit/src/gtk/EdgeStyleEditor.h2
-rw-r--r--tikzit/src/gtk/EdgeStyleEditor.m199
-rw-r--r--tikzit/src/gtk/EdgeStyleSelector.m38
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>)surface withContext:(id<RenderContext>)context;
+- (void) renderControlsInContext:(id<RenderContext>)context withTransformer:(Transformer*)transformer;
+- (void) renderBasicEdgeInContext:(id<RenderContext>)context withTransformer:(Transformer*)t selected:(BOOL)selected;
- (void) renderToSurface:(id<Surface>)surface withContext:(id<RenderContext>)context selected:(BOOL)selected;
- (NSRect) renderedBoundsWithTransformer:(Transformer*)t whenSelected:(BOOL)selected;
- (BOOL) hitByPoint:(NSPoint)p onSurface:(id<Surface>)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>)surface withContext:(id<RenderContext>)context {
- Transformer *transformer = [surface transformer];
-
+- (void) renderControlsInContext:(id<RenderContext>)context withTransformer:(Transformer*)transformer {
[context saveState];
[self updateControls];
@@ -124,38 +122,45 @@ static const float cpLineWidth = 1.0;
[context restoreState];
}
-- (void) createArrowStrokePathInContext:(id<RenderContext>)context withTransformer:(Transformer*)transformer {
- [context startPath];
+- (void) renderArrowStrokePathInContext:(id<RenderContext>)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>)surface withContext:(id<RenderContext>)context selected:(BOOL)selected {
+- (void) renderBasicEdgeInContext:(id<RenderContext>)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>)surface withContext:(id<RenderContext>)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];
@@ -138,6 +171,32 @@ static void thickness_adjustment_changed_cb (GtkAdjustment *widget, EdgeStyleEdi
/**
+ * 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
*/
thicknessAdj = GTK_ADJUSTMENT (gtk_adjustment_new (
@@ -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];