axlsx 1.1.5 → 1.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/README.md +26 -7
  2. data/Rakefile +2 -1
  3. data/examples/chart_colors.xlsx +0 -0
  4. data/examples/data_validation.rb +50 -0
  5. data/examples/example.xlsx +0 -0
  6. data/examples/example_streamed.xlsx +0 -0
  7. data/examples/examples_saved.xlsx +0 -0
  8. data/examples/fish.xlsx +0 -0
  9. data/examples/no-use_autowidth.xlsx +0 -0
  10. data/examples/pareto.rb +28 -0
  11. data/examples/pareto.xlsx +0 -0
  12. data/examples/pie_chart.rb +16 -0
  13. data/examples/pie_chart.xlsx +0 -0
  14. data/examples/pie_chart_saved.xlsx +0 -0
  15. data/examples/shared_strings_example.xlsx +0 -0
  16. data/examples/sheet_protection.rb +10 -0
  17. data/examples/sheet_protection.xlsx +0 -0
  18. data/examples/two_cell_anchor_image.rb +11 -0
  19. data/examples/two_cell_anchor_image.xlsx +0 -0
  20. data/examples/~$pie_chart_saved.xlsx +0 -0
  21. data/lib/axlsx.rb +7 -0
  22. data/lib/axlsx/content_type/default.rb +15 -9
  23. data/lib/axlsx/content_type/override.rb +10 -6
  24. data/lib/axlsx/doc_props/app.rb +152 -99
  25. data/lib/axlsx/drawing/axis.rb +30 -23
  26. data/lib/axlsx/drawing/bar_series.rb +1 -1
  27. data/lib/axlsx/drawing/drawing.rb +7 -2
  28. data/lib/axlsx/drawing/pic.rb +44 -4
  29. data/lib/axlsx/drawing/two_cell_anchor.rb +6 -1
  30. data/lib/axlsx/drawing/vml_shape.rb +2 -5
  31. data/lib/axlsx/rels/relationships.rb +1 -1
  32. data/lib/axlsx/stylesheet/table_style.rb +3 -3
  33. data/lib/axlsx/util/simple_typed_list.rb +0 -13
  34. data/lib/axlsx/util/validators.rb +21 -0
  35. data/lib/axlsx/version.rb +1 -1
  36. data/lib/axlsx/workbook/workbook.rb +2 -0
  37. data/lib/axlsx/workbook/worksheet/cell.rb +3 -4
  38. data/lib/axlsx/workbook/worksheet/comment.rb +3 -9
  39. data/lib/axlsx/workbook/worksheet/data_validation.rb +245 -0
  40. data/lib/axlsx/workbook/worksheet/page_setup.rb +17 -3
  41. data/lib/axlsx/workbook/worksheet/row.rb +34 -18
  42. data/lib/axlsx/workbook/worksheet/sheet_protection.rb +224 -0
  43. data/lib/axlsx/workbook/worksheet/table.rb +2 -2
  44. data/lib/axlsx/workbook/worksheet/worksheet.rb +57 -22
  45. data/test/doc_props/tc_app.rb +31 -1
  46. data/test/drawing/tc_axis.rb +12 -2
  47. data/test/drawing/tc_chart.rb +21 -3
  48. data/test/drawing/tc_drawing.rb +6 -1
  49. data/test/drawing/tc_hyperlink.rb +0 -5
  50. data/test/drawing/tc_pic.rb +22 -2
  51. data/test/drawing/tc_scatter_chart.rb +6 -1
  52. data/test/drawing/tc_two_cell_anchor.rb +1 -2
  53. data/test/stylesheet/tc_styles.rb +3 -4
  54. data/test/stylesheet/tc_table_style.rb +8 -0
  55. data/test/stylesheet/tc_table_style_element.rb +10 -1
  56. data/test/tc_package.rb +43 -15
  57. data/test/util/tc_simple_typed_list.rb +13 -0
  58. data/test/util/tc_validators.rb +7 -7
  59. data/test/workbook/worksheet/table/tc_table.rb +3 -3
  60. data/test/workbook/worksheet/tc_cell.rb +15 -6
  61. data/test/workbook/worksheet/tc_col.rb +9 -0
  62. data/test/workbook/worksheet/tc_comment.rb +8 -7
  63. data/test/workbook/worksheet/tc_comments.rb +8 -1
  64. data/test/workbook/worksheet/tc_conditional_formatting.rb +44 -0
  65. data/test/workbook/worksheet/tc_data_bar.rb +1 -1
  66. data/test/workbook/worksheet/tc_data_validation.rb +265 -0
  67. data/test/workbook/worksheet/tc_page_setup.rb +22 -4
  68. data/test/workbook/worksheet/tc_row.rb +14 -2
  69. data/test/workbook/worksheet/tc_sheet_protection.rb +117 -0
  70. data/test/workbook/worksheet/tc_worksheet.rb +29 -4
  71. metadata +31 -10
