mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2024-11-22 09:36:35 +03:00
Fixes #1584 (hello @w3af looking for the patch of this one ;)
This commit is contained in:
parent
b5b3411f16
commit
af60f11319
|
@ -87,5 +87,4 @@ def profile(profileOutputFile=None, dotOutputFile=None, imageOutputFile=None):
|
||||||
win.connect('destroy', gtk.main_quit)
|
win.connect('destroy', gtk.main_quit)
|
||||||
win.set_filter("dot")
|
win.set_filter("dot")
|
||||||
win.open_file(dotOutputFile)
|
win.open_file(dotOutputFile)
|
||||||
gobject.timeout_add(1000, win.update, dotOutputFile)
|
|
||||||
gtk.main()
|
gtk.main()
|
||||||
|
|
464
thirdparty/xdot/xdot.py
vendored
464
thirdparty/xdot/xdot.py
vendored
|
@ -16,11 +16,9 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
'''Visualize dot graphs via the xdot Format.'''
|
'''Visualize dot graphs via the xdot format.'''
|
||||||
|
|
||||||
__author__ = "Jose Fonseca"
|
__author__ = "Jose Fonseca et al"
|
||||||
|
|
||||||
__version__ = "0.4"
|
|
||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
@ -30,6 +28,7 @@ import math
|
||||||
import colorsys
|
import colorsys
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
|
import optparse
|
||||||
|
|
||||||
import gobject
|
import gobject
|
||||||
import gtk
|
import gtk
|
||||||
|
@ -90,13 +89,12 @@ class Shape:
|
||||||
else:
|
else:
|
||||||
return self.pen
|
return self.pen
|
||||||
|
|
||||||
|
def search_text(self, regexp):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class TextShape(Shape):
|
class TextShape(Shape):
|
||||||
|
|
||||||
#fontmap = pangocairo.CairoFontMap()
|
|
||||||
#fontmap.set_resolution(72)
|
|
||||||
#context = fontmap.create_context()
|
|
||||||
|
|
||||||
LEFT, CENTER, RIGHT = -1, 0, 1
|
LEFT, CENTER, RIGHT = -1, 0, 1
|
||||||
|
|
||||||
def __init__(self, pen, x, y, j, w, t):
|
def __init__(self, pen, x, y, j, w, t):
|
||||||
|
@ -191,6 +189,33 @@ class TextShape(Shape):
|
||||||
cr.line_to(x+self.w, self.y)
|
cr.line_to(x+self.w, self.y)
|
||||||
cr.stroke()
|
cr.stroke()
|
||||||
|
|
||||||
|
def search_text(self, regexp):
|
||||||
|
return regexp.search(self.t) is not None
|
||||||
|
|
||||||
|
|
||||||
|
class ImageShape(Shape):
|
||||||
|
|
||||||
|
def __init__(self, pen, x0, y0, w, h, path):
|
||||||
|
Shape.__init__(self)
|
||||||
|
self.pen = pen.copy()
|
||||||
|
self.x0 = x0
|
||||||
|
self.y0 = y0
|
||||||
|
self.w = w
|
||||||
|
self.h = h
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def draw(self, cr, highlight=False):
|
||||||
|
cr2 = gtk.gdk.CairoContext(cr)
|
||||||
|
pixbuf = gtk.gdk.pixbuf_new_from_file(self.path)
|
||||||
|
sx = float(self.w)/float(pixbuf.get_width())
|
||||||
|
sy = float(self.h)/float(pixbuf.get_height())
|
||||||
|
cr.save()
|
||||||
|
cr.translate(self.x0, self.y0 - self.h)
|
||||||
|
cr.scale(sx, sy)
|
||||||
|
cr2.set_source_pixbuf(pixbuf, 0, 0)
|
||||||
|
cr2.paint()
|
||||||
|
cr.restore()
|
||||||
|
|
||||||
|
|
||||||
class EllipseShape(Shape):
|
class EllipseShape(Shape):
|
||||||
|
|
||||||
|
@ -304,6 +329,12 @@ class CompoundShape(Shape):
|
||||||
for shape in self.shapes:
|
for shape in self.shapes:
|
||||||
shape.draw(cr, highlight=highlight)
|
shape.draw(cr, highlight=highlight)
|
||||||
|
|
||||||
|
def search_text(self, regexp):
|
||||||
|
for shape in self.shapes:
|
||||||
|
if shape.search_text(regexp):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Url(object):
|
class Url(object):
|
||||||
|
|
||||||
|
@ -332,6 +363,9 @@ class Element(CompoundShape):
|
||||||
def __init__(self, shapes):
|
def __init__(self, shapes):
|
||||||
CompoundShape.__init__(self, shapes)
|
CompoundShape.__init__(self, shapes)
|
||||||
|
|
||||||
|
def is_inside(self, x, y):
|
||||||
|
return False
|
||||||
|
|
||||||
def get_url(self, x, y):
|
def get_url(self, x, y):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -341,9 +375,10 @@ class Element(CompoundShape):
|
||||||
|
|
||||||
class Node(Element):
|
class Node(Element):
|
||||||
|
|
||||||
def __init__(self, x, y, w, h, shapes, url):
|
def __init__(self, id, x, y, w, h, shapes, url):
|
||||||
Element.__init__(self, shapes)
|
Element.__init__(self, shapes)
|
||||||
|
|
||||||
|
self.id = id
|
||||||
self.x = x
|
self.x = x
|
||||||
self.y = y
|
self.y = y
|
||||||
|
|
||||||
|
@ -360,7 +395,6 @@ class Node(Element):
|
||||||
def get_url(self, x, y):
|
def get_url(self, x, y):
|
||||||
if self.url is None:
|
if self.url is None:
|
||||||
return None
|
return None
|
||||||
#print (x, y), (self.x1, self.y1), "-", (self.x2, self.y2)
|
|
||||||
if self.is_inside(x, y):
|
if self.is_inside(x, y):
|
||||||
return Url(self, self.url)
|
return Url(self, self.url)
|
||||||
return None
|
return None
|
||||||
|
@ -370,6 +404,9 @@ class Node(Element):
|
||||||
return Jump(self, self.x, self.y)
|
return Jump(self, self.x, self.y)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Node %s>" % self.id
|
||||||
|
|
||||||
|
|
||||||
def square_distance(x1, y1, x2, y2):
|
def square_distance(x1, y1, x2, y2):
|
||||||
deltax = x2 - x1
|
deltax = x2 - x1
|
||||||
|
@ -387,13 +424,29 @@ class Edge(Element):
|
||||||
|
|
||||||
RADIUS = 10
|
RADIUS = 10
|
||||||
|
|
||||||
|
def is_inside_begin(self, x, y):
|
||||||
|
return square_distance(x, y, *self.points[0]) <= self.RADIUS*self.RADIUS
|
||||||
|
|
||||||
|
def is_inside_end(self, x, y):
|
||||||
|
return square_distance(x, y, *self.points[-1]) <= self.RADIUS*self.RADIUS
|
||||||
|
|
||||||
|
def is_inside(self, x, y):
|
||||||
|
if self.is_inside_begin(x, y):
|
||||||
|
return True
|
||||||
|
if self.is_inside_end(x, y):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def get_jump(self, x, y):
|
def get_jump(self, x, y):
|
||||||
if square_distance(x, y, *self.points[0]) <= self.RADIUS*self.RADIUS:
|
if self.is_inside_begin(x, y):
|
||||||
return Jump(self, self.dst.x, self.dst.y, highlight=set([self, self.dst]))
|
return Jump(self, self.dst.x, self.dst.y, highlight=set([self, self.dst]))
|
||||||
if square_distance(x, y, *self.points[-1]) <= self.RADIUS*self.RADIUS:
|
if self.is_inside_end(x, y):
|
||||||
return Jump(self, self.src.x, self.src.y, highlight=set([self, self.src]))
|
return Jump(self, self.src.x, self.src.y, highlight=set([self, self.src]))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Edge %s -> %s>" % (self.src, self.dst)
|
||||||
|
|
||||||
|
|
||||||
class Graph(Shape):
|
class Graph(Shape):
|
||||||
|
|
||||||
|
@ -424,6 +477,14 @@ class Graph(Shape):
|
||||||
for node in self.nodes:
|
for node in self.nodes:
|
||||||
node.draw(cr, highlight=(node in highlight_items))
|
node.draw(cr, highlight=(node in highlight_items))
|
||||||
|
|
||||||
|
def get_element(self, x, y):
|
||||||
|
for node in self.nodes:
|
||||||
|
if node.is_inside(x, y):
|
||||||
|
return node
|
||||||
|
for edge in self.edges:
|
||||||
|
if edge.is_inside(x, y):
|
||||||
|
return edge
|
||||||
|
|
||||||
def get_url(self, x, y):
|
def get_url(self, x, y):
|
||||||
for node in self.nodes:
|
for node in self.nodes:
|
||||||
url = node.get_url(x, y)
|
url = node.get_url(x, y)
|
||||||
|
@ -443,6 +504,14 @@ class Graph(Shape):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
BOLD = 1
|
||||||
|
ITALIC = 2
|
||||||
|
UNDERLINE = 4
|
||||||
|
SUPERSCRIPT = 8
|
||||||
|
SUBSCRIPT = 16
|
||||||
|
STRIKE_THROUGH = 32
|
||||||
|
|
||||||
|
|
||||||
class XDotAttrParser:
|
class XDotAttrParser:
|
||||||
"""Parser for xdot drawing attributes.
|
"""Parser for xdot drawing attributes.
|
||||||
See also:
|
See also:
|
||||||
|
@ -451,7 +520,7 @@ class XDotAttrParser:
|
||||||
|
|
||||||
def __init__(self, parser, buf):
|
def __init__(self, parser, buf):
|
||||||
self.parser = parser
|
self.parser = parser
|
||||||
self.buf = self.unescape(buf)
|
self.buf = buf
|
||||||
self.pos = 0
|
self.pos = 0
|
||||||
|
|
||||||
self.pen = Pen()
|
self.pen = Pen()
|
||||||
|
@ -460,11 +529,6 @@ class XDotAttrParser:
|
||||||
def __nonzero__(self):
|
def __nonzero__(self):
|
||||||
return self.pos < len(self.buf)
|
return self.pos < len(self.buf)
|
||||||
|
|
||||||
def unescape(self, buf):
|
|
||||||
buf = buf.replace('\\"', '"')
|
|
||||||
buf = buf.replace('\\n', '\n')
|
|
||||||
return buf
|
|
||||||
|
|
||||||
def read_code(self):
|
def read_code(self):
|
||||||
pos = self.buf.find(" ", self.pos)
|
pos = self.buf.find(" ", self.pos)
|
||||||
res = self.buf[self.pos:pos]
|
res = self.buf[self.pos:pos]
|
||||||
|
@ -473,19 +537,19 @@ class XDotAttrParser:
|
||||||
self.pos += 1
|
self.pos += 1
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def read_number(self):
|
def read_int(self):
|
||||||
return int(self.read_code())
|
return int(self.read_code())
|
||||||
|
|
||||||
def read_float(self):
|
def read_float(self):
|
||||||
return float(self.read_code())
|
return float(self.read_code())
|
||||||
|
|
||||||
def read_point(self):
|
def read_point(self):
|
||||||
x = self.read_number()
|
x = self.read_float()
|
||||||
y = self.read_number()
|
y = self.read_float()
|
||||||
return self.transform(x, y)
|
return self.transform(x, y)
|
||||||
|
|
||||||
def read_text(self):
|
def read_text(self):
|
||||||
num = self.read_number()
|
num = self.read_int()
|
||||||
pos = self.buf.find("-", self.pos) + 1
|
pos = self.buf.find("-", self.pos) + 1
|
||||||
self.pos = pos + num
|
self.pos = pos + num
|
||||||
res = self.buf[pos:self.pos]
|
res = self.buf[pos:self.pos]
|
||||||
|
@ -494,7 +558,7 @@ class XDotAttrParser:
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def read_polygon(self):
|
def read_polygon(self):
|
||||||
n = self.read_number()
|
n = self.read_int()
|
||||||
p = []
|
p = []
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
x, y = self.read_point()
|
x, y = self.read_point()
|
||||||
|
@ -521,6 +585,9 @@ class XDotAttrParser:
|
||||||
r, g, b = colorsys.hsv_to_rgb(h, s, v)
|
r, g, b = colorsys.hsv_to_rgb(h, s, v)
|
||||||
a = 1.0
|
a = 1.0
|
||||||
return r, g, b, a
|
return r, g, b, a
|
||||||
|
elif c1 == "[":
|
||||||
|
sys.stderr.write('warning: color gradients not supported yet\n')
|
||||||
|
return None
|
||||||
else:
|
else:
|
||||||
return self.lookup_color(c)
|
return self.lookup_color(c)
|
||||||
|
|
||||||
|
@ -550,7 +617,7 @@ class XDotAttrParser:
|
||||||
a = 1.0
|
a = 1.0
|
||||||
return r, g, b, a
|
return r, g, b, a
|
||||||
|
|
||||||
sys.stderr.write("unknown color '%s'\n" % c)
|
sys.stderr.write("warning: unknown color '%s'\n" % c)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
|
@ -573,7 +640,7 @@ class XDotAttrParser:
|
||||||
lw = style.split("(")[1].split(")")[0]
|
lw = style.split("(")[1].split(")")[0]
|
||||||
lw = float(lw)
|
lw = float(lw)
|
||||||
self.handle_linewidth(lw)
|
self.handle_linewidth(lw)
|
||||||
elif style in ("solid", "dashed"):
|
elif style in ("solid", "dashed", "dotted"):
|
||||||
self.handle_linestyle(style)
|
self.handle_linestyle(style)
|
||||||
elif op == "F":
|
elif op == "F":
|
||||||
size = s.read_float()
|
size = s.read_float()
|
||||||
|
@ -581,19 +648,22 @@ class XDotAttrParser:
|
||||||
self.handle_font(size, name)
|
self.handle_font(size, name)
|
||||||
elif op == "T":
|
elif op == "T":
|
||||||
x, y = s.read_point()
|
x, y = s.read_point()
|
||||||
j = s.read_number()
|
j = s.read_int()
|
||||||
w = s.read_number()
|
w = s.read_float()
|
||||||
t = s.read_text()
|
t = s.read_text()
|
||||||
self.handle_text(x, y, j, w, t)
|
self.handle_text(x, y, j, w, t)
|
||||||
|
elif op == "t":
|
||||||
|
f = s.read_int()
|
||||||
|
self.handle_font_characteristics(f)
|
||||||
elif op == "E":
|
elif op == "E":
|
||||||
x0, y0 = s.read_point()
|
x0, y0 = s.read_point()
|
||||||
w = s.read_number()
|
w = s.read_float()
|
||||||
h = s.read_number()
|
h = s.read_float()
|
||||||
self.handle_ellipse(x0, y0, w, h, filled=True)
|
self.handle_ellipse(x0, y0, w, h, filled=True)
|
||||||
elif op == "e":
|
elif op == "e":
|
||||||
x0, y0 = s.read_point()
|
x0, y0 = s.read_point()
|
||||||
w = s.read_number()
|
w = s.read_float()
|
||||||
h = s.read_number()
|
h = s.read_float()
|
||||||
self.handle_ellipse(x0, y0, w, h, filled=False)
|
self.handle_ellipse(x0, y0, w, h, filled=False)
|
||||||
elif op == "L":
|
elif op == "L":
|
||||||
points = self.read_polygon()
|
points = self.read_polygon()
|
||||||
|
@ -610,9 +680,15 @@ class XDotAttrParser:
|
||||||
elif op == "p":
|
elif op == "p":
|
||||||
points = self.read_polygon()
|
points = self.read_polygon()
|
||||||
self.handle_polygon(points, filled=False)
|
self.handle_polygon(points, filled=False)
|
||||||
|
elif op == "I":
|
||||||
|
x0, y0 = s.read_point()
|
||||||
|
w = s.read_float()
|
||||||
|
h = s.read_float()
|
||||||
|
path = s.read_text()
|
||||||
|
self.handle_image(x0, y0, w, h, path)
|
||||||
else:
|
else:
|
||||||
sys.stderr.write("unknown xdot opcode '%s'\n" % op)
|
sys.stderr.write("error: unknown xdot opcode '%s'\n" % op)
|
||||||
break
|
sys.exit(1)
|
||||||
|
|
||||||
return self.shapes
|
return self.shapes
|
||||||
|
|
||||||
|
@ -633,11 +709,18 @@ class XDotAttrParser:
|
||||||
self.pen.dash = ()
|
self.pen.dash = ()
|
||||||
elif style == "dashed":
|
elif style == "dashed":
|
||||||
self.pen.dash = (6, ) # 6pt on, 6pt off
|
self.pen.dash = (6, ) # 6pt on, 6pt off
|
||||||
|
elif style == "dotted":
|
||||||
|
self.pen.dash = (2, 4) # 2pt on, 4pt off
|
||||||
|
|
||||||
def handle_font(self, size, name):
|
def handle_font(self, size, name):
|
||||||
self.pen.fontsize = size
|
self.pen.fontsize = size
|
||||||
self.pen.fontname = name
|
self.pen.fontname = name
|
||||||
|
|
||||||
|
def handle_font_characteristics(self, flags):
|
||||||
|
# TODO
|
||||||
|
if flags != 0:
|
||||||
|
sys.stderr.write("warning: font characteristics not supported yet\n" % op)
|
||||||
|
|
||||||
def handle_text(self, x, y, j, w, t):
|
def handle_text(self, x, y, j, w, t):
|
||||||
self.shapes.append(TextShape(self.pen, x, y, j, w, t))
|
self.shapes.append(TextShape(self.pen, x, y, j, w, t))
|
||||||
|
|
||||||
|
@ -647,6 +730,9 @@ class XDotAttrParser:
|
||||||
self.shapes.append(EllipseShape(self.pen, x0, y0, w, h, filled=True))
|
self.shapes.append(EllipseShape(self.pen, x0, y0, w, h, filled=True))
|
||||||
self.shapes.append(EllipseShape(self.pen, x0, y0, w, h))
|
self.shapes.append(EllipseShape(self.pen, x0, y0, w, h))
|
||||||
|
|
||||||
|
def handle_image(self, x0, y0, w, h, path):
|
||||||
|
self.shapes.append(ImageShape(self.pen, x0, y0, w, h, path))
|
||||||
|
|
||||||
def handle_line(self, points):
|
def handle_line(self, points):
|
||||||
self.shapes.append(LineShape(self.pen, points))
|
self.shapes.append(LineShape(self.pen, points))
|
||||||
|
|
||||||
|
@ -922,10 +1008,11 @@ class DotLexer(Lexer):
|
||||||
text = text.replace('\\\r', '')
|
text = text.replace('\\\r', '')
|
||||||
text = text.replace('\\\n', '')
|
text = text.replace('\\\n', '')
|
||||||
|
|
||||||
text = text.replace('\\r', '\r')
|
# quotes
|
||||||
text = text.replace('\\n', '\n')
|
text = text.replace('\\"', '"')
|
||||||
text = text.replace('\\t', '\t')
|
|
||||||
text = text.replace('\\', '')
|
# layout engines recognize other escape codes (many non-standard)
|
||||||
|
# but we don't translate them here
|
||||||
|
|
||||||
type = ID
|
type = ID
|
||||||
|
|
||||||
|
@ -1059,6 +1146,8 @@ class DotParser(Parser):
|
||||||
|
|
||||||
class XDotParser(DotParser):
|
class XDotParser(DotParser):
|
||||||
|
|
||||||
|
XDOTVERSION = '1.6'
|
||||||
|
|
||||||
def __init__(self, xdotcode):
|
def __init__(self, xdotcode):
|
||||||
lexer = DotLexer(buf = xdotcode)
|
lexer = DotLexer(buf = xdotcode)
|
||||||
DotParser.__init__(self, lexer)
|
DotParser.__init__(self, lexer)
|
||||||
|
@ -1071,26 +1160,34 @@ class XDotParser(DotParser):
|
||||||
|
|
||||||
def handle_graph(self, attrs):
|
def handle_graph(self, attrs):
|
||||||
if self.top_graph:
|
if self.top_graph:
|
||||||
|
# Check xdot version
|
||||||
|
try:
|
||||||
|
xdotversion = attrs['xdotversion']
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if float(xdotversion) > float(self.XDOTVERSION):
|
||||||
|
sys.stderr.write('warning: xdot version %s, but supported is %s\n' % (xdotversion, self.XDOTVERSION))
|
||||||
|
|
||||||
|
# Parse bounding box
|
||||||
try:
|
try:
|
||||||
bb = attrs['bb']
|
bb = attrs['bb']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return
|
return
|
||||||
|
|
||||||
if not bb:
|
if bb:
|
||||||
return
|
xmin, ymin, xmax, ymax = map(float, bb.split(","))
|
||||||
|
|
||||||
xmin, ymin, xmax, ymax = map(float, bb.split(","))
|
self.xoffset = -xmin
|
||||||
|
self.yoffset = -ymax
|
||||||
|
self.xscale = 1.0
|
||||||
|
self.yscale = -1.0
|
||||||
|
# FIXME: scale from points to pixels
|
||||||
|
|
||||||
self.xoffset = -xmin
|
self.width = max(xmax - xmin, 1)
|
||||||
self.yoffset = -ymax
|
self.height = max(ymax - ymin, 1)
|
||||||
self.xscale = 1.0
|
|
||||||
self.yscale = -1.0
|
|
||||||
# FIXME: scale from points to pixels
|
|
||||||
|
|
||||||
self.width = xmax - xmin
|
self.top_graph = False
|
||||||
self.height = ymax - ymin
|
|
||||||
|
|
||||||
self.top_graph = False
|
|
||||||
|
|
||||||
for attr in ("_draw_", "_ldraw_", "_hdraw_", "_tdraw_", "_hldraw_", "_tldraw_"):
|
for attr in ("_draw_", "_ldraw_", "_hdraw_", "_tdraw_", "_hldraw_", "_tldraw_"):
|
||||||
if attr in attrs:
|
if attr in attrs:
|
||||||
|
@ -1104,15 +1201,15 @@ class XDotParser(DotParser):
|
||||||
return
|
return
|
||||||
|
|
||||||
x, y = self.parse_node_pos(pos)
|
x, y = self.parse_node_pos(pos)
|
||||||
w = float(attrs['width'])*72
|
w = float(attrs.get('width', 0))*72
|
||||||
h = float(attrs['height'])*72
|
h = float(attrs.get('height', 0))*72
|
||||||
shapes = []
|
shapes = []
|
||||||
for attr in ("_draw_", "_ldraw_"):
|
for attr in ("_draw_", "_ldraw_"):
|
||||||
if attr in attrs:
|
if attr in attrs:
|
||||||
parser = XDotAttrParser(self, attrs[attr])
|
parser = XDotAttrParser(self, attrs[attr])
|
||||||
shapes.extend(parser.parse())
|
shapes.extend(parser.parse())
|
||||||
url = attrs.get('URL', None)
|
url = attrs.get('URL', None)
|
||||||
node = Node(x, y, w, h, shapes, url)
|
node = Node(id, x, y, w, h, shapes, url)
|
||||||
self.node_by_name[id] = node
|
self.node_by_name[id] = node
|
||||||
if shapes:
|
if shapes:
|
||||||
self.nodes.append(node)
|
self.nodes.append(node)
|
||||||
|
@ -1399,6 +1496,9 @@ class DotWidget(gtk.DrawingArea):
|
||||||
self.connect("size-allocate", self.on_area_size_allocate)
|
self.connect("size-allocate", self.on_area_size_allocate)
|
||||||
|
|
||||||
self.connect('key-press-event', self.on_key_press_event)
|
self.connect('key-press-event', self.on_key_press_event)
|
||||||
|
self.last_mtime = None
|
||||||
|
|
||||||
|
gobject.timeout_add(1000, self.update)
|
||||||
|
|
||||||
self.x, self.y = 0.0, 0.0
|
self.x, self.y = 0.0, 0.0
|
||||||
self.zoom_ratio = 1.0
|
self.zoom_ratio = 1.0
|
||||||
|
@ -1411,9 +1511,9 @@ class DotWidget(gtk.DrawingArea):
|
||||||
def set_filter(self, filter):
|
def set_filter(self, filter):
|
||||||
self.filter = filter
|
self.filter = filter
|
||||||
|
|
||||||
def set_dotcode(self, dotcode, filename='<stdin>'):
|
def run_filter(self, dotcode):
|
||||||
if isinstance(dotcode, unicode):
|
if not self.filter:
|
||||||
dotcode = dotcode.encode('utf8')
|
return dotcode
|
||||||
p = subprocess.Popen(
|
p = subprocess.Popen(
|
||||||
[self.filter, '-Txdot'],
|
[self.filter, '-Txdot'],
|
||||||
stdin=subprocess.PIPE,
|
stdin=subprocess.PIPE,
|
||||||
|
@ -1423,6 +1523,7 @@ class DotWidget(gtk.DrawingArea):
|
||||||
universal_newlines=True
|
universal_newlines=True
|
||||||
)
|
)
|
||||||
xdotcode, error = p.communicate(dotcode)
|
xdotcode, error = p.communicate(dotcode)
|
||||||
|
sys.stderr.write(error)
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
|
dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
|
||||||
message_format=error,
|
message_format=error,
|
||||||
|
@ -1430,10 +1531,19 @@ class DotWidget(gtk.DrawingArea):
|
||||||
dialog.set_title('Dot Viewer')
|
dialog.set_title('Dot Viewer')
|
||||||
dialog.run()
|
dialog.run()
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
|
return None
|
||||||
|
return xdotcode
|
||||||
|
|
||||||
|
def set_dotcode(self, dotcode, filename=None):
|
||||||
|
self.openfilename = None
|
||||||
|
if isinstance(dotcode, unicode):
|
||||||
|
dotcode = dotcode.encode('utf8')
|
||||||
|
xdotcode = self.run_filter(dotcode)
|
||||||
|
if xdotcode is None:
|
||||||
return False
|
return False
|
||||||
try:
|
try:
|
||||||
self.set_xdotcode(xdotcode)
|
self.set_xdotcode(xdotcode)
|
||||||
except ParseError, ex:
|
except ParseError as ex:
|
||||||
dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
|
dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
|
||||||
message_format=str(ex),
|
message_format=str(ex),
|
||||||
buttons=gtk.BUTTONS_OK)
|
buttons=gtk.BUTTONS_OK)
|
||||||
|
@ -1442,11 +1552,14 @@ class DotWidget(gtk.DrawingArea):
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
|
if filename is None:
|
||||||
|
self.last_mtime = None
|
||||||
|
else:
|
||||||
|
self.last_mtime = os.stat(filename).st_mtime
|
||||||
self.openfilename = filename
|
self.openfilename = filename
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def set_xdotcode(self, xdotcode):
|
def set_xdotcode(self, xdotcode):
|
||||||
#print xdotcode
|
|
||||||
parser = XDotParser(xdotcode)
|
parser = XDotParser(xdotcode)
|
||||||
self.graph = parser.parse()
|
self.graph = parser.parse()
|
||||||
self.zoom_image(self.zoom_ratio, center=True)
|
self.zoom_image(self.zoom_ratio, center=True)
|
||||||
|
@ -1460,6 +1573,14 @@ class DotWidget(gtk.DrawingArea):
|
||||||
except IOError:
|
except IOError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
if self.openfilename is not None:
|
||||||
|
current_mtime = os.stat(self.openfilename).st_mtime
|
||||||
|
if current_mtime != self.last_mtime:
|
||||||
|
self.last_mtime = current_mtime
|
||||||
|
self.reload()
|
||||||
|
return True
|
||||||
|
|
||||||
def do_expose_event(self, event):
|
def do_expose_event(self, event):
|
||||||
cr = self.window.cairo_create()
|
cr = self.window.cairo_create()
|
||||||
|
|
||||||
|
@ -1500,6 +1621,10 @@ class DotWidget(gtk.DrawingArea):
|
||||||
self.queue_draw()
|
self.queue_draw()
|
||||||
|
|
||||||
def zoom_image(self, zoom_ratio, center=False, pos=None):
|
def zoom_image(self, zoom_ratio, center=False, pos=None):
|
||||||
|
# Constrain zoom ratio to a sane range to prevent numeric instability.
|
||||||
|
zoom_ratio = min(zoom_ratio, 1E4)
|
||||||
|
zoom_ratio = max(zoom_ratio, 1E-6)
|
||||||
|
|
||||||
if center:
|
if center:
|
||||||
self.x = self.graph.width/2
|
self.x = self.graph.width/2
|
||||||
self.y = self.graph.height/2
|
self.y = self.graph.height/2
|
||||||
|
@ -1518,10 +1643,13 @@ class DotWidget(gtk.DrawingArea):
|
||||||
rect = self.get_allocation()
|
rect = self.get_allocation()
|
||||||
width = abs(x1 - x2)
|
width = abs(x1 - x2)
|
||||||
height = abs(y1 - y2)
|
height = abs(y1 - y2)
|
||||||
self.zoom_ratio = min(
|
if width == 0 and height == 0:
|
||||||
float(rect.width)/float(width),
|
self.zoom_ratio *= self.ZOOM_INCREMENT
|
||||||
float(rect.height)/float(height)
|
else:
|
||||||
)
|
self.zoom_ratio = min(
|
||||||
|
float(rect.width)/float(width),
|
||||||
|
float(rect.height)/float(height)
|
||||||
|
)
|
||||||
self.zoom_to_fit_on_resize = False
|
self.zoom_to_fit_on_resize = False
|
||||||
self.x = (x1 + x2) / 2
|
self.x = (x1 + x2) / 2
|
||||||
self.y = (y1 + y2) / 2
|
self.y = (y1 + y2) / 2
|
||||||
|
@ -1574,11 +1702,16 @@ class DotWidget(gtk.DrawingArea):
|
||||||
self.y += self.POS_INCREMENT/self.zoom_ratio
|
self.y += self.POS_INCREMENT/self.zoom_ratio
|
||||||
self.queue_draw()
|
self.queue_draw()
|
||||||
return True
|
return True
|
||||||
if event.keyval == gtk.keysyms.Page_Up:
|
if event.keyval in (gtk.keysyms.Page_Up,
|
||||||
|
gtk.keysyms.plus,
|
||||||
|
gtk.keysyms.equal,
|
||||||
|
gtk.keysyms.KP_Add):
|
||||||
self.zoom_image(self.zoom_ratio * self.ZOOM_INCREMENT)
|
self.zoom_image(self.zoom_ratio * self.ZOOM_INCREMENT)
|
||||||
self.queue_draw()
|
self.queue_draw()
|
||||||
return True
|
return True
|
||||||
if event.keyval == gtk.keysyms.Page_Down:
|
if event.keyval in (gtk.keysyms.Page_Down,
|
||||||
|
gtk.keysyms.minus,
|
||||||
|
gtk.keysyms.KP_Subtract):
|
||||||
self.zoom_image(self.zoom_ratio / self.ZOOM_INCREMENT)
|
self.zoom_image(self.zoom_ratio / self.ZOOM_INCREMENT)
|
||||||
self.queue_draw()
|
self.queue_draw()
|
||||||
return True
|
return True
|
||||||
|
@ -1589,11 +1722,49 @@ class DotWidget(gtk.DrawingArea):
|
||||||
if event.keyval == gtk.keysyms.r:
|
if event.keyval == gtk.keysyms.r:
|
||||||
self.reload()
|
self.reload()
|
||||||
return True
|
return True
|
||||||
|
if event.keyval == gtk.keysyms.f:
|
||||||
|
win = widget.get_toplevel()
|
||||||
|
find_toolitem = win.uimanager.get_widget('/ToolBar/Find')
|
||||||
|
textentry = find_toolitem.get_children()
|
||||||
|
win.set_focus(textentry[0])
|
||||||
|
return True
|
||||||
if event.keyval == gtk.keysyms.q:
|
if event.keyval == gtk.keysyms.q:
|
||||||
gtk.main_quit()
|
gtk.main_quit()
|
||||||
return True
|
return True
|
||||||
|
if event.keyval == gtk.keysyms.p:
|
||||||
|
self.on_print()
|
||||||
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
print_settings = None
|
||||||
|
def on_print(self, action=None):
|
||||||
|
print_op = gtk.PrintOperation()
|
||||||
|
|
||||||
|
if self.print_settings != None:
|
||||||
|
print_op.set_print_settings(self.print_settings)
|
||||||
|
|
||||||
|
print_op.connect("begin_print", self.begin_print)
|
||||||
|
print_op.connect("draw_page", self.draw_page)
|
||||||
|
|
||||||
|
res = print_op.run(gtk.PRINT_OPERATION_ACTION_PRINT_DIALOG, self.parent.parent)
|
||||||
|
|
||||||
|
if res == gtk.PRINT_OPERATION_RESULT_APPLY:
|
||||||
|
print_settings = print_op.get_print_settings()
|
||||||
|
|
||||||
|
def begin_print(self, operation, context):
|
||||||
|
operation.set_n_pages(1)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def draw_page(self, operation, context, page_nr):
|
||||||
|
cr = context.get_cairo_context()
|
||||||
|
|
||||||
|
rect = self.get_allocation()
|
||||||
|
cr.translate(0.5*rect.width, 0.5*rect.height)
|
||||||
|
cr.scale(self.zoom_ratio, self.zoom_ratio)
|
||||||
|
cr.translate(-self.x, -self.y)
|
||||||
|
|
||||||
|
self.graph.draw(cr, highlight_items=self.highlight)
|
||||||
|
|
||||||
def get_drag_action(self, event):
|
def get_drag_action(self, event):
|
||||||
state = event.state
|
state = event.state
|
||||||
if event.button in (1, 2): # left or middle button
|
if event.button in (1, 2): # left or middle button
|
||||||
|
@ -1628,20 +1799,32 @@ class DotWidget(gtk.DrawingArea):
|
||||||
return (time.time() < self.presstime + click_timeout
|
return (time.time() < self.presstime + click_timeout
|
||||||
and math.hypot(deltax, deltay) < click_fuzz)
|
and math.hypot(deltax, deltay) < click_fuzz)
|
||||||
|
|
||||||
|
def on_click(self, element, event):
|
||||||
|
"""Override this method in subclass to process
|
||||||
|
click events. Note that element can be None
|
||||||
|
(click on empty space)."""
|
||||||
|
return False
|
||||||
|
|
||||||
def on_area_button_release(self, area, event):
|
def on_area_button_release(self, area, event):
|
||||||
self.drag_action.on_button_release(event)
|
self.drag_action.on_button_release(event)
|
||||||
self.drag_action = NullAction(self)
|
self.drag_action = NullAction(self)
|
||||||
if event.button == 1 and self.is_click(event):
|
x, y = int(event.x), int(event.y)
|
||||||
x, y = int(event.x), int(event.y)
|
if self.is_click(event):
|
||||||
url = self.get_url(x, y)
|
el = self.get_element(x, y)
|
||||||
if url is not None:
|
if self.on_click(el, event):
|
||||||
self.emit('clicked', unicode(url.url), event)
|
return True
|
||||||
else:
|
|
||||||
jump = self.get_jump(x, y)
|
if event.button == 1:
|
||||||
if jump is not None:
|
url = self.get_url(x, y)
|
||||||
self.animate_to(jump.x, jump.y)
|
if url is not None:
|
||||||
|
self.emit('clicked', unicode(url.url), event)
|
||||||
|
else:
|
||||||
|
jump = self.get_jump(x, y)
|
||||||
|
if jump is not None:
|
||||||
|
self.animate_to(jump.x, jump.y)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
return True
|
|
||||||
if event.button == 1 or event.button == 2:
|
if event.button == 1 or event.button == 2:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -1679,6 +1862,10 @@ class DotWidget(gtk.DrawingArea):
|
||||||
y += self.y
|
y += self.y
|
||||||
return x, y
|
return x, y
|
||||||
|
|
||||||
|
def get_element(self, x, y):
|
||||||
|
x, y = self.window2graph(x, y)
|
||||||
|
return self.graph.get_element(x, y)
|
||||||
|
|
||||||
def get_url(self, x, y):
|
def get_url(self, x, y):
|
||||||
x, y = self.window2graph(x, y)
|
x, y = self.window2graph(x, y)
|
||||||
return self.graph.get_url(x, y)
|
return self.graph.get_url(x, y)
|
||||||
|
@ -1688,6 +1875,14 @@ class DotWidget(gtk.DrawingArea):
|
||||||
return self.graph.get_jump(x, y)
|
return self.graph.get_jump(x, y)
|
||||||
|
|
||||||
|
|
||||||
|
class FindMenuToolAction(gtk.Action):
|
||||||
|
__gtype_name__ = "FindMenuToolAction"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kw):
|
||||||
|
gtk.Action.__init__(self, *args, **kw)
|
||||||
|
self.set_tool_item_type(gtk.ToolItem)
|
||||||
|
|
||||||
|
|
||||||
class DotWindow(gtk.Window):
|
class DotWindow(gtk.Window):
|
||||||
|
|
||||||
ui = '''
|
ui = '''
|
||||||
|
@ -1695,28 +1890,33 @@ class DotWindow(gtk.Window):
|
||||||
<toolbar name="ToolBar">
|
<toolbar name="ToolBar">
|
||||||
<toolitem action="Open"/>
|
<toolitem action="Open"/>
|
||||||
<toolitem action="Reload"/>
|
<toolitem action="Reload"/>
|
||||||
|
<toolitem action="Print"/>
|
||||||
<separator/>
|
<separator/>
|
||||||
<toolitem action="ZoomIn"/>
|
<toolitem action="ZoomIn"/>
|
||||||
<toolitem action="ZoomOut"/>
|
<toolitem action="ZoomOut"/>
|
||||||
<toolitem action="ZoomFit"/>
|
<toolitem action="ZoomFit"/>
|
||||||
<toolitem action="Zoom100"/>
|
<toolitem action="Zoom100"/>
|
||||||
|
<separator/>
|
||||||
|
<toolitem name="Find" action="Find"/>
|
||||||
</toolbar>
|
</toolbar>
|
||||||
</ui>
|
</ui>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self):
|
base_title = 'Dot Viewer'
|
||||||
|
|
||||||
|
def __init__(self, widget=None):
|
||||||
gtk.Window.__init__(self)
|
gtk.Window.__init__(self)
|
||||||
|
|
||||||
self.graph = Graph()
|
self.graph = Graph()
|
||||||
|
|
||||||
window = self
|
window = self
|
||||||
|
|
||||||
window.set_title('Dot Viewer')
|
window.set_title(self.base_title)
|
||||||
window.set_default_size(512, 512)
|
window.set_default_size(512, 512)
|
||||||
vbox = gtk.VBox()
|
vbox = gtk.VBox()
|
||||||
window.add(vbox)
|
window.add(vbox)
|
||||||
|
|
||||||
self.widget = DotWidget()
|
self.widget = widget or DotWidget()
|
||||||
|
|
||||||
# Create a UIManager instance
|
# Create a UIManager instance
|
||||||
uimanager = self.uimanager = gtk.UIManager()
|
uimanager = self.uimanager = gtk.UIManager()
|
||||||
|
@ -1733,12 +1933,17 @@ class DotWindow(gtk.Window):
|
||||||
actiongroup.add_actions((
|
actiongroup.add_actions((
|
||||||
('Open', gtk.STOCK_OPEN, None, None, None, self.on_open),
|
('Open', gtk.STOCK_OPEN, None, None, None, self.on_open),
|
||||||
('Reload', gtk.STOCK_REFRESH, None, None, None, self.on_reload),
|
('Reload', gtk.STOCK_REFRESH, None, None, None, self.on_reload),
|
||||||
|
('Print', gtk.STOCK_PRINT, None, None, "Prints the currently visible part of the graph", self.widget.on_print),
|
||||||
('ZoomIn', gtk.STOCK_ZOOM_IN, None, None, None, self.widget.on_zoom_in),
|
('ZoomIn', gtk.STOCK_ZOOM_IN, None, None, None, self.widget.on_zoom_in),
|
||||||
('ZoomOut', gtk.STOCK_ZOOM_OUT, None, None, None, self.widget.on_zoom_out),
|
('ZoomOut', gtk.STOCK_ZOOM_OUT, None, None, None, self.widget.on_zoom_out),
|
||||||
('ZoomFit', gtk.STOCK_ZOOM_FIT, None, None, None, self.widget.on_zoom_fit),
|
('ZoomFit', gtk.STOCK_ZOOM_FIT, None, None, None, self.widget.on_zoom_fit),
|
||||||
('Zoom100', gtk.STOCK_ZOOM_100, None, None, None, self.widget.on_zoom_100),
|
('Zoom100', gtk.STOCK_ZOOM_100, None, None, None, self.widget.on_zoom_100),
|
||||||
))
|
))
|
||||||
|
|
||||||
|
find_action = FindMenuToolAction("Find", None,
|
||||||
|
"Find a node by name", None)
|
||||||
|
actiongroup.add_action(find_action)
|
||||||
|
|
||||||
# Add the actiongroup to the uimanager
|
# Add the actiongroup to the uimanager
|
||||||
uimanager.insert_action_group(actiongroup, 0)
|
uimanager.insert_action_group(actiongroup, 0)
|
||||||
|
|
||||||
|
@ -1751,45 +1956,82 @@ class DotWindow(gtk.Window):
|
||||||
|
|
||||||
vbox.pack_start(self.widget)
|
vbox.pack_start(self.widget)
|
||||||
|
|
||||||
|
self.last_open_dir = "."
|
||||||
|
|
||||||
self.set_focus(self.widget)
|
self.set_focus(self.widget)
|
||||||
|
|
||||||
|
# Add Find text search
|
||||||
|
find_toolitem = uimanager.get_widget('/ToolBar/Find')
|
||||||
|
self.textentry = gtk.Entry(max=20)
|
||||||
|
self.textentry.set_icon_from_stock(0, gtk.STOCK_FIND)
|
||||||
|
find_toolitem.add(self.textentry)
|
||||||
|
|
||||||
|
self.textentry.set_activates_default(True)
|
||||||
|
self.textentry.connect ("activate", self.textentry_activate, self.textentry);
|
||||||
|
self.textentry.connect ("changed", self.textentry_changed, self.textentry);
|
||||||
|
|
||||||
self.show_all()
|
self.show_all()
|
||||||
|
|
||||||
def update(self, filename):
|
def find_text(self, entry_text):
|
||||||
import os
|
found_items = []
|
||||||
if not hasattr(self, "last_mtime"):
|
dot_widget = self.widget
|
||||||
self.last_mtime = None
|
regexp = re.compile(entry_text)
|
||||||
|
for node in dot_widget.graph.nodes:
|
||||||
|
if node.search_text(regexp):
|
||||||
|
found_items.append(node)
|
||||||
|
return found_items
|
||||||
|
|
||||||
current_mtime = os.stat(filename).st_mtime
|
def textentry_changed(self, widget, entry):
|
||||||
if current_mtime != self.last_mtime:
|
entry_text = entry.get_text()
|
||||||
self.last_mtime = current_mtime
|
dot_widget = self.widget
|
||||||
self.open_file(filename)
|
if not entry_text:
|
||||||
|
dot_widget.set_highlight(None)
|
||||||
|
return
|
||||||
|
|
||||||
return True
|
found_items = self.find_text(entry_text)
|
||||||
|
dot_widget.set_highlight(found_items)
|
||||||
|
|
||||||
|
def textentry_activate(self, widget, entry):
|
||||||
|
entry_text = entry.get_text()
|
||||||
|
dot_widget = self.widget
|
||||||
|
if not entry_text:
|
||||||
|
dot_widget.set_highlight(None)
|
||||||
|
return;
|
||||||
|
|
||||||
|
found_items = self.find_text(entry_text)
|
||||||
|
dot_widget.set_highlight(found_items)
|
||||||
|
if(len(found_items) == 1):
|
||||||
|
dot_widget.animate_to(found_items[0].x, found_items[0].y)
|
||||||
|
|
||||||
def set_filter(self, filter):
|
def set_filter(self, filter):
|
||||||
self.widget.set_filter(filter)
|
self.widget.set_filter(filter)
|
||||||
|
|
||||||
def set_dotcode(self, dotcode, filename='<stdin>'):
|
def set_dotcode(self, dotcode, filename=None):
|
||||||
if self.widget.set_dotcode(dotcode, filename):
|
if self.widget.set_dotcode(dotcode, filename):
|
||||||
self.set_title(os.path.basename(filename) + ' - Dot Viewer')
|
self.update_title(filename)
|
||||||
self.widget.zoom_to_fit()
|
self.widget.zoom_to_fit()
|
||||||
|
|
||||||
def set_xdotcode(self, xdotcode, filename='<stdin>'):
|
def set_xdotcode(self, xdotcode, filename=None):
|
||||||
if self.widget.set_xdotcode(xdotcode):
|
if self.widget.set_xdotcode(xdotcode):
|
||||||
self.set_title(os.path.basename(filename) + ' - Dot Viewer')
|
self.update_title(filename)
|
||||||
self.widget.zoom_to_fit()
|
self.widget.zoom_to_fit()
|
||||||
|
|
||||||
|
def update_title(self, filename=None):
|
||||||
|
if filename is None:
|
||||||
|
self.set_title(self.base_title)
|
||||||
|
else:
|
||||||
|
self.set_title(os.path.basename(filename) + ' - ' + self.base_title)
|
||||||
|
|
||||||
def open_file(self, filename):
|
def open_file(self, filename):
|
||||||
try:
|
try:
|
||||||
fp = file(filename, 'rt')
|
fp = file(filename, 'rt')
|
||||||
self.set_dotcode(fp.read(), filename)
|
self.set_dotcode(fp.read(), filename)
|
||||||
fp.close()
|
fp.close()
|
||||||
except IOError, ex:
|
except IOError as ex:
|
||||||
dlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
|
dlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
|
||||||
message_format=str(ex),
|
message_format=str(ex),
|
||||||
buttons=gtk.BUTTONS_OK)
|
buttons=gtk.BUTTONS_OK)
|
||||||
dlg.set_title('Dot Viewer')
|
dlg.set_title(self.base_title)
|
||||||
dlg.run()
|
dlg.run()
|
||||||
dlg.destroy()
|
dlg.destroy()
|
||||||
|
|
||||||
|
@ -1801,6 +2043,7 @@ class DotWindow(gtk.Window):
|
||||||
gtk.STOCK_OPEN,
|
gtk.STOCK_OPEN,
|
||||||
gtk.RESPONSE_OK))
|
gtk.RESPONSE_OK))
|
||||||
chooser.set_default_response(gtk.RESPONSE_OK)
|
chooser.set_default_response(gtk.RESPONSE_OK)
|
||||||
|
chooser.set_current_folder(self.last_open_dir)
|
||||||
filter = gtk.FileFilter()
|
filter = gtk.FileFilter()
|
||||||
filter.set_name("Graphviz dot files")
|
filter.set_name("Graphviz dot files")
|
||||||
filter.add_pattern("*.dot")
|
filter.add_pattern("*.dot")
|
||||||
|
@ -1811,6 +2054,7 @@ class DotWindow(gtk.Window):
|
||||||
chooser.add_filter(filter)
|
chooser.add_filter(filter)
|
||||||
if chooser.run() == gtk.RESPONSE_OK:
|
if chooser.run() == gtk.RESPONSE_OK:
|
||||||
filename = chooser.get_filename()
|
filename = chooser.get_filename()
|
||||||
|
self.last_open_dir = chooser.get_current_folder()
|
||||||
chooser.destroy()
|
chooser.destroy()
|
||||||
self.open_file(filename)
|
self.open_file(filename)
|
||||||
else:
|
else:
|
||||||
|
@ -1820,17 +2064,41 @@ class DotWindow(gtk.Window):
|
||||||
self.widget.reload()
|
self.widget.reload()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
class OptionParser(optparse.OptionParser):
|
||||||
import optparse
|
|
||||||
|
|
||||||
parser = optparse.OptionParser(
|
def format_epilog(self, formatter):
|
||||||
|
# Prevent stripping the newlines in epilog message
|
||||||
|
# http://stackoverflow.com/questions/1857346/python-optparse-how-to-include-additional-info-in-usage-output
|
||||||
|
return self.epilog
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
parser = OptionParser(
|
||||||
usage='\n\t%prog [file]',
|
usage='\n\t%prog [file]',
|
||||||
version='%%prog %s' % __version__)
|
epilog='''
|
||||||
|
Shortcuts:
|
||||||
|
Up, Down, Left, Right scroll
|
||||||
|
PageUp, +, = zoom in
|
||||||
|
PageDown, - zoom out
|
||||||
|
R reload dot file
|
||||||
|
F find
|
||||||
|
Q quit
|
||||||
|
P print
|
||||||
|
Escape halt animation
|
||||||
|
Ctrl-drag zoom in/out
|
||||||
|
Shift-drag zooms an area
|
||||||
|
'''
|
||||||
|
)
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
'-f', '--filter',
|
'-f', '--filter',
|
||||||
type='choice', choices=('dot', 'neato', 'twopi', 'circo', 'fdp'),
|
type='choice', choices=('dot', 'neato', 'twopi', 'circo', 'fdp'),
|
||||||
dest='filter', default='dot',
|
dest='filter', default='dot',
|
||||||
help='graphviz filter: dot, neato, twopi, circo, or fdp [default: %default]')
|
help='graphviz filter: dot, neato, twopi, circo, or fdp [default: %default]')
|
||||||
|
parser.add_option(
|
||||||
|
'-n', '--no-filter',
|
||||||
|
action='store_const', const=None, dest='filter',
|
||||||
|
help='assume input is already filtered into xdot format (use e.g. dot -Txdot)')
|
||||||
|
|
||||||
(options, args) = parser.parse_args(sys.argv[1:])
|
(options, args) = parser.parse_args(sys.argv[1:])
|
||||||
if len(args) > 1:
|
if len(args) > 1:
|
||||||
|
@ -1839,12 +2107,14 @@ def main():
|
||||||
win = DotWindow()
|
win = DotWindow()
|
||||||
win.connect('destroy', gtk.main_quit)
|
win.connect('destroy', gtk.main_quit)
|
||||||
win.set_filter(options.filter)
|
win.set_filter(options.filter)
|
||||||
if len(args) >= 1:
|
if len(args) == 0:
|
||||||
|
if not sys.stdin.isatty():
|
||||||
|
win.set_dotcode(sys.stdin.read())
|
||||||
|
else:
|
||||||
if args[0] == '-':
|
if args[0] == '-':
|
||||||
win.set_dotcode(sys.stdin.read())
|
win.set_dotcode(sys.stdin.read())
|
||||||
else:
|
else:
|
||||||
win.open_file(args[0])
|
win.open_file(args[0])
|
||||||
gobject.timeout_add(1000, win.update, args[0])
|
|
||||||
gtk.main()
|
gtk.main()
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user