From 3bfdc232f22f0fa5be20e0b603a8287626ec0c2f Mon Sep 17 00:00:00 2001 From: Alex Merry Date: Tue, 26 Mar 2013 12:54:41 +0000 Subject: Add edge anchor editing to GTK port Includes functions for testing if an anchor is valid. --- tikzit/src/common/NSString+Tikz.h | 3 +- tikzit/src/common/NSString+Tikz.m | 7 +- tikzit/src/common/TikzGraphAssembler.h | 10 +++ tikzit/src/common/TikzGraphAssembler.m | 39 +++++++--- tikzit/src/gtk/PropertiesPane.h | 2 + tikzit/src/gtk/PropertiesPane.m | 127 ++++++++++++++++++++++++++++++++- tikzit/src/gtk/PropertyListEditor.m | 6 +- 7 files changed, 179 insertions(+), 15 deletions(-) diff --git a/tikzit/src/common/NSString+Tikz.h b/tikzit/src/common/NSString+Tikz.h index 639e269..ea6ea40 100644 --- a/tikzit/src/common/NSString+Tikz.h +++ b/tikzit/src/common/NSString+Tikz.h @@ -19,7 +19,8 @@ @interface NSString (Tikz) - (NSString*) tikzEscapedString; - - (BOOL) isValidTikz; + - (BOOL) isValidTikzPropertyNameOrValue; + - (BOOL) isValidAnchor; @end // vi:ft=objc:noet:ts=4:sts=4:sw=4 diff --git a/tikzit/src/common/NSString+Tikz.m b/tikzit/src/common/NSString+Tikz.m index 8b11ad2..520ffd3 100644 --- a/tikzit/src/common/NSString+Tikz.m +++ b/tikzit/src/common/NSString+Tikz.m @@ -16,6 +16,7 @@ */ #import "NSString+Tikz.h" +#import "TikzGraphAssembler.h" @implementation NSString (Tikz) @@ -31,7 +32,7 @@ } } -- (BOOL) isValidTikz { +- (BOOL) isValidTikzPropertyNameOrValue { NSUInteger length = [self length]; unsigned int brace_depth = 0; unsigned int escape = 0; @@ -53,6 +54,10 @@ return !escape && brace_depth == 0; } +- (BOOL) isValidAnchor { + return [TikzGraphAssembler validateTikzEdgeAnchor:self]; +} + @end // vi:ft=objc:noet:ts=4:sts=4:sw=4 diff --git a/tikzit/src/common/TikzGraphAssembler.h b/tikzit/src/common/TikzGraphAssembler.h index c365128..3403969 100644 --- a/tikzit/src/common/TikzGraphAssembler.h +++ b/tikzit/src/common/TikzGraphAssembler.h @@ -100,6 +100,16 @@ */ + (BOOL)validateTikzPropertyNameOrValue:(NSString*)tikz; +/** + * Validate an edge anchor + * + * Checks that the given string will successfully lex if used as an anchor for + * and edge + * @param tikz the string to validate + * @return YES if the string can be used as an edge anchor, NO otherwise + */ ++ (BOOL)validateTikzEdgeAnchor:(NSString*)tikz; + @end // vi:ft=objc:noet:ts=4:sts=4:sw=4 diff --git a/tikzit/src/common/TikzGraphAssembler.m b/tikzit/src/common/TikzGraphAssembler.m index c49298f..6b191a2 100644 --- a/tikzit/src/common/TikzGraphAssembler.m +++ b/tikzit/src/common/TikzGraphAssembler.m @@ -113,21 +113,44 @@ } + (BOOL)validateTikzPropertyNameOrValue:(NSString*)tikz { - BOOL r; - - NSString * testTikz = [NSString stringWithFormat: @"{%@}", tikz]; - + BOOL valid; + + NSString * testTikz = [NSString stringWithFormat: @"{%@}", tikz]; + void *scanner; yylex_init (&scanner); yyset_extra(nil, scanner); yy_scan_string([testTikz UTF8String], scanner); YYSTYPE lval; YYLTYPE lloc; - yylex(&lval, &lloc, scanner); - r = !(yyget_leng(scanner) < [testTikz length]); + int result = yylex(&lval, &lloc, scanner); + valid = (result == DELIMITEDSTRING) && + (yyget_leng(scanner) == [testTikz length]); yylex_destroy(scanner); - - return r; + + return valid; +} + ++ (BOOL)validateTikzEdgeAnchor:(NSString*)tikz { + BOOL valid = YES; + + NSString * testTikz = [NSString stringWithFormat: @"(1.%@)", tikz]; + + void *scanner; + yylex_init (&scanner); + yyset_extra(nil, scanner); + yy_scan_string([testTikz UTF8String], scanner); + YYSTYPE lval; + YYLTYPE lloc; + valid = valid && (yylex(&lval, &lloc, scanner) == LEFTPARENTHESIS); + valid = valid && (yylex(&lval, &lloc, scanner) == REFSTRING); + valid = valid && (yylex(&lval, &lloc, scanner) == FULLSTOP); + valid = valid && (yylex(&lval, &lloc, scanner) == REFSTRING); + valid = valid && (yylex(&lval, &lloc, scanner) == RIGHTPARENTHESIS); + valid = valid && (lloc.last_column == [testTikz length]); + yylex_destroy(scanner); + + return valid; } @end diff --git a/tikzit/src/gtk/PropertiesPane.h b/tikzit/src/gtk/PropertiesPane.h index bde4424..c76efae 100644 --- a/tikzit/src/gtk/PropertiesPane.h +++ b/tikzit/src/gtk/PropertiesPane.h @@ -53,6 +53,8 @@ GtkToggleButton *edgeNodeToggle; GtkWidget *edgeNodePropsWidget; GtkEntry *edgeNodeLabelEntry; + GtkEntry *edgeSourceAnchorEntry; + GtkEntry *edgeTargetAnchorEntry; } @property (retain) TikzDocument *document; diff --git a/tikzit/src/gtk/PropertiesPane.m b/tikzit/src/gtk/PropertiesPane.m index c42dded..ba43298 100644 --- a/tikzit/src/gtk/PropertiesPane.m +++ b/tikzit/src/gtk/PropertiesPane.m @@ -33,6 +33,8 @@ static GtkWidget *createBoldLabel (const gchar *text); static void node_label_changed_cb (GtkEditable *widget, PropertiesPane *pane); static void edge_node_label_changed_cb (GtkEditable *widget, PropertiesPane *pane); static void edge_node_toggled_cb (GtkToggleButton *widget, PropertiesPane *pane); +static void edge_source_anchor_changed_cb (GtkEditable *widget, PropertiesPane *pane); +static void edge_target_anchor_changed_cb (GtkEditable *widget, PropertiesPane *pane); // }}} @interface PropertiesPane (Notifications) @@ -42,6 +44,8 @@ static void edge_node_toggled_cb (GtkToggleButton *widget, PropertiesPane *pane) - (void) nodeLabelEdited:(NSString*)newValue; - (void) edgeNodeLabelEdited:(NSString*)newValue; - (void) edgeNodeToggled:(BOOL)newValue; +- (BOOL) edgeSourceAnchorEdited:(NSString*)newValue; +- (BOOL) edgeTargetAnchorEdited:(NSString*)newValue; @end @interface PropertiesPane (Private) @@ -186,6 +190,45 @@ static void edge_node_toggled_cb (GtkToggleButton *widget, PropertiesPane *pane) split, FALSE, FALSE, 0); + GtkWidget *anchorTable = gtk_table_new (2, 2, FALSE); + + label = gtk_label_new ("Source anchor:"); + gtk_table_attach_defaults (GTK_TABLE (anchorTable), label, + 0, 1, 0, 1); + edgeSourceAnchorEntry = GTK_ENTRY (gtk_entry_new ()); + g_object_ref_sink (edgeSourceAnchorEntry); + gtk_table_attach_defaults (GTK_TABLE (anchorTable), + GTK_WIDGET (edgeSourceAnchorEntry), + 1, 2, 0, 1); + g_signal_connect (G_OBJECT (edgeSourceAnchorEntry), + "changed", + G_CALLBACK (edge_source_anchor_changed_cb), + self); + + label = gtk_label_new ("Target anchor:"); + gtk_table_attach_defaults (GTK_TABLE (anchorTable), label, + 0, 1, 1, 2); + edgeTargetAnchorEntry = GTK_ENTRY (gtk_entry_new ()); + g_object_ref_sink (edgeTargetAnchorEntry); + gtk_table_attach_defaults (GTK_TABLE (anchorTable), + GTK_WIDGET (edgeTargetAnchorEntry), + 1, 2, 1, 2); + g_signal_connect (G_OBJECT (edgeTargetAnchorEntry), + "changed", + G_CALLBACK (edge_target_anchor_changed_cb), + self); + + gtk_widget_show_all (anchorTable); + gtk_box_pack_start (GTK_BOX (edgePropsWidget), + anchorTable, + FALSE, FALSE, 0); + + split = gtk_hseparator_new (); + gtk_widget_show (split); + gtk_box_pack_start (GTK_BOX (edgePropsWidget), + split, + FALSE, FALSE, 0); + edgeNodeToggle = GTK_TOGGLE_BUTTON (gtk_check_button_new_with_label ("Child node")); g_object_ref_sink (edgeNodeToggle); gtk_widget_show (GTK_WIDGET (edgeNodeToggle)); @@ -229,6 +272,8 @@ static void edge_node_toggled_cb (GtkToggleButton *widget, PropertiesPane *pane) g_object_unref (edgeNodeToggle); g_object_unref (edgeNodePropsWidget); g_object_unref (edgeNodeLabelEntry); + g_object_unref (edgeSourceAnchorEntry); + g_object_unref (edgeTargetAnchorEntry); g_object_unref (layout); @@ -325,7 +370,7 @@ static void edge_node_toggled_cb (GtkToggleButton *widget, PropertiesPane *pane) return; } - if ([newValue isValidTikz]) { + if ([newValue isValidTikzPropertyNameOrValue]) { Node *node = [sel anyObject]; [document startModifyNode:node]; [node setLabel:newValue]; @@ -351,7 +396,7 @@ static void edge_node_toggled_cb (GtkToggleButton *widget, PropertiesPane *pane) return; } - if ([newValue isValidTikz]) { + if ([newValue isValidTikzPropertyNameOrValue]) { [document startModifyEdge:edge]; [[edge edgeNode] setLabel:newValue]; [document endModifyEdge]; @@ -377,6 +422,48 @@ static void edge_node_toggled_cb (GtkToggleButton *widget, PropertiesPane *pane) [document endModifyEdge]; } +- (BOOL) edgeSourceAnchorEdited:(NSString*)newValue { + if (blockUpdates) + return YES; + + NSSet *sel = [[document pickSupport] selectedEdges]; + if ([sel count] != 1) { + NSLog(@"Expected single edge selected; got %lu", [sel count]); + return YES; + } + + Edge *edge = [sel anyObject]; + if ([newValue isValidAnchor]) { + [document startModifyEdge:edge]; + [edge setSourceAnchor:newValue]; + [document endModifyEdge]; + return YES; + } else { + return NO; + } +} + +- (BOOL) edgeTargetAnchorEdited:(NSString*)newValue { + if (blockUpdates) + return YES; + + NSSet *sel = [[document pickSupport] selectedEdges]; + if ([sel count] != 1) { + NSLog(@"Expected single edge selected; got %lu", [sel count]); + return YES; + } + + Edge *edge = [sel anyObject]; + if ([newValue isValidAnchor]) { + [document startModifyEdge:edge]; + [edge setTargetAnchor:newValue]; + [document endModifyEdge]; + return YES; + } else { + return NO; + } +} + @end // }}} // {{{ Private @@ -419,6 +506,12 @@ static void edge_node_toggled_cb (GtkToggleButton *widget, PropertiesPane *pane) Edge *e = [edgeSel anyObject]; [edgePropDelegate setEdge:e]; [edgeProps setData:[e data]]; + gtk_entry_set_text (edgeSourceAnchorEntry, + [[e sourceAnchor] UTF8String]); + gtk_entry_set_text (edgeTargetAnchorEntry, + [[e targetAnchor] UTF8String]); + widget_clear_error (GTK_WIDGET (edgeSourceAnchorEntry)); + widget_clear_error (GTK_WIDGET (edgeTargetAnchorEntry)); widget_clear_error (GTK_WIDGET (edgeNodeLabelEntry)); if ([e hasEdgeNode]) { gtk_toggle_button_set_active (edgeNodeToggle, TRUE); @@ -635,6 +728,36 @@ static void edge_node_toggled_cb (GtkToggleButton *toggle, PropertiesPane *pane) [pool drain]; } +static void edge_source_anchor_changed_cb (GtkEditable *editable, PropertiesPane *pane) { + if (!gtk_widget_is_sensitive (GTK_WIDGET (editable))) { + // clearly wasn't the user editing + return; + } + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSString *newValue = gtk_editable_get_string (editable, 0, -1); + if (![pane edgeSourceAnchorEdited:newValue]) + widget_set_error (GTK_WIDGET (editable)); + + [pool drain]; +} + +static void edge_target_anchor_changed_cb (GtkEditable *editable, PropertiesPane *pane) { + if (!gtk_widget_is_sensitive (GTK_WIDGET (editable))) { + // clearly wasn't the user editing + return; + } + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSString *newValue = gtk_editable_get_string (editable, 0, -1); + if (![pane edgeTargetAnchorEdited:newValue]) + widget_set_error (GTK_WIDGET (editable)); + + [pool drain]; +} + // }}} // vim:ft=objc:ts=8:et:sts=4:sw=4:foldmethod=marker diff --git a/tikzit/src/gtk/PropertyListEditor.m b/tikzit/src/gtk/PropertyListEditor.m index 428050e..9760618 100644 --- a/tikzit/src/gtk/PropertyListEditor.m +++ b/tikzit/src/gtk/PropertyListEditor.m @@ -245,7 +245,7 @@ static void selection_changed_cb (GtkTreeSelection *selection, @implementation PropertyListEditor (Private) - (void) updatePath:(gchar*)pathStr withValue:(NSString*)newText { - if (![newText isValidTikz]) + if (![newText isValidTikzPropertyNameOrValue]) return; GtkTreeIter iter; @@ -276,7 +276,7 @@ static void selection_changed_cb (GtkTreeSelection *selection, } - (void) updatePath:(gchar*)pathStr withName:(NSString*)newText { - if (![newText isValidTikz]) + if (![newText isValidTikzPropertyNameOrValue]) return; GtkTreeIter iter; @@ -478,7 +478,7 @@ static void text_changed_cb (GtkEditable *editable, PropertyListEditor *pane) NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSString *newValue = gtk_editable_get_string (editable, 0, -1); - if (![newValue isValidTikz]) { + if (![newValue isValidTikzPropertyNameOrValue]) { widget_set_error (GTK_WIDGET (editable)); } else { widget_clear_error (GTK_WIDGET (editable)); -- cgit v1.2.3