@@ -5,11 +5,13 @@ module Axlsx
5
5
 
6
6
  # the id of the axis.
7
7
  # @return [Integer]
8
- attr_reader :axId
8
+ attr_reader :ax_id
9
+ alias :axID :ax_id
9
10
 
10
11
  # The perpendicular axis
11
12
  # @return [Integer]
12
- attr_reader :crossAx
13
+ attr_reader :cross_ax
14
+ alias :crossAx :cross_ax
13
15
 
14
16
  # The scaling of the axis
15
17
  # @see Scaling
@@ -19,12 +21,14 @@ module Axlsx
19
21
  # The position of the axis
20
22
  # must be one of [:l, :r, :t, :b]
21
23
  # @return [Symbol]
22
- attr_reader :axPos
24
+ attr_reader :ax_pos
25
+ alias :axPos :ax_pos
23
26
 
24
27
  # the position of the tick labels
25
28
  # must be one of [:nextTo, :high, :low]
26
29
  # @return [Symbol]
27
- attr_reader :tickLblPos
30
+ attr_reader :tick_lbl_pos
31
+ alias :tickLblPos :tick_lbl_pos
28
32
 
29
33
  # The number format format code for this axis
30
34
  # default :General
@@ -49,22 +53,22 @@ module Axlsx
49
53
  attr_reader :delete
50
54
 
51
55
  # Creates an Axis object
52
- # @param [Integer] axId the id of this axis
53
- # @param [Integer] crossAx the id of the perpendicular axis
54
- # @option options [Symbol] axPos
56
+ # @param [Integer] ax_id the id of this axis
57
+ # @param [Integer] cross_ax the id of the perpendicular axis
58
+ # @option options [Symbol] ax_pos
55
59
  # @option options [Symbol] crosses
56
- # @option options [Symbol] tickLblPos
57
- # @raise [ArgumentError] If axId or crossAx are not unsigned integers
58
- def initialize(axId, crossAx, options={})
59
- Axlsx::validate_unsigned_int(axId)
60
- Axlsx::validate_unsigned_int(crossAx)
61
- @axId = axId
62
- @crossAx = crossAx
60
+ # @option options [Symbol] tick_lbl_pos
61
+ # @raise [ArgumentError] If axi_id or cross_ax are not unsigned integers
62
+ def initialize(ax_id, cross_ax, options={})
63
+ Axlsx::validate_unsigned_int(ax_id)
64
+ Axlsx::validate_unsigned_int(cross_ax)
65
+ @ax_id = ax_id
66
+ @cross_ax = cross_ax
63
67
  @format_code = "General"
64
68
  @delete = @label_rotation = 0
65
69
  @scaling = Scaling.new(:orientation=>:minMax)
66
- self.axPos = :b
67
- self.tickLblPos = :nextTo
70
+ self.ax_pos = :b
71
+ self.tick_lbl_pos = :nextTo
68
72
  self.format_code = "General"
69
73
  self.crosses = :autoZero
70
74
  self.gridlines = true
@@ -72,13 +76,16 @@ module Axlsx
72
76
  self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
73
77
  end
74
78
  end
79
+
75
80
  # The position of the axis
76
81
  # must be one of [:l, :r, :t, :b]
77
- def axPos=(v) RestrictionValidator.validate "#{self.class}.axPos", [:l, :r, :b, :t], v; @axPos = v; end
82
+ def ax_pos=(v) RestrictionValidator.validate "#{self.class}.ax_pos", [:l, :r, :b, :t], v; @ax_pos = v; end
83
+ alias :axPos= :ax_pos=
78
84
 
79
85
  # the position of the tick labels
80
86
  # must be one of [:nextTo, :high, :low1]
81
- def tickLblPos=(v) RestrictionValidator.validate "#{self.class}.tickLblPos", [:nextTo, :high, :low], v; @tickLblPos = v; end
87
+ def tick_lbl_pos=(v) RestrictionValidator.validate "#{self.class}.tick_lbl_pos", [:nextTo, :high, :low], v; @tick_lbl_pos = v; end
88
+ alias :tickLblPos= :tick_lbl_pos=
82
89
 
83
90
  # The number format format code for this axis
84
91
  # default :General
@@ -111,12 +118,12 @@ module Axlsx
111
118
  # @param [String] str
112
119
  # @return [String]
113
120
  def to_xml_string(str = '')
114
- str << '<c:axId val="' << @axId.to_s << '"/>'
121
+ str << '<c:axId val="' << @ax_id.to_s << '"/>'
115
122
  @scaling.to_xml_string str
