package org.aplikator.client.local.widgets; import static org.aplikator.client.local.widgets.DateFieldWidget.mapLanguageToAplikatorLocale; import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; import org.aplikator.client.local.Aplikator; import org.aplikator.client.local.SettingsStorage; import org.aplikator.client.local.wizards.WizardSupport; import org.aplikator.client.local.wizards.WizardSupportCallbacks; import org.aplikator.client.shared.data.ClientContext; import org.aplikator.client.shared.data.Operation; import org.aplikator.client.shared.data.PrimaryKey; import org.aplikator.client.shared.data.RecordContainerDTO; import org.aplikator.client.shared.data.RecordDTO; import org.aplikator.client.shared.data.SearchResult; import org.aplikator.client.shared.descriptor.FunctionDTO; import org.aplikator.client.shared.descriptor.PropertyDTO; import org.aplikator.client.shared.descriptor.QueryDescriptorDTO; import org.aplikator.client.shared.descriptor.QueryParameterDTO; import org.aplikator.client.shared.descriptor.RecordsPageDTO; import org.aplikator.client.shared.descriptor.SortDescriptorDTO; import org.aplikator.client.shared.descriptor.ViewDTO; import org.aplikator.client.shared.descriptor.WizardPageDTO; import org.aplikator.client.shared.rpc.AplikatorErrorCallback; import org.aplikator.client.shared.rpc.AplikatorException; import org.aplikator.client.shared.rpc.AplikatorService; import org.gwtbootstrap3.client.shared.event.ModalShownEvent; import org.gwtbootstrap3.client.shared.event.ModalShownHandler; import org.gwtbootstrap3.client.ui.*; import org.gwtbootstrap3.client.ui.constants.FormType; import org.gwtbootstrap3.client.ui.constants.IconType; import org.gwtbootstrap3.client.ui.constants.ModalBackdrop; import org.gwtbootstrap3.client.ui.gwt.CellTable; import org.gwtbootstrap3.client.ui.html.Div; import org.gwtbootstrap3.client.ui.html.Span; import org.gwtbootstrap3.client.ui.html.Strong; import org.gwtbootstrap3.extras.datetimepicker.client.ui.DateTimePicker; import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.DateTimePickerView; import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.events.ChangeDateEvent; import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.events.ChangeDateHandler; import org.jboss.errai.common.client.api.RemoteCallback; import org.jboss.errai.enterprise.client.jaxrs.api.RestClient; import com.google.gwt.cell.client.AbstractCell; import com.google.gwt.cell.client.TextCell; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.dom.client.BrowserEvents; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.event.dom.client.ScrollEvent; import com.google.gwt.event.dom.client.ScrollHandler; import com.google.gwt.event.logical.shared.ResizeEvent; import com.google.gwt.event.logical.shared.ResizeHandler; import com.google.gwt.i18n.client.NumberFormat; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.cellview.client.AbstractPager; import com.google.gwt.user.cellview.client.CellList; import com.google.gwt.user.cellview.client.HasKeyboardPagingPolicy; import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy; import com.google.gwt.user.cellview.client.SimplePager; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.DeckPanel; import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.Widget; import com.google.gwt.view.client.AsyncDataProvider; import com.google.gwt.view.client.CellPreviewEvent; import com.google.gwt.view.client.DefaultSelectionEventManager; import com.google.gwt.view.client.HasData; import com.google.gwt.view.client.HasRows; import com.google.gwt.view.client.Range; import com.google.gwt.view.client.SelectionChangeEvent; import com.google.gwt.view.client.SingleSelectionModel; public class TableListerWidget extends Composite implements TableInterface { public static final int TABLE_NAVBAR_HEIGHT = 52; private static final int MAX_TABLE_STRING_LENGTH = 80; private static Logger LOG = Logger.getLogger(TableListerWidget.class.getName()); private static TableListerWidgetUiBinder uiBinder = GWT.create(TableListerWidgetUiBinder.class); static { TableListerWidgetResources.INSTANCE.cellListStyle().ensureInjected(); } private final CellList<RecordDTO> cellList; private final SingleSelectionModel<RecordDTO> listSelectionModel; private final ShowMorePagerPanel listPagerPanel; private final ScrollPanel listScroller; private final ScrollPanel formScroller; private final AplikatorDataProvider listDataProvider; private final SingleSelectionModel<RecordDTO> gridSelectionModel; private final ScrollPanel gridScroller; private final GridDataProvider gridDataProvider; private final ViewDTO view; private final String ownerProperty; @UiField Navbar navbar; @UiField Strong title; @UiField ButtonGroup switchTableList; @UiField RadioButton buttonList; @UiField RadioButton buttonTable; @UiField Row listContainer; @UiField Row gridContainer; @UiField NavbarForm queryControls; @UiField ListBox querySelector; @UiField FormLabel queryLabel; @UiField NavbarForm sortControls; @UiField ListBox sortSelector; @UiField FormLabel sortLabel; @UiField NavbarForm queryParameters; @UiField TextBox queryTextField; @UiField ListBox queryListField; @UiField DateTimePicker queryDateField; @UiField Button buttonQuery; @UiField Button buttonPopupSearch; @UiField TextBox searchField; @UiField Button buttonSearch; @UiField Modal searchModal; @UiField Button buttonCreate; @UiField Button buttonDelete; @UiField Button buttonReload; @UiField Button buttonCancel; @UiField Button buttonSave; @UiField Button buttonFunction; @UiField ListDropDown menuFunction; @UiField DropDownMenu menuFunctionItems; @UiField NavbarText rangeLabelHolder; RangeLabelPager listPager; SimplePager gridPager; @UiField Column listColumn; @UiField Column formColumn; @UiField Column gridColumn; CellTable<RecordDTO> grid; Modal formPopup; FormWidget formForGrid; RecordContainerDTO recordContainerDTO; private NestedEditorNotification nestedEditorNotification = null; private FormWidget form; private DeckPanel formHolder; private Label noForm; private boolean editingNewRecord = false; private ListMode listMode = ListMode.STANDARD; private DisplayMode displayMode = DisplayMode.LIST; private int pageSize = 0; private String indexSearchArgument; private RecordDTO listSelectedRecordDTO = null; private RecordDTO gridSelectedRecordDTO = null; private int recordCount = -1; private PrimaryKey ownerPrimaryKey; private HasFields ownerForm; private boolean enabled = true; private boolean initialized = false; @SuppressWarnings("GWTStyleCheck") public TableListerWidget(ViewDTO view, PropertyDTO ownerProperty, HasFields ownerForm, int availableHeight) { super(); this.view = view; this.pageSize = view.getPageSize(); this.ownerProperty = (ownerProperty != null) ? ownerProperty.getId() : null; this.ownerForm = ownerForm; recordContainerDTO = new RecordContainerDTO(); initWidget(uiBinder.createAndBindUi(this)); //top navigation panel if (ownerProperty != null) { navbar.addStyleName("nestedTableNavbar"); } //title title.setText(this.view.getLocalizedName()); //buttons LayoutUtils.addTooltip(buttonList, Aplikator.application.getConfigString("aplikator.table.showList")); LayoutUtils.addTooltip(buttonTable, Aplikator.application.getConfigString("aplikator.table.showGrid")); LayoutUtils.addTooltip(buttonReload, Aplikator.application.getConfigString("aplikator.table.reload")); LayoutUtils.addTooltip(buttonCreate, Aplikator.application.getConfigString("aplikator.table.create")); LayoutUtils.addTooltip(buttonDelete, Aplikator.application.getConfigString("aplikator.table.delete")); LayoutUtils.addTooltip(buttonCancel, Aplikator.application.getConfigString("aplikator.table.cancel")); LayoutUtils.addTooltip(buttonSave, Aplikator.application.getConfigString("aplikator.table.save")); //sort selector for (SortDescriptorDTO sd : this.view.getSortDescriptors()) { String sortLabel = sd.getLocalizedName(); if (sd.isPrimaryAscending()) { sortLabel = "\u2191 " + sortLabel; } else { sortLabel = "\u2193 " + sortLabel; } sortSelector.addItem(sortLabel, sd.getId()); } final String searchPopupTitlePrefix = Aplikator.application.getConfigString("aplikator.table.searchPopupPrefix"); sortSelector.addChangeHandler(new ChangeHandler() { @Override public void onChange(ChangeEvent event) { buttonPopupSearch.setEnabled(isSelectedSortScrollable()); SortDescriptorDTO activeSort = getSelectedSort(); TableListerWidget.this.view.setActiveSort(activeSort.getId()); searchModal.setTitle(searchPopupTitlePrefix + " " + activeSort.getLocalizedName()); SettingsStorage.storeSetting(TableListerWidget.this.view.getId() + SettingsStorage.ACTIVE_SORT, activeSort.getId()); reload(); } }); if (this.view.getSortDescriptors().size() > 0) { String storedSort = SettingsStorage.getSetting(TableListerWidget.this.view.getId() + SettingsStorage.ACTIVE_SORT); int storedSortIndex = TableListerWidget.this.view.getSortIndex(storedSort); sortSelector.setSelectedIndex(-1); sortSelector.setSelectedIndex(storedSortIndex); TableListerWidget.this.view.setActiveSort(TableListerWidget.this.view.getSortDescriptors().get(storedSortIndex).getId()); searchModal.setTitle(searchPopupTitlePrefix + " " + TableListerWidget.this.view.getSortDescriptors().get(storedSortIndex).getLocalizedName()); } sortLabel.setText(Aplikator.application.getConfigString("aplikator.table.sort")); sortControls.setVisible(this.view.getSortDescriptors().size() > 1); //search field and button buttonPopupSearch.setEnabled(false); //searchModal.getWidget(0).addStyleName("app-mg-right-15"); searchModal.getWidget(0).addStyleName("app-mg-top-94"); searchModal.addShownHandler(new ModalShownHandler() { @Override public void onShown(ModalShownEvent evt) { searchField.setFocus(true); } }); buttonPopupSearch.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { searchModal.show(); } }); buttonSearch.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { searchModal.hide(); TableListerWidget.this.sqlSearch(); } }); searchField.addKeyUpHandler(new KeyUpHandler() { @Override public void onKeyUp(KeyUpEvent event) { event.stopPropagation(); if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { searchModal.hide(); TableListerWidget.this.sqlSearch(); } } }); //filter selector queryDateField.setFormat("dd.MM.yyyy"); queryDateField.setMinView(DateTimePickerView.MONTH); queryDateField.setAutoClose(true); for (QueryDescriptorDTO qd : this.view.getQueryDescriptors()) { querySelector.addItem(qd.getLocalizedName(), qd.getId()); } querySelector.addChangeHandler(new ChangeHandler() { @Override public void onChange(ChangeEvent event) { enableQueryParameterFields(true); } }); buttonQuery.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { reload(); } }); queryTextField.addKeyUpHandler(new KeyUpHandler() { @Override public void onKeyUp(KeyUpEvent event) { event.stopPropagation(); if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { reload(); } } }); queryListField.addChangeHandler(new ChangeHandler() { @Override public void onChange(ChangeEvent event) { event.stopPropagation(); reload(); } }); queryDateField.addChangeDateHandler(new ChangeDateHandler() { @Override public void onChangeDate(ChangeDateEvent evt) { reload(); } }); if (this.view.getQueryDescriptors().size() > 0) { String storedFilter = SettingsStorage.getSetting(TableListerWidget.this.view.getId() + SettingsStorage.ACTIVE_FILTER); int storedFilterIndex = TableListerWidget.this.view.getQueryIndex(storedFilter); querySelector.setSelectedIndex(-1); querySelector.setSelectedIndex(storedFilterIndex); //querySelector.setItemSelected(querySelector.getSelectedIndex(), false); TableListerWidget.this.view.setActiveFilter(TableListerWidget.this.view.getQueryDescriptors().get(storedFilterIndex).getId()); } queryLabel.setText(Aplikator.application.getConfigString("aplikator.table.filter")); queryControls.setVisible(this.view.getQueryDescriptors().size() > 1); queryParameters.setVisible(TableListerWidget.this.view.getQueryDescriptors().get(querySelector.getSelectedIndex()).getQueryParameters().size() == 1); if (view.getFunctions().size() == 1) { buttonFunction.setVisible(true); buttonFunction.setEnabled(true); buttonFunction.setText(view.getFunctions().get(0).getLocalizedName()); ClientContext clientContext = new ClientContext(); clientContext.setUser(Aplikator.application.getUsername()); clientContext.setLocale(Aplikator.application.getLocale()); clientContext.setView(view); final WizardSupport wizardSupport = new WizardSupport(view.getFunctions().get(0), clientContext, new WizardSupportCallbacks() { @Override public void wizardPageCallback(WizardPageDTO response, boolean forw) { } @Override public void runBeforeFunctionCallback() { buttonFunction.setEnabled(false); } @Override public void runAfterFunctionCallback() { buttonFunction.setEnabled(true); } }); buttonFunction.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { wizardSupport.changePageRequest(true); } }); } else if (view.getFunctions().size() > 1) { menuFunction.setVisible(true); for (FunctionDTO functionDTO : view.getFunctions()) { final AnchorListItem functionHandle = new AnchorListItem(functionDTO.getLocalizedName()); menuFunctionItems.add(functionHandle); ClientContext clientContext = new ClientContext(); clientContext.setUser(Aplikator.application.getUsername()); clientContext.setLocale(Aplikator.application.getLocale()); clientContext.setView(view); final WizardSupport wizardSupport = new WizardSupport(functionDTO, clientContext, new WizardSupportCallbacks() { @Override public void wizardPageCallback(WizardPageDTO response, boolean forw) { } @Override public void runBeforeFunctionCallback() { functionHandle.setEnabled(false); } @Override public void runAfterFunctionCallback() { functionHandle.setEnabled(true); } }); functionHandle.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { wizardSupport.changePageRequest(true); } }); } } listPager = new RangeLabelPager(); gridPager = new SimplePager(SimplePager.TextLocation.CENTER, false, false) { @Override protected String createText() { // Default text is 1 based. NumberFormat formatter = NumberFormat.getFormat("#,###"); HasRows display = getDisplay(); Range range = display.getVisibleRange(); int pageStart = range.getStart() + 1; int pageSize = range.getLength(); int dataSize = display.getRowCount(); int endIndex = Math.min(dataSize, pageStart + pageSize - 1); endIndex = Math.max(pageStart, endIndex); boolean exact = display.isRowCountExact(); return formatter.format(pageStart) + "-" + formatter.format(endIndex) + (exact ? " : " : " : ") + formatter.format(dataSize); } }; rangeLabelHolder.add(listPager); rangeLabelHolder.add(gridPager); // scrollable cellList cellList = new CellList<RecordDTO>(new RecordCell(), TableListerWidgetResources.INSTANCE, RecordDTO.KEY_PROVIDER); cellList.setPageSize(pageSize); listSelectionModel = new SingleSelectionModel<RecordDTO>(RecordDTO.KEY_PROVIDER); cellList.setSelectionModel(listSelectionModel, DefaultSelectionEventManager.createCustomManager(createVetoTranslator())); cellList.setKeyboardPagingPolicy(HasKeyboardPagingPolicy.KeyboardPagingPolicy.CHANGE_PAGE); cellList.setKeyboardSelectionPolicy(HasKeyboardSelectionPolicy.KeyboardSelectionPolicy.ENABLED); cellList.setLoadingIndicator(new LoadingLabel()); cellList.setEmptyListWidget(new EmptyLabel()); listSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() { public void onSelectionChange(SelectionChangeEvent event) { if (listSelectionModel.getSelectedObject() != null) { listSelectedRecordDTO = listSelectionModel.getSelectedObject(); formHolder.showWidget(1); if (!editingNewRecord) { openForm(); } } else { listSelectedRecordDTO = null; formHolder.showWidget(0); } } }); final int SCROLL_OFFSET = Aplikator.application.isShowNavigation() ? Aplikator.MAINMENUBAR_HEIGHT + TABLE_NAVBAR_HEIGHT : TABLE_NAVBAR_HEIGHT; final int scrollHeight = (availableHeight > 0 ? availableHeight + TABLE_NAVBAR_HEIGHT + SCROLL_OFFSET : Window.getClientHeight()) - SCROLL_OFFSET; listScroller = new ScrollPanel(); listScroller.getElement().getStyle().setHeight(scrollHeight, Unit.PX); listPagerPanel = new ShowMorePagerPanel(listScroller); listPagerPanel.setIncrementSize(pageSize); listPagerPanel.setDisplay(cellList); listPager.setDisplay(cellList); listDataProvider = new AplikatorDataProvider(listScroller, pageSize); listDataProvider.addDataDisplay(cellList); form = new FormWidget(this.view, this, this.ownerForm); form.addStyleName("app-mg-top-5"); form.addStyleName("app-row-divider"); noForm = new Label(Aplikator.application.getConfigString("aplikator.table.noselect")); formHolder = new DeckPanel(); formHolder.add(noForm); formHolder.add(form); formHolder.showWidget(0); formScroller = new ScrollPanel(formHolder); formScroller.addStyleName("formscroller"); formScroller.getElement().getStyle().setHeight(scrollHeight, Unit.PX); formPopup = createFormPopup(); grid = new CellTable<RecordDTO>(pageSize, RecordDTO.KEY_PROVIDER); grid.setWidth("100%"); gridSelectionModel = new SingleSelectionModel<RecordDTO>(RecordDTO.KEY_PROVIDER); grid.setSelectionModel(gridSelectionModel/*, DefaultSelectionEventManager.createCustomManager(createVetoTranslator())*/); grid.setKeyboardPagingPolicy(HasKeyboardPagingPolicy.KeyboardPagingPolicy.CHANGE_PAGE); grid.setKeyboardSelectionPolicy(HasKeyboardSelectionPolicy.KeyboardSelectionPolicy.BOUND_TO_SELECTION); grid.setLoadingIndicator(new LoadingLabel()); grid.setEmptyTableWidget(new EmptyLabel()); gridSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() { public void onSelectionChange(SelectionChangeEvent event) { if (gridSelectionModel.getSelectedObject() != null) { gridSelectedRecordDTO = gridSelectionModel.getSelectedObject(); } else { gridSelectedRecordDTO = null; } } }); for (final PropertyDTO propertyDTO : view.getProperties()) { grid.addColumn(new com.google.gwt.user.cellview.client.Column<RecordDTO, String>(new TextCell()) { @Override public String getValue(RecordDTO row) { String retval = propertyDTO.getStringValue(row); if (retval.length() <= MAX_TABLE_STRING_LENGTH) { return retval; } else { return retval.substring(0, MAX_TABLE_STRING_LENGTH) + "..."; } } }, propertyDTO.getLocalizedName()); } grid.addCellPreviewHandler(new CellPreviewEvent.Handler<RecordDTO>() { long lastClick = -1000; @Override public void onCellPreview(CellPreviewEvent<RecordDTO> event) { long clictAt = System.currentTimeMillis(); if (BrowserEvents.CLICK.equalsIgnoreCase(event.getNativeEvent().getType())) { if (clictAt - lastClick < 300) { // dblclick on 2 clicks detected within 300 ms showFormPopup(event.getValue().getPrimaryKey()); } lastClick = System.currentTimeMillis(); } if (BrowserEvents.KEYUP.equalsIgnoreCase(event.getNativeEvent().getType())) { event.getNativeEvent().stopPropagation(); if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) { showFormPopup(event.getValue().getPrimaryKey()); } } } }); gridScroller = new ScrollPanel(); gridScroller.getElement().getStyle().setHeight(scrollHeight, Unit.PX); // gridPagerPanel = new ShowMorePagerPanel(gridScroller); // gridPagerPanel.setIncrementSize(pageSize); // gridPagerPanel.setDisplay(grid); gridPager.setDisplay(grid); gridDataProvider = new GridDataProvider(); gridDataProvider.addDataDisplay(grid); if (ownerProperty == null) { gridScroller.add(grid); } if (availableHeight <= 0) { Window.addResizeHandler(new ResizeHandler() { @Override public void onResize(ResizeEvent event) { listScroller.getElement().getStyle().setHeight(event.getHeight() - SCROLL_OFFSET, Unit.PX); formScroller.getElement().getStyle().setHeight(event.getHeight() - SCROLL_OFFSET, Unit.PX); gridScroller.getElement().getStyle().setHeight(event.getHeight() - SCROLL_OFFSET, Unit.PX); formPopup.setPixelSize(Window.getClientWidth() - 40, Window.getClientHeight()); } }); } listColumn.setSize(LayoutUtils.size(this.view.getListPanelWidth())); listColumn.add(listPagerPanel); formColumn.setSize(LayoutUtils.size(12 - this.view.getListPanelWidth())); formColumn.add(formScroller); gridScroller.getElement().getStyle().setHeight(scrollHeight, Unit.PX); if (ownerProperty == null) { gridColumn.add(gridScroller); } else { gridColumn.add(grid); } initialized = true; boolean openAsTable = view.isOpenAsTable(); String storedtableModeString = SettingsStorage.getSetting(TableListerWidget.this.view.getId() + SettingsStorage.TABLE_MODE); if (storedtableModeString != null) { openAsTable = Boolean.parseBoolean(storedtableModeString); } if (openAsTable) { showTable(null); } else { showList(null); } enableNavigationControls(); enableQueryParameterFields(false); } private void enableQueryParameterFields(boolean fireReload) { QueryDescriptorDTO activeFilter = TableListerWidget.this.view.getQueryDescriptors().get(querySelector.getSelectedIndex()); TableListerWidget.this.view.setActiveFilter(activeFilter.getId()); SettingsStorage.storeSetting(TableListerWidget.this.view.getId() + SettingsStorage.ACTIVE_FILTER, activeFilter.getId()); if (activeFilter.getQueryParameters().size() == 1) { queryParameters.setVisible(true); if ("java.util.Date".equals(TableListerWidget.this.view.getActiveQueryDescriptor().getQueryParameters().get(0).getProperty().getType())) { String formatPattern = TableListerWidget.this.view.getActiveQueryDescriptor().getQueryParameters().get(0).getProperty().getFormatPattern(); queryDateField.setFormat(formatPattern.replace("M", "m")); queryDateField.setLanguage(mapLanguageToAplikatorLocale()); if (!formatPattern.contains("h") && !formatPattern.contains("H")) { //display date picker only queryDateField.setMinView(DateTimePickerView.MONTH); } queryDateField.setVisible(true); queryListField.setVisible(false); queryTextField.setVisible(false); queryDateField.setValue(null); } else if (TableListerWidget.this.view.getActiveQueryDescriptor().getQueryParameters().get(0).getProperty().getListValues() != null) { queryDateField.setVisible(false); queryListField.setVisible(true); queryTextField.setVisible(false); queryListField.clear(); for (org.aplikator.client.shared.data.ListItem item : TableListerWidget.this.view.getActiveQueryDescriptor().getQueryParameters().get(0).getProperty().getListValues()) { queryListField.addItem(item.getName(), item.getValue().toString()); } queryListField.setSelectedIndex(0); queryListField.setFocus(true); reload(); } else { queryDateField.setVisible(false); queryListField.setVisible(false); queryTextField.setVisible(true); queryTextField.setText(""); queryTextField.setFocus(true); } } else { queryParameters.setVisible(false); if (fireReload) { reload(); } } } private void showFormPopup(PrimaryKey primaryKey) { formPopup.setPixelSize(Window.getClientWidth() - 40, Window.getClientHeight()); formPopup.show(); formForGrid.displayRecord(primaryKey, recordContainerDTO, TableListerWidget.this.ownerProperty, ownerPrimaryKey, null); } private Modal createFormPopup() { ModalBody formPopupContents = new ModalBody(); formPopupContents.addStyleName("formscroller"); final Modal formPopup = new Modal(); formPopup.getElement().getStyle().setOverflowY(Style.Overflow.AUTO);//fix against hidding scrollbar in modal stack formPopup.setDataBackdrop(ModalBackdrop.STATIC); formForGrid = new FormWidget(this.view, this, this.ownerForm); ModalHeader buttonPanel = new ModalHeader(); Button buttonCancelPopup = new Button("", IconType.BAN, new ClickHandler() { public void onClick(ClickEvent event) { formForGrid.cancel(); if (editingNewRecord) { gridDataProvider.removeFirstRecord(); } editingNewRecord = false; formPopup.hide(); focusGrid(); } }); buttonPanel.add(buttonCancelPopup); final Button buttonSavePopup = new Button("", IconType.SAVE, new ClickHandler() { public void onClick(ClickEvent event) { formPopup.hide(); if (formForGrid != null) { formForGrid.save(true); } focusGrid(); } }); buttonPanel.add(buttonSavePopup); LayoutUtils.addTooltip(buttonCancelPopup, Aplikator.application.getConfigString("aplikator.table.cancel")); LayoutUtils.addTooltip(buttonSavePopup, Aplikator.application.getConfigString("aplikator.table.save")); formPopup.add(buttonPanel); formPopup.add(formPopupContents); Row formPopupHolder = new Row(); formPopupHolder.add(formForGrid); formPopupContents.add(formPopupHolder); return formPopup; } @UiHandler("buttonList") void showList(ClickEvent e) { if (!buttonList.isEnabled()) { buttonTable.setActive(true); buttonList.setActive(false); return; } gridContainer.setVisible(false); listContainer.setVisible(true); gridPager.setVisible(false); listPager.setVisible(true); rangeLabelHolder.getElement().getStyle().setMarginTop(15, Unit.PX); rangeLabelHolder.getElement().getStyle().setMarginBottom(15, Unit.PX); displayMode = DisplayMode.LIST; buttonCancel.setVisible(true); buttonSave.setVisible(true); buttonList.setActive(true); buttonTable.setActive(false); listDataProvider.refreshListAndExtendScroll(0, null, cellList); focusList(); SettingsStorage.storeSetting(TableListerWidget.this.view.getId() + SettingsStorage.TABLE_MODE, "false"); } @UiHandler("buttonTable") void showTable(ClickEvent e) { if (!buttonTable.isEnabled()) { buttonTable.setActive(false); buttonList.setActive(true); return; } if (listMode.equals(ListMode.INSERT)) { refresh(); } listContainer.setVisible(false); gridContainer.setVisible(true); listPager.setVisible(false); gridPager.setVisible(true); rangeLabelHolder.getElement().getStyle().setMarginTop(11, Unit.PX); rangeLabelHolder.getElement().getStyle().setMarginBottom(11, Unit.PX); displayMode = DisplayMode.GRID; buttonCancel.setVisible(false); buttonSave.setVisible(false); buttonList.setActive(false); buttonTable.setActive(true); focusGrid(); SettingsStorage.storeSetting(TableListerWidget.this.view.getId() + SettingsStorage.TABLE_MODE, "true"); } private void focusList() { if (ownerProperty == null && displayMode.equals(DisplayMode.LIST)) { Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() { @Override public void execute() { cellList.setFocus(true); } }); } } private void focusGrid() { if (ownerProperty == null && displayMode.equals(DisplayMode.GRID)) { Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() { @Override public void execute() { grid.setFocus(true); } }); } } public SingleSelectionModel<RecordDTO> getListSelectionModel() { return listSelectionModel; } private boolean canEnableEmbedded() { if (ownerPrimaryKey == null) { return true; } else { return ownerPrimaryKey.getId() != -1; } } void enableEditingControls() { searchField.setEnabled(false); buttonSearch.setEnabled(false); buttonCreate.setEnabled(false); buttonDelete.setEnabled(false); buttonReload.setEnabled(false); buttonPopupSearch.setEnabled(false); sortSelector.setEnabled(false); querySelector.setEnabled(false); queryTextField.setEnabled(false); queryListField.setEnabled(false); queryDateField.setEnabled(false); buttonQuery.setEnabled(false); boolean canEnable = canEnableEmbedded(); buttonCancel.setEnabled(canEnable && enabled); buttonSave.setEnabled(canEnable && enabled); buttonList.setEnabled(false); buttonTable.setEnabled(false); if (nestedEditorNotification != null) { nestedEditorNotification.nestedEditorStarted(); } cellList.setKeyboardSelectionPolicy(HasKeyboardSelectionPolicy.KeyboardSelectionPolicy.DISABLED); } void enableNavigationControls() { boolean canEnable = canEnableEmbedded(); searchField.setEnabled(canEnable); buttonSearch.setEnabled(canEnable); buttonCreate.setEnabled(canEnable && enabled); buttonDelete.setEnabled(canEnable && enabled); buttonReload.setEnabled(canEnable); buttonPopupSearch.setEnabled(canEnable && isSelectedSortScrollable()); sortSelector.setEnabled(canEnable); querySelector.setEnabled(canEnable); queryTextField.setEnabled(canEnable); queryListField.setEnabled(canEnable); queryDateField.setEnabled(canEnable); buttonQuery.setEnabled(canEnable); buttonCancel.setEnabled(false); buttonSave.setEnabled(false); buttonList.setEnabled(canEnable); buttonTable.setEnabled(canEnable); if (nestedEditorNotification != null) { nestedEditorNotification.nestedEditorFinished(); } cellList.setKeyboardSelectionPolicy(HasKeyboardSelectionPolicy.KeyboardSelectionPolicy.BOUND_TO_SELECTION); } private boolean isSelectedSortScrollable() { SortDescriptorDTO sort = getSelectedSort(); if (sort.getPrimarySortProperty().getType().equals("java.lang.String")) {//accept all strings return true; } if (sort.getPrimarySortProperty().getType().equals("java.lang.Integer") //accept all int except primary key && !sort.getPrimarySortProperty().getId().equals(view.getEntity().getPrimaryKey().getId())) { return true; } if (sort.getPrimarySortProperty().getType().equals("java.math.BigDecimal")) {//accept all numbers return true; } return false; } private SortDescriptorDTO getSelectedSort() { return TableListerWidget.this.view.getSortDescriptors().get(sortSelector.getSelectedIndex()); } private DefaultSelectionEventManager.EventTranslator<RecordDTO> createVetoTranslator() { DefaultSelectionEventManager.EventTranslator<RecordDTO> translator = new DefaultSelectionEventManager.EventTranslator<RecordDTO>() { @Override public boolean clearCurrentSelection(CellPreviewEvent<RecordDTO> event) { NativeEvent nativeEvent = event.getNativeEvent(); boolean ctrlOrMeta = nativeEvent.getCtrlKey() || nativeEvent.getMetaKey(); return !ctrlOrMeta; } @Override public DefaultSelectionEventManager.SelectAction translateSelectionEvent(CellPreviewEvent<RecordDTO> event) { if (form.isDirty()) { return DefaultSelectionEventManager.SelectAction.IGNORE; } return DefaultSelectionEventManager.SelectAction.DEFAULT; } }; return translator; } public void reload(String searchArgument) { this.indexSearchArgument = searchArgument; refresh(); } @Override public void initFromContainer(RecordContainerDTO initializingRecords) { //Not supported for table yet, just reload reload(); } @Override public void reload() { this.indexSearchArgument = null; searchField.setText(""); view.setSearchString(""); if (view.getActiveQueryDescriptor() != null && view.getActiveQueryDescriptor().getQueryParameters().size() > 1) { final QueryParamDialogBox dialogBox = createQueryParamDialogBox(); dialogBox.show(); } else if (view.getActiveQueryDescriptor() != null && view.getActiveQueryDescriptor().getQueryParameters().size() == 1) { view.getActiveQueryDescriptor().getQueryParameters().get(0).setValue(getQueryFieldValue()); refresh(); } else { refresh(); } } private String getQueryFieldValue() { if ("java.util.Date".equals(view.getActiveQueryDescriptor().getQueryParameters().get(0).getProperty().getType())) { if (queryDateField.getValue() != null) { return Long.toString(queryDateField.getValue().getTime()); } else { return null; } } else if (view.getActiveQueryDescriptor().getQueryParameters().get(0).getProperty().getListValues() != null) { return queryListField.getSelectedValue(); } return queryTextField.getText(); } @Override public void refresh() { editingNewRecord = false; listSelectionModel.clear(); gridSelectionModel.clear(); if (ownerPrimaryKey == null) { recordContainerDTO.clear(); } if (indexSearchArgument != null) { //loadDataSearch(); listMode = ListMode.INDEX; recordCount = 0; resetDataProviders(); } else { listMode = ListMode.STANDARD; recordCount = 0; //resetDataProviders(); listDataProvider.resetCache(); cellList.setRowCount(0); cellList.setVisibleRange(0, 0); gridDataProvider.resetCache(); grid.setRowCount(0); grid.setVisibleRange(0, 0); RestClient.create(AplikatorService.class, new RemoteCallback<Integer>() { @Override public void callback(Integer resp) { recordCount = resp; resetDataProviders(); } }, new AplikatorErrorCallback("aplikator.table.loaderror") ).getRecordCount(view.getId(), view.getActiveFilter(), view.getActiveSort(), view.getActiveQueryDescriptor(), view.getSearchString(), ownerProperty, (ownerPrimaryKey == null) ? null : ownerPrimaryKey.getSerializationString()); } enableNavigationControls(); } private void resetDataProviders() { listDataProvider.resetCache(); cellList.setVisibleRange(0, pageSize); listDataProvider.updateRowCount(recordCount, false); listDataProvider.onRangeChanged(cellList); gridDataProvider.resetCache(); grid.setVisibleRange(0, pageSize); gridDataProvider.updateRowCount(recordCount, false); gridDataProvider.onRangeChanged(grid); } private QueryParamDialogBox createQueryParamDialogBox() { Container dialogWrapper = new Container(); dialogWrapper.setFluid(true); Form dialogContents = new Form(); dialogContents.setType(FormType.HORIZONTAL); final QueryParamDialogBox dialogBox = new QueryParamDialogBox(); dialogBox.setTitle(Aplikator.application.getConfigString("aplikator.table.queryparams")); ModalBody contents = new ModalBody(); dialogWrapper.add(dialogContents); contents.add(dialogWrapper); dialogBox.add(contents); final int paramCount = view.getActiveQueryDescriptor().getQueryParameters().size(); final ParamValues[] paramValues = new ParamValues[paramCount]; final Button closeButton = new Button(Aplikator.application.getConfigString("aplikator.table.ok"), new ClickHandler() { @SuppressWarnings("unchecked") public void onClick(ClickEvent event) { queryParamDialogBoxClosed(dialogBox, paramCount, paramValues); } }); for (int i = 0; i < paramCount; i++) { FormGroup paramPane = new FormGroup(); FormLabel paramLabel = new FormLabel(); paramLabel.setText(view.getActiveQueryDescriptor().getQueryParameters().get(i).getName()); paramPane.add(paramLabel); paramValues[i] = new ParamValues(view.getActiveQueryDescriptor().getQueryParameters().get(i), dialogBox, paramCount, paramValues); if (i == 0) { dialogBox.firstField = paramValues[i]; } paramPane.add(paramValues[i]); dialogContents.add(paramPane); } ModalFooter buttonPanel = new ModalFooter(); dialogBox.add(buttonPanel); buttonPanel.add(closeButton); return dialogBox; } private void queryParamDialogBoxClosed(Modal dialogBox, int paramCount, ParamValues[] paramValues) { dialogBox.hide(); for (int i = 0; i < paramCount; i++) { view.getActiveQueryDescriptor().getQueryParameters().get(i).setValue(paramValues[i].getQueryParamValue()); } refresh(); } public PrimaryKey getSelectedPrimaryKey() { RecordDTO rec = getSelectedRecord(); return rec == null ? null : rec.getPrimaryKey(); } public RecordDTO getSelectedRecord() { if (displayMode.equals(DisplayMode.GRID)) { return this.gridSelectedRecordDTO; } else { return this.listSelectedRecordDTO; } } private void sqlSearch() { view.setSearchString(searchField.getText()); indexSearchArgument = null; refresh(); } private void indexSearch() { indexSearchArgument = (searchField.getText()); refresh(); } @UiHandler("buttonCreate") void buttonCreateClicked(ClickEvent event) { RecordDTO recordToCopy = null; NativeEvent nEv = event.getNativeEvent(); if (nEv.getAltKey()) { recordToCopy = getSelectedRecord(); } listMode = ListMode.INSERT; if (displayMode.equals(DisplayMode.GRID)) { formPopup.setPixelSize(Window.getClientWidth() - 40, Window.getClientHeight()); formPopup.show(); formForGrid.addRecord(recordContainerDTO, ownerProperty, ownerPrimaryKey, recordToCopy, new AddRecordCallback() { @Override public void recordAdded(RecordDTO recordDTO) { recordDTO.setPreview(Aplikator.application.getConfigString("aplikator.table.newRecordLabel")); editingNewRecord = true; gridSelectionModel.clear(); gridDataProvider.insertRecord(recordDTO); gridSelectionModel.setSelected(recordDTO, true); } }); } else { formHolder.showWidget(1); form.addRecord(recordContainerDTO, ownerProperty, ownerPrimaryKey, recordToCopy, new AddRecordCallback() { @Override public void recordAdded(RecordDTO recordDTO) { recordDTO.setPreview(Aplikator.application.getConfigString("aplikator.table.newRecordLabel")); editingNewRecord = true; listSelectionModel.clear(); listDataProvider.insertRecord(recordDTO); listSelectionModel.setSelected(recordDTO, true); } }); formScroller.setVerticalScrollPosition(0); } } @UiHandler("buttonCancel") void buttonCancelClicked(ClickEvent event) { buttonSave.setEnabled(false); buttonCancel.setEnabled(false); form.cancel(); if (editingNewRecord) { listDataProvider.removeFirstRecord(); } editingNewRecord = false; } private void openForm() { form.displayRecord(getSelectedPrimaryKey(), recordContainerDTO, ownerProperty, ownerPrimaryKey, null); formScroller.setVerticalScrollPosition(0); } @UiHandler("buttonSave") void buttonSaveClicked(ClickEvent event) { buttonSave.setEnabled(false); buttonCancel.setEnabled(false); form.save(true); } public void updateEditedRecord(RecordDTO savedRecordDTO) { if (displayMode.equals(DisplayMode.LIST)) { if (editingNewRecord) { if (listSelectedRecordDTO != null && listSelectedRecordDTO.getPrimaryKey().equals(savedRecordDTO.getPrimaryKey())) { listSelectionModel.setSelected(listSelectedRecordDTO, false);//remove temporary record from selection before changing ID and reselect afterwards listSelectedRecordDTO.getPrimaryKey().setId(savedRecordDTO.getPrimaryKey().getId()); listSelectionModel.setSelected(listSelectedRecordDTO, true); } else { Messages.warning("Inconsistent state: selected pk:" + listSelectedRecordDTO.getPrimaryKey() + "\nsaved pk:" + savedRecordDTO.getPrimaryKey()); } editingNewRecord = false; } if (listSelectedRecordDTO != null && listSelectedRecordDTO.getPrimaryKey().equals(savedRecordDTO.getPrimaryKey())) { LOG.fine("UPDATE EDITED RECORD:" + listSelectedRecordDTO.getPrimaryKey() + " - " + savedRecordDTO.getPreview()); listSelectedRecordDTO.setPreview(savedRecordDTO.getPreview()); for (String s : savedRecordDTO.getProperties()) { listSelectedRecordDTO.setValue(s, savedRecordDTO.getValue(s)); } cellList.redraw(); } } else { if (editingNewRecord) { if (gridSelectedRecordDTO != null && gridSelectedRecordDTO.getPrimaryKey().equals(savedRecordDTO.getPrimaryKey())) { gridSelectionModel.setSelected(gridSelectedRecordDTO, false);//remove temporary record from selection before changing ID and reselect afterwards gridSelectedRecordDTO.getPrimaryKey().setId(savedRecordDTO.getPrimaryKey().getId()); gridSelectionModel.setSelected(gridSelectedRecordDTO, true); } else { Messages.warning("Inconsistent state: selected pk:" + gridSelectedRecordDTO.getPrimaryKey() + "\nsaved pk:" + savedRecordDTO.getPrimaryKey()); } editingNewRecord = false; } if (gridSelectedRecordDTO != null && gridSelectedRecordDTO.getPrimaryKey().equals(savedRecordDTO.getPrimaryKey())) { LOG.fine("UPDATE EDITED RECORD:" + gridSelectedRecordDTO.getPrimaryKey() + " - " + savedRecordDTO.getPreview()); gridSelectedRecordDTO.setPreview(savedRecordDTO.getPreview()); for (String s : savedRecordDTO.getProperties()) { gridSelectedRecordDTO.setValue(s, savedRecordDTO.getValue(s)); } grid.redraw(); } } } @Override public void save() { if (form != null && ownerProperty == null) { form.save(true); } } @UiHandler("buttonDelete") void buttonDeleteClicked(ClickEvent event) { createDeleteConfirmDialogBox().show(); } private Modal createDeleteConfirmDialogBox() { // Create a dialog box and set the caption text // Create a table to layout the content final Modal dialogBox = new Modal(); dialogBox.setDataBackdrop(ModalBackdrop.STATIC); dialogBox.setTitle(Aplikator.application.getConfigString("aplikator.table.confirmation")); ModalBody contents = new ModalBody(); contents.add(new Span(Aplikator.application.getConfigString("aplikator.table.confirmdelete"))); dialogBox.add(contents); // Add a close button at the bottom of the dialog Button okButton = new Button(Aplikator.application.getConfigString("aplikator.table.ok"), new ClickHandler() { @SuppressWarnings("unchecked") public void onClick(ClickEvent event) { dialogBox.hide(); //if (ownerProperty != null) { // recordContainer.addRecord(view, getSelectedRecord(), null, Operation.DELETE); // pageData.remove(getSelectedRecord()); // redrawPage(); //} else { RecordContainerDTO newContainer = new RecordContainerDTO(); if (displayMode.equals(DisplayMode.LIST)) { //for (Record toDelete : listSelectionModel.getSelectedList()) { newContainer.addRecord(view.getId(), listSelectionModel.getSelectedObject(), null, Operation.DELETE); //} } else { //for (RecordDTO toDelete : gridSelectionModel.getSelectedList()) { newContainer.addRecord(view.getId(), gridSelectionModel.getSelectedObject(), null, Operation.DELETE); //} } try { RestClient.create(AplikatorService.class, new RemoteCallback<RecordContainerDTO>() { @Override public void callback(RecordContainerDTO records) { Messages.success(Aplikator.application.getConfigString("aplikator.table.deletedok")); reload(); } }, new AplikatorErrorCallback("aplikator.table.deletederror") ).processRecords(newContainer); } catch (AplikatorException e) { Window.alert("CHYBA: " + e); } //} } }); Button cancelButton = new Button(Aplikator.application.getConfigString("aplikator.table.cancel"), new ClickHandler() { @SuppressWarnings("unchecked") public void onClick(ClickEvent event) { dialogBox.hide(); } }); ModalFooter buttonPanel = new ModalFooter(); dialogBox.add(buttonPanel); buttonPanel.add(cancelButton); buttonPanel.add(okButton); // Return the dialog box return dialogBox; } @UiHandler("buttonReload") void buttonReloadClicked(ClickEvent event) { reload(); } public PrimaryKey getOwnerPrimaryKey() { return ownerPrimaryKey; } public void setOwnerPrimaryKey(PrimaryKey value) { ownerPrimaryKey = value; } public void setRecordContainerDTO(RecordContainerDTO recordContainerDTO) { this.recordContainerDTO = recordContainerDTO; } public void setDirty(boolean dirty) { // for (Form nestedForm:nestedForms){ // nestedForm.setDirty(dirty); // } } @Override public void setEnabled(boolean enabled) { this.enabled = enabled; this.form.setEnabled(enabled); this.formForGrid.setEnabled(enabled); } void setListAccess(boolean active) { if (!active) { cellList.getElement().addClassName("inactive"); } else { cellList.getElement().removeClassName("inactive"); } } public void addNestedEditorNotification(NestedEditorNotification nestedEditorNotification) { this.nestedEditorNotification = nestedEditorNotification; } private enum ListMode {STANDARD, INSERT, INDEX} private enum DisplayMode {LIST, GRID} interface TableListerWidgetUiBinder extends UiBinder<Div, TableListerWidget> { } public interface TableListerWidgetResources extends CellList.Resources { public static final TableListerWidgetResources INSTANCE = GWT.create(TableListerWidgetResources.class); @Source("TableListerCellList.css") CellList.Style cellListStyle(); } public interface DataGridResources extends CellList.Resources { public static final TableListerWidgetResources INSTANCE = GWT.create(TableListerWidgetResources.class); @Source("TableListerCellList.css") CellList.Style cellListStyle(); } private static class QueryParamDialogBox extends Modal { ParamValues firstField; @Override public void show() { super.show(); if (firstField != null) { firstField.setFocus(); } } } private static class RecordCell extends AbstractCell<RecordDTO> { @Override public void render(Context ctx, RecordDTO value, SafeHtmlBuilder sb) { if (value != null) { String preview = value.getPreview(); if (preview == null || "".equals(preview)) { preview = " "; } sb.appendHtmlConstant(preview); } } } private class ParamValues extends Composite { TextBox queryTextField; ListBox queryListField; DateTimePicker queryDateField; QueryParameterDTO queryParameter; Div holder; ParamValues(QueryParameterDTO queryParameter, final Modal dialogBox, final int paramCount, final ParamValues[] paramValues) { this.queryParameter = queryParameter; holder = new Div(); initWidget(holder); if ("java.util.Date".equals(queryParameter.getProperty().getType())) { queryDateField = new DateTimePicker(); String formatPattern = queryParameter.getProperty().getFormatPattern(); queryDateField.setFormat(formatPattern.replace("M", "m")); queryDateField.setLanguage(mapLanguageToAplikatorLocale()); if (!formatPattern.contains("h") && !formatPattern.contains("H")) { //display date picker only queryDateField.setMinView(DateTimePickerView.MONTH); } queryDateField.setAutoClose(true); queryDateField.setValue(null); holder.add(queryDateField); } else if (queryParameter.getProperty().getListValues() != null) { queryListField = new ListBox(); for (org.aplikator.client.shared.data.ListItem item : queryParameter.getProperty().getListValues()) { queryListField.addItem(item.getName(), item.getValue().toString()); } queryListField.setSelectedIndex(0); holder.add(queryListField); } else { queryTextField = new TextBox(); queryTextField.setText(""); holder.add(queryTextField); queryTextField.addKeyUpHandler(new KeyUpHandler() { @Override public void onKeyUp(KeyUpEvent event) { event.stopPropagation(); if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { queryParamDialogBoxClosed(dialogBox, paramCount, paramValues); } } }); } } String getQueryParamValue() { if ("java.util.Date".equals(queryParameter.getProperty().getType())) { if (queryDateField.getValue() != null) { return Long.toString(queryDateField.getValue().getTime()); } else { return null; } } else if (queryParameter.getProperty().getListValues() != null) { return queryListField.getSelectedValue(); } else { return queryTextField.getValue(); } } void setFocus() { if (queryTextField != null) { queryTextField.setFocus(true); } else if (queryListField != null) { queryListField.setFocus(true); } } } /** * A scrolling pager that automatically increases the range every time the * scroll bar reaches the bottom. */ class ShowMorePagerPanel extends AbstractPager { /** * The default increment size. */ private static final int DEFAULT_INCREMENT = 20; /** * The scrollable panel. */ private final ScrollPanel scrollable; /** * The increment size. */ private int incrementSize = DEFAULT_INCREMENT; /** * The last scroll position. */ private int lastScrollPos = 0; /** * Construct a new {@link ShowMorePagerPanel}. */ public ShowMorePagerPanel(ScrollPanel scrollPanel) { scrollable = scrollPanel; initWidget(scrollable); // Do not let the scrollable take tab focus. scrollable.getElement().setTabIndex(-1); // Handle scroll events. scrollable.addScrollHandler(new ScrollHandler() { @Override public void onScroll(ScrollEvent event) { HasRows display = getDisplay(); if (display == null) { return; } // If scrolling up, ignore the event. int oldScrollPos = lastScrollPos; lastScrollPos = scrollable.getVerticalScrollPosition(); //LOG.info(TableListerWidget.this.view.getLocalizedName()+" OldScrollPos:" + oldScrollPos + " LastScrollPos:" + lastScrollPos); if (oldScrollPos >= lastScrollPos) { return; } int maxScrollTop = scrollable.getWidget().getOffsetHeight() - scrollable.getOffsetHeight(); //LOG.info(TableListerWidget.this.view.getLocalizedName()+" Lastscrollpos:"+lastScrollPos+ " maxScrollTop:"+maxScrollTop); //LOG.info(TableListerWidget.this.view.getLocalizedName()+" WidgetOffset:" + scrollable.getWidget().getOffsetHeight() + " panelOffset:" + scrollable.getOffsetHeight()); if (lastScrollPos >= maxScrollTop - 10) { // We are near the end, so increase the page size. int newPageSize = Math.min( display.getVisibleRange().getLength() + incrementSize, display.getRowCount()); //LOG.info(TableListerWidget.this.view.getLocalizedName()+" Extending scroll down. Size:" + newPageSize); display.setVisibleRange(0, newPageSize); } } }); } /** * Get the number of rows by which the range is increased when the scrollbar * reaches the bottom. * * @return the increment size */ public int getIncrementSize() { return incrementSize; } /** * Set the number of rows by which the range is increased when the scrollbar * reaches the bottom. * * @param incrementSize the incremental number of rows */ public void setIncrementSize(int incrementSize) { this.incrementSize = incrementSize; } @Override public void setDisplay(HasRows display) { assert display instanceof Widget : "display must extend Widget"; scrollable.setWidget((Widget) display); super.setDisplay(display); } @Override protected void onRangeOrRowCountChanged() { } } private class RangeLabelPager extends AbstractPager { /** * The label that shows the current range. */ private final Span label = new Span(); /** * Construct a new {@link RangeLabelPager}. */ public RangeLabelPager() { initWidget(label); //label.addStyleName("control-label"); } @Override protected void onRangeOrRowCountChanged() { if (listMode.equals(ListMode.STANDARD) || listMode.equals(ListMode.INDEX)) { HasRows display = getDisplay(); Range range = display.getVisibleRange(); int start = range.getStart(); int end = Math.min(start + range.getLength(), display.getRowCount()); label.setText((start + 1) + " - " + end + " : " + display.getRowCount()); } else if (listMode.equals(ListMode.INSERT)) { label.setText(Aplikator.application.getConfigString("aplikator.table.insertMode")); } } } private class AplikatorDataProvider extends AsyncDataProvider<RecordDTO> { private final ScrollPanel scrollable; private final int incrementSize; private final List<RecordDTO> recordCache = new ArrayList<RecordDTO>(); private final List<RecordDTO> insertList = new ArrayList<RecordDTO>(); private boolean waitingForNextPage = false; public AplikatorDataProvider(ScrollPanel scrollPanel, int inc) { scrollable = scrollPanel; incrementSize = inc; } /** * {@link #onRangeChanged(HasData)} is called when the table requests a new * range of data. You can push data back to the displays using * {@link #updateRowData(int, List)}. */ @Override protected void onRangeChanged(final HasData<RecordDTO> display) { if (listMode.equals(ListMode.STANDARD)) { // Get the new range. final Range range = display.getVisibleRange(); final int start = range.getStart(); if (start > 0) { Messages.warning("ONRANGECHANGE START>0"); } final int length = range.getLength(); if (recordCount == 0 || length == 0) { display.setRowCount(0); return; } if (length > recordCache.size() && !waitingForNextPage) { if (ownerProperty != null && (ownerPrimaryKey == null || ownerPrimaryKey.getId() == -1)) { return; } final int pageStart = recordCache.size(); final int pageSize = length - pageStart; //LOG.fine("LOADING PAGE:"+pageStart+"-"+length); if (initialized) { waitingForNextPage = true; RestClient.create(AplikatorService.class, new RemoteCallback<RecordsPageDTO>() { @Override public void callback(RecordsPageDTO resp) { waitingForNextPage = false; if (resp.getOffset() == recordCache.size()) { processPageCallback(resp.getRecords(), start, display); } } }, new AplikatorErrorCallback("aplikator.table.loaderror") ).getRecords(view.getId(), view.getActiveFilter(), view.getActiveSort(), view.getActiveQueryDescriptor(), view.getSearchString(), ownerProperty, (ownerPrimaryKey == null) ? null : ownerPrimaryKey.getSerializationString(), pageStart, pageSize); } } } else if (listMode.equals(ListMode.INSERT)) { updateRowCount(insertList.size(), true); updateRowData(0, insertList); if ((listSelectionModel.getSelectedObject() == null /*|| listSelectionModel.getSelectedList().size() == 0*/) && insertList.size() > 0) { listSelectionModel.setSelected(insertList.get(0), true); } } else if (listMode.equals(ListMode.INDEX)) { // Get the new range. final Range range = display.getVisibleRange(); final int start = range.getStart(); if (start > 0) { Messages.warning("ONRANGECHANGE START>0"); } final int length = range.getLength(); //if (recordCount == 0 || length == 0) // return; if (length > recordCache.size() && !waitingForNextPage) { final int pageStart = recordCache.size(); final int pageSize = length - pageStart; waitingForNextPage = true; RestClient.create(AplikatorService.class, new RemoteCallback<SearchResult>() { @Override public void callback(SearchResult resp) { recordCount = resp.getCount().intValue(); listDataProvider.updateRowCount(recordCount, true); processPageCallback(resp.getRecords(), start, display); } }, new AplikatorErrorCallback("aplikator.table.loaderror") ).getPageSearch(view.getId(), indexSearchArgument, pageStart, pageSize); } } } private void processPageCallback(List<RecordDTO> newPage, int start, HasData<RecordDTO> display) { int oldListEnd = recordCache.size(); recordCache.addAll(newPage); waitingForNextPage = false; refreshListAndExtendScroll(oldListEnd, newPage, display); if ((listSelectionModel.getSelectedObject() == null /* || listSelectionModel.getSelectedList().size() == 0 */) && newPage.size() > 0) { listSelectionModel.setSelected(newPage.get(0), true); if (ownerProperty == null) { cellList.setKeyboardSelectedRow(0, true); } } } public void refreshListAndExtendScroll(int start, List<RecordDTO> newPage, HasData<RecordDTO> display) { if (cellList.getKeyboardSelectedRow() >= 0 && cellList.getKeyboardSelectedRow() <= (start - pageSize - 1)) { //Window.alert("SELECTED:"+cellList.getKeyboardSelectedRow()+" START:"+start+" pageSize:"+pageSize); scrollable.getElement().focus(); } if (newPage == null && start == 0) { updateRowData(start, recordCache); } else { updateRowData(start, newPage); } int scrollpos = scrollable.getMaximumVerticalScrollPosition(); if (TableListerWidget.this.ownerProperty == null && scrollpos <= 0 && scrollable.getElement().getClientHeight() > Window.getClientHeight()) { return; //Safeguard against unreliable scroll.getMaximumVerticalScrollPosition() (but only for top level tables } int rangestart = display.getVisibleRange().getStart(); int rangelength = display.getVisibleRange().getLength(); int rowcount = display.getRowCount(); int newPageSize = Math.min( display.getVisibleRange().getLength() + incrementSize, display.getRowCount()); //LOG.fine("???Display "+TableListerWidget.this.view.getLocalizedName()+" VISIBLE:"+scrollable.isVisible()+"!!!Size:" + newPageSize + " scrolpos:" + scrollpos + " rangestart:" + rangestart + " rangelength:" + rangelength + " rowcount:" + rowcount); if (scrollpos <= 0 && (rangestart + rangelength < rowcount)) { //LOG.fine("!!!Display"+TableListerWidget.this.view.getLocalizedName()+" Extending scroll down automatically.-" + (rangestart+rangelength)+"<"+rowcount); if (displayMode.equals(DisplayMode.LIST)) { display.setVisibleRange(0, newPageSize); } } } public void resetCache() { recordCache.clear(); waitingForNextPage = false; insertList.clear(); } public void insertRecord(RecordDTO rec) { insertList.add(0, rec); updateRowCount(insertList.size(), true); updateRowData(0, insertList); cellList.redraw(); } public void removeFirstRecord() { if (insertList.size() > 0) { listSelectionModel.setSelected(insertList.get(0), false); insertList.remove(0); } updateRowCount(insertList.size(), true); updateRowData(0, insertList); cellList.redraw(); } } private class GridDataProvider extends AsyncDataProvider<RecordDTO> { private final List<RecordDTO> insertList = new ArrayList<RecordDTO>(); public GridDataProvider() { } /** * {@link #onRangeChanged(HasData)} is called when the table requests a new * range of data. You can push data back to the displays using * {@link #updateRowData(int, List)}. */ @Override protected void onRangeChanged(final HasData<RecordDTO> display) { if (listMode.equals(ListMode.STANDARD)) { // Get the new range. final Range range = display.getVisibleRange(); final int start = range.getStart(); final int length = range.getLength(); if (recordCount == 0 || length == 0) { display.setRowCount(0); return; } if (initialized) { RestClient.create(AplikatorService.class, new RemoteCallback<RecordsPageDTO>() { @Override public void callback(RecordsPageDTO resp) { updateRowData(start, resp.getRecords()); if ((gridSelectionModel.getSelectedObject() == null/* || gridSelectionModel.getSelectedList().size() == 0*/) && resp.getRecords().size() > 0) { gridSelectionModel.setSelected(resp.getRecords().get(0), true); if (ownerProperty == null) { grid.setKeyboardSelectedRow(0, true); focusGrid(); } } } }, new AplikatorErrorCallback("aplikator.table.loaderror") ).getRecords(view.getId(), view.getActiveFilter(), view.getActiveSort(), view.getActiveQueryDescriptor(), view.getSearchString(), ownerProperty, (ownerPrimaryKey == null) ? null : ownerPrimaryKey.getSerializationString(), start, length); } } else if (listMode.equals(ListMode.INSERT)) { updateRowCount(insertList.size(), true); updateRowData(0, insertList); if ((gridSelectionModel.getSelectedObject() == null /*|| gridSelectionModel.getSelectedList().size() == 0*/) && insertList.size() > 0) { gridSelectionModel.setSelected(insertList.get(0), true); } } } public void resetCache() { insertList.clear(); } public void insertRecord(RecordDTO rec) { insertList.add(0, rec); updateRowCount(insertList.size(), true); updateRowData(0, insertList); grid.redraw(); } public void removeFirstRecord() { if (insertList.size() > 0) { gridSelectionModel.setSelected(insertList.get(0), false); insertList.remove(0); } updateRowCount(insertList.size(), true); updateRowData(0, insertList); grid.redraw(); } } }