SpinnerValueFactory svf = new SpinnerValueFactory.DoubleSpinnerValueFactory(-500,500,1,0.5);
spiKolicina.setValueFactory(svf);
spiKolicina.setEditable(true);
UnaryOperator<TextFormatter.Change> filter = new UnaryOperator<TextFormatter.Change>() {
#Override
public TextFormatter.Change apply(TextFormatter.Change t) {
if (t.isReplaced())
if(t.getText().matches("[^0-9]"))
t.setText(t.getControlText().substring(t.getRangeStart(), t.getRangeEnd()));
if (t.isAdded()) {
if (t.getControlText().contains(".")) {
if (t.getText().matches("[^0-9]")) {
t.setText("");
}
}
else if (t.getText().matches("[^0-9.]")) {
t.setText("");
}
}
return t;
}
};
spiKolicina.getEditor().setTextFormatter(new TextFormatter<>(filter));
How to alter regex expression to allow me to enter negative double number in spinner textfield?
Also when the spinner textfield is empty and you press the up or down btn it gives nullPointException, i would like it to go to some default value
I usually find it easier to check the resulting text from a change; return the change "as-is" if it is ok, and return null otherwise. Here you want the resulting text to be an optional negative sign, zero or more digits, an options decimal separator, and zero or more digits. (Note this allows any valid floating point number, or an empty string, or a negative sign or digital separator on its own.)
So for the filter:
UnaryOperator<TextFormatter.Change> filter = new UnaryOperator<TextFormatter.Change>() {
#Override
public TextFormatter.Change apply(TextFormatter.Change t) {
String newText = t.getControlNewText() ;
if (newText.matches("-?[0-9]*\\.?[0-9]*")) {
return t ;
}
return null ;
}
};
You can also define a converter, which you can use with both the spinner itself and the text field which is the editor for the spinner. This just needs to parse the string as a double, but should handle the special cases of -, ., and -.. So:
StringConverter<Double> converter = new StringConverter<Double>() {
#Override
public String toString(Double object) {
return object.toString() ;
}
#Override
public Double fromString(String string) {
if (string.isEmpty() || ".".equals(string) || "-".equals(string) || "-.".equals(string)) {
return 0.0 ;
} else {
return new Double(string);
}
}
};
then do
svf.setConverter(converter);
spinner.getEditor().setTextFormatter(new TextFormatter<>(converter, 0.0, filter));
This converter will properly handle interpreting empty strings, etc, as 0.0, and so will avoid the exception when you try to increment or decrement when the editor is in that state.
SSCCE:
import java.util.function.UnaryOperator;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class SpinnerTest extends Application {
#Override
public void start(Stage primaryStage) {
SpinnerValueFactory<Double> svf = new SpinnerValueFactory.DoubleSpinnerValueFactory(-500,500,1,0.5);
Spinner<Double> spinner = new Spinner<>();
spinner.setValueFactory(svf);
spinner.setEditable(true);
UnaryOperator<TextFormatter.Change> filter = new UnaryOperator<TextFormatter.Change>() {
#Override
public TextFormatter.Change apply(TextFormatter.Change t) {
String newText = t.getControlNewText() ;
if (newText.matches("-?[0-9]*\\.?[0-9]*")) {
return t ;
}
return null ;
}
};
StringConverter<Double> converter = new StringConverter<Double>() {
#Override
public String toString(Double object) {
return object.toString() ;
}
#Override
public Double fromString(String string) {
if (string.isEmpty() || ".".equals(string) || "-".equals(string) || "-.".equals(string)) {
return 0.0 ;
} else {
return new Double(string);
}
}
};
svf.setConverter(converter);
spinner.getEditor().setTextFormatter(new TextFormatter<>(converter, 0.0, filter));
StackPane root = new StackPane(spinner);
primaryStage.setScene(new Scene(root, 180, 80));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Related
I have a JavaFX TextField specialized to accept numbers, including scientific notation. It does pretty much everything I want. But, because it accepts scientific notation, it is easy for a user to enter a number beyond the range that can be represented by a double. When they do, the TextField displays "Infinity" (or "-Infinity"). When that happens the field can no longer be edited to correct the problem. The contents cannot be selected and deleted either. Tapping the "Escape" key does not return to the previous contents.
Here is an SSCCE, based closely on the answer by James_D to this question a few years ago.
import java.text.DecimalFormatSymbols;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class NumericTextFieldDemo extends Application {
char sep = new DecimalFormatSymbols().getDecimalSeparator();
String negStarter = new StringBuilder("-").append(sep).toString();
String posStarter = new StringBuilder("+").append(sep).toString();
String patternStr = new StringBuilder()
.append("[+|-]?(([1-9][0-9]*)|0)?(\\")
.append(sep)
.append("[0-9]*)?(([e|E][+|-]?[0-9]*)?)")
.toString();
Pattern validEditingState = Pattern.compile(patternStr);
class NumericTextField extends TextField {
UnaryOperator<TextFormatter.Change> filter = c -> {
String text = c.getControlNewText();
if (validEditingState.matcher(text).matches()) {
return c;
} else {
return null;
}
};
StringConverter<Double> converter = new StringConverter<Double>() {
#Override
public Double fromString(String s) {
if (s.isEmpty() || "-".equals(s) || "+".equals(s)
|| negStarter.equals(s) || posStarter.equals(s)) {
return 0.0;
} else {
return Double.valueOf(s);
}
}
#Override
public String toString(Double d) {
return d.toString();
}
};
NumericTextField(double initValue) {
TextFormatter<Double> textFormatter = new TextFormatter<>(converter, initValue, filter);
textFormatter.valueProperty().addListener((ObservableValue<? extends Double> obs, Double oldValue, Double newValue) -> {
System.out.println("User entered value: " + newValue);
});
setTextFormatter(textFormatter);
}
NumericTextField() {
this(0.0);
}
}
#Override
public void start(Stage primaryStage) throws Exception {
NumericTextField ntf = new NumericTextField();
// Setting the font seems to be required on macOS.
ntf.setFont(new Font("Arial", 14));
VBox root = new VBox(5, ntf);
root.setAlignment(Pos.CENTER);
primaryStage.setScene(new Scene(root, 250, 150));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Is there any way to catch the infinities and leave the TextField in a usable state? Is there some change that could be made to the class to prevent entering such numbers in the first place?
Just use the built-in string converter for doubles:
TextFormatter<Double> tf = new TextFormatter<>(new DoubleStringConverter());
TextField ntf = new TextField();
ntf.setTextFormatter(tf);
Using as a starting point the DecimalField class found on this site, I wrote the following:
import javafx.scene.control.TextField;
import java.util.regex.Pattern;
public class DecimalField extends TextField {
public Boolean rate, positive, integer;
Pattern decimalPattern;
DecimalField(Boolean rate, Boolean positive) {
this.rate = rate;
this.positive = positive;
decimalPattern = Pattern.compile ("[-+]?[0-9]*(\\.[0-9]*)?");
// decimalPattern = Pattern.compile("[-+]?(\\b[0-9]+(\\.[0-9]*)?|\\.[0-9]+)([eE][-+]?[0-9]+\\b)?");
if (rate) {
decimalPattern = Pattern.compile ("[-+]?[0-9]*(\\.[0-9]*)?[%]?");
} else if (positive) {
decimalPattern = Pattern.compile ("[1-9][0-9]*(\\.[0-9]*)?");
}
}
#Override
public void replaceText(int start, int end, String text) {
if (validate (start, text)) {
super.replaceText (start, end, text);
}
}
#Override
public void replaceSelection(String text) {
if (validate (Integer.MAX_VALUE, text)) {
super.replaceSelection (text);
}
}
private boolean validate(int start, String text) {
String currentText = (getText ().isEmpty ()) ? "" : getText ();
if (start == 0) { //to handle "-1.1" or ".1" cases
return decimalPattern.matcher (text + currentText).matches ();
} else {
return decimalPattern.matcher (currentText + text).matches ();
}
}
}
Depending on the parameters sent to the constructor, this class can be used to restrict entries to a standard decimal number, to a positive only (i.e. > 0) decimal number, or to a number followed by the per-cent character.
It seems to work fine (a small test application is provided), but I wanted to also be able to specify a number in scientific notation such as 25.56e-5
I could not write the right regex pattern. A pattern such as "[0-9.eE+-]*" would limit the entry to acceptable characters but would not enforce the number syntax! Suggestions are welcome.
Here is the test program:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class DecimalFieldTest extends Application {
#Override
public void start(Stage primaryStage) {
// Boolean rate;
// rate = false; positive = true;
Label basicLbl = new Label("Basic Decimal");
DecimalField decimalField = new DecimalField (false, false);
Label rateLbl = new Label("Rate Decimal");
DecimalField rateDecimalField = new DecimalField (true, false);
Label positiveLbl = new Label("Positive Decimal");
DecimalField positiveDecimalField = new DecimalField (false, true);
Button clickMe = new Button ("Click Me");
clickMe.setOnAction (new EventHandler<ActionEvent> () {
#Override
public void handle(ActionEvent event) {
String s;
s = decimalField.getText ();
if (!s.isEmpty ()) getOut(s, false);
s = rateDecimalField.getText ();
if (!s.isEmpty ()) getOut(s, true);
s = positiveDecimalField.getText ();
if (!s.isEmpty ()) getOut(s, false);
}
});
decimalField.setOnAction (new EventHandler<ActionEvent> () {
#Override
public void handle(ActionEvent event) {
getOut(decimalField.getText (),false);
}
});
rateDecimalField.setOnAction (new EventHandler<ActionEvent> () {
#Override
public void handle(ActionEvent event) {
getOut(rateDecimalField.getText (),true);
}
});
positiveDecimalField.setOnAction (new EventHandler<ActionEvent> () {
#Override
public void handle(ActionEvent event) {
getOut(positiveDecimalField.getText (),false);
}
});
VBox root = new VBox (5, basicLbl, decimalField,
rateLbl, rateDecimalField, positiveLbl, positiveDecimalField, clickMe);
root.setAlignment (Pos.CENTER);
Scene scene = new Scene (root, 300, 250);
primaryStage.setScene (scene);
primaryStage.show ();
}
void getOut(String s, Boolean rate) {
// for rate : textField.getText().replaceAll("%","")
String ss = s.replaceAll ("%", "");
double value = Double.parseDouble (ss);
if (rate) {
System.out.println (String.format (ss + " <-> " + value) + "%");
} else {
System.out.println (String.format (ss + " <-> " + value));
}
}
public static void main(String[] args) {
launch (args);
}
}
You can use NumberTextField with BigDecimal and different number formats.
package control;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.geometry.Pos;
import javafx.scene.control.TextField;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.text.ParseException;
/**
* Number text field
*
* <p>
* Original source code from:
* https://dzone.com/articles/javafx-numbertextfield-and
*/
public class NumberTextField extends TextField {
private final NumberFormat numberFormat;
private ObjectProperty<BigDecimal> value = new SimpleObjectProperty<>();
public NumberTextField(NumberFormat numberFormat) {
this(BigDecimal.ZERO, numberFormat, 100);
}
public NumberTextField(NumberFormat numberFormat, double width) {
this(BigDecimal.ZERO, numberFormat, width);
}
/**
* Number field with properties.
*
* #param value decimal value
* #param numberFormat number format
* #param width min, max and pref width
*/
public NumberTextField(BigDecimal value, NumberFormat numberFormat, double width) {
super();
this.numberFormat = numberFormat;
setMinWidth(width);
setMaxWidth(width);
setPrefWidth(width);
initHandlers();
setValue(value);
setAlignment(Pos.BOTTOM_RIGHT);
}
public final BigDecimal getValue() {
return value.get();
}
public final void setValue(BigDecimal value) {
this.value.set(value);
}
public ObjectProperty<BigDecimal> valueProperty() {
return value;
}
private void initHandlers() {
// try to parse when focus is lost or RETURN is hit
setOnAction((ActionEvent arg0) -> {
parseAndFormatInput();
});
focusedProperty().addListener((ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) -> {
if (!newValue) {
parseAndFormatInput();
}
});
// Set text in field if BigDecimal property is changed from outside.
valueProperty().addListener((ObservableValue<? extends BigDecimal> observable,
BigDecimal oldValue, BigDecimal newValue) -> {
setText(numberFormat.format(newValue));
});
}
/**
* Tries to parse the user input to a number according to the provided
* NumberFormat
*/
private void parseAndFormatInput() {
try {
String input = getText();
if (input == null || input.length() == 0) {
return;
}
Number parsedNumber = numberFormat.parse(input);
BigDecimal newValue = new BigDecimal(parsedNumber.toString());
setValue(newValue);
selectAll();
} catch (ParseException ex) {
// If parsing fails keep old number
setText(numberFormat.format(value.get()));
}
}
}
And as BigDecimalTextField:
NumberTextField bigDecimalField =
new NumberTextField(new DecimalFormat("#,###,###,##0.00"));
I have a problem that whenever I create a process instance in Camunda Process Service, it does not write anything to ACT_HI_OP_LOG. I am not sure why it is not saving the histories into database.
#Component
#Order(Ordering.DEFAULT_ORDER + 1)
public class ProcessEngineConfiguration implements ProcessEnginePlugin {
private String tenantId;
#Override
public void preInit(ProcessEngineConfigurationImpl processEngineConfiguration) {
HistoryLevel historyLevel = new HistoryLevelFull();
processEngineConfiguration.setHistoryLevel(historyLevel);
processEngineConfiguration.setTenantCheckEnabled(true);
// processEngineConfiguration.setHistory(org.camunda.bpm.engine.ProcessEngineConfiguration.HISTORY_FULL);
processEngineConfiguration.setTenantIdProvider(new TenantIdProvider() {
#Override
public String provideTenantIdForProcessInstance(TenantIdProviderProcessInstanceContext ctx) {
return tenantId;
}
#Override
public String provideTenantIdForHistoricDecisionInstance(TenantIdProviderHistoricDecisionInstanceContext ctx) {
return tenantId;
}
#Override
public String provideTenantIdForCaseInstance(TenantIdProviderCaseInstanceContext ctx) {
return tenantId;
}
});
processEngineConfiguration.setJobExecutor(processEngineConfiguration.getJobExecutor());
}
This is how I start the process.
ProcessInstance pi = null;
try {
identityService.setAuthentication(getAuthentication());
pi = runtimeService.startProcessInstanceByKey(flowName, businessKey, variables);
} finally {
identityService.setAuthentication(null);
}
if (pi == null)
return null;
Did you check configuration?
historyLevelCheckEnabled default value is true.
Can you try to set that value false.
If you set that false, this check would not be performed.
I've used the following code to add two properties to Form Designer but they won't display. The field type is similar to Sitecore.Form.Web.UI.Controls.CheckboxList and I need it to display the same properties. Unfortunately I can't step into this code and the module isn't throwing any errors so I feel like I'm missing something simple.
public class CheckBoxListPipedField : Sitecore.Forms.Mvc.Models.Fields.CheckBoxListField
{
[VisualCategory("List")]
[VisualFieldType(typeof(Sitecore.Form.Core.Visual.ListField))]
[VisualProperty("Items:", 100)]
public ListItemCollection ListItems { get; set; }
[VisualCategory("List")]
[VisualFieldType(typeof(MultipleSelectedValueField))]
[VisualProperty("Selected Value:", 200)]
public ListItemCollection SelectedValue { get; set; }
public CheckBoxListPipedField(Item item) : base(item)
{
}
public override ControlResult GetResult()
{
var values = new List<string>();
StringBuilder stringBuilder1 = new StringBuilder();
if (this.Items != null)
{
foreach (SelectListItem selectListItem in
from item in this.Items
where item.Selected
select item)
{
values.Add(selectListItem.Value);
stringBuilder1.AppendFormat("{0}, ", selectListItem.Text);
}
}
var results = string.Join("|", values);
return new ControlResult(base.ID.ToString(), base.Title, results, stringBuilder1.ToString(0, (stringBuilder1.Length > 0 ? stringBuilder1.Length - 2 : 0)));
}
}
Not sure why they wouldn't show up as the default code for the CheckboxListField doesn't have those, but try:
[TypeConverter(typeof(ListSelectItemsConverter))]
public override List<SelectListItem> Items
{
get
{
return base.Items;
}
set
{
base.Items = value;
if (this.Items != null)
{
this.Value = (
from x in this.Items
where x.Selected
select x.Value).ToList<string>();
}
}
}
[ParameterName("selectedvalue")]
[PropertyBinder(typeof(ListFieldValueBinder))]
[TypeConverter(typeof(ListItemsConverter))]
public override object Value
{
get;
set;
}
You might be able to just set these to get { return base.Column }, etc, but here is how it looks on the base class.
[DefaultValue(1)]
public int Columns
{
get;
set;
}
[TypeConverter(typeof(StringToDirection))]
public Direction Direction
{
get;
set;
}
public int Rows
{
get
{
if (this.Items.IsNullOrEmpty<SelectListItem>())
{
return 1;
}
int num = (this.Columns == 0 ? 1 : this.Columns);
if (this.Items.Count % num <= 0)
{
return this.Items.Count / num;
}
return this.Items.Count / num + 1;
}
}
There was nothing wrong with the code in question. I neglected to add the assembly and class to the new Field Type, and had only set the MVC type.
I have tried for days but I couldn't reach any successful result. I need to post images with their information (s.t. created user name).
This is my method;
[HttpPost]
public Task<HttpResponseMessage> PostFile(string createdByName)
{
HttpRequestMessage request = this.Request;
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = System.Configuration.ConfigurationSettings.AppSettings["TempUploadDir"];
var provider = new MultipartFormDataStreamProvider(root);
var task = request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(o =>
{
AddImages(provider.BodyPartFileNames);
string file1 = provider.BodyPartFileNames.First().Value;
// this is the file name on the server where the file was saved
return new HttpResponseMessage()
{
Content = new StringContent("File uploaded.")
};
}
);
return task;
}
And this my TypeFormatterClass which is added global.asax
public class MultiFormDataMediaTypeFormatter : FormUrlEncodedMediaTypeFormatter
{
public MultiFormDataMediaTypeFormatter()
: base()
{
this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/form-data"));
}
protected override bool CanReadType(Type type)
{
return true;
}
protected override bool CanWriteType(Type type)
{
return false;
}
protected override Task<object> OnReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext)
{
var contents = formatterContext.Request.Content.ReadAsMultipartAsync().Result;
return Task.Factory.StartNew<object>(() =>
{
return new MultiFormKeyValueModel(contents);
});
}
class MultiFormKeyValueModel : IKeyValueModel
{
IEnumerable<HttpContent> _contents;
public MultiFormKeyValueModel(IEnumerable<HttpContent> contents)
{
_contents = contents;
}
public IEnumerable<string> Keys
{
get
{
return _contents.Cast<string>();
}
}
public bool TryGetValue(string key, out object value)
{
value = _contents.FirstDispositionNameOrDefault(key).ReadAsStringAsync().Result;
return true;
}
}
}
When I post images and "createdByName" I can reach images but I couldn't parameters. How can I do this?
Thank you.
To get your createdByName field, inside your ContinueWith :
var parts = o.Result;
HttpContent namePart = parts.FirstDispositionNameOrDefault("createdByName");
if (namePart == null)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
string name = namePart.ReadAsStringAsync().Result;
For a more detailed example, see :
http://www.asp.net/web-api/overview/working-with-http/html-forms-and-multipart-mime#multipartmime