116
123
  str << '<c:delete val="'<< @delete.to_s << '"/>'
117
- str << '<c:axPos val="' << @axPos.to_s << '"/>'
124
+ str << '<c:axPos val="' << @ax_pos.to_s << '"/>'
118
125
  str << '<c:majorGridlines>'
119
- if self.gridlines == false
126
+ if gridlines == false
120
127
  str << '<c:spPr>'
121
128
  str << '<a:ln>'
122
129
  str << '<a:noFill/>'
@@ -127,10 +134,10 @@ module Axlsx
127
134
  str << '<c:numFmt formatCode="' << @format_code << '" sourceLinked="1"/>'
128
135
  str << '<c:majorTickMark val="none"/>'
129
136
  str << '<c:minorTickMark val="none"/>'
130
- str << '<c:tickLblPos val="' << @tickLblPos.to_s << '"/>'
137
+ str << '<c:tickLblPos val="' << @tick_lbl_pos.to_s << '"/>'
131
138
  # some potential value in implementing this in full. Very detailed!
132
139
  str << '<c:txPr><a:bodyPr rot="' << @label_rotation.to_s << '"/><a:lstStyle/><a:p><a:pPr><a:defRPr/></a:pPr><a:endParaRPr/></a:p></c:txPr>'
133
- str << '<c:crossAx val="' << @crossAx.to_s << '"/>'
140
+ str << '<c:crossAx val="' << @cross_ax.to_s << '"/>'
134
141
  str << '<c:crosses val="' << @crosses.to_s << '"/>'
135
142
  end
136
143
 
@@ -42,7 +42,7 @@ module Axlsx
42
42
  def colors=(v) DataTypeValidator.validate "BarSeries.colors", [Array], v; @colors = v end
43
43
 
44
44
  # The shabe of the bars or columns
45
- # must be one of [:percentStacked, :clustered, :standard, :stacked]
45
+ # must be one of [:cone, :coneToMax, :box, :cylinder, :pyramid, :pyramidToMax]
46
46
  def shape=(v)
47
47
  RestrictionValidator.validate "BarSeries.shape", [:cone, :coneToMax, :box, :cylinder, :pyramid, :pyramidToMax], v
48
48
  @shape = v
@@ -68,11 +68,16 @@ module Axlsx
68
68
  @anchors = SimpleTypedList.new [TwoCellAnchor, OneCellAnchor]
69
69
  end
70
70
 
71
- # Adds an image to the chart
71
+ # Adds an image to the chart If th end_at option is specified we create a two cell anchor. By default we use a one cell anchor.
72
72
  # @note The recommended way to manage images is to use Worksheet.add_image. Please refer to that method for documentation.
73
73
  # @see Worksheet#add_image
74
+ # @return [Pic]
74
75
  def add_image(options={})
75
- OneCellAnchor.new(self, options)
76
+ if options[:end_at]
77
+ TwoCellAnchor.new(self, options).add_pic(options)
78
+ else
79
+ OneCellAnchor.new(self, options)
80
+ end
76
81
  @anchors.last.object
77
82
  end
78
83
 
@@ -90,8 +90,7 @@ module Axlsx
90
90
  # @return [String]
91
91
  def extname
92
92
  File.extname(image_src).delete('.') unless image_src.nil?
93
- end
94
-
93
+ end
95
94
  # The index of this image in the workbooks images collections
96
95
  # @return [Index]
97
96
  def index
@@ -113,27 +112,32 @@ module Axlsx
113
112
  # @param [Integer] v
114
113
  # @see OneCellAnchor.width
115
114
  def width
115
+ return unless @anchor.is_a?(OneCellAnchor)
116
116
  @anchor.width
117
117
  end
118
118
 
119
119
  # @see width
120
120
  def width=(v)
121
+ use_one_cell_anchor unless @anchor.is_a?(OneCellAnchor)
121
122
  @anchor.width = v
122
123
  end
123
124
 
124
125
  # providing access to update the anchor's height attribute
125
126
  # @param [Integer] v
126
127
  # @see OneCellAnchor.width
128
+ # @note this is a noop if you are using a TwoCellAnchor
127
129
  def height
128
130
  @anchor.height
129
131
  end
130
132
 
131
133
  # @see height
134
+ # @note This is a noop if you are using a TwoCellAnchor
132
135
  def height=(v)
136
+ use_one_cell_anchor unless @anchor.is_a?(OneCellAnchor)
133
137
  @anchor.height = v
134
138
  end
135
-
136
- # This is a short cut method to set the start anchor position
139
+
140
+ # This is a short cut method to set the start anchor position
137
141
  # If you need finer granularity in positioning use
