libguib 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/src/fxobjects.rb ADDED
@@ -0,0 +1,253 @@
1
+ # Copyright (c) 2004-2006 by Henon (meinrad dot recheis at gmail dot com)
2
+
3
+ if __FILE__ == $0
4
+ require "fox"
5
+ end
6
+
7
+ def loadImageToString filename
8
+ data = nil
9
+ File.open(filename, "rb") { |f| data = f.read }
10
+ data.unpack1("h#{data.size * 2}")
11
+ end
12
+
13
+ def loadImageFromString s, icon_class = Fox::FXPNGIcon
14
+ raise TypeError unless $fxapp
15
+ imgdata = [s].pack("h#{s.size * 2}")
16
+ img = icon_class.new($fxapp, imgdata, Fox::IMAGE_KEEP | Fox::IMAGE_SHMI | Fox::IMAGE_SHMP)
17
+ img.create
18
+ img
19
+ end
20
+
21
+ def load_dummy
22
+ raise TypeError unless $fxapp
23
+ imgdata = "9805e474d0a0a1a0000000d0948444250000000100000001803000000082d2f035000000a247548547342756164796f6e6024596d65600d4f60293026456260223030343021363a33363a3133302b2031303037c63ded4000000704794d454704d2090f0627041559b9c00000090078495370000b0210000b021102ddde7cf000000407614d41400001bf8b0cf16500000003305c44554000000ffefeffff0f0ffc1c1ffcbcbff7878ff5b5bffc5c5ff7979ff9292ffadadff2f2fff7171ff4747ffb4b4ff6e6eff8383bfe98b0d000000104725e43500046e8d66000000b59444144587ad58ec1ee008028040069333b223d7ff7adec6a39c5b57f38d8f406a3992ea61d0508294b382bda9d373840c595953630f0c1c930ece73e940ee8506f8dc0446f14600fddfa260877711b0c50971c4f5ff898f7819b1678020e2a25402a2000000009454e444ea240628"
24
+ imgdata = [imgdata].pack("h#{imgdata.size * 2}")
25
+ $dummy_img = img = Fox::FXPNGIcon.new($fxapp, imgdata, Fox::IMAGE_KEEP | Fox::IMAGE_SHMI | Fox::IMAGE_SHMP)
26
+ img.create
27
+ img
28
+ end
29
+
30
+ def hasExtension(filename, ext)
31
+ File.basename(filename, ext) != File.basename(filename)
32
+ end
33
+
34
+ # Load the named image file
35
+ def loadImage(file)
36
+ img = load_dummy unless $dummy_img
37
+
38
+ unless File.exist? file
39
+ return img
40
+ end
41
+ begin
42
+ opts = Fox::IMAGE_KEEP | Fox::IMAGE_SHMI | Fox::IMAGE_SHMP
43
+ if hasExtension(file, ".gif")
44
+ img = Fox::FXGIFIcon.new($fxapp, nil, opts)
45
+ elsif hasExtension(file, ".bmp")
46
+ img = Fox::FXBMPIcon.new($fxapp, nil, opts)
47
+ elsif hasExtension(file, ".xpm")
48
+ img = Fox::FXXPMIcon.new($fxapp, nil, opts)
49
+ elsif hasExtension(file, ".png")
50
+ img = Fox::FXPNGIcon.new($fxapp, nil, opts)
51
+ elsif hasExtension(file, ".jpg")
52
+ img = Fox::FXJPGIcon.new($fxapp, nil, opts)
53
+ elsif hasExtension(file, ".pcx")
54
+ img = Fox::FXPCXIcon.new($fxapp, nil, opts)
55
+ elsif hasExtension(file, ".tif")
56
+ img = Fox::FXTIFIcon.new($fxapp, nil, opts)
57
+ elsif hasExtension(file, ".tga")
58
+ img = Fox::FXTGAIcon.new($fxapp, nil, opts)
59
+ elsif hasExtension(file, ".ico")
60
+ img = Fox::FXICOIcon.new($fxapp, nil, opts)
61
+ end
62
+ unless img
63
+ puts("Unsupported image type: #{file}")
64
+ return img
65
+ end
66
+ Fox::FXFileStream.open(file, Fox::FXStreamLoad) { |stream| img.loadPixels(stream) }
67
+ img.filename = file
68
+ img.create
69
+ rescue Exception
70
+ puts "load Image: #{$!}"
71
+ end
72
+ img
73
+ end
74
+
75
+ module FX
76
+ class Icon
77
+ def initialize filename
78
+ if filename
79
+ @filename = filename
80
+ @img = loadImage(filename)
81
+ end
82
+ end
83
+ attr_accessor :img
84
+
85
+ def to_s
86
+ @filename
87
+ end
88
+
89
+ def self.LoadFromString s, icon_class = Fox::FXPNGIcon
90
+ icon = Icon.new nil
91
+ icon.img = loadImageFromString s, icon_class
92
+ icon
93
+ end
94
+ end
95
+
96
+ # color object (united fox functions)
97
+ class Color
98
+ attr_accessor :r, :g, :b, :a
99
+ # construct by red, green, blue and (optionally) alpha value
100
+ def initialize r = 0, g = 0, b = 0, a = nil
101
+ @r, @g, @b, @a = r, g, b, a
102
+ end
103
+
104
+ # returns Fox::FXColor value
105
+ def to_FXColor
106
+ @a ? Fox::FXRGBA(@r, @g, @b, @a) : Fox::FXRGB(@r, @g, @b)
107
+ end
108
+
109
+ # get value from FXColor
110
+ def from_FXColor c
111
+ @r = Fox::FXREDVAL(c)
112
+ @g = Fox::FXGREENVAL(c)
113
+ @b = Fox::FXBLUEVAL(c)
114
+ @a = Fox::FXALPHAVAL(c)
115
+ self
116
+ end
117
+
118
+ # get value according to Fox colorname
119
+ def from_name(name)
120
+ from_FXColor(Fox.fxcolorfromname(name))
121
+ self
122
+ end
123
+
124
+ # encode binary string representation
125
+ def serialize
126
+ Fox.fxencodeColorData(to_FXColor)
127
+ end
128
+
129
+ # decode from binary string representation
130
+ def deserialize(data)
131
+ from_FXColor(Fox.fxdecodeColorData(data))
132
+ self
133
+ end
134
+
135
+ # returns human readable string representation
136
+ def to_s
137
+ (@a ? [@r, @g, @b, @a] : [@r, @g, @b]).join(",")
138
+ end
139
+
140
+ # reads value from human readable string representation
141
+ def from_s s
142
+ s = "0, 0, 0, 0" if s.size < 5
143
+ @r, @g, @b, @a = s.split(",").collect { |c| c.to_i }
144
+ self
145
+ end
146
+ end
147
+
148
+ # font object. cannot be substituted for FXFont
149
+ class Font
150
+ # param is either a Fox::FXFontDesc or a string representation
151
+ def initialize
152
+ @fd = Fox::FXFontDesc.new
153
+ end
154
+ attr_accessor :fd
155
+ # human readable string representation
156
+ def to_s
157
+ @fd.to_s
158
+ end
159
+
160
+ def from_s s
161
+ @fd.from_s s
162
+ self
163
+ end
164
+
165
+ def to_FXFont
166
+ f = Fox::FXFont.new $fxapp, @fd
167
+ f.create
168
+ f
169
+ end
170
+
171
+ def from_FXFont f
172
+ @fd = f.fontDesc
173
+ self
174
+ end
175
+ end
176
+ end # module
177
+
178
+ class Fox::FXIcon
179
+ attr_accessor :filename
180
+ def to_s
181
+ filename || ""
182
+ end
183
+ end
184
+
185
+ class Fox::FXFontDesc
186
+ # ~ alias :_initialize_ :initialize
187
+ # ~ def initialize
188
+ # ~ _initialize_
189
+ # ~ from_s "Tahoma|80|400|1|0|0|0"
190
+ # ~ end
191
+ # human readable string representation
192
+ def to_s
193
+ [face,
194
+ size,
195
+ weight,
196
+ slant,
197
+ encoding,
198
+ setwidth,
199
+ flags].join("|")
200
+ end
201
+
202
+ # parse human readable string representation
203
+ def from_s(s)
204
+ # ~ begin
205
+ a = s.split("|")
206
+ self.face = a[0]
207
+ self.size = a[1].to_i
208
+ self.weight = a[2].to_i
209
+ self.slant = a[3].to_i
210
+ self.encoding = a[4].to_i
211
+ self.setwidth = a[5].to_i
212
+ self.flags = a[6].to_i
213
+ # ~ rescue Exception
214
+ # ~ puts "error parsing string representation: #{$!}"
215
+ # ~ puts $!.backtrace.join($/)
216
+ # ~ return nil
217
+ # ~ end
218
+ self
219
+ end
220
+
221
+ # initialize from other font desc or string representation
222
+ def init fd
223
+ if fd.is_a? Fox::FXFontDesc
224
+ self.face = fd.face
225
+ self.size = fd.size
226
+ self.weight = fd.weight
227
+ self.slant = fd.slant
228
+ self.encoding = fd.encoding
229
+ self.setwidth = fd.setwidth
230
+ self.flags = fd.flags
231
+ elsif fd.is_a? String
232
+ from_s fd
233
+ end
234
+ end
235
+ end
236
+
237
+ class Fox::FXFont
238
+ def to_s
239
+ fontDesc.to_s
240
+ end
241
+ end
242
+
243
+ class Fox::FXIcon
244
+ attr_accessor :filename
245
+ def to_s
246
+ filename || ""
247
+ end
248
+ end
249
+
250
+ if __FILE__ == $0
251
+ s = loadImageToString "icon_not_found.png"
252
+ puts s == "9805e474d0a0a1a0000000d0948444250000000100000001803000000082d2f035000000a247548547342756164796f6e6024596d65600d4f60293026456260223030343021363a33363a3133302b2031303037c63ded4000000704794d454704d2090f0627041559b9c00000090078495370000b0210000b021102ddde7cf000000407614d41400001bf8b0cf16500000003305c44554000000ffefeffff0f0ffc1c1ffcbcbff7878ff5b5bffc5c5ff7979ff9292ffadadff2f2fff7171ff4747ffb4b4ff6e6eff8383bfe98b0d000000104725e43500046e8d66000000b59444144587ad58ec1ee008028040069333b223d7ff7adec6a39c5b57f38d8f406a3992ea61d0508294b382bda9d373840c595953630f0c1c930ece73e940ee8506f8dc0446f14600fddfa260877711b0c50971c4f5ff898f7819b1678020e2a25402a2000000009454e444ea240628"
253
+ end
Binary file
@@ -0,0 +1 @@
1
+
data/src/make.rb ADDED
@@ -0,0 +1,196 @@
1
+ # Copyright (c) 2004-2006 by Henon (meinrad dot recheis at gmail dot com)
2
+
3
+ # spag/hettizer:
4
+ # packs everything into one executable ruby script
5
+ STDOUT.sync = true
6
+
7
+ $program_src_dir = "./"
8
+ $program_release_dir = "../"
9
+ $program_file = "__FX__.rb"
10
+ $output_file = "lib/libGUIb16.rb"
11
+ $zip = false
12
+
13
+ $DEBUG = false
14
+
15
+ $debugstring = '"##{$program_working_dir}/#{filename}:#{i}: "'
16
+
17
+ $exclusion_names = [ # won't be inserted
18
+ /paths/,
19
+ /fox(\/)?/,
20
+ /etc\.so/,
21
+ /properties/,
22
+ /code-gen/,
23
+ /^FX/,
24
+ /fxruby/
25
+ ]
26
+ $force_require = [ # files that are not implicitely required can be forced to be "included" here
27
+ # "FileSelector-extension",
28
+ "PathSelector-extension"
29
+ ]
30
+ $required_files = [] # will be required bevore generation of the release script
31
+ $files_to_be_copied = [ # will be copied to release dir; $V marks directory
32
+ ]
33
+ $require_paths = ["", "FX"]
34
+
35
+ $path_replacements = {
36
+ # "pp"=>ENV["RUBY_HOME"]+"/..todo../pp"
37
+ }
38
+
39
+ # ####################################
40
+
41
+ def prepare
42
+ Dir.chdir($program_src_dir)
43
+ $required_files.each { |file|
44
+ require file
45
+ }
46
+
47
+ $required_files = []
48
+ end
49
+
50
+ def winPath(path)
51
+ path.tr("/", "\\")
52
+ end
53
+
54
+ def check_exclusions filename
55
+ $exclusion_names.each { |regex|
56
+ if filename&.match?(regex)
57
+ puts "#### excluded: #{filename}"
58
+ return true
59
+ end
60
+ }
61
+ false
62
+ end
63
+
64
+ $inside_unittest_block = false
65
+
66
+ def check_exist filename
67
+ if File.exist?(filename)
68
+ filename
69
+ else
70
+ $require_paths.each { |path|
71
+ newname = File.join path, filename
72
+ return newname if File.exist? newname
73
+ }
74
+ raise "! File not found: " + filename
75
+ end
76
+ end
77
+
78
+ # simulates require
79
+ def fill_in f, require_string
80
+ filename = eval(require_string)
81
+ return unless filename
82
+ filename += ".rb" unless /(\.rb$)|(\.rbw$)/.match?(filename)
83
+ if check_exclusions(filename)
84
+ f.print "###### excluded: \n" if $DEBUG
85
+ f.print "require " + require_string, "\n"
86
+ return
87
+ end
88
+ f.print "# require " + filename, "\n" if $DEBUG
89
+ puts "REQUIRE-STRING: #{require_string}"
90
+ filename = check_exist(filename)
91
+ return if $required_files.member? filename
92
+ puts "\tFILENAME: #{filename}"
93
+ $required_files << filename
94
+ f1 = File.open(filename, "r")
95
+ i = 0
96
+ for line in f1.readlines
97
+ i += 1
98
+ if $inside_unittest_block and line.strip =~ /end/
99
+ $inside_unittest_block = false
100
+ next
101
+ end
102
+ break if /#\s*MAKE_CUTOFF/.match?(line)
103
+ next if /#\s*MAKE_DROP/.match?(line.strip)
104
+ next if $inside_unittest_block
105
+ next if line.strip.empty?
106
+ next if /^#/.match?(line.strip)
107
+ next if /^;/.match?(line.strip)
108
+ next if /^dbg/.match?(line.strip)
109
+ next if /^assert/.match?(line.strip)
110
+ if /^if __FILE__/.match?(line.strip)
111
+ $inside_unittest_block = true
112
+ next
113
+ end
114
+ next if /\sif __FILE__/.match?(line.strip)
115
+ unless /require(\s|\().*((".*")|('.*'))/.match?(line)
116
+ f.print line.chomp, "\n"
117
+ f.print eval($debugstring), "\n" if $DEBUG and line.strip =~ /end/
118
+ next
119
+ end
120
+ require_string = line.strip.gsub("require", "")
121
+ fill_in f, require_string
122
+ end
123
+ end
124
+
125
+ def make_dir dir
126
+ Dir.mkdir dir
127
+ rescue Exception
128
+ puts "mkdir: " + $!
129
+ end
130
+
131
+ def delete_release
132
+ cmd = "rd /s /q #{winPath($program_release_dir)}"
133
+ puts "> " + cmd
134
+ puts "> " + `#{cmd}`
135
+ make_dir($release_dir)
136
+ end
137
+
138
+ def copy file
139
+ isdir = (file =~ /\$V/)
140
+ if isdir
141
+ file.gsub!("$V", "")
142
+ cmd = "xcopy /I /R #{winPath(file)} #{winPath($program_release_dir + File.basename(file))}"
143
+ else
144
+ cmd = "copy #{winPath(file)} #{winPath($program_release_dir + File.basename(file))}"
145
+ end
146
+ puts "> " + cmd
147
+ puts "\t" + `#{cmd}`
148
+ end
149
+
150
+ def zippe
151
+ f = File.open($program_release_dir + $gz_output_file, "wb")
152
+ g = File.open($program_release_dir + $output_file, "rb")
153
+ f.write(Zlib::Deflate.deflate(g.read, Zlib::BEST_COMPRESSION))
154
+ f.close
155
+ g.close
156
+ end
157
+
158
+ # delete_release
159
+ # make_dir $program_release_dir
160
+
161
+ puts "###### generating #{$output_file}:"
162
+ # klartext output file
163
+ File.open($program_release_dir + $output_file, "wb") { |g|
164
+ g.puts "# libGUIb: Copyright (c) by Meinrad Recheis aka Henon, 2006"
165
+ g.puts "# THIS SOFTWARE IS PROVIDED IN THE HOPE THAT IT WILL BE USEFUL"
166
+ g.puts "# WITHOUT ANY IMPLIED WARRANTY OR FITNESS FOR ANY PURPOSE."
167
+ prepare
168
+ # input
169
+ # ~ File.open( $program_file, "r"){|f|
170
+ # make it
171
+ fill_in g, "'#{$program_file}'"
172
+ $force_require.each { |name| fill_in g, "'#{name}'" }
173
+ # File.open("../build/included_files.rb", "w"){|h|
174
+ # h.puts $required_files.inspect.split.join("\n")
175
+ # }
176
+ puts "###### copying files"
177
+ $files_to_be_copied.each { |fname|
178
+ copy(fname)
179
+ }
180
+ # ~ }
181
+ }
182
+ if $zip
183
+ require "zlib"
184
+ zippe
185
+ end
186
+
187
+ # installing:
188
+ Dir.chdir $program_release_dir
189
+ if PLATFORM.match?(/win32/)
190
+ `ruby install.rb`
191
+ else
192
+ pw = File.read("pw")
193
+ io = IO.popen("sudo -S ruby install.rb", "w")
194
+ sleep 1
195
+ io.puts pw
196
+ end
@@ -0,0 +1,51 @@
1
+ # Copyright (c) 2004-2006 by Henon (meinrad dot recheis at gmail dot com)
2
+
3
+ module MiddleBtn
4
+ def handleMMB_Events
5
+ FXMAPFUNC(Fox::SEL_MIDDLEBUTTONPRESS, 0, :onMiddleBtnPress)
6
+ FXMAPFUNC(Fox::SEL_MIDDLEBUTTONRELEASE, 0, :onMiddleBtnRelease)
7
+ end
8
+
9
+ def onMiddleBtnPress(sender, sel, evt)
10
+ if enabled?
11
+ if !target.nil? && (target.handle(self, MKUINT(selector, Fox::SEL_MIDDLEBUTTONPRESS), evt) != 0)
12
+ return 1
13
+ end
14
+ end
15
+ 0
16
+ end
17
+
18
+ def onMiddleBtnRelease(sender, sel, evt)
19
+ if enabled?
20
+ if !target.nil? && (target.handle(self, MKUINT(selector, Fox::SEL_MIDDLEBUTTONRELEASE), evt) != 0)
21
+ return 1
22
+ end
23
+ end
24
+ 0
25
+ end
26
+ end
27
+
28
+ module RightBtn
29
+ def handleRMB_Events
30
+ FXMAPFUNC(Fox::SEL_MIDDLEBUTTONPRESS, 0, :onMiddleBtnPress)
31
+ FXMAPFUNC(Fox::SEL_MIDDLEBUTTONRELEASE, 0, :onMiddleBtnRelease)
32
+ end
33
+
34
+ def onRightBtnPress(sender, sel, evt)
35
+ if enabled?
36
+ if !target.nil? && (target.handle(self, MKUINT(selector, Fox::SEL_RIGHTBUTTONPRESS), evt) != 0)
37
+ return 1
38
+ end
39
+ end
40
+ 0
41
+ end
42
+
43
+ def onRightBtnRelease(sender, sel, evt)
44
+ if enabled?
45
+ if !target.nil? && (target.handle(self, MKUINT(selector, Fox::SEL_RIGHTBUTTONRELEASE), evt) != 0)
46
+ return 1
47
+ end
48
+ end
49
+ 0
50
+ end
51
+ end
@@ -0,0 +1,46 @@
1
+ # Copyright (c) 2004-2006 by Henon (meinrad dot recheis at gmail dot com)
2
+
3
+ require "event_listener"
4
+
5
+ module FX
6
+ class RadioGroup
7
+ __sends__ :event_selected
8
+ def initialize
9
+ @radio_widgets = []
10
+ @selected = nil
11
+ end
12
+ attr_reader :radio_widgets
13
+ def add_radio(radio_widget, &block)
14
+ @radio_widgets << radio_widget
15
+ radio_widget.connect(Fox::SEL_COMMAND) {
16
+ select(radio_widget)
17
+ yield if block
18
+ }
19
+ end
20
+ alias_method :<<, :add_radio
21
+ def select radio_widget
22
+ @selected = radio_widget
23
+ event_selected @selected
24
+ for w in @radio_widgets
25
+ begin
26
+ w.state = (w == @selected) # only the selected widget is checked.
27
+ rescue Exception
28
+ # this is necessary to prevent a radiomutex from crashing
29
+ # after a radiobutton has been deleted in foxGUIb
30
+ puts $!
31
+ end
32
+ end
33
+ end
34
+
35
+ attr_reader :selected
36
+ end
37
+ end # fx
38
+
39
+ if __FILE__ == $0
40
+ $app = FX::App.new "", ""
41
+ FX::MainWindow.new($app) {
42
+ }
43
+
44
+ $app.create
45
+ $app.run
46
+ end
@@ -0,0 +1,95 @@
1
+ # Copyright (c) 2004-2006 by Henon (meinrad dot recheis at gmail dot com)
2
+
3
+ if __FILE__ == $0
4
+ Dir.chdir ".."
5
+ require "FX"
6
+ end
7
+
8
+ module RadioGroup1
9
+ def RadioGroup1_initialize
10
+ @RadioGroup1_initialized = true
11
+ @RadioGroup1_listeners ||= []
12
+ end
13
+
14
+ def radio_command w
15
+ RadioGroup1_initialize unless @RadioGroup1_initialized
16
+ return if @selected_radio_widget == w
17
+ if @selected_radio_widget
18
+ @selected_radio_widget.set_radio_state false
19
+ end
20
+ @selected_radio_widget = w
21
+ @RadioGroup1_listeners.each { |l|
22
+ l.on_event(@selected_radio_widget)
23
+ }
24
+ end
25
+ attr_accessor :selected_radio_widget, :RadioGroup1_listeners
26
+ end
27
+
28
+ module RadioWidget
29
+ def radio_initialize col = FX::Color.new(255, 255, 255)
30
+ @radio_initialized = true
31
+ @radioBackColor = FX::Color.new.from_FXColor(backColor)
32
+ @radioSelectColor ||= col
33
+ @state = false
34
+ connect(SEL_LEFTBUTTONPRESS, method(:lmb_press))
35
+ end
36
+ attr_accessor :radioSelectColor, :radioSelectColor
37
+ def set_radio_state state
38
+ @state = state
39
+ change_radio_selection
40
+ end
41
+
42
+ def change_radio_selection
43
+ self.backColor = @state ? @radioSelectColor.to_FXColor : @radioBackColor.to_FXColor
44
+ end
45
+ attr_accessor :state, :group, :lmbdown
46
+
47
+ def lmb_press(*args)
48
+ if @group and @group.respond_to?(:radio_command)
49
+ set_radio_state true
50
+ @group.radio_command(self)
51
+ else
52
+ set_radio_state(!@state)
53
+ end
54
+ @lmbdown = true
55
+ 0
56
+ end
57
+ end
58
+
59
+ module FX
60
+ class RadioMatrix < Fox::FXMatrix
61
+ include RadioGroup1
62
+ def initialize(p)
63
+ super
64
+ end
65
+ end
66
+
67
+ class RadioLabel < Label
68
+ include RadioWidget
69
+ def initialize(p)
70
+ super
71
+ radio_initialize
72
+ end
73
+ end
74
+ end
75
+
76
+ # unit test
77
+ if __FILE__ == $0
78
+ $stdout.sync = true
79
+ app = App.new
80
+ w = MainWindow.new app
81
+ RadioMatrix.new(w) { |lm|
82
+ lm.matrixStyle = MATRIX_BY_COLUMNS
83
+ lm.numColumns = 3
84
+ 10.times { |i|
85
+ RadioLabel.new(lm) { |l|
86
+ l.group = lm
87
+ l.text = i.to_s
88
+ l.img = "FX/icon_not_found.png"
89
+ }
90
+ }
91
+ }
92
+ w.show(0)
93
+ app.create
94
+ app.run
95
+ end
@@ -0,0 +1,14 @@
1
+ # Copyright (c) 2004-2006 by Henon (meinrad dot recheis at gmail dot com)
2
+
3
+ def rel_path(a, b)
4
+ raise TypeError unless a.is_a? String and b.is_a? String
5
+ a.tr!("\\", "/")
6
+ b.tr!("\\", "/")
7
+ a = a.split("/")
8
+ b = b.split("/")
9
+ i = 0
10
+ while (a[i] == b[i]) && (i < a.size)
11
+ i += 1
12
+ end
13
+ "../" * (a.size - i) + b[i..-1].join("/")
14
+ end