Bug fixing: corrected Number Update in List behavior; corrected Number conversion

This commit is contained in:
Anry Das 2025-03-15 11:38:15 +02:00
commit f9ec156507
286 changed files with 24678 additions and 0 deletions

45
NovaPoshta/.gitignore vendored Normal file
View File

@ -0,0 +1,45 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### App ###
*.properties
mvnw
mvnw.cmd
*.zip
*.png
/plugins/tt1/
/plugins/tt2/
/plugins/DemoPlugin.jar
/plugins/RunTestImpl.jar
/DB/np.db

BIN
NovaPoshta/DB/np_empty.db Normal file

Binary file not shown.

View File

@ -0,0 +1,18 @@
{
"apiKey": "[ВАШ КЛЮЧ]",
"modelName": "TrackingDocument",
"calledMethod": "getStatusDocuments",
"methodProperties": {
"Documents" : [
{
"DocumentNumber":"20400048799000",
"Phone":"380600000000"
}
,
{
"DocumentNumber":"20400048799001",
"Phone":"380600000000"
}
]
}
}

Binary file not shown.

View File

@ -0,0 +1,119 @@
{
"success": true,
"data": [{
"PossibilityCreateReturn" : true,
"PossibilityCreateRefusal" : true,
"PossibilityChangeEW" : true,
"PossibilityCreateRedirecting" : true,
"Number" : "20400048799000",
"Redelivery" : "0",
"RedeliverySum" : "0",
"RedeliveryNum" : "",
"RedeliveryPayer" : "Sender/Recipient",
"OwnerDocumentType" : "",
"LastCreatedOnTheBasisDocumentType" : "",
"LastCreatedOnTheBasisPayerType" : "",
"LastCreatedOnTheBasisDateTime" : "",
"LastTransactionStatusGM" : "",
"LastTransactionDateTimeGM" : "",
"LastAmountTransferGM" : "",
"DateCreated" : "18-11-2021 11:52:42",
"DocumentWeight" : "3",
"FactualWeight" : "3",
"VolumeWeight" : "0.1",
"CheckWeight" : "",
"CheckWeightMethod" : "",
"DocumentCost" : "51",
"CalculatedWeight" : "3",
"SumBeforeCheckWeight" : "",
"PayerType" : "Sender",
"RecipientFullName" : "ПІБ",
"RecipientDateTime" : "21.11.2021 13:53:47",
"ScheduledDeliveryDate" : "19.11.2021 13:53:47",
"PaymentMethod" : "Cash",
"CargoDescriptionString" : "Одяг",
"CargoType" : "Cargo",
"CitySender" : "Київ",
"CityRecipient" : "Київ",
"WarehouseRecipient" : "Відділення №101 (до 15 кг), Міні-відділення: вул. Велика Васильківська, 143/2, (маг. "Фора")",
"CounterpartyType" : "PrivatePerson",
"AfterpaymentOnGoodsCost" : "0",
"ServiceType" : "WarehouseWarehouse",
"UndeliveryReasonsSubtypeDescription" : "",
"WarehouseRecipientNumber" : "101",
"LastCreatedOnTheBasisNumber" : "",
"PhoneRecipient" : "380600000000",
"RecipientFullNameEW" : "",
"WarehouseRecipientInternetAddressRef" : "00000000-0000-0000-0000-000000000000",
"MarketplacePartnerToken" : "",
"ClientBarcode" : "",
"RecipientAddress" : "м. Київ, Відділення №101 (до 15 кг), Міні-відділення, вул. Велика Васильківська, 143/2",
"CounterpartyRecipientDescription" : "Приватна особа",
"DateScan" : "0001-01-01 00:00:00",
"PaymentStatus" : "",
"PaymentStatusDate" : "",
"AmountToPay" : "",
"AmountPaid" : "",
"Status" : "",
"StatusCode" : "",
"RefEW" : "00000000-0000-0000-0000-000000000000",
"BackwardDeliverySubTypesActions" : "",
"BackwardDeliverySubTypesServices" : "",
"UndeliveryReasons" : "",
"DatePayedKeeping" : "",
"InternationalDeliveryType" : "",
"SeatsAmount" : "1",
"CardMaskedNumber" : "",
"ExpressWaybillPaymentStatus" : "PaymentNotAvailable",
"ExpressWaybillAmountToPay" : "",
"PhoneSender" : "",
"TrackingUpdateDate" : "2022-06-07 13:42:56",
"WarehouseSender" : "Отделение №178 (до 30 кг): просп. Оболонский, 35",
"DateReturnCargo" : "",
"DateMoving" : "",
"DateFirstDayStorage" : "",
"RefCityRecipient" : "00000000-0000-0000-0000-000000000000",
"RefCitySender" : "00000000-0000-0000-0000-000000000000",
"RefSettlementRecipient" : "00000000-0000-0000-0000-000000000000",
"RefSettlementSender" : "00000000-0000-0000-0000-000000000000",
"SenderAddress" : "м. Київ, Відділення №178 (до 30 кг): просп. Оболонський, 35",
"SenderFullNameEW" : "Іванов Петро Миколайович",
"AnnouncedPrice" : "50000",
"AdditionalInformationEW" : "",
"ActualDeliveryDate" : "",
"PostomatV3CellReservationNumber" : "00000000-0000-0000-0000-000000000000",
"OwnerDocumentNumber" : "",
"LastAmountReceivedCommissionGM" : "",
"DeliveryTimeframe" : "",
"CreatedOnTheBasis" : "",
"UndeliveryReasonsDate" : "",
"RecipientWarehouseTypeRef" : "00000000-0000-0000-0000-000000000000",
"WarehouseRecipientRef" : "00000000-0000-0000-0000-000000000000",
"CategoryOfWarehouse" : "Branch",
"WarehouseRecipientAddress" : "Киев, Героев Днепра, 53",
"WarehouseSenderInternetAddressRef" : "00000000-0000-0000-0000-000000000000",
"WarehouseSenderAddress" : "Киев, Оболонский, 35",
"CounterpartySenderType" : "PrivatePerson",
"AviaDelivery" : "",
"BarcodeRedBox" : "",
"CargoReturnRefusal" : "false",
"DaysStorageCargo" : "",
"Packaging" : null,
"PartialReturnGoods" : null,
"SecurePayment" : "false",
"PossibilityChangeCash2Card" : true,
"PossibilityChangeDeliveryIntervals" : true,
"PossibilityTermExtensio" : true,
"StorageAmount" : "",
"StoragePrice" : "",
"FreeShipping" : "",
"LoyaltyCardRecipient" : ""
}],
"errors": [],
"warnings": [],
"info": [],
"messageCodes": [],
"errorCodes": [],
"warningCodes": [],
"infoCodes": []
}

View File

@ -0,0 +1,204 @@
/**
* Copyright (c) 2013, ControlsFX
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of ControlsFX, any associated website, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.controlsfx.samples.actions;
import javafx.beans.property.BooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.controlsfx.ControlsFXSample;
import org.controlsfx.control.action.Action;
import org.controlsfx.control.action.ActionCheck;
import org.controlsfx.control.action.ActionGroup;
import org.controlsfx.control.action.ActionUtils;
import org.controlsfx.control.action.ActionUtils.ActionTextBehavior;
import org.controlsfx.samples.Utils;
import java.util.Arrays;
import java.util.Collection;
import static org.controlsfx.control.action.ActionUtils.ACTION_SEPARATOR;
import static org.controlsfx.control.action.ActionUtils.ACTION_SPAN;
public class HelloActionGroup extends ControlsFXSample {
private static final ImageView image = new ImageView( new Image("/org/controlsfx/samples/security-low.png"));
private Collection<? extends Action> actions = Arrays.asList(
new ActionGroup("Group 1", image, new DummyAction("Action 1.1", image),
new CheckDummyAction("Action 1.2") ),
new ActionGroup("Group 2", image, new DummyAction("Action 2.1"),
ACTION_SEPARATOR,
new ActionGroup("Action 2.2", new DummyAction("Action 2.2.1"),
new CheckDummyAction("Action 2.2.2")),
new DummyAction("Action 2.3") ),
ACTION_SPAN,
ACTION_SEPARATOR,
new CheckDummyAction("Action 3", image),
new ActionGroup("Group 4", image, new DummyAction("Action 4.1", image),
new CheckDummyAction("Action 4.2"))
);
private static class DummyAction extends Action {
public DummyAction(String name, Node image) {
super(name);
setGraphic(image);
setEventHandler(ae -> String.format("Action '%s' is executed", getText()) );
}
public DummyAction( String name ) {
super(name);
}
@Override public String toString() {
return getText();
}
}
@ActionCheck
private static class CheckDummyAction extends Action {
public CheckDummyAction(String name, Node image) {
super(name);
setGraphic(image);
setEventHandler(ae -> String.format("Action '%s' is executed", getText()) );
}
public CheckDummyAction( String name ) {
super(name);
}
@Override public String toString() {
return getText();
}
}
private ObservableList<Action> flatten( Collection<? extends Action> actions, ObservableList<Action> dest ) {
for (Action a : actions) {
if ( a == null || a == ActionUtils.ACTION_SEPARATOR ) continue;
dest.add(a);
if ( a instanceof ActionGroup ) {
flatten( ((ActionGroup)a).getActions(), dest);
}
}
return dest;
}
@Override public String getSampleName() {
return "Action Group";
}
@Override public String getJavaDocURL() {
return Utils.JAVADOC_BASE + "org/controlsfx/control/action/ActionGroup.html";
}
@Override public String getSampleDescription() {
return "MenuBar, ToolBar and ContextMenu presented here are effortlessly built out of the same action tree. " +
"Action properties can be dynamically changed, triggering changes in all related controls";
}
@Override public Node getControlPanel() {
GridPane grid = new GridPane();
grid.setVgap(10);
grid.setHgap(10);
grid.setPadding(new Insets(30, 30, 0, 30));
int row = 0;
// Dynamically enable/disable action
Label lblAddCrumb = new Label("Dynamically enable/disable action: ");
lblAddCrumb.getStyleClass().add("property");
grid.add(lblAddCrumb, 0, row);
final ComboBox<Action> cbActions = new ComboBox<>(flatten( actions, FXCollections.<Action>observableArrayList()));
cbActions.getSelectionModel().select(0);
grid.add(cbActions, 1, row);
Action toggleAction = new Action("Enable/Disable") {
{ setEventHandler(this::handleAction); }
private void handleAction(ActionEvent ae) {
Action action = cbActions.getSelectionModel().getSelectedItem();
if ( action != null ) {
BooleanProperty p = action.disabledProperty();
p.set(!p.get());
}
}
};
grid.add(ActionUtils.createButton(toggleAction), 2, row++);
return grid;
}
@Override public Node getPanel(final Stage stage) {
VBox root = new VBox(10);
root.setPadding(new Insets(10, 10, 10, 10));
root.setMaxHeight(Double.MAX_VALUE);
Insets topMargin = new Insets(7, 7, 0, 7);
Insets margin = new Insets(0, 7, 7, 7);
addWithMargin(root, new Label("MenuBar:"), topMargin ).setStyle("-fx-font-weight: bold;");
addWithMargin(root, ActionUtils.createMenuBar(actions), margin);
addWithMargin(root,new Label("ToolBar (with text on controls):"), topMargin).setStyle("-fx-font-weight: bold;");
addWithMargin(root, ActionUtils.createToolBar(actions, ActionTextBehavior.SHOW), margin);
addWithMargin(root,new Label("ToolBar (no text on controls):"), topMargin).setStyle("-fx-font-weight: bold;");
addWithMargin(root, ActionUtils.createToolBar(actions, ActionTextBehavior.HIDE), margin);
addWithMargin(root, new Label("ContextMenu:"), topMargin).setStyle("-fx-font-weight: bold;");
Label context = new Label("Right-click to see the context menu");
addWithMargin(root,context, margin);
context.setContextMenu(ActionUtils.createContextMenu(actions));
context.setStyle("-fx-background-color: #E0E0E0 ;-fx-border-color: black;-fx-border-style: dotted");
context.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
VBox.setVgrow(context, Priority.ALWAYS);
VBox.setVgrow(root, Priority.ALWAYS);
return root;
}
private Control addWithMargin( VBox parent, Control control, Insets insets) {
parent.getChildren().add(control);
VBox.setMargin(control, insets);
return control;
}
public static void main(String[] args) {
launch(args);
}
}

View File

@ -0,0 +1,224 @@
/**
* Copyright (c) 2013, ControlsFX
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of ControlsFX, any associated website, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.controlsfx.samples.actions;
import javafx.beans.property.BooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.controlsfx.ControlsFXSample;
import org.controlsfx.control.action.*;
import org.controlsfx.control.action.ActionUtils.ActionTextBehavior;
import org.controlsfx.samples.Utils;
import java.util.Arrays;
import java.util.Collection;
import static org.controlsfx.control.action.ActionMap.action;
import static org.controlsfx.control.action.ActionMap.actions;
import static org.controlsfx.control.action.ActionUtils.ACTION_SEPARATOR;
import static org.controlsfx.control.action.ActionUtils.ACTION_SPAN;
public class HelloActionProxy extends ControlsFXSample {
private static final String imagePath = "/org/controlsfx/samples/security-low.png";
private static final ImageView image = new ImageView(new Image(imagePath));
private Collection<? extends Action> actions;
public HelloActionProxy() {
ActionMap.register(this);
actions = Arrays.asList(
new ActionGroup("Group 1", image, actions("action11","action12") ),
new ActionGroup("Group 2", image, actions("action21","---","action22", "action221","action222","action23") ),
ACTION_SPAN,
ACTION_SEPARATOR,
action("action3"),
new ActionGroup("Group 4", image, actions("action41","action42"))
);
}
@ActionProxy(text="Action 1.1", graphic=imagePath, accelerator="ctrl+shift+T")
private void action11() {
System.out.println( "Action 1.1 is executed");
}
@ActionCheck
@ActionProxy(text="Action 1.2", graphic="http://icons.iconarchive.com/icons/custom-icon-design/mini-3/16/teacher-male-icon.png")
private void action12() {
System.out.println( "Action 1.2 is executed");
}
@ActionProxy(text="Action 2.1", graphic=imagePath, factory="org.controlsfx.samples.actions.HelloCustomActionFactory")
private void action21() {
System.out.println( "Action 2.1 is executed (and used a custom action factory)");
}
@ActionProxy(text="Action 2.2", graphic=imagePath)
private void action22( ActionEvent evt ) {
System.out.println( "Action 2.2 is executed (and received an ActionEvent)");
}
@ActionProxy(text="Action 2.2.1", graphic=imagePath)
private void action221( ActionEvent evt, Action action ) {
System.out.println( "Action 2.2.1 is executed (and received both an ActionEvent and an Action)");
}
@ActionProxy(text="Action 2.2.2", graphic=imagePath)
private void action222() {
System.out.println( "Action 2.2.2 is executed");
}
@ActionProxy(text="Action 2.3", graphic=imagePath)
private void action23() {
System.out.println( "Action 2.3 is executed");
}
@ActionCheck
@ActionProxy(text="Action 3", graphic="font>FontAwesome|STAR")
private void action3() {
System.out.println( "Action 3 is executed");
}
@ActionProxy(text="Action 4.1", graphic=imagePath)
private void action41() {
System.out.println( "Action 4.1 is executed");
}
@ActionProxy(text="Action 4.2", graphic=imagePath)
private void action42() {
System.out.println( "Action 4.2 is executed");
}
private ObservableList<Action> flatten( Collection<? extends Action> actions, ObservableList<Action> dest ) {
for (Action a : actions) {
if ( a == null || a == ActionUtils.ACTION_SEPARATOR ) continue;
dest.add(a);
if ( a instanceof ActionGroup ) {
flatten( ((ActionGroup)a).getActions(), dest);
}
}
return dest;
}
@Override public String getSampleName() {
return "Action Proxy";
}
@Override public String getJavaDocURL() {
return Utils.JAVADOC_BASE + "org/controlsfx/control/action/ActionProxy.html";
}
@Override public String getSampleDescription() {
return "MenuBar, ToolBar and ContextMenu presented here are effortlessly built out of the same action tree. " +
"Action properties can be dynamically changed, triggering changes in all related controls";
}
@Override public Node getControlPanel() {
GridPane grid = new GridPane();
grid.setVgap(10);
grid.setHgap(10);
grid.setPadding(new Insets(30, 30, 0, 30));
int row = 0;
// Dynamically enable/disable action
Label lblAddCrumb = new Label("Dynamically enable/disable action: ");
lblAddCrumb.getStyleClass().add("property");
grid.add(lblAddCrumb, 0, row);
final ComboBox<Action> cbActions = new ComboBox<>(flatten( actions, FXCollections.<Action>observableArrayList()));
cbActions.getSelectionModel().select(0);
grid.add(cbActions, 1, row);
Action toggleAction = new Action("Enable/Disable") {
{ setEventHandler(this::handleAction); }
private void handleAction(ActionEvent ae) {
Action action = cbActions.getSelectionModel().getSelectedItem();
if ( action != null ) {
BooleanProperty p = action.disabledProperty();
p.set(!p.get());
}
}
};
grid.add(ActionUtils.createButton(toggleAction), 2, row++);
return grid;
}
@Override public Node getPanel(final Stage stage) {
VBox root = new VBox(10);
root.setPadding(new Insets(10, 10, 10, 10));
root.setMaxHeight(Double.MAX_VALUE);
Insets topMargin = new Insets(7, 7, 0, 7);
Insets margin = new Insets(0, 7, 7, 7);
addWithMargin(root, new Label("MenuBar:"), topMargin ).setStyle("-fx-font-weight: bold;");
addWithMargin(root, ActionUtils.createMenuBar(actions), margin);
addWithMargin(root,new Label("ToolBar (with text on controls):"), topMargin).setStyle("-fx-font-weight: bold;");
addWithMargin(root, ActionUtils.createToolBar(actions, ActionTextBehavior.SHOW), margin);
addWithMargin(root,new Label("ToolBar (no text on controls):"), topMargin).setStyle("-fx-font-weight: bold;");
addWithMargin(root, ActionUtils.createToolBar(actions, ActionTextBehavior.HIDE), margin);
addWithMargin(root, new Label("ContextMenu:"), topMargin).setStyle("-fx-font-weight: bold;");
Label context = new Label("Right-click to see the context menu");
addWithMargin(root,context, margin);
context.setContextMenu(ActionUtils.createContextMenu(actions));
context.setStyle("-fx-background-color: #E0E0E0 ;-fx-border-color: black;-fx-border-style: dotted");
context.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
VBox.setVgrow(context, Priority.ALWAYS);
VBox.setVgrow(root, Priority.ALWAYS);
return root;
}
private Control addWithMargin( VBox parent, Control control, Insets insets) {
parent.getChildren().add(control);
VBox.setMargin(control, insets);
return control;
}
public static void main(String[] args) {
launch(args);
}
}

View File

@ -0,0 +1,208 @@
/**
* Copyright (c) 2014, ControlsFX
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of ControlsFX, any associated website, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.controlsfx.samples.textfields;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.controlsfx.ControlsFXSample;
import org.controlsfx.control.textfield.AutoCompletionBinding;
import org.controlsfx.control.textfield.TextFields;
import org.controlsfx.samples.Utils;
import impl.org.controlsfx.skin.AutoCompletePopup;
import impl.org.controlsfx.skin.AutoCompletePopupSkin;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class HelloAutoComplete extends ControlsFXSample {
private AutoCompletionBinding<String> autoCompletionBinding;
private String[] _possibleSuggestions = {"Hey", "Hello", "Hello World", "Apple", "Cool", "Costa", "Cola", "Coca Cola"};
private Set<String> possibleSuggestions = new HashSet<>(Arrays.asList(_possibleSuggestions));
private Map<String, Color> colorSuggestions = allColorsWithName();
private TextField learningTextField;
@Override public String getSampleName() {
return "AutoComplete";
}
@Override public String getJavaDocURL() {
return Utils.JAVADOC_BASE + "org/controlsfx/control/textfield/TextFields.html";
}
@Override public String getSampleDescription() {
return "AutoComplete helps a user with suggestions to type faster, "
+ "but does not limit the user from entering alternative text."
+ "\n\n"
+ "The textfields have been primed with the following words:\n"
+ "\"Hey\", \"Hello\", \"Hello World\", \"Apple\", \"Cool\", "
+ "\"Costa\", \"Cola\", \"Coca Cola\""
+ "\n\n"
+ "The 'Learning TextField' will add whatever words are typed "
+ "to the auto-complete popup, as long as you press Enter once "
+ "you've finished typing the word."
+ "\n\n"
+ "The Color TextField will suggest different colors when you type "
+ "in their name.";
}
@Override public Node getPanel(final Stage stage) {
BorderPane root = new BorderPane();
GridPane grid = new GridPane();
grid.setVgap(10);
grid.setHgap(10);
grid.setPadding(new Insets(30, 30, 0, 30));
//
// TextField with static auto-complete functionality
//
TextField textField = new TextField();
TextFields.bindAutoCompletion(
textField,
"Hey", "Hello", "Hello World", "Apple", "Cool", "Costa", "Cola", "Coca Cola");
grid.add(new Label("Auto-complete Text"), 0, 0);
grid.add(textField, 1, 0);
GridPane.setHgrow(textField, Priority.ALWAYS);
//
// TextField with learning auto-complete functionality
// Learn the word when user presses ENTER
//
learningTextField = new TextField();
autoCompletionBinding = TextFields.bindAutoCompletion(learningTextField, possibleSuggestions);
learningTextField.setOnKeyPressed(new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent ke) {
switch (ke.getCode()) {
case ENTER:
autoCompletionLearnWord(learningTextField.getText().trim());
break;
default:
break;
}
}
});
grid.add(new Label("Learning TextField"), 0, 1);
grid.add(learningTextField, 1, 1);
GridPane.setHgrow(learningTextField, Priority.ALWAYS);
//
// TextField with custom cell factory
// Completes color names
//
TextField customTextField = new TextField();
AutoCompletePopup<String> colorCompletionPopup = TextFields.bindAutoCompletion(customTextField, colorSuggestions.keySet()).getAutoCompletionPopup();
colorCompletionPopup.setSkin(new AutoCompletePopupSkin<String>(colorCompletionPopup, param -> new ListCell<String>() {
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setGraphic(new Rectangle(32, 32, colorSuggestions.get(item)));
setText(item);
}
}
}));
grid.add(new Label("Color TextField with custom CellFactory"), 0, 2);
grid.add(customTextField, 1, 2);
GridPane.setHgrow(customTextField, Priority.ALWAYS);
root.setTop(grid);
return root;
}
private void autoCompletionLearnWord(String newWord){
possibleSuggestions.add(newWord);
// we dispose the old binding and recreate a new binding
if (autoCompletionBinding != null) {
autoCompletionBinding.dispose();
}
autoCompletionBinding = TextFields.bindAutoCompletion(learningTextField, possibleSuggestions);
}
@Override public Node getControlPanel() {
GridPane grid = new GridPane();
grid.setVgap(10);
grid.setHgap(10);
grid.setPadding(new Insets(30, 30, 0, 30));
// TODO Add customization example controls
return grid;
}
/* Modified from https://stackoverflow.com/a/17465261/6094756 */
private Map<String, Color> allColorsWithName() {
Map<String, Color> map = new HashMap<>();
try {
for (Field f : Color.class.getFields()) {
Object obj = f.get(null);
if (obj instanceof Color) {
map.put(f.getName(), (Color) obj);
}
}
} catch (IllegalArgumentException | IllegalAccessException e) {
map.put("red", Color.RED);
map.put("green", Color.GREEN);
map.put("blue", Color.BLUE);
}
return map;
}
public static void main(String[] args) {
launch(args);
}
}