138
142
  # graphic_frame.anchor.from.colOff / rowOff
139
143
  # @param [Integer] x The column
@@ -142,6 +146,18 @@ module Axlsx
142
146
  def start_at(x, y)
143
147
  @anchor.from.col = x
144
148
  @anchor.from.row = y
149
+ @anchor.from
150
+ end
151
+
152
+ # noop if not using a two cell anchor
153
+ # @param [Integer] x The column
154
+ # @param [Integer] y The row
155
+ # @return [Marker]
156
+ def end_at(x, y)
157
+ use_two_cell_anchor unless @anchor.is_a?(TwoCellAnchor)
158
+ @anchor.to.col = x
159
+ @anchor.to.row = y
160
+ @anchor.to
145
161
  end
146
162
 
147
163
  # Serializes the object
@@ -162,6 +178,30 @@ module Axlsx
162
178
  str << '<a:prstGeom prst="rect"><a:avLst/></a:prstGeom></xdr:spPr></xdr:pic>'
163
179
 
164
180
  end
181
+
182
+ private
183
+
184
+ # Changes the anchor to a one cell anchor.
185
+ def use_one_cell_anchor
186
+ return if @anchor.is_a?(OneCellAnchor)
187
+ swap_anchor(OneCellAnchor.new(@anchor.drawing, :from => @anchor.from))
188
+ end
189
+
190
+ #changes the anchor type to a two cell anchor
191
+ def use_two_cell_anchor
192
+ return if @anchor.is_a?(TwoCellAnchor)
193
+ swap_anchor(TwoCellAnchor.new(@anchor.drawing)).tap do |new_anchor|
194
+ new_anchor.from.col = @anchor.from.col
195
+ new_anchor.from.row = @anchor.from.row
196
+ end
197
+ end
198
+
199
+ # refactoring of swapping code, law of demeter be damned!
200
+ def swap_anchor(new_anchor)
201
+ new_anchor.drawing.anchors.delete(new_anchor)
202
+ @anchor.drawing.anchors[@anchor.drawing.anchors.index(@anchor)] = new_anchor
203
+ @anchor = new_anchor
204
+ end
165
205
 
166
206
  end
167
207
  end
@@ -34,7 +34,7 @@ module Axlsx
34
34
  # @param [Drawing] drawing
35
35
  # @param [Class] chart_type This is passed to the graphic frame for instantiation. must be Chart or a subclass of Chart
36
36
  # @param object The object this anchor holds.
37
- # @option options [Array] start_at the col, row to start at
37
+ # @option options [Array] start_at the col, row to start at THIS IS DOCUMENTED BUT NOT IMPLEMENTED HERE!
38
38
  # @option options [Array] end_at the col, row to end at
39
39
  def initialize(drawing, options={})
40
40
  @drawing = drawing
@@ -49,6 +49,11 @@ module Axlsx
49
49
  @object.chart
50
50
  end
51
51
 
52
+ # Creates an image associated with this anchor.
53
+ def add_pic(options={})
54
+ @object = Pic.new(self, options)
55
+ end
56
+
52
57
  # The index of this anchor in the drawing
53
58
  # @return [Integer]
54
59
  def index
@@ -58,6 +58,7 @@ module Axlsx
58
58
  @top_offset = 2
59
59
  @right_offset = 50
60
60
  @bottom_offset = 5
61
+ @id = (0...8).map{65.+(rand(25)).chr}.join
61
62
  options.each do |o|
62
63
  self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
63
64
  end
@@ -99,14 +100,10 @@ module Axlsx
99
100
  def to_xml_string(str ='')
100
101
  str << <<SHAME_ON_YOU
101
102
 
102
- <v:shape id="" type="#_x0000_t202"
103
- style='position:absolute;margin-left:104pt;margin-top:2pt;width:800px;height:27pt;z-index:1;mso-wrap-style:tight'
104
- fillcolor="#ffffa1 [80]" o:insetmode="auto">
105
-
103
+ <v:shape id="#{@id}" type="#_x0000_t202" fillcolor="#ffffa1 [80]" o:insetmode="auto">
106
104
  <v:fill color2="#ffffa1 [80]"/>
107
105
  <v:shadow on="t" obscured="t"/>
108
106
  <v:path o:connecttype="none"/>
109
-
110
107
  <v:textbox style='mso-fit-text-with-word-wrap:t'>
111
108
  <div style='text-align:left'></div>
112
109
  </v:textbox>
@@ -10,7 +10,7 @@ require 'axlsx/rels/relationship.rb'
10
10
  def initialize
11
11
  super Relationship
12
12
  end
13
-
13
+
14
14
  def to_xml_string(str = '')
