axlsx 1.1.5 → 1.1.6

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.
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