View File

@ -0,0 +1,241 @@
/**
* Copyright (c) 2013, 2018 ControlsFX
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of ControlsFX, any associated website, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.controlsfx.samples.checked;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import org.controlsfx.ControlsFXSample;
import org.controlsfx.control.CheckComboBox;
import org.controlsfx.control.IndexedCheckModel;
import org.controlsfx.samples.Utils;
public class HelloCheckComboBox extends ControlsFXSample {
private final Label checkedItemsLabel = new Label();
private CheckComboBox<String> checkComboBox;
@Override public String getSampleName() {
return "CheckComboBox";
}
@Override public String getJavaDocURL() {
return Utils.JAVADOC_BASE + "org/controlsfx/control/CheckComboBox.html";
}
@Override public String getSampleDescription() {
return "A simple UI control that makes it possible to select zero or "
+ "more items within a ComboBox without the need to set a custom "
+ "cell factory or manually create boolean properties for each "
+ "row - simply use the check model property to request the "
+ "current selection state.";
}
@Override public Node getPanel(Stage stage) {
GridPane grid = new GridPane();
grid.setVgap(10);
grid.setHgap(10);
grid.setPadding(new Insets(30, 30, 0, 30));
int row = 0;
final ObservableList<String> strings = FXCollections.observableArrayList();
for (int i = 0; i <= 100; i++) {
strings.add("Item " + i);
}
// normal ComboBox
grid.add(new Label("Normal ComboBox: "), 0, row);
final ComboBox<String> comboBox = new ComboBox<>(strings);
comboBox.focusedProperty().addListener((o, ov, nv) -> {
if(nv) comboBox.show(); else comboBox.hide();
});
grid.add(comboBox, 1, row++);
// CheckComboBox
checkComboBox = new CheckComboBox<>(strings);
checkComboBox.focusedProperty().addListener((o, ov, nv) -> {
if(nv) checkComboBox.show(); else checkComboBox.hide();
});
checkComboBox.getCheckModel().getCheckedItems().addListener((ListChangeListener<String>) change -> {
updateText(checkedItemsLabel, change.getList());
while (change.next()) {
System.out.println("============================================");
System.out.println("Change: " + change);
System.out.println("Added sublist " + change.getAddedSubList());
System.out.println("Removed sublist " + change.getRemoved());
System.out.println("List " + change.getList());
System.out.println("Added " + change.wasAdded() + " Permutated " + change.wasPermutated() + " Removed " + change.wasRemoved() + " Replaced "
+ change.wasReplaced() + " Updated " + change.wasUpdated());
System.out.println("============================================");
}
});
grid.add(new Label("CheckComboBox: "), 0, row);
grid.add(checkComboBox, 1, row++);
CheckComboBox<Person> checkComboBox2 = new CheckComboBox<>(Person.createDemoList());
checkComboBox2.setConverter(new StringConverter<Person>() {
@Override
public String toString(Person object) {
return object.getFullName();
}
@Override
public Person fromString(String string) {
return null;
}
});
checkComboBox2.focusedProperty().addListener((o, ov, nv) -> {
if(nv) checkComboBox2.show(); else checkComboBox2.hide();
});
grid.add(new Label("CheckComboBox with data objects: "), 0, row);
grid.add(checkComboBox2, 1, row);
return grid;
}
@Override public Node getControlPanel() {
GridPane grid = new GridPane();
grid.setVgap(10);
grid.setHgap(10);
grid.setPadding(new Insets(30, 30, 0, 30));
int row = 0;
Label label1 = new Label("Checked items: ");
label1.getStyleClass().add("property");
grid.add(label1, 0, 0);
grid.add(checkedItemsLabel, 1, row++);
updateText(checkedItemsLabel, null);
Label checkItem2Label = new Label("Check 'Item 2': ");
checkItem2Label.getStyleClass().add("property");
grid.add(checkItem2Label, 0, row);
final CheckBox checkItem2Btn = new CheckBox();
checkItem2Btn.setOnAction(e -> {
IndexedCheckModel<String> cm = checkComboBox.getCheckModel();
if (cm != null) {
cm.toggleCheckState(2);
}
});
grid.add(checkItem2Btn, 1, row);
return grid;
}
protected void updateText(Label label, ObservableList<? extends String> list) {
final StringBuilder sb = new StringBuilder();
if (list != null) {
for (int i = 0, max = list.size(); i < max; i++) {
sb.append(list.get(i));
if (i < max - 1) {
sb.append(", ");
}
}
}
final String str = sb.toString();
label.setText(str.isEmpty() ? "<empty>" : str);
}
public static void main(String[] args) {
launch(args);
}
}
class Person {
private StringProperty firstName = new SimpleStringProperty();
private StringProperty lastName = new SimpleStringProperty();
private ReadOnlyStringWrapper fullName = new ReadOnlyStringWrapper();
public Person(String firstName, String lastName) {
this.firstName.set(firstName);
this.lastName.set(lastName);
fullName.bind(Bindings.concat(firstName, " ", lastName));
}
public static final ObservableList<Person> createDemoList() {
final ObservableList<Person> result = FXCollections.observableArrayList();
result.add(new Person("Paul", "McCartney"));
result.add(new Person("Andrew Lloyd", "Webber"));
result.add(new Person("Herb", "Alpert"));
result.add(new Person("Emilio", "Estefan"));
result.add(new Person("Bernie", "Taupin"));
result.add(new Person("Elton", "John"));
result.add(new Person("Mick", "Jagger"));
result.add(new Person("Keith", "Richerds"));
return result;
}
public final StringProperty firstNameProperty() {
return this.firstName;
}
public final java.lang.String getFirstName() {
return this.firstNameProperty().get();
}
public final void setFirstName(final String firstName) {
this.firstNameProperty().set(firstName);
}
public final StringProperty lastNameProperty() {
return this.lastName;
}
public final String getLastName() {
return this.lastNameProperty().get();
}
public final void setLastName(final String lastName) {
this.lastNameProperty().set(lastName);
}
public final ReadOnlyStringProperty fullNameProperty() {
return this.fullName.getReadOnlyProperty();
}
public final String getFullName() {
return this.fullNameProperty().get();
}
}

View File

@ -0,0 +1,742 @@
/**
* Copyright (c) 2013, 2015 ControlsFX
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of ControlsFX, any associated website, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.controlsfx.samples.dialogs;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceDialog;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextInputDialog;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.Window;
import org.controlsfx.ControlsFXSample;
import org.controlsfx.dialog.CommandLinksDialog;
import org.controlsfx.dialog.CommandLinksDialog.CommandLinksButtonType;
import org.controlsfx.dialog.WizardPane;
import org.controlsfx.dialog.ExceptionDialog;
import org.controlsfx.dialog.FontSelectorDialog;
import org.controlsfx.dialog.LoginDialog;
import org.controlsfx.dialog.ProgressDialog;
import org.controlsfx.dialog.Wizard;
import org.controlsfx.dialog.Wizard.LinearFlow;
import org.controlsfx.validation.ValidationSupport;
import org.controlsfx.validation.Validator;
public class HelloDialogs extends ControlsFXSample {
@Override
public String getSampleName() {
return "Dialogs";
}
@Override
public String getJavaDocURL() {
// return Utils.JAVADOC_BASE + "org/controlsfx/dialog/Dialogs.html";
return null;
}
@Override
public String getSampleDescription() {
return "";
}
private final ComboBox<StageStyle> styleCombobox = new ComboBox<>();
private final ComboBox<Modality> modalityCombobox = new ComboBox<>();
private final CheckBox cbUseBlocking = new CheckBox();
private final CheckBox cbCloseDialogAutomatically = new CheckBox();
private final CheckBox cbShowMasthead = new CheckBox();
private final CheckBox cbSetOwner = new CheckBox();
private final CheckBox cbCustomGraphic = new CheckBox();
private Stage stage;
@Override
public Node getPanel(Stage stage) {
this.stage = stage;
GridPane grid = new GridPane();
grid.setPadding(new Insets(10, 10, 10, 10));
grid.setHgap(10);
grid.setVgap(10);
int row = 0;
Label javafxDialogs = new Label("JavaFX Dialogs:");
javafxDialogs.setFont(Font.font(25));
grid.add(javafxDialogs, 0, row++, 2, 1);
// *******************************************************************
// Information Dialog
// *******************************************************************
grid.add(createLabel("Information Dialog: "), 0, row);
final Button Hyperlink2 = new Button("Show");
Hyperlink2.setOnAction( (ActionEvent e) -> {
Alert dlg = createAlert(AlertType.INFORMATION);
dlg.setTitle("Custom title");
String optionalMasthead = "Wouldn't this be nice?";
dlg.getDialogPane().setContentText("A collection of pre-built JavaFX dialogs?\nSeems like a great idea to me...");
configureSampleDialog(dlg, optionalMasthead);
// lets get some output when events happen
dlg.setOnShowing(evt -> System.out.println(evt));
dlg.setOnShown(evt -> System.out.println(evt));
dlg.setOnHiding(evt -> System.out.println(evt));
dlg.setOnHidden(evt -> System.out.println(evt));
// dlg.setOnCloseRequest(evt -> evt.consume());
showDialog(dlg);
});
final Button Hyperlink2a = new Button("2 x Buttons (no cancel)");
Hyperlink2a.setOnAction( (ActionEvent e) -> {
Alert dlg = createAlert(AlertType.INFORMATION);
dlg.setTitle("Custom title");
String optionalMasthead = "Wouldn't this be nice?";
dlg.getDialogPane().setContentText("A collection of pre-built JavaFX dialogs?\nSeems like a great idea to me...");
configureSampleDialog(dlg, optionalMasthead);
dlg.getButtonTypes().add(ButtonType.NEXT);
// dlg.setOnCloseRequest(evt -> evt.consume());
showDialog(dlg);
});
grid.add(new HBox(10, Hyperlink2, Hyperlink2a), 1, row);
row++;
// *******************************************************************
// Confirmation Dialog
// *******************************************************************
grid.add(createLabel("Confirmation Dialog: "), 0, row);
final CheckBox cbShowCancel = new CheckBox("Show Cancel Button");
cbShowCancel.setSelected(true);
final Button Hyperlink3 = new Button("Show");
Hyperlink3.setOnAction(e -> {
Alert dlg = createAlert(AlertType.CONFIRMATION);
dlg.setTitle("You do want dialogs right?");
String optionalMasthead = "Just Checkin'";
dlg.getDialogPane().setContentText("I was a bit worried that you might not want them, so I wanted to double check.");
if (!cbShowCancel.isSelected()) {
dlg.getDialogPane().getButtonTypes().remove(ButtonType.CANCEL);
}
configureSampleDialog(dlg, optionalMasthead);
showDialog(dlg);
});
grid.add(new HBox(10, Hyperlink3, cbShowCancel), 1, row);
row++;
// *******************************************************************
// Warning Dialog
// *******************************************************************
grid.add(createLabel("Warning Dialog: "), 0, row);
final Button Hyperlink6a = new Button("Show");
Hyperlink6a.setOnAction(e -> {
Alert dlg = createAlert(AlertType.WARNING);
dlg.setTitle("I'm warning you!");
String optionalMasthead = "This is a warning";
dlg.getDialogPane().setContentText("I'm glad I didn't need to use this...");
configureSampleDialog(dlg, optionalMasthead);
showDialog(dlg);
});
grid.add(new HBox(10, Hyperlink6a), 1, row);
row++;
// *******************************************************************
// Error Dialog
// *******************************************************************
grid.add(createLabel("Error Dialog: "), 0, row);
final Button Hyperlink7a = new Button("Show");
Hyperlink7a.setOnAction(e -> {
Alert dlg = createAlert(AlertType.ERROR);
dlg.setTitle("It looks like you're making a bad decision");
String optionalMasthead = "Exception Encountered";
dlg.getDialogPane().setContentText("Better change your mind - this is really your last chance! (Even longer text that should probably wrap)");
configureSampleDialog(dlg, optionalMasthead);
showDialog(dlg);
});
grid.add(new HBox(10, Hyperlink7a), 1, row);
row++;
// *******************************************************************
// Input Dialog (with header)
// *******************************************************************
grid.add(createLabel("Input Dialog: "), 0, row);
final Button Hyperlink8 = new Button("TextField");
Hyperlink8.setOnAction(e -> {
TextInputDialog dlg = new TextInputDialog("");
dlg.setTitle("Name Check");
String optionalMasthead = "Please type in your name";
dlg.getDialogPane().setContentText("What is your name?");
configureSampleDialog(dlg, optionalMasthead);
showDialog(dlg);
});
final Button Hyperlink9 = new Button("Initial Value Set");
Hyperlink9.setOnAction(e -> {
TextInputDialog dlg = new TextInputDialog("Jonathan");
dlg.setTitle("Name Guess");
String optionalMasthead = "Name Guess";
dlg.getDialogPane().setContentText("Pick a name?");
configureSampleDialog(dlg, optionalMasthead);
showDialog(dlg);
});
final Button Hyperlink10 = new Button("Set Choices (< 10)");
Hyperlink10.setOnAction(e -> {
ChoiceDialog<String> dlg = new ChoiceDialog<>("Jonathan",
"Matthew", "Jonathan", "Ian", "Sue", "Hannah");
dlg.setTitle("Name Guess");
String optionalMasthead = "Name Guess";
dlg.getDialogPane().setContentText("Pick a name?");
configureSampleDialog(dlg, optionalMasthead);
showDialog(dlg);
});
final Button Hyperlink11 = new Button("Set Choices (>= 10)");
Hyperlink11.setOnAction(e -> {
ChoiceDialog<String> dlg = new ChoiceDialog<>("Jonathan",
"Matthew", "Jonathan", "Ian", "Sue",
"Hannah", "Julia", "Denise", "Stephan",
"Sarah", "Ron", "Ingrid");
dlg.setTitle("Name Guess");
String optionalMasthead = "Name Guess";
dlg.getDialogPane().setContentText("Pick a name?");
configureSampleDialog(dlg, optionalMasthead);
showDialog(dlg);
});
grid.add(new HBox(10, Hyperlink8, Hyperlink9, Hyperlink10, Hyperlink11), 1, row);
row++;
// --------- ControlsFX-specific Dialogs
Label controlsfxDialogs = new Label("ControlsFX Dialogs:");
controlsfxDialogs.setFont(Font.font(25));
grid.add(controlsfxDialogs, 0, row++, 2, 1);
// *******************************************************************
// Command links
// *******************************************************************
grid.add(createLabel("Pre-built dialogs: "), 0, row);
final Button Hyperlink12 = new Button("Command Links");
Hyperlink12.setOnAction(e -> {
List<CommandLinksButtonType> links = Arrays
.asList(new CommandLinksButtonType(
"Add a network that is in the range of this computer",
"This shows you a list of networks that are currently available and lets you connect to one.", false),
new CommandLinksButtonType(
"Manually create a network profile",
"This creates a new network profile or locates an existing one and saves it on your computer",
true /*default*/),
new CommandLinksButtonType("Create an ad hoc network",
"This creates a temporary network for sharing files or and Internet connection", false));
CommandLinksDialog dlg = new CommandLinksDialog(links);
dlg.setTitle("Manually connect to wireless network");
String optionalMasthead = "Manually connect to wireless network";
dlg.getDialogPane().setContentText("How do you want to add a network?");
configureSampleDialog(dlg, optionalMasthead);
showDialog(dlg);
});
final Button Hyperlink12a = new Button("Font Selector");
Hyperlink12a.setOnAction(e -> {
FontSelectorDialog dlg = new FontSelectorDialog(null);
configureSampleDialog(dlg, "Please select a font!");
showDialog(dlg);
});
final Button Hyperlink12b = new Button("Progress");
Hyperlink12b.setOnAction((ActionEvent e) -> {
Task<Object> worker = new Task<Object>() {
@Override
protected Object call() throws Exception {
for (int i = 0; i <= 100; i++) {
updateProgress(i, 99);
updateMessage("progress: " + i);
System.out.println("progress: " + i);
Thread.sleep(100);
}
return null;
}
};
ProgressDialog dlg = new ProgressDialog(worker);
configureSampleDialog(dlg, "");
Thread th = new Thread(worker);
th.setDaemon(true);
th.start();
});
final Button Hyperlink12c = new Button("Login");
Hyperlink12c.setOnAction((ActionEvent e) -> {
LoginDialog dlg = new LoginDialog(null, null);
configureSampleDialog(dlg, "");
showDialog(dlg);
});
final Button Hyperlink12d = new Button("Exception");
Hyperlink12d.setOnAction((ActionEvent e) -> {
ExceptionDialog dlg = new ExceptionDialog(new Exception("ControlsFX is _too_ awesome!"));
configureSampleDialog(dlg, "");
showDialog(dlg);
});
grid.add(new HBox(10, Hyperlink12, Hyperlink12a, Hyperlink12b, Hyperlink12c, Hyperlink12d), 1, row);
row++;
// *******************************************************************
// wizards
// *******************************************************************
grid.add(createLabel("Wizard: "), 0, row);
final Button Hyperlink15a = new Button("Linear Wizard");
Hyperlink15a.setOnAction(e -> showLinearWizard());
final Button Hyperlink15b = new Button("Branching Wizard");
Hyperlink15b.setOnAction(e -> showBranchingWizard());
final Button Hyperlink15c = new Button("Validated Linear Wizard");
Hyperlink15c.setOnAction(e -> showValidatedLinearWizard());
grid.add(new HBox(10, Hyperlink15a, Hyperlink15b, Hyperlink15c), 1, row++);
return grid;
}
private Alert createAlert(AlertType type) {
Window owner = cbSetOwner.isSelected() ? stage : null;
Alert dlg = new Alert(type, "");
dlg.initModality(modalityCombobox.getValue());
dlg.initOwner(owner);
return dlg;
}
private void configureSampleDialog(Dialog<?> dlg, String header) {
Window owner = cbSetOwner.isSelected() ? stage : null;
if (header != null && cbShowMasthead.isSelected()) {
dlg.getDialogPane().setHeaderText(header);
}
if (cbCustomGraphic.isSelected()) {
dlg.getDialogPane().setGraphic(new ImageView(new Image(getClass().getResource("/org/controlsfx/samples/controlsfx-logo.png").toExternalForm())));
}
dlg.initStyle(styleCombobox.getValue());
dlg.initOwner(owner);
}
private void showDialog(Dialog<?> dlg) {
Window owner = cbSetOwner.isSelected() ? stage : null;
if (cbCloseDialogAutomatically.isSelected()) {
new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Attempting to close dialog now...");
Platform.runLater(() -> dlg.close());
}).start();
}
dlg.initOwner(owner);
if (cbUseBlocking.isSelected()) {
dlg.showAndWait().ifPresent(result -> System.out.println("Result is " + result));
} else {
dlg.show();
dlg.resultProperty().addListener(o -> System.out.println("Result is: " + dlg.getResult()));
System.out.println("This println is _after_ the show method - we're non-blocking!");
}
}
@Override public Node getControlPanel() {
GridPane grid = new GridPane();
grid.setVgap(10);
grid.setHgap(10);
grid.setPadding(new Insets(30, 30, 0, 30));
int row = 0;
// stage style
grid.add(createLabel("Style: ", "property"), 0, row);
styleCombobox.getItems().setAll(StageStyle.values());
styleCombobox.setValue(styleCombobox.getItems().get(0));
grid.add(styleCombobox, 1, row);
row++;
// modality
grid.add(createLabel("Modality: ", "property"), 0, row);
modalityCombobox.getItems().setAll(Modality.values());
modalityCombobox.setValue(modalityCombobox.getItems().get(Modality.values().length-1));
grid.add(modalityCombobox, 1, row);
row++;
// use blocking
cbUseBlocking.setSelected(true);
grid.add(createLabel("Use blocking: ", "property"), 0, row);
grid.add(cbUseBlocking, 1, row);
row++;
// close dialog automatically
grid.add(createLabel("Close dialog after 2000ms: ", "property"), 0, row);
grid.add(cbCloseDialogAutomatically, 1, row);
row++;
// show header
grid.add(createLabel("Show custom header text: ", "property"), 0, row);
grid.add(cbShowMasthead, 1, row);
row++;
// set owner
grid.add(createLabel("Set dialog owner: ", "property"), 0, row);
grid.add(cbSetOwner, 1, row);
row++;
// custom graphic
grid.add(createLabel("Use custom graphic: ", "property"), 0, row);
grid.add(cbCustomGraphic, 1, row);
row++;
return grid;
}
// private CommandLinksButtonType buildCommandLink( String text, String comment, boolean isDefault ) {
// return new CommandLinksButtonType(text, comment, isDefault);
// }
public static void main(String[] args) {
Application.launch(args);
}
private Node createLabel(String text, String... styleclass) {
Label label = new Label(text);
if (styleclass == null || styleclass.length == 0) {
label.setFont(Font.font(13));
} else {
label.getStyleClass().addAll(styleclass);
}
return label;
}
private void showLinearWizard() {
Window owner = cbSetOwner.isSelected() ? stage : null;
// define pages to show
Wizard wizard = new Wizard(owner);
wizard.setTitle("Linear Wizard");
// --- page 1
int row = 0;
GridPane page1Grid = new GridPane();
page1Grid.setVgap(10);
page1Grid.setHgap(10);
page1Grid.add(new Label("First Name:"), 0, row);
TextField txFirstName = createTextField("firstName");
// wizard.getValidationSupport().registerValidator(txFirstName, Validator.createEmptyValidator("First Name is mandatory"));
page1Grid.add(txFirstName, 1, row++);
page1Grid.add(new Label("Last Name:"), 0, row);
TextField txLastName = createTextField("lastName");
// wizard.getValidationSupport().registerValidator(txLastName, Validator.createEmptyValidator("Last Name is mandatory"));
page1Grid.add(txLastName, 1, row);
WizardPane page1 = new WizardPane();
page1.setHeaderText("Please Enter Your Details");
page1.setContent(page1Grid);
// --- page 2
final WizardPane page2 = new WizardPane() {
@Override public void onEnteringPage(Wizard wizard) {
String firstName = (String) wizard.getSettings().get("firstName");
String lastName = (String) wizard.getSettings().get("lastName");
setContentText("Welcome, " + firstName + " " + lastName + "! Let's add some newlines!\n\n\n\n\n\n\nHello World!");
}
};
page2.setHeaderText("Thanks For Your Details!");
// --- page 3
WizardPane page3 = new WizardPane();
page3.setHeaderText("Goodbye!");
page3.setContentText("Page 3, with extra 'help' button!");
ButtonType helpDialogButton = new ButtonType("Help", ButtonData.HELP_2);
page3.getButtonTypes().add(helpDialogButton);
Button helpButton = (Button) page3.lookupButton(helpDialogButton);
helpButton.addEventFilter(ActionEvent.ACTION, actionEvent -> {
actionEvent.consume(); // stop hello.dialog from closing
System.out.println("Help clicked!");
});
// create wizard
wizard.setFlow(new LinearFlow(page1, page2, page3));
System.out.println("page1: " + page1);
System.out.println("page2: " + page2);
System.out.println("page3: " + page3);
// show wizard and wait for response
wizard.showAndWait().ifPresent(result -> {
if (result == ButtonType.FINISH) {
System.out.println("Wizard finished, settings: " + wizard.getSettings());
}
});
}
private void showBranchingWizard() {
Window owner = cbSetOwner.isSelected() ? stage : null;
// define pages to show.
// Because page1 references page2, we need to declare page2 first.
final WizardPane page2 = new WizardPane();
page2.setContentText("Page 2");
final CheckBox checkBox = new CheckBox("Skip the second page");
checkBox.setId("skip-page-2");
VBox vbox = new VBox(10, new Label("Page 1"), checkBox);
final WizardPane page1 = new WizardPane() {
// when we exit page 1, we will check the state of the 'skip page 2'
// checkbox, and if it is true, we will remove page 2 from the pages list
@Override public void onExitingPage(Wizard wizard) {
// List<WizardPage> pages = wizard.getPages();
// if (checkBox.isSelected()) {
// pages.remove(page2);
// } else {
// if (! pages.contains(page2)) {
// pages.add(1, page2);
// }
// }
}
};
page1.setContent(vbox);
final WizardPane page3 = new WizardPane();
page3.setContentText("Page 3");
// create wizard
Wizard wizard = new Wizard(owner);
wizard.setTitle("Branching Wizard");
Wizard.Flow branchingFlow = new Wizard.Flow() {
@Override
public Optional<WizardPane> advance(WizardPane currentPage) {
return Optional.of(getNext(currentPage));
}
@Override
public boolean canAdvance(WizardPane currentPage) {
return currentPage != page3;
}
private WizardPane getNext(WizardPane currentPage) {
if ( currentPage == null ) {
return page1;
} else if ( currentPage == page1) {
return checkBox.isSelected()? page3: page2;
} else {
return page3;
}
}
};
//wizard.setFlow( new LinearWizardFlow( page1, page2, page3));
wizard.setFlow( branchingFlow);
// show wizard
wizard.showAndWait().ifPresent(result -> {
if (result == ButtonType.FINISH) {
System.out.println("Wizard finished, settings: " + wizard.getSettings());
}
});
}
private void showValidatedLinearWizard() {
Window owner = cbSetOwner.isSelected() ? stage : null;
Wizard wizard = new Wizard(owner);
wizard.setTitle("Validated Linear Wizard");
// Page 1
WizardPane page1 = new WizardPane() {
ValidationSupport vs = new ValidationSupport();
{
vs.initInitialDecoration();
int row = 0;
GridPane page1Grid = new GridPane();
page1Grid.setVgap(10);
page1Grid.setHgap(10);
page1Grid.add(new Label("Username:"), 0, row);
TextField txUsername = createTextField("username");
vs.registerValidator(txUsername, Validator.createEmptyValidator("EMPTY!"));
page1Grid.add(txUsername, 1, row++);
page1Grid.add(new Label("Full Name:"), 0, row);
TextField txFullName = createTextField("fullName");
page1Grid.add(txFullName, 1, row);
setContent(page1Grid);
}
@Override
public void onEnteringPage(Wizard wizard) {
wizard.invalidProperty().unbind();
wizard.invalidProperty().bind(vs.invalidProperty());
}
};
// Page 2
WizardPane page2 = new WizardPane() {
ValidationSupport vs = new ValidationSupport();
{
vs.initInitialDecoration();
int row = 0;
GridPane page2Grid = new GridPane();
page2Grid.setVgap(10);
page2Grid.setHgap(10);
page2Grid.add(new Label("ControlsFX is:"), 0, row);
ComboBox<String> cbControlsFX = createComboBox("controlsfx");
cbControlsFX.setItems(FXCollections.observableArrayList("Cool", "Great"));
vs.registerValidator(cbControlsFX, Validator.createEmptyValidator("EMPTY!"));
page2Grid.add(cbControlsFX, 1, row++);
page2Grid.add(new Label("Where have you heard of it?:"), 0, row);
TextField txWhere = createTextField("where");
vs.registerValidator(txWhere, Validator.createEmptyValidator("EMPTY!"));
page2Grid.add(txWhere, 1, row++);
page2Grid.add(new Label("Free text:"), 0, row);
TextField txFreeText = createTextField("freetext");
page2Grid.add(txFreeText, 1, row);
setContent(page2Grid);
}
@Override
public void onEnteringPage(Wizard wizard) {
wizard.invalidProperty().unbind();
wizard.invalidProperty().bind(vs.invalidProperty());
}
};
// create wizard
wizard.setFlow(new LinearFlow(page1, page2));
// show wizard and wait for response
wizard.showAndWait().ifPresent(result -> {
if (result == ButtonType.FINISH) {
System.out.println("Wizard finished, settings: " + wizard.getSettings());
}
});
}
private TextField createTextField(String id) {
TextField textField = new TextField();
textField.setId(id);
GridPane.setHgrow(textField, Priority.ALWAYS);
return textField;
}
private ComboBox<String> createComboBox(String id) {
ComboBox<String> comboBox = new ComboBox<>();
comboBox.setId(id);
GridPane.setHgrow(comboBox, Priority.ALWAYS);
return comboBox;
}
}

