mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-16 08:34:10 +02:00
Fix for bug 661
Introduce *DECIMAL-SEPARATOR* global for Nyquist. Improvements to numeric validation error messages. Fix *TRACK* START-TIME and END-TIME properties for tracks with different length channels. Update Adjustable Fade, Regular Interval Labels and Vocal Removal to use numeric text inputs. This does NOT fix bug 1020.
This commit is contained in:
parent
7608e9cb52
commit
57d1f5583d
plug-ins
src
@ -19,14 +19,15 @@
|
|||||||
;control type "Fade Type" choice "Fade Up,Fade Down,S-Curve Up,S-Curve Down" 0
|
;control type "Fade Type" choice "Fade Up,Fade Down,S-Curve Up,S-Curve Down" 0
|
||||||
;control curve "Mid-fade Adjust (%)" real "" 0 -100 100
|
;control curve "Mid-fade Adjust (%)" real "" 0 -100 100
|
||||||
;control units "Start/End as" choice "% of Original,dB Gain" 0
|
;control units "Start/End as" choice "% of Original,dB Gain" 0
|
||||||
;control gain-string-0 "Start (or end)" string "" "0" ""
|
;control gain0 "Start (or end)" float-text "" 0 nil nil
|
||||||
;control gain-string-1 "End (or start)" string "" "100" ""
|
;control gain1 "End (or start)" float-text "" 100 nil nil
|
||||||
;control preset " Handy Presets\n(override controls)" choice "None Selected,Linear In,Linear Out,Exponential In,Exponential Out,Logarithmic In,Logarithmic Out,Rounded In,Rounded Out,Cosine In,Cosine Out,S-Curve In,S-Curve Out" 0
|
;control preset " Handy Presets\n(override controls)" choice "None Selected,Linear In,Linear Out,Exponential In,Exponential Out,Logarithmic In,Logarithmic Out,Rounded In,Rounded Out,Cosine In,Cosine Out,S-Curve In,S-Curve Out" 0
|
||||||
|
|
||||||
|
|
||||||
(defun get-input (sig)
|
(defun get-input (sig)
|
||||||
"Preview takes the entire selection so that we know the correct
|
"Preview takes the entire selection so that we know the correct
|
||||||
selection length, but preview only needs to process preview length."
|
selection length, but preview only needs to process preview length."
|
||||||
|
;; (if *previewp* sig (multichan-expand #'trim-input sig)))
|
||||||
(if (get '*track* 'view) ;NIL if preview
|
(if (get '*track* 'view) ;NIL if preview
|
||||||
sig
|
sig
|
||||||
(multichan-expand #'trim-input sig)))
|
(multichan-expand #'trim-input sig)))
|
||||||
@ -37,42 +38,23 @@ selection length, but preview only needs to process preview length."
|
|||||||
(get '*project* 'preview-duration))))
|
(get '*project* 'preview-duration))))
|
||||||
(setf sig (extract-abs 0 dur *track*))))
|
(setf sig (extract-abs 0 dur *track*))))
|
||||||
|
|
||||||
|
|
||||||
(setq err "")
|
|
||||||
; bad things may happen outside of the slider range.
|
|
||||||
(setq curve (min 1 (max -1 (/ curve 100.0))))
|
|
||||||
|
|
||||||
;;; Convert string to value
|
|
||||||
(defun string-to-val (string)
|
|
||||||
(setq val-array
|
|
||||||
(read (make-string-input-stream (format nil "(~a)" string))))
|
|
||||||
(if (and (= (length val-array) 1)
|
|
||||||
(numberp (car val-array)))
|
|
||||||
(let ((val (float (car val-array))))
|
|
||||||
(case units
|
|
||||||
(0 (/ val 100))
|
|
||||||
(t (db-to-linear val))))))
|
|
||||||
|
|
||||||
;;; invalid string error
|
|
||||||
(defun invalid-string (x y)
|
|
||||||
(unless x
|
|
||||||
(setf err (format nil "~aYou entered \"~a\"~%~
|
|
||||||
\"Start (or end):\" must be one number.~%"
|
|
||||||
err
|
|
||||||
gain-string-0)))
|
|
||||||
(unless y
|
|
||||||
(setf err (format nil "~aYou entered \"~a\"~%~
|
|
||||||
\"End (or start):\" must be one number.~%"
|
|
||||||
err gain-string-1)))
|
|
||||||
err)
|
|
||||||
|
|
||||||
;;; invalid values
|
;;; invalid values
|
||||||
(defun check-values (x y)
|
(defun check-values (x y)
|
||||||
(when (or (< x 0)(< y 0))
|
(if (= units 0) ;percentage values
|
||||||
(setf err (format nil "~a% values cannot be negative.~%" err))))
|
(cond
|
||||||
|
((or (< x 0)(< y 0))
|
||||||
|
(throw 'err (format nil "~aPercentage values cannot be negative." err)))
|
||||||
|
((or (> x 1000)(> y 1000))
|
||||||
|
(throw 'err (format nil "~aPercentage values cannot be more than 1000 %." err))))
|
||||||
|
(cond ;dB values
|
||||||
|
((or (> x 100)(> y 100))
|
||||||
|
(throw 'err (format nil "~adB values cannot be more than +100 dB.~%~%~
|
||||||
|
Hint: 6 dB doubles the amplitude~%~
|
||||||
|
\t-6 dB halves the amplitude." err))))))
|
||||||
|
|
||||||
;;; select and apply fade
|
;;; select and apply fade
|
||||||
(defun fade (sig type curve g0 g1)
|
(defun fade (sig type curve g0 g1)
|
||||||
|
(check-values gain0 gain1)
|
||||||
(mult (get-input sig)
|
(mult (get-input sig)
|
||||||
(case preset
|
(case preset
|
||||||
(0 (case type ; Custom fade
|
(0 (case type ; Custom fade
|
||||||
@ -154,8 +136,8 @@ selection length, but preview only needs to process preview length."
|
|||||||
(setf env
|
(setf env
|
||||||
(control-srate-abs *sound-srate* ; sound srate required for accuracy.
|
(control-srate-abs *sound-srate* ; sound srate required for accuracy.
|
||||||
(cond
|
(cond
|
||||||
((= g0 g1) g0) ; amplify
|
((= g0 g1) g0) ; amplify
|
||||||
((> g0 g1) ; fade down
|
((> g0 g1) ; fade down
|
||||||
(snd-exp
|
(snd-exp
|
||||||
(mult (pwlv (- 1 curve) 1 1)
|
(mult (pwlv (- 1 curve) 1 1)
|
||||||
(snd-log (raised-cosin 90)))))
|
(snd-log (raised-cosin 90)))))
|
||||||
@ -181,8 +163,8 @@ selection length, but preview only needs to process preview length."
|
|||||||
(if (= direction 0)
|
(if (= direction 0)
|
||||||
(setf env (pwev x 1 1))
|
(setf env (pwev x 1 1))
|
||||||
(setf env (pwev 1 1 x)))
|
(setf env (pwev 1 1 x)))
|
||||||
(mult (/ (- 1 x)) ; normalize to 0 dB
|
(mult (/ (- 1 x)) ; normalize to 0 dB
|
||||||
(diff env x))))) ; drop down to silence
|
(diff env x))))) ; drop down to silence
|
||||||
|
|
||||||
;;; curve scaling for S-curve
|
;;; curve scaling for S-curve
|
||||||
(defun exp-scale-mid (x)
|
(defun exp-scale-mid (x)
|
||||||
@ -190,11 +172,18 @@ selection length, but preview only needs to process preview length."
|
|||||||
(/ (- (exp (- 1 x)) e)
|
(/ (- (exp (- 1 x)) e)
|
||||||
(- 1 e))))
|
(- 1 e))))
|
||||||
|
|
||||||
(let ((gain0 (string-to-val gain-string-0))
|
(defmacro gainscale (gain type)
|
||||||
(gain1 (string-to-val gain-string-1)))
|
`(setf ,gain
|
||||||
(if (and gain0 gain1)
|
(if (= ,type 0) ; percent
|
||||||
(check-values gain0 gain1)
|
(/ ,gain 100.0)
|
||||||
(setf err (format nil "~a" (invalid-string gain0 gain1))))
|
(db-to-linear ,gain))))
|
||||||
(if (= (length err) 0)
|
|
||||||
(fade *track* type curve gain0 gain1)
|
|
||||||
(format nil "Error.~%~a." err)))
|
(setf curve (/ curve 100.0))
|
||||||
|
(setf gain0 (gainscale gain0 units))
|
||||||
|
(setf gain1 (gainscale gain1 units))
|
||||||
|
(setf err "Error\n\n")
|
||||||
|
|
||||||
|
|
||||||
|
(catch 'err (fade *track* type curve gain0 gain1))
|
||||||
|
|
||||||
|
@ -1,153 +1,138 @@
|
|||||||
;nyquist plug-in
|
;nyquist plug-in
|
||||||
;version 3
|
;version 4
|
||||||
;type analyze
|
;type analyze
|
||||||
;name "Regular Interval Labels..."
|
;name "Regular Interval Labels..."
|
||||||
;action "Adding equally-spaced labels to the label track..."
|
;action "Adding equally-spaced labels to the label track..."
|
||||||
;author "David R. Sky"
|
;author "Steve Daulton"
|
||||||
;copyright "Released under terms of the GNU General Public License version 2"
|
;copyright "Released under terms of the GNU General Public License version 2"
|
||||||
|
|
||||||
;; by David R. Sky (http://www.garyallendj.com/davidsky/), June-October 2007.
|
;; Released under terms of the GNU General Public License version 2:
|
||||||
;; Code for label placement based on silencemarker.ny by Alex S.Brown.
|
;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||||
;; Updated by Steve Daulton (http://easyspacepro.com)
|
;;
|
||||||
|
;; Original version by David R. Sky (http://www.garyallendj.com/davidsky/) 2007.
|
||||||
|
;; Based on an idea by Sami Jumppanen, with contributions from
|
||||||
|
;; Alex S.Brown, Dominic Mazzoni, Pierre M.I., Gale Andrews, Steve Daulton.
|
||||||
|
;; This version by Steve Daulton (http://easyspacepro.com) 2016
|
||||||
|
|
||||||
;; Released under terms of the GNU General Public License version 2
|
;control mode "Use 'Number of labels' OR 'Label interval'" choice "Number of Labels,Label Interval" 0
|
||||||
;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html .
|
;control number "Number of labels" int-text "" 10 1 1000
|
||||||
;; Thanks Sami Jumppanen for plug-in suggestion.
|
;control interval "Label interval (seconds)" float-text "" 60 0.001 1000
|
||||||
;; Thanks Dominic Mazzoni, Pierre M.I., Gale Andrews
|
;control adjust "Adjust label interval to fit length" choice "No,Yes" 0
|
||||||
;; for improvement suggestions.
|
;control labeltext "Label text" string "" "Label" ""
|
||||||
;; Disallow labels before zero by Steve Daulton, September 2010.
|
;control zeros "Minimum number of digits in label" choice "None - text only,1 (before label),2 (before label),3 (before label),1 (after label),2 (after label),3 (after label)" 2
|
||||||
;; Enhanced label numbering by Steve Daulton, April 17th 2011.
|
;control labelnum "Begin numbering from" int-text "" 1 0 nil
|
||||||
;; Error checking by Steve Daulton, April 18th 2011.
|
|
||||||
;; Final Label bug fixed and minor GUI modifications. SD Apr 2011
|
|
||||||
;; Requires Audacity 1.3.4 or later.
|
|
||||||
|
|
||||||
;control start "Time to place first label [seconds]" string " " "0.0"
|
|
||||||
;control placement "Label placement method" choice "Label interval,Number of labels" 0
|
|
||||||
;control time "Set either: Label interval [seconds]" string " " "60.0"
|
|
||||||
;control label-number "Or: Number of labels" int "" 10 2 100
|
|
||||||
;control text "Label text" string "" "Label"
|
|
||||||
;control labelnum "Minimum number of digits in label" choice "None - text only,1 (before label),2 (before label),3 (before label),1 (after label),2 (after label),3 (after label)" 2
|
|
||||||
;control countfrom "Begin numbering from" int "" 1 0 100
|
|
||||||
;control include "Add final label" choice "No,Yes" 0
|
|
||||||
;control t-choice "Adjust label interval to fit length" choice "No,Yes" 1
|
|
||||||
|
|
||||||
; function to convert string to list
|
|
||||||
(defun string-to-list (str)
|
|
||||||
(read (make-string-input-stream (format nil "(~a)" str))))
|
|
||||||
|
|
||||||
|
|
||||||
; function to calculate new time value
|
(defun make-labels ()
|
||||||
; if user value does not create equal duration final audio chunk
|
(when (and (= mode 1)(= adjust 1)) ;adjust interval to fit
|
||||||
; returns new time value
|
(setf interval (get-interval)))
|
||||||
(defun new-time (time dur check-labels)
|
(validate)
|
||||||
; calculate duration of last audio chunk
|
(let ((labels ()))
|
||||||
(setf last-label (- dur (* time check-labels)))
|
(cond
|
||||||
(if (< last-label (* time 0.5)) ; last chunk is less than 1/2 time
|
((= mode 0) ;number of labels
|
||||||
(/ dur (- check-labels 1))
|
(setf interval (/ (get-duration 1) number))
|
||||||
(/ dur check-labels)))
|
(do* ((i 0 (1+ i))
|
||||||
|
(labelnum labelnum (1+ labelnum)))
|
||||||
|
((= i number))
|
||||||
|
(push (make-label (* i interval) labelnum) labels))
|
||||||
|
;print what we've done to debug window
|
||||||
|
(if (= number 1)
|
||||||
|
(format t "1 label requested.")
|
||||||
|
(format t "~a labels at intervals of ~a seconds."
|
||||||
|
number interval)))
|
||||||
|
(t
|
||||||
|
(setf counter 0)
|
||||||
|
(do* ((i 0 (1+ i))
|
||||||
|
(labelnum labelnum (1+ labelnum))
|
||||||
|
(time 0 (* i interval)))
|
||||||
|
((>= (round-to-sample time) (get-duration 1)))
|
||||||
|
(incf counter)
|
||||||
|
(push (make-label time labelnum) labels))
|
||||||
|
;print what we've done to debug window
|
||||||
|
(if (and (= adjust 0)(/= (* counter interval)(get-duration 1)))
|
||||||
|
(if (= counter 1)
|
||||||
|
(format t "1 label requested.")
|
||||||
|
(format t "~a labels at regular intervals of ~a seconds.~%~
|
||||||
|
Final label at ~a seconds from end of selection."
|
||||||
|
counter
|
||||||
|
interval
|
||||||
|
(- (get-duration 1) (* (1- counter) interval))))
|
||||||
|
(format t "~a labels at regular intervals of ~a seconds."
|
||||||
|
counter interval))))
|
||||||
|
;return labels
|
||||||
|
labels))
|
||||||
|
|
||||||
|
(defun validate ()
|
||||||
|
(when (= mode 1) ;Label interval
|
||||||
|
(when (> (get-duration 1) (round-to-sample (* 1000 interval)))
|
||||||
|
(throw 'err
|
||||||
|
(format nil "Too many labels.~%~%~
|
||||||
|
Selection length is ~a seconds and~%~
|
||||||
|
Label interval is ~a seconds~%~
|
||||||
|
giving a total of ~a labels.~%~
|
||||||
|
Maximum number of labels from this effect is 1000.~%~
|
||||||
|
Please use a shorter selection, or a longer Label interval."
|
||||||
|
(trim-trailing-zeros (get-duration 1))
|
||||||
|
(trim-trailing-zeros interval)
|
||||||
|
(if (= adjust 1)
|
||||||
|
(round (/ (get-duration 1) interval))
|
||||||
|
(1+ (round (/ (get-duration 1) interval)))))))))
|
||||||
|
|
||||||
|
(defun round-to-sample (time)
|
||||||
|
"Round time in seconds to nearest sample period."
|
||||||
|
(let ((samples (round (* time *sound-srate*))))
|
||||||
|
(/ samples *sound-srate*)))
|
||||||
|
|
||||||
|
(defun make-label (time num)
|
||||||
|
"Make a single label"
|
||||||
|
(let* ((num-text (format nil "~a" num))
|
||||||
|
(non-zero-digits (length num-text)))
|
||||||
|
(if (= zeros 0)
|
||||||
|
(setf num-text "")
|
||||||
|
(dotimes (i (max 0 (- zeros non-zero-digits)))
|
||||||
|
(setf num-text (format nil "~a~a" "0" num-text))))
|
||||||
|
(if num-before-text
|
||||||
|
(setf text (format nil "~a~a" num-text labeltext))
|
||||||
|
(setf text (format nil "~a~a" labeltext num-text)))
|
||||||
|
(list time text)))
|
||||||
|
|
||||||
|
(defun get-interval ()
|
||||||
|
"Get adjusted interval to fit duration"
|
||||||
|
(let* ((min-num (truncate (/ (get-duration 1) interval)))
|
||||||
|
(max-num (1+ min-num)))
|
||||||
|
(if (and (> min-num 0)
|
||||||
|
(< (abs (- interval (/ (get-duration 1) min-num)))
|
||||||
|
(abs (- interval (/ (get-duration 1) max-num)))))
|
||||||
|
(/ (get-duration 1) min-num)
|
||||||
|
(/ (get-duration 1) max-num))))
|
||||||
|
|
||||||
|
(defun lasttrackp ()
|
||||||
|
"true when processing the final selected track"
|
||||||
|
(let ((index (get '*track* 'index))
|
||||||
|
(num (length (get '*selection* 'tracks))))
|
||||||
|
(= index num)))
|
||||||
|
|
||||||
|
(defun trim-trailing-zeros (num)
|
||||||
|
;; sometimes need more precission than "%g".
|
||||||
|
(cond
|
||||||
|
((/= num (truncate num)) ; not integer
|
||||||
|
(setf *float-format* "%.5f")
|
||||||
|
(let ((numtxt (format nil "~a" num)))
|
||||||
|
(do* ((i (1- (length numtxt)) (1- i))
|
||||||
|
(ch (char numtxt i)(char numtxt i)))
|
||||||
|
((char/= ch #\0))
|
||||||
|
(setf numtxt (subseq numtxt 0 i)))
|
||||||
|
(setf *float-format* "%g")
|
||||||
|
numtxt))
|
||||||
|
(t num)))
|
||||||
|
|
||||||
|
|
||||||
; function to add labels to the label track
|
(setf num-before-text (<= zeros 3))
|
||||||
; from silencemarker.ny by Alex S. Brown
|
(setf zeros (1+ (rem (1- zeros) 3)))
|
||||||
(defun add-label (l-time l-text)
|
|
||||||
(setq label-list (cons (list l-time l-text) label-list)))
|
|
||||||
|
|
||||||
|
;; Analyze plug-ins may return text message per track
|
||||||
; function to add label number
|
;; but we only want error messages once, and we only want
|
||||||
(defun add-number (i text)
|
;; one set of labels.
|
||||||
(let* ((num (+ countfrom i))
|
(if (lasttrackp)
|
||||||
(appendt (if (> labelnum 3) t nil))
|
(catch 'err (make-labels))
|
||||||
(labelnum (if (> labelnum 3)(- labelnum 3) labelnum))
|
nil)
|
||||||
(sign (if (< num 0) "-" ""))
|
|
||||||
(num (abs num))
|
|
||||||
(zeros (case (- labelnum (length (format nil "~a" num)))
|
|
||||||
(1 "0")
|
|
||||||
(2 "00")
|
|
||||||
(t ""))))
|
|
||||||
(if appendt
|
|
||||||
(format nil "~a~a~a~a" text sign zeros num) ;append number to text
|
|
||||||
(format nil "~a~a~a~a" sign zeros num text)))) ;prepend number
|
|
||||||
|
|
||||||
|
|
||||||
; function to calculate 'time' based on number of labels
|
|
||||||
(defun num-of-label-time ()
|
|
||||||
(setf time (/ dur label-number)); set label interval
|
|
||||||
(if (= include 0)
|
|
||||||
(setf labels label-number)
|
|
||||||
(setf labels (1+ label-number))))
|
|
||||||
|
|
||||||
|
|
||||||
; function to calculate 'time' based on user-selected time interval
|
|
||||||
; calculate number of labels in selection
|
|
||||||
; this includes label at start of selection
|
|
||||||
; which is label number 0 if numbers are prepended to label text
|
|
||||||
; if final label is unwanted, decrement number of labels by 1
|
|
||||||
(defun user-selected-time ()
|
|
||||||
(setf labels (if (= include 0)
|
|
||||||
(truncate (/ dur time))
|
|
||||||
(+ 1 (truncate (/ dur time)))))
|
|
||||||
; setf check-labels: number of labels if user includes last label
|
|
||||||
(setf check-labels (+ 1 (truncate (/ dur time))))
|
|
||||||
; depending whether user wants specified time used
|
|
||||||
; or wants equal duration audio chunks including last chunk
|
|
||||||
; user time may create equal audio chunks even for last chunk
|
|
||||||
(setf time
|
|
||||||
(if (= t-choice 0) ; final segment need not be equal
|
|
||||||
time ; use user time value...
|
|
||||||
; ...otherwise calculate time for equal duration audio chunks
|
|
||||||
; if user time value creates equal final audio segment duration anyway
|
|
||||||
; then use user interval
|
|
||||||
(if (= dur (* time (- check-labels 1)))
|
|
||||||
time
|
|
||||||
; user time value does not create equal duration audio chunks
|
|
||||||
(new-time time dur check-labels)))))
|
|
||||||
|
|
||||||
|
|
||||||
;; ERROR CHECKING AND INITIALISE VARIABLES
|
|
||||||
|
|
||||||
(setq err ""); initialise error message
|
|
||||||
(setq label-list nil); initialize blank label track
|
|
||||||
(setq time (first (string-to-list time))); get Interval Label time from string
|
|
||||||
(setq start (first (string-to-list start))); get 'start' from string
|
|
||||||
|
|
||||||
; check 'start' is a valid number, then calculate and check 'dur(ation)'
|
|
||||||
(if (not(numberp start)) ; check that it is a number
|
|
||||||
(setq err (strcat err (format nil "'Time to place first label [seconds]' must be a number.~%~%")))
|
|
||||||
(progn
|
|
||||||
(setq start (max 0 start)) ;disallow negative label time
|
|
||||||
(setf dur (- (get-duration 1) start)) ;calculate duration
|
|
||||||
(if (<= dur 0)
|
|
||||||
(setq err (strcat err (format nil
|
|
||||||
"'Time to place first label' (~a seconds) is greater than selection length (~a seconds).~%~%"
|
|
||||||
start (get-duration 1)))))))
|
|
||||||
|
|
||||||
; If using 'Label Interval'
|
|
||||||
(if (= placement 0)
|
|
||||||
(if (not(numberp time)); check time is a number
|
|
||||||
(setq err (strcat err (format nil "'Label interval [seconds]' must be a number.~%~%")))
|
|
||||||
(if (<= time 0); Label interval must be positive
|
|
||||||
(setq err (strcat err (format nil "'Label Interval [seconds]' must be a positive number.~%~%")))
|
|
||||||
(if (and (> dur 0)(> time dur))
|
|
||||||
(setq err (strcat err (format nil
|
|
||||||
"Including your time offset of ~a seconds, your requested~%label interval of ~a seconds is greater than the duration~%of your selected audio (~a seconds). ~%~%"
|
|
||||||
start time dur))))))
|
|
||||||
;else using 'Number of labels'
|
|
||||||
(if (<= label-number 0)
|
|
||||||
(setq err (strcat err (format nil
|
|
||||||
"Based on 'Number of labels' you have selected ~a labels.~%~%" label-number)))))
|
|
||||||
|
|
||||||
;; MAIN PROGRAM
|
|
||||||
|
|
||||||
(if (> (length err) 0) ; if errors
|
|
||||||
(format nil "ERROR.~%~%~a" err) ; print error message(s)
|
|
||||||
(progn ; otherwise run program
|
|
||||||
(if (= placement 1) ; number of labels
|
|
||||||
(num-of-label-time)
|
|
||||||
(user-selected-time))
|
|
||||||
; add the labels
|
|
||||||
(dotimes (i labels)
|
|
||||||
(if (> labelnum 0)
|
|
||||||
(add-label (+ start (* i time)) (add-number i text))
|
|
||||||
(add-label (+ start (* i time)) text)))
|
|
||||||
; return label track
|
|
||||||
label-list))
|
|
||||||
|
@ -22,8 +22,8 @@
|
|||||||
|
|
||||||
;control action "Remove vocals or view Help" choice "Remove vocals,View Help" 0
|
;control action "Remove vocals or view Help" choice "Remove vocals,View Help" 0
|
||||||
;control band-choice "Removal choice" choice "Simple (entire spectrum),Remove frequency band,Retain frequency band" 0
|
;control band-choice "Removal choice" choice "Simple (entire spectrum),Remove frequency band,Retain frequency band" 0
|
||||||
;control low-range "Lower frequency band limit (Hz)" string " " "500"
|
;control low-range "Frequency band from (Hz)" float-text "" 500 0 nil
|
||||||
;control high-range "Upper frequency band limit (Hz)" string " " "2000"
|
;control high-range "Frequency band to (Hz)" float-text "" 2000 0 nil
|
||||||
|
|
||||||
|
|
||||||
; Initialize globals
|
; Initialize globals
|
||||||
@ -57,9 +57,6 @@ audio in a particular frequency range (such as low drums
|
|||||||
or bass), try 'Retain frequency band'. This only removes
|
or bass), try 'Retain frequency band'. This only removes
|
||||||
frequencies outside the limits, retaining the others."))
|
frequencies outside the limits, retaining the others."))
|
||||||
|
|
||||||
(defun string-to-list (str)
|
|
||||||
(read (make-string-input-stream (format nil "(~a)" str))))
|
|
||||||
|
|
||||||
|
|
||||||
;;; ERROR CHECKING:
|
;;; ERROR CHECKING:
|
||||||
|
|
||||||
@ -72,39 +69,21 @@ frequencies outside the limits, retaining the others."))
|
|||||||
channels, use 'Make Stereo Track' on the Track~%~
|
channels, use 'Make Stereo Track' on the Track~%~
|
||||||
Drop-Down Menu, then run Vocal Remover again.~%"))))
|
Drop-Down Menu, then run Vocal Remover again.~%"))))
|
||||||
|
|
||||||
|
(defmacro validate (Hz)
|
||||||
|
;; Filters become unstable when very close to 0 Hz or
|
||||||
|
;; Nyquist frequency, so disable.
|
||||||
|
`(if (or (< ,Hz 1)(> ,hz (- (/ *sound-srate* 2) 1)))
|
||||||
|
(setf ,Hz nil)))
|
||||||
|
|
||||||
;;; Check that frequency range is valid
|
;;; Check that frequency range is valid
|
||||||
(defun check-range ()
|
(defun check-range ()
|
||||||
(setq low-range (first (string-to-list low-range)))
|
;; Ensure min < max
|
||||||
(setq high-range (first (string-to-list high-range)))
|
(when (< high-range low-range)
|
||||||
(if (or (not (numberp low-range))
|
(let ((temp low-range))
|
||||||
(not (numberp high-range))
|
(setf low-range high-range)
|
||||||
(< low-range 0)
|
(setf high-range temp)))
|
||||||
(> low-range 20000)
|
(validate low-range)
|
||||||
(< high-range 0)
|
(validate high-range))
|
||||||
(> high-range 20000))
|
|
||||||
(progn
|
|
||||||
(if (= band-choice 1)
|
|
||||||
(setf band-type "remove")
|
|
||||||
(setf band-type "retain"))
|
|
||||||
(setf message (format nil
|
|
||||||
"~a~%~
|
|
||||||
Enter both a lower and an upper limit for the~%~
|
|
||||||
frequency band you want to ~a.~%~
|
|
||||||
You entered: \"~a\" \"~a\"~%~%~
|
|
||||||
Both values must be between 0 and 20000."
|
|
||||||
message
|
|
||||||
band-type
|
|
||||||
low-range
|
|
||||||
high-range)))
|
|
||||||
;; Else ensure that high-range > low-range.
|
|
||||||
(let ((temp low-range))
|
|
||||||
(when (> low-range high-range)
|
|
||||||
(setq low-range high-range)
|
|
||||||
(setq high-range temp))))
|
|
||||||
;; Range values must cannot be higher than Nyquist frequency.
|
|
||||||
(setq low-range (min low-range (/ *sound-srate* 2)))
|
|
||||||
(setq high-range (min high-range (/ *sound-srate* 2))))
|
|
||||||
|
|
||||||
|
|
||||||
(defun show-message ()
|
(defun show-message ()
|
||||||
;; output to both message box and to debug window
|
;; output to both message box and to debug window
|
||||||
@ -118,13 +97,27 @@ frequencies outside the limits, retaining the others."))
|
|||||||
;;; DSP FUNCTIONS:
|
;;; DSP FUNCTIONS:
|
||||||
|
|
||||||
(defun bandpass (sig low high)
|
(defun bandpass (sig low high)
|
||||||
(lowpass8
|
(cond
|
||||||
(highpass8 sig low)
|
((and low high) ;bandpass
|
||||||
high))
|
(lowpass8
|
||||||
|
(highpass8 sig low)
|
||||||
|
high))
|
||||||
|
(low (highpass8 sig low))
|
||||||
|
(high (lowpass8 sig high))
|
||||||
|
(t sig)))
|
||||||
|
|
||||||
(defun bandstop (sig low high)
|
(defun bandstop (sig low high)
|
||||||
(sum (lowpass8 sig low)
|
(if (< (/ (- high low) low) 0.1)
|
||||||
(highpass8 sig high)))
|
(format t "Warning:~%~
|
||||||
|
Selected band-stop filter is~%~
|
||||||
|
~a Hz to ~a Hz.~%~
|
||||||
|
A very narrow stop-band filter may have~%~
|
||||||
|
unexpected results.~%~%"
|
||||||
|
low high))
|
||||||
|
(let ((low-sig (if low (lowpass8 sig low)(s-rest 1))))
|
||||||
|
(sum
|
||||||
|
low-sig
|
||||||
|
(if high (highpass8 (diff sig low-sig) high)(s-rest 1)))))
|
||||||
|
|
||||||
(defun CentrePanRemove ()
|
(defun CentrePanRemove ()
|
||||||
(cond
|
(cond
|
||||||
|
@ -40,6 +40,7 @@ effects from this one class.
|
|||||||
#include <wx/txtstrm.h>
|
#include <wx/txtstrm.h>
|
||||||
#include <wx/valgen.h>
|
#include <wx/valgen.h>
|
||||||
#include <wx/wfstream.h>
|
#include <wx/wfstream.h>
|
||||||
|
#include <wx/numformatter.h>
|
||||||
|
|
||||||
#include "../../AudacityApp.h"
|
#include "../../AudacityApp.h"
|
||||||
#include "../../FileNames.h"
|
#include "../../FileNames.h"
|
||||||
@ -482,6 +483,8 @@ bool NyquistEffect::Process()
|
|||||||
|
|
||||||
mProps += wxString::Format(wxT("(putprop '*AUDACITY* (list %d %d %d) 'VERSION)\n"), AUDACITY_VERSION, AUDACITY_RELEASE, AUDACITY_REVISION);
|
mProps += wxString::Format(wxT("(putprop '*AUDACITY* (list %d %d %d) 'VERSION)\n"), AUDACITY_VERSION, AUDACITY_RELEASE, AUDACITY_REVISION);
|
||||||
|
|
||||||
|
mProps += wxString::Format(wxT("(setf *DECIMAL-SEPARATOR* #\\%c)\n"), wxNumberFormatter::GetDecimalSeparator());
|
||||||
|
|
||||||
mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'BASE)\n"), EscapeString(FileNames::BaseDir()).c_str());
|
mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'BASE)\n"), EscapeString(FileNames::BaseDir()).c_str());
|
||||||
mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'DATA)\n"), EscapeString(FileNames::DataDir()).c_str());
|
mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'DATA)\n"), EscapeString(FileNames::DataDir()).c_str());
|
||||||
mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'HELP)\n"), EscapeString(FileNames::HtmlHelpDir().RemoveLast()).c_str());
|
mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'HELP)\n"), EscapeString(FileNames::HtmlHelpDir().RemoveLast()).c_str());
|
||||||
@ -868,10 +871,28 @@ bool NyquistEffect::ProcessOne()
|
|||||||
// Note: "View" property may change when Audacity's choice of track views has stabilized.
|
// Note: "View" property may change when Audacity's choice of track views has stabilized.
|
||||||
cmd += wxString::Format(wxT("(putprop '*TRACK* %s 'VIEW)\n"), view.c_str());
|
cmd += wxString::Format(wxT("(putprop '*TRACK* %s 'VIEW)\n"), view.c_str());
|
||||||
cmd += wxString::Format(wxT("(putprop '*TRACK* %d 'CHANNELS)\n"), mCurNumChannels);
|
cmd += wxString::Format(wxT("(putprop '*TRACK* %d 'CHANNELS)\n"), mCurNumChannels);
|
||||||
|
|
||||||
|
double startTime = 0.0;
|
||||||
|
double endTime = 0.0;
|
||||||
|
|
||||||
|
if (mCurTrack[0]->GetLinked()) {
|
||||||
|
startTime = std::min<double>(mCurTrack[0]->GetStartTime(), mCurTrack[0]->GetLink()->GetStartTime());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
startTime = mCurTrack[0]->GetStartTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCurTrack[0]->GetLinked()) {
|
||||||
|
endTime = std::max<double>(mCurTrack[0]->GetEndTime(), mCurTrack[0]->GetLink()->GetEndTime());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
endTime = mCurTrack[0]->GetEndTime();
|
||||||
|
}
|
||||||
|
|
||||||
cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'START-TIME)\n"),
|
cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'START-TIME)\n"),
|
||||||
Internat::ToString(mCurTrack[0]->GetStartTime()).c_str());
|
Internat::ToString(startTime).c_str());
|
||||||
cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'END-TIME)\n"),
|
cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'END-TIME)\n"),
|
||||||
Internat::ToString(mCurTrack[0]->GetEndTime()).c_str());
|
Internat::ToString(endTime).c_str());
|
||||||
cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'GAIN)\n"),
|
cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'GAIN)\n"),
|
||||||
Internat::ToString(mCurTrack[0]->GetGain()).c_str());
|
Internat::ToString(mCurTrack[0]->GetGain()).c_str());
|
||||||
cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'PAN)\n"),
|
cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'PAN)\n"),
|
||||||
|
@ -23,65 +23,68 @@
|
|||||||
class NumberFormatter
|
class NumberFormatter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Bit masks for ToString()
|
// Bit masks for ToString()
|
||||||
enum Style
|
enum Style
|
||||||
{
|
{
|
||||||
Style_None = 0x00,
|
Style_None = 0x00,
|
||||||
Style_WithThousandsSep = 0x01,
|
Style_WithThousandsSep = 0x01,
|
||||||
Style_NoTrailingZeroes = 0x02, // Only for floating point numbers
|
Style_NoTrailingZeroes = 0x02, // Only for floating point numbers
|
||||||
Style_OneTrailingZero = 0x04, // Only for floating point numbers
|
Style_OneTrailingZero = 0x04, // Only for floating point numbers
|
||||||
Style_TwoTrailingZeroes = 0x08, // Only for floating point numbers
|
Style_TwoTrailingZeroes = 0x08, // Only for floating point numbers
|
||||||
Style_ThreeTrailingZeroes = 0x10 // Only for floating point numbers
|
Style_ThreeTrailingZeroes = 0x10 // Only for floating point numbers
|
||||||
};
|
};
|
||||||
|
|
||||||
// Format a number as a string. By default, the thousands separator is
|
// Format a number as a string. By default, the thousands separator is
|
||||||
// used, specify Style_None to prevent this. For floating point numbers,
|
// used, specify Style_None to prevent this. For floating point numbers,
|
||||||
// precision can also be specified.
|
// precision can also be specified.
|
||||||
static wxString ToString(long val,
|
static wxString ToString(long val,
|
||||||
int style = Style_WithThousandsSep);
|
int style = Style_WithThousandsSep);
|
||||||
#ifdef HAS_LONG_LONG_T_DIFFERENT_FROM_LONG
|
#ifdef HAS_LONG_LONG_T_DIFFERENT_FROM_LONG
|
||||||
static wxString ToString(wxLongLong_t val,
|
static wxString ToString(wxLongLong_t val,
|
||||||
int style = Style_WithThousandsSep);
|
int style = Style_WithThousandsSep);
|
||||||
#endif // HAS_LONG_LONG_T_DIFFERENT_FROM_LONG
|
#endif // HAS_LONG_LONG_T_DIFFERENT_FROM_LONG
|
||||||
static wxString ToString(double val,
|
static wxString ToString(double val,
|
||||||
int precision,
|
int precision,
|
||||||
int style = Style_WithThousandsSep);
|
int style = Style_WithThousandsSep);
|
||||||
|
|
||||||
// Parse a string representing a number, possibly with thousands separator.
|
// Parse a string representing a number, possibly with thousands separator.
|
||||||
//
|
//
|
||||||
// Return true on success and stores the result in the provided location
|
// Return true on success and stores the result in the provided location
|
||||||
// which must be a valid non-NULL pointer.
|
// which must be a valid non-NULL pointer.
|
||||||
static bool FromString(wxString s, long *val);
|
static bool FromString(wxString s, long *val);
|
||||||
#ifdef HAS_LONG_LONG_T_DIFFERENT_FROM_LONG
|
#ifdef HAS_LONG_LONG_T_DIFFERENT_FROM_LONG
|
||||||
static bool FromString(wxString s, wxLongLong_t *val);
|
static bool FromString(wxString s, wxLongLong_t *val);
|
||||||
#endif // HAS_LONG_LONG_T_DIFFERENT_FROM_LONG
|
#endif // HAS_LONG_LONG_T_DIFFERENT_FROM_LONG
|
||||||
static bool FromString(wxString s, double *val);
|
static bool FromString(wxString s, double *val);
|
||||||
|
|
||||||
|
|
||||||
// Get the decimal separator for the current locale. It is always defined
|
// Get the decimal separator for the current locale. It is always defined
|
||||||
// and we fall back to returning '.' in case of an error.
|
// and we fall back to returning '.' in case of an error.
|
||||||
static wxChar GetDecimalSeparator();
|
static wxChar GetDecimalSeparator();
|
||||||
|
|
||||||
// Get the thousands separator if grouping of the digits is used by the
|
// Get the thousands separator if grouping of the digits is used by the
|
||||||
// current locale. The value returned in sep should be only used if the
|
// current locale. The value returned in sep should be only used if the
|
||||||
// function returns true.
|
// function returns true.
|
||||||
static bool GetThousandsSeparatorIfUsed(wxChar *sep);
|
static bool GetThousandsSeparatorIfUsed(wxChar *sep);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Post-process the string representing an integer.
|
// Post-process the string representing an integer.
|
||||||
static wxString PostProcessIntString(wxString s, int style);
|
static wxString PostProcessIntString(wxString s, int style);
|
||||||
|
|
||||||
// Add the thousands separators to a string representing a number without
|
// Add the thousands separators to a string representing a number without
|
||||||
// the separators. This is used by ToString(Style_WithThousandsSep).
|
// the separators. This is used by ToString(Style_WithThousandsSep).
|
||||||
static void AddThousandsSeparators(wxString& s);
|
static void AddThousandsSeparators(wxString& s);
|
||||||
|
|
||||||
// Remove trailing zeroes and, if there is nothing left after it, the
|
// Remove all thousands separators from a string representing a number.
|
||||||
// decimal separator itself from a string representing a floating point
|
static void RemoveThousandsSeparators(wxString& s);
|
||||||
// number. Also used by ToString().
|
|
||||||
static void RemoveTrailingZeroes(wxString& s, size_t retain = 0);
|
|
||||||
|
|
||||||
// Remove all thousands separators from a string representing a number.
|
protected:
|
||||||
static void RemoveThousandsSeparators(wxString& s);
|
// Remove trailing zeroes and, if there is nothing left after it, the
|
||||||
|
// decimal separator itself from a string representing a floating point
|
||||||
|
// number. Also used by ToString().
|
||||||
|
static void RemoveTrailingZeroes(wxString& s, size_t retain = 0);
|
||||||
|
|
||||||
|
friend class FloatingPointValidatorBase;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _WIDGETS_NUMFORMATTER_H_
|
#endif // _WIDGETS_NUMFORMATTER_H_
|
||||||
|
@ -44,242 +44,242 @@
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
BEGIN_EVENT_TABLE(NumValidatorBase, wxValidator)
|
BEGIN_EVENT_TABLE(NumValidatorBase, wxValidator)
|
||||||
EVT_CHAR(NumValidatorBase::OnChar)
|
EVT_CHAR(NumValidatorBase::OnChar)
|
||||||
EVT_TEXT_PASTE(wxID_ANY, NumValidatorBase::OnPaste)
|
EVT_TEXT_PASTE(wxID_ANY, NumValidatorBase::OnPaste)
|
||||||
EVT_KILL_FOCUS(NumValidatorBase::OnKillFocus)
|
EVT_KILL_FOCUS(NumValidatorBase::OnKillFocus)
|
||||||
END_EVENT_TABLE()
|
END_EVENT_TABLE()
|
||||||
|
|
||||||
int NumValidatorBase::GetFormatFlags() const
|
int NumValidatorBase::GetFormatFlags() const
|
||||||
{
|
{
|
||||||
int flags = NumberFormatter::Style_None;
|
int flags = NumberFormatter::Style_None;
|
||||||
if ( m_style & NUM_VAL_THOUSANDS_SEPARATOR )
|
if ( m_style & NUM_VAL_THOUSANDS_SEPARATOR )
|
||||||
flags |= NumberFormatter::Style_WithThousandsSep;
|
flags |= NumberFormatter::Style_WithThousandsSep;
|
||||||
if ( m_style & NUM_VAL_NO_TRAILING_ZEROES )
|
if ( m_style & NUM_VAL_NO_TRAILING_ZEROES )
|
||||||
flags |= NumberFormatter::Style_NoTrailingZeroes;
|
flags |= NumberFormatter::Style_NoTrailingZeroes;
|
||||||
if ( m_style & NUM_VAL_ONE_TRAILING_ZERO )
|
if ( m_style & NUM_VAL_ONE_TRAILING_ZERO )
|
||||||
flags |= NumberFormatter::Style_OneTrailingZero;
|
flags |= NumberFormatter::Style_OneTrailingZero;
|
||||||
if ( m_style & NUM_VAL_TWO_TRAILING_ZEROES )
|
if ( m_style & NUM_VAL_TWO_TRAILING_ZEROES )
|
||||||
flags |= NumberFormatter::Style_TwoTrailingZeroes;
|
flags |= NumberFormatter::Style_TwoTrailingZeroes;
|
||||||
if ( m_style & NUM_VAL_THREE_TRAILING_ZEROES )
|
if ( m_style & NUM_VAL_THREE_TRAILING_ZEROES )
|
||||||
flags |= NumberFormatter::Style_ThreeTrailingZeroes;
|
flags |= NumberFormatter::Style_ThreeTrailingZeroes;
|
||||||
|
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxTextEntry *NumValidatorBase::GetTextEntry() const
|
wxTextEntry *NumValidatorBase::GetTextEntry() const
|
||||||
{
|
{
|
||||||
#if wxUSE_TEXTCTRL
|
#if wxUSE_TEXTCTRL
|
||||||
if ( wxTextCtrl *text = wxDynamicCast(m_validatorWindow, wxTextCtrl) )
|
if ( wxTextCtrl *text = wxDynamicCast(m_validatorWindow, wxTextCtrl) )
|
||||||
return text;
|
return text;
|
||||||
#endif // wxUSE_TEXTCTRL
|
#endif // wxUSE_TEXTCTRL
|
||||||
|
|
||||||
wxFAIL_MSG(wxT("Can only be used with wxTextCtrl or wxComboBox"));
|
wxFAIL_MSG(wxT("Can only be used with wxTextCtrl or wxComboBox"));
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NumValidatorBase::Validate(wxWindow *parent)
|
bool NumValidatorBase::Validate(wxWindow *parent)
|
||||||
{
|
{
|
||||||
// If window is disabled, simply return
|
// If window is disabled, simply return
|
||||||
if ( !m_validatorWindow->IsEnabled() )
|
if ( !m_validatorWindow->IsEnabled() )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
wxString errmsg;
|
wxString errmsg;
|
||||||
bool res = DoValidateNumber(&errmsg);
|
bool res = DoValidateNumber(&errmsg);
|
||||||
|
|
||||||
if ( !res )
|
if ( !res )
|
||||||
{
|
{
|
||||||
wxMessageBox(errmsg, _("Validation error"),
|
wxMessageBox(errmsg, _("Validation error"),
|
||||||
wxOK | wxICON_ERROR, parent);
|
wxOK | wxICON_ERROR, parent);
|
||||||
wxTextEntry *te = GetTextEntry();
|
wxTextEntry *te = GetTextEntry();
|
||||||
if ( te )
|
if ( te )
|
||||||
{
|
{
|
||||||
te->SelectAll();
|
te->SelectAll();
|
||||||
te->SetFocus();
|
te->SetFocus();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
NumValidatorBase::GetCurrentValueAndInsertionPoint(wxString& val,
|
NumValidatorBase::GetCurrentValueAndInsertionPoint(wxString& val,
|
||||||
int& pos) const
|
int& pos) const
|
||||||
{
|
{
|
||||||
wxTextEntry * const control = GetTextEntry();
|
wxTextEntry * const control = GetTextEntry();
|
||||||
if ( !control )
|
if ( !control )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
val = control->GetValue();
|
val = control->GetValue();
|
||||||
pos = control->GetInsertionPoint();
|
pos = control->GetInsertionPoint();
|
||||||
|
|
||||||
long selFrom, selTo;
|
long selFrom, selTo;
|
||||||
control->GetSelection(&selFrom, &selTo);
|
control->GetSelection(&selFrom, &selTo);
|
||||||
|
|
||||||
const long selLen = selTo - selFrom;
|
const long selLen = selTo - selFrom;
|
||||||
if ( selLen )
|
if ( selLen )
|
||||||
{
|
{
|
||||||
// Remove selected text because pressing a key would make it disappear.
|
// Remove selected text because pressing a key would make it disappear.
|
||||||
val.erase(selFrom, selLen);
|
val.erase(selFrom, selLen);
|
||||||
|
|
||||||
// And adjust the insertion point to have correct position in the new
|
// And adjust the insertion point to have correct position in the new
|
||||||
// string.
|
// string.
|
||||||
if ( pos > selFrom )
|
if ( pos > selFrom )
|
||||||
{
|
{
|
||||||
if ( pos >= selTo )
|
if ( pos >= selTo )
|
||||||
pos -= selLen;
|
pos -= selLen;
|
||||||
else
|
else
|
||||||
pos = selFrom;
|
pos = selFrom;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NumValidatorBase::IsMinusOk(const wxString& val, int pos) const
|
bool NumValidatorBase::IsMinusOk(const wxString& val, int pos) const
|
||||||
{
|
{
|
||||||
// Minus is only ever accepted in the beginning of the string.
|
// Minus is only ever accepted in the beginning of the string.
|
||||||
if ( pos != 0 )
|
if ( pos != 0 )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// And then only if there is no existing minus sign there.
|
// And then only if there is no existing minus sign there.
|
||||||
if ( !val.empty() && val[0] == '-' )
|
if ( !val.empty() && val[0] == '-' )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NumValidatorBase::OnChar(wxKeyEvent& event)
|
void NumValidatorBase::OnChar(wxKeyEvent& event)
|
||||||
{
|
{
|
||||||
// By default we just validate this key so don't prevent the normal
|
// By default we just validate this key so don't prevent the normal
|
||||||
// handling from taking place.
|
// handling from taking place.
|
||||||
event.Skip();
|
event.Skip();
|
||||||
|
|
||||||
if ( !m_validatorWindow )
|
if ( !m_validatorWindow )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#if wxUSE_UNICODE
|
#if wxUSE_UNICODE
|
||||||
const int ch = event.GetUnicodeKey();
|
const int ch = event.GetUnicodeKey();
|
||||||
const int c = event.GetKeyCode();
|
const int c = event.GetKeyCode();
|
||||||
if ( c > WXK_START )
|
if ( c > WXK_START )
|
||||||
{
|
{
|
||||||
// It's a character without any Unicode equivalent at all, e.g. cursor
|
// It's a character without any Unicode equivalent at all, e.g. cursor
|
||||||
// arrow or function key, we never filter those.
|
// arrow or function key, we never filter those.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#else // !wxUSE_UNICODE
|
#else // !wxUSE_UNICODE
|
||||||
const int ch = event.GetKeyCode();
|
const int ch = event.GetKeyCode();
|
||||||
const int c = ch;
|
const int c = ch;
|
||||||
if ( ch > WXK_DELETE )
|
if ( ch > WXK_DELETE )
|
||||||
{
|
{
|
||||||
// Not a character neither.
|
// Not a character neither.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif // wxUSE_UNICODE/!wxUSE_UNICODE
|
#endif // wxUSE_UNICODE/!wxUSE_UNICODE
|
||||||
|
|
||||||
// Space is an allowed thousands separator. But we don't allow user to type
|
// Space is an allowed thousands separator. But we don't allow user to type
|
||||||
// it. We will add it at formatting time in OnKillFocus().
|
// it. We will add it at formatting time in OnKillFocus().
|
||||||
if ( c < WXK_SPACE || c == WXK_DELETE )
|
if ( c < WXK_SPACE || c == WXK_DELETE )
|
||||||
{
|
{
|
||||||
// Allow ASCII control characters and Delete.
|
// Allow ASCII control characters and Delete.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this character is allowed in the current state.
|
// Check if this character is allowed in the current state.
|
||||||
wxString val;
|
wxString val;
|
||||||
int pos;
|
int pos;
|
||||||
GetCurrentValueAndInsertionPoint(val, pos);
|
GetCurrentValueAndInsertionPoint(val, pos);
|
||||||
|
|
||||||
if ( !IsCharOk(val, pos, ch) )
|
if ( !IsCharOk(val, pos, ch) )
|
||||||
{
|
{
|
||||||
if ( !wxValidator::IsSilent() )
|
if ( !wxValidator::IsSilent() )
|
||||||
wxBell();
|
wxBell();
|
||||||
|
|
||||||
// Do not skip the event in this case, stop handling it here.
|
// Do not skip the event in this case, stop handling it here.
|
||||||
event.Skip(false);
|
event.Skip(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NumValidatorBase::OnPaste(wxClipboardTextEvent& event)
|
void NumValidatorBase::OnPaste(wxClipboardTextEvent& event)
|
||||||
{
|
{
|
||||||
event.Skip(false);
|
event.Skip(false);
|
||||||
|
|
||||||
wxTextEntry * const control = GetTextEntry();
|
wxTextEntry * const control = GetTextEntry();
|
||||||
if ( !control )
|
if ( !control )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxClipboardLocker cb;
|
wxClipboardLocker cb;
|
||||||
// if (!wxClipboard::Get()->IsSupported(wxDataFormat(wxDF_TEXT)))
|
// if (!wxClipboard::Get()->IsSupported(wxDataFormat(wxDF_TEXT)))
|
||||||
if (!wxClipboard::Get()->IsSupported(wxDF_TEXT))
|
if (!wxClipboard::Get()->IsSupported(wxDF_TEXT))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxTextDataObject data;
|
wxTextDataObject data;
|
||||||
if (!wxClipboard::Get()->GetData( data ))
|
if (!wxClipboard::Get()->GetData( data ))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString toPaste = data.GetText();
|
wxString toPaste = data.GetText();
|
||||||
wxString val;
|
wxString val;
|
||||||
int pos;
|
int pos;
|
||||||
GetCurrentValueAndInsertionPoint(val, pos);
|
GetCurrentValueAndInsertionPoint(val, pos);
|
||||||
|
|
||||||
for (size_t i = 0, cnt = toPaste.Length(); i < cnt; i++)
|
for (size_t i = 0, cnt = toPaste.Length(); i < cnt; i++)
|
||||||
{
|
{
|
||||||
const wxChar ch = toPaste[i];
|
const wxChar ch = toPaste[i];
|
||||||
|
|
||||||
// Check if this character is allowed in the current state.
|
// Check if this character is allowed in the current state.
|
||||||
if ( IsCharOk(val, pos, ch) )
|
if ( IsCharOk(val, pos, ch) )
|
||||||
{
|
{
|
||||||
val = GetValueAfterInsertingChar(val, pos++, ch);
|
val = GetValueAfterInsertingChar(val, pos++, ch);
|
||||||
}
|
}
|
||||||
else if ( !wxValidator::IsSilent() )
|
else if ( !wxValidator::IsSilent() )
|
||||||
{
|
{
|
||||||
wxBell();
|
wxBell();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// When we change the control value below, its "modified" status is reset
|
// When we change the control value below, its "modified" status is reset
|
||||||
// so we need to explicitly keep it marked as modified if it was so in the
|
// so we need to explicitly keep it marked as modified if it was so in the
|
||||||
// first place.
|
// first place.
|
||||||
//
|
//
|
||||||
// Notice that only wxTextCtrl (and not wxTextEntry) has
|
// Notice that only wxTextCtrl (and not wxTextEntry) has
|
||||||
// IsModified()/MarkDirty() methods hence the need for dynamic cast.
|
// IsModified()/MarkDirty() methods hence the need for dynamic cast.
|
||||||
wxTextCtrl * const text = wxDynamicCast(m_validatorWindow, wxTextCtrl);
|
wxTextCtrl * const text = wxDynamicCast(m_validatorWindow, wxTextCtrl);
|
||||||
const bool wasModified = text ? text->IsModified() : false;
|
const bool wasModified = text ? text->IsModified() : false;
|
||||||
|
|
||||||
control->ChangeValue(NormalizeString(val));
|
control->ChangeValue(NormalizeString(val));
|
||||||
|
|
||||||
if ( wasModified )
|
if ( wasModified )
|
||||||
{
|
{
|
||||||
text->MarkDirty();
|
text->MarkDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NumValidatorBase::OnKillFocus(wxFocusEvent& event)
|
void NumValidatorBase::OnKillFocus(wxFocusEvent& event)
|
||||||
{
|
{
|
||||||
wxTextEntry * const control = GetTextEntry();
|
wxTextEntry * const control = GetTextEntry();
|
||||||
if ( !control )
|
if ( !control )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// When we change the control value below, its "modified" status is reset
|
// When we change the control value below, its "modified" status is reset
|
||||||
// so we need to explicitly keep it marked as modified if it was so in the
|
// so we need to explicitly keep it marked as modified if it was so in the
|
||||||
// first place.
|
// first place.
|
||||||
//
|
//
|
||||||
// Notice that only wxTextCtrl (and not wxTextEntry) has
|
// Notice that only wxTextCtrl (and not wxTextEntry) has
|
||||||
// IsModified()/MarkDirty() methods hence the need for dynamic cast.
|
// IsModified()/MarkDirty() methods hence the need for dynamic cast.
|
||||||
wxTextCtrl * const text = wxDynamicCast(m_validatorWindow, wxTextCtrl);
|
wxTextCtrl * const text = wxDynamicCast(m_validatorWindow, wxTextCtrl);
|
||||||
const bool wasModified = text ? text->IsModified() : false;
|
const bool wasModified = text ? text->IsModified() : false;
|
||||||
|
|
||||||
control->ChangeValue(NormalizeString(control->GetValue()));
|
control->ChangeValue(NormalizeString(control->GetValue()));
|
||||||
|
|
||||||
if ( wasModified )
|
if ( wasModified )
|
||||||
text->MarkDirty();
|
text->MarkDirty();
|
||||||
|
|
||||||
event.Skip();
|
event.Skip();
|
||||||
|
|
||||||
// Validate(text);
|
// Validate(text);
|
||||||
}
|
}
|
||||||
@ -290,90 +290,91 @@ void NumValidatorBase::OnKillFocus(wxFocusEvent& event)
|
|||||||
|
|
||||||
wxString IntegerValidatorBase::ToString(LongestValueType value) const
|
wxString IntegerValidatorBase::ToString(LongestValueType value) const
|
||||||
{
|
{
|
||||||
return NumberFormatter::ToString(value, GetFormatFlags());
|
return NumberFormatter::ToString(value, GetFormatFlags());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IntegerValidatorBase::FromString(const wxString& s, LongestValueType *value)
|
IntegerValidatorBase::FromString(const wxString& s, LongestValueType *value)
|
||||||
{
|
{
|
||||||
return NumberFormatter::FromString(s, value);
|
return NumberFormatter::FromString(s, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IntegerValidatorBase::IsCharOk(const wxString& val, int pos, wxChar ch) const
|
IntegerValidatorBase::IsCharOk(const wxString& val, int pos, wxChar ch) const
|
||||||
{
|
{
|
||||||
// We may accept minus sign if we can represent negative numbers at all.
|
// We may accept minus sign if we can represent negative numbers at all.
|
||||||
if ( ch == '-' )
|
if ( ch == '-' )
|
||||||
{
|
{
|
||||||
// Notice that entering '-' can make our value invalid, for example if
|
// Notice that entering '-' can make our value invalid, for example if
|
||||||
// we're limited to -5..15 range and the current value is 12, then the
|
// we're limited to -5..15 range and the current value is 12, then the
|
||||||
// new value would be (invalid) -12. We consider it better to let the
|
// new value would be (invalid) -12. We consider it better to let the
|
||||||
// user do this because perhaps he is going to press Delete key next to
|
// user do this because perhaps he is going to press Delete key next to
|
||||||
// make it -2 and forcing him to delete 1 first would be unnatural.
|
// make it -2 and forcing him to delete 1 first would be unnatural.
|
||||||
//
|
//
|
||||||
// TODO: It would be nice to indicate that the current control contents
|
// TODO: It would be nice to indicate that the current control contents
|
||||||
// is invalid (if it's indeed going to be the case) once
|
// is invalid (if it's indeed going to be the case) once
|
||||||
// wxValidator supports doing this non-intrusively.
|
// wxValidator supports doing this non-intrusively.
|
||||||
return m_min < 0 && IsMinusOk(val, pos);
|
return m_min < 0 && IsMinusOk(val, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A separator is accepted if the locale allow it, the other chars must be digits
|
// A separator is accepted if the locale allow it, the other chars must be digits
|
||||||
if ( ch < '0' || ch > '9' )
|
if ( ch < '0' || ch > '9' )
|
||||||
{
|
{
|
||||||
wxChar thousands;
|
wxChar thousands;
|
||||||
if ( NumberFormatter::GetThousandsSeparatorIfUsed(&thousands) )
|
if ( NumberFormatter::GetThousandsSeparatorIfUsed(&thousands) )
|
||||||
{
|
{
|
||||||
// if (ch != thousands)
|
// if (ch != thousands)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IntegerValidatorBase::DoValidateNumber(wxString * errMsg) const
|
bool IntegerValidatorBase::DoValidateNumber(wxString * errMsg) const
|
||||||
{
|
{
|
||||||
wxTextEntry * const control = GetTextEntry();
|
wxTextEntry * const control = GetTextEntry();
|
||||||
if ( !control )
|
if ( !control )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
wxString s(control->GetValue());
|
wxString s(control->GetValue());
|
||||||
wxChar thousandsSep;
|
wxChar thousandsSep;
|
||||||
if ( NumberFormatter::GetThousandsSeparatorIfUsed(&thousandsSep) )
|
if ( NumberFormatter::GetThousandsSeparatorIfUsed(&thousandsSep) )
|
||||||
s.Replace(wxString(thousandsSep), wxString());
|
s.Replace(wxString(thousandsSep), wxString());
|
||||||
|
|
||||||
if ( s.empty() )
|
if ( s.empty() )
|
||||||
{
|
{
|
||||||
// Is blank, but allowed. Stop here
|
// Is blank, but allowed. Stop here
|
||||||
if ( HasFlag(NUM_VAL_ZERO_AS_BLANK) )
|
if ( HasFlag(NUM_VAL_ZERO_AS_BLANK) )
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// We can't do any check with an empty string
|
// We can't do any check with an empty string
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
*errMsg = _("Empty value");
|
*errMsg = _("Empty value");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can it be converted to a value?
|
// Can it be converted to a value?
|
||||||
LongestValueType value;
|
LongestValueType value;
|
||||||
bool res = FromString(s, &value);
|
bool res = FromString(s, &value);
|
||||||
if ( !res )
|
if ( !res )
|
||||||
*errMsg = _("Malformed number");
|
*errMsg = _("Malformed number");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
res = IsInRange(value);
|
res = IsInRange(value);
|
||||||
if ( !res )
|
if ( !res )
|
||||||
*errMsg = _("Not in range");
|
errMsg->Printf(_("Not in range %d to %d"),
|
||||||
}
|
(int) m_min, (int) m_max);
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@ -382,14 +383,14 @@ bool IntegerValidatorBase::DoValidateNumber(wxString * errMsg) const
|
|||||||
|
|
||||||
wxString FloatingPointValidatorBase::ToString(LongestValueType value) const
|
wxString FloatingPointValidatorBase::ToString(LongestValueType value) const
|
||||||
{
|
{
|
||||||
return NumberFormatter::ToString(value, m_precision, GetFormatFlags());
|
return NumberFormatter::ToString(value, m_precision, GetFormatFlags());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
FloatingPointValidatorBase::FromString(const wxString& s,
|
FloatingPointValidatorBase::FromString(const wxString& s,
|
||||||
LongestValueType *value)
|
LongestValueType *value)
|
||||||
{
|
{
|
||||||
return NumberFormatter::FromString(s, value);
|
return NumberFormatter::FromString(s, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -397,141 +398,144 @@ FloatingPointValidatorBase::IsCharOk(const wxString& val,
|
|||||||
int pos,
|
int pos,
|
||||||
wxChar ch) const
|
wxChar ch) const
|
||||||
{
|
{
|
||||||
if ( ch == '-' )
|
if ( ch == '-' )
|
||||||
{
|
{
|
||||||
// We may accept minus sign if we can represent negative numbers at all.
|
// We may accept minus sign if we can represent negative numbers at all.
|
||||||
if ( pos == 0 )
|
if ( pos == 0 )
|
||||||
return m_min < 0 && IsMinusOk(val, pos);
|
return m_min < 0 && IsMinusOk(val, pos);
|
||||||
// or for the exponent definition
|
// or for the exponent definition
|
||||||
else if ( val[pos-1] != 'e' && val[pos-1] != 'E' )
|
else if ( val[pos-1] != 'e' && val[pos-1] != 'E' )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if ( ch == '+' )
|
else if ( ch == '+' )
|
||||||
{
|
{
|
||||||
if ( pos == 0 )
|
if ( pos == 0 )
|
||||||
return m_max >= 0;
|
return m_max >= 0;
|
||||||
else if ( val[pos-1] != 'e' && val[pos-1] != 'E' )
|
else if ( val[pos-1] != 'e' && val[pos-1] != 'E' )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const wxChar separator = NumberFormatter::GetDecimalSeparator();
|
const wxChar separator = NumberFormatter::GetDecimalSeparator();
|
||||||
if ( ch == separator )
|
if ( ch == separator )
|
||||||
{
|
{
|
||||||
if ( val.find(separator) != wxString::npos )
|
if ( val.find(separator) != wxString::npos )
|
||||||
{
|
{
|
||||||
// There is already a decimal separator, can't insert another one.
|
// There is already a decimal separator, can't insert another one.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepending a separator before the sign isn't allowed.
|
// Prepending a separator before the sign isn't allowed.
|
||||||
if ( pos == 0 && !val.empty() && ( val[0] == '-' || val[0] == '+' ) )
|
if ( pos == 0 && !val.empty() && ( val[0] == '-' || val[0] == '+' ) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Otherwise always accept it, adding a decimal separator doesn't
|
// Otherwise always accept it, adding a decimal separator doesn't
|
||||||
// change the number value and, in particular, can't make it invalid.
|
// change the number value and, in particular, can't make it invalid.
|
||||||
// OTOH the checks below might not pass because strings like "." or
|
// OTOH the checks below might not pass because strings like "." or
|
||||||
// "-." are not valid numbers so parsing them would fail, hence we need
|
// "-." are not valid numbers so parsing them would fail, hence we need
|
||||||
// to treat it specially here.
|
// to treat it specially here.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be a digit, an exponent or a thousands separator.
|
// Must be a digit, an exponent or a thousands separator.
|
||||||
if( ( ch < '0' || ch > '9' ) && ch != 'E' && ch != 'e' )
|
if( ( ch < '0' || ch > '9' ) && ch != 'E' && ch != 'e' )
|
||||||
{
|
{
|
||||||
wxChar thousands;
|
wxChar thousands;
|
||||||
if ( NumberFormatter::GetThousandsSeparatorIfUsed(&thousands) )
|
if ( NumberFormatter::GetThousandsSeparatorIfUsed(&thousands) )
|
||||||
{
|
{
|
||||||
// if (ch != thousands)
|
// if (ch != thousands)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the number of decimal digits in the final string
|
// Check the number of decimal digits in the final string
|
||||||
wxString str(val);
|
wxString str(val);
|
||||||
str.insert(pos, ch);
|
str.insert(pos, ch);
|
||||||
return ValidatePrecision(str);
|
return ValidatePrecision(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FloatingPointValidatorBase::DoValidateNumber(wxString * errMsg) const
|
bool FloatingPointValidatorBase::DoValidateNumber(wxString * errMsg) const
|
||||||
{
|
{
|
||||||
wxTextEntry * const control = GetTextEntry();
|
wxTextEntry * const control = GetTextEntry();
|
||||||
if ( !control )
|
if ( !control )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
wxString s(control->GetValue());
|
wxString s(control->GetValue());
|
||||||
wxChar thousandsSep;
|
wxChar thousandsSep;
|
||||||
if ( NumberFormatter::GetThousandsSeparatorIfUsed(&thousandsSep) )
|
if ( NumberFormatter::GetThousandsSeparatorIfUsed(&thousandsSep) )
|
||||||
s.Replace(wxString(thousandsSep), wxString());
|
s.Replace(wxString(thousandsSep), wxString());
|
||||||
|
|
||||||
if ( s.empty() )
|
if ( s.empty() )
|
||||||
{
|
{
|
||||||
if ( HasFlag(NUM_VAL_ZERO_AS_BLANK) )
|
if ( HasFlag(NUM_VAL_ZERO_AS_BLANK) )
|
||||||
return true; //Is blank, but allowed. Stop here
|
return true; //Is blank, but allowed. Stop here
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
*errMsg = _("Empty value");
|
*errMsg = _("Empty value");
|
||||||
return false; //We can't do any checks with an empty string
|
return false; //We can't do any checks with an empty string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LongestValueType value;
|
LongestValueType value;
|
||||||
bool res = FromString(s, &value); // Can it be converted to a value?
|
bool res = FromString(s, &value); // Can it be converted to a value?
|
||||||
if ( !res )
|
if ( !res )
|
||||||
*errMsg = _("Value overflow");
|
*errMsg = _("Value overflow");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
res = ValidatePrecision(s);
|
res = ValidatePrecision(s);
|
||||||
if ( !res )
|
if ( !res )
|
||||||
*errMsg = _("Too many decimal digits");
|
*errMsg = _("Too many decimal digits");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
res = IsInRange(value);
|
res = IsInRange(value);
|
||||||
if ( !res )
|
if ( !res )
|
||||||
|
{
|
||||||
|
wxString strMin = wxString::Format(wxT("%f"), m_min);
|
||||||
|
wxString strMax = wxString::Format(wxT("%f"), m_max);
|
||||||
|
NumberFormatter::RemoveTrailingZeroes(strMin);
|
||||||
|
NumberFormatter::RemoveTrailingZeroes(strMax);
|
||||||
|
|
||||||
|
if (m_minSet && m_maxSet)
|
||||||
{
|
{
|
||||||
if (m_minSet && m_maxSet)
|
errMsg->Printf(_("Value not in range: %s to %s"),
|
||||||
{
|
strMin, strMax);
|
||||||
errMsg->Printf(_("Value not in range: %.*f to %.*f"),
|
|
||||||
m_precision, m_min, m_precision, m_max);
|
|
||||||
}
|
|
||||||
else if (m_minSet)
|
|
||||||
{
|
|
||||||
errMsg->Printf(_("Value must not be less than %.*f"),
|
|
||||||
m_precision, m_min);
|
|
||||||
}
|
|
||||||
else if (m_maxSet)
|
|
||||||
{
|
|
||||||
errMsg->Printf(_("Value must not be greather than %.*f"),
|
|
||||||
m_precision, m_max);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
else if (m_minSet)
|
||||||
}
|
{
|
||||||
|
errMsg->Printf(_("Value must not be less than %s"), strMin);
|
||||||
|
}
|
||||||
|
else if (m_maxSet)
|
||||||
|
{
|
||||||
|
errMsg->Printf(_("Value must not be greather than %s"), strMax);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FloatingPointValidatorBase::ValidatePrecision(const wxString& s) const
|
bool FloatingPointValidatorBase::ValidatePrecision(const wxString& s) const
|
||||||
{
|
{
|
||||||
size_t posSep = s.find(NumberFormatter::GetDecimalSeparator());
|
size_t posSep = s.find(NumberFormatter::GetDecimalSeparator());
|
||||||
if ( posSep == wxString::npos )
|
if ( posSep == wxString::npos )
|
||||||
posSep = s.length();
|
posSep = s.length();
|
||||||
|
|
||||||
// If user typed exponent 'e' the number of decimal digits is not
|
// If user typed exponent 'e' the number of decimal digits is not
|
||||||
// important at all. But we must know that 'e' position.
|
// important at all. But we must know that 'e' position.
|
||||||
size_t posExp = s.Lower().Find(_("e"));
|
size_t posExp = s.Lower().Find(_("e"));
|
||||||
if ( posExp == wxString::npos )
|
if ( posExp == wxString::npos )
|
||||||
posExp = s.length();
|
posExp = s.length();
|
||||||
|
|
||||||
// Return true if number has no more decimal digits than allowed
|
// Return true if number has no more decimal digits than allowed
|
||||||
return ( (int)(posExp - posSep) - 1 <= (int)m_precision );
|
return ( (int)(posExp - posSep) - 1 <= (int)m_precision );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // wxUSE_VALIDATORS && wxUSE_TEXTCTRL
|
#endif // wxUSE_VALIDATORS && wxUSE_TEXTCTRL
|
||||||
|
Loading…
x
Reference in New Issue
Block a user