summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Merry <alex.merry@cs.ox.ac.uk>2012-12-04 18:55:25 +0000
committerAlex Merry <dev@randomguy3.me.uk>2012-12-06 12:08:03 +0000
commitf23a2d9daa1eb62b2afac8997f1d76eb881628f7 (patch)
tree378c1cc752b7aa1fd52f35da23bce7de855bef5d
parent134de8169993f23f2c3a733a7bf96622965e7d7d (diff)
Refactor GraphInputHandler into Tools
-rw-r--r--tikzit/src/Makefile.am8
-rw-r--r--tikzit/src/gtk/Application.h17
-rw-r--r--tikzit/src/gtk/Application.m48
-rw-r--r--tikzit/src/gtk/BoundingBoxTool.h45
-rw-r--r--tikzit/src/gtk/BoundingBoxTool.m353
-rw-r--r--tikzit/src/gtk/CreateEdgeTool.h43
-rw-r--r--tikzit/src/gtk/CreateEdgeTool.m166
-rw-r--r--tikzit/src/gtk/CreateNodeTool.h39
-rw-r--r--tikzit/src/gtk/CreateNodeTool.m109
-rw-r--r--tikzit/src/gtk/DocumentContext.h27
-rw-r--r--tikzit/src/gtk/GraphEditorPanel.h48
-rw-r--r--tikzit/src/gtk/GraphEditorPanel.m170
-rw-r--r--tikzit/src/gtk/GraphInputHandler.m469
-rw-r--r--tikzit/src/gtk/GraphRenderer.h37
-rw-r--r--tikzit/src/gtk/GraphRenderer.m249
-rw-r--r--tikzit/src/gtk/HandTool.h33
-rw-r--r--tikzit/src/gtk/HandTool.m75
-rw-r--r--tikzit/src/gtk/Menu.h18
-rw-r--r--tikzit/src/gtk/Menu.m678
-rw-r--r--tikzit/src/gtk/SelectTool.h (renamed from tikzit/src/gtk/GraphInputHandler.h)56
-rw-r--r--tikzit/src/gtk/SelectTool.m366
-rw-r--r--tikzit/src/gtk/TikzDocument.h17
-rw-r--r--tikzit/src/gtk/TikzDocument.m14
-rw-r--r--tikzit/src/gtk/Tool.h42
-rw-r--r--tikzit/src/gtk/Window.h25
-rw-r--r--tikzit/src/gtk/Window.m69
-rw-r--r--tikzit/src/gtk/main.m2
-rw-r--r--tikzit/src/gtk/tzstockitems.h26
-rw-r--r--tikzit/src/gtk/tzstockitems.m64
29 files changed, 2056 insertions, 1257 deletions
diff --git a/tikzit/src/Makefile.am b/tikzit/src/Makefile.am
index 30d9815..83e0978 100644
--- a/tikzit/src/Makefile.am
+++ b/tikzit/src/Makefile.am
@@ -25,10 +25,13 @@ EDGEDECFILES = ../AH_*.png ../ED_*.png
bin_PROGRAMS = tikzit
BUILT_SOURCES = $(PARSERFILES)
tikzit_SOURCES = gtk/Application.m \
+ gtk/BoundingBoxTool.m \
gtk/CairoRenderContext.m \
gtk/ColorRGB+IntegerListStorage.m \
gtk/ColorRGB+Gtk.m \
gtk/Configuration.m \
+ gtk/CreateEdgeTool.m \
+ gtk/CreateNodeTool.m \
gtk/Edge+Render.m \
gtk/EdgeStyle+Gtk.m \
gtk/EdgeStyle+Storage.m \
@@ -36,7 +39,8 @@ tikzit_SOURCES = gtk/Application.m \
gtk/EdgeStyleSelector.m \
gtk/EdgeStylesPalette.m \
gtk/FileChooserDialog.m \
- gtk/GraphInputHandler.m \
+ gtk/HandTool.m \
+ gtk/GraphEditorPanel.m \
gtk/GraphRenderer.m \
gtk/Menu.m \
gtk/Node+Render.m \
@@ -51,6 +55,7 @@ tikzit_SOURCES = gtk/Application.m \
gtk/PropertyPane.m \
gtk/PropertyListEditor.m \
gtk/RecentManager.m \
+ gtk/SelectTool.m \
gtk/Shape+Render.m \
gtk/StyleManager+Storage.m \
gtk/StylesPane.m \
@@ -63,6 +68,7 @@ tikzit_SOURCES = gtk/Application.m \
gtk/logo.m \
gtk/mkdtemp.m \
gtk/main.m \
+ gtk/tzstockitems.m \
common/CircleShape.m \
common/ColorRGB.m \
common/DiamondShape.m \
diff --git a/tikzit/src/gtk/Application.h b/tikzit/src/gtk/Application.h
index 1967132..b05a9cd 100644
--- a/tikzit/src/gtk/Application.h
+++ b/tikzit/src/gtk/Application.h
@@ -26,6 +26,7 @@
@class StyleManager;
@class TikzDocument;
@class Window;
+@protocol Tool;
extern Application* app;
@@ -43,15 +44,16 @@ extern Application* app;
NSString *lastOpenFolder;
NSString *lastSaveAsFolder;
- // the open (active) document
- TikzDocument *activeDocument;
-
PreambleEditor *preambleWindow;
PreviewWindow *previewWindow;
SettingsDialog *settingsDialog;
// the open windows (array of Window*)
NSMutableArray *openWindows;
+
+ // tools
+ id<Tool> activeTool;
+ NSArray *tools;
}
/**
@@ -70,9 +72,14 @@ extern Application* app;
@property (readonly) Preambles *preambles;
/**
- * The document the user is currently editing
+ * The tools
+ */
+@property (readonly) NSArray *tools;
+
+/**
+ * The currently-selected tool
*/
-@property (retain) TikzDocument *activeDocument;
+@property (retain) id<Tool> activeTool;
/**
* The folder last actively chosen by a user for opening a file
diff --git a/tikzit/src/gtk/Application.m b/tikzit/src/gtk/Application.m
index 2631bb3..ac6a451 100644
--- a/tikzit/src/gtk/Application.m
+++ b/tikzit/src/gtk/Application.m
@@ -27,12 +27,19 @@
#ifdef HAVE_POPPLER
#import "SettingsDialog.h"
#endif
+#import "Shape.h"
#import "StyleManager.h"
#import "StyleManager+Storage.h"
#import "SupportDir.h"
#import "TikzDocument.h"
#import "Window.h"
+#import "BoundingBoxTool.h"
+#import "CreateNodeTool.h"
+#import "CreateEdgeTool.h"
+#import "HandTool.h"
+#import "SelectTool.h"
+
// used for args to g_mkdir_with_parents
#import "stat.h"
@@ -47,7 +54,7 @@ Application* app = nil;
@synthesize mainConfiguration=configFile;
@synthesize styleManager, preambles;
@synthesize lastOpenFolder, lastSaveAsFolder;
-@synthesize activeDocument;
+@synthesize tools;
+ (Application*) app {
if (app == nil) {
@@ -93,6 +100,15 @@ Application* app = nil;
openWindows = [[NSMutableArray alloc] init];
+ tools = [[NSArray alloc] initWithObjects:
+ [SelectTool tool],
+ [CreateNodeTool tool],
+ [CreateEdgeTool tool],
+ [BoundingBoxTool tool],
+ [HandTool tool],
+ nil];
+ activeTool = [[tools objectAtIndex:0] retain];
+
// FIXME: toolboxes
app = [self retain];
@@ -142,23 +158,25 @@ Application* app = nil;
[preambles release];
[lastOpenFolder release];
[lastSaveAsFolder release];
- [activeDocument release];
[preambleWindow release];
[previewWindow release];
[settingsDialog release];
[openWindows release];
+ [tools release];
+ [activeTool release];
[super dealloc];
}
-- (void) newWindow {
- [self newWindowWithDocument:nil];
+- (id<Tool>) activeTool { return activeTool; }
+- (void) setActiveTool:(id<Tool>)tool {
+ for (Window* window in openWindows) {
+ [window setActiveTool:tool];
+ }
}
-- (void) newWindowWithDocument:(TikzDocument*)doc {
- Window *window = (doc == nil)
- ? [Window window]
- : [Window windowWithDocument:doc];
+- (void) _addWindow:(Window*)window {
+ [window setActiveTool:activeTool];
[openWindows addObject:window];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(windowClosed:)
@@ -167,6 +185,14 @@ Application* app = nil;
// FIXME: focus?
}
+- (void) newWindow {
+ [self _addWindow:[Window window]];
+}
+
+- (void) newWindowWithDocument:(TikzDocument*)doc {
+ [self _addWindow:[Window windowWithDocument:doc]];
+}
+
- (void) quit {
NSMutableArray *unsavedDocs = [NSMutableArray arrayWithCapacity:[openWindows count]];
for (Window *window in openWindows) {
@@ -217,10 +243,6 @@ Application* app = nil;
return configFile;
}
-- (TikzDocument*) activeDocument {
- return activeDocument;
-}
-
- (void) saveConfiguration {
NSError *error = nil;
@@ -259,8 +281,6 @@ Application* app = nil;
object:window];
if ([openWindows count] == 0) {
gtk_main_quit();
- } else if ([[window document] isEqual:activeDocument]) {
- [self setActiveDocument:[[openWindows objectAtIndex:0] document]];
}
}
@end
diff --git a/tikzit/src/gtk/BoundingBoxTool.h b/tikzit/src/gtk/BoundingBoxTool.h
new file mode 100644
index 0000000..f6498b0
--- /dev/null
+++ b/tikzit/src/gtk/BoundingBoxTool.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2012 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "TZFoundation.h"
+#import "Tool.h"
+
+typedef enum {
+ NoHandle,
+ EastHandle,
+ SouthEastHandle,
+ SouthHandle,
+ SouthWestHandle,
+ WestHandle,
+ NorthWestHandle,
+ NorthHandle,
+ NorthEastHandle
+} ResizeHandle;
+
+@interface BoundingBoxTool : NSObject <Tool> {
+ GraphRenderer *renderer;
+ NSPoint dragOrigin;
+ ResizeHandle currentResizeHandle;
+ BOOL drawingNewBox;
+}
+
++ (id) tool;
+- (id) init;
+@end
+
+
+// vim:ft=objc:ts=8:et:sts=4:sw=4
diff --git a/tikzit/src/gtk/BoundingBoxTool.m b/tikzit/src/gtk/BoundingBoxTool.m
new file mode 100644
index 0000000..03bd341
--- /dev/null
+++ b/tikzit/src/gtk/BoundingBoxTool.m
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2011-2012 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "BoundingBoxTool.h"
+
+#import "GraphRenderer.h"
+#import "TikzDocument.h"
+#import "tzstockitems.h"
+
+static const float handle_size = 8.0;
+float sideHandleTop(NSRect bbox) {
+ return (NSMinY(bbox) + NSMaxY(bbox) - handle_size)/2.0f;
+}
+float tbHandleLeft(NSRect bbox) {
+ return (NSMinX(bbox) + NSMaxX(bbox) - handle_size)/2.0f;
+}
+
+@interface BoundingBoxTool (Private)
+- (NSRect) screenBoundingBox;
+- (ResizeHandle) boundingBoxResizeHandleAt:(NSPoint)p;
+- (NSRect) boundingBoxResizeHandleRect:(ResizeHandle)handle;
+- (void) setResizeCursorForHandle:(ResizeHandle)handle;
+@end
+
+@implementation BoundingBoxTool
+- (NSString*) name { return @"Bounding Box Tool"; }
+- (const gchar*) stockIcon { return TIKZIT_STOCK_BOUNDING_BOX; }
+- (NSString*) helpText { return @"Set the bounding box"; }
+- (NSString*) shortcut { return @"b"; }
+
++ (id) tool {
+ return [[[self alloc] init] autorelease];
+}
+
+- (id) init {
+ self = [super init];
+
+ if (self) {
+ currentResizeHandle = NoHandle;
+ }
+
+ return self;
+}
+
+- (void) dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [renderer release];
+
+ [super dealloc];
+}
+
+- (GraphRenderer*) activeRenderer { return renderer; }
+- (void) setActiveRenderer:(GraphRenderer*)r {
+ if (r == renderer)
+ return;
+
+ [[renderer surface] setCursor:NormalCursor];
+
+ [r retain];
+ [renderer release];
+ renderer = r;
+}
+
+- (GtkWidget*) configurationWidget { return NULL; }
+
+- (void) mousePressAt:(NSPoint)pos withButton:(MouseButton)button andMask:(InputMask)mask {
+ if (button != LeftButton)
+ return;
+
+ dragOrigin = pos;
+ currentResizeHandle = [self boundingBoxResizeHandleAt:pos];
+ [[renderer document] startChangeBoundingBox];
+ if (currentResizeHandle == NoHandle) {
+ drawingNewBox = YES;
+ [[[renderer document] graph] setBoundingBox:NSZeroRect];
+ } else {
+ drawingNewBox = NO;
+ }
+ [renderer invalidateGraph];
+}
+
+- (void) mouseMoveTo:(NSPoint)pos withButtons:(MouseButton)buttons andMask:(InputMask)mask {
+ if (!(buttons & LeftButton)) {
+ ResizeHandle handle = [self boundingBoxResizeHandleAt:pos];
+ [self setResizeCursorForHandle:handle];
+ return;
+ }
+
+ Transformer *transformer = [renderer transformer];
+ Grid *grid = [renderer grid];
+ Graph *graph = [[renderer document] graph];
+
+ if (currentResizeHandle == NoHandle) {
+ NSRect bbox = NSRectAroundPoints(
+ [grid snapScreenPoint:dragOrigin],
+ [grid snapScreenPoint:pos]
+ );
+ [graph setBoundingBox:[transformer rectFromScreen:bbox]];
+ } else {
+ NSRect bbox = [transformer rectToScreen:[graph boundingBox]];
+ NSPoint p2 = [grid snapScreenPoint:pos];
+
+ if (currentResizeHandle == NorthWestHandle ||
+ currentResizeHandle == NorthHandle ||
+ currentResizeHandle == NorthEastHandle) {
+
+ float dy = p2.y - NSMinY(bbox);
+ if (dy < bbox.size.height) {
+ bbox.origin.y += dy;
+ bbox.size.height -= dy;
+ } else {
+ bbox.origin.y = NSMaxY(bbox);
+ bbox.size.height = 0;
+ }
+
+ } else if (currentResizeHandle == SouthWestHandle ||
+ currentResizeHandle == SouthHandle ||
+ currentResizeHandle == SouthEastHandle) {
+
+ float dy = p2.y - NSMaxY(bbox);
+ if (-dy < bbox.size.height) {
+ bbox.size.height += dy;
+ } else {
+ bbox.size.height = 0;
+ }
+ }
+
+ if (currentResizeHandle == NorthWestHandle ||
+ currentResizeHandle == WestHandle ||
+ currentResizeHandle == SouthWestHandle) {
+
+ float dx = p2.x - NSMinX(bbox);
+ if (dx < bbox.size.width) {
+ bbox.origin.x += dx;
+ bbox.size.width -= dx;
+ } else {
+ bbox.origin.x = NSMaxX(bbox);
+ bbox.size.width = 0;
+ }
+
+ } else if (currentResizeHandle == NorthEastHandle ||
+ currentResizeHandle == EastHandle ||
+ currentResizeHandle == SouthEastHandle) {
+
+ float dx = p2.x - NSMaxX(bbox);
+ if (-dx < bbox.size.width) {
+ bbox.size.width += dx;
+ } else {
+ bbox.size.width = 0;
+ }
+ }
+ [graph setBoundingBox:[transformer rectFromScreen:bbox]];
+ }
+ [[renderer document] changeBoundingBoxCheckPoint];
+ [renderer invalidateGraph];
+}
+
+- (void) mouseReleaseAt:(NSPoint)pos withButton:(MouseButton)button andMask:(InputMask)mask {
+ if (button != LeftButton)
+ return;
+
+ [[renderer document] endChangeBoundingBox];
+ drawingNewBox = NO;
+ [renderer invalidateGraph];
+}
+
+- (void) renderWithContext:(id<RenderContext>)context onSurface:(id<Surface>)surface {
+ if (!drawingNewBox && [[[renderer document] graph] hasBoundingBox]) {
+ [context saveState];
+
+ [context setAntialiasMode:AntialiasDisabled];
+ [context setLineWidth:1.0];
+
+ [context startPath];
+ [context rect:[self boundingBoxResizeHandleRect:EastHandle]];
+ [context rect:[self boundingBoxResizeHandleRect:SouthEastHandle]];
+ [context rect:[self boundingBoxResizeHandleRect:SouthHandle]];
+ [context rect:[self boundingBoxResizeHandleRect:SouthWestHandle]];
+ [context rect:[self boundingBoxResizeHandleRect:WestHandle]];
+ [context rect:[self boundingBoxResizeHandleRect:NorthWestHandle]];
+ [context rect:[self boundingBoxResizeHandleRect:NorthHandle]];
+ [context rect:[self boundingBoxResizeHandleRect:NorthEastHandle]];
+ [context strokePathWithColor:MakeSolidRColor (0.5, 0.5, 0.5)];
+
+ [context restoreState];
+ }
+}
+- (void) loadConfiguration:(Configuration*)config {}
+- (void) saveConfiguration:(Configuration*)config {}
+@end
+
+@implementation BoundingBoxTool (Private)
+- (NSRect) screenBoundingBox {
+ Transformer *transformer = [[renderer surface] transformer];
+ Graph *graph = [[renderer document] graph];
+ return [transformer rectToScreen:[graph boundingBox]];
+}
+
+- (ResizeHandle) boundingBoxResizeHandleAt:(NSPoint)p {
+ NSRect bbox = [self screenBoundingBox];
+ if (p.x >= NSMaxX(bbox)) {
+ if (p.x <= NSMaxX(bbox) + handle_size) {
+ if (p.y >= NSMaxY(bbox)) {
+ if (p.y <= NSMaxY(bbox) + handle_size) {
+ return SouthEastHandle;
+ }
+ } else if (p.y <= NSMinY(bbox)) {
+ if (p.y >= NSMinY(bbox) - handle_size) {
+ return NorthEastHandle;
+ }
+ } else {
+ float eastHandleTop = sideHandleTop(bbox);
+ if (p.y >= eastHandleTop && p.y <= (eastHandleTop + handle_size)) {
+ return EastHandle;
+ }
+ }
+ }
+ } else if (p.x <= NSMinX(bbox)) {
+ if (p.x >= NSMinX(bbox) - handle_size) {
+ if (p.y >= NSMaxY(bbox)) {
+ if (p.y <= NSMaxY(bbox) + handle_size) {
+ return SouthWestHandle;
+ }
+ } else if (p.y <= NSMinY(bbox)) {
+ if (p.y >= NSMinY(bbox) - handle_size) {
+ return NorthWestHandle;
+ }
+ } else {
+ float westHandleTop = sideHandleTop(bbox);
+ if (p.y >= westHandleTop && p.y <= (westHandleTop + handle_size)) {
+ return WestHandle;
+ }
+ }
+ }
+ } else if (p.y >= NSMaxY(bbox)) {
+ if (p.y <= NSMaxY(bbox) + handle_size) {
+ float southHandleLeft = tbHandleLeft(bbox);
+ if (p.x >= southHandleLeft && p.x <= (southHandleLeft + handle_size)) {
+ return SouthHandle;
+ }
+ }
+ } else if (p.y <= NSMinY(bbox)) {
+ if (p.y >= NSMinY(bbox) - handle_size) {
+ float northHandleLeft = tbHandleLeft(bbox);
+ if (p.x >= northHandleLeft && p.x <= (northHandleLeft + handle_size)) {
+ return NorthHandle;
+ }
+ }
+ }
+ return NoHandle;
+}
+
+- (NSRect) boundingBoxResizeHandleRect:(ResizeHandle)handle {
+ Graph *graph = [[renderer document] graph];
+ if (![graph hasBoundingBox]) {
+ return NSZeroRect;
+ }
+ NSRect bbox = [self screenBoundingBox];
+ float x;
+ float y;
+ switch (handle) {
+ case NorthEastHandle:
+ case EastHandle:
+ case SouthEastHandle:
+ x = NSMaxX(bbox);
+ break;
+ case NorthWestHandle:
+ case WestHandle:
+ case SouthWestHandle:
+ x = NSMinX(bbox) - handle_size;
+ break;
+ case SouthHandle:
+ case NorthHandle:
+ x = tbHandleLeft(bbox);
+ break;
+ default:
+ return NSZeroRect;
+ }
+ switch (handle) {
+ case EastHandle:
+ case WestHandle:
+ y = sideHandleTop(bbox);
+ break;
+ case SouthEastHandle:
+ case SouthHandle:
+ case SouthWestHandle:
+ y = NSMaxY(bbox);
+ break;
+ case NorthEastHandle:
+ case NorthHandle:
+ case NorthWestHandle:
+ y = NSMinY(bbox) - handle_size;
+ break;
+ default:
+ return NSZeroRect;
+ }
+ return NSMakeRect(x, y, handle_size, handle_size);
+}
+
+- (void) setResizeCursorForHandle:(ResizeHandle)handle {
+ if (handle != currentResizeHandle) {
+ currentResizeHandle = handle;
+ Cursor c = NormalCursor;
+ switch (handle) {
+ case EastHandle:
+ c = ResizeRightCursor;
+ break;
+ case SouthEastHandle:
+ c = ResizeBottomRightCursor;
+ break;
+ case SouthHandle:
+ c = ResizeBottomCursor;
+ break;
+ case SouthWestHandle:
+ c = ResizeBottomLeftCursor;
+ break;
+ case WestHandle:
+ c = ResizeLeftCursor;
+ break;
+ case NorthWestHandle:
+ c = ResizeTopLeftCursor;
+ break;
+ case NorthHandle:
+ c = ResizeTopCursor;
+ break;
+ case NorthEastHandle:
+ c = ResizeTopRightCursor;
+ break;
+ default:
+ c = NormalCursor;
+ break;
+ }
+ [[renderer surface] setCursor:c];
+ }
+}
+@end
+
+// vim:ft=objc:ts=8:et:sts=4:sw=4
diff --git a/tikzit/src/gtk/CreateEdgeTool.h b/tikzit/src/gtk/CreateEdgeTool.h
new file mode 100644
index 0000000..cd5787b
--- /dev/null
+++ b/tikzit/src/gtk/CreateEdgeTool.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2012 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "TZFoundation.h"
+#import "Tool.h"
+
+@class EdgeStyle;
+@class EdgeStyleSelector;
+@class Node;
+@class StyleManager;
+
+@interface CreateEdgeTool : NSObject <Tool> {
+ GraphRenderer *renderer;
+ StyleManager *styleManager;
+ EdgeStyleSelector *stylePicker;
+ Node *sourceNode;
+ NSPoint sourceNodeScreenPoint;
+ NSPoint halfEdgeEnd;
+}
+
+@property (retain) StyleManager *styleManager;
+@property (retain) EdgeStyle *activeStyle;
+
++ (id) toolWithStyleManager:(StyleManager*)sm;
+- (id) initWithStyleManager:(StyleManager*)sm;
+@end
+
+
+// vim:ft=objc:ts=8:et:sts=4:sw=4
diff --git a/tikzit/src/gtk/CreateEdgeTool.m b/tikzit/src/gtk/CreateEdgeTool.m
new file mode 100644
index 0000000..4124a78
--- /dev/null
+++ b/tikzit/src/gtk/CreateEdgeTool.m
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2011-2012 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "CreateEdgeTool.h"
+
+#import "Configuration.h"
+#import "EdgeStyleSelector.h"
+#import "GraphRenderer.h"
+#import "TikzDocument.h"
+#import "tzstockitems.h"
+
+@implementation CreateEdgeTool
+- (NSString*) name { return @"Create Edge Tool"; }
+- (const gchar*) stockIcon { return TIKZIT_STOCK_CREATE_EDGE; }
+- (NSString*) helpText { return @"Create new edges"; }
+- (NSString*) shortcut { return @"e"; }
+@synthesize activeRenderer=renderer;
+@synthesize styleManager;
+
++ (id) tool {
+ return [[[self alloc] init] autorelease];
+}
+
++ (id) toolWithStyleManager:(StyleManager*)sm {
+ return [[[self alloc] initWithStyleManager:sm] autorelease];
+}
+
+- (id) init {
+ return [self initWithStyleManager:[StyleManager manager]];
+}
+
+- (id) initWithStyleManager:(StyleManager*)sm {
+ self = [super init];
+
+ if (self) {
+ styleManager = [sm retain];
+ stylePicker = [[EdgeStyleSelector alloc] initWithStyleManager:sm];
+ }
+
+ return self;
+}
+
+- (void) dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [renderer release];
+ [styleManager release];
+ [stylePicker release];
+ [sourceNode release];
+
+ [super dealloc];
+}
+
+- (GtkWidget*) configurationWidget {
+ return [stylePicker widget];
+}
+
+- (EdgeStyle*) activeStyle {
+ return [stylePicker selectedStyle];
+}
+
+- (void) setActiveStyle:(EdgeStyle*)style {
+ return [stylePicker setSelectedStyle:style];
+}
+
+- (void) invalidateHalfEdge {
+ NSRect invRect = NSRectAroundPoints(sourceNodeScreenPoint, halfEdgeEnd);
+ [renderer invalidateRect:NSInsetRect (invRect, -2.0f, -2.0f)];
+}
+
+- (void) mousePressAt:(NSPoint)pos withButton:(MouseButton)button andMask:(InputMask)mask {
+ if (button != LeftButton)
+ return;
+
+ sourceNode = [renderer anyNodeAt:pos];
+ if (sourceNode != nil) {
+ Transformer *transformer = [[renderer surface] transformer];
+ sourceNodeScreenPoint = [transformer toScreen:[sourceNode point]];
+ halfEdgeEnd = pos;
+ [renderer setNode:sourceNode highlighted:YES];
+ }
+}
+
+- (void) mouseMoveTo:(NSPoint)pos withButtons:(MouseButton)buttons andMask:(InputMask)mask {
+ if (!(buttons & LeftButton))
+ return;
+ if (sourceNode == nil)
+ return;
+
+ [self invalidateHalfEdge];
+
+ [renderer clearHighlightedNodes];
+ [renderer setNode:sourceNode highlighted:YES];
+ halfEdgeEnd = pos;
+ Node *targ = [renderer anyNodeAt:pos];
+ if (targ != nil) {
+ [renderer setNode:targ highlighted:YES];
+ }
+
+ [self invalidateHalfEdge];
+}
+
+- (void) mouseReleaseAt:(NSPoint)pos withButton:(MouseButton)button andMask:(InputMask)mask {
+ if (button != LeftButton)
+ return;
+ if (sourceNode == nil)
+ return;
+
+ [renderer clearHighlightedNodes];
+ [self invalidateHalfEdge];
+
+ Node *targ = [renderer anyNodeAt:pos];
+ if (targ != nil) {
+ Edge *edge = [Edge edgeWithSource:sourceNode andTarget:targ];
+ [edge setStyle:[self activeStyle]];
+ [[renderer document] addEdge:edge];
+ [renderer invalidateEdge:edge];
+ }
+
+ sourceNode = nil;
+}
+
+- (void) renderWithContext:(id<RenderContext>)context onSurface:(id<Surface>)surface {
+ if (sourceNode == nil) {
+ return;
+ }
+ [context saveState];
+
+ [context setLineWidth:1.0];
+ [context startPath];
+ [context moveTo:sourceNodeScreenPoint];
+ [context lineTo:halfEdgeEnd];
+ [context strokePathWithColor:MakeRColor (0, 0, 0, 0.5)];
+
+ [context restoreState];
+}
+
+- (void) loadConfiguration:(Configuration*)config {
+ NSString *styleName = [config stringEntry:@"ActiveStyle"
+ inGroup:@"CreateEdgeTool"
+ withDefault:nil];
+ [self setActiveStyle:[styleManager edgeStyleForName:styleName]];
+}
+
+- (void) saveConfiguration:(Configuration*)config {
+ [config setStringEntry:@"ActiveStyle"
+ inGroup:@"CreateEdgeTool"
+ value:[[self activeStyle] name]];
+}
+@end
+
+// vim:ft=objc:ts=8:et:sts=4:sw=4
diff --git a/tikzit/src/gtk/CreateNodeTool.h b/tikzit/src/gtk/CreateNodeTool.h
new file mode 100644
index 0000000..b2205e3
--- /dev/null
+++ b/tikzit/src/gtk/CreateNodeTool.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2012 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "TZFoundation.h"
+#import "Tool.h"
+
+@class NodeStyle;
+@class NodeStyleSelector;
+@class StyleManager;
+
+@interface CreateNodeTool : NSObject <Tool> {
+ GraphRenderer *renderer;
+ StyleManager *styleManager;
+ NodeStyleSelector *stylePicker;
+}
+
+@property (retain) StyleManager *styleManager;
+@property (retain) NodeStyle *activeStyle;
+
++ (id) toolWithStyleManager:(StyleManager*)sm;
+- (id) initWithStyleManager:(StyleManager*)sm;
+@end
+
+
+// vim:ft=objc:ts=8:et:sts=4:sw=4
diff --git a/tikzit/src/gtk/CreateNodeTool.m b/tikzit/src/gtk/CreateNodeTool.m
new file mode 100644
index 0000000..581ace3
--- /dev/null
+++ b/tikzit/src/gtk/CreateNodeTool.m
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2011-2012 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "CreateNodeTool.h"
+
+#import "Configuration.h"
+#import "GraphRenderer.h"
+#import "NodeStyleSelector.h"
+#import "TikzDocument.h"
+#import "tzstockitems.h"
+
+@implementation CreateNodeTool
+- (NSString*) name { return @"Create Node Tool"; }
+- (const gchar*) stockIcon { return TIKZIT_STOCK_CREATE_NODE; }
+- (NSString*) helpText { return @"Create new nodes"; }
+- (NSString*) shortcut { return @"n"; }
+@synthesize activeRenderer=renderer;
+@synthesize styleManager;
+
++ (id) tool {
+ return [[[self alloc] init] autorelease];
+}
+
++ (id) toolWithStyleManager:(StyleManager*)sm {
+ return [[[self alloc] initWithStyleManager:sm] autorelease];
+}
+
+- (id) init {
+ return [self initWithStyleManager:[StyleManager manager]];
+}
+
+- (id) initWithStyleManager:(StyleManager*)sm {
+ self = [super init];
+
+ if (self) {
+ styleManager = [sm retain];
+ stylePicker = [[NodeStyleSelector alloc] initWithStyleManager:sm];
+ }
+
+ return self;
+}
+
+- (void) dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [renderer release];
+ [styleManager release];
+ [stylePicker release];
+
+ [super dealloc];
+}
+
+- (GtkWidget*) configurationWidget {
+ return [stylePicker widget];
+}
+
+- (NodeStyle*) activeStyle {
+ return [stylePicker selectedStyle];
+}
+
+- (void) setActiveStyle:(NodeStyle*)style {
+ return [stylePicker setSelectedStyle:style];
+}
+
+// FIXME: create node on press, and drag it around?
+- (void) mousePressAt:(NSPoint)pos withButton:(MouseButton)button andMask:(InputMask)mask {}
+
+- (void) mouseReleaseAt:(NSPoint)pos withButton:(MouseButton)button andMask:(InputMask)mask {
+ if (button != LeftButton)
+ return;
+
+ Transformer *transformer = [renderer transformer];
+ NSPoint nodePoint = [transformer fromScreen:[[renderer grid] snapScreenPoint:pos]];
+ Node *node = [Node nodeWithPoint:nodePoint];
+ [node setStyle:[self activeStyle]];
+ [[renderer document] addNode:node];
+}
+
+- (void) renderWithContext:(id<RenderContext>)context onSurface:(id<Surface>)surface {}
+
+- (void) loadConfiguration:(Configuration*)config {
+ NSString *styleName = [config stringEntry:@"ActiveStyle"
+ inGroup:@"CreateNodeTool"
+ withDefault:nil];
+ [self setActiveStyle:[styleManager nodeStyleForName:styleName]];
+}
+
+- (void) saveConfiguration:(Configuration*)config {
+ [config setStringEntry:@"ActiveStyle"
+ inGroup:@"CreateNodeTool"
+ value:[[self activeStyle] name]];
+}
+@end
+
+// vim:ft=objc:ts=8:et:sts=4:sw=4
diff --git a/tikzit/src/gtk/DocumentContext.h b/tikzit/src/gtk/DocumentContext.h
new file mode 100644
index 0000000..e4c1065
--- /dev/null
+++ b/tikzit/src/gtk/DocumentContext.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2012 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "TZFoundation.h"
+
+@class TikzDocument;
+
+@interface DocumentContext {
+ TikzDocument *document;
+ GraphRenderer *renderSurface;
+}
+
+// vim:ft=objc:ts=8:et:sts=4:sw=4
diff --git a/tikzit/src/gtk/GraphEditorPanel.h b/tikzit/src/gtk/GraphEditorPanel.h
new file mode 100644
index 0000000..857b0ba
--- /dev/null
+++ b/tikzit/src/gtk/GraphEditorPanel.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2012 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "TZFoundation.h"
+#import "Tool.h"
+#import <gtk/gtk.h>
+
+@class GraphInputHandler;
+@class GraphRenderer;
+@class TikzDocument;
+@class WidgetSurface;
+
+@interface GraphEditorPanel : NSObject {
+ GraphRenderer *renderer;
+ WidgetSurface *surface;
+ GraphInputHandler *inputHandler;
+ id<Tool> tool;
+}
+@property (retain) TikzDocument *document;
+@property (readonly) GtkWidget *widget;
+@property (retain) id<Tool> activeTool;
+
+- (id) init;
+- (id) initWithDocument:(TikzDocument*)document;
+- (void) grabTool;
+- (void) zoomInAboutPoint:(NSPoint)pos;
+- (void) zoomOutAboutPoint:(NSPoint)pos;
+- (void) zoomIn;
+- (void) zoomOut;
+- (void) zoomReset;
+
+@end
+
+// vim:ft=objc:ts=8:et:sts=4:sw=4
diff --git a/tikzit/src/gtk/GraphEditorPanel.m b/tikzit/src/gtk/GraphEditorPanel.m
new file mode 100644
index 0000000..6d98fa5
--- /dev/null
+++ b/tikzit/src/gtk/GraphEditorPanel.m
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2012 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "GraphEditorPanel.h"
+
+#import "GraphRenderer.h"
+#import "HandTool.h"
+#import "InputDelegate.h"
+#import "TikzDocument.h"
+#import "WidgetSurface.h"
+
+@class GraphRenderer;
+@class WidgetSurface;
+
+/**
+ * Mostly just a multiplexer
+ */
+@interface GraphInputHandler : NSObject<InputDelegate> {
+ GraphEditorPanel *panel;
+}
+- (id) initForPanel:(GraphEditorPanel*)p;
+@end
+
+@implementation GraphEditorPanel
+- (id) init {
+ return [self initWithDocument:nil];
+}
+- (id) initWithDocument:(TikzDocument*)document {
+ self = [super init];
+ if (self) {
+ surface = [[WidgetSurface alloc] init];
+ [surface setDefaultScale:50.0f];
+ [surface setKeepCentered:YES];
+ [surface setGrabsFocusOnClick:YES];
+ renderer = [[GraphRenderer alloc] initWithSurface:surface document:document];
+
+ inputHandler = [[GraphInputHandler alloc] initForPanel:self];
+ [surface setInputDelegate:inputHandler];
+ }
+ return self;
+}
+
+- (void) dealloc {
+ [renderer release];
+ [surface release];
+ [inputHandler release];
+
+ [super dealloc];
+}
+
+- (TikzDocument*) document {
+ return [renderer document];
+}
+- (void) setDocument:(TikzDocument*)doc {
+ [renderer setDocument:doc];
+}
+- (GtkWidget*) widget {
+ return [surface widget];
+}
+- (id<Tool>) activeTool {
+ return tool;
+}
+- (void) setActiveTool:(id<Tool>)t {
+ if (t == tool)
+ return;
+
+ BOOL hadOldTool = ([tool activeRenderer] == renderer);
+
+ id oldTool = tool;
+ tool = [t retain];
+ [oldTool release];
+
+ if (hadOldTool) {
+ [self grabTool];
+ }
+}
+
+- (void) grabTool {
+ if ([tool activeRenderer] != renderer) {
+ [[tool activeRenderer] setPostRenderer:nil];
+ [tool setActiveRenderer:renderer];
+ }
+ [renderer setPostRenderer:tool];
+}
+
+- (void) zoomInAboutPoint:(NSPoint)pos { [surface zoomInAboutPoint:pos]; }
+- (void) zoomOutAboutPoint:(NSPoint)pos { [surface zoomOutAboutPoint:pos]; }
+- (void) zoomIn { [surface zoomIn]; }
+- (void) zoomOut { [surface zoomOut]; }
+- (void) zoomReset { [surface zoomReset]; }
+
+@end
+
+@implementation GraphInputHandler
+- (id) initForPanel:(GraphEditorPanel*)p {
+ self = [super init];
+ if (self) {
+ // NB: no retention!
+ panel = p;
+ }
+ return self;
+}
+- (id) init {
+ [self dealloc];
+ return nil;
+}
+
+// FIXME: use a local copy of HandTool to implement CTRL-dragging
+- (void) mousePressAt:(NSPoint)pos withButton:(MouseButton)button andMask:(InputMask)mask {
+ [panel grabTool];
+ id<Tool> tool = [panel activeTool];
+ if ([tool respondsToSelector:@selector(mousePressAt:withButton:andMask:)]) {
+ [tool mousePressAt:pos withButton:button andMask:mask];
+ }
+}
+
+- (void) mouseDoubleClickAt:(NSPoint)pos withButton:(MouseButton)button andMask:(InputMask)mask {
+ [panel grabTool];
+ id<Tool> tool = [panel activeTool];
+ if ([tool respondsToSelector:@selector(mouseDoubleClickAt:withButton:andMask:)]) {
+ [tool mouseDoubleClickAt:pos withButton:button andMask:mask];
+ }
+}
+
+- (void) mouseReleaseAt:(NSPoint)pos withButton:(MouseButton)button andMask:(InputMask)mask {
+ [panel grabTool];
+ id<Tool> tool = [panel activeTool];
+ if ([tool respondsToSelector:@selector(mouseReleaseAt:withButton:andMask:)]) {
+ [tool mouseReleaseAt:pos withButton:button andMask:mask];
+ }
+}
+
+- (void) mouseMoveTo:(NSPoint)pos withButtons:(MouseButton)buttons andMask:(InputMask)mask {
+ [panel grabTool];
+ id<Tool> tool = [panel activeTool];
+ if ([tool respondsToSelector:@selector(mouseMoveTo:withButtons:andMask:)]) {
+ [tool mouseMoveTo:pos withButtons:buttons andMask:mask];
+ }
+}
+
+- (void) mouseScrolledAt:(NSPoint)pos inDirection:(ScrollDirection)dir withMask:(InputMask)mask {
+ [panel grabTool];
+ id<Tool> tool = [panel activeTool];
+ if (mask == ControlMask) {
+ if (dir == ScrollUp) {
+ [panel zoomInAboutPoint:pos];
+ } else if (dir == ScrollDown) {
+ [panel zoomOutAboutPoint:pos];
+ }
+ } else if ([tool respondsToSelector:@selector(mouseScrolledAt:inDirection:withMask:)]) {
+ [tool mouseScrolledAt:pos inDirection:dir withMask:mask];
+ }
+}
+@end
+
+// vim:ft=objc:ts=8:et:sts=4:sw=4
diff --git a/tikzit/src/gtk/GraphInputHandler.m b/tikzit/src/gtk/GraphInputHandler.m
deleted file mode 100644
index 788dfeb..0000000
--- a/tikzit/src/gtk/GraphInputHandler.m
+++ /dev/null
@@ -1,469 +0,0 @@
-/*
- * Copyright 2011 Alex Merry <alex.merry@kdemail.net>
- * Copyright 2010 Chris Heunen
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#import "GraphInputHandler.h"
-#import <gdk/gdkkeysyms.h>
-#import "Edge+Render.h"
-
-static const InputMask unionSelectMask = ShiftMask;
-
-@implementation GraphInputHandler
-- (id) initWithGraphRenderer:(GraphRenderer*)r {
- self = [super init];
-
- if (self) {
- renderer = r;
- mode = SelectMode;
- state = QuietState;
- edgeFuzz = 3.0f;
- leaderNode = nil;
- modifyEdge = nil;
- selectionBoxContents = [[NSMutableSet alloc] initWithCapacity:10];
- currentResizeHandle = NoHandle;
- // FIXME: listen only to the doc's PickSupport
- // (need to track document changes)
- }
-
- return self;
-}
-
-- (void) dealloc {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- [selectionBoxContents release];
-
- [super dealloc];
-}
-
-- (TikzDocument*) doc {
- return [renderer document];
-}
-
-- (void) deselectAllNodes {
- [[[self doc] pickSupport] deselectAllNodes];
-}
-
-- (void) deselectAllEdges {
- [[[self doc] pickSupport] deselectAllEdges];
-}
-
-- (void) deselectAll {
- [[[self doc] pickSupport] deselectAllNodes];
- [[[self doc] pickSupport] deselectAllEdges];
-}
-
-- (void) shiftNodesByMovingLeader:(Node*)leader to:(NSPoint)to {
- Transformer *transformer = [renderer transformer];
-
- NSPoint from = [transformer toScreen:[leader point]];
- //to = [[renderer grid] snapScreenPoint:to];
- float dx = to.x - from.x;
- float dy = to.y - from.y;
-
- for (Node *node in [[[self doc] pickSupport] selectedNodes]) {
- NSPoint p = [transformer toScreen:[node point]];
- p.x += dx;
- p.y += dy;
- p = [[renderer grid] snapScreenPoint:p];
- [node setPoint:[transformer fromScreen:p]];
- }
-}
-
-- (float) edgeFuzz {
- return edgeFuzz;
-}
-
-- (void) setEdgeFuzz:(float)fuzz {
- edgeFuzz = fuzz;
-}
-
-- (InputMode) mode {
- return mode;
-}
-
-- (void) resetState {
- state = QuietState;
-}
-
-- (void) setMode:(InputMode)m {
- if (mode != m) {
- if (mode == BoundingBoxMode) {
- [renderer setBoundingBoxHandlesShown:NO];
- [[renderer surface] setCursor:NormalCursor];
- }
- mode = m;
- [self deselectAll];
- if (mode == BoundingBoxMode) {
- [renderer setBoundingBoxHandlesShown:YES];
- }
- }
-}
-
-- (BOOL) circleWithCenter:(NSPoint)c andRadius:(float)r containsPoint:(NSPoint)p {
- return (NSDistanceBetweenPoints(c, p) <= r);
-}
-
-- (void) lookForControlPointAt:(NSPoint)pos {
- const float cpr = [Edge controlPointRadius];
- for (Edge *e in [[[self doc] pickSupport] selectedEdges]) {
- NSPoint cp1 = [[renderer transformer] toScreen:[e cp1]];
- if ([self circleWithCenter:cp1 andRadius:cpr containsPoint:pos]) {
- state = DragEdgeControlPoint1;
- modifyEdge = e;
- [[self doc] startModifyEdge:e];
- return;
- }
- NSPoint cp2 = [[renderer transformer] toScreen:[e cp2]];
- if ([self circleWithCenter:cp2 andRadius:cpr containsPoint:pos]) {
- state = DragEdgeControlPoint2;
- modifyEdge = e;
- [[self doc] startModifyEdge:e];
- return;
- }
- }
-}
-
-- (void) mousePressAt:(NSPoint)pos withButton:(MouseButton)button andMask:(InputMask)mask {
- if (button != LeftButton)
- return;
-
- dragOrigin = pos;
-
- // we should already be in a quiet state, but no harm in making sure
- state = QuietState;
-
- if (mode == HandMode || mask == ControlMask) {
- state = CanvasDragState;
- oldOrigin = [[renderer transformer] origin];
- } else if (mode == DrawEdgeMode) {
- leaderNode = [renderer anyNodeAt:pos];
- if (leaderNode != nil) {
- state = EdgeDragState;
- }
- } else if (mode == BoundingBoxMode) {
- state = BoundingBoxState;
- currentResizeHandle = [renderer boundingBoxResizeHandleAt:pos];
- [[self doc] startChangeBoundingBox];
- if (currentResizeHandle == NoHandle) {
- [[[self doc] graph] setBoundingBox:NSZeroRect];
- [renderer setBoundingBoxHandlesShown:NO];
- }
- } else if (mode == SelectMode) {
- modifyEdge = nil;
- [self lookForControlPointAt:pos];
-
- if (modifyEdge == nil) {
- // we didn't find a control point
-
- BOOL unionSelect = (mask & unionSelectMask);
-
- leaderNode = [renderer anyNodeAt:pos];
- // if we hit a node, deselect other nodes (if Shift is up) and go to move mode
- if (leaderNode != nil) {
- BOOL alreadySelected = [[self doc] isNodeSelected:leaderNode];
- if (!unionSelect && !alreadySelected) {
- [self deselectAllEdges];
- [self deselectAllNodes];
- }
- if (unionSelect && alreadySelected) {
- state = ToggleSelectState;
- } else {
- [[[self doc] pickSupport] selectNode:leaderNode];
- state = MoveSelectedNodesState;
- oldLeaderPos = [leaderNode point];
- [[self doc] startShiftNodes:[[[self doc] pickSupport] selectedNodes]];
- }
- }
-
- // if mouse did not hit a node, check if mouse hit an edge
- if (leaderNode == nil) {
- Edge *edge = [renderer anyEdgeAt:pos withFuzz:edgeFuzz];
- if (edge != nil) {
- BOOL alreadySelected = [[self doc] isEdgeSelected:edge];
- if (!unionSelect) {
- [self deselectAll];
- }
- if (unionSelect && alreadySelected) {
- [[[self doc] pickSupport] deselectEdge:edge];
- } else {
- [[[self doc] pickSupport] selectEdge:edge];
- }
- } else {
- // if mouse did not hit anything, put us in box mode
- if (!unionSelect) {
- [self deselectAll];
- }
- [selectionBoxContents removeAllObjects];
- state = SelectBoxState;
- }
- }
- }
- }
-}
-
-- (void) mouseReleaseAt:(NSPoint)pos withButton:(MouseButton)button andMask:(InputMask)mask {
- if (button != LeftButton)
- return;
-
- if (state == SelectBoxState) {
- BOOL shouldDeselect = !(mask & unionSelectMask);
- if (shouldDeselect) {
- [self deselectAllEdges];
- }
- [[[self doc] pickSupport] selectAllNodes:selectionBoxContents
- replacingSelection:shouldDeselect];
- [renderer clearSelectionBox];
- } else if (state == ToggleSelectState) {
- [[[self doc] pickSupport] deselectNode:leaderNode];
- leaderNode = nil;
- } else if (state == MoveSelectedNodesState) {
- if (NSEqualPoints (oldLeaderPos, [leaderNode point])) {
- [[self doc] cancelShiftNodes];
- } else {
- [[self doc] endShiftNodes];
- }
- leaderNode = nil;
- } else if (state == DragEdgeControlPoint1 || state == DragEdgeControlPoint2) {
- // FIXME: check if there was any real change
- [[self doc] endModifyEdge];
- } else if (state == EdgeDragState) {
- [renderer clearHalfEdge];
- Node *targ = [renderer anyNodeAt:pos];
- if (targ != nil) {
- [[self doc] addEdgeFrom:leaderNode to:targ];
- }
- } else if (state == QuietState && mode == CreateNodeMode) {
- Transformer *transformer = [renderer transformer];
- NSPoint nodePoint = [transformer fromScreen:[[renderer grid] snapScreenPoint:pos]];
- [[self doc] addNodeAt:nodePoint];
- } else if (state == BoundingBoxState) {
- [[self doc] endChangeBoundingBox];
- [renderer setBoundingBoxHandlesShown:YES];
- }
-
- state = QuietState;
-}
-
-- (void) mouseDoubleClickAt:(NSPoint)pos withButton:(MouseButton)button andMask:(InputMask)mask {
- if (button != LeftButton)
- return;
-
- if (mode != SelectMode) {
- return;
- }
- if (state != QuietState) {
- return;
- }
- // convert bend mode on edge under mouse cursor
- Edge *edge = [renderer anyEdgeAt:pos withFuzz:edgeFuzz];
- if (edge != nil) {
- [[self doc] startModifyEdge:edge];
- if ([edge bendMode]==EdgeBendModeBasic) {
- [edge convertBendToAngles];
- [edge setBendMode:EdgeBendModeInOut];
- } else {
- [edge setBendMode:EdgeBendModeBasic];
- }
- [[self doc] endModifyEdge];
-
- [self deselectAllEdges];
- [[[self doc] pickSupport] selectEdge:edge];
- }
-}
-
-- (void) mouseMoveTo:(NSPoint)pos withButtons:(MouseButton)buttons andMask:(InputMask)mask {
- if (!(buttons & LeftButton))
- return;
-
- Transformer *transformer = [renderer transformer];
-
- if (state == ToggleSelectState) {
- state = MoveSelectedNodesState;
- oldLeaderPos = [leaderNode point];
- [[self doc] startShiftNodes:[[[self doc] pickSupport] selectedNodes]];
- }
-
- if (state == SelectBoxState) {
- NSRect selectionBox = NSRectAroundPoints(dragOrigin, pos);
- [renderer setSelectionBox:selectionBox];
-
- NSEnumerator *enumerator = [[self doc] nodeEnumerator];
- Node *node;
- while ((node = [enumerator nextObject]) != nil) {
- NSPoint nodePos = [transformer toScreen:[node point]];
- if (NSPointInRect(nodePos, selectionBox)) {
- if (![selectionBoxContents member:node]) {
- [selectionBoxContents addObject:node];
- [renderer invalidateNode:node];
- }
- } else {
- if ([selectionBoxContents member:node]) {
- [selectionBoxContents removeObject:node];
- [renderer invalidateNode:node];
- }
- }
- }
- } else if (state == MoveSelectedNodesState) {
- if (leaderNode != nil) {
- [self shiftNodesByMovingLeader:leaderNode to:pos];
- NSPoint shiftSoFar;
- shiftSoFar.x = [leaderNode point].x - oldLeaderPos.x;
- shiftSoFar.y = [leaderNode point].y - oldLeaderPos.y;
- [[self doc] shiftNodesUpdate:shiftSoFar];
- }
- } else if (state == DragEdgeControlPoint1 || state == DragEdgeControlPoint2) {
- // invalidate once before we start changing it: we may be shrinking
- // the control circles
- [[self doc] modifyEdgeCheckPoint];
- if (state == DragEdgeControlPoint1) {
- [modifyEdge moveCp1To:[transformer fromScreen:pos]
- withWeightCourseness:0.1f
- andBendCourseness:15
- forceLinkControlPoints:(mask & ControlMask)];
- } else {
- [modifyEdge moveCp2To:[transformer fromScreen:pos]
- withWeightCourseness:0.1f
- andBendCourseness:15
- forceLinkControlPoints:(mask & ControlMask)];
- }
- [[self doc] modifyEdgeCheckPoint];
- } else if (state == EdgeDragState) {
- [renderer setHalfEdgeFrom:leaderNode to:pos];
- } else if (state == BoundingBoxState) {
- Grid *grid = [renderer grid];
- Graph *graph = [[self doc] graph];
- if (currentResizeHandle == NoHandle) {
- NSRect bbox = NSRectAroundPoints(
- [grid snapScreenPoint:dragOrigin],
- [grid snapScreenPoint:pos]
- );
- [graph setBoundingBox:[transformer rectFromScreen:bbox]];
- } else {
- NSRect bbox = [transformer rectToScreen:[graph boundingBox]];
- NSPoint p2 = [grid snapScreenPoint:pos];
-
- if (currentResizeHandle == NorthWestHandle ||
- currentResizeHandle == NorthHandle ||
- currentResizeHandle == NorthEastHandle) {
-
- float dy = p2.y - NSMinY(bbox);
- if (dy < bbox.size.height) {
- bbox.origin.y += dy;
- bbox.size.height -= dy;
- } else {
- bbox.origin.y = NSMaxY(bbox);
- bbox.size.height = 0;
- }
-
- } else if (currentResizeHandle == SouthWestHandle ||
- currentResizeHandle == SouthHandle ||
- currentResizeHandle == SouthEastHandle) {
-
- float dy = p2.y - NSMaxY(bbox);
- if (-dy < bbox.size.height) {
- bbox.size.height += dy;
- } else {
- bbox.size.height = 0;
- }
- }
-
- if (currentResizeHandle == NorthWestHandle ||
- currentResizeHandle == WestHandle ||
- currentResizeHandle == SouthWestHandle) {
-
- float dx = p2.x - NSMinX(bbox);
- if (dx < bbox.size.width) {
- bbox.origin.x += dx;
- bbox.size.width -= dx;
- } else {
- bbox.origin.x = NSMaxX(bbox);
- bbox.size.width = 0;
- }
-
- } else if (currentResizeHandle == NorthEastHandle ||
- currentResizeHandle == EastHandle ||
- currentResizeHandle == SouthEastHandle) {
-
- float dx = p2.x - NSMaxX(bbox);
- if (-dx < bbox.size.width) {
- bbox.size.width += dx;
- } else {
- bbox.size.width = 0;
- }
- }
- [graph setBoundingBox:[transformer rectFromScreen:bbox]];
- }
- [[self doc] changeBoundingBoxCheckPoint];
- } else if (state == CanvasDragState) {
- NSPoint newOrigin = oldOrigin;
- newOrigin.x += pos.x - dragOrigin.x;
- newOrigin.y += pos.y - dragOrigin.y;
- [[renderer transformer] setOrigin:newOrigin];
- [renderer invalidateGraph];
- }
- if (mode == BoundingBoxMode && state != BoundingBoxState) {
- ResizeHandle handle = [renderer boundingBoxResizeHandleAt:pos];
- if (handle != currentResizeHandle) {
- currentResizeHandle = handle;
- Cursor c = NormalCursor;
- switch (handle) {
- case EastHandle:
- c = ResizeRightCursor;
- break;
- case SouthEastHandle:
- c = ResizeBottomRightCursor;
- break;
- case SouthHandle:
- c = ResizeBottomCursor;
- break;
- case SouthWestHandle:
- c = ResizeBottomLeftCursor;
- break;
- case WestHandle:
- c = ResizeLeftCursor;
- break;
- case NorthWestHandle:
- c = ResizeTopLeftCursor;
- break;
- case NorthHandle:
- c = ResizeTopCursor;
- break;
- case NorthEastHandle:
- c = ResizeTopRightCursor;
- break;
- default:
- c = NormalCursor;
- break;
- }
- [[renderer surface] setCursor:c];
- }
- }
-}
-
-- (void) mouseScrolledAt:(NSPoint)pos inDirection:(ScrollDirection)dir withMask:(InputMask)mask {
- if (mask == ControlMask) {
- if (dir == ScrollUp) {
- [[renderer surface] zoomInAboutPoint:pos];
- } else if (dir == ScrollDown) {
- [[renderer surface] zoomOutAboutPoint:pos];
- }
- }
-}
-
-@end
-
-// vim:ft=objc:ts=8:et:sts=4:sw=4
diff --git a/tikzit/src/gtk/GraphRenderer.h b/tikzit/src/gtk/GraphRenderer.h
index 4609766..730d606 100644
--- a/tikzit/src/gtk/GraphRenderer.h
+++ b/tikzit/src/gtk/GraphRenderer.h
@@ -27,32 +27,20 @@
// protocols
#import "Surface.h"
-typedef enum {
- NoHandle,
- EastHandle,
- SouthEastHandle,
- SouthHandle,
- SouthWestHandle,
- WestHandle,
- NorthWestHandle,
- NorthHandle,
- NorthEastHandle
-} ResizeHandle;
-
@interface GraphRenderer: NSObject <RenderDelegate> {
TikzDocument *doc;
NSObject<Surface> *surface;
Grid *grid;
- NSRect selectionBox;
- Node *halfEdgeOrigin;
- NSPoint halfEdgeOriginPoint;
- NSPoint halfEdgeEnd;
- BOOL showBoundingBoxHandles;
+ NSMutableSet *highlightedNodes;
+ id<RenderDelegate> postRenderer;
}
+@property (retain) id<RenderDelegate> postRenderer;
+
- (id) initWithSurface:(NSObject <Surface> *)surface;
- (id) initWithSurface:(NSObject <Surface> *)surface document:(TikzDocument*)document;
- (void) renderWithContext:(id<RenderContext>)context;
+- (void) invalidateRect:(NSRect)rect;
- (void) invalidateGraph;
- (void) invalidateNode:(Node*)node;
- (void) invalidateEdge:(Edge*)edge;
@@ -87,18 +75,9 @@ typedef enum {
- (TikzDocument*) document;
- (void) setDocument:(TikzDocument*)document;
-- (NSRect) selectionBox;
-- (void) setSelectionBox:(NSRect)box;
-- (void) clearSelectionBox;
-
-- (void) setHalfEdgeFrom:(Node*)origin to:(NSPoint)end;
-- (void) clearHalfEdge;
-
-- (BOOL) boundingBoxHandlesShown;
-- (void) setBoundingBoxHandlesShown:(BOOL)shown;
-
-- (ResizeHandle) boundingBoxResizeHandleAt:(NSPoint)point;
-- (NSRect) boundingBoxResizeHandleRect:(ResizeHandle)handle;
+- (BOOL) isNodeHighlighted:(Node*)node;
+- (void) setNode:(Node*)node highlighted:(BOOL)h;
+- (void) clearHighlightedNodes;
@end
diff --git a/tikzit/src/gtk/GraphRenderer.m b/tikzit/src/gtk/GraphRenderer.m
index 3fc14d6..76fe551 100644
--- a/tikzit/src/gtk/GraphRenderer.m
+++ b/tikzit/src/gtk/GraphRenderer.m
@@ -20,24 +20,11 @@
#import "Edge+Render.h"
#import "Node+Render.h"
-static const float size = 8.0;
-
-float sideHandleTop(NSRect bbox) {
- return (NSMinY(bbox) + NSMaxY(bbox) - size)/2.0f;
-}
-
-float tbHandleLeft(NSRect bbox) {
- return (NSMinX(bbox) + NSMaxX(bbox) - size)/2.0f;
-}
void graph_renderer_expose_event(GtkWidget *widget, GdkEventExpose *event);
@interface GraphRenderer (Private)
-- (BOOL) selectionBoxContainsNode:(Node*)node;
-- (BOOL) halfEdgeIncludesNode:(Node*)node;
- (enum NodeState) nodeState:(Node*)node;
- (void) renderBoundingBoxWithContext:(id<RenderContext>)context;
-- (void) renderSelectionBoxWithContext:(id<RenderContext>)context;
-- (void) renderImpendingEdgeWithContext:(id<RenderContext>)context;
- (void) nodeNeedsRefreshing:(NSNotification*)notification;
- (void) edgeNeedsRefreshing:(NSNotification*)notification;
- (void) graphNeedsRefreshing:(NSNotification*)notification;
@@ -53,9 +40,8 @@ void graph_renderer_expose_event(GtkWidget *widget, GdkEventExpose *event);
if (self) {
surface = [s retain];
- doc = nil;
grid = [[Grid alloc] initWithSpacing:1.0f subdivisions:4 transformer:[s transformer]];
- halfEdgeOrigin = nil;
+ highlightedNodes = [[NSMutableSet alloc] initWithCapacity:10];
[surface setRenderDelegate:self];
[[NSNotificationCenter defaultCenter] addObserver:self
@@ -85,11 +71,26 @@ void graph_renderer_expose_event(GtkWidget *widget, GdkEventExpose *event);
[[NSNotificationCenter defaultCenter] removeObserver:self];
[doc release];
[grid release];
+ [highlightedNodes release];
[surface release];
[super dealloc];
}
+- (id<RenderDelegate>) postRenderer {
+ return postRenderer;
+}
+- (void) setPostRenderer:(id<RenderDelegate>)r {
+ if (r == postRenderer)
+ return;
+
+ [r retain];
+ [postRenderer release];
+ postRenderer = r;
+
+ [self invalidateGraph];
+}
+
- (void) renderWithContext:(id<RenderContext>)context onSurface:(id<Surface>)surface {
[self renderWithContext:context];
}
@@ -116,14 +117,17 @@ void graph_renderer_expose_event(GtkWidget *widget, GdkEventExpose *event);
}
[self renderBoundingBoxWithContext:context];
- [self renderSelectionBoxWithContext:context];
- [self renderImpendingEdgeWithContext:context];
+ [postRenderer renderWithContext:context onSurface:surface];
}
- (void) invalidateGraph {
[surface invalidate];
}
+- (void) invalidateRect:(NSRect)rect {
+ [surface invalidateRect:rect];
+}
+
- (void) invalidateNodes:(NSSet*)nodes {
for (Node *node in nodes) {
[self invalidateNode:node];
@@ -285,171 +289,34 @@ void graph_renderer_expose_event(GtkWidget *widget, GdkEventExpose *event);
[surface invalidate];
}
-- (NSRect) selectionBox {
- return selectionBox;
-}
-
-- (void) setSelectionBox:(NSRect)box {
- NSRect invRect = NSUnionRect (selectionBox, box);
- selectionBox = box;
- [surface invalidateRect:NSInsetRect (invRect, -2, -2)];
-}
-
-- (void) clearSelectionBox {
- NSRect oldRect = selectionBox;
-
- NSRect emptyRect;
- selectionBox = emptyRect;
-
- [surface invalidateRect:NSInsetRect (oldRect, -2, -2)];
+- (BOOL) isNodeHighlighted:(Node*)node {
+ return [highlightedNodes containsObject:node];
}
-
-- (void) invalidateHalfEdge {
- if (halfEdgeOrigin != nil) {
- NSRect invRect = NSRectAroundPoints(halfEdgeEnd, halfEdgeOriginPoint);
- invRect = NSUnionRect(invRect, [halfEdgeOrigin renderBoundsWithLabelForSurface:surface]);
-
- NSEnumerator *enumerator = [doc nodeEnumerator];
- Node *node;
- while ((node = [enumerator nextObject]) != nil) {
- if ([self point:halfEdgeEnd fuzzyHitsNode:node]) {
- invRect = NSUnionRect(invRect, [node renderBoundsWithLabelForSurface:surface]);
- }
- }
- [surface invalidateRect:NSInsetRect (invRect, -2.0f, -2.0f)];
- }
-}
-
-- (void) setHalfEdgeFrom:(Node*)origin to:(NSPoint)end {
- [self invalidateHalfEdge];
-
- if (halfEdgeOrigin != origin) {
- [self invalidateNode:halfEdgeOrigin];
- halfEdgeOrigin = origin;
- halfEdgeOriginPoint = [[surface transformer] toScreen:[origin point]];
- [self invalidateNode:origin];
- }
-
- if (origin != nil) {
- halfEdgeEnd = end;
- [self invalidateHalfEdge];
- }
-}
-
-- (void) clearHalfEdge {
- [self invalidateHalfEdge];
- halfEdgeOrigin = nil;
-}
-
-- (BOOL) boundingBoxHandlesShown {
- return showBoundingBoxHandles;
-}
-
-- (void) setBoundingBoxHandlesShown:(BOOL)shown {
- if (showBoundingBoxHandles != shown) {
- showBoundingBoxHandles = shown;
- [self invalidateGraph];
- }
-}
-
-- (ResizeHandle) boundingBoxResizeHandleAt:(NSPoint)p {
- NSRect bbox = [[surface transformer] rectToScreen:[[self graph] boundingBox]];
- if (p.x >= NSMaxX(bbox)) {
- if (p.x <= NSMaxX(bbox) + size) {
- if (p.y >= NSMaxY(bbox)) {
- if (p.y <= NSMaxY(bbox) + size) {
- return SouthEastHandle;
- }
- } else if (p.y <= NSMinY(bbox)) {
- if (p.y >= NSMinY(bbox) - size) {
- return NorthEastHandle;
- }
- } else {
- float eastHandleTop = sideHandleTop(bbox);
- if (p.y >= eastHandleTop && p.y <= (eastHandleTop + size)) {
- return EastHandle;
- }
- }
- }
- } else if (p.x <= NSMinX(bbox)) {
- if (p.x >= NSMinX(bbox) - size) {
- if (p.y >= NSMaxY(bbox)) {
- if (p.y <= NSMaxY(bbox) + size) {
- return SouthWestHandle;
- }
- } else if (p.y <= NSMinY(bbox)) {
- if (p.y >= NSMinY(bbox) - size) {
- return NorthWestHandle;
- }
- } else {
- float westHandleTop = sideHandleTop(bbox);
- if (p.y >= westHandleTop && p.y <= (westHandleTop + size)) {
- return WestHandle;
- }
- }
- }
- } else if (p.y >= NSMaxY(bbox)) {
- if (p.y <= NSMaxY(bbox) + size) {
- float southHandleLeft = tbHandleLeft(bbox);
- if (p.x >= southHandleLeft && p.x <= (southHandleLeft + size)) {
- return SouthHandle;
- }
+- (void) setNode:(Node*)node highlighted:(BOOL)h {
+ if (h) {
+ if (![highlightedNodes containsObject:node]) {
+ [highlightedNodes addObject:node];
+ [self invalidateNode:node];
}
- } else if (p.y <= NSMinY(bbox)) {
- if (p.y >= NSMinY(bbox) - size) {
- float northHandleLeft = tbHandleLeft(bbox);
- if (p.x >= northHandleLeft && p.x <= (northHandleLeft + size)) {
- return NorthHandle;
- }
+ } else {
+ if ([highlightedNodes containsObject:node]) {
+ [highlightedNodes removeObject:node];
+ [self invalidateNode:node];
}
}
- return NoHandle;
}
-
-- (NSRect) boundingBoxResizeHandleRect:(ResizeHandle)handle {
- if (![[self graph] hasBoundingBox]) {
- return NSZeroRect;
- }
- NSRect bbox = [[surface transformer] rectToScreen:[[self graph] boundingBox]];
- switch (handle) {
- case EastHandle:
- return NSMakeRect(NSMaxX(bbox), sideHandleTop(bbox), size, size);
- case SouthEastHandle:
- return NSMakeRect(NSMaxX(bbox), NSMaxY(bbox), size, size);
- case SouthHandle:
- return NSMakeRect(tbHandleLeft(bbox), NSMaxY(bbox), size, size);
- case SouthWestHandle:
- return NSMakeRect(NSMaxX(bbox), NSMinY(bbox) - size, size, size);
- case WestHandle:
- return NSMakeRect(NSMinX(bbox) - size, sideHandleTop(bbox), size, size);
- case NorthWestHandle:
- return NSMakeRect(NSMinX(bbox) - size, NSMinY(bbox) - size, size, size);
- case NorthHandle:
- return NSMakeRect(tbHandleLeft(bbox), NSMinY(bbox) - size, size, size);
- case NorthEastHandle:
- return NSMakeRect(NSMinX(bbox) - size, NSMaxY(bbox), size, size);
- default:
- return NSZeroRect;
- }
+- (void) clearHighlightedNodes {
+ [self invalidateNodes:highlightedNodes];
+ [highlightedNodes removeAllObjects];
}
@end
@implementation GraphRenderer (Private)
-- (BOOL) selectionBoxContainsNode:(Node*)node {
- return !NSIsEmptyRect (selectionBox)
- && NSPointInRect([[surface transformer] toScreen:[node point]], selectionBox);
-}
-- (BOOL) halfEdgeIncludesNode:(Node*)node {
- if (halfEdgeOrigin == nil) {
- return FALSE;
- }
- return halfEdgeOrigin == node || [self point:halfEdgeEnd hitsNode:node];
-}
- (enum NodeState) nodeState:(Node*)node {
if ([doc isNodeSelected:node]) {
return NodeSelected;
- } else if ([self selectionBoxContainsNode:node] || [self halfEdgeIncludesNode:node]) {
+ } else if ([self isNodeHighlighted:node]) {
return NodeHighlighted;
} else {
return NodeNormal;
@@ -468,54 +335,10 @@ void graph_renderer_expose_event(GtkWidget *widget, GdkEventExpose *event);
[context rect:bbox];
[context strokePathWithColor:MakeSolidRColor (1.0, 0.7, 0.5)];
- if ([self boundingBoxHandlesShown]) {
- [context startPath];
- [context rect:[self boundingBoxResizeHandleRect:EastHandle]];
- [context rect:[self boundingBoxResizeHandleRect:SouthEastHandle]];
- [context rect:[self boundingBoxResizeHandleRect:SouthHandle]];
- [context rect:[self boundingBoxResizeHandleRect:SouthWestHandle]];
- [context rect:[self boundingBoxResizeHandleRect:WestHandle]];
- [context rect:[self boundingBoxResizeHandleRect:NorthWestHandle]];
- [context rect:[self boundingBoxResizeHandleRect:NorthHandle]];
- [context rect:[self boundingBoxResizeHandleRect:NorthEastHandle]];
- [context strokePathWithColor:MakeSolidRColor (0.5, 0.5, 0.5)];
- }
-
- [context restoreState];
- }
-}
-
-- (void) renderSelectionBoxWithContext:(id<RenderContext>)context {
- if (!NSIsEmptyRect (selectionBox)) {
- [context saveState];
-
- [context setAntialiasMode:AntialiasDisabled];
- [context setLineWidth:1.0];
- [context startPath];
- [context rect:selectionBox];
- RColor fColor = MakeRColor (0.8, 0.8, 0.8, 0.2);
- RColor sColor = MakeSolidRColor (0.6, 0.6, 0.6);
- [context strokePathWithColor:sColor andFillWithColor:fColor];
-
[context restoreState];
}
}
-- (void) renderImpendingEdgeWithContext:(id<RenderContext>)context {
- if (halfEdgeOrigin == nil) {
- return;
- }
- [context saveState];
-
- [context setLineWidth:1.0];
- [context startPath];
- [context moveTo:halfEdgeOriginPoint];
- [context lineTo:halfEdgeEnd];
- [context strokePathWithColor:MakeRColor (0, 0, 0, 0.5)];
-
- [context restoreState];
-}
-
- (void) nodeNeedsRefreshing:(NSNotification*)notification {
[self invalidateNode:[[notification userInfo] objectForKey:@"node"]];
}
diff --git a/tikzit/src/gtk/HandTool.h b/tikzit/src/gtk/HandTool.h
new file mode 100644
index 0000000..c96de36
--- /dev/null
+++ b/tikzit/src/gtk/HandTool.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2012 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "TZFoundation.h"
+#import "Tool.h"
+
+@interface HandTool : NSObject <Tool> {
+ GraphRenderer *renderer;
+ NSPoint dragOrigin;
+ NSPoint oldGraphOrigin;
+}
+
+
++ (id) tool;
+- (id) init;
+@end
+
+
+// vim:ft=objc:ts=8:et:sts=4:sw=4
diff --git a/tikzit/src/gtk/HandTool.m b/tikzit/src/gtk/HandTool.m
new file mode 100644
index 0000000..830fc28
--- /dev/null
+++ b/tikzit/src/gtk/HandTool.m
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2011-2012 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "HandTool.h"
+
+#import "GraphRenderer.h"
+#import "TikzDocument.h"
+#import "tzstockitems.h"
+
+@implementation HandTool
+- (NSString*) name { return @"Drag Tool"; }
+- (const gchar*) stockIcon { return TIKZIT_STOCK_DRAG; }
+- (NSString*) helpText { return @"Move the diagram to view different parts"; }
+- (NSString*) shortcut { return @"m"; }
+@synthesize activeRenderer=renderer;
+
++ (id) tool {
+ return [[[self alloc] init] autorelease];
+}
+
+- (id) init {
+ return [super init];
+}
+
+- (void) dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [renderer release];
+
+ [super dealloc];
+}
+
+- (GtkWidget*) configurationWidget { return NULL; }
+
+- (void) mousePressAt:(NSPoint)pos withButton:(MouseButton)button andMask:(InputMask)mask {
+ if (button != LeftButton)
+ return;
+
+ dragOrigin = pos;
+ oldGraphOrigin = [[renderer transformer] origin];
+}
+
+- (void) mouseMoveTo:(NSPoint)pos withButtons:(MouseButton)buttons andMask:(InputMask)mask {
+ if (!(buttons & LeftButton))
+ return;
+
+ NSPoint newGraphOrigin = oldGraphOrigin;
+ newGraphOrigin.x += pos.x - dragOrigin.x;
+ newGraphOrigin.y += pos.y - dragOrigin.y;
+ [[renderer transformer] setOrigin:newGraphOrigin];
+ [renderer invalidateGraph];
+}
+
+- (void) mouseReleaseAt:(NSPoint)pos withButton:(MouseButton)button andMask:(InputMask)mask {}
+
+- (void) renderWithContext:(id<RenderContext>)context onSurface:(id<Surface>)surface {}
+- (void) loadConfiguration:(Configuration*)config {}
+- (void) saveConfiguration:(Configuration*)config {}
+@end
+
+// vim:ft=objc:ts=8:et:sts=4:sw=4
diff --git a/tikzit/src/gtk/Menu.h b/tikzit/src/gtk/Menu.h
index f195765..e0f78d4 100644
--- a/tikzit/src/gtk/Menu.h
+++ b/tikzit/src/gtk/Menu.h
@@ -25,14 +25,12 @@
* Manages the menu
*/
@interface Menu: NSObject {
- Window *window;
- GtkUIManager *ui;
- GtkActionGroup *staticActions;
- GtkActionGroup *documentActions;
-// GtkActionGroup *documents_list_menu_actions;
- GtkAction *undoAction;
- GtkAction *redoAction;
- GtkAction *pasteAction;
+ GtkWidget *menubar;
+ GtkActionGroup *appActions;
+ GtkActionGroup *windowActions;
+ GtkAction *undoAction; // no ref
+ GtkAction *redoAction; // no ref
+ GtkAction *pasteAction; // no ref
GtkAction **nodeSelBasedActions;
guint nodeSelBasedActionCount;
GtkAction **edgeSelBasedActions;
@@ -45,10 +43,6 @@
* The menubar widget, to be inserted into the window
*/
@property (readonly) GtkWidget *menubar;
-/**
- * The window object passed to initForWindow
- */
-@property (readonly) Window *window;
/**
* Constructs the menu for @p window
diff --git a/tikzit/src/gtk/Menu.m b/tikzit/src/gtk/Menu.m
index c6f2fde..8d0cd97 100644
--- a/tikzit/src/gtk/Menu.m
+++ b/tikzit/src/gtk/Menu.m
@@ -23,10 +23,10 @@
#import "Application.h"
#import "Window.h"
-#import "GraphInputHandler.h"
#import "Configuration.h"
#import "PickSupport.h"
#import "Shape.h"
+#import "Tool.h"
#import "TikzDocument.h"
#import <glib.h>
@@ -38,63 +38,42 @@
#import "gtkhelpers.h"
-#define ACTION_GROUP_STATIC "TZStatic"
-#define ACTION_GROUP_DOCUMENT "TZDocument"
-#define ACTION_GROUP_DOCUMENTS_LIST_MENU "TZDocumentsList"
-
#import "logo.h"
-#include <gdk-pixbuf/gdk-pixdata.h>
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wpointer-sign"
-#import "icondata.m"
-#pragma GCC diagnostic pop
-
-
-// {{{ Callbacks
-
-static void new_cb (GtkAction *action, Window *window) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [app newWindow];
- [pool drain];
-}
-
-static void open_cb (GtkAction *action, Window *window) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [window openFile];
- [pool drain];
-}
-static void save_cb (GtkAction *action, Window *window) {
+// {{{ Application actions
+static void new_cb (GtkAction *action, Application *appl) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [window saveActiveDocument];
+ [appl newWindow];
[pool drain];
}
-static void save_as_cb (GtkAction *action, Window *window) {
+static void refresh_shapes_cb (GtkAction *action, Application *appl) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [window saveActiveDocumentAs];
+ [Shape refreshShapeDictionary];
[pool drain];
}
-static void save_as_shape_cb (GtkAction *action, Window *window) {
+#ifdef HAVE_POPPLER
+static void show_preferences_cb (GtkAction *action, Application *appl) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [window saveActiveDocumentAsShape];
+ [appl showSettingsDialog];
[pool drain];
}
-static void refresh_shapes_cb (GtkAction *action, Window *window) {
+static void show_preamble_cb (GtkAction *action, Application *appl) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [Shape refreshShapeDictionary];
+ [appl showPreamblesEditor];
[pool drain];
}
+#endif
-static void quit_cb (GtkAction *action, Window *window) {
+static void quit_cb (GtkAction *action, Application *appl) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [app quit];
+ [appl quit];
[pool drain];
}
-static void help_cb (GtkAction *action, Window *window) {
+static void help_cb (GtkAction *action, Application *appl) {
GError *gerror = NULL;
gtk_show_uri (NULL, "http://tikzit.sourceforge.net/manual.html", GDK_CURRENT_TIME, &gerror);
if (gerror != NULL) {
@@ -104,7 +83,7 @@ static void help_cb (GtkAction *action, Window *window) {
}
}
-static void about_cb (GtkAction *action, Window *window) {
+static void about_cb (GtkAction *action, Application *appl) {
static const gchar * const authors[] =
{ "Aleks Kissinger <aleks0@gmail.com>",
"Chris Heunen <chrisheunen@gmail.com>",
@@ -131,7 +110,7 @@ static void about_cb (GtkAction *action, Window *window) {
"Copyright \xc2\xa9 2010-2011 Aleks Kissinger, Chris Heunen and Alex Merry.";
GdkPixbuf *logo = get_logo (LOGO_SIZE_128);
- gtk_show_about_dialog (GTK_WINDOW ([window gtkWindow]),
+ gtk_show_about_dialog (NULL,
"program-name", PACKAGE_NAME,
"logo", logo,
"authors", authors,
@@ -146,6 +125,76 @@ static void about_cb (GtkAction *action, Window *window) {
g_object_unref (logo);
}
+static GtkActionEntry app_action_entries[] = {
+ /*
+ Fields:
+ * action name
+ * stock id or name of icon for action
+ * label for action (mark for translation with N_)
+ * accelerator (as understood by gtk_accelerator_parse())
+ * tooltip (mark for translation with N_)
+ * callback
+ */
+ { "New", GTK_STOCK_NEW, NULL, "<control>N",
+ N_("Create a new graph"), G_CALLBACK (new_cb) },
+
+ { "RefreshShapes", NULL, N_("_Refresh shapes"), NULL,
+ N_(""), G_CALLBACK (refresh_shapes_cb) },
+
+ { "Quit", GTK_STOCK_QUIT, NULL, "<control>Q",
+ N_("Quit the program"), G_CALLBACK (quit_cb) },
+
+ { "Tool", NULL, N_("_Tool") },
+
+#ifdef HAVE_POPPLER
+ { "ShowPreferences", GTK_STOCK_PREFERENCES, NULL, NULL,
+ N_("Edit the TikZiT preferences"), G_CALLBACK (show_preferences_cb) },
+
+ { "ShowPreamble", NULL, N_("_Edit Preambles..."), NULL,
+ N_("Edit the preambles used to generate the preview"), G_CALLBACK (show_preamble_cb) },
+#endif
+
+ /* HelpMenu */
+ { "HelpManual", GTK_STOCK_HELP, N_("_Online manual"), "F1",
+ N_("TikZiT manual (online)"), G_CALLBACK (help_cb) },
+
+ { "About", GTK_STOCK_ABOUT, NULL, NULL,
+ N_("About this application"), G_CALLBACK (about_cb) },
+};
+static guint n_app_action_entries = G_N_ELEMENTS (app_action_entries);
+// }}}
+// {{{ Window actions
+
+static void open_cb (GtkAction *action, Window *window) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [window openFile];
+ [pool drain];
+}
+
+static void close_cb (GtkAction *action, Window *window) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [window close];
+ [pool drain];
+}
+
+static void save_cb (GtkAction *action, Window *window) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [window saveActiveDocument];
+ [pool drain];
+}
+
+static void save_as_cb (GtkAction *action, Window *window) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [window saveActiveDocumentAs];
+ [pool drain];
+}
+
+static void save_as_shape_cb (GtkAction *action, Window *window) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [window saveActiveDocumentAsShape];
+ [pool drain];
+}
+
static void undo_cb (GtkAction *action, Window *window) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
@@ -256,18 +305,6 @@ static void send_to_back_cb (GtkAction *action, Window *window) {
}
#ifdef HAVE_POPPLER
-static void show_preferences_cb (GtkAction *action, Window *window) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [app showSettingsDialog];
- [pool drain];
-}
-
-static void show_preamble_cb (GtkAction *action, Window *window) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- [app showPreamblesEditor];
- [pool drain];
-}
-
static void show_preview_cb (GtkAction *action, Window *window) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[app showPreviewForDocument:[window document]];
@@ -293,25 +330,6 @@ static void zoom_reset_cb (GtkAction *action, Window *window) {
[pool drain];
}
-static void input_mode_change_cb (GtkRadioAction *action, GtkRadioAction *current, Window *window) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- InputMode mode = (InputMode)gtk_radio_action_get_current_value (action);
- [[window graphInputHandler] setMode:mode];
- [pool drain];
-}
-
-/*
-static void toolbar_style_change_cb (GtkRadioAction *action, GtkRadioAction *current, Menu *menu) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- gint value = gtk_radio_action_get_current_value (action);
- gtk_toolbar_set_style (GTK_TOOLBAR ([menu toolbar]), (GtkToolbarStyle)value);
- [[[menu mainWindow] mainConfiguration] setIntegerEntry:@"toolbarStyle" inGroup:@"UI" value:value];
-
- [pool drain];
-}
-*/
-
static void recent_chooser_item_activated_cb (GtkRecentChooser *chooser, Window *window) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
@@ -336,121 +354,7 @@ static void recent_chooser_item_activated_cb (GtkRecentChooser *chooser, Window
}
-
-// }}}
-// {{{ UI XML
-
-static const gchar ui_info[] =
-"<ui>"
-" <menubar name='MenuBar'>"
-" <menu action='FileMenu'>"
-" <menuitem action='New'/>"
-" <menuitem action='Open'/>"
-" <menuitem action='OpenRecent'/>"
-" <separator/>"
-" <menuitem action='Save'/>"
-" <menuitem action='SaveAs'/>"
-" <separator/>"
-" <menuitem action='SaveAsShape'/>"
-" <menuitem action='RefreshShapes'/>"
-" <separator/>"
-//" <menuitem action='Close'/>"
-" <menuitem action='Quit'/>"
-" </menu>"
-" <menu action='EditMenu'>"
-" <menu action='Tool'>"
-" <menuitem action='SelectMode'/>"
-" <menuitem action='CreateNodeMode'/>"
-" <menuitem action='DrawEdgeMode'/>"
-" <menuitem action='BoundingBoxMode'/>"
-" <menuitem action='HandMode'/>"
-" </menu>"
-" <separator/>"
-" <menuitem action='Undo'/>"
-" <menuitem action='Redo'/>"
-" <separator/>"
-" <menuitem action='Cut'/>"
-" <menuitem action='Copy'/>"
-" <menuitem action='Paste'/>"
-" <menuitem action='Delete'/>"
-" <separator/>"
-" <menuitem action='SelectAll'/>"
-" <menuitem action='DeselectAll'/>"
-" <separator/>"
-" <menuitem action='FlipVert'/>"
-" <menuitem action='FlipHoriz'/>"
-" <menuitem action='ReverseEdges'/>"
-" <separator/>"
-" <menu action='Arrange'>"
-" <menuitem action='SendToBack'/>"
-" <menuitem action='SendBackward'/>"
-" <menuitem action='BringForward'/>"
-" <menuitem action='BringToFront'/>"
-" </menu>"
-#ifdef HAVE_POPPLER
-" <separator/>"
-" <menuitem action='ShowPreferences'/>"
-#endif
-" </menu>"
-" <menu action='ViewMenu'>"
-/*
-" <menu action='ToolbarStyle'>"
-" <menuitem action='ToolbarIconsOnly'/>"
-" <menuitem action='ToolbarTextOnly'/>"
-" <menuitem action='ToolbarTextIcons'/>"
-" <menuitem action='ToolbarTextIconsHoriz'/>"
-" </menu>"
-*/
-/*
-" <menuitem action='ToolbarVisible'/>"
-" <menuitem action='StatusbarVisible'/>"
-*/
-#ifdef HAVE_POPPLER
-" <menuitem action='ShowPreamble'/>"
-" <menuitem action='ShowPreview'/>"
-#endif
-" <menu action='Zoom'>"
-" <menuitem action='ZoomIn'/>"
-" <menuitem action='ZoomOut'/>"
-" <menuitem action='ZoomReset'/>"
-" </menu>"
-" </menu>"
-/*
-" <menu action='Window'>"
-" <placeholder name='DocumentsListPlaceholder'/>"
-" </menu>"
-*/
-" <menu action='HelpMenu'>"
-" <menuitem action='HelpManual'/>"
-" <separator/>"
-" <menuitem action='About'/>"
-" </menu>"
-" </menubar>"
-/*
-" <toolbar name='ToolBar'>"
-" <toolitem action='New'/>"
-" <toolitem action='Open'/>"
-" <toolitem action='Save'/>"
-" <separator/>"
-" <toolitem action='Cut'/>"
-" <toolitem action='Copy'/>"
-" <toolitem action='Paste'/>"
-" <separator/>"
-" <toolitem action='SelectMode'/>"
-" <toolitem action='CreateNodeMode'/>"
-" <toolitem action='DrawEdgeMode'/>"
-" <toolitem action='BoundingBoxMode'/>"
-" <toolitem action='HandMode'/>"
-" </toolbar>"
-*/
-"</ui>";
-
-
-
-// }}}
-// {{{ Actions
-
-static GtkActionEntry static_entries[] = {
+static GtkActionEntry window_action_entries[] = {
/*
Fields:
* action name
@@ -463,45 +367,16 @@ static GtkActionEntry static_entries[] = {
{ "FileMenu", NULL, N_("_File") },
{ "EditMenu", NULL, N_("_Edit") },
{ "ViewMenu", NULL, N_("_View") },
- //{ "ProjectMenu", NULL, N_("_Projects") },
{ "HelpMenu", NULL, N_("_Help") },
- //{ "UndoMenu", NULL, NULL },
- //{ "RedoMenu", NULL, NULL },
- /* FileMenu */
- { "New", GTK_STOCK_NEW, NULL, "<control>N",
- N_("Create a new graph"), G_CALLBACK (new_cb) },
+ { "Arrange", NULL, N_("_Arrange") },
+ { "Zoom", NULL, N_("_Zoom") },
{ "Open", GTK_STOCK_OPEN, N_("_Open\342\200\246") ,"<control>O",
N_("Open a graph"), G_CALLBACK (open_cb) },
- { "OpenRecent", NULL, N_("Open _Recent") },
-
- { "RefreshShapes", NULL, N_("_Refresh shapes"), NULL,
- N_(""), G_CALLBACK (refresh_shapes_cb) },
-
- { "Quit", GTK_STOCK_QUIT, NULL, "<control>Q",
- N_("Quit the program"), G_CALLBACK (quit_cb) },
-
- /* EditMenu */
- { "Tool", NULL, N_("_Tool") },
-
- { "Arrange", NULL, N_("_Arrange") },
-
-#ifdef HAVE_POPPLER
- { "ShowPreferences", GTK_STOCK_PREFERENCES, NULL, NULL,
- N_("Edit the TikZiT preferences"), G_CALLBACK (show_preferences_cb) },
-#endif
-
- /* ViewMenu */
- //{ "ToolbarStyle", NULL, N_("_Toolbar style") },
-
-#ifdef HAVE_POPPLER
- { "ShowPreamble", NULL, N_("_Edit Preambles..."), NULL,
- N_("Edit the preambles used to generate the preview"), G_CALLBACK (show_preamble_cb) },
-#endif
-
- { "Zoom", NULL, N_("_Zoom") },
+ { "Close", GTK_STOCK_CLOSE, NULL, "<control>W",
+ N_("Close the current graph"), G_CALLBACK (close_cb) },
{ "ZoomIn", GTK_STOCK_ZOOM_IN, NULL, "<control>plus",
NULL, G_CALLBACK (zoom_in_cb) },
@@ -512,19 +387,6 @@ static GtkActionEntry static_entries[] = {
{ "ZoomReset", GTK_STOCK_ZOOM_100, N_("_Reset zoom"), "<control>0",
NULL, G_CALLBACK (zoom_reset_cb) },
- /* HelpMenu */
- { "HelpManual", GTK_STOCK_HELP, N_("_Online manual"), "F1",
- N_("TikZiT manual (online)"), G_CALLBACK (help_cb) },
-
- { "About", GTK_STOCK_ABOUT, NULL, NULL,
- N_("About this application"), G_CALLBACK (about_cb) },
-};
-
-static guint n_static_entries = G_N_ELEMENTS (static_entries);
-
-static GtkActionEntry document_entries[] = {
-
- /* FileMenu */
{ "Save", GTK_STOCK_SAVE, NULL, "<control>S",
N_("Save the current graph"), G_CALLBACK (save_cb) },
@@ -534,12 +396,6 @@ static GtkActionEntry document_entries[] = {
{ "SaveAsShape", NULL, N_("Save As S_hape\342\200\246"), NULL,
N_("Save the current graph as a shape for use in styles"), G_CALLBACK (save_as_shape_cb) },
-/*
- { "Close", GTK_STOCK_CLOSE, NULL, "<control>W",
- N_("Close the current graph"), G_CALLBACK (close_cb) },
-*/
-
- /* EditMenu */
{ "Undo", GTK_STOCK_UNDO, NULL, "<control>Z",
N_("Undo the last action"), G_CALLBACK (undo_cb) },
@@ -591,101 +447,114 @@ static GtkActionEntry document_entries[] = {
N_("See the graph as it will look when rendered in LaTeX"), G_CALLBACK (show_preview_cb) },
#endif
};
-static guint n_document_entries = G_N_ELEMENTS (document_entries);
+static guint n_window_action_entries = G_N_ELEMENTS (window_action_entries);
-static GtkRadioActionEntry mode_entries[] = {
- /*
- Fields:
- * action name
- * stock id or name of icon for action
- * label for action (mark for translation with N_)
- * accelerator (as understood by gtk_accelerator_parse())
- * tooltip (mark for translation with N_)
- * value (see gtk_radio_action_get_current_value())
- */
-
- { "SelectMode", NULL, N_("_Select"), "<control><shift>s",
- N_("Select, move and edit nodes and edges"), (gint)SelectMode },
-
- { "CreateNodeMode", NULL, N_("_Create nodes"), "<control><shift>c",
- N_("Create new nodes"), (gint)CreateNodeMode },
-
- { "DrawEdgeMode", NULL, N_("_Draw edges"), "<control><shift>e",
- N_("Draw new edges"), (gint)DrawEdgeMode },
-
- { "BoundingBoxMode", NULL, N_("_Bounding box"), "<control><shift>x",
- N_("Set the bounding box"), (gint)BoundingBoxMode },
-
- { "HandMode", NULL, N_("_Pan"), "<control><shift>f",
- N_("Move the diagram to view different parts"), (gint)HandMode },
-};
-static guint n_mode_entries = G_N_ELEMENTS (mode_entries);
-
-static GtkRadioActionEntry toolbar_style_entries[] = {
- /*
- Fields:
- * action name
- * stock id or name of icon for action
- * label for action (mark for translation with N_)
- * accelerator (as understood by gtk_accelerator_parse())
- * tooltip (mark for translation with N_)
- * value (see gtk_radio_action_get_current_value())
- */
-
- { "ToolbarIconsOnly", NULL, N_("_Icons only"), NULL,
- N_("Show only icons on the toolbar"), (gint)GTK_TOOLBAR_ICONS },
+// }}}
+// {{{ UI XML
- { "ToolbarTextOnly", NULL, N_("_Text only"), NULL,
- N_("Show only text on the toolbar"), (gint)GTK_TOOLBAR_TEXT },
+static const gchar ui_info[] =
+"<ui>"
+" <menubar name='MenuBar'>"
+" <menu action='FileMenu'>"
+" <menuitem action='New'/>"
+" <menuitem action='Open'/>"
+" <menuitem action='OpenRecent'/>"
+" <separator/>"
+" <menuitem action='Save'/>"
+" <menuitem action='SaveAs'/>"
+" <separator/>"
+" <menuitem action='SaveAsShape'/>"
+" <menuitem action='RefreshShapes'/>"
+" <separator/>"
+" <menuitem action='Close'/>"
+" <menuitem action='Quit'/>"
+" </menu>"
+" <menu action='EditMenu'>"
+" <menu action='Tool'>"
+" </menu>"
+" <separator/>"
+" <menuitem action='Undo'/>"
+" <menuitem action='Redo'/>"
+" <separator/>"
+" <menuitem action='Cut'/>"
+" <menuitem action='Copy'/>"
+" <menuitem action='Paste'/>"
+" <menuitem action='Delete'/>"
+" <separator/>"
+" <menuitem action='SelectAll'/>"
+" <menuitem action='DeselectAll'/>"
+" <separator/>"
+" <menuitem action='FlipVert'/>"
+" <menuitem action='FlipHoriz'/>"
+" <menuitem action='ReverseEdges'/>"
+" <separator/>"
+" <menu action='Arrange'>"
+" <menuitem action='SendToBack'/>"
+" <menuitem action='SendBackward'/>"
+" <menuitem action='BringForward'/>"
+" <menuitem action='BringToFront'/>"
+" </menu>"
+#ifdef HAVE_POPPLER
+" <separator/>"
+" <menuitem action='ShowPreferences'/>"
+#endif
+" </menu>"
+" <menu action='ViewMenu'>"
+#ifdef HAVE_POPPLER
+" <menuitem action='ShowPreamble'/>"
+" <menuitem action='ShowPreview'/>"
+#endif
+" <menu action='Zoom'>"
+" <menuitem action='ZoomIn'/>"
+" <menuitem action='ZoomOut'/>"
+" <menuitem action='ZoomReset'/>"
+" </menu>"
+" </menu>"
+" <menu action='HelpMenu'>"
+" <menuitem action='HelpManual'/>"
+" <separator/>"
+" <menuitem action='About'/>"
+" </menu>"
+" </menubar>"
+/*
+" <toolbar name='ToolBar'>"
+" <toolitem action='New'/>"
+" <toolitem action='Open'/>"
+" <toolitem action='Save'/>"
+" <separator/>"
+" <toolitem action='Cut'/>"
+" <toolitem action='Copy'/>"
+" <toolitem action='Paste'/>"
+" <separator/>"
+" <toolitem action='SelectMode'/>"
+" <toolitem action='CreateNodeMode'/>"
+" <toolitem action='DrawEdgeMode'/>"
+" <toolitem action='BoundingBoxMode'/>"
+" <toolitem action='HandMode'/>"
+" </toolbar>"
+*/
+"</ui>";
- { "ToolbarTextIcons", NULL, N_("Text _below icons"), NULL,
- N_("Show icons on the toolbar with text below"), (gint)GTK_TOOLBAR_BOTH },
- { "ToolbarTextIconsHoriz", NULL, N_("Text be_side icons"), NULL,
- N_("Show icons on the toolbar with text beside"), (gint)GTK_TOOLBAR_BOTH_HORIZ },
-};
-static guint n_toolbar_style_entries = G_N_ELEMENTS (toolbar_style_entries);
// }}}
// {{{ Helper methods
-
-static void
-set_tool_button_image (GtkToolButton *button, const GdkPixdata *image_data)
+static void configure_recent_chooser (GtkRecentChooser *chooser)
{
- GtkWidget *image = NULL;
-
- if (image_data) {
- GdkPixbuf *buf = gdk_pixbuf_from_pixdata (image_data, FALSE, NULL);
- image = gtk_image_new_from_pixbuf (buf);
- g_object_unref (buf);
- }
-
- gtk_tool_button_set_icon_widget (button, image);
-
- if (image) {
- gtk_widget_show (image);
- }
-}
+ gtk_recent_chooser_set_local_only (chooser, TRUE);
+ gtk_recent_chooser_set_show_icons (chooser, FALSE);
+ gtk_recent_chooser_set_sort_type (chooser, GTK_RECENT_SORT_MRU);
-GtkWidget *
-create_recent_chooser_menu ()
-{
- GtkWidget *recent_menu;
- GtkRecentFilter *filter;
-
- recent_menu = gtk_recent_chooser_menu_new_for_manager (gtk_recent_manager_get_default ());
-
- gtk_recent_chooser_set_local_only (GTK_RECENT_CHOOSER (recent_menu), TRUE);
- gtk_recent_chooser_set_show_icons (GTK_RECENT_CHOOSER (recent_menu), FALSE);
- gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (recent_menu), GTK_RECENT_SORT_MRU);
- gtk_recent_chooser_menu_set_show_numbers (GTK_RECENT_CHOOSER_MENU (recent_menu), TRUE);
-
- filter = gtk_recent_filter_new ();
+ GtkRecentFilter *filter = gtk_recent_filter_new ();
gtk_recent_filter_add_application (filter, g_get_application_name());
- gtk_recent_chooser_set_filter (GTK_RECENT_CHOOSER (recent_menu), filter);
+ gtk_recent_chooser_set_filter (chooser, filter);
+}
- return recent_menu;
+static void tool_cb (GtkAction *action, id<Tool> tool) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ [app setActiveTool:tool];
+ [pool drain];
}
@@ -701,107 +570,102 @@ create_recent_chooser_menu ()
return nil;
}
-- (id) initForWindow:(Window*)w {
+- (id) initForWindow:(Window*)window {
self = [super init];
if (!self) {
return nil;
}
- window = w;
-
GError *error = NULL;
- staticActions = gtk_action_group_new (ACTION_GROUP_STATIC);
- //gtk_action_group_set_translation_domain (staticActions, GETTEXT_PACKAGE);
-
- gtk_action_group_add_actions (staticActions,
- static_entries,
- n_static_entries,
- window);
- gtk_action_group_add_radio_actions (staticActions, mode_entries,
- n_mode_entries, (gint)SelectMode,
- G_CALLBACK (input_mode_change_cb), window);
- /*
- GtkToolbarStyle style;
- g_object_get (G_OBJECT (gtk_settings_get_default ()), "gtk-toolbar-style", &style, NULL);
- gtk_action_group_add_radio_actions (staticActions, toolbar_style_entries,
- n_toolbar_style_entries, style,
- G_CALLBACK (toolbar_style_change_cb), self);
- */
-
- documentActions = gtk_action_group_new (ACTION_GROUP_DOCUMENT);
- //gtk_action_group_set_translation_domain (documentActions, GETTEXT_PACKAGE);
-
- gtk_action_group_add_actions (documentActions,
- document_entries,
- n_document_entries,
- window);
-
- /*
- documents_list_menu_actions =
- gtk_action_group_new (ACTION_GROUP_DOCUMENTS_LIST_MENU);
- gtk_action_group_set_translation_domain (documents_list_menu_actions,
- GETTEXT_PACKAGE);
- */
-
- ui = gtk_ui_manager_new ();
-
- gtk_ui_manager_insert_action_group (ui, staticActions, 0);
- gtk_ui_manager_insert_action_group (ui, documentActions, 1);
- //gtk_ui_manager_insert_action_group (ui, documents_list_menu_actions, 3);
-
- gtk_window_add_accel_group ([window gtkWindow], gtk_ui_manager_get_accel_group (ui));
-
- if (!gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error))
- {
- g_message ("Building menus failed: %s", error->message);
- g_error_free (error);
- return NULL;
+ appActions = gtk_action_group_new ("TZApp");
+ //gtk_action_group_set_translation_domain (actions, GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (appActions,
+ app_action_entries,
+ n_app_action_entries,
+ app);
+ for (id<Tool> tool in [app tools]) {
+ NSString *tooltip = [NSString stringWithFormat:
+ @"%@: %@ (%@)", [tool name], [tool helpText], [tool shortcut]];
+ GtkAction *action = gtk_action_new (
+ [[tool name] UTF8String],
+ [[tool name] UTF8String],
+ [tooltip UTF8String],
+ [tool stockIcon]);
+ gtk_action_group_add_action_with_accel (
+ appActions,
+ action,
+ [[tool shortcut] UTF8String]);
+ g_signal_connect (
+ G_OBJECT (action),
+ "activate",
+ G_CALLBACK (tool_cb),
+ tool);
+ g_object_unref (action);
}
- /* Set custom images for tool mode buttons */
- /*
- set_tool_button_image (GTK_TOOL_BUTTON (gtk_ui_manager_get_widget (ui, "/ToolBar/SelectMode")), &select_rectangular);
- set_tool_button_image (GTK_TOOL_BUTTON (gtk_ui_manager_get_widget (ui, "/ToolBar/CreateNodeMode")), &draw_ellipse);
- set_tool_button_image (GTK_TOOL_BUTTON (gtk_ui_manager_get_widget (ui, "/ToolBar/DrawEdgeMode")), &draw_path);
- set_tool_button_image (GTK_TOOL_BUTTON (gtk_ui_manager_get_widget (ui, "/ToolBar/BoundingBoxMode")), &transform_crop_and_resize);
- set_tool_button_image (GTK_TOOL_BUTTON (gtk_ui_manager_get_widget (ui, "/ToolBar/HandMode")), &transform_move);
- */
+ windowActions = gtk_action_group_new ("TZWindow");
+ //gtk_action_group_set_translation_domain (windowActions, GETTEXT_PACKAGE);
- /* Save the undo and redo actions so they can be updated */
- undoAction = gtk_action_group_get_action (documentActions, "Undo");
- redoAction = gtk_action_group_get_action (documentActions, "Redo");
- pasteAction = gtk_action_group_get_action (documentActions, "Paste");
+ gtk_action_group_add_actions (windowActions,
+ window_action_entries,
+ n_window_action_entries,
+ window);
+
+ GtkAction *action = gtk_recent_action_new ("OpenRecent", N_("Open _Recent"), NULL, NULL);
+ g_signal_connect (G_OBJECT (action),
+ "item-activated",
+ G_CALLBACK (recent_chooser_item_activated_cb),
+ window);
+ configure_recent_chooser (GTK_RECENT_CHOOSER (action));
+ gtk_action_group_add_action_with_accel (windowActions, action, NULL);
+ g_object_unref (action);
- /* Recent items */
- GtkWidget *recentMenu = create_recent_chooser_menu();
- GtkMenuItem *recentMenuItem = GTK_MENU_ITEM (gtk_ui_manager_get_widget (ui, "/MenuBar/FileMenu/OpenRecent"));
- gtk_menu_item_set_submenu (recentMenuItem, recentMenu);
- g_signal_connect (recentMenu, "item-activated", G_CALLBACK (recent_chooser_item_activated_cb), window);
+ /* Save refs to actions that will need to be updated */
+ undoAction = gtk_action_group_get_action (windowActions, "Undo");
+ redoAction = gtk_action_group_get_action (windowActions, "Redo");
+ pasteAction = gtk_action_group_get_action (windowActions, "Paste");
nodeSelBasedActionCount = 4;
nodeSelBasedActions = g_new (GtkAction*, nodeSelBasedActionCount);
- nodeSelBasedActions[0] = gtk_action_group_get_action (documentActions, "Cut");
- nodeSelBasedActions[1] = gtk_action_group_get_action (documentActions, "Copy");
- nodeSelBasedActions[2] = gtk_action_group_get_action (documentActions, "FlipHoriz");
- nodeSelBasedActions[3] = gtk_action_group_get_action (documentActions, "FlipVert");
+ nodeSelBasedActions[0] = gtk_action_group_get_action (windowActions, "Cut");
+ nodeSelBasedActions[1] = gtk_action_group_get_action (windowActions, "Copy");
+ nodeSelBasedActions[2] = gtk_action_group_get_action (windowActions, "FlipHoriz");
+ nodeSelBasedActions[3] = gtk_action_group_get_action (windowActions, "FlipVert");
edgeSelBasedActionCount = 1;
edgeSelBasedActions = g_new (GtkAction*, edgeSelBasedActionCount);
- edgeSelBasedActions[0] = gtk_action_group_get_action (documentActions, "ReverseEdges");
+ edgeSelBasedActions[0] = gtk_action_group_get_action (windowActions, "ReverseEdges");
selBasedActionCount = 2;
selBasedActions = g_new (GtkAction*, selBasedActionCount);
- selBasedActions[0] = gtk_action_group_get_action (documentActions, "Delete");
- selBasedActions[1] = gtk_action_group_get_action (documentActions, "DeselectAll");
+ selBasedActions[0] = gtk_action_group_get_action (windowActions, "Delete");
+ selBasedActions[1] = gtk_action_group_get_action (windowActions, "DeselectAll");
- /*
- Configuration *configFile = [app mainConfiguration];
- if ([configFile hasKey:@"toolbarStyle" inGroup:@"UI"]) {
- int value = [configFile integerEntry:@"toolbarStyle" inGroup:@"UI"];
- gtk_radio_action_set_current_value (
- GTK_RADIO_ACTION (gtk_action_group_get_action (staticActions, "ToolbarIconsOnly")),
- value);
+
+ GtkUIManager *ui = gtk_ui_manager_new ();
+ gtk_ui_manager_insert_action_group (ui, windowActions, 0);
+ gtk_ui_manager_insert_action_group (ui, appActions, 1);
+ gtk_window_add_accel_group ([window gtkWindow], gtk_ui_manager_get_accel_group (ui));
+ if (!gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error))
+ {
+ g_message ("Building menus failed: %s", error->message);
+ g_error_free (error);
+ g_object_unref (ui);
+ [self release];
+ return nil;
}
- */
+ guint tool_merge_id = gtk_ui_manager_new_merge_id (ui);
+ for (id<Tool> tool in [app tools]) {
+ gtk_ui_manager_add_ui (ui,
+ tool_merge_id,
+ "/ui/MenuBar/EditMenu/Tool",
+ [[tool name] UTF8String],
+ [[tool name] UTF8String],
+ GTK_UI_MANAGER_AUTO,
+ FALSE);
+ }
+ menubar = gtk_ui_manager_get_widget (ui, "/MenuBar");
+ g_object_ref_sink (menubar);
+ g_object_unref (ui);
return self;
}
@@ -809,21 +673,13 @@ create_recent_chooser_menu ()
- (void) dealloc {
g_free (nodeSelBasedActions);
g_free (selBasedActions);
+ g_object_unref (menubar);
+ g_object_unref (windowActions);
[super dealloc];
}
-- (GtkWidget*) menubar {
- return gtk_ui_manager_get_widget (ui, "/MenuBar");
-}
-
-/*
-- (GtkWidget*) toolbar {
- return gtk_ui_manager_get_widget (ui, "/ToolBar");
-}
-*/
-
-@synthesize window;
+@synthesize menubar;
- (void) setUndoActionEnabled:(BOOL)enabled {
gtk_action_set_sensitive (undoAction, enabled);
diff --git a/tikzit/src/gtk/GraphInputHandler.h b/tikzit/src/gtk/SelectTool.h
index 4dbe5ca..172a7ae 100644
--- a/tikzit/src/gtk/GraphInputHandler.h
+++ b/tikzit/src/gtk/SelectTool.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2011 Alex Merry <alex.merry@kdemail.net>
+ * Copyright 2012 Alex Merry <alex.merry@kdemail.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -16,51 +16,39 @@
*/
#import "TZFoundation.h"
-#import "GraphRenderer.h"
-#import "InputDelegate.h"
-#import "StyleManager.h"
+#import "Tool.h"
-typedef enum {
- SelectMode,
- CreateNodeMode,
- DrawEdgeMode,
- BoundingBoxMode,
- HandMode
-} InputMode;
+@class Edge;
+@class Node;
+// FIXME: replace this with delegates
typedef enum {
QuietState,
SelectBoxState,
ToggleSelectState,
MoveSelectedNodesState,
DragEdgeControlPoint1,
- DragEdgeControlPoint2,
- EdgeDragState,
- BoundingBoxState,
- CanvasDragState
-} MouseState;
-
-@interface GraphInputHandler: NSObject <InputDelegate> {
- GraphRenderer *renderer;
- InputMode mode;
- MouseState state;
- float edgeFuzz;
- NSPoint dragOrigin;
- Node *leaderNode;
- NSPoint oldLeaderPos;
- Edge *modifyEdge;
- NSMutableSet *selectionBoxContents;
- ResizeHandle currentResizeHandle;
- NSPoint oldOrigin;
+ DragEdgeControlPoint2
+} SelectToolState;
+
+@interface SelectTool : NSObject <Tool> {
+ GraphRenderer *renderer;
+ SelectToolState state;
+ float edgeFuzz;
+ NSPoint dragOrigin;
+ Node *leaderNode;
+ NSPoint oldLeaderPos;
+ Edge *modifyEdge;
+ NSRect selectionBox;
+ NSMutableSet *selectionBoxContents;
+
+ GtkWidget *configWidget;
}
@property (assign) float edgeFuzz;
-@property (assign) InputMode mode;
-
-- (id) initWithGraphRenderer:(GraphRenderer*)r;
-
-- (void) resetState;
+- (id) init;
++ (id) tool;
@end
// vim:ft=objc:ts=8:et:sts=4:sw=4
diff --git a/tikzit/src/gtk/SelectTool.m b/tikzit/src/gtk/SelectTool.m
new file mode 100644
index 0000000..08abbea
--- /dev/null
+++ b/tikzit/src/gtk/SelectTool.m
@@ -0,0 +1,366 @@
+/*
+ * Copyright 2012 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "SelectTool.h"
+
+#import "Edge+Render.h"
+#import "GraphRenderer.h"
+#import "TikzDocument.h"
+#import "tzstockitems.h"
+
+static const InputMask unionSelectMask = ShiftMask;
+
+@interface SelectTool (Private)
+- (TikzDocument*) doc;
+- (void) shiftNodesByMovingLeader:(Node*)leader to:(NSPoint)to;
+- (void) deselectAllNodes;
+- (void) deselectAllEdges;
+- (void) deselectAll;
+- (BOOL) circleWithCenter:(NSPoint)c andRadius:(float)r containsPoint:(NSPoint)p;
+- (void) lookForControlPointAt:(NSPoint)pos;
+- (void) setSelectionBox:(NSRect)box;
+- (void) clearSelectionBox;
+- (BOOL) selectionBoxContainsNode:(Node*)node;
+@end
+
+@implementation SelectTool
+- (NSString*) name { return @"Select Tool"; }
+- (const gchar*) stockIcon { return TIKZIT_STOCK_SELECT; }
+- (NSString*) helpText { return @"Select, move and edit nodes and edges"; }
+- (NSString*) shortcut { return @"s"; }
+@synthesize configurationWidget=configWidget;
+@synthesize edgeFuzz;
+
++ (id) tool {
+ return [[[self alloc] init] autorelease];
+}
+
+- (id) init {
+ self = [super init];
+
+ if (self) {
+ state = QuietState;
+ edgeFuzz = 3.0f;
+ selectionBoxContents = [[NSMutableSet alloc] initWithCapacity:10];
+ }
+
+ return self;
+}
+
+- (void) dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [renderer release];
+ [leaderNode release];
+ [modifyEdge release];
+ [selectionBoxContents release];
+
+ g_object_unref (G_OBJECT (configWidget));
+
+ [super dealloc];
+}
+
+- (GraphRenderer*) activeRenderer { return renderer; }
+- (void) setActiveRenderer:(GraphRenderer*)r {
+ if (r == renderer)
+ return;
+
+ [self deselectAll];
+
+ [r retain];
+ [renderer release];
+ renderer = r;
+
+ state = QuietState;
+}
+
+- (void) mousePressAt:(NSPoint)pos withButton:(MouseButton)button andMask:(InputMask)mask {
+ if (button != LeftButton)
+ return;
+
+ dragOrigin = pos;
+
+ // we should already be in a quiet state, but no harm in making sure
+ state = QuietState;
+
+ modifyEdge = nil;
+ [self lookForControlPointAt:pos];
+
+ if (modifyEdge == nil) {
+ // we didn't find a control point
+
+ BOOL unionSelect = (mask & unionSelectMask);
+
+ leaderNode = [renderer anyNodeAt:pos];
+ // if we hit a node, deselect other nodes (if Shift is up) and go to move mode
+ if (leaderNode != nil) {
+ BOOL alreadySelected = [[self doc] isNodeSelected:leaderNode];
+ if (!unionSelect && !alreadySelected) {
+ [self deselectAllEdges];
+ [self deselectAllNodes];
+ }
+ if (unionSelect && alreadySelected) {
+ state = ToggleSelectState;
+ } else {
+ [[[self doc] pickSupport] selectNode:leaderNode];
+ state = MoveSelectedNodesState;
+ oldLeaderPos = [leaderNode point];
+ [[self doc] startShiftNodes:[[[self doc] pickSupport] selectedNodes]];
+ }
+ }
+
+ // if mouse did not hit a node, check if mouse hit an edge
+ if (leaderNode == nil) {
+ Edge *edge = [renderer anyEdgeAt:pos withFuzz:edgeFuzz];
+ if (edge != nil) {
+ BOOL alreadySelected = [[self doc] isEdgeSelected:edge];
+ if (!unionSelect) {
+ [self deselectAll];
+ }
+ if (unionSelect && alreadySelected) {
+ [[[self doc] pickSupport] deselectEdge:edge];
+ } else {
+ [[[self doc] pickSupport] selectEdge:edge];
+ }
+ } else {
+ // if mouse did not hit anything, put us in box mode
+ if (!unionSelect) {
+ [self deselectAll];
+ }
+ [selectionBoxContents removeAllObjects];
+ state = SelectBoxState;
+ }
+ }
+ }
+}
+
+- (void) mouseReleaseAt:(NSPoint)pos withButton:(MouseButton)button andMask:(InputMask)mask {
+ if (button != LeftButton)
+ return;
+
+ if (state == SelectBoxState) {
+ BOOL shouldDeselect = !(mask & unionSelectMask);
+ if (shouldDeselect) {
+ [self deselectAllEdges];
+ }
+ [[[self doc] pickSupport] selectAllNodes:selectionBoxContents
+ replacingSelection:shouldDeselect];
+ [self clearSelectionBox];
+ } else if (state == ToggleSelectState) {
+ [[[self doc] pickSupport] deselectNode:leaderNode];
+ leaderNode = nil;
+ } else if (state == MoveSelectedNodesState) {
+ if (NSEqualPoints (oldLeaderPos, [leaderNode point])) {
+ [[self doc] cancelShiftNodes];
+ } else {
+ [[self doc] endShiftNodes];
+ }
+ leaderNode = nil;
+ } else if (state == DragEdgeControlPoint1 || state == DragEdgeControlPoint2) {
+ // FIXME: check if there was any real change
+ [[self doc] endModifyEdge];
+ }
+}
+
+- (void) mouseDoubleClickAt:(NSPoint)pos withButton:(MouseButton)button andMask:(InputMask)mask {
+ if (button != LeftButton)
+ return;
+
+ if (state != QuietState) {
+ return;
+ }
+ // convert bend mode on edge under mouse cursor
+ Edge *edge = [renderer anyEdgeAt:pos withFuzz:edgeFuzz];
+ if (edge != nil) {
+ [[self doc] startModifyEdge:edge];
+ if ([edge bendMode]==EdgeBendModeBasic) {
+ [edge convertBendToAngles];
+ [edge setBendMode:EdgeBendModeInOut];
+ } else {
+ [edge setBendMode:EdgeBendModeBasic];
+ }
+ [[self doc] endModifyEdge];
+
+ [self deselectAllEdges];
+ [[[self doc] pickSupport] selectEdge:edge];
+ }
+}
+
+- (void) mouseMoveTo:(NSPoint)pos withButtons:(MouseButton)buttons andMask:(InputMask)mask {
+ if (!(buttons & LeftButton))
+ return;
+
+ Transformer *transformer = [renderer transformer];
+
+ if (state == ToggleSelectState) {
+ state = MoveSelectedNodesState;
+ oldLeaderPos = [leaderNode point];
+ [[self doc] startShiftNodes:[[[self doc] pickSupport] selectedNodes]];
+ }
+
+ if (state == SelectBoxState) {
+ [self setSelectionBox:NSRectAroundPoints(dragOrigin, pos)];
+
+ NSEnumerator *enumerator = [[self doc] nodeEnumerator];
+ Node *node;
+ while ((node = [enumerator nextObject]) != nil) {
+ NSPoint nodePos = [transformer toScreen:[node point]];
+ if (NSPointInRect(nodePos, selectionBox)) {
+ if (![selectionBoxContents member:node]) {
+ [selectionBoxContents addObject:node];
+ [renderer invalidateNode:node];
+ }
+ } else {
+ if ([selectionBoxContents member:node]) {
+ [selectionBoxContents removeObject:node];
+ [renderer invalidateNode:node];
+ }
+ }
+ }
+ } else if (state == MoveSelectedNodesState) {
+ if (leaderNode != nil) {
+ [self shiftNodesByMovingLeader:leaderNode to:pos];
+ NSPoint shiftSoFar;
+ shiftSoFar.x = [leaderNode point].x - oldLeaderPos.x;
+ shiftSoFar.y = [leaderNode point].y - oldLeaderPos.y;
+ [[self doc] shiftNodesUpdate:shiftSoFar];
+ }
+ } else if (state == DragEdgeControlPoint1 || state == DragEdgeControlPoint2) {
+ // invalidate once before we start changing it: we may be shrinking
+ // the control circles
+ [[self doc] modifyEdgeCheckPoint];
+ if (state == DragEdgeControlPoint1) {
+ [modifyEdge moveCp1To:[transformer fromScreen:pos]
+ withWeightCourseness:0.1f
+ andBendCourseness:15
+ forceLinkControlPoints:(mask & ControlMask)];
+ } else {
+ [modifyEdge moveCp2To:[transformer fromScreen:pos]
+ withWeightCourseness:0.1f
+ andBendCourseness:15
+ forceLinkControlPoints:(mask & ControlMask)];
+ }
+ [[self doc] modifyEdgeCheckPoint];
+ }
+}
+
+- (void) renderWithContext:(id<RenderContext>)context onSurface:(id<Surface>)surface {
+ if (!NSIsEmptyRect (selectionBox)) {
+ [context saveState];
+
+ [context setAntialiasMode:AntialiasDisabled];
+ [context setLineWidth:1.0];
+ [context startPath];
+ [context rect:selectionBox];
+ RColor fColor = MakeRColor (0.8, 0.8, 0.8, 0.2);
+ RColor sColor = MakeSolidRColor (0.6, 0.6, 0.6);
+ [context strokePathWithColor:sColor andFillWithColor:fColor];
+
+ [context restoreState];
+ }
+}
+
+- (void) loadConfiguration:(Configuration*)config {}
+- (void) saveConfiguration:(Configuration*)config {}
+
+@end
+
+@implementation SelectTool (Private)
+- (TikzDocument*) doc {
+ return [renderer document];
+}
+
+- (void) shiftNodesByMovingLeader:(Node*)leader to:(NSPoint)to {
+ Transformer *transformer = [renderer transformer];
+
+ NSPoint from = [transformer toScreen:[leader point]];
+ //to = [[renderer grid] snapScreenPoint:to];
+ float dx = to.x - from.x;
+ float dy = to.y - from.y;
+
+ for (Node *node in [[[self doc] pickSupport] selectedNodes]) {
+ NSPoint p = [transformer toScreen:[node point]];
+ p.x += dx;
+ p.y += dy;
+ p = [[renderer grid] snapScreenPoint:p];
+ [node setPoint:[transformer fromScreen:p]];
+ }
+}
+
+- (void) deselectAllNodes {
+ [[[self doc] pickSupport] deselectAllNodes];
+}
+
+- (void) deselectAllEdges {
+ [[[self doc] pickSupport] deselectAllEdges];
+}
+
+- (void) deselectAll {
+ [[[self doc] pickSupport] deselectAllNodes];
+ [[[self doc] pickSupport] deselectAllEdges];
+}
+
+- (BOOL) circleWithCenter:(NSPoint)c andRadius:(float)r containsPoint:(NSPoint)p {
+ return (NSDistanceBetweenPoints(c, p) <= r);
+}
+
+- (void) lookForControlPointAt:(NSPoint)pos {
+ const float cpr = [Edge controlPointRadius];
+ for (Edge *e in [[[self doc] pickSupport] selectedEdges]) {
+ NSPoint cp1 = [[renderer transformer] toScreen:[e cp1]];
+ if ([self circleWithCenter:cp1 andRadius:cpr containsPoint:pos]) {
+ state = DragEdgeControlPoint1;
+ modifyEdge = e;
+ [[self doc] startModifyEdge:e];
+ return;
+ }
+ NSPoint cp2 = [[renderer transformer] toScreen:[e cp2]];
+ if ([self circleWithCenter:cp2 andRadius:cpr containsPoint:pos]) {
+ state = DragEdgeControlPoint2;
+ modifyEdge = e;
+ [[self doc] startModifyEdge:e];
+ return;
+ }
+ }
+}
+
+- (void) setSelectionBox:(NSRect)box {
+ NSRect invRect = NSUnionRect (selectionBox, box);
+ selectionBox = box;
+ [renderer invalidateRect:NSInsetRect (invRect, -2, -2)];
+}
+
+- (void) clearSelectionBox {
+ NSRect oldRect = selectionBox;
+
+ NSRect emptyRect;
+ selectionBox = emptyRect;
+
+ [renderer invalidateRect:NSInsetRect (oldRect, -2, -2)];
+}
+
+- (BOOL) selectionBoxContainsNode:(Node*)node {
+ if (!NSIsEmptyRect (selectionBox))
+ return NO;
+
+ Transformer *transf = [[renderer surface] transformer];
+ NSPoint screenPt = [transf toScreen:[node point]];
+ return NSPointInRect(screenPt, selectionBox);
+}
+@end
+
+// vim:ft=objc:ts=8:et:sts=4:sw=4
diff --git a/tikzit/src/gtk/TikzDocument.h b/tikzit/src/gtk/TikzDocument.h
index 79a9b17..da73fac 100644
--- a/tikzit/src/gtk/TikzDocument.h
+++ b/tikzit/src/gtk/TikzDocument.h
@@ -128,26 +128,9 @@
- (void) removeSelected;
- (void) addNode:(Node*)node;
-/*!
- * Convenience function to add a node in the active style
- * at the given point.
- *
- * @param pos the position (in graph co-ordinates) of the new node
- * @return the added node
- */
-- (Node*) addNodeAt:(NSPoint)pos;
- (void) removeNode:(Node*)node;
- (void) addEdge:(Edge*)edge;
- (void) removeEdge:(Edge*)edge;
-/*!
- * Convenience function to add an edge in the active style
- * between the given nodes.
- *
- * @param source the source node
- * @param target the target node
- * @return the added edge
- */
-- (Edge*) addEdgeFrom:(Node*)source to:(Node*)target;
- (void) shiftSelectedNodesByPoint:(NSPoint)offset;
- (void) insertGraph:(Graph*)g;
- (void) flipSelectedNodesHorizontally;
diff --git a/tikzit/src/gtk/TikzDocument.m b/tikzit/src/gtk/TikzDocument.m
index 2016d2a..30d604a 100644
--- a/tikzit/src/gtk/TikzDocument.m
+++ b/tikzit/src/gtk/TikzDocument.m
@@ -669,13 +669,6 @@
[self completedGraphChange:change withName:@"Add node"];
}
-- (Node*) addNodeAt:(NSPoint)pos {
- Node *node = [Node nodeWithPoint:pos];
- [node setStyle:[styleManager activeNodeStyle]];
- [self addNode:node];
- return node;
-}
-
- (void) removeNode:(Node*)node {
[pickSupport deselectNode:node];
GraphChange *change = [graph removeNode:node];
@@ -693,13 +686,6 @@
[self completedGraphChange:change withName:@"Remove edge"];
}
-- (Edge*) addEdgeFrom:(Node*)source to:(Node*)target {
- Edge *edge = [Edge edgeWithSource:source andTarget:target];
- [edge setStyle:[styleManager activeEdgeStyle]];
- [self addEdge:edge];
- return edge;
-}
-
- (void) shiftSelectedNodesByPoint:(NSPoint)offset {
if ([[pickSupport selectedNodes] count] > 0) {
GraphChange *change = [graph shiftNodes:[pickSupport selectedNodes] byPoint:offset];
diff --git a/tikzit/src/gtk/Tool.h b/tikzit/src/gtk/Tool.h
new file mode 100644
index 0000000..de3d3ff
--- /dev/null
+++ b/tikzit/src/gtk/Tool.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2012 Alex Merry <alex.merry@kdemail.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#import "TZFoundation.h"
+#import "InputDelegate.h"
+#import "Surface.h"
+
+#import <gtk/gtk.h>
+#import <gdk-pixbuf/gdk-pixdata.h>
+
+@class Configuration;
+@class GraphRenderer;
+@protocol InputDelegate;
+@protocol RenderDelegate;
+
+@protocol Tool <RenderDelegate,InputDelegate>
+@property (readonly) NSString *name;
+@property (readonly) const gchar *stockIcon;
+@property (readonly) NSString *helpText;
+@property (readonly) NSString *shortcut;
+@property (retain) GraphRenderer *activeRenderer;
+@property (readonly) GtkWidget *configurationWidget;
+- (void) loadConfiguration:(Configuration*)config;
+- (void) saveConfiguration:(Configuration*)config;
++ (id) tool;
+@end
+
+// vim:ft=objc:ts=8:et:sts=4:sw=4
diff --git a/tikzit/src/gtk/Window.h b/tikzit/src/gtk/Window.h
index 6aa710a..089cc49 100644
--- a/tikzit/src/gtk/Window.h
+++ b/tikzit/src/gtk/Window.h
@@ -17,10 +17,8 @@
#import "TZFoundation.h"
#import <gtk/gtk.h>
-#import "WidgetSurface.h"
-@class GraphRenderer;
-@class GraphInputHandler;
+@class GraphEditorPanel;
@class Menu;
@class PropertyPane;
@class Preambles;
@@ -30,6 +28,7 @@
@class StyleManager;
@class StylesPane;
@class TikzDocument;
+@protocol Tool;
/**
* Manages a document window
@@ -44,10 +43,7 @@
// Classes that manage parts of the window
Menu *menu;
- GraphRenderer *renderer;
- GraphInputHandler *inputHandler;
-
- WidgetSurface *surface;
+ GraphEditorPanel *graphPanel;
// state variables
BOOL suppressTikzUpdates;
@@ -97,6 +93,15 @@
- (void) saveActiveDocumentAsShape;
/**
+ * Close the window.
+ *
+ * May terminate the application if this is the last window.
+ *
+ * Will ask for user confirmation if the document is not saved.
+ */
+- (void) close;
+
+/**
* Cut the current selection to the clipboard.
*/
- (void) cut;
@@ -110,10 +115,6 @@
- (void) paste;
/**
- * The graph input handler
- */
-- (GraphInputHandler*) graphInputHandler;
-/**
* The GTK+ window that this class manages.
*/
- (GtkWindow*) gtkWindow;
@@ -149,6 +150,8 @@
*/
- (void) presentGError:(GError*)error withMessage:(NSString*)message;
+- (void) setActiveTool:(id<Tool>)tool;
+
- (void) zoomIn;
- (void) zoomOut;
- (void) zoomReset;
diff --git a/tikzit/src/gtk/Window.m b/tikzit/src/gtk/Window.m
index f8e4aef..930b296 100644
--- a/tikzit/src/gtk/Window.m
+++ b/tikzit/src/gtk/Window.m
@@ -24,14 +24,12 @@
#import "Application.h"
#import "Configuration.h"
#import "FileChooserDialog.h"
-#import "GraphInputHandler.h"
-#import "GraphRenderer.h"
+#import "GraphEditorPanel.h"
#import "Menu.h"
#import "RecentManager.h"
#import "Shape.h"
#import "SupportDir.h"
#import "TikzDocument.h"
-#import "WidgetSurface.h"
// {{{ Internal interfaces
@@ -53,6 +51,7 @@ static void clipboard_paste_contents (GtkClipboard *clipboard,
// }}}
// {{{ Signals
+static void window_toplevel_focus_changed_cb (GObject *gobject, GParamSpec *pspec, GraphEditorPanel *panel);
static void graph_divider_position_changed_cb (GObject *gobject, GParamSpec *pspec, Window *window);
static void tikz_buffer_changed_cb (GtkTextBuffer *buffer, Window *window);
static gboolean main_window_delete_event_cb (GtkWidget *widget, GdkEvent *event, Window *window);
@@ -127,9 +126,7 @@ static void update_paste_action (GtkClipboard *clipboard, GdkEvent *event, GtkAc
[[NSNotificationCenter defaultCenter] removeObserver:self];
[menu release];
- [renderer release];
- [inputHandler release];
- [surface release];
+ [graphPanel release];
[document release];
g_object_unref (tikzBuffer);
@@ -152,13 +149,12 @@ static void update_paste_action (GtkClipboard *clipboard, GdkEvent *event, GtkAc
[document release];
document = newDoc;
- [renderer setDocument:document];
+ [graphPanel setDocument:document];
[self _updateTikz];
[self _updateTitle];
[self _updateStatus];
[self _updateUndoActions];
[menu notifySelectionChanged:[document pickSupport]];
- [inputHandler resetState];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(documentTikzChanged:)
name:@"TikzChanged" object:document];
@@ -324,6 +320,12 @@ static void update_paste_action (GtkClipboard *clipboard, GdkEvent *event, GtkAc
gtk_widget_destroy (dialog);
}
+- (void) close {
+ if ([self _askCanClose]) {
+ gtk_widget_destroy (GTK_WIDGET (window));
+ }
+}
+
- (void) cut {
if ([[[document pickSupport] selectedNodes] count] > 0) {
[self _placeGraphOnClipboard:[document cutSelection]];
@@ -343,10 +345,6 @@ static void update_paste_action (GtkClipboard *clipboard, GdkEvent *event, GtkAc
document);
}
-- (GraphInputHandler*) graphInputHandler {
- return inputHandler;
-}
-
- (GtkWindow*) gtkWindow {
return window;
}
@@ -405,16 +403,25 @@ static void update_paste_action (GtkClipboard *clipboard, GdkEvent *event, GtkAc
gtk_widget_destroy (dialog);
}
+- (void) setActiveTool:(id<Tool>)tool {
+ [graphPanel setActiveTool:tool];
+ gboolean hasfocus;
+ g_object_get (G_OBJECT (window), "has-toplevel-focus", &hasfocus, NULL);
+ if (hasfocus) {
+ [graphPanel grabTool];
+ }
+}
+
- (void) zoomIn {
- [surface zoomIn];
+ [graphPanel zoomIn];
}
- (void) zoomOut {
- [surface zoomOut];
+ [graphPanel zoomOut];
}
- (void) zoomReset {
- [surface zoomReset];
+ [graphPanel zoomReset];
}
@end
@@ -501,19 +508,13 @@ static void update_paste_action (GtkClipboard *clipboard, GdkEvent *event, GtkAc
gtk_widget_show (GTK_WIDGET (tikzPaneSplitter));
gtk_box_pack_start (mainLayout, GTK_WIDGET (tikzPaneSplitter), TRUE, TRUE, 0);
- surface = [[WidgetSurface alloc] init];
- gtk_widget_show ([surface widget]);
+ graphPanel = [[GraphEditorPanel alloc] initWithDocument:document];
+ GtkWidget *graphEditorWidget = [graphPanel widget];
+ gtk_widget_show (graphEditorWidget);
GtkWidget *graphFrame = gtk_frame_new (NULL);
- gtk_container_add (GTK_CONTAINER (graphFrame), [surface widget]);
+ gtk_container_add (GTK_CONTAINER (graphFrame), graphEditorWidget);
gtk_widget_show (graphFrame);
gtk_paned_pack1 (tikzPaneSplitter, graphFrame, TRUE, TRUE);
- [surface setDefaultScale:50.0f];
- [surface setKeepCentered:YES];
- [surface setGrabsFocusOnClick:YES];
- renderer = [[GraphRenderer alloc] initWithSurface:surface document:document];
-
- inputHandler = [[GraphInputHandler alloc] initWithGraphRenderer:renderer];
- [surface setInputDelegate:inputHandler];
tikzBuffer = gtk_text_buffer_new (NULL);
g_object_ref_sink (tikzBuffer);
@@ -568,6 +569,10 @@ static void update_paste_action (GtkClipboard *clipboard, GdkEvent *event, GtkAc
"key-press-event",
G_CALLBACK (tz_hijack_key_press),
NULL);
+ g_signal_connect (G_OBJECT (window),
+ "notify::has-toplevel-focus",
+ G_CALLBACK (window_toplevel_focus_changed_cb),
+ graphPanel);
g_signal_connect (G_OBJECT (tikzPaneSplitter),
"notify::position",
G_CALLBACK (graph_divider_position_changed_cb),
@@ -739,6 +744,16 @@ static void update_paste_action (GtkClipboard *clipboard, GdkEvent *event, GtkAc
// }}}
// {{{ GTK+ callbacks
+static void window_toplevel_focus_changed_cb (GObject *gobject, GParamSpec *pspec, GraphEditorPanel *panel) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ gboolean hasfocus;
+ g_object_get (gobject, "has-toplevel-focus", &hasfocus, NULL);
+ if (hasfocus) {
+ [panel grabTool];
+ }
+ [pool drain];
+}
+
static void graph_divider_position_changed_cb (GObject *gobject, GParamSpec *pspec, Window *window) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
gint position;
@@ -755,9 +770,9 @@ static void tikz_buffer_changed_cb (GtkTextBuffer *buffer, Window *window) {
static gboolean main_window_delete_event_cb (GtkWidget *widget, GdkEvent *event, Window *window) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- BOOL canClose = ![window _askCanClose];
+ [window close];
[pool drain];
- return canClose;
+ return TRUE;
}
static void main_window_destroy_cb (GtkWidget *widget, Window *window) {
diff --git a/tikzit/src/gtk/main.m b/tikzit/src/gtk/main.m
index 9d179ab..eb06449 100644
--- a/tikzit/src/gtk/main.m
+++ b/tikzit/src/gtk/main.m
@@ -25,6 +25,7 @@
#import <gtk/gtk.h>
#import "clipboard.h"
#import "logo.h"
+#import "tzstockitems.h"
#import "Application.h"
#import "TikzGraphAssembler.h"
@@ -78,6 +79,7 @@ int main (int argc, char *argv[]) {
NSAutoreleasePool *initPool = [[NSAutoreleasePool alloc] init];
+ tz_register_stock_items();
clipboard_init();
[TikzGraphAssembler setup];
diff --git a/tikzit/src/gtk/tzstockitems.h b/tikzit/src/gtk/tzstockitems.h
new file mode 100644
index 0000000..5ad0da9
--- /dev/null
+++ b/tikzit/src/gtk/tzstockitems.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2012 Alex Merry <dev@randomguy3.me.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define TIKZIT_STOCK_SELECT "tikzit-select"
+#define TIKZIT_STOCK_CREATE_NODE "tikzit-create-node"
+#define TIKZIT_STOCK_CREATE_EDGE "tikzit-create-edge"
+#define TIKZIT_STOCK_BOUNDING_BOX "tikzit-bounding-box"
+#define TIKZIT_STOCK_DRAG "tikzit-drag"
+
+void tz_register_stock_items();
+
+// vim:ft=objc:ts=8:et:sts=4:sw=4:foldmethod=marker
diff --git a/tikzit/src/gtk/tzstockitems.m b/tikzit/src/gtk/tzstockitems.m
new file mode 100644
index 0000000..6425d5f
--- /dev/null
+++ b/tikzit/src/gtk/tzstockitems.m
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2012 Alex Merry <dev@randomguy3.me.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tzstockitems.h"
+#include <gtk/gtk.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpointer-sign"
+#import "icondata.m"
+#pragma GCC diagnostic pop
+
+static GtkStockItem stock_items[] = {
+ // gchar *stock_id;
+ // gchar *label;
+ // GdkModifierType modifier;
+ // guint keyval;
+ // gchar *translation_domain;
+ { TIKZIT_STOCK_SELECT, "Select Tool", 0, 0, NULL },
+ { TIKZIT_STOCK_CREATE_NODE, "Create Node Tool", 0, 0, NULL },
+ { TIKZIT_STOCK_CREATE_EDGE, "Create Edge Tool", 0, 0, NULL },
+ { TIKZIT_STOCK_BOUNDING_BOX, "Bounding Box Tool", 0, 0, NULL },
+ { TIKZIT_STOCK_DRAG, "Drag Tool", 0, 0, NULL },
+};
+static guint n_stock_items = G_N_ELEMENTS (stock_items);
+
+static void icon_factory_add_pixdata (GtkIconFactory *factory,
+ const gchar *stock_id,
+ const GdkPixdata *image_data) {
+
+ GdkPixbuf *buf = gdk_pixbuf_from_pixdata (image_data, FALSE, NULL);
+ GtkIconSet *icon_set = gtk_icon_set_new_from_pixbuf (buf);
+ gtk_icon_factory_add (factory, stock_id, icon_set);
+ gtk_icon_set_unref (icon_set);
+ g_object_unref (G_OBJECT (buf));
+}
+
+void tz_register_stock_items() {
+ gtk_stock_add_static (stock_items, n_stock_items);
+
+ GtkIconFactory *factory = gtk_icon_factory_new ();
+ icon_factory_add_pixdata (factory, TIKZIT_STOCK_SELECT, &select_rectangular);
+ icon_factory_add_pixdata (factory, TIKZIT_STOCK_CREATE_NODE, &draw_ellipse);
+ icon_factory_add_pixdata (factory, TIKZIT_STOCK_CREATE_EDGE, &draw_path);
+ icon_factory_add_pixdata (factory, TIKZIT_STOCK_BOUNDING_BOX, &transform_crop_and_resize);
+ icon_factory_add_pixdata (factory, TIKZIT_STOCK_DRAG, &transform_move);
+ gtk_icon_factory_add_default (factory);
+}
+
+// vim:ft=objc:ts=8:et:sts=4:sw=4:foldmethod=marker