View File

@ -0,0 +1,170 @@
/**
* Copyright (c) 2013, 2014 ControlsFX
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of ControlsFX, any associated website, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.controlsfx.samples;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.ToolBar;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import org.controlsfx.ControlsFXSample;
import org.controlsfx.control.GridCell;
import org.controlsfx.control.GridView;
import org.controlsfx.glyphfont.FontAwesome;
import org.controlsfx.glyphfont.Glyph;
import org.controlsfx.glyphfont.GlyphFont;
import org.controlsfx.glyphfont.GlyphFontRegistry;
public class HelloGlyphFont extends ControlsFXSample {
static {
// Register a custom default font
GlyphFontRegistry.register("icomoon", HelloGlyphFont.class.getResourceAsStream("icomoon.ttf") , 16);
}
private GlyphFont fontAwesome = GlyphFontRegistry.font("FontAwesome");
private GlyphFont icoMoon = GlyphFontRegistry.font("icomoon");
// private static char FAW_TRASH = '\uf014';
private static char FAW_GEAR = '\uf013';
// private static char FAW_STAR = '\uf005';
private static char IM_BOLD = '\ue027';
private static char IM_UNDERSCORED = '\ue02b';
private static char IM_ITALIC = '\ue13e';
@Override
public String getSampleName() {
return "Glyph Font";
}
@Override
public String getJavaDocURL() {
return Utils.JAVADOC_BASE + "org/controlsfx/glyphfont/GlyphFont.html";
}
@Override
public Node getPanel(final Stage stage) {
VBox root = new VBox(10);
root.setPadding(new Insets(10, 10, 10, 10));
root.setMaxHeight(Double.MAX_VALUE);
Label title = new Label("Using FontAwesome(CDN)");
root.getChildren().add(title);
ToolBar toolbar = new ToolBar(
// There are many ways how you can define a Glyph:
new Button("", new Glyph("FontAwesome", "TRASH_ALT")), // Use the Glyph-class with a icon name
new Button("", new Glyph("FontAwesome", FontAwesome.Glyph.STAR)), // Use the Glyph-class with a known enum value
new Button("", Glyph.create("FontAwesome|BUG")), // Use the static Glyph-class create protocol
new Button("", fontAwesome.create("REBEL")), // Use the font-instance with a name
new Button("", fontAwesome.create(FontAwesome.Glyph.SMILE_ALT)), // Use the font-instance with a enum
new Button("", fontAwesome.create(FAW_GEAR).color(Color.RED)) // Use the font-instance with a unicode char
);
root.getChildren().add(toolbar);
title = new Label("Using IcoMoon (Local)");
root.getChildren().add(title);
Glyph effectGlyph = icoMoon.create(IM_UNDERSCORED)
.color(Color.BLUE)
.size(48)
.useHoverEffect();
Glyph effectGlyph2 = icoMoon.create(IM_UNDERSCORED)
.color(Color.BLUE)
.size(48)
.useGradientEffect().useHoverEffect();
toolbar = new ToolBar(
// Since we have a custom font without named characters,
// we have to use unicode character codes for the icons:
new Button("", icoMoon.create(IM_BOLD).size(16)),
new Button("", icoMoon.create(IM_UNDERSCORED).color(Color.GREEN).size(32)),
new Button("", icoMoon.create(IM_ITALIC).size(48)),
new Button("", effectGlyph),
new Button("", effectGlyph2));
root.getChildren().add(toolbar);
GridPane fontDemo = new GridPane();
fontDemo.setHgap(5);
fontDemo.setVgap(5);
int maxColumns = 10;
int col = 0;
int row = 0;
for ( FontAwesome.Glyph glyph: FontAwesome.Glyph.values() ){
Color randomColor = new Color( Math.random(), Math.random(), Math.random(), 1);
Glyph graphic = Glyph.create( "FontAwesome|" + glyph.name()).sizeFactor(2).color(randomColor).useGradientEffect();
Button button = new Button(glyph.name(), graphic);
button.setContentDisplay(ContentDisplay.TOP);
button.setMaxWidth(Double.MAX_VALUE);
col = col % maxColumns + 1;
if ( col == 1 ) row++;
fontDemo.add( button, col, row);
GridPane.setFillHeight(button, true);
GridPane.setFillWidth(button, true);
}
ScrollPane scroller = new ScrollPane(fontDemo);
scroller.setFitToWidth(true);
TabPane tabs = new TabPane();
Tab tab = new Tab("FontAwesome Glyph Demo");
tab.setContent(scroller);
tabs.getTabs().add(tab);
root.getChildren().add(tabs);
VBox.setVgrow(tabs, Priority.ALWAYS);
return root;
}
public static void main(String[] args) {
launch(args);
}
}

View File

@ -0,0 +1,124 @@
/**
* Copyright (c) 2014, ControlsFX
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of ControlsFX, any associated website, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.controlsfx.samples;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.controlsfx.ControlsFXSample;
import org.controlsfx.control.HiddenSidesPane;
public class HelloHiddenSidesPane extends ControlsFXSample {
@Override
public String getSampleName() {
return "Hidden Sides Pane";
}
@Override
public String getJavaDocURL() {
return Utils.JAVADOC_BASE
+ "org/controlsfx/control/HiddenSidesPane.html";
}
@Override
public String getSampleDescription() {
return "Hidden nodes will appear when moving the mouse cursor close to the edge of the content node. "
+ "They disappear again when the mouse cursor exits them. In this example a hidden node "
+ "can be pinned (and unpinned) by clicking on it so that it stays visible all the time.";
}
@Override
public Node getPanel(Stage stage) {
StackPane stackPane = new StackPane();
stackPane.setStyle("-fx-padding: 30");
HiddenSidesPane pane = new HiddenSidesPane();
Label content = new Label("Content Node");
content.setStyle("-fx-background-color: white; -fx-border-color: black;");
content.setAlignment(Pos.CENTER);
content.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
pane.setContent(content);
SideNode top = new SideNode("Top", Side.TOP, pane);
top.setStyle("-fx-background-color: rgba(0,255,0,.25);");
pane.setTop(top);
SideNode right = new SideNode("Right", Side.RIGHT, pane);
right.setStyle("-fx-background-color: rgba(0,0, 255,.25);");
pane.setRight(right);
SideNode bottom = new SideNode("Bottom", Side.BOTTOM, pane);
bottom.setStyle("-fx-background-color: rgba(255,255,0,.25);");
pane.setBottom(bottom);
SideNode left = new SideNode("Left", Side.LEFT, pane);
left.setStyle("-fx-background-color: rgba(255,0,0,.25);");
pane.setLeft(left);
stackPane.getChildren().add(pane);
return stackPane;
}
class SideNode extends Label {
public SideNode(final String text, final Side side,
final HiddenSidesPane pane) {
super(text + " (Click to pin / unpin)");
setAlignment(Pos.CENTER);
setPrefSize(200, 200);
setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if (pane.getPinnedSide() != null) {
setText(text + " (unpinned)");
pane.setPinnedSide(null);
} else {
setText(text + " (pinned)");
pane.setPinnedSide(side);
}
}
});
}
}
public static void main(String[] args) {
launch(args);
}
}

View File

@ -0,0 +1,196 @@
/**
* Copyright (c) 2018 ControlsFX
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of ControlsFX, any associated website, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.controlsfx.samples;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
import org.controlsfx.ControlsFXSample;
import org.controlsfx.control.ListActionView;
import org.controlsfx.control.action.Action;
import org.controlsfx.control.action.ActionUtils;
import org.controlsfx.glyphfont.FontAwesome;
import java.util.List;
public class HelloListActionView extends ControlsFXSample {
private ListActionView<String> view;
@Override
public String getSampleName() {
return "List Action View";
}
@Override
public Node getPanel(Stage stage) {
view = new ListActionView<>();
view.getItems().addAll("Dirk", "Jonathan", "Eugene","Abhinay", "Samir");
view.getActions().addAll(createActions());
GridPane pane = new GridPane();
pane.add(view, 0, 0);
pane.setAlignment(Pos.CENTER);
return pane;
}
@Override
public Node getControlPanel() {
VBox root = new VBox(20);
root.setPadding(new Insets(30, 30, 30, 30));
CheckBox useCellFactory = new CheckBox("Use cell factory");
useCellFactory.setOnAction(evt -> {
if (useCellFactory.isSelected()) {
view.setCellFactory(listView -> {
ListCell<String> cell = new ListCell<String>() {
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setText(item == null ? "null" : item);
setGraphic(null);
}
}
};
cell.setFont(Font.font("Arial", FontWeight.BOLD,
FontPosture.ITALIC, 18));
return cell;
});
} else {
view.setCellFactory(null);
}
});
CheckBox clearActions = new CheckBox("Clear Actions");
clearActions.selectedProperty().addListener((observable, oldValue, newValue) -> {
if (newValue) {
view.getActions().clear();
} else {
view.getActions().addAll(createActions());
}
});
ComboBox<Side> sideComboBox = new ComboBox<>(FXCollections.observableArrayList(Side.values()));
sideComboBox.setTooltip(new Tooltip("The side of the ListView on which the actions should be displayed"));
sideComboBox.getSelectionModel().select(Side.LEFT);
view.sideProperty().bind(sideComboBox.getSelectionModel().selectedItemProperty());
root.getChildren().addAll(useCellFactory, clearActions, sideComboBox);
return root;
}
@Override
public String getSampleDescription() {
return "A control used to let the user select multiple values from a "
+ "list of available values. Selected values are moved into a "
+ "second list that is showing the current selection. Items can "
+ "be moved by double clicking on them or by first selecting "
+ "them and then pressing one of the buttons in the center.";
}
@Override
public String getJavaDocURL() {
return Utils.JAVADOC_BASE
+ "org/controlsfx/control/ListActionView.html";
}
public static void main(String[] args) {
launch(args);
}
private ObservableList<Action> createActions() {
ListActionView.ListAction<String> moveUp = new ListActionView.ListAction<String>(
new FontAwesome().create(FontAwesome.Glyph.ANGLE_UP)) {
@Override
public void initialize(ListView<String> listView) {
setEventHandler(e -> moveSelectedItemsUp(listView));
}
};
ListActionView.ListAction<String> moveDown = new ListActionView.ListAction<String>(new FontAwesome().create(FontAwesome.Glyph.ANGLE_DOWN)) {
@Override
public void initialize(ListView<String> listView) {
setEventHandler(e -> moveSelectedItemsDown(listView));
}
};
return FXCollections.observableArrayList(moveUp, ActionUtils.ACTION_SEPARATOR, moveDown);
}
private void moveSelectedItemsUp(ListView<String> listView) {
final MultipleSelectionModel<String> selectionModel = listView.getSelectionModel();
final ObservableList<Integer> selectedIndices = selectionModel.getSelectedIndices();
final ObservableList<String> items = listView.getItems();
for (final Integer selectedIndex : selectedIndices) {
if (selectedIndex > 0) {
if (selectedIndices.contains(selectedIndex - 1)) continue;
final String item = items.get(selectedIndex);
final String itemToBeReplaced = items.get(selectedIndex - 1);
items.set(selectedIndex - 1, item);
items.set(selectedIndex, itemToBeReplaced);
selectionModel.clearSelection(selectedIndex);
selectionModel.select(selectedIndex - 1);
}
}
}
private void moveSelectedItemsDown(ListView<String> listView) {
final ObservableList<String> items = listView.getItems();
final MultipleSelectionModel<String> selectionModel = listView.getSelectionModel();
final List<Integer> selectedIndices = selectionModel.getSelectedIndices();
int lastIndex = items.size() - 1;
// Last selected item is to be replaced first
for (int index = selectedIndices.size() - 1; index >= 0; index--) {
final Integer selectedIndex = selectedIndices.get(index);
if (selectedIndex < lastIndex) {
if (selectedIndices.contains(selectedIndex + 1)) continue;
final String item = items.get(selectedIndex);
final String itemToBeReplaced = items.get(selectedIndex + 1);
items.set(selectedIndex + 1, item);
items.set(selectedIndex, itemToBeReplaced);
selectionModel.clearSelection(selectedIndex);
selectionModel.select(selectedIndex + 1);
}
}
}
}

View File

@ -0,0 +1,156 @@
/**
* Copyright (c) 2014 - 2016 ControlsFX
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of ControlsFX, any associated website, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.controlsfx.samples;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import org.controlsfx.ControlsFXSample;
import org.controlsfx.control.MasterDetailPane;
public class HelloMasterDetailPane extends ControlsFXSample {
@Override
public String getJavaDocURL() {
return Utils.JAVADOC_BASE
+ "org/controlsfx/control/MasterDetailPane.html";
}
@Override
public String getControlStylesheetURL() {
return "/org/controlsfx/control/masterdetailpane.css";
}
private MasterDetailPane masterDetailPane;
@Override
public Node getPanel(Stage stage) {
masterDetailPane = new MasterDetailPane(Side.BOTTOM);
masterDetailPane.setShowDetailNode(true);
return masterDetailPane;
}
@Override
public Node getControlPanel() {
GridPane grid = new GridPane();
grid.setVgap(10);
grid.setHgap(10);
grid.setPadding(new Insets(30, 30, 0, 30));
int row = 0;
// show details
Label lblShowDetail = new Label("Show details: ");
lblShowDetail.getStyleClass().add("property");
grid.add(lblShowDetail, 0, row);
CheckBox chkShowDetails = new CheckBox();
grid.add(chkShowDetails, 1, row++);
chkShowDetails.selectedProperty().bindBidirectional(masterDetailPane.showDetailNodeProperty());
// animated
Label lblAnimated = new Label("Animated: ");
lblAnimated.getStyleClass().add("property");
grid.add(lblAnimated, 0, row);
CheckBox chkAnimated = new CheckBox();
grid.add(chkAnimated, 1, row++);
chkAnimated.selectedProperty().bindBidirectional(masterDetailPane.animatedProperty());
// side
Label lblSide = new Label("Side: ");
lblSide.getStyleClass().add("property");
grid.add(lblSide, 0, row);
ComboBox<Side> positionBox = new ComboBox<>();
positionBox.getItems().addAll(Side.values());
grid.add(positionBox, 1, row++);
positionBox.setValue(masterDetailPane.getDetailSide());
masterDetailPane.detailSideProperty().bind(positionBox.valueProperty());
// detail node
Label lblDetailNode = new Label("Detail Node: ");
lblDetailNode.getStyleClass().add("property");
grid.add(lblDetailNode, 0, row);
ComboBox<DetailType> detailNodeBox = new ComboBox<>();
detailNodeBox.getItems().addAll(DetailType.values());
grid.add(detailNodeBox, 1, row++);
detailNodeBox.valueProperty().addListener(it -> {
switch (detailNodeBox.getValue()) {
case LABEL:
final Label label = new Label("Detail");
label.setMaxWidth(Double.MAX_VALUE);
label.setAlignment(Pos.CENTER);
label.setStyle("-fx-background-color: lightcoral;");
masterDetailPane.setDetailNode(label);
break;
case NONE:
masterDetailPane.setDetailNode(null);
break;
case LIST_VIEW:
final ListView<String> listView = new ListView<>();
listView.getItems().addAll("Katja", "Dirk", "Philip", "Jule", "Armin");
listView.setPrefSize(150,150);
masterDetailPane.setDetailNode(listView);
break;
}
});
// reset position
Button resetButton = new Button("Reset Divider");
resetButton.setOnAction(evt -> masterDetailPane.resetDividerPosition());
grid.add(resetButton, 1, row++);
return grid;
}
public enum DetailType {
NONE,
LABEL,
LIST_VIEW
}
@Override
public String getSampleDescription() {
return "A control used to display a master node and a detail node. The detail can be shown / hidden at the top, the bottom, to the left or to the right.";
}
public static void main(String[] args) {
Application.launch(args);
}
@Override
public String getSampleName() {
return "Master Detail Pane";
}
}

View File

@ -0,0 +1,258 @@
/**
* Copyright (c) 2014, 2015 ControlsFX
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of ControlsFX, any associated website, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.controlsfx.samples;
import java.time.LocalDate;
import java.time.Month;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.controlsfx.ControlsFXSample;
import org.controlsfx.control.PropertySheet;
import org.controlsfx.control.PropertySheet.Item;
import org.controlsfx.control.PropertySheet.Mode;
import org.controlsfx.control.SegmentedButton;
import org.controlsfx.control.action.Action;
import org.controlsfx.control.action.ActionUtils;
import org.controlsfx.property.BeanProperty;
import org.controlsfx.property.BeanPropertyUtils;
import org.controlsfx.samples.propertysheet.CustomPropertyDescriptor;
import org.controlsfx.samples.propertysheet.SampleBean;
public class HelloPropertySheet extends ControlsFXSample {
private static Map<String, Object> customDataMap = new LinkedHashMap<>();
static {
customDataMap.put("1. Name#First Name", "Jonathan");
customDataMap.put("1. Name#Last Name", "Giles");
customDataMap.put("1. Name#Birthday", LocalDate.of(1985, Month.JANUARY, 12));
customDataMap.put("2. Billing Address#Address 1", "");
customDataMap.put("2. Billing Address#Address 2", "");
customDataMap.put("2. Billing Address#City", "");
customDataMap.put("2. Billing Address#State", "");
customDataMap.put("2. Billing Address#Zip", "");
customDataMap.put("3. Phone#Home", "123-123-1234");
customDataMap.put("3. Phone#Mobile", "234-234-2345");
customDataMap.put("3. Phone#Work", "");
}
private PropertySheet propertySheet = new PropertySheet();
public static void main(String[] args) {
launch();
}
@Override
public String getSampleName() {
return "Property Sheet";
}
@Override
public String getSampleDescription() {
return "The PropertySheet control is useful when you want to present a number"
+ " of properties to a user for them to edit.";
}
@Override
public String getJavaDocURL() {
return Utils.JAVADOC_BASE + "org/controlsfx/control/PropertySheet.html";
}
@Override
public String getControlStylesheetURL() {
return "/org/controlsfx/control/propertysheet.css";
}
class CustomPropertyItem implements Item {
private String key;
private String category, name;
public CustomPropertyItem(String key) {
this.key = key;
String[] skey = key.split("#");
category = skey[0];
name = skey[1];
}
@Override
public Class<?> getType() {
return customDataMap.get(key).getClass();
}
@Override
public String getCategory() {
return category;
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return null;
}
@Override
public Object getValue() {
return customDataMap.get(key);
}
@Override
public void setValue(Object value) {
customDataMap.put(key, value);
}
@Override
public Optional<ObservableValue<? extends Object>> getObservableValue() {
return Optional.empty();
}
}
class ActionShowInPropertySheet extends Action {
private Object bean;
public ActionShowInPropertySheet(String title, Object bean) {
super(title);
setEventHandler(this::handleAction);
this.bean = bean;
}
private ObservableList<Item> getCustomModelProperties() {
ObservableList<Item> list = FXCollections.observableArrayList();
for (String key : customDataMap.keySet()) {
list.add(new CustomPropertyItem(key));
}
return list;
}
private void handleAction(ActionEvent ae) {
// retrieving bean properties may take some time
// so we have to put it on separate thread to keep UI responsive
Service<?> service = new Service<ObservableList<Item>>() {
@Override
protected Task<ObservableList<Item>> createTask() {
return new Task<ObservableList<Item>>() {
@Override
protected ObservableList<Item> call() throws Exception {
return bean == null ? getCustomModelProperties() : BeanPropertyUtils.getProperties(bean);
}
};
}
};
service.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@SuppressWarnings("unchecked")
@Override
public void handle(WorkerStateEvent e) {
if (bean instanceof SampleBean) {
for (Item i : (ObservableList<Item>) e.getSource().getValue()) {
if (i instanceof BeanProperty && ((BeanProperty) i).getPropertyDescriptor() instanceof CustomPropertyDescriptor) {
BeanProperty bi = (BeanProperty) i;
bi.setEditable(((CustomPropertyDescriptor) bi.getPropertyDescriptor()).isEditable());
}
}
}
propertySheet.getItems().setAll((ObservableList<Item>) e.getSource().getValue());
}
});
service.start();
}
}
@Override
public Node getPanel(Stage stage) {
return propertySheet;
}
@Override
public Node getControlPanel() {
VBox infoPane = new VBox(10);
Button button = new Button("Title");
TextField textField = new TextField();
SampleBean sampleBean = new SampleBean();
SegmentedButton segmentedButton = ActionUtils.createSegmentedButton(
new ActionShowInPropertySheet("Bean: Button", button),
new ActionShowInPropertySheet("Bean: TextField", textField),
new ActionShowInPropertySheet("Custom Model", null),
new ActionShowInPropertySheet("Custom BeanInfo", sampleBean)
);
segmentedButton.getStyleClass().add(SegmentedButton.STYLE_CLASS_DARK);
segmentedButton.getButtons().get(0).fire();
CheckBox toolbarModeVisible = new CheckBox("Show Mode Buttons");
toolbarModeVisible.selectedProperty().bindBidirectional(propertySheet.modeSwitcherVisibleProperty());
CheckBox toolbarSearchVisible = new CheckBox("Show Search Field");
toolbarSearchVisible.selectedProperty().bindBidirectional(propertySheet.searchBoxVisibleProperty());
infoPane.getChildren().add(toolbarModeVisible);
infoPane.getChildren().add(toolbarSearchVisible);
infoPane.getChildren().add(segmentedButton);
infoPane.getChildren().add(button);
infoPane.getChildren().add(textField);
return infoPane;
}
class ActionModeChange extends Action {
public ActionModeChange(String title, Mode mode) {
super(title);
setEventHandler(ae -> propertySheet.modeProperty().set(mode));
}
}
}

View File

@ -0,0 +1,234 @@
/**
* Copyright (c) 2014, 2019 ControlsFX
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of ControlsFX, any associated website, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.controlsfx.samples;
import java.time.LocalDate;
import java.util.Arrays;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Control;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Callback;
import org.controlsfx.ControlsFXSample;
import org.controlsfx.validation.ValidationResult;
import org.controlsfx.validation.ValidationSupport;
import org.controlsfx.validation.Validator;
import org.controlsfx.validation.decoration.CompoundValidationDecoration;
import org.controlsfx.validation.decoration.GraphicValidationDecoration;
import org.controlsfx.validation.decoration.StyleClassValidationDecoration;
import org.controlsfx.validation.decoration.ValidationDecoration;
public class HelloValidation extends ControlsFXSample {
private TextField textField = new TextField();
private ValidationSupport validationSupport = new ValidationSupport();
@Override public String getSampleName() {
return "Component Validation";
}
@Override public String getJavaDocURL() {
return Utils.JAVADOC_BASE + "org/controlsfx/validation/ValidationSupport.html";
}
@Override public String getSampleDescription() {
return "Component Validation";
}
@Override public Node getPanel(final Stage stage) {
GridPane root = new GridPane();
root.setVgap(10);
root.setHgap(10);
root.setPadding(new Insets(30, 30, 0, 30));
root.sceneProperty().addListener(new InvalidationListener() {
@Override public void invalidated(Observable o) {
if (root.getScene() != null) {
root.getScene().getStylesheets().add(HelloDecorator.class.getResource("validation.css").toExternalForm());
}
}
});
int row = 0;
// text field
validationSupport.registerValidator(textField, Validator.createEmptyValidator("Text is required"));
root.add(new Label("TextField"), 0, row);
root.add(textField, 1, row);
GridPane.setHgrow(textField, Priority.ALWAYS);
//combobox
row++;
ComboBox<String> combobox = new ComboBox<String>();
combobox.getItems().addAll("Item A", "Item B", "Item C");
validationSupport.registerValidator(combobox, (Control c, String newValue) ->
ValidationResult.fromErrorIf(combobox, "ComboBox Selection required", newValue == null || newValue.isEmpty())
.addInfoIf(combobox, "Item A is the first item", "Item A".equals(newValue))
.addInfoIf(combobox, "Item B is the second item", "Item B".equals(newValue))
.addInfoIf(combobox, "Item C is the third item", "Item C".equals(newValue))
);
root.add(new Label("ComboBox"), 0, row);
root.add(combobox, 1, row);
GridPane.setHgrow(combobox, Priority.ALWAYS);
//choicebox
row++;
ChoiceBox<String> choiceBox = new ChoiceBox<String>();
choiceBox.getItems().addAll("Item A", "Item B", "Item C");
validationSupport.registerValidator(choiceBox, Validator.createEmptyValidator("ChoiceBox Selection required"));
root.add(new Label("ChoiceBox"), 0, row);
root.add(choiceBox, 1, row);
GridPane.setHgrow(choiceBox, Priority.ALWAYS);
//checkbox
row++;
CheckBox checkBox = new CheckBox();
validationSupport.registerValidator(checkBox, (Control c, Boolean newValue) ->
ValidationResult.fromErrorIf(c, "Checkbox should be checked", !newValue));
root.add(new Label("CheckBox"), 0, row);
root.add(checkBox, 1, row);
GridPane.setHgrow(checkBox, Priority.ALWAYS);
//slider
row++;
Slider slider = new Slider(-50d, 50d, -10d);
slider.setShowTickLabels(true);
validationSupport.registerValidator(slider, (Control c, Double newValue) ->
ValidationResult.fromErrorIf(slider, "Slider value should be > 0", newValue <= 0));
root.add(new Label("Slider"), 0, row);
root.add(slider, 1, row);
GridPane.setHgrow(slider, Priority.ALWAYS);
// color picker
row++;
ColorPicker colorPicker = new ColorPicker(Color.RED);
validationSupport.registerValidator(colorPicker,
Validator.createEqualsValidator("Color should be WHITE or BLACK", Arrays.asList(Color.WHITE, Color.BLACK)));
root.add(new Label("Color Picker"), 0, row);
root.add(colorPicker, 1, row);
GridPane.setHgrow(colorPicker, Priority.ALWAYS);
// date picker
row++;
DatePicker datePicker = new DatePicker();
validationSupport.registerValidator(datePicker, false, (Control c, LocalDate newValue) ->
ValidationResult.fromWarningIf(datePicker, "The date should be today", !LocalDate.now().equals(newValue)));
root.add(new Label("Date Picker"), 0, row);
root.add(datePicker, 1, row);
GridPane.setHgrow(datePicker, Priority.ALWAYS);
ScrollPane scrollPane = new ScrollPane(root);
return scrollPane;
}
@Override public Node getControlPanel() {
GridPane grid = new GridPane();
grid.setVgap(10);
grid.setHgap(10);
grid.setPadding(new Insets(30, 30, 0, 30));
ValidationDecoration iconDecorator = new GraphicValidationDecoration();
ValidationDecoration cssDecorator = new StyleClassValidationDecoration();
ValidationDecoration compoundDecorator = new CompoundValidationDecoration(cssDecorator, iconDecorator);
int row = 0;
// --- validation decorator
Callback<ListView<ValidationDecoration>, ListCell<ValidationDecoration>> cellFactory = listView -> new ListCell<ValidationDecoration>() {
@Override protected void updateItem(ValidationDecoration decorator, boolean empty) {
super.updateItem(decorator, empty);
if (empty) {
setText("");
} else {
if (decorator instanceof StyleClassValidationDecoration) {
setText("Style Class Validation Decorator");
} else if (decorator instanceof GraphicValidationDecoration) {
setText("Graphic Validation Decorator");
} else if (decorator instanceof CompoundValidationDecoration) {
setText("Compound Validation Decorator");
} else {
setText("Unknown decorator type!");
}
}
}
};
ComboBox<ValidationDecoration> decoratorBox = new ComboBox<>();
decoratorBox.getItems().addAll(iconDecorator, cssDecorator, compoundDecorator);
decoratorBox.setCellFactory(cellFactory);
decoratorBox.setButtonCell(cellFactory.call(null));
decoratorBox.getSelectionModel().selectedItemProperty().addListener((o,old,decorator) ->
validationSupport.setValidationDecorator(decorator));
decoratorBox.getSelectionModel().select(0);
Label validationDecoratorLabel = new Label("Validation Decorator: ");
validationDecoratorLabel.getStyleClass().add("property");
grid.add(validationDecoratorLabel, 0, row);
grid.add(decoratorBox, 1, row);
GridPane.setHgrow(decoratorBox, Priority.ALWAYS);
row++;
ToggleButton btnToggleRequired = new ToggleButton("Toggle TextField required status");
btnToggleRequired.setSelected(ValidationSupport.isRequired(textField));
btnToggleRequired.setOnAction(e -> {
ValidationSupport.setRequired(textField, btnToggleRequired.isSelected());
});
grid.add(btnToggleRequired, 1, row, 1, 1);
return grid;
}
public static void main(String[] args) {
launch(args);
}
}

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.*?>
<AnchorPane fx:id="root" prefHeight="300.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/17.0.2-ea"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="das.tools.np.gui.controllers.EditPhonesController">
<HBox alignment="CENTER" layoutX="14.0" layoutY="251.0" prefHeight="35.0" prefWidth="200.0" spacing="20.0"
AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0">
<Button fx:id="btOk" defaultButton="true" mnemonicParsing="false" prefHeight="24.0" prefWidth="88.0"
text="OK"/>
<Button fx:id="btCancel" cancelButton="true" mnemonicParsing="false" prefHeight="24.0" prefWidth="88.0"
text="Cancel"/>
</HBox>
</AnchorPane>

View File

@ -0,0 +1,742 @@
/**
* Copyright (c) 2013, 2015 ControlsFX
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of ControlsFX, any associated website, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.controlsfx.samples.dialogs;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceDialog;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextInputDialog;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.Window;
import org.controlsfx.ControlsFXSample;
import org.controlsfx.dialog.CommandLinksDialog;
import org.controlsfx.dialog.CommandLinksDialog.CommandLinksButtonType;
import org.controlsfx.dialog.WizardPane;
import org.controlsfx.dialog.ExceptionDialog;
import org.controlsfx.dialog.FontSelectorDialog;
import org.controlsfx.dialog.LoginDialog;
import org.controlsfx.dialog.ProgressDialog;
import org.controlsfx.dialog.Wizard;
import org.controlsfx.dialog.Wizard.LinearFlow;
import org.controlsfx.validation.ValidationSupport;
import org.controlsfx.validation.Validator;
public class HelloDialogs extends ControlsFXSample {
@Override
public String getSampleName() {
return "Dialogs";
}
@Override
public String getJavaDocURL() {
// return Utils.JAVADOC_BASE + "org/controlsfx/dialog/Dialogs.html";
return null;
}
@Override
public String getSampleDescription() {
return "";
}
private final ComboBox<StageStyle> styleCombobox = new ComboBox<>();
private final ComboBox<Modality> modalityCombobox = new ComboBox<>();
private final CheckBox cbUseBlocking = new CheckBox();
private final CheckBox cbCloseDialogAutomatically = new CheckBox();
private final CheckBox cbShowMasthead = new CheckBox();
private final CheckBox cbSetOwner = new CheckBox();
private final CheckBox cbCustomGraphic = new CheckBox();
private Stage stage;
@Override
public Node getPanel(Stage stage) {
this.stage = stage;
GridPane grid = new GridPane();
grid.setPadding(new Insets(10, 10, 10, 10));
grid.setHgap(10);
grid.setVgap(10);
int row = 0;
Label javafxDialogs = new Label("JavaFX Dialogs:");
javafxDialogs.setFont(Font.font(25));
grid.add(javafxDialogs, 0, row++, 2, 1);
// *******************************************************************
// Information Dialog
// *******************************************************************
grid.add(createLabel("Information Dialog: "), 0, row);
final Button Hyperlink2 = new Button("Show");
Hyperlink2.setOnAction( (ActionEvent e) -> {
Alert dlg = createAlert(AlertType.INFORMATION);
dlg.setTitle("Custom title");
String optionalMasthead = "Wouldn't this be nice?";
dlg.getDialogPane().setContentText("A collection of pre-built JavaFX dialogs?\nSeems like a great idea to me...");
configureSampleDialog(dlg, optionalMasthead);
// lets get some output when events happen
dlg.setOnShowing(evt -> System.out.println(evt));
dlg.setOnShown(evt -> System.out.println(evt));
dlg.setOnHiding(evt -> System.out.println(evt));
dlg.setOnHidden(evt -> System.out.println(evt));
// dlg.setOnCloseRequest(evt -> evt.consume());
showDialog(dlg);
});
final Button Hyperlink2a = new Button("2 x Buttons (no cancel)");
Hyperlink2a.setOnAction( (ActionEvent e) -> {
Alert dlg = createAlert(AlertType.INFORMATION);
dlg.setTitle("Custom title");
String optionalMasthead = "Wouldn't this be nice?";
dlg.getDialogPane().setContentText("A collection of pre-built JavaFX dialogs?\nSeems like a great idea to me...");
configureSampleDialog(dlg, optionalMasthead);
dlg.getButtonTypes().add(ButtonType.NEXT);
// dlg.setOnCloseRequest(evt -> evt.consume());
showDialog(dlg);
});
grid.add(new HBox(10, Hyperlink2, Hyperlink2a), 1, row);
row++;
// *******************************************************************
// Confirmation Dialog
// *******************************************************************
grid.add(createLabel("Confirmation Dialog: "), 0, row);
final CheckBox cbShowCancel = new CheckBox("Show Cancel Button");
cbShowCancel.setSelected(true);
final Button Hyperlink3 = new Button("Show");
Hyperlink3.setOnAction(e -> {
Alert dlg = createAlert(AlertType.CONFIRMATION);
dlg.setTitle("You do want dialogs right?");
String optionalMasthead = "Just Checkin'";
dlg.getDialogPane().setContentText("I was a bit worried that you might not want them, so I wanted to double check.");
if (!cbShowCancel.isSelected()) {
dlg.getDialogPane().getButtonTypes().remove(ButtonType.CANCEL);
}
configureSampleDialog(dlg, optionalMasthead);
showDialog(dlg);
});
grid.add(new HBox(10, Hyperlink3, cbShowCancel), 1, row);
row++;
// *******************************************************************
// Warning Dialog
// *******************************************************************
grid.add(createLabel("Warning Dialog: "), 0, row);
final Button Hyperlink6a = new Button("Show");
Hyperlink6a.setOnAction(e -> {
Alert dlg = createAlert(AlertType.WARNING);
dlg.setTitle("I'm warning you!");
String optionalMasthead = "This is a warning";
dlg.getDialogPane().setContentText("I'm glad I didn't need to use this...");
configureSampleDialog(dlg, optionalMasthead);
showDialog(dlg);
});
grid.add(new HBox(10, Hyperlink6a), 1, row);
row++;
// *******************************************************************
// Error Dialog
// *******************************************************************
grid.add(createLabel("Error Dialog: "), 0, row);
final Button Hyperlink7a = new Button("Show");
Hyperlink7a.setOnAction(e -> {
Alert dlg = createAlert(AlertType.ERROR);
dlg.setTitle("It looks like you're making a bad decision");
String optionalMasthead = "Exception Encountered";
dlg.getDialogPane().setContentText("Better change your mind - this is really your last chance! (Even longer text that should probably wrap)");
configureSampleDialog(dlg, optionalMasthead);
showDialog(dlg);
});
grid.add(new HBox(10, Hyperlink7a), 1, row);
row++;
// *******************************************************************
// Input Dialog (with header)
// *******************************************************************
grid.add(createLabel("Input Dialog: "), 0, row);
final Button Hyperlink8 = new Button("TextField");
Hyperlink8.setOnAction(e -> {
TextInputDialog dlg = new TextInputDialog("");
dlg.setTitle("Name Check");
String optionalMasthead = "Please type in your name";
dlg.getDialogPane().setContentText("What is your name?");
configureSampleDialog(dlg, optionalMasthead);
showDialog(dlg);
});
final Button Hyperlink9 = new Button("Initial Value Set");
Hyperlink9.setOnAction(e -> {
TextInputDialog dlg = new TextInputDialog("Jonathan");
dlg.setTitle("Name Guess");
String optionalMasthead = "Name Guess";
dlg.getDialogPane().setContentText("Pick a name?");
configureSampleDialog(dlg, optionalMasthead);
showDialog(dlg);
});
final Button Hyperlink10 = new Button("Set Choices (< 10)");
Hyperlink10.setOnAction(e -> {
ChoiceDialog<String> dlg = new ChoiceDialog<>("Jonathan",
"Matthew", "Jonathan", "Ian", "Sue", "Hannah");
dlg.setTitle("Name Guess");
String optionalMasthead = "Name Guess";
dlg.getDialogPane().setContentText("Pick a name?");
configureSampleDialog(dlg, optionalMasthead);
showDialog(dlg);
});
final Button Hyperlink11 = new Button("Set Choices (>= 10)");
Hyperlink11.setOnAction(e -> {
ChoiceDialog<String> dlg = new ChoiceDialog<>("Jonathan",
"Matthew", "Jonathan", "Ian", "Sue",
"Hannah", "Julia", "Denise", "Stephan",
"Sarah", "Ron", "Ingrid");
dlg.setTitle("Name Guess");
String optionalMasthead = "Name Guess";
dlg.getDialogPane().setContentText("Pick a name?");
configureSampleDialog(dlg, optionalMasthead);
showDialog(dlg);
});
grid.add(new HBox(10, Hyperlink8, Hyperlink9, Hyperlink10, Hyperlink11), 1, row);
row++;
// --------- ControlsFX-specific Dialogs
Label controlsfxDialogs = new Label("ControlsFX Dialogs:");
controlsfxDialogs.setFont(Font.font(25));
grid.add(controlsfxDialogs, 0, row++, 2, 1);
// *******************************************************************
// Command links
// *******************************************************************
grid.add(createLabel("Pre-built dialogs: "), 0, row);
final Button Hyperlink12 = new Button("Command Links");
Hyperlink12.setOnAction(e -> {
List<CommandLinksButtonType> links = Arrays
.asList(new CommandLinksButtonType(
"Add a network that is in the range of this computer",
"This shows you a list of networks that are currently available and lets you connect to one.", false),
new CommandLinksButtonType(
"Manually create a network profile",
"This creates a new network profile or locates an existing one and saves it on your computer",
true /*default*/),
new CommandLinksButtonType("Create an ad hoc network",
"This creates a temporary network for sharing files or and Internet connection", false));
CommandLinksDialog dlg = new CommandLinksDialog(links);
dlg.setTitle("Manually connect to wireless network");
String optionalMasthead = "Manually connect to wireless network";
dlg.getDialogPane().setContentText("How do you want to add a network?");
configureSampleDialog(dlg, optionalMasthead);
showDialog(dlg);
});
final Button Hyperlink12a = new Button("Font Selector");
Hyperlink12a.setOnAction(e -> {
FontSelectorDialog dlg = new FontSelectorDialog(null);
configureSampleDialog(dlg, "Please select a font!");
showDialog(dlg);
});
final Button Hyperlink12b = new Button("Progress");
Hyperlink12b.setOnAction((ActionEvent e) -> {
Task<Object> worker = new Task<Object>() {
@Override
protected Object call() throws Exception {
for (int i = 0; i <= 100; i++) {
updateProgress(i, 99);
updateMessage("progress: " + i);
System.out.println("progress: " + i);
Thread.sleep(100);
}
return null;
}
};
ProgressDialog dlg = new ProgressDialog(worker);
configureSampleDialog(dlg, "");
Thread th = new Thread(worker);
th.setDaemon(true);
th.start();
});
final Button Hyperlink12c = new Button("Login");
Hyperlink12c.setOnAction((ActionEvent e) -> {
LoginDialog dlg = new LoginDialog(null, null);
configureSampleDialog(dlg, "");
showDialog(dlg);
});
final Button Hyperlink12d = new Button("Exception");
Hyperlink12d.setOnAction((ActionEvent e) -> {
ExceptionDialog dlg = new ExceptionDialog(new Exception("ControlsFX is _too_ awesome!"));
configureSampleDialog(dlg, "");
showDialog(dlg);
});
grid.add(new HBox(10, Hyperlink12, Hyperlink12a, Hyperlink12b, Hyperlink12c, Hyperlink12d), 1, row);
row++;
// *******************************************************************
// wizards
// *******************************************************************
grid.add(createLabel("Wizard: "), 0, row);
final Button Hyperlink15a = new Button("Linear Wizard");
Hyperlink15a.setOnAction(e -> showLinearWizard());
final Button Hyperlink15b = new Button("Branching Wizard");
Hyperlink15b.setOnAction(e -> showBranchingWizard());
final Button Hyperlink15c = new Button("Validated Linear Wizard");
Hyperlink15c.setOnAction(e -> showValidatedLinearWizard());
grid.add(new HBox(10, Hyperlink15a, Hyperlink15b, Hyperlink15c), 1, row++);
return grid;
}
private Alert createAlert(AlertType type) {
Window owner = cbSetOwner.isSelected() ? stage : null;
Alert dlg = new Alert(type, "");
dlg.initModality(modalityCombobox.getValue());
dlg.initOwner(owner);
return dlg;
}
private void configureSampleDialog(Dialog<?> dlg, String header) {
Window owner = cbSetOwner.isSelected() ? stage : null;
if (header != null && cbShowMasthead.isSelected()) {
dlg.getDialogPane().setHeaderText(header);
}
if (cbCustomGraphic.isSelected()) {
dlg.getDialogPane().setGraphic(new ImageView(new Image(getClass().getResource("/org/controlsfx/samples/controlsfx-logo.png").toExternalForm())));
}
dlg.initStyle(styleCombobox.getValue());
dlg.initOwner(owner);
}
private void showDialog(Dialog<?> dlg) {
Window owner = cbSetOwner.isSelected() ? stage : null;
if (cbCloseDialogAutomatically.isSelected()) {
new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Attempting to close dialog now...");
Platform.runLater(() -> dlg.close());
}).start();
}
dlg.initOwner(owner);
if (cbUseBlocking.isSelected()) {
dlg.showAndWait().ifPresent(result -> System.out.println("Result is " + result));
} else {
dlg.show();
dlg.resultProperty().addListener(o -> System.out.println("Result is: " + dlg.getResult()));
System.out.println("This println is _after_ the show method - we're non-blocking!");
}
}
@Override public Node getControlPanel() {
GridPane grid = new GridPane();
grid.setVgap(10);
grid.setHgap(10);
grid.setPadding(new Insets(30, 30, 0, 30));
int row = 0;
// stage style
grid.add(createLabel("Style: ", "property"), 0, row);
styleCombobox.getItems().setAll(StageStyle.values());
styleCombobox.setValue(styleCombobox.getItems().get(0));
grid.add(styleCombobox, 1, row);
row++;
// modality
grid.add(createLabel("Modality: ", "property"), 0, row);
modalityCombobox.getItems().setAll(Modality.values());
modalityCombobox.setValue(modalityCombobox.getItems().get(Modality.values().length-1));
grid.add(modalityCombobox, 1, row);
row++;
// use blocking
cbUseBlocking.setSelected(true);
grid.add(createLabel("Use blocking: ", "property"), 0, row);
grid.add(cbUseBlocking, 1, row);
row++;
// close dialog automatically
grid.add(createLabel("Close dialog after 2000ms: ", "property"), 0, row);
grid.add(cbCloseDialogAutomatically, 1, row);
row++;
// show header
grid.add(createLabel("Show custom header text: ", "property"), 0, row);
grid.add(cbShowMasthead, 1, row);
row++;
// set owner
grid.add(createLabel("Set dialog owner: ", "property"), 0, row);
grid.add(cbSetOwner, 1, row);
row++;
// custom graphic
grid.add(createLabel("Use custom graphic: ", "property"), 0, row);
grid.add(cbCustomGraphic, 1, row);
row++;
return grid;
}
// private CommandLinksButtonType buildCommandLink( String text, String comment, boolean isDefault ) {
// return new CommandLinksButtonType(text, comment, isDefault);
// }
public static void main(String[] args) {
Application.launch(args);
}
private Node createLabel(String text, String... styleclass) {
Label label = new Label(text);
if (styleclass == null || styleclass.length == 0) {
label.setFont(Font.font(13));
} else {
label.getStyleClass().addAll(styleclass);
}
return label;
}
private void showLinearWizard() {
Window owner = cbSetOwner.isSelected() ? stage : null;
// define pages to show
Wizard wizard = new Wizard(owner);
wizard.setTitle("Linear Wizard");
// --- page 1
int row = 0;
GridPane page1Grid = new GridPane();
page1Grid.setVgap(10);
page1Grid.setHgap(10);
page1Grid.add(new Label("First Name:"), 0, row);
TextField txFirstName = createTextField("firstName");
// wizard.getValidationSupport().registerValidator(txFirstName, Validator.createEmptyValidator("First Name is mandatory"));
page1Grid.add(txFirstName, 1, row++);
page1Grid.add(new Label("Last Name:"), 0, row);
TextField txLastName = createTextField("lastName");
// wizard.getValidationSupport().registerValidator(txLastName, Validator.createEmptyValidator("Last Name is mandatory"));
page1Grid.add(txLastName, 1, row);
WizardPane page1 = new WizardPane();
page1.setHeaderText("Please Enter Your Details");
page1.setContent(page1Grid);
// --- page 2
final WizardPane page2 = new WizardPane() {
@Override public void onEnteringPage(Wizard wizard) {
String firstName = (String) wizard.getSettings().get("firstName");
String lastName = (String) wizard.getSettings().get("lastName");
setContentText("Welcome, " + firstName + " " + lastName + "! Let's add some newlines!\n\n\n\n\n\n\nHello World!");
}
};
page2.setHeaderText("Thanks For Your Details!");
// --- page 3
WizardPane page3 = new WizardPane();
page3.setHeaderText("Goodbye!");
page3.setContentText("Page 3, with extra 'help' button!");
ButtonType helpDialogButton = new ButtonType("Help", ButtonData.HELP_2);
page3.getButtonTypes().add(helpDialogButton);
Button helpButton = (Button) page3.lookupButton(helpDialogButton);
helpButton.addEventFilter(ActionEvent.ACTION, actionEvent -> {
actionEvent.consume(); // stop hello.dialog from closing
System.out.println("Help clicked!");
});
// create wizard
wizard.setFlow(new LinearFlow(page1, page2, page3));
System.out.println("page1: " + page1);
System.out.println("page2: " + page2);
System.out.println("page3: " + page3);
// show wizard and wait for response
wizard.showAndWait().ifPresent(result -> {
if (result == ButtonType.FINISH) {
System.out.println("Wizard finished, settings: " + wizard.getSettings());
}
});
}
private void showBranchingWizard() {
Window owner = cbSetOwner.isSelected() ? stage : null;
// define pages to show.
// Because page1 references page2, we need to declare page2 first.
final WizardPane page2 = new WizardPane();
page2.setContentText("Page 2");
final CheckBox checkBox = new CheckBox("Skip the second page");
checkBox.setId("skip-page-2");
VBox vbox = new VBox(10, new Label("Page 1"), checkBox);
final WizardPane page1 = new WizardPane() {
// when we exit page 1, we will check the state of the 'skip page 2'
// checkbox, and if it is true, we will remove page 2 from the pages list
@Override public void onExitingPage(Wizard wizard) {
// List<WizardPage> pages = wizard.getPages();
// if (checkBox.isSelected()) {
// pages.remove(page2);
// } else {
// if (! pages.contains(page2)) {
// pages.add(1, page2);
// }
// }
}
};
page1.setContent(vbox);
final WizardPane page3 = new WizardPane();
page3.setContentText("Page 3");
// create wizard
Wizard wizard = new Wizard(owner);
wizard.setTitle("Branching Wizard");
Wizard.Flow branchingFlow = new Wizard.Flow() {
@Override
public Optional<WizardPane> advance(WizardPane currentPage) {
return Optional.of(getNext(currentPage));
}
@Override
public boolean canAdvance(WizardPane currentPage) {
return currentPage != page3;
}
private WizardPane getNext(WizardPane currentPage) {
if ( currentPage == null ) {
return page1;
} else if ( currentPage == page1) {
return checkBox.isSelected()? page3: page2;
} else {
return page3;
}
}
};
//wizard.setFlow( new LinearWizardFlow( page1, page2, page3));
wizard.setFlow( branchingFlow);
// show wizard
wizard.showAndWait().ifPresent(result -> {
if (result == ButtonType.FINISH) {
System.out.println("Wizard finished, settings: " + wizard.getSettings());
}
});
}
private void showValidatedLinearWizard() {
Window owner = cbSetOwner.isSelected() ? stage : null;
Wizard wizard = new Wizard(owner);
wizard.setTitle("Validated Linear Wizard");
// Page 1
WizardPane page1 = new WizardPane() {
ValidationSupport vs = new ValidationSupport();
{
vs.initInitialDecoration();
int row = 0;
GridPane page1Grid = new GridPane();
page1Grid.setVgap(10);
page1Grid.setHgap(10);
page1Grid.add(new Label("Username:"), 0, row);
TextField txUsername = createTextField("username");
vs.registerValidator(txUsername, Validator.createEmptyValidator("EMPTY!"));
page1Grid.add(txUsername, 1, row++);
page1Grid.add(new Label("Full Name:"), 0, row);
TextField txFullName = createTextField("fullName");
page1Grid.add(txFullName, 1, row);
setContent(page1Grid);
}
@Override
public void onEnteringPage(Wizard wizard) {
wizard.invalidProperty().unbind();
wizard.invalidProperty().bind(vs.invalidProperty());
}
};
// Page 2
WizardPane page2 = new WizardPane() {
ValidationSupport vs = new ValidationSupport();
{
vs.initInitialDecoration();
int row = 0;
GridPane page2Grid = new GridPane();
page2Grid.setVgap(10);
page2Grid.setHgap(10);
page2Grid.add(new Label("ControlsFX is:"), 0, row);
ComboBox<String> cbControlsFX = createComboBox("controlsfx");
cbControlsFX.setItems(FXCollections.observableArrayList("Cool", "Great"));
vs.registerValidator(cbControlsFX, Validator.createEmptyValidator("EMPTY!"));
page2Grid.add(cbControlsFX, 1, row++);
page2Grid.add(new Label("Where have you heard of it?:"), 0, row);
TextField txWhere = createTextField("where");
vs.registerValidator(txWhere, Validator.createEmptyValidator("EMPTY!"));
page2Grid.add(txWhere, 1, row++);
page2Grid.add(new Label("Free text:"), 0, row);
TextField txFreeText = createTextField("freetext");
page2Grid.add(txFreeText, 1, row);
setContent(page2Grid);
}
@Override
public void onEnteringPage(Wizard wizard) {
wizard.invalidProperty().unbind();
wizard.invalidProperty().bind(vs.invalidProperty());
}
};
// create wizard
wizard.setFlow(new LinearFlow(page1, page2));
// show wizard and wait for response
wizard.showAndWait().ifPresent(result -> {
if (result == ButtonType.FINISH) {
System.out.println("Wizard finished, settings: " + wizard.getSettings());
}
});
}
private TextField createTextField(String id) {
TextField textField = new TextField();
textField.setId(id);
GridPane.setHgrow(textField, Priority.ALWAYS);
return textField;
}
private ComboBox<String> createComboBox(String id) {
ComboBox<String> comboBox = new ComboBox<>();
comboBox.setId(id);
GridPane.setHgrow(comboBox, Priority.ALWAYS);
return comboBox;
}
}

