It is possible to issue java.lang.reflect.Field to javafx.scene.control.TextField? - javafx-8

It is possible to issue java.lang.reflect.Field to javafx.scene.control.TextField?
For example:
Field[] nodes;
nodes = clase.getDeclaredFields();
for (Field n : nodes)
if (n.getType().getSimpleName().equals("TextField"))
((TextField)((Object) n)).setText("Text");

If you want to modify the TextFields, you need to retrieve the value from those fields (and cast this value to TextField).
The following example should demonstrate the approach:
private TextField t1 = new TextField();
private TextField t2 = new TextField();
#Override
public void start(Stage primaryStage) {
Button btn = new Button("Say 'Hello World'");
btn.setOnAction((ActionEvent event) -> {
Object object = this;
Class clazz = object.getClass();
for (Field field : clazz.getDeclaredFields()) {
if (field.getType().getName().equals("javafx.scene.control.TextField")) {
try {
// get field value here
TextField textField = (TextField) field.get(object);
if (textField != null) {
textField.setText("Hello World");
}
} catch (IllegalArgumentException | IllegalAccessException ex) {
Logger.getLogger(ReflectTest.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
});
VBox root = new VBox();
root.getChildren().addAll(btn, t1, t2);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}

Reflection is probably a really bad approach to this. Among many problems is that you make the functionality dependent on how the code is written. Specifically, you assume that each text field is stored in a specific instance field in some class. If you change the implementation, e.g. so that you keep the text fields in a data structure instead of maintaining references to them yourself, then your functionality will break. It is bad practice to write code that is so tightly coupled to the actual implementation of the code, for obvious reasons.
One better approach would simply to be to put all the text fields in a list (or other data structure), so you can do whatever you need with them easily. E.g.
public class MyForm {
private GridPane view ;
private String[] messages = {"First name:", "Last name", "Email"} ;
private List<TextField> textFields ;
public MyForm {
view = new GridPane();
textFields = new ArrayList<>();
for (int r = 0; r < messages.length ; r++) {
view.addRow(r, new Label(messages[r]), createTextField(messages[r]));
}
}
private TextField createTextField(String text) {
TextField textField = new TextField();
textField.setPromptText(text);
textFields.add(textField);
return textField ;
}
public void processTextFields() {
textField.forEach(tf -> tf.setText("Hello"));
}
}
Another approach would be to use a CSS lookup. If myForm is some node that is an ancestor of all the text fields:
myForm.lookupAll(".text-field").forEach(node -> {
TextField textField = (TextField)node ;
textField.setText("Hello");
});
but note that CSS lookups will not work until after CSS has been applied (by default, this means after the scene has been rendered for the first time).
Another way, if all the text fields are all contained in a single direct parent (such as the grid pane in the first example), would be to iterate through the child nodes and filter the text fields:
textFieldParentNode.getChildrenUnmodifiable().stream()
.filter(TextField.class::isInstance)
.map(TextField.class::cast)
.forEach(tf -> tf.setText("Hello"));

Related

In iText 7 java how do you update Link text after it's already been added to the document

I am using iText7 to build a table of contents for my document. I know all the section names before I start, but don't know what the page numbers will be. My current process is to create a table on the first page and create all the Link objects with generic text "GO!". Then as I add sections I add through the link objects and update the text with the page numbers that I figured out as I created the document.
However, at the end, what gets written out for the link is "GO!", not the updated page number values I set as I was creating the rest of the document.
I did set the immediateFlush flag to false when I created the Document.
public class UpdateLinkTest {
PdfDocument pdfDocument = null;
List<Link>links = null;
Color hyperlinkColor = new DeviceRgb(0, 102, 204);
public static void main(String[] args) throws Exception {
List<String[]>notes = new ArrayList<>();
notes.add(new String[] {"me", "title", "this is my text" });
notes.add(new String[] {"me2", "title2", "this is my text 2" });
new UpdateLinkTest().exportPdf(notes, new File("./test2.pdf"));
}
public void exportPdf(List<String[]> notes, File selectedFile) throws Exception {
PdfWriter pdfWriter = new PdfWriter(selectedFile);
pdfDocument = new PdfDocument(pdfWriter);
Document document = new Document(pdfDocument, PageSize.A4, false);
// add the table of contents table
addSummaryTable(notes, document);
// add a page break
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
// add the body of the document
addNotesText(notes, document);
document.close();
}
private void addSummaryTable(List<String[]> notes, Document document) {
links = new ArrayList<>();
Table table = new Table(3);
float pageWidth = PageSize.A4.getWidth();
table.setWidth(pageWidth-document.getLeftMargin()*2);
// add header
addCell("Author", table, true);
addCell("Title", table, true);
addCell("Page", table, true);
int count = 0;
for (String[] note : notes) {
addCell(note[0], table, false);
addCell(note[1], table, false);
Link link = new Link("Go!", PdfAction.createGoTo(""+ (count+1)));
links.add(link);
addCell(link, hyperlinkColor, table, false);
count++;
}
document.add(table);
}
private void addNotesText(List<String[]> notes, Document document)
throws Exception {
int count = 0;
for (String[] note : notes) {
int numberOfPages = pdfDocument.getNumberOfPages();
Link link = links.get(count);
link.setText(""+(numberOfPages+1));
Paragraph noteText = new Paragraph(note[2]);
document.add(noteText);
noteText.setDestination(++count+"");
if (note != notes.get(notes.size()-1))
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
}
}
private static void addCell(String text, Table table, boolean b) {
Cell c1 = new Cell().add(new Paragraph(text));
table.addCell(c1);
}
private static void addCell(Link text, Color backgroundColor, Table table, boolean b) {
Cell c1 = new Cell().add(new Paragraph(text));
text.setUnderline();
text.setFontColor(backgroundColor);
table.addCell(c1);
}
}
Quite more work needs to be done compared to the code you have now because the changes to the elements don't take any effect once you've added them to the document. Immediate flush set to false allows you to relayout the elements, but that does not happen automatically. The way you calculate the current page the paragraph will be placed on (int numberOfPages = pdfDocument.getNumberOfPages();) is not bulletproof because in some cases pages might be added in advance, even if the content is not going to be placed on them immediately.
There is a very low level way to achieve your goal but with the recent version of iText (7.1.15) there is a simpler way as well, which still requires some work though. Basically your use case is very similar to target-counter concept in CSS, with page counter being the target one in your case. To support target counters in pdfHTML add-on we added new capabilities to our layout module which are possible to use directly as well.
To start off, we are going to tie our Link elements to the corresponding Paragraph elements that they will point to. We are going to do it with ID property in layout:
link.setProperty(Property.ID, String.valueOf(count));
noteText.setProperty(Property.ID, String.valueOf(count));
Next up, we are going to create custom renderers for our Link elements and Paragraph elements. Those custom renderers will interact with TargetCounterHandler which is the new capability in layout module I mentioned in the introduction. The idea is that during layout operation the paragraph will remember the page on which it was placed and then the corresponding link element (remember, link elements are connected to paragraph elements) will ask TargetCounterHandler during layout process of that link element which page the corresponding paragraph was planed on. So in a way, TargetCounterHandler is a connector.
Code for custom renderers:
private static class CustomParagraphRenderer extends ParagraphRenderer {
public CustomParagraphRenderer(Paragraph modelElement) {
super(modelElement);
}
#Override
public IRenderer getNextRenderer() {
return new CustomParagraphRenderer((Paragraph) modelElement);
}
#Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult result = super.layout(layoutContext);
TargetCounterHandler.addPageByID(this);
return result;
}
}
private static class CustomLinkRenderer extends LinkRenderer {
public CustomLinkRenderer(Link link) {
super(link);
}
#Override
public LayoutResult layout(LayoutContext layoutContext) {
Integer targetPageNumber = TargetCounterHandler.getPageByID(this, getProperty(Property.ID));
if (targetPageNumber != null) {
setText(String.valueOf(targetPageNumber));
}
return super.layout(layoutContext);
}
#Override
public IRenderer getNextRenderer() {
return new CustomLinkRenderer((Link) getModelElement());
}
}
Don't forget to assign the custom renderers to their elements:
link.setNextRenderer(new CustomLinkRenderer(link));
noteText.setNextRenderer(new CustomParagraphRenderer(noteText));
Now, the other thing we need to do it relayout. You already set immediateFlush to false and this is needed for relayout to work. Relayout is needed because on the first layout loop we will not know all the positions of the paragraphs, but we will already have placed the links on the pages by the time we know those positions. So we need the second pass to use the information about page numbers the paragraphs will reside on and set that information to the links.
Relayout is pretty straightforward - once you've put all the content you just need to call a single dedicated method:
// For now we have to prepare the handler for relayout manually, this is going to be improved
// in future iText versions
((DocumentRenderer)document.getRenderer()).getTargetCounterHandler().prepareHandlerToRelayout();
document.relayout();
One caveat is that for now you also need to subclass the DocumentRenderer since there is an additional operation that needs to be done that is not performed under the hood - propagation of the target counter handler to the root renderer we will be using for the second layout operation:
// For now we have to create a custom renderer for the root document to propagate the
// target counter handler to the renderer that will be used on the second layout process
// This is going to be improved in future iText versions
private static class CustomDocumentRenderer extends DocumentRenderer {
public CustomDocumentRenderer(Document document, boolean immediateFlush) {
super(document, immediateFlush);
}
#Override
public IRenderer getNextRenderer() {
CustomDocumentRenderer renderer = new CustomDocumentRenderer(document, immediateFlush);
renderer.targetCounterHandler = new TargetCounterHandler(targetCounterHandler);
return renderer;
}
}
document.setRenderer(new CustomDocumentRenderer(document, false));
And now we are done. Here is our visual result:
Complete code looks as follows:
public class UpdateLinkTest {
PdfDocument pdfDocument = null;
Color hyperlinkColor = new DeviceRgb(0, 102, 204);
public static void main(String[] args) throws Exception {
List<String[]> notes = new ArrayList<>();
notes.add(new String[] {"me", "title", "this is my text" });
notes.add(new String[] {"me2", "title2", "this is my text 2" });
new UpdateLinkTest().exportPdf(notes, new File("./test2.pdf"));
}
public void exportPdf(List<String[]> notes, File selectedFile) throws Exception {
PdfWriter pdfWriter = new PdfWriter(selectedFile);
pdfDocument = new PdfDocument(pdfWriter);
Document document = new Document(pdfDocument, PageSize.A4, false);
document.setRenderer(new CustomDocumentRenderer(document, false));
// add the table of contents table
addSummaryTable(notes, document);
// add a page break
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
// add the body of the document
addNotesText(notes, document);
// For now we have to prepare the handler for relayout manually, this is going to be improved
// in future iText versions
((DocumentRenderer)document.getRenderer()).getTargetCounterHandler().prepareHandlerToRelayout();
document.relayout();
document.close();
}
private void addSummaryTable(List<String[]> notes, Document document) {
Table table = new Table(3);
float pageWidth = PageSize.A4.getWidth();
table.setWidth(pageWidth-document.getLeftMargin()*2);
// add header
addCell("Author", table, true);
addCell("Title", table, true);
addCell("Page", table, true);
int count = 0;
for (String[] note : notes) {
addCell(note[0], table, false);
addCell(note[1], table, false);
Link link = new Link("Go!", PdfAction.createGoTo(""+ (count+1)));
link.setProperty(Property.ID, String.valueOf(count));
link.setNextRenderer(new CustomLinkRenderer(link));
addCell(link, hyperlinkColor, table, false);
count++;
}
document.add(table);
}
private void addNotesText(List<String[]> notes, Document document) {
int count = 0;
for (String[] note : notes) {
Paragraph noteText = new Paragraph(note[2]);
noteText.setProperty(Property.ID, String.valueOf(count));
noteText.setNextRenderer(new CustomParagraphRenderer(noteText));
document.add(noteText);
noteText.setDestination(++count+"");
if (note != notes.get(notes.size()-1))
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
}
}
private static void addCell(String text, Table table, boolean b) {
Cell c1 = new Cell().add(new Paragraph(text));
table.addCell(c1);
}
private static void addCell(Link text, Color backgroundColor, Table table, boolean b) {
Cell c1 = new Cell().add(new Paragraph(text));
text.setUnderline();
text.setFontColor(backgroundColor);
table.addCell(c1);
}
private static class CustomLinkRenderer extends LinkRenderer {
public CustomLinkRenderer(Link link) {
super(link);
}
#Override
public LayoutResult layout(LayoutContext layoutContext) {
Integer targetPageNumber = TargetCounterHandler.getPageByID(this, getProperty(Property.ID));
if (targetPageNumber != null) {
setText(String.valueOf(targetPageNumber));
}
return super.layout(layoutContext);
}
#Override
public IRenderer getNextRenderer() {
return new CustomLinkRenderer((Link) getModelElement());
}
}
private static class CustomParagraphRenderer extends ParagraphRenderer {
public CustomParagraphRenderer(Paragraph modelElement) {
super(modelElement);
}
#Override
public IRenderer getNextRenderer() {
return new CustomParagraphRenderer((Paragraph) modelElement);
}
#Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult result = super.layout(layoutContext);
TargetCounterHandler.addPageByID(this);
return result;
}
}
// For now we have to create a custom renderer for the root document to propagate the
// target counter handler to the renderer that will be used on the second layout process
// This is going to be improved in future iText versions
private static class CustomDocumentRenderer extends DocumentRenderer {
public CustomDocumentRenderer(Document document, boolean immediateFlush) {
super(document, immediateFlush);
}
#Override
public IRenderer getNextRenderer() {
CustomDocumentRenderer renderer = new CustomDocumentRenderer(document, immediateFlush);
renderer.targetCounterHandler = new TargetCounterHandler(targetCounterHandler);
return renderer;
}
}
}

how to access the components of a composite outside its defined method

I have a method called creatcomponents() where I am creating few text fields and buttons in a composite. Now I want to write listeners to the button which calls a method and in this method I have get the values of the text fields. The problem I am facing is I am unable to access textfields from the method called in the listeners. Can someone help me on how to acheive this?
One way is to save the controls are fields in your class:
public class MyClass
{
private Text text1;
private Text text2;
public void createComponents(Composite parent)
{
Composite composite = new Composite(parent, SWT.None);
text1 = new Text(composite, SWT.SINGLE);
text2 = new Text(composite, SWT.SINGLE);
text1.addModifyListener(new ModifyListener()
{
#Override
public void modifyText(ModifyEvent event)
{
// Access field
String text = text1.getText();
}
});
}
}
Also note that many of the event classes passed to listeners have a widget field which refers to the current control which you can also use:
text1.addModifyListener(new ModifyListener()
{
#Override
public void modifyText(ModifyEvent event)
{
Text control = (Text)event.widget;
String text = control.getText();
}
});

How can I observe the changed state of model items in an ObservableList?

I have an ObservableList of model items. The model item is enabled for property binding (the setter fires a property changed event). The list is the content provider to a TableViewer which allows cell editing. I also intend to add a way of adding new rows (model items) via the TableViewer so the number of items in the list may vary with time.
So far, so good.
As this is all within an eclipse editor, I would like to know when the model gets changed. I just need one changed event from any changed model item in order to set the editor 'dirty'. I guess I could attach some kind of listener to each individual list item object but I wonder if there is a clever way to do it.
I think that I might have a solution. The following class is an inline Text editor. Changes to the model bean (all instances) are picked up using the listener added in doCreateElementObservable. My eclipse editor just needs to add its' own change listener to be kept informed.
public class InlineEditingSupport extends ObservableValueEditingSupport
{
private CellEditor cellEditor;
private String property;
private DataBindingContext dbc;
IChangeListener changeListener = new IChangeListener()
{
#Override
public void handleChange(ChangeEvent event)
{
for (ITableEditorChangeListener listener : listenersChange)
{
listener.changed();
}
}
};
public InlineEditingSupport(ColumnViewer viewer, DataBindingContext dbc, String property)
{
super(viewer, dbc);
cellEditor = new TextCellEditor((Composite) viewer.getControl());
this.property = property;
this.dbc = dbc;
}
protected CellEditor getCellEditor(Object element)
{
return cellEditor;
}
#Override
protected IObservableValue doCreateCellEditorObservable(CellEditor cellEditor)
{
return SWTObservables.observeText(cellEditor.getControl(), SWT.Modify);
}
#Override
protected IObservableValue doCreateElementObservable(Object element, ViewerCell cell)
{
IObservableValue value = BeansObservables.observeValue(element, property);
value.addChangeListener(changeListener); // ADD THIS LINE TO GET CHANGE EVENTS
return value;
}
private List<ITableEditorChangeListener> listenersChange = new ArrayList<ITableEditorChangeListener>();
public void addChangeListener(ITableEditorChangeListener listener)
{
listenersChange.remove(listener);
listenersChange.add(listener);
}
public void removeChangeListener(ITableEditorChangeListener listener)
{
listenersChange.remove(listener);
}
}

How does one get the HTML attributes from a wicket componet?

I can't find a method that allows me to pull the attributes from a component, or better, one specific attribute. Here is an example:
I have two text areas (components).
Both are expandable
While typing in the first one, it auto expands with Ajax, and the height grows.
I get the height attribute from the first text area
I set the height of the second text area to height I have got from the first one.
public class HomePage extends WebPage {
TextArea t = new TextArea("m", "hey");;
TextArea t2 = new TextArea("m2",
"this is a label with label ones attributes added");
public HomePage() {
add(t.add(new AttributeModifier("height", "100;")));
add(t2.add(HERE ADD THE ATTRIBUE HEIGHT FROM LABEL);
}
My solution uses JavaScript for the height modification. t1 is the one that by typing is growing, t2 is the one that's height is being adjusted. Clean and nice i think.
private TextArea textarea1() {
TextArea t1 = new TextArea("t1", Model.of("t1"));
final String js = "document.getElementById('%s').style.height = document.getElementById('%s').style.height;";
t1.add(new OnChangeAjaxBehavior() {
#Override
protected void onUpdate(AjaxRequestTarget target) {
target.appendJavaScript(String.format(js, textarea2.getMarkupId(), textarea1.getMarkupId()));
}
});
t1.setOutputMarkupId(true);
return t1;
}
private TextArea textarea2() {
TextArea t2 = new TextArea("t2", Model.of("t2"));
t2.setOutputMarkupId(true);
return t2;
}
Earlier in your page/panel class declare private fields and assign them the two textarea's
private Textarea textarea1;
private Textarea textarea2;
in ctor:
textarea1 = textarea1();
textarea2 = textarea2();
add(textarea1);
add(textarea2);

dynamically select a checkbox for siblings treenode in Smart GWT

I have a selectable Tree with checkbox appearance. I need to select all sibling TreeNode on selection of a specific TreeNode.
I could get all the sibling tree nodes, but I don't know what is the attribute name of TreeNode to make that checkbox selected.
Can anybody help me giving some way to select those nodes.
compareGrid.addSelectionChangedHandler(new SelectionChangedHandler() {
#Override
public void onSelectionChanged(SelectionEvent event) {
TreeNode node = (TreeNode) event.getSelectedRecord();
TreeNode parent = tree.getParent(node);//tree is Tree object
treeGrid.selectRecord(parent);
TreeNode[] nodes = tree.getAllNodes(parent);
for(int i=0; i< nodes.length; i++){
if(!nodes[i].getAttributeAsBoolean("isSelected"))
treeGrid.selectRecord(nodes[i]);
}
}
}
});
You can use any of the following:
treeGrid.selectAllRecords();
treeGrid.selectRecord(record);
treeGrid.selectRecords(records);
The first method will select all the TreeNodes of the tree.
The 2nd one will select only one specified TreeNodes of the tree.
And the 3rd one will select multiple specified TreeNodes of the tree.
There are multiple overloaded methods for the last 2 methods, which allows you to specify Nodes in terms of, TreeNode(s) itself, or index of the TreeNode(s).
Here's a solution quite close (without checkboxes) to what you need.
employeeTreeGrid.addNodeClickHandler(new NodeClickHandler() {
public void onNodeClick(NodeClickEvent event) {
if (event.getNode() != null) {
TreeNode node = event.getNode();
TreeNode parent = employeeTree.getParent(node);
if (employeeTreeGrid.isSelected(node)) {
List<TreeNode> nodesToSelect = new ArrayList<TreeNode>();
// omit parent (root) if on first level
if (!"1".equals(node.getAttribute("ReportsTo"))) {
nodesToSelect.add(parent);
}
TreeNode[] siblings = employeeTree.getChildren(parent);
nodesToSelect.addAll(Arrays.asList(siblings));
RecordList recordList = employeeTreeGrid.getOriginalRecordList();
for (TreeNode treeNode : nodesToSelect) {
Record record = recordList.find("EmployeeId", treeNode.getAttribute("EmployeeId"));
if (record != null) {
employeeTreeGrid.selectRecord(record);
}
}
}
}
}
});
Have to use the RecordList and first find required records in order to use ListGrid.selectRecord() methods.
Using SelectionAppearance.CHECKBOX and SelectionChangedHandler can be tricky as programmatic selections are going to trigger further selection events.
This is based on Checkbox tree sample with below changes.
// employeeTreeGrid.setSelectionAppearance(SelectionAppearance.CHECKBOX);
// employeeTreeGrid.setShowSelectedStyle(false);
employeeTreeGrid.setShowPartialSelection(false);
// employeeTreeGrid.setCascadeSelection(true);
employeeTreeGrid.setSelectionType(SelectionStyle.SIMPLE);
To get the value of selected checkbox from tree grid in smart gwt I have following solution ListGridRecord[] arrRec = event.getSelection(); sample code is below.
employeeTreeGrid.setSelectionAppearance(SelectionAppearance.CHECKBOX);
employeeTreeGrid.setSelectionType(SelectionStyle.SIMPLE);
employeeTreeGrid.addSelectionChangedHandler(new SelectionChangedHandler() {
#Override
public void onSelectionChanged(SelectionEvent event)
//selectedCounties Set to add selected checkbox or deslected checkbox names/title
if (selectedCounties == null || selectedCounties.size() == 0)
selectedCounties = new TreeSet<String>();
selectedCounties.clear();
ListGridRecord[] arrRec = event.getSelection();
for (ListGridRecord listGridRecord : arrRec) {
selectedCounties.add(listGridRecord.getAttribute("Name"));
}
// You can do iteration over it if needed
selectedCounties.remove("All Counties");
Iterator<String> it = selectedCounties.iterator();
while (it.hasNext()) {
if (it.next().contains("Zone")) {
it.remove();
}
}
}
});