15
15
  str << '<?xml version="1.0" encoding="UTF-8"?>'
16
16
  str << '<Relationships xmlns="' << RELS_R << '">'
@@ -40,10 +40,10 @@ module Axlsx
40
40
  # @param [String] str
41
41
  # @return [String]
42
42
  def to_xml_string(str = '')
43
- attr = self.instance_values.select { |k, v| [:name, :pivot, :table].include? k }
44
- attr[:count] = self.size
43
+ attrs = instance_values.reject { |k, v| ![:name, :pivot, :table].include?(k) }
44
+ attrs[:count] = self.size
45
45
  str << '<tableStyle '
46
- str << attr.map { |key, value| '' << key.to_s << '="' << value.to_s << '"' }.join(' ')
46
+ str << attrs.map { |key, value| '' << key.to_s << '="' << value.to_s << '"' }.join(' ')
47
47
  str << '>'
48
48
  each { |table_style_el| table_style_el.to_xml_string(str) }
49
49
  str << '</tableStyle>'
@@ -155,19 +155,6 @@ module Axlsx
155
155
  str << '</' << el_name << '>'
156
156
  end
157
157
 
158
- # Serializes the list
159
- # If the serialize_as property is set, it is used as the parent node name.
160
- # If the serialize_as property is nil, the first item in the list of allowed_types will be used, having the first letter of the class changed to lower case.
161
- # @param [Nokogiri::XML::Builder] xml The document builder instance this objects xml will be added to.
162
- # @return [String]
163
- def to_xml(xml)
164
- classname = @allowed_types[0].name.split('::').last
165
- el_name = serialize_as || (classname[0,1].downcase + classname[1..-1])
166
- xml.send(el_name, :count=>@list.size) {
167
- @list.each { |item| item.to_xml(xml) }
168
- }
169
- end
170
-
171
158
  end
172
159
 
173
160
 
@@ -217,4 +217,25 @@ module Axlsx
217
217
  RestrictionValidator.validate :table_element_type, [:wholeTable, :headerRow, :totalRow, :firstColumn, :lastColumn, :firstRowStripe, :secondRowStripe, :firstColumnStripe, :secondColumnStripe, :firstHeaderCell, :lastHeaderCell, :firstTotalCell, :lastTotalCell, :firstSubtotalColumn, :secondSubtotalColumn, :thirdSubtotalColumn, :firstSubtotalRow, :secondSubtotalRow, :thirdSubtotalRow, :blankRow, :firstColumnSubheading, :secondColumnSubheading, :thirdColumnSubheading, :firstRowSubheading, :secondRowSubheading, :thirdRowSubheading, :pageFieldLabels, :pageFieldValues], v
218
218
  end
219
219
 
220
+ # Requires that the value is a valid data_validation_error_style
221
+ # :information, :stop, :warning
222
+ # @param [Any] v The value validated
223
+ def self.validate_data_validation_error_style(v)
224
+ RestrictionValidator.validate :validate_data_validation_error_style, [:information, :stop, :warning], v
225
+ end
226
+
227
+ # Requires that the value is valid data validation operator.
228
+ # valid operators must be one of lessThan, lessThanOrEqual, equal,
229
+ # notEqual, greaterThanOrEqual, greaterThan, between, notBetween
230
+ # @param [Any] v The value validated
231
+ def self.validate_data_validation_operator(v)
232
+ RestrictionValidator.validate :data_validation_operator, [:lessThan, :lessThanOrEqual, :equal, :notEqual, :greaterThanOrEqual, :greaterThan, :between, :notBetween], v
233
+ end
234
+
235
+ # Requires that the value is valid data validation type.
236
+ # valid types must be one of custom, data, decimal, list, none, textLength, time, whole
237
+ # @param [Any] v The value validated
238
+ def self.validate_data_validation_type(v)
239
+ RestrictionValidator.validate :data_validation_type, [:custom, :data, :decimal, :list, :none, :textLength, :time, :whole], v
240
+ end
220
241
  end
@@ -5,6 +5,6 @@ module Axlsx
5
5
  # When using bunle exec rake and referencing the gem on github or locally
6
6
  # it will use the gemspec, which preloads this constant for the gem's version.
7
7
  # We check to make sure that it has not already been loaded
8
- VERSION="1.1.5" unless defined? Axlsx::VERSION
8
+ VERSION="1.1.6" unless defined? Axlsx::VERSION
9
9
 
10
10
  end
@@ -16,9 +16,11 @@ require 'axlsx/workbook/worksheet/row.rb'
16
16
  require 'axlsx/workbook/worksheet/col.rb'
17
17
  require 'axlsx/workbook/worksheet/comments.rb'