View File

@ -0,0 +1,196 @@
/**
* Copyright (c) 2018 ControlsFX
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of ControlsFX, any associated website, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.controlsfx.samples;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
import org.controlsfx.ControlsFXSample;
import org.controlsfx.control.ListActionView;
import org.controlsfx.control.action.Action;
import org.controlsfx.control.action.ActionUtils;
import org.controlsfx.glyphfont.FontAwesome;
import java.util.List;
public class HelloListActionView extends ControlsFXSample {
private ListActionView<String> view;
@Override
public String getSampleName() {
return "List Action View";
}
@Override
public Node getPanel(Stage stage) {
view = new ListActionView<>();
view.getItems().addAll("Dirk", "Jonathan", "Eugene","Abhinay", "Samir");
view.getActions().addAll(createActions());
GridPane pane = new GridPane();
pane.add(view, 0, 0);
pane.setAlignment(Pos.CENTER);
return pane;
}
@Override
public Node getControlPanel() {
VBox root = new VBox(20);
root.setPadding(new Insets(30, 30, 30, 30));
CheckBox useCellFactory = new CheckBox("Use cell factory");
useCellFactory.setOnAction(evt -> {
if (useCellFactory.isSelected()) {
view.setCellFactory(listView -> {
ListCell<String> cell = new ListCell<String>() {
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setText(item == null ? "null" : item);
setGraphic(null);
}
}
};
cell.setFont(Font.font("Arial", FontWeight.BOLD,
FontPosture.ITALIC, 18));
return cell;
});
} else {
view.setCellFactory(null);
}
});
CheckBox clearActions = new CheckBox("Clear Actions");
clearActions.selectedProperty().addListener((observable, oldValue, newValue) -> {
if (newValue) {
view.getActions().clear();
} else {
view.getActions().addAll(createActions());
}
});
ComboBox<Side> sideComboBox = new ComboBox<>(FXCollections.observableArrayList(Side.values()));
sideComboBox.setTooltip(new Tooltip("The side of the ListView on which the actions should be displayed"));
sideComboBox.getSelectionModel().select(Side.LEFT);
view.sideProperty().bind(sideComboBox.getSelectionModel().selectedItemProperty());
root.getChildren().addAll(useCellFactory, clearActions, sideComboBox);
return root;
}
@Override
public String getSampleDescription() {
return "A control used to let the user select multiple values from a "
+ "list of available values. Selected values are moved into a "
+ "second list that is showing the current selection. Items can "
+ "be moved by double clicking on them or by first selecting "
+ "them and then pressing one of the buttons in the center.";
}
@Override
public String getJavaDocURL() {
return Utils.JAVADOC_BASE
+ "org/controlsfx/control/ListActionView.html";
}
public static void main(String[] args) {
launch(args);
}
private ObservableList<Action> createActions() {
ListActionView.ListAction<String> moveUp = new ListActionView.ListAction<String>(
new FontAwesome().create(FontAwesome.Glyph.ANGLE_UP)) {
@Override
public void initialize(ListView<String> listView) {
setEventHandler(e -> moveSelectedItemsUp(listView));
}
};
ListActionView.ListAction<String> moveDown = new ListActionView.ListAction<String>(new FontAwesome().create(FontAwesome.Glyph.ANGLE_DOWN)) {
@Override
public void initialize(ListView<String> listView) {
setEventHandler(e -> moveSelectedItemsDown(listView));
}
};
return FXCollections.observableArrayList(moveUp, ActionUtils.ACTION_SEPARATOR, moveDown);
}
private void moveSelectedItemsUp(ListView<String> listView) {
final MultipleSelectionModel<String> selectionModel = listView.getSelectionModel();
final ObservableList<Integer> selectedIndices = selectionModel.getSelectedIndices();
final ObservableList<String> items = listView.getItems();
for (final Integer selectedIndex : selectedIndices) {
if (selectedIndex > 0) {
if (selectedIndices.contains(selectedIndex - 1)) continue;
final String item = items.get(selectedIndex);
final String itemToBeReplaced = items.get(selectedIndex - 1);
items.set(selectedIndex - 1, item);
items.set(selectedIndex, itemToBeReplaced);
selectionModel.clearSelection(selectedIndex);
selectionModel.select(selectedIndex - 1);
}
}
}
private void moveSelectedItemsDown(ListView<String> listView) {
final ObservableList<String> items = listView.getItems();
final MultipleSelectionModel<String> selectionModel = listView.getSelectionModel();
final List<Integer> selectedIndices = selectionModel.getSelectedIndices();
int lastIndex = items.size() - 1;
// Last selected item is to be replaced first
for (int index = selectedIndices.size() - 1; index >= 0; index--) {
final Integer selectedIndex = selectedIndices.get(index);
if (selectedIndex < lastIndex) {
if (selectedIndices.contains(selectedIndex + 1)) continue;
final String item = items.get(selectedIndex);
final String itemToBeReplaced = items.get(selectedIndex + 1);
items.set(selectedIndex + 1, item);
items.set(selectedIndex, itemToBeReplaced);
selectionModel.clearSelection(selectedIndex);
selectionModel.select(selectedIndex + 1);
}
}
}
}

View File

@ -0,0 +1,234 @@
/**
* Copyright (c) 2014, 2019 ControlsFX
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of ControlsFX, any associated website, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.controlsfx.samples;
import java.time.LocalDate;
import java.util.Arrays;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Control;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Callback;
import org.controlsfx.ControlsFXSample;
import org.controlsfx.validation.ValidationResult;
import org.controlsfx.validation.ValidationSupport;
import org.controlsfx.validation.Validator;
import org.controlsfx.validation.decoration.CompoundValidationDecoration;
import org.controlsfx.validation.decoration.GraphicValidationDecoration;
import org.controlsfx.validation.decoration.StyleClassValidationDecoration;
import org.controlsfx.validation.decoration.ValidationDecoration;
public class HelloValidation extends ControlsFXSample {
private TextField textField = new TextField();
private ValidationSupport validationSupport = new ValidationSupport();
@Override public String getSampleName() {
return "Component Validation";
}
@Override public String getJavaDocURL() {
return Utils.JAVADOC_BASE + "org/controlsfx/validation/ValidationSupport.html";
}
@Override public String getSampleDescription() {
return "Component Validation";
}
@Override public Node getPanel(final Stage stage) {
GridPane root = new GridPane();
root.setVgap(10);
root.setHgap(10);
root.setPadding(new Insets(30, 30, 0, 30));
root.sceneProperty().addListener(new InvalidationListener() {
@Override public void invalidated(Observable o) {
if (root.getScene() != null) {
root.getScene().getStylesheets().add(HelloDecorator.class.getResource("validation.css").toExternalForm());
}
}
});
int row = 0;
// text field
validationSupport.registerValidator(textField, Validator.createEmptyValidator("Text is required"));
root.add(new Label("TextField"), 0, row);
root.add(textField, 1, row);
GridPane.setHgrow(textField, Priority.ALWAYS);
//combobox
row++;
ComboBox<String> combobox = new ComboBox<String>();
combobox.getItems().addAll("Item A", "Item B", "Item C");
validationSupport.registerValidator(combobox, (Control c, String newValue) ->
ValidationResult.fromErrorIf(combobox, "ComboBox Selection required", newValue == null || newValue.isEmpty())
.addInfoIf(combobox, "Item A is the first item", "Item A".equals(newValue))
.addInfoIf(combobox, "Item B is the second item", "Item B".equals(newValue))
.addInfoIf(combobox, "Item C is the third item", "Item C".equals(newValue))
);
root.add(new Label("ComboBox"), 0, row);
root.add(combobox, 1, row);
GridPane.setHgrow(combobox, Priority.ALWAYS);
//choicebox
row++;
ChoiceBox<String> choiceBox = new ChoiceBox<String>();
choiceBox.getItems().addAll("Item A", "Item B", "Item C");
validationSupport.registerValidator(choiceBox, Validator.createEmptyValidator("ChoiceBox Selection required"));
root.add(new Label("ChoiceBox"), 0, row);
root.add(choiceBox, 1, row);
GridPane.setHgrow(choiceBox, Priority.ALWAYS);
//checkbox
row++;
CheckBox checkBox = new CheckBox();
validationSupport.registerValidator(checkBox, (Control c, Boolean newValue) ->
ValidationResult.fromErrorIf(c, "Checkbox should be checked", !newValue));
root.add(new Label("CheckBox"), 0, row);
root.add(checkBox, 1, row);
GridPane.setHgrow(checkBox, Priority.ALWAYS);
//slider
row++;
Slider slider = new Slider(-50d, 50d, -10d);
slider.setShowTickLabels(true);
validationSupport.registerValidator(slider, (Control c, Double newValue) ->
ValidationResult.fromErrorIf(slider, "Slider value should be > 0", newValue <= 0));
root.add(new Label("Slider"), 0, row);
root.add(slider, 1, row);
GridPane.setHgrow(slider, Priority.ALWAYS);
// color picker
row++;
ColorPicker colorPicker = new ColorPicker(Color.RED);
validationSupport.registerValidator(colorPicker,
Validator.createEqualsValidator("Color should be WHITE or BLACK", Arrays.asList(Color.WHITE, Color.BLACK)));
root.add(new Label("Color Picker"), 0, row);
root.add(colorPicker, 1, row);
GridPane.setHgrow(colorPicker, Priority.ALWAYS);
// date picker
row++;
DatePicker datePicker = new DatePicker();
validationSupport.registerValidator(datePicker, false, (Control c, LocalDate newValue) ->
ValidationResult.fromWarningIf(datePicker, "The date should be today", !LocalDate.now().equals(newValue)));
root.add(new Label("Date Picker"), 0, row);
root.add(datePicker, 1, row);
GridPane.setHgrow(datePicker, Priority.ALWAYS);
ScrollPane scrollPane = new ScrollPane(root);
return scrollPane;
}
@Override public Node getControlPanel() {
GridPane grid = new GridPane();
grid.setVgap(10);
grid.setHgap(10);
grid.setPadding(new Insets(30, 30, 0, 30));
ValidationDecoration iconDecorator = new GraphicValidationDecoration();
ValidationDecoration cssDecorator = new StyleClassValidationDecoration();
ValidationDecoration compoundDecorator = new CompoundValidationDecoration(cssDecorator, iconDecorator);
int row = 0;
// --- validation decorator
Callback<ListView<ValidationDecoration>, ListCell<ValidationDecoration>> cellFactory = listView -> new ListCell<ValidationDecoration>() {
@Override protected void updateItem(ValidationDecoration decorator, boolean empty) {
super.updateItem(decorator, empty);
if (empty) {
setText("");
} else {
if (decorator instanceof StyleClassValidationDecoration) {
setText("Style Class Validation Decorator");
} else if (decorator instanceof GraphicValidationDecoration) {
setText("Graphic Validation Decorator");
} else if (decorator instanceof CompoundValidationDecoration) {
setText("Compound Validation Decorator");
} else {
setText("Unknown decorator type!");
}
}
}
};
ComboBox<ValidationDecoration> decoratorBox = new ComboBox<>();
decoratorBox.getItems().addAll(iconDecorator, cssDecorator, compoundDecorator);
decoratorBox.setCellFactory(cellFactory);
decoratorBox.setButtonCell(cellFactory.call(null));
decoratorBox.getSelectionModel().selectedItemProperty().addListener((o,old,decorator) ->
validationSupport.setValidationDecorator(decorator));
decoratorBox.getSelectionModel().select(0);
Label validationDecoratorLabel = new Label("Validation Decorator: ");
validationDecoratorLabel.getStyleClass().add("property");
grid.add(validationDecoratorLabel, 0, row);
grid.add(decoratorBox, 1, row);
GridPane.setHgrow(decoratorBox, Priority.ALWAYS);
row++;
ToggleButton btnToggleRequired = new ToggleButton("Toggle TextField required status");
btnToggleRequired.setSelected(ValidationSupport.isRequired(textField));
btnToggleRequired.setOnAction(e -> {
ValidationSupport.setRequired(textField, btnToggleRequired.isSelected());
});
grid.add(btnToggleRequired, 1, row, 1, 1);
return grid;
}
public static void main(String[] args) {
launch(args);
}
}

View File

@ -0,0 +1,33 @@
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
@EnableCaching
public class ProxyBean {
@Bean
public RestTemplate restTemplate() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
//Proxy usage example
TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
.loadTrustMaterial(null, acceptingTrustStrategy)
.build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
HttpHost proxy = new HttpHost("192.168.88.129", 8080);
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(csf)
.setRoutePlanner(new DefaultProxyRoutePlanner(proxy))
.build();
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
return restTemplate;
}
}

View File

@ -0,0 +1,51 @@
https://kostenko.org/blog/2019/06/runtime-class-loading.html
How to use custom ClassLoader to load jars in runtime
28 June 2019
To load calsses in runtime java uses ClassLoader mechanism which is based on next core principles:
delegation - by default uses parent-first delegation, - child ClassLoader will be used if parent is not able to find or load class. This behavior can be changed to child-first by overwriting ClassLoader.loadClass(...);
visibility - child ClassLoader is able to see all the classes loaded by parent but vice-versa is not true;
uniqueness - allows to load a class exactly once, which is basically achieved by delegation and ensures that child ClassLoader doesn't reload the class already loaded by parent;
The main scenarios to use custom ClassLoader is:
Class Instrumentation - modifying classes at runtime. For example, to unit testing, debugging or monitoring;
Isolation of executions - isolate several execution environments within a single process by making visible only a subset of classes for a particular thread, like it does in EE environments;
So, let's see how using of custom ClassLoader looks from source code perspective:
List<File> jars = Arrays.asList(new File("/tmp/jars").listFiles());
URL[] urls = new URL[files.size()];
for (int i = 0; i < jars.size(); i++) {
try {
urls[i] = jars.get(i).toURI().toURL();
} catch (Exception e) {
e.printStackTrace();
}
}
URLClassLoader childClassLoader = new URLClassLoader(urls, ClassLoader.getSystemClassLoader());
Then load class with custom ClassLoader:
Class.forName("org.kostenko.examples.core.classloader.ClassLoaderTest", true , childClassLoader);
Note! If your loaded libraries uses some resources like properties or something else, you need to provide context class loader:
Thread.currentThread().setContextClassLoader(childClassLoader);
Also, you can use custom ClassLoaders to load services with Java Service Provider Interface(SPI)
ServiceLoader<MyProvider> serviceLoader = ServiceLoader.load(MyProvider.class, childClassLoader);
...
---
https://stackoverflow.com/questions/60764/how-to-load-jar-files-dynamically-at-runtime
The reason it's hard is security. Classloaders are meant to be immutable; you shouldn't be able to willy-nilly add classes to it at runtime. I'm actually very surprised that works with the system classloader. Here's how you do it making your own child classloader:
URLClassLoader child = new URLClassLoader(
new URL[] {myJar.toURI().toURL()},
this.getClass().getClassLoader()
);
Class classToLoad = Class.forName("com.MyClass", true, child);
Method method = classToLoad.getDeclaredMethod("myMethod");
Object instance = classToLoad.newInstance();
Object result = method.invoke(instance);
Painful, but there it is.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,74 @@
// at https://stackoverflow.com/questions/14630539/scheduling-a-job-with-spring-programmatically-with-fixedrate-set-dynamically/14632758
@Autowired
Environment env;
@Bean
public MyBean myBean() {
return new MyBean();
}
@Bean(destroyMethod = "shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(100);
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
taskRegistrar.addTriggerTask(
new Runnable() {
@Override public void run() {
myBean().getSchedule();
}
},
new Trigger() {
@Override public Date nextExecutionTime(TriggerContext triggerContext) {
Calendar nextExecutionTime = new GregorianCalendar();
Date lastActualExecutionTime = triggerContext.lastActualExecutionTime();
nextExecutionTime.setTime(lastActualExecutionTime != null ? lastActualExecutionTime : new Date());
nextExecutionTime.add(Calendar.MILLISECOND, env.getProperty("myRate", Integer.class)); //you can get the value from wherever you want
return nextExecutionTime.getTime();
}
}
);
}
// also at https://www.baeldung.com/spring-scheduled-tasks
@Configuration
@EnableScheduling
public class DynamicSchedulingConfig implements SchedulingConfigurer {
@Autowired
private TickService tickService;
@Bean
public Executor taskExecutor() {
return Executors.newSingleThreadScheduledExecutor();
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
taskRegistrar.addTriggerTask(
new Runnable() {
@Override
public void run() {
tickService.tick();
}
},
new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext context) {
Optional<Date> lastCompletionTime =
Optional.ofNullable(context.lastCompletionTime());
Instant nextExecutionTime =
lastCompletionTime.orElseGet(Date::new).toInstant()
.plusMillis(tickService.getDelay());
return Date.from(nextExecutionTime);
}
}
);
}
}

View File

@ -0,0 +1,4 @@
spNumberList.getDividers().get(0).positionProperty().addListener((obs, oldVal, newVal) -> {
log.info("divider old={} new={}", oldVal, newVal);
});

View File

@ -0,0 +1,49 @@
package das.tools.np.services;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.stereotype.Service;
import javax.sql.DataSource;
@Service
@Slf4j
public class DbCreationService {
public final String SQL_GET_TABLES_COUNT = "select count(1) from sqlite_master where type = 'table' " +
"and name in('archive','properties','groups','numbers','extra_phones','number_to_phone','search_history', 'search_params')";
private final JdbcTemplate jdbcTemplate;
private final DataSource dataSource;
public DbCreationService(JdbcTemplate jdbcTemplate, DataSource dataSource) {
this.jdbcTemplate = jdbcTemplate;
this.dataSource = dataSource;
}
@PostConstruct
public void checkDbExistence() {
log.info("checkDbExistence started");
if (getTablesCount() < 8) {
if (log.isDebugEnabled()) log.debug("--- Creating new DB ---");
Resource resource = new ClassPathResource("sql/schema.sql");
ResourceDatabasePopulator populator = new ResourceDatabasePopulator(resource);
populator.execute(dataSource);
if (log.isDebugEnabled()) log.debug("--- DB structure created ---");
resource = new ClassPathResource("sql/data.sql");
populator = new ResourceDatabasePopulator(resource);
populator.execute(dataSource);
if (log.isDebugEnabled()) log.debug("--- Initial data inserted ---");
}
}
private int getTablesCount() {
Integer cnt = jdbcTemplate.queryForObject(SQL_GET_TABLES_COUNT, Integer.class);
if (cnt == null) {
throw new RuntimeException("Couldn't execute SQL");
}
return cnt;
}
}

View File

@ -0,0 +1,10 @@
insert into properties (prop_key, prop_value) values ('end_point', 'https://api.novaposhta.ua/v2.0/json/');
insert into properties (prop_key, prop_value) values ('phone_number', '');
insert into properties (prop_key, prop_value) values ('language', 'en');
insert into groups (id, name) values (1, 'Default');
-- TEST ONLY! ToDo: Remove IT in Prod!!!!
update properties set prop_value = '+380955580145' where prop_key = 'phone_number';
commit;

View File

@ -0,0 +1,119 @@
CREATE TABLE properties (
id INTEGER primary key AUTOINCREMENT,
prop_key varchar not null,
prop_value varchar not null,
created timestamp default CURRENT_TIMESTAMP,
updated timestamp default CURRENT_TIMESTAMP
);
CREATE INDEX properties_keys_idx ON properties (prop_key);
CREATE TABLE groups (
id INTEGER primary key AUTOINCREMENT,
name varchar not null,
created timestamp default CURRENT_TIMESTAMP
);
insert into groups (id, name) values (1, 'Default');
CREATE TABLE numbers (
id INTEGER primary key AUTOINCREMENT,
number varchar(36),
group_id integer not null default 1,
app_status integer not null default 0,
number_type integer not null default 0,
comment varchar(127),
dateCreated varchar(36),
weight int,
cost int,
description varchar(36),
cargoType varchar(36),
status int,
seatsAmount int,
announcedPrice varchar(36),
scheduledDeliveryDate varchar(36),
recipientFullName varchar(36),
cityRecipient varchar(36),
warehouseRecipient varchar(36),
warehouseRecipientNumber varchar(36),
phoneRecipient varchar(36),
recipientAddress varchar(36),
citySender varchar(36),
phoneSender varchar(36),
warehouseSender varchar(36),
senderAddress varchar(36),
autoUpdated int(1) default 0,
json_data text,
created timestamp default CURRENT_TIMESTAMP,
updated timestamp default CURRENT_TIMESTAMP,
FOREIGN KEY (group_id) REFERENCES groups(id)
);
CREATE INDEX numbers_number_idx ON numbers(number);
CREATE INDEX numbers_cityR_idx ON numbers(cityRecipient);
CREATE INDEX numbers_cityS_idx ON numbers(citySender);
CREATE INDEX numbers_phoneR_idx ON numbers(phoneRecipient);
CREATE INDEX numbers_phoneS_idx ON numbers(phoneSender);
CREATE TABLE extra_phones (
id INTEGER primary key AUTOINCREMENT,
phone varchar(20) not null,
order_number integer not null
);
CREATE INDEX extra_phones_phone_idx ON extra_phones(phone);
CREATE TABLE number_to_phone (
id INTEGER primary key AUTOINCREMENT,
number_id INTEGER not null UNIQUE,
phone_id INTEGER not null,
CONSTRAINT number_id_fk FOREIGN KEY (number_id) REFERENCES numbers(id) ON DELETE CASCADE,
CONSTRAINT phone_id_fk FOREIGN KEY (phone_id) REFERENCES extra_phones(id) ON DELETE CASCADE
);
CREATE INDEX number_to_phone_number_id_idx ON number_to_phone(number_id);
CREATE INDEX number_to_phone_phone_id_idx ON number_to_phone(phone_id);
CREATE TABLE search_params (
id INTEGER primary key AUTOINCREMENT,
name varchar(64) not null UNIQUE,
options TEXT not null,
order_number integer not null,
created timestamp default CURRENT_TIMESTAMP,
updated timestamp default CURRENT_TIMESTAMP
);
CREATE INDEX search_params_name_idx ON search_params(name);
CREATE TABLE search_history (
id INTEGER primary key AUTOINCREMENT,
search_text TEXT not null,
created timestamp default CURRENT_TIMESTAMP
);
CREATE TABLE archive (
id INTEGER not null,
number varchar(36),
group_id integer not null,
app_status integer not null,
number_type integer not null,
comment varchar(127),
dateCreated varchar(36),
weight int,
cost int,
description varchar(36),
cargoType varchar(36),
status int,
seatsAmount int,
announcedPrice varchar(36),
scheduledDeliveryDate varchar(36),
recipientFullName varchar(36),
cityRecipient varchar(36),
warehouseRecipient varchar(36),
warehouseRecipientNumber varchar(36),
phoneRecipient varchar(36),
recipientAddress varchar(36),
citySender varchar(36),
phoneSender varchar(36),
warehouseSender varchar(36),
senderAddress varchar(36),
json_data text,
created timestamp,
updated timestamp
);
CREATE INDEX archive_number_idx ON archive(number);

View File

@ -0,0 +1,22 @@
Код Статус
1 Відправник самостійно створив цю накладну, але ще не надав до відправки
2 Видалено
3 Номер не знайдено
4 Відправлення у місті ХХXХ. (Статус для межобластных отправлений)
41 Відправлення у місті ХХXХ. (Статус для услуг локал стандарт и локал экспресс - доставка в пределах города)
5 Відправлення прямує до міста YYYY
6 Відправлення у місті YYYY, орієнтовна доставка до ВІДДІЛЕННЯ-XXX dd-mm. Очікуйте додаткове повідомлення про прибуття
7 Прибув на відділення
8 Прибув на відділення (завантажено в Поштомат)
9 Відправлення отримано
10 Відправлення отримано %DateReceived%. Протягом доби ви одержите SMS-повідомлення про надходження грошового переказу та зможете отримати його в касі відділення «Нова пошта»
11 Відправлення отримано %DateReceived%. Грошовий переказ видано одержувачу.
12 Нова Пошта комплектує ваше відправлення
101 На шляху до одержувача
102 Відмова від отримання (Відправником створено замовлення на повернення)
103 Відмова одержувача (отримувач відмовився від відправлення)
104 Змінено адресу
105 Припинено зберігання
106 Одержано і створено ЄН зворотньої доставки
111 Невдала спроба доставки через відсутність Одержувача на адресі або зв'язку з ним
112 Дата доставки перенесена Одержувачем

35
NovaPoshta/README.md Normal file
View File

@ -0,0 +1,35 @@
## Nova Poshta: Tracker and Organizer
### _Desktop application to track, organize and manage parcels from Ukrainian delivery operator Nova Poshta_
[![Java 8](https://img.shields.io/badge/Java-8-white)](https://www.java.com/) [![ControlsFX](https://img.shields.io/badge/controls-FX-66B2C4)](https://controlsfx.github.io) [![maven](https://img.shields.io/badge/maven-project-blue)](https://maven.apache.org/)
### 💡 Purpose
![Logo](help/images/app_logo.png)
There wasn't desktop application for Nova Poshta.
That it.
### 📃 Features
- multiple OS
- useful, informative and flexible interface
- in-fly interface language changing
- plugins support
- auto update uncompleted numbers
- some number views and filtration options
- custom numbers list - not all numbers to view, but numbers you need
- numbers grouping support
- extended search with saving search options feature
- multiple addition windows support
- archiving completed/unused numbers
- visually logging main application's events
### ⏬ Getting
You can download archive from [Releases section](https://github.com/NpTracker/releases) or clone the repository.
Find full application's documentation at: [Ukrainian](help/uk/help.md) and [English](help/en/help.md).
### 🚀 Launch<a id='Launch'/>
The application's distribution included run.cmd or run.sh depended on your OS. Just launch it.
Note: run.* script need to `JAVA_HOME` environment variable. If it doesn't provided made changes into script to include full path to JRE.
### 📜 Main Window<a id='MainWin'/>
![Main Window](help/images/main.png)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,44 @@
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.2.0 http://maven.apache.org/xsd/assembly-2.2.0.xsd">
<id>zip</id>
<includeBaseDirectory>true</includeBaseDirectory>
<formats>
<format>zip</format>
</formats>
<files>
<file>
<source>${project.build.directory}/${project.artifactId}.jar</source>
<outputDirectory>/</outputDirectory>
</file>
<file>
<source>${project.basedir}/src/main/resources/application.properties</source>
<outputDirectory>/</outputDirectory>
<destName>application-default.properties</destName>
</file>
<file>
<source>${project.basedir}/DB/np_empty.db</source>
<outputDirectory>/DB</outputDirectory>
<destName>np.db</destName>
</file>
<file>
<source>${project.basedir}/run.sh</source>
<outputDirectory>/</outputDirectory>
</file>
<file>
<source>${project.basedir}/run.cmd</source>
<outputDirectory>/</outputDirectory>
</file>
<file>
<source>${project.basedir}/src/main/resources/images/np_app_icon.ico</source>
<outputDirectory>/</outputDirectory>
</file>
</files>
<fileSets>
<fileSet>
<outputDirectory>plugins</outputDirectory>
<excludes><exclude>**/*</exclude></excludes>
</fileSet>
</fileSets>
</assembly>

