I am facing some issues in eclipse rcp for setting the dropdown for a particular cell.
My requrement is to set the dropdown in the first row of the table. And that dropdown should be able to remove also.
One more thing that drop should be able to filter the contents in the table. So My question is that
1) Is it possible to add the dropdown only to the particular cell or row?
2) Can that filter act as a filter for the table?
3) How do I remove once I add the dropdown to the table cell?
Yes this is entirely possible. I suggest you start by reading Building and delivering a table editor with SWT/JFace, this tutorial contains everything you need to know.
As a rough outline, you will need to make the first item in your content model different from your data items - it will store the filter values. Then setup editing support on your TableViewerColumns something like (this is just a starter - this code will not work on its own):
tableViewerColumn.setEditingSupport(new EditingSupport(tableViewer)
{
#Override
protected boolean canEdit(Object element) {
if(object instanceof FilterDataObject) // your model object you are using to store the filter selections
{
return true;
}
}
#Override
protected CellEditor getCellEditor(Object element)
{
final ComboBoxCellEditor editor = new ComboBoxCellEditor(table, getPossibleFilterValues(), SWT.READ_ONLY);
((CCombo)editor.getControl()).addModifyListener(new ModifyListener()
{
public void modifyText(ModifyEvent e)
{
IStructuredSelection sel = (IStructuredSelection)m_tableViewer.getSelection();
FilterDataObject filterValue = (FilterDataObject)sel.getFirstElement();
// .. update the filter on your TableViewer
}
});
return editor;
}
#Override
protected Object getValue(Object element)
{
if(object instanceof FilterDataObject)
{
// get the filter value
}
else
{
// get your data model's value for this column
}
}
#Override
protected void setValue(Object element, Object value)
{
if(object instanceof FilterDataObject)
{
// update your FilterDataObject
}
}
});
Related
I am trying to change the color of the items on click when the action mode is active. The problem is that e.g if there are five items in a recyclerview and you click one, scroll down and select sixth item and destroy the action mode. The next time you start selecting, that sixth item has automatically changed its color without you selecting it. I don't know why it is happening.
public static List<ModelClass> items = new ArrayList<>();
boolean isSelectMode = false;
boolean isActionModeEnabled = false;
public static List<ModelClass> selectList = new ArrayList<>();
#Override
public void onBindViewHolder(#NonNull MyAdapter.MyViewHolder holder, int
position) {
holder.bind(items.get(position));
ModelClass modelClass = items.get(position);
if (modelClass.isChecked() && isActionModeEnabled){
holder.row.setBackgroundColor(Color.GREEN);
modelClass.setChecked(true);
} else {
holder.row.setBackgroundColor(Color.TRANSPARENT);
modelClass.setChecked(false);
}
}
public class MyViewHolder extends RecyclerView.ViewHolder{
public MyViewHolder(#NonNull View itemView) {
super(itemView);
row = itemView.findViewById(R.id.row);
public void bind(ModelClass model) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (isActionModeEnabled) {
isSelectMode = true;
s = items.get(getAdapterPosition());
if (!selectList.contains(s)){
selectList.add(s);
row.setBackgroundColor(Color.GREEN);
model.setChecked(true);
} else {
selectList.remove(s);
row.setBackgroundColor(Color.TRANSPARENT);
model.setChecked(false);
}
}
});
}
The problem is going to be in your view holder binding:
if (modelClass.isChecked() && isActionModeEnabled){
holder.row.setBackgroundColor(Color.GREEN);
modelClass.setChecked(true);
} else {
holder.row.setBackgroundColor(Color.TRANSPARENT);
modelClass.setChecked(false);
}
Remember that view holders are reused. That means that they will retain their internal state unless you change them. Your item list will also remember its state. Make sure you cover all the possible states of the item list and the reused view holders in the code above: You are probably missing a combination.
I recommend that you set a break point in the code above to make sure it is doing what you want. It should become obvious to you once you take a closer look.
I have a use case where a modal dialog has three set of Addresses of checkbox (Address A, Address B, Other Address) divided in separate divs for each address type for the user to select one address.
Each address type has their own set of TextFields for showing Name, House no, Street, City, Postal code and country.
So far so good. I was able to handle the selection of Address and pass the textfield values to the model. The model is getting reflected with the selected address.
On click of the "OK" button of Modal Window, (AjaxLink), there is a sub-panel (under another main panel, having all the address readonly textfields in it) which has to display the selected address type. But it isnt getting refresh. But the textfields model is having the updated values. (I have found this during debug)
Have tried everything on this site:
- using LoadableDetachableModel, setDefaultModel(), creating new instance of the Panel etc. but nothing is of use
i have also pasted the relevant code parts here. Request anyone who can guide me here as to what is going wrong and where I need to correct to get the sub-panel text fields refreshed on click of "OK' button of the modal.
thanks in advance
--ShipmentAddressSelectedPanel constructor Code
public ShipmentAddressSelectedPanel(String id, IModel<OrderDTO> orderDTOIModel) {
super(id, orderDTOIModel);
name = orderDTOIModel.getObject().getDeliveryAddress().getName();
streetAddress = subOrderDTOIModel.getObject().getDeliveryAddress().getStreet();
floor = orderDTOIModel.getObject().getDeliveryAddress().getFloor();
postalNumber = orderDTOIModel.getObject().getDeliveryAddress().getZipCode();
city = orderDTOIModel.getObject().getDeliveryAddress().getCity();
add(new TextField<>("name", new LoadableDetachableModel<String>() {
#Override
protected String load() {
return "" + name;
}
}));
add(new TextField<>("streetAddress", new LoadableDetachableModel<String>() {
#Override
protected String load() {
return "" + streetAddress;
}
}));
add(new TextField<>("floor", new LoadableDetachableModel<String>() {
#Override
protected String load() {
return "" + floor;
}
}));
//floor
add(new TextField<>("postalNumber", new LoadableDetachableModel<String>() {
#Override
protected String load() {
return "" + postalNumber;
}
}));
//postalNumber
add(new TextField<>("city", new LoadableDetachableModel<String>() {
#Override
protected String load() {
return "" + city;
}
}));
}
--code of onClick of AjaxLink ("Ok") button
add(new AjaxLink("saveAddress") {
#Override
public void onClick(final AjaxRequestTarget target) {
if (addressA.getValue().equals("true"))
{
onSaveAddress(target, getShipmentAddressModelFromDeliveryAddress(addressAModel.getObject()));
}
--Code of onSaveAddress where the logic refresh Panel is called from the main panel
#Override
public void onSaveAddress(AjaxRequestTarget target, ShipmentAddressModel shipmentAddressModel) {
ShipmentAddressSelectedPanel newShipmentAddressSelectedPanel = new ShipmentAddressSelectedPanel("showShipmentAddressSelected", orderDTOIModel);
newShipmentAddressSelectedPanel.setOutputMarkupId(true);
//newShipmentAddressSelectedPanel.setDefaultModelObject(orderDTOIModel);
target.add(newShipmentAddressSelectedPanel);
showAddressModal.close(target);
}
You're creating a new ShipmentAddressSelectedPanel, but it doesn't get added to the component tree.
Note that AjaxRequestTarget skips components, which are not inside the current page. You should have a log entry:
"Component .. with markupid: .. not rendered because it was already removed from page"
Note your usage of LDM is wrong, it should be:
//name = orderDTOIModel.getObject().getDeliveryAddress().getName();
add(new TextField<>("name", new LoadableDetachableModel<String>() {
#Override
protected String load() {
return orderDTOIModel.getObject().getDeliveryAddress().getName();
}
}));
Otherwise you'll pull out the name once only and never update when the model has changed.
If I have for example one master view on the left and one in the middle, each showing oder Java Beans/POJOs, can I use a shared detail view that somehow listens to the active beans of each view and then displays the currently selected one in more detail? A one to one relation is quite easy to manage by using your Context library.
#ViewDocking(areaId ="left", position=1, displayName="Profiles", menuEntry = #WindowMenuEntry(path = "", position=0), accelerator="Shortcut+1")
public class ProfileListView extends BorderPane implements LocalContextProvider {
private final SimpleContextContent content = new SimpleContextContent();
private final SimpleContext context = new SimpleContext(content);
#FXML
private ListView<Profile> listview;
public ProfileListView() {
load();
// add some profiles
listview.getItems().add(new Profile("Profile1"));
listview.getItems().add(new Profile("Profile2"));
listview.getItems().add(new Profile("Profile3"));
// setup selection listener
listview.getSelectionModel().selectedItemProperty().addListener((value, oldProfile, newProfile) -> {
// set active profile and remove old one
content.remove(oldProfile);
content.add(newProfile);
});
// setup double click listener
configureClickListener();
}
private Profile getSelectedProfile() {
return listview.getSelectionModel().getSelectedItem();
}
private void configureClickListener() {
listview.setOnMouseClicked(event -> {
// check if it was a double click
if(event.getClickCount() == 2) {
System.out.println(getSelectedProfile());
// inject into editor pane
// calls the procedure to create a tab in the center area...
}
});
}
private void load() {
FXMLLoaders.loadRoot(this);
}
#Override
public Context getLocalContext() {
return context;
}
}
This is one master view holding a list view of items.
The other one would be the same, docking to the right as another tab and holding POJOs of type 'Action'.
The detail view is here:
#ViewDocking(areaId = "right", displayName = "Properties", accelerator = "Shortcut+2", menuEntry = #WindowMenuEntry(path = "", position = 0), position = 1)
public class ProfilePropertiesView extends BorderPane implements LocalContextProvider, ActiveContextSensitive {
private Context activeContext;
private SimpleContextContent content = new SimpleContextContent();
private SimpleContext context = new SimpleContext(content);
private Profile profile;
private IWindowService service = new NullWindowService();
#FXML
private PropertySheet propertysheet;
public ProfilePropertiesView() {
load();
// retrieve framework service, TODO: use tracker
BundleContext ctx = FrameworkUtil.getBundle(getClass()).getBundleContext();
service = ctx.getService(ctx.getServiceReference(IWindowService.class));
// initialize callback
service.addCallback(title -> {
System.out.println("callback called " + title);
// update the property sheet ui by re-creating the items list
// updateUI();
// we can safely return null
return null;
});
// configure editor factory so the user is able to use a combobox
propertysheet.setPropertyEditorFactory(new CustomPropertyEditorFactory(service));
}
private void load() {
FXMLLoaders.loadRoot(this);
}
#Override
public Context getLocalContext() {
return context;
}
private void contextChanged() {
// find profile information
Profile found = activeContext.find(Profile.class);
// if the found profile is null, ignore it
if (found != null) {
// reset if profile is valid
if (profile != null) {
reset();
}
// create reference and register
profile = found;
register();
}
}
private void register() {
// retrieve observablelist of bean properties if some profile is selected
if(profile != null) {
ObservableList<Item> items = createDetailedList(profile);
propertysheet.getItems().setAll(items);
}
}
private void updateUI() {
// clear property elements and re-create them
reset();
// re-create items
ObservableList<Item> items = createDetailedList(profile);
propertysheet.getItems().addAll(items);
}
private ObservableList<Item> createDetailedList(Object bean) {
ObservableList<Item> list = FXCollections.observableArrayList();
try {
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass(), Object.class);
Arrays.stream(beanInfo.getPropertyDescriptors()).map(pd -> new DetailedBeanProperty(bean, pd)).forEach(list::add);
} catch (IntrospectionException e) {
e.printStackTrace();
}
return list;
}
private void reset() {
propertysheet.getItems().clear();
}
#Override
public void setActiveContext(Context activeContext) {
this.activeContext = activeContext;
this.activeContext.addContextListener(Profile.class, event -> contextChanged());
// trigger change
contextChanged();
}
}
The current ProfilePropertiesView is just configured to display the properties of the selected profile. I want it to be able to display the current information of the last selected POJO in the UI. That means that if the user selected a Profile from the ListView, that profile should be displayed in the properties view. If he selected an Action from the Table (which is displayed in the center), the properties of the Action should be displayed.
Do I just need to register a new ContextListener for the Action.class
POJO and then call a method to populate the PropertiesView? I was
unsure if this is the right solution...
Yes, just add another ContextListener to the activeContext for every POJO type you want to observe.
Also note that in the constructor of views it's better to use a ServiceTracker instead of looking for the service via BundleContext as the service might not be available yet, depending on the order the bundles are loaded.
You can find a sample which uses a ServiceTracker here: https://stackoverflow.com/a/35974498/506855
It may seem the question is the duplicate of this. But my question is i have developed a integer textfield in JavaFX by two ways. The code is given below
public class FXNDigitsField extends TextField
{
private long m_digit;
public FXNDigitsField()
{
super();
}
public FXNDigitsField(long number)
{
super();
this.m_digit = number;
onInitialization();
}
private void onInitialization()
{
setText(Long.toString(this.m_digit));
}
#Override
public void replaceText(int startIndex, int endIndex, String text)
{
if (text.matches(Constants.DIGITS_PATTERN) || text.equals(Constants.EMPTY_STRING)) {
super.replaceText(startIndex, endIndex, text);
}
}
#Override
public void replaceSelection(String text)
{
if (text.matches(Constants.DIGITS_PATTERN) || text.equals(Constants.EMPTY_STRING)) {
super.replaceSelection(text);
}
}
}
And the second way is by adding an event Filter.
The code snippet is given.
// restrict key input to numerals.
this.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() {
#Override public void handle(KeyEvent keyEvent) {
if (!"0123456789".contains(keyEvent.getCharacter())) {
keyEvent.consume();
}
}
});
My question is which is the slandered way to do this? Can anyone help me to pick up the right?
The best way to add validation in TextField is a 3rd way.
This method lets TextField finish all processing (copy/paste/undo safe).
It does not require you to extend the TextField class.
And it allows you to decide what to do with new text after every change
(to push it to logic, or turn back to previous value, or even to modify it).
// fired by every text property changes
textField.textProperty().addListener(
(observable, oldValue, newValue) -> {
// Your validation rules, anything you like
// (! note 1 !) make sure that empty string (newValue.equals(""))
// or initial text is always valid
// to prevent inifinity cycle
// do whatever you want with newValue
// If newValue is not valid for your rules
((StringProperty)observable).setValue(oldValue);
// (! note 2 !) do not bind textProperty (textProperty().bind(someProperty))
// to anything in your code. TextProperty implementation
// of StringProperty in TextFieldControl
// will throw RuntimeException in this case on setValue(string) call.
// Or catch and handle this exception.
// If you want to change something in text
// When it is valid for you with some changes that can be automated.
// For example change it to upper case
((StringProperty)observable).setValue(newValue.toUpperCase());
}
);
JavaFX has a class TextFormatter for this use-case.
It allows you to validate and adjust the text content before it is "commited" to the textProperty of the TextField.
See this example:
TextFormatter<String> textFormatter = new TextFormatter<>(change -> {
if (!change.isContentChange()) {
return change;
}
String text = change.getControlNewText();
if (isValid(text)) { // your validation logic
return null;
}
return change;
});
textField.setTextFormatter(textFormatter);
In both of your ways, you are not allowed to type characters other then numeric characters. But it will allow to paste any character there (Copy Text from any source and Paste in your TextField).
A good way to do validation is after submitting it,
Like (For integers):
try {
Integer.parseInt(myNumField.getText());
} catch(Exception e) {
System.out.println("Non-numeric character exist");
}
(or you can use any combination of yours + the above method)
Similar of what Manuel Mauky posted, but in groovy is:
Note: This will prevent any other character except for digits to be input.
def digitsOnlyOperator = new UnaryOperator<TextFormatter.Change>() {
#Override
TextFormatter.Change apply(TextFormatter.Change change) {
return !change.contentChange || change.controlNewText.isNumber() ? change : null
}
}
textField.textFormatter = new TextFormatter<>(digitsOnlyOperator)
There is probably a shorter way to do it in groovy. If you know it, please post it here.
This code snippet allows just digits to be entered by the user in a TextField.
/**
* This will check whether the incoming data from the user is valid.
*/
UnaryOperator<TextFormatter.Change> numberValidationFormatter = change -> {
if (change.getText().matches("\\d+")) {
return change; //if change is a number
} else {
change.setText(""); //else make no change
change.setRange( //don't remove any selected text either.
change.getRangeStart(),
change.getRangeStart()
);
return change;
}
};
TextFormatter tf = new TextFormatter(numberValidationFormatter);
textfield.setTextFormatter(tf);
How can I find the value of an attribute? I need to check the value and set the textbox maxlength to that value. Here is an example of the value I'm looking to retrieve.
public class DogClass
{
[StringLength(5)]
public string LegalName
{
}
You can use reflection to get this info. Below is a snippet that should get you started.
protected void GetStringLength(object objDog) {
// loop through each property in object
foreach (PropertyInfo pi in objDog.GetType().GetProperties())
{
// for each object property, get the SringLength tag (if there is one)
foreach (Attribute attribute in Attribute.GetCustomAttributes(pi, typeof(StringLengthAttribute), true))
{
// we'll assume there is only one
var stringLenVal = (attribute as StringLengthAttribute).MaximumLength;
break;
}
}
}