18
18
  require 'axlsx/workbook/worksheet/comment.rb'
19
+ require 'axlsx/workbook/worksheet/sheet_protection.rb'
19
20
  require 'axlsx/workbook/worksheet/worksheet.rb'
20
21
  require 'axlsx/workbook/shared_strings_table.rb'
21
22
  require 'axlsx/workbook/worksheet/table.rb'
23
+ require 'axlsx/workbook/worksheet/data_validation.rb'
22
24
 
23
25
  # The Workbook class is an xlsx workbook that manages worksheets, charts, drawings and styles.
24
26
  # The following parts of the Office Open XML spreadsheet specification are not implimented in this version.
@@ -195,7 +195,7 @@ module Axlsx
195
195
  # @option options [Symbol] scheme must be one of :none, major, :minor
196
196
  def initialize(row, value="", options={})
197
197
  self.row=row
198
- @font_name = @charset = @family = @b = @i = @strike = @outline = @shadow = nil
198
+ @value = @font_name = @charset = @family = @b = @i = @strike = @outline = @shadow = nil
199
199
  @condense = @u = @vertAlign = @sz = @color = @scheme = @extend = @ssti = nil
200
200
  @styles = row.worksheet.workbook.styles
201
201
  @row.cells << self
@@ -261,9 +261,8 @@ module Axlsx
261
261
  # @return [String]
262
262
  def run_xml_string(str = '')
263
263
  if is_text_run?
264
- data = self.instance_values.reject{|key, value| value == nil }
264
+ data = instance_values.reject{|key, value| value == nil || key == 'value' || key == 'type' }
265
265
  keys = data.keys & INLINE_STYLES
266
- keys.delete ['value', 'type']
267
266
  str << "<r><rPr>"
268
267
  keys.each do |key|
269
268
  case key
@@ -272,7 +271,7 @@ module Axlsx
272
271
  when 'color'
273
272
  str << data[key].to_xml_string
274
273
  else
275
- "<" << key.to_s << " val='" << data[key].to_s << "'/>"
274
+ str << "<" << key.to_s << " val='" << data[key].to_s << "'/>"
276
275
  end
277
276
  end
278
277
  str << "</rPr>" << "<t>" << value.to_s << "</t></r>"
@@ -41,12 +41,6 @@ module Axlsx
41
41
  @vml_shape ||= initialize_vml_shape
42
42
  end
43
43
 
44
- # The index of this comment
45
- # @return [Integer]
46
- def index
47
- @comments.index(self)
48
- end
49
-
50
44
  #
51
45
  # The index of this author in a unique sorted list of all authors in
52
46
  # the comment.
@@ -96,10 +90,10 @@ module Axlsx
96
90
  def initialize_vml_shape
97
91
  pos = Axlsx::name_to_indices(ref)
98
92
  @vml_shape = VmlShape.new(:row => pos[1], :column => pos[0]) do |vml|
99
- vml.left_column = vml.row + 1
100
- vml.right_column = vml.column + 4
93
+ vml.left_column = vml.column
94
+ vml.right_column = vml.column + 2
101
95
  vml.top_row = vml.row
102
- vml.bottom_row = vml.row + 4
96
+ vml.bottom_row = vml.row + 4
103
97
  end
104
98
  end
105
99
 
