# -*- encoding: utf-8 -*- # frozen_string_literal: true module Writexlsx class Worksheet class DataValidation # :nodoc: include Writexlsx::Utility attr_reader :value, :source, :minimum, :maximum, :validate, :criteria attr_reader :error_type, :cells, :other_cells attr_reader :ignore_blank, :dropdown, :show_input, :show_error attr_reader :error_title, :error_message, :input_title, :input_message def initialize(*args) # Check for a cell reference in A1 notation and substitute row and column. row1, col1, row2, col2, options = row_col_notation(args) if row2.respond_to?(:keys) options_to_instance_variable(row2.dup) row2 = row1 col2 = col1 elsif options.respond_to?(:keys) options_to_instance_variable(options.dup) else raise WriteXLSXInsufficientArgumentError end raise WriteXLSXInsufficientArgumentError if [row1, col1, row2, col2].include?(nil) check_for_valid_input_params check_dimensions(row1, col1) check_dimensions(row2, col2) @cells = [[row1, col1, row2, col2]] @value = @source if @source @value = @minimum if @minimum @validate = valid_validation_type[@validate.downcase] # No action is required for validate type 'any' # unless there are input messages. if @validate == 'none' && !@input_message && !@input_title @validate_none = true return end # The any, list and custom validations don't have a criteria # so we use a default of 'between' if %w[none list custom].include?(@validate) @criteria = 'between' @maximum = nil end check_criteria_required check_valid_citeria_types @criteria = valid_criteria_type[@criteria.downcase] check_maximum_value_when_criteria_is_between_or_notbetween @error_type = has_key?(:error_type) ? error_type_hash[@error_type.downcase] : 0 convert_date_time_value_if_required # Check that the input title doesn't exceed the maximum length. raise "Length of input title '#{@input_title}' exceeds Excel's limit of 32" if @input_title && @input_title.length > 32 # Check that the input message doesn't exceed the maximum length. raise "Length of input message '#{@input_message}' exceeds Excel's limit of 255" if @input_message && @input_message.length > 255 set_some_defaults # A (for now) undocumented parameter to pass additional cell ranges. @other_cells.each { |cells| @cells << cells } if has_key?(:other_cells) end def options_to_instance_variable(params) params.each do |k, v| instance_variable_set("@#{k}", v) end end def keys instance_variables.collect { |v| v.to_s.sub(/@/, '').to_sym } end def validate_none? @validate_none end # # Write the element. # def write_data_validation(writer) # :nodoc: @writer = writer if @validate == 'none' @writer.empty_tag('dataValidation', attributes) else @writer.tag_elements('dataValidation', attributes) do # Write the formula1 element. write_formula_1(@value) # Write the formula2 element. write_formula_2(@maximum) if @maximum end end end private # # Write the element. # def write_formula_1(formula) # :nodoc: # Convert a list array ref into a comma separated string. formula = %("#{formula.join(",")}") if formula.is_a?(Array) formula = formula.sub(/^=/, '') if formula.respond_to?(:sub) @writer.data_element('formula1', formula) end # # Write the element. # def write_formula_2(formula) # :nodoc: formula = formula.sub(/^=/, '') if formula.respond_to?(:sub) @writer.data_element('formula2', formula) end def attributes sqref = '' attributes = [] # Set the cell range(s) for the data validation. @cells.each do |cells| # Add a space between multiple cell ranges. sqref += ' ' if sqref != '' row_first, col_first, row_last, col_last = cells # Swap last row/col for first row/col as necessary row_first, row_last = row_last, row_first if row_first > row_last col_first, col_last = col_last, col_first if col_first > col_last sqref += xl_range(row_first, row_last, col_first, col_last) end if @validate != 'none' attributes << ['type', @validate] attributes << ['operator', @criteria] if @criteria != 'between' end if @error_type attributes << %w[errorStyle warning] if @error_type == 1 attributes << %w[errorStyle information] if @error_type == 2 end attributes << ['allowBlank', 1] if @ignore_blank != 0 attributes << ['showDropDown', 1] if @dropdown == 0 attributes << ['showInputMessage', 1] if @show_input != 0 attributes << ['showErrorMessage', 1] if @show_error != 0 attributes << ['errorTitle', @error_title] if @error_title attributes << ['error', @error_message] if @error_message attributes << ['promptTitle', @input_title] if @input_title attributes << ['prompt', @input_message] if @input_message attributes << ['sqref', sqref] end def has_key?(key) keys.index(key) end def set_some_defaults @ignore_blank ||= 1 @dropdown ||= 1 @show_input ||= 1 @show_error ||= 1 end def check_for_valid_input_params check_parameter(self, valid_validation_parameter, 'data_validation') raise WriteXLSXOptionParameterError, "Parameter :validate is required in data_validation()" unless has_key?(:validate) unless valid_validation_type.has_key?(@validate.downcase) raise WriteXLSXOptionParameterError, "Unknown validation type '#{@validate}' for parameter :validate in data_validation()" end if @error_type && !error_type_hash.has_key?(@error_type.downcase) raise WriteXLSXOptionParameterError, "Unknown criteria type '#param[:error_type}' for parameter :error_type in data_validation()" end end def check_criteria_required raise WriteXLSXOptionParameterError, "Parameter :criteria is required in data_validation()" unless has_key?(:criteria) end def check_maximum_value_when_criteria_is_between_or_notbetween if @criteria == 'between' || @criteria == 'notBetween' unless has_key?(:maximum) raise WriteXLSXOptionParameterError, "Parameter :maximum is required in data_validation() when using :between or :not between criteria" end else @maximum = nil end end def check_valid_citeria_types unless valid_criteria_type.has_key?(@criteria.downcase) raise WriteXLSXOptionParameterError, "Unknown criteria type '#{@criteria}' for parameter :criteria in data_validation()" end end def convert_date_time_value_if_required if @validate == 'date' || @validate == 'time' date_time = convert_date_time(@value) @value = date_time if date_time if @maximum date_time = convert_date_time(@maximum) @maximum = date_time if date_time end end end def error_type_hash { 'stop' => 0, 'warning' => 1, 'information' => 2 } end def valid_validation_type # :nodoc: { 'any' => 'none', 'any value' => 'none', 'whole number' => 'whole', 'whole' => 'whole', 'integer' => 'whole', 'decimal' => 'decimal', 'list' => 'list', 'date' => 'date', 'time' => 'time', 'text length' => 'textLength', 'length' => 'textLength', 'custom' => 'custom' } end # List of valid input parameters. def valid_validation_parameter %i[ validate criteria value source minimum maximum ignore_blank dropdown show_input input_title input_message show_error error_title error_message error_type other_cells ] end # List of valid criteria types. def valid_criteria_type # :nodoc: { 'between' => 'between', 'not between' => 'notBetween', 'equal to' => 'equal', '=' => 'equal', '==' => 'equal', 'not equal to' => 'notEqual', '!=' => 'notEqual', '<>' => 'notEqual', 'greater than' => 'greaterThan', '>' => 'greaterThan', 'less than' => 'lessThan', '<' => 'lessThan', 'greater than or equal to' => 'greaterThanOrEqual', '>=' => 'greaterThanOrEqual', 'less than or equal to' => 'lessThanOrEqual', '<=' => 'lessThanOrEqual' } end def date_1904? @date_1904 end end end end