Graphiclious 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.txt +126 -0
- data/Rakefile +28 -0
- data/bin/graphiclious +3 -0
- data/doc/cloud.jpg +0 -0
- data/doc/index.html +32 -0
- data/doc/screenshots/default.css +44 -0
- data/doc/screenshots/original/blue-style-1.html +37 -0
- data/doc/screenshots/original/blue-style-1.jpg +0 -0
- data/doc/screenshots/original/cornflower.html +37 -0
- data/doc/screenshots/original/cornflower.jpg +0 -0
- data/doc/screenshots/original/custom-style.html +37 -0
- data/doc/screenshots/original/custom-style.jpg +0 -0
- data/doc/screenshots/original/sample-graph.html +37 -0
- data/doc/screenshots/original/sample-graph.jpg +0 -0
- data/doc/screenshots/original/user-interface.html +37 -0
- data/doc/screenshots/original/user-interface.jpg +0 -0
- data/doc/screenshots/thumb.html +46 -0
- data/doc/screenshots/thumb/t_blue-style-1.jpg +0 -0
- data/doc/screenshots/thumb/t_cornflower.jpg +0 -0
- data/doc/screenshots/thumb/t_custom-style.jpg +0 -0
- data/doc/screenshots/thumb/t_sample-graph.jpg +0 -0
- data/doc/screenshots/thumb/t_user-interface.jpg +0 -0
- data/doc/style.css +60 -0
- data/gemspec.rb +45 -0
- data/lib/graphiclious.rb +78 -0
- data/lib/graphiclious/delicious2yaml.rb +235 -0
- data/lib/graphiclious/dot_attributes.rb +35 -0
- data/lib/graphiclious/environment.rb +36 -0
- data/lib/graphiclious/gui.rb +260 -0
- data/lib/graphiclious/help.rb +22 -0
- data/lib/graphiclious/require_fox.rb +12 -0
- data/lib/graphiclious/url_graph.rb +135 -0
- data/lib/graphiclious/utf8.rb +19 -0
- data/lib/graphiclious/version.rb +8 -0
- data/lib/graphiclious/yaml-links2html.rb +476 -0
- data/options.yaml +7 -0
- data/styles/blue-style-1/dot-attributes.yaml +21 -0
- data/styles/blue-style-1/extern.gif +0 -0
- data/styles/blue-style-1/style.css +128 -0
- data/styles/cornflower/cornflower.png +0 -0
- data/styles/cornflower/dot-attributes.yaml +21 -0
- data/styles/cornflower/extern.gif +0 -0
- data/styles/cornflower/h3.gif +0 -0
- data/styles/cornflower/menuitem.png +0 -0
- data/styles/cornflower/menuselected.png +0 -0
- data/styles/cornflower/style.css +149 -0
- data/styles/cornflower/tr-edge.png +0 -0
- data/test/test_delicious2yaml.rb +97 -0
- data/wickie +249 -0
- metadata +119 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#$Id: dot_attributes.rb,v 1.1 2005/04/13 21:16:14 wsdng Exp $
|
3
|
+
|
4
|
+
class DotAttributes
|
5
|
+
attr_accessor(:node_attributes,
|
6
|
+
:edge_attributes,
|
7
|
+
:graph_attributes)
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@node_attributes = {
|
11
|
+
'shape' => 'box',
|
12
|
+
'style' => 'filled',
|
13
|
+
'height' => '0.2',
|
14
|
+
'width' => '1',
|
15
|
+
#'fixedsize' => 'true',
|
16
|
+
'fontname' => 'Arial',
|
17
|
+
'fontsize' => '10',
|
18
|
+
'fontcolor' => 'blue',
|
19
|
+
'color' => 'orange',
|
20
|
+
'fillcolor' => 'orange'
|
21
|
+
}
|
22
|
+
@edge_attributes = {}
|
23
|
+
@graph_attributes = {
|
24
|
+
'bgcolor' => 'firebrick',
|
25
|
+
'rankdir' => 'LR',
|
26
|
+
'ranksep' => '0.2',
|
27
|
+
'nodesep' => '0.1',
|
28
|
+
'orientation' => 'portrait',
|
29
|
+
'labelloc' => 'top',
|
30
|
+
'label' => 'Tag Relations',
|
31
|
+
'name' => 'UrlGraph'
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Require this file will set GRAPHICLIOUS_PATH to an appropriate value.
|
4
|
+
#
|
5
|
+
# The standard method is to find the installed gem and let
|
6
|
+
# GRAPHICLIOUS_PATH point to the gem's installation directory.
|
7
|
+
#
|
8
|
+
# If the gem isn't found, we assume that Graphiclious has been started
|
9
|
+
# from within the graphiclious directory itself and therefore set
|
10
|
+
# GRAPHICLIOUS_PATH = '.'. (This is often the case during development.)
|
11
|
+
#
|
12
|
+
# Note that GRAPHICLIOUS_PATH uses the Ruby notation of the path.
|
13
|
+
#
|
14
|
+
# So, what ist this good for? We need to find the style directories
|
15
|
+
# provided with the gem to make those styles availiable without any
|
16
|
+
# additional user interaction. We might use the path for finding icons
|
17
|
+
# or other user-independent ressource files, too.
|
18
|
+
|
19
|
+
begin
|
20
|
+
require 'rubygems'
|
21
|
+
require 'graphiclious/version.rb'
|
22
|
+
|
23
|
+
# Try to find lib directory of this specific Graphiclious gem version
|
24
|
+
def findGraphicliousDirectory
|
25
|
+
graphicliousGemDir = "Graphiclious-#{Graphiclious.version}"
|
26
|
+
all = Gem::path.collect { |gempath|
|
27
|
+
File.join(gempath, 'gems', graphicliousGemDir)
|
28
|
+
}.select { |f|
|
29
|
+
File.directory?(f)
|
30
|
+
}
|
31
|
+
all.empty? ? '.' : all.first
|
32
|
+
end
|
33
|
+
GRAPHICLIOUS_PATH = findGraphicliousDirectory
|
34
|
+
rescue LoadError
|
35
|
+
GRAPHICLIOUS_PATH = '.'
|
36
|
+
end
|
@@ -0,0 +1,260 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#$Id: gui.rb,v 1.18 2006/03/08 20:28:47 wsdng Exp $
|
3
|
+
|
4
|
+
require 'graphiclious/require_fox'
|
5
|
+
require 'graphiclious/delicious2yaml'
|
6
|
+
require 'graphiclious/yaml-links2html'
|
7
|
+
|
8
|
+
class GraphicliousMainWindow < Fox::FXMainWindow
|
9
|
+
include Fox
|
10
|
+
include Responder
|
11
|
+
|
12
|
+
def initialize(app, user=nil, password=nil, style=nil)
|
13
|
+
super(app, "Graphiclious")
|
14
|
+
@user = user
|
15
|
+
@password = password
|
16
|
+
@style = style
|
17
|
+
@stylesFolder = YamlLinksToHtml::STYLES_FOLDER
|
18
|
+
@workingDir = Dir.getwd
|
19
|
+
initView
|
20
|
+
updateUserList
|
21
|
+
updateStylesList
|
22
|
+
setOptionDelicious(false)
|
23
|
+
connect(SEL_CLOSE, method(:onCmdQuit))
|
24
|
+
end
|
25
|
+
|
26
|
+
def newButton(frame, txt)
|
27
|
+
b = FXButton.new(frame, txt)
|
28
|
+
b.setLayoutHints(LAYOUT_LEFT)
|
29
|
+
b.width = 100
|
30
|
+
b.height = 24
|
31
|
+
b
|
32
|
+
end
|
33
|
+
|
34
|
+
def newCheckBox(frame, txt)
|
35
|
+
cbFlags = ICON_BEFORE_TEXT|LAYOUT_SIDE_TOP
|
36
|
+
cb = FXCheckButton.new(frame, txt, nil, 0, cbFlags)
|
37
|
+
cb.setCheck(true)
|
38
|
+
cb
|
39
|
+
end
|
40
|
+
|
41
|
+
def newFrame(frame)
|
42
|
+
frame.setFrameStyle(FRAME_NONE)
|
43
|
+
frame.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X)
|
44
|
+
frame
|
45
|
+
end
|
46
|
+
|
47
|
+
def noPaddingForFrame(frame)
|
48
|
+
frame.padBottom = 0
|
49
|
+
frame.padLeft = 0
|
50
|
+
frame.padRight = 0
|
51
|
+
frame.padTop = 0
|
52
|
+
end
|
53
|
+
|
54
|
+
def initView
|
55
|
+
basicFrame = newFrame(FXVerticalFrame.new(self))
|
56
|
+
basicFrame.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_Y)
|
57
|
+
|
58
|
+
# input user
|
59
|
+
userAndPasswordFrame = newFrame(FXMatrix.new(basicFrame, 2, MATRIX_BY_COLUMNS))
|
60
|
+
userAndPasswordFrame.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
|
61
|
+
FXLabel.new(userAndPasswordFrame, "Working directory:")
|
62
|
+
wdFrame = newFrame(FXHorizontalFrame.new(userAndPasswordFrame))
|
63
|
+
wdFrame.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
|
64
|
+
noPaddingForFrame(wdFrame)
|
65
|
+
@workingDirTextField = FXTextField.new(wdFrame,
|
66
|
+
32, nil, 0,
|
67
|
+
TEXTFIELD_NORMAL)
|
68
|
+
@workingDirTextField.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
|
69
|
+
@workingDirTextField.setText(@workingDir)
|
70
|
+
wdChooseButton = newButton(wdFrame, '..')
|
71
|
+
wdChooseButton.connect(SEL_COMMAND, method(:onChooseWorkingDir))
|
72
|
+
|
73
|
+
FXLabel.new(userAndPasswordFrame, "Enter User:")
|
74
|
+
@userTextField = newCombobox(userAndPasswordFrame, 32,
|
75
|
+
10.size,
|
76
|
+
nil, 0, COMBOBOX_INSERT_LAST)
|
77
|
+
@userTextField.setFrameStyle(FRAME_SUNKEN|FRAME_THICK)
|
78
|
+
@userTextField.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
|
79
|
+
|
80
|
+
# input password
|
81
|
+
FXLabel.new(userAndPasswordFrame, "Password:")
|
82
|
+
@passwordTextField = FXTextField.new(userAndPasswordFrame,
|
83
|
+
32, nil, 0,
|
84
|
+
TEXTFIELD_NORMAL|TEXTFIELD_PASSWD)
|
85
|
+
@passwordTextField.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
|
86
|
+
@passwordTextField.setText(@password) if @password
|
87
|
+
|
88
|
+
# input style
|
89
|
+
FXLabel.new(userAndPasswordFrame, "Style:")
|
90
|
+
@styleTextField = newCombobox(userAndPasswordFrame, 32,
|
91
|
+
10,
|
92
|
+
nil, 0, COMBOBOX_INSERT_LAST)
|
93
|
+
@styleTextField.setFrameStyle(FRAME_SUNKEN|FRAME_THICK)
|
94
|
+
@styleTextField.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
|
95
|
+
|
96
|
+
# set actions
|
97
|
+
|
98
|
+
optionsFrame = newFrame(FXMatrix.new(basicFrame, 2, MATRIX_BY_COLUMNS))
|
99
|
+
optionsFrame.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
|
100
|
+
|
101
|
+
@checkFetch = newCheckBox(optionsFrame, 'Fetch links from del.icio.us')
|
102
|
+
fetchFrame = newFrame(FXVerticalFrame.new(optionsFrame))
|
103
|
+
noPaddingForFrame(fetchFrame)
|
104
|
+
fetchFrame.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
|
105
|
+
|
106
|
+
@fetchMode = newCombobox(fetchFrame, 10,
|
107
|
+
Delicious2Yaml::MODES.size,
|
108
|
+
nil, 0, COMBOBOX_STATIC)
|
109
|
+
@fetchMode.setFrameStyle(FRAME_SUNKEN|FRAME_THICK)
|
110
|
+
modeKeys = Delicious2Yaml::MODES.keys.sort {|m1, m2| m1.to_s <=> m2.to_s}
|
111
|
+
modeKeys.each { |m| @fetchMode.appendItem(Delicious2Yaml::MODES[m]) }
|
112
|
+
@fetchMode.setText(Delicious2Yaml::MODES[modeKeys.first])
|
113
|
+
@fetchMode.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
|
114
|
+
|
115
|
+
@checkGenerate = newCheckBox(optionsFrame, 'Generate HTML')
|
116
|
+
generateFrame = newFrame(FXVerticalFrame.new(optionsFrame))
|
117
|
+
noPaddingForFrame(generateFrame)
|
118
|
+
generateFrame.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
|
119
|
+
|
120
|
+
@checkDelicious = newCheckBox(generateFrame, 'Link to del.icio.us')
|
121
|
+
@checkBundles = newCheckBox(generateFrame, 'Include bundles')
|
122
|
+
@checkGraphViews = newCheckBox(generateFrame, 'Include graph views')
|
123
|
+
@checkGraphThumbs = newCheckBox(generateFrame, 'Graph thumbnails')
|
124
|
+
includePrivateFrame = newFrame(FXHorizontalFrame.new(generateFrame))
|
125
|
+
noPaddingForFrame(includePrivateFrame)
|
126
|
+
includePrivateFrame.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
|
127
|
+
@checkIncludePrivate = newCheckBox(includePrivateFrame, 'Include private?')
|
128
|
+
@checkReallyIncludePrivate = newCheckBox(includePrivateFrame, 'Really?')
|
129
|
+
@checkIncludePrivate.setCheck(false)
|
130
|
+
@checkReallyIncludePrivate.setCheck(false)
|
131
|
+
|
132
|
+
# buttons
|
133
|
+
buttonFrame = newFrame(FXHorizontalFrame.new(basicFrame))
|
134
|
+
buttonFrame.setPackingHints(PACK_UNIFORM_WIDTH)
|
135
|
+
okButton = newButton(buttonFrame, 'Ok')
|
136
|
+
okButton.connect(SEL_COMMAND, method(:onCmdOk))
|
137
|
+
cancelButton = newButton(buttonFrame, 'Cancel')
|
138
|
+
cancelButton.connect(SEL_COMMAND, method(:onCmdQuit))
|
139
|
+
|
140
|
+
@cmdOutput = FXText.new(basicFrame, nil, 0,
|
141
|
+
TEXT_READONLY|TEXT_WORDWRAP|TEXT_SHOWACTIVE)
|
142
|
+
@cmdOutput.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_Y)
|
143
|
+
end
|
144
|
+
|
145
|
+
def setOptionFetch(bool)
|
146
|
+
@checkFetch.setCheck(bool ? true : false)
|
147
|
+
end
|
148
|
+
|
149
|
+
def setOptionGenerate(bool)
|
150
|
+
@checkGenerate.setCheck(bool ? true : false)
|
151
|
+
end
|
152
|
+
|
153
|
+
def setOptionDelicious(bool)
|
154
|
+
@checkDelicious.setCheck(bool ? true : false)
|
155
|
+
end
|
156
|
+
|
157
|
+
def setOptionBundles(bool)
|
158
|
+
@checkBundles.setCheck(bool ? true : false)
|
159
|
+
end
|
160
|
+
|
161
|
+
def setOptionGraphViews(bool)
|
162
|
+
@checkGraphViews.setCheck(bool ? true : false)
|
163
|
+
end
|
164
|
+
|
165
|
+
def setOptionGraphThumbs(bool)
|
166
|
+
@checkGraphThumbs.setCheck(bool ? true : false)
|
167
|
+
end
|
168
|
+
|
169
|
+
def create
|
170
|
+
resize(340, 400)
|
171
|
+
place(PLACEMENT_SCREEN)
|
172
|
+
super
|
173
|
+
show
|
174
|
+
end
|
175
|
+
|
176
|
+
def appendText(txt)
|
177
|
+
@cmdOutput.appendText(txt, true)
|
178
|
+
@cmdOutput.makePositionVisible(@cmdOutput.text.size)
|
179
|
+
@cmdOutput.repaint
|
180
|
+
end
|
181
|
+
|
182
|
+
def updateUserList
|
183
|
+
cachedUsers = YamlLinksToHtml.guess_users_within(@workingDir).collect {
|
184
|
+
|userDir|
|
185
|
+
File.basename(userDir)
|
186
|
+
}
|
187
|
+
@userTextField
|
188
|
+
cachedUsers.each { |m| @userTextField.appendItem(m) }
|
189
|
+
@userTextField.setText(@user ? @user : cachedUsers.first)
|
190
|
+
end
|
191
|
+
|
192
|
+
def updateStylesList
|
193
|
+
@styleTextField.clearItems
|
194
|
+
[@workingDir, @stylesFolder].each do
|
195
|
+
|folder|
|
196
|
+
YamlLinksToHtml.guess_styles_within(folder).each do
|
197
|
+
|styleDir|
|
198
|
+
@styleTextField.appendItem(styleDir)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
@styleTextField.setText(@style ? @style : "")
|
202
|
+
end
|
203
|
+
|
204
|
+
def onChooseWorkingDir(sender, sel, ptr)
|
205
|
+
newFolder = FXDirDialog.getOpenDirectory(self, "Choose working directory", @workingDir)
|
206
|
+
return if newFolder.nil?
|
207
|
+
@workingDir = newFolder.gsub(/\\/, File::SEPARATOR)
|
208
|
+
@workingDirTextField.setText(@workingDir)
|
209
|
+
updateUserList
|
210
|
+
updateStylesList
|
211
|
+
end
|
212
|
+
|
213
|
+
def onCmdOk(sender, sel, ptr)
|
214
|
+
@user = @userTextField.getText
|
215
|
+
@password = @passwordTextField.getText
|
216
|
+
@style = @styleTextField.getText
|
217
|
+
@workingDir = @workingDirTextField.getText
|
218
|
+
mode = Delicious2Yaml::MODES.index(@fetchMode.getText)
|
219
|
+
outputBlock = proc { |line| appendText("#{line}\n") }
|
220
|
+
appendText("-Start-\n")
|
221
|
+
begin
|
222
|
+
getApp.beginWaitCursor
|
223
|
+
if @checkFetch.checked?
|
224
|
+
appendText("-Start import-\n")
|
225
|
+
importer = Delicious2Yaml.new(@user, @password)
|
226
|
+
importer.set_protocol_block(outputBlock)
|
227
|
+
importer.set_working_dir(@workingDir)
|
228
|
+
importer.set_mode(mode)
|
229
|
+
importer.run
|
230
|
+
setOptionFetch(false)
|
231
|
+
appendText("-Done import-\n")
|
232
|
+
end
|
233
|
+
if @checkGenerate.checked?
|
234
|
+
appendText("-Start generation-\n")
|
235
|
+
exporter = YamlLinksToHtml.new
|
236
|
+
exporter.set_protocol_block(outputBlock)
|
237
|
+
exporter.set_working_dir(@workingDir)
|
238
|
+
exporter.set_user(@user)
|
239
|
+
exporter.set_style(@style)
|
240
|
+
exporter.link_to_delicious_if_possible = @checkDelicious.checked?
|
241
|
+
exporter.use_bundles = @checkBundles.checked?
|
242
|
+
exporter.build_graph_thumbs = @checkGraphThumbs.checked?
|
243
|
+
exporter.include_graph_views = @checkGraphViews.checked?
|
244
|
+
exporter.include_private = @checkIncludePrivate.checked? && @checkReallyIncludePrivate.checked?
|
245
|
+
exporter.run
|
246
|
+
appendText("-Done generation-\n")
|
247
|
+
end
|
248
|
+
appendText("-Done-\n")
|
249
|
+
rescue StandardError => bang
|
250
|
+
appendText("Fatal error: " + bang + "\n-\n")
|
251
|
+
ensure
|
252
|
+
getApp.endWaitCursor
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
256
|
+
|
257
|
+
def onCmdQuit(sender, sel, ptr)
|
258
|
+
getApp.exit(0)
|
259
|
+
end
|
260
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#$Id: help.rb,v 1.3 2005/11/26 14:26:21 wsdng Exp $
|
3
|
+
|
4
|
+
module Graphiclious
|
5
|
+
def self.help
|
6
|
+
h = String.new
|
7
|
+
h <<
|
8
|
+
"Commandline options:\n" <<
|
9
|
+
"--bundles/-b include bundles\n" <<
|
10
|
+
"--delicious/-d prefer links to del.icio.us\n" <<
|
11
|
+
"--gui/-g will start a graphical user interface\n" <<
|
12
|
+
"--help/-h will display this text\n" <<
|
13
|
+
"--noupdate/-l do not connect to del.icio.us\n" <<
|
14
|
+
"--nohtml/-o ommit html generation\n" <<
|
15
|
+
"--password/-p specify password for given user\n" <<
|
16
|
+
"--user/-u specify a del.icio.us user\n" <<
|
17
|
+
"--version/-v prints actual version\n" <<
|
18
|
+
"Without gui option you'll need to specify user and\n" <<
|
19
|
+
"password."
|
20
|
+
h
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#$Id: url_graph.rb,v 1.8 2006/03/09 22:57:52 wsdng Exp $
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubygems'
|
6
|
+
require_gem 'rgl'
|
7
|
+
rescue LoadError
|
8
|
+
end
|
9
|
+
require 'rgl/adjacency'
|
10
|
+
require 'rgl/dot'
|
11
|
+
require 'rgl/implicit'
|
12
|
+
require 'graphiclious/dot_attributes'
|
13
|
+
|
14
|
+
# UrlGraph models relations between tags
|
15
|
+
|
16
|
+
class UrlGraph < RGL::ImplicitGraph
|
17
|
+
|
18
|
+
attr_accessor(:fixed_dot_attributes, :filter_set)
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@fixed_dot_attributes = DotAttributes.new
|
22
|
+
@wrapped_graph = RGL::AdjacencyGraph.new
|
23
|
+
@filter = proc { |v| @filter_set.nil? || @filter_set.include?(v) }
|
24
|
+
@filter_set = nil
|
25
|
+
super { |g|
|
26
|
+
g.vertex_iterator { |b|
|
27
|
+
@wrapped_graph.each_vertex { |v| b.call(v) if @filter.call(v) }
|
28
|
+
}
|
29
|
+
g.adjacent_iterator { |v, b|
|
30
|
+
@wrapped_graph.each_adjacent(v) { |u| b.call(u) if @filter.call(u) }
|
31
|
+
}
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_vertex(vertex)
|
36
|
+
@wrapped_graph.add_vertex(vertex)
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_edge(tag, otherTag)
|
40
|
+
@wrapped_graph.add_edge(tag, otherTag)
|
41
|
+
end
|
42
|
+
|
43
|
+
# some characters are not allowed in filenames and need to be replaced
|
44
|
+
def self.tag2file(tag)
|
45
|
+
# Windows: \/:*?"<>|
|
46
|
+
# Linux: ()
|
47
|
+
# Call to Graphviz: Space
|
48
|
+
tag.gsub(/[\\\/:*?"<>|() ]/, '_')
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.ankor(url, name)
|
52
|
+
"<a class=\"extern\" href=\"#{url}\" target=\"_top\">#{name}</a>"
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.intern_ankor(url, name, style=nil)
|
56
|
+
link = "<a href=\"#{tag2file(url)}\">#{name}</a>"
|
57
|
+
link = "<span class=\"#{style}\">#{link}</span>" unless style.nil?
|
58
|
+
link
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.node_url(tag)
|
62
|
+
"#{tag2file(tag)}.htm"
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.node_ankor(tag, style=nil)
|
66
|
+
intern_ankor(node_url(tag), tag, style)
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.edge_name(tag1, tag2)
|
70
|
+
if tag1 <= tag2
|
71
|
+
tag1 + '-' + tag2
|
72
|
+
else
|
73
|
+
tag2 + '-' + tag1
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.edge_url(tag1, tag2)
|
78
|
+
node_url(edge_name(tag1, tag2))
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.edge_ankor(tag1, tag2, style=nil)
|
82
|
+
intern_ankor(edge_url(tag1, tag2), edge_name(tag1, tag2), style)
|
83
|
+
end
|
84
|
+
|
85
|
+
def fixed_dot_node_attributes
|
86
|
+
fixed_dot_attributes.node_attributes
|
87
|
+
end
|
88
|
+
|
89
|
+
def fixed_dot_edge_attributes
|
90
|
+
fixed_dot_attributes.edge_attributes
|
91
|
+
end
|
92
|
+
|
93
|
+
def fixed_dot_graph_attributes
|
94
|
+
fixed_dot_attributes.graph_attributes
|
95
|
+
end
|
96
|
+
|
97
|
+
def d_quote(string)
|
98
|
+
"\"#{string}\""
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_dot_graph(params = {})
|
102
|
+
fixed_dot_graph_attributes.each { |k, v|
|
103
|
+
params[k] ||= v
|
104
|
+
}
|
105
|
+
graph = (directed? ? DOT::DOTDigraph : DOT::DOTSubgraph).new(params)
|
106
|
+
edge_class = directed? ? DOT::DOTDirectedEdge : DOT::DOTEdge
|
107
|
+
dot_node_attributes = fixed_dot_node_attributes.clone
|
108
|
+
each_vertex do |v|
|
109
|
+
name = v.to_s
|
110
|
+
dot_node_attributes['name'] = d_quote(name)
|
111
|
+
dot_node_attributes['URL'] = d_quote(UrlGraph.node_url(name))
|
112
|
+
dot_node_attributes['label'] = name
|
113
|
+
graph << DOT::DOTNode.new(dot_node_attributes)
|
114
|
+
end
|
115
|
+
dot_edge_attributes = fixed_dot_edge_attributes.clone
|
116
|
+
each_edge do |u,v|
|
117
|
+
tag1 = u.to_s
|
118
|
+
tag2 = v.to_s
|
119
|
+
dot_edge_attributes['from'] = d_quote(tag1)
|
120
|
+
dot_edge_attributes['to'] = d_quote(tag2)
|
121
|
+
dot_edge_attributes['URL'] = d_quote(UrlGraph.edge_url(tag1, tag2))
|
122
|
+
graph << edge_class.new(dot_edge_attributes)
|
123
|
+
end
|
124
|
+
graph
|
125
|
+
end
|
126
|
+
|
127
|
+
# Output the DOT-graph to a file.
|
128
|
+
|
129
|
+
def print_dotted_on_file(filename = 'graph.dot', params = {})
|
130
|
+
File.open(filename, "w") {|f|
|
131
|
+
print_dotted_on(params, f)
|
132
|
+
}
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|