robust_excel_ole 1.8 → 1.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Changelog +8 -0
- data/README.rdoc +17 -3
- data/docs/README_excel.rdoc +23 -0
- data/docs/README_ranges.rdoc +73 -5
- data/examples/modifying_sheets/example_add_names.rb +57 -0
- data/examples/modifying_sheets/example_adding_sheets.rb +1 -1
- data/examples/open_save_close/example_if_obstructed_closeifsaved.rb +1 -1
- data/examples/open_save_close/example_if_unsaved_forget.rb +1 -1
- data/examples/open_save_close/example_reuse.rb +1 -1
- data/lib/robust_excel_ole.rb +2 -0
- data/lib/robust_excel_ole/address.rb +114 -0
- data/lib/robust_excel_ole/excel.rb +81 -53
- data/lib/robust_excel_ole/general.rb +37 -3
- data/lib/robust_excel_ole/range.rb +20 -20
- data/lib/robust_excel_ole/range_owners.rb +236 -0
- data/lib/robust_excel_ole/reo_common.rb +18 -276
- data/lib/robust_excel_ole/version.rb +1 -1
- data/lib/robust_excel_ole/workbook.rb +177 -91
- data/lib/robust_excel_ole/worksheet.rb +12 -4
- data/reo.bat +1 -1
- data/spec/address_spec.rb +174 -0
- data/spec/data/another_workbook.xls +0 -0
- data/spec/data/different_workbook.xls +0 -0
- data/spec/data/workbook.xls +0 -0
- data/spec/excel_spec.rb +168 -29
- data/spec/reo_common_spec.rb +0 -103
- data/spec/workbook_spec.rb +12 -2
- data/spec/workbook_specs/workbook_close_spec.rb +2 -1
- data/spec/workbook_specs/workbook_misc_spec.rb +41 -7
- data/spec/workbook_specs/workbook_open_spec.rb +8 -3
- data/spec/workbook_specs/workbook_save_spec.rb +5 -3
- data/spec/worksheet_spec.rb +83 -10
- metadata +7 -3
@@ -33,15 +33,49 @@ module General
|
|
33
33
|
|
34
34
|
end
|
35
35
|
|
36
|
+
# @private
|
37
|
+
class Integer
|
38
|
+
|
39
|
+
alias old_spaceship <=>
|
40
|
+
|
41
|
+
def <=> other
|
42
|
+
# p other
|
43
|
+
if other.is_a? Array
|
44
|
+
self <=> other.first
|
45
|
+
else
|
46
|
+
old_spaceship other
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
# @private
|
53
|
+
class Array
|
54
|
+
|
55
|
+
alias old_spaceship <=>
|
56
|
+
|
57
|
+
def <=> other
|
58
|
+
# p other
|
59
|
+
if other.is_a? Integer
|
60
|
+
self <=> [other]
|
61
|
+
else
|
62
|
+
old_spaceship other
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
|
36
69
|
# @private
|
37
70
|
class WIN32OLE
|
71
|
+
|
38
72
|
# promoting WIN32OLE objects to RobustExcelOle objects
|
39
73
|
def to_reo
|
40
74
|
case ole_type.name
|
41
75
|
when 'Range' then RobustExcelOle::Range.new(self)
|
42
|
-
when '_Worksheet' then Worksheet.new(self)
|
43
|
-
when '_Workbook' then Workbook.new(self)
|
44
|
-
when '_Application' then Excel.new(self)
|
76
|
+
when '_Worksheet' then RobustExcelOle::Worksheet.new(self)
|
77
|
+
when '_Workbook' then RobustExcelOle::Workbook.new(self)
|
78
|
+
when '_Application' then RobustExcelOle::Excel.new(self)
|
45
79
|
else
|
46
80
|
self
|
47
81
|
end
|
@@ -73,18 +73,18 @@ module RobustExcelOle
|
|
73
73
|
{ }
|
74
74
|
end
|
75
75
|
end
|
76
|
-
|
76
|
+
rows, columns = Address.int_range(dest_address)
|
77
77
|
dest_sheet = @worksheet if dest_sheet == :__not_provided
|
78
|
-
dest_address_is_position = (
|
78
|
+
dest_address_is_position = (rows.min == rows.max && columns.min == columns.max)
|
79
79
|
dest_range_address = if (not dest_address_is_position)
|
80
|
-
[
|
80
|
+
[rows.min..rows.max,columns.min..columns.max]
|
81
81
|
else
|
82
82
|
if (not options[:transpose])
|
83
|
-
[
|
84
|
-
|
83
|
+
[rows.min..rows.min+self.Rows.Count-1,
|
84
|
+
columns.min..columns.min+self.Columns.Count-1]
|
85
85
|
else
|
86
|
-
[
|
87
|
-
|
86
|
+
[rows.min..rows.min+self.Columns.Count-1,
|
87
|
+
columns.min..columns.min+self.Rows.Count-1]
|
88
88
|
end
|
89
89
|
end
|
90
90
|
dest_range = dest_sheet.range(dest_range_address)
|
@@ -95,9 +95,9 @@ module RobustExcelOle
|
|
95
95
|
if dest_range.worksheet.workbook.excel == @worksheet.workbook.excel
|
96
96
|
if options[:transpose]
|
97
97
|
self.Copy
|
98
|
-
dest_range.PasteSpecial(
|
98
|
+
dest_range.PasteSpecial('transpose' => true)
|
99
99
|
else
|
100
|
-
self.Copy(
|
100
|
+
self.Copy('destination' => dest_range.ole_range)
|
101
101
|
end
|
102
102
|
else
|
103
103
|
if options[:transpose]
|
@@ -107,7 +107,7 @@ module RobustExcelOle
|
|
107
107
|
@worksheet.workbook.excel.with_displayalerts(false) {added_sheet.Delete}
|
108
108
|
else
|
109
109
|
self.Copy
|
110
|
-
dest_sheet.Paste(
|
110
|
+
dest_sheet.Paste('destination' => dest_range.ole_range)
|
111
111
|
end
|
112
112
|
end
|
113
113
|
end
|
@@ -122,18 +122,18 @@ module RobustExcelOle
|
|
122
122
|
# @options [Worksheet] the destination worksheet
|
123
123
|
# @options [Hash] options: :transpose, :values_only
|
124
124
|
def copy_special(dest_address, dest_sheet = :__not_provided, options = { })
|
125
|
-
|
125
|
+
rows, columns = Address.int_range(dest_address)
|
126
126
|
dest_sheet = @worksheet if dest_sheet == :__not_provided
|
127
|
-
dest_address_is_position = (
|
127
|
+
dest_address_is_position = (rows.min == rows.max && columns.min == columns.max)
|
128
128
|
dest_range_address = if (not dest_address_is_position)
|
129
|
-
[
|
129
|
+
[rows.min..rows.max,columns.min..columns.max]
|
130
130
|
else
|
131
131
|
if (not options[:transpose])
|
132
|
-
[
|
133
|
-
|
132
|
+
[rows.min..rows.min+self.Rows.Count-1,
|
133
|
+
columns.min..columns.min+self.Columns.Count-1]
|
134
134
|
else
|
135
|
-
[
|
136
|
-
|
135
|
+
[rows.min..rows.min+self.Columns.Count-1,
|
136
|
+
columns.min..columns.min+self.Rows.Count-1]
|
137
137
|
end
|
138
138
|
end
|
139
139
|
dest_range = dest_sheet.range(dest_range_address)
|
@@ -144,9 +144,9 @@ module RobustExcelOle
|
|
144
144
|
if dest_range.worksheet.workbook.excel == @worksheet.workbook.excel
|
145
145
|
if options[:transpose]
|
146
146
|
self.Copy
|
147
|
-
dest_range.PasteSpecial(
|
147
|
+
dest_range.PasteSpecial('transpose' => true)
|
148
148
|
else
|
149
|
-
self.Copy(
|
149
|
+
self.Copy('destination' => dest_range.ole_range)
|
150
150
|
end
|
151
151
|
else
|
152
152
|
if options[:transpose]
|
@@ -156,7 +156,7 @@ module RobustExcelOle
|
|
156
156
|
@worksheet.workbook.excel.with_displayalerts(false) {added_sheet.Delete}
|
157
157
|
else
|
158
158
|
self.Copy
|
159
|
-
dest_sheet.Paste(
|
159
|
+
dest_sheet.Paste('destination' => dest_range.ole_range)
|
160
160
|
end
|
161
161
|
end
|
162
162
|
end
|
@@ -0,0 +1,236 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module RobustExcelOle
|
4
|
+
|
5
|
+
class RangeOwners < REOCommon
|
6
|
+
|
7
|
+
# returns the contents of a range with given name
|
8
|
+
# if the name could not be found or the value could not be determined,
|
9
|
+
# then return default value, if provided, raise error otherwise
|
10
|
+
# Excel Bug: if a local name without a qualifier is given,
|
11
|
+
# then by default Excel takes the first worksheet,
|
12
|
+
# even if a different worksheet is active
|
13
|
+
# @param [String] name the name of the range
|
14
|
+
# @param [Hash] opts the options
|
15
|
+
# @option opts [Symbol] :default the default value that is provided if no contents could be returned
|
16
|
+
# @return [Variant] the contents of a range with given name
|
17
|
+
def namevalue_glob(name, opts = { :default => :__not_provided })
|
18
|
+
name_obj = begin
|
19
|
+
name_object(name)
|
20
|
+
rescue NameNotFound => msg
|
21
|
+
return opts[:default] unless opts[:default] == :__not_provided
|
22
|
+
raise
|
23
|
+
end
|
24
|
+
value = begin
|
25
|
+
name_obj.RefersToRange.Value
|
26
|
+
rescue WIN32OLERuntimeError
|
27
|
+
sheet = if self.is_a?(Worksheet) then self
|
28
|
+
elsif self.is_a?(Workbook) then self.sheet(1)
|
29
|
+
elsif self.is_a?(Excel) then self.workbook.sheet(1)
|
30
|
+
end
|
31
|
+
begin
|
32
|
+
sheet.Evaluate(name_obj.Name).Value
|
33
|
+
rescue # WIN32OLERuntimeError
|
34
|
+
return opts[:default] unless opts[:default] == :__not_provided
|
35
|
+
raise RangeNotEvaluatable, "cannot evaluate range named #{name.inspect} in #{self}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
if value == -2146828288 + RobustExcelOle::XlErrName
|
39
|
+
return opts[:default] unless opts[:default] == :__not_provided
|
40
|
+
raise RangeNotEvaluatable, "cannot evaluate range named #{name.inspect} in #{File.basename(workbook.stored_filename).inspect rescue nil}"
|
41
|
+
end
|
42
|
+
return opts[:default] unless (opts[:default] == :__not_provided) || value.nil?
|
43
|
+
value
|
44
|
+
end
|
45
|
+
|
46
|
+
# sets the contents of a range
|
47
|
+
# @param [String] name the name of a range
|
48
|
+
# @param [Variant] value the contents of the range
|
49
|
+
# @param [FixNum] color the color when setting a value
|
50
|
+
# @param [Hash] opts :color [FixNum] the color when setting the contents
|
51
|
+
def set_namevalue_glob(name, value, opts = { :color => 0 })
|
52
|
+
cell = name_object(name).RefersToRange
|
53
|
+
cell.Interior.ColorIndex = opts[:color]
|
54
|
+
workbook.modified_cells << cell if workbook # unless cell_modified?(cell)
|
55
|
+
cell.Value = value
|
56
|
+
rescue WIN32OLERuntimeError
|
57
|
+
raise RangeNotEvaluatable, "cannot assign value to range named #{name.inspect} in #{self.inspect}"
|
58
|
+
end
|
59
|
+
|
60
|
+
# returns the contents of a range with a locally defined name
|
61
|
+
# evaluates the formula if the contents is a formula
|
62
|
+
# if the name could not be found or the range or value could not be determined,
|
63
|
+
# then return default value, if provided, raise error otherwise
|
64
|
+
# @param [String] name the name of a range
|
65
|
+
# @param [Hash] opts the options
|
66
|
+
# @option opts [Symbol] :default the default value that is provided if no contents could be returned
|
67
|
+
# @return [Variant] the contents of a range with given name
|
68
|
+
def namevalue(name, opts = { :default => :__not_provided })
|
69
|
+
return namevalue_glob(name, opts) if self.is_a?(Workbook)
|
70
|
+
begin
|
71
|
+
range = self.Range(name)
|
72
|
+
rescue WIN32OLERuntimeError
|
73
|
+
return opts[:default] unless opts[:default] == :__not_provided
|
74
|
+
raise NameNotFound, "name #{name.inspect} not in #{self.inspect}"
|
75
|
+
end
|
76
|
+
begin
|
77
|
+
value = range.Value
|
78
|
+
rescue WIN32OLERuntimeError
|
79
|
+
return opts[:default] unless opts[:default] == :__not_provided
|
80
|
+
raise RangeNotEvaluatable, "cannot determine value of range named #{name.inspect} in #{self.inspect}"
|
81
|
+
end
|
82
|
+
if value == -2146828288 + RobustExcelOle::XlErrName
|
83
|
+
return opts[:default] unless opts[:default] == __not_provided
|
84
|
+
raise RangeNotEvaluatable, "cannot evaluate range named #{name.inspect} in #{File.basename(workbook.stored_filename).inspect rescue nil}"
|
85
|
+
end
|
86
|
+
return opts[:default] unless (opts[:default] == :__not_provided) || value.nil?
|
87
|
+
value
|
88
|
+
end
|
89
|
+
|
90
|
+
# assigns a value to a range given a locally defined name
|
91
|
+
# @param [String] name the name of a range
|
92
|
+
# @param [Variant] value the assigned value
|
93
|
+
# @param [Hash] opts :color [FixNum] the color when setting the contents
|
94
|
+
def set_namevalue(name, value, opts = { :color => 0 })
|
95
|
+
begin
|
96
|
+
return set_namevalue_glob(name, value, opts) if self.is_a?(Workbook)
|
97
|
+
range = self.Range(name)
|
98
|
+
rescue WIN32OLERuntimeError
|
99
|
+
raise NameNotFound, "name #{name.inspect} not in #{self.inspect}"
|
100
|
+
end
|
101
|
+
begin
|
102
|
+
range.Interior.ColorIndex = opts[:color]
|
103
|
+
workbook.modified_cells << range if workbook # unless cell_modified?(range)
|
104
|
+
range.Value = value
|
105
|
+
rescue WIN32OLERuntimeError
|
106
|
+
raise RangeNotEvaluatable, "cannot assign value to range named #{name.inspect} in #{self.inspect}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# @private
|
111
|
+
def nameval(name, opts = { :default => :__not_provided }) # :deprecated: #
|
112
|
+
namevalue_glob(name, opts)
|
113
|
+
end
|
114
|
+
|
115
|
+
# @private
|
116
|
+
def set_nameval(name, value, opts = { :color => 0 }) # :deprecated: #
|
117
|
+
set_namevalue_glob(name, value, opts)
|
118
|
+
end
|
119
|
+
|
120
|
+
# @private
|
121
|
+
def rangeval(name, opts = { :default => :__not_provided }) # :deprecated: #
|
122
|
+
namevalue(name, opts)
|
123
|
+
end
|
124
|
+
|
125
|
+
# @private
|
126
|
+
def set_rangeval(name, value, opts = { :color => 0 }) # :deprecated: #
|
127
|
+
set_namevalue(name, value, opts)
|
128
|
+
end
|
129
|
+
|
130
|
+
# creates a range from a given defined name or address
|
131
|
+
# range(address) does work for Worksheet objects only
|
132
|
+
# @params [Variant] range name or address
|
133
|
+
# @return [Range] a range
|
134
|
+
def range(name_or_address, address2 = :__not_provided)
|
135
|
+
begin
|
136
|
+
if address2 == :__not_provided
|
137
|
+
range = begin
|
138
|
+
RobustExcelOle::Range.new(name_object(name_or_address).RefersToRange)
|
139
|
+
rescue NameNotFound
|
140
|
+
nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
if self.is_a?(Worksheet) && (range.nil? || (address2 != :__not_provided))
|
144
|
+
address = name_or_address
|
145
|
+
address = [name_or_address,address2] unless address2 == :__not_provided
|
146
|
+
self.Names.Add('Name' => '__dummy001', 'RefersToR1C1' => '=' + Address.r1c1(address))
|
147
|
+
range = RobustExcelOle::Range.new(name_object('__dummy001').RefersToRange)
|
148
|
+
self.Names.Item('__dummy001').Delete
|
149
|
+
range
|
150
|
+
# variant via rows, columns:
|
151
|
+
#rows, columns = Address.int_range(address)
|
152
|
+
#ole_range = if rows.nil?
|
153
|
+
# self.Range(self.Columns(columns.min), self.Columns(columns.max))
|
154
|
+
#elsif columns.nil?
|
155
|
+
# self.Range(self.Rows(rows.min), self.Rows(rows.max))
|
156
|
+
#else
|
157
|
+
# self.Range(self.Cells(rows.min, columns.min), self.Cells(rows.max, columns.max))
|
158
|
+
#end
|
159
|
+
##range = ole_range.to_reo
|
160
|
+
#range = Range.new(ole_range)
|
161
|
+
end
|
162
|
+
rescue WIN32OLERuntimeError
|
163
|
+
address2_string = address2.nil? ? "" : ", #{address2.inspect}"
|
164
|
+
raise RangeNotCreated, "cannot create range (#{name_or_address.inspect}#{address2_string})"
|
165
|
+
end
|
166
|
+
range
|
167
|
+
end
|
168
|
+
|
169
|
+
def name2range(name) # :deprecated: #
|
170
|
+
range(name)
|
171
|
+
end
|
172
|
+
|
173
|
+
# adds a name referring to a range given by the row and column
|
174
|
+
# @param [String] name the range name
|
175
|
+
# @params [Address] address of the range
|
176
|
+
def add_name(name, addr, addr_deprecated = :__not_provided)
|
177
|
+
addr = [addr,addr_deprecated] unless addr_deprecated == :__not_provided
|
178
|
+
begin
|
179
|
+
self.Names.Add('Name' => name, 'RefersToR1C1' => '=' + Address.r1c1(addr))
|
180
|
+
rescue WIN32OLERuntimeError => msg
|
181
|
+
raise RangeNotEvaluatable, "cannot add name #{name.inspect} to range #{addr.inspect}"
|
182
|
+
end
|
183
|
+
name
|
184
|
+
end
|
185
|
+
|
186
|
+
def set_name(name,row,column) # :deprecated :#
|
187
|
+
add_name(name,row,column)
|
188
|
+
end
|
189
|
+
|
190
|
+
# renames a range
|
191
|
+
# @param [String] name the previous range name
|
192
|
+
# @param [String] new_name the new range name
|
193
|
+
def rename_range(name, new_name)
|
194
|
+
begin
|
195
|
+
item = self.Names.Item(name)
|
196
|
+
rescue WIN32OLERuntimeError
|
197
|
+
raise NameNotFound, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
|
198
|
+
end
|
199
|
+
begin
|
200
|
+
item.Name = new_name
|
201
|
+
rescue WIN32OLERuntimeError
|
202
|
+
raise UnexpectedREOError, "name error in #{File.basename(self.stored_filename).inspect}"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# deletes a name of a range
|
207
|
+
# @param [String] name the previous range name
|
208
|
+
# @param [String] new_name the new range name
|
209
|
+
def delete_name(name)
|
210
|
+
begin
|
211
|
+
item = self.Names.Item(name)
|
212
|
+
rescue WIN32OLERuntimeError
|
213
|
+
raise NameNotFound, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
|
214
|
+
end
|
215
|
+
begin
|
216
|
+
item.Delete
|
217
|
+
rescue WIN32OLERuntimeError
|
218
|
+
raise UnexpectedREOError, "name error in #{File.basename(self.stored_filename).inspect}"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
private
|
223
|
+
|
224
|
+
def name_object(name)
|
225
|
+
self.Names.Item(name)
|
226
|
+
rescue WIN32OLERuntimeError
|
227
|
+
begin
|
228
|
+
self.Parent.Names.Item(name)
|
229
|
+
rescue WIN32OLERuntimeError
|
230
|
+
raise RobustExcelOle::NameNotFound, "name #{name.inspect} not in #{self.inspect}"
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
@@ -1,11 +1,21 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
|
-
LOG_TO_STDOUT =
|
3
|
+
LOG_TO_STDOUT = false unless Object.const_defined?(:LOG_TO_STDOUT)
|
4
4
|
REO_LOG_DIR = ''.freeze unless Object.const_defined?(:REO_LOG_DIR)
|
5
5
|
REO_LOG_FILE = 'reo.log'.freeze unless Object.const_defined?(:REO_LOG_FILE)
|
6
6
|
|
7
7
|
File.delete REO_LOG_FILE rescue nil
|
8
8
|
|
9
|
+
unless "any string".respond_to?(:end_with?)
|
10
|
+
class String
|
11
|
+
def end_with?(*suffixes)
|
12
|
+
suffixes.any? do |suffix|
|
13
|
+
self[-suffix.size .. -1] == suffix
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
9
19
|
module RobustExcelOle
|
10
20
|
|
11
21
|
# @private
|
@@ -21,7 +31,7 @@ module RobustExcelOle
|
|
21
31
|
end
|
22
32
|
|
23
33
|
# @private
|
24
|
-
class
|
34
|
+
class WorksheetREOError < REOError
|
25
35
|
end
|
26
36
|
|
27
37
|
# @private
|
@@ -122,18 +132,22 @@ module RobustExcelOle
|
|
122
132
|
|
123
133
|
class REOCommon
|
124
134
|
|
135
|
+
# @private
|
125
136
|
def excel
|
126
137
|
raise TypeREOError, 'receiver instance is neither an Excel nor a Workbook'
|
127
138
|
end
|
128
139
|
|
140
|
+
# @private
|
129
141
|
def own_methods
|
130
142
|
(self.methods - Object.methods).sort
|
131
143
|
end
|
132
144
|
|
145
|
+
# @private
|
133
146
|
def self.tr1(_text)
|
134
147
|
puts :text
|
135
148
|
end
|
136
149
|
|
150
|
+
# @private
|
137
151
|
def self.trace(text)
|
138
152
|
if LOG_TO_STDOUT
|
139
153
|
puts text
|
@@ -144,6 +158,7 @@ module RobustExcelOle
|
|
144
158
|
reo_log_dir = ENV[home]
|
145
159
|
else
|
146
160
|
reo_log_dir = REO_LOG_DIR
|
161
|
+
#reo_log_dir = "C:/Users/User"
|
147
162
|
end
|
148
163
|
File.open(reo_log_dir + '/' + REO_LOG_FILE,'a') do |file|
|
149
164
|
file.puts text
|
@@ -151,6 +166,7 @@ module RobustExcelOle
|
|
151
166
|
end
|
152
167
|
end
|
153
168
|
|
169
|
+
# @private
|
154
170
|
def self.puts_hash(hash)
|
155
171
|
hash.each do |e|
|
156
172
|
if e[1].is_a?(Hash)
|
@@ -166,278 +182,4 @@ module RobustExcelOle
|
|
166
182
|
|
167
183
|
end
|
168
184
|
|
169
|
-
class Address < REOCommon
|
170
|
-
|
171
|
-
attr_reader :rows
|
172
|
-
attr_reader :columns
|
173
|
-
|
174
|
-
def initialize(address)
|
175
|
-
address = [address] unless address.is_a?(Array)
|
176
|
-
raise AddressInvalid, 'more than two components' if address.size > 2
|
177
|
-
begin
|
178
|
-
if address.size == 1
|
179
|
-
comp1, comp2 = address[0].split(':')
|
180
|
-
address_comp1 = comp1.gsub(/[A-Z]/,'')
|
181
|
-
address_comp2 = comp1.gsub(/[0-9]/,'')
|
182
|
-
if comp1 != address_comp2 + address_comp1
|
183
|
-
raise AddressInvalid, "address #{comp1.inspect} not in A1-format"
|
184
|
-
end
|
185
|
-
unless comp2.nil?
|
186
|
-
address_comp3 = comp2.gsub(/[A-Z]/,'')
|
187
|
-
address_comp4 = comp2.gsub(/[0-9]/,'')
|
188
|
-
if comp2 != address_comp4 + address_comp3
|
189
|
-
raise AddressInvalid, "address #{comp2.inspect} not in A1-format"
|
190
|
-
end
|
191
|
-
address_comp1 = address_comp1..address_comp3
|
192
|
-
address_comp2 = address_comp2..address_comp4
|
193
|
-
end
|
194
|
-
else
|
195
|
-
address_comp1, address_comp2 = address
|
196
|
-
end
|
197
|
-
address_comp1 = address_comp1..address_comp1 unless address_comp1.is_a?(Object::Range)
|
198
|
-
address_comp2 = address_comp2..address_comp2 unless address_comp2.is_a?(Object::Range)
|
199
|
-
@rows = address_comp1.min.to_i..address_comp1.max.to_i
|
200
|
-
if address_comp2.min.to_i == 0
|
201
|
-
raise AddressInvalid, "address (#{address_comp1.inspect}, #{address_comp2.inspect}) not in A1-format" if address_comp1.min.to_i == 0
|
202
|
-
@columns = str2num(address_comp2.begin)..str2num(address_comp2.end)
|
203
|
-
else
|
204
|
-
@columns = address_comp2.min.to_i..address_comp2.max.to_i
|
205
|
-
end
|
206
|
-
rescue
|
207
|
-
raise AddressInvalid, "address (#{address.inspect}) not in A1- or R1C1-format"
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
private
|
212
|
-
|
213
|
-
def str2num(str)
|
214
|
-
str = str.upcase
|
215
|
-
sum = 0
|
216
|
-
(1..str.length).each { |i| sum += (str[i - 1].ord - 64) * 26**(str.length - i) }
|
217
|
-
sum
|
218
|
-
end
|
219
|
-
|
220
|
-
end
|
221
|
-
|
222
|
-
class RangeOwners < REOCommon
|
223
|
-
|
224
|
-
# returns the contents of a range with given name
|
225
|
-
# if the name could not be found or the value could not be determined,
|
226
|
-
# then return default value, if provided, raise error otherwise
|
227
|
-
# Excel Bug: if a local name without a qualifier is given,
|
228
|
-
# then by default Excel takes the first worksheet,
|
229
|
-
# even if a different worksheet is active
|
230
|
-
# @param [String] name the name of the range
|
231
|
-
# @param [Hash] opts the options
|
232
|
-
# @option opts [Symbol] :default the default value that is provided if no contents could be returned
|
233
|
-
# @return [Variant] the contents of a range with given name
|
234
|
-
def namevalue_glob(name, opts = { :default => :__not_provided })
|
235
|
-
name_obj = begin
|
236
|
-
name_object(name)
|
237
|
-
rescue NameNotFound => msg
|
238
|
-
return opts[:default] unless opts[:default] == :__not_provided
|
239
|
-
raise
|
240
|
-
end
|
241
|
-
value = begin
|
242
|
-
name_obj.RefersToRange.Value
|
243
|
-
rescue WIN32OLERuntimeError
|
244
|
-
sheet = if self.is_a?(Worksheet) then self
|
245
|
-
elsif self.is_a?(Workbook) then self.sheet(1)
|
246
|
-
elsif self.is_a?(Excel) then self.workbook.sheet(1)
|
247
|
-
end
|
248
|
-
begin
|
249
|
-
sheet.Evaluate(name_obj.Name).Value
|
250
|
-
rescue # WIN32OLERuntimeError
|
251
|
-
return opts[:default] unless opts[:default] == :__not_provided
|
252
|
-
raise RangeNotEvaluatable, "cannot evaluate range named #{name.inspect} in #{self}"
|
253
|
-
end
|
254
|
-
end
|
255
|
-
if value == -2146828288 + RobustExcelOle::XlErrName
|
256
|
-
return opts[:default] unless opts[:default] == :__not_provided
|
257
|
-
raise RangeNotEvaluatable, "cannot evaluate range named #{name.inspect} in #{File.basename(workbook.stored_filename).inspect rescue nil}"
|
258
|
-
end
|
259
|
-
return opts[:default] unless (opts[:default] == :__not_provided) || value.nil?
|
260
|
-
value
|
261
|
-
end
|
262
|
-
|
263
|
-
# sets the contents of a range
|
264
|
-
# @param [String] name the name of a range
|
265
|
-
# @param [Variant] value the contents of the range
|
266
|
-
# @param [FixNum] color the color when setting a value
|
267
|
-
# @param [Hash] opts :color [FixNum] the color when setting the contents
|
268
|
-
def set_namevalue_glob(name, value, opts = { :color => 0 })
|
269
|
-
cell = name_object(name).RefersToRange
|
270
|
-
cell.Interior.ColorIndex = opts[:color]
|
271
|
-
workbook.modified_cells << cell if workbook # unless cell_modified?(cell)
|
272
|
-
cell.Value = value
|
273
|
-
rescue WIN32OLERuntimeError
|
274
|
-
raise RangeNotEvaluatable, "cannot assign value to range named #{name.inspect} in #{self.inspect}"
|
275
|
-
end
|
276
|
-
|
277
|
-
# returns the contents of a range with a locally defined name
|
278
|
-
# evaluates the formula if the contents is a formula
|
279
|
-
# if the name could not be found or the range or value could not be determined,
|
280
|
-
# then return default value, if provided, raise error otherwise
|
281
|
-
# @param [String] name the name of a range
|
282
|
-
# @param [Hash] opts the options
|
283
|
-
# @option opts [Symbol] :default the default value that is provided if no contents could be returned
|
284
|
-
# @return [Variant] the contents of a range with given name
|
285
|
-
def namevalue(name, opts = { :default => :__not_provided })
|
286
|
-
return namevalue_glob(name, opts) if self.is_a?(Workbook)
|
287
|
-
begin
|
288
|
-
range = self.Range(name)
|
289
|
-
rescue WIN32OLERuntimeError
|
290
|
-
return opts[:default] unless opts[:default] == :__not_provided
|
291
|
-
raise NameNotFound, "name #{name.inspect} not in #{self.inspect}"
|
292
|
-
end
|
293
|
-
begin
|
294
|
-
value = range.Value
|
295
|
-
rescue WIN32OLERuntimeError
|
296
|
-
return opts[:default] unless opts[:default] == :__not_provided
|
297
|
-
raise RangeNotEvaluatable, "cannot determine value of range named #{name.inspect} in #{self.inspect}"
|
298
|
-
end
|
299
|
-
if value == -2146828288 + RobustExcelOle::XlErrName
|
300
|
-
return opts[:default] unless opts[:default] == __not_provided
|
301
|
-
raise RangeNotEvaluatable, "cannot evaluate range named #{name.inspect} in #{File.basename(workbook.stored_filename).inspect rescue nil}"
|
302
|
-
end
|
303
|
-
return opts[:default] unless (opts[:default] == :__not_provided) || value.nil?
|
304
|
-
value
|
305
|
-
end
|
306
|
-
|
307
|
-
# assigns a value to a range given a locally defined name
|
308
|
-
# @param [String] name the name of a range
|
309
|
-
# @param [Variant] value the assigned value
|
310
|
-
# @param [Hash] opts :color [FixNum] the color when setting the contents
|
311
|
-
def set_namevalue(name, value, opts = { :color => 0 })
|
312
|
-
begin
|
313
|
-
return set_namevalue_glob(name, value, opts) if self.is_a?(Workbook)
|
314
|
-
range = self.Range(name)
|
315
|
-
rescue WIN32OLERuntimeError
|
316
|
-
raise NameNotFound, "name #{name.inspect} not in #{self.inspect}"
|
317
|
-
end
|
318
|
-
begin
|
319
|
-
range.Interior.ColorIndex = opts[:color]
|
320
|
-
workbook.modified_cells << range if workbook # unless cell_modified?(range)
|
321
|
-
range.Value = value
|
322
|
-
rescue WIN32OLERuntimeError
|
323
|
-
raise RangeNotEvaluatable, "cannot assign value to range named #{name.inspect} in #{self.inspect}"
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
def nameval(name, opts = { :default => :__not_provided }) # :deprecated: #
|
328
|
-
namevalue_glob(name, opts)
|
329
|
-
end
|
330
|
-
|
331
|
-
def set_nameval(name, value, opts = { :color => 0 }) # :deprecated: #
|
332
|
-
set_namevalue_glob(name, value, opts)
|
333
|
-
end
|
334
|
-
|
335
|
-
def rangeval(name, opts = { :default => :__not_provided }) # :deprecated: #
|
336
|
-
namevalue(name, opts)
|
337
|
-
end
|
338
|
-
|
339
|
-
def set_rangeval(name, value, opts = { :color => 0 }) # :deprecated: #
|
340
|
-
set_namevalue(name, value, opts)
|
341
|
-
end
|
342
|
-
|
343
|
-
# creates a range from a given defined name or address
|
344
|
-
# @params [Variant] range name or address
|
345
|
-
# @return [Range] a range
|
346
|
-
def range(name_or_address, address2 = :__not_provided)
|
347
|
-
begin
|
348
|
-
if address2 == :__not_provided
|
349
|
-
range = RobustExcelOle::Range.new(name_object(name_or_address).RefersToRange) rescue nil
|
350
|
-
end
|
351
|
-
if self.is_a?(Worksheet) && (range.nil? || (address2 != :__not_provided))
|
352
|
-
address = name_or_address
|
353
|
-
address = [name_or_address,address2] unless address2 == :__not_provided
|
354
|
-
address = Address.new(address)
|
355
|
-
range = RobustExcelOle::Range.new(@ole_worksheet.Range(
|
356
|
-
@ole_worksheet.Cells(address.rows.min, address.columns.min),
|
357
|
-
@ole_worksheet.Cells(address.rows.max, address.columns.max)
|
358
|
-
))
|
359
|
-
end
|
360
|
-
rescue WIN32OLERuntimeError
|
361
|
-
address2_string = address2.nil? ? "" : ", #{address2.inspect}"
|
362
|
-
raise RangeNotCreated, "cannot create range (#{name_or_address.inspect}#{address2_string})"
|
363
|
-
end
|
364
|
-
range
|
365
|
-
end
|
366
|
-
|
367
|
-
def name2range(name) # :deprecated: #
|
368
|
-
range(name)
|
369
|
-
end
|
370
|
-
|
371
|
-
# adds a name referring to a range given by the row and column
|
372
|
-
# @param [String] name the range name
|
373
|
-
# @params [Address] address of the range
|
374
|
-
def add_name(name, addr, addr_deprecated = :__not_provided)
|
375
|
-
addr = [addr,addr_deprecated] unless addr_deprecated == :__not_provided
|
376
|
-
address = Address.new(addr)
|
377
|
-
address_string = 'Z' + address.rows.min.to_s + 'S' + address.columns.min.to_s +
|
378
|
-
':Z' + address.rows.max.to_s + 'S' + address.columns.max.to_s
|
379
|
-
begin
|
380
|
-
self.Names.Add('Name' => name, 'RefersToR1C1' => '=' + address_string)
|
381
|
-
rescue WIN32OLERuntimeError => msg
|
382
|
-
# trace "WIN32OLERuntimeError: #{msg.message}"
|
383
|
-
raise RangeNotEvaluatable, "cannot add name #{name.inspect} to range #{addr.inspect}"
|
384
|
-
end
|
385
|
-
name
|
386
|
-
end
|
387
|
-
|
388
|
-
def set_name(name,row,column) # :deprecated :#
|
389
|
-
add_name(name,row,column)
|
390
|
-
end
|
391
|
-
|
392
|
-
# renames a range
|
393
|
-
# @param [String] name the previous range name
|
394
|
-
# @param [String] new_name the new range name
|
395
|
-
def rename_range(name, new_name)
|
396
|
-
begin
|
397
|
-
item = self.Names.Item(name)
|
398
|
-
rescue WIN32OLERuntimeError
|
399
|
-
raise NameNotFound, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
|
400
|
-
end
|
401
|
-
begin
|
402
|
-
item.Name = new_name
|
403
|
-
rescue WIN32OLERuntimeError
|
404
|
-
raise UnexpectedREOError, "name error in #{File.basename(self.stored_filename).inspect}"
|
405
|
-
end
|
406
|
-
end
|
407
|
-
|
408
|
-
# deletes a name of a range
|
409
|
-
# @param [String] name the previous range name
|
410
|
-
# @param [String] new_name the new range name
|
411
|
-
def delete_name(name)
|
412
|
-
begin
|
413
|
-
item = self.Names.Item(name)
|
414
|
-
rescue WIN32OLERuntimeError
|
415
|
-
raise NameNotFound, "name #{name.inspect} not in #{File.basename(self.stored_filename).inspect}"
|
416
|
-
end
|
417
|
-
begin
|
418
|
-
item.Delete
|
419
|
-
rescue WIN32OLERuntimeError
|
420
|
-
raise UnexpectedREOError, "name error in #{File.basename(self.stored_filename).inspect}"
|
421
|
-
end
|
422
|
-
end
|
423
|
-
|
424
|
-
private
|
425
|
-
|
426
|
-
def name_object(name)
|
427
|
-
self.Names.Item(name)
|
428
|
-
rescue WIN32OLERuntimeError
|
429
|
-
begin
|
430
|
-
self.Parent.Names.Item(name)
|
431
|
-
rescue WIN32OLERuntimeError
|
432
|
-
raise RobustExcelOle::NameNotFound, "name #{name.inspect} not in #{self.inspect}"
|
433
|
-
end
|
434
|
-
end
|
435
|
-
|
436
|
-
# def cell_modified?(cell)
|
437
|
-
# workbook.modified_cells.each{|c| return true if c.Name.Value == cell.Name.Value}
|
438
|
-
# false
|
439
|
-
# end
|
440
|
-
|
441
|
-
end
|
442
|
-
|
443
185
|
end
|