@@ -0,0 +1,245 @@
1
+ # encoding: UTF-8
2
+ module Axlsx
3
+ # Data validation allows the validation of cell data
4
+ #
5
+ # @note The recommended way to manage data validations is via Worksheet#add_data_validation
6
+ # @see Worksheet#add_data_validation
7
+ class DataValidation
8
+
9
+ # instance values that must be serialized as their own elements - e.g. not attributes.
10
+ CHILD_ELEMENTS = [:formula1, :formula2]
11
+
12
+ # Formula1
13
+ # Available for type whole, decimal, date, time, textLength, list, custom
14
+ # @see type
15
+ # @return [String]
16
+ # default nil
17
+ attr_reader :formula1
18
+
19
+ # Formula2
20
+ # Available for type whole, decimal, date, time, textLength
21
+ # @see type
22
+ # @return [String]
23
+ # default nil
24
+ attr_reader :formula2
25
+
26
+ # Allow Blank
27
+ # A boolean value indicating whether the data validation allows the use of empty or blank
28
+ # entries. 1 means empty entries are OK and do not violate the validation constraints.
29
+ # Available for type whole, decimal, date, time, textLength, list, custom
30
+ # @see type
31
+ # @return [Boolean]
32
+ # default true
33
+ attr_reader :allowBlank
34
+
35
+ # Error Message
36
+ # Message text of error alert.
37
+ # Available for type whole, decimal, date, time, textLength, list, custom
38
+ # @see type
39
+ # @return [String]
40
+ # default nil
41
+ attr_reader :error
42
+
43
+ # Error Style (ST_DataValidationErrorStyle)
44
+ # The style of error alert used for this data validation.
45
+ # Options are:
46
+ # * information: This data validation error style uses an information icon in the error alert.
47
+ # * stop: This data validation error style uses a stop icon in the error alert.
48
+ # * warning: This data validation error style uses a warning icon in the error alert.
49
+ # Available for type whole, decimal, date, time, textLength, list, custom
50
+ # @see type
51
+ # @return [Symbol]
52
+ # default :stop
53
+ attr_reader :errorStyle
54
+
55
+ # Error Title
56
+ # Title bar text of error alert.
57
+ # Available for type whole, decimal, date, time, textLength, list, custom
58
+ # @see type
59
+ # @return [String]
60
+ # default nil
61
+ attr_reader :errorTitle
62
+
63
+ # Operator (ST_DataValidationOperator)
64
+ # The relational operator used with this data validation.
65
+ # Options are:
66
+ # * between: Data validation which checks if a value is between two other values.
67
+ # * equal: Data validation which checks if a value is equal to a specified value.
68
+ # * greater_than: Data validation which checks if a value is greater than a specified value.
69
+ # * greater_than_or_equal: Data validation which checks if a value is greater than or equal to a specified value.
70
+ # * less_than: Data validation which checks if a value is less than a specified value.
71
+ # * less_than_or_equal: Data validation which checks if a value is less than or equal to a specified value.
72
+ # * not_between: Data validation which checks if a value is not between two other values.
73
+ # * not_equal: Data validation which checks if a value is not equal to a specified value.
74
+ # Available for type whole, decimal, date, time, textLength
75
+ # @see type
76
+ # @return [Symbol]
77
+ # default nil
78
+ attr_reader :operator
79
+
80
+ # Input prompt
81
+ # Message text of input prompt.
82
+ # Available for type whole, decimal, date, time, textLength, list, custom
83
+ # @see type
84
+ # @return [String]
85
+ # default nil
86
+ attr_reader :prompt
87
+
88
+ # Prompt title
89
+ # Title bar text of input prompt.
90
+ # Available for type whole, decimal, date, time, textLength, list, custom
91
+ # @see type
92
+ # @return [String]
93
+ # default nil
94
+ attr_reader :promptTitle
95
+
96
+ # Show drop down
97
+ # A boolean value indicating whether to display a dropdown combo box for a list type data
98
+ # validation. Be careful: false shows the dropdown list!
99
+ # Available for type list
100
+ # @see type
101
+ # @return [Boolean]
102
+ # default false
103
+ attr_reader :showDropDown
104
+
105
+ # Show error message
106
+ # A boolean value indicating whether to display the error alert message when an invalid
107
+ # value has been entered, according to the criteria specified.
108
+ # Available for type whole, decimal, date, time, textLength, list, custom
109
+ # @see type
110
+ # @return [Boolean]
111
+ # default false
112
+ attr_reader :showErrorMessage
113
+
114
+ # Show input message
115
+ # A boolean value indicating whether to display the input prompt message.
116
+ # Available for type whole, decimal, date, time, textLength, list, custom
117
+ # @see type
118
+ # @return [Boolean]
119
+ # default false
120
+ attr_reader :showInputMessage
121
+
122
+ # Range over which data validation is applied, in "A1:B2" format
123
+ # Available for type whole, decimal, date, time, textLength, list, custom
124
+ # @see type
125
+ # @return [String]
126
+ # default nil
127
+ attr_reader :sqref
128
+
129
+ # The type (ST_DataValidationType) of data validation.
130
+ # Options are:
131
+ # * custom: Data validation which uses a custom formula to check the cell value.
132
+ # * date: Data validation which checks for date values satisfying the given condition.
133
+ # * decimal: Data validation which checks for decimal values satisfying the given condition.
134
+ # * list: Data validation which checks for a value matching one of list of values.
135
+ # * none: No data validation.
136
+ # * textLength: Data validation which checks for text values, whose length satisfies the given condition.
137
+ # * time: Data validation which checks for time values satisfying the given condition.
138
+ # * whole: Data validation which checks for whole number values satisfying the given condition.
139
+ # @return [Symbol]
140
+ # default none
141
+ attr_reader :type
142
+
143
+ # Creates a new {DataValidation} object
144
+ # @option options [String] formula1
145
+ # @option options [String] formula2
146
+ # @option options [Boolean] allowBlank - A boolean value indicating whether the data validation allows the use of empty or blank entries.
147
+ # @option options [String] error - Message text of error alert.
148
+ # @option options [Symbol] errorStyle - The style of error alert used for this data validation.
149
+ # @option options [String] errorTitle - itle bar text of error alert.
150
+ # @option options [Symbol] operator - The relational operator used with this data validation.
151
+ # @option options [String] prompt - Message text of input prompt.
152
+ # @option options [String] promptTitle - Title bar text of input prompt.
153
+ # @option options [Boolean] showDropDown - A boolean value indicating whether to display a dropdown combo box for a list type data validation
154
+ # @option options [Boolean] showErrorMessage - A boolean value indicating whether to display the error alert message when an invalid value has been entered, according to the criteria specified.
155
+ # @option options [Boolean] showInputMessage - A boolean value indicating whether to display the input prompt message.
156
+ # @option options [String] sqref - Range over which data validation is applied, in "A1:B2" format.
157
+ # @option options [Symbol] type - The type of data validation.
158
+ def initialize(options={})
159
+ # defaults
160
+ @formula1 = @formula2 = @error = @errorTitle = @operator = @prompt = @promptTitle = @sqref = nil
161
+ @allowBlank = @showErrorMessage = true
162
+ @showDropDown = @showInputMessage = false
163
+ @type = :none
164
+ @errorStyle = :stop
165
+
166
+ options.each do |o|
167
+ self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
168
+ end
169
+ end
170
+
171
+ # @see formula1
172
+ def formula1=(v); Axlsx::validate_string(v); @formula1 = v end
173
+
174
+ # @see formula2
175
+ def formula2=(v); Axlsx::validate_string(v); @formula2 = v end
176
+
177
+ # @see allowBlank
178
+ def allowBlank=(v); Axlsx::validate_boolean(v); @allowBlank = v end
179
+
180
+ # @see error
181
+ def error=(v); Axlsx::validate_string(v); @error = v end
182
+
183
+ # @see errorStyle
184
+ def errorStyle=(v); Axlsx::validate_data_validation_error_style(v); @errorStyle = v end
185
+
186
+ # @see errorTitle
187
+ def errorTitle=(v); Axlsx::validate_string(v); @errorTitle = v end
188
+
189
+ # @see operator
190
+ def operator=(v); Axlsx::validate_data_validation_operator(v); @operator = v end
191
+
192
+ # @see prompt
193
+ def prompt=(v); Axlsx::validate_string(v); @prompt = v end
194
+
195
+ # @see promptTitle
196
+ def promptTitle=(v); Axlsx::validate_string(v); @promptTitle = v end
197
+
198
+ # @see showDropDown
199
+ def showDropDown=(v); Axlsx::validate_boolean(v); @showDropDown = v end
200
+
201
+ # @see showErrorMessage
202
+ def showErrorMessage=(v); Axlsx::validate_boolean(v); @showErrorMessage = v end
203
+
204
+ # @see showInputMessage
205
+ def showInputMessage=(v); Axlsx::validate_boolean(v); @showInputMessage = v end
206
+
207
+ # @see sqref
208
+ def sqref=(v); Axlsx::validate_string(v); @sqref = v end
209
+
210
+ # @see type
211
+ def type=(v); Axlsx::validate_data_validation_type(v); @type = v end
212
+
213
+ # Serializes the data validation
214
+ # @param [String] str
215
+ # @return [String]
216
+ def to_xml_string(str = '')
217
+ valid_attributes = get_valid_attributes
218
+
219
+ str << '<dataValidation '
220
+ str << instance_values.map { |key, value| '' << key << '="' << value.to_s << '"' if (valid_attributes.include?(key.to_sym) and not CHILD_ELEMENTS.include?(key.to_sym)) }.join(' ')
221
+ str << '>'
222
+ str << '<formula1>' << self.formula1 << '</formula1>' if @formula1 and valid_attributes.include?(:formula1)
223
+ str << '<formula2>' << self.formula2 << '</formula2>' if @formula2 and valid_attributes.include?(:formula2)
224
+ str << '</dataValidation>'
225
+ end
226
+
227
+ private
228
+ def get_valid_attributes
229
+ attributes = [:allowBlank, :error, :errorStyle, :errorTitle, :prompt, :promptTitle, :showErrorMessage, :showInputMessage, :sqref, :type ]
230
+
231
+ if [:whole, :decimal, :data, :time, :textLength].include?(@type)
232
+ attributes << [:operator, :formula1]
233
+ attributes << [:formula2] if [:between, :notBetween].include?(@operator)
234
+ elsif @type == :list
235
+ attributes << [:showDropDown, :formula1]
236
+ elsif @type == :custom
237
+ attributes << [:formula1]
238
+ else
239
+ attributes = []
240
+ end
241
+
242
+ attributes.flatten!
243
+ end
244
+ end
245
+ end