package de.dhbw.humbuch.view; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.NoSuchElementException; import com.google.common.eventbus.EventBus; import com.google.inject.Inject; import com.vaadin.data.Container; import com.vaadin.data.Property; import com.vaadin.data.Property.ValueChangeEvent; import com.vaadin.data.util.BeanItemContainer; import com.vaadin.event.ItemClickEvent; import com.vaadin.navigator.View; import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.Component; import com.vaadin.ui.DefaultFieldFactory; import com.vaadin.ui.Field; import com.vaadin.ui.FormLayout; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.PasswordField; import com.vaadin.ui.TabSheet; import com.vaadin.ui.TabSheet.SelectedTabChangeListener; import com.vaadin.ui.Table; import com.vaadin.ui.TabSheet.SelectedTabChangeEvent; import com.vaadin.ui.Table.CellStyleGenerator; import com.vaadin.ui.TextField; import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.Window; import de.davherrmann.mvvm.BasicState; import de.davherrmann.mvvm.State; import de.davherrmann.mvvm.StateChangeListener; import de.davherrmann.mvvm.ViewModelComposer; import de.davherrmann.mvvm.annotations.BindAction; import de.davherrmann.mvvm.annotations.BindState; import de.dhbw.humbuch.event.ConfirmEvent; import de.dhbw.humbuch.event.MessageEvent; import de.dhbw.humbuch.event.MessageEvent.Type; import de.dhbw.humbuch.model.entity.SchoolYear; import de.dhbw.humbuch.model.entity.Category; import de.dhbw.humbuch.model.entity.SettingsEntry; import de.dhbw.humbuch.viewmodel.SettingsViewModel; import de.dhbw.humbuch.viewmodel.SettingsViewModel.Categories; import de.dhbw.humbuch.viewmodel.SettingsViewModel.SettingsEntries; import de.dhbw.humbuch.viewmodel.SettingsViewModel.SchoolYears; import de.dhbw.humbuch.viewmodel.SettingsViewModel.ChangeStatus; import de.dhbw.humbuch.viewmodel.SettingsViewModel.UserEmail; import de.dhbw.humbuch.viewmodel.SettingsViewModel.UserName; import de.dhbw.humbuch.viewmodel.SettingsViewModel.DoUpdateUser; import de.dhbw.humbuch.viewmodel.SettingsViewModel.DoPasswordChange; import de.dhbw.humbuch.viewmodel.SettingsViewModel.PasswordChangeStatus; /** * Provides a {@link View} to manage the application settings. * @author Johannes Idelhauser */ @SuppressWarnings({"rawtypes", "serial"}) public class SettingsView extends VerticalLayout implements View, ViewInformation { private static final long serialVersionUID = 410136048295487570L; private static final String TITLE = "Einstellungen"; private static final String YEAR_YEAR = "year"; private static final String YEAR_FROM = "fromDate"; private static final String YEAR_TO = "toDate"; private static final String YEAR_END_FIRST = "endFirstTerm"; private static final String YEAR_BEGIN_SEC = "beginSecondTerm"; private static final String CAT_NAME = "name"; private static final String CAT_DESCRIPTION = "description"; private static final String SETTINGS_KEY = "settingKey"; private static final String SETTINGS_VALUE = "settingValue"; private static final String SETTINGS_STANDARD_VALUE = "settingStandardValue"; /** * User */ private TabSheet tabs; private Button btnChangePw = new Button("Kennwort ändern..."); @BindState(UserName.class) private State<String> userName = new BasicState<>(String.class); @BindState(UserEmail.class) private State<String> userEmail = new BasicState<>(String.class); private TextField txtUserName = new TextField("Nutzername:"); private TextField txtUserEmail = new TextField("E-Mail-Adresse: "); @BindAction(value = DoUpdateUser.class, source = { "txtUserName", "txtUserEmail" }) private Button userSaveBtn = new Button("Speichern"); private Window changePwWindow = new Window();; private PasswordField currentPassword = new PasswordField("Aktuelles Passwort:"); private PasswordField newPassword = new PasswordField("Neues Passwort:"); private PasswordField newPasswordVerified = new PasswordField("Neues Passwort wiederholen:"); @BindAction(value = DoPasswordChange.class, source = { "currentPassword", "newPassword", "newPasswordVerified" }) private Button changePwSave = new Button("Speichern"); private Button btnChangePwCancel = new Button("Abbrechen"); @BindState(PasswordChangeStatus.class) private State<ChangeStatus> passwordChangeStatus = new BasicState<>(ChangeStatus.class); /** * Due dates */ private Table yearTable; private BeanItemContainer<SchoolYear> yearData = new BeanItemContainer<SchoolYear>(SchoolYear.class); @BindState(SchoolYears.class) private State<Collection<SchoolYear>> schoolYears = new BasicState<>(Collection.class); private List<Field> yearFields = new ArrayList<Field>(); /** * Categories */ private Table categoryTable = new Table(); private BeanItemContainer<Category> categoryData = new BeanItemContainer<Category>(Category.class); @BindState(Categories.class) private State<Collection<Category>> categories = new BasicState<>(Collection.class); private List<Field> categoryFields = new ArrayList<Field>(); /** * Settings */ private Table settingsTable = new Table(); private BeanItemContainer<SettingsEntry> settingsData = new BeanItemContainer<SettingsEntry>(SettingsEntry.class); private List<Field> settingsFields = new ArrayList<Field>(); @BindState(SettingsEntries.class) private State<Collection<SettingsEntry>> settingsEntries = new BasicState<>(Collection.class); private EventBus eventBus; private SettingsViewModel settingsViewModel; @Inject public SettingsView(ViewModelComposer viewModelComposer, SettingsViewModel settingsViewModel, EventBus eventBus) { this.settingsViewModel = settingsViewModel; this.eventBus = eventBus; init(); bindViewModel(viewModelComposer, settingsViewModel); } /** * Initializes the {@link Tabsheet} */ public void init() { tabs = new TabSheet(); tabs.setSizeFull(); tabs.addTab(buildUserTab(), "Benutzer"); tabs.addTab(buildCategoryTab(), "Lehrmittelkategorien"); tabs.addTab(buildDueDatesTab(), "Schuljahre"); tabs.addTab(buildSettingsTab(), "Weitere Einstellungen"); addComponent(tabs); setMargin(true); setSizeFull(); } /** * Constructs the user tab of the {@link Tabsheet} * @return The {@link Component} with the constructed tab */ private Component buildUserTab() { FormLayout tab = new FormLayout(); tab.setMargin(true); tab.setSpacing(true); tab.addComponent(btnChangePw); txtUserName.setValue(userName.get()); tab.addComponent(txtUserName); userName.addStateChangeListener(new StateChangeListener() { @Override public void stateChange(Object arg0) { txtUserName.setValue(userName.get()); } }); txtUserEmail.setValue(userEmail.get()); tab.addComponent(txtUserEmail); userEmail.addStateChangeListener(new StateChangeListener() { @Override public void stateChange(Object arg0) { txtUserEmail.setValue(userEmail.get()); } }); userSaveBtn.addStyleName("default"); tab.addComponent(userSaveBtn); /* * Change password window */ VerticalLayout wContent = new VerticalLayout(); wContent.setMargin(true); wContent.setSpacing(true); currentPassword.setRequired(true); newPassword.setRequired(true); newPasswordVerified.setRequired(true); wContent.addComponent(currentPassword); wContent.addComponent(newPassword); wContent.addComponent(newPasswordVerified); HorizontalLayout changePwButtons = new HorizontalLayout(); wContent.addComponent(changePwButtons); wContent.setComponentAlignment(changePwButtons, Alignment.MIDDLE_RIGHT); changePwSave.addStyleName("default"); changePwButtons.addComponent(btnChangePwCancel); changePwButtons.addComponent(changePwSave); changePwButtons.setSpacing(true); changePwWindow.setContent(wContent); changePwWindow.setCaption("Passwort ändern"); changePwWindow.center(); changePwWindow.setClosable(false); changePwWindow.setResizable(false); changePwWindow.setModal(true); /** * Opens the password change window */ btnChangePw.addClickListener(new ClickListener() { private static final long serialVersionUID = 5691533989323693312L; @Override public void buttonClick(ClickEvent event) { UI.getCurrent().addWindow(changePwWindow); changePwWindow.focus(); } }); /** * Closes the password change window */ btnChangePwCancel.addClickListener(new ClickListener() { private static final long serialVersionUID = 5691533989323693312L; @Override public void buttonClick(ClickEvent event) { changePwWindow.close(); } }); /** * Reacts on states from the doPasswordChange state */ passwordChangeStatus.addStateChangeListener(new StateChangeListener() { @Override public void stateChange(Object arg0) { ChangeStatus status = passwordChangeStatus.get(); // Clear the password fields currentPassword.setValue(""); newPassword.setValue(""); newPasswordVerified.setValue(""); // Close the window if successful if (status == ChangeStatus.SUCCESSFULL) { changePwWindow.close(); } } }); return tab; } /** * Constructs the tab to manage categories * @return The {@link Component} with the constructed tab */ private Component buildCategoryTab() { VerticalLayout tab = new VerticalLayout(); tab.setMargin(true); tab.setSpacing(true); tab.setSizeFull(); final HorizontalLayout editButtons = new HorizontalLayout(); editButtons.setSpacing(true); final Button btnEdit = new Button("Bearbeiten"); final Button btnCancel = new Button("Abbrechen"); final Button btnSave = new Button("Speichern"); btnEdit.setEnabled(false); btnSave.setVisible(false); btnSave.addStyleName("default"); btnCancel.setVisible(false); editButtons.addComponent(btnEdit); editButtons.addComponent(btnCancel); editButtons.addComponent(btnSave); final HorizontalLayout addButtons = new HorizontalLayout(); addButtons.setSpacing(true); final Button btnAdd = new Button("Hinzufügen"); final Button btnDelete = new Button("Löschen"); btnDelete.setEnabled(false); addButtons.addComponent(btnAdd); addButtons.addComponent(btnDelete); tab.addComponent(new HorizontalLayout() { { setWidth("100%"); setHeight(null); addComponent(addButtons); addComponent(editButtons); setComponentAlignment(editButtons, Alignment.MIDDLE_RIGHT); } }); // Table categoryTable.setSizeFull(); categoryTable.setSelectable(true); categoryTable.setImmediate(true); categoryTable.setContainerDataSource(categoryData); categoryTable.setVisibleColumns(new Object[] { CAT_NAME, CAT_DESCRIPTION }); categoryTable.setColumnHeader(CAT_NAME, "Kategorie"); categoryTable.setColumnHeader(CAT_DESCRIPTION, "Beschreibung"); tab.addComponent(categoryTable); tab.setExpandRatio(categoryTable, 1); /* * Listeners */ // Enable/Disable edit button categoryTable.addValueChangeListener(new Property.ValueChangeListener() { @Override public void valueChange(ValueChangeEvent event) { Category item = (Category) categoryTable.getValue(); btnEdit.setEnabled(item != null); btnDelete.setEnabled(item != null); } }); // Edit btnEdit.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { configureEditable(categoryTable, new Button[]{btnSave, btnCancel}, new Button[]{btnEdit}, categoryFields, true); } }); // Cancel btnCancel.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { discardFields(categoryFields); configureEditable(categoryTable, new Button[]{btnSave, btnCancel}, new Button[]{btnEdit}, categoryFields, false); categories.notifyAllListeners(); } }); // Save btnSave.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { commitFields(categoryFields); Category item = (Category) categoryTable.getValue(); configureEditable(categoryTable, new Button[]{btnSave, btnCancel}, new Button[]{btnEdit}, categoryFields, false); settingsViewModel.doUpdateCategory(item); } }); // Add btnAdd.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { Category item = new Category.Builder("").description("") .build(); categoryTable.addItem(item); categoryTable.select(item); configureEditable(categoryTable, new Button[]{btnSave, btnCancel}, new Button[]{btnEdit}, categoryFields, true); } }); // Delete btnDelete.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { Runnable confirmRunnable = new Runnable() { @Override public void run() { Category item = (Category) categoryTable.getValue(); settingsViewModel.doDeleteCategory(item); configureEditable(categoryTable, new Button[]{btnSave, btnCancel}, new Button[]{btnEdit}, categoryFields, false); categoryTable.select(null); } }; eventBus.post(new ConfirmEvent.Builder("Wollen Sie diese Kategorie wirklich löschen?") .caption("Löschen").confirmRunnable(confirmRunnable).build()); } }); // Fill table container categories.addStateChangeListener(new StateChangeListener() { @Override public void stateChange(Object arg0) { categoryTable.removeAllItems(); categoryData.addAll(categories.get()); } }); // Double click on a row: make it editable categoryTable.addItemClickListener(new ItemClickEvent.ItemClickListener() { @Override public void itemClick(ItemClickEvent itemClickEvent) { if (itemClickEvent.isDoubleClick() && !categoryTable.isEditable()) { categoryTable.setValue(itemClickEvent.getItemId()); configureEditable(categoryTable, new Button[]{btnSave, btnCancel}, new Button[]{btnEdit}, categoryFields, true); } } }); // define field factory categoryTable.setTableFieldFactory(new DefaultFieldFactory() { private static final long serialVersionUID = 1L; @Override public Field<?> createField(Container container, Object itemId,Object propertyId, Component uiContext) { //if its not the currently selected item in the table, don't // generate fields if (!itemId.equals(categoryTable.getValue())) { return null; } TextField field = new TextField(); field.setNullRepresentation(""); //discard the value field.setBuffered(true); //keep track of all of the attached fields field.addAttachListener(new AttachListener() { @Override public void attach(AttachEvent attachEvent) { categoryFields.add((Field) attachEvent.getConnector()); } }); field.addDetachListener(new DetachListener() { @Override public void detach(DetachEvent event) { categoryFields.remove((Field) event.getConnector()); } }); return field; } }); return tab; } /** * Constructs the tab to manage school years * @return The {@link Component} with the constructed tab */ private Component buildDueDatesTab() { VerticalLayout tab = new VerticalLayout(); tab.setSizeFull(); tab.setMargin(true); tab.setSpacing(true); //Edit buttons final HorizontalLayout editButtons = new HorizontalLayout(); editButtons.setSpacing(true); final Button btnEdit = new Button("Bearbeiten"); final Button btnCancel = new Button("Abbrechen"); final Button btnSave = new Button("Speichern"); btnEdit.setEnabled(false); btnSave.setVisible(false); btnSave.addStyleName("default"); btnCancel.setVisible(false); editButtons.addComponent(btnEdit); editButtons.addComponent(btnCancel); editButtons.addComponent(btnSave); //Add/Delete buttons final HorizontalLayout addButtons = new HorizontalLayout(); addButtons.setSpacing(true); final Button btnAdd = new Button("Hinzufügen"); final Button btnDelete = new Button("Löschen"); btnDelete.setEnabled(false); addButtons.addComponent(btnAdd); addButtons.addComponent(btnDelete); tab.addComponent(new HorizontalLayout() { { setWidth("100%"); setHeight(null); addComponent(addButtons); addComponent(editButtons); setComponentAlignment(editButtons, Alignment.MIDDLE_RIGHT); } }); yearTable = new Table() { private static final long serialVersionUID = 1L; @Override protected String formatPropertyValue(Object rowId, Object colId, Property<?> property) { if (!colId.equals(YEAR_YEAR)) { SimpleDateFormat df = new SimpleDateFormat(); df.applyPattern("dd.MM.yyyy"); if (property.getValue() == null) { return null; } else { return df .format(((Date) property.getValue()).getTime()); } } return super.formatPropertyValue(rowId, colId, property); } }; yearTable.setSizeFull(); yearTable.setSelectable(true); yearTable.setImmediate(true); yearTable.setContainerDataSource(yearData); yearTable.setVisibleColumns(new Object[] { YEAR_YEAR, YEAR_FROM, YEAR_TO, YEAR_END_FIRST, YEAR_BEGIN_SEC }); yearTable.setColumnHeader(YEAR_YEAR, "Schuljahr"); yearTable.setColumnHeader(YEAR_FROM, "Beginn"); yearTable.setColumnHeader(YEAR_TO, "Ende"); yearTable.setColumnHeader(YEAR_END_FIRST, "Ende 1. Halbjahr"); yearTable.setColumnHeader(YEAR_BEGIN_SEC, "Begin 2. Halbjahr"); yearTable.setColumnAlignment(YEAR_FROM, Table.Align.RIGHT); yearTable.setColumnAlignment(YEAR_TO, Table.Align.RIGHT); yearTable.setColumnAlignment(YEAR_END_FIRST, Table.Align.RIGHT); yearTable.setColumnAlignment(YEAR_BEGIN_SEC, Table.Align.RIGHT); tab.addComponent(yearTable); tab.setExpandRatio(yearTable, 1); /* * Listeners */ // Enable/Disable edit + delete button yearTable.addValueChangeListener(new Property.ValueChangeListener() { @Override public void valueChange(ValueChangeEvent event) { SchoolYear item = (SchoolYear) yearTable.getValue(); btnEdit.setEnabled(item != null); btnDelete.setEnabled(item != null); } }); // Edit btnEdit.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { configureEditable(yearTable, new Button[]{btnCancel, btnSave}, new Button[]{btnEdit}, yearFields, true); } }); // Cancel btnCancel.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { discardFields(yearFields); configureEditable(yearTable, new Button[]{btnCancel, btnSave}, new Button[]{btnEdit}, yearFields, false); schoolYears.notifyAllListeners(); } }); // Save btnSave.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { commitFields(yearFields); SchoolYear item = (SchoolYear) yearTable.getValue(); if (item.getYear().isEmpty() || item.getFromDate() == null || item.getToDate() == null || item.getBeginSecondTerm() == null || item.getEndFirstTerm() == null) { eventBus.post(new MessageEvent("Speichern nicht möglich!", "Alle Felder müssen ausgefüllt sein.", Type.WARNING)); } else if (!item.getFromDate().before(item.getEndFirstTerm())) { eventBus.post(new MessageEvent("Speichern nicht möglich!", "Das Anfangsdatum muss vor dem Ende des 1. Halbjahres liegen.", Type.WARNING)); } else if (!item.getEndFirstTerm().before(item.getBeginSecondTerm())) { eventBus.post(new MessageEvent("Speichern nicht möglich!", "Das Enddatum des 1. Halbjahres muss vor dem Anfangsdatum des 2. Halbjahres liegen.", Type.WARNING)); } else if (!item.getBeginSecondTerm().before(item.getToDate())) { eventBus.post(new MessageEvent("Speichern nicht möglich!", "Das Anfangsdatum des 2. Halbjahres muss vor dem Enddatum des gesamten Schuljahres liegen.", Type.WARNING)); } else { configureEditable(yearTable, new Button[]{btnCancel, btnSave}, new Button[]{btnEdit}, yearFields, false); settingsViewModel.doUpdateSchoolYear(item); } } }); // Add btnAdd.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { SchoolYear item = new SchoolYear.Builder("", new Date(), new Date()).build(); yearData.addBean(item); yearTable.select(item); configureEditable(yearTable, new Button[]{btnCancel, btnSave}, new Button[]{btnEdit}, yearFields, true); } }); // Delete btnDelete.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { Runnable confirmRunnable = new Runnable() { @Override public void run() { SchoolYear item = (SchoolYear) yearTable.getValue(); settingsViewModel.doDeleteSchoolYear(item); configureEditable(yearTable, new Button[]{btnCancel, btnSave}, new Button[]{btnEdit}, yearFields, false); yearTable.select(null); } }; eventBus.post(new ConfirmEvent.Builder("Wollen Sie dieses Schuljahr wirklich löschen?") .caption("Löschen").confirmRunnable(confirmRunnable).build()); } }); // Fill table container schoolYears.addStateChangeListener(new StateChangeListener() { @Override public void stateChange(Object arg0) { yearTable.removeAllItems(); yearData.addAll(schoolYears.get()); } }); // Double click on a row: make it editable yearTable.addItemClickListener(new ItemClickEvent.ItemClickListener() { @Override public void itemClick(ItemClickEvent itemClickEvent) { if (itemClickEvent.isDoubleClick() && !yearTable.isEditable()) { yearTable.setValue(itemClickEvent.getItemId()); configureEditable(yearTable, new Button[]{btnCancel, btnSave}, new Button[]{btnEdit}, yearFields, true); } } }); // define field factory yearTable.setTableFieldFactory(new DefaultFieldFactory() { private static final long serialVersionUID = 1L; @Override public Field<?> createField(Container container, Object itemId, Object propertyId, Component uiContext) { // If its not the currently selected item in the table, don't // generate fields if (!itemId.equals(yearTable.getValue())) { return null; } Field field = super.createField(container, itemId, propertyId,uiContext); // Possibility to discard the value field.setBuffered(true); // keep track of all of the attached fields field.addAttachListener(new AttachListener() { @Override public void attach(AttachEvent attachEvent) { yearFields.add((Field) attachEvent.getConnector()); } }); field.addDetachListener(new DetachListener() { @Override public void detach(DetachEvent event) { yearFields.remove((Field) event.getConnector()); } }); return field; } }); return tab; } /** * Creates the general settings tab * @return A Component with the generated tab */ private Component buildSettingsTab() { /* * Initialize the tab */ final VerticalLayout tab = new VerticalLayout(); tab.setMargin(true); tab.setSpacing(true); tab.setSizeFull(); /* * Define edit, save and cancel buttons */ final HorizontalLayout editButtons = new HorizontalLayout(); editButtons.setSpacing(true); tab.addComponent(editButtons); tab.setComponentAlignment(editButtons, Alignment.MIDDLE_RIGHT); final Button btnEdit = new Button("Bearbeiten"); final Button btnSave = new Button("Speichern"); final Button btnCancel = new Button("Abbrechen"); editButtons.addComponent(btnEdit); editButtons.addComponent(btnCancel); editButtons.addComponent(btnSave); btnEdit.setEnabled(false); btnSave.setVisible(false); btnSave.addStyleName("default"); btnCancel.setVisible(false); /* * Define table */ settingsTable.setSizeFull(); settingsTable.setSelectable(true); settingsTable.setImmediate(true); settingsTable.setContainerDataSource(settingsData); settingsTable.setVisibleColumns(new Object[] { SETTINGS_KEY, SETTINGS_VALUE, SETTINGS_STANDARD_VALUE }); settingsTable.setColumnHeader(SETTINGS_KEY, "Schlüssel"); settingsTable.setColumnHeader(SETTINGS_VALUE, "Wert"); settingsTable.setColumnHeader(SETTINGS_STANDARD_VALUE, "Standardwert"); tab.addComponent(settingsTable); tab.setExpandRatio(settingsTable, 1); /* * Define listeners and factories */ //Warning that is displayed when settings tab is accessed tabs.addSelectedTabChangeListener(new SelectedTabChangeListener() { boolean preventEvent = false; @Override public void selectedTabChange(SelectedTabChangeEvent event) { if (preventEvent) { preventEvent = false; return; } final TabSheet source = (TabSheet) event.getSource(); if(source.getSelectedTab() == tab) { source.setSelectedTab(0); Runnable runnable = new Runnable() { @Override public void run() { preventEvent = true; source.setSelectedTab(tab); } }; eventBus.post(new ConfirmEvent.Builder( "Änderungen der Standardwerte können unter Umständen dazu führen, dass die Anwendung<br>" + "nicht mehr wie erwartet funktioniert. Sie sollten nur fortfahren, wenn Sie genau wissen,<br>" + "was Sie tun.") .caption("Achtung, hier endet möglicherweise die Gewährleistung!") .cancelCaption("Abbrechen") .confirmCaption("Ich werde vorsichtig sein, versprochen!").confirmRunnable(runnable) .build()); } } }); //Print cell bold if not equal to standard value settingsTable.setCellStyleGenerator(new CellStyleGenerator() { @Override public String getStyle(Table source, Object itemId, Object propertyId) { if (!source.getItem(itemId).getItemProperty(SETTINGS_VALUE).getValue() .equals(source.getItem(itemId).getItemProperty(SETTINGS_STANDARD_VALUE).getValue())) { if(SETTINGS_VALUE.equals(propertyId)) return "bold"; } return null; } }); //Edit button listeners btnEdit.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { configureEditable(settingsTable, new Button[]{btnSave, btnCancel}, new Button[]{btnEdit}, settingsFields, true); } }); //Cancel button listeners btnCancel.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { discardFields(settingsFields); configureEditable(settingsTable, new Button[]{btnSave, btnCancel}, new Button[]{btnEdit}, settingsFields, false); } }); //Save button listeners btnSave.addClickListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { commitFields(settingsFields); SettingsEntry item = (SettingsEntry) settingsTable.getValue(); configureEditable(settingsTable, new Button[]{btnSave, btnCancel}, new Button[]{btnEdit}, settingsFields, false); settingsViewModel.doUpdateSettingsEntry(item); } }); //Enable/Disable edit button settingsTable.addValueChangeListener(new Property.ValueChangeListener() { @Override public void valueChange(ValueChangeEvent event) { SettingsEntry item = (SettingsEntry) settingsTable.getValue(); btnEdit.setEnabled(item != null); } }); //Double click on a row to make it editable settingsTable.addItemClickListener(new ItemClickEvent.ItemClickListener() { @Override public void itemClick(ItemClickEvent itemClickEvent) { if (itemClickEvent.isDoubleClick() && !settingsTable.isEditable()) { settingsTable.setValue(itemClickEvent.getItemId()); configureEditable(settingsTable, new Button[]{btnSave, btnCancel}, new Button[]{btnEdit}, settingsFields, true); } } }); // Fill table container settingsEntries.addStateChangeListener(new StateChangeListener() { @Override public void stateChange(Object arg0) { settingsTable.removeAllItems(); settingsData.addAll(settingsEntries.get()); } }); // define field factory settingsTable.setTableFieldFactory(new DefaultFieldFactory() { private static final long serialVersionUID = 1L; @Override public Field<?> createField(Container container, Object itemId, Object propertyId, Component uiContext) { //if its not the currently selected item in the table, don't // generate fields if (!itemId.equals(settingsTable.getValue())) { return null; } if(!SETTINGS_VALUE.equals(propertyId)) { return null; } TextField field = new TextField(); field.setNullRepresentation(""); //discard the value field.setBuffered(true); //keep track of all of the attached fields field.addAttachListener(new AttachListener() { @Override public void attach(AttachEvent attachEvent) { settingsFields.add((Field) attachEvent.getConnector()); } }); field.addDetachListener(new DetachListener() { @Override public void detach(DetachEvent event) { settingsFields.remove((Field) event.getConnector()); } }); return field; } }); return tab; } /** * Configures the given table to be editable or not and changes the buttons visibility accordingly. * @param table The {@link Table} to be configured * @param editButtons {@link Button}s to be displayed when the table is editable * @param nonEditButtons {@link Button}s to be displayed when the table is not editable * @param fields {@link List} with generated {@link Field}s in row to edit * @param editable Whether the table should be editable or not */ public void configureEditable(Table table, Button[] editButtons, Button[] nonEditButtons, List<Field> fields, boolean editable) { //Set the table editable table.setSelectable(!editable); table.setEditable(editable); //Switch visibility of buttons for (Button btn : editButtons) { btn.setVisible(editable); } for (Button btn : nonEditButtons) { btn.setVisible(!editable); } //Set focus to the first field if (editable && !fields.isEmpty()) { fields.get(0).focus(); } } /** * Discard any field edits * @param fields {@link List} of fields */ protected void discardFields(List<Field> fields) { for (Field field : fields) { field.discard(); } } /** * Commit all field edits and handle handle validation problems here * @param fields {@link List} of fields to be committed */ protected void commitFields(List<Field> fields) { for (Field field : fields) { field.commit(); } } private void bindViewModel(ViewModelComposer viewModelComposer, Object... viewModels) { try { viewModelComposer.bind(this, viewModels); } catch (IllegalAccessException | NoSuchElementException | UnsupportedOperationException e) { e.printStackTrace(); } } @Override public void enter(ViewChangeEvent event) { settingsViewModel.refresh(); tabs.setSelectedTab(0); } @Override public String getTitle() { return TITLE; } }