View File

@ -0,0 +1,25 @@
## Nova Poshta: Tracker and Organizer
### Налаштування додатка
![Config](../images/config.png)
In the application configuration window, you can change the following parameters:
- Link to API New Mail. It does not require change. When you enter the wrong link, the app will not be able to receive data from the NP server
- Default Phone number. This number will be sent in request to the NP server to receive a Number's data. Without the Phone number the Number's data received from the server will be incomplete. Therefore, this parameter must be specified.
- Enable or turn off automatic Number's update with "unfinished" status
- Records number in [search history](search.md?history)
Positions and sizes of windows (for those that can change the size) are stored automatically when the window is closed and played when it is shown.
Automatically stored parameters for the following windows:
- [Main window](main.md)
- [Search window](search.md)
- [Detailed view window]()
- [Archived Numbers window](windows.md?archive)
- [Custom view Numbers window](windows.md?custom)
- [Phones edit window](phones.md)
- [Groups edit window](groups.md)
- [Search options edit window](search.md?options)
#### [to Contents](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,12 @@
## Nova Poshta: Tracker and Organizer
### Вікно детального перегляду ТТН
![Detailed Window](../images/detailed_window.png)
Detailed Number view window duplicates all the elements of [Detailed view](main.md?detail) of the [Main window](main.md). In addition, there is an additional table (at the bottom) that presents all Number's data obtained as a result of the server request to the [API of Nova Poshta](https://api.novaposhta.ua/v2.0/json/).
##### _see also:_ [детальний перегляд](main.md?detail)
#### [to Contents](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,21 @@
## Nova Poshta: Tracker and Organizer
### Групи
All Numbers in the application can be divided into groups. After the first start, one group **Default** is created, in which creates all new Numbers.
You can add a new group using [Menu](menu.md?menu) **Edit**, or using a button on the [Toolbar](menu.md?toolbar)
Also add, change and delete groups you can in [Group edit window](#edit)
There can be no two different groups with the same name.
#### Вікно редагування груп<a id='edit'/>
![Groups](../images/edit_groups.png)
In the group editing window, you can add, change and delete groups. The changed data is highlighted in the list of bold.
All actions are performed with the appropriate buttons. All changes are fixed only after pressing the button **OK**
It is impossible to remove the non-empty group. To removing the non-empty group move numbers from it another group (or to erchive), and only then - remove the group.
#### [to Contents](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,22 @@
## Nova Poshta: Tracker and Organizer
### _Desktop application to track, organize and manage parcels from Ukrainian delivery operator Nova Poshta_
### Contents
1. **[Main window](main.md)**
- [list element](main.md?element)
- [detail view](main.md?detail)
- [context menu](main.md?context)
2. **[Menu ant Toolbar](menu.md)**
3. [View modes and Filtration](view_mode.md)
4. [Configuration](config.md)
5. [Adding new Number](new.md)
6. [Groups](groups.md)
7. [Archived numbers and Custom view numbers](windows.md)
8. [Detailed number view window](detail.md)
9. **[Search and search options](search.md)**
10. [Phone numbers](phones.md)
11. **[Plugins](plugins.md)**
12. [Log](log.md)
13. **[Hot keys](hot_keys.md)**
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,49 @@
## Nova Poshta: Tracker and Organizer
### Hot keys
All the hotkeys in the application are listed here:
[Main window](main.md)
| **Комбінація** | **Дія** |
|---------------------------|-----------------------------|
| **Номер** ||
| Alt + A | Add new Number |
| F5 | Update Number |
| Shift + F5 | Update uncompleted |
| Alt + F4 | Exit application |
| **Змінити** ||
| Alt + Shift + G | New Group |
| Ctrl + Alt + G | Edit Groups |
| Alt + P | Edit Phone numbers |
| Alt + S | Edit Search options |
| Ctrl + F | Search |
| Alt + Shift + C | Configuration |
| **Вигляд** ||
| Ctrl + I | Detailed Number view window |
| Ctrl + L | Toggle Log |
| | _View modes_ |
| Alt + N | Numbers List |
| Alt + G | Groups |
| Alt + C | Calendar (Create) |
| Alt + U | Calendar (Update) |
| | _Filtration_ |
| Alt + Shift + A | All Numbers |
| Alt + Shift + I | Inbound |
| Alt + Shift + O | Outbound |
| Alt + Shift + U | Undefined |
[Search window](search.md)
| **Комбінація** | **Дія** |
|----------------|-----------------------------|
| Ctrl + S | Save search option |
| Ctrl + O | Open saved search options |
| Ctrl + M | Activate Simple search |
| Ctrl + E | Activate Extended search |
| Ctrl + P | Toggle search options panel |
| Esc | Hide search options panel |
#### [to Contents](help.md)
###### _Made by -=:dAs:=-_

16
NovaPoshta/help/en/log.md Normal file
View File

@ -0,0 +1,16 @@
## Nova Poshta: Tracker and Organizer
### Log
![Log](../images/log.png)
After pressing the **Toggle Log** button on the [Toolbar](menu.md?toolbar) or selection of the appropriate menu item in the menu **View**, in the bottom of the [Main window](main.md) the log area will be displayed showing the main actions of the application.
New records in the log area are added to the top line. Upon reaching the maximum number of entries, older entries will be deleted from the log.
The Log has a context menu that allows you to copy the log records to the clipboard or clear the Log.
The maximum number of log records is set in [config window](config.md).
#### [to Contents](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,29 @@
## Nova Poshta: Tracker and Organizer
### Main window
![Main Window](../images/main.png)
The main application window shows all added Numbers (left side), [Detailed info](#detail) (right side). There is also a [Log](log.md) at the bottom of the window showing the main events of the application.
#### List element<a id='element'/>
![Element](../images/list_element.png)
The list item contains basic information about the Number:
- number
- Number's State (new, error, processing, waiting, completed)
- the user's description or comment (if added); If both the description and the user's comment are added, the comment will be shown - the priority of custom editing
- Number's Type (inbound, outbound, undefined)
- Number Status (i.e. Completed), Date of Creation, Date Adding to the application, Last Update Date
#### Detailed view<a id='detail'/>
![Detailed](../images/detailed_number.png)
The detailed view shows all the Number's main fields. Detailed view is almost completely duplicated (except for increased Number status) the [List element](#element)
There are also buttons that allow you to copy the text to the system clipboard and the opening [Detailed view window](detail.md) button, where all Number's data is shown.
#### Context menu<a id='context'/>
![Context](../images/context_menu.png)
The Context menu of the [List element](#element) main Number's commands are contained
Also those commands contains in the [Menu](menu.md?menu) and in the [Toolbar](menu.md?toolbar).
#### [to Contents](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,61 @@
## Nova Poshta: Tracker and Organizer
### Menu and Toolbar
#### Toolbar<a id='toolbar'/>
![Toolbar](../images/tool_bar.png)
_All panel buttons have the same images as the corresponding [menu items](#menu), so they are easy to identify._
The Toolbar contains the following application commands:
- [Add Number](new.md) - add new number
- Update Number - update selected Number
- Update uncompleted - update all Numbers having Uncompleted status
- [Add to Custom view](windows.md?custom) - Add Number to Custom view
- [Move to Archive](windows.md?archive) - move number to Archive
- [Add new group](groups.md) - adding new group
- [Edit Groups](groups.md?edit) - edit groups window
- [Edit Phones](phones.md) - edit phones window
- [Edit Search Options](search.md?options) - edit search options window
- [Search](search.md?search) - Search window
- [Configuration](config.md) - configuration window
- [Detailed info](detail.md) - detailed Number info window
- [Toggle Log](log.md) - show or hide the Log
- [View mode](view_mode.md?view) - select the view mode
- [Filtering mode](view_mode.md?filter) - select the filtering mode
#### Menu<a id='menu'/>
The menu contains all application commands:
**Numbers**
- [Add Number](new.md) - add new number
- Update Number - update selected Number
- Update uncompleted - update all Numbers having Uncompleted status
- [Add to Custom view](windows.md?custom) - Add Number to Custom view
- [Move to Archive](windows.md?archive) - move number to Archive
- Exit - exit application
**Edit**
- [Add new group](groups.md) - adding new group
- [Edit Groups](groups.md?edit) - edit groups window
- [Edit Phones](phones.md) - edit phones window
- [Edit Search Options](search.md?options) - edit search options window
- [Search](search.md?search) - Search window
- [Configuration](config.md) - configuration window
**View**
- [View mode](view_mode.md?view) - select the view mode
- [Filtering mode](view_mode.md?filter) - select the filtering mode
- [Detailed info](detail.md) - detailed Number info window
- [Toggle Log](log.md) - show or hide the Log
**Plugins**
- Load plugins - reload [plugins](plugins.md) from storage
- _<plugins menu items>_ - send data to plugin
**Windows**
- [Custom view numbers](windows.md?custom) - Custom view window
- [Archived numbers](windows.md?archive) - Archived numbers window
- Cascade - show all additional windows as cascade
- Close all - close all additional windows
**Help**
- About - information about application version
_All menu items have the same images as the corresponding buttons of [Toolbar](#toolbar), so they are easy to identify._
#### [to Contents](help.md)
###### _Made by -=:dAs:=-_

19
NovaPoshta/help/en/new.md Normal file
View File

@ -0,0 +1,19 @@
## Nova Poshta: Tracker and Organizer
### Adding new Number
![New](../images/new.png)
In the Adding new Number window, you must provide:
- Number type (inbound, outbound, undefined)
- sender's or recoiver's phone number
- Group
- user comment
- is the auto-update need or not
If you plan to add another Number then you can choose **Add another** then the window after adding the current Number will not be closed.
At the same time, several windows of adding a new Number can be opened. If the **Adding new Number window** is still open when the application is closing, the application will not be closed, but will propose to close the **Adding new Number windows**.
#### [to Contents](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,16 @@
## Nova Poshta: Tracker and Organizer
### Phone numbers
![Phones](../images/phones.png)
In the application, several phone numbers can be stored, which are used to receive data about Number from the NP server. For example, it can be your personal phone number and the Business phone number for order tracking.
You can add, change and delete phone numbers in the phone number editing window. The main phone number in the list will be highlighted in bold.
All actions are performed using the corresponding buttons. All changes are fixed only after pressing the **OK** button
The phone number used in Number(s) cannot be deleted. To delete a phone number, you must first transfer associated Numbers to the archive.
#### [to Contents](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,26 @@
## Nova Poshta: Tracker and Organizer
### Plugins
Plugins are used to expand the functionality of the application. All plugins must be placed in the **plugins** folder and in its sub-folders. When loading plugins, the application scans the contents of all sub-folders of the folder **plugins** and sorts all folders by name, then the plugins contained directly in the folder **plugins** are added. In all folders, plugins are sorted by file name.
The plugin has an extension **jar** and must have the same name as the plugin's base class.
The plugin's base class must implement the **das.tools.np.entity.plugin.PluginInterface** interface:
```java
public interface PluginInterface {
String getName();
String getDescription();
String getNameUK();
String getDescriptionUK();
void doProcess(List<CargoNumber> list);
}
```
The [NP Plugin Template](#ToDo: add link) project was created for convenience. It contains the interface and all necessary classes for work and you need to take it as a basis for creating your plugin.
Also the [NP Demo Plugin](#ToDo: add link) project exists. It contains an example plugin that simply outputs all Numbers passed to it to the console.
At the moment (v.1.*), the plugins work only for outputting information from the application.
#### [to Contents](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,52 @@
## Nova Poshta: Tracker and Organizer
### Search, Search Options
#### Search<a id='search'/>
![Search](../images/search.png)
![Search1](../images/search1.png)
Search has 2 modes:
- simple - search the entered text only in the Numbers number; this is the default search. Activated by selecting **Simple Search**
- advanced - search for the entered text in the fields selected by the user according to [Search options](#options). Activated by selecting **Advanced search**
The search can take place in the main list of Numbers, and in [Archived Numbers](windows.md?archive). You can choose a place by choosing **In Archive**
**_Advanced Search_** allows you to set search options, save and restore [Search Options](#options)
You can select the fields in which the search will take place using the **Field list**. By default, search is selected in the **User Comment** and **Description** fields
**_The search is case-insensitive_**
The search window has a menu button [plugins](plugins.md), with which you can transfer all or only selected TTNs in the search results to the plugin. If the **All items** option is activated, all search results will be transferred, otherwise - only the selected ones.
#### Search history<a id='history'/>
The application keeps a history of search queries that have been entered in the search text line and suggests it, if there are matches, when entering a new search query. The number of search history entries can be changed in [Settings window](config.md) in the range from 10 to 500 entries. When the maximum number of records is reached, older records will be deleted.
History records are unique, meaning no two records can have the same text.
#### Search options<a id='options'/>
![Options](../images/edit_search_options.png)
The search options contain the following information:
- search name
- the text to be searched for
- fields in which the search will take place
- search by group:
- group: matches or does not match
- group name: matches, does not match or contains text
- weight: equal, not equal, more, less
- value: equal, not equal, more, less
- declared value: equal, not equal, greater than, less than
- seats amount: equal, not equal, more, less
- Number state: equal, not equal to one of the states: new, delivered, error, waiting, completed
- Number type: equal, not equal to one of the types: inbound, outbound, undefined
- Number status: equal, not equal to one of the NP's Number statuses
- date of creation of Number: equal, not equal, greater than, less than the value of the date
- the date of the scheduled delivery of the TTN: equal, not equal, greater than, less than the value of the date
In the edit search parameters window, you can add, change and delete search parameters. Changed data is highlighted in bold in the list.
All actions are performed using the corresponding buttons. All changes are fixed only after pressing the **OK** button
#### [to Contents](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,29 @@
## Nova Poshta: Tracker and Organizer
### View modes, Filtration
The application allows you to change the viewing and filtering mode for more convenient work. This can be done using:
- [Menu](menu.md?menu) - "View" select the viewing or filtering mode
- [Toolbar](menu.md?toolbar) - select viewing or filtering mode
- corresponding lists in the left part of [Main window](main.md) ![Modes](../images/view_modes_filters.png)
#### View mode<a id='view'/>
The application has the following viewing modes:
- Numbers list
- Numbers by Groups
- Numbers by date of creation
- TNumbers by the date of update
When changing the viewing mode, the list of Numbers will change accordingly. The [List items](main.md?element) view remain unchanged. It will also remain unchanged the [Context menu](main.md?context) of the list element.
#### Filtering mode<a id='filter'/>
The application has the following filtering modes (by Number type):
- All
- Inbound
- Outbound
- Undefined
When you change the filtering, the list of Numbers will change accordingly to show only those Numbers that satisfy the selected filter.
#### [to Contents](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,35 @@
## Nova Poshta: Tracker and Organizer
### Archive Numbers and Custom Numbers view
### Archive Numbers<a id='archive'/>
![Archive](../images/archive_win.png)
You can show this window using the [Menu](menu.md?menu) item **Windows -> Archive numbers**
Numbers may be moved to the archive. You can move using [Context menu](main.md?context), [Menu](menu.md?menu) or [Toolbar](menu.md?toolbar)
From the list, archived Numbers can be moved to [Plugin](plugins.md). If the **All items** option is activated, all search results will be transferred, otherwise - only the selected ones.
The list has a context menu that allows you to restore or permanently delete the item(s).
**_You can finally delete TTN only from the archive._**
### Custom Numbers view<a id='custom'/>
![Custom](../images/custom_win.png)
You can show this window using [Menu](menu.md?menu) **Windows -> Selected numbers**
Any Number from the list in [Main window](main.md) can be added to the Custom Numbers view using [Context menu](main.md?context), [Menu](menu.md?menu) or [Toolbar](menu.md?toolbar)
From the list of Custom Numbers view, you can go to the main window using **Ctr + Click** on the element.
Double-clicking on the element will open [Detailed view window](detail.md)
The list has a context menu that allows you to delete items or clear the list completely. **_Items are only removed from the favorites list_**
The Custom Numbers view window has a menu button [plugins](plugins.md) with which you can move all or only selected TTNs in the search results to the plugin. If the **All items** option is activated, all search results will be transferred, otherwise - only the selected ones.
The list of Custom Numbers view is saved and will be available after restarting the application.
#### [to Contents](help.md)
###### _Made by -=:dAs:=-_

4
NovaPoshta/help/index.md Normal file
View File

@ -0,0 +1,4 @@
## Nova Poshta: Tracker and Organizer
### _Desktop application to track, organize and manage parcels from Ukrainian delivery operator Nova Poshta_
[Ukrainian](uk/help.md) and [English](en/help.md)

View File

@ -0,0 +1,25 @@
## Нова пошта: трекер та органайзер
### Налаштування додатка
![Config](../images/config.png)
У вікні конфігурації додатку можна змінити такі параметри:
- посилання на API Нової пошти. За звичай не потребує змін. При введенні неправильного посилання додаток не зможе отримувати дані з сервера НП
- номер телефону за замовчуванням. Цей номер буде відправлятися у запиті до сервера НП для отримання даних за ТТН. Без номера дані ТТН, отримані з сервера, будуть неповними. Тому цей параметр необхідно вказати.
- увімкнути чи вимкнути автоматичне оновлення ТТН зі статусом "Незавершені"
- кількість записів в [історії пошуку](search.md?history)
Позиції та розміри вікон (для тих, що можуть змінювати розмір), зберігаються автоматично при закриванні вікна та відтворюються при його показі.
Автоматично зберігаються параметри для таких вікон:
- [Головне вікно](main.md)
- [Вікно пошуку](search.md)
- [Вікно детального перегляду]()
- [Вікно архівних ТТН](windows.md?archive)
- [Вікно обраних ТТН](windows.md?custom)
- [Вікно редагування номерів телефонів](phones.md)
- [Вікно редагування груп](groups.md)
- [Вікно редагування параметрів пошуку](search.md?options)
#### [до Змісту](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,12 @@
## Нова пошта: трекер та органайзер
### Вікно детального перегляду ТТН
![Detailed Window](../images/detailed_window.png)
Вікно детально перегляду ТТН дублює всі елементи [Детального перегляду](main.md?detail) [Головного вікна](main.md). Крім того, тут є додаткова таблиця (у нижній частині), у якій представлені всі дані про ТТН, отримані в результаті запиту з сервера [API Нової Пошти](https://api.novaposhta.ua/v2.0/json/).
##### _див. також:_ [детальний перегляд](main.md?detail)
#### [до Змісту](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,21 @@
## Нова пошта: трекер та органайзер
### Групи
Усі ТТН в додатку можуть бути рознесені по групах. Після першого запуску створюється одна група **Default**, в якій створюються усі ТТН.
Додати нову групу можна за допомогою [Меню](menu.md?menu) **Змінити**, або кнопкою на [Панелі інструментів](menu.md?toolbar)
Також додати, змінити та видалити групи можна у [Вікні редагування груп](#edit)
В додатку не може бути двох різних груп з однаковим іменем.
#### Вікно редагування груп<a id='edit'/>
![Groups](../images/edit_groups.png)
У вікні редагування груп можна додати, змінити та видалити групи. Змінені дані виділяються у списку жирним шрифтом.
Всі дії виконуються за допомогою відповідних кнопок. Усі зміни закріплюються лише після натискання кнопки **OK**
Видалити непорожню групу не можна. Для видалення потрібно номери з групи потрібно перенести їх до іншої (або до архіву), і лише після цього - видалити групу.
#### [до Змісту](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,22 @@
## Нова пошта: трекер та органайзер
### _Десктопний додаток для відстеження, впорядкування та керування посилками українського поштового оператора Нова пошта_
### Зміст
1. **[Головне вікно](main.md)**
- [елемент списку](main.md?element)
- [детальний перегляд](main.md?detail)
- [контекстне меню](main.md?context)
2. **[Меню та панель інструментів](menu.md)**
3. [Режими перегляду та фільтрація](view_mode.md)
4. [Налаштування додатка](config.md)
5. [Додавання нового ТТН](new.md)
6. [Групи](groups.md)
7. [Архів та Користувацький перегляд ТТН](windows.md)
8. [Вікно детального перегляду ТТН](detail.md)
9. **[Пошук, параметри пошуку](search.md)**
10. [Номери телефонів](phones.md)
11. **[Плагіни](plugins.md)**
12. [Журнал](log.md)
13. **[Гарячі клавіші](hot_keys.md)**
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,49 @@
## Нова пошта: трекер та органайзер
### Гарячі клавіші
Тут перераховано усі гарячі клавіші в додатку:
[Головне вікно](main.md)
| **Комбінація** | **Дія** |
|---------------------------|------------------------------------|
| **Номер** ||
| Alt + A | Дадати ТТН |
| F5 | Оновити |
| Shift + F5 | Оновити незавершені |
| Alt + F4 | Вийти з додатка |
| **Змінити** ||
| Alt + Shift + G | Нова група |
| Ctrl + Alt + G | Редагувати групи |
| Alt + P | Редагувати номери телефонів |
| Alt + S | Редагувати налаштування пошуку |
| Ctrl + F | Пошук |
| Alt + Shift + C | Налаштування додатку |
| **Вигляд** ||
| Ctrl + I | Вікно детального перегляду для ТТН |
| Ctrl + L | Показати / приховати Журнал |
| | _Режими перегляду_ |
| Alt + N | Список ТТН |
| Alt + G | ТТН по групах |
| Alt + C | ТТН по даті створення |
| Alt + U | ТТН по даті оновлення |
| | ільтрація_ |
| Alt + Shift + A | Усі ТТН |
| Alt + Shift + I | Вхідні ТТН |
| Alt + Shift + O | Вихідні ТТН |
| Alt + Shift + U | Невизначено |
[Вікно пошуку](search.md)
| **Комбінація** | **Дія** |
|----------------|------------------------------------------|
| Ctrl + S | Зберегти налаштування пошуку |
| Ctrl + O | Завантажити налаштування пошуку |
| Ctrl + M | Активувати простий пошук |
| Ctrl + E | Активувати розширений пошук |
| Ctrl + P | Показати / приховати панель опцій пошуку |
| Esc | Приховати панель опцій пошуку |
#### [до Змісту](help.md)
###### _Made by -=:dAs:=-_

16
NovaPoshta/help/uk/log.md Normal file
View File

@ -0,0 +1,16 @@
## Нова пошта: трекер та органайзер
### Журнал
![Log](../images/log.png)
Після натискання кнопки **Показати журнал** на [Панелі інструментів](menu.md?toolbar) або вибору відповідного пункту меню в меню **Перегляд** у нижній частині [Головного вікна](main.md) відобразиться область Журналу, в якому показано основні події додатка.
Нові записи в області Журналу додаються у верхній рядок. При досягненні максимальної кількості записів старіші записи будуть видалені з Журналу.
Журнал має контекстне меню, що дозволяє скопіювати записи журналу до буфера обміну та очистити журнал.
Максимальна кількість записів журналу встановлюється у [вікні налаштувань додатку](config.md).
#### [до Змісту](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,29 @@
## Нова пошта: трекер та органайзер
### Головне вікно
![Main Window](../images/main.png)
В головному вікні додатку показано всі додані на цей час ТТН (ліворуч), [детальне інфо про номер](#detail) (праворуч). Також в нижній частині вікна є [Журнал](log.md), де показано основні події додатка.
#### Елемент списку<a id='element'/>
![Element](../images/list_element.png)
Елемент списку містить основну інформацію про ТТН:
- номер ТТН
- статус номеру (новий, помилка, в обробці, очикує, завершено)
- опис або коментар користувача (якщо додано); якщо вказано і опис, і коментар користувача, то буде показано коментар - пріоритет користувацьких правок
- тип номеру (вхідний, вихідний, невизначено)
- статус номеру (зменшений), дата створення, дата додавання в додаток, дата останнього оновлення
#### Детальний перегляд<a id='detail'/>
![Detailed](../images/detailed_number.png)
В детальному перегляді видно всі основні поля ТТН. Детальний перегляд майже повністю дублює (окрім збільшеного статусу ТТН) [Едемент списку](#element)
Також тут є кнопки, що дозволяють скопіювати текст до системного буфера обміну, а також кнопка відкривання [Вікна детального перегляду ТТН](detail.md), де показано всі дані ТТН.
#### Контекстне меню<a id='context'/>
![Context](../images/context_menu.png)
В контекстному меню [Елемента списку](#element) містяться основні команди ТТН
Також ці команди є в [Меню](menu.md?menu) та в [Панельі інструментів](menu.md?toolbar).
#### [до Змісту](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,61 @@
## Нова пошта: трекер та органайзер
### Меню та панель інструментів
#### Панель інструментів<a id='toolbar'/>
![Toolbar](../images/tool_bar.png)
_Усі кнопки панелі мають такі ж зображення, що й відповідні [пункти меню](#menu), тому їх легко ідентифікувати._
В панелі інструментів містяться такі команди додатку:
- [додати новий ТТН](new.md) - додати новий ТТН
- оновити активний ТТН - оновити вибраний ТТН
- оновити всі незавершені ТТН - оновити все ТТН в додатку, у яких статус "Не завершено"
- [додати ТТН до обраних](windows.md?custom) - додати ТТН до списку обраних
- [перемістити ТТН до архіву](windows.md?archive) - перемістити ТТН до архіву
- [додати нову групу](groups.md) - додати нову групу
- [редагувати групи](groups.md?edit) - вікно редагування груп
- [редагувати номери телефонів](phones.md) - вікно редагування номерів
- [редагувати налаштування пошуку](search.md?options) - вікно редагування налаштувань пошуку
- [пошук](search.md?search) - вікно пошуку
- [налаштування додатку](config.md) - вікно налаштувань
- [детальний перегляд ТТН](detail.md) - вікно детально перегляду ТТН
- [показати журнал](log.md) - вікно журналу
- [вибрати режим перегляду](view_mode.md?view) - змінити режим перегляду ТТН
- [вибрати режим фільтрації](view_mode.md?filter) - змінити режим фільтрації ТТН
#### Меню<a id='menu'/>
Меню містить усі команди додатку:
**Номер**
- [додати новий ТТН](new.md)
- оновити активний ТТН
- оновити всі незавершені ТТН
- [додати ТТН до обраних](windows.md?custom) - додати ТТН до списку обраних
- [перемістити ТТН до архіву](windows.md?archive) - перемістити ТТН до архіву
- вихід з додатка - завершення роботи з додатком
**Змінити**
- [додати нову групу](groups.md) - додати нову групу
- [редагувати групи](groups.md?edit) - вікно редагування груп
- [редагувати номери телефонів](phones.md) - вікно редагування номерів
- [редагувати налаштування пошуку](search.md?options) - вікно редагування налаштувань пошуку
- [пошук](search.md?search) - вікно пошуку
- [налаштування додатку](config.md) - вікно налаштувань
**Перегляд**
- [вибрати режим перегляду](view_mode.md?view) - змінити режим перегляду ТТН
- [вибрати режим фільтрації](view_mode.md?filter) - змінити режим фільтрації ТТН
- [детальний перегляд ТТН](detail.md) - вікно детально перегляду ТТН
- [показати журнал](log.md) - вікно журналу
**Плагіни**
- завантажити плагіни - перезавантаження даних про [плагіни](plugins.md) зі сховища
- _<пункти меню плагінів>_ - надіслати дані до плагіну
**Вікна**
- [обрані номери](windows.md?custom) - показати вікно обраних ТТН
- [архівні номери](windows.md?archive) - показати вікно архівних ТТН
- вишикувати вікна каскадом
- закрити всі додаткові вікна
**Допомога**
- про додаток - інформація про версію додатка
_Усі пункти меню мають такі ж зображення, що й відповідні кнопки [панелі інструментів](#toolbar), тому їх легко ідентифікувати._
#### [до Змісту](help.md)
###### _Made by -=:dAs:=-_

19
NovaPoshta/help/uk/new.md Normal file
View File

@ -0,0 +1,19 @@
## Нова пошта: трекер та органайзер
### Додавання нового ТТН
![New](../images/new.png)
У Вікні додавання нового ТТН необхідно вказати:
- тип ТТН (вхідний, вихідний, невизначено)
- номер телефону отримувача чи відправника
- групу
- коментар користувача
- чи потрібно оновлювати ТТН автоматично
Якщо планується додавання ще одного ТТН, то можна вибрати "Додати ще один", тоді вікно після додавання поточногоТТН не буде закрито
Одночасно може бути відкрито кілька вікон додавання нового ТТН. Якщо при завершенні роботи додатка все ще є відкриті вікна додавання, то додаток роботу не завершить, а запропонує закрити вказані вікна або завершити додавання.
#### [до Змісту](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,16 @@
## Нова пошта: трекер та органайзер
### Номери телефонів
![Phones](../images/phones.png)
В додатку може бути збережено кілька номерів телефонів, які використовуються для отримання даних про ТТН з сервера НП. Наприклад, це може бути Ваш особистий телефон та телефон ФОП для відстеження замовлень.
У вікні редагування номерів телефонів можна додавати, змінювати та видаляти номери телефонів. Основний номер телефону в списку буде виділено жирним шрифтом.
Всі дії виконуються за допомогою відповідних кнопок. Усі зміни закріплюються лише після натискання кнопки **OK**
Номер телефону, що використовується в ТТН, не може бути видалений. Для видалення телефонного номера спершу потрібно перенести прив'язані до нього ТТН до архіву.
#### [до Змісту](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,26 @@
## Нова пошта: трекер та органайзер
### Плагіни
Для розширення функціоналу додатка використовуються плагіни. Усі плагіни мають бути розміщені у теці **plugins** та у її підтеках. При завантаженні плагінів додаток переглядає вміст усіх підтек теки **plugins** та сортує всі теки за іменем, потім додаються плагіни, що містяться безпосередньо у теці **plugins**. У всіх теках плагіни сортуються за іменем файлу.
Плагін має розширення **jar** та повинен мати ім'я таке ж, як у основного класу плагіну.
Основний клас плагіну має імплементувати інтерфейс **das.tools.np.entity.plugin.PluginInterface**:
```java
public interface PluginInterface {
String getName();
String getDescription();
String getNameUK();
String getDescriptionUK();
void doProcess(List<CargoNumber> list);
}
```
Для зручності було створено проект [NP Plugin Template](#ToDo: add link), який містить інтерфейс та всі необхідні для роботи класи і який потрібно брати за основу для створення свого плагіну.
Також є проект [NP Demo Plugin](#ToDo: add link), який містить приклад плагіну, що просто виводить усі передані йому ТТН в консоль.
На даний момент (v.1.*) плагіни працюють лише на виведення інформації з додатка.
#### [до Змісту](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,52 @@
## Нова пошта: трекер та органайзер
### Пошук, Параметри пошуку
#### Пошук<a id='search'/>
![Search](../images/search.png)
![Search1](../images/search1.png)
Пошук має 2 режими:
- простий - шукати введений текст лише у номері ТТН; це пошук за замовчуванням. Активується вибором **Простий пошук**
- розширений - шукати введений текст в обраних користувачем полях згідно з [Параметрами пошуку](#options). Активується вибором **Розширений пошук**
Пошук може відбуватися в основному списку ТТН, та в [Архіві ТТН](windows.md?archive). Обрати місце можна вибравши **в архіві**
**_Розширений режим_** пошуку дозволяє встановлювати параметри пошуку, зберігати та відновлювати [Параметри пошуку](#options)
Обирати поля, в яких відбуватиметься пошук можна за допомогою **Списку полів**. За замовчуванням вибрано пошук у полях **Користуваций коментар** та **Опис**
**_Пошук відбувається без урахування регістру символів_**
Вікно пошуку має кнопку меню [плагінів](plugins.md), за допомогою якої можна передати всі чи лише виділені ТТН в результатах пошуку до плагіну. При активованому виборі **Передати всі** будуть передані всі результати пошуку, інакше - лише виділені.
#### Історія пошуку<a id='history'/>
Додаток зберігає історію запитів пошуку, що були введені в рядок пошукового тексту, та пропонує, за наявності збігів, під час введення нового пошукового запиту. Кількість записів історії пошуку може бути змінена у [Вікні налаштувань](config.md) в діапазоні від 10 до 500 записів. При досягненні максимальної кількості записів старіші записи видалятимуться.
Записи історії унікальні, тобто не може бути двох записів з однаковим текстом.
#### Параметри пошуку<a id='options'/>
![Options](../images/edit_search_options.png)
Параметри пошуку містять таку інформацію:
- назва пошуку
- текст, що шукається
- поля, в яких відбуватиметься пошук
- пошук по групі:
- група: збігається чи не збігається
- ім'я групи: збігається, не збігається чи містить текст
- вага: дорівнює, не дорівнює, більше, менше
- вартість: дорівнює, не дорівнює, більше, менше
- оголошена вартість: дорівнює, не дорівнює, більше, менше
- кількість місць: дорівнює, не дорівнює, більше, менше
- стан ТТН: дорівнює, не дорівнює одному зі станів: новий, доставляється, помилка, очікує, завершено
- тип ТТН: дорівнює, не дорівнює одному із типів: вхідний, вихідний, невизначено
- статус ТТН: дорівнює, не дорівнює одному зі статусів ТТН НП
- дата створення ТТН: дорівнює, не дорівнює, більше, менше значенню дати
- дата запланованого доставлення ТТН: дорівнює, не дорівнює, більше, менше значенню дати
У вікні редагування параметрів пошуку можна додати, змінити та видалити параметри пошуку. Змінені дані виділяються у списку жирним шрифтом.
Всі дії виконуються за допомогою відповідних кнопок. Усі зміни закріплюються лише після натискання кнопки **OK**
#### [до Змісту](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,29 @@
## Нова пошта: трекер та органайзер
### Режими перегляду та Фільтрація
Додаток дозволяє змінювати режим перегляду та фільтрації для більш зручної роботи. Це можна зробити за допомогою:
- [Меню](menu.md?menu) - "Перегляд" вибрати режим перегляду чи фільтрації
- [Панелі інструментів](menu.md?toolbar) - вибрати режим перегляду чи фільтрації
- відповідних списків у лівій частині [Головного вікна](main.md) ![Modes](../images/view_modes_filters.png)
#### Режими перегляду<a id='view'/>
У додатку є такі режими перегляду:
- список ТТН
- ТТН по групах
- ТТН по даті створення
- ТТН по даті оновлення
При зміні режиму перегляду список ТТН зміниться відповідним чином. Вигляд [Елементів списку](main.md?element) при цьому залишаються без змін. Також без змін залишиться [Контекстне меню](main.md?context) елемента.
#### Фільтрація<a id='filter'/>
У додатку є такі режими фільтрації (за типом ТТН):
- усі
- вхідні
- вихідні
- невизначено
При зміні фільтрації список ТТН зміниться відповідним чином, щоб показати лише ті ТТН, що задовільняють обраному фільтру.
#### [до Змісту](help.md)
###### _Made by -=:dAs:=-_

View File

@ -0,0 +1,33 @@
## Нова пошта: трекер та органайзер
### Архів та Обрані ТТН
### Архів ТТН<a id='archive'/>
![Archive](../images/archive_win.png)
Показати це вікно можна за допомогою пункту [Меню](menu.md?menu) **Вікна -> Архівні номери**
До архіву переміщуються ТТН, з якими вже завершено роботу, але остаточно видалити їх ще зарано. Перемістити можна за допомогою [Контекстного меню](main.md?context), [Меню](menu.md?menu) чи [Панелі інструментів](menu.md?toolbar)
Зі списку архівні ТТН можна передати до [Плагіну](plugins.md). При активованому виборі **Передати всі** будуть передані всі результати пошуку, інакше - лише виділені.
Список має контекстне мею, що дозволяє відновити чи остаточно видалити елемент(и).
**_Остаточно видалити ТТН можна лише з архіву._**
### Обрані ТТН<a id='custom'/>
![Custom](../images/custom_win.png)
Показати це вікно можна за допомогою пункту [Меню](menu.md?menu) **Вікна -> Обрані номери**
До списку обраних можна додати будь-який ТТН зі списку в [Головному вікні](main.md) за допомогою [Контекстного меню](main.md?context), [Меню](menu.md?menu) чи [Панелі інструментів](menu.md?toolbar)
Зі списку обраних можна перейти до головного вікна за допомогою **Ctr + Click** на елементі.
При подвійному кліку на елементі буде відкрито [Вікно детального перегляду ТТН](detail.md)
Список має контекстне мею, що дозволяє видаляти елементи чи повністю очищати список. **_Елементи видаляються лише зі списку обраних_**
Вікно Обраних ТТН має кнопку меню [плагінів](plugins.md), за допомогою якої можна передати всі чи лише виділені ТТН в результатах пошуку до плагіну. При активованому виборі **Передати всі** будуть передані всі результати пошуку, інакше - лише виділені.
Список обраних зберігається і буде доступний після перезапуску додатку.
#### [до Змісту](help.md)
###### _Made by -=:dAs:=-_

5958
NovaPoshta/logs/NP.log Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

182
NovaPoshta/pom.xml Normal file
View File

@ -0,0 +1,182 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.4</version>
<relativePath/>
</parent>
<properties>
<app.version>1.1</app.version>
<java.version>17</java.version>
<fxweavr.version>1.3.0</fxweavr.version>
<jackson.version>2.17.0</jackson.version>
<httpclient.version>4.5.14</httpclient.version>
<spring-web.version>5.3.27</spring-web.version>
<spring-webmvc.version>5.3.27</spring-webmvc.version>
<javafx.version>12</javafx.version>
<sqlite-jdbc.version>3.42.0.0</sqlite-jdbc.version>
<controlsfx.version>11.2.1</controlsfx.version>
<spring-boot-starter-cache.version>3.1.5</spring-boot-starter-cache.version>
<javafx-swing.version>11-ea+24</javafx-swing.version>
<barbecue.version>1.5-beta1</barbecue.version>
</properties>
<groupId>das.tools.np</groupId>
<artifactId>NovaPoshta</artifactId>
<version>${app.version}</version>
<name>Nova Poshta</name>
<description>Nova Poshta: Tracker and Organizer</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>${spring-boot-starter-cache.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring-web.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-webmvc.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>net.rgielen</groupId>
<artifactId>javafx-weaver-spring</artifactId>
<version>${fxweavr.version}</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>${sqlite-jdbc.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-community-dialects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.controlsfx</groupId>
<artifactId>controlsfx</artifactId>
<version>${controlsfx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-swing</artifactId>
<version>${javafx-swing.version}</version>
</dependency>
<dependency>
<groupId>net.sourceforge.barbecue</groupId>
<artifactId>barbecue</artifactId>
<version>${barbecue.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<configuration>
<mainClass>NovaPoshtaApplication</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>build-info</id>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
<finalName>${project.artifactId}</finalName>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>distribution/assembly.xml</descriptor>
</descriptors>
<finalName>${project.artifactId}-distribution</finalName>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

1
NovaPoshta/run.cmd Normal file
View File

@ -0,0 +1 @@
start %JAVA_HOME%\bin\javaw.exe -jar NovaPoshta.jar

2
NovaPoshta/run.sh Normal file
View File

@ -0,0 +1,2 @@
#!/bin/bash
${JAVA_HOME}/bin/java -jar NovaPoshta.jar &

View File

@ -0,0 +1,33 @@
package das.tools.np;
import das.tools.np.entity.StageReadyEvent;
import das.tools.np.gui.Splash;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
public class JavaFxApplication extends Application {
private ConfigurableApplicationContext context;
@Override
public void init() throws Exception {
Splash splash = new Splash();
splash.show();
this.context = new SpringApplicationBuilder()
.sources(NovaPoshtaApplication.class)
.run(getParameters().getRaw().toArray(new String[0]));
}
@Override
public void start(Stage stage) throws Exception {
context.publishEvent(new StageReadyEvent(stage));
}
@Override
public void stop() throws Exception {
this.context.close();
Platform.exit();
}
}

View File

@ -0,0 +1,17 @@
package das.tools.np;
import javafx.application.Application;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@Lazy
@EnableScheduling
@EnableAsync
public class NovaPoshtaApplication {
public static void main(String[] args) {
Application.launch(JavaFxApplication.class, args);
}
}

View File

@ -0,0 +1,61 @@
package das.tools.np.config;
import jakarta.annotation.PostConstruct;
import org.controlsfx.glyphfont.GlyphFont;
import org.controlsfx.glyphfont.GlyphFontRegistry;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.client.RestTemplate;
import javax.sql.DataSource;
import java.util.Objects;
@Configuration
@EnableTransactionManagement
@EnableCaching
@EnableScheduling
public class AppConfig {
private final Environment env;
@PostConstruct
public void setAwtHeadlessOff () {
/* to prevent Headless exception. It has thrown when code that is dependent on a keyboard, display, or mouse
is called in an environment that does not support a keyboard, display, or mouse. */
System.setProperty("java.awt.headless", "false");
}
public AppConfig(Environment env) {
this.env = env;
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public DataSource dataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(Objects.requireNonNull(env.getProperty("driverClassName")));
dataSource.setUrl(env.getProperty("url"));
dataSource.setUsername(env.getProperty("user"));
dataSource.setPassword(env.getProperty("password"));
return dataSource;
}
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public GlyphFont glyphFont() {
return GlyphFontRegistry.font("FontAwesome");
}
}

View File

@ -0,0 +1,38 @@
package das.tools.np.config;
import das.tools.np.converter.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ConversionServiceFactoryBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import java.util.HashSet;
import java.util.Set;
@Configuration
public class ConversionConfig {
@Bean
public ConversionService getConversionService() {
ConversionServiceFactoryBean bean = new ConversionServiceFactoryBean();
bean.setConverters(getConverters(bean));
bean.afterPropertiesSet();
return bean.getObject();
}
private Set<Converter> getConverters(ConversionServiceFactoryBean bean) {
Set<Converter> converters = new HashSet<>();
converters.add(new GZipToResponseDataConverter());
converters.add(new ResponseDataToGzipConverter());
converters.add(new StringArrayIdsToDocumentsArrayConverter());
converters.add(new ResponseDataToCargoNumberConverter());
CargoNumberToSimpleConverter cargoNumberToSimple = new CargoNumberToSimpleConverter();
converters.add(cargoNumberToSimple);
converters.add(new CargoNumbersToSimpleNumbersConverter(cargoNumberToSimple));
converters.add(new SearchParamsToJsonStrConverter());
converters.add(new JsonStrToSearchParamsConverter());
converters.add(new WindowPositionToJsonStrConverter());
converters.add(new JsonStrToWindowPositionConverter());
return converters;
}
}

View File

@ -0,0 +1,24 @@
package das.tools.np.config;
import das.tools.np.services.impl.UTF8ControlImpl;
import lombok.extern.slf4j.Slf4j;
import net.rgielen.fxweaver.core.FxWeaver;
import net.rgielen.fxweaver.spring.SpringFxWeaver;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ResourceBundle;
@Configuration
@Slf4j
public class GuiConfig {
@Bean(name = "utf8Control")
public ResourceBundle.Control getUtf8Control() {
return new UTF8ControlImpl();
}
@Bean
public FxWeaver fxWeaver(ConfigurableApplicationContext applicationContext) {
return new SpringFxWeaver(applicationContext);
}
}

View File

@ -0,0 +1,26 @@
package das.tools.np.controller;
import das.tools.np.gui.RunInThread;
import das.tools.np.gui.controllers.MainController;
import lombok.extern.slf4j.Slf4j;
import net.rgielen.fxweaver.core.FxWeaver;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class UpdateController {
private final FxWeaver fxWeaver;
public UpdateController(FxWeaver fxWeaver) {
this.fxWeaver = fxWeaver;
}
@Async
@Scheduled(fixedDelay = 600000, initialDelay = 30000)
public void updateNumbers() {
MainController controller = fxWeaver.loadController(MainController.class);
new RunInThread(() -> controller.scheduledNumbersUpdate(false)).run();
}
}

View File

@ -0,0 +1,39 @@
package das.tools.np.converter;
import das.tools.np.entity.db.CargoNumber;
import das.tools.np.entity.db.SimpleNumber;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
@Slf4j
public class CargoNumberToSimpleConverter implements Converter<CargoNumber, SimpleNumber> {
private final SimpleDateFormat CREATED_DATE_FORMAT = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
@Override
public SimpleNumber convert(CargoNumber source) {
return SimpleNumber.builder()
.id(source.getId())
.number(source.getNumber())
.appStatus(source.getAppStatus())
.numberType(source.getNumberType())
.description(source.getDescription())
.comment(source.getComment())
.status(source.getStatusCode())
.createDate(getCreatedDate(source.getDateCreated()))
.created((source.getCreated()))
.updated((source.getUpdated()))
.build();
}
private Date getCreatedDate(String date) {
try {
return CREATED_DATE_FORMAT.parse(date);
} catch (ParseException e) {
log.error("Error parsing date {}", date);
return new Date(0);
}
}
}

View File

@ -0,0 +1,25 @@
package das.tools.np.converter;
import das.tools.np.entity.db.CargoNumber;
import das.tools.np.entity.db.SimpleNumber;
import org.springframework.core.convert.converter.Converter;
import java.util.ArrayList;
import java.util.List;
public class CargoNumbersToSimpleNumbersConverter implements Converter<List<CargoNumber>,List<SimpleNumber>> {
private final CargoNumberToSimpleConverter converter;
public CargoNumbersToSimpleNumbersConverter(CargoNumberToSimpleConverter converter) {
this.converter = converter;
}
@Override
public List<SimpleNumber> convert(List<CargoNumber> source) {
List<SimpleNumber> result = new ArrayList<>(source.size());
for (CargoNumber n : source) {
result.add(converter.convert(n));
}
return result;
}
}

View File

@ -0,0 +1,47 @@
package das.tools.np.converter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import das.tools.np.entity.response.ResponseData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.zip.GZIPInputStream;
@Slf4j
public class GZipToResponseDataConverter implements Converter<String, ResponseData> {
@Override
public ResponseData convert(String source) {
ResponseData responseData = null;
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
try {
responseData = objectMapper.readValue(decompress(source), ResponseData.class);
return responseData;
} catch (JsonProcessingException e) {
log.error("Couldn't convert GZip to ResponseData: ", e);
return ResponseData.builder().build();
}
}
private static String decompress(String source) {
try(GZIPInputStream in = new GZIPInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(source)));
BufferedReader bf = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line=bf.readLine()) != null) {
sb.append(line);
}
return sb.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,25 @@
package das.tools.np.converter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import das.tools.np.entity.search.SearchParams;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
@Slf4j
public class JsonStrToSearchParamsConverter implements Converter<String, SearchParams> {
@Override
public SearchParams convert(String source) {
SearchParams searchParams = null;
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
try {
searchParams = objectMapper.readValue(source, SearchParams.class);
return searchParams;
} catch (JsonProcessingException e) {
log.error("Couldn't convert Json string to SearchParams: ", e);
return SearchParams.builder().build();
}
}
}

View File

@ -0,0 +1,23 @@
package das.tools.np.converter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import das.tools.np.entity.WindowPosition;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
@Slf4j
public class JsonStrToWindowPositionConverter implements Converter<String, WindowPosition> {
@Override
public WindowPosition convert(String source) {
WindowPosition windowPosition = null;
ObjectMapper objectMapper = new ObjectMapper();
try {
windowPosition = objectMapper.readValue(source, WindowPosition.class);
return windowPosition;
} catch (JsonProcessingException e) {
log.error("Couldn't convert Json string to SearchParams: ", e);
return WindowPosition.builder().build();
}
}
}

View File

@ -0,0 +1,33 @@
package das.tools.np.converter;
import das.tools.np.entity.db.CargoNumber;
import das.tools.np.entity.response.ResponseData;
import org.springframework.core.convert.converter.Converter;
public class ResponseDataToCargoNumberConverter implements Converter<ResponseData, CargoNumber> {
@Override
public CargoNumber convert(ResponseData source) {
return CargoNumber.builder()
.number(source.getNumber())
.dateCreated(source.getDateCreated())
.weight(source.getWeight())
.cost(source.getCost())
.seatsAmount(source.getSeatsAmount())
.description(source.getDescription())
.cargoType(source.getCargoType())
.statusCode(Integer.parseInt(source.getStatusCode()))
.announcedPrice(source.getAnnouncedPrice())
.scheduledDeliveryDate(source.getScheduledDeliveryDate())
.recipientFullName(source.getRecipientFullName())
.cityRecipient(source.getCityRecipient())
.warehouseRecipient(source.getWarehouseRecipient())
.warehouseRecipientNumber(source.getWarehouseRecipientNumber())
.phoneRecipient(source.getPhoneRecipient())
.recipientAddress(source.getRecipientAddress())
.citySender(source.getCitySender())
.phoneSender(source.getPhoneSender())
.warehouseSender(source.getWarehouseSender())
.senderAddress(source.getSenderAddress())
.build();
}
}

View File

@ -0,0 +1,39 @@
package das.tools.np.converter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import das.tools.np.entity.response.ResponseData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.zip.GZIPOutputStream;
@Slf4j
public class ResponseDataToGzipConverter implements Converter<ResponseData, String> {
@Override
public String convert(ResponseData source) {
ObjectMapper objectMapper = new ObjectMapper();
try {
return compress(objectMapper.writeValueAsString(source));
} catch (JsonProcessingException e) {
log.error("Couldn't convert ResponseData to GZip: ", e);
return "";
}
}
private static String compress(String source) {
try(ByteArrayOutputStream arr = new ByteArrayOutputStream();
GZIPOutputStream os = new GZIPOutputStream(arr)) {
os.write(source.getBytes(StandardCharsets.UTF_8));
os.finish();
return Base64.getEncoder().encodeToString(arr.toByteArray());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,23 @@
package das.tools.np.converter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import das.tools.np.entity.search.SearchParams;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
@Slf4j
public class SearchParamsToJsonStrConverter implements Converter<SearchParams,String> {
@Override
public String convert(SearchParams source) {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
try {
return objectMapper.writeValueAsString(source);
} catch (JsonProcessingException e) {
log.error("Couldn't convert SearchParams to Json: ", e);
return "";
}
}
}

View File

@ -0,0 +1,17 @@
package das.tools.np.converter;
import das.tools.np.entity.request.Document;
import org.springframework.core.convert.converter.Converter;
public class StringArrayIdsToDocumentsArrayConverter implements Converter<String[], Document[]> {
@Override
public Document[] convert(String[] source) {
Document[] docs = new Document[source.length];
for (int i = 0; i < source.length; i++) {
docs[i] = Document.builder()
.documentNumber(source[i])
.build();
}
return docs;
}
}

View File

@ -0,0 +1,22 @@
package das.tools.np.converter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import das.tools.np.entity.WindowPosition;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
@Slf4j
public class WindowPositionToJsonStrConverter implements Converter<WindowPosition,String> {
@Override
public String convert(WindowPosition source) {
ObjectMapper objectMapper = new ObjectMapper();
try {
return objectMapper.writeValueAsString(source);
} catch (JsonProcessingException e) {
log.error("Couldn't convert WindowPosition to Json: ", e);
return "";
}
}
}

View File

@ -0,0 +1,15 @@
package das.tools.np.entity;
import lombok.*;
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Setter
@ToString
@EqualsAndHashCode
public class NameValue {
private String name;
private String value;
}

View File

@ -0,0 +1,13 @@
package das.tools.np.entity;
import javafx.stage.Stage;
import org.springframework.context.ApplicationEvent;
public class StageReadyEvent extends ApplicationEvent {
public final Stage stage;
public StageReadyEvent(Stage stage) {
super(stage);
this.stage = stage;
}
}

View File

@ -0,0 +1,17 @@
package das.tools.np.entity;
import das.tools.np.gui.enums.SwitchControlType;
import javafx.scene.control.Control;
import lombok.*;
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Setter
@ToString
@EqualsAndHashCode
public class SwitchViewEntry {
private SwitchControlType type;
private Control control;
}

View File

@ -0,0 +1,21 @@
package das.tools.np.entity;
import lombok.*;
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Setter
@ToString
@EqualsAndHashCode
public class WindowPosition {
@Builder.Default
private int x = 0;
@Builder.Default
private int y = 0;
@Builder.Default
private int width = -1;
@Builder.Default
private int height = -1;
}

View File

@ -0,0 +1,49 @@
package das.tools.np.entity.db;
import das.tools.np.entity.response.ResponseData;
import lombok.*;
import java.util.Date;
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Setter
@ToString
@EqualsAndHashCode
public class CargoNumber {
@Builder.Default
private long id = -1;
private String number;
private Group group;
private String groupName;
private String descr;
private CargoStatus appStatus;
@Builder.Default
private NumberType numberType = NumberType.UNDEF;
private String comment;
private String dateCreated;
private float weight;
private float cost;
private float seatsAmount;
private String description;
private String cargoType;
private int statusCode;
private String announcedPrice;
private String scheduledDeliveryDate;
private String recipientFullName;
private String cityRecipient;
private String warehouseRecipient;
private String warehouseRecipientNumber;
private String phoneRecipient;
private String recipientAddress;
private String citySender;
private String phoneSender;
private String warehouseSender;
private String senderAddress;
private boolean autoUpdated;
private ResponseData fullData;
private Date created;
private Date updated;
}

View File

@ -0,0 +1,16 @@
package das.tools.np.entity.db;
public enum CargoStatus {
NEW, ERROR, PROCESSING, WAITING, COMPLETED;
public static CargoStatus valueOf(int value) {
return CargoStatus.values()[value];
}
/*public static CargoStatus valueOf(String value) {
for (CargoStatus status : values()) {
if (status.name().equals(value))
return status;
}
return null;
}*/
}

View File

@ -0,0 +1,18 @@
package das.tools.np.entity.db;
import lombok.*;
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Setter
@ToString
@EqualsAndHashCode
public class ExtraPhone {
@Builder.Default
private long id = 0;
private String phone;
@Builder.Default
private int orderNumber = -1;
}

View File

@ -0,0 +1,18 @@
package das.tools.np.entity.db;
import lombok.*;
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Setter
@ToString
@EqualsAndHashCode
public class Group {
@Builder.Default
private long id = 0;
private String name;
@Builder.Default
private boolean changed = false;
}

View File

@ -0,0 +1,18 @@
package das.tools.np.entity.db;
import lombok.*;
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Setter
@ToString
@EqualsAndHashCode
public class NumberToPhone {
private long id;
private long numberId;
private long phoneId;
private String number;
private String phone;
}

View File

@ -0,0 +1,8 @@
package das.tools.np.entity.db;
public enum NumberType {
UNDEF, IN, OUT;
public static NumberType valueOf(int value) {
return NumberType.values()[value];
}
}

View File

@ -0,0 +1,15 @@
package das.tools.np.entity.db;
import lombok.*;
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Setter
@ToString
@EqualsAndHashCode
public class Property {
private String key;
private String value;
}

View File

@ -0,0 +1,16 @@
package das.tools.np.entity.db;
import lombok.*;
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Setter
@ToString
@EqualsAndHashCode
public class SearchHistory {
@Builder.Default
private long id = 0;
private String searchText;
}

Some files were not shown because too many files have changed in this diff Show More